render / rpms / libvirt

Forked from rpms/libvirt 10 months ago
Clone
79b470
From 17e9b949ec3876e74bcaa217810afbd46f297a65 Mon Sep 17 00:00:00 2001
79b470
Message-Id: <17e9b949ec3876e74bcaa217810afbd46f297a65@dist-git>
79b470
From: Michal Privoznik <mprivozn@redhat.com>
79b470
Date: Wed, 7 Oct 2020 18:45:39 +0200
79b470
Subject: [PATCH] conf: Parse and format HMAT
79b470
MIME-Version: 1.0
79b470
Content-Type: text/plain; charset=UTF-8
79b470
Content-Transfer-Encoding: 8bit
79b470
79b470
To cite ACPI specification:
79b470
79b470
  Heterogeneous Memory Attribute Table describes the memory
79b470
  attributes, such as memory side cache attributes and bandwidth
79b470
  and latency details, related to the System Physical Address
79b470
  (SPA) Memory Ranges. The software is expected to use this
79b470
  information as hint for optimization.
79b470
79b470
According to our upstream discussion [1] this is exposed under
79b470
<numa/> as <cache/> under NUMA <cell/> and <latency> or
79b470
<bandwidth/> under numa/latencies.
79b470
79b470
1: https://www.redhat.com/archives/libvir-list/2020-January/msg00422.html
79b470
79b470
Signed-off-by: Michal Privoznik <mprivozn@redhat.com>
79b470
Reviewed-by: Daniel Henrique Barboza <danielhb413@gmail.com>
79b470
(cherry picked from commit a89bbbac86383a10be0cec5a93feb7ed820871eb)
79b470
79b470
Resolves: https://bugzilla.redhat.com/show_bug.cgi?id=1749518
79b470
79b470
Conflicts:
79b470
- src/conf/numa_conf.c: Context, because we're not using
79b470
VIR_XPATH_NODE_AUTORESTORE() everywhere in the old code.
79b470
79b470
Signed-off-by: Michal Privoznik <mprivozn@redhat.com>
79b470
Message-Id: <f5d9028ca8eff876c2bd471460629d0ef3b20630.1602087923.git.mprivozn@redhat.com>
79b470
Reviewed-by: Ján Tomko <jtomko@redhat.com>
79b470
---
79b470
 docs/formatdomain.html.in                  | 107 +++++++
79b470
 docs/schemas/cputypes.rng                  | 110 ++++++-
79b470
 src/conf/numa_conf.c                       | 349 ++++++++++++++++++++-
79b470
 src/conf/numa_conf.h                       |  33 ++
79b470
 src/libvirt_private.syms                   |   6 +
79b470
 tests/qemuxml2argvdata/numatune-hmat.xml   |  52 +++
79b470
 tests/qemuxml2xmloutdata/numatune-hmat.xml |   1 +
79b470
 tests/qemuxml2xmltest.c                    |   1 +
79b470
 8 files changed, 644 insertions(+), 15 deletions(-)
79b470
 create mode 100644 tests/qemuxml2argvdata/numatune-hmat.xml
79b470
 create mode 120000 tests/qemuxml2xmloutdata/numatune-hmat.xml
79b470
79b470
diff --git a/docs/formatdomain.html.in b/docs/formatdomain.html.in
79b470
index 4b8d312596..bec753e37f 100644
79b470
--- a/docs/formatdomain.html.in
79b470
+++ b/docs/formatdomain.html.in
79b470
@@ -1874,6 +1874,113 @@
79b470
       using 10 for local and 20 for remote distances.
79b470
     

79b470
 
79b470
+    

ACPI Heterogeneous Memory Attribute Table

79b470
+
79b470
+
79b470
+...
79b470
+<cpu>
79b470
+  ...
79b470
+  <numa>
79b470
+    <cell id='0' cpus='0-3' memory='512000' unit='KiB' discard='yes'/>
79b470
+    <cell id='1' cpus='4-7' memory='512000' unit='KiB' memAccess='shared'/>
79b470
+    <cell id='3' cpus='0-3' memory='2097152' unit='KiB'>
79b470
+      <cache level='1' associativity='direct' policy='writeback'>
79b470
+        <size value='10' unit='KiB'/>
79b470
+        <line value='8' unit='B'/>
79b470
+      </cache>
79b470
+    </cell>
79b470
+    <interconnects>
79b470
+      <latency initiator='0' target='0' type='access' value='5'/>
79b470
+      <latency initiator='0' target='0' cache='1' type='access' value='10'/>
79b470
+      <bandwidth initiator='0' target='0' type='access' value='204800' unit='KiB'/>
79b470
+    </interconnects>
79b470
+  </numa>
79b470
+  ...
79b470
+</cpu>
79b470
+...
79b470
+
79b470
+    

79b470
+      Since 6.6.0 the cell element can
79b470
+      have a cache child element which describes memory side cache
79b470
+      for memory proximity domains. The cache element has a
79b470
+      level attribute describing the cache level and thus the
79b470
+      element can be repeated multiple times to describe different levels of
79b470
+      the cache.
79b470
+    

79b470
+
79b470
+    

79b470
+      The cache element then has following mandatory attributes:
79b470
+    

