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