1
2
3
4
5
6
7
8
9
10
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
24#define MAX_CPUS 512
25
26#define SMP_MACHINE_NAME "TEST-SMP"
27
28
29
30
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
52
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
66
67
68
69
70
71
72
73typedef struct SMPTestData {
74 SMPConfiguration config;
75 CpuTopology expect_prefer_sockets;
76 CpuTopology expect_prefer_cores;
77 const char *expect_error;
78} SMPTestData;
79
80
81
82
83
84
85
86static struct SMPTestData data_generic_valid[] = {
87 {
88
89
90 .config = SMP_CONFIG_GENERIC(F, 0, F, 0, F, 0, F, 0, F, 0),
91 .expect_prefer_sockets = CPU_TOPOLOGY_GENERIC(1, 1, 1, 1, 1),
92 .expect_prefer_cores = CPU_TOPOLOGY_GENERIC(1, 1, 1, 1, 1),
93 }, {
94
95
96
97 .config = SMP_CONFIG_GENERIC(T, 8, F, 0, F, 0, F, 0, F, 0),
98 .expect_prefer_sockets = CPU_TOPOLOGY_GENERIC(8, 8, 1, 1, 8),
99 .expect_prefer_cores = CPU_TOPOLOGY_GENERIC(8, 1, 8, 1, 8),
100 }, {
101
102
103 .config = SMP_CONFIG_GENERIC(F, 0, T, 2, F, 0, F, 0, F, 0),
104 .expect_prefer_sockets = CPU_TOPOLOGY_GENERIC(2, 2, 1, 1, 2),
105 .expect_prefer_cores = CPU_TOPOLOGY_GENERIC(2, 2, 1, 1, 2),
106 }, {
107
108
109 .config = SMP_CONFIG_GENERIC(F, 0, F, 0, T, 4, F, 0, F, 0),
110 .expect_prefer_sockets = CPU_TOPOLOGY_GENERIC(4, 1, 4, 1, 4),
111 .expect_prefer_cores = CPU_TOPOLOGY_GENERIC(4, 1, 4, 1, 4),
112 }, {
113
114
115 .config = SMP_CONFIG_GENERIC(F, 0, F, 0, F, 0, T, 2, F, 0),
116 .expect_prefer_sockets = CPU_TOPOLOGY_GENERIC(2, 1, 1, 2, 2),
117 .expect_prefer_cores = CPU_TOPOLOGY_GENERIC(2, 1, 1, 2, 2),
118 }, {
119
120
121
122 .config = SMP_CONFIG_GENERIC(F, 0, F, 0, F, 0, F, 0, T, 16),
123 .expect_prefer_sockets = CPU_TOPOLOGY_GENERIC(16, 16, 1, 1, 16),
124 .expect_prefer_cores = CPU_TOPOLOGY_GENERIC(16, 1, 16, 1, 16),
125 }, {
126
127
128 .config = SMP_CONFIG_GENERIC(T, 8, T, 2, F, 0, F, 0, F, 0),
129 .expect_prefer_sockets = CPU_TOPOLOGY_GENERIC(8, 2, 4, 1, 8),
130 .expect_prefer_cores = CPU_TOPOLOGY_GENERIC(8, 2, 4, 1, 8),
131 }, {
132
133
134 .config = SMP_CONFIG_GENERIC(T, 8, F, 0, T, 4, F, 0, F, 0),
135 .expect_prefer_sockets = CPU_TOPOLOGY_GENERIC(8, 2, 4, 1, 8),
136 .expect_prefer_cores = CPU_TOPOLOGY_GENERIC(8, 2, 4, 1, 8),
137 }, {
138
139
140
141 .config = SMP_CONFIG_GENERIC(T, 8, F, 0, F, 0, T, 2, F, 0),
142 .expect_prefer_sockets = CPU_TOPOLOGY_GENERIC(8, 4, 1, 2, 8),
143 .expect_prefer_cores = CPU_TOPOLOGY_GENERIC(8, 1, 4, 2, 8),
144 }, {
145
146
147
148 .config = SMP_CONFIG_GENERIC(T, 8, F, 0, F, 0, F, 0, T, 16),
149 .expect_prefer_sockets = CPU_TOPOLOGY_GENERIC(8, 16, 1, 1, 16),
150 .expect_prefer_cores = CPU_TOPOLOGY_GENERIC(8, 1, 16, 1, 16),
151 }, {
152
153
154 .config = SMP_CONFIG_GENERIC(F, 0, T, 2, T, 4, F, 0, F, 0),
155 .expect_prefer_sockets = CPU_TOPOLOGY_GENERIC(8, 2, 4, 1, 8),
156 .expect_prefer_cores = CPU_TOPOLOGY_GENERIC(8, 2, 4, 1, 8),
157 }, {
158
159
160 .config = SMP_CONFIG_GENERIC(F, 0, T, 2, F, 0, T, 2, F, 0),
161 .expect_prefer_sockets = CPU_TOPOLOGY_GENERIC(4, 2, 1, 2, 4),
162 .expect_prefer_cores = CPU_TOPOLOGY_GENERIC(4, 2, 1, 2, 4),
163 }, {
164
165
166 .config = SMP_CONFIG_GENERIC(F, 0, T, 2, F, 0, F, 0, T, 16),
167 .expect_prefer_sockets = CPU_TOPOLOGY_GENERIC(16, 2, 8, 1, 16),
168 .expect_prefer_cores = CPU_TOPOLOGY_GENERIC(16, 2, 8, 1, 16),
169 }, {
170
171
172 .config = SMP_CONFIG_GENERIC(F, 0, F, 0, T, 4, T, 2, F, 0),
173 .expect_prefer_sockets = CPU_TOPOLOGY_GENERIC(8, 1, 4, 2, 8),
174 .expect_prefer_cores = CPU_TOPOLOGY_GENERIC(8, 1, 4, 2, 8),
175 }, {
176
177
178 .config = SMP_CONFIG_GENERIC(F, 0, F, 0, T, 4, F, 0, T, 16),
179 .expect_prefer_sockets = CPU_TOPOLOGY_GENERIC(16, 4, 4, 1, 16),
180 .expect_prefer_cores = CPU_TOPOLOGY_GENERIC(16, 4, 4, 1, 16),
181 }, {
182
183
184
185 .config = SMP_CONFIG_GENERIC(F, 0, F, 0, F, 0, T, 2, T, 16),
186 .expect_prefer_sockets = CPU_TOPOLOGY_GENERIC(16, 8, 1, 2, 16),
187 .expect_prefer_cores = CPU_TOPOLOGY_GENERIC(16, 1, 8, 2, 16),
188 }, {
189
190
191 .config = SMP_CONFIG_GENERIC(T, 8, T, 2, T, 4, F, 0, F, 0),
192 .expect_prefer_sockets = CPU_TOPOLOGY_GENERIC(8, 2, 4, 1, 8),
193 .expect_prefer_cores = CPU_TOPOLOGY_GENERIC(8, 2, 4, 1, 8),
194 }, {
195
196
197 .config = SMP_CONFIG_GENERIC(T, 8, T, 2, F, 0, T, 2, F, 0),
198 .expect_prefer_sockets = CPU_TOPOLOGY_GENERIC(8, 2, 2, 2, 8),
199 .expect_prefer_cores = CPU_TOPOLOGY_GENERIC(8, 2, 2, 2, 8),
200 }, {
201
202
203 .config = SMP_CONFIG_GENERIC(T, 8, T, 2, F, 0, F, 0, T, 16),
204 .expect_prefer_sockets = CPU_TOPOLOGY_GENERIC(8, 2, 8, 1, 16),
205 .expect_prefer_cores = CPU_TOPOLOGY_GENERIC(8, 2, 8, 1, 16),
206 }, {
207
208
209 .config = SMP_CONFIG_GENERIC(T, 8, F, 0, T, 4, T, 2, F, 0),
210 .expect_prefer_sockets = CPU_TOPOLOGY_GENERIC(8, 1, 4, 2, 8),
211 .expect_prefer_cores = CPU_TOPOLOGY_GENERIC(8, 1, 4, 2, 8),
212 }, {
213
214
215 .config = SMP_CONFIG_GENERIC(T, 8, F, 0, T, 4, F, 0, T, 16),
216 .expect_prefer_sockets = CPU_TOPOLOGY_GENERIC(8, 4, 4, 1, 16),
217 .expect_prefer_cores = CPU_TOPOLOGY_GENERIC(8, 4, 4, 1, 16),
218 }, {
219
220
221
222 .config = SMP_CONFIG_GENERIC(T, 8, F, 0, F, 0, T, 2, T, 16),
223 .expect_prefer_sockets = CPU_TOPOLOGY_GENERIC(8, 8, 1, 2, 16),
224 .expect_prefer_cores = CPU_TOPOLOGY_GENERIC(8, 1, 8, 2, 16),
225 }, {
226
227
228 .config = SMP_CONFIG_GENERIC(F, 0, T, 2, T, 4, T, 2, F, 0),
229 .expect_prefer_sockets = CPU_TOPOLOGY_GENERIC(16, 2, 4, 2, 16),
230 .expect_prefer_cores = CPU_TOPOLOGY_GENERIC(16, 2, 4, 2, 16),
231 }, {
232
233
234 .config = SMP_CONFIG_GENERIC(F, 0, T, 2, T, 4, F, 0, T, 16),
235 .expect_prefer_sockets = CPU_TOPOLOGY_GENERIC(16, 2, 4, 2, 16),
236 .expect_prefer_cores = CPU_TOPOLOGY_GENERIC(16, 2, 4, 2, 16),
237 }, {
238
239
240 .config = SMP_CONFIG_GENERIC(F, 0, T, 2, F, 0, T, 2, T, 16),
241 .expect_prefer_sockets = CPU_TOPOLOGY_GENERIC(16, 2, 4, 2, 16),
242 .expect_prefer_cores = CPU_TOPOLOGY_GENERIC(16, 2, 4, 2, 16),
243 }, {
244
245
246 .config = SMP_CONFIG_GENERIC(F, 0, F, 0, T, 4, T, 2, T, 16),
247 .expect_prefer_sockets = CPU_TOPOLOGY_GENERIC(16, 2, 4, 2, 16),
248 .expect_prefer_cores = CPU_TOPOLOGY_GENERIC(16, 2, 4, 2, 16),
249 }, {
250
251
252 .config = SMP_CONFIG_GENERIC(T, 8, T, 2, T, 4, T, 1, F, 0),
253 .expect_prefer_sockets = CPU_TOPOLOGY_GENERIC(8, 2, 4, 1, 8),
254 .expect_prefer_cores = CPU_TOPOLOGY_GENERIC(8, 2, 4, 1, 8),
255 }, {
256
257
258 .config = SMP_CONFIG_GENERIC(T, 8, T, 2, T, 4, F, 0, T, 16),
259 .expect_prefer_sockets = CPU_TOPOLOGY_GENERIC(8, 2, 4, 2, 16),
260 .expect_prefer_cores = CPU_TOPOLOGY_GENERIC(8, 2, 4, 2, 16),
261 }, {
262
263
264 .config = SMP_CONFIG_GENERIC(T, 8, T, 2, F, 0, T, 2, T, 16),
265 .expect_prefer_sockets = CPU_TOPOLOGY_GENERIC(8, 2, 4, 2, 16),
266 .expect_prefer_cores = CPU_TOPOLOGY_GENERIC(8, 2, 4, 2, 16),
267 }, {
268
269
270 .config = SMP_CONFIG_GENERIC(T, 8, F, 0, T, 4, T, 2, T, 16),
271 .expect_prefer_sockets = CPU_TOPOLOGY_GENERIC(8, 2, 4, 2, 16),
272 .expect_prefer_cores = CPU_TOPOLOGY_GENERIC(8, 2, 4, 2, 16),
273 }, {
274
275
276 .config = SMP_CONFIG_GENERIC(F, 0, T, 2, 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),
279 }, {
280
281
282 .config = SMP_CONFIG_GENERIC(T, 8, T, 2, T, 4, T, 2, T, 16),
283 .expect_prefer_sockets = CPU_TOPOLOGY_GENERIC(8, 2, 4, 2, 16),
284 .expect_prefer_cores = CPU_TOPOLOGY_GENERIC(8, 2, 4, 2, 16),
285 },
286};
287
288static struct SMPTestData data_generic_invalid[] = {
289 {
290
291 .config = SMP_CONFIG_WITH_DIES(T, 2, F, 0, T, 2, F, 0, F, 0, F, 0),
292 .expect_error = "dies not supported by this machine's CPU topology",
293 }, {
294
295 .config = SMP_CONFIG_GENERIC(T, 8, T, 2, T, 4, T, 2, T, 8),
296 .expect_error = "Invalid CPU topology: "
297 "product of the hierarchy must match maxcpus: "
298 "sockets (2) * cores (4) * threads (2) "
299 "!= maxcpus (8)",
300 }, {
301
302 .config = SMP_CONFIG_GENERIC(T, 18, T, 2, T, 4, T, 2, T, 16),
303 .expect_error = "Invalid CPU topology: "
304 "maxcpus must be equal to or greater than smp: "
305 "sockets (2) * cores (4) * threads (2) "
306 "== maxcpus (16) < smp_cpus (18)",
307 }, {
308
309
310 .config = SMP_CONFIG_GENERIC(T, 1, F, 0, F, 0, F, 0, F, 0),
311 .expect_error = "Invalid SMP CPUs 1. The min CPUs supported "
312 "by machine '" SMP_MACHINE_NAME "' is 2",
313 }, {
314
315
316 .config = SMP_CONFIG_GENERIC(T, 512, F, 0, F, 0, F, 0, F, 0),
317 .expect_error = "Invalid SMP CPUs 512. The max CPUs supported "
318 "by machine '" SMP_MACHINE_NAME "' is 511",
319 },
320};
321
322static struct SMPTestData data_with_dies_invalid[] = {
323 {
324
325 .config = SMP_CONFIG_WITH_DIES(T, 16, T, 2, T, 2, T, 4, T, 2, T, 16),
326 .expect_error = "Invalid CPU topology: "
327 "product of the hierarchy must match maxcpus: "
328 "sockets (2) * dies (2) * cores (4) * threads (2) "
329 "!= maxcpus (16)",
330 }, {
331
332 .config = SMP_CONFIG_WITH_DIES(T, 34, T, 2, T, 2, T, 4, T, 2, T, 32),
333 .expect_error = "Invalid CPU topology: "
334 "maxcpus must be equal to or greater than smp: "
335 "sockets (2) * dies (2) * cores (4) * threads (2) "
336 "== maxcpus (32) < smp_cpus (34)",
337 },
338};
339
340static char *smp_config_to_string(SMPConfiguration *config)
341{
342 return g_strdup_printf(
343 "(SMPConfiguration) {\n"
344 " .has_cpus = %5s, cpus = %" PRId64 ",\n"
345 " .has_sockets = %5s, sockets = %" PRId64 ",\n"
346 " .has_dies = %5s, dies = %" PRId64 ",\n"
347 " .has_cores = %5s, cores = %" PRId64 ",\n"
348 " .has_threads = %5s, threads = %" PRId64 ",\n"
349 " .has_maxcpus = %5s, maxcpus = %" PRId64 ",\n"
350 "}",
351 config->has_cpus ? "true" : "false", config->cpus,
352 config->has_sockets ? "true" : "false", config->sockets,
353 config->has_dies ? "true" : "false", config->dies,
354 config->has_cores ? "true" : "false", config->cores,
355 config->has_threads ? "true" : "false", config->threads,
356 config->has_maxcpus ? "true" : "false", config->maxcpus);
357}
358
359static char *cpu_topology_to_string(CpuTopology *topo)
360{
361 return g_strdup_printf(
362 "(CpuTopology) {\n"
363 " .cpus = %u,\n"
364 " .sockets = %u,\n"
365 " .dies = %u,\n"
366 " .cores = %u,\n"
367 " .threads = %u,\n"
368 " .max_cpus = %u,\n"
369 "}",
370 topo->cpus, topo->sockets, topo->dies,
371 topo->cores, topo->threads, topo->max_cpus);
372}
373
374static void check_parse(MachineState *ms, SMPConfiguration *config,
375 CpuTopology *expect_topo, const char *expect_err,
376 bool is_valid)
377{
378 g_autofree char *config_str = smp_config_to_string(config);
379 g_autofree char *expect_topo_str = cpu_topology_to_string(expect_topo);
380 g_autofree char *output_topo_str = NULL;
381 Error *err = NULL;
382
383
384 smp_parse(ms, config, &err);
385
386 output_topo_str = cpu_topology_to_string(&ms->smp);
387
388
389 if (is_valid) {
390 if ((err == NULL) &&
391 (ms->smp.cpus == expect_topo->cpus) &&
392 (ms->smp.sockets == expect_topo->sockets) &&
393 (ms->smp.dies == expect_topo->dies) &&
394 (ms->smp.cores == expect_topo->cores) &&
395 (ms->smp.threads == expect_topo->threads) &&
396 (ms->smp.max_cpus == expect_topo->max_cpus)) {
397 return;
398 }
399
400 if (err != NULL) {
401 g_printerr("Test smp_parse failed!\n"
402 "Input configuration: %s\n"
403 "Should be valid: yes\n"
404 "Expected topology: %s\n\n"
405 "Result is valid: no\n"
406 "Output error report: %s\n",
407 config_str, expect_topo_str, error_get_pretty(err));
408 goto end;
409 }
410
411 g_printerr("Test smp_parse failed!\n"
412 "Input configuration: %s\n"
413 "Should be valid: yes\n"
414 "Expected topology: %s\n\n"
415 "Result is valid: yes\n"
416 "Output topology: %s\n",
417 config_str, expect_topo_str, output_topo_str);
418 goto end;
419 }
420
421
422 if (err != NULL) {
423 if (expect_err == NULL ||
424 g_str_equal(expect_err, error_get_pretty(err))) {
425 error_free(err);
426 return;
427 }
428
429 g_printerr("Test smp_parse failed!\n"
430 "Input configuration: %s\n"
431 "Should be valid: no\n"
432 "Expected error report: %s\n\n"
433 "Result is valid: no\n"
434 "Output error report: %s\n",
435 config_str, expect_err, error_get_pretty(err));
436 goto end;
437 }
438
439 g_printerr("Test smp_parse failed!\n"
440 "Input configuration: %s\n"
441 "Should be valid: no\n"
442 "Expected error report: %s\n\n"
443 "Result is valid: yes\n"
444 "Output topology: %s\n",
445 config_str, expect_err, output_topo_str);
446
447end:
448 if (err != NULL) {
449 error_free(err);
450 }
451
452 abort();
453}
454
455static void smp_parse_test(MachineState *ms, SMPTestData *data, bool is_valid)
456{
457 MachineClass *mc = MACHINE_GET_CLASS(ms);
458
459 mc->smp_props.prefer_sockets = true;
460 check_parse(ms, &data->config, &data->expect_prefer_sockets,
461 data->expect_error, is_valid);
462
463 mc->smp_props.prefer_sockets = false;
464 check_parse(ms, &data->config, &data->expect_prefer_cores,
465 data->expect_error, is_valid);
466}
467
468
469static void unsupported_params_init(MachineClass *mc, SMPTestData *data)
470{
471 if (!mc->smp_props.dies_supported) {
472 data->expect_prefer_sockets.dies = 1;
473 data->expect_prefer_cores.dies = 1;
474 }
475}
476
477static void machine_base_class_init(ObjectClass *oc, void *data)
478{
479 MachineClass *mc = MACHINE_CLASS(oc);
480
481 mc->min_cpus = MIN_CPUS;
482 mc->max_cpus = MAX_CPUS;
483
484 mc->smp_props.prefer_sockets = true;
485 mc->smp_props.dies_supported = false;
486
487 mc->name = g_strdup(SMP_MACHINE_NAME);
488}
489
490static void test_generic(void)
491{
492 Object *obj = object_new(TYPE_MACHINE);
493 MachineState *ms = MACHINE(obj);
494 MachineClass *mc = MACHINE_GET_CLASS(obj);
495 SMPTestData *data = &(SMPTestData){{ }};
496 int i;
497
498 for (i = 0; i < ARRAY_SIZE(data_generic_valid); i++) {
499 *data = data_generic_valid[i];
500 unsupported_params_init(mc, data);
501
502 smp_parse_test(ms, data, true);
503
504
505 data->config.has_dies = true;
506 data->config.dies = 1;
507 smp_parse_test(ms, data, true);
508 }
509
510
511 mc->min_cpus = 2;
512 mc->max_cpus = 511;
513
514 for (i = 0; i < ARRAY_SIZE(data_generic_invalid); i++) {
515 *data = data_generic_invalid[i];
516 unsupported_params_init(mc, data);
517
518 smp_parse_test(ms, data, false);
519 }
520
521
522 mc->min_cpus = MIN_CPUS;
523 mc->max_cpus = MAX_CPUS;
524
525 object_unref(obj);
526}
527
528static void test_with_dies(void)
529{
530 Object *obj = object_new(TYPE_MACHINE);
531 MachineState *ms = MACHINE(obj);
532 MachineClass *mc = MACHINE_GET_CLASS(obj);
533 SMPTestData *data = &(SMPTestData){{ }};
534 unsigned int num_dies = 2;
535 int i;
536
537
538 mc->smp_props.dies_supported = true;
539
540 for (i = 0; i < ARRAY_SIZE(data_generic_valid); i++) {
541 *data = data_generic_valid[i];
542 unsupported_params_init(mc, data);
543
544
545 data->expect_prefer_sockets.dies = 1;
546 data->expect_prefer_cores.dies = 1;
547
548 smp_parse_test(ms, data, true);
549
550
551 data->config.has_dies = true;
552 data->config.dies = num_dies;
553 if (data->config.has_cpus) {
554 data->config.cpus *= num_dies;
555 }
556 if (data->config.has_maxcpus) {
557 data->config.maxcpus *= num_dies;
558 }
559
560 data->expect_prefer_sockets.dies = num_dies;
561 data->expect_prefer_sockets.cpus *= num_dies;
562 data->expect_prefer_sockets.max_cpus *= num_dies;
563 data->expect_prefer_cores.dies = num_dies;
564 data->expect_prefer_cores.cpus *= num_dies;
565 data->expect_prefer_cores.max_cpus *= num_dies;
566
567 smp_parse_test(ms, data, true);
568 }
569
570 for (i = 0; i < ARRAY_SIZE(data_with_dies_invalid); i++) {
571 *data = data_with_dies_invalid[i];
572 unsupported_params_init(mc, data);
573
574 smp_parse_test(ms, data, false);
575 }
576
577
578 mc->smp_props.dies_supported = false;
579
580 object_unref(obj);
581}
582
583
584static const TypeInfo smp_machine_types[] = {
585 {
586 .name = TYPE_MACHINE,
587 .parent = TYPE_OBJECT,
588 .class_init = machine_base_class_init,
589 .class_size = sizeof(MachineClass),
590 .instance_size = sizeof(MachineState),
591 }
592};
593
594DEFINE_TYPES(smp_machine_types)
595
596int main(int argc, char *argv[])
597{
598 module_call_init(MODULE_INIT_QOM);
599
600 g_test_init(&argc, &argv, NULL);
601
602 g_test_add_func("/test-smp-parse/generic", test_generic);
603 g_test_add_func("/test-smp-parse/with_dies", test_with_dies);
604
605 g_test_run();
606
607 return 0;
608}
609