Signed-off-by: Martin Kletzander <mkletzan(a)redhat.com>
---
docs/formatdomain.html.in | 24 ++
docs/schemas/domaincommon.rng | 32 +++
src/conf/domain_conf.c | 249 +++++++++++++++++++++
src/conf/domain_conf.h | 21 ++
src/util/virresctrl.c | 2 +-
.../genericxml2xmlindata/generic-cachetune-cdp.xml | 36 +++
.../generic-cachetune-colliding-allocs.xml | 30 +++
.../generic-cachetune-colliding-tunes.xml | 32 +++
.../generic-cachetune-colliding-types.xml | 30 +++
.../generic-cachetune-small.xml | 29 +++
tests/genericxml2xmlindata/generic-cachetune.xml | 33 +++
tests/genericxml2xmltest.c | 10 +
12 files changed, 527 insertions(+), 1 deletion(-)
create mode 100644 tests/genericxml2xmlindata/generic-cachetune-cdp.xml
create mode 100644 tests/genericxml2xmlindata/generic-cachetune-colliding-allocs.xml
create mode 100644 tests/genericxml2xmlindata/generic-cachetune-colliding-tunes.xml
create mode 100644 tests/genericxml2xmlindata/generic-cachetune-colliding-types.xml
create mode 100644 tests/genericxml2xmlindata/generic-cachetune-small.xml
create mode 100644 tests/genericxml2xmlindata/generic-cachetune.xml
diff --git a/docs/formatdomain.html.in b/docs/formatdomain.html.in
index 92e14a919aba..b9d7f53b31f9 100644
--- a/docs/formatdomain.html.in
+++ b/docs/formatdomain.html.in
@@ -689,6 +689,10 @@
<iothread_quota>-1</iothread_quota>
<vcpusched vcpus='0-4,^3' scheduler='fifo'
priority='1'/>
<iothreadsched iothreads='2' scheduler='batch'/>
+ <cachetune vcpus='0-3'>
+ <cache id='0' level='3' type='both' size='3'
unit='MiB'/>
+ <cache id='1' level='3' type='both' size='3'
unit='MiB'/>
+ </cachetune>
</cputune>
...
</domain>
@@ -834,6 +838,26 @@
<span class="since">Since 1.2.13</span>
</dd>
+ <dt><code>cachetune</code></dt>
+ <dd>
+ Optional <code>cachetune</code> element can control allocations for
CPU
+ caches using the resctrlfs on the host. Whether or not is this supported
+ can be gathered from capabilities where some limitations like minimum
+ size and required granularity are reported as well.
+ Attribute <code>vcpus</code> specifies what vCPUs this allocation
+ applies to. One vCPU cannot have multiple <code>cachetune</code>
+ allocations. For now there is only one supported
+ sub-element, <code>cache</code>. That element must have
+ attributes <code>level</code> and <code>id</code> -
host's cache level/id
+ to allocate from, <code>type</code> - whether to
+ allocate <code>code</code>, <code>data</code> or
<code>both</code>
+ and <code>size</code> - size of the region to allocate. Size is by
+ default in bytes, but optional attribute <code>unit</code> can be
used
+ to scale the size appropriately. Each <code>cachetune</code> can
have
+ multiple <code>cache</code> elements. <span
class="since">Since
+ 3.10.0</span>
+
+ </dd>
</dl>
diff --git a/docs/schemas/domaincommon.rng b/docs/schemas/domaincommon.rng
index 9cec1a063724..5d90247799ad 100644
--- a/docs/schemas/domaincommon.rng
+++ b/docs/schemas/domaincommon.rng
@@ -900,6 +900,38 @@
<ref name="schedparam"/>
</element>
</zeroOrMore>
+ <zeroOrMore>
+ <element name="cachetune">
+ <attribute name="vcpus">
+ <ref name='cpuset'/>
+ </attribute>
+ <oneOrMore>
+ <element name="cache">
+ <attribute name="id">
+ <ref name='unsignedInt'/>
+ </attribute>
+ <attribute name="level">
+ <ref name='unsignedInt'/>
+ </attribute>
+ <attribute name="type">
+ <choice>
+ <value>both</value>
+ <value>code</value>
+ <value>data</value>
+ </choice>
+ </attribute>
+ <attribute name="size">
+ <ref name='unsignedLong'/>
+ </attribute>
+ <optional>
+ <attribute name='unit'>
+ <ref name='unit'/>
+ </attribute>
+ </optional>
+ </element>
+ </oneOrMore>
+ </element>
+ </zeroOrMore>
</interleave>
</element>
</define>
diff --git a/src/conf/domain_conf.c b/src/conf/domain_conf.c
index 400e900325f2..bf9e61efc8d2 100644
--- a/src/conf/domain_conf.c
+++ b/src/conf/domain_conf.c
@@ -2848,6 +2848,17 @@ virDomainLoaderDefFree(virDomainLoaderDefPtr loader)
VIR_FREE(loader);
}
+static void
+virDomainCachetuneDefFree(virDomainCachetuneDefPtr cachetune)
+{
+ if (!cachetune)
+ return;
+
+ virObjectUnref(cachetune->alloc);
+ virBitmapFree(cachetune->vcpus);
+ VIR_FREE(cachetune);
+}
+
void virDomainDefFree(virDomainDefPtr def)
{
size_t i;
@@ -3020,6 +3031,10 @@ void virDomainDefFree(virDomainDefPtr def)
virDomainShmemDefFree(def->shmems[i]);
VIR_FREE(def->shmems);
+ for (i = 0; i < def->ncachetunes; i++)
+ virDomainCachetuneDefFree(def->cachetunes[i]);
+ VIR_FREE(def->cachetunes);
+
VIR_FREE(def->keywrap);
if (def->namespaceData && def->ns.free)
@@ -18080,6 +18095,163 @@ virDomainDefParseBootOptions(virDomainDefPtr def,
}
+static int
+virDomainCachetuneDefParseCache(xmlXPathContextPtr ctxt,
+ xmlNodePtr node,
+ virResctrlAllocPtr *alloc)
+{
+ xmlNodePtr oldnode = ctxt->node;
+ unsigned int level;
+ unsigned int cache;
+ int type;
+ unsigned long long size;
+ char *tmp = NULL;
+ int ret = -1;
+
+ ctxt->node = node;
+
+ tmp = virXMLPropString(node, "id");
+ if (!tmp) {
+ virReportError(VIR_ERR_XML_ERROR, "%s",
+ _("Missing attribute id for cachetune/cache"));
+ goto cleanup;
+ }
+ if (virStrToLong_uip(tmp, NULL, 10, &cache) < 0) {
+ virReportError(VIR_ERR_XML_ERROR,
+ _("Invalid attribute id '%s' for
cachetune/cache"),
+ tmp);
+ goto cleanup;
+ }
+ VIR_FREE(tmp);
+
+ tmp = virXMLPropString(node, "level");
+ if (!tmp) {
+ virReportError(VIR_ERR_XML_ERROR, "%s",
+ _("Missing attribute level for cachetune/cache"));
+ goto cleanup;
+ }
+ if (virStrToLong_uip(tmp, NULL, 10, &level) < 0) {
+ virReportError(VIR_ERR_XML_ERROR,
+ _("Invalid attribute level '%s' for
cachetune/cache"),
+ tmp);
+ goto cleanup;
+ }
+ VIR_FREE(tmp);
+
+ tmp = virXMLPropString(node, "type");
+ if (!tmp) {
+ virReportError(VIR_ERR_XML_ERROR, "%s",
+ _("Missing attribute type for cachetune/cache"));
+ goto cleanup;
+ }
+ type = virCacheTypeFromString(tmp);
+ if (type < 0) {
+ virReportError(VIR_ERR_XML_ERROR,
+ _("Invalid attribute type '%s' for cachetune"),
+ tmp);
+ goto cleanup;
+ }
+ VIR_FREE(tmp);
+
+ if (virDomainParseScaledValue("./@size", "./@unit",
+ ctxt, &size, 1024,
+ ULLONG_MAX, true) < 0)
+ goto cleanup;
+
+ if (virResctrlAllocSetSize(alloc, level, type, cache, size) < 0)
+ goto cleanup;
+
+ ret = 0;
+ cleanup:
+ ctxt->node = oldnode;
+ VIR_FREE(tmp);
+ return ret;
+}
+
+static int
+virDomainCachetuneDefParse(virDomainDefPtr def,
+ xmlXPathContextPtr ctxt,
+ xmlNodePtr node)
+{
+ xmlNodePtr oldnode = ctxt->node;
+ xmlNodePtr *nodes = NULL;
+ virBitmapPtr vcpus = NULL;
+ virResctrlAllocPtr alloc = NULL;
+ virDomainCachetuneDefPtr tmp_cachetune = NULL;
+ char *tmp = NULL;
+ char *vcpus_str = NULL;
+ ssize_t i = 0;
+ int n;
+ int ret = -1;
+
+ ctxt->node = node;
+
+ if (VIR_ALLOC(tmp_cachetune) < 0)
+ goto cleanup;
+
+ vcpus_str = virXMLPropString(node, "vcpus");
+ if (!vcpus_str) {
+ virReportError(VIR_ERR_XML_ERROR, "%s",
+ _("Missing attribute vcpus for cachetune"));
+ goto cleanup;
+ }
+ if (virBitmapParse(vcpus_str, &vcpus, VIR_DOMAIN_CPUMASK_LEN) < 0) {
+ virReportError(VIR_ERR_XML_ERROR,
+ _("Invalid attribute vcpus '%s' for
cachetune"),
+ vcpus_str);
+ goto cleanup;
+ }
+
+ if ((n = virXPathNodeSet("./cache", ctxt, &nodes)) < 0) {
+ virReportError(VIR_ERR_INTERNAL_ERROR, "%s",
+ _("cannot extract cachetune/cache nodes"));
+ goto cleanup;
+ }
+
+ for (i = 0; i < n; i++) {
+ if (virDomainCachetuneDefParseCache(ctxt, nodes[i], &alloc) < 0)
+ goto cleanup;
+ }
+
+ if (!alloc) {
+ ret = 0;
+ goto cleanup;
+ }
+
+ virBitmapShrink(vcpus, def->maxvcpus);
+
+ for (i = 0; i < def->ncachetunes; i++) {
+ if (virBitmapOverlaps(def->cachetunes[i]->vcpus, vcpus)) {
+ virReportError(VIR_ERR_XML_ERROR, "%s",
+ _("Overlapping vcpus in cachetunes"));
+ goto cleanup;
+ }
+ }
+
+ if (virResctrlAllocSetID(alloc, vcpus_str) < 0)
+ goto cleanup;
+
+ tmp_cachetune->vcpus = vcpus;
+ tmp_cachetune->alloc = alloc;
+ vcpus = NULL;
+ alloc = NULL;
+
+ if (VIR_APPEND_ELEMENT(def->cachetunes, def->ncachetunes, tmp_cachetune) <
0)
+ goto cleanup;
+
+ ret = 0;
+ cleanup:
+ ctxt->node = oldnode;
+ virDomainCachetuneDefFree(tmp_cachetune);
+ virObjectUnref(alloc);
+ virBitmapFree(vcpus);
+ VIR_FREE(vcpus_str);
+ VIR_FREE(nodes);
+ VIR_FREE(tmp);
+ return ret;
+}
+
+
static virDomainDefPtr
virDomainDefParseXML(xmlDocPtr xml,
xmlNodePtr root,
@@ -18632,6 +18804,18 @@ virDomainDefParseXML(xmlDocPtr xml,
}
VIR_FREE(nodes);
+ if ((n = virXPathNodeSet("./cputune/cachetune", ctxt, &nodes)) < 0)
{
+ virReportError(VIR_ERR_INTERNAL_ERROR, "%s",
+ _("cannot extract cachetune nodes"));
+ goto error;
+ }
+
+ for (i = 0; i < n; i++) {
+ if (virDomainCachetuneDefParse(def, ctxt, nodes[i]) < 0)
+ goto error;
+ }
+ VIR_FREE(nodes);
+
if (virCPUDefParseXML(ctxt, "./cpu[1]", VIR_CPU_TYPE_GUEST,
&def->cpu) < 0)
goto error;
@@ -25444,6 +25628,68 @@ virDomainSchedulerFormat(virBufferPtr buf,
}
+struct virCachetuneHelperData {
+ virBufferPtr buf;
+ size_t vcpu_id;
+};
+
+static int
+virDomainCachetuneDefFormatHelper(unsigned int level,
+ virCacheType type,
+ unsigned int cache,
+ unsigned long long size,
+ void *opaque)
+{
+ const char *unit;
+ virBufferPtr buf = opaque;
+ unsigned long long short_size = virPrettySize(size, &unit);
+
+ virBufferAsprintf(buf,
+ "<cache id='%u' level='%u'
type='%s' "
+ "size='%llu' unit='%s'/>\n",
+ cache, level, virCacheTypeToString(type),
+ short_size, unit);
+
+ return 0;
+}
+
+
+static int
+virDomainCachetuneDefFormat(virBufferPtr buf,
+ virDomainCachetuneDefPtr cachetune)
+{
+ virBuffer childrenBuf = VIR_BUFFER_INITIALIZER;
+ char *vcpus = NULL;
+ int ret = -1;
+
+ virBufferSetChildIndent(&childrenBuf, buf);
+ virResctrlAllocForeachSize(cachetune->alloc,
+ virDomainCachetuneDefFormatHelper,
+ &childrenBuf);
+
+
+ if (virBufferCheckError(&childrenBuf) < 0)
+ goto cleanup;
+
+ if (!virBufferUse(&childrenBuf)) {
+ ret = 0;
+ goto cleanup;
+ }
+
+ vcpus = virBitmapFormat(cachetune->vcpus);
+
+ virBufferAsprintf(buf, "<cachetune vcpus='%s'>\n", vcpus);
+ virBufferAddBuffer(buf, &childrenBuf);
+ virBufferAddLit(buf, "</cachetune>\n");
+
+ ret = 0;
+ cleanup:
+ virBufferFreeAndReset(&childrenBuf);
+ VIR_FREE(vcpus);
+ return ret;
+}
+
+
static int
virDomainCputuneDefFormat(virBufferPtr buf,
virDomainDefPtr def)
@@ -25545,6 +25791,9 @@ virDomainCputuneDefFormat(virBufferPtr buf,
def->iothreadids[i]->iothread_id);
}
+ for (i = 0; i < def->ncachetunes; i++)
+ virDomainCachetuneDefFormat(&childrenBuf, def->cachetunes[i]);
+
if (virBufferCheckError(&childrenBuf) < 0)
return -1;
diff --git a/src/conf/domain_conf.h b/src/conf/domain_conf.h
index be38792c6942..bda83d72e7ed 100644
--- a/src/conf/domain_conf.h
+++ b/src/conf/domain_conf.h
@@ -56,6 +56,7 @@
# include "virperf.h"
# include "virtypedparam.h"
# include "virsavecookie.h"
+# include "virresctrl.h"
/* forward declarations of all device types, required by
* virDomainDeviceDef
@@ -2167,6 +2168,15 @@ struct _virDomainCputune {
};
+typedef struct _virDomainCachetuneDef virDomainCachetuneDef;
+typedef virDomainCachetuneDef *virDomainCachetuneDefPtr;
+
+struct _virDomainCachetuneDef {
+ virBitmapPtr vcpus;
+ virResctrlAllocPtr alloc;
+};
+
+
typedef struct _virDomainVcpuDef virDomainVcpuDef;
typedef virDomainVcpuDef *virDomainVcpuDefPtr;
@@ -2294,6 +2304,17 @@ struct _virDomainDef {
virDomainIOThreadIDDefPtr *iothreadids;
virDomainCputune cputune;
+ /*
+ * Both cachetune as well as runtime allocation (the same structure, but
+ * updated) for all threads is kept here. It is specified here instead of
+ * specifying it in virDomainVcpuDef _deliberately_ because it needs to be
+ * set at once under one flock() and not for each thread separately. Also
+ * if one cachetune is specified for multiple vCPUs, it must be created as
+ * one due to limited number of concurrent cachetune settings (number of
+ * CLoS IDs) provided by hardware.
+ */
+ virDomainCachetuneDefPtr *cachetunes;
+ size_t ncachetunes;
virDomainNumaPtr numa;
virDomainResourceDefPtr resource;
diff --git a/src/util/virresctrl.c b/src/util/virresctrl.c
index ac1b38436bb2..8753b1dca325 100644
--- a/src/util/virresctrl.c
+++ b/src/util/virresctrl.c
@@ -693,7 +693,7 @@ virResctrlAllocSetID(virResctrlAllocPtr alloc,
{
if (!id) {
virReportError(VIR_ERR_INTERNAL_ERROR, "%s",
- _("Resctrl allocation id cannot be NULL"));
+ _("Resctrl allocation 'id' cannot be NULL"));
return -1;
}
diff --git a/tests/genericxml2xmlindata/generic-cachetune-cdp.xml
b/tests/genericxml2xmlindata/generic-cachetune-cdp.xml
new file mode 100644
index 000000000000..1331aad06e54
--- /dev/null
+++ b/tests/genericxml2xmlindata/generic-cachetune-cdp.xml
@@ -0,0 +1,36 @@
+<domain type='qemu'>
+ <name>QEMUGuest1</name>
+ <uuid>c7a5fdbd-edaf-9455-926a-d65c16db1809</uuid>
+ <memory unit='KiB'>219136</memory>
+ <currentMemory unit='KiB'>219136</currentMemory>
+ <vcpu placement='static'>2</vcpu>
+ <cputune>
+ <cachetune vcpus='0-1'>
+ <cache id='0' level='3' type='code' size='7680'
unit='KiB'/>
+ <cache id='1' level='3' type='data' size='3840'
unit='KiB'/>
+ </cachetune>
+ <cachetune vcpus='2'>
+ <cache id='1' level='3' type='code' size='6'
unit='MiB'/>
+ </cachetune>
+ <cachetune vcpus='3'>
+ <cache id='1' level='3' type='data' size='6912'
unit='KiB'/>
+ </cachetune>
+ </cputune>
+ <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-system-i686</emulator>
+ <controller type='usb' index='0'/>
+ <controller type='ide' index='0'/>
+ <controller type='pci' index='0' model='pci-root'/>
+ <input type='mouse' bus='ps2'/>
+ <input type='keyboard' bus='ps2'/>
+ <memballoon model='virtio'/>
+ </devices>
+</domain>
diff --git a/tests/genericxml2xmlindata/generic-cachetune-colliding-allocs.xml
b/tests/genericxml2xmlindata/generic-cachetune-colliding-allocs.xml
new file mode 100644
index 000000000000..994f8fcf2a4e
--- /dev/null
+++ b/tests/genericxml2xmlindata/generic-cachetune-colliding-allocs.xml
@@ -0,0 +1,30 @@
+<domain type='qemu'>
+ <name>QEMUGuest1</name>
+ <uuid>c7a5fdbd-edaf-9455-926a-d65c16db1809</uuid>
+ <memory unit='KiB'>219136</memory>
+ <currentMemory unit='KiB'>219136</currentMemory>
+ <vcpu placement='static'>2</vcpu>
+ <cputune>
+ <cachetune vcpus='0'>
+ <cache id='0' level='3' type='code' size='12'
unit='KiB'/>
+ <cache id='0' level='3' type='code' size='18'
unit='KiB'/>
+ </cachetune>
+ </cputune>
+ <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-system-i686</emulator>
+ <controller type='usb' index='0'/>
+ <controller type='ide' index='0'/>
+ <controller type='pci' index='0' model='pci-root'/>
+ <input type='mouse' bus='ps2'/>
+ <input type='keyboard' bus='ps2'/>
+ <memballoon model='virtio'/>
+ </devices>
+</domain>
diff --git a/tests/genericxml2xmlindata/generic-cachetune-colliding-tunes.xml
b/tests/genericxml2xmlindata/generic-cachetune-colliding-tunes.xml
new file mode 100644
index 000000000000..3d9db85b12d4
--- /dev/null
+++ b/tests/genericxml2xmlindata/generic-cachetune-colliding-tunes.xml
@@ -0,0 +1,32 @@
+<domain type='qemu'>
+ <name>QEMUGuest1</name>
+ <uuid>c7a5fdbd-edaf-9455-926a-d65c16db1809</uuid>
+ <memory unit='KiB'>219136</memory>
+ <currentMemory unit='KiB'>219136</currentMemory>
+ <vcpu placement='static'>2</vcpu>
+ <cputune>
+ <cachetune vcpus='0'>
+ <cache id='0' level='3' type='code' size='12'
unit='KiB'/>
+ </cachetune>
+ <cachetune vcpus='0'>
+ <cache id='0' level='3' type='data' size='18'
unit='KiB'/>
+ </cachetune>
+ </cputune>
+ <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-system-i686</emulator>
+ <controller type='usb' index='0'/>
+ <controller type='ide' index='0'/>
+ <controller type='pci' index='0' model='pci-root'/>
+ <input type='mouse' bus='ps2'/>
+ <input type='keyboard' bus='ps2'/>
+ <memballoon model='virtio'/>
+ </devices>
+</domain>
diff --git a/tests/genericxml2xmlindata/generic-cachetune-colliding-types.xml
b/tests/genericxml2xmlindata/generic-cachetune-colliding-types.xml
new file mode 100644
index 000000000000..1a4e62cd17d3
--- /dev/null
+++ b/tests/genericxml2xmlindata/generic-cachetune-colliding-types.xml
@@ -0,0 +1,30 @@
+<domain type='qemu'>
+ <name>QEMUGuest1</name>
+ <uuid>c7a5fdbd-edaf-9455-926a-d65c16db1809</uuid>
+ <memory unit='KiB'>219136</memory>
+ <currentMemory unit='KiB'>219136</currentMemory>
+ <vcpu placement='static'>2</vcpu>
+ <cputune>
+ <cachetune vcpus='0-1'>
+ <cache id='0' level='3' type='both' size='12'
unit='KiB'/>
+ <cache id='0' level='3' type='code' size='6'
unit='KiB'/>
+ </cachetune>
+ </cputune>
+ <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-system-i686</emulator>
+ <controller type='usb' index='0'/>
+ <controller type='ide' index='0'/>
+ <controller type='pci' index='0' model='pci-root'/>
+ <input type='mouse' bus='ps2'/>
+ <input type='keyboard' bus='ps2'/>
+ <memballoon model='virtio'/>
+ </devices>
+</domain>
diff --git a/tests/genericxml2xmlindata/generic-cachetune-small.xml
b/tests/genericxml2xmlindata/generic-cachetune-small.xml
new file mode 100644
index 000000000000..4b25490b3abb
--- /dev/null
+++ b/tests/genericxml2xmlindata/generic-cachetune-small.xml
@@ -0,0 +1,29 @@
+<domain type='qemu'>
+ <name>QEMUGuest1</name>
+ <uuid>c7a5fdbd-edaf-9455-926a-d65c16db1809</uuid>
+ <memory unit='KiB'>219136</memory>
+ <currentMemory unit='KiB'>219136</currentMemory>
+ <vcpu placement='static'>2</vcpu>
+ <cputune>
+ <cachetune vcpus='0-1'>
+ <cache id='0' level='3' type='both' size='768'
unit='KiB'/>
+ </cachetune>
+ </cputune>
+ <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-system-i686</emulator>
+ <controller type='usb' index='0'/>
+ <controller type='ide' index='0'/>
+ <controller type='pci' index='0' model='pci-root'/>
+ <input type='mouse' bus='ps2'/>
+ <input type='keyboard' bus='ps2'/>
+ <memballoon model='virtio'/>
+ </devices>
+</domain>
diff --git a/tests/genericxml2xmlindata/generic-cachetune.xml
b/tests/genericxml2xmlindata/generic-cachetune.xml
new file mode 100644
index 000000000000..0051410aec32
--- /dev/null
+++ b/tests/genericxml2xmlindata/generic-cachetune.xml
@@ -0,0 +1,33 @@
+<domain type='qemu'>
+ <name>QEMUGuest1</name>
+ <uuid>c7a5fdbd-edaf-9455-926a-d65c16db1809</uuid>
+ <memory unit='KiB'>219136</memory>
+ <currentMemory unit='KiB'>219136</currentMemory>
+ <vcpu placement='static'>2</vcpu>
+ <cputune>
+ <cachetune vcpus='0-1'>
+ <cache id='0' level='3' type='both' size='3'
unit='MiB'/>
+ <cache id='1' level='3' type='both' size='3'
unit='MiB'/>
+ </cachetune>
+ <cachetune vcpus='3'>
+ <cache id='0' level='3' type='both' size='3'
unit='MiB'/>
+ </cachetune>
+ </cputune>
+ <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-system-i686</emulator>
+ <controller type='usb' index='0'/>
+ <controller type='ide' index='0'/>
+ <controller type='pci' index='0' model='pci-root'/>
+ <input type='mouse' bus='ps2'/>
+ <input type='keyboard' bus='ps2'/>
+ <memballoon model='virtio'/>
+ </devices>
+</domain>
diff --git a/tests/genericxml2xmltest.c b/tests/genericxml2xmltest.c
index a0900d9db7e4..0f1a69fef892 100644
--- a/tests/genericxml2xmltest.c
+++ b/tests/genericxml2xmltest.c
@@ -130,6 +130,16 @@ mymain(void)
DO_TEST_FULL("chardev-reconnect-invalid-mode", 0, false,
TEST_COMPARE_DOM_XML2XML_RESULT_FAIL_PARSE);
+ DO_TEST("cachetune");
+ DO_TEST("cachetune-small");
+ DO_TEST("cachetune-cdp");
+ DO_TEST_FULL("cachetune-colliding-allocs", false, true,
+ TEST_COMPARE_DOM_XML2XML_RESULT_FAIL_PARSE);
+ DO_TEST_FULL("cachetune-colliding-tunes", false, true,
+ TEST_COMPARE_DOM_XML2XML_RESULT_FAIL_PARSE);
+ DO_TEST_FULL("cachetune-colliding-types", false, true,
+ TEST_COMPARE_DOM_XML2XML_RESULT_FAIL_PARSE);
+
virObjectUnref(caps);
virObjectUnref(xmlopt);
--
2.15.0