Refine generic/PCI MSI irqodmian interfaces
Subsystems (3)
v2 cover Refine generic/PCI MSI irqodmian interfaces
This patch set is based on tip/irq/irqdomain and tries to refine interfaces to support irqdomain for generic MSI and PCI MSI.
Patch 1 is just minor fixes for tip/irq/irqdomain.
Patch 2-4 introduce mechanisms to help reduce irqdomain users' code size. When converting XEN to use hierarchy irqdomain, I found the code size increases about 100 lines. So we still need helpers to reduce code size.
Patch 5 introduces some helpers to hide struct msi_desc implementation details, so later we could move msi_list from struct pci_dev into struct device to enable generic MSI support.
Patch 6 introduces msi_domain_{alloc|free}_irqs() which generalize pci_msi_domain_alloc_irqs() to support generic MSI.
Patch 7 introduces default data structures and callback implementations to support msi_domain_alloc_irqs(), so reduce burden on generic MSI users.
Patch 8 converts PCI MSI to use generic MSI interfaces, and also implement default callbacks for PCI MSI.
Patch 9 introduces a mechanism to replace arch_setup_msi_irq()/ arch_setup_msi_irqs()/arch_teardown_msi_irq()/arch_teardown_msi_irqs().
With this patch set applied, the generic MSI and PCI MSI interfaces are much easier to use. For extreme case, you only need to define a "struct msi_domain_info" and don't need to implement any callbacks, just using the default callbacks is OK:)
This patch set is also a preparation for:
1) Kill all weak functions in drivers/pci/msi.c
2) Implement support for non-PCI-compliant MSI device
It has been tested on x86 platforms, comments are welcomed!
V1->V2:
1) Fix bugs reported by Marc
2) Rename MSI_FLAG_USE_DEF_OPS as MSI_FLAG_USE_DEF_DOM_OPS
3) Add support of MSI_FLAG_USE_DEF_CHIP_OPS
4) Include linxu/irqhandler.h to use irq_flow_handler_t instead of "void *"
5) Change iterfaces to pass in "nvect" to msi_domain_ops.msi_prepare()
6) Add new interfaces/flags to help reduce irqdomain users' code size
Jiang Liu (8):
PCI, MSI: Fix errors caused by commit e5f1a59c4e12
irqdomain: Implement a method to automatically call parent domain's
alloc/free
irqdomain: Introduce helper function irq_domain_add_hierarchy() PCI, MSI: Introduce helpers to hide struct msi_desc implementation
details
genirq: Introduce msi_domain_{alloc|free}_irqs()
genirq: Provide default callbacks for msi_domain_ops PCI, MSI: Refine irqdomain interfaces to simplify its usage PCI, MSI: Provide mechanism to alloc/free MSI/MSIX interrupt from
irqdomain
Yingjoe Chen (1):
irqdomain: Use consistent prototype for irq_domain_free_irqs_*
drivers/pci/msi.c | 181 ++++++++++++++++++++++++++++++++++++--------- include/linux/irqdomain.h | 33 ++++----- include/linux/msi.h | 99 ++++++++++++++++++++++--- kernel/irq/irqdomain.c | 93 +++++++++++++++++++++-- kernel/irq/msi.c | 169 ++++++++++++++++++++++++++++++++++++++++-- 5 files changed, 502 insertions(+), 73 deletions(-)
Signature · 1 line
1.7.10.4
PATCH v2 1/9 PCI, MSI: Fix errors caused by commit e5f1a59c4e12
Better to fold into commit e5f1a59c4e12 ("PCI/MSI: Rename write_msi_msg() to pci_write_msi_msg()").
--- drivers/pci/msi.c | 2 +- include/linux/msi.h | 2 -- 2 files changed, 1 insertion(+), 3 deletions(-)
drivers/pci/msi.c +1 -1
@@ -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
@@ -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
PATCH v2 2/9 irqdomain: Use consistent prototype for irq_domain_free_irqs_*
In reply to Jiang LiuFrom: 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
@@ -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
@@ -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
PATCH v2 3/9 irqdomain: Implement a method to automatically call parent domain's alloc/free
In reply to Jiang LiuAdd 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
@@ -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
tip-bot for Jiang Liu
merge bot[tip:irq/irqdomain] irqdomain: Implement a method to automatically call parent domains alloc/free
irqdomain: Implement a method to automatically call parent domains alloc/free
Commit-ID: 36d727310cb9f85efb5ac089ffb1797e7c3538e1 Gitweb: http://git.kernel.org/tip/36d727310cb9f85efb5ac089ffb1797e7c3538e1 Author: Jiang Liu <[email protected]> AuthorDate: Sat, 15 Nov 2014 22:24:01 +0800 Committer: Thomas Gleixner <[email protected]> CommitDate: Sun, 23 Nov 2014 13:01:46 +0100
irqdomain: Implement a method to automatically call parent domains alloc/free
Add a flags to irq_domain.flags to control whether the irqdomain core should automatically call parent irqdomain's alloc/free callbacks. It help to reduce hierarchy irqdomains users' code size.
--- include/linux/irqdomain.h | 24 ++++++-------- kernel/irq/irqdomain.c | 82 +++++++++++++++++++++++++++++++++++++++++++++-- 2 files changed, 89 insertions(+), 17 deletions(-)
include/linux/irqdomain.h +9 -15
@@ -134,6 +134,9 @@ enum {/* Irq domain is hierarchical */ IRQ_DOMAIN_FLAG_HIERARCHY = (1 <<0 ), +/* Core calls alloc/free recursive through the domain hierarchy. */ + IRQ_DOMAIN_FLAG_AUTO_RECURSIVE = (1 << 1), +/* * Flags starting from IRQ_DOMAIN_FLAG_NONCORE are reserved * for implementation specific purposes and ignored by the
@@ -285,22 +288,13 @@ extern void irq_domain_free_irqs_common(struct irq_domain *domain,extern void irq_domain_free_irqs_top (struct irq_domain *domain,unsigned int virq,unsigned int nr_irqs); -static inline int irq_domain_alloc_irqs_parent (struct irq_domain *domain, -unsigned int irq_base, -unsigned int nr_irqs,void *arg) -{ -if (domain->parent && domain->parent->ops->alloc) -return domain->parent->ops->alloc (domain->parent, irq_base, - nr_irqs, arg); -return -ENOSYS ; -} +extern int irq_domain_alloc_irqs_parent (struct irq_domain *domain, +unsigned int irq_base, +unsigned int nr_irqs,void *arg); -static inline void irq_domain_free_irqs_parent (struct irq_domain *domain, -unsigned int irq_base,unsigned int nr_irqs) -{ -if (domain->parent && domain->parent->ops->free) - domain->parent->ops->free (domain->parent, irq_base, nr_irqs); -} +extern void irq_domain_free_irqs_parent (struct irq_domain *domain, +unsigned int irq_base, +unsigned int nr_irqs);static inline bool irq_domain_is_hierarchy (struct irq_domain *domain) {
kernel/irq/irqdomain.c +80 -2
106 lines not on this page · raw message
PATCH v2 4/9 irqdomain: Introduce helper function irq_domain_add_hierarchy()
In reply to Jiang LiuIntroduce 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
@@ -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
@@ -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
tip-bot for Jiang Liu
merge bot[tip:irq/irqdomain] irqdomain: Introduce helper function irq_domain_add_hierarchy()
irqdomain: Introduce helper function irq_domain_add_hierarchy()
Commit-ID: afb7da83b9f476728623130703acb553d7c7c4d9 Gitweb: http://git.kernel.org/tip/afb7da83b9f476728623130703acb553d7c7c4d9 Author: Jiang Liu <[email protected]> AuthorDate: Sat, 15 Nov 2014 22:24:02 +0800 Committer: Thomas Gleixner <[email protected]> CommitDate: Sun, 23 Nov 2014 13:01:46 +0100
irqdomain: Introduce helper function irq_domain_add_hierarchy()
Introduce helper function irq_domain_add_hierarchy(), which creates a linear irqdomain if parameter 'size' is not zero, otherwise creates a tree irqdomain.
--- include/linux/irqdomain.h | 4 ++++ kernel/irq/irqdomain.c | 36 ++++++++++++++++++++++++++++++++++++ 2 files changed, 40 insertions(+)
include/linux/irqdomain.h +4 -0
@@ -259,6 +259,10 @@ int irq_domain_xlate_onetwocell(struct irq_domain *d, struct device_node *ctrlr,extern struct irq_data *irq_domain_get_irq_data (struct irq_domain *domain,unsigned int virq);#ifdef CONFIG_IRQ_DOMAIN_HIERARCHY +extern struct irq_domain *irq_domain_add_hierarchy (struct irq_domain *parent, +unsigned int flags,unsigned int size, +struct device_node *node, +const struct irq_domain_ops *ops,void *host_data);extern int __irq_domain_alloc_irqs (struct irq_domain *domain,int irq_base,unsigned int nr_irqs,int node,void *arg,bool realloc);
kernel/irq/irqdomain.c +36 -0
@@ -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;
Re: [tip:irq/irqdomain] irqdomain: Introduce helper function irq_domain_add_hierarchy()
Quoted 76 lines · tip-bot for Jiang Liu
Commit-ID: afb7da83b9f476728623130703acb553d7c7c4d9 Gitweb: http://git.kernel.org/tip/afb7da83b9f476728623130703acb553d7c7c4d9 Author: Jiang Liu <[email protected]> AuthorDate: Sat, 15 Nov 2014 22:24:02 +0800 Committer: Thomas Gleixner <[email protected]> CommitDate: Sun, 23 Nov 2014 13:01:46 +0100irqdomain: 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 <[email protected]>Cc Tony Luck <[email protected]>Cc Bjorn Helgaas <[email protected]>Cc Grant Likely <[email protected]>Cc Marc Zyngier <[email protected]>Cc Yijing Wang <[email protected]>Cc Yingjoe Chen <[email protected]>Cc Borislav Petkov <[email protected]>Cc Benjamin Herrenschmidt <[email protected]>Cc Matthias Brugger <[email protected]>Signed-off-by Thomas Gleixner <[email protected]>--- include/linux/irqdomain.h | 4 ++++ kernel/irq/irqdomain.c | 36 ++++++++++++++++++++++++++++++++++++ 2 files changed, 40 insertions(+)include/linux/irqdomain.h +4
-0@@ -259,6 +259,10 @@ int irq_domain_xlate_onetwocell(struct irq_domain *d, struct device_node *ctrlr,extern struct irq_data *irq_domain_get_irq_data (struct irq_domain *domain,unsigned int virq);#ifdef CONFIG_IRQ_DOMAIN_HIERARCHY +extern struct irq_domain *irq_domain_add_hierarchy (struct irq_domain *parent, +unsigned int flags,unsigned int size, +struct device_node *node, +const struct irq_domain_ops *ops,void *host_data);extern int __irq_domain_alloc_irqs (struct irq_domain *domain,int irq_base,unsigned int nr_irqs,int node,void *arg,bool realloc);kernel/irq/irqdomain.c +22
-0@@ -742,6 +742,42 @@ static int irq_domain_alloc_descs(int virq, unsigned int cnt, }#ifdef CONFIG_IRQ_DOMAIN_HIERARCHY +/** +* irq_domain_add_hierarchy - Add a irqdomain into the hierarchy +* @parent: Parent irq domain to associate with the new domain +* @flags: Irq domain flags associated to the domain +* @size: Size of the domain. See below +* @node: Optional device-tree node of the interrupt controller +* @ops: Pointer to the interrupt domain callbacks +* @host_data: Controller private data pointer +* +* If @size is 0 a tree domain is created, otherwise a linear domain. +* +* If successful the parent is associated to the new domain and the +* domain flags are set. +* Returns pointer to IRQ domain, or NULL on failure. +*/ +struct irq_domain *irq_domain_add_hierarchy (struct irq_domain *parent, +unsigned int flags, +unsigned int size, +struct device_node *node, +const struct irq_domain_ops *ops, +void *host_data) +{
kernel/irq/irqdomain.c static int irq_domain_alloc_descs(int virq, unsigned int cnt, · line 776
Quoted 10 lines
kernel/irq/irqdomain.c +10
-0+struct irq_domain *domain; + +if (size) + domain =irq_domain_add_linear (node, size, ops, host_data); +else + domain =irq_domain_add_tree (node, ops, host_data); +if (domain) { + domain->parent = parent; + domain->flags |= flags; + }
Ok, I'm going to reply to this patch because it is on the splat path but fixing this would need a bit more reorganizing.
So I'm seeing the lockdep splat below really early on an IVB laptop.
Basically we're not supposed to do __GFP_FS allocations with IRQs off:
2737 /* We're only interested __GFP_FS allocations for now */
2738 if (!(gfp_mask & __GFP_FS))
2739 return;
2740
2741 /*
2742 * Oi! Can't be having __GFP_FS allocations with IRQs disabled.
2743 */
2744 if (DEBUG_LOCKS_WARN_ON(irqs_disabled_flags(flags))) <--- HERE!
2745 return;
2746
2747 mark_held_locks(curr, RECLAIM_FS);
2748 }
Now, AFAICT, enable_IR_x2apic() disables interrupts and the whole init is done with IRQs off but down that path intel_setup_irq_remapping() calls irq_domain_add_hierarchy() and it does by default GFP_KERNEL allocations.
The obvious fix is this and the machine boots fine with it. I'm not sure it is kosher though so I rather run it by people first:
---
kernel/irq/irqdomain.c +6 -2
@@ -46,14 +46,18 @@ struct irq_domain *__irq_domain_add(struct device_node *of_node, int size, void *host_data) {struct irq_domain *domain; +gfp_t gfp_flags =GFP_KERNEL ; + +if (irqs_disabled ()) + gfp_flags =GFP_NOFS ; domain =kzalloc_node (sizeof (*domain) + (sizeof (unsigned int ) * size), -GFP_KERNEL ,of_node_to_nid (of_node)); + gfp_flags,of_node_to_nid (of_node));if (WARN_ON (!domain))return NULL ;/* Fill structure */ -INIT_RADIX_TREE (&domain->revmap_tree,GFP_KERNEL ); +INIT_RADIX_TREE (&domain->revmap_tree, gfp_flags); domain->ops = ops; domain->host_data = host_data; domain->of_node =of_node_get (of_node);
--
[ 0.085221] dmar: Host address width 36 [ 0.085298] dmar: DRHD base: 0x000000fed90000 flags: 0x0 [ 0.085414] dmar: IOMMU 0: reg_base_addr fed90000 ver 1:0 cap c0000020e60262 ecap f0101a [ 0.085513] dmar: DRHD base: 0x000000fed91000 flags: 0x1 [ 0.085598] dmar: IOMMU 1: reg_base_addr fed91000 ver 1:0 cap c9008020660262 ecap f0105a [ 0.085693] dmar: RMRR base: 0x000000da2ba000 end: 0x000000da2d0fff [ 0.085789] dmar: RMRR base: 0x000000db800000 end: 0x000000df9fffff [ 0.085955] IOAPIC id 2 under DRHD base 0xfed91000 IOMMU 1 [ 0.086034] HPET id 0 under DRHD base 0xfed91000 [ 0.086109] Queued invalidation will be enabled to support x2apic and Intr-remapping. [ 0.086351] ------------[ cut here ]------------ [ 0.086438] WARNING: CPU: 0 PID: 1 at kernel/locking/lockdep.c:2744 lockdep_trace_alloc+0xd4/0xe0() [ 0.086537] DEBUG_LOCKS_WARN_ON(irqs_disabled_flags(flags)) [ 0.086598] Modules linked in: [ 0.086783] CPU: 0 PID: 1 Comm: swapper/0 Not tainted 3.18.0-rc6+ #1 [ 0.086863] Hardware name: LENOVO 2320CTO/2320CTO, BIOS G2ET86WW (2.06 ) 11/13/2012 [ 0.086959] 0000000000000009 ffff880213d07b58 ffffffff816502d7 0000000000000000 [ 0.087240] ffff880213d07ba8 ffff880213d07b98 ffffffff81059790 0000000000000001 [ 0.087521] 0000000000000092 0000000000000000 00000000000080d0 0000000000000000 [ 0.087803] Call Trace: [ 0.087878] [<ffffffff816502d7>] dump_stack+0x4f/0x7b [ 0.087960] [<ffffffff81059790>] warn_slowpath_common+0x80/0xa0 [ 0.088041] [<ffffffff810597f6>] warn_slowpath_fmt+0x46/0x50 [ 0.088124] [<ffffffff810a5514>] lockdep_trace_alloc+0xd4/0xe0 [ 0.088208] [<ffffffff8114877f>] __alloc_pages_nodemask+0x7f/0x970 [ 0.088291] [<ffffffff81148158>] ? get_page_from_freelist+0x5b8/0x980 [ 0.088375] [<ffffffff8109f72f>] ? trace_hardirqs_off_caller+0x1f/0xc0 [ 0.088457] [<ffffffff811490af>] alloc_kmem_pages_node+0x3f/0x60 [ 0.088542] [<ffffffff8119280e>] kmalloc_large_node+0x2e/0x70 [ 0.088622] [<ffffffff811989a5>] __kmalloc_node+0x215/0x2b0 [ 0.088707] [<ffffffff81539c10>] ? __dmar_enable_qi+0x40/0x130 [ 0.088789] [<ffffffff810bafe1>] __irq_domain_add+0x41/0x110 [ 0.088871] [<ffffffff810bb553>] irq_domain_add_hierarchy+0x23/0x50 [ 0.088952] [<ffffffff81651f33>] intel_setup_irq_remapping.part.4+0xdc/0x169 [ 0.089039] [<ffffffff81d61123>] intel_enable_irq_remapping+0x240/0x2f9 [ 0.089123] [<ffffffff81d61315>] irq_remapping_enable+0x20/0x36 [ 0.089208] [<ffffffff81d2cbaf>] enable_IR+0x3c/0x3e [ 0.089288] [<ffffffff81d2cd92>] enable_IR_x2apic+0x82/0x120 [ 0.089369] [<ffffffff81d2e614>] default_setup_apic_routing+0x12/0x6b [ 0.089453] [<ffffffff81d2aadc>] native_smp_prepare_cpus+0x29b/0x39f [ 0.089538] [<ffffffff81d1df5d>] kernel_init_freeable+0x5f/0x1a2 [ 0.089622] [<ffffffff8165933f>] ? ret_from_fork+0xf/0xb0 [ 0.089705] [<ffffffff8164a640>] ? rest_init+0xd0/0xd0 [ 0.089787] [<ffffffff8164a64e>] kernel_init+0xe/0xf0 [ 0.089867] [<ffffffff816593ac>] ret_from_fork+0x7c/0xb0 [ 0.089948] [<ffffffff8164a640>] ? rest_init+0xd0/0xd0 [ 0.090032] ---[ end trace fac50e785fc22942 ]--- [ 0.090387] Enabled IRQ remapping in x2apic mode [ 0.090466] Enabling x2apic [ 0.090537] Enabled x2apic [ 0.090630] Switched APIC routing to cluster x2apic. [ 0.091287] ..TIMER: vector=0x30 apic1=0 pin1=2 apic2=-1 pin2=-1 [ 0.101375] smpboot: CPU0: Intel(R) Core(TM) i7-3520M CPU @ 2.90GHz (fam: 06, model: 3a, stepping: 09)
Signature · 5 lines
Regards/Gruss,
Boris.
Sent from a fat crate under my desk. Formatting is fine.
--Re: [tip:irq/irqdomain] irqdomain: Introduce helper function irq_domain_add_hierarchy()
Quoted 122 lines · Borislav Petkov
Quoted 76 lines · tip-bot for Jiang Liu
Commit-ID: afb7da83b9f476728623130703acb553d7c7c4d9 Gitweb: http://git.kernel.org/tip/afb7da83b9f476728623130703acb553d7c7c4d9 Author: Jiang Liu <[email protected]> AuthorDate: Sat, 15 Nov 2014 22:24:02 +0800 Committer: Thomas Gleixner <[email protected]> CommitDate: Sun, 23 Nov 2014 13:01:46 +0100irqdomain: 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 <[email protected]>Cc Tony Luck <[email protected]>Cc Bjorn Helgaas <[email protected]>Cc Grant Likely <[email protected]>Cc Marc Zyngier <[email protected]>Cc Yijing Wang <[email protected]>Cc Yingjoe Chen <[email protected]>Cc Borislav Petkov <[email protected]>Cc Benjamin Herrenschmidt <[email protected]>Cc Matthias Brugger <[email protected]>Signed-off-by Thomas Gleixner <[email protected]>--- include/linux/irqdomain.h | 4 ++++ kernel/irq/irqdomain.c | 36 ++++++++++++++++++++++++++++++++++++ 2 files changed, 40 insertions(+)include/linux/irqdomain.h +4
-0@@ -259,6 +259,10 @@ int irq_domain_xlate_onetwocell(struct irq_domain *d, struct device_node *ctrlr,extern struct irq_data *irq_domain_get_irq_data (struct irq_domain *domain,unsigned int virq);#ifdef CONFIG_IRQ_DOMAIN_HIERARCHY +extern struct irq_domain *irq_domain_add_hierarchy (struct irq_domain *parent, +unsigned int flags,unsigned int size, +struct device_node *node, +const struct irq_domain_ops *ops,void *host_data);extern int __irq_domain_alloc_irqs (struct irq_domain *domain,int irq_base,unsigned int nr_irqs,int node,void *arg,bool realloc);kernel/irq/irqdomain.c +22
-0@@ -742,6 +742,42 @@ static int irq_domain_alloc_descs(int virq, unsigned int cnt, }#ifdef CONFIG_IRQ_DOMAIN_HIERARCHY +/** +* irq_domain_add_hierarchy - Add a irqdomain into the hierarchy +* @parent: Parent irq domain to associate with the new domain +* @flags: Irq domain flags associated to the domain +* @size: Size of the domain. See below +* @node: Optional device-tree node of the interrupt controller +* @ops: Pointer to the interrupt domain callbacks +* @host_data: Controller private data pointer +* +* If @size is 0 a tree domain is created, otherwise a linear domain. +* +* If successful the parent is associated to the new domain and the +* domain flags are set. +* Returns pointer to IRQ domain, or NULL on failure. +*/ +struct irq_domain *irq_domain_add_hierarchy (struct irq_domain *parent, +unsigned int flags, +unsigned int size, +struct device_node *node, +const struct irq_domain_ops *ops, +void *host_data) +{kernel/irq/irqdomain.c static int irq_domain_alloc_descs(int virq, unsigned int cnt, · line 776
Quoted 10 lines
kernel/irq/irqdomain.c +10
-0+struct irq_domain *domain; + +if (size) + domain =irq_domain_add_linear (node, size, ops, host_data); +else + domain =irq_domain_add_tree (node, ops, host_data); +if (domain) { + domain->parent = parent; + domain->flags |= flags; + }Ok, I'm going to reply to this patch because it is on the splat path but fixing this would need a bit more reorganizing.
So I'm seeing the lockdep splat below really early on an IVB laptop.
Basically we're not supposed to do
__GFP_FSallocations with IRQs off:2737 /* We're only interested
__GFP_FSallocations for now */
2738 if (!(gfp_mask &__GFP_FS))
2739 return;
2740
2741 /*
2742 * Oi! Can't be having__GFP_FSallocations with IRQs disabled.
2743 */
2744 if (DEBUG_LOCKS_WARN_ON(irqs_disabled_flags(flags))) <--- HERE!
2745 return;
2746
2747mark_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 pathintel_setup_irq_remapping()callsirq_domain_add_hierarchy()and it does by defaultGFP_KERNELallocations.The obvious fix is this and the machine boots fine with it. I'm not sure it is kosher though so I rather run it by people first:
---
kernel/irq/irqdomain.c +0
-0
kernel/irq/irqdomain.c struct irq_domain *__irq_domain_add(struct device_node *of_node, int size, · line 52
Quoted 7 lines
kernel/irq/irqdomain.c +4
-0@@ -46,14 +46,18 @@ struct irq_domain *__irq_domain_add(struct device_node *of_node, int size, void *host_data) {struct irq_domain *domain; +gfp_t gfp_flags =GFP_KERNEL ; + +if (irqs_disabled ()) + gfp_flags =GFP_NOFS ;
Hi Borislav,
Thanks for reporting and fixing this. How about usingGFP_ATOMIC here?
Regards!
Gerry
Quoted 71 lines
kernel/irq/irqdomain.c +2
-3domain =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)
Re: [tip:irq/irqdomain] irqdomain: Introduce helper function irq_domain_add_hierarchy()
Quoted 2 lines · Jiang Liu
Thanks for reporting and fixing this. How about using
GFP_ATOMIC
here?
Well, I don't see the need to use GFP_ATOMIC if we absolutely don't have to. And in this case lockdep is, AFAICT, correct in saying that we still can do allocations with interrupts disabled, only not go down into fs and do all kinds of lock grabbing operations like page reclaim, writeout or whatever it is being done nowadays there.
Yeah, this is also some old "no-no" in my memory which says that we should almost never use GFP_ATOMIC if it can be helped.
OTOH, I wonder if this code would rather need to hand down explicit gfp flags in case it should be able to do GFP_ATOMIC operations at some point...
Thanks.
Signature · 5 lines
Regards/Gruss,
Boris.
Sent from a fat crate under my desk. Formatting is fine.
--Re: [tip:irq/irqdomain] irqdomain: Introduce helper function irq_domain_add_hierarchy()
Quoted 4 lines · Borislav Petkov
Quoted 2 lines · Jiang Liu
Thanks for reporting and fixing this. How about using
GFP_ATOMIC
here?
Quoted 8 lines
Well, I don't see the need to use
GFP_ATOMICif 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_ATOMICif it can be helped.
Thanks for the info about GFP_ATOMIC, originally I have an impression that we should use GFP_ATOMIC when interrupt is disabled:(
Quoted 7 lines
OTOH, I wonder if this code would rather need to hand down explicit gfp flags in case it should be able to do
GFP_ATOMICoperations at some point...Thanks.
Re: [tip:irq/irqdomain] irqdomain: Introduce helper function irq_domain_add_hierarchy()
Quoted 2 lines · Jiang Liu
Thanks for the info about
GFP_ATOMIC, originally I have an impression that we should useGFP_ATOMICwhen interrupt is disabled:(
Hmm, that's actually a good point. GFP_NOFS has __GFP_WAIT which means that we can wait and resched when the first alloc attempt fails so maybe GFP_ATOMIC would be the correct solution after all...
Signature · 5 lines
Regards/Gruss,
Boris.
Sent from a fat crate under my desk. Formatting is fine.
--Re: [tip:irq/irqdomain] irqdomain: Introduce helper function irq_domain_add_hierarchy()
In reply to Borislav PetkovQuoted 31 lines · Borislav Petkov
So I'm seeing the lockdep splat below really early on an IVB laptop.
Basically we're not supposed to do
__GFP_FSallocations with IRQs off:2737 /* We're only interested
__GFP_FSallocations for now */
2738 if (!(gfp_mask &__GFP_FS))
2739 return;
2740
2741 /*
2742 * Oi! Can't be having__GFP_FSallocations with IRQs disabled.
2743 */
2744 if (DEBUG_LOCKS_WARN_ON(irqs_disabled_flags(flags))) <--- HERE!
2745 return;
2746
2747mark_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 pathintel_setup_irq_remapping()callsirq_domain_add_hierarchy()and it does by defaultGFP_KERNELallocations.The obvious fix is this and the machine boots fine with it. I'm not sure it is kosher though so I rather run it by people first:
---
kernel/irq/irqdomain.c +0
-0
kernel/irq/irqdomain.c struct irq_domain *__irq_domain_add(struct device_node *of_node, int size, · line 52
Quoted 7 lines
kernel/irq/irqdomain.c +4
-0@@ -46,14 +46,18 @@ struct irq_domain *__irq_domain_add(struct device_node *of_node, int size, void *host_data) {struct irq_domain *domain; +gfp_t gfp_flags =GFP_KERNEL ; + +if (irqs_disabled ()) + gfp_flags =GFP_NOFS ;
We want to use GFP_ATOMIC for that, but I really hate to do so. There is no reason except for the early boot stage to call into this code with interrupts disabled. And there we are covered by gfp_allowed_mask, so that a GFP_KERNEL allocation can succeed.
I have no idea, why enable_IR_x2apic() has been bolted into smp_prepare_cpus(). Probably just because.
There is no reason WHY this cannot be done in the early irq setup path (at least nowadays with the allocators being available early), but that is another area which needs some care and cleanup, but definitely too late before the 3.19 merge window opens.
So we have to bite the bullet and apply something like this along with a big fat comment WHY we are doing so and I'm tempted to wrap this into a #ifdef CONFIG_X86 so that noone else thinks that calling this code with interrupts disabled - except for the early boot stage - is a brilliant idea.
Thanks,
tglx
PATCH 1/1 irqdomain: Correct early allocation of irq domains with IRQs off
From: Borislav Petkov <[email protected]>
I'm seeing the following lockdep splat on an x2APIC machine with interrupts remapping. The problem is that enable_IR_x2apic() disables interrupts before doing any further initialization. However, after having moved to irq domains, domain allocation cannot happen with interrupts disabled (GFP_KERNEL).
A proper fix would be to move that initialization to the early irq setup path, which is something for another day. It is more involved work too.
So do a temporary fix which should not encourage the behaviour of even assuming the irq domains code should be called with interrupts disabled. Make it x86-only too.
...
dmar: Host address width 36
dmar: DRHD base: 0x000000fed90000 flags: 0x0
dmar: IOMMU 0: reg_base_addr fed90000 ver 1:0 cap c0000020e60262 ecap f0101a
dmar: DRHD base: 0x000000fed91000 flags: 0x1
dmar: IOMMU 1: reg_base_addr fed91000 ver 1:0 cap c9008020660262 ecap f0105a
dmar: RMRR base: 0x000000da2ba000 end: 0x000000da2d0fff
dmar: RMRR base: 0x000000db800000 end: 0x000000df9fffff
IOAPIC id 2 under DRHD base 0xfed91000 IOMMU 1
HPET id 0 under DRHD base 0xfed91000
Queued invalidation will be enabled to support x2apic and Intr-remapping.
------------[ cut here ]------------ WARNING: CPU: 0 PID: 1 at kernel/locking/lockdep.c:2744 lockdep_trace_alloc+0xd4/0xe0() DEBUG_LOCKS_WARN_ON(irqs_disabled_flags(flags)) Modules linked in: CPU: 0 PID: 1 Comm: swapper/0 Not tainted 3.18.0-rc6+ #1 Hardware name: LENOVO 2320CTO/2320CTO, BIOS G2ET86WW (2.06 ) 11/13/2012 0000000000000009 ffff880213d07b58 ffffffff816502d7 0000000000000000 ffff880213d07ba8 ffff880213d07b98 ffffffff81059790 0000000000000001 0000000000000092 0000000000000000 00000000000080d0 0000000000000000 Call Trace: dump_stack warn_slowpath_common warn_slowpath_fmt lockdep_trace_alloc __alloc_pages_nodemask ? get_page_from_freelist ? trace_hardirqs_off_caller alloc_kmem_pages_node kmalloc_large_node __kmalloc_node ? __dmar_enable_qi __irq_domain_add irq_domain_add_hierarchy intel_setup_irq_remapping.part.4 intel_enable_irq_remapping irq_remapping_enable enable_IR enable_IR_x2apic default_setup_apic_routing native_smp_prepare_cpus kernel_init_freeable ? ret_from_fork ? rest_init kernel_init ret_from_fork ? rest_init ---[ end trace fac50e785fc22942 ]---
Enabled IRQ remapping in x2apic mode Enabling x2apic
Enabled x2apic
Switched APIC routing to cluster x2apic. ..TIMER: vector=0x30 apic1=0 pin1=2 apic2=-1 pin2=-1
...
--- kernel/irq/irqdomain.c | 21 +++++++++++++++++++-- 1 file changed, 19 insertions(+), 2 deletions(-)
kernel/irq/irqdomain.c +19 -2
@@ -46,14 +46,31 @@ struct irq_domain *__irq_domain_add(struct device_node *of_node, int size, void *host_data) {struct irq_domain *domain; +gfp_t gfp_flags =GFP_KERNEL ; + +#ifdef CONFIG_X86 +/* +* BIG FAT COMMENT: Early initialization paths like enable_IR_x2apic(), +* for example, call into here with interrupts disabled but then we do +* allocate memory and can sleep so no-no. A proper fix would be to do +* x2APIC IR setup in the early irq setup path but it is too late for +* fixing it this way now, shortly before the merge window. +* +* So we do this little brown paper bag, which is temporary! Do not even +* think of calling irq domain setup code with IRQs disabled. You will +* get frozen-sharked! +*/ +if (irqs_disabled ()) + gfp_flags =GFP_NOFS ; +#endif domain =kzalloc_node (sizeof (*domain) + (sizeof (unsigned int ) * size), -GFP_KERNEL ,of_node_to_nid (of_node)); + gfp_flags,of_node_to_nid (of_node));if (WARN_ON (!domain))return NULL ;/* Fill structure */ -INIT_RADIX_TREE (&domain->revmap_tree,GFP_KERNEL ); +INIT_RADIX_TREE (&domain->revmap_tree, gfp_flags); domain->ops = ops; domain->host_data = host_data; domain->of_node =of_node_get (of_node);
Signature · 1 line
2.0.0
tip-bot for Borislav Petkov
merge bot[tip:x86/apic] irqdomain: Correct early allocation of irq domains with IRQs off
irqdomain: Correct early allocation of irq domains with IRQs off
Commit-ID: eda7516e1d428caf3cfc88e4292f2a1357a2e569 Gitweb: http://git.kernel.org/tip/eda7516e1d428caf3cfc88e4292f2a1357a2e569 Author: Borislav Petkov <[email protected]> AuthorDate: Sun, 30 Nov 2014 13:37:43 +0100 Committer: Thomas Gleixner <[email protected]> CommitDate: Mon, 1 Dec 2014 10:38:30 +0100
irqdomain: Correct early allocation of irq domains with IRQs off
I'm seeing the following lockdep splat on an x2APIC machine with interrupts remapping. The problem is that enable_IR_x2apic() disables interrupts before doing any further initialization. However, after having moved to irq domains, domain allocation cannot happen with interrupts disabled (GFP_KERNEL).
A proper fix would be to move that initialization to the early irq setup path, which is something for another day. It is more involved work too.
So do a temporary fix which should not encourage the behaviour of even assuming the irq domains code should be called with interrupts disabled. Make it x86-only too.
...
dmar: Host address width 36
dmar: DRHD base: 0x000000fed90000 flags: 0x0
dmar: IOMMU 0: reg_base_addr fed90000 ver 1:0 cap c0000020e60262 ecap f0101a
dmar: DRHD base: 0x000000fed91000 flags: 0x1
dmar: IOMMU 1: reg_base_addr fed91000 ver 1:0 cap c9008020660262 ecap f0105a
dmar: RMRR base: 0x000000da2ba000 end: 0x000000da2d0fff
dmar: RMRR base: 0x000000db800000 end: 0x000000df9fffff
IOAPIC id 2 under DRHD base 0xfed91000 IOMMU 1
HPET id 0 under DRHD base 0xfed91000
Queued invalidation will be enabled to support x2apic and Intr-remapping.
------------[ cut here ]------------ WARNING: CPU: 0 PID: 1 at kernel/locking/lockdep.c:2744 lockdep_trace_alloc+0xd4/0xe0() DEBUG_LOCKS_WARN_ON(irqs_disabled_flags(flags)) Modules linked in: CPU: 0 PID: 1 Comm: swapper/0 Not tainted 3.18.0-rc6+ #1 Hardware name: LENOVO 2320CTO/2320CTO, BIOS G2ET86WW (2.06 ) 11/13/2012 0000000000000009 ffff880213d07b58 ffffffff816502d7 0000000000000000 ffff880213d07ba8 ffff880213d07b98 ffffffff81059790 0000000000000001 0000000000000092 0000000000000000 00000000000080d0 0000000000000000 Call Trace: dump_stack warn_slowpath_common warn_slowpath_fmt lockdep_trace_alloc __alloc_pages_nodemask ? get_page_from_freelist ? trace_hardirqs_off_caller alloc_kmem_pages_node kmalloc_large_node __kmalloc_node ? __dmar_enable_qi __irq_domain_add irq_domain_add_hierarchy intel_setup_irq_remapping.part.4 intel_enable_irq_remapping irq_remapping_enable enable_IR enable_IR_x2apic default_setup_apic_routing native_smp_prepare_cpus kernel_init_freeable ? ret_from_fork ? rest_init kernel_init ret_from_fork ? rest_init ---[ end trace fac50e785fc22942 ]---
Enabled IRQ remapping in x2apic mode Enabling x2apic
Enabled x2apic
Switched APIC routing to cluster x2apic. ..TIMER: vector=0x30 apic1=0 pin1=2 apic2=-1 pin2=-1
...
--- kernel/irq/irqdomain.c | 21 +++++++++++++++++++-- 1 file changed, 19 insertions(+), 2 deletions(-)
kernel/irq/irqdomain.c +19 -2
@@ -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);
Re: [tip:irq/irqdomain] irqdomain: Introduce helper function irq_domain_add_hierarchy()
In reply to Thomas GleixnerQuoted 45 lines · Thomas Gleixner
Quoted 31 lines · Borislav Petkov
So I'm seeing the lockdep splat below really early on an IVB laptop.
Basically we're not supposed to do
__GFP_FSallocations with IRQs off:2737 /* We're only interested
__GFP_FSallocations for now */
2738 if (!(gfp_mask &__GFP_FS))
2739 return;
2740
2741 /*
2742 * Oi! Can't be having__GFP_FSallocations with IRQs disabled.
2743 */
2744 if (DEBUG_LOCKS_WARN_ON(irqs_disabled_flags(flags))) <--- HERE!
2745 return;
2746
2747mark_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 pathintel_setup_irq_remapping()callsirq_domain_add_hierarchy()and it does by defaultGFP_KERNELallocations.The obvious fix is this and the machine boots fine with it. I'm not sure it is kosher though so I rather run it by people first:
---
kernel/irq/irqdomain.c +0
-0kernel/irq/irqdomain.c struct irq_domain *__irq_domain_add(struct device_node *of_node, int size, · line 52
Quoted 7 lines
kernel/irq/irqdomain.c +4
-0@@ -46,14 +46,18 @@ struct irq_domain *__irq_domain_add(struct device_node *of_node, int size, void *host_data) {struct irq_domain *domain; +gfp_t gfp_flags =GFP_KERNEL ; + +if (irqs_disabled ()) + gfp_flags =GFP_NOFS ;We want to use
GFP_ATOMICfor 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 bygfp_allowed_mask, so that aGFP_KERNELallocation can succeed.
Quoted 7 lines
I have no idea, why
enable_IR_x2apic()has been bolted intosmp_prepare_cpus(). Probably just because.There is no reason WHY this cannot be done in the early irq setup path (at least nowadays with the allocators being available early), but that is another area which needs some care and cleanup, but definitely too late before the 3.19 merge window opens.
Hi Thomas,
I will have a look at this after 3.19 merge window:)
Quoted 11 lines
So we have to bite the bullet and apply something like this along with a big fat comment WHY we are doing so and I'm tempted to wrap this into a #ifdef
CONFIG_X86so that noone else thinks that calling this code with interrupts disabled - except for the early boot stage - is a brilliant idea.Thanks,
tglx
PATCH v2 5/9 PCI, MSI: Introduce helpers to hide struct msi_desc implementation details
In reply to Jiang LiuIntroduce 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
@@ -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
tip-bot for Jiang Liu
merge bot[tip:irq/irqdomain] PCI/MSI: Introduce helpers to hide struct msi_desc implementation details
PCI/MSI: Introduce helpers to hide struct msi_desc implementation details
Commit-ID: d31eb342409b24e3d2e1989c775f3361e93acc08 Gitweb: http://git.kernel.org/tip/d31eb342409b24e3d2e1989c775f3361e93acc08 Author: Jiang Liu <[email protected]> AuthorDate: Sat, 15 Nov 2014 22:24:03 +0800 Committer: Thomas Gleixner <[email protected]> CommitDate: Sun, 23 Nov 2014 13:01:45 +0100
PCI/MSI: Introduce helpers to hide struct msi_desc implementation details
Introduce helpers to hide struct msi_desc implementation details, so we could easily support non-PCI-compliant MSI devices later by moving msi_list into struct device.
--- include/linux/msi.h | 19 +++++++++++++++++++ 1 file changed, 19 insertions(+)
include/linux/msi.h +19 -0
@@ -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);
PATCH v2 6/9 genirq: Introduce msi_domain_{alloc|free}_irqs()
In reply to Jiang LiuIntroduce msi_domain_{alloc|free}_irqs() to alloc/free interrupts from generic MSI irqdomain.
--- include/linux/msi.h | 39 +++++++++++++++++++++++++++++++++ kernel/irq/msi.c | 60 +++++++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 99 insertions(+)
include/linux/msi.h +39 -0
@@ -3,6 +3,7 @@#include <linux/kobject.h> #include <linux/list.h> +#include <asm/hw_irq.h> /* for msi_alloc_info_t */ struct msi_msg {u32 address_lo;/* low 32 bits of msi message address */
@@ -100,7 +101,29 @@ struct irq_chip;struct device_node ;struct msi_domain_info ; +#ifndef NUM_MSI_ALLOC_SCRATCHPAD_REGS +#define NUM_MSI_ALLOC_SCRATCHPAD_REGS 2 +#endif + +/* +* Default structure for MSI interrupt allocation. +* Arch may overwrite it by defining msi_alloc_info_t. +*/ +struct msi_alloc_info { +struct msi_desc *desc; +irq_hw_number_t hwirq; +union { +unsigned long ul; +void *ptr; + } scratchpad[NUM_MSI_ALLOC_SCRATCHPAD_REGS ]; +}; + +#ifndef msi_alloc_info_t +typedef struct msi_alloc_info msi_alloc_info_t ; +#endif +struct msi_domain_ops { +/* Callbacks for msi_create_irq_domain() */ void (*calc_hwirq)(struct msi_domain_info *info,void *arg,struct msi_desc *desc);irq_hw_number_t (*get_hwirq)(struct msi_domain_info *info,void *arg);
@@ -111,6 +134,19 @@ struct msi_domain_ops {void (*msi_free)(struct irq_domain *domain,struct msi_domain_info *info,unsigned int virq); + +/* Callbacks for msi_irq_domain_alloc_irqs() based on msi_descs */ +int (*msi_check)(struct irq_domain *domain, +struct msi_domain_info *info, +struct device *dev); +int (*msi_prepare)(struct irq_domain *domain, +struct device *dev,int nvec, +msi_alloc_info_t *arg); +void (*msi_finish)(msi_alloc_info_t *arg,int retval); +void (*set_desc)(msi_alloc_info_t *arg, +struct msi_desc *desc); +int (*handle_error)(struct irq_domain *domain, +struct msi_desc *desc,int error); };struct msi_domain_info {
@@ -125,6 +161,9 @@ int msi_domain_set_affinity(struct irq_data *data, const struct cpumask *mask,struct irq_domain *msi_create_irq_domain (struct device_node *of_node,struct msi_domain_info *info,struct irq_domain *parent); +int msi_domain_alloc_irqs (struct irq_domain *domain,struct device *dev, +int nvec); +void msi_domain_free_irqs (struct irq_domain *domain,struct device *dev);struct msi_domain_info *msi_get_domain_info (struct irq_domain *domain);#endif /* CONFIG_GENERIC_MSI_IRQ_DOMAIN */
kernel/irq/msi.c +60 -0
72 lines not on this page · raw message
Signature · 1 line
1.7.10.4
tip-bot for Jiang Liu
merge bot[tip:irq/irqdomain] genirq: Introduce msi_domain_alloc/free_irqs( )
genirq: Introduce msi_domain_alloc/free_irqs()
Commit-ID: d9109698be6e7439e6082aa00d79d4556114739b Gitweb: http://git.kernel.org/tip/d9109698be6e7439e6082aa00d79d4556114739b Author: Jiang Liu <[email protected]> AuthorDate: Sat, 15 Nov 2014 22:24:04 +0800 Committer: Thomas Gleixner <[email protected]> CommitDate: Sun, 23 Nov 2014 13:01:47 +0100
genirq: Introduce msi_domain_alloc/free_irqs()
Introduce msi_domain_{alloc|free}_irqs() to alloc/free interrupts from generic MSI irqdomain.
--- include/linux/msi.h | 29 +++++++++++++++++++++ kernel/irq/msi.c | 75 +++++++++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 104 insertions(+)
include/linux/msi.h +29 -0
@@ -115,6 +115,9 @@ struct msi_controller { };#ifdef CONFIG_GENERIC_MSI_IRQ_DOMAIN + +#include <asm/msi.h> +struct irq_domain ;struct irq_chip ;struct device_node ;
@@ -125,6 +128,18 @@ struct msi_domain_info;* @get_hwirq: Retrieve the resulting hw irq number * @msi_init: Domain specific init function for MSI interrupts * @msi_free: Domain specific function to free a MSI interrupts +* @msi_check: Callback for verification of the domain/info/dev data +* @msi_prepare: Prepare the allocation of the interrupts in the domain +* @msi_finish: Optional callbacl to finalize the allocation +* @set_desc: Set the msi descriptor for an interrupt +* @handle_error: Optional error handler if the allocation fails +* +* @get_hwirq, @msi_init and @msi_free are callbacks used by +* msi_create_irq_domain() and related interfaces +* +* @msi_check, @msi_prepare, @msi_finish, @set_desc and @handle_error +* are callbacks used by msi_irq_domain_alloc_irqs() and related +* interfaces which are based on msi_desc. */ struct msi_domain_ops {irq_hw_number_t (*get_hwirq)(struct msi_domain_info *info,void *arg);
@@ -135,6 +150,17 @@ struct msi_domain_ops {void (*msi_free)(struct irq_domain *domain,struct msi_domain_info *info,unsigned int virq); +int (*msi_check)(struct irq_domain *domain, +struct msi_domain_info *info, +struct device *dev); +int (*msi_prepare)(struct irq_domain *domain, +struct device *dev,int nvec, +msi_alloc_info_t *arg); +void (*msi_finish)(msi_alloc_info_t *arg,int retval); +void (*set_desc)(msi_alloc_info_t *arg, +struct msi_desc *desc); +int (*handle_error)(struct irq_domain *domain, +struct msi_desc *desc,int error); };/**
@@ -155,6 +181,9 @@ int msi_domain_set_affinity(struct irq_data *data, const struct cpumask *mask,struct irq_domain *msi_create_irq_domain (struct device_node *of_node,struct msi_domain_info *info,struct irq_domain *parent); +int msi_domain_alloc_irqs (struct irq_domain *domain,struct device *dev, +int nvec); +void msi_domain_free_irqs (struct irq_domain *domain,struct device *dev);struct msi_domain_info *msi_get_domain_info (struct irq_domain *domain);#endif /* CONFIG_GENERIC_MSI_IRQ_DOMAIN */
kernel/irq/msi.c +75 -0
87 lines not on this page · raw message
PATCH v2 7/9 genirq: Provide default callbacks for msi_domain_ops
In reply to Jiang LiuExtend struct msi_domain_info and provide default callbacks for msi_domain_ops.
--- include/linux/msi.h | 29 ++++++++++--- kernel/irq/msi.c | 113 +++++++++++++++++++++++++++++++++++++++++++++++---- 2 files changed, 129 insertions(+), 13 deletions(-)
include/linux/msi.h +24 -5
@@ -3,6 +3,7 @@#include <linux/kobject.h> #include <linux/list.h> +#include <linux/irqhandler.h> #include <asm/hw_irq.h> /* for msi_alloc_info_t */ struct msi_msg {
@@ -124,13 +125,15 @@ typedef struct msi_alloc_info msi_alloc_info_t;struct msi_domain_ops {/* Callbacks for msi_create_irq_domain() */ -void (*calc_hwirq)(struct msi_domain_info *info,void *arg, +void (*calc_hwirq)(struct msi_domain_info *info, +msi_alloc_info_t *arg,struct msi_desc *desc); -irq_hw_number_t (*get_hwirq)(struct msi_domain_info *info,void *arg); +irq_hw_number_t (*get_hwirq)(struct msi_domain_info *info, +msi_alloc_info_t *arg);int (*msi_init)(struct irq_domain *domain,struct msi_domain_info *info,unsigned int virq,irq_hw_number_t hwirq, -void *arg); +msi_alloc_info_t *arg);void (*msi_free)(struct irq_domain *domain,struct msi_domain_info *info,unsigned int virq);
@@ -150,15 +153,31 @@ struct msi_domain_ops { };struct msi_domain_info { +u32 flags;struct msi_domain_ops *ops;struct irq_chip *chip; -void *data; +void *chip_data;/* optional chip data */ +irq_flow_handler_t handler;/* optional flow handler */ +void *handler_data;/* optional handler data */ +const char *handler_name;/* optional handler name */ +void *data;/* optional private data */ }; +/* Use default MSI domain ops if possible */ +#define MSI_FLAG_USE_DEF_DOM_OPS 0x1 +/* Use default MSI chip ops if possible */ +#define MSI_FLAG_USE_DEF_CHIP_OPS 0x2 +/* Build identity map between hwirq and irq */ +#define MSI_FLAG_IDENTITY_MAP 0x10 +/* Support multiple PCI MSI interrupts */ +#define MSI_FLAG_MULTI_PCI_MSI 0x100 +/* Support PCI MSIX interrupts */ +#define MSI_FLAG_PCI_MSIX 0x200 +int msi_domain_set_affinity (struct irq_data *data,const struct cpumask *mask,bool force); -struct irq_domain *msi_create_irq_domain (struct device_node *of_node, +struct irq_domain *msi_create_irq_domain (struct device_node *node,struct msi_domain_info *info,struct irq_domain *parent); int msi_domain_alloc_irqs(struct irq_domain *domain, struct device *dev,
kernel/irq/msi.c +105 -8
136 lines not on this page · raw message
Signature · 1 line
1.7.10.4
tip-bot for Jiang Liu
merge bot[tip:irq/irqdomain] genirq: Provide default callbacks for msi_domain_ops
genirq: Provide default callbacks for msi_domain_ops
Commit-ID: aeeb59657c35da64068336c20068da237f41ab76 Gitweb: http://git.kernel.org/tip/aeeb59657c35da64068336c20068da237f41ab76 Author: Jiang Liu <[email protected]> AuthorDate: Sat, 15 Nov 2014 22:24:05 +0800 Committer: Thomas Gleixner <[email protected]> CommitDate: Sun, 23 Nov 2014 13:01:47 +0100
genirq: Provide default callbacks for msi_domain_ops
Extend struct msi_domain_info and provide default callbacks for msi_domain_ops.
--- include/linux/msi.h | 42 +++++++++++++++++--- kernel/irq/msi.c | 111 ++++++++++++++++++++++++++++++++++++++++++++++++---- 2 files changed, 140 insertions(+), 13 deletions(-)
include/linux/msi.h +37 -5
@@ -116,6 +116,7 @@ struct msi_controller {#ifdef CONFIG_GENERIC_MSI_IRQ_DOMAIN +#include <linux/irqhandler.h> #include <asm/msi.h> struct irq_domain ;
@@ -142,11 +143,12 @@ struct msi_domain_info;* interfaces which are based on msi_desc. */ struct msi_domain_ops { -irq_hw_number_t (*get_hwirq)(struct msi_domain_info *info,void *arg); +irq_hw_number_t (*get_hwirq)(struct msi_domain_info *info, +msi_alloc_info_t *arg);int (*msi_init)(struct irq_domain *domain,struct msi_domain_info *info,unsigned int virq,irq_hw_number_t hwirq, -void *arg); +msi_alloc_info_t *arg);void (*msi_free)(struct irq_domain *domain,struct msi_domain_info *info,unsigned int virq);
@@ -165,16 +167,46 @@ struct msi_domain_ops {/** * struct msi_domain_info - MSI interrupt domain data -* @ops: The callback data structure -* @chip: The associated interrupt chip -* @data: Domain specific data +* @flags: Flags to decribe features and capabilities +* @ops: The callback data structure +* @chip: Optional: associated interrupt chip +* @chip_data: Optional: associated interrupt chip data +* @handler: Optional: associated interrupt flow handler +* @handler_data: Optional: associated interrupt flow handler data +* @handler_name: Optional: associated interrupt flow handler name +* @data: Optional: domain specific data */ struct msi_domain_info { +u32 flags;struct msi_domain_ops *ops;struct irq_chip *chip; +void *chip_data; +irq_flow_handler_t handler; +void *handler_data; +const char *handler_name;void *data; }; +/* Flags for msi_domain_info */ +enum { +/* +* Init non implemented ops callbacks with default MSI domain +* callbacks. +*/ +MSI_FLAG_USE_DEF_DOM_OPS = (1 <<0 ), +/* +* Init non implemented chip callbacks with default MSI chip +* callbacks. +*/ +MSI_FLAG_USE_DEF_CHIP_OPS = (1 <<1 ), +/* Build identity map between hwirq and irq */ +MSI_FLAG_IDENTITY_MAP = (1 <<2 ), +/* Support multiple PCI MSI interrupts */ +MSI_FLAG_MULTI_PCI_MSI = (1 <<3 ), +/* Support PCI MSIX interrupts */ +MSI_FLAG_PCI_MSIX = (1 <<4 ), +}; +int msi_domain_set_affinity (struct irq_data *data,const struct cpumask *mask,bool force);
kernel/irq/msi.c +103 -8
140 lines not on this page · raw message
PATCH v2 8/9 PCI, MSI: Refine irqdomain interfaces to simplify its usage
In reply to Jiang LiuRefine irqdomain interfaces to simplify its usage.
--- drivers/pci/msi.c | 119 ++++++++++++++++++++++++++++++++++++++------------- include/linux/msi.h | 13 +++--- 2 files changed, 98 insertions(+), 34 deletions(-)
drivers/pci/msi.c +90 -29
@@ -1099,38 +1099,99 @@ irq_hw_number_t pci_msi_domain_calc_hwirq(struct pci_dev *dev, (pci_domain_nr (dev->bus) &0xFFFFFFFF ) <<27 ; } -int pci_msi_domain_alloc_irqs (struct irq_domain *domain,int type, -struct pci_dev *dev,void *arg) +static inline bool pci_msi_desc_is_multi_msi (struct msi_desc *desc) { -struct msi_domain_info *info = domain->host_data; -int node =dev_to_node (&dev->dev); -struct msi_desc *desc; -int i, virq; - -list_for_each_entry (desc, &dev->msi_list, list) { -if (info->ops->calc_hwirq) - info->ops->calc_hwirq (info, arg, desc); - - virq =irq_domain_alloc_irqs (domain, desc->nvec_used, - node, arg); -if (virq <0 ) { -/* Special handling for pci_enable_msi_range(). */ -if (type ==PCI_CAP_ID_MSI && desc->nvec_used >1 ) -return 1 ; -else -return -ENOSPC ; - } -for (i =0 ; i < desc->nvec_used; i++) -irq_set_msi_desc_off (virq, i, desc); - } +return !desc->msi_attrib.is_msix && desc->nvec_used >1 ; +} -list_for_each_entry (desc, &dev->msi_list, list) -if (desc->nvec_used ==1 ) -dev_dbg (&dev->dev,"irq %d for MSI/MSI-X\n" , virq); -else -dev_dbg (&dev->dev,"irq [%d-%d] for MSI/MSI-X\n" , - virq, virq + desc->nvec_used -1 ); +int pci_msi_domain_check_cap (struct irq_domain *domain, +struct msi_domain_info *info,struct device *dev) +{ +struct msi_desc *desc =first_pci_msi_entry (to_pci_dev (dev)); + +/* Special handling to support pci_enable_msi_range() */ +if (pci_msi_desc_is_multi_msi (desc) && + !(info->flags &MSI_FLAG_MULTI_PCI_MSI )) +return 1 ; +else if (desc->msi_attrib.is_msix && !(info->flags &MSI_FLAG_PCI_MSIX )) +return -ENOTSUPP ;return 0 ; } + +static int pci_msi_domain_handle_error (struct irq_domain *domain, +struct msi_desc *desc,int error) +{ +/* Special handling to support pci_enable_msi_range() */ +if (pci_msi_desc_is_multi_msi (desc) && error == -ENOSPC ) +return 1 ; + +return error; +} + +#ifndef msi_alloc_info_t +static void pci_msi_domain_set_desc (struct msi_alloc_info *arg, +struct msi_desc *desc) +{ + arg->desc = desc; + arg->hwirq =pci_msi_domain_calc_hwirq (msi_desc_to_pci_dev (desc), + desc); +} +#else +#define pci_msi_domain_set_desc NULL +#endif + +static struct msi_domain_ops pci_msi_domain_ops_default = { + .set_desc = pci_msi_domain_set_desc, + .msi_check = pci_msi_domain_check_cap, + .handle_error = pci_msi_domain_handle_error, +}; + +static void pci_msi_domain_update_dom_ops (struct msi_domain_info *info) +{ +struct msi_domain_ops *ops = info->ops; + +if (ops ==NULL ) { + info->ops = &pci_msi_domain_ops_default; + }else { +if (ops->set_desc ==NULL ) + ops->set_desc = pci_msi_domain_set_desc; +if (ops->msi_check ==NULL ) + ops->msi_check = pci_msi_domain_check_cap; +if (ops->handle_error ==NULL ) + ops->handle_error = pci_msi_domain_handle_error; + } +} + +static void pci_msi_domain_update_chip_ops (struct msi_domain_info *info) +{ +struct irq_chip *chip = info->chip; + +BUG_ON (!chip); +if (!chip->irq_write_msi_msg) + chip->irq_write_msi_msg = pci_msi_domain_write_msg; +} + +struct irq_domain *pci_msi_create_irq_domain (struct device_node *node, +struct msi_domain_info *info, +struct irq_domain *parent) +{ +if (info->flags &MSI_FLAG_USE_DEF_DOM_OPS ) +pci_msi_domain_update_dom_ops (info); +if (info->flags &MSI_FLAG_USE_DEF_CHIP_OPS ) +pci_msi_domain_update_chip_ops (info); + +return msi_create_irq_domain (node, info, parent); +} + +int pci_msi_domain_alloc_irqs (struct irq_domain *domain,struct pci_dev *dev, +int nvec,int type) +{ +return msi_domain_alloc_irqs (domain, &dev->dev, nvec); +} + +void pci_msi_domain_free_irqs (struct irq_domain *domain,struct pci_dev *dev) +{ +msi_domain_free_irqs (domain, &dev->dev); +}#endif /* CONFIG_PCI_MSI_IRQ_DOMAIN */
include/linux/msi.h +8 -5
@@ -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
PATCH v2 9/9 PCI, MSI: Provide mechanism to alloc/free MSI/MSIX interrupt from irqdomain
In reply to Jiang LiuProvide 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
@@ -27,8 +27,41 @@ static int pci_msi_enable = 1;#define msix_table_size (flags) ((flags & PCI_MSIX_FLAGS_QSIZE) + 1) -/* Arch hooks */ +#ifdef CONFIG_PCI_MSI_IRQ_DOMAIN +static struct irq_domain *pci_msi_default_domain; + +struct irq_domain *__weak arch_get_pci_msi_domain (struct pci_dev *dev) +{ +return pci_msi_default_domain; +} + +static int pci_msi_setup_msi_irqs (struct pci_dev *dev,int nvec,int type) +{ +struct irq_domain *domain; + domain =arch_get_pci_msi_domain (dev); +if (domain) +return pci_msi_domain_alloc_irqs (domain, dev, nvec, type); + +return arch_setup_msi_irqs (dev, nvec, type); +} + +static void pci_msi_teardown_msi_irqs (struct pci_dev *dev) +{ +struct irq_domain *domain; + + domain =arch_get_pci_msi_domain (dev); +if (domain) +pci_msi_domain_free_irqs (domain, dev); +else +arch_teardown_msi_irqs (dev); +} +#else +#define pci_msi_setup_msi_irqs arch_setup_msi_irqs +#define pci_msi_teardown_msi_irqs arch_teardown_msi_irqs +#endif + +/* Arch hooks */ int __weak arch_setup_msi_irq (struct pci_dev *dev,struct msi_desc *desc) {struct msi_chip *chip = dev->bus->msi;
@@ -329,7 +362,7 @@ static void free_msi_irqs(struct pci_dev *dev)for (i =0 ; i < entry->nvec_used; i++)BUG_ON (irq_has_action (entry->irq + i)); -arch_teardown_msi_irqs (dev); +pci_msi_teardown_msi_irqs (dev);list_for_each_entry_safe (entry, tmp, &dev->msi_list, list) {if (entry->msi_attrib.is_msix) {
@@ -581,7 +614,7 @@ static int msi_capability_init(struct pci_dev *dev, int nvec)list_add_tail (&entry->list, &dev->msi_list);/* Configure MSI capability structure */ - ret =arch_setup_msi_irqs (dev, nvec,PCI_CAP_ID_MSI ); + ret =pci_msi_setup_msi_irqs (dev, nvec,PCI_CAP_ID_MSI );if (ret) {msi_mask_irq (entry, mask, ~mask);free_msi_irqs (dev);
@@ -696,7 +729,7 @@ static int msix_capability_init(struct pci_dev *dev,if (ret)return ret; - ret =arch_setup_msi_irqs (dev, nvec,PCI_CAP_ID_MSIX ); + ret =pci_msi_setup_msi_irqs (dev, nvec,PCI_CAP_ID_MSIX );if (ret) goto out_avail;
@@ -1194,4 +1227,23 @@ void pci_msi_domain_free_irqs(struct irq_domain *domain, struct pci_dev *dev) {msi_domain_free_irqs (domain, &dev->dev); } + +struct irq_domain *pci_msi_create_default_irq_domain (struct device_node *node, +struct msi_domain_info *info,struct irq_domain *parent) +{ +struct irq_domain *domain; +static DEFINE_MUTEX (pci_msi_domain_lock); + +mutex_lock (&pci_msi_domain_lock); +if (pci_msi_default_domain) { +pr_err ("PCI: default irq domain for PCI MSI has already been created.\n" ); + domain =NULL ; + }else { + domain =pci_msi_create_irq_domain (node, info, parent); + pci_msi_default_domain = domain; + } +mutex_unlock (&pci_msi_domain_lock); + +return domain; +}#endif /* CONFIG_PCI_MSI_IRQ_DOMAIN */
include/linux/msi.h +3 -0
@@ -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
tip-bot for Jiang Liu
merge bot[tip:irq/irqdomain] PCI/MSI: Provide mechanism to alloc/free MSI/ MSIX interrupt from irqdomain
PCI/MSI: Provide mechanism to alloc/free MSI/MSIX interrupt from irqdomain
Commit-ID: 8e047adae969701c6cec136484bb9de8572af934 Gitweb: http://git.kernel.org/tip/8e047adae969701c6cec136484bb9de8572af934 Author: Jiang Liu <[email protected]> AuthorDate: Sat, 15 Nov 2014 22:24:07 +0800 Committer: Thomas Gleixner <[email protected]> CommitDate: Sun, 23 Nov 2014 13:01:47 +0100
PCI/MSI: Provide mechanism to alloc/free MSI/MSIX interrupt from irqdomain
Provide mechanism to directly alloc/free MSI/MSIX interrupt from irqdomain, which will be used to replace arch_setup_msi_irq()/ arch_setup_msi_irqs()/arch_teardown_msi_irq()/arch_teardown_msi_irqs().
To kill weak functions, this patch introduce a new weak function arch_get_pci_msi_domain(), which is to retrieve the MSI irqdomain for a PCI device. This weak function could be killed once we get a common way to associate MSI domain with PCI device.
--- drivers/pci/msi.c | 67 ++++++++++++++++++++++++++++++++++++++++++++++++++--- include/linux/msi.h | 3 +++ 2 files changed, 67 insertions(+), 3 deletions(-)
drivers/pci/msi.c +64 -3
@@ -28,6 +28,40 @@ int pci_msi_ignore_mask;#define msix_table_size (flags) ((flags & PCI_MSIX_FLAGS_QSIZE) + 1) +#ifdef CONFIG_PCI_MSI_IRQ_DOMAIN +static struct irq_domain *pci_msi_default_domain; +static DEFINE_MUTEX (pci_msi_domain_lock); + +struct irq_domain *__weak arch_get_pci_msi_domain (struct pci_dev *dev) +{ +return pci_msi_default_domain; +} + +static int pci_msi_setup_msi_irqs (struct pci_dev *dev,int nvec,int type) +{ +struct irq_domain *domain; + + domain =arch_get_pci_msi_domain (dev); +if (domain) +return pci_msi_domain_alloc_irqs (domain, dev, nvec, type); + +return arch_setup_msi_irqs (dev, nvec, type); +} + +static void pci_msi_teardown_msi_irqs (struct pci_dev *dev) +{ +struct irq_domain *domain; + + domain =arch_get_pci_msi_domain (dev); +if (domain) +pci_msi_domain_free_irqs (domain, dev); +else +arch_teardown_msi_irqs (dev); +} +#else +#define pci_msi_setup_msi_irqs arch_setup_msi_irqs +#define pci_msi_teardown_msi_irqs arch_teardown_msi_irqs +#endif /* Arch hooks */
@@ -348,7 +382,7 @@ static void free_msi_irqs(struct pci_dev *dev)for (i =0 ; i < entry->nvec_used; i++)BUG_ON (irq_has_action (entry->irq + i)); -arch_teardown_msi_irqs (dev); +pci_msi_teardown_msi_irqs (dev);list_for_each_entry_safe (entry, tmp, &dev->msi_list, list) {if (entry->msi_attrib.is_msix) {
@@ -600,7 +634,7 @@ static int msi_capability_init(struct pci_dev *dev, int nvec)list_add_tail (&entry->list, &dev->msi_list);/* Configure MSI capability structure */ - ret =arch_setup_msi_irqs (dev, nvec,PCI_CAP_ID_MSI ); + ret =pci_msi_setup_msi_irqs (dev, nvec,PCI_CAP_ID_MSI );if (ret) {msi_mask_irq (entry, mask, ~mask);free_msi_irqs (dev);
@@ -715,7 +749,7 @@ static int msix_capability_init(struct pci_dev *dev,if (ret)return ret; - ret =arch_setup_msi_irqs (dev, nvec,PCI_CAP_ID_MSIX ); + ret =pci_msi_setup_msi_irqs (dev, nvec,PCI_CAP_ID_MSIX );if (ret) goto out_avail;
@@ -1258,4 +1292,31 @@ void pci_msi_domain_free_irqs(struct irq_domain *domain, struct pci_dev *dev) {msi_domain_free_irqs (domain, &dev->dev); } + +/** +* pci_msi_create_default_irq_domain - Create a default MSI interrupt domain +* @node: Optional device-tree node of the interrupt controller +* @info: MSI domain info +* @parent: Parent irq domain +* +* Returns: A domain pointer or NULL in case of failure. If successful +* the default PCI/MSI irqdomain pointer is updated. +*/ +struct irq_domain *pci_msi_create_default_irq_domain (struct device_node *node, +struct msi_domain_info *info,struct irq_domain *parent) +{ +struct irq_domain *domain; + +mutex_lock (&pci_msi_domain_lock); +if (pci_msi_default_domain) { +pr_err ("PCI: default irq domain for PCI MSI has already been created.\n" ); + domain =NULL ; + }else { + domain =pci_msi_create_irq_domain (node, info, parent); + pci_msi_default_domain = domain; + } +mutex_unlock (&pci_msi_domain_lock); + +return domain; +}#endif /* CONFIG_PCI_MSI_IRQ_DOMAIN */
include/linux/msi.h +3 -0
@@ -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,