All of lore.kernel.org
 help / color / mirror / Atom feed
* [Patch V2 0/9] Refine generic/PCI MSI irqodmian interfaces
@ 2014-11-15 14:23 ` Jiang Liu
  0 siblings, 0 replies; 36+ messages in thread
From: Jiang Liu @ 2014-11-15 14:23 UTC (permalink / raw)
  To: Bjorn Helgaas, Thomas Gleixner, Ingo Molnar, Grant Likely,
	Marc Zyngier, Yijing Wang, Yingjoe Chen, Borislav Petkov,
	H. Peter Anvin, Matthias Brugger
  Cc: Jiang Liu, Tony Luck, linux-kernel, linux-pci, linux-arm-kernel

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(-)

-- 
1.7.10.4


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

* [Patch V2 0/9] Refine generic/PCI MSI irqodmian interfaces
@ 2014-11-15 14:23 ` Jiang Liu
  0 siblings, 0 replies; 36+ messages in thread
From: Jiang Liu @ 2014-11-15 14:23 UTC (permalink / raw)
  To: linux-arm-kernel

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(-)

-- 
1.7.10.4

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

* [Patch V2 1/9] PCI, MSI: Fix errors caused by commit e5f1a59c4e12
  2014-11-15 14:23 ` Jiang Liu
@ 2014-11-15 14:23   ` Jiang Liu
  -1 siblings, 0 replies; 36+ messages in thread
From: Jiang Liu @ 2014-11-15 14:23 UTC (permalink / raw)
  To: Bjorn Helgaas, Thomas Gleixner, Ingo Molnar, Grant Likely,
	Marc Zyngier, Yijing Wang, Yingjoe Chen, Borislav Petkov,
	H. Peter Anvin, Matthias Brugger, Jiang Liu, Alexander Gordeev
  Cc: Tony Luck, linux-kernel, linux-pci, linux-arm-kernel

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

Signed-off-by: Jiang Liu <jiang.liu@linux.intel.com>
---
 drivers/pci/msi.c   |    2 +-
 include/linux/msi.h |    2 --
 2 files changed, 1 insertion(+), 3 deletions(-)