79b470
+
79b470
+    
79b470
+      
level
79b470
+      
79b470
+        Level of the cache this description refers to.
79b470
+      
79b470
+
79b470
+      
associativity
79b470
+      
79b470
+        Describes cache associativity (accepted values are none,
79b470
+        direct and full).
79b470
+      
79b470
+
79b470
+      
policy
79b470
+      
79b470
+        Describes cache write associativity (accepted values are
79b470
+        none, writeback and
79b470
+        writethrough).
79b470
+      
79b470
+    
79b470
+
79b470
+    

79b470
+      The cache element has two mandatory child elements then:
79b470
+      size and line which describe cache size and
79b470
+      cache line size. Both elements accept two attributes: value
79b470
+      and unit which set the value of corresponding cache
79b470
+      attribute.
79b470
+    

79b470
+
79b470
+    

79b470
+      The NUMA description has an optional interconnects element that
79b470
+      describes the normalized memory read/write latency, read/write bandwidth
79b470
+      between Initiator Proximity Domains (Processor or I/O) and Target
79b470
+      Proximity Domains (Memory).
79b470
+    

79b470
+
79b470
+    

79b470
+      The interconnects element can have zero or more
79b470
+      latency child elements to describe latency between two
79b470
+      memory nodes and zero or more bandwidth child elements to
79b470
+      describe bandwidth between two memory nodes. Both these have the
79b470
+      following mandatory attributes:
79b470
+    

79b470
+
79b470
+    
79b470
+      
initiator
79b470
+      
Refers to the source NUMA node
79b470
+
79b470
+      
target
79b470
+      
Refers to the target NUMA node
79b470
+
79b470
+      
type
79b470
+      
The type of the access. Accepted values: access,
79b470
+      read, write
79b470
+
79b470
+      
value
79b470
+      
The actual value. For latency this is delay in nanoseconds, for
79b470
+      bandwidth this value is in kibibytes per second. Use additional
79b470
+      unit attribute to change the units.
79b470
+    
79b470
+
79b470
+    

79b470
+      To describe latency from one NUMA node to a cache of another NUMA node
79b470
+      the latency element has optional cache
79b470
+      attribute which in combination with target attribute creates
79b470
+      full reference to distant NUMA node's cache level. For instance,
79b470
+      target='0' cache='1' refers to the first level cache of NUMA
79b470
+      node 0.
79b470
+    

79b470
+
79b470
     

Events configuration

79b470
 
79b470
     

79b470
diff --git a/docs/schemas/cputypes.rng b/docs/schemas/cputypes.rng
79b470
index a1682a1003..ba30dbf9ff 100644
79b470
--- a/docs/schemas/cputypes.rng
79b470
+++ b/docs/schemas/cputypes.rng
79b470
@@ -102,9 +102,14 @@
79b470
 
79b470
   <define name="cpuNuma">
79b470
     <element name="numa">
79b470
-      <oneOrMore>
79b470
-        <ref name="numaCell"/>
79b470
-      </oneOrMore>
79b470
+      <interleave>
79b470
+        <oneOrMore>
79b470
+          <ref name="numaCell"/>
79b470
+        </oneOrMore>
79b470
+        <optional>
79b470
+          <ref name="numaInterconnects"/>
79b470
+        </optional>
79b470
+      </interleave>
79b470
     </element>
79b470
   </define>
79b470
 
79b470
@@ -148,6 +153,9 @@
79b470
           </oneOrMore>
79b470
         </element>
79b470
       </optional>
79b470
+      <zeroOrMore>
79b470
+        <ref name="numaCache"/>
79b470
+      </zeroOrMore>
79b470
     </element>
79b470
   </define>
79b470
 
79b470
@@ -162,6 +170,102 @@
79b470
     </element>
79b470
   </define>
79b470
 
