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