diff --git a/drivers/pci/msi.c b/drivers/pci/msi.c
index 564850b1316e..9c53b865cb1b 100644
--- a/drivers/pci/msi.c
+++ b/drivers/pci/msi.c
@@ -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);
 }
 
 /*
diff --git a/include/linux/msi.h b/include/linux/msi.h
index 8112a17cdca1..190c7abbec84 100644
--- a/include/linux/msi.h
+++ b/include/linux/msi.h
@@ -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
-- 
1.7.10.4


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

* [Patch V2 1/9] PCI, MSI: Fix errors caused by commit e5f1a59c4e12
@ 2014-11-15 14:23   ` Jiang Liu
  0 siblings, 0 replies; 36+ messages in thread
From: Jiang Liu @ 2014-11-15 14:23 UTC (permalink / raw)
  To: linux-arm-kernel

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

Signed-off-by: Jiang Liu <jiang.liu@linux.intel.com>
---
 drivers/pci/msi.c   |    2 +-
 include/linux/msi.h |    2 --
 2 files changed, 1 insertion(+), 3 deletions(-)

diff --git a/drivers/pci/msi.c b/drivers/pci/msi.c
index 564850b1316e..9c53b865cb1b 100644
--- a/drivers/pci/msi.c
+++ b/drivers/pci/msi.c
@@ -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);
 }
 
 /*
diff --git a/include/linux/msi.h b/include/linux/msi.h
index 8112a17cdca1..190c7abbec84 100644
--- a/include/linux/msi.h
+++ b/include/linux/msi.h
@@ -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
-- 
1.7.10.4

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

* [Patch V2 2/9] irqdomain: Use consistent prototype for irq_domain_free_irqs_*
  2014-11-15 14:23 ` Jiang Liu
@ 2014-11-15 14:24   ` Jiang Liu
  -1 siblings, 0 replies; 36+ messages in thread
From: Jiang Liu @ 2014-11-15 14:24 UTC (permalink / raw)
  To: Bjorn Helgaas, Thomas Gleixner, Ingo Molnar, Grant Likely,
	Marc Zyngier, Yijing Wang, Yingjoe Chen, Borislav Petkov,
	H. Peter Anvin, Benjamin Herrenschmidt, Matthias Brugger
  Cc: Tony Luck, linux-kernel, linux-pci, linux-arm-kernel, Jiang Liu

From: Yingjoe Chen <yingjoe.chen@mediatek.com>

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

Signed-off-by: Yingjoe Chen <yingjoe.chen@mediatek.com>
Signed-off-by: Jiang Liu <jiang.liu@linux.intel.com>
---
 include/linux/irqdomain.h |    9 +++++----
 kernel/irq/irqdomain.c    |    8 ++++----
 2 files changed, 9 insertions(+), 8 deletions(-)

diff --git a/include/linux/irqdomain.h b/include/linux/irqdomain.h
index 640a1ec54772..fbe542967c20 100644
--- a/include/linux/irqdomain.h
+++ b/include/linux/irqdomain.h
@@ -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);
diff --git a/kernel/irq/irqdomain.c b/kernel/irq/irqdomain.c
index 8b0eddee0b21..705fb573e509 100644
--- a/kernel/irq/irqdomain.c
+++ b/kernel/irq/irqdomain.c
@@ -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;
 
-- 
1.7.10.4


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

* [Patch V2 2/9] irqdomain: Use consistent prototype for irq_domain_free_irqs_*
@ 2014-11-15 14:24   ` Jiang Liu
  0 siblings, 0 replies; 36+ messages in thread
From: Jiang Liu @ 2014-11-15 14:24 UTC (permalink / raw)
  To: linux-arm-kernel

From: Yingjoe Chen <yingjoe.chen@mediatek.com>

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

Signed-off-by: Yingjoe Chen <yingjoe.chen@mediatek.com>
Signed-off-by: Jiang Liu <jiang.liu@linux.intel.com>
---
 include/linux/irqdomain.h |    9 +++++----
 kernel/irq/irqdomain.c    |    8 ++++----
 2 files changed, 9 insertions(+), 8 deletions(-)

diff --git a/include/linux/irqdomain.h b/include/linux/irqdomain.h
index 640a1ec54772..fbe542967c20 100644
--- a/include/linux/irqdomain.h
+++ b/include/linux/irqdomain.h
@@ -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);
diff --git a/kernel/irq/irqdomain.c b/kernel/irq/irqdomain.c
index 8b0eddee0b21..705fb573e509 100644
--- a/kernel/irq/irqdomain.c
+++ b/kernel/irq/irqdomain.c
@@ -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;
 
-- 
1.7.10.4

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

* [Patch V2 3/9] irqdomain: Implement a method to automatically call parent domain's alloc/free
  2014-11-15 14:23 ` Jiang Liu
@ 2014-11-15 14:24   ` Jiang Liu
  -1 siblings, 0 replies; 36+ messages in thread
From: Jiang Liu @ 2014-11-15 14:24 UTC (permalink / raw)
  To: Bjorn Helgaas, Thomas Gleixner, Ingo Molnar, Grant Likely,
	Marc Zyngier, Yijing Wang, Yingjoe Chen, Borislav Petkov,
	H. Peter Anvin, Benjamin Herrenschmidt, Matthias Brugger
  Cc: Jiang Liu, Tony Luck, linux-kernel, linux-pci, linux-arm-kernel

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.

Signed-off-by: Jiang Liu <jiang.liu@linux.intel.com>
---
 include/linux/irqdomain.h |   24 ++++++-----------
 kernel/irq/irqdomain.c    |   66 +++++++++++++++++++++++++++++++++++++++++++--
 2 files changed, 72 insertions(+), 18 deletions(-)

diff --git a/include/linux/irqdomain.h b/include/linux/irqdomain.h
index fbe542967c20..ad92b0b15945 100644
--- a/include/linux/irqdomain.h
+++ b/include/linux/irqdomain.h
@@ -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)
 {
diff --git a/kernel/irq/irqdomain.c b/kernel/irq/irqdomain.c
index 705fb573e509..03fc7d018324 100644
--- a/kernel/irq/irqdomain.c
+++ b/kernel/irq/irqdomain.c
@@ -925,6 +925,43 @@ void irq_domain_free_irqs_top(struct irq_domain *domain, unsigned int virq,
 	irq_domain_free_irqs_common(domain, virq, nr_irqs);
 }
 
+static bool irq_domain_is_auto_recursive(struct irq_domain *domain)
+{
+	return domain->flags & IRQ_DOMAIN_FLAG_AUTO_RECURSIVE;
+}
+
+static void irq_domain_free_irqs_recursive(struct irq_domain *domain,
+					   unsigned int irq_base,
+					   unsigned int nr_irqs)
+{
+	domain->ops->free(domain, irq_base, nr_irqs);
+	if (irq_domain_is_auto_recursive(domain)) {
+		BUG_ON(!domain->parent);
+		irq_domain_free_irqs_recursive(domain->parent, irq_base,
+					       nr_irqs);
+	}
+}
+
+static int irq_domain_alloc_irqs_recursive(struct irq_domain *domain,
+					   unsigned int irq_base,
+					   unsigned int nr_irqs, void *arg)
+{
+	int ret = 0;
+	struct irq_domain *parent = domain->parent;
+	bool recursive = irq_domain_is_auto_recursive(domain);
+
+	BUG_ON(recursive && !parent);
+	if (recursive)
+		ret = irq_domain_alloc_irqs_recursive(parent, irq_base,
+						      nr_irqs, arg);
+	if (ret >= 0)
+		ret = domain->ops->alloc(domain, irq_base, nr_irqs, arg);
+	if (ret < 0 && recursive)
+		irq_domain_free_irqs_recursive(parent, irq_base, nr_irqs);
+
+	return ret;
+}
+
 /**
  * __irq_domain_alloc_irqs - Allocate IRQs from domain
  * @domain: domain to allocate from
@@ -981,7 +1018,7 @@ int __irq_domain_alloc_irqs(struct irq_domain *domain, int irq_base,
 	}
 
 	mutex_lock(&irq_domain_mutex);
-	ret = domain->ops->alloc(domain, virq, nr_irqs, arg);
+	ret = irq_domain_alloc_irqs_recursive(domain, virq, nr_irqs, arg);
 	if (ret < 0) {
 		mutex_unlock(&irq_domain_mutex);
 		goto out_free_irq_data;
@@ -1016,13 +1053,38 @@ void irq_domain_free_irqs(unsigned int virq, unsigned int nr_irqs)
 	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);
+	irq_domain_free_irqs_recursive(data->domain, virq, nr_irqs);
 	mutex_unlock(&irq_domain_mutex);
 
 	irq_domain_free_irq_data(virq, nr_irqs);
 	irq_free_descs(virq, nr_irqs);
 }
 
+int irq_domain_alloc_irqs_parent(struct irq_domain *domain,
+				 unsigned int irq_base, unsigned int nr_irqs,
+				 void *arg)
+{
+	/* irq_domain_alloc_irqs_recursive() has called parent's alloc() */
+	if (irq_domain_is_auto_recursive(domain))
+		return 0;
+
+	domain = domain->parent;
+	if (domain)
+		return irq_domain_alloc_irqs_recursive(domain, irq_base,
+						       nr_irqs, arg);
+
+	return -ENOSYS;
+}
+
+void irq_domain_free_irqs_parent(struct irq_domain *domain,
+				 unsigned int irq_base, unsigned int nr_irqs)
+{
+	/* irq_domain_free_irqs_recursive() will call parent's free */
+	if (!irq_domain_is_auto_recursive(domain))
+		irq_domain_free_irqs_recursive(domain->parent, irq_base,
+					       nr_irqs);
+}
+
 /**
  * irq_domain_activate_irq - Call domain_ops->activate recursively to activate
  *			     interrupt
-- 
1.7.10.4


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

* [Patch V2 3/9] irqdomain: Implement a method to automatically call parent domain's alloc/free
@ 2014-11-15 14:24   ` Jiang Liu
  0 siblings, 0 replies; 36+ messages in thread
From: Jiang Liu @ 2014-11-15 14:24 UTC (permalink / raw)
  To: linux-arm-kernel

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.

Signed-off-by: Jiang Liu <jiang.liu@linux.intel.com>
---
 include/linux/irqdomain.h |   24 ++++++-----------
 kernel/irq/irqdomain.c    |   66 +++++++++++++++++++++++++++++++++++++++++++--
 2 files changed, 72 insertions(+), 18 deletions(-)

diff --git a/include/linux/irqdomain.h b/include/linux/irqdomain.h
index fbe542967c20..ad92b0b15945 100644
--- a/include/linux/irqdomain.h
+++ b/include/linux/irqdomain.h
@@ -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)
 {
diff --git a/kernel/irq/irqdomain.c b/kernel/irq/irqdomain.c
index 705fb573e509..03fc7d018324 100644
--- a/kernel/irq/irqdomain.c
+++ b/kernel/irq/irqdomain.c
@@ -925,6 +925,43 @@ void irq_domain_free_irqs_top(struct irq_domain *domain, unsigned int virq,
 	irq_domain_free_irqs_common(domain, virq, nr_irqs);
 }
 
+static bool irq_domain_is_auto_recursive(struct irq_domain *domain)
+{
+	return domain->flags & IRQ_DOMAIN_FLAG_AUTO_RECURSIVE;
+}
+
+static void irq_domain_free_irqs_recursive(struct irq_domain *domain,
+					   unsigned int irq_base,
+					   unsigned int nr_irqs)
+{
+	domain->ops->free(domain, irq_base, nr_irqs);
+	if (irq_domain_is_auto_recursive(domain)) {
+		BUG_ON(!domain->parent);
+		irq_domain_free_irqs_recursive(domain->parent, irq_base,
+					       nr_irqs);
+	}
+}
+
+static int irq_domain_alloc_irqs_recursive(struct irq_domain *domain,
+					   unsigned int irq_base,
+					   unsigned int nr_irqs, void *arg)
+{
+	int ret = 0;
+	struct irq_domain *parent = domain->parent;
+	bool recursive = irq_domain_is_auto_recursive(domain);
+
+	BUG_ON(recursive && !parent);
+	if (recursive)
+		ret = irq_domain_alloc_irqs_recursive(parent, irq_base,
+						      nr_irqs, arg);
+	if (ret >= 0)
+		ret = domain->ops->alloc(domain, irq_base, nr_irqs, arg);
+	if (ret < 0 && recursive)
+		irq_domain_free_irqs_recursive(parent, irq_base, nr_irqs);
+
+	return ret;
+}
+
 /**
  * __irq_domain_alloc_irqs - Allocate IRQs from domain
  * @domain: domain to allocate from
@@ -981,7 +1018,7 @@ int __irq_domain_alloc_irqs(struct irq_domain *domain, int irq_base,
 	}
 
 	mutex_lock(&irq_domain_mutex);
-	ret = domain->ops->alloc(domain, virq, nr_irqs, arg);
+	ret = irq_domain_alloc_irqs_recursive(domain, virq, nr_irqs, arg);
 	if (ret < 0) {
 		mutex_unlock(&irq_domain_mutex);
 		goto out_free_irq_data;
@@ -1016,13 +1053,38 @@ void irq_domain_free_irqs(unsigned int virq, unsigned int nr_irqs)
 	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);
+	irq_domain_free_irqs_recursive(data->domain, virq, nr_irqs);
 	mutex_unlock(&irq_domain_mutex);
 
 	irq_domain_free_irq_data(virq, nr_irqs);
 	irq_free_descs(virq, nr_irqs);
 }
 
+int irq_domain_alloc_irqs_parent(struct irq_domain *domain,
+				 unsigned int irq_base, unsigned int nr_irqs,
+				 void *arg)
+{
+	/* irq_domain_alloc_irqs_recursive() has called parent's alloc() */
+	if (irq_domain_is_auto_recursive(domain))
+		return 0;
+
+	domain = domain->parent;
+	if (domain)
+		return irq_domain_alloc_irqs_recursive(domain, irq_base,
+						       nr_irqs, arg);
+
+	return -ENOSYS;
+}
+
+void irq_domain_free_irqs_parent(struct irq_domain *domain,
+				 unsigned int irq_base, unsigned int nr_irqs)
+{
+	/* irq_domain_free_irqs_recursive() will call parent's free */
+	if (!irq_domain_is_auto_recursive(domain))
+		irq_domain_free_irqs_recursive(domain->parent, irq_base,
+					       nr_irqs);
+}
+
 /**
  * irq_domain_activate_irq - Call domain_ops->activate recursively to activate
  *			     interrupt
-- 
1.7.10.4

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

* [Patch V2 4/9] irqdomain: Introduce helper function irq_domain_add_hierarchy()
  2014-11-15 14:23 ` Jiang Liu
@ 2014-11-15 14:24   ` Jiang Liu
  -1 siblings, 0 replies; 36+ messages in thread
From: Jiang Liu @ 2014-11-15 14:24 UTC (permalink / raw)
  To: Bjorn Helgaas, Thomas Gleixner, Ingo Molnar, Grant Likely,
	Marc Zyngier, Yijing Wang, Yingjoe Chen, Borislav Petkov,
	H. Peter Anvin, Benjamin Herrenschmidt, Matthias Brugger
  Cc: Jiang Liu, Tony Luck, linux-kernel, linux-pci, linux-arm-kernel

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

Signed-off-by: Jiang Liu <jiang.liu@linux.intel.com>
---
 include/linux/irqdomain.h |    4 ++++
 kernel/irq/irqdomain.c    |   19 +++++++++++++++++++
 2 files changed, 23 insertions(+)

diff --git a/include/linux/irqdomain.h b/include/linux/irqdomain.h
index ad92b0b15945..30e8c753fa98 100644
--- a/include/linux/irqdomain.h
+++ b/include/linux/irqdomain.h
@@ -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);
diff --git a/kernel/irq/irqdomain.c b/kernel/irq/irqdomain.c
index 03fc7d018324..73c66d6f9ffc 100644
--- a/kernel/irq/irqdomain.c
+++ b/kernel/irq/irqdomain.c
@@ -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;
-- 
1.7.10.4


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

* [Patch V2 4/9] irqdomain: Introduce helper function irq_domain_add_hierarchy()
@ 2014-11-15 14:24   ` Jiang Liu
  0 siblings, 0 replies; 36+ messages in thread
From: Jiang Liu @ 2014-11-15 14:24 UTC (permalink / raw)
  To: linux-arm-kernel

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

Signed-off-by: Jiang Liu <jiang.liu@linux.intel.com>
---
 include/linux/irqdomain.h |    4 ++++
 kernel/irq/irqdomain.c    |   19 +++++++++++++++++++
 2 files changed, 23 insertions(+)

diff --git a/include/linux/irqdomain.h b/include/linux/irqdomain.h
index ad92b0b15945..30e8c753fa98 100644
--- a/include/linux/irqdomain.h
+++ b/include/linux/irqdomain.h
@@ -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);
diff --git a/kernel/irq/irqdomain.c b/kernel/irq/irqdomain.c
index 03fc7d018324..73c66d6f9ffc 100644
--- a/kernel/irq/irqdomain.c
+++ b/kernel/irq/irqdomain.c
@@ -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;
-- 
1.7.10.4

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

* [Patch V2 5/9] PCI, MSI: Introduce helpers to hide struct msi_desc implementation details
  2014-11-15 14:23 ` Jiang Liu
@ 2014-11-15 14:24   ` Jiang Liu
  -1 siblings, 0 replies; 36+ messages in thread
From: Jiang Liu @ 2014-11-15 14:24 UTC (permalink / raw)
  To: Bjorn Helgaas, Thomas Gleixner, Ingo Molnar, Grant Likely,
	Marc Zyngier, Yijing Wang, Yingjoe Chen, Borislav Petkov,
	H. Peter Anvin, Matthias Brugger, Jiang Liu, Alexander Gordeev
  Cc: Tony Luck, linux-kernel, linux-pci, linux-arm-kernel

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.

Signed-off-by: Jiang Liu <jiang.liu@linux.intel.com>
---
 include/linux/msi.h |   19 +++++++++++++++++++
 1 file changed, 19 insertions(+)

diff --git a/include/linux/msi.h b/include/linux/msi.h
index 190c7abbec84..714716a3ffdd 100644
--- a/include/linux/msi.h
+++ b/include/linux/msi.h
@@ -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);
-- 
1.7.10.4


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

* [Patch V2 5/9] PCI, MSI: Introduce helpers to hide struct msi_desc implementation details
@ 2014-11-15 14:24   ` Jiang Liu
  0 siblings, 0 replies; 36+ messages in thread
From: Jiang Liu @ 2014-11-15 14:24 UTC (permalink / raw)
  To: linux-arm-kernel

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.

Signed-off-by: Jiang Liu <jiang.liu@linux.intel.com>
---
 include/linux/msi.h |   19 +++++++++++++++++++
 1 file changed, 19 insertions(+)

diff --git a/include/linux/msi.h b/include/linux/msi.h
index 190c7abbec84..714716a3ffdd 100644
--- a/include/linux/msi.h
+++ b/include/linux/msi.h
@@ -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);
-- 
1.7.10.4

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

* [Patch V2 6/9] genirq: Introduce msi_domain_{alloc|free}_irqs()
  2014-11-15 14:23 ` Jiang Liu
@ 2014-11-15 14:24   ` Jiang Liu
  -1 siblings, 0 replies; 36+ messages in thread
From: Jiang Liu @ 2014-11-15 14:24 UTC (permalink / raw)
  To: Bjorn Helgaas, Thomas Gleixner, Ingo Molnar, Grant Likely,
	Marc Zyngier, Yijing Wang, Yingjoe Chen, Borislav Petkov,
	H. Peter Anvin, Matthias Brugger, Jiang Liu, Alexander Gordeev
  Cc: Tony Luck, linux-kernel, linux-pci, linux-arm-kernel

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

Signed-off-by: Jiang Liu <jiang.liu@linux.intel.com>
---
 include/linux/msi.h |   39 +++++++++++++++++++++++++++++++++
 kernel/irq/msi.c    |   60 +++++++++++++++++++++++++++++++++++++++++++++++++++
 2 files changed, 99 insertions(+)

diff --git a/include/linux/msi.h b/include/linux/msi.h
index 714716a3ffdd..7283e155afef 100644
--- a/include/linux/msi.h
+++ b/include/linux/msi.h
@@ -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 */
diff --git a/kernel/irq/msi.c b/kernel/irq/msi.c
index 94d6d87ee23e..ce054566edf5 100644
--- a/kernel/irq/msi.c
+++ b/kernel/irq/msi.c
@@ -13,6 +13,9 @@
 #include <linux/irqdomain.h>
 #include <linux/msi.h>
 
