generic_make_request 函数注释
/**
* generic_make_request - re-submit a bio to the block device layer for I/O
* @bio: The bio describing the location in memory and on the device.
*
* This is a version of submit_bio() that shall only be used for I/O that is
* resubmitted to lower level drivers by stacking block drivers. All file
* systems and other upper level users of the block layer should use
* submit_bio() instead.
* bio层的入口函数,负责把bio传递给通用块层;入参为初始化好的bio结构;并使用make_request_fn将请求置于驱动程序的 请求队列上。即把该bio传给设备对应的驱动程序;此时还未到IO调度层
*/
blk_qc_t generic_make_request(struct bio *bio)
{
/*
* bio_list_on_stack[0] contains bios submitted by the current
* make_request_fn.
* 包含当前make_request_fn提交的bio
* bio_list_on_stack[1] contains bios that were submitted before
* the current make_request_fn, but that haven't been processed yet.
* 包含当前make_request_fn函数之前提交过的但是尚未经过make_request_fn函数处理的bio;
*/
struct bio_list bio_list_on_stack[2];
blk_qc_t ret = BLK_QC_T_NONE;
if (!generic_make_request_checks(bio))//用于检查本次需要处理的bio
goto out;
/*
* We only want one ->make_request_fn to be active at a time, else
* stack usage with stacked devices could be a problem. So use
* current->bio_list to keep a list of requests submited by a
* make_request_fn function. current->bio_list is also used as a
* flag to say if generic_make_request is currently active in this
* task or not. If it is NULL, then no make_request is active. If
* it is non-NULL, then a make_request is active, and new requests
* should be added at the tail
* make_request_fn函数中可能会存在递归调用generic_make_request函数的情况,
* 下面处理的意义:
* 第一次调用generic_make_request函数时,current->bio_list为null,那么下面的if
* 语句不执行,继续执行下面的程序;
* 第二次调用generic_make_request函数时,current->bio_list不为null,那么此时执行
* if语句将bio结构加入到current->bio_list中后直接返回,此时上一层的generic_make_request
* 函数中执行了make_request_fn函数后,会去执行bio_list_pop函数再次获取bio_list
* 中的bio,因此可以将刚刚添加的bio结构拿出来进行处理。
*/
if (current->bio_list) {
bio_list_add(¤t->bio_list[0], bio);
goto out;
}
/* following loop may be a bit non-obvious, and so deserves some
* explanation.
* Before entering the loop, bio->bi_next is NULL (as all callers
* ensure that) so we have a list with a single bio.
* We pretend that we have just taken it off a longer list, so
* we assign bio_list to a pointer to the bio_list_on_stack,
* thus initialising the bio_list of new bios to be
* added. ->make_request() may indeed add some more bios
* through a recursive call to generic_make_request. If it
* did, we find a non-NULL value in bio_list and re-enter the loop
* from the top. In this case we really did just take the bio
* of the top of the list (no pretending) and so remove it from
* bio_list, and call into ->make_request() again.
*/
BUG_ON(bio->bi_next);//保证每次只处理一个bio
bio_list_init(&bio_list_on_stack[0]);//初始化bio_list_on_stack[0]
current->bio_list = bio_list_on_stack;//保证前面的if语句中能添加bio到bio_list_on_stack[0]
do {
struct request_queue *q = bio->bi_disk->queue;
if (likely(bio_queue_enter(bio) == 0)) {
struct bio_list lower, same;
/* Create a fresh bio_list for all subordinate requests */
bio_list_on_stack[1] = bio_list_on_stack[0];//将上一次提交的bio赋值给[1]
bio_list_init(&bio_list_on_stack[0]);//重新初始化stack[0]
ret = do_make_request(bio);//真正处理bio的函数
/* sort new bios into those for a lower level
* and those for the same level
*/
bio_list_init(&lower);
bio_list_init(&same);
//此时在调用make_request_fn时可能会有情况再次调用generic_make_request
//传递新的bio处理;此时新传入的bio都保存在bio_list链表结构中;
//下面的循环将依次将bio_list链表中的bio取出,放入same或者lower的链表中;
//bio_list_pop函数会将取出的bio从链表中删除,因此此循环结束后,
//bio_list_on_stack[0]将为空
while ((bio = bio_list_pop(&bio_list_on_stack[0])) != NULL)
if (q == bio->bi_disk->queue)
bio_list_add(&same, bio);
else
bio_list_add(&lower, bio);
/* now assemble so we handle the lowest level first */
//&bio_list_on_stack[1]中存放的是上次未来得及处理的bio请求
//下面的三个合并处理完成后,bio_list_on_stack[0]中将包含上次
//未来得及处理的bio以及新加入的bio;
bio_list_merge(&bio_list_on_stack[0], &lower);//bio合并
bio_list_merge(&bio_list_on_stack[0], &same);
bio_list_merge(&bio_list_on_stack[0], &bio_list_on_stack[1]);
}
//新一轮次,提取一个bio进行处理,并从链表中删除,直到bio为null退出循环;
bio = bio_list_pop(&bio_list_on_stack[0]);
} while (bio);
out:
return ret;
}
EXPORT_SYMBOL(generic_make_request);
以一个例子来说明整个函数的流程:(假设在处理bio的时候在if的语句判断中新加入了两个bio)
1、第一次调用generic_make_request,bio1入参,进入循环
第一次do循环开始:
2、调用make_request_fn函数处理bio1进入IO调度层,将bio1插入到请求队列q中,此时可能会递归调用generic_make_request,假设会在该过程中有两个bio传入,分别假设为bio2和bio3,那么这两个bio会加入到bio_list_on_stack[0]链表中。那么此时bio_list_on_stack[0]的情况存在两个bio,即bio2->bio3,如下图所示,bio_list_on_stack[1]为空;
3、此时经过while循环,主要是重新刷新bio_list_on_stack[0]链表,bio_list_pop函数后,bio_list_on_stack[0]为空,这两个bio2和bio3分别加入到same和lower链表中;开始调用bio_list_merge进行合并,合并完成后,bio_list_on_stack[0]链表包含bio2和bio3两个bio,bio2->bio3;
第一次do循环结束;
4、获取下一次要处理的bio2。利用bio_list_pop函数在bio_list_on_stack[0]提取bio2,提取后bio_list_on_stack[0]中只包含bio3,如下图所示:
第二次do循环开始:
5、首先将bio_list_on_stack[0]赋值给bio_list_on_stack[1],保存上次未来得及处理的bio3,然后初始化bio_list_on_stack[0];
6、再次调用generic_make_request处理bio2;假设此时无新的bio加入
7、while循环,提取bio3,并合并bio到bio_list_on_stack[0];那么此时bio_list_on_stack[0]中包含bio3;
此时就将bio_list_on_stack[1]中保存的上次未来得及处理的bio传递给bio_list_on_stack[0]链表;
第二次do循环结束;
8、提取bio3作为下次循环要处理的bio,此时bio_list_on_stack[0]变为空;
第三次do循环开始:
9、此时bio_list_on_stack[1]为空,说明没有未处理的bio了,假设在后面也没有新的bio加入;(加入中途还有其他的bio加入,那会按照上面的流程依次循环,每次循环处理一个bio,直到无法从bio_list_on_stack[0]中获取bio,说明无bio可处理了。)
10、处理bio3完成后,while循环未成立;
第三次do循环结束;
11、此时获取到的bio为null,do大循环结束,本次bio链表处理完成;