Fix up qemu domain save/managed save locking.

The current version of the qemu managed save implementation
is subject to a race where the domain shuts down between
the time that we start the command and the time that we
actually try to do the save.  Close this race by making
qemuDomainSaveFlags() expect both the driver and the passed-in
vm object to be locked before executing.

Signed-off-by: Chris Lalancette <clalance@redhat.com>
This commit is contained in:
Chris Lalancette 2010-08-13 09:23:11 -04:00
parent 2ad42978ea
commit 4303c91cc3

View File

@ -5236,12 +5236,11 @@ endjob:
return ret; return ret;
} }
/* this internal function expects the driver lock to already be held on entry */
static int qemudDomainSaveFlag(virDomainPtr dom, const char *path, static int qemudDomainSaveFlag(struct qemud_driver *driver, virDomainPtr dom,
virDomainObjPtr vm, const char *path,
int compressed) int compressed)
{ {
struct qemud_driver *driver = dom->conn->privateData;
virDomainObjPtr vm = NULL;
char *xml = NULL; char *xml = NULL;
struct qemud_save_header header; struct qemud_save_header header;
struct fileOpHookData hdata; struct fileOpHookData hdata;
@ -5259,19 +5258,8 @@ static int qemudDomainSaveFlag(virDomainPtr dom, const char *path,
memcpy(header.magic, QEMUD_SAVE_MAGIC, sizeof(header.magic)); memcpy(header.magic, QEMUD_SAVE_MAGIC, sizeof(header.magic));
header.version = QEMUD_SAVE_VERSION; header.version = QEMUD_SAVE_VERSION;
qemuDriverLock(driver);
header.compressed = compressed; header.compressed = compressed;
vm = virDomainFindByUUID(&driver->domains, dom->uuid);
if (!vm) {
char uuidstr[VIR_UUID_STRING_BUFLEN];
virUUIDFormat(dom->uuid, uuidstr);
qemuReportError(VIR_ERR_NO_DOMAIN,
_("no domain with matching uuid '%s'"), uuidstr);
goto cleanup;
}
priv = vm->privateData; priv = vm->privateData;
if (qemuDomainObjBeginJobWithDriver(driver, vm) < 0) if (qemuDomainObjBeginJobWithDriver(driver, vm) < 0)
@ -5556,12 +5544,9 @@ cleanup:
VIR_FREE(xml); VIR_FREE(xml);
if (ret != 0 && is_reg) if (ret != 0 && is_reg)
unlink(path); unlink(path);
if (vm)
virDomainObjUnlock(vm);
if (event) if (event)
qemuDomainEventQueue(driver, event); qemuDomainEventQueue(driver, event);
virCgroupFree(&cgroup); virCgroupFree(&cgroup);
qemuDriverUnlock(driver);
return ret; return ret;
} }
@ -5569,8 +5554,11 @@ static int qemudDomainSave(virDomainPtr dom, const char *path)
{ {
struct qemud_driver *driver = dom->conn->privateData; struct qemud_driver *driver = dom->conn->privateData;
int compressed; int compressed;
int ret = -1;
virDomainObjPtr vm = NULL;
qemuDriverLock(driver);
/* Hm, is this safe against qemudReload? */
if (driver->saveImageFormat == NULL) if (driver->saveImageFormat == NULL)
compressed = QEMUD_SAVE_FORMAT_RAW; compressed = QEMUD_SAVE_FORMAT_RAW;
else { else {
@ -5583,7 +5571,23 @@ static int qemudDomainSave(virDomainPtr dom, const char *path)
} }
} }
return qemudDomainSaveFlag(dom, path, compressed); vm = virDomainFindByUUID(&driver->domains, dom->uuid);
if (!vm) {
char uuidstr[VIR_UUID_STRING_BUFLEN];
virUUIDFormat(dom->uuid, uuidstr);
qemuReportError(VIR_ERR_NO_DOMAIN,
_("no domain with matching uuid '%s'"), uuidstr);
goto cleanup;
}
ret = qemudDomainSaveFlag(driver, dom, vm, path, compressed);
cleanup:
if (vm)
virDomainObjUnlock(vm);
qemuDriverUnlock(driver);
return ret;
} }
static char * static char *
@ -5616,48 +5620,25 @@ qemuDomainManagedSave(virDomainPtr dom, unsigned int flags)
virUUIDFormat(dom->uuid, uuidstr); virUUIDFormat(dom->uuid, uuidstr);
qemuReportError(VIR_ERR_NO_DOMAIN, qemuReportError(VIR_ERR_NO_DOMAIN,
_("no domain with matching uuid '%s'"), uuidstr); _("no domain with matching uuid '%s'"), uuidstr);
goto error; goto cleanup;
}
if (!virDomainObjIsActive(vm)) {
qemuReportError(VIR_ERR_OPERATION_INVALID,
"%s", _("domain is not running"));
goto error;
} }
name = qemuDomainManagedSavePath(driver, vm); name = qemuDomainManagedSavePath(driver, vm);
if (name == NULL) if (name == NULL)
goto error; goto cleanup;
VIR_DEBUG("Saving state to %s", name); VIR_DEBUG("Saving state to %s", name);
/* FIXME: we should take the flags parameter, and use bits out
* of there to control whether we are compressing or not
*/
compressed = QEMUD_SAVE_FORMAT_RAW; compressed = QEMUD_SAVE_FORMAT_RAW;
ret = qemudDomainSaveFlag(driver, dom, vm, name, compressed);
/* we have to drop our locks here because qemudDomainSaveFlag is
* going to pick them back up. Unfortunately it opens a race window
* between us dropping and qemudDomainSaveFlag picking it back up, but
* if we want to allow other operations to be able to happen while
* qemuDomainSaveFlag is running (possibly for a long time), I'm not
* sure if there is a better solution
*/
virDomainObjUnlock(vm);
qemuDriverUnlock(driver);
ret = qemudDomainSaveFlag(dom, name, compressed);
cleanup: cleanup:
VIR_FREE(name);
return ret;
error:
if (vm) if (vm)
virDomainObjUnlock(vm); virDomainObjUnlock(vm);
qemuDriverUnlock(driver); qemuDriverUnlock(driver);
goto cleanup; VIR_FREE(name);
return ret;
} }
static int static int