submit_bh(int rw, struct buffer_head * bh)
=>bio = bio_alloc(GFP_NOIO, 1);//申请1个bio
//初始化bio
=>bio->bi_sector = bh->b_blocknr * (bh->b_size >> 9);//需要数据传输的起始逻辑扇区号(相对于分区)
bio->bi_bdev = bh->b_bdev;
bio->bi_io_vec[0].bv_page = bh->b_page;
bio->bi_io_vec[0].bv_len = bh->b_size;
bio->bi_io_vec[0].bv_offset = bh_offset(bh);
bio->bi_vcnt = 1;//第一个bio只有一个vec,后续加入到集合里面
bio->bi_idx = 0;
bio->bi_size = bh->b_size;//需要传输数据的大小
bio->bi_end_io = end_bio_bh_io_sync;
bio->bi_private = bh;
=>bio_get(bio);
=>submit_bio(rw, bio);//下发块驱动
=>bio->bi_rw |= rw;
=>generic_make_request(bio);
=>if (current->bio_tail) {//如果已经有队列了,则插到队尾返回,防止嵌套
/* make_request is active */
*(current->bio_tail) = bio;
bio->bi_next = NULL;
current->bio_tail = &bio->bi_next;
return;
}
=>do {
==>current->bio_list = bio->bi_next;
==>if (bio->bi_next == NULL)
current->bio_tail = ¤t->bio_list;
==>else
bio->bi_next = NULL;
==>__generic_make_request(bio);
=>if (maxsector < nr_sectors || maxsector - nr_sectors < sector)
handle_bad_sector(bio);
=>do {
==>q = bdev_get_queue(bio->bi_bdev);
==>blk_partition_remap(bio);//bio的sector和b_size更新,相对于整个磁盘,因为卡驱动看不到分区
==>ret = q->make_request_fn(q, bio);
//在函数void blk_queue_make_request(struct request_queue * q, make_request_fn * mfn)里面赋值,即blk_queue_make_request(q, __make_request);
=>__make_request //switch各种调度算法
=>blk_queue_bounce(q, &bio);//回弹区,拷贝高端内存区的bio
=>elv_queue_empty(q)
=>el_ret = elv_merge(q, &req, bio);//如果能够合并到已有的request,则合并,否则创建新的request
==>switch (el_ret) {
case ELEVATOR_BACK_MERGE:
req->biotail->bi_next = bio;
req->biotail = bio;
req->nr_sectors = req->hard_nr_sectors += nr_sectors;
req->ioprio = ioprio_best(req->ioprio, prio);
drive_stat_acct(req, nr_sectors, 0);
goto out;//能够合并则不创建新的request
case ELEVATOR_FRONT_MERGE:
bio->bi_next = req->bio;
req->bio = bio;
req->buffer = bio_data(bio);
req->current_nr_sectors = bio_cur_sectors(bio);
req->hard_cur_sectors = req->current_nr_sectors;
req->sector = req->hard_sector = bio->bi_sector;
req->nr_sectors = req->hard_nr_sectors += nr_sectors;
req->ioprio = ioprio_best(req->ioprio, prio);
drive_stat_acct(req, nr_sectors, 0);
if (!attempt_front_merge(q, req))
elv_merged_request(q, req, el_ret);
goto out;//能够合并则不创建新的request
default://不能合并则创建新的request
=>init_request_from_bio(req, bio);
=>req->errors = 0;
req->hard_sector = req->sector = bio->bi_sector;
req->hard_nr_sectors = req->nr_sectors = bio_sectors(bio);
req->current_nr_sectors = req->hard_cur_sectors = bio_cur_sectors(bio);
req->nr_phys_segments = bio_phys_segments(req->q, bio);
req->nr_hw_segments = bio_hw_segments(req->q, bio);
req->buffer = bio_data(bio); /* see ->buffer comment above */
req->bio = req->biotail = bio;
req->ioprio = bio_prio(bio);
req->rq_disk = bio->bi_bdev->bd_disk;
req->start_time = jiffies;
=>blk_plug_device(q)//详见激活设备,定时唤醒块设备驱动处理 http://blog.csdn.net/shipinsky/article/details/79102293
=>add_request(q, req);//将新的request加入到队列
out://bio能合并的话,跳过request的创建
=>if (sync)//如果紧急,则立即唤醒
__generic_unplug_device(q);
} while (ret);
==>bio = current->bio_list;//从队尾取插入的bio继续处理
} while (bio);
==>current->bio_tail = NULL;
=>bio_put(bio);
参考
向通用块层提交IO请求
http://blog.chinaunix.net/uid-28897358-id-3776947.html
块设备层分析
http://www.360doc.com/content/16/0510/02/478627_557720637.shtml
linux块设备读写流程(一篇很难的块设备文件 没怎么看懂)
http://blog.csdn.net/mbh_1991/article/details/9455113
generic_make_request函数初探
https://blog.csdn.net/huoshanbaofa123/article/details/51163708
__make_request()函数分析
https://blog.csdn.net/hs794502825/article/details/8719034
generic_make_request的总结性分析
https://blog.csdn.net/guogaofeng1219/article/details/5545439
Linux 性能优化之 IO 子系统
http://blog.sina.com.cn/s/blog_416656f70102vwld.html
Linux性能调优之I/O调优
https://blog.csdn.net/chuxuan909/article/details/72550729