]> git.ipfire.org Git - thirdparty/util-linux.git/commitdiff
lscpu: new cpuinfo parser
authorKarel Zak <kzak@redhat.com>
Fri, 17 Jul 2020 14:21:04 +0000 (16:21 +0200)
committerKarel Zak <kzak@redhat.com>
Fri, 13 Nov 2020 08:19:02 +0000 (09:19 +0100)
* extendable by new patterns
* simple semantic
* the same code for all pattern types

Signed-off-by: Karel Zak <kzak@redhat.com>
sys-utils/lscpu-api.h
sys-utils/lscpu-cpu.c
sys-utils/lscpu-cputype.c

index 494260f6f6e2a2fbb1f5ec5f3d72d3c46c174d02..b3067dadd2dd0d3f6ed14188812b74db282355bd 100644 (file)
@@ -55,8 +55,6 @@ struct lscpu_cputype {
        int     physchips;      /* Physical chips */
        int     physcoresperchip;       /* Physical cores per chip */
 
-       int     ncpus;          /* how many CPUs references this type */
-
        int     nthreads_per_core;
        int     ncores_per_socket;
        int     nsockets_per_book;
@@ -217,7 +215,6 @@ void lscpu_unref_cpu(struct lscpu_cpu *cpu);
 struct lscpu_cpu *lscpu_get_cpu(struct lscpu_cxt *cxt, int logical_id);
 int lscpu_cpu_set_type(struct lscpu_cpu *cpu, struct lscpu_cputype *type);
 int lscpu_create_cpus(struct lscpu_cxt *cxt, cpu_set_t *cpuset, size_t setsize);
-int lscpu_cpus_apply_type(struct lscpu_cxt *cxt, struct lscpu_cputype *type);
 struct lscpu_cpu *lscpu_cpus_loopup_by_type(struct lscpu_cxt *cxt, struct lscpu_cputype *ct);
 
 void lscpu_decode_arm(struct lscpu_cxt *cxt);
index dafc26bc38b98d8e873e41bd92cdb23fa490a088..0dc34ecc9e57428d16044f63424bcb4c5601a782 100644 (file)
@@ -24,7 +24,7 @@ void lscpu_unref_cpu(struct lscpu_cpu *cpu)
                return;
 
        if (--cpu->refcount <= 0) {
-               DBG(CPU, ul_debugobj(cpu, "  freeing"));
+               DBG(CPU, ul_debugobj(cpu, "  freeing #%d", cpu->logical_id));
                lscpu_unref_cputype(cpu->type);
                free(cpu->dynamic_mhz);
                free(cpu->static_mhz);
@@ -55,34 +55,13 @@ int lscpu_create_cpus(struct lscpu_cxt *cxt, cpu_set_t *cpuset, size_t setsize)
 
 int lscpu_cpu_set_type(struct lscpu_cpu *cpu, struct lscpu_cputype *type)
 {
-       struct lscpu_cputype *old = cpu->type;
-
+       lscpu_unref_cputype(cpu->type);
        cpu->type = type;
-
-       if (type) {
-               lscpu_ref_cputype(type);
-               type->ncpus++;
-       }
-       if (old) {
-               lscpu_unref_cputype(old);
-               old->ncpus--;
-       }
-       return 0;
-}
-
-int lscpu_cpus_apply_type(struct lscpu_cxt *cxt, struct lscpu_cputype *type)
-{
-       size_t i;
-
-       for (i = 0; i < cxt->npossibles; i++) {
-               struct lscpu_cpu *cpu = cxt->cpus[i];
-
-               if (cpu && !cpu->type)
-                       lscpu_cpu_set_type(cpu, type);
-       }
+       lscpu_ref_cputype(type);
        return 0;
 }
 
+/* don't forget lscpu_ref_cpu() ! */
 struct lscpu_cpu *lscpu_get_cpu(struct lscpu_cxt *cxt, int logical_id)
 {
        size_t i;
index 7d2a1697ef7fbf0dd505e7e0edd4e176262e332a..f8ddded02228e772efc055a6f9e9f83eefa8f140 100644 (file)
@@ -84,8 +84,10 @@ struct lscpu_cputype *lscpu_new_cputype(void)
 
 void lscpu_ref_cputype(struct lscpu_cputype *ct)
 {
-       if (ct)
+       if (ct) {
                ct->refcount++;
+               /*DBG(TYPE, ul_debugobj(ct, ">>> ref %d", ct->refcount));*/
+       }
 }
 
 void lscpu_unref_cputype(struct lscpu_cputype *ct)
@@ -93,6 +95,8 @@ void lscpu_unref_cputype(struct lscpu_cputype *ct)
        if (!ct)
                return;
 
+       /*DBG(TYPE, ul_debugobj(ct, ">>> unref %d", ct->refcount - 1));*/
+
        if (--ct->refcount <= 0) {
                DBG(TYPE, ul_debugobj(ct, "  freeing"));
                lscpu_cputype_free_topology(ct);
@@ -119,7 +123,10 @@ struct lscpu_cputype *lscpu_cputype_get_default(struct lscpu_cxt *cxt)
 #define match(astr, bstr) \
                ((!astr && !bstr) || (astr && bstr && strcmp(astr, bstr) == 0))
 
-struct lscpu_cputype *lscpu_add_cputype(struct lscpu_cxt *cxt, struct lscpu_cputype *ct)
+/*
+ * Return: 1|0 if @ct matches any exising type; @cur returns the type
+ */
+static int has_cputype(struct lscpu_cxt *cxt, struct lscpu_cputype *ct, struct lscpu_cputype **cur)
 {
        size_t i;
 
@@ -131,23 +138,22 @@ struct lscpu_cputype *lscpu_add_cputype(struct lscpu_cxt *cxt, struct lscpu_cput
                    match(x->model, ct->model) &&
                    match(x->modelname, ct->modelname) &&
                    match(x->stepping, ct->stepping)) {
-
-                       DBG(TYPE, ul_debugobj(x, "reuse"));
-                       return x;
+                       if (cur)
+                               *cur = x;
+                       return 1;
                }
        }
+       return 0;
+}
 
+struct lscpu_cputype *lscpu_add_cputype(struct lscpu_cxt *cxt, struct lscpu_cputype *ct)
+{
        DBG(TYPE, ul_debugobj(ct, "add new"));
        cxt->cputypes = xrealloc(cxt->cputypes, (cxt->ncputypes + 1)
                                * sizeof(struct lscpu_cputype *));
        cxt->cputypes[cxt->ncputypes] = ct;
        cxt->ncputypes++;
        lscpu_ref_cputype(ct);
-
-       /* first type -- use it for all CPUs */
-       if (cxt->ncputypes == 1)
-               lscpu_cpus_apply_type(cxt, ct);
-
        return ct;
 }
 
@@ -177,9 +183,18 @@ static void lscpu_merge_cputype(struct lscpu_cputype *a, struct lscpu_cputype *b
                a->addrsz = xstrdup(b->addrsz);
 }
 
+
+enum {
+       CPUINFO_LINE_UNKNOWN,   /* unknown line */
+       CPUINFO_LINE_CPUTYPE,   /* line found in type_patterns[] */
+       CPUINFO_LINE_CPU,       /* line found in cpu_patterns[] */
+       CPUINFO_LINE_CACHE      /* line found in cache_pattern[] */
+};
+
 /* Describes /proc/cpuinfo fields */
 struct cpuinfo_pattern {
        int id;                 /* field ID */
+       int domain;             /* CPUINFO_LINE_* */
        const char *pattern;    /* field name as used in /proc/cpuinfo */
        size_t  offset;         /* offset in lscpu_cputype or lscpu_cpu struct */
 };
@@ -214,6 +229,7 @@ enum {
 #define DEF_PAT_CPUTYPE(_str, _id, _member) \
        { \
                .id = (_id), \
+               .domain = CPUINFO_LINE_CPUTYPE, \
                .pattern = (_str), \
                .offset = offsetof(struct lscpu_cputype, _member), \
        }
@@ -251,6 +267,7 @@ static const struct cpuinfo_pattern type_patterns[] =
 #define DEF_PAT_CPU(_str, _id, _member) \
        { \
                .id = (_id), \
+               .domain = CPUINFO_LINE_CPU, \
                .pattern = (_str), \
                .offset = offsetof(struct lscpu_cpu, _member), \
        }
@@ -263,6 +280,7 @@ static const struct cpuinfo_pattern cpu_patterns[] =
        DEF_PAT_CPU( "cpu MHz",         PAT_MHZ,        mhz),
        DEF_PAT_CPU( "cpu number",      PAT_PROCESSOR,  logical_id),    /* s390 */
         DEF_PAT_CPU( "processor",      PAT_PROCESSOR,  logical_id),
+
 };
 
 #define CPUTYPE_PATTERN_BUFSZ  32
@@ -275,132 +293,127 @@ static int cmp_pattern(const void *a0, const void *b0)
        return strcmp(a->pattern, b->pattern);
 }
 
-/*
- * This function parses "key : value" from /proc/cpuinfo. The "key" describes CPU-type
- * (type_patterns[]) or CPU specific (cpu_patterns[]) values.
- *
- * Note that s390-like "processor <n>:" lines are not suported here.
- */
-static int cpuinfo_parse_line( struct lscpu_cxt *cxt,
-                               struct lscpu_cputype **ct,
-                               struct lscpu_cpu **cpu,
-                               const char *str)
+
+struct cpuinfo_parser {
+       struct lscpu_cxt        *cxt;
+       struct lscpu_cpu        *curr_cpu;
+       struct lscpu_cputype    *curr_type;
+};
+
+/* cannonicalize @str -- replaces number at the end with %d and 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)
+{
+       size_t sz = rtrim_whitespace((unsigned char *)str);
+       size_t i;
+
+       if (!sz)
+               return str;
+       i = --sz;
+       while (i > 0 && isdigit(str[i--]));
+
+       if (i + 1 < sz) {
+               i++;
+               *keynum = atoi(str + i);
+               str[i] = '\0';
+       }
+       return str;
+}
+
+static const struct cpuinfo_pattern *cpuinfo_parse_line(char *str, char **value, int *keynum)
 {
        struct cpuinfo_pattern key, *pat;
-       const char *p, *v;
-       char buf[CPUTYPE_PATTERN_BUFSZ] = { 0 }, **data;
-       void *stru = NULL;
+       char *p, *v;
+       char buf[CPUTYPE_PATTERN_BUFSZ] = { 0 };
 
        DBG(GATHER, ul_debug("parse \"%s\"", str));
 
        if (!str || !*str)
-               return -EINVAL;
-       p = skip_blank(str);
+               return NULL;
+       p = (char *) skip_blank(str);
        if (!p || !*p)
-               return -EINVAL;
+               return NULL;
 
        v = strchr(p, ':');
        if (!v || !*v)
-               return -EINVAL;
+               return NULL;
 
        /* prepare name of the field */
        xstrncpy(buf, p, sizeof(buf));
        buf[v - p] = '\0';
        v++;
 
-       rtrim_whitespace((unsigned char *)buf);
-
        /* prepare value */
-       v = skip_space(v);
+       v = (char *) skip_space(v);
        if (!v || !*v)
-               return -EINVAL;
+               return NULL;
+
+       key.pattern = key_cleanup(buf, keynum);
 
-       /* search in cpu-types patterns */
-       key.pattern = buf;
-       pat = bsearch(&key, type_patterns,
+       /* CPU-type */
+       if ((pat = bsearch(&key, type_patterns,
                        ARRAY_SIZE(type_patterns),
                        sizeof(struct cpuinfo_pattern),
-                       cmp_pattern);
-       if (pat) {
-               if (!*ct)
-                       *ct = lscpu_new_cputype();
-               stru = *ct;
-
-       /* search in cpu patterns */
-       } else {
-               pat = bsearch(&key, cpu_patterns,
+                       cmp_pattern)))
+               goto found;
+
+       /* CPU */
+       if ((pat = bsearch(&key, cpu_patterns,
                        ARRAY_SIZE(cpu_patterns),
                        sizeof(struct cpuinfo_pattern),
-                       cmp_pattern);
-
-               if (pat) {
-                       if (pat->id == PAT_PROCESSOR) {
-                               /* switch to another CPU only */
-                               *cpu = lscpu_get_cpu(cxt, atoi(v));
-                               return 0;
-                       }
-                       stru = *cpu;
-               }
-       }
-
-       if (!stru) {
-               DBG(GATHER, ul_debug("'%s' not found", buf));
-               return 1;
-       }
-
-       /* set value as a string and cleanup */
-       strdup_to_offset(stru, pat->offset, v);
-       data = (char **) ((char *) stru + pat->offset);
-       rtrim_whitespace((unsigned char *) *data);
-
-       return 0;
+                       cmp_pattern)))
+               goto found;
+
+       return NULL;
+found:
+       rtrim_whitespace((unsigned char *) v);
+       *value = v;
+       return pat;
 }
 
-static int is_processor_line(const char *str)
-{
-       const char *p;
-
-       if (!startswith(str, "processor "))
-               return 0;
-       p = str + 10;
-       while (*p && isdigit(*p))
-               p++;
-       if (*p == ':')
-               return 1;
-       return 0;
-}
-
-/*
- * s390-like "processor <n>: value = xxx, value = yyy" lines parser.
- */
-static int cpuinfo_parse_processor_line(struct lscpu_cxt *cxt, struct lscpu_cpu **cpu, const char *str)
+/* saves the current state to the @cxt and cleanup */
+static void cpuinfo_commit(struct cpuinfo_parser *pr)
 {
-       const char *v;
-       char *end = NULL;
-       int n;
+       /* save new type */
+       if (pr->curr_type) {
+               struct lscpu_cputype *type;
+
+               if (has_cputype(pr->cxt, pr->curr_type, &type)) {
+                       /* type already exist, merge new to the old; free new;
+                        * continue with original */
+                       lscpu_ref_cputype(type);
+                       lscpu_merge_cputype(type, pr->curr_type);
+                       lscpu_unref_cputype(pr->curr_type);
+                       pr->curr_type = type;
+               } else
+                       lscpu_add_cputype(pr->cxt, pr->curr_type);
+
+       /* we have CPU, but not type, let's use the default type or create a new one */
+       } else if (pr->curr_cpu && !pr->curr_cpu->type) {
+               pr->curr_type = lscpu_cputype_get_default(pr->cxt);
+               if (!pr->curr_type)
+                       pr->curr_type = lscpu_new_cputype();
+       }
 
-       v = strchr(str, ' ');
-       if (!v)
-               return -EINVAL;
-       v++;
-       errno = 0;
-       n = strtol(v, &end, 10);
-       if (errno || !end || v == end || *end != ':')
-               return -EINVAL;
+       /* Make connection between CPU and cputype. Note that CPUs are already
+        * stored in cxt->cpus */
+       if (pr->curr_cpu)
+               lscpu_cpu_set_type(pr->curr_cpu, pr->curr_type);
 
-       /* switch to another CPU */
-       *cpu = lscpu_get_cpu(cxt, n);
 
-       /* we don't parse anythign more from the line */
-       return 0;
+       /* cleanup */
+       lscpu_unref_cputype(pr->curr_type);
+       lscpu_unref_cpu(pr->curr_cpu);
+       pr->curr_type = NULL;
+       pr->curr_cpu = NULL;
 }
 
 int lscpu_read_cpuinfo(struct lscpu_cxt *cxt)
 {
-       struct lscpu_cputype *type = NULL;
-       struct lscpu_cpu *cpu = NULL;
        FILE *fp;
        char buf[BUFSIZ];
+       struct cpuinfo_parser _pr = { .cxt = cxt }, *pr = &_pr;
 
        assert(cxt->npossibles);        /* lscpu_create_cpus() required */
        assert(cxt->cpus);
@@ -412,58 +425,62 @@ int lscpu_read_cpuinfo(struct lscpu_cxt *cxt)
                err(EXIT_FAILURE, _("cannot open %s"), "/proc/cpuinfo");
 
        do {
-               const char *p = NULL;
+               int keynum = -1;
+               char *p = NULL, *value = NULL;
+               const struct cpuinfo_pattern *pattern;
 
                if (fgets(buf, sizeof(buf), fp) != NULL)
-                       p = skip_space(buf);
+                       p = (char *) skip_space(buf);
 
                if (p == NULL || (*buf && !*p)) {
-                       /*
-                        * Blank line separates CPUs
-                        */
-                       if (cpu)
-                               lscpu_cpu_set_type(cpu, type);
-                       else if (type) {
-                               /* Generic non-cpu data. For some architectures
-                                * cpuinfo contains description block (at the
-                                * beginning of the file (IBM s390) or at the
-                                * end of the file (IBM POWER). The block is
-                                * global for all CPUs.
-                                */
-                               if (cxt->ncputypes == 1) {
-                                       /* The type already exist, merge it. For example on POWER
-                                        * CPU list contains "cpu:" line with architecture and
-                                        * global information at the end of the file */
-                                       struct lscpu_cputype *dflt = lscpu_cputype_get_default(cxt);
-                                       if (dflt)
-                                               lscpu_merge_cputype(dflt, type);
-                               } else
-                                       lscpu_add_cputype(cxt, type);
-                       }
-
-                       lscpu_unref_cputype(type);
-                       cpu = NULL, type = NULL;
+                       /* Blank line separates information */
+                       cpuinfo_commit(pr);
 
                        if (p == NULL)
                                break;  /* fgets() returns nothing; EOF */
-               } else {
-                       rtrim_whitespace((unsigned char *) buf);
-
-                       if (is_processor_line(buf)) {
-                               /* s390-like "processor <n>:" per line */
-                               cpuinfo_parse_processor_line(cxt, &cpu, p);
-                               if (cpu) {
-                                       lscpu_cpu_set_type(cpu, type);
-                                       cpu = NULL;
-                               }
-                       } else
-                               cpuinfo_parse_line(cxt, &type, &cpu, p);
+                       continue;
+               }
+
+               rtrim_whitespace((unsigned char *) buf);
+
+               /* parse */
+               pattern = cpuinfo_parse_line(p, &value, &keynum);
+               if (!pattern) {
+                       DBG(GATHER, ul_debug("'%s' not found", buf));
+                       continue;
+               }
+
+               /* set data */
+               switch (pattern->domain) {
+               case CPUINFO_LINE_CPU:
+                       if (pattern->id == PAT_PROCESSOR) {
+                               int id = keynum >= 0 ? keynum : atoi(value);
+                               lscpu_unref_cpu(pr->curr_cpu);
+                               pr->curr_cpu = lscpu_get_cpu(cxt, id);
+                               if (!pr->curr_cpu)
+                                       DBG(GATHER, ul_debug("*** cpu ID '%d' undefined", id));
+                               else
+                                       DBG(GATHER, ul_debug(" switch to CPU %d", id));
+                               lscpu_ref_cpu(pr->curr_cpu);
+                               break;
+                       }
+                       if (!pr->curr_cpu)
+                               DBG(GATHER, ul_debug("*** cpu data before cpu ID"));
+                       else
+                               strdup_to_offset(pr->curr_cpu, pattern->offset, value);
+                       break;
+               case CPUINFO_LINE_CPUTYPE:
+                       if (!pr->curr_type)
+                               pr->curr_type = lscpu_new_cputype();
+                       strdup_to_offset(pr->curr_type, pattern->offset, value);
+                       break;
+               case CPUINFO_LINE_CACHE:
+                       /* not implemented yet */
+                       break;
                }
        } while (1);
 
-       lscpu_unref_cputype(type);
        fclose(fp);
-
        return 0;
 }