diff options
Diffstat (limited to 'drivers/xen/pci.c')
| -rw-r--r-- | drivers/xen/pci.c | 105 | 
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 @@   */  #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) { +			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 -	if (pci_dev->is_virtfn) { +	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;  } @@ -96,13 +170,16 @@ static int xen_pci_notifier(struct notifier_block *nb,  		r = xen_remove_device(dev);  		break;  	default: -		break; +		return NOTIFY_DONE;  	} - -	return r; +	if (r) +		dev_err(dev, "Failed to %s - passthrough or MSI/MSI-X might fail!\n", +			action == BUS_NOTIFY_ADD_DEVICE ? "add" : +			(action == BUS_NOTIFY_DEL_DEVICE ? "delete" : "?")); +	return NOTIFY_OK;  } -struct notifier_block device_nb = { +static struct notifier_block device_nb = {  	.notifier_call = xen_pci_notifier,  }; | 
