]> err.no Git - util-linux/commitdiff
lscpu: use cpuset masks, read data for all CPUs
authorKarel Zak <kzak@redhat.com>
Fri, 28 May 2010 12:23:15 +0000 (14:23 +0200)
committerKarel Zak <kzak@redhat.com>
Tue, 1 Jun 2010 09:11:26 +0000 (11:11 +0200)
Signed-off-by: Karel Zak <kzak@redhat.com>
sys-utils/Makefile.am
sys-utils/lscpu.1
sys-utils/lscpu.c

index 0d9b3798f015b8a34cda55704fb7d600a7d4a54d..1ed07bee63fa46495b0df270f941e2cf3f723d6b 100644 (file)
@@ -11,11 +11,18 @@ dist_man_MANS = flock.1 ipcrm.1 ipcs.1 ipcmk.1 renice.1 setsid.1 \
 if LINUX
 bin_PROGRAMS += dmesg
 sbin_PROGRAMS += ctrlaltdel fsfreeze
-usrbin_exec_PROGRAMS += cytune setarch lscpu
+usrbin_exec_PROGRAMS += cytune setarch
 usrsbin_exec_PROGRAMS += ldattach tunelp rtcwake
 
 dist_man_MANS += dmesg.1 ctrlaltdel.8 cytune.8 setarch.8 \
-               ldattach.8 lscpu.1 tunelp.8 rtcwake.8 fsfreeze.8
+               ldattach.8 tunelp.8 rtcwake.8 fsfreeze.8
+
+if HAVE_CPU_SET_T
+usrbin_exec_PROGRAMS += lscpu
+lscpu_SOURCES = lscpu.c $(top_srcdir)/lib/cpuset.c
+dist_man_MANS += lscpu.1
+endif
+
 endif
 
 cytune_SOURCES = cytune.c cyclades.h
@@ -23,6 +30,7 @@ tunelp_SOURCES = tunelp.c lp.h
 
 info_TEXINFOS = ipc.texi
 
+
 if BUILD_FALLOCATE
 usrbin_exec_PROGRAMS += fallocate
 fallocate_SOURCES = fallocate.c $(top_srcdir)/lib/strtosize.c
index 2b8d33b160e11147effc9d4f30395208e4e6681d..fc3c46fa49e55e320bba51fdeeb7fde3c2928d15 100644 (file)
@@ -23,12 +23,15 @@ Print a help message.
 .BR \-p , " \-\-parse"
 Print out in parsable instead of printable format.
 .SH BUGS
-The program at the moment does not handle the system installed with
-different types of physical processors.
+The basic overview about CPU family, model, etc. is always based on the first
+CPU only.
 
 Sometimes in Xen Dom0 kernel reports wrong data.
 .SH AUTHOR
+.nf
 Cai Qian <qcai@redhat.com>
+Karel Zak <kzak@redhat.com>
+.fi
 .SH AVAILABILITY
 The setarch command is part of the util-linux-ng package and is available from
 ftp://ftp.kernel.org/pub/linux/utils/util-linux-ng/.
index d554f9b2effec63211d33c748a295772517a3ce7..6b786e8cc4bf57782ed72ec6ce78c71b27d52885 100644 (file)
 #include <unistd.h>
 #include <stdarg.h>
 
+#include "cpuset.h"
 #include "nls.h"
 
 #define CACHE_MAX 100
 
 /* /sys paths */
 #define _PATH_SYS_SYSTEM       "/sys/devices/system"
-#define _PATH_SYS_CPU0         _PATH_SYS_SYSTEM "/cpu/cpu0"
+#define _PATH_SYS_CPU          _PATH_SYS_SYSTEM "/cpu"
 #define _PATH_PROC_XEN         "/proc/xen"
 #define _PATH_PROC_XENCAP      _PATH_PROC_XEN "/capabilities"
 #define _PATH_PROC_CPUINFO     "/proc/cpuinfo"
 #define _PATH_PROC_PCIDEVS     "/proc/bus/pci/devices"
 
