]> Git Repo - qemu.git/blob - tests/postcopy-test.c
tests: virtio-9p: add walk operation test
[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 #include "hw/nvram/chrp_nvram.h"
22
23 #define MIN_NVRAM_SIZE 8192 /* from spapr_nvram.c */
24
25 const unsigned start_address = 1024 * 1024;
26 const unsigned end_address = 100 * 1024 * 1024;
27 bool got_stop;
28
29 #if defined(__linux__)
30 #include <sys/syscall.h>
31 #include <sys/vfs.h>
32 #endif
33
34 #if defined(__linux__) && defined(__NR_userfaultfd) && defined(CONFIG_EVENTFD)
35 #include <sys/eventfd.h>
36 #include <sys/ioctl.h>
37 #include <linux/userfaultfd.h>
38
39 static bool ufd_version_check(void)
40 {
41     struct uffdio_api api_struct;
42     uint64_t ioctl_mask;
43
44     int ufd = ufd = syscall(__NR_userfaultfd, O_CLOEXEC);
45
46     if (ufd == -1) {
47         g_test_message("Skipping test: userfaultfd not available");
48         return false;
49     }
50
51     api_struct.api = UFFD_API;
52     api_struct.features = 0;
53     if (ioctl(ufd, UFFDIO_API, &api_struct)) {
54         g_test_message("Skipping test: UFFDIO_API failed");
55         return false;
56     }
57
58     ioctl_mask = (__u64)1 << _UFFDIO_REGISTER |
59                  (__u64)1 << _UFFDIO_UNREGISTER;
60     if ((api_struct.ioctls & ioctl_mask) != ioctl_mask) {
61         g_test_message("Skipping test: Missing userfault feature");
62         return false;
63     }
64
65     return true;
66 }
67
68 #else
69 static bool ufd_version_check(void)
70 {
71     g_test_message("Skipping test: Userfault not available (builtdtime)");
72     return false;
73 }
74
75 #endif
76
77 static const char *tmpfs;
78
79 /* A simple PC boot sector that modifies memory (1-100MB) quickly
80  * outputing a 'B' every so often if it's still running.
81  */
82 unsigned char bootsect[] = {
83   0xfa, 0x0f, 0x01, 0x16, 0x74, 0x7c, 0x66, 0xb8, 0x01, 0x00, 0x00, 0x00,
84   0x0f, 0x22, 0xc0, 0x66, 0xea, 0x20, 0x7c, 0x00, 0x00, 0x08, 0x00, 0x00,
85   0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xe4, 0x92, 0x0c, 0x02,
86   0xe6, 0x92, 0xb8, 0x10, 0x00, 0x00, 0x00, 0x8e, 0xd8, 0x66, 0xb8, 0x41,
87   0x00, 0x66, 0xba, 0xf8, 0x03, 0xee, 0xb3, 0x00, 0xb8, 0x00, 0x00, 0x10,
88   0x00, 0xfe, 0x00, 0x05, 0x00, 0x10, 0x00, 0x00, 0x3d, 0x00, 0x00, 0x40,
89   0x06, 0x7c, 0xf2, 0xfe, 0xc3, 0x75, 0xe9, 0x66, 0xb8, 0x42, 0x00, 0x66,
90   0xba, 0xf8, 0x03, 0xee, 0xeb, 0xde, 0x66, 0x90, 0x00, 0x00, 0x00, 0x00,
91   0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0x00, 0x00, 0x00, 0x9a, 0xcf, 0x00,
92   0xff, 0xff, 0x00, 0x00, 0x00, 0x92, 0xcf, 0x00, 0x27, 0x00, 0x5c, 0x7c,
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, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
123   0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
124   0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
125   0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x55, 0xaa
126 };
127
128 static void init_bootfile_x86(const char *bootpath)
129 {
130     FILE *bootfile = fopen(bootpath, "wb");
131
132     g_assert_cmpint(fwrite(bootsect, 512, 1, bootfile), ==, 1);
133     fclose(bootfile);
134 }
135
136 static void init_bootfile_ppc(const char *bootpath)
137 {
138     FILE *bootfile;
139     char buf[MIN_NVRAM_SIZE];
140     ChrpNvramPartHdr *header = (ChrpNvramPartHdr *)buf;
141
142     memset(buf, 0, MIN_NVRAM_SIZE);
143
144     /* Create a "common" partition in nvram to store boot-command property */
145
146     header->signature = CHRP_NVPART_SYSTEM;
147     memcpy(header->name, "common", 6);
148     chrp_nvram_finish_partition(header, MIN_NVRAM_SIZE);
149
150     /* FW_MAX_SIZE is 4MB, but slof.bin is only 900KB,
151      * so let's modify memory between 1MB and 100MB
152      * to do like PC bootsector
153      */
154
155     sprintf(buf + 16,
156             "boot-command=hex .\" _\" begin %x %x do i c@ 1 + i c! 1000 +loop "
157             ".\" B\" 0 until", end_address, start_address);
158
159     /* Write partition to the NVRAM file */
160
161     bootfile = fopen(bootpath, "wb");
162     g_assert_cmpint(fwrite(buf, MIN_NVRAM_SIZE, 1, bootfile), ==, 1);
163     fclose(bootfile);
164 }
165
166 /*
167  * Wait for some output in the serial output file,
168  * we get an 'A' followed by an endless string of 'B's
169  * but on the destination we won't have the A.
170  */
171 static void wait_for_serial(const char *side)
172 {
173     char *serialpath = g_strdup_printf("%s/%s", tmpfs, side);
174     FILE *serialfile = fopen(serialpath, "r");
175     const char *arch = qtest_get_arch();
176     int started = (strcmp(side, "src_serial") == 0 &&
177                    strcmp(arch, "ppc64") == 0) ? 0 : 1;
178
179     g_free(serialpath);
180     do {
181         int readvalue = fgetc(serialfile);
182
183         if (!started) {
184             /* SLOF prints its banner before starting test,
185              * to ignore it, mark the start of the test with '_',
186              * ignore all characters until this marker
187              */
188             switch (readvalue) {
189             case '_':
190                 started = 1;
191                 break;
192             case EOF:
193                 fseek(serialfile, 0, SEEK_SET);
194                 usleep(1000);
195                 break;
196             }
197             continue;
198         }
199         switch (readvalue) {
200         case 'A':
201             /* Fine */
202             break;
203
204         case 'B':
205             /* It's alive! */
206             fclose(serialfile);
207             return;
208
209         case EOF:
210             started = (strcmp(side, "src_serial") == 0 &&
211                        strcmp(arch, "ppc64") == 0) ? 0 : 1;
212             fseek(serialfile, 0, SEEK_SET);
213             usleep(1000);
214             break;
215
216         default:
217             fprintf(stderr, "Unexpected %d on %s serial\n", readvalue, side);
218             g_assert_not_reached();
219         }
220     } while (true);
221 }
222
223 /*
224  * Events can get in the way of responses we are actually waiting for.
225  */
226 static QDict *return_or_event(QDict *response)
227 {
228     const char *event_string;
229     if (!qdict_haskey(response, "event")) {
230         return response;
231     }
232
233     /* OK, it was an event */
234     event_string = qdict_get_str(response, "event");
235     if (!strcmp(event_string, "STOP")) {
236         got_stop = true;
237     }
238     QDECREF(response);
239     return return_or_event(qtest_qmp_receive(global_qtest));
240 }
241
242
243 /*
244  * It's tricky to use qemu's migration event capability with qtest,
245  * events suddenly appearing confuse the qmp()/hmp() responses.
246  * so wait for a couple of passes to have happened before
247  * going postcopy.
248  */
249
250 static uint64_t get_migration_pass(void)
251 {
252     QDict *rsp, *rsp_return, *rsp_ram;
253     uint64_t result;
254
255     rsp = return_or_event(qmp("{ 'execute': 'query-migrate' }"));
256     rsp_return = qdict_get_qdict(rsp, "return");
257     if (!qdict_haskey(rsp_return, "ram")) {
258         /* Still in setup */
259         result = 0;
260     } else {
261         rsp_ram = qdict_get_qdict(rsp_return, "ram");
262         result = qdict_get_try_int(rsp_ram, "dirty-sync-count", 0);
263     }
264     QDECREF(rsp);
265     return result;
266 }
267
268 static void wait_for_migration_complete(void)
269 {
270     QDict *rsp, *rsp_return;
271     bool completed;
272
273     do {
274         const char *status;
275
276         rsp = return_or_event(qmp("{ 'execute': 'query-migrate' }"));
277         rsp_return = qdict_get_qdict(rsp, "return");
278         status = qdict_get_str(rsp_return, "status");
279         completed = strcmp(status, "completed") == 0;
280         g_assert_cmpstr(status, !=,  "failed");
281         QDECREF(rsp);
282         usleep(1000 * 100);
283     } while (!completed);
284 }
285
286 static void wait_for_migration_pass(void)
287 {
288     uint64_t initial_pass = get_migration_pass();
289     uint64_t pass;
290
291     /* Wait for the 1st sync */
292     do {
293         initial_pass = get_migration_pass();
294         if (got_stop || initial_pass) {
295             break;
296         }
297         usleep(1000 * 100);
298     } while (true);
299
300     do {
301         usleep(1000 * 100);
302         pass = get_migration_pass();
303     } while (pass == initial_pass && !got_stop);
304 }
305
306 static void check_guests_ram(void)
307 {
308     /* Our ASM test will have been incrementing one byte from each page from
309      * 1MB to <100MB in order.
310      * This gives us a constraint that any page's byte should be equal or less
311      * than the previous pages byte (mod 256); and they should all be equal
312      * except for one transition at the point where we meet the incrementer.
313      * (We're running this with the guest stopped).
314      */
315     unsigned address;
316     uint8_t first_byte;
317     uint8_t last_byte;
318     bool hit_edge = false;
319     bool bad = false;
320
321     qtest_memread(global_qtest, start_address, &first_byte, 1);
322     last_byte = first_byte;
323
324     for (address = start_address + 4096; address < end_address; address += 4096)
325     {
326         uint8_t b;
327         qtest_memread(global_qtest, address, &b, 1);
328         if (b != last_byte) {
329             if (((b + 1) % 256) == last_byte && !hit_edge) {
330                 /* This is OK, the guest stopped at the point of
331                  * incrementing the previous page but didn't get
332                  * to us yet.
333                  */
334                 hit_edge = true;
335             } else {
336                 fprintf(stderr, "Memory content inconsistency at %x"
337                                 " first_byte = %x last_byte = %x current = %x"
338                                 " hit_edge = %x\n",
339                                 address, first_byte, last_byte, b, hit_edge);
340                 bad = true;
341             }
342         }
343         last_byte = b;
344     }
345     g_assert_false(bad);
346 }
347
348 static void cleanup(const char *filename)
349 {
350     char *path = g_strdup_printf("%s/%s", tmpfs, filename);
351
352     unlink(path);
353     g_free(path);
354 }
355
356 static void test_migrate(void)
357 {
358     char *uri = g_strdup_printf("unix:%s/migsocket", tmpfs);
359     QTestState *global = global_qtest, *from, *to;
360     unsigned char dest_byte_a, dest_byte_b, dest_byte_c, dest_byte_d;
361     gchar *cmd, *cmd_src, *cmd_dst;
362     QDict *rsp;
363
364     char *bootpath = g_strdup_printf("%s/bootsect", tmpfs);
365     const char *arch = qtest_get_arch();
366
367     got_stop = false;
368
369     if (strcmp(arch, "i386") == 0 || strcmp(arch, "x86_64") == 0) {
370         init_bootfile_x86(bootpath);
371         cmd_src = g_strdup_printf("-machine accel=kvm:tcg -m 150M"
372                                   " -name pcsource,debug-threads=on"
373                                   " -serial file:%s/src_serial"
374                                   " -drive file=%s,format=raw",
375                                   tmpfs, bootpath);
376         cmd_dst = g_strdup_printf("-machine accel=kvm:tcg -m 150M"
377                                   " -name pcdest,debug-threads=on"
378                                   " -serial file:%s/dest_serial"
379                                   " -drive file=%s,format=raw"
380                                   " -incoming %s",
381                                   tmpfs, bootpath, uri);
382     } else if (strcmp(arch, "ppc64") == 0) {
383         const char *accel;
384
385         /* On ppc64, the test only works with kvm-hv, but not with kvm-pr */
386         accel = access("/sys/module/kvm_hv", F_OK) ? "tcg" : "kvm:tcg";
387         init_bootfile_ppc(bootpath);
388         cmd_src = g_strdup_printf("-machine accel=%s -m 256M"
389                                   " -name pcsource,debug-threads=on"
390                                   " -serial file:%s/src_serial"
391                                   " -drive file=%s,if=pflash,format=raw",
392                                   accel, tmpfs, bootpath);
393         cmd_dst = g_strdup_printf("-machine accel=%s -m 256M"
394                                   " -name pcdest,debug-threads=on"
395                                   " -serial file:%s/dest_serial"
396                                   " -incoming %s",
397                                   accel, tmpfs, uri);
398     } else {
399         g_assert_not_reached();
400     }
401
402     g_free(bootpath);
403
404     from = qtest_start(cmd_src);
405     g_free(cmd_src);
406
407     to = qtest_init(cmd_dst);
408     g_free(cmd_dst);
409
410     global_qtest = from;
411     rsp = qmp("{ 'execute': 'migrate-set-capabilities',"
412                   "'arguments': { "
413                       "'capabilities': [ {"
414                           "'capability': 'postcopy-ram',"
415                           "'state': true } ] } }");
416     g_assert(qdict_haskey(rsp, "return"));
417     QDECREF(rsp);
418
419     global_qtest = to;
420     rsp = qmp("{ 'execute': 'migrate-set-capabilities',"
421                   "'arguments': { "
422                       "'capabilities': [ {"
423                           "'capability': 'postcopy-ram',"
424                           "'state': true } ] } }");
425     g_assert(qdict_haskey(rsp, "return"));
426     QDECREF(rsp);
427
428     /* We want to pick a speed slow enough that the test completes
429      * quickly, but that it doesn't complete precopy even on a slow
430      * machine, so also set the downtime.
431      */
432     global_qtest = from;
433     rsp = qmp("{ 'execute': 'migrate_set_speed',"
434               "'arguments': { 'value': 100000000 } }");
435     g_assert(qdict_haskey(rsp, "return"));
436     QDECREF(rsp);
437
438     /* 1ms downtime - it should never finish precopy */
439     rsp = qmp("{ 'execute': 'migrate_set_downtime',"
440               "'arguments': { 'value': 0.001 } }");
441     g_assert(qdict_haskey(rsp, "return"));
442     QDECREF(rsp);
443
444
445     /* Wait for the first serial output from the source */
446     wait_for_serial("src_serial");
447
448     cmd = g_strdup_printf("{ 'execute': 'migrate',"
449                           "'arguments': { 'uri': '%s' } }",
450                           uri);
451     rsp = qmp(cmd);
452     g_free(cmd);
453     g_assert(qdict_haskey(rsp, "return"));
454     QDECREF(rsp);
455
456     wait_for_migration_pass();
457
458     rsp = return_or_event(qmp("{ 'execute': 'migrate-start-postcopy' }"));
459     g_assert(qdict_haskey(rsp, "return"));
460     QDECREF(rsp);
461
462     if (!got_stop) {
463         qmp_eventwait("STOP");
464     }
465
466     global_qtest = to;
467     qmp_eventwait("RESUME");
468
469     wait_for_serial("dest_serial");
470     global_qtest = from;
471     wait_for_migration_complete();
472
473     qtest_quit(from);
474
475     global_qtest = to;
476
477     qtest_memread(to, start_address, &dest_byte_a, 1);
478
479     /* Destination still running, wait for a byte to change */
480     do {
481         qtest_memread(to, start_address, &dest_byte_b, 1);
482         usleep(10 * 1000);
483     } while (dest_byte_a == dest_byte_b);
484
485     qmp("{ 'execute' : 'stop'}");
486     /* With it stopped, check nothing changes */
487     qtest_memread(to, start_address, &dest_byte_c, 1);
488     sleep(1);
489     qtest_memread(to, start_address, &dest_byte_d, 1);
490     g_assert_cmpint(dest_byte_c, ==, dest_byte_d);
491
492     check_guests_ram();
493
494     qtest_quit(to);
495     g_free(uri);
496
497     global_qtest = global;
498
499     cleanup("bootsect");
500     cleanup("migsocket");
501     cleanup("src_serial");
502     cleanup("dest_serial");
503 }
504
505 int main(int argc, char **argv)
506 {
507     char template[] = "/tmp/postcopy-test-XXXXXX";
508     int ret;
509
510     g_test_init(&argc, &argv, NULL);
511
512     if (!ufd_version_check()) {
513         return 0;
514     }
515
516     tmpfs = mkdtemp(template);
517     if (!tmpfs) {
518         g_test_message("mkdtemp on path (%s): %s\n", template, strerror(errno));
519     }
520     g_assert(tmpfs);
521
522     module_call_init(MODULE_INIT_QOM);
523
524     qtest_add_func("/postcopy", test_migrate);
525
526     ret = g_test_run();
527
528     g_assert_cmpint(ret, ==, 0);
529
530     ret = rmdir(tmpfs);
531     if (ret != 0) {
532         g_test_message("unable to rmdir: path (%s): %s\n",
533                        tmpfs, strerror(errno));
534     }
535
536     return ret;
537 }
This page took 0.077806 seconds and 4 git commands to generate.