]> git.ipfire.org Git - thirdparty/util-linux.git/commitdiff
lscpu; (cpuinfo) parse caches
authorKarel Zak <kzak@redhat.com>
Mon, 17 Aug 2020 12:52:49 +0000 (14:52 +0200)
committerKarel Zak <kzak@redhat.com>
Fri, 13 Nov 2020 08:19:02 +0000 (09:19 +0100)
Signed-off-by: Karel Zak <kzak@redhat.com>
sys-utils/lscpu-api.h
sys-utils/lscpu-cputype.c

index 5c40c421cf9555c0c69341248513d910eb9ff586..7c62d05a35f52bf3afcb983ed5cd1923167e7334 100644 (file)
@@ -155,6 +155,25 @@ struct lscpu_virt {
 
 };
 
+struct lscpu_cache {
+       int             nth;            /* cache<number> from cpuinfo */
+       char            *name;
+       char            *type;
+       char            *allocation_policy;
+       char            *write_policy;
+
+       int             level;
+       uint64_t        size;
+
+       unsigned int    ways_of_associativity;
+       unsigned int    physical_line_partition;
+       unsigned int    number_of_sets;
+       unsigned int    coherency_line_size;
+
+       int             nsharedmaps;
+       cpu_set_t       **sharedmaps;
+};
+
 struct lscpu_cxt {
        int maxcpus;            /* size in bits of kernel cpu mask */
        const char *prefix;     /* path to /sys and /proc snapshot or NULL */
@@ -180,6 +199,12 @@ struct lscpu_cxt {
        struct lscpu_vulnerability *vuls;       /* array of CPU vulnerabilities */
        size_t  nvuls;                          /* number of CPU vulnerabilities */
 
+       struct lscpu_cache *caches;
+       size_t ncaches;
+
+       struct lscpu_cache *ecaches;
+       size_t necaches;                /* extra caches (s390) from /proc/cpuinfo */
+
        size_t nnodes;          /* number of NUMA modes */
        int *idx2nodenum;       /* Support for discontinuous nodes */
        cpu_set_t **nodemaps;   /* array with NUMA nodes */
index ac96d7f1d0938f8a35e9d86b60a764515afe0e4b..fc8321ef2e6fb113a995dce97a2cc3c943fcbbbd 100644 (file)
@@ -216,6 +216,7 @@ enum {
        PAT_TYPE,
        PAT_VARIANT,
        PAT_VENDOR,
+       PAT_CACHE
 };
 
 /*
@@ -278,6 +279,22 @@ static const struct cpuinfo_pattern cpu_patterns[] =
 
 };
 
+/*
+ * /proc/cpuinfo to lscpu_cache conversion
+ */
+#define DEF_PAT_CACHE(_str, _id) \
+       { \
+               .id = (_id), \
+               .domain = CPUINFO_LINE_CACHE, \
+               .pattern = (_str) \
+       }
+
+static const struct cpuinfo_pattern cache_patterns[] =
+{
+       /* Sort by fields name! */
+        DEF_PAT_CACHE("cache", PAT_CACHE),
+};
+
 #define CPUTYPE_PATTERN_BUFSZ  32
 
 static int cmp_pattern(const void *a0, const void *b0)
@@ -310,7 +327,7 @@ static int is_different_cputype(struct lscpu_cputype *ct, size_t offset, const c
        return 0;
 }
 
-/* cannonicalize @str -- replaces number at the end with %d and return the
+/* cannonicalize @str -- remove number at the end return the
  * number by @keynum. This is usable for example for "processor 5" or "cache1"
  * cpuinfo lines */
 static char *key_cleanup(char *str, int *keynum)
@@ -320,13 +337,16 @@ static char *key_cleanup(char *str, int *keynum)
 
        if (!sz)
                return str;
-       i = --sz;
-       while (i > 0 && isdigit(str[i--]));
 
-       if (i + 1 < sz) {
-               i++;
+       for (i = sz; i > 0; i--) {
+               if (!isdigit(str[i - 1]))
+                       break;
+       }
+
+       if (i < sz) {
                *keynum = atoi(str + i);
                str[i] = '\0';
+               rtrim_whitespace((unsigned char *)str);
        }
        return str;
 }
@@ -375,6 +395,13 @@ static const struct cpuinfo_pattern *cpuinfo_parse_line(char *str, char **value,
                        cmp_pattern)))
                goto found;
 
