]> git.proxmox.com Git - mirror_qemu.git/blame - tests/postcopy-test.c
test: Postcopy
[mirror_qemu.git] / tests / postcopy-test.c
CommitLineData
ea0c6d62
DDAG
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#include <glib.h>
15
16#include "libqtest.h"
17#include "qemu/option.h"
18#include "qemu/range.h"
19#include "sysemu/char.h"
20#include "sysemu/sysemu.h"
21
22#include <qemu/sockets.h>
23
24const unsigned start_address = 1024 * 1024;
25const unsigned end_address = 100 * 1024 * 1024;
26bool got_stop;
27
28#if defined(__linux__)
29#include <sys/mman.h>
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
39static 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
69static bool ufd_version_check(void)
70{
71 g_test_message("Skipping test: Userfault not available (builtdtime)");
72 return false;
73}
74
75#endif
76
77static 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 */
82unsigned 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/*
129 * Wait for some output in the serial output file,
130 * we get an 'A' followed by an endless string of 'B's
131 * but on the destination we won't have the A.
132 */
133static void wait_for_serial(const char *side)
134{
135 char *serialpath = g_strdup_printf("%s/%s", tmpfs, side);
136 FILE *serialfile = fopen(serialpath, "r");
137
138 do {
139 int readvalue = fgetc(serialfile);
140
141 switch (readvalue) {
142 case 'A':
143 /* Fine */
144 break;
145
146 case 'B':
147 /* It's alive! */
148 fclose(serialfile);
149 g_free(serialpath);
150 return;
151
152 case EOF:
153 fseek(serialfile, 0, SEEK_SET);
154 usleep(1000);
155 break;
156
157 default:
158 fprintf(stderr, "Unexpected %d on %s serial\n", readvalue, side);
159 g_assert_not_reached();
160 }
161 } while (true);
162}
163
164/*
165 * Events can get in the way of responses we are actually waiting for.
166 */
167static QDict *return_or_event(QDict *response)
168{
169 const char *event_string;
170 if (!qdict_haskey(response, "event")) {
171 return response;
172 }
173
174 /* OK, it was an event */
175 event_string = qdict_get_str(response, "event");
176 if (!strcmp(event_string, "STOP")) {
177 got_stop = true;
178 }
179 QDECREF(response);
180 return return_or_event(qtest_qmp_receive(global_qtest));
181}
182
183
184/*
185 * It's tricky to use qemu's migration event capability with qtest,
186 * events suddenly appearing confuse the qmp()/hmp() responses.
187 * so wait for a couple of passes to have happened before
188 * going postcopy.
189 */
190
191static uint64_t get_migration_pass(void)
192{
193 QDict *rsp, *rsp_return, *rsp_ram;
194 uint64_t result;
195
196 rsp = return_or_event(qmp("{ 'execute': 'query-migrate' }"));
197 rsp_return = qdict_get_qdict(rsp, "return");
198 if (!qdict_haskey(rsp_return, "ram")) {
199 /* Still in setup */
200 result = 0;
201 } else {
202 rsp_ram = qdict_get_qdict(rsp_return, "ram");
203 result = qdict_get_try_int(rsp_ram, "dirty-sync-count", 0);
204 QDECREF(rsp);
205 }
206 return result;
207}
208
209static void wait_for_migration_complete(void)
210{
211 QDict *rsp, *rsp_return;
212 bool completed;
213
214 do {
215 const char *status;
216
217 rsp = return_or_event(qmp("{ 'execute': 'query-migrate' }"));
218 rsp_return = qdict_get_qdict(rsp, "return");
219 status = qdict_get_str(rsp_return, "status");
220 completed = strcmp(status, "completed") == 0;
221 g_assert_cmpstr(status, !=, "failed");
222 QDECREF(rsp);
223 usleep(1000 * 100);
224 } while (!completed);
225}
226
227static void wait_for_migration_pass(void)
228{
229 uint64_t initial_pass = get_migration_pass();
230 uint64_t pass;
231
232 /* Wait for the 1st sync */
233 do {
234 initial_pass = get_migration_pass();
235 if (got_stop || initial_pass) {
236 break;
237 }
238 usleep(1000 * 100);
239 } while (true);
240
241 do {
242 usleep(1000 * 100);
243 pass = get_migration_pass();
244 } while (pass == initial_pass && !got_stop);
245}
246
247static void check_guests_ram(void)
248{
249 /* Our ASM test will have been incrementing one byte from each page from
250 * 1MB to <100MB in order.
251 * This gives us a constraint that any page's byte should be equal or less
252 * than the previous pages byte (mod 256); and they should all be equal
253 * except for one transition at the point where we meet the incrementer.
254 * (We're running this with the guest stopped).
255 */
256 unsigned address;
257 uint8_t first_byte;
258 uint8_t last_byte;
259 bool hit_edge = false;
260 bool bad = false;
261
262 qtest_memread(global_qtest, start_address, &first_byte, 1);
263 last_byte = first_byte;
264
265 for (address = start_address + 4096; address < end_address; address += 4096)
266 {
267 uint8_t b;
268 qtest_memread(global_qtest, address, &b, 1);
269 if (b != last_byte) {
270 if (((b + 1) % 256) == last_byte && !hit_edge) {
271 /* This is OK, the guest stopped at the point of
272 * incrementing the previous page but didn't get
273 * to us yet.
274 */
275 hit_edge = true;
276 } else {
277 fprintf(stderr, "Memory content inconsistency at %x"
278 " first_byte = %x last_byte = %x current = %x"
279 " hit_edge = %x\n",
280 address, first_byte, last_byte, b, hit_edge);
281 bad = true;
282 }
283 }
284 last_byte = b;
285 }
286 g_assert_false(bad);
287}
288
289static void cleanup(const char *filename)
290{
291 char *path = g_strdup_printf("%s/%s", tmpfs, filename);
292
293 unlink(path);
294}
295
296static void test_migrate(void)
297{
298 char *uri = g_strdup_printf("unix:%s/migsocket", tmpfs);
299 QTestState *global = global_qtest, *from, *to;
300 unsigned char dest_byte_a, dest_byte_b, dest_byte_c, dest_byte_d;
301 gchar *cmd;
302 QDict *rsp;
303
304 char *bootpath = g_strdup_printf("%s/bootsect", tmpfs);
305 FILE *bootfile = fopen(bootpath, "wb");
306
307 got_stop = false;
308 g_assert_cmpint(fwrite(bootsect, 512, 1, bootfile), ==, 1);
309 fclose(bootfile);
310
311 cmd = g_strdup_printf("-machine accel=kvm:tcg -m 150M"
312 " -name pcsource,debug-threads=on"
313 " -serial file:%s/src_serial"
314 " -drive file=%s,format=raw",
315 tmpfs, bootpath);
316 from = qtest_start(cmd);
317 g_free(cmd);
318
319 cmd = g_strdup_printf("-machine accel=kvm:tcg -m 150M"
320 " -name pcdest,debug-threads=on"
321 " -serial file:%s/dest_serial"
322 " -drive file=%s,format=raw"
323 " -incoming %s",
324 tmpfs, bootpath, uri);
325 to = qtest_init(cmd);
326 g_free(cmd);
327
328 global_qtest = from;
329 rsp = qmp("{ 'execute': 'migrate-set-capabilities',"
330 "'arguments': { "
331 "'capabilities': [ {"
332 "'capability': 'postcopy-ram',"
333 "'state': true } ] } }");
334 g_assert(qdict_haskey(rsp, "return"));
335 QDECREF(rsp);
336
337 global_qtest = to;
338 rsp = qmp("{ 'execute': 'migrate-set-capabilities',"
339 "'arguments': { "
340 "'capabilities': [ {"
341 "'capability': 'postcopy-ram',"
342 "'state': true } ] } }");
343 g_assert(qdict_haskey(rsp, "return"));
344 QDECREF(rsp);
345
346 /* We want to pick a speed slow enough that the test completes
347 * quickly, but that it doesn't complete precopy even on a slow
348 * machine, so also set the downtime.
349 */
350 global_qtest = from;
351 rsp = qmp("{ 'execute': 'migrate_set_speed',"
352 "'arguments': { 'value': 100000000 } }");
353 g_assert(qdict_haskey(rsp, "return"));
354 QDECREF(rsp);
355
356 /* 1ms downtime - it should never finish precopy */
357 rsp = qmp("{ 'execute': 'migrate_set_downtime',"
358 "'arguments': { 'value': 0.001 } }");
359 g_assert(qdict_haskey(rsp, "return"));
360 QDECREF(rsp);
361
362
363 /* Wait for the first serial output from the source */
364 wait_for_serial("src_serial");
365
366 cmd = g_strdup_printf("{ 'execute': 'migrate',"
367 "'arguments': { 'uri': '%s' } }",
368 uri);
369 rsp = qmp(cmd);
370 g_free(cmd);
371 g_assert(qdict_haskey(rsp, "return"));
372 QDECREF(rsp);
373
374 wait_for_migration_pass();
375
376 rsp = return_or_event(qmp("{ 'execute': 'migrate-start-postcopy' }"));
377 g_assert(qdict_haskey(rsp, "return"));
378 QDECREF(rsp);
379
380 if (!got_stop) {
381 qmp_eventwait("STOP");
382 }
383
384 global_qtest = to;
385 qmp_eventwait("RESUME");
386
387 wait_for_serial("dest_serial");
388 global_qtest = from;
389 wait_for_migration_complete();
390
391 qtest_quit(from);
392
393 global_qtest = to;
394
395 qtest_memread(to, start_address, &dest_byte_a, 1);
396
397 /* Destination still running, wait for a byte to change */
398 do {
399 qtest_memread(to, start_address, &dest_byte_b, 1);
400 usleep(10 * 1000);
401 } while (dest_byte_a == dest_byte_b);
402
403 qmp("{ 'execute' : 'stop'}");
404 /* With it stopped, check nothing changes */
405 qtest_memread(to, start_address, &dest_byte_c, 1);
406 sleep(1);
407 qtest_memread(to, start_address, &dest_byte_d, 1);
408 g_assert_cmpint(dest_byte_c, ==, dest_byte_d);
409
410 check_guests_ram();
411
412 qtest_quit(to);
413 g_free(uri);
414
415 global_qtest = global;
416
417 cleanup("bootsect");
418 cleanup("migsocket");
419 cleanup("src_serial");
420 cleanup("dest_serial");
421}
422
423int main(int argc, char **argv)
424{
425 char template[] = "/tmp/postcopy-test-XXXXXX";
426 int ret;
427
428 g_test_init(&argc, &argv, NULL);
429
430 if (!ufd_version_check()) {
431 return 0;
432 }
433
434 tmpfs = mkdtemp(template);
435 if (!tmpfs) {
436 g_test_message("mkdtemp on path (%s): %s\n", template, strerror(errno));
437 }
438 g_assert(tmpfs);
439
440 module_call_init(MODULE_INIT_QOM);
441
442 qtest_add_func("/postcopy", test_migrate);
443
444 ret = g_test_run();
445
446 g_assert_cmpint(ret, ==, 0);
447
448 ret = rmdir(tmpfs);
449 if (ret != 0) {
450 g_test_message("unable to rmdir: path (%s): %s\n",
451 tmpfs, strerror(errno));
452 }
453
454 return ret;
455}