From a5cfffff28a4eb10604750b0f1f4e8696af3947c Mon Sep 17 00:00:00 2001 From: Toshi Kani Date: Tue, 23 Oct 2012 13:49:46 -0600 Subject: lscpu: Fix issue found on CPU hot-remove 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 Signed-off-by: Karel Zak --- sys-utils/lscpu.c | 66 ++++++++++++++++++++++++++++++++++++------------------- 1 file changed, 44 insertions(+), 22 deletions(-) (limited to 'sys-utils/lscpu.c') diff --git a/sys-utils/lscpu.c b/sys-utils/lscpu.c index 25a027345..5b1a0afee 100644 --- a/sys-utils/lscpu.c +++ b/sys-utils/lscpu.c @@ -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); -- cgit v1.2.3-55-g7522