Bootstrap

向通用块层提交一个bio请求

向通用块层提交一个bio请求

当上层构建后io操作对应的bio请求后,通过submit_bio函数向通用块层提交一个 bio请求。

void submit_bio(int rw, struct bio *bio)
{
    bio->bi_rw |= rw;//io操作是读操作还是写操作
    ...
    generic_make_request(bio);//通用块层的主要入口点
}

void generic_make_request(struct bio *bio)
{
    struct bio_list bio_list_on_stack;

    /*对bio进行检查,查看bio的各种参数是否合理,如bio->bi_sector没有超过块设备的扇区数*/
    if (!generic_make_request_checks(bio))
        return;

    /*在一个进程内,同一时刻内核希望只有一个bio正在被处理,内核使用current->bio_list
    来维护当前进程提交的bio请求,如果链表不为空,则说明前面有bio正在被处理,只需要将新
    bio挂入链表即可返回(令我疑惑的是并未见加锁操作,是怎么避免竞态的呢?)*/
    if (current->bio_list) {
        bio_list_add(current->bio_list, bio);
        return;
    }

    //能走到这说明前面没有bio请求等待处理
    BUG_ON(bio->bi_next);
    bio_list_init(&bio_list_on_stack);//初始化链表,表头就是函数开头定义的那个局部变量
    //将表头赋给current->bio_list,后面来的bio都将挂在这个链表上
    current->bio_list = &bio_list_on_stack;                   

    do {
        //获取bio要发送的设备对应的请求队列
        struct request_queue *q = bdev_get_queue(bio->bi_bdev); 
        //将请求插入队列,所谓的io调度都是在该函数完成
        q->make_request_fn(q, bio);

        bio = bio_list_pop(current->bio_list);//从链表移除
    } while (bio);
    current->bio_list = NULL; /* deactivate */
    //该bio链表已经处理完了,下次再进入这个函数将创建新的链表
}

最终,调用q->make_request_fn方法进入块设备的I/O调度层,将bio请求插入请求队列q中。
大家一定很好奇q->make_request_fn函数到底是哪个函数

void blk_queue_make_request(struct request_queue *q, make_request_fn *mfn)
{
    ...
    q->make_request_fn = mfn;
    ...
}

struct request_queue * blk_init_allocated_queue(struct  request_queue *q, request_fn_proc *rfn, spinlock_t *lock)
{
    q->request_fn       = rfn;  //请求处理函数,负责处理请求队列中的每一个请求 
    q->prep_rq_fn       = NULL;
    ...
    blk_queue_make_request(q, blk_queue_bio);  //设置q->make_request_fn
    ...
    if (elevator_init(q, NULL)) {
        mutex_unlock(&q->sysfs_lock);
        return NULL;
    }//初始化调度算法
    ...
}

由上可知一般默认的q->make_request_fn就是blk_queue_bio函数。如果被访问的设备是一个有queue的块设备,那么系统会调用blk_queue_bio函数进行bio的调度合并。我们将在io调度层具体分析该函数。

;