|
@@ -18,6 +18,7 @@
|
|
|
*/
|
|
|
|
|
|
#include <linux/pci.h>
|
|
|
+#include <linux/acpi.h>
|
|
|
#include <xen/xen.h>
|
|
|
#include <xen/interface/physdev.h>
|
|
|
#include <xen/interface/xen.h>
|
|
@@ -26,26 +27,85 @@
|
|
|
#include <asm/xen/hypercall.h>
|
|
|
#include "../pci/pci.h"
|
|
|
|
|
|
+static bool __read_mostly pci_seg_supported = true;
|
|
|
+
|
|
|
static int xen_add_device(struct device *dev)
|
|
|
{
|
|
|
int r;
|
|
|
struct pci_dev *pci_dev = to_pci_dev(dev);
|
|
|
+#ifdef CONFIG_PCI_IOV
|
|
|
+ struct pci_dev *physfn = pci_dev->physfn;
|
|
|
+#endif
|
|
|
+
|
|
|
+ if (pci_seg_supported) {
|
|
|
+ struct physdev_pci_device_add add = {
|
|
|
+ .seg = pci_domain_nr(pci_dev->bus),
|
|
|
+ .bus = pci_dev->bus->number,
|
|
|
+ .devfn = pci_dev->devfn
|
|
|
+ };
|
|
|
+#ifdef CONFIG_ACPI
|
|
|
+ acpi_handle handle;
|
|
|
+#endif
|
|
|
|
|
|
#ifdef CONFIG_PCI_IOV
|
|
|
- if (pci_dev->is_virtfn) {
|
|
|
+ if (pci_dev->is_virtfn) {
|
|
|
+ add.flags = XEN_PCI_DEV_VIRTFN;
|
|
|
+ add.physfn.bus = physfn->bus->number;
|
|
|
+ add.physfn.devfn = physfn->devfn;
|
|
|
+ } else
|
|
|
+#endif
|
|
|
+ if (pci_ari_enabled(pci_dev->bus) && PCI_SLOT(pci_dev->devfn))
|
|
|
+ add.flags = XEN_PCI_DEV_EXTFN;
|
|
|
+
|
|
|
+#ifdef CONFIG_ACPI
|
|
|
+ handle = DEVICE_ACPI_HANDLE(&pci_dev->dev);
|
|
|
+ if (!handle)
|
|
|
+ handle = DEVICE_ACPI_HANDLE(pci_dev->bus->bridge);
|
|
|
+#ifdef CONFIG_PCI_IOV
|
|
|
+ if (!handle && pci_dev->is_virtfn)
|
|
|
+ handle = DEVICE_ACPI_HANDLE(physfn->bus->bridge);
|
|
|
+#endif
|
|
|
+ if (handle) {
|
|
|
+ acpi_status status;
|
|
|
+
|
|
|
+ do {
|
|
|
+ unsigned long long pxm;
|
|
|
+
|
|
|
+ status = acpi_evaluate_integer(handle, "_PXM",
|
|
|
+ NULL, &pxm);
|
|
|
+ if (ACPI_SUCCESS(status)) {
|
|
|
+ add.optarr[0] = pxm;
|
|
|
+ add.flags |= XEN_PCI_DEV_PXM;
|
|
|
+ break;
|
|
|
+ }
|
|
|
+ status = acpi_get_parent(handle, &handle);
|
|
|
+ } while (ACPI_SUCCESS(status));
|
|
|
+ }
|
|
|
+#endif /* CONFIG_ACPI */
|
|
|
+
|
|
|
+ r = HYPERVISOR_physdev_op(PHYSDEVOP_pci_device_add, &add);
|
|
|
+ if (r != -ENOSYS)
|
|
|
+ return r;
|
|
|
+ pci_seg_supported = false;
|
|
|
+ }
|
|
|
+
|
|
|
+ if (pci_domain_nr(pci_dev->bus))
|
|
|
+ r = -ENOSYS;
|
|
|
+#ifdef CONFIG_PCI_IOV
|
|
|
+ else if (pci_dev->is_virtfn) {
|
|
|
struct physdev_manage_pci_ext manage_pci_ext = {
|
|
|
.bus = pci_dev->bus->number,
|
|
|
.devfn = pci_dev->devfn,
|
|
|
.is_virtfn = 1,
|
|
|
- .physfn.bus = pci_dev->physfn->bus->number,
|
|
|
- .physfn.devfn = pci_dev->physfn->devfn,
|
|
|
+ .physfn.bus = physfn->bus->number,
|
|
|
+ .physfn.devfn = physfn->devfn,
|
|
|
};
|
|
|
|
|
|
r = HYPERVISOR_physdev_op(PHYSDEVOP_manage_pci_add_ext,
|
|
|
&manage_pci_ext);
|
|
|
- } else
|
|
|
+ }
|
|
|
#endif
|
|
|
- if (pci_ari_enabled(pci_dev->bus) && PCI_SLOT(pci_dev->devfn)) {
|
|
|
+ else if (pci_ari_enabled(pci_dev->bus) && PCI_SLOT(pci_dev->devfn)) {
|
|
|
struct physdev_manage_pci_ext manage_pci_ext = {
|
|
|
.bus = pci_dev->bus->number,
|
|
|
.devfn = pci_dev->devfn,
|
|
@@ -71,13 +131,27 @@ static int xen_remove_device(struct device *dev)
|
|
|
{
|
|
|
int r;
|
|
|
struct pci_dev *pci_dev = to_pci_dev(dev);
|
|
|
- struct physdev_manage_pci manage_pci;
|
|
|
|
|
|
- manage_pci.bus = pci_dev->bus->number;
|
|
|
- manage_pci.devfn = pci_dev->devfn;
|
|
|
+ if (pci_seg_supported) {
|
|
|
+ struct physdev_pci_device device = {
|
|
|
+ .seg = pci_domain_nr(pci_dev->bus),
|
|
|
+ .bus = pci_dev->bus->number,
|
|
|
+ .devfn = pci_dev->devfn
|
|
|
+ };
|
|
|
|
|
|
- r = HYPERVISOR_physdev_op(PHYSDEVOP_manage_pci_remove,
|
|
|
- &manage_pci);
|
|
|
+ r = HYPERVISOR_physdev_op(PHYSDEVOP_pci_device_remove,
|
|
|
+ &device);
|
|
|
+ } else if (pci_domain_nr(pci_dev->bus))
|
|
|
+ r = -ENOSYS;
|
|
|
+ else {
|
|
|
+ struct physdev_manage_pci manage_pci = {
|
|
|
+ .bus = pci_dev->bus->number,
|
|
|
+ .devfn = pci_dev->devfn
|
|
|
+ };
|
|
|
+
|
|
|
+ r = HYPERVISOR_physdev_op(PHYSDEVOP_manage_pci_remove,
|
|
|
+ &manage_pci);
|
|
|
+ }
|
|
|
|
|
|
return r;
|
|
|
}
|