]> xenbits.xensource.com Git - libvirt.git/commitdiff
Support memory ballooning in QEMU
authorDaniel P. Berrange <berrange@redhat.com>
Fri, 27 Mar 2009 11:44:29 +0000 (11:44 +0000)
committerDaniel P. Berrange <berrange@redhat.com>
Fri, 27 Mar 2009 11:44:29 +0000 (11:44 +0000)
ChangeLog
src/qemu_conf.c
src/qemu_driver.c

index f1a420cfe0655130aa9cb4fd7d114dbba9bc746c..03989a99b72a7f05b2bb19af3e83dbed8d036a25 100644 (file)
--- a/ChangeLog
+++ b/ChangeLog
@@ -1,3 +1,13 @@
+Fri Mar 27 11:44:22 GMT 2009 Daniel P. Berrange <berrange@redhat.com>
+
+       Support memory ballooning in QEMU
+       * src/qemu_conf.c: Fix initial QEMU startup memory allocation
+       to be based on 'max memory'
+       * src/qemu_driver.c: Balloon down allocation to 'memory'
+       setting at startup. Implement virDomainSetMemory() for running
+       guests via memory balloon. Report mem current usage by querying
+       memory balloon.
+
 Tue Mar 24 11:14:22 GMT 2009 Daniel P. Berrange <berrange@redhat.com>
 
        * src/capabilities.c, file src/capabilities.h,
index 9727a1501930430cec82462841a347578f6ebd8e..50b903f2f98c8c0c4384b11c435e3430c7cae4fc 100644 (file)
@@ -927,7 +927,11 @@ int qemudBuildCommandLine(virConnectPtr conn,
         }                                                               \
     } 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);
index afec99c666143c923bb1fdbce665777573047fd3..dda5fcf4f96e339544a47454975a9ebe3a66cd5d 100644 (file)
@@ -122,6 +122,9 @@ static int qemudMonitorCommandExtra(const virDomainObjPtr vm,
                                     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;
 
@@ -1481,6 +1484,7 @@ static int qemudStartVMDaemon(virConnectPtr conn,
             (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;
@@ -2410,6 +2414,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;
@@ -2427,20 +2520,21 @@ static int qemudDomainSetMemory(virDomainPtr dom, unsigned long newmem) {
         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;
+    if (virDomainIsActive(vm)) {
+        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)
@@ -2453,6 +2547,8 @@ static int qemudDomainGetInfo(virDomainPtr dom,
     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);
@@ -2474,8 +2570,16 @@ static int qemudDomainGetInfo(virDomainPtr dom,
         }
     }
 
+    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;
 
@@ -3170,6 +3274,8 @@ static char *qemudDomainDumpXML(virDomainPtr dom,
     struct qemud_driver *driver = dom->conn->privateData;
     virDomainObjPtr vm;
     char *ret = NULL;
+    unsigned long balloon;
+    int err;
 
     qemuDriverLock(driver);
     vm = virDomainFindByUUID(&driver->domains, dom->uuid);
@@ -3181,6 +3287,13 @@ static char *qemudDomainDumpXML(virDomainPtr dom,
         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 ?
                              vm->newDef : vm->def,