linux内核奇遇记之md源代码解读之十二raid读写

linux内核奇遇记之md源代码解读之十二raid读写
转载请注明出处:http://blog.csdn.net/liumangxiong
我们都知道,对一个linux块设备来说,都有一个对应的请求队列。注册在这个请求队列上的请求就是该块设备的请求入口。对于raid来说,分配struct mddev时就已经设置好了,在函数md_alloc中有这样的代码:
[cpp]  view plain  copy
  1. 4846 blk_queue_make_request(mddev->queue, md_make_request);  
  2. 4847 blk_set_stacking_limits(&mddev->queue->limits);  
虽然全国的PM一直保持着稳健的增长,但丝毫也阻挡不了我们看代码的慧眼,在成千上万行的代码里我们依然能够迅速地找出raid读写入口就是md_make_request。
[html]  view plain  copy
  1. 328/* Rather than calling directly into the personality make_request function,  
  2. 329 * IO requests come here first so that we can check if the device is  
  3. 330 * being suspended pending a reconfiguration.  
  4. 331 * We hold a refcount over the call to ->make_request.  By the time that  
  5. 332 * call has finished, the bio has been linked into some internal structure  
  6. 333 * and so is visible to ->quiesce(), so we don't need the refcount any more.  
  7. 334 */  

我们在调用make_request函数之前,先检查设备是否因为重配置而挂起。在调用make_request函数之前,我们增加设备的引用计数,在make_request调用完成时再递减。增加设备引用计数主要是为调用->quiesce()之前保证下发到设备的IO已经完成。
[cpp]  view plain  copy
  1. 335static void md_make_request(struct request_queue *q, struct bio *bio)  
  2. 336{  
  3. 337     const int rw = bio_data_dir(bio);  
  4. 338     struct mddev *mddev = q->queuedata;  
  5. 339     int cpu;  
  6. 340     unsigned int sectors;  
  7. 341  
  8. 342     if (mddev == NULL || mddev->pers == NULL  
  9. 343         || !mddev->ready) {  
  10. 344          bio_io_error(bio);  
  11. 345          return;  
  12. 346     }  
  13. 347     smp_rmb(); /* Ensure implications of  'active' are visible */  
  14. 348     rcu_read_lock();  
  15. 349     if (mddev->suspended) {  
  16. 350          DEFINE_WAIT(__wait);  
  17. 351          for (;;) {  
  18. 352               prepare_to_wait(&mddev->sb_wait, &__wait,  
  19. 353                         TASK_UNINTERRUPTIBLE);  
  20. 354               if (!mddev->suspended)  
  21. 355                    break;  
  22. 356               rcu_read_unlock();  
  23. 357               schedule();  
  24. 358               rcu_read_lock();  
  25. 359          }  
  26. 360          finish_wait(&mddev->sb_wait, &__wait);  
  27. 361     }  
  28. 362     atomic_inc(&mddev->active_io);  
  29. 363     rcu_read_unlock();  
  30. 364  
  31. 365     /* 
  32. 366     * save the sectors now since our bio can 
  33. 367     * go away inside make_request 
  34. 368     */  
  35. 369     sectors = bio_sectors(bio);  
  36. 370     mddev->pers->make_request(mddev, bio);  
  37. 371  
  38. 372     cpu = part_stat_lock();  
  39. 373     part_stat_inc(cpu, &mddev->gendisk->part0, ios[rw]);  
  40. 374     part_stat_add(cpu, &mddev->gendisk->part0, sectors[rw], sectors);  
  41. 375     part_stat_unlock();  
  42. 376  
  43. 377     if (atomic_dec_and_test(&mddev->active_io) && mddev->suspended)  
  44. 378          wake_up(&mddev->sb_wait);  
  45. 379}  

