c1c534
From ba1ce05bd6e49217cd8c89b75e741e518d2b9292 Mon Sep 17 00:00:00 2001
c1c534
Message-Id: <ba1ce05bd6e49217cd8c89b75e741e518d2b9292@dist-git>
c1c534
From: Martin Kletzander <mkletzan@redhat.com>
c1c534
Date: Wed, 31 Jan 2018 16:32:30 +0100
c1c534
Subject: [PATCH] conf: Add support for cputune/cachetune
c1c534
c1c534
More info in the documentation, this is basically the XML parsing/formatting
c1c534
support, schemas, tests and documentation for the new cputune/cachetune element
c1c534
that will get used by following patches.
c1c534
c1c534
Signed-off-by: Martin Kletzander <mkletzan@redhat.com>
c1c534
(cherry picked from commit 7387e3fea44e28118e1f72841d79503849e15985)
c1c534
Signed-off-by: Martin Kletzander <mkletzan@redhat.com>
c1c534
c1c534
https://bugzilla.redhat.com/show_bug.cgi?id=1289368
c1c534
c1c534
Downstream changes:
c1c534
c1c534
- Rename test XML files due to ab7a2fe230bb742cc4bc7f8d1475201b97fe49ab
c1c534
c1c534
Signed-off-by: Martin Kletzander <mkletzan@redhat.com>
c1c534
---
c1c534
 docs/formatdomain.html.in                          |  54 ++++
c1c534
 docs/schemas/domaincommon.rng                      |  32 +++
c1c534
 src/conf/domain_conf.c                             | 295 ++++++++++++++++++++-
c1c534
 src/conf/domain_conf.h                             |  13 +
c1c534
 .../genericxml2xmlindata/generic-cachetune-cdp.xml |  36 +++
c1c534
 .../generic-cachetune-colliding-allocs.xml         |  30 +++
c1c534
 .../generic-cachetune-colliding-tunes.xml          |  32 +++
c1c534
 .../generic-cachetune-colliding-types.xml          |  30 +++
c1c534
 .../generic-cachetune-small.xml                    |  29 ++
c1c534
 tests/genericxml2xmlindata/generic-cachetune.xml   |  33 +++
c1c534
 tests/genericxml2xmltest.c                         |  10 +
c1c534
 11 files changed, 592 insertions(+), 2 deletions(-)
c1c534
 create mode 100644 tests/genericxml2xmlindata/generic-cachetune-cdp.xml
c1c534
 create mode 100644 tests/genericxml2xmlindata/generic-cachetune-colliding-allocs.xml
c1c534
 create mode 100644 tests/genericxml2xmlindata/generic-cachetune-colliding-tunes.xml
c1c534
 create mode 100644 tests/genericxml2xmlindata/generic-cachetune-colliding-types.xml
c1c534
 create mode 100644 tests/genericxml2xmlindata/generic-cachetune-small.xml
c1c534
 create mode 100644 tests/genericxml2xmlindata/generic-cachetune.xml
c1c534
c1c534
diff --git a/docs/formatdomain.html.in b/docs/formatdomain.html.in
c1c534
index 0cfbf051b7..f33261d05c 100644
c1c534
--- a/docs/formatdomain.html.in
c1c534
+++ b/docs/formatdomain.html.in
c1c534
@@ -689,6 +689,10 @@
c1c534
     <iothread_quota>-1</iothread_quota>
c1c534
     <vcpusched vcpus='0-4,^3' scheduler='fifo' priority='1'/>
c1c534
     <iothreadsched iothreads='2' scheduler='batch'/>
c1c534
+    <cachetune vcpus='0-3'>
c1c534
+      <cache id='0' level='3' type='both' size='3' unit='MiB'/>
c1c534
+      <cache id='1' level='3' type='both' size='3' unit='MiB'/>
c1c534
+    </cachetune>
c1c534
   </cputune>
c1c534
   ...
c1c534
 </domain>
c1c534
@@ -834,6 +838,56 @@
c1c534
         Since 1.2.13
c1c534
       
c1c534
 
c1c534
+      
cachetuneSince 4.1.0
c1c534
+      
c1c534
+        Optional cachetune element can control allocations for CPU
c1c534
+        caches using the resctrl on the host. Whether or not is this supported
c1c534
+        can be gathered from capabilities where some limitations like minimum
c1c534
+        size and required granularity are reported as well. The required
c1c534
+        attribute vcpus specifies to which vCPUs this allocation
c1c534
+        applies. A vCPU can only be member of one cachetune element
c1c534
+        allocations. Supported subelements are:
c1c534
+        
c1c534
+          
cache
c1c534
+          
c1c534
+            This element controls the allocation of CPU cache and has the
c1c534
+            following attributes:
c1c534
+            
c1c534
+              
level
c1c534
+              
c1c534
+                Host cache level from which to allocate.
c1c534
+              
c1c534
+              
id
c1c534
+              
c1c534
+                Host cache id from which to allocate.
c1c534
+              
c1c534
+              
type
c1c534
+              
c1c534
+                Type of allocation. Can be code for code
c1c534
+                (instructions), data for data or both
c1c534
+                for both code and data (unified). Currently the allocation can
c1c534
+                be done only with the same type as the host supports, meaning
c1c534
+                you cannot request both for host with CDP
c1c534
+                (code/data prioritization) enabled.
c1c534
+              
c1c534
+              
size
c1c534
+              
c1c534
+                The size of the region to allocate. The value by default is in
c1c534
+                bytes, but the unit attribute can be used to scale
c1c534
+                the value.
c1c534
+              
c1c534
+              
unit (optional)
c1c534
+              
c1c534
+                If specified it is the unit such as KiB, MiB, GiB, or TiB
c1c534
+                (described in the memory element
c1c534
+                for Memory Allocation)
c1c534
+                in which size is specified, defaults to bytes.
c1c534
+              
c1c534
+            
c1c534
+          
c1c534
+        
c1c534
+
c1c534
+      
c1c534
     