+/* Temparory solution for building, will be removed later */
+#include <linux/pci.h>
+
 void __get_cached_msi_msg(struct msi_desc *entry, struct msi_msg *msg)
 {
 	*msg = entry->msg;
@@ -124,6 +127,63 @@ struct irq_domain *msi_create_irq_domain(struct device_node *of_node,
 	return domain;
 }
 
+int msi_domain_alloc_irqs(struct irq_domain *domain, struct device *dev,
+			  int nvec)
+{
+	int i, ret, virq = -1;
+	msi_alloc_info_t arg;
+	struct msi_desc *desc;
+	struct msi_domain_info *info = domain->host_data;
+	struct msi_domain_ops *ops = info->ops;
+
+	ret = ops->msi_check(domain, info, dev);
+	if (ret == 0)
+		ret = ops->msi_prepare(domain, dev, nvec, &arg);
+	if (ret)
+		return ret;
+
+	for_each_msi_entry(desc, dev) {
+		ops->set_desc(&arg, desc);
+
+		virq = __irq_domain_alloc_irqs(domain, -1, desc->nvec_used,
+					       dev_to_node(dev), &arg, false);
+		if (virq < 0) {
+			ret = -ENOSPC;
+			if (ops->handle_error)
+				ret = ops->handle_error(domain, desc, ret);
+			if (ops->msi_finish)
+				ops->msi_finish(&arg, ret);
+			return ret;
+		}
+
+		for (i = 0; i < desc->nvec_used; i++)
+			irq_set_msi_desc_off(virq, i, desc);
+	}
+
+	if (ops->msi_finish)
+		ops->msi_finish(&arg, 0);
+
+	for_each_msi_entry(desc, dev) {
+		if (desc->nvec_used == 1)
+			dev_dbg(dev, "irq %d for MSI\n", virq);
+		else
+			dev_dbg(dev, "irq [%d-%d] for MSI\n",
+				virq, virq + desc->nvec_used - 1);
+	}
+
+	return 0;
+}
+
+void msi_domain_free_irqs(struct irq_domain *domain, struct device *dev)
+{
+	struct msi_desc *desc;
+
+	for_each_msi_entry(desc, dev) {
+		irq_domain_free_irqs(desc->irq, desc->nvec_used);
+		desc->irq = 0;
+	}
+}
+
 struct msi_domain_info *msi_get_domain_info(struct irq_domain *domain)
 {
 	return (struct msi_domain_info *)domain->host_data;
-- 
1.7.10.4


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

* [Patch V2 6/9] genirq: Introduce msi_domain_{alloc|free}_irqs()
@ 2014-11-15 14:24   ` Jiang Liu
  0 siblings, 0 replies; 36+ messages in thread
From: Jiang Liu @ 2014-11-15 14:24 UTC (permalink / raw)
  To: linux-arm-kernel

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

Signed-off-by: Jiang Liu <jiang.liu@linux.intel.com>
---
 include/linux/msi.h |   39 +++++++++++++++++++++++++++++++++
 kernel/irq/msi.c    |   60 +++++++++++++++++++++++++++++++++++++++++++++++++++
 2 files changed, 99 insertions(+)

diff --git a/include/linux/msi.h b/include/linux/msi.h
index 714716a3ffdd..7283e155afef 100644
--- a/include/linux/msi.h
+++ b/include/linux/msi.h
@@ -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 */
diff --git a/kernel/irq/msi.c b/kernel/irq/msi.c
index 94d6d87ee23e..ce054566edf5 100644
--- a/kernel/irq/msi.c
+++ b/kernel/irq/msi.c
@@ -13,6 +13,9 @@
 #include <linux/irqdomain.h>
 #include <linux/msi.h>
 
+/* Temparory solution for building, will be removed later */
+#include <linux/pci.h>
+
 void __get_cached_msi_msg(struct msi_desc *entry, struct msi_msg *msg)
 {
 	*msg = entry->msg;
@@ -124,6 +127,63 @@ struct irq_domain *msi_create_irq_domain(struct device_node *of_node,
 	return domain;
 }
 
+int msi_domain_alloc_irqs(struct irq_domain *domain, struct device *dev,
+			  int nvec)
+{
+	int i, ret, virq = -1;
+	msi_alloc_info_t arg;
+	struct msi_desc *desc;
+	struct msi_domain_info *info = domain->host_data;
+	struct msi_domain_ops *ops = info->ops;
+
+	ret = ops->msi_check(domain, info, dev);
+	if (ret == 0)
+		ret = ops->msi_prepare(domain, dev, nvec, &arg);
+	if (ret)
+		return ret;
+
+	for_each_msi_entry(desc, dev) {
+		ops->set_desc(&arg, desc);
+
+		virq = __irq_domain_alloc_irqs(domain, -1, desc->nvec_used,
+					       dev_to_node(dev), &arg, false);
+		if (virq < 0) {
+			ret = -ENOSPC;
+			if (ops->handle_error)
+				ret = ops->handle_error(domain, desc, ret);
+			if (ops->msi_finish)
+				ops->msi_finish(&arg, ret);
+			return ret;
+		}
+
+		for (i = 0; i < desc->nvec_used; i++)
+			irq_set_msi_desc_off(virq, i, desc);
+	}
+
+	if (ops->msi_finish)
+		ops->msi_finish(&arg, 0);
+
+	for_each_msi_entry(desc, dev) {
+		if (desc->nvec_used == 1)
+			dev_dbg(dev, "irq %d for MSI\n", virq);
+		else
+			dev_dbg(dev, "irq [%d-%d] for MSI\n",
+				virq, virq + desc->nvec_used - 1);
+	}
+
+	return 0;
+}
+
+void msi_domain_free_irqs(struct irq_domain *domain, struct device *dev)
+{
+	struct msi_desc *desc;
+
+	for_each_msi_entry(desc, dev) {
+		irq_domain_free_irqs(desc->irq, desc->nvec_used);
+		desc->irq = 0;
+	}
+}
+
 struct msi_domain_info *msi_get_domain_info(struct irq_domain *domain)
 {
 	return (struct msi_domain_info *)domain->host_data;
-- 
1.7.10.4

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

* [Patch V2 7/9] genirq: Provide default callbacks for msi_domain_ops
  2014-11-15 14:23 ` Jiang Liu
@ 2014-11-15 14:24   ` Jiang Liu
  -1 siblings, 0 replies; 36+ messages in thread
From: Jiang Liu @ 2014-11-15 14:24 UTC (permalink / raw)
  To: Bjorn Helgaas, Thomas Gleixner, Ingo Molnar, Grant Likely,
	Marc Zyngier, Yijing Wang, Yingjoe Chen, Borislav Petkov,
	H. Peter Anvin, Matthias Brugger, Jiang Liu, Alexander Gordeev
  Cc: Tony Luck, linux-kernel, linux-pci, linux-arm-kernel

Extend struct msi_domain_info and provide default callbacks for
msi_domain_ops.

Signed-off-by: Jiang Liu <jiang.liu@linux.intel.com>
---
 include/linux/msi.h |   29 ++++++++++---
 kernel/irq/msi.c    |  113 +++++++++++++++++++++++++++++++++++++++++++++++----
 2 files changed, 129 insertions(+), 13 deletions(-)

diff --git a/include/linux/msi.h b/include/linux/msi.h
index 7283e155afef..96ee8c3b98cc 100644
--- a/include/linux/msi.h
+++ b/include/linux/msi.h
@@ -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,
diff --git a/kernel/irq/msi.c b/kernel/irq/msi.c
index ce054566edf5..ff15fd643b3d 100644
--- a/kernel/irq/msi.c
+++ b/kernel/irq/msi.c
@@ -9,6 +9,8 @@
  * This file contains common code to support Message Signalled Interrupt for
  * PCI compatible and non PCI compatible devices.
  */
+#include <linux/types.h>
+#include <linux/device.h>
 #include <linux/irq.h>
 #include <linux/irqdomain.h>
 #include <linux/msi.h>
@@ -114,17 +116,108 @@ static struct irq_domain_ops msi_domain_ops = {
 	.deactivate	= msi_domain_deactivate,
 };
 
-struct irq_domain *msi_create_irq_domain(struct device_node *of_node,
+#ifndef msi_alloc_info_t
+static irq_hw_number_t msi_domain_ops_get_hwirq(struct msi_domain_info *info,
+						struct msi_alloc_info *arg)
+{
+	return arg->hwirq;
+}
+
+static int msi_domain_ops_prepare(struct irq_domain *domain, struct device *dev,
+				  int nvec, struct msi_alloc_info *arg)
+{
+	memset(arg, 0, sizeof(*arg));
+
+	return 0;
+}
+
+static void msi_domain_ops_set_desc(struct msi_alloc_info *arg,
+				    struct msi_desc *desc)
+{
+	arg->desc = desc;
+}
+#else
+#define msi_domain_ops_get_hwirq	NULL
+#define msi_domain_ops_prepare		NULL
+#define msi_domain_ops_set_desc		NULL
+#endif /* msi_alloc_info_t */
+
+static int msi_domain_ops_init(struct irq_domain *domain,
+			       struct msi_domain_info *info,
+			       unsigned int virq, irq_hw_number_t hwirq,
+			       msi_alloc_info_t *arg)
+{
+	irq_domain_set_hwirq_and_chip(domain, virq, hwirq, info->chip,
+				      info->chip_data);
+	if (info->handler && info->handler_name) {
+		__irq_set_handler(virq, info->handler, 0, info->handler_name);
+		if (info->handler_data)
+			irq_set_handler_data(virq, info->handler_data);
+	}
+
+	return 0;
+}
+
+static int msi_domain_ops_check(struct irq_domain *domain,
+				struct msi_domain_info *info,
+				struct device *dev)
+{
+	return 0;
+}
+
+static struct msi_domain_ops msi_domain_ops_default = {
+	.get_hwirq	= msi_domain_ops_get_hwirq,
+	.msi_init	= msi_domain_ops_init,
+	.msi_check	= msi_domain_ops_check,
+	.msi_prepare	= msi_domain_ops_prepare,
+	.set_desc	= msi_domain_ops_set_desc,
+};
+
+static void msi_domain_update_dom_ops(struct msi_domain_info *info)
+{
+	struct msi_domain_ops *ops = info->ops;
+
+	if (ops == NULL) {
+		info->ops = &msi_domain_ops_default;
+		return;
+	}
+
+	if (ops->get_hwirq == NULL)
+		ops->get_hwirq = msi_domain_ops_default.get_hwirq;
+	if (ops->msi_init == NULL)
+		ops->msi_init = msi_domain_ops_default.msi_init;
+	if (ops->msi_check == NULL)
+		ops->msi_check = msi_domain_ops_default.msi_check;
+	if (ops->msi_prepare == NULL)
+		ops->msi_prepare = msi_domain_ops_default.msi_prepare;
+	if (ops->set_desc == NULL)
+		ops->set_desc = msi_domain_ops_default.set_desc;
+}
+
+static void msi_domain_update_chip_ops(struct msi_domain_info *info)
+{
+	struct irq_chip *chip = info->chip;
+
+	BUG_ON(!chip);
+	if (!chip->irq_mask)
+		chip->irq_mask = mask_msi_irq;
+	if (!chip->irq_unmask)
+		chip->irq_unmask = unmask_msi_irq;
+	if (!chip->irq_set_affinity)
+		chip->irq_set_affinity = msi_domain_set_affinity;
+}
+
+struct irq_domain *msi_create_irq_domain(struct device_node *node,
 					 struct msi_domain_info *info,
 					 struct irq_domain *parent)
 {
-	struct irq_domain *domain;
+	if (info->flags & MSI_FLAG_USE_DEF_DOM_OPS)
+		msi_domain_update_dom_ops(info);
+	if (info->flags & MSI_FLAG_USE_DEF_CHIP_OPS)
+		msi_domain_update_chip_ops(info);
 
-	domain = irq_domain_add_tree(of_node, &msi_domain_ops, info);
-	if (domain)
-		domain->parent = parent;
-
-	return domain;
+	return irq_domain_add_hierarchy(parent, 0, 0, node, &msi_domain_ops,
+					info);
 }
 
 int msi_domain_alloc_irqs(struct irq_domain *domain, struct device *dev,
@@ -144,8 +237,12 @@ int msi_domain_alloc_irqs(struct irq_domain *domain, struct device *dev,
 
 	for_each_msi_entry(desc, dev) {
 		ops->set_desc(&arg, desc);
+		if (info->flags & MSI_FLAG_IDENTITY_MAP)
+			virq = (int)ops->get_hwirq(info, &arg);
+		else
+			virq = -1;
 
-		virq = __irq_domain_alloc_irqs(domain, -1, desc->nvec_used,
+		virq = __irq_domain_alloc_irqs(domain, virq, desc->nvec_used,
 					       dev_to_node(dev), &arg, false);
 		if (virq < 0) {
 			ret = -ENOSPC;
-- 
1.7.10.4


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

* [Patch V2 7/9] genirq: Provide default callbacks for msi_domain_ops
@ 2014-11-15 14:24   ` Jiang Liu
  0 siblings, 0 replies; 36+ messages in thread
From: Jiang Liu @ 2014-11-15 14:24 UTC (permalink / raw)
  To: linux-arm-kernel

Extend struct msi_domain_info and provide default callbacks for
msi_domain_ops.

Signed-off-by: Jiang Liu <jiang.liu@linux.intel.com>
---
 include/linux/msi.h |   29 ++++++++++---
 kernel/irq/msi.c    |  113 +++++++++++++++++++++++++++++++++++++++++++++++----
 2 files changed, 129 insertions(+), 13 deletions(-)

diff --git a/include/linux/msi.h b/include/linux/msi.h
index 7283e155afef..96ee8c3b98cc 100644
--- a/include/linux/msi.h
+++ b/include/linux/msi.h
@@ -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,
diff --git a/kernel/irq/msi.c b/kernel/irq/msi.c
index ce054566edf5..ff15fd643b3d 100644
--- a/kernel/irq/msi.c
+++ b/kernel/irq/msi.c
@@ -9,6 +9,8 @@
  * This file contains common code to support Message Signalled Interrupt for
  * PCI compatible and non PCI compatible devices.
  */
+#include <linux/types.h>
+#include <linux/device.h>
 #include <linux/irq.h>
 #include <linux/irqdomain.h>
 #include <linux/msi.h>
@@ -114,17 +116,108 @@ static struct irq_domain_ops msi_domain_ops = {
 	.deactivate	= msi_domain_deactivate,
 };
 
-struct irq_domain *msi_create_irq_domain(struct device_node *of_node,
+#ifndef msi_alloc_info_t
+static irq_hw_number_t msi_domain_ops_get_hwirq(struct msi_domain_info *info,
+						struct msi_alloc_info *arg)
+{
+	return arg->hwirq;
+}
+
+static int msi_domain_ops_prepare(struct irq_domain *domain, struct device *dev,
+				  int nvec, struct msi_alloc_info *arg)
+{
+	memset(arg, 0, sizeof(*arg));
+
+	return 0;
+}
+
+static void msi_domain_ops_set_desc(struct msi_alloc_info *arg,
+				    struct msi_desc *desc)
+{
+	arg->desc = desc;
+}
+#else
+#define msi_domain_ops_get_hwirq	NULL
+#define msi_domain_ops_prepare		NULL
+#define msi_domain_ops_set_desc		NULL
+#endif /* msi_alloc_info_t */
+
+static int msi_domain_ops_init(struct irq_domain *domain,
+			       struct msi_domain_info *info,
+			       unsigned int virq, irq_hw_number_t hwirq,
+			       msi_alloc_info_t *arg)
+{
+	irq_domain_set_hwirq_and_chip(domain, virq, hwirq, info->chip,
+				      info->chip_data);
+	if (info->handler && info->handler_name) {
+		__irq_set_handler(virq, info->handler, 0, info->handler_name);
+		if (info->handler_data)
+			irq_set_handler_data(virq, info->handler_data);
+	}
+
+	return 0;
+}
+
+static int msi_domain_ops_check(struct irq_domain *domain,
+				struct msi_domain_info *info,
+				struct device *dev)
+{
+	return 0;
+}
+
+static struct msi_domain_ops msi_domain_ops_default = {
+	.get_hwirq	= msi_domain_ops_get_hwirq,
+	.msi_init	= msi_domain_ops_init,
+	.msi_check	= msi_domain_ops_check,
+	.msi_prepare	= msi_domain_ops_prepare,
+	.set_desc	= msi_domain_ops_set_desc,
+};
+
+static void msi_domain_update_dom_ops(struct msi_domain_info *info)
+{
+	struct msi_domain_ops *ops = info->ops;
+
+	if (ops == NULL) {
+		info->ops = &msi_domain_ops_default;
+		return;
+	}
+
+	if (ops->get_hwirq == NULL)
+		ops->get_hwirq = msi_domain_ops_default.get_hwirq;
+	if (ops->msi_init == NULL)
+		ops->msi_init = msi_domain_ops_default.msi_init;
+	if (ops->msi_check == NULL)
+		ops->msi_check = msi_domain_ops_default.msi_check;
+	if (ops->msi_prepare == NULL)
+		ops->msi_prepare = msi_domain_ops_default.msi_prepare;
+	if (ops->set_desc == NULL)
+		ops->set_desc = msi_domain_ops_default.set_desc;
+}
+
+static void msi_domain_update_chip_ops(struct msi_domain_info *info)
+{
+	struct irq_chip *chip = info->chip;
+
+	BUG_ON(!chip);
+	if (!chip->irq_mask)
+		chip->irq_mask = mask_msi_irq;
+	if (!chip->irq_unmask)
+		chip->irq_unmask = unmask_msi_irq;
+	if (!chip->irq_set_affinity)
+		chip->irq_set_affinity = msi_domain_set_affinity;
+}
+
+struct irq_domain *msi_create_irq_domain(struct device_node *node,
 					 struct msi_domain_info *info,
 					 struct irq_domain *parent)
 {
-	struct irq_domain *domain;
+	if (info->flags & MSI_FLAG_USE_DEF_DOM_OPS)
+		msi_domain_update_dom_ops(info);
+	if (info->flags & MSI_FLAG_USE_DEF_CHIP_OPS)
+		msi_domain_update_chip_ops(info);
 
-	domain = irq_domain_add_tree(of_node, &msi_domain_ops, info);
-	if (domain)
-		domain->parent = parent;
-
-	return domain;
+	return irq_domain_add_hierarchy(parent, 0, 0, node, &msi_domain_ops,
+					info);
 }
 
 int msi_domain_alloc_irqs(struct irq_domain *domain, struct device *dev,
@@ -144,8 +237,12 @@ int msi_domain_alloc_irqs(struct irq_domain *domain, struct device *dev,
 
 	for_each_msi_entry(desc, dev) {
 		ops->set_desc(&arg, desc);
+		if (info->flags & MSI_FLAG_IDENTITY_MAP)
+			virq = (int)ops->get_hwirq(info, &arg);
+		else
+			virq = -1;
 
-		virq = __irq_domain_alloc_irqs(domain, -1, desc->nvec_used,
+		virq = __irq_domain_alloc_irqs(domain, virq, desc->nvec_used,
 					       dev_to_node(dev), &arg, false);
 		if (virq < 0) {
 			ret = -ENOSPC;
-- 
1.7.10.4

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

* [Patch V2 8/9] PCI, MSI: Refine irqdomain interfaces to simplify its usage
  2014-11-15 14:23 ` Jiang Liu
@ 2014-11-15 14:24   ` Jiang Liu
  -1 siblings, 0 replies; 36+ messages in thread
From: Jiang Liu @ 2014-11-15 14:24 UTC (permalink / raw)
  To: Bjorn Helgaas, Thomas Gleixner, Ingo Molnar, Grant Likely,
	Marc Zyngier, Yijing Wang, Yingjoe Chen, Borislav Petkov,
	H. Peter Anvin, Matthias Brugger, Jiang Liu, Alexander Gordeev
  Cc: Tony Luck, linux-kernel, linux-pci, linux-arm-kernel

Refine irqdomain interfaces to simplify its usage.

Signed-off-by: Jiang Liu <jiang.liu@linux.intel.com>
---
 drivers/pci/msi.c   |  119 ++++++++++++++++++++++++++++++++++++++-------------
 include/linux/msi.h |   13 +++---
 2 files changed, 98 insertions(+), 34 deletions(-)

diff --git a/drivers/pci/msi.c b/drivers/pci/msi.c
index 9c53b865cb1b..1dd5d93bd4b8 100644
--- a/drivers/pci/msi.c
+++ b/drivers/pci/msi.c
@@ -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 */
diff --git a/include/linux/msi.h b/include/linux/msi.h
index 96ee8c3b98cc..0463e361fef8 100644
--- a/include/linux/msi.h
+++ b/include/linux/msi.h
@@ -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 */
-- 
1.7.10.4


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

* [Patch V2 8/9] PCI, MSI: Refine irqdomain interfaces to simplify its usage
@ 2014-11-15 14:24   ` Jiang Liu
  0 siblings, 0 replies; 36+ messages in thread
From: Jiang Liu @ 2014-11-15 14:24 UTC (permalink / raw)
  To: linux-arm-kernel

Refine irqdomain interfaces to simplify its usage.

Signed-off-by: Jiang Liu <jiang.liu@linux.intel.com>
---
 drivers/pci/msi.c   |  119 ++++++++++++++++++++++++++++++++++++++-------------
 include/linux/msi.h |   13 +++---
 2 files changed, 98 insertions(+), 34 deletions(-)

diff --git a/drivers/pci/msi.c b/drivers/pci/msi.c
index 9c53b865cb1b..1dd5d93bd4b8 100644
--- a/drivers/pci/msi.c
+++ b/drivers/pci/msi.c
@@ -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 */
diff --git a/include/linux/msi.h b/include/linux/msi.h
index 96ee8c3b98cc..0463e361fef8 100644
--- a/include/linux/msi.h
+++ b/include/linux/msi.h
@@ -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 */
-- 
1.7.10.4

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

* [Patch V2 9/9] PCI, MSI: Provide mechanism to alloc/free MSI/MSIX interrupt from irqdomain
  2014-11-15 14:23 ` Jiang Liu
@ 2014-11-15 14:24   ` Jiang Liu
  -1 siblings, 0 replies; 36+ messages in thread
From: Jiang Liu @ 2014-11-15 14:24 UTC (permalink / raw)
  To: Bjorn Helgaas, Thomas Gleixner, Ingo Molnar, Grant Likely,
	Marc Zyngier, Yijing Wang, Yingjoe Chen, Borislav Petkov,
	H. Peter Anvin, Matthias Brugger, Jiang Liu, Alexander Gordeev
  Cc: Tony Luck, linux-kernel, linux-pci, linux-arm-kernel

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.

Signed-off-by: Jiang Liu <jiang.liu@linux.intel.com>
---
 drivers/pci/msi.c   |   60 +++++++++++++++++++++++++++++++++++++++++++++++----
 include/linux/msi.h |    3 +++
 2 files changed, 59 insertions(+), 4 deletions(-)

diff --git a/drivers/pci/msi.c b/drivers/pci/msi.c
index 1dd5d93bd4b8..a66a4eaf8111 100644
--- a/drivers/pci/msi.c
+++ b/drivers/pci/msi.c
@@ -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 */
diff --git a/include/linux/msi.h b/include/linux/msi.h
index 0463e361fef8..ba173a6a007e 100644
--- a/include/linux/msi.h
+++ b/include/linux/msi.h
@@ -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,
-- 
1.7.10.4


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

* [Patch V2 9/9] PCI, MSI: Provide mechanism to alloc/free MSI/MSIX interrupt from irqdomain
@ 2014-11-15 14:24   ` Jiang Liu
  0 siblings, 0 replies; 36+ messages in thread
From: Jiang Liu @ 2014-11-15 14:24 UTC (permalink / raw)
  To: linux-arm-kernel

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.

Signed-off-by: Jiang Liu <jiang.liu@linux.intel.com>
---
 drivers/pci/msi.c   |   60 +++++++++++++++++++++++++++++++++++++++++++++++----
 include/linux/msi.h |    3 +++
 2 files changed, 59 insertions(+), 4 deletions(-)

diff --git a/drivers/pci/msi.c b/drivers/pci/msi.c
index 1dd5d93bd4b8..a66a4eaf8111 100644
--- a/drivers/pci/msi.c
+++ b/drivers/pci/msi.c
@@ -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 */
diff --git a/include/linux/msi.h b/include/linux/msi.h
index 0463e361fef8..ba173a6a007e 100644
--- a/include/linux/msi.h
+++ b/include/linux/msi.h
@@ -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,
-- 
1.7.10.4

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

* [tip:irq/irqdomain] PCI/MSI: Introduce helpers to hide struct msi_desc implementation details
  2014-11-15 14:24   ` Jiang Liu
  (?)
@ 2014-11-23 18:08   ` tip-bot for Jiang Liu
  -1 siblings, 0 replies; 36+ messages in thread
From: tip-bot for Jiang Liu @ 2014-11-23 18:08 UTC (permalink / raw)
  To: linux-tip-commits
  Cc: jiang.liu, tglx, bhelgaas, matthias.bgg, marc.zyngier,
	wangyijing, hpa, tony.luck, bp, linux-kernel, mingo,
	yingjoe.chen, agordeev, grant.likely

Commit-ID:  d31eb342409b24e3d2e1989c775f3361e93acc08
Gitweb:     http://git.kernel.org/tip/d31eb342409b24e3d2e1989c775f3361e93acc08
Author:     Jiang Liu <jiang.liu@linux.intel.com>
AuthorDate: Sat, 15 Nov 2014 22:24:03 +0800
Committer:  Thomas Gleixner <tglx@linutronix.de>
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.

Signed-off-by: Jiang Liu <jiang.liu@linux.intel.com>
Cc: Tony Luck <tony.luck@intel.com>
Cc: linux-arm-kernel@lists.infradead.org
Cc: Bjorn Helgaas <bhelgaas@google.com>
Cc: Grant Likely <grant.likely@linaro.org>
Cc: Marc Zyngier <marc.zyngier@arm.com>
Cc: Yijing Wang <wangyijing@huawei.com>
Cc: Yingjoe Chen <yingjoe.chen@mediatek.com>
Cc: Borislav Petkov <bp@alien8.de>
Cc: Matthias Brugger <matthias.bgg@gmail.com>
Cc: Alexander Gordeev <agordeev@redhat.com>
Link: http://lkml.kernel.org/r/1416061447-9472-6-git-send-email-jiang.liu@linux.intel.com
Signed-off-by: Thomas Gleixner <tglx@linutronix.de>
---
 include/linux/msi.h | 19 +++++++++++++++++++
 1 file changed, 19 insertions(+)

diff --git a/include/linux/msi.h b/include/linux/msi.h
index 1dcad0c..e0d0851 100644
--- a/include/linux/msi.h
+++ b/include/linux/msi.h
@@ -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);

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

* [tip:irq/irqdomain] irqdomain: Implement a method to automatically call parent domains alloc/free
  2014-11-15 14:24   ` Jiang Liu
  (?)
@ 2014-11-23 18:11   ` tip-bot for Jiang Liu
  -1 siblings, 0 replies; 36+ messages in thread
From: tip-bot for Jiang Liu @ 2014-11-23 18:11 UTC (permalink / raw)
  To: linux-tip-commits
  Cc: bhelgaas, tglx, grant.likely, matthias.bgg, marc.zyngier, hpa,
	wangyijing, yingjoe.chen, tony.luck, linux-kernel, bp, benh,
	jiang.liu, mingo

Commit-ID:  36d727310cb9f85efb5ac089ffb1797e7c3538e1
Gitweb:     http://git.kernel.org/tip/36d727310cb9f85efb5ac089ffb1797e7c3538e1
Author:     Jiang Liu <jiang.liu@linux.intel.com>
AuthorDate: Sat, 15 Nov 2014 22:24:01 +0800
Committer:  Thomas Gleixner <tglx@linutronix.de>
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.

Signed-off-by: Jiang Liu <jiang.liu@linux.intel.com>
Cc: Tony Luck <tony.luck@intel.com>
Cc: linux-arm-kernel@lists.infradead.org
Cc: Bjorn Helgaas <bhelgaas@google.com>
Cc: Grant Likely <grant.likely@linaro.org>
Cc: Marc Zyngier <marc.zyngier@arm.com>
Cc: Yijing Wang <wangyijing@huawei.com>
Cc: Yingjoe Chen <yingjoe.chen@mediatek.com>
Cc: Borislav Petkov <bp@alien8.de>
Cc: Benjamin Herrenschmidt <benh@kernel.crashing.org>
Cc: Matthias Brugger <matthias.bgg@gmail.com>
Link: http://lkml.kernel.org/r/1416061447-9472-4-git-send-email-jiang.liu@linux.intel.com
Signed-off-by: Thomas Gleixner <tglx@linutronix.de>
---
 include/linux/irqdomain.h | 24 ++++++--------
 kernel/irq/irqdomain.c    | 82 +++++++++++++++++++++++++++++++++++++++++++++--
 2 files changed, 89 insertions(+), 17 deletions(-)

diff --git a/include/linux/irqdomain.h b/include/linux/irqdomain.h
index 7aca1ad..dd2709b 100644
--- a/include/linux/irqdomain.h
+++ b/include/linux/irqdomain.h
@@ -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)
 {
diff --git a/kernel/irq/irqdomain.c b/kernel/irq/irqdomain.c
index 4e62832..9c88db7 100644
--- a/kernel/irq/irqdomain.c
+++ b/kernel/irq/irqdomain.c
@@ -960,6 +960,43 @@ void irq_domain_free_irqs_top(struct irq_domain *domain, unsigned int virq,
 	irq_domain_free_irqs_common(domain, virq, nr_irqs);
 }
 
+static bool irq_domain_is_auto_recursive(struct irq_domain *domain)
+{
+	return domain->flags & IRQ_DOMAIN_FLAG_AUTO_RECURSIVE;
+}
+
+static void irq_domain_free_irqs_recursive(struct irq_domain *domain,
+					   unsigned int irq_base,
+					   unsigned int nr_irqs)
+{
+	domain->ops->free(domain, irq_base, nr_irqs);
+	if (irq_domain_is_auto_recursive(domain)) {
+		BUG_ON(!domain->parent);
+		irq_domain_free_irqs_recursive(domain->parent, irq_base,
+					       nr_irqs);
+	}
+}
+
+static int irq_domain_alloc_irqs_recursive(struct irq_domain *domain,
+					   unsigned int irq_base,
+					   unsigned int nr_irqs, void *arg)
+{
+	int ret = 0;
+	struct irq_domain *parent = domain->parent;
+	bool recursive = irq_domain_is_auto_recursive(domain);
+
+	BUG_ON(recursive && !parent);
+	if (recursive)
+		ret = irq_domain_alloc_irqs_recursive(parent, irq_base,
+						      nr_irqs, arg);
+	if (ret >= 0)
+		ret = domain->ops->alloc(domain, irq_base, nr_irqs, arg);
+	if (ret < 0 && recursive)
+		irq_domain_free_irqs_recursive(parent, irq_base, nr_irqs);
+
+	return ret;
+}
+
 /**
  * __irq_domain_alloc_irqs - Allocate IRQs from domain
  * @domain:	domain to allocate from
@@ -1016,7 +1053,7 @@ int __irq_domain_alloc_irqs(struct irq_domain *domain, int irq_base,
 	}
 
 	mutex_lock(&irq_domain_mutex);
-	ret = domain->ops->alloc(domain, virq, nr_irqs, arg);
+	ret = irq_domain_alloc_irqs_recursive(domain, virq, nr_irqs, arg);
 	if (ret < 0) {
 		mutex_unlock(&irq_domain_mutex);
 		goto out_free_irq_data;
@@ -1051,7 +1088,7 @@ void irq_domain_free_irqs(unsigned int virq, unsigned int nr_irqs)
 	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);
+	irq_domain_free_irqs_recursive(data->domain, virq, nr_irqs);
 	mutex_unlock(&irq_domain_mutex);
 
 	irq_domain_free_irq_data(virq, nr_irqs);
@@ -1059,6 +1096,47 @@ void irq_domain_free_irqs(unsigned int virq, unsigned int nr_irqs)
 }
 
 /**
+ * irq_domain_alloc_irqs_parent - Allocate interrupts from parent domain
+ * @irq_base:	Base IRQ number
+ * @nr_irqs:	Number of IRQs to allocate
+ * @arg:	Allocation data (arch/domain specific)
+ *
+ * Check whether the domain has been setup recursive. If not allocate
+ * through the parent domain.
+ */
+int irq_domain_alloc_irqs_parent(struct irq_domain *domain,
+				 unsigned int irq_base, unsigned int nr_irqs,
+				 void *arg)
+{
+	/* irq_domain_alloc_irqs_recursive() has called parent's alloc() */
+	if (irq_domain_is_auto_recursive(domain))
+		return 0;
+
+	domain = domain->parent;
+	if (domain)
+		return irq_domain_alloc_irqs_recursive(domain, irq_base,
+						       nr_irqs, arg);
+	return -ENOSYS;
+}
+
+/**
+ * irq_domain_free_irqs_parent - Free interrupts from parent domain
+ * @irq_base:	Base IRQ number
+ * @nr_irqs:	Number of IRQs to free
+ *
+ * Check whether the domain has been setup recursive. If not free
+ * through the parent domain.
+ */
+void irq_domain_free_irqs_parent(struct irq_domain *domain,
+				 unsigned int irq_base, unsigned int nr_irqs)
+{
+	/* irq_domain_free_irqs_recursive() will call parent's free */
+	if (!irq_domain_is_auto_recursive(domain) && domain->parent)
+		irq_domain_free_irqs_recursive(domain->parent, irq_base,
+					       nr_irqs);
+}
+
+/**
  * irq_domain_activate_irq - Call domain_ops->activate recursively to activate
  *			     interrupt
  * @irq_data:	outermost irq_data associated with interrupt

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

* [tip:irq/irqdomain] irqdomain: Introduce helper function irq_domain_add_hierarchy()
  2014-11-15 14:24   ` Jiang Liu
  (?)
@ 2014-11-23 18:11   ` tip-bot for Jiang Liu
  2014-11-29 12:53     ` Borislav Petkov
  -1 siblings, 1 reply; 36+ messages in thread
From: tip-bot for Jiang Liu @ 2014-11-23 18:11 UTC (permalink / raw)
  To: linux-tip-commits
  Cc: matthias.bgg, tony.luck, tglx, benh, linux-kernel, hpa,
	grant.likely, wangyijing, marc.zyngier, bhelgaas, yingjoe.chen,
	bp, mingo, jiang.liu

Commit-ID:  afb7da83b9f476728623130703acb553d7c7c4d9
Gitweb:     http://git.kernel.org/tip/afb7da83b9f476728623130703acb553d7c7c4d9
Author:     Jiang Liu <jiang.liu@linux.intel.com>
AuthorDate: Sat, 15 Nov 2014 22:24:02 +0800
Committer:  Thomas Gleixner <tglx@linutronix.de>
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.

Signed-off-by: Jiang Liu <jiang.liu@linux.intel.com>
Cc: Tony Luck <tony.luck@intel.com>
Cc: linux-arm-kernel@lists.infradead.org
Cc: Bjorn Helgaas <bhelgaas@google.com>
Cc: Grant Likely <grant.likely@linaro.org>
Cc: Marc Zyngier <marc.zyngier@arm.com>
Cc: Yijing Wang <wangyijing@huawei.com>
Cc: Yingjoe Chen <yingjoe.chen@mediatek.com>
Cc: Borislav Petkov <bp@alien8.de>
Cc: Benjamin Herrenschmidt <benh@kernel.crashing.org>
Cc: Matthias Brugger <matthias.bgg@gmail.com>
Link: http://lkml.kernel.org/r/1416061447-9472-5-git-send-email-jiang.liu@linux.intel.com
Signed-off-by: Thomas Gleixner <tglx@linutronix.de>
---
 include/linux/irqdomain.h |  4 ++++
 kernel/irq/irqdomain.c    | 36 ++++++++++++++++++++++++++++++++++++
 2 files changed, 40 insertions(+)

diff --git a/include/linux/irqdomain.h b/include/linux/irqdomain.h
index dd2709b..676d730 100644
--- a/include/linux/irqdomain.h
+++ b/include/linux/irqdomain.h
@@ -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);
diff --git a/kernel/irq/irqdomain.c b/kernel/irq/irqdomain.c
index 9c88db7..7fac311 100644
--- a/kernel/irq/irqdomain.c
+++ b/kernel/irq/irqdomain.c
@@ -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;

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

* [tip:irq/irqdomain] genirq: Introduce msi_domain_alloc/free_irqs( )
  2014-11-15 14:24   ` Jiang Liu
  (?)
@ 2014-11-23 18:13   ` tip-bot for Jiang Liu
  -1 siblings, 0 replies; 36+ messages in thread
From: tip-bot for Jiang Liu @ 2014-11-23 18:13 UTC (permalink / raw)
  To: linux-tip-commits
  Cc: wangyijing, tglx, marc.zyngier, matthias.bgg, yingjoe.chen,
	tony.luck, hpa, grant.likely, bp, bhelgaas, jiang.liu, mingo,
	linux-kernel, agordeev

Commit-ID:  d9109698be6e7439e6082aa00d79d4556114739b
Gitweb:     http://git.kernel.org/tip/d9109698be6e7439e6082aa00d79d4556114739b
Author:     Jiang Liu <jiang.liu@linux.intel.com>
AuthorDate: Sat, 15 Nov 2014 22:24:04 +0800
Committer:  Thomas Gleixner <tglx@linutronix.de>
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.

Signed-off-by: Jiang Liu <jiang.liu@linux.intel.com>
Cc: Tony Luck <tony.luck@intel.com>
Cc: linux-arm-kernel@lists.infradead.org
Cc: Bjorn Helgaas <bhelgaas@google.com>
Cc: Grant Likely <grant.likely@linaro.org>
Cc: Marc Zyngier <marc.zyngier@arm.com>
Cc: Yijing Wang <wangyijing@huawei.com>
Cc: Yingjoe Chen <yingjoe.chen@mediatek.com>
Cc: Borislav Petkov <bp@alien8.de>
Cc: Matthias Brugger <matthias.bgg@gmail.com>
Cc: Alexander Gordeev <agordeev@redhat.com>
Link: http://lkml.kernel.org/r/1416061447-9472-7-git-send-email-jiang.liu@linux.intel.com
Signed-off-by: Thomas Gleixner <tglx@linutronix.de>
---
 include/linux/msi.h | 29 +++++++++++++++++++++
 kernel/irq/msi.c    | 75 +++++++++++++++++++++++++++++++++++++++++++++++++++++
 2 files changed, 104 insertions(+)

diff --git a/include/linux/msi.h b/include/linux/msi.h
index b5ca245..7a93a98 100644
--- a/include/linux/msi.h
+++ b/include/linux/msi.h
@@ -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 */
diff --git a/kernel/irq/msi.c b/kernel/irq/msi.c
index 5e0cef4..23111aa 100644
--- a/kernel/irq/msi.c
+++ b/kernel/irq/msi.c
@@ -13,6 +13,9 @@
 #include <linux/irqdomain.h>
 #include <linux/msi.h>
 
+/* Temparory solution for building, will be removed later */
+#include <linux/pci.h>
+
 #ifdef CONFIG_GENERIC_MSI_IRQ_DOMAIN
 /**
  * msi_domain_set_affinity - Generic affinity setter function for MSI domains
@@ -127,6 +130,78 @@ struct irq_domain *msi_create_irq_domain(struct device_node *of_node,
 }
 
 /**
+ * msi_domain_alloc_irqs - Allocate interrupts from a MSI interrupt domain
+ * @domain:	The domain to allocate from
+ * @dev:	Pointer to device struct of the device for which the interrupts
+ *		are allocated
+ * @nvec:	The number of interrupts to allocate
+ *
+ * Returns 0 on success or an error code.
+ */
+int msi_domain_alloc_irqs(struct irq_domain *domain, struct device *dev,
+			  int nvec)
+{
+	struct msi_domain_info *info = domain->host_data;
+	struct msi_domain_ops *ops = info->ops;
+	msi_alloc_info_t arg;
+	struct msi_desc *desc;
+	int i, ret, virq = -1;
+
+	ret = ops->msi_check(domain, info, dev);
+	if (ret == 0)
+		ret = ops->msi_prepare(domain, dev, nvec, &arg);
+	if (ret)
+		return ret;
+
+	for_each_msi_entry(desc, dev) {
+		ops->set_desc(&arg, desc);
+
+		virq = __irq_domain_alloc_irqs(domain, -1, desc->nvec_used,
+					       dev_to_node(dev), &arg, false);
+		if (virq < 0) {
+			ret = -ENOSPC;
+			if (ops->handle_error)
+				ret = ops->handle_error(domain, desc, ret);
+			if (ops->msi_finish)
+				ops->msi_finish(&arg, ret);
+			return ret;
+		}
+
+		for (i = 0; i < desc->nvec_used; i++)
+			irq_set_msi_desc_off(virq, i, desc);
+	}
+
+	if (ops->msi_finish)
+		ops->msi_finish(&arg, 0);
+
+	for_each_msi_entry(desc, dev) {
+		if (desc->nvec_used == 1)
+			dev_dbg(dev, "irq %d for MSI\n", virq);
+		else
+			dev_dbg(dev, "irq [%d-%d] for MSI\n",
+				virq, virq + desc->nvec_used - 1);
+	}
+
+	return 0;
+}
+
+/**
+ * msi_domain_free_irqs - Free interrupts from a MSI interrupt @domain associated tp @dev
+ * @domain:	The domain to managing the interrupts
+ * @dev:	Pointer to device struct of the device for which the interrupts
+ *		are free
+ */
+void msi_domain_free_irqs(struct irq_domain *domain, struct device *dev)
+{
+	struct msi_desc *desc;
+
+	for_each_msi_entry(desc, dev) {
+		irq_domain_free_irqs(desc->irq, desc->nvec_used);
+		desc->irq = 0;
+	}
+}
+
+/**
  * msi_get_domain_info - Get the MSI interrupt domain info for @domain
  * @domain:	The interrupt domain to retrieve data from
  *

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

* [tip:irq/irqdomain] genirq: Provide default callbacks for msi_domain_ops
  2014-11-15 14:24   ` Jiang Liu
  (?)
@ 2014-11-23 18:13   ` tip-bot for Jiang Liu
  -1 siblings, 0 replies; 36+ messages in thread
From: tip-bot for Jiang Liu @ 2014-11-23 18:13 UTC (permalink / raw)
  To: linux-tip-commits
  Cc: matthias.bgg, hpa, agordeev, tglx, mingo, bhelgaas, linux-kernel,
	bp, grant.likely, tony.luck, yingjoe.chen, marc.zyngier,
	jiang.liu, wangyijing

Commit-ID:  aeeb59657c35da64068336c20068da237f41ab76
Gitweb:     http://git.kernel.org/tip/aeeb59657c35da64068336c20068da237f41ab76
Author:     Jiang Liu <jiang.liu@linux.intel.com>
AuthorDate: Sat, 15 Nov 2014 22:24:05 +0800
Committer:  Thomas Gleixner <tglx@linutronix.de>
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.

Signed-off-by: Jiang Liu <jiang.liu@linux.intel.com>
Cc: Tony Luck <tony.luck@intel.com>
Cc: linux-arm-kernel@lists.infradead.org
Cc: Bjorn Helgaas <bhelgaas@google.com>
Cc: Grant Likely <grant.likely@linaro.org>
Cc: Marc Zyngier <marc.zyngier@arm.com>
Cc: Yijing Wang <wangyijing@huawei.com>
Cc: Yingjoe Chen <yingjoe.chen@mediatek.com>
Cc: Borislav Petkov <bp@alien8.de>
Cc: Matthias Brugger <matthias.bgg@gmail.com>
Cc: Alexander Gordeev <agordeev@redhat.com>
Link: http://lkml.kernel.org/r/1416061447-9472-8-git-send-email-jiang.liu@linux.intel.com
Signed-off-by: Thomas Gleixner <tglx@linutronix.de>
---
 include/linux/msi.h |  42 +++++++++++++++++---
 kernel/irq/msi.c    | 111 ++++++++++++++++++++++++++++++++++++++++++++++++----
 2 files changed, 140 insertions(+), 13 deletions(-)

diff --git a/include/linux/msi.h b/include/linux/msi.h
index 7a93a98..0098e2c 100644
--- a/include/linux/msi.h
+++ b/include/linux/msi.h
@@ -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);
 
diff --git a/kernel/irq/msi.c b/kernel/irq/msi.c
index 23111aa..d0fe84d 100644
--- a/kernel/irq/msi.c
+++ b/kernel/irq/msi.c
@@ -9,6 +9,8 @@
  * This file contains common code to support Message Signalled Interrupt for
  * PCI compatible and non PCI compatible devices.
  */
+#include <linux/types.h>
+#include <linux/device.h>
 #include <linux/irq.h>
 #include <linux/irqdomain.h>
 #include <linux/msi.h>
@@ -110,23 +112,112 @@ static struct irq_domain_ops msi_domain_ops = {
 	.deactivate	= msi_domain_deactivate,
 };
 
+#ifdef GENERIC_MSI_DOMAIN_OPS
+static irq_hw_number_t msi_domain_ops_get_hwirq(struct msi_domain_info *info,
+						msi_alloc_info_t *arg)
+{
+	return arg->hwirq;
+}
+
+static int msi_domain_ops_prepare(struct irq_domain *domain, struct device *dev,
+				  int nvec, msi_alloc_info_t *arg)
+{
+	memset(arg, 0, sizeof(*arg));
+	return 0;
+}
+
+static void msi_domain_ops_set_desc(msi_alloc_info_t *arg,
+				    struct msi_desc *desc)
+{
+	arg->desc = desc;
+}
+#else
+#define msi_domain_ops_get_hwirq	NULL
+#define msi_domain_ops_prepare		NULL
+#define msi_domain_ops_set_desc		NULL
+#endif /* !GENERIC_MSI_DOMAIN_OPS */
+
+static int msi_domain_ops_init(struct irq_domain *domain,
+			       struct msi_domain_info *info,
+			       unsigned int virq, irq_hw_number_t hwirq,
+			       msi_alloc_info_t *arg)
+{
+	irq_domain_set_hwirq_and_chip(domain, virq, hwirq, info->chip,
+				      info->chip_data);
+	if (info->handler && info->handler_name) {
+		__irq_set_handler(virq, info->handler, 0, info->handler_name);
+		if (info->handler_data)
+			irq_set_handler_data(virq, info->handler_data);
+	}
+	return 0;
+}
+
+static int msi_domain_ops_check(struct irq_domain *domain,
+				struct msi_domain_info *info,
+				struct device *dev)
+{
+	return 0;
+}
+
+static struct msi_domain_ops msi_domain_ops_default = {
+	.get_hwirq	= msi_domain_ops_get_hwirq,
+	.msi_init	= msi_domain_ops_init,
+	.msi_check	= msi_domain_ops_check,
+	.msi_prepare	= msi_domain_ops_prepare,
+	.set_desc	= msi_domain_ops_set_desc,
+};
+
+static void msi_domain_update_dom_ops(struct msi_domain_info *info)
+{
+	struct msi_domain_ops *ops = info->ops;
+
+	if (ops == NULL) {
+		info->ops = &msi_domain_ops_default;
+		return;
+	}
+
+	if (ops->get_hwirq == NULL)
+		ops->get_hwirq = msi_domain_ops_default.get_hwirq;
+	if (ops->msi_init == NULL)
+		ops->msi_init = msi_domain_ops_default.msi_init;
+	if (ops->msi_check == NULL)
+		ops->msi_check = msi_domain_ops_default.msi_check;
+	if (ops->msi_prepare == NULL)
+		ops->msi_prepare = msi_domain_ops_default.msi_prepare;
+	if (ops->set_desc == NULL)
+		ops->set_desc = msi_domain_ops_default.set_desc;
+}
+
+static void msi_domain_update_chip_ops(struct msi_domain_info *info)
+{
+	struct irq_chip *chip = info->chip;
+
+	BUG_ON(!chip);
+	if (!chip->irq_mask)
+		chip->irq_mask = pci_msi_mask_irq;
+	if (!chip->irq_unmask)
+		chip->irq_unmask = pci_msi_unmask_irq;
+	if (!chip->irq_set_affinity)
+		chip->irq_set_affinity = msi_domain_set_affinity;
+}
+
 /**
  * msi_create_irq_domain - Create a MSI interrupt domain
  * @of_node:	Optional device-tree node of the interrupt controller
  * @info:	MSI domain info
  * @parent:	Parent irq domain
  */
-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)
 {
-	struct irq_domain *domain;
+	if (info->flags & MSI_FLAG_USE_DEF_DOM_OPS)
+		msi_domain_update_dom_ops(info);
+	if (info->flags & MSI_FLAG_USE_DEF_CHIP_OPS)
+		msi_domain_update_chip_ops(info);
 
-	domain = irq_domain_add_tree(of_node, &msi_domain_ops, info);
-	if (domain)
-		domain->parent = parent;
-
-	return domain;
+	return irq_domain_add_hierarchy(parent, 0, 0, node, &msi_domain_ops,
+					info);
 }
 
 /**
@@ -155,8 +246,12 @@ int msi_domain_alloc_irqs(struct irq_domain *domain, struct device *dev,
 
 	for_each_msi_entry(desc, dev) {
 		ops->set_desc(&arg, desc);
+		if (info->flags & MSI_FLAG_IDENTITY_MAP)
+			virq = (int)ops->get_hwirq(info, &arg);
+		else
+			virq = -1;
 
-		virq = __irq_domain_alloc_irqs(domain, -1, desc->nvec_used,
+		virq = __irq_domain_alloc_irqs(domain, virq, desc->nvec_used,
 					       dev_to_node(dev), &arg, false);
 		if (virq < 0) {
 			ret = -ENOSPC;

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

* [tip:irq/irqdomain] PCI/MSI: Provide mechanism to alloc/free MSI/ MSIX interrupt from irqdomain
  2014-11-15 14:24   ` Jiang Liu
  (?)
@ 2014-11-23 18:14   ` tip-bot for Jiang Liu
  -1 siblings, 0 replies; 36+ messages in thread
From: tip-bot for Jiang Liu @ 2014-11-23 18:14 UTC (permalink / raw)
  To: linux-tip-commits
  Cc: marc.zyngier, bp, tglx, wangyijing, hpa, agordeev, tony.luck,
	bhelgaas, mingo, yingjoe.chen, linux-kernel, grant.likely,
	jiang.liu, matthias.bgg

Commit-ID:  8e047adae969701c6cec136484bb9de8572af934
Gitweb:     http://git.kernel.org/tip/8e047adae969701c6cec136484bb9de8572af934
Author:     Jiang Liu <jiang.liu@linux.intel.com>
AuthorDate: Sat, 15 Nov 2014 22:24:07 +0800
Committer:  Thomas Gleixner <tglx@linutronix.de>
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.

Signed-off-by: Jiang Liu <jiang.liu@linux.intel.com>
Cc: Tony Luck <tony.luck@intel.com>
Cc: linux-arm-kernel@lists.infradead.org
Cc: Bjorn Helgaas <bhelgaas@google.com>
Cc: Grant Likely <grant.likely@linaro.org>
Cc: Marc Zyngier <marc.zyngier@arm.com>
Cc: Yijing Wang <wangyijing@huawei.com>
Cc: Yingjoe Chen <yingjoe.chen@mediatek.com>
Cc: Borislav Petkov <bp@alien8.de>
Cc: Matthias Brugger <matthias.bgg@gmail.com>
Cc: Alexander Gordeev <agordeev@redhat.com>
Link: http://lkml.kernel.org/r/1416061447-9472-10-git-send-email-jiang.liu@linux.intel.com
Signed-off-by: Thomas Gleixner <tglx@linutronix.de>
---
 drivers/pci/msi.c   | 67 ++++++++++++++++++++++++++++++++++++++++++++++++++---
 include/linux/msi.h |  3 +++
 2 files changed, 67 insertions(+), 3 deletions(-)

diff --git a/drivers/pci/msi.c b/drivers/pci/msi.c
index 36b0c2e..4befe09 100644
--- a/drivers/pci/msi.c
+++ b/drivers/pci/msi.c
@@ -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 */
diff --git a/include/linux/msi.h b/include/linux/msi.h
index 1628788..692f217 100644
--- a/include/linux/msi.h
+++ b/include/linux/msi.h
@@ -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,

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

* Re: [tip:irq/irqdomain] irqdomain: Introduce helper function irq_domain_add_hierarchy()
  2014-11-23 18:11   ` [tip:irq/irqdomain] " tip-bot for Jiang Liu
@ 2014-11-29 12:53     ` Borislav Petkov
  2014-11-29 14:29       ` Jiang Liu
  2014-11-29 20:42       ` Thomas Gleixner
  0 siblings, 2 replies; 36+ messages in thread
From: Borislav Petkov @ 2014-11-29 12:53 UTC (permalink / raw)
  To: Jiang Liu
  Cc: linux-tip-commits, matthias.bgg, tony.luck, tglx, benh,
	linux-kernel, hpa, grant.likely, wangyijing, marc.zyngier,
	bhelgaas, yingjoe.chen, mingo, jiang.liu

On Sun, Nov 23, 2014 at 10:11:44AM -0800, tip-bot for Jiang Liu wrote:
> Commit-ID:  afb7da83b9f476728623130703acb553d7c7c4d9
> Gitweb:     http://git.kernel.org/tip/afb7da83b9f476728623130703acb553d7c7c4d9
> Author:     Jiang Liu <jiang.liu@linux.intel.com>
> AuthorDate: Sat, 15 Nov 2014 22:24:02 +0800
> Committer:  Thomas Gleixner <tglx@linutronix.de>
> 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.
> 
> Signed-off-by: Jiang Liu <jiang.liu@linux.intel.com>
> Cc: Tony Luck <tony.luck@intel.com>
> Cc: linux-arm-kernel@lists.infradead.org
> Cc: Bjorn Helgaas <bhelgaas@google.com>
> Cc: Grant Likely <grant.likely@linaro.org>
> Cc: Marc Zyngier <marc.zyngier@arm.com>
> Cc: Yijing Wang <wangyijing@huawei.com>
> Cc: Yingjoe Chen <yingjoe.chen@mediatek.com>
> Cc: Borislav Petkov <bp@alien8.de>
> Cc: Benjamin Herrenschmidt <benh@kernel.crashing.org>
> Cc: Matthias Brugger <matthias.bgg@gmail.com>
> Link: http://lkml.kernel.org/r/1416061447-9472-5-git-send-email-jiang.liu@linux.intel.com
> Signed-off-by: Thomas Gleixner <tglx@linutronix.de>
> ---
>  include/linux/irqdomain.h |  4 ++++
>  kernel/irq/irqdomain.c    | 36 ++++++++++++++++++++++++++++++++++++
>  2 files changed, 40 insertions(+)
> 
> diff --git a/include/linux/irqdomain.h b/include/linux/irqdomain.h
> index dd2709b..676d730 100644
> --- a/include/linux/irqdomain.h
> +++ b/include/linux/irqdomain.h
> @@ -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);
> diff --git a/kernel/irq/irqdomain.c b/kernel/irq/irqdomain.c
> index 9c88db7..7fac311 100644
> --- a/kernel/irq/irqdomain.c
> +++ b/kernel/irq/irqdomain.c
> @@ -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;
> +	}

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:

---
diff --git a/kernel/irq/irqdomain.c b/kernel/irq/irqdomain.c
index 7fac311057b8..c21a003b996a 100644
--- a/kernel/irq/irqdomain.c
+++ b/kernel/irq/irqdomain.c
@@ -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)


-- 
Regards/Gruss,
    Boris.

Sent from a fat crate under my desk. Formatting is fine.
--

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

* Re: [tip:irq/irqdomain] irqdomain: Introduce helper function irq_domain_add_hierarchy()
  2014-11-29 12:53     ` Borislav Petkov
@ 2014-11-29 14:29       ` Jiang Liu
  2014-11-29 14:56         ` Borislav Petkov
  2014-11-29 20:42       ` Thomas Gleixner
  1 sibling, 1 reply; 36+ messages in thread
From: Jiang Liu @ 2014-11-29 14:29 UTC (permalink / raw)
  To: Borislav Petkov
  Cc: linux-tip-commits, matthias.bgg, tony.luck, tglx, benh,
	linux-kernel, hpa, grant.likely, wangyijing, marc.zyngier,
	bhelgaas, yingjoe.chen, mingo

On 2014/11/29 20:53, Borislav Petkov wrote:
> On Sun, Nov 23, 2014 at 10:11:44AM -0800, tip-bot for Jiang Liu wrote:
>> Commit-ID:  afb7da83b9f476728623130703acb553d7c7c4d9
>> Gitweb:     http://git.kernel.org/tip/afb7da83b9f476728623130703acb553d7c7c4d9
>> Author:     Jiang Liu <jiang.liu@linux.intel.com>
>> AuthorDate: Sat, 15 Nov 2014 22:24:02 +0800
>> Committer:  Thomas Gleixner <tglx@linutronix.de>
>> 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.
>>
>> Signed-off-by: Jiang Liu <jiang.liu@linux.intel.com>
>> Cc: Tony Luck <tony.luck@intel.com>
>> Cc: linux-arm-kernel@lists.infradead.org
>> Cc: Bjorn Helgaas <bhelgaas@google.com>
>> Cc: Grant Likely <grant.likely@linaro.org>
>> Cc: Marc Zyngier <marc.zyngier@arm.com>
>> Cc: Yijing Wang <wangyijing@huawei.com>
>> Cc: Yingjoe Chen <yingjoe.chen@mediatek.com>
>> Cc: Borislav Petkov <bp@alien8.de>
>> Cc: Benjamin Herrenschmidt <benh@kernel.crashing.org>
>> Cc: Matthias Brugger <matthias.bgg@gmail.com>
>> Link: http://lkml.kernel.org/r/1416061447-9472-5-git-send-email-jiang.liu@linux.intel.com
>> Signed-off-by: Thomas Gleixner <tglx@linutronix.de>
>> ---
>>  include/linux/irqdomain.h |  4 ++++
>>  kernel/irq/irqdomain.c    | 36 ++++++++++++++++++++++++++++++++++++
>>  2 files changed, 40 insertions(+)
>>
>> diff --git a/include/linux/irqdomain.h b/include/linux/irqdomain.h
>> index dd2709b..676d730 100644
>> --- a/include/linux/irqdomain.h
>> +++ b/include/linux/irqdomain.h
>> @@ -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);
>> diff --git a/kernel/irq/irqdomain.c b/kernel/irq/irqdomain.c
>> index 9c88db7..7fac311 100644
>> --- a/kernel/irq/irqdomain.c
>> +++ b/kernel/irq/irqdomain.c
>> @@ -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;
>> +	}
> 
> 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:
> 
> ---
> diff --git a/kernel/irq/irqdomain.c b/kernel/irq/irqdomain.c
> index 7fac311057b8..c21a003b996a 100644
> --- a/kernel/irq/irqdomain.c
> +++ b/kernel/irq/irqdomain.c
> @@ -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

>  
>  	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)
> 
> 

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

