[Date Prev][Date Next]   [Thread Prev][Thread Next]   [Thread Index] [Date Index] [Author Index]

[libvirt] [PATCH 2/3] reattach pci device when pciBindDeviceToStub() failed



We should bind pci device to original driver when pciBindDeviceToStub() failed.
If the pci device is not bound to any driver before calling pciBindDeviceToStub(),
we should only unbind it from pci-stub. If it is bound to pci-stub, we should not
unbid it from pci-stub.

---
 src/util/pci.c |   90 +++++++++++++++++++++++++++++++++++++++++++++++++------
 1 files changed, 80 insertions(+), 10 deletions(-)

diff --git a/src/util/pci.c b/src/util/pci.c
index 8d2dbb0..e30f5cf 100644
--- a/src/util/pci.c
+++ b/src/util/pci.c
@@ -65,6 +65,11 @@ struct _pciDevice {
     unsigned      has_flr : 1;
     unsigned      has_pm_reset : 1;
     unsigned      managed : 1;
+
+    /* used by reattach function */
+    unsigned      unbind_from_stub : 1;
+    unsigned      remove_slot : 1;
+    unsigned      reprobe : 1;
 };
 
 struct _pciDeviceList {
@@ -834,11 +839,25 @@ recheck:
 }
 
 
+static int pciUnBindDeviceFromStub(pciDevice *dev, const char *driver);
 static int
 pciBindDeviceToStub(pciDevice *dev, const char *driver)
 {
     char drvdir[PATH_MAX];
     char path[PATH_MAX];
+    int reprobe = 0;
+    int ret = 0;
+
+    /* check whether the device is already bound to a driver */
+    pciDriverDir(drvdir, sizeof(drvdir), driver);
+    pciDeviceFile(path, sizeof(path), dev->name, "driver");
+    if (virFileExists(path)) {
+        if (virFileLinkPointsTo(path, drvdir)) {
+            /* The device is already bound to pci-stub */
+            return 0;
+        }
+        reprobe = 1;
+    }
 
     /* Add the PCI device ID to the stub's dynamic ID table;
      * this is needed to allow us to bind the device to the stub.
@@ -856,16 +875,31 @@ pciBindDeviceToStub(pciDevice *dev, const char *driver)
         return -1;
     }
 
+    /* check whether the device is bound to pci-stub when we write dev->id to
+     * new_id.
+     */
+    pciDriverDir(drvdir, sizeof(drvdir), driver);
+    pciDeviceFile(path, sizeof(path), dev->name, "driver");
+    if (virFileLinkPointsTo(path, drvdir)) {
+        dev->unbind_from_stub = 1;
+        dev->remove_slot = 1;
+        goto remove_id;
+    }
+
     /* If the device is already bound to a driver, unbind it.
      * Note, this will have rather unpleasant side effects if this
      * PCI device happens to be IDE controller for the disk hosting
      * your root filesystem.
      */
     pciDeviceFile(path, sizeof(path), dev->name, "driver/unbind");
-    if (virFileExists(path) && virFileWriteStr(path, dev->name, 0) < 0) {
-        virReportSystemError(errno,
-                             _("Failed to unbind PCI device '%s'"), dev->name);
-        return -1;
+    if (virFileExists(path)) {
+        if (virFileWriteStr(path, dev->name, 0) < 0) {
+            virReportSystemError(errno,
+                                 _("Failed to unbind PCI device '%s'"),
+                                 dev->name);
+            return -1;
+        }
+        dev->reprobe = reprobe;
     }
 
     /* If the device isn't already bound to pci-stub, try binding it now.
@@ -879,18 +913,23 @@ pciBindDeviceToStub(pciDevice *dev, const char *driver)
             virReportSystemError(errno,
                                  _("Failed to add slot for PCI device '%s' to %s"),
                                  dev->name, driver);
-            return -1;
+            ret = -1;
+            goto remove_id;
         }
+        dev->remove_slot = 1;
 
         pciDriverFile(path, sizeof(path), driver, "bind");
         if (virFileWriteStr(path, dev->name, 0) < 0) {
             virReportSystemError(errno,
                                  _("Failed to bind PCI device '%s' to %s"),
                                  dev->name, driver);
-            return -1;
+            ret = -1;
+            goto remove_id;
         }
+        dev->unbind_from_stub = 1;
     }
 
+remove_id:
     /* If 'remove_id' exists, remove the device id from pci-stub's dynamic
      * ID table so that 'drivers_probe' works below.
      */
@@ -899,10 +938,21 @@ pciBindDeviceToStub(pciDevice *dev, const char *driver)
         virReportSystemError(errno,
                              _("Failed to remove PCI ID '%s' from %s"),
                              dev->id, driver);
-        return -1;
+        ret = -1;
+
+        /* remove PCI ID from pci-stub failed, and we can not reprobe it */
+        if (dev->reprobe) {
+            VIR_WARN("Faile to remove PCI ID '%s' from %s, and the device can"
+                     "not be reprobed again.", dev->id, driver);
+        }
+        dev->reprobe = 0;
     }
 
-    return 0;
+    if (ret < 0) {
+        pciUnBindDeviceFromStub(dev, driver);
+    }
+
+    return ret;
 }
 
 int
@@ -930,6 +980,9 @@ pciUnBindDeviceFromStub(pciDevice *dev, const char *driver)
     char drvdir[PATH_MAX];
     char path[PATH_MAX];
 
+    if (!dev->unbind_from_stub)
+        goto remove_slot;
+
     /* If the device is bound to stub, unbind it.
      */
     pciDriverDir(drvdir, sizeof(drvdir), driver);
@@ -938,21 +991,36 @@ pciUnBindDeviceFromStub(pciDevice *dev, const char *driver)
         pciDriverFile(path, sizeof(path), driver, "unbind");
         if (virFileWriteStr(path, dev->name, 0) < 0) {
             virReportSystemError(errno,
-                                 _("Failed to bind PCI device '%s' to %s"),
+                                 _("Failed to unbind PCI device '%s' from %s"),
                                  dev->name, driver);
+            /* do not do it again */
+            dev->unbind_from_stub = 0;
+            dev->remove_slot = 0;
+            dev->reprobe = 0;
             return -1;
         }
     }
+    dev->unbind_from_stub = 0;
+
+remove_slot:
+    if (!dev->remove_slot)
+        goto reprobe;
 
     /* Xen's pciback.ko wants you to use remove_slot on the specific device */
     pciDriverFile(path, sizeof(path), driver, "remove_slot");
     if (virFileExists(path) && virFileWriteStr(path, dev->name, 0) < 0) {
         virReportSystemError(errno,
-                             _("Failed to remove slot for PCI device '%s' to %s"),
+                             _("Failed to remove slot for PCI device '%s' from %s"),
                              dev->name, driver);
+        dev->remove_slot = 0;
+        dev->reprobe = 0;
         return -1;
     }
+    dev->remove_slot = 0;
 
+reprobe:
+    if (!dev->reprobe)
+        return 0;
 
     /* Trigger a re-probe of the device is not in the stub's dynamic
      * ID table. If the stub is available, but 'remove_id' isn't
@@ -965,9 +1033,11 @@ pciUnBindDeviceFromStub(pciDevice *dev, const char *driver)
             virReportSystemError(errno,
                                  _("Failed to trigger a re-probe for PCI device '%s'"),
                                  dev->name);
+            dev->reprobe = 0;
             return -1;
         }
     }
+    dev->reprobe = 0;
 
     return 0;
 }
-- 
1.7.1


[Date Prev][Date Next]   [Thread Prev][Thread Next]   [Thread Index] [Date Index] [Author Index]