[libvirt] [PATCHv3.5 0/4] Automaticaly fill <memory> element for NUMA enabled guests

Pavel's series changed few same places thus the previous version no longer applies. Peter Krempa (4): conf: Replace access to def->mem.max_balloon with accessor functions qemu: command: Add helper to align memory sizes conf: Automatically use NUMA memory size in case NUMA is enabled conf: Make specifying <memory> optional docs/schemas/domaincommon.rng | 18 ++--- src/conf/domain_conf.c | 81 +++++++++++++++++++--- src/conf/domain_conf.h | 4 ++ src/hyperv/hyperv_driver.c | 2 +- src/libvirt_private.syms | 3 + src/libxl/libxl_conf.c | 2 +- src/libxl/libxl_driver.c | 8 +-- src/lxc/lxc_cgroup.c | 2 +- src/lxc/lxc_driver.c | 12 ++-- src/lxc/lxc_fuse.c | 4 +- src/lxc/lxc_native.c | 4 +- src/openvz/openvz_driver.c | 2 +- src/parallels/parallels_driver.c | 2 +- src/parallels/parallels_sdk.c | 12 ++-- src/phyp/phyp_driver.c | 11 +-- src/qemu/qemu_command.c | 23 +++--- src/qemu/qemu_domain.c | 21 ++++++ src/qemu/qemu_domain.h | 2 + src/qemu/qemu_driver.c | 21 +++--- src/qemu/qemu_hotplug.c | 8 ++- src/qemu/qemu_process.c | 2 +- src/test/test_driver.c | 8 +-- src/uml/uml_driver.c | 8 +-- src/vbox/vbox_common.c | 4 +- src/vmware/vmware_driver.c | 2 +- src/vmx/vmx.c | 12 ++-- src/xen/xm_internal.c | 14 ++-- src/xenapi/xenapi_driver.c | 2 +- src/xenapi/xenapi_utils.c | 4 +- src/xenconfig/xen_common.c | 8 ++- src/xenconfig/xen_sxpr.c | 9 +-- .../qemuxml2argv-cpu-numa-no-memory-element.args | 7 ++ .../qemuxml2argv-cpu-numa-no-memory-element.xml | 24 +++++++ .../qemuxml2argv-minimal-no-memory.xml | 25 +++++++ .../qemuxml2argv-numatune-memnode.args | 2 +- tests/qemuxml2argvtest.c | 2 + .../qemuxml2xmlout-cpu-numa-no-memory-element.xml | 28 ++++++++ tests/qemuxml2xmltest.c | 1 + 38 files changed, 299 insertions(+), 105 deletions(-) create mode 100644 tests/qemuxml2argvdata/qemuxml2argv-cpu-numa-no-memory-element.args create mode 100644 tests/qemuxml2argvdata/qemuxml2argv-cpu-numa-no-memory-element.xml create mode 100644 tests/qemuxml2argvdata/qemuxml2argv-minimal-no-memory.xml create mode 100644 tests/qemuxml2xmloutdata/qemuxml2xmlout-cpu-numa-no-memory-element.xml -- 2.2.2

