block层:3. plug机制

Kernel源码笔记目录

block层:1. 提交io
block层:2. mq提交io
block层:3. plug机制
block层:4. 运行队列
block层:5. 请求分配
block层:6. tag机制
block层:7. 请求下发
block层:8. deadline调度器

plug 机制

源码基于5.10

blk_start_plug

void blk_start_plug(struct blk_plug *plug)
{
	struct task_struct *tsk = current;

	// 该进程已经有了plug
	if (tsk->plug)
		return;

	// 一些基本的初始化
	INIT_LIST_HEAD(&plug->mq_list);
	INIT_LIST_HEAD(&plug->cb_list);
	plug->rq_count = 0;
	plug->multiple_queues = false;
	plug->nowait = false;

	// 设置值
	tsk->plug = plug;
}

blk_finish_plug

void blk_finish_plug(struct blk_plug *plug)
{
	// 当前进程没有plug
	if (plug != current->plug)
		return;
	// 刷出plug
	blk_flush_plug_list(plug, false);

	// 当前进程plug置空
	current->plug = NULL;
}

blk_mq_plug

static inline struct blk_plug *blk_mq_plug(struct request_queue *q,
					   struct bio *bio)
{
	/*
	 * 对于普通块设备和读请求使用current->plug,如果blk_start_plug()还没执
	 * 行时它是NULL
	 */
	if (!blk_queue_is_zoned(q) || !op_is_write(bio_op(bio)))
		return current->plug;

	/* Zoned block device write operation case: do not plug the BIO */
	return NULL;
}

blk_add_rq_to_plug

static void blk_add_rq_to_plug(struct blk_plug *plug, struct request *rq)
{
	// 把请求加到末尾
	list_add_tail(&rq->queuelist, &plug->mq_list);
	// 请求数量增加
	plug->rq_count++;
	// 当前plug没有多个队列的请求 && 有多个请求
	if (!plug->multiple_queues && !list_is_singular(&plug->mq_list)) {
		struct request *tmp;

		// 第一个请求
		tmp = list_first_entry(&plug->mq_list, struct request,
						queuelist);
		// 如果rq的队列和第1个请求不是一个队列,则标记plug里的请求有多个队列
		if (tmp->q != rq->q)
			plug->multiple_queues = true;
	}
}

blk_flush_plug_list

void blk_flush_plug_list(struct blk_plug *plug, bool from_schedule)
{
	// 先调用回调列表
	flush_plug_callbacks(plug, from_schedule);

	// 请求列表不为空,则刷出请求
	if (!list_empty(&plug->mq_list))
		blk_mq_flush_plug_list(plug, from_schedule);
}

static void flush_plug_callbacks(struct blk_plug *plug, bool from_schedule)
{
	LIST_HEAD(callbacks);

	// 遍历plug的回调函数列表,这个循环主要是怕在调用回调期间再往回调链表上加回调
	while (!list_empty(&plug->cb_list)) {
		// 把plug的回调列表链到callbacks上,然后初始化cb_list
		// 这样做主要是为了在调用回调期间再往上加新的回调?
		list_splice_init(&plug->cb_list, &callbacks);

		// 遍历每个回调
		while (!list_empty(&callbacks)) {
			// 取出回调
			struct blk_plug_cb *cb = list_first_entry(&callbacks,
							  struct blk_plug_cb,
							  list);
			// 从callbacks列表上删除回调
			list_del(&cb->list);
			// 调用回调
			cb->callback(cb, from_schedule);
		}
	}
}

void blk_mq_flush_plug_list(struct blk_plug *plug, bool from_schedule)
{
	LIST_HEAD(list);

	// 请求列表为空
	if (list_empty(&plug->mq_list))
		return;
	// 把请求放到临时列表上,并初始化mq_list
	list_splice_init(&plug->mq_list, &list);

	// 请求数大于2 && plug里的请求有多个队列
	if (plug->rq_count > 2 && plug->multiple_queues)
		// 给链表排序,plug_rq_cmp是按照请求的ctx, hctx来排序
		// 这样能优化效率
		list_sort(NULL, &list, plug_rq_cmp);

	// 请求数量置0
	plug->rq_count = 0;

	do {
		struct list_head rq_list;
		// head_rq:请求头
		struct request *rq, *head_rq = list_entry_rq(list.next);
		struct list_head *pos = &head_rq->queuelist; /* skip first */
		// 硬上下文
		struct blk_mq_hw_ctx *this_hctx = head_rq->mq_hctx;
		// 软上下文
		struct blk_mq_ctx *this_ctx = head_rq->mq_ctx;
		// depth是给一个队列里插入请求的数量
		unsigned int depth = 1;

		// 遍历plug里的请求队列,算出同一个队列里要插入请求的数量
		// 因为depth刚开始是1,所以从第2个开始遍历
		list_for_each_continue(pos, &list) {
			// 获取请求
			rq = list_entry_rq(pos);

			// 请求的队列为空,什么情况?
			BUG_ON(!rq->q);
			// 上下文不一致,退出
			if (rq->mq_hctx != this_hctx || rq->mq_ctx != this_ctx)
				break;
			// 请求数量加1
			depth++;
		}

		// 这个函数会把[list, pos)的元素放到rq_list里,然后把list指向pos
		list_cut_before(&rq_list, &list, pos);
		trace_block_unplug(head_rq->q, depth, !from_schedule);
		// 给队列里插入list里的请求
		blk_mq_sched_insert_requests(this_hctx, this_ctx, &rq_list,
						from_schedule);
	} while(!list_empty(&list));
}

static int plug_rq_cmp(void *priv, const struct list_head *a,
		       const struct list_head *b)
{
	struct request *rqa = container_of(a, struct request, queuelist);
	struct request *rqb = container_of(b, struct request, queuelist);
	// 先比软队列
	if (rqa->mq_ctx != rqb->mq_ctx)
		return rqa->mq_ctx > rqb->mq_ctx;
	// 再比硬队列
	if (rqa->mq_hctx != rqb->mq_hctx)
		return rqa->mq_hctx > rqb->mq_hctx;

	// 软硬都相同,再比qos
	return blk_rq_pos(rqa) > blk_rq_pos(rqb);
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值