无线电爱好网

 找回密码
 注册

QQ登录

只需一步,快速开始

扫一扫,访问微社区

无线电爱好网 首页 技术应用 单片机 查看内容

Multi-queue 架构分析

2022-10-13 10:16| 发布者: 闪电| 查看: 6| 评论: 0

摘要: Linux上传统的块设备层(Block Layer)和IO调度器(如cfq)主要是针对HDD(hard disk drivers)设计的。我们知道,HDD设备的随机IO性能很差,吞吐量大约是几百IOPS(IOs per second),延迟在毫秒级,所以当时IO性能 ...


三、多队列框架代码分析



blk-mq代码在Linux-3.13(2014)内核中合入主线,在Linux-3.16中成为内核的一个完整特性,在Linux-5.0内核中,blk-sq代码(包括基于blk-sq的IO调度器,如cfq、noop)已被完全移除,MQ成为Linux Block layer的默认选项。下面基于Linux-5.6.0内核介绍blk-mq代码和关键数据结构。



request和tag分配


blk-mq中,request和tag是绑定的。首先,我们来看下两个与tag分配有关的重要数据结构--blk_mq_tags和blk_mq_tag_set。


  • blk_mq_tags,用于描述tag和request的集合,它的主要成员如下:


  • blk_mq_tag_set,用于描述与存储器件相关的tag集合,抽象了存储器件的IO特征,它的主要成员如下:


与SQ框架一样,MQ框架中使用request结构体来描述IO请求;不同的是,SQ使用内存池来分配request结构体(参见__get_request),在request往驱动派发时分配tag(参见blk_queue_start_tag),MQ中request和tag分配是绑定在一起的(参见blk_mq_get_request), 具体表现为:


  • request内存分配在块设备驱动初始化时完成(通过调用blk_mq_alloc_tag_set),避免IO发生时request内存分配带来的开销


  • tag 作为request(static_rqs/rqs数组)的索引


blk_mq_alloc_tag_set: 为一个或者多个请求队列分配tag和request集合(tag set可以是多个request queue共享的,例如UFS设备,一个host controller只有一个tag set,但器件可能划分成多个LU--Logical Unit,每个LU有单独的request queue, 这些不同的request queue共享一个tag set),主要流程如下:


  • 设置硬件队列数量(nr_hw_queues)和映射表数量(nr_maps)


  • 调用blk_mq_realloc_tag_set_tag 根据硬件队列数量扩展tags数组


  • 调用blk_mq_update_queue_map更新映射表(map: cpu id->hw queue id)


  • 调用blk_mq_alloc_rq_maps分配request和tag(队列深度可能会根据内存状态下调)


图7. scsi-mq驱动初始化时tag set分配流程


blk_mq_get_request: 为bio分配request。MQ中request占用的内存在块设备驱动初始化时分配完成(tags->static_rqs), tag作为数组的索引获取对应的request,因此MQ中分配request即分配tag(使用sbitmap标记对应tag的是否已被使用)。该函数的主要流程如下:


  • 调用blk_mq_get_ctx 获取当前cpu的软件队列(ctx)


  • 调用blk_mq_map_queue 找到软件队列(ctx)对应的硬件派发队列(hctx)


  • 对于配置了调度器的队列,调用limit_depth限制队列深度(影响tag获取);对于无调度器的队列,更新tag set的当前活跃队列数量(用于均分tag到不同request_queue)


  • 调用blk_mq_get_tag获取tag, 可能因当前无可用tag进入iowait状态


  • 调用blk_mq_rq_ctx_init 初始化tag对应的request(tags->static_rqs[tag])


图8. blk-mq bio提交时request分配流程



request_queue初始化


基于blk-mq的块设备驱动初始化时,通过调用blk_mq_init_queue初始化IO请求队列(request_queue)。例如,scsi-mq驱动中,每次添加scsi设备(scsi_device)时都会调用blk_mq_init_queue接口来初始化scsi设备的请求队列。
blk_mq_init_queue:初始化IO请求队列--request_queue。函数的主要流程如下:


  • 调用blk_alloc_queue_node分配请求队列的内存,分配的内存节点与设备连接的NUMA节点一致,避免远端内存访问问题。


  • 调用blk_mq_init_allocated_queue初始化分配的请求队列(request_queue),blk-mq的request_queue中包含两层队列,即percpu的软件队列(ctx)和与块设备硬件队列一一对应的硬件派发队列(hctx)。这个初始化过程主要包含下面几步:


1.设置队列的mq_ops(q->mq_ops)为set->ops (例如scsi对应的实现是scsi_mq_ops)


2.设置request超时时间,初始化timeout_work(处理函数是blk_mq_timeout_work)


3.设置队列的make_request回调为blk_mq_make_request (bio的提交时会用到)


4.分配和初始化percpu软件队列(ctx)


5.关联request_queue和块设备的tag set


6.更新软件队列(ctx)到硬件派发队列(hctx)的映射关系(map: ctx->hctx)


图9. scsi-mq驱动创建scsi device时初始化requst_queue流程



IO的提交(submit)


blk-mq中,通过调用blk_mq_make_request将上层提交的bio封装成request并提交到块设备层,它的主要流程如下:


  • 尝试与当前线程plug list(如果当前线程正在做IO plug)中的IO request合并


  • 尝试与当前cpu软件队列中的IO request合并(如果使能调度器,且调度器实现bio_merge接口,则调用这个接口尝试与调度器队列中的IO request合并)



路过

雷人

握手

鲜花

鸡蛋

QQ|关于本站|小黑屋|Archiver|手机版|无线电爱好网 ( 粤ICP备15040352号 ) 无线电爱好技术交流5 无线电爱好技术交流1无线电爱好技术交流9开关电源讨论群LED照明应用、电源无线电爱好技术交流4无线电爱好技术交流8无线电爱好技术交流10无线电爱好技术交流11

粤公网安备 44030702001224号

GMT+8, 2022-10-13 10:16 , Processed in 0.109201 second(s), 17 queries .

Powered by Discuz! X3.4 Licensed

Copyright © 2001-2020, Tencent Cloud.

返回顶部