79b470
+  <define name="numaCache">
79b470
+    <element name="cache">
79b470
+      <attribute name="level">
79b470
+        <ref name="unsignedInt"/>
79b470
+      </attribute>
79b470
+      <attribute name="associativity">
79b470
+        <choice>
79b470
+          <value>none</value>
79b470
+          <value>direct</value>
79b470
+          <value>full</value>
79b470
+        </choice>
79b470
+      </attribute>
79b470
+      <attribute name="policy">
79b470
+        <choice>
79b470
+          <value>none</value>
79b470
+          <value>writeback</value>
79b470
+          <value>writethrough</value>
79b470
+        </choice>
79b470
+      </attribute>
79b470
+      <interleave>
79b470
+        <element name="size">
79b470
+          <attribute name="value">
79b470
+            <ref name="unsignedInt"/>
79b470
+          </attribute>
79b470
+          <attribute name="unit">
79b470
+            <ref name="unit"/>
79b470
+          </attribute>
79b470
+        </element>
79b470
+        <element name="line">
79b470
+          <attribute name="value">
79b470
+            <ref name="unsignedInt"/>
79b470
+          </attribute>
79b470
+          <attribute name="unit">
79b470
+            <ref name="unit"/>
79b470
+          </attribute>
79b470
+        </element>
79b470
+      </interleave>
79b470
+    </element>
79b470
+  </define>
79b470
+
79b470
+  <define name="numaInterconnects">
79b470
+    <element name="interconnects">
79b470
+      <interleave>
79b470
+        <zeroOrMore>
79b470
+          <element name="latency">
79b470
+            <attribute name="initiator">
79b470
+              <ref name="unsignedInt"/>
79b470
+            </attribute>
79b470
+            <attribute name="target">
79b470
+              <ref name="unsignedInt"/>
79b470
+            </attribute>
79b470
+            <optional>
79b470
+              <attribute name="cache">
79b470
+                <ref name="unsignedInt"/>
79b470
+              </attribute>
79b470
+            </optional>
79b470
+            <attribute name="type">
79b470
+              <choice>
79b470
+                <value>access</value>
79b470
+                <value>read</value>
79b470
+                <value>write</value>
79b470
+              </choice>
79b470
+            </attribute>
79b470
+            <attribute name="value">
79b470
+              <ref name="unsignedInt"/>
79b470
+            </attribute>
79b470
+            <empty/>
79b470
+          </element>
79b470
+        </zeroOrMore>
79b470
+        <zeroOrMore>
79b470
+          <element name="bandwidth">
79b470
+            <attribute name="initiator">
79b470
+              <ref name="unsignedInt"/>
79b470
+            </attribute>
79b470
+            <attribute name="target">
79b470
+              <ref name="unsignedInt"/>
79b470
+            </attribute>
79b470
+            <attribute name="type">
79b470
+              <choice>
79b470
+                <value>access</value>
79b470
+                <value>read</value>
79b470
+                <value>write</value>
79b470
+              </choice>
79b470
+            </attribute>
79b470
+            <attribute name="value">
79b470
+              <ref name="unsignedInt"/>
79b470
+            </attribute>
79b470
+            <attribute name="unit">
79b470
+              <ref name="unit"/>
79b470
+            </attribute>
79b470
+          </element>
79b470
+        </zeroOrMore>
79b470
+      </interleave>
79b470
+    </element>
79b470
+  </define>
79b470
+
79b470
   
79b470
   <define name="memoryKB">
79b470
     <data type="unsignedLong"/>
79b470
diff --git a/src/conf/numa_conf.c b/src/conf/numa_conf.c
79b470
index a805336d16..5c764190c3 100644
79b470
--- a/src/conf/numa_conf.c
79b470
+++ b/src/conf/numa_conf.c
79b470
@@ -59,9 +59,37 @@ VIR_ENUM_IMPL(virDomainMemoryAccess,
79b470
               "private",
79b470
 );
79b470
 
79b470
+VIR_ENUM_IMPL(virDomainCacheAssociativity,
79b470
+              VIR_DOMAIN_CACHE_ASSOCIATIVITY_LAST,
79b470
+              "none",
79b470
+              "direct",
79b470
+              "full",
79b470
+);
79b470
+
79b470
+VIR_ENUM_IMPL(virDomainCachePolicy,
79b470
+              VIR_DOMAIN_CACHE_POLICY_LAST,
79b470
+              "none",
79b470
+              "writeback",
79b470
+              "writethrough",
79b470
+);
79b470
+
79b470
+VIR_ENUM_IMPL(virDomainMemoryLatency,
79b470
+              VIR_DOMAIN_MEMORY_LATENCY_LAST,
79b470
+              "none",
79b470
+              "access",
79b470
+              "read",
79b470
+              "write"
79b470
+);
79b470
+
79b470
 typedef struct _virDomainNumaDistance virDomainNumaDistance;
79b470
 typedef virDomainNumaDistance *virDomainNumaDistancePtr;
79b470
 
79b470
+typedef struct _virDomainNumaCache virDomainNumaCache;
79b470
+typedef virDomainNumaCache *virDomainNumaCachePtr;
79b470
+
79b470
+typedef struct _virDomainNumaInterconnect virDomainNumaInterconnect;
79b470
+typedef virDomainNumaInterconnect *virDomainNumaInterconnectPtr;
79b470
+
79b470
 typedef struct _virDomainNumaNode virDomainNumaNode;
79b470
 typedef virDomainNumaNode *virDomainNumaNodePtr;
79b470
 
79b470
@@ -86,9 +114,30 @@ struct _virDomainNuma {
79b470
             unsigned int cellid;
79b470
         } *distances;           /* remote node distances */
79b470
         size_t ndistances;
79b470
+
79b470
+        struct _virDomainNumaCache {
79b470
+            unsigned int level; /* cache level */
79b470
+            unsigned int size;  /* cache size */
79b470
+            unsigned int line;  /* line size, !!! in bytes !!! */
79b470
+            virDomainCacheAssociativity associativity; /* cache associativity */
79b470
+            virDomainCachePolicy policy; /* cache policy */
79b470
+        } *caches;
79b470
+        size_t ncaches;
79b470
     } *mem_nodes;           /* guest node configuration */
79b470
     size_t nmem_nodes;
79b470
 
79b470
+    struct _virDomainNumaInterconnect {
79b470
+        virDomainNumaInterconnectType type;  /* whether structure describes latency
79b470
+                                                or bandwidth */
79b470
+        unsigned int initiator; /* the initiator NUMA node */
79b470
+        unsigned int target;    /* the target NUMA node */
79b470
+        unsigned int cache;     /* the target cache on @target; if 0 then the
79b470
+                                   memory on @target */
79b470
+        virDomainMemoryLatency accessType;  /* what type of access is defined */
79b470
+        unsigned long value;    /* value itself */
79b470
+    } *interconnects;
79b470
+    size_t ninterconnects;
79b470
+
79b470
     /* Future NUMA tuning related stuff should go here. */
