Skip to contentSkip to conversation

Refine generic/PCI MSI irqodmian interfaces

PATCH Superseded Jiang Liu 4 participants 25 messages Nov 15, 2014 – Dec 1, 2014

Subsystems (3)

v2 of 3 Superseded Latest is v3 →

lore
Share & tools
lore

Jiang Liu

author
#0 lore raw

v2 cover Refine generic/PCI MSI irqodmian interfaces

This patch set is based on tip/irq/irqdomain and tries to refine interfaces to support irqdomain for generic MSI and PCI MSI.

Patch 1 is just minor fixes for tip/irq/irqdomain.

Patch 2-4 introduce mechanisms to help reduce irqdomain users' code size. When converting XEN to use hierarchy irqdomain, I found the code size increases about 100 lines. So we still need helpers to reduce code size.

Patch 5 introduces some helpers to hide struct msi_desc implementation details, so later we could move msi_list from struct pci_dev into struct device to enable generic MSI support.

Patch 6 introduces msi_domain_{alloc|free}_irqs() which generalize pci_msi_domain_alloc_irqs() to support generic MSI.

Patch 7 introduces default data structures and callback implementations to support msi_domain_alloc_irqs(), so reduce burden on generic MSI users.

Patch 8 converts PCI MSI to use generic MSI interfaces, and also implement default callbacks for PCI MSI.

Patch 9 introduces a mechanism to replace arch_setup_msi_irq()/ arch_setup_msi_irqs()/arch_teardown_msi_irq()/arch_teardown_msi_irqs().

With this patch set applied, the generic MSI and PCI MSI interfaces are much easier to use. For extreme case, you only need to define a "struct msi_domain_info" and don't need to implement any callbacks, just using the default callbacks is OK:)

This patch set is also a preparation for:
1) Kill all weak functions in drivers/pci/msi.c
2) Implement support for non-PCI-compliant MSI device

It has been tested on x86 platforms, comments are welcomed!

V1->V2:
1) Fix bugs reported by Marc
2) Rename MSI_FLAG_USE_DEF_OPS as MSI_FLAG_USE_DEF_DOM_OPS
3) Add support of MSI_FLAG_USE_DEF_CHIP_OPS
4) Include linxu/irqhandler.h to use irq_flow_handler_t instead of "void *"
5) Change iterfaces to pass in "nvect" to msi_domain_ops.msi_prepare()
6) Add new interfaces/flags to help reduce irqdomain users' code size

Jiang Liu (8):
PCI, MSI: Fix errors caused by commit e5f1a59c4e12
irqdomain: Implement a method to automatically call parent domain's
alloc/free
irqdomain: Introduce helper function irq_domain_add_hierarchy() PCI, MSI: Introduce helpers to hide struct msi_desc implementation
details
genirq: Introduce msi_domain_{alloc|free}_irqs()
genirq: Provide default callbacks for msi_domain_ops PCI, MSI: Refine irqdomain interfaces to simplify its usage PCI, MSI: Provide mechanism to alloc/free MSI/MSIX interrupt from
irqdomain

Yingjoe Chen (1):
irqdomain: Use consistent prototype for irq_domain_free_irqs_*

 drivers/pci/msi.c         |  181 ++++++++++++++++++++++++++++++++++++---------
 include/linux/irqdomain.h |   33 ++++-----
 include/linux/msi.h       |   99 ++++++++++++++++++++++---
 kernel/irq/irqdomain.c    |   93 +++++++++++++++++++++--
 kernel/irq/msi.c          |  169 ++++++++++++++++++++++++++++++++++++++++--
 5 files changed, 502 insertions(+), 73 deletions(-)
Signature · 1 line
1.7.10.4
author
#1 lore raw

PATCH v2 1/9 PCI, MSI: Fix errors caused by commit e5f1a59c4e12

Better to fold into commit e5f1a59c4e12 ("PCI/MSI: Rename write_msi_msg() to pci_write_msi_msg()").

---
 drivers/pci/msi.c   |    2 +-
 include/linux/msi.h |    2 --
 2 files changed, 1 insertion(+), 3 deletions(-)
drivers/pci/msi.c +1 -1
index 564850b1316e..9c53b865cb1b 100644
@@ -1084,7 +1084,7 @@ void pci_msi_domain_write_msg(struct irq_data *irq_data, struct msi_msg *msg)
 	 * MSI message denotes a contiguous group of IRQs, written for 0th IRQ.
 	 */
 	if (desc->irq == irq_data->irq)
-		pci_write_msi_msg(desc, msg);
+		__pci_write_msi_msg(desc, msg);
 }
 
 /*
include/linux/msi.h +0 -2
index 8112a17cdca1..190c7abbec84 100644
@@ -44,11 +44,9 @@ struct msi_desc {
 	struct msi_msg msg;
 };
 
-#ifdef CONFIG_PCI_MSI
 void __pci_read_msi_msg(struct msi_desc *entry, struct msi_msg *msg);
 void __pci_write_msi_msg(struct msi_desc *entry, struct msi_msg *msg);
 void pci_write_msi_msg(unsigned int irq, struct msi_msg *msg);
-#endif
 
 /*
  * The arch hooks to setup up msi irqs. Those functions are
Signature · 1 line
1.7.10.4
author
#2 lore raw

PATCH v2 2/9 irqdomain: Use consistent prototype for irq_domain_free_irqs_*

In reply to Jiang Liu

From: Yingjoe Chen <[email protected]>

When using irq_domain_free_irqs_top() directly in irq_domain_ops, gcc generate the following warnings:

../drivers/irqchip/irq-gic.c:879:2: warning: initialization from incompatible pointer type [enabled by default] ../drivers/irqchip/irq-gic.c:879:2: warning: (near initialization for 'gic_irq_domain_hierarchy_ops.free') [enabled by default]

Change to use consistent prototype for all irq_domain_free_irqs*

Better to fold this into "irqdomain: Introduce new interfaces to support hierarchy irqdomains".

---
 include/linux/irqdomain.h |    9 +++++----
 kernel/irq/irqdomain.c    |    8 ++++----
 2 files changed, 9 insertions(+), 8 deletions(-)
include/linux/irqdomain.h +5 -4
index 640a1ec54772..fbe542967c20 100644
@@ -270,12 +270,13 @@ extern void irq_domain_set_info(struct irq_domain *domain, unsigned int virq,
 				void *handler_data, const char *handler_name);
 extern void irq_domain_reset_irq_data(struct irq_data *irq_data);
 extern void irq_domain_free_irqs_common(struct irq_domain *domain,
-					int virq, int nr_irqs);
+					unsigned int virq,
+					unsigned int nr_irqs);
 extern void irq_domain_free_irqs_top(struct irq_domain *domain,
-				     int virq, int nr_irqs);
+				     unsigned int virq, unsigned int nr_irqs);
 
 static inline int irq_domain_alloc_irqs_parent(struct irq_domain *domain,
-				int irq_base, unsigned int nr_irqs, void *arg)
+			unsigned int irq_base, unsigned int nr_irqs, void *arg)
 {
 	if (domain->parent && domain->parent->ops->alloc)
 		return domain->parent->ops->alloc(domain->parent, irq_base,
@@ -284,7 +285,7 @@ static inline int irq_domain_alloc_irqs_parent(struct irq_domain *domain,
 }
 
 static inline void irq_domain_free_irqs_parent(struct irq_domain *domain,
-					int irq_base, unsigned int nr_irqs)
+				unsigned int irq_base, unsigned int nr_irqs)
 {
 	if (domain->parent && domain->parent->ops->free)
 		domain->parent->ops->free(domain->parent, irq_base, nr_irqs);
kernel/irq/irqdomain.c +4 -4
index 8b0eddee0b21..705fb573e509 100644
@@ -899,8 +899,8 @@ void irq_domain_reset_irq_data(struct irq_data *irq_data)
 	irq_data->chip_data = NULL;
 }
 
-void irq_domain_free_irqs_common(struct irq_domain *domain, int virq,
-				 int nr_irqs)
+void irq_domain_free_irqs_common(struct irq_domain *domain, unsigned int virq,
+				 unsigned int nr_irqs)
 {
 	int i;
 	struct irq_data *irq_data;
@@ -913,8 +913,8 @@ void irq_domain_free_irqs_common(struct irq_domain *domain, int virq,
 	irq_domain_free_irqs_parent(domain, virq, nr_irqs);
 }
 
-void irq_domain_free_irqs_top(struct irq_domain *domain, int virq,
-			      int nr_irqs)
+void irq_domain_free_irqs_top(struct irq_domain *domain, unsigned int virq,
+			      unsigned int nr_irqs)
 {
 	int i;
 
Signature · 1 line
1.7.10.4
author
#3 lore raw

PATCH v2 3/9 irqdomain: Implement a method to automatically call parent domain's alloc/free

In reply to Jiang Liu

Add a flags to irq_domain.flags to control whether the irqdomain core should automatically call parent irqdomain's alloc/free callbacks. It help to reduce hierarchy irqdomains users' code size.

---
 include/linux/irqdomain.h |   24 ++++++-----------
 kernel/irq/irqdomain.c    |   66 +++++++++++++++++++++++++++++++++++++++++++--
 2 files changed, 72 insertions(+), 18 deletions(-)
include/linux/irqdomain.h +8 -16
index fbe542967c20..ad92b0b15945 100644
@@ -130,6 +130,8 @@ struct irq_domain {
 };
 
 #define	IRQ_DOMAIN_FLAG_HIERARCHY	0x1
+/* Framework automatically calls parent domain's alloc()/free() */
+#define	IRQ_DOMAIN_FLAG_AUTO_RECURSIVE	0x2
 #define	IRQ_DOMAIN_FLAG_ARCH1		0x10000
 
 #ifdef CONFIG_IRQ_DOMAIN
@@ -274,22 +276,12 @@ extern void irq_domain_free_irqs_common(struct irq_domain *domain,
 					unsigned int nr_irqs);
 extern void irq_domain_free_irqs_top(struct irq_domain *domain,
 				     unsigned int virq, unsigned int nr_irqs);
