1 /*
2  * NUMA configuration test cases
3  *
4  * Copyright (c) 2017 Red Hat Inc.
5  * Authors:
6  *  Igor Mammedov <imammedo@redhat.com>
7  *
8  * This work is licensed under the terms of the GNU GPL, version 2 or later.
9  * See the COPYING file in the top-level directory.
10  */
11 
12 #include "qemu/osdep.h"
13 #include "libqtest.h"
14 #include "qapi/qmp/qdict.h"
15 #include "qapi/qmp/qlist.h"
16 
make_cli(const char * generic_cli,const char * test_cli)17 static char *make_cli(const char *generic_cli, const char *test_cli)
18 {
19     return g_strdup_printf("%s %s", generic_cli ? generic_cli : "", test_cli);
20 }
21 
test_mon_explicit(const void * data)22 static void test_mon_explicit(const void *data)
23 {
24     char *s;
25     char *cli;
26     QTestState *qts;
27 
28     cli = make_cli(data, "-smp 8 "
29                    "-numa node,nodeid=0,cpus=0-3 "
30                    "-numa node,nodeid=1,cpus=4-7 ");
31     qts = qtest_init(cli);
32 
33     s = qtest_hmp(qts, "info numa");
34     g_assert(strstr(s, "node 0 cpus: 0 1 2 3"));
35     g_assert(strstr(s, "node 1 cpus: 4 5 6 7"));
36     g_free(s);
37 
38     qtest_quit(qts);
39     g_free(cli);
40 }
41 
test_mon_default(const void * data)42 static void test_mon_default(const void *data)
43 {
44     char *s;
45     char *cli;
46     QTestState *qts;
47 
48     cli = make_cli(data, "-smp 8 -numa node -numa node");
49     qts = qtest_init(cli);
50 
51     s = qtest_hmp(qts, "info numa");
52     g_assert(strstr(s, "node 0 cpus: 0 2 4 6"));
53     g_assert(strstr(s, "node 1 cpus: 1 3 5 7"));
54     g_free(s);
55 
56     qtest_quit(qts);
57     g_free(cli);
58 }
59 
test_mon_partial(const void * data)60 static void test_mon_partial(const void *data)
61 {
62     char *s;
63     char *cli;
64     QTestState *qts;
65 
66     cli = make_cli(data, "-smp 8 "
67                    "-numa node,nodeid=0,cpus=0-1 "
68                    "-numa node,nodeid=1,cpus=4-5 ");
69     qts = qtest_init(cli);
70 
71     s = qtest_hmp(qts, "info numa");
72     g_assert(strstr(s, "node 0 cpus: 0 1 2 3 6 7"));
73     g_assert(strstr(s, "node 1 cpus: 4 5"));
74     g_free(s);
75 
76     qtest_quit(qts);
77     g_free(cli);
78 }
79 
get_cpus(QTestState * qts,QDict ** resp)80 static QList *get_cpus(QTestState *qts, QDict **resp)
81 {
82     *resp = qtest_qmp(qts, "{ 'execute': 'query-cpus' }");
83     g_assert(*resp);
84     g_assert(qdict_haskey(*resp, "return"));
85     return qdict_get_qlist(*resp, "return");
86 }
87 
test_query_cpus(const void * data)88 static void test_query_cpus(const void *data)
89 {
90     char *cli;
91     QDict *resp;
92     QList *cpus;
93     QObject *e;
94     QTestState *qts;
95 
96     cli = make_cli(data, "-smp 8 -numa node,cpus=0-3 -numa node,cpus=4-7");
97     qts = qtest_init(cli);
98     cpus = get_cpus(qts, &resp);
99     g_assert(cpus);
100 
101     while ((e = qlist_pop(cpus))) {
102         QDict *cpu, *props;
103         int64_t cpu_idx, node;
104 
105         cpu = qobject_to(QDict, e);
106         g_assert(qdict_haskey(cpu, "CPU"));
107         g_assert(qdict_haskey(cpu, "props"));
108 
109         cpu_idx = qdict_get_int(cpu, "CPU");
110         props = qdict_get_qdict(cpu, "props");
111         g_assert(qdict_haskey(props, "node-id"));
112         node = qdict_get_int(props, "node-id");
113         if (cpu_idx >= 0 && cpu_idx < 4) {
114             g_assert_cmpint(node, ==, 0);
115         } else {
116             g_assert_cmpint(node, ==, 1);
117         }
118         qobject_unref(e);
119     }
120 
121     qobject_unref(resp);
122     qtest_quit(qts);
123     g_free(cli);
124 }
125 
pc_numa_cpu(const void * data)126 static void pc_numa_cpu(const void *data)
127 {
128     char *cli;
129     QDict *resp;
130     QList *cpus;
131     QObject *e;
132     QTestState *qts;
133 
134     cli = make_cli(data, "-cpu pentium -smp 8,sockets=2,cores=2,threads=2 "
135         "-numa node,nodeid=0 -numa node,nodeid=1 "
136         "-numa cpu,node-id=1,socket-id=0 "
137         "-numa cpu,node-id=0,socket-id=1,core-id=0 "
138         "-numa cpu,node-id=0,socket-id=1,core-id=1,thread-id=0 "
139         "-numa cpu,node-id=1,socket-id=1,core-id=1,thread-id=1");
140     qts = qtest_init(cli);
141     cpus = get_cpus(qts, &resp);
142     g_assert(cpus);
143 
144     while ((e = qlist_pop(cpus))) {
145         QDict *cpu, *props;
146         int64_t socket, core, thread, node;
147 
148         cpu = qobject_to(QDict, e);
149         g_assert(qdict_haskey(cpu, "props"));
150         props = qdict_get_qdict(cpu, "props");
151 
152         g_assert(qdict_haskey(props, "node-id"));
153         node = qdict_get_int(props, "node-id");
154         g_assert(qdict_haskey(props, "socket-id"));
155         socket = qdict_get_int(props, "socket-id");
156         g_assert(qdict_haskey(props, "core-id"));
157         core = qdict_get_int(props, "core-id");
158         g_assert(qdict_haskey(props, "thread-id"));
159         thread = qdict_get_int(props, "thread-id");
160 
161         if (socket == 0) {
162             g_assert_cmpint(node, ==, 1);
163         } else if (socket == 1 && core == 0) {
164             g_assert_cmpint(node, ==, 0);
165         } else if (socket == 1 && core == 1 && thread == 0) {
166             g_assert_cmpint(node, ==, 0);
167         } else if (socket == 1 && core == 1 && thread == 1) {
168             g_assert_cmpint(node, ==, 1);
169         } else {
170             g_assert(false);
171         }
172         qobject_unref(e);
173     }
174 
175     qobject_unref(resp);
176     qtest_quit(qts);
177     g_free(cli);
178 }
179 
spapr_numa_cpu(const void * data)180 static void spapr_numa_cpu(const void *data)
181 {
182     char *cli;
183     QDict *resp;
184     QList *cpus;
185     QObject *e;
186     QTestState *qts;
187 
188     cli = make_cli(data, "-smp 4,cores=4 "
189         "-numa node,nodeid=0 -numa node,nodeid=1 "
190         "-numa cpu,node-id=0,core-id=0 "
191         "-numa cpu,node-id=0,core-id=1 "
192         "-numa cpu,node-id=0,core-id=2 "
193         "-numa cpu,node-id=1,core-id=3");
194     qts = qtest_init(cli);
195     cpus = get_cpus(qts, &resp);
196     g_assert(cpus);
197 
198     while ((e = qlist_pop(cpus))) {
199         QDict *cpu, *props;
200         int64_t core, node;
201 
202         cpu = qobject_to(QDict, e);
203         g_assert(qdict_haskey(cpu, "props"));
204         props = qdict_get_qdict(cpu, "props");
205 
206         g_assert(qdict_haskey(props, "node-id"));
207         node = qdict_get_int(props, "node-id");
208         g_assert(qdict_haskey(props, "core-id"));
209         core = qdict_get_int(props, "core-id");
210 
211         if (core >= 0 && core < 3) {
212             g_assert_cmpint(node, ==, 0);
213         } else if (core == 3) {
214             g_assert_cmpint(node, ==, 1);
215         } else {
216             g_assert(false);
217         }
218         qobject_unref(e);
219     }
220 
221     qobject_unref(resp);
222     qtest_quit(qts);
223     g_free(cli);
224 }
225 
aarch64_numa_cpu(const void * data)226 static void aarch64_numa_cpu(const void *data)
227 {
228     char *cli;
229     QDict *resp;
230     QList *cpus;
231     QObject *e;
232     QTestState *qts;
233 
234     cli = make_cli(data, "-smp 2 "
235         "-numa node,nodeid=0 -numa node,nodeid=1 "
236         "-numa cpu,node-id=1,thread-id=0 "
237         "-numa cpu,node-id=0,thread-id=1");
238     qts = qtest_init(cli);
239     cpus = get_cpus(qts, &resp);
240     g_assert(cpus);
241 
242     while ((e = qlist_pop(cpus))) {
243         QDict *cpu, *props;
244         int64_t thread, node;
245 
246         cpu = qobject_to(QDict, e);
247         g_assert(qdict_haskey(cpu, "props"));
248         props = qdict_get_qdict(cpu, "props");
249 
250         g_assert(qdict_haskey(props, "node-id"));
251         node = qdict_get_int(props, "node-id");
252         g_assert(qdict_haskey(props, "thread-id"));
253         thread = qdict_get_int(props, "thread-id");
254 
255         if (thread == 0) {
256             g_assert_cmpint(node, ==, 1);
257         } else if (thread == 1) {
258             g_assert_cmpint(node, ==, 0);
259         } else {
260             g_assert(false);
261         }
262         qobject_unref(e);
263     }
264 
265     qobject_unref(resp);
266     qtest_quit(qts);
267     g_free(cli);
268 }
269 
pc_dynamic_cpu_cfg(const void * data)270 static void pc_dynamic_cpu_cfg(const void *data)
271 {
272     QObject *e;
273     QDict *resp;
274     QList *cpus;
275     QTestState *qs;
276 
277     qs = qtest_initf("%s -nodefaults --preconfig -smp 2",
278                      data ? (char *)data : "");
279 
280     /* create 2 numa nodes */
281     g_assert(!qmp_rsp_is_err(qtest_qmp(qs, "{ 'execute': 'set-numa-node',"
282         " 'arguments': { 'type': 'node', 'nodeid': 0 } }")));
283     g_assert(!qmp_rsp_is_err(qtest_qmp(qs, "{ 'execute': 'set-numa-node',"
284         " 'arguments': { 'type': 'node', 'nodeid': 1 } }")));
285 
286     /* map 2 cpus in non default reverse order
287      * i.e socket1->node0, socket0->node1
288      */
289     g_assert(!qmp_rsp_is_err(qtest_qmp(qs, "{ 'execute': 'set-numa-node',"
290         " 'arguments': { 'type': 'cpu', 'node-id': 0, 'socket-id': 1 } }")));
291     g_assert(!qmp_rsp_is_err(qtest_qmp(qs, "{ 'execute': 'set-numa-node',"
292         " 'arguments': { 'type': 'cpu', 'node-id': 1, 'socket-id': 0 } }")));
293 
294     /* let machine initialization to complete and run */
295     g_assert(!qmp_rsp_is_err(qtest_qmp(qs, "{ 'execute': 'x-exit-preconfig' }")));
296     qtest_qmp_eventwait(qs, "RESUME");
297 
298     /* check that CPUs are mapped as expected */
299     resp = qtest_qmp(qs, "{ 'execute': 'query-hotpluggable-cpus'}");
300     g_assert(qdict_haskey(resp, "return"));
301     cpus = qdict_get_qlist(resp, "return");
302     g_assert(cpus);
303     while ((e = qlist_pop(cpus))) {
304         const QDict *cpu, *props;
305         int64_t socket, node;
306 
307         cpu = qobject_to(QDict, e);
308         g_assert(qdict_haskey(cpu, "props"));
309         props = qdict_get_qdict(cpu, "props");
310 
311         g_assert(qdict_haskey(props, "node-id"));
312         node = qdict_get_int(props, "node-id");
313         g_assert(qdict_haskey(props, "socket-id"));
314         socket = qdict_get_int(props, "socket-id");
315 
316         if (socket == 0) {
317             g_assert_cmpint(node, ==, 1);
318         } else if (socket == 1) {
319             g_assert_cmpint(node, ==, 0);
320         } else {
321             g_assert(false);
322         }
323         qobject_unref(e);
324     }
325     qobject_unref(resp);
326 
327     qtest_quit(qs);
328 }
329 
main(int argc,char ** argv)330 int main(int argc, char **argv)
331 {
332     const char *args = NULL;
333     const char *arch = qtest_get_arch();
334 
335     if (strcmp(arch, "aarch64") == 0) {
336         args = "-machine virt";
337     }
338 
339     g_test_init(&argc, &argv, NULL);
340 
341     qtest_add_data_func("/numa/mon/default", args, test_mon_default);
342     qtest_add_data_func("/numa/mon/cpus/explicit", args, test_mon_explicit);
343     qtest_add_data_func("/numa/mon/cpus/partial", args, test_mon_partial);
344     qtest_add_data_func("/numa/qmp/cpus/query-cpus", args, test_query_cpus);
345 
346     if (!strcmp(arch, "i386") || !strcmp(arch, "x86_64")) {
347         qtest_add_data_func("/numa/pc/cpu/explicit", args, pc_numa_cpu);
348         qtest_add_data_func("/numa/pc/dynamic/cpu", args, pc_dynamic_cpu_cfg);
349     }
350 
351     if (!strcmp(arch, "ppc64")) {
352         qtest_add_data_func("/numa/spapr/cpu/explicit", args, spapr_numa_cpu);
353     }
354 
355     if (!strcmp(arch, "aarch64")) {
356         qtest_add_data_func("/numa/aarch64/cpu/explicit", args,
357                             aarch64_numa_cpu);
358     }
359 
360     return g_test_run();
361 }
362