diff --git a/docs/schemas/capability.rng b/docs/schemas/capability.rng
index 88e08d299ac988fbb27583c5624bda64c5208904..26f0aa22bd3fe5193049dc7904d2582a26d25e8e 100644
--- a/docs/schemas/capability.rng
+++ b/docs/schemas/capability.rng
@@ -45,6 +45,9 @@
+
+
+
@@ -248,6 +251,37 @@
+
+
+
+
+
+
+
+
+
+
+
+
+ both
+ code
+ data
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/src/conf/capabilities.c b/src/conf/capabilities.c
index 7ed76e65b1a145825c97e58a736858b1e1229f5d..c36ca4088976e0e3ca1e8c7aa6725daa5e602d5a 100644
--- a/src/conf/capabilities.c
+++ b/src/conf/capabilities.c
@@ -50,6 +50,8 @@
#define VIR_FROM_THIS VIR_FROM_CAPABILITIES
+#define SYSFS_SYSTEM_PATH "/sys/devices/system"
+
VIR_LOG_INIT("conf.capabilities")
VIR_ENUM_DECL(virCapsHostPMTarget)
@@ -237,6 +239,10 @@ virCapabilitiesDispose(void *object)
virCapabilitiesClearSecModel(&caps->host.secModels[i]);
VIR_FREE(caps->host.secModels);
+ for (i = 0; i < caps->host.ncaches; i++)
+ virCapsHostCacheBankFree(caps->host.caches[i]);
+ VIR_FREE(caps->host.caches);
+
VIR_FREE(caps->host.netprefix);
VIR_FREE(caps->host.pagesSize);
virCPUDefFree(caps->host.cpu);
@@ -860,6 +866,49 @@ virCapabilitiesFormatNUMATopology(virBufferPtr buf,
return 0;
}
+static int
+virCapabilitiesFormatCaches(virBufferPtr buf,
+ size_t ncaches,
+ virCapsHostCacheBankPtr *caches)
+{
+ size_t i = 0;
+
+ if (!ncaches)
+ return 0;
+
+ virBufferAddLit(buf, "\n");
+ virBufferAdjustIndent(buf, 2);
+
+ for (i = 0; i < ncaches; i++) {
+ virCapsHostCacheBankPtr bank = caches[i];
+ char *cpus_str = virBitmapFormat(bank->cpus);
+ bool kilos = !(bank->size % 1024);
+
+ if (!cpus_str)
+ return -1;
+
+ /*
+ * Let's just *hope* the size is aligned to KiBs so that it does not
+ * bite is back in the future
+ */
+ virBufferAsprintf(buf,
+ "\n",
+ bank->id, bank->level,
+ virCacheTypeToString(bank->type),
+ bank->size >> (kilos * 10),
+ kilos ? "KiB" : "B",
+ cpus_str);
+
+ VIR_FREE(cpus_str);
+ }
+
+ virBufferAdjustIndent(buf, -2);
+ virBufferAddLit(buf, "\n");
+
+ return 0;
+}
+
/**
* virCapabilitiesFormatXML:
* @caps: capabilities to format
@@ -956,6 +1005,10 @@ virCapabilitiesFormatXML(virCapsPtr caps)
caps->host.numaCell) < 0)
goto error;
+ if (virCapabilitiesFormatCaches(&buf, caps->host.ncaches,
+ caps->host.caches) < 0)
+ goto error;
+
for (i = 0; i < caps->host.nsecModels; i++) {
virBufferAddLit(&buf, "\n");
virBufferAdjustIndent(&buf, 2);
@@ -1438,3 +1491,154 @@ virCapabilitiesInitPages(virCapsPtr caps)
VIR_FREE(pages_size);
return ret;
}
+
+/* Cache name mapping for Linux kernel naming */
+VIR_ENUM_DECL(virCacheKernel);
+VIR_ENUM_IMPL(virCacheKernel, VIR_CACHE_TYPE_LAST,
+ "Unified",
+ "Instruction",
+ "Data")
+
+/* Our naming for cache types and scopes */
+VIR_ENUM_IMPL(virCache, VIR_CACHE_TYPE_LAST,
+ "both",
+ "code",
+ "data")
+
+bool
+virCapsHostCacheBankEquals(virCapsHostCacheBankPtr a,
+ virCapsHostCacheBankPtr b)
+{
+ return (a->id == b->id &&
+ a->level == b->level &&
+ a->type == b->type &&
+ a->size == b->size &&
+ virBitmapEqual(a->cpus, b->cpus));
+}
+
+void
+virCapsHostCacheBankFree(virCapsHostCacheBankPtr ptr)
+{
+ if (!ptr)
+ return;
+
+ virBitmapFree(ptr->cpus);
+ VIR_FREE(ptr);
+}
+
+int
+virCapabilitiesInitCaches(virCapsPtr caps)
+{
+ size_t i = 0;
+ virBitmapPtr cpus = NULL;
+ ssize_t pos = -1;
+ DIR *dirp = NULL;
+ int ret = -1;
+ char *path = NULL;
+ char *type = NULL;
+ struct dirent *ent = NULL;
+ virCapsHostCacheBankPtr bank = NULL;
+
+ /* Minimum level to expose in capabilities. Can be lowered or removed (with
+ * the appropriate code below), but should not be increased, because we'd
+ * lose information. */
+ const int cache_min_level = 3;
+
+ /* offline CPUs don't provide cache info */
+ if (virFileReadValueBitmap(&cpus, "%s/cpu/online", SYSFS_SYSTEM_PATH) < 0)
+ return -1;
+
+ while ((pos = virBitmapNextSetBit(cpus, pos)) >= 0) {
+ int rv = -1;
+
+ VIR_FREE(path);
+ if (virAsprintf(&path, "%s/cpu/cpu%zd/cache/", SYSFS_SYSTEM_PATH, pos) < 0)
+ goto cleanup;
+
+ rv = virDirOpenIfExists(&dirp, path);
+ if (rv < 0)
+ goto cleanup;
+
+ if (!dirp)
+ continue;
+
+ while ((rv = virDirRead(dirp, &ent, path)) > 0) {
+ int kernel_type;
+ unsigned int level;
+
+ if (!STRPREFIX(ent->d_name, "index"))
+ continue;
+
+ if (virFileReadValueUint(&level,
+ "%s/cpu/cpu%zd/cache/%s/level",
+ SYSFS_SYSTEM_PATH, pos, ent->d_name) < 0)
+ goto cleanup;
+
+ if (level < cache_min_level)
+ continue;
+
+ if (VIR_ALLOC(bank) < 0)
+ goto cleanup;
+
+ bank->level = level;
+
+ if (virFileReadValueUint(&bank->id,
+ "%s/cpu/cpu%zd/cache/%s/id",
+ SYSFS_SYSTEM_PATH, pos, ent->d_name) < 0)
+ goto cleanup;
+
+ if (virFileReadValueUint(&bank->level,
+ "%s/cpu/cpu%zd/cache/%s/level",
+ SYSFS_SYSTEM_PATH, pos, ent->d_name) < 0)
+ goto cleanup;
+
+ if (virFileReadValueString(&type,
+ "%s/cpu/cpu%zd/cache/%s/type",
+ SYSFS_SYSTEM_PATH, pos, ent->d_name) < 0)
+ goto cleanup;
+
+ if (virFileReadValueScaledInt(&bank->size,
+ "%s/cpu/cpu%zd/cache/%s/size",
+ SYSFS_SYSTEM_PATH, pos, ent->d_name) < 0)
+ goto cleanup;
+
+ if (virFileReadValueBitmap(&bank->cpus,
+ "%s/cpu/cpu%zd/cache/%s/shared_cpu_list",
+ SYSFS_SYSTEM_PATH, pos, ent->d_name) < 0)
+ goto cleanup;
+
+ kernel_type = virCacheKernelTypeFromString(type);
+ if (kernel_type < 0) {
+ virReportError(VIR_ERR_INTERNAL_ERROR,
+ _("Unknown cache type '%s'"), type);
+ VIR_FREE(type);
+ goto cleanup;
+ }
+ bank->type = kernel_type;
+
+ for (i = 0; i < caps->host.ncaches; i++) {
+ if (virCapsHostCacheBankEquals(bank, caps->host.caches[i]))
+ break;
+ }
+ if (i == caps->host.ncaches) {
+ if (VIR_APPEND_ELEMENT(caps->host.caches,
+ caps->host.ncaches,
+ bank) < 0) {
+ goto cleanup;
+ }
+ }
+
+ virCapsHostCacheBankFree(bank);
+ bank = NULL;
+ }
+ if (rv < 0)
+ goto cleanup;
+ }
+
+ ret = 0;
+ cleanup:
+ VIR_FREE(path);
+ virDirClose(&dirp);
+ virCapsHostCacheBankFree(bank);
+ return ret;
+}
diff --git a/src/conf/capabilities.h b/src/conf/capabilities.h
index d10eef3afdeacde1ed20377f0ea8ebae46adfc88..a8cccf7184a54c92e4babc1dcd9b4ba4d4feb295 100644
--- a/src/conf/capabilities.h
+++ b/src/conf/capabilities.h
@@ -138,6 +138,26 @@ struct _virCapsHostSecModel {
virCapsHostSecModelLabelPtr labels;
};
+typedef enum {
+ VIR_CACHE_TYPE_BOTH,
+ VIR_CACHE_TYPE_CODE,
+ VIR_CACHE_TYPE_DATA,
+
+ VIR_CACHE_TYPE_LAST
+} virCacheType;
+
+VIR_ENUM_DECL(virCache);
+
+typedef struct _virCapsHostCacheBank virCapsHostCacheBank;
+typedef virCapsHostCacheBank *virCapsHostCacheBankPtr;
+struct _virCapsHostCacheBank {
+ unsigned int id;
+ unsigned int level; /* 1=L1, 2=L2, 3=L3, etc. */
+ unsigned long long size; /* B */
+ virCacheType type; /* Data, Instruction or Unified */
+ virBitmapPtr cpus; /* All CPUs that share this bank */
+};
+
typedef struct _virCapsHost virCapsHost;
typedef virCapsHost *virCapsHostPtr;
struct _virCapsHost {
@@ -157,6 +177,9 @@ struct _virCapsHost {
size_t nnumaCell_max;
virCapsHostNUMACellPtr *numaCell;
+ size_t ncaches;
+ virCapsHostCacheBankPtr *caches;
+
size_t nsecModels;
virCapsHostSecModelPtr secModels;
@@ -303,4 +326,10 @@ int virCapabilitiesInitPages(virCapsPtr caps);
int virCapabilitiesInitNUMA(virCapsPtr caps);
+bool virCapsHostCacheBankEquals(virCapsHostCacheBankPtr a,
+ virCapsHostCacheBankPtr b);
+void virCapsHostCacheBankFree(virCapsHostCacheBankPtr ptr);
+
+int virCapabilitiesInitCaches(virCapsPtr caps);
+
#endif /* __VIR_CAPABILITIES_H */
diff --git a/src/libvirt_private.syms b/src/libvirt_private.syms
index e15d19fa86aae6fafad6a371f9b515f8398b5dbd..4cca0ca9ad3b7952756feb4c1a1f0ac01c8aa8d1 100644
--- a/src/libvirt_private.syms
+++ b/src/libvirt_private.syms
@@ -59,6 +59,7 @@ virCapabilitiesFreeNUMAInfo;
virCapabilitiesGetCpusForNodemask;
virCapabilitiesGetNodeInfo;
virCapabilitiesHostSecModelAddBaseLabel;
+virCapabilitiesInitCaches;
virCapabilitiesInitNUMA;
virCapabilitiesInitPages;
virCapabilitiesNew;
diff --git a/tests/vircaps2xmldata/vircaps-x86_64-caches.xml b/tests/vircaps2xmldata/vircaps-x86_64-caches.xml
index 88f2ec62277e64d13bca198399dd271debef5495..fe0be6d08fa7cdcf66ccef830560a4825dbb2ed4 100644
--- a/tests/vircaps2xmldata/vircaps-x86_64-caches.xml
+++ b/tests/vircaps2xmldata/vircaps-x86_64-caches.xml
@@ -28,6 +28,9 @@
+
+
+
diff --git a/tests/vircaps2xmltest.c b/tests/vircaps2xmltest.c
index 6bf55aae5ba776a6feb3ffd997fdf7b40ab5ac6b..c957120aae440f7ae6a5bf2956497e792aca2a97 100644
--- a/tests/vircaps2xmltest.c
+++ b/tests/vircaps2xmltest.c
@@ -58,7 +58,8 @@ test_virCapabilities(const void *opaque)
if (!caps)
goto cleanup;
- if (virCapabilitiesInitNUMA(caps) < 0)
+ if (virCapabilitiesInitNUMA(caps) < 0 ||
+ virCapabilitiesInitCaches(caps) < 0)
goto cleanup;
virFileWrapperClearPrefixes();