歡迎來到Linux教程網
Linux教程網
Linux教程網
Linux教程網
Linux教程網 >> Linux綜合 >> Linux內核 >> linux內核md源代碼解讀 六 介紹raid10陣列的運行

linux內核md源代碼解讀 六 介紹raid10陣列的運行

日期:2017/3/3 16:17:34   编辑:Linux內核

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

Copyright © Linux教程網 All Rights Reserved