📄 raid5.c.txt
字号:
any questions,send email to netxiong@263.net
相关文件
********************守护线程************************
这是raid5的核心态守护线程,他扫描整那些可以被处理的stripe并处理他们。
(1)static void raid5d (void *data)
while (!list_empty(&conf->handle_list)) {
handle_stripe(sh); //最主要的功能就是处理stripe
release_stripe(sh);
}
这是一个内核线程,用来进行奇偶重构,重构到空闲的磁盘上
(2)static void raid5syncd (void *data)
md_do_sync(mddev,NULL) //主要调用的是md的操作函数
****************************************************
***********************基本的raid5的操作*************************
(1)static int raid5_run (mddev_t *mddev) //主要的运行函数,在md中调用
raid5_conf_t *conf;
mddev->private = kmalloc (sizeof (raid5_conf_t), GFP_KERNEL);
conf = mddev->private
//设置conf中的一些参数,也就相当于设置private中的参数,包含设置hash表
//,一些信号量,一些头指针
……
conf->thread = md_register_thread(raid5d, conf, name);
//注册一些必要的内核线程,如raid5syncd
if (!start_recovery && !(sb->state & (1 << MD_SB_CLEAN)))
conf->resync_thread = md_register_thread(raid5syncd, conf,name);
md_wakeup_thread(conf->resync_thread);
//如果需要同步,就唤醒相应的重构线程进行重构。
if (start_recovery) //如果需要重构
md_recover_arrays(); //调用重构函数
*****************************************************************
********************request的生成过程*****************************
static int raid5_make_request (mddev_t *mddev, int rw, struct buffer_head * bh)
这是整个raid5系统中最为重要的一个函数
new_sector = raid5_compute_sector //计算新的扇区号
sh = get_active_stripe(conf, new_sector, bh->b_size, read_ahead);
//得到活动的扇区
add_stripe_bh(sh, bh, dd_idx, rw); //这一步很重要,将bh加入到条
//纹中去
handle_stripe(sh);
release_stripe(sh); //处理这些扇区
******************************************************************
*******************buffer_head请求如何和stripe条纹关联的***********
static void add_stripe_bh (struct stripe_head *sh, struct buffer_head *bh,
int dd_idx, int rw)
将数据buffer加入到相应的链表中,sh->bh_read,sh->bh_write等中去。但不会加入到writen队列中去。这一步只是为系统的进一步处理提供方便。
*******************************************************************
**********************扇区的重定位********************************
static unsigned long raid5_compute_sector(unsigned long r_sector, unsigned
int raid_disks,
unsigned int data_disks, unsigned int * dd_idx,
unsigned int * pd_idx, raid5_conf_t *conf)
这个函数的作用是重新计算扇区的编号。
chunk_number = r_sector / sectors_per_chunk;
chunk_offset = r_sector % sectors_per_chunk;//线计算扇区处于那个块
stripe = chunk_number / data_disks;//在计算块属于那个条纹
switch (conf->algorithm) {
case ALGORITHM_LEFT_SYMMETRIC:
*pd_idx = data_disks - stripe % raid_disks;
*dd_idx = (*pd_idx + 1 + *dd_idx) % raid_disks;
//分别计算该扇区所处块在那个以盘上,以及同一个条纹中
//的校验盘的号码
new_sector = stripe * sectors_per_chunk + chunk_offset;
//重新计算扇区号分组以后的扇区号
屏幕洁图
###########################
raid5:buffer_size is 1024
raid5:chunk_size is 32768
raid5:bh->b_rsector is 4856
raid5:raid_disks is 3
raid5:algorithm is 2
raid5:chunk_number is 75
raid5:chunk_offset is 56
raid5:stripe is 37
raid5:new_sector is 2424
raid5:*pd_idx is 1
raid5:*dd_idx is 0
该阵列的结构图
0 1 2 盘号
0 32k,32k,32k
1 32k,32k,32k
2 32k,32k,32k
……
37 32k,32k,32k
dd pd
new_sector
###########################
*******************************************************************
**********************条纹的获取和释放******************************
stripe_head是有一定的数量的,每次使用stripe_head要申请,用完后要释放
分配实在函数get_active_stripe中
wait_event_lock_irq(conf->wait_for_stripe,conf->buffer_size,
conf->device_lock);
如果得不到相应的stripe_head,要在系统上等待
释放是在函数__release_stripe中
wake_up(&conf->wait_for_stripe);
唤醒等待的进程
********************************************************************
**************************处理stripe********************************
这是最为重要的一个函数,所有的stripe的处理都在这里完成
static void handle_stripe(struct stripe_head *sh)
int action[MD_SB_DISKS];//没一个磁盘都有一个行动的指标,也就是
//READ,WRITE,READA这些
clear_bit(STRIPE_HANDLE, &sh->state);//第一件事情就是清除这一位。因为之所以调用这个函数的原因就是由于设置了这一位的原因。
if (buffer_uptodate(bh) && sh->bh_read[i])
//如果cache中的数据已经被更新了,并且在bh_read中有队列在等待
请求,则可以直接将数据传出来,注意,这里使用了return_ok这个buffer链表,如果在cache中游数据的话,就复制到bh_read中,另外有一点要说明,由于可能有几个请求,就都复制出去,分别复制到每一个请求当中,这些请求由bh中的b_reqnext连结起来,然后从stripe_head中的bh_read中卸载下来(至一点很重要),这些卸载下来的buffer就链接在return_ok后面,注意,所有的盘的读数据都一次连结到return_ok后面的。
if (buffer_locked(bh)) locked++;
if (buffer_uptodate(bh)) uptodate++;
if (sh->bh_read[i]) to_read++;
if (sh->bh_write[i]) to_write++;
if (sh->bh_written[i]) written++;
if (!conf->disks[i].operational) {
failed++;
failed_num = i;
}//分别计算磁盘所对应的读写buffer和cache buffer的数量
if (failed > 1 && to_read+to_write)
{……
}//如果磁盘阵列中错误数量大于2,而且有数据处于读写状态,那么 //进行相应的处理
if (to_write) //如果是有数据要写的话
bh = sh->bh_cache[sh->pd_idx];
if ( written && ( (conf->disks[sh->pd_idx].operational && !buffer_locked(bh) && buffer_uptodate(bh)) || (failed == 1 && failed_num == sh->pd_idx)))
while (wbh) {
wbh2 = wbh->b_reqnext;
wbh->b_reqnext = return_ok;
return_ok = wbh;
wbh = wbh2;
}
注意这里的处理方法和bh_read完全不同,在bh_read中,使每一个bh都连结到return_ok中的,而这里,只是最后的一个连结到return_ok中,很显然,因为读写往磁盘上写的时候,只用写一次就够了。
********************************************************************
********************读操作的完成函数********************************
static void raid5_end_read_request (struct buffer_head * bh, int uptodate)
for (i=0 ; i<disks; i++)
if (bh == sh->bh_cache[i])
break; //先找到相应的读取得数据的buffer_head
buffers = sh->bh_read[i];
sh->bh_read[i] = NULL; //再把上层的读请求的buffer_head取下来。
set_bit(BH_Uptodate, &bh->b_state);//设置update位
raid5_end_buffer_read(buffers, bh);//将从盘中读取得数据送到buffers中
clear_bit(BH_Lock, &bh->b_state);//清除lock伟,这是bh中的位就是 //!lock,update了,也就是和磁盘中的数据是一致的
set_bit(STRIPE_HANDLE, &sh->state);//设置条纹的状态为stripe_handle,使得下面的release_stripe函数可以唤醒相应的守护进程来对数据进行处理。
__release_stripe(conf, sh);//释放条纹
static inline void raid5_end_buffer_read(struct buffer_head *blist, struct buffer_head *bh)
while (blist) {
struct buffer_head *new = blist;
blist = new->b_reqnext;
memcpy(new->b_data, bh->b_data, bh->b_size);
new->b_end_io(new, 1);
}//将bh中的数据复制到buffers中,并且,完成new的io操作。这一点很重要, //每一个buffer_head都要完成相应的end_io操作。注意,这里的每一个读请求的buffer的数据都复制成为cache中的数据。
********************************************************************
***************raid5系统中的一次读操作的流程************************
(1)首先,当读请求的buffer_head传导raid5_make_request中后,先经过compute_setctor进行对扇区的重定位,然后通过调用函数get_active_stripe得到相应的扇区所处的条纹,再将这个buffer_head加入到这个条纹中,通过函数add_stripe_bh。
(2)第一次进入handle_stripe函数,此时只有bh_read有数据,所以只会执行包含在条件if (to_read || (syncing && (uptodate+failed < disks)))中的语句。
如果cache中的buffer_head没有枷锁,也没有更新,说明是空的,可以使用,这是对这个buffer_head加锁,并把相应的action[i]设置为READ+1(为以后的判断方便),并且对整个条纹设置STRIPE_HANDLE锁。
(3)在handle_stripe函数的末尾,再对action[i]中的数据进行逐一的处理。如果是读操作的话,则设置bh->b_end_io的函数指针为raid5_end_read_request,设置bh的各种参数,调用generic_make_request进行实际的读操作。
(4)读写完毕之后,数据已经在bh中了,但是,请注意,这是的数据并不是在传入到make_request中的bh中,而是在sh->bh_cache中,也就是说在缓存中存放。并没有真正地传给用户。这是会调用bh->b_end_io,也就是调用raid5_end_read_request。
(5)注意,这是的raid5_end_read_request和函数raid5_make_request中的下一个函数realease_stripe是同步进行的。
(6)再raidt5_end_read_request中,系统会将从cache中得到的数据复制到真正请求的buffer_head中,并完成相应的请求,同时调用release_stripe来释放条纹,在释放条纹的过程中,会唤醒raid5d内核进程,再raid5d中会第二次调用handle_stripe。
(7)在进入handle_stripe中以后,什么也不作,因为所有的东西都已经设置好了,同时cache中的数据也是从磁盘中读的,并且是update and !lock,是最新的数据。所要做的所有的事情就是把stripe的STRIPE_HANDLE位清空。
总结
总体来看,RAID5系统中的数据传递是分两步的,第一步:当请求的读请求来的时候,首先将他链结到相应的条纹的相应的盘的bh_read队列中进行等待。然后,检查cache中的buffer_head,如果是已经更新的了,和磁盘上的数据一致的话,就直接将数据复制到读请求的buffer_head中,否则的话,就进行第二步:就吧cache中的buffer_head调用generice_make_request,启动普通的硬盘读写程序,将数据读入到cache中去,再返回函数raid5_end_read_request中吧数据从cache的buffer中复制到条纹中的相应的请求队列中去,有几个请求的buffer就都复制成为当前的值。然后,调用他们end_io函数结束请求。
********************************************************************
************************奇偶校验函数*****************************
static void compute_parity(struct stripe_head *sh, int method)
*****************************************************************
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -