diff --git a/docs/schemas/capability.rng b/docs/schemas/capability.rng
index fcc262ae1a..5c1fb3607c 100644
--- a/docs/schemas/capability.rng
+++ b/docs/schemas/capability.rng
@@ -163,6 +163,10 @@
+
+
+
+
diff --git a/src/conf/capabilities.c b/src/conf/capabilities.c
index c825cb0503..891dadfa44 100644
--- a/src/conf/capabilities.c
+++ b/src/conf/capabilities.c
@@ -121,6 +121,8 @@ virCapabilitiesFreeHostNUMACell(virCapsHostNUMACell *cell)
g_free(cell->cpus);
g_free(cell->distances);
g_free(cell->pageinfo);
+ if (cell->caches)
+ g_array_unref(cell->caches);
g_free(cell);
}
@@ -335,6 +337,7 @@ virCapabilitiesSetNetPrefix(virCaps *caps,
* @distances: NUMA distances to other nodes
* @npageinfo: number of pages at node @num
* @pageinfo: info on each single memory page
+ * @caches: info on memory side caches
*
* Registers a new NUMA cell for a host, passing in a array of
* CPU IDs belonging to the cell, distances to other NUMA nodes
@@ -351,7 +354,8 @@ virCapabilitiesHostNUMAAddCell(virCapsHostNUMA *caps,
int ndistances,
virNumaDistance **distances,
int npageinfo,
- virCapsHostNUMACellPageInfo **pageinfo)
+ virCapsHostNUMACellPageInfo **pageinfo,
+ GArray **caches)
{
virCapsHostNUMACell *cell = g_new0(virCapsHostNUMACell, 1);
@@ -369,6 +373,9 @@ virCapabilitiesHostNUMAAddCell(virCapsHostNUMA *caps,
cell->npageinfo = npageinfo;
cell->pageinfo = g_steal_pointer(pageinfo);
}
+ if (caches) {
+ cell->caches = g_steal_pointer(caches);
+ }
g_ptr_array_add(caps->cells, cell);
}
@@ -870,6 +877,11 @@ virCapabilitiesHostNUMAFormat(virBuffer *buf,
virNumaDistanceFormat(buf, cell->distances, cell->ndistances);
+ if (cell->caches) {
+ virNumaCache *caches = &g_array_index(cell->caches, virNumaCache, 0);
+ virNumaCacheFormat(buf, caches, cell->caches->len);
+ }
+
if (virCapsHostNUMACellCPUFormat(buf, cell->cpus, cell->ncpus) < 0)
return -1;
@@ -1535,6 +1547,129 @@ virCapabilitiesGetNUMAPagesInfo(int node,
}
+static int
+virCapabilitiesGetNodeCacheReadFile(const char *prefix,
+ const char *dir,
+ const char *file,
+ unsigned int *value)
+{
+ g_autofree char *path = g_build_filename(prefix, dir, file, NULL);
+ int rv = virFileReadValueUint(value, "%s", path);
+
+ if (rv < 0) {
+ if (rv == -2) {
+ virReportError(VIR_ERR_INTERNAL_ERROR,
+ _("File '%s' does not exist"),
+ path);
+ }
+ return -1;
+ }
+
+ return 0;
+}
+
+
+static int
+virCapsHostNUMACellCacheComparator(const void *a,
+ const void *b)
+{
+ const virNumaCache *aa = a;
+ const virNumaCache *bb = b;
+
+ return aa->level - bb->level;
+}
+
+
+static int
+virCapabilitiesGetNodeCache(int node,
+ GArray **cachesRet)
+{
+ g_autoptr(DIR) dir = NULL;
+ int direrr = 0;
+ struct dirent *entry;
+ g_autofree char *path = NULL;
+ g_autoptr(GArray) caches = g_array_new(FALSE, FALSE, sizeof(virNumaCache));
+
+ path = g_strdup_printf(SYSFS_SYSTEM_PATH "/node/node%d/memory_side_cache", node);
+
+ if (virDirOpenIfExists(&dir, path) < 0)
+ return -1;
+
+ while (dir && (direrr = virDirRead(dir, &entry, path)) > 0) {
+ const char *dname = STRSKIP(entry->d_name, "index");
+ virNumaCache cache = { 0 };
+ unsigned int indexing;
+ unsigned int write_policy;
+
+ if (!dname)
+ continue;
+
+ if (virStrToLong_ui(dname, NULL, 10, &cache.level) < 0) {
+ virReportError(VIR_ERR_INTERNAL_ERROR,
+ _("unable to parse %s"),
+ entry->d_name);
+ return -1;
+ }
+
+ if (virCapabilitiesGetNodeCacheReadFile(path, entry->d_name,
+ "size", &cache.size) < 0)
+ return -1;
+
+ cache.size >>= 10; /* read in bytes but stored in kibibytes */
+
+ if (virCapabilitiesGetNodeCacheReadFile(path, entry->d_name,
+ "line_size", &cache.line) < 0)
+ return -1;
+
+ if (virCapabilitiesGetNodeCacheReadFile(path, entry->d_name,
+ "indexing", &indexing) < 0)
+ return -1;
+
+ /* see enum cache_indexing in kernel */
+ switch (indexing) {
+ case 0: cache.associativity = VIR_NUMA_CACHE_ASSOCIATIVITY_DIRECT; break;
+ case 1: cache.associativity = VIR_NUMA_CACHE_ASSOCIATIVITY_FULL; break;
+ case 2: cache.associativity = VIR_NUMA_CACHE_ASSOCIATIVITY_NONE; break;
+ default:
+ virReportError(VIR_ERR_INTERNAL_ERROR,
+ _("unknown indexing value '%u'"),
+ indexing);
+ return -1;
+ }
+
+ if (virCapabilitiesGetNodeCacheReadFile(path, entry->d_name,
+ "write_policy", &write_policy) < 0)
+ return -1;
+
+ /* see enum cache_write_policy in kernel */
+ switch (write_policy) {
+ case 0: cache.policy = VIR_NUMA_CACHE_POLICY_WRITEBACK; break;
+ case 1: cache.policy = VIR_NUMA_CACHE_POLICY_WRITETHROUGH; break;
+ case 2: cache.policy = VIR_NUMA_CACHE_POLICY_NONE; break;
+ default:
+ virReportError(VIR_ERR_INTERNAL_ERROR,
+ _("unknown write_policy value '%u'"),
+ write_policy);
+ return -1;
+ }
+
+ g_array_append_val(caches, cache);
+ }
+
+ if (direrr < 0)
+ return -1;
+
+ if (caches->len > 0) {
+ g_array_sort(caches, virCapsHostNUMACellCacheComparator);
+ *cachesRet = g_steal_pointer(&caches);
+ } else {
+ *cachesRet = NULL;
+ }
+
+ return 0;
+}
+
+
static int
virCapabilitiesHostNUMAInitFake(virCapsHostNUMA *caps)
{
@@ -1586,7 +1721,8 @@ virCapabilitiesHostNUMAInitFake(virCapsHostNUMA *caps)
nodeinfo.memory,
cid, &cpus,
0, NULL,
- 0, NULL);
+ 0, NULL,
+ NULL);
}
return 0;
@@ -1616,8 +1752,9 @@ virCapabilitiesHostNUMAInitReal(virCapsHostNUMA *caps)
g_autofree virNumaDistance *distances = NULL;
int ndistances = 0;
g_autofree virCapsHostNUMACellPageInfo *pageinfo = NULL;
- int npageinfo;
+ int npageinfo = 0;
unsigned long long memory;
+ g_autoptr(GArray) caches = NULL;
int cpu;
size_t i;
@@ -1644,6 +1781,9 @@ virCapabilitiesHostNUMAInitReal(virCapsHostNUMA *caps)
if (virCapabilitiesGetNUMAPagesInfo(n, &pageinfo, &npageinfo) < 0)
goto cleanup;
+ if (virCapabilitiesGetNodeCache(n, &caches) < 0)
+ goto cleanup;
+
/* Detect the amount of memory in the numa cell in KiB */
virNumaGetNodeMemory(n, &memory, NULL);
memory >>= 10;
@@ -1651,7 +1791,8 @@ virCapabilitiesHostNUMAInitReal(virCapsHostNUMA *caps)
virCapabilitiesHostNUMAAddCell(caps, n, memory,
ncpus, &cpus,
ndistances, &distances,
- npageinfo, &pageinfo);
+ npageinfo, &pageinfo,
+ &caches);
}
ret = 0;
diff --git a/src/conf/capabilities.h b/src/conf/capabilities.h
index 4d4ac476ea..334b361e7a 100644
--- a/src/conf/capabilities.h
+++ b/src/conf/capabilities.h
@@ -108,6 +108,7 @@ struct _virCapsHostNUMACell {
virNumaDistance *distances;
int npageinfo;
virCapsHostNUMACellPageInfo *pageinfo;
+ GArray *caches; /* virNumaCache */
};
struct _virCapsHostNUMA {
@@ -253,7 +254,8 @@ virCapabilitiesHostNUMAAddCell(virCapsHostNUMA *caps,
int ndistances,
virNumaDistance **distances,
int npageinfo,
- virCapsHostNUMACellPageInfo **pageinfo);
+ virCapsHostNUMACellPageInfo **pageinfo,
+ GArray **caches);
virCapsGuestMachine **
virCapabilitiesAllocMachines(const char *const *names,
diff --git a/src/libxl/libxl_capabilities.c b/src/libxl/libxl_capabilities.c
index b8600ca479..7385ad0d38 100644
--- a/src/libxl/libxl_capabilities.c
+++ b/src/libxl/libxl_capabilities.c
@@ -332,7 +332,8 @@ libxlCapsInitNuma(libxl_ctx *ctx, virCaps *caps)
numa_info[i].size / 1024,
nr_cpus_node[i], &cpus[i],
nr_distances, &distances,
- 0, NULL);
+ 0, NULL,
+ NULL);
/* This is safe, as the CPU list is now stored in the NUMA cell */
cpus[i] = NULL;
diff --git a/src/test/test_driver.c b/src/test/test_driver.c
index ea5a5005e7..1b36c67eb2 100644
--- a/src/test/test_driver.c
+++ b/src/test/test_driver.c
@@ -331,7 +331,8 @@ testBuildCapabilities(virConnectPtr conn)
i, privconn->cells[i].mem,
privconn->cells[i].numCpus, &cpu_cells,
0, NULL,
- nPages, &pages);
+ nPages, &pages,
+ NULL);
}
for (i = 0; i < G_N_ELEMENTS(guest_types); i++) {
diff --git a/tests/testutils.c b/tests/testutils.c
index eb3bd48b6a..7d87e30a5c 100644
--- a/tests/testutils.c
+++ b/tests/testutils.c
@@ -947,7 +947,8 @@ virTestCapsBuildNUMATopology(int seq)
MAX_MEM_IN_CELL,
MAX_CPUS_IN_CELL, &cell_cpus,
0, NULL,
- 0, NULL);
+ 0, NULL,
+ NULL);
cell_cpus = NULL;
}