blk-mq: Allow PCI vector offset for mapping queues
Subsystems (2)
PATCH 1/3 blk-mq: Allow PCI vector offset for mapping queues
The PCI interrupt vectors intended to be associated with a queue may not start at 0. This patch adds an offset parameter so blk-mq may find the intended affinity mask. The default value is 0 so existing drivers that don't care about this parameter don't need to change.
--- block/blk-mq-pci.c | 12 ++++++++++-- include/linux/blk-mq-pci.h | 2 ++ 2 files changed, 12 insertions(+), 2 deletions(-)
block/blk-mq-pci.c +10 -2
@@ -21,6 +21,7 @@* blk_mq_pci_map_queues - provide a default queue mapping for PCI device * @set: tagset to provide the mapping for * @pdev: PCI device associated with @set. +* @offset: PCI irq starting vector offset * * This function assumes the PCI device @pdev has at least as many available * interrupt vectors as @set has queues. It will then query the vector
@@ -28,13 +29,14 @@* 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_tag_set *set,struct pci_dev *pdev, +int offset) {const struct cpumask *mask;unsigned int queue, cpu;for (queue =0 ; queue < set->nr_hw_queues; queue++) { - mask =pci_irq_get_affinity (pdev, queue); + mask =pci_irq_get_affinity (pdev, queue + offset);if (!mask) goto fallback;
@@ -50,4 +52,10 @@ int blk_mq_pci_map_queues(struct blk_mq_tag_set *set, struct pci_dev *pdev) set->mq_map[cpu] =0 ;return 0 ; } +EXPORT_SYMBOL_GPL (__blk_mq_pci_map_queues); + +int blk_mq_pci_map_queues (struct blk_mq_tag_set *set,struct pci_dev *pdev) +{ +return __blk_mq_pci_map_queues (set, pdev,0 ); +}EXPORT_SYMBOL_GPL (blk_mq_pci_map_queues);
include/linux/blk-mq-pci.h +2 -0
@@ -5,6 +5,8 @@struct blk_mq_tag_set ;struct pci_dev ; +int __blk_mq_pci_map_queues (struct blk_mq_tag_set *set,struct pci_dev *pdev, +int offset);int blk_mq_pci_map_queues (struct blk_mq_tag_set *set,struct pci_dev *pdev);#endif /* _LINUX_BLK_MQ_PCI_H */
Signature · 1 line
2.14.3
PATCH 2/3 nvme-pci: Remove unused queue parameter
All nvme queue memory is allocated up front. We don't take the node into consideration when creating queues anymore, so removing the unused parameter.
--- drivers/nvme/host/pci.c | 10 +++------- 1 file changed, 3 insertions(+), 7 deletions(-)
drivers/nvme/host/pci.c +3 -7
@@ -1379,8 +1379,7 @@ static int nvme_alloc_sq_cmds(struct nvme_dev *dev, struct nvme_queue *nvmeq,return 0 ; } -static int nvme_alloc_queue (struct nvme_dev *dev,int qid, -int depth,int node) +static int nvme_alloc_queue (struct nvme_dev *dev,int qid,int depth) {struct nvme_queue *nvmeq = &dev->queues[qid];
@@ -1595,8 +1594,7 @@ static int nvme_pci_configure_admin_queue(struct nvme_dev *dev)if (result <0 )return result; - result =nvme_alloc_queue (dev,0 ,NVME_AQ_DEPTH , -dev_to_node (dev->dev)); + result =nvme_alloc_queue (dev,0 ,NVME_AQ_DEPTH );if (result)return result;
@@ -1629,9 +1627,7 @@ static int nvme_create_io_queues(struct nvme_dev *dev)int ret =0 ;for (i = dev->ctrl.queue_count; i <= dev->max_qid; i++) { -/* vector == qid - 1, match nvme_create_queue */ -if (nvme_alloc_queue (dev, i, dev->q_depth, -pci_irq_get_node (to_pci_dev (dev->dev), i -1 ))) { +if (nvme_alloc_queue (dev, i, dev->q_depth)) { ret = -ENOMEM ;break ; }
Signature · 1 line
2.14.3
Re: [PATCH 2/3] nvme-pci: Remove unused queue parameter
Quoted 34 lines · Keith Busch
All nvme queue memory is allocated up front. We don't take the node into consideration when creating queues anymore, so removing the unused parameter.
Signed-off-by Keith Busch <[email protected]>--- drivers/nvme/host/pci.c | 10 +++------- 1 file changed, 3 insertions(+), 7 deletions(-)drivers/nvme/host/pci.c +2
-4@@ -1379,8 +1379,7 @@ static int nvme_alloc_sq_cmds(struct nvme_dev *dev, struct nvme_queue *nvmeq,return 0 ; } -static int nvme_alloc_queue (struct nvme_dev *dev,int qid, -int depth,int node) +static int nvme_alloc_queue (struct nvme_dev *dev,int qid,int depth) {struct nvme_queue *nvmeq = &dev->queues[qid];@@ -1595,8 +1594,7 @@ static int nvme_pci_configure_admin_queue(struct nvme_dev *dev)if (result <0 )return result; - result =nvme_alloc_queue (dev,0 ,NVME_AQ_DEPTH , -dev_to_node (dev->dev)); + result =nvme_alloc_queue (dev,0 ,NVME_AQ_DEPTH );if (result)return result;
drivers/nvme/host/pci.c static int nvme_create_io_queues(struct nvme_dev *dev) · line 1633
Quoted 10 lines
drivers/nvme/host/pci.c +1
-3@@ -1629,9 +1627,7 @@ static int nvme_create_io_queues(struct nvme_dev *dev)int ret =0 ;for (i = dev->ctrl.queue_count; i <= dev->max_qid; i++) { -/* vector == qid - 1, match nvme_create_queue */ -if (nvme_alloc_queue (dev, i, dev->q_depth, -pci_irq_get_node (to_pci_dev (dev->dev), i -1 ))) { +if (nvme_alloc_queue (dev, i, dev->q_depth)) { ret = -ENOMEM ;break ; }
nvme_create_io_queues() is called after pci_alloc_irq_vectors() returns, and the above pci_irq_get_node() should return the correct node info, right?
Thanks,
Ming
Re: [PATCH 2/3] nvme-pci: Remove unused queue parameter
Quoted 4 lines · Ming Lei
Quoted 3 lines · Keith Busch
drivers/nvme/host/pci.c +0
-0@@ -1629,9 +1627,7 @@ static int nvme_create_io_queues(struct nvme_dev *dev)int ret =0 ;
Quoted 12 lines
drivers/nvme/host/pci.c static int nvme_create_io_queues(struct nvme_dev *dev) · line 1633
Quoted 8 lines
drivers/nvme/host/pci.c +1
-3for (i = dev->ctrl.queue_count; i <= dev->max_qid; i++) { -/* vector == qid - 1, match nvme_create_queue */ -if (nvme_alloc_queue (dev, i, dev->q_depth, -pci_irq_get_node (to_pci_dev (dev->dev), i -1 ))) { +if (nvme_alloc_queue (dev, i, dev->q_depth)) { ret = -ENOMEM ;break ; }
nvme_create_io_queues()is called afterpci_alloc_irq_vectors()returns, and the abovepci_irq_get_node()should return the correct node info, right?
Right, the return is correct. It's just not being used anymore.
Re: [PATCH 2/3] nvme-pci: Remove unused queue parameter
In reply to Keith BuschLooks good,
PATCH 3/3 nvme-pci: Separate IO and admin queue IRQ vectors
In reply to Keith BuschFrom: Jianchao Wang <[email protected]>
The admin and first IO queues shared the first irq vector, which has an affinity mask including cpu0. If a system allows cpu0 to be offlined, the admin queue may not be usable if no other CPUs in the affinity mask are online. This is a problem since unlike IO queues, there is only one admin queue that always needs to be usable.
To fix, this patch allocates one pre_vector for the admin queue that is assigned all CPUs, so will always be accessible. The IO queues are assigned the remaining managed vectors.
In case a controller has only one interrupt vector available, the admin and IO queues will share the pre_vector with all CPUs assigned.
--- drivers/nvme/host/pci.c | 27 +++++++++++++++++++++------ 1 file changed, 21 insertions(+), 6 deletions(-)
drivers/nvme/host/pci.c +21 -6
@@ -84,6 +84,7 @@ struct nvme_dev {struct dma_pool *prp_small_pool;unsigned online_queues;unsigned max_qid; +unsigned int num_vecs;int q_depth;u32 db_stride;void __iomem *bar;
@@ -139,6 +140,16 @@ static inline struct nvme_dev *to_nvme_dev(struct nvme_ctrl *ctrl) return container_of(ctrl, struct nvme_dev, ctrl); } +static inline unsigned int nvme_ioq_vector (struct nvme_dev *dev, +unsigned int qid) +{ +/* +* A queue's vector matches the queue identifier unless the controller +* has only one vector available. +*/ +return (dev->num_vecs ==1 ) ?0 : qid; +} +/* * An NVM Express queue. Each device has at least two (one for admin * commands and one for I/O commands).
@@ -414,7 +425,8 @@ 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,to_pci_dev (dev->dev), + dev->num_vecs >1 ); }/**
@@ -1455,7 +1467,7 @@ static int nvme_create_queue(struct nvme_queue *nvmeq, int qid) nvmeq->sq_cmds_io = dev->cmb + offset; } - nvmeq->cq_vector = qid -1 ; + nvmeq->cq_vector =nvme_ioq_vector (dev, qid); result =adapter_alloc_cq (dev, qid, nvmeq);if (result <0 ) goto release_vector;
@@ -1908,6 +1920,8 @@ static int nvme_setup_io_queues(struct nvme_dev *dev)struct pci_dev *pdev =to_pci_dev (dev->dev);int result, nr_io_queues;unsigned long size; +struct irq_affinity affd = {.pre_vectors =1 }; +int ret; nr_io_queues =num_present_cpus (); result =nvme_set_queue_count (&dev->ctrl, &nr_io_queues);
@@ -1944,11 +1958,12 @@ static int nvme_setup_io_queues(struct nvme_dev *dev)* setting up the full range we need. */ pci_free_irq_vectors (pdev); - nr_io_queues =pci_alloc_irq_vectors (pdev,1 , nr_io_queues, -PCI_IRQ_ALL_TYPES |PCI_IRQ_AFFINITY ); -if (nr_io_queues <=0 ) + ret =pci_alloc_irq_vectors_affinity (pdev,1 , (nr_io_queues +1 ), +PCI_IRQ_ALL_TYPES |PCI_IRQ_AFFINITY , &affd); +if (ret <=0 )return -EIO ; - dev->max_qid = nr_io_queues; + dev->num_vecs = ret; + dev->max_qid =max (ret -1 ,1 );/* * Should investigate if there's a performance win from allocating
Signature · 1 line
2.14.3
Christoph Hellwig
maintainerRe: [PATCH 3/3] nvme-pci: Separate IO and admin queue IRQ vectors
drivers/nvme/host/pci.c static inline struct nvme_dev *to_nvme_dev(struct nvme_ctrl *ctrl) · line 144
Quoted 2 lines
drivers/nvme/host/pci.c +2
-0+static inline unsigned int nvme_ioq_vector (struct nvme_dev *dev, +unsigned int qid)
No need for the inline here I think.
drivers/nvme/host/pci.c static inline struct nvme_dev *to_nvme_dev(struct nvme_ctrl *ctrl) · line 150
Quoted 6 lines
drivers/nvme/host/pci.c +6
-0+{ +/* +* A queue's vector matches the queue identifier unless the controller +* has only one vector available. +*/ +return (dev->num_vecs ==1 ) ?0 : qid;
and no need for the braces here.
drivers/nvme/host/pci.c static int nvme_setup_io_queues(struct nvme_dev *dev) · line 1923
Quoted 1 line
drivers/nvme/host/pci.c +1
-0+struct irq_affinity affd = {.pre_vectors =1 };
struct irq_affinity affd = {
.pre_vectors = 1
};to make it a little more readable.
Hi Keith
Thanks for your time and patch for this.
Quoted 30 lines · Keith Busch
The PCI interrupt vectors intended to be associated with a queue may not start at 0. This patch adds an offset parameter so blk-mq may find the intended affinity mask. The default value is 0 so existing drivers that don't care about this parameter don't need to change.
Signed-off-by Keith Busch <[email protected]>--- block/blk-mq-pci.c | 12 ++++++++++-- include/linux/blk-mq-pci.h | 2 ++ 2 files changed, 12 insertions(+), 2 deletions(-)block/blk-mq-pci.c +3
-1@@ -21,6 +21,7 @@* blk_mq_pci_map_queues - provide a default queue mapping for PCI device * @set: tagset to provide the mapping for * @pdev: PCI device associated with @set. +* @offset: PCI irq starting vector offset * * This function assumes the PCI device @pdev has at least as many available * interrupt vectors as @set has queues. It will then query the vector @@ -28,13 +29,14 @@* 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_tag_set *set,struct pci_dev *pdev, +int offset)
block/blk-mq-pci.c line 42
Quoted 10 lines
block/blk-mq-pci.c +1
-1{const struct cpumask *mask;unsigned int queue, cpu;for (queue =0 ; queue < set->nr_hw_queues; queue++) { - mask =pci_irq_get_affinity (pdev, queue); + mask =pci_irq_get_affinity (pdev, queue + offset);if (!mask) goto fallback;
Maybe we could provide a callback parameter for __blk_mq_pci_map_queues which give the mapping from hctx queue num to device-relative interrupt vector index.
Thanks
Jianchao
Quoted 2 lines · jianchao.wang
Maybe we could provide a callback parameter for
__blk_mq_pci_map_queueswhich give the mapping from hctx queue num to device-relative interrupt vector index.
If a driver's mapping is so complicated as to require a special per-hctx callback, it'd be just as easy to implement the mapping in that driver's blk_mq_ops' 'map_queues' directly.
Quoted 67 lines · Keith Busch
The PCI interrupt vectors intended to be associated with a queue may not start at 0. This patch adds an offset parameter so blk-mq may find the intended affinity mask. The default value is 0 so existing drivers that don't care about this parameter don't need to change.
Signed-off-by Keith Busch <[email protected]>--- block/blk-mq-pci.c | 12 ++++++++++-- include/linux/blk-mq-pci.h | 2 ++ 2 files changed, 12 insertions(+), 2 deletions(-)block/blk-mq-pci.c +10
-2@@ -21,6 +21,7 @@* blk_mq_pci_map_queues - provide a default queue mapping for PCI device * @set: tagset to provide the mapping for * @pdev: PCI device associated with @set. +* @offset: PCI irq starting vector offset * * This function assumes the PCI device @pdev has at least as many available * interrupt vectors as @set has queues. It will then query the vector @@ -28,13 +29,14 @@* 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_tag_set *set,struct pci_dev *pdev, +int offset) {const struct cpumask *mask;unsigned int queue, cpu;for (queue =0 ; queue < set->nr_hw_queues; queue++) { - mask =pci_irq_get_affinity (pdev, queue); + mask =pci_irq_get_affinity (pdev, queue + offset);if (!mask) goto fallback;@@ -50,4 +52,10 @@ int blk_mq_pci_map_queues(struct blk_mq_tag_set *set, struct pci_dev *pdev) set->mq_map[cpu] =0 ;return 0 ; } +EXPORT_SYMBOL_GPL (__blk_mq_pci_map_queues); + +int blk_mq_pci_map_queues (struct blk_mq_tag_set *set,struct pci_dev *pdev) +{ +return __blk_mq_pci_map_queues (set, pdev,0 ); +}EXPORT_SYMBOL_GPL (blk_mq_pci_map_queues);include/linux/blk-mq-pci.h +2
-0@@ -5,6 +5,8 @@struct blk_mq_tag_set ;struct pci_dev ; +int __blk_mq_pci_map_queues (struct blk_mq_tag_set *set,struct pci_dev *pdev, +int offset);int blk_mq_pci_map_queues (struct blk_mq_tag_set *set,struct pci_dev *pdev);#endif /* _LINUX_BLK_MQ_PCI_H */ Signature · 1 line
2.14.3
Given no many callers of blk_mq_pci_map_queues(), I suggest to add the parameter of 'offset' to this API directly, then people may keep the '.pre_vectors' stuff in mind, and avoid to misuse it.
Thanks,
Ming
Quoted 4 lines · Ming Lei
Given no many callers of
blk_mq_pci_map_queues(), I suggest to add the parameter of 'offset' to this API directly, then people may keep the '.pre_vectors' stuff in mind, and avoid to misuse it.
Yeah, I think I have to agree. I was trying really hard to not touch other drivers, but the concept doesn't seem odd enough to justify hiding it behind a default parameter. I'll send v2 tomorrow if there's no other feedback.
Christoph Hellwig
maintainerQuoted 24 lines · Keith Busch
The PCI interrupt vectors intended to be associated with a queue may not start at 0. This patch adds an offset parameter so blk-mq may find the intended affinity mask. The default value is 0 so existing drivers that don't care about this parameter don't need to change.
Signed-off-by Keith Busch <[email protected]>--- block/blk-mq-pci.c | 12 ++++++++++-- include/linux/blk-mq-pci.h | 2 ++ 2 files changed, 12 insertions(+), 2 deletions(-)block/blk-mq-pci.c +1
-0@@ -21,6 +21,7 @@* blk_mq_pci_map_queues - provide a default queue mapping for PCI device * @set: tagset to provide the mapping for * @pdev: PCI device associated with @set. +* @offset: PCI irq starting vector offset * * This function assumes the PCI device @pdev has at least as many available * interrupt vectors as @set has queues. It will then query the vector
block/blk-mq-pci.c line 33
Quoted 6 lines
block/blk-mq-pci.c +2
-1@@ -28,13 +29,14 @@* 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_tag_set *set,struct pci_dev *pdev, +int offset)
Can you just change the blk_mq_pci_map_queues prototype instead?