]> git.ipfire.org Git - thirdparty/util-linux.git/commitdiff
lscpu: Fix issue found on CPU hot-remove
authorToshi Kani <toshi.kani@hp.com>
Tue, 23 Oct 2012 19:49:46 +0000 (13:49 -0600)
committerKarel Zak <kzak@redhat.com>
Mon, 12 Nov 2012 13:47:13 +0000 (14:47 +0100)
read_basicinfo() relies on sysfs cpu directories
"/sys/devices/system/cpu/cpu%d" with assumption that cpu
logical number %d is always sequentially assigned for all
CPUs. However, this assumption is not correct with CPU
hot-remove operation since it removes a target sysfs cpu
directory after it is ejected. As a result, lscpu may not
recognize all CPUs.

The issue can be easily reproduced on KVM or VirtualBox,
which supports CPU eject operation, as follows.

1) The system has 4 CPUs
  $ lscpu -a -e
  CPU NODE SOCKET CORE L1d:L1i:L2 ONLINE
  0   0    0      0    0:0:0      yes
  1   0    1      1    1:1:1      yes
  2   0    2      2    2:2:2      yes
  3   0    3      3    3:3:3      yes

2) Eject cpu2
  # echo 1 > /sys/bus/acpi/devices/LNXCPU:02/eject

3) lscpu no longer recognizes cpu3 after cpu2 is ejected
  $ lscpu -a -e
  CPU NODE SOCKET CORE L1d:L1i:L2 ONLINE
  0   0    0      0    0:0:0      yes
  1   0    1      1    1:1:1      yes

The following changes are made to address this issue.
 - Use maxcpus to allocate and parse bitmaps.
 - Set desc->ncpu from cpu/present, which includes both on-line
   and off-line CPUs.
 - Add is_cpu_present() to check if a CPU is present. Ejected
   CPUs are not present.

[kzak@redhat.com: - read also /sys/devices/system/cpu/possible mask to
                    determine maximal number of CPUs,
                  - err() if possible mask is not found in /sys]

Signed-off-by: Toshi Kani <toshi.kani@hp.com>
Signed-off-by: Karel Zak <kzak@redhat.com>
sys-utils/lscpu.c

index 25a027345a61d7c4849c8c7b2fa69ea0613abd82..5b1a0afeeb6ad2e2f7ef1b551d4af49599a86785 100644 (file)
@@ -150,7 +150,9 @@ struct lscpu_desc {
        int     dispatching;    /* none, horizontal or vertical */
        int     mode;           /* rm, lm or/and tm */
 
-       int             ncpus;          /* number of CPUs */
+       int             ncpuspos;       /* maximal possible CPUs */
+       int             ncpus;          /* number of present CPUs */
+       cpu_set_t       *present;       /* mask with present CPUs */
        cpu_set_t       *online;        /* mask with online CPUs */
 
        int             nnodes;         /* number of NUMA modes */
@@ -204,8 +206,11 @@ struct lscpu_modifier {
 static int maxcpus;            /* size in bits of kernel cpu mask */
 
 #define is_cpu_online(_d, _cpu) \
-               ((_d) && (_d)->online ? \
-                       CPU_ISSET_S((_cpu), CPU_ALLOC_SIZE(maxcpus), (_d)->online) : 0)
+       ((_d) && (_d)->online ? \
+               CPU_ISSET_S((_cpu), CPU_ALLOC_SIZE(maxcpus), (_d)->online) : 0)
+#define is_cpu_present(_d, _cpu) \
+       ((_d) && (_d)->present ? \
+               CPU_ISSET_S((_cpu), CPU_ALLOC_SIZE(maxcpus), (_d)->present) : 0)
 
 /*
  * IDs
@@ -334,16 +339,13 @@ read_basicinfo(struct lscpu_desc *desc, struct lscpu_modifier *mod)
        FILE *fp = path_fopen("r", 1, _PATH_PROC_CPUINFO);
        char buf[BUFSIZ];
        struct utsname utsbuf;
+       size_t setsize;
 
        /* architecture */
        if (uname(&utsbuf) == -1)
                err(EXIT_FAILURE, _("error: uname failed"));
        desc->arch = xstrdup(utsbuf.machine);
 
-       /* count CPU(s) */
-       while(path_exist(_PATH_SYS_SYSTEM "/cpu/cpu%d", desc->ncpus))
-               desc->ncpus++;
-
        /* details */
        while (fgets(buf, sizeof(buf), fp) != NULL) {
                if (lookup(buf, "vendor", &desc->vendor)) ;
@@ -391,11 +393,27 @@ read_basicinfo(struct lscpu_desc *desc, struct lscpu_modifier *mod)
        if (maxcpus <= 0)
                /* error or we are reading some /sys snapshot instead of the
                 * real /sys, let's use any crazy number... */
-               maxcpus = desc->ncpus > 2048 ? desc->ncpus : 2048;
+               maxcpus = 2048;
+
+       setsize = CPU_ALLOC_SIZE(maxcpus);
+
+       if (path_exist(_PATH_SYS_SYSTEM "/cpu/possible")) {
+               cpu_set_t *tmp = path_cpulist(maxcpus, _PATH_SYS_SYSTEM "/cpu/possible");
+               desc->ncpuspos = CPU_COUNT_S(setsize, tmp);
+               cpuset_free(tmp);
+       } else
+               err(EXIT_FAILURE, _("failed to determine number of CPUs: %s"),
+                               _PATH_SYS_SYSTEM "/cpu/possible");
+
+
+       /* get mask for present CPUs */
+       if (path_exist(_PATH_SYS_SYSTEM "/cpu/present")) {
+               desc->present = path_cpulist(maxcpus, _PATH_SYS_SYSTEM "/cpu/present");
+               desc->ncpus = CPU_COUNT_S(setsize, desc->present);
+       }
 
        /* get mask for online CPUs */
        if (path_exist(_PATH_SYS_SYSTEM "/cpu/online")) {
-               size_t setsize = CPU_ALLOC_SIZE(maxcpus);
                desc->online = path_cpulist(maxcpus, _PATH_SYS_SYSTEM "/cpu/online");
                desc->nthreads = CPU_COUNT_S(setsize, desc->online);
        }
@@ -626,16 +644,16 @@ read_topology(struct lscpu_desc *desc, int num)
                 */
                if (!desc->nthreads)
                        desc->nthreads = nbooks * nsockets * ncores * nthreads;
-               /* For each map we make sure that it can have up to ncpus
+               /* For each map we make sure that it can have up to ncpuspos
                 * entries. This is because we cannot reliably calculate the
                 * number of cores, sockets and books on all architectures.
                 * E.g. completely virtualized architectures like s390 may
                 * have multiple sockets of different sizes.
                 */
-               desc->coremaps = xcalloc(desc->ncpus, sizeof(cpu_set_t *));
-               desc->socketmaps = xcalloc(desc->ncpus, sizeof(cpu_set_t *));
+               desc->coremaps = xcalloc(desc->ncpuspos, sizeof(cpu_set_t *));
+               desc->socketmaps = xcalloc(desc->ncpuspos, sizeof(cpu_set_t *));
                if (book_siblings)
-                       desc->bookmaps = xcalloc(desc->ncpus, sizeof(cpu_set_t *));
+                       desc->bookmaps = xcalloc(desc->ncpuspos, sizeof(cpu_set_t *));
        }
 
        add_cpuset_to_array(desc->socketmaps, &desc->nsockets, core_siblings);
@@ -653,7 +671,7 @@ read_polarization(struct lscpu_desc *desc, int num)
        if (!path_exist(_PATH_SYS_CPU "/cpu%d/polarization", num))
                return;
        if (!desc->polarization)
-               desc->polarization = xcalloc(desc->ncpus, sizeof(int));
+               desc->polarization = xcalloc(desc->ncpuspos, sizeof(int));
        path_getstr(mode, sizeof(mode), _PATH_SYS_CPU "/cpu%d/polarization", num);
        if (strncmp(mode, "vertical:low", sizeof(mode)) == 0)
                desc->polarization[num] = POLAR_VLOW;
@@ -673,7 +691,7 @@ read_address(struct lscpu_desc *desc, int num)
        if (!path_exist(_PATH_SYS_CPU "/cpu%d/address", num))
                return;
        if (!desc->addresses)
-               desc->addresses = xcalloc(desc->ncpus, sizeof(int));
+               desc->addresses = xcalloc(desc->ncpuspos, sizeof(int));
        desc->addresses[num] = path_getnum(_PATH_SYS_CPU "/cpu%d/address", num);
 }
 
@@ -683,7 +701,7 @@ read_configured(struct lscpu_desc *desc, int num)
        if (!path_exist(_PATH_SYS_CPU "/cpu%d/configure", num))
                return;
        if (!desc->configured)
-               desc->configured = xcalloc(desc->ncpus, sizeof(int));
+               desc->configured = xcalloc(desc->ncpuspos, sizeof(int));
        desc->configured[num] = path_getnum(_PATH_SYS_CPU "/cpu%d/configure", num);
 }
 