-int have_topology;
-int have_cache;
-int have_node;
-
-
 /* virtualization types */
 enum {
        VIRT_NONE       = 0,
@@ -83,22 +79,15 @@ enum {
 
 /* cache(s) description */
 struct cpu_cache {
-       char    *name;
-       char    *size;
-       int     map;
+       char            *name;
+       char            *size;
+
+       int             nsharedmaps;
+       cpu_set_t       **sharedmaps;
 };
 
 /* global description */
 struct lscpu_desc {
-       /* counters */
-       int     ct_cpu;
-       int     ct_thread;
-       int     ct_core;
-       int     ct_socket;
-       int     ct_node;
-       int     ct_cache;
-
-       /* who is who */
        char    *arch;
        char    *vendor;
        char    *family;
@@ -106,26 +95,36 @@ struct lscpu_desc {
        char    *virtflag;      /* virtualization flag (vmx, svm) */
        int     hyper;          /* hypervisor vendor ID */
        int     virtype;        /* VIRT_PARA|FULL|NONE ? */
-
-       /* caches */
-       struct cpu_cache        cache[CACHE_MAX];
-
-       /* misc */
        char    *mhz;
        char    *stepping;
        char    *flags;
-
        int     mode;           /* rm, lm or/and tm */
 
-       /* NUMA */
-       int     *nodecpu;
+       int             ncpus;          /* number of CPUs */
+
+       int             nnodes;         /* number of NUMA modes */
+       cpu_set_t       **nodemaps;     /* array with NUMA nodes */
+
+       /* sockets -- based on core_siblings (internal kernel map of cpuX's
+        * hardware threads within the same physical_package_id (socket)) */
+       int             nsockets;       /* number of all sockets */
+       cpu_set_t       **socketmaps;   /* unique core_siblings */
+
+       /* cores -- based on thread_siblings (internel kernel map of cpuX's
+        * hardware threads within the same core as cpuX) */
+       int             ncores;         /* number of all cores */
+       cpu_set_t       **coremaps;     /* unique thread_siblings */
+
+       int             nthreads;       /* number of threads */
+
+       int             ncaches;
+       struct cpu_cache *caches;
 };
 
 static size_t sysrootlen;
 static char pathbuf[PATH_MAX];
+static int maxcpus;            /* size in bits of kernel cpu mask */
 
-static const char *path_create(const char *path, ...)
-               __attribute__ ((__format__ (__printf__, 1, 2)));
 static FILE *path_fopen(const char *mode, int exit_on_err, const char *path, ...)
                __attribute__ ((__format__ (__printf__, 3, 4)));
 static void path_getstr(char *result, size_t len, const char *path, ...)
@@ -134,7 +133,7 @@ static int path_getnum(const char *path, ...)
                __attribute__ ((__format__ (__printf__, 1, 2)));
 static int path_exist(const char *path, ...)
                __attribute__ ((__format__ (__printf__, 1, 2)));
-static int path_sibling(const char *path, ...)
+static cpu_set_t *path_cpuset(const char *path, ...)
                __attribute__ ((__format__ (__printf__, 1, 2)));
 
 static const char *
@@ -148,19 +147,6 @@ path_vcreate(const char *path, va_list ap)
        return pathbuf;
 }
 
-static const char *
-path_create(const char *path, ...)
-{
-       const char *p;
-       va_list ap;
-
-       va_start(ap, path);
-       p = path_vcreate(path, ap);
-       va_end(ap);
-
-       return p;
-}
-
 static FILE *
 path_vfopen(const char *mode, int exit_on_error, const char *path, va_list ap)
 {
@@ -239,7 +225,7 @@ path_exist(const char *path, ...)
        return access(p, F_OK) == 0;
 }
 
-char *
+static char *
 xstrdup(const char *str)
 {
        char *s = strdup(str);
@@ -248,33 +234,35 @@ xstrdup(const char *str)
        return s;
 }
 
-/* count the set bit in a mapping file */
-static int
-path_sibling(const char *path, ...)
+static cpu_set_t *
+path_cpuset(const char *path, ...)
 {
-       int c, n;
-       int result = 0;
-       char s[2];
-       FILE *fp;
+       FILE *fd;
        va_list ap;
+       cpu_set_t *set;
+       size_t setsize, len = maxcpus * 7;
+       char buf[len];
 
        va_start(ap, path);
-       fp = path_vfopen("r", 1, path, ap);
+       fd = path_vfopen("r", 1, path, ap);
        va_end(ap);
 
-       while ((c = fgetc(fp)) != EOF) {
-               if (isxdigit(c)) {
-                       s[0] = c;
-                       s[1] = '\0';
-                       for (n = strtol(s, NULL, 16); n > 0; n /= 2) {
-                               if (n % 2)
-                                       result++;
-                       }
-               }
-       }
-       fclose(fp);
+       if (!fgets(buf, len, fd))
+               err(EXIT_FAILURE, _("failed to read: %s"), pathbuf);
+       fclose(fd);
 
-       return result;
+       len = strlen(buf);
+       if (buf[len - 1] == '\n')
+               buf[len - 1] = '\0';
+
+       set = cpuset_alloc(maxcpus, &setsize, NULL);
+       if (!set)
+               err(EXIT_FAILURE, _("failed to callocate cpu set"));
+
+       if (cpumask_parse(buf, set, setsize))
+               errx(EXIT_FAILURE, _("faild to parse CPU mask %s"), buf);
+
+       return set;
 }
 
 /* Lookup a pattern and get the value from cpuinfo.
@@ -331,8 +319,8 @@ read_basicinfo(struct lscpu_desc *desc)
        desc->arch = xstrdup(utsbuf.machine);
 
        /* count CPU(s) */
-       while(path_exist(_PATH_SYS_SYSTEM "/cpu/cpu%d", desc->ct_cpu))
-               desc->ct_cpu++;
+       while(path_exist(_PATH_SYS_SYSTEM "/cpu/cpu%d", desc->ncpus))
+               desc->ncpus++;
 
        /* details */
        while (fgets(buf, sizeof(buf), fp) != NULL) {
@@ -366,6 +354,17 @@ read_basicinfo(struct lscpu_desc *desc)
        }
 
        fclose(fp);
+
+       if (path_exist(_PATH_SYS_SYSTEM "/cpu/kernel_max"))
+               maxcpus = path_getnum(_PATH_SYS_SYSTEM "/cpu/kernel_max");
+
+       else if (!sysrootlen)
+               /* the root is '/' so we are working with data from the current kernel */
+               maxcpus = get_max_number_of_cpus();
+       else
+               /* we are reading some /sys snapshot instead the real /sys,
+                * let's use any crazy number... */
+               maxcpus = desc->ncpus > 2048 ? desc->ncpus : 2048;
 }
 
 static int
@@ -490,71 +489,138 @@ read_hypervisor(struct lscpu_desc *desc)
        }
 }
 