c1c534
 
c1c534
 
c1c534
diff --git a/docs/schemas/domaincommon.rng b/docs/schemas/domaincommon.rng
c1c534
index 05709afc0c..564674b659 100644
c1c534
--- a/docs/schemas/domaincommon.rng
c1c534
+++ b/docs/schemas/domaincommon.rng
c1c534
@@ -900,6 +900,38 @@
c1c534
             <ref name="schedparam"/>
c1c534
           </element>
c1c534
         </zeroOrMore>
c1c534
+        <zeroOrMore>
c1c534
+          <element name="cachetune">
c1c534
+            <attribute name="vcpus">
c1c534
+              <ref name='cpuset'/>
c1c534
+            </attribute>
c1c534
+            <oneOrMore>
c1c534
+              <element name="cache">
c1c534
+                <attribute name="id">
c1c534
+                  <ref name='unsignedInt'/>
c1c534
+                </attribute>
c1c534
+                <attribute name="level">
c1c534
+                  <ref name='unsignedInt'/>
c1c534
+                </attribute>
c1c534
+                <attribute name="type">
c1c534
+                  <choice>
c1c534
+                    <value>both</value>
c1c534
+                    <value>code</value>
c1c534
+                    <value>data</value>
c1c534
+                  </choice>
c1c534
+                </attribute>
c1c534
+                <attribute name="size">
c1c534
+                  <ref name='unsignedLong'/>
c1c534
+                </attribute>
c1c534
+                <optional>
c1c534
+                  <attribute name='unit'>
c1c534
+                    <ref name='unit'/>
c1c534
+                  </attribute>
c1c534
+                </optional>
c1c534
+              </element>
c1c534
+            </oneOrMore>
c1c534
+          </element>
c1c534
+        </zeroOrMore>
c1c534
       </interleave>
c1c534
     </element>
c1c534
   </define>
c1c534
diff --git a/src/conf/domain_conf.c b/src/conf/domain_conf.c
c1c534
index ad9c16a1e0..181b035647 100644
c1c534
--- a/src/conf/domain_conf.c
c1c534
+++ b/src/conf/domain_conf.c
c1c534
@@ -2886,6 +2886,19 @@ virDomainLoaderDefFree(virDomainLoaderDefPtr loader)
c1c534
     VIR_FREE(loader);
c1c534
 }
c1c534
 
c1c534
+
c1c534
+static void
c1c534
+virDomainCachetuneDefFree(virDomainCachetuneDefPtr cachetune)
c1c534
+{
c1c534
+    if (!cachetune)
c1c534
+        return;
c1c534
+
c1c534
+    virObjectUnref(cachetune->alloc);
c1c534
+    virBitmapFree(cachetune->vcpus);
c1c534
+    VIR_FREE(cachetune);
c1c534
+}
c1c534
+
c1c534
+
c1c534
 void virDomainDefFree(virDomainDefPtr def)
