Skip to contentSkip to conversation

Enable hierarchy irqdomian on x86 platforms

PATCH Superseded Jiang Liu 4 participants 38 messages Oct 28, 2014 – Oct 31, 2014

Subsystems (9)
Documentation x86 ACPI PCI +5 more subsystems

v3 of 8 Superseded Latest is v8 →

lore
Share & tools
lore

Jiang Liu

author
#0 lore raw

v3 cover Enable hierarchy irqdomian on x86 platforms

We plan to restructure x86 interrupt code based on hierarchy irqdomain, that is to build irqdomains for CPU vector, interrupt remapping unit, IOAPIC, MSI and HPET etc and organize those irqdomains in hierarchy mode. Each irqdomain manages corresponding interrupt controller and talks to parent interrupt controller through public irqdomain interfaces. We also support stacked irq_chip based on hierarchy irqdomain. It will make the x86 interrupt architecture much more clear and more easy to maintain with hierarchy irqdomain and stacked irq_chip. It may also help ARM interrupt management architecture too.

This is the second patch set to enable support of hierarchy irqdomain on x86 platforms. It depends on the first part at:
https://lkml.org/lkml/2014/10/27/122
And you may access it at:
https://github.com/jiangliu/linux.git irqdomain/p2v3

And there will be a third patch set to convert IOAPIC driver to support hierarchy irqdomain and clean up code.

The first patch extends irqdomain interfaces to support hierarchy irqdomain. Hope this interface could be used by other architectures too, such as ARM/ARM64.
The second patch introduces two helper functions to support stacked irq_chip.
Patch 3-9 implements an irqdomain to manange CPU interrupt vectors, and it's the root irqdomain for x86 platforms.
Patch 10-13 converts Intel and AMD interrupt remapping drivers to support hierarchy irqdomain.
Patch 14-16 converts HPET and MSI to support hierarchy irqdomain. Patch 17-20 cleans up unsued code in x86 arch and interrupt remapping drivers.
Patch 21-24 converts DMAR, HTIRQ and UV to support hierarchy irqdomain.

We have tested this patchset on Intel 32-bit and 64-bit systems. And it also passes Fengguang's 0day tests. But helps are need for testing:
1) AMD interrupt remapping
2) AMD HT_IRQ
3) UV platform

V2->V3:
1) Fix bugs in handling OF irqdomain
2) Add documentation
3) Rebase to v3.18-rc2
V1->V2
1) Add hierarchy iredomain support of DMAR IRQ and UV IRQ.
2) Fix bugs reported by Joe C.
3) Address all review comments from Thomas
4) Fix a bug found during tests
5) Fix errors and warning found by 0day tests

Jiang Liu (24):
irqdomain: Introduce new interfaces to support hierarchy irqdomains
genirq: Introduce helper functions to support stacked irq_chip x86, irq: Save destination CPU ID in irq_cfg
x86, irq: Use hierarchy irqdomain to manage CPU interrupt vectors x86, hpet: Use new irqdomain interfaces to allocate/free IRQ x86, MSI: Use new irqdomain interfaces to allocate/free IRQ x86, uv: Use new irqdomain interfaces to allocate/free IRQ x86, htirq: Use new irqdomain interfaces to allocate/free IRQ x86, dmar: Use new irqdomain interfaces to allocate/free IRQ
x86: irq_remapping: Introduce new interfaces to support hierarchy
irqdomain
iommu/vt-d: Change prototypes to prepare for enabling hierarchy
irqdomain
iommu/vt-d: Enhance Intel IR driver to suppport hierarchy irqdomain iommu/amd: Enhance AMD IR driver to suppport hierarchy irqdomain x86, hpet: Enhance HPET IRQ to support hierarchy irqdomain x86, MSI: Use hierarchy irqdomain to manage MSI interrupts x86, irq: Directly call native_compose_msi_msg() for DMAR IRQ iommu/vt-d: Clean up unused MSI related code
iommu/amd: Clean up unused MSI related code
x86: irq_remapping: Clean up unused MSI related code x86, irq: Clean up unused MSI related code and interfaces iommu/vt-d: Refine the interfaces to create IRQ for DMAR unit x86, irq: Use hierarchy irqdomain to manage DMAR interrupts x86, htirq: Use hierarchy irqdomain to manage Hypertransport
interrupts
x86, uv: Use hierarchy irqdomain to manage UV interrupts

 Documentation/IRQ-domain.txt          |   71 +++++
 arch/ia64/include/asm/irq_remapping.h |    2 -
 arch/ia64/kernel/msi_ia64.c           |   30 +-
 arch/x86/Kconfig                      |    3 +-
 arch/x86/include/asm/hpet.h           |   16 +-
 arch/x86/include/asm/hw_irq.h         |   82 +++++
 arch/x86/include/asm/irq_remapping.h  |   85 ++++-
 arch/x86/include/asm/pci.h            |    5 -
 arch/x86/include/asm/x86_init.h       |    4 -
 arch/x86/kernel/apic/htirq.c          |  178 ++++++++---
 arch/x86/kernel/apic/io_apic.c        |    3 -
 arch/x86/kernel/apic/msi.c            |  545 +++++++++++++++++++++++++--------
 arch/x86/kernel/apic/vector.c         |  160 +++++++++-
 arch/x86/kernel/hpet.c                |   57 +---
 arch/x86/kernel/x86_init.c            |    2 -
 arch/x86/platform/uv/uv_irq.c         |  301 +++++++-----------
 drivers/iommu/amd_iommu.c             |  388 ++++++++++++++++++-----
 drivers/iommu/amd_iommu_init.c        |    4 +
 drivers/iommu/amd_iommu_proto.h       |    9 +
 drivers/iommu/amd_iommu_types.h       |    5 +
 drivers/iommu/dmar.c                  |   19 +-
 drivers/iommu/intel_irq_remapping.c   |  470 +++++++++++++++++++---------
 drivers/iommu/irq_remapping.c         |  227 +++++---------
 drivers/iommu/irq_remapping.h         |   23 +-
 drivers/pci/htirq.c                   |   48 +--
 include/linux/dmar.h                  |    3 +-
 include/linux/htirq.h                 |   22 +-
 include/linux/intel-iommu.h           |    4 +
 include/linux/irq.h                   |   10 +
 include/linux/irqdomain.h             |   81 +++++
 kernel/irq/Kconfig                    |    4 +
 kernel/irq/chip.c                     |   20 ++
 kernel/irq/irqdomain.c                |  373 +++++++++++++++++++++-
 33 files changed, 2319 insertions(+), 935 deletions(-)
Signature · 1 line
1.7.10.4
author
#1 lore raw

PATCH v3 1/24 irqdomain: Introduce new interfaces to support hierarchy irqdomains

We plan to use hierarchy irqdomain to suppport CPU vector assignment, interrupt remapping controller, IO-APIC controller, MSI interrupt and hypertransport interrupt etc on x86 platforms. So extend irqdomain interfaces to support hierarchy irqdomain.

There are already many clients of current irqdomain interfaces. To minimize the changes, we choose to introduce new version 2 interfaces to support hierarchy instead of extending existing irqdomain interfaces.

According to Thomas's suggestion, the most important design decision is to build hierarchy struct irq_data to support hierarchy irqdomain, so hierarchy irqdomain related data could be saved in struct irq_data. With support of hierarchy irq_data, we could also support stacked irq_chips. This is most useful in case of set_affinity().

The new hierarchy irqdomain introduces following interfaces:
1) irq_domain_alloc_irqs()/irq_domain_free_irqs(): allocate/release IRQ and related resources.
2) __irq_domain_alloc_irqs(): a special version to support legacy IRQs.
3) irq_domain_activate_irq()/irq_domain_deactivate_irq(): program interrupt controllers to activate/deactivate interrupt.

There are also several help functions to ease irqdomain implemenations:
1) irq_domain_get_irq_data(): get irq_data associated with a specific irqdomain.
2) irq_domain_set_hwirq_and_chip(): save irqdomain specific data into irq_data.
3) irq_domain_alloc_irqs_parent()/irq_domain_free_irqs_parent(): invoke parent irqdomain's alloc/free callbacks.

We also changed irq_startup()/irq_shutdown() to invoke irq_domain_activate_irq()/irq_domain_deactivate_irq() to program interrupt controller when start/stop interrupts.

---
 Documentation/IRQ-domain.txt |   71 ++++++++
 include/linux/irq.h          |    5 +
 include/linux/irqdomain.h    |   81 +++++++++
 kernel/irq/Kconfig           |    4 +
 kernel/irq/chip.c            |    3 +
 kernel/irq/irqdomain.c       |  373 ++++++++++++++++++++++++++++++++++++++++--
 6 files changed, 521 insertions(+), 16 deletions(-)
Documentation/IRQ-domain.txt +71 -0
index 8a8b82c9ca53..39cfa72732ff 100644
@@ -151,3 +151,74 @@ used and no descriptor gets allocated it is very important to make sure
 that the driver using the simple domain call irq_create_mapping()
 before any irq_find_mapping() since the latter will actually work
 for the static IRQ assignment case.
+
+==== Hierarchy IRQ domain ====
+On some architectures, there may be multiple interrupt controllers
+involved in delivering an interrupt from the device to the target CPU.
+Let's look at a typical interrupt delivering path on x86 platforms:
+
+Device --> IOAPIC -> Interrupt remapping Controller -> Local APIC -> CPU
+
+There are three interrupt controllers involved:
+1) IOAPIC controller
+2) Interrupt remapping controller
+3) Local APIC controller
+
+To support such a hardware topology and make software architecture match
+hardware architecture, an irq_domain data structure is built for each
+interrupt controller and those irq_domains are organized into hierarchy.
+When building irq_domain hierarchy, the irq_domain near to the device is
+child and the irq_domain near to CPU is parent. So a hierarchy structure
+as below will be built for the example above.
+	CPU Vector irq_domain (root irq_domain to manage CPU vectors)
+		^
+		|
+	Interrupt Remapping irq_domain (manage irq_remapping entries)
+		^
+		|
+	IOAPIC irq_domain (manage IOAPIC delivery entries/pins)
+
+There are four major interfaces to use hierarchy irq_domain:
+1) irq_domain_alloc_irqs(): allocate IRQ descriptors and interrupt
+   controller related resources to deliver these interrupts.
+2) irq_domain_free_irqs(): free IRQ descriptors and interrupt controller
+   related resources associated with these interrupts.
+3) irq_domain_activate_irq(): activate interrupt controller hardware to
+   deliver the interrupt.
+3) irq_domain_deactivate_irq(): deactivate interrupt controller hardware
+   to stop delivering the interrupt.
+
+Following changes are needed to support hierarchy irq_domain.
+1) a new field 'parent' is added to struct irq_domain; it's used to
+   maintain irq_domain hierarchy information.
+2) a new field 'parent_data' is added to struct irq_data; it's used to
+   build hierarchy irq_data to match hierarchy irq_domains. The irq_data
+   is used to store irq_domain pointer and hardware irq number.
+3) new callbacks are added to struct irq_domain_ops to support hierarchy
+   irq_domain operations.
+
+With support of hierarchy irq_domain and hierarchy irq_data ready, an
+irq_domain structure is built for each interrupt controller, and an
+irq_data structure is allocated for each irq_domain associated with an
+IRQ. Now we could go one step further to support stacked(hierarchy)
+irq_chip. That is, an irq_chip is associated with each irq_data along
+the hierarchy. A child irq_chip may implement a required action by
+itself or by cooperating with its parent irq_chip.
+
+With stacked irq_chip, interrupt controller driver only needs to deal
+with the hardware managed by itself and may ask for services from its
+parent irq_chip when needed. So we could achieve a much cleaner
+software architecture.
+
+For an interrupt controller driver to support hierarchy irq_domain, it
+needs to:
+1) Implement irq_domain_ops.alloc and irq_domain_ops.free
+2) Optionally implement irq_domain_ops.activate and
+   irq_domain_ops.deactivate.
+3) Optionally implement an irq_chip to manage the interrupt controller
+   hardware.
+4) No need to implement irq_domain_ops.map and irq_domain_ops.unmap,
+   they are unused with hierarchy irq_domain.
+
+Hierarchy irq_domain may also be used to support other architectures,
+such as ARM, ARM64 etc.
include/linux/irq.h +5 -0
index 03f48d936f66..13ba412ce3a0 100644
@@ -133,6 +133,8 @@ struct irq_domain;
  * @chip:		low level interrupt hardware access
  * @domain:		Interrupt translation domain; responsible for mapping
  *			between hwirq number and linux irq number.
+ * @parent_data:	pointer to parent struct irq_data to support hierarchy
+ *			irq_domain
  * @handler_data:	per-IRQ data for the irq_chip methods
  * @chip_data:		platform-specific per-chip private data for the chip
  *			methods, to allow shared chip implementations
