diff --git a/docs/formatdomain.html.in b/docs/formatdomain.html.in
index eb203670d3..3c85fc591c 100644
--- a/docs/formatdomain.html.in
+++ b/docs/formatdomain.html.in
@@ -709,6 +709,8 @@
...
<numatune>
<memory mode="strict" nodeset="1-4,^3"/>
+ <memnode cellid="0" mode="strict" nodeset="1"/>
+ <memnode cellid="2" mode="preferred" nodeset="2"/>
</numatune>
...
</domain>
@@ -745,6 +747,19 @@
Since 0.9.3
+
memnode
+
+ Optional memnode
elements can specify memory allocation
+ policies per each guest NUMA node. For those nodes having no
+ corresponding memnode
element, the default from
+ element memory
will be used. Attribute cellid
+ addresses guest NUMA node for which the settings are applied.
+ Attributes mode
and nodeset
have the same
+ meaning and syntax as in memory
element.
+
+ This setting is not compatible with automatic placement.
+ QEMU Since 1.2.7
+
diff --git a/docs/schemas/domaincommon.rng b/docs/schemas/domaincommon.rng
index 1d3b4d8544..a0ea3003a0 100644
--- a/docs/schemas/domaincommon.rng
+++ b/docs/schemas/domaincommon.rng
@@ -789,6 +789,23 @@
+
+
+
+
+
+
+
+ strict
+ preferred
+ interleave
+
+
+
+
+
+
+
diff --git a/src/conf/numatune_conf.c b/src/conf/numatune_conf.c
index 6ce1e2d5e3..a39c028b3d 100644
--- a/src/conf/numatune_conf.c
+++ b/src/conf/numatune_conf.c
@@ -42,17 +42,140 @@ VIR_ENUM_IMPL(virDomainNumatunePlacement,
"static",
"auto");
+typedef struct _virDomainNumatuneNode virDomainNumatuneNode;
+typedef virDomainNumatuneNode *virDomainNumatuneNodePtr;
+
struct _virDomainNumatune {
struct {
+ bool specified;
virBitmapPtr nodeset;
virDomainNumatuneMemMode mode;
virDomainNumatunePlacement placement;
} memory; /* pinning for all the memory */
+ struct _virDomainNumatuneNode {
+ virBitmapPtr nodeset;
+ virDomainNumatuneMemMode mode;
+ } *mem_nodes; /* fine tuning per guest node */
+ size_t nmem_nodes;
+
/* Future NUMA tuning related stuff should go here. */
};
+static int
+virDomainNumatuneNodeParseXML(virDomainDefPtr def,
+ xmlXPathContextPtr ctxt)
+{
+ char *tmp = NULL;
+ int n = 0;;
+ int ret = -1;
+ size_t i = 0;
+ xmlNodePtr *nodes = NULL;
+
+ if ((n = virXPathNodeSet("./numatune/memnode", ctxt, &nodes)) < 0) {
+ virReportError(VIR_ERR_INTERNAL_ERROR, "%s",
+ _("Cannot extract memnode nodes"));
+ goto cleanup;
+ }
+
+ if (!n)
+ return 0;
+
+ if (def->numatune && def->numatune->memory.specified &&
+ def->numatune->memory.placement == VIR_DOMAIN_NUMATUNE_PLACEMENT_AUTO) {
+ virReportError(VIR_ERR_CONFIG_UNSUPPORTED, "%s",
+ _("Per-node binding is not compatible with "
+ "automatic NUMA placement."));
+ goto cleanup;
+ }
+
+ if (!def->cpu || !def->cpu->ncells) {
+ virReportError(VIR_ERR_XML_ERROR, "%s",
+ _("Element 'memnode' is invalid without "
+ "any guest NUMA cells"));
+ goto cleanup;
+ }
+
+ if (!def->numatune && VIR_ALLOC(def->numatune) < 0)
+ goto cleanup;
+
+ VIR_FREE(def->numatune->mem_nodes);
+ if (VIR_ALLOC_N(def->numatune->mem_nodes, def->cpu->ncells) < 0)
+ goto cleanup;
+
+ def->numatune->nmem_nodes = def->cpu->ncells;
+
+ for (i = 0; i < n; i++) {
+ int mode = 0;
+ unsigned int cellid = 0;
+ virDomainNumatuneNodePtr mem_node = NULL;
+ xmlNodePtr cur_node = nodes[i];
+
+ tmp = virXMLPropString(cur_node, "cellid");
+ if (!tmp) {
+ virReportError(VIR_ERR_XML_ERROR, "%s",
+ _("Missing required cellid attribute "
+ "in memnode element"));
+ goto cleanup;
+ }
+ if (virStrToLong_uip(tmp, NULL, 10, &cellid) < 0) {
+ virReportError(VIR_ERR_XML_ERROR,
+ _("Invalid cellid attribute in memnode element: %s"),
+ tmp);
+ goto cleanup;
+ }
+ VIR_FREE(tmp);
+
+ if (cellid >= def->numatune->nmem_nodes) {
+ virReportError(VIR_ERR_XML_ERROR, "%s",
+ _("Argument 'cellid' in memnode element must "
+ "correspond to existing guest's NUMA cell"));
+ goto cleanup;
+ }
+
+ mem_node = &def->numatune->mem_nodes[cellid];
+
+ if (mem_node->nodeset) {
+ virReportError(VIR_ERR_XML_ERROR,
+ _("Multiple memnode elements with cellid %u"),
+ cellid);
+ goto cleanup;
+ }
+
+ tmp = virXMLPropString(cur_node, "mode");
+ if (!tmp) {
+ mem_node->mode = VIR_DOMAIN_NUMATUNE_MEM_STRICT;
+ } else {
+ if ((mode = virDomainNumatuneMemModeTypeFromString(tmp)) < 0) {
+ virReportError(VIR_ERR_XML_ERROR, "%s",
+ _("Invalid mode attribute in memnode element"));
+ goto cleanup;
+ }
+ VIR_FREE(tmp);
+ mem_node->mode = mode;
+ }
+
+ tmp = virXMLPropString(cur_node, "nodeset");
+ if (!tmp) {
+ virReportError(VIR_ERR_XML_ERROR, "%s",
+ _("Missing required nodeset attribute "
+ "in memnode element"));
+ goto cleanup;
+ }
+ if (virBitmapParse(tmp, 0, &mem_node->nodeset,
+ VIR_DOMAIN_CPUMASK_LEN) < 0)
+ goto cleanup;
+ VIR_FREE(tmp);
+ }
+
+ ret = 0;
+ cleanup:
+ VIR_FREE(nodes);
+ VIR_FREE(tmp);
+ return ret;
+}
+
int
virDomainNumatuneParseXML(virDomainDefPtr def,
xmlXPathContextPtr ctxt)
@@ -82,8 +205,11 @@ virDomainNumatuneParseXML(virDomainDefPtr def,
def->numatune = NULL;
}
- if (!node && def->placement_mode != VIR_DOMAIN_CPU_PLACEMENT_MODE_AUTO)
+ if (!node && def->placement_mode != VIR_DOMAIN_CPU_PLACEMENT_MODE_AUTO) {
+ if (virDomainNumatuneNodeParseXML(def, ctxt) < 0)
+ goto cleanup;
return 0;
+ }
if (!node) {
/* We know that def->placement_mode is "auto" if we're here */
@@ -125,10 +251,9 @@ virDomainNumatuneParseXML(virDomainDefPtr def,
if (virDomainNumatuneSet(def, placement, mode, nodeset) < 0)
goto cleanup;
- if (!n) {
- ret = 0;
+ if (virDomainNumatuneNodeParseXML(def, ctxt) < 0)
goto cleanup;
- }
+
ret = 0;
cleanup:
@@ -143,6 +268,7 @@ virDomainNumatuneFormatXML(virBufferPtr buf,
{
const char *tmp = NULL;
char *nodeset = NULL;
+ size_t i = 0;
if (!numatune)
return 0;
@@ -150,17 +276,36 @@ virDomainNumatuneFormatXML(virBufferPtr buf,
virBufferAddLit(buf, "\n");
virBufferAdjustIndent(buf, 2);
- tmp = virDomainNumatuneMemModeTypeToString(numatune->memory.mode);
- virBufferAsprintf(buf, "memory.specified) {
+ tmp = virDomainNumatuneMemModeTypeToString(numatune->memory.mode);
+ virBufferAsprintf(buf, "memory.placement == VIR_DOMAIN_NUMATUNE_PLACEMENT_STATIC) {
- if (!(nodeset = virBitmapFormat(numatune->memory.nodeset)))
+ if (numatune->memory.placement == VIR_DOMAIN_NUMATUNE_PLACEMENT_STATIC) {
+ if (!(nodeset = virBitmapFormat(numatune->memory.nodeset)))
+ return -1;
+ virBufferAsprintf(buf, "nodeset='%s'/>\n", nodeset);
+ VIR_FREE(nodeset);
+ } else if (numatune->memory.placement) {
+ tmp = virDomainNumatunePlacementTypeToString(numatune->memory.placement);
+ virBufferAsprintf(buf, "placement='%s'/>\n", tmp);
+ }
+ }
+
+ for (i = 0; i < numatune->nmem_nodes; i++) {
+ virDomainNumatuneNodePtr mem_node = &numatune->mem_nodes[i];
+
+ if (!mem_node->nodeset)
+ continue;
+
+ if (!(nodeset = virBitmapFormat(mem_node->nodeset)))
return -1;
- virBufferAsprintf(buf, "nodeset='%s'/>\n", nodeset);
+
+ virBufferAsprintf(buf,
+ "\n",
+ i,
+ virDomainNumatuneMemModeTypeToString(mem_node->mode),
+ nodeset);
VIR_FREE(nodeset);
- } else if (numatune->memory.placement) {
- tmp = virDomainNumatunePlacementTypeToString(numatune->memory.placement);
- virBufferAsprintf(buf, "placement='%s'/>\n", tmp);
}
virBufferAdjustIndent(buf, -2);
@@ -171,10 +316,15 @@ virDomainNumatuneFormatXML(virBufferPtr buf,
void
virDomainNumatuneFree(virDomainNumatunePtr numatune)
{
+ size_t i = 0;
+
if (!numatune)
return;
virBitmapFree(numatune->memory.nodeset);
+ for (i = 0; i < numatune->nmem_nodes; i++)
+ virBitmapFree(numatune->mem_nodes[i].nodeset);
+ VIR_FREE(numatune->mem_nodes);
VIR_FREE(numatune);
}
@@ -182,7 +332,7 @@ virDomainNumatuneFree(virDomainNumatunePtr numatune)
virDomainNumatuneMemMode
virDomainNumatuneGetMode(virDomainNumatunePtr numatune)
{
- return numatune ? numatune->memory.mode : 0;
+ return (numatune && numatune->memory.specified) ? numatune->memory.mode : 0;
}
virBitmapPtr
@@ -318,6 +468,8 @@ virDomainNumatuneSet(virDomainDefPtr def,
if (placement != -1)
numatune->memory.placement = placement;
+ numatune->memory.specified = true;
+
ret = 0;
cleanup:
return ret;
@@ -333,6 +485,12 @@ virDomainNumatuneEquals(virDomainNumatunePtr n1,
if (!n1 || !n2)
return false;
+ if (!n1->memory.specified && !n2->memory.specified)
+ return true;
+
+ if (!n1->memory.specified || !n2->memory.specified)
+ return false;
+
if (n1->memory.mode != n2->memory.mode)
return false;
@@ -348,6 +506,9 @@ virDomainNumatuneHasPlacementAuto(virDomainNumatunePtr numatune)
if (!numatune)
return false;
+ if (!numatune->memory.specified)
+ return false;
+
if (numatune->memory.placement == VIR_DOMAIN_NUMATUNE_PLACEMENT_AUTO)
return true;
diff --git a/tests/qemuxml2argvdata/qemuxml2argv-numatune-memnode-no-memory.xml b/tests/qemuxml2argvdata/qemuxml2argv-numatune-memnode-no-memory.xml
new file mode 100644
index 0000000000..4b2efa2105
--- /dev/null
+++ b/tests/qemuxml2argvdata/qemuxml2argv-numatune-memnode-no-memory.xml
@@ -0,0 +1,30 @@
+
+ QEMUGuest
+ 9f4b6512-e73a-4a25-93e8-5307802821ce
+ 65536
+ 65536
+ 2
+
+
+
+
+ hvm
+
+
+
+
+ |
+ |
+
+
+
+ destroy
+ restart
+ destroy
+
+ /usr/bin/kvm
+
+
+
+
+
diff --git a/tests/qemuxml2argvdata/qemuxml2argv-numatune-memnode-nocpu.xml b/tests/qemuxml2argvdata/qemuxml2argv-numatune-memnode-nocpu.xml
new file mode 100644
index 0000000000..7b0e2483a5
--- /dev/null
+++ b/tests/qemuxml2argvdata/qemuxml2argv-numatune-memnode-nocpu.xml
@@ -0,0 +1,25 @@
+
+ QEMUGuest
+ 9f4b6512-e73a-4a25-93e8-5307802821ce
+ 65536
+ 65536
+ 1
+
+
+
+
+
+ hvm
+
+
+
+ destroy
+ restart
+ destroy
+
+ /usr/bin/kvm
+
+
+
+
+
diff --git a/tests/qemuxml2argvdata/qemuxml2argv-numatune-memnode.xml b/tests/qemuxml2argvdata/qemuxml2argv-numatune-memnode.xml
new file mode 100644
index 0000000000..49b328cb18
--- /dev/null
+++ b/tests/qemuxml2argvdata/qemuxml2argv-numatune-memnode.xml
@@ -0,0 +1,33 @@
+
+ QEMUGuest
+ 9f4b6512-e73a-4a25-93e8-5307802821ce
+ 24682468
+ 24682468
+ 32
+
+
+
+
+
+
+ hvm
+
+
+
+
+ |
+ |
+ |
+
+
+
+ destroy
+ restart
+ destroy
+
+ /usr/bin/kvm
+
+
+
+
+
diff --git a/tests/qemuxml2argvdata/qemuxml2argv-numatune-memnodes-problematic.xml b/tests/qemuxml2argvdata/qemuxml2argv-numatune-memnodes-problematic.xml
new file mode 100644
index 0000000000..bb4e4af043
--- /dev/null
+++ b/tests/qemuxml2argvdata/qemuxml2argv-numatune-memnodes-problematic.xml
@@ -0,0 +1,31 @@
+
+ QEMUGuest
+ 9f4b6512-e73a-4a25-93e8-5307802821ce
+ 65536
+ 65536
+ 2
+
+
+
+
+
+ hvm
+
+
+
+
+ |
+ |
+
+
+
+ destroy
+ restart
+ destroy
+
+ /usr/bin/kvm
+
+
+
+
+
diff --git a/tests/qemuxml2argvtest.c b/tests/qemuxml2argvtest.c
index 8eb982f2c6..3a76aa6bee 100644
--- a/tests/qemuxml2argvtest.c
+++ b/tests/qemuxml2argvtest.c
@@ -1197,6 +1197,8 @@ mymain(void)
DO_TEST("cputune-zero-shares", QEMU_CAPS_NAME);
DO_TEST("numatune-memory", NONE);
DO_TEST("numatune-auto-nodeset-invalid", NONE);
+ DO_TEST_PARSE_ERROR("numatune-memnode-nocpu", NONE);
+ DO_TEST_PARSE_ERROR("numatune-memnodes-problematic", NONE);
DO_TEST("numad", NONE);
DO_TEST("numad-auto-vcpu-static-numatune", NONE);
DO_TEST("numad-auto-memory-vcpu-cpuset", NONE);
diff --git a/tests/qemuxml2xmloutdata/qemuxml2xmlout-numatune-memnode.xml b/tests/qemuxml2xmloutdata/qemuxml2xmlout-numatune-memnode.xml
new file mode 100644
index 0000000000..82b5f61870
--- /dev/null
+++ b/tests/qemuxml2xmloutdata/qemuxml2xmlout-numatune-memnode.xml
@@ -0,0 +1,33 @@
+
+ QEMUGuest
+ 9f4b6512-e73a-4a25-93e8-5307802821ce
+ 24682468
+ 24682468
+ 32
+
+
+
+
+
+
+ hvm
+
+
+
+
+ |
+ |
+ |
+
+
+
+ destroy
+ restart
+ destroy
+
+ /usr/bin/kvm
+
+
+
+
+
diff --git a/tests/qemuxml2xmltest.c b/tests/qemuxml2xmltest.c
index 8b1b11ad26..cbeabad307 100644
--- a/tests/qemuxml2xmltest.c
+++ b/tests/qemuxml2xmltest.c
@@ -374,6 +374,8 @@ mymain(void)
DO_TEST_DIFFERENT("cpu-numa2");
DO_TEST_DIFFERENT("numatune-auto-prefer");
+ DO_TEST_DIFFERENT("numatune-memnode");
+ DO_TEST("numatune-memnode-no-memory");
virObjectUnref(driver.caps);
virObjectUnref(driver.xmlopt);