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.
---
Notes:
Version 2:
- merge initializers
- replace virQEMUDriverPtr with virQEMUCloseCallbacksPtr in all
virQEMUCloseCallbacks* APIs
src/qemu/qemu_conf.c | 158 +++++++++++++++++++++++++++++-----------------
src/qemu/qemu_conf.h | 41 ++++++------
src/qemu/qemu_driver.c | 16 +++--
src/qemu/qemu_migration.c | 7 +-
src/qemu/qemu_process.c | 11 ++--
5 files changed, 140 insertions(+), 93 deletions(-)
diff --git a/src/qemu/qemu_conf.c b/src/qemu/qemu_conf.c
index 4ff912d..f3c3cf3 100644
--- a/src/qemu/qemu_conf.c
+++ b/src/qemu/qemu_conf.c
@@ -57,28 +57,47 @@
#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)
{
- if (!(virQEMUDriverConfigClass = virClassNew(virClassForObject(),
- "virQEMUDriverConfig",
- sizeof(virQEMUDriverConfig),
- virQEMUDriverConfigDispose)))
- return -1;
+ virQEMUDriverConfigClass = virClassNew(virClassForObject(),
+ "virQEMUDriverConfig",
+ sizeof(virQEMUDriverConfig),
+ virQEMUDriverConfigDispose);
- return 0;
+ virQEMUCloseCallbacksClass = virClassNew(virClassForObjectLockable(),
+ "virQEMUCloseCallbacks",
+ sizeof(virQEMUCloseCallbacks),
+ virQEMUCloseCallbacksDispose);
+
+ if (!virQEMUDriverConfigClass || !virQEMUCloseCallbacksClass)
+ return -1;
+ else
+ return 0;
}
VIR_ONCE_GLOBAL_INIT(virQEMUConfig)
-struct _qemuDriverCloseDef {
- virConnectPtr conn;
- qemuDriverCloseCallback cb;
-};
-
static void
qemuDriverLock(virQEMUDriverPtr driver)
{
@@ -639,44 +658,57 @@ 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 (virQEMUConfigInitialize() < 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(virQEMUCloseCallbacksPtr closeCallbacks,
+ virDomainObjPtr vm,
+ virConnectPtr conn,
+ virQEMUCloseCallback cb)
{
char uuidstr[VIR_UUID_STRING_BUFLEN];
qemuDriverCloseDefPtr closeDef;
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(closeCallbacks);
+
+ closeDef = virHashLookup(closeCallbacks->list, uuidstr);
if (closeDef) {
if (closeDef->conn != conn) {
virReportError(VIR_ERR_INTERNAL_ERROR,
@@ -701,7 +733,7 @@ qemuDriverCloseCallbackSet(virQEMUDriverPtr driver,
closeDef->conn = conn;
closeDef->cb = cb;
- if (virHashAddEntry(driver->closeCallbacks, uuidstr, closeDef) < 0) {
+ if (virHashAddEntry(closeCallbacks->list, uuidstr, closeDef) < 0) {
VIR_FREE(closeDef);
goto cleanup;
}
@@ -709,25 +741,26 @@ qemuDriverCloseCallbackSet(virQEMUDriverPtr driver,
ret = 0;
cleanup:
- qemuDriverUnlock(driver);
+ virObjectUnlock(closeCallbacks);
return ret;
}
int
-qemuDriverCloseCallbackUnset(virQEMUDriverPtr driver,
- virDomainObjPtr vm,
- qemuDriverCloseCallback cb)
+virQEMUCloseCallbacksUnset(virQEMUCloseCallbacksPtr closeCallbacks,
+ virDomainObjPtr vm,
+ virQEMUCloseCallback cb)
{
char uuidstr[VIR_UUID_STRING_BUFLEN];
qemuDriverCloseDefPtr closeDef;
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(closeCallbacks);
+
+ closeDef = virHashLookup(closeCallbacks->list, uuidstr);
if (!closeDef)
goto cleanup;
@@ -738,46 +771,49 @@ qemuDriverCloseCallbackUnset(virQEMUDriverPtr driver,
goto cleanup;
}
- ret = virHashRemoveEntry(driver->closeCallbacks, uuidstr);
+ ret = virHashRemoveEntry(closeCallbacks->list, uuidstr);
cleanup:
- qemuDriverUnlock(driver);
+ virObjectUnlock(closeCallbacks);
return ret;
}
-qemuDriverCloseCallback
-qemuDriverCloseCallbackGet(virQEMUDriverPtr driver,
- virDomainObjPtr vm,
- virConnectPtr conn)
+virQEMUCloseCallback
+virQEMUCloseCallbacksGet(virQEMUCloseCallbacksPtr closeCallbacks,
+ 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(closeCallbacks);
+
+ closeDef = virHashLookup(closeCallbacks->list, uuidstr);
if (closeDef && (!conn || closeDef->conn == conn))
cb = closeDef->cb;
+ virObjectUnlock(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 +844,26 @@ qemuDriverCloseCallbackRun(void *payload,
if (dom)
virObjectUnlock(dom);
- virHashRemoveEntry(data->driver->closeCallbacks, uuidstr);
+ virHashRemoveEntry(data->list, uuidstr);
}
void
-qemuDriverCloseCallbackRunAll(virQEMUDriverPtr driver,
- virConnectPtr conn)
+virQEMUCloseCallbacksRun(virQEMUCloseCallbacksPtr closeCallbacks,
+ virConnectPtr conn,
+ virQEMUDriverPtr driver)
{
- 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(closeCallbacks);
+
+ data.list = closeCallbacks->list;
+ virHashForEach(data.list, virQEMUCloseCallbacksRunOne, &data);
+
+ virObjectUnlock(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 ea4f393..c6bc883 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,24 @@ 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(virQEMUCloseCallbacksPtr closeCallbacks,
+ virDomainObjPtr vm,
+ virConnectPtr conn,
+ virQEMUCloseCallback cb);
+int virQEMUCloseCallbacksUnset(virQEMUCloseCallbacksPtr closeCallbacks,
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(virQEMUCloseCallbacksPtr closeCallbacks,
+ virDomainObjPtr vm,
+ virConnectPtr conn);
+void virQEMUCloseCallbacksRun(virQEMUCloseCallbacksPtr closeCallbacks,
+ virConnectPtr conn,
+ virQEMUDriverPtr driver);
int qemuAddSharedDisk(virQEMUDriverPtr driver,
const char *disk_path)
diff --git a/src/qemu/qemu_driver.c b/src/qemu/qemu_driver.c
index dc35b91..76fbb51 100644
--- a/src/qemu/qemu_driver.c
+++ b/src/qemu/qemu_driver.c
@@ -757,7 +757,7 @@ qemuStartup(bool privileged,
cfg->hugepagePath = mempath;
}
- if (qemuDriverCloseCallbackInit(qemu_driver) < 0)
+ if (!(qemu_driver->closeCallbacks = virQEMUCloseCallbacksNew()))
goto error;
/* Get all the running persistent or transient configs first */
@@ -958,7 +958,7 @@ qemuShutdown(void) {
virSysinfoDefFree(qemu_driver->hostsysinfo);
- qemuDriverCloseCallbackShutdown(qemu_driver);
+ virObjectUnref(qemu_driver->closeCallbacks);
VIR_FREE(qemu_driver->qemuImgBinary);
@@ -1052,11 +1052,12 @@ cleanup:
return ret;
}
-static int qemuClose(virConnectPtr conn) {
+static int qemuClose(virConnectPtr conn)
+{
virQEMUDriverPtr driver = conn->privateData;
/* Get rid of callbacks registered for this conn */
- qemuDriverCloseCallbackRunAll(driver, conn);
+ virQEMUCloseCallbacksRun(driver->closeCallbacks, conn, driver);
conn->privateData = NULL;
@@ -9630,8 +9631,8 @@ qemuDomainMigrateBegin3(virDomainPtr domain,
* This prevents any other APIs being invoked while migration is taking
* place.
*/
- if (qemuDriverCloseCallbackSet(driver, vm, domain->conn,
- qemuMigrationCleanup) < 0)
+ if (virQEMUCloseCallbacksSet(driver->closeCallbacks, vm, domain->conn,
+ qemuMigrationCleanup) < 0)
goto endjob;
if (qemuMigrationJobContinue(vm) == 0) {
vm = NULL;
@@ -9857,7 +9858,8 @@ qemuDomainMigrateConfirm3(virDomainPtr domain,
phase = QEMU_MIGRATION_PHASE_CONFIRM3;
qemuMigrationJobStartPhase(driver, vm, phase);
- qemuDriverCloseCallbackUnset(driver, vm, qemuMigrationCleanup);
+ virQEMUCloseCallbacksUnset(driver->closeCallbacks, vm,
+ qemuMigrationCleanup);
ret = qemuMigrationConfirm(driver, domain->conn, vm,
cookiein, cookieinlen,
diff --git a/src/qemu/qemu_migration.c b/src/qemu/qemu_migration.c
index 36e55d2..8485b20 100644
--- a/src/qemu/qemu_migration.c
+++ b/src/qemu/qemu_migration.c
@@ -3155,7 +3155,8 @@ qemuMigrationPerformPhase(virQEMUDriverPtr driver,
}
qemuMigrationJobStartPhase(driver, vm, QEMU_MIGRATION_PHASE_PERFORM3);
- qemuDriverCloseCallbackUnset(driver, vm, qemuMigrationCleanup);
+ virQEMUCloseCallbacksUnset(driver->closeCallbacks, vm,
+ qemuMigrationCleanup);
resume = virDomainObjGetState(vm, NULL) == VIR_DOMAIN_RUNNING;
ret = doNativeMigrate(driver, vm, uri, cookiein, cookieinlen,
@@ -3186,8 +3187,8 @@ qemuMigrationPerformPhase(virQEMUDriverPtr driver,
qemuMigrationJobSetPhase(driver, vm, QEMU_MIGRATION_PHASE_PERFORM3_DONE);
- if (qemuDriverCloseCallbackSet(driver, vm, conn,
- qemuMigrationCleanup) < 0)
+ if (virQEMUCloseCallbacksSet(driver->closeCallbacks, vm, conn,
+ qemuMigrationCleanup) < 0)
goto endjob;
endjob:
diff --git a/src/qemu/qemu_process.c b/src/qemu/qemu_process.c
index 67362af..0fcc14f 100644
--- a/src/qemu/qemu_process.c
+++ b/src/qemu/qemu_process.c
@@ -4667,22 +4667,23 @@ int qemuProcessAutoDestroyAdd(virQEMUDriverPtr driver,
virConnectPtr conn)
{
VIR_DEBUG("vm=%s, conn=%p", vm->def->name, conn);
- return qemuDriverCloseCallbackSet(driver, vm, conn,
- qemuProcessAutoDestroy);
+ return virQEMUCloseCallbacksSet(driver->closeCallbacks, vm, conn,
+ qemuProcessAutoDestroy);
}
int qemuProcessAutoDestroyRemove(virQEMUDriverPtr driver,
virDomainObjPtr vm)
{
VIR_DEBUG("vm=%s", vm->def->name);
- return qemuDriverCloseCallbackUnset(driver, vm, qemuProcessAutoDestroy);
+ return virQEMUCloseCallbacksUnset(driver->closeCallbacks, vm,
+ qemuProcessAutoDestroy);
}
bool qemuProcessAutoDestroyActive(virQEMUDriverPtr driver,
virDomainObjPtr vm)
{
- qemuDriverCloseCallback cb;
+ virQEMUCloseCallback cb;
VIR_DEBUG("vm=%s", vm->def->name);
- cb = qemuDriverCloseCallbackGet(driver, vm, NULL);
+ cb = virQEMUCloseCallbacksGet(driver->closeCallbacks, vm, NULL);
return cb == qemuProcessAutoDestroy;
}
--
1.8.1.2