* Re: [tip:irq/irqdomain] irqdomain: Introduce helper function irq_domain_add_hierarchy()
  2014-11-29 14:29       ` Jiang Liu
@ 2014-11-29 14:56         ` Borislav Petkov
  2014-11-29 15:21           ` Jiang Liu
  0 siblings, 1 reply; 36+ messages in thread
From: Borislav Petkov @ 2014-11-29 14:56 UTC (permalink / raw)
  To: Jiang Liu
  Cc: linux-tip-commits, matthias.bgg, tony.luck, tglx, benh,
	linux-kernel, hpa, grant.likely, wangyijing, marc.zyngier,
	bhelgaas, yingjoe.chen, mingo

On Sat, Nov 29, 2014 at 10:29:33PM +0800, Jiang Liu wrote:
>  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.

-- 
Regards/Gruss,
    Boris.

Sent from a fat crate under my desk. Formatting is fine.
--

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

* Re: [tip:irq/irqdomain] irqdomain: Introduce helper function irq_domain_add_hierarchy()
  2014-11-29 14:56         ` Borislav Petkov
@ 2014-11-29 15:21           ` Jiang Liu
  2014-11-29 15:37             ` Borislav Petkov
  0 siblings, 1 reply; 36+ messages in thread
From: Jiang Liu @ 2014-11-29 15:21 UTC (permalink / raw)
  To: Borislav Petkov
  Cc: linux-tip-commits, matthias.bgg, tony.luck, tglx, benh,
	linux-kernel, hpa, grant.likely, wangyijing, marc.zyngier,
	bhelgaas, yingjoe.chen, mingo

