
On Mon, Feb 18, 2013 at 05:07:44PM +0100, Jiri Denemark wrote:
To avoid having to hold the qemu driver lock while iterating through close callbacks and calling them. This fixes a real deadlock when a domain which is being migrated from another host gets autodestoyed as a result of broken connection to the other host.
Since you're turning this into a full object, I think it would be nice to move it to a standalone file, so it can be reused by the LXC driver, which currently re-invents this same kind of code.
--- src/qemu/qemu_conf.c | 155 ++++++++++++++++++++++++++++++---------------- src/qemu/qemu_conf.h | 39 ++++++------ src/qemu/qemu_driver.c | 12 ++-- src/qemu/qemu_migration.c | 6 +- src/qemu/qemu_process.c | 10 +-- 5 files changed, 136 insertions(+), 86 deletions(-)
diff --git a/src/qemu/qemu_conf.c b/src/qemu/qemu_conf.c index 4ff912d..edadf36 100644 --- a/src/qemu/qemu_conf.c +++ b/src/qemu/qemu_conf.c @@ -57,8 +57,25 @@
#define VIR_FROM_THIS VIR_FROM_QEMU
+typedef struct _qemuDriverCloseDef qemuDriverCloseDef; +typedef qemuDriverCloseDef *qemuDriverCloseDefPtr; +struct _qemuDriverCloseDef { + virConnectPtr conn; + virQEMUCloseCallback cb; +}; + +struct _virQEMUCloseCallbacks { + virObjectLockable parent; + + /* UUID string to qemuDriverCloseDef mapping */ + virHashTablePtr list; +}; + + static virClassPtr virQEMUDriverConfigClass; +static virClassPtr virQEMUCloseCallbacksClass; static void virQEMUDriverConfigDispose(void *obj); +static void virQEMUCloseCallbacksDispose(void *obj);
static int virQEMUConfigOnceInit(void) { @@ -71,13 +88,21 @@ static int virQEMUConfigOnceInit(void) return 0; }
-VIR_ONCE_GLOBAL_INIT(virQEMUConfig) +static int +virQEMUCloseCallbacksOnceInit(void) +{ + virQEMUCloseCallbacksClass = virClassNew(virClassForObjectLockable(), + "virQEMUCloseCallbacks", + sizeof(virQEMUCloseCallbacks), + virQEMUCloseCallbacksDispose); + if (!virQEMUCloseCallbacksClass) + return -1; + return 0; +}
+VIR_ONCE_GLOBAL_INIT(virQEMUConfig) +VIR_ONCE_GLOBAL_INIT(virQEMUCloseCallbacks)
No need for two initializers, just make the virQEMUConfigOnceInit method create both classes.
@@ -639,44 +664,59 @@ virCapsPtr virQEMUDriverGetCapabilities(virQEMUDriverPtr driver,
static void -qemuDriverCloseCallbackFree(void *payload, - const void *name ATTRIBUTE_UNUSED) +virQEMUCloseCallbacksFreeData(void *payload, + const void *name ATTRIBUTE_UNUSED) { VIR_FREE(payload); }
-int -qemuDriverCloseCallbackInit(virQEMUDriverPtr driver) +virQEMUCloseCallbacksPtr +virQEMUCloseCallbacksNew(void) { - driver->closeCallbacks = virHashCreate(5, qemuDriverCloseCallbackFree); - if (!driver->closeCallbacks) - return -1; + virQEMUCloseCallbacksPtr closeCallbacks;
- return 0; + if (virQEMUCloseCallbacksInitialize() < 0) + return NULL; + + if (!(closeCallbacks = virObjectLockableNew(virQEMUCloseCallbacksClass))) + return NULL; + + closeCallbacks->list = virHashCreate(5, virQEMUCloseCallbacksFreeData); + if (!closeCallbacks->list) { + virObjectUnref(closeCallbacks); + return NULL; + } + + return closeCallbacks; }
-void -qemuDriverCloseCallbackShutdown(virQEMUDriverPtr driver) +static void +virQEMUCloseCallbacksDispose(void *obj) { - virHashFree(driver->closeCallbacks); + virQEMUCloseCallbacksPtr closeCallbacks = obj; + + virHashFree(closeCallbacks->list); }
int -qemuDriverCloseCallbackSet(virQEMUDriverPtr driver, - virDomainObjPtr vm, - virConnectPtr conn, - qemuDriverCloseCallback cb) +virQEMUCloseCallbacksSet(virQEMUDriverPtr driver, + virDomainObjPtr vm, + virConnectPtr conn, + virQEMUCloseCallback cb) { char uuidstr[VIR_UUID_STRING_BUFLEN]; qemuDriverCloseDefPtr closeDef; + virHashTablePtr list; int ret = -1;
- qemuDriverLock(driver); virUUIDFormat(vm->def->uuid, uuidstr); VIR_DEBUG("vm=%s, uuid=%s, conn=%p, cb=%p", vm->def->name, uuidstr, conn, cb);
- closeDef = virHashLookup(driver->closeCallbacks, uuidstr); + virObjectLock(driver->closeCallbacks); + + list = driver->closeCallbacks->list; + closeDef = virHashLookup(list, uuidstr); if (closeDef) { if (closeDef->conn != conn) { virReportError(VIR_ERR_INTERNAL_ERROR, @@ -701,7 +741,7 @@ qemuDriverCloseCallbackSet(virQEMUDriverPtr driver,
closeDef->conn = conn; closeDef->cb = cb; - if (virHashAddEntry(driver->closeCallbacks, uuidstr, closeDef) < 0) { + if (virHashAddEntry(list, uuidstr, closeDef) < 0) { VIR_FREE(closeDef); goto cleanup; } @@ -709,25 +749,28 @@ qemuDriverCloseCallbackSet(virQEMUDriverPtr driver,
ret = 0; cleanup: - qemuDriverUnlock(driver); + virObjectUnlock(driver->closeCallbacks); return ret; }
int -qemuDriverCloseCallbackUnset(virQEMUDriverPtr driver, - virDomainObjPtr vm, - qemuDriverCloseCallback cb) +virQEMUCloseCallbacksUnset(virQEMUDriverPtr driver, + virDomainObjPtr vm, + virQEMUCloseCallback cb) { char uuidstr[VIR_UUID_STRING_BUFLEN]; qemuDriverCloseDefPtr closeDef; + virHashTablePtr list; int ret = -1;
- qemuDriverLock(driver); virUUIDFormat(vm->def->uuid, uuidstr); VIR_DEBUG("vm=%s, uuid=%s, cb=%p", vm->def->name, uuidstr, cb);
- closeDef = virHashLookup(driver->closeCallbacks, uuidstr); + virObjectLock(driver->closeCallbacks); + + list = driver->closeCallbacks->list; + closeDef = virHashLookup(list, uuidstr); if (!closeDef) goto cleanup;
@@ -738,46 +781,49 @@ qemuDriverCloseCallbackUnset(virQEMUDriverPtr driver, goto cleanup; }
- ret = virHashRemoveEntry(driver->closeCallbacks, uuidstr); + ret = virHashRemoveEntry(list, uuidstr); cleanup: - qemuDriverUnlock(driver); + virObjectUnlock(driver->closeCallbacks); return ret; }
-qemuDriverCloseCallback -qemuDriverCloseCallbackGet(virQEMUDriverPtr driver, - virDomainObjPtr vm, - virConnectPtr conn) +virQEMUCloseCallback +virQEMUCloseCallbacksGet(virQEMUDriverPtr driver, + virDomainObjPtr vm, + virConnectPtr conn) { char uuidstr[VIR_UUID_STRING_BUFLEN]; qemuDriverCloseDefPtr closeDef; - qemuDriverCloseCallback cb = NULL; + virQEMUCloseCallback cb = NULL;
- qemuDriverLock(driver); virUUIDFormat(vm->def->uuid, uuidstr); VIR_DEBUG("vm=%s, uuid=%s, conn=%p", vm->def->name, uuidstr, conn);
- closeDef = virHashLookup(driver->closeCallbacks, uuidstr); + virObjectLock(driver->closeCallbacks); + + closeDef = virHashLookup(driver->closeCallbacks->list, uuidstr); if (closeDef && (!conn || closeDef->conn == conn)) cb = closeDef->cb;
+ virObjectUnlock(driver->closeCallbacks); + VIR_DEBUG("cb=%p", cb); - qemuDriverUnlock(driver); return cb; }
-struct qemuDriverCloseCallbackData { +struct virQEMUCloseCallbacksData { + virHashTablePtr list; virQEMUDriverPtr driver; virConnectPtr conn; };
static void -qemuDriverCloseCallbackRun(void *payload, - const void *name, - void *opaque) +virQEMUCloseCallbacksRunOne(void *payload, + const void *name, + void *opaque) { - struct qemuDriverCloseCallbackData *data = opaque; + struct virQEMUCloseCallbacksData *data = opaque; qemuDriverCloseDefPtr closeDef = payload; unsigned char uuid[VIR_UUID_BUFLEN]; char uuidstr[VIR_UUID_STRING_BUFLEN]; @@ -808,20 +854,25 @@ qemuDriverCloseCallbackRun(void *payload, if (dom) virObjectUnlock(dom);
- virHashRemoveEntry(data->driver->closeCallbacks, uuidstr); + virHashRemoveEntry(data->list, uuidstr); }
void -qemuDriverCloseCallbackRunAll(virQEMUDriverPtr driver, - virConnectPtr conn) +virQEMUCloseCallbacksRun(virQEMUDriverPtr driver, + virConnectPtr conn) { - struct qemuDriverCloseCallbackData data = { - driver, conn + struct virQEMUCloseCallbacksData data = { + .driver = driver, + .conn = conn }; + VIR_DEBUG("conn=%p", conn); - qemuDriverLock(driver); - virHashForEach(driver->closeCallbacks, qemuDriverCloseCallbackRun, &data); - qemuDriverUnlock(driver); + virObjectLock(driver->closeCallbacks); + + data.list = driver->closeCallbacks->list; + virHashForEach(data.list, virQEMUCloseCallbacksRunOne, &data); + + virObjectUnlock(driver->closeCallbacks); }
/* Construct the hash key for sharedDisks as "major:minor" */ diff --git a/src/qemu/qemu_conf.h b/src/qemu/qemu_conf.h index 14f1427..72380dc 100644 --- a/src/qemu/qemu_conf.h +++ b/src/qemu/qemu_conf.h @@ -47,8 +47,8 @@
# define QEMUD_CPUMASK_LEN CPU_SETSIZE
-typedef struct _qemuDriverCloseDef qemuDriverCloseDef; -typedef qemuDriverCloseDef *qemuDriverCloseDefPtr; +typedef struct _virQEMUCloseCallbacks virQEMUCloseCallbacks; +typedef virQEMUCloseCallbacks *virQEMUCloseCallbacksPtr;
typedef struct _virQEMUDriver virQEMUDriver; typedef virQEMUDriver *virQEMUDriverPtr; @@ -216,8 +216,8 @@ struct _virQEMUDriver { /* Immutable pointer. lockless access */ virLockManagerPluginPtr lockManager;
- /* Immutable pointer. Unsafe APIs. XXX */ - virHashTablePtr closeCallbacks; + /* Immutable pointer, self-clocking APIs */ + virQEMUCloseCallbacksPtr closeCallbacks; };
typedef struct _qemuDomainCmdlineDef qemuDomainCmdlineDef; @@ -254,23 +254,22 @@ struct qemuDomainDiskInfo { int io_status; };
-typedef virDomainObjPtr (*qemuDriverCloseCallback)(virQEMUDriverPtr driver, - virDomainObjPtr vm, - virConnectPtr conn); -int qemuDriverCloseCallbackInit(virQEMUDriverPtr driver); -void qemuDriverCloseCallbackShutdown(virQEMUDriverPtr driver); -int qemuDriverCloseCallbackSet(virQEMUDriverPtr driver, +typedef virDomainObjPtr (*virQEMUCloseCallback)(virQEMUDriverPtr driver, + virDomainObjPtr vm, + virConnectPtr conn); +virQEMUCloseCallbacksPtr virQEMUCloseCallbacksNew(void); +int virQEMUCloseCallbacksSet(virQEMUDriverPtr driver,
I was rather expecting to see the virQEMUDriverPtr replacd with a virQEMUCloseCallbacksPtr in all the methods now it becomes a full object.
+ virDomainObjPtr vm, + virConnectPtr conn, + virQEMUCloseCallback cb); +int virQEMUCloseCallbacksUnset(virQEMUDriverPtr driver, virDomainObjPtr vm, - virConnectPtr conn, - qemuDriverCloseCallback cb); -int qemuDriverCloseCallbackUnset(virQEMUDriverPtr driver, - virDomainObjPtr vm, - qemuDriverCloseCallback cb); -qemuDriverCloseCallback qemuDriverCloseCallbackGet(virQEMUDriverPtr driver, - virDomainObjPtr vm, - virConnectPtr conn); -void qemuDriverCloseCallbackRunAll(virQEMUDriverPtr driver, - virConnectPtr conn); + virQEMUCloseCallback cb); +virQEMUCloseCallback virQEMUCloseCallbacksGet(virQEMUDriverPtr driver, + virDomainObjPtr vm, + virConnectPtr conn); +void virQEMUCloseCallbacksRun(virQEMUDriverPtr driver, + virConnectPtr conn);
int qemuAddSharedDisk(virQEMUDriverPtr driver, const char *disk_path)
ACK to the general approach, but I think there's a few tweaks needed still. Daniel -- |: http://berrange.com -o- http://www.flickr.com/photos/dberrange/ :| |: http://libvirt.org -o- http://virt-manager.org :| |: http://autobuild.org -o- http://search.cpan.org/~danberr/ :| |: http://entangle-photo.org -o- http://live.gnome.org/gtk-vnc :|