Add host cache information in capabilities

We're only adding only info about L3 caches, we can add more
later (just by changing one line), but for now that's more than enough
without overwhelming anyone.

XML snippet of how this should look like (also seen as part of the commit):

  <cache>
    <bank id='0' level='3' type='both' size='8192' unit='KiB' cpus='0-7'/>
  </cache>

Signed-off-by: Martin Kletzander <mkletzan@redhat.com>
This commit is contained in:
Martin Kletzander 2017-03-30 15:01:27 +02:00
parent 90a118fa69
commit 4ad6a73bfc
6 changed files with 273 additions and 1 deletions

View File

@ -45,6 +45,9 @@
<optional> <optional>
<ref name='topology'/> <ref name='topology'/>
</optional> </optional>
<optional>
<ref name='cache'/>
</optional>
<zeroOrMore> <zeroOrMore>
<ref name='secmodel'/> <ref name='secmodel'/>
</zeroOrMore> </zeroOrMore>
@ -248,6 +251,37 @@
</element> </element>
</define> </define>
<define name='cache'>
<element name='cache'>
<oneOrMore>
<element name='bank'>
<attribute name='id'>
<ref name='unsignedInt'/>
</attribute>
<attribute name='level'>
<ref name='unsignedInt'/>
</attribute>
<attribute name='type'>
<choice>
<value>both</value>
<value>code</value>
<value>data</value>
</choice>
</attribute>
<attribute name='size'>
<ref name='unsignedInt'/>
</attribute>
<attribute name='unit'>
<ref name='unit'/>
</attribute>
<attribute name='cpus'>
<ref name='cpuset'/>
</attribute>
</element>
</oneOrMore>
</element>
</define>
<define name='guestcaps'> <define name='guestcaps'>
<element name='guest'> <element name='guest'>
<ref name='ostype'/> <ref name='ostype'/>

View File

@ -50,6 +50,8 @@
#define VIR_FROM_THIS VIR_FROM_CAPABILITIES #define VIR_FROM_THIS VIR_FROM_CAPABILITIES
#define SYSFS_SYSTEM_PATH "/sys/devices/system"
VIR_LOG_INIT("conf.capabilities") VIR_LOG_INIT("conf.capabilities")
VIR_ENUM_DECL(virCapsHostPMTarget) VIR_ENUM_DECL(virCapsHostPMTarget)
@ -237,6 +239,10 @@ virCapabilitiesDispose(void *object)
virCapabilitiesClearSecModel(&caps->host.secModels[i]); virCapabilitiesClearSecModel(&caps->host.secModels[i]);
VIR_FREE(caps->host.secModels); 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.netprefix);
VIR_FREE(caps->host.pagesSize); VIR_FREE(caps->host.pagesSize);
virCPUDefFree(caps->host.cpu); virCPUDefFree(caps->host.cpu);
@ -860,6 +866,49 @@ virCapabilitiesFormatNUMATopology(virBufferPtr buf,
return 0; return 0;
} }
static int
virCapabilitiesFormatCaches(virBufferPtr buf,
size_t ncaches,
virCapsHostCacheBankPtr *caches)
{
size_t i = 0;
if (!ncaches)
return 0;
virBufferAddLit(buf, "<cache>\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,
"<bank id='%u' level='%u' type='%s' "
"size='%llu' unit='%s' cpus='%s'/>\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, "</cache>\n");
return 0;
}
/** /**
* virCapabilitiesFormatXML: * virCapabilitiesFormatXML:
* @caps: capabilities to format * @caps: capabilities to format
@ -956,6 +1005,10 @@ virCapabilitiesFormatXML(virCapsPtr caps)
caps->host.numaCell) < 0) caps->host.numaCell) < 0)
goto error; goto error;
if (virCapabilitiesFormatCaches(&buf, caps->host.ncaches,
caps->host.caches) < 0)
goto error;
for (i = 0; i < caps->host.nsecModels; i++) { for (i = 0; i < caps->host.nsecModels; i++) {
virBufferAddLit(&buf, "<secmodel>\n"); virBufferAddLit(&buf, "<secmodel>\n");
virBufferAdjustIndent(&buf, 2); virBufferAdjustIndent(&buf, 2);
@ -1438,3 +1491,154 @@ virCapabilitiesInitPages(virCapsPtr caps)
VIR_FREE(pages_size); VIR_FREE(pages_size);
return ret; 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;
}

View File

@ -138,6 +138,26 @@ struct _virCapsHostSecModel {
virCapsHostSecModelLabelPtr labels; 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 struct _virCapsHost virCapsHost;
typedef virCapsHost *virCapsHostPtr; typedef virCapsHost *virCapsHostPtr;
struct _virCapsHost { struct _virCapsHost {
@ -157,6 +177,9 @@ struct _virCapsHost {
size_t nnumaCell_max; size_t nnumaCell_max;
virCapsHostNUMACellPtr *numaCell; virCapsHostNUMACellPtr *numaCell;
size_t ncaches;
virCapsHostCacheBankPtr *caches;
size_t nsecModels; size_t nsecModels;
virCapsHostSecModelPtr secModels; virCapsHostSecModelPtr secModels;
@ -303,4 +326,10 @@ int virCapabilitiesInitPages(virCapsPtr caps);
int virCapabilitiesInitNUMA(virCapsPtr caps); int virCapabilitiesInitNUMA(virCapsPtr caps);
bool virCapsHostCacheBankEquals(virCapsHostCacheBankPtr a,
virCapsHostCacheBankPtr b);
void virCapsHostCacheBankFree(virCapsHostCacheBankPtr ptr);
int virCapabilitiesInitCaches(virCapsPtr caps);
#endif /* __VIR_CAPABILITIES_H */ #endif /* __VIR_CAPABILITIES_H */

View File

@ -59,6 +59,7 @@ virCapabilitiesFreeNUMAInfo;
virCapabilitiesGetCpusForNodemask; virCapabilitiesGetCpusForNodemask;
virCapabilitiesGetNodeInfo; virCapabilitiesGetNodeInfo;
virCapabilitiesHostSecModelAddBaseLabel; virCapabilitiesHostSecModelAddBaseLabel;
virCapabilitiesInitCaches;
virCapabilitiesInitNUMA; virCapabilitiesInitNUMA;
virCapabilitiesInitPages; virCapabilitiesInitPages;
virCapabilitiesNew; virCapabilitiesNew;

View File

@ -28,6 +28,9 @@
</cell> </cell>
</cells> </cells>
</topology> </topology>
<cache>
<bank id='0' level='3' type='both' size='8192' unit='KiB' cpus='0-7'/>
</cache>
</host> </host>
</capabilities> </capabilities>

View File

@ -58,7 +58,8 @@ test_virCapabilities(const void *opaque)
if (!caps) if (!caps)
goto cleanup; goto cleanup;
if (virCapabilitiesInitNUMA(caps) < 0) if (virCapabilitiesInitNUMA(caps) < 0 ||
virCapabilitiesInitCaches(caps) < 0)
goto cleanup; goto cleanup;
virFileWrapperClearPrefixes(); virFileWrapperClearPrefixes();