On 2014/11/29 22:56, Borislav Petkov wrote:
> On Sat, Nov 29, 2014 at 10:29:33PM +0800, Jiang Liu wrote:
>>  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.
Thanks for the info about GFP_ATOMIC, originally I have an impression
that we should use GFP_ATOMIC when interrupt is disabled:(

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

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

* Re: [tip:irq/irqdomain] irqdomain: Introduce helper function irq_domain_add_hierarchy()
  2014-11-29 15:21           ` Jiang Liu
@ 2014-11-29 15:37             ` Borislav Petkov
  0 siblings, 0 replies; 36+ messages in thread
From: Borislav Petkov @ 2014-11-29 15:37 UTC (permalink / raw)
  To: Jiang Liu
  Cc: linux-tip-commits, matthias.bgg, tony.luck, tglx, benh,
	linux-kernel, hpa, grant.likely, wangyijing, marc.zyngier,
	bhelgaas, yingjoe.chen, mingo

On Sat, Nov 29, 2014 at 11:21:11PM +0800, Jiang Liu wrote:
> 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...

-- 
Regards/Gruss,
    Boris.

Sent from a fat crate under my desk. Formatting is fine.
--

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

* Re: [tip:irq/irqdomain] irqdomain: Introduce helper function irq_domain_add_hierarchy()
  2014-11-29 12:53     ` Borislav Petkov
  2014-11-29 14:29       ` Jiang Liu
@ 2014-11-29 20:42       ` Thomas Gleixner
  2014-11-30 12:37           ` Borislav Petkov
  2014-12-01  2:20         ` [tip:irq/irqdomain] irqdomain: Introduce helper function irq_domain_add_hierarchy() Jiang Liu
  1 sibling, 2 replies; 36+ messages in thread
From: Thomas Gleixner @ 2014-11-29 20:42 UTC (permalink / raw)
  To: Borislav Petkov
  Cc: Jiang Liu, linux-tip-commits, matthias.bgg, tony.luck, benh,
	linux-kernel, hpa, grant.likely, wangyijing, marc.zyngier,
	bhelgaas, yingjoe.chen, mingo

On Sat, 29 Nov 2014, Borislav Petkov wrote:
> 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:
> 
> ---
> diff --git a/kernel/irq/irqdomain.c b/kernel/irq/irqdomain.c
> index 7fac311057b8..c21a003b996a 100644
> --- a/kernel/irq/irqdomain.c
> +++ b/kernel/irq/irqdomain.c
> @@ -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

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

* [PATCH] irqdomain: Correct early allocation of irq domains with IRQs off
  2014-11-29 20:42       ` Thomas Gleixner
@ 2014-11-30 12:37           ` Borislav Petkov
  2014-12-01  2:20         ` [tip:irq/irqdomain] irqdomain: Introduce helper function irq_domain_add_hierarchy() Jiang Liu
  1 sibling, 0 replies; 36+ messages in thread
From: Borislav Petkov @ 2014-11-30 12:37 UTC (permalink / raw)
  To: LKML
  Cc: Thomas Gleixner, Jiang Liu, Tony Luck, linux-arm-kernel,
	Bjorn Helgaas, Grant Likely, Marc Zyngier, Yijing Wang,
	Yingjoe Chen, Benjamin Herrenschmidt, Matthias Brugger,
	Joerg Roedel

From: Borislav Petkov <bp@suse.de>

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 <tglx@linutronix.de>
Cc: Jiang Liu <jiang.liu@linux.intel.com>
Cc: Tony Luck <tony.luck@intel.com>
Cc: linux-arm-kernel@lists.infradead.org
Cc: Bjorn Helgaas <bhelgaas@google.com>
Cc: Grant Likely <grant.likely@linaro.org>
Cc: Marc Zyngier <marc.zyngier@arm.com>
Cc: Yijing Wang <wangyijing@huawei.com>
Cc: Yingjoe Chen <yingjoe.chen@mediatek.com>
Cc: Benjamin Herrenschmidt <benh@kernel.crashing.org>
Cc: Matthias Brugger <matthias.bgg@gmail.com>
Cc: Joerg Roedel <joro@8bytes.org>
Link: Link: http://lkml.kernel.org/r/20141129125319.GA6491@pd.tnic
Signed-off-by: Borislav Petkov <bp@suse.de>
---
 kernel/irq/irqdomain.c | 21 +++++++++++++++++++--
 1 file changed, 19 insertions(+), 2 deletions(-)

diff --git a/kernel/irq/irqdomain.c b/kernel/irq/irqdomain.c
index 7fac311057b8..3395d8923f96 100644
--- a/kernel/irq/irqdomain.c
+++ b/kernel/irq/irqdomain.c
@@ -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);
-- 
2.0.0


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

* [PATCH] irqdomain: Correct early allocation of irq domains with IRQs off
@ 2014-11-30 12:37           ` Borislav Petkov
  0 siblings, 0 replies; 36+ messages in thread
From: Borislav Petkov @ 2014-11-30 12:37 UTC (permalink / raw)
  To: linux-arm-kernel

From: Borislav Petkov <bp@suse.de>

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 <tglx@linutronix.de>
Cc: Jiang Liu <jiang.liu@linux.intel.com>
Cc: Tony Luck <tony.luck@intel.com>
Cc: linux-arm-kernel at lists.infradead.org
Cc: Bjorn Helgaas <bhelgaas@google.com>
Cc: Grant Likely <grant.likely@linaro.org>
Cc: Marc Zyngier <marc.zyngier@arm.com>
Cc: Yijing Wang <wangyijing@huawei.com>
Cc: Yingjoe Chen <yingjoe.chen@mediatek.com>
Cc: Benjamin Herrenschmidt <benh@kernel.crashing.org>
Cc: Matthias Brugger <matthias.bgg@gmail.com>
Cc: Joerg Roedel <joro@8bytes.org>
Link: Link: http://lkml.kernel.org/r/20141129125319.GA6491 at pd.tnic
Signed-off-by: Borislav Petkov <bp@suse.de>
---
 kernel/irq/irqdomain.c | 21 +++++++++++++++++++--
 1 file changed, 19 insertions(+), 2 deletions(-)

diff --git a/kernel/irq/irqdomain.c b/kernel/irq/irqdomain.c
index 7fac311057b8..3395d8923f96 100644
--- a/kernel/irq/irqdomain.c
+++ b/kernel/irq/irqdomain.c
@@ -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);
-- 
2.0.0

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

* Re: [tip:irq/irqdomain] irqdomain: Introduce helper function irq_domain_add_hierarchy()
  2014-11-29 20:42       ` Thomas Gleixner
  2014-11-30 12:37           ` Borislav Petkov
@ 2014-12-01  2:20         ` Jiang Liu
  1 sibling, 0 replies; 36+ messages in thread
