mirror of
https://gitlab.com/libvirt/libvirt.git
synced 2025-02-22 19:32:19 +00:00
Fix deadlock in QEMU close callback APIs
There is a lock ordering problem in the QEMU close callback APIs. When starting a guest we have a lock on the VM. We then set a autodestroy callback, which acquires a lock on the close callbacks. When running auto-destroy, we obtain a lock on the close callbacks, then run each callbacks - which obtains a lock on the VM. This causes deadlock if anyone tries to start a VM, while autodestroy is taking place. The fix is to do autodestroy in 2 phases. First obtain all the callbacks and remove them from the list under the close callback lock. Then invoke each callback from outside the close callback lock. Signed-off-by: Daniel P. Berrange <berrange@redhat.com>
This commit is contained in:
parent
7ccad0b16d
commit
96b893f092
@ -797,20 +797,37 @@ virQEMUCloseCallbacksGet(virQEMUCloseCallbacksPtr closeCallbacks,
|
|||||||
return cb;
|
return cb;
|
||||||
}
|
}
|
||||||
|
|
||||||
struct virQEMUCloseCallbacksData {
|
|
||||||
virHashTablePtr list;
|
typedef struct _virQEMUCloseCallbacksListEntry virQEMUCloseCallbacksListEntry;
|
||||||
virQEMUDriverPtr driver;
|
typedef virQEMUCloseCallbacksListEntry *virQEMUCloseCallbacksListEntryPtr;
|
||||||
virConnectPtr conn;
|
struct _virQEMUCloseCallbacksListEntry {
|
||||||
|
unsigned char uuid[VIR_UUID_BUFLEN];
|
||||||
|
virQEMUCloseCallback callback;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
|
typedef struct _virQEMUCloseCallbacksList virQEMUCloseCallbacksList;
|
||||||
|
typedef virQEMUCloseCallbacksList *virQEMUCloseCallbacksListPtr;
|
||||||
|
struct _virQEMUCloseCallbacksList {
|
||||||
|
size_t nentries;
|
||||||
|
virQEMUCloseCallbacksListEntryPtr entries;
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
struct virQEMUCloseCallbacksData {
|
||||||
|
virConnectPtr conn;
|
||||||
|
virQEMUCloseCallbacksListPtr list;
|
||||||
|
bool oom;
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
static void
|
static void
|
||||||
virQEMUCloseCallbacksRunOne(void *payload,
|
virQEMUCloseCallbacksGetOne(void *payload,
|
||||||
const void *key,
|
const void *key,
|
||||||
void *opaque)
|
void *opaque)
|
||||||
{
|
{
|
||||||
struct virQEMUCloseCallbacksData *data = opaque;
|
struct virQEMUCloseCallbacksData *data = opaque;
|
||||||
qemuDriverCloseDefPtr closeDef = payload;
|
qemuDriverCloseDefPtr closeDef = payload;
|
||||||
virDomainObjPtr dom;
|
|
||||||
const char *uuidstr = key;
|
const char *uuidstr = key;
|
||||||
unsigned char uuid[VIR_UUID_BUFLEN];
|
unsigned char uuid[VIR_UUID_BUFLEN];
|
||||||
|
|
||||||
@ -823,35 +840,90 @@ virQEMUCloseCallbacksRunOne(void *payload,
|
|||||||
if (data->conn != closeDef->conn || !closeDef->cb)
|
if (data->conn != closeDef->conn || !closeDef->cb)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
if (!(dom = virDomainObjListFindByUUID(data->driver->domains, uuid))) {
|
if (VIR_EXPAND_N(data->list->entries,
|
||||||
VIR_DEBUG("No domain object with UUID %s", uuidstr);
|
data->list->nentries, 1) < 0) {
|
||||||
|
data->oom = true;
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
dom = closeDef->cb(data->driver, dom, data->conn);
|
memcpy(data->list->entries[data->list->nentries - 1].uuid,
|
||||||
if (dom)
|
uuid, VIR_UUID_BUFLEN);
|
||||||
virObjectUnlock(dom);
|
data->list->entries[data->list->nentries - 1].callback = closeDef->cb;
|
||||||
|
|
||||||
virHashRemoveEntry(data->list, uuid);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
static virQEMUCloseCallbacksListPtr
|
||||||
|
virQEMUCloseCallbacksGetForConn(virQEMUCloseCallbacksPtr closeCallbacks,
|
||||||
|
virConnectPtr conn)
|
||||||
|
{
|
||||||
|
virQEMUCloseCallbacksListPtr list = NULL;
|
||||||
|
struct virQEMUCloseCallbacksData data;
|
||||||
|
|
||||||
|
if (VIR_ALLOC(list) < 0) {
|
||||||
|
virReportOOMError();
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
data.conn = conn;
|
||||||
|
data.list = list;
|
||||||
|
data.oom = false;
|
||||||
|
|
||||||
|
virHashForEach(closeCallbacks->list, virQEMUCloseCallbacksGetOne, &data);
|
||||||
|
|
||||||
|
if (data.oom) {
|
||||||
|
VIR_FREE(list->entries);
|
||||||
|
VIR_FREE(list);
|
||||||
|
virReportOOMError();
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
return list;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
void
|
void
|
||||||
virQEMUCloseCallbacksRun(virQEMUCloseCallbacksPtr closeCallbacks,
|
virQEMUCloseCallbacksRun(virQEMUCloseCallbacksPtr closeCallbacks,
|
||||||
virConnectPtr conn,
|
virConnectPtr conn,
|
||||||
virQEMUDriverPtr driver)
|
virQEMUDriverPtr driver)
|
||||||
{
|
{
|
||||||
struct virQEMUCloseCallbacksData data = {
|
virQEMUCloseCallbacksListPtr list;
|
||||||
.driver = driver,
|
size_t i;
|
||||||
.conn = conn
|
|
||||||
};
|
|
||||||
|
|
||||||
VIR_DEBUG("conn=%p", conn);
|
VIR_DEBUG("conn=%p", conn);
|
||||||
|
|
||||||
|
/* We must not hold the lock while running the callbacks,
|
||||||
|
* so first we obtain the list of callbacks, then remove
|
||||||
|
* them all from the hash. At that point we can release
|
||||||
|
* the lock and run the callbacks safely. */
|
||||||
|
|
||||||
virObjectLock(closeCallbacks);
|
virObjectLock(closeCallbacks);
|
||||||
|
list = virQEMUCloseCallbacksGetForConn(closeCallbacks, conn);
|
||||||
|
if (!list)
|
||||||
|
return;
|
||||||
|
|
||||||
data.list = closeCallbacks->list;
|
for (i = 0 ; i < list->nentries ; i++) {
|
||||||
virHashForEach(data.list, virQEMUCloseCallbacksRunOne, &data);
|
virHashRemoveEntry(closeCallbacks->list,
|
||||||
|
list->entries[i].uuid);
|
||||||
|
}
|
||||||
virObjectUnlock(closeCallbacks);
|
virObjectUnlock(closeCallbacks);
|
||||||
|
|
||||||
|
for (i = 0 ; i < list->nentries ; i++) {
|
||||||
|
virDomainObjPtr vm;
|
||||||
|
|
||||||
|
if (!(vm = virDomainObjListFindByUUID(driver->domains,
|
||||||
|
list->entries[i].uuid))) {
|
||||||
|
char uuidstr[VIR_UUID_STRING_BUFLEN];
|
||||||
|
virUUIDFormat(list->entries[i].uuid, uuidstr);
|
||||||
|
VIR_DEBUG("No domain object with UUID %s", uuidstr);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
vm = list->entries[i].callback(driver, vm, conn);
|
||||||
|
if (vm)
|
||||||
|
virObjectUnlock(vm);
|
||||||
|
}
|
||||||
|
VIR_FREE(list->entries);
|
||||||
|
VIR_FREE(list);
|
||||||
}
|
}
|
||||||
|
|
||||||
struct _qemuSharedDiskEntry {
|
struct _qemuSharedDiskEntry {
|
||||||
|
Loading…
x
Reference in New Issue
Block a user