[libvirt] PATCH: Support memory ballooning for QEMU

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. 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 16:16:59 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 16:16:59 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,98 @@ cleanup: return ret; } + +#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; + } + + /* If the command failed qemu prints: + * device not found, device is locked ... + * No message is printed on success it seems */ + 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: + * device not found, device is locked ... + * 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 +2522,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 +2549,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 +2572,15 @@ 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 */ + info->memory = vm->def->memory; + else + info->memory = balloon; info->nrVirtCpu = vm->def->vcpus; ret = 0; @@ -3169,16 +3275,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 :|

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.
Before we balloon the guest at startup, does QEMU actually claim the maxmem amount? As in, if maxmem is 4G, and mem is 512M, will qemu starting grabbing the 4G in its first few moments? Also, if for some reason someone currently has different maxmem and mem values for a QEMU/KVM version that doesn't support ballooning, their guest will now use the maxmem amount? I don't think this is really a big deal (since it's not very likely to happen), just curious.
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 16:16:59 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 16:16:59 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,98 @@ cleanup: return ret; }
+ +#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; + } + + /* If the command failed qemu prints: + * device not found, device is locked ... + * No message is printed on success it seems */
This comment isn't accurate.
+ 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: + * device not found, device is locked ... + * No message is printed on success it seems */
Neither is this one.
+ 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 +2522,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 +2549,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 +2572,15 @@ 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 */ + info->memory = vm->def->memory;
If balloon isn't supported, won't this always be maxmem, since that's what we pass on the command line?
+ else + info->memory = balloon; info->nrVirtCpu = vm->def->vcpus; ret = 0;
@@ -3169,16 +3275,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 ?
The rest looks good. Thanks, Cole

On Tue, Mar 10, 2009 at 02:25:56PM -0400, Cole Robinson wrote:
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.
Before we balloon the guest at startup, does QEMU actually claim the maxmem amount? As in, if maxmem is 4G, and mem is 512M, will qemu starting grabbing the 4G in its first few moments?
Yes & no :-) QEMU/KVM doesn't actually pin all guest RAM in memory on startup. So if you boot a guest with maxmem=4G and memory=500 MB, even if the guest OS takes a while before loading the virtio_balloon.ko module, the guest won't be using all 4G of RAM. The host only pulls guest RAM into memory when pages are touched by the guest OS.
Also, if for some reason someone currently has different maxmem and mem values for a QEMU/KVM version that doesn't support ballooning, their guest will now use the maxmem amount? I don't think this is really a big deal (since it's not very likely to happen), just curious.
Yes, that is correct - it should have always been doing this because that's the same semantics that other drivers have.
@@ -2473,8 +2572,15 @@ 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 */ + info->memory = vm->def->memory;
If balloon isn't supported, won't this always be maxmem, since that's what we pass on the command line?
That is true - we should probably set that explicitly to be clear. Daniel -- |: 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 :|

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 :|

On Mon, Mar 16, 2009 at 01:00:22PM +0000, Daniel P. Berrange wrote:
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.
Patch looks fine to me, maybe this should be applied now, thanks ! Daniel -- Daniel Veillard | libxml Gnome XML XSLT toolkit http://xmlsoft.org/ daniel@veillard.com | Rpmfind RPM search engine http://rpmfind.net/ http://veillard.com/ | virtualization library http://libvirt.org/
participants (3)
-
Cole Robinson
-
Daniel P. Berrange
-
Daniel Veillard