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