600字范文,内容丰富有趣,生活中的好帮手!
600字范文 > linux内核奇遇记之md源代码解读之六

linux内核奇遇记之md源代码解读之六

时间:2021-08-16 04:57:19

相关推荐

linux内核奇遇记之md源代码解读之六

linux内核奇遇记之md源代码解读之六 转载请注明出处:/liumangxiong

raid10的run函数与raid5的run函数最大区别在于setup_conf,那就直接深入核心:[cpp]view plaincopy3540staticstructr10conf*setup_conf(structmddev*mddev) 3541{ 3542structr10conf*conf=NULL; 3543interr=-EINVAL; 3544structgeomgeo; 3545intcopies; 3546 3547copies=setup_geo(&geo,mddev,geo_new); 3548 3549if(copies==-2){ 3550printk(KERN_ERR"md/raid10:%s:chunksizemustbe" 3551"atleastPAGE_SIZE(%ld)andbeapowerof2.\n", 3552mdname(mddev),PAGE_SIZE); 3553gotoout; 3554} 3555 3556if(copies<2||copies>mddev->raid_disks){ 3557printk(KERN_ERR"md/raid10:%s:unsupportedraid10layout:0x%8x\n", 3558mdname(mddev),mddev->new_layout); 3559gotoout; 3560} 3561 3562err=-ENOMEM; 3563conf=kzalloc(sizeof(structr10conf),GFP_KERNEL); 3564if(!conf) 3565gotoout; 3566 3567/*FIXMEcalcproperly*/ 3568conf->mirrors=kzalloc(sizeof(structraid10_info)*(mddev->raid_disks+ 3569max(0,-mddev->delta_disks)), 3570GFP_KERNEL); 3571if(!conf->mirrors) 3572gotoout; 3573 3574conf->tmppage=alloc_page(GFP_KERNEL); 3575if(!conf->tmppage) 3576gotoout; 3577 3578conf->geo=geo; 3579conf->copies=copies; 3580conf->r10bio_pool=mempool_create(NR_RAID10_BIOS,r10bio_pool_alloc, 3581r10bio_pool_free,conf); 3582if(!conf->r10bio_pool) 3583gotoout; 3584 3585calc_sectors(conf,mddev->dev_sectors); 3586if(mddev->reshape_position==MaxSector){ 3587conf->prev=conf->geo; 3588conf->reshape_progress=MaxSector; 3589}else{ 3590if(setup_geo(&conf->prev,mddev,geo_old)!=conf->copies){ 3591err=-EINVAL; 3592gotoout; 3593} 3594conf->reshape_progress=mddev->reshape_position; 3595if(conf->prev.far_offset) 3596conf->prev.stride=1<<conf->prev.chunk_shift; 3597else 3598/*far_copiesmustbe1*/ 3599conf->prev.stride=conf->dev_sectors; 3600} 3601spin_lock_init(&conf->device_lock); 3602INIT_LIST_HEAD(&conf->retry_list); 3603 3604spin_lock_init(&conf->resync_lock); 3605init_waitqueue_head(&conf->wait_barrier); 3606 3607conf->thread=md_register_thread(raid10d,mddev,"raid10"); 3608if(!conf->thread) 3609gotoout; 3610 3611conf->mddev=mddev; 3612returnconf;

3547行,设置raid10布局,这个函数代码很简单,但意义很重要,特别是在处理读写流程里要对这个布局十分清楚。看setup_geo函数:[cpp]view plaincopy3498enumgeo_type{geo_new,geo_old,geo_start}; 3499staticintsetup_geo(structgeom*geo,structmddev*mddev,enumgeo_typenew) 3500{ 3501intnc,fc,fo; 3502intlayout,chunk,disks; 3503switch(new){ 3504casegeo_old: 3505layout=mddev->layout; 3506chunk=mddev->chunk_sectors; 3507disks=mddev->raid_disks-mddev->delta_disks; 3508break; 3509casegeo_new: 3510layout=mddev->new_layout; 3511chunk=mddev->new_chunk_sectors; 3512disks=mddev->raid_disks; 3513break; 3514default:/*avoid'maybeunused'warnings*/ 3515casegeo_start:/*newwhenstartingreshape-raid_disksnot 3516*updatedyet.*/ 3517layout=mddev->new_layout; 3518chunk=mddev->new_chunk_sectors; 3519disks=mddev->raid_disks+mddev->delta_disks; 3520break; 3521} 3522if(layout>>18) 3523return-1; 3524if(chunk<(PAGE_SIZE>>9)|| 3525!is_power_of_2(chunk)) 3526return-2; 3527nc=layout&255; 3528fc=(layout>>8)&255; 3529fo=layout&(1<<16); 3530geo->raid_disks=disks; 3531geo->near_copies=nc; 3532geo->far_copies=fc; 3533geo->far_offset=fo; 3534geo->far_set_size=(layout&(1<<17))?disks/fc:disks; 3535geo->chunk_mask=chunk-1; 3536geo->chunk_shift=ffz(~chunk); 3537returnnc*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函数就容易了:[cpp]view plaincopy3468staticvoidcalc_sectors(structr10conf*conf,sector_tsize) 3469{ 3470/*Calculatethenumberofsectors-per-devicethatwill 3471*actuallybeused,andsetconf->dev_sectorsand 3472*conf->stride 3473*/ 3474 3475size=size>>conf->geo.chunk_shift; 3476sector_div(size,conf->geo.far_copies); 3477size=size*conf->geo.raid_disks; 3478sector_div(size,conf->geo.near_copies); 3479/*'size'isnowthenumberofchunksinthearray*/ 3480/*calculate"usedchunksperdevice"*/ 3481size=size*conf->copies; 3482 3483/*Weneedtoroundupwhendividingbyraid_disksto 3484*getthestridesize. 3485*/ 3486size=DIV_ROUND_UP_SECTOR_T(size,conf->geo.raid_disks); 3487 3488conf->dev_sectors=size<<conf->geo.chunk_shift; 3489 3490if(conf->geo.far_offset) 3491conf->geo.stride=1<<conf->geo.chunk_shift; 3492else{ 3493sector_div(size,conf->geo.far_copies); 3494conf->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函数剩余部分都是按步就班。 小结一下,各种级别阵列运行函数建立与磁盘之间的关联,建立数据流通道,申请数据流所需要的资源。不同的是,由于阵列级别差异,阵列与磁盘之间关联不一样,申请资源也不一样。 阵列已经运行起来了,那么第一件事情就是同步了。下一节开始介绍阵列同步。 转载请注明出处:/liumangxiong

本内容不代表本网观点和政治立场,如有侵犯你的权益请联系我们处理。
网友评论
网友评论仅供其表达个人看法,并不表明网站立场。