On Tue, Mar 10, 2009 at 05:39:22PM +0000, Daniel P. Berrange wrote: > This patch implements full support for memory ballooning in the QEMU > driver. > > - Fix qemudBuildCommandLine() to set the initial boot time VM memory > allocation based off the 'maxmem' config field. > - Add call to 'qemudDomainSetMemoryBalloon' immediately at startup > to make the guest balloon to initial requested allocation. > - In the qemudDomainGetInfo() and qemudDomainDumpXML calls, query > the monitor to find current balloon allocation and update config > - Implement qemudDomainSetMemory() for running guests using the > monitor balloon command > > In all of these scenarios, if the QEMU being used is too old to support > the memory balloon device, the user will get a suitable error or it'll > report the statically defined memory allocation from boot time.
Updated, with the bugs Cole noticed fixed. Daniel diff -r 4bfef84f4d6f src/qemu_conf.c --- a/src/qemu_conf.c Tue Mar 10 12:09:41 2009 +0000 +++ b/src/qemu_conf.c Tue Mar 10 18:58:40 2009 +0000 @@ -870,7 +870,11 @@ int qemudBuildCommandLine(virConnectPtr } \ } 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 -r 4bfef84f4d6f src/qemu_driver.c --- a/src/qemu_driver.c Tue Mar 10 12:09:41 2009 +0000 +++ b/src/qemu_driver.c Tue Mar 10 18:58:40 2009 +0000 @@ -122,6 +122,9 @@ static int qemudMonitorCommandExtra(cons 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; @@ -1480,6 +1483,7 @@ static int qemudStartVMDaemon(virConnect (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; @@ -2409,6 +2413,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; @@ -2426,20 +2519,21 @@ static int qemudDomainSetMemory(virDomai 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; + } + 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; + 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) @@ -2452,6 +2546,8 @@ static int qemudDomainGetInfo(virDomainP 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); @@ -2473,8 +2569,16 @@ static int qemudDomainGetInfo(virDomainP } } + 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; @@ -3169,16 +3273,25 @@ static char *qemudDomainDumpXML(virDomai struct qemud_driver *driver = dom->conn->privateData; virDomainObjPtr vm; char *ret = NULL; - - qemuDriverLock(driver); - vm = virDomainFindByUUID(&driver->domains, dom->uuid); - qemuDriverUnlock(driver); - - if (!vm) { - qemudReportError(dom->conn, dom, NULL, VIR_ERR_INVALID_DOMAIN, - "%s", _("no domain with matching uuid")); - goto cleanup; - } + unsigned long balloon; + int err; + + qemuDriverLock(driver); + vm = virDomainFindByUUID(&driver->domains, dom->uuid); + qemuDriverUnlock(driver); + + if (!vm) { + qemudReportError(dom->conn, dom, NULL, VIR_ERR_INVALID_DOMAIN, + "%s", _("no domain with matching uuid")); + 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 ? -- |: Red Hat, Engineering, London -o- http://people.redhat.com/berrange/ :| |: http://libvirt.org -o- http://virt-manager.org -o- http://ovirt.org :| |: http://autobuild.org -o- http://search.cpan.org/~danberr/ :| |: GnuPG: 7D3B9505 -o- F3C9 553F A1DA 4AC2 5648 23C1 B3DF F742 7D3B 9505 :| -- Libvir-list mailing list Libvir-list@redhat.com https://www.redhat.com/mailman/listinfo/libvir-list