From cb7593bd192a9ad99126701d7b751fd67d551f74 Mon Sep 17 00:00:00 2001 From: "Daniel P. Berrange" Date: Fri, 27 Mar 2009 11:44:29 +0000 Subject: [PATCH] Support memory ballooning in QEMU --- ChangeLog | 10 ++++ src/qemu_conf.c | 6 ++- src/qemu_driver.c | 131 ++++++++++++++++++++++++++++++++++++++++++---- 3 files changed, 137 insertions(+), 10 deletions(-) diff --git a/ChangeLog b/ChangeLog index f1a420cfe0..03989a99b7 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,3 +1,13 @@ +Fri Mar 27 11:44:22 GMT 2009 Daniel P. Berrange + + Support memory ballooning in QEMU + * src/qemu_conf.c: Fix initial QEMU startup memory allocation + to be based on 'max memory' + * src/qemu_driver.c: Balloon down allocation to 'memory' + setting at startup. Implement virDomainSetMemory() for running + guests via memory balloon. Report mem current usage by querying + memory balloon. + Tue Mar 24 11:14:22 GMT 2009 Daniel P. Berrange * src/capabilities.c, file src/capabilities.h, diff --git a/src/qemu_conf.c b/src/qemu_conf.c index 9727a15019..50b903f2f9 100644 --- a/src/qemu_conf.c +++ b/src/qemu_conf.c @@ -927,7 +927,11 @@ int qemudBuildCommandLine(virConnectPtr conn, } \ } while (0) - snprintf(memory, sizeof(memory), "%lu", vm->def->memory/1024); + /* Set '-m MB' based on maxmem, because the lower 'memory' limit + * is set post-startup using the balloon driver. If balloon driver + * is not supported, then they're out of luck anyway + */ + snprintf(memory, sizeof(memory), "%lu", vm->def->maxmem/1024); snprintf(vcpus, sizeof(vcpus), "%lu", vm->def->vcpus); snprintf(domid, sizeof(domid), "%d", vm->def->id); pidfile = virFilePid(driver->stateDir, vm->def->name); diff --git a/src/qemu_driver.c b/src/qemu_driver.c index afec99c666..dda5fcf4f9 100644 --- a/src/qemu_driver.c +++ b/src/qemu_driver.c @@ -122,6 +122,9 @@ static int qemudMonitorCommandExtra(const virDomainObjPtr vm, const char *extra, const char *extraPrompt, char **reply); +static int qemudDomainSetMemoryBalloon(virConnectPtr conn, + virDomainObjPtr vm, + unsigned long newmem); static struct qemud_driver *qemu_driver = NULL; @@ -1481,6 +1484,7 @@ static int qemudStartVMDaemon(virConnectPtr conn, (qemudDetectVcpuPIDs(conn, vm) < 0) || (qemudInitCpus(conn, vm, migrateFrom) < 0) || (qemudInitPasswords(conn, driver, vm) < 0) || + (qemudDomainSetMemoryBalloon(conn, vm, vm->def->memory) < 0) || (qemudSaveDomainStatus(conn, qemu_driver, vm) < 0)) { qemudShutdownVMDaemon(conn, driver, vm); return -1; @@ -2410,6 +2414,95 @@ cleanup: return ret; } + +/* The reply from QEMU contains 'ballon: actual=421' where value is in MB */ +#define BALLOON_PREFIX "balloon: actual=" + +/* + * Returns: 0 if balloon not supported, +1 if balloon query worked + * or -1 on failure + */ +static int qemudDomainGetMemoryBalloon(virConnectPtr conn, + virDomainObjPtr vm, + unsigned long *currmem) { + char *reply = NULL; + int ret = -1; + char *offset; + + if (qemudMonitorCommand(vm, "info balloon", &reply) < 0) { + qemudReportError(conn, dom, NULL, VIR_ERR_OPERATION_FAILED, + "%s", _("could not query memory balloon allocation")); + goto cleanup; + } + + DEBUG ("balloon reply: '%s'", reply); + if ((offset = strstr(reply, BALLOON_PREFIX)) != NULL) { + unsigned int memMB; + char *end; + offset += strlen(BALLOON_PREFIX); + if (virStrToLong_ui(offset, &end, 10, &memMB) < 0) { + qemudReportError(conn, dom, NULL, VIR_ERR_OPERATION_FAILED, + "%s", _("could not parse memory balloon allocation")); + goto cleanup; + } + *currmem = memMB * 1024; + ret = 1; + } else { + /* We don't raise an error here, since its to be expected that + * many QEMU's don't support ballooning + */ + ret = 0; + } + +cleanup: + VIR_FREE(reply); + return ret; +} + +/* + * Returns: 0 if balloon not supported, +1 if balloon query worked + * or -1 on failure + */ +static int qemudDomainSetMemoryBalloon(virConnectPtr conn, + virDomainObjPtr vm, + unsigned long newmem) { + char *cmd; + char *reply = NULL; + int ret = -1; + + /* + * 'newmem' is in KB, QEMU monitor works in MB, and we all wish + * we just worked in bytes with unsigned long long everywhere. + */ + if (virAsprintf(&cmd, "balloon %lu", (newmem / 1024)) < 0) { + virReportOOMError(conn); + goto cleanup; + } + + if (qemudMonitorCommand(vm, cmd, &reply) < 0) { + qemudReportError(conn, dom, NULL, VIR_ERR_OPERATION_FAILED, + "%s", _("could not balloon memory allocation")); + VIR_FREE(cmd); + goto cleanup; + } + VIR_FREE(cmd); + + /* If the command failed qemu prints: 'unknown command' + * No message is printed on success it seems */ + DEBUG ("balloon reply: %s", reply); + if (strstr(reply, "\nunknown command:")) { + /* Don't set error - it is expected memory balloon fails on many qemu */ + ret = 0; + } else { + ret = 1; + } + +cleanup: + VIR_FREE(reply); + return ret; +} + + static int qemudDomainSetMemory(virDomainPtr dom, unsigned long newmem) { struct qemud_driver *driver = dom->conn->privateData; virDomainObjPtr vm; @@ -2427,20 +2520,21 @@ static int qemudDomainSetMemory(virDomainPtr dom, unsigned long newmem) { goto cleanup; } - if (virDomainIsActive(vm)) { - qemudReportError(dom->conn, dom, NULL, VIR_ERR_NO_SUPPORT, - "%s", _("cannot set memory of an active domain")); - goto cleanup; - } - if (newmem > vm->def->maxmem) { qemudReportError(dom->conn, dom, NULL, VIR_ERR_INVALID_ARG, "%s", _("cannot set memory higher than max memory")); goto cleanup; } - vm->def->memory = newmem; - ret = 0; + if (virDomainIsActive(vm)) { + ret = qemudDomainSetMemoryBalloon(dom->conn, vm, newmem); + if (ret == 0) + qemudReportError(dom->conn, dom, NULL, VIR_ERR_NO_SUPPORT, + "%s", _("cannot set memory of an active domain")); + } else { + vm->def->memory = newmem; + ret = 0; + } cleanup: if (vm) @@ -2453,6 +2547,8 @@ static int qemudDomainGetInfo(virDomainPtr dom, struct qemud_driver *driver = dom->conn->privateData; virDomainObjPtr vm; int ret = -1; + int err; + unsigned long balloon; qemuDriverLock(driver); vm = virDomainFindByUUID(&driver->domains, dom->uuid); @@ -2474,8 +2570,16 @@ static int qemudDomainGetInfo(virDomainPtr dom, } } + err = qemudDomainGetMemoryBalloon(dom->conn, vm, &balloon); + if (err < 0) + goto cleanup; + info->maxMem = vm->def->maxmem; - info->memory = vm->def->memory; + if (err == 0) + /* Balloon not supported, so maxmem is always the allocation */ + info->memory = vm->def->maxmem; + else + info->memory = balloon; info->nrVirtCpu = vm->def->vcpus; ret = 0; @@ -3170,6 +3274,8 @@ static char *qemudDomainDumpXML(virDomainPtr dom, struct qemud_driver *driver = dom->conn->privateData; virDomainObjPtr vm; char *ret = NULL; + unsigned long balloon; + int err; qemuDriverLock(driver); vm = virDomainFindByUUID(&driver->domains, dom->uuid); @@ -3181,6 +3287,13 @@ static char *qemudDomainDumpXML(virDomainPtr dom, goto cleanup; } + /* Refresh current memory based on balloon info */ + err = qemudDomainGetMemoryBalloon(dom->conn, vm, &balloon); + if (err < 0) + goto cleanup; + if (err > 0) + vm->def->memory = balloon; + ret = virDomainDefFormat(dom->conn, (flags & VIR_DOMAIN_XML_INACTIVE) && vm->newDef ? vm->newDef : vm->def,