All of lore.kernel.org
 help / color / mirror / Atom feed
* [PATCH for-6.2 v2 00/11] machine: smp parsing fixes and improvement
@ 2021-07-19  3:20 Yanan Wang
  2021-07-19  3:20 ` [PATCH for-6.2 v2 01/11] machine: Disallow specifying topology parameters as zero Yanan Wang
                   ` (11 more replies)
  0 siblings, 12 replies; 58+ messages in thread
From: Yanan Wang @ 2021-07-19  3:20 UTC (permalink / raw)
  To: qemu-devel
  Cc: Peter Maydell, Andrew Jones, Daniel P . Berrangé,
	Eduardo Habkost, Michael S . Tsirkin, wanghaibin.wang,
	Richard Henderson, Greg Kurz, Halil Pasic, yuzenghui,
	Igor Mammedov, Thomas Huth, Paolo Bonzini, Yanan Wang,
	David Gibson

Hi,

This is v2 of the series [1] that I have posted to introduce some smp parsing
fixes and improvement, much more work has been processed compared to RFC v1.

[1] https://lists.gnu.org/archive/html/qemu-devel/2021-07/msg00259.html

The purpose of this series is to improve/fix the parsing logic. Explicitly
specifying a CPU topology parameter as zero is not allowed any more, and
maxcpus is now uniformly used to calculate the omitted parameters. It's also
suggested that we should start to prefer cores over sockets over threads on
the newer machine types, which will make the computed virtual topology more
reflective of the real hardware.

In order to reduce code duplication and ease the code maintenance, smp_parse
in now converted into a parser generic enough for all arches, so that the PC
specific one can be removed. It's also convenient to introduce more topology
members (e.g. cluster) to the generic parser in the future.

Finally, a QEMU unit test for the parsing of given SMP configuration is added.
Since all the parsing logic is in generic function smp_parse(), this test
passes diffenent SMP configurations to the function and compare the parsing
result with what is expected. In the test, all possible collections of the
topology parameters and the corressponding expected results are listed,
including the valid and invalid ones. The preference of sockets over cores
and the preference of cores over sockets, and the support of multi-dies are
also taken into consideration.

---

Changelogs:

v1->v2:
- disallow "anything=0" in the smp configuration (Andrew)
- make function smp_parse() a generic helper for all arches
- improve the error reporting in the parser
- start to prefer cores over sockets since 6.2 (Daniel)
- add a unit test for the smp parsing (Daniel)

---

Yanan Wang (11):
  machine: Disallow specifying topology parameters as zero
  machine: Make smp_parse generic enough for all arches
  machine: Uniformly use maxcpus to calculate the omitted parameters
  machine: Use the computed parameters to calculate omitted cpus
  machine: Improve the error reporting of smp parsing
  hw: Add compat machines for 6.2
  machine: Prefer cores over sockets in smp parsing since 6.2
  machine: Use ms instead of global current_machine in sanity-check
  machine: Tweak the order of topology members in struct CpuTopology
  machine: Split out the smp parsing code
  tests/unit: Add a unit test for smp parsing

 MAINTAINERS                 |    2 +
 hw/arm/virt.c               |   10 +-
 hw/core/machine-smp.c       |  124 ++++
 hw/core/machine.c           |   68 +--
 hw/core/meson.build         |    1 +
 hw/i386/pc.c                |   66 +--
 hw/i386/pc_piix.c           |   15 +-
 hw/i386/pc_q35.c            |   14 +-
 hw/ppc/spapr.c              |   16 +-
 hw/s390x/s390-virtio-ccw.c  |   15 +-
 include/hw/boards.h         |   13 +-
 include/hw/i386/pc.h        |    3 +
 qapi/machine.json           |    6 +-
 qemu-options.hx             |    4 +-
 tests/unit/meson.build      |    1 +
 tests/unit/test-smp-parse.c | 1117 +++++++++++++++++++++++++++++++++++
 16 files changed, 1338 insertions(+), 137 deletions(-)
 create mode 100644 hw/core/machine-smp.c
 create mode 100644 tests/unit/test-smp-parse.c

-- 
2.19.1



^ permalink raw reply	[flat|nested] 58+ messages in thread

* [PATCH for-6.2 v2 01/11] machine: Disallow specifying topology parameters as zero
  2021-07-19  3:20 [PATCH for-6.2 v2 00/11] machine: smp parsing fixes and improvement Yanan Wang
@ 2021-07-19  3:20 ` Yanan Wang
  2021-07-19 16:11   ` Andrew Jones
  2021-07-19 16:46   ` Daniel P. Berrangé
  2021-07-19  3:20 ` [PATCH for-6.2 v2 02/11] machine: Make smp_parse generic enough for all arches Yanan Wang
                   ` (10 subsequent siblings)
  11 siblings, 2 replies; 58+ messages in thread
From: Yanan Wang @ 2021-07-19  3:20 UTC (permalink / raw)
  To: qemu-devel
  Cc: Peter Maydell, Andrew Jones, Daniel P . Berrangé,
	Eduardo Habkost, Michael S . Tsirkin, wanghaibin.wang,
	Richard Henderson, Greg Kurz, Halil Pasic, yuzenghui,
	Igor Mammedov, Thomas Huth, Paolo Bonzini, Yanan Wang,
	David Gibson

In the SMP configuration, we should either specify a topology
parameter with a reasonable value (equal to or greater than 1)
or just leave it omitted and QEMU will calculate its value.

Configurations which explicitly specify the topology parameters
as zero like "sockets=0" are meaningless, so disallow them.

Suggested-by: Andrew Jones <drjones@redhat.com>
Signed-off-by: Yanan Wang <wangyanan55@huawei.com>
---
 hw/core/machine.c | 31 +++++++++++++++++++++++--------
 hw/i386/pc.c      | 29 +++++++++++++++++++++--------
 qapi/machine.json |  4 ++--
 3 files changed, 46 insertions(+), 18 deletions(-)

diff --git a/hw/core/machine.c b/hw/core/machine.c
index 775add0795..d73daa10f4 100644
--- a/hw/core/machine.c
+++ b/hw/core/machine.c
@@ -745,11 +745,25 @@ static void smp_parse(MachineState *ms, SMPConfiguration *config, Error **errp)
 {
     unsigned cpus    = config->has_cpus ? config->cpus : 0;
     unsigned sockets = config->has_sockets ? config->sockets : 0;
+    unsigned dies    = config->has_dies ? config->dies : 1;
     unsigned cores   = config->has_cores ? config->cores : 0;
     unsigned threads = config->has_threads ? config->threads : 0;
+    unsigned maxcpus = config->has_maxcpus ? config->maxcpus : 0;
+
+    if ((config->has_cpus && config->cpus == 0) ||
+        (config->has_sockets && config->sockets == 0) ||
+        (config->has_dies && config->dies == 0) ||
+        (config->has_cores && config->cores == 0) ||
+        (config->has_threads && config->threads == 0) ||
+        (config->has_maxcpus && config->maxcpus == 0)) {
+        error_setg(errp, "parameters must be equal to or greater than one"
+                   "if provided");
+        return;
+    }
 
-    if (config->has_dies && config->dies != 0 && config->dies != 1) {
+    if (dies > 1) {
         error_setg(errp, "dies not supported by this machine's CPU topology");
+        return;
     }
 
     /* compute missing values, prefer sockets over cores over threads */
@@ -760,8 +774,8 @@ static void smp_parse(MachineState *ms, SMPConfiguration *config, Error **errp)
             sockets = sockets > 0 ? sockets : 1;
             cpus = cores * threads * sockets;
         } else {
-            ms->smp.max_cpus = config->has_maxcpus ? config->maxcpus : cpus;
-            sockets = ms->smp.max_cpus / (cores * threads);
+            maxcpus = maxcpus > 0 ? maxcpus : cpus;
+            sockets = maxcpus / (cores * threads);
         }
     } else if (cores == 0) {
         threads = threads > 0 ? threads : 1;
@@ -778,26 +792,27 @@ static void smp_parse(MachineState *ms, SMPConfiguration *config, Error **errp)
         return;
     }
 
-    ms->smp.max_cpus = config->has_maxcpus ? config->maxcpus : cpus;
+    maxcpus = maxcpus > 0 ? maxcpus : cpus;
 
-    if (ms->smp.max_cpus < cpus) {
+    if (maxcpus < cpus) {
         error_setg(errp, "maxcpus must be equal to or greater than smp");
         return;
     }
 
-    if (sockets * cores * threads != ms->smp.max_cpus) {
+    if (sockets * cores * threads != maxcpus) {
         error_setg(errp, "Invalid CPU topology: "
                    "sockets (%u) * cores (%u) * threads (%u) "
                    "!= maxcpus (%u)",
                    sockets, cores, threads,
-                   ms->smp.max_cpus);
+                   maxcpus);
         return;
     }
 
     ms->smp.cpus = cpus;
+    ms->smp.sockets = sockets;
     ms->smp.cores = cores;
     ms->smp.threads = threads;
-    ms->smp.sockets = sockets;
+    ms->smp.max_cpus = maxcpus;
 }
 
 static void machine_get_smp(Object *obj, Visitor *v, const char *name,
diff --git a/hw/i386/pc.c b/hw/i386/pc.c
index c2b9d62a35..c6b63c00a5 100644
--- a/hw/i386/pc.c
+++ b/hw/i386/pc.c
@@ -719,6 +719,18 @@ static void pc_smp_parse(MachineState *ms, SMPConfiguration *config, Error **err
     unsigned dies    = config->has_dies ? config->dies : 1;
     unsigned cores   = config->has_cores ? config->cores : 0;
     unsigned threads = config->has_threads ? config->threads : 0;
+    unsigned maxcpus = config->has_maxcpus ? config->maxcpus : 0;
+
+    if ((config->has_cpus && config->cpus == 0) ||
+        (config->has_sockets && config->sockets == 0) ||
+        (config->has_dies && config->dies == 0) ||
+        (config->has_cores && config->cores == 0) ||
+        (config->has_threads && config->threads == 0) ||
+        (config->has_maxcpus && config->maxcpus == 0)) {
+        error_setg(errp, "parameters must be equal to or greater than one"
+                   "if provided");
+        return;
+    }
 
     /* compute missing values, prefer sockets over cores over threads */
     if (cpus == 0 || sockets == 0) {
@@ -728,8 +740,8 @@ static void pc_smp_parse(MachineState *ms, SMPConfiguration *config, Error **err
             sockets = sockets > 0 ? sockets : 1;
             cpus = cores * threads * dies * sockets;
         } else {
-            ms->smp.max_cpus = config->has_maxcpus ? config->maxcpus : cpus;
-            sockets = ms->smp.max_cpus / (cores * threads * dies);
+            maxcpus = maxcpus > 0 ? maxcpus : cpus;
+            sockets = maxcpus / (cores * threads * dies);
         }
     } else if (cores == 0) {
         threads = threads > 0 ? threads : 1;
@@ -746,27 +758,28 @@ static void pc_smp_parse(MachineState *ms, SMPConfiguration *config, Error **err
         return;
     }
 
-    ms->smp.max_cpus = config->has_maxcpus ? config->maxcpus : cpus;
+    maxcpus = maxcpus > 0 ? maxcpus : cpus;
 
-    if (ms->smp.max_cpus < cpus) {
+    if (maxcpus < cpus) {
         error_setg(errp, "maxcpus must be equal to or greater than smp");
         return;
     }
 
-    if (sockets * dies * cores * threads != ms->smp.max_cpus) {
+    if (sockets * dies * cores * threads != maxcpus) {
         error_setg(errp, "Invalid CPU topology deprecated: "
                    "sockets (%u) * dies (%u) * cores (%u) * threads (%u) "
                    "!= maxcpus (%u)",
                    sockets, dies, cores, threads,
-                   ms->smp.max_cpus);
+                   maxcpus);
         return;
     }
 
     ms->smp.cpus = cpus;
-    ms->smp.cores = cores;
-    ms->smp.threads = threads;
     ms->smp.sockets = sockets;
     ms->smp.dies = dies;
+    ms->smp.cores = cores;
+    ms->smp.threads = threads;
+    ms->smp.max_cpus = maxcpus;
 }
 
 static
diff --git a/qapi/machine.json b/qapi/machine.json
index c3210ee1fb..c11b2e6f73 100644
--- a/qapi/machine.json
+++ b/qapi/machine.json
@@ -1288,8 +1288,8 @@
 ##
 # @SMPConfiguration:
 #
-# Schema for CPU topology configuration.  "0" or a missing value lets
-# QEMU figure out a suitable value based on the ones that are provided.
+# Schema for CPU topology configuration. A missing value lets QEMU
+# figure out a suitable value based on the ones that are provided.
 #
 # @cpus: number of virtual CPUs in the virtual machine
 #
-- 
2.19.1



^ permalink raw reply related	[flat|nested] 58+ messages in thread

* [PATCH for-6.2 v2 02/11] machine: Make smp_parse generic enough for all arches
  2021-07-19  3:20 [PATCH for-6.2 v2 00/11] machine: smp parsing fixes and improvement Yanan Wang
  2021-07-19  3:20 ` [PATCH for-6.2 v2 01/11] machine: Disallow specifying topology parameters as zero Yanan Wang
@ 2021-07-19  3:20 ` Yanan Wang
  2021-07-19 16:28   ` Andrew Jones
                     ` (2 more replies)
  2021-07-19  3:20 ` [PATCH for-6.2 v2 03/11] machine: Uniformly use maxcpus to calculate the omitted parameters Yanan Wang
                   ` (9 subsequent siblings)
  11 siblings, 3 replies; 58+ messages in thread
From: Yanan Wang @ 2021-07-19  3:20 UTC (permalink / raw)
  To: qemu-devel
  Cc: Peter Maydell, Andrew Jones, Daniel P . Berrangé,
	Eduardo Habkost, Michael S . Tsirkin, wanghaibin.wang,
	Richard Henderson, Greg Kurz, Halil Pasic, yuzenghui,
	Igor Mammedov, Thomas Huth, Paolo Bonzini, Yanan Wang,
	David Gibson

Currently the only difference between smp_parse and pc_smp_parse
is the support of multi-dies and the related error reporting code.
With an arch compat variable "bool smp_dies_supported", we can
easily make smp_parse generic enough for all arches and the PC
specific one can be removed.

Making smp_parse() generic enough can reduce code duplication and
ease the code maintenance, and also allows extending the topology
with more arch specific members (e.g., clusters) in the future.

No functional change intended.

Suggested-by: Andrew Jones <drjones@redhat.com>
Signed-off-by: Yanan Wang <wangyanan55@huawei.com>
---
 hw/core/machine.c   | 28 ++++++++++-------
 hw/i386/pc.c        | 76 +--------------------------------------------
 include/hw/boards.h |  1 +
 3 files changed, 19 insertions(+), 86 deletions(-)

diff --git a/hw/core/machine.c b/hw/core/machine.c
index d73daa10f4..ed6712e964 100644
--- a/hw/core/machine.c
+++ b/hw/core/machine.c
@@ -743,6 +743,7 @@ void machine_set_cpu_numa_node(MachineState *machine,
 
 static void smp_parse(MachineState *ms, SMPConfiguration *config, Error **errp)
 {
+    MachineClass *mc = MACHINE_GET_CLASS(ms);
     unsigned cpus    = config->has_cpus ? config->cpus : 0;
     unsigned sockets = config->has_sockets ? config->sockets : 0;
     unsigned dies    = config->has_dies ? config->dies : 1;
@@ -761,7 +762,7 @@ static void smp_parse(MachineState *ms, SMPConfiguration *config, Error **errp)
         return;
     }
 
-    if (dies > 1) {
+    if (!mc->smp_dies_supported && dies > 1) {
         error_setg(errp, "dies not supported by this machine's CPU topology");
         return;
     }
@@ -772,23 +773,25 @@ static void smp_parse(MachineState *ms, SMPConfiguration *config, Error **errp)
         threads = threads > 0 ? threads : 1;
         if (cpus == 0) {
             sockets = sockets > 0 ? sockets : 1;
-            cpus = cores * threads * sockets;
+            cpus = sockets * dies * cores * threads;
         } else {
             maxcpus = maxcpus > 0 ? maxcpus : cpus;
-            sockets = maxcpus / (cores * threads);
+            sockets = maxcpus / (dies * cores * threads);
         }
     } else if (cores == 0) {
         threads = threads > 0 ? threads : 1;
-        cores = cpus / (sockets * threads);
+        cores = cpus / (sockets * dies * threads);
         cores = cores > 0 ? cores : 1;
     } else if (threads == 0) {
-        threads = cpus / (cores * sockets);
+        threads = cpus / (sockets * dies * cores);
         threads = threads > 0 ? threads : 1;
-    } else if (sockets * cores * threads < cpus) {
+    } else if (sockets * dies * cores * threads < cpus) {
+        g_autofree char *dies_msg = g_strdup_printf(
+            mc->smp_dies_supported ? " * dies (%u)" : "", dies);
         error_setg(errp, "cpu topology: "
-                   "sockets (%u) * cores (%u) * threads (%u) < "
+                   "sockets (%u)%s * cores (%u) * threads (%u) < "
                    "smp_cpus (%u)",
-                   sockets, cores, threads, cpus);
+                   sockets, dies_msg, cores, threads, cpus);
         return;
     }
 
@@ -799,17 +802,20 @@ static void smp_parse(MachineState *ms, SMPConfiguration *config, Error **errp)
         return;
     }
 
-    if (sockets * cores * threads != maxcpus) {
+    if (sockets * dies * cores * threads != maxcpus) {
+        g_autofree char *dies_msg = g_strdup_printf(
+            mc->smp_dies_supported ? " * dies (%u)" : "", dies);
         error_setg(errp, "Invalid CPU topology: "
-                   "sockets (%u) * cores (%u) * threads (%u) "
+                   "sockets (%u)%s * cores (%u) * threads (%u) "
                    "!= maxcpus (%u)",
-                   sockets, cores, threads,
+                   sockets, dies_msg, cores, threads,
                    maxcpus);
         return;
     }
 
     ms->smp.cpus = cpus;
     ms->smp.sockets = sockets;
+    ms->smp.dies = dies;
     ms->smp.cores = cores;
     ms->smp.threads = threads;
     ms->smp.max_cpus = maxcpus;
diff --git a/hw/i386/pc.c b/hw/i386/pc.c
index c6b63c00a5..d94ef582b5 100644
--- a/hw/i386/pc.c
+++ b/hw/i386/pc.c
@@ -708,80 +708,6 @@ void pc_acpi_smi_interrupt(void *opaque, int irq, int level)
     }
 }
 
-/*
- * This function is very similar to smp_parse()
- * in hw/core/machine.c but includes CPU die support.
- */
-static void pc_smp_parse(MachineState *ms, SMPConfiguration *config, Error **errp)
-{
-    unsigned cpus    = config->has_cpus ? config->cpus : 0;
-    unsigned sockets = config->has_sockets ? config->sockets : 0;
-    unsigned dies    = config->has_dies ? config->dies : 1;
-    unsigned cores   = config->has_cores ? config->cores : 0;
-    unsigned threads = config->has_threads ? config->threads : 0;
-    unsigned maxcpus = config->has_maxcpus ? config->maxcpus : 0;
-
-    if ((config->has_cpus && config->cpus == 0) ||
-        (config->has_sockets && config->sockets == 0) ||
-        (config->has_dies && config->dies == 0) ||
-        (config->has_cores && config->cores == 0) ||
-        (config->has_threads && config->threads == 0) ||
-        (config->has_maxcpus && config->maxcpus == 0)) {
-        error_setg(errp, "parameters must be equal to or greater than one"
-                   "if provided");
-        return;
-    }
-
-    /* compute missing values, prefer sockets over cores over threads */
-    if (cpus == 0 || sockets == 0) {
-        cores = cores > 0 ? cores : 1;
-        threads = threads > 0 ? threads : 1;
-        if (cpus == 0) {
-            sockets = sockets > 0 ? sockets : 1;
-            cpus = cores * threads * dies * sockets;
-        } else {
-            maxcpus = maxcpus > 0 ? maxcpus : cpus;
-            sockets = maxcpus / (cores * threads * dies);
-        }
-    } else if (cores == 0) {
-        threads = threads > 0 ? threads : 1;
-        cores = cpus / (sockets * dies * threads);
-        cores = cores > 0 ? cores : 1;
-    } else if (threads == 0) {
-        threads = cpus / (cores * dies * sockets);
-        threads = threads > 0 ? threads : 1;
-    } else if (sockets * dies * cores * threads < cpus) {
-        error_setg(errp, "cpu topology: "
-                   "sockets (%u) * dies (%u) * cores (%u) * threads (%u) < "
-                   "smp_cpus (%u)",
-                   sockets, dies, cores, threads, cpus);
-        return;
-    }
-
-    maxcpus = maxcpus > 0 ? maxcpus : cpus;
-
-    if (maxcpus < cpus) {
-        error_setg(errp, "maxcpus must be equal to or greater than smp");
-        return;
-    }
-
-    if (sockets * dies * cores * threads != maxcpus) {
-        error_setg(errp, "Invalid CPU topology deprecated: "
-                   "sockets (%u) * dies (%u) * cores (%u) * threads (%u) "
-                   "!= maxcpus (%u)",
-                   sockets, dies, cores, threads,
-                   maxcpus);
-        return;
-    }
-
-    ms->smp.cpus = cpus;
-    ms->smp.sockets = sockets;
-    ms->smp.dies = dies;
-    ms->smp.cores = cores;
-    ms->smp.threads = threads;
-    ms->smp.max_cpus = maxcpus;
-}
-
 static
 void pc_machine_done(Notifier *notifier, void *data)
 {
@@ -1735,7 +1661,6 @@ static void pc_machine_class_init(ObjectClass *oc, void *data)
     mc->auto_enable_numa_with_memdev = true;
     mc->has_hotpluggable_cpus = true;
     mc->default_boot_order = "cad";
-    mc->smp_parse = pc_smp_parse;
     mc->block_default_type = IF_IDE;
     mc->max_cpus = 255;
     mc->reset = pc_machine_reset;
@@ -1746,6 +1671,7 @@ static void pc_machine_class_init(ObjectClass *oc, void *data)
     hc->unplug = pc_machine_device_unplug_cb;
     mc->default_cpu_type = TARGET_DEFAULT_CPU_TYPE;
     mc->nvdimm_supported = true;
+    mc->smp_dies_supported = true;
     mc->default_ram_id = "pc.ram";
 
     object_class_property_add(oc, PC_MACHINE_MAX_RAM_BELOW_4G, "size",
diff --git a/include/hw/boards.h b/include/hw/boards.h
index accd6eff35..b6161cee88 100644
--- a/include/hw/boards.h
+++ b/include/hw/boards.h
@@ -246,6 +246,7 @@ struct MachineClass {
     bool smbus_no_migration_support;
     bool nvdimm_supported;
     bool numa_mem_supported;
+    bool smp_dies_supported;
     bool auto_enable_numa;
     const char *default_ram_id;
 
-- 
2.19.1



^ permalink raw reply related	[flat|nested] 58+ messages in thread

* [PATCH for-6.2 v2 03/11] machine: Uniformly use maxcpus to calculate the omitted parameters
  2021-07-19  3:20 [PATCH for-6.2 v2 00/11] machine: smp parsing fixes and improvement Yanan Wang
  2021-07-19  3:20 ` [PATCH for-6.2 v2 01/11] machine: Disallow specifying topology parameters as zero Yanan Wang
  2021-07-19  3:20 ` [PATCH for-6.2 v2 02/11] machine: Make smp_parse generic enough for all arches Yanan Wang
@ 2021-07-19  3:20 ` Yanan Wang
  2021-07-19 16:36   ` Andrew Jones
  2021-07-19  3:20 ` [PATCH for-6.2 v2 04/11] machine: Use the computed parameters to calculate omitted cpus Yanan Wang
                   ` (8 subsequent siblings)
  11 siblings, 1 reply; 58+ messages in thread
From: Yanan Wang @ 2021-07-19  3:20 UTC (permalink / raw)
  To: qemu-devel
  Cc: Peter Maydell, Andrew Jones, Daniel P . Berrangé,
	Eduardo Habkost, Michael S . Tsirkin, wanghaibin.wang,
	Richard Henderson, Greg Kurz, Halil Pasic, yuzenghui,
	Igor Mammedov, Thomas Huth, Paolo Bonzini, Yanan Wang,
	David Gibson

We are currently using maxcpus to calculate the omitted sockets
but using cpus to calculate the omitted cores/threads. This makes
cmdlines like:
  -smp cpus=8,maxcpus=16
  -smp cpus=8,cores=4,maxcpus=16
  -smp cpus=8,threads=2,maxcpus=16
work fine but the ones like:
  -smp cpus=8,sockets=2,maxcpus=16
  -smp cpus=8,sockets=2,cores=4,maxcpus=16
  -smp cpus=8,sockets=2,threads=2,maxcpus=16
break the invalid cpu topology check.

Since we require for the valid config that the sum of "sockets * cores
* dies * threads" should equal to the maxcpus, we should uniformly use
maxcpus to calculate their omitted values.

Also the if-branch of "cpus == 0 || sockets == 0" was splited into two
branches of "cpus == 0" and "sockets == 0" so that we can clearly read
that we are parsing -smp cmdlines with a preference of cpus over sockets
over cores over threads.

Note: change in this patch won't affect any existing working cmdlines
but improves consistency and allow more incomplete configs to be valid.

Signed-off-by: Yanan Wang <wangyanan55@huawei.com>
---
 hw/core/machine.c | 30 +++++++++++++++---------------
 1 file changed, 15 insertions(+), 15 deletions(-)

diff --git a/hw/core/machine.c b/hw/core/machine.c
index ed6712e964..c9f15b15a5 100644
--- a/hw/core/machine.c
+++ b/hw/core/machine.c
@@ -768,24 +768,26 @@ static void smp_parse(MachineState *ms, SMPConfiguration *config, Error **errp)
     }
 
     /* compute missing values, prefer sockets over cores over threads */
-    if (cpus == 0 || sockets == 0) {
+    maxcpus = maxcpus > 0 ? maxcpus : cpus;
+
+    if (cpus == 0) {
+        sockets = sockets > 0 ? sockets : 1;
         cores = cores > 0 ? cores : 1;
         threads = threads > 0 ? threads : 1;
-        if (cpus == 0) {
-            sockets = sockets > 0 ? sockets : 1;
-            cpus = sockets * dies * cores * threads;
-        } else {
-            maxcpus = maxcpus > 0 ? maxcpus : cpus;
-            sockets = maxcpus / (dies * cores * threads);
-        }
+        cpus = sockets * dies * cores * threads;
+        maxcpus = maxcpus > 0 ? maxcpus : cpus;
+    } else if (sockets == 0) {
+        cores = cores > 0 ? cores : 1;
+        threads = threads > 0 ? threads : 1;
+        sockets = maxcpus / (dies * cores * threads);
     } else if (cores == 0) {
         threads = threads > 0 ? threads : 1;
-        cores = cpus / (sockets * dies * threads);
-        cores = cores > 0 ? cores : 1;
+        cores = maxcpus / (sockets * dies * threads);
     } else if (threads == 0) {
-        threads = cpus / (sockets * dies * cores);
-        threads = threads > 0 ? threads : 1;
-    } else if (sockets * dies * cores * threads < cpus) {
+        threads = maxcpus / (sockets * dies * cores);
+    }
+
+    if (sockets * dies * cores * threads < cpus) {
         g_autofree char *dies_msg = g_strdup_printf(
             mc->smp_dies_supported ? " * dies (%u)" : "", dies);
         error_setg(errp, "cpu topology: "
@@ -795,8 +797,6 @@ static void smp_parse(MachineState *ms, SMPConfiguration *config, Error **errp)
         return;
     }
 
-    maxcpus = maxcpus > 0 ? maxcpus : cpus;
-
     if (maxcpus < cpus) {
         error_setg(errp, "maxcpus must be equal to or greater than smp");
         return;
-- 
2.19.1



^ permalink raw reply related	[flat|nested] 58+ messages in thread

* [PATCH for-6.2 v2 04/11] machine: Use the computed parameters to calculate omitted cpus
  2021-07-19  3:20 [PATCH for-6.2 v2 00/11] machine: smp parsing fixes and improvement Yanan Wang
                   ` (2 preceding siblings ...)
  2021-07-19  3:20 ` [PATCH for-6.2 v2 03/11] machine: Uniformly use maxcpus to calculate the omitted parameters Yanan Wang
@ 2021-07-19  3:20 ` Yanan Wang
  2021-07-19 16:42   ` Andrew Jones
  2021-07-19  3:20 ` [PATCH for-6.2 v2 05/11] machine: Improve the error reporting of smp parsing Yanan Wang
                   ` (7 subsequent siblings)
  11 siblings, 1 reply; 58+ messages in thread
From: Yanan Wang @ 2021-07-19  3:20 UTC (permalink / raw)
  To: qemu-devel
  Cc: Peter Maydell, Andrew Jones, Daniel P . Berrangé,
	Eduardo Habkost, Michael S . Tsirkin, wanghaibin.wang,
	Richard Henderson, Greg Kurz, Halil Pasic, yuzenghui,
	Igor Mammedov, Thomas Huth, Paolo Bonzini, Yanan Wang,
	David Gibson

Currently we directly calculate the omitted cpus based on the already
provided parameters. This makes some cmdlines like:
  -smp maxcpus=16
  -smp sockets=2,maxcpus=16
  -smp sockets=2,dies=2,maxcpus=16
  -smp sockets=2,cores=4,maxcpus=16
not work. We should probably use the computed paramters to calculate
cpus when maxcpus is provided while cpus is omitted, which will make
above configs start to work.

Note: change in this patch won't affect any existing working cmdlines
but allows more incomplete configs to be valid.

Signed-off-by: Yanan Wang <wangyanan55@huawei.com>
---
 hw/core/machine.c | 17 +++++++++--------
 1 file changed, 9 insertions(+), 8 deletions(-)

diff --git a/hw/core/machine.c b/hw/core/machine.c
index c9f15b15a5..668f0a1553 100644
--- a/hw/core/machine.c
+++ b/hw/core/machine.c
@@ -767,26 +767,27 @@ static void smp_parse(MachineState *ms, SMPConfiguration *config, Error **errp)
         return;
     }
 
-    /* compute missing values, prefer sockets over cores over threads */
     maxcpus = maxcpus > 0 ? maxcpus : cpus;
 
-    if (cpus == 0) {
-        sockets = sockets > 0 ? sockets : 1;
-        cores = cores > 0 ? cores : 1;
-        threads = threads > 0 ? threads : 1;
-        cpus = sockets * dies * cores * threads;
-        maxcpus = maxcpus > 0 ? maxcpus : cpus;
-    } else if (sockets == 0) {
+    /* compute missing values, prefer sockets over cores over threads */
+    if (sockets == 0) {
         cores = cores > 0 ? cores : 1;
         threads = threads > 0 ? threads : 1;
         sockets = maxcpus / (dies * cores * threads);
+        sockets = sockets > 0 ? sockets : 1;
     } else if (cores == 0) {
         threads = threads > 0 ? threads : 1;
         cores = maxcpus / (sockets * dies * threads);
+        cores = cores > 0 ? cores : 1;
     } else if (threads == 0) {
         threads = maxcpus / (sockets * dies * cores);
+        threads = threads > 0 ? threads : 1;
     }
 
+    /* use the computed parameters to calculate the omitted cpus */
+    cpus = cpus > 0 ? cpus : sockets * dies * cores * threads;
+    maxcpus = maxcpus > 0 ? maxcpus : cpus;
+
     if (sockets * dies * cores * threads < cpus) {
         g_autofree char *dies_msg = g_strdup_printf(
             mc->smp_dies_supported ? " * dies (%u)" : "", dies);
-- 
2.19.1



^ permalink raw reply related	[flat|nested] 58+ messages in thread

* [PATCH for-6.2 v2 05/11] machine: Improve the error reporting of smp parsing
  2021-07-19  3:20 [PATCH for-6.2 v2 00/11] machine: smp parsing fixes and improvement Yanan Wang
                   ` (3 preceding siblings ...)
  2021-07-19  3:20 ` [PATCH for-6.2 v2 04/11] machine: Use the computed parameters to calculate omitted cpus Yanan Wang
@ 2021-07-19  3:20 ` Yanan Wang
  2021-07-19 16:53   ` Andrew Jones
  2021-07-19  3:20 ` [PATCH for-6.2 v2 06/11] hw: Add compat machines for 6.2 Yanan Wang
                   ` (6 subsequent siblings)
  11 siblings, 1 reply; 58+ messages in thread
From: Yanan Wang @ 2021-07-19  3:20 UTC (permalink / raw)
  To: qemu-devel
  Cc: Peter Maydell, Andrew Jones, Daniel P . Berrangé,
	Eduardo Habkost, Michael S . Tsirkin, wanghaibin.wang,
	Richard Henderson, Greg Kurz, Halil Pasic, yuzenghui,
	Igor Mammedov, Thomas Huth, Paolo Bonzini, Yanan Wang,
	David Gibson

We totally have two requirements for a valid SMP configuration:
the sum of "sockets * dies * cores * threads" must represent all
the possible cpus, i.e., max_cpus, and must include the initial
present cpus, i.e., smp_cpus.

We only need to ensure "sockets * dies * cores * threads == maxcpus"
at first and then ensure "sockets * dies * cores * threads >= cpus".
With a reasonable order of the sanity-check, we can simplify the
error reporting code.

Signed-off-by: Yanan Wang <wangyanan55@huawei.com>
---
 hw/core/machine.c | 25 ++++++++++---------------
 1 file changed, 10 insertions(+), 15 deletions(-)

diff --git a/hw/core/machine.c b/hw/core/machine.c
index 668f0a1553..8b4d07d3fc 100644
--- a/hw/core/machine.c
+++ b/hw/core/machine.c
@@ -788,21 +788,6 @@ static void smp_parse(MachineState *ms, SMPConfiguration *config, Error **errp)
     cpus = cpus > 0 ? cpus : sockets * dies * cores * threads;
     maxcpus = maxcpus > 0 ? maxcpus : cpus;
 
-    if (sockets * dies * cores * threads < cpus) {
-        g_autofree char *dies_msg = g_strdup_printf(
-            mc->smp_dies_supported ? " * dies (%u)" : "", dies);
-        error_setg(errp, "cpu topology: "
-                   "sockets (%u)%s * cores (%u) * threads (%u) < "
-                   "smp_cpus (%u)",
-                   sockets, dies_msg, cores, threads, cpus);
-        return;
-    }
-
-    if (maxcpus < cpus) {
-        error_setg(errp, "maxcpus must be equal to or greater than smp");
-        return;
-    }
-
     if (sockets * dies * cores * threads != maxcpus) {
         g_autofree char *dies_msg = g_strdup_printf(
             mc->smp_dies_supported ? " * dies (%u)" : "", dies);
@@ -814,6 +799,16 @@ static void smp_parse(MachineState *ms, SMPConfiguration *config, Error **errp)
         return;
     }
 
+    if (sockets * dies * cores * threads < cpus) {
+        g_autofree char *dies_msg = g_strdup_printf(
+            mc->smp_dies_supported ? " * dies (%u)" : "", dies);
+        error_setg(errp, "Invalid CPU topology: "
+                   "sockets (%u)%s * cores (%u) * threads (%u) < "
+                   "smp_cpus (%u)",
+                   sockets, dies_msg, cores, threads, cpus);
+        return;
+    }
+
     ms->smp.cpus = cpus;
     ms->smp.sockets = sockets;
     ms->smp.dies = dies;
-- 
2.19.1



^ permalink raw reply related	[flat|nested] 58+ messages in thread

* [PATCH for-6.2 v2 06/11] hw: Add compat machines for 6.2
  2021-07-19  3:20 [PATCH for-6.2 v2 00/11] machine: smp parsing fixes and improvement Yanan Wang
                   ` (4 preceding siblings ...)
  2021-07-19  3:20 ` [PATCH for-6.2 v2 05/11] machine: Improve the error reporting of smp parsing Yanan Wang
@ 2021-07-19  3:20 ` Yanan Wang
  2021-07-19  3:38   ` David Gibson
                     ` (3 more replies)
  2021-07-19  3:20 ` [PATCH for-6.2 v2 07/11] machine: Prefer cores over sockets in smp parsing since 6.2 Yanan Wang
                   ` (5 subsequent siblings)
  11 siblings, 4 replies; 58+ messages in thread
From: Yanan Wang @ 2021-07-19  3:20 UTC (permalink / raw)
  To: qemu-devel
  Cc: Peter Maydell, Andrew Jones, Daniel P . Berrangé,
	Eduardo Habkost, Michael S . Tsirkin, wanghaibin.wang,
	Richard Henderson, Greg Kurz, Halil Pasic, yuzenghui,
	Igor Mammedov, Thomas Huth, Paolo Bonzini, Yanan Wang,
	David Gibson

Add 6.2 machine types for arm/i440fx/q35/s390x/spapr.

Signed-off-by: Yanan Wang <wangyanan55@huawei.com>
---
 hw/arm/virt.c              |  9 ++++++++-
 hw/core/machine.c          |  3 +++
 hw/i386/pc.c               |  3 +++
 hw/i386/pc_piix.c          | 14 +++++++++++++-
 hw/i386/pc_q35.c           | 13 ++++++++++++-
 hw/ppc/spapr.c             | 15 +++++++++++++--
 hw/s390x/s390-virtio-ccw.c | 14 +++++++++++++-
 include/hw/boards.h        |  3 +++
 include/hw/i386/pc.h       |  3 +++
 9 files changed, 71 insertions(+), 6 deletions(-)

diff --git a/hw/arm/virt.c b/hw/arm/virt.c
index 81eda46b0b..01165f7f53 100644
--- a/hw/arm/virt.c
+++ b/hw/arm/virt.c
@@ -2788,10 +2788,17 @@ static void machvirt_machine_init(void)
 }
 type_init(machvirt_machine_init);
 
+static void virt_machine_6_2_options(MachineClass *mc)
+{
+}
+DEFINE_VIRT_MACHINE_AS_LATEST(6, 2)
+
 static void virt_machine_6_1_options(MachineClass *mc)
 {
+    virt_machine_6_2_options(mc);
+    compat_props_add(mc->compat_props, hw_compat_6_1, hw_compat_6_1_len);
 }
-DEFINE_VIRT_MACHINE_AS_LATEST(6, 1)
+DEFINE_VIRT_MACHINE(6, 1)
 
 static void virt_machine_6_0_options(MachineClass *mc)
 {
diff --git a/hw/core/machine.c b/hw/core/machine.c
index 8b4d07d3fc..63439c4a6d 100644
--- a/hw/core/machine.c
+++ b/hw/core/machine.c
@@ -37,6 +37,9 @@
 #include "hw/virtio/virtio.h"
 #include "hw/virtio/virtio-pci.h"
 
+GlobalProperty hw_compat_6_1[] = {};
+const size_t hw_compat_6_1_len = G_N_ELEMENTS(hw_compat_6_1);
+
 GlobalProperty hw_compat_6_0[] = {
     { "gpex-pcihost", "allow-unmapped-accesses", "false" },
     { "i8042", "extended-state", "false"},
diff --git a/hw/i386/pc.c b/hw/i386/pc.c
index d94ef582b5..c6768c6ce7 100644
--- a/hw/i386/pc.c
+++ b/hw/i386/pc.c
@@ -94,6 +94,9 @@
 #include "trace.h"
 #include CONFIG_DEVICES
 
+GlobalProperty pc_compat_6_1[] = {};
+const size_t pc_compat_6_1_len = G_N_ELEMENTS(pc_compat_6_1);
+
 GlobalProperty pc_compat_6_0[] = {
     { "qemu64" "-" TYPE_X86_CPU, "family", "6" },
     { "qemu64" "-" TYPE_X86_CPU, "model", "6" },
diff --git a/hw/i386/pc_piix.c b/hw/i386/pc_piix.c
index 30b8bd6ea9..fd5c2277f2 100644
--- a/hw/i386/pc_piix.c
+++ b/hw/i386/pc_piix.c
@@ -413,7 +413,7 @@ static void pc_i440fx_machine_options(MachineClass *m)
     machine_class_allow_dynamic_sysbus_dev(m, TYPE_VMBUS_BRIDGE);
 }
 
-static void pc_i440fx_6_1_machine_options(MachineClass *m)
+static void pc_i440fx_6_2_machine_options(MachineClass *m)
 {
     PCMachineClass *pcmc = PC_MACHINE_CLASS(m);
     pc_i440fx_machine_options(m);
@@ -422,6 +422,18 @@ static void pc_i440fx_6_1_machine_options(MachineClass *m)
     pcmc->default_cpu_version = 1;
 }
 
+DEFINE_I440FX_MACHINE(v6_2, "pc-i440fx-6.2", NULL,
+                      pc_i440fx_6_2_machine_options);
+
+static void pc_i440fx_6_1_machine_options(MachineClass *m)
+{
+    pc_i440fx_6_2_machine_options(m);
+    m->alias = NULL;
+    m->is_default = false;
+    compat_props_add(m->compat_props, hw_compat_6_1, hw_compat_6_1_len);
+    compat_props_add(m->compat_props, pc_compat_6_1, pc_compat_6_1_len);
+}
+
 DEFINE_I440FX_MACHINE(v6_1, "pc-i440fx-6.1", NULL,
                       pc_i440fx_6_1_machine_options);
 
diff --git a/hw/i386/pc_q35.c b/hw/i386/pc_q35.c
index 04b4a4788d..b45903b15e 100644
--- a/hw/i386/pc_q35.c
+++ b/hw/i386/pc_q35.c
@@ -355,7 +355,7 @@ static void pc_q35_machine_options(MachineClass *m)
     m->max_cpus = 288;
 }
 
-static void pc_q35_6_1_machine_options(MachineClass *m)
+static void pc_q35_6_2_machine_options(MachineClass *m)
 {
     PCMachineClass *pcmc = PC_MACHINE_CLASS(m);
     pc_q35_machine_options(m);
@@ -363,6 +363,17 @@ static void pc_q35_6_1_machine_options(MachineClass *m)
     pcmc->default_cpu_version = 1;
 }
 
+DEFINE_Q35_MACHINE(v6_2, "pc-q35-6.2", NULL,
+                   pc_q35_6_2_machine_options);
+
+static void pc_q35_6_1_machine_options(MachineClass *m)
+{
+    pc_q35_6_2_machine_options(m);
+    m->alias = NULL;
+    compat_props_add(m->compat_props, hw_compat_6_1, hw_compat_6_1_len);
+    compat_props_add(m->compat_props, pc_compat_6_1, pc_compat_6_1_len);
+}
+
 DEFINE_Q35_MACHINE(v6_1, "pc-q35-6.1", NULL,
                    pc_q35_6_1_machine_options);
 
diff --git a/hw/ppc/spapr.c b/hw/ppc/spapr.c
index 81699d4f8b..d39fd4e644 100644
--- a/hw/ppc/spapr.c
+++ b/hw/ppc/spapr.c
@@ -4685,15 +4685,26 @@ static void spapr_machine_latest_class_options(MachineClass *mc)
     }                                                                \
     type_init(spapr_machine_register_##suffix)
 
+/*
+ * pseries-6.2
+ */
+static void spapr_machine_6_2_class_options(MachineClass *mc)
+{
+    /* Defaults for the latest behaviour inherited from the base class */
+}
+
+DEFINE_SPAPR_MACHINE(6_2, "6.2", true);
+
 /*
  * pseries-6.1
  */
 static void spapr_machine_6_1_class_options(MachineClass *mc)
 {
-    /* Defaults for the latest behaviour inherited from the base class */
+    spapr_machine_6_2_class_options(mc);
+    compat_props_add(mc->compat_props, hw_compat_6_1, hw_compat_6_1_len);
 }
 
-DEFINE_SPAPR_MACHINE(6_1, "6.1", true);
+DEFINE_SPAPR_MACHINE(6_1, "6.1", false);
 
 /*
  * pseries-6.0
diff --git a/hw/s390x/s390-virtio-ccw.c b/hw/s390x/s390-virtio-ccw.c
index e4b18aef49..4d25278cf2 100644
--- a/hw/s390x/s390-virtio-ccw.c
+++ b/hw/s390x/s390-virtio-ccw.c
@@ -791,14 +791,26 @@ bool css_migration_enabled(void)
     }                                                                         \
     type_init(ccw_machine_register_##suffix)
 
+static void ccw_machine_6_2_instance_options(MachineState *machine)
+{
+}
+
+static void ccw_machine_6_2_class_options(MachineClass *mc)
+{
+}
+DEFINE_CCW_MACHINE(6_2, "6.2", true);
+
 static void ccw_machine_6_1_instance_options(MachineState *machine)
 {
+    ccw_machine_6_2_instance_options(machine);
 }
 
 static void ccw_machine_6_1_class_options(MachineClass *mc)
 {
+    ccw_machine_6_2_class_options(mc);
+    compat_props_add(mc->compat_props, hw_compat_6_1, hw_compat_6_1_len);
 }
-DEFINE_CCW_MACHINE(6_1, "6.1", true);
+DEFINE_CCW_MACHINE(6_1, "6.1", false);
 
 static void ccw_machine_6_0_instance_options(MachineState *machine)
 {
diff --git a/include/hw/boards.h b/include/hw/boards.h
index b6161cee88..2832f0f8aa 100644
--- a/include/hw/boards.h
+++ b/include/hw/boards.h
@@ -354,6 +354,9 @@ struct MachineState {
     } \
     type_init(machine_initfn##_register_types)
 
+extern GlobalProperty hw_compat_6_1[];
+extern const size_t hw_compat_6_1_len;
+
 extern GlobalProperty hw_compat_6_0[];
 extern const size_t hw_compat_6_0_len;
 
diff --git a/include/hw/i386/pc.h b/include/hw/i386/pc.h
index 88dffe7517..97b4ab79b5 100644
--- a/include/hw/i386/pc.h
+++ b/include/hw/i386/pc.h
@@ -196,6 +196,9 @@ void pc_system_parse_ovmf_flash(uint8_t *flash_ptr, size_t flash_size);
 void pc_madt_cpu_entry(AcpiDeviceIf *adev, int uid,
                        const CPUArchIdList *apic_ids, GArray *entry);
 
+extern GlobalProperty pc_compat_6_1[];
+extern const size_t pc_compat_6_1_len;
+
 extern GlobalProperty pc_compat_6_0[];
 extern const size_t pc_compat_6_0_len;
 
-- 
2.19.1



^ permalink raw reply related	[flat|nested] 58+ messages in thread

* [PATCH for-6.2 v2 07/11] machine: Prefer cores over sockets in smp parsing since 6.2
  2021-07-19  3:20 [PATCH for-6.2 v2 00/11] machine: smp parsing fixes and improvement Yanan Wang
                   ` (5 preceding siblings ...)
  2021-07-19  3:20 ` [PATCH for-6.2 v2 06/11] hw: Add compat machines for 6.2 Yanan Wang
@ 2021-07-19  3:20 ` Yanan Wang
  2021-07-19  3:40   ` David Gibson
  2021-07-19 17:13   ` Andrew Jones
  2021-07-19  3:20 ` [PATCH for-6.2 v2 08/11] machine: Use ms instead of global current_machine in sanity-check Yanan Wang
                   ` (4 subsequent siblings)
  11 siblings, 2 replies; 58+ messages in thread
From: Yanan Wang @ 2021-07-19  3:20 UTC (permalink / raw)
  To: qemu-devel
  Cc: Peter Maydell, Andrew Jones, Daniel P . Berrangé,
	Eduardo Habkost, Michael S . Tsirkin, wanghaibin.wang,
	Richard Henderson, Greg Kurz, Halil Pasic, yuzenghui,
	Igor Mammedov, Thomas Huth, Paolo Bonzini, Yanan Wang,
	David Gibson

In the real SMP hardware topology world, it's much more likely that
we have high cores-per-socket counts and few sockets totally. While
the current preference of sockets over cores in smp parsing results
in a virtual cpu topology with low cores-per-sockets counts and a
large number of sockets, which is just contrary to the real world.

Given that it is better to make the virtual cpu topology be more
reflective of the real world and also for the sake of compatibility,
we start to prefer cores over sockets over threads in smp parsing
since machine type 6.2 for different arches.

In this patch, a boolean "smp_prefer_sockets" is added, and we only
enable the old preference on older machines and enable the new one
since type 6.2 for all arches by using the machine compat mechanism.

Suggested-by: Daniel P. Berrange <berrange@redhat.com>
Signed-off-by: Yanan Wang <wangyanan55@huawei.com>
---
 hw/arm/virt.c              |  1 +
 hw/core/machine.c          | 59 +++++++++++++++++++++++++++++---------
 hw/i386/pc_piix.c          |  1 +
 hw/i386/pc_q35.c           |  1 +
 hw/ppc/spapr.c             |  1 +
 hw/s390x/s390-virtio-ccw.c |  1 +
 include/hw/boards.h        |  1 +
 qemu-options.hx            |  4 ++-
 8 files changed, 55 insertions(+), 14 deletions(-)

diff --git a/hw/arm/virt.c b/hw/arm/virt.c
index 01165f7f53..7babea40dc 100644
--- a/hw/arm/virt.c
+++ b/hw/arm/virt.c
@@ -2797,6 +2797,7 @@ static void virt_machine_6_1_options(MachineClass *mc)
 {
     virt_machine_6_2_options(mc);
     compat_props_add(mc->compat_props, hw_compat_6_1, hw_compat_6_1_len);
+    mc->smp_prefer_sockets = true;
 }
 DEFINE_VIRT_MACHINE(6, 1)
 
diff --git a/hw/core/machine.c b/hw/core/machine.c
index 63439c4a6d..c074425015 100644
--- a/hw/core/machine.c
+++ b/hw/core/machine.c
@@ -744,6 +744,22 @@ void machine_set_cpu_numa_node(MachineState *machine,
     }
 }
 
+/*
+ * smp_parse - Generic function used to parse the given SMP configuration
+ *
+ * The topology parameters must be specified equal to or great than one
+ * or just omitted, explicit configuration like "cpus=0" is not allowed.
+ * The omitted parameters will be calculated based on the provided ones.
+ *
+ * maxcpus will default to the value of cpus if omitted and will be used
+ * to compute the missing sockets/cores/threads. cpus will be calculated
+ * from the computed parametrs if omitted.
+ *
+ * In calculation of omitted arch-netural sockets/cores/threads, we prefer
+ * sockets over cores over threads before 6.2, while prefer cores over
+ * sockets over threads since 6.2 on. The arch-specific dies will directly
+ * default to 1 if omitted.
+ */
 static void smp_parse(MachineState *ms, SMPConfiguration *config, Error **errp)
 {
     MachineClass *mc = MACHINE_GET_CLASS(ms);
@@ -772,19 +788,36 @@ static void smp_parse(MachineState *ms, SMPConfiguration *config, Error **errp)
 
     maxcpus = maxcpus > 0 ? maxcpus : cpus;
 
-    /* compute missing values, prefer sockets over cores over threads */
-    if (sockets == 0) {
-        cores = cores > 0 ? cores : 1;
-        threads = threads > 0 ? threads : 1;
-        sockets = maxcpus / (dies * cores * threads);
-        sockets = sockets > 0 ? sockets : 1;
-    } else if (cores == 0) {
-        threads = threads > 0 ? threads : 1;
-        cores = maxcpus / (sockets * dies * threads);
-        cores = cores > 0 ? cores : 1;
-    } else if (threads == 0) {
-        threads = maxcpus / (sockets * dies * cores);
-        threads = threads > 0 ? threads : 1;
+    /* prefer sockets over cores over threads before 6.2 */
+    if (mc->smp_prefer_sockets) {
+        if (sockets == 0) {
+            cores = cores > 0 ? cores : 1;
+            threads = threads > 0 ? threads : 1;
+            sockets = maxcpus / (dies * cores * threads);
+            sockets = sockets > 0 ? sockets : 1;
+        } else if (cores == 0) {
+            threads = threads > 0 ? threads : 1;
+            cores = maxcpus / (sockets * dies * threads);
+            cores = cores > 0 ? cores : 1;
+        } else if (threads == 0) {
+            threads = maxcpus / (sockets * dies * cores);
+            threads = threads > 0 ? threads : 1;
+        }
+    /* prefer cores over sockets over threads since 6.2 */
+    } else {
+        if (cores == 0) {
+            sockets = sockets > 0 ? sockets : 1;
+            threads = threads > 0 ? threads : 1;
+            cores = maxcpus / (sockets * dies * threads);
+            cores = cores > 0 ? cores : 1;
+        } else if (sockets == 0) {
+            threads = threads > 0 ? threads : 1;
+            sockets = maxcpus / (dies * cores * threads);
+            sockets = sockets > 0 ? sockets : 1;
+        } else if (threads == 0) {
+            threads = maxcpus / (sockets * dies * cores);
+            threads = threads > 0 ? threads : 1;
+        }
     }
 
     /* use the computed parameters to calculate the omitted cpus */
diff --git a/hw/i386/pc_piix.c b/hw/i386/pc_piix.c
index fd5c2277f2..9b811fc6ca 100644
--- a/hw/i386/pc_piix.c
+++ b/hw/i386/pc_piix.c
@@ -432,6 +432,7 @@ static void pc_i440fx_6_1_machine_options(MachineClass *m)
     m->is_default = false;
     compat_props_add(m->compat_props, hw_compat_6_1, hw_compat_6_1_len);
     compat_props_add(m->compat_props, pc_compat_6_1, pc_compat_6_1_len);
+    m->smp_prefer_sockets = true;
 }
 
 DEFINE_I440FX_MACHINE(v6_1, "pc-i440fx-6.1", NULL,
diff --git a/hw/i386/pc_q35.c b/hw/i386/pc_q35.c
index b45903b15e..88efb7fde4 100644
--- a/hw/i386/pc_q35.c
+++ b/hw/i386/pc_q35.c
@@ -372,6 +372,7 @@ static void pc_q35_6_1_machine_options(MachineClass *m)
     m->alias = NULL;
     compat_props_add(m->compat_props, hw_compat_6_1, hw_compat_6_1_len);
     compat_props_add(m->compat_props, pc_compat_6_1, pc_compat_6_1_len);
+    m->smp_prefer_sockets = true;
 }
 
 DEFINE_Q35_MACHINE(v6_1, "pc-q35-6.1", NULL,
diff --git a/hw/ppc/spapr.c b/hw/ppc/spapr.c
index d39fd4e644..a481fade51 100644
--- a/hw/ppc/spapr.c
+++ b/hw/ppc/spapr.c
@@ -4702,6 +4702,7 @@ static void spapr_machine_6_1_class_options(MachineClass *mc)
 {
     spapr_machine_6_2_class_options(mc);
     compat_props_add(mc->compat_props, hw_compat_6_1, hw_compat_6_1_len);
+    mc->smp_prefer_sockets = true;
 }
 
 DEFINE_SPAPR_MACHINE(6_1, "6.1", false);
diff --git a/hw/s390x/s390-virtio-ccw.c b/hw/s390x/s390-virtio-ccw.c
index 4d25278cf2..b40e647883 100644
--- a/hw/s390x/s390-virtio-ccw.c
+++ b/hw/s390x/s390-virtio-ccw.c
@@ -809,6 +809,7 @@ static void ccw_machine_6_1_class_options(MachineClass *mc)
 {
     ccw_machine_6_2_class_options(mc);
     compat_props_add(mc->compat_props, hw_compat_6_1, hw_compat_6_1_len);
+    mc->smp_prefer_sockets = true;
 }
 DEFINE_CCW_MACHINE(6_1, "6.1", false);
 
diff --git a/include/hw/boards.h b/include/hw/boards.h
index 2832f0f8aa..8df885c9d2 100644
--- a/include/hw/boards.h
+++ b/include/hw/boards.h
@@ -247,6 +247,7 @@ struct MachineClass {
     bool nvdimm_supported;
     bool numa_mem_supported;
     bool smp_dies_supported;
+    bool smp_prefer_sockets;
     bool auto_enable_numa;
     const char *default_ram_id;
 
diff --git a/qemu-options.hx b/qemu-options.hx
index 0c9ddc0274..6ef57e838c 100644
--- a/qemu-options.hx
+++ b/qemu-options.hx
@@ -227,7 +227,9 @@ SRST
     from those which are given. Historically preference was given to the
     coarsest topology parameters when computing missing values (ie sockets
     preferred over cores, which were preferred over threads), however, this
-    behaviour is considered liable to change.
+    behaviour is considered liable to change. The historical preference of
+    sockets over cores over threads works before 6.2, and a new preference
+    of cores over sockets over threads starts to work since 6.2 on.
 ERST
 
 DEF("numa", HAS_ARG, QEMU_OPTION_numa,
-- 
2.19.1



^ permalink raw reply related	[flat|nested] 58+ messages in thread

* [PATCH for-6.2 v2 08/11] machine: Use ms instead of global current_machine in sanity-check
  2021-07-19  3:20 [PATCH for-6.2 v2 00/11] machine: smp parsing fixes and improvement Yanan Wang
                   ` (6 preceding siblings ...)
  2021-07-19  3:20 ` [PATCH for-6.2 v2 07/11] machine: Prefer cores over sockets in smp parsing since 6.2 Yanan Wang
@ 2021-07-19  3:20 ` Yanan Wang
  2021-07-19 17:14   ` Andrew Jones
  2021-07-19  3:20 ` [PATCH for-6.2 v2 09/11] machine: Tweak the order of topology members in struct CpuTopology Yanan Wang
                   ` (3 subsequent siblings)
  11 siblings, 1 reply; 58+ messages in thread
From: Yanan Wang @ 2021-07-19  3:20 UTC (permalink / raw)
  To: qemu-devel
  Cc: Peter Maydell, Andrew Jones, Daniel P . Berrangé,
	Eduardo Habkost, Michael S . Tsirkin, wanghaibin.wang,
	Richard Henderson, Greg Kurz, Halil Pasic, yuzenghui,
	Igor Mammedov, Thomas Huth, Paolo Bonzini, Yanan Wang,
	David Gibson

In the sanity-check of smp_cpus and max_cpus against mc in function
machine_set_smp(), we are now using ms->smp.max_cpus for the check
but using current_machine->smp.max_cpus in the error message.
Tweak this by uniformly using the local ms.

Signed-off-by: Yanan Wang <wangyanan55@huawei.com>
---
 hw/core/machine.c | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/hw/core/machine.c b/hw/core/machine.c
index c074425015..f5620c4d34 100644
--- a/hw/core/machine.c
+++ b/hw/core/machine.c
@@ -896,7 +896,7 @@ static void machine_set_smp(Object *obj, Visitor *v, const char *name,
     } else if (ms->smp.max_cpus > mc->max_cpus) {
         error_setg(errp, "Invalid SMP CPUs %d. The max CPUs "
                    "supported by machine '%s' is %d",
-                   current_machine->smp.max_cpus,
+                   ms->smp.max_cpus,
                    mc->name, mc->max_cpus);
     }
 
-- 
2.19.1



^ permalink raw reply related	[flat|nested] 58+ messages in thread

* [PATCH for-6.2 v2 09/11] machine: Tweak the order of topology members in struct CpuTopology
  2021-07-19  3:20 [PATCH for-6.2 v2 00/11] machine: smp parsing fixes and improvement Yanan Wang
                   ` (7 preceding siblings ...)
  2021-07-19  3:20 ` [PATCH for-6.2 v2 08/11] machine: Use ms instead of global current_machine in sanity-check Yanan Wang
@ 2021-07-19  3:20 ` Yanan Wang
  2021-07-19  3:20 ` [PATCH for-6.2 v2 10/11] machine: Split out the smp parsing code Yanan Wang
                   ` (2 subsequent siblings)
  11 siblings, 0 replies; 58+ messages in thread
From: Yanan Wang @ 2021-07-19  3:20 UTC (permalink / raw)
  To: qemu-devel
  Cc: Peter Maydell, Andrew Jones, Daniel P . Berrangé,
	Eduardo Habkost, Michael S . Tsirkin, wanghaibin.wang,
	Richard Henderson, Greg Kurz, Halil Pasic, yuzenghui,
	Igor Mammedov, Thomas Huth, Paolo Bonzini, Yanan Wang,
	David Gibson

Now that all the possible topology parameters are integrated in struct
CpuTopology, tweak the order of topology members to be "cpus/sockets/
dies/cores/threads/maxcpus" for readability and consistency. We also
tweak the comment by adding explanation of dies parameter.

Furthermore, a comment of struct SMPConfiguration is fixed.

Reviewed-by: Pankaj Gupta <pankaj.gupta@ionos.com>
Reviewed-by: Andrew Jones <drjones@redhat.com>
Signed-off-by: Yanan Wang <wangyanan55@huawei.com>
---
 hw/core/machine.c   | 4 ++--
 include/hw/boards.h | 7 ++++---
 qapi/machine.json   | 2 +-
 3 files changed, 7 insertions(+), 6 deletions(-)

diff --git a/hw/core/machine.c b/hw/core/machine.c
index f5620c4d34..9d24b67ef3 100644
--- a/hw/core/machine.c
+++ b/hw/core/machine.c
@@ -1075,10 +1075,10 @@ static void machine_initfn(Object *obj)
     /* default to mc->default_cpus */
     ms->smp.cpus = mc->default_cpus;
     ms->smp.max_cpus = mc->default_cpus;
-    ms->smp.cores = 1;
+    ms->smp.sockets = 1;
     ms->smp.dies = 1;
+    ms->smp.cores = 1;
     ms->smp.threads = 1;
-    ms->smp.sockets = 1;
 }
 
 static void machine_finalize(Object *obj)
diff --git a/include/hw/boards.h b/include/hw/boards.h
index 8df885c9d2..12ab0f5968 100644
--- a/include/hw/boards.h
+++ b/include/hw/boards.h
@@ -276,17 +276,18 @@ typedef struct DeviceMemoryState {
 /**
  * CpuTopology:
  * @cpus: the number of present logical processors on the machine
- * @cores: the number of cores in one package
- * @threads: the number of threads in one core
  * @sockets: the number of sockets on the machine
+ * @dies: the number of dies in one socket
+ * @cores: the number of cores in one die
+ * @threads: the number of threads in one core
  * @max_cpus: the maximum number of logical processors on the machine
  */
 typedef struct CpuTopology {
     unsigned int cpus;
+    unsigned int sockets;
     unsigned int dies;
     unsigned int cores;
     unsigned int threads;
-    unsigned int sockets;
     unsigned int max_cpus;
 } CpuTopology;
 
diff --git a/qapi/machine.json b/qapi/machine.json
index c11b2e6f73..9272cb3cf8 100644
--- a/qapi/machine.json
+++ b/qapi/machine.json
@@ -1297,7 +1297,7 @@
 #
 # @dies: number of dies per socket in the CPU topology
 #
-# @cores: number of cores per thread in the CPU topology
+# @cores: number of cores per die in the CPU topology
 #
 # @threads: number of threads per core in the CPU topology
 #
-- 
2.19.1



^ permalink raw reply related	[flat|nested] 58+ messages in thread

* [PATCH for-6.2 v2 10/11] machine: Split out the smp parsing code
  2021-07-19  3:20 [PATCH for-6.2 v2 00/11] machine: smp parsing fixes and improvement Yanan Wang
                   ` (8 preceding siblings ...)
  2021-07-19  3:20 ` [PATCH for-6.2 v2 09/11] machine: Tweak the order of topology members in struct CpuTopology Yanan Wang
@ 2021-07-19  3:20 ` Yanan Wang
  2021-07-19 17:20   ` Andrew Jones
  2021-07-19  3:20 ` [PATCH for-6.2 v2 11/11] tests/unit: Add a unit test for smp parsing Yanan Wang
  2021-07-19 16:57 ` [PATCH for-6.2 v2 00/11] machine: smp parsing fixes and improvement Cornelia Huck
  11 siblings, 1 reply; 58+ messages in thread
From: Yanan Wang @ 2021-07-19  3:20 UTC (permalink / raw)
  To: qemu-devel
  Cc: Peter Maydell, Andrew Jones, Daniel P . Berrangé,
	Eduardo Habkost, Michael S . Tsirkin, wanghaibin.wang,
	Richard Henderson, Greg Kurz, Halil Pasic, yuzenghui,
	Igor Mammedov, Thomas Huth, Paolo Bonzini, Yanan Wang,
	David Gibson

We are going to introduce an unit test for the parser smp_parse()
in hw/core/machine.c, but now machine.c is only built in softmmu.

In order to solve the build dependency on the smp parsing code and
avoid building unrelated stuff for the unit tests, move the related
code from machine.c into a new common file, i.e., machine-smp.c.

Signed-off-by: Yanan Wang <wangyanan55@huawei.com>
---
 MAINTAINERS           |   1 +
 hw/core/machine-smp.c | 124 ++++++++++++++++++++++++++++++++++++++++++
 hw/core/machine.c     | 109 -------------------------------------
 hw/core/meson.build   |   1 +
 include/hw/boards.h   |   1 +
 5 files changed, 127 insertions(+), 109 deletions(-)
 create mode 100644 hw/core/machine-smp.c

diff --git a/MAINTAINERS b/MAINTAINERS
index 9100f9a043..70633e3bf4 100644
--- a/MAINTAINERS
+++ b/MAINTAINERS
@@ -1626,6 +1626,7 @@ F: cpu.c
 F: hw/core/cpu.c
 F: hw/core/machine-qmp-cmds.c
 F: hw/core/machine.c
+F: hw/core/machine-smp.c
 F: hw/core/null-machine.c
 F: hw/core/numa.c
 F: hw/cpu/cluster.c
diff --git a/hw/core/machine-smp.c b/hw/core/machine-smp.c
new file mode 100644
index 0000000000..6a00cfe44a
--- /dev/null
+++ b/hw/core/machine-smp.c
@@ -0,0 +1,124 @@
+/*
+ * QEMU Machine (related to SMP configuration)
+ *
+ * Copyright (C) 2014 Red Hat Inc
+ *
+ * Authors:
+ *   Marcel Apfelbaum <marcel.a@redhat.com>
+ *
+ * This work is licensed under the terms of the GNU GPL, version 2 or later.
+ * See the COPYING file in the top-level directory.
+ */
+
+#include "qemu/osdep.h"
+#include "hw/boards.h"
+#include "qapi/error.h"
+
+/*
+ * smp_parse - Generic function used to parse the given SMP configuration
+ *
+ * The topology parameters must be specified equal to or great than one
+ * or just omitted, explicit configuration like "cpus=0" is not allowed.
+ * The omitted parameters will be calculated based on the provided ones.
+ *
+ * maxcpus will default to the value of cpus if omitted and will be used
+ * to compute the missing sockets/cores/threads. cpus will be calculated
+ * from the computed parametrs if omitted.
+ *
+ * In calculation of omitted arch-netural sockets/cores/threads, we prefer
+ * sockets over cores over threads before 6.2, while prefer cores over
+ * sockets over threads since 6.2 on. The arch-specific dies will directly
+ * default to 1 if omitted.
+ */
+void smp_parse(MachineState *ms, SMPConfiguration *config, Error **errp)
+{
+    MachineClass *mc = MACHINE_GET_CLASS(ms);
+    unsigned cpus    = config->has_cpus ? config->cpus : 0;
+    unsigned sockets = config->has_sockets ? config->sockets : 0;
+    unsigned dies    = config->has_dies ? config->dies : 1;
+    unsigned cores   = config->has_cores ? config->cores : 0;
+    unsigned threads = config->has_threads ? config->threads : 0;
+    unsigned maxcpus = config->has_maxcpus ? config->maxcpus : 0;
+
+    if ((config->has_cpus && config->cpus == 0) ||
+        (config->has_sockets && config->sockets == 0) ||
+        (config->has_dies && config->dies == 0) ||
+        (config->has_cores && config->cores == 0) ||
+        (config->has_threads && config->threads == 0) ||
+        (config->has_maxcpus && config->maxcpus == 0)) {
+        error_setg(errp, "parameters must be equal to or greater than one"
+                   "if provided");
+        return;
+    }
+
+    if (!mc->smp_dies_supported && dies > 1) {
+        error_setg(errp, "dies not supported by this machine's CPU topology");
+        return;
+    }
+
+    maxcpus = maxcpus > 0 ? maxcpus : cpus;
+
+    /* prefer sockets over cores over threads before 6.2 */
+    if (mc->smp_prefer_sockets) {
+        if (sockets == 0) {
+            cores = cores > 0 ? cores : 1;
+            threads = threads > 0 ? threads : 1;
+            sockets = maxcpus / (dies * cores * threads);
+            sockets = sockets > 0 ? sockets : 1;
+        } else if (cores == 0) {
+            threads = threads > 0 ? threads : 1;
+            cores = maxcpus / (sockets * dies * threads);
+            cores = cores > 0 ? cores : 1;
+        } else if (threads == 0) {
+            threads = maxcpus / (sockets * dies * cores);
+            threads = threads > 0 ? threads : 1;
+        }
+    /* prefer cores over sockets over threads since 6.2 */
+    } else {
+        if (cores == 0) {
+            sockets = sockets > 0 ? sockets : 1;
+            threads = threads > 0 ? threads : 1;
+            cores = maxcpus / (sockets * dies * threads);
+            cores = cores > 0 ? cores : 1;
+        } else if (sockets == 0) {
+            threads = threads > 0 ? threads : 1;
+            sockets = maxcpus / (dies * cores * threads);
+            sockets = sockets > 0 ? sockets : 1;
+        } else if (threads == 0) {
+            threads = maxcpus / (sockets * dies * cores);
+            threads = threads > 0 ? threads : 1;
+        }
+    }
+
+    /* use the computed parameters to calculate the omitted cpus */
+    cpus = cpus > 0 ? cpus : sockets * dies * cores * threads;
+    maxcpus = maxcpus > 0 ? maxcpus : cpus;
+
+    if (sockets * dies * cores * threads != maxcpus) {
+        g_autofree char *dies_msg = g_strdup_printf(
+            mc->smp_dies_supported ? " * dies (%u)" : "", dies);
+        error_setg(errp, "Invalid CPU topology: "
+                   "sockets (%u)%s * cores (%u) * threads (%u) "
+                   "!= maxcpus (%u)",
+                   sockets, dies_msg, cores, threads,
+                   maxcpus);
+        return;
+    }
+
+    if (sockets * dies * cores * threads < cpus) {
+        g_autofree char *dies_msg = g_strdup_printf(
+            mc->smp_dies_supported ? " * dies (%u)" : "", dies);
+        error_setg(errp, "Invalid CPU topology: "
+                   "sockets (%u)%s * cores (%u) * threads (%u) < "
+                   "smp_cpus (%u)",
+                   sockets, dies_msg, cores, threads, cpus);
+        return;
+    }
+
+    ms->smp.cpus = cpus;
+    ms->smp.sockets = sockets;
+    ms->smp.dies = dies;
+    ms->smp.cores = cores;
+    ms->smp.threads = threads;
+    ms->smp.max_cpus = maxcpus;
+}
diff --git a/hw/core/machine.c b/hw/core/machine.c
index 9d24b67ef3..61be266b6c 100644
--- a/hw/core/machine.c
+++ b/hw/core/machine.c
@@ -744,115 +744,6 @@ void machine_set_cpu_numa_node(MachineState *machine,
     }
 }
 
-/*
- * smp_parse - Generic function used to parse the given SMP configuration
- *
- * The topology parameters must be specified equal to or great than one
- * or just omitted, explicit configuration like "cpus=0" is not allowed.
- * The omitted parameters will be calculated based on the provided ones.
- *
- * maxcpus will default to the value of cpus if omitted and will be used
- * to compute the missing sockets/cores/threads. cpus will be calculated
- * from the computed parametrs if omitted.
- *
- * In calculation of omitted arch-netural sockets/cores/threads, we prefer
- * sockets over cores over threads before 6.2, while prefer cores over
- * sockets over threads since 6.2 on. The arch-specific dies will directly
- * default to 1 if omitted.
- */
-static void smp_parse(MachineState *ms, SMPConfiguration *config, Error **errp)
-{
-    MachineClass *mc = MACHINE_GET_CLASS(ms);
-    unsigned cpus    = config->has_cpus ? config->cpus : 0;
-    unsigned sockets = config->has_sockets ? config->sockets : 0;
-    unsigned dies    = config->has_dies ? config->dies : 1;
-    unsigned cores   = config->has_cores ? config->cores : 0;
-    unsigned threads = config->has_threads ? config->threads : 0;
-    unsigned maxcpus = config->has_maxcpus ? config->maxcpus : 0;
-
-    if ((config->has_cpus && config->cpus == 0) ||
-        (config->has_sockets && config->sockets == 0) ||
-        (config->has_dies && config->dies == 0) ||
-        (config->has_cores && config->cores == 0) ||
-        (config->has_threads && config->threads == 0) ||
-        (config->has_maxcpus && config->maxcpus == 0)) {
-        error_setg(errp, "parameters must be equal to or greater than one"
-                   "if provided");
-        return;
-    }
-
-    if (!mc->smp_dies_supported && dies > 1) {
-        error_setg(errp, "dies not supported by this machine's CPU topology");
-        return;
-    }
-
-    maxcpus = maxcpus > 0 ? maxcpus : cpus;
-
-    /* prefer sockets over cores over threads before 6.2 */
-    if (mc->smp_prefer_sockets) {
-        if (sockets == 0) {
-            cores = cores > 0 ? cores : 1;
-            threads = threads > 0 ? threads : 1;
-            sockets = maxcpus / (dies * cores * threads);
-            sockets = sockets > 0 ? sockets : 1;
-        } else if (cores == 0) {
-            threads = threads > 0 ? threads : 1;
-            cores = maxcpus / (sockets * dies * threads);
-            cores = cores > 0 ? cores : 1;
-        } else if (threads == 0) {
-            threads = maxcpus / (sockets * dies * cores);
-            threads = threads > 0 ? threads : 1;
-        }
-    /* prefer cores over sockets over threads since 6.2 */
-    } else {
-        if (cores == 0) {
-            sockets = sockets > 0 ? sockets : 1;
-            threads = threads > 0 ? threads : 1;
-            cores = maxcpus / (sockets * dies * threads);
-            cores = cores > 0 ? cores : 1;
-        } else if (sockets == 0) {
-            threads = threads > 0 ? threads : 1;
-            sockets = maxcpus / (dies * cores * threads);
-            sockets = sockets > 0 ? sockets : 1;
-        } else if (threads == 0) {
-            threads = maxcpus / (sockets * dies * cores);
-            threads = threads > 0 ? threads : 1;
-        }
-    }
-
-    /* use the computed parameters to calculate the omitted cpus */
-    cpus = cpus > 0 ? cpus : sockets * dies * cores * threads;
-    maxcpus = maxcpus > 0 ? maxcpus : cpus;
-
-    if (sockets * dies * cores * threads != maxcpus) {
-        g_autofree char *dies_msg = g_strdup_printf(
-            mc->smp_dies_supported ? " * dies (%u)" : "", dies);
-        error_setg(errp, "Invalid CPU topology: "
-                   "sockets (%u)%s * cores (%u) * threads (%u) "
-                   "!= maxcpus (%u)",
-                   sockets, dies_msg, cores, threads,
-                   maxcpus);
-        return;
-    }
-
-    if (sockets * dies * cores * threads < cpus) {
-        g_autofree char *dies_msg = g_strdup_printf(
-            mc->smp_dies_supported ? " * dies (%u)" : "", dies);
-        error_setg(errp, "Invalid CPU topology: "
-                   "sockets (%u)%s * cores (%u) * threads (%u) < "
-                   "smp_cpus (%u)",
-                   sockets, dies_msg, cores, threads, cpus);
-        return;
-    }
-
-    ms->smp.cpus = cpus;
-    ms->smp.sockets = sockets;
-    ms->smp.dies = dies;
-    ms->smp.cores = cores;
-    ms->smp.threads = threads;
-    ms->smp.max_cpus = maxcpus;
-}
-
 static void machine_get_smp(Object *obj, Visitor *v, const char *name,
                             void *opaque, Error **errp)
 {
diff --git a/hw/core/meson.build b/hw/core/meson.build
index 18f44fb7c2..6d727c7742 100644
--- a/hw/core/meson.build
+++ b/hw/core/meson.build
@@ -14,6 +14,7 @@ hwcore_files = files(
 )
 
 common_ss.add(files('cpu-common.c'))
+common_ss.add(files('machine-smp.c'))
 common_ss.add(when: 'CONFIG_FITLOADER', if_true: files('loader-fit.c'))
 common_ss.add(when: 'CONFIG_GENERIC_LOADER', if_true: files('generic-loader.c'))
 common_ss.add(when: ['CONFIG_GUEST_LOADER', fdt], if_true: files('guest-loader.c'))
diff --git a/include/hw/boards.h b/include/hw/boards.h
index 12ab0f5968..071eec1e74 100644
--- a/include/hw/boards.h
+++ b/include/hw/boards.h
@@ -34,6 +34,7 @@ HotpluggableCPUList *machine_query_hotpluggable_cpus(MachineState *machine);
 void machine_set_cpu_numa_node(MachineState *machine,
                                const CpuInstanceProperties *props,
                                Error **errp);
+void smp_parse(MachineState *ms, SMPConfiguration *config, Error **errp);
 
 /**
  * machine_class_allow_dynamic_sysbus_dev: Add type to list of valid devices
-- 
2.19.1



^ permalink raw reply related	[flat|nested] 58+ messages in thread

* [PATCH for-6.2 v2 11/11] tests/unit: Add a unit test for smp parsing
  2021-07-19  3:20 [PATCH for-6.2 v2 00/11] machine: smp parsing fixes and improvement Yanan Wang
                   ` (9 preceding siblings ...)
  2021-07-19  3:20 ` [PATCH for-6.2 v2 10/11] machine: Split out the smp parsing code Yanan Wang
@ 2021-07-19  3:20 ` Yanan Wang
  2021-07-19 18:57   ` Andrew Jones
  2021-07-19 16:57 ` [PATCH for-6.2 v2 00/11] machine: smp parsing fixes and improvement Cornelia Huck
  11 siblings, 1 reply; 58+ messages in thread
From: Yanan Wang @ 2021-07-19  3:20 UTC (permalink / raw)
  To: qemu-devel
  Cc: Peter Maydell, Andrew Jones, Daniel P . Berrangé,
	Eduardo Habkost, Michael S . Tsirkin, wanghaibin.wang,
	Richard Henderson, Greg Kurz, Halil Pasic, yuzenghui,
	Igor Mammedov, Thomas Huth, Paolo Bonzini, Yanan Wang,
	David Gibson

Add a QEMU unit test for the parsing of given SMP configuration.
Since all the parsing logic is in generic function smp_parse(),
this test passes diffenent SMP configurations to the function
and compare the parsing result with what is expected.

In the test, all possible collections of the topology parameters
and the corressponding expected results are listed, including the
valid and invalid ones.

The preference of sockets over cores and the preference of cores
over sockets, and the support of multi-dies are also considered.

Signed-off-by: Yanan Wang <wangyanan55@huawei.com>
---
 MAINTAINERS                 |    1 +
 tests/unit/meson.build      |    1 +
 tests/unit/test-smp-parse.c | 1117 +++++++++++++++++++++++++++++++++++
 3 files changed, 1119 insertions(+)
 create mode 100644 tests/unit/test-smp-parse.c

diff --git a/MAINTAINERS b/MAINTAINERS
index 70633e3bf4..160dba2e57 100644
--- a/MAINTAINERS
+++ b/MAINTAINERS
@@ -1636,6 +1636,7 @@ F: include/hw/boards.h
 F: include/hw/core/cpu.h
 F: include/hw/cpu/cluster.h
 F: include/sysemu/numa.h
+F: tests/unit/test-smp-parse.c
 T: git https://gitlab.com/ehabkost/qemu.git machine-next
 
 Xtensa Machines
diff --git a/tests/unit/meson.build b/tests/unit/meson.build
index 3e0504dd21..694a924627 100644
--- a/tests/unit/meson.build
+++ b/tests/unit/meson.build
@@ -44,6 +44,7 @@ tests = {
   'test-uuid': [],
   'ptimer-test': ['ptimer-test-stubs.c', meson.source_root() / 'hw/core/ptimer.c'],
   'test-qapi-util': [],
+  'test-smp-parse': [qom, meson.source_root() / 'hw/core/machine-smp.c'],
 }
 
 if have_system or have_tools
diff --git a/tests/unit/test-smp-parse.c b/tests/unit/test-smp-parse.c
new file mode 100644
index 0000000000..bc1d324c3d
--- /dev/null
+++ b/tests/unit/test-smp-parse.c
@@ -0,0 +1,1117 @@
+/*
+ * SMP parsing unit-tests
+ *
+ * Copyright (C) 2021, Huawei, Inc.
+ *
+ * Authors:
+ *  Yanan Wang <wangyanan55@huawei.com>
+ *
+ * This work is licensed under the terms of the GNU LGPL, version 2.1 or later.
+ * See the COPYING.LIB file in the top-level directory.
+ */
+
+#include "qemu/osdep.h"
+#include "qom/object.h"
+#include "qemu/module.h"
+#include "qapi/error.h"
+
+#include "hw/boards.h"
+
+#define T true
+#define F false
+
+/**
+ * SMPTestData:
+ * @config - the given SMP configuration for parsing
+ * @should_be_valid - whether the given configuration is supposed to be valid
+ * @expect - the CPU topology info expected to be parsed out
+ */
+typedef struct SMPTestData {
+    SMPConfiguration config;
+    bool should_be_valid;
+    CpuTopology expect;
+} SMPTestData;
+
+/* the specific machine type info for this test */
+static const TypeInfo smp_machine_info = {
+    .name = TYPE_MACHINE,
+    .parent = TYPE_OBJECT,
+    .class_size = sizeof(MachineClass),
+    .instance_size = sizeof(MachineState),
+};
+
+/*
+ * prefer sockets over cores over threads before 6.2.
+ * all possible SMP configurations and the corressponding expected outputs
+ * are listed for testing, including the valid and invalid ones.
+ */
+static struct SMPTestData prefer_sockets[] = {
+    {
+        /* config: no smp configuration provided
+         * expect: cpus=1,sockets=1,dies=1,cores=1,threads=1,maxcpus=1 */
+        .config = (SMPConfiguration) { F, 0, F, 0, F, 0, F, 0, F, 0, F, 0 },
+        .should_be_valid = true,
+        .expect = (CpuTopology) { 1, 1, 1, 1, 1, 1 },
+    }, {
+        /* config: -smp 8
+         * expect: cpus=8,sockets=8,dies=1,cores=1,threads=1,maxcpus=8 */
+        .config = (SMPConfiguration) { T, 8, F, 0, F, 0, F, 0, F, 0, F, 0 },
+        .should_be_valid = true,
+        .expect = (CpuTopology) { 8, 8, 1, 1, 1, 8 },
+    }, {
+        /* config: -smp sockets=2
+         * expect: cpus=2,sockets=2,dies=1,cores=1,threads=1,maxcpus=2 */
+        .config = (SMPConfiguration) { F, 0, T, 2, F, 0, F, 0, F, 0, F, 0 },
+        .should_be_valid = true,
+        .expect = (CpuTopology) { 2, 2, 1, 1, 1, 2 },
+    }, {
+        /* config: -smp cores=4
+         * expect: cpus=4,sockets=1,dies=1,cores=4,threads=1,maxcpus=4 */
+        .config = (SMPConfiguration) { F, 0, F, 0, F, 0, T, 4, F, 0, F, 0 },
+        .should_be_valid = true,
+        .expect = (CpuTopology) { 4, 1, 1, 4, 1, 4 },
+    }, {
+        /* config: -smp threads=2
+         * expect: cpus=2,sockets=1,dies=1,cores=1,threads=2,maxcpus=2 */
+        .config = (SMPConfiguration) { F, 0, F, 0, F, 0, F, 0, T, 2, F, 0 },
+        .should_be_valid = true,
+        .expect = (CpuTopology) { 2, 1, 1, 1, 2, 2 },
+    }, {
+        /* config: -smp maxcpus=16
+         * expect: cpus=16,sockets=16,dies=1,cores=1,threads=1,maxcpus=16 */
+        .config = (SMPConfiguration) { F, 0, F, 0, F, 0, F, 0, F, 0, T, 16 },
+        .should_be_valid = true,
+        .expect = (CpuTopology) { 16, 16, 1, 1, 1, 16 },
+    }, {
+        /* config: -smp 8,sockets=2
+         * expect: cpus=8,sockets=2,dies=1,cores=4,threads=1,maxcpus=8 */
+        .config = (SMPConfiguration) { T, 8, T, 2, F, 0, F, 0, F, 0, F, 0 },
+        .should_be_valid = true,
+        .expect = (CpuTopology) { 8, 2, 1, 4, 1, 8 },
+    }, {
+        /* config: -smp 8,cores=4
+         * expect: cpus=8,sockets=2,dies=1,cores=4,threads=1,maxcpus=8 */
+        .config = (SMPConfiguration) { T, 8, F, 0, F, 0, T, 4, F, 0, F, 0 },
+        .should_be_valid = true,
+        .expect = (CpuTopology) { 8, 2, 1, 4, 1, 8 },
+    }, {
+        /* config: -smp 8,threads=2
+         * expect: cpus=8,sockets=4,dies=1,cores=1,threads=2,maxcpus=8 */
+        .config = (SMPConfiguration) { T, 8, F, 0, F, 0, F, 0, T, 2, F, 0 },
+        .should_be_valid = true,
+        .expect = (CpuTopology) { 8, 4, 1, 1, 2, 8 },
+    }, {
+        /* config: -smp 8,maxcpus=16
+         * expect: cpus=8,sockets=16,dies=1,cores=1,threads=1,maxcpus=16 */
+        .config = (SMPConfiguration) { T, 8, F, 0, F, 0, F, 0, F, 0, T, 16 },
+        .should_be_valid = true,
+        .expect = (CpuTopology) { 8, 16, 1, 1, 1, 16 },
+    }, {
+        /* config: -smp sockets=2,cores=4
+         * expect: cpus=8,sockets=2,dies=1,cores=4,threads=1,maxcpus=8 */
+        .config = (SMPConfiguration) { F, 0, T, 2, F, 0, T, 4, F, 0, F, 0 },
+        .should_be_valid = true,
+        .expect = (CpuTopology) { 8, 2, 1, 4, 1, 8 },
+    }, {
+        /* config: -smp sockets=2,threads=2
+         * expect: cpus=4,sockets=2,dies=1,cores=1,threads=2,maxcpus=4 */
+        .config = (SMPConfiguration) { F, 0, T, 2, F, 0, F, 0, T, 2, F, 0 },
+        .should_be_valid = true,
+        .expect = (CpuTopology) { 4, 2, 1, 1, 2, 4 },
+    }, {
+        /* config: -smp sockets=2,maxcpus=16
+         * expect: cpus=16,sockets=2,dies=1,cores=8,threads=1,maxcpus=16 */
+        .config = (SMPConfiguration) { F, 0, T, 2, F, 0, F, 0, F, 0, T, 16 },
+        .should_be_valid = true,
+        .expect = (CpuTopology) { 16, 2, 1, 8, 1, 16 },
+    }, {
+        /* config: -smp cores=4,threads=2
+         * expect: cpus=8,sockets=1,dies=1,cores=4,threads=2,maxcpus=8 */
+        .config = (SMPConfiguration) { F, 0, F, 0, F, 0, T, 4, T, 2, F, 0 },
+        .should_be_valid = true,
+        .expect = (CpuTopology) { 8, 1, 1, 4, 2, 8 },
+    }, {
+        /* config: -smp cores=4,maxcpus=16
+         * expect: cpus=16,sockets=4,dies=1,cores=4,threads=1,maxcpus=16 */
+        .config = (SMPConfiguration) { F, 0, F, 0, F, 0, T, 4, F, 0, T, 16 },
+        .should_be_valid = true,
+        .expect = (CpuTopology) { 16, 4, 1, 4, 1, 16 },
+    }, {
+        /* config: -smp threads=2,maxcpus=16
+         * expect: cpus=16,sockets=8,dies=1,cores=1,threads=2,maxcpus=16 */
+        .config = (SMPConfiguration) { F, 0, F, 0, F, 0, F, 0, T, 2, T, 16 },
+        .should_be_valid = true,
+        .expect = (CpuTopology) { 16, 8, 1, 1, 2, 16 },
+    }, {
+        /* config: -smp 8,sockets=2,cores=4
+         * expect: cpus=8,sockets=2,dies=1,cores=4,threads=1,maxcpus=8 */
+        .config = (SMPConfiguration) { T, 8, T, 2, F, 0, T, 4, F, 0, F, 0 },
+        .should_be_valid = true,
+        .expect = (CpuTopology) { 8, 2, 1, 4, 1, 8 },
+    }, {
+        /* config: -smp 8,sockets=2,threads=2
+         * expect: cpus=8,sockets=2,dies=1,cores=2,threads=2,maxcpus=8 */
+        .config = (SMPConfiguration) { T, 8, T, 2, F, 0, F, 0, T, 2, F, 0 },
+        .should_be_valid = true,
+        .expect = (CpuTopology) { 8, 2, 1, 2, 2, 8 },
+    }, {
+        /* config: -smp 8,sockets=2,maxcpus=16
+         * expect: cpus=8,sockets=2,dies=1,cores=8,threads=1,maxcpus=16 */
+        .config = (SMPConfiguration) { T, 8, T, 2, F, 0, F, 0, F, 0, T, 16 },
+        .should_be_valid = true,
+        .expect = (CpuTopology) { 8, 2, 1, 8, 1, 16 },
+    }, {
+        /* config: -smp 8,cores=4,threads=2
+         * expect: cpus=8,sockets=1,dies=1,cores=4,threads=2,maxcpus=8 */
+        .config = (SMPConfiguration) { T, 8, F, 0, F, 0, T, 4, T, 2, F, 0 },
+        .should_be_valid = true,
+        .expect = (CpuTopology) { 8, 1, 1, 4, 2, 8 },
+    }, {
+        /* config: -smp 8,cores=4,maxcpus=16
+         * expect: cpus=8,sockets=4,dies=1,cores=4,threads=1,maxcpus=16 */
+        .config = (SMPConfiguration) { T, 8, F, 0, F, 0, T, 4, F, 0, T, 16 },
+        .should_be_valid = true,
+        .expect = (CpuTopology) { 8, 4, 1, 4, 1, 16 },
+    }, {
+        /* config: -smp 8,threads=2,maxcpus=16
+         * expect: cpus=8,sockets=8,dies=1,cores=1,threads=2,maxcpus=16 */
+        .config = (SMPConfiguration) { T, 8, F, 0, F, 0, F, 0, T, 2, T, 16 },
+        .should_be_valid = true,
+        .expect = (CpuTopology) { 8, 8, 1, 1, 2, 16 },
+    }, {
+        /* config: -smp sockets=2,cores=4,threads=2
+         * expect: cpus=16,sockets=2,dies=1,cores=4,threads=2,maxcpus=16 */
+        .config = (SMPConfiguration) { F, 0, T, 2, F, 0, T, 4, T, 2, F, 0 },
+        .should_be_valid = true,
+        .expect = (CpuTopology) { 16, 2, 1, 4, 2, 16 },
+    }, {
+        /* config: -smp sockets=2,cores=4,maxcpus=16
+         * expect: cpus=16,sockets=2,dies=1,cores=4,threads=2,maxcpus=16 */
+        .config = (SMPConfiguration) { F, 0, T, 2, F, 0, T, 4, F, 0, T, 16 },
+        .should_be_valid = true,
+        .expect = (CpuTopology) { 16, 2, 1, 4, 2, 16 },
+    }, {
+        /* config: -smp sockets=2,threads=2,maxcpus=16
+         * expect: cpus=16,sockets=2,dies=1,cores=4,threads=2,maxcpus=16 */
+        .config = (SMPConfiguration) { F, 0, T, 2, F, 0, F, 0, T, 2, T, 16 },
+        .should_be_valid = true,
+        .expect = (CpuTopology) { 16, 2, 1, 4, 2, 16 },
+    }, {
+        /* config: -smp cores=4,threads=2,maxcpus=16
+         * expect: cpus=16,sockets=2,dies=1,cores=4,threads=2,maxcpus=16 */
+        .config = (SMPConfiguration) { F, 0, F, 0, F, 0, T, 4, T, 2, T, 16 },
+        .should_be_valid = true,
+        .expect = (CpuTopology) { 16, 2, 1, 4, 2, 16 },
+    }, {
+        /* config: -smp 8,sockets=2,cores=4,threads=1
+         * expect: cpus=8,sockets=2,dies=1,cores=4,threads=1,maxcpus=8 */
+        .config = (SMPConfiguration) { T, 8, T, 2, F, 0, T, 4, T, 1, F, 0 },
+        .should_be_valid = true,
+        .expect = (CpuTopology) { 8, 2, 1, 4, 1, 8 },
+    }, {
+        /* config: -smp 8,sockets=2,cores=4,maxcpus=16
+         * expect: cpus=8,sockets=2,dies=1,cores=4,threads=2,maxcpus=16 */
+        .config = (SMPConfiguration) { T, 8, T, 2, F, 0, T, 4, F, 0, T, 16 },
+        .should_be_valid = true,
+        .expect = (CpuTopology) { 8, 2, 1, 4, 2, 16 },
+    }, {
+        /* config: -smp 8,sockets=2,threads=2,maxcpus=16
+         * expect: cpus=8,sockets=2,dies=1,cores=4,threads=2,maxcpus=16 */
+        .config = (SMPConfiguration) { T, 8, T, 2, F, 0, F, 0, T, 2, T, 16 },
+        .should_be_valid = true,
+        .expect = (CpuTopology) { 8, 2, 1, 4, 2, 16 },
+    }, {
+        /* config: -smp 8,cores=4,threads=2,maxcpus=16
+         * expect: cpus=8,sockets=2,dies=1,cores=4,threads=2,maxcpus=16 */
+        .config = (SMPConfiguration) { T, 8, F, 0, F, 0, T, 4, T, 2, T, 16 },
+        .should_be_valid = true,
+        .expect = (CpuTopology) { 8, 2, 1, 4, 2, 16 },
+    }, {
+        /* config: -smp sockets=2,cores=4,threads=2,maxcpus=16
+         * expect: -smp 16,sockets=2,dies=1,cores=4,threads=2,maxcpus=16 */
+        .config = (SMPConfiguration) { F, 0, T, 2, F, 0, T, 4, T, 2, T, 16 },
+        .should_be_valid = true,
+        .expect = (CpuTopology) { 16, 2, 1, 4, 2, 16 },
+    }, {
+        /* config: -smp 8,sockets=2,cores=4,threads=2,maxcpus=16
+         * expect: cpus=8,sockets=2,dies=1,cores=4,threads=2,maxcpus=16 */
+        .config = (SMPConfiguration) { T, 8, T, 2, F, 0, T, 4, T, 2, T, 16 },
+        .should_be_valid = true,
+        .expect = (CpuTopology) { 8, 2, 1, 4, 2, 16 },
+    }, {
+        /* config: -smp 8,sockets=2,dies=1,cores=4,threads=2,maxcpus=16
+         * expect: cpus=8,sockets=2,dies=1,cores=4,threads=2,maxcpus=16 */
+        .config = (SMPConfiguration) { T, 8, T, 2, T, 1, T, 4, T, 2, T, 16 },
+        .should_be_valid = true,
+        .expect = (CpuTopology) { 8, 2, 1, 4, 2, 16 },
+    }, {
+        /* config: -smp 0
+         * expect: error, "anything=0" is not allowed */
+        .config = (SMPConfiguration) { T, 0, F, 0, F, 0, F, 0, F, 0, F, 0 },
+        .should_be_valid = false,
+    }, {
+        /* config: -smp 8,sockets=0
+         * expect: error, "anything=0" is not allowed */
+        .config = (SMPConfiguration) { T, 8, T, 0, F, 0, F, 0, F, 0, F, 0 },
+        .should_be_valid = false,
+    }, {
+        /* config: -smp 8,sockets=2,dies=0
+         * expect: error, "anything=0" is not allowed */
+        .config = (SMPConfiguration) { T, 0, T, 2, T, 0, F, 0, F, 0, F, 0 },
+        .should_be_valid = false,
+    }, {
+        /* config: -smp 8,sockets=2,dies=1,cores=0
+         * expect: error, "anything=0" is not allowed */
+        .config = (SMPConfiguration) { T, 8, T, 2, T, 1, T, 0, F, 0, F, 0 },
+        .should_be_valid = false,
+    }, {
+        /* config: -smp 8,sockets=2,dies=1,cores=4,threads=0
+         * expect: error, "anything=0" is not allowed */
+        .config = (SMPConfiguration) { T, 8, T, 2, T, 1, T, 4, T, 0, F, 0 },
+        .should_be_valid = false,
+    }, {
+        /* config: -smp 8,sockets=2,dies=1,cores=4,threads=2,maxcpus=0
+         * expect: error, "anything=0" is not allowed */
+        .config = (SMPConfiguration) { T, 8, T, 2, T, 1, T, 4, T, 2, T, 0 },
+        .should_be_valid = false,
+    }, {
+        /* config: -smp 8,dies=2
+         * expect: error, multi-dies not supported */
+        .config = (SMPConfiguration) { T, 8, F, 0, T, 2, F, 0, F, 0, F, 0 },
+        .should_be_valid = false,
+    }, {
+        /* config: -smp 8,sockets=2,cores=8
+         * expect: error, sum (16) != max_cpus (8) */
+        .config = (SMPConfiguration) { T, 8, T, 2, F, 0, T, 4, T, 2, F, 0 },
+        .should_be_valid = false,
+    }, {
+        /* config: -smp 8,sockets=2,cores=5,threads=2,maxcpus=16
+         * expect: error, sum (20) != max_cpus (16) */
+        .config = (SMPConfiguration) { F, 0, T, 3, F, 0, T, 5, T, 1, T, 16 },
+        .should_be_valid = false,
+    }, {
+        /* config: -smp 16,maxcpus=12
+         * expect: error, sum (12) < smp_cpus (16) */
+        .config = (SMPConfiguration) { T, 16, F, 0, F, 0, F, 0, F, 0, T, 12 },
+        .should_be_valid = false,
+    },
+};
+
+static struct SMPTestData prefer_sockets_support_dies[] = {
+    {
+        /* config: -smp dies=2
+         * expect: cpus=2,sockets=1,dies=2,cores=1,threads=1,maxcpus=2 */
+        .config = (SMPConfiguration) { F, 0, F, 0, T, 2, F, 0, F, 0, F, 0 },
+        .should_be_valid = true,
+        .expect = (CpuTopology) { 2, 1, 2, 1, 1, 2 },
+    }, {
+        /* config: -smp 16,dies=2
+         * expect: cpus=16,sockets=8,dies=2,cores=1,threads=1,maxcpus=16 */
+        .config = (SMPConfiguration) { T, 16, F, 0, T, 2, F, 0, F, 0, F, 0 },
+        .should_be_valid = true,
+        .expect = (CpuTopology) { 16, 8, 2, 1, 1, 16 },
+    }, {
+        /* config: -smp sockets=2,dies=2
+         * expect: cpus=4,sockets=2,dies=2,cores=1,threads=1,maxcpus=4 */
+        .config = (SMPConfiguration) { F, 0, T, 2, T, 2, F, 0, F, 0, F, 0 },
+        .should_be_valid = true,
+        .expect = (CpuTopology) { 4, 2, 2, 1, 1, 4 },
+    }, {
+        /* config: -smp dies=2,cores=4
+         * expect: cpus=8,sockets=1,dies=2,cores=4,threads=1,maxcpus=8 */
+        .config = (SMPConfiguration) { F, 0, F, 0, T, 2, T, 4, F, 0, F, 0 },
+        .should_be_valid = true,
+        .expect = (CpuTopology) { 8, 1, 2, 4, 1, 8 },
+    }, {
+        /* config: -smp dies=2,threads=2
+         * expect: cpus=4,sockets=1,dies=2,cores=1,threads=2,maxcpus=4 */
+        .config = (SMPConfiguration) { F, 0, F, 0, T, 2, F, 0, T, 2, F, 0 },
+        .should_be_valid = true,
+        .expect = (CpuTopology) { 4, 1, 2, 1, 2, 4 },
+    }, {
+        /* config: -smp dies=2,maxcpus=32
+         * expect: cpus=32,sockets=16,dies=2,cores=1,threads=1,maxcpus=32 */
+        .config = (SMPConfiguration) { F, 0, F, 0, T, 2, F, 0, F, 0, T, 32 },
+        .should_be_valid = true,
+        .expect = (CpuTopology) { 32, 16, 2, 1, 1, 32 },
+    }, {
+        /* config: -smp 16,sockets=2,dies=2
+         * expect: cpus=16,sockets=2,dies=2,cores=4,threads=1,maxcpus=16 */
+        .config = (SMPConfiguration) { T, 16, T, 2, T, 2, F, 0, F, 0, F, 0 },
+        .should_be_valid = true,
+        .expect = (CpuTopology) { 16, 2, 2, 4, 1, 16 },
+    }, {
+        /* config: -smp 16,dies=2,cores=4
+         * expect: cpus=16,sockets=2,dies=2,cores=4,threads=1,maxcpus=16 */
+        .config = (SMPConfiguration) { T, 16, F, 0, T, 2, T, 4, F, 0, F, 0 },
+        .should_be_valid = true,
+        .expect = (CpuTopology) { 16, 2, 2, 4, 1, 16 },
+    }, {
+        /* config: -smp 16,dies=2,threads=2
+         * expect: cpus=16,sockets=4,dies=2,cores=1,threads=2,maxcpus=16 */
+        .config = (SMPConfiguration) { T, 16, F, 0, T, 2, F, 0, T, 2, F, 0 },
+        .should_be_valid = true,
+        .expect = (CpuTopology) { 16, 4, 2, 1, 2, 16 },
+    }, {
+        /* config: -smp 16,dies=2,maxcpus=32
+         * expect: cpus=16,sockets=16,dies=2,cores=1,threads=1,maxcpus=32 */
+        .config = (SMPConfiguration) { T, 16, F, 0, T, 2, F, 0, F, 0, T, 32 },
+        .should_be_valid = true,
+        .expect = (CpuTopology) { 16, 16, 2, 1, 1, 32 },
+    }, {
+        /* config: -smp sockets=2,dies=2,cores=4
+         * expect: cpus=16,sockets=2,dies=2,cores=4,threads=1,maxcpus=16 */
+        .config = (SMPConfiguration) { F, 0, T, 2, T, 2, T, 4, F, 0, F, 0 },
+        .should_be_valid = true,
+        .expect = (CpuTopology) { 16, 2, 2, 4, 1, 16 },
+    }, {
+        /* config: -smp sockets=2,dies=2,threads=2
+         * expect: cpus=8,sockets=2,dies=2,cores=1,threads=2,maxcpus=8 */
+        .config = (SMPConfiguration) { F, 0, T, 2, T, 2, F, 0, T, 2, F, 0 },
+        .should_be_valid = true,
+        .expect = (CpuTopology) { 8, 2, 2, 1, 2, 8 },
+    }, {
+        /* config: -smp sockets=2,dies=2,maxcpus=32
+         * expect: cpus=32,sockets=2,dies=2,cores=8,threads=1,maxcpus=32 */
+        .config = (SMPConfiguration) { F, 0, T, 2, T, 2, F, 0, F, 0, T, 32 },
+        .should_be_valid = true,
+        .expect = (CpuTopology) { 32, 2, 2, 8, 1, 32 },
+    }, {
+        /* config: -smp dies=2,cores=4,threads=2
+         * expect: cpus=16,sockets=1,dies=2,cores=4,threads=2,maxcpus=16 */
+        .config = (SMPConfiguration) { F, 0, F, 0, T, 2, T, 4, T, 2, F, 0 },
+        .should_be_valid = true,
+        .expect = (CpuTopology) { 16, 1, 2, 4, 2, 16 },
+    }, {
+        /* config: -smp dies=2,cores=4,maxcpus=32
+         * expect: cpus=32,sockets=4,dies=2,cores=4,threads=1,maxcpus=32 */
+        .config = (SMPConfiguration) { F, 0, F, 0, T, 2, T, 4, F, 0, T, 32 },
+        .should_be_valid = true,
+        .expect = (CpuTopology) { 32, 4, 2, 4, 1, 32 },
+    }, {
+        /* config: -smp dies=2,threads=2,maxcpus=32
+         * expect: cpus=32,sockets=8,dies=2,cores=1,threads=2,maxcpus=32 */
+        .config = (SMPConfiguration) { F, 0, F, 0, T, 2, F, 0, T, 2, T, 32 },
+        .should_be_valid = true,
+        .expect = (CpuTopology) { 32, 8, 2, 1, 2, 32 },
+    }, {
+        /* config: -smp 16,sockets=2,dies=2,cores=4
+         * expect: cpus=16,sockets=2,dies=2,cores=4,threads=1,maxcpus=16 */
+        .config = (SMPConfiguration) { T, 16, T, 2, T, 2, T, 4, F, 0, F, 0 },
+        .should_be_valid = true,
+        .expect = (CpuTopology) { 16, 2, 2, 4, 1, 16 },
+    }, {
+        /* config: -smp 16,sockets=2,dies=2,threads=2
+         * expect: cpus=16,sockets=2,dies=2,cores=2,threads=2,maxcpus=16 */
+        .config = (SMPConfiguration) { T, 16, T, 2, T, 2, F, 0, T, 2, F, 0 },
+        .should_be_valid = true,
+        .expect = (CpuTopology) { 16, 2, 2, 2, 2, 16 },
+    }, {
+        /* config: -smp 16,sockets=2,dies=2,maxcpus=32
+         * expect: cpus=16,sockets=2,dies=2,cores=8,threads=1,maxcpus=32 */
+        .config = (SMPConfiguration) { T, 16, T, 2, T, 2, F, 0, F, 0, T, 32 },
+        .should_be_valid = true,
+        .expect = (CpuTopology) { 16, 2, 2, 8, 1, 32 },
+    }, {
+        /* config: -smp 16,dies=2,cores=4,threads=2
+         * expect: cpus=16,sockets=1,dies=2,cores=4,threads=2,maxcpus=16 */
+        .config = (SMPConfiguration) { T, 16, F, 0, T, 2, T, 4, T, 2, F, 0 },
+        .should_be_valid = true,
+        .expect = (CpuTopology) { 16, 1, 2, 4, 2, 16 },
+    }, {
+        /* config: -smp 16,dies=2,cores=4,maxcpus=32
+         * expect: cpus=16,sockets=4,dies=2,cores=4,threads=1,maxcpus=32 */
+        .config = (SMPConfiguration) { T, 16, F, 0, T, 2, T, 4, F, 0, T, 32 },
+        .should_be_valid = true,
+        .expect = (CpuTopology) { 16, 4, 2, 4, 1, 32 },
+    }, {
+        /* config: -smp 16,dies=2,threads=2,maxcpus=32
+         * expect: cpus=16,sockets=8,dies=2,cores=1,threads=2,maxcpus=32 */
+        .config = (SMPConfiguration) { T, 16, F, 0, T, 2, F, 0, T, 2, T, 32 },
+        .should_be_valid = true,
+        .expect = (CpuTopology) { 16, 8, 2, 1, 2, 32 },
+    }, {
+        /* config: -smp sockets=2,dies=2,cores=4,threads=2
+         * expect: cpus=32,sockets=2,dies=2,cores=4,threads=2,maxcpus=32 */
+        .config = (SMPConfiguration) { F, 0, T, 2, T, 2, T, 4, T, 2, F, 0 },
+        .should_be_valid = true,
+        .expect = (CpuTopology) { 32, 2, 2, 4, 2, 32 },
+    }, {
+        /* config: -smp sockets=2,dies=2,cores=4,maxcpus=32
+         * expect: cpus=32,sockets=2,dies=2,cores=4,threads=2,maxcpus=32 */
+        .config = (SMPConfiguration) { F, 0, T, 2, T, 2, T, 4, F, 0, T, 32 },
+        .should_be_valid = true,
+        .expect = (CpuTopology) { 32, 2, 2, 4, 2, 32 },
+    }, {
+        /* config: -smp sockets=2,dies=2,threads=2,maxcpus=32
+         * expect: cpus=32,sockets=2,dies=2,cores=4,threads=2,maxcpus=32 */
+        .config = (SMPConfiguration) { F, 0, T, 2, T, 2, F, 0, T, 2, T, 32 },
+        .should_be_valid = true,
+        .expect = (CpuTopology) { 32, 2, 2, 4, 2, 32 },
+    }, {
+        /* config: -smp dies=2,cores=4,threads=2,maxcpus=32
+         * expect: cpus=32,sockets=2,dies=2,cores=4,threads=2,maxcpus=32 */
+        .config = (SMPConfiguration) { F, 0, F, 0, T, 2, T, 4, T, 2, T, 32 },
+        .should_be_valid = true,
+        .expect = (CpuTopology) { 32, 2, 2, 4, 2, 32 },
+    }, {
+        /* config: -smp 16,sockets=2,dies=2,cores=4,threads=1
+         * expect: cpus=16,sockets=2,dies=2,cores=4,threads=1,maxcpus=16 */
+        .config = (SMPConfiguration) { T, 16, T, 2, T, 2, T, 4, T, 1, F, 0 },
+        .should_be_valid = true,
+        .expect = (CpuTopology) { 16, 2, 2, 4, 1, 16 },
+    }, {
+        /* config: -smp 16,sockets=2,dies=2,cores=4,maxcpus=32
+         * expect: cpus=16,sockets=2,dies=2,cores=4,threads=2,maxcpus=32 */
+        .config = (SMPConfiguration) { T, 16, T, 2, T, 2, T, 4, F, 0, T, 32 },
+        .should_be_valid = true,
+        .expect = (CpuTopology) { 16, 2, 2, 4, 2, 32 },
+    }, {
+        /* config: -smp 16,sockets=2,dies=2,threads=2,maxcpus=32
+         * expect: cpus=16,sockets=2,dies=2,cores=4,threads=2,maxcpus=32 */
+        .config = (SMPConfiguration) { T, 16, T, 2, T, 2, F, 0, T, 2, T, 32 },
+        .should_be_valid = true,
+        .expect = (CpuTopology) { 16, 2, 2, 4, 2, 32 },
+    }, {
+        /* config: -smp 16,dies=2,cores=4,threads=2,maxcpus=32
+         * expect: cpus=16,sockets=2,dies=2,cores=4,threads=2,maxcpus=32 */
+        .config = (SMPConfiguration) { T, 16, F, 0, T, 2, T, 4, T, 2, T, 32 },
+        .should_be_valid = true,
+        .expect = (CpuTopology) { 16, 2, 2, 4, 2, 32 },
+    }, {
+        /* config: -smp sockets=2,dies=2,cores=4,threads=2,maxcpus=32
+         * expect: -smp 32,sockets=2,dies=2,cores=4,threads=2,maxcpus=32 */
+        .config = (SMPConfiguration) { F, 0, T, 2, T, 2, T, 4, T, 2, T, 32 },
+        .should_be_valid = true,
+        .expect = (CpuTopology) { 32, 2, 2, 4, 2, 32 },
+    }, {
+        /* config: -smp 16,sockets=2,dies=2,cores=4,threads=2,maxcpus=32
+         * expect: cpus=16,sockets=2,dies=2,cores=4,threads=2,maxcpus=32 */
+        .config = (SMPConfiguration) { T, 16, T, 2, T, 2, T, 4, T, 2, T, 32 },
+        .should_be_valid = true,
+        .expect = (CpuTopology) { 16, 2, 2, 4, 2, 32 },
+    },
+};
+
+/*
+ * prefer cores over sockets over threads since 6.2.
+ * all possible SMP configurations and the corressponding expected outputs
+ * are listed for testing, including the valid and invalid ones.
+ */
+static struct SMPTestData prefer_cores[] = {
+    {
+        /* config: no smp configuration
+         * expect: cpus=1,sockets=1,dies=1,cores=1,threads=1,maxcpus=1 */
+        .config = (SMPConfiguration) { F, 0, F, 0, F, 0, F, 0, F, 0, F, 0 },
+        .should_be_valid = true,
+        .expect = (CpuTopology) { 1, 1, 1, 1, 1, 1 },
+    }, {
+        /* config: -smp 8
+         * expect: cpus=8,sockets=1,dies=1,cores=8,threads=1,maxcpus=8 */
+        .config = (SMPConfiguration) { T, 8, F, 0, F, 0, F, 0, F, 0, F, 0 },
+        .should_be_valid = true,
+        .expect = (CpuTopology) { 8, 1, 1, 8, 1, 8 },
+    }, {
+        /* config: -smp sockets=2
+         * expect: cpus=2,sockets=2,dies=1,cores=1,threads=1,maxcpus=2 */
+        .config = (SMPConfiguration) { F, 0, T, 2, F, 0, F, 0, F, 0, F, 0 },
+        .should_be_valid = true,
+        .expect = (CpuTopology) { 2, 2, 1, 1, 1, 2 },
+    }, {
+        /* config: -smp cores=4
+         * expect: cpus=4,sockets=1,dies=1,cores=4,threads=1,maxcpus=4 */
+        .config = (SMPConfiguration) { F, 0, F, 0, F, 0, T, 4, F, 0, F, 0 },
+        .should_be_valid = true,
+        .expect = (CpuTopology) { 4, 1, 1, 4, 1, 4 },
+    }, {
+        /* config: -smp threads=2
+         * expect: cpus=2,sockets=1,dies=1,cores=1,threads=2,maxcpus=2 */
+        .config = (SMPConfiguration) { F, 0, F, 0, F, 0, F, 0, T, 2, F, 0 },
+        .should_be_valid = true,
+        .expect = (CpuTopology) { 2, 1, 1, 1, 2, 2 },
+    }, {
+        /* config: -smp maxcpus=16
+         * expect: cpus=16,sockets=1,dies=1,cores=16,threads=1,maxcpus=16 */
+        .config = (SMPConfiguration) { F, 0, F, 0, F, 0, F, 0, F, 0, T, 16 },
+        .should_be_valid = true,
+        .expect = (CpuTopology) { 16, 1, 1, 16, 1, 16 },
+    }, {
+        /* config: -smp 8,sockets=2
+         * expect: cpus=8,sockets=2,dies=1,cores=4,threads=1,maxcpus=8 */
+        .config = (SMPConfiguration) { T, 8, T, 2, F, 0, F, 0, F, 0, F, 0 },
+        .should_be_valid = true,
+        .expect = (CpuTopology) { 8, 2, 1, 4, 1, 8 },
+    }, {
+        /* config: -smp 8,cores=4
+         * expect: cpus=8,sockets=2,dies=1,cores=4,threads=1,maxcpus=8 */
+        .config = (SMPConfiguration) { T, 8, F, 0, F, 0, T, 4, F, 0, F, 0 },
+        .should_be_valid = true,
+        .expect = (CpuTopology) { 8, 2, 1, 4, 1, 8 },
+    }, {
+        /* config: -smp 8,threads=2
+         * expect: cpus=8,sockets=1,dies=1,cores=4,threads=2,maxcpus=8 */
+        .config = (SMPConfiguration) { T, 8, F, 0, F, 0, F, 0, T, 2, F, 0 },
+        .should_be_valid = true,
+        .expect = (CpuTopology) { 8, 1, 1, 4, 2, 8 },
+    }, {
+        /* config: -smp 8,maxcpus=16
+         * expect: cpus=8,sockets=1,dies=1,cores=16,threads=1,maxcpus=16 */
+        .config = (SMPConfiguration) { T, 8, F, 0, F, 0, F, 0, F, 0, T, 16 },
+        .should_be_valid = true,
+        .expect = (CpuTopology) { 8, 1, 1, 16, 1, 16 },
+    }, {
+        /* config: -smp sockets=2,cores=4
+         * expect: cpus=8,sockets=2,dies=1,cores=4,threads=1,maxcpus=8 */
+        .config = (SMPConfiguration) { F, 0, T, 2, F, 0, T, 4, F, 0, F, 0 },
+        .should_be_valid = true,
+        .expect = (CpuTopology) { 8, 2, 1, 4, 1, 8 },
+    }, {
+        /* config: -smp sockets=2,threads=2
+         * expect: cpus=4,sockets=2,dies=1,cores=1,threads=2,maxcpus=4 */
+        .config = (SMPConfiguration) { F, 0, T, 2, F, 0, F, 0, T, 2, F, 0 },
+        .should_be_valid = true,
+        .expect = (CpuTopology) { 4, 2, 1, 1, 2, 4 },
+    }, {
+        /* config: -smp sockets=2,maxcpus=16
+         * expect: cpus=16,sockets=2,dies=1,cores=8,threads=1,maxcpus=16 */
+        .config = (SMPConfiguration) { F, 0, T, 2, F, 0, F, 0, F, 0, T, 16 },
+        .should_be_valid = true,
+        .expect = (CpuTopology) { 16, 2, 1, 8, 1, 16 },
+    }, {
+        /* config: -smp cores=4,threads=2
+         * expect: cpus=8,sockets=1,dies=1,cores=4,threads=2,maxcpus=8 */
+        .config = (SMPConfiguration) { F, 0, F, 0, F, 0, T, 4, T, 2, F, 0 },
+        .should_be_valid = true,
+        .expect = (CpuTopology) { 8, 1, 1, 4, 2, 8 },
+    }, {
+        /* config: -smp cores=4,maxcpus=16
+         * expect: cpus=16,sockets=4,dies=1,cores=4,threads=1,maxcpus=16 */
+        .config = (SMPConfiguration) { F, 0, F, 0, F, 0, T, 4, F, 0, T, 16 },
+        .should_be_valid = true,
+        .expect = (CpuTopology) { 16, 4, 1, 4, 1, 16 },
+    }, {
+        /* config: -smp threads=2,maxcpus=16
+         * expect: cpus=16,sockets=1,dies=1,cores=8,threads=2,maxcpus=16 */
+        .config = (SMPConfiguration) { F, 0, F, 0, F, 0, F, 0, T, 2, T, 16 },
+        .should_be_valid = true,
+        .expect = (CpuTopology) { 16, 1, 1, 8, 2, 16 },
+    }, {
+        /* config: -smp 8,sockets=2,cores=4
+         * expect: cpus=8,sockets=2,dies=1,cores=4,threads=1,maxcpus=8 */
+        .config = (SMPConfiguration) { T, 8, T, 2, F, 0, T, 4, F, 0, F, 0 },
+        .should_be_valid = true,
+        .expect = (CpuTopology) { 8, 2, 1, 4, 1, 8 },
+    }, {
+        /* config: -smp 8,sockets=2,threads=2
+         * expect: cpus=8,sockets=2,dies=1,cores=2,threads=2,maxcpus=8 */
+        .config = (SMPConfiguration) { T, 8, T, 2, F, 0, F, 0, T, 2, F, 0 },
+        .should_be_valid = true,
+        .expect = (CpuTopology) { 8, 2, 1, 2, 2, 8 },
+    }, {
+        /* config: -smp 8,sockets=2,maxcpus=16
+         * expect: cpus=8,sockets=2,dies=1,cores=8,threads=1,maxcpus=16 */
+        .config = (SMPConfiguration) { T, 8, T, 2, F, 0, F, 0, F, 0, T, 16 },
+        .should_be_valid = true,
+        .expect = (CpuTopology) { 8, 2, 1, 8, 1, 16 },
+    }, {
+        /* config: -smp 8,cores=4,threads=2
+         * expect: cpus=8,sockets=1,dies=1,cores=4,threads=2,maxcpus=8 */
+        .config = (SMPConfiguration) { T, 8, F, 0, F, 0, T, 4, T, 2, F, 0 },
+        .should_be_valid = true,
+        .expect = (CpuTopology) { 8, 1, 1, 4, 2, 8 },
+    }, {
+        /* config: -smp 8,cores=4,maxcpus=16
+         * expect: cpus=8,sockets=4,dies=1,cores=4,threads=1,maxcpus=16 */
+        .config = (SMPConfiguration) { T, 8, F, 0, F, 0, T, 4, F, 0, T, 16 },
+        .should_be_valid = true,
+        .expect = (CpuTopology) { 8, 4, 1, 4, 1, 16 },
+    }, {
+        /* config: -smp 8,threads=2,maxcpus=16
+         * expect: cpus=8,sockets=1,dies=1,cores=8,threads=2,maxcpus=16 */
+        .config = (SMPConfiguration) { T, 8, F, 0, F, 0, F, 0, T, 2, T, 16 },
+        .should_be_valid = true,
+        .expect = (CpuTopology) { 8, 1, 1, 8, 2, 16 },
+    }, {
+        /* config: -smp sockets=2,cores=4,threads=2
+         * expect: cpus=16,sockets=2,dies=1,cores=4,threads=2,maxcpus=16 */
+        .config = (SMPConfiguration) { F, 0, T, 2, F, 0, T, 4, T, 2, F, 0 },
+        .should_be_valid = true,
+        .expect = (CpuTopology) { 16, 2, 1, 4, 2, 16 },
+    }, {
+        /* config: -smp sockets=2,cores=4,maxcpus=16
+         * expect: cpus=16,sockets=2,dies=1,cores=4,threads=2,maxcpus=16 */
+        .config = (SMPConfiguration) { F, 0, T, 2, F, 0, T, 4, F, 0, T, 16 },
+        .should_be_valid = true,
+        .expect = (CpuTopology) { 16, 2, 1, 4, 2, 16 },
+    }, {
+        /* config: -smp sockets=2,threads=2,maxcpus=16
+         * expect: cpus=16,sockets=2,dies=1,cores=4,threads=2,maxcpus=16 */
+        .config = (SMPConfiguration) { F, 0, T, 2, F, 0, F, 0, T, 2, T, 16 },
+        .should_be_valid = true,
+        .expect = (CpuTopology) { 16, 2, 1, 4, 2, 16 },
+    }, {
+        /* config: -smp cores=4,threads=2,maxcpus=16
+         * expect: cpus=16,sockets=2,dies=1,cores=4,threads=2,maxcpus=16 */
+        .config = (SMPConfiguration) { F, 0, F, 0, F, 0, T, 4, T, 2, T, 16 },
+        .should_be_valid = true,
+        .expect = (CpuTopology) { 16, 2, 1, 4, 2, 16 },
+    }, {
+        /* config: -smp 8,sockets=2,cores=4,threads=1
+         * expect: cpus=8,sockets=2,dies=1,cores=4,threads=1,maxcpus=8 */
+        .config = (SMPConfiguration) { T, 8, T, 2, F, 0, T, 4, T, 1, F, 0 },
+        .should_be_valid = true,
+        .expect = (CpuTopology) { 8, 2, 1, 4, 1, 8 },
+    }, {
+        /* config: -smp 8,sockets=2,cores=4,maxcpus=16
+         * expect: cpus=8,sockets=2,dies=1,cores=4,threads=2,maxcpus=16 */
+        .config = (SMPConfiguration) { T, 8, T, 2, F, 0, T, 4, F, 0, T, 16 },
+        .should_be_valid = true,
+        .expect = (CpuTopology) { 8, 2, 1, 4, 2, 16 },
+    }, {
+        /* config: -smp 8,sockets=2,threads=2,maxcpus=16
+         * expect: cpus=8,sockets=2,dies=1,cores=4,threads=2,maxcpus=16 */
+        .config = (SMPConfiguration) { T, 8, T, 2, F, 0, F, 0, T, 2, T, 16 },
+        .should_be_valid = true,
+        .expect = (CpuTopology) { 8, 2, 1, 4, 2, 16 },
+    }, {
+        /* config: -smp 8,cores=4,threads=2,maxcpus=16
+         * expect: cpus=8,sockets=2,dies=1,cores=4,threads=2,maxcpus=16 */
+        .config = (SMPConfiguration) { T, 8, F, 0, F, 0, T, 4, T, 2, T, 16 },
+        .should_be_valid = true,
+        .expect = (CpuTopology) { 8, 2, 1, 4, 2, 16 },
+    }, {
+        /* config: -smp sockets=2,cores=4,threads=2,maxcpus=16
+         * expect: -smp 16,sockets=2,dies=1,cores=4,threads=2,maxcpus=16 */
+        .config = (SMPConfiguration) { F, 0, T, 2, F, 0, T, 4, T, 2, T, 16 },
+        .should_be_valid = true,
+        .expect = (CpuTopology) { 16, 2, 1, 4, 2, 16 },
+    }, {
+        /* config: -smp 8,sockets=2,cores=4,threads=2,maxcpus=16
+         * expect: cpus=8,sockets=2,dies=1,cores=4,threads=2,maxcpus=16 */
+        .config = (SMPConfiguration) { T, 8, T, 2, F, 0, T, 4, T, 2, T, 16 },
+        .should_be_valid = true,
+        .expect = (CpuTopology) { 8, 2, 1, 4, 2, 16 },
+    }, {
+        /* config: -smp 8,sockets=2,dies=1,cores=4,threads=2,maxcpus=16
+         * expect: cpus=8,sockets=2,dies=1,cores=4,threads=2,maxcpus=16 */
+        .config = (SMPConfiguration) { T, 8, T, 2, T, 1, T, 4, T, 2, T, 16 },
+        .should_be_valid = true,
+        .expect = (CpuTopology) { 8, 2, 1, 4, 2, 16 },
+    }, {
+        /* config: -smp 0
+         * expect: error, "anything=0" is not allowed */
+        .config = (SMPConfiguration) { T, 0, F, 0, F, 0, F, 0, F, 0, F, 0 },
+        .should_be_valid = false,
+    }, {
+        /* config: -smp 8,sockets=0
+         * expect: error, "anything=0" is not allowed */
+        .config = (SMPConfiguration) { T, 8, T, 0, F, 0, F, 0, F, 0, F, 0 },
+        .should_be_valid = false,
+    }, {
+        /* config: -smp 8,sockets=2,dies=0
+         * expect: error, "anything=0" is not allowed */
+        .config = (SMPConfiguration) { T, 0, T, 2, T, 0, F, 0, F, 0, F, 0 },
+        .should_be_valid = false,
+    }, {
+        /* config: -smp 8,sockets=2,dies=1,cores=0
+         * expect: error, "anything=0" is not allowed */
+        .config = (SMPConfiguration) { T, 8, T, 2, T, 1, T, 0, F, 0, F, 0 },
+        .should_be_valid = false,
+    }, {
+        /* config: -smp 8,sockets=2,dies=1,cores=4,threads=0
+         * expect: error, "anything=0" is not allowed */
+        .config = (SMPConfiguration) { T, 8, T, 2, T, 1, T, 4, T, 0, F, 0 },
+        .should_be_valid = false,
+    }, {
+        /* config: -smp 8,sockets=2,dies=1,cores=4,threads=2,maxcpus=0
+         * expect: error, "anything=0" is not allowed */
+        .config = (SMPConfiguration) { T, 8, T, 2, T, 1, T, 4, T, 2, T, 0 },
+        .should_be_valid = false,
+    }, {
+        /* config: -smp 8,dies=2
+         * expect: error, multi-dies not supported */
+        .config = (SMPConfiguration) { T, 8, F, 0, T, 2, F, 0, F, 0, F, 0 },
+        .should_be_valid = false,
+    }, {
+        /* config: -smp 8,sockets=2,cores=8
+         * expect: error, sum (16) != max_cpus (8) */
+        .config = (SMPConfiguration) { T, 8, T, 2, F, 0, T, 4, T, 2, F, 0 },
+        .should_be_valid = false,
+    }, {
+        /* config: -smp 8,sockets=2,cores=5,threads=2,maxcpus=16
+         * expect: error, sum (20) != max_cpus (16) */
+        .config = (SMPConfiguration) { F, 0, T, 3, F, 0, T, 5, T, 1, T, 16 },
+        .should_be_valid = false,
+    }, {
+        /* config: -smp 16,maxcpus=12
+         * expect: error, sum (12) < smp_cpus (16) */
+        .config = (SMPConfiguration) { T, 16, F, 0, F, 0, F, 0, F, 0, T, 12 },
+        .should_be_valid = false,
+    },
+};
+
+static struct SMPTestData prefer_cores_support_dies[] = {
+    {
+        /* config: -smp dies=2
+         * expect: cpus=2,sockets=1,dies=2,cores=1,threads=1,maxcpus=2 */
+        .config = (SMPConfiguration) { F, 0, F, 0, T, 2, F, 0, F, 0, F, 0 },
+        .should_be_valid = true,
+        .expect = (CpuTopology) { 2, 1, 2, 1, 1, 2 },
+    }, {
+        /* config: -smp 16,dies=2
+         * expect: cpus=16,sockets=1,dies=2,cores=8,threads=1,maxcpus=16 */
+        .config = (SMPConfiguration) { T, 16, F, 0, T, 2, F, 0, F, 0, F, 0 },
+        .should_be_valid = true,
+        .expect = (CpuTopology) { 16, 1, 2, 8, 1, 16 },
+    }, {
+        /* config: -smp sockets=2,dies=2
+         * expect: cpus=4,sockets=2,dies=2,cores=1,threads=1,maxcpus=4 */
+        .config = (SMPConfiguration) { F, 0, T, 2, T, 2, F, 0, F, 0, F, 0 },
+        .should_be_valid = true,
+        .expect = (CpuTopology) { 4, 2, 2, 1, 1, 4 },
+    }, {
+        /* config: -smp dies=2,cores=4
+         * expect: cpus=8,sockets=1,dies=2,cores=4,threads=1,maxcpus=8 */
+        .config = (SMPConfiguration) { F, 0, F, 0, T, 2, T, 4, F, 0, F, 0 },
+        .should_be_valid = true,
+        .expect = (CpuTopology) { 8, 1, 2, 4, 1, 8 },
+    }, {
+        /* config: -smp dies=2,threads=2
+         * expect: cpus=4,sockets=1,dies=2,cores=1,threads=2,maxcpus=4 */
+        .config = (SMPConfiguration) { F, 0, F, 0, T, 2, F, 0, T, 2, F, 0 },
+        .should_be_valid = true,
+        .expect = (CpuTopology) { 4, 1, 2, 1, 2, 4 },
+    }, {
+        /* config: -smp dies=2,maxcpus=32
+         * expect: cpus=32,sockets=1,dies=2,cores=16,threads=1,maxcpus=32 */
+        .config = (SMPConfiguration) { F, 0, F, 0, T, 2, F, 0, F, 0, T, 32 },
+        .should_be_valid = true,
+        .expect = (CpuTopology) { 32, 1, 2, 16, 1, 32 },
+    }, {
+        /* config: -smp 16,sockets=2,dies=2
+         * expect: cpus=16,sockets=2,dies=2,cores=4,threads=1,maxcpus=16 */
+        .config = (SMPConfiguration) { T, 16, T, 2, T, 2, F, 0, F, 0, F, 0 },
+        .should_be_valid = true,
+        .expect = (CpuTopology) { 16, 2, 2, 4, 1, 16 },
+    }, {
+        /* config: -smp 16,dies=2,cores=4
+         * expect: cpus=16,sockets=2,dies=2,cores=4,threads=1,maxcpus=16 */
+        .config = (SMPConfiguration) { T, 16, F, 0, T, 2, T, 4, F, 0, F, 0 },
+        .should_be_valid = true,
+        .expect = (CpuTopology) { 16, 2, 2, 4, 1, 16 },
+    }, {
+        /* config: -smp 16,dies=2,threads=2
+         * expect: cpus=16,sockets=1,dies=2,cores=4,threads=2,maxcpus=16 */
+        .config = (SMPConfiguration) { T, 16, F, 0, T, 2, F, 0, T, 2, F, 0 },
+        .should_be_valid = true,
+        .expect = (CpuTopology) { 16, 1, 2, 4, 2, 16 },
+    }, {
+        /* config: -smp 16,dies=2,maxcpus=32
+         * expect: cpus=16,sockets=1,dies=2,cores=16,threads=1,maxcpus=32 */
+        .config = (SMPConfiguration) { T, 16, F, 0, T, 2, F, 0, F, 0, T, 32 },
+        .should_be_valid = true,
+        .expect = (CpuTopology) { 16, 1, 2, 16, 1, 32 },
+    }, {
+        /* config: -smp sockets=2,dies=2,cores=4
+         * expect: cpus=16,sockets=2,dies=2,cores=4,threads=1,maxcpus=16 */
+        .config = (SMPConfiguration) { F, 0, T, 2, T, 2, T, 4, F, 0, F, 0 },
+        .should_be_valid = true,
+        .expect = (CpuTopology) { 16, 2, 2, 4, 1, 16 },
+    }, {
+        /* config: -smp sockets=2,dies=2,threads=2
+         * expect: cpus=8,sockets=2,dies=2,cores=1,threads=2,maxcpus=8 */
+        .config = (SMPConfiguration) { F, 0, T, 2, T, 2, F, 0, T, 2, F, 0 },
+        .should_be_valid = true,
+        .expect = (CpuTopology) { 8, 2, 2, 1, 2, 8 },
+    }, {
+        /* config: -smp sockets=2,dies=2,maxcpus=32
+         * expect: cpus=32,sockets=2,dies=2,cores=8,threads=1,maxcpus=32 */
+        .config = (SMPConfiguration) { F, 0, T, 2, T, 2, F, 0, F, 0, T, 32 },
+        .should_be_valid = true,
+        .expect = (CpuTopology) { 32, 2, 2, 8, 1, 32 },
+    }, {
+        /* config: -smp dies=2,cores=4,threads=2
+         * expect: cpus=16,sockets=1,dies=2,cores=4,threads=2,maxcpus=16 */
+        .config = (SMPConfiguration) { F, 0, F, 0, T, 2, T, 4, T, 2, F, 0 },
+        .should_be_valid = true,
+        .expect = (CpuTopology) { 16, 1, 2, 4, 2, 16 },
+    }, {
+        /* config: -smp dies=2,cores=4,maxcpus=32
+         * expect: cpus=32,sockets=4,dies=2,cores=4,threads=1,maxcpus=32 */
+        .config = (SMPConfiguration) { F, 0, F, 0, T, 2, T, 4, F, 0, T, 32 },
+        .should_be_valid = true,
+        .expect = (CpuTopology) { 32, 4, 2, 4, 1, 32 },
+    }, {
+        /* config: -smp dies=2,threads=2,maxcpus=32
+         * expect: cpus=32,sockets=1,dies=2,cores=8,threads=2,maxcpus=32 */
+        .config = (SMPConfiguration) { F, 0, F, 0, T, 2, F, 0, T, 2, T, 32 },
+        .should_be_valid = true,
+        .expect = (CpuTopology) { 32, 1, 2, 8, 2, 32 },
+    }, {
+        /* config: -smp 16,sockets=2,dies=2,cores=4
+         * expect: cpus=16,sockets=2,dies=2,cores=4,threads=1,maxcpus=16 */
+        .config = (SMPConfiguration) { T, 16, T, 2, T, 2, T, 4, F, 0, F, 0 },
+        .should_be_valid = true,
+        .expect = (CpuTopology) { 16, 2, 2, 4, 1, 16 },
+    }, {
+        /* config: -smp 16,sockets=2,dies=2,threads=2
+         * expect: cpus=16,sockets=2,dies=2,cores=2,threads=2,maxcpus=16 */
+        .config = (SMPConfiguration) { T, 16, T, 2, T, 2, F, 0, T, 2, F, 0 },
+        .should_be_valid = true,
+        .expect = (CpuTopology) { 16, 2, 2, 2, 2, 16 },
+    }, {
+        /* config: -smp 16,sockets=2,dies=2,maxcpus=32
+         * expect: cpus=16,sockets=2,dies=2,cores=8,threads=1,maxcpus=32 */
+        .config = (SMPConfiguration) { T, 16, T, 2, T, 2, F, 0, F, 0, T, 32 },
+        .should_be_valid = true,
+        .expect = (CpuTopology) { 16, 2, 2, 8, 1, 32 },
+    }, {
+        /* config: -smp 16,dies=2,cores=4,threads=2
+         * expect: cpus=16,sockets=1,dies=2,cores=4,threads=2,maxcpus=16 */
+        .config = (SMPConfiguration) { T, 16, F, 0, T, 2, T, 4, T, 2, F, 0 },
+        .should_be_valid = true,
+        .expect = (CpuTopology) { 16, 1, 2, 4, 2, 16 },
+    }, {
+        /* config: -smp 16,dies=2,cores=4,maxcpus=32
+         * expect: cpus=16,sockets=4,dies=2,cores=4,threads=1,maxcpus=32 */
+        .config = (SMPConfiguration) { T, 16, F, 0, T, 2, T, 4, F, 0, T, 32 },
+        .should_be_valid = true,
+        .expect = (CpuTopology) { 16, 4, 2, 4, 1, 32 },
+    }, {
+        /* config: -smp 16,dies=2,threads=2,maxcpus=32
+         * expect: cpus=16,sockets=1,dies=2,cores=8,threads=2,maxcpus=32 */
+        .config = (SMPConfiguration) { T, 16, F, 0, T, 2, F, 0, T, 2, T, 32 },
+        .should_be_valid = true,
+        .expect = (CpuTopology) { 16, 1, 2, 8, 2, 32 },
+    }, {
+        /* config: -smp sockets=2,dies=2,cores=4,threads=2
+         * expect: cpus=32,sockets=2,dies=2,cores=4,threads=2,maxcpus=32 */
+        .config = (SMPConfiguration) { F, 0, T, 2, T, 2, T, 4, T, 2, F, 0 },
+        .should_be_valid = true,
+        .expect = (CpuTopology) { 32, 2, 2, 4, 2, 32 },
+    }, {
+        /* config: -smp sockets=2,dies=2,cores=4,maxcpus=32
+         * expect: cpus=32,sockets=2,dies=2,cores=4,threads=2,maxcpus=32 */
+        .config = (SMPConfiguration) { F, 0, T, 2, T, 2, T, 4, F, 0, T, 32 },
+        .should_be_valid = true,
+        .expect = (CpuTopology) { 32, 2, 2, 4, 2, 32 },
+    }, {
+        /* config: -smp sockets=2,dies=2,threads=2,maxcpus=32
+         * expect: cpus=32,sockets=2,dies=2,cores=4,threads=2,maxcpus=32 */
+        .config = (SMPConfiguration) { F, 0, T, 2, T, 2, F, 0, T, 2, T, 32 },
+        .should_be_valid = true,
+        .expect = (CpuTopology) { 32, 2, 2, 4, 2, 32 },
+    }, {
+        /* config: -smp dies=2,cores=4,threads=2,maxcpus=32
+         * expect: cpus=32,sockets=2,dies=2,cores=4,threads=2,maxcpus=32 */
+        .config = (SMPConfiguration) { F, 0, F, 0, T, 2, T, 4, T, 2, T, 32 },
+        .should_be_valid = true,
+        .expect = (CpuTopology) { 32, 2, 2, 4, 2, 32 },
+    }, {
+        /* config: -smp 16,sockets=2,dies=2,cores=4,threads=1
+         * expect: cpus=16,sockets=2,dies=2,cores=4,threads=1,maxcpus=16 */
+        .config = (SMPConfiguration) { T, 16, T, 2, T, 2, T, 4, T, 1, F, 0 },
+        .should_be_valid = true,
+        .expect = (CpuTopology) { 16, 2, 2, 4, 1, 16 },
+    }, {
+        /* config: -smp 16,sockets=2,dies=2,cores=4,maxcpus=32
+         * expect: cpus=16,sockets=2,dies=2,cores=4,threads=2,maxcpus=32 */
+        .config = (SMPConfiguration) { T, 16, T, 2, T, 2, T, 4, F, 0, T, 32 },
+        .should_be_valid = true,
+        .expect = (CpuTopology) { 16, 2, 2, 4, 2, 32 },
+    }, {
+        /* config: -smp 16,sockets=2,dies=2,threads=2,maxcpus=32
+         * expect: cpus=16,sockets=2,dies=2,cores=4,threads=2,maxcpus=32 */
+        .config = (SMPConfiguration) { T, 16, T, 2, T, 2, F, 0, T, 2, T, 32 },
+        .should_be_valid = true,
+        .expect = (CpuTopology) { 16, 2, 2, 4, 2, 32 },
+    }, {
+        /* config: -smp 16,dies=2,cores=4,threads=2,maxcpus=32
+         * expect: cpus=16,sockets=2,dies=2,cores=4,threads=2,maxcpus=32 */
+        .config = (SMPConfiguration) { T, 16, F, 0, T, 2, T, 4, T, 2, T, 32 },
+        .should_be_valid = true,
+        .expect = (CpuTopology) { 16, 2, 2, 4, 2, 32 },
+    }, {
+        /* config: -smp sockets=2,dies=2,cores=4,threads=2,maxcpus=32
+         * expect: -smp 32,sockets=2,dies=2,cores=4,threads=2,maxcpus=32 */
+        .config = (SMPConfiguration) { F, 0, T, 2, T, 2, T, 4, T, 2, T, 32 },
+        .should_be_valid = true,
+        .expect = (CpuTopology) { 32, 2, 2, 4, 2, 32 },
+    }, {
+        /* config: -smp 16,sockets=2,dies=2,cores=4,threads=2,maxcpus=32
+         * expect: cpus=16,sockets=2,dies=2,cores=4,threads=2,maxcpus=32 */
+        .config = (SMPConfiguration) { T, 16, T, 2, T, 2, T, 4, T, 2, T, 32 },
+        .should_be_valid = true,
+        .expect = (CpuTopology) { 16, 2, 2, 4, 2, 32 },
+    },
+};
+
+static char *get_config_info(SMPConfiguration *config)
+{
+    return g_strdup_printf(
+        "(SMPConfiguration) {\n"
+        "    .has_cpus    = %5s, cpus    = %ld,\n"
+        "    .has_sockets = %5s, sockets = %ld,\n"
+        "    .has_dies    = %5s, dies    = %ld,\n"
+        "    .has_cores   = %5s, cores   = %ld,\n"
+        "    .has_threads = %5s, threads = %ld,\n"
+        "    .has_maxcpus = %5s, maxcpus = %ld,\n"
+        "}",
+        config->has_cpus ? "true" : "false", config->cpus,
+        config->has_sockets ? "true" : "false", config->sockets,
+        config->has_dies ? "true" : "false", config->dies,
+        config->has_cores ? "true" : "false", config->cores,
+        config->has_threads ? "true" : "false", config->threads,
+        config->has_maxcpus ? "true" : "false", config->maxcpus);
+}
+
+static char *get_topo_info(CpuTopology *topo)
+{
+    return g_strdup_printf(
+        "(CpuTopology) {\n"
+        "    .cpus     = %u,\n"
+        "    .sockets  = %u,\n"
+        "    .dies     = %u,\n"
+        "    .cores    = %u,\n"
+        "    .threads  = %u,\n"
+        "    .max_cpus = %u,\n"
+        "}",
+        topo->cpus, topo->sockets, topo->dies,
+        topo->cores, topo->threads, topo->max_cpus);
+}
+
+static void check_smp_parse(MachineState *ms, SMPTestData *data)
+{
+    SMPConfiguration *config = &data->config;
+    CpuTopology *expect = &data->expect;
+    g_autofree char *config_info = NULL;
+    g_autofree char *expect_info = NULL;
+    g_autofree char *result_info = NULL;
+    Error *err = NULL;
+
+    /* call the generic parser smp_parse() in hw/core/machine-smp.c */
+    smp_parse(ms, config, &err);
+
+    if (data->should_be_valid) {
+        if ((err == NULL) &&
+            (ms->smp.cpus == expect->cpus) &&
+            (ms->smp.sockets == expect->sockets) &&
+            (ms->smp.dies == expect->dies) &&
+            (ms->smp.cores == expect->cores) &&
+            (ms->smp.threads == expect->threads) &&
+            (ms->smp.max_cpus == expect->max_cpus)) {
+            return;
+        }
+
+        config_info = get_config_info(config);
+        expect_info = get_topo_info(expect);
+
+        if (err != NULL) {
+            g_printerr("Check smp_parse failed:\n"
+                       "config: %s\n"
+                       "expect: %s\n"
+                       "should_be_valid: yes\n\n"
+                       "result_is_valid: no\n"
+                       "error_msg: %s\n",
+                       config_info, expect_info, error_get_pretty(err));
+            error_free(err);
+        } else {
+            result_info = get_topo_info(&ms->smp);
+            g_printerr("Check smp_parse failed:\n"
+                       "config: %s\n"
+                       "expect: %s\n"
+                       "should_be_valid: yes\n\n"
+                       "result_is_valid: yes\n"
+                       "result: %s\n",
+                       config_info, expect_info, result_info);
+        }
+    } else {
+        if (err != NULL) {
+            error_free(err);
+            return;
+        }
+
+        config_info = get_config_info(config);
+        result_info = get_topo_info(&ms->smp);
+
+        g_printerr("Check smp_parse failed:\n"
+                   "config: %s\n"
+                   "should_be_valid: no\n\n"
+                   "result_is_valid: yes\n"
+                   "result: %s\n",
+                   config_info, result_info);
+    }
+
+    abort();
+}
+
+static void smp_prefer_sockets_test(void)
+{
+    Object *obj = object_new(TYPE_MACHINE);
+    MachineState *ms = MACHINE(obj);
+    MachineClass *mc = MACHINE_GET_CLASS(obj);
+    int i;
+
+    /* make sure that we have created the object */
+    g_assert_nonnull(ms);
+    g_assert_nonnull(mc);
+
+    mc->smp_prefer_sockets = true;
+
+    /* test cases when multi-dies are not supported */
+    mc->smp_dies_supported = false;
+    for (i = 0; i < ARRAY_SIZE(prefer_sockets); i++) {
+        check_smp_parse(ms, &prefer_sockets[i]);
+    }
+
+    /* test cases when multi-dies are supported */
+    mc->smp_dies_supported = true;
+    for (i = 0; i < ARRAY_SIZE(prefer_sockets_support_dies); i++) {
+        check_smp_parse(ms, &prefer_sockets_support_dies[i]);
+    }
+
+    object_unref(obj);
+}
+
+static void smp_prefer_cores_test(void)
+{
+    Object *obj = object_new(TYPE_MACHINE);
+    MachineState *ms = MACHINE(obj);
+    MachineClass *mc = MACHINE_GET_CLASS(obj);
+    int i;
+
+    /* make sure that we have created the object */
+    g_assert_nonnull(ms);
+    g_assert_nonnull(mc);
+
+    mc->smp_prefer_sockets = false;
+
+    /* test cases when multi-dies are not supported */
+    mc->smp_dies_supported = false;
+    for (i = 0; i < ARRAY_SIZE(prefer_cores); i++) {
+        check_smp_parse(ms, &prefer_cores[i]);
+    }
+
+    /* test cases when multi-dies are supported */
+    mc->smp_dies_supported = true;
+    for (i = 0; i < ARRAY_SIZE(prefer_cores_support_dies); i++) {
+        check_smp_parse(ms, &prefer_cores_support_dies[i]);
+    }
+
+    object_unref(obj);
+}
+
+int main(int argc, char *argv[])
+{
+    g_test_init(&argc, &argv, NULL);
+
+    module_call_init(MODULE_INIT_QOM);
+    type_register_static(&smp_machine_info);
+
+    g_test_add_func("/test-smp-parse/prefer_sockets", smp_prefer_sockets_test);
+    g_test_add_func("/test-smp-parse/prefer_cores", smp_prefer_cores_test);
+
+    g_test_run();
+
+    return 0;
+}
-- 
2.19.1



^ permalink raw reply related	[flat|nested] 58+ messages in thread

* Re: [PATCH for-6.2 v2 06/11] hw: Add compat machines for 6.2
  2021-07-19  3:20 ` [PATCH for-6.2 v2 06/11] hw: Add compat machines for 6.2 Yanan Wang
@ 2021-07-19  3:38   ` David Gibson
  2021-07-19 17:00   ` Andrew Jones
                     ` (2 subsequent siblings)
  3 siblings, 0 replies; 58+ messages in thread
From: David Gibson @ 2021-07-19  3:38 UTC (permalink / raw)
  To: Yanan Wang
  Cc: Peter Maydell, Andrew Jones, Daniel P . Berrangé,
	Eduardo Habkost, Michael S . Tsirkin, wanghaibin.wang,
	Richard Henderson, qemu-devel, Greg Kurz, Halil Pasic, yuzenghui,
	Igor Mammedov, Thomas Huth, Paolo Bonzini

[-- Attachment #1: Type: text/plain, Size: 8056 bytes --]

On Mon, Jul 19, 2021 at 11:20:38AM +0800, Yanan Wang wrote:
> Add 6.2 machine types for arm/i440fx/q35/s390x/spapr.
> 
> Signed-off-by: Yanan Wang <wangyanan55@huawei.com>

Acked-by: David Gibson <david@gibson.dropbear.id.au>

> ---
>  hw/arm/virt.c              |  9 ++++++++-
>  hw/core/machine.c          |  3 +++
>  hw/i386/pc.c               |  3 +++
>  hw/i386/pc_piix.c          | 14 +++++++++++++-
>  hw/i386/pc_q35.c           | 13 ++++++++++++-
>  hw/ppc/spapr.c             | 15 +++++++++++++--
>  hw/s390x/s390-virtio-ccw.c | 14 +++++++++++++-
>  include/hw/boards.h        |  3 +++
>  include/hw/i386/pc.h       |  3 +++
>  9 files changed, 71 insertions(+), 6 deletions(-)
> 
> diff --git a/hw/arm/virt.c b/hw/arm/virt.c
> index 81eda46b0b..01165f7f53 100644
> --- a/hw/arm/virt.c
> +++ b/hw/arm/virt.c
> @@ -2788,10 +2788,17 @@ static void machvirt_machine_init(void)
>  }
>  type_init(machvirt_machine_init);
>  
> +static void virt_machine_6_2_options(MachineClass *mc)
> +{
> +}
> +DEFINE_VIRT_MACHINE_AS_LATEST(6, 2)
> +
>  static void virt_machine_6_1_options(MachineClass *mc)
>  {
> +    virt_machine_6_2_options(mc);
> +    compat_props_add(mc->compat_props, hw_compat_6_1, hw_compat_6_1_len);
>  }
> -DEFINE_VIRT_MACHINE_AS_LATEST(6, 1)
> +DEFINE_VIRT_MACHINE(6, 1)
>  
>  static void virt_machine_6_0_options(MachineClass *mc)
>  {
> diff --git a/hw/core/machine.c b/hw/core/machine.c
> index 8b4d07d3fc..63439c4a6d 100644
> --- a/hw/core/machine.c
> +++ b/hw/core/machine.c
> @@ -37,6 +37,9 @@
>  #include "hw/virtio/virtio.h"
>  #include "hw/virtio/virtio-pci.h"
>  
> +GlobalProperty hw_compat_6_1[] = {};
> +const size_t hw_compat_6_1_len = G_N_ELEMENTS(hw_compat_6_1);
> +
>  GlobalProperty hw_compat_6_0[] = {
>      { "gpex-pcihost", "allow-unmapped-accesses", "false" },
>      { "i8042", "extended-state", "false"},
> diff --git a/hw/i386/pc.c b/hw/i386/pc.c
> index d94ef582b5..c6768c6ce7 100644
> --- a/hw/i386/pc.c
> +++ b/hw/i386/pc.c
> @@ -94,6 +94,9 @@
>  #include "trace.h"
>  #include CONFIG_DEVICES
>  
> +GlobalProperty pc_compat_6_1[] = {};
> +const size_t pc_compat_6_1_len = G_N_ELEMENTS(pc_compat_6_1);
> +
>  GlobalProperty pc_compat_6_0[] = {
>      { "qemu64" "-" TYPE_X86_CPU, "family", "6" },
>      { "qemu64" "-" TYPE_X86_CPU, "model", "6" },
> diff --git a/hw/i386/pc_piix.c b/hw/i386/pc_piix.c
> index 30b8bd6ea9..fd5c2277f2 100644
> --- a/hw/i386/pc_piix.c
> +++ b/hw/i386/pc_piix.c
> @@ -413,7 +413,7 @@ static void pc_i440fx_machine_options(MachineClass *m)
>      machine_class_allow_dynamic_sysbus_dev(m, TYPE_VMBUS_BRIDGE);
>  }
>  
> -static void pc_i440fx_6_1_machine_options(MachineClass *m)
> +static void pc_i440fx_6_2_machine_options(MachineClass *m)
>  {
>      PCMachineClass *pcmc = PC_MACHINE_CLASS(m);
>      pc_i440fx_machine_options(m);
> @@ -422,6 +422,18 @@ static void pc_i440fx_6_1_machine_options(MachineClass *m)
>      pcmc->default_cpu_version = 1;
>  }
>  
> +DEFINE_I440FX_MACHINE(v6_2, "pc-i440fx-6.2", NULL,
> +                      pc_i440fx_6_2_machine_options);
> +
> +static void pc_i440fx_6_1_machine_options(MachineClass *m)
> +{
> +    pc_i440fx_6_2_machine_options(m);
> +    m->alias = NULL;
> +    m->is_default = false;
> +    compat_props_add(m->compat_props, hw_compat_6_1, hw_compat_6_1_len);
> +    compat_props_add(m->compat_props, pc_compat_6_1, pc_compat_6_1_len);
> +}
> +
>  DEFINE_I440FX_MACHINE(v6_1, "pc-i440fx-6.1", NULL,
>                        pc_i440fx_6_1_machine_options);
>  
> diff --git a/hw/i386/pc_q35.c b/hw/i386/pc_q35.c
> index 04b4a4788d..b45903b15e 100644
> --- a/hw/i386/pc_q35.c
> +++ b/hw/i386/pc_q35.c
> @@ -355,7 +355,7 @@ static void pc_q35_machine_options(MachineClass *m)
>      m->max_cpus = 288;
>  }
>  
> -static void pc_q35_6_1_machine_options(MachineClass *m)
> +static void pc_q35_6_2_machine_options(MachineClass *m)
>  {
>      PCMachineClass *pcmc = PC_MACHINE_CLASS(m);
>      pc_q35_machine_options(m);
> @@ -363,6 +363,17 @@ static void pc_q35_6_1_machine_options(MachineClass *m)
>      pcmc->default_cpu_version = 1;
>  }
>  
> +DEFINE_Q35_MACHINE(v6_2, "pc-q35-6.2", NULL,
> +                   pc_q35_6_2_machine_options);
> +
> +static void pc_q35_6_1_machine_options(MachineClass *m)
> +{
> +    pc_q35_6_2_machine_options(m);
> +    m->alias = NULL;
> +    compat_props_add(m->compat_props, hw_compat_6_1, hw_compat_6_1_len);
> +    compat_props_add(m->compat_props, pc_compat_6_1, pc_compat_6_1_len);
> +}
> +
>  DEFINE_Q35_MACHINE(v6_1, "pc-q35-6.1", NULL,
>                     pc_q35_6_1_machine_options);
>  
> diff --git a/hw/ppc/spapr.c b/hw/ppc/spapr.c
> index 81699d4f8b..d39fd4e644 100644
> --- a/hw/ppc/spapr.c
> +++ b/hw/ppc/spapr.c
> @@ -4685,15 +4685,26 @@ static void spapr_machine_latest_class_options(MachineClass *mc)
>      }                                                                \
>      type_init(spapr_machine_register_##suffix)
>  
> +/*
> + * pseries-6.2
> + */
> +static void spapr_machine_6_2_class_options(MachineClass *mc)
> +{
> +    /* Defaults for the latest behaviour inherited from the base class */
> +}
> +
> +DEFINE_SPAPR_MACHINE(6_2, "6.2", true);
> +
>  /*
>   * pseries-6.1
>   */
>  static void spapr_machine_6_1_class_options(MachineClass *mc)
>  {
> -    /* Defaults for the latest behaviour inherited from the base class */
> +    spapr_machine_6_2_class_options(mc);
> +    compat_props_add(mc->compat_props, hw_compat_6_1, hw_compat_6_1_len);
>  }
>  
> -DEFINE_SPAPR_MACHINE(6_1, "6.1", true);
> +DEFINE_SPAPR_MACHINE(6_1, "6.1", false);
>  
>  /*
>   * pseries-6.0
> diff --git a/hw/s390x/s390-virtio-ccw.c b/hw/s390x/s390-virtio-ccw.c
> index e4b18aef49..4d25278cf2 100644
> --- a/hw/s390x/s390-virtio-ccw.c
> +++ b/hw/s390x/s390-virtio-ccw.c
> @@ -791,14 +791,26 @@ bool css_migration_enabled(void)
>      }                                                                         \
>      type_init(ccw_machine_register_##suffix)
>  
> +static void ccw_machine_6_2_instance_options(MachineState *machine)
> +{
> +}
> +
> +static void ccw_machine_6_2_class_options(MachineClass *mc)
> +{
> +}
> +DEFINE_CCW_MACHINE(6_2, "6.2", true);
> +
>  static void ccw_machine_6_1_instance_options(MachineState *machine)
>  {
> +    ccw_machine_6_2_instance_options(machine);
>  }
>  
>  static void ccw_machine_6_1_class_options(MachineClass *mc)
>  {
> +    ccw_machine_6_2_class_options(mc);
> +    compat_props_add(mc->compat_props, hw_compat_6_1, hw_compat_6_1_len);
>  }
> -DEFINE_CCW_MACHINE(6_1, "6.1", true);
> +DEFINE_CCW_MACHINE(6_1, "6.1", false);
>  
>  static void ccw_machine_6_0_instance_options(MachineState *machine)
>  {
> diff --git a/include/hw/boards.h b/include/hw/boards.h
> index b6161cee88..2832f0f8aa 100644
> --- a/include/hw/boards.h
> +++ b/include/hw/boards.h
> @@ -354,6 +354,9 @@ struct MachineState {
>      } \
>      type_init(machine_initfn##_register_types)
>  
> +extern GlobalProperty hw_compat_6_1[];
> +extern const size_t hw_compat_6_1_len;
> +
>  extern GlobalProperty hw_compat_6_0[];
>  extern const size_t hw_compat_6_0_len;
>  
> diff --git a/include/hw/i386/pc.h b/include/hw/i386/pc.h
> index 88dffe7517..97b4ab79b5 100644
> --- a/include/hw/i386/pc.h
> +++ b/include/hw/i386/pc.h
> @@ -196,6 +196,9 @@ void pc_system_parse_ovmf_flash(uint8_t *flash_ptr, size_t flash_size);
>  void pc_madt_cpu_entry(AcpiDeviceIf *adev, int uid,
>                         const CPUArchIdList *apic_ids, GArray *entry);
>  
> +extern GlobalProperty pc_compat_6_1[];
> +extern const size_t pc_compat_6_1_len;
> +
>  extern GlobalProperty pc_compat_6_0[];
>  extern const size_t pc_compat_6_0_len;
>  

-- 
David Gibson			| I'll have my music baroque, and my code
david AT gibson.dropbear.id.au	| minimalist, thank you.  NOT _the_ _other_
				| _way_ _around_!
http://www.ozlabs.org/~dgibson

[-- Attachment #2: signature.asc --]
[-- Type: application/pgp-signature, Size: 833 bytes --]

^ permalink raw reply	[flat|nested] 58+ messages in thread

* Re: [PATCH for-6.2 v2 07/11] machine: Prefer cores over sockets in smp parsing since 6.2
  2021-07-19  3:20 ` [PATCH for-6.2 v2 07/11] machine: Prefer cores over sockets in smp parsing since 6.2 Yanan Wang
@ 2021-07-19  3:40   ` David Gibson
  2021-07-22  5:22     ` wangyanan (Y)
  2021-07-19 17:13   ` Andrew Jones
  1 sibling, 1 reply; 58+ messages in thread
From: David Gibson @ 2021-07-19  3:40 UTC (permalink / raw)
  To: Yanan Wang
  Cc: Peter Maydell, Andrew Jones, Daniel P . Berrangé,
	Eduardo Habkost, Michael S . Tsirkin, wanghaibin.wang,
	Richard Henderson, qemu-devel, Greg Kurz, Halil Pasic, yuzenghui,
	Igor Mammedov, Thomas Huth, Paolo Bonzini

[-- Attachment #1: Type: text/plain, Size: 8821 bytes --]

On Mon, Jul 19, 2021 at 11:20:39AM +0800, Yanan Wang wrote:
> In the real SMP hardware topology world, it's much more likely that
> we have high cores-per-socket counts and few sockets totally. While
> the current preference of sockets over cores in smp parsing results
> in a virtual cpu topology with low cores-per-sockets counts and a
> large number of sockets, which is just contrary to the real world.
> 
> Given that it is better to make the virtual cpu topology be more
> reflective of the real world and also for the sake of compatibility,
> we start to prefer cores over sockets over threads in smp parsing
> since machine type 6.2 for different arches.
> 
> In this patch, a boolean "smp_prefer_sockets" is added, and we only
> enable the old preference on older machines and enable the new one
> since type 6.2 for all arches by using the machine compat mechanism.
> 
> Suggested-by: Daniel P. Berrange <berrange@redhat.com>
> Signed-off-by: Yanan Wang <wangyanan55@huawei.com>

ppc parts

Acked-by: David Gibson <david@gibson.dropbear.id.au>

Note that for the pseries machine types, being paravirtual, there is
essentially no guest visible difference between "cores" and "sockets.

> ---
>  hw/arm/virt.c              |  1 +
>  hw/core/machine.c          | 59 +++++++++++++++++++++++++++++---------
>  hw/i386/pc_piix.c          |  1 +
>  hw/i386/pc_q35.c           |  1 +
>  hw/ppc/spapr.c             |  1 +
>  hw/s390x/s390-virtio-ccw.c |  1 +
>  include/hw/boards.h        |  1 +
>  qemu-options.hx            |  4 ++-
>  8 files changed, 55 insertions(+), 14 deletions(-)
> 
> diff --git a/hw/arm/virt.c b/hw/arm/virt.c
> index 01165f7f53..7babea40dc 100644
> --- a/hw/arm/virt.c
> +++ b/hw/arm/virt.c
> @@ -2797,6 +2797,7 @@ static void virt_machine_6_1_options(MachineClass *mc)
>  {
>      virt_machine_6_2_options(mc);
>      compat_props_add(mc->compat_props, hw_compat_6_1, hw_compat_6_1_len);
> +    mc->smp_prefer_sockets = true;
>  }
>  DEFINE_VIRT_MACHINE(6, 1)
>  
> diff --git a/hw/core/machine.c b/hw/core/machine.c
> index 63439c4a6d..c074425015 100644
> --- a/hw/core/machine.c
> +++ b/hw/core/machine.c
> @@ -744,6 +744,22 @@ void machine_set_cpu_numa_node(MachineState *machine,
>      }
>  }
>  
> +/*
> + * smp_parse - Generic function used to parse the given SMP configuration
> + *
> + * The topology parameters must be specified equal to or great than one
> + * or just omitted, explicit configuration like "cpus=0" is not allowed.
> + * The omitted parameters will be calculated based on the provided ones.
> + *
> + * maxcpus will default to the value of cpus if omitted and will be used
> + * to compute the missing sockets/cores/threads. cpus will be calculated
> + * from the computed parametrs if omitted.
> + *
> + * In calculation of omitted arch-netural sockets/cores/threads, we prefer
> + * sockets over cores over threads before 6.2, while prefer cores over
> + * sockets over threads since 6.2 on. The arch-specific dies will directly
> + * default to 1 if omitted.
> + */
>  static void smp_parse(MachineState *ms, SMPConfiguration *config, Error **errp)
>  {
>      MachineClass *mc = MACHINE_GET_CLASS(ms);
> @@ -772,19 +788,36 @@ static void smp_parse(MachineState *ms, SMPConfiguration *config, Error **errp)
>  
>      maxcpus = maxcpus > 0 ? maxcpus : cpus;
>  
> -    /* compute missing values, prefer sockets over cores over threads */
> -    if (sockets == 0) {
> -        cores = cores > 0 ? cores : 1;
> -        threads = threads > 0 ? threads : 1;
> -        sockets = maxcpus / (dies * cores * threads);
> -        sockets = sockets > 0 ? sockets : 1;
> -    } else if (cores == 0) {
> -        threads = threads > 0 ? threads : 1;
> -        cores = maxcpus / (sockets * dies * threads);
> -        cores = cores > 0 ? cores : 1;
> -    } else if (threads == 0) {
> -        threads = maxcpus / (sockets * dies * cores);
> -        threads = threads > 0 ? threads : 1;
> +    /* prefer sockets over cores over threads before 6.2 */
> +    if (mc->smp_prefer_sockets) {
> +        if (sockets == 0) {
> +            cores = cores > 0 ? cores : 1;
> +            threads = threads > 0 ? threads : 1;
> +            sockets = maxcpus / (dies * cores * threads);
> +            sockets = sockets > 0 ? sockets : 1;
> +        } else if (cores == 0) {
> +            threads = threads > 0 ? threads : 1;
> +            cores = maxcpus / (sockets * dies * threads);
> +            cores = cores > 0 ? cores : 1;
> +        } else if (threads == 0) {
> +            threads = maxcpus / (sockets * dies * cores);
> +            threads = threads > 0 ? threads : 1;
> +        }
> +    /* prefer cores over sockets over threads since 6.2 */
> +    } else {
> +        if (cores == 0) {
> +            sockets = sockets > 0 ? sockets : 1;
> +            threads = threads > 0 ? threads : 1;
> +            cores = maxcpus / (sockets * dies * threads);
> +            cores = cores > 0 ? cores : 1;
> +        } else if (sockets == 0) {
> +            threads = threads > 0 ? threads : 1;
> +            sockets = maxcpus / (dies * cores * threads);
> +            sockets = sockets > 0 ? sockets : 1;
> +        } else if (threads == 0) {
> +            threads = maxcpus / (sockets * dies * cores);
> +            threads = threads > 0 ? threads : 1;
> +        }
>      }
>  
>      /* use the computed parameters to calculate the omitted cpus */
> diff --git a/hw/i386/pc_piix.c b/hw/i386/pc_piix.c
> index fd5c2277f2..9b811fc6ca 100644
> --- a/hw/i386/pc_piix.c
> +++ b/hw/i386/pc_piix.c
> @@ -432,6 +432,7 @@ static void pc_i440fx_6_1_machine_options(MachineClass *m)
>      m->is_default = false;
>      compat_props_add(m->compat_props, hw_compat_6_1, hw_compat_6_1_len);
>      compat_props_add(m->compat_props, pc_compat_6_1, pc_compat_6_1_len);
> +    m->smp_prefer_sockets = true;
>  }
>  
>  DEFINE_I440FX_MACHINE(v6_1, "pc-i440fx-6.1", NULL,
> diff --git a/hw/i386/pc_q35.c b/hw/i386/pc_q35.c
> index b45903b15e..88efb7fde4 100644
> --- a/hw/i386/pc_q35.c
> +++ b/hw/i386/pc_q35.c
> @@ -372,6 +372,7 @@ static void pc_q35_6_1_machine_options(MachineClass *m)
>      m->alias = NULL;
>      compat_props_add(m->compat_props, hw_compat_6_1, hw_compat_6_1_len);
>      compat_props_add(m->compat_props, pc_compat_6_1, pc_compat_6_1_len);
> +    m->smp_prefer_sockets = true;
>  }
>  
>  DEFINE_Q35_MACHINE(v6_1, "pc-q35-6.1", NULL,
> diff --git a/hw/ppc/spapr.c b/hw/ppc/spapr.c
> index d39fd4e644..a481fade51 100644
> --- a/hw/ppc/spapr.c
> +++ b/hw/ppc/spapr.c
> @@ -4702,6 +4702,7 @@ static void spapr_machine_6_1_class_options(MachineClass *mc)
>  {
>      spapr_machine_6_2_class_options(mc);
>      compat_props_add(mc->compat_props, hw_compat_6_1, hw_compat_6_1_len);
> +    mc->smp_prefer_sockets = true;
>  }
>  
>  DEFINE_SPAPR_MACHINE(6_1, "6.1", false);
> diff --git a/hw/s390x/s390-virtio-ccw.c b/hw/s390x/s390-virtio-ccw.c
> index 4d25278cf2..b40e647883 100644
> --- a/hw/s390x/s390-virtio-ccw.c
> +++ b/hw/s390x/s390-virtio-ccw.c
> @@ -809,6 +809,7 @@ static void ccw_machine_6_1_class_options(MachineClass *mc)
>  {
>      ccw_machine_6_2_class_options(mc);
>      compat_props_add(mc->compat_props, hw_compat_6_1, hw_compat_6_1_len);
> +    mc->smp_prefer_sockets = true;
>  }
>  DEFINE_CCW_MACHINE(6_1, "6.1", false);
>  
> diff --git a/include/hw/boards.h b/include/hw/boards.h
> index 2832f0f8aa..8df885c9d2 100644
> --- a/include/hw/boards.h
> +++ b/include/hw/boards.h
> @@ -247,6 +247,7 @@ struct MachineClass {
>      bool nvdimm_supported;
>      bool numa_mem_supported;
>      bool smp_dies_supported;
> +    bool smp_prefer_sockets;
>      bool auto_enable_numa;
>      const char *default_ram_id;
>  
> diff --git a/qemu-options.hx b/qemu-options.hx
> index 0c9ddc0274..6ef57e838c 100644
> --- a/qemu-options.hx
> +++ b/qemu-options.hx
> @@ -227,7 +227,9 @@ SRST
>      from those which are given. Historically preference was given to the
>      coarsest topology parameters when computing missing values (ie sockets
>      preferred over cores, which were preferred over threads), however, this
> -    behaviour is considered liable to change.
> +    behaviour is considered liable to change. The historical preference of
> +    sockets over cores over threads works before 6.2, and a new preference
> +    of cores over sockets over threads starts to work since 6.2 on.
>  ERST
>  
>  DEF("numa", HAS_ARG, QEMU_OPTION_numa,

-- 
David Gibson			| I'll have my music baroque, and my code
david AT gibson.dropbear.id.au	| minimalist, thank you.  NOT _the_ _other_
				| _way_ _around_!
http://www.ozlabs.org/~dgibson

[-- Attachment #2: signature.asc --]
[-- Type: application/pgp-signature, Size: 833 bytes --]

^ permalink raw reply	[flat|nested] 58+ messages in thread

* Re: [PATCH for-6.2 v2 01/11] machine: Disallow specifying topology parameters as zero
  2021-07-19  3:20 ` [PATCH for-6.2 v2 01/11] machine: Disallow specifying topology parameters as zero Yanan Wang
@ 2021-07-19 16:11   ` Andrew Jones
  2021-07-21 12:34     ` wangyanan (Y)
  2021-07-19 16:46   ` Daniel P. Berrangé
  1 sibling, 1 reply; 58+ messages in thread
From: Andrew Jones @ 2021-07-19 16:11 UTC (permalink / raw)
  To: Yanan Wang
  Cc: Peter Maydell, Thomas Huth, Daniel P . Berrangé,
	Eduardo Habkost, Michael S . Tsirkin, wanghaibin.wang,
	Richard Henderson, qemu-devel, Greg Kurz, Halil Pasic,
	Igor Mammedov, yuzenghui, Paolo Bonzini, David Gibson

On Mon, Jul 19, 2021 at 11:20:33AM +0800, Yanan Wang wrote:
> In the SMP configuration, we should either specify a topology
> parameter with a reasonable value (equal to or greater than 1)
> or just leave it omitted and QEMU will calculate its value.
> 
> Configurations which explicitly specify the topology parameters
> as zero like "sockets=0" are meaningless, so disallow them.
> 
> Suggested-by: Andrew Jones <drjones@redhat.com>
> Signed-off-by: Yanan Wang <wangyanan55@huawei.com>
> ---
>  hw/core/machine.c | 31 +++++++++++++++++++++++--------
>  hw/i386/pc.c      | 29 +++++++++++++++++++++--------
>  qapi/machine.json |  4 ++--
>  3 files changed, 46 insertions(+), 18 deletions(-)
> 
> diff --git a/hw/core/machine.c b/hw/core/machine.c
> index 775add0795..d73daa10f4 100644
> --- a/hw/core/machine.c
> +++ b/hw/core/machine.c
> @@ -745,11 +745,25 @@ static void smp_parse(MachineState *ms, SMPConfiguration *config, Error **errp)
>  {
>      unsigned cpus    = config->has_cpus ? config->cpus : 0;
>      unsigned sockets = config->has_sockets ? config->sockets : 0;
> +    unsigned dies    = config->has_dies ? config->dies : 1;
>      unsigned cores   = config->has_cores ? config->cores : 0;
>      unsigned threads = config->has_threads ? config->threads : 0;
> +    unsigned maxcpus = config->has_maxcpus ? config->maxcpus : 0;
> +
> +    if ((config->has_cpus && config->cpus == 0) ||
> +        (config->has_sockets && config->sockets == 0) ||
> +        (config->has_dies && config->dies == 0) ||
> +        (config->has_cores && config->cores == 0) ||
> +        (config->has_threads && config->threads == 0) ||
> +        (config->has_maxcpus && config->maxcpus == 0)) {
> +        error_setg(errp, "parameters must be equal to or greater than one"
> +                   "if provided");

Missing a space between 'one' and 'if'. It's better to just put the whole
string on one line too (ignore the 80 char thing) for error grepping.

> +        return;
> +    }
>  
> -    if (config->has_dies && config->dies != 0 && config->dies != 1) {
> +    if (dies > 1) {
>          error_setg(errp, "dies not supported by this machine's CPU topology");
> +        return;
>      }
>  
>      /* compute missing values, prefer sockets over cores over threads */
> @@ -760,8 +774,8 @@ static void smp_parse(MachineState *ms, SMPConfiguration *config, Error **errp)
>              sockets = sockets > 0 ? sockets : 1;
>              cpus = cores * threads * sockets;
>          } else {
> -            ms->smp.max_cpus = config->has_maxcpus ? config->maxcpus : cpus;
> -            sockets = ms->smp.max_cpus / (cores * threads);
> +            maxcpus = maxcpus > 0 ? maxcpus : cpus;
> +            sockets = maxcpus / (cores * threads);
>          }
>      } else if (cores == 0) {
>          threads = threads > 0 ? threads : 1;
> @@ -778,26 +792,27 @@ static void smp_parse(MachineState *ms, SMPConfiguration *config, Error **errp)
>          return;
>      }
>  
> -    ms->smp.max_cpus = config->has_maxcpus ? config->maxcpus : cpus;
> +    maxcpus = maxcpus > 0 ? maxcpus : cpus;
>  
> -    if (ms->smp.max_cpus < cpus) {
> +    if (maxcpus < cpus) {
>          error_setg(errp, "maxcpus must be equal to or greater than smp");
>          return;
>      }
>  
> -    if (sockets * cores * threads != ms->smp.max_cpus) {
> +    if (sockets * cores * threads != maxcpus) {
>          error_setg(errp, "Invalid CPU topology: "
>                     "sockets (%u) * cores (%u) * threads (%u) "
>                     "!= maxcpus (%u)",
>                     sockets, cores, threads,
> -                   ms->smp.max_cpus);
> +                   maxcpus);
>          return;
>      }
>  
>      ms->smp.cpus = cpus;
> +    ms->smp.sockets = sockets;
>      ms->smp.cores = cores;
>      ms->smp.threads = threads;
> -    ms->smp.sockets = sockets;
> +    ms->smp.max_cpus = maxcpus;
>  }
>  
>  static void machine_get_smp(Object *obj, Visitor *v, const char *name,
> diff --git a/hw/i386/pc.c b/hw/i386/pc.c
> index c2b9d62a35..c6b63c00a5 100644
> --- a/hw/i386/pc.c
> +++ b/hw/i386/pc.c
> @@ -719,6 +719,18 @@ static void pc_smp_parse(MachineState *ms, SMPConfiguration *config, Error **err
>      unsigned dies    = config->has_dies ? config->dies : 1;
>      unsigned cores   = config->has_cores ? config->cores : 0;
>      unsigned threads = config->has_threads ? config->threads : 0;
> +    unsigned maxcpus = config->has_maxcpus ? config->maxcpus : 0;
> +
> +    if ((config->has_cpus && config->cpus == 0) ||
> +        (config->has_sockets && config->sockets == 0) ||
> +        (config->has_dies && config->dies == 0) ||
> +        (config->has_cores && config->cores == 0) ||
> +        (config->has_threads && config->threads == 0) ||
> +        (config->has_maxcpus && config->maxcpus == 0)) {
> +        error_setg(errp, "parameters must be equal to or greater than one"
> +                   "if provided");

Same comment as above.

> +        return;
> +    }
>  
>      /* compute missing values, prefer sockets over cores over threads */
>      if (cpus == 0 || sockets == 0) {
> @@ -728,8 +740,8 @@ static void pc_smp_parse(MachineState *ms, SMPConfiguration *config, Error **err
>              sockets = sockets > 0 ? sockets : 1;
>              cpus = cores * threads * dies * sockets;
>          } else {
> -            ms->smp.max_cpus = config->has_maxcpus ? config->maxcpus : cpus;
> -            sockets = ms->smp.max_cpus / (cores * threads * dies);
> +            maxcpus = maxcpus > 0 ? maxcpus : cpus;
> +            sockets = maxcpus / (cores * threads * dies);
>          }
>      } else if (cores == 0) {
>          threads = threads > 0 ? threads : 1;
> @@ -746,27 +758,28 @@ static void pc_smp_parse(MachineState *ms, SMPConfiguration *config, Error **err
>          return;
>      }
>  
> -    ms->smp.max_cpus = config->has_maxcpus ? config->maxcpus : cpus;
> +    maxcpus = maxcpus > 0 ? maxcpus : cpus;
>  
> -    if (ms->smp.max_cpus < cpus) {
> +    if (maxcpus < cpus) {
>          error_setg(errp, "maxcpus must be equal to or greater than smp");
>          return;
>      }
>  
> -    if (sockets * dies * cores * threads != ms->smp.max_cpus) {
> +    if (sockets * dies * cores * threads != maxcpus) {
>          error_setg(errp, "Invalid CPU topology deprecated: "
>                     "sockets (%u) * dies (%u) * cores (%u) * threads (%u) "
>                     "!= maxcpus (%u)",
>                     sockets, dies, cores, threads,
> -                   ms->smp.max_cpus);
> +                   maxcpus);
>          return;
>      }
>  
>      ms->smp.cpus = cpus;
> -    ms->smp.cores = cores;
> -    ms->smp.threads = threads;
>      ms->smp.sockets = sockets;
>      ms->smp.dies = dies;
> +    ms->smp.cores = cores;
> +    ms->smp.threads = threads;
> +    ms->smp.max_cpus = maxcpus;
>  }
>  
>  static
> diff --git a/qapi/machine.json b/qapi/machine.json
> index c3210ee1fb..c11b2e6f73 100644
> --- a/qapi/machine.json
> +++ b/qapi/machine.json
> @@ -1288,8 +1288,8 @@
>  ##
>  # @SMPConfiguration:
>  #
> -# Schema for CPU topology configuration.  "0" or a missing value lets
> -# QEMU figure out a suitable value based on the ones that are provided.
> +# Schema for CPU topology configuration. A missing value lets QEMU
> +# figure out a suitable value based on the ones that are provided.
>  #
>  # @cpus: number of virtual CPUs in the virtual machine
>  #
> -- 
> 2.19.1
> 

Otherwise

Reviewed-by: Andrew Jones <drjones@redhat.com>



^ permalink raw reply	[flat|nested] 58+ messages in thread

* Re: [PATCH for-6.2 v2 02/11] machine: Make smp_parse generic enough for all arches
  2021-07-19  3:20 ` [PATCH for-6.2 v2 02/11] machine: Make smp_parse generic enough for all arches Yanan Wang
@ 2021-07-19 16:28   ` Andrew Jones
  2021-07-19 16:36     ` Daniel P. Berrangé
  2021-07-19 16:53   ` Daniel P. Berrangé
  2021-07-20  6:57   ` Cornelia Huck
  2 siblings, 1 reply; 58+ messages in thread
From: Andrew Jones @ 2021-07-19 16:28 UTC (permalink / raw)
  To: Yanan Wang
  Cc: Peter Maydell, Thomas Huth, Daniel P . Berrangé,
	Eduardo Habkost, Michael S . Tsirkin, wanghaibin.wang,
	Richard Henderson, qemu-devel, Greg Kurz, Halil Pasic,
	Igor Mammedov, yuzenghui, Paolo Bonzini, David Gibson

On Mon, Jul 19, 2021 at 11:20:34AM +0800, Yanan Wang wrote:
> Currently the only difference between smp_parse and pc_smp_parse
> is the support of multi-dies and the related error reporting code.
> With an arch compat variable "bool smp_dies_supported", we can
> easily make smp_parse generic enough for all arches and the PC
> specific one can be removed.
> 
> Making smp_parse() generic enough can reduce code duplication and
> ease the code maintenance, and also allows extending the topology
> with more arch specific members (e.g., clusters) in the future.
> 
> No functional change intended.
> 
> Suggested-by: Andrew Jones <drjones@redhat.com>
> Signed-off-by: Yanan Wang <wangyanan55@huawei.com>
> ---
>  hw/core/machine.c   | 28 ++++++++++-------
>  hw/i386/pc.c        | 76 +--------------------------------------------
>  include/hw/boards.h |  1 +
>  3 files changed, 19 insertions(+), 86 deletions(-)
> 
> diff --git a/hw/core/machine.c b/hw/core/machine.c
> index d73daa10f4..ed6712e964 100644
> --- a/hw/core/machine.c
> +++ b/hw/core/machine.c
> @@ -743,6 +743,7 @@ void machine_set_cpu_numa_node(MachineState *machine,
>  
>  static void smp_parse(MachineState *ms, SMPConfiguration *config, Error **errp)
>  {
> +    MachineClass *mc = MACHINE_GET_CLASS(ms);
>      unsigned cpus    = config->has_cpus ? config->cpus : 0;
>      unsigned sockets = config->has_sockets ? config->sockets : 0;
>      unsigned dies    = config->has_dies ? config->dies : 1;
> @@ -761,7 +762,7 @@ static void smp_parse(MachineState *ms, SMPConfiguration *config, Error **errp)
>          return;
>      }
>  
> -    if (dies > 1) {
> +    if (!mc->smp_dies_supported && dies > 1) {

Won't this allow a user on an arch with !mc->smp_dies_supported to specify
dies=1? To not allow that, can we do

   if (!mc->smp_dies_supported && config->has_dies)

instead?

>          error_setg(errp, "dies not supported by this machine's CPU topology");
>          return;
>      }
> @@ -772,23 +773,25 @@ static void smp_parse(MachineState *ms, SMPConfiguration *config, Error **errp)
>          threads = threads > 0 ? threads : 1;
>          if (cpus == 0) {
>              sockets = sockets > 0 ? sockets : 1;
> -            cpus = cores * threads * sockets;
> +            cpus = sockets * dies * cores * threads;
>          } else {
>              maxcpus = maxcpus > 0 ? maxcpus : cpus;
> -            sockets = maxcpus / (cores * threads);
> +            sockets = maxcpus / (dies * cores * threads);
>          }
>      } else if (cores == 0) {
>          threads = threads > 0 ? threads : 1;
> -        cores = cpus / (sockets * threads);
> +        cores = cpus / (sockets * dies * threads);
>          cores = cores > 0 ? cores : 1;
>      } else if (threads == 0) {
> -        threads = cpus / (cores * sockets);
> +        threads = cpus / (sockets * dies * cores);
>          threads = threads > 0 ? threads : 1;
> -    } else if (sockets * cores * threads < cpus) {
> +    } else if (sockets * dies * cores * threads < cpus) {
> +        g_autofree char *dies_msg = g_strdup_printf(
> +            mc->smp_dies_supported ? " * dies (%u)" : "", dies);
>          error_setg(errp, "cpu topology: "
> -                   "sockets (%u) * cores (%u) * threads (%u) < "
> +                   "sockets (%u)%s * cores (%u) * threads (%u) < "
>                     "smp_cpus (%u)",
> -                   sockets, cores, threads, cpus);
> +                   sockets, dies_msg, cores, threads, cpus);
>          return;
>      }
>  
> @@ -799,17 +802,20 @@ static void smp_parse(MachineState *ms, SMPConfiguration *config, Error **errp)
>          return;
>      }
>  
> -    if (sockets * cores * threads != maxcpus) {
> +    if (sockets * dies * cores * threads != maxcpus) {
> +        g_autofree char *dies_msg = g_strdup_printf(
> +            mc->smp_dies_supported ? " * dies (%u)" : "", dies);
>          error_setg(errp, "Invalid CPU topology: "
> -                   "sockets (%u) * cores (%u) * threads (%u) "
> +                   "sockets (%u)%s * cores (%u) * threads (%u) "
>                     "!= maxcpus (%u)",
> -                   sockets, cores, threads,
> +                   sockets, dies_msg, cores, threads,
>                     maxcpus);
>          return;
>      }
>  
>      ms->smp.cpus = cpus;
>      ms->smp.sockets = sockets;
> +    ms->smp.dies = dies;
>      ms->smp.cores = cores;
>      ms->smp.threads = threads;
>      ms->smp.max_cpus = maxcpus;
> diff --git a/hw/i386/pc.c b/hw/i386/pc.c
> index c6b63c00a5..d94ef582b5 100644
> --- a/hw/i386/pc.c
> +++ b/hw/i386/pc.c
> @@ -708,80 +708,6 @@ void pc_acpi_smi_interrupt(void *opaque, int irq, int level)
>      }
>  }
>  
> -/*
> - * This function is very similar to smp_parse()
> - * in hw/core/machine.c but includes CPU die support.
> - */
> -static void pc_smp_parse(MachineState *ms, SMPConfiguration *config, Error **errp)
> -{
> -    unsigned cpus    = config->has_cpus ? config->cpus : 0;
> -    unsigned sockets = config->has_sockets ? config->sockets : 0;
> -    unsigned dies    = config->has_dies ? config->dies : 1;
> -    unsigned cores   = config->has_cores ? config->cores : 0;
> -    unsigned threads = config->has_threads ? config->threads : 0;
> -    unsigned maxcpus = config->has_maxcpus ? config->maxcpus : 0;
> -
> -    if ((config->has_cpus && config->cpus == 0) ||
> -        (config->has_sockets && config->sockets == 0) ||
> -        (config->has_dies && config->dies == 0) ||
> -        (config->has_cores && config->cores == 0) ||
> -        (config->has_threads && config->threads == 0) ||
> -        (config->has_maxcpus && config->maxcpus == 0)) {
> -        error_setg(errp, "parameters must be equal to or greater than one"
> -                   "if provided");
> -        return;
> -    }
> -
> -    /* compute missing values, prefer sockets over cores over threads */
> -    if (cpus == 0 || sockets == 0) {
> -        cores = cores > 0 ? cores : 1;
> -        threads = threads > 0 ? threads : 1;
> -        if (cpus == 0) {
> -            sockets = sockets > 0 ? sockets : 1;
> -            cpus = cores * threads * dies * sockets;
> -        } else {
> -            maxcpus = maxcpus > 0 ? maxcpus : cpus;
> -            sockets = maxcpus / (cores * threads * dies);
> -        }
> -    } else if (cores == 0) {
> -        threads = threads > 0 ? threads : 1;
> -        cores = cpus / (sockets * dies * threads);
> -        cores = cores > 0 ? cores : 1;
> -    } else if (threads == 0) {
> -        threads = cpus / (cores * dies * sockets);
> -        threads = threads > 0 ? threads : 1;
> -    } else if (sockets * dies * cores * threads < cpus) {
> -        error_setg(errp, "cpu topology: "
> -                   "sockets (%u) * dies (%u) * cores (%u) * threads (%u) < "
> -                   "smp_cpus (%u)",
> -                   sockets, dies, cores, threads, cpus);
> -        return;
> -    }
> -
> -    maxcpus = maxcpus > 0 ? maxcpus : cpus;
> -
> -    if (maxcpus < cpus) {
> -        error_setg(errp, "maxcpus must be equal to or greater than smp");
> -        return;
> -    }
> -
> -    if (sockets * dies * cores * threads != maxcpus) {
> -        error_setg(errp, "Invalid CPU topology deprecated: "
> -                   "sockets (%u) * dies (%u) * cores (%u) * threads (%u) "
> -                   "!= maxcpus (%u)",
> -                   sockets, dies, cores, threads,
> -                   maxcpus);
> -        return;
> -    }
> -
> -    ms->smp.cpus = cpus;
> -    ms->smp.sockets = sockets;
> -    ms->smp.dies = dies;
> -    ms->smp.cores = cores;
> -    ms->smp.threads = threads;
> -    ms->smp.max_cpus = maxcpus;
> -}
> -
>  static
>  void pc_machine_done(Notifier *notifier, void *data)
>  {
> @@ -1735,7 +1661,6 @@ static void pc_machine_class_init(ObjectClass *oc, void *data)
>      mc->auto_enable_numa_with_memdev = true;
>      mc->has_hotpluggable_cpus = true;
>      mc->default_boot_order = "cad";
> -    mc->smp_parse = pc_smp_parse;

Can we also get rid of MachineClass::smp_parse now?

>      mc->block_default_type = IF_IDE;
>      mc->max_cpus = 255;
>      mc->reset = pc_machine_reset;
> @@ -1746,6 +1671,7 @@ static void pc_machine_class_init(ObjectClass *oc, void *data)
>      hc->unplug = pc_machine_device_unplug_cb;
>      mc->default_cpu_type = TARGET_DEFAULT_CPU_TYPE;
>      mc->nvdimm_supported = true;
> +    mc->smp_dies_supported = true;
>      mc->default_ram_id = "pc.ram";
>  
>      object_class_property_add(oc, PC_MACHINE_MAX_RAM_BELOW_4G, "size",
> diff --git a/include/hw/boards.h b/include/hw/boards.h
> index accd6eff35..b6161cee88 100644
> --- a/include/hw/boards.h
> +++ b/include/hw/boards.h
> @@ -246,6 +246,7 @@ struct MachineClass {
>      bool smbus_no_migration_support;
>      bool nvdimm_supported;
>      bool numa_mem_supported;
> +    bool smp_dies_supported;
>      bool auto_enable_numa;
>      const char *default_ram_id;
>  
> -- 
> 2.19.1
>

Thanks,
drew 



^ permalink raw reply	[flat|nested] 58+ messages in thread

* Re: [PATCH for-6.2 v2 02/11] machine: Make smp_parse generic enough for all arches
  2021-07-19 16:28   ` Andrew Jones
@ 2021-07-19 16:36     ` Daniel P. Berrangé
  2021-07-19 16:48       ` Andrew Jones
  0 siblings, 1 reply; 58+ messages in thread
From: Daniel P. Berrangé @ 2021-07-19 16:36 UTC (permalink / raw)
  To: Andrew Jones
  Cc: Peter Maydell, Thomas Huth, Eduardo Habkost, Michael S . Tsirkin,
	wanghaibin.wang, Richard Henderson, qemu-devel, Greg Kurz,
	Yanan Wang, yuzenghui, Igor Mammedov, Halil Pasic, Paolo Bonzini,
	David Gibson

On Mon, Jul 19, 2021 at 06:28:46PM +0200, Andrew Jones wrote:
> On Mon, Jul 19, 2021 at 11:20:34AM +0800, Yanan Wang wrote:
> > Currently the only difference between smp_parse and pc_smp_parse
> > is the support of multi-dies and the related error reporting code.
> > With an arch compat variable "bool smp_dies_supported", we can
> > easily make smp_parse generic enough for all arches and the PC
> > specific one can be removed.
> > 
> > Making smp_parse() generic enough can reduce code duplication and
> > ease the code maintenance, and also allows extending the topology
> > with more arch specific members (e.g., clusters) in the future.
> > 
> > No functional change intended.
> > 
> > Suggested-by: Andrew Jones <drjones@redhat.com>
> > Signed-off-by: Yanan Wang <wangyanan55@huawei.com>
> > ---
> >  hw/core/machine.c   | 28 ++++++++++-------
> >  hw/i386/pc.c        | 76 +--------------------------------------------
> >  include/hw/boards.h |  1 +
> >  3 files changed, 19 insertions(+), 86 deletions(-)
> > 
> > diff --git a/hw/core/machine.c b/hw/core/machine.c
> > index d73daa10f4..ed6712e964 100644
> > --- a/hw/core/machine.c
> > +++ b/hw/core/machine.c
> > @@ -743,6 +743,7 @@ void machine_set_cpu_numa_node(MachineState *machine,
> >  
> >  static void smp_parse(MachineState *ms, SMPConfiguration *config, Error **errp)
> >  {
> > +    MachineClass *mc = MACHINE_GET_CLASS(ms);
> >      unsigned cpus    = config->has_cpus ? config->cpus : 0;
> >      unsigned sockets = config->has_sockets ? config->sockets : 0;
> >      unsigned dies    = config->has_dies ? config->dies : 1;
> > @@ -761,7 +762,7 @@ static void smp_parse(MachineState *ms, SMPConfiguration *config, Error **errp)
> >          return;
> >      }
> >  
> > -    if (dies > 1) {
> > +    if (!mc->smp_dies_supported && dies > 1) {
> 
> Won't this allow a user on an arch with !mc->smp_dies_supported to specify
> dies=1?

Conceptually that is fine. Before the introduction of CPU sockets
with 2+ dies, you can credibly say that all sockets had 1 die, so
it is nreasonable for users to say -smp ....,dies=1,....

libvirt will unconditionally set dies=1 for all QEMU targets if
the user didn't specify an explicit dies value

>          To not allow that, can we do
> 
>    if (!mc->smp_dies_supported && config->has_dies)
> 
> instead?

I don't see that this is benefitting apps/users.


Regards,
Daniel
-- 
|: https://berrange.com      -o-    https://www.flickr.com/photos/dberrange :|
|: https://libvirt.org         -o-            https://fstop138.berrange.com :|
|: https://entangle-photo.org    -o-    https://www.instagram.com/dberrange :|



^ permalink raw reply	[flat|nested] 58+ messages in thread

* Re: [PATCH for-6.2 v2 03/11] machine: Uniformly use maxcpus to calculate the omitted parameters
  2021-07-19  3:20 ` [PATCH for-6.2 v2 03/11] machine: Uniformly use maxcpus to calculate the omitted parameters Yanan Wang
@ 2021-07-19 16:36   ` Andrew Jones
  2021-07-22  3:00     ` wangyanan (Y)
  0 siblings, 1 reply; 58+ messages in thread
From: Andrew Jones @ 2021-07-19 16:36 UTC (permalink / raw)
  To: Yanan Wang
  Cc: Peter Maydell, Thomas Huth, Daniel P . Berrangé,
	Eduardo Habkost, Michael S . Tsirkin, wanghaibin.wang,
	Richard Henderson, qemu-devel, Greg Kurz, Halil Pasic,
	Igor Mammedov, yuzenghui, Paolo Bonzini, David Gibson

On Mon, Jul 19, 2021 at 11:20:35AM +0800, Yanan Wang wrote:
> We are currently using maxcpus to calculate the omitted sockets
> but using cpus to calculate the omitted cores/threads. This makes
> cmdlines like:
>   -smp cpus=8,maxcpus=16
>   -smp cpus=8,cores=4,maxcpus=16
>   -smp cpus=8,threads=2,maxcpus=16
> work fine but the ones like:
>   -smp cpus=8,sockets=2,maxcpus=16
>   -smp cpus=8,sockets=2,cores=4,maxcpus=16
>   -smp cpus=8,sockets=2,threads=2,maxcpus=16
> break the invalid cpu topology check.
> 
> Since we require for the valid config that the sum of "sockets * cores
> * dies * threads" should equal to the maxcpus, we should uniformly use
> maxcpus to calculate their omitted values.
> 
> Also the if-branch of "cpus == 0 || sockets == 0" was splited into two
> branches of "cpus == 0" and "sockets == 0" so that we can clearly read
> that we are parsing -smp cmdlines with a preference of cpus over sockets
> over cores over threads.
> 
> Note: change in this patch won't affect any existing working cmdlines
> but improves consistency and allow more incomplete configs to be valid.

We also remove rounding of cores and threads when the math doesn't come
out right, which could possible start reporting a bad config as invalid
which worked before. Or were you able to prove that that can't happen with
your testing?

> 
> Signed-off-by: Yanan Wang <wangyanan55@huawei.com>
> ---
>  hw/core/machine.c | 30 +++++++++++++++---------------
>  1 file changed, 15 insertions(+), 15 deletions(-)
> 
> diff --git a/hw/core/machine.c b/hw/core/machine.c
> index ed6712e964..c9f15b15a5 100644
> --- a/hw/core/machine.c
> +++ b/hw/core/machine.c
> @@ -768,24 +768,26 @@ static void smp_parse(MachineState *ms, SMPConfiguration *config, Error **errp)
>      }
>  
>      /* compute missing values, prefer sockets over cores over threads */
> -    if (cpus == 0 || sockets == 0) {
> +    maxcpus = maxcpus > 0 ? maxcpus : cpus;
> +
> +    if (cpus == 0) {
> +        sockets = sockets > 0 ? sockets : 1;
>          cores = cores > 0 ? cores : 1;
>          threads = threads > 0 ? threads : 1;
> -        if (cpus == 0) {
> -            sockets = sockets > 0 ? sockets : 1;
> -            cpus = sockets * dies * cores * threads;
> -        } else {
> -            maxcpus = maxcpus > 0 ? maxcpus : cpus;
> -            sockets = maxcpus / (dies * cores * threads);
> -        }
> +        cpus = sockets * dies * cores * threads;
> +        maxcpus = maxcpus > 0 ? maxcpus : cpus;
> +    } else if (sockets == 0) {
> +        cores = cores > 0 ? cores : 1;
> +        threads = threads > 0 ? threads : 1;
> +        sockets = maxcpus / (dies * cores * threads);
>      } else if (cores == 0) {
>          threads = threads > 0 ? threads : 1;
> -        cores = cpus / (sockets * dies * threads);
> -        cores = cores > 0 ? cores : 1;
> +        cores = maxcpus / (sockets * dies * threads);
>      } else if (threads == 0) {
> -        threads = cpus / (sockets * dies * cores);
> -        threads = threads > 0 ? threads : 1;
> -    } else if (sockets * dies * cores * threads < cpus) {
> +        threads = maxcpus / (sockets * dies * cores);
> +    }
> +
> +    if (sockets * dies * cores * threads < cpus) {
>          g_autofree char *dies_msg = g_strdup_printf(
>              mc->smp_dies_supported ? " * dies (%u)" : "", dies);
>          error_setg(errp, "cpu topology: "
> @@ -795,8 +797,6 @@ static void smp_parse(MachineState *ms, SMPConfiguration *config, Error **errp)
>          return;
>      }
>  
> -    maxcpus = maxcpus > 0 ? maxcpus : cpus;
> -
>      if (maxcpus < cpus) {
>          error_setg(errp, "maxcpus must be equal to or greater than smp");
>          return;
> -- 
> 2.19.1
>

Reviewed-by: Andrew Jones <drjones@redhat.com>



^ permalink raw reply	[flat|nested] 58+ messages in thread

* Re: [PATCH for-6.2 v2 04/11] machine: Use the computed parameters to calculate omitted cpus
  2021-07-19  3:20 ` [PATCH for-6.2 v2 04/11] machine: Use the computed parameters to calculate omitted cpus Yanan Wang
@ 2021-07-19 16:42   ` Andrew Jones
  2021-07-22  4:42     ` wangyanan (Y)
  0 siblings, 1 reply; 58+ messages in thread
From: Andrew Jones @ 2021-07-19 16:42 UTC (permalink / raw)
  To: Yanan Wang
  Cc: Peter Maydell, Thomas Huth, Daniel P . Berrangé,
	Eduardo Habkost, Michael S . Tsirkin, wanghaibin.wang,
	Richard Henderson, qemu-devel, Greg Kurz, Halil Pasic,
	Igor Mammedov, yuzenghui, Paolo Bonzini, David Gibson

On Mon, Jul 19, 2021 at 11:20:36AM +0800, Yanan Wang wrote:
> Currently we directly calculate the omitted cpus based on the already
> provided parameters. This makes some cmdlines like:
>   -smp maxcpus=16
>   -smp sockets=2,maxcpus=16
>   -smp sockets=2,dies=2,maxcpus=16
>   -smp sockets=2,cores=4,maxcpus=16
> not work. We should probably use the computed paramters to calculate
> cpus when maxcpus is provided while cpus is omitted, which will make
> above configs start to work.
> 
> Note: change in this patch won't affect any existing working cmdlines
> but allows more incomplete configs to be valid.
> 
> Signed-off-by: Yanan Wang <wangyanan55@huawei.com>
> ---
>  hw/core/machine.c | 17 +++++++++--------
>  1 file changed, 9 insertions(+), 8 deletions(-)
> 
> diff --git a/hw/core/machine.c b/hw/core/machine.c
> index c9f15b15a5..668f0a1553 100644
> --- a/hw/core/machine.c
> +++ b/hw/core/machine.c
> @@ -767,26 +767,27 @@ static void smp_parse(MachineState *ms, SMPConfiguration *config, Error **errp)
>          return;
>      }
>  
> -    /* compute missing values, prefer sockets over cores over threads */
>      maxcpus = maxcpus > 0 ? maxcpus : cpus;
>  
> -    if (cpus == 0) {
> -        sockets = sockets > 0 ? sockets : 1;
> -        cores = cores > 0 ? cores : 1;
> -        threads = threads > 0 ? threads : 1;
> -        cpus = sockets * dies * cores * threads;
> -        maxcpus = maxcpus > 0 ? maxcpus : cpus;
> -    } else if (sockets == 0) {
> +    /* compute missing values, prefer sockets over cores over threads */
> +    if (sockets == 0) {
>          cores = cores > 0 ? cores : 1;
>          threads = threads > 0 ? threads : 1;
>          sockets = maxcpus / (dies * cores * threads);
> +        sockets = sockets > 0 ? sockets : 1;
>      } else if (cores == 0) {
>          threads = threads > 0 ? threads : 1;
>          cores = maxcpus / (sockets * dies * threads);
> +        cores = cores > 0 ? cores : 1;
>      } else if (threads == 0) {
>          threads = maxcpus / (sockets * dies * cores);
> +        threads = threads > 0 ? threads : 1;
>      }

I didn't think we wanted this rounding which this patch adds back into
cores and threads and now also sockets.

>  
> +    /* use the computed parameters to calculate the omitted cpus */
> +    cpus = cpus > 0 ? cpus : sockets * dies * cores * threads;
> +    maxcpus = maxcpus > 0 ? maxcpus : cpus;

It doesn't really matter, but I think I'd rather write this like

 maxcpus = maxcpus > 0 ? maxcpus : sockets * dies * cores * threads;
 cpus = cpus > 0 ? cpus : maxcpus;

> +
>      if (sockets * dies * cores * threads < cpus) {
>          g_autofree char *dies_msg = g_strdup_printf(
>              mc->smp_dies_supported ? " * dies (%u)" : "", dies);
> -- 
> 2.19.1
> 

Thanks,
drew



^ permalink raw reply	[flat|nested] 58+ messages in thread

* Re: [PATCH for-6.2 v2 01/11] machine: Disallow specifying topology parameters as zero
  2021-07-19  3:20 ` [PATCH for-6.2 v2 01/11] machine: Disallow specifying topology parameters as zero Yanan Wang
  2021-07-19 16:11   ` Andrew Jones
@ 2021-07-19 16:46   ` Daniel P. Berrangé
  2021-07-21 12:35     ` wangyanan (Y)
  1 sibling, 1 reply; 58+ messages in thread
From: Daniel P. Berrangé @ 2021-07-19 16:46 UTC (permalink / raw)
  To: Yanan Wang
  Cc: Peter Maydell, Andrew Jones, Eduardo Habkost,
	Michael S . Tsirkin, wanghaibin.wang, Richard Henderson,
	qemu-devel, Greg Kurz, Halil Pasic, yuzenghui, Igor Mammedov,
	Thomas Huth, Paolo Bonzini, David Gibson

On Mon, Jul 19, 2021 at 11:20:33AM +0800, Yanan Wang wrote:
> In the SMP configuration, we should either specify a topology
> parameter with a reasonable value (equal to or greater than 1)
> or just leave it omitted and QEMU will calculate its value.
> 
> Configurations which explicitly specify the topology parameters
> as zero like "sockets=0" are meaningless, so disallow them.
> 
> Suggested-by: Andrew Jones <drjones@redhat.com>
> Signed-off-by: Yanan Wang <wangyanan55@huawei.com>
> ---
>  hw/core/machine.c | 31 +++++++++++++++++++++++--------
>  hw/i386/pc.c      | 29 +++++++++++++++++++++--------
>  qapi/machine.json |  4 ++--
>  3 files changed, 46 insertions(+), 18 deletions(-)
> 
> diff --git a/hw/core/machine.c b/hw/core/machine.c
> index 775add0795..d73daa10f4 100644
> --- a/hw/core/machine.c
> +++ b/hw/core/machine.c
> @@ -745,11 +745,25 @@ static void smp_parse(MachineState *ms, SMPConfiguration *config, Error **errp)
>  {
>      unsigned cpus    = config->has_cpus ? config->cpus : 0;
>      unsigned sockets = config->has_sockets ? config->sockets : 0;
> +    unsigned dies    = config->has_dies ? config->dies : 1;

It looks odd to set dies=1 by default at initially, when everything
else is set to 0.  I realize you're just copying existing pc_smp_parse
code in this respect, but I feel like could benefit from a separate
initialization with a comment to explain why we're hardcoding it
to 1....

>      unsigned cores   = config->has_cores ? config->cores : 0;
>      unsigned threads = config->has_threads ? config->threads : 0;
> +    unsigned maxcpus = config->has_maxcpus ? config->maxcpus : 0;
> +
> +    if ((config->has_cpus && config->cpus == 0) ||
> +        (config->has_sockets && config->sockets == 0) ||
> +        (config->has_dies && config->dies == 0) ||
> +        (config->has_cores && config->cores == 0) ||
> +        (config->has_threads && config->threads == 0) ||
> +        (config->has_maxcpus && config->maxcpus == 0)) {
> +        error_setg(errp, "parameters must be equal to or greater than one"
> +                   "if provided");
> +        return;
> +    }
>  
> -    if (config->has_dies && config->dies != 0 && config->dies != 1) {
> +    if (dies > 1) {
>          error_setg(errp, "dies not supported by this machine's CPU topology");
> +        return;
>      }

.... eg how about here adding

     /* Never try to assign multiple dies when defaulting omitted topology */
     if (dies == 0) {
         dies = 1;
     }



> diff --git a/qapi/machine.json b/qapi/machine.json
> index c3210ee1fb..c11b2e6f73 100644
> --- a/qapi/machine.json
> +++ b/qapi/machine.json
> @@ -1288,8 +1288,8 @@
>  ##
>  # @SMPConfiguration:
>  #
> -# Schema for CPU topology configuration.  "0" or a missing value lets
> -# QEMU figure out a suitable value based on the ones that are provided.
> +# Schema for CPU topology configuration. A missing value lets QEMU
> +# figure out a suitable value based on the ones that are provided.

Hmm, so we had actually documented that '0' had the same semantics
as omitting a parameter. This was done in:

  commit 1e63fe685804dfadddd643bf3860b1a59702d4bf
  Author: Paolo Bonzini <pbonzini@redhat.com>
  Date:   Thu Jun 17 17:53:06 2021 +0200

    machine: pass QAPI struct to mc->smp_parse

which hasn't been released yet.

This was possible, but never documented, with the traditiaonl -smp
impl before it was qapi-ified. I think that historical behaviour
was simply a side effect of the QemuOpts impl rather than an
intentional design, hence not documented.

At the very least I think need to get rid of this bit of docs about
"0" before this release, otherwise we'll have stronger need to
consider a real deprecation process.

Regards,
Daniel
-- 
|: https://berrange.com      -o-    https://www.flickr.com/photos/dberrange :|
|: https://libvirt.org         -o-            https://fstop138.berrange.com :|
|: https://entangle-photo.org    -o-    https://www.instagram.com/dberrange :|



^ permalink raw reply	[flat|nested] 58+ messages in thread

* Re: [PATCH for-6.2 v2 02/11] machine: Make smp_parse generic enough for all arches
  2021-07-19 16:36     ` Daniel P. Berrangé
@ 2021-07-19 16:48       ` Andrew Jones
  2021-07-19 16:50         ` Daniel P. Berrangé
  0 siblings, 1 reply; 58+ messages in thread
From: Andrew Jones @ 2021-07-19 16:48 UTC (permalink / raw)
  To: Daniel P. Berrangé
  Cc: Peter Maydell, Thomas Huth, Eduardo Habkost, Michael S . Tsirkin,
	wanghaibin.wang, Richard Henderson, qemu-devel, Greg Kurz,
	Yanan Wang, yuzenghui, Igor Mammedov, Halil Pasic, Paolo Bonzini,
	David Gibson

On Mon, Jul 19, 2021 at 05:36:39PM +0100, Daniel P. Berrangé wrote:
> On Mon, Jul 19, 2021 at 06:28:46PM +0200, Andrew Jones wrote:
> > On Mon, Jul 19, 2021 at 11:20:34AM +0800, Yanan Wang wrote:
> > > Currently the only difference between smp_parse and pc_smp_parse
> > > is the support of multi-dies and the related error reporting code.
> > > With an arch compat variable "bool smp_dies_supported", we can
> > > easily make smp_parse generic enough for all arches and the PC
> > > specific one can be removed.
> > > 
> > > Making smp_parse() generic enough can reduce code duplication and
> > > ease the code maintenance, and also allows extending the topology
> > > with more arch specific members (e.g., clusters) in the future.
> > > 
> > > No functional change intended.
> > > 
> > > Suggested-by: Andrew Jones <drjones@redhat.com>
> > > Signed-off-by: Yanan Wang <wangyanan55@huawei.com>
> > > ---
> > >  hw/core/machine.c   | 28 ++++++++++-------
> > >  hw/i386/pc.c        | 76 +--------------------------------------------
> > >  include/hw/boards.h |  1 +
> > >  3 files changed, 19 insertions(+), 86 deletions(-)
> > > 
> > > diff --git a/hw/core/machine.c b/hw/core/machine.c
> > > index d73daa10f4..ed6712e964 100644
> > > --- a/hw/core/machine.c
> > > +++ b/hw/core/machine.c
> > > @@ -743,6 +743,7 @@ void machine_set_cpu_numa_node(MachineState *machine,
> > >  
> > >  static void smp_parse(MachineState *ms, SMPConfiguration *config, Error **errp)
> > >  {
> > > +    MachineClass *mc = MACHINE_GET_CLASS(ms);
> > >      unsigned cpus    = config->has_cpus ? config->cpus : 0;
> > >      unsigned sockets = config->has_sockets ? config->sockets : 0;
> > >      unsigned dies    = config->has_dies ? config->dies : 1;
> > > @@ -761,7 +762,7 @@ static void smp_parse(MachineState *ms, SMPConfiguration *config, Error **errp)
> > >          return;
> > >      }
> > >  
> > > -    if (dies > 1) {
> > > +    if (!mc->smp_dies_supported && dies > 1) {
> > 
> > Won't this allow a user on an arch with !mc->smp_dies_supported to specify
> > dies=1?
> 
> Conceptually that is fine. Before the introduction of CPU sockets
> with 2+ dies, you can credibly say that all sockets had 1 die, so
> it is nreasonable for users to say -smp ....,dies=1,....
> 
> libvirt will unconditionally set dies=1 for all QEMU targets if
> the user didn't specify an explicit dies value
> 
> >          To not allow that, can we do
> > 
> >    if (!mc->smp_dies_supported && config->has_dies)
> > 
> > instead?
> 
> I don't see that this is benefitting apps/users.

Other than maintaining consistency; erroring with "we don't support dies"
for 2+, but not for 1, is inconsistent, then I agree there isn't much
reason to disallow it, since we'll be using the value of 1 anyway
internally. I don't really have a strong preference here, so I'm fine with
allowing dies=1.

Thanks,
drew

> 
> 
> Regards,
> Daniel
> -- 
> |: https://berrange.com      -o-    https://www.flickr.com/photos/dberrange :|
> |: https://libvirt.org         -o-            https://fstop138.berrange.com :|
> |: https://entangle-photo.org    -o-    https://www.instagram.com/dberrange :|
> 



^ permalink raw reply	[flat|nested] 58+ messages in thread

* Re: [PATCH for-6.2 v2 02/11] machine: Make smp_parse generic enough for all arches
  2021-07-19 16:48       ` Andrew Jones
@ 2021-07-19 16:50         ` Daniel P. Berrangé
  0 siblings, 0 replies; 58+ messages in thread
From: Daniel P. Berrangé @ 2021-07-19 16:50 UTC (permalink / raw)
  To: Andrew Jones
  Cc: Peter Maydell, Thomas Huth, Eduardo Habkost, Michael S . Tsirkin,
	wanghaibin.wang, Richard Henderson, qemu-devel, Greg Kurz,
	Yanan Wang, yuzenghui, Igor Mammedov, Halil Pasic, Paolo Bonzini,
	David Gibson

On Mon, Jul 19, 2021 at 06:48:15PM +0200, Andrew Jones wrote:
> On Mon, Jul 19, 2021 at 05:36:39PM +0100, Daniel P. Berrangé wrote:
> > On Mon, Jul 19, 2021 at 06:28:46PM +0200, Andrew Jones wrote:
> > > On Mon, Jul 19, 2021 at 11:20:34AM +0800, Yanan Wang wrote:
> > > > Currently the only difference between smp_parse and pc_smp_parse
> > > > is the support of multi-dies and the related error reporting code.
> > > > With an arch compat variable "bool smp_dies_supported", we can
> > > > easily make smp_parse generic enough for all arches and the PC
> > > > specific one can be removed.
> > > > 
> > > > Making smp_parse() generic enough can reduce code duplication and
> > > > ease the code maintenance, and also allows extending the topology
> > > > with more arch specific members (e.g., clusters) in the future.
> > > > 
> > > > No functional change intended.
> > > > 
> > > > Suggested-by: Andrew Jones <drjones@redhat.com>
> > > > Signed-off-by: Yanan Wang <wangyanan55@huawei.com>
> > > > ---
> > > >  hw/core/machine.c   | 28 ++++++++++-------
> > > >  hw/i386/pc.c        | 76 +--------------------------------------------
> > > >  include/hw/boards.h |  1 +
> > > >  3 files changed, 19 insertions(+), 86 deletions(-)
> > > > 
> > > > diff --git a/hw/core/machine.c b/hw/core/machine.c
> > > > index d73daa10f4..ed6712e964 100644
> > > > --- a/hw/core/machine.c
> > > > +++ b/hw/core/machine.c
> > > > @@ -743,6 +743,7 @@ void machine_set_cpu_numa_node(MachineState *machine,
> > > >  
> > > >  static void smp_parse(MachineState *ms, SMPConfiguration *config, Error **errp)
> > > >  {
> > > > +    MachineClass *mc = MACHINE_GET_CLASS(ms);
> > > >      unsigned cpus    = config->has_cpus ? config->cpus : 0;
> > > >      unsigned sockets = config->has_sockets ? config->sockets : 0;
> > > >      unsigned dies    = config->has_dies ? config->dies : 1;
> > > > @@ -761,7 +762,7 @@ static void smp_parse(MachineState *ms, SMPConfiguration *config, Error **errp)
> > > >          return;
> > > >      }
> > > >  
> > > > -    if (dies > 1) {
> > > > +    if (!mc->smp_dies_supported && dies > 1) {
> > > 
> > > Won't this allow a user on an arch with !mc->smp_dies_supported to specify
> > > dies=1?
> > 
> > Conceptually that is fine. Before the introduction of CPU sockets
> > with 2+ dies, you can credibly say that all sockets had 1 die, so
> > it is nreasonable for users to say -smp ....,dies=1,....
> > 
> > libvirt will unconditionally set dies=1 for all QEMU targets if
> > the user didn't specify an explicit dies value
> > 
> > >          To not allow that, can we do
> > > 
> > >    if (!mc->smp_dies_supported && config->has_dies)
> > > 
> > > instead?
> > 
> > I don't see that this is benefitting apps/users.
> 
> Other than maintaining consistency; erroring with "we don't support dies"
> for 2+, but not for 1, is inconsistent, then I agree there isn't much
> reason to disallow it, since we'll be using the value of 1 anyway
> internally. I don't really have a strong preference here, so I'm fine with
> allowing dies=1.

How about we tweak the error message from

  "dies not supported by this machine's CPU topology"

to

  "multiple dies not supported by this machine's CPU topology"


Regards,
Daniel
-- 
|: https://berrange.com      -o-    https://www.flickr.com/photos/dberrange :|
|: https://libvirt.org         -o-            https://fstop138.berrange.com :|
|: https://entangle-photo.org    -o-    https://www.instagram.com/dberrange :|



^ permalink raw reply	[flat|nested] 58+ messages in thread

* Re: [PATCH for-6.2 v2 02/11] machine: Make smp_parse generic enough for all arches
  2021-07-19  3:20 ` [PATCH for-6.2 v2 02/11] machine: Make smp_parse generic enough for all arches Yanan Wang
  2021-07-19 16:28   ` Andrew Jones
@ 2021-07-19 16:53   ` Daniel P. Berrangé
  2021-07-22  7:18     ` wangyanan (Y)
  2021-07-20  6:57   ` Cornelia Huck
  2 siblings, 1 reply; 58+ messages in thread
From: Daniel P. Berrangé @ 2021-07-19 16:53 UTC (permalink / raw)
  To: Yanan Wang
  Cc: Peter Maydell, Andrew Jones, Eduardo Habkost,
	Michael S . Tsirkin, wanghaibin.wang, Richard Henderson,
	qemu-devel, Greg Kurz, Halil Pasic, yuzenghui, Igor Mammedov,
	Thomas Huth, Paolo Bonzini, David Gibson

On Mon, Jul 19, 2021 at 11:20:34AM +0800, Yanan Wang wrote:
> Currently the only difference between smp_parse and pc_smp_parse
> is the support of multi-dies and the related error reporting code.
> With an arch compat variable "bool smp_dies_supported", we can
> easily make smp_parse generic enough for all arches and the PC
> specific one can be removed.
> 
> Making smp_parse() generic enough can reduce code duplication and
> ease the code maintenance, and also allows extending the topology
> with more arch specific members (e.g., clusters) in the future.
> 
> No functional change intended.
> 
> Suggested-by: Andrew Jones <drjones@redhat.com>
> Signed-off-by: Yanan Wang <wangyanan55@huawei.com>
> ---
>  hw/core/machine.c   | 28 ++++++++++-------
>  hw/i386/pc.c        | 76 +--------------------------------------------
>  include/hw/boards.h |  1 +
>  3 files changed, 19 insertions(+), 86 deletions(-)
> 
> diff --git a/hw/core/machine.c b/hw/core/machine.c
> index d73daa10f4..ed6712e964 100644
> --- a/hw/core/machine.c
> +++ b/hw/core/machine.c
> @@ -743,6 +743,7 @@ void machine_set_cpu_numa_node(MachineState *machine,
>  
>  static void smp_parse(MachineState *ms, SMPConfiguration *config, Error **errp)
>  {
> +    MachineClass *mc = MACHINE_GET_CLASS(ms);
>      unsigned cpus    = config->has_cpus ? config->cpus : 0;
>      unsigned sockets = config->has_sockets ? config->sockets : 0;
>      unsigned dies    = config->has_dies ? config->dies : 1;
> @@ -761,7 +762,7 @@ static void smp_parse(MachineState *ms, SMPConfiguration *config, Error **errp)
>          return;
>      }
>  
> -    if (dies > 1) {
> +    if (!mc->smp_dies_supported && dies > 1) {
>          error_setg(errp, "dies not supported by this machine's CPU topology");
>          return;
>      }
> @@ -772,23 +773,25 @@ static void smp_parse(MachineState *ms, SMPConfiguration *config, Error **errp)
>          threads = threads > 0 ? threads : 1;
>          if (cpus == 0) {
>              sockets = sockets > 0 ? sockets : 1;
> -            cpus = cores * threads * sockets;
> +            cpus = sockets * dies * cores * threads;
>          } else {
>              maxcpus = maxcpus > 0 ? maxcpus : cpus;
> -            sockets = maxcpus / (cores * threads);
> +            sockets = maxcpus / (dies * cores * threads);
>          }
>      } else if (cores == 0) {
>          threads = threads > 0 ? threads : 1;
> -        cores = cpus / (sockets * threads);
> +        cores = cpus / (sockets * dies * threads);
>          cores = cores > 0 ? cores : 1;
>      } else if (threads == 0) {
> -        threads = cpus / (cores * sockets);
> +        threads = cpus / (sockets * dies * cores);
>          threads = threads > 0 ? threads : 1;
> -    } else if (sockets * cores * threads < cpus) {
> +    } else if (sockets * dies * cores * threads < cpus) {
> +        g_autofree char *dies_msg = g_strdup_printf(
> +            mc->smp_dies_supported ? " * dies (%u)" : "", dies);
>          error_setg(errp, "cpu topology: "
> -                   "sockets (%u) * cores (%u) * threads (%u) < "
> +                   "sockets (%u)%s * cores (%u) * threads (%u) < "
>                     "smp_cpus (%u)",
> -                   sockets, cores, threads, cpus);
> +                   sockets, dies_msg, cores, threads, cpus);

Since we're allowing dies=1 (but not greater), I'm not convinced we
need the conditionally built error message, and could just include
"* dies" all the time.

If we do want it to be conditionally different though, I'd just
sugest calling error_setg twice. Although this duplicates stuff,
it'll be clearer to read which I think is a net win.

Regards,
Daniel
-- 
|: https://berrange.com      -o-    https://www.flickr.com/photos/dberrange :|
|: https://libvirt.org         -o-            https://fstop138.berrange.com :|
|: https://entangle-photo.org    -o-    https://www.instagram.com/dberrange :|



^ permalink raw reply	[flat|nested] 58+ messages in thread

* Re: [PATCH for-6.2 v2 05/11] machine: Improve the error reporting of smp parsing
  2021-07-19  3:20 ` [PATCH for-6.2 v2 05/11] machine: Improve the error reporting of smp parsing Yanan Wang
@ 2021-07-19 16:53   ` Andrew Jones
  2021-07-22  8:10     ` wangyanan (Y)
  0 siblings, 1 reply; 58+ messages in thread
From: Andrew Jones @ 2021-07-19 16:53 UTC (permalink / raw)
  To: Yanan Wang
  Cc: Peter Maydell, Thomas Huth, Daniel P . Berrangé,
	Eduardo Habkost, Michael S . Tsirkin, wanghaibin.wang,
	Richard Henderson, qemu-devel, Greg Kurz, Halil Pasic,
	Igor Mammedov, yuzenghui, Paolo Bonzini, David Gibson

On Mon, Jul 19, 2021 at 11:20:37AM +0800, Yanan Wang wrote:
> We totally have two requirements for a valid SMP configuration:

s/totally//

> the sum of "sockets * dies * cores * threads" must represent all

the product

> the possible cpus, i.e., max_cpus, and must include the initial
> present cpus, i.e., smp_cpus.
> 
> We only need to ensure "sockets * dies * cores * threads == maxcpus"
> at first and then ensure "sockets * dies * cores * threads >= cpus".

Or, "maxcpus >= cpus"

> With a reasonable order of the sanity-check, we can simplify the
> error reporting code.
> 
> Signed-off-by: Yanan Wang <wangyanan55@huawei.com>
> ---
>  hw/core/machine.c | 25 ++++++++++---------------
>  1 file changed, 10 insertions(+), 15 deletions(-)
> 
> diff --git a/hw/core/machine.c b/hw/core/machine.c
> index 668f0a1553..8b4d07d3fc 100644
> --- a/hw/core/machine.c
> +++ b/hw/core/machine.c
> @@ -788,21 +788,6 @@ static void smp_parse(MachineState *ms, SMPConfiguration *config, Error **errp)
>      cpus = cpus > 0 ? cpus : sockets * dies * cores * threads;
>      maxcpus = maxcpus > 0 ? maxcpus : cpus;
>  
> -    if (sockets * dies * cores * threads < cpus) {
> -        g_autofree char *dies_msg = g_strdup_printf(
> -            mc->smp_dies_supported ? " * dies (%u)" : "", dies);
> -        error_setg(errp, "cpu topology: "
> -                   "sockets (%u)%s * cores (%u) * threads (%u) < "
> -                   "smp_cpus (%u)",
> -                   sockets, dies_msg, cores, threads, cpus);
> -        return;
> -    }
> -
> -    if (maxcpus < cpus) {
> -        error_setg(errp, "maxcpus must be equal to or greater than smp");
> -        return;
> -    }

This may be redundant when determining a valid config, but by checking it
separately we can provide a more useful error message.

> -
>      if (sockets * dies * cores * threads != maxcpus) {
>          g_autofree char *dies_msg = g_strdup_printf(
>              mc->smp_dies_supported ? " * dies (%u)" : "", dies);
> @@ -814,6 +799,16 @@ static void smp_parse(MachineState *ms, SMPConfiguration *config, Error **errp)
>          return;
>      }
>  
> +    if (sockets * dies * cores * threads < cpus) {
> +        g_autofree char *dies_msg = g_strdup_printf(
> +            mc->smp_dies_supported ? " * dies (%u)" : "", dies);
> +        error_setg(errp, "Invalid CPU topology: "
> +                   "sockets (%u)%s * cores (%u) * threads (%u) < "
> +                   "smp_cpus (%u)",
> +                   sockets, dies_msg, cores, threads, cpus);
> +        return;
> +    }
> +
>      ms->smp.cpus = cpus;
>      ms->smp.sockets = sockets;
>      ms->smp.dies = dies;
> -- 
> 2.19.1
>

I'm not sure we need this patch.

Thanks,
drew 



^ permalink raw reply	[flat|nested] 58+ messages in thread

* Re: [PATCH for-6.2 v2 00/11] machine: smp parsing fixes and improvement
  2021-07-19  3:20 [PATCH for-6.2 v2 00/11] machine: smp parsing fixes and improvement Yanan Wang
                   ` (10 preceding siblings ...)
  2021-07-19  3:20 ` [PATCH for-6.2 v2 11/11] tests/unit: Add a unit test for smp parsing Yanan Wang
@ 2021-07-19 16:57 ` Cornelia Huck
  2021-07-21 12:38   ` wangyanan (Y)
  11 siblings, 1 reply; 58+ messages in thread
From: Cornelia Huck @ 2021-07-19 16:57 UTC (permalink / raw)
  To: Yanan Wang, qemu-devel
  Cc: Peter Maydell, Andrew Jones, Daniel P . Berrangé,
	Eduardo Habkost, Pierre Morel, Michael S . Tsirkin,
	Richard Henderson, Greg Kurz, Halil Pasic, Paolo Bonzini,
	Thomas Huth, Igor Mammedov, yuzenghui, wanghaibin.wang,
	Yanan Wang, David Gibson

On Mon, Jul 19 2021, Yanan Wang <wangyanan55@huawei.com> wrote:

> Hi,
>
> This is v2 of the series [1] that I have posted to introduce some smp parsing
> fixes and improvement, much more work has been processed compared to RFC v1.
>
> [1] https://lists.gnu.org/archive/html/qemu-devel/2021-07/msg00259.html
>
> The purpose of this series is to improve/fix the parsing logic. Explicitly
> specifying a CPU topology parameter as zero is not allowed any more, and
> maxcpus is now uniformly used to calculate the omitted parameters. It's also
> suggested that we should start to prefer cores over sockets over threads on
> the newer machine types, which will make the computed virtual topology more
> reflective of the real hardware.
>
> In order to reduce code duplication and ease the code maintenance, smp_parse
> in now converted into a parser generic enough for all arches, so that the PC
> specific one can be removed. It's also convenient to introduce more topology
> members (e.g. cluster) to the generic parser in the future.

Cc:ing Pierre, as he also had been looking at the smp parsing code (for
s390x) recently.

Also, please keep me on cc: for patches that touch s390x.

>
> Finally, a QEMU unit test for the parsing of given SMP configuration is added.
> Since all the parsing logic is in generic function smp_parse(), this test
> passes diffenent SMP configurations to the function and compare the parsing
> result with what is expected. In the test, all possible collections of the
> topology parameters and the corressponding expected results are listed,
> including the valid and invalid ones. The preference of sockets over cores
> and the preference of cores over sockets, and the support of multi-dies are
> also taken into consideration.
>
> ---
>
> Changelogs:
>
> v1->v2:
> - disallow "anything=0" in the smp configuration (Andrew)
> - make function smp_parse() a generic helper for all arches
> - improve the error reporting in the parser
> - start to prefer cores over sockets since 6.2 (Daniel)
> - add a unit test for the smp parsing (Daniel)
>
> ---
>
> Yanan Wang (11):
>   machine: Disallow specifying topology parameters as zero
>   machine: Make smp_parse generic enough for all arches
>   machine: Uniformly use maxcpus to calculate the omitted parameters
>   machine: Use the computed parameters to calculate omitted cpus
>   machine: Improve the error reporting of smp parsing
>   hw: Add compat machines for 6.2
>   machine: Prefer cores over sockets in smp parsing since 6.2
>   machine: Use ms instead of global current_machine in sanity-check
>   machine: Tweak the order of topology members in struct CpuTopology
>   machine: Split out the smp parsing code
>   tests/unit: Add a unit test for smp parsing
>
>  MAINTAINERS                 |    2 +
>  hw/arm/virt.c               |   10 +-
>  hw/core/machine-smp.c       |  124 ++++
>  hw/core/machine.c           |   68 +--
>  hw/core/meson.build         |    1 +
>  hw/i386/pc.c                |   66 +--
>  hw/i386/pc_piix.c           |   15 +-
>  hw/i386/pc_q35.c            |   14 +-
>  hw/ppc/spapr.c              |   16 +-
>  hw/s390x/s390-virtio-ccw.c  |   15 +-
>  include/hw/boards.h         |   13 +-
>  include/hw/i386/pc.h        |    3 +
>  qapi/machine.json           |    6 +-
>  qemu-options.hx             |    4 +-
>  tests/unit/meson.build      |    1 +
>  tests/unit/test-smp-parse.c | 1117 +++++++++++++++++++++++++++++++++++
>  16 files changed, 1338 insertions(+), 137 deletions(-)
>  create mode 100644 hw/core/machine-smp.c
>  create mode 100644 tests/unit/test-smp-parse.c
>
> -- 
> 2.19.1



^ permalink raw reply	[flat|nested] 58+ messages in thread

* Re: [PATCH for-6.2 v2 06/11] hw: Add compat machines for 6.2
  2021-07-19  3:20 ` [PATCH for-6.2 v2 06/11] hw: Add compat machines for 6.2 Yanan Wang
  2021-07-19  3:38   ` David Gibson
@ 2021-07-19 17:00   ` Andrew Jones
  2021-07-19 17:03   ` Cornelia Huck
  2021-07-19 23:45   ` Pankaj Gupta
  3 siblings, 0 replies; 58+ messages in thread
From: Andrew Jones @ 2021-07-19 17:00 UTC (permalink / raw)
  To: Yanan Wang
  Cc: Peter Maydell, Thomas Huth, Daniel P . Berrangé,
	Eduardo Habkost, Michael S . Tsirkin, wanghaibin.wang,
	Richard Henderson, qemu-devel, Greg Kurz, Halil Pasic,
	Igor Mammedov, yuzenghui, Paolo Bonzini, David Gibson

On Mon, Jul 19, 2021 at 11:20:38AM +0800, Yanan Wang wrote:
> Add 6.2 machine types for arm/i440fx/q35/s390x/spapr.
> 
> Signed-off-by: Yanan Wang <wangyanan55@huawei.com>
> ---
>  hw/arm/virt.c              |  9 ++++++++-
>  hw/core/machine.c          |  3 +++
>  hw/i386/pc.c               |  3 +++
>  hw/i386/pc_piix.c          | 14 +++++++++++++-
>  hw/i386/pc_q35.c           | 13 ++++++++++++-
>  hw/ppc/spapr.c             | 15 +++++++++++++--
>  hw/s390x/s390-virtio-ccw.c | 14 +++++++++++++-
>  include/hw/boards.h        |  3 +++
>  include/hw/i386/pc.h       |  3 +++
>  9 files changed, 71 insertions(+), 6 deletions(-)
> 
> diff --git a/hw/arm/virt.c b/hw/arm/virt.c
> index 81eda46b0b..01165f7f53 100644
> --- a/hw/arm/virt.c
> +++ b/hw/arm/virt.c
> @@ -2788,10 +2788,17 @@ static void machvirt_machine_init(void)
>  }
>  type_init(machvirt_machine_init);
>  
> +static void virt_machine_6_2_options(MachineClass *mc)
> +{
> +}
> +DEFINE_VIRT_MACHINE_AS_LATEST(6, 2)
> +
>  static void virt_machine_6_1_options(MachineClass *mc)
>  {
> +    virt_machine_6_2_options(mc);
> +    compat_props_add(mc->compat_props, hw_compat_6_1, hw_compat_6_1_len);
>  }
> -DEFINE_VIRT_MACHINE_AS_LATEST(6, 1)
> +DEFINE_VIRT_MACHINE(6, 1)
>  
>  static void virt_machine_6_0_options(MachineClass *mc)
>  {
> diff --git a/hw/core/machine.c b/hw/core/machine.c
> index 8b4d07d3fc..63439c4a6d 100644
> --- a/hw/core/machine.c
> +++ b/hw/core/machine.c
> @@ -37,6 +37,9 @@
>  #include "hw/virtio/virtio.h"
>  #include "hw/virtio/virtio-pci.h"
>  
> +GlobalProperty hw_compat_6_1[] = {};
> +const size_t hw_compat_6_1_len = G_N_ELEMENTS(hw_compat_6_1);
> +
>  GlobalProperty hw_compat_6_0[] = {
>      { "gpex-pcihost", "allow-unmapped-accesses", "false" },
>      { "i8042", "extended-state", "false"},


> diff --git a/include/hw/boards.h b/include/hw/boards.h
> index b6161cee88..2832f0f8aa 100644
> --- a/include/hw/boards.h
> +++ b/include/hw/boards.h
> @@ -354,6 +354,9 @@ struct MachineState {
>      } \
>      type_init(machine_initfn##_register_types)
>  
> +extern GlobalProperty hw_compat_6_1[];
> +extern const size_t hw_compat_6_1_len;
> +
>  extern GlobalProperty hw_compat_6_0[];
>  extern const size_t hw_compat_6_0_len;
>

Arm and general parts look good to me

Reviewed-by: Andrew Jones <drjones@redhat.com>



^ permalink raw reply	[flat|nested] 58+ messages in thread

* Re: [PATCH for-6.2 v2 06/11] hw: Add compat machines for 6.2
  2021-07-19  3:20 ` [PATCH for-6.2 v2 06/11] hw: Add compat machines for 6.2 Yanan Wang
  2021-07-19  3:38   ` David Gibson
  2021-07-19 17:00   ` Andrew Jones
@ 2021-07-19 17:03   ` Cornelia Huck
  2021-07-19 23:45   ` Pankaj Gupta
  3 siblings, 0 replies; 58+ messages in thread
From: Cornelia Huck @ 2021-07-19 17:03 UTC (permalink / raw)
  To: Yanan Wang, qemu-devel
  Cc: Peter Maydell, Andrew Jones, Daniel P . Berrangé,
	Eduardo Habkost, Michael S . Tsirkin, Richard Henderson,
	Greg Kurz, Halil Pasic, Paolo Bonzini, Thomas Huth,
	Igor Mammedov, yuzenghui, wanghaibin.wang, Yanan Wang,
	David Gibson

On Mon, Jul 19 2021, Yanan Wang <wangyanan55@huawei.com> wrote:

> Add 6.2 machine types for arm/i440fx/q35/s390x/spapr.
>
> Signed-off-by: Yanan Wang <wangyanan55@huawei.com>
> ---
>  hw/arm/virt.c              |  9 ++++++++-
>  hw/core/machine.c          |  3 +++
>  hw/i386/pc.c               |  3 +++
>  hw/i386/pc_piix.c          | 14 +++++++++++++-
>  hw/i386/pc_q35.c           | 13 ++++++++++++-
>  hw/ppc/spapr.c             | 15 +++++++++++++--
>  hw/s390x/s390-virtio-ccw.c | 14 +++++++++++++-
>  include/hw/boards.h        |  3 +++
>  include/hw/i386/pc.h       |  3 +++
>  9 files changed, 71 insertions(+), 6 deletions(-)

Reviewed-by: Cornelia Huck <cohuck@redhat.com>



^ permalink raw reply	[flat|nested] 58+ messages in thread

* Re: [PATCH for-6.2 v2 07/11] machine: Prefer cores over sockets in smp parsing since 6.2
  2021-07-19  3:20 ` [PATCH for-6.2 v2 07/11] machine: Prefer cores over sockets in smp parsing since 6.2 Yanan Wang
  2021-07-19  3:40   ` David Gibson
@ 2021-07-19 17:13   ` Andrew Jones
  2021-07-22  5:32     ` wangyanan (Y)
  1 sibling, 1 reply; 58+ messages in thread
From: Andrew Jones @ 2021-07-19 17:13 UTC (permalink / raw)
  To: Yanan Wang
  Cc: Peter Maydell, Thomas Huth, Daniel P . Berrangé,
	Eduardo Habkost, Michael S . Tsirkin, wanghaibin.wang,
	Richard Henderson, qemu-devel, Greg Kurz, Halil Pasic,
	Igor Mammedov, yuzenghui, Paolo Bonzini, David Gibson

On Mon, Jul 19, 2021 at 11:20:39AM +0800, Yanan Wang wrote:
> In the real SMP hardware topology world, it's much more likely that
> we have high cores-per-socket counts and few sockets totally. While
> the current preference of sockets over cores in smp parsing results
> in a virtual cpu topology with low cores-per-sockets counts and a
> large number of sockets, which is just contrary to the real world.
> 
> Given that it is better to make the virtual cpu topology be more
> reflective of the real world and also for the sake of compatibility,
> we start to prefer cores over sockets over threads in smp parsing
> since machine type 6.2 for different arches.
> 
> In this patch, a boolean "smp_prefer_sockets" is added, and we only
> enable the old preference on older machines and enable the new one
> since type 6.2 for all arches by using the machine compat mechanism.
> 
> Suggested-by: Daniel P. Berrange <berrange@redhat.com>
> Signed-off-by: Yanan Wang <wangyanan55@huawei.com>
> ---
>  hw/arm/virt.c              |  1 +
>  hw/core/machine.c          | 59 +++++++++++++++++++++++++++++---------
>  hw/i386/pc_piix.c          |  1 +
>  hw/i386/pc_q35.c           |  1 +
>  hw/ppc/spapr.c             |  1 +
>  hw/s390x/s390-virtio-ccw.c |  1 +
>  include/hw/boards.h        |  1 +
>  qemu-options.hx            |  4 ++-
>  8 files changed, 55 insertions(+), 14 deletions(-)
> 
> diff --git a/hw/arm/virt.c b/hw/arm/virt.c
> index 01165f7f53..7babea40dc 100644
> --- a/hw/arm/virt.c
> +++ b/hw/arm/virt.c
> @@ -2797,6 +2797,7 @@ static void virt_machine_6_1_options(MachineClass *mc)
>  {
>      virt_machine_6_2_options(mc);
>      compat_props_add(mc->compat_props, hw_compat_6_1, hw_compat_6_1_len);
> +    mc->smp_prefer_sockets = true;
>  }
>  DEFINE_VIRT_MACHINE(6, 1)
>  
> diff --git a/hw/core/machine.c b/hw/core/machine.c
> index 63439c4a6d..c074425015 100644
> --- a/hw/core/machine.c
> +++ b/hw/core/machine.c
> @@ -744,6 +744,22 @@ void machine_set_cpu_numa_node(MachineState *machine,
>      }
>  }
>  
> +/*
> + * smp_parse - Generic function used to parse the given SMP configuration
> + *
> + * The topology parameters must be specified equal to or great than one
> + * or just omitted, explicit configuration like "cpus=0" is not allowed.
> + * The omitted parameters will be calculated based on the provided ones.
> + *
> + * maxcpus will default to the value of cpus if omitted and will be used
> + * to compute the missing sockets/cores/threads. cpus will be calculated
> + * from the computed parametrs if omitted.

parameters

Or how about something like this:

When both maxcpus and cpus are omitted maxcpus will be calculated from the
given parameters and cpus will be set equal to maxcpus. When only one of
maxcpus and cpus is given then the omitted one will be set to its given
counterpart's value. Both maxcpus and cpus may be specified, but cpus must
be less than or equal to maxcpus.

> + *
> + * In calculation of omitted arch-netural sockets/cores/threads, we prefer
> + * sockets over cores over threads before 6.2, while prefer cores over

while preferring

> + * sockets over threads since 6.2 on. The arch-specific dies will directly

s/on//

> + * default to 1 if omitted.
> + */
>  static void smp_parse(MachineState *ms, SMPConfiguration *config, Error **errp)
>  {
>      MachineClass *mc = MACHINE_GET_CLASS(ms);
> @@ -772,19 +788,36 @@ static void smp_parse(MachineState *ms, SMPConfiguration *config, Error **errp)
>  
>      maxcpus = maxcpus > 0 ? maxcpus : cpus;
>  
> -    /* compute missing values, prefer sockets over cores over threads */
> -    if (sockets == 0) {
> -        cores = cores > 0 ? cores : 1;
> -        threads = threads > 0 ? threads : 1;
> -        sockets = maxcpus / (dies * cores * threads);
> -        sockets = sockets > 0 ? sockets : 1;
> -    } else if (cores == 0) {
> -        threads = threads > 0 ? threads : 1;
> -        cores = maxcpus / (sockets * dies * threads);
> -        cores = cores > 0 ? cores : 1;
> -    } else if (threads == 0) {
> -        threads = maxcpus / (sockets * dies * cores);
> -        threads = threads > 0 ? threads : 1;
> +    /* prefer sockets over cores over threads before 6.2 */
> +    if (mc->smp_prefer_sockets) {

please move the comment into the if, so...

> +        if (sockets == 0) {
> +            cores = cores > 0 ? cores : 1;
> +            threads = threads > 0 ? threads : 1;
> +            sockets = maxcpus / (dies * cores * threads);
> +            sockets = sockets > 0 ? sockets : 1;
> +        } else if (cores == 0) {
> +            threads = threads > 0 ? threads : 1;
> +            cores = maxcpus / (sockets * dies * threads);
> +            cores = cores > 0 ? cores : 1;
> +        } else if (threads == 0) {
> +            threads = maxcpus / (sockets * dies * cores);
> +            threads = threads > 0 ? threads : 1;
> +        }
> +    /* prefer cores over sockets over threads since 6.2 */

...here we can put the comment in the else

> +    } else {
> +        if (cores == 0) {
> +            sockets = sockets > 0 ? sockets : 1;
> +            threads = threads > 0 ? threads : 1;
> +            cores = maxcpus / (sockets * dies * threads);
> +            cores = cores > 0 ? cores : 1;
> +        } else if (sockets == 0) {
> +            threads = threads > 0 ? threads : 1;
> +            sockets = maxcpus / (dies * cores * threads);
> +            sockets = sockets > 0 ? sockets : 1;
> +        } else if (threads == 0) {
> +            threads = maxcpus / (sockets * dies * cores);
> +            threads = threads > 0 ? threads : 1;
> +        }
>      }
>  
>      /* use the computed parameters to calculate the omitted cpus */
> diff --git a/hw/i386/pc_piix.c b/hw/i386/pc_piix.c
> index fd5c2277f2..9b811fc6ca 100644
> --- a/hw/i386/pc_piix.c
> +++ b/hw/i386/pc_piix.c
> @@ -432,6 +432,7 @@ static void pc_i440fx_6_1_machine_options(MachineClass *m)
>      m->is_default = false;
>      compat_props_add(m->compat_props, hw_compat_6_1, hw_compat_6_1_len);
>      compat_props_add(m->compat_props, pc_compat_6_1, pc_compat_6_1_len);
> +    m->smp_prefer_sockets = true;
>  }
>  
>  DEFINE_I440FX_MACHINE(v6_1, "pc-i440fx-6.1", NULL,
> diff --git a/hw/i386/pc_q35.c b/hw/i386/pc_q35.c
> index b45903b15e..88efb7fde4 100644
> --- a/hw/i386/pc_q35.c
> +++ b/hw/i386/pc_q35.c
> @@ -372,6 +372,7 @@ static void pc_q35_6_1_machine_options(MachineClass *m)
>      m->alias = NULL;
>      compat_props_add(m->compat_props, hw_compat_6_1, hw_compat_6_1_len);
>      compat_props_add(m->compat_props, pc_compat_6_1, pc_compat_6_1_len);
> +    m->smp_prefer_sockets = true;
>  }
>  
>  DEFINE_Q35_MACHINE(v6_1, "pc-q35-6.1", NULL,
> diff --git a/hw/ppc/spapr.c b/hw/ppc/spapr.c
> index d39fd4e644..a481fade51 100644
> --- a/hw/ppc/spapr.c
> +++ b/hw/ppc/spapr.c
> @@ -4702,6 +4702,7 @@ static void spapr_machine_6_1_class_options(MachineClass *mc)
>  {
>      spapr_machine_6_2_class_options(mc);
>      compat_props_add(mc->compat_props, hw_compat_6_1, hw_compat_6_1_len);
> +    mc->smp_prefer_sockets = true;
>  }
>  
>  DEFINE_SPAPR_MACHINE(6_1, "6.1", false);
> diff --git a/hw/s390x/s390-virtio-ccw.c b/hw/s390x/s390-virtio-ccw.c
> index 4d25278cf2..b40e647883 100644
> --- a/hw/s390x/s390-virtio-ccw.c
> +++ b/hw/s390x/s390-virtio-ccw.c
> @@ -809,6 +809,7 @@ static void ccw_machine_6_1_class_options(MachineClass *mc)
>  {
>      ccw_machine_6_2_class_options(mc);
>      compat_props_add(mc->compat_props, hw_compat_6_1, hw_compat_6_1_len);
> +    mc->smp_prefer_sockets = true;
>  }
>  DEFINE_CCW_MACHINE(6_1, "6.1", false);
>  
> diff --git a/include/hw/boards.h b/include/hw/boards.h
> index 2832f0f8aa..8df885c9d2 100644
> --- a/include/hw/boards.h
> +++ b/include/hw/boards.h
> @@ -247,6 +247,7 @@ struct MachineClass {
>      bool nvdimm_supported;
>      bool numa_mem_supported;
>      bool smp_dies_supported;
> +    bool smp_prefer_sockets;
>      bool auto_enable_numa;
>      const char *default_ram_id;
>  
> diff --git a/qemu-options.hx b/qemu-options.hx
> index 0c9ddc0274..6ef57e838c 100644
> --- a/qemu-options.hx
> +++ b/qemu-options.hx
> @@ -227,7 +227,9 @@ SRST
>      from those which are given. Historically preference was given to the
>      coarsest topology parameters when computing missing values (ie sockets
>      preferred over cores, which were preferred over threads), however, this
> -    behaviour is considered liable to change.
> +    behaviour is considered liable to change. The historical preference of
> +    sockets over cores over threads works before 6.2, and a new preference
> +    of cores over sockets over threads starts to work since 6.2 on.

Prior to 6.2 the preference was sockets over cores over threads. Since 6.2
the preference is cores over sockets over threads.

>  ERST
>  
>  DEF("numa", HAS_ARG, QEMU_OPTION_numa,
> -- 
> 2.19.1
>

Thanks,
drew 



^ permalink raw reply	[flat|nested] 58+ messages in thread

* Re: [PATCH for-6.2 v2 08/11] machine: Use ms instead of global current_machine in sanity-check
  2021-07-19  3:20 ` [PATCH for-6.2 v2 08/11] machine: Use ms instead of global current_machine in sanity-check Yanan Wang
@ 2021-07-19 17:14   ` Andrew Jones
  0 siblings, 0 replies; 58+ messages in thread
From: Andrew Jones @ 2021-07-19 17:14 UTC (permalink / raw)
  To: Yanan Wang
  Cc: Peter Maydell, Thomas Huth, Daniel P . Berrangé,
	Eduardo Habkost, Michael S . Tsirkin, wanghaibin.wang,
	Richard Henderson, qemu-devel, Greg Kurz, Halil Pasic,
	Igor Mammedov, yuzenghui, Paolo Bonzini, David Gibson

On Mon, Jul 19, 2021 at 11:20:40AM +0800, Yanan Wang wrote:
> In the sanity-check of smp_cpus and max_cpus against mc in function
> machine_set_smp(), we are now using ms->smp.max_cpus for the check
> but using current_machine->smp.max_cpus in the error message.
> Tweak this by uniformly using the local ms.
> 
> Signed-off-by: Yanan Wang <wangyanan55@huawei.com>
> ---
>  hw/core/machine.c | 2 +-
>  1 file changed, 1 insertion(+), 1 deletion(-)
> 
> diff --git a/hw/core/machine.c b/hw/core/machine.c
> index c074425015..f5620c4d34 100644
> --- a/hw/core/machine.c
> +++ b/hw/core/machine.c
> @@ -896,7 +896,7 @@ static void machine_set_smp(Object *obj, Visitor *v, const char *name,
>      } else if (ms->smp.max_cpus > mc->max_cpus) {
>          error_setg(errp, "Invalid SMP CPUs %d. The max CPUs "
>                     "supported by machine '%s' is %d",
> -                   current_machine->smp.max_cpus,
> +                   ms->smp.max_cpus,
>                     mc->name, mc->max_cpus);
>      }
>  
> -- 
> 2.19.1
>

Reviewed-by: Andrew Jones <drjones@redhat.com>



^ permalink raw reply	[flat|nested] 58+ messages in thread

* Re: [PATCH for-6.2 v2 10/11] machine: Split out the smp parsing code
  2021-07-19  3:20 ` [PATCH for-6.2 v2 10/11] machine: Split out the smp parsing code Yanan Wang
@ 2021-07-19 17:20   ` Andrew Jones
  2021-07-22  6:24     ` wangyanan (Y)
  0 siblings, 1 reply; 58+ messages in thread
From: Andrew Jones @ 2021-07-19 17:20 UTC (permalink / raw)
  To: Yanan Wang
  Cc: Peter Maydell, Thomas Huth, Daniel P . Berrangé,
	Eduardo Habkost, Michael S . Tsirkin, wanghaibin.wang,
	Richard Henderson, qemu-devel, Greg Kurz, Halil Pasic,
	Igor Mammedov, yuzenghui, Paolo Bonzini, David Gibson

On Mon, Jul 19, 2021 at 11:20:42AM +0800, Yanan Wang wrote:
> We are going to introduce an unit test for the parser smp_parse()
> in hw/core/machine.c, but now machine.c is only built in softmmu.
> 
> In order to solve the build dependency on the smp parsing code and
> avoid building unrelated stuff for the unit tests, move the related
> code from machine.c into a new common file, i.e., machine-smp.c.
> 
> Signed-off-by: Yanan Wang <wangyanan55@huawei.com>
> ---
>  MAINTAINERS           |   1 +
>  hw/core/machine-smp.c | 124 ++++++++++++++++++++++++++++++++++++++++++
>  hw/core/machine.c     | 109 -------------------------------------
>  hw/core/meson.build   |   1 +
>  include/hw/boards.h   |   1 +
>  5 files changed, 127 insertions(+), 109 deletions(-)
>  create mode 100644 hw/core/machine-smp.c
> 
> diff --git a/MAINTAINERS b/MAINTAINERS
> index 9100f9a043..70633e3bf4 100644
> --- a/MAINTAINERS
> +++ b/MAINTAINERS
> @@ -1626,6 +1626,7 @@ F: cpu.c
>  F: hw/core/cpu.c
>  F: hw/core/machine-qmp-cmds.c
>  F: hw/core/machine.c
> +F: hw/core/machine-smp.c
>  F: hw/core/null-machine.c
>  F: hw/core/numa.c
>  F: hw/cpu/cluster.c
> diff --git a/hw/core/machine-smp.c b/hw/core/machine-smp.c
> new file mode 100644
> index 0000000000..6a00cfe44a
> --- /dev/null
> +++ b/hw/core/machine-smp.c
> @@ -0,0 +1,124 @@
> +/*
> + * QEMU Machine (related to SMP configuration)
> + *
> + * Copyright (C) 2014 Red Hat Inc
> + *
> + * Authors:
> + *   Marcel Apfelbaum <marcel.a@redhat.com>

This header was obviously copy+pasted without being updated.

> + *
> + * This work is licensed under the terms of the GNU GPL, version 2 or later.
> + * See the COPYING file in the top-level directory.
> + */
> +
> +#include "qemu/osdep.h"
> +#include "hw/boards.h"
> +#include "qapi/error.h"
> +
> +/*
> + * smp_parse - Generic function used to parse the given SMP configuration
> + *
> + * The topology parameters must be specified equal to or great than one
> + * or just omitted, explicit configuration like "cpus=0" is not allowed.
> + * The omitted parameters will be calculated based on the provided ones.
> + *
> + * maxcpus will default to the value of cpus if omitted and will be used
> + * to compute the missing sockets/cores/threads. cpus will be calculated
> + * from the computed parametrs if omitted.
> + *
> + * In calculation of omitted arch-netural sockets/cores/threads, we prefer
> + * sockets over cores over threads before 6.2, while prefer cores over
> + * sockets over threads since 6.2 on. The arch-specific dies will directly
> + * default to 1 if omitted.
> + */
> +void smp_parse(MachineState *ms, SMPConfiguration *config, Error **errp)
> +{
> +    MachineClass *mc = MACHINE_GET_CLASS(ms);
> +    unsigned cpus    = config->has_cpus ? config->cpus : 0;
> +    unsigned sockets = config->has_sockets ? config->sockets : 0;
> +    unsigned dies    = config->has_dies ? config->dies : 1;
> +    unsigned cores   = config->has_cores ? config->cores : 0;
> +    unsigned threads = config->has_threads ? config->threads : 0;
> +    unsigned maxcpus = config->has_maxcpus ? config->maxcpus : 0;
> +
> +    if ((config->has_cpus && config->cpus == 0) ||
> +        (config->has_sockets && config->sockets == 0) ||
> +        (config->has_dies && config->dies == 0) ||
> +        (config->has_cores && config->cores == 0) ||
> +        (config->has_threads && config->threads == 0) ||
> +        (config->has_maxcpus && config->maxcpus == 0)) {
> +        error_setg(errp, "parameters must be equal to or greater than one"
> +                   "if provided");
> +        return;
> +    }
> +
> +    if (!mc->smp_dies_supported && dies > 1) {
> +        error_setg(errp, "dies not supported by this machine's CPU topology");
> +        return;
> +    }
> +
> +    maxcpus = maxcpus > 0 ? maxcpus : cpus;
> +
> +    /* prefer sockets over cores over threads before 6.2 */
> +    if (mc->smp_prefer_sockets) {
> +        if (sockets == 0) {
> +            cores = cores > 0 ? cores : 1;
> +            threads = threads > 0 ? threads : 1;
> +            sockets = maxcpus / (dies * cores * threads);
> +            sockets = sockets > 0 ? sockets : 1;
> +        } else if (cores == 0) {
> +            threads = threads > 0 ? threads : 1;
> +            cores = maxcpus / (sockets * dies * threads);
> +            cores = cores > 0 ? cores : 1;
> +        } else if (threads == 0) {
> +            threads = maxcpus / (sockets * dies * cores);
> +            threads = threads > 0 ? threads : 1;
> +        }
> +    /* prefer cores over sockets over threads since 6.2 */
> +    } else {
> +        if (cores == 0) {
> +            sockets = sockets > 0 ? sockets : 1;
> +            threads = threads > 0 ? threads : 1;
> +            cores = maxcpus / (sockets * dies * threads);
> +            cores = cores > 0 ? cores : 1;
> +        } else if (sockets == 0) {
> +            threads = threads > 0 ? threads : 1;
> +            sockets = maxcpus / (dies * cores * threads);
> +            sockets = sockets > 0 ? sockets : 1;
> +        } else if (threads == 0) {
> +            threads = maxcpus / (sockets * dies * cores);
> +            threads = threads > 0 ? threads : 1;
> +        }
> +    }
> +
> +    /* use the computed parameters to calculate the omitted cpus */
> +    cpus = cpus > 0 ? cpus : sockets * dies * cores * threads;
> +    maxcpus = maxcpus > 0 ? maxcpus : cpus;
> +
> +    if (sockets * dies * cores * threads != maxcpus) {
> +        g_autofree char *dies_msg = g_strdup_printf(
> +            mc->smp_dies_supported ? " * dies (%u)" : "", dies);
> +        error_setg(errp, "Invalid CPU topology: "
> +                   "sockets (%u)%s * cores (%u) * threads (%u) "
> +                   "!= maxcpus (%u)",
> +                   sockets, dies_msg, cores, threads,
> +                   maxcpus);
> +        return;
> +    }
> +
> +    if (sockets * dies * cores * threads < cpus) {
> +        g_autofree char *dies_msg = g_strdup_printf(
> +            mc->smp_dies_supported ? " * dies (%u)" : "", dies);
> +        error_setg(errp, "Invalid CPU topology: "
> +                   "sockets (%u)%s * cores (%u) * threads (%u) < "
> +                   "smp_cpus (%u)",
> +                   sockets, dies_msg, cores, threads, cpus);
> +        return;
> +    }
> +
> +    ms->smp.cpus = cpus;
> +    ms->smp.sockets = sockets;
> +    ms->smp.dies = dies;
> +    ms->smp.cores = cores;
> +    ms->smp.threads = threads;
> +    ms->smp.max_cpus = maxcpus;
> +}
> diff --git a/hw/core/machine.c b/hw/core/machine.c
> index 9d24b67ef3..61be266b6c 100644
> --- a/hw/core/machine.c
> +++ b/hw/core/machine.c
> @@ -744,115 +744,6 @@ void machine_set_cpu_numa_node(MachineState *machine,
>      }
>  }
>  
> -/*
> - * smp_parse - Generic function used to parse the given SMP configuration
> - *
> - * The topology parameters must be specified equal to or great than one
> - * or just omitted, explicit configuration like "cpus=0" is not allowed.
> - * The omitted parameters will be calculated based on the provided ones.
> - *
> - * maxcpus will default to the value of cpus if omitted and will be used
> - * to compute the missing sockets/cores/threads. cpus will be calculated
> - * from the computed parametrs if omitted.
> - *
> - * In calculation of omitted arch-netural sockets/cores/threads, we prefer
> - * sockets over cores over threads before 6.2, while prefer cores over
> - * sockets over threads since 6.2 on. The arch-specific dies will directly
> - * default to 1 if omitted.
> - */
> -static void smp_parse(MachineState *ms, SMPConfiguration *config, Error **errp)
> -{
> -    MachineClass *mc = MACHINE_GET_CLASS(ms);
> -    unsigned cpus    = config->has_cpus ? config->cpus : 0;
> -    unsigned sockets = config->has_sockets ? config->sockets : 0;
> -    unsigned dies    = config->has_dies ? config->dies : 1;
> -    unsigned cores   = config->has_cores ? config->cores : 0;
> -    unsigned threads = config->has_threads ? config->threads : 0;
> -    unsigned maxcpus = config->has_maxcpus ? config->maxcpus : 0;
> -
> -    if ((config->has_cpus && config->cpus == 0) ||
> -        (config->has_sockets && config->sockets == 0) ||
> -        (config->has_dies && config->dies == 0) ||
> -        (config->has_cores && config->cores == 0) ||
> -        (config->has_threads && config->threads == 0) ||
> -        (config->has_maxcpus && config->maxcpus == 0)) {
> -        error_setg(errp, "parameters must be equal to or greater than one"
> -                   "if provided");
> -        return;
> -    }
> -
> -    if (!mc->smp_dies_supported && dies > 1) {
> -        error_setg(errp, "dies not supported by this machine's CPU topology");
> -        return;
> -    }
> -
> -    maxcpus = maxcpus > 0 ? maxcpus : cpus;
> -
> -    /* prefer sockets over cores over threads before 6.2 */
> -    if (mc->smp_prefer_sockets) {
> -        if (sockets == 0) {
> -            cores = cores > 0 ? cores : 1;
> -            threads = threads > 0 ? threads : 1;
> -            sockets = maxcpus / (dies * cores * threads);
> -            sockets = sockets > 0 ? sockets : 1;
> -        } else if (cores == 0) {
> -            threads = threads > 0 ? threads : 1;
> -            cores = maxcpus / (sockets * dies * threads);
> -            cores = cores > 0 ? cores : 1;
> -        } else if (threads == 0) {
> -            threads = maxcpus / (sockets * dies * cores);
> -            threads = threads > 0 ? threads : 1;
> -        }
> -    /* prefer cores over sockets over threads since 6.2 */
> -    } else {
> -        if (cores == 0) {
> -            sockets = sockets > 0 ? sockets : 1;
> -            threads = threads > 0 ? threads : 1;
> -            cores = maxcpus / (sockets * dies * threads);
> -            cores = cores > 0 ? cores : 1;
> -        } else if (sockets == 0) {
> -            threads = threads > 0 ? threads : 1;
> -            sockets = maxcpus / (dies * cores * threads);
> -            sockets = sockets > 0 ? sockets : 1;
> -        } else if (threads == 0) {
> -            threads = maxcpus / (sockets * dies * cores);
> -            threads = threads > 0 ? threads : 1;
> -        }
> -    }
> -
> -    /* use the computed parameters to calculate the omitted cpus */
> -    cpus = cpus > 0 ? cpus : sockets * dies * cores * threads;
> -    maxcpus = maxcpus > 0 ? maxcpus : cpus;
> -
> -    if (sockets * dies * cores * threads != maxcpus) {
> -        g_autofree char *dies_msg = g_strdup_printf(
> -            mc->smp_dies_supported ? " * dies (%u)" : "", dies);
> -        error_setg(errp, "Invalid CPU topology: "
> -                   "sockets (%u)%s * cores (%u) * threads (%u) "
> -                   "!= maxcpus (%u)",
> -                   sockets, dies_msg, cores, threads,
> -                   maxcpus);
> -        return;
> -    }
> -
> -    if (sockets * dies * cores * threads < cpus) {
> -        g_autofree char *dies_msg = g_strdup_printf(
> -            mc->smp_dies_supported ? " * dies (%u)" : "", dies);
> -        error_setg(errp, "Invalid CPU topology: "
> -                   "sockets (%u)%s * cores (%u) * threads (%u) < "
> -                   "smp_cpus (%u)",
> -                   sockets, dies_msg, cores, threads, cpus);
> -        return;
> -    }
> -
> -    ms->smp.cpus = cpus;
> -    ms->smp.sockets = sockets;
> -    ms->smp.dies = dies;
> -    ms->smp.cores = cores;
> -    ms->smp.threads = threads;
> -    ms->smp.max_cpus = maxcpus;
> -}
> -
>  static void machine_get_smp(Object *obj, Visitor *v, const char *name,
>                              void *opaque, Error **errp)
>  {
> diff --git a/hw/core/meson.build b/hw/core/meson.build
> index 18f44fb7c2..6d727c7742 100644
> --- a/hw/core/meson.build
> +++ b/hw/core/meson.build
> @@ -14,6 +14,7 @@ hwcore_files = files(
>  )
>  
>  common_ss.add(files('cpu-common.c'))
> +common_ss.add(files('machine-smp.c'))
>  common_ss.add(when: 'CONFIG_FITLOADER', if_true: files('loader-fit.c'))
>  common_ss.add(when: 'CONFIG_GENERIC_LOADER', if_true: files('generic-loader.c'))
>  common_ss.add(when: ['CONFIG_GUEST_LOADER', fdt], if_true: files('guest-loader.c'))
> diff --git a/include/hw/boards.h b/include/hw/boards.h
> index 12ab0f5968..071eec1e74 100644
> --- a/include/hw/boards.h
> +++ b/include/hw/boards.h
> @@ -34,6 +34,7 @@ HotpluggableCPUList *machine_query_hotpluggable_cpus(MachineState *machine);
>  void machine_set_cpu_numa_node(MachineState *machine,
>                                 const CpuInstanceProperties *props,
>                                 Error **errp);
> +void smp_parse(MachineState *ms, SMPConfiguration *config, Error **errp);
>  
>  /**
>   * machine_class_allow_dynamic_sysbus_dev: Add type to list of valid devices
> -- 
> 2.19.1
>

Otherwise

Reviewed-by: Andrew Jones <drjones@redhat.com>



^ permalink raw reply	[flat|nested] 58+ messages in thread

* Re: [PATCH for-6.2 v2 11/11] tests/unit: Add a unit test for smp parsing
  2021-07-19  3:20 ` [PATCH for-6.2 v2 11/11] tests/unit: Add a unit test for smp parsing Yanan Wang
@ 2021-07-19 18:57   ` Andrew Jones
  2021-07-22  6:15     ` wangyanan (Y)
  0 siblings, 1 reply; 58+ messages in thread
From: Andrew Jones @ 2021-07-19 18:57 UTC (permalink / raw)
  To: Yanan Wang
  Cc: Peter Maydell, Thomas Huth, Daniel P . Berrangé,
	Eduardo Habkost, Michael S . Tsirkin, wanghaibin.wang,
	Richard Henderson, qemu-devel, Greg Kurz, Halil Pasic,
	Igor Mammedov, yuzenghui, Paolo Bonzini, David Gibson

On Mon, Jul 19, 2021 at 11:20:43AM +0800, Yanan Wang wrote:
> Add a QEMU unit test for the parsing of given SMP configuration.
> Since all the parsing logic is in generic function smp_parse(),
> this test passes diffenent SMP configurations to the function
> and compare the parsing result with what is expected.
> 
> In the test, all possible collections of the topology parameters
> and the corressponding expected results are listed, including the
> valid and invalid ones.
> 
> The preference of sockets over cores and the preference of cores
> over sockets, and the support of multi-dies are also considered.
> 
> Signed-off-by: Yanan Wang <wangyanan55@huawei.com>
> ---
>  MAINTAINERS                 |    1 +
>  tests/unit/meson.build      |    1 +
>  tests/unit/test-smp-parse.c | 1117 +++++++++++++++++++++++++++++++++++
>  3 files changed, 1119 insertions(+)
>  create mode 100644 tests/unit/test-smp-parse.c
> 
> diff --git a/MAINTAINERS b/MAINTAINERS
> index 70633e3bf4..160dba2e57 100644
> --- a/MAINTAINERS
> +++ b/MAINTAINERS
> @@ -1636,6 +1636,7 @@ F: include/hw/boards.h
>  F: include/hw/core/cpu.h
>  F: include/hw/cpu/cluster.h
>  F: include/sysemu/numa.h
> +F: tests/unit/test-smp-parse.c
>  T: git https://gitlab.com/ehabkost/qemu.git machine-next
>  
>  Xtensa Machines
> diff --git a/tests/unit/meson.build b/tests/unit/meson.build
> index 3e0504dd21..694a924627 100644
> --- a/tests/unit/meson.build
> +++ b/tests/unit/meson.build
> @@ -44,6 +44,7 @@ tests = {
>    'test-uuid': [],
>    'ptimer-test': ['ptimer-test-stubs.c', meson.source_root() / 'hw/core/ptimer.c'],
>    'test-qapi-util': [],
> +  'test-smp-parse': [qom, meson.source_root() / 'hw/core/machine-smp.c'],
>  }
>  
>  if have_system or have_tools
> diff --git a/tests/unit/test-smp-parse.c b/tests/unit/test-smp-parse.c
> new file mode 100644
> index 0000000000..bc1d324c3d
> --- /dev/null
> +++ b/tests/unit/test-smp-parse.c
> @@ -0,0 +1,1117 @@
> +/*
> + * SMP parsing unit-tests
> + *
> + * Copyright (C) 2021, Huawei, Inc.
> + *
> + * Authors:
> + *  Yanan Wang <wangyanan55@huawei.com>
> + *
> + * This work is licensed under the terms of the GNU LGPL, version 2.1 or later.
> + * See the COPYING.LIB file in the top-level directory.
> + */
> +
> +#include "qemu/osdep.h"
> +#include "qom/object.h"
> +#include "qemu/module.h"
> +#include "qapi/error.h"
> +
> +#include "hw/boards.h"
> +
> +#define T true
> +#define F false
> +
> +/**
> + * SMPTestData:
> + * @config - the given SMP configuration for parsing
> + * @should_be_valid - whether the given configuration is supposed to be valid
> + * @expect - the CPU topology info expected to be parsed out
> + */
> +typedef struct SMPTestData {
> +    SMPConfiguration config;
> +    bool should_be_valid;

Long way to say 'valid'.

> +    CpuTopology expect;
> +} SMPTestData;
> +
> +/* the specific machine type info for this test */
> +static const TypeInfo smp_machine_info = {
> +    .name = TYPE_MACHINE,
> +    .parent = TYPE_OBJECT,
> +    .class_size = sizeof(MachineClass),
> +    .instance_size = sizeof(MachineState),
> +};
> +
> +/*
> + * prefer sockets over cores over threads before 6.2.
> + * all possible SMP configurations and the corressponding expected outputs

corresponding (please run spell check on your commit messages)


> + * are listed for testing, including the valid and invalid ones.
> + */
> +static struct SMPTestData prefer_sockets[] = {
> +    {
> +        /* config: no smp configuration provided
> +         * expect: cpus=1,sockets=1,dies=1,cores=1,threads=1,maxcpus=1 */
> +        .config = (SMPConfiguration) { F, 0, F, 0, F, 0, F, 0, F, 0, F, 0 },

SMPConfiguration and CpuTopology have named fields so we could drop the
'expect: ...' comment line and instead do

 {
  /* no configuration provided */ 
  .config = { .has_cpus = F, .has_maxcpus = F, ... },
  .valid = T,
  .expect = { .sockets = 1, .cores = 1, ... },
 }, {
  ...
 }

which may be easier to maintain. OTOH, the concise form this approach has
is also nice. I don't think you should need the casts in the assignments
though.

> +        .should_be_valid = true,
> +        .expect = (CpuTopology) { 1, 1, 1, 1, 1, 1 },
> +    }, {
> +        /* config: -smp 8
> +         * expect: cpus=8,sockets=8,dies=1,cores=1,threads=1,maxcpus=8 */
> +        .config = (SMPConfiguration) { T, 8, F, 0, F, 0, F, 0, F, 0, F, 0 },
> +        .should_be_valid = true,
> +        .expect = (CpuTopology) { 8, 8, 1, 1, 1, 8 },
> +    }, {
> +        /* config: -smp sockets=2
> +         * expect: cpus=2,sockets=2,dies=1,cores=1,threads=1,maxcpus=2 */
> +        .config = (SMPConfiguration) { F, 0, T, 2, F, 0, F, 0, F, 0, F, 0 },
> +        .should_be_valid = true,
> +        .expect = (CpuTopology) { 2, 2, 1, 1, 1, 2 },
> +    }, {
> +        /* config: -smp cores=4
> +         * expect: cpus=4,sockets=1,dies=1,cores=4,threads=1,maxcpus=4 */
> +        .config = (SMPConfiguration) { F, 0, F, 0, F, 0, T, 4, F, 0, F, 0 },
> +        .should_be_valid = true,
> +        .expect = (CpuTopology) { 4, 1, 1, 4, 1, 4 },
> +    }, {
> +        /* config: -smp threads=2
> +         * expect: cpus=2,sockets=1,dies=1,cores=1,threads=2,maxcpus=2 */
> +        .config = (SMPConfiguration) { F, 0, F, 0, F, 0, F, 0, T, 2, F, 0 },
> +        .should_be_valid = true,
> +        .expect = (CpuTopology) { 2, 1, 1, 1, 2, 2 },
> +    }, {
> +        /* config: -smp maxcpus=16
> +         * expect: cpus=16,sockets=16,dies=1,cores=1,threads=1,maxcpus=16 */
> +        .config = (SMPConfiguration) { F, 0, F, 0, F, 0, F, 0, F, 0, T, 16 },
> +        .should_be_valid = true,
> +        .expect = (CpuTopology) { 16, 16, 1, 1, 1, 16 },
> +    }, {
> +        /* config: -smp 8,sockets=2
> +         * expect: cpus=8,sockets=2,dies=1,cores=4,threads=1,maxcpus=8 */
> +        .config = (SMPConfiguration) { T, 8, T, 2, F, 0, F, 0, F, 0, F, 0 },
> +        .should_be_valid = true,
> +        .expect = (CpuTopology) { 8, 2, 1, 4, 1, 8 },
> +    }, {
> +        /* config: -smp 8,cores=4
> +         * expect: cpus=8,sockets=2,dies=1,cores=4,threads=1,maxcpus=8 */
> +        .config = (SMPConfiguration) { T, 8, F, 0, F, 0, T, 4, F, 0, F, 0 },
> +        .should_be_valid = true,
> +        .expect = (CpuTopology) { 8, 2, 1, 4, 1, 8 },
> +    }, {
> +        /* config: -smp 8,threads=2
> +         * expect: cpus=8,sockets=4,dies=1,cores=1,threads=2,maxcpus=8 */
> +        .config = (SMPConfiguration) { T, 8, F, 0, F, 0, F, 0, T, 2, F, 0 },
> +        .should_be_valid = true,
> +        .expect = (CpuTopology) { 8, 4, 1, 1, 2, 8 },
> +    }, {
> +        /* config: -smp 8,maxcpus=16
> +         * expect: cpus=8,sockets=16,dies=1,cores=1,threads=1,maxcpus=16 */
> +        .config = (SMPConfiguration) { T, 8, F, 0, F, 0, F, 0, F, 0, T, 16 },
> +        .should_be_valid = true,
> +        .expect = (CpuTopology) { 8, 16, 1, 1, 1, 16 },
> +    }, {
> +        /* config: -smp sockets=2,cores=4
> +         * expect: cpus=8,sockets=2,dies=1,cores=4,threads=1,maxcpus=8 */
> +        .config = (SMPConfiguration) { F, 0, T, 2, F, 0, T, 4, F, 0, F, 0 },
> +        .should_be_valid = true,
> +        .expect = (CpuTopology) { 8, 2, 1, 4, 1, 8 },
> +    }, {
> +        /* config: -smp sockets=2,threads=2
> +         * expect: cpus=4,sockets=2,dies=1,cores=1,threads=2,maxcpus=4 */
> +        .config = (SMPConfiguration) { F, 0, T, 2, F, 0, F, 0, T, 2, F, 0 },
> +        .should_be_valid = true,
> +        .expect = (CpuTopology) { 4, 2, 1, 1, 2, 4 },
> +    }, {
> +        /* config: -smp sockets=2,maxcpus=16
> +         * expect: cpus=16,sockets=2,dies=1,cores=8,threads=1,maxcpus=16 */
> +        .config = (SMPConfiguration) { F, 0, T, 2, F, 0, F, 0, F, 0, T, 16 },
> +        .should_be_valid = true,
> +        .expect = (CpuTopology) { 16, 2, 1, 8, 1, 16 },
> +    }, {
> +        /* config: -smp cores=4,threads=2
> +         * expect: cpus=8,sockets=1,dies=1,cores=4,threads=2,maxcpus=8 */
> +        .config = (SMPConfiguration) { F, 0, F, 0, F, 0, T, 4, T, 2, F, 0 },
> +        .should_be_valid = true,
> +        .expect = (CpuTopology) { 8, 1, 1, 4, 2, 8 },
> +    }, {
> +        /* config: -smp cores=4,maxcpus=16
> +         * expect: cpus=16,sockets=4,dies=1,cores=4,threads=1,maxcpus=16 */
> +        .config = (SMPConfiguration) { F, 0, F, 0, F, 0, T, 4, F, 0, T, 16 },
> +        .should_be_valid = true,
> +        .expect = (CpuTopology) { 16, 4, 1, 4, 1, 16 },
> +    }, {
> +        /* config: -smp threads=2,maxcpus=16
> +         * expect: cpus=16,sockets=8,dies=1,cores=1,threads=2,maxcpus=16 */
> +        .config = (SMPConfiguration) { F, 0, F, 0, F, 0, F, 0, T, 2, T, 16 },
> +        .should_be_valid = true,
> +        .expect = (CpuTopology) { 16, 8, 1, 1, 2, 16 },
> +    }, {
> +        /* config: -smp 8,sockets=2,cores=4
> +         * expect: cpus=8,sockets=2,dies=1,cores=4,threads=1,maxcpus=8 */
> +        .config = (SMPConfiguration) { T, 8, T, 2, F, 0, T, 4, F, 0, F, 0 },
> +        .should_be_valid = true,
> +        .expect = (CpuTopology) { 8, 2, 1, 4, 1, 8 },
> +    }, {
> +        /* config: -smp 8,sockets=2,threads=2
> +         * expect: cpus=8,sockets=2,dies=1,cores=2,threads=2,maxcpus=8 */
> +        .config = (SMPConfiguration) { T, 8, T, 2, F, 0, F, 0, T, 2, F, 0 },
> +        .should_be_valid = true,
> +        .expect = (CpuTopology) { 8, 2, 1, 2, 2, 8 },
> +    }, {
> +        /* config: -smp 8,sockets=2,maxcpus=16
> +         * expect: cpus=8,sockets=2,dies=1,cores=8,threads=1,maxcpus=16 */
> +        .config = (SMPConfiguration) { T, 8, T, 2, F, 0, F, 0, F, 0, T, 16 },
> +        .should_be_valid = true,
> +        .expect = (CpuTopology) { 8, 2, 1, 8, 1, 16 },
> +    }, {
> +        /* config: -smp 8,cores=4,threads=2
> +         * expect: cpus=8,sockets=1,dies=1,cores=4,threads=2,maxcpus=8 */
> +        .config = (SMPConfiguration) { T, 8, F, 0, F, 0, T, 4, T, 2, F, 0 },
> +        .should_be_valid = true,
> +        .expect = (CpuTopology) { 8, 1, 1, 4, 2, 8 },
> +    }, {
> +        /* config: -smp 8,cores=4,maxcpus=16
> +         * expect: cpus=8,sockets=4,dies=1,cores=4,threads=1,maxcpus=16 */
> +        .config = (SMPConfiguration) { T, 8, F, 0, F, 0, T, 4, F, 0, T, 16 },
> +        .should_be_valid = true,
> +        .expect = (CpuTopology) { 8, 4, 1, 4, 1, 16 },
> +    }, {
> +        /* config: -smp 8,threads=2,maxcpus=16
> +         * expect: cpus=8,sockets=8,dies=1,cores=1,threads=2,maxcpus=16 */
> +        .config = (SMPConfiguration) { T, 8, F, 0, F, 0, F, 0, T, 2, T, 16 },
> +        .should_be_valid = true,
> +        .expect = (CpuTopology) { 8, 8, 1, 1, 2, 16 },
> +    }, {
> +        /* config: -smp sockets=2,cores=4,threads=2
> +         * expect: cpus=16,sockets=2,dies=1,cores=4,threads=2,maxcpus=16 */
> +        .config = (SMPConfiguration) { F, 0, T, 2, F, 0, T, 4, T, 2, F, 0 },
> +        .should_be_valid = true,
> +        .expect = (CpuTopology) { 16, 2, 1, 4, 2, 16 },
> +    }, {
> +        /* config: -smp sockets=2,cores=4,maxcpus=16
> +         * expect: cpus=16,sockets=2,dies=1,cores=4,threads=2,maxcpus=16 */
> +        .config = (SMPConfiguration) { F, 0, T, 2, F, 0, T, 4, F, 0, T, 16 },
> +        .should_be_valid = true,
> +        .expect = (CpuTopology) { 16, 2, 1, 4, 2, 16 },
> +    }, {
> +        /* config: -smp sockets=2,threads=2,maxcpus=16
> +         * expect: cpus=16,sockets=2,dies=1,cores=4,threads=2,maxcpus=16 */
> +        .config = (SMPConfiguration) { F, 0, T, 2, F, 0, F, 0, T, 2, T, 16 },
> +        .should_be_valid = true,
> +        .expect = (CpuTopology) { 16, 2, 1, 4, 2, 16 },
> +    }, {
> +        /* config: -smp cores=4,threads=2,maxcpus=16
> +         * expect: cpus=16,sockets=2,dies=1,cores=4,threads=2,maxcpus=16 */
> +        .config = (SMPConfiguration) { F, 0, F, 0, F, 0, T, 4, T, 2, T, 16 },
> +        .should_be_valid = true,
> +        .expect = (CpuTopology) { 16, 2, 1, 4, 2, 16 },
> +    }, {
> +        /* config: -smp 8,sockets=2,cores=4,threads=1
> +         * expect: cpus=8,sockets=2,dies=1,cores=4,threads=1,maxcpus=8 */
> +        .config = (SMPConfiguration) { T, 8, T, 2, F, 0, T, 4, T, 1, F, 0 },
> +        .should_be_valid = true,
> +        .expect = (CpuTopology) { 8, 2, 1, 4, 1, 8 },
> +    }, {
> +        /* config: -smp 8,sockets=2,cores=4,maxcpus=16
> +         * expect: cpus=8,sockets=2,dies=1,cores=4,threads=2,maxcpus=16 */
> +        .config = (SMPConfiguration) { T, 8, T, 2, F, 0, T, 4, F, 0, T, 16 },
> +        .should_be_valid = true,
> +        .expect = (CpuTopology) { 8, 2, 1, 4, 2, 16 },
> +    }, {
> +        /* config: -smp 8,sockets=2,threads=2,maxcpus=16
> +         * expect: cpus=8,sockets=2,dies=1,cores=4,threads=2,maxcpus=16 */
> +        .config = (SMPConfiguration) { T, 8, T, 2, F, 0, F, 0, T, 2, T, 16 },
> +        .should_be_valid = true,
> +        .expect = (CpuTopology) { 8, 2, 1, 4, 2, 16 },
> +    }, {
> +        /* config: -smp 8,cores=4,threads=2,maxcpus=16
> +         * expect: cpus=8,sockets=2,dies=1,cores=4,threads=2,maxcpus=16 */
> +        .config = (SMPConfiguration) { T, 8, F, 0, F, 0, T, 4, T, 2, T, 16 },
> +        .should_be_valid = true,
> +        .expect = (CpuTopology) { 8, 2, 1, 4, 2, 16 },
> +    }, {
> +        /* config: -smp sockets=2,cores=4,threads=2,maxcpus=16
> +         * expect: -smp 16,sockets=2,dies=1,cores=4,threads=2,maxcpus=16 */
> +        .config = (SMPConfiguration) { F, 0, T, 2, F, 0, T, 4, T, 2, T, 16 },
> +        .should_be_valid = true,
> +        .expect = (CpuTopology) { 16, 2, 1, 4, 2, 16 },
> +    }, {
> +        /* config: -smp 8,sockets=2,cores=4,threads=2,maxcpus=16
> +         * expect: cpus=8,sockets=2,dies=1,cores=4,threads=2,maxcpus=16 */
> +        .config = (SMPConfiguration) { T, 8, T, 2, F, 0, T, 4, T, 2, T, 16 },
> +        .should_be_valid = true,
> +        .expect = (CpuTopology) { 8, 2, 1, 4, 2, 16 },
> +    }, {
> +        /* config: -smp 8,sockets=2,dies=1,cores=4,threads=2,maxcpus=16
> +         * expect: cpus=8,sockets=2,dies=1,cores=4,threads=2,maxcpus=16 */
> +        .config = (SMPConfiguration) { T, 8, T, 2, T, 1, T, 4, T, 2, T, 16 },
> +        .should_be_valid = true,
> +        .expect = (CpuTopology) { 8, 2, 1, 4, 2, 16 },
> +    }, {
> +        /* config: -smp 0
> +         * expect: error, "anything=0" is not allowed */
> +        .config = (SMPConfiguration) { T, 0, F, 0, F, 0, F, 0, F, 0, F, 0 },
> +        .should_be_valid = false,
> +    }, {
> +        /* config: -smp 8,sockets=0
> +         * expect: error, "anything=0" is not allowed */
> +        .config = (SMPConfiguration) { T, 8, T, 0, F, 0, F, 0, F, 0, F, 0 },
> +        .should_be_valid = false,
> +    }, {
> +        /* config: -smp 8,sockets=2,dies=0
> +         * expect: error, "anything=0" is not allowed */
> +        .config = (SMPConfiguration) { T, 0, T, 2, T, 0, F, 0, F, 0, F, 0 },
> +        .should_be_valid = false,
> +    }, {
> +        /* config: -smp 8,sockets=2,dies=1,cores=0
> +         * expect: error, "anything=0" is not allowed */
> +        .config = (SMPConfiguration) { T, 8, T, 2, T, 1, T, 0, F, 0, F, 0 },
> +        .should_be_valid = false,
> +    }, {
> +        /* config: -smp 8,sockets=2,dies=1,cores=4,threads=0
> +         * expect: error, "anything=0" is not allowed */
> +        .config = (SMPConfiguration) { T, 8, T, 2, T, 1, T, 4, T, 0, F, 0 },
> +        .should_be_valid = false,
> +    }, {
> +        /* config: -smp 8,sockets=2,dies=1,cores=4,threads=2,maxcpus=0
> +         * expect: error, "anything=0" is not allowed */
> +        .config = (SMPConfiguration) { T, 8, T, 2, T, 1, T, 4, T, 2, T, 0 },
> +        .should_be_valid = false,
> +    }, {
> +        /* config: -smp 8,dies=2
> +         * expect: error, multi-dies not supported */
> +        .config = (SMPConfiguration) { T, 8, F, 0, T, 2, F, 0, F, 0, F, 0 },
> +        .should_be_valid = false,
> +    }, {
> +        /* config: -smp 8,sockets=2,cores=8
> +         * expect: error, sum (16) != max_cpus (8) */
> +        .config = (SMPConfiguration) { T, 8, T, 2, F, 0, T, 4, T, 2, F, 0 },
> +        .should_be_valid = false,
> +    }, {
> +        /* config: -smp 8,sockets=2,cores=5,threads=2,maxcpus=16
> +         * expect: error, sum (20) != max_cpus (16) */
> +        .config = (SMPConfiguration) { F, 0, T, 3, F, 0, T, 5, T, 1, T, 16 },
> +        .should_be_valid = false,
> +    }, {
> +        /* config: -smp 16,maxcpus=12
> +         * expect: error, sum (12) < smp_cpus (16) */
> +        .config = (SMPConfiguration) { T, 16, F, 0, F, 0, F, 0, F, 0, T, 12 },
> +        .should_be_valid = false,
> +    },
> +};
> +
> +static struct SMPTestData prefer_sockets_support_dies[] = {
> +    {
> +        /* config: -smp dies=2
> +         * expect: cpus=2,sockets=1,dies=2,cores=1,threads=1,maxcpus=2 */
> +        .config = (SMPConfiguration) { F, 0, F, 0, T, 2, F, 0, F, 0, F, 0 },
> +        .should_be_valid = true,
> +        .expect = (CpuTopology) { 2, 1, 2, 1, 1, 2 },
> +    }, {
> +        /* config: -smp 16,dies=2
> +         * expect: cpus=16,sockets=8,dies=2,cores=1,threads=1,maxcpus=16 */
> +        .config = (SMPConfiguration) { T, 16, F, 0, T, 2, F, 0, F, 0, F, 0 },
> +        .should_be_valid = true,
> +        .expect = (CpuTopology) { 16, 8, 2, 1, 1, 16 },
> +    }, {
> +        /* config: -smp sockets=2,dies=2
> +         * expect: cpus=4,sockets=2,dies=2,cores=1,threads=1,maxcpus=4 */
> +        .config = (SMPConfiguration) { F, 0, T, 2, T, 2, F, 0, F, 0, F, 0 },
> +        .should_be_valid = true,
> +        .expect = (CpuTopology) { 4, 2, 2, 1, 1, 4 },
> +    }, {
> +        /* config: -smp dies=2,cores=4
> +         * expect: cpus=8,sockets=1,dies=2,cores=4,threads=1,maxcpus=8 */
> +        .config = (SMPConfiguration) { F, 0, F, 0, T, 2, T, 4, F, 0, F, 0 },
> +        .should_be_valid = true,
> +        .expect = (CpuTopology) { 8, 1, 2, 4, 1, 8 },
> +    }, {
> +        /* config: -smp dies=2,threads=2
> +         * expect: cpus=4,sockets=1,dies=2,cores=1,threads=2,maxcpus=4 */
> +        .config = (SMPConfiguration) { F, 0, F, 0, T, 2, F, 0, T, 2, F, 0 },
> +        .should_be_valid = true,
> +        .expect = (CpuTopology) { 4, 1, 2, 1, 2, 4 },
> +    }, {
> +        /* config: -smp dies=2,maxcpus=32
> +         * expect: cpus=32,sockets=16,dies=2,cores=1,threads=1,maxcpus=32 */
> +        .config = (SMPConfiguration) { F, 0, F, 0, T, 2, F, 0, F, 0, T, 32 },
> +        .should_be_valid = true,
> +        .expect = (CpuTopology) { 32, 16, 2, 1, 1, 32 },
> +    }, {
> +        /* config: -smp 16,sockets=2,dies=2
> +         * expect: cpus=16,sockets=2,dies=2,cores=4,threads=1,maxcpus=16 */
> +        .config = (SMPConfiguration) { T, 16, T, 2, T, 2, F, 0, F, 0, F, 0 },
> +        .should_be_valid = true,
> +        .expect = (CpuTopology) { 16, 2, 2, 4, 1, 16 },
> +    }, {
> +        /* config: -smp 16,dies=2,cores=4
> +         * expect: cpus=16,sockets=2,dies=2,cores=4,threads=1,maxcpus=16 */
> +        .config = (SMPConfiguration) { T, 16, F, 0, T, 2, T, 4, F, 0, F, 0 },
> +        .should_be_valid = true,
> +        .expect = (CpuTopology) { 16, 2, 2, 4, 1, 16 },
> +    }, {
> +        /* config: -smp 16,dies=2,threads=2
> +         * expect: cpus=16,sockets=4,dies=2,cores=1,threads=2,maxcpus=16 */
> +        .config = (SMPConfiguration) { T, 16, F, 0, T, 2, F, 0, T, 2, F, 0 },
> +        .should_be_valid = true,
> +        .expect = (CpuTopology) { 16, 4, 2, 1, 2, 16 },
> +    }, {
> +        /* config: -smp 16,dies=2,maxcpus=32
> +         * expect: cpus=16,sockets=16,dies=2,cores=1,threads=1,maxcpus=32 */
> +        .config = (SMPConfiguration) { T, 16, F, 0, T, 2, F, 0, F, 0, T, 32 },
> +        .should_be_valid = true,
> +        .expect = (CpuTopology) { 16, 16, 2, 1, 1, 32 },
> +    }, {
> +        /* config: -smp sockets=2,dies=2,cores=4
> +         * expect: cpus=16,sockets=2,dies=2,cores=4,threads=1,maxcpus=16 */
> +        .config = (SMPConfiguration) { F, 0, T, 2, T, 2, T, 4, F, 0, F, 0 },
> +        .should_be_valid = true,
> +        .expect = (CpuTopology) { 16, 2, 2, 4, 1, 16 },
> +    }, {
> +        /* config: -smp sockets=2,dies=2,threads=2
> +         * expect: cpus=8,sockets=2,dies=2,cores=1,threads=2,maxcpus=8 */
> +        .config = (SMPConfiguration) { F, 0, T, 2, T, 2, F, 0, T, 2, F, 0 },
> +        .should_be_valid = true,
> +        .expect = (CpuTopology) { 8, 2, 2, 1, 2, 8 },
> +    }, {
> +        /* config: -smp sockets=2,dies=2,maxcpus=32
> +         * expect: cpus=32,sockets=2,dies=2,cores=8,threads=1,maxcpus=32 */
> +        .config = (SMPConfiguration) { F, 0, T, 2, T, 2, F, 0, F, 0, T, 32 },
> +        .should_be_valid = true,
> +        .expect = (CpuTopology) { 32, 2, 2, 8, 1, 32 },
> +    }, {
> +        /* config: -smp dies=2,cores=4,threads=2
> +         * expect: cpus=16,sockets=1,dies=2,cores=4,threads=2,maxcpus=16 */
> +        .config = (SMPConfiguration) { F, 0, F, 0, T, 2, T, 4, T, 2, F, 0 },
> +        .should_be_valid = true,
> +        .expect = (CpuTopology) { 16, 1, 2, 4, 2, 16 },
> +    }, {
> +        /* config: -smp dies=2,cores=4,maxcpus=32
> +         * expect: cpus=32,sockets=4,dies=2,cores=4,threads=1,maxcpus=32 */
> +        .config = (SMPConfiguration) { F, 0, F, 0, T, 2, T, 4, F, 0, T, 32 },
> +        .should_be_valid = true,
> +        .expect = (CpuTopology) { 32, 4, 2, 4, 1, 32 },
> +    }, {
> +        /* config: -smp dies=2,threads=2,maxcpus=32
> +         * expect: cpus=32,sockets=8,dies=2,cores=1,threads=2,maxcpus=32 */
> +        .config = (SMPConfiguration) { F, 0, F, 0, T, 2, F, 0, T, 2, T, 32 },
> +        .should_be_valid = true,
> +        .expect = (CpuTopology) { 32, 8, 2, 1, 2, 32 },
> +    }, {
> +        /* config: -smp 16,sockets=2,dies=2,cores=4
> +         * expect: cpus=16,sockets=2,dies=2,cores=4,threads=1,maxcpus=16 */
> +        .config = (SMPConfiguration) { T, 16, T, 2, T, 2, T, 4, F, 0, F, 0 },
> +        .should_be_valid = true,
> +        .expect = (CpuTopology) { 16, 2, 2, 4, 1, 16 },
> +    }, {
> +        /* config: -smp 16,sockets=2,dies=2,threads=2
> +         * expect: cpus=16,sockets=2,dies=2,cores=2,threads=2,maxcpus=16 */
> +        .config = (SMPConfiguration) { T, 16, T, 2, T, 2, F, 0, T, 2, F, 0 },
> +        .should_be_valid = true,
> +        .expect = (CpuTopology) { 16, 2, 2, 2, 2, 16 },
> +    }, {
> +        /* config: -smp 16,sockets=2,dies=2,maxcpus=32
> +         * expect: cpus=16,sockets=2,dies=2,cores=8,threads=1,maxcpus=32 */
> +        .config = (SMPConfiguration) { T, 16, T, 2, T, 2, F, 0, F, 0, T, 32 },
> +        .should_be_valid = true,
> +        .expect = (CpuTopology) { 16, 2, 2, 8, 1, 32 },
> +    }, {
> +        /* config: -smp 16,dies=2,cores=4,threads=2
> +         * expect: cpus=16,sockets=1,dies=2,cores=4,threads=2,maxcpus=16 */
> +        .config = (SMPConfiguration) { T, 16, F, 0, T, 2, T, 4, T, 2, F, 0 },
> +        .should_be_valid = true,
> +        .expect = (CpuTopology) { 16, 1, 2, 4, 2, 16 },
> +    }, {
> +        /* config: -smp 16,dies=2,cores=4,maxcpus=32
> +         * expect: cpus=16,sockets=4,dies=2,cores=4,threads=1,maxcpus=32 */
> +        .config = (SMPConfiguration) { T, 16, F, 0, T, 2, T, 4, F, 0, T, 32 },
> +        .should_be_valid = true,
> +        .expect = (CpuTopology) { 16, 4, 2, 4, 1, 32 },
> +    }, {
> +        /* config: -smp 16,dies=2,threads=2,maxcpus=32
> +         * expect: cpus=16,sockets=8,dies=2,cores=1,threads=2,maxcpus=32 */
> +        .config = (SMPConfiguration) { T, 16, F, 0, T, 2, F, 0, T, 2, T, 32 },
> +        .should_be_valid = true,
> +        .expect = (CpuTopology) { 16, 8, 2, 1, 2, 32 },
> +    }, {
> +        /* config: -smp sockets=2,dies=2,cores=4,threads=2
> +         * expect: cpus=32,sockets=2,dies=2,cores=4,threads=2,maxcpus=32 */
> +        .config = (SMPConfiguration) { F, 0, T, 2, T, 2, T, 4, T, 2, F, 0 },
> +        .should_be_valid = true,
> +        .expect = (CpuTopology) { 32, 2, 2, 4, 2, 32 },
> +    }, {
> +        /* config: -smp sockets=2,dies=2,cores=4,maxcpus=32
> +         * expect: cpus=32,sockets=2,dies=2,cores=4,threads=2,maxcpus=32 */
> +        .config = (SMPConfiguration) { F, 0, T, 2, T, 2, T, 4, F, 0, T, 32 },
> +        .should_be_valid = true,
> +        .expect = (CpuTopology) { 32, 2, 2, 4, 2, 32 },
> +    }, {
> +        /* config: -smp sockets=2,dies=2,threads=2,maxcpus=32
> +         * expect: cpus=32,sockets=2,dies=2,cores=4,threads=2,maxcpus=32 */
> +        .config = (SMPConfiguration) { F, 0, T, 2, T, 2, F, 0, T, 2, T, 32 },
> +        .should_be_valid = true,
> +        .expect = (CpuTopology) { 32, 2, 2, 4, 2, 32 },
> +    }, {
> +        /* config: -smp dies=2,cores=4,threads=2,maxcpus=32
> +         * expect: cpus=32,sockets=2,dies=2,cores=4,threads=2,maxcpus=32 */
> +        .config = (SMPConfiguration) { F, 0, F, 0, T, 2, T, 4, T, 2, T, 32 },
> +        .should_be_valid = true,
> +        .expect = (CpuTopology) { 32, 2, 2, 4, 2, 32 },
> +    }, {
> +        /* config: -smp 16,sockets=2,dies=2,cores=4,threads=1
> +         * expect: cpus=16,sockets=2,dies=2,cores=4,threads=1,maxcpus=16 */
> +        .config = (SMPConfiguration) { T, 16, T, 2, T, 2, T, 4, T, 1, F, 0 },
> +        .should_be_valid = true,
> +        .expect = (CpuTopology) { 16, 2, 2, 4, 1, 16 },
> +    }, {
> +        /* config: -smp 16,sockets=2,dies=2,cores=4,maxcpus=32
> +         * expect: cpus=16,sockets=2,dies=2,cores=4,threads=2,maxcpus=32 */
> +        .config = (SMPConfiguration) { T, 16, T, 2, T, 2, T, 4, F, 0, T, 32 },
> +        .should_be_valid = true,
> +        .expect = (CpuTopology) { 16, 2, 2, 4, 2, 32 },
> +    }, {
> +        /* config: -smp 16,sockets=2,dies=2,threads=2,maxcpus=32
> +         * expect: cpus=16,sockets=2,dies=2,cores=4,threads=2,maxcpus=32 */
> +        .config = (SMPConfiguration) { T, 16, T, 2, T, 2, F, 0, T, 2, T, 32 },
> +        .should_be_valid = true,
> +        .expect = (CpuTopology) { 16, 2, 2, 4, 2, 32 },
> +    }, {
> +        /* config: -smp 16,dies=2,cores=4,threads=2,maxcpus=32
> +         * expect: cpus=16,sockets=2,dies=2,cores=4,threads=2,maxcpus=32 */
> +        .config = (SMPConfiguration) { T, 16, F, 0, T, 2, T, 4, T, 2, T, 32 },
> +        .should_be_valid = true,
> +        .expect = (CpuTopology) { 16, 2, 2, 4, 2, 32 },
> +    }, {
> +        /* config: -smp sockets=2,dies=2,cores=4,threads=2,maxcpus=32
> +         * expect: -smp 32,sockets=2,dies=2,cores=4,threads=2,maxcpus=32 */
> +        .config = (SMPConfiguration) { F, 0, T, 2, T, 2, T, 4, T, 2, T, 32 },
> +        .should_be_valid = true,
> +        .expect = (CpuTopology) { 32, 2, 2, 4, 2, 32 },
> +    }, {
> +        /* config: -smp 16,sockets=2,dies=2,cores=4,threads=2,maxcpus=32
> +         * expect: cpus=16,sockets=2,dies=2,cores=4,threads=2,maxcpus=32 */
> +        .config = (SMPConfiguration) { T, 16, T, 2, T, 2, T, 4, T, 2, T, 32 },
> +        .should_be_valid = true,
> +        .expect = (CpuTopology) { 16, 2, 2, 4, 2, 32 },
> +    },
> +};
> +
> +/*
> + * prefer cores over sockets over threads since 6.2.
> + * all possible SMP configurations and the corressponding expected outputs
> + * are listed for testing, including the valid and invalid ones.
> + */
> +static struct SMPTestData prefer_cores[] = {
> +    {
> +        /* config: no smp configuration
> +         * expect: cpus=1,sockets=1,dies=1,cores=1,threads=1,maxcpus=1 */
> +        .config = (SMPConfiguration) { F, 0, F, 0, F, 0, F, 0, F, 0, F, 0 },
> +        .should_be_valid = true,
> +        .expect = (CpuTopology) { 1, 1, 1, 1, 1, 1 },
> +    }, {
> +        /* config: -smp 8
> +         * expect: cpus=8,sockets=1,dies=1,cores=8,threads=1,maxcpus=8 */
> +        .config = (SMPConfiguration) { T, 8, F, 0, F, 0, F, 0, F, 0, F, 0 },
> +        .should_be_valid = true,
> +        .expect = (CpuTopology) { 8, 1, 1, 8, 1, 8 },
> +    }, {
> +        /* config: -smp sockets=2
> +         * expect: cpus=2,sockets=2,dies=1,cores=1,threads=1,maxcpus=2 */
> +        .config = (SMPConfiguration) { F, 0, T, 2, F, 0, F, 0, F, 0, F, 0 },
> +        .should_be_valid = true,
> +        .expect = (CpuTopology) { 2, 2, 1, 1, 1, 2 },
> +    }, {
> +        /* config: -smp cores=4
> +         * expect: cpus=4,sockets=1,dies=1,cores=4,threads=1,maxcpus=4 */
> +        .config = (SMPConfiguration) { F, 0, F, 0, F, 0, T, 4, F, 0, F, 0 },
> +        .should_be_valid = true,
> +        .expect = (CpuTopology) { 4, 1, 1, 4, 1, 4 },
> +    }, {
> +        /* config: -smp threads=2
> +         * expect: cpus=2,sockets=1,dies=1,cores=1,threads=2,maxcpus=2 */
> +        .config = (SMPConfiguration) { F, 0, F, 0, F, 0, F, 0, T, 2, F, 0 },
> +        .should_be_valid = true,
> +        .expect = (CpuTopology) { 2, 1, 1, 1, 2, 2 },
> +    }, {
> +        /* config: -smp maxcpus=16
> +         * expect: cpus=16,sockets=1,dies=1,cores=16,threads=1,maxcpus=16 */
> +        .config = (SMPConfiguration) { F, 0, F, 0, F, 0, F, 0, F, 0, T, 16 },
> +        .should_be_valid = true,
> +        .expect = (CpuTopology) { 16, 1, 1, 16, 1, 16 },
> +    }, {
> +        /* config: -smp 8,sockets=2
> +         * expect: cpus=8,sockets=2,dies=1,cores=4,threads=1,maxcpus=8 */
> +        .config = (SMPConfiguration) { T, 8, T, 2, F, 0, F, 0, F, 0, F, 0 },
> +        .should_be_valid = true,
> +        .expect = (CpuTopology) { 8, 2, 1, 4, 1, 8 },
> +    }, {
> +        /* config: -smp 8,cores=4
> +         * expect: cpus=8,sockets=2,dies=1,cores=4,threads=1,maxcpus=8 */
> +        .config = (SMPConfiguration) { T, 8, F, 0, F, 0, T, 4, F, 0, F, 0 },
> +        .should_be_valid = true,
> +        .expect = (CpuTopology) { 8, 2, 1, 4, 1, 8 },
> +    }, {
> +        /* config: -smp 8,threads=2
> +         * expect: cpus=8,sockets=1,dies=1,cores=4,threads=2,maxcpus=8 */
> +        .config = (SMPConfiguration) { T, 8, F, 0, F, 0, F, 0, T, 2, F, 0 },
> +        .should_be_valid = true,
> +        .expect = (CpuTopology) { 8, 1, 1, 4, 2, 8 },
> +    }, {
> +        /* config: -smp 8,maxcpus=16
> +         * expect: cpus=8,sockets=1,dies=1,cores=16,threads=1,maxcpus=16 */
> +        .config = (SMPConfiguration) { T, 8, F, 0, F, 0, F, 0, F, 0, T, 16 },
> +        .should_be_valid = true,
> +        .expect = (CpuTopology) { 8, 1, 1, 16, 1, 16 },
> +    }, {
> +        /* config: -smp sockets=2,cores=4
> +         * expect: cpus=8,sockets=2,dies=1,cores=4,threads=1,maxcpus=8 */
> +        .config = (SMPConfiguration) { F, 0, T, 2, F, 0, T, 4, F, 0, F, 0 },
> +        .should_be_valid = true,
> +        .expect = (CpuTopology) { 8, 2, 1, 4, 1, 8 },
> +    }, {
> +        /* config: -smp sockets=2,threads=2
> +         * expect: cpus=4,sockets=2,dies=1,cores=1,threads=2,maxcpus=4 */
> +        .config = (SMPConfiguration) { F, 0, T, 2, F, 0, F, 0, T, 2, F, 0 },
> +        .should_be_valid = true,
> +        .expect = (CpuTopology) { 4, 2, 1, 1, 2, 4 },
> +    }, {
> +        /* config: -smp sockets=2,maxcpus=16
> +         * expect: cpus=16,sockets=2,dies=1,cores=8,threads=1,maxcpus=16 */
> +        .config = (SMPConfiguration) { F, 0, T, 2, F, 0, F, 0, F, 0, T, 16 },
> +        .should_be_valid = true,
> +        .expect = (CpuTopology) { 16, 2, 1, 8, 1, 16 },
> +    }, {
> +        /* config: -smp cores=4,threads=2
> +         * expect: cpus=8,sockets=1,dies=1,cores=4,threads=2,maxcpus=8 */
> +        .config = (SMPConfiguration) { F, 0, F, 0, F, 0, T, 4, T, 2, F, 0 },
> +        .should_be_valid = true,
> +        .expect = (CpuTopology) { 8, 1, 1, 4, 2, 8 },
> +    }, {
> +        /* config: -smp cores=4,maxcpus=16
> +         * expect: cpus=16,sockets=4,dies=1,cores=4,threads=1,maxcpus=16 */
> +        .config = (SMPConfiguration) { F, 0, F, 0, F, 0, T, 4, F, 0, T, 16 },
> +        .should_be_valid = true,
> +        .expect = (CpuTopology) { 16, 4, 1, 4, 1, 16 },
> +    }, {
> +        /* config: -smp threads=2,maxcpus=16
> +         * expect: cpus=16,sockets=1,dies=1,cores=8,threads=2,maxcpus=16 */
> +        .config = (SMPConfiguration) { F, 0, F, 0, F, 0, F, 0, T, 2, T, 16 },
> +        .should_be_valid = true,
> +        .expect = (CpuTopology) { 16, 1, 1, 8, 2, 16 },
> +    }, {
> +        /* config: -smp 8,sockets=2,cores=4
> +         * expect: cpus=8,sockets=2,dies=1,cores=4,threads=1,maxcpus=8 */
> +        .config = (SMPConfiguration) { T, 8, T, 2, F, 0, T, 4, F, 0, F, 0 },
> +        .should_be_valid = true,
> +        .expect = (CpuTopology) { 8, 2, 1, 4, 1, 8 },
> +    }, {
> +        /* config: -smp 8,sockets=2,threads=2
> +         * expect: cpus=8,sockets=2,dies=1,cores=2,threads=2,maxcpus=8 */
> +        .config = (SMPConfiguration) { T, 8, T, 2, F, 0, F, 0, T, 2, F, 0 },
> +        .should_be_valid = true,
> +        .expect = (CpuTopology) { 8, 2, 1, 2, 2, 8 },
> +    }, {
> +        /* config: -smp 8,sockets=2,maxcpus=16
> +         * expect: cpus=8,sockets=2,dies=1,cores=8,threads=1,maxcpus=16 */
> +        .config = (SMPConfiguration) { T, 8, T, 2, F, 0, F, 0, F, 0, T, 16 },
> +        .should_be_valid = true,
> +        .expect = (CpuTopology) { 8, 2, 1, 8, 1, 16 },
> +    }, {
> +        /* config: -smp 8,cores=4,threads=2
> +         * expect: cpus=8,sockets=1,dies=1,cores=4,threads=2,maxcpus=8 */
> +        .config = (SMPConfiguration) { T, 8, F, 0, F, 0, T, 4, T, 2, F, 0 },
> +        .should_be_valid = true,
> +        .expect = (CpuTopology) { 8, 1, 1, 4, 2, 8 },
> +    }, {
> +        /* config: -smp 8,cores=4,maxcpus=16
> +         * expect: cpus=8,sockets=4,dies=1,cores=4,threads=1,maxcpus=16 */
> +        .config = (SMPConfiguration) { T, 8, F, 0, F, 0, T, 4, F, 0, T, 16 },
> +        .should_be_valid = true,
> +        .expect = (CpuTopology) { 8, 4, 1, 4, 1, 16 },
> +    }, {
> +        /* config: -smp 8,threads=2,maxcpus=16
> +         * expect: cpus=8,sockets=1,dies=1,cores=8,threads=2,maxcpus=16 */
> +        .config = (SMPConfiguration) { T, 8, F, 0, F, 0, F, 0, T, 2, T, 16 },
> +        .should_be_valid = true,
> +        .expect = (CpuTopology) { 8, 1, 1, 8, 2, 16 },
> +    }, {
> +        /* config: -smp sockets=2,cores=4,threads=2
> +         * expect: cpus=16,sockets=2,dies=1,cores=4,threads=2,maxcpus=16 */
> +        .config = (SMPConfiguration) { F, 0, T, 2, F, 0, T, 4, T, 2, F, 0 },
> +        .should_be_valid = true,
> +        .expect = (CpuTopology) { 16, 2, 1, 4, 2, 16 },
> +    }, {
> +        /* config: -smp sockets=2,cores=4,maxcpus=16
> +         * expect: cpus=16,sockets=2,dies=1,cores=4,threads=2,maxcpus=16 */
> +        .config = (SMPConfiguration) { F, 0, T, 2, F, 0, T, 4, F, 0, T, 16 },
> +        .should_be_valid = true,
> +        .expect = (CpuTopology) { 16, 2, 1, 4, 2, 16 },
> +    }, {
> +        /* config: -smp sockets=2,threads=2,maxcpus=16
> +         * expect: cpus=16,sockets=2,dies=1,cores=4,threads=2,maxcpus=16 */
> +        .config = (SMPConfiguration) { F, 0, T, 2, F, 0, F, 0, T, 2, T, 16 },
> +        .should_be_valid = true,
> +        .expect = (CpuTopology) { 16, 2, 1, 4, 2, 16 },
> +    }, {
> +        /* config: -smp cores=4,threads=2,maxcpus=16
> +         * expect: cpus=16,sockets=2,dies=1,cores=4,threads=2,maxcpus=16 */
> +        .config = (SMPConfiguration) { F, 0, F, 0, F, 0, T, 4, T, 2, T, 16 },
> +        .should_be_valid = true,
> +        .expect = (CpuTopology) { 16, 2, 1, 4, 2, 16 },
> +    }, {
> +        /* config: -smp 8,sockets=2,cores=4,threads=1
> +         * expect: cpus=8,sockets=2,dies=1,cores=4,threads=1,maxcpus=8 */
> +        .config = (SMPConfiguration) { T, 8, T, 2, F, 0, T, 4, T, 1, F, 0 },
> +        .should_be_valid = true,
> +        .expect = (CpuTopology) { 8, 2, 1, 4, 1, 8 },
> +    }, {
> +        /* config: -smp 8,sockets=2,cores=4,maxcpus=16
> +         * expect: cpus=8,sockets=2,dies=1,cores=4,threads=2,maxcpus=16 */
> +        .config = (SMPConfiguration) { T, 8, T, 2, F, 0, T, 4, F, 0, T, 16 },
> +        .should_be_valid = true,
> +        .expect = (CpuTopology) { 8, 2, 1, 4, 2, 16 },
> +    }, {
> +        /* config: -smp 8,sockets=2,threads=2,maxcpus=16
> +         * expect: cpus=8,sockets=2,dies=1,cores=4,threads=2,maxcpus=16 */
> +        .config = (SMPConfiguration) { T, 8, T, 2, F, 0, F, 0, T, 2, T, 16 },
> +        .should_be_valid = true,
> +        .expect = (CpuTopology) { 8, 2, 1, 4, 2, 16 },
> +    }, {
> +        /* config: -smp 8,cores=4,threads=2,maxcpus=16
> +         * expect: cpus=8,sockets=2,dies=1,cores=4,threads=2,maxcpus=16 */
> +        .config = (SMPConfiguration) { T, 8, F, 0, F, 0, T, 4, T, 2, T, 16 },
> +        .should_be_valid = true,
> +        .expect = (CpuTopology) { 8, 2, 1, 4, 2, 16 },
> +    }, {
> +        /* config: -smp sockets=2,cores=4,threads=2,maxcpus=16
> +         * expect: -smp 16,sockets=2,dies=1,cores=4,threads=2,maxcpus=16 */
> +        .config = (SMPConfiguration) { F, 0, T, 2, F, 0, T, 4, T, 2, T, 16 },
> +        .should_be_valid = true,
> +        .expect = (CpuTopology) { 16, 2, 1, 4, 2, 16 },
> +    }, {
> +        /* config: -smp 8,sockets=2,cores=4,threads=2,maxcpus=16
> +         * expect: cpus=8,sockets=2,dies=1,cores=4,threads=2,maxcpus=16 */
> +        .config = (SMPConfiguration) { T, 8, T, 2, F, 0, T, 4, T, 2, T, 16 },
> +        .should_be_valid = true,
> +        .expect = (CpuTopology) { 8, 2, 1, 4, 2, 16 },
> +    }, {
> +        /* config: -smp 8,sockets=2,dies=1,cores=4,threads=2,maxcpus=16
> +         * expect: cpus=8,sockets=2,dies=1,cores=4,threads=2,maxcpus=16 */
> +        .config = (SMPConfiguration) { T, 8, T, 2, T, 1, T, 4, T, 2, T, 16 },
> +        .should_be_valid = true,
> +        .expect = (CpuTopology) { 8, 2, 1, 4, 2, 16 },
> +    }, {
> +        /* config: -smp 0
> +         * expect: error, "anything=0" is not allowed */
> +        .config = (SMPConfiguration) { T, 0, F, 0, F, 0, F, 0, F, 0, F, 0 },
> +        .should_be_valid = false,
> +    }, {
> +        /* config: -smp 8,sockets=0
> +         * expect: error, "anything=0" is not allowed */
> +        .config = (SMPConfiguration) { T, 8, T, 0, F, 0, F, 0, F, 0, F, 0 },
> +        .should_be_valid = false,
> +    }, {
> +        /* config: -smp 8,sockets=2,dies=0
> +         * expect: error, "anything=0" is not allowed */
> +        .config = (SMPConfiguration) { T, 0, T, 2, T, 0, F, 0, F, 0, F, 0 },
> +        .should_be_valid = false,
> +    }, {
> +        /* config: -smp 8,sockets=2,dies=1,cores=0
> +         * expect: error, "anything=0" is not allowed */
> +        .config = (SMPConfiguration) { T, 8, T, 2, T, 1, T, 0, F, 0, F, 0 },
> +        .should_be_valid = false,
> +    }, {
> +        /* config: -smp 8,sockets=2,dies=1,cores=4,threads=0
> +         * expect: error, "anything=0" is not allowed */
> +        .config = (SMPConfiguration) { T, 8, T, 2, T, 1, T, 4, T, 0, F, 0 },
> +        .should_be_valid = false,
> +    }, {
> +        /* config: -smp 8,sockets=2,dies=1,cores=4,threads=2,maxcpus=0
> +         * expect: error, "anything=0" is not allowed */
> +        .config = (SMPConfiguration) { T, 8, T, 2, T, 1, T, 4, T, 2, T, 0 },
> +        .should_be_valid = false,
> +    }, {
> +        /* config: -smp 8,dies=2
> +         * expect: error, multi-dies not supported */
> +        .config = (SMPConfiguration) { T, 8, F, 0, T, 2, F, 0, F, 0, F, 0 },
> +        .should_be_valid = false,
> +    }, {
> +        /* config: -smp 8,sockets=2,cores=8
> +         * expect: error, sum (16) != max_cpus (8) */
> +        .config = (SMPConfiguration) { T, 8, T, 2, F, 0, T, 4, T, 2, F, 0 },
> +        .should_be_valid = false,
> +    }, {
> +        /* config: -smp 8,sockets=2,cores=5,threads=2,maxcpus=16
> +         * expect: error, sum (20) != max_cpus (16) */
> +        .config = (SMPConfiguration) { F, 0, T, 3, F, 0, T, 5, T, 1, T, 16 },
> +        .should_be_valid = false,
> +    }, {
> +        /* config: -smp 16,maxcpus=12
> +         * expect: error, sum (12) < smp_cpus (16) */
> +        .config = (SMPConfiguration) { T, 16, F, 0, F, 0, F, 0, F, 0, T, 12 },
> +        .should_be_valid = false,
> +    },
> +};
> +
> +static struct SMPTestData prefer_cores_support_dies[] = {
> +    {
> +        /* config: -smp dies=2
> +         * expect: cpus=2,sockets=1,dies=2,cores=1,threads=1,maxcpus=2 */
> +        .config = (SMPConfiguration) { F, 0, F, 0, T, 2, F, 0, F, 0, F, 0 },
> +        .should_be_valid = true,
> +        .expect = (CpuTopology) { 2, 1, 2, 1, 1, 2 },
> +    }, {
> +        /* config: -smp 16,dies=2
> +         * expect: cpus=16,sockets=1,dies=2,cores=8,threads=1,maxcpus=16 */
> +        .config = (SMPConfiguration) { T, 16, F, 0, T, 2, F, 0, F, 0, F, 0 },
> +        .should_be_valid = true,
> +        .expect = (CpuTopology) { 16, 1, 2, 8, 1, 16 },
> +    }, {
> +        /* config: -smp sockets=2,dies=2
> +         * expect: cpus=4,sockets=2,dies=2,cores=1,threads=1,maxcpus=4 */
> +        .config = (SMPConfiguration) { F, 0, T, 2, T, 2, F, 0, F, 0, F, 0 },
> +        .should_be_valid = true,
> +        .expect = (CpuTopology) { 4, 2, 2, 1, 1, 4 },
> +    }, {
> +        /* config: -smp dies=2,cores=4
> +         * expect: cpus=8,sockets=1,dies=2,cores=4,threads=1,maxcpus=8 */
> +        .config = (SMPConfiguration) { F, 0, F, 0, T, 2, T, 4, F, 0, F, 0 },
> +        .should_be_valid = true,
> +        .expect = (CpuTopology) { 8, 1, 2, 4, 1, 8 },
> +    }, {
> +        /* config: -smp dies=2,threads=2
> +         * expect: cpus=4,sockets=1,dies=2,cores=1,threads=2,maxcpus=4 */
> +        .config = (SMPConfiguration) { F, 0, F, 0, T, 2, F, 0, T, 2, F, 0 },
> +        .should_be_valid = true,
> +        .expect = (CpuTopology) { 4, 1, 2, 1, 2, 4 },
> +    }, {
> +        /* config: -smp dies=2,maxcpus=32
> +         * expect: cpus=32,sockets=1,dies=2,cores=16,threads=1,maxcpus=32 */
> +        .config = (SMPConfiguration) { F, 0, F, 0, T, 2, F, 0, F, 0, T, 32 },
> +        .should_be_valid = true,
> +        .expect = (CpuTopology) { 32, 1, 2, 16, 1, 32 },
> +    }, {
> +        /* config: -smp 16,sockets=2,dies=2
> +         * expect: cpus=16,sockets=2,dies=2,cores=4,threads=1,maxcpus=16 */
> +        .config = (SMPConfiguration) { T, 16, T, 2, T, 2, F, 0, F, 0, F, 0 },
> +        .should_be_valid = true,
> +        .expect = (CpuTopology) { 16, 2, 2, 4, 1, 16 },
> +    }, {
> +        /* config: -smp 16,dies=2,cores=4
> +         * expect: cpus=16,sockets=2,dies=2,cores=4,threads=1,maxcpus=16 */
> +        .config = (SMPConfiguration) { T, 16, F, 0, T, 2, T, 4, F, 0, F, 0 },
> +        .should_be_valid = true,
> +        .expect = (CpuTopology) { 16, 2, 2, 4, 1, 16 },
> +    }, {
> +        /* config: -smp 16,dies=2,threads=2
> +         * expect: cpus=16,sockets=1,dies=2,cores=4,threads=2,maxcpus=16 */
> +        .config = (SMPConfiguration) { T, 16, F, 0, T, 2, F, 0, T, 2, F, 0 },
> +        .should_be_valid = true,
> +        .expect = (CpuTopology) { 16, 1, 2, 4, 2, 16 },
> +    }, {
> +        /* config: -smp 16,dies=2,maxcpus=32
> +         * expect: cpus=16,sockets=1,dies=2,cores=16,threads=1,maxcpus=32 */
> +        .config = (SMPConfiguration) { T, 16, F, 0, T, 2, F, 0, F, 0, T, 32 },
> +        .should_be_valid = true,
> +        .expect = (CpuTopology) { 16, 1, 2, 16, 1, 32 },
> +    }, {
> +        /* config: -smp sockets=2,dies=2,cores=4
> +         * expect: cpus=16,sockets=2,dies=2,cores=4,threads=1,maxcpus=16 */
> +        .config = (SMPConfiguration) { F, 0, T, 2, T, 2, T, 4, F, 0, F, 0 },
> +        .should_be_valid = true,
> +        .expect = (CpuTopology) { 16, 2, 2, 4, 1, 16 },
> +    }, {
> +        /* config: -smp sockets=2,dies=2,threads=2
> +         * expect: cpus=8,sockets=2,dies=2,cores=1,threads=2,maxcpus=8 */
> +        .config = (SMPConfiguration) { F, 0, T, 2, T, 2, F, 0, T, 2, F, 0 },
> +        .should_be_valid = true,
> +        .expect = (CpuTopology) { 8, 2, 2, 1, 2, 8 },
> +    }, {
> +        /* config: -smp sockets=2,dies=2,maxcpus=32
> +         * expect: cpus=32,sockets=2,dies=2,cores=8,threads=1,maxcpus=32 */
> +        .config = (SMPConfiguration) { F, 0, T, 2, T, 2, F, 0, F, 0, T, 32 },
> +        .should_be_valid = true,
> +        .expect = (CpuTopology) { 32, 2, 2, 8, 1, 32 },
> +    }, {
> +        /* config: -smp dies=2,cores=4,threads=2
> +         * expect: cpus=16,sockets=1,dies=2,cores=4,threads=2,maxcpus=16 */
> +        .config = (SMPConfiguration) { F, 0, F, 0, T, 2, T, 4, T, 2, F, 0 },
> +        .should_be_valid = true,
> +        .expect = (CpuTopology) { 16, 1, 2, 4, 2, 16 },
> +    }, {
> +        /* config: -smp dies=2,cores=4,maxcpus=32
> +         * expect: cpus=32,sockets=4,dies=2,cores=4,threads=1,maxcpus=32 */
> +        .config = (SMPConfiguration) { F, 0, F, 0, T, 2, T, 4, F, 0, T, 32 },
> +        .should_be_valid = true,
> +        .expect = (CpuTopology) { 32, 4, 2, 4, 1, 32 },
> +    }, {
> +        /* config: -smp dies=2,threads=2,maxcpus=32
> +         * expect: cpus=32,sockets=1,dies=2,cores=8,threads=2,maxcpus=32 */
> +        .config = (SMPConfiguration) { F, 0, F, 0, T, 2, F, 0, T, 2, T, 32 },
> +        .should_be_valid = true,
> +        .expect = (CpuTopology) { 32, 1, 2, 8, 2, 32 },
> +    }, {
> +        /* config: -smp 16,sockets=2,dies=2,cores=4
> +         * expect: cpus=16,sockets=2,dies=2,cores=4,threads=1,maxcpus=16 */
> +        .config = (SMPConfiguration) { T, 16, T, 2, T, 2, T, 4, F, 0, F, 0 },
> +        .should_be_valid = true,
> +        .expect = (CpuTopology) { 16, 2, 2, 4, 1, 16 },
> +    }, {
> +        /* config: -smp 16,sockets=2,dies=2,threads=2
> +         * expect: cpus=16,sockets=2,dies=2,cores=2,threads=2,maxcpus=16 */
> +        .config = (SMPConfiguration) { T, 16, T, 2, T, 2, F, 0, T, 2, F, 0 },
> +        .should_be_valid = true,
> +        .expect = (CpuTopology) { 16, 2, 2, 2, 2, 16 },
> +    }, {
> +        /* config: -smp 16,sockets=2,dies=2,maxcpus=32
> +         * expect: cpus=16,sockets=2,dies=2,cores=8,threads=1,maxcpus=32 */
> +        .config = (SMPConfiguration) { T, 16, T, 2, T, 2, F, 0, F, 0, T, 32 },
> +        .should_be_valid = true,
> +        .expect = (CpuTopology) { 16, 2, 2, 8, 1, 32 },
> +    }, {
> +        /* config: -smp 16,dies=2,cores=4,threads=2
> +         * expect: cpus=16,sockets=1,dies=2,cores=4,threads=2,maxcpus=16 */
> +        .config = (SMPConfiguration) { T, 16, F, 0, T, 2, T, 4, T, 2, F, 0 },
> +        .should_be_valid = true,
> +        .expect = (CpuTopology) { 16, 1, 2, 4, 2, 16 },
> +    }, {
> +        /* config: -smp 16,dies=2,cores=4,maxcpus=32
> +         * expect: cpus=16,sockets=4,dies=2,cores=4,threads=1,maxcpus=32 */
> +        .config = (SMPConfiguration) { T, 16, F, 0, T, 2, T, 4, F, 0, T, 32 },
> +        .should_be_valid = true,
> +        .expect = (CpuTopology) { 16, 4, 2, 4, 1, 32 },
> +    }, {
> +        /* config: -smp 16,dies=2,threads=2,maxcpus=32
> +         * expect: cpus=16,sockets=1,dies=2,cores=8,threads=2,maxcpus=32 */
> +        .config = (SMPConfiguration) { T, 16, F, 0, T, 2, F, 0, T, 2, T, 32 },
> +        .should_be_valid = true,
> +        .expect = (CpuTopology) { 16, 1, 2, 8, 2, 32 },
> +    }, {
> +        /* config: -smp sockets=2,dies=2,cores=4,threads=2
> +         * expect: cpus=32,sockets=2,dies=2,cores=4,threads=2,maxcpus=32 */
> +        .config = (SMPConfiguration) { F, 0, T, 2, T, 2, T, 4, T, 2, F, 0 },
> +        .should_be_valid = true,
> +        .expect = (CpuTopology) { 32, 2, 2, 4, 2, 32 },
> +    }, {
> +        /* config: -smp sockets=2,dies=2,cores=4,maxcpus=32
> +         * expect: cpus=32,sockets=2,dies=2,cores=4,threads=2,maxcpus=32 */
> +        .config = (SMPConfiguration) { F, 0, T, 2, T, 2, T, 4, F, 0, T, 32 },
> +        .should_be_valid = true,
> +        .expect = (CpuTopology) { 32, 2, 2, 4, 2, 32 },
> +    }, {
> +        /* config: -smp sockets=2,dies=2,threads=2,maxcpus=32
> +         * expect: cpus=32,sockets=2,dies=2,cores=4,threads=2,maxcpus=32 */
> +        .config = (SMPConfiguration) { F, 0, T, 2, T, 2, F, 0, T, 2, T, 32 },
> +        .should_be_valid = true,
> +        .expect = (CpuTopology) { 32, 2, 2, 4, 2, 32 },
> +    }, {
> +        /* config: -smp dies=2,cores=4,threads=2,maxcpus=32
> +         * expect: cpus=32,sockets=2,dies=2,cores=4,threads=2,maxcpus=32 */
> +        .config = (SMPConfiguration) { F, 0, F, 0, T, 2, T, 4, T, 2, T, 32 },
> +        .should_be_valid = true,
> +        .expect = (CpuTopology) { 32, 2, 2, 4, 2, 32 },
> +    }, {
> +        /* config: -smp 16,sockets=2,dies=2,cores=4,threads=1
> +         * expect: cpus=16,sockets=2,dies=2,cores=4,threads=1,maxcpus=16 */
> +        .config = (SMPConfiguration) { T, 16, T, 2, T, 2, T, 4, T, 1, F, 0 },
> +        .should_be_valid = true,
> +        .expect = (CpuTopology) { 16, 2, 2, 4, 1, 16 },
> +    }, {
> +        /* config: -smp 16,sockets=2,dies=2,cores=4,maxcpus=32
> +         * expect: cpus=16,sockets=2,dies=2,cores=4,threads=2,maxcpus=32 */
> +        .config = (SMPConfiguration) { T, 16, T, 2, T, 2, T, 4, F, 0, T, 32 },
> +        .should_be_valid = true,
> +        .expect = (CpuTopology) { 16, 2, 2, 4, 2, 32 },
> +    }, {
> +        /* config: -smp 16,sockets=2,dies=2,threads=2,maxcpus=32
> +         * expect: cpus=16,sockets=2,dies=2,cores=4,threads=2,maxcpus=32 */
> +        .config = (SMPConfiguration) { T, 16, T, 2, T, 2, F, 0, T, 2, T, 32 },
> +        .should_be_valid = true,
> +        .expect = (CpuTopology) { 16, 2, 2, 4, 2, 32 },
> +    }, {
> +        /* config: -smp 16,dies=2,cores=4,threads=2,maxcpus=32
> +         * expect: cpus=16,sockets=2,dies=2,cores=4,threads=2,maxcpus=32 */
> +        .config = (SMPConfiguration) { T, 16, F, 0, T, 2, T, 4, T, 2, T, 32 },
> +        .should_be_valid = true,
> +        .expect = (CpuTopology) { 16, 2, 2, 4, 2, 32 },
> +    }, {
> +        /* config: -smp sockets=2,dies=2,cores=4,threads=2,maxcpus=32
> +         * expect: -smp 32,sockets=2,dies=2,cores=4,threads=2,maxcpus=32 */
> +        .config = (SMPConfiguration) { F, 0, T, 2, T, 2, T, 4, T, 2, T, 32 },
> +        .should_be_valid = true,
> +        .expect = (CpuTopology) { 32, 2, 2, 4, 2, 32 },
> +    }, {
> +        /* config: -smp 16,sockets=2,dies=2,cores=4,threads=2,maxcpus=32
> +         * expect: cpus=16,sockets=2,dies=2,cores=4,threads=2,maxcpus=32 */
> +        .config = (SMPConfiguration) { T, 16, T, 2, T, 2, T, 4, T, 2, T, 32 },
> +        .should_be_valid = true,
> +        .expect = (CpuTopology) { 16, 2, 2, 4, 2, 32 },
> +    },
> +};
> +
> +static char *get_config_info(SMPConfiguration *config)
> +{
> +    return g_strdup_printf(
> +        "(SMPConfiguration) {\n"
> +        "    .has_cpus    = %5s, cpus    = %ld,\n"
> +        "    .has_sockets = %5s, sockets = %ld,\n"
> +        "    .has_dies    = %5s, dies    = %ld,\n"
> +        "    .has_cores   = %5s, cores   = %ld,\n"
> +        "    .has_threads = %5s, threads = %ld,\n"
> +        "    .has_maxcpus = %5s, maxcpus = %ld,\n"
> +        "}",
> +        config->has_cpus ? "true" : "false", config->cpus,
> +        config->has_sockets ? "true" : "false", config->sockets,
> +        config->has_dies ? "true" : "false", config->dies,
> +        config->has_cores ? "true" : "false", config->cores,
> +        config->has_threads ? "true" : "false", config->threads,
> +        config->has_maxcpus ? "true" : "false", config->maxcpus);
> +}
> +
> +static char *get_topo_info(CpuTopology *topo)
> +{
> +    return g_strdup_printf(
> +        "(CpuTopology) {\n"
> +        "    .cpus     = %u,\n"
> +        "    .sockets  = %u,\n"
> +        "    .dies     = %u,\n"
> +        "    .cores    = %u,\n"
> +        "    .threads  = %u,\n"
> +        "    .max_cpus = %u,\n"
> +        "}",
> +        topo->cpus, topo->sockets, topo->dies,
> +        topo->cores, topo->threads, topo->max_cpus);
> +}
> +
> +static void check_smp_parse(MachineState *ms, SMPTestData *data)
> +{
> +    SMPConfiguration *config = &data->config;
> +    CpuTopology *expect = &data->expect;
> +    g_autofree char *config_info = NULL;
> +    g_autofree char *expect_info = NULL;
> +    g_autofree char *result_info = NULL;
> +    Error *err = NULL;
> +
> +    /* call the generic parser smp_parse() in hw/core/machine-smp.c */
> +    smp_parse(ms, config, &err);
> +
> +    if (data->should_be_valid) {
> +        if ((err == NULL) &&
> +            (ms->smp.cpus == expect->cpus) &&
> +            (ms->smp.sockets == expect->sockets) &&
> +            (ms->smp.dies == expect->dies) &&
> +            (ms->smp.cores == expect->cores) &&
> +            (ms->smp.threads == expect->threads) &&
> +            (ms->smp.max_cpus == expect->max_cpus)) {
> +            return;
> +        }
> +
> +        config_info = get_config_info(config);
> +        expect_info = get_topo_info(expect);
> +
> +        if (err != NULL) {
> +            g_printerr("Check smp_parse failed:\n"
> +                       "config: %s\n"
> +                       "expect: %s\n"
> +                       "should_be_valid: yes\n\n"
> +                       "result_is_valid: no\n"
> +                       "error_msg: %s\n",
> +                       config_info, expect_info, error_get_pretty(err));
> +            error_free(err);
> +        } else {
> +            result_info = get_topo_info(&ms->smp);
> +            g_printerr("Check smp_parse failed:\n"
> +                       "config: %s\n"
> +                       "expect: %s\n"
> +                       "should_be_valid: yes\n\n"
> +                       "result_is_valid: yes\n"
> +                       "result: %s\n",
> +                       config_info, expect_info, result_info);
> +        }
> +    } else {
> +        if (err != NULL) {
> +            error_free(err);
> +            return;
> +        }
> +
> +        config_info = get_config_info(config);
> +        result_info = get_topo_info(&ms->smp);
> +
> +        g_printerr("Check smp_parse failed:\n"
> +                   "config: %s\n"
> +                   "should_be_valid: no\n\n"
> +                   "result_is_valid: yes\n"
> +                   "result: %s\n",
> +                   config_info, result_info);
> +    }
> +
> +    abort();
> +}
> +
> +static void smp_prefer_sockets_test(void)
> +{
> +    Object *obj = object_new(TYPE_MACHINE);
> +    MachineState *ms = MACHINE(obj);
> +    MachineClass *mc = MACHINE_GET_CLASS(obj);
> +    int i;
> +
> +    /* make sure that we have created the object */
> +    g_assert_nonnull(ms);
> +    g_assert_nonnull(mc);
> +
> +    mc->smp_prefer_sockets = true;
> +
> +    /* test cases when multi-dies are not supported */
> +    mc->smp_dies_supported = false;
> +    for (i = 0; i < ARRAY_SIZE(prefer_sockets); i++) {
> +        check_smp_parse(ms, &prefer_sockets[i]);
> +    }
> +
> +    /* test cases when multi-dies are supported */
> +    mc->smp_dies_supported = true;
> +    for (i = 0; i < ARRAY_SIZE(prefer_sockets_support_dies); i++) {
> +        check_smp_parse(ms, &prefer_sockets_support_dies[i]);
> +    }
> +
> +    object_unref(obj);
> +}
> +
> +static void smp_prefer_cores_test(void)
> +{
> +    Object *obj = object_new(TYPE_MACHINE);
> +    MachineState *ms = MACHINE(obj);
> +    MachineClass *mc = MACHINE_GET_CLASS(obj);
> +    int i;
> +
> +    /* make sure that we have created the object */
> +    g_assert_nonnull(ms);
> +    g_assert_nonnull(mc);
> +
> +    mc->smp_prefer_sockets = false;
> +
> +    /* test cases when multi-dies are not supported */
> +    mc->smp_dies_supported = false;
> +    for (i = 0; i < ARRAY_SIZE(prefer_cores); i++) {
> +        check_smp_parse(ms, &prefer_cores[i]);
> +    }
> +
> +    /* test cases when multi-dies are supported */
> +    mc->smp_dies_supported = true;
> +    for (i = 0; i < ARRAY_SIZE(prefer_cores_support_dies); i++) {
> +        check_smp_parse(ms, &prefer_cores_support_dies[i]);
> +    }
> +
> +    object_unref(obj);
> +}
> +
> +int main(int argc, char *argv[])
> +{
> +    g_test_init(&argc, &argv, NULL);
> +
> +    module_call_init(MODULE_INIT_QOM);
> +    type_register_static(&smp_machine_info);
> +
> +    g_test_add_func("/test-smp-parse/prefer_sockets", smp_prefer_sockets_test);
> +    g_test_add_func("/test-smp-parse/prefer_cores", smp_prefer_cores_test);
> +
> +    g_test_run();
> +
> +    return 0;
> +}
> -- 
> 2.19.1
>

Besides the nits, it looks good to me.

Reviewed-by: Andrew Jones <drjones@redhat.com>



^ permalink raw reply	[flat|nested] 58+ messages in thread

* Re: [PATCH for-6.2 v2 06/11] hw: Add compat machines for 6.2
  2021-07-19  3:20 ` [PATCH for-6.2 v2 06/11] hw: Add compat machines for 6.2 Yanan Wang
                     ` (2 preceding siblings ...)
  2021-07-19 17:03   ` Cornelia Huck
@ 2021-07-19 23:45   ` Pankaj Gupta
  3 siblings, 0 replies; 58+ messages in thread
From: Pankaj Gupta @ 2021-07-19 23:45 UTC (permalink / raw)
  To: Yanan Wang
  Cc: Peter Maydell, Andrew Jones, Daniel P . Berrangé,
	Eduardo Habkost, Michael S . Tsirkin, Richard Henderson,
	Qemu Developers, Greg Kurz, Halil Pasic, Paolo Bonzini,
	Thomas Huth, Igor Mammedov, yuzenghui, wanghaibin.wang,
	David Gibson

> Add 6.2 machine types for arm/i440fx/q35/s390x/spapr.
>
> Signed-off-by: Yanan Wang <wangyanan55@huawei.com>
> ---
>  hw/arm/virt.c              |  9 ++++++++-
>  hw/core/machine.c          |  3 +++
>  hw/i386/pc.c               |  3 +++
>  hw/i386/pc_piix.c          | 14 +++++++++++++-
>  hw/i386/pc_q35.c           | 13 ++++++++++++-
>  hw/ppc/spapr.c             | 15 +++++++++++++--
>  hw/s390x/s390-virtio-ccw.c | 14 +++++++++++++-
>  include/hw/boards.h        |  3 +++
>  include/hw/i386/pc.h       |  3 +++
>  9 files changed, 71 insertions(+), 6 deletions(-)
>
> diff --git a/hw/arm/virt.c b/hw/arm/virt.c
> index 81eda46b0b..01165f7f53 100644
> --- a/hw/arm/virt.c
> +++ b/hw/arm/virt.c
> @@ -2788,10 +2788,17 @@ static void machvirt_machine_init(void)
>  }
>  type_init(machvirt_machine_init);
>
> +static void virt_machine_6_2_options(MachineClass *mc)
> +{
> +}
> +DEFINE_VIRT_MACHINE_AS_LATEST(6, 2)
> +
>  static void virt_machine_6_1_options(MachineClass *mc)
>  {
> +    virt_machine_6_2_options(mc);
> +    compat_props_add(mc->compat_props, hw_compat_6_1, hw_compat_6_1_len);
>  }
> -DEFINE_VIRT_MACHINE_AS_LATEST(6, 1)
> +DEFINE_VIRT_MACHINE(6, 1)
>
>  static void virt_machine_6_0_options(MachineClass *mc)
>  {
> diff --git a/hw/core/machine.c b/hw/core/machine.c
> index 8b4d07d3fc..63439c4a6d 100644
> --- a/hw/core/machine.c
> +++ b/hw/core/machine.c
> @@ -37,6 +37,9 @@
>  #include "hw/virtio/virtio.h"
>  #include "hw/virtio/virtio-pci.h"
>
> +GlobalProperty hw_compat_6_1[] = {};
> +const size_t hw_compat_6_1_len = G_N_ELEMENTS(hw_compat_6_1);
> +
>  GlobalProperty hw_compat_6_0[] = {
>      { "gpex-pcihost", "allow-unmapped-accesses", "false" },
>      { "i8042", "extended-state", "false"},
> diff --git a/hw/i386/pc.c b/hw/i386/pc.c
> index d94ef582b5..c6768c6ce7 100644
> --- a/hw/i386/pc.c
> +++ b/hw/i386/pc.c
> @@ -94,6 +94,9 @@
>  #include "trace.h"
>  #include CONFIG_DEVICES
>
> +GlobalProperty pc_compat_6_1[] = {};
> +const size_t pc_compat_6_1_len = G_N_ELEMENTS(pc_compat_6_1);
> +
>  GlobalProperty pc_compat_6_0[] = {
>      { "qemu64" "-" TYPE_X86_CPU, "family", "6" },
>      { "qemu64" "-" TYPE_X86_CPU, "model", "6" },
> diff --git a/hw/i386/pc_piix.c b/hw/i386/pc_piix.c
> index 30b8bd6ea9..fd5c2277f2 100644
> --- a/hw/i386/pc_piix.c
> +++ b/hw/i386/pc_piix.c
> @@ -413,7 +413,7 @@ static void pc_i440fx_machine_options(MachineClass *m)
>      machine_class_allow_dynamic_sysbus_dev(m, TYPE_VMBUS_BRIDGE);
>  }
>
> -static void pc_i440fx_6_1_machine_options(MachineClass *m)
> +static void pc_i440fx_6_2_machine_options(MachineClass *m)
>  {
>      PCMachineClass *pcmc = PC_MACHINE_CLASS(m);
>      pc_i440fx_machine_options(m);
> @@ -422,6 +422,18 @@ static void pc_i440fx_6_1_machine_options(MachineClass *m)
>      pcmc->default_cpu_version = 1;
>  }
>
> +DEFINE_I440FX_MACHINE(v6_2, "pc-i440fx-6.2", NULL,
> +                      pc_i440fx_6_2_machine_options);
> +
> +static void pc_i440fx_6_1_machine_options(MachineClass *m)
> +{
> +    pc_i440fx_6_2_machine_options(m);
> +    m->alias = NULL;
> +    m->is_default = false;
> +    compat_props_add(m->compat_props, hw_compat_6_1, hw_compat_6_1_len);
> +    compat_props_add(m->compat_props, pc_compat_6_1, pc_compat_6_1_len);
> +}
> +
>  DEFINE_I440FX_MACHINE(v6_1, "pc-i440fx-6.1", NULL,
>                        pc_i440fx_6_1_machine_options);
>
> diff --git a/hw/i386/pc_q35.c b/hw/i386/pc_q35.c
> index 04b4a4788d..b45903b15e 100644
> --- a/hw/i386/pc_q35.c
> +++ b/hw/i386/pc_q35.c
> @@ -355,7 +355,7 @@ static void pc_q35_machine_options(MachineClass *m)
>      m->max_cpus = 288;
>  }
>
> -static void pc_q35_6_1_machine_options(MachineClass *m)
> +static void pc_q35_6_2_machine_options(MachineClass *m)
>  {
>      PCMachineClass *pcmc = PC_MACHINE_CLASS(m);
>      pc_q35_machine_options(m);
> @@ -363,6 +363,17 @@ static void pc_q35_6_1_machine_options(MachineClass *m)
>      pcmc->default_cpu_version = 1;
>  }
>
> +DEFINE_Q35_MACHINE(v6_2, "pc-q35-6.2", NULL,
> +                   pc_q35_6_2_machine_options);
> +
> +static void pc_q35_6_1_machine_options(MachineClass *m)
> +{
> +    pc_q35_6_2_machine_options(m);
> +    m->alias = NULL;
> +    compat_props_add(m->compat_props, hw_compat_6_1, hw_compat_6_1_len);
> +    compat_props_add(m->compat_props, pc_compat_6_1, pc_compat_6_1_len);
> +}
> +
>  DEFINE_Q35_MACHINE(v6_1, "pc-q35-6.1", NULL,
>                     pc_q35_6_1_machine_options);
>
> diff --git a/hw/ppc/spapr.c b/hw/ppc/spapr.c
> index 81699d4f8b..d39fd4e644 100644
> --- a/hw/ppc/spapr.c
> +++ b/hw/ppc/spapr.c
> @@ -4685,15 +4685,26 @@ static void spapr_machine_latest_class_options(MachineClass *mc)
>      }                                                                \
>      type_init(spapr_machine_register_##suffix)
>
> +/*
> + * pseries-6.2
> + */
> +static void spapr_machine_6_2_class_options(MachineClass *mc)
> +{
> +    /* Defaults for the latest behaviour inherited from the base class */
> +}
> +
> +DEFINE_SPAPR_MACHINE(6_2, "6.2", true);
> +
>  /*
>   * pseries-6.1
>   */
>  static void spapr_machine_6_1_class_options(MachineClass *mc)
>  {
> -    /* Defaults for the latest behaviour inherited from the base class */
> +    spapr_machine_6_2_class_options(mc);
> +    compat_props_add(mc->compat_props, hw_compat_6_1, hw_compat_6_1_len);
>  }
>
> -DEFINE_SPAPR_MACHINE(6_1, "6.1", true);
> +DEFINE_SPAPR_MACHINE(6_1, "6.1", false);
>
>  /*
>   * pseries-6.0
> diff --git a/hw/s390x/s390-virtio-ccw.c b/hw/s390x/s390-virtio-ccw.c
> index e4b18aef49..4d25278cf2 100644
> --- a/hw/s390x/s390-virtio-ccw.c
> +++ b/hw/s390x/s390-virtio-ccw.c
> @@ -791,14 +791,26 @@ bool css_migration_enabled(void)
>      }                                                                         \
>      type_init(ccw_machine_register_##suffix)
>
> +static void ccw_machine_6_2_instance_options(MachineState *machine)
> +{
> +}
> +
> +static void ccw_machine_6_2_class_options(MachineClass *mc)
> +{
> +}
> +DEFINE_CCW_MACHINE(6_2, "6.2", true);
> +
>  static void ccw_machine_6_1_instance_options(MachineState *machine)
>  {
> +    ccw_machine_6_2_instance_options(machine);
>  }
>
>  static void ccw_machine_6_1_class_options(MachineClass *mc)
>  {
> +    ccw_machine_6_2_class_options(mc);
> +    compat_props_add(mc->compat_props, hw_compat_6_1, hw_compat_6_1_len);
>  }
> -DEFINE_CCW_MACHINE(6_1, "6.1", true);
> +DEFINE_CCW_MACHINE(6_1, "6.1", false);
>
>  static void ccw_machine_6_0_instance_options(MachineState *machine)
>  {
> diff --git a/include/hw/boards.h b/include/hw/boards.h
> index b6161cee88..2832f0f8aa 100644
> --- a/include/hw/boards.h
> +++ b/include/hw/boards.h
> @@ -354,6 +354,9 @@ struct MachineState {
>      } \
>      type_init(machine_initfn##_register_types)
>
> +extern GlobalProperty hw_compat_6_1[];
> +extern const size_t hw_compat_6_1_len;
> +
>  extern GlobalProperty hw_compat_6_0[];
>  extern const size_t hw_compat_6_0_len;
>
> diff --git a/include/hw/i386/pc.h b/include/hw/i386/pc.h
> index 88dffe7517..97b4ab79b5 100644
> --- a/include/hw/i386/pc.h
> +++ b/include/hw/i386/pc.h
> @@ -196,6 +196,9 @@ void pc_system_parse_ovmf_flash(uint8_t *flash_ptr, size_t flash_size);
>  void pc_madt_cpu_entry(AcpiDeviceIf *adev, int uid,
>                         const CPUArchIdList *apic_ids, GArray *entry);
>
> +extern GlobalProperty pc_compat_6_1[];
> +extern const size_t pc_compat_6_1_len;
> +
>  extern GlobalProperty pc_compat_6_0[];
>  extern const size_t pc_compat_6_0_len;
>
> --

Reviewed-by: Pankaj Gupta <pankaj.gupta@ionos.com>


^ permalink raw reply	[flat|nested] 58+ messages in thread

* Re: [PATCH for-6.2 v2 02/11] machine: Make smp_parse generic enough for all arches
  2021-07-19  3:20 ` [PATCH for-6.2 v2 02/11] machine: Make smp_parse generic enough for all arches Yanan Wang
  2021-07-19 16:28   ` Andrew Jones
  2021-07-19 16:53   ` Daniel P. Berrangé
@ 2021-07-20  6:57   ` Cornelia Huck
  2021-07-22  7:12     ` wangyanan (Y)
  2 siblings, 1 reply; 58+ messages in thread
From: Cornelia Huck @ 2021-07-20  6:57 UTC (permalink / raw)
  To: Yanan Wang, qemu-devel
  Cc: Peter Maydell, Andrew Jones, Daniel P . Berrangé,
	Eduardo Habkost, Pierre Morel, Michael S . Tsirkin,
	Richard Henderson, Greg Kurz, Halil Pasic, Paolo Bonzini,
	Thomas Huth, IgorMammedov, yuzenghui, wanghaibin.wang,
	Yanan Wang, David Gibson

On Mon, Jul 19 2021, Yanan Wang <wangyanan55@huawei.com> wrote:

> Currently the only difference between smp_parse and pc_smp_parse
> is the support of multi-dies and the related error reporting code.
> With an arch compat variable "bool smp_dies_supported", we can
> easily make smp_parse generic enough for all arches and the PC
> specific one can be removed.
>
> Making smp_parse() generic enough can reduce code duplication and
> ease the code maintenance, and also allows extending the topology
> with more arch specific members (e.g., clusters) in the future.

So I guess that should also allow us to include s390x books/drawers?

>
> No functional change intended.
>
> Suggested-by: Andrew Jones <drjones@redhat.com>
> Signed-off-by: Yanan Wang <wangyanan55@huawei.com>
> ---
>  hw/core/machine.c   | 28 ++++++++++-------
>  hw/i386/pc.c        | 76 +--------------------------------------------
>  include/hw/boards.h |  1 +
>  3 files changed, 19 insertions(+), 86 deletions(-)
>
> diff --git a/hw/core/machine.c b/hw/core/machine.c
> index d73daa10f4..ed6712e964 100644
> --- a/hw/core/machine.c
> +++ b/hw/core/machine.c
> @@ -743,6 +743,7 @@ void machine_set_cpu_numa_node(MachineState *machine,
>  
>  static void smp_parse(MachineState *ms, SMPConfiguration *config, Error **errp)
>  {
> +    MachineClass *mc = MACHINE_GET_CLASS(ms);
>      unsigned cpus    = config->has_cpus ? config->cpus : 0;
>      unsigned sockets = config->has_sockets ? config->sockets : 0;
>      unsigned dies    = config->has_dies ? config->dies : 1;
> @@ -761,7 +762,7 @@ static void smp_parse(MachineState *ms, SMPConfiguration *config, Error **errp)
>          return;
>      }
>  
> -    if (dies > 1) {
> +    if (!mc->smp_dies_supported && dies > 1) {
>          error_setg(errp, "dies not supported by this machine's CPU topology");
>          return;
>      }

I'm wondering how we should handle parameters that are not supported by
a certain machine type. E.g. if we add support for books/drawers,
specifying them is unlikely to make sense on anything but s390x. Would
we allow to specify books=1 on a non-s390x machine, even though that is
quite bogus? Or do we want to disallow setting any parameters that are
not supported by the machine type?

> @@ -772,23 +773,25 @@ static void smp_parse(MachineState *ms, SMPConfiguration *config, Error **errp)
>          threads = threads > 0 ? threads : 1;
>          if (cpus == 0) {
>              sockets = sockets > 0 ? sockets : 1;
> -            cpus = cores * threads * sockets;
> +            cpus = sockets * dies * cores * threads;
>          } else {
>              maxcpus = maxcpus > 0 ? maxcpus : cpus;
> -            sockets = maxcpus / (cores * threads);
> +            sockets = maxcpus / (dies * cores * threads);
>          }
>      } else if (cores == 0) {
>          threads = threads > 0 ? threads : 1;
> -        cores = cpus / (sockets * threads);
> +        cores = cpus / (sockets * dies * threads);
>          cores = cores > 0 ? cores : 1;
>      } else if (threads == 0) {
> -        threads = cpus / (cores * sockets);
> +        threads = cpus / (sockets * dies * cores);
>          threads = threads > 0 ? threads : 1;
> -    } else if (sockets * cores * threads < cpus) {
> +    } else if (sockets * dies * cores * threads < cpus) {
> +        g_autofree char *dies_msg = g_strdup_printf(
> +            mc->smp_dies_supported ? " * dies (%u)" : "", dies);
>          error_setg(errp, "cpu topology: "
> -                   "sockets (%u) * cores (%u) * threads (%u) < "
> +                   "sockets (%u)%s * cores (%u) * threads (%u) < "
>                     "smp_cpus (%u)",
> -                   sockets, cores, threads, cpus);
> +                   sockets, dies_msg, cores, threads, cpus);
>          return;
>      }
>  
> @@ -799,17 +802,20 @@ static void smp_parse(MachineState *ms, SMPConfiguration *config, Error **errp)
>          return;
>      }
>  
> -    if (sockets * cores * threads != maxcpus) {
> +    if (sockets * dies * cores * threads != maxcpus) {
> +        g_autofree char *dies_msg = g_strdup_printf(
> +            mc->smp_dies_supported ? " * dies (%u)" : "", dies);
>          error_setg(errp, "Invalid CPU topology: "
> -                   "sockets (%u) * cores (%u) * threads (%u) "
> +                   "sockets (%u)%s * cores (%u) * threads (%u) "
>                     "!= maxcpus (%u)",
> -                   sockets, cores, threads,
> +                   sockets, dies_msg, cores, threads,
>                     maxcpus);

Similarily here; do we want to mention parameters that are not
applicable for the machine type? That might be confusing, but a
conditional error message may get too complex if we add some more
parameters.

>          return;
>      }
>  
>      ms->smp.cpus = cpus;
>      ms->smp.sockets = sockets;
> +    ms->smp.dies = dies;
>      ms->smp.cores = cores;
>      ms->smp.threads = threads;
>      ms->smp.max_cpus = maxcpus;
> diff --git a/hw/i386/pc.c b/hw/i386/pc.c
> index c6b63c00a5..d94ef582b5 100644
> --- a/hw/i386/pc.c
> +++ b/hw/i386/pc.c
> @@ -708,80 +708,6 @@ void pc_acpi_smi_interrupt(void *opaque, int irq, int level)
>      }
>  }
>  
> -/*
> - * This function is very similar to smp_parse()
> - * in hw/core/machine.c but includes CPU die support.
> - */
> -static void pc_smp_parse(MachineState *ms, SMPConfiguration *config, Error **errp)
> -{
> -    unsigned cpus    = config->has_cpus ? config->cpus : 0;
> -    unsigned sockets = config->has_sockets ? config->sockets : 0;
> -    unsigned dies    = config->has_dies ? config->dies : 1;
> -    unsigned cores   = config->has_cores ? config->cores : 0;
> -    unsigned threads = config->has_threads ? config->threads : 0;
> -    unsigned maxcpus = config->has_maxcpus ? config->maxcpus : 0;
> -
> -    if ((config->has_cpus && config->cpus == 0) ||
> -        (config->has_sockets && config->sockets == 0) ||
> -        (config->has_dies && config->dies == 0) ||
> -        (config->has_cores && config->cores == 0) ||
> -        (config->has_threads && config->threads == 0) ||
> -        (config->has_maxcpus && config->maxcpus == 0)) {
> -        error_setg(errp, "parameters must be equal to or greater than one"
> -                   "if provided");
> -        return;
> -    }
> -
> -    /* compute missing values, prefer sockets over cores over threads */
> -    if (cpus == 0 || sockets == 0) {
> -        cores = cores > 0 ? cores : 1;
> -        threads = threads > 0 ? threads : 1;
> -        if (cpus == 0) {
> -            sockets = sockets > 0 ? sockets : 1;
> -            cpus = cores * threads * dies * sockets;
> -        } else {
> -            maxcpus = maxcpus > 0 ? maxcpus : cpus;
> -            sockets = maxcpus / (cores * threads * dies);
> -        }
> -    } else if (cores == 0) {
> -        threads = threads > 0 ? threads : 1;
> -        cores = cpus / (sockets * dies * threads);
> -        cores = cores > 0 ? cores : 1;
> -    } else if (threads == 0) {
> -        threads = cpus / (cores * dies * sockets);
> -        threads = threads > 0 ? threads : 1;
> -    } else if (sockets * dies * cores * threads < cpus) {
> -        error_setg(errp, "cpu topology: "
> -                   "sockets (%u) * dies (%u) * cores (%u) * threads (%u) < "
> -                   "smp_cpus (%u)",
> -                   sockets, dies, cores, threads, cpus);
> -        return;
> -    }
> -
> -    maxcpus = maxcpus > 0 ? maxcpus : cpus;
> -
> -    if (maxcpus < cpus) {
> -        error_setg(errp, "maxcpus must be equal to or greater than smp");
> -        return;
> -    }
> -
> -    if (sockets * dies * cores * threads != maxcpus) {
> -        error_setg(errp, "Invalid CPU topology deprecated: "
> -                   "sockets (%u) * dies (%u) * cores (%u) * threads (%u) "
> -                   "!= maxcpus (%u)",
> -                   sockets, dies, cores, threads,
> -                   maxcpus);
> -        return;
> -    }
> -
> -    ms->smp.cpus = cpus;
> -    ms->smp.sockets = sockets;
> -    ms->smp.dies = dies;
> -    ms->smp.cores = cores;
> -    ms->smp.threads = threads;
> -    ms->smp.max_cpus = maxcpus;
> -}
> -
>  static
>  void pc_machine_done(Notifier *notifier, void *data)
>  {
> @@ -1735,7 +1661,6 @@ static void pc_machine_class_init(ObjectClass *oc, void *data)
>      mc->auto_enable_numa_with_memdev = true;
>      mc->has_hotpluggable_cpus = true;
>      mc->default_boot_order = "cad";
> -    mc->smp_parse = pc_smp_parse;

We can probably remove smp_parse, and call the generic parser directly?

>      mc->block_default_type = IF_IDE;
>      mc->max_cpus = 255;
>      mc->reset = pc_machine_reset;
> @@ -1746,6 +1671,7 @@ static void pc_machine_class_init(ObjectClass *oc, void *data)
>      hc->unplug = pc_machine_device_unplug_cb;
>      mc->default_cpu_type = TARGET_DEFAULT_CPU_TYPE;
>      mc->nvdimm_supported = true;
> +    mc->smp_dies_supported = true;
>      mc->default_ram_id = "pc.ram";
>  
>      object_class_property_add(oc, PC_MACHINE_MAX_RAM_BELOW_4G, "size",
> diff --git a/include/hw/boards.h b/include/hw/boards.h
> index accd6eff35..b6161cee88 100644
> --- a/include/hw/boards.h
> +++ b/include/hw/boards.h
> @@ -246,6 +246,7 @@ struct MachineClass {
>      bool smbus_no_migration_support;
>      bool nvdimm_supported;
>      bool numa_mem_supported;
> +    bool smp_dies_supported;
>      bool auto_enable_numa;
>      const char *default_ram_id;
>  
> -- 
> 2.19.1



^ permalink raw reply	[flat|nested] 58+ messages in thread

* Re: [PATCH for-6.2 v2 01/11] machine: Disallow specifying topology parameters as zero
  2021-07-19 16:11   ` Andrew Jones
@ 2021-07-21 12:34     ` wangyanan (Y)
  0 siblings, 0 replies; 58+ messages in thread
From: wangyanan (Y) @ 2021-07-21 12:34 UTC (permalink / raw)
  To: Andrew Jones
  Cc: Peter Maydell, Thomas Huth, Daniel P. Berrangé,
	Eduardo Habkost, Michael S . Tsirkin, wanghaibin.wang,
	Richard Henderson, qemu-devel, Greg Kurz, Halil Pasic,
	Igor Mammedov, yuzenghui, Paolo Bonzini, David Gibson

On 2021/7/20 0:11, Andrew Jones wrote:
> On Mon, Jul 19, 2021 at 11:20:33AM +0800, Yanan Wang wrote:
>> In the SMP configuration, we should either specify a topology
>> parameter with a reasonable value (equal to or greater than 1)
>> or just leave it omitted and QEMU will calculate its value.
>>
>> Configurations which explicitly specify the topology parameters
>> as zero like "sockets=0" are meaningless, so disallow them.
>>
>> Suggested-by: Andrew Jones <drjones@redhat.com>
>> Signed-off-by: Yanan Wang <wangyanan55@huawei.com>
>> ---
>>   hw/core/machine.c | 31 +++++++++++++++++++++++--------
>>   hw/i386/pc.c      | 29 +++++++++++++++++++++--------
>>   qapi/machine.json |  4 ++--
>>   3 files changed, 46 insertions(+), 18 deletions(-)
>>
>> diff --git a/hw/core/machine.c b/hw/core/machine.c
>> index 775add0795..d73daa10f4 100644
>> --- a/hw/core/machine.c
>> +++ b/hw/core/machine.c
>> @@ -745,11 +745,25 @@ static void smp_parse(MachineState *ms, SMPConfiguration *config, Error **errp)
>>   {
>>       unsigned cpus    = config->has_cpus ? config->cpus : 0;
>>       unsigned sockets = config->has_sockets ? config->sockets : 0;
>> +    unsigned dies    = config->has_dies ? config->dies : 1;
>>       unsigned cores   = config->has_cores ? config->cores : 0;
>>       unsigned threads = config->has_threads ? config->threads : 0;
>> +    unsigned maxcpus = config->has_maxcpus ? config->maxcpus : 0;
>> +
>> +    if ((config->has_cpus && config->cpus == 0) ||
>> +        (config->has_sockets && config->sockets == 0) ||
>> +        (config->has_dies && config->dies == 0) ||
>> +        (config->has_cores && config->cores == 0) ||
>> +        (config->has_threads && config->threads == 0) ||
>> +        (config->has_maxcpus && config->maxcpus == 0)) {
>> +        error_setg(errp, "parameters must be equal to or greater than one"
>> +                   "if provided");
> Missing a space between 'one' and 'if'. It's better to just put the whole
> string on one line too (ignore the 80 char thing) for error grepping.
>
>> +        return;
>> +    }
>>   
>> -    if (config->has_dies && config->dies != 0 && config->dies != 1) {
>> +    if (dies > 1) {
>>           error_setg(errp, "dies not supported by this machine's CPU topology");
>> +        return;
>>       }
>>   
>>       /* compute missing values, prefer sockets over cores over threads */
>> @@ -760,8 +774,8 @@ static void smp_parse(MachineState *ms, SMPConfiguration *config, Error **errp)
>>               sockets = sockets > 0 ? sockets : 1;
>>               cpus = cores * threads * sockets;
>>           } else {
>> -            ms->smp.max_cpus = config->has_maxcpus ? config->maxcpus : cpus;
>> -            sockets = ms->smp.max_cpus / (cores * threads);
>> +            maxcpus = maxcpus > 0 ? maxcpus : cpus;
>> +            sockets = maxcpus / (cores * threads);
>>           }
>>       } else if (cores == 0) {
>>           threads = threads > 0 ? threads : 1;
>> @@ -778,26 +792,27 @@ static void smp_parse(MachineState *ms, SMPConfiguration *config, Error **errp)
>>           return;
>>       }
>>   
>> -    ms->smp.max_cpus = config->has_maxcpus ? config->maxcpus : cpus;
>> +    maxcpus = maxcpus > 0 ? maxcpus : cpus;
>>   
>> -    if (ms->smp.max_cpus < cpus) {
>> +    if (maxcpus < cpus) {
>>           error_setg(errp, "maxcpus must be equal to or greater than smp");
>>           return;
>>       }
>>   
>> -    if (sockets * cores * threads != ms->smp.max_cpus) {
>> +    if (sockets * cores * threads != maxcpus) {
>>           error_setg(errp, "Invalid CPU topology: "
>>                      "sockets (%u) * cores (%u) * threads (%u) "
>>                      "!= maxcpus (%u)",
>>                      sockets, cores, threads,
>> -                   ms->smp.max_cpus);
>> +                   maxcpus);
>>           return;
>>       }
>>   
>>       ms->smp.cpus = cpus;
>> +    ms->smp.sockets = sockets;
>>       ms->smp.cores = cores;
>>       ms->smp.threads = threads;
>> -    ms->smp.sockets = sockets;
>> +    ms->smp.max_cpus = maxcpus;
>>   }
>>   
>>   static void machine_get_smp(Object *obj, Visitor *v, const char *name,
>> diff --git a/hw/i386/pc.c b/hw/i386/pc.c
>> index c2b9d62a35..c6b63c00a5 100644
>> --- a/hw/i386/pc.c
>> +++ b/hw/i386/pc.c
>> @@ -719,6 +719,18 @@ static void pc_smp_parse(MachineState *ms, SMPConfiguration *config, Error **err
>>       unsigned dies    = config->has_dies ? config->dies : 1;
>>       unsigned cores   = config->has_cores ? config->cores : 0;
>>       unsigned threads = config->has_threads ? config->threads : 0;
>> +    unsigned maxcpus = config->has_maxcpus ? config->maxcpus : 0;
>> +
>> +    if ((config->has_cpus && config->cpus == 0) ||
>> +        (config->has_sockets && config->sockets == 0) ||
>> +        (config->has_dies && config->dies == 0) ||
>> +        (config->has_cores && config->cores == 0) ||
>> +        (config->has_threads && config->threads == 0) ||
>> +        (config->has_maxcpus && config->maxcpus == 0)) {
>> +        error_setg(errp, "parameters must be equal to or greater than one"
>> +                   "if provided");
> Same comment as above.
Ok, I will fix them.
>> +        return;
>> +    }
>>   
>>       /* compute missing values, prefer sockets over cores over threads */
>>       if (cpus == 0 || sockets == 0) {
>> @@ -728,8 +740,8 @@ static void pc_smp_parse(MachineState *ms, SMPConfiguration *config, Error **err
>>               sockets = sockets > 0 ? sockets : 1;
>>               cpus = cores * threads * dies * sockets;
>>           } else {
>> -            ms->smp.max_cpus = config->has_maxcpus ? config->maxcpus : cpus;
>> -            sockets = ms->smp.max_cpus / (cores * threads * dies);
>> +            maxcpus = maxcpus > 0 ? maxcpus : cpus;
>> +            sockets = maxcpus / (cores * threads * dies);
>>           }
>>       } else if (cores == 0) {
>>           threads = threads > 0 ? threads : 1;
>> @@ -746,27 +758,28 @@ static void pc_smp_parse(MachineState *ms, SMPConfiguration *config, Error **err
>>           return;
>>       }
>>   
>> -    ms->smp.max_cpus = config->has_maxcpus ? config->maxcpus : cpus;
>> +    maxcpus = maxcpus > 0 ? maxcpus : cpus;
>>   
>> -    if (ms->smp.max_cpus < cpus) {
>> +    if (maxcpus < cpus) {
>>           error_setg(errp, "maxcpus must be equal to or greater than smp");
>>           return;
>>       }
>>   
>> -    if (sockets * dies * cores * threads != ms->smp.max_cpus) {
>> +    if (sockets * dies * cores * threads != maxcpus) {
>>           error_setg(errp, "Invalid CPU topology deprecated: "
>>                      "sockets (%u) * dies (%u) * cores (%u) * threads (%u) "
>>                      "!= maxcpus (%u)",
>>                      sockets, dies, cores, threads,
>> -                   ms->smp.max_cpus);
>> +                   maxcpus);
>>           return;
>>       }
>>   
>>       ms->smp.cpus = cpus;
>> -    ms->smp.cores = cores;
>> -    ms->smp.threads = threads;
>>       ms->smp.sockets = sockets;
>>       ms->smp.dies = dies;
>> +    ms->smp.cores = cores;
>> +    ms->smp.threads = threads;
>> +    ms->smp.max_cpus = maxcpus;
>>   }
>>   
>>   static
>> diff --git a/qapi/machine.json b/qapi/machine.json
>> index c3210ee1fb..c11b2e6f73 100644
>> --- a/qapi/machine.json
>> +++ b/qapi/machine.json
>> @@ -1288,8 +1288,8 @@
>>   ##
>>   # @SMPConfiguration:
>>   #
>> -# Schema for CPU topology configuration.  "0" or a missing value lets
>> -# QEMU figure out a suitable value based on the ones that are provided.
>> +# Schema for CPU topology configuration. A missing value lets QEMU
>> +# figure out a suitable value based on the ones that are provided.
>>   #
>>   # @cpus: number of virtual CPUs in the virtual machine
>>   #
>> -- 
>> 2.19.1
>>
> Otherwise
>
> Reviewed-by: Andrew Jones <drjones@redhat.com>
>
> .
Thanks,
Yanan
.



^ permalink raw reply	[flat|nested] 58+ messages in thread

* Re: [PATCH for-6.2 v2 01/11] machine: Disallow specifying topology parameters as zero
  2021-07-19 16:46   ` Daniel P. Berrangé
@ 2021-07-21 12:35     ` wangyanan (Y)
  0 siblings, 0 replies; 58+ messages in thread
From: wangyanan (Y) @ 2021-07-21 12:35 UTC (permalink / raw)
  To: Daniel P. Berrangé
  Cc: Peter Maydell, Andrew Jones, Eduardo Habkost,
	Michael S . Tsirkin, Richard Henderson, qemu-devel, Greg Kurz,
	Halil Pasic, Paolo Bonzini, Thomas Huth, Igor Mammedov,
	yuzenghui, wanghaibin.wang, David Gibson

On 2021/7/20 0:46, Daniel P. Berrangé wrote:
> On Mon, Jul 19, 2021 at 11:20:33AM +0800, Yanan Wang wrote:
>> In the SMP configuration, we should either specify a topology
>> parameter with a reasonable value (equal to or greater than 1)
>> or just leave it omitted and QEMU will calculate its value.
>>
>> Configurations which explicitly specify the topology parameters
>> as zero like "sockets=0" are meaningless, so disallow them.
>>
>> Suggested-by: Andrew Jones <drjones@redhat.com>
>> Signed-off-by: Yanan Wang <wangyanan55@huawei.com>
>> ---
>>   hw/core/machine.c | 31 +++++++++++++++++++++++--------
>>   hw/i386/pc.c      | 29 +++++++++++++++++++++--------
>>   qapi/machine.json |  4 ++--
>>   3 files changed, 46 insertions(+), 18 deletions(-)
>>
>> diff --git a/hw/core/machine.c b/hw/core/machine.c
>> index 775add0795..d73daa10f4 100644
>> --- a/hw/core/machine.c
>> +++ b/hw/core/machine.c
>> @@ -745,11 +745,25 @@ static void smp_parse(MachineState *ms, SMPConfiguration *config, Error **errp)
>>   {
>>       unsigned cpus    = config->has_cpus ? config->cpus : 0;
>>       unsigned sockets = config->has_sockets ? config->sockets : 0;
>> +    unsigned dies    = config->has_dies ? config->dies : 1;
> It looks odd to set dies=1 by default at initially, when everything
> else is set to 0.  I realize you're just copying existing pc_smp_parse
> code in this respect, but I feel like could benefit from a separate
> initialization with a comment to explain why we're hardcoding it
> to 1....
>
>>       unsigned cores   = config->has_cores ? config->cores : 0;
>>       unsigned threads = config->has_threads ? config->threads : 0;
>> +    unsigned maxcpus = config->has_maxcpus ? config->maxcpus : 0;
>> +
>> +    if ((config->has_cpus && config->cpus == 0) ||
>> +        (config->has_sockets && config->sockets == 0) ||
>> +        (config->has_dies && config->dies == 0) ||
>> +        (config->has_cores && config->cores == 0) ||
>> +        (config->has_threads && config->threads == 0) ||
>> +        (config->has_maxcpus && config->maxcpus == 0)) {
>> +        error_setg(errp, "parameters must be equal to or greater than one"
>> +                   "if provided");
>> +        return;
>> +    }
>>   
>> -    if (config->has_dies && config->dies != 0 && config->dies != 1) {
>> +    if (dies > 1) {
>>           error_setg(errp, "dies not supported by this machine's CPU topology");
>> +        return;
>>       }
> .... eg how about here adding
>
>       /* Never try to assign multiple dies when defaulting omitted topology */
>       if (dies == 0) {
>           dies = 1;
>       }
Yeah, I agree to default dies to 0 like the other parameters at initially
and then explicitly assign it to 1 if omitted here. But I think this 
explicit
assignment should be in pc_smp_parse, because dies is never used in
the calculation in smp_parse yet except the front sanity-check.

So I think what should be updated for this patch is:
1) default dies to 0 at initially both in smp_parse and pc_smp_parse
2) then explicitly assign dies to 1 if it's omitted in pc_smp_parse
>
>
>> diff --git a/qapi/machine.json b/qapi/machine.json
>> index c3210ee1fb..c11b2e6f73 100644
>> --- a/qapi/machine.json
>> +++ b/qapi/machine.json
>> @@ -1288,8 +1288,8 @@
>>   ##
>>   # @SMPConfiguration:
>>   #
>> -# Schema for CPU topology configuration.  "0" or a missing value lets
>> -# QEMU figure out a suitable value based on the ones that are provided.
>> +# Schema for CPU topology configuration. A missing value lets QEMU
>> +# figure out a suitable value based on the ones that are provided.
> Hmm, so we had actually documented that '0' had the same semantics
> as omitting a parameter. This was done in:
>
>    commit 1e63fe685804dfadddd643bf3860b1a59702d4bf
>    Author: Paolo Bonzini <pbonzini@redhat.com>
>    Date:   Thu Jun 17 17:53:06 2021 +0200
>
>      machine: pass QAPI struct to mc->smp_parse
>
> which hasn't been released yet.
>
> This was possible, but never documented, with the traditiaonl -smp
> impl before it was qapi-ified. I think that historical behaviour
> was simply a side effect of the QemuOpts impl rather than an
> intentional design, hence not documented.
Agreed.
> At the very least I think need to get rid of this bit of docs about
> "0" before this release, otherwise we'll have stronger need to
> consider a real deprecation process.
If the doc needs to be fixed right now, then I think we'd better resend this
single patch separately for 6.1, including the doc fix and also the related
sanity-check in the parsers. Right?

Thanks,
Yanan
.


^ permalink raw reply	[flat|nested] 58+ messages in thread

* Re: [PATCH for-6.2 v2 00/11] machine: smp parsing fixes and improvement
  2021-07-19 16:57 ` [PATCH for-6.2 v2 00/11] machine: smp parsing fixes and improvement Cornelia Huck
@ 2021-07-21 12:38   ` wangyanan (Y)
  2021-07-21 13:52     ` Pankaj Gupta
  2021-07-22  7:51     ` Pierre Morel
  0 siblings, 2 replies; 58+ messages in thread
From: wangyanan (Y) @ 2021-07-21 12:38 UTC (permalink / raw)
  To: Cornelia Huck, qemu-devel
  Cc: Peter Maydell, Andrew Jones, Daniel P . Berrangé,
	Eduardo Habkost, Pierre Morel, Michael S . Tsirkin,
	Richard Henderson, Greg Kurz, Halil Pasic, Paolo Bonzini,
	Thomas Huth, Igor Mammedov, yuzenghui, wanghaibin.wang,
	David Gibson

On 2021/7/20 0:57, Cornelia Huck wrote:
> On Mon, Jul 19 2021, Yanan Wang <wangyanan55@huawei.com> wrote:
>
>> Hi,
>>
>> This is v2 of the series [1] that I have posted to introduce some smp parsing
>> fixes and improvement, much more work has been processed compared to RFC v1.
>>
>> [1] https://lists.gnu.org/archive/html/qemu-devel/2021-07/msg00259.html
>>
>> The purpose of this series is to improve/fix the parsing logic. Explicitly
>> specifying a CPU topology parameter as zero is not allowed any more, and
>> maxcpus is now uniformly used to calculate the omitted parameters. It's also
>> suggested that we should start to prefer cores over sockets over threads on
>> the newer machine types, which will make the computed virtual topology more
>> reflective of the real hardware.
>>
>> In order to reduce code duplication and ease the code maintenance, smp_parse
>> in now converted into a parser generic enough for all arches, so that the PC
>> specific one can be removed. It's also convenient to introduce more topology
>> members (e.g. cluster) to the generic parser in the future.
> Cc:ing Pierre, as he also had been looking at the smp parsing code (for
> s390x) recently.
>
> Also, please keep me on cc: for patches that touch s390x.
Sure, I will. Sorry about the missing. :)

Thanks,
Yanan
.
>> Finally, a QEMU unit test for the parsing of given SMP configuration is added.
>> Since all the parsing logic is in generic function smp_parse(), this test
>> passes diffenent SMP configurations to the function and compare the parsing
>> result with what is expected. In the test, all possible collections of the
>> topology parameters and the corressponding expected results are listed,
>> including the valid and invalid ones. The preference of sockets over cores
>> and the preference of cores over sockets, and the support of multi-dies are
>> also taken into consideration.
>>
>> ---
>>
>> Changelogs:
>>
>> v1->v2:
>> - disallow "anything=0" in the smp configuration (Andrew)
>> - make function smp_parse() a generic helper for all arches
>> - improve the error reporting in the parser
>> - start to prefer cores over sockets since 6.2 (Daniel)
>> - add a unit test for the smp parsing (Daniel)
>>
>> ---
>>
>> Yanan Wang (11):
>>    machine: Disallow specifying topology parameters as zero
>>    machine: Make smp_parse generic enough for all arches
>>    machine: Uniformly use maxcpus to calculate the omitted parameters
>>    machine: Use the computed parameters to calculate omitted cpus
>>    machine: Improve the error reporting of smp parsing
>>    hw: Add compat machines for 6.2
>>    machine: Prefer cores over sockets in smp parsing since 6.2
>>    machine: Use ms instead of global current_machine in sanity-check
>>    machine: Tweak the order of topology members in struct CpuTopology
>>    machine: Split out the smp parsing code
>>    tests/unit: Add a unit test for smp parsing
>>
>>   MAINTAINERS                 |    2 +
>>   hw/arm/virt.c               |   10 +-
>>   hw/core/machine-smp.c       |  124 ++++
>>   hw/core/machine.c           |   68 +--
>>   hw/core/meson.build         |    1 +
>>   hw/i386/pc.c                |   66 +--
>>   hw/i386/pc_piix.c           |   15 +-
>>   hw/i386/pc_q35.c            |   14 +-
>>   hw/ppc/spapr.c              |   16 +-
>>   hw/s390x/s390-virtio-ccw.c  |   15 +-
>>   include/hw/boards.h         |   13 +-
>>   include/hw/i386/pc.h        |    3 +
>>   qapi/machine.json           |    6 +-
>>   qemu-options.hx             |    4 +-
>>   tests/unit/meson.build      |    1 +
>>   tests/unit/test-smp-parse.c | 1117 +++++++++++++++++++++++++++++++++++
>>   16 files changed, 1338 insertions(+), 137 deletions(-)
>>   create mode 100644 hw/core/machine-smp.c
>>   create mode 100644 tests/unit/test-smp-parse.c
>>
>> -- 
>> 2.19.1
> .



^ permalink raw reply	[flat|nested] 58+ messages in thread

* Re: [PATCH for-6.2 v2 00/11] machine: smp parsing fixes and improvement
  2021-07-21 12:38   ` wangyanan (Y)
@ 2021-07-21 13:52     ` Pankaj Gupta
  2021-07-22  2:22       ` wangyanan (Y)
  2021-07-22  7:51     ` Pierre Morel
  1 sibling, 1 reply; 58+ messages in thread
From: Pankaj Gupta @ 2021-07-21 13:52 UTC (permalink / raw)
  To: wangyanan (Y)
  Cc: Peter Maydell, Andrew Jones, Daniel P . Berrangé,
	Eduardo Habkost, Pierre Morel, Michael S . Tsirkin,
	wanghaibin.wang, Cornelia Huck, Richard Henderson,
	Qemu Developers, Greg Kurz, Halil Pasic, yuzenghui,
	Igor Mammedov, Thomas Huth, Paolo Bonzini, David Gibson

> > On Mon, Jul 19 2021, Yanan Wang <wangyanan55@huawei.com> wrote:
> >
> >> Hi,
> >>
> >> This is v2 of the series [1] that I have posted to introduce some smp parsing
> >> fixes and improvement, much more work has been processed compared to RFC v1.
> >>
> >> [1] https://lists.gnu.org/archive/html/qemu-devel/2021-07/msg00259.html
> >>
> >> The purpose of this series is to improve/fix the parsing logic. Explicitly
> >> specifying a CPU topology parameter as zero is not allowed any more, and
> >> maxcpus is now uniformly used to calculate the omitted parameters. It's also
> >> suggested that we should start to prefer cores over sockets over threads on
> >> the newer machine types, which will make the computed virtual topology more
> >> reflective of the real hardware.
> >>
> >> In order to reduce code duplication and ease the code maintenance, smp_parse
> >> in now converted into a parser generic enough for all arches, so that the PC
> >> specific one can be removed. It's also convenient to introduce more topology
> >> members (e.g. cluster) to the generic parser in the future.
> > Cc:ing Pierre, as he also had been looking at the smp parsing code (for
> > s390x) recently.
> >
> > Also, please keep me on cc: for patches that touch s390x.
> Sure, I will. Sorry about the missing. :)
>
> Thanks,
> Yanan
> .
> >> Finally, a QEMU unit test for the parsing of given SMP configuration is added.
> >> Since all the parsing logic is in generic function smp_parse(), this test
> >> passes diffenent SMP configurations to the function and compare the parsing
> >> result with what is expected. In the test, all possible collections of the
> >> topology parameters and the corressponding expected results are listed,
> >> including the valid and invalid ones. The preference of sockets over cores
> >> and the preference of cores over sockets, and the support of multi-dies are
> >> also taken into consideration.
> >>
> >> ---
> >>
> >> Changelogs:
> >>
> >> v1->v2:
> >> - disallow "anything=0" in the smp configuration (Andrew)
> >> - make function smp_parse() a generic helper for all arches
> >> - improve the error reporting in the parser
> >> - start to prefer cores over sockets since 6.2 (Daniel)
> >> - add a unit test for the smp parsing (Daniel)
> >>
> >> ---
> >>
> >> Yanan Wang (11):
> >>    machine: Disallow specifying topology parameters as zero
> >>    machine: Make smp_parse generic enough for all arches
> >>    machine: Uniformly use maxcpus to calculate the omitted parameters
> >>    machine: Use the computed parameters to calculate omitted cpus
> >>    machine: Improve the error reporting of smp parsing
> >>    hw: Add compat machines for 6.2
> >>    machine: Prefer cores over sockets in smp parsing since 6.2
> >>    machine: Use ms instead of global current_machine in sanity-check
> >>    machine: Tweak the order of topology members in struct CpuTopology
> >>    machine: Split out the smp parsing code
> >>    tests/unit: Add a unit test for smp parsing
> >>
> >>   MAINTAINERS                 |    2 +
> >>   hw/arm/virt.c               |   10 +-
> >>   hw/core/machine-smp.c       |  124 ++++
> >>   hw/core/machine.c           |   68 +--
> >>   hw/core/meson.build         |    1 +
> >>   hw/i386/pc.c                |   66 +--
> >>   hw/i386/pc_piix.c           |   15 +-
> >>   hw/i386/pc_q35.c            |   14 +-
> >>   hw/ppc/spapr.c              |   16 +-
> >>   hw/s390x/s390-virtio-ccw.c  |   15 +-
> >>   include/hw/boards.h         |   13 +-
> >>   include/hw/i386/pc.h        |    3 +
> >>   qapi/machine.json           |    6 +-
> >>   qemu-options.hx             |    4 +-
> >>   tests/unit/meson.build      |    1 +
> >>   tests/unit/test-smp-parse.c | 1117 +++++++++++++++++++++++++++++++++++
> >>   16 files changed, 1338 insertions(+), 137 deletions(-)
> >>   create mode 100644 hw/core/machine-smp.c
> >>   create mode 100644 tests/unit/test-smp-parse.c
> >>

Please add me in CC as I reviewed one of the patch and was in middle
of reviewing few other patches
but missed the latest version.

Thanks,
Pankaj


^ permalink raw reply	[flat|nested] 58+ messages in thread

* Re: [PATCH for-6.2 v2 00/11] machine: smp parsing fixes and improvement
  2021-07-21 13:52     ` Pankaj Gupta
@ 2021-07-22  2:22       ` wangyanan (Y)
  0 siblings, 0 replies; 58+ messages in thread
From: wangyanan (Y) @ 2021-07-22  2:22 UTC (permalink / raw)
  To: Pankaj Gupta
  Cc: Peter Maydell, Andrew Jones, Daniel P . Berrangé,
	Eduardo Habkost, Pierre Morel, Michael S . Tsirkin,
	Cornelia Huck, Richard Henderson, Qemu Developers, Greg Kurz,
	Halil Pasic, Paolo Bonzini, Thomas Huth, Igor Mammedov,
	yuzenghui, wanghaibin.wang, David Gibson

On 2021/7/21 21:52, Pankaj Gupta wrote:
>>> On Mon, Jul 19 2021, Yanan Wang <wangyanan55@huawei.com> wrote:
>>>
>>>> Hi,
>>>>
>>>> This is v2 of the series [1] that I have posted to introduce some smp parsing
>>>> fixes and improvement, much more work has been processed compared to RFC v1.
>>>>
>>>> [1] https://lists.gnu.org/archive/html/qemu-devel/2021-07/msg00259.html
>>>>
>>>> The purpose of this series is to improve/fix the parsing logic. Explicitly
>>>> specifying a CPU topology parameter as zero is not allowed any more, and
>>>> maxcpus is now uniformly used to calculate the omitted parameters. It's also
>>>> suggested that we should start to prefer cores over sockets over threads on
>>>> the newer machine types, which will make the computed virtual topology more
>>>> reflective of the real hardware.
>>>>
>>>> In order to reduce code duplication and ease the code maintenance, smp_parse
>>>> in now converted into a parser generic enough for all arches, so that the PC
>>>> specific one can be removed. It's also convenient to introduce more topology
>>>> members (e.g. cluster) to the generic parser in the future.
>>> Cc:ing Pierre, as he also had been looking at the smp parsing code (for
>>> s390x) recently.
>>>
>>> Also, please keep me on cc: for patches that touch s390x.
>> Sure, I will. Sorry about the missing. :)
>>
>> Thanks,
>> Yanan
>> .
>>>> Finally, a QEMU unit test for the parsing of given SMP configuration is added.
>>>> Since all the parsing logic is in generic function smp_parse(), this test
>>>> passes diffenent SMP configurations to the function and compare the parsing
>>>> result with what is expected. In the test, all possible collections of the
>>>> topology parameters and the corressponding expected results are listed,
>>>> including the valid and invalid ones. The preference of sockets over cores
>>>> and the preference of cores over sockets, and the support of multi-dies are
>>>> also taken into consideration.
>>>>
>>>> ---
>>>>
>>>> Changelogs:
>>>>
>>>> v1->v2:
>>>> - disallow "anything=0" in the smp configuration (Andrew)
>>>> - make function smp_parse() a generic helper for all arches
>>>> - improve the error reporting in the parser
>>>> - start to prefer cores over sockets since 6.2 (Daniel)
>>>> - add a unit test for the smp parsing (Daniel)
>>>>
>>>> ---
>>>>
>>>> Yanan Wang (11):
>>>>     machine: Disallow specifying topology parameters as zero
>>>>     machine: Make smp_parse generic enough for all arches
>>>>     machine: Uniformly use maxcpus to calculate the omitted parameters
>>>>     machine: Use the computed parameters to calculate omitted cpus
>>>>     machine: Improve the error reporting of smp parsing
>>>>     hw: Add compat machines for 6.2
>>>>     machine: Prefer cores over sockets in smp parsing since 6.2
>>>>     machine: Use ms instead of global current_machine in sanity-check
>>>>     machine: Tweak the order of topology members in struct CpuTopology
>>>>     machine: Split out the smp parsing code
>>>>     tests/unit: Add a unit test for smp parsing
>>>>
>>>>    MAINTAINERS                 |    2 +
>>>>    hw/arm/virt.c               |   10 +-
>>>>    hw/core/machine-smp.c       |  124 ++++
>>>>    hw/core/machine.c           |   68 +--
>>>>    hw/core/meson.build         |    1 +
>>>>    hw/i386/pc.c                |   66 +--
>>>>    hw/i386/pc_piix.c           |   15 +-
>>>>    hw/i386/pc_q35.c            |   14 +-
>>>>    hw/ppc/spapr.c              |   16 +-
>>>>    hw/s390x/s390-virtio-ccw.c  |   15 +-
>>>>    include/hw/boards.h         |   13 +-
>>>>    include/hw/i386/pc.h        |    3 +
>>>>    qapi/machine.json           |    6 +-
>>>>    qemu-options.hx             |    4 +-
>>>>    tests/unit/meson.build      |    1 +
>>>>    tests/unit/test-smp-parse.c | 1117 +++++++++++++++++++++++++++++++++++
>>>>    16 files changed, 1338 insertions(+), 137 deletions(-)
>>>>    create mode 100644 hw/core/machine-smp.c
>>>>    create mode 100644 tests/unit/test-smp-parse.c
>>>>
> Please add me in CC as I reviewed one of the patch and was in middle
> of reviewing few other patches
> but missed the latest version.
>
Ok, I will, and thanks for the reviewing. v2 i.e. this version is now 
the latest. :)

Thanks,
Yanan



^ permalink raw reply	[flat|nested] 58+ messages in thread

* Re: [PATCH for-6.2 v2 03/11] machine: Uniformly use maxcpus to calculate the omitted parameters
  2021-07-19 16:36   ` Andrew Jones
@ 2021-07-22  3:00     ` wangyanan (Y)
  0 siblings, 0 replies; 58+ messages in thread
From: wangyanan (Y) @ 2021-07-22  3:00 UTC (permalink / raw)
  To: Andrew Jones
  Cc: Peter Maydell, Thomas Huth, Daniel P. Berrangé,
	Eduardo Habkost, Michael S . Tsirkin, wanghaibin.wang,
	Richard Henderson, qemu-devel, Greg Kurz, Halil Pasic,
	Igor Mammedov, yuzenghui, Paolo Bonzini, David Gibson

On 2021/7/20 0:36, Andrew Jones wrote:
> On Mon, Jul 19, 2021 at 11:20:35AM +0800, Yanan Wang wrote:
>> We are currently using maxcpus to calculate the omitted sockets
>> but using cpus to calculate the omitted cores/threads. This makes
>> cmdlines like:
>>    -smp cpus=8,maxcpus=16
>>    -smp cpus=8,cores=4,maxcpus=16
>>    -smp cpus=8,threads=2,maxcpus=16
>> work fine but the ones like:
>>    -smp cpus=8,sockets=2,maxcpus=16
>>    -smp cpus=8,sockets=2,cores=4,maxcpus=16
>>    -smp cpus=8,sockets=2,threads=2,maxcpus=16
>> break the invalid cpu topology check.
>>
>> Since we require for the valid config that the sum of "sockets * cores
>> * dies * threads" should equal to the maxcpus, we should uniformly use
>> maxcpus to calculate their omitted values.
>>
>> Also the if-branch of "cpus == 0 || sockets == 0" was splited into two
>> branches of "cpus == 0" and "sockets == 0" so that we can clearly read
>> that we are parsing -smp cmdlines with a preference of cpus over sockets
>> over cores over threads.
>>
>> Note: change in this patch won't affect any existing working cmdlines
>> but improves consistency and allow more incomplete configs to be valid.
> We also remove rounding of cores and threads when the math doesn't come
> out right, which could possible start reporting a bad config as invalid
> which worked before. Or were you able to prove that that can't happen with
> your testing?
I also had this concern, but I think that can't happen for sure.

Take the if-branch of "cores == 0" as an example,
Before this patch:
We use cpus to calculate the missing cores and round it up to 1 if the
result is zero, and at last set maxcpus to match cpus if it's omitted.
So the parsing result must have met the two conditions:
1) sockets * cores * threads == maxcpus
2) sockets * cores * threads >= cpus

After this patch:
We start to use maxcpus to calculate the missing cores and also remove
the rounding. For the same config mentioned above, it still works and the
parsing result will also not change, because we will never get a fractional
value of cores (maxcpus is multiple of (sockets * threads) ).

Like the valid config "-smp 8,sockets=16,maxcpus=16", we will get
"cpus=8,sockets=16,cores=1,threads=1,maxcpus=16" before, and
still get the same result after.

Please correct me if I missed something, thanks.
>> Signed-off-by: Yanan Wang <wangyanan55@huawei.com>
>> ---
>>   hw/core/machine.c | 30 +++++++++++++++---------------
>>   1 file changed, 15 insertions(+), 15 deletions(-)
>>
>> diff --git a/hw/core/machine.c b/hw/core/machine.c
>> index ed6712e964..c9f15b15a5 100644
>> --- a/hw/core/machine.c
>> +++ b/hw/core/machine.c
>> @@ -768,24 +768,26 @@ static void smp_parse(MachineState *ms, SMPConfiguration *config, Error **errp)
>>       }
>>   
>>       /* compute missing values, prefer sockets over cores over threads */
>> -    if (cpus == 0 || sockets == 0) {
>> +    maxcpus = maxcpus > 0 ? maxcpus : cpus;
>> +
>> +    if (cpus == 0) {
>> +        sockets = sockets > 0 ? sockets : 1;
>>           cores = cores > 0 ? cores : 1;
>>           threads = threads > 0 ? threads : 1;
>> -        if (cpus == 0) {
>> -            sockets = sockets > 0 ? sockets : 1;
>> -            cpus = sockets * dies * cores * threads;
>> -        } else {
>> -            maxcpus = maxcpus > 0 ? maxcpus : cpus;
>> -            sockets = maxcpus / (dies * cores * threads);
>> -        }
>> +        cpus = sockets * dies * cores * threads;
>> +        maxcpus = maxcpus > 0 ? maxcpus : cpus;
>> +    } else if (sockets == 0) {
>> +        cores = cores > 0 ? cores : 1;
>> +        threads = threads > 0 ? threads : 1;
>> +        sockets = maxcpus / (dies * cores * threads);
>>       } else if (cores == 0) {
>>           threads = threads > 0 ? threads : 1;
>> -        cores = cpus / (sockets * dies * threads);
>> -        cores = cores > 0 ? cores : 1;
>> +        cores = maxcpus / (sockets * dies * threads);
>>       } else if (threads == 0) {
>> -        threads = cpus / (sockets * dies * cores);
>> -        threads = threads > 0 ? threads : 1;
>> -    } else if (sockets * dies * cores * threads < cpus) {
>> +        threads = maxcpus / (sockets * dies * cores);
>> +    }
>> +
>> +    if (sockets * dies * cores * threads < cpus) {
>>           g_autofree char *dies_msg = g_strdup_printf(
>>               mc->smp_dies_supported ? " * dies (%u)" : "", dies);
>>           error_setg(errp, "cpu topology: "
>> @@ -795,8 +797,6 @@ static void smp_parse(MachineState *ms, SMPConfiguration *config, Error **errp)
>>           return;
>>       }
>>   
>> -    maxcpus = maxcpus > 0 ? maxcpus : cpus;
>> -
>>       if (maxcpus < cpus) {
>>           error_setg(errp, "maxcpus must be equal to or greater than smp");
>>           return;
>> -- 
>> 2.19.1
>>
> Reviewed-by: Andrew Jones <drjones@redhat.com>
Thanks,
Yanan
.



^ permalink raw reply	[flat|nested] 58+ messages in thread

* Re: [PATCH for-6.2 v2 04/11] machine: Use the computed parameters to calculate omitted cpus
  2021-07-19 16:42   ` Andrew Jones
@ 2021-07-22  4:42     ` wangyanan (Y)
  2021-07-22 12:27       ` Andrew Jones
  0 siblings, 1 reply; 58+ messages in thread
From: wangyanan (Y) @ 2021-07-22  4:42 UTC (permalink / raw)
  To: Andrew Jones
  Cc: Peter Maydell, Thomas Huth, Daniel P. Berrangé,
	Eduardo Habkost, Michael S . Tsirkin, wanghaibin.wang,
	Richard Henderson, qemu-devel, Greg Kurz, Halil Pasic,
	Igor Mammedov, yuzenghui, Paolo Bonzini, David Gibson

On 2021/7/20 0:42, Andrew Jones wrote:
> On Mon, Jul 19, 2021 at 11:20:36AM +0800, Yanan Wang wrote:
>> Currently we directly calculate the omitted cpus based on the already
>> provided parameters. This makes some cmdlines like:
>>    -smp maxcpus=16
>>    -smp sockets=2,maxcpus=16
>>    -smp sockets=2,dies=2,maxcpus=16
>>    -smp sockets=2,cores=4,maxcpus=16
>> not work. We should probably use the computed paramters to calculate
>> cpus when maxcpus is provided while cpus is omitted, which will make
>> above configs start to work.
>>
>> Note: change in this patch won't affect any existing working cmdlines
>> but allows more incomplete configs to be valid.
>>
>> Signed-off-by: Yanan Wang <wangyanan55@huawei.com>
>> ---
>>   hw/core/machine.c | 17 +++++++++--------
>>   1 file changed, 9 insertions(+), 8 deletions(-)
>>
>> diff --git a/hw/core/machine.c b/hw/core/machine.c
>> index c9f15b15a5..668f0a1553 100644
>> --- a/hw/core/machine.c
>> +++ b/hw/core/machine.c
>> @@ -767,26 +767,27 @@ static void smp_parse(MachineState *ms, SMPConfiguration *config, Error **errp)
>>           return;
>>       }
>>   
>> -    /* compute missing values, prefer sockets over cores over threads */
>>       maxcpus = maxcpus > 0 ? maxcpus : cpus;
>>   
>> -    if (cpus == 0) {
>> -        sockets = sockets > 0 ? sockets : 1;
>> -        cores = cores > 0 ? cores : 1;
>> -        threads = threads > 0 ? threads : 1;
>> -        cpus = sockets * dies * cores * threads;
>> -        maxcpus = maxcpus > 0 ? maxcpus : cpus;
>> -    } else if (sockets == 0) {
>> +    /* compute missing values, prefer sockets over cores over threads */
>> +    if (sockets == 0) {
>>           cores = cores > 0 ? cores : 1;
>>           threads = threads > 0 ? threads : 1;
>>           sockets = maxcpus / (dies * cores * threads);
>> +        sockets = sockets > 0 ? sockets : 1;
>>       } else if (cores == 0) {
>>           threads = threads > 0 ? threads : 1;
>>           cores = maxcpus / (sockets * dies * threads);
>> +        cores = cores > 0 ? cores : 1;
>>       } else if (threads == 0) {
>>           threads = maxcpus / (sockets * dies * cores);
>> +        threads = threads > 0 ? threads : 1;
>>       }
> I didn't think we wanted this rounding which this patch adds back into
> cores and threads and now also sockets.
Firstly, I think we can agree that with or without the rounding, the invalid
configs will still be reported as invalid. So the only difference is in 
the err
message (either report 0 or 1 of a fractional parameter). :)

I added back the rounding because this patch indeed need it, we start
to use the computed parameters to calculate cpus, so we have to ensure
that the computed parameters are at least 1. If both cpus and maxcpus
are omitted (e.g. -smp sockets=16), without the rounding we will get
zeroed cpus and maxcpus, and with the rounding we will get valid result
like "cpus=16,sockets=16,cores=1,threads=1,maxcpus=16".

If a "0" or "1" in the error message doesn't make so much difference as
assumed for the error reporting, I suggest that we probably can keep the
rounding which makes the parser code concise.
>>   
>> +    /* use the computed parameters to calculate the omitted cpus */
>> +    cpus = cpus > 0 ? cpus : sockets * dies * cores * threads;
>> +    maxcpus = maxcpus > 0 ? maxcpus : cpus;
> It doesn't really matter, but I think I'd rather write this like
>
>   maxcpus = maxcpus > 0 ? maxcpus : sockets * dies * cores * threads;
>   cpus = cpus > 0 ? cpus : maxcpus;
Yes, there is no functional difference. But I think maybe we'd better keep
some consistence with the "maxcpus = maxcpus > 0 ? maxcpus : cpus"
at top this function and also with the smp doc in qemu-option.hx i.e.
"If omitted the maximum number of CPUs will be set to match the initial
CPU count" ?

Thanks,
Yanan
.
>> +
>>       if (sockets * dies * cores * threads < cpus) {
>>           g_autofree char *dies_msg = g_strdup_printf(
>>               mc->smp_dies_supported ? " * dies (%u)" : "", dies);
>> -- 
>> 2.19.1
>>
> Thanks,
> drew
>
> .



^ permalink raw reply	[flat|nested] 58+ messages in thread

* Re: [PATCH for-6.2 v2 07/11] machine: Prefer cores over sockets in smp parsing since 6.2
  2021-07-19  3:40   ` David Gibson
@ 2021-07-22  5:22     ` wangyanan (Y)
  0 siblings, 0 replies; 58+ messages in thread
From: wangyanan (Y) @ 2021-07-22  5:22 UTC (permalink / raw)
  To: David Gibson
  Cc: Peter Maydell, Andrew Jones, Daniel P. Berrangé,
	Eduardo Habkost, Michael S . Tsirkin, wanghaibin.wang,
	Richard Henderson, qemu-devel, Greg Kurz, Halil Pasic, yuzenghui,
	Igor Mammedov, Thomas Huth, Paolo Bonzini

On 2021/7/19 11:40, David Gibson wrote:
> On Mon, Jul 19, 2021 at 11:20:39AM +0800, Yanan Wang wrote:
>> In the real SMP hardware topology world, it's much more likely that
>> we have high cores-per-socket counts and few sockets totally. While
>> the current preference of sockets over cores in smp parsing results
>> in a virtual cpu topology with low cores-per-sockets counts and a
>> large number of sockets, which is just contrary to the real world.
>>
>> Given that it is better to make the virtual cpu topology be more
>> reflective of the real world and also for the sake of compatibility,
>> we start to prefer cores over sockets over threads in smp parsing
>> since machine type 6.2 for different arches.
>>
>> In this patch, a boolean "smp_prefer_sockets" is added, and we only
>> enable the old preference on older machines and enable the new one
>> since type 6.2 for all arches by using the machine compat mechanism.
>>
>> Suggested-by: Daniel P. Berrange <berrange@redhat.com>
>> Signed-off-by: Yanan Wang <wangyanan55@huawei.com>
> ppc parts
>
> Acked-by: David Gibson <david@gibson.dropbear.id.au>
>
> Note that for the pseries machine types, being paravirtual, there is
> essentially no guest visible difference between "cores" and "sockets.
I see. When the difference start to make some sense for the pseries guest,
then I think high-count cores may also be preferred and we will already have
the preference of cores at that time because of today's work.

Thanks,
Yanan
.
>> ---
>>   hw/arm/virt.c              |  1 +
>>   hw/core/machine.c          | 59 +++++++++++++++++++++++++++++---------
>>   hw/i386/pc_piix.c          |  1 +
>>   hw/i386/pc_q35.c           |  1 +
>>   hw/ppc/spapr.c             |  1 +
>>   hw/s390x/s390-virtio-ccw.c |  1 +
>>   include/hw/boards.h        |  1 +
>>   qemu-options.hx            |  4 ++-
>>   8 files changed, 55 insertions(+), 14 deletions(-)
>>
>> diff --git a/hw/arm/virt.c b/hw/arm/virt.c
>> index 01165f7f53..7babea40dc 100644
>> --- a/hw/arm/virt.c
>> +++ b/hw/arm/virt.c
>> @@ -2797,6 +2797,7 @@ static void virt_machine_6_1_options(MachineClass *mc)
>>   {
>>       virt_machine_6_2_options(mc);
>>       compat_props_add(mc->compat_props, hw_compat_6_1, hw_compat_6_1_len);
>> +    mc->smp_prefer_sockets = true;
>>   }
>>   DEFINE_VIRT_MACHINE(6, 1)
>>   
>> diff --git a/hw/core/machine.c b/hw/core/machine.c
>> index 63439c4a6d..c074425015 100644
>> --- a/hw/core/machine.c
>> +++ b/hw/core/machine.c
>> @@ -744,6 +744,22 @@ void machine_set_cpu_numa_node(MachineState *machine,
>>       }
>>   }
>>   
>> +/*
>> + * smp_parse - Generic function used to parse the given SMP configuration
>> + *
>> + * The topology parameters must be specified equal to or great than one
>> + * or just omitted, explicit configuration like "cpus=0" is not allowed.
>> + * The omitted parameters will be calculated based on the provided ones.
>> + *
>> + * maxcpus will default to the value of cpus if omitted and will be used
>> + * to compute the missing sockets/cores/threads. cpus will be calculated
>> + * from the computed parametrs if omitted.
>> + *
>> + * In calculation of omitted arch-netural sockets/cores/threads, we prefer
>> + * sockets over cores over threads before 6.2, while prefer cores over
>> + * sockets over threads since 6.2 on. The arch-specific dies will directly
>> + * default to 1 if omitted.
>> + */
>>   static void smp_parse(MachineState *ms, SMPConfiguration *config, Error **errp)
>>   {
>>       MachineClass *mc = MACHINE_GET_CLASS(ms);
>> @@ -772,19 +788,36 @@ static void smp_parse(MachineState *ms, SMPConfiguration *config, Error **errp)
>>   
>>       maxcpus = maxcpus > 0 ? maxcpus : cpus;
>>   
>> -    /* compute missing values, prefer sockets over cores over threads */
>> -    if (sockets == 0) {
>> -        cores = cores > 0 ? cores : 1;
>> -        threads = threads > 0 ? threads : 1;
>> -        sockets = maxcpus / (dies * cores * threads);
>> -        sockets = sockets > 0 ? sockets : 1;
>> -    } else if (cores == 0) {
>> -        threads = threads > 0 ? threads : 1;
>> -        cores = maxcpus / (sockets * dies * threads);
>> -        cores = cores > 0 ? cores : 1;
>> -    } else if (threads == 0) {
>> -        threads = maxcpus / (sockets * dies * cores);
>> -        threads = threads > 0 ? threads : 1;
>> +    /* prefer sockets over cores over threads before 6.2 */
>> +    if (mc->smp_prefer_sockets) {
>> +        if (sockets == 0) {
>> +            cores = cores > 0 ? cores : 1;
>> +            threads = threads > 0 ? threads : 1;
>> +            sockets = maxcpus / (dies * cores * threads);
>> +            sockets = sockets > 0 ? sockets : 1;
>> +        } else if (cores == 0) {
>> +            threads = threads > 0 ? threads : 1;
>> +            cores = maxcpus / (sockets * dies * threads);
>> +            cores = cores > 0 ? cores : 1;
>> +        } else if (threads == 0) {
>> +            threads = maxcpus / (sockets * dies * cores);
>> +            threads = threads > 0 ? threads : 1;
>> +        }
>> +    /* prefer cores over sockets over threads since 6.2 */
>> +    } else {
>> +        if (cores == 0) {
>> +            sockets = sockets > 0 ? sockets : 1;
>> +            threads = threads > 0 ? threads : 1;
>> +            cores = maxcpus / (sockets * dies * threads);
>> +            cores = cores > 0 ? cores : 1;
>> +        } else if (sockets == 0) {
>> +            threads = threads > 0 ? threads : 1;
>> +            sockets = maxcpus / (dies * cores * threads);
>> +            sockets = sockets > 0 ? sockets : 1;
>> +        } else if (threads == 0) {
>> +            threads = maxcpus / (sockets * dies * cores);
>> +            threads = threads > 0 ? threads : 1;
>> +        }
>>       }
>>   
>>       /* use the computed parameters to calculate the omitted cpus */
>> diff --git a/hw/i386/pc_piix.c b/hw/i386/pc_piix.c
>> index fd5c2277f2..9b811fc6ca 100644
>> --- a/hw/i386/pc_piix.c
>> +++ b/hw/i386/pc_piix.c
>> @@ -432,6 +432,7 @@ static void pc_i440fx_6_1_machine_options(MachineClass *m)
>>       m->is_default = false;
>>       compat_props_add(m->compat_props, hw_compat_6_1, hw_compat_6_1_len);
>>       compat_props_add(m->compat_props, pc_compat_6_1, pc_compat_6_1_len);
>> +    m->smp_prefer_sockets = true;
>>   }
>>   
>>   DEFINE_I440FX_MACHINE(v6_1, "pc-i440fx-6.1", NULL,
>> diff --git a/hw/i386/pc_q35.c b/hw/i386/pc_q35.c
>> index b45903b15e..88efb7fde4 100644
>> --- a/hw/i386/pc_q35.c
>> +++ b/hw/i386/pc_q35.c
>> @@ -372,6 +372,7 @@ static void pc_q35_6_1_machine_options(MachineClass *m)
>>       m->alias = NULL;
>>       compat_props_add(m->compat_props, hw_compat_6_1, hw_compat_6_1_len);
>>       compat_props_add(m->compat_props, pc_compat_6_1, pc_compat_6_1_len);
>> +    m->smp_prefer_sockets = true;
>>   }
>>   
>>   DEFINE_Q35_MACHINE(v6_1, "pc-q35-6.1", NULL,
>> diff --git a/hw/ppc/spapr.c b/hw/ppc/spapr.c
>> index d39fd4e644..a481fade51 100644
>> --- a/hw/ppc/spapr.c
>> +++ b/hw/ppc/spapr.c
>> @@ -4702,6 +4702,7 @@ static void spapr_machine_6_1_class_options(MachineClass *mc)
>>   {
>>       spapr_machine_6_2_class_options(mc);
>>       compat_props_add(mc->compat_props, hw_compat_6_1, hw_compat_6_1_len);
>> +    mc->smp_prefer_sockets = true;
>>   }
>>   
>>   DEFINE_SPAPR_MACHINE(6_1, "6.1", false);
>> diff --git a/hw/s390x/s390-virtio-ccw.c b/hw/s390x/s390-virtio-ccw.c
>> index 4d25278cf2..b40e647883 100644
>> --- a/hw/s390x/s390-virtio-ccw.c
>> +++ b/hw/s390x/s390-virtio-ccw.c
>> @@ -809,6 +809,7 @@ static void ccw_machine_6_1_class_options(MachineClass *mc)
>>   {
>>       ccw_machine_6_2_class_options(mc);
>>       compat_props_add(mc->compat_props, hw_compat_6_1, hw_compat_6_1_len);
>> +    mc->smp_prefer_sockets = true;
>>   }
>>   DEFINE_CCW_MACHINE(6_1, "6.1", false);
>>   
>> diff --git a/include/hw/boards.h b/include/hw/boards.h
>> index 2832f0f8aa..8df885c9d2 100644
>> --- a/include/hw/boards.h
>> +++ b/include/hw/boards.h
>> @@ -247,6 +247,7 @@ struct MachineClass {
>>       bool nvdimm_supported;
>>       bool numa_mem_supported;
>>       bool smp_dies_supported;
>> +    bool smp_prefer_sockets;
>>       bool auto_enable_numa;
>>       const char *default_ram_id;
>>   
>> diff --git a/qemu-options.hx b/qemu-options.hx
>> index 0c9ddc0274..6ef57e838c 100644
>> --- a/qemu-options.hx
>> +++ b/qemu-options.hx
>> @@ -227,7 +227,9 @@ SRST
>>       from those which are given. Historically preference was given to the
>>       coarsest topology parameters when computing missing values (ie sockets
>>       preferred over cores, which were preferred over threads), however, this
>> -    behaviour is considered liable to change.
>> +    behaviour is considered liable to change. The historical preference of
>> +    sockets over cores over threads works before 6.2, and a new preference
>> +    of cores over sockets over threads starts to work since 6.2 on.
>>   ERST
>>   
>>   DEF("numa", HAS_ARG, QEMU_OPTION_numa,



^ permalink raw reply	[flat|nested] 58+ messages in thread

* Re: [PATCH for-6.2 v2 07/11] machine: Prefer cores over sockets in smp parsing since 6.2
  2021-07-19 17:13   ` Andrew Jones
@ 2021-07-22  5:32     ` wangyanan (Y)
  0 siblings, 0 replies; 58+ messages in thread
From: wangyanan (Y) @ 2021-07-22  5:32 UTC (permalink / raw)
  To: Andrew Jones
  Cc: Peter Maydell, Thomas Huth, Daniel P. Berrangé,
	Eduardo Habkost, Michael S . Tsirkin, wanghaibin.wang,
	Richard Henderson, qemu-devel, Greg Kurz, Halil Pasic,
	Igor Mammedov, yuzenghui, Paolo Bonzini, David Gibson

On 2021/7/20 1:13, Andrew Jones wrote:
> On Mon, Jul 19, 2021 at 11:20:39AM +0800, Yanan Wang wrote:
>> In the real SMP hardware topology world, it's much more likely that
>> we have high cores-per-socket counts and few sockets totally. While
>> the current preference of sockets over cores in smp parsing results
>> in a virtual cpu topology with low cores-per-sockets counts and a
>> large number of sockets, which is just contrary to the real world.
>>
>> Given that it is better to make the virtual cpu topology be more
>> reflective of the real world and also for the sake of compatibility,
>> we start to prefer cores over sockets over threads in smp parsing
>> since machine type 6.2 for different arches.
>>
>> In this patch, a boolean "smp_prefer_sockets" is added, and we only
>> enable the old preference on older machines and enable the new one
>> since type 6.2 for all arches by using the machine compat mechanism.
>>
>> Suggested-by: Daniel P. Berrange <berrange@redhat.com>
>> Signed-off-by: Yanan Wang <wangyanan55@huawei.com>
>> ---
>>   hw/arm/virt.c              |  1 +
>>   hw/core/machine.c          | 59 +++++++++++++++++++++++++++++---------
>>   hw/i386/pc_piix.c          |  1 +
>>   hw/i386/pc_q35.c           |  1 +
>>   hw/ppc/spapr.c             |  1 +
>>   hw/s390x/s390-virtio-ccw.c |  1 +
>>   include/hw/boards.h        |  1 +
>>   qemu-options.hx            |  4 ++-
>>   8 files changed, 55 insertions(+), 14 deletions(-)
>>
>> diff --git a/hw/arm/virt.c b/hw/arm/virt.c
>> index 01165f7f53..7babea40dc 100644
>> --- a/hw/arm/virt.c
>> +++ b/hw/arm/virt.c
>> @@ -2797,6 +2797,7 @@ static void virt_machine_6_1_options(MachineClass *mc)
>>   {
>>       virt_machine_6_2_options(mc);
>>       compat_props_add(mc->compat_props, hw_compat_6_1, hw_compat_6_1_len);
>> +    mc->smp_prefer_sockets = true;
>>   }
>>   DEFINE_VIRT_MACHINE(6, 1)
>>   
>> diff --git a/hw/core/machine.c b/hw/core/machine.c
>> index 63439c4a6d..c074425015 100644
>> --- a/hw/core/machine.c
>> +++ b/hw/core/machine.c
>> @@ -744,6 +744,22 @@ void machine_set_cpu_numa_node(MachineState *machine,
>>       }
>>   }
>>   
>> +/*
>> + * smp_parse - Generic function used to parse the given SMP configuration
>> + *
>> + * The topology parameters must be specified equal to or great than one
>> + * or just omitted, explicit configuration like "cpus=0" is not allowed.
>> + * The omitted parameters will be calculated based on the provided ones.
>> + *
>> + * maxcpus will default to the value of cpus if omitted and will be used
>> + * to compute the missing sockets/cores/threads. cpus will be calculated
>> + * from the computed parametrs if omitted.
> parameters
>
> Or how about something like this:
>
> When both maxcpus and cpus are omitted maxcpus will be calculated from the
> given parameters and cpus will be set equal to maxcpus. When only one of
> maxcpus and cpus is given then the omitted one will be set to its given
> counterpart's value. Both maxcpus and cpus may be specified, but cpus must
> be less than or equal to maxcpus.
Yes, this is much clearer.
Now I understand why you suggested in patch #4 to change the format from
   cpus = cpus > 0 ? cpus : sockets * dies * cores * threads;
   maxcpus = maxcpus > 0 ? maxcpus : cpus;
to
   maxcpus = maxcpus > 0 ? maxcpus : sockets * dies * cores * threads;
   cpus = cpus > 0 ? cpus : maxcpus;

What you suggest seems more reasonable according to above doc,
I will adjust the related part as you suggested.
>> + *
>> + * In calculation of omitted arch-netural sockets/cores/threads, we prefer
>> + * sockets over cores over threads before 6.2, while prefer cores over
> while preferring
>
>> + * sockets over threads since 6.2 on. The arch-specific dies will directly
> s/on//
Right.
>> + * default to 1 if omitted.
>> + */
>>   static void smp_parse(MachineState *ms, SMPConfiguration *config, Error **errp)
>>   {
>>       MachineClass *mc = MACHINE_GET_CLASS(ms);
>> @@ -772,19 +788,36 @@ static void smp_parse(MachineState *ms, SMPConfiguration *config, Error **errp)
>>   
>>       maxcpus = maxcpus > 0 ? maxcpus : cpus;
>>   
>> -    /* compute missing values, prefer sockets over cores over threads */
>> -    if (sockets == 0) {
>> -        cores = cores > 0 ? cores : 1;
>> -        threads = threads > 0 ? threads : 1;
>> -        sockets = maxcpus / (dies * cores * threads);
>> -        sockets = sockets > 0 ? sockets : 1;
>> -    } else if (cores == 0) {
>> -        threads = threads > 0 ? threads : 1;
>> -        cores = maxcpus / (sockets * dies * threads);
>> -        cores = cores > 0 ? cores : 1;
>> -    } else if (threads == 0) {
>> -        threads = maxcpus / (sockets * dies * cores);
>> -        threads = threads > 0 ? threads : 1;
>> +    /* prefer sockets over cores over threads before 6.2 */
>> +    if (mc->smp_prefer_sockets) {
> please move the comment into the if, so...
>
>> +        if (sockets == 0) {
>> +            cores = cores > 0 ? cores : 1;
>> +            threads = threads > 0 ? threads : 1;
>> +            sockets = maxcpus / (dies * cores * threads);
>> +            sockets = sockets > 0 ? sockets : 1;
>> +        } else if (cores == 0) {
>> +            threads = threads > 0 ? threads : 1;
>> +            cores = maxcpus / (sockets * dies * threads);
>> +            cores = cores > 0 ? cores : 1;
>> +        } else if (threads == 0) {
>> +            threads = maxcpus / (sockets * dies * cores);
>> +            threads = threads > 0 ? threads : 1;
>> +        }
>> +    /* prefer cores over sockets over threads since 6.2 */
> ...here we can put the comment in the else
Ok
>> +    } else {
>> +        if (cores == 0) {
>> +            sockets = sockets > 0 ? sockets : 1;
>> +            threads = threads > 0 ? threads : 1;
>> +            cores = maxcpus / (sockets * dies * threads);
>> +            cores = cores > 0 ? cores : 1;
>> +        } else if (sockets == 0) {
>> +            threads = threads > 0 ? threads : 1;
>> +            sockets = maxcpus / (dies * cores * threads);
>> +            sockets = sockets > 0 ? sockets : 1;
>> +        } else if (threads == 0) {
>> +            threads = maxcpus / (sockets * dies * cores);
>> +            threads = threads > 0 ? threads : 1;
>> +        }
>>       }
>>   
>>       /* use the computed parameters to calculate the omitted cpus */
>> diff --git a/hw/i386/pc_piix.c b/hw/i386/pc_piix.c
>> index fd5c2277f2..9b811fc6ca 100644
>> --- a/hw/i386/pc_piix.c
>> +++ b/hw/i386/pc_piix.c
>> @@ -432,6 +432,7 @@ static void pc_i440fx_6_1_machine_options(MachineClass *m)
>>       m->is_default = false;
>>       compat_props_add(m->compat_props, hw_compat_6_1, hw_compat_6_1_len);
>>       compat_props_add(m->compat_props, pc_compat_6_1, pc_compat_6_1_len);
>> +    m->smp_prefer_sockets = true;
>>   }
>>   
>>   DEFINE_I440FX_MACHINE(v6_1, "pc-i440fx-6.1", NULL,
>> diff --git a/hw/i386/pc_q35.c b/hw/i386/pc_q35.c
>> index b45903b15e..88efb7fde4 100644
>> --- a/hw/i386/pc_q35.c
>> +++ b/hw/i386/pc_q35.c
>> @@ -372,6 +372,7 @@ static void pc_q35_6_1_machine_options(MachineClass *m)
>>       m->alias = NULL;
>>       compat_props_add(m->compat_props, hw_compat_6_1, hw_compat_6_1_len);
>>       compat_props_add(m->compat_props, pc_compat_6_1, pc_compat_6_1_len);
>> +    m->smp_prefer_sockets = true;
>>   }
>>   
>>   DEFINE_Q35_MACHINE(v6_1, "pc-q35-6.1", NULL,
>> diff --git a/hw/ppc/spapr.c b/hw/ppc/spapr.c
>> index d39fd4e644..a481fade51 100644
>> --- a/hw/ppc/spapr.c
>> +++ b/hw/ppc/spapr.c
>> @@ -4702,6 +4702,7 @@ static void spapr_machine_6_1_class_options(MachineClass *mc)
>>   {
>>       spapr_machine_6_2_class_options(mc);
>>       compat_props_add(mc->compat_props, hw_compat_6_1, hw_compat_6_1_len);
>> +    mc->smp_prefer_sockets = true;
>>   }
>>   
>>   DEFINE_SPAPR_MACHINE(6_1, "6.1", false);
>> diff --git a/hw/s390x/s390-virtio-ccw.c b/hw/s390x/s390-virtio-ccw.c
>> index 4d25278cf2..b40e647883 100644
>> --- a/hw/s390x/s390-virtio-ccw.c
>> +++ b/hw/s390x/s390-virtio-ccw.c
>> @@ -809,6 +809,7 @@ static void ccw_machine_6_1_class_options(MachineClass *mc)
>>   {
>>       ccw_machine_6_2_class_options(mc);
>>       compat_props_add(mc->compat_props, hw_compat_6_1, hw_compat_6_1_len);
>> +    mc->smp_prefer_sockets = true;
>>   }
>>   DEFINE_CCW_MACHINE(6_1, "6.1", false);
>>   
>> diff --git a/include/hw/boards.h b/include/hw/boards.h
>> index 2832f0f8aa..8df885c9d2 100644
>> --- a/include/hw/boards.h
>> +++ b/include/hw/boards.h
>> @@ -247,6 +247,7 @@ struct MachineClass {
>>       bool nvdimm_supported;
>>       bool numa_mem_supported;
>>       bool smp_dies_supported;
>> +    bool smp_prefer_sockets;
>>       bool auto_enable_numa;
>>       const char *default_ram_id;
>>   
>> diff --git a/qemu-options.hx b/qemu-options.hx
>> index 0c9ddc0274..6ef57e838c 100644
>> --- a/qemu-options.hx
>> +++ b/qemu-options.hx
>> @@ -227,7 +227,9 @@ SRST
>>       from those which are given. Historically preference was given to the
>>       coarsest topology parameters when computing missing values (ie sockets
>>       preferred over cores, which were preferred over threads), however, this
>> -    behaviour is considered liable to change.
>> +    behaviour is considered liable to change. The historical preference of
>> +    sockets over cores over threads works before 6.2, and a new preference
>> +    of cores over sockets over threads starts to work since 6.2 on.
> Prior to 6.2 the preference was sockets over cores over threads. Since 6.2
> the preference is cores over sockets over threads.
It's more concise and clearer.

Thanks,
Yanan
.
>>   ERST
>>   
>>   DEF("numa", HAS_ARG, QEMU_OPTION_numa,
>> -- 
>> 2.19.1
>>
> Thanks,
> drew
>
> .



^ permalink raw reply	[flat|nested] 58+ messages in thread

* Re: [PATCH for-6.2 v2 11/11] tests/unit: Add a unit test for smp parsing
  2021-07-19 18:57   ` Andrew Jones
@ 2021-07-22  6:15     ` wangyanan (Y)
  2021-07-22 13:12       ` Andrew Jones
  0 siblings, 1 reply; 58+ messages in thread
From: wangyanan (Y) @ 2021-07-22  6:15 UTC (permalink / raw)
  To: Andrew Jones
  Cc: Peter Maydell, Thomas Huth, Daniel P. Berrangé,
	Eduardo Habkost, Michael S . Tsirkin, Richard Henderson,
	qemu-devel, Greg Kurz, Halil Pasic, Paolo Bonzini, Igor Mammedov,
	yuzenghui, wanghaibin.wang, David Gibson

On 2021/7/20 2:57, Andrew Jones wrote:
> On Mon, Jul 19, 2021 at 11:20:43AM +0800, Yanan Wang wrote:
>> Add a QEMU unit test for the parsing of given SMP configuration.
>> Since all the parsing logic is in generic function smp_parse(),
>> this test passes diffenent SMP configurations to the function
>> and compare the parsing result with what is expected.
>>
>> In the test, all possible collections of the topology parameters
>> and the corressponding expected results are listed, including the
>> valid and invalid ones.
>>
>> The preference of sockets over cores and the preference of cores
>> over sockets, and the support of multi-dies are also considered.
>>
>> Signed-off-by: Yanan Wang <wangyanan55@huawei.com>
>> ---
>>   MAINTAINERS                 |    1 +
>>   tests/unit/meson.build      |    1 +
>>   tests/unit/test-smp-parse.c | 1117 +++++++++++++++++++++++++++++++++++
>>   3 files changed, 1119 insertions(+)
>>   create mode 100644 tests/unit/test-smp-parse.c
>>
>> diff --git a/MAINTAINERS b/MAINTAINERS
>> index 70633e3bf4..160dba2e57 100644
>> --- a/MAINTAINERS
>> +++ b/MAINTAINERS
>> @@ -1636,6 +1636,7 @@ F: include/hw/boards.h
>>   F: include/hw/core/cpu.h
>>   F: include/hw/cpu/cluster.h
>>   F: include/sysemu/numa.h
>> +F: tests/unit/test-smp-parse.c
>>   T: git https://gitlab.com/ehabkost/qemu.git machine-next
>>   
>>   Xtensa Machines
>> diff --git a/tests/unit/meson.build b/tests/unit/meson.build
>> index 3e0504dd21..694a924627 100644
>> --- a/tests/unit/meson.build
>> +++ b/tests/unit/meson.build
>> @@ -44,6 +44,7 @@ tests = {
>>     'test-uuid': [],
>>     'ptimer-test': ['ptimer-test-stubs.c', meson.source_root() / 'hw/core/ptimer.c'],
>>     'test-qapi-util': [],
>> +  'test-smp-parse': [qom, meson.source_root() / 'hw/core/machine-smp.c'],
>>   }
>>   
>>   if have_system or have_tools
>> diff --git a/tests/unit/test-smp-parse.c b/tests/unit/test-smp-parse.c
>> new file mode 100644
>> index 0000000000..bc1d324c3d
>> --- /dev/null
>> +++ b/tests/unit/test-smp-parse.c
>> @@ -0,0 +1,1117 @@
>> +/*
>> + * SMP parsing unit-tests
>> + *
>> + * Copyright (C) 2021, Huawei, Inc.
>> + *
>> + * Authors:
>> + *  Yanan Wang <wangyanan55@huawei.com>
>> + *
>> + * This work is licensed under the terms of the GNU LGPL, version 2.1 or later.
>> + * See the COPYING.LIB file in the top-level directory.
>> + */
>> +
>> +#include "qemu/osdep.h"
>> +#include "qom/object.h"
>> +#include "qemu/module.h"
>> +#include "qapi/error.h"
>> +
>> +#include "hw/boards.h"
>> +
>> +#define T true
>> +#define F false
>> +
>> +/**
>> + * SMPTestData:
>> + * @config - the given SMP configuration for parsing
>> + * @should_be_valid - whether the given configuration is supposed to be valid
>> + * @expect - the CPU topology info expected to be parsed out
>> + */
>> +typedef struct SMPTestData {
>> +    SMPConfiguration config;
>> +    bool should_be_valid;
> Long way to say 'valid'.
Indeed..., "valid" should be enough.
>> +    CpuTopology expect;
>> +} SMPTestData;
>> +
>> +/* the specific machine type info for this test */
>> +static const TypeInfo smp_machine_info = {
>> +    .name = TYPE_MACHINE,
>> +    .parent = TYPE_OBJECT,
>> +    .class_size = sizeof(MachineClass),
>> +    .instance_size = sizeof(MachineState),
>> +};
>> +
>> +/*
>> + * prefer sockets over cores over threads before 6.2.
>> + * all possible SMP configurations and the corressponding expected outputs
> corresponding (please run spell check on your commit messages)
>
Ok, I missed the check.
>> + * are listed for testing, including the valid and invalid ones.
>> + */
>> +static struct SMPTestData prefer_sockets[] = {
>> +    {
>> +        /* config: no smp configuration provided
>> +         * expect: cpus=1,sockets=1,dies=1,cores=1,threads=1,maxcpus=1 */
>> +        .config = (SMPConfiguration) { F, 0, F, 0, F, 0, F, 0, F, 0, F, 0 },
> SMPConfiguration and CpuTopology have named fields so we could drop the
> 'expect: ...' comment line and instead do
>
>   {
>    /* no configuration provided */
>    .config = { .has_cpus = F, .has_maxcpus = F, ... },
>    .valid = T,
>    .expect = { .sockets = 1, .cores = 1, ... },
>   }, {
>    ...
>   }
>
> which may be easier to maintain. OTOH, the concise form this approach has
> is also nice.
I tried the structure initialization with explicit name fields in it 
like above,
actually we are supposed to do in this way so that we don't have to worry
about the order change of the structure members.

But this would break the 80-char line limit or introduce more lines for
for each SMP configuration. If this is not a real problem, I also prefer
above format.
> I don't think you should need the casts in the assignments
> though.
Yes, the casts may be unnecessary, will remove them.
>
>> +        .should_be_valid = true,
>> +        .expect = (CpuTopology) { 1, 1, 1, 1, 1, 1 },
>> +    }, {
>> +        /* config: -smp 8
>> +         * expect: cpus=8,sockets=8,dies=1,cores=1,threads=1,maxcpus=8 */
>> +        .config = (SMPConfiguration) { T, 8, F, 0, F, 0, F, 0, F, 0, F, 0 },
>> +        .should_be_valid = true,
>> +        .expect = (CpuTopology) { 8, 8, 1, 1, 1, 8 },
>> +    }, {
>> +        /* config: -smp sockets=2
>> +         * expect: cpus=2,sockets=2,dies=1,cores=1,threads=1,maxcpus=2 */
>> +        .config = (SMPConfiguration) { F, 0, T, 2, F, 0, F, 0, F, 0, F, 0 },
>> +        .should_be_valid = true,
>> +        .expect = (CpuTopology) { 2, 2, 1, 1, 1, 2 },
>> +    }, {
>> +        /* config: -smp cores=4
>> +         * expect: cpus=4,sockets=1,dies=1,cores=4,threads=1,maxcpus=4 */
>> +        .config = (SMPConfiguration) { F, 0, F, 0, F, 0, T, 4, F, 0, F, 0 },
>> +        .should_be_valid = true,
>> +        .expect = (CpuTopology) { 4, 1, 1, 4, 1, 4 },
>> +    }, {
>> +        /* config: -smp threads=2
>> +         * expect: cpus=2,sockets=1,dies=1,cores=1,threads=2,maxcpus=2 */
>> +        .config = (SMPConfiguration) { F, 0, F, 0, F, 0, F, 0, T, 2, F, 0 },
>> +        .should_be_valid = true,
>> +        .expect = (CpuTopology) { 2, 1, 1, 1, 2, 2 },
>> +    }, {
>> +        /* config: -smp maxcpus=16
>> +         * expect: cpus=16,sockets=16,dies=1,cores=1,threads=1,maxcpus=16 */
>> +        .config = (SMPConfiguration) { F, 0, F, 0, F, 0, F, 0, F, 0, T, 16 },
>> +        .should_be_valid = true,
>> +        .expect = (CpuTopology) { 16, 16, 1, 1, 1, 16 },
>> +    }, {
>> +        /* config: -smp 8,sockets=2
>> +         * expect: cpus=8,sockets=2,dies=1,cores=4,threads=1,maxcpus=8 */
>> +        .config = (SMPConfiguration) { T, 8, T, 2, F, 0, F, 0, F, 0, F, 0 },
>> +        .should_be_valid = true,
>> +        .expect = (CpuTopology) { 8, 2, 1, 4, 1, 8 },
>> +    }, {
>> +        /* config: -smp 8,cores=4
>> +         * expect: cpus=8,sockets=2,dies=1,cores=4,threads=1,maxcpus=8 */
>> +        .config = (SMPConfiguration) { T, 8, F, 0, F, 0, T, 4, F, 0, F, 0 },
>> +        .should_be_valid = true,
>> +        .expect = (CpuTopology) { 8, 2, 1, 4, 1, 8 },
>> +    }, {
>> +        /* config: -smp 8,threads=2
>> +         * expect: cpus=8,sockets=4,dies=1,cores=1,threads=2,maxcpus=8 */
>> +        .config = (SMPConfiguration) { T, 8, F, 0, F, 0, F, 0, T, 2, F, 0 },
>> +        .should_be_valid = true,
>> +        .expect = (CpuTopology) { 8, 4, 1, 1, 2, 8 },
>> +    }, {
>> +        /* config: -smp 8,maxcpus=16
>> +         * expect: cpus=8,sockets=16,dies=1,cores=1,threads=1,maxcpus=16 */
>> +        .config = (SMPConfiguration) { T, 8, F, 0, F, 0, F, 0, F, 0, T, 16 },
>> +        .should_be_valid = true,
>> +        .expect = (CpuTopology) { 8, 16, 1, 1, 1, 16 },
>> +    }, {
>> +        /* config: -smp sockets=2,cores=4
>> +         * expect: cpus=8,sockets=2,dies=1,cores=4,threads=1,maxcpus=8 */
>> +        .config = (SMPConfiguration) { F, 0, T, 2, F, 0, T, 4, F, 0, F, 0 },
>> +        .should_be_valid = true,
>> +        .expect = (CpuTopology) { 8, 2, 1, 4, 1, 8 },
>> +    }, {
>> +        /* config: -smp sockets=2,threads=2
>> +         * expect: cpus=4,sockets=2,dies=1,cores=1,threads=2,maxcpus=4 */
>> +        .config = (SMPConfiguration) { F, 0, T, 2, F, 0, F, 0, T, 2, F, 0 },
>> +        .should_be_valid = true,
>> +        .expect = (CpuTopology) { 4, 2, 1, 1, 2, 4 },
>> +    }, {
>> +        /* config: -smp sockets=2,maxcpus=16
>> +         * expect: cpus=16,sockets=2,dies=1,cores=8,threads=1,maxcpus=16 */
>> +        .config = (SMPConfiguration) { F, 0, T, 2, F, 0, F, 0, F, 0, T, 16 },
>> +        .should_be_valid = true,
>> +        .expect = (CpuTopology) { 16, 2, 1, 8, 1, 16 },
>> +    }, {
>> +        /* config: -smp cores=4,threads=2
>> +         * expect: cpus=8,sockets=1,dies=1,cores=4,threads=2,maxcpus=8 */
>> +        .config = (SMPConfiguration) { F, 0, F, 0, F, 0, T, 4, T, 2, F, 0 },
>> +        .should_be_valid = true,
>> +        .expect = (CpuTopology) { 8, 1, 1, 4, 2, 8 },
>> +    }, {
>> +        /* config: -smp cores=4,maxcpus=16
>> +         * expect: cpus=16,sockets=4,dies=1,cores=4,threads=1,maxcpus=16 */
>> +        .config = (SMPConfiguration) { F, 0, F, 0, F, 0, T, 4, F, 0, T, 16 },
>> +        .should_be_valid = true,
>> +        .expect = (CpuTopology) { 16, 4, 1, 4, 1, 16 },
>> +    }, {
>> +        /* config: -smp threads=2,maxcpus=16
>> +         * expect: cpus=16,sockets=8,dies=1,cores=1,threads=2,maxcpus=16 */
>> +        .config = (SMPConfiguration) { F, 0, F, 0, F, 0, F, 0, T, 2, T, 16 },
>> +        .should_be_valid = true,
>> +        .expect = (CpuTopology) { 16, 8, 1, 1, 2, 16 },
>> +    }, {
>> +        /* config: -smp 8,sockets=2,cores=4
>> +         * expect: cpus=8,sockets=2,dies=1,cores=4,threads=1,maxcpus=8 */
>> +        .config = (SMPConfiguration) { T, 8, T, 2, F, 0, T, 4, F, 0, F, 0 },
>> +        .should_be_valid = true,
>> +        .expect = (CpuTopology) { 8, 2, 1, 4, 1, 8 },
>> +    }, {
>> +        /* config: -smp 8,sockets=2,threads=2
>> +         * expect: cpus=8,sockets=2,dies=1,cores=2,threads=2,maxcpus=8 */
>> +        .config = (SMPConfiguration) { T, 8, T, 2, F, 0, F, 0, T, 2, F, 0 },
>> +        .should_be_valid = true,
>> +        .expect = (CpuTopology) { 8, 2, 1, 2, 2, 8 },
>> +    }, {
>> +        /* config: -smp 8,sockets=2,maxcpus=16
>> +         * expect: cpus=8,sockets=2,dies=1,cores=8,threads=1,maxcpus=16 */
>> +        .config = (SMPConfiguration) { T, 8, T, 2, F, 0, F, 0, F, 0, T, 16 },
>> +        .should_be_valid = true,
>> +        .expect = (CpuTopology) { 8, 2, 1, 8, 1, 16 },
>> +    }, {
>> +        /* config: -smp 8,cores=4,threads=2
>> +         * expect: cpus=8,sockets=1,dies=1,cores=4,threads=2,maxcpus=8 */
>> +        .config = (SMPConfiguration) { T, 8, F, 0, F, 0, T, 4, T, 2, F, 0 },
>> +        .should_be_valid = true,
>> +        .expect = (CpuTopology) { 8, 1, 1, 4, 2, 8 },
>> +    }, {
>> +        /* config: -smp 8,cores=4,maxcpus=16
>> +         * expect: cpus=8,sockets=4,dies=1,cores=4,threads=1,maxcpus=16 */
>> +        .config = (SMPConfiguration) { T, 8, F, 0, F, 0, T, 4, F, 0, T, 16 },
>> +        .should_be_valid = true,
>> +        .expect = (CpuTopology) { 8, 4, 1, 4, 1, 16 },
>> +    }, {
>> +        /* config: -smp 8,threads=2,maxcpus=16
>> +         * expect: cpus=8,sockets=8,dies=1,cores=1,threads=2,maxcpus=16 */
>> +        .config = (SMPConfiguration) { T, 8, F, 0, F, 0, F, 0, T, 2, T, 16 },
>> +        .should_be_valid = true,
>> +        .expect = (CpuTopology) { 8, 8, 1, 1, 2, 16 },
>> +    }, {
>> +        /* config: -smp sockets=2,cores=4,threads=2
>> +         * expect: cpus=16,sockets=2,dies=1,cores=4,threads=2,maxcpus=16 */
>> +        .config = (SMPConfiguration) { F, 0, T, 2, F, 0, T, 4, T, 2, F, 0 },
>> +        .should_be_valid = true,
>> +        .expect = (CpuTopology) { 16, 2, 1, 4, 2, 16 },
>> +    }, {
>> +        /* config: -smp sockets=2,cores=4,maxcpus=16
>> +         * expect: cpus=16,sockets=2,dies=1,cores=4,threads=2,maxcpus=16 */
>> +        .config = (SMPConfiguration) { F, 0, T, 2, F, 0, T, 4, F, 0, T, 16 },
>> +        .should_be_valid = true,
>> +        .expect = (CpuTopology) { 16, 2, 1, 4, 2, 16 },
>> +    }, {
>> +        /* config: -smp sockets=2,threads=2,maxcpus=16
>> +         * expect: cpus=16,sockets=2,dies=1,cores=4,threads=2,maxcpus=16 */
>> +        .config = (SMPConfiguration) { F, 0, T, 2, F, 0, F, 0, T, 2, T, 16 },
>> +        .should_be_valid = true,
>> +        .expect = (CpuTopology) { 16, 2, 1, 4, 2, 16 },
>> +    }, {
>> +        /* config: -smp cores=4,threads=2,maxcpus=16
>> +         * expect: cpus=16,sockets=2,dies=1,cores=4,threads=2,maxcpus=16 */
>> +        .config = (SMPConfiguration) { F, 0, F, 0, F, 0, T, 4, T, 2, T, 16 },
>> +        .should_be_valid = true,
>> +        .expect = (CpuTopology) { 16, 2, 1, 4, 2, 16 },
>> +    }, {
>> +        /* config: -smp 8,sockets=2,cores=4,threads=1
>> +         * expect: cpus=8,sockets=2,dies=1,cores=4,threads=1,maxcpus=8 */
>> +        .config = (SMPConfiguration) { T, 8, T, 2, F, 0, T, 4, T, 1, F, 0 },
>> +        .should_be_valid = true,
>> +        .expect = (CpuTopology) { 8, 2, 1, 4, 1, 8 },
>> +    }, {
>> +        /* config: -smp 8,sockets=2,cores=4,maxcpus=16
>> +         * expect: cpus=8,sockets=2,dies=1,cores=4,threads=2,maxcpus=16 */
>> +        .config = (SMPConfiguration) { T, 8, T, 2, F, 0, T, 4, F, 0, T, 16 },
>> +        .should_be_valid = true,
>> +        .expect = (CpuTopology) { 8, 2, 1, 4, 2, 16 },
>> +    }, {
>> +        /* config: -smp 8,sockets=2,threads=2,maxcpus=16
>> +         * expect: cpus=8,sockets=2,dies=1,cores=4,threads=2,maxcpus=16 */
>> +        .config = (SMPConfiguration) { T, 8, T, 2, F, 0, F, 0, T, 2, T, 16 },
>> +        .should_be_valid = true,
>> +        .expect = (CpuTopology) { 8, 2, 1, 4, 2, 16 },
>> +    }, {
>> +        /* config: -smp 8,cores=4,threads=2,maxcpus=16
>> +         * expect: cpus=8,sockets=2,dies=1,cores=4,threads=2,maxcpus=16 */
>> +        .config = (SMPConfiguration) { T, 8, F, 0, F, 0, T, 4, T, 2, T, 16 },
>> +        .should_be_valid = true,
>> +        .expect = (CpuTopology) { 8, 2, 1, 4, 2, 16 },
>> +    }, {
>> +        /* config: -smp sockets=2,cores=4,threads=2,maxcpus=16
>> +         * expect: -smp 16,sockets=2,dies=1,cores=4,threads=2,maxcpus=16 */
>> +        .config = (SMPConfiguration) { F, 0, T, 2, F, 0, T, 4, T, 2, T, 16 },
>> +        .should_be_valid = true,
>> +        .expect = (CpuTopology) { 16, 2, 1, 4, 2, 16 },
>> +    }, {
>> +        /* config: -smp 8,sockets=2,cores=4,threads=2,maxcpus=16
>> +         * expect: cpus=8,sockets=2,dies=1,cores=4,threads=2,maxcpus=16 */
>> +        .config = (SMPConfiguration) { T, 8, T, 2, F, 0, T, 4, T, 2, T, 16 },
>> +        .should_be_valid = true,
>> +        .expect = (CpuTopology) { 8, 2, 1, 4, 2, 16 },
>> +    }, {
>> +        /* config: -smp 8,sockets=2,dies=1,cores=4,threads=2,maxcpus=16
>> +         * expect: cpus=8,sockets=2,dies=1,cores=4,threads=2,maxcpus=16 */
>> +        .config = (SMPConfiguration) { T, 8, T, 2, T, 1, T, 4, T, 2, T, 16 },
>> +        .should_be_valid = true,
>> +        .expect = (CpuTopology) { 8, 2, 1, 4, 2, 16 },
>> +    }, {
>> +        /* config: -smp 0
>> +         * expect: error, "anything=0" is not allowed */
>> +        .config = (SMPConfiguration) { T, 0, F, 0, F, 0, F, 0, F, 0, F, 0 },
>> +        .should_be_valid = false,
>> +    }, {
>> +        /* config: -smp 8,sockets=0
>> +         * expect: error, "anything=0" is not allowed */
>> +        .config = (SMPConfiguration) { T, 8, T, 0, F, 0, F, 0, F, 0, F, 0 },
>> +        .should_be_valid = false,
>> +    }, {
>> +        /* config: -smp 8,sockets=2,dies=0
>> +         * expect: error, "anything=0" is not allowed */
>> +        .config = (SMPConfiguration) { T, 0, T, 2, T, 0, F, 0, F, 0, F, 0 },
>> +        .should_be_valid = false,
>> +    }, {
>> +        /* config: -smp 8,sockets=2,dies=1,cores=0
>> +         * expect: error, "anything=0" is not allowed */
>> +        .config = (SMPConfiguration) { T, 8, T, 2, T, 1, T, 0, F, 0, F, 0 },
>> +        .should_be_valid = false,
>> +    }, {
>> +        /* config: -smp 8,sockets=2,dies=1,cores=4,threads=0
>> +         * expect: error, "anything=0" is not allowed */
>> +        .config = (SMPConfiguration) { T, 8, T, 2, T, 1, T, 4, T, 0, F, 0 },
>> +        .should_be_valid = false,
>> +    }, {
>> +        /* config: -smp 8,sockets=2,dies=1,cores=4,threads=2,maxcpus=0
>> +         * expect: error, "anything=0" is not allowed */
>> +        .config = (SMPConfiguration) { T, 8, T, 2, T, 1, T, 4, T, 2, T, 0 },
>> +        .should_be_valid = false,
>> +    }, {
>> +        /* config: -smp 8,dies=2
>> +         * expect: error, multi-dies not supported */
>> +        .config = (SMPConfiguration) { T, 8, F, 0, T, 2, F, 0, F, 0, F, 0 },
>> +        .should_be_valid = false,
>> +    }, {
>> +        /* config: -smp 8,sockets=2,cores=8
>> +         * expect: error, sum (16) != max_cpus (8) */
>> +        .config = (SMPConfiguration) { T, 8, T, 2, F, 0, T, 4, T, 2, F, 0 },
>> +        .should_be_valid = false,
>> +    }, {
>> +        /* config: -smp 8,sockets=2,cores=5,threads=2,maxcpus=16
>> +         * expect: error, sum (20) != max_cpus (16) */
>> +        .config = (SMPConfiguration) { F, 0, T, 3, F, 0, T, 5, T, 1, T, 16 },
>> +        .should_be_valid = false,
>> +    }, {
>> +        /* config: -smp 16,maxcpus=12
>> +         * expect: error, sum (12) < smp_cpus (16) */
>> +        .config = (SMPConfiguration) { T, 16, F, 0, F, 0, F, 0, F, 0, T, 12 },
>> +        .should_be_valid = false,
>> +    },
>> +};
>> +
>> +static struct SMPTestData prefer_sockets_support_dies[] = {
>> +    {
>> +        /* config: -smp dies=2
>> +         * expect: cpus=2,sockets=1,dies=2,cores=1,threads=1,maxcpus=2 */
>> +        .config = (SMPConfiguration) { F, 0, F, 0, T, 2, F, 0, F, 0, F, 0 },
>> +        .should_be_valid = true,
>> +        .expect = (CpuTopology) { 2, 1, 2, 1, 1, 2 },
>> +    }, {
>> +        /* config: -smp 16,dies=2
>> +         * expect: cpus=16,sockets=8,dies=2,cores=1,threads=1,maxcpus=16 */
>> +        .config = (SMPConfiguration) { T, 16, F, 0, T, 2, F, 0, F, 0, F, 0 },
>> +        .should_be_valid = true,
>> +        .expect = (CpuTopology) { 16, 8, 2, 1, 1, 16 },
>> +    }, {
>> +        /* config: -smp sockets=2,dies=2
>> +         * expect: cpus=4,sockets=2,dies=2,cores=1,threads=1,maxcpus=4 */
>> +        .config = (SMPConfiguration) { F, 0, T, 2, T, 2, F, 0, F, 0, F, 0 },
>> +        .should_be_valid = true,
>> +        .expect = (CpuTopology) { 4, 2, 2, 1, 1, 4 },
>> +    }, {
>> +        /* config: -smp dies=2,cores=4
>> +         * expect: cpus=8,sockets=1,dies=2,cores=4,threads=1,maxcpus=8 */
>> +        .config = (SMPConfiguration) { F, 0, F, 0, T, 2, T, 4, F, 0, F, 0 },
>> +        .should_be_valid = true,
>> +        .expect = (CpuTopology) { 8, 1, 2, 4, 1, 8 },
>> +    }, {
>> +        /* config: -smp dies=2,threads=2
>> +         * expect: cpus=4,sockets=1,dies=2,cores=1,threads=2,maxcpus=4 */
>> +        .config = (SMPConfiguration) { F, 0, F, 0, T, 2, F, 0, T, 2, F, 0 },
>> +        .should_be_valid = true,
>> +        .expect = (CpuTopology) { 4, 1, 2, 1, 2, 4 },
>> +    }, {
>> +        /* config: -smp dies=2,maxcpus=32
>> +         * expect: cpus=32,sockets=16,dies=2,cores=1,threads=1,maxcpus=32 */
>> +        .config = (SMPConfiguration) { F, 0, F, 0, T, 2, F, 0, F, 0, T, 32 },
>> +        .should_be_valid = true,
>> +        .expect = (CpuTopology) { 32, 16, 2, 1, 1, 32 },
>> +    }, {
>> +        /* config: -smp 16,sockets=2,dies=2
>> +         * expect: cpus=16,sockets=2,dies=2,cores=4,threads=1,maxcpus=16 */
>> +        .config = (SMPConfiguration) { T, 16, T, 2, T, 2, F, 0, F, 0, F, 0 },
>> +        .should_be_valid = true,
>> +        .expect = (CpuTopology) { 16, 2, 2, 4, 1, 16 },
>> +    }, {
>> +        /* config: -smp 16,dies=2,cores=4
>> +         * expect: cpus=16,sockets=2,dies=2,cores=4,threads=1,maxcpus=16 */
>> +        .config = (SMPConfiguration) { T, 16, F, 0, T, 2, T, 4, F, 0, F, 0 },
>> +        .should_be_valid = true,
>> +        .expect = (CpuTopology) { 16, 2, 2, 4, 1, 16 },
>> +    }, {
>> +        /* config: -smp 16,dies=2,threads=2
>> +         * expect: cpus=16,sockets=4,dies=2,cores=1,threads=2,maxcpus=16 */
>> +        .config = (SMPConfiguration) { T, 16, F, 0, T, 2, F, 0, T, 2, F, 0 },
>> +        .should_be_valid = true,
>> +        .expect = (CpuTopology) { 16, 4, 2, 1, 2, 16 },
>> +    }, {
>> +        /* config: -smp 16,dies=2,maxcpus=32
>> +         * expect: cpus=16,sockets=16,dies=2,cores=1,threads=1,maxcpus=32 */
>> +        .config = (SMPConfiguration) { T, 16, F, 0, T, 2, F, 0, F, 0, T, 32 },
>> +        .should_be_valid = true,
>> +        .expect = (CpuTopology) { 16, 16, 2, 1, 1, 32 },
>> +    }, {
>> +        /* config: -smp sockets=2,dies=2,cores=4
>> +         * expect: cpus=16,sockets=2,dies=2,cores=4,threads=1,maxcpus=16 */
>> +        .config = (SMPConfiguration) { F, 0, T, 2, T, 2, T, 4, F, 0, F, 0 },
>> +        .should_be_valid = true,
>> +        .expect = (CpuTopology) { 16, 2, 2, 4, 1, 16 },
>> +    }, {
>> +        /* config: -smp sockets=2,dies=2,threads=2
>> +         * expect: cpus=8,sockets=2,dies=2,cores=1,threads=2,maxcpus=8 */
>> +        .config = (SMPConfiguration) { F, 0, T, 2, T, 2, F, 0, T, 2, F, 0 },
>> +        .should_be_valid = true,
>> +        .expect = (CpuTopology) { 8, 2, 2, 1, 2, 8 },
>> +    }, {
>> +        /* config: -smp sockets=2,dies=2,maxcpus=32
>> +         * expect: cpus=32,sockets=2,dies=2,cores=8,threads=1,maxcpus=32 */
>> +        .config = (SMPConfiguration) { F, 0, T, 2, T, 2, F, 0, F, 0, T, 32 },
>> +        .should_be_valid = true,
>> +        .expect = (CpuTopology) { 32, 2, 2, 8, 1, 32 },
>> +    }, {
>> +        /* config: -smp dies=2,cores=4,threads=2
>> +         * expect: cpus=16,sockets=1,dies=2,cores=4,threads=2,maxcpus=16 */
>> +        .config = (SMPConfiguration) { F, 0, F, 0, T, 2, T, 4, T, 2, F, 0 },
>> +        .should_be_valid = true,
>> +        .expect = (CpuTopology) { 16, 1, 2, 4, 2, 16 },
>> +    }, {
>> +        /* config: -smp dies=2,cores=4,maxcpus=32
>> +         * expect: cpus=32,sockets=4,dies=2,cores=4,threads=1,maxcpus=32 */
>> +        .config = (SMPConfiguration) { F, 0, F, 0, T, 2, T, 4, F, 0, T, 32 },
>> +        .should_be_valid = true,
>> +        .expect = (CpuTopology) { 32, 4, 2, 4, 1, 32 },
>> +    }, {
>> +        /* config: -smp dies=2,threads=2,maxcpus=32
>> +         * expect: cpus=32,sockets=8,dies=2,cores=1,threads=2,maxcpus=32 */
>> +        .config = (SMPConfiguration) { F, 0, F, 0, T, 2, F, 0, T, 2, T, 32 },
>> +        .should_be_valid = true,
>> +        .expect = (CpuTopology) { 32, 8, 2, 1, 2, 32 },
>> +    }, {
>> +        /* config: -smp 16,sockets=2,dies=2,cores=4
>> +         * expect: cpus=16,sockets=2,dies=2,cores=4,threads=1,maxcpus=16 */
>> +        .config = (SMPConfiguration) { T, 16, T, 2, T, 2, T, 4, F, 0, F, 0 },
>> +        .should_be_valid = true,
>> +        .expect = (CpuTopology) { 16, 2, 2, 4, 1, 16 },
>> +    }, {
>> +        /* config: -smp 16,sockets=2,dies=2,threads=2
>> +         * expect: cpus=16,sockets=2,dies=2,cores=2,threads=2,maxcpus=16 */
>> +        .config = (SMPConfiguration) { T, 16, T, 2, T, 2, F, 0, T, 2, F, 0 },
>> +        .should_be_valid = true,
>> +        .expect = (CpuTopology) { 16, 2, 2, 2, 2, 16 },
>> +    }, {
>> +        /* config: -smp 16,sockets=2,dies=2,maxcpus=32
>> +         * expect: cpus=16,sockets=2,dies=2,cores=8,threads=1,maxcpus=32 */
>> +        .config = (SMPConfiguration) { T, 16, T, 2, T, 2, F, 0, F, 0, T, 32 },
>> +        .should_be_valid = true,
>> +        .expect = (CpuTopology) { 16, 2, 2, 8, 1, 32 },
>> +    }, {
>> +        /* config: -smp 16,dies=2,cores=4,threads=2
>> +         * expect: cpus=16,sockets=1,dies=2,cores=4,threads=2,maxcpus=16 */
>> +        .config = (SMPConfiguration) { T, 16, F, 0, T, 2, T, 4, T, 2, F, 0 },
>> +        .should_be_valid = true,
>> +        .expect = (CpuTopology) { 16, 1, 2, 4, 2, 16 },
>> +    }, {
>> +        /* config: -smp 16,dies=2,cores=4,maxcpus=32
>> +         * expect: cpus=16,sockets=4,dies=2,cores=4,threads=1,maxcpus=32 */
>> +        .config = (SMPConfiguration) { T, 16, F, 0, T, 2, T, 4, F, 0, T, 32 },
>> +        .should_be_valid = true,
>> +        .expect = (CpuTopology) { 16, 4, 2, 4, 1, 32 },
>> +    }, {
>> +        /* config: -smp 16,dies=2,threads=2,maxcpus=32
>> +         * expect: cpus=16,sockets=8,dies=2,cores=1,threads=2,maxcpus=32 */
>> +        .config = (SMPConfiguration) { T, 16, F, 0, T, 2, F, 0, T, 2, T, 32 },
>> +        .should_be_valid = true,
>> +        .expect = (CpuTopology) { 16, 8, 2, 1, 2, 32 },
>> +    }, {
>> +        /* config: -smp sockets=2,dies=2,cores=4,threads=2
>> +         * expect: cpus=32,sockets=2,dies=2,cores=4,threads=2,maxcpus=32 */
>> +        .config = (SMPConfiguration) { F, 0, T, 2, T, 2, T, 4, T, 2, F, 0 },
>> +        .should_be_valid = true,
>> +        .expect = (CpuTopology) { 32, 2, 2, 4, 2, 32 },
>> +    }, {
>> +        /* config: -smp sockets=2,dies=2,cores=4,maxcpus=32
>> +         * expect: cpus=32,sockets=2,dies=2,cores=4,threads=2,maxcpus=32 */
>> +        .config = (SMPConfiguration) { F, 0, T, 2, T, 2, T, 4, F, 0, T, 32 },
>> +        .should_be_valid = true,
>> +        .expect = (CpuTopology) { 32, 2, 2, 4, 2, 32 },
>> +    }, {
>> +        /* config: -smp sockets=2,dies=2,threads=2,maxcpus=32
>> +         * expect: cpus=32,sockets=2,dies=2,cores=4,threads=2,maxcpus=32 */
>> +        .config = (SMPConfiguration) { F, 0, T, 2, T, 2, F, 0, T, 2, T, 32 },
>> +        .should_be_valid = true,
>> +        .expect = (CpuTopology) { 32, 2, 2, 4, 2, 32 },
>> +    }, {
>> +        /* config: -smp dies=2,cores=4,threads=2,maxcpus=32
>> +         * expect: cpus=32,sockets=2,dies=2,cores=4,threads=2,maxcpus=32 */
>> +        .config = (SMPConfiguration) { F, 0, F, 0, T, 2, T, 4, T, 2, T, 32 },
>> +        .should_be_valid = true,
>> +        .expect = (CpuTopology) { 32, 2, 2, 4, 2, 32 },
>> +    }, {
>> +        /* config: -smp 16,sockets=2,dies=2,cores=4,threads=1
>> +         * expect: cpus=16,sockets=2,dies=2,cores=4,threads=1,maxcpus=16 */
>> +        .config = (SMPConfiguration) { T, 16, T, 2, T, 2, T, 4, T, 1, F, 0 },
>> +        .should_be_valid = true,
>> +        .expect = (CpuTopology) { 16, 2, 2, 4, 1, 16 },
>> +    }, {
>> +        /* config: -smp 16,sockets=2,dies=2,cores=4,maxcpus=32
>> +         * expect: cpus=16,sockets=2,dies=2,cores=4,threads=2,maxcpus=32 */
>> +        .config = (SMPConfiguration) { T, 16, T, 2, T, 2, T, 4, F, 0, T, 32 },
>> +        .should_be_valid = true,
>> +        .expect = (CpuTopology) { 16, 2, 2, 4, 2, 32 },
>> +    }, {
>> +        /* config: -smp 16,sockets=2,dies=2,threads=2,maxcpus=32
>> +         * expect: cpus=16,sockets=2,dies=2,cores=4,threads=2,maxcpus=32 */
>> +        .config = (SMPConfiguration) { T, 16, T, 2, T, 2, F, 0, T, 2, T, 32 },
>> +        .should_be_valid = true,
>> +        .expect = (CpuTopology) { 16, 2, 2, 4, 2, 32 },
>> +    }, {
>> +        /* config: -smp 16,dies=2,cores=4,threads=2,maxcpus=32
>> +         * expect: cpus=16,sockets=2,dies=2,cores=4,threads=2,maxcpus=32 */
>> +        .config = (SMPConfiguration) { T, 16, F, 0, T, 2, T, 4, T, 2, T, 32 },
>> +        .should_be_valid = true,
>> +        .expect = (CpuTopology) { 16, 2, 2, 4, 2, 32 },
>> +    }, {
>> +        /* config: -smp sockets=2,dies=2,cores=4,threads=2,maxcpus=32
>> +         * expect: -smp 32,sockets=2,dies=2,cores=4,threads=2,maxcpus=32 */
>> +        .config = (SMPConfiguration) { F, 0, T, 2, T, 2, T, 4, T, 2, T, 32 },
>> +        .should_be_valid = true,
>> +        .expect = (CpuTopology) { 32, 2, 2, 4, 2, 32 },
>> +    }, {
>> +        /* config: -smp 16,sockets=2,dies=2,cores=4,threads=2,maxcpus=32
>> +         * expect: cpus=16,sockets=2,dies=2,cores=4,threads=2,maxcpus=32 */
>> +        .config = (SMPConfiguration) { T, 16, T, 2, T, 2, T, 4, T, 2, T, 32 },
>> +        .should_be_valid = true,
>> +        .expect = (CpuTopology) { 16, 2, 2, 4, 2, 32 },
>> +    },
>> +};
>> +
>> +/*
>> + * prefer cores over sockets over threads since 6.2.
>> + * all possible SMP configurations and the corressponding expected outputs
>> + * are listed for testing, including the valid and invalid ones.
>> + */
>> +static struct SMPTestData prefer_cores[] = {
>> +    {
>> +        /* config: no smp configuration
>> +         * expect: cpus=1,sockets=1,dies=1,cores=1,threads=1,maxcpus=1 */
>> +        .config = (SMPConfiguration) { F, 0, F, 0, F, 0, F, 0, F, 0, F, 0 },
>> +        .should_be_valid = true,
>> +        .expect = (CpuTopology) { 1, 1, 1, 1, 1, 1 },
>> +    }, {
>> +        /* config: -smp 8
>> +         * expect: cpus=8,sockets=1,dies=1,cores=8,threads=1,maxcpus=8 */
>> +        .config = (SMPConfiguration) { T, 8, F, 0, F, 0, F, 0, F, 0, F, 0 },
>> +        .should_be_valid = true,
>> +        .expect = (CpuTopology) { 8, 1, 1, 8, 1, 8 },
>> +    }, {
>> +        /* config: -smp sockets=2
>> +         * expect: cpus=2,sockets=2,dies=1,cores=1,threads=1,maxcpus=2 */
>> +        .config = (SMPConfiguration) { F, 0, T, 2, F, 0, F, 0, F, 0, F, 0 },
>> +        .should_be_valid = true,
>> +        .expect = (CpuTopology) { 2, 2, 1, 1, 1, 2 },
>> +    }, {
>> +        /* config: -smp cores=4
>> +         * expect: cpus=4,sockets=1,dies=1,cores=4,threads=1,maxcpus=4 */
>> +        .config = (SMPConfiguration) { F, 0, F, 0, F, 0, T, 4, F, 0, F, 0 },
>> +        .should_be_valid = true,
>> +        .expect = (CpuTopology) { 4, 1, 1, 4, 1, 4 },
>> +    }, {
>> +        /* config: -smp threads=2
>> +         * expect: cpus=2,sockets=1,dies=1,cores=1,threads=2,maxcpus=2 */
>> +        .config = (SMPConfiguration) { F, 0, F, 0, F, 0, F, 0, T, 2, F, 0 },
>> +        .should_be_valid = true,
>> +        .expect = (CpuTopology) { 2, 1, 1, 1, 2, 2 },
>> +    }, {
>> +        /* config: -smp maxcpus=16
>> +         * expect: cpus=16,sockets=1,dies=1,cores=16,threads=1,maxcpus=16 */
>> +        .config = (SMPConfiguration) { F, 0, F, 0, F, 0, F, 0, F, 0, T, 16 },
>> +        .should_be_valid = true,
>> +        .expect = (CpuTopology) { 16, 1, 1, 16, 1, 16 },
>> +    }, {
>> +        /* config: -smp 8,sockets=2
>> +         * expect: cpus=8,sockets=2,dies=1,cores=4,threads=1,maxcpus=8 */
>> +        .config = (SMPConfiguration) { T, 8, T, 2, F, 0, F, 0, F, 0, F, 0 },
>> +        .should_be_valid = true,
>> +        .expect = (CpuTopology) { 8, 2, 1, 4, 1, 8 },
>> +    }, {
>> +        /* config: -smp 8,cores=4
>> +         * expect: cpus=8,sockets=2,dies=1,cores=4,threads=1,maxcpus=8 */
>> +        .config = (SMPConfiguration) { T, 8, F, 0, F, 0, T, 4, F, 0, F, 0 },
>> +        .should_be_valid = true,
>> +        .expect = (CpuTopology) { 8, 2, 1, 4, 1, 8 },
>> +    }, {
>> +        /* config: -smp 8,threads=2
>> +         * expect: cpus=8,sockets=1,dies=1,cores=4,threads=2,maxcpus=8 */
>> +        .config = (SMPConfiguration) { T, 8, F, 0, F, 0, F, 0, T, 2, F, 0 },
>> +        .should_be_valid = true,
>> +        .expect = (CpuTopology) { 8, 1, 1, 4, 2, 8 },
>> +    }, {
>> +        /* config: -smp 8,maxcpus=16
>> +         * expect: cpus=8,sockets=1,dies=1,cores=16,threads=1,maxcpus=16 */
>> +        .config = (SMPConfiguration) { T, 8, F, 0, F, 0, F, 0, F, 0, T, 16 },
>> +        .should_be_valid = true,
>> +        .expect = (CpuTopology) { 8, 1, 1, 16, 1, 16 },
>> +    }, {
>> +        /* config: -smp sockets=2,cores=4
>> +         * expect: cpus=8,sockets=2,dies=1,cores=4,threads=1,maxcpus=8 */
>> +        .config = (SMPConfiguration) { F, 0, T, 2, F, 0, T, 4, F, 0, F, 0 },
>> +        .should_be_valid = true,
>> +        .expect = (CpuTopology) { 8, 2, 1, 4, 1, 8 },
>> +    }, {
>> +        /* config: -smp sockets=2,threads=2
>> +         * expect: cpus=4,sockets=2,dies=1,cores=1,threads=2,maxcpus=4 */
>> +        .config = (SMPConfiguration) { F, 0, T, 2, F, 0, F, 0, T, 2, F, 0 },
>> +        .should_be_valid = true,
>> +        .expect = (CpuTopology) { 4, 2, 1, 1, 2, 4 },
>> +    }, {
>> +        /* config: -smp sockets=2,maxcpus=16
>> +         * expect: cpus=16,sockets=2,dies=1,cores=8,threads=1,maxcpus=16 */
>> +        .config = (SMPConfiguration) { F, 0, T, 2, F, 0, F, 0, F, 0, T, 16 },
>> +        .should_be_valid = true,
>> +        .expect = (CpuTopology) { 16, 2, 1, 8, 1, 16 },
>> +    }, {
>> +        /* config: -smp cores=4,threads=2
>> +         * expect: cpus=8,sockets=1,dies=1,cores=4,threads=2,maxcpus=8 */
>> +        .config = (SMPConfiguration) { F, 0, F, 0, F, 0, T, 4, T, 2, F, 0 },
>> +        .should_be_valid = true,
>> +        .expect = (CpuTopology) { 8, 1, 1, 4, 2, 8 },
>> +    }, {
>> +        /* config: -smp cores=4,maxcpus=16
>> +         * expect: cpus=16,sockets=4,dies=1,cores=4,threads=1,maxcpus=16 */
>> +        .config = (SMPConfiguration) { F, 0, F, 0, F, 0, T, 4, F, 0, T, 16 },
>> +        .should_be_valid = true,
>> +        .expect = (CpuTopology) { 16, 4, 1, 4, 1, 16 },
>> +    }, {
>> +        /* config: -smp threads=2,maxcpus=16
>> +         * expect: cpus=16,sockets=1,dies=1,cores=8,threads=2,maxcpus=16 */
>> +        .config = (SMPConfiguration) { F, 0, F, 0, F, 0, F, 0, T, 2, T, 16 },
>> +        .should_be_valid = true,
>> +        .expect = (CpuTopology) { 16, 1, 1, 8, 2, 16 },
>> +    }, {
>> +        /* config: -smp 8,sockets=2,cores=4
>> +         * expect: cpus=8,sockets=2,dies=1,cores=4,threads=1,maxcpus=8 */
>> +        .config = (SMPConfiguration) { T, 8, T, 2, F, 0, T, 4, F, 0, F, 0 },
>> +        .should_be_valid = true,
>> +        .expect = (CpuTopology) { 8, 2, 1, 4, 1, 8 },
>> +    }, {
>> +        /* config: -smp 8,sockets=2,threads=2
>> +         * expect: cpus=8,sockets=2,dies=1,cores=2,threads=2,maxcpus=8 */
>> +        .config = (SMPConfiguration) { T, 8, T, 2, F, 0, F, 0, T, 2, F, 0 },
>> +        .should_be_valid = true,
>> +        .expect = (CpuTopology) { 8, 2, 1, 2, 2, 8 },
>> +    }, {
>> +        /* config: -smp 8,sockets=2,maxcpus=16
>> +         * expect: cpus=8,sockets=2,dies=1,cores=8,threads=1,maxcpus=16 */
>> +        .config = (SMPConfiguration) { T, 8, T, 2, F, 0, F, 0, F, 0, T, 16 },
>> +        .should_be_valid = true,
>> +        .expect = (CpuTopology) { 8, 2, 1, 8, 1, 16 },
>> +    }, {
>> +        /* config: -smp 8,cores=4,threads=2
>> +         * expect: cpus=8,sockets=1,dies=1,cores=4,threads=2,maxcpus=8 */
>> +        .config = (SMPConfiguration) { T, 8, F, 0, F, 0, T, 4, T, 2, F, 0 },
>> +        .should_be_valid = true,
>> +        .expect = (CpuTopology) { 8, 1, 1, 4, 2, 8 },
>> +    }, {
>> +        /* config: -smp 8,cores=4,maxcpus=16
>> +         * expect: cpus=8,sockets=4,dies=1,cores=4,threads=1,maxcpus=16 */
>> +        .config = (SMPConfiguration) { T, 8, F, 0, F, 0, T, 4, F, 0, T, 16 },
>> +        .should_be_valid = true,
>> +        .expect = (CpuTopology) { 8, 4, 1, 4, 1, 16 },
>> +    }, {
>> +        /* config: -smp 8,threads=2,maxcpus=16
>> +         * expect: cpus=8,sockets=1,dies=1,cores=8,threads=2,maxcpus=16 */
>> +        .config = (SMPConfiguration) { T, 8, F, 0, F, 0, F, 0, T, 2, T, 16 },
>> +        .should_be_valid = true,
>> +        .expect = (CpuTopology) { 8, 1, 1, 8, 2, 16 },
>> +    }, {
>> +        /* config: -smp sockets=2,cores=4,threads=2
>> +         * expect: cpus=16,sockets=2,dies=1,cores=4,threads=2,maxcpus=16 */
>> +        .config = (SMPConfiguration) { F, 0, T, 2, F, 0, T, 4, T, 2, F, 0 },
>> +        .should_be_valid = true,
>> +        .expect = (CpuTopology) { 16, 2, 1, 4, 2, 16 },
>> +    }, {
>> +        /* config: -smp sockets=2,cores=4,maxcpus=16
>> +         * expect: cpus=16,sockets=2,dies=1,cores=4,threads=2,maxcpus=16 */
>> +        .config = (SMPConfiguration) { F, 0, T, 2, F, 0, T, 4, F, 0, T, 16 },
>> +        .should_be_valid = true,
>> +        .expect = (CpuTopology) { 16, 2, 1, 4, 2, 16 },
>> +    }, {
>> +        /* config: -smp sockets=2,threads=2,maxcpus=16
>> +         * expect: cpus=16,sockets=2,dies=1,cores=4,threads=2,maxcpus=16 */
>> +        .config = (SMPConfiguration) { F, 0, T, 2, F, 0, F, 0, T, 2, T, 16 },
>> +        .should_be_valid = true,
>> +        .expect = (CpuTopology) { 16, 2, 1, 4, 2, 16 },
>> +    }, {
>> +        /* config: -smp cores=4,threads=2,maxcpus=16
>> +         * expect: cpus=16,sockets=2,dies=1,cores=4,threads=2,maxcpus=16 */
>> +        .config = (SMPConfiguration) { F, 0, F, 0, F, 0, T, 4, T, 2, T, 16 },
>> +        .should_be_valid = true,
>> +        .expect = (CpuTopology) { 16, 2, 1, 4, 2, 16 },
>> +    }, {
>> +        /* config: -smp 8,sockets=2,cores=4,threads=1
>> +         * expect: cpus=8,sockets=2,dies=1,cores=4,threads=1,maxcpus=8 */
>> +        .config = (SMPConfiguration) { T, 8, T, 2, F, 0, T, 4, T, 1, F, 0 },
>> +        .should_be_valid = true,
>> +        .expect = (CpuTopology) { 8, 2, 1, 4, 1, 8 },
>> +    }, {
>> +        /* config: -smp 8,sockets=2,cores=4,maxcpus=16
>> +         * expect: cpus=8,sockets=2,dies=1,cores=4,threads=2,maxcpus=16 */
>> +        .config = (SMPConfiguration) { T, 8, T, 2, F, 0, T, 4, F, 0, T, 16 },
>> +        .should_be_valid = true,
>> +        .expect = (CpuTopology) { 8, 2, 1, 4, 2, 16 },
>> +    }, {
>> +        /* config: -smp 8,sockets=2,threads=2,maxcpus=16
>> +         * expect: cpus=8,sockets=2,dies=1,cores=4,threads=2,maxcpus=16 */
>> +        .config = (SMPConfiguration) { T, 8, T, 2, F, 0, F, 0, T, 2, T, 16 },
>> +        .should_be_valid = true,
>> +        .expect = (CpuTopology) { 8, 2, 1, 4, 2, 16 },
>> +    }, {
>> +        /* config: -smp 8,cores=4,threads=2,maxcpus=16
>> +         * expect: cpus=8,sockets=2,dies=1,cores=4,threads=2,maxcpus=16 */
>> +        .config = (SMPConfiguration) { T, 8, F, 0, F, 0, T, 4, T, 2, T, 16 },
>> +        .should_be_valid = true,
>> +        .expect = (CpuTopology) { 8, 2, 1, 4, 2, 16 },
>> +    }, {
>> +        /* config: -smp sockets=2,cores=4,threads=2,maxcpus=16
>> +         * expect: -smp 16,sockets=2,dies=1,cores=4,threads=2,maxcpus=16 */
>> +        .config = (SMPConfiguration) { F, 0, T, 2, F, 0, T, 4, T, 2, T, 16 },
>> +        .should_be_valid = true,
>> +        .expect = (CpuTopology) { 16, 2, 1, 4, 2, 16 },
>> +    }, {
>> +        /* config: -smp 8,sockets=2,cores=4,threads=2,maxcpus=16
>> +         * expect: cpus=8,sockets=2,dies=1,cores=4,threads=2,maxcpus=16 */
>> +        .config = (SMPConfiguration) { T, 8, T, 2, F, 0, T, 4, T, 2, T, 16 },
>> +        .should_be_valid = true,
>> +        .expect = (CpuTopology) { 8, 2, 1, 4, 2, 16 },
>> +    }, {
>> +        /* config: -smp 8,sockets=2,dies=1,cores=4,threads=2,maxcpus=16
>> +         * expect: cpus=8,sockets=2,dies=1,cores=4,threads=2,maxcpus=16 */
>> +        .config = (SMPConfiguration) { T, 8, T, 2, T, 1, T, 4, T, 2, T, 16 },
>> +        .should_be_valid = true,
>> +        .expect = (CpuTopology) { 8, 2, 1, 4, 2, 16 },
>> +    }, {
>> +        /* config: -smp 0
>> +         * expect: error, "anything=0" is not allowed */
>> +        .config = (SMPConfiguration) { T, 0, F, 0, F, 0, F, 0, F, 0, F, 0 },
>> +        .should_be_valid = false,
>> +    }, {
>> +        /* config: -smp 8,sockets=0
>> +         * expect: error, "anything=0" is not allowed */
>> +        .config = (SMPConfiguration) { T, 8, T, 0, F, 0, F, 0, F, 0, F, 0 },
>> +        .should_be_valid = false,
>> +    }, {
>> +        /* config: -smp 8,sockets=2,dies=0
>> +         * expect: error, "anything=0" is not allowed */
>> +        .config = (SMPConfiguration) { T, 0, T, 2, T, 0, F, 0, F, 0, F, 0 },
>> +        .should_be_valid = false,
>> +    }, {
>> +        /* config: -smp 8,sockets=2,dies=1,cores=0
>> +         * expect: error, "anything=0" is not allowed */
>> +        .config = (SMPConfiguration) { T, 8, T, 2, T, 1, T, 0, F, 0, F, 0 },
>> +        .should_be_valid = false,
>> +    }, {
>> +        /* config: -smp 8,sockets=2,dies=1,cores=4,threads=0
>> +         * expect: error, "anything=0" is not allowed */
>> +        .config = (SMPConfiguration) { T, 8, T, 2, T, 1, T, 4, T, 0, F, 0 },
>> +        .should_be_valid = false,
>> +    }, {
>> +        /* config: -smp 8,sockets=2,dies=1,cores=4,threads=2,maxcpus=0
>> +         * expect: error, "anything=0" is not allowed */
>> +        .config = (SMPConfiguration) { T, 8, T, 2, T, 1, T, 4, T, 2, T, 0 },
>> +        .should_be_valid = false,
>> +    }, {
>> +        /* config: -smp 8,dies=2
>> +         * expect: error, multi-dies not supported */
>> +        .config = (SMPConfiguration) { T, 8, F, 0, T, 2, F, 0, F, 0, F, 0 },
>> +        .should_be_valid = false,
>> +    }, {
>> +        /* config: -smp 8,sockets=2,cores=8
>> +         * expect: error, sum (16) != max_cpus (8) */
>> +        .config = (SMPConfiguration) { T, 8, T, 2, F, 0, T, 4, T, 2, F, 0 },
>> +        .should_be_valid = false,
>> +    }, {
>> +        /* config: -smp 8,sockets=2,cores=5,threads=2,maxcpus=16
>> +         * expect: error, sum (20) != max_cpus (16) */
>> +        .config = (SMPConfiguration) { F, 0, T, 3, F, 0, T, 5, T, 1, T, 16 },
>> +        .should_be_valid = false,
>> +    }, {
>> +        /* config: -smp 16,maxcpus=12
>> +         * expect: error, sum (12) < smp_cpus (16) */
>> +        .config = (SMPConfiguration) { T, 16, F, 0, F, 0, F, 0, F, 0, T, 12 },
>> +        .should_be_valid = false,
>> +    },
>> +};
>> +
>> +static struct SMPTestData prefer_cores_support_dies[] = {
>> +    {
>> +        /* config: -smp dies=2
>> +         * expect: cpus=2,sockets=1,dies=2,cores=1,threads=1,maxcpus=2 */
>> +        .config = (SMPConfiguration) { F, 0, F, 0, T, 2, F, 0, F, 0, F, 0 },
>> +        .should_be_valid = true,
>> +        .expect = (CpuTopology) { 2, 1, 2, 1, 1, 2 },
>> +    }, {
>> +        /* config: -smp 16,dies=2
>> +         * expect: cpus=16,sockets=1,dies=2,cores=8,threads=1,maxcpus=16 */
>> +        .config = (SMPConfiguration) { T, 16, F, 0, T, 2, F, 0, F, 0, F, 0 },
>> +        .should_be_valid = true,
>> +        .expect = (CpuTopology) { 16, 1, 2, 8, 1, 16 },
>> +    }, {
>> +        /* config: -smp sockets=2,dies=2
>> +         * expect: cpus=4,sockets=2,dies=2,cores=1,threads=1,maxcpus=4 */
>> +        .config = (SMPConfiguration) { F, 0, T, 2, T, 2, F, 0, F, 0, F, 0 },
>> +        .should_be_valid = true,
>> +        .expect = (CpuTopology) { 4, 2, 2, 1, 1, 4 },
>> +    }, {
>> +        /* config: -smp dies=2,cores=4
>> +         * expect: cpus=8,sockets=1,dies=2,cores=4,threads=1,maxcpus=8 */
>> +        .config = (SMPConfiguration) { F, 0, F, 0, T, 2, T, 4, F, 0, F, 0 },
>> +        .should_be_valid = true,
>> +        .expect = (CpuTopology) { 8, 1, 2, 4, 1, 8 },
>> +    }, {
>> +        /* config: -smp dies=2,threads=2
>> +         * expect: cpus=4,sockets=1,dies=2,cores=1,threads=2,maxcpus=4 */
>> +        .config = (SMPConfiguration) { F, 0, F, 0, T, 2, F, 0, T, 2, F, 0 },
>> +        .should_be_valid = true,
>> +        .expect = (CpuTopology) { 4, 1, 2, 1, 2, 4 },
>> +    }, {
>> +        /* config: -smp dies=2,maxcpus=32
>> +         * expect: cpus=32,sockets=1,dies=2,cores=16,threads=1,maxcpus=32 */
>> +        .config = (SMPConfiguration) { F, 0, F, 0, T, 2, F, 0, F, 0, T, 32 },
>> +        .should_be_valid = true,
>> +        .expect = (CpuTopology) { 32, 1, 2, 16, 1, 32 },
>> +    }, {
>> +        /* config: -smp 16,sockets=2,dies=2
>> +         * expect: cpus=16,sockets=2,dies=2,cores=4,threads=1,maxcpus=16 */
>> +        .config = (SMPConfiguration) { T, 16, T, 2, T, 2, F, 0, F, 0, F, 0 },
>> +        .should_be_valid = true,
>> +        .expect = (CpuTopology) { 16, 2, 2, 4, 1, 16 },
>> +    }, {
>> +        /* config: -smp 16,dies=2,cores=4
>> +         * expect: cpus=16,sockets=2,dies=2,cores=4,threads=1,maxcpus=16 */
>> +        .config = (SMPConfiguration) { T, 16, F, 0, T, 2, T, 4, F, 0, F, 0 },
>> +        .should_be_valid = true,
>> +        .expect = (CpuTopology) { 16, 2, 2, 4, 1, 16 },
>> +    }, {
>> +        /* config: -smp 16,dies=2,threads=2
>> +         * expect: cpus=16,sockets=1,dies=2,cores=4,threads=2,maxcpus=16 */
>> +        .config = (SMPConfiguration) { T, 16, F, 0, T, 2, F, 0, T, 2, F, 0 },
>> +        .should_be_valid = true,
>> +        .expect = (CpuTopology) { 16, 1, 2, 4, 2, 16 },
>> +    }, {
>> +        /* config: -smp 16,dies=2,maxcpus=32
>> +         * expect: cpus=16,sockets=1,dies=2,cores=16,threads=1,maxcpus=32 */
>> +        .config = (SMPConfiguration) { T, 16, F, 0, T, 2, F, 0, F, 0, T, 32 },
>> +        .should_be_valid = true,
>> +        .expect = (CpuTopology) { 16, 1, 2, 16, 1, 32 },
>> +    }, {
>> +        /* config: -smp sockets=2,dies=2,cores=4
>> +         * expect: cpus=16,sockets=2,dies=2,cores=4,threads=1,maxcpus=16 */
>> +        .config = (SMPConfiguration) { F, 0, T, 2, T, 2, T, 4, F, 0, F, 0 },
>> +        .should_be_valid = true,
>> +        .expect = (CpuTopology) { 16, 2, 2, 4, 1, 16 },
>> +    }, {
>> +        /* config: -smp sockets=2,dies=2,threads=2
>> +         * expect: cpus=8,sockets=2,dies=2,cores=1,threads=2,maxcpus=8 */
>> +        .config = (SMPConfiguration) { F, 0, T, 2, T, 2, F, 0, T, 2, F, 0 },
>> +        .should_be_valid = true,
>> +        .expect = (CpuTopology) { 8, 2, 2, 1, 2, 8 },
>> +    }, {
>> +        /* config: -smp sockets=2,dies=2,maxcpus=32
>> +         * expect: cpus=32,sockets=2,dies=2,cores=8,threads=1,maxcpus=32 */
>> +        .config = (SMPConfiguration) { F, 0, T, 2, T, 2, F, 0, F, 0, T, 32 },
>> +        .should_be_valid = true,
>> +        .expect = (CpuTopology) { 32, 2, 2, 8, 1, 32 },
>> +    }, {
>> +        /* config: -smp dies=2,cores=4,threads=2
>> +         * expect: cpus=16,sockets=1,dies=2,cores=4,threads=2,maxcpus=16 */
>> +        .config = (SMPConfiguration) { F, 0, F, 0, T, 2, T, 4, T, 2, F, 0 },
>> +        .should_be_valid = true,
>> +        .expect = (CpuTopology) { 16, 1, 2, 4, 2, 16 },
>> +    }, {
>> +        /* config: -smp dies=2,cores=4,maxcpus=32
>> +         * expect: cpus=32,sockets=4,dies=2,cores=4,threads=1,maxcpus=32 */
>> +        .config = (SMPConfiguration) { F, 0, F, 0, T, 2, T, 4, F, 0, T, 32 },
>> +        .should_be_valid = true,
>> +        .expect = (CpuTopology) { 32, 4, 2, 4, 1, 32 },
>> +    }, {
>> +        /* config: -smp dies=2,threads=2,maxcpus=32
>> +         * expect: cpus=32,sockets=1,dies=2,cores=8,threads=2,maxcpus=32 */
>> +        .config = (SMPConfiguration) { F, 0, F, 0, T, 2, F, 0, T, 2, T, 32 },
>> +        .should_be_valid = true,
>> +        .expect = (CpuTopology) { 32, 1, 2, 8, 2, 32 },
>> +    }, {
>> +        /* config: -smp 16,sockets=2,dies=2,cores=4
>> +         * expect: cpus=16,sockets=2,dies=2,cores=4,threads=1,maxcpus=16 */
>> +        .config = (SMPConfiguration) { T, 16, T, 2, T, 2, T, 4, F, 0, F, 0 },
>> +        .should_be_valid = true,
>> +        .expect = (CpuTopology) { 16, 2, 2, 4, 1, 16 },
>> +    }, {
>> +        /* config: -smp 16,sockets=2,dies=2,threads=2
>> +         * expect: cpus=16,sockets=2,dies=2,cores=2,threads=2,maxcpus=16 */
>> +        .config = (SMPConfiguration) { T, 16, T, 2, T, 2, F, 0, T, 2, F, 0 },
>> +        .should_be_valid = true,
>> +        .expect = (CpuTopology) { 16, 2, 2, 2, 2, 16 },
>> +    }, {
>> +        /* config: -smp 16,sockets=2,dies=2,maxcpus=32
>> +         * expect: cpus=16,sockets=2,dies=2,cores=8,threads=1,maxcpus=32 */
>> +        .config = (SMPConfiguration) { T, 16, T, 2, T, 2, F, 0, F, 0, T, 32 },
>> +        .should_be_valid = true,
>> +        .expect = (CpuTopology) { 16, 2, 2, 8, 1, 32 },
>> +    }, {
>> +        /* config: -smp 16,dies=2,cores=4,threads=2
>> +         * expect: cpus=16,sockets=1,dies=2,cores=4,threads=2,maxcpus=16 */
>> +        .config = (SMPConfiguration) { T, 16, F, 0, T, 2, T, 4, T, 2, F, 0 },
>> +        .should_be_valid = true,
>> +        .expect = (CpuTopology) { 16, 1, 2, 4, 2, 16 },
>> +    }, {
>> +        /* config: -smp 16,dies=2,cores=4,maxcpus=32
>> +         * expect: cpus=16,sockets=4,dies=2,cores=4,threads=1,maxcpus=32 */
>> +        .config = (SMPConfiguration) { T, 16, F, 0, T, 2, T, 4, F, 0, T, 32 },
>> +        .should_be_valid = true,
>> +        .expect = (CpuTopology) { 16, 4, 2, 4, 1, 32 },
>> +    }, {
>> +        /* config: -smp 16,dies=2,threads=2,maxcpus=32
>> +         * expect: cpus=16,sockets=1,dies=2,cores=8,threads=2,maxcpus=32 */
>> +        .config = (SMPConfiguration) { T, 16, F, 0, T, 2, F, 0, T, 2, T, 32 },
>> +        .should_be_valid = true,
>> +        .expect = (CpuTopology) { 16, 1, 2, 8, 2, 32 },
>> +    }, {
>> +        /* config: -smp sockets=2,dies=2,cores=4,threads=2
>> +         * expect: cpus=32,sockets=2,dies=2,cores=4,threads=2,maxcpus=32 */
>> +        .config = (SMPConfiguration) { F, 0, T, 2, T, 2, T, 4, T, 2, F, 0 },
>> +        .should_be_valid = true,
>> +        .expect = (CpuTopology) { 32, 2, 2, 4, 2, 32 },
>> +    }, {
>> +        /* config: -smp sockets=2,dies=2,cores=4,maxcpus=32
>> +         * expect: cpus=32,sockets=2,dies=2,cores=4,threads=2,maxcpus=32 */
>> +        .config = (SMPConfiguration) { F, 0, T, 2, T, 2, T, 4, F, 0, T, 32 },
>> +        .should_be_valid = true,
>> +        .expect = (CpuTopology) { 32, 2, 2, 4, 2, 32 },
>> +    }, {
>> +        /* config: -smp sockets=2,dies=2,threads=2,maxcpus=32
>> +         * expect: cpus=32,sockets=2,dies=2,cores=4,threads=2,maxcpus=32 */
>> +        .config = (SMPConfiguration) { F, 0, T, 2, T, 2, F, 0, T, 2, T, 32 },
>> +        .should_be_valid = true,
>> +        .expect = (CpuTopology) { 32, 2, 2, 4, 2, 32 },
>> +    }, {
>> +        /* config: -smp dies=2,cores=4,threads=2,maxcpus=32
>> +         * expect: cpus=32,sockets=2,dies=2,cores=4,threads=2,maxcpus=32 */
>> +        .config = (SMPConfiguration) { F, 0, F, 0, T, 2, T, 4, T, 2, T, 32 },
>> +        .should_be_valid = true,
>> +        .expect = (CpuTopology) { 32, 2, 2, 4, 2, 32 },
>> +    }, {
>> +        /* config: -smp 16,sockets=2,dies=2,cores=4,threads=1
>> +         * expect: cpus=16,sockets=2,dies=2,cores=4,threads=1,maxcpus=16 */
>> +        .config = (SMPConfiguration) { T, 16, T, 2, T, 2, T, 4, T, 1, F, 0 },
>> +        .should_be_valid = true,
>> +        .expect = (CpuTopology) { 16, 2, 2, 4, 1, 16 },
>> +    }, {
>> +        /* config: -smp 16,sockets=2,dies=2,cores=4,maxcpus=32
>> +         * expect: cpus=16,sockets=2,dies=2,cores=4,threads=2,maxcpus=32 */
>> +        .config = (SMPConfiguration) { T, 16, T, 2, T, 2, T, 4, F, 0, T, 32 },
>> +        .should_be_valid = true,
>> +        .expect = (CpuTopology) { 16, 2, 2, 4, 2, 32 },
>> +    }, {
>> +        /* config: -smp 16,sockets=2,dies=2,threads=2,maxcpus=32
>> +         * expect: cpus=16,sockets=2,dies=2,cores=4,threads=2,maxcpus=32 */
>> +        .config = (SMPConfiguration) { T, 16, T, 2, T, 2, F, 0, T, 2, T, 32 },
>> +        .should_be_valid = true,
>> +        .expect = (CpuTopology) { 16, 2, 2, 4, 2, 32 },
>> +    }, {
>> +        /* config: -smp 16,dies=2,cores=4,threads=2,maxcpus=32
>> +         * expect: cpus=16,sockets=2,dies=2,cores=4,threads=2,maxcpus=32 */
>> +        .config = (SMPConfiguration) { T, 16, F, 0, T, 2, T, 4, T, 2, T, 32 },
>> +        .should_be_valid = true,
>> +        .expect = (CpuTopology) { 16, 2, 2, 4, 2, 32 },
>> +    }, {
>> +        /* config: -smp sockets=2,dies=2,cores=4,threads=2,maxcpus=32
>> +         * expect: -smp 32,sockets=2,dies=2,cores=4,threads=2,maxcpus=32 */
>> +        .config = (SMPConfiguration) { F, 0, T, 2, T, 2, T, 4, T, 2, T, 32 },
>> +        .should_be_valid = true,
>> +        .expect = (CpuTopology) { 32, 2, 2, 4, 2, 32 },
>> +    }, {
>> +        /* config: -smp 16,sockets=2,dies=2,cores=4,threads=2,maxcpus=32
>> +         * expect: cpus=16,sockets=2,dies=2,cores=4,threads=2,maxcpus=32 */
>> +        .config = (SMPConfiguration) { T, 16, T, 2, T, 2, T, 4, T, 2, T, 32 },
>> +        .should_be_valid = true,
>> +        .expect = (CpuTopology) { 16, 2, 2, 4, 2, 32 },
>> +    },
>> +};
>> +
>> +static char *get_config_info(SMPConfiguration *config)
>> +{
>> +    return g_strdup_printf(
>> +        "(SMPConfiguration) {\n"
>> +        "    .has_cpus    = %5s, cpus    = %ld,\n"
>> +        "    .has_sockets = %5s, sockets = %ld,\n"
>> +        "    .has_dies    = %5s, dies    = %ld,\n"
>> +        "    .has_cores   = %5s, cores   = %ld,\n"
>> +        "    .has_threads = %5s, threads = %ld,\n"
>> +        "    .has_maxcpus = %5s, maxcpus = %ld,\n"
>> +        "}",
>> +        config->has_cpus ? "true" : "false", config->cpus,
>> +        config->has_sockets ? "true" : "false", config->sockets,
>> +        config->has_dies ? "true" : "false", config->dies,
>> +        config->has_cores ? "true" : "false", config->cores,
>> +        config->has_threads ? "true" : "false", config->threads,
>> +        config->has_maxcpus ? "true" : "false", config->maxcpus);
>> +}
>> +
>> +static char *get_topo_info(CpuTopology *topo)
>> +{
>> +    return g_strdup_printf(
>> +        "(CpuTopology) {\n"
>> +        "    .cpus     = %u,\n"
>> +        "    .sockets  = %u,\n"
>> +        "    .dies     = %u,\n"
>> +        "    .cores    = %u,\n"
>> +        "    .threads  = %u,\n"
>> +        "    .max_cpus = %u,\n"
>> +        "}",
>> +        topo->cpus, topo->sockets, topo->dies,
>> +        topo->cores, topo->threads, topo->max_cpus);
>> +}
>> +
>> +static void check_smp_parse(MachineState *ms, SMPTestData *data)
>> +{
>> +    SMPConfiguration *config = &data->config;
>> +    CpuTopology *expect = &data->expect;
>> +    g_autofree char *config_info = NULL;
>> +    g_autofree char *expect_info = NULL;
>> +    g_autofree char *result_info = NULL;
>> +    Error *err = NULL;
>> +
>> +    /* call the generic parser smp_parse() in hw/core/machine-smp.c */
>> +    smp_parse(ms, config, &err);
>> +
>> +    if (data->should_be_valid) {
>> +        if ((err == NULL) &&
>> +            (ms->smp.cpus == expect->cpus) &&
>> +            (ms->smp.sockets == expect->sockets) &&
>> +            (ms->smp.dies == expect->dies) &&
>> +            (ms->smp.cores == expect->cores) &&
>> +            (ms->smp.threads == expect->threads) &&
>> +            (ms->smp.max_cpus == expect->max_cpus)) {
>> +            return;
>> +        }
>> +
>> +        config_info = get_config_info(config);
>> +        expect_info = get_topo_info(expect);
>> +
>> +        if (err != NULL) {
>> +            g_printerr("Check smp_parse failed:\n"
>> +                       "config: %s\n"
>> +                       "expect: %s\n"
>> +                       "should_be_valid: yes\n\n"
>> +                       "result_is_valid: no\n"
>> +                       "error_msg: %s\n",
>> +                       config_info, expect_info, error_get_pretty(err));
>> +            error_free(err);
>> +        } else {
>> +            result_info = get_topo_info(&ms->smp);
>> +            g_printerr("Check smp_parse failed:\n"
>> +                       "config: %s\n"
>> +                       "expect: %s\n"
>> +                       "should_be_valid: yes\n\n"
>> +                       "result_is_valid: yes\n"
>> +                       "result: %s\n",
>> +                       config_info, expect_info, result_info);
>> +        }
>> +    } else {
>> +        if (err != NULL) {
>> +            error_free(err);
>> +            return;
>> +        }
>> +
>> +        config_info = get_config_info(config);
>> +        result_info = get_topo_info(&ms->smp);
>> +
>> +        g_printerr("Check smp_parse failed:\n"
>> +                   "config: %s\n"
>> +                   "should_be_valid: no\n\n"
>> +                   "result_is_valid: yes\n"
>> +                   "result: %s\n",
>> +                   config_info, result_info);
>> +    }
>> +
>> +    abort();
>> +}
>> +
>> +static void smp_prefer_sockets_test(void)
>> +{
>> +    Object *obj = object_new(TYPE_MACHINE);
>> +    MachineState *ms = MACHINE(obj);
>> +    MachineClass *mc = MACHINE_GET_CLASS(obj);
>> +    int i;
>> +
>> +    /* make sure that we have created the object */
>> +    g_assert_nonnull(ms);
>> +    g_assert_nonnull(mc);
>> +
>> +    mc->smp_prefer_sockets = true;
>> +
>> +    /* test cases when multi-dies are not supported */
>> +    mc->smp_dies_supported = false;
>> +    for (i = 0; i < ARRAY_SIZE(prefer_sockets); i++) {
>> +        check_smp_parse(ms, &prefer_sockets[i]);
>> +    }
>> +
>> +    /* test cases when multi-dies are supported */
>> +    mc->smp_dies_supported = true;
>> +    for (i = 0; i < ARRAY_SIZE(prefer_sockets_support_dies); i++) {
>> +        check_smp_parse(ms, &prefer_sockets_support_dies[i]);
>> +    }
>> +
>> +    object_unref(obj);
>> +}
>> +
>> +static void smp_prefer_cores_test(void)
>> +{
>> +    Object *obj = object_new(TYPE_MACHINE);
>> +    MachineState *ms = MACHINE(obj);
>> +    MachineClass *mc = MACHINE_GET_CLASS(obj);
>> +    int i;
>> +
>> +    /* make sure that we have created the object */
>> +    g_assert_nonnull(ms);
>> +    g_assert_nonnull(mc);
>> +
>> +    mc->smp_prefer_sockets = false;
>> +
>> +    /* test cases when multi-dies are not supported */
>> +    mc->smp_dies_supported = false;
>> +    for (i = 0; i < ARRAY_SIZE(prefer_cores); i++) {
>> +        check_smp_parse(ms, &prefer_cores[i]);
>> +    }
>> +
>> +    /* test cases when multi-dies are supported */
>> +    mc->smp_dies_supported = true;
>> +    for (i = 0; i < ARRAY_SIZE(prefer_cores_support_dies); i++) {
>> +        check_smp_parse(ms, &prefer_cores_support_dies[i]);
>> +    }
>> +
>> +    object_unref(obj);
>> +}
>> +
>> +int main(int argc, char *argv[])
>> +{
>> +    g_test_init(&argc, &argv, NULL);
>> +
>> +    module_call_init(MODULE_INIT_QOM);
>> +    type_register_static(&smp_machine_info);
>> +
>> +    g_test_add_func("/test-smp-parse/prefer_sockets", smp_prefer_sockets_test);
>> +    g_test_add_func("/test-smp-parse/prefer_cores", smp_prefer_cores_test);
>> +
>> +    g_test_run();
>> +
>> +    return 0;
>> +}
>> -- 
>> 2.19.1
>>
> Besides the nits, it looks good to me.
>
> Reviewed-by: Andrew Jones <drjones@redhat.com>
>
Thanks,
Yanan
.
> .



^ permalink raw reply	[flat|nested] 58+ messages in thread

* Re: [PATCH for-6.2 v2 10/11] machine: Split out the smp parsing code
  2021-07-19 17:20   ` Andrew Jones
@ 2021-07-22  6:24     ` wangyanan (Y)
  2021-07-22 13:07       ` Andrew Jones
  0 siblings, 1 reply; 58+ messages in thread
From: wangyanan (Y) @ 2021-07-22  6:24 UTC (permalink / raw)
  To: Andrew Jones
  Cc: Peter Maydell, Thomas Huth, Daniel P. Berrangé,
	Eduardo Habkost, Michael S . Tsirkin, wanghaibin.wang,
	Richard Henderson, qemu-devel, Greg Kurz, Halil Pasic,
	Igor Mammedov, yuzenghui, Paolo Bonzini, David Gibson

On 2021/7/20 1:20, Andrew Jones wrote:
> On Mon, Jul 19, 2021 at 11:20:42AM +0800, Yanan Wang wrote:
>> We are going to introduce an unit test for the parser smp_parse()
>> in hw/core/machine.c, but now machine.c is only built in softmmu.
>>
>> In order to solve the build dependency on the smp parsing code and
>> avoid building unrelated stuff for the unit tests, move the related
>> code from machine.c into a new common file, i.e., machine-smp.c.
>>
>> Signed-off-by: Yanan Wang <wangyanan55@huawei.com>
>> ---
>>   MAINTAINERS           |   1 +
>>   hw/core/machine-smp.c | 124 ++++++++++++++++++++++++++++++++++++++++++
>>   hw/core/machine.c     | 109 -------------------------------------
>>   hw/core/meson.build   |   1 +
>>   include/hw/boards.h   |   1 +
>>   5 files changed, 127 insertions(+), 109 deletions(-)
>>   create mode 100644 hw/core/machine-smp.c
>>
>> diff --git a/MAINTAINERS b/MAINTAINERS
>> index 9100f9a043..70633e3bf4 100644
>> --- a/MAINTAINERS
>> +++ b/MAINTAINERS
>> @@ -1626,6 +1626,7 @@ F: cpu.c
>>   F: hw/core/cpu.c
>>   F: hw/core/machine-qmp-cmds.c
>>   F: hw/core/machine.c
>> +F: hw/core/machine-smp.c
>>   F: hw/core/null-machine.c
>>   F: hw/core/numa.c
>>   F: hw/cpu/cluster.c
>> diff --git a/hw/core/machine-smp.c b/hw/core/machine-smp.c
>> new file mode 100644
>> index 0000000000..6a00cfe44a
>> --- /dev/null
>> +++ b/hw/core/machine-smp.c
>> @@ -0,0 +1,124 @@
>> +/*
>> + * QEMU Machine (related to SMP configuration)
>> + *
>> + * Copyright (C) 2014 Red Hat Inc
>> + *
>> + * Authors:
>> + *   Marcel Apfelbaum <marcel.a@redhat.com>
> This header was obviously copy+pasted without being updated.
Yes, the header was kept unchanged.

But actually I'm not completely sure which field should be updated. :)
Should I add "Copyright (C) 2021, Huawei, Inc." and also the authorship
"Yanan Wang <wangyanan55@huawei.com>" behind the existing ones
or just replace them?
>> + *
>> + * This work is licensed under the terms of the GNU GPL, version 2 or later.
>> + * See the COPYING file in the top-level directory.
>> + */
>> +
>> +#include "qemu/osdep.h"
>> +#include "hw/boards.h"
>> +#include "qapi/error.h"
>> +
>> +/*
>> + * smp_parse - Generic function used to parse the given SMP configuration
>> + *
>> + * The topology parameters must be specified equal to or great than one
>> + * or just omitted, explicit configuration like "cpus=0" is not allowed.
>> + * The omitted parameters will be calculated based on the provided ones.
>> + *
>> + * maxcpus will default to the value of cpus if omitted and will be used
>> + * to compute the missing sockets/cores/threads. cpus will be calculated
>> + * from the computed parametrs if omitted.
>> + *
>> + * In calculation of omitted arch-netural sockets/cores/threads, we prefer
>> + * sockets over cores over threads before 6.2, while prefer cores over
>> + * sockets over threads since 6.2 on. The arch-specific dies will directly
>> + * default to 1 if omitted.
>> + */
>> +void smp_parse(MachineState *ms, SMPConfiguration *config, Error **errp)
>> +{
>> +    MachineClass *mc = MACHINE_GET_CLASS(ms);
>> +    unsigned cpus    = config->has_cpus ? config->cpus : 0;
>> +    unsigned sockets = config->has_sockets ? config->sockets : 0;
>> +    unsigned dies    = config->has_dies ? config->dies : 1;
>> +    unsigned cores   = config->has_cores ? config->cores : 0;
>> +    unsigned threads = config->has_threads ? config->threads : 0;
>> +    unsigned maxcpus = config->has_maxcpus ? config->maxcpus : 0;
>> +
>> +    if ((config->has_cpus && config->cpus == 0) ||
>> +        (config->has_sockets && config->sockets == 0) ||
>> +        (config->has_dies && config->dies == 0) ||
>> +        (config->has_cores && config->cores == 0) ||
>> +        (config->has_threads && config->threads == 0) ||
>> +        (config->has_maxcpus && config->maxcpus == 0)) {
>> +        error_setg(errp, "parameters must be equal to or greater than one"
>> +                   "if provided");
>> +        return;
>> +    }
>> +
>> +    if (!mc->smp_dies_supported && dies > 1) {
>> +        error_setg(errp, "dies not supported by this machine's CPU topology");
>> +        return;
>> +    }
>> +
>> +    maxcpus = maxcpus > 0 ? maxcpus : cpus;
>> +
>> +    /* prefer sockets over cores over threads before 6.2 */
>> +    if (mc->smp_prefer_sockets) {
>> +        if (sockets == 0) {
>> +            cores = cores > 0 ? cores : 1;
>> +            threads = threads > 0 ? threads : 1;
>> +            sockets = maxcpus / (dies * cores * threads);
>> +            sockets = sockets > 0 ? sockets : 1;
>> +        } else if (cores == 0) {
>> +            threads = threads > 0 ? threads : 1;
>> +            cores = maxcpus / (sockets * dies * threads);
>> +            cores = cores > 0 ? cores : 1;
>> +        } else if (threads == 0) {
>> +            threads = maxcpus / (sockets * dies * cores);
>> +            threads = threads > 0 ? threads : 1;
>> +        }
>> +    /* prefer cores over sockets over threads since 6.2 */
>> +    } else {
>> +        if (cores == 0) {
>> +            sockets = sockets > 0 ? sockets : 1;
>> +            threads = threads > 0 ? threads : 1;
>> +            cores = maxcpus / (sockets * dies * threads);
>> +            cores = cores > 0 ? cores : 1;
>> +        } else if (sockets == 0) {
>> +            threads = threads > 0 ? threads : 1;
>> +            sockets = maxcpus / (dies * cores * threads);
>> +            sockets = sockets > 0 ? sockets : 1;
>> +        } else if (threads == 0) {
>> +            threads = maxcpus / (sockets * dies * cores);
>> +            threads = threads > 0 ? threads : 1;
>> +        }
>> +    }
>> +
>> +    /* use the computed parameters to calculate the omitted cpus */
>> +    cpus = cpus > 0 ? cpus : sockets * dies * cores * threads;
>> +    maxcpus = maxcpus > 0 ? maxcpus : cpus;
>> +
>> +    if (sockets * dies * cores * threads != maxcpus) {
>> +        g_autofree char *dies_msg = g_strdup_printf(
>> +            mc->smp_dies_supported ? " * dies (%u)" : "", dies);
>> +        error_setg(errp, "Invalid CPU topology: "
>> +                   "sockets (%u)%s * cores (%u) * threads (%u) "
>> +                   "!= maxcpus (%u)",
>> +                   sockets, dies_msg, cores, threads,
>> +                   maxcpus);
>> +        return;
>> +    }
>> +
>> +    if (sockets * dies * cores * threads < cpus) {
>> +        g_autofree char *dies_msg = g_strdup_printf(
>> +            mc->smp_dies_supported ? " * dies (%u)" : "", dies);
>> +        error_setg(errp, "Invalid CPU topology: "
>> +                   "sockets (%u)%s * cores (%u) * threads (%u) < "
>> +                   "smp_cpus (%u)",
>> +                   sockets, dies_msg, cores, threads, cpus);
>> +        return;
>> +    }
>> +
>> +    ms->smp.cpus = cpus;
>> +    ms->smp.sockets = sockets;
>> +    ms->smp.dies = dies;
>> +    ms->smp.cores = cores;
>> +    ms->smp.threads = threads;
>> +    ms->smp.max_cpus = maxcpus;
>> +}
>> diff --git a/hw/core/machine.c b/hw/core/machine.c
>> index 9d24b67ef3..61be266b6c 100644
>> --- a/hw/core/machine.c
>> +++ b/hw/core/machine.c
>> @@ -744,115 +744,6 @@ void machine_set_cpu_numa_node(MachineState *machine,
>>       }
>>   }
>>   
>> -/*
>> - * smp_parse - Generic function used to parse the given SMP configuration
>> - *
>> - * The topology parameters must be specified equal to or great than one
>> - * or just omitted, explicit configuration like "cpus=0" is not allowed.
>> - * The omitted parameters will be calculated based on the provided ones.
>> - *
>> - * maxcpus will default to the value of cpus if omitted and will be used
>> - * to compute the missing sockets/cores/threads. cpus will be calculated
>> - * from the computed parametrs if omitted.
>> - *
>> - * In calculation of omitted arch-netural sockets/cores/threads, we prefer
>> - * sockets over cores over threads before 6.2, while prefer cores over
>> - * sockets over threads since 6.2 on. The arch-specific dies will directly
>> - * default to 1 if omitted.
>> - */
>> -static void smp_parse(MachineState *ms, SMPConfiguration *config, Error **errp)
>> -{
>> -    MachineClass *mc = MACHINE_GET_CLASS(ms);
>> -    unsigned cpus    = config->has_cpus ? config->cpus : 0;
>> -    unsigned sockets = config->has_sockets ? config->sockets : 0;
>> -    unsigned dies    = config->has_dies ? config->dies : 1;
>> -    unsigned cores   = config->has_cores ? config->cores : 0;
>> -    unsigned threads = config->has_threads ? config->threads : 0;
>> -    unsigned maxcpus = config->has_maxcpus ? config->maxcpus : 0;
>> -
>> -    if ((config->has_cpus && config->cpus == 0) ||
>> -        (config->has_sockets && config->sockets == 0) ||
>> -        (config->has_dies && config->dies == 0) ||
>> -        (config->has_cores && config->cores == 0) ||
>> -        (config->has_threads && config->threads == 0) ||
>> -        (config->has_maxcpus && config->maxcpus == 0)) {
>> -        error_setg(errp, "parameters must be equal to or greater than one"
>> -                   "if provided");
>> -        return;
>> -    }
>> -
>> -    if (!mc->smp_dies_supported && dies > 1) {
>> -        error_setg(errp, "dies not supported by this machine's CPU topology");
>> -        return;
>> -    }
>> -
>> -    maxcpus = maxcpus > 0 ? maxcpus : cpus;
>> -
>> -    /* prefer sockets over cores over threads before 6.2 */
>> -    if (mc->smp_prefer_sockets) {
>> -        if (sockets == 0) {
>> -            cores = cores > 0 ? cores : 1;
>> -            threads = threads > 0 ? threads : 1;
>> -            sockets = maxcpus / (dies * cores * threads);
>> -            sockets = sockets > 0 ? sockets : 1;
>> -        } else if (cores == 0) {
>> -            threads = threads > 0 ? threads : 1;
>> -            cores = maxcpus / (sockets * dies * threads);
>> -            cores = cores > 0 ? cores : 1;
>> -        } else if (threads == 0) {
>> -            threads = maxcpus / (sockets * dies * cores);
>> -            threads = threads > 0 ? threads : 1;
>> -        }
>> -    /* prefer cores over sockets over threads since 6.2 */
>> -    } else {
>> -        if (cores == 0) {
>> -            sockets = sockets > 0 ? sockets : 1;
>> -            threads = threads > 0 ? threads : 1;
>> -            cores = maxcpus / (sockets * dies * threads);
>> -            cores = cores > 0 ? cores : 1;
>> -        } else if (sockets == 0) {
>> -            threads = threads > 0 ? threads : 1;
>> -            sockets = maxcpus / (dies * cores * threads);
>> -            sockets = sockets > 0 ? sockets : 1;
>> -        } else if (threads == 0) {
>> -            threads = maxcpus / (sockets * dies * cores);
>> -            threads = threads > 0 ? threads : 1;
>> -        }
>> -    }
>> -
>> -    /* use the computed parameters to calculate the omitted cpus */
>> -    cpus = cpus > 0 ? cpus : sockets * dies * cores * threads;
>> -    maxcpus = maxcpus > 0 ? maxcpus : cpus;
>> -
>> -    if (sockets * dies * cores * threads != maxcpus) {
>> -        g_autofree char *dies_msg = g_strdup_printf(
>> -            mc->smp_dies_supported ? " * dies (%u)" : "", dies);
>> -        error_setg(errp, "Invalid CPU topology: "
>> -                   "sockets (%u)%s * cores (%u) * threads (%u) "
>> -                   "!= maxcpus (%u)",
>> -                   sockets, dies_msg, cores, threads,
>> -                   maxcpus);
>> -        return;
>> -    }
>> -
>> -    if (sockets * dies * cores * threads < cpus) {
>> -        g_autofree char *dies_msg = g_strdup_printf(
>> -            mc->smp_dies_supported ? " * dies (%u)" : "", dies);
>> -        error_setg(errp, "Invalid CPU topology: "
>> -                   "sockets (%u)%s * cores (%u) * threads (%u) < "
>> -                   "smp_cpus (%u)",
>> -                   sockets, dies_msg, cores, threads, cpus);
>> -        return;
>> -    }
>> -
>> -    ms->smp.cpus = cpus;
>> -    ms->smp.sockets = sockets;
>> -    ms->smp.dies = dies;
>> -    ms->smp.cores = cores;
>> -    ms->smp.threads = threads;
>> -    ms->smp.max_cpus = maxcpus;
>> -}
>> -
>>   static void machine_get_smp(Object *obj, Visitor *v, const char *name,
>>                               void *opaque, Error **errp)
>>   {
>> diff --git a/hw/core/meson.build b/hw/core/meson.build
>> index 18f44fb7c2..6d727c7742 100644
>> --- a/hw/core/meson.build
>> +++ b/hw/core/meson.build
>> @@ -14,6 +14,7 @@ hwcore_files = files(
>>   )
>>   
>>   common_ss.add(files('cpu-common.c'))
>> +common_ss.add(files('machine-smp.c'))
>>   common_ss.add(when: 'CONFIG_FITLOADER', if_true: files('loader-fit.c'))
>>   common_ss.add(when: 'CONFIG_GENERIC_LOADER', if_true: files('generic-loader.c'))
>>   common_ss.add(when: ['CONFIG_GUEST_LOADER', fdt], if_true: files('guest-loader.c'))
>> diff --git a/include/hw/boards.h b/include/hw/boards.h
>> index 12ab0f5968..071eec1e74 100644
>> --- a/include/hw/boards.h
>> +++ b/include/hw/boards.h
>> @@ -34,6 +34,7 @@ HotpluggableCPUList *machine_query_hotpluggable_cpus(MachineState *machine);
>>   void machine_set_cpu_numa_node(MachineState *machine,
>>                                  const CpuInstanceProperties *props,
>>                                  Error **errp);
>> +void smp_parse(MachineState *ms, SMPConfiguration *config, Error **errp);
>>   
>>   /**
>>    * machine_class_allow_dynamic_sysbus_dev: Add type to list of valid devices
>> -- 
>> 2.19.1
>>
> Otherwise
>
> Reviewed-by: Andrew Jones <drjones@redhat.com>
>
Thanks,
Yanan
.



^ permalink raw reply	[flat|nested] 58+ messages in thread

* Re: [PATCH for-6.2 v2 02/11] machine: Make smp_parse generic enough for all arches
  2021-07-20  6:57   ` Cornelia Huck
@ 2021-07-22  7:12     ` wangyanan (Y)
  0 siblings, 0 replies; 58+ messages in thread
From: wangyanan (Y) @ 2021-07-22  7:12 UTC (permalink / raw)
  To: Cornelia Huck, qemu-devel
  Cc: Peter Maydell, Andrew Jones, Daniel P . Berrangé,
	Eduardo Habkost, Pierre Morel, Michael S . Tsirkin,
	Richard Henderson, Greg Kurz, Halil Pasic, Paolo Bonzini,
	Thomas Huth, IgorMammedov, yuzenghui, wanghaibin.wang,
	David Gibson

On 2021/7/20 14:57, Cornelia Huck wrote:
> On Mon, Jul 19 2021, Yanan Wang <wangyanan55@huawei.com> wrote:
>
>> Currently the only difference between smp_parse and pc_smp_parse
>> is the support of multi-dies and the related error reporting code.
>> With an arch compat variable "bool smp_dies_supported", we can
>> easily make smp_parse generic enough for all arches and the PC
>> specific one can be removed.
>>
>> Making smp_parse() generic enough can reduce code duplication and
>> ease the code maintenance, and also allows extending the topology
>> with more arch specific members (e.g., clusters) in the future.
> So I guess that should also allow us to include s390x books/drawers?
I guess so. Ideally, we hope for a generic parser which is friendly to 
different
arch-specific members. We now have included PC specific dies, then I think
maybe we should also welcome s390 specific books/drawers in.

Now only sockets/cores/threads will be automatically calculated if omitted,
the later introduced members (e.g. dies) will directly default to 1 if 
omitted.
So if we also treat books/drawers like dies, at least the calculation 
code will
be easy to maintain when new members come in the generic parser.
>> No functional change intended.
>>
>> Suggested-by: Andrew Jones <drjones@redhat.com>
>> Signed-off-by: Yanan Wang <wangyanan55@huawei.com>
>> ---
>>   hw/core/machine.c   | 28 ++++++++++-------
>>   hw/i386/pc.c        | 76 +--------------------------------------------
>>   include/hw/boards.h |  1 +
>>   3 files changed, 19 insertions(+), 86 deletions(-)
>>
>> diff --git a/hw/core/machine.c b/hw/core/machine.c
>> index d73daa10f4..ed6712e964 100644
>> --- a/hw/core/machine.c
>> +++ b/hw/core/machine.c
>> @@ -743,6 +743,7 @@ void machine_set_cpu_numa_node(MachineState *machine,
>>   
>>   static void smp_parse(MachineState *ms, SMPConfiguration *config, Error **errp)
>>   {
>> +    MachineClass *mc = MACHINE_GET_CLASS(ms);
>>       unsigned cpus    = config->has_cpus ? config->cpus : 0;
>>       unsigned sockets = config->has_sockets ? config->sockets : 0;
>>       unsigned dies    = config->has_dies ? config->dies : 1;
>> @@ -761,7 +762,7 @@ static void smp_parse(MachineState *ms, SMPConfiguration *config, Error **errp)
>>           return;
>>       }
>>   
>> -    if (dies > 1) {
>> +    if (!mc->smp_dies_supported && dies > 1) {
>>           error_setg(errp, "dies not supported by this machine's CPU topology");
>>           return;
>>       }
> I'm wondering how we should handle parameters that are not supported by
> a certain machine type. E.g. if we add support for books/drawers,
> specifying them is unlikely to make sense on anything but s390x. Would
> we allow to specify books=1 on a non-s390x machine, even though that is
> quite bogus? Or do we want to disallow setting any parameters that are
> not supported by the machine type?
The "parameter = 1" specification for a machine type that doesn't support
it will not affect the calculation and also the reporting. The value 
will be 1
in the calculation and we can chose to only report the supported parameters
in the error message.

The only problem is whether we should reject "parameter=1" for the unrelated
machine types. It seems that libvirt now unconditionally default dies to 
1, so if
we reject "dies=1" unexpected errors may be caused.

Now books and drawers are introduced, I also wonder how we plan to deal with
these two members in libvirt, just like dies or different with it ?
>> @@ -772,23 +773,25 @@ static void smp_parse(MachineState *ms, SMPConfiguration *config, Error **errp)
>>           threads = threads > 0 ? threads : 1;
>>           if (cpus == 0) {
>>               sockets = sockets > 0 ? sockets : 1;
>> -            cpus = cores * threads * sockets;
>> +            cpus = sockets * dies * cores * threads;
>>           } else {
>>               maxcpus = maxcpus > 0 ? maxcpus : cpus;
>> -            sockets = maxcpus / (cores * threads);
>> +            sockets = maxcpus / (dies * cores * threads);
>>           }
>>       } else if (cores == 0) {
>>           threads = threads > 0 ? threads : 1;
>> -        cores = cpus / (sockets * threads);
>> +        cores = cpus / (sockets * dies * threads);
>>           cores = cores > 0 ? cores : 1;
>>       } else if (threads == 0) {
>> -        threads = cpus / (cores * sockets);
>> +        threads = cpus / (sockets * dies * cores);
>>           threads = threads > 0 ? threads : 1;
>> -    } else if (sockets * cores * threads < cpus) {
>> +    } else if (sockets * dies * cores * threads < cpus) {
>> +        g_autofree char *dies_msg = g_strdup_printf(
>> +            mc->smp_dies_supported ? " * dies (%u)" : "", dies);
>>           error_setg(errp, "cpu topology: "
>> -                   "sockets (%u) * cores (%u) * threads (%u) < "
>> +                   "sockets (%u)%s * cores (%u) * threads (%u) < "
>>                      "smp_cpus (%u)",
>> -                   sockets, cores, threads, cpus);
>> +                   sockets, dies_msg, cores, threads, cpus);
>>           return;
>>       }
>>   
>> @@ -799,17 +802,20 @@ static void smp_parse(MachineState *ms, SMPConfiguration *config, Error **errp)
>>           return;
>>       }
>>   
>> -    if (sockets * cores * threads != maxcpus) {
>> +    if (sockets * dies * cores * threads != maxcpus) {
>> +        g_autofree char *dies_msg = g_strdup_printf(
>> +            mc->smp_dies_supported ? " * dies (%u)" : "", dies);
>>           error_setg(errp, "Invalid CPU topology: "
>> -                   "sockets (%u) * cores (%u) * threads (%u) "
>> +                   "sockets (%u)%s * cores (%u) * threads (%u) "
>>                      "!= maxcpus (%u)",
>> -                   sockets, cores, threads,
>> +                   sockets, dies_msg, cores, threads,
>>                      maxcpus);
> Similarily here; do we want to mention parameters that are not
> applicable for the machine type? That might be confusing, but a
> conditional error message may get too complex if we add some more
> parameters.
I think we'd better not report all the parameters. It's ok to use the arch
specific members in the internal calculation, but it's indeed confusing
to report them to users if they are not supported.

There may be a better implementation of the current conditional error
message reporting, I'm thinking about it...
>>           return;
>>       }
>>   
>>       ms->smp.cpus = cpus;
>>       ms->smp.sockets = sockets;
>> +    ms->smp.dies = dies;
>>       ms->smp.cores = cores;
>>       ms->smp.threads = threads;
>>       ms->smp.max_cpus = maxcpus;
>> diff --git a/hw/i386/pc.c b/hw/i386/pc.c
>> index c6b63c00a5..d94ef582b5 100644
>> --- a/hw/i386/pc.c
>> +++ b/hw/i386/pc.c
>> @@ -708,80 +708,6 @@ void pc_acpi_smi_interrupt(void *opaque, int irq, int level)
>>       }
>>   }
>>   
>> -/*
>> - * This function is very similar to smp_parse()
>> - * in hw/core/machine.c but includes CPU die support.
>> - */
>> -static void pc_smp_parse(MachineState *ms, SMPConfiguration *config, Error **errp)
>> -{
>> -    unsigned cpus    = config->has_cpus ? config->cpus : 0;
>> -    unsigned sockets = config->has_sockets ? config->sockets : 0;
>> -    unsigned dies    = config->has_dies ? config->dies : 1;
>> -    unsigned cores   = config->has_cores ? config->cores : 0;
>> -    unsigned threads = config->has_threads ? config->threads : 0;
>> -    unsigned maxcpus = config->has_maxcpus ? config->maxcpus : 0;
>> -
>> -    if ((config->has_cpus && config->cpus == 0) ||
>> -        (config->has_sockets && config->sockets == 0) ||
>> -        (config->has_dies && config->dies == 0) ||
>> -        (config->has_cores && config->cores == 0) ||
>> -        (config->has_threads && config->threads == 0) ||
>> -        (config->has_maxcpus && config->maxcpus == 0)) {
>> -        error_setg(errp, "parameters must be equal to or greater than one"
>> -                   "if provided");
>> -        return;
>> -    }
>> -
>> -    /* compute missing values, prefer sockets over cores over threads */
>> -    if (cpus == 0 || sockets == 0) {
>> -        cores = cores > 0 ? cores : 1;
>> -        threads = threads > 0 ? threads : 1;
>> -        if (cpus == 0) {
>> -            sockets = sockets > 0 ? sockets : 1;
>> -            cpus = cores * threads * dies * sockets;
>> -        } else {
>> -            maxcpus = maxcpus > 0 ? maxcpus : cpus;
>> -            sockets = maxcpus / (cores * threads * dies);
>> -        }
>> -    } else if (cores == 0) {
>> -        threads = threads > 0 ? threads : 1;
>> -        cores = cpus / (sockets * dies * threads);
>> -        cores = cores > 0 ? cores : 1;
>> -    } else if (threads == 0) {
>> -        threads = cpus / (cores * dies * sockets);
>> -        threads = threads > 0 ? threads : 1;
>> -    } else if (sockets * dies * cores * threads < cpus) {
>> -        error_setg(errp, "cpu topology: "
>> -                   "sockets (%u) * dies (%u) * cores (%u) * threads (%u) < "
>> -                   "smp_cpus (%u)",
>> -                   sockets, dies, cores, threads, cpus);
>> -        return;
>> -    }
>> -
>> -    maxcpus = maxcpus > 0 ? maxcpus : cpus;
>> -
>> -    if (maxcpus < cpus) {
>> -        error_setg(errp, "maxcpus must be equal to or greater than smp");
>> -        return;
>> -    }
>> -
>> -    if (sockets * dies * cores * threads != maxcpus) {
>> -        error_setg(errp, "Invalid CPU topology deprecated: "
>> -                   "sockets (%u) * dies (%u) * cores (%u) * threads (%u) "
>> -                   "!= maxcpus (%u)",
>> -                   sockets, dies, cores, threads,
>> -                   maxcpus);
>> -        return;
>> -    }
>> -
>> -    ms->smp.cpus = cpus;
>> -    ms->smp.sockets = sockets;
>> -    ms->smp.dies = dies;
>> -    ms->smp.cores = cores;
>> -    ms->smp.threads = threads;
>> -    ms->smp.max_cpus = maxcpus;
>> -}
>> -
>>   static
>>   void pc_machine_done(Notifier *notifier, void *data)
>>   {
>> @@ -1735,7 +1661,6 @@ static void pc_machine_class_init(ObjectClass *oc, void *data)
>>       mc->auto_enable_numa_with_memdev = true;
>>       mc->has_hotpluggable_cpus = true;
>>       mc->default_boot_order = "cad";
>> -    mc->smp_parse = pc_smp_parse;
> We can probably remove smp_parse, and call the generic parser directly?
Yes, I think we can.

Thanks,
Yanan
.
>>       mc->block_default_type = IF_IDE;
>>       mc->max_cpus = 255;
>>       mc->reset = pc_machine_reset;
>> @@ -1746,6 +1671,7 @@ static void pc_machine_class_init(ObjectClass *oc, void *data)
>>       hc->unplug = pc_machine_device_unplug_cb;
>>       mc->default_cpu_type = TARGET_DEFAULT_CPU_TYPE;
>>       mc->nvdimm_supported = true;
>> +    mc->smp_dies_supported = true;
>>       mc->default_ram_id = "pc.ram";
>>   
>>       object_class_property_add(oc, PC_MACHINE_MAX_RAM_BELOW_4G, "size",
>> diff --git a/include/hw/boards.h b/include/hw/boards.h
>> index accd6eff35..b6161cee88 100644
>> --- a/include/hw/boards.h
>> +++ b/include/hw/boards.h
>> @@ -246,6 +246,7 @@ struct MachineClass {
>>       bool smbus_no_migration_support;
>>       bool nvdimm_supported;
>>       bool numa_mem_supported;
>> +    bool smp_dies_supported;
>>       bool auto_enable_numa;
>>       const char *default_ram_id;
>>   
>> -- 
>> 2.19.1
> .



^ permalink raw reply	[flat|nested] 58+ messages in thread

* Re: [PATCH for-6.2 v2 02/11] machine: Make smp_parse generic enough for all arches
  2021-07-19 16:53   ` Daniel P. Berrangé
@ 2021-07-22  7:18     ` wangyanan (Y)
  0 siblings, 0 replies; 58+ messages in thread
From: wangyanan (Y) @ 2021-07-22  7:18 UTC (permalink / raw)
  To: Daniel P. Berrangé
  Cc: Peter Maydell, Andrew Jones, Eduardo Habkost,
	Michael S . Tsirkin, Richard Henderson, qemu-devel, Greg Kurz,
	Halil Pasic, Paolo Bonzini, Thomas Huth, Igor Mammedov,
	yuzenghui, wanghaibin.wang, David Gibson

On 2021/7/20 0:53, Daniel P. Berrangé wrote:
> On Mon, Jul 19, 2021 at 11:20:34AM +0800, Yanan Wang wrote:
>> Currently the only difference between smp_parse and pc_smp_parse
>> is the support of multi-dies and the related error reporting code.
>> With an arch compat variable "bool smp_dies_supported", we can
>> easily make smp_parse generic enough for all arches and the PC
>> specific one can be removed.
>>
>> Making smp_parse() generic enough can reduce code duplication and
>> ease the code maintenance, and also allows extending the topology
>> with more arch specific members (e.g., clusters) in the future.
>>
>> No functional change intended.
>>
>> Suggested-by: Andrew Jones <drjones@redhat.com>
>> Signed-off-by: Yanan Wang <wangyanan55@huawei.com>
>> ---
>>   hw/core/machine.c   | 28 ++++++++++-------
>>   hw/i386/pc.c        | 76 +--------------------------------------------
>>   include/hw/boards.h |  1 +
>>   3 files changed, 19 insertions(+), 86 deletions(-)
>>
>> diff --git a/hw/core/machine.c b/hw/core/machine.c
>> index d73daa10f4..ed6712e964 100644
>> --- a/hw/core/machine.c
>> +++ b/hw/core/machine.c
>> @@ -743,6 +743,7 @@ void machine_set_cpu_numa_node(MachineState *machine,
>>   
>>   static void smp_parse(MachineState *ms, SMPConfiguration *config, Error **errp)
>>   {
>> +    MachineClass *mc = MACHINE_GET_CLASS(ms);
>>       unsigned cpus    = config->has_cpus ? config->cpus : 0;
>>       unsigned sockets = config->has_sockets ? config->sockets : 0;
>>       unsigned dies    = config->has_dies ? config->dies : 1;
>> @@ -761,7 +762,7 @@ static void smp_parse(MachineState *ms, SMPConfiguration *config, Error **errp)
>>           return;
>>       }
>>   
>> -    if (dies > 1) {
>> +    if (!mc->smp_dies_supported && dies > 1) {
>>           error_setg(errp, "dies not supported by this machine's CPU topology");
>>           return;
>>       }
>> @@ -772,23 +773,25 @@ static void smp_parse(MachineState *ms, SMPConfiguration *config, Error **errp)
>>           threads = threads > 0 ? threads : 1;
>>           if (cpus == 0) {
>>               sockets = sockets > 0 ? sockets : 1;
>> -            cpus = cores * threads * sockets;
>> +            cpus = sockets * dies * cores * threads;
>>           } else {
>>               maxcpus = maxcpus > 0 ? maxcpus : cpus;
>> -            sockets = maxcpus / (cores * threads);
>> +            sockets = maxcpus / (dies * cores * threads);
>>           }
>>       } else if (cores == 0) {
>>           threads = threads > 0 ? threads : 1;
>> -        cores = cpus / (sockets * threads);
>> +        cores = cpus / (sockets * dies * threads);
>>           cores = cores > 0 ? cores : 1;
>>       } else if (threads == 0) {
>> -        threads = cpus / (cores * sockets);
>> +        threads = cpus / (sockets * dies * cores);
>>           threads = threads > 0 ? threads : 1;
>> -    } else if (sockets * cores * threads < cpus) {
>> +    } else if (sockets * dies * cores * threads < cpus) {
>> +        g_autofree char *dies_msg = g_strdup_printf(
>> +            mc->smp_dies_supported ? " * dies (%u)" : "", dies);
>>           error_setg(errp, "cpu topology: "
>> -                   "sockets (%u) * cores (%u) * threads (%u) < "
>> +                   "sockets (%u)%s * cores (%u) * threads (%u) < "
>>                      "smp_cpus (%u)",
>> -                   sockets, cores, threads, cpus);
>> +                   sockets, dies_msg, cores, threads, cpus);
> Since we're allowing dies=1 (but not greater), I'm not convinced we
> need the conditionally built error message, and could just include
> "* dies" all the time.
>
> If we do want it to be conditionally different though, I'd just
> sugest calling error_setg twice. Although this duplicates stuff,
> it'll be clearer to read which I think is a net win.
>
The duplicates may increase quickly if more arch specific members
are introduced, I think the conditional error reporting may still be
necessary, but should be more clearer than current approach. :)

Thanks,
Yanan
.



^ permalink raw reply	[flat|nested] 58+ messages in thread

* Re: [PATCH for-6.2 v2 00/11] machine: smp parsing fixes and improvement
  2021-07-21 12:38   ` wangyanan (Y)
  2021-07-21 13:52     ` Pankaj Gupta
@ 2021-07-22  7:51     ` Pierre Morel
  2021-07-22  8:32       ` wangyanan (Y)
  1 sibling, 1 reply; 58+ messages in thread
From: Pierre Morel @ 2021-07-22  7:51 UTC (permalink / raw)
  To: wangyanan (Y), Cornelia Huck, qemu-devel
  Cc: Peter Maydell, Andrew Jones, Daniel P . Berrangé,
	Eduardo Habkost, Michael S . Tsirkin, Richard Henderson,
	Greg Kurz, Halil Pasic, Paolo Bonzini, Thomas Huth,
	Igor Mammedov, yuzenghui, wanghaibin.wang, David Gibson



On 7/21/21 2:38 PM, wangyanan (Y) wrote:
> On 2021/7/20 0:57, Cornelia Huck wrote:
>> On Mon, Jul 19 2021, Yanan Wang <wangyanan55@huawei.com> wrote:
>>
>>> Hi,
>>>
>>> This is v2 of the series [1] that I have posted to introduce some smp 
>>> parsing
>>> fixes and improvement, much more work has been processed compared to 
>>> RFC v1.
>>>
>>> [1] https://lists.gnu.org/archive/html/qemu-devel/2021-07/msg00259.html
>>>
>>> The purpose of this series is to improve/fix the parsing logic. 
>>> Explicitly
>>> specifying a CPU topology parameter as zero is not allowed any more, and
>>> maxcpus is now uniformly used to calculate the omitted parameters. 
>>> It's also
>>> suggested that we should start to prefer cores over sockets over 
>>> threads on
>>> the newer machine types, which will make the computed virtual 
>>> topology more
>>> reflective of the real hardware.
>>>
>>> In order to reduce code duplication and ease the code maintenance, 
>>> smp_parse
>>> in now converted into a parser generic enough for all arches, so that 
>>> the PC
>>> specific one can be removed. It's also convenient to introduce more 
>>> topology
>>> members (e.g. cluster) to the generic parser in the future.
>> Cc:ing Pierre, as he also had been looking at the smp parsing code (for
>> s390x) recently.
>>
>> Also, please keep me on cc: for patches that touch s390x.
> Sure, I will. Sorry about the missing. :)
> 
> Thanks,
> Yanan
> .
And me too please.
Thanks
Pierre

-- 
Pierre Morel
IBM Lab Boeblingen


^ permalink raw reply	[flat|nested] 58+ messages in thread

* Re: [PATCH for-6.2 v2 05/11] machine: Improve the error reporting of smp parsing
  2021-07-19 16:53   ` Andrew Jones
@ 2021-07-22  8:10     ` wangyanan (Y)
  2021-07-22 12:47       ` Andrew Jones
  0 siblings, 1 reply; 58+ messages in thread
From: wangyanan (Y) @ 2021-07-22  8:10 UTC (permalink / raw)
  To: Andrew Jones
  Cc: Peter Maydell, Thomas Huth, Daniel P. Berrangé,
	Eduardo Habkost, Michael S . Tsirkin, wanghaibin.wang,
	Richard Henderson, qemu-devel, Greg Kurz, Halil Pasic,
	Igor Mammedov, yuzenghui, Paolo Bonzini, David Gibson

On 2021/7/20 0:53, Andrew Jones wrote:
> On Mon, Jul 19, 2021 at 11:20:37AM +0800, Yanan Wang wrote:
>> We totally have two requirements for a valid SMP configuration:
> s/totally//
>
>> the sum of "sockets * dies * cores * threads" must represent all
> the product
>
>> the possible cpus, i.e., max_cpus, and must include the initial
>> present cpus, i.e., smp_cpus.
>>
>> We only need to ensure "sockets * dies * cores * threads == maxcpus"
>> at first and then ensure "sockets * dies * cores * threads >= cpus".
> Or, "maxcpus >= cpus"
>
>> With a reasonable order of the sanity-check, we can simplify the
>> error reporting code.
>>
>> Signed-off-by: Yanan Wang <wangyanan55@huawei.com>
>> ---
>>   hw/core/machine.c | 25 ++++++++++---------------
>>   1 file changed, 10 insertions(+), 15 deletions(-)
>>
>> diff --git a/hw/core/machine.c b/hw/core/machine.c
>> index 668f0a1553..8b4d07d3fc 100644
>> --- a/hw/core/machine.c
>> +++ b/hw/core/machine.c
>> @@ -788,21 +788,6 @@ static void smp_parse(MachineState *ms, SMPConfiguration *config, Error **errp)
>>       cpus = cpus > 0 ? cpus : sockets * dies * cores * threads;
>>       maxcpus = maxcpus > 0 ? maxcpus : cpus;
>>   
>> -    if (sockets * dies * cores * threads < cpus) {
>> -        g_autofree char *dies_msg = g_strdup_printf(
>> -            mc->smp_dies_supported ? " * dies (%u)" : "", dies);
>> -        error_setg(errp, "cpu topology: "
>> -                   "sockets (%u)%s * cores (%u) * threads (%u) < "
>> -                   "smp_cpus (%u)",
>> -                   sockets, dies_msg, cores, threads, cpus);
>> -        return;
>> -    }
>> -
>> -    if (maxcpus < cpus) {
>> -        error_setg(errp, "maxcpus must be equal to or greater than smp");
>> -        return;
>> -    }
> This may be redundant when determining a valid config, but by checking it
> separately we can provide a more useful error message.
Yes, this message is more useful. Can we also report the exact values of the
parameters within this error message ? How about the following:

if (sockets * cores * threads != maxcpus) {
     error_setg("product of the topology must be equal to maxcpus"
                "sockets (%u) * cores (%u) * threads (%u)"
                "!= maxcpus (%u)",
                sockets, cores, threads, maxcpus);
return;
}

if (maxcpus < cpus) {
     error_setg("maxcpus must be equal to or greater than smp:"
                "sockets (%u) * cores (%u) * threads (%u)"
                "== maxcpus (%u) < smp_cpus (%u)",
                sockets, cores, threads, maxcpus, cpus);
return;
}

Thanks,
Yanan
.
>> -
>>       if (sockets * dies * cores * threads != maxcpus) {
>>           g_autofree char *dies_msg = g_strdup_printf(
>>               mc->smp_dies_supported ? " * dies (%u)" : "", dies);
>> @@ -814,6 +799,16 @@ static void smp_parse(MachineState *ms, SMPConfiguration *config, Error **errp)
>>           return;
>>       }
>>   
>> +    if (sockets * dies * cores * threads < cpus) {
>> +        g_autofree char *dies_msg = g_strdup_printf(
>> +            mc->smp_dies_supported ? " * dies (%u)" : "", dies);
>> +        error_setg(errp, "Invalid CPU topology: "
>> +                   "sockets (%u)%s * cores (%u) * threads (%u) < "
>> +                   "smp_cpus (%u)",
>> +                   sockets, dies_msg, cores, threads, cpus);
>> +        return;
>> +    }
>> +
>>       ms->smp.cpus = cpus;
>>       ms->smp.sockets = sockets;
>>       ms->smp.dies = dies;
>> -- 
>> 2.19.1
>>
> I'm not sure we need this patch.
>
> Thanks,
> drew
>
> .



^ permalink raw reply	[flat|nested] 58+ messages in thread

* Re: [PATCH for-6.2 v2 00/11] machine: smp parsing fixes and improvement
  2021-07-22  7:51     ` Pierre Morel
@ 2021-07-22  8:32       ` wangyanan (Y)
  0 siblings, 0 replies; 58+ messages in thread
From: wangyanan (Y) @ 2021-07-22  8:32 UTC (permalink / raw)
  To: Pierre Morel, Cornelia Huck, qemu-devel
  Cc: Peter Maydell, Andrew Jones, Daniel P . Berrangé,
	Eduardo Habkost, Michael S . Tsirkin, Richard Henderson,
	Greg Kurz, Halil Pasic, Paolo Bonzini, Thomas Huth,
	Igor Mammedov, yuzenghui, wanghaibin.wang, David Gibson

On 2021/7/22 15:51, Pierre Morel wrote:
>
>
> On 7/21/21 2:38 PM, wangyanan (Y) wrote:
>> On 2021/7/20 0:57, Cornelia Huck wrote:
>>> On Mon, Jul 19 2021, Yanan Wang <wangyanan55@huawei.com> wrote:
>>>
>>>> Hi,
>>>>
>>>> This is v2 of the series [1] that I have posted to introduce some 
>>>> smp parsing
>>>> fixes and improvement, much more work has been processed compared 
>>>> to RFC v1.
>>>>
>>>> [1] 
>>>> https://lists.gnu.org/archive/html/qemu-devel/2021-07/msg00259.html
>>>>
>>>> The purpose of this series is to improve/fix the parsing logic. 
>>>> Explicitly
>>>> specifying a CPU topology parameter as zero is not allowed any 
>>>> more, and
>>>> maxcpus is now uniformly used to calculate the omitted parameters. 
>>>> It's also
>>>> suggested that we should start to prefer cores over sockets over 
>>>> threads on
>>>> the newer machine types, which will make the computed virtual 
>>>> topology more
>>>> reflective of the real hardware.
>>>>
>>>> In order to reduce code duplication and ease the code maintenance, 
>>>> smp_parse
>>>> in now converted into a parser generic enough for all arches, so 
>>>> that the PC
>>>> specific one can be removed. It's also convenient to introduce more 
>>>> topology
>>>> members (e.g. cluster) to the generic parser in the future.
>>> Cc:ing Pierre, as he also had been looking at the smp parsing code (for
>>> s390x) recently.
>>>
>>> Also, please keep me on cc: for patches that touch s390x.
>> Sure, I will. Sorry about the missing. :)
>>
>> Thanks,
>> Yanan
>> .
> And me too please.
> Thanks
> Pierre
>
Of course.

Thanks,
Yanan



^ permalink raw reply	[flat|nested] 58+ messages in thread

* Re: [PATCH for-6.2 v2 04/11] machine: Use the computed parameters to calculate omitted cpus
  2021-07-22  4:42     ` wangyanan (Y)
@ 2021-07-22 12:27       ` Andrew Jones
  2021-07-22 14:59         ` wangyanan (Y)
  0 siblings, 1 reply; 58+ messages in thread
From: Andrew Jones @ 2021-07-22 12:27 UTC (permalink / raw)
  To: wangyanan (Y)
  Cc: Peter Maydell, Thomas Huth, Daniel P. Berrangé,
	Eduardo Habkost, Michael S . Tsirkin, wanghaibin.wang,
	Richard Henderson, qemu-devel, Greg Kurz, Halil Pasic,
	Igor Mammedov, yuzenghui, Paolo Bonzini, David Gibson

On Thu, Jul 22, 2021 at 12:42:55PM +0800, wangyanan (Y) wrote:
> On 2021/7/20 0:42, Andrew Jones wrote:
> > On Mon, Jul 19, 2021 at 11:20:36AM +0800, Yanan Wang wrote:
> > > Currently we directly calculate the omitted cpus based on the already
> > > provided parameters. This makes some cmdlines like:
> > >    -smp maxcpus=16
> > >    -smp sockets=2,maxcpus=16
> > >    -smp sockets=2,dies=2,maxcpus=16
> > >    -smp sockets=2,cores=4,maxcpus=16
> > > not work. We should probably use the computed paramters to calculate
> > > cpus when maxcpus is provided while cpus is omitted, which will make
> > > above configs start to work.
> > > 
> > > Note: change in this patch won't affect any existing working cmdlines
> > > but allows more incomplete configs to be valid.
> > > 
> > > Signed-off-by: Yanan Wang <wangyanan55@huawei.com>
> > > ---
> > >   hw/core/machine.c | 17 +++++++++--------
> > >   1 file changed, 9 insertions(+), 8 deletions(-)
> > > 
> > > diff --git a/hw/core/machine.c b/hw/core/machine.c
> > > index c9f15b15a5..668f0a1553 100644
> > > --- a/hw/core/machine.c
> > > +++ b/hw/core/machine.c
> > > @@ -767,26 +767,27 @@ static void smp_parse(MachineState *ms, SMPConfiguration *config, Error **errp)
> > >           return;
> > >       }
> > > -    /* compute missing values, prefer sockets over cores over threads */
> > >       maxcpus = maxcpus > 0 ? maxcpus : cpus;
> > > -    if (cpus == 0) {
> > > -        sockets = sockets > 0 ? sockets : 1;
> > > -        cores = cores > 0 ? cores : 1;
> > > -        threads = threads > 0 ? threads : 1;
> > > -        cpus = sockets * dies * cores * threads;
> > > -        maxcpus = maxcpus > 0 ? maxcpus : cpus;
> > > -    } else if (sockets == 0) {
> > > +    /* compute missing values, prefer sockets over cores over threads */
> > > +    if (sockets == 0) {
> > >           cores = cores > 0 ? cores : 1;
> > >           threads = threads > 0 ? threads : 1;
> > >           sockets = maxcpus / (dies * cores * threads);
> > > +        sockets = sockets > 0 ? sockets : 1;
> > >       } else if (cores == 0) {
> > >           threads = threads > 0 ? threads : 1;
> > >           cores = maxcpus / (sockets * dies * threads);
> > > +        cores = cores > 0 ? cores : 1;
> > >       } else if (threads == 0) {
> > >           threads = maxcpus / (sockets * dies * cores);
> > > +        threads = threads > 0 ? threads : 1;
> > >       }
> > I didn't think we wanted this rounding which this patch adds back into
> > cores and threads and now also sockets.
> Firstly, I think we can agree that with or without the rounding, the invalid
> configs will still be reported as invalid. So the only difference is in the
> err
> message (either report 0 or 1 of a fractional parameter). :)

An error message that says the inputs produced a fractional parameter
would be even better. If the code gets too hairy because some parameters
may be zero and without additional checks we'd risk divide by zeros,
then maybe we should output ("fractional %s", param_name) and exit at the
same places we're currently rounding up.

> 
> I added back the rounding because this patch indeed need it, we start
> to use the computed parameters to calculate cpus, so we have to ensure
> that the computed parameters are at least 1. If both cpus and maxcpus
> are omitted (e.g. -smp sockets=16), without the rounding we will get
> zeroed cpus and maxcpus, and with the rounding we will get valid result
> like "cpus=16,sockets=16,cores=1,threads=1,maxcpus=16".
> 
> If a "0" or "1" in the error message doesn't make so much difference as
> assumed for the error reporting, I suggest that we probably can keep the
> rounding which makes the parser code concise.
> > > +    /* use the computed parameters to calculate the omitted cpus */
> > > +    cpus = cpus > 0 ? cpus : sockets * dies * cores * threads;
> > > +    maxcpus = maxcpus > 0 ? maxcpus : cpus;
> > It doesn't really matter, but I think I'd rather write this like
> > 
> >   maxcpus = maxcpus > 0 ? maxcpus : sockets * dies * cores * threads;
> >   cpus = cpus > 0 ? cpus : maxcpus;
> Yes, there is no functional difference. But I think maybe we'd better keep
> some consistence with the "maxcpus = maxcpus > 0 ? maxcpus : cpus"
> at top this function and also with the smp doc in qemu-option.hx i.e.
> "If omitted the maximum number of CPUs will be set to match the initial
> CPU count" ?

I won't argue it too much, but I think we should be thinking about maxcpus
more than cpus if we're thinking about cpu topologies. I'd rather have
users keep in mind that whatever their topology generates is the max and
if they don't want to expose that many cpus to the guest then they should
provide an additional, smaller number 'cpus'. To get that mindset we may
need to adjust the documentation.

Thanks,
drew



^ permalink raw reply	[flat|nested] 58+ messages in thread

* Re: [PATCH for-6.2 v2 05/11] machine: Improve the error reporting of smp parsing
  2021-07-22  8:10     ` wangyanan (Y)
@ 2021-07-22 12:47       ` Andrew Jones
  0 siblings, 0 replies; 58+ messages in thread
From: Andrew Jones @ 2021-07-22 12:47 UTC (permalink / raw)
  To: wangyanan (Y)
  Cc: Peter Maydell, Thomas Huth, Daniel P. Berrangé,
	Eduardo Habkost, Michael S . Tsirkin, wanghaibin.wang,
	Richard Henderson, qemu-devel, Greg Kurz, Halil Pasic,
	Igor Mammedov, yuzenghui, Paolo Bonzini, David Gibson

On Thu, Jul 22, 2021 at 04:10:32PM +0800, wangyanan (Y) wrote:
> On 2021/7/20 0:53, Andrew Jones wrote:
> > On Mon, Jul 19, 2021 at 11:20:37AM +0800, Yanan Wang wrote:
> > > We totally have two requirements for a valid SMP configuration:
> > s/totally//
> > 
> > > the sum of "sockets * dies * cores * threads" must represent all
> > the product
> > 
> > > the possible cpus, i.e., max_cpus, and must include the initial
> > > present cpus, i.e., smp_cpus.
> > > 
> > > We only need to ensure "sockets * dies * cores * threads == maxcpus"
> > > at first and then ensure "sockets * dies * cores * threads >= cpus".
> > Or, "maxcpus >= cpus"
> > 
> > > With a reasonable order of the sanity-check, we can simplify the
> > > error reporting code.
> > > 
> > > Signed-off-by: Yanan Wang <wangyanan55@huawei.com>
> > > ---
> > >   hw/core/machine.c | 25 ++++++++++---------------
> > >   1 file changed, 10 insertions(+), 15 deletions(-)
> > > 
> > > diff --git a/hw/core/machine.c b/hw/core/machine.c
> > > index 668f0a1553..8b4d07d3fc 100644
> > > --- a/hw/core/machine.c
> > > +++ b/hw/core/machine.c
> > > @@ -788,21 +788,6 @@ static void smp_parse(MachineState *ms, SMPConfiguration *config, Error **errp)
> > >       cpus = cpus > 0 ? cpus : sockets * dies * cores * threads;
> > >       maxcpus = maxcpus > 0 ? maxcpus : cpus;
> > > -    if (sockets * dies * cores * threads < cpus) {
> > > -        g_autofree char *dies_msg = g_strdup_printf(
> > > -            mc->smp_dies_supported ? " * dies (%u)" : "", dies);
> > > -        error_setg(errp, "cpu topology: "
> > > -                   "sockets (%u)%s * cores (%u) * threads (%u) < "
> > > -                   "smp_cpus (%u)",
> > > -                   sockets, dies_msg, cores, threads, cpus);
> > > -        return;
> > > -    }
> > > -
> > > -    if (maxcpus < cpus) {
> > > -        error_setg(errp, "maxcpus must be equal to or greater than smp");
> > > -        return;
> > > -    }
> > This may be redundant when determining a valid config, but by checking it
> > separately we can provide a more useful error message.
> Yes, this message is more useful. Can we also report the exact values of the
> parameters within this error message ?

sure

> How about the following:
> 
> if (sockets * cores * threads != maxcpus) {
>     error_setg("product of the topology must be equal to maxcpus"
>                "sockets (%u) * cores (%u) * threads (%u)"
>                "!= maxcpus (%u)",
>                sockets, cores, threads, maxcpus);
> return;
> }
> 
> if (maxcpus < cpus) {
>     error_setg("maxcpus must be equal to or greater than smp:"
>                "sockets (%u) * cores (%u) * threads (%u)"
>                "== maxcpus (%u) < smp_cpus (%u)",
>                sockets, cores, threads, maxcpus, cpus);
> return;
> }

OK by me

Thanks,
drew

> 
> Thanks,
> Yanan
> .
> > > -
> > >       if (sockets * dies * cores * threads != maxcpus) {
> > >           g_autofree char *dies_msg = g_strdup_printf(
> > >               mc->smp_dies_supported ? " * dies (%u)" : "", dies);
> > > @@ -814,6 +799,16 @@ static void smp_parse(MachineState *ms, SMPConfiguration *config, Error **errp)
> > >           return;
> > >       }
> > > +    if (sockets * dies * cores * threads < cpus) {
> > > +        g_autofree char *dies_msg = g_strdup_printf(
> > > +            mc->smp_dies_supported ? " * dies (%u)" : "", dies);
> > > +        error_setg(errp, "Invalid CPU topology: "
> > > +                   "sockets (%u)%s * cores (%u) * threads (%u) < "
> > > +                   "smp_cpus (%u)",
> > > +                   sockets, dies_msg, cores, threads, cpus);
> > > +        return;
> > > +    }
> > > +
> > >       ms->smp.cpus = cpus;
> > >       ms->smp.sockets = sockets;
> > >       ms->smp.dies = dies;
> > > -- 
> > > 2.19.1
> > > 
> > I'm not sure we need this patch.
> > 
> > Thanks,
> > drew
> > 
> > .
> 



^ permalink raw reply	[flat|nested] 58+ messages in thread

* Re: [PATCH for-6.2 v2 10/11] machine: Split out the smp parsing code
  2021-07-22  6:24     ` wangyanan (Y)
@ 2021-07-22 13:07       ` Andrew Jones
  2021-07-22 14:29         ` wangyanan (Y)
  0 siblings, 1 reply; 58+ messages in thread
From: Andrew Jones @ 2021-07-22 13:07 UTC (permalink / raw)
  To: wangyanan (Y)
  Cc: Peter Maydell, Thomas Huth, Daniel P. Berrangé,
	Eduardo Habkost, Michael S . Tsirkin, wanghaibin.wang,
	Richard Henderson, qemu-devel, Greg Kurz, Halil Pasic,
	Igor Mammedov, yuzenghui, Paolo Bonzini, David Gibson

On Thu, Jul 22, 2021 at 02:24:03PM +0800, wangyanan (Y) wrote:
> On 2021/7/20 1:20, Andrew Jones wrote:
> > On Mon, Jul 19, 2021 at 11:20:42AM +0800, Yanan Wang wrote:
> > > We are going to introduce an unit test for the parser smp_parse()
> > > in hw/core/machine.c, but now machine.c is only built in softmmu.
> > > 
> > > In order to solve the build dependency on the smp parsing code and
> > > avoid building unrelated stuff for the unit tests, move the related
> > > code from machine.c into a new common file, i.e., machine-smp.c.
> > > 
> > > Signed-off-by: Yanan Wang <wangyanan55@huawei.com>
> > > ---
> > >   MAINTAINERS           |   1 +
> > >   hw/core/machine-smp.c | 124 ++++++++++++++++++++++++++++++++++++++++++
> > >   hw/core/machine.c     | 109 -------------------------------------
> > >   hw/core/meson.build   |   1 +
> > >   include/hw/boards.h   |   1 +
> > >   5 files changed, 127 insertions(+), 109 deletions(-)
> > >   create mode 100644 hw/core/machine-smp.c
> > > 
> > > diff --git a/MAINTAINERS b/MAINTAINERS
> > > index 9100f9a043..70633e3bf4 100644
> > > --- a/MAINTAINERS
> > > +++ b/MAINTAINERS
> > > @@ -1626,6 +1626,7 @@ F: cpu.c
> > >   F: hw/core/cpu.c
> > >   F: hw/core/machine-qmp-cmds.c
> > >   F: hw/core/machine.c
> > > +F: hw/core/machine-smp.c

I just noticed that the spacing in this change might not be right.

> > >   F: hw/core/null-machine.c
> > >   F: hw/core/numa.c
> > >   F: hw/cpu/cluster.c
> > > diff --git a/hw/core/machine-smp.c b/hw/core/machine-smp.c
> > > new file mode 100644
> > > index 0000000000..6a00cfe44a
> > > --- /dev/null
> > > +++ b/hw/core/machine-smp.c
> > > @@ -0,0 +1,124 @@
> > > +/*
> > > + * QEMU Machine (related to SMP configuration)
> > > + *
> > > + * Copyright (C) 2014 Red Hat Inc
> > > + *
> > > + * Authors:
> > > + *   Marcel Apfelbaum <marcel.a@redhat.com>
> > This header was obviously copy+pasted without being updated.
> Yes, the header was kept unchanged.
> 
> But actually I'm not completely sure which field should be updated. :)
> Should I add "Copyright (C) 2021, Huawei, Inc." and also the authorship
> "Yanan Wang <wangyanan55@huawei.com>" behind the existing ones
> or just replace them?

I see what you were attempting to do now. You were deriving this new work
(a source file) from an existing work and you wanted to preserve the
original copyright and authorship. It's not so simple with these types of
projects though. In this case, smp_parse wasn't even part of the original
machine.c file (it came over with commit 6f479566a87d). I think it's
pretty common for these projects to just put whatever your preferred
(or your employer's preferred) copyright/authorship on new files. So, I'd
just replace the fields.

I'm interested in what others have to say about this though.

Thanks,
drew


> > > + *
> > > + * This work is licensed under the terms of the GNU GPL, version 2 or later.
> > > + * See the COPYING file in the top-level directory.
> > > + */
> > > +
> > > +#include "qemu/osdep.h"
> > > +#include "hw/boards.h"
> > > +#include "qapi/error.h"
> > > +
> > > +/*
> > > + * smp_parse - Generic function used to parse the given SMP configuration
> > > + *
> > > + * The topology parameters must be specified equal to or great than one
> > > + * or just omitted, explicit configuration like "cpus=0" is not allowed.
> > > + * The omitted parameters will be calculated based on the provided ones.
> > > + *
> > > + * maxcpus will default to the value of cpus if omitted and will be used
> > > + * to compute the missing sockets/cores/threads. cpus will be calculated
> > > + * from the computed parametrs if omitted.
> > > + *
> > > + * In calculation of omitted arch-netural sockets/cores/threads, we prefer
> > > + * sockets over cores over threads before 6.2, while prefer cores over
> > > + * sockets over threads since 6.2 on. The arch-specific dies will directly
> > > + * default to 1 if omitted.
> > > + */
> > > +void smp_parse(MachineState *ms, SMPConfiguration *config, Error **errp)
> > > +{
> > > +    MachineClass *mc = MACHINE_GET_CLASS(ms);
> > > +    unsigned cpus    = config->has_cpus ? config->cpus : 0;
> > > +    unsigned sockets = config->has_sockets ? config->sockets : 0;
> > > +    unsigned dies    = config->has_dies ? config->dies : 1;
> > > +    unsigned cores   = config->has_cores ? config->cores : 0;
> > > +    unsigned threads = config->has_threads ? config->threads : 0;
> > > +    unsigned maxcpus = config->has_maxcpus ? config->maxcpus : 0;
> > > +
> > > +    if ((config->has_cpus && config->cpus == 0) ||
> > > +        (config->has_sockets && config->sockets == 0) ||
> > > +        (config->has_dies && config->dies == 0) ||
> > > +        (config->has_cores && config->cores == 0) ||
> > > +        (config->has_threads && config->threads == 0) ||
> > > +        (config->has_maxcpus && config->maxcpus == 0)) {
> > > +        error_setg(errp, "parameters must be equal to or greater than one"
> > > +                   "if provided");
> > > +        return;
> > > +    }
> > > +
> > > +    if (!mc->smp_dies_supported && dies > 1) {
> > > +        error_setg(errp, "dies not supported by this machine's CPU topology");
> > > +        return;
> > > +    }
> > > +
> > > +    maxcpus = maxcpus > 0 ? maxcpus : cpus;
> > > +
> > > +    /* prefer sockets over cores over threads before 6.2 */
> > > +    if (mc->smp_prefer_sockets) {
> > > +        if (sockets == 0) {
> > > +            cores = cores > 0 ? cores : 1;
> > > +            threads = threads > 0 ? threads : 1;
> > > +            sockets = maxcpus / (dies * cores * threads);
> > > +            sockets = sockets > 0 ? sockets : 1;
> > > +        } else if (cores == 0) {
> > > +            threads = threads > 0 ? threads : 1;
> > > +            cores = maxcpus / (sockets * dies * threads);
> > > +            cores = cores > 0 ? cores : 1;
> > > +        } else if (threads == 0) {
> > > +            threads = maxcpus / (sockets * dies * cores);
> > > +            threads = threads > 0 ? threads : 1;
> > > +        }
> > > +    /* prefer cores over sockets over threads since 6.2 */
> > > +    } else {
> > > +        if (cores == 0) {
> > > +            sockets = sockets > 0 ? sockets : 1;
> > > +            threads = threads > 0 ? threads : 1;
> > > +            cores = maxcpus / (sockets * dies * threads);
> > > +            cores = cores > 0 ? cores : 1;
> > > +        } else if (sockets == 0) {
> > > +            threads = threads > 0 ? threads : 1;
> > > +            sockets = maxcpus / (dies * cores * threads);
> > > +            sockets = sockets > 0 ? sockets : 1;
> > > +        } else if (threads == 0) {
> > > +            threads = maxcpus / (sockets * dies * cores);
> > > +            threads = threads > 0 ? threads : 1;
> > > +        }
> > > +    }
> > > +
> > > +    /* use the computed parameters to calculate the omitted cpus */
> > > +    cpus = cpus > 0 ? cpus : sockets * dies * cores * threads;
> > > +    maxcpus = maxcpus > 0 ? maxcpus : cpus;
> > > +
> > > +    if (sockets * dies * cores * threads != maxcpus) {
> > > +        g_autofree char *dies_msg = g_strdup_printf(
> > > +            mc->smp_dies_supported ? " * dies (%u)" : "", dies);
> > > +        error_setg(errp, "Invalid CPU topology: "
> > > +                   "sockets (%u)%s * cores (%u) * threads (%u) "
> > > +                   "!= maxcpus (%u)",
> > > +                   sockets, dies_msg, cores, threads,
> > > +                   maxcpus);
> > > +        return;
> > > +    }
> > > +
> > > +    if (sockets * dies * cores * threads < cpus) {
> > > +        g_autofree char *dies_msg = g_strdup_printf(
> > > +            mc->smp_dies_supported ? " * dies (%u)" : "", dies);
> > > +        error_setg(errp, "Invalid CPU topology: "
> > > +                   "sockets (%u)%s * cores (%u) * threads (%u) < "
> > > +                   "smp_cpus (%u)",
> > > +                   sockets, dies_msg, cores, threads, cpus);
> > > +        return;
> > > +    }
> > > +
> > > +    ms->smp.cpus = cpus;
> > > +    ms->smp.sockets = sockets;
> > > +    ms->smp.dies = dies;
> > > +    ms->smp.cores = cores;
> > > +    ms->smp.threads = threads;
> > > +    ms->smp.max_cpus = maxcpus;
> > > +}
> > > diff --git a/hw/core/machine.c b/hw/core/machine.c
> > > index 9d24b67ef3..61be266b6c 100644
> > > --- a/hw/core/machine.c
> > > +++ b/hw/core/machine.c
> > > @@ -744,115 +744,6 @@ void machine_set_cpu_numa_node(MachineState *machine,
> > >       }
> > >   }
> > > -/*
> > > - * smp_parse - Generic function used to parse the given SMP configuration
> > > - *
> > > - * The topology parameters must be specified equal to or great than one
> > > - * or just omitted, explicit configuration like "cpus=0" is not allowed.
> > > - * The omitted parameters will be calculated based on the provided ones.
> > > - *
> > > - * maxcpus will default to the value of cpus if omitted and will be used
> > > - * to compute the missing sockets/cores/threads. cpus will be calculated
> > > - * from the computed parametrs if omitted.
> > > - *
> > > - * In calculation of omitted arch-netural sockets/cores/threads, we prefer
> > > - * sockets over cores over threads before 6.2, while prefer cores over
> > > - * sockets over threads since 6.2 on. The arch-specific dies will directly
> > > - * default to 1 if omitted.
> > > - */
> > > -static void smp_parse(MachineState *ms, SMPConfiguration *config, Error **errp)
> > > -{
> > > -    MachineClass *mc = MACHINE_GET_CLASS(ms);
> > > -    unsigned cpus    = config->has_cpus ? config->cpus : 0;
> > > -    unsigned sockets = config->has_sockets ? config->sockets : 0;
> > > -    unsigned dies    = config->has_dies ? config->dies : 1;
> > > -    unsigned cores   = config->has_cores ? config->cores : 0;
> > > -    unsigned threads = config->has_threads ? config->threads : 0;
> > > -    unsigned maxcpus = config->has_maxcpus ? config->maxcpus : 0;
> > > -
> > > -    if ((config->has_cpus && config->cpus == 0) ||
> > > -        (config->has_sockets && config->sockets == 0) ||
> > > -        (config->has_dies && config->dies == 0) ||
> > > -        (config->has_cores && config->cores == 0) ||
> > > -        (config->has_threads && config->threads == 0) ||
> > > -        (config->has_maxcpus && config->maxcpus == 0)) {
> > > -        error_setg(errp, "parameters must be equal to or greater than one"
> > > -                   "if provided");
> > > -        return;
> > > -    }
> > > -
> > > -    if (!mc->smp_dies_supported && dies > 1) {
> > > -        error_setg(errp, "dies not supported by this machine's CPU topology");
> > > -        return;
> > > -    }
> > > -
> > > -    maxcpus = maxcpus > 0 ? maxcpus : cpus;
> > > -
> > > -    /* prefer sockets over cores over threads before 6.2 */
> > > -    if (mc->smp_prefer_sockets) {
> > > -        if (sockets == 0) {
> > > -            cores = cores > 0 ? cores : 1;
> > > -            threads = threads > 0 ? threads : 1;
> > > -            sockets = maxcpus / (dies * cores * threads);
> > > -            sockets = sockets > 0 ? sockets : 1;
> > > -        } else if (cores == 0) {
> > > -            threads = threads > 0 ? threads : 1;
> > > -            cores = maxcpus / (sockets * dies * threads);
> > > -            cores = cores > 0 ? cores : 1;
> > > -        } else if (threads == 0) {
> > > -            threads = maxcpus / (sockets * dies * cores);
> > > -            threads = threads > 0 ? threads : 1;
> > > -        }
> > > -    /* prefer cores over sockets over threads since 6.2 */
> > > -    } else {
> > > -        if (cores == 0) {
> > > -            sockets = sockets > 0 ? sockets : 1;
> > > -            threads = threads > 0 ? threads : 1;
> > > -            cores = maxcpus / (sockets * dies * threads);
> > > -            cores = cores > 0 ? cores : 1;
> > > -        } else if (sockets == 0) {
> > > -            threads = threads > 0 ? threads : 1;
> > > -            sockets = maxcpus / (dies * cores * threads);
> > > -            sockets = sockets > 0 ? sockets : 1;
> > > -        } else if (threads == 0) {
> > > -            threads = maxcpus / (sockets * dies * cores);
> > > -            threads = threads > 0 ? threads : 1;
> > > -        }
> > > -    }
> > > -
> > > -    /* use the computed parameters to calculate the omitted cpus */
> > > -    cpus = cpus > 0 ? cpus : sockets * dies * cores * threads;
> > > -    maxcpus = maxcpus > 0 ? maxcpus : cpus;
> > > -
> > > -    if (sockets * dies * cores * threads != maxcpus) {
> > > -        g_autofree char *dies_msg = g_strdup_printf(
> > > -            mc->smp_dies_supported ? " * dies (%u)" : "", dies);
> > > -        error_setg(errp, "Invalid CPU topology: "
> > > -                   "sockets (%u)%s * cores (%u) * threads (%u) "
> > > -                   "!= maxcpus (%u)",
> > > -                   sockets, dies_msg, cores, threads,
> > > -                   maxcpus);
> > > -        return;
> > > -    }
> > > -
> > > -    if (sockets * dies * cores * threads < cpus) {
> > > -        g_autofree char *dies_msg = g_strdup_printf(
> > > -            mc->smp_dies_supported ? " * dies (%u)" : "", dies);
> > > -        error_setg(errp, "Invalid CPU topology: "
> > > -                   "sockets (%u)%s * cores (%u) * threads (%u) < "
> > > -                   "smp_cpus (%u)",
> > > -                   sockets, dies_msg, cores, threads, cpus);
> > > -        return;
> > > -    }
> > > -
> > > -    ms->smp.cpus = cpus;
> > > -    ms->smp.sockets = sockets;
> > > -    ms->smp.dies = dies;
> > > -    ms->smp.cores = cores;
> > > -    ms->smp.threads = threads;
> > > -    ms->smp.max_cpus = maxcpus;
> > > -}
> > > -
> > >   static void machine_get_smp(Object *obj, Visitor *v, const char *name,
> > >                               void *opaque, Error **errp)
> > >   {
> > > diff --git a/hw/core/meson.build b/hw/core/meson.build
> > > index 18f44fb7c2..6d727c7742 100644
> > > --- a/hw/core/meson.build
> > > +++ b/hw/core/meson.build
> > > @@ -14,6 +14,7 @@ hwcore_files = files(
> > >   )
> > >   common_ss.add(files('cpu-common.c'))
> > > +common_ss.add(files('machine-smp.c'))
> > >   common_ss.add(when: 'CONFIG_FITLOADER', if_true: files('loader-fit.c'))
> > >   common_ss.add(when: 'CONFIG_GENERIC_LOADER', if_true: files('generic-loader.c'))
> > >   common_ss.add(when: ['CONFIG_GUEST_LOADER', fdt], if_true: files('guest-loader.c'))
> > > diff --git a/include/hw/boards.h b/include/hw/boards.h
> > > index 12ab0f5968..071eec1e74 100644
> > > --- a/include/hw/boards.h
> > > +++ b/include/hw/boards.h
> > > @@ -34,6 +34,7 @@ HotpluggableCPUList *machine_query_hotpluggable_cpus(MachineState *machine);
> > >   void machine_set_cpu_numa_node(MachineState *machine,
> > >                                  const CpuInstanceProperties *props,
> > >                                  Error **errp);
> > > +void smp_parse(MachineState *ms, SMPConfiguration *config, Error **errp);
> > >   /**
> > >    * machine_class_allow_dynamic_sysbus_dev: Add type to list of valid devices
> > > -- 
> > > 2.19.1
> > > 
> > Otherwise
> > 
> > Reviewed-by: Andrew Jones <drjones@redhat.com>
> > 
> Thanks,
> Yanan
> .
> 



^ permalink raw reply	[flat|nested] 58+ messages in thread

* Re: [PATCH for-6.2 v2 11/11] tests/unit: Add a unit test for smp parsing
  2021-07-22  6:15     ` wangyanan (Y)
@ 2021-07-22 13:12       ` Andrew Jones
  2021-07-22 14:18         ` wangyanan (Y)
  0 siblings, 1 reply; 58+ messages in thread
From: Andrew Jones @ 2021-07-22 13:12 UTC (permalink / raw)
  To: wangyanan (Y)
  Cc: Peter Maydell, Thomas Huth, Daniel P. Berrangé,
	Eduardo Habkost, Michael S . Tsirkin, Richard Henderson,
	qemu-devel, Greg Kurz, Halil Pasic, Paolo Bonzini, Igor Mammedov,
	yuzenghui, wanghaibin.wang, David Gibson

On Thu, Jul 22, 2021 at 02:15:18PM +0800, wangyanan (Y) wrote:
> On 2021/7/20 2:57, Andrew Jones wrote:
> > On Mon, Jul 19, 2021 at 11:20:43AM +0800, Yanan Wang wrote:
> > > Add a QEMU unit test for the parsing of given SMP configuration.
> > > Since all the parsing logic is in generic function smp_parse(),
> > > this test passes diffenent SMP configurations to the function
> > > and compare the parsing result with what is expected.
> > > 
> > > In the test, all possible collections of the topology parameters
> > > and the corressponding expected results are listed, including the
> > > valid and invalid ones.
> > > 
> > > The preference of sockets over cores and the preference of cores
> > > over sockets, and the support of multi-dies are also considered.
> > > 
> > > Signed-off-by: Yanan Wang <wangyanan55@huawei.com>
> > > ---
> > >   MAINTAINERS                 |    1 +
> > >   tests/unit/meson.build      |    1 +
> > >   tests/unit/test-smp-parse.c | 1117 +++++++++++++++++++++++++++++++++++
> > >   3 files changed, 1119 insertions(+)
> > >   create mode 100644 tests/unit/test-smp-parse.c
> > > 
> > > diff --git a/MAINTAINERS b/MAINTAINERS
> > > index 70633e3bf4..160dba2e57 100644
> > > --- a/MAINTAINERS
> > > +++ b/MAINTAINERS
> > > @@ -1636,6 +1636,7 @@ F: include/hw/boards.h
> > >   F: include/hw/core/cpu.h
> > >   F: include/hw/cpu/cluster.h
> > >   F: include/sysemu/numa.h
> > > +F: tests/unit/test-smp-parse.c
> > >   T: git https://gitlab.com/ehabkost/qemu.git machine-next
> > >   Xtensa Machines
> > > diff --git a/tests/unit/meson.build b/tests/unit/meson.build
> > > index 3e0504dd21..694a924627 100644
> > > --- a/tests/unit/meson.build
> > > +++ b/tests/unit/meson.build
> > > @@ -44,6 +44,7 @@ tests = {
> > >     'test-uuid': [],
> > >     'ptimer-test': ['ptimer-test-stubs.c', meson.source_root() / 'hw/core/ptimer.c'],
> > >     'test-qapi-util': [],
> > > +  'test-smp-parse': [qom, meson.source_root() / 'hw/core/machine-smp.c'],
> > >   }
> > >   if have_system or have_tools
> > > diff --git a/tests/unit/test-smp-parse.c b/tests/unit/test-smp-parse.c
> > > new file mode 100644
> > > index 0000000000..bc1d324c3d
> > > --- /dev/null
> > > +++ b/tests/unit/test-smp-parse.c
> > > @@ -0,0 +1,1117 @@
> > > +/*
> > > + * SMP parsing unit-tests
> > > + *
> > > + * Copyright (C) 2021, Huawei, Inc.
> > > + *
> > > + * Authors:
> > > + *  Yanan Wang <wangyanan55@huawei.com>
> > > + *
> > > + * This work is licensed under the terms of the GNU LGPL, version 2.1 or later.
> > > + * See the COPYING.LIB file in the top-level directory.
> > > + */
> > > +
> > > +#include "qemu/osdep.h"
> > > +#include "qom/object.h"
> > > +#include "qemu/module.h"
> > > +#include "qapi/error.h"
> > > +
> > > +#include "hw/boards.h"
> > > +
> > > +#define T true
> > > +#define F false
> > > +
> > > +/**
> > > + * SMPTestData:
> > > + * @config - the given SMP configuration for parsing
> > > + * @should_be_valid - whether the given configuration is supposed to be valid
> > > + * @expect - the CPU topology info expected to be parsed out
> > > + */
> > > +typedef struct SMPTestData {
> > > +    SMPConfiguration config;
> > > +    bool should_be_valid;
> > Long way to say 'valid'.
> Indeed..., "valid" should be enough.
> > > +    CpuTopology expect;
> > > +} SMPTestData;
> > > +
> > > +/* the specific machine type info for this test */
> > > +static const TypeInfo smp_machine_info = {
> > > +    .name = TYPE_MACHINE,
> > > +    .parent = TYPE_OBJECT,
> > > +    .class_size = sizeof(MachineClass),
> > > +    .instance_size = sizeof(MachineState),
> > > +};
> > > +
> > > +/*
> > > + * prefer sockets over cores over threads before 6.2.
> > > + * all possible SMP configurations and the corressponding expected outputs
> > corresponding (please run spell check on your commit messages)
> > 
> Ok, I missed the check.
> > > + * are listed for testing, including the valid and invalid ones.
> > > + */
> > > +static struct SMPTestData prefer_sockets[] = {
> > > +    {
> > > +        /* config: no smp configuration provided
> > > +         * expect: cpus=1,sockets=1,dies=1,cores=1,threads=1,maxcpus=1 */
> > > +        .config = (SMPConfiguration) { F, 0, F, 0, F, 0, F, 0, F, 0, F, 0 },
> > SMPConfiguration and CpuTopology have named fields so we could drop the
> > 'expect: ...' comment line and instead do
> > 
> >   {
> >    /* no configuration provided */
> >    .config = { .has_cpus = F, .has_maxcpus = F, ... },
> >    .valid = T,
> >    .expect = { .sockets = 1, .cores = 1, ... },
> >   }, {
> >    ...
> >   }
> > 
> > which may be easier to maintain. OTOH, the concise form this approach has
> > is also nice.
> I tried the structure initialization with explicit name fields in it like
> above,
> actually we are supposed to do in this way so that we don't have to worry
> about the order change of the structure members.
> 
> But this would break the 80-char line limit or introduce more lines for

I wouldn't worry about 80-char (or even 90, which is when checkpatch
switches from a warning to an error) for something like this, but that's
just my opinion. You'd have to get agreement from whomever would decide /
not decide to merge this.

Thanks,
drew

> for each SMP configuration. If this is not a real problem, I also prefer
> above format.
> > I don't think you should need the casts in the assignments
> > though.
> Yes, the casts may be unnecessary, will remove them.
> > 
> > > +        .should_be_valid = true,
> > > +        .expect = (CpuTopology) { 1, 1, 1, 1, 1, 1 },
> > > +    }, {
> > > +        /* config: -smp 8
> > > +         * expect: cpus=8,sockets=8,dies=1,cores=1,threads=1,maxcpus=8 */
> > > +        .config = (SMPConfiguration) { T, 8, F, 0, F, 0, F, 0, F, 0, F, 0 },
> > > +        .should_be_valid = true,
> > > +        .expect = (CpuTopology) { 8, 8, 1, 1, 1, 8 },
> > > +    }, {
> > > +        /* config: -smp sockets=2
> > > +         * expect: cpus=2,sockets=2,dies=1,cores=1,threads=1,maxcpus=2 */
> > > +        .config = (SMPConfiguration) { F, 0, T, 2, F, 0, F, 0, F, 0, F, 0 },
> > > +        .should_be_valid = true,
> > > +        .expect = (CpuTopology) { 2, 2, 1, 1, 1, 2 },
> > > +    }, {
> > > +        /* config: -smp cores=4
> > > +         * expect: cpus=4,sockets=1,dies=1,cores=4,threads=1,maxcpus=4 */
> > > +        .config = (SMPConfiguration) { F, 0, F, 0, F, 0, T, 4, F, 0, F, 0 },
> > > +        .should_be_valid = true,
> > > +        .expect = (CpuTopology) { 4, 1, 1, 4, 1, 4 },
> > > +    }, {
> > > +        /* config: -smp threads=2
> > > +         * expect: cpus=2,sockets=1,dies=1,cores=1,threads=2,maxcpus=2 */
> > > +        .config = (SMPConfiguration) { F, 0, F, 0, F, 0, F, 0, T, 2, F, 0 },
> > > +        .should_be_valid = true,
> > > +        .expect = (CpuTopology) { 2, 1, 1, 1, 2, 2 },
> > > +    }, {
> > > +        /* config: -smp maxcpus=16
> > > +         * expect: cpus=16,sockets=16,dies=1,cores=1,threads=1,maxcpus=16 */
> > > +        .config = (SMPConfiguration) { F, 0, F, 0, F, 0, F, 0, F, 0, T, 16 },
> > > +        .should_be_valid = true,
> > > +        .expect = (CpuTopology) { 16, 16, 1, 1, 1, 16 },
> > > +    }, {
> > > +        /* config: -smp 8,sockets=2
> > > +         * expect: cpus=8,sockets=2,dies=1,cores=4,threads=1,maxcpus=8 */
> > > +        .config = (SMPConfiguration) { T, 8, T, 2, F, 0, F, 0, F, 0, F, 0 },
> > > +        .should_be_valid = true,
> > > +        .expect = (CpuTopology) { 8, 2, 1, 4, 1, 8 },
> > > +    }, {
> > > +        /* config: -smp 8,cores=4
> > > +         * expect: cpus=8,sockets=2,dies=1,cores=4,threads=1,maxcpus=8 */
> > > +        .config = (SMPConfiguration) { T, 8, F, 0, F, 0, T, 4, F, 0, F, 0 },
> > > +        .should_be_valid = true,
> > > +        .expect = (CpuTopology) { 8, 2, 1, 4, 1, 8 },
> > > +    }, {
> > > +        /* config: -smp 8,threads=2
> > > +         * expect: cpus=8,sockets=4,dies=1,cores=1,threads=2,maxcpus=8 */
> > > +        .config = (SMPConfiguration) { T, 8, F, 0, F, 0, F, 0, T, 2, F, 0 },
> > > +        .should_be_valid = true,
> > > +        .expect = (CpuTopology) { 8, 4, 1, 1, 2, 8 },
> > > +    }, {
> > > +        /* config: -smp 8,maxcpus=16
> > > +         * expect: cpus=8,sockets=16,dies=1,cores=1,threads=1,maxcpus=16 */
> > > +        .config = (SMPConfiguration) { T, 8, F, 0, F, 0, F, 0, F, 0, T, 16 },
> > > +        .should_be_valid = true,
> > > +        .expect = (CpuTopology) { 8, 16, 1, 1, 1, 16 },
> > > +    }, {
> > > +        /* config: -smp sockets=2,cores=4
> > > +         * expect: cpus=8,sockets=2,dies=1,cores=4,threads=1,maxcpus=8 */
> > > +        .config = (SMPConfiguration) { F, 0, T, 2, F, 0, T, 4, F, 0, F, 0 },
> > > +        .should_be_valid = true,
> > > +        .expect = (CpuTopology) { 8, 2, 1, 4, 1, 8 },
> > > +    }, {
> > > +        /* config: -smp sockets=2,threads=2
> > > +         * expect: cpus=4,sockets=2,dies=1,cores=1,threads=2,maxcpus=4 */
> > > +        .config = (SMPConfiguration) { F, 0, T, 2, F, 0, F, 0, T, 2, F, 0 },
> > > +        .should_be_valid = true,
> > > +        .expect = (CpuTopology) { 4, 2, 1, 1, 2, 4 },
> > > +    }, {
> > > +        /* config: -smp sockets=2,maxcpus=16
> > > +         * expect: cpus=16,sockets=2,dies=1,cores=8,threads=1,maxcpus=16 */
> > > +        .config = (SMPConfiguration) { F, 0, T, 2, F, 0, F, 0, F, 0, T, 16 },
> > > +        .should_be_valid = true,
> > > +        .expect = (CpuTopology) { 16, 2, 1, 8, 1, 16 },
> > > +    }, {
> > > +        /* config: -smp cores=4,threads=2
> > > +         * expect: cpus=8,sockets=1,dies=1,cores=4,threads=2,maxcpus=8 */
> > > +        .config = (SMPConfiguration) { F, 0, F, 0, F, 0, T, 4, T, 2, F, 0 },
> > > +        .should_be_valid = true,
> > > +        .expect = (CpuTopology) { 8, 1, 1, 4, 2, 8 },
> > > +    }, {
> > > +        /* config: -smp cores=4,maxcpus=16
> > > +         * expect: cpus=16,sockets=4,dies=1,cores=4,threads=1,maxcpus=16 */
> > > +        .config = (SMPConfiguration) { F, 0, F, 0, F, 0, T, 4, F, 0, T, 16 },
> > > +        .should_be_valid = true,
> > > +        .expect = (CpuTopology) { 16, 4, 1, 4, 1, 16 },
> > > +    }, {
> > > +        /* config: -smp threads=2,maxcpus=16
> > > +         * expect: cpus=16,sockets=8,dies=1,cores=1,threads=2,maxcpus=16 */
> > > +        .config = (SMPConfiguration) { F, 0, F, 0, F, 0, F, 0, T, 2, T, 16 },
> > > +        .should_be_valid = true,
> > > +        .expect = (CpuTopology) { 16, 8, 1, 1, 2, 16 },
> > > +    }, {
> > > +        /* config: -smp 8,sockets=2,cores=4
> > > +         * expect: cpus=8,sockets=2,dies=1,cores=4,threads=1,maxcpus=8 */
> > > +        .config = (SMPConfiguration) { T, 8, T, 2, F, 0, T, 4, F, 0, F, 0 },
> > > +        .should_be_valid = true,
> > > +        .expect = (CpuTopology) { 8, 2, 1, 4, 1, 8 },
> > > +    }, {
> > > +        /* config: -smp 8,sockets=2,threads=2
> > > +         * expect: cpus=8,sockets=2,dies=1,cores=2,threads=2,maxcpus=8 */
> > > +        .config = (SMPConfiguration) { T, 8, T, 2, F, 0, F, 0, T, 2, F, 0 },
> > > +        .should_be_valid = true,
> > > +        .expect = (CpuTopology) { 8, 2, 1, 2, 2, 8 },
> > > +    }, {
> > > +        /* config: -smp 8,sockets=2,maxcpus=16
> > > +         * expect: cpus=8,sockets=2,dies=1,cores=8,threads=1,maxcpus=16 */
> > > +        .config = (SMPConfiguration) { T, 8, T, 2, F, 0, F, 0, F, 0, T, 16 },
> > > +        .should_be_valid = true,
> > > +        .expect = (CpuTopology) { 8, 2, 1, 8, 1, 16 },
> > > +    }, {
> > > +        /* config: -smp 8,cores=4,threads=2
> > > +         * expect: cpus=8,sockets=1,dies=1,cores=4,threads=2,maxcpus=8 */
> > > +        .config = (SMPConfiguration) { T, 8, F, 0, F, 0, T, 4, T, 2, F, 0 },
> > > +        .should_be_valid = true,
> > > +        .expect = (CpuTopology) { 8, 1, 1, 4, 2, 8 },
> > > +    }, {
> > > +        /* config: -smp 8,cores=4,maxcpus=16
> > > +         * expect: cpus=8,sockets=4,dies=1,cores=4,threads=1,maxcpus=16 */
> > > +        .config = (SMPConfiguration) { T, 8, F, 0, F, 0, T, 4, F, 0, T, 16 },
> > > +        .should_be_valid = true,
> > > +        .expect = (CpuTopology) { 8, 4, 1, 4, 1, 16 },
> > > +    }, {
> > > +        /* config: -smp 8,threads=2,maxcpus=16
> > > +         * expect: cpus=8,sockets=8,dies=1,cores=1,threads=2,maxcpus=16 */
> > > +        .config = (SMPConfiguration) { T, 8, F, 0, F, 0, F, 0, T, 2, T, 16 },
> > > +        .should_be_valid = true,
> > > +        .expect = (CpuTopology) { 8, 8, 1, 1, 2, 16 },
> > > +    }, {
> > > +        /* config: -smp sockets=2,cores=4,threads=2
> > > +         * expect: cpus=16,sockets=2,dies=1,cores=4,threads=2,maxcpus=16 */
> > > +        .config = (SMPConfiguration) { F, 0, T, 2, F, 0, T, 4, T, 2, F, 0 },
> > > +        .should_be_valid = true,
> > > +        .expect = (CpuTopology) { 16, 2, 1, 4, 2, 16 },
> > > +    }, {
> > > +        /* config: -smp sockets=2,cores=4,maxcpus=16
> > > +         * expect: cpus=16,sockets=2,dies=1,cores=4,threads=2,maxcpus=16 */
> > > +        .config = (SMPConfiguration) { F, 0, T, 2, F, 0, T, 4, F, 0, T, 16 },
> > > +        .should_be_valid = true,
> > > +        .expect = (CpuTopology) { 16, 2, 1, 4, 2, 16 },
> > > +    }, {
> > > +        /* config: -smp sockets=2,threads=2,maxcpus=16
> > > +         * expect: cpus=16,sockets=2,dies=1,cores=4,threads=2,maxcpus=16 */
> > > +        .config = (SMPConfiguration) { F, 0, T, 2, F, 0, F, 0, T, 2, T, 16 },
> > > +        .should_be_valid = true,
> > > +        .expect = (CpuTopology) { 16, 2, 1, 4, 2, 16 },
> > > +    }, {
> > > +        /* config: -smp cores=4,threads=2,maxcpus=16
> > > +         * expect: cpus=16,sockets=2,dies=1,cores=4,threads=2,maxcpus=16 */
> > > +        .config = (SMPConfiguration) { F, 0, F, 0, F, 0, T, 4, T, 2, T, 16 },
> > > +        .should_be_valid = true,
> > > +        .expect = (CpuTopology) { 16, 2, 1, 4, 2, 16 },
> > > +    }, {
> > > +        /* config: -smp 8,sockets=2,cores=4,threads=1
> > > +         * expect: cpus=8,sockets=2,dies=1,cores=4,threads=1,maxcpus=8 */
> > > +        .config = (SMPConfiguration) { T, 8, T, 2, F, 0, T, 4, T, 1, F, 0 },
> > > +        .should_be_valid = true,
> > > +        .expect = (CpuTopology) { 8, 2, 1, 4, 1, 8 },
> > > +    }, {
> > > +        /* config: -smp 8,sockets=2,cores=4,maxcpus=16
> > > +         * expect: cpus=8,sockets=2,dies=1,cores=4,threads=2,maxcpus=16 */
> > > +        .config = (SMPConfiguration) { T, 8, T, 2, F, 0, T, 4, F, 0, T, 16 },
> > > +        .should_be_valid = true,
> > > +        .expect = (CpuTopology) { 8, 2, 1, 4, 2, 16 },
> > > +    }, {
> > > +        /* config: -smp 8,sockets=2,threads=2,maxcpus=16
> > > +         * expect: cpus=8,sockets=2,dies=1,cores=4,threads=2,maxcpus=16 */
> > > +        .config = (SMPConfiguration) { T, 8, T, 2, F, 0, F, 0, T, 2, T, 16 },
> > > +        .should_be_valid = true,
> > > +        .expect = (CpuTopology) { 8, 2, 1, 4, 2, 16 },
> > > +    }, {
> > > +        /* config: -smp 8,cores=4,threads=2,maxcpus=16
> > > +         * expect: cpus=8,sockets=2,dies=1,cores=4,threads=2,maxcpus=16 */
> > > +        .config = (SMPConfiguration) { T, 8, F, 0, F, 0, T, 4, T, 2, T, 16 },
> > > +        .should_be_valid = true,
> > > +        .expect = (CpuTopology) { 8, 2, 1, 4, 2, 16 },
> > > +    }, {
> > > +        /* config: -smp sockets=2,cores=4,threads=2,maxcpus=16
> > > +         * expect: -smp 16,sockets=2,dies=1,cores=4,threads=2,maxcpus=16 */
> > > +        .config = (SMPConfiguration) { F, 0, T, 2, F, 0, T, 4, T, 2, T, 16 },
> > > +        .should_be_valid = true,
> > > +        .expect = (CpuTopology) { 16, 2, 1, 4, 2, 16 },
> > > +    }, {
> > > +        /* config: -smp 8,sockets=2,cores=4,threads=2,maxcpus=16
> > > +         * expect: cpus=8,sockets=2,dies=1,cores=4,threads=2,maxcpus=16 */
> > > +        .config = (SMPConfiguration) { T, 8, T, 2, F, 0, T, 4, T, 2, T, 16 },
> > > +        .should_be_valid = true,
> > > +        .expect = (CpuTopology) { 8, 2, 1, 4, 2, 16 },
> > > +    }, {
> > > +        /* config: -smp 8,sockets=2,dies=1,cores=4,threads=2,maxcpus=16
> > > +         * expect: cpus=8,sockets=2,dies=1,cores=4,threads=2,maxcpus=16 */
> > > +        .config = (SMPConfiguration) { T, 8, T, 2, T, 1, T, 4, T, 2, T, 16 },
> > > +        .should_be_valid = true,
> > > +        .expect = (CpuTopology) { 8, 2, 1, 4, 2, 16 },
> > > +    }, {
> > > +        /* config: -smp 0
> > > +         * expect: error, "anything=0" is not allowed */
> > > +        .config = (SMPConfiguration) { T, 0, F, 0, F, 0, F, 0, F, 0, F, 0 },
> > > +        .should_be_valid = false,
> > > +    }, {
> > > +        /* config: -smp 8,sockets=0
> > > +         * expect: error, "anything=0" is not allowed */
> > > +        .config = (SMPConfiguration) { T, 8, T, 0, F, 0, F, 0, F, 0, F, 0 },
> > > +        .should_be_valid = false,
> > > +    }, {
> > > +        /* config: -smp 8,sockets=2,dies=0
> > > +         * expect: error, "anything=0" is not allowed */
> > > +        .config = (SMPConfiguration) { T, 0, T, 2, T, 0, F, 0, F, 0, F, 0 },
> > > +        .should_be_valid = false,
> > > +    }, {
> > > +        /* config: -smp 8,sockets=2,dies=1,cores=0
> > > +         * expect: error, "anything=0" is not allowed */
> > > +        .config = (SMPConfiguration) { T, 8, T, 2, T, 1, T, 0, F, 0, F, 0 },
> > > +        .should_be_valid = false,
> > > +    }, {
> > > +        /* config: -smp 8,sockets=2,dies=1,cores=4,threads=0
> > > +         * expect: error, "anything=0" is not allowed */
> > > +        .config = (SMPConfiguration) { T, 8, T, 2, T, 1, T, 4, T, 0, F, 0 },
> > > +        .should_be_valid = false,
> > > +    }, {
> > > +        /* config: -smp 8,sockets=2,dies=1,cores=4,threads=2,maxcpus=0
> > > +         * expect: error, "anything=0" is not allowed */
> > > +        .config = (SMPConfiguration) { T, 8, T, 2, T, 1, T, 4, T, 2, T, 0 },
> > > +        .should_be_valid = false,
> > > +    }, {
> > > +        /* config: -smp 8,dies=2
> > > +         * expect: error, multi-dies not supported */
> > > +        .config = (SMPConfiguration) { T, 8, F, 0, T, 2, F, 0, F, 0, F, 0 },
> > > +        .should_be_valid = false,
> > > +    }, {
> > > +        /* config: -smp 8,sockets=2,cores=8
> > > +         * expect: error, sum (16) != max_cpus (8) */
> > > +        .config = (SMPConfiguration) { T, 8, T, 2, F, 0, T, 4, T, 2, F, 0 },
> > > +        .should_be_valid = false,
> > > +    }, {
> > > +        /* config: -smp 8,sockets=2,cores=5,threads=2,maxcpus=16
> > > +         * expect: error, sum (20) != max_cpus (16) */
> > > +        .config = (SMPConfiguration) { F, 0, T, 3, F, 0, T, 5, T, 1, T, 16 },
> > > +        .should_be_valid = false,
> > > +    }, {
> > > +        /* config: -smp 16,maxcpus=12
> > > +         * expect: error, sum (12) < smp_cpus (16) */
> > > +        .config = (SMPConfiguration) { T, 16, F, 0, F, 0, F, 0, F, 0, T, 12 },
> > > +        .should_be_valid = false,
> > > +    },
> > > +};
> > > +
> > > +static struct SMPTestData prefer_sockets_support_dies[] = {
> > > +    {
> > > +        /* config: -smp dies=2
> > > +         * expect: cpus=2,sockets=1,dies=2,cores=1,threads=1,maxcpus=2 */
> > > +        .config = (SMPConfiguration) { F, 0, F, 0, T, 2, F, 0, F, 0, F, 0 },
> > > +        .should_be_valid = true,
> > > +        .expect = (CpuTopology) { 2, 1, 2, 1, 1, 2 },
> > > +    }, {
> > > +        /* config: -smp 16,dies=2
> > > +         * expect: cpus=16,sockets=8,dies=2,cores=1,threads=1,maxcpus=16 */
> > > +        .config = (SMPConfiguration) { T, 16, F, 0, T, 2, F, 0, F, 0, F, 0 },
> > > +        .should_be_valid = true,
> > > +        .expect = (CpuTopology) { 16, 8, 2, 1, 1, 16 },
> > > +    }, {
> > > +        /* config: -smp sockets=2,dies=2
> > > +         * expect: cpus=4,sockets=2,dies=2,cores=1,threads=1,maxcpus=4 */
> > > +        .config = (SMPConfiguration) { F, 0, T, 2, T, 2, F, 0, F, 0, F, 0 },
> > > +        .should_be_valid = true,
> > > +        .expect = (CpuTopology) { 4, 2, 2, 1, 1, 4 },
> > > +    }, {
> > > +        /* config: -smp dies=2,cores=4
> > > +         * expect: cpus=8,sockets=1,dies=2,cores=4,threads=1,maxcpus=8 */
> > > +        .config = (SMPConfiguration) { F, 0, F, 0, T, 2, T, 4, F, 0, F, 0 },
> > > +        .should_be_valid = true,
> > > +        .expect = (CpuTopology) { 8, 1, 2, 4, 1, 8 },
> > > +    }, {
> > > +        /* config: -smp dies=2,threads=2
> > > +         * expect: cpus=4,sockets=1,dies=2,cores=1,threads=2,maxcpus=4 */
> > > +        .config = (SMPConfiguration) { F, 0, F, 0, T, 2, F, 0, T, 2, F, 0 },
> > > +        .should_be_valid = true,
> > > +        .expect = (CpuTopology) { 4, 1, 2, 1, 2, 4 },
> > > +    }, {
> > > +        /* config: -smp dies=2,maxcpus=32
> > > +         * expect: cpus=32,sockets=16,dies=2,cores=1,threads=1,maxcpus=32 */
> > > +        .config = (SMPConfiguration) { F, 0, F, 0, T, 2, F, 0, F, 0, T, 32 },
> > > +        .should_be_valid = true,
> > > +        .expect = (CpuTopology) { 32, 16, 2, 1, 1, 32 },
> > > +    }, {
> > > +        /* config: -smp 16,sockets=2,dies=2
> > > +         * expect: cpus=16,sockets=2,dies=2,cores=4,threads=1,maxcpus=16 */
> > > +        .config = (SMPConfiguration) { T, 16, T, 2, T, 2, F, 0, F, 0, F, 0 },
> > > +        .should_be_valid = true,
> > > +        .expect = (CpuTopology) { 16, 2, 2, 4, 1, 16 },
> > > +    }, {
> > > +        /* config: -smp 16,dies=2,cores=4
> > > +         * expect: cpus=16,sockets=2,dies=2,cores=4,threads=1,maxcpus=16 */
> > > +        .config = (SMPConfiguration) { T, 16, F, 0, T, 2, T, 4, F, 0, F, 0 },
> > > +        .should_be_valid = true,
> > > +        .expect = (CpuTopology) { 16, 2, 2, 4, 1, 16 },
> > > +    }, {
> > > +        /* config: -smp 16,dies=2,threads=2
> > > +         * expect: cpus=16,sockets=4,dies=2,cores=1,threads=2,maxcpus=16 */
> > > +        .config = (SMPConfiguration) { T, 16, F, 0, T, 2, F, 0, T, 2, F, 0 },
> > > +        .should_be_valid = true,
> > > +        .expect = (CpuTopology) { 16, 4, 2, 1, 2, 16 },
> > > +    }, {
> > > +        /* config: -smp 16,dies=2,maxcpus=32
> > > +         * expect: cpus=16,sockets=16,dies=2,cores=1,threads=1,maxcpus=32 */
> > > +        .config = (SMPConfiguration) { T, 16, F, 0, T, 2, F, 0, F, 0, T, 32 },
> > > +        .should_be_valid = true,
> > > +        .expect = (CpuTopology) { 16, 16, 2, 1, 1, 32 },
> > > +    }, {
> > > +        /* config: -smp sockets=2,dies=2,cores=4
> > > +         * expect: cpus=16,sockets=2,dies=2,cores=4,threads=1,maxcpus=16 */
> > > +        .config = (SMPConfiguration) { F, 0, T, 2, T, 2, T, 4, F, 0, F, 0 },
> > > +        .should_be_valid = true,
> > > +        .expect = (CpuTopology) { 16, 2, 2, 4, 1, 16 },
> > > +    }, {
> > > +        /* config: -smp sockets=2,dies=2,threads=2
> > > +         * expect: cpus=8,sockets=2,dies=2,cores=1,threads=2,maxcpus=8 */
> > > +        .config = (SMPConfiguration) { F, 0, T, 2, T, 2, F, 0, T, 2, F, 0 },
> > > +        .should_be_valid = true,
> > > +        .expect = (CpuTopology) { 8, 2, 2, 1, 2, 8 },
> > > +    }, {
> > > +        /* config: -smp sockets=2,dies=2,maxcpus=32
> > > +         * expect: cpus=32,sockets=2,dies=2,cores=8,threads=1,maxcpus=32 */
> > > +        .config = (SMPConfiguration) { F, 0, T, 2, T, 2, F, 0, F, 0, T, 32 },
> > > +        .should_be_valid = true,
> > > +        .expect = (CpuTopology) { 32, 2, 2, 8, 1, 32 },
> > > +    }, {
> > > +        /* config: -smp dies=2,cores=4,threads=2
> > > +         * expect: cpus=16,sockets=1,dies=2,cores=4,threads=2,maxcpus=16 */
> > > +        .config = (SMPConfiguration) { F, 0, F, 0, T, 2, T, 4, T, 2, F, 0 },
> > > +        .should_be_valid = true,
> > > +        .expect = (CpuTopology) { 16, 1, 2, 4, 2, 16 },
> > > +    }, {
> > > +        /* config: -smp dies=2,cores=4,maxcpus=32
> > > +         * expect: cpus=32,sockets=4,dies=2,cores=4,threads=1,maxcpus=32 */
> > > +        .config = (SMPConfiguration) { F, 0, F, 0, T, 2, T, 4, F, 0, T, 32 },
> > > +        .should_be_valid = true,
> > > +        .expect = (CpuTopology) { 32, 4, 2, 4, 1, 32 },
> > > +    }, {
> > > +        /* config: -smp dies=2,threads=2,maxcpus=32
> > > +         * expect: cpus=32,sockets=8,dies=2,cores=1,threads=2,maxcpus=32 */
> > > +        .config = (SMPConfiguration) { F, 0, F, 0, T, 2, F, 0, T, 2, T, 32 },
> > > +        .should_be_valid = true,
> > > +        .expect = (CpuTopology) { 32, 8, 2, 1, 2, 32 },
> > > +    }, {
> > > +        /* config: -smp 16,sockets=2,dies=2,cores=4
> > > +         * expect: cpus=16,sockets=2,dies=2,cores=4,threads=1,maxcpus=16 */
> > > +        .config = (SMPConfiguration) { T, 16, T, 2, T, 2, T, 4, F, 0, F, 0 },
> > > +        .should_be_valid = true,
> > > +        .expect = (CpuTopology) { 16, 2, 2, 4, 1, 16 },
> > > +    }, {
> > > +        /* config: -smp 16,sockets=2,dies=2,threads=2
> > > +         * expect: cpus=16,sockets=2,dies=2,cores=2,threads=2,maxcpus=16 */
> > > +        .config = (SMPConfiguration) { T, 16, T, 2, T, 2, F, 0, T, 2, F, 0 },
> > > +        .should_be_valid = true,
> > > +        .expect = (CpuTopology) { 16, 2, 2, 2, 2, 16 },
> > > +    }, {
> > > +        /* config: -smp 16,sockets=2,dies=2,maxcpus=32
> > > +         * expect: cpus=16,sockets=2,dies=2,cores=8,threads=1,maxcpus=32 */
> > > +        .config = (SMPConfiguration) { T, 16, T, 2, T, 2, F, 0, F, 0, T, 32 },
> > > +        .should_be_valid = true,
> > > +        .expect = (CpuTopology) { 16, 2, 2, 8, 1, 32 },
> > > +    }, {
> > > +        /* config: -smp 16,dies=2,cores=4,threads=2
> > > +         * expect: cpus=16,sockets=1,dies=2,cores=4,threads=2,maxcpus=16 */
> > > +        .config = (SMPConfiguration) { T, 16, F, 0, T, 2, T, 4, T, 2, F, 0 },
> > > +        .should_be_valid = true,
> > > +        .expect = (CpuTopology) { 16, 1, 2, 4, 2, 16 },
> > > +    }, {
> > > +        /* config: -smp 16,dies=2,cores=4,maxcpus=32
> > > +         * expect: cpus=16,sockets=4,dies=2,cores=4,threads=1,maxcpus=32 */
> > > +        .config = (SMPConfiguration) { T, 16, F, 0, T, 2, T, 4, F, 0, T, 32 },
> > > +        .should_be_valid = true,
> > > +        .expect = (CpuTopology) { 16, 4, 2, 4, 1, 32 },
> > > +    }, {
> > > +        /* config: -smp 16,dies=2,threads=2,maxcpus=32
> > > +         * expect: cpus=16,sockets=8,dies=2,cores=1,threads=2,maxcpus=32 */
> > > +        .config = (SMPConfiguration) { T, 16, F, 0, T, 2, F, 0, T, 2, T, 32 },
> > > +        .should_be_valid = true,
> > > +        .expect = (CpuTopology) { 16, 8, 2, 1, 2, 32 },
> > > +    }, {
> > > +        /* config: -smp sockets=2,dies=2,cores=4,threads=2
> > > +         * expect: cpus=32,sockets=2,dies=2,cores=4,threads=2,maxcpus=32 */
> > > +        .config = (SMPConfiguration) { F, 0, T, 2, T, 2, T, 4, T, 2, F, 0 },
> > > +        .should_be_valid = true,
> > > +        .expect = (CpuTopology) { 32, 2, 2, 4, 2, 32 },
> > > +    }, {
> > > +        /* config: -smp sockets=2,dies=2,cores=4,maxcpus=32
> > > +         * expect: cpus=32,sockets=2,dies=2,cores=4,threads=2,maxcpus=32 */
> > > +        .config = (SMPConfiguration) { F, 0, T, 2, T, 2, T, 4, F, 0, T, 32 },
> > > +        .should_be_valid = true,
> > > +        .expect = (CpuTopology) { 32, 2, 2, 4, 2, 32 },
> > > +    }, {
> > > +        /* config: -smp sockets=2,dies=2,threads=2,maxcpus=32
> > > +         * expect: cpus=32,sockets=2,dies=2,cores=4,threads=2,maxcpus=32 */
> > > +        .config = (SMPConfiguration) { F, 0, T, 2, T, 2, F, 0, T, 2, T, 32 },
> > > +        .should_be_valid = true,
> > > +        .expect = (CpuTopology) { 32, 2, 2, 4, 2, 32 },
> > > +    }, {
> > > +        /* config: -smp dies=2,cores=4,threads=2,maxcpus=32
> > > +         * expect: cpus=32,sockets=2,dies=2,cores=4,threads=2,maxcpus=32 */
> > > +        .config = (SMPConfiguration) { F, 0, F, 0, T, 2, T, 4, T, 2, T, 32 },
> > > +        .should_be_valid = true,
> > > +        .expect = (CpuTopology) { 32, 2, 2, 4, 2, 32 },
> > > +    }, {
> > > +        /* config: -smp 16,sockets=2,dies=2,cores=4,threads=1
> > > +         * expect: cpus=16,sockets=2,dies=2,cores=4,threads=1,maxcpus=16 */
> > > +        .config = (SMPConfiguration) { T, 16, T, 2, T, 2, T, 4, T, 1, F, 0 },
> > > +        .should_be_valid = true,
> > > +        .expect = (CpuTopology) { 16, 2, 2, 4, 1, 16 },
> > > +    }, {
> > > +        /* config: -smp 16,sockets=2,dies=2,cores=4,maxcpus=32
> > > +         * expect: cpus=16,sockets=2,dies=2,cores=4,threads=2,maxcpus=32 */
> > > +        .config = (SMPConfiguration) { T, 16, T, 2, T, 2, T, 4, F, 0, T, 32 },
> > > +        .should_be_valid = true,
> > > +        .expect = (CpuTopology) { 16, 2, 2, 4, 2, 32 },
> > > +    }, {
> > > +        /* config: -smp 16,sockets=2,dies=2,threads=2,maxcpus=32
> > > +         * expect: cpus=16,sockets=2,dies=2,cores=4,threads=2,maxcpus=32 */
> > > +        .config = (SMPConfiguration) { T, 16, T, 2, T, 2, F, 0, T, 2, T, 32 },
> > > +        .should_be_valid = true,
> > > +        .expect = (CpuTopology) { 16, 2, 2, 4, 2, 32 },
> > > +    }, {
> > > +        /* config: -smp 16,dies=2,cores=4,threads=2,maxcpus=32
> > > +         * expect: cpus=16,sockets=2,dies=2,cores=4,threads=2,maxcpus=32 */
> > > +        .config = (SMPConfiguration) { T, 16, F, 0, T, 2, T, 4, T, 2, T, 32 },
> > > +        .should_be_valid = true,
> > > +        .expect = (CpuTopology) { 16, 2, 2, 4, 2, 32 },
> > > +    }, {
> > > +        /* config: -smp sockets=2,dies=2,cores=4,threads=2,maxcpus=32
> > > +         * expect: -smp 32,sockets=2,dies=2,cores=4,threads=2,maxcpus=32 */
> > > +        .config = (SMPConfiguration) { F, 0, T, 2, T, 2, T, 4, T, 2, T, 32 },
> > > +        .should_be_valid = true,
> > > +        .expect = (CpuTopology) { 32, 2, 2, 4, 2, 32 },
> > > +    }, {
> > > +        /* config: -smp 16,sockets=2,dies=2,cores=4,threads=2,maxcpus=32
> > > +         * expect: cpus=16,sockets=2,dies=2,cores=4,threads=2,maxcpus=32 */
> > > +        .config = (SMPConfiguration) { T, 16, T, 2, T, 2, T, 4, T, 2, T, 32 },
> > > +        .should_be_valid = true,
> > > +        .expect = (CpuTopology) { 16, 2, 2, 4, 2, 32 },
> > > +    },
> > > +};
> > > +
> > > +/*
> > > + * prefer cores over sockets over threads since 6.2.
> > > + * all possible SMP configurations and the corressponding expected outputs
> > > + * are listed for testing, including the valid and invalid ones.
> > > + */
> > > +static struct SMPTestData prefer_cores[] = {
> > > +    {
> > > +        /* config: no smp configuration
> > > +         * expect: cpus=1,sockets=1,dies=1,cores=1,threads=1,maxcpus=1 */
> > > +        .config = (SMPConfiguration) { F, 0, F, 0, F, 0, F, 0, F, 0, F, 0 },
> > > +        .should_be_valid = true,
> > > +        .expect = (CpuTopology) { 1, 1, 1, 1, 1, 1 },
> > > +    }, {
> > > +        /* config: -smp 8
> > > +         * expect: cpus=8,sockets=1,dies=1,cores=8,threads=1,maxcpus=8 */
> > > +        .config = (SMPConfiguration) { T, 8, F, 0, F, 0, F, 0, F, 0, F, 0 },
> > > +        .should_be_valid = true,
> > > +        .expect = (CpuTopology) { 8, 1, 1, 8, 1, 8 },
> > > +    }, {
> > > +        /* config: -smp sockets=2
> > > +         * expect: cpus=2,sockets=2,dies=1,cores=1,threads=1,maxcpus=2 */
> > > +        .config = (SMPConfiguration) { F, 0, T, 2, F, 0, F, 0, F, 0, F, 0 },
> > > +        .should_be_valid = true,
> > > +        .expect = (CpuTopology) { 2, 2, 1, 1, 1, 2 },
> > > +    }, {
> > > +        /* config: -smp cores=4
> > > +         * expect: cpus=4,sockets=1,dies=1,cores=4,threads=1,maxcpus=4 */
> > > +        .config = (SMPConfiguration) { F, 0, F, 0, F, 0, T, 4, F, 0, F, 0 },
> > > +        .should_be_valid = true,
> > > +        .expect = (CpuTopology) { 4, 1, 1, 4, 1, 4 },
> > > +    }, {
> > > +        /* config: -smp threads=2
> > > +         * expect: cpus=2,sockets=1,dies=1,cores=1,threads=2,maxcpus=2 */
> > > +        .config = (SMPConfiguration) { F, 0, F, 0, F, 0, F, 0, T, 2, F, 0 },
> > > +        .should_be_valid = true,
> > > +        .expect = (CpuTopology) { 2, 1, 1, 1, 2, 2 },
> > > +    }, {
> > > +        /* config: -smp maxcpus=16
> > > +         * expect: cpus=16,sockets=1,dies=1,cores=16,threads=1,maxcpus=16 */
> > > +        .config = (SMPConfiguration) { F, 0, F, 0, F, 0, F, 0, F, 0, T, 16 },
> > > +        .should_be_valid = true,
> > > +        .expect = (CpuTopology) { 16, 1, 1, 16, 1, 16 },
> > > +    }, {
> > > +        /* config: -smp 8,sockets=2
> > > +         * expect: cpus=8,sockets=2,dies=1,cores=4,threads=1,maxcpus=8 */
> > > +        .config = (SMPConfiguration) { T, 8, T, 2, F, 0, F, 0, F, 0, F, 0 },
> > > +        .should_be_valid = true,
> > > +        .expect = (CpuTopology) { 8, 2, 1, 4, 1, 8 },
> > > +    }, {
> > > +        /* config: -smp 8,cores=4
> > > +         * expect: cpus=8,sockets=2,dies=1,cores=4,threads=1,maxcpus=8 */
> > > +        .config = (SMPConfiguration) { T, 8, F, 0, F, 0, T, 4, F, 0, F, 0 },
> > > +        .should_be_valid = true,
> > > +        .expect = (CpuTopology) { 8, 2, 1, 4, 1, 8 },
> > > +    }, {
> > > +        /* config: -smp 8,threads=2
> > > +         * expect: cpus=8,sockets=1,dies=1,cores=4,threads=2,maxcpus=8 */
> > > +        .config = (SMPConfiguration) { T, 8, F, 0, F, 0, F, 0, T, 2, F, 0 },
> > > +        .should_be_valid = true,
> > > +        .expect = (CpuTopology) { 8, 1, 1, 4, 2, 8 },
> > > +    }, {
> > > +        /* config: -smp 8,maxcpus=16
> > > +         * expect: cpus=8,sockets=1,dies=1,cores=16,threads=1,maxcpus=16 */
> > > +        .config = (SMPConfiguration) { T, 8, F, 0, F, 0, F, 0, F, 0, T, 16 },
> > > +        .should_be_valid = true,
> > > +        .expect = (CpuTopology) { 8, 1, 1, 16, 1, 16 },
> > > +    }, {
> > > +        /* config: -smp sockets=2,cores=4
> > > +         * expect: cpus=8,sockets=2,dies=1,cores=4,threads=1,maxcpus=8 */
> > > +        .config = (SMPConfiguration) { F, 0, T, 2, F, 0, T, 4, F, 0, F, 0 },
> > > +        .should_be_valid = true,
> > > +        .expect = (CpuTopology) { 8, 2, 1, 4, 1, 8 },
> > > +    }, {
> > > +        /* config: -smp sockets=2,threads=2
> > > +         * expect: cpus=4,sockets=2,dies=1,cores=1,threads=2,maxcpus=4 */
> > > +        .config = (SMPConfiguration) { F, 0, T, 2, F, 0, F, 0, T, 2, F, 0 },
> > > +        .should_be_valid = true,
> > > +        .expect = (CpuTopology) { 4, 2, 1, 1, 2, 4 },
> > > +    }, {
> > > +        /* config: -smp sockets=2,maxcpus=16
> > > +         * expect: cpus=16,sockets=2,dies=1,cores=8,threads=1,maxcpus=16 */
> > > +        .config = (SMPConfiguration) { F, 0, T, 2, F, 0, F, 0, F, 0, T, 16 },
> > > +        .should_be_valid = true,
> > > +        .expect = (CpuTopology) { 16, 2, 1, 8, 1, 16 },
> > > +    }, {
> > > +        /* config: -smp cores=4,threads=2
> > > +         * expect: cpus=8,sockets=1,dies=1,cores=4,threads=2,maxcpus=8 */
> > > +        .config = (SMPConfiguration) { F, 0, F, 0, F, 0, T, 4, T, 2, F, 0 },
> > > +        .should_be_valid = true,
> > > +        .expect = (CpuTopology) { 8, 1, 1, 4, 2, 8 },
> > > +    }, {
> > > +        /* config: -smp cores=4,maxcpus=16
> > > +         * expect: cpus=16,sockets=4,dies=1,cores=4,threads=1,maxcpus=16 */
> > > +        .config = (SMPConfiguration) { F, 0, F, 0, F, 0, T, 4, F, 0, T, 16 },
> > > +        .should_be_valid = true,
> > > +        .expect = (CpuTopology) { 16, 4, 1, 4, 1, 16 },
> > > +    }, {
> > > +        /* config: -smp threads=2,maxcpus=16
> > > +         * expect: cpus=16,sockets=1,dies=1,cores=8,threads=2,maxcpus=16 */
> > > +        .config = (SMPConfiguration) { F, 0, F, 0, F, 0, F, 0, T, 2, T, 16 },
> > > +        .should_be_valid = true,
> > > +        .expect = (CpuTopology) { 16, 1, 1, 8, 2, 16 },
> > > +    }, {
> > > +        /* config: -smp 8,sockets=2,cores=4
> > > +         * expect: cpus=8,sockets=2,dies=1,cores=4,threads=1,maxcpus=8 */
> > > +        .config = (SMPConfiguration) { T, 8, T, 2, F, 0, T, 4, F, 0, F, 0 },
> > > +        .should_be_valid = true,
> > > +        .expect = (CpuTopology) { 8, 2, 1, 4, 1, 8 },
> > > +    }, {
> > > +        /* config: -smp 8,sockets=2,threads=2
> > > +         * expect: cpus=8,sockets=2,dies=1,cores=2,threads=2,maxcpus=8 */
> > > +        .config = (SMPConfiguration) { T, 8, T, 2, F, 0, F, 0, T, 2, F, 0 },
> > > +        .should_be_valid = true,
> > > +        .expect = (CpuTopology) { 8, 2, 1, 2, 2, 8 },
> > > +    }, {
> > > +        /* config: -smp 8,sockets=2,maxcpus=16
> > > +         * expect: cpus=8,sockets=2,dies=1,cores=8,threads=1,maxcpus=16 */
> > > +        .config = (SMPConfiguration) { T, 8, T, 2, F, 0, F, 0, F, 0, T, 16 },
> > > +        .should_be_valid = true,
> > > +        .expect = (CpuTopology) { 8, 2, 1, 8, 1, 16 },
> > > +    }, {
> > > +        /* config: -smp 8,cores=4,threads=2
> > > +         * expect: cpus=8,sockets=1,dies=1,cores=4,threads=2,maxcpus=8 */
> > > +        .config = (SMPConfiguration) { T, 8, F, 0, F, 0, T, 4, T, 2, F, 0 },
> > > +        .should_be_valid = true,
> > > +        .expect = (CpuTopology) { 8, 1, 1, 4, 2, 8 },
> > > +    }, {
> > > +        /* config: -smp 8,cores=4,maxcpus=16
> > > +         * expect: cpus=8,sockets=4,dies=1,cores=4,threads=1,maxcpus=16 */
> > > +        .config = (SMPConfiguration) { T, 8, F, 0, F, 0, T, 4, F, 0, T, 16 },
> > > +        .should_be_valid = true,
> > > +        .expect = (CpuTopology) { 8, 4, 1, 4, 1, 16 },
> > > +    }, {
> > > +        /* config: -smp 8,threads=2,maxcpus=16
> > > +         * expect: cpus=8,sockets=1,dies=1,cores=8,threads=2,maxcpus=16 */
> > > +        .config = (SMPConfiguration) { T, 8, F, 0, F, 0, F, 0, T, 2, T, 16 },
> > > +        .should_be_valid = true,
> > > +        .expect = (CpuTopology) { 8, 1, 1, 8, 2, 16 },
> > > +    }, {
> > > +        /* config: -smp sockets=2,cores=4,threads=2
> > > +         * expect: cpus=16,sockets=2,dies=1,cores=4,threads=2,maxcpus=16 */
> > > +        .config = (SMPConfiguration) { F, 0, T, 2, F, 0, T, 4, T, 2, F, 0 },
> > > +        .should_be_valid = true,
> > > +        .expect = (CpuTopology) { 16, 2, 1, 4, 2, 16 },
> > > +    }, {
> > > +        /* config: -smp sockets=2,cores=4,maxcpus=16
> > > +         * expect: cpus=16,sockets=2,dies=1,cores=4,threads=2,maxcpus=16 */
> > > +        .config = (SMPConfiguration) { F, 0, T, 2, F, 0, T, 4, F, 0, T, 16 },
> > > +        .should_be_valid = true,
> > > +        .expect = (CpuTopology) { 16, 2, 1, 4, 2, 16 },
> > > +    }, {
> > > +        /* config: -smp sockets=2,threads=2,maxcpus=16
> > > +         * expect: cpus=16,sockets=2,dies=1,cores=4,threads=2,maxcpus=16 */
> > > +        .config = (SMPConfiguration) { F, 0, T, 2, F, 0, F, 0, T, 2, T, 16 },
> > > +        .should_be_valid = true,
> > > +        .expect = (CpuTopology) { 16, 2, 1, 4, 2, 16 },
> > > +    }, {
> > > +        /* config: -smp cores=4,threads=2,maxcpus=16
> > > +         * expect: cpus=16,sockets=2,dies=1,cores=4,threads=2,maxcpus=16 */
> > > +        .config = (SMPConfiguration) { F, 0, F, 0, F, 0, T, 4, T, 2, T, 16 },
> > > +        .should_be_valid = true,
> > > +        .expect = (CpuTopology) { 16, 2, 1, 4, 2, 16 },
> > > +    }, {
> > > +        /* config: -smp 8,sockets=2,cores=4,threads=1
> > > +         * expect: cpus=8,sockets=2,dies=1,cores=4,threads=1,maxcpus=8 */
> > > +        .config = (SMPConfiguration) { T, 8, T, 2, F, 0, T, 4, T, 1, F, 0 },
> > > +        .should_be_valid = true,
> > > +        .expect = (CpuTopology) { 8, 2, 1, 4, 1, 8 },
> > > +    }, {
> > > +        /* config: -smp 8,sockets=2,cores=4,maxcpus=16
> > > +         * expect: cpus=8,sockets=2,dies=1,cores=4,threads=2,maxcpus=16 */
> > > +        .config = (SMPConfiguration) { T, 8, T, 2, F, 0, T, 4, F, 0, T, 16 },
> > > +        .should_be_valid = true,
> > > +        .expect = (CpuTopology) { 8, 2, 1, 4, 2, 16 },
> > > +    }, {
> > > +        /* config: -smp 8,sockets=2,threads=2,maxcpus=16
> > > +         * expect: cpus=8,sockets=2,dies=1,cores=4,threads=2,maxcpus=16 */
> > > +        .config = (SMPConfiguration) { T, 8, T, 2, F, 0, F, 0, T, 2, T, 16 },
> > > +        .should_be_valid = true,
> > > +        .expect = (CpuTopology) { 8, 2, 1, 4, 2, 16 },
> > > +    }, {
> > > +        /* config: -smp 8,cores=4,threads=2,maxcpus=16
> > > +         * expect: cpus=8,sockets=2,dies=1,cores=4,threads=2,maxcpus=16 */
> > > +        .config = (SMPConfiguration) { T, 8, F, 0, F, 0, T, 4, T, 2, T, 16 },
> > > +        .should_be_valid = true,
> > > +        .expect = (CpuTopology) { 8, 2, 1, 4, 2, 16 },
> > > +    }, {
> > > +        /* config: -smp sockets=2,cores=4,threads=2,maxcpus=16
> > > +         * expect: -smp 16,sockets=2,dies=1,cores=4,threads=2,maxcpus=16 */
> > > +        .config = (SMPConfiguration) { F, 0, T, 2, F, 0, T, 4, T, 2, T, 16 },
> > > +        .should_be_valid = true,
> > > +        .expect = (CpuTopology) { 16, 2, 1, 4, 2, 16 },
> > > +    }, {
> > > +        /* config: -smp 8,sockets=2,cores=4,threads=2,maxcpus=16
> > > +         * expect: cpus=8,sockets=2,dies=1,cores=4,threads=2,maxcpus=16 */
> > > +        .config = (SMPConfiguration) { T, 8, T, 2, F, 0, T, 4, T, 2, T, 16 },
> > > +        .should_be_valid = true,
> > > +        .expect = (CpuTopology) { 8, 2, 1, 4, 2, 16 },
> > > +    }, {
> > > +        /* config: -smp 8,sockets=2,dies=1,cores=4,threads=2,maxcpus=16
> > > +         * expect: cpus=8,sockets=2,dies=1,cores=4,threads=2,maxcpus=16 */
> > > +        .config = (SMPConfiguration) { T, 8, T, 2, T, 1, T, 4, T, 2, T, 16 },
> > > +        .should_be_valid = true,
> > > +        .expect = (CpuTopology) { 8, 2, 1, 4, 2, 16 },
> > > +    }, {
> > > +        /* config: -smp 0
> > > +         * expect: error, "anything=0" is not allowed */
> > > +        .config = (SMPConfiguration) { T, 0, F, 0, F, 0, F, 0, F, 0, F, 0 },
> > > +        .should_be_valid = false,
> > > +    }, {
> > > +        /* config: -smp 8,sockets=0
> > > +         * expect: error, "anything=0" is not allowed */
> > > +        .config = (SMPConfiguration) { T, 8, T, 0, F, 0, F, 0, F, 0, F, 0 },
> > > +        .should_be_valid = false,
> > > +    }, {
> > > +        /* config: -smp 8,sockets=2,dies=0
> > > +         * expect: error, "anything=0" is not allowed */
> > > +        .config = (SMPConfiguration) { T, 0, T, 2, T, 0, F, 0, F, 0, F, 0 },
> > > +        .should_be_valid = false,
> > > +    }, {
> > > +        /* config: -smp 8,sockets=2,dies=1,cores=0
> > > +         * expect: error, "anything=0" is not allowed */
> > > +        .config = (SMPConfiguration) { T, 8, T, 2, T, 1, T, 0, F, 0, F, 0 },
> > > +        .should_be_valid = false,
> > > +    }, {
> > > +        /* config: -smp 8,sockets=2,dies=1,cores=4,threads=0
> > > +         * expect: error, "anything=0" is not allowed */
> > > +        .config = (SMPConfiguration) { T, 8, T, 2, T, 1, T, 4, T, 0, F, 0 },
> > > +        .should_be_valid = false,
> > > +    }, {
> > > +        /* config: -smp 8,sockets=2,dies=1,cores=4,threads=2,maxcpus=0
> > > +         * expect: error, "anything=0" is not allowed */
> > > +        .config = (SMPConfiguration) { T, 8, T, 2, T, 1, T, 4, T, 2, T, 0 },
> > > +        .should_be_valid = false,
> > > +    }, {
> > > +        /* config: -smp 8,dies=2
> > > +         * expect: error, multi-dies not supported */
> > > +        .config = (SMPConfiguration) { T, 8, F, 0, T, 2, F, 0, F, 0, F, 0 },
> > > +        .should_be_valid = false,
> > > +    }, {
> > > +        /* config: -smp 8,sockets=2,cores=8
> > > +         * expect: error, sum (16) != max_cpus (8) */
> > > +        .config = (SMPConfiguration) { T, 8, T, 2, F, 0, T, 4, T, 2, F, 0 },
> > > +        .should_be_valid = false,
> > > +    }, {
> > > +        /* config: -smp 8,sockets=2,cores=5,threads=2,maxcpus=16
> > > +         * expect: error, sum (20) != max_cpus (16) */
> > > +        .config = (SMPConfiguration) { F, 0, T, 3, F, 0, T, 5, T, 1, T, 16 },
> > > +        .should_be_valid = false,
> > > +    }, {
> > > +        /* config: -smp 16,maxcpus=12
> > > +         * expect: error, sum (12) < smp_cpus (16) */
> > > +        .config = (SMPConfiguration) { T, 16, F, 0, F, 0, F, 0, F, 0, T, 12 },
> > > +        .should_be_valid = false,
> > > +    },
> > > +};
> > > +
> > > +static struct SMPTestData prefer_cores_support_dies[] = {
> > > +    {
> > > +        /* config: -smp dies=2
> > > +         * expect: cpus=2,sockets=1,dies=2,cores=1,threads=1,maxcpus=2 */
> > > +        .config = (SMPConfiguration) { F, 0, F, 0, T, 2, F, 0, F, 0, F, 0 },
> > > +        .should_be_valid = true,
> > > +        .expect = (CpuTopology) { 2, 1, 2, 1, 1, 2 },
> > > +    }, {
> > > +        /* config: -smp 16,dies=2
> > > +         * expect: cpus=16,sockets=1,dies=2,cores=8,threads=1,maxcpus=16 */
> > > +        .config = (SMPConfiguration) { T, 16, F, 0, T, 2, F, 0, F, 0, F, 0 },
> > > +        .should_be_valid = true,
> > > +        .expect = (CpuTopology) { 16, 1, 2, 8, 1, 16 },
> > > +    }, {
> > > +        /* config: -smp sockets=2,dies=2
> > > +         * expect: cpus=4,sockets=2,dies=2,cores=1,threads=1,maxcpus=4 */
> > > +        .config = (SMPConfiguration) { F, 0, T, 2, T, 2, F, 0, F, 0, F, 0 },
> > > +        .should_be_valid = true,
> > > +        .expect = (CpuTopology) { 4, 2, 2, 1, 1, 4 },
> > > +    }, {
> > > +        /* config: -smp dies=2,cores=4
> > > +         * expect: cpus=8,sockets=1,dies=2,cores=4,threads=1,maxcpus=8 */
> > > +        .config = (SMPConfiguration) { F, 0, F, 0, T, 2, T, 4, F, 0, F, 0 },
> > > +        .should_be_valid = true,
> > > +        .expect = (CpuTopology) { 8, 1, 2, 4, 1, 8 },
> > > +    }, {
> > > +        /* config: -smp dies=2,threads=2
> > > +         * expect: cpus=4,sockets=1,dies=2,cores=1,threads=2,maxcpus=4 */
> > > +        .config = (SMPConfiguration) { F, 0, F, 0, T, 2, F, 0, T, 2, F, 0 },
> > > +        .should_be_valid = true,
> > > +        .expect = (CpuTopology) { 4, 1, 2, 1, 2, 4 },
> > > +    }, {
> > > +        /* config: -smp dies=2,maxcpus=32
> > > +         * expect: cpus=32,sockets=1,dies=2,cores=16,threads=1,maxcpus=32 */
> > > +        .config = (SMPConfiguration) { F, 0, F, 0, T, 2, F, 0, F, 0, T, 32 },
> > > +        .should_be_valid = true,
> > > +        .expect = (CpuTopology) { 32, 1, 2, 16, 1, 32 },
> > > +    }, {
> > > +        /* config: -smp 16,sockets=2,dies=2
> > > +         * expect: cpus=16,sockets=2,dies=2,cores=4,threads=1,maxcpus=16 */
> > > +        .config = (SMPConfiguration) { T, 16, T, 2, T, 2, F, 0, F, 0, F, 0 },
> > > +        .should_be_valid = true,
> > > +        .expect = (CpuTopology) { 16, 2, 2, 4, 1, 16 },
> > > +    }, {
> > > +        /* config: -smp 16,dies=2,cores=4
> > > +         * expect: cpus=16,sockets=2,dies=2,cores=4,threads=1,maxcpus=16 */
> > > +        .config = (SMPConfiguration) { T, 16, F, 0, T, 2, T, 4, F, 0, F, 0 },
> > > +        .should_be_valid = true,
> > > +        .expect = (CpuTopology) { 16, 2, 2, 4, 1, 16 },
> > > +    }, {
> > > +        /* config: -smp 16,dies=2,threads=2
> > > +         * expect: cpus=16,sockets=1,dies=2,cores=4,threads=2,maxcpus=16 */
> > > +        .config = (SMPConfiguration) { T, 16, F, 0, T, 2, F, 0, T, 2, F, 0 },
> > > +        .should_be_valid = true,
> > > +        .expect = (CpuTopology) { 16, 1, 2, 4, 2, 16 },
> > > +    }, {
> > > +        /* config: -smp 16,dies=2,maxcpus=32
> > > +         * expect: cpus=16,sockets=1,dies=2,cores=16,threads=1,maxcpus=32 */
> > > +        .config = (SMPConfiguration) { T, 16, F, 0, T, 2, F, 0, F, 0, T, 32 },
> > > +        .should_be_valid = true,
> > > +        .expect = (CpuTopology) { 16, 1, 2, 16, 1, 32 },
> > > +    }, {
> > > +        /* config: -smp sockets=2,dies=2,cores=4
> > > +         * expect: cpus=16,sockets=2,dies=2,cores=4,threads=1,maxcpus=16 */
> > > +        .config = (SMPConfiguration) { F, 0, T, 2, T, 2, T, 4, F, 0, F, 0 },
> > > +        .should_be_valid = true,
> > > +        .expect = (CpuTopology) { 16, 2, 2, 4, 1, 16 },
> > > +    }, {
> > > +        /* config: -smp sockets=2,dies=2,threads=2
> > > +         * expect: cpus=8,sockets=2,dies=2,cores=1,threads=2,maxcpus=8 */
> > > +        .config = (SMPConfiguration) { F, 0, T, 2, T, 2, F, 0, T, 2, F, 0 },
> > > +        .should_be_valid = true,
> > > +        .expect = (CpuTopology) { 8, 2, 2, 1, 2, 8 },
> > > +    }, {
> > > +        /* config: -smp sockets=2,dies=2,maxcpus=32
> > > +         * expect: cpus=32,sockets=2,dies=2,cores=8,threads=1,maxcpus=32 */
> > > +        .config = (SMPConfiguration) { F, 0, T, 2, T, 2, F, 0, F, 0, T, 32 },
> > > +        .should_be_valid = true,
> > > +        .expect = (CpuTopology) { 32, 2, 2, 8, 1, 32 },
> > > +    }, {
> > > +        /* config: -smp dies=2,cores=4,threads=2
> > > +         * expect: cpus=16,sockets=1,dies=2,cores=4,threads=2,maxcpus=16 */
> > > +        .config = (SMPConfiguration) { F, 0, F, 0, T, 2, T, 4, T, 2, F, 0 },
> > > +        .should_be_valid = true,
> > > +        .expect = (CpuTopology) { 16, 1, 2, 4, 2, 16 },
> > > +    }, {
> > > +        /* config: -smp dies=2,cores=4,maxcpus=32
> > > +         * expect: cpus=32,sockets=4,dies=2,cores=4,threads=1,maxcpus=32 */
> > > +        .config = (SMPConfiguration) { F, 0, F, 0, T, 2, T, 4, F, 0, T, 32 },
> > > +        .should_be_valid = true,
> > > +        .expect = (CpuTopology) { 32, 4, 2, 4, 1, 32 },
> > > +    }, {
> > > +        /* config: -smp dies=2,threads=2,maxcpus=32
> > > +         * expect: cpus=32,sockets=1,dies=2,cores=8,threads=2,maxcpus=32 */
> > > +        .config = (SMPConfiguration) { F, 0, F, 0, T, 2, F, 0, T, 2, T, 32 },
> > > +        .should_be_valid = true,
> > > +        .expect = (CpuTopology) { 32, 1, 2, 8, 2, 32 },
> > > +    }, {
> > > +        /* config: -smp 16,sockets=2,dies=2,cores=4
> > > +         * expect: cpus=16,sockets=2,dies=2,cores=4,threads=1,maxcpus=16 */
> > > +        .config = (SMPConfiguration) { T, 16, T, 2, T, 2, T, 4, F, 0, F, 0 },
> > > +        .should_be_valid = true,
> > > +        .expect = (CpuTopology) { 16, 2, 2, 4, 1, 16 },
> > > +    }, {
> > > +        /* config: -smp 16,sockets=2,dies=2,threads=2
> > > +         * expect: cpus=16,sockets=2,dies=2,cores=2,threads=2,maxcpus=16 */
> > > +        .config = (SMPConfiguration) { T, 16, T, 2, T, 2, F, 0, T, 2, F, 0 },
> > > +        .should_be_valid = true,
> > > +        .expect = (CpuTopology) { 16, 2, 2, 2, 2, 16 },
> > > +    }, {
> > > +        /* config: -smp 16,sockets=2,dies=2,maxcpus=32
> > > +         * expect: cpus=16,sockets=2,dies=2,cores=8,threads=1,maxcpus=32 */
> > > +        .config = (SMPConfiguration) { T, 16, T, 2, T, 2, F, 0, F, 0, T, 32 },
> > > +        .should_be_valid = true,
> > > +        .expect = (CpuTopology) { 16, 2, 2, 8, 1, 32 },
> > > +    }, {
> > > +        /* config: -smp 16,dies=2,cores=4,threads=2
> > > +         * expect: cpus=16,sockets=1,dies=2,cores=4,threads=2,maxcpus=16 */
> > > +        .config = (SMPConfiguration) { T, 16, F, 0, T, 2, T, 4, T, 2, F, 0 },
> > > +        .should_be_valid = true,
> > > +        .expect = (CpuTopology) { 16, 1, 2, 4, 2, 16 },
> > > +    }, {
> > > +        /* config: -smp 16,dies=2,cores=4,maxcpus=32
> > > +         * expect: cpus=16,sockets=4,dies=2,cores=4,threads=1,maxcpus=32 */
> > > +        .config = (SMPConfiguration) { T, 16, F, 0, T, 2, T, 4, F, 0, T, 32 },
> > > +        .should_be_valid = true,
> > > +        .expect = (CpuTopology) { 16, 4, 2, 4, 1, 32 },
> > > +    }, {
> > > +        /* config: -smp 16,dies=2,threads=2,maxcpus=32
> > > +         * expect: cpus=16,sockets=1,dies=2,cores=8,threads=2,maxcpus=32 */
> > > +        .config = (SMPConfiguration) { T, 16, F, 0, T, 2, F, 0, T, 2, T, 32 },
> > > +        .should_be_valid = true,
> > > +        .expect = (CpuTopology) { 16, 1, 2, 8, 2, 32 },
> > > +    }, {
> > > +        /* config: -smp sockets=2,dies=2,cores=4,threads=2
> > > +         * expect: cpus=32,sockets=2,dies=2,cores=4,threads=2,maxcpus=32 */
> > > +        .config = (SMPConfiguration) { F, 0, T, 2, T, 2, T, 4, T, 2, F, 0 },
> > > +        .should_be_valid = true,
> > > +        .expect = (CpuTopology) { 32, 2, 2, 4, 2, 32 },
> > > +    }, {
> > > +        /* config: -smp sockets=2,dies=2,cores=4,maxcpus=32
> > > +         * expect: cpus=32,sockets=2,dies=2,cores=4,threads=2,maxcpus=32 */
> > > +        .config = (SMPConfiguration) { F, 0, T, 2, T, 2, T, 4, F, 0, T, 32 },
> > > +        .should_be_valid = true,
> > > +        .expect = (CpuTopology) { 32, 2, 2, 4, 2, 32 },
> > > +    }, {
> > > +        /* config: -smp sockets=2,dies=2,threads=2,maxcpus=32
> > > +         * expect: cpus=32,sockets=2,dies=2,cores=4,threads=2,maxcpus=32 */
> > > +        .config = (SMPConfiguration) { F, 0, T, 2, T, 2, F, 0, T, 2, T, 32 },
> > > +        .should_be_valid = true,
> > > +        .expect = (CpuTopology) { 32, 2, 2, 4, 2, 32 },
> > > +    }, {
> > > +        /* config: -smp dies=2,cores=4,threads=2,maxcpus=32
> > > +         * expect: cpus=32,sockets=2,dies=2,cores=4,threads=2,maxcpus=32 */
> > > +        .config = (SMPConfiguration) { F, 0, F, 0, T, 2, T, 4, T, 2, T, 32 },
> > > +        .should_be_valid = true,
> > > +        .expect = (CpuTopology) { 32, 2, 2, 4, 2, 32 },
> > > +    }, {
> > > +        /* config: -smp 16,sockets=2,dies=2,cores=4,threads=1
> > > +         * expect: cpus=16,sockets=2,dies=2,cores=4,threads=1,maxcpus=16 */
> > > +        .config = (SMPConfiguration) { T, 16, T, 2, T, 2, T, 4, T, 1, F, 0 },
> > > +        .should_be_valid = true,
> > > +        .expect = (CpuTopology) { 16, 2, 2, 4, 1, 16 },
> > > +    }, {
> > > +        /* config: -smp 16,sockets=2,dies=2,cores=4,maxcpus=32
> > > +         * expect: cpus=16,sockets=2,dies=2,cores=4,threads=2,maxcpus=32 */
> > > +        .config = (SMPConfiguration) { T, 16, T, 2, T, 2, T, 4, F, 0, T, 32 },
> > > +        .should_be_valid = true,
> > > +        .expect = (CpuTopology) { 16, 2, 2, 4, 2, 32 },
> > > +    }, {
> > > +        /* config: -smp 16,sockets=2,dies=2,threads=2,maxcpus=32
> > > +         * expect: cpus=16,sockets=2,dies=2,cores=4,threads=2,maxcpus=32 */
> > > +        .config = (SMPConfiguration) { T, 16, T, 2, T, 2, F, 0, T, 2, T, 32 },
> > > +        .should_be_valid = true,
> > > +        .expect = (CpuTopology) { 16, 2, 2, 4, 2, 32 },
> > > +    }, {
> > > +        /* config: -smp 16,dies=2,cores=4,threads=2,maxcpus=32
> > > +         * expect: cpus=16,sockets=2,dies=2,cores=4,threads=2,maxcpus=32 */
> > > +        .config = (SMPConfiguration) { T, 16, F, 0, T, 2, T, 4, T, 2, T, 32 },
> > > +        .should_be_valid = true,
> > > +        .expect = (CpuTopology) { 16, 2, 2, 4, 2, 32 },
> > > +    }, {
> > > +        /* config: -smp sockets=2,dies=2,cores=4,threads=2,maxcpus=32
> > > +         * expect: -smp 32,sockets=2,dies=2,cores=4,threads=2,maxcpus=32 */
> > > +        .config = (SMPConfiguration) { F, 0, T, 2, T, 2, T, 4, T, 2, T, 32 },
> > > +        .should_be_valid = true,
> > > +        .expect = (CpuTopology) { 32, 2, 2, 4, 2, 32 },
> > > +    }, {
> > > +        /* config: -smp 16,sockets=2,dies=2,cores=4,threads=2,maxcpus=32
> > > +         * expect: cpus=16,sockets=2,dies=2,cores=4,threads=2,maxcpus=32 */
> > > +        .config = (SMPConfiguration) { T, 16, T, 2, T, 2, T, 4, T, 2, T, 32 },
> > > +        .should_be_valid = true,
> > > +        .expect = (CpuTopology) { 16, 2, 2, 4, 2, 32 },
> > > +    },
> > > +};
> > > +
> > > +static char *get_config_info(SMPConfiguration *config)
> > > +{
> > > +    return g_strdup_printf(
> > > +        "(SMPConfiguration) {\n"
> > > +        "    .has_cpus    = %5s, cpus    = %ld,\n"
> > > +        "    .has_sockets = %5s, sockets = %ld,\n"
> > > +        "    .has_dies    = %5s, dies    = %ld,\n"
> > > +        "    .has_cores   = %5s, cores   = %ld,\n"
> > > +        "    .has_threads = %5s, threads = %ld,\n"
> > > +        "    .has_maxcpus = %5s, maxcpus = %ld,\n"
> > > +        "}",
> > > +        config->has_cpus ? "true" : "false", config->cpus,
> > > +        config->has_sockets ? "true" : "false", config->sockets,
> > > +        config->has_dies ? "true" : "false", config->dies,
> > > +        config->has_cores ? "true" : "false", config->cores,
> > > +        config->has_threads ? "true" : "false", config->threads,
> > > +        config->has_maxcpus ? "true" : "false", config->maxcpus);
> > > +}
> > > +
> > > +static char *get_topo_info(CpuTopology *topo)
> > > +{
> > > +    return g_strdup_printf(
> > > +        "(CpuTopology) {\n"
> > > +        "    .cpus     = %u,\n"
> > > +        "    .sockets  = %u,\n"
> > > +        "    .dies     = %u,\n"
> > > +        "    .cores    = %u,\n"
> > > +        "    .threads  = %u,\n"
> > > +        "    .max_cpus = %u,\n"
> > > +        "}",
> > > +        topo->cpus, topo->sockets, topo->dies,
> > > +        topo->cores, topo->threads, topo->max_cpus);
> > > +}
> > > +
> > > +static void check_smp_parse(MachineState *ms, SMPTestData *data)
> > > +{
> > > +    SMPConfiguration *config = &data->config;
> > > +    CpuTopology *expect = &data->expect;
> > > +    g_autofree char *config_info = NULL;
> > > +    g_autofree char *expect_info = NULL;
> > > +    g_autofree char *result_info = NULL;
> > > +    Error *err = NULL;
> > > +
> > > +    /* call the generic parser smp_parse() in hw/core/machine-smp.c */
> > > +    smp_parse(ms, config, &err);
> > > +
> > > +    if (data->should_be_valid) {
> > > +        if ((err == NULL) &&
> > > +            (ms->smp.cpus == expect->cpus) &&
> > > +            (ms->smp.sockets == expect->sockets) &&
> > > +            (ms->smp.dies == expect->dies) &&
> > > +            (ms->smp.cores == expect->cores) &&
> > > +            (ms->smp.threads == expect->threads) &&
> > > +            (ms->smp.max_cpus == expect->max_cpus)) {
> > > +            return;
> > > +        }
> > > +
> > > +        config_info = get_config_info(config);
> > > +        expect_info = get_topo_info(expect);
> > > +
> > > +        if (err != NULL) {
> > > +            g_printerr("Check smp_parse failed:\n"
> > > +                       "config: %s\n"
> > > +                       "expect: %s\n"
> > > +                       "should_be_valid: yes\n\n"
> > > +                       "result_is_valid: no\n"
> > > +                       "error_msg: %s\n",
> > > +                       config_info, expect_info, error_get_pretty(err));
> > > +            error_free(err);
> > > +        } else {
> > > +            result_info = get_topo_info(&ms->smp);
> > > +            g_printerr("Check smp_parse failed:\n"
> > > +                       "config: %s\n"
> > > +                       "expect: %s\n"
> > > +                       "should_be_valid: yes\n\n"
> > > +                       "result_is_valid: yes\n"
> > > +                       "result: %s\n",
> > > +                       config_info, expect_info, result_info);
> > > +        }
> > > +    } else {
> > > +        if (err != NULL) {
> > > +            error_free(err);
> > > +            return;
> > > +        }
> > > +
> > > +        config_info = get_config_info(config);
> > > +        result_info = get_topo_info(&ms->smp);
> > > +
> > > +        g_printerr("Check smp_parse failed:\n"
> > > +                   "config: %s\n"
> > > +                   "should_be_valid: no\n\n"
> > > +                   "result_is_valid: yes\n"
> > > +                   "result: %s\n",
> > > +                   config_info, result_info);
> > > +    }
> > > +
> > > +    abort();
> > > +}
> > > +
> > > +static void smp_prefer_sockets_test(void)
> > > +{
> > > +    Object *obj = object_new(TYPE_MACHINE);
> > > +    MachineState *ms = MACHINE(obj);
> > > +    MachineClass *mc = MACHINE_GET_CLASS(obj);
> > > +    int i;
> > > +
> > > +    /* make sure that we have created the object */
> > > +    g_assert_nonnull(ms);
> > > +    g_assert_nonnull(mc);
> > > +
> > > +    mc->smp_prefer_sockets = true;
> > > +
> > > +    /* test cases when multi-dies are not supported */
> > > +    mc->smp_dies_supported = false;
> > > +    for (i = 0; i < ARRAY_SIZE(prefer_sockets); i++) {
> > > +        check_smp_parse(ms, &prefer_sockets[i]);
> > > +    }
> > > +
> > > +    /* test cases when multi-dies are supported */
> > > +    mc->smp_dies_supported = true;
> > > +    for (i = 0; i < ARRAY_SIZE(prefer_sockets_support_dies); i++) {
> > > +        check_smp_parse(ms, &prefer_sockets_support_dies[i]);
> > > +    }
> > > +
> > > +    object_unref(obj);
> > > +}
> > > +
> > > +static void smp_prefer_cores_test(void)
> > > +{
> > > +    Object *obj = object_new(TYPE_MACHINE);
> > > +    MachineState *ms = MACHINE(obj);
> > > +    MachineClass *mc = MACHINE_GET_CLASS(obj);
> > > +    int i;
> > > +
> > > +    /* make sure that we have created the object */
> > > +    g_assert_nonnull(ms);
> > > +    g_assert_nonnull(mc);
> > > +
> > > +    mc->smp_prefer_sockets = false;
> > > +
> > > +    /* test cases when multi-dies are not supported */
> > > +    mc->smp_dies_supported = false;
> > > +    for (i = 0; i < ARRAY_SIZE(prefer_cores); i++) {
> > > +        check_smp_parse(ms, &prefer_cores[i]);
> > > +    }
> > > +
> > > +    /* test cases when multi-dies are supported */
> > > +    mc->smp_dies_supported = true;
> > > +    for (i = 0; i < ARRAY_SIZE(prefer_cores_support_dies); i++) {
> > > +        check_smp_parse(ms, &prefer_cores_support_dies[i]);
> > > +    }
> > > +
> > > +    object_unref(obj);
> > > +}
> > > +
> > > +int main(int argc, char *argv[])
> > > +{
> > > +    g_test_init(&argc, &argv, NULL);
> > > +
> > > +    module_call_init(MODULE_INIT_QOM);
> > > +    type_register_static(&smp_machine_info);
> > > +
> > > +    g_test_add_func("/test-smp-parse/prefer_sockets", smp_prefer_sockets_test);
> > > +    g_test_add_func("/test-smp-parse/prefer_cores", smp_prefer_cores_test);
> > > +
> > > +    g_test_run();
> > > +
> > > +    return 0;
> > > +}
> > > -- 
> > > 2.19.1
> > > 
> > Besides the nits, it looks good to me.
> > 
> > Reviewed-by: Andrew Jones <drjones@redhat.com>
> > 
> Thanks,
> Yanan
> .
> > .
> 



^ permalink raw reply	[flat|nested] 58+ messages in thread

* Re: [PATCH for-6.2 v2 11/11] tests/unit: Add a unit test for smp parsing
  2021-07-22 13:12       ` Andrew Jones
@ 2021-07-22 14:18         ` wangyanan (Y)
  0 siblings, 0 replies; 58+ messages in thread
From: wangyanan (Y) @ 2021-07-22 14:18 UTC (permalink / raw)
  To: Andrew Jones
  Cc: Peter Maydell, Thomas Huth, Daniel P. Berrangé,
	Eduardo Habkost, Michael S . Tsirkin, Richard Henderson,
	qemu-devel, Greg Kurz, Halil Pasic, Paolo Bonzini, Igor Mammedov,
	yuzenghui, wanghaibin.wang, David Gibson

On 2021/7/22 21:12, Andrew Jones wrote:
> On Thu, Jul 22, 2021 at 02:15:18PM +0800, wangyanan (Y) wrote:
>> On 2021/7/20 2:57, Andrew Jones wrote:
>>> On Mon, Jul 19, 2021 at 11:20:43AM +0800, Yanan Wang wrote:
>>>> Add a QEMU unit test for the parsing of given SMP configuration.
>>>> Since all the parsing logic is in generic function smp_parse(),
>>>> this test passes diffenent SMP configurations to the function
>>>> and compare the parsing result with what is expected.
>>>>
>>>> In the test, all possible collections of the topology parameters
>>>> and the corressponding expected results are listed, including the
>>>> valid and invalid ones.
>>>>
>>>> The preference of sockets over cores and the preference of cores
>>>> over sockets, and the support of multi-dies are also considered.
>>>>
>>>> Signed-off-by: Yanan Wang <wangyanan55@huawei.com>
>>>> ---
>>>>    MAINTAINERS                 |    1 +
>>>>    tests/unit/meson.build      |    1 +
>>>>    tests/unit/test-smp-parse.c | 1117 +++++++++++++++++++++++++++++++++++
>>>>    3 files changed, 1119 insertions(+)
>>>>    create mode 100644 tests/unit/test-smp-parse.c
>>>>
>>>> diff --git a/MAINTAINERS b/MAINTAINERS
>>>> index 70633e3bf4..160dba2e57 100644
>>>> --- a/MAINTAINERS
>>>> +++ b/MAINTAINERS
>>>> @@ -1636,6 +1636,7 @@ F: include/hw/boards.h
>>>>    F: include/hw/core/cpu.h
>>>>    F: include/hw/cpu/cluster.h
>>>>    F: include/sysemu/numa.h
>>>> +F: tests/unit/test-smp-parse.c
>>>>    T: git https://gitlab.com/ehabkost/qemu.git machine-next
>>>>    Xtensa Machines
>>>> diff --git a/tests/unit/meson.build b/tests/unit/meson.build
>>>> index 3e0504dd21..694a924627 100644
>>>> --- a/tests/unit/meson.build
>>>> +++ b/tests/unit/meson.build
>>>> @@ -44,6 +44,7 @@ tests = {
>>>>      'test-uuid': [],
>>>>      'ptimer-test': ['ptimer-test-stubs.c', meson.source_root() / 'hw/core/ptimer.c'],
>>>>      'test-qapi-util': [],
>>>> +  'test-smp-parse': [qom, meson.source_root() / 'hw/core/machine-smp.c'],
>>>>    }
>>>>    if have_system or have_tools
>>>> diff --git a/tests/unit/test-smp-parse.c b/tests/unit/test-smp-parse.c
>>>> new file mode 100644
>>>> index 0000000000..bc1d324c3d
>>>> --- /dev/null
>>>> +++ b/tests/unit/test-smp-parse.c
>>>> @@ -0,0 +1,1117 @@
>>>> +/*
>>>> + * SMP parsing unit-tests
>>>> + *
>>>> + * Copyright (C) 2021, Huawei, Inc.
>>>> + *
>>>> + * Authors:
>>>> + *  Yanan Wang <wangyanan55@huawei.com>
>>>> + *
>>>> + * This work is licensed under the terms of the GNU LGPL, version 2.1 or later.
>>>> + * See the COPYING.LIB file in the top-level directory.
>>>> + */
>>>> +
>>>> +#include "qemu/osdep.h"
>>>> +#include "qom/object.h"
>>>> +#include "qemu/module.h"
>>>> +#include "qapi/error.h"
>>>> +
>>>> +#include "hw/boards.h"
>>>> +
>>>> +#define T true
>>>> +#define F false
>>>> +
>>>> +/**
>>>> + * SMPTestData:
>>>> + * @config - the given SMP configuration for parsing
>>>> + * @should_be_valid - whether the given configuration is supposed to be valid
>>>> + * @expect - the CPU topology info expected to be parsed out
>>>> + */
>>>> +typedef struct SMPTestData {
>>>> +    SMPConfiguration config;
>>>> +    bool should_be_valid;
>>> Long way to say 'valid'.
>> Indeed..., "valid" should be enough.
>>>> +    CpuTopology expect;
>>>> +} SMPTestData;
>>>> +
>>>> +/* the specific machine type info for this test */
>>>> +static const TypeInfo smp_machine_info = {
>>>> +    .name = TYPE_MACHINE,
>>>> +    .parent = TYPE_OBJECT,
>>>> +    .class_size = sizeof(MachineClass),
>>>> +    .instance_size = sizeof(MachineState),
>>>> +};
>>>> +
>>>> +/*
>>>> + * prefer sockets over cores over threads before 6.2.
>>>> + * all possible SMP configurations and the corressponding expected outputs
>>> corresponding (please run spell check on your commit messages)
>>>
>> Ok, I missed the check.
>>>> + * are listed for testing, including the valid and invalid ones.
>>>> + */
>>>> +static struct SMPTestData prefer_sockets[] = {
>>>> +    {
>>>> +        /* config: no smp configuration provided
>>>> +         * expect: cpus=1,sockets=1,dies=1,cores=1,threads=1,maxcpus=1 */
>>>> +        .config = (SMPConfiguration) { F, 0, F, 0, F, 0, F, 0, F, 0, F, 0 },
>>> SMPConfiguration and CpuTopology have named fields so we could drop the
>>> 'expect: ...' comment line and instead do
>>>
>>>    {
>>>     /* no configuration provided */
>>>     .config = { .has_cpus = F, .has_maxcpus = F, ... },
>>>     .valid = T,
>>>     .expect = { .sockets = 1, .cores = 1, ... },
>>>    }, {
>>>     ...
>>>    }
>>>
>>> which may be easier to maintain. OTOH, the concise form this approach has
>>> is also nice.
>> I tried the structure initialization with explicit name fields in it like
>> above,
>> actually we are supposed to do in this way so that we don't have to worry
>> about the order change of the structure members.
>>
>> But this would break the 80-char line limit or introduce more lines for
> I wouldn't worry about 80-char (or even 90, which is when checkpatch
> switches from a warning to an error) for something like this, but that's
> just my opinion. You'd have to get agreement from whomever would decide /
> not decide to merge this.
>
Ok, I think I will try the format with explicit name fields.

Thanks,
Yanan
>
>> for each SMP configuration. If this is not a real problem, I also prefer
>> above format.
>>> I don't think you should need the casts in the assignments
>>> though.
>> Yes, the casts may be unnecessary, will remove them.
>>>> +        .should_be_valid = true,
>>>> +        .expect = (CpuTopology) { 1, 1, 1, 1, 1, 1 },
>>>> +    }, {
>>>> +        /* config: -smp 8
>>>> +         * expect: cpus=8,sockets=8,dies=1,cores=1,threads=1,maxcpus=8 */
>>>> +        .config = (SMPConfiguration) { T, 8, F, 0, F, 0, F, 0, F, 0, F, 0 },
>>>> +        .should_be_valid = true,
>>>> +        .expect = (CpuTopology) { 8, 8, 1, 1, 1, 8 },
>>>> +    }, {
>>>> +        /* config: -smp sockets=2
>>>> +         * expect: cpus=2,sockets=2,dies=1,cores=1,threads=1,maxcpus=2 */
>>>> +        .config = (SMPConfiguration) { F, 0, T, 2, F, 0, F, 0, F, 0, F, 0 },
>>>> +        .should_be_valid = true,
>>>> +        .expect = (CpuTopology) { 2, 2, 1, 1, 1, 2 },
>>>> +    }, {
>>>> +        /* config: -smp cores=4
>>>> +         * expect: cpus=4,sockets=1,dies=1,cores=4,threads=1,maxcpus=4 */
>>>> +        .config = (SMPConfiguration) { F, 0, F, 0, F, 0, T, 4, F, 0, F, 0 },
>>>> +        .should_be_valid = true,
>>>> +        .expect = (CpuTopology) { 4, 1, 1, 4, 1, 4 },
>>>> +    }, {
>>>> +        /* config: -smp threads=2
>>>> +         * expect: cpus=2,sockets=1,dies=1,cores=1,threads=2,maxcpus=2 */
>>>> +        .config = (SMPConfiguration) { F, 0, F, 0, F, 0, F, 0, T, 2, F, 0 },
>>>> +        .should_be_valid = true,
>>>> +        .expect = (CpuTopology) { 2, 1, 1, 1, 2, 2 },
>>>> +    }, {
>>>> +        /* config: -smp maxcpus=16
>>>> +         * expect: cpus=16,sockets=16,dies=1,cores=1,threads=1,maxcpus=16 */
>>>> +        .config = (SMPConfiguration) { F, 0, F, 0, F, 0, F, 0, F, 0, T, 16 },
>>>> +        .should_be_valid = true,
>>>> +        .expect = (CpuTopology) { 16, 16, 1, 1, 1, 16 },
>>>> +    }, {
>>>> +        /* config: -smp 8,sockets=2
>>>> +         * expect: cpus=8,sockets=2,dies=1,cores=4,threads=1,maxcpus=8 */
>>>> +        .config = (SMPConfiguration) { T, 8, T, 2, F, 0, F, 0, F, 0, F, 0 },
>>>> +        .should_be_valid = true,
>>>> +        .expect = (CpuTopology) { 8, 2, 1, 4, 1, 8 },
>>>> +    }, {
>>>> +        /* config: -smp 8,cores=4
>>>> +         * expect: cpus=8,sockets=2,dies=1,cores=4,threads=1,maxcpus=8 */
>>>> +        .config = (SMPConfiguration) { T, 8, F, 0, F, 0, T, 4, F, 0, F, 0 },
>>>> +        .should_be_valid = true,
>>>> +        .expect = (CpuTopology) { 8, 2, 1, 4, 1, 8 },
>>>> +    }, {
>>>> +        /* config: -smp 8,threads=2
>>>> +         * expect: cpus=8,sockets=4,dies=1,cores=1,threads=2,maxcpus=8 */
>>>> +        .config = (SMPConfiguration) { T, 8, F, 0, F, 0, F, 0, T, 2, F, 0 },
>>>> +        .should_be_valid = true,
>>>> +        .expect = (CpuTopology) { 8, 4, 1, 1, 2, 8 },
>>>> +    }, {
>>>> +        /* config: -smp 8,maxcpus=16
>>>> +         * expect: cpus=8,sockets=16,dies=1,cores=1,threads=1,maxcpus=16 */
>>>> +        .config = (SMPConfiguration) { T, 8, F, 0, F, 0, F, 0, F, 0, T, 16 },
>>>> +        .should_be_valid = true,
>>>> +        .expect = (CpuTopology) { 8, 16, 1, 1, 1, 16 },
>>>> +    }, {
>>>> +        /* config: -smp sockets=2,cores=4
>>>> +         * expect: cpus=8,sockets=2,dies=1,cores=4,threads=1,maxcpus=8 */
>>>> +        .config = (SMPConfiguration) { F, 0, T, 2, F, 0, T, 4, F, 0, F, 0 },
>>>> +        .should_be_valid = true,
>>>> +        .expect = (CpuTopology) { 8, 2, 1, 4, 1, 8 },
>>>> +    }, {
>>>> +        /* config: -smp sockets=2,threads=2
>>>> +         * expect: cpus=4,sockets=2,dies=1,cores=1,threads=2,maxcpus=4 */
>>>> +        .config = (SMPConfiguration) { F, 0, T, 2, F, 0, F, 0, T, 2, F, 0 },
>>>> +        .should_be_valid = true,
>>>> +        .expect = (CpuTopology) { 4, 2, 1, 1, 2, 4 },
>>>> +    }, {
>>>> +        /* config: -smp sockets=2,maxcpus=16
>>>> +         * expect: cpus=16,sockets=2,dies=1,cores=8,threads=1,maxcpus=16 */
>>>> +        .config = (SMPConfiguration) { F, 0, T, 2, F, 0, F, 0, F, 0, T, 16 },
>>>> +        .should_be_valid = true,
>>>> +        .expect = (CpuTopology) { 16, 2, 1, 8, 1, 16 },
>>>> +    }, {
>>>> +        /* config: -smp cores=4,threads=2
>>>> +         * expect: cpus=8,sockets=1,dies=1,cores=4,threads=2,maxcpus=8 */
>>>> +        .config = (SMPConfiguration) { F, 0, F, 0, F, 0, T, 4, T, 2, F, 0 },
>>>> +        .should_be_valid = true,
>>>> +        .expect = (CpuTopology) { 8, 1, 1, 4, 2, 8 },
>>>> +    }, {
>>>> +        /* config: -smp cores=4,maxcpus=16
>>>> +         * expect: cpus=16,sockets=4,dies=1,cores=4,threads=1,maxcpus=16 */
>>>> +        .config = (SMPConfiguration) { F, 0, F, 0, F, 0, T, 4, F, 0, T, 16 },
>>>> +        .should_be_valid = true,
>>>> +        .expect = (CpuTopology) { 16, 4, 1, 4, 1, 16 },
>>>> +    }, {
>>>> +        /* config: -smp threads=2,maxcpus=16
>>>> +         * expect: cpus=16,sockets=8,dies=1,cores=1,threads=2,maxcpus=16 */
>>>> +        .config = (SMPConfiguration) { F, 0, F, 0, F, 0, F, 0, T, 2, T, 16 },
>>>> +        .should_be_valid = true,
>>>> +        .expect = (CpuTopology) { 16, 8, 1, 1, 2, 16 },
>>>> +    }, {
>>>> +        /* config: -smp 8,sockets=2,cores=4
>>>> +         * expect: cpus=8,sockets=2,dies=1,cores=4,threads=1,maxcpus=8 */
>>>> +        .config = (SMPConfiguration) { T, 8, T, 2, F, 0, T, 4, F, 0, F, 0 },
>>>> +        .should_be_valid = true,
>>>> +        .expect = (CpuTopology) { 8, 2, 1, 4, 1, 8 },
>>>> +    }, {
>>>> +        /* config: -smp 8,sockets=2,threads=2
>>>> +         * expect: cpus=8,sockets=2,dies=1,cores=2,threads=2,maxcpus=8 */
>>>> +        .config = (SMPConfiguration) { T, 8, T, 2, F, 0, F, 0, T, 2, F, 0 },
>>>> +        .should_be_valid = true,
>>>> +        .expect = (CpuTopology) { 8, 2, 1, 2, 2, 8 },
>>>> +    }, {
>>>> +        /* config: -smp 8,sockets=2,maxcpus=16
>>>> +         * expect: cpus=8,sockets=2,dies=1,cores=8,threads=1,maxcpus=16 */
>>>> +        .config = (SMPConfiguration) { T, 8, T, 2, F, 0, F, 0, F, 0, T, 16 },
>>>> +        .should_be_valid = true,
>>>> +        .expect = (CpuTopology) { 8, 2, 1, 8, 1, 16 },
>>>> +    }, {
>>>> +        /* config: -smp 8,cores=4,threads=2
>>>> +         * expect: cpus=8,sockets=1,dies=1,cores=4,threads=2,maxcpus=8 */
>>>> +        .config = (SMPConfiguration) { T, 8, F, 0, F, 0, T, 4, T, 2, F, 0 },
>>>> +        .should_be_valid = true,
>>>> +        .expect = (CpuTopology) { 8, 1, 1, 4, 2, 8 },
>>>> +    }, {
>>>> +        /* config: -smp 8,cores=4,maxcpus=16
>>>> +         * expect: cpus=8,sockets=4,dies=1,cores=4,threads=1,maxcpus=16 */
>>>> +        .config = (SMPConfiguration) { T, 8, F, 0, F, 0, T, 4, F, 0, T, 16 },
>>>> +        .should_be_valid = true,
>>>> +        .expect = (CpuTopology) { 8, 4, 1, 4, 1, 16 },
>>>> +    }, {
>>>> +        /* config: -smp 8,threads=2,maxcpus=16
>>>> +         * expect: cpus=8,sockets=8,dies=1,cores=1,threads=2,maxcpus=16 */
>>>> +        .config = (SMPConfiguration) { T, 8, F, 0, F, 0, F, 0, T, 2, T, 16 },
>>>> +        .should_be_valid = true,
>>>> +        .expect = (CpuTopology) { 8, 8, 1, 1, 2, 16 },
>>>> +    }, {
>>>> +        /* config: -smp sockets=2,cores=4,threads=2
>>>> +         * expect: cpus=16,sockets=2,dies=1,cores=4,threads=2,maxcpus=16 */
>>>> +        .config = (SMPConfiguration) { F, 0, T, 2, F, 0, T, 4, T, 2, F, 0 },
>>>> +        .should_be_valid = true,
>>>> +        .expect = (CpuTopology) { 16, 2, 1, 4, 2, 16 },
>>>> +    }, {
>>>> +        /* config: -smp sockets=2,cores=4,maxcpus=16
>>>> +         * expect: cpus=16,sockets=2,dies=1,cores=4,threads=2,maxcpus=16 */
>>>> +        .config = (SMPConfiguration) { F, 0, T, 2, F, 0, T, 4, F, 0, T, 16 },
>>>> +        .should_be_valid = true,
>>>> +        .expect = (CpuTopology) { 16, 2, 1, 4, 2, 16 },
>>>> +    }, {
>>>> +        /* config: -smp sockets=2,threads=2,maxcpus=16
>>>> +         * expect: cpus=16,sockets=2,dies=1,cores=4,threads=2,maxcpus=16 */
>>>> +        .config = (SMPConfiguration) { F, 0, T, 2, F, 0, F, 0, T, 2, T, 16 },
>>>> +        .should_be_valid = true,
>>>> +        .expect = (CpuTopology) { 16, 2, 1, 4, 2, 16 },
>>>> +    }, {
>>>> +        /* config: -smp cores=4,threads=2,maxcpus=16
>>>> +         * expect: cpus=16,sockets=2,dies=1,cores=4,threads=2,maxcpus=16 */
>>>> +        .config = (SMPConfiguration) { F, 0, F, 0, F, 0, T, 4, T, 2, T, 16 },
>>>> +        .should_be_valid = true,
>>>> +        .expect = (CpuTopology) { 16, 2, 1, 4, 2, 16 },
>>>> +    }, {
>>>> +        /* config: -smp 8,sockets=2,cores=4,threads=1
>>>> +         * expect: cpus=8,sockets=2,dies=1,cores=4,threads=1,maxcpus=8 */
>>>> +        .config = (SMPConfiguration) { T, 8, T, 2, F, 0, T, 4, T, 1, F, 0 },
>>>> +        .should_be_valid = true,
>>>> +        .expect = (CpuTopology) { 8, 2, 1, 4, 1, 8 },
>>>> +    }, {
>>>> +        /* config: -smp 8,sockets=2,cores=4,maxcpus=16
>>>> +         * expect: cpus=8,sockets=2,dies=1,cores=4,threads=2,maxcpus=16 */
>>>> +        .config = (SMPConfiguration) { T, 8, T, 2, F, 0, T, 4, F, 0, T, 16 },
>>>> +        .should_be_valid = true,
>>>> +        .expect = (CpuTopology) { 8, 2, 1, 4, 2, 16 },
>>>> +    }, {
>>>> +        /* config: -smp 8,sockets=2,threads=2,maxcpus=16
>>>> +         * expect: cpus=8,sockets=2,dies=1,cores=4,threads=2,maxcpus=16 */
>>>> +        .config = (SMPConfiguration) { T, 8, T, 2, F, 0, F, 0, T, 2, T, 16 },
>>>> +        .should_be_valid = true,
>>>> +        .expect = (CpuTopology) { 8, 2, 1, 4, 2, 16 },
>>>> +    }, {
>>>> +        /* config: -smp 8,cores=4,threads=2,maxcpus=16
>>>> +         * expect: cpus=8,sockets=2,dies=1,cores=4,threads=2,maxcpus=16 */
>>>> +        .config = (SMPConfiguration) { T, 8, F, 0, F, 0, T, 4, T, 2, T, 16 },
>>>> +        .should_be_valid = true,
>>>> +        .expect = (CpuTopology) { 8, 2, 1, 4, 2, 16 },
>>>> +    }, {
>>>> +        /* config: -smp sockets=2,cores=4,threads=2,maxcpus=16
>>>> +         * expect: -smp 16,sockets=2,dies=1,cores=4,threads=2,maxcpus=16 */
>>>> +        .config = (SMPConfiguration) { F, 0, T, 2, F, 0, T, 4, T, 2, T, 16 },
>>>> +        .should_be_valid = true,
>>>> +        .expect = (CpuTopology) { 16, 2, 1, 4, 2, 16 },
>>>> +    }, {
>>>> +        /* config: -smp 8,sockets=2,cores=4,threads=2,maxcpus=16
>>>> +         * expect: cpus=8,sockets=2,dies=1,cores=4,threads=2,maxcpus=16 */
>>>> +        .config = (SMPConfiguration) { T, 8, T, 2, F, 0, T, 4, T, 2, T, 16 },
>>>> +        .should_be_valid = true,
>>>> +        .expect = (CpuTopology) { 8, 2, 1, 4, 2, 16 },
>>>> +    }, {
>>>> +        /* config: -smp 8,sockets=2,dies=1,cores=4,threads=2,maxcpus=16
>>>> +         * expect: cpus=8,sockets=2,dies=1,cores=4,threads=2,maxcpus=16 */
>>>> +        .config = (SMPConfiguration) { T, 8, T, 2, T, 1, T, 4, T, 2, T, 16 },
>>>> +        .should_be_valid = true,
>>>> +        .expect = (CpuTopology) { 8, 2, 1, 4, 2, 16 },
>>>> +    }, {
>>>> +        /* config: -smp 0
>>>> +         * expect: error, "anything=0" is not allowed */
>>>> +        .config = (SMPConfiguration) { T, 0, F, 0, F, 0, F, 0, F, 0, F, 0 },
>>>> +        .should_be_valid = false,
>>>> +    }, {
>>>> +        /* config: -smp 8,sockets=0
>>>> +         * expect: error, "anything=0" is not allowed */
>>>> +        .config = (SMPConfiguration) { T, 8, T, 0, F, 0, F, 0, F, 0, F, 0 },
>>>> +        .should_be_valid = false,
>>>> +    }, {
>>>> +        /* config: -smp 8,sockets=2,dies=0
>>>> +         * expect: error, "anything=0" is not allowed */
>>>> +        .config = (SMPConfiguration) { T, 0, T, 2, T, 0, F, 0, F, 0, F, 0 },
>>>> +        .should_be_valid = false,
>>>> +    }, {
>>>> +        /* config: -smp 8,sockets=2,dies=1,cores=0
>>>> +         * expect: error, "anything=0" is not allowed */
>>>> +        .config = (SMPConfiguration) { T, 8, T, 2, T, 1, T, 0, F, 0, F, 0 },
>>>> +        .should_be_valid = false,
>>>> +    }, {
>>>> +        /* config: -smp 8,sockets=2,dies=1,cores=4,threads=0
>>>> +         * expect: error, "anything=0" is not allowed */
>>>> +        .config = (SMPConfiguration) { T, 8, T, 2, T, 1, T, 4, T, 0, F, 0 },
>>>> +        .should_be_valid = false,
>>>> +    }, {
>>>> +        /* config: -smp 8,sockets=2,dies=1,cores=4,threads=2,maxcpus=0
>>>> +         * expect: error, "anything=0" is not allowed */
>>>> +        .config = (SMPConfiguration) { T, 8, T, 2, T, 1, T, 4, T, 2, T, 0 },
>>>> +        .should_be_valid = false,
>>>> +    }, {
>>>> +        /* config: -smp 8,dies=2
>>>> +         * expect: error, multi-dies not supported */
>>>> +        .config = (SMPConfiguration) { T, 8, F, 0, T, 2, F, 0, F, 0, F, 0 },
>>>> +        .should_be_valid = false,
>>>> +    }, {
>>>> +        /* config: -smp 8,sockets=2,cores=8
>>>> +         * expect: error, sum (16) != max_cpus (8) */
>>>> +        .config = (SMPConfiguration) { T, 8, T, 2, F, 0, T, 4, T, 2, F, 0 },
>>>> +        .should_be_valid = false,
>>>> +    }, {
>>>> +        /* config: -smp 8,sockets=2,cores=5,threads=2,maxcpus=16
>>>> +         * expect: error, sum (20) != max_cpus (16) */
>>>> +        .config = (SMPConfiguration) { F, 0, T, 3, F, 0, T, 5, T, 1, T, 16 },
>>>> +        .should_be_valid = false,
>>>> +    }, {
>>>> +        /* config: -smp 16,maxcpus=12
>>>> +         * expect: error, sum (12) < smp_cpus (16) */
>>>> +        .config = (SMPConfiguration) { T, 16, F, 0, F, 0, F, 0, F, 0, T, 12 },
>>>> +        .should_be_valid = false,
>>>> +    },
>>>> +};
>>>> +
>>>> +static struct SMPTestData prefer_sockets_support_dies[] = {
>>>> +    {
>>>> +        /* config: -smp dies=2
>>>> +         * expect: cpus=2,sockets=1,dies=2,cores=1,threads=1,maxcpus=2 */
>>>> +        .config = (SMPConfiguration) { F, 0, F, 0, T, 2, F, 0, F, 0, F, 0 },
>>>> +        .should_be_valid = true,
>>>> +        .expect = (CpuTopology) { 2, 1, 2, 1, 1, 2 },
>>>> +    }, {
>>>> +        /* config: -smp 16,dies=2
>>>> +         * expect: cpus=16,sockets=8,dies=2,cores=1,threads=1,maxcpus=16 */
>>>> +        .config = (SMPConfiguration) { T, 16, F, 0, T, 2, F, 0, F, 0, F, 0 },
>>>> +        .should_be_valid = true,
>>>> +        .expect = (CpuTopology) { 16, 8, 2, 1, 1, 16 },
>>>> +    }, {
>>>> +        /* config: -smp sockets=2,dies=2
>>>> +         * expect: cpus=4,sockets=2,dies=2,cores=1,threads=1,maxcpus=4 */
>>>> +        .config = (SMPConfiguration) { F, 0, T, 2, T, 2, F, 0, F, 0, F, 0 },
>>>> +        .should_be_valid = true,
>>>> +        .expect = (CpuTopology) { 4, 2, 2, 1, 1, 4 },
>>>> +    }, {
>>>> +        /* config: -smp dies=2,cores=4
>>>> +         * expect: cpus=8,sockets=1,dies=2,cores=4,threads=1,maxcpus=8 */
>>>> +        .config = (SMPConfiguration) { F, 0, F, 0, T, 2, T, 4, F, 0, F, 0 },
>>>> +        .should_be_valid = true,
>>>> +        .expect = (CpuTopology) { 8, 1, 2, 4, 1, 8 },
>>>> +    }, {
>>>> +        /* config: -smp dies=2,threads=2
>>>> +         * expect: cpus=4,sockets=1,dies=2,cores=1,threads=2,maxcpus=4 */
>>>> +        .config = (SMPConfiguration) { F, 0, F, 0, T, 2, F, 0, T, 2, F, 0 },
>>>> +        .should_be_valid = true,
>>>> +        .expect = (CpuTopology) { 4, 1, 2, 1, 2, 4 },
>>>> +    }, {
>>>> +        /* config: -smp dies=2,maxcpus=32
>>>> +         * expect: cpus=32,sockets=16,dies=2,cores=1,threads=1,maxcpus=32 */
>>>> +        .config = (SMPConfiguration) { F, 0, F, 0, T, 2, F, 0, F, 0, T, 32 },
>>>> +        .should_be_valid = true,
>>>> +        .expect = (CpuTopology) { 32, 16, 2, 1, 1, 32 },
>>>> +    }, {
>>>> +        /* config: -smp 16,sockets=2,dies=2
>>>> +         * expect: cpus=16,sockets=2,dies=2,cores=4,threads=1,maxcpus=16 */
>>>> +        .config = (SMPConfiguration) { T, 16, T, 2, T, 2, F, 0, F, 0, F, 0 },
>>>> +        .should_be_valid = true,
>>>> +        .expect = (CpuTopology) { 16, 2, 2, 4, 1, 16 },
>>>> +    }, {
>>>> +        /* config: -smp 16,dies=2,cores=4
>>>> +         * expect: cpus=16,sockets=2,dies=2,cores=4,threads=1,maxcpus=16 */
>>>> +        .config = (SMPConfiguration) { T, 16, F, 0, T, 2, T, 4, F, 0, F, 0 },
>>>> +        .should_be_valid = true,
>>>> +        .expect = (CpuTopology) { 16, 2, 2, 4, 1, 16 },
>>>> +    }, {
>>>> +        /* config: -smp 16,dies=2,threads=2
>>>> +         * expect: cpus=16,sockets=4,dies=2,cores=1,threads=2,maxcpus=16 */
>>>> +        .config = (SMPConfiguration) { T, 16, F, 0, T, 2, F, 0, T, 2, F, 0 },
>>>> +        .should_be_valid = true,
>>>> +        .expect = (CpuTopology) { 16, 4, 2, 1, 2, 16 },
>>>> +    }, {
>>>> +        /* config: -smp 16,dies=2,maxcpus=32
>>>> +         * expect: cpus=16,sockets=16,dies=2,cores=1,threads=1,maxcpus=32 */
>>>> +        .config = (SMPConfiguration) { T, 16, F, 0, T, 2, F, 0, F, 0, T, 32 },
>>>> +        .should_be_valid = true,
>>>> +        .expect = (CpuTopology) { 16, 16, 2, 1, 1, 32 },
>>>> +    }, {
>>>> +        /* config: -smp sockets=2,dies=2,cores=4
>>>> +         * expect: cpus=16,sockets=2,dies=2,cores=4,threads=1,maxcpus=16 */
>>>> +        .config = (SMPConfiguration) { F, 0, T, 2, T, 2, T, 4, F, 0, F, 0 },
>>>> +        .should_be_valid = true,
>>>> +        .expect = (CpuTopology) { 16, 2, 2, 4, 1, 16 },
>>>> +    }, {
>>>> +        /* config: -smp sockets=2,dies=2,threads=2
>>>> +         * expect: cpus=8,sockets=2,dies=2,cores=1,threads=2,maxcpus=8 */
>>>> +        .config = (SMPConfiguration) { F, 0, T, 2, T, 2, F, 0, T, 2, F, 0 },
>>>> +        .should_be_valid = true,
>>>> +        .expect = (CpuTopology) { 8, 2, 2, 1, 2, 8 },
>>>> +    }, {
>>>> +        /* config: -smp sockets=2,dies=2,maxcpus=32
>>>> +         * expect: cpus=32,sockets=2,dies=2,cores=8,threads=1,maxcpus=32 */
>>>> +        .config = (SMPConfiguration) { F, 0, T, 2, T, 2, F, 0, F, 0, T, 32 },
>>>> +        .should_be_valid = true,
>>>> +        .expect = (CpuTopology) { 32, 2, 2, 8, 1, 32 },
>>>> +    }, {
>>>> +        /* config: -smp dies=2,cores=4,threads=2
>>>> +         * expect: cpus=16,sockets=1,dies=2,cores=4,threads=2,maxcpus=16 */
>>>> +        .config = (SMPConfiguration) { F, 0, F, 0, T, 2, T, 4, T, 2, F, 0 },
>>>> +        .should_be_valid = true,
>>>> +        .expect = (CpuTopology) { 16, 1, 2, 4, 2, 16 },
>>>> +    }, {
>>>> +        /* config: -smp dies=2,cores=4,maxcpus=32
>>>> +         * expect: cpus=32,sockets=4,dies=2,cores=4,threads=1,maxcpus=32 */
>>>> +        .config = (SMPConfiguration) { F, 0, F, 0, T, 2, T, 4, F, 0, T, 32 },
>>>> +        .should_be_valid = true,
>>>> +        .expect = (CpuTopology) { 32, 4, 2, 4, 1, 32 },
>>>> +    }, {
>>>> +        /* config: -smp dies=2,threads=2,maxcpus=32
>>>> +         * expect: cpus=32,sockets=8,dies=2,cores=1,threads=2,maxcpus=32 */
>>>> +        .config = (SMPConfiguration) { F, 0, F, 0, T, 2, F, 0, T, 2, T, 32 },
>>>> +        .should_be_valid = true,
>>>> +        .expect = (CpuTopology) { 32, 8, 2, 1, 2, 32 },
>>>> +    }, {
>>>> +        /* config: -smp 16,sockets=2,dies=2,cores=4
>>>> +         * expect: cpus=16,sockets=2,dies=2,cores=4,threads=1,maxcpus=16 */
>>>> +        .config = (SMPConfiguration) { T, 16, T, 2, T, 2, T, 4, F, 0, F, 0 },
>>>> +        .should_be_valid = true,
>>>> +        .expect = (CpuTopology) { 16, 2, 2, 4, 1, 16 },
>>>> +    }, {
>>>> +        /* config: -smp 16,sockets=2,dies=2,threads=2
>>>> +         * expect: cpus=16,sockets=2,dies=2,cores=2,threads=2,maxcpus=16 */
>>>> +        .config = (SMPConfiguration) { T, 16, T, 2, T, 2, F, 0, T, 2, F, 0 },
>>>> +        .should_be_valid = true,
>>>> +        .expect = (CpuTopology) { 16, 2, 2, 2, 2, 16 },
>>>> +    }, {
>>>> +        /* config: -smp 16,sockets=2,dies=2,maxcpus=32
>>>> +         * expect: cpus=16,sockets=2,dies=2,cores=8,threads=1,maxcpus=32 */
>>>> +        .config = (SMPConfiguration) { T, 16, T, 2, T, 2, F, 0, F, 0, T, 32 },
>>>> +        .should_be_valid = true,
>>>> +        .expect = (CpuTopology) { 16, 2, 2, 8, 1, 32 },
>>>> +    }, {
>>>> +        /* config: -smp 16,dies=2,cores=4,threads=2
>>>> +         * expect: cpus=16,sockets=1,dies=2,cores=4,threads=2,maxcpus=16 */
>>>> +        .config = (SMPConfiguration) { T, 16, F, 0, T, 2, T, 4, T, 2, F, 0 },
>>>> +        .should_be_valid = true,
>>>> +        .expect = (CpuTopology) { 16, 1, 2, 4, 2, 16 },
>>>> +    }, {
>>>> +        /* config: -smp 16,dies=2,cores=4,maxcpus=32
>>>> +         * expect: cpus=16,sockets=4,dies=2,cores=4,threads=1,maxcpus=32 */
>>>> +        .config = (SMPConfiguration) { T, 16, F, 0, T, 2, T, 4, F, 0, T, 32 },
>>>> +        .should_be_valid = true,
>>>> +        .expect = (CpuTopology) { 16, 4, 2, 4, 1, 32 },
>>>> +    }, {
>>>> +        /* config: -smp 16,dies=2,threads=2,maxcpus=32
>>>> +         * expect: cpus=16,sockets=8,dies=2,cores=1,threads=2,maxcpus=32 */
>>>> +        .config = (SMPConfiguration) { T, 16, F, 0, T, 2, F, 0, T, 2, T, 32 },
>>>> +        .should_be_valid = true,
>>>> +        .expect = (CpuTopology) { 16, 8, 2, 1, 2, 32 },
>>>> +    }, {
>>>> +        /* config: -smp sockets=2,dies=2,cores=4,threads=2
>>>> +         * expect: cpus=32,sockets=2,dies=2,cores=4,threads=2,maxcpus=32 */
>>>> +        .config = (SMPConfiguration) { F, 0, T, 2, T, 2, T, 4, T, 2, F, 0 },
>>>> +        .should_be_valid = true,
>>>> +        .expect = (CpuTopology) { 32, 2, 2, 4, 2, 32 },
>>>> +    }, {
>>>> +        /* config: -smp sockets=2,dies=2,cores=4,maxcpus=32
>>>> +         * expect: cpus=32,sockets=2,dies=2,cores=4,threads=2,maxcpus=32 */
>>>> +        .config = (SMPConfiguration) { F, 0, T, 2, T, 2, T, 4, F, 0, T, 32 },
>>>> +        .should_be_valid = true,
>>>> +        .expect = (CpuTopology) { 32, 2, 2, 4, 2, 32 },
>>>> +    }, {
>>>> +        /* config: -smp sockets=2,dies=2,threads=2,maxcpus=32
>>>> +         * expect: cpus=32,sockets=2,dies=2,cores=4,threads=2,maxcpus=32 */
>>>> +        .config = (SMPConfiguration) { F, 0, T, 2, T, 2, F, 0, T, 2, T, 32 },
>>>> +        .should_be_valid = true,
>>>> +        .expect = (CpuTopology) { 32, 2, 2, 4, 2, 32 },
>>>> +    }, {
>>>> +        /* config: -smp dies=2,cores=4,threads=2,maxcpus=32
>>>> +         * expect: cpus=32,sockets=2,dies=2,cores=4,threads=2,maxcpus=32 */
>>>> +        .config = (SMPConfiguration) { F, 0, F, 0, T, 2, T, 4, T, 2, T, 32 },
>>>> +        .should_be_valid = true,
>>>> +        .expect = (CpuTopology) { 32, 2, 2, 4, 2, 32 },
>>>> +    }, {
>>>> +        /* config: -smp 16,sockets=2,dies=2,cores=4,threads=1
>>>> +         * expect: cpus=16,sockets=2,dies=2,cores=4,threads=1,maxcpus=16 */
>>>> +        .config = (SMPConfiguration) { T, 16, T, 2, T, 2, T, 4, T, 1, F, 0 },
>>>> +        .should_be_valid = true,
>>>> +        .expect = (CpuTopology) { 16, 2, 2, 4, 1, 16 },
>>>> +    }, {
>>>> +        /* config: -smp 16,sockets=2,dies=2,cores=4,maxcpus=32
>>>> +         * expect: cpus=16,sockets=2,dies=2,cores=4,threads=2,maxcpus=32 */
>>>> +        .config = (SMPConfiguration) { T, 16, T, 2, T, 2, T, 4, F, 0, T, 32 },
>>>> +        .should_be_valid = true,
>>>> +        .expect = (CpuTopology) { 16, 2, 2, 4, 2, 32 },
>>>> +    }, {
>>>> +        /* config: -smp 16,sockets=2,dies=2,threads=2,maxcpus=32
>>>> +         * expect: cpus=16,sockets=2,dies=2,cores=4,threads=2,maxcpus=32 */
>>>> +        .config = (SMPConfiguration) { T, 16, T, 2, T, 2, F, 0, T, 2, T, 32 },
>>>> +        .should_be_valid = true,
>>>> +        .expect = (CpuTopology) { 16, 2, 2, 4, 2, 32 },
>>>> +    }, {
>>>> +        /* config: -smp 16,dies=2,cores=4,threads=2,maxcpus=32
>>>> +         * expect: cpus=16,sockets=2,dies=2,cores=4,threads=2,maxcpus=32 */
>>>> +        .config = (SMPConfiguration) { T, 16, F, 0, T, 2, T, 4, T, 2, T, 32 },
>>>> +        .should_be_valid = true,
>>>> +        .expect = (CpuTopology) { 16, 2, 2, 4, 2, 32 },
>>>> +    }, {
>>>> +        /* config: -smp sockets=2,dies=2,cores=4,threads=2,maxcpus=32
>>>> +         * expect: -smp 32,sockets=2,dies=2,cores=4,threads=2,maxcpus=32 */
>>>> +        .config = (SMPConfiguration) { F, 0, T, 2, T, 2, T, 4, T, 2, T, 32 },
>>>> +        .should_be_valid = true,
>>>> +        .expect = (CpuTopology) { 32, 2, 2, 4, 2, 32 },
>>>> +    }, {
>>>> +        /* config: -smp 16,sockets=2,dies=2,cores=4,threads=2,maxcpus=32
>>>> +         * expect: cpus=16,sockets=2,dies=2,cores=4,threads=2,maxcpus=32 */
>>>> +        .config = (SMPConfiguration) { T, 16, T, 2, T, 2, T, 4, T, 2, T, 32 },
>>>> +        .should_be_valid = true,
>>>> +        .expect = (CpuTopology) { 16, 2, 2, 4, 2, 32 },
>>>> +    },
>>>> +};
>>>> +
>>>> +/*
>>>> + * prefer cores over sockets over threads since 6.2.
>>>> + * all possible SMP configurations and the corressponding expected outputs
>>>> + * are listed for testing, including the valid and invalid ones.
>>>> + */
>>>> +static struct SMPTestData prefer_cores[] = {
>>>> +    {
>>>> +        /* config: no smp configuration
>>>> +         * expect: cpus=1,sockets=1,dies=1,cores=1,threads=1,maxcpus=1 */
>>>> +        .config = (SMPConfiguration) { F, 0, F, 0, F, 0, F, 0, F, 0, F, 0 },
>>>> +        .should_be_valid = true,
>>>> +        .expect = (CpuTopology) { 1, 1, 1, 1, 1, 1 },
>>>> +    }, {
>>>> +        /* config: -smp 8
>>>> +         * expect: cpus=8,sockets=1,dies=1,cores=8,threads=1,maxcpus=8 */
>>>> +        .config = (SMPConfiguration) { T, 8, F, 0, F, 0, F, 0, F, 0, F, 0 },
>>>> +        .should_be_valid = true,
>>>> +        .expect = (CpuTopology) { 8, 1, 1, 8, 1, 8 },
>>>> +    }, {
>>>> +        /* config: -smp sockets=2
>>>> +         * expect: cpus=2,sockets=2,dies=1,cores=1,threads=1,maxcpus=2 */
>>>> +        .config = (SMPConfiguration) { F, 0, T, 2, F, 0, F, 0, F, 0, F, 0 },
>>>> +        .should_be_valid = true,
>>>> +        .expect = (CpuTopology) { 2, 2, 1, 1, 1, 2 },
>>>> +    }, {
>>>> +        /* config: -smp cores=4
>>>> +         * expect: cpus=4,sockets=1,dies=1,cores=4,threads=1,maxcpus=4 */
>>>> +        .config = (SMPConfiguration) { F, 0, F, 0, F, 0, T, 4, F, 0, F, 0 },
>>>> +        .should_be_valid = true,
>>>> +        .expect = (CpuTopology) { 4, 1, 1, 4, 1, 4 },
>>>> +    }, {
>>>> +        /* config: -smp threads=2
>>>> +         * expect: cpus=2,sockets=1,dies=1,cores=1,threads=2,maxcpus=2 */
>>>> +        .config = (SMPConfiguration) { F, 0, F, 0, F, 0, F, 0, T, 2, F, 0 },
>>>> +        .should_be_valid = true,
>>>> +        .expect = (CpuTopology) { 2, 1, 1, 1, 2, 2 },
>>>> +    }, {
>>>> +        /* config: -smp maxcpus=16
>>>> +         * expect: cpus=16,sockets=1,dies=1,cores=16,threads=1,maxcpus=16 */
>>>> +        .config = (SMPConfiguration) { F, 0, F, 0, F, 0, F, 0, F, 0, T, 16 },
>>>> +        .should_be_valid = true,
>>>> +        .expect = (CpuTopology) { 16, 1, 1, 16, 1, 16 },
>>>> +    }, {
>>>> +        /* config: -smp 8,sockets=2
>>>> +         * expect: cpus=8,sockets=2,dies=1,cores=4,threads=1,maxcpus=8 */
>>>> +        .config = (SMPConfiguration) { T, 8, T, 2, F, 0, F, 0, F, 0, F, 0 },
>>>> +        .should_be_valid = true,
>>>> +        .expect = (CpuTopology) { 8, 2, 1, 4, 1, 8 },
>>>> +    }, {
>>>> +        /* config: -smp 8,cores=4
>>>> +         * expect: cpus=8,sockets=2,dies=1,cores=4,threads=1,maxcpus=8 */
>>>> +        .config = (SMPConfiguration) { T, 8, F, 0, F, 0, T, 4, F, 0, F, 0 },
>>>> +        .should_be_valid = true,
>>>> +        .expect = (CpuTopology) { 8, 2, 1, 4, 1, 8 },
>>>> +    }, {
>>>> +        /* config: -smp 8,threads=2
>>>> +         * expect: cpus=8,sockets=1,dies=1,cores=4,threads=2,maxcpus=8 */
>>>> +        .config = (SMPConfiguration) { T, 8, F, 0, F, 0, F, 0, T, 2, F, 0 },
>>>> +        .should_be_valid = true,
>>>> +        .expect = (CpuTopology) { 8, 1, 1, 4, 2, 8 },
>>>> +    }, {
>>>> +        /* config: -smp 8,maxcpus=16
>>>> +         * expect: cpus=8,sockets=1,dies=1,cores=16,threads=1,maxcpus=16 */
>>>> +        .config = (SMPConfiguration) { T, 8, F, 0, F, 0, F, 0, F, 0, T, 16 },
>>>> +        .should_be_valid = true,
>>>> +        .expect = (CpuTopology) { 8, 1, 1, 16, 1, 16 },
>>>> +    }, {
>>>> +        /* config: -smp sockets=2,cores=4
>>>> +         * expect: cpus=8,sockets=2,dies=1,cores=4,threads=1,maxcpus=8 */
>>>> +        .config = (SMPConfiguration) { F, 0, T, 2, F, 0, T, 4, F, 0, F, 0 },
>>>> +        .should_be_valid = true,
>>>> +        .expect = (CpuTopology) { 8, 2, 1, 4, 1, 8 },
>>>> +    }, {
>>>> +        /* config: -smp sockets=2,threads=2
>>>> +         * expect: cpus=4,sockets=2,dies=1,cores=1,threads=2,maxcpus=4 */
>>>> +        .config = (SMPConfiguration) { F, 0, T, 2, F, 0, F, 0, T, 2, F, 0 },
>>>> +        .should_be_valid = true,
>>>> +        .expect = (CpuTopology) { 4, 2, 1, 1, 2, 4 },
>>>> +    }, {
>>>> +        /* config: -smp sockets=2,maxcpus=16
>>>> +         * expect: cpus=16,sockets=2,dies=1,cores=8,threads=1,maxcpus=16 */
>>>> +        .config = (SMPConfiguration) { F, 0, T, 2, F, 0, F, 0, F, 0, T, 16 },
>>>> +        .should_be_valid = true,
>>>> +        .expect = (CpuTopology) { 16, 2, 1, 8, 1, 16 },
>>>> +    }, {
>>>> +        /* config: -smp cores=4,threads=2
>>>> +         * expect: cpus=8,sockets=1,dies=1,cores=4,threads=2,maxcpus=8 */
>>>> +        .config = (SMPConfiguration) { F, 0, F, 0, F, 0, T, 4, T, 2, F, 0 },
>>>> +        .should_be_valid = true,
>>>> +        .expect = (CpuTopology) { 8, 1, 1, 4, 2, 8 },
>>>> +    }, {
>>>> +        /* config: -smp cores=4,maxcpus=16
>>>> +         * expect: cpus=16,sockets=4,dies=1,cores=4,threads=1,maxcpus=16 */
>>>> +        .config = (SMPConfiguration) { F, 0, F, 0, F, 0, T, 4, F, 0, T, 16 },
>>>> +        .should_be_valid = true,
>>>> +        .expect = (CpuTopology) { 16, 4, 1, 4, 1, 16 },
>>>> +    }, {
>>>> +        /* config: -smp threads=2,maxcpus=16
>>>> +         * expect: cpus=16,sockets=1,dies=1,cores=8,threads=2,maxcpus=16 */
>>>> +        .config = (SMPConfiguration) { F, 0, F, 0, F, 0, F, 0, T, 2, T, 16 },
>>>> +        .should_be_valid = true,
>>>> +        .expect = (CpuTopology) { 16, 1, 1, 8, 2, 16 },
>>>> +    }, {
>>>> +        /* config: -smp 8,sockets=2,cores=4
>>>> +         * expect: cpus=8,sockets=2,dies=1,cores=4,threads=1,maxcpus=8 */
>>>> +        .config = (SMPConfiguration) { T, 8, T, 2, F, 0, T, 4, F, 0, F, 0 },
>>>> +        .should_be_valid = true,
>>>> +        .expect = (CpuTopology) { 8, 2, 1, 4, 1, 8 },
>>>> +    }, {
>>>> +        /* config: -smp 8,sockets=2,threads=2
>>>> +         * expect: cpus=8,sockets=2,dies=1,cores=2,threads=2,maxcpus=8 */
>>>> +        .config = (SMPConfiguration) { T, 8, T, 2, F, 0, F, 0, T, 2, F, 0 },
>>>> +        .should_be_valid = true,
>>>> +        .expect = (CpuTopology) { 8, 2, 1, 2, 2, 8 },
>>>> +    }, {
>>>> +        /* config: -smp 8,sockets=2,maxcpus=16
>>>> +         * expect: cpus=8,sockets=2,dies=1,cores=8,threads=1,maxcpus=16 */
>>>> +        .config = (SMPConfiguration) { T, 8, T, 2, F, 0, F, 0, F, 0, T, 16 },
>>>> +        .should_be_valid = true,
>>>> +        .expect = (CpuTopology) { 8, 2, 1, 8, 1, 16 },
>>>> +    }, {
>>>> +        /* config: -smp 8,cores=4,threads=2
>>>> +         * expect: cpus=8,sockets=1,dies=1,cores=4,threads=2,maxcpus=8 */
>>>> +        .config = (SMPConfiguration) { T, 8, F, 0, F, 0, T, 4, T, 2, F, 0 },
>>>> +        .should_be_valid = true,
>>>> +        .expect = (CpuTopology) { 8, 1, 1, 4, 2, 8 },
>>>> +    }, {
>>>> +        /* config: -smp 8,cores=4,maxcpus=16
>>>> +         * expect: cpus=8,sockets=4,dies=1,cores=4,threads=1,maxcpus=16 */
>>>> +        .config = (SMPConfiguration) { T, 8, F, 0, F, 0, T, 4, F, 0, T, 16 },
>>>> +        .should_be_valid = true,
>>>> +        .expect = (CpuTopology) { 8, 4, 1, 4, 1, 16 },
>>>> +    }, {
>>>> +        /* config: -smp 8,threads=2,maxcpus=16
>>>> +         * expect: cpus=8,sockets=1,dies=1,cores=8,threads=2,maxcpus=16 */
>>>> +        .config = (SMPConfiguration) { T, 8, F, 0, F, 0, F, 0, T, 2, T, 16 },
>>>> +        .should_be_valid = true,
>>>> +        .expect = (CpuTopology) { 8, 1, 1, 8, 2, 16 },
>>>> +    }, {
>>>> +        /* config: -smp sockets=2,cores=4,threads=2
>>>> +         * expect: cpus=16,sockets=2,dies=1,cores=4,threads=2,maxcpus=16 */
>>>> +        .config = (SMPConfiguration) { F, 0, T, 2, F, 0, T, 4, T, 2, F, 0 },
>>>> +        .should_be_valid = true,
>>>> +        .expect = (CpuTopology) { 16, 2, 1, 4, 2, 16 },
>>>> +    }, {
>>>> +        /* config: -smp sockets=2,cores=4,maxcpus=16
>>>> +         * expect: cpus=16,sockets=2,dies=1,cores=4,threads=2,maxcpus=16 */
>>>> +        .config = (SMPConfiguration) { F, 0, T, 2, F, 0, T, 4, F, 0, T, 16 },
>>>> +        .should_be_valid = true,
>>>> +        .expect = (CpuTopology) { 16, 2, 1, 4, 2, 16 },
>>>> +    }, {
>>>> +        /* config: -smp sockets=2,threads=2,maxcpus=16
>>>> +         * expect: cpus=16,sockets=2,dies=1,cores=4,threads=2,maxcpus=16 */
>>>> +        .config = (SMPConfiguration) { F, 0, T, 2, F, 0, F, 0, T, 2, T, 16 },
>>>> +        .should_be_valid = true,
>>>> +        .expect = (CpuTopology) { 16, 2, 1, 4, 2, 16 },
>>>> +    }, {
>>>> +        /* config: -smp cores=4,threads=2,maxcpus=16
>>>> +         * expect: cpus=16,sockets=2,dies=1,cores=4,threads=2,maxcpus=16 */
>>>> +        .config = (SMPConfiguration) { F, 0, F, 0, F, 0, T, 4, T, 2, T, 16 },
>>>> +        .should_be_valid = true,
>>>> +        .expect = (CpuTopology) { 16, 2, 1, 4, 2, 16 },
>>>> +    }, {
>>>> +        /* config: -smp 8,sockets=2,cores=4,threads=1
>>>> +         * expect: cpus=8,sockets=2,dies=1,cores=4,threads=1,maxcpus=8 */
>>>> +        .config = (SMPConfiguration) { T, 8, T, 2, F, 0, T, 4, T, 1, F, 0 },
>>>> +        .should_be_valid = true,
>>>> +        .expect = (CpuTopology) { 8, 2, 1, 4, 1, 8 },
>>>> +    }, {
>>>> +        /* config: -smp 8,sockets=2,cores=4,maxcpus=16
>>>> +         * expect: cpus=8,sockets=2,dies=1,cores=4,threads=2,maxcpus=16 */
>>>> +        .config = (SMPConfiguration) { T, 8, T, 2, F, 0, T, 4, F, 0, T, 16 },
>>>> +        .should_be_valid = true,
>>>> +        .expect = (CpuTopology) { 8, 2, 1, 4, 2, 16 },
>>>> +    }, {
>>>> +        /* config: -smp 8,sockets=2,threads=2,maxcpus=16
>>>> +         * expect: cpus=8,sockets=2,dies=1,cores=4,threads=2,maxcpus=16 */
>>>> +        .config = (SMPConfiguration) { T, 8, T, 2, F, 0, F, 0, T, 2, T, 16 },
>>>> +        .should_be_valid = true,
>>>> +        .expect = (CpuTopology) { 8, 2, 1, 4, 2, 16 },
>>>> +    }, {
>>>> +        /* config: -smp 8,cores=4,threads=2,maxcpus=16
>>>> +         * expect: cpus=8,sockets=2,dies=1,cores=4,threads=2,maxcpus=16 */
>>>> +        .config = (SMPConfiguration) { T, 8, F, 0, F, 0, T, 4, T, 2, T, 16 },
>>>> +        .should_be_valid = true,
>>>> +        .expect = (CpuTopology) { 8, 2, 1, 4, 2, 16 },
>>>> +    }, {
>>>> +        /* config: -smp sockets=2,cores=4,threads=2,maxcpus=16
>>>> +         * expect: -smp 16,sockets=2,dies=1,cores=4,threads=2,maxcpus=16 */
>>>> +        .config = (SMPConfiguration) { F, 0, T, 2, F, 0, T, 4, T, 2, T, 16 },
>>>> +        .should_be_valid = true,
>>>> +        .expect = (CpuTopology) { 16, 2, 1, 4, 2, 16 },
>>>> +    }, {
>>>> +        /* config: -smp 8,sockets=2,cores=4,threads=2,maxcpus=16
>>>> +         * expect: cpus=8,sockets=2,dies=1,cores=4,threads=2,maxcpus=16 */
>>>> +        .config = (SMPConfiguration) { T, 8, T, 2, F, 0, T, 4, T, 2, T, 16 },
>>>> +        .should_be_valid = true,
>>>> +        .expect = (CpuTopology) { 8, 2, 1, 4, 2, 16 },
>>>> +    }, {
>>>> +        /* config: -smp 8,sockets=2,dies=1,cores=4,threads=2,maxcpus=16
>>>> +         * expect: cpus=8,sockets=2,dies=1,cores=4,threads=2,maxcpus=16 */
>>>> +        .config = (SMPConfiguration) { T, 8, T, 2, T, 1, T, 4, T, 2, T, 16 },
>>>> +        .should_be_valid = true,
>>>> +        .expect = (CpuTopology) { 8, 2, 1, 4, 2, 16 },
>>>> +    }, {
>>>> +        /* config: -smp 0
>>>> +         * expect: error, "anything=0" is not allowed */
>>>> +        .config = (SMPConfiguration) { T, 0, F, 0, F, 0, F, 0, F, 0, F, 0 },
>>>> +        .should_be_valid = false,
>>>> +    }, {
>>>> +        /* config: -smp 8,sockets=0
>>>> +         * expect: error, "anything=0" is not allowed */
>>>> +        .config = (SMPConfiguration) { T, 8, T, 0, F, 0, F, 0, F, 0, F, 0 },
>>>> +        .should_be_valid = false,
>>>> +    }, {
>>>> +        /* config: -smp 8,sockets=2,dies=0
>>>> +         * expect: error, "anything=0" is not allowed */
>>>> +        .config = (SMPConfiguration) { T, 0, T, 2, T, 0, F, 0, F, 0, F, 0 },
>>>> +        .should_be_valid = false,
>>>> +    }, {
>>>> +        /* config: -smp 8,sockets=2,dies=1,cores=0
>>>> +         * expect: error, "anything=0" is not allowed */
>>>> +        .config = (SMPConfiguration) { T, 8, T, 2, T, 1, T, 0, F, 0, F, 0 },
>>>> +        .should_be_valid = false,
>>>> +    }, {
>>>> +        /* config: -smp 8,sockets=2,dies=1,cores=4,threads=0
>>>> +         * expect: error, "anything=0" is not allowed */
>>>> +        .config = (SMPConfiguration) { T, 8, T, 2, T, 1, T, 4, T, 0, F, 0 },
>>>> +        .should_be_valid = false,
>>>> +    }, {
>>>> +        /* config: -smp 8,sockets=2,dies=1,cores=4,threads=2,maxcpus=0
>>>> +         * expect: error, "anything=0" is not allowed */
>>>> +        .config = (SMPConfiguration) { T, 8, T, 2, T, 1, T, 4, T, 2, T, 0 },
>>>> +        .should_be_valid = false,
>>>> +    }, {
>>>> +        /* config: -smp 8,dies=2
>>>> +         * expect: error, multi-dies not supported */
>>>> +        .config = (SMPConfiguration) { T, 8, F, 0, T, 2, F, 0, F, 0, F, 0 },
>>>> +        .should_be_valid = false,
>>>> +    }, {
>>>> +        /* config: -smp 8,sockets=2,cores=8
>>>> +         * expect: error, sum (16) != max_cpus (8) */
>>>> +        .config = (SMPConfiguration) { T, 8, T, 2, F, 0, T, 4, T, 2, F, 0 },
>>>> +        .should_be_valid = false,
>>>> +    }, {
>>>> +        /* config: -smp 8,sockets=2,cores=5,threads=2,maxcpus=16
>>>> +         * expect: error, sum (20) != max_cpus (16) */
>>>> +        .config = (SMPConfiguration) { F, 0, T, 3, F, 0, T, 5, T, 1, T, 16 },
>>>> +        .should_be_valid = false,
>>>> +    }, {
>>>> +        /* config: -smp 16,maxcpus=12
>>>> +         * expect: error, sum (12) < smp_cpus (16) */
>>>> +        .config = (SMPConfiguration) { T, 16, F, 0, F, 0, F, 0, F, 0, T, 12 },
>>>> +        .should_be_valid = false,
>>>> +    },
>>>> +};
>>>> +
>>>> +static struct SMPTestData prefer_cores_support_dies[] = {
>>>> +    {
>>>> +        /* config: -smp dies=2
>>>> +         * expect: cpus=2,sockets=1,dies=2,cores=1,threads=1,maxcpus=2 */
>>>> +        .config = (SMPConfiguration) { F, 0, F, 0, T, 2, F, 0, F, 0, F, 0 },
>>>> +        .should_be_valid = true,
>>>> +        .expect = (CpuTopology) { 2, 1, 2, 1, 1, 2 },
>>>> +    }, {
>>>> +        /* config: -smp 16,dies=2
>>>> +         * expect: cpus=16,sockets=1,dies=2,cores=8,threads=1,maxcpus=16 */
>>>> +        .config = (SMPConfiguration) { T, 16, F, 0, T, 2, F, 0, F, 0, F, 0 },
>>>> +        .should_be_valid = true,
>>>> +        .expect = (CpuTopology) { 16, 1, 2, 8, 1, 16 },
>>>> +    }, {
>>>> +        /* config: -smp sockets=2,dies=2
>>>> +         * expect: cpus=4,sockets=2,dies=2,cores=1,threads=1,maxcpus=4 */
>>>> +        .config = (SMPConfiguration) { F, 0, T, 2, T, 2, F, 0, F, 0, F, 0 },
>>>> +        .should_be_valid = true,
>>>> +        .expect = (CpuTopology) { 4, 2, 2, 1, 1, 4 },
>>>> +    }, {
>>>> +        /* config: -smp dies=2,cores=4
>>>> +         * expect: cpus=8,sockets=1,dies=2,cores=4,threads=1,maxcpus=8 */
>>>> +        .config = (SMPConfiguration) { F, 0, F, 0, T, 2, T, 4, F, 0, F, 0 },
>>>> +        .should_be_valid = true,
>>>> +        .expect = (CpuTopology) { 8, 1, 2, 4, 1, 8 },
>>>> +    }, {
>>>> +        /* config: -smp dies=2,threads=2
>>>> +         * expect: cpus=4,sockets=1,dies=2,cores=1,threads=2,maxcpus=4 */
>>>> +        .config = (SMPConfiguration) { F, 0, F, 0, T, 2, F, 0, T, 2, F, 0 },
>>>> +        .should_be_valid = true,
>>>> +        .expect = (CpuTopology) { 4, 1, 2, 1, 2, 4 },
>>>> +    }, {
>>>> +        /* config: -smp dies=2,maxcpus=32
>>>> +         * expect: cpus=32,sockets=1,dies=2,cores=16,threads=1,maxcpus=32 */
>>>> +        .config = (SMPConfiguration) { F, 0, F, 0, T, 2, F, 0, F, 0, T, 32 },
>>>> +        .should_be_valid = true,
>>>> +        .expect = (CpuTopology) { 32, 1, 2, 16, 1, 32 },
>>>> +    }, {
>>>> +        /* config: -smp 16,sockets=2,dies=2
>>>> +         * expect: cpus=16,sockets=2,dies=2,cores=4,threads=1,maxcpus=16 */
>>>> +        .config = (SMPConfiguration) { T, 16, T, 2, T, 2, F, 0, F, 0, F, 0 },
>>>> +        .should_be_valid = true,
>>>> +        .expect = (CpuTopology) { 16, 2, 2, 4, 1, 16 },
>>>> +    }, {
>>>> +        /* config: -smp 16,dies=2,cores=4
>>>> +         * expect: cpus=16,sockets=2,dies=2,cores=4,threads=1,maxcpus=16 */
>>>> +        .config = (SMPConfiguration) { T, 16, F, 0, T, 2, T, 4, F, 0, F, 0 },
>>>> +        .should_be_valid = true,
>>>> +        .expect = (CpuTopology) { 16, 2, 2, 4, 1, 16 },
>>>> +    }, {
>>>> +        /* config: -smp 16,dies=2,threads=2
>>>> +         * expect: cpus=16,sockets=1,dies=2,cores=4,threads=2,maxcpus=16 */
>>>> +        .config = (SMPConfiguration) { T, 16, F, 0, T, 2, F, 0, T, 2, F, 0 },
>>>> +        .should_be_valid = true,
>>>> +        .expect = (CpuTopology) { 16, 1, 2, 4, 2, 16 },
>>>> +    }, {
>>>> +        /* config: -smp 16,dies=2,maxcpus=32
>>>> +         * expect: cpus=16,sockets=1,dies=2,cores=16,threads=1,maxcpus=32 */
>>>> +        .config = (SMPConfiguration) { T, 16, F, 0, T, 2, F, 0, F, 0, T, 32 },
>>>> +        .should_be_valid = true,
>>>> +        .expect = (CpuTopology) { 16, 1, 2, 16, 1, 32 },
>>>> +    }, {
>>>> +        /* config: -smp sockets=2,dies=2,cores=4
>>>> +         * expect: cpus=16,sockets=2,dies=2,cores=4,threads=1,maxcpus=16 */
>>>> +        .config = (SMPConfiguration) { F, 0, T, 2, T, 2, T, 4, F, 0, F, 0 },
>>>> +        .should_be_valid = true,
>>>> +        .expect = (CpuTopology) { 16, 2, 2, 4, 1, 16 },
>>>> +    }, {
>>>> +        /* config: -smp sockets=2,dies=2,threads=2
>>>> +         * expect: cpus=8,sockets=2,dies=2,cores=1,threads=2,maxcpus=8 */
>>>> +        .config = (SMPConfiguration) { F, 0, T, 2, T, 2, F, 0, T, 2, F, 0 },
>>>> +        .should_be_valid = true,
>>>> +        .expect = (CpuTopology) { 8, 2, 2, 1, 2, 8 },
>>>> +    }, {
>>>> +        /* config: -smp sockets=2,dies=2,maxcpus=32
>>>> +         * expect: cpus=32,sockets=2,dies=2,cores=8,threads=1,maxcpus=32 */
>>>> +        .config = (SMPConfiguration) { F, 0, T, 2, T, 2, F, 0, F, 0, T, 32 },
>>>> +        .should_be_valid = true,
>>>> +        .expect = (CpuTopology) { 32, 2, 2, 8, 1, 32 },
>>>> +    }, {
>>>> +        /* config: -smp dies=2,cores=4,threads=2
>>>> +         * expect: cpus=16,sockets=1,dies=2,cores=4,threads=2,maxcpus=16 */
>>>> +        .config = (SMPConfiguration) { F, 0, F, 0, T, 2, T, 4, T, 2, F, 0 },
>>>> +        .should_be_valid = true,
>>>> +        .expect = (CpuTopology) { 16, 1, 2, 4, 2, 16 },
>>>> +    }, {
>>>> +        /* config: -smp dies=2,cores=4,maxcpus=32
>>>> +         * expect: cpus=32,sockets=4,dies=2,cores=4,threads=1,maxcpus=32 */
>>>> +        .config = (SMPConfiguration) { F, 0, F, 0, T, 2, T, 4, F, 0, T, 32 },
>>>> +        .should_be_valid = true,
>>>> +        .expect = (CpuTopology) { 32, 4, 2, 4, 1, 32 },
>>>> +    }, {
>>>> +        /* config: -smp dies=2,threads=2,maxcpus=32
>>>> +         * expect: cpus=32,sockets=1,dies=2,cores=8,threads=2,maxcpus=32 */
>>>> +        .config = (SMPConfiguration) { F, 0, F, 0, T, 2, F, 0, T, 2, T, 32 },
>>>> +        .should_be_valid = true,
>>>> +        .expect = (CpuTopology) { 32, 1, 2, 8, 2, 32 },
>>>> +    }, {
>>>> +        /* config: -smp 16,sockets=2,dies=2,cores=4
>>>> +         * expect: cpus=16,sockets=2,dies=2,cores=4,threads=1,maxcpus=16 */
>>>> +        .config = (SMPConfiguration) { T, 16, T, 2, T, 2, T, 4, F, 0, F, 0 },
>>>> +        .should_be_valid = true,
>>>> +        .expect = (CpuTopology) { 16, 2, 2, 4, 1, 16 },
>>>> +    }, {
>>>> +        /* config: -smp 16,sockets=2,dies=2,threads=2
>>>> +         * expect: cpus=16,sockets=2,dies=2,cores=2,threads=2,maxcpus=16 */
>>>> +        .config = (SMPConfiguration) { T, 16, T, 2, T, 2, F, 0, T, 2, F, 0 },
>>>> +        .should_be_valid = true,
>>>> +        .expect = (CpuTopology) { 16, 2, 2, 2, 2, 16 },
>>>> +    }, {
>>>> +        /* config: -smp 16,sockets=2,dies=2,maxcpus=32
>>>> +         * expect: cpus=16,sockets=2,dies=2,cores=8,threads=1,maxcpus=32 */
>>>> +        .config = (SMPConfiguration) { T, 16, T, 2, T, 2, F, 0, F, 0, T, 32 },
>>>> +        .should_be_valid = true,
>>>> +        .expect = (CpuTopology) { 16, 2, 2, 8, 1, 32 },
>>>> +    }, {
>>>> +        /* config: -smp 16,dies=2,cores=4,threads=2
>>>> +         * expect: cpus=16,sockets=1,dies=2,cores=4,threads=2,maxcpus=16 */
>>>> +        .config = (SMPConfiguration) { T, 16, F, 0, T, 2, T, 4, T, 2, F, 0 },
>>>> +        .should_be_valid = true,
>>>> +        .expect = (CpuTopology) { 16, 1, 2, 4, 2, 16 },
>>>> +    }, {
>>>> +        /* config: -smp 16,dies=2,cores=4,maxcpus=32
>>>> +         * expect: cpus=16,sockets=4,dies=2,cores=4,threads=1,maxcpus=32 */
>>>> +        .config = (SMPConfiguration) { T, 16, F, 0, T, 2, T, 4, F, 0, T, 32 },
>>>> +        .should_be_valid = true,
>>>> +        .expect = (CpuTopology) { 16, 4, 2, 4, 1, 32 },
>>>> +    }, {
>>>> +        /* config: -smp 16,dies=2,threads=2,maxcpus=32
>>>> +         * expect: cpus=16,sockets=1,dies=2,cores=8,threads=2,maxcpus=32 */
>>>> +        .config = (SMPConfiguration) { T, 16, F, 0, T, 2, F, 0, T, 2, T, 32 },
>>>> +        .should_be_valid = true,
>>>> +        .expect = (CpuTopology) { 16, 1, 2, 8, 2, 32 },
>>>> +    }, {
>>>> +        /* config: -smp sockets=2,dies=2,cores=4,threads=2
>>>> +         * expect: cpus=32,sockets=2,dies=2,cores=4,threads=2,maxcpus=32 */
>>>> +        .config = (SMPConfiguration) { F, 0, T, 2, T, 2, T, 4, T, 2, F, 0 },
>>>> +        .should_be_valid = true,
>>>> +        .expect = (CpuTopology) { 32, 2, 2, 4, 2, 32 },
>>>> +    }, {
>>>> +        /* config: -smp sockets=2,dies=2,cores=4,maxcpus=32
>>>> +         * expect: cpus=32,sockets=2,dies=2,cores=4,threads=2,maxcpus=32 */
>>>> +        .config = (SMPConfiguration) { F, 0, T, 2, T, 2, T, 4, F, 0, T, 32 },
>>>> +        .should_be_valid = true,
>>>> +        .expect = (CpuTopology) { 32, 2, 2, 4, 2, 32 },
>>>> +    }, {
>>>> +        /* config: -smp sockets=2,dies=2,threads=2,maxcpus=32
>>>> +         * expect: cpus=32,sockets=2,dies=2,cores=4,threads=2,maxcpus=32 */
>>>> +        .config = (SMPConfiguration) { F, 0, T, 2, T, 2, F, 0, T, 2, T, 32 },
>>>> +        .should_be_valid = true,
>>>> +        .expect = (CpuTopology) { 32, 2, 2, 4, 2, 32 },
>>>> +    }, {
>>>> +        /* config: -smp dies=2,cores=4,threads=2,maxcpus=32
>>>> +         * expect: cpus=32,sockets=2,dies=2,cores=4,threads=2,maxcpus=32 */
>>>> +        .config = (SMPConfiguration) { F, 0, F, 0, T, 2, T, 4, T, 2, T, 32 },
>>>> +        .should_be_valid = true,
>>>> +        .expect = (CpuTopology) { 32, 2, 2, 4, 2, 32 },
>>>> +    }, {
>>>> +        /* config: -smp 16,sockets=2,dies=2,cores=4,threads=1
>>>> +         * expect: cpus=16,sockets=2,dies=2,cores=4,threads=1,maxcpus=16 */
>>>> +        .config = (SMPConfiguration) { T, 16, T, 2, T, 2, T, 4, T, 1, F, 0 },
>>>> +        .should_be_valid = true,
>>>> +        .expect = (CpuTopology) { 16, 2, 2, 4, 1, 16 },
>>>> +    }, {
>>>> +        /* config: -smp 16,sockets=2,dies=2,cores=4,maxcpus=32
>>>> +         * expect: cpus=16,sockets=2,dies=2,cores=4,threads=2,maxcpus=32 */
>>>> +        .config = (SMPConfiguration) { T, 16, T, 2, T, 2, T, 4, F, 0, T, 32 },
>>>> +        .should_be_valid = true,
>>>> +        .expect = (CpuTopology) { 16, 2, 2, 4, 2, 32 },
>>>> +    }, {
>>>> +        /* config: -smp 16,sockets=2,dies=2,threads=2,maxcpus=32
>>>> +         * expect: cpus=16,sockets=2,dies=2,cores=4,threads=2,maxcpus=32 */
>>>> +        .config = (SMPConfiguration) { T, 16, T, 2, T, 2, F, 0, T, 2, T, 32 },
>>>> +        .should_be_valid = true,
>>>> +        .expect = (CpuTopology) { 16, 2, 2, 4, 2, 32 },
>>>> +    }, {
>>>> +        /* config: -smp 16,dies=2,cores=4,threads=2,maxcpus=32
>>>> +         * expect: cpus=16,sockets=2,dies=2,cores=4,threads=2,maxcpus=32 */
>>>> +        .config = (SMPConfiguration) { T, 16, F, 0, T, 2, T, 4, T, 2, T, 32 },
>>>> +        .should_be_valid = true,
>>>> +        .expect = (CpuTopology) { 16, 2, 2, 4, 2, 32 },
>>>> +    }, {
>>>> +        /* config: -smp sockets=2,dies=2,cores=4,threads=2,maxcpus=32
>>>> +         * expect: -smp 32,sockets=2,dies=2,cores=4,threads=2,maxcpus=32 */
>>>> +        .config = (SMPConfiguration) { F, 0, T, 2, T, 2, T, 4, T, 2, T, 32 },
>>>> +        .should_be_valid = true,
>>>> +        .expect = (CpuTopology) { 32, 2, 2, 4, 2, 32 },
>>>> +    }, {
>>>> +        /* config: -smp 16,sockets=2,dies=2,cores=4,threads=2,maxcpus=32
>>>> +         * expect: cpus=16,sockets=2,dies=2,cores=4,threads=2,maxcpus=32 */
>>>> +        .config = (SMPConfiguration) { T, 16, T, 2, T, 2, T, 4, T, 2, T, 32 },
>>>> +        .should_be_valid = true,
>>>> +        .expect = (CpuTopology) { 16, 2, 2, 4, 2, 32 },
>>>> +    },
>>>> +};
>>>> +
>>>> +static char *get_config_info(SMPConfiguration *config)
>>>> +{
>>>> +    return g_strdup_printf(
>>>> +        "(SMPConfiguration) {\n"
>>>> +        "    .has_cpus    = %5s, cpus    = %ld,\n"
>>>> +        "    .has_sockets = %5s, sockets = %ld,\n"
>>>> +        "    .has_dies    = %5s, dies    = %ld,\n"
>>>> +        "    .has_cores   = %5s, cores   = %ld,\n"
>>>> +        "    .has_threads = %5s, threads = %ld,\n"
>>>> +        "    .has_maxcpus = %5s, maxcpus = %ld,\n"
>>>> +        "}",
>>>> +        config->has_cpus ? "true" : "false", config->cpus,
>>>> +        config->has_sockets ? "true" : "false", config->sockets,
>>>> +        config->has_dies ? "true" : "false", config->dies,
>>>> +        config->has_cores ? "true" : "false", config->cores,
>>>> +        config->has_threads ? "true" : "false", config->threads,
>>>> +        config->has_maxcpus ? "true" : "false", config->maxcpus);
>>>> +}
>>>> +
>>>> +static char *get_topo_info(CpuTopology *topo)
>>>> +{
>>>> +    return g_strdup_printf(
>>>> +        "(CpuTopology) {\n"
>>>> +        "    .cpus     = %u,\n"
>>>> +        "    .sockets  = %u,\n"
>>>> +        "    .dies     = %u,\n"
>>>> +        "    .cores    = %u,\n"
>>>> +        "    .threads  = %u,\n"
>>>> +        "    .max_cpus = %u,\n"
>>>> +        "}",
>>>> +        topo->cpus, topo->sockets, topo->dies,
>>>> +        topo->cores, topo->threads, topo->max_cpus);
>>>> +}
>>>> +
>>>> +static void check_smp_parse(MachineState *ms, SMPTestData *data)
>>>> +{
>>>> +    SMPConfiguration *config = &data->config;
>>>> +    CpuTopology *expect = &data->expect;
>>>> +    g_autofree char *config_info = NULL;
>>>> +    g_autofree char *expect_info = NULL;
>>>> +    g_autofree char *result_info = NULL;
>>>> +    Error *err = NULL;
>>>> +
>>>> +    /* call the generic parser smp_parse() in hw/core/machine-smp.c */
>>>> +    smp_parse(ms, config, &err);
>>>> +
>>>> +    if (data->should_be_valid) {
>>>> +        if ((err == NULL) &&
>>>> +            (ms->smp.cpus == expect->cpus) &&
>>>> +            (ms->smp.sockets == expect->sockets) &&
>>>> +            (ms->smp.dies == expect->dies) &&
>>>> +            (ms->smp.cores == expect->cores) &&
>>>> +            (ms->smp.threads == expect->threads) &&
>>>> +            (ms->smp.max_cpus == expect->max_cpus)) {
>>>> +            return;
>>>> +        }
>>>> +
>>>> +        config_info = get_config_info(config);
>>>> +        expect_info = get_topo_info(expect);
>>>> +
>>>> +        if (err != NULL) {
>>>> +            g_printerr("Check smp_parse failed:\n"
>>>> +                       "config: %s\n"
>>>> +                       "expect: %s\n"
>>>> +                       "should_be_valid: yes\n\n"
>>>> +                       "result_is_valid: no\n"
>>>> +                       "error_msg: %s\n",
>>>> +                       config_info, expect_info, error_get_pretty(err));
>>>> +            error_free(err);
>>>> +        } else {
>>>> +            result_info = get_topo_info(&ms->smp);
>>>> +            g_printerr("Check smp_parse failed:\n"
>>>> +                       "config: %s\n"
>>>> +                       "expect: %s\n"
>>>> +                       "should_be_valid: yes\n\n"
>>>> +                       "result_is_valid: yes\n"
>>>> +                       "result: %s\n",
>>>> +                       config_info, expect_info, result_info);
>>>> +        }
>>>> +    } else {
>>>> +        if (err != NULL) {
>>>> +            error_free(err);
>>>> +            return;
>>>> +        }
>>>> +
>>>> +        config_info = get_config_info(config);
>>>> +        result_info = get_topo_info(&ms->smp);
>>>> +
>>>> +        g_printerr("Check smp_parse failed:\n"
>>>> +                   "config: %s\n"
>>>> +                   "should_be_valid: no\n\n"
>>>> +                   "result_is_valid: yes\n"
>>>> +                   "result: %s\n",
>>>> +                   config_info, result_info);
>>>> +    }
>>>> +
>>>> +    abort();
>>>> +}
>>>> +
>>>> +static void smp_prefer_sockets_test(void)
>>>> +{
>>>> +    Object *obj = object_new(TYPE_MACHINE);
>>>> +    MachineState *ms = MACHINE(obj);
>>>> +    MachineClass *mc = MACHINE_GET_CLASS(obj);
>>>> +    int i;
>>>> +
>>>> +    /* make sure that we have created the object */
>>>> +    g_assert_nonnull(ms);
>>>> +    g_assert_nonnull(mc);
>>>> +
>>>> +    mc->smp_prefer_sockets = true;
>>>> +
>>>> +    /* test cases when multi-dies are not supported */
>>>> +    mc->smp_dies_supported = false;
>>>> +    for (i = 0; i < ARRAY_SIZE(prefer_sockets); i++) {
>>>> +        check_smp_parse(ms, &prefer_sockets[i]);
>>>> +    }
>>>> +
>>>> +    /* test cases when multi-dies are supported */
>>>> +    mc->smp_dies_supported = true;
>>>> +    for (i = 0; i < ARRAY_SIZE(prefer_sockets_support_dies); i++) {
>>>> +        check_smp_parse(ms, &prefer_sockets_support_dies[i]);
>>>> +    }
>>>> +
>>>> +    object_unref(obj);
>>>> +}
>>>> +
>>>> +static void smp_prefer_cores_test(void)
>>>> +{
>>>> +    Object *obj = object_new(TYPE_MACHINE);
>>>> +    MachineState *ms = MACHINE(obj);
>>>> +    MachineClass *mc = MACHINE_GET_CLASS(obj);
>>>> +    int i;
>>>> +
>>>> +    /* make sure that we have created the object */
>>>> +    g_assert_nonnull(ms);
>>>> +    g_assert_nonnull(mc);
>>>> +
>>>> +    mc->smp_prefer_sockets = false;
>>>> +
>>>> +    /* test cases when multi-dies are not supported */
>>>> +    mc->smp_dies_supported = false;
>>>> +    for (i = 0; i < ARRAY_SIZE(prefer_cores); i++) {
>>>> +        check_smp_parse(ms, &prefer_cores[i]);
>>>> +    }
>>>> +
>>>> +    /* test cases when multi-dies are supported */
>>>> +    mc->smp_dies_supported = true;
>>>> +    for (i = 0; i < ARRAY_SIZE(prefer_cores_support_dies); i++) {
>>>> +        check_smp_parse(ms, &prefer_cores_support_dies[i]);
>>>> +    }
>>>> +
>>>> +    object_unref(obj);
>>>> +}
>>>> +
>>>> +int main(int argc, char *argv[])
>>>> +{
>>>> +    g_test_init(&argc, &argv, NULL);
>>>> +
>>>> +    module_call_init(MODULE_INIT_QOM);
>>>> +    type_register_static(&smp_machine_info);
>>>> +
>>>> +    g_test_add_func("/test-smp-parse/prefer_sockets", smp_prefer_sockets_test);
>>>> +    g_test_add_func("/test-smp-parse/prefer_cores", smp_prefer_cores_test);
>>>> +
>>>> +    g_test_run();
>>>> +
>>>> +    return 0;
>>>> +}
>>>> -- 
>>>> 2.19.1
>>>>
>>> Besides the nits, it looks good to me.
>>>
>>> Reviewed-by: Andrew Jones <drjones@redhat.com>
>>>
>> Thanks,
>> Yanan
>> .
>>> .
> .



^ permalink raw reply	[flat|nested] 58+ messages in thread

* Re: [PATCH for-6.2 v2 10/11] machine: Split out the smp parsing code
  2021-07-22 13:07       ` Andrew Jones
@ 2021-07-22 14:29         ` wangyanan (Y)
  0 siblings, 0 replies; 58+ messages in thread
From: wangyanan (Y) @ 2021-07-22 14:29 UTC (permalink / raw)
  To: Andrew Jones
  Cc: Peter Maydell, Thomas Huth, Daniel P. Berrangé,
	Eduardo Habkost, Michael S . Tsirkin, wanghaibin.wang,
	Richard Henderson, qemu-devel, Greg Kurz, Halil Pasic,
	Igor Mammedov, yuzenghui, Paolo Bonzini, David Gibson

On 2021/7/22 21:07, Andrew Jones wrote:
> On Thu, Jul 22, 2021 at 02:24:03PM +0800, wangyanan (Y) wrote:
>> On 2021/7/20 1:20, Andrew Jones wrote:
>>> On Mon, Jul 19, 2021 at 11:20:42AM +0800, Yanan Wang wrote:
>>>> We are going to introduce an unit test for the parser smp_parse()
>>>> in hw/core/machine.c, but now machine.c is only built in softmmu.
>>>>
>>>> In order to solve the build dependency on the smp parsing code and
>>>> avoid building unrelated stuff for the unit tests, move the related
>>>> code from machine.c into a new common file, i.e., machine-smp.c.
>>>>
>>>> Signed-off-by: Yanan Wang <wangyanan55@huawei.com>
>>>> ---
>>>>    MAINTAINERS           |   1 +
>>>>    hw/core/machine-smp.c | 124 ++++++++++++++++++++++++++++++++++++++++++
>>>>    hw/core/machine.c     | 109 -------------------------------------
>>>>    hw/core/meson.build   |   1 +
>>>>    include/hw/boards.h   |   1 +
>>>>    5 files changed, 127 insertions(+), 109 deletions(-)
>>>>    create mode 100644 hw/core/machine-smp.c
>>>>
>>>> diff --git a/MAINTAINERS b/MAINTAINERS
>>>> index 9100f9a043..70633e3bf4 100644
>>>> --- a/MAINTAINERS
>>>> +++ b/MAINTAINERS
>>>> @@ -1626,6 +1626,7 @@ F: cpu.c
>>>>    F: hw/core/cpu.c
>>>>    F: hw/core/machine-qmp-cmds.c
>>>>    F: hw/core/machine.c
>>>> +F: hw/core/machine-smp.c
> I just noticed that the spacing in this change might not be right.
Right, will fix it.
>>>>    F: hw/core/null-machine.c
>>>>    F: hw/core/numa.c
>>>>    F: hw/cpu/cluster.c
>>>> diff --git a/hw/core/machine-smp.c b/hw/core/machine-smp.c
>>>> new file mode 100644
>>>> index 0000000000..6a00cfe44a
>>>> --- /dev/null
>>>> +++ b/hw/core/machine-smp.c
>>>> @@ -0,0 +1,124 @@
>>>> +/*
>>>> + * QEMU Machine (related to SMP configuration)
>>>> + *
>>>> + * Copyright (C) 2014 Red Hat Inc
>>>> + *
>>>> + * Authors:
>>>> + *   Marcel Apfelbaum <marcel.a@redhat.com>
>>> This header was obviously copy+pasted without being updated.
>> Yes, the header was kept unchanged.
>>
>> But actually I'm not completely sure which field should be updated. :)
>> Should I add "Copyright (C) 2021, Huawei, Inc." and also the authorship
>> "Yanan Wang <wangyanan55@huawei.com>" behind the existing ones
>> or just replace them?
> I see what you were attempting to do now. You were deriving this new work
> (a source file) from an existing work and you wanted to preserve the
> original copyright and authorship. It's not so simple with these types of
> projects though. In this case, smp_parse wasn't even part of the original
> machine.c file (it came over with commit 6f479566a87d). I think it's
> pretty common for these projects to just put whatever your preferred
> (or your employer's preferred) copyright/authorship on new files. So, I'd
> just replace the fields.
I see, will have some update.

Thanks,
Yanan
> I'm interested in what others have to say about this though.
>
> Thanks,
> drew
>
>
>>>> + *
>>>> + * This work is licensed under the terms of the GNU GPL, version 2 or later.
>>>> + * See the COPYING file in the top-level directory.
>>>> + */
>>>> +
>>>> +#include "qemu/osdep.h"
>>>> +#include "hw/boards.h"
>>>> +#include "qapi/error.h"
>>>> +
>>>> +/*
>>>> + * smp_parse - Generic function used to parse the given SMP configuration
>>>> + *
>>>> + * The topology parameters must be specified equal to or great than one
>>>> + * or just omitted, explicit configuration like "cpus=0" is not allowed.
>>>> + * The omitted parameters will be calculated based on the provided ones.
>>>> + *
>>>> + * maxcpus will default to the value of cpus if omitted and will be used
>>>> + * to compute the missing sockets/cores/threads. cpus will be calculated
>>>> + * from the computed parametrs if omitted.
>>>> + *
>>>> + * In calculation of omitted arch-netural sockets/cores/threads, we prefer
>>>> + * sockets over cores over threads before 6.2, while prefer cores over
>>>> + * sockets over threads since 6.2 on. The arch-specific dies will directly
>>>> + * default to 1 if omitted.
>>>> + */
>>>> +void smp_parse(MachineState *ms, SMPConfiguration *config, Error **errp)
>>>> +{
>>>> +    MachineClass *mc = MACHINE_GET_CLASS(ms);
>>>> +    unsigned cpus    = config->has_cpus ? config->cpus : 0;
>>>> +    unsigned sockets = config->has_sockets ? config->sockets : 0;
>>>> +    unsigned dies    = config->has_dies ? config->dies : 1;
>>>> +    unsigned cores   = config->has_cores ? config->cores : 0;
>>>> +    unsigned threads = config->has_threads ? config->threads : 0;
>>>> +    unsigned maxcpus = config->has_maxcpus ? config->maxcpus : 0;
>>>> +
>>>> +    if ((config->has_cpus && config->cpus == 0) ||
>>>> +        (config->has_sockets && config->sockets == 0) ||
>>>> +        (config->has_dies && config->dies == 0) ||
>>>> +        (config->has_cores && config->cores == 0) ||
>>>> +        (config->has_threads && config->threads == 0) ||
>>>> +        (config->has_maxcpus && config->maxcpus == 0)) {
>>>> +        error_setg(errp, "parameters must be equal to or greater than one"
>>>> +                   "if provided");
>>>> +        return;
>>>> +    }
>>>> +
>>>> +    if (!mc->smp_dies_supported && dies > 1) {
>>>> +        error_setg(errp, "dies not supported by this machine's CPU topology");
>>>> +        return;
>>>> +    }
>>>> +
>>>> +    maxcpus = maxcpus > 0 ? maxcpus : cpus;
>>>> +
>>>> +    /* prefer sockets over cores over threads before 6.2 */
>>>> +    if (mc->smp_prefer_sockets) {
>>>> +        if (sockets == 0) {
>>>> +            cores = cores > 0 ? cores : 1;
>>>> +            threads = threads > 0 ? threads : 1;
>>>> +            sockets = maxcpus / (dies * cores * threads);
>>>> +            sockets = sockets > 0 ? sockets : 1;
>>>> +        } else if (cores == 0) {
>>>> +            threads = threads > 0 ? threads : 1;
>>>> +            cores = maxcpus / (sockets * dies * threads);
>>>> +            cores = cores > 0 ? cores : 1;
>>>> +        } else if (threads == 0) {
>>>> +            threads = maxcpus / (sockets * dies * cores);
>>>> +            threads = threads > 0 ? threads : 1;
>>>> +        }
>>>> +    /* prefer cores over sockets over threads since 6.2 */
>>>> +    } else {
>>>> +        if (cores == 0) {
>>>> +            sockets = sockets > 0 ? sockets : 1;
>>>> +            threads = threads > 0 ? threads : 1;
>>>> +            cores = maxcpus / (sockets * dies * threads);
>>>> +            cores = cores > 0 ? cores : 1;
>>>> +        } else if (sockets == 0) {
>>>> +            threads = threads > 0 ? threads : 1;
>>>> +            sockets = maxcpus / (dies * cores * threads);
>>>> +            sockets = sockets > 0 ? sockets : 1;
>>>> +        } else if (threads == 0) {
>>>> +            threads = maxcpus / (sockets * dies * cores);
>>>> +            threads = threads > 0 ? threads : 1;
>>>> +        }
>>>> +    }
>>>> +
>>>> +    /* use the computed parameters to calculate the omitted cpus */
>>>> +    cpus = cpus > 0 ? cpus : sockets * dies * cores * threads;
>>>> +    maxcpus = maxcpus > 0 ? maxcpus : cpus;
>>>> +
>>>> +    if (sockets * dies * cores * threads != maxcpus) {
>>>> +        g_autofree char *dies_msg = g_strdup_printf(
>>>> +            mc->smp_dies_supported ? " * dies (%u)" : "", dies);
>>>> +        error_setg(errp, "Invalid CPU topology: "
>>>> +                   "sockets (%u)%s * cores (%u) * threads (%u) "
>>>> +                   "!= maxcpus (%u)",
>>>> +                   sockets, dies_msg, cores, threads,
>>>> +                   maxcpus);
>>>> +        return;
>>>> +    }
>>>> +
>>>> +    if (sockets * dies * cores * threads < cpus) {
>>>> +        g_autofree char *dies_msg = g_strdup_printf(
>>>> +            mc->smp_dies_supported ? " * dies (%u)" : "", dies);
>>>> +        error_setg(errp, "Invalid CPU topology: "
>>>> +                   "sockets (%u)%s * cores (%u) * threads (%u) < "
>>>> +                   "smp_cpus (%u)",
>>>> +                   sockets, dies_msg, cores, threads, cpus);
>>>> +        return;
>>>> +    }
>>>> +
>>>> +    ms->smp.cpus = cpus;
>>>> +    ms->smp.sockets = sockets;
>>>> +    ms->smp.dies = dies;
>>>> +    ms->smp.cores = cores;
>>>> +    ms->smp.threads = threads;
>>>> +    ms->smp.max_cpus = maxcpus;
>>>> +}
>>>> diff --git a/hw/core/machine.c b/hw/core/machine.c
>>>> index 9d24b67ef3..61be266b6c 100644
>>>> --- a/hw/core/machine.c
>>>> +++ b/hw/core/machine.c
>>>> @@ -744,115 +744,6 @@ void machine_set_cpu_numa_node(MachineState *machine,
>>>>        }
>>>>    }
>>>> -/*
>>>> - * smp_parse - Generic function used to parse the given SMP configuration
>>>> - *
>>>> - * The topology parameters must be specified equal to or great than one
>>>> - * or just omitted, explicit configuration like "cpus=0" is not allowed.
>>>> - * The omitted parameters will be calculated based on the provided ones.
>>>> - *
>>>> - * maxcpus will default to the value of cpus if omitted and will be used
>>>> - * to compute the missing sockets/cores/threads. cpus will be calculated
>>>> - * from the computed parametrs if omitted.
>>>> - *
>>>> - * In calculation of omitted arch-netural sockets/cores/threads, we prefer
>>>> - * sockets over cores over threads before 6.2, while prefer cores over
>>>> - * sockets over threads since 6.2 on. The arch-specific dies will directly
>>>> - * default to 1 if omitted.
>>>> - */
>>>> -static void smp_parse(MachineState *ms, SMPConfiguration *config, Error **errp)
>>>> -{
>>>> -    MachineClass *mc = MACHINE_GET_CLASS(ms);
>>>> -    unsigned cpus    = config->has_cpus ? config->cpus : 0;
>>>> -    unsigned sockets = config->has_sockets ? config->sockets : 0;
>>>> -    unsigned dies    = config->has_dies ? config->dies : 1;
>>>> -    unsigned cores   = config->has_cores ? config->cores : 0;
>>>> -    unsigned threads = config->has_threads ? config->threads : 0;
>>>> -    unsigned maxcpus = config->has_maxcpus ? config->maxcpus : 0;
>>>> -
>>>> -    if ((config->has_cpus && config->cpus == 0) ||
>>>> -        (config->has_sockets && config->sockets == 0) ||
>>>> -        (config->has_dies && config->dies == 0) ||
>>>> -        (config->has_cores && config->cores == 0) ||
>>>> -        (config->has_threads && config->threads == 0) ||
>>>> -        (config->has_maxcpus && config->maxcpus == 0)) {
>>>> -        error_setg(errp, "parameters must be equal to or greater than one"
>>>> -                   "if provided");
>>>> -        return;
>>>> -    }
>>>> -
>>>> -    if (!mc->smp_dies_supported && dies > 1) {
>>>> -        error_setg(errp, "dies not supported by this machine's CPU topology");
>>>> -        return;
>>>> -    }
>>>> -
>>>> -    maxcpus = maxcpus > 0 ? maxcpus : cpus;
>>>> -
>>>> -    /* prefer sockets over cores over threads before 6.2 */
>>>> -    if (mc->smp_prefer_sockets) {
>>>> -        if (sockets == 0) {
>>>> -            cores = cores > 0 ? cores : 1;
>>>> -            threads = threads > 0 ? threads : 1;
>>>> -            sockets = maxcpus / (dies * cores * threads);
>>>> -            sockets = sockets > 0 ? sockets : 1;
>>>> -        } else if (cores == 0) {
>>>> -            threads = threads > 0 ? threads : 1;
>>>> -            cores = maxcpus / (sockets * dies * threads);
>>>> -            cores = cores > 0 ? cores : 1;
>>>> -        } else if (threads == 0) {
>>>> -            threads = maxcpus / (sockets * dies * cores);
>>>> -            threads = threads > 0 ? threads : 1;
>>>> -        }
>>>> -    /* prefer cores over sockets over threads since 6.2 */
>>>> -    } else {
>>>> -        if (cores == 0) {
>>>> -            sockets = sockets > 0 ? sockets : 1;
>>>> -            threads = threads > 0 ? threads : 1;
>>>> -            cores = maxcpus / (sockets * dies * threads);
>>>> -            cores = cores > 0 ? cores : 1;
>>>> -        } else if (sockets == 0) {
>>>> -            threads = threads > 0 ? threads : 1;
>>>> -            sockets = maxcpus / (dies * cores * threads);
>>>> -            sockets = sockets > 0 ? sockets : 1;
>>>> -        } else if (threads == 0) {
>>>> -            threads = maxcpus / (sockets * dies * cores);
>>>> -            threads = threads > 0 ? threads : 1;
>>>> -        }
>>>> -    }
>>>> -
>>>> -    /* use the computed parameters to calculate the omitted cpus */
>>>> -    cpus = cpus > 0 ? cpus : sockets * dies * cores * threads;
>>>> -    maxcpus = maxcpus > 0 ? maxcpus : cpus;
>>>> -
>>>> -    if (sockets * dies * cores * threads != maxcpus) {
>>>> -        g_autofree char *dies_msg = g_strdup_printf(
>>>> -            mc->smp_dies_supported ? " * dies (%u)" : "", dies);
>>>> -        error_setg(errp, "Invalid CPU topology: "
>>>> -                   "sockets (%u)%s * cores (%u) * threads (%u) "
>>>> -                   "!= maxcpus (%u)",
>>>> -                   sockets, dies_msg, cores, threads,
>>>> -                   maxcpus);
>>>> -        return;
>>>> -    }
>>>> -
>>>> -    if (sockets * dies * cores * threads < cpus) {
>>>> -        g_autofree char *dies_msg = g_strdup_printf(
>>>> -            mc->smp_dies_supported ? " * dies (%u)" : "", dies);
>>>> -        error_setg(errp, "Invalid CPU topology: "
>>>> -                   "sockets (%u)%s * cores (%u) * threads (%u) < "
>>>> -                   "smp_cpus (%u)",
>>>> -                   sockets, dies_msg, cores, threads, cpus);
>>>> -        return;
>>>> -    }
>>>> -
>>>> -    ms->smp.cpus = cpus;
>>>> -    ms->smp.sockets = sockets;
>>>> -    ms->smp.dies = dies;
>>>> -    ms->smp.cores = cores;
>>>> -    ms->smp.threads = threads;
>>>> -    ms->smp.max_cpus = maxcpus;
>>>> -}
>>>> -
>>>>    static void machine_get_smp(Object *obj, Visitor *v, const char *name,
>>>>                                void *opaque, Error **errp)
>>>>    {
>>>> diff --git a/hw/core/meson.build b/hw/core/meson.build
>>>> index 18f44fb7c2..6d727c7742 100644
>>>> --- a/hw/core/meson.build
>>>> +++ b/hw/core/meson.build
>>>> @@ -14,6 +14,7 @@ hwcore_files = files(
>>>>    )
>>>>    common_ss.add(files('cpu-common.c'))
>>>> +common_ss.add(files('machine-smp.c'))
>>>>    common_ss.add(when: 'CONFIG_FITLOADER', if_true: files('loader-fit.c'))
>>>>    common_ss.add(when: 'CONFIG_GENERIC_LOADER', if_true: files('generic-loader.c'))
>>>>    common_ss.add(when: ['CONFIG_GUEST_LOADER', fdt], if_true: files('guest-loader.c'))
>>>> diff --git a/include/hw/boards.h b/include/hw/boards.h
>>>> index 12ab0f5968..071eec1e74 100644
>>>> --- a/include/hw/boards.h
>>>> +++ b/include/hw/boards.h
>>>> @@ -34,6 +34,7 @@ HotpluggableCPUList *machine_query_hotpluggable_cpus(MachineState *machine);
>>>>    void machine_set_cpu_numa_node(MachineState *machine,
>>>>                                   const CpuInstanceProperties *props,
>>>>                                   Error **errp);
>>>> +void smp_parse(MachineState *ms, SMPConfiguration *config, Error **errp);
>>>>    /**
>>>>     * machine_class_allow_dynamic_sysbus_dev: Add type to list of valid devices
>>>> -- 
>>>> 2.19.1
>>>>
>>> Otherwise
>>>
>>> Reviewed-by: Andrew Jones <drjones@redhat.com>
>>>
>> Thanks,
>> Yanan
>> .
>>
> .



^ permalink raw reply	[flat|nested] 58+ messages in thread

* Re: [PATCH for-6.2 v2 04/11] machine: Use the computed parameters to calculate omitted cpus
  2021-07-22 12:27       ` Andrew Jones
@ 2021-07-22 14:59         ` wangyanan (Y)
  2021-07-22 15:05           ` Andrew Jones
  0 siblings, 1 reply; 58+ messages in thread
From: wangyanan (Y) @ 2021-07-22 14:59 UTC (permalink / raw)
  To: Andrew Jones
  Cc: Peter Maydell, Thomas Huth, Daniel P. Berrangé,
	Eduardo Habkost, Michael S . Tsirkin, wanghaibin.wang,
	Richard Henderson, qemu-devel, Greg Kurz, Halil Pasic,
	Igor Mammedov, yuzenghui, Paolo Bonzini, David Gibson

On 2021/7/22 20:27, Andrew Jones wrote:
> On Thu, Jul 22, 2021 at 12:42:55PM +0800, wangyanan (Y) wrote:
>> On 2021/7/20 0:42, Andrew Jones wrote:
>>> On Mon, Jul 19, 2021 at 11:20:36AM +0800, Yanan Wang wrote:
>>>> Currently we directly calculate the omitted cpus based on the already
>>>> provided parameters. This makes some cmdlines like:
>>>>     -smp maxcpus=16
>>>>     -smp sockets=2,maxcpus=16
>>>>     -smp sockets=2,dies=2,maxcpus=16
>>>>     -smp sockets=2,cores=4,maxcpus=16
>>>> not work. We should probably use the computed paramters to calculate
>>>> cpus when maxcpus is provided while cpus is omitted, which will make
>>>> above configs start to work.
>>>>
>>>> Note: change in this patch won't affect any existing working cmdlines
>>>> but allows more incomplete configs to be valid.
>>>>
>>>> Signed-off-by: Yanan Wang <wangyanan55@huawei.com>
>>>> ---
>>>>    hw/core/machine.c | 17 +++++++++--------
>>>>    1 file changed, 9 insertions(+), 8 deletions(-)
>>>>
>>>> diff --git a/hw/core/machine.c b/hw/core/machine.c
>>>> index c9f15b15a5..668f0a1553 100644
>>>> --- a/hw/core/machine.c
>>>> +++ b/hw/core/machine.c
>>>> @@ -767,26 +767,27 @@ static void smp_parse(MachineState *ms, SMPConfiguration *config, Error **errp)
>>>>            return;
>>>>        }
>>>> -    /* compute missing values, prefer sockets over cores over threads */
>>>>        maxcpus = maxcpus > 0 ? maxcpus : cpus;
>>>> -    if (cpus == 0) {
>>>> -        sockets = sockets > 0 ? sockets : 1;
>>>> -        cores = cores > 0 ? cores : 1;
>>>> -        threads = threads > 0 ? threads : 1;
>>>> -        cpus = sockets * dies * cores * threads;
>>>> -        maxcpus = maxcpus > 0 ? maxcpus : cpus;
>>>> -    } else if (sockets == 0) {
>>>> +    /* compute missing values, prefer sockets over cores over threads */
>>>> +    if (sockets == 0) {
>>>>            cores = cores > 0 ? cores : 1;
>>>>            threads = threads > 0 ? threads : 1;
>>>>            sockets = maxcpus / (dies * cores * threads);
>>>> +        sockets = sockets > 0 ? sockets : 1;
>>>>        } else if (cores == 0) {
>>>>            threads = threads > 0 ? threads : 1;
>>>>            cores = maxcpus / (sockets * dies * threads);
>>>> +        cores = cores > 0 ? cores : 1;
>>>>        } else if (threads == 0) {
>>>>            threads = maxcpus / (sockets * dies * cores);
>>>> +        threads = threads > 0 ? threads : 1;
>>>>        }
>>> I didn't think we wanted this rounding which this patch adds back into
>>> cores and threads and now also sockets.
>> Firstly, I think we can agree that with or without the rounding, the invalid
>> configs will still be reported as invalid. So the only difference is in the
>> err
>> message (either report 0 or 1 of a fractional parameter). :)
> An error message that says the inputs produced a fractional parameter
> would be even better. If the code gets too hairy because some parameters
> may be zero and without additional checks we'd risk divide by zeros,
> then maybe we should output ("fractional %s", param_name) and exit at the
> same places we're currently rounding up.
Ok. If we remove the rounding, then the calculation code has to be modified
to be like the following. We have to separately consider the case that cpus
and maxcpus are both omitted (e.g. -smp sockets=16).

maxcpus = maxcpus > 0 ? maxcpus : cpus;

if (cpus == 0 && maxcpus == 0) {
     sockets = sockets > 0 ? sockets : 1;
     cores = cores > 0 ? cores : 1;
     threads = threads > 0 ? threads : 1;
     goto cal;
}

if (sockets == 0) {
...
} else if (cores == 0) {
...
} else if (threads == 0) {
...
}

cal:
maxcpus = maxcpus > 0 ? maxcpus : sockets * cores * threads;
cpus = cpus > 0 ? cpus : maxcpus;
>> I added back the rounding because this patch indeed need it, we start
>> to use the computed parameters to calculate cpus, so we have to ensure
>> that the computed parameters are at least 1. If both cpus and maxcpus
>> are omitted (e.g. -smp sockets=16), without the rounding we will get
>> zeroed cpus and maxcpus, and with the rounding we will get valid result
>> like "cpus=16,sockets=16,cores=1,threads=1,maxcpus=16".
>>
>> If a "0" or "1" in the error message doesn't make so much difference as
>> assumed for the error reporting, I suggest that we probably can keep the
>> rounding which makes the parser code concise.
>>>> +    /* use the computed parameters to calculate the omitted cpus */
>>>> +    cpus = cpus > 0 ? cpus : sockets * dies * cores * threads;
>>>> +    maxcpus = maxcpus > 0 ? maxcpus : cpus;
>>> It doesn't really matter, but I think I'd rather write this like
>>>
>>>    maxcpus = maxcpus > 0 ? maxcpus : sockets * dies * cores * threads;
>>>    cpus = cpus > 0 ? cpus : maxcpus;
>> Yes, there is no functional difference. But I think maybe we'd better keep
>> some consistence with the "maxcpus = maxcpus > 0 ? maxcpus : cpus"
>> at top this function and also with the smp doc in qemu-option.hx i.e.
>> "If omitted the maximum number of CPUs will be set to match the initial
>> CPU count" ?
> I won't argue it too much, but I think we should be thinking about maxcpus
> more than cpus if we're thinking about cpu topologies. I'd rather have
> users keep in mind that whatever their topology generates is the max and
> if they don't want to expose that many cpus to the guest then they should
> provide an additional, smaller number 'cpus'. To get that mindset we may
> need to adjust the documentation.
>
Already agree with this in the reply of patch#7.

Thanks,
Yanan



^ permalink raw reply	[flat|nested] 58+ messages in thread

* Re: [PATCH for-6.2 v2 04/11] machine: Use the computed parameters to calculate omitted cpus
  2021-07-22 14:59         ` wangyanan (Y)
@ 2021-07-22 15:05           ` Andrew Jones
  2021-07-22 15:45             ` wangyanan (Y)
  0 siblings, 1 reply; 58+ messages in thread
From: Andrew Jones @ 2021-07-22 15:05 UTC (permalink / raw)
  To: wangyanan (Y)
  Cc: Peter Maydell, Thomas Huth, Daniel P. Berrangé,
	Eduardo Habkost, Michael S . Tsirkin, wanghaibin.wang,
	Richard Henderson, qemu-devel, Greg Kurz, Halil Pasic,
	Igor Mammedov, yuzenghui, Paolo Bonzini, David Gibson

On Thu, Jul 22, 2021 at 10:59:11PM +0800, wangyanan (Y) wrote:
> Ok. If we remove the rounding, then the calculation code has to be modified
> to be like the following. We have to separately consider the case that cpus
> and maxcpus are both omitted (e.g. -smp sockets=16).
> 
> maxcpus = maxcpus > 0 ? maxcpus : cpus;
> 
> if (cpus == 0 && maxcpus == 0) {
>     sockets = sockets > 0 ? sockets : 1;
>     cores = cores > 0 ? cores : 1;
>     threads = threads > 0 ? threads : 1;
>     goto cal;
> }
> 
> if (sockets == 0) {
> ...
> } else if (cores == 0) {
> ...
> } else if (threads == 0) {
> ...
> }
> 
> cal:
> maxcpus = maxcpus > 0 ? maxcpus : sockets * cores * threads;
> cpus = cpus > 0 ? cpus : maxcpus;

Whatever works, but hopefully you can avoid an ugly goto.

Thanks,
drew



^ permalink raw reply	[flat|nested] 58+ messages in thread

* Re: [PATCH for-6.2 v2 04/11] machine: Use the computed parameters to calculate omitted cpus
  2021-07-22 15:05           ` Andrew Jones
@ 2021-07-22 15:45             ` wangyanan (Y)
  0 siblings, 0 replies; 58+ messages in thread
From: wangyanan (Y) @ 2021-07-22 15:45 UTC (permalink / raw)
  To: Andrew Jones
  Cc: Peter Maydell, Thomas Huth, Daniel P. Berrangé,
	Eduardo Habkost, Michael S . Tsirkin, wanghaibin.wang,
	Richard Henderson, qemu-devel, Greg Kurz, Halil Pasic,
	Igor Mammedov, yuzenghui, Paolo Bonzini, David Gibson

On 2021/7/22 23:05, Andrew Jones wrote:
> On Thu, Jul 22, 2021 at 10:59:11PM +0800, wangyanan (Y) wrote:
>> Ok. If we remove the rounding, then the calculation code has to be modified
>> to be like the following. We have to separately consider the case that cpus
>> and maxcpus are both omitted (e.g. -smp sockets=16).
>>
>> maxcpus = maxcpus > 0 ? maxcpus : cpus;
>>
>> if (cpus == 0 && maxcpus == 0) {
>>      sockets = sockets > 0 ? sockets : 1;
>>      cores = cores > 0 ? cores : 1;
>>      threads = threads > 0 ? threads : 1;
>>      goto cal;
>> }
>>
>> if (sockets == 0) {
>> ...
>> } else if (cores == 0) {
>> ...
>> } else if (threads == 0) {
>> ...
>> }
>>
>> cal:
>> maxcpus = maxcpus > 0 ? maxcpus : sockets * cores * threads;
>> cpus = cpus > 0 ? cpus : maxcpus;
> Whatever works, but hopefully you can avoid an ugly goto.
>
Well, it can be avoided.

Thanks,
Yanan



^ permalink raw reply	[flat|nested] 58+ messages in thread

end of thread, other threads:[~2021-07-22 15:47 UTC | newest]

Thread overview: 58+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2021-07-19  3:20 [PATCH for-6.2 v2 00/11] machine: smp parsing fixes and improvement Yanan Wang
2021-07-19  3:20 ` [PATCH for-6.2 v2 01/11] machine: Disallow specifying topology parameters as zero Yanan Wang
2021-07-19 16:11   ` Andrew Jones
2021-07-21 12:34     ` wangyanan (Y)
2021-07-19 16:46   ` Daniel P. Berrangé
2021-07-21 12:35     ` wangyanan (Y)
2021-07-19  3:20 ` [PATCH for-6.2 v2 02/11] machine: Make smp_parse generic enough for all arches Yanan Wang
2021-07-19 16:28   ` Andrew Jones
2021-07-19 16:36     ` Daniel P. Berrangé
2021-07-19 16:48       ` Andrew Jones
2021-07-19 16:50         ` Daniel P. Berrangé
2021-07-19 16:53   ` Daniel P. Berrangé
2021-07-22  7:18     ` wangyanan (Y)
2021-07-20  6:57   ` Cornelia Huck
2021-07-22  7:12     ` wangyanan (Y)
2021-07-19  3:20 ` [PATCH for-6.2 v2 03/11] machine: Uniformly use maxcpus to calculate the omitted parameters Yanan Wang
2021-07-19 16:36   ` Andrew Jones
2021-07-22  3:00     ` wangyanan (Y)
2021-07-19  3:20 ` [PATCH for-6.2 v2 04/11] machine: Use the computed parameters to calculate omitted cpus Yanan Wang
2021-07-19 16:42   ` Andrew Jones
2021-07-22  4:42     ` wangyanan (Y)
2021-07-22 12:27       ` Andrew Jones
2021-07-22 14:59         ` wangyanan (Y)
2021-07-22 15:05           ` Andrew Jones
2021-07-22 15:45             ` wangyanan (Y)
2021-07-19  3:20 ` [PATCH for-6.2 v2 05/11] machine: Improve the error reporting of smp parsing Yanan Wang
2021-07-19 16:53   ` Andrew Jones
2021-07-22  8:10     ` wangyanan (Y)
2021-07-22 12:47       ` Andrew Jones
2021-07-19  3:20 ` [PATCH for-6.2 v2 06/11] hw: Add compat machines for 6.2 Yanan Wang
2021-07-19  3:38   ` David Gibson
2021-07-19 17:00   ` Andrew Jones
2021-07-19 17:03   ` Cornelia Huck
2021-07-19 23:45   ` Pankaj Gupta
2021-07-19  3:20 ` [PATCH for-6.2 v2 07/11] machine: Prefer cores over sockets in smp parsing since 6.2 Yanan Wang
2021-07-19  3:40   ` David Gibson
2021-07-22  5:22     ` wangyanan (Y)
2021-07-19 17:13   ` Andrew Jones
2021-07-22  5:32     ` wangyanan (Y)
2021-07-19  3:20 ` [PATCH for-6.2 v2 08/11] machine: Use ms instead of global current_machine in sanity-check Yanan Wang
2021-07-19 17:14   ` Andrew Jones
2021-07-19  3:20 ` [PATCH for-6.2 v2 09/11] machine: Tweak the order of topology members in struct CpuTopology Yanan Wang
2021-07-19  3:20 ` [PATCH for-6.2 v2 10/11] machine: Split out the smp parsing code Yanan Wang
2021-07-19 17:20   ` Andrew Jones
2021-07-22  6:24     ` wangyanan (Y)
2021-07-22 13:07       ` Andrew Jones
2021-07-22 14:29         ` wangyanan (Y)
2021-07-19  3:20 ` [PATCH for-6.2 v2 11/11] tests/unit: Add a unit test for smp parsing Yanan Wang
2021-07-19 18:57   ` Andrew Jones
2021-07-22  6:15     ` wangyanan (Y)
2021-07-22 13:12       ` Andrew Jones
2021-07-22 14:18         ` wangyanan (Y)
2021-07-19 16:57 ` [PATCH for-6.2 v2 00/11] machine: smp parsing fixes and improvement Cornelia Huck
2021-07-21 12:38   ` wangyanan (Y)
2021-07-21 13:52     ` Pankaj Gupta
2021-07-22  2:22       ` wangyanan (Y)
2021-07-22  7:51     ` Pierre Morel
2021-07-22  8:32       ` wangyanan (Y)

This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.