From: Jiang Liu @ 2014-12-01  2:20 UTC (permalink / raw)
  To: Thomas Gleixner, Borislav Petkov
  Cc: linux-tip-commits, matthias.bgg, tony.luck, benh, linux-kernel,
	hpa, grant.likely, wangyijing, marc.zyngier, bhelgaas,
	yingjoe.chen, mingo



On 2014/11/30 4:42, Thomas Gleixner wrote:
> On Sat, 29 Nov 2014, Borislav Petkov wrote:
>> 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:
>>
>> ---
>> diff --git a/kernel/irq/irqdomain.c b/kernel/irq/irqdomain.c
>> index 7fac311057b8..c21a003b996a 100644
>> --- a/kernel/irq/irqdomain.c
>> +++ b/kernel/irq/irqdomain.c
>> @@ -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.
Hi Thomas,
	I will have a look at this after 3.19 merge window:)

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

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

* [tip:x86/apic] irqdomain: Correct early allocation of irq domains with IRQs off
  2014-11-30 12:37           ` Borislav Petkov
  (?)
@ 2014-12-01  9:45           ` tip-bot for Borislav Petkov
  -1 siblings, 0 replies; 36+ messages in thread
From: tip-bot for Borislav Petkov @ 2014-12-01  9:45 UTC (permalink / raw)
  To: linux-tip-commits
  Cc: matthias.bgg, hpa, benh, grant.likely, bp, mingo, wangyijing,
	tony.luck, bhelgaas, marc.zyngier, joro, jiang.liu, linux-kernel,
	yingjoe.chen, tglx

