diff options
author | Jiang Liu <jiang.liu@linux.intel.com> | 2015-04-13 02:11:35 -0400 |
---|---|---|
committer | Thomas Gleixner <tglx@linutronix.de> | 2015-04-24 09:36:49 -0400 |
commit | 52f518a3a7c2f80551a38d38be28bc9f335e713c (patch) | |
tree | 76b270835fbb27ffdaf72004594405bc058c7d54 | |
parent | 3cb96f0c97330834929abe9bd2ca3c252a83def0 (diff) |
x86/MSI: Use hierarchical irqdomains to manage MSI interrupts
Enhance MSI code to support hierarchical irqdomains, it helps to make
the architecture more clear.
Signed-off-by: Jiang Liu <jiang.liu@linux.intel.com>
Cc: Konrad Rzeszutek Wilk <konrad.wilk@oracle.com>
Cc: David Cohen <david.a.cohen@linux.intel.com>
Cc: Sander Eikelenboom <linux@eikelenboom.it>
Cc: David Vrabel <david.vrabel@citrix.com>
Cc: Tony Luck <tony.luck@intel.com>
Cc: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
Cc: iommu@lists.linux-foundation.org
Cc: Joerg Roedel <jroedel@suse.de>
Cc: Bjorn Helgaas <bhelgaas@google.com>
Cc: Benjamin Herrenschmidt <benh@kernel.crashing.org>
Cc: Rafael J. Wysocki <rjw@rjwysocki.net>
Cc: Randy Dunlap <rdunlap@infradead.org>
Cc: Yinghai Lu <yinghai@kernel.org>
Cc: Borislav Petkov <bp@alien8.de>
Cc: Dimitri Sivanich <sivanich@sgi.com>
Cc: Joerg Roedel <joro@8bytes.org>
Link: http://lkml.kernel.org/r/1428905519-23704-14-git-send-email-jiang.liu@linux.intel.com
Signed-off-by: Thomas Gleixner <tglx@linutronix.de>
-rw-r--r-- | arch/x86/Kconfig | 1 | ||||
-rw-r--r-- | arch/x86/include/asm/hw_irq.h | 9 | ||||
-rw-r--r-- | arch/x86/include/asm/irq_remapping.h | 6 | ||||
-rw-r--r-- | arch/x86/include/asm/msi.h | 7 | ||||
-rw-r--r-- | arch/x86/kernel/apic/msi.c | 141 | ||||
-rw-r--r-- | arch/x86/kernel/apic/vector.c | 2 | ||||
-rw-r--r-- | drivers/iommu/irq_remapping.c | 1 |
7 files changed, 94 insertions, 73 deletions
diff --git a/arch/x86/Kconfig b/arch/x86/Kconfig index e75a96c38c58..26e066579637 100644 --- a/arch/x86/Kconfig +++ b/arch/x86/Kconfig | |||
@@ -914,6 +914,7 @@ config X86_LOCAL_APIC | |||
914 | depends on X86_64 || SMP || X86_32_NON_STANDARD || X86_UP_APIC || PCI_MSI | 914 | depends on X86_64 || SMP || X86_32_NON_STANDARD || X86_UP_APIC || PCI_MSI |
915 | select GENERIC_IRQ_LEGACY_ALLOC_HWIRQ | 915 | select GENERIC_IRQ_LEGACY_ALLOC_HWIRQ |
916 | select IRQ_DOMAIN_HIERARCHY | 916 | select IRQ_DOMAIN_HIERARCHY |
917 | select PCI_MSI_IRQ_DOMAIN if PCI_MSI | ||
917 | 918 | ||
918 | config X86_IO_APIC | 919 | config X86_IO_APIC |
919 | def_bool y | 920 | def_bool y |
diff --git a/arch/x86/include/asm/hw_irq.h b/arch/x86/include/asm/hw_irq.h index 75a97a5bbfa8..05829e973a2a 100644 --- a/arch/x86/include/asm/hw_irq.h +++ b/arch/x86/include/asm/hw_irq.h | |||
@@ -110,9 +110,10 @@ struct irq_2_irte { | |||
110 | }; | 110 | }; |
111 | #endif /* CONFIG_IRQ_REMAP */ | 111 | #endif /* CONFIG_IRQ_REMAP */ |
112 | 112 | ||
113 | struct irq_domain; | ||
114 | |||
113 | #ifdef CONFIG_X86_LOCAL_APIC | 115 | #ifdef CONFIG_X86_LOCAL_APIC |
114 | struct irq_data; | 116 | struct irq_data; |
115 | struct irq_domain; | ||
116 | struct pci_dev; | 117 | struct pci_dev; |
117 | struct msi_desc; | 118 | struct msi_desc; |
118 | 119 | ||
@@ -214,6 +215,12 @@ static inline void lock_vector_lock(void) {} | |||
214 | static inline void unlock_vector_lock(void) {} | 215 | static inline void unlock_vector_lock(void) {} |
215 | #endif /* CONFIG_X86_LOCAL_APIC */ | 216 | #endif /* CONFIG_X86_LOCAL_APIC */ |
216 | 217 | ||
218 | #ifdef CONFIG_PCI_MSI | ||
219 | extern void arch_init_msi_domain(struct irq_domain *domain); | ||
220 | #else | ||
221 | static inline void arch_init_msi_domain(struct irq_domain *domain) { } | ||
222 | #endif | ||
223 | |||
217 | /* Statistics */ | 224 | /* Statistics */ |
218 | extern atomic_t irq_err_count; | 225 | extern atomic_t irq_err_count; |
219 | extern atomic_t irq_mis_count; | 226 | extern atomic_t irq_mis_count; |
diff --git a/arch/x86/include/asm/irq_remapping.h b/arch/x86/include/asm/irq_remapping.h index 0cd6195cc375..0d3bbd0e2c42 100644 --- a/arch/x86/include/asm/irq_remapping.h +++ b/arch/x86/include/asm/irq_remapping.h | |||
@@ -66,11 +66,7 @@ irq_remapping_get_irq_domain(struct irq_alloc_info *info); | |||
66 | extern void irq_remapping_print_chip(struct irq_data *data, struct seq_file *p); | 66 | extern void irq_remapping_print_chip(struct irq_data *data, struct seq_file *p); |
67 | 67 | ||
68 | /* Create PCI MSI/MSIx irqdomain, use @parent as the parent irqdomain. */ | 68 | /* Create PCI MSI/MSIx irqdomain, use @parent as the parent irqdomain. */ |
69 | static inline struct irq_domain * | 69 | extern struct irq_domain *arch_create_msi_irq_domain(struct irq_domain *parent); |
70 | arch_create_msi_irq_domain(struct irq_domain *parent) | ||
71 | { | ||
72 | return NULL; | ||
73 | } | ||
74 | 70 | ||
75 | /* Get parent irqdomain for interrupt remapping irqdomain */ | 71 | /* Get parent irqdomain for interrupt remapping irqdomain */ |
76 | static inline struct irq_domain *arch_get_ir_parent_domain(void) | 72 | static inline struct irq_domain *arch_get_ir_parent_domain(void) |
diff --git a/arch/x86/include/asm/msi.h b/arch/x86/include/asm/msi.h new file mode 100644 index 000000000000..93724cc62177 --- /dev/null +++ b/arch/x86/include/asm/msi.h | |||
@@ -0,0 +1,7 @@ | |||
1 | #ifndef _ASM_X86_MSI_H | ||
2 | #define _ASM_X86_MSI_H | ||
3 | #include <asm/hw_irq.h> | ||
4 | |||
5 | typedef struct irq_alloc_info msi_alloc_info_t; | ||
6 | |||
7 | #endif /* _ASM_X86_MSI_H */ | ||
diff --git a/arch/x86/kernel/apic/msi.c b/arch/x86/kernel/apic/msi.c index 10d9ae8f2166..c426cd58844e 100644 --- a/arch/x86/kernel/apic/msi.c +++ b/arch/x86/kernel/apic/msi.c | |||
@@ -3,6 +3,8 @@ | |||
3 | * | 3 | * |
4 | * Copyright (C) 1997, 1998, 1999, 2000, 2009 Ingo Molnar, Hajnalka Szabo | 4 | * Copyright (C) 1997, 1998, 1999, 2000, 2009 Ingo Molnar, Hajnalka Szabo |
5 | * Moved from arch/x86/kernel/apic/io_apic.c. | 5 | * Moved from arch/x86/kernel/apic/io_apic.c. |
6 | * Jiang Liu <jiang.liu@linux.intel.com> | ||
7 | * Convert to hierarchical irqdomain | ||
6 | * | 8 | * |
7 | * This program is free software; you can redistribute it and/or modify | 9 | * This program is free software; you can redistribute it and/or modify |
8 | * it under the terms of the GNU General Public License version 2 as | 10 | * it under the terms of the GNU General Public License version 2 as |
@@ -21,6 +23,8 @@ | |||
21 | #include <asm/apic.h> | 23 | #include <asm/apic.h> |
22 | #include <asm/irq_remapping.h> | 24 | #include <asm/irq_remapping.h> |
23 | 25 | ||
26 | static struct irq_domain *msi_default_domain; | ||
27 | |||
24 | void native_compose_msi_msg(struct pci_dev *pdev, | 28 | void native_compose_msi_msg(struct pci_dev *pdev, |
25 | unsigned int irq, unsigned int dest, | 29 | unsigned int irq, unsigned int dest, |
26 | struct msi_msg *msg, u8 hpet_id) | 30 | struct msi_msg *msg, u8 hpet_id) |
@@ -114,102 +118,107 @@ static int msi_compose_msg(struct pci_dev *pdev, unsigned int irq, | |||
114 | return 0; | 118 | return 0; |
115 | } | 119 | } |
116 | 120 | ||
117 | static int | ||
118 | msi_set_affinity(struct irq_data *data, const struct cpumask *mask, bool force) | ||
119 | { | ||
120 | struct irq_cfg *cfg = irqd_cfg(data); | ||
121 | struct msi_msg msg; | ||
122 | unsigned int dest; | ||
123 | int ret; | ||
124 | |||
125 | ret = apic_set_affinity(data, mask, &dest); | ||
126 | if (ret) | ||
127 | return ret; | ||
128 | |||
129 | __get_cached_msi_msg(data->msi_desc, &msg); | ||
130 | |||
131 | msg.data &= ~MSI_DATA_VECTOR_MASK; | ||
132 | msg.data |= MSI_DATA_VECTOR(cfg->vector); | ||
133 | msg.address_lo &= ~MSI_ADDR_DEST_ID_MASK; | ||
134 | msg.address_lo |= MSI_ADDR_DEST_ID(dest); | ||
135 | |||
136 | __pci_write_msi_msg(data->msi_desc, &msg); | ||
137 | |||
138 | return IRQ_SET_MASK_OK_NOCOPY; | ||
139 | } | ||
140 | |||
141 | /* | 121 | /* |
142 | * IRQ Chip for MSI PCI/PCI-X/PCI-Express Devices, | 122 | * IRQ Chip for MSI PCI/PCI-X/PCI-Express Devices, |
143 | * which implement the MSI or MSI-X Capability Structure. | 123 | * which implement the MSI or MSI-X Capability Structure. |
144 | */ | 124 | */ |
145 | static struct irq_chip msi_chip = { | 125 | static struct irq_chip pci_msi_controller = { |
146 | .name = "PCI-MSI", | 126 | .name = "PCI-MSI", |
147 | .irq_unmask = pci_msi_unmask_irq, | 127 | .irq_unmask = pci_msi_unmask_irq, |
148 | .irq_mask = pci_msi_mask_irq, | 128 | .irq_mask = pci_msi_mask_irq, |
149 | .irq_ack = apic_ack_edge, | 129 | .irq_ack = irq_chip_ack_parent, |
150 | .irq_set_affinity = msi_set_affinity, | 130 | .irq_set_affinity = msi_domain_set_affinity, |
151 | .irq_retrigger = apic_retrigger_irq, | 131 | .irq_retrigger = irq_chip_retrigger_hierarchy, |
132 | .irq_print_chip = irq_remapping_print_chip, | ||
133 | .irq_compose_msi_msg = irq_msi_compose_msg, | ||
134 | .irq_write_msi_msg = pci_msi_domain_write_msg, | ||
152 | .flags = IRQCHIP_SKIP_SET_WAKE, | 135 | .flags = IRQCHIP_SKIP_SET_WAKE, |
153 | }; | 136 | }; |
154 | 137 | ||
155 | int setup_msi_irq(struct pci_dev *dev, struct msi_desc *msidesc, | 138 | int native_setup_msi_irqs(struct pci_dev *dev, int nvec, int type) |
156 | unsigned int irq_base, unsigned int irq_offset) | ||
157 | { | 139 | { |
158 | struct irq_chip *chip = &msi_chip; | 140 | struct irq_domain *domain; |
159 | struct msi_msg msg; | 141 | struct irq_alloc_info info; |
160 | unsigned int irq = irq_base + irq_offset; | ||
161 | int ret; | ||
162 | 142 | ||
163 | ret = msi_compose_msg(dev, irq, &msg, -1); | 143 | init_irq_alloc_info(&info, NULL); |
164 | if (ret < 0) | 144 | info.type = X86_IRQ_ALLOC_TYPE_MSI; |
165 | return ret; | 145 | info.msi_dev = dev; |
166 | 146 | ||
167 | irq_set_msi_desc_off(irq_base, irq_offset, msidesc); | 147 | domain = irq_remapping_get_irq_domain(&info); |
148 | if (domain == NULL) | ||
149 | domain = msi_default_domain; | ||
150 | if (domain == NULL) | ||
151 | return -ENOSYS; | ||
168 | 152 | ||
169 | /* | 153 | return pci_msi_domain_alloc_irqs(domain, dev, nvec, type); |
170 | * MSI-X message is written per-IRQ, the offset is always 0. | 154 | } |
171 | * MSI message denotes a contiguous group of IRQs, written for 0th IRQ. | ||
172 | */ | ||
173 | if (!irq_offset) | ||
174 | pci_write_msi_msg(irq, &msg); | ||
175 | 155 | ||
176 | setup_remapped_irq(irq, irq_cfg(irq), chip); | 156 | void native_teardown_msi_irq(unsigned int irq) |
157 | { | ||
158 | irq_domain_free_irqs(irq, 1); | ||
159 | } | ||
177 | 160 | ||
178 | irq_set_chip_and_handler_name(irq, chip, handle_edge_irq, "edge"); | 161 | static irq_hw_number_t pci_msi_get_hwirq(struct msi_domain_info *info, |
162 | msi_alloc_info_t *arg) | ||
163 | { | ||
164 | return arg->msi_hwirq; | ||
165 | } | ||
179 | 166 | ||
180 | dev_dbg(&dev->dev, "irq %d for MSI/MSI-X\n", irq); | 167 | static int pci_msi_prepare(struct irq_domain *domain, struct device *dev, |
168 | int nvec, msi_alloc_info_t *arg) | ||
169 | { | ||
170 | struct pci_dev *pdev = to_pci_dev(dev); | ||
171 | struct msi_desc *desc = first_pci_msi_entry(pdev); | ||
172 | |||
173 | init_irq_alloc_info(arg, NULL); | ||
174 | arg->msi_dev = pdev; | ||
175 | if (desc->msi_attrib.is_msix) { | ||
176 | arg->type = X86_IRQ_ALLOC_TYPE_MSIX; | ||
177 | } else { | ||
178 | arg->type = X86_IRQ_ALLOC_TYPE_MSI; | ||
179 | arg->flags |= X86_IRQ_ALLOC_CONTIGUOUS_VECTORS; | ||
180 | } | ||
181 | 181 | ||
182 | return 0; | 182 | return 0; |
183 | } | 183 | } |
184 | 184 | ||
185 | int native_setup_msi_irqs(struct pci_dev *dev, int nvec, int type) | 185 | static void pci_msi_set_desc(msi_alloc_info_t *arg, struct msi_desc *desc) |
186 | { | 186 | { |
187 | struct msi_desc *msidesc; | 187 | arg->msi_hwirq = pci_msi_domain_calc_hwirq(arg->msi_dev, desc); |
188 | int irq, ret; | 188 | } |
189 | 189 | ||
190 | /* Multiple MSI vectors only supported with interrupt remapping */ | 190 | static struct msi_domain_ops pci_msi_domain_ops = { |
191 | if (type == PCI_CAP_ID_MSI && nvec > 1) | 191 | .get_hwirq = pci_msi_get_hwirq, |
192 | return 1; | 192 | .msi_prepare = pci_msi_prepare, |
193 | .set_desc = pci_msi_set_desc, | ||
194 | }; | ||
193 | 195 | ||
194 | list_for_each_entry(msidesc, &dev->msi_list, list) { | 196 | static struct msi_domain_info pci_msi_domain_info = { |
195 | irq = irq_domain_alloc_irqs(NULL, 1, NUMA_NO_NODE, NULL); | 197 | .flags = MSI_FLAG_USE_DEF_DOM_OPS | MSI_FLAG_USE_DEF_CHIP_OPS | |
196 | if (irq <= 0) | 198 | MSI_FLAG_MULTI_PCI_MSI | MSI_FLAG_PCI_MSIX, |
197 | return -ENOSPC; | 199 | .ops = &pci_msi_domain_ops, |
200 | .chip = &pci_msi_controller, | ||
201 | .handler = handle_edge_irq, | ||
202 | .handler_name = "edge", | ||
203 | }; | ||
198 | 204 | ||
199 | ret = setup_msi_irq(dev, msidesc, irq, 0); | 205 | void arch_init_msi_domain(struct irq_domain *parent) |
200 | if (ret < 0) { | 206 | { |
201 | irq_domain_free_irqs(irq, 1); | 207 | if (disable_apic) |
202 | return ret; | 208 | return; |
203 | } | ||
204 | 209 | ||
205 | } | 210 | msi_default_domain = pci_msi_create_irq_domain(NULL, |
206 | return 0; | 211 | &pci_msi_domain_info, parent); |
212 | if (!msi_default_domain) | ||
213 | pr_warn("failed to initialize irqdomain for MSI/MSI-x.\n"); | ||
207 | } | 214 | } |
208 | 215 | ||
209 | void native_teardown_msi_irq(unsigned int irq) | 216 | #ifdef CONFIG_IRQ_REMAP |
217 | struct irq_domain *arch_create_msi_irq_domain(struct irq_domain *parent) | ||
210 | { | 218 | { |
211 | irq_domain_free_irqs(irq, 1); | 219 | return msi_create_irq_domain(NULL, &pci_msi_domain_info, parent); |
212 | } | 220 | } |
221 | #endif | ||
213 | 222 | ||
214 | #ifdef CONFIG_DMAR_TABLE | 223 | #ifdef CONFIG_DMAR_TABLE |
215 | static int | 224 | static int |
diff --git a/arch/x86/kernel/apic/vector.c b/arch/x86/kernel/apic/vector.c index 6358d8d351f5..a8d82896be75 100644 --- a/arch/x86/kernel/apic/vector.c +++ b/arch/x86/kernel/apic/vector.c | |||
@@ -364,6 +364,8 @@ int __init arch_early_irq_init(void) | |||
364 | BUG_ON(x86_vector_domain == NULL); | 364 | BUG_ON(x86_vector_domain == NULL); |
365 | irq_set_default_host(x86_vector_domain); | 365 | irq_set_default_host(x86_vector_domain); |
366 | 366 | ||
367 | arch_init_msi_domain(x86_vector_domain); | ||
368 | |||
367 | return arch_early_ioapic_init(); | 369 | return arch_early_ioapic_init(); |
368 | } | 370 | } |
369 | 371 | ||
diff --git a/drivers/iommu/irq_remapping.c b/drivers/iommu/irq_remapping.c index c306421d86c1..d77e3711c2aa 100644 --- a/drivers/iommu/irq_remapping.c +++ b/drivers/iommu/irq_remapping.c | |||
@@ -170,7 +170,6 @@ static void __init irq_remapping_modify_x86_ops(void) | |||
170 | x86_io_apic_ops.set_affinity = set_remapped_irq_affinity; | 170 | x86_io_apic_ops.set_affinity = set_remapped_irq_affinity; |
171 | x86_io_apic_ops.setup_entry = setup_ioapic_remapped_entry; | 171 | x86_io_apic_ops.setup_entry = setup_ioapic_remapped_entry; |
172 | x86_io_apic_ops.eoi_ioapic_pin = eoi_ioapic_pin_remapped; | 172 | x86_io_apic_ops.eoi_ioapic_pin = eoi_ioapic_pin_remapped; |
173 | x86_msi.setup_msi_irqs = irq_remapping_setup_msi_irqs; | ||
174 | x86_msi.setup_hpet_msi = setup_hpet_msi_remapped; | 173 | x86_msi.setup_hpet_msi = setup_hpet_msi_remapped; |
175 | x86_msi.compose_msi_msg = compose_remapped_msi_msg; | 174 | x86_msi.compose_msi_msg = compose_remapped_msi_msg; |
176 | } | 175 | } |