block多队列分析 - 2. block多队列的初始化

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并初始化

  1. config_group_init 初始化cofigfs group以供使用

  2. configfs_register_subsystem: 将config group注册进configfs

  3. null_alloc_dev 分配nullb_device,并初始化nullb_device的成员

  4. register_blkdev完成了nullb块设备的注册,实际是添加到全局major_names数组中

  5. 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并注册进系统

  1. setup_queues:主要为nullb_queue分配空间,个数由nr_cpu_ids来定义,同时也设置了队列深度
    nullb->queue_depth = nullb->dev->hw_queue_depth

  2. null_init_tag_set:对nullb的内嵌blk_mq_tag_set进行初始化,为set->mq_map分配空间,用于保存软硬队列的映射关系建立软硬件队列的映射关系,set->mq_map数组下标为cpu编号,数组元素为硬队列号。根据队列深度为每个硬件队列分配bitmap,创建request。

  3. blk_mq_init_queue_data创建request_queue并对其进行初始化,这里只考虑了多队列的情况

  4. 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

  1. 对内嵌blk_mq_tag_set其进行初始化,其中包含了硬队列个数和硬队列深度,同时根据是否禁用IO调度器、是否共享tag bitmap、是否允许阻塞等设置blk_mq_tag_set的flags标志;

  2. 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

  1. blk_mq_alloc_rq_map:分配blk_mq_tags结构体并初始化, 根据队列深度分配bitmap,根据tags->nr_tags数目和tags->nr_reserved_tags数目分配struct request *指针数组;

  2. 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的分配

  1. kzalloc_node
    分配blk_mq_tags结构体并初始化tags->nr_tags和tags->nr_reserved_tags

  2. blk_mq_init_bitmap_tag
    根据实际使用的tag数目和保留的tag数目(两者之和为硬队列深度)分别分配bitmap

  3. 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进行初始化,期间会分配软队列和硬队列并初始化,并进一步建立软队列和硬队列的映射关系

  1. blk_stat_alloc_callback:分配并初始化统计回调函数描述符,统计回调函数针对request进行定时统计(TODO)

  2. blk_mq_alloc_ctxs:分配软队列,软队列数目与cpu core的数目一致

  3. blk_mq_sysfs_init:初始化 q->mq_kobj和软队列的kobjects

  4. blk_mq_realloc_hw_ctxs:分配并初始化硬队列

  5. INIT_WORK(&q->timeout_work, blk_mq_timeout_work):初始化超时请求的处理

  6. INIT_DELAYED_WORK(&q->requeue_work, blk_mq_requeue_work);:重入队处理

  7. blk_mq_init_cpu_queues:进一步初始化软硬队列

  8. 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)

  9. 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;
  1. blk_mq_alloc_hctx分配硬队列,其中会对硬队列做部分初始化

  2. 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调度器进行初始化

  1. blk_mq_sched_alloc_tags: 对每个硬件队列,根据队列深度来分配tag bitmap, 保存到hctx->sched_tags,注意与blk_mq_alloc_rq_maps时保存在hctx->tags有所区别

  2. blk_mq_alloc_rqs:根据队列深度分配request, 分配的request最终保存到hctx->sched_tags,->static_rqs[i]。注意此处分配request时,同时也分配了driver payload的空间用于存放cmd

  3. e->ops.mq.init_sched:为dd_init_queue,分配elevator_queue,deadline_data,并对deadline_data数据描述符进行初始化

  4. blk_mq_debugfs_register_sched:在sys/kernel/debug/下创建调试节点,对于null block为:/sys/kernel/debug/block/nullb0/sched

  5. e->ops.mq.init_hctx: deadline未实现

  6. 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。

  1. blk_mq_alloc_tag_set 会初始化内嵌的blk_mq_tag_set,为每个硬队列分配blk_mq_tags;分配数组mq_map用于维护软硬队列的映射,并建立软硬队列的映射关系,同时对每个硬件队列,根据队列深度为每个硬件队列分配bitmap,并根据队列深度创建相应数目的request
  2. blk_mq_init_queue_data分配request_queue,并对其进行初始化,期间会分配软队列和硬队列并初始化,并进一步建立软队列和硬队列的映射关系。对于使能IO调度器的情形,则要对调度器进行初始化。

参考文档

  1. linux Kernel block Layer Multi queue Multi Queue Core Point Analysis
  2. block多队列分析 - 1.概述
  • 3
    点赞
  • 6
    收藏
    觉得还不错? 一键收藏
  • 3
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值