2010-3-7 sbull代码阅读

昨晚,我把sbull的代码,大体上看了一下,因为ldd这一章已经看过,此外对Linux驱动程序的结构有一定了解,所以基本上看得懂,但是也遇到了一些疑惑,今天上午,我再次阅读了ldd中的相关内容,主要如下:

1、弄清楚了以下几个概念:
请求函数(请求处理函数):负责执行块设备的读写请求,blk_init_queue函数负责分配请求队列,并绑定自旋锁和请求函数
在sbull的代码中,RM_SIMPLE模式和RM_FULL的不同就在于请求函数的不同。
请求完成函数:通知块设备子系统,设备已完成在一个I/O请求中的部分或全部扇区
主要函数有end_that_requset_first和end_that_request_end
“构造请求”的函数:在不使用请求队列的时候会用到,使用来对请求直接进行传输,或者把请求定向到其他设备
通过blk_queue_t_make_request()函数通知块设备子系统,驱动程序使用定制的函数。
2、request结构中的buffer成员的理解(现在仍不太理解)
sbull程序有三种模式:
enum {
RM_SIMPLE = 0, /* The extra-simple request function */
RM_FULL = 1, /* The full-blown version */
RM_NOQUEUE = 2, /* Use make_request */
};
其中RM_SIMPLE对应的是使用简单的请求处理函数,RM_FULL则使用了bio方面的内容,通过阅读两者的代码,我对buffer(request结构中的成员)产生了很大的疑惑。
ldd中写到“从本质上讲,一个request结构式作为一个bio结构的链表实现的”,request中的bio指针就负责指向bio链表,而bio结构则描述I/O请求。
RM_FULL对应的请求处理函数就是直接对bio进行操作完成I/O请求的,函数调用链为:
sbull_full_request--->sbull_xfer_request--->sbull_xfer_bio,sbull_xfer_bio的代码如下:

static int sbull_xfer_bio(struct sbull_dev *dev, struct bio *bio)
{
int i;
struct bio_vec *bvec;
sector_t sector = bio->bi_sector;

/* Do each segment independently. */

/*bio_for_each_segment 只是简单的在bi_io_vec数组中遍历每个没有被处理的入口*/
bio_for_each_segment(bvec, bio, i) {
/*__bio_kmap_atomic函数用来为缓冲区返回内核虚拟地址*/
char *buffer = __bio_kmap_atomic(bio, i, KM_USER0);
sbull_transfer(dev, sector, bio_cur_sectors(bio),buffer, bio_data_dir(bio) == WRITE);
sector += bio_cur_sectors(bio);
/*__bio_kunmap_atomic用于取消缓冲区映射*/
__bio_kunmap_atomic(bio, KM_USER0);
}
return 0; /* Always "succeed" */
}

从代码中,可以看到,在使用sbull_transfer传输数据前需要进行虚拟地址映射,之后还有取消映射,
但是RM_SIMPLE模式使用的请求处理函数只是直接使用request结构中的buffer成员就搞定了,我就不知道buffer到地指向什么。
首先,我在想,是不是request结构中的bio链表中各个bio的缓存区域都已经映射到有buffer指向的同一个虚存地址了呢?
在ldd的470页写到buffer是“要传输或者要接受数据的缓冲区指针。该指针在内核虚拟地址中,如果有需要,驱动程序可以直接引用它。”
因此,我觉得我的想法可能说对了,但是在478页又提到“这个成员不过是在当前bio中调用的bio_data的结果”,
#define bio_data(bio) (page_address(bio_page((bio))) + bio_offset((bio)))
#define bio_page(bio) bio_iovec((bio))->bv_page
如果是这样的话,buffer则只是一个bio对应的虚存地址,于是我又陷入了疑惑:
难道request链表只有一个bio?或者在sbull中的RM_SIMPLE模式是这样的?
或者块设备子系统有什么机制,将bio链表中每个bio对应的缓存映射到buffer?
这估计还是得请教学长。
3、end_that_request_last函数
以前我编译sbull程序时,报错end_that_request_last的参数少了(2.6.18的内核中该函数有两个参数,而ldd中该函数只有一个参数),
由于ldd中提到“如果有必要的话,还使用end_that_request函数”,我误以为该函数可要可不要于是直接注释掉了,后来发现该函数的作用是:
“通知任何等待已经完成请求的对象,并重复利用该request结构”,之所以编译后能正常使用是因为默认是RM_SIMPLE,根本不用改函数。

那么第二个参数到底写什么呢?
阅读源代码发现在end_that_request_last函数中,第二个参数只用到一次,就是用来设置error,如下所示:
if (end_io_error(uptodate))
error = !uptodate ? -EIO : uptodate;
于是,我觉得如果成功给个1就行了,end_that_request_last()也是用到了该函数,它在失败是传的是0(对于非文件系统操作),成功则传1。

你可能感兴趣的:(2010-3-7 sbull代码阅读)