Current processing requires a "fire dance" unlocking the @obj, adding an @obj ref, locking the @devs, and relocking @obj in order to ensure proper proper lock ordering. This can be avoided by changing virNodeDeviceObjListRemove to take @name instead of @obj. Then, we can lock the @devs list, look up the @obj by @name (like we do when adding), and remove the @obj from the list. This removes the last reference to the object effectively reaping it. NB: Since prior to calling we remove the reference to the object we cannot pass anything contained within the object (such as the obj->def or obj->def->name) because it's possible that the object could be reaped by two competing remove threads. Signed-off-by: John Ferlan <jferlan@xxxxxxxxxx> --- src/conf/virnodedeviceobj.c | 29 +++++++++++++++++------------ src/conf/virnodedeviceobj.h | 2 +- src/node_device/node_device_hal.c | 16 +++++++++------- src/node_device/node_device_udev.c | 13 +++++++++---- src/test/test_driver.c | 28 ++++++++++++++-------------- 5 files changed, 50 insertions(+), 38 deletions(-) diff --git a/src/conf/virnodedeviceobj.c b/src/conf/virnodedeviceobj.c index ad0f27ee4..9064a6cfb 100644 --- a/src/conf/virnodedeviceobj.c +++ b/src/conf/virnodedeviceobj.c @@ -470,23 +470,28 @@ virNodeDeviceObjListAssignDef(virNodeDeviceObjListPtr devs, } +/* + * virNodeDeviceObjListRemove: + * @devs: list of node device objects + * @name: a node device definition + * + * Find the object by name in the list, remove the object from the + * list hash table, and free the object. + * + * Upon entry it's expected that prior to entry any locks on + * the object related to @name will have been removed. + */ void virNodeDeviceObjListRemove(virNodeDeviceObjListPtr devs, - virNodeDeviceObjPtr obj) + const char *name) { - virNodeDeviceDefPtr def; - - if (!obj) - return; - def = obj->def; + virNodeDeviceObjPtr obj; - virObjectRef(obj); - virObjectUnlock(obj); virObjectRWLockWrite(devs); - virObjectLock(obj); - virHashRemoveEntry(devs->objs, def->name); - virObjectUnlock(obj); - virObjectUnref(obj); + if ((obj = virNodeDeviceObjListFindByNameLocked(devs, name))) { + virHashRemoveEntry(devs->objs, name); + virNodeDeviceObjEndAPI(&obj); + } virObjectRWUnlock(devs); } diff --git a/src/conf/virnodedeviceobj.h b/src/conf/virnodedeviceobj.h index 87f908369..b9752bc62 100644 --- a/src/conf/virnodedeviceobj.h +++ b/src/conf/virnodedeviceobj.h @@ -72,7 +72,7 @@ virNodeDeviceObjListAssignDef(virNodeDeviceObjListPtr devs, void virNodeDeviceObjListRemove(virNodeDeviceObjListPtr devs, - virNodeDeviceObjPtr dev); + const char *name); int virNodeDeviceObjListGetParentHost(virNodeDeviceObjListPtr devs, diff --git a/src/node_device/node_device_hal.c b/src/node_device/node_device_hal.c index 6ad56f416..46a419a6e 100644 --- a/src/node_device/node_device_hal.c +++ b/src/node_device/node_device_hal.c @@ -511,8 +511,8 @@ dev_refresh(const char *udi) /* Simply "rediscover" device -- incrementally handling changes * to sub-capabilities (like net.80203) is nasty ... so avoid it. */ - virNodeDeviceObjListRemove(driver->devs, obj); - virObjectUnref(obj); + virNodeDeviceObjEndAPI(&obj); + virNodeDeviceObjListRemoveDef(driver->devs, name); dev_create(udi); } else { VIR_DEBUG("no device named %s", name); @@ -536,12 +536,14 @@ device_removed(LibHalContext *ctx ATTRIBUTE_UNUSED, virNodeDeviceObjPtr obj; obj = virNodeDeviceObjListFindByName(driver->devs, name); - VIR_DEBUG("%s", name); - if (obj) - virNodeDeviceObjListRemove(driver->devs, obj); - else + if (!obj) { VIR_DEBUG("no device named %s", name); - virObjectUnref(obj); + return; + } + + VIR_DEBUG("%s", name); + virNodeDeviceObjEndAPI(&obj); + virNodeDeviceObjListRemove(driver->devs, name); } diff --git a/src/node_device/node_device_udev.c b/src/node_device/node_device_udev.c index e87eb32a8..f6d223fe4 100644 --- a/src/node_device/node_device_udev.c +++ b/src/node_device/node_device_udev.c @@ -1282,6 +1282,7 @@ udevRemoveOneDevice(struct udev_device *device) virNodeDeviceDefPtr def; virObjectEventPtr event = NULL; const char *name = NULL; + char *defname = NULL; name = udev_device_get_syspath(device); if (!(obj = virNodeDeviceObjListFindBySysfsPath(driver->devs, name))) { @@ -1290,15 +1291,19 @@ udevRemoveOneDevice(struct udev_device *device) return -1; } def = virNodeDeviceObjGetDef(obj); + if (VIR_STRDUP(defname, def->name) < 0) { + virNodeDeviceObjEndAPI(&obj); + return -1; + } event = virNodeDeviceEventLifecycleNew(def->name, VIR_NODE_DEVICE_EVENT_DELETED, 0); - VIR_DEBUG("Removing device '%s' with sysfs path '%s'", - def->name, name); - virNodeDeviceObjListRemove(driver->devs, obj); - virObjectUnref(obj); + VIR_DEBUG("Removing device '%s' with sysfs path '%s'", defname, name); + virNodeDeviceObjEndAPI(&obj); + virNodeDeviceObjListRemove(driver->devs, defname); + VIR_FREE(defname); if (event) virObjectEventStateQueue(driver->nodeDeviceEventState, event); diff --git a/src/test/test_driver.c b/src/test/test_driver.c index ddddd8dcb..568d537e9 100644 --- a/src/test/test_driver.c +++ b/src/test/test_driver.c @@ -4723,8 +4723,8 @@ testDestroyVport(testDriverPtr privconn, VIR_NODE_DEVICE_EVENT_DELETED, 0); - virNodeDeviceObjListRemove(privconn->devs, obj); - virObjectUnref(obj); + virNodeDeviceObjEndAPI(&obj); + virNodeDeviceObjListRemove(privconn->devs, "scsi_host12"); testObjectEventQueue(privconn, event); return 0; @@ -5670,6 +5670,7 @@ testNodeDeviceDestroy(virNodeDevicePtr dev) virNodeDeviceObjPtr obj = NULL; virNodeDeviceObjPtr parentobj = NULL; virNodeDeviceDefPtr def; + char *parent = NULL; char *wwnn = NULL, *wwpn = NULL; virObjectEventPtr event = NULL; @@ -5681,18 +5682,19 @@ testNodeDeviceDestroy(virNodeDevicePtr dev) goto cleanup; /* Unlike the real code we cannot run into the udevAddOneDevice race - * which would replace obj->def, so no need to save off the parent, - * but do need to drop the @obj lock so that the FindByName code doesn't - * deadlock on ourselves */ - virObjectUnlock(obj); + * which would replace obj->def, but we still need to save off the + * parent name since we're about to Unref and Unlock the @obj containing + * the @def so that we don't deadlock in virNodeDeviceObjListFindByName. */ + if (VIR_STRDUP(parent, def->parent) < 0) + goto cleanup; + + virNodeDeviceObjEndAPI(&obj); /* We do this just for basic validation and throw away the parentobj * since there's no vport_delete to be run */ - if (!(parentobj = virNodeDeviceObjListFindByName(driver->devs, - def->parent))) { + if (!(parentobj = virNodeDeviceObjListFindByName(driver->devs, parent))) { virReportError(VIR_ERR_INTERNAL_ERROR, - _("cannot find parent '%s' definition"), def->parent); - virObjectLock(obj); + _("cannot find parent '%s' definition"), parent); goto cleanup; } virNodeDeviceObjEndAPI(&parentobj); @@ -5701,16 +5703,14 @@ testNodeDeviceDestroy(virNodeDevicePtr dev) VIR_NODE_DEVICE_EVENT_DELETED, 0); - virObjectLock(obj); - virNodeDeviceObjListRemove(driver->devs, obj); - virObjectUnref(obj); - obj = NULL; + virNodeDeviceObjListRemove(driver->devs, dev->name); cleanup: virNodeDeviceObjEndAPI(&obj); testObjectEventQueue(driver, event); VIR_FREE(wwnn); VIR_FREE(wwpn); + VIR_FREE(parent); return ret; } -- 2.13.6 -- libvir-list mailing list libvir-list@xxxxxxxxxx https://www.redhat.com/mailman/listinfo/libvir-list