[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