2 * SMP parsing unit-tests
4 * Copyright (c) 2021 Huawei Technologies Co., Ltd
7 * Yanan Wang <wangyanan55@huawei.com>
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.
13 #include "qemu/osdep.h"
14 #include "qom/object.h"
15 #include "qemu/module.h"
16 #include "qapi/error.h"
18 #include "hw/boards.h"
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 */
26 #define SMP_MACHINE_NAME "TEST-SMP"
29 * Used to define the generic 3-level CPU topology hierarchy
30 * -sockets/cores/threads
32 #define SMP_CONFIG_GENERIC(ha, a, hb, b, hc, c, hd, d, he, e) \
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, \
41 #define CPU_TOPOLOGY_GENERIC(a, b, c, d, e) \
51 * Currently a 4-level topology hierarchy is supported on PC machines
52 * -sockets/dies/cores/threads
54 #define SMP_CONFIG_WITH_DIES(ha, a, hb, b, hc, c, hd, d, he, e, hf, f) \
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, \
65 * Currently a 4-level topology hierarchy is supported on ARM virt machines
66 * -sockets/clusters/cores/threads
68 #define SMP_CONFIG_WITH_CLUSTERS(ha, a, hb, b, hc, c, hd, d, he, e, hf, f) \
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, \
79 * Currently a 5-level topology hierarchy is supported on s390 ccw machines
80 * -drawers/books/sockets/cores/threads
82 #define SMP_CONFIG_WITH_BOOKS_DRAWERS(ha, a, hb, b, hc, c, hd, \
83 d, he, e, hf, f, hg, g) \
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, \
95 * @config - the given SMP configuration
96 * @expect_prefer_sockets - the expected parsing result for the
97 * valid configuration, when sockets are preferred over cores
98 * @expect_prefer_cores - the expected parsing result for the
99 * valid configuration, when cores are preferred over sockets
100 * @expect_error - the expected error report when the given
101 * configuration is invalid
103 typedef struct SMPTestData
{
104 SMPConfiguration config
;
105 CpuTopology expect_prefer_sockets
;
106 CpuTopology expect_prefer_cores
;
107 const char *expect_error
;
111 * List all the possible valid sub-collections of the generic 5
112 * topology parameters (i.e. cpus/maxcpus/sockets/cores/threads),
113 * then test the automatic calculation algorithm of the missing
114 * values in the parser.
116 static const struct SMPTestData data_generic_valid
[] = {
118 /* config: no configuration provided
119 * expect: cpus=1,sockets=1,cores=1,threads=1,maxcpus=1 */
120 .config
= SMP_CONFIG_GENERIC(F
, 0, F
, 0, F
, 0, F
, 0, F
, 0),
121 .expect_prefer_sockets
= CPU_TOPOLOGY_GENERIC(1, 1, 1, 1, 1),
122 .expect_prefer_cores
= CPU_TOPOLOGY_GENERIC(1, 1, 1, 1, 1),
125 * prefer_sockets: cpus=8,sockets=8,cores=1,threads=1,maxcpus=8
126 * prefer_cores: cpus=8,sockets=1,cores=8,threads=1,maxcpus=8 */
127 .config
= SMP_CONFIG_GENERIC(T
, 8, F
, 0, F
, 0, F
, 0, F
, 0),
128 .expect_prefer_sockets
= CPU_TOPOLOGY_GENERIC(8, 8, 1, 1, 8),
129 .expect_prefer_cores
= CPU_TOPOLOGY_GENERIC(8, 1, 8, 1, 8),
131 /* config: -smp sockets=2
132 * expect: cpus=2,sockets=2,cores=1,threads=1,maxcpus=2 */
133 .config
= SMP_CONFIG_GENERIC(F
, 0, T
, 2, F
, 0, F
, 0, F
, 0),
134 .expect_prefer_sockets
= CPU_TOPOLOGY_GENERIC(2, 2, 1, 1, 2),
135 .expect_prefer_cores
= CPU_TOPOLOGY_GENERIC(2, 2, 1, 1, 2),
137 /* config: -smp cores=4
138 * expect: cpus=4,sockets=1,cores=4,threads=1,maxcpus=4 */
139 .config
= SMP_CONFIG_GENERIC(F
, 0, F
, 0, T
, 4, F
, 0, F
, 0),
140 .expect_prefer_sockets
= CPU_TOPOLOGY_GENERIC(4, 1, 4, 1, 4),
141 .expect_prefer_cores
= CPU_TOPOLOGY_GENERIC(4, 1, 4, 1, 4),
143 /* config: -smp threads=2
144 * expect: cpus=2,sockets=1,cores=1,threads=2,maxcpus=2 */
145 .config
= SMP_CONFIG_GENERIC(F
, 0, F
, 0, F
, 0, T
, 2, F
, 0),
146 .expect_prefer_sockets
= CPU_TOPOLOGY_GENERIC(2, 1, 1, 2, 2),
147 .expect_prefer_cores
= CPU_TOPOLOGY_GENERIC(2, 1, 1, 2, 2),
149 /* config: -smp maxcpus=16
150 * prefer_sockets: cpus=16,sockets=16,cores=1,threads=1,maxcpus=16
151 * prefer_cores: cpus=16,sockets=1,cores=16,threads=1,maxcpus=16 */
152 .config
= SMP_CONFIG_GENERIC(F
, 0, F
, 0, F
, 0, F
, 0, T
, 16),
153 .expect_prefer_sockets
= CPU_TOPOLOGY_GENERIC(16, 16, 1, 1, 16),
154 .expect_prefer_cores
= CPU_TOPOLOGY_GENERIC(16, 1, 16, 1, 16),
156 /* config: -smp 8,sockets=2
157 * expect: cpus=8,sockets=2,cores=4,threads=1,maxcpus=8 */
158 .config
= SMP_CONFIG_GENERIC(T
, 8, T
, 2, F
, 0, F
, 0, F
, 0),
159 .expect_prefer_sockets
= CPU_TOPOLOGY_GENERIC(8, 2, 4, 1, 8),
160 .expect_prefer_cores
= CPU_TOPOLOGY_GENERIC(8, 2, 4, 1, 8),
162 /* config: -smp 8,cores=4
163 * expect: cpus=8,sockets=2,cores=4,threads=1,maxcpus=8 */
164 .config
= SMP_CONFIG_GENERIC(T
, 8, F
, 0, T
, 4, F
, 0, F
, 0),
165 .expect_prefer_sockets
= CPU_TOPOLOGY_GENERIC(8, 2, 4, 1, 8),
166 .expect_prefer_cores
= CPU_TOPOLOGY_GENERIC(8, 2, 4, 1, 8),
168 /* config: -smp 8,threads=2
169 * prefer_sockets: cpus=8,sockets=4,cores=1,threads=2,maxcpus=8
170 * prefer_cores: cpus=8,sockets=1,cores=4,threads=2,maxcpus=8 */
171 .config
= SMP_CONFIG_GENERIC(T
, 8, F
, 0, F
, 0, T
, 2, F
, 0),
172 .expect_prefer_sockets
= CPU_TOPOLOGY_GENERIC(8, 4, 1, 2, 8),
173 .expect_prefer_cores
= CPU_TOPOLOGY_GENERIC(8, 1, 4, 2, 8),
175 /* config: -smp 8,maxcpus=16
176 * prefer_sockets: cpus=8,sockets=16,cores=1,threads=1,maxcpus=16
177 * prefer_cores: cpus=8,sockets=1,cores=16,threads=1,maxcpus=16 */
178 .config
= SMP_CONFIG_GENERIC(T
, 8, F
, 0, F
, 0, F
, 0, T
, 16),
179 .expect_prefer_sockets
= CPU_TOPOLOGY_GENERIC(8, 16, 1, 1, 16),
180 .expect_prefer_cores
= CPU_TOPOLOGY_GENERIC(8, 1, 16, 1, 16),
182 /* config: -smp sockets=2,cores=4
183 * expect: cpus=8,sockets=2,cores=4,threads=1,maxcpus=8 */
184 .config
= SMP_CONFIG_GENERIC(F
, 0, T
, 2, T
, 4, F
, 0, F
, 0),
185 .expect_prefer_sockets
= CPU_TOPOLOGY_GENERIC(8, 2, 4, 1, 8),
186 .expect_prefer_cores
= CPU_TOPOLOGY_GENERIC(8, 2, 4, 1, 8),
188 /* config: -smp sockets=2,threads=2
189 * expect: cpus=4,sockets=2,cores=1,threads=2,maxcpus=4 */
190 .config
= SMP_CONFIG_GENERIC(F
, 0, T
, 2, F
, 0, T
, 2, F
, 0),
191 .expect_prefer_sockets
= CPU_TOPOLOGY_GENERIC(4, 2, 1, 2, 4),
192 .expect_prefer_cores
= CPU_TOPOLOGY_GENERIC(4, 2, 1, 2, 4),
194 /* config: -smp sockets=2,maxcpus=16
195 * expect: cpus=16,sockets=2,cores=8,threads=1,maxcpus=16 */
196 .config
= SMP_CONFIG_GENERIC(F
, 0, T
, 2, F
, 0, F
, 0, T
, 16),
197 .expect_prefer_sockets
= CPU_TOPOLOGY_GENERIC(16, 2, 8, 1, 16),
198 .expect_prefer_cores
= CPU_TOPOLOGY_GENERIC(16, 2, 8, 1, 16),
200 /* config: -smp cores=4,threads=2
201 * expect: cpus=8,sockets=1,cores=4,threads=2,maxcpus=8 */
202 .config
= SMP_CONFIG_GENERIC(F
, 0, F
, 0, T
, 4, T
, 2, F
, 0),
203 .expect_prefer_sockets
= CPU_TOPOLOGY_GENERIC(8, 1, 4, 2, 8),
204 .expect_prefer_cores
= CPU_TOPOLOGY_GENERIC(8, 1, 4, 2, 8),
206 /* config: -smp cores=4,maxcpus=16
207 * expect: cpus=16,sockets=4,cores=4,threads=1,maxcpus=16 */
208 .config
= SMP_CONFIG_GENERIC(F
, 0, F
, 0, T
, 4, F
, 0, T
, 16),
209 .expect_prefer_sockets
= CPU_TOPOLOGY_GENERIC(16, 4, 4, 1, 16),
210 .expect_prefer_cores
= CPU_TOPOLOGY_GENERIC(16, 4, 4, 1, 16),
212 /* config: -smp threads=2,maxcpus=16
213 * prefer_sockets: cpus=16,sockets=8,cores=1,threads=2,maxcpus=16
214 * prefer_cores: cpus=16,sockets=1,cores=8,threads=2,maxcpus=16 */
215 .config
= SMP_CONFIG_GENERIC(F
, 0, F
, 0, F
, 0, T
, 2, T
, 16),
216 .expect_prefer_sockets
= CPU_TOPOLOGY_GENERIC(16, 8, 1, 2, 16),
217 .expect_prefer_cores
= CPU_TOPOLOGY_GENERIC(16, 1, 8, 2, 16),
219 /* config: -smp 8,sockets=2,cores=4
220 * expect: cpus=8,sockets=2,cores=4,threads=1,maxcpus=8 */
221 .config
= SMP_CONFIG_GENERIC(T
, 8, T
, 2, T
, 4, F
, 0, F
, 0),
222 .expect_prefer_sockets
= CPU_TOPOLOGY_GENERIC(8, 2, 4, 1, 8),
223 .expect_prefer_cores
= CPU_TOPOLOGY_GENERIC(8, 2, 4, 1, 8),
225 /* config: -smp 8,sockets=2,threads=2
226 * expect: cpus=8,sockets=2,cores=2,threads=2,maxcpus=8 */
227 .config
= SMP_CONFIG_GENERIC(T
, 8, T
, 2, F
, 0, T
, 2, F
, 0),
228 .expect_prefer_sockets
= CPU_TOPOLOGY_GENERIC(8, 2, 2, 2, 8),
229 .expect_prefer_cores
= CPU_TOPOLOGY_GENERIC(8, 2, 2, 2, 8),
231 /* config: -smp 8,sockets=2,maxcpus=16
232 * expect: cpus=8,sockets=2,cores=8,threads=1,maxcpus=16 */
233 .config
= SMP_CONFIG_GENERIC(T
, 8, T
, 2, F
, 0, F
, 0, T
, 16),
234 .expect_prefer_sockets
= CPU_TOPOLOGY_GENERIC(8, 2, 8, 1, 16),
235 .expect_prefer_cores
= CPU_TOPOLOGY_GENERIC(8, 2, 8, 1, 16),
237 /* config: -smp 8,cores=4,threads=2
238 * expect: cpus=8,sockets=1,cores=4,threads=2,maxcpus=8 */
239 .config
= SMP_CONFIG_GENERIC(T
, 8, F
, 0, T
, 4, T
, 2, F
, 0),
240 .expect_prefer_sockets
= CPU_TOPOLOGY_GENERIC(8, 1, 4, 2, 8),
241 .expect_prefer_cores
= CPU_TOPOLOGY_GENERIC(8, 1, 4, 2, 8),
243 /* config: -smp 8,cores=4,maxcpus=16
244 * expect: cpus=8,sockets=4,cores=4,threads=1,maxcpus=16 */
245 .config
= SMP_CONFIG_GENERIC(T
, 8, F
, 0, T
, 4, F
, 0, T
, 16),
246 .expect_prefer_sockets
= CPU_TOPOLOGY_GENERIC(8, 4, 4, 1, 16),
247 .expect_prefer_cores
= CPU_TOPOLOGY_GENERIC(8, 4, 4, 1, 16),
249 /* config: -smp 8,threads=2,maxcpus=16
250 * prefer_sockets: cpus=8,sockets=8,cores=1,threads=2,maxcpus=16
251 * prefer_cores: cpus=8,sockets=1,cores=8,threads=2,maxcpus=16 */
252 .config
= SMP_CONFIG_GENERIC(T
, 8, F
, 0, F
, 0, T
, 2, T
, 16),
253 .expect_prefer_sockets
= CPU_TOPOLOGY_GENERIC(8, 8, 1, 2, 16),
254 .expect_prefer_cores
= CPU_TOPOLOGY_GENERIC(8, 1, 8, 2, 16),
256 /* config: -smp sockets=2,cores=4,threads=2
257 * expect: cpus=16,sockets=2,cores=4,threads=2,maxcpus=16 */
258 .config
= SMP_CONFIG_GENERIC(F
, 0, T
, 2, T
, 4, T
, 2, F
, 0),
259 .expect_prefer_sockets
= CPU_TOPOLOGY_GENERIC(16, 2, 4, 2, 16),
260 .expect_prefer_cores
= CPU_TOPOLOGY_GENERIC(16, 2, 4, 2, 16),
262 /* config: -smp sockets=2,cores=4,maxcpus=16
263 * expect: cpus=16,sockets=2,cores=4,threads=2,maxcpus=16 */
264 .config
= SMP_CONFIG_GENERIC(F
, 0, T
, 2, T
, 4, F
, 0, T
, 16),
265 .expect_prefer_sockets
= CPU_TOPOLOGY_GENERIC(16, 2, 4, 2, 16),
266 .expect_prefer_cores
= CPU_TOPOLOGY_GENERIC(16, 2, 4, 2, 16),
268 /* config: -smp sockets=2,threads=2,maxcpus=16
269 * expect: cpus=16,sockets=2,cores=4,threads=2,maxcpus=16 */
270 .config
= SMP_CONFIG_GENERIC(F
, 0, T
, 2, F
, 0, T
, 2, T
, 16),
271 .expect_prefer_sockets
= CPU_TOPOLOGY_GENERIC(16, 2, 4, 2, 16),
272 .expect_prefer_cores
= CPU_TOPOLOGY_GENERIC(16, 2, 4, 2, 16),
274 /* config: -smp cores=4,threads=2,maxcpus=16
275 * expect: cpus=16,sockets=2,cores=4,threads=2,maxcpus=16 */
276 .config
= SMP_CONFIG_GENERIC(F
, 0, F
, 0, T
, 4, T
, 2, T
, 16),
277 .expect_prefer_sockets
= CPU_TOPOLOGY_GENERIC(16, 2, 4, 2, 16),
278 .expect_prefer_cores
= CPU_TOPOLOGY_GENERIC(16, 2, 4, 2, 16),
280 /* config: -smp 8,sockets=2,cores=4,threads=1
281 * expect: cpus=8,sockets=2,cores=4,threads=1,maxcpus=8 */
282 .config
= SMP_CONFIG_GENERIC(T
, 8, T
, 2, T
, 4, T
, 1, F
, 0),
283 .expect_prefer_sockets
= CPU_TOPOLOGY_GENERIC(8, 2, 4, 1, 8),
284 .expect_prefer_cores
= CPU_TOPOLOGY_GENERIC(8, 2, 4, 1, 8),
286 /* config: -smp 8,sockets=2,cores=4,maxcpus=16
287 * expect: cpus=8,sockets=2,cores=4,threads=2,maxcpus=16 */
288 .config
= SMP_CONFIG_GENERIC(T
, 8, T
, 2, T
, 4, F
, 0, T
, 16),
289 .expect_prefer_sockets
= CPU_TOPOLOGY_GENERIC(8, 2, 4, 2, 16),
290 .expect_prefer_cores
= CPU_TOPOLOGY_GENERIC(8, 2, 4, 2, 16),
292 /* config: -smp 8,sockets=2,threads=2,maxcpus=16
293 * expect: cpus=8,sockets=2,cores=4,threads=2,maxcpus=16 */
294 .config
= SMP_CONFIG_GENERIC(T
, 8, T
, 2, F
, 0, T
, 2, T
, 16),
295 .expect_prefer_sockets
= CPU_TOPOLOGY_GENERIC(8, 2, 4, 2, 16),
296 .expect_prefer_cores
= CPU_TOPOLOGY_GENERIC(8, 2, 4, 2, 16),
298 /* config: -smp 8,cores=4,threads=2,maxcpus=16
299 * expect: cpus=8,sockets=2,cores=4,threads=2,maxcpus=16 */
300 .config
= SMP_CONFIG_GENERIC(T
, 8, F
, 0, T
, 4, T
, 2, T
, 16),
301 .expect_prefer_sockets
= CPU_TOPOLOGY_GENERIC(8, 2, 4, 2, 16),
302 .expect_prefer_cores
= CPU_TOPOLOGY_GENERIC(8, 2, 4, 2, 16),
304 /* config: -smp sockets=2,cores=4,threads=2,maxcpus=16
305 * expect: cpus=16,sockets=2,cores=4,threads=2,maxcpus=16 */
306 .config
= SMP_CONFIG_GENERIC(F
, 0, T
, 2, T
, 4, T
, 2, T
, 16),
307 .expect_prefer_sockets
= CPU_TOPOLOGY_GENERIC(16, 2, 4, 2, 16),
308 .expect_prefer_cores
= CPU_TOPOLOGY_GENERIC(16, 2, 4, 2, 16),
310 /* config: -smp 8,sockets=2,cores=4,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, T
, 4, 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),
318 static const struct SMPTestData data_generic_invalid
[] = {
320 /* config: -smp 2,dies=2 */
321 .config
= SMP_CONFIG_WITH_DIES(T
, 2, F
, 0, T
, 2, F
, 0, F
, 0, F
, 0),
322 .expect_error
= "dies not supported by this machine's CPU topology",
324 /* config: -smp 2,clusters=2 */
325 .config
= SMP_CONFIG_WITH_CLUSTERS(T
, 2, F
, 0, T
, 2, F
, 0, F
, 0, F
, 0),
326 .expect_error
= "clusters not supported by this machine's CPU topology",
328 /* config: -smp 2,books=2 */
329 .config
= SMP_CONFIG_WITH_BOOKS_DRAWERS(T
, 2, F
, 0, T
, 2, F
,
330 0, F
, 0, F
, 0, F
, 0),
331 .expect_error
= "books not supported by this machine's CPU topology",
333 /* config: -smp 2,drawers=2 */
334 .config
= SMP_CONFIG_WITH_BOOKS_DRAWERS(T
, 2, T
, 2, F
, 0, F
,
335 0, F
, 0, F
, 0, F
, 0),
336 .expect_error
= "drawers not supported by this machine's CPU topology",
338 /* config: -smp 8,sockets=2,cores=4,threads=2,maxcpus=8 */
339 .config
= SMP_CONFIG_GENERIC(T
, 8, T
, 2, T
, 4, T
, 2, T
, 8),
340 .expect_error
= "Invalid CPU topology: "
341 "product of the hierarchy must match maxcpus: "
342 "sockets (2) * cores (4) * threads (2) "
345 /* config: -smp 18,sockets=2,cores=4,threads=2,maxcpus=16 */
346 .config
= SMP_CONFIG_GENERIC(T
, 18, T
, 2, T
, 4, T
, 2, T
, 16),
347 .expect_error
= "Invalid CPU topology: "
348 "maxcpus must be equal to or greater than smp: "
349 "sockets (2) * cores (4) * threads (2) "
350 "== maxcpus (16) < smp_cpus (18)",
354 * The test machine should tweak the supported min CPUs to
355 * 2 (MIN_CPUS + 1) for testing.
357 .config
= SMP_CONFIG_GENERIC(T
, MIN_CPUS
, F
, 0, F
, 0, F
, 0, F
, 0),
358 .expect_error
= "Invalid SMP CPUs 1. The min CPUs supported "
359 "by machine '" SMP_MACHINE_NAME
"' is 2",
363 * The test machine should tweak the supported max CPUs to
364 * 4095 (MAX_CPUS - 1) for testing.
366 .config
= SMP_CONFIG_GENERIC(T
, 4096, F
, 0, F
, 0, F
, 0, F
, 0),
367 .expect_error
= "Invalid SMP CPUs 4096. The max CPUs supported "
368 "by machine '" SMP_MACHINE_NAME
"' is 4095",
372 static const struct SMPTestData data_with_dies_invalid
[] = {
374 /* config: -smp 16,sockets=2,dies=2,cores=4,threads=2,maxcpus=16 */
375 .config
= SMP_CONFIG_WITH_DIES(T
, 16, T
, 2, T
, 2, T
, 4, T
, 2, T
, 16),
376 .expect_error
= "Invalid CPU topology: "
377 "product of the hierarchy must match maxcpus: "
378 "sockets (2) * dies (2) * cores (4) * threads (2) "
381 /* config: -smp 34,sockets=2,dies=2,cores=4,threads=2,maxcpus=32 */
382 .config
= SMP_CONFIG_WITH_DIES(T
, 34, T
, 2, T
, 2, T
, 4, T
, 2, T
, 32),
383 .expect_error
= "Invalid CPU topology: "
384 "maxcpus must be equal to or greater than smp: "
385 "sockets (2) * dies (2) * cores (4) * threads (2) "
386 "== maxcpus (32) < smp_cpus (34)",
390 static const struct SMPTestData data_with_clusters_invalid
[] = {
392 /* config: -smp 16,sockets=2,clusters=2,cores=4,threads=2,maxcpus=16 */
393 .config
= SMP_CONFIG_WITH_CLUSTERS(T
, 16, T
, 2, T
, 2, T
, 4, T
, 2, T
, 16),
394 .expect_error
= "Invalid CPU topology: "
395 "product of the hierarchy must match maxcpus: "
396 "sockets (2) * clusters (2) * cores (4) * threads (2) "
399 /* config: -smp 34,sockets=2,clusters=2,cores=4,threads=2,maxcpus=32 */
400 .config
= SMP_CONFIG_WITH_CLUSTERS(T
, 34, T
, 2, T
, 2, T
, 4, T
, 2, T
, 32),
401 .expect_error
= "Invalid CPU topology: "
402 "maxcpus must be equal to or greater than smp: "
403 "sockets (2) * clusters (2) * cores (4) * threads (2) "
404 "== maxcpus (32) < smp_cpus (34)",
408 static const struct SMPTestData data_with_books_invalid
[] = {
410 /* config: -smp 16,books=2,sockets=2,cores=4,threads=2,maxcpus=16 */
411 .config
= SMP_CONFIG_WITH_BOOKS_DRAWERS(T
, 16, F
, 1, T
, 2, T
,
412 2, T
, 4, T
, 2, T
, 16),
413 .expect_error
= "Invalid CPU topology: "
414 "product of the hierarchy must match maxcpus: "
415 "books (2) * sockets (2) * cores (4) * threads (2) "
418 /* config: -smp 34,books=2,sockets=2,cores=4,threads=2,maxcpus=32 */
419 .config
= SMP_CONFIG_WITH_BOOKS_DRAWERS(T
, 34, F
, 1, T
, 2, T
,
420 2, T
, 4, T
, 2, T
, 32),
421 .expect_error
= "Invalid CPU topology: "
422 "maxcpus must be equal to or greater than smp: "
423 "books (2) * sockets (2) * cores (4) * threads (2) "
424 "== maxcpus (32) < smp_cpus (34)",
428 static const struct SMPTestData data_with_drawers_invalid
[] = {
430 /* config: -smp 16,drawers=2,sockets=2,cores=4,threads=2,maxcpus=16 */
431 .config
= SMP_CONFIG_WITH_BOOKS_DRAWERS(T
, 16, T
, 2, F
, 1, T
,
432 2, T
, 4, T
, 2, T
, 16),
433 .expect_error
= "Invalid CPU topology: "
434 "product of the hierarchy must match maxcpus: "
435 "drawers (2) * sockets (2) * cores (4) * threads (2) "
438 /* config: -smp 34,drawers=2,sockets=2,cores=4,threads=2,maxcpus=32 */
439 .config
= SMP_CONFIG_WITH_BOOKS_DRAWERS(T
, 34, T
, 2, F
, 1, T
,
440 2, T
, 4, T
, 2, T
, 32),
441 .expect_error
= "Invalid CPU topology: "
442 "maxcpus must be equal to or greater than smp: "
443 "drawers (2) * sockets (2) * cores (4) * threads (2) "
444 "== maxcpus (32) < smp_cpus (34)",
448 static char *smp_config_to_string(const SMPConfiguration
*config
)
450 return g_strdup_printf(
451 "(SMPConfiguration) {\n"
452 " .has_cpus = %5s, cpus = %" PRId64
",\n"
453 " .has_drawers = %5s, drawers = %" PRId64
",\n"
454 " .has_books = %5s, books = %" PRId64
",\n"
455 " .has_sockets = %5s, sockets = %" PRId64
",\n"
456 " .has_dies = %5s, dies = %" PRId64
",\n"
457 " .has_clusters = %5s, clusters = %" PRId64
",\n"
458 " .has_cores = %5s, cores = %" PRId64
",\n"
459 " .has_threads = %5s, threads = %" PRId64
",\n"
460 " .has_maxcpus = %5s, maxcpus = %" PRId64
",\n"
462 config
->has_cpus
? "true" : "false", config
->cpus
,
463 config
->has_drawers
? "true" : "false", config
->drawers
,
464 config
->has_books
? "true" : "false", config
->books
,
465 config
->has_sockets
? "true" : "false", config
->sockets
,
466 config
->has_dies
? "true" : "false", config
->dies
,
467 config
->has_clusters
? "true" : "false", config
->clusters
,
468 config
->has_cores
? "true" : "false", config
->cores
,
469 config
->has_threads
? "true" : "false", config
->threads
,
470 config
->has_maxcpus
? "true" : "false", config
->maxcpus
);
473 /* Use the different calculation than machine_topo_get_threads_per_socket(). */
474 static unsigned int cpu_topology_get_threads_per_socket(const CpuTopology
*topo
)
476 /* Check the divisor to avoid invalid topology examples causing SIGFPE. */
477 if (!topo
->drawers
|| !topo
->books
|| !topo
->sockets
) {
480 return topo
->max_cpus
/ topo
->drawers
/ topo
->books
/ topo
->sockets
;
484 /* Use the different calculation than machine_topo_get_cores_per_socket(). */
485 static unsigned int cpu_topology_get_cores_per_socket(const CpuTopology
*topo
)
487 /* Check the divisor to avoid invalid topology examples causing SIGFPE. */
488 if (!topo
->threads
) {
491 return cpu_topology_get_threads_per_socket(topo
) / topo
->threads
;
495 static char *cpu_topology_to_string(const CpuTopology
*topo
,
496 unsigned int threads_per_socket
,
497 unsigned int cores_per_socket
)
499 return g_strdup_printf(
510 " .threads_per_socket = %u,\n"
511 " .cores_per_socket = %u,\n"
513 topo
->cpus
, topo
->drawers
, topo
->books
,
514 topo
->sockets
, topo
->dies
, topo
->clusters
,
515 topo
->cores
, topo
->threads
, topo
->max_cpus
,
516 threads_per_socket
, cores_per_socket
);
519 static void check_parse(MachineState
*ms
, const SMPConfiguration
*config
,
520 const CpuTopology
*expect_topo
, const char *expect_err
,
523 g_autofree
char *config_str
= smp_config_to_string(config
);
524 g_autofree
char *expect_topo_str
= NULL
, *output_topo_str
= NULL
;
525 unsigned int expect_threads_per_socket
, expect_cores_per_socket
;
526 unsigned int ms_threads_per_socket
, ms_cores_per_socket
;
529 expect_threads_per_socket
=
530 cpu_topology_get_threads_per_socket(expect_topo
);
531 expect_cores_per_socket
=
532 cpu_topology_get_cores_per_socket(expect_topo
);
533 expect_topo_str
= cpu_topology_to_string(expect_topo
,
534 expect_threads_per_socket
,
535 expect_cores_per_socket
);
537 /* call the generic parser */
538 machine_parse_smp_config(ms
, config
, &err
);
540 ms_threads_per_socket
= machine_topo_get_threads_per_socket(ms
);
541 ms_cores_per_socket
= machine_topo_get_cores_per_socket(ms
);
542 output_topo_str
= cpu_topology_to_string(&ms
->smp
, ms_threads_per_socket
,
543 ms_cores_per_socket
);
545 /* when the configuration is supposed to be valid */
548 (ms
->smp
.cpus
== expect_topo
->cpus
) &&
549 (ms
->smp
.drawers
== expect_topo
->drawers
) &&
550 (ms
->smp
.books
== expect_topo
->books
) &&
551 (ms
->smp
.sockets
== expect_topo
->sockets
) &&
552 (ms
->smp
.dies
== expect_topo
->dies
) &&
553 (ms
->smp
.clusters
== expect_topo
->clusters
) &&
554 (ms
->smp
.cores
== expect_topo
->cores
) &&
555 (ms
->smp
.threads
== expect_topo
->threads
) &&
556 (ms
->smp
.max_cpus
== expect_topo
->max_cpus
) &&
557 (ms_threads_per_socket
== expect_threads_per_socket
) &&
558 (ms_cores_per_socket
== expect_cores_per_socket
)) {
563 g_printerr("Test smp_parse failed!\n"
564 "Input configuration: %s\n"
565 "Should be valid: yes\n"
566 "Expected topology: %s\n\n"
567 "Result is valid: no\n"
568 "Output error report: %s\n",
569 config_str
, expect_topo_str
, error_get_pretty(err
));
573 g_printerr("Test smp_parse failed!\n"
574 "Input configuration: %s\n"
575 "Should be valid: yes\n"
576 "Expected topology: %s\n\n"
577 "Result is valid: yes\n"
578 "Output topology: %s\n",
579 config_str
, expect_topo_str
, output_topo_str
);
583 /* when the configuration is supposed to be invalid */
585 if (expect_err
== NULL
||
586 g_str_equal(expect_err
, error_get_pretty(err
))) {
591 g_printerr("Test smp_parse failed!\n"
592 "Input configuration: %s\n"
593 "Should be valid: no\n"
594 "Expected error report: %s\n\n"
595 "Result is valid: no\n"
596 "Output error report: %s\n",
597 config_str
, expect_err
, error_get_pretty(err
));
601 g_printerr("Test smp_parse failed!\n"
602 "Input configuration: %s\n"
603 "Should be valid: no\n"
604 "Expected error report: %s\n\n"
605 "Result is valid: yes\n"
606 "Output topology: %s\n",
607 config_str
, expect_err
, output_topo_str
);
617 static void smp_parse_test(MachineState
*ms
, SMPTestData
*data
, bool is_valid
)
619 MachineClass
*mc
= MACHINE_GET_CLASS(ms
);
621 mc
->smp_props
.prefer_sockets
= true;
622 check_parse(ms
, &data
->config
, &data
->expect_prefer_sockets
,
623 data
->expect_error
, is_valid
);
625 mc
->smp_props
.prefer_sockets
= false;
626 check_parse(ms
, &data
->config
, &data
->expect_prefer_cores
,
627 data
->expect_error
, is_valid
);
630 /* The parsed results of the unsupported parameters should be 1 */
631 static void unsupported_params_init(const MachineClass
*mc
, SMPTestData
*data
)
633 if (!mc
->smp_props
.dies_supported
) {
634 data
->expect_prefer_sockets
.dies
= 1;
635 data
->expect_prefer_cores
.dies
= 1;
638 if (!mc
->smp_props
.clusters_supported
) {
639 data
->expect_prefer_sockets
.clusters
= 1;
640 data
->expect_prefer_cores
.clusters
= 1;
643 if (!mc
->smp_props
.books_supported
) {
644 data
->expect_prefer_sockets
.books
= 1;
645 data
->expect_prefer_cores
.books
= 1;
648 if (!mc
->smp_props
.drawers_supported
) {
649 data
->expect_prefer_sockets
.drawers
= 1;
650 data
->expect_prefer_cores
.drawers
= 1;
654 static void machine_base_class_init(ObjectClass
*oc
, void *data
)
656 MachineClass
*mc
= MACHINE_CLASS(oc
);
658 mc
->min_cpus
= MIN_CPUS
;
659 mc
->max_cpus
= MAX_CPUS
;
661 mc
->name
= g_strdup(SMP_MACHINE_NAME
);
664 static void machine_generic_invalid_class_init(ObjectClass
*oc
, void *data
)
666 MachineClass
*mc
= MACHINE_CLASS(oc
);
668 /* Force invalid min CPUs and max CPUs */
669 mc
->min_cpus
= MIN_CPUS
+ 1;
670 mc
->max_cpus
= MAX_CPUS
- 1;
673 static void machine_with_dies_class_init(ObjectClass
*oc
, void *data
)
675 MachineClass
*mc
= MACHINE_CLASS(oc
);
677 mc
->smp_props
.dies_supported
= true;
680 static void machine_with_clusters_class_init(ObjectClass
*oc
, void *data
)
682 MachineClass
*mc
= MACHINE_CLASS(oc
);
684 mc
->smp_props
.clusters_supported
= true;
687 static void machine_with_books_class_init(ObjectClass
*oc
, void *data
)
689 MachineClass
*mc
= MACHINE_CLASS(oc
);
691 mc
->smp_props
.books_supported
= true;
694 static void machine_with_drawers_class_init(ObjectClass
*oc
, void *data
)
696 MachineClass
*mc
= MACHINE_CLASS(oc
);
698 mc
->smp_props
.drawers_supported
= true;
701 static void test_generic_valid(const void *opaque
)
703 const char *machine_type
= opaque
;
704 Object
*obj
= object_new(machine_type
);
705 MachineState
*ms
= MACHINE(obj
);
706 MachineClass
*mc
= MACHINE_GET_CLASS(obj
);
707 SMPTestData data
= {};
710 for (i
= 0; i
< ARRAY_SIZE(data_generic_valid
); i
++) {
711 data
= data_generic_valid
[i
];
712 unsupported_params_init(mc
, &data
);
714 smp_parse_test(ms
, &data
, true);
720 static void test_generic_invalid(const void *opaque
)
722 const char *machine_type
= opaque
;
723 Object
*obj
= object_new(machine_type
);
724 MachineState
*ms
= MACHINE(obj
);
725 MachineClass
*mc
= MACHINE_GET_CLASS(obj
);
726 SMPTestData data
= {};
729 for (i
= 0; i
< ARRAY_SIZE(data_generic_invalid
); i
++) {
730 data
= data_generic_invalid
[i
];
731 unsupported_params_init(mc
, &data
);
733 smp_parse_test(ms
, &data
, false);
739 static void test_with_dies(const void *opaque
)
741 const char *machine_type
= opaque
;
742 Object
*obj
= object_new(machine_type
);
743 MachineState
*ms
= MACHINE(obj
);
744 MachineClass
*mc
= MACHINE_GET_CLASS(obj
);
745 SMPTestData data
= {};
746 unsigned int num_dies
= 2;
749 for (i
= 0; i
< ARRAY_SIZE(data_generic_valid
); i
++) {
750 data
= data_generic_valid
[i
];
751 unsupported_params_init(mc
, &data
);
753 /* when dies parameter is omitted, it will be set as 1 */
754 data
.expect_prefer_sockets
.dies
= 1;
755 data
.expect_prefer_cores
.dies
= 1;
757 smp_parse_test(ms
, &data
, true);
759 /* when dies parameter is specified */
760 data
.config
.has_dies
= true;
761 data
.config
.dies
= num_dies
;
762 if (data
.config
.has_cpus
) {
763 data
.config
.cpus
*= num_dies
;
765 if (data
.config
.has_maxcpus
) {
766 data
.config
.maxcpus
*= num_dies
;
769 data
.expect_prefer_sockets
.dies
= num_dies
;
770 data
.expect_prefer_sockets
.cpus
*= num_dies
;
771 data
.expect_prefer_sockets
.max_cpus
*= num_dies
;
772 data
.expect_prefer_cores
.dies
= num_dies
;
773 data
.expect_prefer_cores
.cpus
*= num_dies
;
774 data
.expect_prefer_cores
.max_cpus
*= num_dies
;
776 smp_parse_test(ms
, &data
, true);
779 for (i
= 0; i
< ARRAY_SIZE(data_with_dies_invalid
); i
++) {
780 data
= data_with_dies_invalid
[i
];
781 unsupported_params_init(mc
, &data
);
783 smp_parse_test(ms
, &data
, false);
789 static void test_with_clusters(const void *opaque
)
791 const char *machine_type
= opaque
;
792 Object
*obj
= object_new(machine_type
);
793 MachineState
*ms
= MACHINE(obj
);
794 MachineClass
*mc
= MACHINE_GET_CLASS(obj
);
795 SMPTestData data
= {};
796 unsigned int num_clusters
= 2;
799 for (i
= 0; i
< ARRAY_SIZE(data_generic_valid
); i
++) {
800 data
= data_generic_valid
[i
];
801 unsupported_params_init(mc
, &data
);
803 /* when clusters parameter is omitted, it will be set as 1 */
804 data
.expect_prefer_sockets
.clusters
= 1;
805 data
.expect_prefer_cores
.clusters
= 1;
807 smp_parse_test(ms
, &data
, true);
809 /* when clusters parameter is specified */
810 data
.config
.has_clusters
= true;
811 data
.config
.clusters
= num_clusters
;
812 if (data
.config
.has_cpus
) {
813 data
.config
.cpus
*= num_clusters
;
815 if (data
.config
.has_maxcpus
) {
816 data
.config
.maxcpus
*= num_clusters
;
819 data
.expect_prefer_sockets
.clusters
= num_clusters
;
820 data
.expect_prefer_sockets
.cpus
*= num_clusters
;
821 data
.expect_prefer_sockets
.max_cpus
*= num_clusters
;
822 data
.expect_prefer_cores
.clusters
= num_clusters
;
823 data
.expect_prefer_cores
.cpus
*= num_clusters
;
824 data
.expect_prefer_cores
.max_cpus
*= num_clusters
;
826 smp_parse_test(ms
, &data
, true);
829 for (i
= 0; i
< ARRAY_SIZE(data_with_clusters_invalid
); i
++) {
830 data
= data_with_clusters_invalid
[i
];
831 unsupported_params_init(mc
, &data
);
833 smp_parse_test(ms
, &data
, false);
839 static void test_with_books(const void *opaque
)
841 const char *machine_type
= opaque
;
842 Object
*obj
= object_new(machine_type
);
843 MachineState
*ms
= MACHINE(obj
);
844 MachineClass
*mc
= MACHINE_GET_CLASS(obj
);
845 SMPTestData data
= {};
846 unsigned int num_books
= 2;
849 for (i
= 0; i
< ARRAY_SIZE(data_generic_valid
); i
++) {
850 data
= data_generic_valid
[i
];
851 unsupported_params_init(mc
, &data
);
853 /* when books parameter is omitted, it will be set as 1 */
854 data
.expect_prefer_sockets
.books
= 1;
855 data
.expect_prefer_cores
.books
= 1;
857 smp_parse_test(ms
, &data
, true);
859 /* when books parameter is specified */
860 data
.config
.has_books
= true;
861 data
.config
.books
= num_books
;
862 if (data
.config
.has_cpus
) {
863 data
.config
.cpus
*= num_books
;
865 if (data
.config
.has_maxcpus
) {
866 data
.config
.maxcpus
*= num_books
;
869 data
.expect_prefer_sockets
.books
= num_books
;
870 data
.expect_prefer_sockets
.cpus
*= num_books
;
871 data
.expect_prefer_sockets
.max_cpus
*= num_books
;
872 data
.expect_prefer_cores
.books
= num_books
;
873 data
.expect_prefer_cores
.cpus
*= num_books
;
874 data
.expect_prefer_cores
.max_cpus
*= num_books
;
876 smp_parse_test(ms
, &data
, true);
879 for (i
= 0; i
< ARRAY_SIZE(data_with_books_invalid
); i
++) {
880 data
= data_with_books_invalid
[i
];
881 unsupported_params_init(mc
, &data
);
883 smp_parse_test(ms
, &data
, false);
889 static void test_with_drawers(const void *opaque
)
891 const char *machine_type
= opaque
;
892 Object
*obj
= object_new(machine_type
);
893 MachineState
*ms
= MACHINE(obj
);
894 MachineClass
*mc
= MACHINE_GET_CLASS(obj
);
895 SMPTestData data
= {};
896 unsigned int num_drawers
= 2;
899 for (i
= 0; i
< ARRAY_SIZE(data_generic_valid
); i
++) {
900 data
= data_generic_valid
[i
];
901 unsupported_params_init(mc
, &data
);
903 /* when drawers parameter is omitted, it will be set as 1 */
904 data
.expect_prefer_sockets
.drawers
= 1;
905 data
.expect_prefer_cores
.drawers
= 1;
907 smp_parse_test(ms
, &data
, true);
909 /* when drawers parameter is specified */
910 data
.config
.has_drawers
= true;
911 data
.config
.drawers
= num_drawers
;
912 if (data
.config
.has_cpus
) {
913 data
.config
.cpus
*= num_drawers
;
915 if (data
.config
.has_maxcpus
) {
916 data
.config
.maxcpus
*= num_drawers
;
919 data
.expect_prefer_sockets
.drawers
= num_drawers
;
920 data
.expect_prefer_sockets
.cpus
*= num_drawers
;
921 data
.expect_prefer_sockets
.max_cpus
*= num_drawers
;
922 data
.expect_prefer_cores
.drawers
= num_drawers
;
923 data
.expect_prefer_cores
.cpus
*= num_drawers
;
924 data
.expect_prefer_cores
.max_cpus
*= num_drawers
;
926 smp_parse_test(ms
, &data
, true);
929 for (i
= 0; i
< ARRAY_SIZE(data_with_drawers_invalid
); i
++) {
930 data
= data_with_drawers_invalid
[i
];
931 unsupported_params_init(mc
, &data
);
933 smp_parse_test(ms
, &data
, false);
939 /* Type info of the tested machine */
940 static const TypeInfo smp_machine_types
[] = {
942 .name
= TYPE_MACHINE
,
943 .parent
= TYPE_OBJECT
,
945 .class_init
= machine_base_class_init
,
946 .class_size
= sizeof(MachineClass
),
947 .instance_size
= sizeof(MachineState
),
949 .name
= MACHINE_TYPE_NAME("smp-generic-valid"),
950 .parent
= TYPE_MACHINE
,
952 .name
= MACHINE_TYPE_NAME("smp-generic-invalid"),
953 .parent
= TYPE_MACHINE
,
954 .class_init
= machine_generic_invalid_class_init
,
956 .name
= MACHINE_TYPE_NAME("smp-with-dies"),
957 .parent
= TYPE_MACHINE
,
958 .class_init
= machine_with_dies_class_init
,
960 .name
= MACHINE_TYPE_NAME("smp-with-clusters"),
961 .parent
= TYPE_MACHINE
,
962 .class_init
= machine_with_clusters_class_init
,
964 .name
= MACHINE_TYPE_NAME("smp-with-books"),
965 .parent
= TYPE_MACHINE
,
966 .class_init
= machine_with_books_class_init
,
968 .name
= MACHINE_TYPE_NAME("smp-with-drawers"),
969 .parent
= TYPE_MACHINE
,
970 .class_init
= machine_with_drawers_class_init
,
974 DEFINE_TYPES(smp_machine_types
)
976 int main(int argc
, char *argv
[])
978 module_call_init(MODULE_INIT_QOM
);
980 g_test_init(&argc
, &argv
, NULL
);
982 g_test_add_data_func("/test-smp-parse/generic/valid",
983 MACHINE_TYPE_NAME("smp-generic-valid"),
985 g_test_add_data_func("/test-smp-parse/generic/invalid",
986 MACHINE_TYPE_NAME("smp-generic-invalid"),
987 test_generic_invalid
);
988 g_test_add_data_func("/test-smp-parse/with_dies",
989 MACHINE_TYPE_NAME("smp-with-dies"),
991 g_test_add_data_func("/test-smp-parse/with_clusters",
992 MACHINE_TYPE_NAME("smp-with-clusters"),
994 g_test_add_data_func("/test-smp-parse/with_books",
995 MACHINE_TYPE_NAME("smp-with-books"),
997 g_test_add_data_func("/test-smp-parse/with_drawers",
998 MACHINE_TYPE_NAME("smp-with-drawers"),