内核版本4.19.1:
bool blk_attempt_plug_merge(struct request_queue *q, struct bio *bio,
unsigned int *request_count, struct request **same_queue_rq)
{
struct blk_plug *plug;
struct request *rq;
struct list_head *plug_list;
plug = current->plug;
if (!plug)
return false;
*request_count = 0;
if (q->mq_ops)
plug_list = &plug->mq_list;
else
plug_list = &plug->list;
//遍历当前进程所有的request
list_for_each_entry_reverse(rq, plug_list, queuelist) {
bool merged = false;
if (rq->q == q) {
/*
统计plug链表中与当前IO属于同一队列的请求个数
https://www.cnblogs.com/jrchyang/p/16795368.html
*/
(*request_count)++;
//只有block-mq在多个硬件队列情况下检查同一个队列中的rq,
//一个队列中应该只有一个这样的rq
if (same_queue_rq)
*same_queue_rq = rq;
}
/*
1:判断当前遍历到的req所属的request_queue是否等于q,相等才可以合并
2:检查req及bio属性是否支持合并(blk_rq_merge_ok)
*/
if (rq->q != q || !blk_rq_merge_ok(rq, bio))
continue;
//判断能不能合并,要是可以合并,该采用什么合并方式(ELEVATOR_BACK_MERGE/ELEVATOR_FRONT_MERGE)
switch (blk_try_merge(rq, bio)) {
case ELEVATOR_BACK_MERGE:
merged = bio_attempt_back_merge(q, rq, bio);
break;
case ELEVATOR_FRONT_MERGE:
merged = bio_attempt_front_merge(q, rq, bio);
break;
case ELEVATOR_DISCARD_MERGE:
merged = bio_attempt_discard_merge(q, rq, bio);
break;
default:
break;
}
if (merged) //合并成功这里返回true(bio已经合进去了,所以可以直接return)
return true; //合并成功返回true
}
return false;
}
bool blk_rq_merge_ok(struct request *rq, struct bio *bio)
{
/*
rq_mergeable - 判断rq是否有不允许合并的标记
bio_mergeable - 判断bio是否有不允许合并的标记
*/
if (!rq_mergeable(rq) || !bio_mergeable(bio))
return false;
if (req_op(rq) != bio_op(bio))
return false;
//不同的数据方向或已经开始,不要合并(比如一个读,一个写)
if (bio_data_dir(bio) != rq_data_dir(rq))
return false;
/* 指向的通用磁盘不同或者rq是特殊请求,不能合并 */
if (rq->rq_disk != bio->bi_disk || req_no_special_merge(rq))
return false;
/* 完整性保护不同的请求和bio,不能合并 */
if (blk_integrity_merge_bio(rq->q, rq, bio) == false)
return false;
/* REQ_WRITE_SAME请求,来自不同的page,不能合并(REQ_OP_WRITE_SAME这个操作只有一个page) */
if (req_op(rq) == REQ_OP_WRITE_SAME && !blk_write_same_mergeable(rq->bio, bio))
return false;
//Don't allow merge of different write hints, or for a hint with non-hint IO.
if (rq->write_hint != bio->bi_write_hint)
return false;
return true;
}
/*将bio合并到rq里面的bio当中,如果不能合并,则要根据tag再分配一个request*/
enum elv_merge blk_try_merge(struct request *rq, struct bio *bio)
{
//discard不做合并的要求
if (req_op(rq) == REQ_OP_DISCARD && queue_max_discard_segments(rq->q) > 1)
return ELEVATOR_DISCARD_MERGE;
/*
rq当中的bio的起始扇区加上扇区个数等于bio
的起始扇区,则把这个bio加到这个rq当中,rq当中的bio以链表形式管理
*/
else if (blk_rq_pos(rq) + blk_rq_sectors(rq) == bio->bi_iter.bi_sector)
return ELEVATOR_BACK_MERGE;
/*
rq当中的bio起始扇区减去bio的扇区个数等于bio的起始扇区
说明bio的结束扇区号等于rq-bio的起始扇区号
*/
else if (blk_rq_pos(rq) - bio_sectors(bio) == bio->bi_iter.bi_sector)
return ELEVATOR_FRONT_MERGE;
//不合并
return ELEVATOR_NO_MERGE;
}
bool bio_attempt_back_merge(struct request_queue *q, struct request *req, struct bio *bio)
{
const int ff = bio->bi_opf & REQ_FAILFAST_MASK;
//这里涉及具体的合并
if (!ll_back_merge_fn(q, req, bio))
return false;
trace_block_bio_backmerge(q, req, bio);
if ((req->cmd_flags & REQ_FAILFAST_MASK) != ff)
blk_rq_set_mixed_merge(req);
//更新一些变量的值
req->biotail->bi_next = bio;
req->biotail = bio;
req->__data_len += bio->bi_iter.bi_size;
req->ioprio = ioprio_best(req->ioprio, bio_prio(bio));
//这部分不是太懂,感觉和分区有关?
blk_account_io_start(req, false);
return true;
}
bool bio_attempt_front_merge(struct request_queue *q, struct request *req, struct bio *bio)
{
const int ff = bio->bi_opf & REQ_FAILFAST_MASK;
//这里涉及具体的合并代码
if (!ll_front_merge_fn(q, req, bio))
return false;
trace_block_bio_frontmerge(q, req, bio);
if ((req->cmd_flags & REQ_FAILFAST_MASK) != ff)
blk_rq_set_mixed_merge(req);
//向前合并更新起始扇区等变量的值
bio->bi_next = req->bio;
req->bio = bio;
req->__sector = bio->bi_iter.bi_sector;
req->__data_len += bio->bi_iter.bi_size;
req->ioprio = ioprio_best(req->ioprio, bio_prio(bio));
blk_account_io_start(req, false);
return true;
}
bool bio_attempt_discard_merge(struct request_queue *q, struct request *req, struct bio *bio)
{
unsigned short segments = blk_rq_nr_discard_segments(req);
if (segments >= queue_max_discard_segments(q))
goto no_merge;
if (blk_rq_sectors(req) + bio_sectors(bio) >
blk_rq_get_max_sectors(req, blk_rq_pos(req)))
goto no_merge;
req->biotail->bi_next = bio;
req->biotail = bio;
req->__data_len += bio->bi_iter.bi_size;
req->ioprio = ioprio_best(req->ioprio, bio_prio(bio));
req->nr_phys_segments = segments + 1;
blk_account_io_start(req, false);
return true;
no_merge:
req_set_nomerge(q, req);
return false;
}
具体的合并代码就不分析了,比较重要的是,合并过程当中还有考虑一些值的限制情况,在符合这些值的限制下,才会进行合并,如果合并不了,则要重新分配一个request了。