linux内核奇遇记之md源代码解读之十二raid读写
转载请注明出处:http://blog.csdn.net/liumangxiong
我们都知道,对一个linux块设备来说,都有一个对应的请求队列。注册在这个请求队列上的请求就是该块设备的请求入口。对于raid来说,分配struct mddev时就已经设置好了,在函数md_alloc中有这样的代码:
- 4846 blk_queue_make_request(mddev->queue, md_make_request);
- 4847 blk_set_stacking_limits(&mddev->queue->limits);
虽然全国的PM一直保持着稳健的增长,但丝毫也阻挡不了我们看代码的慧眼,在成千上万行的代码里我们依然能够迅速地找出raid读写入口就是md_make_request。
- 328/* Rather than calling directly into the personality make_request function,
- 329 * IO requests come here first so that we can check if the device is
- 330 * being suspended pending a reconfiguration.
- 331 * We hold a refcount over the call to ->make_request. By the time that
- 332 * call has finished, the bio has been linked into some internal structure
- 333 * and so is visible to ->quiesce(), so we don't need the refcount any more.
- 334 */
我们在调用make_request函数之前,先检查设备是否因为重配置而挂起。在调用make_request函数之前,我们增加设备的引用计数,在make_request调用完成时再递减。增加设备引用计数主要是为调用->quiesce()之前保证下发到设备的IO已经完成。
- 335static void md_make_request(struct request_queue *q, struct bio *bio)
- 336{
- 337 const int rw = bio_data_dir(bio);
- 338 struct mddev *mddev = q->queuedata;
- 339 int cpu;
- 340 unsigned int sectors;
- 341
- 342 if (mddev == NULL || mddev->pers == NULL
- 343 || !mddev->ready) {
- 344 bio_io_error(bio);
- 345 return;
- 346 }
- 347 smp_rmb();
- 348 rcu_read_lock();
- 349 if (mddev->suspended) {
- 350 DEFINE_WAIT(__wait);
- 351 for (;;) {
- 352 prepare_to_wait(&mddev->sb_wait, &__wait,
- 353 TASK_UNINTERRUPTIBLE);
- 354 if (!mddev->suspended)
- 355 break;
- 356 rcu_read_unlock();
- 357 schedule();
- 358 rcu_read_lock();
- 359 }
- 360 finish_wait(&mddev->sb_wait, &__wait);
- 361 }
- 362 atomic_inc(&mddev->active_io);
- 363 rcu_read_unlock();
- 364
- 365
-
-
-
- 369 sectors = bio_sectors(bio);
- 370 mddev->pers->make_request(mddev, bio);
- 371
- 372 cpu = part_stat_lock();
- 373 part_stat_inc(cpu, &mddev->gendisk->part0, ios[rw]);
- 374 part_stat_add(cpu, &mddev->gendisk->part0, sectors[rw], sectors);
- 375 part_stat_unlock();
- 376
- 377 if (atomic_dec_and_test(&mddev->active_io) && mddev->suspended)
- 378 wake_up(&mddev->sb_wait);
- 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函数:
- 4075static void make_request(struct mddev *mddev, struct bio * bi)
- 4076{
- 4077 struct r5conf *conf = mddev->private;
- 4078 int dd_idx;
- 4079 sector_t new_sector;
- 4080 sector_t logical_sector, last_sector;
- 4081 struct stripe_head *sh;
- 4082 const int rw = bio_data_dir(bi);
- 4083 int remaining;
- 4084
- 4085 if (unlikely(bi->bi_rw & REQ_FLUSH)) {
- 4086 md_flush_request(mddev, bi);
- 4087 return;
- 4088 }
- 4089
- 4090 md_write_start(mddev, bi);
- 4091
- 4092 if (rw == READ &&
- 4093 mddev->reshape_position == MaxSector &&
- 4094 chunk_aligned_read(mddev,bi))
- 4095 return;
4085行,flush命令
4090行,写开始处理,进入看看:
如果在写请求之前有需要更新阵列metadata,则发起一个同步超级块更新请求。
- 7161void md_write_start(struct mddev *mddev, struct bio *bi)
- 7162{
- 7163 int did_change = 0;
- 7164 if (bio_data_dir(bi) != WRITE)
- 7165 return;
- 7166
- 7167 BUG_ON(mddev->ro == 1);
- 7168 if (mddev->ro == 2) {
- 7169
- 7170 mddev->ro = 0;
- 7171 set_bit(MD_RECOVERY_NEEDED, &mddev->recovery);
- 7172 md_wakeup_thread(mddev->thread);
- 7173 md_wakeup_thread(mddev->sync_thread);
- 7174 did_change = 1;
- 7175 }
- 7176 atomic_inc(&mddev->writes_pending);
- 7177 if (mddev->safemode == 1)
- 7178 mddev->safemode = 0;
- 7179 if (mddev->in_sync) {
- 7180 spin_lock_irq(&mddev->write_lock);
- 7181 if (mddev->in_sync) {
- 7182 mddev->in_sync = 0;
- 7183 set_bit(MD_CHANGE_CLEAN, &mddev->flags);
- 7184 set_bit(MD_CHANGE_PENDING, &mddev->flags);
- 7185 md_wakeup_thread(mddev->thread);
- 7186 did_change = 1;
- 7187 }
- 7188 spin_unlock_irq(&mddev->write_lock);
- 7189 }
- 7190 if (did_change)
- 7191 sysfs_notify_dirent_safe(mddev->sysfs_state);
- 7192 wait_event(mddev->sb_wait,
- 7193 !test_bit(MD_CHANGE_PENDING, &mddev->flags));
- 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函数中来:
- 3885static int chunk_aligned_read(struct mddev *mddev, struct bio * raid_bio)
- 3886{
- 3887 struct r5conf *conf = mddev->private;
- 3888 int dd_idx;
- 3889 struct bio* align_bi;
- 3890 struct md_rdev *rdev;
- 3891 sector_t end_sector;
- 3892
- 3893 if (!in_chunk_boundary(mddev, raid_bio)) {
- 3894 pr_debug("chunk_aligned_read : non aligned\n");
- 3895 return 0;
- 3896 }
3893行,判断请求是否在条块内,如果不是则返回走正常读写流程。
为了满足好奇心,我们还是跟进in_chunk_boundary函数:
- 3775static int in_chunk_boundary(struct mddev *mddev, struct bio *bio)
- 3776{
- 3777 sector_t sector = bio->bi_sector + get_start_sect(bio->bi_bdev);
- 3778 unsigned int chunk_sectors = mddev->chunk_sectors;
- 3779 unsigned int bio_sectors = bio->bi_size >> 9;
- 3780
- 3781 if (mddev->new_chunk_sectors < mddev->chunk_sectors)
- 3782 chunk_sectors = mddev->new_chunk_sectors;
- 3783 return chunk_sectors >=
- 3784 ((sector & (chunk_sectors - 1)) + bio_sectors);
- 3785}
3777行,计算出bio对应的物理扇区
3779行,计算出bio请求的扇区数
3781行,这个是reshape阵列用到的,先不管
3783行,判断请求结束扇区是否和请求起始扇区在同一个条块内,如果是则说明是条块内请求。
- 3897
-
-
- 3900 align_bi = bio_clone_mddev(raid_bio, GFP_NOIO, mddev);
- 3901 if (!align_bi)
- 3902 return 0;
- 3903
-
-
-
- 3907 align_bi->bi_end_io = raid5_align_endio;
- 3908 align_bi->bi_private = raid_bio;
- 3909
-
-
- 3912 align_bi->bi_sector = raid5_compute_sector(conf, raid_bio->bi_sector,
- 3913 0,
- 3914 &dd_idx, NULL);
- 3915
- 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在物理磁盘上的偏移地址。来看一下原型:
- 1943
-
-
-
- 1947static sector_t raid5_compute_sector(struct r5conf *conf, sector_t r_sector,
- 1948 int previous, int *dd_idx,
- 1949 struct stripe_head *sh)
输入参数r_sector为阵列对应的扇区,输出参数dd_idx为r_sector对应磁盘在阵列中的下标,返回值为磁盘上相应的扇区偏移。
3916行,该bio结尾在磁盘上对应的扇区偏移
- 3917 rcu_read_lock();
- 3918 rdev = rcu_dereference(conf->disks[dd_idx].replacement);
- 3919 if (!rdev || test_bit(Faulty, &rdev->flags) ||
- 3920 rdev->recovery_offset < end_sector) {
- 3921 rdev = rcu_dereference(conf->disks[dd_idx].rdev);
- 3922 if (rdev &&
- 3923 (test_bit(Faulty, &rdev->flags) ||
- 3924 !(test_bit(In_sync, &rdev->flags) ||
- 3925 rdev->recovery_offset >= end_sector)))
- 3926 rdev = NULL;
- 3927 }
3918行,优先访问replacement盘,replacement机制在后面统一介绍,这里假设没有replacement盘。
3921行,没有replacement盘,rdev指向对应的数据盘
3922行,如果为Faulty或者!In_sync或者未同步,即该磁盘上数据不是有效数据,所以清空rdev指针。
- 3928 if (rdev) {
- 3929 sector_t first_bad;
- 3930 int bad_sectors;
- 3931
- 3932 atomic_inc(&rdev->nr_pending);
- 3933 rcu_read_unlock();
- 3934 raid_bio->bi_next = (void*)rdev;
- 3935 align_bi->bi_bdev = rdev->bdev;
- 3936 align_bi->bi_flags &= ~(1 << BIO_SEG_VALID);
- 3937
- 3938 if (!bio_fits_rdev(align_bi) ||
- 3939 is_badblock(rdev, align_bi->bi_sector, align_bi->bi_size>>9,
- 3940 &first_bad, &bad_sectors)) {
- 3941
- 3942 bio_put(align_bi);
- 3943 rdev_dec_pending(rdev, mddev);
- 3944 return 0;
- 3945 }
- 3946
- 3947
- 3948 align_bi->bi_sector += rdev->data_offset;
- 3949
- 3950 spin_lock_irq(&conf->device_lock);
- 3951 wait_event_lock_irq(conf->wait_for_stripe,
- 3952 conf->quiesce == 0,
- 3953 conf->device_lock, );
- 3954 atomic_inc(&conf->active_aligned_reads);
- 3955 spin_unlock_irq(&conf->device_lock);
- 3956
- 3957 generic_make_request(align_bi);
- 3958 return 1;
- 3959 } else {
- 3960 rcu_read_unlock();
- 3961 bio_put(align_bi);
- 3962 return 0;
- 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看看条块内读这个故事的继集:
如果条块内读请求成功的话,那么就将原始请求bio done回去。如果失败的话,那么哼哼哼。。。
- 3835static void raid5_align_endio(struct bio *bi, int error)
- 3836{
- 3837 struct bio* raid_bi = bi->bi_private;
- 3838 struct mddev *mddev;
- 3839 struct r5conf *conf;
- 3840 int uptodate = test_bit(BIO_UPTODATE, &bi->bi_flags);
- 3841 struct md_rdev *rdev;
- 3842
- 3843 bio_put(bi);
- 3844
- 3845 rdev = (void*)raid_bi->bi_next;
- 3846 raid_bi->bi_next = NULL;
- 3847 mddev = rdev->mddev;
- 3848 conf = mddev->private;
- 3849
- 3850 rdev_dec_pending(rdev, conf->mddev);
- 3851
- 3852 if (!error && uptodate) {
- 3853 bio_endio(raid_bi, 0);
- 3854 if (atomic_dec_and_test(&conf->active_aligned_reads))
- 3855 wake_up(&conf->wait_for_stripe);
- 3856 return;
- 3857 }
- 3858
- 3859
- 3860 pr_debug("raid5_align_endio : io error...handing IO for a retry\n");
- 3861
- 3862 add_bio_to_retry(raid_bi, conf);
- 3863}
3843行,递减bio计数
3845行,找到保存的磁盘rdev指针
3850行,递减rdev的下发IO计数
3852行,如果读请求成功
3853行,将原始bio done回去
3854行,唤醒等待的控制命令,如果有的话
3862行,如果读请求失败,则加入阵列重试链表
- 3787
-
-
-
- 3791static void add_bio_to_retry(struct bio *bi,struct r5conf *conf)
- 3792{
- 3793 unsigned long flags;
- 3794
- 3795 spin_lock_irqsave(&conf->device_lock, flags);
- 3796
- 3797 bi->bi_next = conf->retry_read_aligned_list;
- 3798 conf->retry_read_aligned_list = bi;
- 3799
- 3800 spin_unlock_irqrestore(&conf->device_lock, flags);
- 3801 md_wakeup_thread(conf->mddev->thread);
- 3802}
重试链表是后进先出的,重试bio加入的是retry_read_aligned_list。这个链表我们已经在raid5d中照过面了。不记得也没有关系,我们再回头去看看:
- 4662 while ((bio = remove_bio_from_retry(conf))) {
- 4663 int ok;
- 4664 spin_unlock_irq(&conf->device_lock);
- 4665 ok = retry_aligned_read(conf, bio);
- 4666 spin_lock_irq(&conf->device_lock);
- 4667 if (!ok)
- 4668 break;
- 4669 handled++;
- 4670 }
4662行,从重试链表中取出一个bio
4665行,重试读请求
至于retry_aligned_read函数不会再走之前直接下发到磁盘的老路了,因为重试之前的方法就相当于一个死循环调用了,那接下来会做些什么呢?大家不妨猜想一下,提示一下,阵列是有数据冗余的。好了,下一节再来揭开重试读的真实面纱。