+       /* CACHE */
+       if ((pat = bsearch(&key, cache_patterns,
+                       ARRAY_SIZE(cache_patterns),
+                       sizeof(struct cpuinfo_pattern),
+                       cmp_pattern)))
+               goto found;
+
        return NULL;
 found:
        rtrim_whitespace((unsigned char *) v);
@@ -382,6 +409,68 @@ found:
        return pat;
 }
 
+/* Parse extra cache lines contained within /proc/cpuinfo but which are not
+ * part of the cache topology information within the sysfs filesystem.  This is
+ * true for all shared caches on e.g. s390. When there are layers of
+ * hypervisors in between it is not knows which CPUs share which caches.
+ * Therefore information about shared caches is only available in
+ * /proc/cpuinfo. Format is:
+ *
+ *  cache<nr> : level=<lvl> type=<type> scope=<scope> size=<size> line_size=<lsz> associativity=<as>
+ *
+ * the cache<nr> part is parsed in cpuinfo_parse_line, in this function parses part after ":".
+ */
+static int cpuinfo_parse_cache(struct lscpu_cxt *cxt, int keynum, char *data)
+{
+       struct lscpu_cache *cache;
+       long long size;
+       char *p, type;
+       int level;
+
+       DBG(GATHER, ul_debugobj(cxt, " parse cpuinfo cache '%s'", data));
+
+       p = strstr(data, "scope=") + 6;
+       /* Skip private caches, also present in sysfs */
+       if (!p || strncmp(p, "Private", 7) == 0)
+               return 0;
+       p = strstr(data, "level=");
+       if (!p || sscanf(p, "level=%d", &level) != 1)
+               return 0;
+       p = strstr(data, "type=") + 5;
+       if (!p || !*p)
+               return 0;
+       type = 0;
+       if (strncmp(p, "Data", 4) == 0)
+               type = 'd';
+       else if (strncmp(p, "Instruction", 11) == 0)
+               type = 'i';
+       else if (strncmp(p, "Unified", 7) == 0)
+               type = 'u';
+       p = strstr(data, "size=");
+       if (!p || sscanf(p, "size=%lld", &size) != 1)
+              return 0;
+
+       cxt->necaches++;
+       cxt->ecaches = xrealloc(cxt->ecaches,
+                               cxt->necaches * sizeof(struct lscpu_cache));
+       cache = &cxt->ecaches[cxt->necaches - 1];
+       memset(cache, 0 , sizeof(*cache));
+
+       if (type == 'i' || type == 'd')
+               xasprintf(&cache->name, "L%d%c", level, type);
+       else
+               xasprintf(&cache->name, "L%d", level);
+
+       cache->nth = keynum;
+       cache->level = level;
+       cache->size = size * 1024;
+
+       cache->type = type == 'i' ? xstrdup("Instruction") :
+                     type == 'd' ? xstrdup("Data") :
+                     type == 'u' ? xstrdup("Unified") : NULL;
+       return 1;
+}
+
 int lscpu_read_cpuinfo(struct lscpu_cxt *cxt)
 {
        FILE *fp;
@@ -459,7 +548,9 @@ int lscpu_read_cpuinfo(struct lscpu_cxt *cxt)
                        strdup_to_offset(pr->curr_type, pattern->offset, value);
                        break;
                case CPUINFO_LINE_CACHE:
-                       /* not implemented yet */
+                       if (pattern->id != PAT_CACHE)
+                               break;
+                       cpuinfo_parse_cache(cxt, keynum, value);
                        break;
                }
        } while (1);
@@ -802,6 +893,27 @@ struct lscpu_cxt *lscpu_new_context(void)
        return xcalloc(1, sizeof(struct lscpu_cxt));
 }
 
+static void lscpu_free_caches(struct lscpu_cache *caches, size_t n)
+{
+       size_t i;
+
+       if (!caches)
+               return;
+
+       for (i = 0; i < n; i++) {
+               struct lscpu_cache *c = &caches[i];
+
+               DBG(MISC, ul_debug(" freeing #%zu cache", i));
+
+               free(c->name);
+               free(c->type);
+               free(c->allocation_policy);
+               free(c->write_policy);
+               free(c->sharedmaps);
+       }
+       free(caches);
+}
+
 void lscpu_free_context(struct lscpu_cxt *cxt)
 {
        size_t i;
@@ -845,6 +957,8 @@ void lscpu_free_context(struct lscpu_cxt *cxt)
 
        lscpu_free_virtualization(cxt->virt);
        lscpu_free_architecture(cxt->arch);
+       lscpu_free_caches(cxt->ecaches, cxt->necaches);
+       lscpu_free_caches(cxt->caches, cxt->ncaches);
 
        free(cxt);
 }