aboutsummaryrefslogtreecommitdiffstats
path: root/drivers/xen/pci.c
diff options
context:
space:
mode:
Diffstat (limited to 'drivers/xen/pci.c')
-rw-r--r--drivers/xen/pci.c105
1 files changed, 91 insertions, 14 deletions
diff --git a/drivers/xen/pci.c b/drivers/xen/pci.c
index cef4bafc07dc..66057075d6e2 100644
--- a/drivers/xen/pci.c
+++ b/drivers/xen/pci.c
@@ -18,6 +18,7 @@
18 */ 18 */
19 19
20#include <linux/pci.h> 20#include <linux/pci.h>
21#include <linux/acpi.h>
21#include <xen/xen.h> 22#include <xen/xen.h>
22#include <xen/interface/physdev.h> 23#include <xen/interface/physdev.h>
23#include <xen/interface/xen.h> 24#include <xen/interface/xen.h>
@@ -26,26 +27,85 @@
26#include <asm/xen/hypercall.h> 27#include <asm/xen/hypercall.h>
27#include "../pci/pci.h" 28#include "../pci/pci.h"
28 29
30static bool __read_mostly pci_seg_supported = true;
31
29static int xen_add_device(struct device *dev) 32static int xen_add_device(struct device *dev)
30{ 33{
31 int r; 34 int r;
32 struct pci_dev *pci_dev = to_pci_dev(dev); 35 struct pci_dev *pci_dev = to_pci_dev(dev);
36#ifdef CONFIG_PCI_IOV
37 struct pci_dev *physfn = pci_dev->physfn;
38#endif
39
40 if (pci_seg_supported) {
41 struct physdev_pci_device_add add = {
42 .seg = pci_domain_nr(pci_dev->bus),
43 .bus = pci_dev->bus->number,
44 .devfn = pci_dev->devfn
45 };
46#ifdef CONFIG_ACPI
47 acpi_handle handle;
48#endif
49
50#ifdef CONFIG_PCI_IOV
51 if (pci_dev->is_virtfn) {
52 add.flags = XEN_PCI_DEV_VIRTFN;
53 add.physfn.bus = physfn->bus->number;
54 add.physfn.devfn = physfn->devfn;
55 } else
56#endif
57 if (pci_ari_enabled(pci_dev->bus) && PCI_SLOT(pci_dev->devfn))
58 add.flags = XEN_PCI_DEV_EXTFN;
59
60#ifdef CONFIG_ACPI
61 handle = DEVICE_ACPI_HANDLE(&pci_dev->dev);
62 if (!handle)
63 handle = DEVICE_ACPI_HANDLE(pci_dev->bus->bridge);
64#ifdef CONFIG_PCI_IOV
65 if (!handle && pci_dev->is_virtfn)
66 handle = DEVICE_ACPI_HANDLE(physfn->bus->bridge);
67#endif
68 if (handle) {
69 acpi_status status;
70
71 do {
72 unsigned long long pxm;
73
74 status = acpi_evaluate_integer(handle, "_PXM",
75 NULL, &pxm);
76 if (ACPI_SUCCESS(status)) {
77 add.optarr[0] = pxm;
78 add.flags |= XEN_PCI_DEV_PXM;
79 break;
80 }
81 status = acpi_get_parent(handle, &handle);
82 } while (ACPI_SUCCESS(status));
83 }
84#endif /* CONFIG_ACPI */
85
86 r = HYPERVISOR_physdev_op(PHYSDEVOP_pci_device_add, &add);
87 if (r != -ENOSYS)
88 return r;
89 pci_seg_supported = false;
90 }
33 91
92 if (pci_domain_nr(pci_dev->bus))
93 r = -ENOSYS;
34#ifdef CONFIG_PCI_IOV 94#ifdef CONFIG_PCI_IOV
35 if (pci_dev->is_virtfn) { 95 else if (pci_dev->is_virtfn) {
36 struct physdev_manage_pci_ext manage_pci_ext = { 96 struct physdev_manage_pci_ext manage_pci_ext = {
37 .bus = pci_dev->bus->number, 97 .bus = pci_dev->bus->number,
38 .devfn = pci_dev->devfn, 98 .devfn = pci_dev->devfn,
39 .is_virtfn = 1, 99 .is_virtfn = 1,
40 .physfn.bus = pci_dev->physfn->bus->number, 100 .physfn.bus = physfn->bus->number,
41 .physfn.devfn = pci_dev->physfn->devfn, 101 .physfn.devfn = physfn->devfn,
42 }; 102 };
43 103
44 r = HYPERVISOR_physdev_op(PHYSDEVOP_manage_pci_add_ext, 104 r = HYPERVISOR_physdev_op(PHYSDEVOP_manage_pci_add_ext,
45 &manage_pci_ext); 105 &manage_pci_ext);
46 } else 106 }
47#endif 107#endif
48 if (pci_ari_enabled(pci_dev->bus) && PCI_SLOT(pci_dev->devfn)) { 108 else if (pci_ari_enabled(pci_dev->bus) && PCI_SLOT(pci_dev->devfn)) {
49 struct physdev_manage_pci_ext manage_pci_ext = { 109 struct physdev_manage_pci_ext manage_pci_ext = {
50 .bus = pci_dev->bus->number, 110 .bus = pci_dev->bus->number,
51 .devfn = pci_dev->devfn, 111 .devfn = pci_dev->devfn,
@@ -71,13 +131,27 @@ static int xen_remove_device(struct device *dev)
71{ 131{
72 int r; 132 int r;
73 struct pci_dev *pci_dev = to_pci_dev(dev); 133 struct pci_dev *pci_dev = to_pci_dev(dev);
74 struct physdev_manage_pci manage_pci;
75 134
76 manage_pci.bus = pci_dev->bus->number; 135 if (pci_seg_supported) {
77 manage_pci.devfn = pci_dev->devfn; 136 struct physdev_pci_device device = {
137 .seg = pci_domain_nr(pci_dev->bus),
138 .bus = pci_dev->bus->number,
139 .devfn = pci_dev->devfn
140 };
78 141
79 r = HYPERVISOR_physdev_op(PHYSDEVOP_manage_pci_remove, 142 r = HYPERVISOR_physdev_op(PHYSDEVOP_pci_device_remove,
80 &manage_pci); 143 &device);
144 } else if (pci_domain_nr(pci_dev->bus))
145 r = -ENOSYS;
146 else {
147 struct physdev_manage_pci manage_pci = {
148 .bus = pci_dev->bus->number,
149 .devfn = pci_dev->devfn
150 };
151
152 r = HYPERVISOR_physdev_op(PHYSDEVOP_manage_pci_remove,
153 &manage_pci);
154 }
81 155
82 return r; 156 return r;
83} 157}
@@ -96,13 +170,16 @@ static int xen_pci_notifier(struct notifier_block *nb,
96 r = xen_remove_device(dev); 170 r = xen_remove_device(dev);
97 break; 171 break;
98 default: 172 default:
99 break; 173 return NOTIFY_DONE;
100 } 174 }
101 175 if (r)
102 return r; 176 dev_err(dev, "Failed to %s - passthrough or MSI/MSI-X might fail!\n",
177 action == BUS_NOTIFY_ADD_DEVICE ? "add" :
178 (action == BUS_NOTIFY_DEL_DEVICE ? "delete" : "?"));
179 return NOTIFY_OK;
103} 180}
104 181
105struct notifier_block device_nb = { 182static struct notifier_block device_nb = {
106 .notifier_call = xen_pci_notifier, 183 .notifier_call = xen_pci_notifier,
107}; 184};
108 185