diff --git a/docs/formatdomain.html.in b/docs/formatdomain.html.in
index 34664f7903..884c58b9c3 100644
--- a/docs/formatdomain.html.in
+++ b/docs/formatdomain.html.in
@@ -759,6 +759,10 @@
<cache id='0' level='3' type='both' size='3' unit='MiB'/>
<cache id='1' level='3' type='both' size='3' unit='MiB'/>
</cachetune>
+ <memorytune vcpus='0-3'>
+ <node id='0' bandwidth='60'/>
+ </memorytune>
+
</cputune>
...
</domain>
@@ -932,7 +936,9 @@
size and required granularity are reported as well. The required
attribute vcpus
specifies to which vCPUs this allocation
applies. A vCPU can only be member of one cachetune
element
- allocations. Supported subelements are:
+ allocation. The vCPUs specified by cachetune can be identical with those
+ in memorytune, however they are not allowed to overlap.
+ Supported subelements are:
cache
@@ -972,7 +978,38 @@
+
+ memorytune
Since 4.7.0
+
+ Optional memorytune
element can control allocations for
+ memory bandwidth using the resctrl on the host. Whether or not is this
+ supported can be gathered from capabilities where some limitations like
+ minimum bandwidth and required granularity are reported as well. The
+ required attribute vcpus
specifies to which vCPUs this
+ allocation applies. A vCPU can only be member of one
+ memorytune
element allocation. The vcpus
specified
+ by memorytune
can be identical to those specified by
+ cachetune
. However they are not allowed to overlap each other.
+ Supported subelements are:
+
+ node
+
+ This element controls the allocation of CPU memory bandwidth and has the
+ following attributes:
+
+ id
+
+ Host node id from which to allocate memory bandwidth.
+
+ bandwidth
+
+ The memory bandwidth to allocate from this node. The value by default
+ is in percentage.
+
+
+
+
diff --git a/docs/schemas/domaincommon.rng b/docs/schemas/domaincommon.rng
index 1a786968cc..749556c013 100644
--- a/docs/schemas/domaincommon.rng
+++ b/docs/schemas/domaincommon.rng
@@ -983,6 +983,23 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/src/conf/domain_conf.c b/src/conf/domain_conf.c
index db9fa446dd..24c0ec2798 100644
--- a/src/conf/domain_conf.c
+++ b/src/conf/domain_conf.c
@@ -19418,6 +19418,129 @@ virDomainDefParseCaps(virDomainDefPtr def,
}
+static int
+virDomainMemorytuneDefParseMemory(xmlXPathContextPtr ctxt,
+ xmlNodePtr node,
+ virResctrlAllocPtr alloc)
+{
+ xmlNodePtr oldnode = ctxt->node;
+ unsigned int id;
+ unsigned int bandwidth;
+ char *tmp = NULL;
+ int ret = -1;
+
+ ctxt->node = node;
+
+ tmp = virXMLPropString(node, "id");
+ if (!tmp) {
+ virReportError(VIR_ERR_XML_ERROR, "%s",
+ _("Missing memorytune attribute 'id'"));
+ goto cleanup;
+ }
+ if (virStrToLong_uip(tmp, NULL, 10, &id) < 0) {
+ virReportError(VIR_ERR_XML_ERROR,
+ _("Invalid memorytune attribute 'id' value '%s'"),
+ tmp);
+ goto cleanup;
+ }
+ VIR_FREE(tmp);
+
+ tmp = virXMLPropString(node, "bandwidth");
+ if (!tmp) {
+ virReportError(VIR_ERR_XML_ERROR, "%s",
+ _("Missing memorytune attribute 'bandwidth'"));
+ goto cleanup;
+ }
+ if (virStrToLong_uip(tmp, NULL, 10, &bandwidth) < 0) {
+ virReportError(VIR_ERR_XML_ERROR,
+ _("Invalid memorytune attribute 'bandwidth' value '%s'"),
+ tmp);
+ goto cleanup;
+ }
+ VIR_FREE(tmp);
+ if (virResctrlAllocSetMemoryBandwidth(alloc, id, bandwidth) < 0)
+ goto cleanup;
+
+ ret = 0;
+ cleanup:
+ ctxt->node = oldnode;
+ VIR_FREE(tmp);
+ return ret;
+}
+
+
+static int
+virDomainMemorytuneDefParse(virDomainDefPtr def,
+ xmlXPathContextPtr ctxt,
+ xmlNodePtr node,
+ unsigned int flags)
+{
+ xmlNodePtr oldnode = ctxt->node;
+ xmlNodePtr *nodes = NULL;
+ virBitmapPtr vcpus = NULL;
+ virResctrlAllocPtr alloc = NULL;
+ ssize_t i = 0;
+ int n;
+ int ret = -1;
+ bool new_alloc = false;
+
+ ctxt->node = node;
+
+ if (virDomainResctrlParseVcpus(def, node, &vcpus) < 0)
+ goto cleanup;
+
+ if (virBitmapIsAllClear(vcpus)) {
+ ret = 0;
+ goto cleanup;
+ }
+
+ if ((n = virXPathNodeSet("./node", ctxt, &nodes)) < 0) {
+ virReportError(VIR_ERR_INTERNAL_ERROR, "%s",
+ _("Cannot extract memory nodes under memorytune"));
+ goto cleanup;
+ }
+
+ if (virDomainResctrlVcpuMatch(def, vcpus, &alloc) < 0)
+ goto cleanup;
+
+ if (!alloc) {
+ alloc = virResctrlAllocNew();
+ if (!alloc)
+ goto cleanup;
+ new_alloc = true;
+ } else {
+ alloc = virObjectRef(alloc);
+ }
+
+ for (i = 0; i < n; i++) {
+ if (virDomainMemorytuneDefParseMemory(ctxt, nodes[i], alloc) < 0)
+ goto cleanup;
+ }
+ if (virResctrlAllocIsEmpty(alloc)) {
+ ret = 0;
+ goto cleanup;
+ }
+ /*
+ * If this is a new allocation, format ID and append to resctrl, otherwise
+ * just update the existing alloc information, which is done in above
+ * virDomainMemorytuneDefParseMemory */
+ if (new_alloc) {
+ if (virDomainResctrlAppend(def, node, alloc, vcpus, flags) < 0)
+ goto cleanup;
+ vcpus = NULL;
+ alloc = NULL;
+ }
+
+ ret = 0;
+ cleanup:
+ ctxt->node = oldnode;
+ virObjectUnref(alloc);
+ virBitmapFree(vcpus);
+ VIR_FREE(nodes);
+ return ret;
+}
+
+
static virDomainDefPtr
virDomainDefParseXML(xmlDocPtr xml,
xmlNodePtr root,
@@ -19897,6 +20020,18 @@ virDomainDefParseXML(xmlDocPtr xml,
}
VIR_FREE(nodes);
+ if ((n = virXPathNodeSet("./cputune/memorytune", ctxt, &nodes)) < 0) {
+ virReportError(VIR_ERR_INTERNAL_ERROR, "%s",
+ _("cannot extract memorytune nodes"));
+ goto error;
+ }
+
+ for (i = 0; i < n; i++) {
+ if (virDomainMemorytuneDefParse(def, ctxt, nodes[i], flags) < 0)
+ goto error;
+ }
+ VIR_FREE(nodes);
+
if (virCPUDefParseXML(ctxt, "./cpu[1]", VIR_CPU_TYPE_GUEST, &def->cpu) < 0)
goto error;
@@ -27198,6 +27333,68 @@ virDomainCachetuneDefFormat(virBufferPtr buf,
}
+static int
+virDomainMemorytuneDefFormatHelper(unsigned int id,
+ unsigned int bandwidth,
+ void *opaque)
+{
+ virBufferPtr buf = opaque;
+
+ virBufferAsprintf(buf,
+ " \n",
+ id, bandwidth);
+ return 0;
+}
+
+
+static int
+virDomainMemorytuneDefFormat(virBufferPtr buf,
+ virDomainResctrlDefPtr resctrl,
+ unsigned int flags)
+{
+ virBuffer childrenBuf = VIR_BUFFER_INITIALIZER;
+ char *vcpus = NULL;
+ int ret = -1;
+
+ virBufferSetChildIndent(&childrenBuf, buf);
+ if (virResctrlAllocForeachMemory(resctrl->alloc,
+ virDomainMemorytuneDefFormatHelper,
+ &childrenBuf) < 0)
+ goto cleanup;
+
+ if (virBufferCheckError(&childrenBuf) < 0)
+ goto cleanup;
+
+ if (!virBufferUse(&childrenBuf)) {
+ ret = 0;
+ goto cleanup;
+ }
+
+ vcpus = virBitmapFormat(resctrl->vcpus);
+ if (!vcpus)
+ goto cleanup;
+
+ virBufferAsprintf(buf, "alloc);
+ if (!alloc_id)
+ goto cleanup;
+
+ virBufferAsprintf(buf, " id='%s'", alloc_id);
+ }
+ virBufferAddLit(buf, ">\n");
+
+ virBufferAddBuffer(buf, &childrenBuf);
+ virBufferAddLit(buf, " \n");
+
+ ret = 0;
+ cleanup:
+ virBufferFreeAndReset(&childrenBuf);
+ VIR_FREE(vcpus);
+ return ret;
+}
+
static int
virDomainCputuneDefFormat(virBufferPtr buf,
virDomainDefPtr def,
@@ -27303,6 +27500,9 @@ virDomainCputuneDefFormat(virBufferPtr buf,
for (i = 0; i < def->nresctrls; i++)
virDomainCachetuneDefFormat(&childrenBuf, def->resctrls[i], flags);
+ for (i = 0; i < def->nresctrls; i++)
+ virDomainMemorytuneDefFormat(&childrenBuf, def->resctrls[i], flags);
+
if (virBufferCheckError(&childrenBuf) < 0)
return -1;
diff --git a/tests/genericxml2xmlindata/memorytune-colliding-allocs.xml b/tests/genericxml2xmlindata/memorytune-colliding-allocs.xml
new file mode 100644
index 0000000000..9b8ebaa084
--- /dev/null
+++ b/tests/genericxml2xmlindata/memorytune-colliding-allocs.xml
@@ -0,0 +1,30 @@
+
+ QEMUGuest1
+ c7a5fdbd-edaf-9455-926a-d65c16db1809
+ 219136
+ 219136
+ 4
+
+
+
+
+
+
+
+ hvm
+
+
+
+ destroy
+ restart
+ destroy
+
+ /usr/bin/qemu-system-i686
+
+
+
+
+
+
+
+
diff --git a/tests/genericxml2xmlindata/memorytune-colliding-cachetune.xml b/tests/genericxml2xmlindata/memorytune-colliding-cachetune.xml
new file mode 100644
index 0000000000..5416870de2
--- /dev/null
+++ b/tests/genericxml2xmlindata/memorytune-colliding-cachetune.xml
@@ -0,0 +1,32 @@
+
+ QEMUGuest1
+ c7a5fdbd-edaf-9455-926a-d65c16db1809
+ 219136
+ 219136
+ 4
+
+
+
+
+
+
+
+
+
+ hvm
+
+
+
+ destroy
+ restart
+ destroy
+
+ /usr/bin/qemu-system-i686
+
+
+
+
+
+
+
+
diff --git a/tests/genericxml2xmlindata/memorytune.xml b/tests/genericxml2xmlindata/memorytune.xml
new file mode 100644
index 0000000000..ea03e22fc2
--- /dev/null
+++ b/tests/genericxml2xmlindata/memorytune.xml
@@ -0,0 +1,33 @@
+
+ QEMUGuest1
+ c7a5fdbd-edaf-9455-926a-d65c16db1809
+ 219136
+ 219136
+ 4
+
+
+
+
+
+
+
+
+
+
+ hvm
+
+
+
+ destroy
+ restart
+ destroy
+
+ /usr/bin/qemu-system-i686
+
+
+
+
+
+
+
+
diff --git a/tests/genericxml2xmltest.c b/tests/genericxml2xmltest.c
index 7a4fc1eb7a..e6d4ef2a7f 100644
--- a/tests/genericxml2xmltest.c
+++ b/tests/genericxml2xmltest.c
@@ -140,6 +140,11 @@ mymain(void)
TEST_COMPARE_DOM_XML2XML_RESULT_FAIL_PARSE);
DO_TEST_FULL("cachetune-colliding-types", false, true,
TEST_COMPARE_DOM_XML2XML_RESULT_FAIL_PARSE);
+ DO_TEST("memorytune");
+ DO_TEST_FULL("memorytune-colliding-allocs", false, true,
+ TEST_COMPARE_DOM_XML2XML_RESULT_FAIL_PARSE);
+ DO_TEST_FULL("memorytune-colliding-cachetune", false, true,
+ TEST_COMPARE_DOM_XML2XML_RESULT_FAIL_PARSE);
DO_TEST("tseg");