[libvirt] PATCH: Support memory ballooning for QEMU
Daniel P. Berrange
berrange at redhat.com
Mon Mar 16 13:00:22 UTC 2009
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 :|
More information about the libvir-list
mailing list