|
@@ -1518,6 +1518,53 @@ parse_error:
|
|
|
fs_initcall(pcistub_init);
|
|
|
#endif
|
|
|
|
|
|
+#ifdef CONFIG_PCI_IOV
|
|
|
+static struct pcistub_device *find_vfs(const struct pci_dev *pdev)
|
|
|
+{
|
|
|
+ struct pcistub_device *psdev = NULL;
|
|
|
+ unsigned long flags;
|
|
|
+ bool found = false;
|
|
|
+
|
|
|
+ spin_lock_irqsave(&pcistub_devices_lock, flags);
|
|
|
+ list_for_each_entry(psdev, &pcistub_devices, dev_list) {
|
|
|
+ if (!psdev->pdev && psdev->dev != pdev
|
|
|
+ && pci_physfn(psdev->dev) == pdev) {
|
|
|
+ found = true;
|
|
|
+ break;
|
|
|
+ }
|
|
|
+ }
|
|
|
+ spin_unlock_irqrestore(&pcistub_devices_lock, flags);
|
|
|
+ if (found)
|
|
|
+ return psdev;
|
|
|
+ return NULL;
|
|
|
+}
|
|
|
+
|
|
|
+static int pci_stub_notifier(struct notifier_block *nb,
|
|
|
+ unsigned long action, void *data)
|
|
|
+{
|
|
|
+ struct device *dev = data;
|
|
|
+ const struct pci_dev *pdev = to_pci_dev(dev);
|
|
|
+
|
|
|
+ if (action != BUS_NOTIFY_UNBIND_DRIVER)
|
|
|
+ return NOTIFY_DONE;
|
|
|
+
|
|
|
+ if (!pdev->is_physfn)
|
|
|
+ return NOTIFY_DONE;
|
|
|
+
|
|
|
+ for (;;) {
|
|
|
+ struct pcistub_device *psdev = find_vfs(pdev);
|
|
|
+ if (!psdev)
|
|
|
+ break;
|
|
|
+ device_release_driver(&psdev->dev->dev);
|
|
|
+ }
|
|
|
+ return NOTIFY_DONE;
|
|
|
+}
|
|
|
+
|
|
|
+static struct notifier_block pci_stub_nb = {
|
|
|
+ .notifier_call = pci_stub_notifier,
|
|
|
+};
|
|
|
+#endif
|
|
|
+
|
|
|
static int __init xen_pcibk_init(void)
|
|
|
{
|
|
|
int err;
|
|
@@ -1539,12 +1586,19 @@ static int __init xen_pcibk_init(void)
|
|
|
err = xen_pcibk_xenbus_register();
|
|
|
if (err)
|
|
|
pcistub_exit();
|
|
|
+#ifdef CONFIG_PCI_IOV
|
|
|
+ else
|
|
|
+ bus_register_notifier(&pci_bus_type, &pci_stub_nb);
|
|
|
+#endif
|
|
|
|
|
|
return err;
|
|
|
}
|
|
|
|
|
|
static void __exit xen_pcibk_cleanup(void)
|
|
|
{
|
|
|
+#ifdef CONFIG_PCI_IOV
|
|
|
+ bus_unregister_notifier(&pci_bus_type, &pci_stub_nb);
|
|
|
+#endif
|
|
|
xen_pcibk_xenbus_unregister();
|
|
|
pcistub_exit();
|
|
|
}
|