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 /* FIXME calc properly */
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 /* far_copies must be 1 */
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: /* avoid 'may be unused' warnings */
3515 case geo_start: /* new when starting reshape - raid_disks not
3516 * updated yet. */
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 /* Calculate the number of sectors-per-device that will
3471 * actually be used, and set conf->dev_sectors and
3472 * conf->stride
3473 */
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 /* 'size' is now the number of chunks in the array */
3480 /* calculate "used chunks per device" */
3481 size = size * conf->copies;
3482
3483 /* We need to round up when dividing by raid_disks to
3484 * get the stride size.
3485 */
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函數剩余部分都是按步就班。小結一下,各種級別陣列運行函數建立與磁盤之間的關聯,建立數據流通道,申請數據流所需要的資源。不同的是,由於陣列級別差異,陣列與磁盤之間關聯不一樣,申請資源也不一樣。陣列已經運行起來了,那麼第一件事情就是同步了。下一節開始介紹陣列同步。
出處:http://blog.csdn.net/liumangxiong