linux block-plug形式的io合并代码分析

主要入口函数blk_attempt_plug_merge

内核版本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;
}

blk_rq_merge_ok

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;
}

blk_try_merge

/*将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;
}

bio_attempt_back_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;
}

bio_attempt_front_merge

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;
}

bio_attempt_discard_merge

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了。

你可能感兴趣的:(#,block,linux)