337行,获取IO方向,用于设备信息统计
338行,获取阵列指针,该指针是在md_alloc中赋值的
342行,基本检查
348行,访问struct mddev信息加rcu读锁
349行,阵列suspend
350行,如果阵列suspend,即前面注释中讲的正在重配置,则加入sb_wait等待队列
360行,阵列完成suspend,从等待队列中移除
362行,递增阵列引用计数,在前面注释里有原因说明
370行,下发bio到阵列
372行,这个开始是信息统计
377行,递减阵列引用计数,如果正在重配置,则唤醒该进程
真正的数据通道命令也就370行这一句,其他的都是控制通道的。
对于raid5阵列,这个请求函数对应的是raid5.c中的make_request函数:
[cpp]  view plain  copy
  1. 4075static void make_request(struct mddev *mddev, struct bio * bi)  
  2. 4076{  
  3. 4077     struct r5conf *conf = mddev->private;  
  4. 4078     int dd_idx;  
  5. 4079     sector_t new_sector;  
  6. 4080     sector_t logical_sector, last_sector;  
  7. 4081     struct stripe_head *sh;  
  8. 4082     const int rw = bio_data_dir(bi);  
  9. 4083     int remaining;  
  10. 4084  
  11. 4085     if (unlikely(bi->bi_rw & REQ_FLUSH)) {  
  12. 4086          md_flush_request(mddev, bi);  
  13. 4087          return;  
  14. 4088     }  
  15. 4089  
  16. 4090     md_write_start(mddev, bi);  
  17. 4091  
  18. 4092     if (rw == READ &&  
  19. 4093          mddev->reshape_position == MaxSector &&  
  20. 4094          chunk_aligned_read(mddev,bi))  
  21. 4095          return;  

4085行,flush命令
4090行,写开始处理,进入看看:
[cpp]  view plain  copy
  1. 7156/* md_write_start(mddev, bi) 
  2. 7157 * If we need to update some array metadata (e.g. 'active' flag 
  3. 7158 * in superblock) before writing, schedule a superblock update 
  4. 7159 * and wait for it to complete. 
  5. 7160 */  

如果在写请求之前有需要更新阵列metadata,则发起一个同步超级块更新请求。

[cpp]  view plain  copy
  1. 7161void md_write_start(struct mddev *mddev, struct bio *bi)  
  2. 7162{  
  3. 7163     int did_change = 0;  
  4. 7164     if (bio_data_dir(bi) != WRITE)  
  5. 7165          return;  
  6. 7166  
  7. 7167     BUG_ON(mddev->ro == 1);  
  8. 7168     if (mddev->ro == 2) {  
  9. 7169          /* need to switch to read/write */  
  10. 7170          mddev->ro = 0;  
  11. 7171          set_bit(MD_RECOVERY_NEEDED, &mddev->recovery);  
  12. 7172          md_wakeup_thread(mddev->thread);  
  13. 7173          md_wakeup_thread(mddev->sync_thread);  
  14. 7174          did_change = 1;  
  15. 7175     }  
  16. 7176     atomic_inc(&mddev->writes_pending);  
  17. 7177     if (mddev->safemode == 1)  
  18. 7178          mddev->safemode = 0;  
  19. 7179     if (mddev->in_sync) {  
  20. 7180          spin_lock_irq(&mddev->write_lock);  
  21. 7181          if (mddev->in_sync) {  
  22. 7182               mddev->in_sync = 0;  
  23. 7183               set_bit(MD_CHANGE_CLEAN, &mddev->flags);  
  24. 7184               set_bit(MD_CHANGE_PENDING, &mddev->flags);  
  25. 7185               md_wakeup_thread(mddev->thread);  
  26. 7186               did_change = 1;  
  27. 7187          }  
  28. 7188          spin_unlock_irq(&mddev->write_lock);  
  29. 7189     }  
  30. 7190     if (did_change)  
  31. 7191          sysfs_notify_dirent_safe(mddev->sysfs_state);  
  32. 7192     wait_event(mddev->sb_wait,  
  33. 7193             !test_bit(MD_CHANGE_PENDING, &mddev->flags));  
  34. 7194}  

7164行,如果不为写,则直接返回
7168行,如果阵列为临时只读状态,则设置回读写状态,并设置检查同步标志。
7177行,如果阵列为安全模式,则设置为不安全模式。安全模式请参考本系列之四。
7179行,如果阵列为in_sync,则设置in_sync=0,设置阵列改变标志
7190行,更新sys对应状态
7192行,这一句代码在这个函数中是最重要。首先一看wait_event就知道该函数是同步的,再看条件是!test_bit(MD_CHANGE_PENDING, &mddev->flags),这个标志是在7184行设置的,所以这一句话最重要的作用就是要同步in_sync标志到磁盘中阵列超级块上。如果这里看明白了,也就懂得了什么是安全模式,safemode和in_sync各自的分工。
既然有md_write_start,那么也就有md_write_end,这个函数的作用就是在没有write_pending时添加一个定时器,这个定时器将in_sync置1并写到磁盘中阵列超级块。这样阵列也就从不安全模式再次回到了安全模式。
回到make_request函数中。
4092行,如果是读请求并且非reshape操作中,则执行chunk_aligned_read操作。顾名思义就是条块对齐读。条块对齐读指的是请求刚好为一个条带在某一个磁盘上的那一部分。由于条块对齐读刚好对应的数据都在一块物理磁盘上,所以可以直接发送到物理磁盘上,而不用申请struct stripe_head,可谓是最单纯的一个读写方式了。我们进入到chunk_aligned_read函数中来:
[cpp]  view plain  copy
  1. 3885static int chunk_aligned_read(struct mddev *mddev, struct bio * raid_bio)  
  2. 3886{  
  3. 3887     struct r5conf *conf = mddev->private;  
  4. 3888     int dd_idx;  
  5. 3889     struct bio* align_bi;  
  6. 3890     struct md_rdev *rdev;  
  7. 3891     sector_t end_sector;  
  8. 3892  
  9. 3893     if (!in_chunk_boundary(mddev, raid_bio)) {  
  10. 3894          pr_debug("chunk_aligned_read : non aligned\n");  
  11. 3895          return 0;  
  12. 3896     }  

3893行,判断请求是否在条块内,如果不是则返回走正常读写流程。
为了满足好奇心,我们还是跟进in_chunk_boundary函数:
[cpp]  view plain  copy
  1. 3775static int in_chunk_boundary(struct mddev *mddev, struct bio *bio)  
  2. 3776{  
  3. 3777     sector_t sector = bio->bi_sector + get_start_sect(bio->bi_bdev);  
  4. 3778     unsigned int chunk_sectors = mddev->chunk_sectors;  
  5. 3779     unsigned int bio_sectors = bio->bi_size >> 9;  
  6. 3780  
  7. 3781     if (mddev->new_chunk_sectors < mddev->chunk_sectors)  
  8. 3782          chunk_sectors = mddev->new_chunk_sectors;  
  9. 3783     return  chunk_sectors >=  
  10. 3784          ((sector & (chunk_sectors - 1)) + bio_sectors);  
  11. 3785}  

3777行,计算出bio对应的物理扇区
3779行,计算出bio请求的扇区数
3781行,这个是reshape阵列用到的,先不管
3783行,判断请求结束扇区是否和请求起始扇区在同一个条块内,如果是则说明是条块内请求。
[cpp]  view plain  copy
  1. 3897     /* 
  2. 3898     * use bio_clone_mddev to make a copy of the bio 
  3. 3899     */  
  4. 3900     align_bi = bio_clone_mddev(raid_bio, GFP_NOIO, mddev);  
  5. 3901     if (!align_bi)  
  6. 3902          return 0;  
  7. 3903     /* 
  8. 3904     *   set bi_end_io to a new function, and set bi_private to the 
  9. 3905     *     original bio. 
  10. 3906     */  
  11. 3907     align_bi->bi_end_io  = raid5_align_endio;  
  12. 3908     align_bi->bi_private = raid_bio;  
  13. 3909     /* 
  14. 3910     *     compute position 
  15. 3911     */  
  16. 3912     align_bi->bi_sector =  raid5_compute_sector(conf, raid_bio->bi_sector,  
  17. 3913                                  0,  
  18. 3914                                  &dd_idx, NULL);  
  19. 3915  
  20. 3916     end_sector = align_bi->bi_sector + (align_bi->bi_size >> 9);  

3900行,克隆一个请求bio,为什么要克隆呢?因为下发到阵列的bio与下发到磁盘的bio中内容是不一样的,比如对应的bi_sector和bi_end_io。所以在克隆之后还要在接下来的代码里修改克隆bio的几个字段。
3907行,设置bio回调函数为raid5_align_endio,所以在这个bio下发到磁盘之后,我们就接着这个函数看bio的返回处理。
3908行,指向原始bio
3912行,计算bio在物理磁盘上的偏移地址。来看一下原型:
[cpp]  view plain  copy
  1. 1943/* 
  2. 1944 * Input: a 'big' sector number, 
  3. 1945 * Output: index of the data and parity disk, and the sector # in them. 
  4. 1946 */  
  5. 1947static sector_t raid5_compute_sector(struct r5conf *conf, sector_t r_sector,  
  6. 1948                         int previous, int *dd_idx,  
  7. 1949                         struct stripe_head *sh)  

输入参数r_sector为阵列对应的扇区,输出参数dd_idx为r_sector对应磁盘在阵列中的下标,返回值为磁盘上相应的扇区偏移。
3916行,该bio结尾在磁盘上对应的扇区偏移
[cpp]  view plain  copy
  1. 3917     rcu_read_lock();  
  2. 3918     rdev = rcu_dereference(conf->disks[dd_idx].replacement);  
  3. 3919     if (!rdev || test_bit(Faulty, &rdev->flags) ||  
  4. 3920         rdev->recovery_offset < end_sector) {  
  5. 3921          rdev = rcu_dereference(conf->disks[dd_idx].rdev);  
  6. 3922          if (rdev &&  
  7. 3923              (test_bit(Faulty, &rdev->flags) ||  
  8. 3924              !(test_bit(In_sync, &rdev->flags) ||  
  9. 3925                rdev->recovery_offset >= end_sector)))  
  10. 3926               rdev = NULL;  
  11. 3927     }  

3918行,优先访问replacement盘,replacement机制在后面统一介绍,这里假设没有replacement盘。
3921行,没有replacement盘,rdev指向对应的数据盘
3922行,如果为Faulty或者!In_sync或者未同步,即该磁盘上数据不是有效数据,所以清空rdev指针。

[cpp]  view plain  copy
  1. 3928     if (rdev) {  
  2. 3929          sector_t first_bad;  
  3. 3930          int bad_sectors;  
  4. 3931  
  5. 3932          atomic_inc(&rdev->nr_pending);  
  6. 3933          rcu_read_unlock();  
  7. 3934          raid_bio->bi_next = (void*)rdev;  
  8. 3935          align_bi->bi_bdev =  rdev->bdev;  
  9. 3936          align_bi->bi_flags &= ~(1 << BIO_SEG_VALID);  
  10. 3937  
  11. 3938          if (!bio_fits_rdev(align_bi) ||  
  12. 3939              is_badblock(rdev, align_bi->bi_sector, align_bi->bi_size>>9,  
  13. 3940                    &first_bad, &bad_sectors)) {  
  14. 3941               /* too big in some way, or has a known bad block */  
  15. 3942               bio_put(align_bi);  
  16. 3943               rdev_dec_pending(rdev, mddev);  
  17. 3944               return 0;  
  18. 3945          }  
  19. 3946  
  20. 3947          /* No reshape active, so we can trust rdev->data_offset */  
  21. 3948          align_bi->bi_sector += rdev->data_offset;  
  22. 3949  
  23. 3950          spin_lock_irq(&conf->device_lock);  
  24. 3951          wait_event_lock_irq(conf->wait_for_stripe,  
  25. 3952                        conf->quiesce == 0,  
  26. 3953                        conf->device_lock, /* nothing */);  
  27. 3954          atomic_inc(&conf->active_aligned_reads);  
  28. 3955          spin_unlock_irq(&conf->device_lock);  
  29. 3956  
  30. 3957          generic_make_request(align_bi);  
  31. 3958          return 1;  
  32. 3959     } else {  
  33. 3960          rcu_read_unlock();  
  34. 3961          bio_put(align_bi);  
  35. 3962          return 0;  
  36. 3963     }  

3928行,对应物理磁盘上数据是有效的
3932行,递增磁盘请求数
3933行,rdev解锁
3934行,这里复用了bi_next指针,记录rdev,在bio done回来的时候用到
3935行,设置磁盘bdev
3938行,判断bio满足请求队列limit,并且不是坏块。如果不满足条件就不能直接发送请求到磁盘上。
3948行,加上磁盘data_offset,就是实际磁盘扇区了。
3951行,阵列正在执行配置命令,等待conf->quiesce为0
3957行,下发bio到磁盘。
3958行,如果下发条块内读请求,则返回1
3961行,读对应的磁盘不存在或者数据无效,释放bio
3962行,未下发读,返回0
如果要读的位置对应磁盘数据是有效的,那么请求就直接下发到磁盘上去了。我们就在bio的回调函数raid5_align_endio看看条块内读这个故事的继集:
[cpp]  view plain  copy
  1. 3829/* 
  2. 3830 *  The "raid5_align_endio" should check if the read succeeded and if it 
  3. 3831 *  did, call bio_endio on the original bio (having bio_put the new bio 
  4. 3832 *  first). 
  5. 3833 *  If the read failed.. 
  6. 3834 */  

如果条块内读请求成功的话,那么就将原始请求bio done回去。如果失败的话,那么哼哼哼。。。
[cpp]  view plain  copy
  1. 3835static void raid5_align_endio(struct bio *bi, int error)  
  2. 3836{  
  3. 3837     struct bio* raid_bi  = bi->bi_private;  
  4. 3838     struct mddev *mddev;  
  5. 3839     struct r5conf *conf;  
  6. 3840     int uptodate = test_bit(BIO_UPTODATE, &bi->bi_flags);  
  7. 3841     struct md_rdev *rdev;  
  8. 3842  
  9. 3843     bio_put(bi);  
  10. 3844  
  11. 3845     rdev = (void*)raid_bi->bi_next;  
  12. 3846     raid_bi->bi_next = NULL;  
  13. 3847     mddev = rdev->mddev;  
  14. 3848     conf = mddev->private;  
  15. 3849  
  16. 3850     rdev_dec_pending(rdev, conf->mddev);  
  17. 3851  
  18. 3852     if (!error && uptodate) {  
  19. 3853          bio_endio(raid_bi, 0);  
  20. 3854          if (atomic_dec_and_test(&conf->active_aligned_reads))  
  21. 3855               wake_up(&conf->wait_for_stripe);  
  22. 3856          return;  
  23. 3857     }  
  24. 3858  
  25. 3859  
  26. 3860     pr_debug("raid5_align_endio : io error...handing IO for a retry\n");  
  27. 3861  
  28. 3862     add_bio_to_retry(raid_bi, conf);  
  29. 3863}  

3843行,递减bio计数
3845行,找到保存的磁盘rdev指针
3850行,递减rdev的下发IO计数
3852行,如果读请求成功
3853行,将原始bio done回去
3854行,唤醒等待的控制命令,如果有的话
3862行,如果读请求失败,则加入阵列重试链表
[cpp]  view plain  copy
  1. 3787/* 
  2. 3788 *  add bio to the retry LIFO  ( in O(1) ... we are in interrupt ) 
  3. 3789 *  later sampled by raid5d. 
  4. 3790 */  
  5. 3791static void add_bio_to_retry(struct bio *bi,struct r5conf *conf)  
  6. 3792{  
  7. 3793     unsigned long flags;  
  8. 3794  
  9. 3795     spin_lock_irqsave(&conf->device_lock, flags);  
  10. 3796  
  11. 3797     bi->bi_next = conf->retry_read_aligned_list;  
  12. 3798     conf->retry_read_aligned_list = bi;  
  13. 3799  
  14. 3800     spin_unlock_irqrestore(&conf->device_lock, flags);  
  15. 3801     md_wakeup_thread(conf->mddev->thread);  
  16. 3802}  

重试链表是后进先出的,重试bio加入的是retry_read_aligned_list。这个链表我们已经在raid5d中照过面了。不记得也没有关系,我们再回头去看看:
[cpp]  view plain  copy
  1. 4662          while ((bio = remove_bio_from_retry(conf))) {  
  2. 4663               int ok;  
  3. 4664               spin_unlock_irq(&conf->device_lock);  
  4. 4665               ok = retry_aligned_read(conf, bio);  
  5. 4666               spin_lock_irq(&conf->device_lock);  
  6. 4667               if (!ok)  
  7. 4668                    break;  
  8. 4669               handled++;  
  9. 4670          }  

4662行,从重试链表中取出一个bio
4665行,重试读请求
至于retry_aligned_read函数不会再走之前直接下发到磁盘的老路了,因为重试之前的方法就相当于一个死循环调用了,那接下来会做些什么呢?大家不妨猜想一下,提示一下,阵列是有数据冗余的。好了,下一节再来揭开重试读的真实面纱。
转载请注明出处:http://blog.csdn.net/liumangxiong
  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值