+/* add @set to the @ary, unnecesary set is deallocated. */
+static int add_cpuset_to_array(cpu_set_t **ary, int *items, cpu_set_t *set)
+{
+       int i;
+       size_t setsize = CPU_ALLOC_SIZE(maxcpus);
+
+       if (!ary)
+               return -1;
+
+       for (i = 0; i < *items; i++) {
+               if (CPU_EQUAL_S(setsize, set, ary[i]))
+                       break;
+       }
+       if (i == *items) {
+               ary[*items] = set;
+               ++*items;
+               return 0;
+       }
+       CPU_FREE(set);
+       return 1;
+}
+
 static void
-read_topology(struct lscpu_desc *desc)
+read_topology(struct lscpu_desc *desc, int num)
 {
-       /* number of threads */
-       desc->ct_thread = path_sibling(
-                               _PATH_SYS_CPU0 "/topology/thread_siblings");
+       cpu_set_t *thread_siblings, *core_siblings;
+
+       if (!path_exist(_PATH_SYS_CPU "/cpu%d/topology/thread_siblings", num))
+               return;
 
-       /* number of cores */
-       desc->ct_core = path_sibling(
-                               _PATH_SYS_CPU0 "/topology/core_siblings")
-                       / desc->ct_thread;
+       thread_siblings = path_cpuset(_PATH_SYS_CPU
+                                       "/cpu%d/topology/thread_siblings", num);
+       core_siblings = path_cpuset(_PATH_SYS_CPU
+                                       "/cpu%d/topology/core_siblings", num);
+       if (num == 0) {
+               int ncores, nsockets, nthreads;
+               size_t setsize = CPU_ALLOC_SIZE(maxcpus);
+
+               /* threads within one core */
+               nthreads = CPU_COUNT_S(setsize, thread_siblings);
+               /* cores within one socket */
+               ncores = CPU_COUNT_S(setsize, core_siblings) / nthreads;
+               /* number of sockets */
+               nsockets = desc->ncpus / nthreads / ncores;
+               /* all threads */
+               desc->nthreads = nsockets * ncores * nthreads;
+
+               desc->socketmaps = calloc(nsockets, sizeof(cpu_set_t *));
+               if (!desc->socketmaps)
+                       err(EXIT_FAILURE, _("error: calloc failed"));
+               desc->coremaps = calloc(ncores * nsockets, sizeof(cpu_set_t *));
+               if (!desc->coremaps)
+                       err(EXIT_FAILURE, _("error: calloc failed"));
+       }
 
-       /* number of sockets */
-       desc->ct_socket = desc->ct_cpu / desc->ct_core / desc->ct_thread;
+       add_cpuset_to_array(desc->socketmaps, &desc->nsockets, core_siblings);
+       add_cpuset_to_array(desc->coremaps, &desc->ncores, thread_siblings);
+}
+
+static int
+cachecmp(const void *a, const void *b)
+{
+       struct cpu_cache *c1 = (struct cpu_cache *) a;
+       struct cpu_cache *c2 = (struct cpu_cache *) b;
+
+       return strcmp(c2->name, c1->name);
 }
 
 static void