-
-static inline int irq_domain_alloc_irqs_parent(struct irq_domain *domain,
-			unsigned int irq_base, unsigned int nr_irqs, void *arg)
-{
-	if (domain->parent && domain->parent->ops->alloc)
-		return domain->parent->ops->alloc(domain->parent, irq_base,
-						  nr_irqs, arg);
-	return -ENOSYS;
-}
-
-static inline void irq_domain_free_irqs_parent(struct irq_domain *domain,
-				unsigned int irq_base, unsigned int nr_irqs)
-{
-	if (domain->parent && domain->parent->ops->free)
-		domain->parent->ops->free(domain->parent, irq_base, nr_irqs);
-}
+extern int irq_domain_alloc_irqs_parent(struct irq_domain *domain,
+					unsigned int irq_base,
+					unsigned int nr_irqs, void *arg);
+extern void irq_domain_free_irqs_parent(struct irq_domain *domain,
+					unsigned int irq_base,
+					unsigned int nr_irqs);
 
 static inline bool irq_domain_is_hierarchy(struct irq_domain *domain)
 {
kernel/irq/irqdomain.c +64 -2

90 lines not on this page · raw message

Signature · 1 line
1.7.10.4
#4 lore raw

[tip:irq/irqdomain] irqdomain: Implement a method to automatically call parent domains alloc/free

irqdomain: Implement a method to automatically call parent domains alloc/free
Commit-ID:  36d727310cb9f85efb5ac089ffb1797e7c3538e1
Gitweb:     http://git.kernel.org/tip/36d727310cb9f85efb5ac089ffb1797e7c3538e1
Author:     Jiang Liu <[email protected]>
AuthorDate: Sat, 15 Nov 2014 22:24:01 +0800
Committer:  Thomas Gleixner <[email protected]>
CommitDate: Sun, 23 Nov 2014 13:01:46 +0100

irqdomain: Implement a method to automatically call parent domains alloc/free

Add a flags to irq_domain.flags to control whether the irqdomain core should automatically call parent irqdomain's alloc/free callbacks. It help to reduce hierarchy irqdomains users' code size.

Cc Tony Luck <[email protected]>
Cc Bjorn Helgaas <[email protected]>
Cc Grant Likely <[email protected]>
Cc Marc Zyngier <[email protected]>
Cc Yijing Wang <[email protected]>
Cc Yingjoe Chen <[email protected]>
Cc Borislav Petkov <[email protected]>
Cc Benjamin Herrenschmidt <[email protected]>
Cc Matthias Brugger <[email protected]>
---
 include/linux/irqdomain.h | 24 ++++++--------
 kernel/irq/irqdomain.c    | 82 +++++++++++++++++++++++++++++++++++++++++++++--
 2 files changed, 89 insertions(+), 17 deletions(-)
include/linux/irqdomain.h +9 -15
index 7aca1ad..dd2709b 100644
@@ -134,6 +134,9 @@ enum {
 	/* Irq domain is hierarchical */
 	IRQ_DOMAIN_FLAG_HIERARCHY	= (1 << 0),
 
+	/* Core calls alloc/free recursive through the domain hierarchy. */
+	IRQ_DOMAIN_FLAG_AUTO_RECURSIVE	= (1 << 1),
+
 	/*
 	 * Flags starting from IRQ_DOMAIN_FLAG_NONCORE are reserved
 	 * for implementation specific purposes and ignored by the
@@ -285,22 +288,13 @@ extern void irq_domain_free_irqs_common(struct irq_domain *domain,
 extern void irq_domain_free_irqs_top(struct irq_domain *domain,
 				     unsigned int virq, unsigned int nr_irqs);
 
-static inline int irq_domain_alloc_irqs_parent(struct irq_domain *domain,
-					       unsigned int irq_base,
-					       unsigned int nr_irqs, void *arg)
-{
-	if (domain->parent && domain->parent->ops->alloc)
-		return domain->parent->ops->alloc(domain->parent, irq_base,
-						  nr_irqs, arg);
-	return -ENOSYS;
-}
+extern int irq_domain_alloc_irqs_parent(struct irq_domain *domain,
+					unsigned int irq_base,
+					unsigned int nr_irqs, void *arg);
 
-static inline void irq_domain_free_irqs_parent(struct irq_domain *domain,
-				unsigned int irq_base, unsigned int nr_irqs)
-{
-	if (domain->parent && domain->parent->ops->free)
-		domain->parent->ops->free(domain->parent, irq_base, nr_irqs);
-}
+extern void irq_domain_free_irqs_parent(struct irq_domain *domain,
+					unsigned int irq_base,
+					unsigned int nr_irqs);
 
 static inline bool irq_domain_is_hierarchy(struct irq_domain *domain)
 {
kernel/irq/irqdomain.c +80 -2

106 lines not on this page · raw message

author
#5 lore raw

PATCH v2 4/9 irqdomain: Introduce helper function irq_domain_add_hierarchy()

In reply to Jiang Liu

Introduce helper function irq_domain_add_hierarchy(), which creates a linear irqdomain if parameter 'size' is not zero, otherwise creates a tree irqdomain.

---
 include/linux/irqdomain.h |    4 ++++
 kernel/irq/irqdomain.c    |   19 +++++++++++++++++++
 2 files changed, 23 insertions(+)
include/linux/irqdomain.h +4 -0
index ad92b0b15945..30e8c753fa98 100644
@@ -248,6 +248,10 @@ int irq_domain_xlate_onetwocell(struct irq_domain *d, struct device_node *ctrlr,
 extern struct irq_data *irq_domain_get_irq_data(struct irq_domain *domain,
 						unsigned int virq);
 #ifdef	CONFIG_IRQ_DOMAIN_HIERARCHY
+extern struct irq_domain *irq_domain_add_hierarchy(struct irq_domain *parent,
+			unsigned int flags, unsigned int size,
+			struct device_node *node,
+			const struct irq_domain_ops *ops, void *host_data);
 extern int __irq_domain_alloc_irqs(struct irq_domain *domain, int irq_base,
 				   unsigned int nr_irqs, int node, void *arg,
 				   bool realloc);
kernel/irq/irqdomain.c +19 -0
index 03fc7d018324..73c66d6f9ffc 100644
@@ -742,6 +742,25 @@ static int irq_domain_alloc_descs(int virq, unsigned int cnt,
 }
 
 #ifdef	CONFIG_IRQ_DOMAIN_HIERARCHY
+struct irq_domain *irq_domain_add_hierarchy(struct irq_domain *parent,
+			unsigned int flags, unsigned int size,
+			struct device_node *node,
+			const struct irq_domain_ops *ops, void *host_data)
+{
+	struct irq_domain *domain;
+
+	if (size)
+		domain = irq_domain_add_linear(node, size, ops, host_data);
+	else
+		domain = irq_domain_add_tree(node, ops, host_data);
+	if (domain) {
+		domain->parent = parent;
+		domain->flags |= flags;
+	}
+
+	return domain;
+}
+
 static void irq_domain_insert_irq(int virq)
 {
 	struct irq_data *data;
Signature · 1 line
1.7.10.4
#6 lore raw

[tip:irq/irqdomain] irqdomain: Introduce helper function irq_domain_add_hierarchy()

irqdomain: Introduce helper function irq_domain_add_hierarchy()
Commit-ID:  afb7da83b9f476728623130703acb553d7c7c4d9
Gitweb:     http://git.kernel.org/tip/afb7da83b9f476728623130703acb553d7c7c4d9
Author:     Jiang Liu <[email protected]>
AuthorDate: Sat, 15 Nov 2014 22:24:02 +0800
Committer:  Thomas Gleixner <[email protected]>
CommitDate: Sun, 23 Nov 2014 13:01:46 +0100

irqdomain: Introduce helper function irq_domain_add_hierarchy()

Introduce helper function irq_domain_add_hierarchy(), which creates a linear irqdomain if parameter 'size' is not zero, otherwise creates a tree irqdomain.

Cc Tony Luck <[email protected]>
Cc Bjorn Helgaas <[email protected]>
Cc Grant Likely <[email protected]>
Cc Marc Zyngier <[email protected]>
Cc Yijing Wang <[email protected]>
Cc Yingjoe Chen <[email protected]>
Cc Borislav Petkov <[email protected]>
Cc Benjamin Herrenschmidt <[email protected]>
Cc Matthias Brugger <[email protected]>
---
 include/linux/irqdomain.h |  4 ++++
 kernel/irq/irqdomain.c    | 36 ++++++++++++++++++++++++++++++++++++
 2 files changed, 40 insertions(+)
include/linux/irqdomain.h +4 -0
index dd2709b..676d730 100644
@@ -259,6 +259,10 @@ int irq_domain_xlate_onetwocell(struct irq_domain *d, struct device_node *ctrlr,
 extern struct irq_data *irq_domain_get_irq_data(struct irq_domain *domain,
 						unsigned int virq);
 #ifdef	CONFIG_IRQ_DOMAIN_HIERARCHY
+extern struct irq_domain *irq_domain_add_hierarchy(struct irq_domain *parent,
+			unsigned int flags, unsigned int size,
+			struct device_node *node,
+			const struct irq_domain_ops *ops, void *host_data);
 extern int __irq_domain_alloc_irqs(struct irq_domain *domain, int irq_base,
 				   unsigned int nr_irqs, int node, void *arg,
 				   bool realloc);
kernel/irq/irqdomain.c +36 -0
index 9c88db7..7fac311 100644
@@ -742,6 +742,42 @@ static int irq_domain_alloc_descs(int virq, unsigned int cnt,
 }
 
 #ifdef	CONFIG_IRQ_DOMAIN_HIERARCHY
+/**
+ * irq_domain_add_hierarchy - Add a irqdomain into the hierarchy
+ * @parent:	Parent irq domain to associate with the new domain
+ * @flags:	Irq domain flags associated to the domain
+ * @size:	Size of the domain. See below
+ * @node:	Optional device-tree node of the interrupt controller
+ * @ops:	Pointer to the interrupt domain callbacks
+ * @host_data:	Controller private data pointer
+ *
+ * If @size is 0 a tree domain is created, otherwise a linear domain.
+ *
+ * If successful the parent is associated to the new domain and the
+ * domain flags are set.
+ * Returns pointer to IRQ domain, or NULL on failure.
+ */
+struct irq_domain *irq_domain_add_hierarchy(struct irq_domain *parent,
+					    unsigned int flags,
+					    unsigned int size,
+					    struct device_node *node,
+					    const struct irq_domain_ops *ops,
+					    void *host_data)
+{
+	struct irq_domain *domain;
+
+	if (size)
+		domain = irq_domain_add_linear(node, size, ops, host_data);
+	else
+		domain = irq_domain_add_tree(node, ops, host_data);
+	if (domain) {
+		domain->parent = parent;
+		domain->flags |= flags;
+	}
+
+	return domain;
+}
+
 static void irq_domain_insert_irq(int virq)
 {
 	struct irq_data *data;
#7 lore raw

Re: [tip:irq/irqdomain] irqdomain: Introduce helper function irq_domain_add_hierarchy()

Quoted 76 lines · tip-bot for Jiang Liu
Commit-ID:  afb7da83b9f476728623130703acb553d7c7c4d9
Gitweb:     http://git.kernel.org/tip/afb7da83b9f476728623130703acb553d7c7c4d9
Author:     Jiang Liu <[email protected]>
AuthorDate: Sat, 15 Nov 2014 22:24:02 +0800
Committer:  Thomas Gleixner <[email protected]>
CommitDate: Sun, 23 Nov 2014 13:01:46 +0100

irqdomain: Introduce helper function irq_domain_add_hierarchy()

Introduce helper function irq_domain_add_hierarchy(), which creates a linear irqdomain if parameter 'size' is not zero, otherwise creates a tree irqdomain.

Cc Tony Luck <[email protected]>
Cc Bjorn Helgaas <[email protected]>
Cc Grant Likely <[email protected]>
Cc Marc Zyngier <[email protected]>
Cc Yijing Wang <[email protected]>
Cc Yingjoe Chen <[email protected]>
Cc Borislav Petkov <[email protected]>
Cc Benjamin Herrenschmidt <[email protected]>
Cc Matthias Brugger <[email protected]>
---
 include/linux/irqdomain.h |  4 ++++
 kernel/irq/irqdomain.c    | 36 ++++++++++++++++++++++++++++++++++++
 2 files changed, 40 insertions(+)
include/linux/irqdomain.h +4 -0
index dd2709b..676d730 100644
@@ -259,6 +259,10 @@ int irq_domain_xlate_onetwocell(struct irq_domain *d, struct device_node *ctrlr,
 extern struct irq_data *irq_domain_get_irq_data(struct irq_domain *domain,
 						unsigned int virq);
 #ifdef	CONFIG_IRQ_DOMAIN_HIERARCHY
+extern struct irq_domain *irq_domain_add_hierarchy(struct irq_domain *parent,
+			unsigned int flags, unsigned int size,
+			struct device_node *node,
+			const struct irq_domain_ops *ops, void *host_data);
 extern int __irq_domain_alloc_irqs(struct irq_domain *domain, int irq_base,
 				   unsigned int nr_irqs, int node, void *arg,
 				   bool realloc);
kernel/irq/irqdomain.c +22 -0
index 9c88db7..7fac311 100644
@@ -742,6 +742,42 @@ static int irq_domain_alloc_descs(int virq, unsigned int cnt,
 }
 
 #ifdef	CONFIG_IRQ_DOMAIN_HIERARCHY
+/**
+ * irq_domain_add_hierarchy - Add a irqdomain into the hierarchy
+ * @parent:	Parent irq domain to associate with the new domain
+ * @flags:	Irq domain flags associated to the domain
+ * @size:	Size of the domain. See below
+ * @node:	Optional device-tree node of the interrupt controller
+ * @ops:	Pointer to the interrupt domain callbacks
+ * @host_data:	Controller private data pointer
+ *
+ * If @size is 0 a tree domain is created, otherwise a linear domain.
+ *
+ * If successful the parent is associated to the new domain and the
+ * domain flags are set.
+ * Returns pointer to IRQ domain, or NULL on failure.
+ */
+struct irq_domain *irq_domain_add_hierarchy(struct irq_domain *parent,
+					    unsigned int flags,
+					    unsigned int size,
+					    struct device_node *node,
+					    const struct irq_domain_ops *ops,
+					    void *host_data)
+{

kernel/irq/irqdomain.c static int irq_domain_alloc_descs(int virq, unsigned int cnt, · line 776

Quoted 10 lines
kernel/irq/irqdomain.c +10 -0
+	struct irq_domain *domain;
+
+	if (size)
+		domain = irq_domain_add_linear(node, size, ops, host_data);
+	else
+		domain = irq_domain_add_tree(node, ops, host_data);
+	if (domain) {
+		domain->parent = parent;
+		domain->flags |= flags;
+	}

Ok, I'm going to reply to this patch because it is on the splat path but fixing this would need a bit more reorganizing.

So I'm seeing the lockdep splat below really early on an IVB laptop.

Basically we're not supposed to do __GFP_FS allocations with IRQs off:

2737 /* We're only interested __GFP_FS allocations for now */
2738 if (!(gfp_mask & __GFP_FS))
2739 return;
2740
2741 /*
2742 * Oi! Can't be having __GFP_FS allocations with IRQs disabled.
2743 */
2744 if (DEBUG_LOCKS_WARN_ON(irqs_disabled_flags(flags))) <--- HERE!
2745 return;
2746
2747 mark_held_locks(curr, RECLAIM_FS);
2748 }

Now, AFAICT, enable_IR_x2apic() disables interrupts and the whole init is done with IRQs off but down that path intel_setup_irq_remapping() calls irq_domain_add_hierarchy() and it does by default GFP_KERNEL allocations.

The obvious fix is this and the machine boots fine with it. I'm not sure it is kosher though so I rather run it by people first:

---

kernel/irq/irqdomain.c +6 -2
index 7fac311057b8..c21a003b996a 100644
@@ -46,14 +46,18 @@ struct irq_domain *__irq_domain_add(struct device_node *of_node, int size,
 				    void *host_data)
 {
 	struct irq_domain *domain;
+	gfp_t gfp_flags = GFP_KERNEL;
+
+	if (irqs_disabled())
+		gfp_flags = GFP_NOFS;
 
 	domain = kzalloc_node(sizeof(*domain) + (sizeof(unsigned int) * size),
-			      GFP_KERNEL, of_node_to_nid(of_node));
+			      gfp_flags, of_node_to_nid(of_node));
 	if (WARN_ON(!domain))
 		return NULL;
 
 	/* Fill structure */
-	INIT_RADIX_TREE(&domain->revmap_tree, GFP_KERNEL);
+	INIT_RADIX_TREE(&domain->revmap_tree, gfp_flags);
 	domain->ops = ops;
 	domain->host_data = host_data;
 	domain->of_node = of_node_get(of_node);

--

[    0.085221] dmar: Host address width 36
[    0.085298] dmar: DRHD base: 0x000000fed90000 flags: 0x0
[    0.085414] dmar: IOMMU 0: reg_base_addr fed90000 ver 1:0 cap c0000020e60262 ecap f0101a
[    0.085513] dmar: DRHD base: 0x000000fed91000 flags: 0x1
[    0.085598] dmar: IOMMU 1: reg_base_addr fed91000 ver 1:0 cap c9008020660262 ecap f0105a
[    0.085693] dmar: RMRR base: 0x000000da2ba000 end: 0x000000da2d0fff
[    0.085789] dmar: RMRR base: 0x000000db800000 end: 0x000000df9fffff
[    0.085955] IOAPIC id 2 under DRHD base  0xfed91000 IOMMU 1
[    0.086034] HPET id 0 under DRHD base 0xfed91000
[    0.086109] Queued invalidation will be enabled to support x2apic and Intr-remapping.
[    0.086351] ------------[ cut here ]------------
[    0.086438] WARNING: CPU: 0 PID: 1 at kernel/locking/lockdep.c:2744 lockdep_trace_alloc+0xd4/0xe0()
[    0.086537] DEBUG_LOCKS_WARN_ON(irqs_disabled_flags(flags))
[    0.086598] Modules linked in:
[    0.086783] CPU: 0 PID: 1 Comm: swapper/0 Not tainted 3.18.0-rc6+ #1
[    0.086863] Hardware name: LENOVO 2320CTO/2320CTO, BIOS G2ET86WW (2.06 ) 11/13/2012
[    0.086959]  0000000000000009 ffff880213d07b58 ffffffff816502d7 0000000000000000
[    0.087240]  ffff880213d07ba8 ffff880213d07b98 ffffffff81059790 0000000000000001
[    0.087521]  0000000000000092 0000000000000000 00000000000080d0 0000000000000000
[    0.087803] Call Trace:
[    0.087878]  [<ffffffff816502d7>] dump_stack+0x4f/0x7b
[    0.087960]  [<ffffffff81059790>] warn_slowpath_common+0x80/0xa0
[    0.088041]  [<ffffffff810597f6>] warn_slowpath_fmt+0x46/0x50
[    0.088124]  [<ffffffff810a5514>] lockdep_trace_alloc+0xd4/0xe0
[    0.088208]  [<ffffffff8114877f>] __alloc_pages_nodemask+0x7f/0x970
[    0.088291]  [<ffffffff81148158>] ? get_page_from_freelist+0x5b8/0x980
[    0.088375]  [<ffffffff8109f72f>] ? trace_hardirqs_off_caller+0x1f/0xc0
[    0.088457]  [<ffffffff811490af>] alloc_kmem_pages_node+0x3f/0x60
[    0.088542]  [<ffffffff8119280e>] kmalloc_large_node+0x2e/0x70
[    0.088622]  [<ffffffff811989a5>] __kmalloc_node+0x215/0x2b0
[    0.088707]  [<ffffffff81539c10>] ? __dmar_enable_qi+0x40/0x130
[    0.088789]  [<ffffffff810bafe1>] __irq_domain_add+0x41/0x110
[    0.088871]  [<ffffffff810bb553>] irq_domain_add_hierarchy+0x23/0x50
[    0.088952]  [<ffffffff81651f33>] intel_setup_irq_remapping.part.4+0xdc/0x169
[    0.089039]  [<ffffffff81d61123>] intel_enable_irq_remapping+0x240/0x2f9
[    0.089123]  [<ffffffff81d61315>] irq_remapping_enable+0x20/0x36
[    0.089208]  [<ffffffff81d2cbaf>] enable_IR+0x3c/0x3e
[    0.089288]  [<ffffffff81d2cd92>] enable_IR_x2apic+0x82/0x120
[    0.089369]  [<ffffffff81d2e614>] default_setup_apic_routing+0x12/0x6b
[    0.089453]  [<ffffffff81d2aadc>] native_smp_prepare_cpus+0x29b/0x39f
[    0.089538]  [<ffffffff81d1df5d>] kernel_init_freeable+0x5f/0x1a2
[    0.089622]  [<ffffffff8165933f>] ? ret_from_fork+0xf/0xb0
[    0.089705]  [<ffffffff8164a640>] ? rest_init+0xd0/0xd0
[    0.089787]  [<ffffffff8164a64e>] kernel_init+0xe/0xf0
[    0.089867]  [<ffffffff816593ac>] ret_from_fork+0x7c/0xb0
[    0.089948]  [<ffffffff8164a640>] ? rest_init+0xd0/0xd0
[    0.090032] ---[ end trace fac50e785fc22942 ]---
[    0.090387] Enabled IRQ remapping in x2apic mode
[    0.090466] Enabling x2apic
[    0.090537] Enabled x2apic
[    0.090630] Switched APIC routing to cluster x2apic.
[    0.091287] ..TIMER: vector=0x30 apic1=0 pin1=2 apic2=-1 pin2=-1
[    0.101375] smpboot: CPU0: Intel(R) Core(TM) i7-3520M CPU @ 2.90GHz (fam: 06, model: 3a, stepping: 09)
Signature · 5 lines
Regards/Gruss,
    Boris.

Sent from a fat crate under my desk. Formatting is fine.
--
author
#8 lore raw

Re: [tip:irq/irqdomain] irqdomain: Introduce helper function irq_domain_add_hierarchy()

Quoted 122 lines · Borislav Petkov
Quoted 76 lines · tip-bot for Jiang Liu
Commit-ID:  afb7da83b9f476728623130703acb553d7c7c4d9
Gitweb:     http://git.kernel.org/tip/afb7da83b9f476728623130703acb553d7c7c4d9
Author:     Jiang Liu <[email protected]>
AuthorDate: Sat, 15 Nov 2014 22:24:02 +0800
Committer:  Thomas Gleixner <[email protected]>
CommitDate: Sun, 23 Nov 2014 13:01:46 +0100

irqdomain: Introduce helper function irq_domain_add_hierarchy()

Introduce helper function irq_domain_add_hierarchy(), which creates a linear irqdomain if parameter 'size' is not zero, otherwise creates a tree irqdomain.

Cc Tony Luck <[email protected]>
Cc Bjorn Helgaas <[email protected]>
Cc Grant Likely <[email protected]>
Cc Marc Zyngier <[email protected]>
Cc Yijing Wang <[email protected]>
Cc Yingjoe Chen <[email protected]>
Cc Borislav Petkov <[email protected]>
Cc Benjamin Herrenschmidt <[email protected]>
Cc Matthias Brugger <[email protected]>
---
 include/linux/irqdomain.h |  4 ++++
 kernel/irq/irqdomain.c    | 36 ++++++++++++++++++++++++++++++++++++
 2 files changed, 40 insertions(+)
include/linux/irqdomain.h +4 -0
index dd2709b..676d730 100644
@@ -259,6 +259,10 @@ int irq_domain_xlate_onetwocell(struct irq_domain *d, struct device_node *ctrlr,
 extern struct irq_data *irq_domain_get_irq_data(struct irq_domain *domain,
 						unsigned int virq);
 #ifdef	CONFIG_IRQ_DOMAIN_HIERARCHY
+extern struct irq_domain *irq_domain_add_hierarchy(struct irq_domain *parent,
+			unsigned int flags, unsigned int size,
+			struct device_node *node,
+			const struct irq_domain_ops *ops, void *host_data);
 extern int __irq_domain_alloc_irqs(struct irq_domain *domain, int irq_base,
 				   unsigned int nr_irqs, int node, void *arg,
 				   bool realloc);
kernel/irq/irqdomain.c +22 -0
index 9c88db7..7fac311 100644
@@ -742,6 +742,42 @@ static int irq_domain_alloc_descs(int virq, unsigned int cnt,
 }
 
 #ifdef	CONFIG_IRQ_DOMAIN_HIERARCHY
+/**
+ * irq_domain_add_hierarchy - Add a irqdomain into the hierarchy
+ * @parent:	Parent irq domain to associate with the new domain
+ * @flags:	Irq domain flags associated to the domain
+ * @size:	Size of the domain. See below
+ * @node:	Optional device-tree node of the interrupt controller
+ * @ops:	Pointer to the interrupt domain callbacks
+ * @host_data:	Controller private data pointer
+ *
+ * If @size is 0 a tree domain is created, otherwise a linear domain.
+ *
+ * If successful the parent is associated to the new domain and the
+ * domain flags are set.
+ * Returns pointer to IRQ domain, or NULL on failure.
+ */
+struct irq_domain *irq_domain_add_hierarchy(struct irq_domain *parent,
+					    unsigned int flags,
+					    unsigned int size,
+					    struct device_node *node,
+					    const struct irq_domain_ops *ops,
+					    void *host_data)
+{

kernel/irq/irqdomain.c static int irq_domain_alloc_descs(int virq, unsigned int cnt, · line 776

Quoted 10 lines
kernel/irq/irqdomain.c +10 -0
+	struct irq_domain *domain;
+
+	if (size)
+		domain = irq_domain_add_linear(node, size, ops, host_data);
+	else
+		domain = irq_domain_add_tree(node, ops, host_data);
+	if (domain) {
+		domain->parent = parent;
+		domain->flags |= flags;
+	}

Ok, I'm going to reply to this patch because it is on the splat path but fixing this would need a bit more reorganizing.

So I'm seeing the lockdep splat below really early on an IVB laptop.

Basically we're not supposed to do __GFP_FS allocations with IRQs off:

2737 /* We're only interested __GFP_FS allocations for now */
2738 if (!(gfp_mask & __GFP_FS))
2739 return;
2740
2741 /*
2742 * Oi! Can't be having __GFP_FS allocations with IRQs disabled.
2743 */
2744 if (DEBUG_LOCKS_WARN_ON(irqs_disabled_flags(flags))) <--- HERE!
2745 return;
2746
2747 mark_held_locks(curr, RECLAIM_FS);
2748 }

Now, AFAICT, enable_IR_x2apic() disables interrupts and the whole init is done with IRQs off but down that path intel_setup_irq_remapping() calls irq_domain_add_hierarchy() and it does by default GFP_KERNEL allocations.

The obvious fix is this and the machine boots fine with it. I'm not sure it is kosher though so I rather run it by people first:

---

kernel/irq/irqdomain.c +0 -0
index 7fac311057b8..c21a003b996a 100644

kernel/irq/irqdomain.c struct irq_domain *__irq_domain_add(struct device_node *of_node, int size, · line 52

Quoted 7 lines
kernel/irq/irqdomain.c +4 -0
@@ -46,14 +46,18 @@ struct irq_domain *__irq_domain_add(struct device_node *of_node, int size,
 				    void *host_data)
 {
 	struct irq_domain *domain;
+	gfp_t gfp_flags = GFP_KERNEL;
+
+	if (irqs_disabled())
+		gfp_flags = GFP_NOFS;

Hi Borislav,
Thanks for reporting and fixing this. How about using
GFP_ATOMIC here?
Regards!
Gerry

Quoted 71 lines
kernel/irq/irqdomain.c +2 -3
 	domain = kzalloc_node(sizeof(*domain) + (sizeof(unsigned int) * size),
-			      GFP_KERNEL, of_node_to_nid(of_node));
+			      gfp_flags, of_node_to_nid(of_node));
 	if (WARN_ON(!domain))
 		return NULL;
 
 	/* Fill structure */
-	INIT_RADIX_TREE(&domain->revmap_tree, GFP_KERNEL);
+	INIT_RADIX_TREE(&domain->revmap_tree, gfp_flags);
 	domain->ops = ops;
 	domain->host_data = host_data;
 	domain->of_node = of_node_get(of_node);
--

[    0.085221] dmar: Host address width 36
[    0.085298] dmar: DRHD base: 0x000000fed90000 flags: 0x0
[    0.085414] dmar: IOMMU 0: reg_base_addr fed90000 ver 1:0 cap c0000020e60262 ecap f0101a
[    0.085513] dmar: DRHD base: 0x000000fed91000 flags: 0x1
[    0.085598] dmar: IOMMU 1: reg_base_addr fed91000 ver 1:0 cap c9008020660262 ecap f0105a
[    0.085693] dmar: RMRR base: 0x000000da2ba000 end: 0x000000da2d0fff
[    0.085789] dmar: RMRR base: 0x000000db800000 end: 0x000000df9fffff
[    0.085955] IOAPIC id 2 under DRHD base  0xfed91000 IOMMU 1
[    0.086034] HPET id 0 under DRHD base 0xfed91000
[    0.086109] Queued invalidation will be enabled to support x2apic and Intr-remapping.
[    0.086351] ------------[ cut here ]------------
[    0.086438] WARNING: CPU: 0 PID: 1 at kernel/locking/lockdep.c:2744 lockdep_trace_alloc+0xd4/0xe0()
[    0.086537] DEBUG_LOCKS_WARN_ON(irqs_disabled_flags(flags))
[    0.086598] Modules linked in:
[    0.086783] CPU: 0 PID: 1 Comm: swapper/0 Not tainted 3.18.0-rc6+ #1
[    0.086863] Hardware name: LENOVO 2320CTO/2320CTO, BIOS G2ET86WW (2.06 ) 11/13/2012
[    0.086959]  0000000000000009 ffff880213d07b58 ffffffff816502d7 0000000000000000
[    0.087240]  ffff880213d07ba8 ffff880213d07b98 ffffffff81059790 0000000000000001
[    0.087521]  0000000000000092 0000000000000000 00000000000080d0 0000000000000000
[    0.087803] Call Trace:
[    0.087878]  [<ffffffff816502d7>] dump_stack+0x4f/0x7b
[    0.087960]  [<ffffffff81059790>] warn_slowpath_common+0x80/0xa0
[    0.088041]  [<ffffffff810597f6>] warn_slowpath_fmt+0x46/0x50
[    0.088124]  [<ffffffff810a5514>] lockdep_trace_alloc+0xd4/0xe0
[    0.088208]  [<ffffffff8114877f>] __alloc_pages_nodemask+0x7f/0x970
[    0.088291]  [<ffffffff81148158>] ? get_page_from_freelist+0x5b8/0x980
[    0.088375]  [<ffffffff8109f72f>] ? trace_hardirqs_off_caller+0x1f/0xc0
[    0.088457]  [<ffffffff811490af>] alloc_kmem_pages_node+0x3f/0x60
[    0.088542]  [<ffffffff8119280e>] kmalloc_large_node+0x2e/0x70
[    0.088622]  [<ffffffff811989a5>] __kmalloc_node+0x215/0x2b0
[    0.088707]  [<ffffffff81539c10>] ? __dmar_enable_qi+0x40/0x130
[    0.088789]  [<ffffffff810bafe1>] __irq_domain_add+0x41/0x110
[    0.088871]  [<ffffffff810bb553>] irq_domain_add_hierarchy+0x23/0x50
[    0.088952]  [<ffffffff81651f33>] intel_setup_irq_remapping.part.4+0xdc/0x169
[    0.089039]  [<ffffffff81d61123>] intel_enable_irq_remapping+0x240/0x2f9
[    0.089123]  [<ffffffff81d61315>] irq_remapping_enable+0x20/0x36
[    0.089208]  [<ffffffff81d2cbaf>] enable_IR+0x3c/0x3e
[    0.089288]  [<ffffffff81d2cd92>] enable_IR_x2apic+0x82/0x120
[    0.089369]  [<ffffffff81d2e614>] default_setup_apic_routing+0x12/0x6b
[    0.089453]  [<ffffffff81d2aadc>] native_smp_prepare_cpus+0x29b/0x39f
[    0.089538]  [<ffffffff81d1df5d>] kernel_init_freeable+0x5f/0x1a2
[    0.089622]  [<ffffffff8165933f>] ? ret_from_fork+0xf/0xb0
[    0.089705]  [<ffffffff8164a640>] ? rest_init+0xd0/0xd0
[    0.089787]  [<ffffffff8164a64e>] kernel_init+0xe/0xf0
[    0.089867]  [<ffffffff816593ac>] ret_from_fork+0x7c/0xb0
[    0.089948]  [<ffffffff8164a640>] ? rest_init+0xd0/0xd0
[    0.090032] ---[ end trace fac50e785fc22942 ]---
[    0.090387] Enabled IRQ remapping in x2apic mode
[    0.090466] Enabling x2apic
[    0.090537] Enabled x2apic
[    0.090630] Switched APIC routing to cluster x2apic.
[    0.091287] ..TIMER: vector=0x30 apic1=0 pin1=2 apic2=-1 pin2=-1
[    0.101375] smpboot: CPU0: Intel(R) Core(TM) i7-3520M CPU @ 2.90GHz (fam: 06, model: 3a, stepping: 09)
#9 lore raw

Re: [tip:irq/irqdomain] irqdomain: Introduce helper function irq_domain_add_hierarchy()

Quoted 2 lines · Jiang Liu

Thanks for reporting and fixing this. How about using GFP_ATOMIC
here?

Well, I don't see the need to use GFP_ATOMIC if we absolutely don't have to. And in this case lockdep is, AFAICT, correct in saying that we still can do allocations with interrupts disabled, only not go down into fs and do all kinds of lock grabbing operations like page reclaim, writeout or whatever it is being done nowadays there.

Yeah, this is also some old "no-no" in my memory which says that we should almost never use GFP_ATOMIC if it can be helped.

OTOH, I wonder if this code would rather need to hand down explicit gfp flags in case it should be able to do GFP_ATOMIC operations at some point...

Thanks.

Signature · 5 lines
Regards/Gruss,
    Boris.

Sent from a fat crate under my desk. Formatting is fine.
--
author
#10 lore raw

Re: [tip:irq/irqdomain] irqdomain: Introduce helper function irq_domain_add_hierarchy()

Quoted 4 lines · Borislav Petkov
Quoted 2 lines · Jiang Liu

Thanks for reporting and fixing this. How about using GFP_ATOMIC
here?

Quoted 8 lines

Well, I don't see the need to use GFP_ATOMIC if we absolutely don't have to. And in this case lockdep is, AFAICT, correct in saying that we still can do allocations with interrupts disabled, only not go down into fs and do all kinds of lock grabbing operations like page reclaim, writeout or whatever it is being done nowadays there.

Yeah, this is also some old "no-no" in my memory which says that we should almost never use GFP_ATOMIC if it can be helped.

Thanks for the info about GFP_ATOMIC, originally I have an impression that we should use GFP_ATOMIC when interrupt is disabled:(

Quoted 7 lines

OTOH, I wonder if this code would rather need to hand down explicit gfp flags in case it should be able to do GFP_ATOMIC operations at some point...

Thanks.

#11 lore raw

Re: [tip:irq/irqdomain] irqdomain: Introduce helper function irq_domain_add_hierarchy()

Quoted 2 lines · Jiang Liu

Thanks for the info about GFP_ATOMIC, originally I have an impression that we should use GFP_ATOMIC when interrupt is disabled:(

Hmm, that's actually a good point. GFP_NOFS has __GFP_WAIT which means that we can wait and resched when the first alloc attempt fails so maybe GFP_ATOMIC would be the correct solution after all...

Signature · 5 lines
Regards/Gruss,
    Boris.

Sent from a fat crate under my desk. Formatting is fine.
--
#12 lore raw

Re: [tip:irq/irqdomain] irqdomain: Introduce helper function irq_domain_add_hierarchy()

In reply to Borislav Petkov
Quoted 31 lines · Borislav Petkov

So I'm seeing the lockdep splat below really early on an IVB laptop.

Basically we're not supposed to do __GFP_FS allocations with IRQs off:

2737 /* We're only interested __GFP_FS allocations for now */
2738 if (!(gfp_mask & __GFP_FS))
2739 return;
2740
2741 /*
2742 * Oi! Can't be having __GFP_FS allocations with IRQs disabled.
2743 */
2744 if (DEBUG_LOCKS_WARN_ON(irqs_disabled_flags(flags))) <--- HERE!
2745 return;
2746
2747 mark_held_locks(curr, RECLAIM_FS);
2748 }

Now, AFAICT, enable_IR_x2apic() disables interrupts and the whole init is done with IRQs off but down that path intel_setup_irq_remapping() calls irq_domain_add_hierarchy() and it does by default GFP_KERNEL allocations.

The obvious fix is this and the machine boots fine with it. I'm not sure it is kosher though so I rather run it by people first:

---

kernel/irq/irqdomain.c +0 -0
index 7fac311057b8..c21a003b996a 100644

kernel/irq/irqdomain.c struct irq_domain *__irq_domain_add(struct device_node *of_node, int size, · line 52

Quoted 7 lines
kernel/irq/irqdomain.c +4 -0
@@ -46,14 +46,18 @@ struct irq_domain *__irq_domain_add(struct device_node *of_node, int size,
 				    void *host_data)
 {
 	struct irq_domain *domain;
+	gfp_t gfp_flags = GFP_KERNEL;
+
+	if (irqs_disabled())
+		gfp_flags = GFP_NOFS;

We want to use GFP_ATOMIC for that, but I really hate to do so. There is no reason except for the early boot stage to call into this code with interrupts disabled. And there we are covered by gfp_allowed_mask, so that a GFP_KERNEL allocation can succeed.

I have no idea, why enable_IR_x2apic() has been bolted into smp_prepare_cpus(). Probably just because.

There is no reason WHY this cannot be done in the early irq setup path (at least nowadays with the allocators being available early), but that is another area which needs some care and cleanup, but definitely too late before the 3.19 merge window opens.

So we have to bite the bullet and apply something like this along with a big fat comment WHY we are doing so and I'm tempted to wrap this into a #ifdef CONFIG_X86 so that noone else thinks that calling this code with interrupts disabled - except for the early boot stage - is a brilliant idea.

Thanks,

tglx

#13 lore raw

PATCH 1/1 irqdomain: Correct early allocation of irq domains with IRQs off

From: Borislav Petkov <[email protected]>

I'm seeing the following lockdep splat on an x2APIC machine with interrupts remapping. The problem is that enable_IR_x2apic() disables interrupts before doing any further initialization. However, after having moved to irq domains, domain allocation cannot happen with interrupts disabled (GFP_KERNEL).

A proper fix would be to move that initialization to the early irq setup path, which is something for another day. It is more involved work too.

So do a temporary fix which should not encourage the behaviour of even assuming the irq domains code should be called with interrupts disabled. Make it x86-only too.

...
dmar: Host address width 36
dmar: DRHD base: 0x000000fed90000 flags: 0x0
dmar: IOMMU 0: reg_base_addr fed90000 ver 1:0 cap c0000020e60262 ecap f0101a
dmar: DRHD base: 0x000000fed91000 flags: 0x1
dmar: IOMMU 1: reg_base_addr fed91000 ver 1:0 cap c9008020660262 ecap f0105a
dmar: RMRR base: 0x000000da2ba000 end: 0x000000da2d0fff
dmar: RMRR base: 0x000000db800000 end: 0x000000df9fffff
IOAPIC id 2 under DRHD base 0xfed91000 IOMMU 1
HPET id 0 under DRHD base 0xfed91000
Queued invalidation will be enabled to support x2apic and Intr-remapping.

------------[ cut here ]------------
WARNING: CPU: 0 PID: 1 at kernel/locking/lockdep.c:2744 lockdep_trace_alloc+0xd4/0xe0()
DEBUG_LOCKS_WARN_ON(irqs_disabled_flags(flags))
Modules linked in:
CPU: 0 PID: 1 Comm: swapper/0 Not tainted 3.18.0-rc6+ #1
Hardware name: LENOVO 2320CTO/2320CTO, BIOS G2ET86WW (2.06 ) 11/13/2012
 0000000000000009 ffff880213d07b58 ffffffff816502d7 0000000000000000
 ffff880213d07ba8 ffff880213d07b98 ffffffff81059790 0000000000000001
 0000000000000092 0000000000000000 00000000000080d0 0000000000000000
Call Trace:
 dump_stack
 warn_slowpath_common
 warn_slowpath_fmt
 lockdep_trace_alloc
 __alloc_pages_nodemask
 ? get_page_from_freelist
 ? trace_hardirqs_off_caller
 alloc_kmem_pages_node
 kmalloc_large_node
 __kmalloc_node
 ? __dmar_enable_qi
 __irq_domain_add
 irq_domain_add_hierarchy
 intel_setup_irq_remapping.part.4
 intel_enable_irq_remapping
 irq_remapping_enable
 enable_IR
 enable_IR_x2apic
 default_setup_apic_routing
 native_smp_prepare_cpus
 kernel_init_freeable
 ? ret_from_fork
 ? rest_init
 kernel_init
 ret_from_fork
 ? rest_init
---[ end trace fac50e785fc22942 ]---

Enabled IRQ remapping in x2apic mode Enabling x2apic
Enabled x2apic
Switched APIC routing to cluster x2apic. ..TIMER: vector=0x30 apic1=0 pin1=2 apic2=-1 pin2=-1
...

Cc Thomas Gleixner <[email protected]>
Cc Jiang Liu <[email protected]>
Cc Tony Luck <[email protected]>
Cc linux-arm-kernel at lists.infradead.org
Cc Bjorn Helgaas <[email protected]>
Cc Grant Likely <[email protected]>
Cc Marc Zyngier <[email protected]>
Cc Yijing Wang <[email protected]>
Cc Yingjoe Chen <[email protected]>
Cc Benjamin Herrenschmidt <[email protected]>
Cc Matthias Brugger <[email protected]>
Cc Joerg Roedel <[email protected]>
---
 kernel/irq/irqdomain.c | 21 +++++++++++++++++++--
 1 file changed, 19 insertions(+), 2 deletions(-)
kernel/irq/irqdomain.c +19 -2
index 7fac311057b8..3395d8923f96 100644
@@ -46,14 +46,31 @@ struct irq_domain *__irq_domain_add(struct device_node *of_node, int size,
 				    void *host_data)
 {
 	struct irq_domain *domain;
+	gfp_t gfp_flags = GFP_KERNEL;
+
+#ifdef CONFIG_X86
+	/*
+	 * BIG FAT COMMENT: Early initialization paths like enable_IR_x2apic(),
+	 * for example, call into here with interrupts disabled but then we do
+	 * allocate memory and can sleep so no-no. A proper fix would be to do
+	 * x2APIC IR setup in the early irq setup path but it is too late for
+	 * fixing it this way now, shortly before the merge window.
+	 *
+	 * So we do this little brown paper bag, which is temporary! Do not even
+	 * think of calling irq domain setup code with IRQs disabled. You will
+	 * get frozen-sharked!
+	 */
+	if (irqs_disabled())
+		gfp_flags = GFP_NOFS;
+#endif
 
 	domain = kzalloc_node(sizeof(*domain) + (sizeof(unsigned int) * size),
-			      GFP_KERNEL, of_node_to_nid(of_node));
+			      gfp_flags, of_node_to_nid(of_node));
 	if (WARN_ON(!domain))
 		return NULL;
 
 	/* Fill structure */
-	INIT_RADIX_TREE(&domain->revmap_tree, GFP_KERNEL);
+	INIT_RADIX_TREE(&domain->revmap_tree, gfp_flags);
 	domain->ops = ops;
 	domain->host_data = host_data;
 	domain->of_node = of_node_get(of_node);
Signature · 1 line
2.0.0
#14 lore raw

[tip:x86/apic] irqdomain: Correct early allocation of irq domains with IRQs off

irqdomain: Correct early allocation of irq domains with IRQs off
Commit-ID:  eda7516e1d428caf3cfc88e4292f2a1357a2e569
Gitweb:     http://git.kernel.org/tip/eda7516e1d428caf3cfc88e4292f2a1357a2e569
Author:     Borislav Petkov <[email protected]>
AuthorDate: Sun, 30 Nov 2014 13:37:43 +0100
Committer:  Thomas Gleixner <[email protected]>
CommitDate: Mon, 1 Dec 2014 10:38:30 +0100

irqdomain: Correct early allocation of irq domains with IRQs off

I'm seeing the following lockdep splat on an x2APIC machine with interrupts remapping. The problem is that enable_IR_x2apic() disables interrupts before doing any further initialization. However, after having moved to irq domains, domain allocation cannot happen with interrupts disabled (GFP_KERNEL).

A proper fix would be to move that initialization to the early irq setup path, which is something for another day. It is more involved work too.

So do a temporary fix which should not encourage the behaviour of even assuming the irq domains code should be called with interrupts disabled. Make it x86-only too.

...
dmar: Host address width 36
dmar: DRHD base: 0x000000fed90000 flags: 0x0
dmar: IOMMU 0: reg_base_addr fed90000 ver 1:0 cap c0000020e60262 ecap f0101a
dmar: DRHD base: 0x000000fed91000 flags: 0x1
dmar: IOMMU 1: reg_base_addr fed91000 ver 1:0 cap c9008020660262 ecap f0105a
dmar: RMRR base: 0x000000da2ba000 end: 0x000000da2d0fff
dmar: RMRR base: 0x000000db800000 end: 0x000000df9fffff
IOAPIC id 2 under DRHD base 0xfed91000 IOMMU 1
HPET id 0 under DRHD base 0xfed91000
Queued invalidation will be enabled to support x2apic and Intr-remapping.

------------[ cut here ]------------
WARNING: CPU: 0 PID: 1 at kernel/locking/lockdep.c:2744 lockdep_trace_alloc+0xd4/0xe0()
DEBUG_LOCKS_WARN_ON(irqs_disabled_flags(flags))
Modules linked in:
CPU: 0 PID: 1 Comm: swapper/0 Not tainted 3.18.0-rc6+ #1
Hardware name: LENOVO 2320CTO/2320CTO, BIOS G2ET86WW (2.06 ) 11/13/2012
 0000000000000009 ffff880213d07b58 ffffffff816502d7 0000000000000000
 ffff880213d07ba8 ffff880213d07b98 ffffffff81059790 0000000000000001
 0000000000000092 0000000000000000 00000000000080d0 0000000000000000
Call Trace:
 dump_stack
 warn_slowpath_common
 warn_slowpath_fmt
 lockdep_trace_alloc
 __alloc_pages_nodemask
 ? get_page_from_freelist
 ? trace_hardirqs_off_caller
 alloc_kmem_pages_node
 kmalloc_large_node
 __kmalloc_node
 ? __dmar_enable_qi
 __irq_domain_add
 irq_domain_add_hierarchy
 intel_setup_irq_remapping.part.4
 intel_enable_irq_remapping
 irq_remapping_enable
 enable_IR
 enable_IR_x2apic
 default_setup_apic_routing
 native_smp_prepare_cpus
 kernel_init_freeable
 ? ret_from_fork
 ? rest_init
 kernel_init
 ret_from_fork
 ? rest_init
---[ end trace fac50e785fc22942 ]---

Enabled IRQ remapping in x2apic mode Enabling x2apic
Enabled x2apic
Switched APIC routing to cluster x2apic. ..TIMER: vector=0x30 apic1=0 pin1=2 apic2=-1 pin2=-1
...

---
 kernel/irq/irqdomain.c | 21 +++++++++++++++++++--
 1 file changed, 19 insertions(+), 2 deletions(-)
kernel/irq/irqdomain.c +19 -2
index 7fac311..3395d89 100644
@@ -46,14 +46,31 @@ struct irq_domain *__irq_domain_add(struct device_node *of_node, int size,
 				    void *host_data)
 {
 	struct irq_domain *domain;
+	gfp_t gfp_flags = GFP_KERNEL;
+
+#ifdef CONFIG_X86
+	/*
+	 * BIG FAT COMMENT: Early initialization paths like enable_IR_x2apic(),
+	 * for example, call into here with interrupts disabled but then we do
+	 * allocate memory and can sleep so no-no. A proper fix would be to do
+	 * x2APIC IR setup in the early irq setup path but it is too late for
+	 * fixing it this way now, shortly before the merge window.
+	 *
+	 * So we do this little brown paper bag, which is temporary! Do not even
+	 * think of calling irq domain setup code with IRQs disabled. You will
+	 * get frozen-sharked!
+	 */
+	if (irqs_disabled())
+		gfp_flags = GFP_NOFS;
+#endif
 
 	domain = kzalloc_node(sizeof(*domain) + (sizeof(unsigned int) * size),
-			      GFP_KERNEL, of_node_to_nid(of_node));
+			      gfp_flags, of_node_to_nid(of_node));
 	if (WARN_ON(!domain))
 		return NULL;
 
 	/* Fill structure */
-	INIT_RADIX_TREE(&domain->revmap_tree, GFP_KERNEL);
+	INIT_RADIX_TREE(&domain->revmap_tree, gfp_flags);
 	domain->ops = ops;
 	domain->host_data = host_data;
 	domain->of_node = of_node_get(of_node);
author
#15 lore raw

Re: [tip:irq/irqdomain] irqdomain: Introduce helper function irq_domain_add_hierarchy()

In reply to Thomas Gleixner
Quoted 45 lines · Thomas Gleixner
Quoted 31 lines · Borislav Petkov

So I'm seeing the lockdep splat below really early on an IVB laptop.

Basically we're not supposed to do __GFP_FS allocations with IRQs off:

2737 /* We're only interested __GFP_FS allocations for now */
2738 if (!(gfp_mask & __GFP_FS))
2739 return;
2740
2741 /*
2742 * Oi! Can't be having __GFP_FS allocations with IRQs disabled.
2743 */
2744 if (DEBUG_LOCKS_WARN_ON(irqs_disabled_flags(flags))) <--- HERE!
2745 return;
2746
2747 mark_held_locks(curr, RECLAIM_FS);
2748 }

Now, AFAICT, enable_IR_x2apic() disables interrupts and the whole init is done with IRQs off but down that path intel_setup_irq_remapping() calls irq_domain_add_hierarchy() and it does by default GFP_KERNEL allocations.

The obvious fix is this and the machine boots fine with it. I'm not sure it is kosher though so I rather run it by people first:

---

kernel/irq/irqdomain.c +0 -0
index 7fac311057b8..c21a003b996a 100644

kernel/irq/irqdomain.c struct irq_domain *__irq_domain_add(struct device_node *of_node, int size, · line 52

Quoted 7 lines
kernel/irq/irqdomain.c +4 -0
@@ -46,14 +46,18 @@ struct irq_domain *__irq_domain_add(struct device_node *of_node, int size,
 				    void *host_data)
 {
 	struct irq_domain *domain;
+	gfp_t gfp_flags = GFP_KERNEL;
+
+	if (irqs_disabled())
+		gfp_flags = GFP_NOFS;

We want to use GFP_ATOMIC for that, but I really hate to do so. There is no reason except for the early boot stage to call into this code with interrupts disabled. And there we are covered by gfp_allowed_mask, so that a GFP_KERNEL allocation can succeed.

Quoted 7 lines

I have no idea, why enable_IR_x2apic() has been bolted into smp_prepare_cpus(). Probably just because.

There is no reason WHY this cannot be done in the early irq setup path (at least nowadays with the allocators being available early), but that is another area which needs some care and cleanup, but definitely too late before the 3.19 merge window opens.

Hi Thomas,
I will have a look at this after 3.19 merge window:)

Quoted 11 lines

So we have to bite the bullet and apply something like this along with a big fat comment WHY we are doing so and I'm tempted to wrap this into a #ifdef CONFIG_X86 so that noone else thinks that calling this code with interrupts disabled - except for the early boot stage - is a brilliant idea.

Thanks,

tglx

author
#16 lore raw

PATCH v2 5/9 PCI, MSI: Introduce helpers to hide struct msi_desc implementation details

In reply to Jiang Liu

Introduce helpers to hide struct msi_desc implementation details, so we could easily support non-PCI-compliant MSI devices later by moving msi_list into struct device.

---
 include/linux/msi.h |   19 +++++++++++++++++++
 1 file changed, 19 insertions(+)
include/linux/msi.h +19 -0
index 190c7abbec84..714716a3ffdd 100644
@@ -44,6 +44,25 @@ struct msi_desc {
 	struct msi_msg msg;
 };
 
+/* Helpers to hide struct msi_desc implementation details */
+#define msi_desc_to_dev(desc)		(&(desc)->dev.dev)
+#define dev_to_msi_list(dev)		(&to_pci_dev((dev))->msi_list)
+#define first_msi_entry(dev)		\
+	list_first_entry(dev_to_msi_list((dev)), struct msi_desc, list)
+#define for_each_msi_entry(desc, dev)	\
+	list_for_each_entry((desc), dev_to_msi_list((dev)), list)
+
+#ifdef CONFIG_PCI_MSI
+#define first_pci_msi_entry(pdev)	first_msi_entry(&(pdev)->dev)
+#define for_each_pci_msi_entry(desc, pdev)	\
+	for_each_msi_entry((desc), &(pdev)->dev)
+
+static inline struct pci_dev *msi_desc_to_pci_dev(struct msi_desc *desc)
+{
+	return desc->dev;
+}
+#endif /* CONFIG_PCI_MSI */
+
 void __pci_read_msi_msg(struct msi_desc *entry, struct msi_msg *msg);
 void __pci_write_msi_msg(struct msi_desc *entry, struct msi_msg *msg);
 void pci_write_msi_msg(unsigned int irq, struct msi_msg *msg);
Signature · 1 line
1.7.10.4
#17 lore raw

[tip:irq/irqdomain] PCI/MSI: Introduce helpers to hide struct msi_desc implementation details

PCI/MSI: Introduce helpers to hide struct msi_desc implementation details
Commit-ID:  d31eb342409b24e3d2e1989c775f3361e93acc08
Gitweb:     http://git.kernel.org/tip/d31eb342409b24e3d2e1989c775f3361e93acc08
Author:     Jiang Liu <[email protected]>
AuthorDate: Sat, 15 Nov 2014 22:24:03 +0800
Committer:  Thomas Gleixner <[email protected]>
CommitDate: Sun, 23 Nov 2014 13:01:45 +0100

PCI/MSI: Introduce helpers to hide struct msi_desc implementation details

Introduce helpers to hide struct msi_desc implementation details, so we could easily support non-PCI-compliant MSI devices later by moving msi_list into struct device.

Cc Tony Luck <[email protected]>
Cc Bjorn Helgaas <[email protected]>
Cc Grant Likely <[email protected]>
Cc Marc Zyngier <[email protected]>
Cc Yijing Wang <[email protected]>
Cc Yingjoe Chen <[email protected]>
Cc Borislav Petkov <[email protected]>
Cc Matthias Brugger <[email protected]>
Cc Alexander Gordeev <[email protected]>
---
 include/linux/msi.h | 19 +++++++++++++++++++
 1 file changed, 19 insertions(+)
include/linux/msi.h +19 -0
index 1dcad0c..e0d0851 100644
@@ -43,6 +43,25 @@ struct msi_desc {
 	struct msi_msg msg;
 };
 
+/* Helpers to hide struct msi_desc implementation details */
+#define msi_desc_to_dev(desc)		(&(desc)->dev.dev)
+#define dev_to_msi_list(dev)		(&to_pci_dev((dev))->msi_list)
+#define first_msi_entry(dev)		\
+	list_first_entry(dev_to_msi_list((dev)), struct msi_desc, list)
+#define for_each_msi_entry(desc, dev)	\
+	list_for_each_entry((desc), dev_to_msi_list((dev)), list)
+
+#ifdef CONFIG_PCI_MSI
+#define first_pci_msi_entry(pdev)	first_msi_entry(&(pdev)->dev)
+#define for_each_pci_msi_entry(desc, pdev)	\
+	for_each_msi_entry((desc), &(pdev)->dev)
+
+static inline struct pci_dev *msi_desc_to_pci_dev(struct msi_desc *desc)
+{
+	return desc->dev;
+}
+#endif /* CONFIG_PCI_MSI */
+
 void __pci_read_msi_msg(struct msi_desc *entry, struct msi_msg *msg);
 void __pci_write_msi_msg(struct msi_desc *entry, struct msi_msg *msg);
 void pci_write_msi_msg(unsigned int irq, struct msi_msg *msg);
author
#18 lore raw

PATCH v2 6/9 genirq: Introduce msi_domain_{alloc|free}_irqs()

In reply to Jiang Liu

Introduce msi_domain_{alloc|free}_irqs() to alloc/free interrupts from generic MSI irqdomain.

---
 include/linux/msi.h |   39 +++++++++++++++++++++++++++++++++
 kernel/irq/msi.c    |   60 +++++++++++++++++++++++++++++++++++++++++++++++++++
 2 files changed, 99 insertions(+)
include/linux/msi.h +39 -0
index 714716a3ffdd..7283e155afef 100644
@@ -3,6 +3,7 @@
 
 #include <linux/kobject.h>
 #include <linux/list.h>
+#include <asm/hw_irq.h>		/* for msi_alloc_info_t */
 
 struct msi_msg {
 	u32	address_lo;	/* low 32 bits of msi message address */
@@ -100,7 +101,29 @@ struct irq_chip;
 struct device_node;
 struct msi_domain_info;
 
+#ifndef NUM_MSI_ALLOC_SCRATCHPAD_REGS
+#define NUM_MSI_ALLOC_SCRATCHPAD_REGS	2
+#endif
+
+/*
+ * Default structure for MSI interrupt allocation.
+ * Arch may overwrite it by defining msi_alloc_info_t.
+ */
+struct msi_alloc_info {
+	struct msi_desc			*desc;
+	irq_hw_number_t			hwirq;
+	union {
+		unsigned long		ul;
+		void			*ptr;
+	} scratchpad[NUM_MSI_ALLOC_SCRATCHPAD_REGS];
+};
+
+#ifndef msi_alloc_info_t
+typedef struct msi_alloc_info msi_alloc_info_t;
+#endif
+
 struct msi_domain_ops {
+	/* Callbacks for msi_create_irq_domain() */
 	void		(*calc_hwirq)(struct msi_domain_info *info, void *arg,
 				      struct msi_desc *desc);
 	irq_hw_number_t	(*get_hwirq)(struct msi_domain_info *info, void *arg);
@@ -111,6 +134,19 @@ struct msi_domain_ops {
 	void		(*msi_free)(struct irq_domain *domain,
 				    struct msi_domain_info *info,
 				    unsigned int virq);
+
+	/* Callbacks for msi_irq_domain_alloc_irqs() based on msi_descs */
+	int		(*msi_check)(struct irq_domain *domain,
+				     struct msi_domain_info *info,
+				     struct device *dev);
+	int		(*msi_prepare)(struct irq_domain *domain,
+				       struct device *dev, int nvec,
+				       msi_alloc_info_t *arg);
+	void		(*msi_finish)(msi_alloc_info_t *arg, int retval);
+	void		(*set_desc)(msi_alloc_info_t *arg,
+				    struct msi_desc *desc);
+	int		(*handle_error)(struct irq_domain *domain,
+					struct msi_desc *desc, int error);
 };
 
 struct msi_domain_info {
@@ -125,6 +161,9 @@ int msi_domain_set_affinity(struct irq_data *data, const struct cpumask *mask,
 struct irq_domain *msi_create_irq_domain(struct device_node *of_node,
 					 struct msi_domain_info *info,
 					 struct irq_domain *parent);
+int msi_domain_alloc_irqs(struct irq_domain *domain, struct device *dev,
+			  int nvec);
+void msi_domain_free_irqs(struct irq_domain *domain, struct device *dev);
 struct msi_domain_info *msi_get_domain_info(struct irq_domain *domain);
 
 #endif /* CONFIG_GENERIC_MSI_IRQ_DOMAIN */
kernel/irq/msi.c +60 -0

72 lines not on this page · raw message

Signature · 1 line
1.7.10.4
#19 lore raw

[tip:irq/irqdomain] genirq: Introduce msi_domain_alloc/free_irqs( )

genirq: Introduce msi_domain_alloc/free_irqs()
Commit-ID:  d9109698be6e7439e6082aa00d79d4556114739b
Gitweb:     http://git.kernel.org/tip/d9109698be6e7439e6082aa00d79d4556114739b
Author:     Jiang Liu <[email protected]>
AuthorDate: Sat, 15 Nov 2014 22:24:04 +0800
Committer:  Thomas Gleixner <[email protected]>
CommitDate: Sun, 23 Nov 2014 13:01:47 +0100

genirq: Introduce msi_domain_alloc/free_irqs()

Introduce msi_domain_{alloc|free}_irqs() to alloc/free interrupts from generic MSI irqdomain.

Cc Tony Luck <[email protected]>
Cc Bjorn Helgaas <[email protected]>
Cc Grant Likely <[email protected]>
Cc Marc Zyngier <[email protected]>
Cc Yijing Wang <[email protected]>
Cc Yingjoe Chen <[email protected]>
Cc Borislav Petkov <[email protected]>
Cc Matthias Brugger <[email protected]>
Cc Alexander Gordeev <[email protected]>
---
 include/linux/msi.h | 29 +++++++++++++++++++++
 kernel/irq/msi.c    | 75 +++++++++++++++++++++++++++++++++++++++++++++++++++++
 2 files changed, 104 insertions(+)
include/linux/msi.h +29 -0
index b5ca245..7a93a98 100644
@@ -115,6 +115,9 @@ struct msi_controller {
 };
 
 #ifdef CONFIG_GENERIC_MSI_IRQ_DOMAIN
+
+#include <asm/msi.h>
+
 struct irq_domain;
 struct irq_chip;
 struct device_node;
@@ -125,6 +128,18 @@ struct msi_domain_info;
  * @get_hwirq:		Retrieve the resulting hw irq number
  * @msi_init:		Domain specific init function for MSI interrupts
  * @msi_free:		Domain specific function to free a MSI interrupts
+ * @msi_check:		Callback for verification of the domain/info/dev data
+ * @msi_prepare:	Prepare the allocation of the interrupts in the domain
+ * @msi_finish:		Optional callbacl to finalize the allocation
+ * @set_desc:		Set the msi descriptor for an interrupt
+ * @handle_error:	Optional error handler if the allocation fails
+ *
+ * @get_hwirq, @msi_init and @msi_free are callbacks used by
+ * msi_create_irq_domain() and related interfaces
+ *
+ * @msi_check, @msi_prepare, @msi_finish, @set_desc and @handle_error
+ * are callbacks used by msi_irq_domain_alloc_irqs() and related
+ * interfaces which are based on msi_desc.
  */
 struct msi_domain_ops {
 	irq_hw_number_t	(*get_hwirq)(struct msi_domain_info *info, void *arg);
@@ -135,6 +150,17 @@ struct msi_domain_ops {
 	void		(*msi_free)(struct irq_domain *domain,
 				    struct msi_domain_info *info,
 				    unsigned int virq);
+	int		(*msi_check)(struct irq_domain *domain,
+				     struct msi_domain_info *info,
+				     struct device *dev);
+	int		(*msi_prepare)(struct irq_domain *domain,
+				       struct device *dev, int nvec,
+				       msi_alloc_info_t *arg);
+	void		(*msi_finish)(msi_alloc_info_t *arg, int retval);
+	void		(*set_desc)(msi_alloc_info_t *arg,
+				    struct msi_desc *desc);
+	int		(*handle_error)(struct irq_domain *domain,
+					struct msi_desc *desc, int error);
 };
 
 /**
@@ -155,6 +181,9 @@ int msi_domain_set_affinity(struct irq_data *data, const struct cpumask *mask,
 struct irq_domain *msi_create_irq_domain(struct device_node *of_node,
 					 struct msi_domain_info *info,
 					 struct irq_domain *parent);
+int msi_domain_alloc_irqs(struct irq_domain *domain, struct device *dev,
+			  int nvec);
+void msi_domain_free_irqs(struct irq_domain *domain, struct device *dev);
 struct msi_domain_info *msi_get_domain_info(struct irq_domain *domain);
 
 #endif /* CONFIG_GENERIC_MSI_IRQ_DOMAIN */
kernel/irq/msi.c +75 -0

87 lines not on this page · raw message

author
#20 lore raw

PATCH v2 7/9 genirq: Provide default callbacks for msi_domain_ops

In reply to Jiang Liu

Extend struct msi_domain_info and provide default callbacks for msi_domain_ops.

---
 include/linux/msi.h |   29 ++++++++++---
 kernel/irq/msi.c    |  113 +++++++++++++++++++++++++++++++++++++++++++++++----
 2 files changed, 129 insertions(+), 13 deletions(-)
include/linux/msi.h +24 -5
index 7283e155afef..96ee8c3b98cc 100644
@@ -3,6 +3,7 @@
 
 #include <linux/kobject.h>
 #include <linux/list.h>
+#include <linux/irqhandler.h>
 #include <asm/hw_irq.h>		/* for msi_alloc_info_t */
 
 struct msi_msg {
@@ -124,13 +125,15 @@ typedef struct msi_alloc_info msi_alloc_info_t;
 
 struct msi_domain_ops {
 	/* Callbacks for msi_create_irq_domain() */
-	void		(*calc_hwirq)(struct msi_domain_info *info, void *arg,
+	void		(*calc_hwirq)(struct msi_domain_info *info,
+				      msi_alloc_info_t *arg,
 				      struct msi_desc *desc);
-	irq_hw_number_t	(*get_hwirq)(struct msi_domain_info *info, void *arg);
+	irq_hw_number_t	(*get_hwirq)(struct msi_domain_info *info,
+				     msi_alloc_info_t *arg);
 	int		(*msi_init)(struct irq_domain *domain,
 				    struct msi_domain_info *info,
 				    unsigned int virq, irq_hw_number_t hwirq,
-				    void *arg);
+				    msi_alloc_info_t *arg);
 	void		(*msi_free)(struct irq_domain *domain,
 				    struct msi_domain_info *info,
 				    unsigned int virq);
@@ -150,15 +153,31 @@ struct msi_domain_ops {
 };
 
 struct msi_domain_info {
+	u32			flags;
 	struct msi_domain_ops	*ops;
 	struct irq_chip		*chip;
-	void			*data;
+	void			*chip_data;	/* optional chip data */
+	irq_flow_handler_t	handler;	/* optional flow handler */
+	void			*handler_data;	/* optional handler data */
+	const char		*handler_name;	/* optional handler name */
+	void			*data;		/* optional private data */
 };
 
+/* Use default MSI domain ops if possible */
+#define MSI_FLAG_USE_DEF_DOM_OPS	0x1
+/* Use default MSI chip ops if possible */
+#define MSI_FLAG_USE_DEF_CHIP_OPS	0x2
+/* Build identity map between hwirq and irq */
+#define MSI_FLAG_IDENTITY_MAP		0x10
+/* Support multiple PCI MSI interrupts */
+#define MSI_FLAG_MULTI_PCI_MSI		0x100
+/* Support PCI MSIX interrupts */
+#define MSI_FLAG_PCI_MSIX		0x200
+
 int msi_domain_set_affinity(struct irq_data *data, const struct cpumask *mask,
 			    bool force);
 
-struct irq_domain *msi_create_irq_domain(struct device_node *of_node,
+struct irq_domain *msi_create_irq_domain(struct device_node *node,
 					 struct msi_domain_info *info,
 					 struct irq_domain *parent);
 int msi_domain_alloc_irqs(struct irq_domain *domain, struct device *dev,
kernel/irq/msi.c +105 -8

136 lines not on this page · raw message

Signature · 1 line
1.7.10.4
#21 lore raw

[tip:irq/irqdomain] genirq: Provide default callbacks for msi_domain_ops

genirq: Provide default callbacks for msi_domain_ops
Commit-ID:  aeeb59657c35da64068336c20068da237f41ab76
Gitweb:     http://git.kernel.org/tip/aeeb59657c35da64068336c20068da237f41ab76
Author:     Jiang Liu <[email protected]>
AuthorDate: Sat, 15 Nov 2014 22:24:05 +0800
Committer:  Thomas Gleixner <[email protected]>
CommitDate: Sun, 23 Nov 2014 13:01:47 +0100

genirq: Provide default callbacks for msi_domain_ops

Extend struct msi_domain_info and provide default callbacks for msi_domain_ops.

Cc Tony Luck <[email protected]>
Cc Bjorn Helgaas <[email protected]>
Cc Grant Likely <[email protected]>
Cc Marc Zyngier <[email protected]>
Cc Yijing Wang <[email protected]>
Cc Yingjoe Chen <[email protected]>
Cc Borislav Petkov <[email protected]>
Cc Matthias Brugger <[email protected]>
Cc Alexander Gordeev <[email protected]>
---
 include/linux/msi.h |  42 +++++++++++++++++---
 kernel/irq/msi.c    | 111 ++++++++++++++++++++++++++++++++++++++++++++++++----
 2 files changed, 140 insertions(+), 13 deletions(-)
include/linux/msi.h +37 -5
index 7a93a98..0098e2c 100644
@@ -116,6 +116,7 @@ struct msi_controller {
 
 #ifdef CONFIG_GENERIC_MSI_IRQ_DOMAIN
 
+#include <linux/irqhandler.h>
 #include <asm/msi.h>
 
 struct irq_domain;
@@ -142,11 +143,12 @@ struct msi_domain_info;
  * interfaces which are based on msi_desc.
  */
 struct msi_domain_ops {
-	irq_hw_number_t	(*get_hwirq)(struct msi_domain_info *info, void *arg);
+	irq_hw_number_t	(*get_hwirq)(struct msi_domain_info *info,
+				     msi_alloc_info_t *arg);
 	int		(*msi_init)(struct irq_domain *domain,
 				    struct msi_domain_info *info,
 				    unsigned int virq, irq_hw_number_t hwirq,
-				    void *arg);
+				    msi_alloc_info_t *arg);
 	void		(*msi_free)(struct irq_domain *domain,
 				    struct msi_domain_info *info,
 				    unsigned int virq);
@@ -165,16 +167,46 @@ struct msi_domain_ops {
 
 /**
  * struct msi_domain_info - MSI interrupt domain data
- * @ops:	The callback data structure
- * @chip:	The associated interrupt chip
- * @data:	Domain specific data
+ * @flags:		Flags to decribe features and capabilities
+ * @ops:		The callback data structure
+ * @chip:		Optional: associated interrupt chip
+ * @chip_data:		Optional: associated interrupt chip data
+ * @handler:		Optional: associated interrupt flow handler
+ * @handler_data:	Optional: associated interrupt flow handler data
+ * @handler_name:	Optional: associated interrupt flow handler name
+ * @data:		Optional: domain specific data
  */
 struct msi_domain_info {
+	u32			flags;
 	struct msi_domain_ops	*ops;
 	struct irq_chip		*chip;
+	void			*chip_data;
+	irq_flow_handler_t	handler;
+	void			*handler_data;
+	const char		*handler_name;
 	void			*data;
 };
 
+/* Flags for msi_domain_info */
+enum {
+	/*
+	 * Init non implemented ops callbacks with default MSI domain
+	 * callbacks.
+	 */
+	MSI_FLAG_USE_DEF_DOM_OPS	= (1 << 0),
+	/*
+	 * Init non implemented chip callbacks with default MSI chip
+	 * callbacks.
+	 */
+	MSI_FLAG_USE_DEF_CHIP_OPS	= (1 << 1),
+	/* Build identity map between hwirq and irq */
+	MSI_FLAG_IDENTITY_MAP		= (1 << 2),
+	/* Support multiple PCI MSI interrupts */
+	MSI_FLAG_MULTI_PCI_MSI		= (1 << 3),
+	/* Support PCI MSIX interrupts */
+	MSI_FLAG_PCI_MSIX		= (1 << 4),
+};
+
 int msi_domain_set_affinity(struct irq_data *data, const struct cpumask *mask,
 			    bool force);
 
kernel/irq/msi.c +103 -8

140 lines not on this page · raw message

author
#22 lore raw

PATCH v2 8/9 PCI, MSI: Refine irqdomain interfaces to simplify its usage

In reply to Jiang Liu

Refine irqdomain interfaces to simplify its usage.

---
 drivers/pci/msi.c   |  119 ++++++++++++++++++++++++++++++++++++++-------------
 include/linux/msi.h |   13 +++---
 2 files changed, 98 insertions(+), 34 deletions(-)
drivers/pci/msi.c +90 -29
index 9c53b865cb1b..1dd5d93bd4b8 100644
@@ -1099,38 +1099,99 @@ irq_hw_number_t pci_msi_domain_calc_hwirq(struct pci_dev *dev,
 		(pci_domain_nr(dev->bus) & 0xFFFFFFFF) << 27;
 }
 
-int pci_msi_domain_alloc_irqs(struct irq_domain *domain, int type,
-			      struct pci_dev *dev, void *arg)
+static inline bool pci_msi_desc_is_multi_msi(struct msi_desc *desc)
 {
-	struct msi_domain_info *info = domain->host_data;
-	int node = dev_to_node(&dev->dev);
-	struct msi_desc *desc;
-	int i, virq;
-
-	list_for_each_entry(desc, &dev->msi_list, list) {
-		if (info->ops->calc_hwirq)
-			info->ops->calc_hwirq(info, arg, desc);
-
-		virq = irq_domain_alloc_irqs(domain, desc->nvec_used,
-					     node, arg);
-		if (virq < 0) {
-			/* Special handling for pci_enable_msi_range(). */
-			if (type == PCI_CAP_ID_MSI && desc->nvec_used > 1)
-				return 1;
-			else
-				return -ENOSPC;
-		}
-		for (i = 0; i < desc->nvec_used; i++)
-			irq_set_msi_desc_off(virq, i, desc);
-	}
+	return !desc->msi_attrib.is_msix && desc->nvec_used > 1;
+}
 
-	list_for_each_entry(desc, &dev->msi_list, list)
-		if (desc->nvec_used == 1)
-			dev_dbg(&dev->dev, "irq %d for MSI/MSI-X\n", virq);
-		else
-			dev_dbg(&dev->dev, "irq [%d-%d] for MSI/MSI-X\n",
-				virq, virq + desc->nvec_used - 1);
+int pci_msi_domain_check_cap(struct irq_domain *domain,
+			     struct msi_domain_info *info, struct device *dev)
+{
+	struct msi_desc *desc = first_pci_msi_entry(to_pci_dev(dev));
+
+	/* Special handling to support pci_enable_msi_range() */
+	if (pci_msi_desc_is_multi_msi(desc) &&
+	    !(info->flags & MSI_FLAG_MULTI_PCI_MSI))
+		return 1;
+	else if (desc->msi_attrib.is_msix && !(info->flags & MSI_FLAG_PCI_MSIX))
+		return -ENOTSUPP;
 
 	return 0;
 }
+
+static int pci_msi_domain_handle_error(struct irq_domain *domain,
+				       struct msi_desc *desc, int error)
+{
+	/* Special handling to support pci_enable_msi_range() */
+	if (pci_msi_desc_is_multi_msi(desc) && error == -ENOSPC)
+		return 1;
+
+	return error;
+}
+
+#ifndef msi_alloc_info_t
+static void pci_msi_domain_set_desc(struct msi_alloc_info *arg,
+				    struct msi_desc *desc)
+{
+	arg->desc = desc;
+	arg->hwirq = pci_msi_domain_calc_hwirq(msi_desc_to_pci_dev(desc),
+					       desc);
+}
+#else
+#define pci_msi_domain_set_desc		NULL
+#endif
+
+static struct msi_domain_ops pci_msi_domain_ops_default = {
+	.set_desc	= pci_msi_domain_set_desc,
+	.msi_check	= pci_msi_domain_check_cap,
+	.handle_error	= pci_msi_domain_handle_error,
+};
+
+static void pci_msi_domain_update_dom_ops(struct msi_domain_info *info)
+{
+	struct msi_domain_ops *ops = info->ops;
+
+	if (ops == NULL) {
+		info->ops = &pci_msi_domain_ops_default;
+	} else {
+		if (ops->set_desc == NULL)
+			ops->set_desc = pci_msi_domain_set_desc;
+		if (ops->msi_check == NULL)
+			ops->msi_check = pci_msi_domain_check_cap;
+		if (ops->handle_error == NULL)
+			ops->handle_error = pci_msi_domain_handle_error;
+	}
+}
+
+static void pci_msi_domain_update_chip_ops(struct msi_domain_info *info)
+{
+	struct irq_chip *chip = info->chip;
+
+	BUG_ON(!chip);
+	if (!chip->irq_write_msi_msg)
+		chip->irq_write_msi_msg = pci_msi_domain_write_msg;
+}
+
+struct irq_domain *pci_msi_create_irq_domain(struct device_node *node,
+					     struct msi_domain_info *info,
+					     struct irq_domain *parent)
+{
+	if (info->flags & MSI_FLAG_USE_DEF_DOM_OPS)
+		pci_msi_domain_update_dom_ops(info);
+	if (info->flags & MSI_FLAG_USE_DEF_CHIP_OPS)
+		pci_msi_domain_update_chip_ops(info);
+
+	return msi_create_irq_domain(node, info, parent);
+}
+
+int pci_msi_domain_alloc_irqs(struct irq_domain *domain, struct pci_dev *dev,
+			      int nvec, int type)
+{
+	return msi_domain_alloc_irqs(domain, &dev->dev, nvec);
+}
+
+void pci_msi_domain_free_irqs(struct irq_domain *domain, struct pci_dev *dev)
+{
+	msi_domain_free_irqs(domain, &dev->dev);
+}
 #endif /* CONFIG_PCI_MSI_IRQ_DOMAIN */
include/linux/msi.h +8 -5
index 96ee8c3b98cc..0463e361fef8 100644
@@ -125,9 +125,6 @@ typedef struct msi_alloc_info msi_alloc_info_t;
 
 struct msi_domain_ops {
 	/* Callbacks for msi_create_irq_domain() */
-	void		(*calc_hwirq)(struct msi_domain_info *info,
-				      msi_alloc_info_t *arg,
-				      struct msi_desc *desc);
 	irq_hw_number_t	(*get_hwirq)(struct msi_domain_info *info,
 				     msi_alloc_info_t *arg);
 	int		(*msi_init)(struct irq_domain *domain,
@@ -189,10 +186,16 @@ struct msi_domain_info *msi_get_domain_info(struct irq_domain *domain);
 
 #ifdef CONFIG_PCI_MSI_IRQ_DOMAIN
 void pci_msi_domain_write_msg(struct irq_data *irq_data, struct msi_msg *msg);
-int pci_msi_domain_alloc_irqs(struct irq_domain *domain, int type,
-			      struct pci_dev *dev, void *arg);
+struct irq_domain *pci_msi_create_irq_domain(struct device_node *node,
+					     struct msi_domain_info *info,
+					     struct irq_domain *parent);
+int pci_msi_domain_alloc_irqs(struct irq_domain *domain, struct pci_dev *dev,
+			      int nvec, int type);
+void pci_msi_domain_free_irqs(struct irq_domain *domain, struct pci_dev *dev);
 irq_hw_number_t pci_msi_domain_calc_hwirq(struct pci_dev *dev,
 					  struct msi_desc *desc);
+int pci_msi_domain_check_cap(struct irq_domain *domain,
+			     struct msi_domain_info *info, struct device *dev);
 #endif /* CONFIG_PCI_MSI_IRQ_DOMAIN */
 
 #endif /* LINUX_MSI_H */
Signature · 1 line
1.7.10.4
author
#23 lore raw

PATCH v2 9/9 PCI, MSI: Provide mechanism to alloc/free MSI/MSIX interrupt from irqdomain

In reply to Jiang Liu

Provide mechanism to directly alloc/free MSI/MSIX interrupt from irqdomain, which will be used to replace arch_setup_msi_irq()/ arch_setup_msi_irqs()/arch_teardown_msi_irq()/arch_teardown_msi_irqs().

To kill weak functions, this patch introduce a new weak function arch_get_pci_msi_domain(), which is to retrieve the MSI irqdomain for a PCI device. This weak function could be killed once we get a common way to associate MSI domain with PCI device.

---
 drivers/pci/msi.c   |   60 +++++++++++++++++++++++++++++++++++++++++++++++----
 include/linux/msi.h |    3 +++
 2 files changed, 59 insertions(+), 4 deletions(-)
drivers/pci/msi.c +56 -4
index 1dd5d93bd4b8..a66a4eaf8111 100644
@@ -27,8 +27,41 @@ static int pci_msi_enable = 1;
 
 #define msix_table_size(flags)	((flags & PCI_MSIX_FLAGS_QSIZE) + 1)
 
-/* Arch hooks */
+#ifdef CONFIG_PCI_MSI_IRQ_DOMAIN
+static struct irq_domain *pci_msi_default_domain;
+
+struct irq_domain * __weak arch_get_pci_msi_domain(struct pci_dev *dev)
+{
+	return pci_msi_default_domain;
+}
+
+static int pci_msi_setup_msi_irqs(struct pci_dev *dev, int nvec, int type)
+{
+	struct irq_domain *domain;
 
+	domain = arch_get_pci_msi_domain(dev);
+	if (domain)
+		return pci_msi_domain_alloc_irqs(domain, dev, nvec, type);
+
+	return arch_setup_msi_irqs(dev, nvec, type);
+}
+
+static void pci_msi_teardown_msi_irqs(struct pci_dev *dev)
+{
+	struct irq_domain *domain;
+
+	domain = arch_get_pci_msi_domain(dev);
+	if (domain)
+		pci_msi_domain_free_irqs(domain, dev);
+	else
+		arch_teardown_msi_irqs(dev);
+}
+#else
+#define pci_msi_setup_msi_irqs		arch_setup_msi_irqs
+#define pci_msi_teardown_msi_irqs	arch_teardown_msi_irqs
+#endif
+
+/* Arch hooks */
 int __weak arch_setup_msi_irq(struct pci_dev *dev, struct msi_desc *desc)
 {
 	struct msi_chip *chip = dev->bus->msi;
@@ -329,7 +362,7 @@ static void free_msi_irqs(struct pci_dev *dev)
 			for (i = 0; i < entry->nvec_used; i++)
 				BUG_ON(irq_has_action(entry->irq + i));
 
-	arch_teardown_msi_irqs(dev);
+	pci_msi_teardown_msi_irqs(dev);
 
 	list_for_each_entry_safe(entry, tmp, &dev->msi_list, list) {
 		if (entry->msi_attrib.is_msix) {
@@ -581,7 +614,7 @@ static int msi_capability_init(struct pci_dev *dev, int nvec)
 	list_add_tail(&entry->list, &dev->msi_list);
 
 	/* Configure MSI capability structure */
-	ret = arch_setup_msi_irqs(dev, nvec, PCI_CAP_ID_MSI);
+	ret = pci_msi_setup_msi_irqs(dev, nvec, PCI_CAP_ID_MSI);
 	if (ret) {
 		msi_mask_irq(entry, mask, ~mask);
 		free_msi_irqs(dev);
@@ -696,7 +729,7 @@ static int msix_capability_init(struct pci_dev *dev,
 	if (ret)
 		return ret;
 
-	ret = arch_setup_msi_irqs(dev, nvec, PCI_CAP_ID_MSIX);
+	ret = pci_msi_setup_msi_irqs(dev, nvec, PCI_CAP_ID_MSIX);
 	if (ret)
 		goto out_avail;
 
@@ -1194,4 +1227,23 @@ void pci_msi_domain_free_irqs(struct irq_domain *domain, struct pci_dev *dev)
 {
 	msi_domain_free_irqs(domain, &dev->dev);
 }
+
+struct irq_domain *pci_msi_create_default_irq_domain(struct device_node *node,
+		 struct msi_domain_info *info, struct irq_domain *parent)
+{
+	struct irq_domain *domain;
+	static DEFINE_MUTEX(pci_msi_domain_lock);
+
+	mutex_lock(&pci_msi_domain_lock);
+	if (pci_msi_default_domain) {
+		pr_err("PCI: default irq domain for PCI MSI has already been created.\n");
+		domain = NULL;
+	} else {
+		domain = pci_msi_create_irq_domain(node, info, parent);
+		pci_msi_default_domain = domain;
+	}
+	mutex_unlock(&pci_msi_domain_lock);
+
+	return domain;
+}
 #endif /* CONFIG_PCI_MSI_IRQ_DOMAIN */
include/linux/msi.h +3 -0
index 0463e361fef8..ba173a6a007e 100644
@@ -192,6 +192,9 @@ struct irq_domain *pci_msi_create_irq_domain(struct device_node *node,
 int pci_msi_domain_alloc_irqs(struct irq_domain *domain, struct pci_dev *dev,
 			      int nvec, int type);
 void pci_msi_domain_free_irqs(struct irq_domain *domain, struct pci_dev *dev);
+struct irq_domain *pci_msi_create_default_irq_domain(struct device_node *node,
+		 struct msi_domain_info *info, struct irq_domain *parent);
+
 irq_hw_number_t pci_msi_domain_calc_hwirq(struct pci_dev *dev,
 					  struct msi_desc *desc);
 int pci_msi_domain_check_cap(struct irq_domain *domain,
Signature · 1 line
1.7.10.4
#24 lore raw

[tip:irq/irqdomain] PCI/MSI: Provide mechanism to alloc/free MSI/ MSIX interrupt from irqdomain

PCI/MSI: Provide mechanism to alloc/free MSI/MSIX interrupt from irqdomain
Commit-ID:  8e047adae969701c6cec136484bb9de8572af934
Gitweb:     http://git.kernel.org/tip/8e047adae969701c6cec136484bb9de8572af934
Author:     Jiang Liu <[email protected]>
AuthorDate: Sat, 15 Nov 2014 22:24:07 +0800
Committer:  Thomas Gleixner <[email protected]>
CommitDate: Sun, 23 Nov 2014 13:01:47 +0100

PCI/MSI: Provide mechanism to alloc/free MSI/MSIX interrupt from irqdomain

Provide mechanism to directly alloc/free MSI/MSIX interrupt from irqdomain, which will be used to replace arch_setup_msi_irq()/ arch_setup_msi_irqs()/arch_teardown_msi_irq()/arch_teardown_msi_irqs().

To kill weak functions, this patch introduce a new weak function arch_get_pci_msi_domain(), which is to retrieve the MSI irqdomain for a PCI device. This weak function could be killed once we get a common way to associate MSI domain with PCI device.

Cc Tony Luck <[email protected]>
Cc Bjorn Helgaas <[email protected]>
Cc Grant Likely <[email protected]>
Cc Marc Zyngier <[email protected]>
Cc Yijing Wang <[email protected]>
Cc Yingjoe Chen <[email protected]>
Cc Borislav Petkov <[email protected]>
Cc Matthias Brugger <[email protected]>
Cc Alexander Gordeev <[email protected]>
---
 drivers/pci/msi.c   | 67 ++++++++++++++++++++++++++++++++++++++++++++++++++---
 include/linux/msi.h |  3 +++
 2 files changed, 67 insertions(+), 3 deletions(-)
drivers/pci/msi.c +64 -3
index 36b0c2e..4befe09 100644
@@ -28,6 +28,40 @@ int pci_msi_ignore_mask;
 
 #define msix_table_size(flags)	((flags & PCI_MSIX_FLAGS_QSIZE) + 1)
 
+#ifdef CONFIG_PCI_MSI_IRQ_DOMAIN
+static struct irq_domain *pci_msi_default_domain;
+static DEFINE_MUTEX(pci_msi_domain_lock);
+
+struct irq_domain * __weak arch_get_pci_msi_domain(struct pci_dev *dev)
+{
+	return pci_msi_default_domain;
+}
+
+static int pci_msi_setup_msi_irqs(struct pci_dev *dev, int nvec, int type)
+{
+	struct irq_domain *domain;
+
+	domain = arch_get_pci_msi_domain(dev);
+	if (domain)
+		return pci_msi_domain_alloc_irqs(domain, dev, nvec, type);
+
+	return arch_setup_msi_irqs(dev, nvec, type);
+}
+
+static void pci_msi_teardown_msi_irqs(struct pci_dev *dev)
+{
+	struct irq_domain *domain;
+
+	domain = arch_get_pci_msi_domain(dev);
+	if (domain)
+		pci_msi_domain_free_irqs(domain, dev);
+	else
+		arch_teardown_msi_irqs(dev);
+}
+#else
+#define pci_msi_setup_msi_irqs		arch_setup_msi_irqs
+#define pci_msi_teardown_msi_irqs	arch_teardown_msi_irqs
+#endif
 
 /* Arch hooks */
 
@@ -348,7 +382,7 @@ static void free_msi_irqs(struct pci_dev *dev)
 			for (i = 0; i < entry->nvec_used; i++)
 				BUG_ON(irq_has_action(entry->irq + i));
 
-	arch_teardown_msi_irqs(dev);
+	pci_msi_teardown_msi_irqs(dev);
 
 	list_for_each_entry_safe(entry, tmp, &dev->msi_list, list) {
 		if (entry->msi_attrib.is_msix) {
@@ -600,7 +634,7 @@ static int msi_capability_init(struct pci_dev *dev, int nvec)
 	list_add_tail(&entry->list, &dev->msi_list);
 
 	/* Configure MSI capability structure */
-	ret = arch_setup_msi_irqs(dev, nvec, PCI_CAP_ID_MSI);
+	ret = pci_msi_setup_msi_irqs(dev, nvec, PCI_CAP_ID_MSI);
 	if (ret) {
 		msi_mask_irq(entry, mask, ~mask);
 		free_msi_irqs(dev);
@@ -715,7 +749,7 @@ static int msix_capability_init(struct pci_dev *dev,
 	if (ret)
 		return ret;
 
-	ret = arch_setup_msi_irqs(dev, nvec, PCI_CAP_ID_MSIX);
+	ret = pci_msi_setup_msi_irqs(dev, nvec, PCI_CAP_ID_MSIX);
 	if (ret)
 		goto out_avail;
 
@@ -1258,4 +1292,31 @@ void pci_msi_domain_free_irqs(struct irq_domain *domain, struct pci_dev *dev)
 {
 	msi_domain_free_irqs(domain, &dev->dev);
 }
+
+/**
+ * pci_msi_create_default_irq_domain - Create a default MSI interrupt domain
+ * @node:	Optional device-tree node of the interrupt controller
+ * @info:	MSI domain info
+ * @parent:	Parent irq domain
+ *
+ * Returns: A domain pointer or NULL in case of failure. If successful
+ * the default PCI/MSI irqdomain pointer is updated.
+ */
+struct irq_domain *pci_msi_create_default_irq_domain(struct device_node *node,
+		struct msi_domain_info *info, struct irq_domain *parent)
+{
+	struct irq_domain *domain;
+
+	mutex_lock(&pci_msi_domain_lock);
+	if (pci_msi_default_domain) {
+		pr_err("PCI: default irq domain for PCI MSI has already been created.\n");
+		domain = NULL;
+	} else {
+		domain = pci_msi_create_irq_domain(node, info, parent);
+		pci_msi_default_domain = domain;
+	}
+	mutex_unlock(&pci_msi_domain_lock);
+
+	return domain;
+}
 #endif /* CONFIG_PCI_MSI_IRQ_DOMAIN */
include/linux/msi.h +3 -0
index 1628788..692f217 100644
@@ -228,6 +228,9 @@ struct irq_domain *pci_msi_create_irq_domain(struct device_node *node,
 int pci_msi_domain_alloc_irqs(struct irq_domain *domain, struct pci_dev *dev,
 			      int nvec, int type);
 void pci_msi_domain_free_irqs(struct irq_domain *domain, struct pci_dev *dev);
+struct irq_domain *pci_msi_create_default_irq_domain(struct device_node *node,
+		 struct msi_domain_info *info, struct irq_domain *parent);
+
 irq_hw_number_t pci_msi_domain_calc_hwirq(struct pci_dev *dev,
 					  struct msi_desc *desc);
 int pci_msi_domain_check_cap(struct irq_domain *domain,