Skip to contentSkip to conversation

Refine generic/PCI MSI irqodmian interfaces

PATCH In review Jiang Liu 2 participants 14 messages Nov 17, 2014 – Nov 18, 2014

Subsystems (3)
lore
Share & tools
lore

Jiang Liu

author
#0 lore raw

v3 cover Refine generic/PCI MSI irqodmian interfaces

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

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

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

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

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

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

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

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

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

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

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

V2->V3:
1) Refine msi_domain_ops.set_desc() and msi_domain_ops.handle_error() interfaces.
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         |  183 ++++++++++++++++++++++++++++++++++++---------
 include/linux/irqdomain.h |   33 ++++----
 include/linux/msi.h       |   99 +++++++++++++++++++++---
 kernel/irq/irqdomain.c    |   93 +++++++++++++++++++++--
 kernel/irq/msi.c          |  175 +++++++++++++++++++++++++++++++++++++++++--
 5 files changed, 510 insertions(+), 73 deletions(-)
Signature · 1 line
1.7.10.4
author
#1 lore raw

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

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

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

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

In reply to Jiang Liu

From: Yingjoe Chen <[email protected]>

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

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

Change to use consistent prototype for all irq_domain_free_irqs*

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

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

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

In reply to Jiang Liu

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

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

90 lines not on this page · raw message

Signature · 1 line
1.7.10.4
author
#4 lore raw

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

In reply to Jiang Liu

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

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

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

In reply to Jiang Liu

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

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

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

In reply to Jiang Liu

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

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

75 lines not on this page · raw message

Signature · 1 line
1.7.10.4
#7 lore raw

Re: [Patch V3 6/9] genirq: Introduce msi_domain_{alloc|free}_irqs()