-read_cache(struct lscpu_desc *desc)
+read_cache(struct lscpu_desc *desc, int num)
 {
        char buf[256];
-       DIR *dp;
-       struct dirent *dir;
-       int level, type;
-       const char *p = path_create(_PATH_SYS_CPU0 "/cache");
-
-       dp = opendir(p);
-       if (dp == NULL)
-               err(EXIT_FAILURE, _("error: %s"), p);
-
-       while ((dir = readdir(dp)) != NULL) {
-               if (!strcmp(dir->d_name, ".")
-                   || !strcmp(dir->d_name, ".."))
-                       continue;
+       int i;
 
-               /* cache type */
-               path_getstr(buf, sizeof(buf),
-                               _PATH_SYS_CPU0 "/cache/%s/type", dir->d_name);
-               if (!strcmp(buf, "Data"))
-                       type = 'd';
-               else if (!strcmp(buf, "Instruction"))
-                       type = 'i';
-               else
-                       type = 0;
+       if (num == 0) {
+               while(path_exist(_PATH_SYS_SYSTEM "/cpu/cpu%d/cache/index%d",
+                                       num, desc->ncaches))
+                       desc->ncaches++;
 
-               /* cache level */
-               level = path_getnum(_PATH_SYS_CPU0 "/cache/%s/level",
-                               dir->d_name);
+               if (!desc->ncaches)
+                       return;
 
-               if (type)
-                       snprintf(buf, sizeof(buf), "L%d%c", level, type);
-               else
-                       snprintf(buf, sizeof(buf), "L%d", level);
+               desc->caches = calloc(desc->ncaches, sizeof(*desc->caches));
+               if (!desc->caches)
+                       err(EXIT_FAILURE, _("calloc failed"));
+       }
+       for (i = 0; i < desc->ncaches; i++) {
+               struct cpu_cache *ca = &desc->caches[i];
+               cpu_set_t *map;
+
+               if (!ca->name) {
+                       int type, level;
+
+                       /* cache type */
+                       path_getstr(buf, sizeof(buf),
+                                       _PATH_SYS_CPU "/cpu%d/cache/index%d/type",
+                                       num, i);
+                       if (!strcmp(buf, "Data"))
+                               type = 'd';
+                       else if (!strcmp(buf, "Instruction"))
+                               type = 'i';
+                       else
+                               type = 0;
+
+                       /* cache level */
+                       level = path_getnum(_PATH_SYS_CPU "/cpu%d/cache/index%d/level",
+                                       num, i);
+                       if (type)
+                               snprintf(buf, sizeof(buf), "L%d%c", level, type);
+                       else
+                               snprintf(buf, sizeof(buf), "L%d", level);
+
+                       ca->name = xstrdup(buf);
+
+                       /* cache size */
+                       path_getstr(buf, sizeof(buf),
+                                       _PATH_SYS_CPU "/cpu%d/cache/index%d/size",
+                                       num, i);
+                       ca->size = xstrdup(buf);
+               }
 
-               desc->cache[desc->ct_cache].name = xstrdup(buf);
+               /* information about how CPUs share different caches */
+               map = path_cpuset(_PATH_SYS_CPU "/cpu%d/cache/index%d/shared_cpu_map",
+                               num, i);
 
-               /* cache size */
-               path_getstr(buf, sizeof(buf),
-                               _PATH_SYS_CPU0 "/cache/%s/size", dir->d_name);
-               desc->cache[desc->ct_cache].size = xstrdup(buf);
+               if (!ca->sharedmaps) {
+                       ca->sharedmaps = calloc(desc->ncpus, sizeof(cpu_set_t *));
+                       if (!ca->sharedmaps)
+                               err(EXIT_FAILURE, _("error: calloc failed"));
+               }
 
-               /* information about how CPUs share different caches */
-               desc->cache[desc->ct_cache].map = path_sibling(
-                               _PATH_SYS_CPU0 "/cache/%s/shared_cpu_map",
-                               dir->d_name);
-               desc->ct_cache++;
+               add_cpuset_to_array(ca->sharedmaps, &ca->nsharedmaps, map);
        }
 }
 
@@ -564,42 +630,28 @@ read_nodes(struct lscpu_desc *desc)
        int i;
 
        /* number of NUMA node */
-       while (path_exist(_PATH_SYS_SYSTEM "/node/node%d", desc->ct_node))
-               desc->ct_node++;
+       while (path_exist(_PATH_SYS_SYSTEM "/node/node%d", desc->nnodes))
+               desc->nnodes++;
+
+       if (!desc->nnodes)
+               return;
 
-       desc->nodecpu = (int *) malloc(desc->ct_node * sizeof(int));
-       if (!desc->nodecpu)
-               err(EXIT_FAILURE, _("error: malloc failed"));
+       desc->nodemaps = calloc(desc->nnodes, sizeof(cpu_set_t *));
+       if (!desc->nodemaps)
+               err(EXIT_FAILURE, _("error: calloc failed"));
 
        /* information about how nodes share different CPUs */
-       for (i = 0; i < desc->ct_node; i++)
-               desc->nodecpu[i] = path_sibling(
+       for (i = 0; i < desc->nnodes; i++)
+               desc->nodemaps[i] = path_cpuset(
                                        _PATH_SYS_SYSTEM "/node/node%d/cpumap",
                                        i);
 }
 
-static void
-check_system(void)
-{
-       /* Read through sysfs. */
-       if (!path_exist(_PATH_SYS_SYSTEM))
-               errx(EXIT_FAILURE,
-                    _("error: %s is not accessable."), pathbuf);
-
-       if (path_exist(_PATH_SYS_SYSTEM "/node"))
-               have_node = 1;
-
-       if (path_exist(_PATH_SYS_CPU0 "/topology/thread_siblings"))
-               have_topology = 1;
-
-       if (path_exist(_PATH_SYS_CPU0 "/cache"))
-               have_cache = 1;
-}
-
 static void
 print_parsable(struct lscpu_desc *desc)
 {
        int i, j;
+       size_t setsize = CPU_ALLOC_SIZE(maxcpus);
 
        printf(_(
        "# The following is the parsable format, which can be fed to other\n"
@@ -607,50 +659,66 @@ print_parsable(struct lscpu_desc *desc)
        "# starting from zero.\n"
        "# CPU,Core,Socket,Node"));
 
-       if (have_cache) {
+       if (desc->ncaches) {
                /* separator between CPU topology and cache information */
                putchar(',');
 
-               for (i = desc->ct_cache - 1; i >= 0; i--)
-                       printf(",%s", desc->cache[i].name);
+               for (i = desc->ncaches - 1; i >= 0; i--)
+                       printf(",%s", desc->caches[i].name);
        }
        putchar('\n');
 
-       for (i = 0; i < desc->ct_cpu; i++) {
+       for (i = 0; i < desc->ncpus; i++) {
+
+               /* #CPU */
                printf("%d", i);
 
-               if (have_topology)
-                       printf(",%d,%d",
-                               i / desc->ct_thread,
-                               i / desc->ct_core / desc->ct_thread);
-               else
-                       printf(",,");
+               /* Core */
+               for (j = 0; j < desc->ncores; j++) {
+                       if (CPU_ISSET_S(i, setsize, desc->coremaps[j])) {
+                               printf(",%d", j);
+                               break;
+                       }
+               }
+               if (j == desc->ncores)
+                       putchar(',');
 
-               if (have_node) {
-                       int c = 0;
+               /* Socket */
+               for (j = 0; j < desc->nsockets; j++) {
+                       if (CPU_ISSET_S(i, setsize, desc->socketmaps[j])) {
+                               printf(",%d", j);
+                               break;
+                       }
+               }
+               if (j == desc->nsockets)
+                       putchar(',');
 
-                       for (j = 0; j < desc->ct_node; j++) {
-                               c += desc->nodecpu[j];
-                               if (i < c) {
-                                       printf(",%d", j);
-                                       break;
-                               }
+               /* Nodes */
+               for (j = 0; j < desc->nnodes; j++) {
+                       if (CPU_ISSET_S(i, setsize, desc->nodemaps[j])) {
+                               printf(",%d", j);
+                               break;
                        }
-               } else
+               }
+               if (j == desc->nnodes)
                        putchar(',');
 
-               if (have_cache) {
+               if (desc->ncaches)
                        putchar(',');
 
-                       for (j = desc->ct_cache - 1; j >= 0; j--) {
-                               /* If shared_cpu_map is 0, all CPUs share the same
-                                  cache. */
-                               if (desc->cache[j].map == 0)
-                                       desc->cache[j].map = desc->ct_core *
-                                                             desc->ct_thread;
+               /* Caches */
+               for (j = desc->ncaches - 1; j >= 0; j--) {
+                       struct cpu_cache *ca = &desc->caches[j];
+                       int x;
 
-                               printf(",%d", i / desc->cache[j].map);
+                       for (x = 0; x < ca->nsharedmaps; x++) {
+                               if (CPU_ISSET_S(i, setsize, ca->sharedmaps[x])) {
+                                       printf(",%d", x);
+                                       break;
+                               }
                        }
+                       if (x == ca->nsharedmaps)
+                               putchar(',');
                }
                putchar('\n');
        }
@@ -664,6 +732,9 @@ print_parsable(struct lscpu_desc *desc)
 static void
 print_readable(struct lscpu_desc *desc)
 {
+       char buf[512];
+       int i;
+
        print_s("Architecture:", desc->arch);
 
        if (desc->mode & (MODE_REAL | MODE_TRANSPARENT | MODE_LONG)) {
@@ -685,16 +756,16 @@ print_readable(struct lscpu_desc *desc)
                print_s(_("CPU op-mode(s):"), buf);
        }
 
-       print_n("CPU(s):", desc->ct_cpu);
+       print_n("CPU(s):", desc->ncpus);
 
-       if (have_topology) {
-               print_n(_("Thread(s) per core:"), desc->ct_thread);
-               print_n(_("Core(s) per socket:"), desc->ct_core);
-               print_n(_("CPU socket(s):"), desc->ct_socket);
+       if (desc->nsockets) {
+               print_n(_("Thread(s) per core:"), desc->nthreads / desc->ncores);
+               print_n(_("Core(s) per socket:"), desc->ncores / desc->nsockets);
+               print_n(_("CPU socket(s):"), desc->nsockets);
        }
 
-       if (have_node)
-               print_n(_("NUMA node(s):"), desc->ct_node);
+       if (desc->nnodes)
+               print_n(_("NUMA node(s):"), desc->nnodes);
        if (desc->vendor)
                print_s(_("Vendor ID:"), desc->vendor);
        if (desc->family)
@@ -715,14 +786,27 @@ print_readable(struct lscpu_desc *desc)
                print_s(_("Hypervisor vendor:"), hv_vendors[desc->hyper]);
                print_s(_("Virtualization type:"), virt_types[desc->virtype]);
        }
-       if (have_cache) {
+       if (desc->ncaches) {
                char buf[512];
                int i;
 
-               for (i = desc->ct_cache - 1; i >= 0; i--) {
+               for (i = desc->ncaches - 1; i >= 0; i--) {
                        snprintf(buf, sizeof(buf),
-                                       _("%s cache:"), desc->cache[i].name);
-                       print_s(buf, desc->cache[i].size);
+                                       _("%s cache:"), desc->caches[i].name);
+                       print_s(buf, desc->caches[i].size);
+               }
+       }
+
+       if (desc->nnodes) {
+               size_t setbuflen = 7 * maxcpus;
+               char setbuf[setbuflen];
+
+               for (i = 0; i < desc->nnodes; i++) {
+                       snprintf(buf, sizeof(buf), _("NUMA node%d CPU(s):"), i);
+                       print_s(buf, cpulist_create(
+                                               setbuf, setbuflen,
+                                               desc->nodemaps[i],
+                                               CPU_ALLOC_SIZE(maxcpus)));
                }
        }
 }
@@ -739,19 +823,10 @@ void usage(int rc)
        exit(rc);
 }
 
-static int
-ca_compare(const void *a, const void *b)
-{
-       struct cpu_cache *c1 = (struct cpu_cache *) a;
-       struct cpu_cache *c2 = (struct cpu_cache *) b;
-
-       return strcmp(c2->name, c1->name);
-}
-
 int main(int argc, char *argv[])
 {
        struct lscpu_desc _desc, *desc = &_desc;
-       int parsable = 0, c;
+       int parsable = 0, c, i;
 
        struct option longopts[] = {
                { "help",       no_argument,       0, 'h' },
@@ -783,18 +858,16 @@ int main(int argc, char *argv[])
 
        memset(desc, 0, sizeof(*desc));
 
-       check_system();
-
        read_basicinfo(desc);
 
-       if (have_topology)
-               read_topology(desc);
-       if (have_cache) {
-               read_cache(desc);
-               qsort(desc->cache, desc->ct_cache, sizeof(struct cpu_cache), ca_compare);
+       for (i = 0; i < desc->ncpus; i++) {
+               read_topology(desc, i);
+               read_cache(desc, i);
        }
-       if (have_node)
-               read_nodes(desc);
+
+       qsort(desc->caches, desc->ncaches, sizeof(struct cpu_cache), cachecmp);
+
+       read_nodes(desc);
 
        read_hypervisor(desc);