linux内核奇遇记之md源代码解读之六
转载请注明出处:http://blog.csdn.net/liumangxiong
raid10的run函数与raid5的run函数最大区别在于setup_conf,那就直接深入核心:
- 3540 static struct r10conf *setup_conf(struct mddev *mddev)
- 3541 {
- 3542 struct r10conf *conf = NULL;
- 3543 int err = -EINVAL;
- 3544 struct geom geo;
- 3545 int copies;
- 3546
- 3547 copies = setup_geo(&geo, mddev, geo_new);
- 3548
- 3549 if (copies == -2) {
- 3550 printk(KERN_ERR "md/raid10:%s: chunk size must be "
- 3551 "at least PAGE_SIZE(%ld) and be a power of 2.\n",
- 3552 mdname(mddev), PAGE_SIZE);
- 3553 goto out;
- 3554 }
- 3555
- 3556 if (copies < 2 || copies > mddev->raid_disks) {
- 3557 printk(KERN_ERR "md/raid10:%s: unsupported raid10 layout: 0x%8x\n",
- 3558 mdname(mddev), mddev->new_layout);
- 3559 goto out;
- 3560 }
- 3561
- 3562 err = -ENOMEM;
- 3563 conf = kzalloc(sizeof(struct r10conf), GFP_KERNEL);
- 3564 if (!conf)
- 3565 goto out;
- 3566
- 3567
- 3568 conf->mirrors = kzalloc(sizeof(struct raid10_info)*(mddev->raid_disks +
- 3569 max(0,-mddev->delta_disks)),
- 3570 GFP_KERNEL);
- 3571 if (!conf->mirrors)
- 3572 goto out;
- 3573
- 3574 conf->tmppage = alloc_page(GFP_KERNEL);
- 3575 if (!conf->tmppage)
- 3576 goto out;
- 3577
- 3578 conf->geo = geo;
- 3579 conf->copies = copies;
- 3580 conf->r10bio_pool = mempool_create(NR_RAID10_BIOS, r10bio_pool_alloc,
- 3581 r10bio_pool_free, conf);
- 3582 if (!conf->r10bio_pool)
- 3583 goto out;
- 3584
- 3585 calc_sectors(conf, mddev->dev_sectors);
- 3586 if (mddev->reshape_position == MaxSector) {
- 3587 conf->prev = conf->geo;
- 3588 conf->reshape_progress = MaxSector;
- 3589 } else {
- 3590 if (setup_geo(&conf->prev, mddev, geo_old) != conf->copies) {
- 3591 err = -EINVAL;
- 3592 goto out;
- 3593 }
- 3594 conf->reshape_progress = mddev->reshape_position;
- 3595 if (conf->prev.far_offset)
- 3596 conf->prev.stride = 1 << conf->prev.chunk_shift;
- 3597 else
- 3598
- 3599 conf->prev.stride = conf->dev_sectors;
- 3600 }
- 3601 spin_lock_init(&conf->device_lock);
- 3602 INIT_LIST_HEAD(&conf->retry_list);
- 3603
- 3604 spin_lock_init(&conf->resync_lock);
- 3605 init_waitqueue_head(&conf->wait_barrier);
- 3606
- 3607 conf->thread = md_register_thread(raid10d, mddev, "raid10");
- 3608 if (!conf->thread)
- 3609 goto out;
- 3610
- 3611 conf->mddev = mddev;
- 3612 return conf;
3547行,设置raid10布局,这个函数代码很简单,但意义很重要,特别是在处理读写流程里要对这个布局十分清楚。看setup_geo函数:
- 3498 enum geo_type {geo_new, geo_old, geo_start};
- 3499 static int setup_geo(struct geom *geo, struct mddev *mddev, enum geo_type new)
- 3500 {
- 3501 int nc, fc, fo;
- 3502 int layout, chunk, disks;
- 3503 switch (new) {
- 3504 case geo_old:
- 3505 layout = mddev->layout;
- 3506 chunk = mddev->chunk_sectors;
- 3507 disks = mddev->raid_disks - mddev->delta_disks;
- 3508 break;
- 3509 case geo_new:
- 3510 layout = mddev->new_layout;
- 3511 chunk = mddev->new_chunk_sectors;
- 3512 disks = mddev->raid_disks;
- 3513 break;
- 3514 default:
- 3515 case geo_start:
-
- 3517 layout = mddev->new_layout;
- 3518 chunk = mddev->new_chunk_sectors;
- 3519 disks = mddev->raid_disks + mddev->delta_disks;
- 3520 break;
- 3521 }
- 3522 if (layout >> 18)
- 3523 return -1;
- 3524 if (chunk < (PAGE_SIZE >> 9) ||
- 3525 !is_power_of_2(chunk))
- 3526 return -2;
- 3527 nc = layout & 255;
- 3528 fc = (layout >> 8) & 255;
- 3529 fo = layout & (1<<16);
- 3530 geo->raid_disks = disks;
- 3531 geo->near_copies = nc;
- 3532 geo->far_copies = fc;
- 3533 geo->far_offset = fo;
- 3534 geo->far_set_size = (layout & (1<<17)) ? disks / fc : disks;
- 3535 geo->chunk_mask = chunk - 1;
- 3536 geo->chunk_shift = ffz(~chunk);
- 3537 return nc*fc;
- 3538 }
raid10有近拷贝和远拷贝的设置,简单地说,近拷贝就是组成raid1的镜像磁盘数,远拷贝就是每个磁盘划分为几部分存镜像数据。
3503行,这里传进来的参数是geo_new,转到3509行。
3510行,raid10的layout,默认是0x102,即near_copies=2, far_copies=1。
3511行,chunk size。
3512行,数据盘个数。
3522-3526行,参数合法性检查。
3527行,计算near_copies。
3528行,计算far_copies。
3529行,计算far_offset。
3537行,返回拷贝数。
从上面的代码可以知道,raid10每一份可以有nc*fc份拷贝,但实际应用中考虑到磁盘的利用率,一般采用nc=2, fc=1。
回到setup_conf函数中,
3563行,申请struct r10conf内存空间。
3568行,申请struct raid10_info内存空间。
3574行,申请一个page页,用于读磁盘的临时空间。
3579行,设置数据拷贝数。
3580行,创建struct r10bio内存池,那为什么要有这样一个新的bio呢?原因是raid10大多数情况下io流分二个步骤,例如写同一份数据到两个磁盘,重建时先读数据再把数据写到另一个磁盘上,所以需要一个大的bio来跟踪io流过程。
3585行,计算磁盘实际用于阵列条带的扇区数和阵列存放远拷贝的跨度大小。假设我们用整个磁盘空间创建阵列raid10,但是由于每个磁盘空间大小有差别,阵列会按最小的磁盘空间来创建,这里实际用于阵列的磁盘空间将小于磁盘空间。再次,如果磁盘空间不是整数倍条块大小,这时多余部分还会被空闲出来。同样地,如果raid10远拷贝为2,这时磁盘条块数不能整除3,那么又有一部分磁盘空间空闲出来。正是因为如此,conf->dev_sectors会小于等于mddev->dev_sectors。理解了这些,那么看calc_sectors函数就容易了:
- 3468 static void calc_sectors(struct r10conf *conf, sector_t size)
- 3469 {
- 3470
-
-
-
- 3474
- 3475 size = size >> conf->geo.chunk_shift;
- 3476 sector_div(size, conf->geo.far_copies);
- 3477 size = size * conf->geo.raid_disks;
- 3478 sector_div(size, conf->geo.near_copies);
- 3479
- 3480
- 3481 size = size * conf->copies;
- 3482
- 3483
-
-
- 3486 size = DIV_ROUND_UP_SECTOR_T(size, conf->geo.raid_disks);
- 3487
- 3488 conf->dev_sectors = size << conf->geo.chunk_shift;
- 3489
- 3490 if (conf->geo.far_offset)
- 3491 conf->geo.stride = 1 << conf->geo.chunk_shift;
- 3492 else {
- 3493 sector_div(size, conf->geo.far_copies);
- 3494 conf->geo.stride = size << conf->geo.chunk_shift;
- 3495 }
- 3496 }
3470行,计算每个磁盘实际使用扇区数,并设置conf->dev_sectors和conf->stride。stride是什么意思呢?大步,跨幅。就是同一磁盘上每个far_copies之间的距离,这里有两个可能,一是远拷贝数据间隔存放,一是远拷贝数据相邻存放。conf->geo.far_offset不为0时表示相邻存放,为0时表示间隔存放,间隔多远呢?就由conf->geo.stride决定。
3475行,函数传入参数size为磁盘空间大小,这里转换为条块数。
3476行,除以远拷贝数,转换为单份数据空间大小。
3477-3478行,乘以数据盘数,再除以近拷贝数,转换为总数据空间大小(没有拷贝)。
3481行,乘以拷贝数,可用阵列空间大小。
3486行,除以数据盘数,每个磁盘用于阵列的实际使用空间大小。
3488行,乘以条块大小,单位由条块数转换为扇区数。
这里为什么又乘又除的呢?这就好比有一张长方形的纸,我们要用这张纸分出四个最大的正方形出来,那么就选个角按45度折起来,然后把之外的部分裁掉。
3490行,远拷贝是相邻存放的,那么跨幅就是一个条块。
3493行,远拷贝是间隔存放的,那么跨幅就是磁盘实际使用空间大小size除以远拷贝数。
再次返回到setup_conf函数中。
3586行,没有reshape操作,reshape等于MaxSector。
3601-3611行,conf初始化。
3607行,创建raid10d主线程。
这样setup_conf函数就结束了,run函数剩余部分都是按步就班。
小结一下,各种级别阵列运行函数建立与磁盘之间的关联,建立数据流通道,申请数据流所需要的资源。不同的是,由于阵列级别差异,阵列与磁盘之间关联不一样,申请资源也不一样。
阵列已经运行起来了,那么第一件事情就是同步了。下一节开始介绍阵列同步。