generic_make_request函数处理bio流程分析

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(&current->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链表处理完成;

  • 1
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 2
    评论
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值