]> git.ipfire.org Git - thirdparty/util-linux.git/blobdiff - sys-utils/lscpu.c
sys-utils: cleanup license lines, add SPDX
[thirdparty/util-linux.git] / sys-utils / lscpu.c
index 9e2569d9303429d3932bd731be1e52407ebef375..a65ccad5e435b93686c478aa197adb7dc71fa28c 100644 (file)
@@ -1,24 +1,16 @@
 /*
- * lscpu - CPU architecture information helper
- *
- * Copyright (C) 2008 Cai Qian <qcai@redhat.com>
- * Copyright (C) 2008 Karel Zak <kzak@redhat.com>
+ * SPDX-License-Identifier: GPL-2.0-or-later
  *
  * This program is free software; you can redistribute it and/or modify
  * it under the terms of the GNU General Public License as published by
  * the Free Software Foundation; either version 2 of the License, or
  * (at your option) any later version.
  *
- * This program is distributed in the hope that it would be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
- * GNU General Public License for more details.
+ * Copyright (C) 2008 Cai Qian <qcai@redhat.com>
+ * Copyright (C) 2008-2023 Karel Zak <kzak@redhat.com>
  *
- * You should have received a copy of the GNU General Public License along
- * with this program; if not, write to the Free Software Foundation, Inc.,
- * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ * lscpu - CPU architecture information helper
  */
-
 #include <assert.h>
 #include <ctype.h>
 #include <dirent.h>
@@ -39,6 +31,8 @@
 
 #include "closestream.h"
 #include "optutils.h"
+#include "c_strtod.h"
+#include "sysfs.h"
 
 #include "lscpu.h"
 
