1
2
3
4
5
6
7
8
9
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
24
25const unsigned start_address = 1024 * 1024;
26const unsigned end_address = 100 * 1024 * 1024;
27bool 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
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
80
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
128static 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
136static 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
145
146 header->signature = CHRP_NVPART_SYSTEM;
147 memcpy(header->name, "common", 6);
148 chrp_nvram_finish_partition(header, MIN_NVRAM_SIZE);
149
150
151
152
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
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
168
169
170
171static 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
185
186
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
202 break;
203
204 case 'B':
205
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
225
226static QDict *return_or_event(QDict *response)
227{
228 const char *event_string;
229 if (!qdict_haskey(response, "event")) {
230 return response;
231 }
232
233
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
245
246
247
248
249
250static 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
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
268static 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
286static void wait_for_migration_pass(void)
287{
288 uint64_t initial_pass = get_migration_pass();
289 uint64_t pass;
290
291
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
306static void check_guests_ram(void)
307{
308
309
310
311
312
313
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
331
332
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
348static 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
356static 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
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
429
430
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
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
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
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
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
505int 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}
538