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