@@ -756,7 +774,7 @@ read_cache(struct lscpu_desc *desc, int num)
                                  num, i);
 
                if (!ca->sharedmaps)
-                       ca->sharedmaps = xcalloc(desc->ncpus, sizeof(cpu_set_t *));
+                       ca->sharedmaps = xcalloc(desc->ncpuspos, sizeof(cpu_set_t *));
                add_cpuset_to_array(ca->sharedmaps, &ca->nsharedmaps, map);
        }
 }
@@ -982,13 +1000,15 @@ print_parsable(struct lscpu_desc *desc, int cols[], int ncols,
        /*
         * Data
         */
-       for (i = 0; i < desc->ncpus; i++) {
+       for (i = 0; i < desc->ncpuspos; i++) {
                int c;
 
                if (!mod->offline && desc->online && !is_cpu_online(desc, i))
                        continue;
                if (!mod->online && desc->online && is_cpu_online(desc, i))
                        continue;
+               if (desc->present && !is_cpu_present(desc, i))
+                       continue;
                for (c = 0; c < ncols; c++) {
                        if (mod->compat && cols[c] == COL_CACHE) {
                                if (!desc->ncaches)
@@ -1026,7 +1046,7 @@ print_readable(struct lscpu_desc *desc, int cols[], int ncols,
                tt_define_column(tt, xstrdup(data), 0, 0);
        }
 
-       for (i = 0; i < desc->ncpus; i++) {
+       for (i = 0; i < desc->ncpuspos; i++) {
                int c;
                struct tt_line *line;
 
@@ -1034,6 +1054,8 @@ print_readable(struct lscpu_desc *desc, int cols[], int ncols,
                        continue;
                if (!mod->online && desc->online && is_cpu_online(desc, i))
                        continue;
+               if (desc->present && !is_cpu_present(desc, i))
+                       continue;
 
                line = tt_add_line(tt, NULL);
 
@@ -1117,8 +1139,8 @@ print_summary(struct lscpu_desc *desc, struct lscpu_modifier *mod)
                if (!set)
                        err(EXIT_FAILURE, _("failed to callocate cpu set"));
                CPU_ZERO_S(setsize, set);
-               for (i = 0; i < desc->ncpus; i++) {
-                       if (!is_cpu_online(desc, i))
+               for (i = 0; i < desc->ncpuspos; i++) {
+                       if (!is_cpu_online(desc, i) && is_cpu_present(desc, i))
                                CPU_SET_S(i, setsize, set);
                }
                print_cpuset(mod->hex ? _("Off-line CPU(s) mask:") :
@@ -1339,7 +1361,7 @@ int main(int argc, char *argv[])
 
        read_basicinfo(desc, mod);
 
-       for (i = 0; i < desc->ncpus; i++) {
+       for (i = 0; i < desc->ncpuspos; i++) {
                read_topology(desc, i);
                read_cache(desc, i);
                read_polarization(desc, i);