xref: /qemu/tests/unit/test-smp-parse.c (revision b21e2380)
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 static char *cpu_topology_to_string(const CpuTopology *topo)
398 {
399     return g_strdup_printf(
400         "(CpuTopology) {\n"
401         "    .cpus     = %u,\n"
402         "    .sockets  = %u,\n"
403         "    .dies     = %u,\n"
404         "    .clusters = %u,\n"
405         "    .cores    = %u,\n"
406         "    .threads  = %u,\n"
407         "    .max_cpus = %u,\n"
408         "}",
409         topo->cpus, topo->sockets, topo->dies, topo->clusters,
410         topo->cores, topo->threads, topo->max_cpus);
411 }
412 
413 static void check_parse(MachineState *ms, const SMPConfiguration *config,
414                         const CpuTopology *expect_topo, const char *expect_err,
415                         bool is_valid)
416 {
417     g_autofree char *config_str = smp_config_to_string(config);
418     g_autofree char *expect_topo_str = cpu_topology_to_string(expect_topo);
419     g_autofree char *output_topo_str = NULL;
420     Error *err = NULL;
421 
422     /* call the generic parser */
423     machine_parse_smp_config(ms, config, &err);
424 
425     output_topo_str = cpu_topology_to_string(&ms->smp);
426 
427     /* when the configuration is supposed to be valid */
428     if (is_valid) {
429         if ((err == NULL) &&
430             (ms->smp.cpus == expect_topo->cpus) &&
431             (ms->smp.sockets == expect_topo->sockets) &&
432             (ms->smp.dies == expect_topo->dies) &&
433             (ms->smp.clusters == expect_topo->clusters) &&
434             (ms->smp.cores == expect_topo->cores) &&
435             (ms->smp.threads == expect_topo->threads) &&
436             (ms->smp.max_cpus == expect_topo->max_cpus)) {
437             return;
438         }
439 
440         if (err != NULL) {
441             g_printerr("Test smp_parse failed!\n"
442                        "Input configuration: %s\n"
443                        "Should be valid: yes\n"
444                        "Expected topology: %s\n\n"
445                        "Result is valid: no\n"
446                        "Output error report: %s\n",
447                        config_str, expect_topo_str, error_get_pretty(err));
448             goto end;
449         }
450 
451         g_printerr("Test smp_parse failed!\n"
452                    "Input configuration: %s\n"
453                    "Should be valid: yes\n"
454                    "Expected topology: %s\n\n"
455                    "Result is valid: yes\n"
456                    "Output topology: %s\n",
457                    config_str, expect_topo_str, output_topo_str);
458         goto end;
459     }
460 
461     /* when the configuration is supposed to be invalid */
462     if (err != NULL) {
463         if (expect_err == NULL ||
464             g_str_equal(expect_err, error_get_pretty(err))) {
465             error_free(err);
466             return;
467         }
468 
469         g_printerr("Test smp_parse failed!\n"
470                    "Input configuration: %s\n"
471                    "Should be valid: no\n"
472                    "Expected error report: %s\n\n"
473                    "Result is valid: no\n"
474                    "Output error report: %s\n",
475                    config_str, expect_err, error_get_pretty(err));
476         goto end;
477     }
478 
479     g_printerr("Test smp_parse failed!\n"
480                "Input configuration: %s\n"
481                "Should be valid: no\n"
482                "Expected error report: %s\n\n"
483                "Result is valid: yes\n"
484                "Output topology: %s\n",
485                config_str, expect_err, output_topo_str);
486 
487 end:
488     if (err != NULL) {
489         error_free(err);
490     }
491 
492     abort();
493 }
494 
495 static void smp_parse_test(MachineState *ms, SMPTestData *data, bool is_valid)
496 {
497     MachineClass *mc = MACHINE_GET_CLASS(ms);
498 
499     mc->smp_props.prefer_sockets = true;
500     check_parse(ms, &data->config, &data->expect_prefer_sockets,
501                 data->expect_error, is_valid);
502 
503     mc->smp_props.prefer_sockets = false;
504     check_parse(ms, &data->config, &data->expect_prefer_cores,
505                 data->expect_error, is_valid);
506 }
507 
508 /* The parsed results of the unsupported parameters should be 1 */
509 static void unsupported_params_init(const MachineClass *mc, SMPTestData *data)
510 {
511     if (!mc->smp_props.dies_supported) {
512         data->expect_prefer_sockets.dies = 1;
513         data->expect_prefer_cores.dies = 1;
514     }
515 
516     if (!mc->smp_props.clusters_supported) {
517         data->expect_prefer_sockets.clusters = 1;
518         data->expect_prefer_cores.clusters = 1;
519     }
520 }
521 
522 static void machine_base_class_init(ObjectClass *oc, void *data)
523 {
524     MachineClass *mc = MACHINE_CLASS(oc);
525 
526     mc->min_cpus = MIN_CPUS;
527     mc->max_cpus = MAX_CPUS;
528 
529     mc->name = g_strdup(SMP_MACHINE_NAME);
530 }
531 
532 static void machine_generic_invalid_class_init(ObjectClass *oc, void *data)
533 {
534     MachineClass *mc = MACHINE_CLASS(oc);
535 
536     /* Force invalid min CPUs and max CPUs */
537     mc->min_cpus = 2;
538     mc->max_cpus = 511;
539 }
540 
541 static void machine_with_dies_class_init(ObjectClass *oc, void *data)
542 {
543     MachineClass *mc = MACHINE_CLASS(oc);
544 
545     mc->smp_props.dies_supported = true;
546 }
547 
548 static void machine_with_clusters_class_init(ObjectClass *oc, void *data)
549 {
550     MachineClass *mc = MACHINE_CLASS(oc);
551 
552     mc->smp_props.clusters_supported = true;
553 }
554 
555 static void test_generic_valid(const void *opaque)
556 {
557     const char *machine_type = opaque;
558     Object *obj = object_new(machine_type);
559     MachineState *ms = MACHINE(obj);
560     MachineClass *mc = MACHINE_GET_CLASS(obj);
561     SMPTestData data = {};
562     int i;
563 
564     for (i = 0; i < ARRAY_SIZE(data_generic_valid); i++) {
565         data = data_generic_valid[i];
566         unsupported_params_init(mc, &data);
567 
568         smp_parse_test(ms, &data, true);
569 
570         /* Unsupported parameters can be provided with their values as 1 */
571         data.config.has_dies = true;
572         data.config.dies = 1;
573         smp_parse_test(ms, &data, true);
574     }
575 
576     object_unref(obj);
577 }
578 
579 static void test_generic_invalid(const void *opaque)
580 {
581     const char *machine_type = opaque;
582     Object *obj = object_new(machine_type);
583     MachineState *ms = MACHINE(obj);
584     MachineClass *mc = MACHINE_GET_CLASS(obj);
585     SMPTestData data = {};
586     int i;
587 
588     for (i = 0; i < ARRAY_SIZE(data_generic_invalid); i++) {
589         data = data_generic_invalid[i];
590         unsupported_params_init(mc, &data);
591 
592         smp_parse_test(ms, &data, false);
593     }
594 
595     object_unref(obj);
596 }
597 
598 static void test_with_dies(const void *opaque)
599 {
600     const char *machine_type = opaque;
601     Object *obj = object_new(machine_type);
602     MachineState *ms = MACHINE(obj);
603     MachineClass *mc = MACHINE_GET_CLASS(obj);
604     SMPTestData data = {};
605     unsigned int num_dies = 2;
606     int i;
607 
608     for (i = 0; i < ARRAY_SIZE(data_generic_valid); i++) {
609         data = data_generic_valid[i];
610         unsupported_params_init(mc, &data);
611 
612         /* when dies parameter is omitted, it will be set as 1 */
613         data.expect_prefer_sockets.dies = 1;
614         data.expect_prefer_cores.dies = 1;
615 
616         smp_parse_test(ms, &data, true);
617 
618         /* when dies parameter is specified */
619         data.config.has_dies = true;
620         data.config.dies = num_dies;
621         if (data.config.has_cpus) {
622             data.config.cpus *= num_dies;
623         }
624         if (data.config.has_maxcpus) {
625             data.config.maxcpus *= num_dies;
626         }
627 
628         data.expect_prefer_sockets.dies = num_dies;
629         data.expect_prefer_sockets.cpus *= num_dies;
630         data.expect_prefer_sockets.max_cpus *= num_dies;
631         data.expect_prefer_cores.dies = num_dies;
632         data.expect_prefer_cores.cpus *= num_dies;
633         data.expect_prefer_cores.max_cpus *= num_dies;
634 
635         smp_parse_test(ms, &data, true);
636     }
637 
638     for (i = 0; i < ARRAY_SIZE(data_with_dies_invalid); i++) {
639         data = data_with_dies_invalid[i];
640         unsupported_params_init(mc, &data);
641 
642         smp_parse_test(ms, &data, false);
643     }
644 
645     object_unref(obj);
646 }
647 
648 static void test_with_clusters(const void *opaque)
649 {
650     const char *machine_type = opaque;
651     Object *obj = object_new(machine_type);
652     MachineState *ms = MACHINE(obj);
653     MachineClass *mc = MACHINE_GET_CLASS(obj);
654     SMPTestData data = {};
655     unsigned int num_clusters = 2;
656     int i;
657 
658     for (i = 0; i < ARRAY_SIZE(data_generic_valid); i++) {
659         data = data_generic_valid[i];
660         unsupported_params_init(mc, &data);
661 
662         /* when clusters parameter is omitted, it will be set as 1 */
663         data.expect_prefer_sockets.clusters = 1;
664         data.expect_prefer_cores.clusters = 1;
665 
666         smp_parse_test(ms, &data, true);
667 
668         /* when clusters parameter is specified */
669         data.config.has_clusters = true;
670         data.config.clusters = num_clusters;
671         if (data.config.has_cpus) {
672             data.config.cpus *= num_clusters;
673         }
674         if (data.config.has_maxcpus) {
675             data.config.maxcpus *= num_clusters;
676         }
677 
678         data.expect_prefer_sockets.clusters = num_clusters;
679         data.expect_prefer_sockets.cpus *= num_clusters;
680         data.expect_prefer_sockets.max_cpus *= num_clusters;
681         data.expect_prefer_cores.clusters = num_clusters;
682         data.expect_prefer_cores.cpus *= num_clusters;
683         data.expect_prefer_cores.max_cpus *= num_clusters;
684 
685         smp_parse_test(ms, &data, true);
686     }
687 
688     for (i = 0; i < ARRAY_SIZE(data_with_clusters_invalid); i++) {
689         data = data_with_clusters_invalid[i];
690         unsupported_params_init(mc, &data);
691 
692         smp_parse_test(ms, &data, false);
693     }
694 
695     object_unref(obj);
696 }
697 
698 /* Type info of the tested machine */
699 static const TypeInfo smp_machine_types[] = {
700     {
701         .name           = TYPE_MACHINE,
702         .parent         = TYPE_OBJECT,
703         .abstract       = true,
704         .class_init     = machine_base_class_init,
705         .class_size     = sizeof(MachineClass),
706         .instance_size  = sizeof(MachineState),
707     }, {
708         .name           = MACHINE_TYPE_NAME("smp-generic-valid"),
709         .parent         = TYPE_MACHINE,
710     }, {
711         .name           = MACHINE_TYPE_NAME("smp-generic-invalid"),
712         .parent         = TYPE_MACHINE,
713         .class_init     = machine_generic_invalid_class_init,
714     }, {
715         .name           = MACHINE_TYPE_NAME("smp-with-dies"),
716         .parent         = TYPE_MACHINE,
717         .class_init     = machine_with_dies_class_init,
718     }, {
719         .name           = MACHINE_TYPE_NAME("smp-with-clusters"),
720         .parent         = TYPE_MACHINE,
721         .class_init     = machine_with_clusters_class_init,
722     }
723 };
724 
725 DEFINE_TYPES(smp_machine_types)
726 
727 int main(int argc, char *argv[])
728 {
729     module_call_init(MODULE_INIT_QOM);
730 
731     g_test_init(&argc, &argv, NULL);
732 
733     g_test_add_data_func("/test-smp-parse/generic/valid",
734                          MACHINE_TYPE_NAME("smp-generic-valid"),
735                          test_generic_valid);
736     g_test_add_data_func("/test-smp-parse/generic/invalid",
737                          MACHINE_TYPE_NAME("smp-generic-invalid"),
738                          test_generic_invalid);
739     g_test_add_data_func("/test-smp-parse/with_dies",
740                          MACHINE_TYPE_NAME("smp-with-dies"),
741                          test_with_dies);
742     g_test_add_data_func("/test-smp-parse/with_clusters",
743                          MACHINE_TYPE_NAME("smp-with-clusters"),
744                          test_with_clusters);
745 
746     g_test_run();
747 
748     return 0;
749 }
750