xref: /qemu/tests/unit/test-smp-parse.c (revision ec6f3fc3)
1 /*
2  * SMP parsing unit-tests
3  *
4  * Copyright (c) 2021 Huawei Technologies Co., Ltd
5  *
6  * Authors:
7  *  Yanan Wang <wangyanan55@huawei.com>
8  *
9  * This work is licensed under the terms of the GNU LGPL, version 2.1 or later.
10  * See the COPYING.LIB file in the top-level directory.
11  */
12 
13 #include "qemu/osdep.h"
14 #include "qom/object.h"
15 #include "qemu/module.h"
16 #include "qapi/error.h"
17 
18 #include "hw/boards.h"
19 
20 #define T true
21 #define F false
22 
23 #define MIN_CPUS 1   /* set the min CPUs supported by the machine as 1 */
24 #define MAX_CPUS 512 /* set the max CPUs supported by the machine as 512 */
25 
26 #define SMP_MACHINE_NAME "TEST-SMP"
27 
28 /*
29  * Used to define the generic 3-level CPU topology hierarchy
30  *  -sockets/cores/threads
31  */
32 #define SMP_CONFIG_GENERIC(ha, a, hb, b, hc, c, hd, d, he, e) \
33         {                                                     \
34             .has_cpus    = ha, .cpus    = a,                  \
35             .has_sockets = hb, .sockets = b,                  \
36             .has_cores   = hc, .cores   = c,                  \
37             .has_threads = hd, .threads = d,                  \
38             .has_maxcpus = he, .maxcpus = e,                  \
39         }
40 
41 #define CPU_TOPOLOGY_GENERIC(a, b, c, d, e)                   \
42         {                                                     \
43             .cpus     = a,                                    \
44             .sockets  = b,                                    \
45             .cores    = c,                                    \
46             .threads  = d,                                    \
47             .max_cpus = e,                                    \
48         }
49 
50 /*
51  * Currently a 4-level topology hierarchy is supported on PC machines
52  *  -sockets/dies/cores/threads
53  */
54 #define SMP_CONFIG_WITH_DIES(ha, a, hb, b, hc, c, hd, d, he, e, hf, f) \
55         {                                                     \
56             .has_cpus    = ha, .cpus    = a,                  \
57             .has_sockets = hb, .sockets = b,                  \
58             .has_dies    = hc, .dies    = c,                  \
59             .has_cores   = hd, .cores   = d,                  \
60             .has_threads = he, .threads = e,                  \
61             .has_maxcpus = hf, .maxcpus = f,                  \
62         }
63 
64 /*
65  * 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  * @config - the given SMP configuration
80  * @expect_prefer_sockets - the expected parsing result for the
81  * valid configuration, when sockets are preferred over cores
82  * @expect_prefer_cores - the expected parsing result for the
83  * valid configuration, when cores are preferred over sockets
84  * @expect_error - the expected error report when the given
85  * configuration is invalid
86  */
87 typedef struct SMPTestData {
88     SMPConfiguration config;
89     CpuTopology expect_prefer_sockets;
90     CpuTopology expect_prefer_cores;
91     const char *expect_error;
92 } SMPTestData;
93 
94 /*
95  * List all the possible valid sub-collections of the generic 5
96  * topology parameters (i.e. cpus/maxcpus/sockets/cores/threads),
97  * then test the automatic calculation algorithm of the missing
98  * values in the parser.
99  */
100 static const struct SMPTestData data_generic_valid[] = {
101     {
102         /* config: no configuration provided
103          * expect: cpus=1,sockets=1,cores=1,threads=1,maxcpus=1 */
104         .config = SMP_CONFIG_GENERIC(F, 0, F, 0, F, 0, F, 0, F, 0),
105         .expect_prefer_sockets = CPU_TOPOLOGY_GENERIC(1, 1, 1, 1, 1),
106         .expect_prefer_cores   = CPU_TOPOLOGY_GENERIC(1, 1, 1, 1, 1),
107     }, {
108         /* config: -smp 8
109          * prefer_sockets: cpus=8,sockets=8,cores=1,threads=1,maxcpus=8
110          * prefer_cores: cpus=8,sockets=1,cores=8,threads=1,maxcpus=8 */
111         .config = SMP_CONFIG_GENERIC(T, 8, F, 0, F, 0, F, 0, F, 0),
112         .expect_prefer_sockets = CPU_TOPOLOGY_GENERIC(8, 8, 1, 1, 8),
113         .expect_prefer_cores   = CPU_TOPOLOGY_GENERIC(8, 1, 8, 1, 8),
114     }, {
115         /* config: -smp sockets=2
116          * expect: cpus=2,sockets=2,cores=1,threads=1,maxcpus=2 */
117         .config = SMP_CONFIG_GENERIC(F, 0, T, 2, F, 0, F, 0, F, 0),
118         .expect_prefer_sockets = CPU_TOPOLOGY_GENERIC(2, 2, 1, 1, 2),
119         .expect_prefer_cores   = CPU_TOPOLOGY_GENERIC(2, 2, 1, 1, 2),
120     }, {
121         /* config: -smp cores=4
122          * expect: cpus=4,sockets=1,cores=4,threads=1,maxcpus=4 */
123         .config = SMP_CONFIG_GENERIC(F, 0, F, 0, T, 4, F, 0, F, 0),
124         .expect_prefer_sockets = CPU_TOPOLOGY_GENERIC(4, 1, 4, 1, 4),
125         .expect_prefer_cores   = CPU_TOPOLOGY_GENERIC(4, 1, 4, 1, 4),
126     }, {
127         /* config: -smp threads=2
128          * expect: cpus=2,sockets=1,cores=1,threads=2,maxcpus=2 */
129         .config = SMP_CONFIG_GENERIC(F, 0, F, 0, F, 0, T, 2, F, 0),
130         .expect_prefer_sockets = CPU_TOPOLOGY_GENERIC(2, 1, 1, 2, 2),
131         .expect_prefer_cores   = CPU_TOPOLOGY_GENERIC(2, 1, 1, 2, 2),
132     }, {
133         /* config: -smp maxcpus=16
134          * prefer_sockets: cpus=16,sockets=16,cores=1,threads=1,maxcpus=16
135          * prefer_cores: cpus=16,sockets=1,cores=16,threads=1,maxcpus=16 */
136         .config = SMP_CONFIG_GENERIC(F, 0, F, 0, F, 0, F, 0, T, 16),
137         .expect_prefer_sockets = CPU_TOPOLOGY_GENERIC(16, 16, 1, 1, 16),
138         .expect_prefer_cores   = CPU_TOPOLOGY_GENERIC(16, 1, 16, 1, 16),
139     }, {
140         /* config: -smp 8,sockets=2
141          * expect: cpus=8,sockets=2,cores=4,threads=1,maxcpus=8 */
142         .config = SMP_CONFIG_GENERIC(T, 8, T, 2, F, 0, F, 0, F, 0),
143         .expect_prefer_sockets = CPU_TOPOLOGY_GENERIC(8, 2, 4, 1, 8),
144         .expect_prefer_cores   = CPU_TOPOLOGY_GENERIC(8, 2, 4, 1, 8),
145     }, {
146         /* config: -smp 8,cores=4
147          * expect: cpus=8,sockets=2,cores=4,threads=1,maxcpus=8 */
148         .config = SMP_CONFIG_GENERIC(T, 8, F, 0, T, 4, F, 0, F, 0),
149         .expect_prefer_sockets = CPU_TOPOLOGY_GENERIC(8, 2, 4, 1, 8),
150         .expect_prefer_cores   = CPU_TOPOLOGY_GENERIC(8, 2, 4, 1, 8),
151     }, {
152         /* config: -smp 8,threads=2
153          * prefer_sockets: cpus=8,sockets=4,cores=1,threads=2,maxcpus=8
154          * prefer_cores: cpus=8,sockets=1,cores=4,threads=2,maxcpus=8 */
155         .config = SMP_CONFIG_GENERIC(T, 8, F, 0, F, 0, T, 2, F, 0),
156         .expect_prefer_sockets = CPU_TOPOLOGY_GENERIC(8, 4, 1, 2, 8),
157         .expect_prefer_cores   = CPU_TOPOLOGY_GENERIC(8, 1, 4, 2, 8),
158     }, {
159         /* config: -smp 8,maxcpus=16
160          * prefer_sockets: cpus=8,sockets=16,cores=1,threads=1,maxcpus=16
161          * prefer_cores: cpus=8,sockets=1,cores=16,threads=1,maxcpus=16 */
162         .config = SMP_CONFIG_GENERIC(T, 8, F, 0, F, 0, F, 0, T, 16),
163         .expect_prefer_sockets = CPU_TOPOLOGY_GENERIC(8, 16, 1, 1, 16),
164         .expect_prefer_cores   = CPU_TOPOLOGY_GENERIC(8, 1, 16, 1, 16),
165     }, {
166         /* config: -smp sockets=2,cores=4
167          * expect: cpus=8,sockets=2,cores=4,threads=1,maxcpus=8 */
168         .config = SMP_CONFIG_GENERIC(F, 0, T, 2, T, 4, F, 0, F, 0),
169         .expect_prefer_sockets = CPU_TOPOLOGY_GENERIC(8, 2, 4, 1, 8),
170         .expect_prefer_cores   = CPU_TOPOLOGY_GENERIC(8, 2, 4, 1, 8),
171     }, {
172         /* config: -smp sockets=2,threads=2
173          * expect: cpus=4,sockets=2,cores=1,threads=2,maxcpus=4 */
174         .config = SMP_CONFIG_GENERIC(F, 0, T, 2, F, 0, T, 2, F, 0),
175         .expect_prefer_sockets = CPU_TOPOLOGY_GENERIC(4, 2, 1, 2, 4),
176         .expect_prefer_cores   = CPU_TOPOLOGY_GENERIC(4, 2, 1, 2, 4),
177     }, {
178         /* config: -smp sockets=2,maxcpus=16
179          * expect: cpus=16,sockets=2,cores=8,threads=1,maxcpus=16 */
180         .config = SMP_CONFIG_GENERIC(F, 0, T, 2, F, 0, F, 0, T, 16),
181         .expect_prefer_sockets = CPU_TOPOLOGY_GENERIC(16, 2, 8, 1, 16),
182         .expect_prefer_cores   = CPU_TOPOLOGY_GENERIC(16, 2, 8, 1, 16),
183     }, {
184         /* config: -smp cores=4,threads=2
185          * expect: cpus=8,sockets=1,cores=4,threads=2,maxcpus=8 */
186         .config = SMP_CONFIG_GENERIC(F, 0, F, 0, T, 4, T, 2, F, 0),
187         .expect_prefer_sockets = CPU_TOPOLOGY_GENERIC(8, 1, 4, 2, 8),
188         .expect_prefer_cores   = CPU_TOPOLOGY_GENERIC(8, 1, 4, 2, 8),
189     }, {
190         /* config: -smp cores=4,maxcpus=16
191          * expect: cpus=16,sockets=4,cores=4,threads=1,maxcpus=16 */
192         .config = SMP_CONFIG_GENERIC(F, 0, F, 0, T, 4, F, 0, T, 16),
193         .expect_prefer_sockets = CPU_TOPOLOGY_GENERIC(16, 4, 4, 1, 16),
194         .expect_prefer_cores   = CPU_TOPOLOGY_GENERIC(16, 4, 4, 1, 16),
195     }, {
196         /* config: -smp threads=2,maxcpus=16
197          * prefer_sockets: cpus=16,sockets=8,cores=1,threads=2,maxcpus=16
198          * prefer_cores: cpus=16,sockets=1,cores=8,threads=2,maxcpus=16 */
199         .config = SMP_CONFIG_GENERIC(F, 0, F, 0, F, 0, T, 2, T, 16),
200         .expect_prefer_sockets = CPU_TOPOLOGY_GENERIC(16, 8, 1, 2, 16),
201         .expect_prefer_cores   = CPU_TOPOLOGY_GENERIC(16, 1, 8, 2, 16),
202     }, {
203         /* config: -smp 8,sockets=2,cores=4
204          * expect: cpus=8,sockets=2,cores=4,threads=1,maxcpus=8 */
205         .config = SMP_CONFIG_GENERIC(T, 8, T, 2, T, 4, F, 0, F, 0),
206         .expect_prefer_sockets = CPU_TOPOLOGY_GENERIC(8, 2, 4, 1, 8),
207         .expect_prefer_cores   = CPU_TOPOLOGY_GENERIC(8, 2, 4, 1, 8),
208     }, {
209         /* config: -smp 8,sockets=2,threads=2
210          * expect: cpus=8,sockets=2,cores=2,threads=2,maxcpus=8 */
211         .config = SMP_CONFIG_GENERIC(T, 8, T, 2, F, 0, T, 2, F, 0),
212         .expect_prefer_sockets = CPU_TOPOLOGY_GENERIC(8, 2, 2, 2, 8),
213         .expect_prefer_cores   = CPU_TOPOLOGY_GENERIC(8, 2, 2, 2, 8),
214     }, {
215         /* config: -smp 8,sockets=2,maxcpus=16
216          * expect: cpus=8,sockets=2,cores=8,threads=1,maxcpus=16 */
217         .config = SMP_CONFIG_GENERIC(T, 8, T, 2, F, 0, F, 0, T, 16),
218         .expect_prefer_sockets = CPU_TOPOLOGY_GENERIC(8, 2, 8, 1, 16),
219         .expect_prefer_cores   = CPU_TOPOLOGY_GENERIC(8, 2, 8, 1, 16),
220     }, {
221         /* config: -smp 8,cores=4,threads=2
222          * expect: cpus=8,sockets=1,cores=4,threads=2,maxcpus=8 */
223         .config = SMP_CONFIG_GENERIC(T, 8, F, 0, T, 4, T, 2, F, 0),
224         .expect_prefer_sockets = CPU_TOPOLOGY_GENERIC(8, 1, 4, 2, 8),
225         .expect_prefer_cores   = CPU_TOPOLOGY_GENERIC(8, 1, 4, 2, 8),
226     }, {
227         /* config: -smp 8,cores=4,maxcpus=16
228          * expect: cpus=8,sockets=4,cores=4,threads=1,maxcpus=16 */
229         .config = SMP_CONFIG_GENERIC(T, 8, F, 0, T, 4, F, 0, T, 16),
230         .expect_prefer_sockets = CPU_TOPOLOGY_GENERIC(8, 4, 4, 1, 16),
231         .expect_prefer_cores   = CPU_TOPOLOGY_GENERIC(8, 4, 4, 1, 16),
232     }, {
233         /* config: -smp 8,threads=2,maxcpus=16
234          * prefer_sockets: cpus=8,sockets=8,cores=1,threads=2,maxcpus=16
235          * prefer_cores: cpus=8,sockets=1,cores=8,threads=2,maxcpus=16 */
236         .config = SMP_CONFIG_GENERIC(T, 8, F, 0, F, 0, T, 2, T, 16),
237         .expect_prefer_sockets = CPU_TOPOLOGY_GENERIC(8, 8, 1, 2, 16),
238         .expect_prefer_cores   = CPU_TOPOLOGY_GENERIC(8, 1, 8, 2, 16),
239     }, {
240         /* config: -smp sockets=2,cores=4,threads=2
241          * expect: cpus=16,sockets=2,cores=4,threads=2,maxcpus=16 */
242         .config = SMP_CONFIG_GENERIC(F, 0, T, 2, T, 4, T, 2, F, 0),
243         .expect_prefer_sockets = CPU_TOPOLOGY_GENERIC(16, 2, 4, 2, 16),
244         .expect_prefer_cores   = CPU_TOPOLOGY_GENERIC(16, 2, 4, 2, 16),
245     }, {
246         /* config: -smp sockets=2,cores=4,maxcpus=16
247          * expect: cpus=16,sockets=2,cores=4,threads=2,maxcpus=16 */
248         .config = SMP_CONFIG_GENERIC(F, 0, T, 2, T, 4, F, 0, T, 16),
249         .expect_prefer_sockets = CPU_TOPOLOGY_GENERIC(16, 2, 4, 2, 16),
250         .expect_prefer_cores   = CPU_TOPOLOGY_GENERIC(16, 2, 4, 2, 16),
251     }, {
252         /* config: -smp sockets=2,threads=2,maxcpus=16
253          * expect: cpus=16,sockets=2,cores=4,threads=2,maxcpus=16 */
254         .config = SMP_CONFIG_GENERIC(F, 0, T, 2, F, 0, T, 2, T, 16),
255         .expect_prefer_sockets = CPU_TOPOLOGY_GENERIC(16, 2, 4, 2, 16),
256         .expect_prefer_cores   = CPU_TOPOLOGY_GENERIC(16, 2, 4, 2, 16),
257     }, {
258         /* config: -smp cores=4,threads=2,maxcpus=16
259          * expect: cpus=16,sockets=2,cores=4,threads=2,maxcpus=16 */
260         .config = SMP_CONFIG_GENERIC(F, 0, F, 0, T, 4, T, 2, T, 16),
261         .expect_prefer_sockets = CPU_TOPOLOGY_GENERIC(16, 2, 4, 2, 16),
262         .expect_prefer_cores   = CPU_TOPOLOGY_GENERIC(16, 2, 4, 2, 16),
263     }, {
264         /* config: -smp 8,sockets=2,cores=4,threads=1
265          * expect: cpus=8,sockets=2,cores=4,threads=1,maxcpus=8 */
266         .config = SMP_CONFIG_GENERIC(T, 8, T, 2, T, 4, T, 1, F, 0),
267         .expect_prefer_sockets = CPU_TOPOLOGY_GENERIC(8, 2, 4, 1, 8),
268         .expect_prefer_cores   = CPU_TOPOLOGY_GENERIC(8, 2, 4, 1, 8),
269     }, {
270         /* config: -smp 8,sockets=2,cores=4,maxcpus=16
271          * expect: cpus=8,sockets=2,cores=4,threads=2,maxcpus=16 */
272         .config = SMP_CONFIG_GENERIC(T, 8, T, 2, T, 4, F, 0, T, 16),
273         .expect_prefer_sockets = CPU_TOPOLOGY_GENERIC(8, 2, 4, 2, 16),
274         .expect_prefer_cores   = CPU_TOPOLOGY_GENERIC(8, 2, 4, 2, 16),
275     }, {
276         /* config: -smp 8,sockets=2,threads=2,maxcpus=16
277          * expect: cpus=8,sockets=2,cores=4,threads=2,maxcpus=16 */
278         .config = SMP_CONFIG_GENERIC(T, 8, T, 2, F, 0, T, 2, T, 16),
279         .expect_prefer_sockets = CPU_TOPOLOGY_GENERIC(8, 2, 4, 2, 16),
280         .expect_prefer_cores   = CPU_TOPOLOGY_GENERIC(8, 2, 4, 2, 16),
281     }, {
282         /* config: -smp 8,cores=4,threads=2,maxcpus=16
283          * expect: cpus=8,sockets=2,cores=4,threads=2,maxcpus=16 */
284         .config = SMP_CONFIG_GENERIC(T, 8, F, 0, T, 4, T, 2, T, 16),
285         .expect_prefer_sockets = CPU_TOPOLOGY_GENERIC(8, 2, 4, 2, 16),
286         .expect_prefer_cores   = CPU_TOPOLOGY_GENERIC(8, 2, 4, 2, 16),
287     }, {
288         /* config: -smp sockets=2,cores=4,threads=2,maxcpus=16
289          * expect: cpus=16,sockets=2,cores=4,threads=2,maxcpus=16 */
290         .config = SMP_CONFIG_GENERIC(F, 0, T, 2, T, 4, T, 2, T, 16),
291         .expect_prefer_sockets = CPU_TOPOLOGY_GENERIC(16, 2, 4, 2, 16),
292         .expect_prefer_cores   = CPU_TOPOLOGY_GENERIC(16, 2, 4, 2, 16),
293     }, {
294         /* config: -smp 8,sockets=2,cores=4,threads=2,maxcpus=16
295          * expect: cpus=8,sockets=2,cores=4,threads=2,maxcpus=16 */
296         .config = SMP_CONFIG_GENERIC(T, 8, T, 2, T, 4, T, 2, T, 16),
297         .expect_prefer_sockets = CPU_TOPOLOGY_GENERIC(8, 2, 4, 2, 16),
298         .expect_prefer_cores   = CPU_TOPOLOGY_GENERIC(8, 2, 4, 2, 16),
299     },
300 };
301 
302 static const struct SMPTestData data_generic_invalid[] = {
303     {
304         /* config: -smp 2,dies=2 */
305         .config = SMP_CONFIG_WITH_DIES(T, 2, F, 0, T, 2, F, 0, F, 0, F, 0),
306         .expect_error = "dies not supported by this machine's CPU topology",
307     }, {
308         /* config: -smp 2,clusters=2 */
309         .config = SMP_CONFIG_WITH_CLUSTERS(T, 2, F, 0, T, 2, F, 0, F, 0, F, 0),
310         .expect_error = "clusters not supported by this machine's CPU topology",
311     }, {
312         /* config: -smp 8,sockets=2,cores=4,threads=2,maxcpus=8 */
313         .config = SMP_CONFIG_GENERIC(T, 8, T, 2, T, 4, T, 2, T, 8),
314         .expect_error = "Invalid CPU topology: "
315                         "product of the hierarchy must match maxcpus: "
316                         "sockets (2) * cores (4) * threads (2) "
317                         "!= maxcpus (8)",
318     }, {
319         /* config: -smp 18,sockets=2,cores=4,threads=2,maxcpus=16 */
320         .config = SMP_CONFIG_GENERIC(T, 18, T, 2, T, 4, T, 2, T, 16),
321         .expect_error = "Invalid CPU topology: "
322                         "maxcpus must be equal to or greater than smp: "
323                         "sockets (2) * cores (4) * threads (2) "
324                         "== maxcpus (16) < smp_cpus (18)",
325     }, {
326         /* config: -smp 1
327          * should tweak the supported min CPUs to 2 for testing */
328         .config = SMP_CONFIG_GENERIC(T, 1, F, 0, F, 0, F, 0, F, 0),
329         .expect_error = "Invalid SMP CPUs 1. The min CPUs supported "
330                         "by machine '" SMP_MACHINE_NAME "' is 2",
331     }, {
332         /* config: -smp 512
333          * should tweak the supported max CPUs to 511 for testing */
334         .config = SMP_CONFIG_GENERIC(T, 512, F, 0, F, 0, F, 0, F, 0),
335         .expect_error = "Invalid SMP CPUs 512. The max CPUs supported "
336                         "by machine '" SMP_MACHINE_NAME "' is 511",
337     },
338 };
339 
340 static const struct SMPTestData data_with_dies_invalid[] = {
341     {
342         /* config: -smp 16,sockets=2,dies=2,cores=4,threads=2,maxcpus=16 */
343         .config = SMP_CONFIG_WITH_DIES(T, 16, T, 2, T, 2, T, 4, T, 2, T, 16),
344         .expect_error = "Invalid CPU topology: "
345                         "product of the hierarchy must match maxcpus: "
346                         "sockets (2) * dies (2) * cores (4) * threads (2) "
347                         "!= maxcpus (16)",
348     }, {
349         /* config: -smp 34,sockets=2,dies=2,cores=4,threads=2,maxcpus=32 */
350         .config = SMP_CONFIG_WITH_DIES(T, 34, T, 2, T, 2, T, 4, T, 2, T, 32),
351         .expect_error = "Invalid CPU topology: "
352                         "maxcpus must be equal to or greater than smp: "
353                         "sockets (2) * dies (2) * cores (4) * threads (2) "
354                         "== maxcpus (32) < smp_cpus (34)",
355     },
356 };
357 
358 static const struct SMPTestData data_with_clusters_invalid[] = {
359     {
360         /* config: -smp 16,sockets=2,clusters=2,cores=4,threads=2,maxcpus=16 */
361         .config = SMP_CONFIG_WITH_CLUSTERS(T, 16, T, 2, T, 2, T, 4, T, 2, T, 16),
362         .expect_error = "Invalid CPU topology: "
363                         "product of the hierarchy must match maxcpus: "
364                         "sockets (2) * clusters (2) * cores (4) * threads (2) "
365                         "!= maxcpus (16)",
366     }, {
367         /* config: -smp 34,sockets=2,clusters=2,cores=4,threads=2,maxcpus=32 */
368         .config = SMP_CONFIG_WITH_CLUSTERS(T, 34, T, 2, T, 2, T, 4, T, 2, T, 32),
369         .expect_error = "Invalid CPU topology: "
370                         "maxcpus must be equal to or greater than smp: "
371                         "sockets (2) * clusters (2) * cores (4) * threads (2) "
372                         "== maxcpus (32) < smp_cpus (34)",
373     },
374 };
375 
376 static char *smp_config_to_string(const SMPConfiguration *config)
377 {
378     return g_strdup_printf(
379         "(SMPConfiguration) {\n"
380         "    .has_cpus     = %5s, cpus     = %" PRId64 ",\n"
381         "    .has_sockets  = %5s, sockets  = %" PRId64 ",\n"
382         "    .has_dies     = %5s, dies     = %" PRId64 ",\n"
383         "    .has_clusters = %5s, clusters = %" PRId64 ",\n"
384         "    .has_cores    = %5s, cores    = %" PRId64 ",\n"
385         "    .has_threads  = %5s, threads  = %" PRId64 ",\n"
386         "    .has_maxcpus  = %5s, maxcpus  = %" PRId64 ",\n"
387         "}",
388         config->has_cpus ? "true" : "false", config->cpus,
389         config->has_sockets ? "true" : "false", config->sockets,
390         config->has_dies ? "true" : "false", config->dies,
391         config->has_clusters ? "true" : "false", config->clusters,
392         config->has_cores ? "true" : "false", config->cores,
393         config->has_threads ? "true" : "false", config->threads,
394         config->has_maxcpus ? "true" : "false", config->maxcpus);
395 }
396 
397 /* Use the different calculation than machine_topo_get_threads_per_socket(). */
398 static unsigned int cpu_topology_get_threads_per_socket(const CpuTopology *topo)
399 {
400     /* Check the divisor to avoid invalid topology examples causing SIGFPE. */
401     if (!topo->sockets) {
402         return 0;
403     } else {
404         return topo->max_cpus / topo->sockets;
405     }
406 }
407 
408 /* Use the different calculation than machine_topo_get_cores_per_socket(). */
409 static unsigned int cpu_topology_get_cores_per_socket(const CpuTopology *topo)
410 {
411     /* Check the divisor to avoid invalid topology examples causing SIGFPE. */
412     if (!topo->threads) {
413         return 0;
414     } else {
415         return cpu_topology_get_threads_per_socket(topo) / topo->threads;
416     }
417 }
418 
419 static char *cpu_topology_to_string(const CpuTopology *topo,
420                                     unsigned int threads_per_socket,
421                                     unsigned int cores_per_socket)
422 {
423     return g_strdup_printf(
424         "(CpuTopology) {\n"
425         "    .cpus               = %u,\n"
426         "    .sockets            = %u,\n"
427         "    .dies               = %u,\n"
428         "    .clusters           = %u,\n"
429         "    .cores              = %u,\n"
430         "    .threads            = %u,\n"
431         "    .max_cpus           = %u,\n"
432         "    .threads_per_socket = %u,\n"
433         "    .cores_per_socket   = %u,\n"
434         "}",
435         topo->cpus, topo->sockets, topo->dies, topo->clusters,
436         topo->cores, topo->threads, topo->max_cpus,
437         threads_per_socket, cores_per_socket);
438 }
439 
440 static void check_parse(MachineState *ms, const SMPConfiguration *config,
441                         const CpuTopology *expect_topo, const char *expect_err,
442                         bool is_valid)
443 {
444     g_autofree char *config_str = smp_config_to_string(config);
445     g_autofree char *expect_topo_str = NULL, *output_topo_str = NULL;
446     unsigned int expect_threads_per_socket, expect_cores_per_socket;
447     unsigned int ms_threads_per_socket, ms_cores_per_socket;
448     Error *err = NULL;
449 
450     expect_threads_per_socket =
451                         cpu_topology_get_threads_per_socket(expect_topo);
452     expect_cores_per_socket =
453                         cpu_topology_get_cores_per_socket(expect_topo);
454     expect_topo_str = cpu_topology_to_string(expect_topo,
455                                              expect_threads_per_socket,
456                                              expect_cores_per_socket);
457 
458     /* call the generic parser */
459     machine_parse_smp_config(ms, config, &err);
460 
461     ms_threads_per_socket = machine_topo_get_threads_per_socket(ms);
462     ms_cores_per_socket = machine_topo_get_cores_per_socket(ms);
463     output_topo_str = cpu_topology_to_string(&ms->smp, ms_threads_per_socket,
464                                              ms_cores_per_socket);
465 
466     /* when the configuration is supposed to be valid */
467     if (is_valid) {
468         if ((err == NULL) &&
469             (ms->smp.cpus == expect_topo->cpus) &&
470             (ms->smp.sockets == expect_topo->sockets) &&
471             (ms->smp.dies == expect_topo->dies) &&
472             (ms->smp.clusters == expect_topo->clusters) &&
473             (ms->smp.cores == expect_topo->cores) &&
474             (ms->smp.threads == expect_topo->threads) &&
475             (ms->smp.max_cpus == expect_topo->max_cpus) &&
476             (ms_threads_per_socket == expect_threads_per_socket) &&
477             (ms_cores_per_socket == expect_cores_per_socket)) {
478             return;
479         }
480 
481         if (err != NULL) {
482             g_printerr("Test smp_parse failed!\n"
483                        "Input configuration: %s\n"
484                        "Should be valid: yes\n"
485                        "Expected topology: %s\n\n"
486                        "Result is valid: no\n"
487                        "Output error report: %s\n",
488                        config_str, expect_topo_str, error_get_pretty(err));
489             goto end;
490         }
491 
492         g_printerr("Test smp_parse failed!\n"
493                    "Input configuration: %s\n"
494                    "Should be valid: yes\n"
495                    "Expected topology: %s\n\n"
496                    "Result is valid: yes\n"
497                    "Output topology: %s\n",
498                    config_str, expect_topo_str, output_topo_str);
499         goto end;
500     }
501 
502     /* when the configuration is supposed to be invalid */
503     if (err != NULL) {
504         if (expect_err == NULL ||
505             g_str_equal(expect_err, error_get_pretty(err))) {
506             error_free(err);
507             return;
508         }
509 
510         g_printerr("Test smp_parse failed!\n"
511                    "Input configuration: %s\n"
512                    "Should be valid: no\n"
513                    "Expected error report: %s\n\n"
514                    "Result is valid: no\n"
515                    "Output error report: %s\n",
516                    config_str, expect_err, error_get_pretty(err));
517         goto end;
518     }
519 
520     g_printerr("Test smp_parse failed!\n"
521                "Input configuration: %s\n"
522                "Should be valid: no\n"
523                "Expected error report: %s\n\n"
524                "Result is valid: yes\n"
525                "Output topology: %s\n",
526                config_str, expect_err, output_topo_str);
527 
528 end:
529     if (err != NULL) {
530         error_free(err);
531     }
532 
533     abort();
534 }
535 
536 static void smp_parse_test(MachineState *ms, SMPTestData *data, bool is_valid)
537 {
538     MachineClass *mc = MACHINE_GET_CLASS(ms);
539 
540     mc->smp_props.prefer_sockets = true;
541     check_parse(ms, &data->config, &data->expect_prefer_sockets,
542                 data->expect_error, is_valid);
543 
544     mc->smp_props.prefer_sockets = false;
545     check_parse(ms, &data->config, &data->expect_prefer_cores,
546                 data->expect_error, is_valid);
547 }
548 
549 /* The parsed results of the unsupported parameters should be 1 */
550 static void unsupported_params_init(const MachineClass *mc, SMPTestData *data)
551 {
552     if (!mc->smp_props.dies_supported) {
553         data->expect_prefer_sockets.dies = 1;
554         data->expect_prefer_cores.dies = 1;
555     }
556 
557     if (!mc->smp_props.clusters_supported) {
558         data->expect_prefer_sockets.clusters = 1;
559         data->expect_prefer_cores.clusters = 1;
560     }
561 }
562 
563 static void machine_base_class_init(ObjectClass *oc, void *data)
564 {
565     MachineClass *mc = MACHINE_CLASS(oc);
566 
567     mc->min_cpus = MIN_CPUS;
568     mc->max_cpus = MAX_CPUS;
569 
570     mc->name = g_strdup(SMP_MACHINE_NAME);
571 }
572 
573 static void machine_generic_invalid_class_init(ObjectClass *oc, void *data)
574 {
575     MachineClass *mc = MACHINE_CLASS(oc);
576 
577     /* Force invalid min CPUs and max CPUs */
578     mc->min_cpus = 2;
579     mc->max_cpus = 511;
580 }
581 
582 static void machine_with_dies_class_init(ObjectClass *oc, void *data)
583 {
584     MachineClass *mc = MACHINE_CLASS(oc);
585 
586     mc->smp_props.dies_supported = true;
587 }
588 
589 static void machine_with_clusters_class_init(ObjectClass *oc, void *data)
590 {
591     MachineClass *mc = MACHINE_CLASS(oc);
592 
593     mc->smp_props.clusters_supported = true;
594 }
595 
596 static void test_generic_valid(const void *opaque)
597 {
598     const char *machine_type = opaque;
599     Object *obj = object_new(machine_type);
600     MachineState *ms = MACHINE(obj);
601     MachineClass *mc = MACHINE_GET_CLASS(obj);
602     SMPTestData data = {};
603     int i;
604 
605     for (i = 0; i < ARRAY_SIZE(data_generic_valid); i++) {
606         data = data_generic_valid[i];
607         unsupported_params_init(mc, &data);
608 
609         smp_parse_test(ms, &data, true);
610 
611         /* Unsupported parameters can be provided with their values as 1 */
612         data.config.has_dies = true;
613         data.config.dies = 1;
614         smp_parse_test(ms, &data, true);
615     }
616 
617     object_unref(obj);
618 }
619 
620 static void test_generic_invalid(const void *opaque)
621 {
622     const char *machine_type = opaque;
623     Object *obj = object_new(machine_type);
624     MachineState *ms = MACHINE(obj);
625     MachineClass *mc = MACHINE_GET_CLASS(obj);
626     SMPTestData data = {};
627     int i;
628 
629     for (i = 0; i < ARRAY_SIZE(data_generic_invalid); i++) {
630         data = data_generic_invalid[i];
631         unsupported_params_init(mc, &data);
632 
633         smp_parse_test(ms, &data, false);
634     }
635 
636     object_unref(obj);
637 }
638 
639 static void test_with_dies(const void *opaque)
640 {
641     const char *machine_type = opaque;
642     Object *obj = object_new(machine_type);
643     MachineState *ms = MACHINE(obj);
644     MachineClass *mc = MACHINE_GET_CLASS(obj);
645     SMPTestData data = {};
646     unsigned int num_dies = 2;
647     int i;
648 
649     for (i = 0; i < ARRAY_SIZE(data_generic_valid); i++) {
650         data = data_generic_valid[i];
651         unsupported_params_init(mc, &data);
652 
653         /* when dies parameter is omitted, it will be set as 1 */
654         data.expect_prefer_sockets.dies = 1;
655         data.expect_prefer_cores.dies = 1;
656 
657         smp_parse_test(ms, &data, true);
658 
659         /* when dies parameter is specified */
660         data.config.has_dies = true;
661         data.config.dies = num_dies;
662         if (data.config.has_cpus) {
663             data.config.cpus *= num_dies;
664         }
665         if (data.config.has_maxcpus) {
666             data.config.maxcpus *= num_dies;
667         }
668 
669         data.expect_prefer_sockets.dies = num_dies;
670         data.expect_prefer_sockets.cpus *= num_dies;
671         data.expect_prefer_sockets.max_cpus *= num_dies;
672         data.expect_prefer_cores.dies = num_dies;
673         data.expect_prefer_cores.cpus *= num_dies;
674         data.expect_prefer_cores.max_cpus *= num_dies;
675 
676         smp_parse_test(ms, &data, true);
677     }
678 
679     for (i = 0; i < ARRAY_SIZE(data_with_dies_invalid); i++) {
680         data = data_with_dies_invalid[i];
681         unsupported_params_init(mc, &data);
682 
683         smp_parse_test(ms, &data, false);
684     }
685 
686     object_unref(obj);
687 }
688 
689 static void test_with_clusters(const void *opaque)
690 {
691     const char *machine_type = opaque;
692     Object *obj = object_new(machine_type);
693     MachineState *ms = MACHINE(obj);
694     MachineClass *mc = MACHINE_GET_CLASS(obj);
695     SMPTestData data = {};
696     unsigned int num_clusters = 2;
697     int i;
698 
699     for (i = 0; i < ARRAY_SIZE(data_generic_valid); i++) {
700         data = data_generic_valid[i];
701         unsupported_params_init(mc, &data);
702 
703         /* when clusters parameter is omitted, it will be set as 1 */
704         data.expect_prefer_sockets.clusters = 1;
705         data.expect_prefer_cores.clusters = 1;
706 
707         smp_parse_test(ms, &data, true);
708 
709         /* when clusters parameter is specified */
710         data.config.has_clusters = true;
711         data.config.clusters = num_clusters;
712         if (data.config.has_cpus) {
713             data.config.cpus *= num_clusters;
714         }
715         if (data.config.has_maxcpus) {
716             data.config.maxcpus *= num_clusters;
717         }
718 
719         data.expect_prefer_sockets.clusters = num_clusters;
720         data.expect_prefer_sockets.cpus *= num_clusters;
721         data.expect_prefer_sockets.max_cpus *= num_clusters;
722         data.expect_prefer_cores.clusters = num_clusters;
723         data.expect_prefer_cores.cpus *= num_clusters;
724         data.expect_prefer_cores.max_cpus *= num_clusters;
725 
726         smp_parse_test(ms, &data, true);
727     }
728 
729     for (i = 0; i < ARRAY_SIZE(data_with_clusters_invalid); i++) {
730         data = data_with_clusters_invalid[i];
731         unsupported_params_init(mc, &data);
732 
733         smp_parse_test(ms, &data, false);
734     }
735 
736     object_unref(obj);
737 }
738 
739 /* Type info of the tested machine */
740 static const TypeInfo smp_machine_types[] = {
741     {
742         .name           = TYPE_MACHINE,
743         .parent         = TYPE_OBJECT,
744         .abstract       = true,
745         .class_init     = machine_base_class_init,
746         .class_size     = sizeof(MachineClass),
747         .instance_size  = sizeof(MachineState),
748     }, {
749         .name           = MACHINE_TYPE_NAME("smp-generic-valid"),
750         .parent         = TYPE_MACHINE,
751     }, {
752         .name           = MACHINE_TYPE_NAME("smp-generic-invalid"),
753         .parent         = TYPE_MACHINE,
754         .class_init     = machine_generic_invalid_class_init,
755     }, {
756         .name           = MACHINE_TYPE_NAME("smp-with-dies"),
757         .parent         = TYPE_MACHINE,
758         .class_init     = machine_with_dies_class_init,
759     }, {
760         .name           = MACHINE_TYPE_NAME("smp-with-clusters"),
761         .parent         = TYPE_MACHINE,
762         .class_init     = machine_with_clusters_class_init,
763     }
764 };
765 
766 DEFINE_TYPES(smp_machine_types)
767 
768 int main(int argc, char *argv[])
769 {
770     module_call_init(MODULE_INIT_QOM);
771 
772     g_test_init(&argc, &argv, NULL);
773 
774     g_test_add_data_func("/test-smp-parse/generic/valid",
775                          MACHINE_TYPE_NAME("smp-generic-valid"),
776                          test_generic_valid);
777     g_test_add_data_func("/test-smp-parse/generic/invalid",
778                          MACHINE_TYPE_NAME("smp-generic-invalid"),
779                          test_generic_invalid);
780     g_test_add_data_func("/test-smp-parse/with_dies",
781                          MACHINE_TYPE_NAME("smp-with-dies"),
782                          test_with_dies);
783     g_test_add_data_func("/test-smp-parse/with_clusters",
784                          MACHINE_TYPE_NAME("smp-with-clusters"),
785                          test_with_clusters);
786 
787     g_test_run();
788 
789     return 0;
790 }
791