linux-kernel.vger.kernel.org archive mirror
 help / color / mirror / Atom feed
* [PATCH V4 0/4] genirq/affinity: add .calc_sets for improving IRQ allocation & spread
@ 2019-02-14 12:23 Ming Lei
  2019-02-14 12:23 ` [PATCH V4 1/4] genirq/affinity: store interrupt sets size in 'struct irq_affinity' Ming Lei
                   ` (3 more replies)
  0 siblings, 4 replies; 10+ messages in thread
From: Ming Lei @ 2019-02-14 12:23 UTC (permalink / raw)
  To: Christoph Hellwig, Bjorn Helgaas, Thomas Gleixner
  Cc: Jens Axboe, linux-block, Sagi Grimberg, linux-nvme, linux-kernel,
	linux-pci, Keith Busch, Ming Lei

Hi,

Currently pre-caculated set vectors are provided by driver for
allocating & spread vectors. This way only works when drivers passes
same 'max_vecs' and 'min_vecs' to pci_alloc_irq_vectors_affinity(),
also requires driver to retry the allocating & spread.

As Bjorn and Keith mentioned, the current usage & interface for irq sets
is a bit awkward because the retrying should have been avoided by providing
one resonable 'min_vecs'. However, if 'min_vecs' isn't same with
'max_vecs', number of the allocated vectors is unknown before calling
pci_alloc_irq_vectors_affinity(), then each set's vectors can't be
pre-caculated.

Add a new callback of .calc_sets into 'struct irq_affinity' so that
driver can caculate set vectors after IRQ vector is allocated and
before spread IRQ vectors.

V4:
	- re-writen changelogs by Thomas Gleixner
	- merge patch of removing 'const' into the patch
	for adding new callback
 
V3:
	- don't mark 'affd' as const in related functions
	- add sanity check on affd->nr_sets
	- remove the local variable of 'nr_sets' in irq_create_affinity_masks
	- set .nr_sets as 2 in nvme
	- update comment in msi.c

V2:
	- add .calc_sets instead of .setup_affinity() which is easy to
	be abused by drivers

Ming Lei (4):
  genirq/affinity: store interrupt sets size in 'struct irq_affinity'
  genirq/affinity: add new callback for caculating interrupt sets size
  nvme-pci: Simplify interrupt allocation
  PCI: Document .calc_sets as required in case of multiple interrupt
    sets

 drivers/nvme/host/pci.c   | 63 ++++++++++++-----------------------------------
 drivers/pci/msi.c         | 32 +++++++++++++-----------
 include/linux/interrupt.h | 13 +++++++---
 include/linux/pci.h       |  4 +--
 kernel/irq/affinity.c     | 26 ++++++++++++-------
 5 files changed, 62 insertions(+), 76 deletions(-)

-- 
2.9.5


^ permalink raw reply	[flat|nested] 10+ messages in thread

* [PATCH V4 1/4] genirq/affinity: store interrupt sets size in 'struct irq_affinity'
  2019-02-14 12:23 [PATCH V4 0/4] genirq/affinity: add .calc_sets for improving IRQ allocation & spread Ming Lei
