+ Optional cachetune element can control allocations for CPU
+ caches using the resctrl 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. 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:
+
+
cache
+
+ This element controls the allocation of CPU cache and has the
+ following attributes:
+
+
level
+
+ Host cache level from which to allocate.
+
+
id
+
+ Host cache id from which to allocate.
+
+
type
+
+ Type of allocation. Can be code for code
+ (instructions), data for data or both
+ for both code and data (unified). Currently the allocation can
+ be done only with the same type as the host supports, meaning
+ you cannot request both for host with CDP
+ (code/data prioritization) enabled.
+
+
size
+
+ The size of the region to allocate. The value by default is in
+ bytes, but the unit attribute can be used to scale
+ the value.
+
+
unit (optional)
+
+ If specified it is the unit such as KiB, MiB, GiB, or TiB
+ (described in the memory element
+ for Memory Allocation)
+ in which size is specified, defaults to bytes.
+
+
+
+
+
+
diff --git a/docs/schemas/domaincommon.rng b/docs/schemas/domaincommon.rng
index a154b5a462b1b6e0f693d3f07614647bd2646358..e4efee2e85c477d071475afb29dcb03e0c17365d 100644
--- a/docs/schemas/domaincommon.rng
+++ b/docs/schemas/domaincommon.rng
@@ -900,6 +900,38 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ both
+ code
+ data
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/src/conf/domain_conf.c b/src/conf/domain_conf.c
index 90a58686cb841a7cc40bc72f675bc1dc10335102..9b215dae7211bc6d4712d5042936af221bdedd7d 100644
--- a/src/conf/domain_conf.c
+++ b/src/conf/domain_conf.c
@@ -2883,6 +2883,19 @@ 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;
@@ -3055,6 +3068,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)
@@ -18294,6 +18311,194 @@ 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 cachetune attribute 'id'"));
+ goto cleanup;
+ }
+ if (virStrToLong_uip(tmp, NULL, 10, &cache) < 0) {
+ virReportError(VIR_ERR_XML_ERROR,
+ _("Invalid cachetune attribute 'id' value '%s'"),
+ tmp);
+ goto cleanup;
+ }
+ VIR_FREE(tmp);
+
+ tmp = virXMLPropString(node, "level");
+ if (!tmp) {
+ virReportError(VIR_ERR_XML_ERROR, "%s",
+ _("Missing cachetune attribute 'level'"));
+ goto cleanup;
+ }
+ if (virStrToLong_uip(tmp, NULL, 10, &level) < 0) {
+ virReportError(VIR_ERR_XML_ERROR,
+ _("Invalid cachetune attribute 'level' value '%s'"),
+ tmp);
+ goto cleanup;
+ }
+ VIR_FREE(tmp);
+
+ tmp = virXMLPropString(node, "type");
+ if (!tmp) {
+ virReportError(VIR_ERR_XML_ERROR, "%s",
+ _("Missing cachetune attribute 'type'"));
+ goto cleanup;
+ }
+ type = virCacheTypeFromString(tmp);
+ if (type < 0) {
+ virReportError(VIR_ERR_XML_ERROR,
+ _("Invalid cachetune attribute 'type' value '%s'"),
+ 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,
+ unsigned int flags)
+{
+ xmlNodePtr oldnode = ctxt->node;
+ xmlNodePtr *nodes = NULL;
+ virBitmapPtr vcpus = NULL;
+ virResctrlAllocPtr alloc = virResctrlAllocNew();
+ virDomainCachetuneDefPtr tmp_cachetune = NULL;
+ char *tmp = NULL;
+ char *vcpus_str = NULL;
+ char *alloc_id = NULL;
+ ssize_t i = 0;
+ int n;
+ int ret = -1;
+
+ ctxt->node = node;
+
+ if (!alloc)
+ goto cleanup;
+
+ if (VIR_ALLOC(tmp_cachetune) < 0)
+ goto cleanup;
+
+ vcpus_str = virXMLPropString(node, "vcpus");
+ if (!vcpus_str) {
+ virReportError(VIR_ERR_XML_ERROR, "%s",
+ _("Missing cachetune attribute 'vcpus'"));
+ goto cleanup;
+ }
+ if (virBitmapParse(vcpus_str, &vcpus, VIR_DOMAIN_CPUMASK_LEN) < 0) {
+ virReportError(VIR_ERR_XML_ERROR,
+ _("Invalid cachetune attribute 'vcpus' value '%s'"),
+ vcpus_str);
+ goto cleanup;
+ }
+
+ /* We need to limit the bitmap to number of vCPUs. If there's nothing left,
+ * then we can just clean up and return 0 immediately */
+ virBitmapShrink(vcpus, def->maxvcpus);
+ if (virBitmapIsAllClear(vcpus)) {
+ ret = 0;
+ goto cleanup;
+ }
+
+ if ((n = virXPathNodeSet("./cache", ctxt, &nodes)) < 0) {
+ virReportError(VIR_ERR_INTERNAL_ERROR, "%s",
+ _("Cannot extract cache nodes under cachetune"));
+ goto cleanup;
+ }
+
+ for (i = 0; i < n; i++) {
+ if (virDomainCachetuneDefParseCache(ctxt, nodes[i], alloc) < 0)
+ goto cleanup;
+ }
+
+ if (virResctrlAllocIsEmpty(alloc)) {
+ ret = 0;
+ goto cleanup;
+ }
+
+ 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;
+ }
+ }
+
+ /* We need to format it back because we need to be consistent in the naming
+ * even when users specify some "sub-optimal" string there. */
+ VIR_FREE(vcpus_str);
+ vcpus_str = virBitmapFormat(vcpus);
+ if (!vcpus_str)
+ goto cleanup;
+
+ if (!(flags & VIR_DOMAIN_DEF_PARSE_INACTIVE))
+ alloc_id = virXMLPropString(node, "id");
+
+ if (!alloc_id) {
+ /* The number of allocations is limited and the directory structure is flat,
+ * not hierarchical, so we need to have all same allocations in one
+ * directory, so it's nice to have it named appropriately. For now it's
+ * 'vcpus_...' but it's designed in order for it to be changeable in the
+ * future (it's part of the status XML). */
+ if (virAsprintf(&alloc_id, "vcpus_%s", vcpus_str) < 0)
+ goto cleanup;
+ }
+
+ if (virResctrlAllocSetID(alloc, alloc_id) < 0)
+ goto cleanup;
+
+ VIR_STEAL_PTR(tmp_cachetune->vcpus, vcpus);
+ VIR_STEAL_PTR(tmp_cachetune->alloc, alloc);
+
+ 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(alloc_id);
+ VIR_FREE(vcpus_str);
+ VIR_FREE(nodes);
+ VIR_FREE(tmp);
+ return ret;
+}
+
+
static virDomainDefPtr
virDomainDefParseXML(xmlDocPtr xml,
xmlNodePtr root,
@@ -18846,6 +19051,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], flags) < 0)
+ goto error;
+ }
+ VIR_FREE(nodes);
+
if (virCPUDefParseXML(ctxt, "./cpu[1]", VIR_CPU_TYPE_GUEST, &def->cpu) < 0)
goto error;
@@ -25797,9 +26014,80 @@ virDomainSchedulerFormat(virBufferPtr buf,
}
+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 = virFormatIntPretty(size, &unit);
+
+ virBufferAsprintf(buf,
+ "\n",
+ cache, level, virCacheTypeToString(type),
+ short_size, unit);
+
+ return 0;
+}
+
+
+static int
+virDomainCachetuneDefFormat(virBufferPtr buf,
+ virDomainCachetuneDefPtr cachetune,
+ unsigned int flags)
+{
+ 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);
+ 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)
+ virDomainDefPtr def,
+ unsigned int flags)
{
size_t i;
virBuffer childrenBuf = VIR_BUFFER_INITIALIZER;
@@ -25898,6 +26186,9 @@ virDomainCputuneDefFormat(virBufferPtr buf,
def->iothreadids[i]->iothread_id);
}
+ for (i = 0; i < def->ncachetunes; i++)
+ virDomainCachetuneDefFormat(&childrenBuf, def->cachetunes[i], flags);
+
if (virBufferCheckError(&childrenBuf) < 0)
return -1;
@@ -26235,7 +26526,7 @@ virDomainDefFormatInternal(virDomainDefPtr def,
}
}
- if (virDomainCputuneDefFormat(buf, def) < 0)
+ if (virDomainCputuneDefFormat(buf, def, flags) < 0)
goto error;
if (virDomainNumatuneFormatXML(buf, def->numa) < 0)
diff --git a/src/conf/domain_conf.h b/src/conf/domain_conf.h
index 6f7f96b3ddea69c1f347488fb62edc4b35573472..ed85260926de380b000a08af8e60d5e993d972d3 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
@@ -2194,6 +2195,15 @@ struct _virDomainCputune {
};
+typedef struct _virDomainCachetuneDef virDomainCachetuneDef;
+typedef virDomainCachetuneDef *virDomainCachetuneDefPtr;
+
+struct _virDomainCachetuneDef {
+ virBitmapPtr vcpus;
+ virResctrlAllocPtr alloc;
+};
+
+
typedef struct _virDomainVcpuDef virDomainVcpuDef;
typedef virDomainVcpuDef *virDomainVcpuDefPtr;
@@ -2322,6 +2332,9 @@ struct _virDomainDef {
virDomainCputune cputune;
+ virDomainCachetuneDefPtr *cachetunes;
+ size_t ncachetunes;
+
virDomainNumaPtr numa;
virDomainResourceDefPtr resource;
virDomainIdMapDef idmap;
diff --git a/tests/genericxml2xmlindata/cachetune-cdp.xml b/tests/genericxml2xmlindata/cachetune-cdp.xml
new file mode 100644
index 0000000000000000000000000000000000000000..9718f060987a5b088f75edfe0628506542a6ac05
--- /dev/null
+++ b/tests/genericxml2xmlindata/cachetune-cdp.xml
@@ -0,0 +1,36 @@
+
+ QEMUGuest1
+ c7a5fdbd-edaf-9455-926a-d65c16db1809
+ 219136
+ 219136
+ 4
+
+
+
+
+
+
+
+
+
+
+
+
+
+ hvm
+
+
+
+ destroy
+ restart
+ destroy
+
+ /usr/bin/qemu-system-i686
+
+
+
+
+
+
+
+
diff --git a/tests/genericxml2xmlindata/cachetune-colliding-allocs.xml b/tests/genericxml2xmlindata/cachetune-colliding-allocs.xml
new file mode 100644
index 0000000000000000000000000000000000000000..82c9176cbafaf4009381628d459db10ac32d7f3c
--- /dev/null
+++ b/tests/genericxml2xmlindata/cachetune-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/cachetune-colliding-tunes.xml b/tests/genericxml2xmlindata/cachetune-colliding-tunes.xml
new file mode 100644
index 0000000000000000000000000000000000000000..a0f37028c98cbeebd7b2a57ddfe1f83da9ad1e42
--- /dev/null
+++ b/tests/genericxml2xmlindata/cachetune-colliding-tunes.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/cachetune-colliding-types.xml b/tests/genericxml2xmlindata/cachetune-colliding-types.xml
new file mode 100644
index 0000000000000000000000000000000000000000..c229eccee4b6ad431bdb0aa173cf02941bf08ea7
--- /dev/null
+++ b/tests/genericxml2xmlindata/cachetune-colliding-types.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/cachetune-small.xml b/tests/genericxml2xmlindata/cachetune-small.xml
new file mode 100644
index 0000000000000000000000000000000000000000..ab2d9cf8856a5c2cc4dd1e0c11ed2238d234c65a
--- /dev/null
+++ b/tests/genericxml2xmlindata/cachetune-small.xml
@@ -0,0 +1,29 @@
+
+ QEMUGuest1
+ c7a5fdbd-edaf-9455-926a-d65c16db1809
+ 219136
+ 219136
+ 4
+
+
+
+
+
+
+ hvm
+
+
+
+ destroy
+ restart
+ destroy
+
+ /usr/bin/qemu-system-i686
+
+
+
+
+
+
+
+
diff --git a/tests/genericxml2xmlindata/cachetune.xml b/tests/genericxml2xmlindata/cachetune.xml
new file mode 100644
index 0000000000000000000000000000000000000000..645cab7771cf1214aac195662bccef360ac3ca85
--- /dev/null
+++ b/tests/genericxml2xmlindata/cachetune.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 deaf5bd9b8231b94f4f566ca2fd463f208835245..496e9260fa86684a754ada1d3df3fe35f239a313 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);