xref: /qemu/tests/unit/test-smp-parse.c (revision 43709a0c)
1 /*
2  * SMP parsing unit-tests
3  *
4  * Copyright (c) 2021 Huawei Technologies Co., Ltd
5  *
6  * Authors:
7  *  Yanan Wang <wangyanan55@huawei.com>
8  *
9  * This work is licensed under the terms of the GNU LGPL, version 2.1 or later.
10  * See the COPYING.LIB file in the top-level directory.
11  */
12 
13 #include "qemu/osdep.h"
14 #include "qom/object.h"
15 #include "qemu/module.h"
16 #include "qapi/error.h"
17 
18 #include "hw/boards.h"
19 
20 #define T true
21 #define F false
22 
23 #define MIN_CPUS 1   /* set the min CPUs supported by the machine as 1 */
24 #define MAX_CPUS 512 /* set the max CPUs supported by the machine as 512 */
25 
26 #define SMP_MACHINE_NAME "TEST-SMP"
27 
28 /*
29  * Used to define the generic 3-level CPU topology hierarchy
30  *  -sockets/cores/threads
31  */
32 #define SMP_CONFIG_GENERIC(ha, a, hb, b, hc, c, hd, d, he, e) \
33         {                                                     \
34             .has_cpus    = ha, .cpus    = a,                  \
35             .has_sockets = hb, .sockets = b,                  \
36             .has_cores   = hc, .cores   = c,                  \
37             .has_threads = hd, .threads = d,                  \
38             .has_maxcpus = he, .maxcpus = e,                  \
39         }
40 
41 #define CPU_TOPOLOGY_GENERIC(a, b, c, d, e)                   \
42         {                                                     \
43             .cpus     = a,                                    \
44             .sockets  = b,                                    \
45             .cores    = c,                                    \
46             .threads  = d,                                    \
47             .max_cpus = e,                                    \
48         }
49 
50 /*
51  * Currently a 4-level topology hierarchy is supported on PC machines
52  *  -sockets/dies/cores/threads
53  */
54 #define SMP_CONFIG_WITH_DIES(ha, a, hb, b, hc, c, hd, d, he, e, hf, f) \
55         {                                                     \
56             .has_cpus    = ha, .cpus    = a,                  \
57             .has_sockets = hb, .sockets = b,                  \
58             .has_dies    = hc, .dies    = c,                  \
59             .has_cores   = hd, .cores   = d,                  \
60             .has_threads = he, .threads = e,                  \
61             .has_maxcpus = hf, .maxcpus = f,                  \
62         }
63 
64 /**
65  * @config - the given SMP configuration
66  * @expect_prefer_sockets - the expected parsing result for the
67  * valid configuration, when sockets are preferred over cores
68  * @expect_prefer_cores - the expected parsing result for the
69  * valid configuration, when cores are preferred over sockets
70  * @expect_error - the expected error report when the given
71  * configuration is invalid
72  */
73 typedef struct SMPTestData {
74     SMPConfiguration config;
75     CpuTopology expect_prefer_sockets;
76     CpuTopology expect_prefer_cores;
77     const char *expect_error;
78 } SMPTestData;
79 
80 /*
81  * List all the possible valid sub-collections of the generic 5
82  * topology parameters (i.e. cpus/maxcpus/sockets/cores/threads),
83  * then test the automatic calculation algorithm of the missing
84  * values in the parser.
85  */
86 static struct SMPTestData data_generic_valid[] = {
87     {
88         /* config: no configuration provided
89          * expect: cpus=1,sockets=1,cores=1,threads=1,maxcpus=1 */
90         .config = SMP_CONFIG_GENERIC(F, 0, F, 0, F, 0, F, 0, F, 0),
91         .expect_prefer_sockets = CPU_TOPOLOGY_GENERIC(1, 1, 1, 1, 1),
92         .expect_prefer_cores   = CPU_TOPOLOGY_GENERIC(1, 1, 1, 1, 1),
93     }, {
94         /* config: -smp 8
95          * prefer_sockets: cpus=8,sockets=8,cores=1,threads=1,maxcpus=8
96          * prefer_cores: cpus=8,sockets=1,cores=8,threads=1,maxcpus=8 */
97         .config = SMP_CONFIG_GENERIC(T, 8, F, 0, F, 0, F, 0, F, 0),
98         .expect_prefer_sockets = CPU_TOPOLOGY_GENERIC(8, 8, 1, 1, 8),
99         .expect_prefer_cores   = CPU_TOPOLOGY_GENERIC(8, 1, 8, 1, 8),
100     }, {
101         /* config: -smp sockets=2
102          * expect: cpus=2,sockets=2,cores=1,threads=1,maxcpus=2 */
103         .config = SMP_CONFIG_GENERIC(F, 0, T, 2, F, 0, F, 0, F, 0),
104         .expect_prefer_sockets = CPU_TOPOLOGY_GENERIC(2, 2, 1, 1, 2),
105         .expect_prefer_cores   = CPU_TOPOLOGY_GENERIC(2, 2, 1, 1, 2),
106     }, {
107         /* config: -smp cores=4
108          * expect: cpus=4,sockets=1,cores=4,threads=1,maxcpus=4 */
109         .config = SMP_CONFIG_GENERIC(F, 0, F, 0, T, 4, F, 0, F, 0),
110         .expect_prefer_sockets = CPU_TOPOLOGY_GENERIC(4, 1, 4, 1, 4),
111         .expect_prefer_cores   = CPU_TOPOLOGY_GENERIC(4, 1, 4, 1, 4),
112     }, {
113         /* config: -smp threads=2
114          * expect: cpus=2,sockets=1,cores=1,threads=2,maxcpus=2 */
115         .config = SMP_CONFIG_GENERIC(F, 0, F, 0, F, 0, T, 2, F, 0),
116         .expect_prefer_sockets = CPU_TOPOLOGY_GENERIC(2, 1, 1, 2, 2),
117         .expect_prefer_cores   = CPU_TOPOLOGY_GENERIC(2, 1, 1, 2, 2),
118     }, {
119         /* config: -smp maxcpus=16
120          * prefer_sockets: cpus=16,sockets=16,cores=1,threads=1,maxcpus=16
121          * prefer_cores: cpus=16,sockets=1,cores=16,threads=1,maxcpus=16 */
122         .config = SMP_CONFIG_GENERIC(F, 0, F, 0, F, 0, F, 0, T, 16),
123         .expect_prefer_sockets = CPU_TOPOLOGY_GENERIC(16, 16, 1, 1, 16),
124         .expect_prefer_cores   = CPU_TOPOLOGY_GENERIC(16, 1, 16, 1, 16),
125     }, {
126         /* config: -smp 8,sockets=2
127          * expect: cpus=8,sockets=2,cores=4,threads=1,maxcpus=8 */
128         .config = SMP_CONFIG_GENERIC(T, 8, T, 2, F, 0, F, 0, F, 0),
129         .expect_prefer_sockets = CPU_TOPOLOGY_GENERIC(8, 2, 4, 1, 8),
130         .expect_prefer_cores   = CPU_TOPOLOGY_GENERIC(8, 2, 4, 1, 8),
131     }, {
132         /* config: -smp 8,cores=4
133          * expect: cpus=8,sockets=2,cores=4,threads=1,maxcpus=8 */
134         .config = SMP_CONFIG_GENERIC(T, 8, F, 0, T, 4, F, 0, F, 0),
135         .expect_prefer_sockets = CPU_TOPOLOGY_GENERIC(8, 2, 4, 1, 8),
136         .expect_prefer_cores   = CPU_TOPOLOGY_GENERIC(8, 2, 4, 1, 8),
137     }, {
138         /* config: -smp 8,threads=2
139          * prefer_sockets: cpus=8,sockets=4,cores=1,threads=2,maxcpus=8
140          * prefer_cores: cpus=8,sockets=1,cores=4,threads=2,maxcpus=8 */
141         .config = SMP_CONFIG_GENERIC(T, 8, F, 0, F, 0, T, 2, F, 0),
142         .expect_prefer_sockets = CPU_TOPOLOGY_GENERIC(8, 4, 1, 2, 8),
143         .expect_prefer_cores   = CPU_TOPOLOGY_GENERIC(8, 1, 4, 2, 8),
144     }, {
145         /* config: -smp 8,maxcpus=16
146          * prefer_sockets: cpus=8,sockets=16,cores=1,threads=1,maxcpus=16
147          * prefer_cores: cpus=8,sockets=1,cores=16,threads=1,maxcpus=16 */
148         .config = SMP_CONFIG_GENERIC(T, 8, F, 0, F, 0, F, 0, T, 16),
149         .expect_prefer_sockets = CPU_TOPOLOGY_GENERIC(8, 16, 1, 1, 16),
150         .expect_prefer_cores   = CPU_TOPOLOGY_GENERIC(8, 1, 16, 1, 16),
151     }, {
152         /* config: -smp sockets=2,cores=4
153          * expect: cpus=8,sockets=2,cores=4,threads=1,maxcpus=8 */
154         .config = SMP_CONFIG_GENERIC(F, 0, T, 2, T, 4, F, 0, F, 0),
155         .expect_prefer_sockets = CPU_TOPOLOGY_GENERIC(8, 2, 4, 1, 8),
156         .expect_prefer_cores   = CPU_TOPOLOGY_GENERIC(8, 2, 4, 1, 8),
157     }, {
158         /* config: -smp sockets=2,threads=2
159          * expect: cpus=4,sockets=2,cores=1,threads=2,maxcpus=4 */
160         .config = SMP_CONFIG_GENERIC(F, 0, T, 2, F, 0, T, 2, F, 0),
161         .expect_prefer_sockets = CPU_TOPOLOGY_GENERIC(4, 2, 1, 2, 4),
162         .expect_prefer_cores   = CPU_TOPOLOGY_GENERIC(4, 2, 1, 2, 4),
163     }, {
164         /* config: -smp sockets=2,maxcpus=16
165          * expect: cpus=16,sockets=2,cores=8,threads=1,maxcpus=16 */
166         .config = SMP_CONFIG_GENERIC(F, 0, T, 2, F, 0, F, 0, T, 16),
167         .expect_prefer_sockets = CPU_TOPOLOGY_GENERIC(16, 2, 8, 1, 16),
168         .expect_prefer_cores   = CPU_TOPOLOGY_GENERIC(16, 2, 8, 1, 16),
169     }, {
170         /* config: -smp cores=4,threads=2
171          * expect: cpus=8,sockets=1,cores=4,threads=2,maxcpus=8 */
172         .config = SMP_CONFIG_GENERIC(F, 0, F, 0, T, 4, T, 2, F, 0),
173         .expect_prefer_sockets = CPU_TOPOLOGY_GENERIC(8, 1, 4, 2, 8),
174         .expect_prefer_cores   = CPU_TOPOLOGY_GENERIC(8, 1, 4, 2, 8),
175     }, {
176         /* config: -smp cores=4,maxcpus=16
177          * expect: cpus=16,sockets=4,cores=4,threads=1,maxcpus=16 */
178         .config = SMP_CONFIG_GENERIC(F, 0, F, 0, T, 4, F, 0, T, 16),
179         .expect_prefer_sockets = CPU_TOPOLOGY_GENERIC(16, 4, 4, 1, 16),
180         .expect_prefer_cores   = CPU_TOPOLOGY_GENERIC(16, 4, 4, 1, 16),
181     }, {
182         /* config: -smp threads=2,maxcpus=16
183          * prefer_sockets: cpus=16,sockets=8,cores=1,threads=2,maxcpus=16
184          * prefer_cores: cpus=16,sockets=1,cores=8,threads=2,maxcpus=16 */
185         .config = SMP_CONFIG_GENERIC(F, 0, F, 0, F, 0, T, 2, T, 16),
186         .expect_prefer_sockets = CPU_TOPOLOGY_GENERIC(16, 8, 1, 2, 16),
187         .expect_prefer_cores   = CPU_TOPOLOGY_GENERIC(16, 1, 8, 2, 16),
188     }, {
189         /* config: -smp 8,sockets=2,cores=4
190          * expect: cpus=8,sockets=2,cores=4,threads=1,maxcpus=8 */
191         .config = SMP_CONFIG_GENERIC(T, 8, T, 2, T, 4, F, 0, F, 0),
192         .expect_prefer_sockets = CPU_TOPOLOGY_GENERIC(8, 2, 4, 1, 8),
193         .expect_prefer_cores   = CPU_TOPOLOGY_GENERIC(8, 2, 4, 1, 8),
194     }, {
195         /* config: -smp 8,sockets=2,threads=2
196          * expect: cpus=8,sockets=2,cores=2,threads=2,maxcpus=8 */
197         .config = SMP_CONFIG_GENERIC(T, 8, T, 2, F, 0, T, 2, F, 0),
198         .expect_prefer_sockets = CPU_TOPOLOGY_GENERIC(8, 2, 2, 2, 8),
199         .expect_prefer_cores   = CPU_TOPOLOGY_GENERIC(8, 2, 2, 2, 8),
200     }, {
201         /* config: -smp 8,sockets=2,maxcpus=16
202          * expect: cpus=8,sockets=2,cores=8,threads=1,maxcpus=16 */
203         .config = SMP_CONFIG_GENERIC(T, 8, T, 2, F, 0, F, 0, T, 16),
204         .expect_prefer_sockets = CPU_TOPOLOGY_GENERIC(8, 2, 8, 1, 16),
205         .expect_prefer_cores   = CPU_TOPOLOGY_GENERIC(8, 2, 8, 1, 16),
206     }, {
207         /* config: -smp 8,cores=4,threads=2
208          * expect: cpus=8,sockets=1,cores=4,threads=2,maxcpus=8 */
209         .config = SMP_CONFIG_GENERIC(T, 8, F, 0, T, 4, T, 2, F, 0),
210         .expect_prefer_sockets = CPU_TOPOLOGY_GENERIC(8, 1, 4, 2, 8),
211         .expect_prefer_cores   = CPU_TOPOLOGY_GENERIC(8, 1, 4, 2, 8),
212     }, {
213         /* config: -smp 8,cores=4,maxcpus=16
214          * expect: cpus=8,sockets=4,cores=4,threads=1,maxcpus=16 */
215         .config = SMP_CONFIG_GENERIC(T, 8, F, 0, T, 4, F, 0, T, 16),
216         .expect_prefer_sockets = CPU_TOPOLOGY_GENERIC(8, 4, 4, 1, 16),
217         .expect_prefer_cores   = CPU_TOPOLOGY_GENERIC(8, 4, 4, 1, 16),
218     }, {
219         /* config: -smp 8,threads=2,maxcpus=16
220          * prefer_sockets: cpus=8,sockets=8,cores=1,threads=2,maxcpus=16
221          * prefer_cores: cpus=8,sockets=1,cores=8,threads=2,maxcpus=16 */
222         .config = SMP_CONFIG_GENERIC(T, 8, F, 0, F, 0, T, 2, T, 16),
223         .expect_prefer_sockets = CPU_TOPOLOGY_GENERIC(8, 8, 1, 2, 16),
224         .expect_prefer_cores   = CPU_TOPOLOGY_GENERIC(8, 1, 8, 2, 16),
225     }, {
226         /* config: -smp sockets=2,cores=4,threads=2
227          * expect: cpus=16,sockets=2,cores=4,threads=2,maxcpus=16 */
228         .config = SMP_CONFIG_GENERIC(F, 0, T, 2, T, 4, T, 2, F, 0),
229         .expect_prefer_sockets = CPU_TOPOLOGY_GENERIC(16, 2, 4, 2, 16),
230         .expect_prefer_cores   = CPU_TOPOLOGY_GENERIC(16, 2, 4, 2, 16),
231     }, {
232         /* config: -smp sockets=2,cores=4,maxcpus=16
233          * expect: cpus=16,sockets=2,cores=4,threads=2,maxcpus=16 */
234         .config = SMP_CONFIG_GENERIC(F, 0, T, 2, T, 4, F, 0, T, 16),
235         .expect_prefer_sockets = CPU_TOPOLOGY_GENERIC(16, 2, 4, 2, 16),
236         .expect_prefer_cores   = CPU_TOPOLOGY_GENERIC(16, 2, 4, 2, 16),
237     }, {
238         /* config: -smp sockets=2,threads=2,maxcpus=16
239          * expect: cpus=16,sockets=2,cores=4,threads=2,maxcpus=16 */
240         .config = SMP_CONFIG_GENERIC(F, 0, T, 2, F, 0, T, 2, T, 16),
241         .expect_prefer_sockets = CPU_TOPOLOGY_GENERIC(16, 2, 4, 2, 16),
242         .expect_prefer_cores   = CPU_TOPOLOGY_GENERIC(16, 2, 4, 2, 16),
243     }, {
244         /* config: -smp cores=4,threads=2,maxcpus=16
245          * expect: cpus=16,sockets=2,cores=4,threads=2,maxcpus=16 */
246         .config = SMP_CONFIG_GENERIC(F, 0, F, 0, T, 4, T, 2, T, 16),
247         .expect_prefer_sockets = CPU_TOPOLOGY_GENERIC(16, 2, 4, 2, 16),
248         .expect_prefer_cores   = CPU_TOPOLOGY_GENERIC(16, 2, 4, 2, 16),
249     }, {
250         /* config: -smp 8,sockets=2,cores=4,threads=1
251          * expect: cpus=8,sockets=2,cores=4,threads=1,maxcpus=8 */
252         .config = SMP_CONFIG_GENERIC(T, 8, T, 2, T, 4, T, 1, F, 0),
253         .expect_prefer_sockets = CPU_TOPOLOGY_GENERIC(8, 2, 4, 1, 8),
254         .expect_prefer_cores   = CPU_TOPOLOGY_GENERIC(8, 2, 4, 1, 8),
255     }, {
256         /* config: -smp 8,sockets=2,cores=4,maxcpus=16
257          * expect: cpus=8,sockets=2,cores=4,threads=2,maxcpus=16 */
258         .config = SMP_CONFIG_GENERIC(T, 8, T, 2, T, 4, F, 0, T, 16),
259         .expect_prefer_sockets = CPU_TOPOLOGY_GENERIC(8, 2, 4, 2, 16),
260         .expect_prefer_cores   = CPU_TOPOLOGY_GENERIC(8, 2, 4, 2, 16),
261     }, {
262         /* config: -smp 8,sockets=2,threads=2,maxcpus=16
263          * expect: cpus=8,sockets=2,cores=4,threads=2,maxcpus=16 */
264         .config = SMP_CONFIG_GENERIC(T, 8, T, 2, F, 0, T, 2, T, 16),
265         .expect_prefer_sockets = CPU_TOPOLOGY_GENERIC(8, 2, 4, 2, 16),
266         .expect_prefer_cores   = CPU_TOPOLOGY_GENERIC(8, 2, 4, 2, 16),
267     }, {
268         /* config: -smp 8,cores=4,threads=2,maxcpus=16
269          * expect: cpus=8,sockets=2,cores=4,threads=2,maxcpus=16 */
270         .config = SMP_CONFIG_GENERIC(T, 8, F, 0, T, 4, T, 2, T, 16),
271         .expect_prefer_sockets = CPU_TOPOLOGY_GENERIC(8, 2, 4, 2, 16),
272         .expect_prefer_cores   = CPU_TOPOLOGY_GENERIC(8, 2, 4, 2, 16),
273     }, {
274         /* config: -smp sockets=2,cores=4,threads=2,maxcpus=16
275          * expect: cpus=16,sockets=2,cores=4,threads=2,maxcpus=16 */
276         .config = SMP_CONFIG_GENERIC(F, 0, T, 2, T, 4, T, 2, T, 16),
277         .expect_prefer_sockets = CPU_TOPOLOGY_GENERIC(16, 2, 4, 2, 16),
278         .expect_prefer_cores   = CPU_TOPOLOGY_GENERIC(16, 2, 4, 2, 16),
279     }, {
280         /* config: -smp 8,sockets=2,cores=4,threads=2,maxcpus=16
281          * expect: cpus=8,sockets=2,cores=4,threads=2,maxcpus=16 */
282         .config = SMP_CONFIG_GENERIC(T, 8, T, 2, T, 4, T, 2, T, 16),
283         .expect_prefer_sockets = CPU_TOPOLOGY_GENERIC(8, 2, 4, 2, 16),
284         .expect_prefer_cores   = CPU_TOPOLOGY_GENERIC(8, 2, 4, 2, 16),
285     },
286 };
287 
288 static struct SMPTestData data_generic_invalid[] = {
289     {
290         /* config: -smp 2,dies=2 */
291         .config = SMP_CONFIG_WITH_DIES(T, 2, F, 0, T, 2, F, 0, F, 0, F, 0),
292         .expect_error = "dies not supported by this machine's CPU topology",
293     }, {
294         /* config: -smp 8,sockets=2,cores=4,threads=2,maxcpus=8 */
295         .config = SMP_CONFIG_GENERIC(T, 8, T, 2, T, 4, T, 2, T, 8),
296         .expect_error = "Invalid CPU topology: "
297                         "product of the hierarchy must match maxcpus: "
298                         "sockets (2) * cores (4) * threads (2) "
299                         "!= maxcpus (8)",
300     }, {
301         /* config: -smp 18,sockets=2,cores=4,threads=2,maxcpus=16 */
302         .config = SMP_CONFIG_GENERIC(T, 18, T, 2, T, 4, T, 2, T, 16),
303         .expect_error = "Invalid CPU topology: "
304                         "maxcpus must be equal to or greater than smp: "
305                         "sockets (2) * cores (4) * threads (2) "
306                         "== maxcpus (16) < smp_cpus (18)",
307     }, {
308         /* config: -smp 1
309          * should tweak the supported min CPUs to 2 for testing */
310         .config = SMP_CONFIG_GENERIC(T, 1, F, 0, F, 0, F, 0, F, 0),
311         .expect_error = "Invalid SMP CPUs 1. The min CPUs supported "
312                         "by machine '" SMP_MACHINE_NAME "' is 2",
313     }, {
314         /* config: -smp 512
315          * should tweak the supported max CPUs to 511 for testing */
316         .config = SMP_CONFIG_GENERIC(T, 512, F, 0, F, 0, F, 0, F, 0),
317         .expect_error = "Invalid SMP CPUs 512. The max CPUs supported "
318                         "by machine '" SMP_MACHINE_NAME "' is 511",
319     },
320 };
321 
322 static struct SMPTestData data_with_dies_invalid[] = {
323     {
324         /* config: -smp 16,sockets=2,dies=2,cores=4,threads=2,maxcpus=16 */
325         .config = SMP_CONFIG_WITH_DIES(T, 16, T, 2, T, 2, T, 4, T, 2, T, 16),
326         .expect_error = "Invalid CPU topology: "
327                         "product of the hierarchy must match maxcpus: "
328                         "sockets (2) * dies (2) * cores (4) * threads (2) "
329                         "!= maxcpus (16)",
330     }, {
331         /* config: -smp 34,sockets=2,dies=2,cores=4,threads=2,maxcpus=32 */
332         .config = SMP_CONFIG_WITH_DIES(T, 34, T, 2, T, 2, T, 4, T, 2, T, 32),
333         .expect_error = "Invalid CPU topology: "
334                         "maxcpus must be equal to or greater than smp: "
335                         "sockets (2) * dies (2) * cores (4) * threads (2) "
336                         "== maxcpus (32) < smp_cpus (34)",
337     },
338 };
339 
340 static char *smp_config_to_string(SMPConfiguration *config)
341 {
342     return g_strdup_printf(
343         "(SMPConfiguration) {\n"
344         "    .has_cpus    = %5s, cpus    = %" PRId64 ",\n"
345         "    .has_sockets = %5s, sockets = %" PRId64 ",\n"
346         "    .has_dies    = %5s, dies    = %" PRId64 ",\n"
347         "    .has_cores   = %5s, cores   = %" PRId64 ",\n"
348         "    .has_threads = %5s, threads = %" PRId64 ",\n"
349         "    .has_maxcpus = %5s, maxcpus = %" PRId64 ",\n"
350         "}",
351         config->has_cpus ? "true" : "false", config->cpus,
352         config->has_sockets ? "true" : "false", config->sockets,
353         config->has_dies ? "true" : "false", config->dies,
354         config->has_cores ? "true" : "false", config->cores,
355         config->has_threads ? "true" : "false", config->threads,
356         config->has_maxcpus ? "true" : "false", config->maxcpus);
357 }
358 
359 static char *cpu_topology_to_string(CpuTopology *topo)
360 {
361     return g_strdup_printf(
362         "(CpuTopology) {\n"
363         "    .cpus     = %u,\n"
364         "    .sockets  = %u,\n"
365         "    .dies     = %u,\n"
366         "    .cores    = %u,\n"
367         "    .threads  = %u,\n"
368         "    .max_cpus = %u,\n"
369         "}",
370         topo->cpus, topo->sockets, topo->dies,
371         topo->cores, topo->threads, topo->max_cpus);
372 }
373 
374 static void check_parse(MachineState *ms, SMPConfiguration *config,
375                         CpuTopology *expect_topo, const char *expect_err,
376                         bool is_valid)
377 {
378     g_autofree char *config_str = smp_config_to_string(config);
379     g_autofree char *expect_topo_str = cpu_topology_to_string(expect_topo);
380     g_autofree char *output_topo_str = NULL;
381     Error *err = NULL;
382 
383     /* call the generic parser smp_parse() */
384     smp_parse(ms, config, &err);
385 
386     output_topo_str = cpu_topology_to_string(&ms->smp);
387 
388     /* when the configuration is supposed to be valid */
389     if (is_valid) {
390         if ((err == NULL) &&
391             (ms->smp.cpus == expect_topo->cpus) &&
392             (ms->smp.sockets == expect_topo->sockets) &&
393             (ms->smp.dies == expect_topo->dies) &&
394             (ms->smp.cores == expect_topo->cores) &&
395             (ms->smp.threads == expect_topo->threads) &&
396             (ms->smp.max_cpus == expect_topo->max_cpus)) {
397             return;
398         }
399 
400         if (err != NULL) {
401             g_printerr("Test smp_parse failed!\n"
402                        "Input configuration: %s\n"
403                        "Should be valid: yes\n"
404                        "Expected topology: %s\n\n"
405                        "Result is valid: no\n"
406                        "Output error report: %s\n",
407                        config_str, expect_topo_str, error_get_pretty(err));
408             goto end;
409         }
410 
411         g_printerr("Test smp_parse failed!\n"
412                    "Input configuration: %s\n"
413                    "Should be valid: yes\n"
414                    "Expected topology: %s\n\n"
415                    "Result is valid: yes\n"
416                    "Output topology: %s\n",
417                    config_str, expect_topo_str, output_topo_str);
418         goto end;
419     }
420 
421     /* when the configuration is supposed to be invalid */
422     if (err != NULL) {
423         if (expect_err == NULL ||
424             g_str_equal(expect_err, error_get_pretty(err))) {
425             error_free(err);
426             return;
427         }
428 
429         g_printerr("Test smp_parse failed!\n"
430                    "Input configuration: %s\n"
431                    "Should be valid: no\n"
432                    "Expected error report: %s\n\n"
433                    "Result is valid: no\n"
434                    "Output error report: %s\n",
435                    config_str, expect_err, error_get_pretty(err));
436         goto end;
437     }
438 
439     g_printerr("Test smp_parse failed!\n"
440                "Input configuration: %s\n"
441                "Should be valid: no\n"
442                "Expected error report: %s\n\n"
443                "Result is valid: yes\n"
444                "Output topology: %s\n",
445                config_str, expect_err, output_topo_str);
446 
447 end:
448     if (err != NULL) {
449         error_free(err);
450     }
451 
452     abort();
453 }
454 
455 static void smp_parse_test(MachineState *ms, SMPTestData *data, bool is_valid)
456 {
457     MachineClass *mc = MACHINE_GET_CLASS(ms);
458 
459     mc->smp_props.prefer_sockets = true;
460     check_parse(ms, &data->config, &data->expect_prefer_sockets,
461                 data->expect_error, is_valid);
462 
463     mc->smp_props.prefer_sockets = false;
464     check_parse(ms, &data->config, &data->expect_prefer_cores,
465                 data->expect_error, is_valid);
466 }
467 
468 /* The parsed results of the unsupported parameters should be 1 */
469 static void unsupported_params_init(MachineClass *mc, SMPTestData *data)
470 {
471     if (!mc->smp_props.dies_supported) {
472         data->expect_prefer_sockets.dies = 1;
473         data->expect_prefer_cores.dies = 1;
474     }
475 }
476 
477 static void machine_base_class_init(ObjectClass *oc, void *data)
478 {
479     MachineClass *mc = MACHINE_CLASS(oc);
480 
481     mc->min_cpus = MIN_CPUS;
482     mc->max_cpus = MAX_CPUS;
483 
484     mc->smp_props.prefer_sockets = true;
485     mc->smp_props.dies_supported = false;
486 
487     mc->name = g_strdup(SMP_MACHINE_NAME);
488 }
489 
490 static void test_generic(void)
491 {
492     Object *obj = object_new(TYPE_MACHINE);
493     MachineState *ms = MACHINE(obj);
494     MachineClass *mc = MACHINE_GET_CLASS(obj);
495     SMPTestData *data = &(SMPTestData){{ }};
496     int i;
497 
498     for (i = 0; i < ARRAY_SIZE(data_generic_valid); i++) {
499         *data = data_generic_valid[i];
500         unsupported_params_init(mc, data);
501 
502         smp_parse_test(ms, data, true);
503 
504         /* Unsupported parameters can be provided with their values as 1 */
505         data->config.has_dies = true;
506         data->config.dies = 1;
507         smp_parse_test(ms, data, true);
508     }
509 
510     /* Force invalid min CPUs and max CPUs */
511     mc->min_cpus = 2;
512     mc->max_cpus = 511;
513 
514     for (i = 0; i < ARRAY_SIZE(data_generic_invalid); i++) {
515         *data = data_generic_invalid[i];
516         unsupported_params_init(mc, data);
517 
518         smp_parse_test(ms, data, false);
519     }
520 
521     /* Reset the supported min CPUs and max CPUs */
522     mc->min_cpus = MIN_CPUS;
523     mc->max_cpus = MAX_CPUS;
524 
525     object_unref(obj);
526 }
527 
528 static void test_with_dies(void)
529 {
530     Object *obj = object_new(TYPE_MACHINE);
531     MachineState *ms = MACHINE(obj);
532     MachineClass *mc = MACHINE_GET_CLASS(obj);
533     SMPTestData *data = &(SMPTestData){{ }};
534     unsigned int num_dies = 2;
535     int i;
536 
537     /* Force the SMP compat properties */
538     mc->smp_props.dies_supported = true;
539 
540     for (i = 0; i < ARRAY_SIZE(data_generic_valid); i++) {
541         *data = data_generic_valid[i];
542         unsupported_params_init(mc, data);
543 
544         /* when dies parameter is omitted, it will be set as 1 */
545         data->expect_prefer_sockets.dies = 1;
546         data->expect_prefer_cores.dies = 1;
547 
548         smp_parse_test(ms, data, true);
549 
550         /* when dies parameter is specified */
551         data->config.has_dies = true;
552         data->config.dies = num_dies;
553         if (data->config.has_cpus) {
554             data->config.cpus *= num_dies;
555         }
556         if (data->config.has_maxcpus) {
557             data->config.maxcpus *= num_dies;
558         }
559 
560         data->expect_prefer_sockets.dies = num_dies;
561         data->expect_prefer_sockets.cpus *= num_dies;
562         data->expect_prefer_sockets.max_cpus *= num_dies;
563         data->expect_prefer_cores.dies = num_dies;
564         data->expect_prefer_cores.cpus *= num_dies;
565         data->expect_prefer_cores.max_cpus *= num_dies;
566 
567         smp_parse_test(ms, data, true);
568     }
569 
570     for (i = 0; i < ARRAY_SIZE(data_with_dies_invalid); i++) {
571         *data = data_with_dies_invalid[i];
572         unsupported_params_init(mc, data);
573 
574         smp_parse_test(ms, data, false);
575     }
576 
577     /* Restore the SMP compat properties */
578     mc->smp_props.dies_supported = false;
579 
580     object_unref(obj);
581 }
582 
583 /* Type info of the tested machine */
584 static const TypeInfo smp_machine_types[] = {
585     {
586         .name           = TYPE_MACHINE,
587         .parent         = TYPE_OBJECT,
588         .class_init     = machine_base_class_init,
589         .class_size     = sizeof(MachineClass),
590         .instance_size  = sizeof(MachineState),
591     }
592 };
593 
594 DEFINE_TYPES(smp_machine_types)
595 
596 int main(int argc, char *argv[])
597 {
598     module_call_init(MODULE_INIT_QOM);
599 
600     g_test_init(&argc, &argv, NULL);
601 
602     g_test_add_func("/test-smp-parse/generic", test_generic);
603     g_test_add_func("/test-smp-parse/with_dies", test_with_dies);
604 
605     g_test_run();
606 
607     return 0;
608 }
609