@@ -54,13 +48,13 @@ static const char *hv_vendors[] = {
        [VIRT_VENDOR_XEN]       = "Xen",
        [VIRT_VENDOR_KVM]       = "KVM",
        [VIRT_VENDOR_MSHV]      = "Microsoft",
-       [VIRT_VENDOR_VMWARE]  = "VMware",
+       [VIRT_VENDOR_VMWARE]    = "VMware",
        [VIRT_VENDOR_IBM]       = "IBM",
        [VIRT_VENDOR_VSERVER]   = "Linux-VServer",
        [VIRT_VENDOR_UML]       = "User-mode Linux",
        [VIRT_VENDOR_INNOTEK]   = "Innotek GmbH",
        [VIRT_VENDOR_HITACHI]   = "Hitachi",
-       [VIRT_VENDOR_PARALLELS] = "Parallels",
+       [VIRT_VENDOR_PARALLELS] = "Parallels",
        [VIRT_VENDOR_VBOX]      = "Oracle",
        [VIRT_VENDOR_OS400]     = "OS/400",
        [VIRT_VENDOR_PHYP]      = "pHyp",
@@ -80,10 +74,10 @@ struct polarization_modes {
 };
 
 static struct polarization_modes polar_modes[] = {
-       [POLAR_UNKNOWN]    = {"U",  "-"},
-       [POLAR_VLOW]       = {"VL", "vert-low"},
-       [POLAR_VMEDIUM]    = {"VM", "vert-medium"},
-       [POLAR_VHIGH]      = {"VH", "vert-high"},
+       [POLAR_UNKNOWN]    = {"U",  "-"},
+       [POLAR_VLOW]       = {"VL", "vert-low"},
+       [POLAR_VMEDIUM]    = {"VM", "vert-medium"},
+       [POLAR_VHIGH]      = {"VH", "vert-high"},
        [POLAR_HORIZONTAL] = {"H",  "horizontal"},
 };
 
@@ -91,9 +85,11 @@ static struct polarization_modes polar_modes[] = {
  * IDs
  */
 enum {
+       COL_CPU_BOGOMIPS,
        COL_CPU_CPU,
        COL_CPU_CORE,
        COL_CPU_SOCKET,
+       COL_CPU_CLUSTER,
        COL_CPU_NODE,
        COL_CPU_BOOK,
        COL_CPU_DRAWER,
@@ -102,8 +98,11 @@ enum {
        COL_CPU_ADDRESS,
        COL_CPU_CONFIGURED,
        COL_CPU_ONLINE,
+       COL_CPU_MHZ,
+       COL_CPU_SCALMHZ,
        COL_CPU_MAXMHZ,
        COL_CPU_MINMHZ,
+       COL_CPU_MODELNAME,
 };
 
 enum {
@@ -129,40 +128,48 @@ struct lscpu_coldesc {
 
        int flags;
        unsigned int  is_abbr:1;        /* name is abbreviation */
+       int json_type;
 };
 
 static struct lscpu_coldesc coldescs_cpu[] =
 {
-       [COL_CPU_CPU]          = { "CPU", N_("logical CPU number"), SCOLS_FL_RIGHT, 1 },
-       [COL_CPU_CORE]         = { "CORE", N_("logical core number"), SCOLS_FL_RIGHT },
-       [COL_CPU_SOCKET]       = { "SOCKET", N_("logical socket number"), SCOLS_FL_RIGHT },
-       [COL_CPU_NODE]         = { "NODE", N_("logical NUMA node number"), SCOLS_FL_RIGHT },
-       [COL_CPU_BOOK]         = { "BOOK", N_("logical book number"), SCOLS_FL_RIGHT },
-       [COL_CPU_DRAWER]       = { "DRAWER", N_("logical drawer number"), SCOLS_FL_RIGHT },
+       [COL_CPU_BOGOMIPS]     = { "BOGOMIPS", N_("crude measurement of CPU speed"), SCOLS_FL_RIGHT, 1, SCOLS_JSON_NUMBER },
+       [COL_CPU_CPU]          = { "CPU", N_("logical CPU number"), SCOLS_FL_RIGHT, 1, SCOLS_JSON_NUMBER },
+       [COL_CPU_CORE]         = { "CORE", N_("logical core number"), SCOLS_FL_RIGHT, 0, SCOLS_JSON_NUMBER },
+       [COL_CPU_CLUSTER]      = { "CLUSTER", N_("logical cluster number"), SCOLS_FL_RIGHT, 0, SCOLS_JSON_NUMBER },
+       [COL_CPU_SOCKET]       = { "SOCKET", N_("logical socket number"), SCOLS_FL_RIGHT, 0, SCOLS_JSON_NUMBER },
+       [COL_CPU_NODE]         = { "NODE", N_("logical NUMA node number"), SCOLS_FL_RIGHT, 0, SCOLS_JSON_NUMBER },
+       [COL_CPU_BOOK]         = { "BOOK", N_("logical book number"), SCOLS_FL_RIGHT, 0, SCOLS_JSON_NUMBER },
+       [COL_CPU_DRAWER]       = { "DRAWER", N_("logical drawer number"), SCOLS_FL_RIGHT, SCOLS_JSON_NUMBER },
        [COL_CPU_CACHE]        = { "CACHE", N_("shows how caches are shared between CPUs") },
        [COL_CPU_POLARIZATION] = { "POLARIZATION", N_("CPU dispatching mode on virtual hardware") },
        [COL_CPU_ADDRESS]      = { "ADDRESS", N_("physical address of a CPU") },
-       [COL_CPU_CONFIGURED]   = { "CONFIGURED", N_("shows if the hypervisor has allocated the CPU") },
-       [COL_CPU_ONLINE]       = { "ONLINE", N_("shows if Linux currently makes use of the CPU"), SCOLS_FL_RIGHT },
-       [COL_CPU_MAXMHZ]       = { "MAXMHZ", N_("shows the maximum MHz of the CPU"), SCOLS_FL_RIGHT },
-       [COL_CPU_MINMHZ]       = { "MINMHZ", N_("shows the minimum MHz of the CPU"), SCOLS_FL_RIGHT }
+       [COL_CPU_CONFIGURED]   = { "CONFIGURED", N_("shows if the hypervisor has allocated the CPU"), 0, 0, SCOLS_JSON_BOOLEAN_OPTIONAL },
+       [COL_CPU_ONLINE]       = { "ONLINE", N_("shows if Linux currently makes use of the CPU"), SCOLS_FL_RIGHT, 0, SCOLS_JSON_BOOLEAN_OPTIONAL },
+       [COL_CPU_MHZ]          = { "MHZ", N_("shows the currently MHz of the CPU"), SCOLS_FL_RIGHT, 0, SCOLS_JSON_NUMBER },
+       [COL_CPU_SCALMHZ]      = { "SCALMHZ%", N_("shows scaling percentage of the CPU frequency"), SCOLS_FL_RIGHT, SCOLS_JSON_NUMBER },
+       [COL_CPU_MAXMHZ]       = { "MAXMHZ", N_("shows the maximum MHz of the CPU"), SCOLS_FL_RIGHT, 0, SCOLS_JSON_NUMBER },
+       [COL_CPU_MINMHZ]       = { "MINMHZ", N_("shows the minimum MHz of the CPU"), SCOLS_FL_RIGHT, 0, SCOLS_JSON_NUMBER },
+       [COL_CPU_MODELNAME]    = { "MODELNAME", N_("shows CPU model name"), 0, 0, SCOLS_JSON_STRING }
 };
 
 static struct lscpu_coldesc coldescs_cache[] =
 {
        [COL_CACHE_ALLSIZE]    = { "ALL-SIZE", N_("size of all system caches"), SCOLS_FL_RIGHT },
-       [COL_CACHE_LEVEL]      = { "LEVEL", N_("cache level"), SCOLS_FL_RIGHT },
+       [COL_CACHE_LEVEL]      = { "LEVEL", N_("cache level"), SCOLS_FL_RIGHT, 0, SCOLS_JSON_NUMBER },
        [COL_CACHE_NAME]       = { "NAME", N_("cache name") },
        [COL_CACHE_ONESIZE]    = { "ONE-SIZE", N_("size of one cache"), SCOLS_FL_RIGHT },
        [COL_CACHE_TYPE]       = { "TYPE", N_("cache type") },
-       [COL_CACHE_WAYS]       = { "WAYS", N_("ways of associativity"), SCOLS_FL_RIGHT },
+       [COL_CACHE_WAYS]       = { "WAYS", N_("ways of associativity"), SCOLS_FL_RIGHT, 0, SCOLS_JSON_NUMBER },
        [COL_CACHE_ALLOCPOL]   = { "ALLOC-POLICY", N_("allocation policy") },
        [COL_CACHE_WRITEPOL]   = { "WRITE-POLICY", N_("write policy") },
-       [COL_CACHE_PHYLINE]    = { "PHY-LINE", N_("number of physical cache line per cache t"), SCOLS_FL_RIGHT },
-       [COL_CACHE_SETS]       = { "SETS", N_("number of sets in the cache; set lines has the same cache index"), SCOLS_FL_RIGHT },
-       [COL_CACHE_COHERENCYSIZE] = { "COHERENCY-SIZE", N_("minimum amount of data in bytes transferred from memory to cache"), SCOLS_FL_RIGHT }
+       [COL_CACHE_PHYLINE]    = { "PHY-LINE", N_("number of physical cache line per cache tag"), SCOLS_FL_RIGHT, 0, SCOLS_JSON_NUMBER },
+       [COL_CACHE_SETS]       = { "SETS", N_("number of sets in the cache; set lines has the same cache index"), SCOLS_FL_RIGHT, 0, SCOLS_JSON_NUMBER },
+       [COL_CACHE_COHERENCYSIZE] = { "COHERENCY-SIZE", N_("minimum amount of data in bytes transferred from memory to cache"), SCOLS_FL_RIGHT, 0, SCOLS_JSON_NUMBER }
 };
 
+static int hierarchic = -1;
+
 UL_DEBUG_DEFINE_MASK(lscpu);
 UL_DEBUG_DEFINE_MASKNAMES(lscpu) = UL_DEBUG_EMPTY_MASKNAMES;
 
@@ -206,6 +213,15 @@ static void lscpu_context_init_paths(struct lscpu_cxt *cxt)
        DBG(MISC, ul_debugobj(cxt, "initialize paths"));
        ul_path_init_debug();
 
+       /* / */
+       cxt->rootfs = NULL;
+       if (cxt->prefix) {
+               cxt->rootfs = ul_new_path("/");
+               if (!cxt->rootfs)
+                       err(EXIT_FAILURE, _("failed to initialize rootfs handler"));
+               ul_path_set_prefix(cxt->rootfs, cxt->prefix);
+       }
+
        /* /sys/devices/system/cpu */
        cxt->syscpu = ul_new_path(_PATH_SYS_CPU);
        if (!cxt->syscpu)
@@ -238,6 +254,7 @@ static void lscpu_free_context(struct lscpu_cxt *cxt)
        DBG(MISC, ul_debugobj(cxt, " de-initialize paths"));
        ul_unref_path(cxt->syscpu);
        ul_unref_path(cxt->procfs);
+       ul_unref_path(cxt->rootfs);
 
        DBG(MISC, ul_debugobj(cxt, " freeing cpus"));
        for (i = 0; i < cxt->npossibles; i++) {
@@ -276,308 +293,345 @@ static void lscpu_free_context(struct lscpu_cxt *cxt)
        free(cxt);
 }
 
-#ifdef LSCPU_OLD_OUTPUT_CODE   /* temporary disabled for revrite */
+static void __fill_id( struct lscpu_cxt *cxt,
+                       struct lscpu_cpu *cpu,
+                       int id, cpu_set_t **map,
+                       size_t nitems,
+                       char *buf, size_t bufsz)
+{
+       *buf = '\0';
 
-static char *
-get_cell_data(struct lscpu_desc *desc, int idx, int col,
-             struct lscpu_modifier *mod,
-             char *buf, size_t bufsz)
+       if (cxt->show_physical) {
+               if (id < 0)
+                       snprintf(buf, bufsz, "-");
+               else
+                       snprintf(buf, bufsz, "%d", id);
+       } else if (map) {
+               size_t i;
+
+               if (cpuset_ary_isset(cpu->logical_id, map, nitems,
+                                       cxt->setsize, &i) == 0)
+                       snprintf(buf, bufsz, "%zu", i);
+       }
+}
+
+static void get_cell_boolean(
+               struct lscpu_cxt *cxt,
+               int has_data, int data,
+               char *buf, size_t bufsz)
+{
+               if (!has_data)
+                       return;
+
+               if (cxt->mode == LSCPU_OUTPUT_PARSABLE || cxt->json)
+                       snprintf(buf, bufsz, "%s",
+                                data ? _("Y") : _("N"));
+               else
+                       snprintf(buf, bufsz, "%s",
+                                data ? _("yes") : _("no"));
+}
+
+#define fill_id(_cxt, _cpu, NAME, _buf, _bufsz) \
+               __fill_id(_cxt, (_cpu), \
+                       (_cpu)-> NAME ## id, \
+                       (_cpu)->type-> NAME ## maps, \
+                       (_cpu)->type->n ## NAME ## s, \
+                       _buf, _bufsz)
+
+static char *get_cell_data(
+                       struct lscpu_cxt *cxt,
+                       struct lscpu_cpu *cpu, int col,
+                       char *buf, size_t bufsz)
 {
-       size_t setsize = CPU_ALLOC_SIZE(maxcpus);
        size_t i;
-       int cpu = real_cpu_num(desc, idx);
 
        *buf = '\0';
 
+       if (!cpu->type)
+               return NULL;
+
        switch (col) {
        case COL_CPU_CPU:
-               snprintf(buf, bufsz, "%d", cpu);
+               snprintf(buf, bufsz, "%d", cpu->logical_id);
+               break;
+       case COL_CPU_BOGOMIPS:
+               if (!cpu->bogomips && !cpu->type->bogomips)
+                       break;
+               snprintf(buf, bufsz, "%.2f", (float) c_strtod(
+                               cpu->bogomips ? : cpu->type->bogomips, NULL));
                break;
        case COL_CPU_CORE:
-               if (mod->physical) {
-                       if (desc->coreids[idx] == -1)
-                               snprintf(buf, bufsz, "-");
-                       else
-                               snprintf(buf, bufsz, "%d", desc->coreids[idx]);
-               } else {
-                       if (cpuset_ary_isset(cpu, desc->coremaps,
-                                            desc->ncores, setsize, &i) == 0)
-                               snprintf(buf, bufsz, "%zu", i);
-               }
+               fill_id(cxt, cpu, core, buf, bufsz);
                break;
        case COL_CPU_SOCKET:
-               if (mod->physical) {
-                       if (desc->socketids[idx] ==  -1)
-                               snprintf(buf, bufsz, "-");
-                       else
-                               snprintf(buf, bufsz, "%d", desc->socketids[idx]);
-               } else {
-                       if (cpuset_ary_isset(cpu, desc->socketmaps,
-                                            desc->nsockets, setsize, &i) == 0)
-                               snprintf(buf, bufsz, "%zu", i);
-               }
+               fill_id(cxt, cpu, socket, buf, bufsz);
                break;
-       case COL_CPU_NODE:
-               if (cpuset_ary_isset(cpu, desc->nodemaps,
-                                    desc->nnodes, setsize, &i) == 0)
-                       snprintf(buf, bufsz, "%d", desc->idx2nodenum[i]);
+       case COL_CPU_CLUSTER:
+               if (cxt->is_cluster)
+                       fill_id(cxt, cpu, socket, buf, bufsz);
                break;
        case COL_CPU_DRAWER:
-               if (!desc->drawerids || !desc->drawermaps)
-                       break;
-               if (mod->physical) {
-                       if (desc->drawerids[idx] == -1)
-                               snprintf(buf, bufsz, "-");
-                       else
-                               snprintf(buf, bufsz, "%d", desc->drawerids[idx]);
-               } else {
-                       if (cpuset_ary_isset(cpu, desc->drawermaps,
-                                            desc->ndrawers, setsize, &i) == 0)
-                               snprintf(buf, bufsz, "%zu", i);
-               }
+               fill_id(cxt, cpu, drawer, buf, bufsz);
                break;
        case COL_CPU_BOOK:
-               if (!desc->bookids || !desc->bookmaps)
-                       break;
-               if (mod->physical) {
-                       if (desc->bookids[idx] == -1)
-                               snprintf(buf, bufsz, "-");
-                       else
-                               snprintf(buf, bufsz, "%d", desc->bookids[idx]);
-               } else {
-                       if (cpuset_ary_isset(cpu, desc->bookmaps,
-                                            desc->nbooks, setsize, &i) == 0)
-                               snprintf(buf, bufsz, "%zu", i);
-               }
+               fill_id(cxt, cpu, book, buf, bufsz);
+               break;
+       case COL_CPU_NODE:
+               if (cpuset_ary_isset(cpu->logical_id, cxt->nodemaps,
+                                    cxt->nnodes, cxt->setsize, &i) == 0)
+                       snprintf(buf, bufsz, "%d", cxt->idx2nodenum[i]);
                break;
        case COL_CPU_CACHE:
        {
+               const char *last = NULL;
                char *p = buf;
                size_t sz = bufsz;
-               int j;
-
-               for (j = desc->ncaches - 1; j >= 0; j--) {
-                       struct cpu_cache *ca = &desc->caches[j];
-
-                       if (cpuset_ary_isset(cpu, ca->sharedmaps,
-                                            ca->nsharedmaps, setsize, &i) == 0) {
-                               int x = snprintf(p, sz, "%zu", i);
-                               if (x < 0 || (size_t) x >= sz)
-                                       return NULL;
-                               p += x;
-                               sz -= x;
-                       }
-                       if (j != 0) {
-                               if (sz < 2)
-                                       return NULL;
-                               *p++ = mod->compat ? ',' : ':';
-                               *p = '\0';
-                               sz--;
-                       }
+
+               for (i = 0; i < cxt->ncaches; i++) {
+                       int x;
+                       struct lscpu_cache *ca;
+                       const char *name = cxt->caches[i].name;
+
+                       if (last && strcmp(last, name) == 0)
+                               continue;
+                       last = name;
+                       ca = lscpu_cpu_get_cache(cxt, cpu, name);
+                       if (!ca)
+                               continue;
+                       x = snprintf(p, sz, "%d", ca->id);
+                       if (x < 0 || (size_t) x >= sz)
+                               return NULL;
+                       p += x;
+                       sz -= x;
+                       if (sz < 2)
+                               return NULL;
+                       *p++ = cxt->show_compatible ? ',' : ':';
+                       *p = '\0';
+                       sz--;
                }
+               if (p > buf && (*(p - 1) == ',' || *(p - 1) == ':'))
+                       *(p - 1) = '\0';
                break;
        }
        case COL_CPU_POLARIZATION:
-               if (desc->polarization) {
-                       int x = desc->polarization[idx];
-
-                       snprintf(buf, bufsz, "%s",
-                                mod->mode == LSCPU_OUTPUT_PARSABLE ?
-                                               polar_modes[x].parsable :
-                                               polar_modes[x].readable);
-               }
+               if (cpu->polarization < 0)
+                       break;
+               snprintf(buf, bufsz, "%s",
+                               cxt->mode == LSCPU_OUTPUT_PARSABLE ?
+                               polar_modes[cpu->polarization].parsable :
+                               polar_modes[cpu->polarization].readable);
                break;
        case COL_CPU_ADDRESS:
-               if (desc->addresses)
-                       snprintf(buf, bufsz, "%d", desc->addresses[idx]);
+               if (cpu->address < 0)
+                       break;
+               snprintf(buf, bufsz, "%d", cpu->address);
                break;
        case COL_CPU_CONFIGURED:
-               if (!desc->configured)
-                       break;
-               if (mod->mode == LSCPU_OUTPUT_PARSABLE)
-                       snprintf(buf, bufsz, "%s",
-                                desc->configured[idx] ? _("Y") : _("N"));
-               else
-                       snprintf(buf, bufsz, "%s",
-                                desc->configured[idx] ? _("yes") : _("no"));
+               get_cell_boolean(cxt, cpu->configured >= 0, cpu->configured, buf, bufsz);
                break;
        case COL_CPU_ONLINE:
-               if (!desc->online)
-                       break;
-               if (mod->mode == LSCPU_OUTPUT_PARSABLE)
-                       snprintf(buf, bufsz, "%s",
-                                is_cpu_online(desc, cpu) ? _("Y") : _("N"));
-               else
-                       snprintf(buf, bufsz, "%s",
-                                is_cpu_online(desc, cpu) ? _("yes") : _("no"));
+               get_cell_boolean(cxt, !!cxt->online, is_cpu_online(cxt, cpu), buf, bufsz);
+               break;
+       case COL_CPU_MHZ:
+               if (cpu->mhz_cur_freq)
+                       snprintf(buf, bufsz, "%.4f", cpu->mhz_cur_freq);
+               break;
+       case COL_CPU_SCALMHZ:
+               if (cpu->mhz_cur_freq && cpu->mhz_max_freq)
+                       snprintf(buf, bufsz, "%.0f%%", cpu->mhz_cur_freq / cpu->mhz_max_freq * 100);
                break;
        case COL_CPU_MAXMHZ:
-               if (desc->maxmhz && desc->maxmhz[idx])
-                       xstrncpy(buf, desc->maxmhz[idx], bufsz);
+               if (cpu->mhz_max_freq)
+                       snprintf(buf, bufsz, "%.4f", cpu->mhz_max_freq);
                break;
        case COL_CPU_MINMHZ:
-               if (desc->minmhz && desc->minmhz[idx])
-                       xstrncpy(buf, desc->minmhz[idx], bufsz);
+               if (cpu->mhz_min_freq)
+                       snprintf(buf, bufsz, "%.4f", cpu->mhz_min_freq);
+               break;
+       case COL_CPU_MODELNAME:
+               if (cpu->type && cpu->type->modelname)
+                       xstrncpy(buf, cpu->type->modelname, bufsz);
                break;
        }
        return buf;
 }
 
-static char *
-get_cell_header(struct lscpu_desc *desc, int col,
-               struct lscpu_modifier *mod,
-               char *buf, size_t bufsz)
+static char *get_cell_header(
+                       struct lscpu_cxt *cxt, int col,
+                       char *buf, size_t bufsz)
 {
        *buf = '\0';
 
        if (col == COL_CPU_CACHE) {
+               const char *last = NULL;
                char *p = buf;
                size_t sz = bufsz;
-               int i;
+               size_t i;
 
-               for (i = desc->ncaches - 1; i >= 0; i--) {
-                       int x = snprintf(p, sz, "%s", desc->caches[i].name);
+               for (i = 0; i < cxt->ncaches; i++) {
+                       struct lscpu_cache *ca = &cxt->caches[i];
+                       int x;
+
+                       if (last && strcmp(last, ca->name) == 0)
+                               continue;
+                       last = ca->name;
+
+                       x = snprintf(p, sz, "%s", ca->name);
                        if (x < 0 || (size_t) x >= sz)
                                return NULL;
                        sz -= x;
                        p += x;
-                       if (i > 0) {
-                               if (sz < 2)
-                                       return NULL;
-                               *p++ = mod->compat ? ',' : ':';
-                               *p = '\0';
-                               sz--;
-                       }
+                       if (sz < 2)
+                               return NULL;
+                       *p++ = cxt->show_compatible ? ',' : ':';
+                       *p = '\0';
+                       sz--;
                }
-               if (desc->ncaches)
+               if (p > buf && (*(p - 1) == ',' || *(p - 1) == ':'))
+                       *(p - 1) = '\0';
+               if (cxt->ncaches)
                        return buf;
        }
        snprintf(buf, bufsz, "%s", coldescs_cpu[col].name);
        return buf;
 }
 
+
+static void caches_add_line(struct lscpu_cxt *cxt,
+                           struct libscols_table *tb,
+                           struct lscpu_cache *ca,
+                           int cols[], size_t ncols)
+{
+       struct libscols_line *ln;
+       size_t c;
+
+       ln = scols_table_new_line(tb, NULL);
+       if (!ln)
+               err(EXIT_FAILURE, _("failed to allocate output line"));
+
+       for (c = 0; c < ncols; c++) {
+               char *data = NULL;
+               int col = cols[c];
+
+               switch (col) {
+               case COL_CACHE_NAME:
+                       if (ca->name)
+                               data = xstrdup(ca->name);
+                       break;
+               case COL_CACHE_ONESIZE:
+                       if (!ca->size)
+                               break;
+                       if (cxt->bytes)
+                               xasprintf(&data, "%" PRIu64, ca->size);
+                       else
+                               data = size_to_human_string(SIZE_SUFFIX_1LETTER, ca->size);
+                       break;
+               case COL_CACHE_ALLSIZE:
+               {
+                       uint64_t sz = 0;
+                       if (ca->name)
+                               sz = lscpu_get_cache_full_size(cxt, ca->name, NULL);
+                       if (!sz)
+                               break;
+                       if (cxt->bytes)
+                               xasprintf(&data, "%" PRIu64, sz);
+                       else
+                               data = size_to_human_string(SIZE_SUFFIX_1LETTER, sz);
+                       break;
+               }
+               case COL_CACHE_WAYS:
+                       if (ca->ways_of_associativity)
+                               xasprintf(&data, "%u", ca->ways_of_associativity);
+                       break;
+
+               case COL_CACHE_TYPE:
+                       if (ca->type)
+                               data = xstrdup(ca->type);
+                       break;
+               case COL_CACHE_LEVEL:
+                       if (ca->level)
+                               xasprintf(&data, "%d", ca->level);
+                       break;
+               case COL_CACHE_ALLOCPOL:
+                       if (ca->allocation_policy)
+                               data = xstrdup(ca->allocation_policy);
+                       break;
+               case COL_CACHE_WRITEPOL:
+                       if (ca->write_policy)
+                               data = xstrdup(ca->write_policy);
+                       break;
+               case COL_CACHE_PHYLINE:
+                       if (ca->physical_line_partition)
+                               xasprintf(&data, "%u", ca->physical_line_partition);
+                       break;
+               case COL_CACHE_SETS:
+                       if (ca->number_of_sets)
+                               xasprintf(&data, "%u", ca->number_of_sets);
+                       break;
+               case COL_CACHE_COHERENCYSIZE:
+                       if (ca->coherency_line_size)
+                               xasprintf(&data, "%u", ca->coherency_line_size);
+                       break;
+               }
+
+               if (data && scols_line_refer_data(ln, c, data))
+                       err(EXIT_FAILURE, _("failed to add output data"));
+       }
+}
+
+
 /*
  * [-C] backend
  */
-static void
-print_caches_readable(struct lscpu_desc *desc, int cols[], int ncols,
-              struct lscpu_modifier *mod)
+static void print_caches_readable(struct lscpu_cxt *cxt, int cols[], size_t ncols)
 {
-       struct libscols_table *table;
-       struct cpu_cache *cachesrc;
-       int i, end, j, shared_allsize;
+       size_t i;
+       struct libscols_table *tb;
+       const char *last = NULL;
 
        scols_init_debug(0);
 
-       table = scols_new_table();
-       if (!table)
+       tb = scols_new_table();
+       if (!tb)
                 err(EXIT_FAILURE, _("failed to allocate output table"));
-       if (mod->json) {
-               scols_table_enable_json(table, 1);
-               scols_table_set_name(table, "caches");
+       if (cxt->json) {
+               scols_table_enable_json(tb, 1);
+               scols_table_set_name(tb, "caches");
        }
 
        for (i = 0; i < ncols; i++) {
                struct lscpu_coldesc *cd = &coldescs_cache[cols[i]];
-               if (!scols_table_new_column(table, cd->name, 0, cd->flags))
+               struct libscols_column *cl;
+
+               cl = scols_table_new_column(tb, cd->name, 0, cd->flags);
+               if (cl == NULL)
                        err(EXIT_FAILURE, _("failed to allocate output column"));
+               if (cxt->json)
+                       scols_column_set_json_type(cl, cd->json_type);
        }
 
-       for (j = 0; j < 2; j++) {
-               /* First check the caches from /sys/devices */
-               if (j == 0) {
-                       cachesrc = desc->caches;
-                       end = desc->ncaches - 1;
-                       shared_allsize = 0;
-               } else {
-                       /* Check shared caches from /proc/cpuinfo s390 */
-                       cachesrc = desc->ecaches;
-                       end = desc->necaches - 1;
-                       /* Dont use get_cache_full_size */
-                       shared_allsize = 1;
-               }
+       /* standard caches */
+       for (i = 0; i < cxt->ncaches; i++) {
+               struct lscpu_cache *ca = &cxt->caches[i];
 
-               for (i = end; i >= 0; i--) {
-                       struct libscols_line *line;
-                       struct cpu_cache *ca = &cachesrc[i];
-                       int c;
-
-                       line = scols_table_new_line(table, NULL);
-                       if (!line)
-                               err(EXIT_FAILURE, _("failed to allocate output line"));
-
-                       for (c = 0; c < ncols; c++) {
-                               char *data = NULL;
-
-                               switch (cols[c]) {
-                               case COL_CACHE_NAME:
-                                       if (ca->name)
-                                               data = xstrdup(ca->name);
-                                       break;
-                               case COL_CACHE_ONESIZE:
-                                       if (!ca->size)
-                                               break;
-                                       if (mod->bytes)
-                                               xasprintf(&data, "%" PRIu64, ca->size);
-                                       else
-                                               data = size_to_human_string(SIZE_SUFFIX_1LETTER, ca->size);
-                                       break;
-                               case COL_CACHE_ALLSIZE:
-                               {
-                                       uint64_t sz = 0;
-                                       if (shared_allsize)
-                                               break;
-                                       if (get_cache_full_size(desc, ca, &sz) != 0)
-                                               break;
-                                       if (mod->bytes)
-                                               xasprintf(&data, "%" PRIu64, sz);
-                                       else
-                                               data = size_to_human_string(SIZE_SUFFIX_1LETTER, sz);
-                                       break;
-                               }
-                               case COL_CACHE_WAYS:
-                                       if (ca->ways_of_associativity)
-                                               xasprintf(&data, "%u", ca->ways_of_associativity);
-                                       break;
-                               case COL_CACHE_TYPE:
-                                       if (ca->type)
-                                               data = xstrdup(ca->type);
-                                       break;
-                               case COL_CACHE_LEVEL:
-                                       if (ca->level)
-                                               xasprintf(&data, "%d", ca->level);
-                                       break;
-                               case COL_CACHE_ALLOCPOL:
-                                       if (ca->allocation_policy)
-                                               data = xstrdup(ca->allocation_policy);
-                                       break;
-                               case COL_CACHE_WRITEPOL:
-                                       if (ca->write_policy)
-                                               data = xstrdup(ca->write_policy);
-                                       break;
-                               case COL_CACHE_PHYLINE:
-                                       if (ca->physical_line_partition)
-                                               xasprintf(&data, "%u", ca->physical_line_partition);
-                                       break;
-                               case COL_CACHE_SETS:
-                                       if (ca->number_of_sets)
-                                               xasprintf(&data, "%u", ca->number_of_sets);
-                                       break;
-                               case COL_CACHE_COHERENCYSIZE:
-                                       if (ca->coherency_line_size)
-                                               xasprintf(&data, "%u", ca->coherency_line_size);
-                                       break;
-                               }
-
-                               if (data && scols_line_refer_data(line, c, data))
-                                       err(EXIT_FAILURE, _("failed to add output data"));
-                       }
-               }
+               if (last && strcmp(last, ca->name) == 0)
+                       continue;
+               last = ca->name;
+               caches_add_line(cxt, tb, ca, cols, ncols);
+       }
+
+       /* extra caches */
+       for (i = 0; i < cxt->necaches; i++) {
+               struct lscpu_cache *ca = &cxt->ecaches[i];
 
+               if (last && strcmp(last, ca->name) == 0)
+                       continue;
+               last = ca->name;
+               caches_add_line(cxt, tb, ca, cols, ncols);
        }
 
-       scols_print_table(table);
-       scols_unref_table(table);
+       scols_print_table(tb);
+       scols_unref_table(tb);
 }
 
 /*
@@ -603,12 +657,10 @@ print_caches_readable(struct lscpu_desc *desc, int cols[], int ncols,
  *     0,0,0,0,0:0:0
  *     1,1,0,0,1:1:0
  */
-static void
-print_cpus_parsable(struct lscpu_desc *desc, int cols[], int ncols,
-              struct lscpu_modifier *mod)
+static void print_cpus_parsable(struct lscpu_cxt *cxt, int cols[], size_t ncols)
 {
        char buf[BUFSIZ], *data;
-       int i;
+       size_t i;
 
        /*
         * Header
@@ -616,23 +668,22 @@ print_cpus_parsable(struct lscpu_desc *desc, int cols[], int ncols,
        printf(_(
        "# The following is the parsable format, which can be fed to other\n"
        "# programs. Each different item in every column has an unique ID\n"
-       "# starting from zero.\n"));
+       "# starting usually from zero.\n"));
 
        fputs("# ", stdout);
        for (i = 0; i < ncols; i++) {
                int col = cols[i];
 
                if (col == COL_CPU_CACHE) {
-                       if (mod->compat && !desc->ncaches)
+                       if (cxt->show_compatible && !cxt->ncaches)
                                continue;
-                       if (mod->compat && i != 0)
+                       if (cxt->show_compatible && i != 0)
                                putchar(',');
                }
                if (i > 0)
                        putchar(',');
 
-               data = get_cell_header(desc, col, mod, buf, sizeof(buf));
-
+               data = get_cell_header(cxt, col, buf, sizeof(buf));
                if (data && * data && col != COL_CPU_CACHE &&
                    !coldescs_cpu[col].is_abbr) {
                        /*
@@ -652,21 +703,22 @@ print_cpus_parsable(struct lscpu_desc *desc, int cols[], int ncols,
        /*
         * Data
         */
-       for (i = 0; i < desc->ncpuspos; i++) {
-               int c;
-               int cpu = real_cpu_num(desc, i);
+       for (i = 0; i < cxt->npossibles; i++) {
+               struct lscpu_cpu *cpu = cxt->cpus[i];
+               size_t c;
 
-               if (desc->online) {
-                       if (!mod->offline && !is_cpu_online(desc, cpu))
+               if (cxt->online) {
+                       if (!cxt->show_offline && !is_cpu_online(cxt, cpu))
                                continue;
-                       if (!mod->online && is_cpu_online(desc, cpu))
+                       if (!cxt->show_online && is_cpu_online(cxt, cpu))
                                continue;
                }
-               if (desc->present && !is_cpu_present(desc, cpu))
+               if (cxt->present && !is_cpu_present(cxt, cpu))
                        continue;
+
                for (c = 0; c < ncols; c++) {
-                       if (mod->compat && cols[c] == COL_CPU_CACHE) {
-                               if (!desc->ncaches)
+                       if (cxt->show_compatible && cols[c] == COL_CPU_CACHE) {
+                               if (!cxt->ncaches)
                                        continue;
                                if (c > 0)
                                        putchar(',');
@@ -674,8 +726,7 @@ print_cpus_parsable(struct lscpu_desc *desc, int cols[], int ncols,
                        if (c > 0)
                                putchar(',');
 
-                       data = get_cell_data(desc, i, cols[c], mod,
-                                            buf, sizeof(buf));
+                       data = get_cell_data(cxt, cpu, cols[c], buf, sizeof(buf));
                        fputs(data && *data ? data : "", stdout);
                        *buf = '\0';
                }
@@ -686,77 +737,83 @@ print_cpus_parsable(struct lscpu_desc *desc, int cols[], int ncols,
 /*
  * [-e] backend
  */
-static void
-print_cpus_readable(struct lscpu_desc *desc, int cols[], int ncols,
-              struct lscpu_modifier *mod)
+static void print_cpus_readable(struct lscpu_cxt *cxt, int cols[], size_t ncols)
 {
-       int i;
+       size_t i;
        char buf[BUFSIZ];
        const char *data;
-       struct libscols_table *table;
+       struct libscols_table *tb;
 
        scols_init_debug(0);
 
-       table = scols_new_table();
-       if (!table)
+       tb = scols_new_table();
+       if (!tb)
                 err(EXIT_FAILURE, _("failed to allocate output table"));
-       if (mod->json) {
-               scols_table_enable_json(table, 1);
-               scols_table_set_name(table, "cpus");
+       if (cxt->json) {
+               scols_table_enable_json(tb, 1);
+               scols_table_set_name(tb, "cpus");
        }
 
        for (i = 0; i < ncols; i++) {
-               data = get_cell_header(desc, cols[i], mod, buf, sizeof(buf));
-               if (!scols_table_new_column(table, data, 0, coldescs_cpu[cols[i]].flags))
+               data = get_cell_header(cxt, cols[i], buf, sizeof(buf));
+               struct lscpu_coldesc *cd = &coldescs_cpu[cols[i]];
+               struct libscols_column *cl;
+
+               cl = scols_table_new_column(tb, data, 0, cd->flags);
+               if (cl == NULL)
                        err(EXIT_FAILURE, _("failed to allocate output column"));
+               if (cxt->json)
+                       scols_column_set_json_type(cl, cd->json_type);
        }
 
-       for (i = 0; i < desc->ncpuspos; i++) {
-               int c;
-               struct libscols_line *line;
-               int cpu = real_cpu_num(desc, i);
+       for (i = 0; i < cxt->npossibles; i++) {
+               size_t c;
+               struct libscols_line *ln;
+               struct lscpu_cpu *cpu = cxt->cpus[i];
 
-               if (desc->online) {
-                       if (!mod->offline && !is_cpu_online(desc, cpu))
+               if (cxt->online) {
+                       if (!cxt->show_offline && !is_cpu_online(cxt, cpu))
                                continue;
-                       if (!mod->online && is_cpu_online(desc, cpu))
+                       if (!cxt->show_online && is_cpu_online(cxt, cpu))
                                continue;
                }
-               if (desc->present && !is_cpu_present(desc, cpu))
+
+               if (cxt->present && !is_cpu_present(cxt, cpu))
                        continue;
 
-               line = scols_table_new_line(table, NULL);
-               if (!line)
+               ln = scols_table_new_line(tb, NULL);
+               if (!ln)
                        err(EXIT_FAILURE, _("failed to allocate output line"));
 
                for (c = 0; c < ncols; c++) {
-                       data = get_cell_data(desc, i, cols[c], mod,
-                                            buf, sizeof(buf));
+                       data = get_cell_data(cxt, cpu, cols[c], buf, sizeof(buf));
                        if (!data || !*data)
                                data = "-";
-                       if (scols_line_set_data(line, c, data))
+                       if (scols_line_set_data(ln, c, data))
                                err(EXIT_FAILURE, _("failed to add output data"));
                }
        }
 
-       scols_print_table(table);
-       scols_unref_table(table);
+       scols_print_table(tb);
+       scols_unref_table(tb);
 }
 
-#endif
-
 static struct libscols_line *
-       __attribute__ ((__format__(printf, 3, 4)))
+       __attribute__ ((__format__(printf, 4, 5)))
        add_summary_sprint(struct libscols_table *tb,
                        struct libscols_line *sec,
                        const char *txt,
                        const char *fmt,
                        ...)
 {
-       struct libscols_line *ln = scols_table_new_line(tb, sec);
-       char *data;
+       struct libscols_line *ln;
        va_list args;
 
+       /* Don't print section lines without data */
+       if (!hierarchic && fmt == NULL)
+               return NULL;
+
+       ln = scols_table_new_line(tb, sec);
        if (!ln)
                err(EXIT_FAILURE, _("failed to allocate output line"));
 
@@ -765,16 +822,20 @@ static struct libscols_line *
                err(EXIT_FAILURE, _("failed to add output data"));
 
        /* data column */
-       va_start(args, fmt);
-       xvasprintf(&data, fmt, args);
-       va_end(args);
-
-       if (data && scols_line_refer_data(ln, 1, data))
-                err(EXIT_FAILURE, _("failed to add output data"));
+       if (fmt) {
+               char *data;
+               va_start(args, fmt);
+               xvasprintf(&data, fmt, args);
+               va_end(args);
+
+               if (data && scols_line_refer_data(ln, 1, data))
+                       err(EXIT_FAILURE, _("failed to add output data"));
+       }
 
        return ln;
 }
 
+#define add_summary_e(tb, sec, txt)            add_summary_sprint(tb, sec, txt, NULL)
 #define add_summary_n(tb, sec, txt, num)       add_summary_sprint(tb, sec, txt, "%zu", num)
 #define add_summary_s(tb, sec, txt, str)       add_summary_sprint(tb, sec, txt, "%s", str)
 #define add_summary_x(tb, sec, txt, fmt, x)    add_summary_sprint(tb, sec, txt, fmt, x)
@@ -782,82 +843,108 @@ static struct libscols_line *
 static void
 print_cpuset(struct lscpu_cxt *cxt,
             struct libscols_table *tb,
+            struct libscols_line *sec,
             const char *key, cpu_set_t *set)
 {
        size_t setbuflen = 7 * cxt->maxcpus;
-       char setbuf[setbuflen], *p;
+       char *setbuf, *p;
 
        assert(set);
        assert(key);
        assert(tb);
        assert(cxt);
 
+       setbuf = xmalloc(setbuflen);
+
        if (cxt->hex) {
                p = cpumask_create(setbuf, setbuflen, set, cxt->setsize);
-               add_summary_s(tb, key, p);
+               add_summary_s(tb, sec, key, p);
        } else {
                p = cpulist_create(setbuf, setbuflen, set, cxt->setsize);
-               add_summary_s(tb, key, p);
+               add_summary_s(tb, sec, key, p);
        }
+
+       free(setbuf);
 }
 
 static void
 print_summary_cputype(struct lscpu_cxt *cxt,
                     struct lscpu_cputype *ct,
-                    struct libscols_table *tb)
+                    struct libscols_table *tb,
+                    struct libscols_line *sec)
 {
-       if (ct->vendor)
-               add_summary_s(tb, _("Vendor ID:"), ct->vendor);
+       sec = add_summary_s(tb, sec, _("Model name:"), ct->modelname ? ct->modelname : "-");
+       if (ct->bios_modelname)
+               add_summary_s(tb, sec, _("BIOS Model name:"), ct->bios_modelname);
+       if (ct->bios_family)
+               add_summary_s(tb, sec, _("BIOS CPU family:"), ct->bios_family);
        if (ct->machinetype)
-               add_summary_s(tb, _("Machine type:"), ct->machinetype);
+               add_summary_s(tb, sec, _("Machine type:"), ct->machinetype);
        if (ct->family)
-               add_summary_s(tb, _("CPU family:"), ct->family);
-       if (ct->modelname)
-               add_summary_s(tb, _("Model name:"), ct->modelname);
+               add_summary_s(tb, sec, _("CPU family:"), ct->family);
        if (ct->model || ct->revision)
-               add_summary_s(tb, _("Model:"), ct->revision ? ct->revision : ct->model);
+               add_summary_s(tb, sec, _("Model:"), ct->revision ? ct->revision : ct->model);
+
+       add_summary_n(tb, sec, _("Thread(s) per core:"), ct->nthreads_per_core);
+       if (cxt->is_cluster)
+               add_summary_n(tb, sec, _("Core(s) per cluster:"), ct->ncores_per_socket);
+       else
+               add_summary_n(tb, sec, _("Core(s) per socket:"), ct->ncores_per_socket);
 
-       add_summary_n(tb, _("Thread(s) per core:"), ct->nthreads_per_core);
-       add_summary_n(tb, _("Core(s) per socket:"), ct->ncores_per_socket);
        if (ct->nbooks) {
-               add_summary_n(tb, _("Socket(s) per book:"), ct->nsockets_per_book);
-               if (ct->ndrawers_per_system) {
-                       add_summary_n(tb, _("Book(s) per drawer:"), ct->nbooks_per_drawer);
-                       add_summary_n(tb, _("Drawer(s):"), ct->ndrawers_per_system);
+               add_summary_n(tb, sec, _("Socket(s) per book:"), ct->nsockets_per_book);
+               if (ct->ndrawers_per_system || ct->ndrawers) {
+                       add_summary_n(tb, sec, _("Book(s) per drawer:"), ct->nbooks_per_drawer);
+                       add_summary_n(tb, sec, _("Drawer(s):"), ct->ndrawers_per_system ?: ct->ndrawers);
                } else
-                       add_summary_n(tb, _("Book(s):"), ct->nbooks);
-       } else
-               add_summary_n(tb, _("Socket(s):"), ct->nsockets);
+                       add_summary_n(tb, sec, _("Book(s):"), ct->nbooks_per_drawer ?: ct->nbooks);
+       } else {
+               if (cxt->is_cluster) {
+                       if (ct->nr_socket_on_cluster > 0)
+                               add_summary_n(tb, sec, _("Socket(s):"), ct->nr_socket_on_cluster);
+                       else
+                               add_summary_s(tb, sec, _("Socket(s):"), "-");
+
+                       add_summary_n(tb, sec, _("Cluster(s):"),
+                                       ct->nsockets_per_book ?: ct->nsockets);
+               } else
+                       add_summary_n(tb, sec, _("Socket(s):"),
+                                       ct->nsockets_per_book ?: ct->nsockets);
+       }
 
        if (ct->stepping)
-               add_summary_s(tb, _("Stepping:"), ct->stepping);
+               add_summary_s(tb, sec, _("Stepping:"), ct->stepping);
        if (ct->freqboost >= 0)
-               add_summary_s(tb, _("Frequency boost:"), ct->freqboost ?
+               add_summary_s(tb, sec, _("Frequency boost:"), ct->freqboost ?
                                _("enabled") : _("disabled"));
 
        /* s390 -- from the first CPU where is dynamic/static MHz */
        if (ct->dynamic_mhz)
-               add_summary_s(tb, _("CPU dynamic MHz:"), ct->dynamic_mhz);
+               add_summary_s(tb, sec, _("CPU dynamic MHz:"), ct->dynamic_mhz);
        if (ct->static_mhz)
-               add_summary_s(tb, _("CPU static MHz:"), ct->static_mhz);
+               add_summary_s(tb, sec, _("CPU static MHz:"), ct->static_mhz);
 
        if (ct->has_freq) {
-               add_summary_x(tb, _("CPU max MHz:"), "%.4f", lsblk_cputype_get_maxmhz(cxt, ct));
-               add_summary_x(tb, _("CPU min MHz:"), "%.4f", lsblk_cputype_get_minmhz(cxt, ct));
+               float scal = lsblk_cputype_get_scalmhz(cxt, ct);
+               if (scal > 0.0)
+                       add_summary_x(tb, sec, _("CPU(s) scaling MHz:"), "%.0f%%", scal);
+               add_summary_x(tb, sec, _("CPU max MHz:"), "%.4f", lsblk_cputype_get_maxmhz(cxt, ct));
+               add_summary_x(tb, sec, _("CPU min MHz:"), "%.4f", lsblk_cputype_get_minmhz(cxt, ct));
        }
        if (ct->bogomips)
-               add_summary_s(tb, _("BogoMIPS:"), ct->bogomips);
+               add_summary_x(tb, sec, _("BogoMIPS:"), "%.2f",  (float) c_strtod(ct->bogomips, NULL));
+
        if (ct->dispatching >= 0)
-               add_summary_s(tb, _("Dispatching mode:"), _(disp_modes[ct->dispatching]));
+               add_summary_s(tb, sec, _("Dispatching mode:"), _(disp_modes[ct->dispatching]));
 
        if (ct->physsockets) {
-               add_summary_n(tb, _("Physical sockets:"), ct->physsockets);
-               add_summary_n(tb, _("Physical chips:"), ct->physchips);
-               add_summary_n(tb, _("Physical cores/chip:"), ct->physcoresperchip);
+               add_summary_n(tb, sec, _("Physical sockets:"), ct->physsockets);
+               add_summary_n(tb, sec, _("Physical chips:"), ct->physchips);
+               add_summary_n(tb, sec, _("Physical cores/chip:"), ct->physcoresperchip);
        }
 
        if (ct->flags)
-               add_summary_s(tb, _("Flags:"), ct->flags);
+               add_summary_s(tb, sec, _("Flags:"), ct->flags);
 }
 
 /*
@@ -866,9 +953,11 @@ print_summary_cputype(struct lscpu_cxt *cxt,
 static void print_summary(struct lscpu_cxt *cxt)
 {
        struct lscpu_cputype *ct;
+       char field[256];
        size_t i = 0;
        struct libscols_table *tb;
-       struct libscols_line *sec = NULL;
+       struct libscols_line *sec;
+       int hdr_caches = 0;
 
        scols_init_debug(0);
 
@@ -880,17 +969,27 @@ static void print_summary(struct lscpu_cxt *cxt)
        if (cxt->json) {
                scols_table_enable_json(tb, 1);
                scols_table_set_name(tb, "lscpu");
+       } else if (hierarchic) {
+               struct libscols_symbols *sy = scols_new_symbols();
+
+               if (!sy)
+                       err_oom();
+               scols_symbols_set_branch(sy, "  ");
+               scols_symbols_set_vertical(sy, "  ");
+               scols_symbols_set_right(sy, "  ");
+               scols_table_set_symbols(tb, sy);
+               scols_unref_symbols(sy);
        }
 
-       if (scols_table_new_column(tb, "field", 0, 0) == NULL ||
+       if (scols_table_new_column(tb, "field", 0, hierarchic ? SCOLS_FL_TREE : 0) == NULL ||
            scols_table_new_column(tb, "data", 0, SCOLS_FL_NOEXTREMES | SCOLS_FL_WRAP) == NULL)
                err(EXIT_FAILURE, _("failed to initialize output column"));
 
        ct = lscpu_cputype_get_default(cxt);
 
-       if (cxt->arch)
-               sec = add_summary_s(tb, NULL, _("Architecture:"), cxt->arch->name);
-       if (cxt->arch && (cxt->arch->bit32 || cxt->arch->bit64)) {
+       /* Section: architecture */
+       sec = add_summary_s(tb, NULL, _("Architecture:"), cxt->arch->name);
+       if (cxt->arch->bit32 || cxt->arch->bit64) {
                char buf[32], *p = buf;
 
                if (cxt->arch->bit32) {
@@ -904,15 +1003,19 @@ static void print_summary(struct lscpu_cxt *cxt)
                *(p - 2) = '\0';
                add_summary_s(tb, sec, _("CPU op-mode(s):"), buf);
        }
-       if (ct->addrsz)
+       if (ct && ct->addrsz)
                add_summary_s(tb, sec, _("Address sizes:"), ct->addrsz);
-#if !defined(WORDS_BIGENDIAN)
-       add_summary_s(tb, sec, _("Byte Order:"), "Little Endian");
-#else
-       add_summary_s(tb, sec, _("Byte Order:"), "Big Endian");
-#endif
+
+       if (sysfs_get_byteorder(cxt->rootfs) == SYSFS_BYTEORDER_LITTLE)
+               add_summary_s(tb, sec, _("Byte Order:"), "Little Endian");
+       else
+               add_summary_s(tb, sec, _("Byte Order:"), "Big Endian");
+
+       /* Section: CPU lists */
+       sec = add_summary_n(tb, NULL, _("CPU(s):"), cxt->npresents);
+
        if (cxt->online)
-               print_cpuset(cxt, tb, NULL,
+               print_cpuset(cxt, tb, sec,
                                cxt->hex ? _("On-line CPU(s) mask:") :
                                           _("On-line CPU(s) list:"),
                                cxt->online);
@@ -934,19 +1037,26 @@ static void print_summary(struct lscpu_cxt *cxt)
                        if (cpu && is_cpu_present(cxt, cpu) && !is_cpu_online(cxt, cpu))
                                CPU_SET_S(cpu->logical_id, cxt->setsize, set);
                }
-               print_cpuset(cxt, tb, NULL,
+               print_cpuset(cxt, tb, sec,
                                cxt->hex ? _("Off-line CPU(s) mask:") :
                                           _("Off-line CPU(s) list:"), set);
                cpuset_free(set);
        }
+       sec = NULL;
 
-       sec = add_summary_n(tb, NULL, _("CPU(s):"), cxt->npresents);
+       /* Section: cpu type description */
+       if (ct && ct->vendor)
+               sec = add_summary_s(tb, NULL, _("Vendor ID:"), ct->vendor);
+       if (ct && ct->bios_vendor)
+               add_summary_s(tb, sec, _("BIOS Vendor ID:"), ct->bios_vendor);
 
        for (i = 0; i < cxt->ncputypes; i++)
-               print_summary_cputype(cxt, cxt->cputypes[i], tb);
+               print_summary_cputype(cxt, cxt->cputypes[i], tb, sec);
+       sec = NULL;
 
+       /* Section: vitualiazation */
        if (cxt->virt) {
-               sec = add_summary_s(tb, NULL, _("Virtualization features:"), NULL);
+               sec = add_summary_e(tb, NULL, _("Virtualization features:"));
                if (cxt->virt->cpuflag && !strcmp(cxt->virt->cpuflag, "svm"))
                        add_summary_s(tb, sec, _("Virtualization:"), "AMD-V");
                else if (cxt->virt->cpuflag && !strcmp(cxt->virt->cpuflag, "vmx"))
@@ -960,87 +1070,93 @@ static void print_summary(struct lscpu_cxt *cxt)
                }
                sec = NULL;
        }
+
+       /* Section: caches */
        if (cxt->ncaches) {
                const char *last = NULL;
-               char hdr[256];
 
                /* The caches are sorted by name, cxt->caches[] may contains
                 * multiple instances for the same name.
                 */
-               sec = add_summary_s(tb, NULL, _("Caches:"), NULL);
-
                for (i = 0; i < cxt->ncaches; i++) {
                        const char *name = cxt->caches[i].name;
                        uint64_t sz;
+                       int n = 0;
 
                        if (last && strcmp(last, name) == 0)
                                continue;
-                       sz = lscpu_get_cache_full_size(cxt, name);
+                       sz = lscpu_get_cache_full_size(cxt, name, &n);
                        if (!sz)
                                continue;
-                       snprintf(hdr, sizeof(hdr), _("%s cache:"), name);
+                       if (!hdr_caches) {
+                               sec = add_summary_e(tb, NULL, _("Caches (sum of all):"));
+                               hdr_caches = 1;
+                       }
 
+                       snprintf(field, sizeof(field), hierarchic ? _("%s:") : _("%s cache:"), name);
                        if (cxt->bytes)
-                               add_summary_x(tb, sec, hdr, "%" PRIu64, sz);
+                               add_summary_sprint(tb, sec, field,
+                                               P_("%" PRIu64 " (%d instance)",
+                                                  "%" PRIu64 " (%d instances)", n),
+                                               sz, n);
                        else {
                                char *tmp = size_to_human_string(
                                                SIZE_SUFFIX_3LETTER |
                                                SIZE_SUFFIX_SPACE,
                                                sz);
-                               add_summary_s(tb, sec, hdr, tmp);
+                               add_summary_sprint(tb, sec, field,
+                                               P_("%s (%d instance)",
+                                                  "%s (%d instances)", n),
+                                               tmp, n);
                                free(tmp);
                        }
                        last = name;
                }
-               sec = NULL;
        }
 
-       /* Extra caches (s390, ...) */
-       if (cxt->necaches) {
-               char hdr[256];
-
-               sec = add_summary_s(tb, NULL, _("Extra caches:"), NULL);
-
-               for (i = 0; i < cxt->necaches; i++) {
-                       struct lscpu_cache *ca = &cxt->ecaches[i];
+       for (i = 0; i < cxt->necaches; i++) {
+               struct lscpu_cache *ca = &cxt->ecaches[i];
 
-                       if (ca->size == 0)
-                               continue;
-                       snprintf(hdr, sizeof(hdr), _("%s cache:"), ca->name);
-                       if (cxt->bytes)
-                               add_summary_x(tb, sec, hdr, "%" PRIu64, ca->size);
-                       else {
-                               char *tmp = size_to_human_string(
-                                               SIZE_SUFFIX_3LETTER |
-                                               SIZE_SUFFIX_SPACE,
-                                               ca->size);
-                               add_summary_s(tb, sec, hdr, tmp);
-                               free(tmp);
-                       }
+               if (ca->size == 0)
+                       continue;
+               if (!hdr_caches) {
+                       sec = add_summary_e(tb, NULL, _("Caches:"));
+                       hdr_caches = 1;
+               }
+               snprintf(field, sizeof(field), hierarchic ? _("%s:") : _("%s cache:"), ca->name);
+               if (cxt->bytes)
+                       add_summary_x(tb, sec, field, "%" PRIu64, ca->size);
+               else {
+                       char *tmp = size_to_human_string(
+                                       SIZE_SUFFIX_3LETTER |
+                                       SIZE_SUFFIX_SPACE,
+                                       ca->size);
+                       add_summary_s(tb, sec, field, tmp);
+                       free(tmp);
                }
        }
+       sec = NULL;
 
+       /* Section: NUMA modes */
        if (cxt->nnodes) {
-               char buf[256];
-
-               sec = add_summary_s(tb, NULL, _("NUMA:"), NULL);
+               sec = add_summary_e(tb, NULL, _("NUMA:"));
 
                add_summary_n(tb, sec,_("NUMA node(s):"), cxt->nnodes);
                for (i = 0; i < cxt->nnodes; i++) {
-                       snprintf(buf, sizeof(buf), _("NUMA node%d CPU(s):"), cxt->idx2nodenum[i]);
-                       print_cpuset(cxt, tb, sec, buf, cxt->nodemaps[i]);
+                       snprintf(field, sizeof(field), _("NUMA node%d CPU(s):"), cxt->idx2nodenum[i]);
+                       print_cpuset(cxt, tb, sec, field, cxt->nodemaps[i]);
                }
                sec = NULL;
        }
 
+       /* Section: Vulnerabilities */
        if (cxt->vuls) {
-               char buf[256];
-
-               sec = add_summary_s(tb, NULL, _("Vulnerabilities:"), NULL);
+               sec = add_summary_e(tb, NULL, _("Vulnerabilities:"));
 
                for (i = 0; i < cxt->nvuls; i++) {
-                       snprintf(buf, sizeof(buf), (" %s:"), cxt->vuls[i].name);
-                       add_summary_s(tb, sec, buf, cxt->vuls[i].text);
+                       snprintf(field, sizeof(field), hierarchic ?
+                                       _("%s:") : _("Vulnerability %s:"), cxt->vuls[i].name);
+                       add_summary_s(tb, sec, field, cxt->vuls[i].text);
                }
                sec = NULL;
        }
@@ -1071,9 +1187,10 @@ static void __attribute__((__noreturn__)) usage(void)
        fputs(_(" -s, --sysroot <dir>     use specified directory as system root\n"), out);
        fputs(_(" -x, --hex               print hexadecimal masks rather than lists of CPUs\n"), out);
        fputs(_(" -y, --physical          print physical instead of logical IDs\n"), out);
+       fputs(_("     --hierarchic[=when] use subsections in summary (auto, never, always)\n"), out);
        fputs(_("     --output-all        print all available columns for -e, -p or -C\n"), out);
        fputs(USAGE_SEPARATOR, out);
-       printf(USAGE_HELP_OPTIONS(25));
+       fprintf(out, USAGE_HELP_OPTIONS(25));
 
        fputs(_("\nAvailable output columns for -e or -p:\n"), out);
        for (i = 0; i < ARRAY_SIZE(coldescs_cpu); i++)
@@ -1083,7 +1200,7 @@ static void __attribute__((__noreturn__)) usage(void)
        for (i = 0; i < ARRAY_SIZE(coldescs_cache); i++)
                fprintf(out, " %13s  %s\n", coldescs_cache[i].name, _(coldescs_cache[i].help));
 
-       printf(USAGE_MAN_TAIL("lscpu(1)"));
+       fprintf(out, USAGE_MAN_TAIL("lscpu(1)"));
 
        exit(EXIT_SUCCESS);
 }
@@ -1092,11 +1209,13 @@ int main(int argc, char *argv[])
 {
        struct lscpu_cxt *cxt;
        int c, all = 0;
-       int columns[ARRAY_SIZE(coldescs_cpu)], ncolumns = 0;
+       int columns[ARRAY_SIZE(coldescs_cpu)];
        int cpu_modifier_specified = 0;
-       size_t i;
+       char *outarg = NULL;
+       size_t i, ncolumns = 0;
        enum {
                OPT_OUTPUT_ALL = CHAR_MAX + 1,
+               OPT_HIERARCHIC,
        };
        static const struct option longopts[] = {
                { "all",        no_argument,       NULL, 'a' },
@@ -1113,6 +1232,7 @@ int main(int argc, char *argv[])
                { "hex",        no_argument,       NULL, 'x' },
                { "version",    no_argument,       NULL, 'V' },
                { "output-all", no_argument,       NULL, OPT_OUTPUT_ALL },
+               { "hierarchic", optional_argument, NULL, OPT_HIERARCHIC },
                { NULL,         0, NULL, 0 }
        };
 
@@ -1154,11 +1274,7 @@ int main(int argc, char *argv[])
                        if (optarg) {
                                if (*optarg == '=')
                                        optarg++;
-                               ncolumns = string_to_idarray(optarg,
-                                               columns, ARRAY_SIZE(columns),
-                                               cache_column_name_to_id);
-                               if (ncolumns < 0)
-                                       return EXIT_FAILURE;
+                               outarg = optarg;
                        }
                        cxt->mode = LSCPU_OUTPUT_CACHES;
                        break;
@@ -1170,11 +1286,7 @@ int main(int argc, char *argv[])
                        if (optarg) {
                                if (*optarg == '=')
                                        optarg++;
-                               ncolumns = string_to_idarray(optarg,
-                                               columns, ARRAY_SIZE(columns),
-                                               cpu_column_name_to_id);
-                               if (ncolumns < 0)
-                                       return EXIT_FAILURE;
+                               outarg = optarg;
                        }
                        cxt->mode = c == 'p' ? LSCPU_OUTPUT_PARSABLE : LSCPU_OUTPUT_READABLE;
                        break;
@@ -1191,7 +1303,19 @@ int main(int argc, char *argv[])
                case OPT_OUTPUT_ALL:
                        all = 1;
                        break;
-
+               case OPT_HIERARCHIC:
+                       if (optarg) {
+                               if (strcmp(optarg, "auto") == 0)
+                                       hierarchic = -1;
+                               else if (strcmp(optarg, "never") == 0)
+                                       hierarchic = 0;
+                               else if (strcmp(optarg, "always") == 0)
+                                       hierarchic = 1;
+                               else
+                                       errx(EXIT_FAILURE, _("unsupported --flat argument"));
+                       } else
+                               hierarchic = 1;
+                       break;
                case 'h':
                        usage();
                case 'V':
@@ -1229,6 +1353,7 @@ int main(int argc, char *argv[])
                cxt->show_offline = cxt->mode == LSCPU_OUTPUT_READABLE ? 1 : 0;
        }
 
+
        lscpu_init_debug();
 
        lscpu_context_init_paths(cxt);
@@ -1246,13 +1371,13 @@ int main(int argc, char *argv[])
 
        cxt->virt = lscpu_read_virtualization(cxt);
 
+       if (hierarchic == -1)
+               hierarchic = isatty(STDOUT_FILENO);     /* default */
+
        switch(cxt->mode) {
        case LSCPU_OUTPUT_SUMMARY:
                print_summary(cxt);
                break;
-       }
-
-#ifdef LSCPU_OLD_OUTPUT_CODE
        case LSCPU_OUTPUT_CACHES:
                if (!ncolumns) {
                        columns[ncolumns++] = COL_CACHE_NAME;
@@ -1265,53 +1390,78 @@ int main(int argc, char *argv[])
                        columns[ncolumns++] = COL_CACHE_PHYLINE;
                        columns[ncolumns++] = COL_CACHE_COHERENCYSIZE;
                }
-               print_caches_readable(desc, columns, ncolumns, mod);
-               break;
-       case LSCPU_OUTPUT_PARSABLE:
-               if (!ncolumns) {
-                       columns[ncolumns++] = COL_CPU_CPU;
-                       columns[ncolumns++] = COL_CPU_CORE;
-                       columns[ncolumns++] = COL_CPU_SOCKET;
-                       columns[ncolumns++] = COL_CPU_NODE;
-                       columns[ncolumns++] = COL_CPU_CACHE;
-                       mod->compat = 1;
-               }
-               print_cpus_parsable(desc, columns, ncolumns, mod);
+               if (outarg && string_add_to_idarray(outarg, columns,
+                                       ARRAY_SIZE(columns),
+                                       &ncolumns, cache_column_name_to_id) < 0)
+                       return EXIT_FAILURE;
+
+               print_caches_readable(cxt, columns, ncolumns);
                break;
        case LSCPU_OUTPUT_READABLE:
                if (!ncolumns) {
                        /* No list was given. Just print whatever is there. */
+                       struct lscpu_cputype *ct = lscpu_cputype_get_default(cxt);
+
                        columns[ncolumns++] = COL_CPU_CPU;
-                       if (desc->nodemaps)
+                       if (cxt->nnodes)
                                columns[ncolumns++] = COL_CPU_NODE;
-                       if (desc->drawermaps)
+                       if (ct && ct->ndrawers)
                                columns[ncolumns++] = COL_CPU_DRAWER;
-                       if (desc->bookmaps)
+                       if (ct && ct->nbooks)
                                columns[ncolumns++] = COL_CPU_BOOK;
-                       if (desc->socketmaps)
-                               columns[ncolumns++] = COL_CPU_SOCKET;
-                       if (desc->coremaps)
+                       if (ct && ct->nsockets) {
+                               if (cxt->is_cluster)
+                                       columns[ncolumns++] = COL_CPU_CLUSTER;
+                               else
+                                       columns[ncolumns++] = COL_CPU_SOCKET;
+                       }
+                       if (ct && ct->ncores)
                                columns[ncolumns++] = COL_CPU_CORE;
-                       if (desc->caches)
+                       if (cxt->ncaches)
                                columns[ncolumns++] = COL_CPU_CACHE;
-                       if (desc->online)
+                       if (cxt->online)
                                columns[ncolumns++] = COL_CPU_ONLINE;
-                       if (desc->configured)
+                       if (ct && ct->has_configured)
                                columns[ncolumns++] = COL_CPU_CONFIGURED;
-                       if (desc->polarization)
+                       if (ct && ct->has_polarization)
                                columns[ncolumns++] = COL_CPU_POLARIZATION;
-                       if (desc->addresses)
+                       if (ct && ct->has_addresses)
                                columns[ncolumns++] = COL_CPU_ADDRESS;
-                       if (desc->maxmhz)
+                       if (ct && ct->has_freq) {
                                columns[ncolumns++] = COL_CPU_MAXMHZ;
-                       if (desc->minmhz)
                                columns[ncolumns++] = COL_CPU_MINMHZ;
+                               columns[ncolumns++] = COL_CPU_MHZ;
+                       }
                }
-               print_cpus_readable(desc, columns, ncolumns, mod);
+               if (outarg && string_add_to_idarray(outarg, columns,
+                                       ARRAY_SIZE(columns),
+                                       &ncolumns, cpu_column_name_to_id) < 0)
+                       return EXIT_FAILURE;
+               print_cpus_readable(cxt, columns, ncolumns);
                break;
-       }
+       case LSCPU_OUTPUT_PARSABLE:
+               cxt->show_compatible = 1;
+               if (!ncolumns) {
+                       columns[ncolumns++] = COL_CPU_CPU;
+                       columns[ncolumns++] = COL_CPU_CORE;
+                       if (cxt->is_cluster)
+                               columns[ncolumns++] = COL_CPU_CLUSTER;
+                       else
+                               columns[ncolumns++] = COL_CPU_SOCKET;
+                       columns[ncolumns++] = COL_CPU_NODE;
+                       columns[ncolumns++] = COL_CPU_CACHE;
+               }
+               if (outarg) {
+                       if (string_add_to_idarray(outarg, columns,
+                                       ARRAY_SIZE(columns),
+                                       &ncolumns, cpu_column_name_to_id) < 0)
+                               return EXIT_FAILURE;
+                       cxt->show_compatible = 0;
+               }
 
-#endif /* LSCPU_OLD_OUTPUT_CODE */
+               print_cpus_parsable(cxt, columns, ncolumns);
+               break;
+       }
 
        lscpu_free_context(cxt);