@@ -151,6 +153,9 @@ struct irq_data {
 	unsigned int		state_use_accessors;
 	struct irq_chip		*chip;
 	struct irq_domain	*domain;
+#ifdef	CONFIG_IRQ_DOMAIN_HIERARCHY
+	struct irq_data		*parent_data;
+#endif
 	void			*handler_data;
 	void			*chip_data;
 	struct msi_desc		*msi_desc;
include/linux/irqdomain.h +81 -0

129 lines not on this page · raw message

kernel/irq/Kconfig +4 -0
index 225086b2652e..e9b580eccc01 100644
@@ -55,6 +55,10 @@ config GENERIC_IRQ_CHIP
 config IRQ_DOMAIN
 	bool
 
+config IRQ_DOMAIN_HIERARCHY
+	bool
+	depends on IRQ_DOMAIN
+
 config HANDLE_DOMAIN_IRQ
 	bool
 
kernel/irq/chip.c +3 -0
index e5202f00cabc..72a93086216b 100644
@@ -15,6 +15,7 @@
 #include <linux/module.h>
 #include <linux/interrupt.h>
 #include <linux/kernel_stat.h>
+#include <linux/irqdomain.h>
 
 #include <trace/events/irq.h>
 
@@ -178,6 +179,7 @@ int irq_startup(struct irq_desc *desc, bool resend)
 	irq_state_clr_disabled(desc);
 	desc->depth = 0;
 
+	irq_domain_activate_irq(&desc->irq_data);
 	if (desc->irq_data.chip->irq_startup) {
 		ret = desc->irq_data.chip->irq_startup(&desc->irq_data);
 		irq_state_clr_masked(desc);
@@ -199,6 +201,7 @@ void irq_shutdown(struct irq_desc *desc)
 		desc->irq_data.chip->irq_disable(&desc->irq_data);
 	else
 		desc->irq_data.chip->irq_mask(&desc->irq_data);
+	irq_domain_deactivate_irq(&desc->irq_data);
 	irq_state_set_masked(desc);
 }
 
kernel/irq/irqdomain.c +357 -16

436 lines not on this page · raw message

Signature · 1 line
1.7.10.4
#2 lore raw

Re: [Patch Part2 v3 01/24] irqdomain: Introduce new interfaces to support hierarchy irqdomains

Hi Jiang,

On Tue, 2014-10-28 at 16:26 +0800, Jiang Liu wrote:
<deleted...>

Quoted 22 lines
kernel/irq/irqdomain.c +6 -1
@@ -471,7 +469,7 @@ unsigned int irq_create_of_mapping(struct of_phandle_args *irq_data)
 	struct irq_domain *domain;
 	irq_hw_number_t hwirq;
 	unsigned int type = IRQ_TYPE_NONE;
-	unsigned int virq;
+	int virq;
 
 	domain = irq_data->np ? irq_find_host(irq_data->np) : irq_default_domain;
 	if (!domain) {
@@ -480,6 +478,11 @@ unsigned int irq_create_of_mapping(struct of_phandle_args *irq_data)
 		return 0;
 	}
 
+	if (irq_domain_is_hierarchy(domain)) {
+		virq = irq_domain_alloc_irqs(domain, 1, NUMA_NO_NODE, irq_data);
+		return virq <= 0 ? 0 : virq;
+	}
+
 	/* If domain has no translation, then we assume interrupt line */
 	if (domain->ops->xlate == NULL)
 		hwirq = irq_data->args[0];

kernel/irq/irqdomain.c unsigned int irq_find_mapping(struct irq_domain *domain, · line 549

Quoted 9 lines
kernel/irq/irqdomain.c +2 -2
@@ -540,8 +543,8 @@ unsigned int irq_find_mapping(struct irq_domain *domain,
 		return 0;
 
 	if (hwirq < domain->revmap_direct_max_irq) {
-		data = irq_get_irq_data(hwirq);
-		if (data && (data->domain == domain) && (data->hwirq == hwirq))
+		data = irq_domain_get_irq_data(domain, hwirq);
+		if (data && data->hwirq == hwirq)
 			return hwirq;
 	}

Why do you want to move irq_domain_alloc_irqs up there?

On the ARM side, the irqchips will be different on different SoCs, so we will need to implement irq_find_mapping check and set_type in several different location. This leads to code duplication. It is preferred to do these in general code [1], [2]

Is it acceptable if we do irq_find_mapping and set_type here like [3]?

[1]
http://lists.infradead.org/pipermail/linux-arm-kernel/2014-October/294294.html
[2]
http://lists.infradead.org/pipermail/linux-arm-kernel/2014-October/294306.html
[3]
http://lists.infradead.org/pipermail/linux-arm-kernel/2014-October/296543.html

Joe.C

Quoted 343 lines
kernel/irq/irqdomain.c +338 -0
@@ -709,3 +712,341 @@ const struct irq_domain_ops irq_domain_simple_ops = {
 	.xlate = irq_domain_xlate_onetwocell,
 };
 EXPORT_SYMBOL_GPL(irq_domain_simple_ops);
+
+static int irq_domain_alloc_descs(int virq, unsigned int cnt,
+				  irq_hw_number_t hwirq, int node)
+{
+	unsigned int hint;
+
+	if (virq >= 0) {
+		virq = irq_alloc_descs(virq, virq, cnt, node);
+	} 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);
+	}
+
+	return virq;
+}
+
+#ifdef	CONFIG_IRQ_DOMAIN_HIERARCHY
+static void irq_domain_insert_irq(int virq)
+{
+	struct irq_data *data;
+
+	for (data = irq_get_irq_data(virq); data; data = data->parent_data) {
+		struct irq_domain *domain = data->domain;
+		irq_hw_number_t hwirq = data->hwirq;
+
+		if (hwirq < domain->revmap_size) {
+			domain->linear_revmap[hwirq] = virq;
+		} else {
+			mutex_lock(&revmap_trees_mutex);
+			radix_tree_insert(&domain->revmap_tree, hwirq, data);
+			mutex_unlock(&revmap_trees_mutex);
+		}
+
+		/* If not already assigned, give the domain the chip's name */
+		if (!domain->name && data->chip)
+			domain->name = data->chip->name;
+	}
+
+	irq_clear_status_flags(virq, IRQ_NOREQUEST);
+}
+
+static void irq_domain_remove_irq(int virq)
+{
+	struct irq_data *data;
+
+	irq_set_status_flags(virq, IRQ_NOREQUEST);
+	irq_set_chip_and_handler(virq, NULL, NULL);
+	synchronize_irq(virq);
+	smp_mb();
+
+	for (data = irq_get_irq_data(virq); data; data = data->parent_data) {
+		struct irq_domain *domain = data->domain;
+		irq_hw_number_t hwirq = data->hwirq;
+
+		if (hwirq < domain->revmap_size) {
+			domain->linear_revmap[hwirq] = 0;
+		} else {
+			mutex_lock(&revmap_trees_mutex);
+			radix_tree_delete(&domain->revmap_tree, hwirq);
+			mutex_unlock(&revmap_trees_mutex);
+		}
+	}
+}
+
+static struct irq_data *irq_domain_insert_irq_data(struct irq_domain *domain,
+						   struct irq_data *child)
+{
+	struct irq_data *irq_data;
+
+	irq_data = kzalloc_node(sizeof(*irq_data), GFP_KERNEL, child->node);
+	if (irq_data) {
+		child->parent_data = irq_data;
+		irq_data->irq = child->irq;
+		irq_data->node = child->node;
+		irq_data->domain = domain;
+	}
+
+	return irq_data;
+}
+
+static void irq_domain_free_irq_data(unsigned int virq, unsigned int nr_irqs)
+{
+	int i;
+	struct irq_data *irq_data, *tmp;
+
+	for (i = 0; i < nr_irqs; i++) {
+		irq_data = irq_get_irq_data(virq + i);
+		tmp = irq_data->parent_data;
+		irq_data->parent_data = NULL;
+		irq_data->domain = NULL;
+
+		while (tmp) {
+			irq_data = tmp;
+			tmp = tmp->parent_data;
+			kfree(irq_data);
+		}
+	}
+}
+
+static int irq_domain_alloc_irq_data(struct irq_domain *domain,
+				     unsigned int virq, unsigned int nr_irqs)
+{
+	int i;
+	struct irq_data *irq_data;
+	struct irq_domain *parent;
+
+	/* The outermost irq_data is embedded in struct irq_desc */
+	for (i = 0; i < nr_irqs; i++) {
+		irq_data = irq_get_irq_data(virq + i);
+		irq_data->domain = domain;
+
+		for (parent = domain->parent; parent; parent = parent->parent) {
+			irq_data = irq_domain_insert_irq_data(parent, irq_data);
+			if (!irq_data) {
+				irq_domain_free_irq_data(virq, i + 1);
+				return -ENOMEM;
+			}
+		}
+	}
+
+	return 0;
+}
+
+/**
+ * irq_domain_get_irq_data - Get irq_data associated with @virq and @domain
+ * @domain: domain to match
+ * @virq: IRQ number to get irq_data
+ */
+struct irq_data *irq_domain_get_irq_data(struct irq_domain *domain,
+					 unsigned int virq)
+{
+	struct irq_data *irq_data;
+
+	for (irq_data = irq_get_irq_data(virq); irq_data;
+	     irq_data = irq_data->parent_data)
+		if (irq_data->domain == domain)
+			return irq_data;
+
+	return NULL;
+}
+
+int irq_domain_set_hwirq_and_chip(struct irq_domain *domain, unsigned int virq,
+				  irq_hw_number_t hwirq, struct irq_chip *chip,
+				  void *chip_data)
+{
+	struct irq_data *irq_data = irq_domain_get_irq_data(domain, virq);
+
+	if (!irq_data)
+		return -ENOENT;
+
+	irq_data->hwirq = hwirq;
+	irq_data->chip = chip ? chip : &no_irq_chip;
+	irq_data->chip_data = chip_data;
+
+	return 0;
+}
+
+void irq_domain_reset_irq_data(struct irq_data *irq_data)
+{
+	irq_data->hwirq = 0;
+	irq_data->chip = &no_irq_chip;
+	irq_data->chip_data = NULL;
+}
+
+/**
+ * __irq_domain_alloc_irqs - Allocate IRQs from domain
+ * @domain: domain to allocate from
+ * @irq_base: allocate specified IRQ nubmer if irq_base >= 0
+ * @nr_irqs: number of IRQs to allocate
+ * @node: NUMA node id for memory allocation
+ * @arg: domain specific argument
+ * @realloc: IRQ descriptors have already been allocated if true
+ *
+ * Allocate IRQ numbers and initialized all data structures to support
+ * hiearchy IRQ domains.
+ * Parameter @realloc is mainly to support legacy IRQs.
+ * Returns error code or allocated IRQ number
+ *
+ * The whole process to setup an IRQ has been split into two steps.
+ * The first step, __irq_domain_alloc_irqs(), is to allocate IRQ
+ * descriptor and required hardware resources. The second step,
+ * irq_domain_activate_irq(), is to program hardwares with preallocated
+ * resources. In this way, it's easier to rollback when failing to
+ * allocate resources.
+ */
+int __irq_domain_alloc_irqs(struct irq_domain *domain, int irq_base,
+			    unsigned int nr_irqs, int node, void *arg,
+			    bool realloc)
+{
+	int i, ret, virq;
+
+	if (domain == NULL) {
+		domain = irq_default_domain;
+		if (WARN(!domain, "domain is NULL; cannot allocate IRQ\n"))
+			return -EINVAL;
+	}
+
+	if (!domain->ops->alloc) {
+		pr_debug("domain->ops->alloc() is NULL\n");
+		return -ENOSYS;
+	}
+
+	if (realloc && irq_base >= 0) {
+		virq = irq_base;
+	} else {
+		virq = irq_domain_alloc_descs(irq_base, nr_irqs, 0, node);
+		if (virq < 0) {
+			pr_debug("cannot allocate IRQ(base %d, count %d)\n",
+				 irq_base, nr_irqs);
+			return virq;
+		}
+	}
+
+	if (irq_domain_alloc_irq_data(domain, virq, nr_irqs)) {
+		pr_debug("cannot allocate memory for IRQ%d\n", virq);
+		ret = -ENOMEM;
+		goto out_free_desc;
+	}
+
+	mutex_lock(&irq_domain_mutex);
+	ret = domain->ops->alloc(domain, virq, nr_irqs, arg);
+	if (ret < 0) {
+		mutex_unlock(&irq_domain_mutex);
+		goto out_free_irq_data;
+	}
+	for (i = 0; i < nr_irqs; i++)
+		irq_domain_insert_irq(virq + i);
+	mutex_unlock(&irq_domain_mutex);
+
+	return virq;
+
+out_free_irq_data:
+	irq_domain_free_irq_data(virq, nr_irqs);
+out_free_desc:
+	irq_free_descs(virq, nr_irqs);
+	return ret;
+}
+
+/**
+ * irq_domain_free_irqs - Free IRQ number and associated data structures
+ * @virq: base IRQ number
+ * @nr_irqs: number of IRQs to free
+ */
+void irq_domain_free_irqs(unsigned int virq, unsigned int nr_irqs)
+{
+	int i;
+	struct irq_data *data = irq_get_irq_data(virq);
+
+	if (WARN(!data || !data->domain || !data->domain->ops->free,
+		 "NULL pointer, cannot free irq\n"))
+		return;
+
+	mutex_lock(&irq_domain_mutex);
+	for (i = 0; i < nr_irqs; i++)
+		irq_domain_remove_irq(virq + i);
+	data->domain->ops->free(data->domain, virq, nr_irqs);
+	mutex_unlock(&irq_domain_mutex);
+
+	irq_domain_free_irq_data(virq, nr_irqs);
+	irq_free_descs(virq, nr_irqs);
+}
+
+/**
+ * irq_domain_activate_irq - Call domain_ops->activate recursively to activate
+ *			     interrupt
+ * @irq_data: outermost irq_data associated with interrupt
+ *
+ * This is the second step to call domain_ops->activate to program interrupt
+ * controllers, so the interrupt could actually get delivered.
+ */
+int irq_domain_activate_irq(struct irq_data *irq_data)
+{
+	int ret = 0;
+
+	if (irq_data && irq_data->domain) {
+		struct irq_domain *domain = irq_data->domain;
+
+		if (irq_data->parent_data)
+			ret = irq_domain_activate_irq(irq_data->parent_data);
+		if (ret == 0 && domain->ops->activate)
+			ret = domain->ops->activate(domain, irq_data);
+	}
+
+	return ret;
+}
+
+/**
+ * irq_domain_deactivate_irq - Call domain_ops->deactivate recursively to
+ *			       deactivate interrupt
+ * @irq_data: outermost irq_data associated with interrupt
+ *
+ * It calls domain_ops->deactivate to program interrupt controllers to disable
+ * interrupt delivery.
+ */
+int irq_domain_deactivate_irq(struct irq_data *irq_data)
+{
+	int ret = 0;
+
+	if (irq_data && irq_data->domain) {
+		struct irq_domain *domain = irq_data->domain;
+
+		if (domain->ops->deactivate)
+			ret = domain->ops->deactivate(domain, irq_data);
+		if (ret == 0 && irq_data->parent_data)
+			ret = irq_domain_deactivate_irq(irq_data->parent_data);
+	}
+
+	return ret;
+}
+
+static void irq_domain_check_hierarchy(struct irq_domain *domain)
+{
+	/* Hierarchy irq_domains must implement callback alloc() */
+	if (domain->ops->alloc)
+		domain->flags |= IRQ_DOMAIN_FLAG_HIERARCHY;
+}
+#else	/* CONFIG_IRQ_DOMAIN_HIERARCHY */
+/**
+ * irq_domain_get_irq_data - Get irq_data associated with @virq and @domain
+ * @domain: domain to match
+ * @virq: IRQ number to get irq_data
+ */
+struct irq_data *irq_domain_get_irq_data(struct irq_domain *domain,
+					 unsigned int virq)
+{
+	struct irq_data *irq_data = irq_get_irq_data(virq);
+
+	return (irq_data && irq_data->domain == domain) ? irq_data : NULL;
+}
+
+static void irq_domain_check_hierarchy(struct irq_domain *domain)
+{
+}
+#endif	/* CONFIG_IRQ_DOMAIN_HIERARCHY */
#3 lore raw

Re: [Patch Part2 v3 01/24] irqdomain: Introduce new interfaces to support hierarchy irqdomains

Quoted 26 lines · Yingjoe Chen

On Tue, 2014-10-28 at 16:26 +0800, Jiang Liu wrote:
<deleted...>

Quoted 24 lines
kernel/irq/irqdomain.c +6 -1
@@ -471,7 +469,7 @@ unsigned int irq_create_of_mapping(struct of_phandle_args *irq_data)
 	struct irq_domain *domain;
 	irq_hw_number_t hwirq;
 	unsigned int type = IRQ_TYPE_NONE;
-	unsigned int virq;
+	int virq;
 
 	domain = irq_data->np ? irq_find_host(irq_data->np) : irq_default_domain;
 	if (!domain) {
@@ -480,6 +478,11 @@ unsigned int irq_create_of_mapping(struct of_phandle_args *irq_data)
 		return 0;
 	}
 
+	if (irq_domain_is_hierarchy(domain)) {
+		virq = irq_domain_alloc_irqs(domain, 1, NUMA_NO_NODE, irq_data);
+		return virq <= 0 ? 0 : virq;
+	}
+
 	/* If domain has no translation, then we assume interrupt line */
 	if (domain->ops->xlate == NULL)
 		hwirq = irq_data->args[0];
@@ -540,8 +543,8 @@ unsigned int irq_find_mapping(struct irq_domain *domain,
 		return 0;
 
Quoted 9 lines

kernel/irq/irqdomain.c unsigned int irq_find_mapping(struct irq_domain *domain, · line 549

Quoted 7 lines
kernel/irq/irqdomain.c +2 -2
 	if (hwirq < domain->revmap_direct_max_irq) {
-		data = irq_get_irq_data(hwirq);
-		if (data && (data->domain == domain) && (data->hwirq == hwirq))
+		data = irq_domain_get_irq_data(domain, hwirq);
+		if (data && data->hwirq == hwirq)
 			return hwirq;
 	}

Why do you want to move irq_domain_alloc_irqs up there?

That's the wrong question. The proper question is whether we can move it after the xlate part. I don't see a reason why we cannot do that.

Jiang was right not to incorporate your patch to do that simply because we want to keep the history of the evolution of this code. Your patch is an enhancement and it needs to be discussed and applied seperately.

So while we are at it:

Review

Quoted 9 lines
(quoted diff) +9 -0
+	if (irq_domain_is_hierarchy(domain)) {
+		if (domain->ops->xlate) {
+			/*
+			 * If we've already configured this interrupt,
+			 * don't do it again, or hell will break loose.
+			 */
+			virq = irq_find_mapping(domain, hwirq);
+			if (virq)
+				return virq;

I can understand that it is an issue if the mapping exists already, but I have to ask WHY is it correct behaviour to call into that code for an existing mapping.

And why would this check only apply if domain->ops->xlate is set? irq_create_mapping() does it unconditionally.

Quoted 3 lines

Joe.C

Review const struct irq_domain_ops irq_domain_simple_ops = {

Quoted 2 lines
(quoted diff) +0 -0
@@ -709,3 +712,341 @@ const struct irq_domain_ops irq_domain_simple_ops = {

Please remove the rest of the mail to which you are not answering next time.

Thanks,

tglx

#4 lore raw

Re: [Patch Part2 v3 01/24] irqdomain: Introduce new interfaces to support hierarchy irqdomains

Quoted 47 lines · Thomas Gleixner
Quoted 26 lines · Yingjoe Chen

On Tue, 2014-10-28 at 16:26 +0800, Jiang Liu wrote:
<deleted...>

Quoted 24 lines
kernel/irq/irqdomain.c +6 -1
@@ -471,7 +469,7 @@ unsigned int irq_create_of_mapping(struct of_phandle_args *irq_data)
 	struct irq_domain *domain;
 	irq_hw_number_t hwirq;
 	unsigned int type = IRQ_TYPE_NONE;
-	unsigned int virq;
+	int virq;
 
 	domain = irq_data->np ? irq_find_host(irq_data->np) : irq_default_domain;
 	if (!domain) {
@@ -480,6 +478,11 @@ unsigned int irq_create_of_mapping(struct of_phandle_args *irq_data)
 		return 0;
 	}
 
+	if (irq_domain_is_hierarchy(domain)) {
+		virq = irq_domain_alloc_irqs(domain, 1, NUMA_NO_NODE, irq_data);
+		return virq <= 0 ? 0 : virq;
+	}
+
 	/* If domain has no translation, then we assume interrupt line */
 	if (domain->ops->xlate == NULL)
 		hwirq = irq_data->args[0];
@@ -540,8 +543,8 @@ unsigned int irq_find_mapping(struct irq_domain *domain,
 		return 0;
 
Quoted 9 lines

kernel/irq/irqdomain.c unsigned int irq_find_mapping(struct irq_domain *domain, · line 549

Quoted 7 lines
kernel/irq/irqdomain.c +2 -2
 	if (hwirq < domain->revmap_direct_max_irq) {
-		data = irq_get_irq_data(hwirq);
-		if (data && (data->domain == domain) && (data->hwirq == hwirq))
+		data = irq_domain_get_irq_data(domain, hwirq);
+		if (data && data->hwirq == hwirq)
 			return hwirq;
 	}

Why do you want to move irq_domain_alloc_irqs up there?

That's the wrong question. The proper question is whether we can move it after the xlate part. I don't see a reason why we cannot do that.

Jiang was right not to incorporate your patch to do that simply because we want to keep the history of the evolution of this code. Your patch is an enhancement and it needs to be discussed and applied seperately.

So while we are at it:

Quoted 13 lines

Review

Quoted 9 lines
(quoted diff) +9 -0
+	if (irq_domain_is_hierarchy(domain)) {
+		if (domain->ops->xlate) {
+			/*
+			 * If we've already configured this interrupt,
+			 * don't do it again, or hell will break loose.
+			 */
+			virq = irq_find_mapping(domain, hwirq);
+			if (virq)
+				return virq;

I can understand that it is an issue if the mapping exists already, but I have to ask WHY is it correct behaviour to call into that code for an existing mapping.

As I have originally looked at this, I'll answer the question:

The generic DT code parses the whole tree, and generates platform devices as it goes. As part of the platform device creation, it populates the IRQ resources, which translates into calling into irq_create_of_mapping(). You could argue that this behaviour is crazy, and I wouldn't disagree.

See http://www.spinics.net/lists/devicetree/msg53164.html for more gory details.

Quoted 2 lines

And why would this check only apply if domain->ops->xlate is set? irq_create_mapping() does it unconditionally.

My original code used the xlate callback to parse the opaque irq_data, computing hwirq, and I suspect this is a leftover of it. The above code seems to pull hwirq out of thin air, which is probably not the intended behaviour. Joe?

Thanks,

	M.
Signature · 1 line
Jazz is not dead. It just smells funny...
#5 lore raw

Re: [Patch Part2 v3 01/24] irqdomain: Introduce new interfaces to support hierarchy irqdomains

Quoted 17 lines · Marc Zyngier
Quoted 15 lines · Thomas Gleixner

So while we are at it:

Review

Quoted 9 lines
(quoted diff) +9 -0
+	if (irq_domain_is_hierarchy(domain)) {
+		if (domain->ops->xlate) {
+			/*
+			 * If we've already configured this interrupt,
+			 * don't do it again, or hell will break loose.
+			 */
+			virq = irq_find_mapping(domain, hwirq);
+			if (virq)
+				return virq;

I can understand that it is an issue if the mapping exists already, but I have to ask WHY is it correct behaviour to call into that code for an existing mapping.

Quoted 7 lines

As I have originally looked at this, I'll answer the question:

The generic DT code parses the whole tree, and generates platform devices as it goes. As part of the platform device creation, it populates the IRQ resources, which translates into calling into irq_create_of_mapping(). You could argue that this behaviour is crazy, and I wouldn't disagree.

Mooo.

Quoted 10 lines

See http://www.spinics.net/lists/devicetree/msg53164.html for more gory details.

Quoted 2 lines

And why would this check only apply if domain->ops->xlate is set? irq_create_mapping() does it unconditionally.

My original code used the xlate callback to parse the opaque irq_data, computing hwirq, and I suspect this is a leftover of it. The above code seems to pull hwirq out of thin air, which is probably not the intended behaviour. Joe?

No. Here is the full patch from Joe:

http://lists.infradead.org/pipermail/linux-arm-kernel/2014-October/296543.html

hwirq gets either set from hwirq = irq_data->args[0] or from the xlate call.

But my question still stands:

Why would this check only apply if domain->ops->xlate is set? irq_create_mapping() does it unconditionally.

Thanks,

tglx

#6 lore raw

Re: [Patch Part2 v3 01/24] irqdomain: Introduce new interfaces to support hierarchy irqdomains

Quoted 20 lines · Thomas Gleixner
Quoted 19 lines · Marc Zyngier
Quoted 15 lines · Thomas Gleixner

So while we are at it:

Review

Quoted 9 lines
(quoted diff) +9 -0
+	if (irq_domain_is_hierarchy(domain)) {
+		if (domain->ops->xlate) {
+			/*
+			 * If we've already configured this interrupt,
+			 * don't do it again, or hell will break loose.
+			 */
+			virq = irq_find_mapping(domain, hwirq);
+			if (virq)
+				return virq;

I can understand that it is an issue if the mapping exists already, but I have to ask WHY is it correct behaviour to call into that code for an existing mapping.

As I have originally looked at this, I'll answer the question:

Quoted 7 lines
Quoted 5 lines

The generic DT code parses the whole tree, and generates platform devices as it goes. As part of the platform device creation, it populates the IRQ resources, which translates into calling into irq_create_of_mapping(). You could argue that this behaviour is crazy, and I wouldn't disagree.

Mooo.

Quite.

Quoted 11 lines
Quoted 10 lines

See http://www.spinics.net/lists/devicetree/msg53164.html for more gory details.

Quoted 2 lines

And why would this check only apply if domain->ops->xlate is set? irq_create_mapping() does it unconditionally.

My original code used the xlate callback to parse the opaque irq_data, computing hwirq, and I suspect this is a leftover of it. The above code seems to pull hwirq out of thin air, which is probably not the intended behaviour. Joe?

Quoted 6 lines

No. Here is the full patch from Joe:

http://lists.infradead.org/pipermail/linux-arm-kernel/2014-October/296543.html

hwirq gets either set from hwirq = irq_data->args[0] or from the xlate call.

Ah, that makes a lot more sense.

Quoted 4 lines

But my question still stands:

Why would this check only apply if domain->ops->xlate is set? irq_create_mapping() does it unconditionally.

I don't think we should consider xlate at all. We already resolved hwirq (either directly or through a xlate call), and the check should always be performed (otherwise we're likely to fall into the same trap again). Looks like a bug to me.

Thanks,

	M.
Signature · 1 line
Jazz is not dead. It just smells funny...
#7 lore raw

Re: [Patch Part2 v3 01/24] irqdomain: Introduce new interfaces to support hierarchy irqdomains

Quoted 53 lines · Marc Zyngier
Quoted 20 lines · Thomas Gleixner
Quoted 19 lines · Marc Zyngier
Quoted 15 lines · Thomas Gleixner

So while we are at it:

Review

Quoted 9 lines
(quoted diff) +9 -0
+	if (irq_domain_is_hierarchy(domain)) {
+		if (domain->ops->xlate) {
+			/*
+			 * If we've already configured this interrupt,
+			 * don't do it again, or hell will break loose.
+			 */
+			virq = irq_find_mapping(domain, hwirq);
+			if (virq)
+				return virq;

I can understand that it is an issue if the mapping exists already, but I have to ask WHY is it correct behaviour to call into that code for an existing mapping.

As I have originally looked at this, I'll answer the question:

Quoted 7 lines
Quoted 5 lines

The generic DT code parses the whole tree, and generates platform devices as it goes. As part of the platform device creation, it populates the IRQ resources, which translates into calling into irq_create_of_mapping(). You could argue that this behaviour is crazy, and I wouldn't disagree.

Mooo.

Quite.

Quoted 11 lines
Quoted 10 lines

See http://www.spinics.net/lists/devicetree/msg53164.html for more gory details.

Quoted 2 lines

And why would this check only apply if domain->ops->xlate is set? irq_create_mapping() does it unconditionally.

My original code used the xlate callback to parse the opaque irq_data, computing hwirq, and I suspect this is a leftover of it. The above code seems to pull hwirq out of thin air, which is probably not the intended behaviour. Joe?

Quoted 6 lines

No. Here is the full patch from Joe:

http://lists.infradead.org/pipermail/linux-arm-kernel/2014-October/296543.html

hwirq gets either set from hwirq = irq_data->args[0] or from the xlate call.

Ah, that makes a lot more sense.

Quoted 2 lines

But my question still stands:

Quoted 7 lines
Quoted 2 lines

Why would this check only apply if domain->ops->xlate is set? irq_create_mapping() does it unconditionally.

I don't think we should consider xlate at all. We already resolved hwirq (either directly or through a xlate call), and the check should always be performed (otherwise we're likely to fall into the same trap again). Looks like a bug to me.

I see it now.
When irq_create_of_mapping() is called, we know there exists a mapping between irq_data and hwirq. If xlate exists, we get the mapping from xlate call, otherwise irq_data->args[0] is hwirq. This is true even when using hierarchy irqdomain. So yes, we should also check for existing mapping for empty domain->ops->xlate.
I corrected this in my latest version.
http://lists.infradead.org/pipermail/linux-arm-kernel/2014-October/298158.html

Thanks for catching this.

Joe.C

author
#8 lore raw

PATCH v3 2/24 genirq: Introduce helper functions to support stacked irq_chip

In reply to Jiang Liu

Now we already support hierarchy irq_datas, so introduce several helpers to support stacked irq_chips.

---
 include/linux/irq.h |    5 +++++
 kernel/irq/chip.c   |   17 +++++++++++++++++
 2 files changed, 22 insertions(+)
include/linux/irq.h +5 -0
index 13ba412ce3a0..0adcbbbf2e87 100644
@@ -443,6 +443,11 @@ extern void handle_percpu_devid_irq(unsigned int irq, struct irq_desc *desc);
 extern void handle_bad_irq(unsigned int irq, struct irq_desc *desc);
 extern void handle_nested_irq(unsigned int irq);
 
+#ifdef	CONFIG_IRQ_DOMAIN_HIERARCHY
+extern void irq_chip_ack_parent(struct irq_data *data);
+extern int irq_chip_retrigger_hierarchy(struct irq_data *data);
+#endif
+
 /* Handling of unhandled and spurious interrupts: */
 extern void note_interrupt(unsigned int irq, struct irq_desc *desc,
 			   irqreturn_t action_ret);
kernel/irq/chip.c +17 -0
index 72a93086216b..12f3e72449eb 100644
@@ -850,3 +850,20 @@ void irq_cpu_offline(void)
 		raw_spin_unlock_irqrestore(&desc->lock, flags);
 	}
 }
+
+#ifdef	CONFIG_IRQ_DOMAIN_HIERARCHY
+void irq_chip_ack_parent(struct irq_data *data)
+{
+	data = data->parent_data;
+	data->chip->irq_ack(data);
+}
+
+int irq_chip_retrigger_hierarchy(struct irq_data *data)
+{
+	for (data = data->parent_data; data; data = data->parent_data)
+		if (data->chip && data->chip->irq_retrigger)
+			return data->chip->irq_retrigger(data);
+
+	return -ENOSYS;
+}
+#endif
Signature · 1 line
1.7.10.4
author
#9 lore raw

PATCH v3 3/24 x86, irq: Save destination CPU ID in irq_cfg

In reply to Jiang Liu

Cache destination CPU APIC ID into struct irq_cfg when assigning vector for interrupt. Upper layer just needs to read the cached APIC ID instead of calling apic->cpu_mask_to_apicid_and(), it helps to hide APIC driver details from IOAPIC/HPET/MSI drivers..

---
 arch/x86/include/asm/hw_irq.h |    1 +
 arch/x86/kernel/apic/vector.c |    6 ++++++
 2 files changed, 7 insertions(+)
arch/x86/include/asm/hw_irq.h +1 -0
index 7624fffc2822..3d51d74d6c01 100644
@@ -116,6 +116,7 @@ struct irq_data;
 struct irq_cfg {
 	cpumask_var_t		domain;
 	cpumask_var_t		old_domain;
+	unsigned int		dest_apicid;
 	u8			vector;
 	u8			move_in_progress : 1;
 #ifdef CONFIG_IRQ_REMAP
arch/x86/kernel/apic/vector.c +6 -0
index 5ac840a4cc53..02cb5d386985 100644
@@ -188,6 +188,12 @@ next:
 	}
 	free_cpumask_var(tmp_mask);
 
+	if (!err) {
+		/* cache destination APIC IDs into cfg->dest_apicid */
+		err = apic->cpu_mask_to_apicid_and(mask, cfg->domain,
+						   &cfg->dest_apicid);
+	}
+
 	return err;
 }
 
Signature · 1 line
1.7.10.4
author
#10 lore raw

PATCH v3 4/24 x86, irq: Use hierarchy irqdomain to manage CPU interrupt vectors

In reply to Jiang Liu

Abstract CPU local APIC as an interrupt controller and create an irqdomain for it to manage CPU interupt vectors. It's the base to enable hierarchy irqdomain on x86 systems. Eventually we will build a irqdomain hiearchy as below:
IOAPIC domain-------|
MSI/MSI-x domain------> [Inerrupt Remapping domain] -> CPU vector domain

HPET_IRQ domain_____|                                         ^
DMAR domain---------------------------------------------------|
HT_IRQ domain-------------------------------------------------|
---
 arch/x86/Kconfig               |    3 +-
 arch/x86/include/asm/hw_irq.h  |   11 +++
 arch/x86/kernel/apic/io_apic.c |    3 -
 arch/x86/kernel/apic/vector.c  |  151 ++++++++++++++++++++++++++++++++++++----
 4 files changed, 151 insertions(+), 17 deletions(-)
arch/x86/Kconfig +2 -1
index d019b1bc3efb..62383b641d69 100644
@@ -879,11 +879,12 @@ config X86_LOCAL_APIC
 	def_bool y
 	depends on X86_64 || SMP || X86_32_NON_STANDARD || X86_UP_APIC || PCI_MSI
 	select GENERIC_IRQ_LEGACY_ALLOC_HWIRQ
+	select IRQ_DOMAIN
+	select IRQ_DOMAIN_HIERARCHY
 
 config X86_IO_APIC
 	def_bool X86_64 || SMP || X86_32_NON_STANDARD || X86_UP_IOAPIC
 	depends on X86_LOCAL_APIC
-	select IRQ_DOMAIN
 
 config X86_REROUTE_FOR_BROKEN_BOOT_IRQS
 	bool "Reroute for broken boot IRQs"
arch/x86/include/asm/hw_irq.h +11 -0
index 3d51d74d6c01..1300702adb1e 100644
@@ -112,6 +112,11 @@ struct irq_2_irte {
 
 #ifdef	CONFIG_X86_LOCAL_APIC
 struct irq_data;
+struct irq_domain;
+
+struct irq_alloc_info {
+	const struct cpumask *mask;	/* CPU mask for vector allocation */
+};
 
 struct irq_cfg {
 	cpumask_var_t		domain;
@@ -135,6 +140,12 @@ struct irq_cfg {
 	};
 };
 
+extern struct irq_domain *x86_vector_domain;
+
+extern void init_irq_alloc_info(struct irq_alloc_info *info,
+				const struct cpumask *mask);
+extern void copy_irq_alloc_info(struct irq_alloc_info *dst,
+				struct irq_alloc_info *src);
 extern struct irq_cfg *irq_cfg(unsigned int irq);
 extern struct irq_cfg *irqd_cfg(struct irq_data *irq_data);
 extern struct irq_cfg *alloc_irq_and_cfg_at(unsigned int at, int node);
arch/x86/kernel/apic/io_apic.c +0 -3
index 68158c5112d2..59c4cde4051f 100644
@@ -2353,9 +2353,6 @@ static int mp_irqdomain_create(int ioapic)
 		ioapic_dynirq_base = max(ioapic_dynirq_base,
 					 gsi_cfg->gsi_end + 1);
 
-	if (gsi_cfg->gsi_base == 0)
-		irq_set_default_host(ip->irqdomain);
-
 	return 0;
 }
 
arch/x86/kernel/apic/vector.c +138 -13

226 lines not on this page · raw message

Signature · 1 line
1.7.10.4
author
#11 lore raw

PATCH v3 5/24 x86, hpet: Use new irqdomain interfaces to allocate/free IRQ

In reply to Jiang Liu

Use new irqdomain interfaces to allocate/free IRQ for HPET, so we could kill GENERIC_IRQ_LEGACY_ALLOC_HWIRQ later.

---
 arch/x86/kernel/hpet.c |    8 +++++---
 1 file changed, 5 insertions(+), 3 deletions(-)
arch/x86/kernel/hpet.c +5 -3
index 319bcb9372fe..24db2d33fab7 100644
@@ -11,6 +11,7 @@
 #include <linux/cpu.h>
 #include <linux/pm.h>
 #include <linux/io.h>
+#include <linux/irqdomain.h>
 
 #include <asm/fixmap.h>
 #include <asm/hpet.h>
@@ -476,7 +477,7 @@ static int hpet_msi_next_event(unsigned long delta,
 static int hpet_setup_msi_irq(unsigned int irq)
 {
 	if (x86_msi.setup_hpet_msi(irq, hpet_blockid)) {
-		irq_free_hwirq(irq);
+		irq_domain_free_irqs(irq, 1);
 		return -EINVAL;
 	}
 	return 0;
@@ -484,9 +485,10 @@ static int hpet_setup_msi_irq(unsigned int irq)
 
 static int hpet_assign_irq(struct hpet_dev *dev)
 {
-	unsigned int irq = irq_alloc_hwirq(-1);
+	int irq;
 
-	if (!irq)
+	irq = irq_domain_alloc_irqs(NULL, 1, NUMA_NO_NODE, NULL);
+	if (irq <= 0)
 		return -EINVAL;
 
 	irq_set_handler_data(irq, dev);
Signature · 1 line
1.7.10.4
author
#12 lore raw

PATCH v3 6/24 x86, MSI: Use new irqdomain interfaces to allocate/free IRQ

In reply to Jiang Liu

Use new irqdomain interfaces to allocate/free IRQ for MSI, so we could kill GENERIC_IRQ_LEGACY_ALLOC_HWIRQ later.

---
 arch/x86/kernel/apic/msi.c |   14 ++++++--------
 1 file changed, 6 insertions(+), 8 deletions(-)
arch/x86/kernel/apic/msi.c +6 -8
index 6916246294eb..7e45991c0e79 100644
@@ -14,6 +14,7 @@
 #include <linux/dmar.h>
 #include <linux/hpet.h>
 #include <linux/msi.h>
+#include <linux/irqdomain.h>
 #include <asm/msidef.h>
 #include <asm/hpet.h>
 #include <asm/hw_irq.h>
@@ -146,23 +147,20 @@ int setup_msi_irq(struct pci_dev *dev, struct msi_desc *msidesc,
 int native_setup_msi_irqs(struct pci_dev *dev, int nvec, int type)
 {
 	struct msi_desc *msidesc;
-	unsigned int irq;
-	int node, ret;
+	int irq, ret;
 
 	/* Multiple MSI vectors only supported with interrupt remapping */
 	if (type == PCI_CAP_ID_MSI && nvec > 1)
 		return 1;
 
-	node = dev_to_node(&dev->dev);
-
 	list_for_each_entry(msidesc, &dev->msi_list, list) {
-		irq = irq_alloc_hwirq(node);
-		if (!irq)
+		irq = irq_domain_alloc_irqs(NULL, 1, NUMA_NO_NODE, NULL);
+		if (irq <= 0)
 			return -ENOSPC;
 
 		ret = setup_msi_irq(dev, msidesc, irq, 0);
 		if (ret < 0) {
-			irq_free_hwirq(irq);
+			irq_domain_free_irqs(irq, 1);
 			return ret;
 		}
 
@@ -172,7 +170,7 @@ int native_setup_msi_irqs(struct pci_dev *dev, int nvec, int type)
 
 void native_teardown_msi_irq(unsigned int irq)
 {
-	irq_free_hwirq(irq);
+	irq_domain_free_irqs(irq, 1);
 }
 
 #ifdef CONFIG_DMAR_TABLE
Signature · 1 line
1.7.10.4
author
#13 lore raw

PATCH v3 7/24 x86, uv: Use new irqdomain interfaces to allocate/free IRQ

In reply to Jiang Liu

Use new irqdomain interfaces to allocate/free IRQ, so we could kill GENERIC_IRQ_LEGACY_ALLOC_HWIRQ later.

---
 arch/x86/platform/uv/uv_irq.c |   27 +++++++++++----------------
 1 file changed, 11 insertions(+), 16 deletions(-)
arch/x86/platform/uv/uv_irq.c +11 -16
index 0ce673645432..474912d03f40 100644
@@ -12,6 +12,7 @@
 #include <linux/rbtree.h>
 #include <linux/slab.h>
 #include <linux/irq.h>
+#include <linux/irqdomain.h>
 
 #include <asm/apic.h>
 #include <asm/uv/uv_irq.h>
@@ -130,24 +131,14 @@ static int
 arch_enable_uv_irq(char *irq_name, unsigned int irq, int cpu, int mmr_blade,
 		       unsigned long mmr_offset, int limit)
 {
-	const struct cpumask *eligible_cpu = cpumask_of(cpu);
 	struct irq_cfg *cfg = irq_cfg(irq);
 	unsigned long mmr_value;
 	struct uv_IO_APIC_route_entry *entry;
-	int mmr_pnode, err;
-	unsigned int dest;
+	int mmr_pnode;
 
 	BUILD_BUG_ON(sizeof(struct uv_IO_APIC_route_entry) !=
 			sizeof(unsigned long));
 
-	err = assign_irq_vector(irq, cfg, eligible_cpu);
-	if (err != 0)
-		return err;
-
-	err = apic->cpu_mask_to_apicid_and(eligible_cpu, eligible_cpu, &dest);
-	if (err != 0)
-		return err;
-
 	if (limit == UV_AFFINITY_CPU)
 		irq_set_status_flags(irq, IRQ_NO_BALANCING);
 	else
@@ -164,7 +155,7 @@ arch_enable_uv_irq(char *irq_name, unsigned int irq, int cpu, int mmr_blade,
 	entry->polarity		= 0;
 	entry->trigger		= 0;
 	entry->mask		= 0;
-	entry->dest		= dest;
+	entry->dest		= cfg->dest_apicid;
 
 	mmr_pnode = uv_blade_to_pnode(mmr_blade);
 	uv_write_global_mmr64(mmr_pnode, mmr_offset, mmr_value);
@@ -238,9 +229,13 @@ uv_set_irq_affinity(struct irq_data *data, const struct cpumask *mask,
 int uv_setup_irq(char *irq_name, int cpu, int mmr_blade,
 		 unsigned long mmr_offset, int limit)
 {
-	int ret, irq = irq_alloc_hwirq(uv_blade_to_memory_nid(mmr_blade));
+	int ret, irq;
+	struct irq_alloc_info info;
 
-	if (!irq)
+	init_irq_alloc_info(&info, cpumask_of(cpu));
+	irq = irq_domain_alloc_irqs(NULL, 1, uv_blade_to_memory_nid(mmr_blade),
+				    &info);
+	if (irq <= 0)
 		return -EBUSY;
 
 	ret = arch_enable_uv_irq(irq_name, irq, cpu, mmr_blade, mmr_offset,
@@ -248,7 +243,7 @@ int uv_setup_irq(char *irq_name, int cpu, int mmr_blade,
 	if (ret == irq)
 		uv_set_irq_2_mmr_info(irq, mmr_offset, mmr_blade);
 	else
-		irq_free_hwirq(irq);
+		irq_domain_free_irqs(irq, 1);
 
 	return ret;
 }
@@ -283,6 +278,6 @@ void uv_teardown_irq(unsigned int irq)
 			n = n->rb_right;
 	}
 	spin_unlock_irqrestore(&uv_irq_lock, irqflags);
-	irq_free_hwirq(irq);
+	irq_domain_free_irqs(irq, 1);
 }
 EXPORT_SYMBOL_GPL(uv_teardown_irq);
Signature · 1 line
1.7.10.4
author
#14 lore raw

PATCH v3 8/24 x86, htirq: Use new irqdomain interfaces to allocate/free IRQ

In reply to Jiang Liu

Use new irqdomain interfaces to allocate/free IRQ for HTIRQ, so we could kill GENERIC_IRQ_LEGACY_ALLOC_HWIRQ later.

This patch changes the interfaces between arch independent PCI driver and arch specific code. Currently HT_IRQ is only enabled on x86, so it shouldn't break other architectures.

---
 arch/x86/kernel/apic/htirq.c |   26 +++++++++++++-------------
 drivers/pci/htirq.c          |    7 +++----
 include/linux/htirq.h        |    2 ++
 3 files changed, 18 insertions(+), 17 deletions(-)
arch/x86/kernel/apic/htirq.c +13 -13
index 816f36e979ad..b307ee7a7148 100644
@@ -14,6 +14,7 @@
 #include <linux/device.h>
 #include <linux/pci.h>
 #include <linux/htirq.h>
+#include <linux/irqdomain.h>
 #include <asm/hw_irq.h>
 #include <asm/apic.h>
 #include <asm/hypertransport.h>
@@ -61,31 +62,30 @@ static struct irq_chip ht_irq_chip = {
 	.flags			= IRQCHIP_SKIP_SET_WAKE,
 };
 
+int arch_alloc_ht_irq(struct pci_dev *dev)
+{
+	return irq_domain_alloc_irqs(NULL, 1, dev_to_node(&dev->dev), NULL);
+}
+
+void arch_free_ht_irq(int irq)
+{
+	irq_domain_free_irqs(irq, 1);
+}
+
 int arch_setup_ht_irq(unsigned int irq, struct pci_dev *dev)
 {
 	struct irq_cfg *cfg;
 	struct ht_irq_msg msg;
-	unsigned dest;
-	int err;
 
 	if (disable_apic)
 		return -ENXIO;
 
 	cfg = irq_cfg(irq);
-	err = assign_irq_vector(irq, cfg, apic->target_cpus());
-	if (err)
-		return err;
-
-	err = apic->cpu_mask_to_apicid_and(cfg->domain,
-					   apic->target_cpus(), &dest);
-	if (err)
-		return err;
-
-	msg.address_hi = HT_IRQ_HIGH_DEST_ID(dest);
+	msg.address_hi = HT_IRQ_HIGH_DEST_ID(cfg->dest_apicid);
 
 	msg.address_lo =
 		HT_IRQ_LOW_BASE |
-		HT_IRQ_LOW_DEST_ID(dest) |
+		HT_IRQ_LOW_DEST_ID(cfg->dest_apicid) |
 		HT_IRQ_LOW_VECTOR(cfg->vector) |
 		((apic->irq_dest_mode == 0) ?
 			HT_IRQ_LOW_DM_PHYSICAL :
drivers/pci/htirq.c +3 -4
index a94dd2c4183a..ceb0ebeb7b5f 100644
@@ -117,8 +117,8 @@ int __ht_create_irq(struct pci_dev *dev, int idx, ht_irq_update_t *update)
 	cfg->msg.address_lo = 0xffffffff;
 	cfg->msg.address_hi = 0xffffffff;
 
-	irq = irq_alloc_hwirq(dev_to_node(&dev->dev));
-	if (!irq) {
+	irq = arch_alloc_ht_irq(dev);
+	if (irq <= 0) {
 		kfree(cfg);
 		return -EBUSY;
 	}
@@ -163,8 +163,7 @@ void ht_destroy_irq(unsigned int irq)
 	cfg = irq_get_handler_data(irq);
 	irq_set_chip(irq, NULL);
 	irq_set_handler_data(irq, NULL);
-	irq_free_hwirq(irq);
-
+	arch_free_ht_irq(irq);
 	kfree(cfg);
 }
 EXPORT_SYMBOL(ht_destroy_irq);
include/linux/htirq.h +2 -0
index 70a1dbbf2093..5caa51b7b95c 100644
@@ -15,6 +15,8 @@ void unmask_ht_irq(struct irq_data *data);
 
 /* The arch hook for getting things started */
 int arch_setup_ht_irq(unsigned int irq, struct pci_dev *dev);
+int arch_alloc_ht_irq(struct pci_dev *dev);
+void arch_free_ht_irq(int irq);
 
 /* For drivers of buggy hardware */
 typedef void (ht_irq_update_t)(struct pci_dev *dev, int irq,
Signature · 1 line
1.7.10.4
author
#15 lore raw

PATCH v3 9/24 x86, dmar: Use new irqdomain interfaces to allocate/free IRQ

In reply to Jiang Liu

Use new irqdomain interfaces to allocate/free IRQ for DMAR and interrupt remapping, so we could kill GENERIC_IRQ_LEGACY_ALLOC_HWIRQ later.

The private definition of irq_alloc_hwirqs()/irq_free_hwirqs() are temporary solution, it will be removed once we have converted interrupt remapping driver to use irqdomain framework.

---
 arch/x86/include/asm/irq_remapping.h |    4 ++--
 arch/x86/kernel/apic/msi.c           |   10 ++++++++++
 drivers/iommu/irq_remapping.c        |   17 +++++++++++++++--
 3 files changed, 27 insertions(+), 4 deletions(-)
arch/x86/include/asm/irq_remapping.h +2 -2
index b7747c4c2cf2..230dde9b695e 100644
@@ -103,7 +103,7 @@ static inline bool setup_remapped_irq(int irq,
 }
 #endif /* CONFIG_IRQ_REMAP */
 
-#define dmar_alloc_hwirq()	irq_alloc_hwirq(-1)
-#define dmar_free_hwirq		irq_free_hwirq
+extern int dmar_alloc_hwirq(void);
+extern void dmar_free_hwirq(int irq);
 
 #endif /* __X86_IRQ_REMAPPING_H */
arch/x86/kernel/apic/msi.c +10 -0
index 7e45991c0e79..4bb2b583be7f 100644
@@ -223,6 +223,16 @@ int arch_setup_dmar_msi(unsigned int irq)
 				      "edge");
 	return 0;
 }
+
+int dmar_alloc_hwirq(void)
+{
+	return irq_domain_alloc_irqs(NULL, 1, NUMA_NO_NODE, NULL);
+}
+
+void dmar_free_hwirq(int irq)
+{
+	irq_domain_free_irqs(irq, 1);
+}
 #endif
 
 /*
drivers/iommu/irq_remapping.c +15 -2
index e9fbd68db96e..63886bafed9f 100644
@@ -6,6 +6,7 @@
 #include <linux/msi.h>
 #include <linux/irq.h>
 #include <linux/pci.h>
+#include <linux/irqdomain.h>
 
 #include <asm/hw_irq.h>
 #include <asm/irq_remapping.h>
@@ -50,6 +51,18 @@ static void irq_remapping_disable_io_apic(void)
 		disconnect_bsp_APIC(0);
 }
 
+#ifndef CONFIG_GENERIC_IRQ_LEGACY_ALLOC_HWIRQ
+static unsigned int irq_alloc_hwirqs(int cnt, int node)
+{
+	return irq_domain_alloc_irqs(NULL, -1, cnt, node, NULL);
+}
+
+static void irq_free_hwirqs(unsigned int from, int cnt)
+{
+	irq_domain_free_irqs(from, cnt);
+}
+#endif
+
 static int do_setup_msi_irqs(struct pci_dev *dev, int nvec)
 {
 	int ret, sub_handle, nvec_pow2, index = 0;
@@ -113,7 +126,7 @@ static int do_setup_msix_irqs(struct pci_dev *dev, int nvec)
 
 	list_for_each_entry(msidesc, &dev->msi_list, list) {
 
-		irq = irq_alloc_hwirq(node);
+		irq = irq_alloc_hwirqs(1, node);
 		if (irq == 0)
 			return -1;
 
@@ -136,7 +149,7 @@ static int do_setup_msix_irqs(struct pci_dev *dev, int nvec)
 	return 0;
 
 error:
-	irq_free_hwirq(irq);
+	irq_free_hwirqs(irq, 1);
 	return ret;
 }
 
Signature · 1 line
1.7.10.4
author
#16 lore raw

PATCH v3 10/24 x86: irq_remapping: Introduce new interfaces to support hierarchy irqdomain

In reply to Jiang Liu

Introduce new interfaces for interrupt remapping drivers to support hierarchy irqdomain:
1) irq_remapping_get_ir_irq_domain(): get irqdomain associated with an interrupt remapping unit. IOAPIC/HPET drivers use this interface to get parent interrupt remapping irqdomain.
2) irq_remapping_get_irq_domain(): get irqdomain for an IRQ allocation. This is mainly used to support MSI irqdomain. We must build one MSI irqdomain for each interrupt remapping unit. MSI driver calls this interface to get MSI irqdomain associated with an IR irqdomain which manages the PCI devices.
3) irq_remapping_get_ioapic_entry(): get IOAPIC entry content rewritten by the interrupt remapping driver for remapped IOAPIC interrupt.
4) irq_remapping_get_msi_entry(): get MSI/HPET entry content rewritten by the interrupt remapping driver for remapped MSI/HPET interrupt.

Architecture specific needs to implement two hooks:
1) arch_get_ir_parent_domain(): get parent irqdomain for IR irqdomain, which is x86_vector_domain on x86 platforms.
2) arch_create_msi_irq_domain(): create an MSI irqdomain associated with the interrupt remapping unit.

We also add follwing callbacks into struct irq_remap_ops:

	struct irq_domain *(*get_ir_irq_domain)(struct irq_alloc_info *);
	struct irq_domain *(*get_irq_domain)(struct irq_alloc_info *);
	int (*get_ioapic_entry)(struct irq_data *,
				struct IR_IO_APIC_route_entry *);
	int (*get_msi_entry)(struct irq_data *, struct msi_msg *);

Once all clients of IR have been converted to new hierarchy irqdomain interfaces, we will:
1) Remove set_ioapic_entry, set_affinity, free_irq, compose_msi_msg, msi_alloc_irq, msi_setup_irq, setup_hpet_msi from struct remap_osp
2) Kill setup_ioapic_remapped_entry, free_remapped_irq, compose_remapped_msi_msg, setup_hpet_msi_remapped, setup_remapped_irq.
3) Simplify x86_io_apic_ops and x86_msi.

We could achieve a much more clear architecture with all these changes applied.

---
 arch/x86/include/asm/hw_irq.h        |   36 +++++++++++++++-
 arch/x86/include/asm/irq_remapping.h |   74 ++++++++++++++++++++++++++++++++
 drivers/iommu/irq_remapping.c        |   78 +++++++++++++++++++++++++++++++++-
 drivers/iommu/irq_remapping.h        |   17 ++++++++
 4 files changed, 203 insertions(+), 2 deletions(-)
arch/x86/include/asm/hw_irq.h +35 -1
index 1300702adb1e..545460d470bd 100644
@@ -113,9 +113,43 @@ struct irq_2_irte {
 #ifdef	CONFIG_X86_LOCAL_APIC
 struct irq_data;
 struct irq_domain;
+struct pci_dev;
+struct msi_desc;
+
+enum irq_alloc_type {
+	X86_IRQ_ALLOC_TYPE_IOAPIC = 1,
+	X86_IRQ_ALLOC_TYPE_HPET,
+	X86_IRQ_ALLOC_TYPE_MSI,
+	X86_IRQ_ALLOC_TYPE_MSIX,
+};
 
 struct irq_alloc_info {
-	const struct cpumask *mask;	/* CPU mask for vector allocation */
+	const struct cpumask	*mask;	/* CPU mask for vector allocation */
+	enum irq_alloc_type	type;
+	union {
+		int		unused;
+#ifdef	CONFIG_HPET_TIMER
+		struct {
+			int		hpet_id;
+			int		hpet_index;
+			void		*hpet_data;
+		};
+#endif
+#ifdef	CONFIG_PCI_MSI
+		struct {
+			struct pci_dev	*msi_dev;
+			struct msi_desc *msi_desc;
+		};
+#endif
+#ifdef	CONFIG_X86_IO_APIC
+		struct {
+			int		ioapic_id;
+			int		ioapic_pin;
+			u32		ioapic_trigger : 1;
+			u32		ioapic_polarity : 1;
+		};
+#endif
+	};
 };
 
 struct irq_cfg {
arch/x86/include/asm/irq_remapping.h +74 -0

98 lines not on this page · raw message

drivers/iommu/irq_remapping.c +77 -1

93 lines not on this page · raw message

drivers/iommu/irq_remapping.h +17 -0
index fde250f86e60..fd0270b7a351 100644
@@ -30,6 +30,8 @@ struct irq_data;
 struct cpumask;
 struct pci_dev;
 struct msi_msg;
+struct irq_domain;
+struct irq_alloc_info;
 
 extern int disable_irq_remap;
 extern int irq_remap_broken;
@@ -81,11 +83,26 @@ struct irq_remap_ops {
 
 	/* Setup interrupt remapping for an HPET MSI */
 	int (*alloc_hpet_msi)(unsigned int, unsigned int);
+
+	/* Get the irqdomain associated the IOMMU device */
+	struct irq_domain *(*get_ir_irq_domain)(struct irq_alloc_info *);
+
+	/* Get the MSI irqdomain associated with the IOMMU device */
+	struct irq_domain *(*get_irq_domain)(struct irq_alloc_info *);
+
+	/* Get IOAPIC entry content rewritten by interrupt remapping driver */
+	int (*get_ioapic_entry)(struct irq_data *,
+				struct IR_IO_APIC_route_entry *);
+
+	/*  Get MSI data rewritten by interrupt remapping driver */
+	int (*get_msi_entry)(struct irq_data *, struct msi_msg *);
 };
 
 extern struct irq_remap_ops intel_irq_remap_ops;
 extern struct irq_remap_ops amd_iommu_irq_ops;
 
+extern void ir_ack_apic_edge(struct irq_data *data);
+
 #else  /* CONFIG_IRQ_REMAP */
 
 #define irq_remapping_enabled 0
Signature · 1 line
1.7.10.4
author
#17 lore raw

PATCH v3 11/24 iommu/vt-d: Change prototypes to prepare for enabling hierarchy irqdomain

In reply to Jiang Liu

Prepare for support hierarchy irqdomain by changing function prototypes, should be no function changes.

---
 drivers/iommu/intel_irq_remapping.c |   22 +++++++++++-----------
 1 file changed, 11 insertions(+), 11 deletions(-)
drivers/iommu/intel_irq_remapping.c +11 -11
index fd181cf8a589..5acad492701e 100644
@@ -82,10 +82,10 @@ static int get_irte(int irq, struct irte *entry)
 	return 0;
 }
 
-static int alloc_irte(struct intel_iommu *iommu, int irq, u16 count)
+static int alloc_irte(struct intel_iommu *iommu, int irq,
+		      struct irq_2_iommu *irq_iommu, u16 count)
 {
 	struct ir_table *table = iommu->ir_table;
-	struct irq_2_iommu *irq_iommu = irq_2_iommu(irq);
 	struct irq_cfg *cfg = irq_cfg(irq);
 	unsigned int mask = 0;
 	unsigned long flags;
@@ -173,9 +173,9 @@ static int set_irte_irq(int irq, struct intel_iommu *iommu, u16 index, u16 subha
 	return 0;
 }
 
-static int modify_irte(int irq, struct irte *irte_modified)
+static int modify_irte(struct irq_2_iommu *irq_iommu,
+		       struct irte *irte_modified)
 {
-	struct irq_2_iommu *irq_iommu = irq_2_iommu(irq);
 	struct intel_iommu *iommu;
 	unsigned long flags;
 	struct irte *irte;
@@ -242,7 +242,7 @@ static int clear_entries(struct irq_2_iommu *irq_iommu)
 		return 0;
 
 	iommu = irq_iommu->iommu;
-	index = irq_iommu->irte_index + irq_iommu->sub_handle;
+	index = irq_iommu->irte_index;
 
 	start = iommu->ir_table->base + index;
 	end = start + (1 << irq_iommu->irte_mask);
@@ -937,7 +937,7 @@ static int intel_setup_ioapic_entry(int irq,
 		pr_warn("No mapping iommu for ioapic %d\n", ioapic_id);
 		index = -ENODEV;
 	} else {
-		index = alloc_irte(iommu, irq, 1);
+		index = alloc_irte(iommu, irq, irq_2_iommu(irq), 1);
 		if (index < 0) {
 			pr_warn("Failed to allocate IRTE for ioapic %d\n",
 				ioapic_id);
@@ -953,7 +953,7 @@ static int intel_setup_ioapic_entry(int irq,
 	/* Set source-id of interrupt request */
 	set_ioapic_sid(&irte, ioapic_id);
 
-	modify_irte(irq, &irte);
+	modify_irte(irq_2_iommu(irq), &irte);
 
 	apic_printk(APIC_VERBOSE, KERN_DEBUG "IOAPIC[%d]: "
 		"Set IRTE entry (P:%d FPD:%d Dst_Mode:%d "
@@ -1040,7 +1040,7 @@ intel_ioapic_set_affinity(struct irq_data *data, const struct cpumask *mask,
 	 * Atomically updates the IRTE with the new destination, vector
 	 * and flushes the interrupt entry cache.
 	 */
-	modify_irte(irq, &irte);
+	modify_irte(irq_2_iommu(irq), &irte);
 
 	/*
 	 * After this point, all the interrupts will start arriving
@@ -1076,7 +1076,7 @@ static void intel_compose_msi_msg(struct pci_dev *pdev,
 	else
 		set_hpet_sid(&irte, hpet_id);
 
-	modify_irte(irq, &irte);
+	modify_irte(irq_2_iommu(irq), &irte);
 
 	msg->address_hi = MSI_ADDR_BASE_HI;
 	msg->data = sub_handle;
@@ -1103,7 +1103,7 @@ static int intel_msi_alloc_irq(struct pci_dev *dev, int irq, int nvec)
 		       "Unable to map PCI %s to iommu\n", pci_name(dev));
 		index = -ENOENT;
 	} else {
-		index = alloc_irte(iommu, irq, nvec);
+		index = alloc_irte(iommu, irq, irq_2_iommu(irq), nvec);
 		if (index < 0) {
 			printk(KERN_ERR
 			       "Unable to allocate %d IRTE for PCI %s\n",
@@ -1147,7 +1147,7 @@ static int intel_alloc_hpet_msi(unsigned int irq, unsigned int id)
 	down_read(&dmar_global_lock);
 	iommu = map_hpet_to_ir(id);
 	if (iommu) {
-		index = alloc_irte(iommu, irq, 1);
+		index = alloc_irte(iommu, irq, irq_2_iommu(irq), 1);
 		if (index >= 0)
 			ret = 0;
 	}
Signature · 1 line
1.7.10.4
author
#18 lore raw

PATCH v3 12/24 iommu/vt-d: Enhance Intel IR driver to suppport hierarchy irqdomain

In reply to Jiang Liu

Enhance Intel interrupt remapping driver to support hierarchy irqdomain, it will simplify the code eventually. It also implements intel_ir_chip to support stacked irq_chip.

---
 drivers/iommu/intel_irq_remapping.c |  356 +++++++++++++++++++++++++++++++++--
 include/linux/intel-iommu.h         |    4 +
 2 files changed, 342 insertions(+), 18 deletions(-)
drivers/iommu/intel_irq_remapping.c +338 -18
index 5acad492701e..fdaa15026909 100644
@@ -8,6 +8,7 @@
 #include <linux/irq.h>
 #include <linux/intel-iommu.h>
 #include <linux/acpi.h>
+#include <linux/irqdomain.h>
 #include <asm/io_apic.h>
 #include <asm/smp.h>
 #include <asm/cpu.h>
@@ -31,6 +32,15 @@ struct hpet_scope {
 	unsigned int devfn;
 };
 
+struct intel_ir_data {
+	struct irq_2_iommu			irq_2_iommu;
+	struct irte				irte_entry;
+	union {
+		struct msi_msg			msi_entry;
+		struct IR_IO_APIC_route_entry	ioapic_entry;
+	};
+};
+
 #define IR_X2APIC_MODE(mode) (mode ? (1 << 11) : 0)
 #define IRTE_DEST(dest) ((x2apic_mode) ? dest : dest << 8)
 
@@ -50,6 +60,7 @@ static int ir_ioapic_num, ir_hpet_num;
  * the dmar_global_lock.
  */
 static DEFINE_RAW_SPINLOCK(irq_2_ir_lock);
+static struct irq_domain_ops intel_ir_domain_ops;
 
 static int __init parse_ioapics_under_ir(void);
 
@@ -263,7 +274,7 @@ static int free_irte(int irq)
 	unsigned long flags;
 	int rc;
 
-	if (!irq_iommu)
+	if (!irq_iommu || irq_iommu->iommu == NULL)
 		return -1;
 
 	raw_spin_lock_irqsave(&irq_2_ir_lock, flags);
@@ -480,36 +491,47 @@ static int intel_setup_irq_remapping(struct intel_iommu *iommu, int mode)
 	struct page *pages;
 	unsigned long *bitmap;
 
-	ir_table = iommu->ir_table = kzalloc(sizeof(struct ir_table),
-					     GFP_ATOMIC);
-
-	if (!iommu->ir_table)
+	ir_table = kzalloc(sizeof(struct ir_table), GFP_ATOMIC);
+	if (!ir_table)
 		return -ENOMEM;
 
 	pages = alloc_pages_node(iommu->node, GFP_ATOMIC | __GFP_ZERO,
 				 INTR_REMAP_PAGE_ORDER);
-
 	if (!pages) {
 		pr_err("IR%d: failed to allocate pages of order %d\n",
 		       iommu->seq_id, INTR_REMAP_PAGE_ORDER);
-		kfree(iommu->ir_table);
-		return -ENOMEM;
+		goto out_free_table;
 	}
 
 	bitmap = kcalloc(BITS_TO_LONGS(INTR_REMAP_TABLE_ENTRIES),
 			 sizeof(long), GFP_ATOMIC);
 	if (bitmap == NULL) {
 		pr_err("IR%d: failed to allocate bitmap\n", iommu->seq_id);
-		__free_pages(pages, INTR_REMAP_PAGE_ORDER);
-		kfree(ir_table);
-		return -ENOMEM;
+		goto out_free_pages;
+	}
+
+	iommu->ir_domain = irq_domain_add_linear(NULL, INTR_REMAP_TABLE_ENTRIES,
+						 &intel_ir_domain_ops, iommu);
+	if (!iommu->ir_domain) {
+		pr_err("IR%d: failed to allocate irqdomain\n", iommu->seq_id);
+		goto out_free_bitmap;
 	}
+	iommu->ir_domain->parent = arch_get_ir_parent_domain();
+	iommu->ir_msi_domain = arch_create_msi_irq_domain(iommu->ir_domain);
 
 	ir_table->base = page_address(pages);
 	ir_table->bitmap = bitmap;
-
+	iommu->ir_table = ir_table;
 	iommu_set_irq_remapping(iommu, mode);
 	return 0;
+
+out_free_bitmap:
+	kfree(bitmap);
+out_free_pages:
+	__free_pages(pages, INTR_REMAP_PAGE_ORDER);
+out_free_table:
+	kfree(ir_table);
+	return -ENOMEM;
 }
 
 /*
@@ -1013,12 +1035,6 @@ intel_ioapic_set_affinity(struct irq_data *data, const struct cpumask *mask,
 	struct irte irte;
 	int err;
 
-	if (!config_enabled(CONFIG_SMP))
-		return -EINVAL;
-
-	if (!cpumask_intersects(mask, cpu_online_mask))
-		return -EINVAL;
-
 	if (get_irte(irq, &irte))
 		return -EBUSY;
 
@@ -1156,6 +1172,72 @@ static int intel_alloc_hpet_msi(unsigned int irq, unsigned int id)
 	return ret;
 }
 
+static struct irq_domain *intel_get_ir_irq_domain(struct irq_alloc_info *info)
+{
+	struct intel_iommu *iommu = NULL;
+
+	if (!info)
+		return NULL;
+
+	switch (info->type) {
+	case X86_IRQ_ALLOC_TYPE_IOAPIC:
+		iommu = map_ioapic_to_ir(info->ioapic_id);
+		break;
+	case X86_IRQ_ALLOC_TYPE_HPET:
+		iommu = map_hpet_to_ir(info->hpet_id);
+		break;
+	case X86_IRQ_ALLOC_TYPE_MSI:
+	case X86_IRQ_ALLOC_TYPE_MSIX:
+		iommu = map_dev_to_ir(info->msi_dev);
+		break;
+	default:
+		BUG_ON(1);
+		break;
+	}
+
+	return iommu ? iommu->ir_domain : NULL;
+}
+
+static struct irq_domain *intel_get_irq_domain(struct irq_alloc_info *info)
+{
+	struct intel_iommu *iommu;
+
+	if (!info)
+		return NULL;
+
+	switch (info->type) {
+	case X86_IRQ_ALLOC_TYPE_MSI:
+	case X86_IRQ_ALLOC_TYPE_MSIX:
+		iommu = map_dev_to_ir(info->msi_dev);
+		if (iommu)
+			return iommu->ir_msi_domain;
+		break;
+	default:
+		break;
+	}
+
+	return NULL;
+}
+
+static int intel_get_ioapic_entry(struct irq_data *irq_data,
+				  struct IR_IO_APIC_route_entry *entry)
+{
+	struct intel_ir_data *ir_data = irq_data->chip_data;
+
+	*entry = ir_data->ioapic_entry;
+
+	return 0;
+}
+
+static int intel_get_msi_entry(struct irq_data *irq_data, struct msi_msg *msg)
+{
+	struct intel_ir_data *ir_data = irq_data->chip_data;
+
+	*msg = ir_data->msi_entry;
+
+	return 0;
+}
+
 struct irq_remap_ops intel_irq_remap_ops = {
 	.supported		= intel_irq_remapping_supported,
 	.prepare		= dmar_table_init,
@@ -1170,4 +1252,242 @@ struct irq_remap_ops intel_irq_remap_ops = {
 	.msi_alloc_irq		= intel_msi_alloc_irq,
 	.msi_setup_irq		= intel_msi_setup_irq,
 	.alloc_hpet_msi		= intel_alloc_hpet_msi,
+	.get_ir_irq_domain	= intel_get_ir_irq_domain,
+	.get_irq_domain		= intel_get_irq_domain,
+	.get_ioapic_entry	= intel_get_ioapic_entry,
+	.get_msi_entry		= intel_get_msi_entry,
+};
+
+/*
+ * Migrate the IO-APIC irq in the presence of intr-remapping.
+ *
+ * For both level and edge triggered, irq migration is a simple atomic
+ * update(of vector and cpu destination) of IRTE and flush the hardware cache.
+ *
+ * For level triggered, we eliminate the io-apic RTE modification (with the
+ * updated vector information), by using a virtual vector (io-apic pin number).
+ * Real vector that is used for interrupting cpu will be coming from
+ * the interrupt-remapping table entry.
+ *
+ * As the migration is a simple atomic update of IRTE, the same mechanism
+ * is used to migrate MSI irq's in the presence of interrupt-remapping.
+ */
+static int
+intel_ir_set_affinity(struct irq_data *data, const struct cpumask *mask,
+		      bool force)
+{
+	struct intel_ir_data *ir_data = data->chip_data;
+	struct irte *irte = &ir_data->irte_entry;
+	struct irq_cfg *cfg = irqd_cfg(data);
+	struct irq_data *parent = data->parent_data;
+	int ret;
+
+	ret = parent->chip->irq_set_affinity(parent, mask, force);
+	if (ret < 0)
+		return ret;
+
+	/*
+	 * Atomically updates the IRTE with the new destination, vector
+	 * and flushes the interrupt entry cache.
+	 */
+	irte->vector = cfg->vector;
+	irte->dest_id = IRTE_DEST(cfg->dest_apicid);
+	modify_irte(&ir_data->irq_2_iommu, irte);
+
+	/*
+	 * After this point, all the interrupts will start arriving
+	 * at the new destination. So, time to cleanup the previous
+	 * vector allocation.
+	 */
+	if (cfg->move_in_progress)
+		send_cleanup_vector(cfg);
+
+	return ret;
+}
+
+static struct irq_chip intel_ir_chip = {
+	.irq_ack = ir_ack_apic_edge,
+	.irq_set_affinity = intel_ir_set_affinity,
+};
+
+static void intel_irq_remapping_prepare_irte(struct intel_ir_data *data,
+					     struct irq_cfg *irq_cfg,
+					     struct irq_alloc_info *info,
+					     int index, int sub_handle)
+{
+	struct irte *irte = &data->irte_entry;
+	struct IR_IO_APIC_route_entry *entry = &data->ioapic_entry;
+	struct msi_msg *msg = &data->msi_entry;
+
+	prepare_irte(irte, irq_cfg->vector, irq_cfg->dest_apicid);
+	switch (info->type) {
+	case X86_IRQ_ALLOC_TYPE_IOAPIC:
+		/* Set source-id of interrupt request */
+		set_ioapic_sid(irte, info->ioapic_id);
+		apic_printk(APIC_VERBOSE, KERN_DEBUG "IOAPIC[%d]: Set IRTE entry (P:%d FPD:%d Dst_Mode:%d Redir_hint:%d Trig_Mode:%d Dlvry_Mode:%X Avail:%X Vector:%02X Dest:%08X SID:%04X SQ:%X SVT:%X)\n",
+			info->ioapic_id, irte->present, irte->fpd,
+			irte->dst_mode, irte->redir_hint,
+			irte->trigger_mode, irte->dlvry_mode,
+			irte->avail, irte->vector, irte->dest_id,
+			irte->sid, irte->sq, irte->svt);
+
+		memset(entry, 0, sizeof(*entry));
+		entry->index2	= (index >> 15) & 0x1;
+		entry->zero	= 0;
+		entry->format	= 1;
+		entry->index	= (index & 0x7fff);
+		/*
+		 * IO-APIC RTE will be configured with virtual vector.
+		 * irq handler will do the explicit EOI to the io-apic.
+		 */
+		entry->vector	= info->ioapic_pin;
+		entry->mask	= 0;			/* enable IRQ */
+		entry->trigger	= info->ioapic_trigger;
+		entry->polarity	= info->ioapic_polarity;
+		if (info->ioapic_trigger)
+			entry->mask = 1; /* Mask level triggered irqs. */
+		break;
+
+	case X86_IRQ_ALLOC_TYPE_HPET:
+	case X86_IRQ_ALLOC_TYPE_MSI:
+	case X86_IRQ_ALLOC_TYPE_MSIX:
+		if (info->type == X86_IRQ_ALLOC_TYPE_HPET)
+			set_hpet_sid(irte, info->hpet_id);
+		else
+			set_msi_sid(irte, info->msi_dev);
+
+		msg->address_hi = MSI_ADDR_BASE_HI;
+		msg->data = sub_handle;
+		msg->address_lo = MSI_ADDR_BASE_LO | MSI_ADDR_IR_EXT_INT |
+				  MSI_ADDR_IR_SHV |
+				  MSI_ADDR_IR_INDEX1(index) |
+				  MSI_ADDR_IR_INDEX2(index);
+		break;
+
+	default:
+		BUG_ON(1);
+		break;
+	}
+}
+
+static void intel_free_irq_resources(struct irq_domain *domain,
+				     unsigned int virq, unsigned int nr_irqs)
+{
+	struct irq_data *irq_data;
+	struct intel_ir_data *data;
+	struct irq_2_iommu *irq_iommu;
+	unsigned long flags;
+	int i;
+
+	for (i = 0; i < nr_irqs; i++) {
+		irq_data = irq_domain_get_irq_data(domain, virq  + i);
+		if (irq_data && irq_data->chip_data) {
+			data = irq_data->chip_data;
+			irq_iommu = &data->irq_2_iommu;
+			raw_spin_lock_irqsave(&irq_2_ir_lock, flags);
+			clear_entries(irq_iommu);
+			raw_spin_unlock_irqrestore(&irq_2_ir_lock, flags);
+			irq_domain_reset_irq_data(irq_data);
+			kfree(data);
+		}
+	}
+}
+
+static int intel_irq_remapping_alloc(struct irq_domain *domain,
+				     unsigned int virq, unsigned int nr_irqs,
+				     void *arg)
+{
+	struct intel_iommu *iommu = domain->host_data;
+	struct irq_alloc_info *info = arg;
+	struct intel_ir_data *data;
+	struct irq_data *irq_data;
+	struct irq_cfg *irq_cfg;
+	int i, ret, index;
+
+	if (!info || !iommu)
+		return -EINVAL;
+	if (nr_irqs > 1 && info->type != X86_IRQ_ALLOC_TYPE_MSI &&
+	    info->type != X86_IRQ_ALLOC_TYPE_MSIX)
+		return -EINVAL;
+
+	ret = irq_domain_alloc_irqs_parent(domain, virq, nr_irqs, arg);
+	if (ret < 0)
+		return ret;
+
+	ret = -ENOMEM;
+	data = kzalloc(sizeof(*data), GFP_KERNEL);
+	if (!data)
+		goto out_free_parent;
+
+	down_read(&dmar_global_lock);
+	index = alloc_irte(iommu, virq, &data->irq_2_iommu, nr_irqs);
+	up_read(&dmar_global_lock);
+	if (index < 0) {
+		pr_warn("Failed to allocate IRTE\n");
+		kfree(data);
+		goto out_free_parent;
+	}
+
+	for (i = 0; i < nr_irqs; i++) {
+		irq_data = irq_domain_get_irq_data(domain, virq + i);
+		irq_cfg = irqd_cfg(irq_data);
+		if (!irq_data || !irq_cfg) {
+			ret = -EINVAL;
+			goto out_free_data;
+		}
+
+		if (i > 0) {
+			data = kzalloc(sizeof(*data), GFP_KERNEL);
+			if (!data)
+				goto out_free_data;
+		}
+		irq_data->hwirq = (index << 16) + i;
+		irq_data->chip_data = data;
+		irq_data->chip = &intel_ir_chip;
+		intel_irq_remapping_prepare_irte(data, irq_cfg, info, index, i);
+		irq_set_status_flags(virq + i, IRQ_MOVE_PCNTXT);
+	}
+	return 0;
+
+out_free_data:
+	intel_free_irq_resources(domain, virq, i);
+out_free_parent:
+	irq_domain_free_irqs_parent(domain, virq, nr_irqs);
+	return ret;
+}
+
+static void intel_irq_remapping_free(struct irq_domain *domain,
+				     unsigned int virq, unsigned int nr_irqs)
+{
+	intel_free_irq_resources(domain, virq, nr_irqs);
+	irq_domain_free_irqs_parent(domain, virq, nr_irqs);
+}
+
+static int intel_irq_remapping_activate(struct irq_domain *domain,
+					struct irq_data *irq_data)
+{
+	struct intel_ir_data *data = irq_data->chip_data;
+
+	modify_irte(&data->irq_2_iommu, &data->irte_entry);
+
+	return 0;
+}
+
+static int intel_irq_remapping_deactivate(struct irq_domain *domain,
+					  struct irq_data *irq_data)
+{
+	struct intel_ir_data *data = irq_data->chip_data;
+	struct irte entry;
+
+	memset(&entry, 0, sizeof(entry));
+	modify_irte(&data->irq_2_iommu, &entry);
+
+	return 0;
+}
+
+static struct irq_domain_ops intel_ir_domain_ops = {
+	.alloc = intel_irq_remapping_alloc,
+	.free = intel_irq_remapping_free,
+	.activate = intel_irq_remapping_activate,
+	.deactivate = intel_irq_remapping_deactivate,
 };
include/linux/intel-iommu.h +4 -0
index a65208a8fe18..ecaf3a937845 100644
@@ -286,6 +286,8 @@ struct q_inval {
 
 #define INTR_REMAP_TABLE_ENTRIES	65536
 
+struct irq_domain;
+
 struct ir_table {
 	struct irte *base;
 	unsigned long *bitmap;
@@ -335,6 +337,8 @@ struct intel_iommu {
 
 #ifdef CONFIG_IRQ_REMAP
 	struct ir_table *ir_table;	/* Interrupt remapping info */
+	struct irq_domain *ir_domain;
+	struct irq_domain *ir_msi_domain;
 #endif
 	struct device	*iommu_dev; /* IOMMU-sysfs device */
 	int		node;
Signature · 1 line
1.7.10.4
author
#19 lore raw

PATCH v3 13/24 iommu/amd: Enhance AMD IR driver to suppport hierarchy irqdomain

In reply to Jiang Liu

Enhance AMD interrupt remapping driver to support hierarchy irqdomain, it will simplify the code eventually.

---
 drivers/iommu/amd_iommu.c       |  341 ++++++++++++++++++++++++++++++++++++++-
 drivers/iommu/amd_iommu_init.c  |    4 +
 drivers/iommu/amd_iommu_proto.h |    9 ++
 drivers/iommu/amd_iommu_types.h |    5 +
 4 files changed, 353 insertions(+), 6 deletions(-)
drivers/iommu/amd_iommu.c +335 -6
index 6fda7cc789eb..a3ef3407bb1b 100644
@@ -33,6 +33,7 @@
 #include <linux/export.h>
 #include <linux/irq.h>
 #include <linux/msi.h>
+#include <linux/irqdomain.h>
 #include <asm/irq_remapping.h>
 #include <asm/io_apic.h>
 #include <asm/apic.h>
@@ -3854,6 +3855,17 @@ union irte {
 	} fields;
 };
 
+struct amd_ir_data {
+	struct irq_2_irte			irq_2_irte;
+	union irte				irte_entry;
+	union {
+		struct msi_msg			msi_entry;
+		struct IR_IO_APIC_route_entry	ioapic_entry;
+	};
+};
+
+static struct irq_chip amd_ir_chip;
+
 #define DTE_IRQ_PHYS_ADDR_MASK	(((1ULL << 45)-1) << 6)
 #define DTE_IRQ_REMAP_INTCTL    (2ULL << 60)
 #define DTE_IRQ_TABLE_LEN       (8ULL << 1)
@@ -3947,7 +3959,8 @@ out_unlock:
 	return table;
 }
 
-static int alloc_irq_index(struct irq_cfg *cfg, u16 devid, int count)
+static int alloc_irq_index(struct irq_cfg *cfg, struct irq_2_irte *irte_info,
+			   u16 devid, int count)
 {
 	struct irq_remap_table *table;
 	unsigned long flags;
@@ -3969,15 +3982,12 @@ static int alloc_irq_index(struct irq_cfg *cfg, u16 devid, int count)
 			c = 0;
 
 		if (c == count)	{
-			struct irq_2_irte *irte_info;
-
 			for (; c != 0; --c)
 				table->table[index - c + 1] = IRTE_ALLOCATED;
 
 			index -= count - 1;
 
 			cfg->remapped	      = 1;
-			irte_info             = &cfg->irq_2_irte;
 			irte_info->devid      = devid;
 			irte_info->index      = index;
 
@@ -4222,7 +4232,7 @@ static int msi_alloc_irq(struct pci_dev *pdev, int irq, int nvec)
 		return -EINVAL;
 
 	devid = get_device_id(&pdev->dev);
-	index = alloc_irq_index(cfg, devid, nvec);
+	index = alloc_irq_index(cfg, &cfg->irq_2_irte, devid, nvec);
 
 	return index < 0 ? MAX_IRQS_PER_TABLE : index;
 }
@@ -4269,7 +4279,7 @@ static int alloc_hpet_msi(unsigned int irq, unsigned int id)
 	if (devid < 0)
 		return devid;
 
-	index = alloc_irq_index(cfg, devid, 1);
+	index = alloc_irq_index(cfg, &cfg->irq_2_irte, devid, 1);
 	if (index < 0)
 		return index;
 
@@ -4280,6 +4290,91 @@ static int alloc_hpet_msi(unsigned int irq, unsigned int id)
 	return 0;
 }
 
+static int get_devid(struct irq_alloc_info *info)
+{
+	int devid = -1;
+
+	switch (info->type) {
+	case X86_IRQ_ALLOC_TYPE_IOAPIC:
+		devid     = get_ioapic_devid(info->ioapic_id);
+		break;
+	case X86_IRQ_ALLOC_TYPE_HPET:
+		devid     = get_hpet_devid(info->hpet_id);
+		break;
+	case X86_IRQ_ALLOC_TYPE_MSI:
+	case X86_IRQ_ALLOC_TYPE_MSIX:
+		devid = get_device_id(&info->msi_dev->dev);
+		break;
+	default:
+		BUG_ON(1);
+		break;
+	}
+
+	return devid;
+}
+
+static struct irq_domain *get_ir_irq_domain(struct irq_alloc_info *info)
+{
+	int devid;
+	struct amd_iommu *iommu;
+
+	if (!info)
+		return NULL;
+
+	devid = get_devid(info);
+	if (devid >= 0) {
+		iommu = amd_iommu_rlookup_table[devid];
+		if (iommu)
+			return iommu->ir_domain;
+	}
+
+	return NULL;
+}
+
+static struct irq_domain *get_irq_domain(struct irq_alloc_info *info)
+{
+	int devid;
+	struct amd_iommu *iommu;
+
+	if (!info)
+		return NULL;
+
+	switch (info->type) {
+	case X86_IRQ_ALLOC_TYPE_MSI:
+	case X86_IRQ_ALLOC_TYPE_MSIX:
+		devid = get_device_id(&info->msi_dev->dev);
+		if (devid >= 0) {
+			iommu = amd_iommu_rlookup_table[devid];
+			if (iommu)
+				return iommu->msi_domain;
+		}
+		break;
+	default:
+		break;
+	}
+
+	return NULL;
+}
+
+static int get_ioapic_entry(struct irq_data *irq_data,
+				  struct IR_IO_APIC_route_entry *entry)
+{
+	struct amd_ir_data *ir_data = irq_data->chip_data;
+
+	*entry = ir_data->ioapic_entry;
+
+	return 0;
+}
+
+static int get_msi_entry(struct irq_data *irq_data, struct msi_msg *msg)
+{
+	struct amd_ir_data *ir_data = irq_data->chip_data;
+
+	*msg = ir_data->msi_entry;
+
+	return 0;
+}
+
 struct irq_remap_ops amd_iommu_irq_ops = {
 	.supported		= amd_iommu_supported,
 	.prepare		= amd_iommu_prepare,
@@ -4294,5 +4389,239 @@ struct irq_remap_ops amd_iommu_irq_ops = {
 	.msi_alloc_irq		= msi_alloc_irq,
 	.msi_setup_irq		= msi_setup_irq,
 	.alloc_hpet_msi		= alloc_hpet_msi,
+	.get_ir_irq_domain	= get_ir_irq_domain,
+	.get_irq_domain		= get_irq_domain,
+	.get_ioapic_entry	= get_ioapic_entry,
+	.get_msi_entry		= get_msi_entry,
 };
+
+static void irq_remapping_prepare_irte(struct amd_ir_data *data,
+				       struct irq_cfg *irq_cfg,
+				       struct irq_alloc_info *info,
+				       int devid, int index, int sub_handle)
+{
+	union irte *irte = &data->irte_entry;
+	struct irq_2_irte *irte_info = &data->irq_2_irte;
+	struct IR_IO_APIC_route_entry *entry = &data->ioapic_entry;
+	struct msi_msg *msg = &data->msi_entry;
+
+	irq_cfg->remapped = 1;
+	data->irq_2_irte.devid = devid;
+	data->irq_2_irte.index = index + sub_handle;
+
+	/* Setup IRTE for IOMMU */
+	irte->val = 0;
+	irte->fields.vector      = irq_cfg->vector;
+	irte->fields.int_type    = apic->irq_delivery_mode;
+	irte->fields.destination = irq_cfg->dest_apicid;
+	irte->fields.dm          = apic->irq_dest_mode;
+	irte->fields.valid       = 1;
+
+	switch (info->type) {
+	case X86_IRQ_ALLOC_TYPE_IOAPIC:
+		/* Setup IOAPIC entry */
+		memset(entry, 0, sizeof(*entry));
+		entry->vector        = index;
+		entry->mask          = 0;
+		entry->trigger       = info->ioapic_trigger;
+		entry->polarity      = info->ioapic_polarity;
+		/* Mask level triggered irqs. */
+		if (info->ioapic_trigger)
+			entry->mask = 1;
+		break;
+
+	case X86_IRQ_ALLOC_TYPE_HPET:
+	case X86_IRQ_ALLOC_TYPE_MSI:
+	case X86_IRQ_ALLOC_TYPE_MSIX:
+		msg->address_hi = MSI_ADDR_BASE_HI;
+		msg->address_lo = MSI_ADDR_BASE_LO;
+		msg->data = irte_info->index;
+		break;
+
+	default:
+		BUG_ON(1);
+		break;
+	}
+}
+
+static int irq_remapping_alloc(struct irq_domain *domain, unsigned int virq,
+			       unsigned int nr_irqs, void *arg)
+{
+	struct irq_alloc_info *info = arg;
+	struct amd_ir_data *data;
+	struct irq_data *irq_data;
+	struct irq_cfg *cfg;
+	int i, ret, devid;
+	int index = -1;
+
+	if (!info)
+		return -EINVAL;
+	if (nr_irqs > 1 && info->type != X86_IRQ_ALLOC_TYPE_MSI &&
+	    info->type != X86_IRQ_ALLOC_TYPE_MSIX)
+		return -EINVAL;
+
+	devid = get_devid(info);
+	if (devid < 0)
+		return -EINVAL;
+
+	ret = irq_domain_alloc_irqs_parent(domain, virq, nr_irqs, arg);
+	if (ret < 0)
+		return ret;
+
+	ret = -ENOMEM;
+	data = kzalloc(sizeof(*data), GFP_KERNEL);
+	if (!data)
+		goto out_free_parent;
+
+	if (info->type == X86_IRQ_ALLOC_TYPE_IOAPIC) {
+		if (get_irq_table(devid, true))
+			index = info->ioapic_pin;
+		else
+			ret = -ENOMEM;
+	} else {
+		cfg = irq_cfg(virq);
+		index = alloc_irq_index(cfg, &data->irq_2_irte, devid, nr_irqs);
+	}
+	if (index < 0) {
+		pr_warn("Failed to allocate IRTE\n");
+		kfree(data);
+		goto out_free_parent;
+	}
+
+	for (i = 0; i < nr_irqs; i++) {
+		irq_data = irq_domain_get_irq_data(domain, virq + i);
+		cfg = irqd_cfg(irq_data);
+		if (!irq_data || !cfg) {
+			ret = -EINVAL;
+			goto out_free_data;
+		}
+
+		if (i > 0) {
+			data = kzalloc(sizeof(*data), GFP_KERNEL);
+			if (!data)
+				goto out_free_data;
+		}
+		irq_data->hwirq = (devid << 16) + i;
+		irq_data->chip_data = data;
+		irq_data->chip = &amd_ir_chip;
+		irq_remapping_prepare_irte(data, cfg, info, devid, index, i);
+		irq_set_status_flags(virq + i, IRQ_MOVE_PCNTXT);
+	}
+	return 0;
+
+out_free_data:
+	for (i--; i >= 0; i--) {
+		irq_data = irq_domain_get_irq_data(domain, virq + i);
+		if (irq_data->chip_data) {
+			kfree(irq_data->chip_data);
+			irq_domain_reset_irq_data(irq_data);
+		}
+	}
+	for (i = 0; i < nr_irqs; i++)
+		free_irte(devid, index + i);
+out_free_parent:
+	irq_domain_free_irqs_parent(domain, virq, nr_irqs);
+	return ret;
+}
+
+static void irq_remapping_free(struct irq_domain *domain, unsigned int virq,
+			       unsigned int nr_irqs)
+{
+	struct irq_data *irq_data;
+	struct amd_ir_data *data;
+	struct irq_2_irte *irte_info;
+	int i;
+
+	for (i = 0; i < nr_irqs; i++) {
+		irq_data = irq_domain_get_irq_data(domain, virq  + i);
+		if (irq_data && irq_data->chip_data) {
+			data = irq_data->chip_data;
+			irte_info = &data->irq_2_irte;
+			free_irte(irte_info->devid, irte_info->index);
+			irq_domain_reset_irq_data(irq_data);
+			kfree(data);
+		}
+	}
+	irq_domain_free_irqs_parent(domain, virq, nr_irqs);
+}
+
+static int irq_remapping_activate(struct irq_domain *domain,
+				  struct irq_data *irq_data)
+{
+	struct amd_ir_data *data = irq_data->chip_data;
+	struct irq_2_irte *irte_info = &data->irq_2_irte;
+
+	modify_irte(irte_info->devid, irte_info->index, data->irte_entry);
+
+	return 0;
+}
+
+static int irq_remapping_deactivate(struct irq_domain *domain,
+				    struct irq_data *irq_data)
+{
+	struct amd_ir_data *data = irq_data->chip_data;
+	struct irq_2_irte *irte_info = &data->irq_2_irte;
+	union irte entry;
+
+	entry.val = 0;
+	modify_irte(irte_info->devid, irte_info->index, data->irte_entry);
+
+	return 0;
+}
+
+static struct irq_domain_ops amd_ir_domain_ops = {
+	.alloc = irq_remapping_alloc,
+	.free = irq_remapping_free,
+	.activate = irq_remapping_activate,
+	.deactivate = irq_remapping_deactivate,
+};
+
+static int amd_ir_set_affinity(struct irq_data *data,
+			       const struct cpumask *mask, bool force)
+{
+	struct amd_ir_data *ir_data = data->chip_data;
+	struct irq_2_irte *irte_info = &ir_data->irq_2_irte;
+	struct irq_cfg *cfg = irqd_cfg(data);
+	struct irq_data *parent = data->parent_data;
+	int ret;
+
+	ret = parent->chip->irq_set_affinity(parent, mask, force);
+	if (ret < 0)
+		return ret;
+
+	/*
+	 * Atomically updates the IRTE with the new destination, vector
+	 * and flushes the interrupt entry cache.
+	 */
+	ir_data->irte_entry.fields.vector = cfg->vector;
+	ir_data->irte_entry.fields.destination = cfg->dest_apicid;
+	modify_irte(irte_info->devid, irte_info->index, ir_data->irte_entry);
+
+	/*
+	 * After this point, all the interrupts will start arriving
+	 * at the new destination. So, time to cleanup the previous
+	 * vector allocation.
+	 */
+	if (cfg->move_in_progress)
+		send_cleanup_vector(cfg);
+
+	return ret;
+}
+
+static struct irq_chip amd_ir_chip = {
+	.irq_ack = ir_ack_apic_edge,
+	.irq_set_affinity = amd_ir_set_affinity,
+};
+
+int amd_iommu_create_irq_domain(struct amd_iommu *iommu)
+{
+	iommu->ir_domain = irq_domain_add_tree(NULL, &amd_ir_domain_ops, iommu);
+	if (!iommu->ir_domain)
+		return -ENOMEM;
+
+	iommu->ir_domain->parent = arch_get_ir_parent_domain();
+	iommu->msi_domain = arch_create_msi_irq_domain(iommu->ir_domain);
+
+	return 0;
+}
 #endif
drivers/iommu/amd_iommu_init.c +4 -0
index b0522f15730f..de3390a7d345 100644
@@ -1124,6 +1124,10 @@ static int __init init_iommu_one(struct amd_iommu *iommu, struct ivhd_header *h)
 	if (ret)
 		return ret;
 
+	ret = amd_iommu_create_irq_domain(iommu);
+	if (ret)
+		return ret;
+
 	/*
 	 * Make sure IOMMU is not considered to translate itself. The IVRS
 	 * table tells us so, but this is a lie!
drivers/iommu/amd_iommu_proto.h +9 -0
index 95ed6deae47f..612a22192fa0 100644
@@ -63,6 +63,15 @@ extern u8 amd_iommu_pc_get_max_counters(u16 devid);
 extern int amd_iommu_pc_get_set_reg_val(u16 devid, u8 bank, u8 cntr, u8 fxn,
 				    u64 *value, bool is_write);
 
+#ifdef CONFIG_IRQ_REMAP
+extern int amd_iommu_create_irq_domain(struct amd_iommu *iommu);
+#else
+static inline int amd_iommu_create_irq_domain(struct amd_iommu *iommu)
+{
+	return 0;
+}
+#endif
+
 #define PPR_SUCCESS			0x0
 #define PPR_INVALID			0x1
 #define PPR_FAILURE			0xf
drivers/iommu/amd_iommu_types.h +5 -0
index cec51a8ba844..ef12d74a03fe 100644
@@ -392,6 +392,7 @@ struct amd_iommu_fault {
 
 
 struct iommu_domain;
+struct irq_domain;
 
 /*
  * This structure contains generic data for  IOMMU protection domains
@@ -574,6 +575,10 @@ struct amd_iommu {
 	/* The maximum PC banks and counters/bank (PCSup=1) */
 	u8 max_banks;
 	u8 max_counters;
+#ifdef CONFIG_IRQ_REMAP
+	struct irq_domain *ir_domain;
+	struct irq_domain *msi_domain;
+#endif
 };
 
 struct devid_map {
Signature · 1 line
1.7.10.4
author
#20 lore raw

PATCH v3 14/24 x86, hpet: Enhance HPET IRQ to support hierarchy irqdomain

In reply to Jiang Liu

Enhance HPET code to support hierarchy irqdomain, it helps to make the architecture more clear.

---
 arch/x86/include/asm/hpet.h |    7 +-
 arch/x86/kernel/apic/msi.c  |  168 ++++++++++++++++++++++++++++++++++++++-----
 arch/x86/kernel/hpet.c      |   57 ++++-----------
 3 files changed, 171 insertions(+), 61 deletions(-)
arch/x86/include/asm/hpet.h +6 -1
index 36f7125945e3..e87e9faf87a9 100644
@@ -74,11 +74,16 @@ extern unsigned int hpet_readl(unsigned int a);
 extern void force_hpet_resume(void);
 
 struct irq_data;
+struct hpet_dev;
+struct irq_domain;
+
 extern void hpet_msi_unmask(struct irq_data *data);
 extern void hpet_msi_mask(struct irq_data *data);
-struct hpet_dev;
 extern void hpet_msi_write(struct hpet_dev *hdev, struct msi_msg *msg);
 extern void hpet_msi_read(struct hpet_dev *hdev, struct msi_msg *msg);
+extern struct irq_domain *hpet_create_irq_domain(int hpet_id);
+extern int hpet_assign_irq(struct irq_domain *domain,
+			   struct hpet_dev *dev, int dev_num);
 
 #ifdef CONFIG_PCI_MSI
 extern int default_setup_hpet_msi(unsigned int irq, unsigned int id);
arch/x86/kernel/apic/msi.c +151 -17

205 lines not on this page · raw message

arch/x86/kernel/hpet.c +14 -43

114 lines not on this page · raw message

Signature · 1 line
1.7.10.4
author
#21 lore raw

PATCH v3 15/24 x86, MSI: Use hierarchy irqdomain to manage MSI interrupts

In reply to Jiang Liu

Enhance MSI code to support hierarchy irqdomain, it helps to make the architecture more clear.

---
 arch/x86/include/asm/hw_irq.h        |    9 +-
 arch/x86/include/asm/irq_remapping.h |    6 +-
 arch/x86/kernel/apic/msi.c           |  237 ++++++++++++++++++++++++++++------
 arch/x86/kernel/apic/vector.c        |    2 +
 drivers/iommu/irq_remapping.c        |    1 -
 5 files changed, 209 insertions(+), 46 deletions(-)
arch/x86/include/asm/hw_irq.h +8 -1
index 545460d470bd..1ff7a7f61bf9 100644
@@ -110,9 +110,10 @@ struct irq_2_irte {
 };
 #endif	/* CONFIG_IRQ_REMAP */
 
+struct irq_domain;
+
 #ifdef	CONFIG_X86_LOCAL_APIC
 struct irq_data;
-struct irq_domain;
 struct pci_dev;
 struct msi_desc;
 
@@ -200,6 +201,12 @@ static inline void lock_vector_lock(void) {}
 static inline void unlock_vector_lock(void) {}
 #endif	/* CONFIG_X86_LOCAL_APIC */
 
+#ifdef	CONFIG_PCI_MSI
+extern void arch_init_msi_domain(struct irq_domain *domain);
+#else
+static inline void arch_init_msi_domain(struct irq_domain *domain) { }
+#endif
+
 /* Statistics */
 extern atomic_t irq_err_count;
 extern atomic_t irq_mis_count;
arch/x86/include/asm/irq_remapping.h +1 -5
index f5f624529386..ea71f86423ee 100644
@@ -75,11 +75,7 @@ extern void irq_remapping_print_chip(struct irq_data *data, struct seq_file *p);
  * Create MSI/MSIx irqdomain for interrupt remapping device, use @parent as
  * parent irqdomain.
  */
-static inline struct irq_domain *
-arch_create_msi_irq_domain(struct irq_domain *parent)
-{
-	return NULL;
-}
+extern struct irq_domain *arch_create_msi_irq_domain(struct irq_domain *parent);
 
 /* Get parent irqdomain for interrupt remapping irqdomain */
 static inline struct irq_domain *arch_get_ir_parent_domain(void)
arch/x86/kernel/apic/msi.c +198 -39

306 lines not on this page · raw message

arch/x86/kernel/apic/vector.c +2 -0
index c97848125895..61c55d0aeaec 100644
@@ -359,6 +359,8 @@ int __init arch_early_irq_init(void)
 	BUG_ON(x86_vector_domain == NULL);
 	irq_set_default_host(x86_vector_domain);
 
+	arch_init_msi_domain(x86_vector_domain);
+
 	return arch_early_ioapic_init();
 }
 
drivers/iommu/irq_remapping.c +0 -1
index 3a7f62c8becb..f92d49110f83 100644
@@ -179,7 +179,6 @@ static void __init irq_remapping_modify_x86_ops(void)
 	x86_io_apic_ops.set_affinity	= set_remapped_irq_affinity;
 	x86_io_apic_ops.setup_entry	= setup_ioapic_remapped_entry;
 	x86_io_apic_ops.eoi_ioapic_pin	= eoi_ioapic_pin_remapped;
-	x86_msi.setup_msi_irqs		= irq_remapping_setup_msi_irqs;
 	x86_msi.setup_hpet_msi		= setup_hpet_msi_remapped;
 	x86_msi.compose_msi_msg		= compose_remapped_msi_msg;
 }
Signature · 1 line
1.7.10.4
#22 lore raw

Re: [Patch Part2 v3 15/24] x86, MSI: Use hierarchy irqdomain to manage MSI interrupts

Quoted 10 lines · Jiang Liu
arch/x86/kernel/apic/msi.c +5 -4
+static int msi_set_affinity(struct irq_data *data, const struct cpumask *mask,
+			    bool force)
+{
+	struct irq_data *parent = data->parent_data;
+	int ret;
 
-	msg.data &= ~MSI_DATA_VECTOR_MASK;
-	msg.data |= MSI_DATA_VECTOR(cfg->vector);
-	msg.address_lo &= ~MSI_ADDR_DEST_ID_MASK;
-	msg.address_lo |= MSI_ADDR_DEST_ID(dest);

arch/x86/kernel/apic/msi.c static int msi_compose_msg(struct pci_dev *pdev, unsigned int irq, · line 122

Quoted 10 lines
arch/x86/kernel/apic/msi.c +8 -1
+	ret = parent->chip->irq_set_affinity(parent, mask, force);
+	/* No need to reprogram MSI registers if interrupt is remapped */
+	if (ret >= 0 && !msi_irq_remapped(data)) {
+		struct msi_msg msg;
 
-	__write_msi_msg(data->msi_desc, &msg);
+		__get_cached_msi_msg(data->msi_desc, &msg);
+		msi_update_msg(&msg, data);
+		__write_msi_msg(data->msi_desc, &msg);
+	}

I'm not too happy about the msi_irq_remapped() conditional here. It violates the whole concept of domain stacking somewhat.

A better separation would be to add a callback to the irq chip:

  	void (*irq_write_msi_msg)(struct irq_data *data, struct msi_desc *msi_desc, bool cached);

and change this code to:

    	if (ret >= 0)
	   	parent->chip->irq_write_msi_msg(parent, data->msi-desc, true);

arch/x86/kernel/apic/msi.c static int msi_compose_msg(struct pci_dev *pdev, unsigned int irq, · line 125

Quoted 3 lines
arch/x86/kernel/apic/msi.c +1 -1
-	return IRQ_SET_MASK_OK_NOCOPY;
+	return ret;
 }

And do the same here:

arch/x86/kernel/apic/msi.c static struct irq_chip msi_chip = { · line 202

Quoted 13 lines
arch/x86/kernel/apic/msi.c +13 -0
+static int msi_domain_activate(struct irq_domain *domain,
+			       struct irq_data *irq_data)
+{
+	struct msi_msg msg;
+	struct irq_cfg *cfg = irqd_cfg(irq_data);
+
+	/*
+	 * irq_data->chip_data is MSI/MSIx offset.
+	 * MSI-X message is written per-IRQ, the offset is always 0.
+	 * MSI message denotes a contiguous group of IRQs, written for 0th IRQ.
+	 */
+	if (irq_data->chip_data)
+		return 0;
        parent->chip->irq_write_msi_msg(parent, data->msi_desc, false);  		

arch/x86/kernel/apic/msi.c static struct irq_chip msi_chip = { · line 212

Quoted 9 lines
arch/x86/kernel/apic/msi.c +9 -0
+	if (msi_irq_remapped(irq_data))
+		irq_remapping_get_msi_entry(irq_data->parent_data, &msg);
+	else
+		native_compose_msi_msg(NULL, irq_data->irq, cfg->dest_apicid,
+				       &msg, 0);
+	write_msi_msg(irq_data->irq, &msg);
+
+	return 0;
+}

And here:

arch/x86/kernel/apic/msi.c static struct irq_chip msi_chip = { · line 223

Quoted 10 lines
arch/x86/kernel/apic/msi.c +10 -0
+static int msi_domain_deactivate(struct irq_domain *domain,
+				 struct irq_data *irq_data)
+{
+	struct msi_msg msg;
+
+	if (irq_data->chip_data)
+		return 0;
+
+	memset(&msg, 0, sizeof(msg));
+	write_msi_msg(irq_data->irq, &msg);
  	parent->chip->irq_write_msi_msg(parent, NULL, false);

Review

Quoted 2 lines
(quoted diff) +2 -0
+	return 0;
+}

And let the vector and the remapping domain deal with it in their callbacks.

Quoted 25 lines
arch/x86/kernel/apic/msi.c +12 -7
@@ -166,25 +264,59 @@ int setup_msi_irq(struct pci_dev *dev, struct msi_desc *msidesc,
 
 int native_setup_msi_irqs(struct pci_dev *dev, int nvec, int type)
 {
-	struct msi_desc *msidesc;
-	int irq, ret;
+	int irq, cnt, nvec_pow2;
+	struct irq_domain *domain;
+	struct msi_desc *msidesc, *iter;
+	struct irq_alloc_info info;
+	int node = dev_to_node(&dev->dev);
 
-	/* Multiple MSI vectors only supported with interrupt remapping */
-	if (type == PCI_CAP_ID_MSI && nvec > 1)
-		return 1;
+	if (disable_apic)
+		return -ENOSYS;
 
-	list_for_each_entry(msidesc, &dev->msi_list, list) {
-		irq = irq_domain_alloc_irqs(NULL, 1, NUMA_NO_NODE, NULL);
+	init_irq_alloc_info(&info, NULL);
+	info.msi_dev = dev;
+	if (type == PCI_CAP_ID_MSI) {
+		msidesc = list_first_entry(&dev->msi_list,
+					   struct msi_desc, list);

arch/x86/kernel/apic/msi.c int setup_msi_irq(struct pci_dev *dev, struct msi_desc *msidesc, · line 290

Quoted 10 lines
arch/x86/kernel/apic/msi.c +10 -0
+		WARN_ON(!list_is_singular(&dev->msi_list));
+		WARN_ON(msidesc->irq);
+		WARN_ON(msidesc->msi_attrib.multiple);
+		WARN_ON(msidesc->nvec_used);
+		info.type = X86_IRQ_ALLOC_TYPE_MSI;
+		cnt = nvec;
+	} else {
+		info.type = X86_IRQ_ALLOC_TYPE_MSIX;
+		cnt = 1;
+	}

We have a similar issue here.

arch/x86/kernel/apic/msi.c int setup_msi_irq(struct pci_dev *dev, struct msi_desc *msidesc, · line 292

Quoted 1 line
arch/x86/kernel/apic/msi.c +1 -0
+	domain = irq_remapping_get_irq_domain(&info);

We add domain specific knowledge to the MSI implementation. Not necessary at all.

Again MSI is not an x86 problem and we really can move most of that to the core code. The above sanity checks and the distinction between MSI and MSIX can be handled in the core code. And every domain involved in the MSI chain would need a alloc_msi() callback.

So native_setup_msi_irqs() would boil down to:
+ {
+ if (disable_apic)
+ return -ENOSYS;
+
+ return irq_domain_alloc_msi(msi_domain, dev, nvec, type);
+ }

Now that core function performs the sanity checks for the MSI case. In fact it should not proceed when a warning condition is detected. Not a x86 issue at all, its true for every MSI implementation.

Then it calls down the domain allocation chain. x86_msi_domain would simply hand down to the parent domain. That would either be the remap domain or the vector domain.

The reject for the multi MSI would only be implemented in the vector domain callback, while the remap domain can handle it. Once we gain support for allocating consecutive vectors for multi-MSI in the vector domain we would not have to change any of the MSI code at all.

Thoughts?

Thanks,

tglx

author
#23 lore raw

Re: [Patch Part2 v3 15/24] x86, MSI: Use hierarchy irqdomain to manage MSI interrupts

Quoted 25 lines · Thomas Gleixner
Quoted 10 lines · Jiang Liu
(quoted diff) +5 -4
+static int msi_set_affinity(struct irq_data *data, const struct cpumask *mask,
+			    bool force)
+{
+	struct irq_data *parent = data->parent_data;
+	int ret;
 
-	msg.data &= ~MSI_DATA_VECTOR_MASK;
-	msg.data |= MSI_DATA_VECTOR(cfg->vector);
-	msg.address_lo &= ~MSI_ADDR_DEST_ID_MASK;
-	msg.address_lo |= MSI_ADDR_DEST_ID(dest);

Review

Quoted 10 lines
(quoted diff) +8 -1
+	ret = parent->chip->irq_set_affinity(parent, mask, force);
+	/* No need to reprogram MSI registers if interrupt is remapped */
+	if (ret >= 0 && !msi_irq_remapped(data)) {
+		struct msi_msg msg;
 
-	__write_msi_msg(data->msi_desc, &msg);
+		__get_cached_msi_msg(data->msi_desc, &msg);
+		msi_update_msg(&msg, data);
+		__write_msi_msg(data->msi_desc, &msg);
+	}

I'm not too happy about the msi_irq_remapped() conditional here. It violates the whole concept of domain stacking somewhat.

Quoted 9 lines

A better separation would be to add a callback to the irq chip:

  	void (*irq_write_msi_msg)(struct irq_data *data, struct msi_desc *msi_desc, bool cached);

and change this code to:

    	if (ret >= 0)
	   	parent->chip->irq_write_msi_msg(parent, data->msi-desc, true);

Hi Thomas,
Thanks for your great suggestion and I have worked out a draft
patch to achieve what you want:)
I have made following changes to irq core to get rid of remapped
irq logic from msi.c:
1) Add IRQ_SET_MASK_OK_DONE in addition to IRQ_SET_MASK_OK and IRQ_SET_MASK_OK_NOCOPY. IRQ_SET_MASK_OK_DONE is the same as IRQ_SET_MASK_OK for irq core and indicates to stacked irqchip that parent irqchips have done all work and skip any handling in descendant irqchips.
2) Add int (*irq_compose_msi_msg)(struct irq_data *data, struct msi_msg *msg) into struct irq_chip. I'm still hesitating to return void or int here. By returning void, irq_chip_compose_msi_msg() will be simpler, but it loses flexibility.

With above changes to core, we could remove all knowledge of irq remapping from msi.c and the irq remapping interfaces get simpler too. Please refer to following patch for details. The patch passes basic booting tests with irq remapping enabled. If it's OK, I will fold it into the patch set.

IOAPIC runs into the same situation, but I need some more time to find a solution:)

Regards!
Gerry

arch/x86/include/asm/irq_remapping.h +0 -8
index 68d6dfcf7d92..97dec9eadef3 100644
@@ -63,8 +63,6 @@ extern struct irq_domain *irq_remapping_get_irq_domain(
 				struct irq_alloc_info *info);
 extern int irq_remapping_get_ioapic_entry(struct irq_data *irq_data,
 					  struct IR_IO_APIC_route_entry *entry);
-extern int irq_remapping_get_msi_entry(struct irq_data *irq_data,
-				       struct msi_msg *entry);
 extern void irq_remapping_print_chip(struct irq_data *data, struct seq_file *p);
 
 /*
@@ -142,12 +140,6 @@ static inline int irq_remapping_get_ioapic_entry(struct irq_data *irq_data,
 	return -ENOSYS;
 }
 
-static inline int irq_remapping_get_msi_entry(struct irq_data *irq_data,
-					      struct msi_msg *entry)
-{
-	return -ENOSYS;
-}
-
 static inline void irq_remapping_domain_set_remapped(struct irq_domain *domain)
 {
 }
arch/x86/kernel/apic/msi.c +36 -47

202 lines not on this page · raw message

drivers/iommu/amd_iommu.c +12 -12
index 0b648772c221..8fcd1497b109 100644
@@ -4260,15 +4260,6 @@ static int get_ioapic_entry(struct irq_data *irq_data,
 	return 0;
 }
 
-static int get_msi_entry(struct irq_data *irq_data, struct msi_msg *msg)
-{
-	struct amd_ir_data *ir_data = irq_data->chip_data;
-
-	*msg = ir_data->msi_entry;
-
-	return 0;
-}
-
 struct irq_remap_ops amd_iommu_irq_ops = {
 	.supported		= amd_iommu_supported,
 	.prepare		= amd_iommu_prepare,
@@ -4282,7 +4273,6 @@ struct irq_remap_ops amd_iommu_irq_ops = {
 	.get_ir_irq_domain	= get_ir_irq_domain,
 	.get_irq_domain		= get_irq_domain,
 	.get_ioapic_entry	= get_ioapic_entry,
-	.get_msi_entry		= get_msi_entry,
 };
 
 static void irq_remapping_prepare_irte(struct amd_ir_data *data,
@@ -4475,7 +4465,7 @@ static int amd_ir_set_affinity(struct irq_data *data,
 	int ret;
 
 	ret = parent->chip->irq_set_affinity(parent, mask, force);
-	if (ret < 0)
+	if (ret < 0 || ret == IRQ_SET_MASK_OK_DONE)
 		return ret;
 
 	/*
@@ -4494,12 +4484,22 @@ static int amd_ir_set_affinity(struct irq_data *data,
 	if (cfg->move_in_progress)
 		send_cleanup_vector(cfg);
 
-	return ret;
+	return IRQ_SET_MASK_OK_DONE;
+}
+
+static int compose_msi_msg(struct irq_data *irq_data, struct msi_msg *msg)
+{
+	struct amd_ir_data *ir_data = irq_data->chip_data;
+
+	*msg = ir_data->msi_entry;
+
+	return 0;
 }
 
 static struct irq_chip amd_ir_chip = {
 	.irq_ack = ir_ack_apic_edge,
 	.irq_set_affinity = amd_ir_set_affinity,
+	.irq_compose_msi_msg = compose_msi_msg,
 };
 
 int amd_iommu_create_irq_domain(struct amd_iommu *iommu)
drivers/iommu/intel_irq_remapping.c +13 -12
index 9742011190fb..e2ea4eccf9c9 100644
@@ -1029,6 +1029,7 @@ intel_ioapic_set_affinity(struct irq_data *data, const struct cpumask *mask,
 		send_cleanup_vector(cfg);
 
 	cpumask_copy(data->affinity, mask);
+
 	return 0;
 }
 
@@ -1089,15 +1090,6 @@ static int intel_get_ioapic_entry(struct irq_data *irq_data,
 	return 0;
 }
 
-static int intel_get_msi_entry(struct irq_data *irq_data, struct msi_msg *msg)
-{
-	struct intel_ir_data *ir_data = irq_data->chip_data;
-
-	*msg = ir_data->msi_entry;
-
-	return 0;
-}
-
 struct irq_remap_ops intel_irq_remap_ops = {
 	.supported		= intel_irq_remapping_supported,
 	.prepare		= dmar_table_init,
@@ -1111,7 +1103,6 @@ struct irq_remap_ops intel_irq_remap_ops = {
 	.get_ir_irq_domain	= intel_get_ir_irq_domain,
 	.get_irq_domain		= intel_get_irq_domain,
 	.get_ioapic_entry	= intel_get_ioapic_entry,
-	.get_msi_entry		= intel_get_msi_entry,
 };
 
 /*
@@ -1139,7 +1130,7 @@ intel_ir_set_affinity(struct irq_data *data, const struct cpumask *mask,
 	int ret;
 
 	ret = parent->chip->irq_set_affinity(parent, mask, force);
-	if (ret < 0)
+	if (ret < 0 || ret == IRQ_SET_MASK_OK_DONE)
 		return ret;
 
 	/*
@@ -1158,12 +1149,22 @@ intel_ir_set_affinity(struct irq_data *data, const struct cpumask *mask,
 	if (cfg->move_in_progress)
 		send_cleanup_vector(cfg);
 
-	return ret;
+	return IRQ_SET_MASK_OK_DONE;
+}
+
+static int intel_compose_msi_msg(struct irq_data *irq_data, struct msi_msg *msg)
+{
+	struct intel_ir_data *ir_data = irq_data->chip_data;
+
+	*msg = ir_data->msi_entry;
+
+	return 0;
 }
 
 static struct irq_chip intel_ir_chip = {
 	.irq_ack = ir_ack_apic_edge,
 	.irq_set_affinity = intel_ir_set_affinity,
+	.irq_compose_msi_msg = intel_compose_msi_msg,
 };
 
 static void intel_irq_remapping_prepare_irte(struct intel_ir_data *data,
drivers/iommu/irq_remapping.c +0 -15
index 8fabc1d05f93..aa0cacc1486a 100644
@@ -310,18 +310,3 @@ int irq_remapping_get_ioapic_entry(struct irq_data *irq_data,
 {
 	return remap_ops->get_ioapic_entry(irq_data, entry);
 }
-
-/**
- * irq_remapping_get_ioapic_entry - Get MSI data rewritten by interrupt
- *				    remapping driver
- * @irq_data: irq_data associated with interrupt remapping irqdomain
- * @entry: host returned data
- *
- * Caller must make sure that the interrupt is remapped.
- * Return 0 on success, otherwise return error code
- */
-int irq_remapping_get_msi_entry(struct irq_data *irq_data,
-				struct msi_msg *entry)
-{
-	return remap_ops->get_msi_entry(irq_data, entry);
-}
drivers/iommu/irq_remapping.h +0 -3
index b01a51ed1780..df73f010c326 100644
@@ -79,9 +79,6 @@ struct irq_remap_ops {
 	/* Get IOAPIC entry content rewritten by interrupt remapping driver */
 	int (*get_ioapic_entry)(struct irq_data *,
 				struct IR_IO_APIC_route_entry *);
-
-	/*  Get MSI data rewritten by interrupt remapping driver */
-	int (*get_msi_entry)(struct irq_data *, struct msi_msg *);
 };
 
 extern struct irq_remap_ops intel_irq_remap_ops;
include/linux/irq.h +7 -0
index 0adcbbbf2e87..226449317b78 100644
@@ -113,10 +113,14 @@ enum {
  *
  * IRQ_SET_MASK_OK	- OK, core updates irq_data.affinity
  * IRQ_SET_MASK_NOCPY	- OK, chip did update irq_data.affinity
+ * IRQ_SET_MASK_OK_DONE	- Same as IRQ_SET_MASK_OK for core. Special code to
+ *			  support stacked irqchips, which indicates skipping
+ *			  all descendent irqchips.
  */
 enum {
 	IRQ_SET_MASK_OK = 0,
 	IRQ_SET_MASK_OK_NOCOPY,
+	IRQ_SET_MASK_OK_DONE,
 };
 
 struct msi_desc;
@@ -356,6 +360,8 @@ struct irq_chip {
 	int		(*irq_request_resources)(struct irq_data *data);
 	void		(*irq_release_resources)(struct irq_data *data);
 
+	int		(*irq_compose_msi_msg)(struct irq_data *data, struct msi_msg *msg);
+
 	unsigned long	flags;
 };
 
@@ -443,6 +449,7 @@ extern void handle_percpu_devid_irq(unsigned int irq, struct irq_desc *desc);
 extern void handle_bad_irq(unsigned int irq, struct irq_desc *desc);
 extern void handle_nested_irq(unsigned int irq);
 
+extern int irq_chip_compose_msi_msg(struct irq_data *data, struct msi_msg *msg);
 #ifdef	CONFIG_IRQ_DOMAIN_HIERARCHY
 extern void irq_chip_ack_parent(struct irq_data *data);
 extern int irq_chip_retrigger_hierarchy(struct irq_data *data);
kernel/irq/chip.c +12 -0
index 12f3e72449eb..50cbbff7e27f 100644
@@ -867,3 +867,15 @@ int irq_chip_retrigger_hierarchy(struct irq_data *data)
 	return -ENOSYS;
 }
 #endif
+
+int irq_chip_compose_msi_msg(struct irq_data *data, struct msi_msg *msg)
+{
+	int ret = -ENOSYS;
+
+	if (data->parent_data)
+		ret = irq_chip_compose_msi_msg(data->parent_data, msg);
+	if (ret == -ENOSYS && data->chip && data->chip->irq_compose_msi_msg)
+		ret = data->chip->irq_compose_msi_msg(data, msg);
+
+	return ret;
+}
kernel/irq/manage.c +2 -0
index 0a9104b4608b..80692373abd6 100644
@@ -183,6 +183,7 @@ int irq_do_set_affinity(struct irq_data *data, const struct cpumask *mask,
 	ret = chip->irq_set_affinity(data, mask, force);
 	switch (ret) {
 	case IRQ_SET_MASK_OK:
+	case IRQ_SET_MASK_OK_DONE:
 		cpumask_copy(data->affinity, mask);
 	case IRQ_SET_MASK_OK_NOCOPY:
 		irq_set_thread_affinity(desc);
@@ -600,6 +601,7 @@ int __irq_set_trigger(struct irq_desc *desc, unsigned int irq,
 
 	switch (ret) {
 	case IRQ_SET_MASK_OK:
+	case IRQ_SET_MASK_OK_DONE:
 		irqd_clear(&desc->irq_data, IRQD_TRIGGER_MASK);
 		irqd_set(&desc->irq_data, flags);
 
#24 lore raw

Re: [Patch Part2 v3 15/24] x86, MSI: Use hierarchy irqdomain to manage MSI interrupts

Quoted 35 lines · Jiang Liu
Quoted 25 lines · Thomas Gleixner
Quoted 10 lines · Jiang Liu
(quoted diff) +5 -4
+static int msi_set_affinity(struct irq_data *data, const struct cpumask *mask,
+			    bool force)
+{
+	struct irq_data *parent = data->parent_data;
+	int ret;
 
-	msg.data &= ~MSI_DATA_VECTOR_MASK;
-	msg.data |= MSI_DATA_VECTOR(cfg->vector);
-	msg.address_lo &= ~MSI_ADDR_DEST_ID_MASK;
-	msg.address_lo |= MSI_ADDR_DEST_ID(dest);

Review

Quoted 10 lines
(quoted diff) +8 -1
+	ret = parent->chip->irq_set_affinity(parent, mask, force);
+	/* No need to reprogram MSI registers if interrupt is remapped */
+	if (ret >= 0 && !msi_irq_remapped(data)) {
+		struct msi_msg msg;
 
-	__write_msi_msg(data->msi_desc, &msg);
+		__get_cached_msi_msg(data->msi_desc, &msg);
+		msi_update_msg(&msg, data);
+		__write_msi_msg(data->msi_desc, &msg);
+	}

I'm not too happy about the msi_irq_remapped() conditional here. It violates the whole concept of domain stacking somewhat.

Quoted 9 lines

A better separation would be to add a callback to the irq chip:

  	void (*irq_write_msi_msg)(struct irq_data *data, struct msi_desc *msi_desc, bool cached);

and change this code to:

    	if (ret >= 0)
	   	parent->chip->irq_write_msi_msg(parent, data->msi-desc, true);
Quoted 14 lines

Hi Thomas,
Thanks for your great suggestion and I have worked out a draft
patch to achieve what you want:)
I have made following changes to irq core to get rid of remapped
irq logic from msi.c:
1) Add IRQ_SET_MASK_OK_DONE in addition to IRQ_SET_MASK_OK and IRQ_SET_MASK_OK_NOCOPY. IRQ_SET_MASK_OK_DONE is the same as IRQ_SET_MASK_OK for irq core and indicates to stacked irqchip that parent irqchips have done all work and skip any handling in descendant irqchips.
2) Add int (*irq_compose_msi_msg)(struct irq_data *data, struct msi_msg *msg) into struct irq_chip. I'm still hesitating to return void or int here. By returning void, irq_chip_compose_msi_msg() will be simpler, but it loses flexibility.

void should be sufficient. If the chip advertises this function it better can provide a proper msi msg :)

Quoted 5 lines

With above changes to core, we could remove all knowledge of irq remapping from msi.c and the irq remapping interfaces get simpler too. Please refer to following patch for details. The patch passes basic booting tests with irq remapping enabled. If it's OK, I will fold it into the patch set.

Yes. That looks reasonable.

Quoted 2 lines

IOAPIC runs into the same situation, but I need some more time to find a solution:)

I'm sure you will!

Thanks,

tglx

author
#25 lore raw

Re: [Patch Part2 v3 15/24] x86, MSI: Use hierarchy irqdomain to manage MSI interrupts

Quoted 24 lines · Thomas Gleixner
Quoted 6 lines

Hi Thomas,
Thanks for your great suggestion and I have worked out a draft
patch to achieve what you want:)
I have made following changes to irq core to get rid of remapped
irq logic from msi.c:
1) Add IRQ_SET_MASK_OK_DONE in addition to IRQ_SET_MASK_OK and

Quoted 8 lines

IRQ_SET_MASK_OK_NOCOPY. IRQ_SET_MASK_OK_DONE is the same as IRQ_SET_MASK_OK for irq core and indicates to stacked irqchip that parent irqchips have done all work and skip any handling in descendant irqchips.
2) Add int (*irq_compose_msi_msg)(struct irq_data *data, struct msi_msg *msg) into struct irq_chip. I'm still hesitating to return void or int here. By returning void, irq_chip_compose_msi_msg() will be simpler, but it loses flexibility.

void should be sufficient. If the chip advertises this function it better can provide a proper msi msg :)

Quoted 5 lines

With above changes to core, we could remove all knowledge of irq remapping from msi.c and the irq remapping interfaces get simpler too. Please refer to following patch for details. The patch passes basic booting tests with irq remapping enabled. If it's OK, I will fold it into the patch set.

Quoted 6 lines

Yes. That looks reasonable.

Quoted 2 lines

IOAPIC runs into the same situation, but I need some more time to find a solution:)

I'm sure you will!

Hi Thomas,
I have reviewed IOAPIC related change again, but the conclusion
may not be what you expect:(
Currently IOAPIC driver detects IRQ remapping for following
tasks:
1) Issue different EOI command to IOAPIC chip for unammped and remapped interrupts. It uses pin number instead of vector number for remapped interrupts.
2) Print different format for IOAPIC entries for unmapped and remapped interrupts.
3) ioapic_ack_level() uses different method for unmapped and remapped interrupts
4) create different IOAPIC entry content for unmapped and remapped interrupts
5) choose different flow handler for unmapped and remapped interrupts

For MSI, it only needs to solve task 4) above. For IOAPIC, it needs to solve all five tasks above, which may cause big changes to irq_chip. And it even may cause IRQ remapping driver call back into IOAPIC driver, which breaks another rules that only the driver touches corresponding interrupt controller.

On the other hand, MSI is almost platform independent, so it's reasonable to change public struct irq_chip to support MSI. But IOAPIC is a sort of platform dependent (x86 and IA64), so it doesn't sound good to make great change to struct irq_chip to support IOAPIC.

So I prefer keeping IOAPIC driver sensing interrupt remapping and acting different for unmapped and remapped interrupts. This breaks the layered design, but it does make code simpler.

What's your thoughts?
Regards!
Gerry

Quoted 5 lines

Thanks,

tglx

#26 lore raw

Re: [Patch Part2 v3 15/24] x86, MSI: Use hierarchy irqdomain to manage MSI interrupts

Quoted 5 lines · Jiang Liu
Quoted 4 lines · Thomas Gleixner
Quoted 2 lines

IOAPIC runs into the same situation, but I need some more time to find a solution:)

I'm sure you will!

Quoted 15 lines

Hi Thomas,
I have reviewed IOAPIC related change again, but the conclusion
may not be what you expect:(
Currently IOAPIC driver detects IRQ remapping for following
tasks:
1) Issue different EOI command to IOAPIC chip for unammped and remapped interrupts. It uses pin number instead of vector number for remapped interrupts.
2) Print different format for IOAPIC entries for unmapped and remapped interrupts.
3) ioapic_ack_level() uses different method for unmapped and remapped interrupts
4) create different IOAPIC entry content for unmapped and remapped interrupts
5) choose different flow handler for unmapped and remapped interrupts

So todays code does

1/2/3 via irq_remap_modify_chip_defaults() and via
x86_io_apic_ops.eoi_ioapic_pin with convoluted back and forth calls from remap to ioapic code.

4) is solved via x86_io_apic_ops.setup_entry

5) via setup_remapped_irq()

Now with the stacked irq domains we end up with the following two scenarios:

	ioapic_domain -> vector_domain

	ioapic_domain -> remap_domain -> vector_domain

So if you look at the various issues you want to solve:

#1 Is simple to solve via:

static void ioapic_eoi(struct irq_data *data)

{
	if (data->parent->chip->irq_eoi)
		data->parent->chip->irq_eoi(data->parent);
	else
	    	plain_ioapic_eoi(data);
}

#2/3 Ditto

#4/5 Should be solvable via the irq_startup callback in a similar way

static int ioapic_startup(struct irq_data *data)

{
	if (data->parent->chip->irq_startup)
		return data->parent->chip->irq_startup(data->parent);
	else
	    	return plain_ioapic_startup(data);
}

I.e. you set the entry and the handler from the startup function of ioapic or remap.

It's probably not that simple as the above, but I'm pretty confident, that we can map it without adding a gazillion of new callbacks to irqchip.

Quoted 5 lines

For MSI, it only needs to solve task 4) above. For IOAPIC, it needs to solve all five tasks above, which may cause big changes to irq_chip. And it even may cause IRQ remapping driver call back into IOAPIC driver, which breaks another rules that only the driver touches corresponding interrupt controller.

If the remap driver calls ioapic functions which are provided for that purpose then I think that's unavoidable and ok. But I really want to avoid the intermingled mess in the other code pathes which call back and forth.

Quoted 2 lines

On the other hand, MSI is almost platform independent, so it's reasonable to change public struct irq_chip to support MSI.

Right, but I think we can get away without adding new functions to irqchip for the ioapic/remap thing.

Thanks,

tglx

author
#27 lore raw

Re: [Patch Part2 v3 15/24] x86, MSI: Use hierarchy irqdomain to manage MSI interrupts

In reply to Thomas Gleixner
Quoted 44 lines · Thomas Gleixner
Quoted 25 lines · Jiang Liu
arch/x86/kernel/apic/msi.c +12 -7
@@ -166,25 +264,59 @@ int setup_msi_irq(struct pci_dev *dev, struct msi_desc *msidesc,
 
 int native_setup_msi_irqs(struct pci_dev *dev, int nvec, int type)
 {
-	struct msi_desc *msidesc;
-	int irq, ret;
+	int irq, cnt, nvec_pow2;
+	struct irq_domain *domain;
+	struct msi_desc *msidesc, *iter;
+	struct irq_alloc_info info;
+	int node = dev_to_node(&dev->dev);
 
-	/* Multiple MSI vectors only supported with interrupt remapping */
-	if (type == PCI_CAP_ID_MSI && nvec > 1)
-		return 1;
+	if (disable_apic)
+		return -ENOSYS;
 
-	list_for_each_entry(msidesc, &dev->msi_list, list) {
-		irq = irq_domain_alloc_irqs(NULL, 1, NUMA_NO_NODE, NULL);
+	init_irq_alloc_info(&info, NULL);
+	info.msi_dev = dev;
+	if (type == PCI_CAP_ID_MSI) {
+		msidesc = list_first_entry(&dev->msi_list,
+					   struct msi_desc, list);

arch/x86/kernel/apic/msi.c int setup_msi_irq(struct pci_dev *dev, struct msi_desc *msidesc, · line 290

Quoted 10 lines
arch/x86/kernel/apic/msi.c +10 -0
+		WARN_ON(!list_is_singular(&dev->msi_list));
+		WARN_ON(msidesc->irq);
+		WARN_ON(msidesc->msi_attrib.multiple);
+		WARN_ON(msidesc->nvec_used);
+		info.type = X86_IRQ_ALLOC_TYPE_MSI;
+		cnt = nvec;
+	} else {
+		info.type = X86_IRQ_ALLOC_TYPE_MSIX;
+		cnt = 1;
+	}

We have a similar issue here.

arch/x86/kernel/apic/msi.c int setup_msi_irq(struct pci_dev *dev, struct msi_desc *msidesc, · line 292

Quoted 1 line
arch/x86/kernel/apic/msi.c +1 -0
+	domain = irq_remapping_get_irq_domain(&info);

We add domain specific knowledge to the MSI implementation. Not necessary at all.

Quoted 4 lines

Again MSI is not an x86 problem and we really can move most of that to the core code. The above sanity checks and the distinction between MSI and MSIX can be handled in the core code. And every domain involved in the MSI chain would need a alloc_msi() callback.

Add alloc_msi() callback to irq_domain_ops seems a little overaction. I think the main idea is to make MSI code public as much as possible, so I have found another solution to move MSI irqdomain code into drivers/pci/msi.c and only following code are platform dependent now. How about this solution?
------------------------------------------------------------------------
int native_setup_msi_irqs(struct pci_dev *dev, int nvec, int type)

{
        struct irq_domain *domain;
        struct irq_alloc_info info;

        init_irq_alloc_info(&info, NULL);
        info.msi_dev = dev;
        if (type == PCI_CAP_ID_MSI) {
                info.type = X86_IRQ_ALLOC_TYPE_MSI;
                info.flags |= X86_IRQ_ALLOC_CONTIGOUS_VECTORS;
        } else {
                info.type = X86_IRQ_ALLOC_TYPE_MSIX;
        }

        domain = irq_remapping_get_irq_domain(&info);
        if (domain == NULL)
                domain = msi_default_domain;
        if (domain == NULL)
                return -ENOSYS;

        return msi_irq_domain_alloc_irqs(domain, type, dev, &info);
}

void native_teardown_msi_irq(unsigned int irq)

{
        irq_domain_free_irqs(irq, 1);
}

irq_hw_number_t arch_msi_irq_domain_get_hwirq(void *arg)

{
        struct irq_alloc_info *info = arg;

        return info->msi_hwirq;
}

void arch_msi_irq_domain_set_hwirq(void *arg, irq_hw_number_t hwirq)

{
        struct irq_alloc_info *info = arg;

        info->msi_hwirq = hwirq;
}

void arch_init_msi_domain(struct irq_domain *parent)

{
        if (disable_apic)
                return;

        msi_default_domain = msi_create_irq_domain(parent);
        if (!msi_default_domain)
                pr_warn("failed to initialize irqdomain for MSI/MSI-x.\n");
}

#ifdef CONFIG_IRQ_REMAP
struct irq_domain *arch_create_msi_irq_domain(struct irq_domain *parent)

{
        return msi_create_irq_domain(parent);
}

#endif
-----------------------------------------------------------------

Quoted 9 lines

So native_setup_msi_irqs() would boil down to:
+ {

arch/x86/kernel/apic/msi.c +5 -0
+	if (disable_apic)
+		return -ENOSYS;
+ 
+	return irq_domain_alloc_msi(msi_domain, dev, nvec, type);   
+ }
Quoted 7 lines

Now that core function performs the sanity checks for the MSI case. In fact it should not proceed when a warning condition is detected. Not a x86 issue at all, its true for every MSI implementation.

Then it calls down the domain allocation chain. x86_msi_domain would simply hand down to the parent domain. That would either be the remap domain or the vector domain.

The issue here is that, the hierarchy irqdomain maintains a tree topology and every irqdomain only supports one parent.

In case of irq remapping, we need to build one irqdomain for each IOMMU unit to support hotplug and simplify the implementation. So we need to build one MSI irqdomain for each IOMMU unit too instead of using a common MSI irqdomain.

Current design is that, a common MSI irqdomain to support all MSI when irq remapping is disabled, and one MSI irqdomain for each IOMMU unit when irq remapping is enabled.

So we have the code below to choose the correct irqdomain for MSI.

        domain = irq_remapping_get_irq_domain(&info);
        if (domain == NULL)
                domain = msi_default_domain;
        if (domain == NULL)
                return -ENOSYS;
Quoted 5 lines

The reject for the multi MSI would only be implemented in the vector domain callback, while the remap domain can handle it. Once we gain support for allocating consecutive vectors for multi-MSI in the vector domain we would not have to change any of the MSI code at all.

I have worked out a working patch to make decision in vector domain, as you have suggested.

Quoted 7 lines

Thoughts?

Thanks,

tglx

#28 lore raw

Re: [Patch Part2 v3 15/24] x86, MSI: Use hierarchy irqdomain to manage MSI interrupts

Quoted 11 lines · Jiang Liu
Quoted 3 lines · Thomas Gleixner

Then it calls down the domain allocation chain. x86_msi_domain would simply hand down to the parent domain. That would either be the remap domain or the vector domain.

The issue here is that, the hierarchy irqdomain maintains a tree topology and every irqdomain only supports one parent.

In case of irq remapping, we need to build one irqdomain for each IOMMU unit to support hotplug and simplify the implementation. So we need to build one MSI irqdomain for each IOMMU unit too instead of using a common MSI irqdomain.

That makes indeed a difference.

Quoted 10 lines

Current design is that, a common MSI irqdomain to support all MSI when irq remapping is disabled, and one MSI irqdomain for each IOMMU unit when irq remapping is enabled.

So we have the code below to choose the correct irqdomain for MSI.

        domain = irq_remapping_get_irq_domain(&info);
        if (domain == NULL)
                domain = msi_default_domain;
        if (domain == NULL)
                return -ENOSYS;

Right. I guess we need to keep it that way for now.

But looking at the code makes me wonder why we actually need to call into the remap code and do a list walk to figure the domain out. The association of device and iommu should be known at startup/hotplug time already. That's out of the scope of this work, but should be fixed eventually.

Thanks,

tglx

author
#29 lore raw

PATCH v3 16/24 x86, irq: Directly call native_compose_msi_msg() for DMAR IRQ

In reply to Jiang Liu

DMAR interrupt won't be remapped by interrupt remapping hardware, so directly call native_compose_msi_msg() for DMAR IRQ to compose MSI message data. This will help to simplify MSI code later.

---
 arch/x86/kernel/apic/msi.c |    6 ++----
 1 file changed, 2 insertions(+), 4 deletions(-)
arch/x86/kernel/apic/msi.c +2 -4
index 354abd7ef2ad..6aac2ec1b3ef 100644
@@ -396,12 +396,10 @@ static struct irq_chip dmar_msi_type = {
 
 int arch_setup_dmar_msi(unsigned int irq)
 {
-	int ret;
 	struct msi_msg msg;
+	struct irq_cfg *cfg = irq_cfg(irq);
 
-	ret = msi_compose_msg(NULL, irq, &msg, -1);
-	if (ret < 0)
-		return ret;
+	native_compose_msi_msg(NULL, irq, cfg->dest_apicid, &msg, -1);
 	dmar_msi_write(irq, &msg);
 	irq_set_chip_and_handler_name(irq, &dmar_msi_type, handle_edge_irq,
 				      "edge");
Signature · 1 line
1.7.10.4
author
#30 lore raw

PATCH v3 17/24 iommu/vt-d: Clean up unused MSI related code

In reply to Jiang Liu

Now MSI interrupt has been converted to new hierarchy irqdomain interfaces, so kill legacy MSI related code.

---
 drivers/iommu/intel_irq_remapping.c |  144 -----------------------------------
 1 file changed, 144 deletions(-)
drivers/iommu/intel_irq_remapping.c +0 -144
index fdaa15026909..9742011190fb 100644
@@ -146,44 +146,6 @@ static int qi_flush_iec(struct intel_iommu *iommu, int index, int mask)
 	return qi_submit_sync(&desc, iommu);
 }
 
-static int map_irq_to_irte_handle(int irq, u16 *sub_handle)
-{
-	struct irq_2_iommu *irq_iommu = irq_2_iommu(irq);
-	unsigned long flags;
-	int index;
-
-	if (!irq_iommu)
-		return -1;
-
-	raw_spin_lock_irqsave(&irq_2_ir_lock, flags);
-	*sub_handle = irq_iommu->sub_handle;
-	index = irq_iommu->irte_index;
-	raw_spin_unlock_irqrestore(&irq_2_ir_lock, flags);
-	return index;
-}
-
-static int set_irte_irq(int irq, struct intel_iommu *iommu, u16 index, u16 subhandle)
-{
-	struct irq_2_iommu *irq_iommu = irq_2_iommu(irq);
-	struct irq_cfg *cfg = irq_cfg(irq);
-	unsigned long flags;
-
-	if (!irq_iommu)
-		return -1;
-
-	raw_spin_lock_irqsave(&irq_2_ir_lock, flags);
-
-	cfg->remapped = 1;
-	irq_iommu->iommu = iommu;
-	irq_iommu->irte_index = index;
-	irq_iommu->sub_handle = subhandle;
-	irq_iommu->irte_mask = 0;
-
-	raw_spin_unlock_irqrestore(&irq_2_ir_lock, flags);
-
-	return 0;
-}
-
 static int modify_irte(struct irq_2_iommu *irq_iommu,
 		       struct irte *irte_modified)
 {
@@ -1070,108 +1032,6 @@ intel_ioapic_set_affinity(struct irq_data *data, const struct cpumask *mask,
 	return 0;
 }
 
-static void intel_compose_msi_msg(struct pci_dev *pdev,
-				  unsigned int irq, unsigned int dest,
-				  struct msi_msg *msg, u8 hpet_id)
-{
-	struct irq_cfg *cfg;
-	struct irte irte;
-	u16 sub_handle = 0;
-	int ir_index;
-
-	cfg = irq_cfg(irq);
-
-	ir_index = map_irq_to_irte_handle(irq, &sub_handle);
-	BUG_ON(ir_index == -1);
-
-	prepare_irte(&irte, cfg->vector, dest);
-
-	/* Set source-id of interrupt request */
-	if (pdev)
-		set_msi_sid(&irte, pdev);
-	else
-		set_hpet_sid(&irte, hpet_id);
-
-	modify_irte(irq_2_iommu(irq), &irte);
-
-	msg->address_hi = MSI_ADDR_BASE_HI;
-	msg->data = sub_handle;
-	msg->address_lo = MSI_ADDR_BASE_LO | MSI_ADDR_IR_EXT_INT |
-			  MSI_ADDR_IR_SHV |
-			  MSI_ADDR_IR_INDEX1(ir_index) |
-			  MSI_ADDR_IR_INDEX2(ir_index);
-}
-
-/*
- * Map the PCI dev to the corresponding remapping hardware unit
- * and allocate 'nvec' consecutive interrupt-remapping table entries
- * in it.
- */
-static int intel_msi_alloc_irq(struct pci_dev *dev, int irq, int nvec)
-{
-	struct intel_iommu *iommu;
-	int index;
-
-	down_read(&dmar_global_lock);
-	iommu = map_dev_to_ir(dev);
-	if (!iommu) {
-		printk(KERN_ERR
-		       "Unable to map PCI %s to iommu\n", pci_name(dev));
-		index = -ENOENT;
-	} else {
-		index = alloc_irte(iommu, irq, irq_2_iommu(irq), nvec);
-		if (index < 0) {
-			printk(KERN_ERR
-			       "Unable to allocate %d IRTE for PCI %s\n",
-			       nvec, pci_name(dev));
-			index = -ENOSPC;
-		}
-	}
-	up_read(&dmar_global_lock);
-
-	return index;
-}
-
-static int intel_msi_setup_irq(struct pci_dev *pdev, unsigned int irq,
-			       int index, int sub_handle)
-{
-	struct intel_iommu *iommu;
-	int ret = -ENOENT;
-
-	down_read(&dmar_global_lock);
-	iommu = map_dev_to_ir(pdev);
-	if (iommu) {
-		/*
-		 * setup the mapping between the irq and the IRTE
-		 * base index, the sub_handle pointing to the
-		 * appropriate interrupt remap table entry.
-		 */
-		set_irte_irq(irq, iommu, index, sub_handle);
-		ret = 0;
-	}
-	up_read(&dmar_global_lock);
-
-	return ret;
-}
-
-static int intel_alloc_hpet_msi(unsigned int irq, unsigned int id)
-{
-	int ret = -1;
-	struct intel_iommu *iommu;
-	int index;
-
-	down_read(&dmar_global_lock);
-	iommu = map_hpet_to_ir(id);
-	if (iommu) {
-		index = alloc_irte(iommu, irq, irq_2_iommu(irq), 1);
-		if (index >= 0)
-			ret = 0;
-	}
-	up_read(&dmar_global_lock);
-
-	return ret;
-}
-
 static struct irq_domain *intel_get_ir_irq_domain(struct irq_alloc_info *info)
 {
 	struct intel_iommu *iommu = NULL;
@@ -1248,10 +1108,6 @@ struct irq_remap_ops intel_irq_remap_ops = {
 	.setup_ioapic_entry	= intel_setup_ioapic_entry,
 	.set_affinity		= intel_ioapic_set_affinity,
 	.free_irq		= free_irte,
-	.compose_msi_msg	= intel_compose_msi_msg,
-	.msi_alloc_irq		= intel_msi_alloc_irq,
-	.msi_setup_irq		= intel_msi_setup_irq,
-	.alloc_hpet_msi		= intel_alloc_hpet_msi,
 	.get_ir_irq_domain	= intel_get_ir_irq_domain,
 	.get_irq_domain		= intel_get_irq_domain,
 	.get_ioapic_entry	= intel_get_ioapic_entry,
Signature · 1 line
1.7.10.4
author
#31 lore raw

PATCH v3 18/24 iommu/amd: Clean up unused MSI related code

In reply to Jiang Liu

Now MSI interrupt has been converted to new hierarchy irqdomain interfaces, so kill legacy MSI related code.

---
 drivers/iommu/amd_iommu.c |  115 +--------------------------------------------
 1 file changed, 2 insertions(+), 113 deletions(-)
drivers/iommu/amd_iommu.c +2 -113
index a3ef3407bb1b..0b648772c221 100644
@@ -3959,8 +3959,7 @@ out_unlock:
 	return table;
 }
 
-static int alloc_irq_index(struct irq_cfg *cfg, struct irq_2_irte *irte_info,
-			   u16 devid, int count)
+static int alloc_irq_index(u16 devid, int count)
 {
 	struct irq_remap_table *table;
 	unsigned long flags;
@@ -3986,11 +3985,6 @@ static int alloc_irq_index(struct irq_cfg *cfg, struct irq_2_irte *irte_info,
 				table->table[index - c + 1] = IRTE_ALLOCATED;
 
 			index -= count - 1;
-
-			cfg->remapped	      = 1;
-			irte_info->devid      = devid;
-			irte_info->index      = index;
-
 			goto out;
 		}
 	}
@@ -4190,106 +4184,6 @@ static int free_irq(int irq)
 	return 0;
 }
 
-static void compose_msi_msg(struct pci_dev *pdev,
-			    unsigned int irq, unsigned int dest,
-			    struct msi_msg *msg, u8 hpet_id)
-{
-	struct irq_2_irte *irte_info;
-	struct irq_cfg *cfg;
-	union irte irte;
-
-	cfg = irq_cfg(irq);
-	if (!cfg)
-		return;
-
-	irte_info = &cfg->irq_2_irte;
-
-	irte.val		= 0;
-	irte.fields.vector	= cfg->vector;
-	irte.fields.int_type    = apic->irq_delivery_mode;
-	irte.fields.destination	= dest;
-	irte.fields.dm		= apic->irq_dest_mode;
-	irte.fields.valid	= 1;
-
-	modify_irte(irte_info->devid, irte_info->index, irte);
-
-	msg->address_hi = MSI_ADDR_BASE_HI;
-	msg->address_lo = MSI_ADDR_BASE_LO;
-	msg->data       = irte_info->index;
-}
-
-static int msi_alloc_irq(struct pci_dev *pdev, int irq, int nvec)
-{
-	struct irq_cfg *cfg;
-	int index;
-	u16 devid;
-
-	if (!pdev)
-		return -EINVAL;
-
-	cfg = irq_cfg(irq);
-	if (!cfg)
-		return -EINVAL;
-
-	devid = get_device_id(&pdev->dev);
-	index = alloc_irq_index(cfg, &cfg->irq_2_irte, devid, nvec);
-
-	return index < 0 ? MAX_IRQS_PER_TABLE : index;
-}
-
-static int msi_setup_irq(struct pci_dev *pdev, unsigned int irq,
-			 int index, int offset)
-{
-	struct irq_2_irte *irte_info;
-	struct irq_cfg *cfg;
-	u16 devid;
-
-	if (!pdev)
-		return -EINVAL;
-
-	cfg = irq_cfg(irq);
-	if (!cfg)
-		return -EINVAL;
-
-	if (index >= MAX_IRQS_PER_TABLE)
-		return 0;
-
-	devid		= get_device_id(&pdev->dev);
-	irte_info	= &cfg->irq_2_irte;
-
-	cfg->remapped	      = 1;
-	irte_info->devid      = devid;
-	irte_info->index      = index + offset;
-
-	return 0;
-}
-
-static int alloc_hpet_msi(unsigned int irq, unsigned int id)
-{
-	struct irq_2_irte *irte_info;
-	struct irq_cfg *cfg;
-	int index, devid;
-
-	cfg = irq_cfg(irq);
-	if (!cfg)
-		return -EINVAL;
-
-	irte_info = &cfg->irq_2_irte;
-	devid     = get_hpet_devid(id);
-	if (devid < 0)
-		return devid;
-
-	index = alloc_irq_index(cfg, &cfg->irq_2_irte, devid, 1);
-	if (index < 0)
-		return index;
-
-	cfg->remapped	      = 1;
-	irte_info->devid      = devid;
-	irte_info->index      = index;
-
-	return 0;
-}
-
 static int get_devid(struct irq_alloc_info *info)
 {
 	int devid = -1;
@@ -4385,10 +4279,6 @@ struct irq_remap_ops amd_iommu_irq_ops = {
 	.setup_ioapic_entry	= setup_ioapic_entry,
 	.set_affinity		= set_affinity,
 	.free_irq		= free_irq,
-	.compose_msi_msg	= compose_msi_msg,
-	.msi_alloc_irq		= msi_alloc_irq,
-	.msi_setup_irq		= msi_setup_irq,
-	.alloc_hpet_msi		= alloc_hpet_msi,
 	.get_ir_irq_domain	= get_ir_irq_domain,
 	.get_irq_domain		= get_irq_domain,
 	.get_ioapic_entry	= get_ioapic_entry,
@@ -4479,8 +4369,7 @@ static int irq_remapping_alloc(struct irq_domain *domain, unsigned int virq,
 		else
 			ret = -ENOMEM;
 	} else {
-		cfg = irq_cfg(virq);
-		index = alloc_irq_index(cfg, &data->irq_2_irte, devid, nr_irqs);
+		index = alloc_irq_index(devid, nr_irqs);
 	}
 	if (index < 0) {
 		pr_warn("Failed to allocate IRTE\n");
Signature · 1 line
1.7.10.4
author
#32 lore raw

PATCH v3 19/24 x86: irq_remapping: Clean up unused MSI related code

In reply to Jiang Liu

Now MSI interrupt has been converted to new hierarchy irqdomain interfaces, so kill legacy MSI related code and interfaces.

---
 arch/x86/include/asm/irq_remapping.h |   13 ---
 arch/x86/include/asm/pci.h           |    5 --
 arch/x86/kernel/x86_init.c           |    2 -
 drivers/iommu/irq_remapping.c        |  159 ----------------------------------
 drivers/iommu/irq_remapping.h        |   14 ---
 5 files changed, 193 deletions(-)
arch/x86/include/asm/irq_remapping.h +0 -13
index ea71f86423ee..cda6efe15f63 100644
@@ -50,10 +50,6 @@ extern int setup_ioapic_remapped_entry(int irq,
 				       int vector,
 				       struct io_apic_irq_attr *attr);
 extern void free_remapped_irq(int irq);
-extern void compose_remapped_msi_msg(struct pci_dev *pdev,
-				     unsigned int irq, unsigned int dest,
-				     struct msi_msg *msg, u8 hpet_id);
-extern int setup_hpet_msi_remapped(unsigned int irq, unsigned int id);
 extern void panic_if_irq_remap(const char *msg);
 extern bool setup_remapped_irq(int irq,
 			       struct irq_cfg *cfg,
@@ -112,15 +108,6 @@ static inline int setup_ioapic_remapped_entry(int irq,
 	return -ENODEV;
 }
 static inline void free_remapped_irq(int irq) { }
-static inline void compose_remapped_msi_msg(struct pci_dev *pdev,
-					    unsigned int irq, unsigned int dest,
-					    struct msi_msg *msg, u8 hpet_id)
-{
-}
-static inline int setup_hpet_msi_remapped(unsigned int irq, unsigned int id)
-{
-	return -ENODEV;
-}
 
 static inline void panic_if_irq_remap(const char *msg)
 {
arch/x86/include/asm/pci.h +0 -5
index 4e370a5d8117..d8c80ff32e8c 100644
@@ -96,15 +96,10 @@ extern void pci_iommu_alloc(void);
 #ifdef CONFIG_PCI_MSI
 /* implemented in arch/x86/kernel/apic/io_apic. */
 struct msi_desc;
-void native_compose_msi_msg(struct pci_dev *pdev, unsigned int irq,
-			    unsigned int dest, struct msi_msg *msg, u8 hpet_id);
 int native_setup_msi_irqs(struct pci_dev *dev, int nvec, int type);
 void native_teardown_msi_irq(unsigned int irq);
 void native_restore_msi_irqs(struct pci_dev *dev);
-int setup_msi_irq(struct pci_dev *dev, struct msi_desc *msidesc,
-		  unsigned int irq_base, unsigned int irq_offset);
 #else
-#define native_compose_msi_msg		NULL
 #define native_setup_msi_irqs		NULL
 #define native_teardown_msi_irq		NULL
 #endif
arch/x86/kernel/x86_init.c +0 -2
index e48b674639cc..814fcbadaad1 100644
@@ -111,11 +111,9 @@ EXPORT_SYMBOL_GPL(x86_platform);
 #if defined(CONFIG_PCI_MSI)
 struct x86_msi_ops x86_msi = {
 	.setup_msi_irqs		= native_setup_msi_irqs,
-	.compose_msi_msg	= native_compose_msi_msg,
 	.teardown_msi_irq	= native_teardown_msi_irq,
 	.teardown_msi_irqs	= default_teardown_msi_irqs,
 	.restore_msi_irqs	= default_restore_msi_irqs,
-	.setup_hpet_msi		= default_setup_hpet_msi,
 	.msi_mask_irq		= default_msi_mask_irq,
 	.msix_mask_irq		= default_msix_mask_irq,
 };
drivers/iommu/irq_remapping.c +0 -159

183 lines not on this page · raw message

drivers/iommu/irq_remapping.h +0 -14
index fd0270b7a351..b01a51ed1780 100644
@@ -70,20 +70,6 @@ struct irq_remap_ops {
 	/* Free an IRQ */
 	int (*free_irq)(int);
 
-	/* Create MSI msg to use for interrupt remapping */
-	void (*compose_msi_msg)(struct pci_dev *,
-				unsigned int, unsigned int,
-				struct msi_msg *, u8);
-
-	/* Allocate remapping resources for MSI */
-	int (*msi_alloc_irq)(struct pci_dev *, int, int);
-
-	/* Setup the remapped MSI irq */
-	int (*msi_setup_irq)(struct pci_dev *, unsigned int, int, int);
-
-	/* Setup interrupt remapping for an HPET MSI */
-	int (*alloc_hpet_msi)(unsigned int, unsigned int);
-
 	/* Get the irqdomain associated the IOMMU device */
 	struct irq_domain *(*get_ir_irq_domain)(struct irq_alloc_info *);
 
Signature · 1 line
1.7.10.4
author
#33 lore raw

PATCH v3 20/24 x86, irq: Clean up unused MSI related code and interfaces

In reply to Jiang Liu

Now MSI interrupt has been converted to new hierarchy irqdomain interfaces, so kill legacy MSI related code and interfaces.

---
 arch/x86/include/asm/hpet.h     |    9 ----
 arch/x86/include/asm/x86_init.h |    4 --
 arch/x86/kernel/apic/msi.c      |   91 +++------------------------------------
 3 files changed, 6 insertions(+), 98 deletions(-)
arch/x86/include/asm/hpet.h +0 -9
index e87e9faf87a9..5fa9fb0f8809 100644
@@ -85,15 +85,6 @@ extern struct irq_domain *hpet_create_irq_domain(int hpet_id);
 extern int hpet_assign_irq(struct irq_domain *domain,
 			   struct hpet_dev *dev, int dev_num);
 
-#ifdef CONFIG_PCI_MSI
-extern int default_setup_hpet_msi(unsigned int irq, unsigned int id);
-#else
-static inline int default_setup_hpet_msi(unsigned int irq, unsigned int id)
-{
-	return -EINVAL;
-}
-#endif
-
 #ifdef CONFIG_HPET_EMULATE_RTC
 
 #include <linux/interrupt.h>
arch/x86/include/asm/x86_init.h +0 -4
index e45e4da96bf1..9b53cb2acfbb 100644
@@ -176,13 +176,9 @@ struct msi_desc;
 
 struct x86_msi_ops {
 	int (*setup_msi_irqs)(struct pci_dev *dev, int nvec, int type);
-	void (*compose_msi_msg)(struct pci_dev *dev, unsigned int irq,
-				unsigned int dest, struct msi_msg *msg,
-			       u8 hpet_id);
 	void (*teardown_msi_irq)(unsigned int irq);
 	void (*teardown_msi_irqs)(struct pci_dev *dev);
 	void (*restore_msi_irqs)(struct pci_dev *dev);
-	int  (*setup_hpet_msi)(unsigned int irq, unsigned int id);
 	u32 (*msi_mask_irq)(struct msi_desc *desc, u32 mask, u32 flag);
 	u32 (*msix_mask_irq)(struct msi_desc *desc, u32 flag);
 };
arch/x86/kernel/apic/msi.c +6 -85

143 lines not on this page · raw message

Signature · 1 line
1.7.10.4
author
#34 lore raw

PATCH v3 21/24 iommu/vt-d: Refine the interfaces to create IRQ for DMAR unit

In reply to Jiang Liu

Refine the interfaces to create IRQ for DMAR unit. It's a preparation for converting DMAR IRQ to irqdomain on x86.

It also moves dmar_alloc_hwirq()/dmar_free_hwirq() from irq_remapping.h to dmar.h. They are not irq_remapping specific.

---
 arch/ia64/include/asm/irq_remapping.h |    2 --
 arch/ia64/kernel/msi_ia64.c           |   30 +++++++++++++++++++-----------
 arch/x86/include/asm/irq_remapping.h  |    4 ----
 arch/x86/kernel/apic/msi.c            |   24 +++++++++++++-----------
 drivers/iommu/dmar.c                  |   19 +++++--------------
 include/linux/dmar.h                  |    3 ++-
 6 files changed, 39 insertions(+), 43 deletions(-)
arch/ia64/include/asm/irq_remapping.h +0 -2
index e3b3556e2e1b..a8687b1d8906 100644
@@ -1,6 +1,4 @@
 #ifndef __IA64_INTR_REMAPPING_H
 #define __IA64_INTR_REMAPPING_H
 #define irq_remapping_enabled 0
-#define dmar_alloc_hwirq	create_irq
-#define dmar_free_hwirq		destroy_irq
 #endif
arch/ia64/kernel/msi_ia64.c +19 -11
index 8c3730c3c63d..15032330573b 100644
@@ -166,7 +166,7 @@ static struct irq_chip dmar_msi_type = {
 	.irq_retrigger = ia64_msi_retrigger_irq,
 };
 
-static int
+static void
 msi_compose_msg(struct pci_dev *pdev, unsigned int irq, struct msi_msg *msg)
 {
 	struct irq_cfg *cfg = irq_cfg + irq;
@@ -188,21 +188,29 @@ msi_compose_msg(struct pci_dev *pdev, unsigned int irq, struct msi_msg *msg)
 		MSI_DATA_LEVEL_ASSERT |
 		MSI_DATA_DELIVERY_FIXED |
 		MSI_DATA_VECTOR(cfg->vector);
-	return 0;
 }
 
-int arch_setup_dmar_msi(unsigned int irq)
+int dmar_alloc_hwirq(int id, int node, void *arg)
 {
-	int ret;
+	int irq;
 	struct msi_msg msg;
 
-	ret = msi_compose_msg(NULL, irq, &msg);
-	if (ret < 0)
-		return ret;
-	dmar_msi_write(irq, &msg);
-	irq_set_chip_and_handler_name(irq, &dmar_msi_type, handle_edge_irq,
-				      "edge");
-	return 0;
+	irq = create_irq();
+	if (irq > 0) {
+		irq_set_handler_data(irq, arg);
+		irq_set_chip_and_handler_name(irq, &dmar_msi_type,
+					      handle_edge_irq, "edge");
+		msi_compose_msg(NULL, irq, &msg);
+		dmar_msi_write(irq, &msg);
+	}
+
+	return irq;
+}
+
+void dmar_free_hwirq(int irq)
+{
+	irq_set_handler_data(irq, NULL);
+	destroy_irq(irq);
 }
 #endif /* CONFIG_INTEL_IOMMU */
 
arch/x86/include/asm/irq_remapping.h +0 -4
index cda6efe15f63..68d6dfcf7d92 100644
@@ -159,8 +159,4 @@ static inline bool irq_remapping_domain_is_remapped(struct irq_domain *domain)
 
 #define	irq_remapping_print_chip	NULL
 #endif /* CONFIG_IRQ_REMAP */
-
-extern int dmar_alloc_hwirq(void);
-extern void dmar_free_hwirq(int irq);
-
 #endif /* __X86_IRQ_REMAPPING_H */
arch/x86/kernel/apic/msi.c +13 -11
index 20350b2aaaab..11ced51d6ef2 100644
@@ -334,25 +334,27 @@ static struct irq_chip dmar_msi_type = {
  	.flags			= IRQCHIP_SKIP_SET_WAKE,
 };
 
-int arch_setup_dmar_msi(unsigned int irq)
+int dmar_alloc_hwirq(int id, int node, void *arg)
 {
+	int irq;
 	struct msi_msg msg;
-	struct irq_cfg *cfg = irq_cfg(irq);
 
-	native_compose_msi_msg(cfg, &msg);
-	dmar_msi_write(irq, &msg);
-	irq_set_chip_and_handler_name(irq, &dmar_msi_type, handle_edge_irq,
-				      "edge");
-	return 0;
-}
+	irq = irq_domain_alloc_irqs(NULL, 1, node, NULL);
+	if (irq > 0) {
+		irq_set_handler_data(irq, arg);
+		irq_set_chip_and_handler_name(irq, &dmar_msi_type,
+					      handle_edge_irq, "edge");
+		native_compose_msi_msg(irq_cfg(irq), &msg);
+		dmar_msi_write(irq, &msg);
+	}
 
-int dmar_alloc_hwirq(void)
-{
-	return irq_domain_alloc_irqs(NULL, 1, NUMA_NO_NODE, NULL);
+	return irq;
 }
 
 void dmar_free_hwirq(int irq)
 {
+	irq_set_handler_data(irq, NULL);
+	irq_set_handler(irq, NULL);
 	irq_domain_free_irqs(irq, 1);
 }
 #endif
drivers/iommu/dmar.c +5 -14
index c5c61cabd6e3..25f47937f1d5 100644
@@ -1018,8 +1018,8 @@ static void free_iommu(struct intel_iommu *iommu)
 
 	if (iommu->irq) {
 		free_irq(iommu->irq, iommu);
-		irq_set_handler_data(iommu->irq, NULL);
 		dmar_free_hwirq(iommu->irq);
+		iommu->irq = 0;
 	}
 
 	if (iommu->qi) {
@@ -1572,23 +1572,14 @@ int dmar_set_interrupt(struct intel_iommu *iommu)
 	if (iommu->irq)
 		return 0;
 
-	irq = dmar_alloc_hwirq();
-	if (irq <= 0) {
+	irq = dmar_alloc_hwirq(iommu->seq_id, iommu->node, iommu);
+	if (irq > 0) {
+		iommu->irq = irq;
+	} else {
 		pr_err("IOMMU: no free vectors\n");
 		return -EINVAL;
 	}
 
-	irq_set_handler_data(irq, iommu);
-	iommu->irq = irq;
-
-	ret = arch_setup_dmar_msi(irq);
-	if (ret) {
-		irq_set_handler_data(irq, NULL);
-		iommu->irq = 0;
-		dmar_free_hwirq(irq);
-		return ret;
-	}
-
 	ret = request_irq(irq, dmar_fault, IRQF_NO_THREAD, iommu->name, iommu);
 	if (ret)
 		pr_err("IOMMU: can't request irq\n");
include/linux/dmar.h +2 -1
index 593fff99e6bf..df3918482073 100644
@@ -189,6 +189,7 @@ extern void dmar_msi_read(int irq, struct msi_msg *msg);
 extern void dmar_msi_write(int irq, struct msi_msg *msg);
 extern int dmar_set_interrupt(struct intel_iommu *iommu);
 extern irqreturn_t dmar_fault(int irq, void *dev_id);
-extern int arch_setup_dmar_msi(unsigned int irq);
+extern int dmar_alloc_hwirq(int id, int node, void *arg);
+extern void dmar_free_hwirq(int irq);
 
 #endif /* __DMAR_H__ */
Signature · 1 line
1.7.10.4
author
#35 lore raw

PATCH v3 22/24 x86, irq: Use hierarchy irqdomain to manage DMAR interrupts

In reply to Jiang Liu

Enhance DMAR code to support hierarchy irqdomain, it helps to make the architecture more clear.

---
 arch/x86/include/asm/hw_irq.h |    7 +++
 arch/x86/kernel/apic/msi.c    |  129 +++++++++++++++++++++++++++++++----------
 2 files changed, 106 insertions(+), 30 deletions(-)
arch/x86/include/asm/hw_irq.h +7 -0
index 1ff7a7f61bf9..176c624c9cc9 100644
@@ -122,6 +122,7 @@ enum irq_alloc_type {
 	X86_IRQ_ALLOC_TYPE_HPET,
 	X86_IRQ_ALLOC_TYPE_MSI,
 	X86_IRQ_ALLOC_TYPE_MSIX,
+	X86_IRQ_ALLOC_TYPE_DMAR,
 };
 
 struct irq_alloc_info {
@@ -150,6 +151,12 @@ struct irq_alloc_info {
 			u32		ioapic_polarity : 1;
 		};
 #endif
+#ifdef	CONFIG_DMAR_TABLE
+		struct {
+			int		dmar_id;
+			void		*dmar_data;
+		};
+#endif
 	};
 };
 
arch/x86/kernel/apic/msi.c +99 -30

164 lines not on this page · raw message

Signature · 1 line
1.7.10.4
author
#36 lore raw

PATCH v3 23/24 x86, htirq: Use hierarchy irqdomain to manage Hypertransport interrupts

In reply to Jiang Liu

Use hierarchy irqdomain to manage Hypertransport interrupts. We have slightly changed the architecture interfaces to support htirq PCI driver, it should be safe because currently Hypertransport interrupt is only enabled on x86 platforms.

---
 arch/x86/include/asm/hw_irq.h |   13 ++++
 arch/x86/kernel/apic/htirq.c  |  166 +++++++++++++++++++++++++++++++----------
 arch/x86/kernel/apic/vector.c |    1 +
 drivers/pci/htirq.c           |   47 ++----------
 include/linux/htirq.h         |   24 ++++--
 5 files changed, 164 insertions(+), 87 deletions(-)
arch/x86/include/asm/hw_irq.h +13 -0
index 176c624c9cc9..d245cadfbaac 100644
@@ -157,6 +157,14 @@ struct irq_alloc_info {
 			void		*dmar_data;
 		};
 #endif
+#ifdef	CONFIG_HT_IRQ
+		struct {
+			int		ht_pos;
+			int		ht_idx;
+			struct pci_dev	*ht_dev;
+			void		*ht_update;
+		};
+#endif
 	};
 };
 
@@ -213,6 +221,11 @@ extern void arch_init_msi_domain(struct irq_domain *domain);
 #else
 static inline void arch_init_msi_domain(struct irq_domain *domain) { }
 #endif
+#ifdef	CONFIG_HT_IRQ
+extern void arch_init_htirq_domain(struct irq_domain *domain);
+#else
+static inline void arch_init_htirq_domain(struct irq_domain *domain) { }
+#endif
 
 /* Statistics */
 extern atomic_t irq_err_count;
arch/x86/kernel/apic/htirq.c +126 -40

215 lines not on this page · raw message

arch/x86/kernel/apic/vector.c +1 -0
index 61c55d0aeaec..83536ce0687b 100644
@@ -360,6 +360,7 @@ int __init arch_early_irq_init(void)
 	irq_set_default_host(x86_vector_domain);
 
 	arch_init_msi_domain(x86_vector_domain);
+	arch_init_htirq_domain(x86_vector_domain);
 
 	return arch_early_ioapic_init();
 }
drivers/pci/htirq.c +6 -41

80 lines not on this page · raw message

include/linux/htirq.h +18 -6
index 5caa51b7b95c..d4a527e58434 100644
@@ -1,26 +1,38 @@
 #ifndef LINUX_HTIRQ_H
 #define LINUX_HTIRQ_H
 
+struct pci_dev;
+struct irq_data;
+
 struct ht_irq_msg {
 	u32	address_lo;	/* low 32 bits of the ht irq message */
 	u32	address_hi;	/* high 32 bits of the it irq message */
 };
 
+typedef void (ht_irq_update_t)(struct pci_dev *dev, int irq,
+			       struct ht_irq_msg *msg);
+
+struct ht_irq_cfg {
+	struct pci_dev *dev;
+	 /* Update callback used to cope with buggy hardware */
+	ht_irq_update_t *update;
+	unsigned pos;
+	unsigned idx;
+	struct ht_irq_msg msg;
+};
+
 /* Helper functions.. */
 void fetch_ht_irq_msg(unsigned int irq, struct ht_irq_msg *msg);
 void write_ht_irq_msg(unsigned int irq, struct ht_irq_msg *msg);
-struct irq_data;
 void mask_ht_irq(struct irq_data *data);
 void unmask_ht_irq(struct irq_data *data);
 
 /* The arch hook for getting things started */
-int arch_setup_ht_irq(unsigned int irq, struct pci_dev *dev);
-int arch_alloc_ht_irq(struct pci_dev *dev);
-void arch_free_ht_irq(int irq);
+int arch_setup_ht_irq(int idx, int pos, struct pci_dev *dev,
+		      ht_irq_update_t *update);
+void arch_teardown_ht_irq(unsigned int irq);
 
 /* For drivers of buggy hardware */
-typedef void (ht_irq_update_t)(struct pci_dev *dev, int irq,
-			       struct ht_irq_msg *msg);
 int __ht_create_irq(struct pci_dev *dev, int idx, ht_irq_update_t *update);
 
 #endif /* LINUX_HTIRQ_H */
Signature · 1 line
1.7.10.4
author
#37 lore raw

PATCH v3 24/24 x86, uv: Use hierarchy irqdomain to manage UV interrupts

In reply to Jiang Liu

Enhance UV code to support hierarchy irqdomain, it helps to make the architecture more clear.

We should construct hwirq based on mmr_blade and mmr_offset, but mmr_offset is type of unsigned long, it may exceed the range of irq_hw_number_t. So help about the way to construct hwirq based on mmr_blade and mmr_offset is welcomed!

---
 arch/x86/include/asm/hw_irq.h |    9 ++
 arch/x86/platform/uv/uv_irq.c |  290 ++++++++++++++++-------------------------
 2 files changed, 122 insertions(+), 177 deletions(-)
arch/x86/include/asm/hw_irq.h +9 -0
index d245cadfbaac..199aaa2e6cd4 100644
@@ -123,6 +123,7 @@ enum irq_alloc_type {
 	X86_IRQ_ALLOC_TYPE_MSI,
 	X86_IRQ_ALLOC_TYPE_MSIX,
 	X86_IRQ_ALLOC_TYPE_DMAR,
+	X86_IRQ_ALLOC_TYPE_UV,
 };
 
 struct irq_alloc_info {
@@ -165,6 +166,14 @@ struct irq_alloc_info {
 			void		*ht_update;
 		};
 #endif
+#ifdef	CONFIG_X86_UV
+		struct {
+			int		uv_limit;
+			int		uv_blade;
+			unsigned long	uv_offset;
+			char		*uv_name;
+		};
+#endif
 	};
 };
 
arch/x86/platform/uv/uv_irq.c +113 -177

364 lines not on this page · raw message

Signature · 1 line
1.7.10.4