using 10 for local and 20 for remote distances.
</p>
+ <h4><a id="hmat">ACPI Heterogeneous Memory Attribute Table</a></h4>
+
+<pre>
+...
+<cpu>
+ ...
+ <numa>
+ <cell id='0' cpus='0-3' memory='512000' unit='KiB' discard='yes'/>
+ <cell id='1' cpus='4-7' memory='512000' unit='KiB' memAccess='shared'/>
+ <cell id='3' cpus='0-3' memory='2097152' unit='KiB'>
+ <cache level='1' associativity='direct' policy='writeback'>
+ <size value='10' unit='KiB'/>
+ <line value='8' unit='B'/>
+ </cache>
+ </cell>
+ <interconnects>
+ <latency initiator='0' target='0' type='access' value='5'/>
+ <latency initiator='0' target='0' cache='1' type='access' value='10'/>
+ <bandwidth initiator='0' target='0' type='access' value='204800' unit='KiB'/>
+ </interconnects>
+ </numa>
+ ...
+</cpu>
+...</pre>
+
+ <p>
+ <span class='since'>Since 6.6.0</span> the <code>cell</code> element can
+ have a <code>cache</code> child element which describes memory side cache
+ for memory proximity domains. The <code>cache</code> element has a
+ <code>level</code> attribute describing the cache level and thus the
+ element can be repeated multiple times to describe different levels of
+ the cache.
+ </p>
+
+ <p>
+ The <code>cache</code> element then has following mandatory attributes:
+ </p>
+
+ <dl>
+ <dt><code>level</code></dt>
+ <dd>
+ Level of the cache this description refers to.
+ </dd>
+
+ <dt><code>associativity</code></dt>
+ <dd>
+ Describes cache associativity (accepted values are <code>none</code>,
+ <code>direct</code> and <code>full</code>).
+ </dd>
+
+ <dt><code>policy</code></dt>
+ <dd>
+ Describes cache write associativity (accepted values are
+ <code>none</code>, <code>writeback</code> and
+ <code>writethrough</code>).
+ </dd>
+ </dl>
+
+ <p>
+ The <code>cache</code> element has two mandatory child elements then:
+ <code>size</code> and <code>line</code> which describe cache size and
+ cache line size. Both elements accept two attributes: <code>value</code>
+ and <code>unit</code> which set the value of corresponding cache
+ attribute.
+ </p>
+
+ <p>
+ The NUMA description has an optional <code>interconnects</code> element that
+ describes the normalized memory read/write latency, read/write bandwidth
+ between Initiator Proximity Domains (Processor or I/O) and Target
+ Proximity Domains (Memory).
+ </p>
+
+ <p>
+ The <code>interconnects</code> element can have zero or more
+ <code>latency</code> child elements to describe latency between two
+ memory nodes and zero or more <code>bandwidth</code> child elements to
+ describe bandwidth between two memory nodes. Both these have the
+ following mandatory attributes:
+ </p>
+
+ <dl>
+ <dt><code>initiator</code></dt>
+ <dd>Refers to the source NUMA node</dd>
+
+ <dt><code>target</code></dt>
+ <dd>Refers to the target NUMA node</dd>
+
+ <dt><code>type</code></dt>
+ <dd>The type of the access. Accepted values: <code>access</code>,
+ <code>read</code>, <code>write</code></dd>
+
+ <dt><code>value</code></dt>
+ <dd>The actual value. For latency this is delay in nanoseconds, for
+ bandwidth this value is in kibibytes per second. Use additional
+ <code>unit</code> attribute to change the units.</dd>
+ </dl>
+
+ <p>
+ To describe latency from one NUMA node to a cache of another NUMA node
+ the <code>latency</code> element has optional <code>cache</code>
+ attribute which in combination with <code>target</code> attribute creates
+ full reference to distant NUMA node's cache level. For instance,
+ <code>target='0' cache='1'</code> refers to the first level cache of NUMA
+ node 0.
+ </p>
+
<h3><a id="elementsEvents">Events configuration</a></h3>
<p>
"private",
);
+VIR_ENUM_IMPL(virDomainCacheAssociativity,
+ VIR_DOMAIN_CACHE_ASSOCIATIVITY_LAST,
+ "none",
+ "direct",
+ "full",
+);
+
+VIR_ENUM_IMPL(virDomainCachePolicy,
+ VIR_DOMAIN_CACHE_POLICY_LAST,
+ "none",
+ "writeback",
+ "writethrough",
+);
+
+VIR_ENUM_IMPL(virDomainMemoryLatency,
+ VIR_DOMAIN_MEMORY_LATENCY_LAST,
+ "none",
+ "access",
+ "read",
+ "write"
+);
+
typedef struct _virDomainNumaDistance virDomainNumaDistance;
typedef virDomainNumaDistance *virDomainNumaDistancePtr;
+typedef struct _virDomainNumaCache virDomainNumaCache;
+typedef virDomainNumaCache *virDomainNumaCachePtr;
+
+typedef struct _virDomainNumaInterconnect virDomainNumaInterconnect;
+typedef virDomainNumaInterconnect *virDomainNumaInterconnectPtr;
+
typedef struct _virDomainNumaNode virDomainNumaNode;
typedef virDomainNumaNode *virDomainNumaNodePtr;
unsigned int cellid;
} *distances; /* remote node distances */
size_t ndistances;
+
+ struct _virDomainNumaCache {
+ unsigned int level; /* cache level */
+ unsigned int size; /* cache size */
+ unsigned int line; /* line size, !!! in bytes !!! */
+ virDomainCacheAssociativity associativity; /* cache associativity */
+ virDomainCachePolicy policy; /* cache policy */
+ } *caches;
+ size_t ncaches;
} *mem_nodes; /* guest node configuration */
size_t nmem_nodes;
+ struct _virDomainNumaInterconnect {
+ virDomainNumaInterconnectType type; /* whether structure describes latency
+ or bandwidth */
+ unsigned int initiator; /* the initiator NUMA node */
+ unsigned int target; /* the target NUMA node */
+ unsigned int cache; /* the target cache on @target; if 0 then the
+ memory on @target */
+ virDomainMemoryLatency accessType; /* what type of access is defined */
+ unsigned long value; /* value itself */
+ } *interconnects;
+ size_t ninterconnects;
+
/* Future NUMA tuning related stuff should go here. */
};
if (numa->mem_nodes[i].ndistances > 0)
VIR_FREE(numa->mem_nodes[i].distances);
+
+ VIR_FREE(numa->mem_nodes[i].caches);
}
VIR_FREE(numa->mem_nodes);
+ VIR_FREE(numa->interconnects);
+
VIR_FREE(numa);
}
return ret;
}
+
+static int
+virDomainNumaDefNodeCacheParseXML(virDomainNumaPtr def,
+ xmlXPathContextPtr ctxt,
+ unsigned int cur_cell)
+{
+ g_autofree xmlNodePtr *nodes = NULL;
+ int n;
+ size_t i;
+
+ if ((n = virXPathNodeSet("./cache", ctxt, &nodes)) < 0)
+ return -1;
+
+ def->mem_nodes[cur_cell].caches = g_new0(virDomainNumaCache, n);
+
+ for (i = 0; i < n; i++) {
+ VIR_XPATH_NODE_AUTORESTORE(ctxt);
+ virDomainNumaCachePtr cache = &def->mem_nodes[cur_cell].caches[i];
+ g_autofree char *tmp = NULL;
+ unsigned int level;
+ int associativity;
+ int policy;
+ unsigned long long size;
+ unsigned long long line;
+
+ if (!(tmp = virXMLPropString(nodes[i], "level"))) {
+ virReportError(VIR_ERR_XML_ERROR,
+ _("Missing 'level' attribute in cache "
+ "element for NUMA node %d"),
+ cur_cell);
+ return -1;
+ }
+
+ if (virStrToLong_uip(tmp, NULL, 10, &level) < 0 ||
+ level == 0) {
+ virReportError(VIR_ERR_XML_ERROR,
+ _("Invalid 'level' attribute in cache "
+ "element for NUMA node %d"),
+ cur_cell);
+ return -1;
+ }
+ VIR_FREE(tmp);
+
+ if (!(tmp = virXMLPropString(nodes[i], "associativity"))) {
+ virReportError(VIR_ERR_XML_ERROR,
+ _("Missing 'associativity' attribute in cache "
+ "element for NUMA node %d"),
+ cur_cell);
+ return -1;
+ }
+
+ if ((associativity = virDomainCacheAssociativityTypeFromString(tmp)) < 0) {
+ virReportError(VIR_ERR_XML_ERROR,
+ _("Invalid cache associativity '%s'"),
+ tmp);
+ return -1;
+ }
+ VIR_FREE(tmp);
+
+ if (!(tmp = virXMLPropString(nodes[i], "policy"))) {
+ virReportError(VIR_ERR_XML_ERROR,
+ _("Missing 'policy' attribute in cache "
+ "element for NUMA node %d"),
+ cur_cell);
+ }
+
+ if ((policy = virDomainCachePolicyTypeFromString(tmp)) < 0) {
+ virReportError(VIR_ERR_XML_ERROR,
+ _("Invalid cache policy '%s'"),
+ tmp);
+ return -1;
+ }
+ VIR_FREE(tmp);
+
+ ctxt->node = nodes[i];
+ if (virDomainParseMemory("./size/@value", "./size/unit",
+ ctxt, &size, true, false) < 0)
+ return -1;
+
+ if (virParseScaledValue("./line/@value", "./line/unit",
+ ctxt, &line, 1, ULLONG_MAX, true) < 0)
+ return -1;
+
+ *cache = (virDomainNumaCache){level, size, line, associativity, policy};
+ def->mem_nodes[cur_cell].ncaches++;
+ }
+
+ return 0;
+}
+
+
int
virDomainNumaDefParseXML(virDomainNumaPtr def,
xmlXPathContextPtr ctxt)
{
xmlNodePtr *nodes = NULL;
- VIR_XPATH_NODE_AUTORESTORE(ctxt);
char *tmp = NULL;
int n;
size_t i, j;
def->nmem_nodes = n;
for (i = 0; i < n; i++) {
+ VIR_XPATH_NODE_AUTORESTORE(ctxt);
int rc;
unsigned int cur_cell = i;
/* Parse NUMA distances info */
if (virDomainNumaDefNodeDistanceParseXML(def, ctxt, cur_cell) < 0)
+ goto cleanup;
+
+ /* Parse cache info */
+ if (virDomainNumaDefNodeCacheParseXML(def, ctxt, cur_cell) < 0)
+ goto cleanup;
+ }
+
+ VIR_FREE(nodes);
+ if ((n = virXPathNodeSet("./cpu/numa[1]/interconnects[1]/latency|"
+ "./cpu/numa[1]/interconnects[1]/bandwidth", ctxt, &nodes)) < 0)
+ goto cleanup;
+
+ def->interconnects = g_new0(virDomainNumaInterconnect, n);
+ for (i = 0; i < n; i++) {
+ virDomainNumaInterconnectType type;
+ unsigned int initiator;
+ unsigned int target;
+ unsigned int cache = 0;
+ int accessType;
+ unsigned long long value;
+
+ if (virXMLNodeNameEqual(nodes[i], "latency")) {
+ type = VIR_DOMAIN_NUMA_INTERCONNECT_TYPE_LATENCY;
+
+ if (!(tmp = virXMLPropString(nodes[i], "value"))) {
+ virReportError(VIR_ERR_XML_ERROR, "%s",
+ _("Missing 'value' attribute in NUMA interconnects"));
goto cleanup;
+ }
+
+ if (virStrToLong_ullp(tmp, NULL, 10, &value) < 0) {
+ virReportError(VIR_ERR_XML_ERROR, "%s",
+ _("Invalid 'value' attribute in NUMA interconnects"));
+ goto cleanup;
+ }
+ VIR_FREE(tmp);
+ } else if (virXMLNodeNameEqual(nodes[i], "bandwidth")) {
+ VIR_XPATH_NODE_AUTORESTORE(ctxt);
+ type = VIR_DOMAIN_NUMA_INTERCONNECT_TYPE_BANDWIDTH;
+
+ ctxt->node = nodes[i];
+
+ if (virDomainParseMemory("./@value", "./@unit", ctxt, &value, true, false) < 0)
+ goto cleanup;
+ } else {
+ /* Ignore yet unknown child elements. */
+ continue;
+ }
+
+ if (!(tmp = virXMLPropString(nodes[i], "initiator"))) {
+ virReportError(VIR_ERR_XML_ERROR, "%s",
+ _("Missing 'initiator' attribute in NUMA interconnects"));
+ goto cleanup;
+ }
+
+ if (virStrToLong_uip(tmp, NULL, 10, &initiator) < 0) {
+ virReportError(VIR_ERR_XML_ERROR, "%s",
+ _("Invalid 'initiator' attribute in NUMA interconnects"));
+ goto cleanup;
+ }
+ VIR_FREE(tmp);
+
+ if (!(tmp = virXMLPropString(nodes[i], "target"))) {
+ virReportError(VIR_ERR_XML_ERROR, "%s",
+ _("Missing 'target' attribute in NUMA interconnects"));
+ goto cleanup;
+ }
+
+ if (virStrToLong_uip(tmp, NULL, 10, &target) < 0) {
+ virReportError(VIR_ERR_XML_ERROR, "%s",
+ _("Invalid 'target' attribute in NUMA interconnects"));
+ goto cleanup;
+ }
+ VIR_FREE(tmp);
+
+
+ /* cache attribute is optional */
+ if ((tmp = virXMLPropString(nodes[i], "cache"))) {
+ if (virStrToLong_uip(tmp, NULL, 10, &cache) < 0 ||
+ cache == 0) {
+ virReportError(VIR_ERR_XML_ERROR, "%s",
+ _("Invalid 'cache' attribute in NUMA interconnects"));
+ goto cleanup;
+ }
+ }
+ VIR_FREE(tmp);
+
+ if (!(tmp = virXMLPropString(nodes[i], "type"))) {
+ virReportError(VIR_ERR_XML_ERROR, "%s",
+ _("Missing 'type' attribute in NUMA interconnects"));
+ goto cleanup;
+ }
+
+ if ((accessType = virDomainMemoryLatencyTypeFromString(tmp)) <= 0) {
+ virReportError(VIR_ERR_XML_ERROR, "%s",
+ _("Invalid 'type' attribute in NUMA interconnects"));
+ goto cleanup;
+ }
+ VIR_FREE(tmp);
+
+ def->interconnects[i] = (virDomainNumaInterconnect) {type, initiator, target,
+ cache, accessType, value};
+ def->ninterconnects++;
}
ret = 0;
for (i = 0; i < ncells; i++) {
virBitmapPtr cpumask = virDomainNumaGetNodeCpumask(def, i);
int ndistances;
+ size_t ncaches;
memAccess = virDomainNumaGetNodeMemoryAccessMode(def, i);
discard = virDomainNumaGetNodeDiscard(def, i);
virTristateBoolTypeToString(discard));
ndistances = def->mem_nodes[i].ndistances;
- if (ndistances == 0) {
+ ncaches = def->mem_nodes[i].ncaches;
+ if (ndistances == 0 && ncaches == 0) {
virBufferAddLit(buf, "/>\n");
} else {
size_t j;
- virDomainNumaDistancePtr distances = def->mem_nodes[i].distances;
virBufferAddLit(buf, ">\n");
virBufferAdjustIndent(buf, 2);
- virBufferAddLit(buf, "<distances>\n");
- virBufferAdjustIndent(buf, 2);
- for (j = 0; j < ndistances; j++) {
- if (distances[j].value) {
- virBufferAddLit(buf, "<sibling");
- virBufferAsprintf(buf, " id='%d'", distances[j].cellid);
- virBufferAsprintf(buf, " value='%d'", distances[j].value);
- virBufferAddLit(buf, "/>\n");
+
+ if (ndistances) {
+ virDomainNumaDistancePtr distances = def->mem_nodes[i].distances;
+
+ virBufferAddLit(buf, "<distances>\n");
+ virBufferAdjustIndent(buf, 2);
+ for (j = 0; j < ndistances; j++) {
+ if (distances[j].value) {
+ virBufferAddLit(buf, "<sibling");
+ virBufferAsprintf(buf, " id='%d'", distances[j].cellid);
+ virBufferAsprintf(buf, " value='%d'", distances[j].value);
+ virBufferAddLit(buf, "/>\n");
+ }
}
+ virBufferAdjustIndent(buf, -2);
+ virBufferAddLit(buf, "</distances>\n");
+ }
+
+ for (j = 0; j < ncaches; j++) {
+ virDomainNumaCachePtr cache = &def->mem_nodes[i].caches[j];
+
+ virBufferAsprintf(buf, "<cache level='%u'", cache->level);
+ if (cache->associativity) {
+ virBufferAsprintf(buf, " associativity='%s'",
+ virDomainCacheAssociativityTypeToString(cache->associativity));
+ }
+
+ if (cache->policy) {
+ virBufferAsprintf(buf, " policy='%s'",
+ virDomainCachePolicyTypeToString(cache->policy));
+ }
+ virBufferAddLit(buf, ">\n");
+
+ virBufferAdjustIndent(buf, 2);
+ virBufferAsprintf(buf,
+ "<size value='%u' unit='KiB'/>\n",
+ cache->size);
+
+ if (cache->line) {
+ virBufferAsprintf(buf,
+ "<line value='%u' unit='B'/>\n",
+ cache->line);
+ }
+
+ virBufferAdjustIndent(buf, -2);
+ virBufferAddLit(buf, "</cache>\n");
}
- virBufferAdjustIndent(buf, -2);
- virBufferAddLit(buf, "</distances>\n");
virBufferAdjustIndent(buf, -2);
virBufferAddLit(buf, "</cell>\n");
}
}
+
+ if (def->ninterconnects) {
+ virBufferAddLit(buf, "<interconnects>\n");
+ virBufferAdjustIndent(buf, 2);
+ }
+
+ for (i = 0; i < def->ninterconnects; i++) {
+ virDomainNumaInterconnectPtr l = &def->interconnects[i];
+
+ switch (l->type) {
+ case VIR_DOMAIN_NUMA_INTERCONNECT_TYPE_LATENCY:
+ virBufferAddLit(buf, "<latency");
+ break;
+ case VIR_DOMAIN_NUMA_INTERCONNECT_TYPE_BANDWIDTH:
+ virBufferAddLit(buf, "<bandwidth");
+ }
+
+ virBufferAsprintf(buf,
+ " initiator='%u' target='%u'",
+ l->initiator, l->target);
+
+ if (l->cache > 0) {
+ virBufferAsprintf(buf,
+ " cache='%u'",
+ l->cache);
+ }
+
+ virBufferAsprintf(buf,
+ " type='%s' value='%lu'",
+ virDomainMemoryLatencyTypeToString(l->accessType),
+ l->value);
+
+ if (l->type == VIR_DOMAIN_NUMA_INTERCONNECT_TYPE_BANDWIDTH)
+ virBufferAddLit(buf, " unit='KiB'");
+ virBufferAddLit(buf, "/>\n");
+ }
+
+ if (def->ninterconnects) {
+ virBufferAdjustIndent(buf, -2);
+ virBufferAddLit(buf, "</interconnects>\n");
+ }
+
virBufferAdjustIndent(buf, -2);
virBufferAddLit(buf, "</numa>\n");