执行泄流操作的函数是 blk_flush_plug_list()
, 代码逻辑如下:
void blk_flush_plug_list(struct blk_plug *plug, bool from_schedule){ struct request_queue *q; unsigned long flags; struct request *rq; LIST_HEAD(list); unsigned int depth; BUG_ON(plug->magic != PLUG_MAGIC); flush_plug_callbacks(plug, from_schedule); if (!list_empty(&plug->mq_list))blk_mq_flush_plug_list(plug, from_schedule); if (list_empty(&plug->list))return; list_splice_init(&plug->list, &list); /* 1.根据请求所在的q排序 , 这样可以优先处理同一设备的请求* 2.同一设备的请求根据请求的起始位置排序 , 这样可以优化处理性能*/ list_sort(NULL, &list, plug_rq_cmp); q = NULL; depth = 0; /** Save and disable interrupts here, to avoid doing it for every* queue lock we have to take.*/ local_irq_save(flags); while (!list_empty(&list)) {rq = list_entry_rq(list.next);list_del_init(&rq->queuelist);BUG_ON(!rq->q);/* 当 rq->q != q 时 , 说明一个队列的请求都已经取出 , 排查第一次 , * 当q为真时调用q->request_fn处理请求 , q->request_fn会调用* 具体的调度算法取出请求进行处理*/if (rq->q != q) {/** This drops the queue lock*/if (q)queue_unplugged(q, depth, from_schedule);q = rq->q;depth = 0;spin_lock(q->queue_lock);}/** Short-circuit if @q is dead*/if (unlikely(blk_queue_dying(q))) {__blk_end_request_all(rq, -ENODEV);continue;}/** rq is already accounted, so use raw insert** 在这里处理同一个队列的请求时 , 先将请求插入到调度队列和电梯* 哈希中 , 同一队列全部插入完成后 , 在上边会一次性处理*/if (rq->cmd_flags & (REQ_FLUSH | REQ_FUA))__elv_add_request(q, rq, ELEVATOR_INSERT_FLUSH);else__elv_add_request(q, rq, ELEVATOR_INSERT_SORT_MERGE);depth++; } /** This drops the queue lock* 处理最后一个设备的请求*/ if (q)queue_unplugged(q, depth, from_schedule); local_irq_restore(flags);}
2.3 Elevator合并bio生成新的request后 , 会插入到两个位置 。一是电梯调度算法的哈希表中 , 以request的结束位置为哈希值进行哈希 , 便于查找可以后向合并的请求 , 取值为 q->elevator->hash
;二是具体调度算法的调度队列 , 用于调度算法进行IO调度 , 以deadline调度算法为例 , 取值为 q->elevator->elevator_data->sort_list/fifo_expire
。调度算法派发请求后 , 请求会进入 q
的派发队列并同时从哈希和调度队列中移除 。执行Elevator合并的函数为 elv_merge()
, 主要包含电梯调度算法的后向合并以及具体IO调度算法的前向合并 , 并且为了提高合并效率 , 函数在最开始先检查最近一次合并成功的请求能否与bio进行合并 。代码逻辑如下:
int elv_merge(struct request_queue *q, struct request **req, struct bio *bio){ struct elevator_queue *e = q->elevator; struct request *__rq; int ret; /** Levels of merges:*nomerges:No merges at all attempted*noxmerges: Only simple one-hit cache try*merges:All merge tries attempted** 检查队列是否设置禁止合并的标记*/ if (blk_queue_nomerges(q))return ELEVATOR_NO_MERGE; /** First try one-hit cache.* 首先检测上次进行了合并的请求能否再次合并*/ if (q->last_merge && elv_bio_merge_ok(q->last_merge, bio)) {ret = blk_try_merge(q->last_merge, bio);if (ret != ELEVATOR_NO_MERGE) {*req = q->last_merge;return ret;} } if (blk_queue_noxmerges(q))return ELEVATOR_NO_MERGE; /** See if our hash lookup can find a potential backmerge.* 在哈希中查找可能后向合并的请求*/ __rq = elv_rqhash_find(q, bio->bi_sector); if (__rq && elv_bio_merge_ok(__rq, bio)) {*req = __rq;return ELEVATOR_BACK_MERGE; } /* 调用具体调度算法的接口判断能否进行前向合并 */ if (e->uses_mq && e->aux->ops.mq.request_merge)return e->aux->ops.mq.request_merge(q, req, bio); else if (!e->uses_mq && e->aux->ops.sq.elevator_merge_fn)return e->aux->ops.sq.elevator_merge_fn(q, req, bio); return ELEVATOR_NO_MERGE;}
经验总结扩展阅读
- Sentinel安装教程【Linux+windows】
- Linux 下模拟制作块设备并挂载
- CentOS6/7开机启动配置
- LinkedBlockingQueue详解
- 简析 Linux 的 CPU 时间
- springboot H2 linux下搭建使用
- 驱动开发:内核取ntoskrnl模块基地址
- 已验证 Linux安装中文字体
- 聊聊Linux中CPU上下文切换
- CentOS部署MySQL