Groups | Search | Server Info | Keyboard shortcuts | Login | Register [http] [https] [nntp] [nntps]
Groups > linux.kernel > #1422263 > unrolled thread
| Started by | Christoph Hellwig <hch@lst.de> |
|---|---|
| First post | 2016-06-14 22:00 +0200 |
| Last post | 2016-06-26 21:40 +0200 |
| Articles | 20 on this page of 40 — 4 participants |
Back to article view | Back to linux.kernel
automatic interrupt affinity for MSI/MSI-X capable devices V2 Christoph Hellwig <hch@lst.de> - 2016-06-14 22:00 +0200
[PATCH 03/13] irq: Add affinity hint to irq allocation Christoph Hellwig <hch@lst.de> - 2016-06-14 22:00 +0200
[PATCH 11/13] blk-mq: allow the driver to pass in an affinity mask Christoph Hellwig <hch@lst.de> - 2016-06-14 22:00 +0200
Re: [PATCH 11/13] blk-mq: allow the driver to pass in an affinity mask Alexander Gordeev <agordeev@redhat.com> - 2016-07-04 10:20 +0200
Re: [PATCH 11/13] blk-mq: allow the driver to pass in an affinity mask Christoph Hellwig <hch@lst.de> - 2016-07-04 10:40 +0200
Re: [PATCH 11/13] blk-mq: allow the driver to pass in an affinity mask Alexander Gordeev <agordeev@redhat.com> - 2016-07-04 11:40 +0200
[PATCH 06/13] irq: add a helper spread an affinity mask for MSI/MSI-X vectors Christoph Hellwig <hch@lst.de> - 2016-06-14 22:00 +0200
Re: [PATCH 06/13] irq: add a helper spread an affinity mask for MSI/MSI-X vectors "Guilherme G. Piccoli" <gpiccoli@linux.vnet.ibm.com> - 2016-06-15 00:00 +0200
Re: [PATCH 06/13] irq: add a helper spread an affinity mask for MSI/MSI-X vectors Christoph Hellwig <hch@lst.de> - 2016-06-15 12:20 +0200
Re: [PATCH 06/13] irq: add a helper spread an affinity mask for MSI/MSI-X vectors "Guilherme G. Piccoli" <gpiccoli@linux.vnet.ibm.com> - 2016-06-15 15:10 +0200
Re: [PATCH 06/13] irq: add a helper spread an affinity mask for MSI/MSI-X vectors Christoph Hellwig <hch@lst.de> - 2016-06-16 17:20 +0200
Re: [PATCH 06/13] irq: add a helper spread an affinity mask for MSI/MSI-X vectors Alexander Gordeev <agordeev@redhat.com> - 2016-06-25 22:10 +0200
Re: [PATCH 06/13] irq: add a helper spread an affinity mask for MSI/MSI-X vectors Christoph Hellwig <hch@lst.de> - 2016-06-30 20:10 +0200
Re: [PATCH 06/13] irq: add a helper spread an affinity mask for MSI/MSI-X vectors Alexander Gordeev <agordeev@redhat.com> - 2016-07-01 09:30 +0200
[PATCH 07/13] pci: Provide sensible irq vector alloc/free routines Christoph Hellwig <hch@lst.de> - 2016-06-14 22:00 +0200
Re: [PATCH 07/13] pci: Provide sensible irq vector alloc/free routines Alexander Gordeev <agordeev@redhat.com> - 2016-06-23 13:20 +0200
Re: [PATCH 07/13] pci: Provide sensible irq vector alloc/free routines Christoph Hellwig <hch@lst.de> - 2016-06-30 19:00 +0200
Re: [PATCH 07/13] pci: Provide sensible irq vector alloc/free routines Alexander Gordeev <agordeev@redhat.com> - 2016-06-30 19:30 +0200
Re: [PATCH 07/13] pci: Provide sensible irq vector alloc/free routines Christoph Hellwig <hch@lst.de> - 2016-06-30 19:40 +0200
[PATCH 02/13] irq: Introduce IRQD_AFFINITY_MANAGED flag Christoph Hellwig <hch@lst.de> - 2016-06-14 22:00 +0200
Re: [PATCH 02/13] irq: Introduce IRQD_AFFINITY_MANAGED flag Christoph Hellwig <hch@lst.de> - 2016-06-15 12:30 +0200
Re: [PATCH 02/13] irq: Introduce IRQD_AFFINITY_MANAGED flag Keith Busch <keith.busch@intel.com> - 2016-06-15 17:10 +0200
Re: [PATCH 02/13] irq: Introduce IRQD_AFFINITY_MANAGED flag Keith Busch <keith.busch@intel.com> - 2016-06-15 18:00 +0200
Re: [PATCH 02/13] irq: Introduce IRQD_AFFINITY_MANAGED flag Keith Busch <keith.busch@intel.com> - 2016-06-15 22:00 +0200
Re: [PATCH 02/13] irq: Introduce IRQD_AFFINITY_MANAGED flag Keith Busch <keith.busch@intel.com> - 2016-06-15 22:10 +0200
Re: [PATCH 02/13] irq: Introduce IRQD_AFFINITY_MANAGED flag Keith Busch <keith.busch@intel.com> - 2016-06-16 17:20 +0200
Re: [PATCH 02/13] irq: Introduce IRQD_AFFINITY_MANAGED flag Alexander Gordeev <agordeev@redhat.com> - 2016-06-22 14:00 +0200
Re: [PATCH 02/13] irq: Introduce IRQD_AFFINITY_MANAGED flag Christoph Hellwig <hch@lst.de> - 2016-06-16 17:30 +0200
Re: [PATCH 02/13] irq: Introduce IRQD_AFFINITY_MANAGED flag Christoph Hellwig <hch@lst.de> - 2016-06-20 14:30 +0200
Re: [PATCH 02/13] irq: Introduce IRQD_AFFINITY_MANAGED flag Christoph Hellwig <hch@lst.de> - 2016-06-21 16:40 +0200
[PATCH 01/13] irq/msi: Remove unused MSI_FLAG_IDENTITY_MAP Christoph Hellwig <hch@lst.de> - 2016-06-14 22:00 +0200
[PATCH 09/13] blk-mq: don't redistribute hardware queues on a CPU hotplug event Christoph Hellwig <hch@lst.de> - 2016-06-14 22:10 +0200
[PATCH 13/13] nvme: remove the post_scan callout Christoph Hellwig <hch@lst.de> - 2016-06-14 22:10 +0200
[PATCH 08/13] pci: spread interrupt vectors in pci_alloc_irq_vectors Christoph Hellwig <hch@lst.de> - 2016-06-14 22:10 +0200
Re: [PATCH 08/13] pci: spread interrupt vectors in pci_alloc_irq_vectors Alexander Gordeev <agordeev@redhat.com> - 2016-06-25 22:30 +0200
[PATCH 05/13] irq/msi: Make use of affinity aware allocations Christoph Hellwig <hch@lst.de> - 2016-06-14 22:10 +0200
[PATCH 12/13] nvme: switch to use pci_alloc_irq_vectors Christoph Hellwig <hch@lst.de> - 2016-06-14 22:10 +0200
[PATCH 04/13] irq: Use affinity hint in irqdesc allocation Christoph Hellwig <hch@lst.de> - 2016-06-14 22:10 +0200
Re: automatic interrupt affinity for MSI/MSI-X capable devices V2 Christoph Hellwig <hch@lst.de> - 2016-06-16 17:30 +0200
Re: automatic interrupt affinity for MSI/MSI-X capable devices V2 Alexander Gordeev <agordeev@redhat.com> - 2016-06-26 21:40 +0200
Page 1 of 2 [1] 2 Next page →
| From | Christoph Hellwig <hch@lst.de> |
|---|---|
| Date | 2016-06-14 22:00 +0200 |
| Subject | automatic interrupt affinity for MSI/MSI-X capable devices V2 |
| Message-ID | <rK2Fb-Rz-3@gated-at.bofh.it> |
This series enhances the irq and PCI code to allow spreading around MSI and MSI-X vectors so that they have per-cpu affinity if possible, or at least per-node. For that it takes the algorithm from blk-mq, moves it to a common place, and makes it available through a vastly simplified PCI interrupt allocation API. It then switches blk-mq to be able to pick up the queue mapping from the device if available, and demonstrates all this using the NVMe driver. There also is a git tree available at: git://git.infradead.org/users/hch/block.git Gitweb: http://git.infradead.org/users/hch/block.git/shortlog/refs/heads/msix-spreading.4 Changes since V1: - irq core improvements to properly assign the affinity before request_irq (tglx) - better handling of the MSI vs MSI-X differences in the low level MSI allocator (hch and tglx) - various improvements to pci_alloc_irq_vectors (hch) - remove blk-mq hardware queue reassigned on hotplug cpu events (hch) - forward ported to Jens' current for-next tree (hch)
[toc] | [next] | [standalone]
| From | Christoph Hellwig <hch@lst.de> |
|---|---|
| Date | 2016-06-14 22:00 +0200 |
| Subject | [PATCH 03/13] irq: Add affinity hint to irq allocation |
| Message-ID | <rK2Fc-Rz-9@gated-at.bofh.it> |
| In reply to | #1422263 |
From: Thomas Gleixner <tglx@linutronix.de>
Add an extra argument to the irq(domain) allocation functions, so we can hand
down affinity hints to the allocator. Thats necessary to implement proper
support for multiqueue devices.
Signed-off-by: Thomas Gleixner <tglx@linutronix.de>
---
arch/sparc/kernel/irq_64.c | 2 +-
arch/x86/kernel/apic/io_apic.c | 5 +++--
include/linux/irq.h | 4 ++--
include/linux/irqdomain.h | 9 ++++++---
kernel/irq/ipi.c | 2 +-
kernel/irq/irqdesc.c | 12 ++++++++----
kernel/irq/irqdomain.c | 22 ++++++++++++++--------
kernel/irq/manage.c | 7 ++++---
kernel/irq/msi.c | 3 ++-
9 files changed, 41 insertions(+), 25 deletions(-)
diff --git a/arch/sparc/kernel/irq_64.c b/arch/sparc/kernel/irq_64.c
index e22416c..34a7930 100644
--- a/arch/sparc/kernel/irq_64.c
+++ b/arch/sparc/kernel/irq_64.c
@@ -242,7 +242,7 @@ unsigned int irq_alloc(unsigned int dev_handle, unsigned int dev_ino)
{
int irq;
- irq = __irq_alloc_descs(-1, 1, 1, numa_node_id(), NULL);
+ irq = __irq_alloc_descs(-1, 1, 1, numa_node_id(), NULL, NULL);
if (irq <= 0)
goto out;
diff --git a/arch/x86/kernel/apic/io_apic.c b/arch/x86/kernel/apic/io_apic.c
index 84e33ff..bca0c81 100644
--- a/arch/x86/kernel/apic/io_apic.c
+++ b/arch/x86/kernel/apic/io_apic.c
@@ -981,7 +981,7 @@ static int alloc_irq_from_domain(struct irq_domain *domain, int ioapic, u32 gsi,
return __irq_domain_alloc_irqs(domain, irq, 1,
ioapic_alloc_attr_node(info),
- info, legacy);
+ info, legacy, NULL);
}
/*
@@ -1014,7 +1014,8 @@ static int alloc_isa_irq_from_domain(struct irq_domain *domain,
info->ioapic_pin))
return -ENOMEM;
} else {
- irq = __irq_domain_alloc_irqs(domain, irq, 1, node, info, true);
+ irq = __irq_domain_alloc_irqs(domain, irq, 1, node, info, true,
+ NULL);
if (irq >= 0) {
irq_data = irq_domain_get_irq_data(domain, irq);
data = irq_data->chip_data;
diff --git a/include/linux/irq.h b/include/linux/irq.h
index 49d66d1..63803a4 100644
--- a/include/linux/irq.h
+++ b/include/linux/irq.h
@@ -708,11 +708,11 @@ static inline struct cpumask *irq_data_get_affinity_mask(struct irq_data *d)
unsigned int arch_dynirq_lower_bound(unsigned int from);
int __irq_alloc_descs(int irq, unsigned int from, unsigned int cnt, int node,
- struct module *owner);
+ struct module *owner, const struct cpumask *affinity);
/* use macros to avoid needing export.h for THIS_MODULE */
#define irq_alloc_descs(irq, from, cnt, node) \
- __irq_alloc_descs(irq, from, cnt, node, THIS_MODULE)
+ __irq_alloc_descs(irq, from, cnt, node, THIS_MODULE, NULL)
#define irq_alloc_desc(node) \
irq_alloc_descs(-1, 0, 1, node)
diff --git a/include/linux/irqdomain.h b/include/linux/irqdomain.h
index f1f36e0..1aee0fb 100644
--- a/include/linux/irqdomain.h
+++ b/include/linux/irqdomain.h
@@ -39,6 +39,7 @@ struct irq_domain;
struct of_device_id;
struct irq_chip;
struct irq_data;
+struct cpumask;
/* Number of irqs reserved for a legacy isa controller */
#define NUM_ISA_INTERRUPTS 16
@@ -217,7 +218,8 @@ extern struct irq_domain *irq_find_matching_fwspec(struct irq_fwspec *fwspec,
enum irq_domain_bus_token bus_token);
extern void irq_set_default_host(struct irq_domain *host);
extern int irq_domain_alloc_descs(int virq, unsigned int nr_irqs,
- irq_hw_number_t hwirq, int node);
+ irq_hw_number_t hwirq, int node,
+ const struct cpumask *affinity);
static inline struct fwnode_handle *of_node_to_fwnode(struct device_node *node)
{
@@ -389,7 +391,7 @@ static inline struct irq_domain *irq_domain_add_hierarchy(struct irq_domain *par
extern int __irq_domain_alloc_irqs(struct irq_domain *domain, int irq_base,
unsigned int nr_irqs, int node, void *arg,
- bool realloc);
+ bool realloc, const struct cpumask *affinity);
extern void irq_domain_free_irqs(unsigned int virq, unsigned int nr_irqs);
extern void irq_domain_activate_irq(struct irq_data *irq_data);
extern void irq_domain_deactivate_irq(struct irq_data *irq_data);
@@ -397,7 +399,8 @@ extern void irq_domain_deactivate_irq(struct irq_data *irq_data);
static inline int irq_domain_alloc_irqs(struct irq_domain *domain,
unsigned int nr_irqs, int node, void *arg)
{
- return __irq_domain_alloc_irqs(domain, -1, nr_irqs, node, arg, false);
+ return __irq_domain_alloc_irqs(domain, -1, nr_irqs, node, arg, false,
+ NULL);
}
extern int irq_domain_alloc_irqs_recursive(struct irq_domain *domain,
diff --git a/kernel/irq/ipi.c b/kernel/irq/ipi.c
index 89b49f6..4fd2351 100644
--- a/kernel/irq/ipi.c
+++ b/kernel/irq/ipi.c
@@ -76,7 +76,7 @@ int irq_reserve_ipi(struct irq_domain *domain,
}
}
- virq = irq_domain_alloc_descs(-1, nr_irqs, 0, NUMA_NO_NODE);
+ virq = irq_domain_alloc_descs(-1, nr_irqs, 0, NUMA_NO_NODE, NULL);
if (virq <= 0) {
pr_warn("Can't reserve IPI, failed to alloc descs\n");
return -ENOMEM;
diff --git a/kernel/irq/irqdesc.c b/kernel/irq/irqdesc.c
index 8731e1c..b8df4fc 100644
--- a/kernel/irq/irqdesc.c
+++ b/kernel/irq/irqdesc.c
@@ -223,7 +223,7 @@ static void free_desc(unsigned int irq)
}
static int alloc_descs(unsigned int start, unsigned int cnt, int node,
- struct module *owner)
+ const struct cpumask *affinity, struct module *owner)
{
struct irq_desc *desc;
int i;
@@ -333,6 +333,7 @@ static void free_desc(unsigned int irq)
}
static inline int alloc_descs(unsigned int start, unsigned int cnt, int node,
+ const struct cpumask *affinity,
struct module *owner)
{
u32 i;
@@ -453,12 +454,15 @@ EXPORT_SYMBOL_GPL(irq_free_descs);
* @cnt: Number of consecutive irqs to allocate.
* @node: Preferred node on which the irq descriptor should be allocated
* @owner: Owning module (can be NULL)
+ * @affinity: Optional pointer to an affinity mask which hints where the
+ * irq descriptors should be allocated and which default
+ * affinities to use
*
* Returns the first irq number or error code
*/
int __ref
__irq_alloc_descs(int irq, unsigned int from, unsigned int cnt, int node,
- struct module *owner)
+ struct module *owner, const struct cpumask *affinity)
{
int start, ret;
@@ -494,7 +498,7 @@ __irq_alloc_descs(int irq, unsigned int from, unsigned int cnt, int node,
bitmap_set(allocated_irqs, start, cnt);
mutex_unlock(&sparse_irq_lock);
- return alloc_descs(start, cnt, node, owner);
+ return alloc_descs(start, cnt, node, affinity, owner);
err:
mutex_unlock(&sparse_irq_lock);
@@ -512,7 +516,7 @@ EXPORT_SYMBOL_GPL(__irq_alloc_descs);
*/
unsigned int irq_alloc_hwirqs(int cnt, int node)
{
- int i, irq = __irq_alloc_descs(-1, 0, cnt, node, NULL);
+ int i, irq = __irq_alloc_descs(-1, 0, cnt, node, NULL, NULL);
if (irq < 0)
return 0;
diff --git a/kernel/irq/irqdomain.c b/kernel/irq/irqdomain.c
index 8798b6c..79459b7 100644
--- a/kernel/irq/irqdomain.c
+++ b/kernel/irq/irqdomain.c
@@ -481,7 +481,7 @@ unsigned int irq_create_mapping(struct irq_domain *domain,
}
/* Allocate a virtual interrupt number */
- virq = irq_domain_alloc_descs(-1, 1, hwirq, of_node_to_nid(of_node));
+ virq = irq_domain_alloc_descs(-1, 1, hwirq, of_node_to_nid(of_node), NULL);
if (virq <= 0) {
pr_debug("-> virq allocation failed\n");
return 0;
@@ -835,19 +835,23 @@ const struct irq_domain_ops irq_domain_simple_ops = {
EXPORT_SYMBOL_GPL(irq_domain_simple_ops);
int irq_domain_alloc_descs(int virq, unsigned int cnt, irq_hw_number_t hwirq,
- int node)
+ int node, const struct cpumask *affinity)
{
unsigned int hint;
if (virq >= 0) {
- virq = irq_alloc_descs(virq, virq, cnt, node);
+ virq = __irq_alloc_descs(virq, virq, cnt, node, THIS_MODULE,
+ affinity);
} else {
hint = hwirq % nr_irqs;
if (hint == 0)
hint++;
- virq = irq_alloc_descs_from(hint, cnt, node);
- if (virq <= 0 && hint > 1)
- virq = irq_alloc_descs_from(1, cnt, node);
+ virq = __irq_alloc_descs(-1, hint, cnt, node, THIS_MODULE,
+ affinity);
+ if (virq <= 0 && hint > 1) {
+ virq = __irq_alloc_descs(-1, 1, cnt, node, THIS_MODULE,
+ affinity);
+ }
}
return virq;
@@ -1160,6 +1164,7 @@ int irq_domain_alloc_irqs_recursive(struct irq_domain *domain,
* @node: NUMA node id for memory allocation
* @arg: domain specific argument
* @realloc: IRQ descriptors have already been allocated if true
+ * @affinity: Optional irq affinity mask for multiqueue devices
*
* Allocate IRQ numbers and initialized all data structures to support
* hierarchy IRQ domains.
@@ -1175,7 +1180,7 @@ int irq_domain_alloc_irqs_recursive(struct irq_domain *domain,
*/
int __irq_domain_alloc_irqs(struct irq_domain *domain, int irq_base,
unsigned int nr_irqs, int node, void *arg,
- bool realloc)
+ bool realloc, const struct cpumask *affinity)
{
int i, ret, virq;
@@ -1193,7 +1198,8 @@ int __irq_domain_alloc_irqs(struct irq_domain *domain, int irq_base,
if (realloc && irq_base >= 0) {
virq = irq_base;
} else {
- virq = irq_domain_alloc_descs(irq_base, nr_irqs, 0, node);
+ virq = irq_domain_alloc_descs(irq_base, nr_irqs, 0, node,
+ affinity);
if (virq < 0) {
pr_debug("cannot allocate IRQ(base %d, count %d)\n",
irq_base, nr_irqs);
diff --git a/kernel/irq/manage.c b/kernel/irq/manage.c
index 30658e9..ad0aac6 100644
--- a/kernel/irq/manage.c
+++ b/kernel/irq/manage.c
@@ -353,10 +353,11 @@ static int setup_affinity(struct irq_desc *desc, struct cpumask *mask)
return 0;
/*
- * Preserve an userspace affinity setup, but make sure that
- * one of the targets is online.
+ * Preserve the managed affinity setting and an userspace affinity
+ * setup, but make sure that one of the targets is online.
*/
- if (irqd_has_set(&desc->irq_data, IRQD_AFFINITY_SET)) {
+ if (irqd_affinity_is_managed(&desc->irq_data) ||
+ irqd_has_set(&desc->irq_data, IRQD_AFFINITY_SET)) {
if (cpumask_intersects(desc->irq_common_data.affinity,
cpu_online_mask))
set = desc->irq_common_data.affinity;
diff --git a/kernel/irq/msi.c b/kernel/irq/msi.c
index eb5bf2b..58dbbac 100644
--- a/kernel/irq/msi.c
+++ b/kernel/irq/msi.c
@@ -334,7 +334,8 @@ int msi_domain_alloc_irqs(struct irq_domain *domain, struct device *dev,
ops->set_desc(&arg, desc);
virq = __irq_domain_alloc_irqs(domain, -1, desc->nvec_used,
- dev_to_node(dev), &arg, false);
+ dev_to_node(dev), &arg, false,
+ NULL);
if (virq < 0) {
ret = -ENOSPC;
if (ops->handle_error)
--
2.1.4
[toc] | [prev] | [next] | [standalone]
| From | Christoph Hellwig <hch@lst.de> |
|---|---|
| Date | 2016-06-14 22:00 +0200 |
| Subject | [PATCH 11/13] blk-mq: allow the driver to pass in an affinity mask |
| Message-ID | <rK2Fc-Rz-11@gated-at.bofh.it> |
| In reply to | #1422263 |
Allow drivers to pass in the affinity mask from the generic interrupt
layer, and spread queues based on that. If the driver doesn't pass in
a mask we will create it using the genirq helper. As this helper was
modelled after the blk-mq algorithm there should be no change in
behavior.
Signed-off-by: Christoph Hellwig <hch@lst.de>
---
block/Makefile | 2 +-
block/blk-mq-cpumap.c | 120 -------------------------------------------------
block/blk-mq.c | 72 ++++++++++++++++++++++++++---
block/blk-mq.h | 8 ----
include/linux/blk-mq.h | 1 +
5 files changed, 69 insertions(+), 134 deletions(-)
delete mode 100644 block/blk-mq-cpumap.c
diff --git a/block/Makefile b/block/Makefile
index 9eda232..aeb318d 100644
--- a/block/Makefile
+++ b/block/Makefile
@@ -6,7 +6,7 @@ obj-$(CONFIG_BLOCK) := bio.o elevator.o blk-core.o blk-tag.o blk-sysfs.o \
blk-flush.o blk-settings.o blk-ioc.o blk-map.o \
blk-exec.o blk-merge.o blk-softirq.o blk-timeout.o \
blk-lib.o blk-mq.o blk-mq-tag.o \
- blk-mq-sysfs.o blk-mq-cpu.o blk-mq-cpumap.o ioctl.o \
+ blk-mq-sysfs.o blk-mq-cpu.o ioctl.o \
genhd.o scsi_ioctl.o partition-generic.o ioprio.o \
badblocks.o partitions/
diff --git a/block/blk-mq-cpumap.c b/block/blk-mq-cpumap.c
deleted file mode 100644
index d0634bc..0000000
--- a/block/blk-mq-cpumap.c
+++ /dev/null
@@ -1,120 +0,0 @@
-/*
- * CPU <-> hardware queue mapping helpers
- *
- * Copyright (C) 2013-2014 Jens Axboe
- */
-#include <linux/kernel.h>
-#include <linux/threads.h>
-#include <linux/module.h>
-#include <linux/mm.h>
-#include <linux/smp.h>
-#include <linux/cpu.h>
-
-#include <linux/blk-mq.h>
-#include "blk.h"
-#include "blk-mq.h"
-
-static int cpu_to_queue_index(unsigned int nr_cpus, unsigned int nr_queues,
- const int cpu)
-{
- return cpu * nr_queues / nr_cpus;
-}
-
-static int get_first_sibling(unsigned int cpu)
-{
- unsigned int ret;
-
- ret = cpumask_first(topology_sibling_cpumask(cpu));
- if (ret < nr_cpu_ids)
- return ret;
-
- return cpu;
-}
-
-int blk_mq_update_queue_map(unsigned int *map, unsigned int nr_queues,
- const struct cpumask *online_mask)
-{
- unsigned int i, nr_cpus, nr_uniq_cpus, queue, first_sibling;
- cpumask_var_t cpus;
-
- if (!alloc_cpumask_var(&cpus, GFP_ATOMIC))
- return 1;
-
- cpumask_clear(cpus);
- nr_cpus = nr_uniq_cpus = 0;
- for_each_cpu(i, online_mask) {
- nr_cpus++;
- first_sibling = get_first_sibling(i);
- if (!cpumask_test_cpu(first_sibling, cpus))
- nr_uniq_cpus++;
- cpumask_set_cpu(i, cpus);
- }
-
- queue = 0;
- for_each_possible_cpu(i) {
- if (!cpumask_test_cpu(i, online_mask)) {
- map[i] = 0;
- continue;
- }
-
- /*
- * Easy case - we have equal or more hardware queues. Or
- * there are no thread siblings to take into account. Do
- * 1:1 if enough, or sequential mapping if less.
- */
- if (nr_queues >= nr_cpus || nr_cpus == nr_uniq_cpus) {
- map[i] = cpu_to_queue_index(nr_cpus, nr_queues, queue);
- queue++;
- continue;
- }
-
- /*
- * Less then nr_cpus queues, and we have some number of
- * threads per cores. Map sibling threads to the same
- * queue.
- */
- first_sibling = get_first_sibling(i);
- if (first_sibling == i) {
- map[i] = cpu_to_queue_index(nr_uniq_cpus, nr_queues,
- queue);
- queue++;
- } else
- map[i] = map[first_sibling];
- }
-
- free_cpumask_var(cpus);
- return 0;
-}
-
-unsigned int *blk_mq_make_queue_map(struct blk_mq_tag_set *set)
-{
- unsigned int *map;
-
- /* If cpus are offline, map them to first hctx */
- map = kzalloc_node(sizeof(*map) * nr_cpu_ids, GFP_KERNEL,
- set->numa_node);
- if (!map)
- return NULL;
-
- if (!blk_mq_update_queue_map(map, set->nr_hw_queues, cpu_online_mask))
- return map;
-
- kfree(map);
- return NULL;
-}
-
-/*
- * 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 i;
-
- for_each_possible_cpu(i) {
- if (index == mq_map[i])
- return local_memory_node(cpu_to_node(i));
- }
-
- return NUMA_NO_NODE;
-}
diff --git a/block/blk-mq.c b/block/blk-mq.c
index 622cb22..6027a49 100644
--- a/block/blk-mq.c
+++ b/block/blk-mq.c
@@ -22,6 +22,7 @@
#include <linux/sched/sysctl.h>
#include <linux/delay.h>
#include <linux/crash_dump.h>
+#include <linux/interrupt.h>
#include <trace/events/block.h>
@@ -1954,6 +1955,22 @@ struct request_queue *blk_mq_init_queue(struct blk_mq_tag_set *set)
}
EXPORT_SYMBOL(blk_mq_init_queue);
+/*
+ * We have no quick way of doing reverse lookups. This is only used at
+ * queue init time, so runtime isn't important.
+ */
+static int blk_mq_hw_queue_to_node(unsigned int *mq_map, unsigned int index)
+{
+ int i;
+
+ for_each_possible_cpu(i) {
+ if (index == mq_map[i])
+ return local_memory_node(cpu_to_node(i));
+ }
+
+ return NUMA_NO_NODE;
+}
+
static void blk_mq_realloc_hw_ctxs(struct blk_mq_tag_set *set,
struct request_queue *q)
{
@@ -2253,6 +2270,30 @@ struct cpumask *blk_mq_tags_cpumask(struct blk_mq_tags *tags)
}
EXPORT_SYMBOL_GPL(blk_mq_tags_cpumask);
+static int blk_mq_create_mq_map(struct blk_mq_tag_set *set,
+ const struct cpumask *affinity_mask)
+{
+ int queue = -1, cpu = 0;
+
+ set->mq_map = kzalloc_node(sizeof(*set->mq_map) * nr_cpu_ids,
+ GFP_KERNEL, set->numa_node);
+ if (!set->mq_map)
+ return -ENOMEM;
+
+ if (!affinity_mask)
+ return 0; /* map all cpus to queue 0 */
+
+ /* If cpus are offline, map them to first hctx */
+ for_each_online_cpu(cpu) {
+ if (cpumask_test_cpu(cpu, affinity_mask))
+ queue++;
+ if (queue > 0)
+ set->mq_map[cpu] = queue;
+ }
+
+ return 0;
+}
+
/*
* Alloc a tag set to be associated with one or more request queues.
* May fail with EINVAL for various error conditions. May adjust the
@@ -2261,6 +2302,8 @@ EXPORT_SYMBOL_GPL(blk_mq_tags_cpumask);
*/
int blk_mq_alloc_tag_set(struct blk_mq_tag_set *set)
{
+ int ret;
+
BUILD_BUG_ON(BLK_MQ_MAX_DEPTH > 1 << BLK_MQ_UNIQUE_TAG_BITS);
if (!set->nr_hw_queues)
@@ -2299,11 +2342,30 @@ int blk_mq_alloc_tag_set(struct blk_mq_tag_set *set)
if (!set->tags)
return -ENOMEM;
- set->mq_map = blk_mq_make_queue_map(set);
- if (!set->mq_map)
- goto out_free_tags;
+ /*
+ * Use the passed in affinity mask if the driver provided one.
+ */
+ if (set->affinity_mask) {
+ ret = blk_mq_create_mq_map(set, set->affinity_mask);
+ if (!set->mq_map)
+ goto out_free_tags;
+ } else {
+ struct cpumask *affinity_mask;
- if (blk_mq_alloc_rq_maps(set))
+ ret = irq_create_affinity_mask(&affinity_mask,
+ &set->nr_hw_queues);
+ if (ret)
+ goto out_free_tags;
+
+ ret = blk_mq_create_mq_map(set, affinity_mask);
+ kfree(affinity_mask);
+
+ if (!set->mq_map)
+ goto out_free_tags;
+ }
+
+ ret = blk_mq_alloc_rq_maps(set);
+ if (ret)
goto out_free_mq_map;
mutex_init(&set->tag_list_lock);
@@ -2317,7 +2379,7 @@ out_free_mq_map:
out_free_tags:
kfree(set->tags);
set->tags = NULL;
- return -ENOMEM;
+ return ret;
}
EXPORT_SYMBOL(blk_mq_alloc_tag_set);
diff --git a/block/blk-mq.h b/block/blk-mq.h
index 9087b11..fe7e21f 100644
--- a/block/blk-mq.h
+++ b/block/blk-mq.h
@@ -45,14 +45,6 @@ void blk_mq_enable_hotplug(void);
void blk_mq_disable_hotplug(void);
/*
- * CPU -> queue mappings
- */
-extern unsigned int *blk_mq_make_queue_map(struct blk_mq_tag_set *set);
-extern int blk_mq_update_queue_map(unsigned int *map, unsigned int nr_queues,
- const struct cpumask *online_mask);
-extern int blk_mq_hw_queue_to_node(unsigned int *map, unsigned int);
-
-/*
* sysfs helpers
*/
extern int blk_mq_sysfs_register(struct request_queue *q);
diff --git a/include/linux/blk-mq.h b/include/linux/blk-mq.h
index 0a3b138..404cc86 100644
--- a/include/linux/blk-mq.h
+++ b/include/linux/blk-mq.h
@@ -75,6 +75,7 @@ struct blk_mq_tag_set {
unsigned int timeout;
unsigned int flags; /* BLK_MQ_F_* */
void *driver_data;
+ struct cpumask *affinity_mask;
struct blk_mq_tags **tags;
--
2.1.4
[toc] | [prev] | [next] | [standalone]
| From | Alexander Gordeev <agordeev@redhat.com> |
|---|---|
| Date | 2016-07-04 10:20 +0200 |
| Subject | Re: [PATCH 11/13] blk-mq: allow the driver to pass in an affinity mask |
| Message-ID | <rR7gJ-3eC-3@gated-at.bofh.it> |
| In reply to | #1422265 |
On Tue, Jun 14, 2016 at 09:59:04PM +0200, Christoph Hellwig wrote:
> +static int blk_mq_create_mq_map(struct blk_mq_tag_set *set,
> + const struct cpumask *affinity_mask)
> +{
> + int queue = -1, cpu = 0;
> +
> + set->mq_map = kzalloc_node(sizeof(*set->mq_map) * nr_cpu_ids,
> + GFP_KERNEL, set->numa_node);
> + if (!set->mq_map)
> + return -ENOMEM;
> +
> + if (!affinity_mask)
> + return 0; /* map all cpus to queue 0 */
> +
> + /* If cpus are offline, map them to first hctx */
> + for_each_online_cpu(cpu) {
> + if (cpumask_test_cpu(cpu, affinity_mask))
> + queue++;
CPUs missing in an affinity mask are mapped to hctxs. Is that intended?
> + if (queue > 0)
Why this check?
> + set->mq_map[cpu] = queue;
> + }
> +
> + return 0;
> +}
> +
[toc] | [prev] | [next] | [standalone]
| From | Christoph Hellwig <hch@lst.de> |
|---|---|
| Date | 2016-07-04 10:40 +0200 |
| Subject | Re: [PATCH 11/13] blk-mq: allow the driver to pass in an affinity mask |
| Message-ID | <rR7A5-3l0-7@gated-at.bofh.it> |
| In reply to | #1436254 |
On Mon, Jul 04, 2016 at 10:15:41AM +0200, Alexander Gordeev wrote:
> On Tue, Jun 14, 2016 at 09:59:04PM +0200, Christoph Hellwig wrote:
> > +static int blk_mq_create_mq_map(struct blk_mq_tag_set *set,
> > + const struct cpumask *affinity_mask)
> > +{
> > + int queue = -1, cpu = 0;
> > +
> > + set->mq_map = kzalloc_node(sizeof(*set->mq_map) * nr_cpu_ids,
> > + GFP_KERNEL, set->numa_node);
> > + if (!set->mq_map)
> > + return -ENOMEM;
> > +
> > + if (!affinity_mask)
> > + return 0; /* map all cpus to queue 0 */
> > +
> > + /* If cpus are offline, map them to first hctx */
> > + for_each_online_cpu(cpu) {
> > + if (cpumask_test_cpu(cpu, affinity_mask))
> > + queue++;
>
> CPUs missing in an affinity mask are mapped to hctxs. Is that intended?
Yes - each CPU needs to be mapped to some hctx, otherwise we can't
submit I/O from that CPU.
> > + if (queue > 0)
>
> Why this check?
>
> > + set->mq_map[cpu] = queue;
mq_map is initialized to zero already, so we don't really need the
assignment for queue 0. The reason why this check exists is because
we start with queue = -1 and we never want to assignment -1 to mq_map.
[toc] | [prev] | [next] | [standalone]
| From | Alexander Gordeev <agordeev@redhat.com> |
|---|---|
| Date | 2016-07-04 11:40 +0200 |
| Subject | Re: [PATCH 11/13] blk-mq: allow the driver to pass in an affinity mask |
| Message-ID | <rR8wa-3U6-5@gated-at.bofh.it> |
| In reply to | #1436299 |
On Mon, Jul 04, 2016 at 10:38:49AM +0200, Christoph Hellwig wrote:
> On Mon, Jul 04, 2016 at 10:15:41AM +0200, Alexander Gordeev wrote:
> > On Tue, Jun 14, 2016 at 09:59:04PM +0200, Christoph Hellwig wrote:
> > > +static int blk_mq_create_mq_map(struct blk_mq_tag_set *set,
> > > + const struct cpumask *affinity_mask)
> > > +{
> > > + int queue = -1, cpu = 0;
> > > +
> > > + set->mq_map = kzalloc_node(sizeof(*set->mq_map) * nr_cpu_ids,
> > > + GFP_KERNEL, set->numa_node);
> > > + if (!set->mq_map)
> > > + return -ENOMEM;
> > > +
> > > + if (!affinity_mask)
> > > + return 0; /* map all cpus to queue 0 */
> > > +
> > > + /* If cpus are offline, map them to first hctx */
> > > + for_each_online_cpu(cpu) {
> > > + if (cpumask_test_cpu(cpu, affinity_mask))
> > > + queue++;
> >
> > CPUs missing in an affinity mask are mapped to hctxs. Is that intended?
>
> Yes - each CPU needs to be mapped to some hctx, otherwise we can't
> submit I/O from that CPU.
>
> > > + if (queue > 0)
> >
> > Why this check?
> >
> > > + set->mq_map[cpu] = queue;
>
> mq_map is initialized to zero already, so we don't really need the
> assignment for queue 0. The reason why this check exists is because
> we start with queue = -1 and we never want to assignment -1 to mq_map.
Would this read better then?
int queue = 0;
...
/* If cpus are offline, map them to first hctx */
for_each_online_cpu(cpu) {
set->mq_map[cpu] = queue;
if (cpumask_test_cpu(cpu, affinity_mask))
queue++;
}
[toc] | [prev] | [next] | [standalone]
| From | Christoph Hellwig <hch@lst.de> |
|---|---|
| Date | 2016-06-14 22:00 +0200 |
| Subject | [PATCH 06/13] irq: add a helper spread an affinity mask for MSI/MSI-X vectors |
| Message-ID | <rK2Fc-Rz-17@gated-at.bofh.it> |
| In reply to | #1422263 |
This is lifted from the blk-mq code and adopted to use the affinity mask
concept just intruced in the irq handling code.
Signed-off-by: Christoph Hellwig <hch@lst.de>
---
include/linux/interrupt.h | 11 +++++++++
kernel/irq/Makefile | 1 +
kernel/irq/affinity.c | 60 +++++++++++++++++++++++++++++++++++++++++++++++
3 files changed, 72 insertions(+)
create mode 100644 kernel/irq/affinity.c
diff --git a/include/linux/interrupt.h b/include/linux/interrupt.h
index 9fcabeb..12003c0 100644
--- a/include/linux/interrupt.h
+++ b/include/linux/interrupt.h
@@ -278,6 +278,9 @@ extern int irq_set_affinity_hint(unsigned int irq, const struct cpumask *m);
extern int
irq_set_affinity_notifier(unsigned int irq, struct irq_affinity_notify *notify);
+int irq_create_affinity_mask(struct cpumask **affinity_mask,
+ unsigned int *nr_vecs);
+
#else /* CONFIG_SMP */
static inline int irq_set_affinity(unsigned int irq, const struct cpumask *m)
@@ -308,6 +311,14 @@ irq_set_affinity_notifier(unsigned int irq, struct irq_affinity_notify *notify)
{
return 0;
}
+
+static inline int irq_create_affinity_mask(struct cpumask **affinity_mask,
+ unsigned int *nr_vecs)
+{
+ *affinity_mask = NULL;
+ *nr_vecs = 1;
+ return 0;
+}
#endif /* CONFIG_SMP */
/*
diff --git a/kernel/irq/Makefile b/kernel/irq/Makefile
index 2ee42e9..1d3ee31 100644
--- a/kernel/irq/Makefile
+++ b/kernel/irq/Makefile
@@ -9,3 +9,4 @@ obj-$(CONFIG_GENERIC_IRQ_MIGRATION) += cpuhotplug.o
obj-$(CONFIG_PM_SLEEP) += pm.o
obj-$(CONFIG_GENERIC_MSI_IRQ) += msi.o
obj-$(CONFIG_GENERIC_IRQ_IPI) += ipi.o
+obj-$(CONFIG_SMP) += affinity.o
diff --git a/kernel/irq/affinity.c b/kernel/irq/affinity.c
new file mode 100644
index 0000000..1daf8fb
--- /dev/null
+++ b/kernel/irq/affinity.c
@@ -0,0 +1,60 @@
+
+#include <linux/interrupt.h>
+#include <linux/kernel.h>
+#include <linux/slab.h>
+#include <linux/cpu.h>
+
+static int get_first_sibling(unsigned int cpu)
+{
+ unsigned int ret;
+
+ ret = cpumask_first(topology_sibling_cpumask(cpu));
+ if (ret < nr_cpu_ids)
+ return ret;
+ return cpu;
+}
+
+/*
+ * Take a map of online CPUs and the number of available interrupt vectors
+ * and generate an output cpumask suitable for spreading MSI/MSI-X vectors
+ * so that they are distributed as good as possible around the CPUs. If
+ * more vectors than CPUs are available we'll map one to each CPU,
+ * otherwise we map one to the first sibling of each socket.
+ *
+ * If there are more vectors than CPUs we will still only have one bit
+ * set per CPU, but interrupt code will keep on assining the vectors from
+ * the start of the bitmap until we run out of vectors.
+ */
+int irq_create_affinity_mask(struct cpumask **affinity_mask,
+ unsigned int *nr_vecs)
+{
+ unsigned int vecs = 0;
+
+ if (*nr_vecs == 1) {
+ *affinity_mask = NULL;
+ return 0;
+ }
+
+ *affinity_mask = kzalloc(cpumask_size(), GFP_KERNEL);
+ if (!*affinity_mask)
+ return -ENOMEM;
+
+ if (*nr_vecs >= num_online_cpus()) {
+ cpumask_copy(*affinity_mask, cpu_online_mask);
+ } else {
+ unsigned int cpu;
+
+ for_each_online_cpu(cpu) {
+ if (cpu == get_first_sibling(cpu)) {
+ cpumask_set_cpu(cpu, *affinity_mask);
+ vecs++;
+ }
+
+ if (--(*nr_vecs) == 0)
+ break;
+ }
+ }
+
+ *nr_vecs = vecs;
+ return 0;
+}
--
2.1.4
[toc] | [prev] | [next] | [standalone]
| From | "Guilherme G. Piccoli" <gpiccoli@linux.vnet.ibm.com> |
|---|---|
| Date | 2016-06-15 00:00 +0200 |
| Subject | Re: [PATCH 06/13] irq: add a helper spread an affinity mask for MSI/MSI-X vectors |
| Message-ID | <rK4xp-23y-21@gated-at.bofh.it> |
| In reply to | #1422266 |
On 06/14/2016 04:58 PM, Christoph Hellwig wrote:
> This is lifted from the blk-mq code and adopted to use the affinity mask
> concept just intruced in the irq handling code.
Very nice patch Christoph, thanks. There's a little typo above, on
"intruced".
>
> Signed-off-by: Christoph Hellwig <hch@lst.de>
> ---
> include/linux/interrupt.h | 11 +++++++++
> kernel/irq/Makefile | 1 +
> kernel/irq/affinity.c | 60 +++++++++++++++++++++++++++++++++++++++++++++++
> 3 files changed, 72 insertions(+)
> create mode 100644 kernel/irq/affinity.c
>
> diff --git a/include/linux/interrupt.h b/include/linux/interrupt.h
> index 9fcabeb..12003c0 100644
> --- a/include/linux/interrupt.h
> +++ b/include/linux/interrupt.h
> @@ -278,6 +278,9 @@ extern int irq_set_affinity_hint(unsigned int irq, const struct cpumask *m);
> extern int
> irq_set_affinity_notifier(unsigned int irq, struct irq_affinity_notify *notify);
>
> +int irq_create_affinity_mask(struct cpumask **affinity_mask,
> + unsigned int *nr_vecs);
> +
> #else /* CONFIG_SMP */
>
> static inline int irq_set_affinity(unsigned int irq, const struct cpumask *m)
> @@ -308,6 +311,14 @@ irq_set_affinity_notifier(unsigned int irq, struct irq_affinity_notify *notify)
> {
> return 0;
> }
> +
> +static inline int irq_create_affinity_mask(struct cpumask **affinity_mask,
> + unsigned int *nr_vecs)
> +{
> + *affinity_mask = NULL;
> + *nr_vecs = 1;
> + return 0;
> +}
> #endif /* CONFIG_SMP */
>
> /*
> diff --git a/kernel/irq/Makefile b/kernel/irq/Makefile
> index 2ee42e9..1d3ee31 100644
> --- a/kernel/irq/Makefile
> +++ b/kernel/irq/Makefile
> @@ -9,3 +9,4 @@ obj-$(CONFIG_GENERIC_IRQ_MIGRATION) += cpuhotplug.o
> obj-$(CONFIG_PM_SLEEP) += pm.o
> obj-$(CONFIG_GENERIC_MSI_IRQ) += msi.o
> obj-$(CONFIG_GENERIC_IRQ_IPI) += ipi.o
> +obj-$(CONFIG_SMP) += affinity.o
> diff --git a/kernel/irq/affinity.c b/kernel/irq/affinity.c
> new file mode 100644
> index 0000000..1daf8fb
> --- /dev/null
> +++ b/kernel/irq/affinity.c
> @@ -0,0 +1,60 @@
> +
> +#include <linux/interrupt.h>
> +#include <linux/kernel.h>
> +#include <linux/slab.h>
> +#include <linux/cpu.h>
> +
> +static int get_first_sibling(unsigned int cpu)
> +{
> + unsigned int ret;
> +
> + ret = cpumask_first(topology_sibling_cpumask(cpu));
> + if (ret < nr_cpu_ids)
> + return ret;
> + return cpu;
> +}
> +
> +/*
> + * Take a map of online CPUs and the number of available interrupt vectors
> + * and generate an output cpumask suitable for spreading MSI/MSI-X vectors
> + * so that they are distributed as good as possible around the CPUs. If
> + * more vectors than CPUs are available we'll map one to each CPU,
> + * otherwise we map one to the first sibling of each socket.
> + *
> + * If there are more vectors than CPUs we will still only have one bit
> + * set per CPU, but interrupt code will keep on assining the vectors from
> + * the start of the bitmap until we run out of vectors.
> + */
Another little typo above in "assining".
I take this opportunity to ask you something, since I'm working in a
related code in a specific driver - sorry in advance if my question is
silly or if I misunderstood your code.
The function irq_create_affinity_mask() below deals with the case in
which we have nr_vecs < num_online_cpus(); in this case, wouldn't be a
good idea to trying distribute the vecs among cores?
Example: if we have 128 online cpus, 8 per core (meaning 16 cores) and
64 vecs, I guess would be ideal to distribute 4 vecs _per core_, leaving
4 CPUs in each core without vecs.
Makes sense for you?
Thanks,
Guilherme
> +int irq_create_affinity_mask(struct cpumask **affinity_mask,
> + unsigned int *nr_vecs)
> +{
> + unsigned int vecs = 0;
> +
> + if (*nr_vecs == 1) {
> + *affinity_mask = NULL;
> + return 0;
> + }
> +
> + *affinity_mask = kzalloc(cpumask_size(), GFP_KERNEL);
> + if (!*affinity_mask)
> + return -ENOMEM;
> +
> + if (*nr_vecs >= num_online_cpus()) {
> + cpumask_copy(*affinity_mask, cpu_online_mask);
> + } else {
> + unsigned int cpu;
> +
> + for_each_online_cpu(cpu) {
> + if (cpu == get_first_sibling(cpu)) {
> + cpumask_set_cpu(cpu, *affinity_mask);
> + vecs++;
> + }
> +
> + if (--(*nr_vecs) == 0)
> + break;
> + }
> + }
> +
> + *nr_vecs = vecs;
> + return 0;
> +}
>
[toc] | [prev] | [next] | [standalone]
| From | Christoph Hellwig <hch@lst.de> |
|---|---|
| Date | 2016-06-15 12:20 +0200 |
| Subject | Re: [PATCH 06/13] irq: add a helper spread an affinity mask for MSI/MSI-X vectors |
| Message-ID | <rKg5r-1fI-17@gated-at.bofh.it> |
| In reply to | #1422371 |
On Tue, Jun 14, 2016 at 06:54:22PM -0300, Guilherme G. Piccoli wrote: > On 06/14/2016 04:58 PM, Christoph Hellwig wrote: >> This is lifted from the blk-mq code and adopted to use the affinity mask >> concept just intruced in the irq handling code. > > Very nice patch Christoph, thanks. There's a little typo above, on > "intruced". fixed. > Another little typo above in "assining". fixed a swell. > I take this opportunity to ask you something, since I'm working in a > related code in a specific driver Which driver? One of the points here is to get this sort of code out of drivers and into common code.. > - sorry in advance if my question is > silly or if I misunderstood your code. > > The function irq_create_affinity_mask() below deals with the case in which > we have nr_vecs < num_online_cpus(); in this case, wouldn't be a good idea > to trying distribute the vecs among cores? > > Example: if we have 128 online cpus, 8 per core (meaning 16 cores) and 64 > vecs, I guess would be ideal to distribute 4 vecs _per core_, leaving 4 > CPUs in each core without vecs. There have been some reports about the blk-mq IRQ distribution being suboptimal, but no one sent patches so far. This patch just moves the existing algorithm into the core code to be better bisectable. I think an algorithm that takes cores into account instead of just SMT sibling would be very useful. So if you have a case where this helps for you an incremental patch (or even one against the current blk-mq code for now) would be appreciated.
[toc] | [prev] | [next] | [standalone]
| From | "Guilherme G. Piccoli" <gpiccoli@linux.vnet.ibm.com> |
|---|---|
| Date | 2016-06-15 15:10 +0200 |
| Subject | Re: [PATCH 06/13] irq: add a helper spread an affinity mask for MSI/MSI-X vectors |
| Message-ID | <rKiJY-2Ya-33@gated-at.bofh.it> |
| In reply to | #1422866 |
Thanks for the responses Bart and Christoph. On 06/15/2016 07:10 AM, Christoph Hellwig wrote: > On Tue, Jun 14, 2016 at 06:54:22PM -0300, Guilherme G. Piccoli wrote: >> On 06/14/2016 04:58 PM, Christoph Hellwig wrote: >>> This is lifted from the blk-mq code and adopted to use the affinity mask >>> concept just intruced in the irq handling code. >> >> Very nice patch Christoph, thanks. There's a little typo above, on >> "intruced". > > fixed. > >> Another little typo above in "assining". > > fixed a swell. > >> I take this opportunity to ask you something, since I'm working in a >> related code in a specific driver > > Which driver? One of the points here is to get this sort of code out > of drivers and into common code.. A network driver, i40e. I'd be glad to implement/see some common code to raise the topology information I need, but I was implementing on i40e more as a test case/toy example heheh... >> - sorry in advance if my question is >> silly or if I misunderstood your code. >> >> The function irq_create_affinity_mask() below deals with the case in which >> we have nr_vecs < num_online_cpus(); in this case, wouldn't be a good idea >> to trying distribute the vecs among cores? >> >> Example: if we have 128 online cpus, 8 per core (meaning 16 cores) and 64 >> vecs, I guess would be ideal to distribute 4 vecs _per core_, leaving 4 >> CPUs in each core without vecs. > > There have been some reports about the blk-mq IRQ distribution being > suboptimal, but no one sent patches so far. This patch just moves the > existing algorithm into the core code to be better bisectable. > > I think an algorithm that takes cores into account instead of just SMT > sibling would be very useful. So if you have a case where this helps > for you an incremental patch (or even one against the current blk-mq > code for now) would be appreciated. ...but now I'll focus on the common/general case! Thanks for the suggestion Christoph. I guess would be even better to have a generic function that retrieves an optimal mask, something like topology_get_optimal_mask(n, *cpumask), in which we get the best distribution of n CPUs among all cores and return such a mask - interesting case is when n < num_online_cpus. So, this function could be used inside your irq_create_affinity_mask() and maybe in other places it is needed. I was planning to use topology_core_id() to retrieve the core of a CPU, if anybody has a better idea, I'd be glad to hear it. Cheers, Guilherme > > _______________________________________________ > Linux-nvme mailing list > Linux-nvme@lists.infradead.org > http://lists.infradead.org/mailman/listinfo/linux-nvme >
[toc] | [prev] | [next] | [standalone]
| From | Christoph Hellwig <hch@lst.de> |
|---|---|
| Date | 2016-06-16 17:20 +0200 |
| Subject | Re: [PATCH 06/13] irq: add a helper spread an affinity mask for MSI/MSI-X vectors |
| Message-ID | <rKHfk-1xv-17@gated-at.bofh.it> |
| In reply to | #1423001 |
> > ...but now I'll focus on the common/general case! Thanks for the suggestion > Christoph. I guess would be even better to have a generic function that > retrieves an optimal mask, something like topology_get_optimal_mask(n, > *cpumask), in which we get the best distribution of n CPUs among all cores > and return such a mask - interesting case is when n < num_online_cpus. So, > this function could be used inside your irq_create_affinity_mask() and > maybe in other places it is needed. Yes, we should probably just plug this in where we're using the current routines. Block very much optimizes for the cases of either 1 queue or enough queues for all cpus at the moments. It would be good to check what the network drivers currently do.
[toc] | [prev] | [next] | [standalone]
| From | Alexander Gordeev <agordeev@redhat.com> |
|---|---|
| Date | 2016-06-25 22:10 +0200 |
| Subject | Re: [PATCH 06/13] irq: add a helper spread an affinity mask for MSI/MSI-X vectors |
| Message-ID | <rO23U-1rq-35@gated-at.bofh.it> |
| In reply to | #1422266 |
On Tue, Jun 14, 2016 at 09:58:59PM +0200, Christoph Hellwig wrote:
> This is lifted from the blk-mq code and adopted to use the affinity mask
> concept just intruced in the irq handling code.
>
> Signed-off-by: Christoph Hellwig <hch@lst.de>
> ---
> include/linux/interrupt.h | 11 +++++++++
> kernel/irq/Makefile | 1 +
> kernel/irq/affinity.c | 60 +++++++++++++++++++++++++++++++++++++++++++++++
> 3 files changed, 72 insertions(+)
> create mode 100644 kernel/irq/affinity.c
>
> diff --git a/include/linux/interrupt.h b/include/linux/interrupt.h
> index 9fcabeb..12003c0 100644
> --- a/include/linux/interrupt.h
> +++ b/include/linux/interrupt.h
> @@ -278,6 +278,9 @@ extern int irq_set_affinity_hint(unsigned int irq, const struct cpumask *m);
> extern int
> irq_set_affinity_notifier(unsigned int irq, struct irq_affinity_notify *notify);
>
> +int irq_create_affinity_mask(struct cpumask **affinity_mask,
> + unsigned int *nr_vecs);
> +
> #else /* CONFIG_SMP */
>
> static inline int irq_set_affinity(unsigned int irq, const struct cpumask *m)
> @@ -308,6 +311,14 @@ irq_set_affinity_notifier(unsigned int irq, struct irq_affinity_notify *notify)
> {
> return 0;
> }
> +
> +static inline int irq_create_affinity_mask(struct cpumask **affinity_mask,
> + unsigned int *nr_vecs)
> +{
> + *affinity_mask = NULL;
> + *nr_vecs = 1;
> + return 0;
> +}
> #endif /* CONFIG_SMP */
>
> /*
> diff --git a/kernel/irq/Makefile b/kernel/irq/Makefile
> index 2ee42e9..1d3ee31 100644
> --- a/kernel/irq/Makefile
> +++ b/kernel/irq/Makefile
> @@ -9,3 +9,4 @@ obj-$(CONFIG_GENERIC_IRQ_MIGRATION) += cpuhotplug.o
> obj-$(CONFIG_PM_SLEEP) += pm.o
> obj-$(CONFIG_GENERIC_MSI_IRQ) += msi.o
> obj-$(CONFIG_GENERIC_IRQ_IPI) += ipi.o
> +obj-$(CONFIG_SMP) += affinity.o
> diff --git a/kernel/irq/affinity.c b/kernel/irq/affinity.c
> new file mode 100644
> index 0000000..1daf8fb
> --- /dev/null
> +++ b/kernel/irq/affinity.c
> @@ -0,0 +1,60 @@
> +
> +#include <linux/interrupt.h>
> +#include <linux/kernel.h>
> +#include <linux/slab.h>
> +#include <linux/cpu.h>
> +
> +static int get_first_sibling(unsigned int cpu)
> +{
> + unsigned int ret;
> +
> + ret = cpumask_first(topology_sibling_cpumask(cpu));
> + if (ret < nr_cpu_ids)
> + return ret;
> + return cpu;
> +}
> +
> +/*
> + * Take a map of online CPUs and the number of available interrupt vectors
> + * and generate an output cpumask suitable for spreading MSI/MSI-X vectors
> + * so that they are distributed as good as possible around the CPUs. If
> + * more vectors than CPUs are available we'll map one to each CPU,
Unless I do not misinterpret a loop from msix_setup_entries() (patch 08/13),
the above is incorrect:
for (i = 0; i < nvec; i++) {
if (dev->irq_affinity) {
cpu = cpumask_next(cpu, dev->irq_affinity);
if (cpu >= nr_cpu_ids)
cpu = cpumask_first(dev->irq_affinity);
mask = cpumask_of(cpu);
}
...
entry->affinity = mask;
}
> + * otherwise we map one to the first sibling of each socket.
(*) I guess, in some topology configurations a total number of all
first siblings may be less than the number of vectors.
> + * If there are more vectors than CPUs we will still only have one bit
> + * set per CPU, but interrupt code will keep on assining the vectors from
> + * the start of the bitmap until we run out of vectors.
> + */
> +int irq_create_affinity_mask(struct cpumask **affinity_mask,
> + unsigned int *nr_vecs)
Both the callers of this function and the function itself IMHO would
read better if it simply returned the affinity mask. Or passed the
affinity mask pointer.
> +{
> + unsigned int vecs = 0;
In case (*nr_vecs >= num_online_cpus()) the contents of *nr_vecs
will be overwritten with 0.
> + if (*nr_vecs == 1) {
> + *affinity_mask = NULL;
> + return 0;
> + }
> +
> + *affinity_mask = kzalloc(cpumask_size(), GFP_KERNEL);
> + if (!*affinity_mask)
> + return -ENOMEM;
> +
> + if (*nr_vecs >= num_online_cpus()) {
> + cpumask_copy(*affinity_mask, cpu_online_mask);
> + } else {
> + unsigned int cpu;
> +
> + for_each_online_cpu(cpu) {
> + if (cpu == get_first_sibling(cpu)) {
> + cpumask_set_cpu(cpu, *affinity_mask);
> + vecs++;
> + }
> +
> + if (--(*nr_vecs) == 0)
> + break;
> + }
> + }
> +
> + *nr_vecs = vecs;
So considering (*) comment above the number of available vectors
might be unnecessarily shrunken here.
I think nr_vecs need not be an out-parameter since we always can
assign multiple vectors to a CPU. It is better than limiting number
of available vectors AFAIKT. Or you could pass one-per-cpu flag
explicitly.
> + return 0;
> +}
> --
> 2.1.4
>
[toc] | [prev] | [next] | [standalone]
| From | Christoph Hellwig <hch@lst.de> |
|---|---|
| Date | 2016-06-30 20:10 +0200 |
| Subject | Re: [PATCH 06/13] irq: add a helper spread an affinity mask for MSI/MSI-X vectors |
| Message-ID | <rPOzw-4q2-29@gated-at.bofh.it> |
| In reply to | #1431210 |
On Sat, Jun 25, 2016 at 10:05:19PM +0200, Alexander Gordeev wrote:
> > + * and generate an output cpumask suitable for spreading MSI/MSI-X vectors
> > + * so that they are distributed as good as possible around the CPUs. If
> > + * more vectors than CPUs are available we'll map one to each CPU,
>
> Unless I do not misinterpret a loop from msix_setup_entries() (patch 08/13),
> the above is incorrect:
What part do you think is incorrect?
> > + * otherwise we map one to the first sibling of each socket.
>
> (*) I guess, in some topology configurations a total number of all
> first siblings may be less than the number of vectors.
Yes, in that case we'll assign imcompetely. I've already heard people
complaining about that at LSF/MM, but no one volunteered patches.
I only have devices with 1 or enough vectores to test, so I don't
really dare to touch the algorithm. Either way the algorithm
change should probably be a different patch than refactoring it and
moving it around.
> > + * If there are more vectors than CPUs we will still only have one bit
> > + * set per CPU, but interrupt code will keep on assining the vectors from
> > + * the start of the bitmap until we run out of vectors.
> > + */
> > +int irq_create_affinity_mask(struct cpumask **affinity_mask,
> > + unsigned int *nr_vecs)
>
> Both the callers of this function and the function itself IMHO would
> read better if it simply returned the affinity mask. Or passed the
> affinity mask pointer.
We can't just return the pointer as NULL is a valid and common return
value. If we pass the pointer we'd then also need to allocate one for
the (common) nvec = 1 case.
>
> > +{
> > + unsigned int vecs = 0;
>
> In case (*nr_vecs >= num_online_cpus()) the contents of *nr_vecs
> will be overwritten with 0.
Thanks, fixed.
> So considering (*) comment above the number of available vectors
> might be unnecessarily shrunken here.
>
> I think nr_vecs need not be an out-parameter since we always can
> assign multiple vectors to a CPU. It is better than limiting number
> of available vectors AFAIKT. Or you could pass one-per-cpu flag
> explicitly.
The function is intended to replicate the blk-mq algorithm. I don't
think it's optimal, but I really want to avoid dragging the discussion
about the optimal algorithm into this patchset. We should at least
move to a vector per node/socket model instead of just the siblings,
and be able to use all vectors (at least optionally).
[toc] | [prev] | [next] | [standalone]
| From | Alexander Gordeev <agordeev@redhat.com> |
|---|---|
| Date | 2016-07-01 09:30 +0200 |
| Subject | Re: [PATCH 06/13] irq: add a helper spread an affinity mask for MSI/MSI-X vectors |
| Message-ID | <rQ13I-3J1-19@gated-at.bofh.it> |
| In reply to | #1434785 |
On Thu, Jun 30, 2016 at 07:48:54PM +0200, Christoph Hellwig wrote: > On Sat, Jun 25, 2016 at 10:05:19PM +0200, Alexander Gordeev wrote: > > > + * and generate an output cpumask suitable for spreading MSI/MSI-X vectors > > > + * so that they are distributed as good as possible around the CPUs. If > > > + * more vectors than CPUs are available we'll map one to each CPU, > > > > Unless I do not misinterpret a loop from msix_setup_entries() (patch 08/13), > > the above is incorrect: > > What part do you think is incorrect? With your explanations below and no immediate intention to fix the algorithm it is correct. > > (*) I guess, in some topology configurations a total number of all > > first siblings may be less than the number of vectors. > > Yes, in that case we'll assign imcompetely. I've already heard people > complaining about that at LSF/MM, but no one volunteered patches. > I only have devices with 1 or enough vectores to test, so I don't > really dare to touch the algorithm. Either way the algorithm > change should probably be a different patch than refactoring it and > moving it around. I see your approach now. Thanks!
[toc] | [prev] | [next] | [standalone]
| From | Christoph Hellwig <hch@lst.de> |
|---|---|
| Date | 2016-06-14 22:00 +0200 |
| Subject | [PATCH 07/13] pci: Provide sensible irq vector alloc/free routines |
| Message-ID | <rK2Fc-Rz-23@gated-at.bofh.it> |
| In reply to | #1422263 |
Add a helper to allocate a range of interrupt vectors, which will
transparently use MSI-X and MSI if available or fallback to legacy
vectors. The interrupts are available in a core managed array
in the pci_dev structure, and can also be released using a similar
helper.
The next patch will also add automatic spreading of MSI / MSI-X
vectors to this function.
Signed-off-by: Christoph Hellwig <hch@lst.de>
---
drivers/pci/msi.c | 110 ++++++++++++++++++++++++++++++++++++++++++++++++++++
include/linux/pci.h | 18 +++++++++
2 files changed, 128 insertions(+)
diff --git a/drivers/pci/msi.c b/drivers/pci/msi.c
index a080f44..a33adec 100644
--- a/drivers/pci/msi.c
+++ b/drivers/pci/msi.c
@@ -4,6 +4,7 @@
*
* Copyright (C) 2003-2004 Intel
* Copyright (C) Tom Long Nguyen (tom.l.nguyen@intel.com)
+ * Copyright (c) 2016 Christoph Hellwig.
*/
#include <linux/err.h>
@@ -1120,6 +1121,115 @@ int pci_enable_msix_range(struct pci_dev *dev, struct msix_entry *entries,
}
EXPORT_SYMBOL(pci_enable_msix_range);
+static unsigned int pci_nr_irq_vectors(struct pci_dev *pdev)
+{
+ int nr_entries;
+
+ nr_entries = pci_msix_vec_count(pdev);
+ if (nr_entries <= 0 && pci_msi_supported(pdev, 1))
+ nr_entries = pci_msi_vec_count(pdev);
+ if (nr_entries <= 0)
+ nr_entries = 1;
+ return nr_entries;
+}
+
+static int pci_enable_msix_range_wrapper(struct pci_dev *pdev, u32 *irqs,
+ unsigned int min_vecs, unsigned int max_vecs)
+{
+ struct msix_entry *msix_entries;
+ int vecs, i;
+
+ msix_entries = kcalloc(max_vecs, sizeof(struct msix_entry), GFP_KERNEL);
+ if (!msix_entries)
+ return -ENOMEM;
+
+ for (i = 0; i < max_vecs; i++)
+ msix_entries[i].entry = i;
+
+ vecs = pci_enable_msix_range(pdev, msix_entries, min_vecs, max_vecs);
+ if (vecs > 0) {
+ for (i = 0; i < vecs; i++)
+ irqs[i] = msix_entries[i].vector;
+ }
+
+ kfree(msix_entries);
+ return vecs;
+}
+
+/**
+ * pci_alloc_irq_vectors - allocate multiple IRQs for a device
+ * @dev: PCI device to operate on
+ * @min_vecs: minimum number of vectors required (must be >= 1)
+ * @max_vecs: maximum (desired) number of vectors
+ * @flags: flags or quirks for the allocation
+ *
+ * Allocate up to @max_vecs interrupt vectors for @dev, using MSI-X or MSI
+ * vectors if available, and fall back to a single legacy vector
+ * if neither is available. Return the number of vectors allocated,
+ * (which might be smaller than @max_vecs) if successful, or a negative
+ * error code on error. The Linux irq numbers for the allocated
+ * vectors are stored in pdev->irqs. If less than @min_vecs interrupt
+ * vectors are available for @dev the function will fail with -ENOSPC.
+ */
+int pci_alloc_irq_vectors(struct pci_dev *dev, unsigned int min_vecs,
+ unsigned int max_vecs, unsigned int flags)
+{
+ unsigned int vecs, i;
+ u32 *irqs;
+
+ max_vecs = min(max_vecs, pci_nr_irq_vectors(dev));
+
+ irqs = kcalloc(max_vecs, sizeof(u32), GFP_KERNEL);
+ if (!irqs)
+ return -ENOMEM;
+
+ if (!(flags & PCI_IRQ_NOMSIX)) {
+ vecs = pci_enable_msix_range_wrapper(dev, irqs, min_vecs,
+ max_vecs);
+ if (vecs > 0)
+ goto done;
+ }
+
+ vecs = pci_enable_msi_range(dev, min_vecs, max_vecs);
+ if (vecs > 0) {
+ for (i = 0; i < vecs; i++)
+ irqs[i] = dev->irq + i;
+ goto done;
+ }
+
+ if (min_vecs > 1)
+ return -ENOSPC;
+
+ /* use legacy irq */
+ kfree(irqs);
+ dev->irqs = &dev->irq;
+ return 1;
+
+done:
+ dev->irqs = irqs;
+ return vecs;
+}
+EXPORT_SYMBOL(pci_alloc_irq_vectors);
+
+/**
+ * pci_free_irq_vectors - free previously allocated IRQs for a device
+ * @dev: PCI device to operate on
+ *
+ * Undoes the allocations and enabling in pci_alloc_irq_vectors().
+ */
+void pci_free_irq_vectors(struct pci_dev *dev)
+{
+ if (dev->msix_enabled)
+ pci_disable_msix(dev);
+ else if (dev->msi_enabled)
+ pci_disable_msi(dev);
+
+ if (dev->irqs != &dev->irq)
+ kfree(dev->irqs);
+}
+EXPORT_SYMBOL(pci_free_irq_vectors);
+
+
struct pci_dev *msi_desc_to_pci_dev(struct msi_desc *desc)
{
return to_pci_dev(desc->dev);
diff --git a/include/linux/pci.h b/include/linux/pci.h
index b67e4df..84a20fc 100644
--- a/include/linux/pci.h
+++ b/include/linux/pci.h
@@ -320,6 +320,7 @@ struct pci_dev {
* directly, use the values stored here. They might be different!
*/
unsigned int irq;
+ unsigned int *irqs;
struct resource resource[DEVICE_COUNT_RESOURCE]; /* I/O and memory regions + expansion ROMs */
bool match_driver; /* Skip attaching driver */
@@ -1237,6 +1238,8 @@ resource_size_t pcibios_iov_resource_alignment(struct pci_dev *dev, int resno);
int pci_set_vga_state(struct pci_dev *pdev, bool decode,
unsigned int command_bits, u32 flags);
+#define PCI_IRQ_NOMSIX (1 << 0) /* don't try to use MSI-X interrupts */
+
/* kmem_cache style wrapper around pci_alloc_consistent() */
#include <linux/pci-dma.h>
@@ -1284,6 +1287,9 @@ static inline int pci_enable_msix_exact(struct pci_dev *dev,
return rc;
return 0;
}
+int pci_alloc_irq_vectors(struct pci_dev *dev, unsigned int min_vecs,
+ unsigned int max_vecs, unsigned int flags);
+void pci_free_irq_vectors(struct pci_dev *dev);
#else
static inline int pci_msi_vec_count(struct pci_dev *dev) { return -ENOSYS; }
static inline void pci_msi_shutdown(struct pci_dev *dev) { }
@@ -1307,6 +1313,18 @@ static inline int pci_enable_msix_range(struct pci_dev *dev,
static inline int pci_enable_msix_exact(struct pci_dev *dev,
struct msix_entry *entries, int nvec)
{ return -ENOSYS; }
+static inline int pci_alloc_irq_vectors(struct pci_dev *dev,
+ unsigned int min_vecs, unsigned int max_vecs,
+ unsigned int flags)
+{
+ if (min_vecs > 1)
+ return -ENOSPC;
+ dev->irqs = &dev->irq;
+ return 1;
+}
+static inline void pci_free_irq_vectors(struct pci_dev *dev)
+{
+}
#endif
#ifdef CONFIG_PCIEPORTBUS
--
2.1.4
[toc] | [prev] | [next] | [standalone]
| From | Alexander Gordeev <agordeev@redhat.com> |
|---|---|
| Date | 2016-06-23 13:20 +0200 |
| Subject | Re: [PATCH 07/13] pci: Provide sensible irq vector alloc/free routines |
| Message-ID | <rNaPU-IX-11@gated-at.bofh.it> |
| In reply to | #1422267 |
On Tue, Jun 14, 2016 at 09:59:00PM +0200, Christoph Hellwig wrote:
> Add a helper to allocate a range of interrupt vectors, which will
> transparently use MSI-X and MSI if available or fallback to legacy
> vectors. The interrupts are available in a core managed array
> in the pci_dev structure, and can also be released using a similar
> helper.
>
> The next patch will also add automatic spreading of MSI / MSI-X
> vectors to this function.
>
> Signed-off-by: Christoph Hellwig <hch@lst.de>
> ---
> drivers/pci/msi.c | 110 ++++++++++++++++++++++++++++++++++++++++++++++++++++
> include/linux/pci.h | 18 +++++++++
New APIs should be documented in Documentation/PCI/MSI-HOWTO.txt, I guess.
> 2 files changed, 128 insertions(+)
>
> diff --git a/drivers/pci/msi.c b/drivers/pci/msi.c
> index a080f44..a33adec 100644
> --- a/drivers/pci/msi.c
> +++ b/drivers/pci/msi.c
> @@ -4,6 +4,7 @@
> *
> * Copyright (C) 2003-2004 Intel
> * Copyright (C) Tom Long Nguyen (tom.l.nguyen@intel.com)
> + * Copyright (c) 2016 Christoph Hellwig.
> */
>
> #include <linux/err.h>
> @@ -1120,6 +1121,115 @@ int pci_enable_msix_range(struct pci_dev *dev, struct msix_entry *entries,
> }
> EXPORT_SYMBOL(pci_enable_msix_range);
>
> +static unsigned int pci_nr_irq_vectors(struct pci_dev *pdev)
> +{
> + int nr_entries;
> +
> + nr_entries = pci_msix_vec_count(pdev);
> + if (nr_entries <= 0 && pci_msi_supported(pdev, 1))
> + nr_entries = pci_msi_vec_count(pdev);
> + if (nr_entries <= 0)
> + nr_entries = 1;
> + return nr_entries;
> +}
This function is strange, because it:
(a) does not consider PCI_IRQ_NOMSIX flag;
(b) only calls pci_msi_supported() for MSI case;
(c) calls pci_msi_supported() with just one vector;
(d) might return suboptimal number of vectors (number of MSI-X used
later for MSI or vice versa)
Overall, I would suggest simply return maximum between MSI-X and MSI
numbers and let the rest of the code (i.e the two range functions)
handle a-d.
> +static int pci_enable_msix_range_wrapper(struct pci_dev *pdev, u32 *irqs,
> + unsigned int min_vecs, unsigned int max_vecs)
> +{
> + struct msix_entry *msix_entries;
> + int vecs, i;
> +
> + msix_entries = kcalloc(max_vecs, sizeof(struct msix_entry), GFP_KERNEL);
> + if (!msix_entries)
> + return -ENOMEM;
> +
> + for (i = 0; i < max_vecs; i++)
> + msix_entries[i].entry = i;
> +
> + vecs = pci_enable_msix_range(pdev, msix_entries, min_vecs, max_vecs);
> + if (vecs > 0) {
This condition check is unneeded.
> + for (i = 0; i < vecs; i++)
> + irqs[i] = msix_entries[i].vector;
> + }
> +
> + kfree(msix_entries);
> + return vecs;
> +}
> +
> +/**
> + * pci_alloc_irq_vectors - allocate multiple IRQs for a device
> + * @dev: PCI device to operate on
> + * @min_vecs: minimum number of vectors required (must be >= 1)
> + * @max_vecs: maximum (desired) number of vectors
> + * @flags: flags or quirks for the allocation
> + *
> + * Allocate up to @max_vecs interrupt vectors for @dev, using MSI-X or MSI
> + * vectors if available, and fall back to a single legacy vector
> + * if neither is available. Return the number of vectors allocated,
> + * (which might be smaller than @max_vecs) if successful, or a negative
> + * error code on error. The Linux irq numbers for the allocated
> + * vectors are stored in pdev->irqs. If less than @min_vecs interrupt
> + * vectors are available for @dev the function will fail with -ENOSPC.
> + */
> +int pci_alloc_irq_vectors(struct pci_dev *dev, unsigned int min_vecs,
> + unsigned int max_vecs, unsigned int flags)
> +{
> + unsigned int vecs, i;
> + u32 *irqs;
> +
> + max_vecs = min(max_vecs, pci_nr_irq_vectors(dev));
Optionally, you could move this assignment to pci_nr_irq_vectors() and
simply let it handle number of vectors to request.
> + irqs = kcalloc(max_vecs, sizeof(u32), GFP_KERNEL);
> + if (!irqs)
> + return -ENOMEM;
> +
> + if (!(flags & PCI_IRQ_NOMSIX)) {
> + vecs = pci_enable_msix_range_wrapper(dev, irqs, min_vecs,
> + max_vecs);
> + if (vecs > 0)
> + goto done;
> + }
> +
> + vecs = pci_enable_msi_range(dev, min_vecs, max_vecs);
> + if (vecs > 0) {
> + for (i = 0; i < vecs; i++)
> + irqs[i] = dev->irq + i;
> + goto done;
> + }
> +
> + if (min_vecs > 1)
> + return -ENOSPC;
irqs is leaked if (min_vecs > 1)
You can get rid of this check at all if you reorganize your code i.e.
like this:
...
vecs = pci_enable_msi_range(dev, min_vecs, max_vecs);
if (vecs < 0)
goto legacy;
for (i = 0; i < vecs; i++)
irqs[i] = dev->irq + i;
done:
...
legacy:
...
> +
> + /* use legacy irq */
> + kfree(irqs);
> + dev->irqs = &dev->irq;
> + return 1;
> +
> +done:
> + dev->irqs = irqs;
> + return vecs;
> +}
> +EXPORT_SYMBOL(pci_alloc_irq_vectors);
> +
> +/**
> + * pci_free_irq_vectors - free previously allocated IRQs for a device
> + * @dev: PCI device to operate on
> + *
> + * Undoes the allocations and enabling in pci_alloc_irq_vectors().
> + */
> +void pci_free_irq_vectors(struct pci_dev *dev)
> +{
> + if (dev->msix_enabled)
> + pci_disable_msix(dev);
> + else if (dev->msi_enabled)
> + pci_disable_msi(dev);
The checks are probably redundant or incomplete. Redundant - because
pci_disable_msi()/pci_disable_msix() do it anyways:
if (!pci_msi_enable || !dev || !dev->msi_enabled)
return;
Incomplete - because the two other conditions are not checked.
> + if (dev->irqs != &dev->irq)
> + kfree(dev->irqs);
Unset dev->irqs?
BTW, since (dev->irqs == &dev->irq) effectively checks if MSI/MSI-X
was enabled this function could bail out in case they did not.
> +}
> +EXPORT_SYMBOL(pci_free_irq_vectors);
> +
> +
> struct pci_dev *msi_desc_to_pci_dev(struct msi_desc *desc)
> {
> return to_pci_dev(desc->dev);
> diff --git a/include/linux/pci.h b/include/linux/pci.h
> index b67e4df..84a20fc 100644
> --- a/include/linux/pci.h
> +++ b/include/linux/pci.h
> @@ -320,6 +320,7 @@ struct pci_dev {
> * directly, use the values stored here. They might be different!
> */
> unsigned int irq;
> + unsigned int *irqs;
> struct resource resource[DEVICE_COUNT_RESOURCE]; /* I/O and memory regions + expansion ROMs */
>
> bool match_driver; /* Skip attaching driver */
> @@ -1237,6 +1238,8 @@ resource_size_t pcibios_iov_resource_alignment(struct pci_dev *dev, int resno);
> int pci_set_vga_state(struct pci_dev *pdev, bool decode,
> unsigned int command_bits, u32 flags);
>
> +#define PCI_IRQ_NOMSIX (1 << 0) /* don't try to use MSI-X interrupts */
BTW, why PCI_IRQ_NOMSIX only and no PCI_IRQ_NOMSI?
> /* kmem_cache style wrapper around pci_alloc_consistent() */
>
> #include <linux/pci-dma.h>
> @@ -1284,6 +1287,9 @@ static inline int pci_enable_msix_exact(struct pci_dev *dev,
> return rc;
> return 0;
> }
> +int pci_alloc_irq_vectors(struct pci_dev *dev, unsigned int min_vecs,
> + unsigned int max_vecs, unsigned int flags);
> +void pci_free_irq_vectors(struct pci_dev *dev);
> #else
> static inline int pci_msi_vec_count(struct pci_dev *dev) { return -ENOSYS; }
> static inline void pci_msi_shutdown(struct pci_dev *dev) { }
> @@ -1307,6 +1313,18 @@ static inline int pci_enable_msix_range(struct pci_dev *dev,
> static inline int pci_enable_msix_exact(struct pci_dev *dev,
> struct msix_entry *entries, int nvec)
> { return -ENOSYS; }
> +static inline int pci_alloc_irq_vectors(struct pci_dev *dev,
> + unsigned int min_vecs, unsigned int max_vecs,
> + unsigned int flags)
> +{
> + if (min_vecs > 1)
> + return -ENOSPC;
> + dev->irqs = &dev->irq;
> + return 1;
> +}
> +static inline void pci_free_irq_vectors(struct pci_dev *dev)
> +{
Unset dev->irqs?
> +}
> #endif
>
> #ifdef CONFIG_PCIEPORTBUS
> --
> 2.1.4
>
[toc] | [prev] | [next] | [standalone]
| From | Christoph Hellwig <hch@lst.de> |
|---|---|
| Date | 2016-06-30 19:00 +0200 |
| Subject | Re: [PATCH 07/13] pci: Provide sensible irq vector alloc/free routines |
| Message-ID | <rPNtM-3yl-7@gated-at.bofh.it> |
| In reply to | #1429682 |
On Thu, Jun 23, 2016 at 01:16:10PM +0200, Alexander Gordeev wrote:
> New APIs should be documented in Documentation/PCI/MSI-HOWTO.txt, I guess.
Ok, done.
> > +static unsigned int pci_nr_irq_vectors(struct pci_dev *pdev)
> > +{
> > + int nr_entries;
> > +
> > + nr_entries = pci_msix_vec_count(pdev);
> > + if (nr_entries <= 0 && pci_msi_supported(pdev, 1))
> > + nr_entries = pci_msi_vec_count(pdev);
> > + if (nr_entries <= 0)
> > + nr_entries = 1;
> > + return nr_entries;
> > +}
>
> This function is strange, because it:
> (a) does not consider PCI_IRQ_NOMSIX flag;
> (b) only calls pci_msi_supported() for MSI case;
> (c) calls pci_msi_supported() with just one vector;
> (d) might return suboptimal number of vectors (number of MSI-X used
> later for MSI or vice versa)
>
> Overall, I would suggest simply return maximum between MSI-X and MSI
> numbers and let the rest of the code (i.e the two range functions)
> handle a-d.
Ok, fixed except for (c) - the only thing pci_msi_supported does with
nvec is to check for it being less than 1, which we don't care about,
and which really shouldn't be in this function to start with.
> > + struct msix_entry *msix_entries;
> > + int vecs, i;
> > +
> > + msix_entries = kcalloc(max_vecs, sizeof(struct msix_entry), GFP_KERNEL);
> > + if (!msix_entries)
> > + return -ENOMEM;
> > +
> > + for (i = 0; i < max_vecs; i++)
> > + msix_entries[i].entry = i;
> > +
> > + vecs = pci_enable_msix_range(pdev, msix_entries, min_vecs, max_vecs);
> > + if (vecs > 0) {
>
> This condition check is unneeded.
Why? We could get -ENOSPC back. Oh, because our for loop will
terminate immediately. I can update it, but I think removing it
is less readable than keeping it around.
> > + if (!(flags & PCI_IRQ_NOMSIX)) {
> > + vecs = pci_enable_msix_range_wrapper(dev, irqs, min_vecs,
> > + max_vecs);
> > + if (vecs > 0)
> > + goto done;
> > + }
> > +
> > + vecs = pci_enable_msi_range(dev, min_vecs, max_vecs);
> > + if (vecs > 0) {
> > + for (i = 0; i < vecs; i++)
> > + irqs[i] = dev->irq + i;
> > + goto done;
> > + }
> > +
> > + if (min_vecs > 1)
> > + return -ENOSPC;
>
> irqs is leaked if (min_vecs > 1)
>
> You can get rid of this check at all if you reorganize your code i.e.
> like this:
>
> ...
>
> vecs = pci_enable_msi_range(dev, min_vecs, max_vecs);
> if (vecs < 0)
> goto legacy;
>
> for (i = 0; i < vecs; i++)
> irqs[i] = dev->irq + i;
>
> done:
> ...
>
>
> legacy:
> ...
I've just moved the if below the kfree.
> > +void pci_free_irq_vectors(struct pci_dev *dev)
> > +{
> > + if (dev->msix_enabled)
> > + pci_disable_msix(dev);
> > + else if (dev->msi_enabled)
> > + pci_disable_msi(dev);
>
> The checks are probably redundant or incomplete. Redundant - because
> pci_disable_msi()/pci_disable_msix() do it anyways:
>
> if (!pci_msi_enable || !dev || !dev->msi_enabled)
> return;
>
> Incomplete - because the two other conditions are not checked.
Ok, I've dropped the check.
>
> > + if (dev->irqs != &dev->irq)
> > + kfree(dev->irqs);
>
> Unset dev->irqs?
Fine with me, added.
> > +#define PCI_IRQ_NOMSIX (1 << 0) /* don't try to use MSI-X interrupts */
>
> BTW, why PCI_IRQ_NOMSIX only and no PCI_IRQ_NOMSI?
Because there is no need to call this API if your device only supports
a single legacy vector anyway.
> > + if (min_vecs > 1)
> > + return -ENOSPC;
> > + dev->irqs = &dev->irq;
> > + return 1;
> > +}
> > +static inline void pci_free_irq_vectors(struct pci_dev *dev)
> > +{
>
> Unset dev->irqs?
Ok.
[toc] | [prev] | [next] | [standalone]
| From | Alexander Gordeev <agordeev@redhat.com> |
|---|---|
| Date | 2016-06-30 19:30 +0200 |
| Subject | Re: [PATCH 07/13] pci: Provide sensible irq vector alloc/free routines |
| Message-ID | <rPNWN-3XH-25@gated-at.bofh.it> |
| In reply to | #1434742 |
On Thu, Jun 30, 2016 at 06:54:17PM +0200, Christoph Hellwig wrote:
> > > +static unsigned int pci_nr_irq_vectors(struct pci_dev *pdev)
> > > +{
> > > + int nr_entries;
> > > +
> > > + nr_entries = pci_msix_vec_count(pdev);
> > > + if (nr_entries <= 0 && pci_msi_supported(pdev, 1))
> > > + nr_entries = pci_msi_vec_count(pdev);
> > > + if (nr_entries <= 0)
> > > + nr_entries = 1;
> > > + return nr_entries;
> > > +}
> >
> > This function is strange, because it:
> > (a) does not consider PCI_IRQ_NOMSIX flag;
> > (b) only calls pci_msi_supported() for MSI case;
> > (c) calls pci_msi_supported() with just one vector;
> > (d) might return suboptimal number of vectors (number of MSI-X used
> > later for MSI or vice versa)
> >
> > Overall, I would suggest simply return maximum between MSI-X and MSI
> > numbers and let the rest of the code (i.e the two range functions)
> > handle a-d.
>
> Ok, fixed except for (c) - the only thing pci_msi_supported does with
> nvec is to check for it being less than 1, which we don't care about,
> and which really shouldn't be in this function to start with.
Yes, but we should not rely on our knowledge of pci_msi_supported()
internals, aren't we? We need to follow the API which asks nvecs for
whatever reason. Anyway, if you return maximum of the two it does not
matter.
> > > + struct msix_entry *msix_entries;
> > > + int vecs, i;
> > > +
> > > + msix_entries = kcalloc(max_vecs, sizeof(struct msix_entry), GFP_KERNEL);
> > > + if (!msix_entries)
> > > + return -ENOMEM;
> > > +
> > > + for (i = 0; i < max_vecs; i++)
> > > + msix_entries[i].entry = i;
> > > +
> > > + vecs = pci_enable_msix_range(pdev, msix_entries, min_vecs, max_vecs);
> > > + if (vecs > 0) {
> >
> > This condition check is unneeded.
>
> Why? We could get -ENOSPC back. Oh, because our for loop will
> terminate immediately. I can update it, but I think removing it
> is less readable than keeping it around.
Yes, I think you are right.
> > > + if (!(flags & PCI_IRQ_NOMSIX)) {
> > > + vecs = pci_enable_msix_range_wrapper(dev, irqs, min_vecs,
> > > + max_vecs);
> > > + if (vecs > 0)
> > > + goto done;
> > > + }
> > > +
> > > + vecs = pci_enable_msi_range(dev, min_vecs, max_vecs);
> > > + if (vecs > 0) {
> > > + for (i = 0; i < vecs; i++)
> > > + irqs[i] = dev->irq + i;
> > > + goto done;
> > > + }
> > > +
> > > + if (min_vecs > 1)
> > > + return -ENOSPC;
> >
> > irqs is leaked if (min_vecs > 1)
> >
> > You can get rid of this check at all if you reorganize your code i.e.
> > like this:
> >
> > ...
> >
> > vecs = pci_enable_msi_range(dev, min_vecs, max_vecs);
> > if (vecs < 0)
> > goto legacy;
> >
> > for (i = 0; i < vecs; i++)
> > irqs[i] = dev->irq + i;
> >
> > done:
> > ...
> >
> >
> > legacy:
> > ...
>
> I've just moved the if below the kfree.
I think I need to look at the updated version :)
> > > +#define PCI_IRQ_NOMSIX (1 << 0) /* don't try to use MSI-X interrupts */
> >
> > BTW, why PCI_IRQ_NOMSIX only and no PCI_IRQ_NOMSI?
>
> Because there is no need to call this API if your device only supports
> a single legacy vector anyway.
What if a device reports (up to 32) MSIs and MSI-X allocation failed? The
driver might prefer the legacy single (i.e. due to errata in MSI), but
there is no flag to ask for it.
[toc] | [prev] | [next] | [standalone]
| From | Christoph Hellwig <hch@lst.de> |
|---|---|
| Date | 2016-06-30 19:40 +0200 |
| Subject | Re: [PATCH 07/13] pci: Provide sensible irq vector alloc/free routines |
| Message-ID | <rPO6u-40S-1@gated-at.bofh.it> |
| In reply to | #1434750 |
On Thu, Jun 30, 2016 at 07:28:09PM +0200, Alexander Gordeev wrote: > I think I need to look at the updated version :) I've astarted processing the comments for the whole series and plan to post an update tomorrow. > > > > +#define PCI_IRQ_NOMSIX (1 << 0) /* don't try to use MSI-X interrupts */ > > > > > > BTW, why PCI_IRQ_NOMSIX only and no PCI_IRQ_NOMSI? > > > > Because there is no need to call this API if your device only supports > > a single legacy vector anyway. > > What if a device reports (up to 32) MSIs and MSI-X allocation failed? The > driver might prefer the legacy single (i.e. due to errata in MSI), but > there is no flag to ask for it. Ok, I'll add it for now.
[toc] | [prev] | [next] | [standalone]
| From | Christoph Hellwig <hch@lst.de> |
|---|---|
| Date | 2016-06-14 22:00 +0200 |
| Subject | [PATCH 02/13] irq: Introduce IRQD_AFFINITY_MANAGED flag |
| Message-ID | <rK2Fc-Rz-13@gated-at.bofh.it> |
| In reply to | #1422263 |
From: Thomas Gleixner <tglx@linutronix.de>
Interupts marked with this flag are excluded from user space interrupt
affinity changes. Contrary to the IRQ_NO_BALANCING flag, the kernel internal
affinity mechanism is not blocked.
This flag will be used for multi-queue device interrupts.
Signed-off-by: Thomas Gleixner <tglx@linutronix.de>
---
include/linux/irq.h | 7 +++++++
kernel/irq/internals.h | 2 ++
kernel/irq/manage.c | 21 ++++++++++++++++++---
kernel/irq/proc.c | 2 +-
4 files changed, 28 insertions(+), 4 deletions(-)
diff --git a/include/linux/irq.h b/include/linux/irq.h
index 4d758a7..49d66d1 100644
--- a/include/linux/irq.h
+++ b/include/linux/irq.h
@@ -197,6 +197,7 @@ struct irq_data {
* IRQD_IRQ_INPROGRESS - In progress state of the interrupt
* IRQD_WAKEUP_ARMED - Wakeup mode armed
* IRQD_FORWARDED_TO_VCPU - The interrupt is forwarded to a VCPU
+ * IRQD_AFFINITY_MANAGED - Affinity is managed automatically
*/
enum {
IRQD_TRIGGER_MASK = 0xf,
@@ -212,6 +213,7 @@ enum {
IRQD_IRQ_INPROGRESS = (1 << 18),
IRQD_WAKEUP_ARMED = (1 << 19),
IRQD_FORWARDED_TO_VCPU = (1 << 20),
+ IRQD_AFFINITY_MANAGED = (1 << 21),
};
#define __irqd_to_state(d) ACCESS_PRIVATE((d)->common, state_use_accessors)
@@ -305,6 +307,11 @@ static inline void irqd_clr_forwarded_to_vcpu(struct irq_data *d)
__irqd_to_state(d) &= ~IRQD_FORWARDED_TO_VCPU;
}
+static inline bool irqd_affinity_is_managed(struct irq_data *d)
+{
+ return __irqd_to_state(d) & IRQD_AFFINITY_MANAGED;
+}
+
#undef __irqd_to_state
static inline irq_hw_number_t irqd_to_hwirq(struct irq_data *d)
diff --git a/kernel/irq/internals.h b/kernel/irq/internals.h
index 09be2c9..b15aa3b 100644
--- a/kernel/irq/internals.h
+++ b/kernel/irq/internals.h
@@ -105,6 +105,8 @@ static inline void unregister_handler_proc(unsigned int irq,
struct irqaction *action) { }
#endif
+extern bool irq_can_set_affinity_usr(unsigned int irq);
+
extern int irq_select_affinity_usr(unsigned int irq, struct cpumask *mask);
extern void irq_set_thread_affinity(struct irq_desc *desc);
diff --git a/kernel/irq/manage.c b/kernel/irq/manage.c
index ef0bc02..30658e9 100644
--- a/kernel/irq/manage.c
+++ b/kernel/irq/manage.c
@@ -115,12 +115,12 @@ EXPORT_SYMBOL(synchronize_irq);
#ifdef CONFIG_SMP
cpumask_var_t irq_default_affinity;
-static int __irq_can_set_affinity(struct irq_desc *desc)
+static bool __irq_can_set_affinity(struct irq_desc *desc)
{
if (!desc || !irqd_can_balance(&desc->irq_data) ||
!desc->irq_data.chip || !desc->irq_data.chip->irq_set_affinity)
- return 0;
- return 1;
+ return false;
+ return true;
}
/**
@@ -134,6 +134,21 @@ int irq_can_set_affinity(unsigned int irq)
}
/**
+ * irq_can_set_affinity_usr - Check if affinity of a irq can be set from user space
+ * @irq: Interrupt to check
+ *
+ * Like irq_can_set_affinity() above, but additionally checks for the
+ * AFFINITY_MANAGED flag.
+ */
+bool irq_can_set_affinity_usr(unsigned int irq)
+{
+ struct irq_desc *desc = irq_to_desc(irq);
+
+ return __irq_can_set_affinity(desc) &&
+ !irqd_affinity_is_managed(&desc->irq_data);
+}
+
+/**
* irq_set_thread_affinity - Notify irq threads to adjust affinity
* @desc: irq descriptor which has affitnity changed
*
diff --git a/kernel/irq/proc.c b/kernel/irq/proc.c
index 4e1b947..40bdcdc 100644
--- a/kernel/irq/proc.c
+++ b/kernel/irq/proc.c
@@ -96,7 +96,7 @@ static ssize_t write_irq_affinity(int type, struct file *file,
cpumask_var_t new_value;
int err;
- if (!irq_can_set_affinity(irq) || no_irq_affinity)
+ if (!irq_can_set_affinity_usr(irq) || no_irq_affinity)
return -EIO;
if (!alloc_cpumask_var(&new_value, GFP_KERNEL))
--
2.1.4
[toc] | [prev] | [next] | [standalone]
Page 1 of 2 [1] 2 Next page →
Back to top | Article view | linux.kernel
csiph-web