diff --git a/docs/schemas/capability.rng b/docs/schemas/capability.rng index 88e08d299a..26f0aa22bd 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 7ed76e65b1..c36ca40889 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 d10eef3afd..a8cccf7184 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 e15d19fa86..4cca0ca9ad 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 88f2ec6227..fe0be6d08f 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 6bf55aae5b..c957120aae 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();