[libvirt] PATCH: Support memory ballooning for QEMU

Daniel P. Berrange berrange at redhat.com
Tue Mar 10 17:39:22 UTC 2009


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




More information about the libvir-list mailing list