[libvirt] [PATCH 0/8] Few vfio related fixes

The series fixes 3 issues which lead to host crash. The actual fixes are in p3, p4, p7 and p8. I'll fill in more details / descriptions in v2. The make check fails on virpcitest now, which I am still debugging. I anticipate some rework from review on p7 which I continue to refine. Sending now, to get any feedback that might change things drastically. --- Shivaprasad G Bhat (8): Initialize the stubDriver of pci devices if bound to a valid one Add iommu group number info to virPCIDevice Refuse to reattach from vfio if the iommu group is in use by any domain Wait for vfio-pci device cleanups before reassinging the device to host driver Split reprobe action from the virPCIUnbindFromStub into a new function Pass activeDevs and inactiveDevs to virPCIDeviceUnbindFromStub and virPCIDeviceBindToStub Postpone reprobing till all the devices in iommu group are unbound from vfio Wait for iommmu device to go away before reprobing the host driver src/util/virhostdev.c | 18 ++++ src/util/virpci.c | 206 ++++++++++++++++++++++++++++++++++++++----------- 2 files changed, 177 insertions(+), 47 deletions(-) -- Signature

The stubDriver name can be used to make useful decisions if readily available. Set it if bound to a valid one during initialisation. Signed-off-by: Shivaprasad G Bhat <sbhat@linux.vnet.ibm.com> --- src/util/virpci.c | 36 ++++++++++++++++++++++++++---------- 1 file changed, 26 insertions(+), 10 deletions(-) diff --git a/src/util/virpci.c b/src/util/virpci.c index 35b1459..5acf486 100644 --- a/src/util/virpci.c +++ b/src/util/virpci.c @@ -1080,6 +1080,22 @@ static const char *virPCIKnownStubs[] = { NULL }; +static bool virPCIIsAKnownStub(char *driver) +{ + const char **stubTest; + bool ret = false; + + for (stubTest = virPCIKnownStubs; *stubTest != NULL; stubTest++) { + if (STREQ_NULLABLE(driver, *stubTest)) { + ret = true; + VIR_DEBUG("Found stub driver %s", *stubTest); + break; + } + } + + return ret; +} + static int virPCIDeviceUnbindFromStub(virPCIDevicePtr dev) { @@ -1087,8 +1103,6 @@ virPCIDeviceUnbindFromStub(virPCIDevicePtr dev) char *drvdir = NULL; char *path = NULL; char *driver = NULL; - const char **stubTest; - bool isStub = false; /* If the device is currently bound to one of the "well known" * stub drivers, then unbind it, otherwise ignore it. @@ -1105,14 +1119,7 @@ virPCIDeviceUnbindFromStub(virPCIDevicePtr dev) goto remove_slot; /* If the device isn't bound to a known stub, skip the unbind. */ - for (stubTest = virPCIKnownStubs; *stubTest != NULL; stubTest++) { - if (STREQ(driver, *stubTest)) { - isStub = true; - VIR_DEBUG("Found stub driver %s", *stubTest); - break; - } - } - if (!isStub) + if (!virPCIIsAKnownStub(driver)) goto remove_slot; if (virPCIDeviceUnbind(dev, dev->reprobe) < 0) @@ -1556,6 +1563,8 @@ virPCIDeviceNew(unsigned int domain, virPCIDevicePtr dev; char *vendor = NULL; char *product = NULL; + char *drvpath = NULL; + char *driver = NULL; if (VIR_ALLOC(dev) < 0) return NULL; @@ -1603,9 +1612,16 @@ virPCIDeviceNew(unsigned int domain, goto error; } + if (virPCIDeviceGetDriverPathAndName(dev, &drvpath, &driver) < 0) + goto cleanup; + + if (virPCIIsAKnownStub(driver)) + dev->stubDriver = driver; + VIR_DEBUG("%s %s: initialized", dev->id, dev->name); cleanup: + VIR_FREE(drvpath); VIR_FREE(product); VIR_FREE(vendor); return dev;

On Fri, 2015-10-30 at 04:56 +0530, Shivaprasad G Bhat wrote:
The stubDriver name can be used to make useful decisions if readily available. Set it if bound to a valid one during initialisation.
Signed-off-by: Shivaprasad G Bhat <sbhat@linux.vnet.ibm.com> --- src/util/virpci.c | 36 ++++++++++++++++++++++++++---------- 1 file changed, 26 insertions(+), 10 deletions(-)
diff --git a/src/util/virpci.c b/src/util/virpci.c index 35b1459..5acf486 100644 --- a/src/util/virpci.c +++ b/src/util/virpci.c @@ -1080,6 +1080,22 @@ static const char *virPCIKnownStubs[] = { NULL };
+static bool virPCIIsAKnownStub(char *driver)
Return type on a separate line, please.
+{ + const char **stubTest; + bool ret = false; + + for (stubTest = virPCIKnownStubs; *stubTest != NULL; stubTest++) { + if (STREQ_NULLABLE(driver, *stubTest)) { + ret = true; + VIR_DEBUG("Found stub driver %s", *stubTest); + break; + } + } + + return ret; +} + static int virPCIDeviceUnbindFromStub(virPCIDevicePtr dev) { @@ -1087,8 +1103,6 @@ virPCIDeviceUnbindFromStub(virPCIDevicePtr dev) char *drvdir = NULL; char *path = NULL; char *driver = NULL; - const char **stubTest; - bool isStub = false;
/* If the device is currently bound to one of the "well known" * stub drivers, then unbind it, otherwise ignore it. @@ -1105,14 +1119,7 @@ virPCIDeviceUnbindFromStub(virPCIDevicePtr dev) goto remove_slot;
/* If the device isn't bound to a known stub, skip the unbind. */ - for (stubTest = virPCIKnownStubs; *stubTest != NULL; stubTest++) { - if (STREQ(driver, *stubTest)) { - isStub = true; - VIR_DEBUG("Found stub driver %s", *stubTest); - break; - } - } - if (!isStub) + if (!virPCIIsAKnownStub(driver)) goto remove_slot;
if (virPCIDeviceUnbind(dev, dev->reprobe) < 0)
I would split this patch in two: everything above is just moving an existing check into a separate function, without introducing any functional change. The code below, on the other hand, is changing the behavior of virPCIDeviceNew() and as such should go in its own patch. But see below.
@@ -1556,6 +1563,8 @@ virPCIDeviceNew(unsigned int domain, virPCIDevicePtr dev; char *vendor = NULL; char *product = NULL; + char *drvpath = NULL; + char *driver = NULL;
if (VIR_ALLOC(dev) < 0) return NULL; @@ -1603,9 +1612,16 @@ virPCIDeviceNew(unsigned int domain, goto error; }
+ if (virPCIDeviceGetDriverPathAndName(dev, &drvpath, &driver) < 0) + goto cleanup; + + if (virPCIIsAKnownStub(driver)) + dev->stubDriver = driver; + VIR_DEBUG("%s %s: initialized", dev->id, dev->name);
cleanup: + VIR_FREE(drvpath); VIR_FREE(product); VIR_FREE(vendor); return dev;
What are you doing this for? AFAICT you're using this so you can, in Patch 7, do pci = virPCIDeviceNew(...); if (STREQ_NULLABLE(pci->stubDriver, "vfio-pci")) ... Is that so, or is there another reason I'm missing? If the former, I'd rather not overload the meaning of dev->stubDriver and call virPCIDeviceGetDriverPathAndName() explicitly in Patch 7, so that the intentions behind the code are abundantly clear. Cheers. -- Andrea Bolognani Software Engineer - Virtualization Team

Thanks for the comments Andrea. On Fri, Oct 30, 2015 at 8:27 PM, Andrea Bolognani <abologna@redhat.com> wrote:
On Fri, 2015-10-30 at 04:56 +0530, Shivaprasad G Bhat wrote:
The stubDriver name can be used to make useful decisions if readily available. Set it if bound to a valid one during initialisation.
Signed-off-by: Shivaprasad G Bhat <sbhat@linux.vnet.ibm.com> --- src/util/virpci.c | 36 ++++++++++++++++++++++++++---------- 1 file changed, 26 insertions(+), 10 deletions(-)
diff --git a/src/util/virpci.c b/src/util/virpci.c index 35b1459..5acf486 100644 --- a/src/util/virpci.c +++ b/src/util/virpci.c @@ -1080,6 +1080,22 @@ static const char *virPCIKnownStubs[] = { NULL };
+static bool virPCIIsAKnownStub(char *driver)
Return type on a separate line, please.
+{ + const char **stubTest; + bool ret = false; + + for (stubTest = virPCIKnownStubs; *stubTest != NULL; stubTest++) { + if (STREQ_NULLABLE(driver, *stubTest)) { + ret = true; + VIR_DEBUG("Found stub driver %s", *stubTest); + break; + } + } + + return ret; +} + static int virPCIDeviceUnbindFromStub(virPCIDevicePtr dev) { @@ -1087,8 +1103,6 @@ virPCIDeviceUnbindFromStub(virPCIDevicePtr dev) char *drvdir = NULL; char *path = NULL; char *driver = NULL; - const char **stubTest; - bool isStub = false;
/* If the device is currently bound to one of the "well known" * stub drivers, then unbind it, otherwise ignore it. @@ -1105,14 +1119,7 @@ virPCIDeviceUnbindFromStub(virPCIDevicePtr dev) goto remove_slot;
/* If the device isn't bound to a known stub, skip the unbind. */ - for (stubTest = virPCIKnownStubs; *stubTest != NULL; stubTest++) { - if (STREQ(driver, *stubTest)) { - isStub = true; - VIR_DEBUG("Found stub driver %s", *stubTest); - break; - } - } - if (!isStub) + if (!virPCIIsAKnownStub(driver)) goto remove_slot;
if (virPCIDeviceUnbind(dev, dev->reprobe) < 0)
I would split this patch in two: everything above is just moving an existing check into a separate function, without introducing any functional change.
Sure.. Will do that.
The code below, on the other hand, is changing the behavior of virPCIDeviceNew() and as such should go in its own patch. But see below.
@@ -1556,6 +1563,8 @@ virPCIDeviceNew(unsigned int domain, virPCIDevicePtr dev; char *vendor = NULL; char *product = NULL; + char *drvpath = NULL; + char *driver = NULL;
if (VIR_ALLOC(dev) < 0) return NULL; @@ -1603,9 +1612,16 @@ virPCIDeviceNew(unsigned int domain, goto error; }
+ if (virPCIDeviceGetDriverPathAndName(dev, &drvpath, &driver) < 0) + goto cleanup; + + if (virPCIIsAKnownStub(driver)) + dev->stubDriver = driver; + VIR_DEBUG("%s %s: initialized", dev->id, dev->name);
cleanup: + VIR_FREE(drvpath); VIR_FREE(product); VIR_FREE(vendor); return dev;
What are you doing this for? AFAICT you're using this so you can, in Patch 7, do
pci = virPCIDeviceNew(...); if (STREQ_NULLABLE(pci->stubDriver, "vfio-pci")) ...
Is that so, or is there another reason I'm missing?
Its used in P3 as well in virHostdevPCINodeDeviceReAttach(). I want to keep that function as simple as it is now. And as you pointed out, i am using it in P7 too.Hope its okay now.
If the former, I'd rather not overload the meaning of dev->stubDriver and call virPCIDeviceGetDriverPathAndName() explicitly in Patch 7, so that the intentions behind the code are abundantly clear.
Cheers.
-- Andrea Bolognani Software Engineer - Virtualization Team

On Fri, 2015-10-30 at 20:34 +0530, Shivaprasad bhat wrote:
@@ -1556,6 +1563,8 @@ virPCIDeviceNew(unsigned int domain, virPCIDevicePtr dev; char *vendor = NULL; char *product = NULL; + char *drvpath = NULL; + char *driver = NULL;
if (VIR_ALLOC(dev) < 0) return NULL; @@ -1603,9 +1612,16 @@ virPCIDeviceNew(unsigned int domain, goto error; }
+ if (virPCIDeviceGetDriverPathAndName(dev, &drvpath, &driver) < 0) + goto cleanup; + + if (virPCIIsAKnownStub(driver)) + dev->stubDriver = driver; + VIR_DEBUG("%s %s: initialized", dev->id, dev->name);
cleanup: + VIR_FREE(drvpath); VIR_FREE(product); VIR_FREE(vendor); return dev;
What are you doing this for? AFAICT you're using this so you can, in Patch 7, do
pci = virPCIDeviceNew(...); if (STREQ_NULLABLE(pci->stubDriver, "vfio-pci")) ...
Is that so, or is there another reason I'm missing?
Its used in P3 as well in virHostdevPCINodeDeviceReAttach(). I want to keep that function as simple as it is now. And as you pointed out, i am using it in P7 too.Hope its okay now.
I don't see how it's used in that function, as neither the function itself nor the calls to virHostdevIsPCINodeDeviceUsed() you've added with Patch 3 seem to touch dev->stubDriver... Please walk me through it, I'm probably just missing it because it's Friday :) Cheers. -- Andrea Bolognani Software Engineer - Virtualization Team

The iommu group number need not be fetched from the sysfs everytime as it remains constant. Fetch it once during allocation Signed-off-by: Shivaprasad G Bhat <sbhat@linux.vnet.ibm.com> --- src/util/virpci.c | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/src/util/virpci.c b/src/util/virpci.c index 5acf486..eba285a 100644 --- a/src/util/virpci.c +++ b/src/util/virpci.c @@ -75,6 +75,7 @@ struct _virPCIDevice { bool has_pm_reset; bool managed; char *stubDriver; + unsigned int iommuGroup; /* used by reattach function */ bool unbind_from_stub; @@ -1565,6 +1566,8 @@ virPCIDeviceNew(unsigned int domain, char *product = NULL; char *drvpath = NULL; char *driver = NULL; + virPCIDeviceAddress devAddr = { domain, bus, + slot, function }; if (VIR_ALLOC(dev) < 0) return NULL; @@ -1618,6 +1621,8 @@ virPCIDeviceNew(unsigned int domain, if (virPCIIsAKnownStub(driver)) dev->stubDriver = driver; + dev->iommuGroup = virPCIDeviceAddressGetIOMMUGroupNum(&devAddr); + VIR_DEBUG("%s %s: initialized", dev->id, dev->name); cleanup:

n Fri, 2015-10-30 at 04:56 +0530, Shivaprasad G Bhat wrote:
The iommu group number need not be fetched from the sysfs everytime as it remains constant. Fetch it once during allocation
Signed-off-by: Shivaprasad G Bhat <sbhat@linux.vnet.ibm.com> --- src/util/virpci.c | 5 +++++ 1 file changed, 5 insertions(+)
diff --git a/src/util/virpci.c b/src/util/virpci.c index 5acf486..eba285a 100644 --- a/src/util/virpci.c +++ b/src/util/virpci.c @@ -75,6 +75,7 @@ struct _virPCIDevice { bool has_pm_reset; bool managed; char *stubDriver; + unsigned int iommuGroup;
This can't be unsigned int, as virPCIDeviceAddressGetIOMMUGroupNum() will return -2 if there is no IOMMU group for the device.
/* used by reattach function */ bool unbind_from_stub; @@ -1565,6 +1566,8 @@ virPCIDeviceNew(unsigned int domain, char *product = NULL; char *drvpath = NULL; char *driver = NULL; + virPCIDeviceAddress devAddr = { domain, bus, + slot, function };
if (VIR_ALLOC(dev) < 0) return NULL; @@ -1618,6 +1621,8 @@ virPCIDeviceNew(unsigned int domain, if (virPCIIsAKnownStub(driver)) dev->stubDriver = driver;
+ dev->iommuGroup = virPCIDeviceAddressGetIOMMUGroupNum(&devAddr); + VIR_DEBUG("%s %s: initialized", dev->id, dev->name);
cleanup:
Cheers. -- Andrea Bolognani Software Engineer - Virtualization Team

It is incorrect to attempt the device reattach of a function, when some other domain is using some functions belonging to the same iommu group. Signed-off-by: Shivaprasad G Bhat <sbhat@linux.vnet.ibm.com> --- src/util/virhostdev.c | 11 ++++++++++- 1 file changed, 10 insertions(+), 1 deletion(-) diff --git a/src/util/virhostdev.c b/src/util/virhostdev.c index de029a0..91f28e9 100644 --- a/src/util/virhostdev.c +++ b/src/util/virhostdev.c @@ -1590,6 +1590,7 @@ virHostdevPCINodeDeviceReAttach(virHostdevManagerPtr hostdev_mgr, virPCIDevicePtr pci) { virPCIDeviceAddressPtr devAddr = NULL; + bool usesVfio = STREQ_NULLABLE(virPCIDeviceGetStubDriver(pci), "vfio-pci"); struct virHostdevIsPCINodeDeviceUsedData data = {hostdev_mgr, NULL, false}; int ret = -1; @@ -1600,8 +1601,16 @@ virHostdevPCINodeDeviceReAttach(virHostdevManagerPtr hostdev_mgr, if (!(devAddr = virPCIDeviceGetAddress(pci))) goto out; - if (virHostdevIsPCINodeDeviceUsed(devAddr, &data)) + if (usesVfio) { + /* Doesn't matter which function. If any domain is actively using the + iommu group, refuse to reattach */ + if (virPCIDeviceAddressIOMMUGroupIterate(devAddr, + virHostdevIsPCINodeDeviceUsed, + &data) < 0) + goto out; + } else if (virHostdevIsPCINodeDeviceUsed(devAddr, &data)) { goto out; + } virPCIDeviceReattachInit(pci);

On Fri, 2015-10-30 at 04:57 +0530, Shivaprasad G Bhat wrote:
It is incorrect to attempt the device reattach of a function, when some other domain is using some functions belonging to the same iommu group.
Signed-off-by: Shivaprasad G Bhat <sbhat@linux.vnet.ibm.com> --- src/util/virhostdev.c | 11 ++++++++++- 1 file changed, 10 insertions(+), 1 deletion(-)
diff --git a/src/util/virhostdev.c b/src/util/virhostdev.c index de029a0..91f28e9 100644 --- a/src/util/virhostdev.c +++ b/src/util/virhostdev.c @@ -1590,6 +1590,7 @@ virHostdevPCINodeDeviceReAttach(virHostdevManagerPtr hostdev_mgr, virPCIDevicePtr pci) { virPCIDeviceAddressPtr devAddr = NULL; + bool usesVfio = STREQ_NULLABLE(virPCIDeviceGetStubDriver(pci), "vfio-pci"); struct virHostdevIsPCINodeDeviceUsedData data = {hostdev_mgr, NULL, false}; int ret = -1; @@ -1600,8 +1601,16 @@ virHostdevPCINodeDeviceReAttach(virHostdevManagerPtr hostdev_mgr, if (!(devAddr = virPCIDeviceGetAddress(pci))) goto out;
- if (virHostdevIsPCINodeDeviceUsed(devAddr, &data)) + if (usesVfio) { + /* Doesn't matter which function. If any domain is actively using the + iommu group, refuse to reattach */
Please indent this comment properly. The second line should start with * as well.
+ if (virPCIDeviceAddressIOMMUGroupIterate(devAddr, + virHostdevIsPCINodeDeviceUsed, + &data) < 0) + goto out; + } else if (virHostdevIsPCINodeDeviceUsed(devAddr, &data)) { goto out; + }
virPCIDeviceReattachInit(pci);
Cheers. -- Andrea Bolognani Software Engineer - Virtualization Team

Before unbind from stub driver libvirt should be sure the guest is not using the device anymore. The libvirt today waits for pci-stub driver alone in /proc/iommu. The same wait is needed for vfio devices too. Signed-off-by: Shivaprasad G Bhat <sbhat@linux.vnet.ibm.com> --- src/util/virhostdev.c | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/src/util/virhostdev.c b/src/util/virhostdev.c index 91f28e9..6247477 100644 --- a/src/util/virhostdev.c +++ b/src/util/virhostdev.c @@ -756,6 +756,13 @@ virHostdevReattachPCIDevice(virPCIDevicePtr dev, virHostdevManagerPtr mgr) usleep(100*1000); retries--; } + } else if (STREQ(virPCIDeviceGetStubDriver(dev), "vfio-pci")) { + int retries = 100; + while (virPCIDeviceWaitForCleanup(dev, "vfio-pci") + && retries) { + usleep(100*1000); + retries--; + } } if (virPCIDeviceReattach(dev, mgr->activePCIHostdevs,

On Fri, 2015-10-30 at 04:58 +0530, Shivaprasad G Bhat wrote:
Before unbind from stub driver libvirt should be sure the guest is not using the device anymore. The libvirt today waits for pci-stub driver alone in /proc/iommu. The same wait is needed for vfio devices too.
Signed-off-by: Shivaprasad G Bhat <sbhat@linux.vnet.ibm.com> --- src/util/virhostdev.c | 7 +++++++ 1 file changed, 7 insertions(+)
diff --git a/src/util/virhostdev.c b/src/util/virhostdev.c index 91f28e9..6247477 100644 --- a/src/util/virhostdev.c +++ b/src/util/virhostdev.c @@ -756,6 +756,13 @@ virHostdevReattachPCIDevice(virPCIDevicePtr dev, virHostdevManagerPtr mgr) usleep(100*1000); retries--; } + } else if (STREQ(virPCIDeviceGetStubDriver(dev), "vfio-pci")) { + int retries = 100; + while (virPCIDeviceWaitForCleanup(dev, "vfio-pci") + && retries) { + usleep(100*1000); + retries--; + } }
if (virPCIDeviceReattach(dev, mgr->activePCIHostdevs,
This looks good. Weak ACK, we need somebody who's more familiar with this area of libvirt to go over the whole series anyway. Cheers. -- Andrea Bolognani Software Engineer - Virtualization Team

The reprobe needs to be called multiple times for vfio devices for each device in the iommu group in future patch. So split the reprobe into a new function. No functional change. Signed-off-by: Shivaprasad G Bhat <sbhat@linux.vnet.ibm.com> --- src/util/virpci.c | 54 +++++++++++++++++++++++++++++++---------------------- 1 file changed, 32 insertions(+), 22 deletions(-) diff --git a/src/util/virpci.c b/src/util/virpci.c index eba285a..2709ddd 100644 --- a/src/util/virpci.c +++ b/src/util/virpci.c @@ -1097,6 +1097,37 @@ static bool virPCIIsAKnownStub(char *driver) return ret; } +static int virPCIDeviceReprobeHostDriver(virPCIDevicePtr dev, char *driver, char *drvdir) +{ + char *path = NULL; + int result = -1; + + 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 + * available, then re-probing would just cause the device to be + * re-bound to the stub. + */ + if (driver && !(path = virPCIDriverFile(driver, "remove_id"))) + goto cleanup; + + if (!driver || !virFileExists(drvdir) || virFileExists(path)) { + if (virFileWriteStr(PCI_SYSFS "drivers_probe", dev->name, 0) < 0) { + virReportSystemError(errno, + _("Failed to trigger a re-probe for PCI device '%s'"), + dev->name); + goto cleanup; + } + } + result = 0; + cleanup: + VIR_FREE(path); + dev->reprobe = false; + return result; +} + static int virPCIDeviceUnbindFromStub(virPCIDevicePtr dev) { @@ -1144,28 +1175,8 @@ virPCIDeviceUnbindFromStub(virPCIDevicePtr dev) dev->remove_slot = false; reprobe: - if (!dev->reprobe) { - result = 0; + if (virPCIDeviceReprobeHostDriver(dev, driver, drvdir) < 0) goto cleanup; - } - - /* 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 - * available, then re-probing would just cause the device to be - * re-bound to the stub. - */ - VIR_FREE(path); - if (driver && !(path = virPCIDriverFile(driver, "remove_id"))) - goto cleanup; - - if (!driver || !virFileExists(drvdir) || virFileExists(path)) { - if (virFileWriteStr(PCI_SYSFS "drivers_probe", dev->name, 0) < 0) { - virReportSystemError(errno, - _("Failed to trigger a re-probe for PCI device '%s'"), - dev->name); - goto cleanup; - } - } result = 0; @@ -1173,7 +1184,6 @@ virPCIDeviceUnbindFromStub(virPCIDevicePtr dev) /* do not do it again */ dev->unbind_from_stub = false; dev->remove_slot = false; - dev->reprobe = false; VIR_FREE(drvdir); VIR_FREE(path);

On Fri, 2015-10-30 at 04:59 +0530, Shivaprasad G Bhat wrote:
The reprobe needs to be called multiple times for vfio devices for each device in the iommu group in future patch. So split the reprobe into a new function. No functional change.
Signed-off-by: Shivaprasad G Bhat <sbhat@linux.vnet.ibm.com> --- src/util/virpci.c | 54 +++++++++++++++++++++++++++++++---------------------- 1 file changed, 32 insertions(+), 22 deletions(-)
diff --git a/src/util/virpci.c b/src/util/virpci.c index eba285a..2709ddd 100644 --- a/src/util/virpci.c +++ b/src/util/virpci.c @@ -1097,6 +1097,37 @@ static bool virPCIIsAKnownStub(char *driver) return ret; }
+static int virPCIDeviceReprobeHostDriver(virPCIDevicePtr dev, char *driver, char *drvdir) +{ + char *path = NULL; + int result = -1; + + 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 + * available, then re-probing would just cause the device to be + * re-bound to the stub. + */ + if (driver && !(path = virPCIDriverFile(driver, "remove_id"))) + goto cleanup; + + if (!driver || !virFileExists(drvdir) || virFileExists(path)) { + if (virFileWriteStr(PCI_SYSFS "drivers_probe", dev->name, 0) < 0) { + virReportSystemError(errno, + _("Failed to trigger a re-probe for PCI device '%s'"), + dev->name); + goto cleanup; + } + } + result = 0; + cleanup: + VIR_FREE(path); + dev->reprobe = false; + return result; +} + static int virPCIDeviceUnbindFromStub(virPCIDevicePtr dev) { @@ -1144,28 +1175,8 @@ virPCIDeviceUnbindFromStub(virPCIDevicePtr dev) dev->remove_slot = false;
reprobe: - if (!dev->reprobe) { - result = 0; + if (virPCIDeviceReprobeHostDriver(dev, driver, drvdir) < 0) goto cleanup; - }
This actually introduces a flow change in the function: before this patch, when dev->reprobe is false we set result = 0 and jumped to cleanup, while now virPCIDeviceReprobeHostDriver() returns 0 and the rest of virPCIDeviceUnbindFromStub() is executed. I would leave the check for the value of dev->reprobe and setting it to false in cleanup out of virPCIDeviceReprobeHostDriver().
- - /* 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 - * available, then re-probing would just cause the device to be - * re-bound to the stub. - */ - VIR_FREE(path); - if (driver && !(path = virPCIDriverFile(driver, "remove_id"))) - goto cleanup; - - if (!driver || !virFileExists(drvdir) || virFileExists(path)) { - if (virFileWriteStr(PCI_SYSFS "drivers_probe", dev->name, 0) < 0) { - virReportSystemError(errno, - _("Failed to trigger a re-probe for PCI device '%s'"), - dev->name); - goto cleanup; - } - }
result = 0;
@@ -1173,7 +1184,6 @@ virPCIDeviceUnbindFromStub(virPCIDevicePtr dev) /* do not do it again */ dev->unbind_from_stub = false; dev->remove_slot = false; - dev->reprobe = false;
VIR_FREE(drvdir); VIR_FREE(path);
Cheers. -- Andrea Bolognani Software Engineer - Virtualization Team

The inactiveDevs need to be selectively altered for more than one device in case of vfio devices. So, pass the whole list. Signed-off-by: Shivaprasad G Bhat <sbhat@linux.vnet.ibm.com> --- src/util/virpci.c | 38 +++++++++++++++++++++----------------- 1 file changed, 21 insertions(+), 17 deletions(-) diff --git a/src/util/virpci.c b/src/util/virpci.c index 2709ddd..6c24a81 100644 --- a/src/util/virpci.c +++ b/src/util/virpci.c @@ -1129,7 +1129,9 @@ static int virPCIDeviceReprobeHostDriver(virPCIDevicePtr dev, char *driver, char } static int -virPCIDeviceUnbindFromStub(virPCIDevicePtr dev) +virPCIDeviceUnbindFromStub(virPCIDevicePtr dev, + virPCIDeviceListPtr activeDevs ATTRIBUTE_UNUSED, + virPCIDeviceListPtr inactiveDevs) { int result = -1; char *drvdir = NULL; @@ -1177,6 +1179,9 @@ virPCIDeviceUnbindFromStub(virPCIDevicePtr dev) reprobe: if (virPCIDeviceReprobeHostDriver(dev, driver, drvdir) < 0) goto cleanup; + /* Steal the dev from list inactiveDevs */ + if (inactiveDevs) + virPCIDeviceListDel(inactiveDevs, dev); result = 0; @@ -1195,7 +1200,9 @@ virPCIDeviceUnbindFromStub(virPCIDevicePtr dev) static int virPCIDeviceBindToStub(virPCIDevicePtr dev, - const char *stubDriverName) + const char *stubDriverName, + virPCIDeviceListPtr activeDevs, + virPCIDeviceListPtr inactiveDevs) { int result = -1; bool reprobe = false; @@ -1317,6 +1324,14 @@ virPCIDeviceBindToStub(virPCIDevicePtr dev, goto cleanup; } + /* Add *a copy of* the dev into list inactiveDevs, if + * it's not already there. + */ + if (inactiveDevs && !virPCIDeviceListFind(inactiveDevs, dev) && + virPCIDeviceListAddCopy(inactiveDevs, dev) < 0) { + result = -1; + } + cleanup: VIR_FREE(stubDriverPath); VIR_FREE(driverLink); @@ -1324,7 +1339,7 @@ virPCIDeviceBindToStub(virPCIDevicePtr dev, if (result < 0) { VIR_FREE(newDriverName); - virPCIDeviceUnbindFromStub(dev); + virPCIDeviceUnbindFromStub(dev, activeDevs, NULL); } else { VIR_FREE(dev->stubDriver); dev->stubDriver = newDriverName; @@ -1371,16 +1386,9 @@ virPCIDeviceDetach(virPCIDevicePtr dev, return -1; } - if (virPCIDeviceBindToStub(dev, dev->stubDriver) < 0) - return -1; - - /* Add *a copy of* the dev into list inactiveDevs, if - * it's not already there. - */ - if (inactiveDevs && !virPCIDeviceListFind(inactiveDevs, dev) && - virPCIDeviceListAddCopy(inactiveDevs, dev) < 0) { + if (virPCIDeviceBindToStub(dev, dev->stubDriver, + activeDevs, inactiveDevs) < 0) return -1; - } return 0; } @@ -1396,13 +1404,9 @@ virPCIDeviceReattach(virPCIDevicePtr dev, return -1; } - if (virPCIDeviceUnbindFromStub(dev) < 0) + if (virPCIDeviceUnbindFromStub(dev, activeDevs, inactiveDevs) < 0) return -1; - /* Steal the dev from list inactiveDevs */ - if (inactiveDevs) - virPCIDeviceListDel(inactiveDevs, dev); - return 0; }

On Fri, 2015-10-30 at 05:00 +0530, Shivaprasad G Bhat wrote:
The inactiveDevs need to be selectively altered for more than one device in case of vfio devices. So, pass the whole list.
Signed-off-by: Shivaprasad G Bhat <sbhat@linux.vnet.ibm.com> --- src/util/virpci.c | 38 +++++++++++++++++++++----------------- 1 file changed, 21 insertions(+), 17 deletions(-)
diff --git a/src/util/virpci.c b/src/util/virpci.c index 2709ddd..6c24a81 100644 --- a/src/util/virpci.c +++ b/src/util/virpci.c @@ -1129,7 +1129,9 @@ static int virPCIDeviceReprobeHostDriver(virPCIDevicePtr dev, char *driver, char }
static int -virPCIDeviceUnbindFromStub(virPCIDevicePtr dev) +virPCIDeviceUnbindFromStub(virPCIDevicePtr dev, + virPCIDeviceListPtr activeDevs ATTRIBUTE_UNUSED, + virPCIDeviceListPtr inactiveDevs) { int result = -1; char *drvdir = NULL; @@ -1177,6 +1179,9 @@ virPCIDeviceUnbindFromStub(virPCIDevicePtr dev) reprobe: if (virPCIDeviceReprobeHostDriver(dev, driver, drvdir) < 0) goto cleanup; + /* Steal the dev from list inactiveDevs */ + if (inactiveDevs) + virPCIDeviceListDel(inactiveDevs, dev);
result = 0;
@@ -1195,7 +1200,9 @@ virPCIDeviceUnbindFromStub(virPCIDevicePtr dev)
static int virPCIDeviceBindToStub(virPCIDevicePtr dev, - const char *stubDriverName) + const char *stubDriverName, + virPCIDeviceListPtr activeDevs, + virPCIDeviceListPtr inactiveDevs) { int result = -1; bool reprobe = false; @@ -1317,6 +1324,14 @@ virPCIDeviceBindToStub(virPCIDevicePtr dev, goto cleanup; }
+ /* Add *a copy of* the dev into list inactiveDevs, if + * it's not already there. + */
Just close the comment in the previous line.
+ if (inactiveDevs && !virPCIDeviceListFind(inactiveDevs, dev) && + virPCIDeviceListAddCopy(inactiveDevs, dev) < 0) {
Really weird alignment here...
+ result = -1; + } + cleanup: VIR_FREE(stubDriverPath); VIR_FREE(driverLink); @@ -1324,7 +1339,7 @@ virPCIDeviceBindToStub(virPCIDevicePtr dev,
if (result < 0) { VIR_FREE(newDriverName); - virPCIDeviceUnbindFromStub(dev); + virPCIDeviceUnbindFromStub(dev, activeDevs, NULL);
Why pass NULL instead of inactiveDevs here?
} else { VIR_FREE(dev->stubDriver); dev->stubDriver = newDriverName; @@ -1371,16 +1386,9 @@ virPCIDeviceDetach(virPCIDevicePtr dev, return -1; }
- if (virPCIDeviceBindToStub(dev, dev->stubDriver) < 0) - return -1; - - /* Add *a copy of* the dev into list inactiveDevs, if - * it's not already there. - */ - if (inactiveDevs && !virPCIDeviceListFind(inactiveDevs, dev) && - virPCIDeviceListAddCopy(inactiveDevs, dev) < 0) { + if (virPCIDeviceBindToStub(dev, dev->stubDriver, + activeDevs, inactiveDevs) < 0) return -1; - }
return 0; } @@ -1396,13 +1404,9 @@ virPCIDeviceReattach(virPCIDevicePtr dev, return -1; }
- if (virPCIDeviceUnbindFromStub(dev) < 0) + if (virPCIDeviceUnbindFromStub(dev, activeDevs, inactiveDevs) < 0) return -1;
- /* Steal the dev from list inactiveDevs */ - if (inactiveDevs) - virPCIDeviceListDel(inactiveDevs, dev); - return 0; }
Cheers. -- Andrea Bolognani Software Engineer - Virtualization Team

On Fri, Oct 30, 2015 at 9:13 PM, Andrea Bolognani <abologna@redhat.com> wrote:
On Fri, 2015-10-30 at 05:00 +0530, Shivaprasad G Bhat wrote:
The inactiveDevs need to be selectively altered for more than one device in case of vfio devices. So, pass the whole list.
Signed-off-by: Shivaprasad G Bhat <sbhat@linux.vnet.ibm.com> --- src/util/virpci.c | 38 +++++++++++++++++++++----------------- 1 file changed, 21 insertions(+), 17 deletions(-)
diff --git a/src/util/virpci.c b/src/util/virpci.c index 2709ddd..6c24a81 100644 --- a/src/util/virpci.c +++ b/src/util/virpci.c @@ -1129,7 +1129,9 @@ static int virPCIDeviceReprobeHostDriver(virPCIDevicePtr dev, char *driver, char }
static int -virPCIDeviceUnbindFromStub(virPCIDevicePtr dev) +virPCIDeviceUnbindFromStub(virPCIDevicePtr dev, + virPCIDeviceListPtr activeDevs ATTRIBUTE_UNUSED, + virPCIDeviceListPtr inactiveDevs) { int result = -1; char *drvdir = NULL; @@ -1177,6 +1179,9 @@ virPCIDeviceUnbindFromStub(virPCIDevicePtr dev) reprobe: if (virPCIDeviceReprobeHostDriver(dev, driver, drvdir) < 0) goto cleanup; + /* Steal the dev from list inactiveDevs */ + if (inactiveDevs) + virPCIDeviceListDel(inactiveDevs, dev);
result = 0;
@@ -1195,7 +1200,9 @@ virPCIDeviceUnbindFromStub(virPCIDevicePtr dev)
static int virPCIDeviceBindToStub(virPCIDevicePtr dev, - const char *stubDriverName) + const char *stubDriverName, + virPCIDeviceListPtr activeDevs, + virPCIDeviceListPtr inactiveDevs) { int result = -1; bool reprobe = false; @@ -1317,6 +1324,14 @@ virPCIDeviceBindToStub(virPCIDevicePtr dev, goto cleanup; }
+ /* Add *a copy of* the dev into list inactiveDevs, if + * it's not already there. + */
Just close the comment in the previous line.
+ if (inactiveDevs && !virPCIDeviceListFind(inactiveDevs, dev) && + virPCIDeviceListAddCopy(inactiveDevs, dev) < 0) {
Really weird alignment here...
+ result = -1; + } + cleanup: VIR_FREE(stubDriverPath); VIR_FREE(driverLink); @@ -1324,7 +1339,7 @@ virPCIDeviceBindToStub(virPCIDevicePtr dev,
if (result < 0) { VIR_FREE(newDriverName); - virPCIDeviceUnbindFromStub(dev); + virPCIDeviceUnbindFromStub(dev, activeDevs, NULL);
Why pass NULL instead of inactiveDevs here?
Correct. Will Pass the inactiveDevs. The devices are removed from inactiveDevs before coming here. We need to restore if going on error path.
} else { VIR_FREE(dev->stubDriver); dev->stubDriver = newDriverName; @@ -1371,16 +1386,9 @@ virPCIDeviceDetach(virPCIDevicePtr dev, return -1; }
- if (virPCIDeviceBindToStub(dev, dev->stubDriver) < 0) - return -1; - - /* Add *a copy of* the dev into list inactiveDevs, if - * it's not already there. - */ - if (inactiveDevs && !virPCIDeviceListFind(inactiveDevs, dev) && - virPCIDeviceListAddCopy(inactiveDevs, dev) < 0) { + if (virPCIDeviceBindToStub(dev, dev->stubDriver, + activeDevs, inactiveDevs) < 0) return -1; - }
return 0; } @@ -1396,13 +1404,9 @@ virPCIDeviceReattach(virPCIDevicePtr dev, return -1; }
- if (virPCIDeviceUnbindFromStub(dev) < 0) + if (virPCIDeviceUnbindFromStub(dev, activeDevs, inactiveDevs) < 0) return -1;
- /* Steal the dev from list inactiveDevs */ - if (inactiveDevs) - virPCIDeviceListDel(inactiveDevs, dev); - return 0; }
Cheers.
-- Andrea Bolognani Software Engineer - Virtualization Team

Author: Shivaprasad G Bhat <sbhat@linux.vnet.ibm.com> The host will crash if a device is bound to host driver when the device belonging to same iommu group is in use by any of the guests. So, do the host driver probe only after all the devices in the iommu group have unbound from the vfio. The patch fixes https://bugzilla.redhat.com/show_bug.cgi?id=1272300 Signed-off-by: Shivaprasad G Bhat <sbhat@linux.vnet.ibm.com> --- src/util/virpci.c | 50 +++++++++++++++++++++++++++++++++++++++++++++----- 1 file changed, 45 insertions(+), 5 deletions(-) diff --git a/src/util/virpci.c b/src/util/virpci.c index 6c24a81..425c1a7 100644 --- a/src/util/virpci.c +++ b/src/util/virpci.c @@ -1128,6 +1128,23 @@ static int virPCIDeviceReprobeHostDriver(virPCIDevicePtr dev, char *driver, char return result; } +static int virPCIDeviceBoundToVFIODriver(virPCIDeviceAddressPtr devAddr, void *opaque ATTRIBUTE_UNUSED) +{ + int ret = 0; + virPCIDevicePtr pci = NULL; + + if (!(pci = virPCIDeviceNew(devAddr->domain, devAddr->bus, + devAddr->slot, devAddr->function))) + goto cleanup; + + if (STREQ_NULLABLE(pci->stubDriver, "vfio-pci")) + ret = -1; + + cleanup: + virPCIDeviceFree(pci); + return ret; +} + static int virPCIDeviceUnbindFromStub(virPCIDevicePtr dev, virPCIDeviceListPtr activeDevs ATTRIBUTE_UNUSED, @@ -1177,11 +1194,34 @@ virPCIDeviceUnbindFromStub(virPCIDevicePtr dev, dev->remove_slot = false; reprobe: - if (virPCIDeviceReprobeHostDriver(dev, driver, drvdir) < 0) - goto cleanup; - /* Steal the dev from list inactiveDevs */ - if (inactiveDevs) - virPCIDeviceListDel(inactiveDevs, dev); + if (STREQ_NULLABLE(dev->stubDriver, "vfio-pci")) { + size_t i = 0; + virPCIDeviceAddress devAddr = { dev->domain, dev->bus, + dev->slot, dev->function }; + if (virPCIDeviceAddressIOMMUGroupIterate(&devAddr, virPCIDeviceBoundToVFIODriver, NULL) < 0) { + result = 0; + goto cleanup; + } + + while (inactiveDevs && (i < virPCIDeviceListCount(inactiveDevs))) { + virPCIDevicePtr pcidev = virPCIDeviceListGet(inactiveDevs, i); + if (dev->iommuGroup == pcidev->iommuGroup) { + if (virPCIDeviceReprobeHostDriver(pcidev, driver, drvdir) < 0) + goto cleanup; + /* Steal the dev from list inactiveDevs */ + virPCIDeviceListDel(inactiveDevs, pcidev); + continue; + } + i++; + } + } else { + if (virPCIDeviceReprobeHostDriver(dev, driver, drvdir) < 0) + goto cleanup; + /* Steal the dev from list inactiveDevs */ + + if (inactiveDevs) + virPCIDeviceListDel(inactiveDevs, dev); + } result = 0;

I know you're already working on a v2 of this, just a couple of quick remarks below. On Fri, 2015-10-30 at 05:01 +0530, Shivaprasad G Bhat wrote:
Author: Shivaprasad G Bhat <sbhat@linux.vnet.ibm.com>
The host will crash if a device is bound to host driver when the device belonging to same iommu group is in use by any of the guests. So, do the host driver probe only after all the devices in the iommu group have unbound from the vfio.
The patch fixes https://bugzilla.redhat.com/show_bug.cgi?id=1272300
Signed-off-by: Shivaprasad G Bhat <sbhat@linux.vnet.ibm.com> --- src/util/virpci.c | 50 +++++++++++++++++++++++++++++++++++++++++++++----- 1 file changed, 45 insertions(+), 5 deletions(-)
diff --git a/src/util/virpci.c b/src/util/virpci.c index 6c24a81..425c1a7 100644 --- a/src/util/virpci.c +++ b/src/util/virpci.c @@ -1128,6 +1128,23 @@ static int virPCIDeviceReprobeHostDriver(virPCIDevicePtr dev, char *driver, char return result; }
+static int virPCIDeviceBoundToVFIODriver(virPCIDeviceAddressPtr devAddr, void *opaque ATTRIBUTE_UNUSED)
Return type on a separate line, please.
+{ + int ret = 0; + virPCIDevicePtr pci = NULL; + + if (!(pci = virPCIDeviceNew(devAddr->domain, devAddr->bus, + devAddr->slot, devAddr->function))) + goto cleanup; + + if (STREQ_NULLABLE(pci->stubDriver, "vfio-pci")) + ret = -1;
As mentioned in the comment for Patch 1, I think this is fairly obscure: if I had not read throught the whole series, I'd assume this checks whether the device is configured to use vfio-pci as stub driver, not whether it is currently bound to it, and it would not be immediately clear to me how this check fits in a function called virPCIDeviceBoundToVFIODriver(). I think it would be much cleaner to query the driver explicitly using virPCIDeviceGetDriverPathAndName() and remove that call from virPCIDeviceNew().
+ + cleanup: + virPCIDeviceFree(pci); + return ret; +} + static int virPCIDeviceUnbindFromStub(virPCIDevicePtr dev, virPCIDeviceListPtr activeDevs ATTRIBUTE_UNUSED, @@ -1177,11 +1194,34 @@ virPCIDeviceUnbindFromStub(virPCIDevicePtr dev, dev->remove_slot = false;
reprobe: - if (virPCIDeviceReprobeHostDriver(dev, driver, drvdir) < 0) - goto cleanup; - /* Steal the dev from list inactiveDevs */ - if (inactiveDevs) - virPCIDeviceListDel(inactiveDevs, dev); + if (STREQ_NULLABLE(dev->stubDriver, "vfio-pci")) { + size_t i = 0; + virPCIDeviceAddress devAddr = { dev->domain, dev->bus, + dev->slot, dev->function }; + if (virPCIDeviceAddressIOMMUGroupIterate(&devAddr, virPCIDeviceBoundToVFIODriver, NULL) < 0) { + result = 0; + goto cleanup; + } + + while (inactiveDevs && (i < virPCIDeviceListCount(inactiveDevs))) { + virPCIDevicePtr pcidev = virPCIDeviceListGet(inactiveDevs, i); + if (dev->iommuGroup == pcidev->iommuGroup) { + if (virPCIDeviceReprobeHostDriver(pcidev, driver, drvdir) < 0) + goto cleanup; + /* Steal the dev from list inactiveDevs */ + virPCIDeviceListDel(inactiveDevs, pcidev); + continue; + } + i++; + } + } else { + if (virPCIDeviceReprobeHostDriver(dev, driver, drvdir) < 0) + goto cleanup; + /* Steal the dev from list inactiveDevs */ + + if (inactiveDevs) + virPCIDeviceListDel(inactiveDevs, dev);
Either put the empty line before the comment or just get rid of it.
+ }
result = 0;
Cheers. -- Andrea Bolognani Software Engineer - Virtualization Team

Author: Shivaprasad G Bhat <sbhat@linux.vnet.ibm.com> There could be a delay of 1 or 2 seconds before the vfio-pci driver is unbound and the device file /dev/vfio/<iommu> is actually removed. If the file exists, the host driver probing the device can lead to crash. So, wait and avoid the crash. Setting the timeout to 15 seconds for now. Signed-off-by: Shivaprasad G Bhat <sbhat@linux.vnet.ibm.com> --- src/util/virpci.c | 39 +++++++++++++++++++++++++++++++++++++++ 1 file changed, 39 insertions(+) diff --git a/src/util/virpci.c b/src/util/virpci.c index 425c1a7..6bf640d 100644 --- a/src/util/virpci.c +++ b/src/util/virpci.c @@ -1097,6 +1097,42 @@ static bool virPCIIsAKnownStub(char *driver) return ret; } +#define VFIO_UNBIND_TIMEOUT 15 + +/* It is not safe to initiate host driver probe if the vfio driver has not + * completely unbound the device. + * So, return if the unbind didn't complete in 15 seconds. + */ +static int virPCIWaitForVfioUnbindCompletion(virPCIDevicePtr dev) +{ + int retry = 0; + int ret = -1; + char *path = NULL; + + if (!(path = virPCIDeviceGetIOMMUGroupDev(dev))) + goto cleanup; + + while (retry++ < VFIO_UNBIND_TIMEOUT) { + if (!virFileExists(path)) + break; + sleep(1); + } + + if (virFileExists(path)) { + virReportError(VIR_ERR_INTERNAL_ERROR, + _("The VFIO unbind not completed even after %d seconds for device %.4x:%.2x:%.2x.%.1x"), + retry, dev->domain, dev->bus, dev->slot, dev->function); + goto cleanup; + } + + ret = 0; +cleanup : + VIR_FREE(path); + return ret; + +} + + static int virPCIDeviceReprobeHostDriver(virPCIDevicePtr dev, char *driver, char *drvdir) { char *path = NULL; @@ -1203,6 +1239,9 @@ virPCIDeviceUnbindFromStub(virPCIDevicePtr dev, goto cleanup; } + if (virPCIWaitForVfioUnbindCompletion(dev) < 0) + goto cleanup; + while (inactiveDevs && (i < virPCIDeviceListCount(inactiveDevs))) { virPCIDevicePtr pcidev = virPCIDeviceListGet(inactiveDevs, i); if (dev->iommuGroup == pcidev->iommuGroup) {

On Fri, 2015-10-30 at 05:04 +0530, Shivaprasad G Bhat wrote:
Author: Shivaprasad G Bhat <sbhat@linux.vnet.ibm.com>
This line is redundant since the information is already stored in git, plus you have the Signed-off-by below. This applies to Patch 7 as well.
There could be a delay of 1 or 2 seconds before the vfio-pci driver is unbound and the device file /dev/vfio/<iommu> is actually removed. If the file exists, the host driver probing the device can lead to crash. So, wait and avoid the crash. Setting the timeout to 15 seconds for now.
Signed-off-by: Shivaprasad G Bhat <sbhat@linux.vnet.ibm.com> --- src/util/virpci.c | 39 +++++++++++++++++++++++++++++++++++++++ 1 file changed, 39 insertions(+)
diff --git a/src/util/virpci.c b/src/util/virpci.c index 425c1a7..6bf640d 100644 --- a/src/util/virpci.c +++ b/src/util/virpci.c @@ -1097,6 +1097,42 @@ static bool virPCIIsAKnownStub(char *driver) return ret; }
+#define VFIO_UNBIND_TIMEOUT 15
There's one trailing space here, and git complains about it when applying the patch. syntax-check complains too.
+ +/* It is not safe to initiate host driver probe if the vfio driver has not + * completely unbound the device. + * So, return if the unbind didn't complete in 15 seconds. + */ +static int virPCIWaitForVfioUnbindCompletion(virPCIDevicePtr dev)
Return type on a separate line, please. VFIO is an acronym, like PCI, and should always be all-caps.
+{ + int retry = 0; + int ret = -1; + char *path = NULL; + + if (!(path = virPCIDeviceGetIOMMUGroupDev(dev))) + goto cleanup; + + while (retry++ < VFIO_UNBIND_TIMEOUT) { + if (!virFileExists(path)) + break; + sleep(1);
Do we want this to be in seconds, or would a higher granularity be better?
+ } + + if (virFileExists(path)) { + virReportError(VIR_ERR_INTERNAL_ERROR, + _("The VFIO unbind not completed even after %d seconds for device %.4x:%.2x:%.2x.%.1x"), + retry, dev->domain, dev->bus, dev->slot, dev->function);
This line is very long, please split the error message into two parts. I'd say "The" and "even" can be dropped. You should use dev->name instead of formatting the name yourself.
+ goto cleanup; + } + + ret = 0; +cleanup :
Please remove the space between the label and the semicolon, and add one in front of the label. I'd also add an empty line before the label.
+ VIR_FREE(path); + return ret; + +} + + static int virPCIDeviceReprobeHostDriver(virPCIDevicePtr dev, char *driver, char *drvdir) { char *path = NULL; @@ -1203,6 +1239,9 @@ virPCIDeviceUnbindFromStub(virPCIDevicePtr dev, goto cleanup; }
+ if (virPCIWaitForVfioUnbindCompletion(dev) < 0) + goto cleanup; + while (inactiveDevs && (i < virPCIDeviceListCount(inactiveDevs))) { virPCIDevicePtr pcidev = virPCIDeviceListGet(inactiveDevs, i); if (dev->iommuGroup == pcidev->iommuGroup) {
Cheers. -- Andrea Bolognani Software Engineer - Virtualization Team
participants (4)
-
Andrea Bolognani
-
Shivaprasad bhat
-
Shivaprasad G Bhat
-
Shivaprasad G Bhat