xen-pciback: drop SR-IOV VFs when PF driver unloads
[deliverable/linux.git] / drivers / xen / xen-pciback / pci_stub.c
index 8580e53355ec8e3e52618079689c640b21e27b10..cc3cbb4435f8d61c15959e8146906c360c1a324b 100644 (file)
@@ -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();
 }
This page took 0.024327 seconds and 5 git commands to generate.