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