As there are two possible approaches to define a domain's memory size - one used with legacy, non-NUMA VMs configured in the <memory> element and per-node based approach on NUMA machines - the user needs to make sure that both are specified correctly in the NUMA case. To avoid this burden on the user I'd like to replace the NUMA case with automatic totaling of the memory size. To achieve this I need to replace direct access to the virDomainMemtune's 'max_balloon' field with two separate getters depending on the desired size. The two sizes are needed as: 1) Startup memory size doesn't include memory modules in some hypervisors. 2) After startup these count as the usable memory size. Note that the comments for the functions are future aware and document state that will be present after a few later patches. --- src/conf/domain_conf.c | 66 ++++++++++++++++++++++++++++++++++------ src/conf/domain_conf.h | 4 +++ src/hyperv/hyperv_driver.c | 2 +- src/libvirt_private.syms | 3 ++ src/libxl/libxl_conf.c | 2 +- src/libxl/libxl_driver.c | 8 ++--- src/lxc/lxc_cgroup.c | 2 +- src/lxc/lxc_driver.c | 12 ++++---- src/lxc/lxc_fuse.c | 4 +-- src/lxc/lxc_native.c | 4 +-- src/openvz/openvz_driver.c | 2 +- src/parallels/parallels_driver.c | 2 +- src/parallels/parallels_sdk.c | 12 ++++---- src/phyp/phyp_driver.c | 11 ++++--- src/qemu/qemu_command.c | 18 +++++++---- src/qemu/qemu_driver.c | 21 +++++++------ src/qemu/qemu_hotplug.c | 8 +++-- src/qemu/qemu_process.c | 2 +- src/test/test_driver.c | 8 ++--- src/uml/uml_driver.c | 8 ++--- src/vbox/vbox_common.c | 4 +-- src/vmware/vmware_driver.c | 2 +- src/vmx/vmx.c | 12 ++++---- src/xen/xm_internal.c | 14 ++++----- src/xenapi/xenapi_driver.c | 2 +- src/xenapi/xenapi_utils.c | 4 +-- src/xenconfig/xen_common.c | 8 +++-- src/xenconfig/xen_sxpr.c | 9 +++--- 28 files changed, 162 insertions(+), 92 deletions(-) diff --git a/src/conf/domain_conf.c b/src/conf/domain_conf.c index 01a3fbc..462d05e 100644 --- a/src/conf/domain_conf.c +++ b/src/conf/domain_conf.c @@ -3210,24 +3210,26 @@ virDomainDefPostParseInternal(virDomainDefPtr def, return -1; } - if (def->mem.cur_balloon > def->mem.max_balloon) { + if (def->mem.cur_balloon > virDomainDefGetMemoryActual(def)) { /* Older libvirt could get into this situation due to * rounding; if the discrepancy is less than 4MiB, we silently * round down, otherwise we flag the issue. */ if (VIR_DIV_UP(def->mem.cur_balloon, 4096) > - VIR_DIV_UP(def->mem.max_balloon, 4096)) { + VIR_DIV_UP(virDomainDefGetMemoryActual(def), 4096)) { virReportError(VIR_ERR_XML_ERROR, _("current memory '%lluk' exceeds " "maximum '%lluk'"), - def->mem.cur_balloon, def->mem.max_balloon); + def->mem.cur_balloon, + virDomainDefGetMemoryActual(def)); return -1; } else { VIR_DEBUG("Truncating current %lluk to maximum %lluk", - def->mem.cur_balloon, def->mem.max_balloon); - def->mem.cur_balloon = def->mem.max_balloon; + def->mem.cur_balloon, + virDomainDefGetMemoryActual(def)); + def->mem.cur_balloon = virDomainDefGetMemoryActual(def); } } else if (def->mem.cur_balloon == 0) { - def->mem.cur_balloon = def->mem.max_balloon; + def->mem.cur_balloon = virDomainDefGetMemoryActual(def); } /* @@ -6974,6 +6976,51 @@ virDomainParseMemoryLimit(const char *xpath, } +/** + * virDomainDefGetMemoryInitial: + * @def: domain definition + * + * Returns the size of the initial amount of guest memory. The initial amount + * is the memory size is either the configured amount in the <memory> element + * or the sum of memory sizes of NUMA nodes in case NUMA is enabled in @def. + */ +unsigned long long +virDomainDefGetMemoryInitial(virDomainDefPtr def) +{ + return def->mem.max_balloon; +} + + +/** + * virDomainDefSetMemoryInitial: + * @def: domain definition + * @size: size to set + * + * Sets the initial memory size in @def. + */ +void +virDomainDefSetMemoryInitial(virDomainDefPtr def, + unsigned long long size) +{ + def->mem.max_balloon = size; +} + + +/** + * virDomainDefGetMemoryActual: + * @def: domain definition + * + * Returns the current maximum memory size usable by the domain described by + * @def. This size is a sum of size returned by virDomainDefGetMemoryInitial + * and possible additional memory devices. + */ +unsigned long long +virDomainDefGetMemoryActual(virDomainDefPtr def) +{ + return virDomainDefGetMemoryInitial(def); +} + + static int virDomainControllerModelTypeFromString(const virDomainControllerDef *def, const char *model) @@ -16089,10 +16136,11 @@ virDomainDefCheckABIStability(virDomainDefPtr src, goto error; } - if (src->mem.max_balloon != dst->mem.max_balloon) { + if (virDomainDefGetMemoryInitial(src) != virDomainDefGetMemoryInitial(dst)) { virReportError(VIR_ERR_CONFIG_UNSUPPORTED, _("Target domain max memory %lld does not match source %lld"), - dst->mem.max_balloon, src->mem.max_balloon); + virDomainDefGetMemoryInitial(dst), + virDomainDefGetMemoryInitial(src)); goto error; } if (src->mem.cur_balloon != dst->mem.cur_balloon) { @@ -19794,7 +19842,7 @@ virDomainDefFormatInternal(virDomainDefPtr def, virBufferAsprintf(buf, " dumpCore='%s'", virTristateSwitchTypeToString(def->mem.dump_core)); virBufferAsprintf(buf, " unit='KiB'>%llu</memory>\n", - def->mem.max_balloon); + virDomainDefGetMemoryActual(def)); virBufferAsprintf(buf, "<currentMemory unit='KiB'>%llu</currentMemory>\n", def->mem.cur_balloon); diff --git a/src/conf/domain_conf.h b/src/conf/domain_conf.h index 36bb418..e27f7b2 100644 --- a/src/conf/domain_conf.h +++ b/src/conf/domain_conf.h @@ -2190,6 +2190,10 @@ struct _virDomainDef { xmlNodePtr metadata; }; +unsigned long long virDomainDefGetMemoryInitial(virDomainDefPtr def); +void virDomainDefSetMemoryInitial(virDomainDefPtr def, unsigned long long size); +unsigned long long virDomainDefGetMemoryActual(virDomainDefPtr def); + typedef enum { VIR_DOMAIN_TAINT_CUSTOM_ARGV, /* Custom ARGV passthrough from XML */ VIR_DOMAIN_TAINT_CUSTOM_MONITOR, /* Custom monitor commands issued */ diff --git a/src/hyperv/hyperv_driver.c b/src/hyperv/hyperv_driver.c index 906c603..00169c7 100644 --- a/src/hyperv/hyperv_driver.c +++ b/src/hyperv/hyperv_driver.c @@ -870,7 +870,7 @@ hypervDomainGetXMLDesc(virDomainPtr domain, unsigned int flags) if (VIR_STRDUP(def->description, virtualSystemSettingData->data->Notes) < 0) goto cleanup; - def->mem.max_balloon = memorySettingData->data->Limit * 1024; /* megabyte to kilobyte */ + virDomainDefSetMemoryInitial(def, memorySettingData->data->Limit * 1024); /* megabyte to kilobyte */ def->mem.cur_balloon = memorySettingData->data->VirtualQuantity * 1024; /* megabyte to kilobyte */ def->vcpus = processorSettingData->data->VirtualQuantity; diff --git a/src/libvirt_private.syms b/src/libvirt_private.syms index a579bc5..7a5e35b 100644 --- a/src/libvirt_private.syms +++ b/src/libvirt_private.syms @@ -198,6 +198,8 @@ virDomainDefFormatConvertXMLFlags; virDomainDefFormatInternal; virDomainDefFree; virDomainDefGetDefaultEmulator; +virDomainDefGetMemoryActual; +virDomainDefGetMemoryInitial; virDomainDefGetSecurityLabelDef; virDomainDefHasDeviceAddress; virDomainDefMaybeAddController; @@ -209,6 +211,7 @@ virDomainDefParseFile; virDomainDefParseNode; virDomainDefParseString; virDomainDefPostParse; +virDomainDefSetMemoryInitial; virDomainDeleteConfig; virDomainDeviceAddressIsValid; virDomainDeviceAddressTypeToString; diff --git a/src/libxl/libxl_conf.c b/src/libxl/libxl_conf.c index 2321660..997b73c 100644 --- a/src/libxl/libxl_conf.c +++ b/src/libxl/libxl_conf.c @@ -659,7 +659,7 @@ libxlMakeDomBuildInfo(virDomainDefPtr def, } } b_info->sched_params.weight = 1000; - b_info->max_memkb = def->mem.max_balloon; + b_info->max_memkb = virDomainDefGetMemoryInitial(def); b_info->target_memkb = def->mem.cur_balloon; if (hvm) { char bootorder[VIR_DOMAIN_BOOT_LAST + 1]; diff --git a/src/libxl/libxl_driver.c b/src/libxl/libxl_driver.c index 88fa6ff..12112ab 100644 --- a/src/libxl/libxl_driver.c +++ b/src/libxl/libxl_driver.c @@ -1075,7 +1075,7 @@ libxlDomainGetMaxMemory(virDomainPtr dom) if (virDomainGetMaxMemoryEnsureACL(dom->conn, vm->def) < 0) goto cleanup; - ret = vm->def->mem.max_balloon; + ret = virDomainDefGetMemoryActual(vm->def); cleanup: if (vm) @@ -1157,7 +1157,7 @@ libxlDomainSetMemoryFlags(virDomainPtr dom, unsigned long newmem, if (flags & VIR_DOMAIN_MEM_CONFIG) { /* Help clang 2.8 decipher the logic flow. */ sa_assert(persistentDef); - persistentDef->mem.max_balloon = newmem; + virDomainDefSetMemoryInitial(persistentDef, newmem); if (persistentDef->mem.cur_balloon > newmem) persistentDef->mem.cur_balloon = newmem; ret = virDomainSaveConfig(cfg->configDir, persistentDef); @@ -1167,7 +1167,7 @@ libxlDomainSetMemoryFlags(virDomainPtr dom, unsigned long newmem, } else { /* resize the current memory */ - if (newmem > vm->def->mem.max_balloon) { + if (newmem > virDomainDefGetMemoryActual(vm->def)) { virReportError(VIR_ERR_INVALID_ARG, "%s", _("cannot set memory higher than max memory")); goto endjob; @@ -1241,7 +1241,7 @@ libxlDomainGetInfo(virDomainPtr dom, virDomainInfoPtr info) if (!virDomainObjIsActive(vm)) { info->cpuTime = 0; info->memory = vm->def->mem.cur_balloon; - info->maxMem = vm->def->mem.max_balloon; + info->maxMem = virDomainDefGetMemoryActual(vm->def); } else { if (libxl_domain_info(priv->ctx, &d_info, vm->def->id) != 0) { virReportError(VIR_ERR_INTERNAL_ERROR, diff --git a/src/lxc/lxc_cgroup.c b/src/lxc/lxc_cgroup.c index 5a49e2d..c1813e2 100644 --- a/src/lxc/lxc_cgroup.c +++ b/src/lxc/lxc_cgroup.c @@ -146,7 +146,7 @@ static int virLXCCgroupSetupMemTune(virDomainDefPtr def, { int ret = -1; - if (virCgroupSetMemory(cgroup, def->mem.max_balloon) < 0) + if (virCgroupSetMemory(cgroup, virDomainDefGetMemoryInitial(def)) < 0) goto cleanup; if (virMemoryLimitIsSet(def->mem.hard_limit)) diff --git a/src/lxc/lxc_driver.c b/src/lxc/lxc_driver.c index 6b0dea1..98fbea8 100644 --- a/src/lxc/lxc_driver.c +++ b/src/lxc/lxc_driver.c @@ -617,7 +617,7 @@ static int lxcDomainGetInfo(virDomainPtr dom, } } - info->maxMem = vm->def->mem.max_balloon; + info->maxMem = virDomainDefGetMemoryActual(vm->def); info->nrVirtCpu = vm->def->vcpus; ret = 0; @@ -686,7 +686,7 @@ lxcDomainGetMaxMemory(virDomainPtr dom) if (virDomainGetMaxMemoryEnsureACL(dom->conn, vm->def) < 0) goto cleanup; - ret = vm->def->mem.max_balloon; + ret = virDomainDefGetMemoryActual(vm->def); cleanup: if (vm) @@ -735,7 +735,7 @@ static int lxcDomainSetMemoryFlags(virDomainPtr dom, unsigned long newmem, } if (flags & VIR_DOMAIN_AFFECT_CONFIG) { - persistentDef->mem.max_balloon = newmem; + virDomainDefSetMemoryInitial(persistentDef, newmem); if (persistentDef->mem.cur_balloon > newmem) persistentDef->mem.cur_balloon = newmem; if (virDomainSaveConfig(cfg->configDir, persistentDef) < 0) @@ -745,10 +745,10 @@ static int lxcDomainSetMemoryFlags(virDomainPtr dom, unsigned long newmem, unsigned long oldmax = 0; if (flags & VIR_DOMAIN_AFFECT_LIVE) - oldmax = vm->def->mem.max_balloon; + oldmax = virDomainDefGetMemoryActual(vm->def); if (flags & VIR_DOMAIN_AFFECT_CONFIG) { - if (!oldmax || oldmax > persistentDef->mem.max_balloon) - oldmax = persistentDef->mem.max_balloon; + if (!oldmax || oldmax > virDomainDefGetMemoryActual(persistentDef)) + oldmax = virDomainDefGetMemoryActual(persistentDef); } if (newmem > oldmax) { diff --git a/src/lxc/lxc_fuse.c b/src/lxc/lxc_fuse.c index bc2b92c..34a69cc 100644 --- a/src/lxc/lxc_fuse.c +++ b/src/lxc/lxc_fuse.c @@ -166,12 +166,12 @@ static int lxcProcReadMeminfo(char *hostpath, virDomainDefPtr def, if (STREQ(line, "MemTotal") && (virMemoryLimitIsSet(def->mem.hard_limit) || - def->mem.max_balloon)) { + virDomainDefGetMemoryActual(def))) { virBufferAsprintf(new_meminfo, "MemTotal: %8llu kB\n", meminfo.memtotal); } else if (STREQ(line, "MemFree") && (virMemoryLimitIsSet(def->mem.hard_limit) || - def->mem.max_balloon)) { + virDomainDefGetMemoryActual(def))) { virBufferAsprintf(new_meminfo, "MemFree: %8llu kB\n", (meminfo.memtotal - meminfo.memusage)); } else if (STREQ(line, "Buffers")) { diff --git a/src/lxc/lxc_native.c b/src/lxc/lxc_native.c index 2ebe610..c15eb19 100644 --- a/src/lxc/lxc_native.c +++ b/src/lxc/lxc_native.c @@ -772,7 +772,7 @@ lxcSetMemTune(virDomainDefPtr def, virConfPtr properties) if (lxcConvertSize(value->str, &size) < 0) return -1; size = size / 1024; - def->mem.max_balloon = size; + virDomainDefSetMemoryInitial(def, size); def->mem.hard_limit = virMemoryLimitTruncate(size); } @@ -1012,7 +1012,7 @@ lxcParseConfigString(const char *config) } vmdef->id = -1; - vmdef->mem.max_balloon = 64 * 1024; + virDomainDefSetMemoryInitial(vmdef, 64 * 1024); vmdef->onReboot = VIR_DOMAIN_LIFECYCLE_RESTART; vmdef->onCrash = VIR_DOMAIN_LIFECYCLE_DESTROY; diff --git a/src/openvz/openvz_driver.c b/src/openvz/openvz_driver.c index a55e6a6..71b0471 100644 --- a/src/openvz/openvz_driver.c +++ b/src/openvz/openvz_driver.c @@ -458,7 +458,7 @@ static int openvzDomainGetInfo(virDomainPtr dom, } } - info->maxMem = vm->def->mem.max_balloon; + info->maxMem = virDomainDefGetMemoryActual(vm->def); info->memory = vm->def->mem.cur_balloon; info->nrVirtCpu = vm->def->vcpus; ret = 0; diff --git a/src/parallels/parallels_driver.c b/src/parallels/parallels_driver.c index 650b790..793f1a3 100644 --- a/src/parallels/parallels_driver.c +++ b/src/parallels/parallels_driver.c @@ -527,7 +527,7 @@ parallelsDomainGetInfo(virDomainPtr domain, virDomainInfoPtr info) info->state = virDomainObjGetState(privdom, NULL); info->memory = privdom->def->mem.cur_balloon; - info->maxMem = privdom->def->mem.max_balloon; + info->maxMem = virDomainDefGetMemoryActual(privdom->def); info->nrVirtCpu = privdom->def->vcpus; info->cpuTime = 0; ret = 0; diff --git a/src/parallels/parallels_sdk.c b/src/parallels/parallels_sdk.c index fec145d..0b92666 100644 --- a/src/parallels/parallels_sdk.c +++ b/src/parallels/parallels_sdk.c @@ -1213,9 +1213,9 @@ prlsdkLoadDomain(parallelsConnPtr privconn, /* get RAM parameters */ pret = PrlVmCfg_GetRamSize(sdkdom, &ram); prlsdkCheckRetGoto(pret, error); - def->mem.max_balloon = ram << 10; /* RAM size obtained in Mbytes, - convert to Kbytes */ - def->mem.cur_balloon = def->mem.max_balloon; + virDomainDefSetMemoryInitial(def, ram << 10); /* RAM size obtained in Mbytes, + convert to Kbytes */ + def->mem.cur_balloon = ram << 10; if (prlsdkConvertCpuInfo(sdkdom, def, pdom) < 0) goto error; @@ -1769,14 +1769,14 @@ prlsdkCheckUnsupportedParams(PRL_HANDLE sdkdom, virDomainDefPtr def) return -1; } - if (def->mem.max_balloon != def->mem.cur_balloon) { + if (virDomainDefGetMemoryActual(def) != def->mem.cur_balloon) { virReportError(VIR_ERR_CONFIG_UNSUPPORTED, "%s", _("changing balloon parameters is not supported " "by parallels driver")); return -1; } - if (def->mem.max_balloon % (1 << 10) != 0) { + if (virDomainDefGetMemoryActual(def) % (1 << 10) != 0) { virReportError(VIR_ERR_CONFIG_UNSUPPORTED, "%s", _("Memory size should be multiple of 1Mb.")); return -1; @@ -2863,7 +2863,7 @@ prlsdkDoApplyConfig(PRL_HANDLE sdkdom, prlsdkCheckRetGoto(pret, error); } - pret = PrlVmCfg_SetRamSize(sdkdom, def->mem.max_balloon >> 10); + pret = PrlVmCfg_SetRamSize(sdkdom, virDomainDefGetMemoryActual(def) >> 10); prlsdkCheckRetGoto(pret, error); pret = PrlVmCfg_SetCpuCount(sdkdom, def->vcpus); diff --git a/src/phyp/phyp_driver.c b/src/phyp/phyp_driver.c index 2873998..60a47ad 100644 --- a/src/phyp/phyp_driver.c +++ b/src/phyp/phyp_driver.c @@ -3252,6 +3252,7 @@ phypDomainGetXMLDesc(virDomainPtr dom, unsigned int flags) LIBSSH2_SESSION *session = phyp_driver->session; virDomainDef def; char *managed_system = phyp_driver->managed_system; + unsigned long long memory; /* Flags checked by virDomainDefFormat */ @@ -3273,12 +3274,13 @@ phypDomainGetXMLDesc(virDomainPtr dom, unsigned int flags) goto err; } - if ((def.mem.max_balloon = - phypGetLparMem(dom->conn, managed_system, dom->id, 0)) == 0) { + if ((memory = phypGetLparMem(dom->conn, managed_system, dom->id, 0)) == 0) { VIR_ERROR(_("Unable to determine domain's max memory.")); goto err; } + virDomainDefSetMemoryInitial(&def, memory); + if ((def.mem.cur_balloon = phypGetLparMem(dom->conn, managed_system, dom->id, 1)) == 0) { VIR_ERROR(_("Unable to determine domain's memory.")); @@ -3491,7 +3493,7 @@ phypBuildLpar(virConnectPtr conn, virDomainDefPtr def) goto cleanup; } - if (!def->mem.max_balloon) { + if (!virDomainDefGetMemoryInitial(def)) { virReportError(VIR_ERR_XML_ERROR, "%s", _("Field <memory> on the domain XML file is missing or " "has invalid value")); @@ -3517,7 +3519,8 @@ phypBuildLpar(virConnectPtr conn, virDomainDefPtr def) virBufferAsprintf(&buf, " -r lpar -p %s -i min_mem=%lld,desired_mem=%lld," "max_mem=%lld,desired_procs=%d,virtual_scsi_adapters=%s", def->name, def->mem.cur_balloon, - def->mem.cur_balloon, def->mem.max_balloon, + def->mem.cur_balloon, + virDomainDefGetMemoryInitial(def), (int) def->vcpus, virDomainDiskGetSource(def->disks[0])); ret = phypExecBuffer(session, &buf, &exit_status, conn, false); diff --git a/src/qemu/qemu_command.c b/src/qemu/qemu_command.c index 1510797..b3b573b 100644 --- a/src/qemu/qemu_command.c +++ b/src/qemu/qemu_command.c @@ -8501,8 +8501,9 @@ qemuBuildCommandLine(virConnectPtr conn, * XML to reflect our rounding. */ virCommandAddArg(cmd, "-m"); - def->mem.max_balloon = VIR_DIV_UP(def->mem.max_balloon, 1024) * 1024; - virCommandAddArgFormat(cmd, "%llu", def->mem.max_balloon / 1024); + virDomainDefSetMemoryInitial(def, VIR_ROUND_UP(virDomainDefGetMemoryInitial(def), 1024)); + virCommandAddArgFormat(cmd, "%llu", virDomainDefGetMemoryInitial(def) / 1024); + if (def->mem.nhugepages && !virDomainNumaGetNodeCount(def->numa)) { const long system_page_size = virGetSystemPageSizeKB(); char *mem_path = NULL; @@ -10485,8 +10486,11 @@ qemuBuildCommandLine(virConnectPtr conn, * space just to be safe (some finer tuning might be * nice, though). */ - memKB = virMemoryLimitIsSet(def->mem.hard_limit) ? - def->mem.hard_limit : def->mem.max_balloon + 1024 * 1024; + if (virMemoryLimitIsSet(def->mem.hard_limit)) + memKB = def->mem.hard_limit; + else + memKB = virDomainDefGetMemoryActual(def) + 1024 * 1024; + virCommandSetMaxMemLock(cmd, memKB * 1024); } @@ -12040,7 +12044,8 @@ qemuParseCommandLine(virCapsPtr qemuCaps, } def->id = -1; - def->mem.cur_balloon = def->mem.max_balloon = 64 * 1024; + def->mem.cur_balloon = 64 * 1024; + virDomainDefSetMemoryInitial(def, def->mem.cur_balloon); def->maxvcpus = 1; def->vcpus = 1; def->clock.offset = VIR_DOMAIN_CLOCK_OFFSET_UTC; @@ -12248,7 +12253,8 @@ qemuParseCommandLine(virCapsPtr qemuCaps, _("cannot parse memory level '%s'"), val); goto error; } - def->mem.cur_balloon = def->mem.max_balloon = mem * 1024; + virDomainDefSetMemoryInitial(def, mem * 1024); + def->mem.cur_balloon = mem * 1024; } else if (STREQ(arg, "-smp")) { WANT_VALUE(); if (qemuParseCommandLineSmp(def, val) < 0) diff --git a/src/qemu/qemu_driver.c b/src/qemu/qemu_driver.c index b37474f..37b72e4 100644 --- a/src/qemu/qemu_driver.c +++ b/src/qemu/qemu_driver.c @@ -2256,7 +2256,7 @@ qemuDomainGetMaxMemory(virDomainPtr dom) if (virDomainGetMaxMemoryEnsureACL(dom->conn, vm->def) < 0) goto cleanup; - ret = vm->def->mem.max_balloon; + ret = virDomainDefGetMemoryActual(vm->def); cleanup: qemuDomObjEndAPI(&vm); @@ -2318,7 +2318,8 @@ static int qemuDomainSetMemoryFlags(virDomainPtr dom, unsigned long newmem, goto endjob; } - persistentDef->mem.max_balloon = newmem; + virDomainDefSetMemoryInitial(persistentDef, newmem); + if (persistentDef->mem.cur_balloon > newmem) persistentDef->mem.cur_balloon = newmem; ret = virDomainSaveConfig(cfg->configDir, persistentDef); @@ -2330,10 +2331,10 @@ static int qemuDomainSetMemoryFlags(virDomainPtr dom, unsigned long newmem, unsigned long oldmax = 0; if (flags & VIR_DOMAIN_AFFECT_LIVE) - oldmax = vm->def->mem.max_balloon; + oldmax = virDomainDefGetMemoryActual(vm->def); if (flags & VIR_DOMAIN_AFFECT_CONFIG) { - if (!oldmax || oldmax > persistentDef->mem.max_balloon) - oldmax = persistentDef->mem.max_balloon; + if (!oldmax || oldmax > virDomainDefGetMemoryActual(persistentDef)) + oldmax = virDomainDefGetMemoryActual(persistentDef); } if (newmem > oldmax) { @@ -2595,14 +2596,14 @@ static int qemuDomainGetInfo(virDomainPtr dom, } } - info->maxMem = vm->def->mem.max_balloon; + info->maxMem = virDomainDefGetMemoryActual(vm->def); if (virDomainObjIsActive(vm)) { qemuDomainObjPrivatePtr priv = vm->privateData; if ((vm->def->memballoon != NULL) && (vm->def->memballoon->model == VIR_DOMAIN_MEMBALLOON_MODEL_NONE)) { - info->memory = vm->def->mem.max_balloon; + info->memory = virDomainDefGetMemoryActual(vm->def); } else if (virQEMUCapsGet(priv->qemuCaps, QEMU_CAPS_BALLOON_EVENT)) { info->memory = vm->def->mem.cur_balloon; } else if (qemuDomainJobAllowed(priv, QEMU_JOB_QUERY)) { @@ -2628,7 +2629,7 @@ static int qemuDomainGetInfo(virDomainPtr dom, info->memory = vm->def->mem.cur_balloon; } else if (err == 0) { /* Balloon not supported, so maxmem is always the allocation */ - info->memory = vm->def->mem.max_balloon; + info->memory = virDomainDefGetMemoryActual(vm->def); } else { info->memory = balloon; } @@ -18603,7 +18604,7 @@ qemuDomainGetStatsBalloon(virQEMUDriverPtr driver ATTRIBUTE_UNUSED, if (dom->def->memballoon && dom->def->memballoon->model == VIR_DOMAIN_MEMBALLOON_MODEL_NONE) { - cur_balloon = dom->def->mem.max_balloon; + cur_balloon = virDomainDefGetMemoryActual(dom->def); } else if (virQEMUCapsGet(priv->qemuCaps, QEMU_CAPS_BALLOON_EVENT)) { cur_balloon = dom->def->mem.cur_balloon; } else { @@ -18621,7 +18622,7 @@ qemuDomainGetStatsBalloon(virQEMUDriverPtr driver ATTRIBUTE_UNUSED, &record->nparams, maxparams, "balloon.maximum", - dom->def->mem.max_balloon) < 0) + virDomainDefGetMemoryActual(dom->def)) < 0) return -1; return 0; diff --git a/src/qemu/qemu_hotplug.c b/src/qemu/qemu_hotplug.c index 6ad48f7..7845fd1 100644 --- a/src/qemu/qemu_hotplug.c +++ b/src/qemu/qemu_hotplug.c @@ -1250,9 +1250,11 @@ qemuDomainAttachHostPCIDevice(virQEMUDriverPtr driver, * Kibibytes, but virProcessSetMaxMemLock expects the value in * bytes. */ - memKB = virMemoryLimitIsSet(vm->def->mem.hard_limit) - ? vm->def->mem.hard_limit - : vm->def->mem.max_balloon + (1024 * 1024); + if (virMemoryLimitIsSet(vm->def->mem.hard_limit)) + memKB = vm->def->mem.hard_limit; + else + memKB = virDomainDefGetMemoryActual(vm->def) + (1024 * 1024); + virProcessSetMaxMemLock(vm->pid, memKB * 1024); break; diff --git a/src/qemu/qemu_process.c b/src/qemu/qemu_process.c index d1f089d..019b477 100644 --- a/src/qemu/qemu_process.c +++ b/src/qemu/qemu_process.c @@ -4649,7 +4649,7 @@ int qemuProcessStart(virConnectPtr conn, */ if (virDomainDefNeedsPlacementAdvice(vm->def)) { nodeset = virNumaGetAutoPlacementAdvice(vm->def->vcpus, - vm->def->mem.max_balloon); + virDomainDefGetMemoryActual(vm->def)); if (!nodeset) goto cleanup; diff --git a/src/test/test_driver.c b/src/test/test_driver.c index 9591b7c..d686419 100644 --- a/src/test/test_driver.c +++ b/src/test/test_driver.c @@ -2154,7 +2154,7 @@ static int testDomainGetInfo(virDomainPtr domain, info->state = virDomainObjGetState(privdom, NULL); info->memory = privdom->def->mem.cur_balloon; - info->maxMem = privdom->def->mem.max_balloon; + info->maxMem = virDomainDefGetMemoryActual(privdom->def); info->nrVirtCpu = privdom->def->vcpus; info->cpuTime = ((tv.tv_sec * 1000ll * 1000ll * 1000ll) + (tv.tv_usec * 1000ll)); ret = 0; @@ -2522,7 +2522,7 @@ testDomainGetMaxMemory(virDomainPtr domain) goto cleanup; } - ret = privdom->def->mem.max_balloon; + ret = virDomainDefGetMemoryActual(privdom->def); cleanup: if (privdom) @@ -2548,7 +2548,7 @@ static int testDomainSetMaxMemory(virDomainPtr domain, } /* XXX validate not over host memory wrt to other domains */ - privdom->def->mem.max_balloon = memory; + virDomainDefSetMemoryInitial(privdom->def, memory); ret = 0; cleanup: @@ -2574,7 +2574,7 @@ static int testDomainSetMemory(virDomainPtr domain, goto cleanup; } - if (memory > privdom->def->mem.max_balloon) { + if (memory > virDomainDefGetMemoryActual(privdom->def)) { virReportError(VIR_ERR_INVALID_ARG, __FUNCTION__); goto cleanup; } diff --git a/src/uml/uml_driver.c b/src/uml/uml_driver.c index 68efd18..27731f2 100644 --- a/src/uml/uml_driver.c +++ b/src/uml/uml_driver.c @@ -1802,7 +1802,7 @@ umlDomainGetMaxMemory(virDomainPtr dom) if (virDomainGetMaxMemoryEnsureACL(dom->conn, vm->def) < 0) goto cleanup; - ret = vm->def->mem.max_balloon; + ret = virDomainDefGetMemoryActual(vm->def); cleanup: if (vm) @@ -1838,7 +1838,7 @@ static int umlDomainSetMaxMemory(virDomainPtr dom, unsigned long newmax) goto cleanup; } - vm->def->mem.max_balloon = newmax; + virDomainDefSetMemoryInitial(vm->def, newmax); ret = 0; cleanup: @@ -1875,7 +1875,7 @@ static int umlDomainSetMemory(virDomainPtr dom, unsigned long newmem) goto cleanup; } - if (newmem > vm->def->mem.max_balloon) { + if (newmem > virDomainDefGetMemoryActual(vm->def)) { virReportError(VIR_ERR_INVALID_ARG, "%s", _("cannot set memory higher than max memory")); goto cleanup; @@ -1922,7 +1922,7 @@ static int umlDomainGetInfo(virDomainPtr dom, } } - info->maxMem = vm->def->mem.max_balloon; + info->maxMem = virDomainDefGetMemoryActual(vm->def); info->memory = vm->def->mem.cur_balloon; info->nrVirtCpu = vm->def->vcpus; ret = 0; diff --git a/src/vbox/vbox_common.c b/src/vbox/vbox_common.c index 55d3624..6697ecc 100644 --- a/src/vbox/vbox_common.c +++ b/src/vbox/vbox_common.c @@ -3894,7 +3894,7 @@ static char *vboxDomainGetXMLDesc(virDomainPtr dom, unsigned int flags) * reading and while dumping xml */ /* def->mem.max_balloon = maxMemorySize * 1024; */ - def->mem.max_balloon = memorySize * 1024; + virDomainDefSetMemoryInitial(def, memorySize * 1024); gVBoxAPI.UIMachine.GetCPUCount(machine, &CPUCount); def->maxvcpus = def->vcpus = CPUCount; @@ -6055,7 +6055,7 @@ static char *vboxDomainSnapshotGetXMLDesc(virDomainSnapshotPtr snapshot, * the notation here seems to be inconsistent while * reading and while dumping xml */ - def->dom->mem.max_balloon = memorySize * 1024; + virDomainDefSetMemoryInitial(def->dom, memorySize * 1024); if (VIR_STRDUP(def->dom->os.type, "hvm") < 0) goto cleanup; def->dom->os.arch = virArchFromHost(); diff --git a/src/vmware/vmware_driver.c b/src/vmware/vmware_driver.c index fb7fa5b..36f992b 100644 --- a/src/vmware/vmware_driver.c +++ b/src/vmware/vmware_driver.c @@ -1126,7 +1126,7 @@ vmwareDomainGetInfo(virDomainPtr dom, virDomainInfoPtr info) info->state = virDomainObjGetState(vm, NULL); info->cpuTime = 0; - info->maxMem = vm->def->mem.max_balloon; + info->maxMem = virDomainDefGetMemoryActual(vm->def); info->memory = vm->def->mem.cur_balloon; info->nrVirtCpu = vm->def->vcpus; ret = 0; diff --git a/src/vmx/vmx.c b/src/vmx/vmx.c index ac2542a..95c081b 100644 --- a/src/vmx/vmx.c +++ b/src/vmx/vmx.c @@ -1375,7 +1375,7 @@ virVMXParseConfig(virVMXContext *ctx, goto cleanup; } - def->mem.max_balloon = memsize * 1024; /* Scale from megabytes to kilobytes */ + virDomainDefSetMemoryInitial(def, memsize * 1024); /* Scale from megabytes to kilobytes */ /* vmx:sched.mem.max -> def:mem.cur_balloon */ if (virVMXGetConfigLong(conf, "sched.mem.max", &sched_mem_max, memsize, @@ -1388,8 +1388,8 @@ virVMXParseConfig(virVMXContext *ctx, def->mem.cur_balloon = sched_mem_max * 1024; /* Scale from megabytes to kilobytes */ - if (def->mem.cur_balloon > def->mem.max_balloon) - def->mem.cur_balloon = def->mem.max_balloon; + if (def->mem.cur_balloon > virDomainDefGetMemoryActual(def)) + def->mem.cur_balloon = virDomainDefGetMemoryActual(def); /* vmx:sched.mem.minsize -> def:mem.min_guarantee */ if (virVMXGetConfigLong(conf, "sched.mem.minsize", &sched_mem_minsize, 0, @@ -1402,8 +1402,8 @@ virVMXParseConfig(virVMXContext *ctx, def->mem.min_guarantee = sched_mem_minsize * 1024; /* Scale from megabytes to kilobytes */ - if (def->mem.min_guarantee > def->mem.max_balloon) - def->mem.min_guarantee = def->mem.max_balloon; + if (def->mem.min_guarantee > virDomainDefGetMemoryActual(def)) + def->mem.min_guarantee = virDomainDefGetMemoryActual(def); /* vmx:numvcpus -> def:vcpus */ if (virVMXGetConfigLong(conf, "numvcpus", &numvcpus, 1, true) < 0) @@ -3083,7 +3083,7 @@ virVMXFormatConfig(virVMXContext *ctx, virDomainXMLOptionPtr xmlopt, virDomainDe /* def:mem.max_balloon -> vmx:memsize */ /* max-memory must be a multiple of 4096 kilobyte */ - max_balloon = VIR_DIV_UP(def->mem.max_balloon, 4096) * 4096; + max_balloon = VIR_DIV_UP(virDomainDefGetMemoryActual(def), 4096) * 4096; virBufferAsprintf(&buffer, "memsize = \"%llu\"\n", max_balloon / 1024); /* Scale from kilobytes to megabytes */ diff --git a/src/xen/xm_internal.c b/src/xen/xm_internal.c index 354ab3f..64752df 100644 --- a/src/xen/xm_internal.c +++ b/src/xen/xm_internal.c @@ -479,7 +479,7 @@ xenXMDomainGetInfo(virConnectPtr conn, goto error; memset(info, 0, sizeof(virDomainInfo)); - info->maxMem = entry->def->mem.max_balloon; + info->maxMem = virDomainDefGetMemoryActual(entry->def); info->memory = entry->def->mem.cur_balloon; info->nrVirtCpu = entry->def->vcpus; info->state = VIR_DOMAIN_SHUTOFF; @@ -557,8 +557,8 @@ xenXMDomainSetMemory(virConnectPtr conn, goto cleanup; entry->def->mem.cur_balloon = memory; - if (entry->def->mem.cur_balloon > entry->def->mem.max_balloon) - entry->def->mem.cur_balloon = entry->def->mem.max_balloon; + if (entry->def->mem.cur_balloon > virDomainDefGetMemoryActual(entry->def)) + entry->def->mem.cur_balloon = virDomainDefGetMemoryActual(entry->def); /* If this fails, should we try to undo our changes to the * in-memory representation of the config file. I say not! @@ -600,10 +600,10 @@ xenXMDomainSetMaxMemory(virConnectPtr conn, if (!(entry = virHashLookup(priv->configCache, filename))) goto cleanup; - entry->def->mem.max_balloon = memory; - if (entry->def->mem.cur_balloon > entry->def->mem.max_balloon) - entry->def->mem.cur_balloon = entry->def->mem.max_balloon; + if (entry->def->mem.cur_balloon > memory) + entry->def->mem.cur_balloon = memory; + virDomainDefSetMemoryInitial(entry->def, memory); /* If this fails, should we try to undo our changes to the * in-memory representation of the config file. I say not! */ @@ -636,7 +636,7 @@ xenXMDomainGetMaxMemory(virConnectPtr conn, if (!(entry = virHashLookup(priv->configCache, filename))) goto cleanup; - ret = entry->def->mem.max_balloon; + ret = virDomainDefGetMemoryActual(entry->def); cleanup: xenUnifiedUnlock(priv); diff --git a/src/xenapi/xenapi_driver.c b/src/xenapi/xenapi_driver.c index afb6d6c..b3dd852 100644 --- a/src/xenapi/xenapi_driver.c +++ b/src/xenapi/xenapi_driver.c @@ -1490,7 +1490,7 @@ xenapiDomainGetXMLDesc(virDomainPtr dom, unsigned int flags) VIR_FREE(val); } memory = xenapiDomainGetMaxMemory(dom); - defPtr->mem.max_balloon = memory; + virDomainDefSetMemoryInitial(defPtr, memory); if (xen_vm_get_memory_dynamic_max(session, &dynamic_mem, vm)) { defPtr->mem.cur_balloon = (unsigned long) (dynamic_mem / 1024); } else { diff --git a/src/xenapi/xenapi_utils.c b/src/xenapi/xenapi_utils.c index 91bc649..6ec9375 100644 --- a/src/xenapi/xenapi_utils.c +++ b/src/xenapi/xenapi_utils.c @@ -495,8 +495,8 @@ createVMRecordFromXml(virConnectPtr conn, virDomainDefPtr def, if (def->mem.cur_balloon) (*record)->memory_static_max = (int64_t) (def->mem.cur_balloon * 1024); - if (def->mem.max_balloon) - (*record)->memory_dynamic_max = (int64_t) (def->mem.max_balloon * 1024); + if (virDomainDefGetMemoryActual(def)) + (*record)->memory_dynamic_max = (int64_t) (virDomainDefGetMemoryActual(def) * 1024); else (*record)->memory_dynamic_max = (*record)->memory_static_max; diff --git a/src/xenconfig/xen_common.c b/src/xenconfig/xen_common.c index a2a1474..2712732 100644 --- a/src/xenconfig/xen_common.c +++ b/src/xenconfig/xen_common.c @@ -305,16 +305,18 @@ xenConfigSetString(virConfPtr conf, const char *setting, const char *str) static int xenParseMem(virConfPtr conf, virDomainDefPtr def) { + unsigned long long memory; + if (xenConfigGetULongLong(conf, "memory", &def->mem.cur_balloon, MIN_XEN_GUEST_SIZE * 2) < 0) return -1; - if (xenConfigGetULongLong(conf, "maxmem", &def->mem.max_balloon, + if (xenConfigGetULongLong(conf, "maxmem", &memory, def->mem.cur_balloon) < 0) return -1; def->mem.cur_balloon *= 1024; - def->mem.max_balloon *= 1024; + virDomainDefSetMemoryInitial(def, memory * 1024); return 0; } @@ -1410,7 +1412,7 @@ static int xenFormatMem(virConfPtr conf, virDomainDefPtr def) { if (xenConfigSetInt(conf, "maxmem", - VIR_DIV_UP(def->mem.max_balloon, 1024)) < 0) + VIR_DIV_UP(virDomainDefGetMemoryActual(def), 1024)) < 0) return -1; if (xenConfigSetInt(conf, "memory", diff --git a/src/xenconfig/xen_sxpr.c b/src/xenconfig/xen_sxpr.c index 3e18a7e..5a170d3 100644 --- a/src/xenconfig/xen_sxpr.c +++ b/src/xenconfig/xen_sxpr.c @@ -1155,10 +1155,11 @@ xenParseSxpr(const struct sexpr *root, } } - def->mem.max_balloon = (sexpr_u64(root, "domain/maxmem") << 10); + virDomainDefSetMemoryInitial(def, (sexpr_u64(root, "domain/maxmem") << 10)); def->mem.cur_balloon = (sexpr_u64(root, "domain/memory") << 10); - if (def->mem.cur_balloon > def->mem.max_balloon) - def->mem.cur_balloon = def->mem.max_balloon; + + if (def->mem.cur_balloon > virDomainDefGetMemoryActual(def)) + def->mem.cur_balloon = virDomainDefGetMemoryActual(def); if (cpus != NULL) { if (virBitmapParse(cpus, 0, &def->cpumask, @@ -2213,7 +2214,7 @@ xenFormatSxpr(virConnectPtr conn, virBufferEscapeSexpr(&buf, "(name '%s')", def->name); virBufferAsprintf(&buf, "(memory %llu)(maxmem %llu)", VIR_DIV_UP(def->mem.cur_balloon, 1024), - VIR_DIV_UP(def->mem.max_balloon, 1024)); + VIR_DIV_UP(virDomainDefGetMemoryActual(def), 1024)); virBufferAsprintf(&buf, "(vcpus %u)", def->maxvcpus); /* Computing the vcpu_avail bitmask works because MAX_VIRT_CPUS is either 32, or 64 on a platform where long is big enough. */ -- 2.2.2

The memory sizes in qemu are aligned up to 1 MiB boundaries. There are two places where this was done once for the total size and then for individual NUMA cell sizes. Add a function that will align the sizes in one place so that it's clear where the sizes are aligned. --- src/qemu/qemu_command.c | 7 +++---- src/qemu/qemu_domain.c | 21 +++++++++++++++++++++ src/qemu/qemu_domain.h | 2 ++ 3 files changed, 26 insertions(+), 4 deletions(-) diff --git a/src/qemu/qemu_command.c b/src/qemu/qemu_command.c index b3b573b..ac96583 100644 --- a/src/qemu/qemu_command.c +++ b/src/qemu/qemu_command.c @@ -7270,9 +7270,6 @@ qemuBuildNumaArgStr(virQEMUDriverConfigPtr cfg, /* using of -numa memdev= cannot be combined with -numa mem=, thus we * need to check which approach to use */ for (i = 0; i < ncells; i++) { - unsigned long long cellmem = virDomainNumaGetNodeMemorySize(def->numa, i); - virDomainNumaSetNodeMemorySize(def->numa, i, VIR_ROUND_UP(cellmem, 1024)); - if (virQEMUCapsGet(qemuCaps, QEMU_CAPS_OBJECT_MEMORY_RAM) || virQEMUCapsGet(qemuCaps, QEMU_CAPS_OBJECT_MEMORY_FILE)) { if ((rc = qemuBuildMemoryCellBackendStr(def, qemuCaps, cfg, i, @@ -8495,13 +8492,15 @@ qemuBuildCommandLine(virConnectPtr conn, if (qemuBuildDomainLoaderCommandLine(cmd, def, qemuCaps) < 0) goto error; + if (qemuDomainAlignMemorySizes(def) < 0) + goto error; + /* 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. Update the * XML to reflect our rounding. */ virCommandAddArg(cmd, "-m"); - virDomainDefSetMemoryInitial(def, VIR_ROUND_UP(virDomainDefGetMemoryInitial(def), 1024)); virCommandAddArgFormat(cmd, "%llu", virDomainDefGetMemoryInitial(def) / 1024); if (def->mem.nhugepages && !virDomainNumaGetNodeCount(def->numa)) { diff --git a/src/qemu/qemu_domain.c b/src/qemu/qemu_domain.c index d8a2087..e5f11fc 100644 --- a/src/qemu/qemu_domain.c +++ b/src/qemu/qemu_domain.c @@ -2852,3 +2852,24 @@ qemuDomObjEndAPI(virDomainObjPtr *vm) virObjectUnref(*vm); *vm = NULL; } + + +int +qemuDomainAlignMemorySizes(virDomainDefPtr def) +{ + unsigned long long mem; + size_t ncells = virDomainNumaGetNodeCount(def->numa); + size_t i; + + /* align NUMA cell sizes if relevant */ + for (i = 0; i < ncells; i++) { + mem = virDomainNumaGetNodeMemorySize(def->numa, i); + virDomainNumaSetNodeMemorySize(def->numa, i, VIR_ROUND_UP(mem, 1024)); + } + + /* align initial memory size */ + mem = virDomainDefGetMemoryInitial(def); + virDomainDefSetMemoryInitial(def, VIR_ROUND_UP(mem, 1024)); + + return 0; +} diff --git a/src/qemu/qemu_domain.h b/src/qemu/qemu_domain.h index fe3e2b1..9760095 100644 --- a/src/qemu/qemu_domain.h +++ b/src/qemu/qemu_domain.h @@ -414,4 +414,6 @@ int qemuDomainJobInfoToParams(qemuDomainJobInfoPtr jobInfo, void qemuDomObjEndAPI(virDomainObjPtr *vm); +int qemuDomainAlignMemorySizes(virDomainDefPtr def); + #endif /* __QEMU_DOMAIN_H__ */ -- 2.2.2

Use the NUMA total instead of the configured size both in XML and for uses in the code once NUMA is enabled for a domain. One test case change is necessary as the rounding of the individual cell sizes was not matching the rounding of the total size. --- src/conf/domain_conf.c | 6 ++++++ tests/qemuxml2argvdata/qemuxml2argv-numatune-memnode.args | 2 +- 2 files changed, 7 insertions(+), 1 deletion(-) diff --git a/src/conf/domain_conf.c b/src/conf/domain_conf.c index 462d05e..638c258 100644 --- a/src/conf/domain_conf.c +++ b/src/conf/domain_conf.c @@ -6987,6 +6987,12 @@ virDomainParseMemoryLimit(const char *xpath, unsigned long long virDomainDefGetMemoryInitial(virDomainDefPtr def) { + unsigned long long ret; + + /* return NUMA memory size total in case numa is enabled */ + if ((ret = virDomainNumaGetMemorySize(def->numa)) > 0) + return ret; + return def->mem.max_balloon; } diff --git a/tests/qemuxml2argvdata/qemuxml2argv-numatune-memnode.args b/tests/qemuxml2argvdata/qemuxml2argv-numatune-memnode.args index 513d657..5dd7fcd 100644 --- a/tests/qemuxml2argvdata/qemuxml2argv-numatune-memnode.args +++ b/tests/qemuxml2argvdata/qemuxml2argv-numatune-memnode.args @@ -1,5 +1,5 @@ LC_ALL=C PATH=/bin HOME=/home/test USER=test LOGNAME=test QEMU_AUDIO_DRV=none \ -/usr/bin/kvm -S -M pc -m 24104 -smp 32 \ +/usr/bin/kvm -S -M pc -m 24105 -smp 32 \ -object memory-backend-ram,id=ram-node0,size=20971520,host-nodes=3,\ policy=preferred \ -numa node,nodeid=0,cpus=0,memdev=ram-node0 \ -- 2.2.2

Now that the size of guest's memory can be inferred from the NUMA configuration (if present) make it optional to specify <memory> explicitly. To make sure that memory is specified add a check that some form of memory size was specified. One side effect of this change is that it is no longer possible to specify 0KiB as memory size for the VM, but I don't think it would be any useful to do so. (I can imagine embedded systems without memory, just registers, but that's far from what libvirt is usually doing). Forbidding 0 memory for guests also fixes a few corner cases where 0 was not interpreted correctly and caused failures. (Arguments for numad when using automatic placement, size of the balloon). This fixes problems described in https://bugzilla.redhat.com/show_bug.cgi?id=1161461 Test case changes are added to verify that the schema change and code behave correctly. --- docs/schemas/domaincommon.rng | 18 +++++++------- src/conf/domain_conf.c | 9 ++++++- .../qemuxml2argv-cpu-numa-no-memory-element.args | 7 ++++++ .../qemuxml2argv-cpu-numa-no-memory-element.xml | 24 +++++++++++++++++++ .../qemuxml2argv-minimal-no-memory.xml | 25 +++++++++++++++++++ tests/qemuxml2argvtest.c | 2 ++ .../qemuxml2xmlout-cpu-numa-no-memory-element.xml | 28 ++++++++++++++++++++++ tests/qemuxml2xmltest.c | 1 + 8 files changed, 105 insertions(+), 9 deletions(-) create mode 100644 tests/qemuxml2argvdata/qemuxml2argv-cpu-numa-no-memory-element.args create mode 100644 tests/qemuxml2argvdata/qemuxml2argv-cpu-numa-no-memory-element.xml create mode 100644 tests/qemuxml2argvdata/qemuxml2argv-minimal-no-memory.xml create mode 100644 tests/qemuxml2xmloutdata/qemuxml2xmlout-cpu-numa-no-memory-element.xml diff --git a/docs/schemas/domaincommon.rng b/docs/schemas/domaincommon.rng index 56ea6a4..3e2fe45 100644 --- a/docs/schemas/domaincommon.rng +++ b/docs/schemas/domaincommon.rng @@ -582,14 +582,16 @@ --> <define name="resources"> <interleave> - <element name="memory"> - <ref name='scaledInteger'/> - <optional> - <attribute name="dumpCore"> - <ref name="virOnOff"/> - </attribute> - </optional> - </element> + <optional> + <element name="memory"> + <ref name='scaledInteger'/> + <optional> + <attribute name="dumpCore"> + <ref name="virOnOff"/> + </attribute> + </optional> + </element> + </optional> <optional> <element name="currentMemory"> <ref name='scaledInteger'/> diff --git a/src/conf/domain_conf.c b/src/conf/domain_conf.c index 638c258..3a12b35 100644 --- a/src/conf/domain_conf.c +++ b/src/conf/domain_conf.c @@ -3210,6 +3210,13 @@ virDomainDefPostParseInternal(virDomainDefPtr def, return -1; } + if (virDomainDefGetMemoryInitial(def) == 0) { + virReportError(VIR_ERR_XML_ERROR, "%s", + _("Memory size must be specified via <memory> or in the " + "<numa> configuration")); + return -1; + } + if (def->mem.cur_balloon > virDomainDefGetMemoryActual(def)) { /* Older libvirt could get into this situation due to * rounding; if the discrepancy is less than 4MiB, we silently @@ -13226,7 +13233,7 @@ virDomainDefParseXML(xmlDocPtr xml, /* Extract domain memory */ if (virDomainParseMemory("./memory[1]", NULL, ctxt, - &def->mem.max_balloon, true, true) < 0) + &def->mem.max_balloon, false, true) < 0) goto error; if (virDomainParseMemory("./currentMemory[1]", NULL, ctxt, diff --git a/tests/qemuxml2argvdata/qemuxml2argv-cpu-numa-no-memory-element.args b/tests/qemuxml2argvdata/qemuxml2argv-cpu-numa-no-memory-element.args new file mode 100644 index 0000000..ca34f73 --- /dev/null +++ b/tests/qemuxml2argvdata/qemuxml2argv-cpu-numa-no-memory-element.args @@ -0,0 +1,7 @@ +LC_ALL=C PATH=/bin HOME=/home/test USER=test LOGNAME=test QEMU_AUDIO_DRV=none \ +/usr/bin/qemu -S -M pc \ +-m 214 -smp 16,sockets=2,cores=4,threads=2 \ +-numa node,nodeid=0,cpus=0-7,mem=107 \ +-numa node,nodeid=1,cpus=8-15,mem=107 -nographic -monitor \ +unix:/tmp/test-monitor,server,nowait -no-acpi -boot n -usb -net none -serial none \ +-parallel none diff --git a/tests/qemuxml2argvdata/qemuxml2argv-cpu-numa-no-memory-element.xml b/tests/qemuxml2argvdata/qemuxml2argv-cpu-numa-no-memory-element.xml new file mode 100644 index 0000000..fbdc5e6 --- /dev/null +++ b/tests/qemuxml2argvdata/qemuxml2argv-cpu-numa-no-memory-element.xml @@ -0,0 +1,24 @@ +<domain type='qemu'> + <name>QEMUGuest1</name> + <uuid>c7a5fdbd-edaf-9455-926a-d65c16db1809</uuid> + <currentMemory unit='KiB'>219100</currentMemory> + <vcpu placement='static'>16</vcpu> + <os> + <type arch='x86_64' machine='pc'>hvm</type> + <boot dev='network'/> + </os> + <cpu> + <topology sockets='2' cores='4' threads='2'/> + <numa> + <cell id='1' cpus='8-15' memory='109550' unit='KiB'/> + <cell id='0' cpus='0-7' memory='109550' unit='KiB'/> + </numa> + </cpu> + <clock offset='utc'/> + <on_poweroff>destroy</on_poweroff> + <on_reboot>restart</on_reboot> + <on_crash>destroy</on_crash> + <devices> + <emulator>/usr/bin/qemu</emulator> + </devices> +</domain> diff --git a/tests/qemuxml2argvdata/qemuxml2argv-minimal-no-memory.xml b/tests/qemuxml2argvdata/qemuxml2argv-minimal-no-memory.xml new file mode 100644 index 0000000..6824651 --- /dev/null +++ b/tests/qemuxml2argvdata/qemuxml2argv-minimal-no-memory.xml @@ -0,0 +1,25 @@ +<domain type='qemu'> + <name>QEMUGuest1</name> + <uuid>c7a5fdbd-edaf-9455-926a-d65c16db1809</uuid> + <vcpu placement='static' cpuset='1-4,8-20,525'>1</vcpu> + <os> + <type arch='i686' machine='pc'>hvm</type> + <boot dev='hd'/> + </os> + <clock offset='utc'/> + <on_poweroff>destroy</on_poweroff> + <on_reboot>restart</on_reboot> + <on_crash>destroy</on_crash> + <devices> + <emulator>/usr/bin/qemu</emulator> + <disk type='block' device='disk'> + <source dev='/dev/HostVG/QEMUGuest1'/> + <target dev='hda' bus='ide'/> + <address type='drive' controller='0' bus='0' target='0' unit='0'/> + </disk> + <controller type='usb' index='0'/> + <controller type='ide' index='0'/> + <controller type='pci' index='0' model='pci-root'/> + <memballoon model='virtio'/> + </devices> +</domain> diff --git a/tests/qemuxml2argvtest.c b/tests/qemuxml2argvtest.c index 00fd044..373595d 100644 --- a/tests/qemuxml2argvtest.c +++ b/tests/qemuxml2argvtest.c @@ -594,6 +594,7 @@ mymain(void) unsetenv("SDL_AUDIODRIVER"); DO_TEST("minimal", QEMU_CAPS_NAME); + DO_TEST_PARSE_ERROR("minimal-no-memory", NONE); DO_TEST("minimal-msg-timestamp", QEMU_CAPS_NAME, QEMU_CAPS_MSG_TIMESTAMP); DO_TEST("minimal-s390", QEMU_CAPS_NAME); DO_TEST("machine-aliases1", NONE); @@ -1235,6 +1236,7 @@ mymain(void) DO_TEST("cpu-strict1", QEMU_CAPS_KVM); DO_TEST("cpu-numa1", NONE); DO_TEST("cpu-numa2", QEMU_CAPS_SMP_TOPOLOGY); + DO_TEST("cpu-numa-no-memory-element", QEMU_CAPS_SMP_TOPOLOGY); DO_TEST_PARSE_ERROR("cpu-numa3", NONE); DO_TEST_FAILURE("cpu-numa-disjoint", NONE); DO_TEST("cpu-numa-disjoint", QEMU_CAPS_NUMA); diff --git a/tests/qemuxml2xmloutdata/qemuxml2xmlout-cpu-numa-no-memory-element.xml b/tests/qemuxml2xmloutdata/qemuxml2xmlout-cpu-numa-no-memory-element.xml new file mode 100644 index 0000000..58f40b9 --- /dev/null +++ b/tests/qemuxml2xmloutdata/qemuxml2xmlout-cpu-numa-no-memory-element.xml @@ -0,0 +1,28 @@ +<domain type='qemu'> + <name>QEMUGuest1</name> + <uuid>c7a5fdbd-edaf-9455-926a-d65c16db1809</uuid> + <memory unit='KiB'>219100</memory> + <currentMemory unit='KiB'>219100</currentMemory> + <vcpu placement='static'>16</vcpu> + <os> + <type arch='x86_64' machine='pc'>hvm</type> + <boot dev='network'/> + </os> + <cpu> + <topology sockets='2' cores='4' threads='2'/> + <numa> + <cell id='0' cpus='0-7' memory='109550' unit='KiB'/> + <cell id='1' cpus='8-15' memory='109550' unit='KiB'/> + </numa> + </cpu> + <clock offset='utc'/> + <on_poweroff>destroy</on_poweroff> + <on_reboot>restart</on_reboot> + <on_crash>destroy</on_crash> + <devices> + <emulator>/usr/bin/qemu</emulator> + <controller type='usb' index='0'/> + <controller type='pci' index='0' model='pci-root'/> + <memballoon model='virtio'/> + </devices> +</domain> diff --git a/tests/qemuxml2xmltest.c b/tests/qemuxml2xmltest.c index 2bec2b2..d255595 100644 --- a/tests/qemuxml2xmltest.c +++ b/tests/qemuxml2xmltest.c @@ -408,6 +408,7 @@ mymain(void) DO_TEST_DIFFERENT("cpu-numa1"); DO_TEST_DIFFERENT("cpu-numa2"); + DO_TEST_DIFFERENT("cpu-numa-no-memory-element"); DO_TEST("cpu-numa-disjoint"); DO_TEST("cpu-numa-memshared"); -- 2.2.2

On 03/06/2015 09:40 AM, Peter Krempa wrote:
Pavel's series changed few same places thus the previous version no longer applies.
Peter Krempa (4): conf: Replace access to def->mem.max_balloon with accessor functions qemu: command: Add helper to align memory sizes conf: Automatically use NUMA memory size in case NUMA is enabled conf: Make specifying <memory> optional
docs/schemas/domaincommon.rng | 18 ++--- src/conf/domain_conf.c | 81 +++++++++++++++++++--- src/conf/domain_conf.h | 4 ++ src/hyperv/hyperv_driver.c | 2 +- src/libvirt_private.syms | 3 + src/libxl/libxl_conf.c | 2 +- src/libxl/libxl_driver.c | 8 +-- src/lxc/lxc_cgroup.c | 2 +- src/lxc/lxc_driver.c | 12 ++-- src/lxc/lxc_fuse.c | 4 +- src/lxc/lxc_native.c | 4 +- src/openvz/openvz_driver.c | 2 +- src/parallels/parallels_driver.c | 2 +- src/parallels/parallels_sdk.c | 12 ++-- src/phyp/phyp_driver.c | 11 +-- src/qemu/qemu_command.c | 23 +++--- src/qemu/qemu_domain.c | 21 ++++++ src/qemu/qemu_domain.h | 2 + src/qemu/qemu_driver.c | 21 +++--- src/qemu/qemu_hotplug.c | 8 ++- src/qemu/qemu_process.c | 2 +- src/test/test_driver.c | 8 +-- src/uml/uml_driver.c | 8 +-- src/vbox/vbox_common.c | 4 +- src/vmware/vmware_driver.c | 2 +- src/vmx/vmx.c | 12 ++-- src/xen/xm_internal.c | 14 ++-- src/xenapi/xenapi_driver.c | 2 +- src/xenapi/xenapi_utils.c | 4 +- src/xenconfig/xen_common.c | 8 ++- src/xenconfig/xen_sxpr.c | 9 +-- .../qemuxml2argv-cpu-numa-no-memory-element.args | 7 ++ .../qemuxml2argv-cpu-numa-no-memory-element.xml | 24 +++++++ .../qemuxml2argv-minimal-no-memory.xml | 25 +++++++ .../qemuxml2argv-numatune-memnode.args | 2 +- tests/qemuxml2argvtest.c | 2 + .../qemuxml2xmlout-cpu-numa-no-memory-element.xml | 28 ++++++++ tests/qemuxml2xmltest.c | 1 + 38 files changed, 299 insertions(+), 105 deletions(-) create mode 100644 tests/qemuxml2argvdata/qemuxml2argv-cpu-numa-no-memory-element.args create mode 100644 tests/qemuxml2argvdata/qemuxml2argv-cpu-numa-no-memory-element.xml create mode 100644 tests/qemuxml2argvdata/qemuxml2argv-minimal-no-memory.xml create mode 100644 tests/qemuxml2xmloutdata/qemuxml2xmlout-cpu-numa-no-memory-element.xml
Since this is the order of your repo on git://pipo.sk/pipo/libvirt.git, I'll start with these. Couple of general comments... - I think the new function names are better - I think one can tell that the "KiB" or "MiB" was realized at some point in time after "KB" and "MB" - as there's stray comments and variables using the (I guess) now incorrect terminology. Not that I'm asking for them to be changed, just an "observation". - Whether they snuck in after you started this - there are a few direct references to mem.max_balloon in the bhyve_command.c. Should they be replaced by the new accessors? - Because it became apparent in patch 4... In virDomainDefParseXML, just to be "complete" shouldn't the: /* Extract domain memory */ if (virDomainParseMemory("./memory[1]", NULL, ctxt, &def->mem.max_balloon, false, true) < 0) be replaced with something like: unsigned long long memory_value; ... /* Extract domain memory */ if (virDomainParseMemory("./memory[1]", NULL, ctxt, &memory_value, false, true) < 0) { ... } virDomainDefSetMemoryInitial(def, memory_value); Although it seems like overkill - it's just making sure no one tries to cut-copy-paste in the future. - With having virDomainDefGetMemoryInitial understand NUMA vs Balloon, should virDomainDefSetMemoryInitial check and not set max_balloon? Theoretically if <memory> wasn't filled, then we're possible setting something we shouldn't which could then be output when the domain is saved, right? ACK in principal - perhaps give it a day to see if anyone else has comments or issues... John

On Thu, Mar 12, 2015 at 10:07:04 -0400, John Ferlan wrote:
On 03/06/2015 09:40 AM, Peter Krempa wrote:
Pavel's series changed few same places thus the previous version no longer applies.
Peter Krempa (4): conf: Replace access to def->mem.max_balloon with accessor functions qemu: command: Add helper to align memory sizes conf: Automatically use NUMA memory size in case NUMA is enabled conf: Make specifying <memory> optional
docs/schemas/domaincommon.rng | 18 ++--- src/conf/domain_conf.c | 81 +++++++++++++++++++--- src/conf/domain_conf.h | 4 ++ src/hyperv/hyperv_driver.c | 2 +- src/libvirt_private.syms | 3 + src/libxl/libxl_conf.c | 2 +- src/libxl/libxl_driver.c | 8 +-- src/lxc/lxc_cgroup.c | 2 +- src/lxc/lxc_driver.c | 12 ++-- src/lxc/lxc_fuse.c | 4 +- src/lxc/lxc_native.c | 4 +- src/openvz/openvz_driver.c | 2 +- src/parallels/parallels_driver.c | 2 +- src/parallels/parallels_sdk.c | 12 ++-- src/phyp/phyp_driver.c | 11 +-- src/qemu/qemu_command.c | 23 +++--- src/qemu/qemu_domain.c | 21 ++++++ src/qemu/qemu_domain.h | 2 + src/qemu/qemu_driver.c | 21 +++--- src/qemu/qemu_hotplug.c | 8 ++- src/qemu/qemu_process.c | 2 +- src/test/test_driver.c | 8 +-- src/uml/uml_driver.c | 8 +-- src/vbox/vbox_common.c | 4 +- src/vmware/vmware_driver.c | 2 +- src/vmx/vmx.c | 12 ++-- src/xen/xm_internal.c | 14 ++-- src/xenapi/xenapi_driver.c | 2 +- src/xenapi/xenapi_utils.c | 4 +- src/xenconfig/xen_common.c | 8 ++- src/xenconfig/xen_sxpr.c | 9 +-- .../qemuxml2argv-cpu-numa-no-memory-element.args | 7 ++ .../qemuxml2argv-cpu-numa-no-memory-element.xml | 24 +++++++ .../qemuxml2argv-minimal-no-memory.xml | 25 +++++++ .../qemuxml2argv-numatune-memnode.args | 2 +- tests/qemuxml2argvtest.c | 2 + .../qemuxml2xmlout-cpu-numa-no-memory-element.xml | 28 ++++++++ tests/qemuxml2xmltest.c | 1 + 38 files changed, 299 insertions(+), 105 deletions(-) create mode 100644 tests/qemuxml2argvdata/qemuxml2argv-cpu-numa-no-memory-element.args create mode 100644 tests/qemuxml2argvdata/qemuxml2argv-cpu-numa-no-memory-element.xml create mode 100644 tests/qemuxml2argvdata/qemuxml2argv-minimal-no-memory.xml create mode 100644 tests/qemuxml2xmloutdata/qemuxml2xmlout-cpu-numa-no-memory-element.xml
Since this is the order of your repo on git://pipo.sk/pipo/libvirt.git, I'll start with these.
Couple of general comments...
- I think the new function names are better
- I think one can tell that the "KiB" or "MiB" was realized at some point in time after "KB" and "MB" - as there's stray comments and variables using the (I guess) now incorrect terminology. Not that I'm asking for them to be changed, just an "observation".
- Whether they snuck in after you started this - there are a few direct references to mem.max_balloon in the bhyve_command.c. Should they be replaced by the new accessors?
I actually missed fixing the bhyve driver. I've done it now.
- Because it became apparent in patch 4... In virDomainDefParseXML, just to be "complete" shouldn't the:
/* Extract domain memory */ if (virDomainParseMemory("./memory[1]", NULL, ctxt, &def->mem.max_balloon, false, true) < 0)
be replaced with something like:
unsigned long long memory_value; ... /* Extract domain memory */ if (virDomainParseMemory("./memory[1]", NULL, ctxt, &memory_value, false, true) < 0) { ... } virDomainDefSetMemoryInitial(def, memory_value);
Although it seems like overkill - it's just making sure no one tries to cut-copy-paste in the future.
In the config the implementation is actually "private" and since it's only a single place we should be okay here.
- With having virDomainDefGetMemoryInitial understand NUMA vs Balloon, should virDomainDefSetMemoryInitial check and not set max_balloon? Theoretically if <memory> wasn't filled, then we're possible setting something we shouldn't which could then be output when the domain is saved, right?
It actually will be overwritten by the correct value
ACK in principal - perhaps give it a day to see if anyone else has comments or issues...
Thanks I'll push this series in a while.
John
Peter
participants (2)
-
John Ferlan
-
Peter Krempa