目录
1. 前言
本专题文章是对多队列部分学习的笔记。主要以null_dev为例来研究多队列的工作机制, 为了能够凸显多队列的工作流程,只对block层做重点分析说明,对其它代码只做注释性说明。本文主要通过分析null blk块设备的初始化流程,来理解多队列的初始化,流程。
kernel版本:5.10
平台:arm64
注:
为方便阅读,正文标题采用分级结构标识,每一级用一个"-“表示,如:两级为”|- -", 三级为”|- - -“
2. null_init
static int __init null_init(void)
|--struct nullb *nullb;
| struct nullb_device *dev;
|--config_group_init(&nullb_subsys.su_group)
|--configfs_register_subsystem(&nullb_subsys)
|--register_blkdev(0, "nullb")
\--for (i = 0; i < nr_devices; i++)
dev = null_alloc_dev();
null_add_dev(dev);
null_init为null dev创建gendisk并注册进系统,创建软硬件队列及其映射关系,期间也会为null设备创建派发队列request_queue并初始化
-
config_group_init 初始化cofigfs group以供使用
-
configfs_register_subsystem: 将config group注册进configfs
-
null_alloc_dev 分配nullb_device,并初始化nullb_device的成员
-
register_blkdev完成了nullb块设备的注册,实际是添加到全局major_names数组中
-
null_add_dev为null设备创建gendisk并注册进系统,同时也创建了软硬队列的映射关系
static int null_add_dev(struct nullb_device *dev)
|--struct nullb *nullb;
|--null_validate_conf(dev)
| //tag_set嵌入nullb,因此分配nullb时连同nullb->tag_set一起分配
|--nullb = kzalloc_node(sizeof(*nullb), GFP_KERNEL, dev->home_node);
| nullb->dev = dev;
| dev->nullb = nullb
|
|--setup_queues(nullb)
|--if (dev->queue_mode == NULL_Q_MQ)
| null_init_tag_set(nullb, nullb->tag_set)
| nullb->q = blk_mq_init_queue_data(nullb->tag_set, nullb);
| else if (dev->queue_mode == NULL_Q_BIO)
| nullb->q = blk_alloc_queue(dev->home_node);
| rv = init_driver_queues(nullb);
|--nullb->q->queuedata = nullb;
|--blk_queue_logical_block_size(nullb->q, dev->blocksize)
|--blk_queue_physical_block_size(nullb->q, dev->blocksize)
|--null_config_discard(nullb);
\--null_gendisk_register(nullb)
|--struct gendisk *disk;
|--disk = nullb->disk = alloc_disk_node(1, nullb->dev->home_node);
|--set_capacity(disk, size);
|--if (queue_is_mq(nullb->q))
| disk->fops = &null_rq_ops;
| else
| disk->fops = &null_bio_ops;
\--add_disk(disk)
null_add_dev创建tag set并初始化,创建软硬件队列及其映射关系,为null设备创建request_queue并初始化,为null设备创建gendisk并注册进系统
-
setup_queues:主要为nullb_queue分配空间,个数由nr_cpu_ids来定义,同时也设置了队列深度
nullb->queue_depth = nullb->dev->hw_queue_depth -
null_init_tag_set:对nullb的内嵌blk_mq_tag_set进行初始化,为set->mq_map分配空间,用于保存软硬队列的映射关系建立软硬件队列的映射关系,set->mq_map数组下标为cpu编号,数组元素为硬队列号。根据队列深度为每个硬件队列分配bitmap,创建request。
-
blk_mq_init_queue_data创建request_queue并对其进行初始化,这里只考虑了多队列的情况
-
null_gendisk_register注册nullb磁盘设备。首先alloc_disk_node分配gendisk,add_disk 将gendisk注册进系统
|- -blk_mq_alloc_tag_set
static int null_init_tag_set(struct nullb *nullb, struct blk_mq_tag_set *set)
| //对nullb的内嵌blk_mq_tag_set进行初始化
|--set->ops = &null_mq_ops;
| set->nr_hw_queues = nullb ? nullb->dev->submit_queues : g_submit_queues;//初始化硬队列个数
| set->queue_depth = nullb ? nullb->dev->hw_queue_depth : g_hw_queue_depth;//初始化硬队列深度
| set->numa_node = nullb ? nullb->dev->home_node : g_home_node;
| set->cmd_size = sizeof(struct nullb_cmd);
| set->flags = BLK_MQ_F_SHOULD_MERGE;
| if (g_no_sched)
| set->flags |= BLK_MQ_F_NO_SCHED;
| if (g_shared_tag_bitmap)
| set->flags |= BLK_MQ_F_TAG_HCTX_SHARED;
| if ((nullb && nullb->dev->blocking) || g_blocking)
| set->flags |= BLK_MQ_F_BLOCKING;
\--blk_mq_alloc_tag_set(set)
| //分配set->nr_hw_queues个struct blk_mq_tags
|--blk_mq_realloc_tag_set_tags(set, 0, set->nr_hw_queues)
| //分配mq_map数组,用于软硬队列映射
|--for (i = 0; i < set->nr_maps; i++)
| set->map[i].mq_map = kcalloc_node(nr_cpu_ids,sizeof(set->map[i].mq_map[0]),
| GFP_KERNEL, set->numa_node);
| set->map[i].nr_queues = is_kdump_kernel() ? 1 : set->nr_hw_queues;
|--blk_mq_update_queue_map(set)
| |--if (set->nr_maps == 1)
| | set->map[HCTX_TYPE_DEFAULT].nr_queues = set->nr_hw_queues;
| |--blk_mq_map_queues(&set->map[HCTX_TYPE_DEFAULT]);
|--blk_mq_alloc_map_and_requests(set)
|--if (blk_mq_is_sbitmap_shared(set->flags))
atomic_set(&set->active_queues_shared_sbitmap, 0);
null_init_tag_set对内嵌blk_mq_tag_set进行初始化,为每种硬队列的mq_map数组分配空间,mq_map数组用于保存软硬队列的映射关系。set->map[i].mq_map数组中map[i]表示不同的硬件队列类型,mq_map数组下标为cpu编号,数组元素为cpu编号所对应的硬队列号。根据队列深度为每个硬件队列分配bitmap,每个bit代表一个request,并根据队列深度创建相应数目的request保存在硬队列的tags->static_rqs
-
对内嵌blk_mq_tag_set其进行初始化,其中包含了硬队列个数和硬队列深度,同时根据是否禁用IO调度器、是否共享tag bitmap、是否允许阻塞等设置blk_mq_tag_set的flags标志;
-
blk_mq_alloc_tag_set
blk_mq_alloc_tag_set分配的不是blk_mq_tag_set ,而是为全体硬队列分配blk_mq_tags指针数组,每个硬队列对应一个blk_mq_tags指针;分配数组set->mq_map用于维护软硬队列的映射,并建立软硬队列的映射关系,同时对每个硬件队列,根据队列深度为每个硬件队列分配bitmap,并根据队列深度创建相应数目的request。此tag set将与一个或多个request_queue进行关联,在分配过程过程中可能由于内存不足而失败,这样就要通过调整queue depth来进行调整后,重新分配,并更新queue depth
(1) blk_mq_realloc_tag_set_tags
分配set->nr_hw_queues个struct blk_mq_tags 指针,并将地址赋值给set->tags[],从此可以看出每一个硬队列对应一个blk_mq_tags。
注:此处分配的是struct blk_mq_tags指针,而非struct blk_mq_tags本身,blk_mq_alloc_rq_map会实际分配blk_mq_tags
(2) kcalloc_node
分配nr_cpu_ids个set->map[i].mq_map数组,用于保存软硬件队列的映射关系,其中set->nr_maps为:Number of elements in the @map array. A number in the range [1, HCTX_MAX_TYPES].
(3) blk_mq_update_queue_map
主要通过调用blk_mq_map_queues来建立的软硬件队列的映射关系,更新mq_map数组,数组下标为cpu编号,数组元素为cpu编号所对应的硬队列号
(4) blk_mq_alloc_map_and_requests
对每个硬件队列,根据队列深度为每个硬件队列分配bitmap,并根据队列深度创建相应数目的request保存在硬队列的tags->static_rqs
static int blk_mq_alloc_map_and_requests(struct blk_mq_tag_set *set)
|--do {
__blk_mq_alloc_rq_maps(set);
//???
set->queue_depth >>= 1;
} while (set->queue_depth);
根据硬件队列的tags数目(set->queue_depth)创建request
__blk_mq_alloc_rq_maps(set)
\--for (i = 0; i < set->nr_hw_queues; i++)
\--__blk_mq_alloc_map_and_request(set, i)
|--set->tags[hctx_idx] = blk_mq_alloc_rq_map(set, hctx_idx,
| set->queue_depth, set->reserved_tags, flags);
|--blk_mq_alloc_rqs(set, set->tags[hctx_idx], hctx_idx,set->queue_depth);
|--blk_mq_free_rq_map(set->tags[hctx_idx], flags);
__blk_mq_alloc_rq_maps对每个硬队列,根据队列深度来分配tag bitmap和request。通过for循环调用了__blk_mq_alloc_map_and_request对每个硬队列根据队列深度来分配tag bitmap和request,每个bit代表一个tag标记,用于标示硬件队列中的request
-
blk_mq_alloc_rq_map:分配blk_mq_tags结构体并初始化, 根据队列深度分配bitmap,根据tags->nr_tags数目和tags->nr_reserved_tags数目分配struct request *指针数组;
-
blk_mq_alloc_rqs:根据队列深度depth分配request, 分配的request指针最终保存到tags->static_rqs[i]
|- - -blk_mq_alloc_rq_map
struct blk_mq_tags *blk_mq_alloc_rq_map(struct blk_mq_tag_set *set,
unsigned int hctx_idx,
unsigned int nr_tags,//nr_tags are set->queue_Depth
unsigned int reserved_tags,
unsigned int flags)
|--node = blk_mq_hw_queue_to_node(&set->map[HCTX_TYPE_DEFAULT], hctx_idx);
|--tags = blk_mq_init_tags(nr_tags, reserved_tags, node, flags);
| |--struct blk_mq_tags *tags;
| |--tags = kzalloc_node(sizeof(*tags), GFP_KERNEL, node);
| | tags->nr_tags = nr_tags;
| | tags->nr_reserved_tags = reserved_tags;
| |--blk_mq_init_bitmap_tags(tags, node, alloc_policy)
| | |--unsigned int depth = tags->nr_tags - tags->nr_reserved_tags;
| | | bool round_robin = alloc_policy == BLK_TAG_ALLOC_RR;
| | |--bt_alloc(&tags->__bitmap_tags, depth, round_robin, node)
| | | bt_alloc(&tags->__breserved_tags, tags->nr_reserved_tags,round_robin, node)
| | | tags->bitmap_tags = &tags->__bitmap_tags;
| | | tags->breserved_tags = &tags->__breserved_tags;
|--tags->rqs = kcalloc_node(nr_tags, sizeof(struct request *),GFP_NOIO | __GFP_NOWARN | __GFP_NORETRY,node);
|--tags->static_rqs= kcalloc_node(nr_tags, sizeof(struct request *),GFP_NOIO | __GFP_NOWARN | __GFP_NORETRY,node);
blk_mq_alloc_rq_map:分配blk_mq_tags结构体并初始化, 根据队列深度set->queue_depth来分配bitmap,每个bit代表一个tag标记,用于标示硬件队列中的request。根据nr_tags分配struct request *指针数组
注意:此处并没有分配request,而是分配的struct request *指针,blk_mq_alloc_rqs中完成request的分配
-
kzalloc_node
分配blk_mq_tags结构体并初始化tags->nr_tags和tags->nr_reserved_tags -
blk_mq_init_bitmap_tag
根据实际使用的tag数目和保留的tag数目(两者之和为硬队列深度)分别分配bitmap -
kcalloc_node
根据nr_tags数目分配struct request *指针数组,分别保存在tags->rqs和tags->static_rqs,此处nr_tags为硬队列深度
|- - -blk_mq_alloc_rqs
int blk_mq_alloc_rqs(struct blk_mq_tag_set *set, struct blk_mq_tags *tags,
unsigned int hctx_idx, unsigned int depth)
|--node = blk_mq_hw_queue_to_node(&set->map[HCTX_TYPE_DEFAULT], hctx_idx);
|--INIT_LIST_HEAD(&tags->page_list);
| //rq_size is the size of the request plus driver payload, rounded to the cacheline size
|--rq_size = round_up(sizeof(struct request) + set->cmd_size,cache_line_size());
| //根据队列深度和rq大小确定要分配的内存大小,保存在left
| left = rq_size * depth;
|--for (i = 0; i < depth; )
int this_order = max_order;
struct page *page;
//确定buddy分配的阶数
while (this_order && left < order_to_size(this_order - 1))
this_order--;
//根据队列深度为request分配空间
do {
page = alloc_pages_node(node,GFP_NOIO | __GFP_NOWARN | __GFP_NORETRY | __GFP_ZERO,this_order);
if (page) break;
if (!this_order--) break;
if (order_to_size(this_order) < rq_size) break;
} while (1);
page->private = this_order;
list_add_tail(&page->lru, &tags->page_list);
p = page_address(page);
//todo保存了实际的request数目
entries_per_page = order_to_size(this_order) / rq_size;
to_do = min(entries_per_page, depth - i);
left -= to_do * rq_size;
//初始化tags->static_rqs数组,它的每一个数组元素保存了request指针
for (j = 0; j < to_do; j++)
struct request *rq = p;
tags->static_rqs[i] = rq;
blk_mq_init_request(set, rq, hctx_idx, node)
p += rq_size;
i++
blk_mq_alloc_rqs:根据队列深度depth分配request, 分配的request指针最终保存到tags->static_rqs[i]。注意此处分配request时,同时也分配了driver payload的空间用于存放cmd;
cmd_size: Number of additional bytes to allocate per request. The block driver owns these additional bytes.
注:上面blk_mq_alloc_rq_map只是分配了struct request *指针数组,此处blk_mq_alloc_rqs根据硬队列深度真实分配了队列的request
|- -blk_mq_init_queue_data
struct request_queue *blk_mq_init_queue_data(struct blk_mq_tag_set *set,void *queuedata)
|--struct request_queue *uninit_q, *q;
|--uninit_q = blk_alloc_queue(set->numa_node);
|--uninit_q->queuedata = queuedata;//对于nullb dev此处queuedata为nullb
|--q = blk_mq_init_allocated_queue(set, uninit_q, false)
|--return q;
分配request_queue,并对其进行初始化,期间会分配软队列和硬队列并初始化,并进一步建立软队列和硬队列的映射关系,如果支持调度器,则对调度器初始化
|- - -blk_alloc_queue
struct request_queue *blk_alloc_queue(int node_id)
|--struct request_queue *q;
|--q = kmem_cache_alloc_node(blk_requestq_cachep,GFP_KERNEL | __GFP_ZERO, node_id);
|--q->id = ida_simple_get(&blk_queue_ida, 0, 0, GFP_KERNEL);
|--bioset_init(&q->bio_split, BIO_POOL_SIZE, 0, BIOSET_NEED_BVECS);
|--q->backing_dev_info = bdi_alloc(node_id);
|--q->stats = blk_alloc_queue_stats()
|--atomic_set(&q->nr_active_requests_shared_sbitmap, 0)
|--timer_setup(&q->backing_dev_info->laptop_mode_wb_timer,laptop_mode_timer_fn, 0);
|--timer_setup(&q->timeout, blk_rq_timed_out_timer, 0);
|--INIT_WORK(&q->timeout_work, blk_timeout_work);
|--init_waitqueue_head(&q->mq_freeze_wq);
分配request_queue,每个request_queue对应一个bdi,此处也做了分配,同时定义了两个定时器:laptop_mode_wb_timer和timeout,其中laptop_mode_wb_timer主要用于page cache脏页的写回,在VFS基础学习笔记 - 7. page cache回写部分会详细说明
|- - -blk_mq_init_allocated_queue
struct request_queue *blk_mq_init_allocated_queue(struct blk_mq_tag_set *set,
struct request_queue *q,
ool elevator_init)
|--q->mq_ops = set->ops;
|--q->poll_cb = blk_stat_alloc_callback(blk_mq_poll_stats_fn,blk_mq_poll_stats_bkt,BLK_MQ_POLL_STATS_BKTS, q);
|--blk_mq_alloc_ctxs(q)//分配per cpu软队列
|--blk_mq_sysfs_init(q)
|--INIT_LIST_HEAD(&q->unused_hctx_list);
|--blk_mq_realloc_hw_ctxs(set, q);
|--INIT_WORK(&q->timeout_work, blk_mq_timeout_work);
|--blk_queue_rq_timeout(q, set->timeout ? set->timeout : 30 * HZ)
|--q->sg_reserved_size = INT_MAX;
|--INIT_DELAYED_WORK(&q->requeue_work, blk_mq_requeue_work);
|--INIT_LIST_HEAD(&q->requeue_list);
|--q->nr_requests = set->queue_depth;
|--q->poll_nsec = BLK_MQ_POLL_CLASSIC;
|--blk_mq_init_cpu_queues(q, set->nr_hw_queues);
|--blk_mq_add_queue_tag_set(set, q);
|--blk_mq_map_swqueue(q);
\--if (elevator_init)
elevator_init_mq(q);
blk_mq_init_allocated_queue主要对request_queue进行初始化,期间会分配软队列和硬队列并初始化,并进一步建立软队列和硬队列的映射关系
-
blk_stat_alloc_callback:分配并初始化统计回调函数描述符,统计回调函数针对request进行定时统计(TODO)
-
blk_mq_alloc_ctxs:分配软队列,软队列数目与cpu core的数目一致
-
blk_mq_sysfs_init:初始化 q->mq_kobj和软队列的kobjects
-
blk_mq_realloc_hw_ctxs:分配并初始化硬队列
-
INIT_WORK(&q->timeout_work, blk_mq_timeout_work):初始化超时请求的处理
-
INIT_DELAYED_WORK(&q->requeue_work, blk_mq_requeue_work);:重入队处理
-
blk_mq_init_cpu_queues:进一步初始化软硬队列
-
blk_mq_map_swqueue:建立软硬队列的映射关系,与blk_mq_map_queues不同,blk_mq_map_queues是通过map数组的mq_map数组通过索引和数组元素记录软硬队列的映射关系,其中数组索引为软队列编号,数组元素为硬队列编号,且映射关系保存在blk_mq_tag_set.map[i]->mq_map中;blk_mq_map_swqueue是基于blk_mq_map_queues创建的映射关系,进一步将软队列描述符指针保存在硬队列描述符,将软队列映射到硬队列的映射号index_hw保存在软队列描述符,它也与软队列索引号相同。如双核,一个硬队列的情况下blk_mq_map_swqueue打印如下:
blk_mq_map_swqueue: hctx(bfa29000), ctx(80e7a9c0), q->nr_queues(2),hctx->nr_ctx(1),
ctx->index_hw(0),hctx->ctxs0
blk_mq_map_swqueue: hctx(bfa29000), ctx(80e829c0), q->nr_queues(2),hctx->nr_ctx(2),
ctx->index_hw(1),hctx->ctxs1
如上就显示了双核下有两个软队列(地址为80e7a9c0和80e829c0),都映射到同一个硬队列(地址为bfa29000) -
elevator_init_mq:如果支持调度器,就要调度器初始化
|- - - -blk_mq_alloc_ctxs
static int blk_mq_alloc_ctxs(struct request_queue *q)
|--struct blk_mq_ctxs *ctxs
|--ctxs = kzalloc(sizeof(*ctxs), GFP_KERNEL);
|--ctxs->queue_ctx = alloc_percpu(struct blk_mq_ctx);
|--for_each_possible_cpu(cpu)
| struct blk_mq_ctx *ctx = per_cpu_ptr(ctxs->queue_ctx, cpu);
| ctx->ctxs = ctxs;
|--q->mq_kobj = &ctxs->kobj;
\--q->queue_ctx = ctxs->queue_ctx;
blk_mq_alloc_ctxs:分配软队列
|- - - -blk_mq_realloc_hw_ctxs
static void blk_mq_realloc_hw_ctxs(struct blk_mq_tag_set *set,struct request_queue *q)
|--struct blk_mq_hw_ctx **hctxs = q->queue_hw_ctx;
\--for (i = 0; i < set->nr_hw_queues; i++)
struct blk_mq_hw_ctx *hctx;
node = blk_mq_hw_queue_to_node(&set->map[HCTX_TYPE_DEFAULT], i);
if (hctxs[i] && (hctxs[i]->numa_node == node))
continue;
hctx = blk_mq_alloc_and_init_hctx(set, q, i, node);
|--hctx = blk_mq_alloc_hctx(q, set, node)
| |--hctx = kzalloc_node(blk_mq_hw_ctx_size(set), gfp, node);
| |--zalloc_cpumask_var_node(&hctx->cpumask, gfp, node)
| |--atomic_set(&hctx->nr_active, 0);//清零有效的request
| | atomic_set(&hctx->elevator_queued, 0);//清零队列中的request
| | //run_work用于对request的处理
| |--INIT_DELAYED_WORK(&hctx->run_work, blk_mq_run_work_fn);
| | INIT_LIST_HEAD(&hctx->dispatch)//hctx_list用于暂存ready的request
| | hctx->queue = q;
| | hctx->flags = set->flags & ~BLK_MQ_F_TAG_QUEUE_SHARED;
| | INIT_LIST_HEAD(&hctx->hctx_list);//用于连接到q->unused_hctx_list的链接件,表示未使用
| |--hctx->fq = blk_alloc_flush_queue(hctx->numa_node, set->cmd_size, gfp);
|--blk_mq_init_hctx(q, set, hctx, hctx_idx)
|--hctx->queue_num = hctx_idx;
|--hctx->tags = set->tags[hctx_idx];
|--set->ops->init_hctx(hctx, set->driver_data, hctx_idx)
|--blk_mq_init_request(set, hctx->fq->flush_rq, hctx_idx,hctx->numa_node)
hctxs[i] = hctx;
-
blk_mq_alloc_hctx分配硬队列,其中会对硬队列做部分初始化
-
blk_mq_init_hctx对硬队列进行初始化
|- - - -elevator_init_mq
elevator_init_mq(q)
| //默认是采用mq-deadline调度器
|--if (!q->required_elevator_features)
| e = elevator_get_default(q);
| else
| e = elevator_get_by_features(q);
| //初始化调度器
\--blk_mq_init_sched(q, e)
|--queue_for_each_hw_ctx(q, hctx, i)
| blk_mq_sched_alloc_tags(q, hctx, i)
| | //对每个硬队列分配tags, 保存在hctx->sched_tags
| |--hctx->sched_tags = blk_mq_alloc_rq_map(set, hctx_idx, q->nr_requests...)
| \--blk_mq_alloc_rqs(set, hctx->sched_tags, hctx_idx, q->nr_requests)
|--e->ops.mq.init_sched(q, e)
|--blk_mq_debugfs_register_sched(q)
\--queue_for_each_hw_ctx(q, hctx, i)
if (e->ops.mq.init_hctx)
e->ops.mq.init_hctx(hctx, i)
blk_mq_debugfs_register_sched_hctx(q, hctx)
elevator_init_mq调度器初始化,默认采用mq-deadline调度器, 根据硬队列个数分配了tags保存在了hctx->sched_tags,根据硬队列深度分配req, 保存在hctx->sched_tags->static_rqs[i];对dd调度器进行初始化
-
blk_mq_sched_alloc_tags: 对每个硬件队列,根据队列深度来分配tag bitmap, 保存到hctx->sched_tags,注意与blk_mq_alloc_rq_maps时保存在hctx->tags有所区别
-
blk_mq_alloc_rqs:根据队列深度分配request, 分配的request最终保存到hctx->sched_tags,->static_rqs[i]。注意此处分配request时,同时也分配了driver payload的空间用于存放cmd
-
e->ops.mq.init_sched:为dd_init_queue,分配elevator_queue,deadline_data,并对deadline_data数据描述符进行初始化
-
blk_mq_debugfs_register_sched:在sys/kernel/debug/下创建调试节点,对于null block为:/sys/kernel/debug/block/nullb0/sched
-
e->ops.mq.init_hctx: deadline未实现
-
blk_mq_debugfs_register_sched_hctx:在sys/kernel/debug/下创建调试节点,对于null block为
/sys/kernel/debug/block/nullb0/hctx0
3. 总结
null_init为null dev创建gendisk并注册进系统,创建软硬件队列及其映射关系,期间也会为null设备创建派发队列request_queue并初始化。对于块设备来讲,一般都会内嵌blk_mq_tag_set,初始化就是要对blk_mq_tag_set进行初始化,建立软硬队列的映射关系。对于每个硬队列都对应一个blk_mq_tags,它维护着rtag bitmap(每个bit代表一个tag request)和硬队列的所有request。
对于块设备来讲,所必须要做的基本工作是初始化时分配并添加gendisk:
alloc_disk_node分配gendisk,add_disk 将gendisk注册进系统
单就多队列来讲,一般块设备驱动初始化时都会调用blk_mq_alloc_tag_set和blk_mq_init_queue_data。
- blk_mq_alloc_tag_set 会初始化内嵌的blk_mq_tag_set,为每个硬队列分配blk_mq_tags;分配数组mq_map用于维护软硬队列的映射,并建立软硬队列的映射关系,同时对每个硬件队列,根据队列深度为每个硬件队列分配bitmap,并根据队列深度创建相应数目的request
- blk_mq_init_queue_data分配request_queue,并对其进行初始化,期间会分配软队列和硬队列并初始化,并进一步建立软队列和硬队列的映射关系。对于使能IO调度器的情形,则要对调度器进行初始化。