]> xenbits.xensource.com Git - libvirt.git/commitdiff
Add host cache information in capabilities
authorMartin Kletzander <mkletzan@redhat.com>
Thu, 30 Mar 2017 13:01:27 +0000 (15:01 +0200)
committerMartin Kletzander <mkletzan@redhat.com>
Tue, 9 May 2017 11:12:40 +0000 (13:12 +0200)
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>
docs/schemas/capability.rng
src/conf/capabilities.c
src/conf/capabilities.h
src/libvirt_private.syms
tests/vircaps2xmldata/vircaps-x86_64-caches.xml
tests/vircaps2xmltest.c

index 88e08d299ac988fbb27583c5624bda64c5208904..26f0aa22bd3fe5193049dc7904d2582a26d25e8e 100644 (file)
@@ -45,6 +45,9 @@
       <optional>
         <ref name='topology'/>
       </optional>
+      <optional>
+        <ref name='cache'/>
+      </optional>
       <zeroOrMore>
         <ref name='secmodel'/>
       </zeroOrMore>
     </element>
   </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'>
     <element name='guest'>
       <ref name='ostype'/>
index 7ed76e65b1a145825c97e58a736858b1e1229f5d..c36ca4088976e0e3ca1e8c7aa6725daa5e602d5a 100644 (file)
@@ -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, "<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:
  * @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, "<secmodel>\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;
+}
index d10eef3afdeacde1ed20377f0ea8ebae46adfc88..a8cccf7184a54c92e4babc1dcd9b4ba4d4feb295 100644 (file)
@@ -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 */
index e15d19fa86aae6fafad6a371f9b515f8398b5dbd..4cca0ca9ad3b7952756feb4c1a1f0ac01c8aa8d1 100644 (file)
@@ -59,6 +59,7 @@ virCapabilitiesFreeNUMAInfo;
 virCapabilitiesGetCpusForNodemask;
 virCapabilitiesGetNodeInfo;
 virCapabilitiesHostSecModelAddBaseLabel;
+virCapabilitiesInitCaches;
 virCapabilitiesInitNUMA;
 virCapabilitiesInitPages;
 virCapabilitiesNew;
index 88f2ec62277e64d13bca198399dd271debef5495..fe0be6d08fa7cdcf66ccef830560a4825dbb2ed4 100644 (file)
@@ -28,6 +28,9 @@
         </cell>
       </cells>
     </topology>
+    <cache>
+      <bank id='0' level='3' type='both' size='8192' unit='KiB' cpus='0-7'/>
+    </cache>
   </host>
 
 </capabilities>
index 6bf55aae5ba776a6feb3ffd997fdf7b40ab5ac6b..c957120aae440f7ae6a5bf2956497e792aca2a97 100644 (file)
@@ -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();