[libvirt] [PATCH 0/3] add binding guest numa nodes to host numa nodes support

Since qemu has supported numa option -memdev config: -object memory-backend-ram,size=1024M,policy=membind,host-nodes=0,id=ram-node0 \ -numa node,nodeid=0,cpus=0,memdev=ram-node0 \ for binding guest numa nodes to host numa nodes. So we introduce this capability in libvirt by configuration domain XML like: ... <cpu> <numa> <cell cpus='0-1' memdev='ram0'/> </numa> </cpu> ... <devices> <memdev type='ram' merge='yes' dump='yes' prealloc='yes'> <name>ram0</name> <capacity unit='MiB'>1000</capacity> <source host-nodes='0-1' policy='bind' /> </memdev> </devices> Chen Fan (3): numa: add '-numa memdev=' support conf: add memdev device in <devices> XML tests: add numa -memdev testing and docs support docs/formatdomain.html.in | 71 ++++++- docs/schemas/domaincommon.rng | 76 +++++++- src/conf/cpu_conf.c | 73 ++++++-- src/conf/cpu_conf.h | 13 +- src/conf/domain_conf.c | 203 ++++++++++++++++++++- src/conf/domain_conf.h | 42 +++++ src/libvirt_private.syms | 4 + src/qemu/qemu_capabilities.c | 4 + src/qemu/qemu_capabilities.h | 2 + src/qemu/qemu_command.c | 84 ++++++++- src/qemu/qemu_command.h | 4 + src/qemu/qemu_hotplug.c | 1 + tests/qemuxml2argvdata/qemuxml2argv-cpu-numa3.args | 9 + tests/qemuxml2argvdata/qemuxml2argv-cpu-numa3.xml | 35 ++++ tests/qemuxml2argvdata/qemuxml2argv-cpu-numa4.args | 10 + tests/qemuxml2argvdata/qemuxml2argv-cpu-numa4.xml | 35 ++++ tests/qemuxml2argvtest.c | 2 + 17 files changed, 644 insertions(+), 24 deletions(-) create mode 100644 tests/qemuxml2argvdata/qemuxml2argv-cpu-numa3.args create mode 100644 tests/qemuxml2argvdata/qemuxml2argv-cpu-numa3.xml create mode 100644 tests/qemuxml2argvdata/qemuxml2argv-cpu-numa4.args create mode 100644 tests/qemuxml2argvdata/qemuxml2argv-cpu-numa4.xml -- 1.9.3

Since qemu has supported '-numa memdev=ram0' command option, so libvirt should add numa element to support specified memdev attrubute in XML. Signed-off-by: Chen Fan <chen.fan.fnst@cn.fujitsu.com> --- src/conf/cpu_conf.c | 73 +++++++++++++++++++++++++++++++++++++++---------- src/conf/cpu_conf.h | 13 ++++++++- src/qemu/qemu_command.c | 10 +++++-- 3 files changed, 78 insertions(+), 18 deletions(-) diff --git a/src/conf/cpu_conf.c b/src/conf/cpu_conf.c index ebdaa19..2d0980e 100644 --- a/src/conf/cpu_conf.c +++ b/src/conf/cpu_conf.c @@ -29,6 +29,7 @@ #include "cpu_conf.h" #include "domain_conf.h" #include "virstring.h" +#include "c-ctype.h" #define VIR_FROM_THIS VIR_FROM_CPU @@ -84,6 +85,8 @@ virCPUDefFree(virCPUDefPtr def) for (i = 0; i < def->ncells; i++) { virBitmapFree(def->cells[i].cpumask); VIR_FREE(def->cells[i].cpustr); + if (def->cells[i].memtype == VIR_CPU_CELL_MEMORY_DEV) + VIR_FREE(def->cells[i].data.memstr); } VIR_FREE(def->cells); VIR_FREE(def->vendor_id); @@ -153,7 +156,13 @@ virCPUDefCopy(const virCPUDef *cpu) for (i = 0; i < cpu->ncells; i++) { copy->cells[i].cellid = cpu->cells[i].cellid; - copy->cells[i].mem = cpu->cells[i].mem; + copy->cells[i].memtype = cpu->cells[i].memtype; + if (cpu->cells[i].memtype == VIR_CPU_CELL_MEMORY_DEV) { + if (VIR_STRDUP(copy->cells[i].data.memstr, cpu->cells[i].data.memstr) < 0) + goto error; + } else { + copy->cells[i].data.mem = cpu->cells[i].data.mem; + } copy->cells[i].cpumask = virBitmapNewCopy(cpu->cells[i].cpumask); @@ -436,7 +445,7 @@ virCPUDefParseXML(xmlNodePtr node, def->ncells = n; for (i = 0; i < n; i++) { - char *cpus, *memory; + char *cpus, *memory, *memdev; int ret, ncpus = 0; def->cells[i].cellid = i; @@ -455,20 +464,52 @@ virCPUDefParseXML(xmlNodePtr node, def->cells_cpus += ncpus; memory = virXMLPropString(nodes[i], "memory"); - if (!memory) { - virReportError(VIR_ERR_XML_ERROR, "%s", - _("Missing 'memory' attribute in NUMA cell")); - goto error; - } - - ret = virStrToLong_ui(memory, NULL, 10, &def->cells[i].mem); - if (ret == -1) { + memdev = virXMLPropString(nodes[i], "memdev"); + if (memory || memdev) { + if (memory && memdev) { + virReportError(VIR_ERR_XML_ERROR, "%s", + _("Both 'memory' and 'memdev' attribute in NUMA cell is not allowed")); + goto error; + } + + if (memory) { + ret = virStrToLong_ui(memory, NULL, 10, &def->cells[i].data.mem); + if (ret == -1) { + virReportError(VIR_ERR_XML_ERROR, "%s", + _("Invalid 'memory' attribute in NUMA cell")); + VIR_FREE(memory); + goto error; + } + def->cells[i].memtype = VIR_CPU_CELL_MEMORY_SIZE; + VIR_FREE(memory); + } else { + if (strlen(memdev) < 1) { + virReportError(VIR_ERR_XML_ERROR, "%s", + _("Empty 'memdev' attribute in NUMA cell")); + VIR_FREE(memdev); + goto error; + } + + if (!c_isalpha(memdev[0])) { + virReportError(VIR_ERR_XML_ERROR, "%s", + _("Invalid 'memdev' attribute name in NUMA cell, " + "it must begin with a letter")); + VIR_FREE(memdev); + goto error; + } + + if (VIR_STRDUP(def->cells[i].data.memstr, memdev) < 0) { + VIR_FREE(memdev); + goto error; + } + def->cells[i].memtype = VIR_CPU_CELL_MEMORY_DEV; + VIR_FREE(memdev); + } + } else { virReportError(VIR_ERR_XML_ERROR, "%s", - _("Invalid 'memory' attribute in NUMA cell")); - VIR_FREE(memory); + _("Missing 'memory' or 'memdev' attribute in NUMA cell")); goto error; } - VIR_FREE(memory); } } @@ -648,7 +689,11 @@ virCPUDefFormatBuf(virBufferPtr buf, for (i = 0; i < def->ncells; i++) { virBufferAddLit(buf, "<cell"); virBufferAsprintf(buf, " cpus='%s'", def->cells[i].cpustr); - virBufferAsprintf(buf, " memory='%d'", def->cells[i].mem); + if (def->cells[i].memtype == VIR_CPU_CELL_MEMORY_DEV) { + virBufferAsprintf(buf, " memdev='%s'", def->cells[i].data.memstr); + } else { + virBufferAsprintf(buf, " memory='%d'", def->cells[i].data.mem); + } virBufferAddLit(buf, "/>\n"); } virBufferAdjustIndent(buf, -2); diff --git a/src/conf/cpu_conf.h b/src/conf/cpu_conf.h index 8c932ce..b1ebd9c 100644 --- a/src/conf/cpu_conf.h +++ b/src/conf/cpu_conf.h @@ -90,13 +90,24 @@ struct _virCPUFeatureDef { int policy; /* enum virCPUFeaturePolicy */ }; +typedef enum { + VIR_CPU_CELL_MEMORY_SIZE, + VIR_CPU_CELL_MEMORY_DEV, + + VIR_CPU_CELL_MEMORY_LAST +} virCPUCellMemoryType; + typedef struct _virCellDef virCellDef; typedef virCellDef *virCellDefPtr; struct _virCellDef { int cellid; virBitmapPtr cpumask; /* CPUs that are part of this node */ char *cpustr; /* CPUs stored in string form for dumpxml */ - unsigned int mem; /* Node memory in kB */ + int memtype; + union { + char *memstr; /* Node memory device name */ + unsigned int mem; /* Node memory in kB */ + } data; }; typedef struct _virCPUDef virCPUDef; diff --git a/src/qemu/qemu_command.c b/src/qemu/qemu_command.c index 2caee66..b351f60 100644 --- a/src/qemu/qemu_command.c +++ b/src/qemu/qemu_command.c @@ -6560,9 +6560,13 @@ qemuBuildNumaArgStr(const virDomainDef *def, virCommandPtr cmd) goto cleanup; } virBufferAdd(&buf, cpumask, -1); - def->cpu->cells[i].mem = VIR_DIV_UP(def->cpu->cells[i].mem, - 1024) * 1024; - virBufferAsprintf(&buf, ",mem=%d", def->cpu->cells[i].mem / 1024); + if (def->cpu->cells[i].memtype == VIR_CPU_CELL_MEMORY_DEV) { + virBufferAsprintf(&buf, ",memdev=%s", def->cpu->cells[i].data.memstr); + } else { + def->cpu->cells[i].data.mem = VIR_DIV_UP(def->cpu->cells[i].data.mem, + 1024) * 1024; + virBufferAsprintf(&buf, ",mem=%d", def->cpu->cells[i].data.mem / 1024); + } if (virBufferError(&buf)) { virReportOOMError(); -- 1.9.3