79b470
 };
79b470
 
79b470
@@ -368,9 +417,13 @@ virDomainNumaFree(virDomainNumaPtr numa)
79b470
 
79b470
         if (numa->mem_nodes[i].ndistances > 0)
79b470
             VIR_FREE(numa->mem_nodes[i].distances);
79b470
+
79b470
+        VIR_FREE(numa->mem_nodes[i].caches);
79b470
     }
79b470
     VIR_FREE(numa->mem_nodes);
79b470
 
79b470
+    VIR_FREE(numa->interconnects);
79b470
+
79b470
     VIR_FREE(numa);
79b470
 }
79b470
 
79b470
@@ -841,6 +894,97 @@ virDomainNumaDefNodeDistanceParseXML(virDomainNumaPtr def,
79b470
     return ret;
79b470
 }
79b470
 
79b470
+
79b470
+static int
79b470
+virDomainNumaDefNodeCacheParseXML(virDomainNumaPtr def,
79b470
+                                  xmlXPathContextPtr ctxt,
79b470
+                                  unsigned int cur_cell)
79b470
+{
79b470
+    g_autofree xmlNodePtr *nodes = NULL;
79b470
+    int n;
79b470
+    size_t i;
79b470
+
79b470
+    if ((n = virXPathNodeSet("./cache", ctxt, &nodes)) < 0)
79b470
+        return -1;
79b470
+
79b470
+    def->mem_nodes[cur_cell].caches = g_new0(virDomainNumaCache, n);
79b470
+
79b470
+    for (i = 0; i < n; i++) {
79b470
+        VIR_XPATH_NODE_AUTORESTORE(ctxt);
79b470
+        virDomainNumaCachePtr cache = &def->mem_nodes[cur_cell].caches[i];
79b470
+        g_autofree char *tmp = NULL;
79b470
+        unsigned int level;
79b470
+        int associativity;
79b470
+        int policy;
79b470
+        unsigned long long size;
79b470
+        unsigned long long line;
79b470
+
79b470
+        if (!(tmp = virXMLPropString(nodes[i], "level"))) {
79b470
+            virReportError(VIR_ERR_XML_ERROR,
79b470
+                           _("Missing 'level' attribute in cache "
79b470
+                             "element for NUMA node %d"),
79b470
+                           cur_cell);
79b470
+            return -1;
79b470
+        }
79b470
+
79b470
+        if (virStrToLong_uip(tmp, NULL, 10, &level) < 0 ||
79b470
+            level == 0) {
79b470
+            virReportError(VIR_ERR_XML_ERROR,
79b470
+                           _("Invalid 'level' attribute in cache "
79b470
+                             "element for NUMA node %d"),
79b470
+                           cur_cell);
79b470
+            return -1;
79b470
+        }
79b470
+        VIR_FREE(tmp);
79b470
+
79b470
+        if (!(tmp = virXMLPropString(nodes[i], "associativity"))) {
79b470
+            virReportError(VIR_ERR_XML_ERROR,
79b470
+                           _("Missing 'associativity' attribute in cache "
79b470
+                             "element for NUMA node %d"),
79b470
+                           cur_cell);
79b470
+            return -1;
79b470
+        }
79b470
+
79b470
+        if ((associativity = virDomainCacheAssociativityTypeFromString(tmp)) < 0) {
79b470
+            virReportError(VIR_ERR_XML_ERROR,
79b470
+                           _("Invalid cache associativity '%s'"),
79b470
+                           tmp);
79b470
+            return -1;
79b470
+        }
79b470
+        VIR_FREE(tmp);
79b470
+
79b470
+        if (!(tmp = virXMLPropString(nodes[i], "policy"))) {
79b470
+            virReportError(VIR_ERR_XML_ERROR,
79b470
+                           _("Missing 'policy' attribute in cache "
79b470
+                             "element for NUMA node %d"),
79b470
+                           cur_cell);
79b470
+        }
79b470
+
79b470
+        if ((policy = virDomainCachePolicyTypeFromString(tmp)) < 0) {
79b470
+            virReportError(VIR_ERR_XML_ERROR,
79b470
+                           _("Invalid cache policy '%s'"),
79b470
+                           tmp);
79b470
+            return -1;
79b470
+        }
79b470
+        VIR_FREE(tmp);
79b470
+
79b470
+        ctxt->node = nodes[i];
79b470
+        if (virDomainParseMemory("./size/@value", "./size/unit",
79b470
+                                 ctxt, &size, true, false) < 0)
79b470
+            return -1;
79b470
+
79b470
+        if (virParseScaledValue("./line/@value", "./line/unit",
79b470
+                                ctxt, &line, 1, ULLONG_MAX, true) < 0)
79b470
+            return -1;
79b470
+
79b470
+        *cache = (virDomainNumaCache){level, size, line, associativity, policy};
79b470
+        def->mem_nodes[cur_cell].ncaches++;
79b470
+    }
79b470
+
79b470
+    return 0;
79b470
+}
79b470
+
79b470
+
79b470
 int
79b470
 virDomainNumaDefParseXML(virDomainNumaPtr def,
79b470
                          xmlXPathContextPtr ctxt)
79b470
@@ -867,6 +1011,7 @@ virDomainNumaDefParseXML(virDomainNumaPtr def,
79b470
     def->nmem_nodes = n;
79b470
 
79b470
     for (i = 0; i < n; i++) {
79b470
+        VIR_XPATH_NODE_AUTORESTORE(ctxt);
79b470
         int rc;
79b470
         unsigned int cur_cell = i;
79b470
 
79b470
@@ -953,7 +1098,109 @@ virDomainNumaDefParseXML(virDomainNumaPtr def,
79b470
 
79b470
         /* Parse NUMA distances info */
79b470
         if (virDomainNumaDefNodeDistanceParseXML(def, ctxt, cur_cell) < 0)
79b470
+            goto cleanup;
79b470
+
79b470
+        /* Parse cache info */
79b470
+        if (virDomainNumaDefNodeCacheParseXML(def, ctxt, cur_cell) < 0)
79b470
+            goto cleanup;
79b470
+    }
79b470
+
79b470
+    VIR_FREE(nodes);
79b470
+    if ((n = virXPathNodeSet("./cpu/numa[1]/interconnects[1]/latency|"
79b470
+                             "./cpu/numa[1]/interconnects[1]/bandwidth", ctxt, &nodes)) < 0)
79b470
+        goto cleanup;
79b470
+
79b470
+    def->interconnects = g_new0(virDomainNumaInterconnect, n);
79b470
+    for (i = 0; i < n; i++) {
79b470
+        virDomainNumaInterconnectType type;
79b470
+        unsigned int initiator;
79b470
+        unsigned int target;
79b470
+        unsigned int cache = 0;
79b470
+        int accessType;
79b470
+        unsigned long long value;
79b470
+
79b470
+        if (virXMLNodeNameEqual(nodes[i], "latency")) {
79b470
+            type = VIR_DOMAIN_NUMA_INTERCONNECT_TYPE_LATENCY;
79b470
+
79b470
+            if (!(tmp = virXMLPropString(nodes[i], "value"))) {
79b470
+                virReportError(VIR_ERR_XML_ERROR, "%s",
79b470
+                               _("Missing 'value' attribute in NUMA interconnects"));
79b470
                 goto cleanup;
79b470
+            }
79b470
+
79b470
+            if (virStrToLong_ullp(tmp, NULL, 10, &value) < 0) {
79b470
+                virReportError(VIR_ERR_XML_ERROR, "%s",
79b470
+                               _("Invalid 'value' attribute in NUMA interconnects"));
79b470
+                goto cleanup;
79b470
+            }
79b470
+            VIR_FREE(tmp);
79b470
+        } else if (virXMLNodeNameEqual(nodes[i], "bandwidth")) {
79b470
+            VIR_XPATH_NODE_AUTORESTORE(ctxt);
79b470
+            type = VIR_DOMAIN_NUMA_INTERCONNECT_TYPE_BANDWIDTH;
79b470
+
79b470
+            ctxt->node = nodes[i];
79b470
+
79b470
+            if (virDomainParseMemory("./@value", "./@unit", ctxt, &value, true, false) < 0)
79b470
+                goto cleanup;
79b470
+        } else {
79b470
+            /* Ignore yet unknown child elements. */
79b470
+            continue;
79b470
+        }
79b470
+
79b470
+        if (!(tmp = virXMLPropString(nodes[i], "initiator"))) {
79b470
+            virReportError(VIR_ERR_XML_ERROR, "%s",
79b470
+                           _("Missing 'initiator' attribute in NUMA interconnects"));
79b470
+            goto cleanup;
79b470
+        }
79b470
+
79b470
+        if (virStrToLong_uip(tmp, NULL, 10, &initiator) < 0) {
79b470
+            virReportError(VIR_ERR_XML_ERROR, "%s",
79b470
+                           _("Invalid 'initiator' attribute in NUMA interconnects"));
79b470
+            goto cleanup;
79b470
+        }
79b470
+        VIR_FREE(tmp);
79b470
+
79b470
+        if (!(tmp = virXMLPropString(nodes[i], "target"))) {
79b470
+            virReportError(VIR_ERR_XML_ERROR, "%s",
79b470
+                           _("Missing 'target' attribute in NUMA interconnects"));
79b470
+            goto cleanup;
79b470
+        }
79b470
+
79b470
+        if (virStrToLong_uip(tmp, NULL, 10, &target) < 0) {
79b470
+            virReportError(VIR_ERR_XML_ERROR, "%s",
79b470
+                           _("Invalid 'target' attribute in NUMA interconnects"));
79b470
+            goto cleanup;
79b470
+        }
79b470
+        VIR_FREE(tmp);
79b470
+
79b470
+
79b470
+        /* cache attribute is optional */
79b470
+        if ((tmp = virXMLPropString(nodes[i], "cache"))) {
79b470
+            if (virStrToLong_uip(tmp, NULL, 10, &cache) < 0 ||
79b470
+                cache == 0) {
79b470
+                virReportError(VIR_ERR_XML_ERROR, "%s",
79b470
+                               _("Invalid 'cache' attribute in NUMA interconnects"));
79b470
+                goto cleanup;
79b470
+            }
79b470
+        }
79b470
+        VIR_FREE(tmp);
79b470
+
79b470
+        if (!(tmp = virXMLPropString(nodes[i], "type"))) {
79b470
+            virReportError(VIR_ERR_XML_ERROR, "%s",
79b470
+                           _("Missing 'type' attribute in NUMA interconnects"));
79b470
+            goto cleanup;
79b470
+        }
79b470
+
79b470
+        if ((accessType = virDomainMemoryLatencyTypeFromString(tmp)) <= 0) {
79b470
+            virReportError(VIR_ERR_XML_ERROR, "%s",
79b470
+                           _("Invalid 'type' attribute in NUMA interconnects"));
79b470
+            goto cleanup;
79b470
+        }
79b470
+        VIR_FREE(tmp);
79b470
+
79b470
+        def->interconnects[i] = (virDomainNumaInterconnect) {type, initiator, target,
79b470
+                                                             cache, accessType, value};
79b470
+        def->ninterconnects++;
79b470
     }
79b470
 
79b470
     ret = 0;
79b470
@@ -983,6 +1230,7 @@ virDomainNumaDefFormatXML(virBufferPtr buf,
79b470
     for (i = 0; i < ncells; i++) {
79b470
         virBitmapPtr cpumask = virDomainNumaGetNodeCpumask(def, i);
79b470
         int ndistances;
79b470
+        size_t ncaches;
79b470
 
79b470
         memAccess = virDomainNumaGetNodeMemoryAccessMode(def, i);
79b470
         discard = virDomainNumaGetNodeDiscard(def, i);
79b470
@@ -1009,30 +1257,107 @@ virDomainNumaDefFormatXML(virBufferPtr buf,
79b470
                               virTristateBoolTypeToString(discard));
79b470
 
79b470
         ndistances = def->mem_nodes[i].ndistances;
79b470
-        if (ndistances == 0) {
79b470
+        ncaches = def->mem_nodes[i].ncaches;
79b470
+        if (ndistances == 0 && ncaches == 0) {
79b470
             virBufferAddLit(buf, "/>\n");
79b470
         } else {
79b470
             size_t j;
79b470
-            virDomainNumaDistancePtr distances = def->mem_nodes[i].distances;
79b470
 
79b470
             virBufferAddLit(buf, ">\n");
79b470
             virBufferAdjustIndent(buf, 2);
79b470
-            virBufferAddLit(buf, "<distances>\n");
79b470
-            virBufferAdjustIndent(buf, 2);
79b470
-            for (j = 0; j < ndistances; j++) {
79b470
-                if (distances[j].value) {
79b470
-                    virBufferAddLit(buf, "
79b470
-                    virBufferAsprintf(buf, " id='%d'", distances[j].cellid);
79b470
-                    virBufferAsprintf(buf, " value='%d'", distances[j].value);
79b470
-                    virBufferAddLit(buf, "/>\n");
79b470
+
79b470
+            if (ndistances) {
79b470
+                virDomainNumaDistancePtr distances = def->mem_nodes[i].distances;
79b470
+
79b470
+                virBufferAddLit(buf, "<distances>\n");
79b470
+                virBufferAdjustIndent(buf, 2);
79b470
+                for (j = 0; j < ndistances; j++) {
79b470
+                    if (distances[j].value) {
79b470
+                        virBufferAddLit(buf, "
79b470
+                        virBufferAsprintf(buf, " id='%d'", distances[j].cellid);
79b470
+                        virBufferAsprintf(buf, " value='%d'", distances[j].value);
79b470
+                        virBufferAddLit(buf, "/>\n");
79b470
+                    }
79b470
                 }
79b470
+                virBufferAdjustIndent(buf, -2);
79b470
+                virBufferAddLit(buf, "</distances>\n");
79b470
+            }
79b470
+
79b470
+            for (j = 0; j < ncaches; j++) {
79b470
+                virDomainNumaCachePtr cache = &def->mem_nodes[i].caches[j];
79b470
+
79b470
+                virBufferAsprintf(buf, "<cache level='%u'", cache->level);
79b470
+                if (cache->associativity) {
79b470
+                    virBufferAsprintf(buf, " associativity='%s'",
79b470
+                                      virDomainCacheAssociativityTypeToString(cache->associativity));
79b470
+                }
79b470
+
79b470
+                if (cache->policy) {
79b470
+                    virBufferAsprintf(buf, " policy='%s'",
79b470
+                                      virDomainCachePolicyTypeToString(cache->policy));
79b470
+                }
79b470
+                virBufferAddLit(buf, ">\n");
79b470
+
79b470
+                virBufferAdjustIndent(buf, 2);
79b470
+                virBufferAsprintf(buf,
79b470
+                                  "<size value='%u' unit='KiB'/>\n",
79b470
+                                  cache->size);
79b470
+
79b470
+                if (cache->line) {
79b470
+                    virBufferAsprintf(buf,
79b470
+                                      "<line value='%u' unit='B'/>\n",
79b470
+                                      cache->line);
79b470
+                }
79b470
+
79b470
+                virBufferAdjustIndent(buf, -2);
79b470
+                virBufferAddLit(buf, "</cache>\n");
79b470
             }
79b470
-            virBufferAdjustIndent(buf, -2);
79b470
-            virBufferAddLit(buf, "</distances>\n");
79b470
             virBufferAdjustIndent(buf, -2);
79b470
             virBufferAddLit(buf, "</cell>\n");
79b470
         }
79b470
     }
79b470
+
79b470
+    if (def->ninterconnects) {
79b470
+        virBufferAddLit(buf, "<interconnects>\n");
79b470
+        virBufferAdjustIndent(buf, 2);
79b470
+    }
79b470
+
79b470
+    for (i = 0; i < def->ninterconnects; i++) {
79b470
+        virDomainNumaInterconnectPtr l = &def->interconnects[i];
79b470
+
79b470
+        switch (l->type) {
79b470
+        case VIR_DOMAIN_NUMA_INTERCONNECT_TYPE_LATENCY:
79b470
+            virBufferAddLit(buf, "
79b470
+            break;
79b470
+        case VIR_DOMAIN_NUMA_INTERCONNECT_TYPE_BANDWIDTH:
79b470
+            virBufferAddLit(buf, "
79b470
+        }
79b470
+
79b470
+        virBufferAsprintf(buf,
79b470
+                          " initiator='%u' target='%u'",
79b470
+                          l->initiator, l->target);
79b470
+
79b470
+        if (l->cache > 0) {
79b470
+            virBufferAsprintf(buf,
79b470
+                              " cache='%u'",
79b470
+                              l->cache);
79b470
+        }
79b470
+
79b470
+        virBufferAsprintf(buf,
79b470
+                          " type='%s' value='%lu'",
79b470
+                          virDomainMemoryLatencyTypeToString(l->accessType),
79b470
+                          l->value);
79b470
+
79b470
+        if (l->type == VIR_DOMAIN_NUMA_INTERCONNECT_TYPE_BANDWIDTH)
79b470
+            virBufferAddLit(buf, " unit='KiB'");
79b470
+        virBufferAddLit(buf, "/>\n");
79b470
+    }
79b470
+
79b470
+    if (def->ninterconnects) {
79b470
+        virBufferAdjustIndent(buf, -2);
79b470
+        virBufferAddLit(buf, "</interconnects>\n");
79b470
+    }
79b470
+
79b470
     virBufferAdjustIndent(buf, -2);
79b470
     virBufferAddLit(buf, "</numa>\n");
79b470
 
79b470
diff --git a/src/conf/numa_conf.h b/src/conf/numa_conf.h
79b470
index 6808439a7c..5043c5a6d4 100644
79b470
--- a/src/conf/numa_conf.h
79b470
+++ b/src/conf/numa_conf.h
79b470
@@ -52,6 +52,39 @@ typedef enum {
79b470
 } virDomainMemoryAccess;
79b470
 VIR_ENUM_DECL(virDomainMemoryAccess);
79b470
 
79b470
+typedef enum {
79b470
+    VIR_DOMAIN_CACHE_ASSOCIATIVITY_NONE,    /* No associativity */
79b470
+    VIR_DOMAIN_CACHE_ASSOCIATIVITY_DIRECT,  /* Direct mapped cache */
79b470
+    VIR_DOMAIN_CACHE_ASSOCIATIVITY_FULL,    /* Fully associative cache */
79b470
+
79b470
+    VIR_DOMAIN_CACHE_ASSOCIATIVITY_LAST
79b470
+} virDomainCacheAssociativity;
79b470
+VIR_ENUM_DECL(virDomainCacheAssociativity);
79b470
+
79b470
+typedef enum {
79b470
+    VIR_DOMAIN_CACHE_POLICY_NONE,           /* No policy */
79b470
+    VIR_DOMAIN_CACHE_POLICY_WRITEBACK,      /* Write-back policy */
79b470
+    VIR_DOMAIN_CACHE_POLICY_WRITETHROUGH,   /* Write-through policy */
79b470
+
79b470
+    VIR_DOMAIN_CACHE_POLICY_LAST
79b470
+} virDomainCachePolicy;
79b470
+VIR_ENUM_DECL(virDomainCachePolicy);
79b470
+
79b470
+typedef enum {
79b470
+    VIR_DOMAIN_NUMA_INTERCONNECT_TYPE_LATENCY,
79b470
+    VIR_DOMAIN_NUMA_INTERCONNECT_TYPE_BANDWIDTH,
79b470
+} virDomainNumaInterconnectType;
79b470
+
79b470
+typedef enum {
79b470
+    VIR_DOMAIN_MEMORY_LATENCY_NONE = 0, /* No memory latency defined */
79b470
+    VIR_DOMAIN_MEMORY_LATENCY_ACCESS,   /* Access latency */
79b470
+    VIR_DOMAIN_MEMORY_LATENCY_READ,     /* Read latency */
79b470
+    VIR_DOMAIN_MEMORY_LATENCY_WRITE,    /* Write latency */
79b470
+
79b470
+    VIR_DOMAIN_MEMORY_LATENCY_LAST
79b470
+} virDomainMemoryLatency;
79b470
+VIR_ENUM_DECL(virDomainMemoryLatency);
79b470
+
79b470
 
79b470
 virDomainNumaPtr virDomainNumaNew(void);
79b470
 void virDomainNumaFree(virDomainNumaPtr numa);
79b470
diff --git a/src/libvirt_private.syms b/src/libvirt_private.syms
79b470
index acb25eb8c8..de95e3b116 100644
79b470
--- a/src/libvirt_private.syms
79b470
+++ b/src/libvirt_private.syms
79b470
@@ -808,8 +808,14 @@ virNodeDeviceDeleteVport;
79b470
 virNodeDeviceGetParentName;
79b470
 
79b470
 # conf/numa_conf.h
79b470
+virDomainCacheAssociativityTypeFromString;
79b470
+virDomainCacheAssociativityTypeToString;
79b470
+virDomainCachePolicyTypeFromString;
79b470
+virDomainCachePolicyTypeToString;
79b470
 virDomainMemoryAccessTypeFromString;
79b470
 virDomainMemoryAccessTypeToString;
79b470
+virDomainMemoryLatencyTypeFromString;
79b470
+virDomainMemoryLatencyTypeToString;
79b470
 virDomainNumaCheckABIStability;
79b470
 virDomainNumaEquals;
79b470
 virDomainNumaFree;
79b470
diff --git a/tests/qemuxml2argvdata/numatune-hmat.xml b/tests/qemuxml2argvdata/numatune-hmat.xml
79b470
new file mode 100644
79b470
index 0000000000..83f0b56c9b
79b470
--- /dev/null
79b470
+++ b/tests/qemuxml2argvdata/numatune-hmat.xml
79b470
@@ -0,0 +1,52 @@
79b470
+<domain type='qemu'>
79b470
+  <name>QEMUGuest</name>
79b470
+  <uuid>c7a5fdb2-cdaf-9455-926a-d65c16db1809</uuid>
79b470
+  <memory unit='KiB'>8388608</memory>
79b470
+  <currentMemory unit='KiB'>8388608</currentMemory>
79b470
+  <vcpu placement='static'>12</vcpu>
79b470
+  <os>
79b470
+    <type arch='x86_64' machine='pc'>hvm</type>
79b470
+    <boot dev='hd'/>
79b470
+  </os>
79b470
+  <features>
79b470
+    <acpi/>
79b470
+    <apic/>
79b470
+    <pae/>
79b470
+  </features>
79b470
+  <cpu>
79b470
+    <numa>
79b470
+      <cell id='0' cpus='0-3' memory='2097152' unit='KiB'>
79b470
+        <cache level='1' associativity='direct' policy='writeback'>
79b470
+          <size value='10' unit='KiB'/>
79b470
+          <line value='8' unit='B'/>
79b470
+        </cache>
79b470
+      </cell>
79b470
+      <cell id='1' cpus='4-7' memory='2097152' unit='KiB'/>
79b470
+      <cell id='2' cpus='8-11' memory='2097152' unit='KiB'/>
79b470
+      <cell id='3' memory='2097152' unit='KiB'/>
79b470
+      <cell id='4' memory='2097152' unit='KiB'/>
79b470
+      <cell id='5' memory='2097152' unit='KiB'/>
79b470
+      <interconnects>
79b470
+        <latency initiator='0' target='0' type='access' value='5'/>
79b470
+        <latency initiator='0' target='0' cache='1' type='access' value='10'/>
79b470
+        <bandwidth initiator='0' target='0' type='access' value='204800' unit='KiB'/>
79b470
+      </interconnects>
79b470
+    </numa>
79b470
+  </cpu>
79b470
+  <clock offset='utc'/>
79b470
+  <on_poweroff>destroy</on_poweroff>
79b470
+  <on_reboot>restart</on_reboot>
79b470
+  <on_crash>restart</on_crash>
79b470
+  <devices>
79b470
+    <emulator>/usr/bin/qemu-system-x86_64</emulator>
79b470
+    <controller type='usb' index='0'>
79b470
+      <address type='pci' domain='0x0000' bus='0x00' slot='0x01' function='0x2'/>
79b470
+    </controller>
79b470
+    <controller type='pci' index='0' model='pci-root'/>
79b470
+    <input type='mouse' bus='ps2'/>
79b470
+    <input type='keyboard' bus='ps2'/>
79b470
+    <memballoon model='virtio'>
79b470
+      <address type='pci' domain='0x0000' bus='0x00' slot='0x03' function='0x0'/>
79b470
+    </memballoon>
79b470
+  </devices>
79b470
+</domain>
79b470
diff --git a/tests/qemuxml2xmloutdata/numatune-hmat.xml b/tests/qemuxml2xmloutdata/numatune-hmat.xml
79b470
new file mode 120000
79b470
index 0000000000..6903a80ab1
79b470
--- /dev/null
79b470
+++ b/tests/qemuxml2xmloutdata/numatune-hmat.xml
79b470
@@ -0,0 +1 @@
79b470
+../qemuxml2argvdata/numatune-hmat.xml
79b470
\ No newline at end of file
79b470
diff --git a/tests/qemuxml2xmltest.c b/tests/qemuxml2xmltest.c
79b470
index 1ddeba30f0..de1d720e1d 100644
79b470
--- a/tests/qemuxml2xmltest.c
79b470
+++ b/tests/qemuxml2xmltest.c
79b470
@@ -1106,6 +1106,7 @@ mymain(void)
79b470
     DO_TEST("numatune-memnode-no-memory", QEMU_CAPS_OBJECT_MEMORY_FILE);
79b470
     DO_TEST("numatune-distances", QEMU_CAPS_NUMA, QEMU_CAPS_NUMA_DIST);
79b470
     DO_TEST("numatune-no-vcpu", QEMU_CAPS_NUMA);
79b470
+    DO_TEST("numatune-hmat", NONE);
79b470
 
79b470
     DO_TEST("bios-nvram", NONE);
79b470
     DO_TEST("bios-nvram-os-interleave", NONE);
79b470
-- 
79b470
2.29.2
79b470