分类: LINUX
2009-08-14 09:17:09
同步的大流程是先读,后写。所以是分两个阶段,sync_request完成第一个阶段,sync_request_write完成第二个阶段。第一个阶段由MD发起,第二个阶段由守护进程发起。
sync_request函数在MD进行同步操作时调用,作用是对设备上指定的块(或者说条带)进行数据同步。入参go_faster指示是否应该更快速进行同步。该函数完成如下工作:
1. 如果同步缓存的mempool没有设置,则此时创建。
2. 如果同步位置不在磁阵内,可能已经完成同步,或者同步出错了。结束同步过程。
3. 如果没有指定bitmap,或者磁阵没有必要进行同步(对于RAID1而言,只有一个磁盘正常工作时设置了mddev->recovery_cp = MaxSector),或者bitmap(bitmap_start_sync)认为无需同步,则设置该段数据跳过,返回。
4. 如果当前设备请求队列忙于处理其他非同步类的访问,或者同步的速度已经比较快了,就稍微延时等待一下。
5. 在磁阵上设置障碍,保证磁阵的同步操作不受其他操作打扰。(该障碍在同步结束后end_sync_write中调用put_buf清除)
6. 申请raid-bio,将其状态设置为同步状态。
7. 对于磁阵中可用的磁盘,如果磁盘不为为In_sync状态,则就是同步过程写入的对象,否则就是同步过程中读的目标。如果没有读目标或者写目标,说明磁阵没有必要同步操作,返回。
8. 构建raid-bio中每个bio的bi_io_vec,将缓存页申请并添加到数组中。直到数组满或者达到一个同步操作要求的数据长度。
9. 如果是用户发起的同步(通过sysfs写入“repair”),则对所有读对象都发起读请求;如果不是,则只发起一个可读的磁盘的读就可以了。
同步读操作结束后,调用end_sync_read,如果读成功,该函数设置raid-bio的状态为update态。如果raid-bio中所有读都完成了,则调用reschedule_retry将raid-bio插入到磁阵的retry队列中(队列头为conf的retry_list,队列元素为raid-bio的retry_list),唤醒守护进程。
在守护进程中,如果发现磁阵的retry队列不为空,则取出raid-bio,如果该bio为同步状态,说明是同步产生的bio,调用sync_request_write进行处理。
在sync_request_write函数中,完成如下操作:
1. 如果同步是用户发起的。
a) 前面对所有可以读的对象都发出了读请求,而且也已经读完成了。只要其中一个读成功,则raid-bio的状态会被设置为Update,如果此时raid-bio仍然不为update,说明所有的读都失败了,此时设置所有读的盘为故障,并且结束同步过程,返回。
b) 找到读成功的第一个盘,并且其与从其他盘读的数据比较,如果发现有的盘和第一个读成功的盘数据不同,则将这些盘也列为写入对象,将读成功的第一个盘的数据拷贝到这些盘的对应bio中,继续下面的处理(其实是下面的3)。
2. 如果读失败,则进行恢复尝试,具体是这样的:尝试通过同步访问接口sync_page_io从所有可能的磁盘读出数据块;如果读成功,则对这个同步操作的读失败的磁盘,尝试进行一次写,进行一次读(都使用同步访问接口),如果都成功,则说明错误已经被纠正,否则将读失败的盘打入死牢。
3. 最后,将所有需要写的盘上的同步bio进行提交。设置bio结束操作为end_sync_write,返回。
4. 那么,读的数据要写入,应该由读的缓存区拷贝到写的缓存区中,在用户发起的同步中,我们看到是在1.a中执行的,但对于非用户发起的同步而言,这个操作是什么时候完成的呢?这个答案要在r1buf_pool_alloc函数中找,还记得函数sync_request的第一步创建的mempool吗?在
r1_bio = mempool_alloc(conf->r1buf_pool, GFP_NOIO);
这个语句中调用,mempoll会调用r1buf_pool_alloc进行实际的内存分配,具体可以去google一下mempool。
这个函数在分配raid-bio的缓存区时,除分配了其中的bio数组外,还分配了bio中的bvec页,对于用户发起的同步操作,是每个bio都分配了RESYNC_PAGES个页;对于非用户发起的同步操作,则是分配了RESYNC_PAGES个页,所有的bio的bvec都指向它们。这就是说,对于非用户发起的同步操作,读和写的缓存区是同一个,所以不需要单独的拷贝操作。
最后,同步写操作完成,调用end_sync_write函数,清除磁阵障碍,释放raid-bio缓存区,唤醒障碍上等待的任务。如果写出错,设置磁盘错误,并且调用bitmap_end_sync将本次同步操作的所有条带对应的bitmap设置为需要进行同步(Needed_mask)操作,下次同步时会尝试恢复这个写错误。