概述
看着题目是不是很奇怪,想不出好的名字,就这样将就吧。
前面bio bounce过程,bio的切分和合并,request的获取是为IO请求下发做准备工作。当这些准备工作完成后,才进入到真正的IO下发过程。之前在前面章节中介绍过,IO下发基本上有三条路径(可参考BLOCK层代码分析(3)IO下发概述):经过plug->mq_list往调度器或ctx->rq_lists下发、不经过plug->mq_list往调度器或ctx->rq_lists下发、直接往驱动下发。是否经过plug->mq_list即为是否支持PLUG/UNPLUG机制。
1. IO同步和异步下发方式
IO同步下发为直接调用queue_rq()下发,待上一个IO下发后再进行下一个IO下发;IO异步下发为通过hctx的workqueue执行run_work,可以将多个work分别下发到不同的cpu上(异步work执行的CPU为hctx->cpumask中的一个,轮询机制,每个cpu执行的WORK数目为BLK_MQ_CPU_WORK_BATCH(8)),分别执行run_work,每个run_work最终会执行queue_rq()。
2. 直接下发路径
对于一些SSD盘或NVME盘,不希望进行PLUG/UNPLUG,且不需要调度层的合并和排序,因此直接下发驱动。
直接下发会通过q->mq_ops->queue_rq()下发IO,但若由于资源不足(比如tag不够),会将当前IO请求放入hctx->dispatch链中。然后通过函数blk_mq_run_hw_queue()以同步方式执行hcxt上IO请求。
3. IO调度器下发路径
对于需要通过IO调度器中排序和合并的情况时,通过函数blk_mq_sched_insert_request(rq, false, true, true)将IO请求插入到IO调度器中(若没有定义IO调度器(调度器类型为none),将IO请求插入到ctx->rq_lists即每CPU的请求链表)。最终也通过函数blk_mq_run_hw_queue()以异步方式执行hctx中IO请求。
4. 函数blk_mq_run_hw_queue()
无论是直接下发IO还是通过IO调度器下发,最终通过函数blk_mq_run_hw_queue()下发IO。
函数blk_mq_run_hw_queue()简单流程如下所示:
- 检查参数,若是异步,执行workqueue的run_work;
- 若是同步,先检查hctx->dispatch上(当资源不足时放入hctx->dispatch)是否存在未完成的IO,若存在执行未完成的IO;
- 执行步骤(2)后仍有IO待完成,检查是否定义调度器类型,若定义,从调度器中取IO下发,若没有定义,从ctx->rq_lists中取IO下发;
- 若hctx->dispatch上没有未完成的IO,执行步骤(3)
5. 小结
准备好request后,可以直接往驱动下发IO,不经过调度层,但若遇到资源不足时,会将未完成的IO放入hctx->dispatch中,然后同步执行blk_mq_run_hw_queue();也可以根据调度层是否定义调度器,决定从调度器中或从ctx->rq_lists中取IO下发,以异步执行blk_mq_run_hw_queue()。若异步执行,通过workqueue执行run_work下发IO,若同步执行,先从hctx->dispatch中取IO执行,再根据是否定义调度器类型,决定是从调度器中取IO还是从ctx->rq_lists中取IO下发。
最后
以上就是眼睛大电话为你收集整理的BLOCK层代码分析(9)IO下发之IO下发的全部内容,希望文章能够帮你解决BLOCK层代码分析(9)IO下发之IO下发所遇到的程序开发问题。
如果觉得靠谱客网站的内容还不错,欢迎将靠谱客网站推荐给程序员好友。
发表评论 取消回复