@ 2019-02-14 12:23 ` Ming Lei
  2019-02-14 14:04   ` Thomas Gleixner
  2019-02-14 18:38   ` Thomas Gleixner
  2019-02-14 12:23 ` [PATCH V4 2/4] genirq/affinity: add new callback for caculating interrupt sets size Ming Lei
                   ` (2 subsequent siblings)
  3 siblings, 2 replies; 10+ messages in thread
From: Ming Lei @ 2019-02-14 12:23 UTC (permalink / raw)
  To: Christoph Hellwig, Bjorn Helgaas, Thomas Gleixner
  Cc: Jens Axboe, linux-block, Sagi Grimberg, linux-nvme, linux-kernel,
	linux-pci, Keith Busch, Ming Lei

The interrupt affinity spreading mechanism supports to spread out
affinities for one or more interrupt sets. A interrupt set contains one
or more interrupts. Each set is mapped to a specific functionality of a
device, e.g. general I/O queues and read I/O queus of multiqueue block
devices.

The number of interrupts per set is defined by the driver. It depends on
the total number of available interrupts for the device, which is
determined by the PCI capabilites and the availability of underlying CPU
resources, and the number of queues which the device provides and the
driver wants to instantiate.

The driver passes initial configuration for the interrupt allocation via
a pointer to struct affinity_desc.

Right now the allocation mechanism is complex as it requires to have a
loop in the driver to determine the maximum number of interrupts which
are provided by the PCI capabilities and the underlying CPU resources.
This loop would have to be replicated in every driver which wants to
utilize this mechanism. That's unwanted code duplication and error
prone.

In order to move this into generic facilities it is required to have a
mechanism, which allows the recalculation of the interrupt sets and
their size, in the core code. As the core code does not have any
knowledge about the underlying device, a driver specific callback will
be added to struct affinity_desc, which will be invoked by the core
code. The callback will get the number of available interupts as an
argument, so the driver can calculate the corresponding number and size
of interrupt sets.

To support this, two modifications for the handling of struct
affinity_desc are required:

1) The (optional) interrupt sets size information is contained in a
   separate array of integers and struct affinity_desc contains a
   pointer to it.

   This is cumbersome and as the maximum number of interrupt sets is
   small, there is no reason to have separate storage. Moving the size
   array into struct affinity_desc avoids indirections makes the code
   simpler.

2) At the moment the struct affinity_desc pointer which is handed in from
   the driver and passed through to several core functions is marked
   'const'.

With the upcoming callback to recalculate the number and size of
interrupt sets, it's necessary to remove the 'const' qualifier. Otherwise
the callback would not be able to update the data.

This patch does the 1st thing and stores interrupt sets size in
'struct irq_affinity'.

Reviewed-by: Jens Axboe <axboe@kernel.dk>
Signed-off-by: Ming Lei <ming.lei@redhat.com>
---
 drivers/nvme/host/pci.c   |  5 ++---
 include/linux/interrupt.h |  6 ++++--
 kernel/irq/affinity.c     | 15 ++++++++++++---
 3 files changed, 18 insertions(+), 8 deletions(-)

diff --git a/drivers/nvme/host/pci.c b/drivers/nvme/host/pci.c
index 022ea1ee63f8..193d94caf457 100644
--- a/drivers/nvme/host/pci.c
+++ b/drivers/nvme/host/pci.c
@@ -2081,12 +2081,11 @@ static void nvme_calc_io_queues(struct nvme_dev *dev, unsigned int irq_queues)
 static int nvme_setup_irqs(struct nvme_dev *dev, unsigned int nr_io_queues)
 {
 	struct pci_dev *pdev = to_pci_dev(dev->dev);
-	int irq_sets[2];
 	struct irq_affinity affd = {
 		.pre_vectors = 1,
-		.nr_sets = ARRAY_SIZE(irq_sets),
-		.sets = irq_sets,
+		.nr_sets = 2,
 	};
+	int *irq_sets = affd.set_size;
 	int result = 0;
 	unsigned int irq_queues, this_p_queues;
 
diff --git a/include/linux/interrupt.h b/include/linux/interrupt.h
index 7c9434652f36..d9dd5bd61e36 100644
--- a/include/linux/interrupt.h
+++ b/include/linux/interrupt.h
@@ -259,6 +259,8 @@ struct irq_affinity_notify {
 	void (*release)(struct kref *ref);
 };
 
+#define	IRQ_AFFINITY_MAX_SETS  4
+
 /**
  * struct irq_affinity - Description for automatic irq affinity assignements
  * @pre_vectors:	Don't apply affinity to @pre_vectors at beginning of
@@ -266,13 +268,13 @@ struct irq_affinity_notify {
  * @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
+ * @set_size:		Number of affinitized sets
  */
 struct irq_affinity {
 	int	pre_vectors;
 	int	post_vectors;
 	int	nr_sets;
-	int	*sets;
+	int	set_size[IRQ_AFFINITY_MAX_SETS];
 };
 
 /**
diff --git a/kernel/irq/affinity.c b/kernel/irq/affinity.c
index 118b66d64a53..a5e3e5fb3b92 100644
--- a/kernel/irq/affinity.c
+++ b/kernel/irq/affinity.c
@@ -245,6 +245,7 @@ irq_create_affinity_masks(int nvecs, const struct irq_affinity *affd)
 	int curvec, usedvecs;
 	struct irq_affinity_desc *masks = NULL;
 	int i, nr_sets;
+	int set_size[IRQ_AFFINITY_MAX_SETS];
 
 	/*
 	 * If there aren't any vectors left after applying the pre/post
@@ -253,6 +254,9 @@ irq_create_affinity_masks(int nvecs, const struct irq_affinity *affd)
 	if (nvecs == affd->pre_vectors + affd->post_vectors)
 		return NULL;
 
+	if (WARN_ON_ONCE(affd->nr_sets > IRQ_AFFINITY_MAX_SETS))
+		return NULL;
+
 	masks = kcalloc(nvecs, sizeof(*masks), GFP_KERNEL);
 	if (!masks)
 		return NULL;
@@ -265,11 +269,16 @@ irq_create_affinity_masks(int nvecs, const struct irq_affinity *affd)
 	 * have multiple sets, build each sets affinity mask separately.
 	 */
 	nr_sets = affd->nr_sets;
-	if (!nr_sets)
+	if (!nr_sets) {
 		nr_sets = 1;
+		set_size[0] = affvecs;
+	} else {
+		memcpy(set_size, affd->set_size,
+				IRQ_AFFINITY_MAX_SETS * sizeof(int));
+	}
 
 	for (i = 0, usedvecs = 0; i < nr_sets; i++) {
-		int this_vecs = affd->sets ? affd->sets[i] : affvecs;
+		int this_vecs = set_size[i];
 		int ret;
 
 		ret = irq_build_affinity_masks(affd, curvec, this_vecs,
@@ -316,7 +325,7 @@ int irq_calc_affinity_vectors(int minvec, int maxvec, const struct irq_affinity
 		int i;
 
 		for (i = 0, set_vecs = 0;  i < affd->nr_sets; i++)
-			set_vecs += affd->sets[i];
+			set_vecs += affd->set_size[i];
 	} else {
 		get_online_cpus();
 		set_vecs = cpumask_weight(cpu_possible_mask);
-- 
2.9.5


^ permalink raw reply related	[flat|nested] 10+ messages in thread

* [PATCH V4 2/4] genirq/affinity: add new callback for caculating interrupt sets size
  2019-02-14 12:23 [PATCH V4 0/4] genirq/affinity: add .calc_sets for improving IRQ allocation & spread Ming Lei
  2019-02-14 12:23 ` [PATCH V4 1/4] genirq/affinity: store interrupt sets size in 'struct irq_affinity' Ming Lei
@ 2019-02-14 12:23 ` Ming Lei
  2019-02-14 14:50   ` Thomas Gleixner
  2019-02-14 20:14   ` Bjorn Helgaas
  2019-02-14 12:23 ` [PATCH V4 3/4] nvme-pci: Simplify interrupt allocation Ming Lei
  2019-02-14 12:23 ` [PATCH V4 4/4] PCI: Document .calc_sets as required in case of multiple interrupt sets Ming Lei
  3 siblings, 2 replies; 10+ messages in thread
From: Ming Lei @ 2019-02-14 12:23 UTC (permalink / raw)
  To: Christoph Hellwig, Bjorn Helgaas, Thomas Gleixner
  Cc: Jens Axboe, linux-block, Sagi Grimberg, linux-nvme, linux-kernel,
	linux-pci, Keith Busch, Ming Lei

The interrupt affinity spreading mechanism supports to spread out
affinities for one or more interrupt sets. A interrupt set contains one
or more interrupts. Each set is mapped to a specific functionality of a
device, e.g. general I/O queues and read I/O queus of multiqueue block
devices.

The number of interrupts per set is defined by the driver. It depends on
the total number of available interrupts for the device, which is
determined by the PCI capabilites and the availability of underlying CPU
resources, and the number of queues which the device provides and the
driver wants to instantiate.

The driver passes initial configuration for the interrupt allocation via
a pointer to struct affinity_desc.

Right now the allocation mechanism is complex as it requires to have a
loop in the driver to determine the maximum number of interrupts which
are provided by the PCI capabilities and the underlying CPU resources.
This loop would have to be replicated in every driver which wants to
utilize this mechanism. That's unwanted code duplication and error
prone.

In order to move this into generic facilities it is required to have a
mechanism, which allows the recalculation of the interrupt sets and
their size, in the core code. As the core code does not have any
knowledge about the underlying device, a driver specific callback will
be added to struct affinity_desc, which will be invoked by the core
code. The callback will get the number of available interupts as an
argument, so the driver can calculate the corresponding number and size
of interrupt sets.

To support this, two modifications for the handling of struct
affinity_desc are required:

1) The (optional) interrupt sets size information is contained in a
   separate array of integers and struct affinity_desc contains a
   pointer to it.

   This is cumbersome and as the maximum number of interrupt sets is
   small, there is no reason to have separate storage. Moving the size
   array into struct affinity_desc avoids indirections makes the code
   simpler.

2) At the moment the struct affinity_desc pointer which is handed in from
   the driver and passed through to several core functions is marked
   'const'.

This patch adds callback to recalculate the number and size of interrupt sets,
also removes the 'const' qualifier for 'affd'.

Reviewed-by: Jens Axboe <axboe@kernel.dk>
Signed-off-by: Ming Lei <ming.lei@redhat.com>
---
 drivers/pci/msi.c         | 18 +++++++++---------
 include/linux/interrupt.h |  6 +++++-
 include/linux/pci.h       |  4 ++--
 kernel/irq/affinity.c     | 25 ++++++++++++-------------
 4 files changed, 28 insertions(+), 25 deletions(-)

diff --git a/drivers/pci/msi.c b/drivers/pci/msi.c
index 4c0b47867258..96978459e2a0 100644
--- a/drivers/pci/msi.c
+++ b/drivers/pci/msi.c
@@ -532,7 +532,7 @@ static int populate_msi_sysfs(struct pci_dev *pdev)
 }
 
 static struct msi_desc *
-msi_setup_entry(struct pci_dev *dev, int nvec, const struct irq_affinity *affd)
+msi_setup_entry(struct pci_dev *dev, int nvec, struct irq_affinity *affd)
 {
 	struct irq_affinity_desc *masks = NULL;
 	struct msi_desc *entry;
@@ -597,7 +597,7 @@ static int msi_verify_entries(struct pci_dev *dev)
  * which could have been allocated.
  */
 static int msi_capability_init(struct pci_dev *dev, int nvec,
-			       const struct irq_affinity *affd)
+			       struct irq_affinity *affd)
 {
 	struct msi_desc *entry;
 	int ret;
@@ -669,7 +669,7 @@ static void __iomem *msix_map_region(struct pci_dev *dev, unsigned nr_entries)
 
 static int msix_setup_entries(struct pci_dev *dev, void __iomem *base,
 			      struct msix_entry *entries, int nvec,
-			      const struct irq_affinity *affd)
+			      struct irq_affinity *affd)
 {
 	struct irq_affinity_desc *curmsk, *masks = NULL;
 	struct msi_desc *entry;
@@ -736,7 +736,7 @@ static void msix_program_entries(struct pci_dev *dev,
  * requested MSI-X entries with allocated irqs or non-zero for otherwise.
  **/
 static int msix_capability_init(struct pci_dev *dev, struct msix_entry *entries,
-				int nvec, const struct irq_affinity *affd)
+				int nvec, struct irq_affinity *affd)
 {
 	int ret;
 	u16 control;
@@ -932,7 +932,7 @@ int pci_msix_vec_count(struct pci_dev *dev)
 EXPORT_SYMBOL(pci_msix_vec_count);
 
 static int __pci_enable_msix(struct pci_dev *dev, struct msix_entry *entries,
-			     int nvec, const struct irq_affinity *affd)
+			     int nvec, struct irq_affinity *affd)
 {
 	int nr_entries;
 	int i, j;
@@ -1018,7 +1018,7 @@ int pci_msi_enabled(void)
 EXPORT_SYMBOL(pci_msi_enabled);
 
 static int __pci_enable_msi_range(struct pci_dev *dev, int minvec, int maxvec,
-				  const struct irq_affinity *affd)
+				  struct irq_affinity *affd)
 {
 	int nvec;
 	int rc;
@@ -1086,7 +1086,7 @@ EXPORT_SYMBOL(pci_enable_msi);
 
 static int __pci_enable_msix_range(struct pci_dev *dev,
 				   struct msix_entry *entries, int minvec,
-				   int maxvec, const struct irq_affinity *affd)
+				   int maxvec, struct irq_affinity *affd)
 {
 	int rc, nvec = maxvec;
 
@@ -1165,9 +1165,9 @@ EXPORT_SYMBOL(pci_enable_msix_range);
  */
 int pci_alloc_irq_vectors_affinity(struct pci_dev *dev, unsigned int min_vecs,
 				   unsigned int max_vecs, unsigned int flags,
-				   const struct irq_affinity *affd)
+				   struct irq_affinity *affd)
 {
-	static const struct irq_affinity msi_default_affd;
+	struct irq_affinity msi_default_affd = {0};
 	int msix_vecs = -ENOSPC;
 	int msi_vecs = -ENOSPC;
 
diff --git a/include/linux/interrupt.h b/include/linux/interrupt.h
index d9dd5bd61e36..acb9c5ad6bc1 100644
--- a/include/linux/interrupt.h
+++ b/include/linux/interrupt.h
@@ -269,12 +269,16 @@ struct irq_affinity_notify {
  *			the MSI(-X) vector space
  * @nr_sets:		Length of passed in *sets array
  * @set_size:		Number of affinitized sets
+ * @calc_sets:		Callback for caculating interrupt sets size
+ * @priv:		Private data of @calc_sets
  */
 struct irq_affinity {
 	int	pre_vectors;
 	int	post_vectors;
 	int	nr_sets;
 	int	set_size[IRQ_AFFINITY_MAX_SETS];
+	void	(*calc_sets)(struct irq_affinity *, int nvecs);
+	void	*priv;
 };
 
 /**
@@ -334,7 +338,7 @@ extern int
 irq_set_affinity_notifier(unsigned int irq, struct irq_affinity_notify *notify);
 
 struct irq_affinity_desc *
-irq_create_affinity_masks(int nvec, const struct irq_affinity *affd);
+irq_create_affinity_masks(int nvec, struct irq_affinity *affd);
 
 int irq_calc_affinity_vectors(int minvec, int maxvec, const struct irq_affinity *affd);
 
diff --git a/include/linux/pci.h b/include/linux/pci.h
index 40b327b814aa..4eca42cf611b 100644
--- a/include/linux/pci.h
+++ b/include/linux/pci.h
@@ -1396,7 +1396,7 @@ static inline int pci_enable_msix_exact(struct pci_dev *dev,
 }
 int pci_alloc_irq_vectors_affinity(struct pci_dev *dev, unsigned int min_vecs,
 				   unsigned int max_vecs, unsigned int flags,
-				   const struct irq_affinity *affd);
+				   struct irq_affinity *affd);
 
 void pci_free_irq_vectors(struct pci_dev *dev);
 int pci_irq_vector(struct pci_dev *dev, unsigned int nr);
@@ -1422,7 +1422,7 @@ static inline int pci_enable_msix_exact(struct pci_dev *dev,
 static inline int
 pci_alloc_irq_vectors_affinity(struct pci_dev *dev, unsigned int min_vecs,
 			       unsigned int max_vecs, unsigned int flags,
-			       const struct irq_affinity *aff_desc)
+			       struct irq_affinity *aff_desc)
 {
 	if ((flags & PCI_IRQ_LEGACY) && min_vecs == 1 && dev->irq)
 		return 1;
diff --git a/kernel/irq/affinity.c b/kernel/irq/affinity.c
index a5e3e5fb3b92..3b1451a895b2 100644
--- a/kernel/irq/affinity.c
+++ b/kernel/irq/affinity.c
@@ -239,13 +239,12 @@ static int irq_build_affinity_masks(const struct irq_affinity *affd,
  * Returns the irq_affinity_desc pointer or NULL if allocation failed.
  */
 struct irq_affinity_desc *
-irq_create_affinity_masks(int nvecs, const struct irq_affinity *affd)
+irq_create_affinity_masks(int nvecs, struct irq_affinity *affd)
 {
 	int affvecs = nvecs - affd->pre_vectors - affd->post_vectors;
 	int curvec, usedvecs;
 	struct irq_affinity_desc *masks = NULL;
-	int i, nr_sets;
-	int set_size[IRQ_AFFINITY_MAX_SETS];
+	int i;
 
 	/*
 	 * If there aren't any vectors left after applying the pre/post
@@ -268,17 +267,15 @@ irq_create_affinity_masks(int nvecs, const struct irq_affinity *affd)
 	 * 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;
-		set_size[0] = affvecs;
-	} else {
-		memcpy(set_size, affd->set_size,
-				IRQ_AFFINITY_MAX_SETS * sizeof(int));
+	if (affd->calc_sets) {
+		affd->calc_sets(affd, nvecs);
+	} else if (!affd->nr_sets) {
+		affd->nr_sets = 1;
+		affd->set_size[0] = affvecs;
 	}
 
-	for (i = 0, usedvecs = 0; i < nr_sets; i++) {
-		int this_vecs = set_size[i];
+	for (i = 0, usedvecs = 0; i < affd->nr_sets; i++) {
+		int this_vecs = affd->set_size[i];
 		int ret;
 
 		ret = irq_build_affinity_masks(affd, curvec, this_vecs,
@@ -321,7 +318,9 @@ int irq_calc_affinity_vectors(int minvec, int maxvec, const struct irq_affinity
 	if (resv > minvec)
 		return 0;
 
-	if (affd->nr_sets) {
+	if (affd->calc_sets) {
+		set_vecs = vecs;
+	} else if (affd->nr_sets) {
 		int i;
 
 		for (i = 0, set_vecs = 0;  i < affd->nr_sets; i++)
-- 
2.9.5


^ permalink raw reply related	[flat|nested] 10+ messages in thread

* [PATCH V4 3/4] nvme-pci: Simplify interrupt allocation
  2019-02-14 12:23 [PATCH V4 0/4] genirq/affinity: add .calc_sets for improving IRQ allocation & spread Ming Lei
  2019-02-14 12:23 ` [PATCH V4 1/4] genirq/affinity: store interrupt sets size in 'struct irq_affinity' Ming Lei
  2019-02-14 12:23 ` [PATCH V4 2/4] genirq/affinity: add new callback for caculating interrupt sets size Ming Lei
@ 2019-02-14 12:23 ` Ming Lei
  2019-02-14 12:59   ` Thomas Gleixner
  2019-02-14 12:23 ` [PATCH V4 4/4] PCI: Document .calc_sets as required in case of multiple interrupt sets Ming Lei
  3 siblings, 1 reply; 10+ messages in thread
From: Ming Lei @ 2019-02-14 12:23 UTC (permalink / raw)
  To: Christoph Hellwig, Bjorn Helgaas, Thomas Gleixner
  Cc: Jens Axboe, linux-block, Sagi Grimberg, linux-nvme, linux-kernel,
	linux-pci, Keith Busch, Ming Lei

The NVME PCI driver contains a tedious mechanism for interrupt
allocation, which is necessary to adjust the number and size of interrupt
sets to the maximum available number of interrupts which depends on the
underlying PCI capabilities and the available CPU resources.

It works around the former short comings of the PCI and core interrupt
allocation mechanims in combination with interrupt sets.

The PCI interrupt allocation function allows to provide a maximum and a
minimum number of interrupts to be allocated and tries to allocate as
many as possible. This worked without driver interaction as long as there
was only a single set of interrupts to handle.

With the addition of support for multiple interrupt sets in the generic
affinity spreading logic, which is invoked from the PCI interrupt
allocation, the adaptive loop in the PCI interrupt allocation did not
work for multiple interrupt sets. The reason is that depending on the
total number of interrupts which the PCI allocation adaptive loop tries
to allocate in each step, the number and the size of the interrupt sets
need to be adapted as well. Due to the way the interrupt sets support was
implemented there was no way for the PCI interrupt allocation code or the
core affinity spreading mechanism to invoke a driver specific function
for adapting the interrupt sets configuration.

As a consequence the driver had to implement another adaptive loop around
the PCI interrupt allocation function and calling that with maximum and
minimum interrupts set to the same value. This ensured that the
allocation either succeeded or immediately failed without any attempt to
adjust the number of interrupts in the PCI code.

The core code now allows drivers to provide a callback to recalculate the
number and the size of interrupt sets during PCI interrupt allocation,
which in turn allows the PCI interrupt allocation function to be called
in the same way as with a single set of interrupts. The PCI code handles
the adaptive loop and the interrupt affinity spreading mechanism invokes
the driver callback to adapt the interrupt set configuration to the
current loop value. This replaces the adaptive loop in the driver
completely.

Implement the NVME specific callback which adjusts the interrupt sets
configuration and remove the adaptive allocation loop.

Reviewed-by: Jens Axboe <axboe@kernel.dk>
Reviewed-by: Keith Busch <keith.busch@intel.com>
Signed-off-by: Ming Lei <ming.lei@redhat.com>
---
 drivers/nvme/host/pci.c | 62 +++++++++++++------------------------------------
 1 file changed, 16 insertions(+), 46 deletions(-)

diff --git a/drivers/nvme/host/pci.c b/drivers/nvme/host/pci.c
index 193d94caf457..02ae653bf2c3 100644
--- a/drivers/nvme/host/pci.c
+++ b/drivers/nvme/host/pci.c
@@ -2078,14 +2078,25 @@ static void nvme_calc_io_queues(struct nvme_dev *dev, unsigned int irq_queues)
 	}
 }
 
+static void nvme_calc_irq_sets(struct irq_affinity *affd, int nvecs)
+{
+	struct nvme_dev *dev = affd->priv;
+
+	nvme_calc_io_queues(dev, nvecs);
+
+	affd->set_size[HCTX_TYPE_DEFAULT] = dev->io_queues[HCTX_TYPE_DEFAULT];
+	affd->set_size[HCTX_TYPE_READ] = dev->io_queues[HCTX_TYPE_READ];
+	affd->nr_sets = 2;
+}
+
 static int nvme_setup_irqs(struct nvme_dev *dev, unsigned int nr_io_queues)
 {
 	struct pci_dev *pdev = to_pci_dev(dev->dev);
 	struct irq_affinity affd = {
 		.pre_vectors = 1,
-		.nr_sets = 2,
+		.calc_sets = nvme_calc_irq_sets,
+		.priv = dev,
 	};
-	int *irq_sets = affd.set_size;
 	int result = 0;
 	unsigned int irq_queues, this_p_queues;
 
@@ -2102,50 +2113,8 @@ static int nvme_setup_irqs(struct nvme_dev *dev, unsigned int nr_io_queues)
 	}
 	dev->io_queues[HCTX_TYPE_POLL] = this_p_queues;
 
-	/*
-	 * 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, irq_queues);
-		irq_sets[0] = dev->io_queues[HCTX_TYPE_DEFAULT];
-		irq_sets[1] = dev->io_queues[HCTX_TYPE_READ];
-		if (!irq_sets[1])
-			affd.nr_sets = 1;
-
-		/*
-		 * If we got a failure and we're down to asking for just
-		 * 1 + 1 queues, just ask for a single vector. We'll share
-		 * that between the single IO queue and the admin queue.
-		 * Otherwise, we assign one independent vector to admin queue.
-		 */
-		if (irq_queues > 1)
-			irq_queues = irq_sets[0] + irq_sets[1] + 1;
-
-		result = pci_alloc_irq_vectors_affinity(pdev, irq_queues,
-				irq_queues,
-				PCI_IRQ_ALL_TYPES | PCI_IRQ_AFFINITY, &affd);
-
-		/*
-		 * Need to reduce our vec counts. If we get ENOSPC, the
-		 * platform should support mulitple vecs, we just need
-		 * to decrease our ask. If we get EINVAL, the platform
-		 * likely does not. Back down to ask for just one vector.
-		 */
-		if (result == -ENOSPC) {
-			irq_queues--;
-			if (!irq_queues)
-				return result;
-			continue;
-		} else if (result == -EINVAL) {
-			irq_queues = 1;
-			continue;
-		} else if (result <= 0)
-			return -EIO;
-		break;
-	} while (1);
-
+	result = pci_alloc_irq_vectors_affinity(pdev, 1, irq_queues,
+			PCI_IRQ_ALL_TYPES | PCI_IRQ_AFFINITY, &affd);
 	return result;
 }
 
@@ -3021,6 +2990,7 @@ static struct pci_driver nvme_driver = {
 
 static int __init nvme_init(void)
 {
+	BUILD_BUG_ON(IRQ_AFFINITY_MAX_SETS < 2);
 	return pci_register_driver(&nvme_driver);
 }
 
-- 
2.9.5


^ permalink raw reply related	[flat|nested] 10+ messages in thread

* [PATCH V4 4/4] PCI: Document .calc_sets as required in case of multiple interrupt sets
  2019-02-14 12:23 [PATCH V4 0/4] genirq/affinity: add .calc_sets for improving IRQ allocation & spread Ming Lei
                   ` (2 preceding siblings ...)
  2019-02-14 12:23 ` [PATCH V4 3/4] nvme-pci: Simplify interrupt allocation Ming Lei
@ 2019-02-14 12:23 ` Ming Lei
  3 siblings, 0 replies; 10+ messages in thread
From: Ming Lei @ 2019-02-14 12:23 UTC (permalink / raw)
  To: Christoph Hellwig, Bjorn Helgaas, Thomas Gleixner
  Cc: Jens Axboe, linux-block, Sagi Grimberg, linux-nvme, linux-kernel,
	linux-pci, Keith Busch, Ming Lei

Now NVMe has implemented the .calc_sets callback for calculating size of
interrupt set.

For other cases of multiple IRQ sets, pre-calculating each set's size
before allocating IRQ vectors can't work too because the available
interrupt number for this device is unknown at that time.

So document .calc_sets as required explicitly for multiple interrupt sets.

Reviewed-by: Jens Axboe <axboe@kernel.dk>
Acked-by: Bjorn Helgaas <bhelgaas@google.com>
Signed-off-by: Ming Lei <ming.lei@redhat.com>
---
 drivers/pci/msi.c         | 14 ++++++++------
 include/linux/interrupt.h |  3 ++-
 2 files changed, 10 insertions(+), 7 deletions(-)

diff --git a/drivers/pci/msi.c b/drivers/pci/msi.c
index 96978459e2a0..64383ab5f53f 100644
--- a/drivers/pci/msi.c
+++ b/drivers/pci/msi.c
@@ -1036,10 +1036,11 @@ static int __pci_enable_msi_range(struct pci_dev *dev, int minvec, int maxvec,
 		return -ERANGE;
 
 	/*
-	 * If the caller is passing in sets, we can't support a range of
-	 * vectors. The caller needs to handle that.
+	 * If the caller requests multiple sets of IRQs where each set
+	 * requires different affinity, it must also supply a ->calc_sets()
+	 * callback to compute size of each interrupt set
 	 */
-	if (affd && affd->nr_sets && minvec != maxvec)
+	if (affd && affd->nr_sets > 1 && !affd->calc_sets)
 		return -EINVAL;
 
 	if (WARN_ON_ONCE(dev->msi_enabled))
@@ -1094,10 +1095,11 @@ static int __pci_enable_msix_range(struct pci_dev *dev,
 		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 the caller requests multiple sets of IRQs where each set
+	 * requires different affinity, it must also supply a ->calc_sets()
+	 * callback to compute size of each interrupt set
 	 */
-	if (affd && affd->nr_sets && minvec != maxvec)
+	if (affd && affd->nr_sets > 1 && !affd->calc_sets)
 		return -EINVAL;
 
 	if (WARN_ON_ONCE(dev->msix_enabled))
diff --git a/include/linux/interrupt.h b/include/linux/interrupt.h
index acb9c5ad6bc1..0da2a5cea8f3 100644
--- a/include/linux/interrupt.h
+++ b/include/linux/interrupt.h
@@ -269,7 +269,8 @@ struct irq_affinity_notify {
  *			the MSI(-X) vector space
  * @nr_sets:		Length of passed in *sets array
  * @set_size:		Number of affinitized sets
- * @calc_sets:		Callback for caculating interrupt sets size
+ * @calc_sets:		Callback for caculating set size, required for
+ * 			multiple interrupt sets.
  * @priv:		Private data of @calc_sets
  */
 struct irq_affinity {
-- 
2.9.5


^ permalink raw reply related	[flat|nested] 10+ messages in thread

* Re: [PATCH V4 3/4] nvme-pci: Simplify interrupt allocation
  2019-02-14 12:23 ` [PATCH V4 3/4] nvme-pci: Simplify interrupt allocation Ming Lei
@ 2019-02-14 12:59   ` Thomas Gleixner
  0 siblings, 0 replies; 10+ messages in thread
From: Thomas Gleixner @ 2019-02-14 12:59 UTC (permalink / raw)
  To: Ming Lei
  Cc: Christoph Hellwig, Bjorn Helgaas, Jens Axboe, linux-block,
	Sagi Grimberg, linux-nvme, linux-kernel, linux-pci, Keith Busch

On Thu, 14 Feb 2019, Ming Lei wrote:
> +static void nvme_calc_irq_sets(struct irq_affinity *affd, int nvecs)
> +{
> +	struct nvme_dev *dev = affd->priv;
> +
> +	nvme_calc_io_queues(dev, nvecs);
> +
> +	affd->set_size[HCTX_TYPE_DEFAULT] = dev->io_queues[HCTX_TYPE_DEFAULT];
> +	affd->set_size[HCTX_TYPE_READ] = dev->io_queues[HCTX_TYPE_READ];
> +	affd->nr_sets = 2;

That's not really correct.

> -		nvme_calc_io_queues(dev, irq_queues);
> -		irq_sets[0] = dev->io_queues[HCTX_TYPE_DEFAULT];
> -		irq_sets[1] = dev->io_queues[HCTX_TYPE_READ];
> -		if (!irq_sets[1])
> -			affd.nr_sets = 1;

-----------------------^^^^^^^^^^^^^^^^^^  				     

That happens when the number of interrupts becomes too small to have split
queues. I'll fix that up.

Thanks,

	tglx



^ permalink raw reply	[flat|nested] 10+ messages in thread

* Re: [PATCH V4 1/4] genirq/affinity: store interrupt sets size in 'struct irq_affinity'
  2019-02-14 12:23 ` [PATCH V4 1/4] genirq/affinity: store interrupt sets size in 'struct irq_affinity' Ming Lei
@ 2019-02-14 14:04   ` Thomas Gleixner
  2019-02-14 18:38   ` Thomas Gleixner
  1 sibling, 0 replies; 10+ messages in thread
From: Thomas Gleixner @ 2019-02-14 14:04 UTC (permalink / raw)
  To: Ming Lei
  Cc: Christoph Hellwig, Bjorn Helgaas, Jens Axboe, linux-block,
	Sagi Grimberg, linux-nvme, linux-kernel, linux-pci, Keith Busch

On Thu, 14 Feb 2019, Ming Lei wrote:
>  /**
>   * struct irq_affinity - Description for automatic irq affinity assignements
>   * @pre_vectors:	Don't apply affinity to @pre_vectors at beginning of
> @@ -266,13 +268,13 @@ struct irq_affinity_notify {
>   * @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
> + * @set_size:		Number of affinitized sets

Both nr_sets and set_size comments are wrong ...

>  	nr_sets = affd->nr_sets;
> -	if (!nr_sets)
> +	if (!nr_sets) {
>  		nr_sets = 1;
> +		set_size[0] = affvecs;
> +	} else {
> +		memcpy(set_size, affd->set_size,
> +				IRQ_AFFINITY_MAX_SETS * sizeof(int));

Uuurgh. No. This needs to be nr_sets * sizeof(int) otherwise you copy
beyond the size of the source. nr_sets is already verified to be less than
IRQ_AFFINITY_MAX_SETS.

Fixed it up.

Thanks,

	tglx



^ permalink raw reply	[flat|nested] 10+ messages in thread

* Re: [PATCH V4 2/4] genirq/affinity: add new callback for caculating interrupt sets size
  2019-02-14 12:23 ` [PATCH V4 2/4] genirq/affinity: add new callback for caculating interrupt sets size Ming Lei
@ 2019-02-14 14:50   ` Thomas Gleixner
  2019-02-14 20:14   ` Bjorn Helgaas
  1 sibling, 0 replies; 10+ messages in thread
From: Thomas Gleixner @ 2019-02-14 14:50 UTC (permalink / raw)
  To: Ming Lei
  Cc: Christoph Hellwig, Bjorn Helgaas, Jens Axboe, linux-block,
	Sagi Grimberg, linux-nvme, linux-kernel, linux-pci, Keith Busch

On Thu, 14 Feb 2019, Ming Lei wrote:
> +	if (affd->calc_sets) {
> +		affd->calc_sets(affd, nvecs);
> +	} else if (!affd->nr_sets) {
> +		affd->nr_sets = 1;
> +		affd->set_size[0] = affvecs;

Hrmpf. I suggested that to you to get rid of the nr_sets local variable,
but that's actually broken. The reason is that on the first invocation from
the pci code, which is with maxvecs usually, the size is stored and if that
allocation failed, the subsequent invocation with maxvecs - 1 will not
update set_size[0] because affd->nr_sets == 1.

/me scratches head and stares at the code some more...

Thanks,

	tglx



^ permalink raw reply	[flat|nested] 10+ messages in thread

* Re: [PATCH V4 1/4] genirq/affinity: store interrupt sets size in 'struct irq_affinity'
  2019-02-14 12:23 ` [PATCH V4 1/4] genirq/affinity: store interrupt sets size in 'struct irq_affinity' Ming Lei
  2019-02-14 14:04   ` Thomas Gleixner
@ 2019-02-14 18:38   ` Thomas Gleixner
  1 sibling, 0 replies; 10+ messages in thread
From: Thomas Gleixner @ 2019-02-14 18:38 UTC (permalink / raw)
  To: Ming Lei
  Cc: Christoph Hellwig, Bjorn Helgaas, Jens Axboe, linux-block,
	Sagi Grimberg, linux-nvme, linux-kernel, linux-pci, Keith Busch

On Thu, 14 Feb 2019, Ming Lei wrote:
> The driver passes initial configuration for the interrupt allocation via
> a pointer to struct affinity_desc.

Btw, blindly copying a suggestion without proof reading is a bad idea. That
want's to be 'struct irq_affinity' obviously. Tired brain confused them
yesterday night.

Thanks,

	tglx

^ permalink raw reply	[flat|nested] 10+ messages in thread

* Re: [PATCH V4 2/4] genirq/affinity: add new callback for caculating interrupt sets size
  2019-02-14 12:23 ` [PATCH V4 2/4] genirq/affinity: add new callback for caculating interrupt sets size Ming Lei
  2019-02-14 14:50   ` Thomas Gleixner
@ 2019-02-14 20:14   ` Bjorn Helgaas
  1 sibling, 0 replies; 10+ messages in thread
From: Bjorn Helgaas @ 2019-02-14 20:14 UTC (permalink / raw)
  To: Ming Lei
  Cc: Christoph Hellwig, Thomas Gleixner, Jens Axboe, linux-block,
	Sagi Grimberg, linux-nvme, linux-kernel, linux-pci, Keith Busch

On Thu, Feb 14, 2019 at 08:23:45PM +0800, Ming Lei wrote:
> The interrupt affinity spreading mechanism supports to spread out
> affinities for one or more interrupt sets. A interrupt set contains one
> or more interrupts. Each set is mapped to a specific functionality of a
> device, e.g. general I/O queues and read I/O queus of multiqueue block
> devices.
> 
> The number of interrupts per set is defined by the driver. It depends on
> the total number of available interrupts for the device, which is
> determined by the PCI capabilites and the availability of underlying CPU
> resources, and the number of queues which the device provides and the
> driver wants to instantiate.
> 
> The driver passes initial configuration for the interrupt allocation via
> a pointer to struct affinity_desc.
> 
> Right now the allocation mechanism is complex as it requires to have a
> loop in the driver to determine the maximum number of interrupts which
> are provided by the PCI capabilities and the underlying CPU resources.
> This loop would have to be replicated in every driver which wants to
> utilize this mechanism. That's unwanted code duplication and error
> prone.
> 
> In order to move this into generic facilities it is required to have a
> mechanism, which allows the recalculation of the interrupt sets and
> their size, in the core code. As the core code does not have any
> knowledge about the underlying device, a driver specific callback will
> be added to struct affinity_desc, which will be invoked by the core
> code. The callback will get the number of available interupts as an
> argument, so the driver can calculate the corresponding number and size
> of interrupt sets.
> 
> To support this, two modifications for the handling of struct
> affinity_desc are required:
> 
> 1) The (optional) interrupt sets size information is contained in a
>    separate array of integers and struct affinity_desc contains a
>    pointer to it.
> 
>    This is cumbersome and as the maximum number of interrupt sets is
>    small, there is no reason to have separate storage. Moving the size
>    array into struct affinity_desc avoids indirections makes the code
>    simpler.
> 
> 2) At the moment the struct affinity_desc pointer which is handed in from
>    the driver and passed through to several core functions is marked
>    'const'.
> 
> This patch adds callback to recalculate the number and size of interrupt sets,
> also removes the 'const' qualifier for 'affd'.
> 
> Reviewed-by: Jens Axboe <axboe@kernel.dk>
> Signed-off-by: Ming Lei <ming.lei@redhat.com>

I know you have something to work out in the affinity.c part of this, but
I'm fine with the PCI part, so:

Acked-by: Bjorn Helgaas <bhelgaas@google.com>

^ permalink raw reply	[flat|nested] 10+ messages in thread

end of thread, other threads:[~2019-02-14 20:14 UTC | newest]

Thread overview: 10+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2019-02-14 12:23 [PATCH V4 0/4] genirq/affinity: add .calc_sets for improving IRQ allocation & spread Ming Lei
2019-02-14 12:23 ` [PATCH V4 1/4] genirq/affinity: store interrupt sets size in 'struct irq_affinity' Ming Lei
2019-02-14 14:04   ` Thomas Gleixner
2019-02-14 18:38   ` Thomas Gleixner
2019-02-14 12:23 ` [PATCH V4 2/4] genirq/affinity: add new callback for caculating interrupt sets size Ming Lei
2019-02-14 14:50   ` Thomas Gleixner
2019-02-14 20:14   ` Bjorn Helgaas
2019-02-14 12:23 ` [PATCH V4 3/4] nvme-pci: Simplify interrupt allocation Ming Lei
2019-02-14 12:59   ` Thomas Gleixner
2019-02-14 12:23 ` [PATCH V4 4/4] PCI: Document .calc_sets as required in case of multiple interrupt sets Ming Lei

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for NNTP newsgroup(s).