aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorRoger Pau Monne <roger.pau@citrix.com>2014-02-27 13:15:35 -0500
committerDavid Vrabel <david.vrabel@citrix.com>2014-03-18 10:40:09 -0400
commit4892c9b4ada9f9a71a0da7a268f95e988d88064b (patch)
tree57c9f627ab44ebec2a948dc11ca69cb0011607de
parentefdfa3eda5d7b33c6e3c4f6e66144fcd10ed41a3 (diff)
xen: add support for MSI message groups
Add support for MSI message groups for Xen Dom0 using the MAP_PIRQ_TYPE_MULTI_MSI pirq map type. In order to keep track of which pirq is the first one in the group all pirqs in the MSI group except for the first one have the newly introduced PIRQ_MSI_GROUP flag set. This prevents calling PHYSDEVOP_unmap_pirq on them, since the unmap must be done with the first pirq in the group. Signed-off-by: Roger Pau Monné <roger.pau@citrix.com> Signed-off-by: David Vrabel <david.vrabel@citrix.com> Cc: Boris Ostrovsky <boris.ostrovsky@oracle.com>
-rw-r--r--arch/x86/pci/xen.c29
-rw-r--r--drivers/xen/events/events_base.c47
-rw-r--r--drivers/xen/events/events_internal.h1
-rw-r--r--include/xen/events.h5
-rw-r--r--include/xen/interface/physdev.h10
5 files changed, 65 insertions, 27 deletions
diff --git a/arch/x86/pci/xen.c b/arch/x86/pci/xen.c
index 103e702ec5a7..905956f16465 100644
--- a/arch/x86/pci/xen.c
+++ b/arch/x86/pci/xen.c
@@ -178,6 +178,7 @@ static int xen_setup_msi_irqs(struct pci_dev *dev, int nvec, int type)
178 i = 0; 178 i = 0;
179 list_for_each_entry(msidesc, &dev->msi_list, list) { 179 list_for_each_entry(msidesc, &dev->msi_list, list) {
180 irq = xen_bind_pirq_msi_to_irq(dev, msidesc, v[i], 180 irq = xen_bind_pirq_msi_to_irq(dev, msidesc, v[i],
181 (type == PCI_CAP_ID_MSI) ? nvec : 1,
181 (type == PCI_CAP_ID_MSIX) ? 182 (type == PCI_CAP_ID_MSIX) ?
182 "pcifront-msi-x" : 183 "pcifront-msi-x" :
183 "pcifront-msi", 184 "pcifront-msi",
@@ -245,6 +246,7 @@ static int xen_hvm_setup_msi_irqs(struct pci_dev *dev, int nvec, int type)
245 "xen: msi already bound to pirq=%d\n", pirq); 246 "xen: msi already bound to pirq=%d\n", pirq);
246 } 247 }
247 irq = xen_bind_pirq_msi_to_irq(dev, msidesc, pirq, 248 irq = xen_bind_pirq_msi_to_irq(dev, msidesc, pirq,
249 (type == PCI_CAP_ID_MSI) ? nvec : 1,
248 (type == PCI_CAP_ID_MSIX) ? 250 (type == PCI_CAP_ID_MSIX) ?
249 "msi-x" : "msi", 251 "msi-x" : "msi",
250 DOMID_SELF); 252 DOMID_SELF);
@@ -269,9 +271,6 @@ static int xen_initdom_setup_msi_irqs(struct pci_dev *dev, int nvec, int type)
269 int ret = 0; 271 int ret = 0;
270 struct msi_desc *msidesc; 272 struct msi_desc *msidesc;
271 273
272 if (type == PCI_CAP_ID_MSI && nvec > 1)
273 return 1;
274
275 list_for_each_entry(msidesc, &dev->msi_list, list) { 274 list_for_each_entry(msidesc, &dev->msi_list, list) {
276 struct physdev_map_pirq map_irq; 275 struct physdev_map_pirq map_irq;
277 domid_t domid; 276 domid_t domid;
@@ -291,7 +290,10 @@ static int xen_initdom_setup_msi_irqs(struct pci_dev *dev, int nvec, int type)
291 (pci_domain_nr(dev->bus) << 16); 290 (pci_domain_nr(dev->bus) << 16);
292 map_irq.devfn = dev->devfn; 291 map_irq.devfn = dev->devfn;
293 292
294 if (type == PCI_CAP_ID_MSIX) { 293 if (type == PCI_CAP_ID_MSI && nvec > 1) {
294 map_irq.type = MAP_PIRQ_TYPE_MULTI_MSI;
295 map_irq.entry_nr = nvec;
296 } else if (type == PCI_CAP_ID_MSIX) {
295 int pos; 297 int pos;
296 u32 table_offset, bir; 298 u32 table_offset, bir;
297 299
@@ -308,6 +310,16 @@ static int xen_initdom_setup_msi_irqs(struct pci_dev *dev, int nvec, int type)
308 if (pci_seg_supported) 310 if (pci_seg_supported)
309 ret = HYPERVISOR_physdev_op(PHYSDEVOP_map_pirq, 311 ret = HYPERVISOR_physdev_op(PHYSDEVOP_map_pirq,
310 &map_irq); 312 &map_irq);
313 if (type == PCI_CAP_ID_MSI && nvec > 1 && ret) {
314 /*
315 * If MAP_PIRQ_TYPE_MULTI_MSI is not available
316 * there's nothing else we can do in this case.
317 * Just set ret > 0 so driver can retry with
318 * single MSI.
319 */
320 ret = 1;
321 goto out;
322 }
311 if (ret == -EINVAL && !pci_domain_nr(dev->bus)) { 323 if (ret == -EINVAL && !pci_domain_nr(dev->bus)) {
312 map_irq.type = MAP_PIRQ_TYPE_MSI; 324 map_irq.type = MAP_PIRQ_TYPE_MSI;
313 map_irq.index = -1; 325 map_irq.index = -1;
@@ -324,11 +336,10 @@ static int xen_initdom_setup_msi_irqs(struct pci_dev *dev, int nvec, int type)
324 goto out; 336 goto out;
325 } 337 }
326 338
327 ret = xen_bind_pirq_msi_to_irq(dev, msidesc, 339 ret = xen_bind_pirq_msi_to_irq(dev, msidesc, map_irq.pirq,
328 map_irq.pirq, 340 (type == PCI_CAP_ID_MSI) ? nvec : 1,
329 (type == PCI_CAP_ID_MSIX) ? 341 (type == PCI_CAP_ID_MSIX) ? "msi-x" : "msi",
330 "msi-x" : "msi", 342 domid);
331 domid);
332 if (ret < 0) 343 if (ret < 0)
333 goto out; 344 goto out;
334 } 345 }
diff --git a/drivers/xen/events/events_base.c b/drivers/xen/events/events_base.c
index 9875d6ec1063..793053065629 100644
--- a/drivers/xen/events/events_base.c
+++ b/drivers/xen/events/events_base.c
@@ -391,10 +391,10 @@ static void xen_irq_init(unsigned irq)
391 list_add_tail(&info->list, &xen_irq_list_head); 391 list_add_tail(&info->list, &xen_irq_list_head);
392} 392}
393 393
394static int __must_check xen_allocate_irq_dynamic(void) 394static int __must_check xen_allocate_irqs_dynamic(int nvec)
395{ 395{
396 int first = 0; 396 int first = 0;
397 int irq; 397 int i, irq;
398 398
399#ifdef CONFIG_X86_IO_APIC 399#ifdef CONFIG_X86_IO_APIC
400 /* 400 /*
@@ -408,14 +408,22 @@ static int __must_check xen_allocate_irq_dynamic(void)
408 first = get_nr_irqs_gsi(); 408 first = get_nr_irqs_gsi();
409#endif 409#endif
410 410
411 irq = irq_alloc_desc_from(first, -1); 411 irq = irq_alloc_descs_from(first, nvec, -1);
412 412
413 if (irq >= 0) 413 if (irq >= 0) {
414 xen_irq_init(irq); 414 for (i = 0; i < nvec; i++)
415 xen_irq_init(irq + i);
416 }
415 417
416 return irq; 418 return irq;
417} 419}
418 420
421static inline int __must_check xen_allocate_irq_dynamic(void)
422{
423
424 return xen_allocate_irqs_dynamic(1);
425}
426
419static int __must_check xen_allocate_irq_gsi(unsigned gsi) 427static int __must_check xen_allocate_irq_gsi(unsigned gsi)
420{ 428{
421 int irq; 429 int irq;
@@ -738,22 +746,25 @@ int xen_allocate_pirq_msi(struct pci_dev *dev, struct msi_desc *msidesc)
738} 746}
739 747
740int xen_bind_pirq_msi_to_irq(struct pci_dev *dev, struct msi_desc *msidesc, 748int xen_bind_pirq_msi_to_irq(struct pci_dev *dev, struct msi_desc *msidesc,
741 int pirq, const char *name, domid_t domid) 749 int pirq, int nvec, const char *name, domid_t domid)
742{ 750{
743 int irq, ret; 751 int i, irq, ret;
744 752
745 mutex_lock(&irq_mapping_update_lock); 753 mutex_lock(&irq_mapping_update_lock);
746 754
747 irq = xen_allocate_irq_dynamic(); 755 irq = xen_allocate_irqs_dynamic(nvec);
748 if (irq < 0) 756 if (irq < 0)
749 goto out; 757 goto out;
750 758
751 irq_set_chip_and_handler_name(irq, &xen_pirq_chip, handle_edge_irq, 759 for (i = 0; i < nvec; i++) {
752 name); 760 irq_set_chip_and_handler_name(irq + i, &xen_pirq_chip, handle_edge_irq, name);
761
762 ret = xen_irq_info_pirq_setup(irq + i, 0, pirq + i, 0, domid,
763 i == 0 ? 0 : PIRQ_MSI_GROUP);
764 if (ret < 0)
765 goto error_irq;
766 }
753 767
754 ret = xen_irq_info_pirq_setup(irq, 0, pirq, 0, domid, 0);
755 if (ret < 0)
756 goto error_irq;
757 ret = irq_set_msi_desc(irq, msidesc); 768 ret = irq_set_msi_desc(irq, msidesc);
758 if (ret < 0) 769 if (ret < 0)
759 goto error_irq; 770 goto error_irq;
@@ -761,7 +772,8 @@ out:
761 mutex_unlock(&irq_mapping_update_lock); 772 mutex_unlock(&irq_mapping_update_lock);
762 return irq; 773 return irq;
763error_irq: 774error_irq:
764 __unbind_from_irq(irq); 775 for (; i >= 0; i--)
776 __unbind_from_irq(irq + i);
765 mutex_unlock(&irq_mapping_update_lock); 777 mutex_unlock(&irq_mapping_update_lock);
766 return ret; 778 return ret;
767} 779}
@@ -780,7 +792,12 @@ int xen_destroy_irq(int irq)
780 if (!desc) 792 if (!desc)
781 goto out; 793 goto out;
782 794
783 if (xen_initial_domain()) { 795 /*
796 * If trying to remove a vector in a MSI group different
797 * than the first one skip the PIRQ unmap unless this vector
798 * is the first one in the group.
799 */
800 if (xen_initial_domain() && !(info->u.pirq.flags & PIRQ_MSI_GROUP)) {
784 unmap_irq.pirq = info->u.pirq.pirq; 801 unmap_irq.pirq = info->u.pirq.pirq;
785 unmap_irq.domid = info->u.pirq.domid; 802 unmap_irq.domid = info->u.pirq.domid;
786 rc = HYPERVISOR_physdev_op(PHYSDEVOP_unmap_pirq, &unmap_irq); 803 rc = HYPERVISOR_physdev_op(PHYSDEVOP_unmap_pirq, &unmap_irq);
diff --git a/drivers/xen/events/events_internal.h b/drivers/xen/events/events_internal.h
index 677f41a0fff9..50c2050a1e32 100644
--- a/drivers/xen/events/events_internal.h
+++ b/drivers/xen/events/events_internal.h
@@ -53,6 +53,7 @@ struct irq_info {
53 53
54#define PIRQ_NEEDS_EOI (1 << 0) 54#define PIRQ_NEEDS_EOI (1 << 0)
55#define PIRQ_SHAREABLE (1 << 1) 55#define PIRQ_SHAREABLE (1 << 1)
56#define PIRQ_MSI_GROUP (1 << 2)
56 57
57struct evtchn_ops { 58struct evtchn_ops {
58 unsigned (*max_channels)(void); 59 unsigned (*max_channels)(void);
diff --git a/include/xen/events.h b/include/xen/events.h
index a6d92378354c..8bee7a75e850 100644
--- a/include/xen/events.h
+++ b/include/xen/events.h
@@ -2,6 +2,9 @@
2#define _XEN_EVENTS_H 2#define _XEN_EVENTS_H
3 3
4#include <linux/interrupt.h> 4#include <linux/interrupt.h>
5#ifdef CONFIG_PCI_MSI
6#include <linux/msi.h>
7#endif
5 8
6#include <xen/interface/event_channel.h> 9#include <xen/interface/event_channel.h>
7#include <asm/xen/hypercall.h> 10#include <asm/xen/hypercall.h>
@@ -101,7 +104,7 @@ int xen_bind_pirq_gsi_to_irq(unsigned gsi,
101int xen_allocate_pirq_msi(struct pci_dev *dev, struct msi_desc *msidesc); 104int xen_allocate_pirq_msi(struct pci_dev *dev, struct msi_desc *msidesc);
102/* Bind an PSI pirq to an irq. */ 105/* Bind an PSI pirq to an irq. */
103int xen_bind_pirq_msi_to_irq(struct pci_dev *dev, struct msi_desc *msidesc, 106int xen_bind_pirq_msi_to_irq(struct pci_dev *dev, struct msi_desc *msidesc,
104 int pirq, const char *name, domid_t domid); 107 int pirq, int nvec, const char *name, domid_t domid);
105#endif 108#endif
106 109
107/* De-allocates the above mentioned physical interrupt. */ 110/* De-allocates the above mentioned physical interrupt. */
diff --git a/include/xen/interface/physdev.h b/include/xen/interface/physdev.h
index 42721d13a106..610dba9b620a 100644
--- a/include/xen/interface/physdev.h
+++ b/include/xen/interface/physdev.h
@@ -131,6 +131,7 @@ struct physdev_irq {
131#define MAP_PIRQ_TYPE_GSI 0x1 131#define MAP_PIRQ_TYPE_GSI 0x1
132#define MAP_PIRQ_TYPE_UNKNOWN 0x2 132#define MAP_PIRQ_TYPE_UNKNOWN 0x2
133#define MAP_PIRQ_TYPE_MSI_SEG 0x3 133#define MAP_PIRQ_TYPE_MSI_SEG 0x3
134#define MAP_PIRQ_TYPE_MULTI_MSI 0x4
134 135
135#define PHYSDEVOP_map_pirq 13 136#define PHYSDEVOP_map_pirq 13
136struct physdev_map_pirq { 137struct physdev_map_pirq {
@@ -141,11 +142,16 @@ struct physdev_map_pirq {
141 int index; 142 int index;
142 /* IN or OUT */ 143 /* IN or OUT */
143 int pirq; 144 int pirq;
144 /* IN - high 16 bits hold segment for MAP_PIRQ_TYPE_MSI_SEG */ 145 /* IN - high 16 bits hold segment for ..._MSI_SEG and ..._MULTI_MSI */
145 int bus; 146 int bus;
146 /* IN */ 147 /* IN */
147 int devfn; 148 int devfn;
148 /* IN */ 149 /* IN
150 * - For MSI-X contains entry number.
151 * - For MSI with ..._MULTI_MSI contains number of vectors.
152 * OUT (..._MULTI_MSI only)
153 * - Number of vectors allocated.
154 */
149 int entry_nr; 155 int entry_nr;
150 /* IN */ 156 /* IN */
151 uint64_t table_base; 157 uint64_t table_base;