c1c534
 {
c1c534
     size_t i;
c1c534
@@ -3058,6 +3071,10 @@ void virDomainDefFree(virDomainDefPtr def)
c1c534
         virDomainShmemDefFree(def->shmems[i]);
c1c534
     VIR_FREE(def->shmems);
c1c534
 
c1c534
+    for (i = 0; i < def->ncachetunes; i++)
c1c534
+        virDomainCachetuneDefFree(def->cachetunes[i]);
c1c534
+    VIR_FREE(def->cachetunes);
c1c534
+
c1c534
     VIR_FREE(def->keywrap);
c1c534
 
c1c534
     if (def->namespaceData && def->ns.free)
c1c534
@@ -18233,6 +18250,194 @@ virDomainDefParseBootOptions(virDomainDefPtr def,
c1c534
 }
c1c534
 
c1c534
 
c1c534
+static int
c1c534
+virDomainCachetuneDefParseCache(xmlXPathContextPtr ctxt,
c1c534
+                                xmlNodePtr node,
c1c534
+                                virResctrlAllocPtr alloc)
c1c534
+{
c1c534
+    xmlNodePtr oldnode = ctxt->node;
c1c534
+    unsigned int level;
c1c534
+    unsigned int cache;
c1c534
+    int type;
c1c534
+    unsigned long long size;
c1c534
+    char *tmp = NULL;
c1c534
+    int ret = -1;
c1c534
+
c1c534
+    ctxt->node = node;
c1c534
+
c1c534
+    tmp = virXMLPropString(node, "id");
c1c534
+    if (!tmp) {
c1c534
+        virReportError(VIR_ERR_XML_ERROR, "%s",
c1c534
+                       _("Missing cachetune attribute 'id'"));
c1c534
+        goto cleanup;
c1c534
+    }
c1c534
+    if (virStrToLong_uip(tmp, NULL, 10, &cache) < 0) {
c1c534
+        virReportError(VIR_ERR_XML_ERROR,
c1c534
+                       _("Invalid cachetune attribute 'id' value '%s'"),
c1c534
+                       tmp);
c1c534
+        goto cleanup;
c1c534
+    }
c1c534
+    VIR_FREE(tmp);
c1c534
+
c1c534
+    tmp = virXMLPropString(node, "level");
c1c534
+    if (!tmp) {
c1c534
+        virReportError(VIR_ERR_XML_ERROR, "%s",
c1c534
+                       _("Missing cachetune attribute 'level'"));
c1c534
+        goto cleanup;
c1c534
+    }
c1c534
+    if (virStrToLong_uip(tmp, NULL, 10, &level) < 0) {
c1c534
+        virReportError(VIR_ERR_XML_ERROR,
c1c534
+                       _("Invalid cachetune attribute 'level' value '%s'"),
c1c534
+                       tmp);
c1c534
+        goto cleanup;
c1c534
+    }
c1c534
+    VIR_FREE(tmp);
c1c534
+
c1c534
+    tmp = virXMLPropString(node, "type");
c1c534
+    if (!tmp) {
c1c534
+        virReportError(VIR_ERR_XML_ERROR, "%s",
c1c534
+                       _("Missing cachetune attribute 'type'"));
c1c534
+        goto cleanup;
c1c534
+    }
c1c534
+    type = virCacheTypeFromString(tmp);
c1c534
+    if (type < 0) {
c1c534
+        virReportError(VIR_ERR_XML_ERROR,
c1c534
+                       _("Invalid cachetune attribute 'type' value '%s'"),
c1c534
+                       tmp);
c1c534
+        goto cleanup;
c1c534
+    }
c1c534
+    VIR_FREE(tmp);
c1c534
+
c1c534
+    if (virDomainParseScaledValue("./@size", "./@unit",
c1c534
+                                  ctxt, &size, 1024,
c1c534
+                                  ULLONG_MAX, true) < 0)
c1c534
+        goto cleanup;
c1c534
+
c1c534
+    if (virResctrlAllocSetSize(alloc, level, type, cache, size) < 0)
c1c534
+        goto cleanup;
c1c534
+
c1c534
+    ret = 0;
c1c534
+ cleanup:
c1c534
+    ctxt->node = oldnode;
c1c534
+    VIR_FREE(tmp);
c1c534
+    return ret;
c1c534
+}
c1c534
+
c1c534
+
c1c534
+static int
c1c534
+virDomainCachetuneDefParse(virDomainDefPtr def,
c1c534
+                           xmlXPathContextPtr ctxt,
c1c534
+                           xmlNodePtr node,
c1c534
+                           unsigned int flags)
c1c534
+{
c1c534
+    xmlNodePtr oldnode = ctxt->node;
c1c534
+    xmlNodePtr *nodes = NULL;
c1c534
+    virBitmapPtr vcpus = NULL;
c1c534
+    virResctrlAllocPtr alloc = virResctrlAllocNew();
c1c534
+    virDomainCachetuneDefPtr tmp_cachetune = NULL;
c1c534
+    char *tmp = NULL;
c1c534
+    char *vcpus_str = NULL;
c1c534
+    char *alloc_id = NULL;
c1c534
+    ssize_t i = 0;
c1c534
+    int n;
c1c534
+    int ret = -1;
c1c534
+
c1c534
+    ctxt->node = node;
c1c534
+
c1c534
+    if (!alloc)
c1c534
+        goto cleanup;
c1c534
+
c1c534
+    if (VIR_ALLOC(tmp_cachetune) < 0)
c1c534
+        goto cleanup;
c1c534
+
c1c534
+    vcpus_str = virXMLPropString(node, "vcpus");
c1c534
+    if (!vcpus_str) {
c1c534
+        virReportError(VIR_ERR_XML_ERROR, "%s",
c1c534
+                       _("Missing cachetune attribute 'vcpus'"));
c1c534
+        goto cleanup;
c1c534
+    }
c1c534
+    if (virBitmapParse(vcpus_str, &vcpus, VIR_DOMAIN_CPUMASK_LEN) < 0) {
c1c534
+        virReportError(VIR_ERR_XML_ERROR,
c1c534
+                       _("Invalid cachetune attribute 'vcpus' value '%s'"),
c1c534
+                       vcpus_str);
c1c534
+        goto cleanup;
c1c534
+    }
c1c534
+
c1c534
+    /* We need to limit the bitmap to number of vCPUs.  If there's nothing left,
c1c534
+     * then we can just clean up and return 0 immediately */
c1c534
+    virBitmapShrink(vcpus, def->maxvcpus);
c1c534
+    if (virBitmapIsAllClear(vcpus)) {
c1c534
+        ret = 0;
c1c534
+        goto cleanup;
c1c534
+    }
c1c534
+
c1c534
+    if ((n = virXPathNodeSet("./cache", ctxt, &nodes)) < 0) {
c1c534
+        virReportError(VIR_ERR_INTERNAL_ERROR, "%s",
c1c534
+                       _("Cannot extract cache nodes under cachetune"));
c1c534
+        goto cleanup;
c1c534
+    }
c1c534
+
c1c534
+    for (i = 0; i < n; i++) {
c1c534
+        if (virDomainCachetuneDefParseCache(ctxt, nodes[i], alloc) < 0)
c1c534
+            goto cleanup;
c1c534
+    }
c1c534
+
c1c534
+    if (virResctrlAllocIsEmpty(alloc)) {
c1c534
+        ret = 0;
c1c534
+        goto cleanup;
c1c534
+    }
c1c534
+
c1c534
+    for (i = 0; i < def->ncachetunes; i++) {
c1c534
+        if (virBitmapOverlaps(def->cachetunes[i]->vcpus, vcpus)) {
c1c534
+            virReportError(VIR_ERR_XML_ERROR, "%s",
c1c534
+                           _("Overlapping vcpus in cachetunes"));
c1c534
+            goto cleanup;
c1c534
+        }
c1c534
+    }
c1c534
+
c1c534
+    /* We need to format it back because we need to be consistent in the naming
c1c534
+     * even when users specify some "sub-optimal" string there. */
c1c534
+    VIR_FREE(vcpus_str);
c1c534
+    vcpus_str = virBitmapFormat(vcpus);
c1c534
+    if (!vcpus_str)
c1c534
+        goto cleanup;
c1c534
+
c1c534
+    if (!(flags & VIR_DOMAIN_DEF_PARSE_INACTIVE))
c1c534
+        alloc_id = virXMLPropString(node, "id");
c1c534
+
c1c534
+    if (!alloc_id) {
c1c534
+        /* The number of allocations is limited and the directory structure is flat,
c1c534
+         * not hierarchical, so we need to have all same allocations in one
c1c534
+         * directory, so it's nice to have it named appropriately.  For now it's
c1c534
+         * 'vcpus_...' but it's designed in order for it to be changeable in the
c1c534
+         * future (it's part of the status XML). */
c1c534
+        if (virAsprintf(&alloc_id, "vcpus_%s", vcpus_str) < 0)
c1c534
+            goto cleanup;
c1c534
+    }
c1c534
+
c1c534
+    if (virResctrlAllocSetID(alloc, alloc_id) < 0)
c1c534
+        goto cleanup;
c1c534
+
c1c534
+    VIR_STEAL_PTR(tmp_cachetune->vcpus, vcpus);
c1c534
+    VIR_STEAL_PTR(tmp_cachetune->alloc, alloc);
c1c534
+
c1c534
+    if (VIR_APPEND_ELEMENT(def->cachetunes, def->ncachetunes, tmp_cachetune) < 0)
c1c534
+        goto cleanup;
c1c534
+
c1c534
+    ret = 0;
c1c534
+ cleanup:
c1c534
+    ctxt->node = oldnode;
c1c534
+    virDomainCachetuneDefFree(tmp_cachetune);
c1c534
+    virObjectUnref(alloc);
c1c534
+    virBitmapFree(vcpus);
c1c534
+    VIR_FREE(alloc_id);
c1c534
+    VIR_FREE(vcpus_str);
c1c534
+    VIR_FREE(nodes);
c1c534
+    VIR_FREE(tmp);
c1c534
+    return ret;
c1c534
+}
c1c534
+
c1c534
+
c1c534
 static virDomainDefPtr
c1c534
 virDomainDefParseXML(xmlDocPtr xml,
c1c534
                      xmlNodePtr root,
c1c534
@@ -18785,6 +18990,18 @@ virDomainDefParseXML(xmlDocPtr xml,
c1c534
     }
c1c534
     VIR_FREE(nodes);
c1c534
 
c1c534
+    if ((n = virXPathNodeSet("./cputune/cachetune", ctxt, &nodes)) < 0) {
c1c534
+        virReportError(VIR_ERR_INTERNAL_ERROR, "%s",
c1c534
+                       _("cannot extract cachetune nodes"));
c1c534
+        goto error;
c1c534
+    }
c1c534
+
c1c534
+    for (i = 0; i < n; i++) {
c1c534
+        if (virDomainCachetuneDefParse(def, ctxt, nodes[i], flags) < 0)
c1c534
+            goto error;
c1c534
+    }
c1c534
+    VIR_FREE(nodes);
c1c534
+
c1c534
     if (virCPUDefParseXML(ctxt, "./cpu[1]", VIR_CPU_TYPE_GUEST, &def->cpu) < 0)
c1c534
         goto error;
c1c534
 
c1c534
@@ -25736,9 +25953,80 @@ virDomainSchedulerFormat(virBufferPtr buf,
c1c534
 }
c1c534
 
c1c534
 
c1c534
+static int
c1c534
+virDomainCachetuneDefFormatHelper(unsigned int level,
c1c534
+                                  virCacheType type,
c1c534
+                                  unsigned int cache,
c1c534
+                                  unsigned long long size,
c1c534
+                                  void *opaque)
c1c534
+{
c1c534
+    const char *unit;
c1c534
+    virBufferPtr buf = opaque;
c1c534
+    unsigned long long short_size = virFormatIntPretty(size, &unit);
c1c534
+
c1c534
+    virBufferAsprintf(buf,
c1c534
+                      "
c1c534
+                      "size='%llu' unit='%s'/>\n",
c1c534
+                      cache, level, virCacheTypeToString(type),
c1c534
+                      short_size, unit);
c1c534
+
c1c534
+    return 0;
c1c534
+}
c1c534
+
c1c534
+
c1c534
+static int
c1c534
+virDomainCachetuneDefFormat(virBufferPtr buf,
c1c534
+                            virDomainCachetuneDefPtr cachetune,
c1c534
+                            unsigned int flags)
c1c534
+{
c1c534
+    virBuffer childrenBuf = VIR_BUFFER_INITIALIZER;
c1c534
+    char *vcpus = NULL;
c1c534
+    int ret = -1;
c1c534
+
c1c534
+    virBufferSetChildIndent(&childrenBuf, buf);
c1c534
+    virResctrlAllocForeachSize(cachetune->alloc,
c1c534
+                               virDomainCachetuneDefFormatHelper,
c1c534
+                               &childrenBuf);
c1c534
+
c1c534
+
c1c534
+    if (virBufferCheckError(&childrenBuf) < 0)
c1c534
+        goto cleanup;
c1c534
+
c1c534
+    if (!virBufferUse(&childrenBuf)) {
c1c534
+        ret = 0;
c1c534
+        goto cleanup;
c1c534
+    }
c1c534
+
c1c534
+    vcpus = virBitmapFormat(cachetune->vcpus);
c1c534
+    if (!vcpus)
c1c534
+        goto cleanup;
c1c534
+
c1c534
+    virBufferAsprintf(buf, "
c1c534
+
c1c534
+    if (!(flags & VIR_DOMAIN_DEF_FORMAT_INACTIVE)) {
c1c534
+        const char *alloc_id = virResctrlAllocGetID(cachetune->alloc);
c1c534
+        if (!alloc_id)
c1c534
+            goto cleanup;
c1c534
+
c1c534
+        virBufferAsprintf(buf, " id='%s'", alloc_id);
c1c534
+    }
c1c534
+    virBufferAddLit(buf, ">\n");
c1c534
+
c1c534
+    virBufferAddBuffer(buf, &childrenBuf);
c1c534
+    virBufferAddLit(buf, "</cachetune>\n");
c1c534
+
c1c534
+    ret = 0;
c1c534
+ cleanup:
c1c534
+    virBufferFreeAndReset(&childrenBuf);
c1c534
+    VIR_FREE(vcpus);
c1c534
+    return ret;
c1c534
+}
c1c534
+
c1c534
+
c1c534
 static int
c1c534
 virDomainCputuneDefFormat(virBufferPtr buf,
c1c534
-                          virDomainDefPtr def)
c1c534
+                          virDomainDefPtr def,
c1c534
+                          unsigned int flags)
c1c534
 {
c1c534
     size_t i;
c1c534
     virBuffer childrenBuf = VIR_BUFFER_INITIALIZER;
c1c534
@@ -25837,6 +26125,9 @@ virDomainCputuneDefFormat(virBufferPtr buf,
c1c534
                                  def->iothreadids[i]->iothread_id);
c1c534
     }
c1c534
 
c1c534
+    for (i = 0; i < def->ncachetunes; i++)
c1c534
+        virDomainCachetuneDefFormat(&childrenBuf, def->cachetunes[i], flags);
c1c534
+
c1c534
     if (virBufferCheckError(&childrenBuf) < 0)
c1c534
         return -1;
c1c534
 
c1c534
@@ -26174,7 +26465,7 @@ virDomainDefFormatInternal(virDomainDefPtr def,
c1c534
         }
c1c534
     }
c1c534
 
c1c534
-    if (virDomainCputuneDefFormat(buf, def) < 0)
c1c534
+    if (virDomainCputuneDefFormat(buf, def, flags) < 0)
c1c534
         goto error;
c1c534
 
c1c534
     if (virDomainNumatuneFormatXML(buf, def->numa) < 0)
c1c534
diff --git a/src/conf/domain_conf.h b/src/conf/domain_conf.h
c1c534
index 5e67c9a9c1..ed7b587bed 100644
c1c534
--- a/src/conf/domain_conf.h
c1c534
+++ b/src/conf/domain_conf.h
c1c534
@@ -56,6 +56,7 @@
c1c534
 # include "virperf.h"
c1c534
 # include "virtypedparam.h"
c1c534
 # include "virsavecookie.h"
c1c534
+# include "virresctrl.h"
c1c534
 
c1c534
 /* forward declarations of all device types, required by
c1c534
  * virDomainDeviceDef
c1c534
@@ -2190,6 +2191,15 @@ struct _virDomainCputune {
c1c534
 };
c1c534
 
c1c534
 
c1c534
+typedef struct _virDomainCachetuneDef virDomainCachetuneDef;
c1c534
+typedef virDomainCachetuneDef *virDomainCachetuneDefPtr;
c1c534
+
c1c534
+struct _virDomainCachetuneDef {
c1c534
+    virBitmapPtr vcpus;
c1c534
+    virResctrlAllocPtr alloc;
c1c534
+};
c1c534
+
c1c534
+
c1c534
 typedef struct _virDomainVcpuDef virDomainVcpuDef;
c1c534
 typedef virDomainVcpuDef *virDomainVcpuDefPtr;
c1c534
 
c1c534
@@ -2318,6 +2328,9 @@ struct _virDomainDef {
c1c534
 
c1c534
     virDomainCputune cputune;
c1c534
 
c1c534
+    virDomainCachetuneDefPtr *cachetunes;
c1c534
+    size_t ncachetunes;
c1c534
+
c1c534
     virDomainNumaPtr numa;
c1c534
     virDomainResourceDefPtr resource;
c1c534
     virDomainIdMapDef idmap;
c1c534
diff --git a/tests/genericxml2xmlindata/generic-cachetune-cdp.xml b/tests/genericxml2xmlindata/generic-cachetune-cdp.xml
c1c534
new file mode 100644
c1c534
index 0000000000..9718f06098
c1c534
--- /dev/null
c1c534
+++ b/tests/genericxml2xmlindata/generic-cachetune-cdp.xml
c1c534
@@ -0,0 +1,36 @@
c1c534
+<domain type='qemu'>
c1c534
+  <name>QEMUGuest1</name>
c1c534
+  <uuid>c7a5fdbd-edaf-9455-926a-d65c16db1809</uuid>
c1c534
+  <memory unit='KiB'>219136</memory>
c1c534
+  <currentMemory unit='KiB'>219136</currentMemory>
c1c534
+  <vcpu placement='static'>4</vcpu>
c1c534
+  <cputune>
c1c534
+    <cachetune vcpus='0-1'>
c1c534
+      <cache id='0' level='3' type='code' size='7680' unit='KiB'/>
c1c534
+      <cache id='1' level='3' type='data' size='3840' unit='KiB'/>
c1c534
+    </cachetune>
c1c534
+    <cachetune vcpus='2'>
c1c534
+      <cache id='1' level='3' type='code' size='6' unit='MiB'/>
c1c534
+    </cachetune>
c1c534
+    <cachetune vcpus='3'>
c1c534
+      <cache id='1' level='3' type='data' size='6912' unit='KiB'/>
c1c534
+    </cachetune>
c1c534
+  </cputune>
c1c534
+  <os>
c1c534
+    <type arch='i686' machine='pc'>hvm</type>
c1c534
+    <boot dev='hd'/>
c1c534
+  </os>
c1c534
+  <clock offset='utc'/>
c1c534
+  <on_poweroff>destroy</on_poweroff>
c1c534
+  <on_reboot>restart</on_reboot>
c1c534
+  <on_crash>destroy</on_crash>
c1c534
+  <devices>
c1c534
+    <emulator>/usr/bin/qemu-system-i686</emulator>
c1c534
+    <controller type='usb' index='0'/>
c1c534
+    <controller type='ide' index='0'/>
c1c534
+    <controller type='pci' index='0' model='pci-root'/>
c1c534
+    <input type='mouse' bus='ps2'/>
c1c534
+    <input type='keyboard' bus='ps2'/>
c1c534
+    <memballoon model='virtio'/>
c1c534
+  </devices>
c1c534
+</domain>
c1c534
diff --git a/tests/genericxml2xmlindata/generic-cachetune-colliding-allocs.xml b/tests/genericxml2xmlindata/generic-cachetune-colliding-allocs.xml
c1c534
new file mode 100644
c1c534
index 0000000000..82c9176cba
c1c534
--- /dev/null
c1c534
+++ b/tests/genericxml2xmlindata/generic-cachetune-colliding-allocs.xml
c1c534
@@ -0,0 +1,30 @@
c1c534
+<domain type='qemu'>
c1c534
+  <name>QEMUGuest1</name>
c1c534
+  <uuid>c7a5fdbd-edaf-9455-926a-d65c16db1809</uuid>
c1c534
+  <memory unit='KiB'>219136</memory>
c1c534
+  <currentMemory unit='KiB'>219136</currentMemory>
c1c534
+  <vcpu placement='static'>4</vcpu>
c1c534
+  <cputune>
c1c534
+    <cachetune vcpus='0'>
c1c534
+      <cache id='0' level='3' type='code' size='12' unit='KiB'/>
c1c534
+      <cache id='0' level='3' type='code' size='18' unit='KiB'/>
c1c534
+    </cachetune>
c1c534
+  </cputune>
c1c534
+  <os>
c1c534
+    <type arch='i686' machine='pc'>hvm</type>
c1c534
+    <boot dev='hd'/>
c1c534
+  </os>
c1c534
+  <clock offset='utc'/>
c1c534
+  <on_poweroff>destroy</on_poweroff>
c1c534
+  <on_reboot>restart</on_reboot>
c1c534
+  <on_crash>destroy</on_crash>
c1c534
+  <devices>
c1c534
+    <emulator>/usr/bin/qemu-system-i686</emulator>
c1c534
+    <controller type='usb' index='0'/>
c1c534
+    <controller type='ide' index='0'/>
c1c534
+    <controller type='pci' index='0' model='pci-root'/>
c1c534
+    <input type='mouse' bus='ps2'/>
c1c534
+    <input type='keyboard' bus='ps2'/>
c1c534
+    <memballoon model='virtio'/>
c1c534
+  </devices>
c1c534
+</domain>
c1c534
diff --git a/tests/genericxml2xmlindata/generic-cachetune-colliding-tunes.xml b/tests/genericxml2xmlindata/generic-cachetune-colliding-tunes.xml
c1c534
new file mode 100644
c1c534
index 0000000000..a0f37028c9
c1c534
--- /dev/null
c1c534
+++ b/tests/genericxml2xmlindata/generic-cachetune-colliding-tunes.xml
c1c534
@@ -0,0 +1,32 @@
c1c534
+<domain type='qemu'>
c1c534
+  <name>QEMUGuest1</name>
c1c534
+  <uuid>c7a5fdbd-edaf-9455-926a-d65c16db1809</uuid>
c1c534
+  <memory unit='KiB'>219136</memory>
c1c534
+  <currentMemory unit='KiB'>219136</currentMemory>
c1c534
+  <vcpu placement='static'>4</vcpu>
c1c534
+  <cputune>
c1c534
+    <cachetune vcpus='0'>
c1c534
+      <cache id='0' level='3' type='code' size='12' unit='KiB'/>
c1c534
+    </cachetune>
c1c534
+    <cachetune vcpus='0'>
c1c534
+      <cache id='0' level='3' type='data' size='18' unit='KiB'/>
c1c534
+    </cachetune>
c1c534
+  </cputune>
c1c534
+  <os>
c1c534
+    <type arch='i686' machine='pc'>hvm</type>
c1c534
+    <boot dev='hd'/>
c1c534
+  </os>
c1c534
+  <clock offset='utc'/>
c1c534
+  <on_poweroff>destroy</on_poweroff>
c1c534
+  <on_reboot>restart</on_reboot>
c1c534
+  <on_crash>destroy</on_crash>
c1c534
+  <devices>
c1c534
+    <emulator>/usr/bin/qemu-system-i686</emulator>
c1c534
+    <controller type='usb' index='0'/>
c1c534
+    <controller type='ide' index='0'/>
c1c534
+    <controller type='pci' index='0' model='pci-root'/>
c1c534
+    <input type='mouse' bus='ps2'/>
c1c534
+    <input type='keyboard' bus='ps2'/>
c1c534
+    <memballoon model='virtio'/>
c1c534
+  </devices>
c1c534
+</domain>
c1c534
diff --git a/tests/genericxml2xmlindata/generic-cachetune-colliding-types.xml b/tests/genericxml2xmlindata/generic-cachetune-colliding-types.xml
c1c534
new file mode 100644
c1c534
index 0000000000..c229eccee4
c1c534
--- /dev/null
c1c534
+++ b/tests/genericxml2xmlindata/generic-cachetune-colliding-types.xml
c1c534
@@ -0,0 +1,30 @@
c1c534
+<domain type='qemu'>
c1c534
+  <name>QEMUGuest1</name>
c1c534
+  <uuid>c7a5fdbd-edaf-9455-926a-d65c16db1809</uuid>
c1c534
+  <memory unit='KiB'>219136</memory>
c1c534
+  <currentMemory unit='KiB'>219136</currentMemory>
c1c534
+  <vcpu placement='static'>4</vcpu>
c1c534
+  <cputune>
c1c534
+    <cachetune vcpus='0-1'>
c1c534
+      <cache id='0' level='3' type='both' size='12' unit='KiB'/>
c1c534
+      <cache id='0' level='3' type='code' size='6' unit='KiB'/>
c1c534
+    </cachetune>
c1c534
+  </cputune>
c1c534
+  <os>
c1c534
+    <type arch='i686' machine='pc'>hvm</type>
c1c534
+    <boot dev='hd'/>
c1c534
+  </os>
c1c534
+  <clock offset='utc'/>
c1c534
+  <on_poweroff>destroy</on_poweroff>
c1c534
+  <on_reboot>restart</on_reboot>
c1c534
+  <on_crash>destroy</on_crash>
c1c534
+  <devices>
c1c534
+    <emulator>/usr/bin/qemu-system-i686</emulator>
c1c534
+    <controller type='usb' index='0'/>
c1c534
+    <controller type='ide' index='0'/>
c1c534
+    <controller type='pci' index='0' model='pci-root'/>
c1c534
+    <input type='mouse' bus='ps2'/>
c1c534
+    <input type='keyboard' bus='ps2'/>
c1c534
+    <memballoon model='virtio'/>
c1c534
+  </devices>
c1c534
+</domain>
c1c534
diff --git a/tests/genericxml2xmlindata/generic-cachetune-small.xml b/tests/genericxml2xmlindata/generic-cachetune-small.xml
c1c534
new file mode 100644
c1c534
index 0000000000..ab2d9cf885
c1c534
--- /dev/null
c1c534
+++ b/tests/genericxml2xmlindata/generic-cachetune-small.xml
c1c534
@@ -0,0 +1,29 @@
c1c534
+<domain type='qemu'>
c1c534
+  <name>QEMUGuest1</name>
c1c534
+  <uuid>c7a5fdbd-edaf-9455-926a-d65c16db1809</uuid>
c1c534
+  <memory unit='KiB'>219136</memory>
c1c534
+  <currentMemory unit='KiB'>219136</currentMemory>
c1c534
+  <vcpu placement='static'>4</vcpu>
c1c534
+  <cputune>
c1c534
+    <cachetune vcpus='0-1'>
c1c534
+      <cache id='0' level='3' type='both' size='768' unit='KiB'/>
c1c534
+    </cachetune>
c1c534
+  </cputune>
c1c534
+  <os>
c1c534
+    <type arch='i686' machine='pc'>hvm</type>
c1c534
+    <boot dev='hd'/>
c1c534
+  </os>
c1c534
+  <clock offset='utc'/>
c1c534
+  <on_poweroff>destroy</on_poweroff>
c1c534
+  <on_reboot>restart</on_reboot>
c1c534
+  <on_crash>destroy</on_crash>
c1c534
+  <devices>
c1c534
+    <emulator>/usr/bin/qemu-system-i686</emulator>
c1c534
+    <controller type='usb' index='0'/>
c1c534
+    <controller type='ide' index='0'/>
c1c534
+    <controller type='pci' index='0' model='pci-root'/>
c1c534
+    <input type='mouse' bus='ps2'/>
c1c534
+    <input type='keyboard' bus='ps2'/>
c1c534
+    <memballoon model='virtio'/>
c1c534
+  </devices>
c1c534
+</domain>
c1c534
diff --git a/tests/genericxml2xmlindata/generic-cachetune.xml b/tests/genericxml2xmlindata/generic-cachetune.xml
c1c534
new file mode 100644
c1c534
index 0000000000..645cab7771
c1c534
--- /dev/null
c1c534
+++ b/tests/genericxml2xmlindata/generic-cachetune.xml
c1c534
@@ -0,0 +1,33 @@
c1c534
+<domain type='qemu'>
c1c534
+  <name>QEMUGuest1</name>
c1c534
+  <uuid>c7a5fdbd-edaf-9455-926a-d65c16db1809</uuid>
c1c534
+  <memory unit='KiB'>219136</memory>
c1c534
+  <currentMemory unit='KiB'>219136</currentMemory>
c1c534
+  <vcpu placement='static'>4</vcpu>
c1c534
+  <cputune>
c1c534
+    <cachetune vcpus='0-1'>
c1c534
+      <cache id='0' level='3' type='both' size='3' unit='MiB'/>
c1c534
+      <cache id='1' level='3' type='both' size='3' unit='MiB'/>
c1c534
+    </cachetune>
c1c534
+    <cachetune vcpus='3'>
c1c534
+      <cache id='0' level='3' type='both' size='3' unit='MiB'/>
c1c534
+    </cachetune>
c1c534
+  </cputune>
c1c534
+  <os>
c1c534
+    <type arch='i686' machine='pc'>hvm</type>
c1c534
+    <boot dev='hd'/>
c1c534
+  </os>
c1c534
+  <clock offset='utc'/>
c1c534
+  <on_poweroff>destroy</on_poweroff>
c1c534
+  <on_reboot>restart</on_reboot>
c1c534
+  <on_crash>destroy</on_crash>
c1c534
+  <devices>
c1c534
+    <emulator>/usr/bin/qemu-system-i686</emulator>
c1c534
+    <controller type='usb' index='0'/>
c1c534
+    <controller type='ide' index='0'/>
c1c534
+    <controller type='pci' index='0' model='pci-root'/>
c1c534
+    <input type='mouse' bus='ps2'/>
c1c534
+    <input type='keyboard' bus='ps2'/>
c1c534
+    <memballoon model='virtio'/>
c1c534
+  </devices>
c1c534
+</domain>
c1c534
diff --git a/tests/genericxml2xmltest.c b/tests/genericxml2xmltest.c
c1c534
index 0377a05e9c..4ced5c349c 100644
c1c534
--- a/tests/genericxml2xmltest.c
c1c534
+++ b/tests/genericxml2xmltest.c
c1c534
@@ -130,6 +130,16 @@ mymain(void)
c1c534
     DO_TEST_FULL("chardev-reconnect-invalid-mode", 0, false,
c1c534
                  TEST_COMPARE_DOM_XML2XML_RESULT_FAIL_PARSE);
c1c534
 
c1c534
+    DO_TEST("cachetune");
c1c534
+    DO_TEST("cachetune-small");
c1c534
+    DO_TEST("cachetune-cdp");
c1c534
+    DO_TEST_FULL("cachetune-colliding-allocs", false, true,
c1c534
+                 TEST_COMPARE_DOM_XML2XML_RESULT_FAIL_PARSE);
c1c534
+    DO_TEST_FULL("cachetune-colliding-tunes", false, true,
c1c534
+                 TEST_COMPARE_DOM_XML2XML_RESULT_FAIL_PARSE);
c1c534
+    DO_TEST_FULL("cachetune-colliding-types", false, true,
c1c534
+                 TEST_COMPARE_DOM_XML2XML_RESULT_FAIL_PARSE);
c1c534
+
c1c534
     virObjectUnref(caps);
c1c534
     virObjectUnref(xmlopt);
c1c534
 
c1c534
-- 
c1c534
2.16.1
c1c534