]> Git Repo - qemu.git/blob - tests/postcopy-test.c
qapi: Plumb in 'boxed' to qapi generator lower levels
[qemu.git] / tests / postcopy-test.c
1 /*
2  * QTest testcase for postcopy
3  *
4  * Copyright (c) 2016 Red Hat, Inc. and/or its affiliates
5  *   based on the vhost-user-test.c that is:
6  *      Copyright (c) 2014 Virtual Open Systems Sarl.
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
13 #include "qemu/osdep.h"
14
15 #include "libqtest.h"
16 #include "qemu/option.h"
17 #include "qemu/range.h"
18 #include "qemu/sockets.h"
19 #include "sysemu/char.h"
20 #include "sysemu/sysemu.h"
21
22 const unsigned start_address = 1024 * 1024;
23 const unsigned end_address = 100 * 1024 * 1024;
24 bool got_stop;
25
26 #if defined(__linux__)
27 #include <sys/syscall.h>
28 #include <sys/vfs.h>
29 #endif
30
31 #if defined(__linux__) && defined(__NR_userfaultfd) && defined(CONFIG_EVENTFD)
32 #include <sys/eventfd.h>
33 #include <sys/ioctl.h>
34 #include <linux/userfaultfd.h>
35
36 static bool ufd_version_check(void)
37 {
38     struct uffdio_api api_struct;
39     uint64_t ioctl_mask;
40
41     int ufd = ufd = syscall(__NR_userfaultfd, O_CLOEXEC);
42
43     if (ufd == -1) {
44         g_test_message("Skipping test: userfaultfd not available");
45         return false;
46     }
47
48     api_struct.api = UFFD_API;
49     api_struct.features = 0;
50     if (ioctl(ufd, UFFDIO_API, &api_struct)) {
51         g_test_message("Skipping test: UFFDIO_API failed");
52         return false;
53     }
54
55     ioctl_mask = (__u64)1 << _UFFDIO_REGISTER |
56                  (__u64)1 << _UFFDIO_UNREGISTER;
57     if ((api_struct.ioctls & ioctl_mask) != ioctl_mask) {
58         g_test_message("Skipping test: Missing userfault feature");
59         return false;
60     }
61
62     return true;
63 }
64
65 #else
66 static bool ufd_version_check(void)
67 {
68     g_test_message("Skipping test: Userfault not available (builtdtime)");
69     return false;
70 }
71
72 #endif
73
74 static const char *tmpfs;
75
76 /* A simple PC boot sector that modifies memory (1-100MB) quickly
77  * outputing a 'B' every so often if it's still running.
78  */
79 unsigned char bootsect[] = {
80   0xfa, 0x0f, 0x01, 0x16, 0x74, 0x7c, 0x66, 0xb8, 0x01, 0x00, 0x00, 0x00,
81   0x0f, 0x22, 0xc0, 0x66, 0xea, 0x20, 0x7c, 0x00, 0x00, 0x08, 0x00, 0x00,
82   0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xe4, 0x92, 0x0c, 0x02,
83   0xe6, 0x92, 0xb8, 0x10, 0x00, 0x00, 0x00, 0x8e, 0xd8, 0x66, 0xb8, 0x41,
84   0x00, 0x66, 0xba, 0xf8, 0x03, 0xee, 0xb3, 0x00, 0xb8, 0x00, 0x00, 0x10,
85   0x00, 0xfe, 0x00, 0x05, 0x00, 0x10, 0x00, 0x00, 0x3d, 0x00, 0x00, 0x40,
86   0x06, 0x7c, 0xf2, 0xfe, 0xc3, 0x75, 0xe9, 0x66, 0xb8, 0x42, 0x00, 0x66,
87   0xba, 0xf8, 0x03, 0xee, 0xeb, 0xde, 0x66, 0x90, 0x00, 0x00, 0x00, 0x00,
88   0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0x00, 0x00, 0x00, 0x9a, 0xcf, 0x00,
89   0xff, 0xff, 0x00, 0x00, 0x00, 0x92, 0xcf, 0x00, 0x27, 0x00, 0x5c, 0x7c,
90   0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
91   0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
92   0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
93   0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
94   0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
95   0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
96   0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
97   0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
98   0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
99   0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
100   0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
101   0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
102   0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
103   0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
104   0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
105   0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
106   0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
107   0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
108   0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
109   0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
110   0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
111   0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
112   0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
113   0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
114   0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
115   0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
116   0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
117   0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
118   0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
119   0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
120   0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
121   0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
122   0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x55, 0xaa
123 };
124
125 /*
126  * Wait for some output in the serial output file,
127  * we get an 'A' followed by an endless string of 'B's
128  * but on the destination we won't have the A.
129  */
130 static void wait_for_serial(const char *side)
131 {
132     char *serialpath = g_strdup_printf("%s/%s", tmpfs, side);
133     FILE *serialfile = fopen(serialpath, "r");
134
135     do {
136         int readvalue = fgetc(serialfile);
137
138         switch (readvalue) {
139         case 'A':
140             /* Fine */
141             break;
142
143         case 'B':
144             /* It's alive! */
145             fclose(serialfile);
146             g_free(serialpath);
147             return;
148
149         case EOF:
150             fseek(serialfile, 0, SEEK_SET);
151             usleep(1000);
152             break;
153
154         default:
155             fprintf(stderr, "Unexpected %d on %s serial\n", readvalue, side);
156             g_assert_not_reached();
157         }
158     } while (true);
159 }
160
161 /*
162  * Events can get in the way of responses we are actually waiting for.
163  */
164 static QDict *return_or_event(QDict *response)
165 {
166     const char *event_string;
167     if (!qdict_haskey(response, "event")) {
168         return response;
169     }
170
171     /* OK, it was an event */
172     event_string = qdict_get_str(response, "event");
173     if (!strcmp(event_string, "STOP")) {
174         got_stop = true;
175     }
176     QDECREF(response);
177     return return_or_event(qtest_qmp_receive(global_qtest));
178 }
179
180
181 /*
182  * It's tricky to use qemu's migration event capability with qtest,
183  * events suddenly appearing confuse the qmp()/hmp() responses.
184  * so wait for a couple of passes to have happened before
185  * going postcopy.
186  */
187
188 static uint64_t get_migration_pass(void)
189 {
190     QDict *rsp, *rsp_return, *rsp_ram;
191     uint64_t result;
192
193     rsp = return_or_event(qmp("{ 'execute': 'query-migrate' }"));
194     rsp_return = qdict_get_qdict(rsp, "return");
195     if (!qdict_haskey(rsp_return, "ram")) {
196         /* Still in setup */
197         result = 0;
198     } else {
199         rsp_ram = qdict_get_qdict(rsp_return, "ram");
200         result = qdict_get_try_int(rsp_ram, "dirty-sync-count", 0);
201         QDECREF(rsp);
202     }
203     return result;
204 }
205
206 static void wait_for_migration_complete(void)
207 {
208     QDict *rsp, *rsp_return;
209     bool completed;
210
211     do {
212         const char *status;
213
214         rsp = return_or_event(qmp("{ 'execute': 'query-migrate' }"));
215         rsp_return = qdict_get_qdict(rsp, "return");
216         status = qdict_get_str(rsp_return, "status");
217         completed = strcmp(status, "completed") == 0;
218         g_assert_cmpstr(status, !=,  "failed");
219         QDECREF(rsp);
220         usleep(1000 * 100);
221     } while (!completed);
222 }
223
224 static void wait_for_migration_pass(void)
225 {
226     uint64_t initial_pass = get_migration_pass();
227     uint64_t pass;
228
229     /* Wait for the 1st sync */
230     do {
231         initial_pass = get_migration_pass();
232         if (got_stop || initial_pass) {
233             break;
234         }
235         usleep(1000 * 100);
236     } while (true);
237
238     do {
239         usleep(1000 * 100);
240         pass = get_migration_pass();
241     } while (pass == initial_pass && !got_stop);
242 }
243
244 static void check_guests_ram(void)
245 {
246     /* Our ASM test will have been incrementing one byte from each page from
247      * 1MB to <100MB in order.
248      * This gives us a constraint that any page's byte should be equal or less
249      * than the previous pages byte (mod 256); and they should all be equal
250      * except for one transition at the point where we meet the incrementer.
251      * (We're running this with the guest stopped).
252      */
253     unsigned address;
254     uint8_t first_byte;
255     uint8_t last_byte;
256     bool hit_edge = false;
257     bool bad = false;
258
259     qtest_memread(global_qtest, start_address, &first_byte, 1);
260     last_byte = first_byte;
261
262     for (address = start_address + 4096; address < end_address; address += 4096)
263     {
264         uint8_t b;
265         qtest_memread(global_qtest, address, &b, 1);
266         if (b != last_byte) {
267             if (((b + 1) % 256) == last_byte && !hit_edge) {
268                 /* This is OK, the guest stopped at the point of
269                  * incrementing the previous page but didn't get
270                  * to us yet.
271                  */
272                 hit_edge = true;
273             } else {
274                 fprintf(stderr, "Memory content inconsistency at %x"
275                                 " first_byte = %x last_byte = %x current = %x"
276                                 " hit_edge = %x\n",
277                                 address, first_byte, last_byte, b, hit_edge);
278                 bad = true;
279             }
280         }
281         last_byte = b;
282     }
283     g_assert_false(bad);
284 }
285
286 static void cleanup(const char *filename)
287 {
288     char *path = g_strdup_printf("%s/%s", tmpfs, filename);
289
290     unlink(path);
291 }
292
293 static void test_migrate(void)
294 {
295     char *uri = g_strdup_printf("unix:%s/migsocket", tmpfs);
296     QTestState *global = global_qtest, *from, *to;
297     unsigned char dest_byte_a, dest_byte_b, dest_byte_c, dest_byte_d;
298     gchar *cmd;
299     QDict *rsp;
300
301     char *bootpath = g_strdup_printf("%s/bootsect", tmpfs);
302     FILE *bootfile = fopen(bootpath, "wb");
303
304     got_stop = false;
305     g_assert_cmpint(fwrite(bootsect, 512, 1, bootfile), ==, 1);
306     fclose(bootfile);
307
308     cmd = g_strdup_printf("-machine accel=kvm:tcg -m 150M"
309                           " -name pcsource,debug-threads=on"
310                           " -serial file:%s/src_serial"
311                           " -drive file=%s,format=raw",
312                           tmpfs, bootpath);
313     from = qtest_start(cmd);
314     g_free(cmd);
315
316     cmd = g_strdup_printf("-machine accel=kvm:tcg -m 150M"
317                           " -name pcdest,debug-threads=on"
318                           " -serial file:%s/dest_serial"
319                           " -drive file=%s,format=raw"
320                           " -incoming %s",
321                           tmpfs, bootpath, uri);
322     to = qtest_init(cmd);
323     g_free(cmd);
324
325     global_qtest = from;
326     rsp = qmp("{ 'execute': 'migrate-set-capabilities',"
327                   "'arguments': { "
328                       "'capabilities': [ {"
329                           "'capability': 'postcopy-ram',"
330                           "'state': true } ] } }");
331     g_assert(qdict_haskey(rsp, "return"));
332     QDECREF(rsp);
333
334     global_qtest = to;
335     rsp = qmp("{ 'execute': 'migrate-set-capabilities',"
336                   "'arguments': { "
337                       "'capabilities': [ {"
338                           "'capability': 'postcopy-ram',"
339                           "'state': true } ] } }");
340     g_assert(qdict_haskey(rsp, "return"));
341     QDECREF(rsp);
342
343     /* We want to pick a speed slow enough that the test completes
344      * quickly, but that it doesn't complete precopy even on a slow
345      * machine, so also set the downtime.
346      */
347     global_qtest = from;
348     rsp = qmp("{ 'execute': 'migrate_set_speed',"
349               "'arguments': { 'value': 100000000 } }");
350     g_assert(qdict_haskey(rsp, "return"));
351     QDECREF(rsp);
352
353     /* 1ms downtime - it should never finish precopy */
354     rsp = qmp("{ 'execute': 'migrate_set_downtime',"
355               "'arguments': { 'value': 0.001 } }");
356     g_assert(qdict_haskey(rsp, "return"));
357     QDECREF(rsp);
358
359
360     /* Wait for the first serial output from the source */
361     wait_for_serial("src_serial");
362
363     cmd = g_strdup_printf("{ 'execute': 'migrate',"
364                           "'arguments': { 'uri': '%s' } }",
365                           uri);
366     rsp = qmp(cmd);
367     g_free(cmd);
368     g_assert(qdict_haskey(rsp, "return"));
369     QDECREF(rsp);
370
371     wait_for_migration_pass();
372
373     rsp = return_or_event(qmp("{ 'execute': 'migrate-start-postcopy' }"));
374     g_assert(qdict_haskey(rsp, "return"));
375     QDECREF(rsp);
376
377     if (!got_stop) {
378         qmp_eventwait("STOP");
379     }
380
381     global_qtest = to;
382     qmp_eventwait("RESUME");
383
384     wait_for_serial("dest_serial");
385     global_qtest = from;
386     wait_for_migration_complete();
387
388     qtest_quit(from);
389
390     global_qtest = to;
391
392     qtest_memread(to, start_address, &dest_byte_a, 1);
393
394     /* Destination still running, wait for a byte to change */
395     do {
396         qtest_memread(to, start_address, &dest_byte_b, 1);
397         usleep(10 * 1000);
398     } while (dest_byte_a == dest_byte_b);
399
400     qmp("{ 'execute' : 'stop'}");
401     /* With it stopped, check nothing changes */
402     qtest_memread(to, start_address, &dest_byte_c, 1);
403     sleep(1);
404     qtest_memread(to, start_address, &dest_byte_d, 1);
405     g_assert_cmpint(dest_byte_c, ==, dest_byte_d);
406
407     check_guests_ram();
408
409     qtest_quit(to);
410     g_free(uri);
411
412     global_qtest = global;
413
414     cleanup("bootsect");
415     cleanup("migsocket");
416     cleanup("src_serial");
417     cleanup("dest_serial");
418 }
419
420 int main(int argc, char **argv)
421 {
422     char template[] = "/tmp/postcopy-test-XXXXXX";
423     int ret;
424
425     g_test_init(&argc, &argv, NULL);
426
427     if (!ufd_version_check()) {
428         return 0;
429     }
430
431     tmpfs = mkdtemp(template);
432     if (!tmpfs) {
433         g_test_message("mkdtemp on path (%s): %s\n", template, strerror(errno));
434     }
435     g_assert(tmpfs);
436
437     module_call_init(MODULE_INIT_QOM);
438
439     qtest_add_func("/postcopy", test_migrate);
440
441     ret = g_test_run();
442
443     g_assert_cmpint(ret, ==, 0);
444
445     ret = rmdir(tmpfs);
446     if (ret != 0) {
447         g_test_message("unable to rmdir: path (%s): %s\n",
448                        tmpfs, strerror(errno));
449     }
450
451     return ret;
452 }
This page took 0.055896 seconds and 4 git commands to generate.