xref: /qemu/tests/unit/test-smp-parse.c (revision d884e272)
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 4096 /* set the max CPUs supported by the machine as 4096 */
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  * Currently a 4-level topology hierarchy is supported on ARM virt machines
66  *  -sockets/clusters/cores/threads
67  */
68 #define SMP_CONFIG_WITH_CLUSTERS(ha, a, hb, b, hc, c, hd, d, he, e, hf, f) \
69         {                                                     \
70             .has_cpus     = ha, .cpus     = a,                \
71             .has_sockets  = hb, .sockets  = b,                \
72             .has_clusters = hc, .clusters = c,                \
73             .has_cores    = hd, .cores    = d,                \
74             .has_threads  = he, .threads  = e,                \
75             .has_maxcpus  = hf, .maxcpus  = f,                \
76         }
77 
78 /*
79  * Currently a 5-level topology hierarchy is supported on s390 ccw machines
80  *  -drawers/books/sockets/cores/threads
81  */
82 #define SMP_CONFIG_WITH_BOOKS_DRAWERS(ha, a, hb, b, hc, c, hd, \
83                                       d, he, e, hf, f, hg, g)  \
84         {                                                      \
85             .has_cpus     = ha, .cpus     = a,                 \
86             .has_drawers  = hb, .drawers  = b,                 \
87             .has_books    = hc, .books    = c,                 \
88             .has_sockets  = hd, .sockets  = d,                 \
89             .has_cores    = he, .cores    = e,                 \
90             .has_threads  = hf, .threads  = f,                 \
91             .has_maxcpus  = hg, .maxcpus  = g,                 \
92         }
93 
94 /*
95  * Currently QEMU supports up to a 7-level topology hierarchy, which is the
96  * QEMU's unified abstract representation of CPU topology.
97  *  -drawers/books/sockets/dies/clusters/cores/threads
98  */
99 #define SMP_CONFIG_WITH_FULL_TOPO(a, b, c, d, e, f, g, h, i)    \
100         {                                                       \
101             .has_cpus     = true, .cpus     = a,                \
102             .has_drawers  = true, .drawers  = b,                \
103             .has_books    = true, .books    = c,                \
104             .has_sockets  = true, .sockets  = d,                \
105             .has_dies     = true, .dies     = e,                \
106             .has_clusters = true, .clusters = f,                \
107             .has_cores    = true, .cores    = g,                \
108             .has_threads  = true, .threads  = h,                \
109             .has_maxcpus  = true, .maxcpus  = i,                \
110         }
111 
112 /**
113  * @config - the given SMP configuration
114  * @expect_prefer_sockets - the expected parsing result for the
115  * valid configuration, when sockets are preferred over cores
116  * @expect_prefer_cores - the expected parsing result for the
117  * valid configuration, when cores are preferred over sockets
118  * @expect_error - the expected error report when the given
119  * configuration is invalid
120  */
121 typedef struct SMPTestData {
122     SMPConfiguration config;
123     CpuTopology expect_prefer_sockets;
124     CpuTopology expect_prefer_cores;
125     const char *expect_error;
126 } SMPTestData;
127 
128 /*
129  * List all the possible valid sub-collections of the generic 5
130  * topology parameters (i.e. cpus/maxcpus/sockets/cores/threads),
131  * then test the automatic calculation algorithm of the missing
132  * values in the parser.
133  */
134 static const struct SMPTestData data_generic_valid[] = {
135     {
136         /* config: no configuration provided
137          * expect: cpus=1,sockets=1,cores=1,threads=1,maxcpus=1 */
138         .config = SMP_CONFIG_GENERIC(F, 0, F, 0, F, 0, F, 0, F, 0),
139         .expect_prefer_sockets = CPU_TOPOLOGY_GENERIC(1, 1, 1, 1, 1),
140         .expect_prefer_cores   = CPU_TOPOLOGY_GENERIC(1, 1, 1, 1, 1),
141     }, {
142         /* config: -smp 8
143          * prefer_sockets: cpus=8,sockets=8,cores=1,threads=1,maxcpus=8
144          * prefer_cores: cpus=8,sockets=1,cores=8,threads=1,maxcpus=8 */
145         .config = SMP_CONFIG_GENERIC(T, 8, F, 0, F, 0, F, 0, F, 0),
146         .expect_prefer_sockets = CPU_TOPOLOGY_GENERIC(8, 8, 1, 1, 8),
147         .expect_prefer_cores   = CPU_TOPOLOGY_GENERIC(8, 1, 8, 1, 8),
148     }, {
149         /* config: -smp sockets=2
150          * expect: cpus=2,sockets=2,cores=1,threads=1,maxcpus=2 */
151         .config = SMP_CONFIG_GENERIC(F, 0, T, 2, F, 0, F, 0, F, 0),
152         .expect_prefer_sockets = CPU_TOPOLOGY_GENERIC(2, 2, 1, 1, 2),
153         .expect_prefer_cores   = CPU_TOPOLOGY_GENERIC(2, 2, 1, 1, 2),
154     }, {
155         /* config: -smp cores=4
156          * expect: cpus=4,sockets=1,cores=4,threads=1,maxcpus=4 */
157         .config = SMP_CONFIG_GENERIC(F, 0, F, 0, T, 4, F, 0, F, 0),
158         .expect_prefer_sockets = CPU_TOPOLOGY_GENERIC(4, 1, 4, 1, 4),
159         .expect_prefer_cores   = CPU_TOPOLOGY_GENERIC(4, 1, 4, 1, 4),
160     }, {
161         /* config: -smp threads=2
162          * expect: cpus=2,sockets=1,cores=1,threads=2,maxcpus=2 */
163         .config = SMP_CONFIG_GENERIC(F, 0, F, 0, F, 0, T, 2, F, 0),
164         .expect_prefer_sockets = CPU_TOPOLOGY_GENERIC(2, 1, 1, 2, 2),
165         .expect_prefer_cores   = CPU_TOPOLOGY_GENERIC(2, 1, 1, 2, 2),
166     }, {
167         /* config: -smp maxcpus=16
168          * prefer_sockets: cpus=16,sockets=16,cores=1,threads=1,maxcpus=16
169          * prefer_cores: cpus=16,sockets=1,cores=16,threads=1,maxcpus=16 */
170         .config = SMP_CONFIG_GENERIC(F, 0, F, 0, F, 0, F, 0, T, 16),
171         .expect_prefer_sockets = CPU_TOPOLOGY_GENERIC(16, 16, 1, 1, 16),
172         .expect_prefer_cores   = CPU_TOPOLOGY_GENERIC(16, 1, 16, 1, 16),
173     }, {
174         /* config: -smp 8,sockets=2
175          * expect: cpus=8,sockets=2,cores=4,threads=1,maxcpus=8 */
176         .config = SMP_CONFIG_GENERIC(T, 8, T, 2, F, 0, F, 0, F, 0),
177         .expect_prefer_sockets = CPU_TOPOLOGY_GENERIC(8, 2, 4, 1, 8),
178         .expect_prefer_cores   = CPU_TOPOLOGY_GENERIC(8, 2, 4, 1, 8),
179     }, {
180         /* config: -smp 8,cores=4
181          * expect: cpus=8,sockets=2,cores=4,threads=1,maxcpus=8 */
182         .config = SMP_CONFIG_GENERIC(T, 8, F, 0, T, 4, F, 0, F, 0),
183         .expect_prefer_sockets = CPU_TOPOLOGY_GENERIC(8, 2, 4, 1, 8),
184         .expect_prefer_cores   = CPU_TOPOLOGY_GENERIC(8, 2, 4, 1, 8),
185     }, {
186         /* config: -smp 8,threads=2
187          * prefer_sockets: cpus=8,sockets=4,cores=1,threads=2,maxcpus=8
188          * prefer_cores: cpus=8,sockets=1,cores=4,threads=2,maxcpus=8 */
189         .config = SMP_CONFIG_GENERIC(T, 8, F, 0, F, 0, T, 2, F, 0),
190         .expect_prefer_sockets = CPU_TOPOLOGY_GENERIC(8, 4, 1, 2, 8),
191         .expect_prefer_cores   = CPU_TOPOLOGY_GENERIC(8, 1, 4, 2, 8),
192     }, {
193         /* config: -smp 8,maxcpus=16
194          * prefer_sockets: cpus=8,sockets=16,cores=1,threads=1,maxcpus=16
195          * prefer_cores: cpus=8,sockets=1,cores=16,threads=1,maxcpus=16 */
196         .config = SMP_CONFIG_GENERIC(T, 8, F, 0, F, 0, F, 0, T, 16),
197         .expect_prefer_sockets = CPU_TOPOLOGY_GENERIC(8, 16, 1, 1, 16),
198         .expect_prefer_cores   = CPU_TOPOLOGY_GENERIC(8, 1, 16, 1, 16),
199     }, {
200         /* config: -smp sockets=2,cores=4
201          * expect: cpus=8,sockets=2,cores=4,threads=1,maxcpus=8 */
202         .config = SMP_CONFIG_GENERIC(F, 0, T, 2, T, 4, F, 0, F, 0),
203         .expect_prefer_sockets = CPU_TOPOLOGY_GENERIC(8, 2, 4, 1, 8),
204         .expect_prefer_cores   = CPU_TOPOLOGY_GENERIC(8, 2, 4, 1, 8),
205     }, {
206         /* config: -smp sockets=2,threads=2
207          * expect: cpus=4,sockets=2,cores=1,threads=2,maxcpus=4 */
208         .config = SMP_CONFIG_GENERIC(F, 0, T, 2, F, 0, T, 2, F, 0),
209         .expect_prefer_sockets = CPU_TOPOLOGY_GENERIC(4, 2, 1, 2, 4),
210         .expect_prefer_cores   = CPU_TOPOLOGY_GENERIC(4, 2, 1, 2, 4),
211     }, {
212         /* config: -smp sockets=2,maxcpus=16
213          * expect: cpus=16,sockets=2,cores=8,threads=1,maxcpus=16 */
214         .config = SMP_CONFIG_GENERIC(F, 0, T, 2, F, 0, F, 0, T, 16),
215         .expect_prefer_sockets = CPU_TOPOLOGY_GENERIC(16, 2, 8, 1, 16),
216         .expect_prefer_cores   = CPU_TOPOLOGY_GENERIC(16, 2, 8, 1, 16),
217     }, {
218         /* config: -smp cores=4,threads=2
219          * expect: cpus=8,sockets=1,cores=4,threads=2,maxcpus=8 */
220         .config = SMP_CONFIG_GENERIC(F, 0, F, 0, T, 4, T, 2, F, 0),
221         .expect_prefer_sockets = CPU_TOPOLOGY_GENERIC(8, 1, 4, 2, 8),
222         .expect_prefer_cores   = CPU_TOPOLOGY_GENERIC(8, 1, 4, 2, 8),
223     }, {
224         /* config: -smp cores=4,maxcpus=16
225          * expect: cpus=16,sockets=4,cores=4,threads=1,maxcpus=16 */
226         .config = SMP_CONFIG_GENERIC(F, 0, F, 0, T, 4, F, 0, T, 16),
227         .expect_prefer_sockets = CPU_TOPOLOGY_GENERIC(16, 4, 4, 1, 16),
228         .expect_prefer_cores   = CPU_TOPOLOGY_GENERIC(16, 4, 4, 1, 16),
229     }, {
230         /* config: -smp threads=2,maxcpus=16
231          * prefer_sockets: cpus=16,sockets=8,cores=1,threads=2,maxcpus=16
232          * prefer_cores: cpus=16,sockets=1,cores=8,threads=2,maxcpus=16 */
233         .config = SMP_CONFIG_GENERIC(F, 0, F, 0, F, 0, T, 2, T, 16),
234         .expect_prefer_sockets = CPU_TOPOLOGY_GENERIC(16, 8, 1, 2, 16),
235         .expect_prefer_cores   = CPU_TOPOLOGY_GENERIC(16, 1, 8, 2, 16),
236     }, {
237         /* config: -smp 8,sockets=2,cores=4
238          * expect: cpus=8,sockets=2,cores=4,threads=1,maxcpus=8 */
239         .config = SMP_CONFIG_GENERIC(T, 8, T, 2, T, 4, F, 0, F, 0),
240         .expect_prefer_sockets = CPU_TOPOLOGY_GENERIC(8, 2, 4, 1, 8),
241         .expect_prefer_cores   = CPU_TOPOLOGY_GENERIC(8, 2, 4, 1, 8),
242     }, {
243         /* config: -smp 8,sockets=2,threads=2
244          * expect: cpus=8,sockets=2,cores=2,threads=2,maxcpus=8 */
245         .config = SMP_CONFIG_GENERIC(T, 8, T, 2, F, 0, T, 2, F, 0),
246         .expect_prefer_sockets = CPU_TOPOLOGY_GENERIC(8, 2, 2, 2, 8),
247         .expect_prefer_cores   = CPU_TOPOLOGY_GENERIC(8, 2, 2, 2, 8),
248     }, {
249         /* config: -smp 8,sockets=2,maxcpus=16
250          * expect: cpus=8,sockets=2,cores=8,threads=1,maxcpus=16 */
251         .config = SMP_CONFIG_GENERIC(T, 8, T, 2, F, 0, F, 0, T, 16),
252         .expect_prefer_sockets = CPU_TOPOLOGY_GENERIC(8, 2, 8, 1, 16),
253         .expect_prefer_cores   = CPU_TOPOLOGY_GENERIC(8, 2, 8, 1, 16),
254     }, {
255         /* config: -smp 8,cores=4,threads=2
256          * expect: cpus=8,sockets=1,cores=4,threads=2,maxcpus=8 */
257         .config = SMP_CONFIG_GENERIC(T, 8, F, 0, T, 4, T, 2, F, 0),
258         .expect_prefer_sockets = CPU_TOPOLOGY_GENERIC(8, 1, 4, 2, 8),
259         .expect_prefer_cores   = CPU_TOPOLOGY_GENERIC(8, 1, 4, 2, 8),
260     }, {
261         /* config: -smp 8,cores=4,maxcpus=16
262          * expect: cpus=8,sockets=4,cores=4,threads=1,maxcpus=16 */
263         .config = SMP_CONFIG_GENERIC(T, 8, F, 0, T, 4, F, 0, T, 16),
264         .expect_prefer_sockets = CPU_TOPOLOGY_GENERIC(8, 4, 4, 1, 16),
265         .expect_prefer_cores   = CPU_TOPOLOGY_GENERIC(8, 4, 4, 1, 16),
266     }, {
267         /* config: -smp 8,threads=2,maxcpus=16
268          * prefer_sockets: cpus=8,sockets=8,cores=1,threads=2,maxcpus=16
269          * prefer_cores: cpus=8,sockets=1,cores=8,threads=2,maxcpus=16 */
270         .config = SMP_CONFIG_GENERIC(T, 8, F, 0, F, 0, T, 2, T, 16),
271         .expect_prefer_sockets = CPU_TOPOLOGY_GENERIC(8, 8, 1, 2, 16),
272         .expect_prefer_cores   = CPU_TOPOLOGY_GENERIC(8, 1, 8, 2, 16),
273     }, {
274         /* config: -smp sockets=2,cores=4,threads=2
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, F, 0),
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 sockets=2,cores=4,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, F, 0, 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 sockets=2,threads=2,maxcpus=16
287          * expect: cpus=16,sockets=2,cores=4,threads=2,maxcpus=16 */
288         .config = SMP_CONFIG_GENERIC(F, 0, T, 2, F, 0, T, 2, T, 16),
289         .expect_prefer_sockets = CPU_TOPOLOGY_GENERIC(16, 2, 4, 2, 16),
290         .expect_prefer_cores   = CPU_TOPOLOGY_GENERIC(16, 2, 4, 2, 16),
291     }, {
292         /* config: -smp cores=4,threads=2,maxcpus=16
293          * expect: cpus=16,sockets=2,cores=4,threads=2,maxcpus=16 */
294         .config = SMP_CONFIG_GENERIC(F, 0, F, 0, T, 4, T, 2, T, 16),
295         .expect_prefer_sockets = CPU_TOPOLOGY_GENERIC(16, 2, 4, 2, 16),
296         .expect_prefer_cores   = CPU_TOPOLOGY_GENERIC(16, 2, 4, 2, 16),
297     }, {
298         /* config: -smp 8,sockets=2,cores=4,threads=1
299          * expect: cpus=8,sockets=2,cores=4,threads=1,maxcpus=8 */
300         .config = SMP_CONFIG_GENERIC(T, 8, T, 2, T, 4, T, 1, F, 0),
301         .expect_prefer_sockets = CPU_TOPOLOGY_GENERIC(8, 2, 4, 1, 8),
302         .expect_prefer_cores   = CPU_TOPOLOGY_GENERIC(8, 2, 4, 1, 8),
303     }, {
304         /* config: -smp 8,sockets=2,cores=4,maxcpus=16
305          * expect: cpus=8,sockets=2,cores=4,threads=2,maxcpus=16 */
306         .config = SMP_CONFIG_GENERIC(T, 8, T, 2, T, 4, F, 0, T, 16),
307         .expect_prefer_sockets = CPU_TOPOLOGY_GENERIC(8, 2, 4, 2, 16),
308         .expect_prefer_cores   = CPU_TOPOLOGY_GENERIC(8, 2, 4, 2, 16),
309     }, {
310         /* config: -smp 8,sockets=2,threads=2,maxcpus=16
311          * expect: cpus=8,sockets=2,cores=4,threads=2,maxcpus=16 */
312         .config = SMP_CONFIG_GENERIC(T, 8, T, 2, F, 0, T, 2, T, 16),
313         .expect_prefer_sockets = CPU_TOPOLOGY_GENERIC(8, 2, 4, 2, 16),
314         .expect_prefer_cores   = CPU_TOPOLOGY_GENERIC(8, 2, 4, 2, 16),
315     }, {
316         /* config: -smp 8,cores=4,threads=2,maxcpus=16
317          * expect: cpus=8,sockets=2,cores=4,threads=2,maxcpus=16 */
318         .config = SMP_CONFIG_GENERIC(T, 8, F, 0, T, 4, T, 2, T, 16),
319         .expect_prefer_sockets = CPU_TOPOLOGY_GENERIC(8, 2, 4, 2, 16),
320         .expect_prefer_cores   = CPU_TOPOLOGY_GENERIC(8, 2, 4, 2, 16),
321     }, {
322         /* config: -smp sockets=2,cores=4,threads=2,maxcpus=16
323          * expect: cpus=16,sockets=2,cores=4,threads=2,maxcpus=16 */
324         .config = SMP_CONFIG_GENERIC(F, 0, T, 2, T, 4, T, 2, T, 16),
325         .expect_prefer_sockets = CPU_TOPOLOGY_GENERIC(16, 2, 4, 2, 16),
326         .expect_prefer_cores   = CPU_TOPOLOGY_GENERIC(16, 2, 4, 2, 16),
327     }, {
328         /* config: -smp 8,sockets=2,cores=4,threads=2,maxcpus=16
329          * expect: cpus=8,sockets=2,cores=4,threads=2,maxcpus=16 */
330         .config = SMP_CONFIG_GENERIC(T, 8, T, 2, T, 4, T, 2, T, 16),
331         .expect_prefer_sockets = CPU_TOPOLOGY_GENERIC(8, 2, 4, 2, 16),
332         .expect_prefer_cores   = CPU_TOPOLOGY_GENERIC(8, 2, 4, 2, 16),
333     },
334 };
335 
336 static const struct SMPTestData data_generic_invalid[] = {
337     {
338         /* config: -smp 2,dies=2 */
339         .config = SMP_CONFIG_WITH_DIES(T, 2, F, 0, T, 2, F, 0, F, 0, F, 0),
340         .expect_error = "dies not supported by this machine's CPU topology",
341     }, {
342         /* config: -smp 2,clusters=2 */
343         .config = SMP_CONFIG_WITH_CLUSTERS(T, 2, F, 0, T, 2, F, 0, F, 0, F, 0),
344         .expect_error = "clusters not supported by this machine's CPU topology",
345     }, {
346         /* config: -smp 2,books=2 */
347         .config = SMP_CONFIG_WITH_BOOKS_DRAWERS(T, 2, F, 0, T, 2, F,
348                                                 0, F, 0, F, 0, F, 0),
349         .expect_error = "books not supported by this machine's CPU topology",
350     }, {
351         /* config: -smp 2,drawers=2 */
352         .config = SMP_CONFIG_WITH_BOOKS_DRAWERS(T, 2, T, 2, F, 0, F,
353                                                 0, F, 0, F, 0, F, 0),
354         .expect_error = "drawers not supported by this machine's CPU topology",
355     }, {
356         /* config: -smp 8,sockets=2,cores=4,threads=2,maxcpus=8 */
357         .config = SMP_CONFIG_GENERIC(T, 8, T, 2, T, 4, T, 2, T, 8),
358         .expect_error = "Invalid CPU topology: "
359                         "product of the hierarchy must match maxcpus: "
360                         "sockets (2) * cores (4) * threads (2) "
361                         "!= maxcpus (8)",
362     }, {
363         /* config: -smp 18,sockets=2,cores=4,threads=2,maxcpus=16 */
364         .config = SMP_CONFIG_GENERIC(T, 18, T, 2, T, 4, T, 2, T, 16),
365         .expect_error = "Invalid CPU topology: "
366                         "maxcpus must be equal to or greater than smp: "
367                         "sockets (2) * cores (4) * threads (2) "
368                         "== maxcpus (16) < smp_cpus (18)",
369     }, {
370         /*
371          * config: -smp 1
372          * The test machine should tweak the supported min CPUs to
373          * 2 (MIN_CPUS + 1) for testing.
374          */
375         .config = SMP_CONFIG_GENERIC(T, MIN_CPUS, F, 0, F, 0, F, 0, F, 0),
376         .expect_error = "Invalid SMP CPUs 1. The min CPUs supported "
377                         "by machine '" SMP_MACHINE_NAME "' is 2",
378     }, {
379         /*
380          * config: -smp 4096
381          * The test machine should tweak the supported max CPUs to
382          * 4095 (MAX_CPUS - 1) for testing.
383          */
384         .config = SMP_CONFIG_GENERIC(T, 4096, F, 0, F, 0, F, 0, F, 0),
385         .expect_error = "Invalid SMP CPUs 4096. The max CPUs supported "
386                         "by machine '" SMP_MACHINE_NAME "' is 4095",
387     },
388 };
389 
390 static const struct SMPTestData data_with_dies_invalid[] = {
391     {
392         /* config: -smp 16,sockets=2,dies=2,cores=4,threads=2,maxcpus=16 */
393         .config = SMP_CONFIG_WITH_DIES(T, 16, T, 2, T, 2, T, 4, T, 2, T, 16),
394         .expect_error = "Invalid CPU topology: "
395                         "product of the hierarchy must match maxcpus: "
396                         "sockets (2) * dies (2) * cores (4) * threads (2) "
397                         "!= maxcpus (16)",
398     }, {
399         /* config: -smp 34,sockets=2,dies=2,cores=4,threads=2,maxcpus=32 */
400         .config = SMP_CONFIG_WITH_DIES(T, 34, T, 2, T, 2, T, 4, T, 2, T, 32),
401         .expect_error = "Invalid CPU topology: "
402                         "maxcpus must be equal to or greater than smp: "
403                         "sockets (2) * dies (2) * cores (4) * threads (2) "
404                         "== maxcpus (32) < smp_cpus (34)",
405     },
406 };
407 
408 static const struct SMPTestData data_with_clusters_invalid[] = {
409     {
410         /* config: -smp 16,sockets=2,clusters=2,cores=4,threads=2,maxcpus=16 */
411         .config = SMP_CONFIG_WITH_CLUSTERS(T, 16, T, 2, T, 2, T, 4, T, 2, T, 16),
412         .expect_error = "Invalid CPU topology: "
413                         "product of the hierarchy must match maxcpus: "
414                         "sockets (2) * clusters (2) * cores (4) * threads (2) "
415                         "!= maxcpus (16)",
416     }, {
417         /* config: -smp 34,sockets=2,clusters=2,cores=4,threads=2,maxcpus=32 */
418         .config = SMP_CONFIG_WITH_CLUSTERS(T, 34, T, 2, T, 2, T, 4, T, 2, T, 32),
419         .expect_error = "Invalid CPU topology: "
420                         "maxcpus must be equal to or greater than smp: "
421                         "sockets (2) * clusters (2) * cores (4) * threads (2) "
422                         "== maxcpus (32) < smp_cpus (34)",
423     },
424 };
425 
426 static const struct SMPTestData data_with_books_invalid[] = {
427     {
428         /* config: -smp 16,books=2,sockets=2,cores=4,threads=2,maxcpus=16 */
429         .config = SMP_CONFIG_WITH_BOOKS_DRAWERS(T, 16, F, 1, T, 2, T,
430                                                 2, T, 4, T, 2, T, 16),
431         .expect_error = "Invalid CPU topology: "
432                         "product of the hierarchy must match maxcpus: "
433                         "books (2) * sockets (2) * cores (4) * threads (2) "
434                         "!= maxcpus (16)",
435     }, {
436         /* config: -smp 34,books=2,sockets=2,cores=4,threads=2,maxcpus=32 */
437         .config = SMP_CONFIG_WITH_BOOKS_DRAWERS(T, 34, F, 1, T, 2, T,
438                                                 2, T, 4, T, 2, T, 32),
439         .expect_error = "Invalid CPU topology: "
440                         "maxcpus must be equal to or greater than smp: "
441                         "books (2) * sockets (2) * cores (4) * threads (2) "
442                         "== maxcpus (32) < smp_cpus (34)",
443     },
444 };
445 
446 static const struct SMPTestData data_with_drawers_invalid[] = {
447     {
448         /* config: -smp 16,drawers=2,sockets=2,cores=4,threads=2,maxcpus=16 */
449         .config = SMP_CONFIG_WITH_BOOKS_DRAWERS(T, 16, T, 2, F, 1, T,
450                                                 2, T, 4, T, 2, T, 16),
451         .expect_error = "Invalid CPU topology: "
452                         "product of the hierarchy must match maxcpus: "
453                         "drawers (2) * sockets (2) * cores (4) * threads (2) "
454                         "!= maxcpus (16)",
455     }, {
456         /* config: -smp 34,drawers=2,sockets=2,cores=4,threads=2,maxcpus=32 */
457         .config = SMP_CONFIG_WITH_BOOKS_DRAWERS(T, 34, T, 2, F, 1, T,
458                                                 2, T, 4, T, 2, T, 32),
459         .expect_error = "Invalid CPU topology: "
460                         "maxcpus must be equal to or greater than smp: "
461                         "drawers (2) * sockets (2) * cores (4) * threads (2) "
462                         "== maxcpus (32) < smp_cpus (34)",
463     },
464 };
465 
466 static const struct SMPTestData data_with_drawers_books_invalid[] = {
467     {
468         /*
469          * config: -smp 200,drawers=2,books=2,sockets=2,cores=4,\
470          * threads=2,maxcpus=200
471          */
472         .config = SMP_CONFIG_WITH_BOOKS_DRAWERS(T, 200, T, 3, T, 5, T,
473                                                 2, T, 4, T, 2, T, 200),
474         .expect_error = "Invalid CPU topology: "
475                         "product of the hierarchy must match maxcpus: "
476                         "drawers (3) * books (5) * sockets (2) * "
477                         "cores (4) * threads (2) != maxcpus (200)",
478     }, {
479         /*
480          * config: -smp 242,drawers=2,books=2,sockets=2,cores=4,\
481          * threads=2,maxcpus=240
482          */
483         .config = SMP_CONFIG_WITH_BOOKS_DRAWERS(T, 242, T, 3, T, 5, T,
484                                                 2, T, 4, T, 2, T, 240),
485         .expect_error = "Invalid CPU topology: "
486                         "maxcpus must be equal to or greater than smp: "
487                         "drawers (3) * books (5) * sockets (2) * "
488                         "cores (4) * threads (2) "
489                         "== maxcpus (240) < smp_cpus (242)",
490     },
491 };
492 
493 static const struct SMPTestData data_full_topo_invalid[] = {
494     {
495         /*
496          * config: -smp 200,drawers=3,books=5,sockets=2,dies=4,\
497          *              clusters=2,cores=7,threads=2,maxcpus=200
498          */
499         .config = SMP_CONFIG_WITH_FULL_TOPO(200, 3, 5, 2, 4, 2, 7, 2, 200),
500         .expect_error = "Invalid CPU topology: "
501                         "product of the hierarchy must match maxcpus: "
502                         "drawers (3) * books (5) * sockets (2) * dies (4) * "
503                         "clusters (2) * cores (7) * threads (2) "
504                         "!= maxcpus (200)",
505     }, {
506         /*
507          * config: -smp 3361,drawers=3,books=5,sockets=2,dies=4,\
508          *              clusters=2,cores=7,threads=2,maxcpus=3360
509          */
510         .config = SMP_CONFIG_WITH_FULL_TOPO(3361, 3, 5, 2, 4, 2, 7, 2, 3360),
511         .expect_error = "Invalid CPU topology: "
512                         "maxcpus must be equal to or greater than smp: "
513                         "drawers (3) * books (5) * sockets (2) * dies (4) * "
514                         "clusters (2) * cores (7) * threads (2) "
515                         "== maxcpus (3360) < smp_cpus (3361)",
516     }, {
517         /*
518          * config: -smp 1,drawers=3,books=5,sockets=2,dies=4,\
519          *              clusters=2,cores=7,threads=3,maxcpus=5040
520          */
521         .config = SMP_CONFIG_WITH_FULL_TOPO(3361, 3, 5, 2, 4, 2, 7, 3, 5040),
522         .expect_error = "Invalid SMP CPUs 5040. The max CPUs supported "
523                         "by machine '" SMP_MACHINE_NAME "' is 4096",
524     },
525 };
526 
527 static const struct SMPTestData data_zero_topo_invalid[] = {
528     {
529         /*
530          * Test "cpus=0".
531          * config: -smp 0,drawers=1,books=1,sockets=1,dies=1,\
532          *              clusters=1,cores=1,threads=1,maxcpus=1
533          */
534         .config = SMP_CONFIG_WITH_FULL_TOPO(0, 1, 1, 1, 1, 1, 1, 1, 1),
535         .expect_error = "Invalid CPU topology: CPU topology parameters must "
536                         "be greater than zero",
537     }, {
538         /*
539          * Test "drawers=0".
540          * config: -smp 1,drawers=0,books=1,sockets=1,dies=1,\
541          *              clusters=1,cores=1,threads=1,maxcpus=1
542          */
543         .config = SMP_CONFIG_WITH_FULL_TOPO(1, 0, 1, 1, 1, 1, 1, 1, 1),
544         .expect_error = "Invalid CPU topology: CPU topology parameters must "
545                         "be greater than zero",
546     }, {
547         /*
548          * Test "books=0".
549          * config: -smp 1,drawers=1,books=0,sockets=1,dies=1,\
550          *              clusters=1,cores=1,threads=1,maxcpus=1
551          */
552         .config = SMP_CONFIG_WITH_FULL_TOPO(1, 1, 0, 1, 1, 1, 1, 1, 1),
553         .expect_error = "Invalid CPU topology: CPU topology parameters must "
554                         "be greater than zero",
555     }, {
556         /*
557          * Test "sockets=0".
558          * config: -smp 1,drawers=1,books=1,sockets=0,dies=1,\
559          *              clusters=1,cores=1,threads=1,maxcpus=1
560          */
561         .config = SMP_CONFIG_WITH_FULL_TOPO(1, 1, 1, 0, 1, 1, 1, 1, 1),
562         .expect_error = "Invalid CPU topology: CPU topology parameters must "
563                         "be greater than zero",
564     }, {
565         /*
566          * Test "dies=0".
567          * config: -smp 1,drawers=1,books=1,sockets=1,dies=0,\
568          *              clusters=1,cores=1,threads=1,maxcpus=1
569          */
570         .config = SMP_CONFIG_WITH_FULL_TOPO(1, 1, 1, 1, 0, 1, 1, 1, 1),
571         .expect_error = "Invalid CPU topology: CPU topology parameters must "
572                         "be greater than zero",
573     }, {
574         /*
575          * Test "clusters=0".
576          * config: -smp 1,drawers=1,books=1,sockets=1,dies=1,\
577          *              clusters=0,cores=1,threads=1,maxcpus=1
578          */
579         .config = SMP_CONFIG_WITH_FULL_TOPO(1, 1, 1, 1, 1, 0, 1, 1, 1),
580         .expect_error = "Invalid CPU topology: CPU topology parameters must "
581                         "be greater than zero",
582     }, {
583         /*
584          * Test "cores=0".
585          * config: -smp 1,drawers=1,books=1,sockets=1,dies=1,\
586          *              clusters=1,cores=0,threads=1,maxcpus=1
587          */
588         .config = SMP_CONFIG_WITH_FULL_TOPO(1, 1, 1, 1, 1, 1, 0, 1, 1),
589         .expect_error = "Invalid CPU topology: CPU topology parameters must "
590                         "be greater than zero",
591     }, {
592         /*
593          * Test "threads=0".
594          * config: -smp 1,drawers=1,books=1,sockets=1,dies=1,\
595          *              clusters=1,cores=1,threads=0,maxcpus=1
596          */
597         .config = SMP_CONFIG_WITH_FULL_TOPO(1, 1, 1, 1, 1, 1, 1, 0, 1),
598         .expect_error = "Invalid CPU topology: CPU topology parameters must "
599                         "be greater than zero",
600     }, {
601         /*
602          * Test "maxcpus=0".
603          * config: -smp 1,drawers=1,books=1,sockets=1,dies=1,\
604          *              clusters=1,cores=1,threads=1,maxcpus=0
605          */
606         .config = SMP_CONFIG_WITH_FULL_TOPO(1, 1, 1, 1, 1, 1, 1, 1, 0),
607         .expect_error = "Invalid CPU topology: CPU topology parameters must "
608                         "be greater than zero",
609     },
610 };
611 
612 static char *smp_config_to_string(const SMPConfiguration *config)
613 {
614     return g_strdup_printf(
615         "(SMPConfiguration) {\n"
616         "    .has_cpus     = %5s, cpus     = %" PRId64 ",\n"
617         "    .has_drawers  = %5s, drawers  = %" PRId64 ",\n"
618         "    .has_books    = %5s, books    = %" PRId64 ",\n"
619         "    .has_sockets  = %5s, sockets  = %" PRId64 ",\n"
620         "    .has_dies     = %5s, dies     = %" PRId64 ",\n"
621         "    .has_clusters = %5s, clusters = %" PRId64 ",\n"
622         "    .has_cores    = %5s, cores    = %" PRId64 ",\n"
623         "    .has_threads  = %5s, threads  = %" PRId64 ",\n"
624         "    .has_maxcpus  = %5s, maxcpus  = %" PRId64 ",\n"
625         "}",
626         config->has_cpus ? "true" : "false", config->cpus,
627         config->has_drawers ? "true" : "false", config->drawers,
628         config->has_books ? "true" : "false", config->books,
629         config->has_sockets ? "true" : "false", config->sockets,
630         config->has_dies ? "true" : "false", config->dies,
631         config->has_clusters ? "true" : "false", config->clusters,
632         config->has_cores ? "true" : "false", config->cores,
633         config->has_threads ? "true" : "false", config->threads,
634         config->has_maxcpus ? "true" : "false", config->maxcpus);
635 }
636 
637 /* Use the different calculation than machine_topo_get_threads_per_socket(). */
638 static unsigned int cpu_topology_get_threads_per_socket(const CpuTopology *topo)
639 {
640     /* Check the divisor to avoid invalid topology examples causing SIGFPE. */
641     if (!topo->drawers || !topo->books || !topo->sockets) {
642         return 0;
643     } else {
644         return topo->max_cpus / topo->drawers / topo->books / topo->sockets;
645     }
646 }
647 
648 /* Use the different calculation than machine_topo_get_cores_per_socket(). */
649 static unsigned int cpu_topology_get_cores_per_socket(const CpuTopology *topo)
650 {
651     /* Check the divisor to avoid invalid topology examples causing SIGFPE. */
652     if (!topo->threads) {
653         return 0;
654     } else {
655         return cpu_topology_get_threads_per_socket(topo) / topo->threads;
656     }
657 }
658 
659 static char *cpu_topology_to_string(const CpuTopology *topo,
660                                     unsigned int threads_per_socket,
661                                     unsigned int cores_per_socket,
662                                     bool has_clusters)
663 {
664     return g_strdup_printf(
665         "(CpuTopology) {\n"
666         "    .cpus               = %u,\n"
667         "    .drawers            = %u,\n"
668         "    .books              = %u,\n"
669         "    .sockets            = %u,\n"
670         "    .dies               = %u,\n"
671         "    .clusters           = %u,\n"
672         "    .cores              = %u,\n"
673         "    .threads            = %u,\n"
674         "    .max_cpus           = %u,\n"
675         "    .threads_per_socket = %u,\n"
676         "    .cores_per_socket   = %u,\n"
677         "    .has_clusters       = %s,\n"
678         "}",
679         topo->cpus, topo->drawers, topo->books,
680         topo->sockets, topo->dies, topo->clusters,
681         topo->cores, topo->threads, topo->max_cpus,
682         threads_per_socket, cores_per_socket,
683         has_clusters ? "true" : "false");
684 }
685 
686 static void check_parse(MachineState *ms, const SMPConfiguration *config,
687                         const CpuTopology *expect_topo, const char *expect_err,
688                         bool is_valid)
689 {
690     MachineClass *mc = MACHINE_GET_CLASS(ms);
691     g_autofree char *config_str = smp_config_to_string(config);
692     g_autofree char *expect_topo_str = NULL, *output_topo_str = NULL;
693     unsigned int expect_threads_per_socket, expect_cores_per_socket;
694     unsigned int ms_threads_per_socket, ms_cores_per_socket;
695     Error *err = NULL;
696 
697     expect_threads_per_socket =
698                         cpu_topology_get_threads_per_socket(expect_topo);
699     expect_cores_per_socket =
700                         cpu_topology_get_cores_per_socket(expect_topo);
701     expect_topo_str = cpu_topology_to_string(expect_topo,
702                                              expect_threads_per_socket,
703                                              expect_cores_per_socket,
704                                              config->has_clusters);
705 
706     /* call the generic parser */
707     machine_parse_smp_config(ms, config, &err);
708 
709     ms_threads_per_socket = machine_topo_get_threads_per_socket(ms);
710     ms_cores_per_socket = machine_topo_get_cores_per_socket(ms);
711     output_topo_str = cpu_topology_to_string(&ms->smp,
712                                              ms_threads_per_socket,
713                                              ms_cores_per_socket,
714                                              mc->smp_props.has_clusters);
715 
716     /* when the configuration is supposed to be valid */
717     if (is_valid) {
718         if ((err == NULL) &&
719             (ms->smp.cpus == expect_topo->cpus) &&
720             (ms->smp.drawers == expect_topo->drawers) &&
721             (ms->smp.books == expect_topo->books) &&
722             (ms->smp.sockets == expect_topo->sockets) &&
723             (ms->smp.dies == expect_topo->dies) &&
724             (ms->smp.clusters == expect_topo->clusters) &&
725             (ms->smp.cores == expect_topo->cores) &&
726             (ms->smp.threads == expect_topo->threads) &&
727             (ms->smp.max_cpus == expect_topo->max_cpus) &&
728             (ms_threads_per_socket == expect_threads_per_socket) &&
729             (ms_cores_per_socket == expect_cores_per_socket) &&
730             (mc->smp_props.has_clusters == config->has_clusters)) {
731             return;
732         }
733 
734         if (err != NULL) {
735             g_printerr("Test smp_parse failed!\n"
736                        "Input configuration: %s\n"
737                        "Should be valid: yes\n"
738                        "Expected topology: %s\n\n"
739                        "Result is valid: no\n"
740                        "Output error report: %s\n",
741                        config_str, expect_topo_str, error_get_pretty(err));
742             goto end;
743         }
744 
745         g_printerr("Test smp_parse failed!\n"
746                    "Input configuration: %s\n"
747                    "Should be valid: yes\n"
748                    "Expected topology: %s\n\n"
749                    "Result is valid: yes\n"
750                    "Output topology: %s\n",
751                    config_str, expect_topo_str, output_topo_str);
752         goto end;
753     }
754 
755     /* when the configuration is supposed to be invalid */
756     if (err != NULL) {
757         if (expect_err == NULL ||
758             g_str_equal(expect_err, error_get_pretty(err))) {
759             error_free(err);
760             return;
761         }
762 
763         g_printerr("Test smp_parse failed!\n"
764                    "Input configuration: %s\n"
765                    "Should be valid: no\n"
766                    "Expected error report: %s\n\n"
767                    "Result is valid: no\n"
768                    "Output error report: %s\n",
769                    config_str, expect_err, error_get_pretty(err));
770         goto end;
771     }
772 
773     g_printerr("Test smp_parse failed!\n"
774                "Input configuration: %s\n"
775                "Should be valid: no\n"
776                "Expected error report: %s\n\n"
777                "Result is valid: yes\n"
778                "Output topology: %s\n",
779                config_str, expect_err, output_topo_str);
780 
781 end:
782     if (err != NULL) {
783         error_free(err);
784     }
785 
786     abort();
787 }
788 
789 static void smp_parse_test(MachineState *ms, SMPTestData *data, bool is_valid)
790 {
791     MachineClass *mc = MACHINE_GET_CLASS(ms);
792 
793     mc->smp_props.prefer_sockets = true;
794     check_parse(ms, &data->config, &data->expect_prefer_sockets,
795                 data->expect_error, is_valid);
796 
797     mc->smp_props.prefer_sockets = false;
798     check_parse(ms, &data->config, &data->expect_prefer_cores,
799                 data->expect_error, is_valid);
800 }
801 
802 /* The parsed results of the unsupported parameters should be 1 */
803 static void unsupported_params_init(const MachineClass *mc, SMPTestData *data)
804 {
805     if (!mc->smp_props.dies_supported) {
806         data->expect_prefer_sockets.dies = 1;
807         data->expect_prefer_cores.dies = 1;
808     }
809 
810     if (!mc->smp_props.clusters_supported) {
811         data->expect_prefer_sockets.clusters = 1;
812         data->expect_prefer_cores.clusters = 1;
813     }
814 
815     if (!mc->smp_props.books_supported) {
816         data->expect_prefer_sockets.books = 1;
817         data->expect_prefer_cores.books = 1;
818     }
819 
820     if (!mc->smp_props.drawers_supported) {
821         data->expect_prefer_sockets.drawers = 1;
822         data->expect_prefer_cores.drawers = 1;
823     }
824 }
825 
826 static void machine_base_class_init(ObjectClass *oc, void *data)
827 {
828     MachineClass *mc = MACHINE_CLASS(oc);
829 
830     mc->min_cpus = MIN_CPUS;
831     mc->max_cpus = MAX_CPUS;
832 
833     mc->name = g_strdup(SMP_MACHINE_NAME);
834 }
835 
836 static void machine_generic_invalid_class_init(ObjectClass *oc, void *data)
837 {
838     MachineClass *mc = MACHINE_CLASS(oc);
839 
840     /* Force invalid min CPUs and max CPUs */
841     mc->min_cpus = MIN_CPUS + 1;
842     mc->max_cpus = MAX_CPUS - 1;
843 }
844 
845 static void machine_with_dies_class_init(ObjectClass *oc, void *data)
846 {
847     MachineClass *mc = MACHINE_CLASS(oc);
848 
849     mc->smp_props.dies_supported = true;
850 }
851 
852 static void machine_with_clusters_class_init(ObjectClass *oc, void *data)
853 {
854     MachineClass *mc = MACHINE_CLASS(oc);
855 
856     mc->smp_props.clusters_supported = true;
857 }
858 
859 static void machine_with_books_class_init(ObjectClass *oc, void *data)
860 {
861     MachineClass *mc = MACHINE_CLASS(oc);
862 
863     mc->smp_props.books_supported = true;
864 }
865 
866 static void machine_with_drawers_class_init(ObjectClass *oc, void *data)
867 {
868     MachineClass *mc = MACHINE_CLASS(oc);
869 
870     mc->smp_props.drawers_supported = true;
871 }
872 
873 static void machine_with_drawers_books_class_init(ObjectClass *oc, void *data)
874 {
875     MachineClass *mc = MACHINE_CLASS(oc);
876 
877     mc->smp_props.drawers_supported = true;
878     mc->smp_props.books_supported = true;
879 }
880 
881 static void machine_full_topo_class_init(ObjectClass *oc, void *data)
882 {
883     MachineClass *mc = MACHINE_CLASS(oc);
884 
885     mc->smp_props.drawers_supported = true;
886     mc->smp_props.books_supported = true;
887     mc->smp_props.dies_supported = true;
888     mc->smp_props.clusters_supported = true;
889 }
890 
891 static void test_generic_valid(const void *opaque)
892 {
893     const char *machine_type = opaque;
894     Object *obj = object_new(machine_type);
895     MachineState *ms = MACHINE(obj);
896     MachineClass *mc = MACHINE_GET_CLASS(obj);
897     SMPTestData data = {};
898     int i;
899 
900     for (i = 0; i < ARRAY_SIZE(data_generic_valid); i++) {
901         data = data_generic_valid[i];
902         unsupported_params_init(mc, &data);
903 
904         smp_parse_test(ms, &data, true);
905     }
906 
907     object_unref(obj);
908 }
909 
910 static void test_generic_invalid(const void *opaque)
911 {
912     const char *machine_type = opaque;
913     Object *obj = object_new(machine_type);
914     MachineState *ms = MACHINE(obj);
915     MachineClass *mc = MACHINE_GET_CLASS(obj);
916     SMPTestData data = {};
917     int i;
918 
919     for (i = 0; i < ARRAY_SIZE(data_generic_invalid); i++) {
920         data = data_generic_invalid[i];
921         unsupported_params_init(mc, &data);
922 
923         smp_parse_test(ms, &data, false);
924     }
925 
926     object_unref(obj);
927 }
928 
929 static void test_with_dies(const void *opaque)
930 {
931     const char *machine_type = opaque;
932     Object *obj = object_new(machine_type);
933     MachineState *ms = MACHINE(obj);
934     MachineClass *mc = MACHINE_GET_CLASS(obj);
935     SMPTestData data = {};
936     unsigned int num_dies = 2;
937     int i;
938 
939     for (i = 0; i < ARRAY_SIZE(data_generic_valid); i++) {
940         data = data_generic_valid[i];
941         unsupported_params_init(mc, &data);
942 
943         /* when dies parameter is omitted, it will be set as 1 */
944         data.expect_prefer_sockets.dies = 1;
945         data.expect_prefer_cores.dies = 1;
946 
947         smp_parse_test(ms, &data, true);
948 
949         /* when dies parameter is specified */
950         data.config.has_dies = true;
951         data.config.dies = num_dies;
952         if (data.config.has_cpus) {
953             data.config.cpus *= num_dies;
954         }
955         if (data.config.has_maxcpus) {
956             data.config.maxcpus *= num_dies;
957         }
958 
959         data.expect_prefer_sockets.dies = num_dies;
960         data.expect_prefer_sockets.cpus *= num_dies;
961         data.expect_prefer_sockets.max_cpus *= num_dies;
962         data.expect_prefer_cores.dies = num_dies;
963         data.expect_prefer_cores.cpus *= num_dies;
964         data.expect_prefer_cores.max_cpus *= num_dies;
965 
966         smp_parse_test(ms, &data, true);
967     }
968 
969     for (i = 0; i < ARRAY_SIZE(data_with_dies_invalid); i++) {
970         data = data_with_dies_invalid[i];
971         unsupported_params_init(mc, &data);
972 
973         smp_parse_test(ms, &data, false);
974     }
975 
976     object_unref(obj);
977 }
978 
979 static void test_with_clusters(const void *opaque)
980 {
981     const char *machine_type = opaque;
982     Object *obj = object_new(machine_type);
983     MachineState *ms = MACHINE(obj);
984     MachineClass *mc = MACHINE_GET_CLASS(obj);
985     SMPTestData data = {};
986     unsigned int num_clusters = 2;
987     int i;
988 
989     for (i = 0; i < ARRAY_SIZE(data_generic_valid); i++) {
990         data = data_generic_valid[i];
991         unsupported_params_init(mc, &data);
992 
993         /* when clusters parameter is omitted, it will be set as 1 */
994         data.expect_prefer_sockets.clusters = 1;
995         data.expect_prefer_cores.clusters = 1;
996 
997         smp_parse_test(ms, &data, true);
998 
999         /* when clusters parameter is specified */
1000         data.config.has_clusters = true;
1001         data.config.clusters = num_clusters;
1002         if (data.config.has_cpus) {
1003             data.config.cpus *= num_clusters;
1004         }
1005         if (data.config.has_maxcpus) {
1006             data.config.maxcpus *= num_clusters;
1007         }
1008 
1009         data.expect_prefer_sockets.clusters = num_clusters;
1010         data.expect_prefer_sockets.cpus *= num_clusters;
1011         data.expect_prefer_sockets.max_cpus *= num_clusters;
1012         data.expect_prefer_cores.clusters = num_clusters;
1013         data.expect_prefer_cores.cpus *= num_clusters;
1014         data.expect_prefer_cores.max_cpus *= num_clusters;
1015 
1016         smp_parse_test(ms, &data, true);
1017     }
1018 
1019     for (i = 0; i < ARRAY_SIZE(data_with_clusters_invalid); i++) {
1020         data = data_with_clusters_invalid[i];
1021         unsupported_params_init(mc, &data);
1022 
1023         smp_parse_test(ms, &data, false);
1024     }
1025 
1026     object_unref(obj);
1027 }
1028 
1029 static void test_with_books(const void *opaque)
1030 {
1031     const char *machine_type = opaque;
1032     Object *obj = object_new(machine_type);
1033     MachineState *ms = MACHINE(obj);
1034     MachineClass *mc = MACHINE_GET_CLASS(obj);
1035     SMPTestData data = {};
1036     unsigned int num_books = 2;
1037     int i;
1038 
1039     for (i = 0; i < ARRAY_SIZE(data_generic_valid); i++) {
1040         data = data_generic_valid[i];
1041         unsupported_params_init(mc, &data);
1042 
1043         /* when books parameter is omitted, it will be set as 1 */
1044         data.expect_prefer_sockets.books = 1;
1045         data.expect_prefer_cores.books = 1;
1046 
1047         smp_parse_test(ms, &data, true);
1048 
1049         /* when books parameter is specified */
1050         data.config.has_books = true;
1051         data.config.books = num_books;
1052         if (data.config.has_cpus) {
1053             data.config.cpus *= num_books;
1054         }
1055         if (data.config.has_maxcpus) {
1056             data.config.maxcpus *= num_books;
1057         }
1058 
1059         data.expect_prefer_sockets.books = num_books;
1060         data.expect_prefer_sockets.cpus *= num_books;
1061         data.expect_prefer_sockets.max_cpus *= num_books;
1062         data.expect_prefer_cores.books = num_books;
1063         data.expect_prefer_cores.cpus *= num_books;
1064         data.expect_prefer_cores.max_cpus *= num_books;
1065 
1066         smp_parse_test(ms, &data, true);
1067     }
1068 
1069     for (i = 0; i < ARRAY_SIZE(data_with_books_invalid); i++) {
1070         data = data_with_books_invalid[i];
1071         unsupported_params_init(mc, &data);
1072 
1073         smp_parse_test(ms, &data, false);
1074     }
1075 
1076     object_unref(obj);
1077 }
1078 
1079 static void test_with_drawers(const void *opaque)
1080 {
1081     const char *machine_type = opaque;
1082     Object *obj = object_new(machine_type);
1083     MachineState *ms = MACHINE(obj);
1084     MachineClass *mc = MACHINE_GET_CLASS(obj);
1085     SMPTestData data = {};
1086     unsigned int num_drawers = 2;
1087     int i;
1088 
1089     for (i = 0; i < ARRAY_SIZE(data_generic_valid); i++) {
1090         data = data_generic_valid[i];
1091         unsupported_params_init(mc, &data);
1092 
1093         /* when drawers parameter is omitted, it will be set as 1 */
1094         data.expect_prefer_sockets.drawers = 1;
1095         data.expect_prefer_cores.drawers = 1;
1096 
1097         smp_parse_test(ms, &data, true);
1098 
1099         /* when drawers parameter is specified */
1100         data.config.has_drawers = true;
1101         data.config.drawers = num_drawers;
1102         if (data.config.has_cpus) {
1103             data.config.cpus *= num_drawers;
1104         }
1105         if (data.config.has_maxcpus) {
1106             data.config.maxcpus *= num_drawers;
1107         }
1108 
1109         data.expect_prefer_sockets.drawers = num_drawers;
1110         data.expect_prefer_sockets.cpus *= num_drawers;
1111         data.expect_prefer_sockets.max_cpus *= num_drawers;
1112         data.expect_prefer_cores.drawers = num_drawers;
1113         data.expect_prefer_cores.cpus *= num_drawers;
1114         data.expect_prefer_cores.max_cpus *= num_drawers;
1115 
1116         smp_parse_test(ms, &data, true);
1117     }
1118 
1119     for (i = 0; i < ARRAY_SIZE(data_with_drawers_invalid); i++) {
1120         data = data_with_drawers_invalid[i];
1121         unsupported_params_init(mc, &data);
1122 
1123         smp_parse_test(ms, &data, false);
1124     }
1125 
1126     object_unref(obj);
1127 }
1128 
1129 static void test_with_drawers_books(const void *opaque)
1130 {
1131     const char *machine_type = opaque;
1132     Object *obj = object_new(machine_type);
1133     MachineState *ms = MACHINE(obj);
1134     MachineClass *mc = MACHINE_GET_CLASS(obj);
1135     SMPTestData data = {};
1136     unsigned int num_drawers = 5, num_books = 3;
1137     int i;
1138 
1139     for (i = 0; i < ARRAY_SIZE(data_generic_valid); i++) {
1140         data = data_generic_valid[i];
1141         unsupported_params_init(mc, &data);
1142 
1143         /*
1144          * when drawers and books parameters are omitted, they will
1145          * be both set as 1.
1146          */
1147         data.expect_prefer_sockets.drawers = 1;
1148         data.expect_prefer_sockets.books = 1;
1149         data.expect_prefer_cores.drawers = 1;
1150         data.expect_prefer_cores.books = 1;
1151 
1152         smp_parse_test(ms, &data, true);
1153 
1154         /* when drawers and books parameters are both specified */
1155         data.config.has_drawers = true;
1156         data.config.drawers = num_drawers;
1157         data.config.has_books = true;
1158         data.config.books = num_books;
1159 
1160         if (data.config.has_cpus) {
1161             data.config.cpus *= num_drawers * num_books;
1162         }
1163         if (data.config.has_maxcpus) {
1164             data.config.maxcpus *= num_drawers * num_books;
1165         }
1166 
1167         data.expect_prefer_sockets.drawers = num_drawers;
1168         data.expect_prefer_sockets.books = num_books;
1169         data.expect_prefer_sockets.cpus *= num_drawers * num_books;
1170         data.expect_prefer_sockets.max_cpus *= num_drawers * num_books;
1171 
1172         data.expect_prefer_cores.drawers = num_drawers;
1173         data.expect_prefer_cores.books = num_books;
1174         data.expect_prefer_cores.cpus *= num_drawers * num_books;
1175         data.expect_prefer_cores.max_cpus *= num_drawers * num_books;
1176 
1177         smp_parse_test(ms, &data, true);
1178     }
1179 
1180     for (i = 0; i < ARRAY_SIZE(data_with_drawers_books_invalid); i++) {
1181         data = data_with_drawers_books_invalid[i];
1182         unsupported_params_init(mc, &data);
1183 
1184         smp_parse_test(ms, &data, false);
1185     }
1186 
1187     object_unref(obj);
1188 }
1189 
1190 static void test_full_topo(const void *opaque)
1191 {
1192     const char *machine_type = opaque;
1193     Object *obj = object_new(machine_type);
1194     MachineState *ms = MACHINE(obj);
1195     MachineClass *mc = MACHINE_GET_CLASS(obj);
1196     SMPTestData data = {};
1197     unsigned int drawers = 5, books = 3, dies = 2, clusters = 7, multiplier;
1198     int i;
1199 
1200     multiplier = drawers * books * dies * clusters;
1201     for (i = 0; i < ARRAY_SIZE(data_generic_valid); i++) {
1202         data = data_generic_valid[i];
1203         unsupported_params_init(mc, &data);
1204 
1205         /*
1206          * when drawers, books, dies and clusters parameters are omitted,
1207          * they will be set as 1.
1208          */
1209         data.expect_prefer_sockets.drawers = 1;
1210         data.expect_prefer_sockets.books = 1;
1211         data.expect_prefer_sockets.dies = 1;
1212         data.expect_prefer_sockets.clusters = 1;
1213         data.expect_prefer_cores.drawers = 1;
1214         data.expect_prefer_cores.books = 1;
1215         data.expect_prefer_cores.dies = 1;
1216         data.expect_prefer_cores.clusters = 1;
1217 
1218         smp_parse_test(ms, &data, true);
1219 
1220         /* when drawers, books, dies and clusters parameters are specified. */
1221         data.config.has_drawers = true;
1222         data.config.drawers = drawers;
1223         data.config.has_books = true;
1224         data.config.books = books;
1225         data.config.has_dies = true;
1226         data.config.dies = dies;
1227         data.config.has_clusters = true;
1228         data.config.clusters = clusters;
1229 
1230         if (data.config.has_cpus) {
1231             data.config.cpus *= multiplier;
1232         }
1233         if (data.config.has_maxcpus) {
1234             data.config.maxcpus *= multiplier;
1235         }
1236 
1237         data.expect_prefer_sockets.drawers = drawers;
1238         data.expect_prefer_sockets.books = books;
1239         data.expect_prefer_sockets.dies = dies;
1240         data.expect_prefer_sockets.clusters = clusters;
1241         data.expect_prefer_sockets.cpus *= multiplier;
1242         data.expect_prefer_sockets.max_cpus *= multiplier;
1243 
1244         data.expect_prefer_cores.drawers = drawers;
1245         data.expect_prefer_cores.books = books;
1246         data.expect_prefer_cores.dies = dies;
1247         data.expect_prefer_cores.clusters = clusters;
1248         data.expect_prefer_cores.cpus *= multiplier;
1249         data.expect_prefer_cores.max_cpus *= multiplier;
1250 
1251         smp_parse_test(ms, &data, true);
1252     }
1253 
1254     for (i = 0; i < ARRAY_SIZE(data_full_topo_invalid); i++) {
1255         data = data_full_topo_invalid[i];
1256         unsupported_params_init(mc, &data);
1257 
1258         smp_parse_test(ms, &data, false);
1259     }
1260 
1261     for (i = 0; i < ARRAY_SIZE(data_zero_topo_invalid); i++) {
1262         data = data_zero_topo_invalid[i];
1263         unsupported_params_init(mc, &data);
1264 
1265         smp_parse_test(ms, &data, false);
1266     }
1267 
1268     object_unref(obj);
1269 }
1270 
1271 /* Type info of the tested machine */
1272 static const TypeInfo smp_machine_types[] = {
1273     {
1274         .name           = TYPE_MACHINE,
1275         .parent         = TYPE_OBJECT,
1276         .abstract       = true,
1277         .class_init     = machine_base_class_init,
1278         .class_size     = sizeof(MachineClass),
1279         .instance_size  = sizeof(MachineState),
1280     }, {
1281         .name           = MACHINE_TYPE_NAME("smp-generic-valid"),
1282         .parent         = TYPE_MACHINE,
1283     }, {
1284         .name           = MACHINE_TYPE_NAME("smp-generic-invalid"),
1285         .parent         = TYPE_MACHINE,
1286         .class_init     = machine_generic_invalid_class_init,
1287     }, {
1288         .name           = MACHINE_TYPE_NAME("smp-with-dies"),
1289         .parent         = TYPE_MACHINE,
1290         .class_init     = machine_with_dies_class_init,
1291     }, {
1292         .name           = MACHINE_TYPE_NAME("smp-with-clusters"),
1293         .parent         = TYPE_MACHINE,
1294         .class_init     = machine_with_clusters_class_init,
1295     }, {
1296         .name           = MACHINE_TYPE_NAME("smp-with-books"),
1297         .parent         = TYPE_MACHINE,
1298         .class_init     = machine_with_books_class_init,
1299     }, {
1300         .name           = MACHINE_TYPE_NAME("smp-with-drawers"),
1301         .parent         = TYPE_MACHINE,
1302         .class_init     = machine_with_drawers_class_init,
1303     }, {
1304         .name           = MACHINE_TYPE_NAME("smp-with-drawers-books"),
1305         .parent         = TYPE_MACHINE,
1306         .class_init     = machine_with_drawers_books_class_init,
1307     }, {
1308         .name           = MACHINE_TYPE_NAME("smp-full-topo"),
1309         .parent         = TYPE_MACHINE,
1310         .class_init     = machine_full_topo_class_init,
1311     }
1312 };
1313 
1314 DEFINE_TYPES(smp_machine_types)
1315 
1316 int main(int argc, char *argv[])
1317 {
1318     module_call_init(MODULE_INIT_QOM);
1319 
1320     g_test_init(&argc, &argv, NULL);
1321 
1322     g_test_add_data_func("/test-smp-parse/generic/valid",
1323                          MACHINE_TYPE_NAME("smp-generic-valid"),
1324                          test_generic_valid);
1325     g_test_add_data_func("/test-smp-parse/generic/invalid",
1326                          MACHINE_TYPE_NAME("smp-generic-invalid"),
1327                          test_generic_invalid);
1328     g_test_add_data_func("/test-smp-parse/with_dies",
1329                          MACHINE_TYPE_NAME("smp-with-dies"),
1330                          test_with_dies);
1331     g_test_add_data_func("/test-smp-parse/with_clusters",
1332                          MACHINE_TYPE_NAME("smp-with-clusters"),
1333                          test_with_clusters);
1334     g_test_add_data_func("/test-smp-parse/with_books",
1335                          MACHINE_TYPE_NAME("smp-with-books"),
1336                          test_with_books);
1337     g_test_add_data_func("/test-smp-parse/with_drawers",
1338                          MACHINE_TYPE_NAME("smp-with-drawers"),
1339                          test_with_drawers);
1340     g_test_add_data_func("/test-smp-parse/with_drawers_books",
1341                          MACHINE_TYPE_NAME("smp-with-drawers-books"),
1342                          test_with_drawers_books);
1343     g_test_add_data_func("/test-smp-parse/full",
1344                          MACHINE_TYPE_NAME("smp-full-topo"),
1345                          test_full_topo);
1346 
1347     g_test_run();
1348 
1349     return 0;
1350 }
1351