blk-mq: Add support for multiple queue maps
Subsystems (10)
Jens Axboe
maintainerv3 cover blk-mq: Add support for multiple queue maps
This series adds support for multiple queue maps for blk-mq. Since blk-mq was introduced, it's only support a single queue map. This means you can have 1 set of queues, and the mapping purely depends on what CPU an IO originated from. With this patch set, drivers can implement mappings that depend on both CPU and request type - and they can have multiple sets of mappings.
NVMe is used as a proof of concept. It adds support for a separate write queue set. One way to use this would be to limit the number of write queues to favor reads, since NVMe does round-robin service of queues. An easy extension of this would be to add multiple sets of queues, for prioritized IO.
NVMe also uses this feature to finally make the polling work efficiently, without triggering interrupts. This both increases performance (and decreases latency), at a lower system load. At the same time it's more flexible, as you don't have to worry about IRQ coalescing and redirection to avoid interrupts disturbing the workload. This is how polling should have worked from day 1.
This is on top of my mq-conversions branch. It can also be bound in my mq-maps branch.
Changes since v2:
- Address review comments (mostly code comments)
- Add rq->mq_hctx cache
- Cleanup/improve blk_mq_sched_insert_requests()
- Harden the IRQ affinity sets feature
- Fix assignment of hardware queue type if already mapped
- Improve plug sorting for multiple maps
Changes since v1:
- Ensure irq_calc_affinity_vectors() doesn't return more than 'maxvec'
- Rebase on top of current mq-conversions series
block/blk-flush.c | 11 +- block/blk-mq-cpumap.c | 19 +-- block/blk-mq-debugfs.c | 2 +- block/blk-mq-pci.c | 10 +- block/blk-mq-rdma.c | 4 +- block/blk-mq-sched.c | 12 +- block/blk-mq-sched.h | 2 +- block/blk-mq-sysfs.c | 10 ++ block/blk-mq-tag.c | 12 +- block/blk-mq-virtio.c | 8 +- block/blk-mq.c | 237 +++++++++++++++++++----------- block/blk-mq.h | 45 ++++-- block/blk.h | 6 +- block/kyber-iosched.c | 6 +- drivers/block/virtio_blk.c | 2 +- drivers/nvme/host/pci.c | 263 ++++++++++++++++++++++++++++++---- drivers/pci/msi.c | 14 ++ drivers/scsi/qla2xxx/qla_os.c | 5 +- drivers/scsi/scsi_lib.c | 2 +- drivers/scsi/smartpqi/smartpqi_init.c | 3 +- drivers/scsi/virtio_scsi.c | 3 +- fs/block_dev.c | 2 + fs/direct-io.c | 2 + fs/iomap.c | 9 +- include/linux/blk-mq-pci.h | 4 +- include/linux/blk-mq-virtio.h | 4 +- include/linux/blk-mq.h | 33 ++++- include/linux/blk_types.h | 4 +- include/linux/blkdev.h | 3 +- include/linux/interrupt.h | 4 + kernel/irq/affinity.c | 40 ++++-- 31 files changed, 582 insertions(+), 199 deletions(-)
Signature · 1 line
Jens Axboe
Jens Axboe
maintainerPATCH v3 1/16 blk-mq: kill q->mq_map
It's just a pointer to set->mq_map, use that instead. Move the assignment a bit earlier, so we always know it's valid.
--- block/blk-mq.c | 13 ++++--------- block/blk-mq.h | 4 +++- include/linux/blkdev.h | 2 -- 3 files changed, 7 insertions(+), 12 deletions(-)
block/blk-mq.c +4 -9
@@ -2321,7 +2321,7 @@ static void blk_mq_map_swqueue(struct request_queue *q)* If the cpu isn't present, the cpu is mapped to first hctx. */ for_each_possible_cpu (i) { - hctx_idx = q->mq_map[i]; + hctx_idx = set->mq_map[i];/* unmapped hw queue can be remapped after CPU topo changed */ if (!set->tags[hctx_idx] && !__blk_mq_alloc_rq_map (set, hctx_idx)) {
@@ -2331,7 +2331,7 @@ static void blk_mq_map_swqueue(struct request_queue *q)* case, remap the current ctx to hctx[0] which * is guaranteed to always have tags allocated */ - q->mq_map[i] =0 ; + set->mq_map[i] =0 ; } ctx =per_cpu_ptr (q->queue_ctx, i);
@@ -2429,8 +2429,6 @@ static void blk_mq_del_queue_tag_set(struct request_queue *q)static void blk_mq_add_queue_tag_set (struct blk_mq_tag_set *set,struct request_queue *q) { - q->tag_set = set; -mutex_lock (&set->tag_list_lock);/*
@@ -2467,8 +2465,6 @@ void blk_mq_release(struct request_queue *q)kobject_put (&hctx->kobj); } - q->mq_map =NULL ; -kfree (q->queue_hw_ctx);/*
@@ -2588,7 +2584,7 @@ static void blk_mq_realloc_hw_ctxs(struct blk_mq_tag_set *set,int node;struct blk_mq_hw_ctx *hctx; - node =blk_mq_hw_queue_to_node (q->mq_map, i); + node =blk_mq_hw_queue_to_node (set->mq_map, i);/* * If the hw queue has been mapped to another numa node, * we need to realloc the hctx. If allocation fails, fallback
@@ -2665,8 +2661,6 @@ struct request_queue *blk_mq_init_allocated_queue(struct blk_mq_tag_set *set,if (!q->queue_hw_ctx) goto err_percpu; - q->mq_map = set->mq_map; -blk_mq_realloc_hw_ctxs (set, q);if (!q->nr_hw_queues) goto err_hctxs;
@@ -2675,6 +2669,7 @@ struct request_queue *blk_mq_init_allocated_queue(struct blk_mq_tag_set *set,blk_queue_rq_timeout (q, set->timeout ? set->timeout :30 *HZ ); q->nr_queues = nr_cpu_ids; + q->tag_set = set; q->queue_flags |=QUEUE_FLAG_MQ_DEFAULT ;
block/blk-mq.h +3 -1
@@ -75,7 +75,9 @@ extern int blk_mq_hw_queue_to_node(unsigned int *map, unsigned int);static inline struct blk_mq_hw_ctx *blk_mq_map_queue (struct request_queue *q,int cpu) { -return q->queue_hw_ctx[q->mq_map[cpu]]; +struct blk_mq_tag_set *set = q->tag_set; + +return q->queue_hw_ctx[set->mq_map[cpu]]; }/*
include/linux/blkdev.h +0 -2
@@ -412,8 +412,6 @@ struct request_queue {const struct blk_mq_ops *mq_ops; -unsigned int *mq_map; -/* sw queues */ struct blk_mq_ctx __percpu *queue_ctx;unsigned int nr_queues;
Signature · 1 line
2.17.1
Re: [PATCH 01/16] blk-mq: kill q->mq_map
Jens Axboe
maintainerPATCH v3 2/16 blk-mq: abstract out queue map
In reply to Jens AxboeThis is in preparation for allowing multiple sets of maps per queue, if so desired.
--- block/blk-mq-cpumap.c | 10 ++++---- block/blk-mq-pci.c | 10 ++++---- block/blk-mq-rdma.c | 4 ++-- block/blk-mq-virtio.c | 8 +++---- block/blk-mq.c | 34 ++++++++++++++------------- block/blk-mq.h | 8 +++---- drivers/block/virtio_blk.c | 2 +- drivers/nvme/host/pci.c | 2 +- drivers/scsi/qla2xxx/qla_os.c | 5 ++-- drivers/scsi/scsi_lib.c | 2 +- drivers/scsi/smartpqi/smartpqi_init.c | 3 ++- drivers/scsi/virtio_scsi.c | 3 ++- include/linux/blk-mq-pci.h | 4 ++-- include/linux/blk-mq-virtio.h | 4 ++-- include/linux/blk-mq.h | 15 +++++++++--- 15 files changed, 64 insertions(+), 50 deletions(-)
block/blk-mq-cpumap.c +5 -5
@@ -30,10 +30,10 @@ static int get_first_sibling(unsigned int cpu)return cpu; } -int blk_mq_map_queues (struct blk_mq_tag_set *set) +int blk_mq_map_queues (struct blk_mq_queue_map *qmap) { -unsigned int *map = set->mq_map; -unsigned int nr_queues = set->nr_hw_queues; +unsigned int *map = qmap->mq_map; +unsigned int nr_queues = qmap->nr_queues;unsigned int cpu, first_sibling;for_each_possible_cpu (cpu) {
@@ -62,12 +62,12 @@ EXPORT_SYMBOL_GPL(blk_mq_map_queues);* We have no quick way of doing reverse lookups. This is only used at * queue init time, so runtime isn't important. */ -int blk_mq_hw_queue_to_node (unsigned int *mq_map,unsigned int index) +int blk_mq_hw_queue_to_node (struct blk_mq_queue_map *qmap,unsigned int index) {int i;for_each_possible_cpu (i) { -if (index == mq_map[i]) +if (index == qmap->mq_map[i])return local_memory_node (cpu_to_node (i)); }
block/blk-mq-pci.c +5 -5
@@ -31,26 +31,26 @@* that maps a queue to the CPUs that have irq affinity for the corresponding * vector. */ -int blk_mq_pci_map_queues (struct blk_mq_tag_set *set,struct pci_dev *pdev, +int blk_mq_pci_map_queues (struct blk_mq_queue_map *qmap,struct pci_dev *pdev,int offset) {const struct cpumask *mask;unsigned int queue, cpu; -for (queue =0 ; queue < set->nr_hw_queues; queue++) { +for (queue =0 ; queue < qmap->nr_queues; queue++) { mask =pci_irq_get_affinity (pdev, queue + offset);if (!mask) goto fallback;for_each_cpu (cpu, mask) - set->mq_map[cpu] = queue; + qmap->mq_map[cpu] = queue; }return 0 ; fallback: -WARN_ON_ONCE (set->nr_hw_queues >1 ); -blk_mq_clear_mq_map (set); +WARN_ON_ONCE (qmap->nr_queues >1 ); +blk_mq_clear_mq_map (qmap);return 0 ; }EXPORT_SYMBOL_GPL (blk_mq_pci_map_queues);
block/blk-mq-rdma.c +2 -2
@@ -41,12 +41,12 @@ int blk_mq_rdma_map_queues(struct blk_mq_tag_set *set, goto fallback;for_each_cpu (cpu, mask) - set->mq_map[cpu] = queue; + set->map[0 ].mq_map[cpu] = queue; }return 0 ; fallback: -return blk_mq_map_queues (set); +return blk_mq_map_queues (&set->map[0 ]); }EXPORT_SYMBOL_GPL (blk_mq_rdma_map_queues);
block/blk-mq-virtio.c +4 -4
@@ -29,7 +29,7 @@* that maps a queue to the CPUs that have irq affinity for the corresponding * vector. */ -int blk_mq_virtio_map_queues (struct blk_mq_tag_set *set, +int blk_mq_virtio_map_queues (struct blk_mq_queue_map *qmap,struct virtio_device *vdev,int first_vec) {const struct cpumask *mask;
@@ -38,17 +38,17 @@ int blk_mq_virtio_map_queues(struct blk_mq_tag_set *set,if (!vdev->config->get_vq_affinity) goto fallback; -for (queue =0 ; queue < set->nr_hw_queues; queue++) { +for (queue =0 ; queue < qmap->nr_queues; queue++) { mask = vdev->config->get_vq_affinity (vdev, first_vec + queue);if (!mask) goto fallback;for_each_cpu (cpu, mask) - set->mq_map[cpu] = queue; + qmap->mq_map[cpu] = queue; }return 0 ; fallback: -return blk_mq_map_queues (set); +return blk_mq_map_queues (qmap); }EXPORT_SYMBOL_GPL (blk_mq_virtio_map_queues);
block/blk-mq.c +18 -16
@@ -1974,7 +1974,7 @@ struct blk_mq_tags *blk_mq_alloc_rq_map(struct blk_mq_tag_set *set,struct blk_mq_tags *tags;int node; - node =blk_mq_hw_queue_to_node (set->mq_map, hctx_idx); + node =blk_mq_hw_queue_to_node (&set->map[0 ], hctx_idx);if (node ==NUMA_NO_NODE ) node = set->numa_node;
@@ -2030,7 +2030,7 @@ int blk_mq_alloc_rqs(struct blk_mq_tag_set *set, struct blk_mq_tags *tags,size_t rq_size, left;int node; - node =blk_mq_hw_queue_to_node (set->mq_map, hctx_idx); + node =blk_mq_hw_queue_to_node (&set->map[0 ], hctx_idx);if (node ==NUMA_NO_NODE ) node = set->numa_node;
@@ -2321,7 +2321,7 @@ static void blk_mq_map_swqueue(struct request_queue *q)* If the cpu isn't present, the cpu is mapped to first hctx. */ for_each_possible_cpu (i) { - hctx_idx = set->mq_map[i]; + hctx_idx = set->map[0 ].mq_map[i];/* unmapped hw queue can be remapped after CPU topo changed */ if (!set->tags[hctx_idx] && !__blk_mq_alloc_rq_map (set, hctx_idx)) {
@@ -2331,7 +2331,7 @@ static void blk_mq_map_swqueue(struct request_queue *q)* case, remap the current ctx to hctx[0] which * is guaranteed to always have tags allocated */ - set->mq_map[i] =0 ; + set->map[0 ].mq_map[i] =0 ; } ctx =per_cpu_ptr (q->queue_ctx, i);
@@ -2584,7 +2584,7 @@ static void blk_mq_realloc_hw_ctxs(struct blk_mq_tag_set *set,int node;struct blk_mq_hw_ctx *hctx; - node =blk_mq_hw_queue_to_node (set->mq_map, i); + node =blk_mq_hw_queue_to_node (&set->map[0 ], i);/* * If the hw queue has been mapped to another numa node, * we need to realloc the hctx. If allocation fails, fallback
@@ -2793,18 +2793,18 @@ static int blk_mq_update_queue_map(struct blk_mq_tag_set *set)* for (queue = 0; queue < set->nr_hw_queues; queue++) { * mask = get_cpu_mask(queue) * for_each_cpu(cpu, mask) -* set->mq_map[cpu] = queue; +* set->map.mq_map[cpu] = queue; * } * * When we need to remap, the table has to be cleared for * killing stale mapping since one CPU may not be mapped * to any hw queue. */ -blk_mq_clear_mq_map (set); +blk_mq_clear_mq_map (&set->map[0 ]);return set->ops->map_queues (set); }else -return blk_mq_map_queues (set); +return blk_mq_map_queues (&set->map[0 ]); }/*
@@ -2859,10 +2859,12 @@ int blk_mq_alloc_tag_set(struct blk_mq_tag_set *set)return -ENOMEM ; ret = -ENOMEM ; - set->mq_map =kcalloc_node (nr_cpu_ids,sizeof (*set->mq_map), -GFP_KERNEL , set->numa_node); -if (!set->mq_map) + set->map[0 ].mq_map =kcalloc_node (nr_cpu_ids, +sizeof (*set->map[0 ].mq_map), +GFP_KERNEL , set->numa_node); +if (!set->map[0 ].mq_map) goto out_free_tags; + set->map[0 ].nr_queues = set->nr_hw_queues; ret =blk_mq_update_queue_map (set);if (ret)
@@ -2878,8 +2880,8 @@ int blk_mq_alloc_tag_set(struct blk_mq_tag_set *set)return 0 ; out_free_mq_map: -kfree (set->mq_map); - set->mq_map =NULL ; +kfree (set->map[0 ].mq_map); + set->map[0 ].mq_map =NULL ; out_free_tags:kfree (set->tags); set->tags =NULL ;
@@ -2894,8 +2896,8 @@ void blk_mq_free_tag_set(struct blk_mq_tag_set *set)for (i =0 ; i < nr_cpu_ids; i++)blk_mq_free_map_and_requests (set, i); -kfree (set->mq_map); - set->mq_map =NULL ; +kfree (set->map[0 ].mq_map); + set->map[0 ].mq_map =NULL ;kfree (set->tags); set->tags =NULL ;
@@ -3056,7 +3058,7 @@ static void __blk_mq_update_nr_hw_queues(struct blk_mq_tag_set *set,pr_warn ("Increasing nr_hw_queues to %d fails, fallback to %d\n" , nr_hw_queues, prev_nr_hw_queues); set->nr_hw_queues = prev_nr_hw_queues; -blk_mq_map_queues (set); +blk_mq_map_queues (&set->map[0 ]); goto fallback; }blk_mq_map_swqueue (q);
block/blk-mq.h +4 -4
@@ -70,14 +70,14 @@ void blk_mq_try_issue_list_directly(struct blk_mq_hw_ctx *hctx,/* * CPU -> queue mappings */ -extern int blk_mq_hw_queue_to_node (unsigned int *map,unsigned int ); +extern int blk_mq_hw_queue_to_node (struct blk_mq_queue_map *qmap,unsigned int );static inline struct blk_mq_hw_ctx *blk_mq_map_queue (struct request_queue *q,int cpu) {struct blk_mq_tag_set *set = q->tag_set; -return q->queue_hw_ctx[set->mq_map[cpu]]; +return q->queue_hw_ctx[set->map[0 ].mq_map[cpu]]; }/*
@@ -206,12 +206,12 @@ static inline void blk_mq_put_driver_tag(struct request *rq) __blk_mq_put_driver_tag(hctx, rq); } -static inline void blk_mq_clear_mq_map (struct blk_mq_tag_set *set) +static inline void blk_mq_clear_mq_map (struct blk_mq_queue_map *qmap) {int cpu;for_each_possible_cpu (cpu) - set->mq_map[cpu] =0 ; + qmap->mq_map[cpu] =0 ; }#endif
drivers/block/virtio_blk.c +1 -1
@@ -624,7 +624,7 @@ static int virtblk_map_queues(struct blk_mq_tag_set *set) {struct virtio_blk *vblk = set->driver_data; -return blk_mq_virtio_map_queues (set, vblk->vdev,0 ); +return blk_mq_virtio_map_queues (&set->map[0 ], vblk->vdev,0 ); }#ifdef CONFIG_VIRTIO_BLK_SCSI
drivers/nvme/host/pci.c +1 -1
@@ -435,7 +435,7 @@ static int nvme_pci_map_queues(struct blk_mq_tag_set *set) {struct nvme_dev *dev = set->driver_data; -return blk_mq_pci_map_queues (set,to_pci_dev (dev->dev), +return blk_mq_pci_map_queues (&set->map[0 ],to_pci_dev (dev->dev), dev->num_vecs >1 ?1 /* admin queue */ :0 ); }
drivers/scsi/qla2xxx/qla_os.c +3 -2
@@ -6934,11 +6934,12 @@ static int qla2xxx_map_queues(struct Scsi_Host *shost) {int rc;scsi_qla_host_t *vha = (scsi_qla_host_t *)shost->hostdata; +struct blk_mq_queue_map *qmap = &shost->tag_set.map[0 ];if (USER_CTRL_IRQ (vha->hw)) - rc =blk_mq_map_queues (&shost->tag_set); + rc =blk_mq_map_queues (qmap);else - rc =blk_mq_pci_map_queues (&shost->tag_set, vha->hw->pdev,0 ); + rc =blk_mq_pci_map_queues (qmap, vha->hw->pdev,0 );return rc; }
drivers/scsi/scsi_lib.c +1 -1
@@ -1812,7 +1812,7 @@ static int scsi_map_queues(struct blk_mq_tag_set *set)if (shost->hostt->map_queues)return shost->hostt->map_queues (shost); -return blk_mq_map_queues (set); +return blk_mq_map_queues (&set->map[0 ]); }void __scsi_init_queue (struct Scsi_Host *shost,struct request_queue *q)
drivers/scsi/smartpqi/smartpqi_init.c +2 -1
@@ -5319,7 +5319,8 @@ static int pqi_map_queues(struct Scsi_Host *shost) {struct pqi_ctrl_info *ctrl_info =shost_to_hba (shost); -return blk_mq_pci_map_queues (&shost->tag_set, ctrl_info->pci_dev,0 ); +return blk_mq_pci_map_queues (&shost->tag_set.map[0 ], + ctrl_info->pci_dev,0 ); } static int pqi_getpciinfo_ioctl(struct pqi_ctrl_info *ctrl_info,
drivers/scsi/virtio_scsi.c +2 -1
@@ -719,8 +719,9 @@ static void virtscsi_target_destroy(struct scsi_target *starget)static int virtscsi_map_queues (struct Scsi_Host *shost) {struct virtio_scsi *vscsi =shost_priv (shost); +struct blk_mq_queue_map *qmap = &shost->tag_set.map[0 ]; -return blk_mq_virtio_map_queues (&shost->tag_set, vscsi->vdev,2 ); +return blk_mq_virtio_map_queues (qmap, vscsi->vdev,2 ); }/*
include/linux/blk-mq-pci.h +2 -2
@@ -2,10 +2,10 @@#ifndef _LINUX_BLK_MQ_PCI_H#define _LINUX_BLK_MQ_PCI_H -struct blk_mq_tag_set ; +struct blk_mq_queue_map ;struct pci_dev ; -int blk_mq_pci_map_queues (struct blk_mq_tag_set *set,struct pci_dev *pdev, +int blk_mq_pci_map_queues (struct blk_mq_queue_map *qmap,struct pci_dev *pdev,int offset);#endif /* _LINUX_BLK_MQ_PCI_H */
include/linux/blk-mq-virtio.h +2 -2
@@ -2,10 +2,10 @@#ifndef _LINUX_BLK_MQ_VIRTIO_H#define _LINUX_BLK_MQ_VIRTIO_H -struct blk_mq_tag_set ; +struct blk_mq_queue_map ;struct virtio_device ; -int blk_mq_virtio_map_queues (struct blk_mq_tag_set *set, +int blk_mq_virtio_map_queues (struct blk_mq_queue_map *qmap,struct virtio_device *vdev,int first_vec);#endif /* _LINUX_BLK_MQ_VIRTIO_H */
include/linux/blk-mq.h +12 -3
@@ -74,10 +74,19 @@ struct blk_mq_hw_ctx {struct srcu_struct srcu[0 ]; }; +struct blk_mq_queue_map { +unsigned int *mq_map; +unsigned int nr_queues; +}; + +enum { +HCTX_MAX_TYPES =1 , +}; +struct blk_mq_tag_set { -unsigned int *mq_map; +struct blk_mq_queue_map map[HCTX_MAX_TYPES ];const struct blk_mq_ops *ops; -unsigned int nr_hw_queues; +unsigned int nr_hw_queues;/* nr hw queues across maps */ unsigned int queue_depth;/* max hw supported */ unsigned int reserved_tags;unsigned int cmd_size;/* per-request extra data */
@@ -294,7 +303,7 @@ void blk_mq_freeze_queue_wait(struct request_queue *q);int blk_mq_freeze_queue_wait_timeout (struct request_queue *q,unsigned long timeout); -int blk_mq_map_queues (struct blk_mq_tag_set *set); +int blk_mq_map_queues (struct blk_mq_queue_map *qmap);void blk_mq_update_nr_hw_queues (struct blk_mq_tag_set *set,int nr_hw_queues);void blk_mq_quiesce_queue_nowait (struct request_queue *q);
Signature · 1 line
2.17.1
Sagi Grimberg
maintainerRe: [PATCH 02/16] blk-mq: abstract out queue map
block/blk-mq-rdma.c int blk_mq_rdma_map_queues(struct blk_mq_tag_set *set, · line 44
Quoted 7 lines
block/blk-mq-rdma.c +1
-1@@ -41,12 +41,12 @@ int blk_mq_rdma_map_queues(struct blk_mq_tag_set *set, goto fallback;for_each_cpu (cpu, mask) - set->mq_map[cpu] = queue; + set->map[0 ].mq_map[cpu] = queue;
Any reason not to change the signature like the other mappers?
Jens Axboe
maintainerRe: [PATCH 02/16] blk-mq: abstract out queue map
Quoted 9 lines · Sagi Grimberg
block/blk-mq-rdma.c int blk_mq_rdma_map_queues(struct blk_mq_tag_set *set, · line 44
Quoted 7 lines
block/blk-mq-rdma.c +1
-1@@ -41,12 +41,12 @@ int blk_mq_rdma_map_queues(struct blk_mq_tag_set *set, goto fallback;for_each_cpu (cpu, mask) - set->mq_map[cpu] = queue; + set->map[0 ].mq_map[cpu] = queue;Any reason not to change the signature like the other mappers?
No particular reason, outside of not wanting to touch more than I had to.
Signature · 1 line
Jens Axboe
Jens Axboe
maintainerPATCH v3 3/16 blk-mq: provide dummy blk_mq_map_queue_type() helper
In reply to Jens AxboeDoesn't do anything right now, but it's needed as a prep patch to get the interfaces right.
While in there, correct the blk_mq_map_queue() CPU type to an unsigned int.
--- block/blk-mq.h | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-)
block/blk-mq.h +8 -1
@@ -73,13 +73,20 @@ void blk_mq_try_issue_list_directly(struct blk_mq_hw_ctx *hctx,extern int blk_mq_hw_queue_to_node (struct blk_mq_queue_map *qmap,unsigned int );static inline struct blk_mq_hw_ctx *blk_mq_map_queue (struct request_queue *q, -int cpu) +unsigned int cpu) {struct blk_mq_tag_set *set = q->tag_set;return q->queue_hw_ctx[set->map[0 ].mq_map[cpu]]; } +static inline struct blk_mq_hw_ctx *blk_mq_map_queue_type (struct request_queue *q, +unsigned int hctx_type, +unsigned int cpu) +{ +return blk_mq_map_queue (q, cpu); +} +/* * sysfs helpers */
Signature · 1 line
2.17.1
Re: [PATCH 03/16] blk-mq: provide dummy blk_mq_map_queue_type() helper
Jens Axboe
maintainerPATCH v3 4/16 blk-mq: pass in request/bio flags to queue mapping
In reply to Jens AxboePrep patch for being able to place request based not just on CPU location, but also on the type of request.
--- block/blk-flush.c | 7 +++--- block/blk-mq-debugfs.c | 4 +++- block/blk-mq-sched.c | 16 ++++++++++---- block/blk-mq-tag.c | 5 +++-- block/blk-mq.c | 50 +++++++++++++++++++++++------------------- block/blk-mq.h | 6 +++-- block/blk.h | 6 ++--- 7 files changed, 57 insertions(+), 37 deletions(-)
block/blk-flush.c +4 -3
@@ -219,7 +219,7 @@ static void flush_end_io(struct request *flush_rq, blk_status_t error)/* release the tag's ownership to the req cloned from */ spin_lock_irqsave (&fq->mq_flush_lock, flags); - hctx =blk_mq_map_queue (q, flush_rq->mq_ctx->cpu); + hctx =blk_mq_map_queue (q, flush_rq->cmd_flags, flush_rq->mq_ctx->cpu);if (!q->elevator) {blk_mq_tag_set_rq (hctx, flush_rq->tag, fq->orig_rq); flush_rq->tag =-1 ;
@@ -307,7 +307,8 @@ static bool blk_kick_flush(struct request_queue *q, struct blk_flush_queue *fq,if (!q->elevator) { fq->orig_rq = first_rq; flush_rq->tag = first_rq->tag; - hctx =blk_mq_map_queue (q, first_rq->mq_ctx->cpu); + hctx =blk_mq_map_queue (q, first_rq->cmd_flags, + first_rq->mq_ctx->cpu);blk_mq_tag_set_rq (hctx, first_rq->tag, flush_rq); }else { flush_rq->internal_tag = first_rq->internal_tag;
@@ -330,7 +331,7 @@ static void mq_flush_data_end_io(struct request *rq, blk_status_t error)unsigned long flags;struct blk_flush_queue *fq =blk_get_flush_queue (q, ctx); - hctx =blk_mq_map_queue (q, ctx->cpu); + hctx =blk_mq_map_queue (q, rq->cmd_flags, ctx->cpu);if (q->elevator) {WARN_ON (rq->tag <0 );
block/blk-mq-debugfs.c +3 -1
@@ -427,8 +427,10 @@ struct show_busy_params {static void hctx_show_busy_rq (struct request *rq,void *data,bool reserved) {const struct show_busy_params *params = data; +struct blk_mq_hw_ctx *hctx; -if (blk_mq_map_queue (rq->q, rq->mq_ctx->cpu) == params->hctx) + hctx =blk_mq_map_queue (rq->q, rq->cmd_flags, rq->mq_ctx->cpu); +if (hctx == params->hctx)__blk_mq_debugfs_rq_show (params->m,list_entry_rq (&rq->queuelist)); }
block/blk-mq-sched.c +12 -4
@@ -311,7 +311,7 @@ bool __blk_mq_sched_bio_merge(struct request_queue *q, struct bio *bio) {struct elevator_queue *e = q->elevator;struct blk_mq_ctx *ctx =blk_mq_get_ctx (q); -struct blk_mq_hw_ctx *hctx =blk_mq_map_queue (q, ctx->cpu); +struct blk_mq_hw_ctx *hctx =blk_mq_map_queue (q, bio->bi_opf, ctx->cpu);bool ret = false;if (e && e->type->ops.mq.bio_merge) {
@@ -367,7 +367,9 @@ void blk_mq_sched_insert_request(struct request *rq, bool at_head,struct request_queue *q = rq->q;struct elevator_queue *e = q->elevator;struct blk_mq_ctx *ctx = rq->mq_ctx; -struct blk_mq_hw_ctx *hctx =blk_mq_map_queue (q, ctx->cpu); +struct blk_mq_hw_ctx *hctx; + + hctx =blk_mq_map_queue (q, rq->cmd_flags, ctx->cpu);/* flush rq in flush machinery need to be dispatched directly */ if (!(rq->rq_flags &RQF_FLUSH_SEQ ) &&op_is_flush (rq->cmd_flags)) {
@@ -400,9 +402,15 @@ void blk_mq_sched_insert_requests(struct request_queue *q,struct blk_mq_ctx *ctx, struct list_head *list, bool run_queue_async) { - struct blk_mq_hw_ctx *hctx = blk_mq_map_queue(q, ctx->cpu); -struct elevator_queue *e = hctx->queue->elevator; + struct blk_mq_hw_ctx *hctx; +struct elevator_queue *e; +struct request *rq; + +/* For list inserts, requests better be on the same hw queue */ + rq = list_first_entry(list, struct request, queuelist); + hctx =blk_mq_map_queue (q, rq->cmd_flags, ctx->cpu); + e = hctx->queue->elevator;if (e && e->type->ops.mq.insert_requests) e->type->ops.mq.insert_requests (hctx, list, false);else {
block/blk-mq-tag.c +3 -2
@@ -168,7 +168,8 @@ unsigned int blk_mq_get_tag(struct blk_mq_alloc_data *data)io_schedule (); data->ctx =blk_mq_get_ctx (data->q); - data->hctx =blk_mq_map_queue (data->q, data->ctx->cpu); + data->hctx =blk_mq_map_queue (data->q, data->cmd_flags, + data->ctx->cpu); tags =blk_mq_tags_from_data (data);if (data->flags &BLK_MQ_REQ_RESERVED ) bt = &tags->breserved_tags;
@@ -530,7 +531,7 @@ u32 blk_mq_unique_tag(struct request *rq)struct blk_mq_hw_ctx *hctx;int hwq =0 ; - hctx =blk_mq_map_queue (q, rq->mq_ctx->cpu); + hctx =blk_mq_map_queue (q, rq->cmd_flags, rq->mq_ctx->cpu); hwq = hctx->queue_num; return (hwq << BLK_MQ_UNIQUE_TAG_BITS) |
block/blk-mq.c +28 -22
166 lines not on this page · raw message
block/blk-mq.h +4 -2
@@ -73,6 +73,7 @@ void blk_mq_try_issue_list_directly(struct blk_mq_hw_ctx *hctx,extern int blk_mq_hw_queue_to_node (struct blk_mq_queue_map *qmap,unsigned int );static inline struct blk_mq_hw_ctx *blk_mq_map_queue (struct request_queue *q, +unsigned int flags,unsigned int cpu) {struct blk_mq_tag_set *set = q->tag_set;
@@ -84,7 +85,7 @@ static inline struct blk_mq_hw_ctx *blk_mq_map_queue_type(struct request_queue *unsigned int hctx_type, unsigned int cpu) { -return blk_mq_map_queue (q, cpu); + return blk_mq_map_queue(q, hctx_type, cpu); }/*
@@ -135,6 +136,7 @@ struct blk_mq_alloc_data {struct request_queue *q;blk_mq_req_flags_t flags;unsigned int shallow_depth; +unsigned int cmd_flags;/* input & output parameter */ struct blk_mq_ctx *ctx;
@@ -209,7 +211,7 @@ static inline void blk_mq_put_driver_tag(struct request *rq)if (rq->tag ==-1 || rq->internal_tag ==-1 )return ; - hctx =blk_mq_map_queue (rq->q, rq->mq_ctx->cpu); + hctx =blk_mq_map_queue (rq->q, rq->cmd_flags, rq->mq_ctx->cpu);__blk_mq_put_driver_tag (hctx, rq); }
block/blk.h +3 -3
@@ -104,10 +104,10 @@ static inline void queue_flag_clear(unsigned int flag, struct request_queue *q)__clear_bit (flag, &q->queue_flags); } -static inline struct blk_flush_queue *blk_get_flush_queue ( -struct request_queue *q,struct blk_mq_ctx *ctx) +static inline struct blk_flush_queue * +blk_get_flush_queue (struct request_queue *q,struct blk_mq_ctx *ctx) { -return blk_mq_map_queue (q, ctx->cpu)->fq; +return blk_mq_map_queue (q,REQ_OP_FLUSH , ctx->cpu)->fq; }static inline void __blk_get_queue (struct request_queue *q)
Signature · 1 line
2.17.1
Sagi Grimberg
maintainerRe: [PATCH 04/16] blk-mq: pass in request/bio flags to queue mapping
block/blk-mq.h static inline struct blk_mq_hw_ctx *blk_mq_map_queue_type(struct request_queue * · line 89
Quoted 13 lines
static inline struct
blk_mq_hw_ctx*blk_mq_map_queue(structrequest_queue*q,block/blk-mq.h +2
-1+ unsigned int flags, unsigned int cpu) {struct blk_mq_tag_set *set = q->tag_set;@@ -84,7 +85,7 @@ static inline struct blk_mq_hw_ctx *blk_mq_map_queue_type(struct request_queue *unsigned int hctx_type, unsigned int cpu) { -return blk_mq_map_queue (q, cpu); + return blk_mq_map_queue(q, hctx_type, cpu); }
Haven't read through the rest of the patch set, but it looks a bit strange that a type enumeration is being passed on as flags field.
Jens Axboe
maintainerRe: [PATCH 04/16] blk-mq: pass in request/bio flags to queue mapping
Quoted 16 lines · Sagi Grimberg
block/blk-mq.h static inline struct blk_mq_hw_ctx *blk_mq_map_queue_type(struct request_queue * · line 89
Quoted 13 lines
static inline struct
blk_mq_hw_ctx*blk_mq_map_queue(structrequest_queue*q,block/blk-mq.h +2
-1+ unsigned int flags, unsigned int cpu) {struct blk_mq_tag_set *set = q->tag_set;@@ -84,7 +85,7 @@ static inline struct blk_mq_hw_ctx *blk_mq_map_queue_type(struct request_queue *unsigned int hctx_type, unsigned int cpu) { -return blk_mq_map_queue (q, cpu); + return blk_mq_map_queue(q, hctx_type, cpu); }Haven't read through the rest of the patch set, but it looks a bit strange that a type enumeration is being passed on as flags field.
It's impossible to get it 100% right at this point, which is why it looks a bit odd. If you read on, it'll make sense.
Signature · 1 line
Jens Axboe
Jens Axboe
maintainerPATCH v3 5/16 blk-mq: allow software queue to map to multiple hardware queues
In reply to Jens AxboeThe mapping used to be dependent on just the CPU location, but now it's a tuple of (type, cpu) instead. This is a prep patch for allowing a single software queue to map to multiple hardware queues. No functional changes in this patch.
--- block/blk-mq-sched.c | 2 +- block/blk-mq.c | 22 ++++++++++++++++------ block/blk-mq.h | 2 +- block/kyber-iosched.c | 6 +++--- include/linux/blk-mq.h | 3 ++- 5 files changed, 23 insertions(+), 12 deletions(-)
block/blk-mq-sched.c +1 -1
@@ -110,7 +110,7 @@ static void blk_mq_do_dispatch_sched(struct blk_mq_hw_ctx *hctx)static struct blk_mq_ctx *blk_mq_next_ctx (struct blk_mq_hw_ctx *hctx,struct blk_mq_ctx *ctx) { -unsigned idx = ctx->index_hw; +unsigned short idx = ctx->index_hw[hctx->type];if (++idx == hctx->nr_ctx) idx =0 ;
block/blk-mq.c +16 -6
@@ -75,14 +75,18 @@ static bool blk_mq_hctx_has_pending(struct blk_mq_hw_ctx *hctx)static void blk_mq_hctx_mark_pending (struct blk_mq_hw_ctx *hctx,struct blk_mq_ctx *ctx) { -if (!sbitmap_test_bit (&hctx->ctx_map, ctx->index_hw)) -sbitmap_set_bit (&hctx->ctx_map, ctx->index_hw); +const int bit = ctx->index_hw[hctx->type]; + +if (!sbitmap_test_bit (&hctx->ctx_map, bit)) +sbitmap_set_bit (&hctx->ctx_map, bit); }static void blk_mq_hctx_clear_pending (struct blk_mq_hw_ctx *hctx,struct blk_mq_ctx *ctx) { -sbitmap_clear_bit (&hctx->ctx_map, ctx->index_hw); +const int bit = ctx->index_hw[hctx->type]; + +sbitmap_clear_bit (&hctx->ctx_map, bit); }struct mq_inflight {
@@ -954,7 +958,7 @@ static bool dispatch_rq_from_ctx(struct sbitmap *sb, unsigned int bitnr,struct request *blk_mq_dequeue_from_ctx (struct blk_mq_hw_ctx *hctx,struct blk_mq_ctx *start) { -unsigned off = start ? start->index_hw :0 ; +unsigned off = start ? start->index_hw[hctx->type] :0 ;struct dispatch_rq_data data = { .hctx = hctx, .rq =NULL ,
@@ -2342,10 +2346,16 @@ static void blk_mq_map_swqueue(struct request_queue *q) ctx =per_cpu_ptr (q->queue_ctx, i); hctx =blk_mq_map_queue_type (q,0 , i); - + hctx->type =0 ;cpumask_set_cpu (i, hctx->cpumask); - ctx->index_hw = hctx->nr_ctx; + ctx->index_hw[hctx->type] = hctx->nr_ctx; hctx->ctxs[hctx->nr_ctx++] = ctx; + +/* +* If the nr_ctx type overflows, we have exceeded the +* amount of sw queues we can support. +*/ +BUG_ON (!hctx->nr_ctx); }mutex_unlock (&q->sysfs_lock);
block/blk-mq.h +1 -1
@@ -17,7 +17,7 @@ struct blk_mq_ctx { }____cacheline_aligned_in_smp ;unsigned int cpu; -unsigned int index_hw; +unsigned short index_hw[HCTX_MAX_TYPES ];/* incremented at dispatch time */ unsigned long rq_dispatched[2 ];
block/kyber-iosched.c +3 -3
@@ -576,7 +576,7 @@ static bool kyber_bio_merge(struct blk_mq_hw_ctx *hctx, struct bio *bio) {struct kyber_hctx_data *khd = hctx->sched_data;struct blk_mq_ctx *ctx =blk_mq_get_ctx (hctx->queue); -struct kyber_ctx_queue *kcq = &khd->kcqs[ctx->index_hw]; +struct kyber_ctx_queue *kcq = &khd->kcqs[ctx->index_hw[hctx->type]];unsigned int sched_domain =kyber_sched_domain (bio->bi_opf);struct list_head *rq_list = &kcq->rq_list[sched_domain];bool merged;
@@ -602,7 +602,7 @@ static void kyber_insert_requests(struct blk_mq_hw_ctx *hctx,list_for_each_entry_safe (rq, next, rq_list, queuelist) {unsigned int sched_domain =kyber_sched_domain (rq->cmd_flags); -struct kyber_ctx_queue *kcq = &khd->kcqs[rq->mq_ctx->index_hw]; +struct kyber_ctx_queue *kcq = &khd->kcqs[rq->mq_ctx->index_hw[hctx->type]];struct list_head *head = &kcq->rq_list[sched_domain];spin_lock (&kcq->lock);
@@ -611,7 +611,7 @@ static void kyber_insert_requests(struct blk_mq_hw_ctx *hctx,else list_move_tail (&rq->queuelist, head);sbitmap_set_bit (&khd->kcq_map[sched_domain], - rq->mq_ctx->index_hw); + rq->mq_ctx->index_hw[hctx->type]);blk_mq_sched_request_inserted (rq);spin_unlock (&kcq->lock); }
include/linux/blk-mq.h +2 -1
@@ -37,7 +37,8 @@ struct blk_mq_hw_ctx {struct blk_mq_ctx *dispatch_from;unsigned int dispatch_busy; -unsigned int nr_ctx; +unsigned short type; +unsigned short nr_ctx;struct blk_mq_ctx **ctxs;spinlock_t dispatch_wait_lock;
Signature · 1 line
2.17.1
Sagi Grimberg
maintainerRe: [PATCH 05/16] blk-mq: allow software queue to map to multiple hardware queues
Quoted 8 lines
block/blk-mq.c +1
-1@@ -2342,10 +2346,16 @@ static void blk_mq_map_swqueue(struct request_queue *q) ctx =per_cpu_ptr (q->queue_ctx, i); hctx =blk_mq_map_queue_type (q,0 , i); - + hctx->type =0 ;
block/blk-mq.c static void blk_mq_map_swqueue(struct request_queue *q) · line 2358
Quoted 10 lines
block/blk-mq.c +7
-1cpumask_set_cpu (i, hctx->cpumask); - ctx->index_hw = hctx->nr_ctx; + ctx->index_hw[hctx->type] = hctx->nr_ctx; hctx->ctxs[hctx->nr_ctx++] = ctx; + +/* +* If the nr_ctx type overflows, we have exceeded the +* amount of sw queues we can support. +*/ +BUG_ON (!hctx->nr_ctx);
Was this added because nr_ctx became short? What was the motivation for that? save space? structure alignment?
Perhaps this should be mentioned in the change log (as it contradicts that no functional changes exist in this patch). Or, if this can happen even when 32bit nr_ctx, it should be a separate small patch?
Jens Axboe
maintainerRe: [PATCH 05/16] blk-mq: allow software queue to map to multiple hardware queues
Quoted 13 lines · Sagi Grimberg
Quoted 13 lines
block/blk-mq.c +3
-2@@ -2342,10 +2346,16 @@ static void blk_mq_map_swqueue(struct request_queue *q) ctx =per_cpu_ptr (q->queue_ctx, i); hctx =blk_mq_map_queue_type (q,0 , i); - + hctx->type =0 ;cpumask_set_cpu (i, hctx->cpumask); - ctx->index_hw = hctx->nr_ctx; + ctx->index_hw[hctx->type] = hctx->nr_ctx; hctx->ctxs[hctx->nr_ctx++] = ctx; +
Quoted 8 lines
block/blk-mq.c static void blk_mq_map_swqueue(struct request_queue *q) · line 2358
Quoted 5 lines
block/blk-mq.c +5
-0+/* +* If the nr_ctx type overflows, we have exceeded the +* amount of sw queues we can support. +*/ +BUG_ON (!hctx->nr_ctx);Was this added because
nr_ctxbecame short? What was the motivation for that? save space? structure alignment?
Right, I made it smaller to not take up more space after this change. And since it is now smaller, adding a check for wrap seemed like a good idea.
Quoted 3 lines
Perhaps this should be mentioned in the change log (as it contradicts that no functional changes exist in this patch). Or, if this can happen even when 32bit
nr_ctx, it should be a separate small patch?
Could happen for any type, but more likely now of course. I'll mention this in the changelog.
Signature · 1 line
Jens Axboe
Jens Axboe
maintainerPATCH v3 6/16 blk-mq: add 'type' attribute to the sysfs hctx directory
In reply to Jens AxboeIt can be useful for a user to verify what type a given hardware queue is, expose this information in sysfs.
--- block/blk-mq-sysfs.c | 10 ++++++++++ 1 file changed, 10 insertions(+)
block/blk-mq-sysfs.c +10 -0
@@ -161,6 +161,11 @@ static ssize_t blk_mq_hw_sysfs_cpus_show(struct blk_mq_hw_ctx *hctx, char *page)return ret; } +static ssize_t blk_mq_hw_sysfs_type_show (struct blk_mq_hw_ctx *hctx,char *page) +{ +return sprintf (page,"%u\n" , hctx->type); +} +static struct attribute *default_ctx_attrs[] = {NULL , };
@@ -177,11 +182,16 @@ static struct blk_mq_hw_ctx_sysfs_entry blk_mq_hw_sysfs_cpus = { .attr = {.name = "cpu_list", .mode = 0444 }, .show = blk_mq_hw_sysfs_cpus_show, }; +static struct blk_mq_hw_ctx_sysfs_entry blk_mq_hw_sysfs_type = { + .attr = {.name ="type" , .mode =0444 }, + .show = blk_mq_hw_sysfs_type_show, +};static struct attribute *default_hw_ctx_attrs[] = { &blk_mq_hw_sysfs_nr_tags.attr, &blk_mq_hw_sysfs_nr_reserved_tags.attr, &blk_mq_hw_sysfs_cpus.attr, + &blk_mq_hw_sysfs_type.attr,NULL , };
Signature · 1 line
2.17.1
Re: [PATCH 06/16] blk-mq: add 'type' attribute to the sysfs hctx directory
Quoted 3 lines
It can be useful for a user to verify what type a given hardware queue is, expose this information in sysfs.
I assume the user is expected to understand the meaning of the enumeration it sees in accessing this field correct?
Would be nice to output some meaningful string but I'm not yet sure how to do that, but would be nice...
Other than that,
Jens Axboe
maintainerRe: [PATCH 06/16] blk-mq: add 'type' attribute to the sysfs hctx directory
Quoted 9 lines · Sagi Grimberg
Quoted 3 lines
It can be useful for a user to verify what type a given hardware queue is, expose this information in sysfs.
I assume the user is expected to understand the meaning of the enumeration it sees in accessing this field correct?
Would be nice to output some meaningful string but I'm not yet sure how to do that, but would be nice...
The type is driver private, there's no specific meaning associated with the type. We could make it output a string provided that the driver had a way to query it. For now I think the type is enough, it'll allow you to see which ones are different.
Quoted 2 lines
Other than that,
Reviewed-by Sagi Grimberg <[email protected]>
Thanks
Signature · 1 line
Jens Axboe
Re: [PATCH 06/16] blk-mq: add 'type' attribute to the sysfs hctx directory
In reply to Jens AxboeQuoted 9 lines · Jens Axboe
It can be useful for a user to verify what type a given hardware queue is, expose this information in sysfs.
Reviewed-by Hannes Reinecke <[email protected]>Reviewed-by Bart Van Assche <[email protected]>Signed-off-by Jens Axboe <[email protected]>--- block/blk-mq-sysfs.c | 10 ++++++++++ 1 file changed, 10 insertions(+)
Out of curiousity, why sysfs instead of debugfs?
Jens Axboe
maintainerRe: [PATCH 06/16] blk-mq: add 'type' attribute to the sysfs hctx directory
Quoted 4 lines · Omar Sandoval
Quoted 3 lines · Jens Axboe
It can be useful for a user to verify what type a given hardware queue is, expose this information in sysfs.
Quoted 8 lines
Quoted 6 lines
Reviewed-by Hannes Reinecke <[email protected]>Reviewed-by Bart Van Assche <[email protected]>Signed-off-by Jens Axboe <[email protected]>--- block/blk-mq-sysfs.c | 10 ++++++++++ 1 file changed, 10 insertions(+)Out of curiousity, why sysfs instead of debugfs?
I don't feel that strongly about it, but it is a property like the mapped CPUs, for instance, so kind of makes sense to put it there rather than in debugfs.
Signature · 1 line
Jens Axboe
Jens Axboe
maintainerPATCH v3 7/16 blk-mq: support multiple hctx maps
In reply to Jens AxboeAdd support for the tag set carrying multiple queue maps, and for the driver to inform blk-mq how many it wishes to support through setting set->nr_maps.
This adds an mq_ops helper for drivers that support more than 1 map, mq_ops->flags_to_type(). The function takes request/bio flags and CPU, and returns a queue map index for that. We then use the type information in blk_mq_map_queue() to index the map set.
--- block/blk-mq.c | 92 ++++++++++++++++++++++++++++-------------- block/blk-mq.h | 33 +++++++++++---- include/linux/blk-mq.h | 14 +++++++ 3 files changed, 100 insertions(+), 39 deletions(-)
block/blk-mq.c +61 -31
@@ -2257,7 +2257,8 @@ static int blk_mq_init_hctx(struct request_queue *q,static void blk_mq_init_cpu_queues (struct request_queue *q,unsigned int nr_hw_queues) { -unsigned int i; +struct blk_mq_tag_set *set = q->tag_set; +unsigned int i, j;for_each_possible_cpu (i) {struct blk_mq_ctx *__ctx =per_cpu_ptr (q->queue_ctx, i);
@@ -2272,9 +2273,11 @@ static void blk_mq_init_cpu_queues(struct request_queue *q,* Set local node, IFF we have more than one hw queue. If * not, we remain on the home node of the device */ - hctx =blk_mq_map_queue_type (q,0 , i); -if (nr_hw_queues >1 && hctx->numa_node ==NUMA_NO_NODE ) - hctx->numa_node =local_memory_node (cpu_to_node (i)); +for (j =0 ; j < set->nr_maps; j++) { + hctx =blk_mq_map_queue_type (q, j, i); +if (nr_hw_queues >1 && hctx->numa_node ==NUMA_NO_NODE ) + hctx->numa_node =local_memory_node (cpu_to_node (i)); + } } }
@@ -2309,7 +2312,7 @@ static void blk_mq_free_map_and_requests(struct blk_mq_tag_set *set,static void blk_mq_map_swqueue (struct request_queue *q) { -unsigned int i, hctx_idx; +unsigned int i, j, hctx_idx;struct blk_mq_hw_ctx *hctx;struct blk_mq_ctx *ctx;struct blk_mq_tag_set *set = q->tag_set;
@@ -2345,17 +2348,28 @@ static void blk_mq_map_swqueue(struct request_queue *q) } ctx =per_cpu_ptr (q->queue_ctx, i); - hctx =blk_mq_map_queue_type (q,0 , i); - hctx->type =0 ; -cpumask_set_cpu (i, hctx->cpumask); - ctx->index_hw[hctx->type] = hctx->nr_ctx; - hctx->ctxs[hctx->nr_ctx++] = ctx; +for (j =0 ; j < set->nr_maps; j++) { + hctx =blk_mq_map_queue_type (q, j, i); -/* -* If the nr_ctx type overflows, we have exceeded the -* amount of sw queues we can support. -*/ -BUG_ON (!hctx->nr_ctx); +/* +* If the CPU is already set in the mask, then we've +* mapped this one already. This can happen if +* devices share queues across queue maps. +*/ +if (cpumask_test_cpu (i, hctx->cpumask)) +continue ; + +cpumask_set_cpu (i, hctx->cpumask); + hctx->type = j; + ctx->index_hw[hctx->type] = hctx->nr_ctx; + hctx->ctxs[hctx->nr_ctx++] = ctx; + +/* +* If the nr_ctx type overflows, we have exceeded the +* amount of sw queues we can support. +*/ +BUG_ON (!hctx->nr_ctx); + } }mutex_unlock (&q->sysfs_lock);
@@ -2523,6 +2537,7 @@ struct request_queue *blk_mq_init_sq_queue(struct blk_mq_tag_set *set,memset (set,0 ,sizeof (*set)); set->ops = ops; set->nr_hw_queues =1 ; + set->nr_maps =1 ; set->queue_depth = queue_depth; set->numa_node =NUMA_NO_NODE ; set->flags = set_flags;
@@ -2802,6 +2817,8 @@ static int blk_mq_alloc_rq_maps(struct blk_mq_tag_set *set)static int blk_mq_update_queue_map (struct blk_mq_tag_set *set) {if (set->ops->map_queues) { +int i; +/* * transport .map_queues is usually done in the following * way:
@@ -2809,18 +2826,21 @@ static int blk_mq_update_queue_map(struct blk_mq_tag_set *set)* for (queue = 0; queue < set->nr_hw_queues; queue++) { * mask = get_cpu_mask(queue) * for_each_cpu(cpu, mask) -* set->map.mq_map[cpu] = queue; +* set->map[x].mq_map[cpu] = queue; * } * * When we need to remap, the table has to be cleared for * killing stale mapping since one CPU may not be mapped * to any hw queue. */ -blk_mq_clear_mq_map (&set->map[0 ]); +for (i =0 ; i < set->nr_maps; i++) +blk_mq_clear_mq_map (&set->map[i]);return set->ops->map_queues (set); - }else + }else { +BUG_ON (set->nr_maps >1 );return blk_mq_map_queues (&set->map[0 ]); + } }/*
@@ -2831,7 +2851,7 @@ static int blk_mq_update_queue_map(struct blk_mq_tag_set *set)*/ int blk_mq_alloc_tag_set (struct blk_mq_tag_set *set) { -int ret; +int i, ret;BUILD_BUG_ON (BLK_MQ_MAX_DEPTH >1 <<BLK_MQ_UNIQUE_TAG_BITS );
@@ -2854,6 +2874,11 @@ int blk_mq_alloc_tag_set(struct blk_mq_tag_set *set) set->queue_depth =BLK_MQ_MAX_DEPTH ; } +if (!set->nr_maps) + set->nr_maps =1 ; +else if (set->nr_maps >HCTX_MAX_TYPES ) +return -EINVAL ; +/* * If a crashdump is active, then we are potentially in a very * memory constrained environment. Limit us to 1 queue and
@@ -2875,12 +2900,14 @@ int blk_mq_alloc_tag_set(struct blk_mq_tag_set *set)return -ENOMEM ; ret = -ENOMEM ; - set->map[0 ].mq_map =kcalloc_node (nr_cpu_ids, -sizeof (*set->map[0 ].mq_map), -GFP_KERNEL , set->numa_node); -if (!set->map[0 ].mq_map) - goto out_free_tags; - set->map[0 ].nr_queues = set->nr_hw_queues; +for (i =0 ; i < set->nr_maps; i++) { + set->map[i].mq_map =kcalloc_node (nr_cpu_ids, +sizeof (struct blk_mq_queue_map ), +GFP_KERNEL , set->numa_node); +if (!set->map[i].mq_map) + goto out_free_mq_map; + set->map[i].nr_queues = set->nr_hw_queues; + } ret =blk_mq_update_queue_map (set);if (ret)
@@ -2896,9 +2923,10 @@ int blk_mq_alloc_tag_set(struct blk_mq_tag_set *set)return 0 ; out_free_mq_map: -kfree (set->map[0 ].mq_map); - set->map[0 ].mq_map =NULL ; -out_free_tags: +for (i =0 ; i < set->nr_maps; i++) { +kfree (set->map[i].mq_map); + set->map[i].mq_map =NULL ; + }kfree (set->tags); set->tags =NULL ;return ret;
@@ -2907,13 +2935,15 @@ EXPORT_SYMBOL(blk_mq_alloc_tag_set);void blk_mq_free_tag_set (struct blk_mq_tag_set *set) { -int i; +int i, j;for (i =0 ; i < nr_cpu_ids; i++)blk_mq_free_map_and_requests (set, i); -kfree (set->map[0 ].mq_map); - set->map[0 ].mq_map =NULL ; +for (j =0 ; j < set->nr_maps; j++) { +kfree (set->map[j].mq_map); + set->map[j].mq_map =NULL ; + }kfree (set->tags); set->tags =NULL ;
block/blk-mq.h +25 -8
@@ -72,20 +72,37 @@ void blk_mq_try_issue_list_directly(struct blk_mq_hw_ctx *hctx,*/ extern int blk_mq_hw_queue_to_node (struct blk_mq_queue_map *qmap,unsigned int ); -static inline struct blk_mq_hw_ctx *blk_mq_map_queue (struct request_queue *q, -unsigned int flags, -unsigned int cpu) +/* +* blk_mq_map_queue_type() - map (hctx_type,cpu) to hardware queue +* @q: request queue +* @hctx_type: the hctx type index +* @cpu: CPU +*/ +static inline struct blk_mq_hw_ctx *blk_mq_map_queue_type (struct request_queue *q, +unsigned int hctx_type, +unsigned int cpu) {struct blk_mq_tag_set *set = q->tag_set; -return q->queue_hw_ctx[set->map[0 ].mq_map[cpu]]; +return q->queue_hw_ctx[set->map[hctx_type].mq_map[cpu]]; } -static inline struct blk_mq_hw_ctx *blk_mq_map_queue_type (struct request_queue *q, -unsigned int hctx_type, -unsigned int cpu) +/* +* blk_mq_map_queue() - map (cmd_flags,type) to hardware queue +* @q: request queue +* @flags: request command flags +* @cpu: CPU +*/ +static inline struct blk_mq_hw_ctx *blk_mq_map_queue (struct request_queue *q, +unsigned int flags, +unsigned int cpu) { -return blk_mq_map_queue (q, hctx_type, cpu); +int hctx_type =0 ; + +if (q->mq_ops->flags_to_type) + hctx_type = q->mq_ops->flags_to_type (q, flags); + +return blk_mq_map_queue_type (q, hctx_type, cpu); }/*
include/linux/blk-mq.h +14 -0
@@ -85,7 +85,14 @@ enum { };struct blk_mq_tag_set { +/* +* map[] holds ctx -> hctx mappings, one map exists for each type +* that the driver wishes to support. There are no restrictions +* on maps being of the same size, and it's perfectly legal to +* share maps between types. +*/ struct blk_mq_queue_map map[HCTX_MAX_TYPES ]; +unsigned int nr_maps;/* nr entries in map[] */ const struct blk_mq_ops *ops;unsigned int nr_hw_queues;/* nr hw queues across maps */ unsigned int queue_depth;/* max hw supported */
@@ -109,6 +116,8 @@ struct blk_mq_queue_data {typedef blk_status_t (queue_rq_fn )(struct blk_mq_hw_ctx *,const struct blk_mq_queue_data *); +/* takes rq->cmd_flags as input, returns a hardware type index */ +typedef int (flags_to_type_fn )(struct request_queue *,unsigned int );typedef bool (get_budget_fn )(struct blk_mq_hw_ctx *);typedef void (put_budget_fn )(struct blk_mq_hw_ctx *);typedef enum blk_eh_timer_return (timeout_fn )(struct request *,bool );
@@ -133,6 +142,11 @@ struct blk_mq_ops {*/ queue_rq_fn *queue_rq; +/* +* Return a queue map type for the given request/bio flags +*/ +flags_to_type_fn *flags_to_type; +/* * Reserve budget before queue request, once .queue_rq is * run, it is driver's responsibility to release the
Signature · 1 line
2.17.1
Re: [PATCH 07/16] blk-mq: support multiple hctx maps
Quoted 36 lines
block/blk-mq.h +15
-7@@ -72,20 +72,37 @@ void blk_mq_try_issue_list_directly(struct blk_mq_hw_ctx *hctx,*/ extern int blk_mq_hw_queue_to_node (struct blk_mq_queue_map *qmap,unsigned int ); -static inline struct blk_mq_hw_ctx *blk_mq_map_queue (struct request_queue *q, -unsigned int flags, -unsigned int cpu) +/* +* blk_mq_map_queue_type() - map (hctx_type,cpu) to hardware queue +* @q: request queue +* @hctx_type: the hctx type index +* @cpu: CPU +*/ +static inline struct blk_mq_hw_ctx *blk_mq_map_queue_type (struct request_queue *q, +unsigned int hctx_type, +unsigned int cpu) {struct blk_mq_tag_set *set = q->tag_set; -return q->queue_hw_ctx[set->map[0 ].mq_map[cpu]]; +return q->queue_hw_ctx[set->map[hctx_type].mq_map[cpu]]; } -static inline struct blk_mq_hw_ctx *blk_mq_map_queue_type (struct request_queue *q, -unsigned int hctx_type, -unsigned int cpu) +/* +* blk_mq_map_queue() - map (cmd_flags,type) to hardware queue +* @q: request queue +* @flags: request command flags +* @cpu: CPU
block/blk-mq.h void blk_mq_try_issue_list_directly(struct blk_mq_hw_ctx *hctx, · line 103
Quoted 10 lines
block/blk-mq.h +8
-1+*/ +static inline struct blk_mq_hw_ctx *blk_mq_map_queue (struct request_queue *q, +unsigned int flags, +unsigned int cpu) { -return blk_mq_map_queue (q, hctx_type, cpu); +int hctx_type =0 ; + +if (q->mq_ops->flags_to_type) + hctx_type = q->mq_ops->flags_to_type (q, flags);
nitpick: I think that rq_flags_to_type is a bit more descriptive here. just a personal opinion.
Other than that,
Jens Axboe
maintainerRe: [PATCH 07/16] blk-mq: support multiple hctx maps
Quoted 4 lines · Sagi Grimberg
Quoted 4 lines
block/blk-mq.h +3
-0+static inline struct blk_mq_hw_ctx *blk_mq_map_queue (struct request_queue *q, +unsigned int flags, +unsigned int cpu) {
Quoted 8 lines
block/blk-mq.h void blk_mq_try_issue_list_directly(struct blk_mq_hw_ctx *hctx, · line 103
Quoted 5 lines
block/blk-mq.h +4
-1-return blk_mq_map_queue (q, hctx_type, cpu); +int hctx_type =0 ; + +if (q->mq_ops->flags_to_type) + hctx_type = q->mq_ops->flags_to_type (q, flags);nitpick: I think that
rq_flags_to_typeis a bit more descriptive here. just a personal opinion.
Sure, I can make that change.
Signature · 1 line
Jens Axboe
Jens Axboe
maintainerPATCH v3 8/16 blk-mq: separate number of hardware queues from nr_cpu_ids
In reply to Jens AxboeWith multiple maps, nr_cpu_ids is no longer the maximum number of hardware queues we support on a given devices. The initializer of the tag_set can have set ->nr_hw_queues larger than the available number of CPUs, since we can exceed that with multiple queue maps.
--- block/blk-mq.c | 28 +++++++++++++++++++++------- 1 file changed, 21 insertions(+), 7 deletions(-)
block/blk-mq.c +21 -7
@@ -2668,6 +2668,19 @@ static void blk_mq_realloc_hw_ctxs(struct blk_mq_tag_set *set,mutex_unlock (&q->sysfs_lock); } +/* +* Maximum number of hardware queues we support. For single sets, we'll never +* have more than the CPUs (software queues). For multiple sets, the tag_set +* user may have set ->nr_hw_queues larger. +*/ +static unsigned int nr_hw_queues (struct blk_mq_tag_set *set) +{ +if (set->nr_maps ==1 ) +return nr_cpu_ids; + +return max (set->nr_hw_queues, nr_cpu_ids); +} +struct request_queue *blk_mq_init_allocated_queue (struct blk_mq_tag_set *set,struct request_queue *q) {
@@ -2687,7 +2700,8 @@ struct request_queue *blk_mq_init_allocated_queue(struct blk_mq_tag_set *set,/* init q->mq_kobj and sw queues' kobjects */ blk_mq_sysfs_init (q); - q->queue_hw_ctx =kcalloc_node (nr_cpu_ids,sizeof (*(q->queue_hw_ctx)), + q->nr_queues =nr_hw_queues (set); + q->queue_hw_ctx =kcalloc_node (q->nr_queues,sizeof (*(q->queue_hw_ctx)),GFP_KERNEL , set->numa_node);if (!q->queue_hw_ctx) goto err_percpu;
@@ -2699,7 +2713,6 @@ struct request_queue *blk_mq_init_allocated_queue(struct blk_mq_tag_set *set,INIT_WORK (&q->timeout_work, blk_mq_timeout_work);blk_queue_rq_timeout (q, set->timeout ? set->timeout :30 *HZ ); - q->nr_queues = nr_cpu_ids; q->tag_set = set; q->queue_flags |=QUEUE_FLAG_MQ_DEFAULT ;
@@ -2889,12 +2902,13 @@ int blk_mq_alloc_tag_set(struct blk_mq_tag_set *set) set->queue_depth =min (64U , set->queue_depth); }/* -* There is no use for more h/w queues than cpus. +* There is no use for more h/w queues than cpus if we just have +* a single map */ -if (set->nr_hw_queues > nr_cpu_ids) +if (set->nr_maps ==1 && set->nr_hw_queues > nr_cpu_ids) set->nr_hw_queues = nr_cpu_ids; - set->tags =kcalloc_node (nr_cpu_ids,sizeof (struct blk_mq_tags *), + set->tags =kcalloc_node (nr_hw_queues (set),sizeof (struct blk_mq_tags *),GFP_KERNEL , set->numa_node);if (!set->tags)return -ENOMEM ;
@@ -2937,7 +2951,7 @@ void blk_mq_free_tag_set(struct blk_mq_tag_set *set) {int i, j; -for (i =0 ; i < nr_cpu_ids; i++) +for (i =0 ; i <nr_hw_queues (set); i++)blk_mq_free_map_and_requests (set, i);for (j =0 ; j < set->nr_maps; j++) {
@@ -3069,7 +3083,7 @@ static void __blk_mq_update_nr_hw_queues(struct blk_mq_tag_set *set,lockdep_assert_held (&set->tag_list_lock); -if (nr_hw_queues > nr_cpu_ids) +if (set->nr_maps ==1 && nr_hw_queues > nr_cpu_ids) nr_hw_queues = nr_cpu_ids;if (nr_hw_queues <1 || nr_hw_queues == set->nr_hw_queues)return ;
Signature · 1 line
2.17.1
Re: [PATCH 08/16] blk-mq: separate number of hardware queues from nr_cpu_ids
Jens Axboe
maintainerPATCH v3 9/16 blk-mq: cache request hardware queue mapping
In reply to Jens AxboeWe call blk_mq_map_queue() a lot, at least two times for each request per IO, sometimes more. Since we now have an indirect call as well in that function. cache the mapping so we don't have to re-call blk_mq_map_queue() for the same request multiple times.
--- block/blk-flush.c | 12 ++++-------- block/blk-mq-debugfs.c | 4 +--- block/blk-mq-sched.c | 6 ++---- block/blk-mq-tag.c | 9 +-------- block/blk-mq.c | 22 +++++++++------------- block/blk-mq.h | 5 +---- include/linux/blkdev.h | 1 + 7 files changed, 19 insertions(+), 40 deletions(-)
block/blk-flush.c +4 -8
@@ -219,7 +219,7 @@ static void flush_end_io(struct request *flush_rq, blk_status_t error)/* release the tag's ownership to the req cloned from */ spin_lock_irqsave (&fq->mq_flush_lock, flags); - hctx =blk_mq_map_queue (q, flush_rq->cmd_flags, flush_rq->mq_ctx->cpu); + hctx = flush_rq->mq_hctx;if (!q->elevator) {blk_mq_tag_set_rq (hctx, flush_rq->tag, fq->orig_rq); flush_rq->tag =-1 ;
@@ -268,7 +268,6 @@ static bool blk_kick_flush(struct request_queue *q, struct blk_flush_queue *fq,struct request *first_rq = list_first_entry(pending, struct request, flush.list);struct request *flush_rq = fq->flush_rq; -struct blk_mq_hw_ctx *hctx;/* C1 described at the top of this file */ if (fq->flush_pending_idx != fq->flush_running_idx ||list_empty (pending))
@@ -303,13 +302,12 @@ static bool blk_kick_flush(struct request_queue *q, struct blk_flush_queue *fq,* just for cheating put/get driver tag. */ flush_rq->mq_ctx = first_rq->mq_ctx; + flush_rq->mq_hctx = first_rq->mq_hctx;if (!q->elevator) { fq->orig_rq = first_rq; flush_rq->tag = first_rq->tag; - hctx =blk_mq_map_queue (q, first_rq->cmd_flags, - first_rq->mq_ctx->cpu); -blk_mq_tag_set_rq (hctx, first_rq->tag, flush_rq); +blk_mq_tag_set_rq (flush_rq->mq_hctx, first_rq->tag, flush_rq); }else { flush_rq->internal_tag = first_rq->internal_tag; }
@@ -326,13 +324,11 @@ static bool blk_kick_flush(struct request_queue *q, struct blk_flush_queue *fq,static void mq_flush_data_end_io (struct request *rq,blk_status_t error) {struct request_queue *q = rq->q; -struct blk_mq_hw_ctx *hctx; +struct blk_mq_hw_ctx *hctx = rq->mq_hctx;struct blk_mq_ctx *ctx = rq->mq_ctx;unsigned long flags;struct blk_flush_queue *fq =blk_get_flush_queue (q, ctx); - hctx =blk_mq_map_queue (q, rq->cmd_flags, ctx->cpu); -if (q->elevator) {WARN_ON (rq->tag <0 );blk_mq_put_driver_tag_hctx (hctx, rq);
block/blk-mq-debugfs.c +1 -3
@@ -427,10 +427,8 @@ struct show_busy_params {static void hctx_show_busy_rq (struct request *rq,void *data,bool reserved) {const struct show_busy_params *params = data; -struct blk_mq_hw_ctx *hctx; - hctx =blk_mq_map_queue (rq->q, rq->cmd_flags, rq->mq_ctx->cpu); -if (hctx == params->hctx) +if (rq->mq_hctx == params->hctx)__blk_mq_debugfs_rq_show (params->m,list_entry_rq (&rq->queuelist)); }
block/blk-mq-sched.c +2 -4
@@ -367,9 +367,7 @@ void blk_mq_sched_insert_request(struct request *rq, bool at_head,struct request_queue *q = rq->q;struct elevator_queue *e = q->elevator;struct blk_mq_ctx *ctx = rq->mq_ctx; -struct blk_mq_hw_ctx *hctx; - - hctx =blk_mq_map_queue (q, rq->cmd_flags, ctx->cpu); +struct blk_mq_hw_ctx *hctx = rq->mq_hctx;/* flush rq in flush machinery need to be dispatched directly */ if (!(rq->rq_flags &RQF_FLUSH_SEQ ) &&op_is_flush (rq->cmd_flags)) {
@@ -408,7 +406,7 @@ void blk_mq_sched_insert_requests(struct request_queue *q,/* For list inserts, requests better be on the same hw queue */ rq = list_first_entry(list, struct request, queuelist); - hctx =blk_mq_map_queue (q, rq->cmd_flags, ctx->cpu); + hctx = rq->mq_hctx; e = hctx->queue->elevator;if (e && e->type->ops.mq.insert_requests)
block/blk-mq-tag.c +1 -8
@@ -527,14 +527,7 @@ int blk_mq_tag_update_depth(struct blk_mq_hw_ctx *hctx,*/ u32 blk_mq_unique_tag (struct request *rq) { -struct request_queue *q = rq->q; -struct blk_mq_hw_ctx *hctx; -int hwq =0 ; - - hctx =blk_mq_map_queue (q, rq->cmd_flags, rq->mq_ctx->cpu); - hwq = hctx->queue_num; - -return (hwq <<BLK_MQ_UNIQUE_TAG_BITS ) | +return (rq->mq_hctx->queue_num <<BLK_MQ_UNIQUE_TAG_BITS ) | (rq->tag &BLK_MQ_UNIQUE_TAG_MASK ); }EXPORT_SYMBOL (blk_mq_unique_tag);
block/blk-mq.c +9 -13
@@ -300,6 +300,7 @@ static struct request *blk_mq_rq_ctx_init(struct blk_mq_alloc_data *data,/* csd/requeue_work/fifo_time is initialized before use */ rq->q = data->q; rq->mq_ctx = data->ctx; + rq->mq_hctx = data->hctx; rq->rq_flags = rq_flags; rq->cpu =-1 ; rq->cmd_flags = op;
@@ -473,10 +474,11 @@ static void __blk_mq_free_request(struct request *rq) {struct request_queue *q = rq->q;struct blk_mq_ctx *ctx = rq->mq_ctx; -struct blk_mq_hw_ctx *hctx =blk_mq_map_queue (q, rq->cmd_flags, ctx->cpu); +struct blk_mq_hw_ctx *hctx = rq->mq_hctx;const int sched_tag = rq->internal_tag;blk_pm_mark_last_busy (rq); + rq->mq_hctx =NULL ;if (rq->tag !=-1 )blk_mq_put_tag (hctx, hctx->tags, ctx, rq->tag);if (sched_tag !=-1 )
@@ -490,7 +492,7 @@ void blk_mq_free_request(struct request *rq)struct request_queue *q = rq->q;struct elevator_queue *e = q->elevator;struct blk_mq_ctx *ctx = rq->mq_ctx; -struct blk_mq_hw_ctx *hctx =blk_mq_map_queue (q, rq->cmd_flags, ctx->cpu); +struct blk_mq_hw_ctx *hctx = rq->mq_hctx;if (rq->rq_flags &RQF_ELVPRIV ) {if (e && e->type->ops.mq.finish_request)
@@ -982,7 +984,7 @@ bool blk_mq_get_driver_tag(struct request *rq) {struct blk_mq_alloc_data data = { .q = rq->q, - .hctx =blk_mq_map_queue (rq->q, rq->cmd_flags, rq->mq_ctx->cpu), + .hctx = rq->mq_hctx, .flags =BLK_MQ_REQ_NOWAIT , .cmd_flags = rq->cmd_flags, };
@@ -1148,7 +1150,7 @@ bool blk_mq_dispatch_rq_list(struct request_queue *q, struct list_head *list, rq = list_first_entry(list, struct request, queuelist); - hctx =blk_mq_map_queue (rq->q, rq->cmd_flags, rq->mq_ctx->cpu); + hctx = rq->mq_hctx;if (!got_budget && !blk_mq_get_dispatch_budget (hctx))break ;
@@ -1578,9 +1580,7 @@ void __blk_mq_insert_request(struct blk_mq_hw_ctx *hctx, struct request *rq,*/ void blk_mq_request_bypass_insert (struct request *rq,bool run_queue) { -struct blk_mq_ctx *ctx = rq->mq_ctx; -struct blk_mq_hw_ctx *hctx =blk_mq_map_queue (rq->q, rq->cmd_flags, - ctx->cpu); +struct blk_mq_hw_ctx *hctx = rq->mq_hctx;spin_lock (&hctx->lock);list_add_tail (&rq->queuelist, &hctx->dispatch);
@@ -1789,9 +1789,7 @@ blk_status_t blk_mq_request_issue_directly(struct request *rq)blk_status_t ret;int srcu_idx;blk_qc_t unused_cookie; -struct blk_mq_ctx *ctx = rq->mq_ctx; -struct blk_mq_hw_ctx *hctx =blk_mq_map_queue (rq->q, rq->cmd_flags, - ctx->cpu); +struct blk_mq_hw_ctx *hctx = rq->mq_hctx;hctx_lock (hctx, &srcu_idx); ret =__blk_mq_try_issue_directly (hctx, rq, &unused_cookie, true);
@@ -1916,9 +1914,7 @@ static blk_qc_t blk_mq_make_request(struct request_queue *q, struct bio *bio)blk_mq_put_ctx (data.ctx);if (same_queue_rq) { - data.hctx =blk_mq_map_queue (q, - same_queue_rq->cmd_flags, - same_queue_rq->mq_ctx->cpu); + data.hctx = same_queue_rq->mq_hctx;blk_mq_try_issue_directly (data.hctx, same_queue_rq, &cookie); }
block/blk-mq.h +1 -4
@@ -223,13 +223,10 @@ static inline void blk_mq_put_driver_tag_hctx(struct blk_mq_hw_ctx *hctx,static inline void blk_mq_put_driver_tag (struct request *rq) { -struct blk_mq_hw_ctx *hctx; -if (rq->tag ==-1 || rq->internal_tag ==-1 )return ; - hctx =blk_mq_map_queue (rq->q, rq->cmd_flags, rq->mq_ctx->cpu); -__blk_mq_put_driver_tag (hctx, rq); +__blk_mq_put_driver_tag (rq->mq_hctx, rq); }static inline void blk_mq_clear_mq_map (struct blk_mq_queue_map *qmap)
include/linux/blkdev.h +1 -0
@@ -129,6 +129,7 @@ enum mq_rq_state {struct request {struct request_queue *q;struct blk_mq_ctx *mq_ctx; +struct blk_mq_hw_ctx *mq_hctx;int cpu;unsigned int cmd_flags;/* op and common flags */
Signature · 1 line
2.17.1
Re: [PATCH 09/16] blk-mq: cache request hardware queue mapping
Re: [PATCH 09/16] blk-mq: cache request hardware queue mapping
In reply to Jens AxboeQuoted 7 lines · Jens Axboe
We call
blk_mq_map_queue()a lot, at least two times for each request per IO, sometimes more. Since we now have an indirect call as well in that function. cache the mapping so we don't have to re-callblk_mq_map_queue()for the same request multiple times.Signed-off-by Jens Axboe <[email protected]>
Quoted 10 lines
--- block/blk-flush.c | 12 ++++-------- block/blk-mq-debugfs.c | 4 +--- block/blk-mq-sched.c | 6 ++---- block/blk-mq-tag.c | 9 +-------- block/blk-mq.c | 22 +++++++++------------- block/blk-mq.h | 5 +---- include/linux/blkdev.h | 1 + 7 files changed, 19 insertions(+), 40 deletions(-)
Hehe.
I had been wondering about this since the original mq implementation, but was told not to worry :-)
Cheers,
Hannes
Jens Axboe
maintainerRe: [PATCH 09/16] blk-mq: cache request hardware queue mapping
Quoted 18 lines · Hannes Reinecke
Quoted 7 lines · Jens Axboe
We call
blk_mq_map_queue()a lot, at least two times for each request per IO, sometimes more. Since we now have an indirect call as well in that function. cache the mapping so we don't have to re-callblk_mq_map_queue()for the same request multiple times.Signed-off-by Jens Axboe <[email protected]>Quoted 10 lines
--- block/blk-flush.c | 12 ++++-------- block/blk-mq-debugfs.c | 4 +--- block/blk-mq-sched.c | 6 ++---- block/blk-mq-tag.c | 9 +-------- block/blk-mq.c | 22 +++++++++------------- block/blk-mq.h | 5 +---- include/linux/blkdev.h | 1 + 7 files changed, 19 insertions(+), 40 deletions(-)
Quoted 3 lines
Hehe.
I had been wondering about this since the original mq implementation, but was told not to worry :-)
To be fair, it was just an array index. Now it's got an indirect call, so it's more expensive.
Quoted 1 line
Reviewed-by Hannes Reinecke <[email protected]>
Thanks!
Signature · 1 line
Jens Axboe
Jens Axboe
maintainerPATCH v3 10/16 blk-mq: cleanup and improve list insertion
In reply to Jens AxboeIt's somewhat strange to have a list insertion function that relies on the fact that the caller has mapped things correctly. Pass in the hardware queue directly for insertion, which makes for a much cleaner interface and implementation.
--- block/blk-mq-sched.c | 8 +------- block/blk-mq-sched.h | 2 +- block/blk-mq.c | 25 ++++++++++++++----------- 3 files changed, 16 insertions(+), 19 deletions(-)
block/blk-mq-sched.c +1 -7
@@ -396,17 +396,11 @@ void blk_mq_sched_insert_request(struct request *rq, bool at_head,blk_mq_run_hw_queue (hctx, async); } -void blk_mq_sched_insert_requests (struct request_queue *q, +void blk_mq_sched_insert_requests (struct blk_mq_hw_ctx *hctx,struct blk_mq_ctx *ctx,struct list_head *list,bool run_queue_async) { -struct blk_mq_hw_ctx *hctx;struct elevator_queue *e; -struct request *rq; - -/* For list inserts, requests better be on the same hw queue */ - rq = list_first_entry(list, struct request, queuelist); - hctx = rq->mq_hctx; e = hctx->queue->elevator;if (e && e->type->ops.mq.insert_requests)
block/blk-mq-sched.h +1 -1
@@ -19,7 +19,7 @@ void blk_mq_sched_restart(struct blk_mq_hw_ctx *hctx);void blk_mq_sched_insert_request (struct request *rq,bool at_head,bool run_queue,bool async); -void blk_mq_sched_insert_requests (struct request_queue *q, +void blk_mq_sched_insert_requests (struct blk_mq_hw_ctx *hctx,struct blk_mq_ctx *ctx,struct list_head *list,bool run_queue_async);
block/blk-mq.c +14 -11
@@ -1623,11 +1623,12 @@ static int plug_ctx_cmp(void *priv, struct list_head *a, struct list_head *b)void blk_mq_flush_plug_list (struct blk_plug *plug,bool from_schedule) { +struct blk_mq_hw_ctx *this_hctx;struct blk_mq_ctx *this_ctx;struct request_queue *this_q;struct request *rq;LIST_HEAD (list); -LIST_HEAD (ctx_list); +LIST_HEAD (rq_list);unsigned int depth;list_splice_init (&plug->mq_list, &list);
@@ -1635,6 +1636,7 @@ void blk_mq_flush_plug_list(struct blk_plug *plug, bool from_schedule)list_sort (NULL , &list, plug_ctx_cmp); this_q =NULL ; + this_hctx =NULL ; this_ctx =NULL ; depth =0 ;
@@ -1642,30 +1644,31 @@ void blk_mq_flush_plug_list(struct blk_plug *plug, bool from_schedule) rq =list_entry_rq (list.next);list_del_init (&rq->queuelist);BUG_ON (!rq->q); -if (rq->mq_ctx != this_ctx) { -if (this_ctx) { +if (rq->mq_hctx != this_hctx || rq->mq_ctx != this_ctx) { +if (this_hctx) {trace_block_unplug (this_q, depth, !from_schedule); -blk_mq_sched_insert_requests (this_q, this_ctx, - &ctx_list, +blk_mq_sched_insert_requests (this_hctx, this_ctx, + &rq_list, from_schedule); } - this_ctx = rq->mq_ctx; this_q = rq->q; + this_ctx = rq->mq_ctx; + this_hctx = rq->mq_hctx; depth =0 ; } depth++; -list_add_tail (&rq->queuelist, &ctx_list); +list_add_tail (&rq->queuelist, &rq_list); }/* -* If 'this_ctx' is set, we know we have entries to complete -* on 'ctx_list'. Do those. +* If 'this_hctx' is set, we know we have entries to complete +* on 'rq_list'. Do those. */ -if (this_ctx) { +if (this_hctx) {trace_block_unplug (this_q, depth, !from_schedule); -blk_mq_sched_insert_requests (this_q, this_ctx, &ctx_list, +blk_mq_sched_insert_requests (this_hctx, this_ctx, &rq_list, from_schedule); } }
Signature · 1 line
2.17.1
Re: [PATCH 10/16] blk-mq: cleanup and improve list insertion
Re: [PATCH 10/16] blk-mq: cleanup and improve list insertion
In reply to Jens AxboeQuoted 5 lines · Jens Axboe
It's somewhat strange to have a list insertion function that relies on the fact that the caller has mapped things correctly. Pass in the hardware queue directly for insertion, which makes for a much cleaner interface and implementation.
Quoted 7 lines
Signed-off-by Jens Axboe <[email protected]>--- block/blk-mq-sched.c | 8 +------- block/blk-mq-sched.h | 2 +- block/blk-mq.c | 25 ++++++++++++++----------- 3 files changed, 16 insertions(+), 19 deletions(-)
Cheers,
Hannes
Jens Axboe
maintainerPATCH v3 11/16 blk-mq: improve plug list sorting
In reply to Jens AxboeCurrently we only look at the software queue, but with support for multiple maps, we should also look at the hardware queue. This is important since we'll flush out the request list if either the software queue or hardware queue don't match.
This sorts by software queue first, then hardware queue if that differs. Finally we sort by request location like before. This minimizes the flush points per plug list.
--- block/blk-mq.c | 17 ++++++++++++----- 1 file changed, 12 insertions(+), 5 deletions(-)
block/blk-mq.c +12 -5
@@ -1611,14 +1611,21 @@ void blk_mq_insert_requests(struct blk_mq_hw_ctx *hctx, struct blk_mq_ctx *ctx,spin_unlock (&ctx->lock); } -static int plug_ctx_cmp (void *priv,struct list_head *a,struct list_head *b) +static int plug_rq_cmp (void *priv,struct list_head *a,struct list_head *b) { struct request *rqa = container_of(a, struct request, queuelist); struct request *rqb = container_of(b, struct request, queuelist); -return !(rqa->mq_ctx < rqb->mq_ctx || - (rqa->mq_ctx == rqb->mq_ctx && -blk_rq_pos (rqa) <blk_rq_pos (rqb))); +if (rqa->mq_ctx < rqb->mq_ctx) +return -1 ; +else if (rqa->mq_ctx > rqb->mq_ctx) +return 1 ; +else if (rqa->mq_hctx < rqb->mq_hctx) +return -1 ; +else if (rqa->mq_hctx > rqb->mq_hctx) +return 1 ; + +return blk_rq_pos (rqa) >blk_rq_pos (rqb); }void blk_mq_flush_plug_list (struct blk_plug *plug,bool from_schedule)
@@ -1633,7 +1640,7 @@ void blk_mq_flush_plug_list(struct blk_plug *plug, bool from_schedule)list_splice_init (&plug->mq_list, &list); -list_sort (NULL , &list, plug_ctx_cmp); +list_sort (NULL , &list, plug_rq_cmp); this_q =NULL ; this_hctx =NULL ;
Signature · 1 line
2.17.1
Re: [PATCH 11/16] blk-mq: improve plug list sorting
Re: [PATCH 11/16] blk-mq: improve plug list sorting
In reply to Jens AxboeQuoted 5 lines · Jens Axboe
Currently we only look at the software queue, but with support for multiple maps, we should also look at the hardware queue. This is important since we'll flush out the request list if either the software queue or hardware queue don't match.
Quoted 9 lines
This sorts by software queue first, then hardware queue if that differs. Finally we sort by request location like before. This minimizes the flush points per plug list.
Signed-off-by Jens Axboe <[email protected]>--- block/blk-mq.c | 17 ++++++++++++----- 1 file changed, 12 insertions(+), 5 deletions(-)
Cheers,
Hannes
Jens Axboe
maintainerPATCH v3 12/16 blk-mq: initial support for multiple queue maps
In reply to Jens AxboeAdd a queue offset to the tag map. This enables users to map iteratively, for each queue map type they support.
Bump maximum number of supported maps to 2, we're now fully able to support more than 1 map.
--- block/blk-mq-cpumap.c | 9 +++++---- block/blk-mq-pci.c | 2 +- block/blk-mq-virtio.c | 2 +- include/linux/blk-mq.h | 3 ++- 4 files changed, 9 insertions(+), 7 deletions(-)
block/blk-mq-cpumap.c +5 -4
@@ -14,9 +14,10 @@#include "blk.h" #include "blk-mq.h" -static int cpu_to_queue_index (unsigned int nr_queues,const int cpu) +static int cpu_to_queue_index (struct blk_mq_queue_map *qmap, +unsigned int nr_queues,const int cpu) { -return cpu % nr_queues; +return qmap->queue_offset + (cpu % nr_queues); }static int get_first_sibling (unsigned int cpu)
@@ -44,11 +45,11 @@ int blk_mq_map_queues(struct blk_mq_queue_map *qmap)* performace optimizations. */ if (cpu < nr_queues) { - map[cpu] =cpu_to_queue_index (nr_queues, cpu); + map[cpu] =cpu_to_queue_index (qmap, nr_queues, cpu); }else { first_sibling =get_first_sibling (cpu);if (first_sibling == cpu) - map[cpu] =cpu_to_queue_index (nr_queues, cpu); + map[cpu] =cpu_to_queue_index (qmap, nr_queues, cpu);else map[cpu] = map[first_sibling]; }
block/blk-mq-pci.c +1 -1
@@ -43,7 +43,7 @@ int blk_mq_pci_map_queues(struct blk_mq_queue_map *qmap, struct pci_dev *pdev, goto fallback;for_each_cpu (cpu, mask) - qmap->mq_map[cpu] = queue; + qmap->mq_map[cpu] = qmap->queue_offset + queue; }return 0 ;
block/blk-mq-virtio.c +1 -1
@@ -44,7 +44,7 @@ int blk_mq_virtio_map_queues(struct blk_mq_queue_map *qmap, goto fallback;for_each_cpu (cpu, mask) - qmap->mq_map[cpu] = queue; + qmap->mq_map[cpu] = qmap->queue_offset + queue; }return 0 ;
include/linux/blk-mq.h +2 -1
@@ -78,10 +78,11 @@ struct blk_mq_hw_ctx {struct blk_mq_queue_map {unsigned int *mq_map;unsigned int nr_queues; +unsigned int queue_offset; };enum { -HCTX_MAX_TYPES =1 , +HCTX_MAX_TYPES =2 , };struct blk_mq_tag_set {
Signature · 1 line
2.17.1
Re: [PATCH 12/16] blk-mq: initial support for multiple queue maps
Jens Axboe
maintainerPATCH v3 13/16 irq: add support for allocating (and affinitizing) sets of IRQs
In reply to Jens AxboeA driver may have a need to allocate multiple sets of MSI/MSI-X interrupts, and have them appropriately affinitized. Add support for defining a number of sets in the irq_affinity structure, of varying sizes, and get each set affinitized correctly across the machine.
--- drivers/pci/msi.c | 14 ++++++++++++++ include/linux/interrupt.h | 4 ++++ kernel/irq/affinity.c | 40 ++++++++++++++++++++++++++++++--------- 3 files changed, 49 insertions(+), 9 deletions(-)
drivers/pci/msi.c +14 -0
@@ -1036,6 +1036,13 @@ static int __pci_enable_msi_range(struct pci_dev *dev, int minvec, int maxvec,if (maxvec < minvec)return -ERANGE ; +/* +* If the caller is passing in sets, we can't support a range of +* vectors. The caller needs to handle that. +*/ +if (affd->nr_sets && minvec != maxvec) +return -EINVAL ; +if (WARN_ON_ONCE (dev->msi_enabled))return -EINVAL ;
@@ -1087,6 +1094,13 @@ static int __pci_enable_msix_range(struct pci_dev *dev,if (maxvec < minvec)return -ERANGE ; +/* +* If the caller is passing in sets, we can't support a range of +* supported vectors. The caller needs to handle that. +*/ +if (affd->nr_sets && minvec != maxvec) +return -EINVAL ; +if (WARN_ON_ONCE (dev->msix_enabled))return -EINVAL ;
include/linux/interrupt.h +4 -0
@@ -247,10 +247,14 @@ struct irq_affinity_notify {* the MSI(-X) vector space * @post_vectors: Don't apply affinity to @post_vectors at end of * the MSI(-X) vector space +* @nr_sets: Length of passed in *sets array +* @sets: Number of affinitized sets */ struct irq_affinity {int pre_vectors;int post_vectors; +int nr_sets; +int *sets; };#if defined(CONFIG_SMP )
kernel/irq/affinity.c +31 -9
@@ -180,6 +180,7 @@ irq_create_affinity_masks(int nvecs, const struct irq_affinity *affd)int curvec, usedvecs;cpumask_var_t nmsk, npresmsk, *node_to_cpumask;struct cpumask *masks =NULL ; +int i, nr_sets;/* * If there aren't any vectors left after applying the pre/post
@@ -210,10 +211,23 @@ irq_create_affinity_masks(int nvecs, const struct irq_affinity *affd)get_online_cpus ();build_node_to_cpumask (node_to_cpumask); -/* Spread on present CPUs starting from affd->pre_vectors */ - usedvecs =irq_build_affinity_masks (affd, curvec, affvecs, - node_to_cpumask, cpu_present_mask, - nmsk, masks); +/* +* Spread on present CPUs starting from affd->pre_vectors. If we +* have multiple sets, build each sets affinity mask separately. +*/ + nr_sets = affd->nr_sets; +if (!nr_sets) + nr_sets =1 ; + +for (i =0 , usedvecs =0 ; i < nr_sets; i++) { +int this_vecs = affd->sets ? affd->sets[i] : affvecs; +int nr; + + nr =irq_build_affinity_masks (affd, curvec, this_vecs, + node_to_cpumask, cpu_present_mask, + nmsk, masks + usedvecs); + usedvecs += nr; + }/* * Spread on non present CPUs starting from the next vector to be
@@ -258,13 +272,21 @@ int irq_calc_affinity_vectors(int minvec, int maxvec, const struct irq_affinity {int resv = affd->pre_vectors + affd->post_vectors;int vecs = maxvec - resv; -int ret; +int set_vecs;if (resv > minvec)return 0 ; -get_online_cpus (); - ret =min_t (int,cpumask_weight (cpu_possible_mask), vecs) + resv; -put_online_cpus (); -return ret; +if (affd->nr_sets) { +int i; + +for (i =0 , set_vecs =0 ; i < affd->nr_sets; i++) + set_vecs += affd->sets[i]; + }else { +get_online_cpus (); + set_vecs =cpumask_weight (cpu_possible_mask); +put_online_cpus (); + } + +return resv +min (set_vecs, vecs); }
Signature · 1 line
2.17.1
Re: [PATCH 13/16] irq: add support for allocating (and affinitizing) sets of IRQs
Re: [PATCH 13/16] irq: add support for allocating (and affinitizing) sets of IRQs
In reply to Jens AxboeQuoted 92 lines · Jens Axboe
A driver may have a need to allocate multiple sets of MSI/MSI-X interrupts, and have them appropriately affinitized. Add support for defining a number of sets in the
irq_affinitystructure, of varying sizes, and get each set affinitized correctly across the machine.Cc Thomas Gleixner <[email protected]>Reviewed-by Hannes Reinecke <[email protected]>Reviewed-by Ming Lei <[email protected]>Signed-off-by Jens Axboe <[email protected]>--- drivers/pci/msi.c | 14 ++++++++++++++ include/linux/interrupt.h | 4 ++++ kernel/irq/affinity.c | 40 ++++++++++++++++++++++++++++++--------- 3 files changed, 49 insertions(+), 9 deletions(-)drivers/pci/msi.c +14
-0@@ -1036,6 +1036,13 @@ static int __pci_enable_msi_range(struct pci_dev *dev, int minvec, int maxvec,if (maxvec < minvec)return -ERANGE ; +/* +* If the caller is passing in sets, we can't support a range of +* vectors. The caller needs to handle that. +*/ +if (affd->nr_sets && minvec != maxvec) +return -EINVAL ; +if (WARN_ON_ONCE (dev->msi_enabled))return -EINVAL ;@@ -1087,6 +1094,13 @@ static int __pci_enable_msix_range(struct pci_dev *dev,if (maxvec < minvec)return -ERANGE ; +/* +* If the caller is passing in sets, we can't support a range of +* supported vectors. The caller needs to handle that. +*/ +if (affd->nr_sets && minvec != maxvec) +return -EINVAL ; +if (WARN_ON_ONCE (dev->msix_enabled))return -EINVAL ;include/linux/interrupt.h +4
-0@@ -247,10 +247,14 @@ struct irq_affinity_notify {* the MSI(-X) vector space * @post_vectors: Don't apply affinity to @post_vectors at end of * the MSI(-X) vector space +* @nr_sets: Length of passed in *sets array +* @sets: Number of affinitized sets */ struct irq_affinity {int pre_vectors;int post_vectors; +int nr_sets; +int *sets; };#if defined(CONFIG_SMP )kernel/irq/affinity.c +6
-4@@ -180,6 +180,7 @@ irq_create_affinity_masks(int nvecs, const struct irq_affinity *affd)int curvec, usedvecs;cpumask_var_t nmsk, npresmsk, *node_to_cpumask;struct cpumask *masks =NULL ; +int i, nr_sets;/* * If there aren't any vectors left after applying the pre/post @@ -210,10 +211,23 @@ irq_create_affinity_masks(int nvecs, const struct irq_affinity *affd)get_online_cpus ();build_node_to_cpumask (node_to_cpumask); -/* Spread on present CPUs starting from affd->pre_vectors */ - usedvecs =irq_build_affinity_masks (affd, curvec, affvecs, - node_to_cpumask, cpu_present_mask, - nmsk, masks); +/* +* Spread on present CPUs starting from affd->pre_vectors. If we +* have multiple sets, build each sets affinity mask separately. +*/ + nr_sets = affd->nr_sets;
kernel/irq/affinity.c irq_create_affinity_masks(int nvecs, const struct irq_affinity *affd) · line 228
Quoted 10 lines
kernel/irq/affinity.c +10
-0+if (!nr_sets) + nr_sets =1 ; + +for (i =0 , usedvecs =0 ; i < nr_sets; i++) { +int this_vecs = affd->sets ? affd->sets[i] : affvecs; +int nr; + + nr =irq_build_affinity_masks (affd, curvec, this_vecs, + node_to_cpumask, cpu_present_mask, + nmsk, masks + usedvecs);
The last parameter of the above function should have been 'masks', because irq_build_affinity_masks() always treats 'masks' as the base address of the array.
kernel/irq/affinity.c irq_create_affinity_masks(int nvecs, const struct irq_affinity *affd) · line 230
Quoted 2 lines
kernel/irq/affinity.c +2
-0+ usedvecs += nr; + }
Thinking of further, one big problem in this patch is that each set of IRQs should have been spread on all possible CPUs, which is done via 2-stages spread now.
However, this patch only spreads each set of IRQs on present CPUs, this way may not work in case of physical CPU hotplug.
Thanks,
Ming
Re: [PATCH 13/16] irq: add support for allocating (and affinitizing) sets of IRQs
Quoted 99 lines · Ming Lei
Quoted 98 lines · Jens Axboe
A driver may have a need to allocate multiple sets of MSI/MSI-X interrupts, and have them appropriately affinitized. Add support for defining a number of sets in the
irq_affinitystructure, of varying sizes, and get each set affinitized correctly across the machine.Cc Thomas Gleixner <tglx at linutronix.de>Cc linux-kernel at vger.kernel.orgReviewed-by Hannes Reinecke <hare at suse.com>Reviewed-by Ming Lei <ming.lei at redhat.com>Signed-off-by Jens Axboe <axboe at kernel.dk>--- drivers/pci/msi.c | 14 ++++++++++++++ include/linux/interrupt.h | 4 ++++ kernel/irq/affinity.c | 40 ++++++++++++++++++++++++++++++--------- 3 files changed, 49 insertions(+), 9 deletions(-)drivers/pci/msi.c +14
-0@@ -1036,6 +1036,13 @@ static int __pci_enable_msi_range(struct pci_dev *dev, int minvec, int maxvec,if (maxvec < minvec)return -ERANGE ; +/* +* If the caller is passing in sets, we can't support a range of +* vectors. The caller needs to handle that. +*/ +if (affd->nr_sets && minvec != maxvec) +return -EINVAL ; +if (WARN_ON_ONCE (dev->msi_enabled))return -EINVAL ;@@ -1087,6 +1094,13 @@ static int __pci_enable_msix_range(struct pci_dev *dev,if (maxvec < minvec)return -ERANGE ; +/* +* If the caller is passing in sets, we can't support a range of +* supported vectors. The caller needs to handle that. +*/ +if (affd->nr_sets && minvec != maxvec) +return -EINVAL ; +if (WARN_ON_ONCE (dev->msix_enabled))return -EINVAL ;include/linux/interrupt.h +4
-0@@ -247,10 +247,14 @@ struct irq_affinity_notify {* the MSI(-X) vector space * @post_vectors: Don't apply affinity to @post_vectors at end of * the MSI(-X) vector space +* @nr_sets: Length of passed in *sets array +* @sets: Number of affinitized sets */ struct irq_affinity {int pre_vectors;int post_vectors; +int nr_sets; +int *sets; };#if defined(CONFIG_SMP )kernel/irq/affinity.c +12
-4@@ -180,6 +180,7 @@ irq_create_affinity_masks(int nvecs, const struct irq_affinity *affd)int curvec, usedvecs;cpumask_var_t nmsk, npresmsk, *node_to_cpumask;struct cpumask *masks =NULL ; +int i, nr_sets;/* * If there aren't any vectors left after applying the pre/post @@ -210,10 +211,23 @@ irq_create_affinity_masks(int nvecs, const struct irq_affinity *affd)get_online_cpus ();build_node_to_cpumask (node_to_cpumask); -/* Spread on present CPUs starting from affd->pre_vectors */ - usedvecs =irq_build_affinity_masks (affd, curvec, affvecs, - node_to_cpumask, cpu_present_mask, - nmsk, masks); +/* +* Spread on present CPUs starting from affd->pre_vectors. If we +* have multiple sets, build each sets affinity mask separately. +*/ + nr_sets = affd->nr_sets; +if (!nr_sets) + nr_sets =1 ; + +for (i =0 , usedvecs =0 ; i < nr_sets; i++) { +int this_vecs = affd->sets ? affd->sets[i] : affvecs; +int nr;
Quoted 8 lines
kernel/irq/affinity.c irq_create_affinity_masks(int nvecs, const struct irq_affinity *affd) · line 228
Quoted 4 lines
kernel/irq/affinity.c +4
-0+ + nr =irq_build_affinity_masks (affd, curvec, this_vecs, + node_to_cpumask, cpu_present_mask, + nmsk, masks + usedvecs);The last parameter of the above function should have been 'masks', because
irq_build_affinity_masks()always treats 'masks' as the base address of the array.
We have multiple "bases" when using sets, so we have to update which base to use by adding accordingly. If you just use 'masks', then you're going to overwrite your masks from the previous set.
Re: [PATCH 13/16] irq: add support for allocating (and affinitizing) sets of IRQs
Quoted 109 lines · Keith Busch
Quoted 99 lines · Ming Lei
Quoted 98 lines · Jens Axboe
A driver may have a need to allocate multiple sets of MSI/MSI-X interrupts, and have them appropriately affinitized. Add support for defining a number of sets in the
irq_affinitystructure, of varying sizes, and get each set affinitized correctly across the machine.Cc Thomas Gleixner <[email protected]>Reviewed-by Hannes Reinecke <[email protected]>Reviewed-by Ming Lei <[email protected]>Signed-off-by Jens Axboe <[email protected]>--- drivers/pci/msi.c | 14 ++++++++++++++ include/linux/interrupt.h | 4 ++++ kernel/irq/affinity.c | 40 ++++++++++++++++++++++++++++++--------- 3 files changed, 49 insertions(+), 9 deletions(-)drivers/pci/msi.c +14
-0@@ -1036,6 +1036,13 @@ static int __pci_enable_msi_range(struct pci_dev *dev, int minvec, int maxvec,if (maxvec < minvec)return -ERANGE ; +/* +* If the caller is passing in sets, we can't support a range of +* vectors. The caller needs to handle that. +*/ +if (affd->nr_sets && minvec != maxvec) +return -EINVAL ; +if (WARN_ON_ONCE (dev->msi_enabled))return -EINVAL ;@@ -1087,6 +1094,13 @@ static int __pci_enable_msix_range(struct pci_dev *dev,if (maxvec < minvec)return -ERANGE ; +/* +* If the caller is passing in sets, we can't support a range of +* supported vectors. The caller needs to handle that. +*/ +if (affd->nr_sets && minvec != maxvec) +return -EINVAL ; +if (WARN_ON_ONCE (dev->msix_enabled))return -EINVAL ;include/linux/interrupt.h +4
-0@@ -247,10 +247,14 @@ struct irq_affinity_notify {* the MSI(-X) vector space * @post_vectors: Don't apply affinity to @post_vectors at end of * the MSI(-X) vector space +* @nr_sets: Length of passed in *sets array +* @sets: Number of affinitized sets */ struct irq_affinity {int pre_vectors;int post_vectors; +int nr_sets; +int *sets; };#if defined(CONFIG_SMP )kernel/irq/affinity.c +12
-4@@ -180,6 +180,7 @@ irq_create_affinity_masks(int nvecs, const struct irq_affinity *affd)int curvec, usedvecs;cpumask_var_t nmsk, npresmsk, *node_to_cpumask;struct cpumask *masks =NULL ; +int i, nr_sets;/* * If there aren't any vectors left after applying the pre/post @@ -210,10 +211,23 @@ irq_create_affinity_masks(int nvecs, const struct irq_affinity *affd)get_online_cpus ();build_node_to_cpumask (node_to_cpumask); -/* Spread on present CPUs starting from affd->pre_vectors */ - usedvecs =irq_build_affinity_masks (affd, curvec, affvecs, - node_to_cpumask, cpu_present_mask, - nmsk, masks); +/* +* Spread on present CPUs starting from affd->pre_vectors. If we +* have multiple sets, build each sets affinity mask separately. +*/ + nr_sets = affd->nr_sets; +if (!nr_sets) + nr_sets =1 ; + +for (i =0 , usedvecs =0 ; i < nr_sets; i++) { +int this_vecs = affd->sets ? affd->sets[i] : affvecs; +int nr;Quoted 8 lines
kernel/irq/affinity.c irq_create_affinity_masks(int nvecs, const struct irq_affinity *affd) · line 228
Quoted 4 lines
kernel/irq/affinity.c +4
-0+ + nr =irq_build_affinity_masks (affd, curvec, this_vecs, + node_to_cpumask, cpu_present_mask, + nmsk, masks + usedvecs);The last parameter of the above function should have been 'masks', because
irq_build_affinity_masks()always treats 'masks' as the base address of the array.
Quoted 3 lines
We have multiple "bases" when using sets, so we have to update which base to use by adding accordingly. If you just use 'masks', then you're going to overwrite your masks from the previous set.
For irq_build_affinity_masks(), the passed 'startvec' is always relative to the absolute 1st element, so the passed 'masks' should be always the absolute base too. Not mention 'curvec' isn't updated in this patch too.
If you test this patchset on one machine which possible CPUs is bigger than present CPUs, you will see the problems I mentioned.
Thanks,
Ming
Jens Axboe
maintainerPATCH v3 14/16 nvme: utilize two queue maps, one for reads and one for writes
In reply to Jens AxboeNVMe does round-robin between queues by default, which means that sharing a queue map for both reads and writes can be problematic in terms of read servicing. It's much easier to flood the queue with writes and reduce the read servicing.
Implement two queue maps, one for reads and one for writes. The write queue count is configurable through the 'write_queues' parameter.
By default, we retain the previous behavior of having a single queue set, shared between reads and writes. Setting 'write_queues' to a non-zero value will create two queue sets, one for reads and one for writes, the latter using the configurable number of queues (hardware queue counts permitting).
--- drivers/nvme/host/pci.c | 174 +++++++++++++++++++++++++++++++++++++--- 1 file changed, 162 insertions(+), 12 deletions(-)
drivers/nvme/host/pci.c +162 -12
@@ -74,11 +74,29 @@ static int io_queue_depth = 1024;module_param_cb (io_queue_depth, &io_queue_depth_ops, &io_queue_depth,0644 );MODULE_PARM_DESC (io_queue_depth,"set io queue depth, should >= 2" ); +static int queue_count_set (const char *val,const struct kernel_param *kp); +static const struct kernel_param_ops queue_count_ops = { + .set = queue_count_set, + .get = param_get_int, +}; + +static int write_queues; +module_param_cb (write_queues, &queue_count_ops, &write_queues,0644 ); +MODULE_PARM_DESC (write_queues, +"Number of queues to use for writes. If not set, reads and writes " +"will share a queue set." ); +struct nvme_dev ;struct nvme_queue ;static void nvme_dev_disable (struct nvme_dev *dev,bool shutdown); +enum { +NVMEQ_TYPE_READ , +NVMEQ_TYPE_WRITE , +NVMEQ_TYPE_NR , +}; +/* * Represents an NVM Express device. Each nvme_dev is a PCI function. */
@@ -92,6 +110,7 @@ struct nvme_dev {struct dma_pool *prp_small_pool;unsigned online_queues;unsigned max_qid; +unsigned io_queues[NVMEQ_TYPE_NR ];unsigned int num_vecs;int q_depth;u32 db_stride;
@@ -134,6 +153,17 @@ static int io_queue_depth_set(const char *val, const struct kernel_param *kp)return param_set_int (val, kp); } +static int queue_count_set (const char *val,const struct kernel_param *kp) +{ +int n =0 , ret; + + ret =kstrtoint (val,10 , &n); +if (n >num_possible_cpus ()) + n =num_possible_cpus (); + +return param_set_int (val, kp); +} +static inline unsigned int sq_idx (unsigned int qid,u32 stride) {return qid *2 * stride;
@@ -218,9 +248,20 @@ static inline void _nvme_check_size(void)BUILD_BUG_ON (sizeof (struct nvme_dbbuf ) !=64 ); } +static unsigned int max_io_queues (void ) +{ +return num_possible_cpus () + write_queues; +} + +static unsigned int max_queue_count (void ) +{ +/* IO queues + admin queue */ +return 1 +max_io_queues (); +} +static inline unsigned int nvme_dbbuf_size (u32 stride) { -return ((num_possible_cpus () +1 ) *8 * stride); +return (max_queue_count () *8 * stride); }static int nvme_dbbuf_dma_alloc (struct nvme_dev *dev)
@@ -431,12 +472,41 @@ static int nvme_init_request(struct blk_mq_tag_set *set, struct request *req,return 0 ; } +static int queue_irq_offset (struct nvme_dev *dev) +{ +/* if we have more than 1 vec, admin queue offsets us 1 */ +if (dev->num_vecs >1 ) +return 1 ; + +return 0 ; +} +static int nvme_pci_map_queues (struct blk_mq_tag_set *set) {struct nvme_dev *dev = set->driver_data; +int i, qoff, offset; + + offset =queue_irq_offset (dev); +for (i =0 , qoff =0 ; i < set->nr_maps; i++) { +struct blk_mq_queue_map *map = &set->map[i]; -return blk_mq_pci_map_queues (&set->map[0 ],to_pci_dev (dev->dev), - dev->num_vecs >1 ?1 /* admin queue */ :0 ); + map->nr_queues = dev->io_queues[i]; +if (!map->nr_queues) { +BUG_ON (i ==NVMEQ_TYPE_READ ); + +/* shared set, resuse read set parameters */ + map->nr_queues = dev->io_queues[NVMEQ_TYPE_READ ]; + qoff =0 ; + offset =queue_irq_offset (dev); + } + + map->queue_offset = qoff; +blk_mq_pci_map_queues (map,to_pci_dev (dev->dev), offset); + qoff += map->nr_queues; + offset += map->nr_queues; + } + +return 0 ; }/**
@@ -849,6 +919,14 @@ static blk_status_t nvme_queue_rq(struct blk_mq_hw_ctx *hctx,return ret; } +static int nvme_flags_to_type (struct request_queue *q,unsigned int flags) +{ +if ((flags &REQ_OP_MASK ) ==REQ_OP_READ ) +return NVMEQ_TYPE_READ ; + +return NVMEQ_TYPE_WRITE ; +} +static void nvme_pci_complete_rq (struct request *req) {struct nvme_iod *iod =blk_mq_rq_to_pdu (req);
@@ -1476,6 +1554,7 @@ static const struct blk_mq_ops nvme_mq_admin_ops = {static const struct blk_mq_ops nvme_mq_ops = { .queue_rq = nvme_queue_rq, + .flags_to_type = nvme_flags_to_type, .complete = nvme_pci_complete_rq, .init_hctx = nvme_init_hctx, .init_request = nvme_init_request,
@@ -1888,18 +1967,53 @@ static int nvme_setup_host_mem(struct nvme_dev *dev)return ret; } +static void nvme_calc_io_queues (struct nvme_dev *dev,unsigned int nr_io_queues) +{ +unsigned int this_w_queues = write_queues; + +/* +* Setup read/write queue split +*/ +if (nr_io_queues ==1 ) { + dev->io_queues[NVMEQ_TYPE_READ ] =1 ; + dev->io_queues[NVMEQ_TYPE_WRITE ] =0 ; +return ; + } + +/* +* If 'write_queues' is set, ensure it leaves room for at least +* one read queue +*/ +if (this_w_queues >= nr_io_queues) + this_w_queues = nr_io_queues -1 ; + +/* +* If 'write_queues' is set to zero, reads and writes will share +* a queue set. +*/ +if (!this_w_queues) { + dev->io_queues[NVMEQ_TYPE_WRITE ] =0 ; + dev->io_queues[NVMEQ_TYPE_READ ] = nr_io_queues; + }else { + dev->io_queues[NVMEQ_TYPE_WRITE ] = this_w_queues; + dev->io_queues[NVMEQ_TYPE_READ ] = nr_io_queues - this_w_queues; + } +} +static int nvme_setup_io_queues (struct nvme_dev *dev) {struct nvme_queue *adminq = &dev->queues[0 ];struct pci_dev *pdev =to_pci_dev (dev->dev);int result, nr_io_queues;unsigned long size; - +int irq_sets[2 ];struct irq_affinity affd = { - .pre_vectors =1 + .pre_vectors =1 , + .nr_sets =ARRAY_SIZE (irq_sets), + .sets = irq_sets, }; - nr_io_queues =num_possible_cpus (); + nr_io_queues =max_io_queues (); result =nvme_set_queue_count (&dev->ctrl, &nr_io_queues);if (result <0 )return result;
@@ -1934,13 +2048,48 @@ static int nvme_setup_io_queues(struct nvme_dev *dev)* setting up the full range we need. */ pci_free_irq_vectors (pdev); - result =pci_alloc_irq_vectors_affinity (pdev,1 , nr_io_queues +1 , -PCI_IRQ_ALL_TYPES |PCI_IRQ_AFFINITY , &affd); -if (result <=0 ) -return -EIO ; + +/* +* For irq sets, we have to ask for minvec == maxvec. This passes +* any reduction back to us, so we can adjust our queue counts and +* IRQ vector needs. +*/ +do { +nvme_calc_io_queues (dev, nr_io_queues); + irq_sets[0 ] = dev->io_queues[NVMEQ_TYPE_READ ]; + irq_sets[1 ] = dev->io_queues[NVMEQ_TYPE_WRITE ]; +if (!irq_sets[1 ]) + affd.nr_sets =1 ; + +/* +* Need IRQs for read+write queues, and one for the admin queue +*/ + nr_io_queues = irq_sets[0 ] + irq_sets[1 ] +1 ; + + result =pci_alloc_irq_vectors_affinity (pdev, nr_io_queues, + nr_io_queues, +PCI_IRQ_ALL_TYPES |PCI_IRQ_AFFINITY , &affd); + +/* +* Need to reduce our vec counts +*/ +if (result == -ENOSPC ) { + nr_io_queues--; +if (!nr_io_queues) +return result; +continue ; + }else if (result <=0 ) +return -EIO ; +break ; + }while (1 ); + dev->num_vecs = result; dev->max_qid =max (result -1 ,1 ); +dev_info (dev->ctrl.device,"%d/%d/%d read/write queues\n" , + dev->io_queues[NVMEQ_TYPE_READ ], + dev->io_queues[NVMEQ_TYPE_WRITE ]); +/* * Should investigate if there's a performance win from allocating * more queues than interrupt vectors; it might allow the submission
@@ -2042,6 +2191,7 @@ static int nvme_dev_add(struct nvme_dev *dev)if (!dev->ctrl.tagset) { dev->tagset.ops = &nvme_mq_ops; dev->tagset.nr_hw_queues = dev->online_queues -1 ; + dev->tagset.nr_maps =NVMEQ_TYPE_NR ; dev->tagset.timeout =NVME_IO_TIMEOUT ; dev->tagset.numa_node =dev_to_node (dev->dev); dev->tagset.queue_depth =
@@ -2489,8 +2639,8 @@ static int nvme_probe(struct pci_dev *pdev, const struct pci_device_id *id)if (!dev)return -ENOMEM ; - dev->queues =kcalloc_node (num_possible_cpus () +1 , -sizeof (struct nvme_queue ),GFP_KERNEL , node); + dev->queues =kcalloc_node (max_queue_count (),sizeof (struct nvme_queue ), +GFP_KERNEL , node);if (!dev->queues) goto free;
Signature · 1 line
2.17.1
Sagi Grimberg
maintainerRe: [PATCH 14/16] nvme: utilize two queue maps, one for reads and one for writes
drivers/nvme/host/pci.c static int nvme_init_request(struct blk_mq_tag_set *set, struct request *req, · line 477
Quoted 4 lines
drivers/nvme/host/pci.c +3
-0+static int queue_irq_offset (struct nvme_dev *dev) +{ +/* if we have more than 1 vec, admin queue offsets us 1 */
offsets us by 1?
Quoted 6 lines
drivers/nvme/host/pci.c +6
-0+if (dev->num_vecs >1 ) +return 1 ; + +return 0 ; +} +
...
Quoted 40 lines
drivers/nvme/host/pci.c +32
-4@@ -1934,13 +2048,48 @@ static int nvme_setup_io_queues(struct nvme_dev *dev)* setting up the full range we need. */ pci_free_irq_vectors (pdev); - result =pci_alloc_irq_vectors_affinity (pdev,1 , nr_io_queues +1 , -PCI_IRQ_ALL_TYPES |PCI_IRQ_AFFINITY , &affd); -if (result <=0 ) -return -EIO ; + +/* +* For irq sets, we have to ask for minvec == maxvec. This passes +* any reduction back to us, so we can adjust our queue counts and +* IRQ vector needs. +*/ + do { + nvme_calc_io_queues(dev, nr_io_queues); + irq_sets[0] = dev->io_queues[NVMEQ_TYPE_READ]; + irq_sets[1] = dev->io_queues[NVMEQ_TYPE_WRITE]; + if (!irq_sets[1]) + affd.nr_sets = 1; + + /* + * Need IRQs for read+write queues, and one for the admin queue + */ + nr_io_queues = irq_sets[0] + irq_sets[1] + 1; + + result = pci_alloc_irq_vectors_affinity(pdev, nr_io_queues, + nr_io_queues, + PCI_IRQ_ALL_TYPES | PCI_IRQ_AFFINITY, &affd); + + /* + * Need to reduce our vec counts + */ + if (result == -ENOSPC) { + nr_io_queues--; + if (!nr_io_queues) + return result; + continue; + } else if (result <= 0) + return -EIO;
drivers/nvme/host/pci.c static int nvme_setup_io_queues(struct nvme_dev *dev) · line 2092
Quoted 10 lines
drivers/nvme/host/pci.c +7
-0+break ; + }while (1 ); + dev->num_vecs = result; dev->max_qid =max (result -1 ,1 ); +dev_info (dev->ctrl.device,"%d/%d/%d read/write queues\n" , + dev->io_queues[NVMEQ_TYPE_READ ], + dev->io_queues[NVMEQ_TYPE_WRITE ]); +
Perhaps it would be better if we move this code into a function.
Jens Axboe
maintainerRe: [PATCH 14/16] nvme: utilize two queue maps, one for reads and one for writes
Quoted 6 lines · Sagi Grimberg
drivers/nvme/host/pci.c static int nvme_init_request(struct blk_mq_tag_set *set, struct request *req, · line 477
Quoted 4 lines
drivers/nvme/host/pci.c +3
-0+static int queue_irq_offset (struct nvme_dev *dev) +{ +/* if we have more than 1 vec, admin queue offsets us 1 */ offsets us by 1?
Fixed
Quoted 46 lines
Quoted 46 lines
drivers/nvme/host/pci.c +35
-4@@ -1934,13 +2048,48 @@ static int nvme_setup_io_queues(struct nvme_dev *dev)* setting up the full range we need. */ pci_free_irq_vectors (pdev); - result =pci_alloc_irq_vectors_affinity (pdev,1 , nr_io_queues +1 , -PCI_IRQ_ALL_TYPES |PCI_IRQ_AFFINITY , &affd); -if (result <=0 ) -return -EIO ; + +/* +* For irq sets, we have to ask for minvec == maxvec. This passes +* any reduction back to us, so we can adjust our queue counts and +* IRQ vector needs. +*/ +do { +nvme_calc_io_queues (dev, nr_io_queues); + irq_sets[0 ] = dev->io_queues[NVMEQ_TYPE_READ ]; + irq_sets[1 ] = dev->io_queues[NVMEQ_TYPE_WRITE ]; +if (!irq_sets[1 ]) + affd.nr_sets =1 ; + +/* +* Need IRQs for read+write queues, and one for the admin queue +*/ + nr_io_queues = irq_sets[0 ] + irq_sets[1 ] +1 ; + + result =pci_alloc_irq_vectors_affinity (pdev, nr_io_queues, + nr_io_queues, +PCI_IRQ_ALL_TYPES |PCI_IRQ_AFFINITY , &affd); + +/* +* Need to reduce our vec counts +*/ +if (result == -ENOSPC ) { + nr_io_queues--; +if (!nr_io_queues) +return result; +continue ; + }else if (result <=0 ) +return -EIO ; +break ; + }while (1 ); + dev->num_vecs = result; dev->max_qid =max (result -1 ,1 );
Quoted 6 lines
drivers/nvme/host/pci.c static int nvme_setup_io_queues(struct nvme_dev *dev) · line 2092
Quoted 4 lines
drivers/nvme/host/pci.c +4
-0+dev_info (dev->ctrl.device,"%d/%d/%d read/write queues\n" , + dev->io_queues[NVMEQ_TYPE_READ ], + dev->io_queues[NVMEQ_TYPE_WRITE ]); +Perhaps it would be better if we move this code into a function.
Agree, I've done that now.
Signature · 1 line
Jens Axboe
Jens Axboe
maintainerPATCH v3 15/16 block: add REQ_HIPRI and inherit it from IOCB_HIPRI
In reply to Jens AxboeWe use IOCB_HIPRI to poll for IO in the caller instead of scheduling. This information is not available for (or after) IO submission. The driver may make different queue choices based on the type of IO, so make the fact that we will poll for this IO known to the lower layers as well.
--- fs/block_dev.c | 2 ++ fs/direct-io.c | 2 ++ fs/iomap.c | 9 ++++++++- include/linux/blk_types.h | 4 +++- 4 files changed, 15 insertions(+), 2 deletions(-)
fs/block_dev.c +2 -0
@@ -232,6 +232,8 @@ __blkdev_direct_IO_simple(struct kiocb *iocb, struct iov_iter *iter, bio.bi_opf =dio_bio_write_op (iocb);task_io_account_write (ret); } +if (iocb->ki_flags &IOCB_HIPRI ) + bio.bi_opf |=REQ_HIPRI ; qc =submit_bio (&bio);for (;;) {
fs/direct-io.c +2 -0
@@ -1265,6 +1265,8 @@ do_blockdev_direct_IO(struct kiocb *iocb, struct inode *inode, }else { dio->op =REQ_OP_READ ; } +if (iocb->ki_flags &IOCB_HIPRI ) + dio->op_flags |=REQ_HIPRI ;/* * For AIO O_(D)SYNC writes we need to defer completions to a workqueue
fs/iomap.c +8 -1
@@ -1554,6 +1554,7 @@ iomap_dio_zero(struct iomap_dio *dio, struct iomap *iomap, loff_t pos, unsigned len) {struct page *page =ZERO_PAGE (0 ); +int flags =REQ_SYNC |REQ_IDLE ;struct bio *bio; bio =bio_alloc (GFP_KERNEL ,1 );
@@ -1562,9 +1563,12 @@ iomap_dio_zero(struct iomap_dio *dio, struct iomap *iomap, loff_t pos, bio->bi_private = dio; bio->bi_end_io = iomap_dio_bio_end_io; +if (dio->iocb->ki_flags &IOCB_HIPRI ) + flags |=REQ_HIPRI ; +get_page (page);__bio_add_page (bio, page, len,0 ); -bio_set_op_attrs (bio,REQ_OP_WRITE ,REQ_SYNC |REQ_IDLE ); +bio_set_op_attrs (bio,REQ_OP_WRITE , flags);atomic_inc (&dio->ref);return submit_bio (bio);
@@ -1663,6 +1667,9 @@ iomap_dio_bio_actor(struct inode *inode, loff_t pos, loff_t length,bio_set_pages_dirty (bio); } +if (dio->iocb->ki_flags &IOCB_HIPRI ) + bio->bi_opf |=REQ_HIPRI ; +iov_iter_advance (dio->submit.iter, n); dio->size += n;
include/linux/blk_types.h +3 -1
@@ -322,6 +322,8 @@ enum req_flag_bits {/* command specific flags for REQ_OP_WRITE_ZEROES: */ __REQ_NOUNMAP,/* do not free blocks when zeroing */ + __REQ_HIPRI, +/* for driver use */ __REQ_DRV, __REQ_SWAP, /* swapping request. */
@@ -342,8 +344,8 @@ enum req_flag_bits {#define REQ_RAHEAD (1ULL << __REQ_RAHEAD)#define REQ_BACKGROUND (1ULL << __REQ_BACKGROUND)#define REQ_NOWAIT (1ULL << __REQ_NOWAIT) -#define REQ_NOUNMAP (1ULL << __REQ_NOUNMAP) +#define REQ_HIPRI (1ULL << __REQ_HIPRI)#define REQ_DRV (1ULL << __REQ_DRV)#define REQ_SWAP (1ULL << __REQ_SWAP)
Signature · 1 line
2.17.1
Re: [PATCH 15/16] block: add REQ_HIPRI and inherit it from IOCB_HIPRI
Jens Axboe
maintainerPATCH v3 16/16 nvme: add separate poll queue map
In reply to Jens AxboeAdds support for defining a variable number of poll queues, currently configurable with the 'poll_queues' module parameter. Defaults to a single poll queue.
And now we finally have poll support without triggering interrupts!
--- drivers/nvme/host/pci.c | 97 +++++++++++++++++++++++++++++++++-------- include/linux/blk-mq.h | 2 +- 2 files changed, 81 insertions(+), 18 deletions(-)
drivers/nvme/host/pci.c +80 -17
@@ -86,6 +86,10 @@ MODULE_PARM_DESC(write_queues,"Number of queues to use for writes. If not set, reads and writes " "will share a queue set."); +static int poll_queues =1 ; +module_param_cb (poll_queues, &queue_count_ops, &poll_queues,0644 ); +MODULE_PARM_DESC (poll_queues,"Number of queues to use for polled IO." ); +struct nvme_dev ;struct nvme_queue ;
@@ -94,6 +98,7 @@ static void nvme_dev_disable(struct nvme_dev *dev, bool shutdown);enum {NVMEQ_TYPE_READ ,NVMEQ_TYPE_WRITE , +NVMEQ_TYPE_POLL ,NVMEQ_TYPE_NR , };
@@ -202,6 +207,7 @@ struct nvme_queue {u16 last_cq_head;u16 qid;u8 cq_phase; +u8 polled;u32 *dbbuf_sq_db;u32 *dbbuf_cq_db;u32 *dbbuf_sq_ei;
@@ -250,7 +256,7 @@ static inline void _nvme_check_size(void)static unsigned int max_io_queues (void ) { -return num_possible_cpus () + write_queues; +return num_possible_cpus () + write_queues + poll_queues; }static unsigned int max_queue_count (void )
@@ -500,8 +506,15 @@ static int nvme_pci_map_queues(struct blk_mq_tag_set *set) offset =queue_irq_offset (dev); } +/* +* The poll queue(s) doesn't have an IRQ (and hence IRQ +* affinity), so use the regular blk-mq cpu mapping +*/ map->queue_offset = qoff; -blk_mq_pci_map_queues (map,to_pci_dev (dev->dev), offset); +if (i !=NVMEQ_TYPE_POLL ) +blk_mq_pci_map_queues (map,to_pci_dev (dev->dev), offset); +else +blk_mq_map_queues (map); qoff += map->nr_queues; offset += map->nr_queues; }
@@ -892,7 +905,7 @@ static blk_status_t nvme_queue_rq(struct blk_mq_hw_ctx *hctx,* We should not need to do this, but we're still using this to * ensure we can drain requests on a dying queue. */ -if (unlikely (nvmeq->cq_vector <0 )) +if (unlikely (nvmeq->cq_vector <0 && !nvmeq->polled))return BLK_STS_IOERR ; ret =nvme_setup_cmd (ns, req, &cmnd);
@@ -921,6 +934,8 @@ static blk_status_t nvme_queue_rq(struct blk_mq_hw_ctx *hctx,static int nvme_flags_to_type (struct request_queue *q,unsigned int flags) { +if (flags &REQ_HIPRI ) +return NVMEQ_TYPE_POLL ;if ((flags &REQ_OP_MASK ) ==REQ_OP_READ )return NVMEQ_TYPE_READ ;
@@ -1094,7 +1109,10 @@ static int adapter_alloc_cq(struct nvme_dev *dev, u16 qid, struct nvme_queue *nvmeq, s16 vector) {struct nvme_command c; -int flags =NVME_QUEUE_PHYS_CONTIG |NVME_CQ_IRQ_ENABLED ; +int flags =NVME_QUEUE_PHYS_CONTIG ; + +if (vector !=-1 ) + flags |=NVME_CQ_IRQ_ENABLED ;/* * Note: we (ab)use the fact that the prp fields survive if no data
@@ -1106,7 +1124,10 @@ static int adapter_alloc_cq(struct nvme_dev *dev, u16 qid, c.create_cq.cqid =cpu_to_le16 (qid); c.create_cq.qsize =cpu_to_le16 (nvmeq->q_depth -1 ); c.create_cq.cq_flags =cpu_to_le16 (flags); - c.create_cq.irq_vector =cpu_to_le16 (vector); +if (vector !=-1 ) + c.create_cq.irq_vector =cpu_to_le16 (vector); +else + c.create_cq.irq_vector =0 ;return nvme_submit_sync_cmd (dev->ctrl.admin_q, &c,NULL ,0 ); }
@@ -1348,13 +1369,14 @@ static int nvme_suspend_queue(struct nvme_queue *nvmeq)int vector;spin_lock_irq (&nvmeq->cq_lock); -if (nvmeq->cq_vector ==-1 ) { +if (nvmeq->cq_vector ==-1 && !nvmeq->polled) {spin_unlock_irq (&nvmeq->cq_lock);return 1 ; } vector = nvmeq->cq_vector; nvmeq->dev->online_queues--; nvmeq->cq_vector =-1 ; + nvmeq->polled = false;spin_unlock_irq (&nvmeq->cq_lock);/*
@@ -1366,7 +1388,8 @@ static int nvme_suspend_queue(struct nvme_queue *nvmeq)if (!nvmeq->qid && nvmeq->dev->ctrl.admin_q)blk_mq_quiesce_queue (nvmeq->dev->ctrl.admin_q); -pci_free_irq (to_pci_dev (nvmeq->dev->dev), vector, nvmeq); +if (vector !=-1 ) +pci_free_irq (to_pci_dev (nvmeq->dev->dev), vector, nvmeq);return 0 ; }
@@ -1500,7 +1523,7 @@ static void nvme_init_queue(struct nvme_queue *nvmeq, u16 qid)spin_unlock_irq (&nvmeq->cq_lock); } -static int nvme_create_queue (struct nvme_queue *nvmeq,int qid) +static int nvme_create_queue (struct nvme_queue *nvmeq,int qid,bool polled) {struct nvme_dev *dev = nvmeq->dev;int result;
@@ -1510,7 +1533,11 @@ static int nvme_create_queue(struct nvme_queue *nvmeq, int qid)* A queue's vector matches the queue identifier unless the controller * has only one vector available. */ - vector = dev->num_vecs ==1 ?0 : qid; +if (!polled) + vector = dev->num_vecs ==1 ?0 : qid; +else + vector =-1 ; + result =adapter_alloc_cq (dev, qid, nvmeq, vector);if (result)return result;
@@ -1527,15 +1554,20 @@ static int nvme_create_queue(struct nvme_queue *nvmeq, int qid)* xxx' warning if the create CQ/SQ command times out. */ nvmeq->cq_vector = vector; + nvmeq->polled = polled;nvme_init_queue (nvmeq, qid); - result =queue_request_irq (nvmeq); -if (result <0 ) - goto release_sq; + +if (vector !=-1 ) { + result =queue_request_irq (nvmeq); +if (result <0 ) + goto release_sq; + }return result; release_sq: nvmeq->cq_vector =-1 ; + nvmeq->polled = false; dev->online_queues--;adapter_delete_sq (dev, qid); release_cq:
@@ -1686,7 +1718,7 @@ static int nvme_pci_configure_admin_queue(struct nvme_dev *dev)static int nvme_create_io_queues (struct nvme_dev *dev) { -unsigned i, max; +unsigned i, max, rw_queues;int ret =0 ;for (i = dev->ctrl.queue_count; i <= dev->max_qid; i++) {
@@ -1697,8 +1729,17 @@ static int nvme_create_io_queues(struct nvme_dev *dev) } max =min (dev->max_qid, dev->ctrl.queue_count -1 ); +if (max !=1 && dev->io_queues[NVMEQ_TYPE_POLL ]) { + rw_queues = dev->io_queues[NVMEQ_TYPE_READ ] + + dev->io_queues[NVMEQ_TYPE_WRITE ]; + }else { + rw_queues = max; + } +for (i = dev->online_queues; i <= max; i++) { - ret =nvme_create_queue (&dev->queues[i], i); +bool polled = i > rw_queues; + + ret =nvme_create_queue (&dev->queues[i], i, polled);if (ret)break ; }
@@ -1970,6 +2011,7 @@ static int nvme_setup_host_mem(struct nvme_dev *dev)static void nvme_calc_io_queues (struct nvme_dev *dev,unsigned int nr_io_queues) {unsigned int this_w_queues = write_queues; +unsigned int this_p_queues = poll_queues;/* * Setup read/write queue split
@@ -1977,9 +2019,28 @@ static void nvme_calc_io_queues(struct nvme_dev *dev, unsigned int nr_io_queues)if (nr_io_queues ==1 ) { dev->io_queues[NVMEQ_TYPE_READ ] =1 ; dev->io_queues[NVMEQ_TYPE_WRITE ] =0 ; + dev->io_queues[NVMEQ_TYPE_POLL ] =0 ;return ; } +/* +* Configure number of poll queues, if set +*/ +if (this_p_queues) { +/* +* We need at least one queue left. With just one queue, we'll +* have a single shared read/write set. +*/ +if (this_p_queues >= nr_io_queues) { + this_w_queues =0 ; + this_p_queues = nr_io_queues -1 ; + } + + dev->io_queues[NVMEQ_TYPE_POLL ] = this_p_queues; + nr_io_queues -= this_p_queues; + }else + dev->io_queues[NVMEQ_TYPE_POLL ] =0 ; +/* * If 'write_queues' is set, ensure it leaves room for at least * one read queue
@@ -2084,11 +2145,13 @@ static int nvme_setup_io_queues(struct nvme_dev *dev) }while (1 ); dev->num_vecs = result; - dev->max_qid =max (result -1 ,1 ); + result =max (result -1 ,1 ); + dev->max_qid = result + dev->io_queues[NVMEQ_TYPE_POLL ]; -dev_info (dev->ctrl.device,"%d/%d/%d read/write queues\n" , +dev_info (dev->ctrl.device,"%d/%d/%d read/write/poll queues\n" , dev->io_queues[NVMEQ_TYPE_READ ], - dev->io_queues[NVMEQ_TYPE_WRITE ]); + dev->io_queues[NVMEQ_TYPE_WRITE ], + dev->io_queues[NVMEQ_TYPE_POLL ]);/* * Should investigate if there's a performance win from allocating
include/linux/blk-mq.h +1 -1
@@ -82,7 +82,7 @@ struct blk_mq_queue_map { };enum { -HCTX_MAX_TYPES =2 , +HCTX_MAX_TYPES =3 , };struct blk_mq_tag_set {
Signature · 1 line
2.17.1
Quoted 20 lines · Jens Axboe
This series adds support for multiple queue maps for blk-mq. Since blk-mq was introduced, it's only support a single queue map. This means you can have 1 set of queues, and the mapping purely depends on what CPU an IO originated from. With this patch set, drivers can implement mappings that depend on both CPU and request type - and they can have multiple sets of mappings.
NVMe is used as a proof of concept. It adds support for a separate write queue set. One way to use this would be to limit the number of write queues to favor reads, since NVMe does round-robin service of queues. An easy extension of this would be to add multiple sets of queues, for prioritized IO.
NVMe also uses this feature to finally make the polling work efficiently, without triggering interrupts. This both increases performance (and decreases latency), at a lower system load. At the same time it's more flexible, as you don't have to worry about IRQ coalescing and redirection to avoid interrupts disturbing the workload. This is how polling should have worked from day 1.
Quoted 2 lines
This is on top of my mq-conversions branch. It can also be bound in my mq-maps branch.
Looks good to me. For the series: