Commit 9cfef55b authored by Ming Lei's avatar Ming Lei Committed by Thomas Gleixner

genirq/affinity: Store interrupt sets size in struct irq_affinity

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

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 irq_affinity
are required:

1) The (optional) interrupt sets size information is contained in a
   separate array of integers and struct irq_affinity 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 and makes the code simpler.

2) At the moment the struct irq_affinity 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.

Implement #1 and store the interrupt sets size in 'struct irq_affinity'.

No functional change.

[ tglx: Fixed the memcpy() size so it won't copy beyond the size of the
  	source. Fixed the kernel doc comments for struct irq_affinity and
  	de-'This patch'-ed the changelog ]
Signed-off-by: default avatarMing Lei <ming.lei@redhat.com>
Signed-off-by: default avatarThomas Gleixner <tglx@linutronix.de>
Acked-by: default avatarMarc Zyngier <marc.zyngier@arm.com>
Cc: Christoph Hellwig <hch@lst.de>
Cc: Bjorn Helgaas <helgaas@kernel.org>
Cc: Jens Axboe <axboe@kernel.dk>
Cc: linux-block@vger.kernel.org
Cc: Sagi Grimberg <sagi@grimberg.me>
Cc: linux-nvme@lists.infradead.org
Cc: linux-pci@vger.kernel.org
Cc: Keith Busch <keith.busch@intel.com>
Cc: Sumit Saxena <sumit.saxena@broadcom.com>
Cc: Kashyap Desai <kashyap.desai@broadcom.com>
Cc: Shivasharan Srikanteshwara <shivasharan.srikanteshwara@broadcom.com>
Link: https://lkml.kernel.org/r/20190216172228.423723127@linutronix.de
parent 0145c30e
...@@ -2081,12 +2081,11 @@ static void nvme_calc_io_queues(struct nvme_dev *dev, unsigned int irq_queues) ...@@ -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) static int nvme_setup_irqs(struct nvme_dev *dev, unsigned int nr_io_queues)
{ {
struct pci_dev *pdev = to_pci_dev(dev->dev); struct pci_dev *pdev = to_pci_dev(dev->dev);
int irq_sets[2];
struct irq_affinity affd = { struct irq_affinity affd = {
.pre_vectors = 1, .pre_vectors = 1,
.nr_sets = ARRAY_SIZE(irq_sets), .nr_sets = 2,
.sets = irq_sets,
}; };
unsigned int *irq_sets = affd.set_size;
int result = 0; int result = 0;
unsigned int irq_queues, this_p_queues; unsigned int irq_queues, this_p_queues;
......
...@@ -241,20 +241,23 @@ struct irq_affinity_notify { ...@@ -241,20 +241,23 @@ struct irq_affinity_notify {
void (*release)(struct kref *ref); void (*release)(struct kref *ref);
}; };
#define IRQ_AFFINITY_MAX_SETS 4
/** /**
* struct irq_affinity - Description for automatic irq affinity assignements * struct irq_affinity - Description for automatic irq affinity assignements
* @pre_vectors: Don't apply affinity to @pre_vectors at beginning of * @pre_vectors: Don't apply affinity to @pre_vectors at beginning of
* the MSI(-X) vector space * the MSI(-X) vector space
* @post_vectors: Don't apply affinity to @post_vectors at end of * @post_vectors: Don't apply affinity to @post_vectors at end of
* the MSI(-X) vector space * the MSI(-X) vector space
* @nr_sets: Length of passed in *sets array * @nr_sets: The number of interrupt sets for which affinity
* @sets: Number of affinitized sets * spreading is required
* @set_size: Array holding the size of each interrupt set
*/ */
struct irq_affinity { struct irq_affinity {
unsigned int pre_vectors; unsigned int pre_vectors;
unsigned int post_vectors; unsigned int post_vectors;
unsigned int nr_sets; unsigned int nr_sets;
unsigned int *sets; unsigned int set_size[IRQ_AFFINITY_MAX_SETS];
}; };
/** /**
......
...@@ -238,9 +238,10 @@ static int irq_build_affinity_masks(const struct irq_affinity *affd, ...@@ -238,9 +238,10 @@ static int irq_build_affinity_masks(const struct irq_affinity *affd,
* Returns the irq_affinity_desc pointer or NULL if allocation failed. * Returns the irq_affinity_desc pointer or NULL if allocation failed.
*/ */
struct irq_affinity_desc * struct irq_affinity_desc *
irq_create_affinity_masks(unsigned int nvecs, const struct irq_affinity *affd) irq_create_affinity_masks(unsigned int nvecs, struct irq_affinity *affd)
{ {
unsigned int affvecs, curvec, usedvecs, nr_sets, i; unsigned int affvecs, curvec, usedvecs, nr_sets, i;
unsigned int set_size[IRQ_AFFINITY_MAX_SETS];
struct irq_affinity_desc *masks = NULL; struct irq_affinity_desc *masks = NULL;
/* /*
...@@ -250,6 +251,9 @@ irq_create_affinity_masks(unsigned int nvecs, const struct irq_affinity *affd) ...@@ -250,6 +251,9 @@ irq_create_affinity_masks(unsigned int nvecs, const struct irq_affinity *affd)
if (nvecs == affd->pre_vectors + affd->post_vectors) if (nvecs == affd->pre_vectors + affd->post_vectors)
return NULL; return NULL;
if (WARN_ON_ONCE(affd->nr_sets > IRQ_AFFINITY_MAX_SETS))
return NULL;
masks = kcalloc(nvecs, sizeof(*masks), GFP_KERNEL); masks = kcalloc(nvecs, sizeof(*masks), GFP_KERNEL);
if (!masks) if (!masks)
return NULL; return NULL;
...@@ -263,11 +267,15 @@ irq_create_affinity_masks(unsigned int nvecs, const struct irq_affinity *affd) ...@@ -263,11 +267,15 @@ irq_create_affinity_masks(unsigned int nvecs, const struct irq_affinity *affd)
*/ */
affvecs = nvecs - affd->pre_vectors - affd->post_vectors; affvecs = nvecs - affd->pre_vectors - affd->post_vectors;
nr_sets = affd->nr_sets; nr_sets = affd->nr_sets;
if (!nr_sets) if (!nr_sets) {
nr_sets = 1; nr_sets = 1;
set_size[0] = affvecs;
} else {
memcpy(set_size, affd->set_size, nr_sets * sizeof(unsigned int));
}
for (i = 0, usedvecs = 0; i < nr_sets; i++) { for (i = 0, usedvecs = 0; i < nr_sets; i++) {
unsigned int this_vecs = affd->sets ? affd->sets[i] : affvecs; unsigned int this_vecs = set_size[i];
int ret; int ret;
ret = irq_build_affinity_masks(affd, curvec, this_vecs, ret = irq_build_affinity_masks(affd, curvec, this_vecs,
...@@ -314,7 +322,7 @@ unsigned int irq_calc_affinity_vectors(unsigned int minvec, unsigned int maxvec, ...@@ -314,7 +322,7 @@ unsigned int irq_calc_affinity_vectors(unsigned int minvec, unsigned int maxvec,
unsigned int i; unsigned int i;
for (i = 0, set_vecs = 0; i < affd->nr_sets; i++) for (i = 0, set_vecs = 0; i < affd->nr_sets; i++)
set_vecs += affd->sets[i]; set_vecs += affd->set_size[i];
} else { } else {
get_online_cpus(); get_online_cpus();
set_vecs = cpumask_weight(cpu_possible_mask); set_vecs = cpumask_weight(cpu_possible_mask);
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment