[libvirt] PATCH: Support memory ballooning for QEMU

Cole Robinson crobinso at redhat.com
Tue Mar 10 18:25:56 UTC 2009


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

Before we balloon the guest at startup, does QEMU actually claim the
maxmem amount? As in, if maxmem is 4G, and mem is 512M, will qemu
starting grabbing the 4G in its first few moments?

Also, if for some reason someone currently has different maxmem and mem
values for a QEMU/KVM version that doesn't support ballooning, their
guest will now use the maxmem amount? I don't think this is really a big
deal (since it's not very likely to happen), just curious.

> 
> 
> 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 */

This comment isn't accurate.

> +    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 */

Neither is this one.

> +    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;

If balloon isn't supported, won't this always be maxmem, since that's
what we pass on the command line?

> +    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 ?
> 

The rest looks good.

Thanks,
Cole




More information about the libvir-list mailing list