Since qemu has supported memory-backend-ram and memory-backend-file object, so we should add a new 'memdev' device type in <devices> XML to introduce the memory element, Its definition like the following: <memdev type='ram' merge='yes' dump='yes' prealloc='yes'> <name>ram0</name> <capacity unit='MiB'>1000</capacity> <source host-nodes='0-1' policy='bind' /> </memdev> then we enable to support -numa memdev=ram0 command line for binding guest numa nodes to host numa nodes. Signed-off-by: Chen Fan <chen.fan.fnst@cn.fujitsu.com> --- src/conf/domain_conf.c | 203 ++++++++++++++++++++++++++++++++++++++++++- src/conf/domain_conf.h | 42 +++++++++ src/libvirt_private.syms | 4 + src/qemu/qemu_capabilities.c | 4 + src/qemu/qemu_capabilities.h | 2 + src/qemu/qemu_command.c | 74 ++++++++++++++++ src/qemu/qemu_command.h | 4 + src/qemu/qemu_hotplug.c | 1 + 8 files changed, 333 insertions(+), 1 deletion(-) diff --git a/src/conf/domain_conf.c b/src/conf/domain_conf.c index be81dbe..c55bf47 100644 --- a/src/conf/domain_conf.c +++ b/src/conf/domain_conf.c @@ -204,7 +204,8 @@ VIR_ENUM_IMPL(virDomainDevice, VIR_DOMAIN_DEVICE_LAST, "chr", "memballoon", "nvram", - "rng") + "rng", + "memdev") VIR_ENUM_IMPL(virDomainDeviceAddress, VIR_DOMAIN_DEVICE_ADDRESS_TYPE_LAST, "none", @@ -454,6 +455,16 @@ VIR_ENUM_IMPL(virDomainMemballoonModel, VIR_DOMAIN_MEMBALLOON_MODEL_LAST, "xen", "none") +VIR_ENUM_IMPL(virDomainMemDev, VIR_DOMAIN_MEMDEV_LAST, + "ram", + "file") + +VIR_ENUM_IMPL(virDomainHostNodePolicy, VIR_DOMAIN_HOST_NODE_POLICY_LAST, + "default", + "preferred", + "bind", + "interleave") + VIR_ENUM_IMPL(virDomainSmbiosMode, VIR_DOMAIN_SMBIOS_LAST, "none", "emulate", @@ -770,6 +781,10 @@ static void virDomainObjDispose(void *obj); static void virDomainObjListDispose(void *obj); static void virDomainXMLOptionClassDispose(void *obj); +static int +virDomainParseMemory(const char *xpath, xmlXPathContextPtr ctxt, + unsigned long long *mem, bool required); + static int virDomainObjOnceInit(void) { if (!(virDomainObjClass = virClassNew(virClassForObjectLockable(), @@ -1661,6 +1676,18 @@ void virDomainMemballoonDefFree(virDomainMemballoonDefPtr def) VIR_FREE(def); } +void virDomainMemDevDefFree(virDomainMemDevDefPtr def) +{ + if (!def) + return; + + VIR_FREE(def->name); + VIR_FREE(def->mempath); + VIR_FREE(def->hostnodes); + VIR_FREE(def); +} + + void virDomainNVRAMDefFree(virDomainNVRAMDefPtr def) { if (!def) @@ -1864,6 +1891,9 @@ void virDomainDeviceDefFree(virDomainDeviceDefPtr def) case VIR_DOMAIN_DEVICE_NVRAM: virDomainNVRAMDefFree(def->data.nvram); break; + case VIR_DOMAIN_DEVICE_MEMDEV: + virDomainMemDevDefFree(def->data.memdev); + break; case VIR_DOMAIN_DEVICE_LAST: case VIR_DOMAIN_DEVICE_NONE: break; @@ -2543,6 +2573,7 @@ virDomainDeviceGetInfo(virDomainDeviceDefPtr device) /* The following devices do not contain virDomainDeviceInfo */ case VIR_DOMAIN_DEVICE_LEASE: case VIR_DOMAIN_DEVICE_GRAPHICS: + case VIR_DOMAIN_DEVICE_MEMDEV: case VIR_DOMAIN_DEVICE_LAST: case VIR_DOMAIN_DEVICE_NONE: break; @@ -2780,6 +2811,7 @@ virDomainDeviceInfoIterateInternal(virDomainDefPtr def, case VIR_DOMAIN_DEVICE_NVRAM: case VIR_DOMAIN_DEVICE_LAST: case VIR_DOMAIN_DEVICE_RNG: + case VIR_DOMAIN_DEVICE_MEMDEV: break; } @@ -9192,6 +9224,104 @@ virDomainMemballoonDefParseXML(xmlNodePtr node, goto cleanup; } +static virDomainMemDevDefPtr +virDomainMemDevDefParseXML(xmlNodePtr node, + xmlXPathContextPtr ctxt) +{ + char *type; + virDomainMemDevDefPtr def; + xmlNodePtr save = ctxt->node; + char *tmp = NULL; + char *policy = NULL; + + if (VIR_ALLOC(def) < 0) + return NULL; + + type = virXMLPropString(node, "type"); + if (type == NULL) { + virReportError(VIR_ERR_XML_ERROR, "%s", + _("memory device must contain a type name")); + goto error; + } + + if ((def->type = virDomainMemDevTypeFromString(type)) < 0) { + virReportError(VIR_ERR_CONFIG_UNSUPPORTED, + _("unknown memory device type '%s'"), type); + goto error; + } + + if ((tmp = virXMLPropString(node, "merge")) != NULL) { + if (STREQ(tmp, "yes")) + def->merge = true; + VIR_FREE(tmp); + } + + if ((tmp = virXMLPropString(node, "dump")) != NULL) { + if (STREQ(tmp, "yes")) + def->dump = true; + VIR_FREE(tmp); + } + + if ((tmp = virXMLPropString(node, "prealloc")) != NULL) { + if (STREQ(tmp, "yes")) + def->prealloc = true; + VIR_FREE(tmp); + } + + ctxt->node = node; + + def->name = virXPathString("string(./name)", ctxt); + if (!def->name) { + virReportError(VIR_ERR_XML_ERROR, "%s", + _("Missing memory device name element")); + goto error; + } + + /* Extract memory capacity */ + if (virDomainParseMemory("./capacity", ctxt, + &def->capacity, true) < 0) + goto error; + + def->mempath = virXPathString("string(./source/@mem-path)", ctxt); + if (def->type == VIR_DOMAIN_MEMDEV_FILE && !def->mempath) { + virReportError(VIR_ERR_XML_ERROR, "%s", + _("The mem-path element must be specified when " + "memory device type is 'file'")); + goto error; + } + + if (def->type == VIR_DOMAIN_MEMDEV_RAM && def->mempath) { + virReportError(VIR_ERR_XML_ERROR, "%s", + _("The mem-path element should be not specified when " + "memory device type is 'ram'")); + goto error; + } + + def->hostnodes = virXPathString("string(./source/@host-nodes)", ctxt); + + policy = virXPathString("string(./source/@policy)", ctxt); + if (policy != NULL) { + if ((def->policy = virDomainHostNodePolicyTypeFromString(policy)) < 0) { + virReportError(VIR_ERR_CONFIG_UNSUPPORTED, + _("unknown host node policy: '%s'"), policy); + goto error; + } + } + +cleanup: + VIR_FREE(type); + VIR_FREE(policy); + ctxt->node = save; + + return def; + + error: + virDomainMemDevDefFree(def); + def = NULL; + goto cleanup; +} + + static virDomainNVRAMDefPtr virDomainNVRAMDefParseXML(xmlNodePtr node, unsigned int flags) @@ -10067,6 +10197,10 @@ virDomainDeviceDefParse(const char *xmlStr, if (!(dev->data.nvram = virDomainNVRAMDefParseXML(node, flags))) goto error; break; + case VIR_DOMAIN_DEVICE_MEMDEV: + if (!(dev->data.memdev = virDomainMemDevDefParseXML(node, ctxt))) + goto error; + break; case VIR_DOMAIN_DEVICE_NONE: case VIR_DOMAIN_DEVICE_LAST: break; @@ -12752,6 +12886,24 @@ virDomainDefParseXML(xmlDocPtr xml, } VIR_FREE(nodes); + /* analysis of the memory devices */ + if ((n = virXPathNodeSet("./devices/memdev", ctxt, &nodes)) < 0) { + goto error; + } + + if (n && VIR_ALLOC_N(def->memdevs, n) < 0) + goto error; + + for (i = 0; i < n; i++) { + virDomainMemDevDefPtr memdev = virDomainMemDevDefParseXML(nodes[i], ctxt); + if (!memdev) + goto error; + + def->memdevs[def->nmemdevs++] = memdev; + } + VIR_FREE(nodes); + + /* Parse the TPM devices */ if ((n = virXPathNodeSet("./devices/tpm", ctxt, &nodes)) < 0) goto error; @@ -16262,6 +16414,50 @@ virDomainTPMDefFormat(virBufferPtr buf, return 0; } +static int +virDomainMemDevDefFormat(virBufferPtr buf, + virDomainMemDevDefPtr def) +{ + const char *type = virDomainMemDevTypeToString(def->type); + + if (!type) { + virReportError(VIR_ERR_INTERNAL_ERROR, + _("unexpected memdev type %d"), def->type); + return -1; + } + + virBufferAsprintf(buf, "<memdev type='%s'", type); + if (def->merge) + virBufferAddLit(buf, " merge='yes'"); + if (def->dump) + virBufferAddLit(buf, " dump='yes'"); + if (def->prealloc) + virBufferAddLit(buf, " prealloc='yes'"); + virBufferAddLit(buf, ">\n"); + + virBufferAdjustIndent(buf, 2); + + virBufferAsprintf(buf, "<name>%s</name>\n", def->name); + virBufferAsprintf(buf, "<capacity unit='KiB'>%llu</capacity>\n", + def->capacity); + + if (def->type == VIR_DOMAIN_MEMDEV_FILE || def->hostnodes || def->policy) { + virBufferAddLit(buf, "<source"); + if (def->type == VIR_DOMAIN_MEMDEV_FILE) + virBufferAsprintf(buf, " mem-path='%s'", def->mempath); + if (def->hostnodes) + virBufferAsprintf(buf, " host-nodes='%s'", def->hostnodes); + if (def->policy) + virBufferAsprintf(buf, " policy='%s'", + virDomainHostNodePolicyTypeToString(def->policy)); + virBufferAddLit(buf, "/>\n"); + } + + virBufferAdjustIndent(buf, -2); + virBufferAddLit(buf, "</memdev>\n"); + + return 0; +} static int virDomainSoundDefFormat(virBufferPtr buf, @@ -17916,6 +18112,10 @@ virDomainDefFormatInternal(virDomainDefPtr def, if (def->memballoon) virDomainMemballoonDefFormat(buf, def->memballoon, flags); + for (n = 0; n < def->nmemdevs; n++) + if (virDomainMemDevDefFormat(buf, def->memdevs[n]) < 0) + goto error; + if (def->rng) virDomainRNGDefFormat(buf, def->rng, flags); @@ -19291,6 +19491,7 @@ virDomainDeviceDefCopy(virDomainDeviceDefPtr src, case VIR_DOMAIN_DEVICE_NONE: case VIR_DOMAIN_DEVICE_SMARTCARD: case VIR_DOMAIN_DEVICE_MEMBALLOON: + case VIR_DOMAIN_DEVICE_MEMDEV: case VIR_DOMAIN_DEVICE_NVRAM: case VIR_DOMAIN_DEVICE_LAST: virReportError(VIR_ERR_INTERNAL_ERROR, diff --git a/src/conf/domain_conf.h b/src/conf/domain_conf.h index a6ac95a..33e57bc 100644 --- a/src/conf/domain_conf.h +++ b/src/conf/domain_conf.h @@ -109,6 +109,9 @@ typedef virDomainChrDef *virDomainChrDefPtr; typedef struct _virDomainMemballoonDef virDomainMemballoonDef; typedef virDomainMemballoonDef *virDomainMemballoonDefPtr; +typedef struct _virDomainMemDevDef virDomainMemDevDef; +typedef virDomainMemDevDef *virDomainMemDevDefPtr; + typedef struct _virDomainNVRAMDef virDomainNVRAMDef; typedef virDomainNVRAMDef *virDomainNVRAMDefPtr; @@ -151,6 +154,7 @@ typedef enum { VIR_DOMAIN_DEVICE_MEMBALLOON, VIR_DOMAIN_DEVICE_NVRAM, VIR_DOMAIN_DEVICE_RNG, + VIR_DOMAIN_DEVICE_MEMDEV, VIR_DOMAIN_DEVICE_LAST } virDomainDeviceType; @@ -178,6 +182,7 @@ struct _virDomainDeviceDef { virDomainMemballoonDefPtr memballoon; virDomainNVRAMDefPtr nvram; virDomainRNGDefPtr rng; + virDomainMemDevDefPtr memdev; } data; }; @@ -1487,6 +1492,37 @@ struct _virDomainNVRAMDef { virDomainDeviceInfo info; }; +enum { + VIR_DOMAIN_MEMDEV_RAM, + VIR_DOMAIN_MEMDEV_FILE, + + VIR_DOMAIN_MEMDEV_LAST +}; + +struct _virDomainMemDevDef { + char *name; + int type; + + unsigned long long capacity; /* bytes */ + + bool merge; + bool dump; + bool prealloc; + + char *mempath; + char *hostnodes; + int policy; +}; + +enum { + VIR_DOMAIN_HOST_NODE_POLICY_DEFAULT = 0, + VIR_DOMAIN_HOST_NODE_POLICY_PREFERRED, + VIR_DOMAIN_HOST_NODE_POLICY_BIND, + VIR_DOMAIN_HOST_NODE_POLICY_INTERLEAVE, + + VIR_DOMAIN_HOST_NODE_POLICY_LAST +}; + typedef enum { VIR_DOMAIN_SMBIOS_NONE = 0, VIR_DOMAIN_SMBIOS_EMULATE, @@ -1970,6 +2006,9 @@ struct _virDomainDef { size_t nseclabels; virSecurityLabelDefPtr *seclabels; + size_t nmemdevs; + virDomainMemDevDefPtr *memdevs; + /* Only 1 */ virDomainWatchdogDefPtr watchdog; virDomainMemballoonDefPtr memballoon; @@ -2159,6 +2198,7 @@ int virDomainChrSourceDefCopy(virDomainChrSourceDefPtr src, void virDomainSoundCodecDefFree(virDomainSoundCodecDefPtr def); void virDomainSoundDefFree(virDomainSoundDefPtr def); void virDomainMemballoonDefFree(virDomainMemballoonDefPtr def); +void virDomainMemDevDefFree(virDomainMemDevDefPtr def); void virDomainNVRAMDefFree(virDomainNVRAMDefPtr def); void virDomainWatchdogDefFree(virDomainWatchdogDefPtr def); void virDomainVideoDefFree(virDomainVideoDefPtr def); @@ -2575,6 +2615,8 @@ VIR_ENUM_DECL(virDomainSoundCodec) VIR_ENUM_DECL(virDomainSoundModel) VIR_ENUM_DECL(virDomainMemDump) VIR_ENUM_DECL(virDomainMemballoonModel) +VIR_ENUM_DECL(virDomainMemDev) +VIR_ENUM_DECL(virDomainHostNodePolicy) VIR_ENUM_DECL(virDomainSmbiosMode) VIR_ENUM_DECL(virDomainWatchdogModel) VIR_ENUM_DECL(virDomainWatchdogAction) diff --git a/src/libvirt_private.syms b/src/libvirt_private.syms index 122c572..06d6d7f 100644 --- a/src/libvirt_private.syms +++ b/src/libvirt_private.syms @@ -293,6 +293,8 @@ virDomainHostdevModeTypeToString; virDomainHostdevRemove; virDomainHostdevSubsysPCIBackendTypeToString; virDomainHostdevSubsysTypeToString; +virDomainHostNodePolicyTypeFromString; +virDomainHostNodePolicyTypeToString; virDomainHubTypeFromString; virDomainHubTypeToString; virDomainHypervTypeFromString; @@ -316,6 +318,8 @@ virDomainLockFailureTypeFromString; virDomainLockFailureTypeToString; virDomainMemballoonModelTypeFromString; virDomainMemballoonModelTypeToString; +virDomainMemDevTypeFromString; +virDomainMemDevTypeToString; virDomainMemDumpTypeFromString; virDomainMemDumpTypeToString; virDomainNetDefFormat; diff --git a/src/qemu/qemu_capabilities.c b/src/qemu/qemu_capabilities.c index 08c3d04..718da2b 100644 --- a/src/qemu/qemu_capabilities.c +++ b/src/qemu/qemu_capabilities.c @@ -256,6 +256,8 @@ VIR_ENUM_IMPL(virQEMUCaps, QEMU_CAPS_LAST, "usb-kbd", /* 165 */ "host-pci-multidomain", "msg-timestamp", + "memory-backend-ram", + "memory-backend-file", ); @@ -1440,6 +1442,8 @@ struct virQEMUCapsStringFlags virQEMUCapsObjectTypes[] = { { "ich9-intel-hda", QEMU_CAPS_DEVICE_ICH9_INTEL_HDA }, { "pvpanic", QEMU_CAPS_DEVICE_PANIC }, { "usb-kbd", QEMU_CAPS_DEVICE_USB_KBD }, + { "memory-backend-ram", QEMU_CAPS_MEMORY_BACKEND_RAM }, + { "memory-backend-file", QEMU_CAPS_MEMORY_BACKEND_FILE }, }; static struct virQEMUCapsStringFlags virQEMUCapsObjectPropsVirtioBlk[] = { diff --git a/src/qemu/qemu_capabilities.h b/src/qemu/qemu_capabilities.h index d755caa..53171be 100644 --- a/src/qemu/qemu_capabilities.h +++ b/src/qemu/qemu_capabilities.h @@ -206,6 +206,8 @@ enum virQEMUCapsFlags { QEMU_CAPS_DEVICE_USB_KBD = 165, /* -device usb-kbd */ QEMU_CAPS_HOST_PCI_MULTIDOMAIN = 166, /* support domain > 0 in host pci address */ QEMU_CAPS_MSG_TIMESTAMP = 167, /* -msg timestamp */ + QEMU_CAPS_MEMORY_BACKEND_RAM = 168, /* -object memory-backend-ram */ + QEMU_CAPS_MEMORY_BACKEND_FILE = 169, /* -object memory-backend-file */ QEMU_CAPS_LAST, /* this must always be the last item */ }; diff --git a/src/qemu/qemu_command.c b/src/qemu/qemu_command.c index b351f60..25a6e00 100644 --- a/src/qemu/qemu_command.c +++ b/src/qemu/qemu_command.c @@ -4743,6 +4743,71 @@ qemuBuildNVRAMDevStr(virDomainNVRAMDefPtr dev) } char * +qemuBuildMemObjectStr(virDomainMemDevDefPtr dev, + virQEMUCapsPtr qemuCaps) +{ + virBuffer buf = VIR_BUFFER_INITIALIZER; + + switch (dev->type) { + case VIR_DOMAIN_MEMDEV_RAM: + if (!virQEMUCapsGet(qemuCaps, QEMU_CAPS_MEMORY_BACKEND_RAM)) { + virReportError(VIR_ERR_CONFIG_UNSUPPORTED, "%s", + _("The object memory-backend-ram " + "is not supported in this QEMU binary")); + goto error; + } + virBufferAddLit(&buf, "memory-backend-ram"); + break; + case VIR_DOMAIN_MEMDEV_FILE: + if (!virQEMUCapsGet(qemuCaps, QEMU_CAPS_MEMORY_BACKEND_FILE)) { + virReportError(VIR_ERR_CONFIG_UNSUPPORTED, "%s", + _("The object memory-backend-file " + "is not supported in this QEMU binary")); + goto error; + } + virBufferAddLit(&buf, "memory-backend-file"); + break; + default: + virReportError(VIR_ERR_XML_ERROR, + _("memdev unsupported with type '%s'"), + virDomainMemDevTypeToString(dev->type)); + goto error; + } + virBufferAsprintf(&buf, ",id=%s", dev->name); + dev->capacity = VIR_DIV_UP(dev->capacity, 1024) * 1024; + virBufferAsprintf(&buf, ",size=%lluK", dev->capacity); + + if (dev->merge) + virBufferAddLit(&buf, ",merge=yes"); + if (dev->dump) + virBufferAddLit(&buf, ",dump=yes"); + if (dev->prealloc) + virBufferAddLit(&buf, ",prealloc=yes"); + + if (dev->type == VIR_DOMAIN_MEMDEV_FILE && dev->mempath) + virBufferAsprintf(&buf, ",mem-path=%s", dev->mempath); + + if (dev->hostnodes) + virBufferAsprintf(&buf, ",host-nodes=%s", dev->hostnodes); + + if (dev->policy) + virBufferAsprintf(&buf, ",policy=%s", + virDomainHostNodePolicyTypeToString(dev->policy)); + + if (virBufferError(&buf)) { + virReportOOMError(); + goto error; + } + + return virBufferContentAndReset(&buf); + +error: + virBufferFreeAndReset(&buf); + return NULL; +} + + +char * qemuBuildUSBInputDevStr(virDomainDefPtr def, virDomainInputDefPtr dev, virQEMUCapsPtr qemuCaps) @@ -7394,6 +7459,15 @@ qemuBuildCommandLine(virConnectPtr conn, } mlock = def->mem.locked; + for (i = 0; i < def->nmemdevs; i++) { + char *objectstr; + virDomainMemDevDefPtr memptr = def->memdevs[i]; + + if (!(objectstr = qemuBuildMemObjectStr(memptr, qemuCaps))) + goto error; + virCommandAddArgList(cmd, "-object", objectstr, NULL); + } + virCommandAddArg(cmd, "-smp"); if (!(smp = qemuBuildSmpArgStr(def, qemuCaps))) goto error; diff --git a/src/qemu/qemu_command.h b/src/qemu/qemu_command.h index afbd6ff..72f4e77 100644 --- a/src/qemu/qemu_command.h +++ b/src/qemu/qemu_command.h @@ -274,4 +274,8 @@ qemuParseKeywords(const char *str, int qemuGetDriveSourceString(virStorageSourcePtr src, virConnectPtr conn, char **source); + +char * +qemuBuildMemObjectStr(virDomainMemDevDefPtr dev, + virQEMUCapsPtr qemuCaps); #endif /* __QEMU_COMMAND_H__*/ diff --git a/src/qemu/qemu_hotplug.c b/src/qemu/qemu_hotplug.c index 7289055..c9f4df9 100644 --- a/src/qemu/qemu_hotplug.c +++ b/src/qemu/qemu_hotplug.c @@ -2818,6 +2818,7 @@ qemuDomainRemoveDevice(virQEMUDriverPtr driver, case VIR_DOMAIN_DEVICE_MEMBALLOON: case VIR_DOMAIN_DEVICE_NVRAM: case VIR_DOMAIN_DEVICE_RNG: + case VIR_DOMAIN_DEVICE_MEMDEV: case VIR_DOMAIN_DEVICE_LAST: virReportError(VIR_ERR_OPERATION_UNSUPPORTED, _("don't know how to remove a %s device"), -- 1.9.3

Signed-off-by: Chen Fan <chen.fan.fnst@cn.fujitsu.com> --- docs/formatdomain.html.in | 71 +++++++++++++++++++- docs/schemas/domaincommon.rng | 76 +++++++++++++++++++++- tests/qemuxml2argvdata/qemuxml2argv-cpu-numa3.args | 9 +++ tests/qemuxml2argvdata/qemuxml2argv-cpu-numa3.xml | 35 ++++++++++ tests/qemuxml2argvdata/qemuxml2argv-cpu-numa4.args | 10 +++ tests/qemuxml2argvdata/qemuxml2argv-cpu-numa4.xml | 35 ++++++++++ tests/qemuxml2argvtest.c | 2 + 7 files changed, 233 insertions(+), 5 deletions(-) create mode 100644 tests/qemuxml2argvdata/qemuxml2argv-cpu-numa3.args create mode 100644 tests/qemuxml2argvdata/qemuxml2argv-cpu-numa3.xml create mode 100644 tests/qemuxml2argvdata/qemuxml2argv-cpu-numa4.args create mode 100644 tests/qemuxml2argvdata/qemuxml2argv-cpu-numa4.xml diff --git a/docs/formatdomain.html.in b/docs/formatdomain.html.in index 1b6ced8..5313c76 100644 --- a/docs/formatdomain.html.in +++ b/docs/formatdomain.html.in @@ -1032,6 +1032,9 @@ <numa> <cell cpus='0-3' memory='512000'/> <cell cpus='4-7' memory='512000'/> + <!-- OR --> + <cell cpus='0-3' memdev='ram0'/> + <cell cpus='4-7' memdev='ram1'/> </numa> ... </cpu> @@ -1041,8 +1044,11 @@ Each <code>cell</code> element specifies a NUMA cell or a NUMA node. <code>cpus</code> specifies the CPU or range of CPUs that are part of the node. <code>memory</code> specifies the node memory in kibibytes - (i.e. blocks of 1024 bytes). Each cell or node is assigned cellid - or nodeid in the increasing order starting from 0. + (i.e. blocks of 1024 bytes). <span class="since">Since 2.1</span>, + <code>memdev</code> specifies the Host NUMA node that the Guest NUMA + node bind to. if <code>memdev</code> specified for one node, it must + be specified for all nodes. Each cell or node is assigned cellid or + nodeid in the increasing order starting from 0. </p> <p> @@ -5249,6 +5255,67 @@ qemu-kvm -net nic,model=? /dev/null </dd> </dl> + <h4><a name="elementsMemdev">memory device</a></h4> + <p> + A memory device can be added to guest via memdev element. + <span class="since">Since 2.1, QEMU and KVM only</span> + </p> + <p> + Example: usage of memory device configuration + </p> +<pre> + ... + <devices> + <memdev type='ram' merge='yes' dump='yes' prealloc='yes'> + <name>ram0</name> + <capacity unit='KiB'>102400</capacity> + <source host-nodes='0-1' policy='bind'/> + </memdev> + </devices> + ... +</pre> + <dl> + <dt><code>type</code></dt> + <dd> + <p> + The required type attribute specifies what type of memory device + is provided. Valid type are: + </p> + <ul> + <li>'ram' — memory ram backend.</li> + <li>'file' — memory file backend.</li> + </ul> + </dd> + <dt><code>merge</code></dt> + <dd> + <p> + The optional merge attribute enables memory merge support. + </p> + </dd> + <dt><code>dump</code></dt> + <dd> + <p> + The optional dump attribute enables to dump memory device's memory in a + core dump file. + </p> + </dd> + <dt><code>prealloc</code></dt> + <dd> + <p> + The optional prealloc attribute enables memory preallocation. + </p> + </dd> + <dt><code>source</code></dt> + <dd>The source element describes the device as seen from the host. + the optional <code>mem-path</code> element is required when specified type = 'file'. + the optional <code>host-nodes</code> element specified the Host nodes ids that the + Guest memory device binds to. the optional <code>policy</code> element specified the + memory policy of memory device. the <code>policy</code> is either "default", "preferred", + "bind", "interleave". defaults to "default". + </dd> + </dl> + + <h3><a name="seclabel">Security label</a></h3> <p> diff --git a/docs/schemas/domaincommon.rng b/docs/schemas/domaincommon.rng index 6cc922c..0334b28 100644 --- a/docs/schemas/domaincommon.rng +++ b/docs/schemas/domaincommon.rng @@ -3148,6 +3148,32 @@ </optional> </element> </define> + <define name="memdev"> + <element name="memdev"> + <attribute name="type"> + <choice> + <value>ram</value> + <value>file</value> + </choice> + </attribute> + <optional> + <attribute name='merge'> + <value>yes</value> + </attribute> + </optional> + <optional> + <attribute name='dump'> + <value>yes</value> + </attribute> + </optional> + <optional> + <attribute name='prealloc'> + <value>yes</value> + </attribute> + </optional> + <ref name="memorybackend"/> + </element> + </define> <define name="parallel"> <element name="parallel"> <ref name="qemucdev"/> @@ -3671,6 +3697,7 @@ <ref name="redirfilter"/> <ref name="rng"/> <ref name="tpm"/> + <ref name="memdev"/> </choice> </zeroOrMore> <optional> @@ -3895,9 +3922,18 @@ <attribute name="cpus"> <ref name="cpuset"/> </attribute> - <attribute name="memory"> - <ref name="memoryKB"/> - </attribute> + <choice> + <group> + <attribute name="memory"> + <ref name="memoryKB"/> + </attribute> + </group> + <group> + <attribute name='memdev'> + <ref name='aliasName'/> + </attribute> + </group> + </choice> </element> </define> @@ -4527,6 +4563,40 @@ </element> <empty/> </define> + <define name='memorybackend'> + <element name='name'> + <ref name='aliasName'/> + </element> + <element name='capacity'> + <ref name='scaledInteger'/> + </element> + <optional> + <element name='source'> + <optional> + <attribute name='mem-path'> + <ref name='absFilePath'/> + </attribute> + </optional> + <optional> + <attribute name='host-nodes'> + <data type="string"> + <param name='pattern'>[a-zA-Z0-9\-_]+</param> + </data> + </attribute> + </optional> + <optional> + <attribute name='policy'> + <choice> + <value>default</value> + <value>preferred</value> + <value>bind</value> + <value>interleave</value> + </choice> + </attribute> + </optional> + </element> + </optional> + </define> <define name="ccwCssidRange"> <choice> <data type="string"> diff --git a/tests/qemuxml2argvdata/qemuxml2argv-cpu-numa3.args b/tests/qemuxml2argvdata/qemuxml2argv-cpu-numa3.args new file mode 100644 index 0000000..1057adc --- /dev/null +++ b/tests/qemuxml2argvdata/qemuxml2argv-cpu-numa3.args @@ -0,0 +1,9 @@ +LC_ALL=C PATH=/bin HOME=/home/test USER=test LOGNAME=test QEMU_AUDIO_DRV=none \ +/usr/bin/qemu -S -M pc \ +-m 214 \ +-object memory-backend-ram,id=ram0,size=109568K,merge=yes,dump=yes,prealloc=yes,host-nodes=0,policy=bind \ +-object memory-backend-ram,id=ram1,size=109568K,host-nodes=0,policy=bind \ +-smp 16 -numa node,nodeid=0,cpus=0-7,memdev=ram0 \ +-numa node,nodeid=1,cpus=8-15,memdev=ram1 -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-numa3.xml b/tests/qemuxml2argvdata/qemuxml2argv-cpu-numa3.xml new file mode 100644 index 0000000..7a299af --- /dev/null +++ b/tests/qemuxml2argvdata/qemuxml2argv-cpu-numa3.xml @@ -0,0 +1,35 @@ +<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 cpus="0-7" memdev="ram0"/> + <cell cpus="8-15" memdev="ram1"/> + </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> + <memdev type='ram' merge='yes' dump='yes' prealloc='yes'> + <name>ram0</name> + <capacity unit='KiB'>109550</capacity> + <source host-nodes='0' policy='bind'/> + </memdev> + <memdev type='ram'> + <name>ram1</name> + <capacity unit='KiB'>109550</capacity> + <source host-nodes='0' policy='bind'/> + </memdev> + </devices> +</domain> diff --git a/tests/qemuxml2argvdata/qemuxml2argv-cpu-numa4.args b/tests/qemuxml2argvdata/qemuxml2argv-cpu-numa4.args new file mode 100644 index 0000000..3a1cd94 --- /dev/null +++ b/tests/qemuxml2argvdata/qemuxml2argv-cpu-numa4.args @@ -0,0 +1,10 @@ +LC_ALL=C PATH=/bin HOME=/home/test USER=test LOGNAME=test QEMU_AUDIO_DRV=none \ +/usr/bin/qemu -S -M pc \ +-m 214 \ +-object memory-backend-file,id=ram0,size=109568K,merge=yes,dump=yes,prealloc=yes,mem-path=/hugepages,\ +host-nodes=0,policy=bind \ +-object memory-backend-file,id=ram1,size=109568K,mem-path=/hugepages,host-nodes=0,policy=bind \ +-smp 16 -numa node,nodeid=0,cpus=0-7,memdev=ram0 \ +-numa node,nodeid=1,cpus=8-15,memdev=ram1 -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-numa4.xml b/tests/qemuxml2argvdata/qemuxml2argv-cpu-numa4.xml new file mode 100644 index 0000000..03e3a28 --- /dev/null +++ b/tests/qemuxml2argvdata/qemuxml2argv-cpu-numa4.xml @@ -0,0 +1,35 @@ +<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 cpus="0-7" memdev="ram0"/> + <cell cpus="8-15" memdev="ram1"/> + </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> + <memdev type='file' merge='yes' dump='yes' prealloc='yes'> + <name>ram0</name> + <capacity unit='KiB'>109550</capacity> + <source mem-path='/hugepages' host-nodes='0' policy='bind'/> + </memdev> + <memdev type='file'> + <name>ram1</name> + <capacity unit='KiB'>109550</capacity> + <source mem-path='/hugepages' host-nodes='0' policy='bind'/> + </memdev> + </devices> +</domain> diff --git a/tests/qemuxml2argvtest.c b/tests/qemuxml2argvtest.c index 24d104e..1b9e48a 100644 --- a/tests/qemuxml2argvtest.c +++ b/tests/qemuxml2argvtest.c @@ -1169,6 +1169,8 @@ mymain(void) DO_TEST("cpu-strict1", NONE); DO_TEST("cpu-numa1", NONE); DO_TEST("cpu-numa2", QEMU_CAPS_SMP_TOPOLOGY); + DO_TEST("cpu-numa3", QEMU_CAPS_MEMORY_BACKEND_RAM); + DO_TEST("cpu-numa4", QEMU_CAPS_MEMORY_BACKEND_FILE); DO_TEST("cpu-host-model", NONE); skipLegacyCPUs = true; DO_TEST("cpu-host-model-fallback", NONE); -- 1.9.3

On Wed, Jun 25, 2014 at 02:42:29PM +0800, Chen Fan wrote:
Since qemu has supported numa option -memdev config: -object memory-backend-ram,size=1024M,policy=membind,host-nodes=0,id=ram-node0 \ -numa node,nodeid=0,cpus=0,memdev=ram-node0 \ for binding guest numa nodes to host numa nodes.
I'm afraid, it seems you've duplicated work that's already in progress: https://www.redhat.com/archives/libvir-list/2014-June/msg00201.html Regards, Daniel -- |: http://berrange.com -o- http://www.flickr.com/photos/dberrange/ :| |: http://libvirt.org -o- http://virt-manager.org :| |: http://autobuild.org -o- http://search.cpan.org/~danberr/ :| |: http://entangle-photo.org -o- http://live.gnome.org/gtk-vnc :|
participants (2)
-
Chen Fan
-
Daniel P. Berrange