Commit-ID:  eda7516e1d428caf3cfc88e4292f2a1357a2e569
Gitweb:     http://git.kernel.org/tip/eda7516e1d428caf3cfc88e4292f2a1357a2e569
Author:     Borislav Petkov <bp@suse.de>
AuthorDate: Sun, 30 Nov 2014 13:37:43 +0100
Committer:  Thomas Gleixner <tglx@linutronix.de>
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
...

Signed-off-by: Borislav Petkov <bp@suse.de>
Cc: Jiang Liu <jiang.liu@linux.intel.com>
Cc: Tony Luck <tony.luck@intel.com>
Cc: linux-arm-kernel@lists.infradead.org
Cc: Bjorn Helgaas <bhelgaas@google.com>
Cc: Grant Likely <grant.likely@linaro.org>
Cc: Marc Zyngier <marc.zyngier@arm.com>
Cc: Yijing Wang <wangyijing@huawei.com>
Cc: Yingjoe Chen <yingjoe.chen@mediatek.com>
Cc: Benjamin Herrenschmidt <benh@kernel.crashing.org>
Cc: Matthias Brugger <matthias.bgg@gmail.com>
Cc: Joerg Roedel <joro@8bytes.org>
Link: http://lkml.kernel.org/r/20141129125319.GA6491@pd.tnic
Link: http://lkml.kernel.org/r/1417351063-19039-1-git-send-email-bp@alien8.de
Signed-off-by: Thomas Gleixner <tglx@linutronix.de>
---
 kernel/irq/irqdomain.c | 21 +++++++++++++++++++--
 1 file changed, 19 insertions(+), 2 deletions(-)

diff --git a/kernel/irq/irqdomain.c b/kernel/irq/irqdomain.c
index 7fac311..3395d89 100644
--- a/kernel/irq/irqdomain.c
+++ b/kernel/irq/irqdomain.c
@@ -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);

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

end of thread, other threads:[~2014-12-01  9:47 UTC | newest]

Thread overview: 36+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2014-11-15 14:23 [Patch V2 0/9] Refine generic/PCI MSI irqodmian interfaces Jiang Liu
2014-11-15 14:23 ` Jiang Liu
2014-11-15 14:23 ` [Patch V2 1/9] PCI, MSI: Fix errors caused by commit e5f1a59c4e12 Jiang Liu
2014-11-15 14:23   ` Jiang Liu
2014-11-15 14:24 ` [Patch V2 2/9] irqdomain: Use consistent prototype for irq_domain_free_irqs_* Jiang Liu
2014-11-15 14:24   ` Jiang Liu
2014-11-15 14:24 ` [Patch V2 3/9] irqdomain: Implement a method to automatically call parent domain's alloc/free Jiang Liu
2014-11-15 14:24   ` Jiang Liu
2014-11-23 18:11   ` [tip:irq/irqdomain] irqdomain: Implement a method to automatically call parent domains alloc/free tip-bot for Jiang Liu
2014-11-15 14:24 ` [Patch V2 4/9] irqdomain: Introduce helper function irq_domain_add_hierarchy() Jiang Liu
2014-11-15 14:24   ` Jiang Liu
2014-11-23 18:11   ` [tip:irq/irqdomain] " tip-bot for Jiang Liu
2014-11-29 12:53     ` Borislav Petkov
2014-11-29 14:29       ` Jiang Liu
2014-11-29 14:56         ` Borislav Petkov
2014-11-29 15:21           ` Jiang Liu
2014-11-29 15:37             ` Borislav Petkov
2014-11-29 20:42       ` Thomas Gleixner
2014-11-30 12:37         ` [PATCH] irqdomain: Correct early allocation of irq domains with IRQs off Borislav Petkov
2014-11-30 12:37           ` Borislav Petkov
2014-12-01  9:45           ` [tip:x86/apic] " tip-bot for Borislav Petkov
2014-12-01  2:20         ` [tip:irq/irqdomain] irqdomain: Introduce helper function irq_domain_add_hierarchy() Jiang Liu
2014-11-15 14:24 ` [Patch V2 5/9] PCI, MSI: Introduce helpers to hide struct msi_desc implementation details Jiang Liu
2014-11-15 14:24   ` Jiang Liu
2014-11-23 18:08   ` [tip:irq/irqdomain] PCI/MSI: " tip-bot for Jiang Liu
2014-11-15 14:24 ` [Patch V2 6/9] genirq: Introduce msi_domain_{alloc|free}_irqs() Jiang Liu
2014-11-15 14:24   ` Jiang Liu
2014-11-23 18:13   ` [tip:irq/irqdomain] genirq: Introduce msi_domain_alloc/free_irqs( ) tip-bot for Jiang Liu
2014-11-15 14:24 ` [Patch V2 7/9] genirq: Provide default callbacks for msi_domain_ops Jiang Liu
2014-11-15 14:24   ` Jiang Liu
2014-11-23 18:13   ` [tip:irq/irqdomain] " tip-bot for Jiang Liu
2014-11-15 14:24 ` [Patch V2 8/9] PCI, MSI: Refine irqdomain interfaces to simplify its usage Jiang Liu
2014-11-15 14:24   ` Jiang Liu
2014-11-15 14:24 ` [Patch V2 9/9] PCI, MSI: Provide mechanism to alloc/free MSI/MSIX interrupt from irqdomain Jiang Liu
2014-11-15 14:24   ` Jiang Liu
2014-11-23 18:14   ` [tip:irq/irqdomain] PCI/MSI: Provide mechanism to alloc/free MSI/ MSIX " tip-bot for Jiang Liu

This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.