Quoted 7 lines · Jiang Liu
include/linux/msi.h +7 -0
+
+/*
+ * Default structure for MSI interrupt allocation.
+ * Arch may overwrite it by defining msi_alloc_info_t.
+ */
+struct msi_alloc_info {
+	struct msi_desc			*desc;

include/linux/msi.h struct irq_chip; · line 123

Quoted 10 lines
include/linux/msi.h +10 -0
+	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

Hmm. Do we really need that?

Thanks,

tglx

author
#8 lore raw

Re: [Patch V3 6/9] genirq: Introduce msi_domain_{alloc|free}_irqs()

Quoted 12 lines · Thomas Gleixner
Quoted 11 lines · Jiang Liu
include/linux/msi.h +11 -0
+
+/*
+ * 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;
Quoted 8 lines

include/linux/msi.h struct irq_chip; · line 123

Quoted 6 lines
include/linux/msi.h +6 -0
+	} scratchpad[NUM_MSI_ALLOC_SCRATCHPAD_REGS];
+};
+
+#ifndef msi_alloc_info_t
+typedef struct msi_alloc_info msi_alloc_info_t;
+#endif

Hmm. Do we really need that?

Hi Thomas,
Do you mean msi_alloc_info_t? It acts as a flag
to use common struct msi_alloc_info or architecture specific data structures. For most architecture, they will use the default struct msi_alloc_info. But for x86, it will use struct irq_alloc_info. Regards!
Gerry

author
#9 lore raw

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

In reply to Jiang Liu

Extend struct msi_domain_info and provide default callbacks for msi_domain_ops.

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

138 lines not on this page · raw message

Signature · 1 line
1.7.10.4
author
#10 lore raw

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

In reply to Jiang Liu

Refine irqdomain interfaces to simplify its usage.

---
 drivers/pci/msi.c   |  121 +++++++++++++++++++++++++++++++++++++++------------
 include/linux/msi.h |   13 +++---
 2 files changed, 100 insertions(+), 34 deletions(-)
drivers/pci/msi.c +92 -29
index 9c53b865cb1b..62f504887c1d 100644
@@ -1099,38 +1099,101 @@ 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;
+}
+
+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;
 
-	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);
+	return 0;
+}
+
+static int pci_msi_domain_handle_error(msi_alloc_info_t *arg,
+				       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 int 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);
 
 	return 0;
 }
+#else
+#define pci_msi_domain_set_desc		NULL
+#endif
+
+static struct msi_domain_ops pci_msi_domain_ops_default = {
+	.set_desc	= pci_msi_domain_set_desc,
+	.msi_check	= pci_msi_domain_check_cap,
+	.handle_error	= pci_msi_domain_handle_error,
+};
+
+static void pci_msi_domain_update_dom_ops(struct msi_domain_info *info)
+{
+	struct msi_domain_ops *ops = info->ops;
+
+	if (ops == NULL) {
+		info->ops = &pci_msi_domain_ops_default;
+	} else {
+		if (ops->set_desc == NULL)
+			ops->set_desc = pci_msi_domain_set_desc;
+		if (ops->msi_check == NULL)
+			ops->msi_check = pci_msi_domain_check_cap;
+		if (ops->handle_error == NULL)
+			ops->handle_error = pci_msi_domain_handle_error;
+	}
+}
+
+static void pci_msi_domain_update_chip_ops(struct msi_domain_info *info)
+{
+	struct irq_chip *chip = info->chip;
+
+	BUG_ON(!chip);
+	if (!chip->irq_write_msi_msg)
+		chip->irq_write_msi_msg = pci_msi_domain_write_msg;
+}
+
+struct irq_domain *pci_msi_create_irq_domain(struct device_node *node,
+					     struct msi_domain_info *info,
+					     struct irq_domain *parent)
+{
+	if (info->flags & MSI_FLAG_USE_DEF_DOM_OPS)
+		pci_msi_domain_update_dom_ops(info);
+	if (info->flags & MSI_FLAG_USE_DEF_CHIP_OPS)
+		pci_msi_domain_update_chip_ops(info);
+
+	return msi_create_irq_domain(node, info, parent);
+}
+
+int pci_msi_domain_alloc_irqs(struct irq_domain *domain, struct pci_dev *dev,
+			      int nvec, int type)
+{
+	return msi_domain_alloc_irqs(domain, &dev->dev, nvec);
+}
+
+void pci_msi_domain_free_irqs(struct irq_domain *domain, struct pci_dev *dev)
+{
+	msi_domain_free_irqs(domain, &dev->dev);
+}
 #endif /* CONFIG_PCI_MSI_IRQ_DOMAIN */
include/linux/msi.h +8 -5
index 3571b4715c75..eadd47c309a7 100644
@@ -125,9 +125,6 @@ typedef struct msi_alloc_info msi_alloc_info_t;
 
 struct msi_domain_ops {
 	/* Callbacks for msi_create_irq_domain() */
-	void		(*calc_hwirq)(struct msi_domain_info *info,
-				      msi_alloc_info_t *arg,
-				      struct msi_desc *desc);
 	irq_hw_number_t	(*get_hwirq)(struct msi_domain_info *info,
 				     msi_alloc_info_t *arg);
 	int		(*msi_init)(struct irq_domain *domain,
@@ -189,10 +186,16 @@ struct msi_domain_info *msi_get_domain_info(struct irq_domain *domain);
 
 #ifdef CONFIG_PCI_MSI_IRQ_DOMAIN
 void pci_msi_domain_write_msg(struct irq_data *irq_data, struct msi_msg *msg);
-int pci_msi_domain_alloc_irqs(struct irq_domain *domain, int type,
-			      struct pci_dev *dev, void *arg);
+struct irq_domain *pci_msi_create_irq_domain(struct device_node *node,
+					     struct msi_domain_info *info,
+					     struct irq_domain *parent);
+int pci_msi_domain_alloc_irqs(struct irq_domain *domain, struct pci_dev *dev,
+			      int nvec, int type);
+void pci_msi_domain_free_irqs(struct irq_domain *domain, struct pci_dev *dev);
 irq_hw_number_t pci_msi_domain_calc_hwirq(struct pci_dev *dev,
 					  struct msi_desc *desc);
+int pci_msi_domain_check_cap(struct irq_domain *domain,
+			     struct msi_domain_info *info, struct device *dev);
 #endif /* CONFIG_PCI_MSI_IRQ_DOMAIN */
 
 #endif /* LINUX_MSI_H */
Signature · 1 line
1.7.10.4
author
#11 lore raw

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

In reply to Jiang Liu

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

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

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

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

So to make progress on that matter I picked that up along with a few other patches which affect the core code (more stacked irq callbacks and Marcs workaround for the handler assignment issue).

I rebased the whole lot on Bjorns pci/msi branch as discussed in this
thread: https://lkml.org/lkml/2014/11/16/292

After that I spent quite some time to reorder and fold back patches so we have no obvious wreckage, but at the same time preserve at least some of the development process for this.

Finally I added docbook comments where missing (please provide them next time yourself).

Marc ran an initial smoketest on an early iteration which did not fall into bits and pieces right away.

The only major change is that I got rid of the ugly

#ifndef msi_alloc_info_t

trickery by moving the generic version into include/asm-generic/msi.h

So if you want to use the generic version, you need to create your arch/xxx/include/asm/msi.h file which includes asm-generic/msi.h.

For the ones who want to override it, create arch/xxx/include/asm/msi.h, include whatever it takes and provide the proper typedef.

I'll send that patch (7bde31a7969f) out tomorrow morning as I want to have Arnds blessing for that.

That all is available in

git://git.kernel.org/pub/scm/linux/kernel/git/tip/tip.git irq/irqdomain

Note, that the branch has been rebased and is still not considered to be immutable.

So while I sorted out the pci related process with Bjorn, here is the process for this and the depending arch/xxx bits and pieces:

1) If the current content of the irq/irqdomain branch in tip does not raise eyebrows and does not require any conceptual fixups within 24 hrs from now, then I'm going to declare it immutable, merge it into next and let the arch folks depend on it.

2) If after that any updates to the core irq/pci/msi code are required they need to go through that branch first unless they are agreed on to be arch/xxx specific and can go through a different tree which is the only one which requires that.

I really rely on your cooperation here, as I don't want to end up with an unexplainable mess when the stuff hits Linus tree and he starts rumaging for his diving harpune ....

Find below the commit list based on commit c167caf8d174: PCI/MSI:
Remove useless bus->msi assignment in Bjorns pci/msi branch.

Thanks,

tglx
---
2d294376193f: PCI/MSI: Allow an msi_chip to be associated to an irq domain 4888af47f51a: PCI/MSI: Provide mechanism to alloc/free MSI/MSIX interrupt from irqdomain
f15c6d33c218: PCI/MSI: Enhance core to support hierarchy irqdomain 1a91b4fb79a2: PCI/MSI: Move cached entry functions to irq core
c0b4a8942b6c: genirq: Provide default callbacks for msi_domain_ops 473e759918d2: genirq: Introduce msi_domain_alloc/free_irqs()
7bde31a7969f: asm-generic: Add msi.h
8c507b885ec3: genirq: Add generic msi irq domain support
f49d8e21319f: genirq: Introduce callback irq_chip.irq_write_msi_msg 0ac68de4f9c7: genirq: Work around __irq_set_handler vs stacked domains ordering issues 153a2674ebe9: irqdomain: Introduce helper function irq_domain_add_hierarchy() 5b15d50cfaa8: irqdomain: Implement a method to automatically call parent domains alloc/free
de81049f177d: genirq: Introduce helper irq_domain_set_info() to reduce duplicated code 5447cf75ebba: genirq: Split out flow handler typedefs into seperate header file 5d7a3e203c54: genirq: Add IRQ_SET_MASK_OK_DONE to support stacked irqchip
fb3577cf30b4: genirq: Introduce irq_chip.irq_compose_msi_msg() to support stacked irqchip 61454f3977d2: genirq: Add more helper functions to support stacked irq_chip 79d4a9783265: genirq: Introduce helper functions to support stacked irq_chip
df333db2e20a: irqdomain: Do irq_find_mapping and set_type for hierarchy irqdomain in case OF 3f4cac0a4ea8: irqdomain: Introduce new interfaces to support hierarchy irqdomains
f9e2d8740075: PCI/MSI: Introduce helpers to hide struct msi_desc implementation details 33190dab3a59: PCI/MSI: Rename write_msi_msg() to pci_write_msi_msg() 1da7e00b1f73: PCI/MSI: Rename __read_msi_msg() to __pci_read_msi_msg()
b9191cbef61f: PCI/MSI: Kill redundant call of irq_set_msi_desc() for MSI-X interrupts
dbcb4c926ece: PCI/MSI: Simplify PCI MSI code by initializing msi_desc.nvec_used earlier 6cf585318fde: PCI/MSI: Remove unnecessary braces around single statements

author
#13 lore raw
Quoted 11 lines · Thomas Gleixner
Quoted 2 lines · Jiang Liu

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

So to make progress on that matter I picked that up along with a few other patches which affect the core code (more stacked irq callbacks and Marcs workaround for the handler assignment issue).

I rebased the whole lot on Bjorns pci/msi branch as discussed in this
thread: https://lkml.org/lkml/2014/11/16/292

Quoted 6 lines

After that I spent quite some time to reorder and fold back patches so we have no obvious wreckage, but at the same time preserve at least some of the development process for this.

Finally I added docbook comments where missing (please provide them next time yourself).

Sure, need improvements on docs.

Quoted 8 lines

Marc ran an initial smoketest on an early iteration which did not fall into bits and pieces right away.

The only major change is that I got rid of the ugly

#ifndef msi_alloc_info_t

Quoted 8 lines

trickery by moving the generic version into include/asm-generic/msi.h

So if you want to use the generic version, you need to create your arch/xxx/include/asm/msi.h file which includes asm-generic/msi.h.

For the ones who want to override it, create arch/xxx/include/asm/msi.h, include whatever it takes and provide the proper typedef.

Two issues are solved by adding new header files, a good technique to improve code quality:)

Quoted 4 lines

I'll send that patch (7bde31a7969f) out tomorrow morning as I want to have Arnds blessing for that.

<snip>

Quoted 3 lines

I really rely on your cooperation here, as I don't want to end up with an unexplainable mess when the stuff hits Linus tree and he starts rumaging for his diving harpune ....

I have rebased x86 changes onto tip/irq/irqdomain, it works perfectly:)

Regards!
Gerry