]>
Commit | Line | Data |
---|---|---|
ea0c6d62 | 1 | /* |
2656bfd9 | 2 | * QTest testcase for migration |
ea0c6d62 | 3 | * |
17ca7746 | 4 | * Copyright (c) 2016-2018 Red Hat, Inc. and/or its affiliates |
ea0c6d62 DDAG |
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" | |
ea0c6d62 | 14 | |
907b5105 | 15 | #include "libqtest.h" |
d0112758 | 16 | #include "qapi/error.h" |
452fcdbc | 17 | #include "qapi/qmp/qdict.h" |
0b8fa32f | 18 | #include "qemu/module.h" |
ea0c6d62 DDAG |
19 | #include "qemu/option.h" |
20 | #include "qemu/range.h" | |
a9c94277 | 21 | #include "qemu/sockets.h" |
8228e353 | 22 | #include "chardev/char.h" |
609d3844 JQ |
23 | #include "qapi/qapi-visit-sockets.h" |
24 | #include "qapi/qobject-input-visitor.h" | |
25 | #include "qapi/qobject-output-visitor.h" | |
58d25e97 | 26 | #include "crypto/tlscredspsk.h" |
8aff6f50 | 27 | #include "qapi/qmp/qlist.h" |
ea0c6d62 | 28 | |
d77799cc | 29 | #include "migration-helpers.h" |
a2ce7dbd | 30 | #include "tests/migration/migration-test.h" |
58d25e97 DB |
31 | #ifdef CONFIG_GNUTLS |
32 | # include "tests/unit/crypto-tls-psk-helpers.h" | |
d47b83b1 DB |
33 | # ifdef CONFIG_TASN1 |
34 | # include "tests/unit/crypto-tls-x509-helpers.h" | |
35 | # endif /* CONFIG_TASN1 */ | |
58d25e97 | 36 | #endif /* CONFIG_GNUTLS */ |
e51e711b | 37 | |
61c32485 PX |
38 | /* For dirty ring test; so far only x86_64 is supported */ |
39 | #if defined(__linux__) && defined(HOST_X86_64) | |
1f546b70 PX |
40 | #include "linux/kvm.h" |
41 | #endif | |
42 | ||
e51e711b WH |
43 | unsigned start_address; |
44 | unsigned end_address; | |
346f3dab | 45 | static bool uffd_feature_thread_id; |
f0649758 SS |
46 | static QTestMigrationState src_state; |
47 | static QTestMigrationState dst_state; | |
ea0c6d62 | 48 | |
e02f56e3 DB |
49 | /* |
50 | * An initial 3 MB offset is used as that corresponds | |
51 | * to ~1 sec of data transfer with our bandwidth setting. | |
52 | */ | |
53 | #define MAGIC_OFFSET_BASE (3 * 1024 * 1024) | |
54 | /* | |
55 | * A further 1k is added to ensure we're not a multiple | |
56 | * of TEST_MEM_PAGE_SIZE, thus avoid clash with writes | |
57 | * from the migration guest workload. | |
58 | */ | |
59 | #define MAGIC_OFFSET_SHUFFLE 1024 | |
60 | #define MAGIC_OFFSET (MAGIC_OFFSET_BASE + MAGIC_OFFSET_SHUFFLE) | |
61 | #define MAGIC_MARKER 0xFEED12345678CAFEULL | |
62 | ||
8aff6f50 HH |
63 | /* |
64 | * Dirtylimit stop working if dirty page rate error | |
65 | * value less than DIRTYLIMIT_TOLERANCE_RANGE | |
66 | */ | |
67 | #define DIRTYLIMIT_TOLERANCE_RANGE 25 /* MB/s */ | |
68 | ||
d864756e FR |
69 | #define ANALYZE_SCRIPT "scripts/analyze-migration.py" |
70 | ||
3dc35470 FR |
71 | #define QEMU_VM_FILE_MAGIC 0x5145564d |
72 | #define FILE_TEST_FILENAME "migfile" | |
73 | #define FILE_TEST_OFFSET 0x1000 | |
5050ad2a FR |
74 | #define QEMU_ENV_SRC "QTEST_QEMU_BINARY_SRC" |
75 | #define QEMU_ENV_DST "QTEST_QEMU_BINARY_DST" | |
3dc35470 | 76 | |
ea0c6d62 | 77 | #if defined(__linux__) |
ea0c6d62 DDAG |
78 | #include <sys/syscall.h> |
79 | #include <sys/vfs.h> | |
80 | #endif | |
81 | ||
82 | #if defined(__linux__) && defined(__NR_userfaultfd) && defined(CONFIG_EVENTFD) | |
83 | #include <sys/eventfd.h> | |
84 | #include <sys/ioctl.h> | |
d5890ea0 | 85 | #include "qemu/userfaultfd.h" |
ea0c6d62 DDAG |
86 | |
87 | static bool ufd_version_check(void) | |
88 | { | |
89 | struct uffdio_api api_struct; | |
90 | uint64_t ioctl_mask; | |
91 | ||
d5890ea0 | 92 | int ufd = uffd_open(O_CLOEXEC); |
ea0c6d62 DDAG |
93 | |
94 | if (ufd == -1) { | |
95 | g_test_message("Skipping test: userfaultfd not available"); | |
96 | return false; | |
97 | } | |
98 | ||
99 | api_struct.api = UFFD_API; | |
100 | api_struct.features = 0; | |
101 | if (ioctl(ufd, UFFDIO_API, &api_struct)) { | |
102 | g_test_message("Skipping test: UFFDIO_API failed"); | |
103 | return false; | |
104 | } | |
346f3dab | 105 | uffd_feature_thread_id = api_struct.features & UFFD_FEATURE_THREAD_ID; |
ea0c6d62 | 106 | |
73b49878 PB |
107 | ioctl_mask = 1ULL << _UFFDIO_REGISTER | |
108 | 1ULL << _UFFDIO_UNREGISTER; | |
ea0c6d62 DDAG |
109 | if ((api_struct.ioctls & ioctl_mask) != ioctl_mask) { |
110 | g_test_message("Skipping test: Missing userfault feature"); | |
111 | return false; | |
112 | } | |
113 | ||
114 | return true; | |
115 | } | |
116 | ||
117 | #else | |
118 | static bool ufd_version_check(void) | |
119 | { | |
120 | g_test_message("Skipping test: Userfault not available (builtdtime)"); | |
121 | return false; | |
122 | } | |
123 | ||
124 | #endif | |
125 | ||
e5553c1b | 126 | static char *tmpfs; |
877cec63 | 127 | static char *bootpath; |
ea0c6d62 | 128 | |
e51e711b WH |
129 | /* The boot file modifies memory area in [start_address, end_address) |
130 | * repeatedly. It outputs a 'B' at a fixed rate while it's still running. | |
ea0c6d62 | 131 | */ |
d54927ef | 132 | #include "tests/migration/i386/a-b-bootblock.h" |
c02b3781 | 133 | #include "tests/migration/aarch64/a-b-kernel.h" |
5571dc82 TH |
134 | #include "tests/migration/s390x/a-b-bios.h" |
135 | ||
5014478e | 136 | static void bootfile_create(char *dir, bool suspend_me) |
5571dc82 | 137 | { |
0c690d3e JQ |
138 | const char *arch = qtest_get_arch(); |
139 | unsigned char *content; | |
140 | size_t len; | |
141 | ||
142 | bootpath = g_strdup_printf("%s/bootsect", dir); | |
143 | if (strcmp(arch, "i386") == 0 || strcmp(arch, "x86_64") == 0) { | |
144 | /* the assembled x86 boot sector should be exactly one sector large */ | |
145 | g_assert(sizeof(x86_bootsect) == 512); | |
5014478e | 146 | x86_bootsect[SYM_suspend_me - SYM_start] = suspend_me; |
0c690d3e JQ |
147 | content = x86_bootsect; |
148 | len = sizeof(x86_bootsect); | |
149 | } else if (g_str_equal(arch, "s390x")) { | |
150 | content = s390x_elf; | |
151 | len = sizeof(s390x_elf); | |
152 | } else if (strcmp(arch, "ppc64") == 0) { | |
153 | /* | |
154 | * sane architectures can be programmed at the boot prompt | |
155 | */ | |
156 | return; | |
157 | } else if (strcmp(arch, "aarch64") == 0) { | |
158 | content = aarch64_kernel; | |
159 | len = sizeof(aarch64_kernel); | |
160 | g_assert(sizeof(aarch64_kernel) <= ARM_TEST_MAX_KERNEL_SIZE); | |
161 | } else { | |
162 | g_assert_not_reached(); | |
163 | } | |
164 | ||
5571dc82 | 165 | FILE *bootfile = fopen(bootpath, "wb"); |
5571dc82 | 166 | |
2785f196 | 167 | g_assert_cmpint(fwrite(content, len, 1, bootfile), ==, 1); |
5571dc82 TH |
168 | fclose(bootfile); |
169 | } | |
170 | ||
0c690d3e JQ |
171 | static void bootfile_delete(void) |
172 | { | |
173 | unlink(bootpath); | |
174 | g_free(bootpath); | |
175 | bootpath = NULL; | |
176 | } | |
177 | ||
ea0c6d62 DDAG |
178 | /* |
179 | * Wait for some output in the serial output file, | |
180 | * we get an 'A' followed by an endless string of 'B's | |
b1fdd21e | 181 | * but on the destination we won't have the A (unless we enabled suspend/resume) |
ea0c6d62 DDAG |
182 | */ |
183 | static void wait_for_serial(const char *side) | |
184 | { | |
ff7b9b56 | 185 | g_autofree char *serialpath = g_strdup_printf("%s/%s", tmpfs, side); |
ea0c6d62 | 186 | FILE *serialfile = fopen(serialpath, "r"); |
aaf89c8a | 187 | const char *arch = qtest_get_arch(); |
188 | int started = (strcmp(side, "src_serial") == 0 && | |
189 | strcmp(arch, "ppc64") == 0) ? 0 : 1; | |
ea0c6d62 DDAG |
190 | |
191 | do { | |
192 | int readvalue = fgetc(serialfile); | |
193 | ||
aaf89c8a | 194 | if (!started) { |
195 | /* SLOF prints its banner before starting test, | |
196 | * to ignore it, mark the start of the test with '_', | |
197 | * ignore all characters until this marker | |
198 | */ | |
199 | switch (readvalue) { | |
200 | case '_': | |
201 | started = 1; | |
202 | break; | |
203 | case EOF: | |
204 | fseek(serialfile, 0, SEEK_SET); | |
205 | usleep(1000); | |
206 | break; | |
207 | } | |
208 | continue; | |
209 | } | |
ea0c6d62 DDAG |
210 | switch (readvalue) { |
211 | case 'A': | |
212 | /* Fine */ | |
213 | break; | |
214 | ||
215 | case 'B': | |
216 | /* It's alive! */ | |
217 | fclose(serialfile); | |
ea0c6d62 DDAG |
218 | return; |
219 | ||
220 | case EOF: | |
aaf89c8a | 221 | started = (strcmp(side, "src_serial") == 0 && |
222 | strcmp(arch, "ppc64") == 0) ? 0 : 1; | |
ea0c6d62 DDAG |
223 | fseek(serialfile, 0, SEEK_SET); |
224 | usleep(1000); | |
225 | break; | |
226 | ||
227 | default: | |
228 | fprintf(stderr, "Unexpected %d on %s serial\n", readvalue, side); | |
229 | g_assert_not_reached(); | |
230 | } | |
231 | } while (true); | |
232 | } | |
233 | ||
f0649758 SS |
234 | static void wait_for_stop(QTestState *who, QTestMigrationState *state) |
235 | { | |
236 | if (!state->stop_seen) { | |
237 | qtest_qmp_eventwait(who, "STOP"); | |
238 | } | |
239 | } | |
240 | ||
241 | static void wait_for_resume(QTestState *who, QTestMigrationState *state) | |
242 | { | |
243 | if (!state->resume_seen) { | |
244 | qtest_qmp_eventwait(who, "RESUME"); | |
245 | } | |
246 | } | |
247 | ||
b1fdd21e SS |
248 | static void wait_for_suspend(QTestState *who, QTestMigrationState *state) |
249 | { | |
250 | if (state->suspend_me && !state->suspend_seen) { | |
251 | qtest_qmp_eventwait(who, "SUSPEND"); | |
252 | } | |
253 | } | |
254 | ||
ea0c6d62 DDAG |
255 | /* |
256 | * It's tricky to use qemu's migration event capability with qtest, | |
257 | * events suddenly appearing confuse the qmp()/hmp() responses. | |
ea0c6d62 DDAG |
258 | */ |
259 | ||
660a9b68 | 260 | static int64_t read_ram_property_int(QTestState *who, const char *property) |
ea0c6d62 | 261 | { |
2f7074c6 | 262 | QDict *rsp_return, *rsp_ram; |
660a9b68 | 263 | int64_t result; |
ea0c6d62 | 264 | |
fd3540ad | 265 | rsp_return = migrate_query_not_failed(who); |
ea0c6d62 DDAG |
266 | if (!qdict_haskey(rsp_return, "ram")) { |
267 | /* Still in setup */ | |
268 | result = 0; | |
269 | } else { | |
270 | rsp_ram = qdict_get_qdict(rsp_return, "ram"); | |
660a9b68 | 271 | result = qdict_get_try_int(rsp_ram, property, 0); |
ea0c6d62 | 272 | } |
2f7074c6 | 273 | qobject_unref(rsp_return); |
ea0c6d62 DDAG |
274 | return result; |
275 | } | |
276 | ||
8c51642b YK |
277 | static int64_t read_migrate_property_int(QTestState *who, const char *property) |
278 | { | |
279 | QDict *rsp_return; | |
280 | int64_t result; | |
281 | ||
fd3540ad | 282 | rsp_return = migrate_query_not_failed(who); |
8c51642b YK |
283 | result = qdict_get_try_int(rsp_return, property, 0); |
284 | qobject_unref(rsp_return); | |
285 | return result; | |
286 | } | |
287 | ||
660a9b68 YK |
288 | static uint64_t get_migration_pass(QTestState *who) |
289 | { | |
290 | return read_ram_property_int(who, "dirty-sync-count"); | |
291 | } | |
292 | ||
346f3dab AP |
293 | static void read_blocktime(QTestState *who) |
294 | { | |
2f7074c6 | 295 | QDict *rsp_return; |
346f3dab | 296 | |
fd3540ad | 297 | rsp_return = migrate_query_not_failed(who); |
346f3dab | 298 | g_assert(qdict_haskey(rsp_return, "postcopy-blocktime")); |
2f7074c6 | 299 | qobject_unref(rsp_return); |
346f3dab AP |
300 | } |
301 | ||
f0649758 SS |
302 | /* |
303 | * Wait for two changes in the migration pass count, but bail if we stop. | |
304 | */ | |
863e27a8 | 305 | static void wait_for_migration_pass(QTestState *who) |
ea0c6d62 | 306 | { |
f0649758 | 307 | uint64_t pass, prev_pass = 0, changes = 0; |
ea0c6d62 | 308 | |
b1fdd21e | 309 | while (changes < 2 && !src_state.stop_seen && !src_state.suspend_seen) { |
6a7724e9 | 310 | usleep(1000); |
863e27a8 | 311 | pass = get_migration_pass(who); |
f0649758 SS |
312 | changes += (pass != prev_pass); |
313 | prev_pass = pass; | |
314 | } | |
ea0c6d62 DDAG |
315 | } |
316 | ||
7195a871 | 317 | static void check_guests_ram(QTestState *who) |
ea0c6d62 DDAG |
318 | { |
319 | /* Our ASM test will have been incrementing one byte from each page from | |
e51e711b WH |
320 | * start_address to < end_address in order. This gives us a constraint |
321 | * that any page's byte should be equal or less than the previous pages | |
322 | * byte (mod 256); and they should all be equal except for one transition | |
323 | * at the point where we meet the incrementer. (We're running this with | |
324 | * the guest stopped). | |
ea0c6d62 DDAG |
325 | */ |
326 | unsigned address; | |
327 | uint8_t first_byte; | |
328 | uint8_t last_byte; | |
329 | bool hit_edge = false; | |
601b5575 | 330 | int bad = 0; |
ea0c6d62 | 331 | |
7195a871 | 332 | qtest_memread(who, start_address, &first_byte, 1); |
ea0c6d62 DDAG |
333 | last_byte = first_byte; |
334 | ||
e51e711b WH |
335 | for (address = start_address + TEST_MEM_PAGE_SIZE; address < end_address; |
336 | address += TEST_MEM_PAGE_SIZE) | |
ea0c6d62 DDAG |
337 | { |
338 | uint8_t b; | |
7195a871 | 339 | qtest_memread(who, address, &b, 1); |
ea0c6d62 DDAG |
340 | if (b != last_byte) { |
341 | if (((b + 1) % 256) == last_byte && !hit_edge) { | |
342 | /* This is OK, the guest stopped at the point of | |
343 | * incrementing the previous page but didn't get | |
344 | * to us yet. | |
345 | */ | |
346 | hit_edge = true; | |
829db8b4 | 347 | last_byte = b; |
ea0c6d62 | 348 | } else { |
601b5575 AB |
349 | bad++; |
350 | if (bad <= 10) { | |
351 | fprintf(stderr, "Memory content inconsistency at %x" | |
352 | " first_byte = %x last_byte = %x current = %x" | |
353 | " hit_edge = %x\n", | |
354 | address, first_byte, last_byte, b, hit_edge); | |
355 | } | |
ea0c6d62 DDAG |
356 | } |
357 | } | |
ea0c6d62 | 358 | } |
601b5575 AB |
359 | if (bad >= 10) { |
360 | fprintf(stderr, "and in another %d pages", bad - 10); | |
361 | } | |
362 | g_assert(bad == 0); | |
ea0c6d62 DDAG |
363 | } |
364 | ||
365 | static void cleanup(const char *filename) | |
366 | { | |
ff7b9b56 | 367 | g_autofree char *path = g_strdup_printf("%s/%s", tmpfs, filename); |
ea0c6d62 DDAG |
368 | |
369 | unlink(path); | |
370 | } | |
371 | ||
609d3844 JQ |
372 | static char *SocketAddress_to_str(SocketAddress *addr) |
373 | { | |
374 | switch (addr->type) { | |
375 | case SOCKET_ADDRESS_TYPE_INET: | |
376 | return g_strdup_printf("tcp:%s:%s", | |
377 | addr->u.inet.host, | |
378 | addr->u.inet.port); | |
379 | case SOCKET_ADDRESS_TYPE_UNIX: | |
380 | return g_strdup_printf("unix:%s", | |
381 | addr->u.q_unix.path); | |
382 | case SOCKET_ADDRESS_TYPE_FD: | |
383 | return g_strdup_printf("fd:%s", addr->u.fd.str); | |
384 | case SOCKET_ADDRESS_TYPE_VSOCK: | |
385 | return g_strdup_printf("tcp:%s:%s", | |
386 | addr->u.vsock.cid, | |
387 | addr->u.vsock.port); | |
388 | default: | |
389 | return g_strdup("unknown address type"); | |
390 | } | |
391 | } | |
392 | ||
393 | static char *migrate_get_socket_address(QTestState *who, const char *parameter) | |
394 | { | |
395 | QDict *rsp; | |
396 | char *result; | |
609d3844 JQ |
397 | SocketAddressList *addrs; |
398 | Visitor *iv = NULL; | |
399 | QObject *object; | |
400 | ||
401 | rsp = migrate_query(who); | |
402 | object = qdict_get(rsp, parameter); | |
403 | ||
404 | iv = qobject_input_visitor_new(object); | |
d0112758 | 405 | visit_type_SocketAddressList(iv, NULL, &addrs, &error_abort); |
1e25879e | 406 | visit_free(iv); |
609d3844 JQ |
407 | |
408 | /* we are only using a single address */ | |
1e25879e | 409 | result = SocketAddress_to_str(addrs->value); |
609d3844 JQ |
410 | |
411 | qapi_free_SocketAddressList(addrs); | |
412 | qobject_unref(rsp); | |
413 | return result; | |
414 | } | |
415 | ||
8f7798f1 JQ |
416 | static long long migrate_get_parameter_int(QTestState *who, |
417 | const char *parameter) | |
609d3844 JQ |
418 | { |
419 | QDict *rsp; | |
420 | long long result; | |
421 | ||
aca04069 DB |
422 | rsp = qtest_qmp_assert_success_ref( |
423 | who, "{ 'execute': 'query-migrate-parameters' }"); | |
609d3844 JQ |
424 | result = qdict_get_int(rsp, parameter); |
425 | qobject_unref(rsp); | |
426 | return result; | |
427 | } | |
428 | ||
8f7798f1 JQ |
429 | static void migrate_check_parameter_int(QTestState *who, const char *parameter, |
430 | long long value) | |
56b4a42a | 431 | { |
609d3844 | 432 | long long result; |
56b4a42a | 433 | |
8f7798f1 | 434 | result = migrate_get_parameter_int(who, parameter); |
609d3844 | 435 | g_assert_cmpint(result, ==, value); |
56b4a42a JQ |
436 | } |
437 | ||
8f7798f1 JQ |
438 | static void migrate_set_parameter_int(QTestState *who, const char *parameter, |
439 | long long value) | |
d62fbe60 | 440 | { |
11936f0e DB |
441 | qtest_qmp_assert_success(who, |
442 | "{ 'execute': 'migrate-set-parameters'," | |
443 | "'arguments': { %s: %lld } }", | |
444 | parameter, value); | |
8f7798f1 | 445 | migrate_check_parameter_int(who, parameter, value); |
d62fbe60 JQ |
446 | } |
447 | ||
6a22c544 JQ |
448 | static char *migrate_get_parameter_str(QTestState *who, |
449 | const char *parameter) | |
450 | { | |
451 | QDict *rsp; | |
452 | char *result; | |
453 | ||
aca04069 DB |
454 | rsp = qtest_qmp_assert_success_ref( |
455 | who, "{ 'execute': 'query-migrate-parameters' }"); | |
6a22c544 JQ |
456 | result = g_strdup(qdict_get_str(rsp, parameter)); |
457 | qobject_unref(rsp); | |
458 | return result; | |
459 | } | |
460 | ||
461 | static void migrate_check_parameter_str(QTestState *who, const char *parameter, | |
462 | const char *value) | |
463 | { | |
ff7b9b56 | 464 | g_autofree char *result = migrate_get_parameter_str(who, parameter); |
6a22c544 | 465 | g_assert_cmpstr(result, ==, value); |
6a22c544 JQ |
466 | } |
467 | ||
6a22c544 JQ |
468 | static void migrate_set_parameter_str(QTestState *who, const char *parameter, |
469 | const char *value) | |
470 | { | |
11936f0e DB |
471 | qtest_qmp_assert_success(who, |
472 | "{ 'execute': 'migrate-set-parameters'," | |
473 | "'arguments': { %s: %s } }", | |
474 | parameter, value); | |
6a22c544 JQ |
475 | migrate_check_parameter_str(who, parameter, value); |
476 | } | |
477 | ||
1536d1da LS |
478 | static long long migrate_get_parameter_bool(QTestState *who, |
479 | const char *parameter) | |
480 | { | |
481 | QDict *rsp; | |
482 | int result; | |
483 | ||
aca04069 DB |
484 | rsp = qtest_qmp_assert_success_ref( |
485 | who, "{ 'execute': 'query-migrate-parameters' }"); | |
1536d1da LS |
486 | result = qdict_get_bool(rsp, parameter); |
487 | qobject_unref(rsp); | |
488 | return !!result; | |
489 | } | |
490 | ||
491 | static void migrate_check_parameter_bool(QTestState *who, const char *parameter, | |
492 | int value) | |
493 | { | |
494 | int result; | |
495 | ||
496 | result = migrate_get_parameter_bool(who, parameter); | |
497 | g_assert_cmpint(result, ==, value); | |
498 | } | |
499 | ||
500 | static void migrate_set_parameter_bool(QTestState *who, const char *parameter, | |
501 | int value) | |
502 | { | |
11936f0e DB |
503 | qtest_qmp_assert_success(who, |
504 | "{ 'execute': 'migrate-set-parameters'," | |
505 | "'arguments': { %s: %i } }", | |
506 | parameter, value); | |
1536d1da LS |
507 | migrate_check_parameter_bool(who, parameter, value); |
508 | } | |
509 | ||
886dfe9d DB |
510 | static void migrate_ensure_non_converge(QTestState *who) |
511 | { | |
1bfc8dde DDAG |
512 | /* Can't converge with 1ms downtime + 3 mbs bandwidth limit */ |
513 | migrate_set_parameter_int(who, "max-bandwidth", 3 * 1000 * 1000); | |
886dfe9d DB |
514 | migrate_set_parameter_int(who, "downtime-limit", 1); |
515 | } | |
516 | ||
517 | static void migrate_ensure_converge(QTestState *who) | |
518 | { | |
519 | /* Should converge with 30s downtime + 1 gbs bandwidth limit */ | |
520 | migrate_set_parameter_int(who, "max-bandwidth", 1 * 1000 * 1000 * 1000); | |
521 | migrate_set_parameter_int(who, "downtime-limit", 30 * 1000); | |
522 | } | |
523 | ||
e02f56e3 DB |
524 | /* |
525 | * Our goal is to ensure that we run a single full migration | |
526 | * iteration, and also dirty memory, ensuring that at least | |
527 | * one further iteration is required. | |
528 | * | |
529 | * We can't directly synchronize with the start of a migration | |
530 | * so we have to apply some tricks monitoring memory that is | |
531 | * transferred. | |
532 | * | |
533 | * Initially we set the migration bandwidth to an insanely | |
534 | * low value, with tiny max downtime too. This basically | |
535 | * guarantees migration will never complete. | |
536 | * | |
537 | * This will result in a test that is unacceptably slow though, | |
538 | * so we can't let the entire migration pass run at this speed. | |
539 | * Our intent is to let it run just long enough that we can | |
540 | * prove data prior to the marker has been transferred *AND* | |
541 | * also prove this transferred data is dirty again. | |
542 | * | |
543 | * Before migration starts, we write a 64-bit magic marker | |
544 | * into a fixed location in the src VM RAM. | |
545 | * | |
546 | * Then watch dst memory until the marker appears. This is | |
547 | * proof that start_address -> MAGIC_OFFSET_BASE has been | |
548 | * transferred. | |
549 | * | |
550 | * Finally we go back to the source and read a byte just | |
96420a30 | 551 | * before the marker until we see it flip in value. This |
e02f56e3 DB |
552 | * is proof that start_address -> MAGIC_OFFSET_BASE |
553 | * is now dirty again. | |
554 | * | |
555 | * IOW, we're guaranteed at least a 2nd migration pass | |
556 | * at this point. | |
557 | * | |
558 | * We can now let migration run at full speed to finish | |
559 | * the test | |
560 | */ | |
561 | static void migrate_prepare_for_dirty_mem(QTestState *from) | |
562 | { | |
563 | /* | |
564 | * The guest workflow iterates from start_address to | |
565 | * end_address, writing 1 byte every TEST_MEM_PAGE_SIZE | |
566 | * bytes. | |
567 | * | |
568 | * IOW, if we write to mem at a point which is NOT | |
569 | * a multiple of TEST_MEM_PAGE_SIZE, our write won't | |
570 | * conflict with the migration workflow. | |
571 | * | |
572 | * We put in a marker here, that we'll use to determine | |
573 | * when the data has been transferred to the dst. | |
574 | */ | |
575 | qtest_writeq(from, start_address + MAGIC_OFFSET, MAGIC_MARKER); | |
576 | } | |
577 | ||
578 | static void migrate_wait_for_dirty_mem(QTestState *from, | |
579 | QTestState *to) | |
580 | { | |
581 | uint64_t watch_address = start_address + MAGIC_OFFSET_BASE; | |
582 | uint64_t marker_address = start_address + MAGIC_OFFSET; | |
583 | uint8_t watch_byte; | |
584 | ||
585 | /* | |
586 | * Wait for the MAGIC_MARKER to get transferred, as an | |
587 | * indicator that a migration pass has made some known | |
588 | * amount of progress. | |
589 | */ | |
590 | do { | |
591 | usleep(1000 * 10); | |
592 | } while (qtest_readq(to, marker_address) != MAGIC_MARKER); | |
593 | ||
b1fdd21e SS |
594 | |
595 | /* If suspended, src only iterates once, and watch_byte may never change */ | |
596 | if (src_state.suspend_me) { | |
597 | return; | |
598 | } | |
599 | ||
e02f56e3 DB |
600 | /* |
601 | * Now ensure that already transferred bytes are | |
602 | * dirty again from the guest workload. Note the | |
603 | * guest byte value will wrap around and by chance | |
604 | * match the original watch_byte. This is harmless | |
605 | * as we'll eventually see a different value if we | |
606 | * keep watching | |
607 | */ | |
608 | watch_byte = qtest_readb(from, watch_address); | |
609 | do { | |
610 | usleep(1000 * 10); | |
611 | } while (qtest_readb(from, watch_address) == watch_byte); | |
612 | } | |
613 | ||
614 | ||
d5f49640 PX |
615 | static void migrate_pause(QTestState *who) |
616 | { | |
aca04069 | 617 | qtest_qmp_assert_success(who, "{ 'execute': 'migrate-pause' }"); |
d5f49640 PX |
618 | } |
619 | ||
8c51642b YK |
620 | static void migrate_continue(QTestState *who, const char *state) |
621 | { | |
aca04069 DB |
622 | qtest_qmp_assert_success(who, |
623 | "{ 'execute': 'migrate-continue'," | |
624 | " 'arguments': { 'state': %s } }", | |
625 | state); | |
8c51642b YK |
626 | } |
627 | ||
d5f49640 PX |
628 | static void migrate_recover(QTestState *who, const char *uri) |
629 | { | |
aca04069 DB |
630 | qtest_qmp_assert_success(who, |
631 | "{ 'execute': 'migrate-recover', " | |
632 | " 'id': 'recover-cmd', " | |
633 | " 'arguments': { 'uri': %s } }", | |
634 | uri); | |
d5f49640 PX |
635 | } |
636 | ||
d795f474 JQ |
637 | static void migrate_cancel(QTestState *who) |
638 | { | |
aca04069 | 639 | qtest_qmp_assert_success(who, "{ 'execute': 'migrate_cancel' }"); |
d795f474 JQ |
640 | } |
641 | ||
d131662a | 642 | static void migrate_postcopy_start(QTestState *from, QTestState *to) |
eb665d7d | 643 | { |
aca04069 | 644 | qtest_qmp_assert_success(from, "{ 'execute': 'migrate-start-postcopy' }"); |
d131662a | 645 | |
f0649758 | 646 | wait_for_stop(from, &src_state); |
d131662a | 647 | qtest_qmp_eventwait(to, "RESUME"); |
eb665d7d JQ |
648 | } |
649 | ||
5d3b575d | 650 | typedef struct { |
a4729501 PX |
651 | /* |
652 | * QTEST_LOG=1 may override this. When QTEST_LOG=1, we always dump errors | |
653 | * unconditionally, because it means the user would like to be verbose. | |
654 | */ | |
5d3b575d JQ |
655 | bool hide_stderr; |
656 | bool use_shmem; | |
d795f474 JQ |
657 | /* only launch the target process */ |
658 | bool only_target; | |
1f546b70 PX |
659 | /* Use dirty ring if true; dirty logging otherwise */ |
660 | bool use_dirty_ring; | |
19da6edf DB |
661 | const char *opts_source; |
662 | const char *opts_target; | |
5014478e SS |
663 | /* suspend the src before migrating to dest. */ |
664 | bool suspend_me; | |
5d3b575d JQ |
665 | } MigrateStart; |
666 | ||
312e9dd0 PX |
667 | /* |
668 | * A hook that runs after the src and dst QEMUs have been | |
669 | * created, but before the migration is started. This can | |
670 | * be used to set migration parameters and capabilities. | |
671 | * | |
672 | * Returns: NULL, or a pointer to opaque state to be | |
673 | * later passed to the TestMigrateFinishHook | |
674 | */ | |
675 | typedef void * (*TestMigrateStartHook)(QTestState *from, | |
676 | QTestState *to); | |
677 | ||
678 | /* | |
679 | * A hook that runs after the migration has finished, | |
680 | * regardless of whether it succeeded or failed, but | |
681 | * before QEMU has terminated (unless it self-terminated | |
682 | * due to migration error) | |
683 | * | |
684 | * @opaque is a pointer to state previously returned | |
685 | * by the TestMigrateStartHook if any, or NULL. | |
686 | */ | |
687 | typedef void (*TestMigrateFinishHook)(QTestState *from, | |
688 | QTestState *to, | |
689 | void *opaque); | |
690 | ||
691 | typedef struct { | |
692 | /* Optional: fine tune start parameters */ | |
693 | MigrateStart start; | |
694 | ||
695 | /* Required: the URI for the dst QEMU to listen on */ | |
696 | const char *listen_uri; | |
697 | ||
698 | /* | |
699 | * Optional: the URI for the src QEMU to connect to | |
700 | * If NULL, then it will query the dst QEMU for its actual | |
701 | * listening address and use that as the connect address. | |
702 | * This allows for dynamically picking a free TCP port. | |
703 | */ | |
704 | const char *connect_uri; | |
705 | ||
706 | /* Optional: callback to run at start to set migration parameters */ | |
707 | TestMigrateStartHook start_hook; | |
708 | /* Optional: callback to run at finish to cleanup */ | |
709 | TestMigrateFinishHook finish_hook; | |
710 | ||
711 | /* | |
712 | * Optional: normally we expect the migration process to complete. | |
713 | * | |
714 | * There can be a variety of reasons and stages in which failure | |
715 | * can happen during tests. | |
716 | * | |
717 | * If a failure is expected to happen at time of establishing | |
718 | * the connection, then MIG_TEST_FAIL will indicate that the dst | |
719 | * QEMU is expected to stay running and accept future migration | |
720 | * connections. | |
721 | * | |
722 | * If a failure is expected to happen while processing the | |
723 | * migration stream, then MIG_TEST_FAIL_DEST_QUIT_ERR will indicate | |
724 | * that the dst QEMU is expected to quit with non-zero exit status | |
725 | */ | |
726 | enum { | |
727 | /* This test should succeed, the default */ | |
728 | MIG_TEST_SUCCEED = 0, | |
729 | /* This test should fail, dest qemu should keep alive */ | |
730 | MIG_TEST_FAIL, | |
731 | /* This test should fail, dest qemu should fail with abnormal status */ | |
732 | MIG_TEST_FAIL_DEST_QUIT_ERR, | |
5274274c FR |
733 | /* The QMP command for this migration should fail with an error */ |
734 | MIG_TEST_QMP_ERROR, | |
312e9dd0 PX |
735 | } result; |
736 | ||
e02f56e3 DB |
737 | /* |
738 | * Optional: set number of migration passes to wait for, if live==true. | |
739 | * If zero, then merely wait for a few MB of dirty data | |
740 | */ | |
312e9dd0 | 741 | unsigned int iterations; |
d1a27b16 | 742 | |
b861383c PX |
743 | /* |
744 | * Optional: whether the guest CPUs should be running during a precopy | |
745 | * migration test. We used to always run with live but it took much | |
746 | * longer so we reduced live tests to only the ones that have solid | |
747 | * reason to be tested live-only. For each of the new test cases for | |
748 | * precopy please provide justifications to use live explicitly (please | |
749 | * refer to existing ones with live=true), or use live=off by default. | |
750 | */ | |
3c4fb177 DB |
751 | bool live; |
752 | ||
d1a27b16 PX |
753 | /* Postcopy specific fields */ |
754 | void *postcopy_data; | |
8f6fe915 | 755 | bool postcopy_preempt; |
7bca2bb7 | 756 | bool postcopy_recovery_test_fail; |
312e9dd0 PX |
757 | } MigrateCommon; |
758 | ||
5fd4a9c9 | 759 | static int test_migrate_start(QTestState **from, QTestState **to, |
19da6edf | 760 | const char *uri, MigrateStart *args) |
ea0c6d62 | 761 | { |
ff7b9b56 PM |
762 | g_autofree gchar *arch_source = NULL; |
763 | g_autofree gchar *arch_target = NULL; | |
832c732c JQ |
764 | /* options for source and target */ |
765 | g_autofree gchar *arch_opts = NULL; | |
ff7b9b56 PM |
766 | g_autofree gchar *cmd_source = NULL; |
767 | g_autofree gchar *cmd_target = NULL; | |
1b023718 | 768 | const gchar *ignore_stderr; |
ff7b9b56 PM |
769 | g_autofree char *shmem_opts = NULL; |
770 | g_autofree char *shmem_path = NULL; | |
71d36124 | 771 | const char *kvm_opts = NULL; |
aaf89c8a | 772 | const char *arch = qtest_get_arch(); |
7b6d44cb | 773 | const char *memory_size; |
c9961391 | 774 | const char *machine_alias, *machine_opts = ""; |
5050ad2a | 775 | g_autofree char *machine = NULL; |
ea0c6d62 | 776 | |
5d3b575d | 777 | if (args->use_shmem) { |
660a9b68 YK |
778 | if (!g_file_test("/dev/shm", G_FILE_TEST_IS_DIR)) { |
779 | g_test_skip("/dev/shm is not supported"); | |
19da6edf | 780 | return -1; |
660a9b68 | 781 | } |
660a9b68 | 782 | } |
ea0c6d62 | 783 | |
f0649758 SS |
784 | dst_state = (QTestMigrationState) { }; |
785 | src_state = (QTestMigrationState) { }; | |
5014478e | 786 | bootfile_create(tmpfs, args->suspend_me); |
b1fdd21e | 787 | src_state.suspend_me = args->suspend_me; |
5014478e | 788 | |
aaf89c8a | 789 | if (strcmp(arch, "i386") == 0 || strcmp(arch, "x86_64") == 0) { |
7b6d44cb | 790 | memory_size = "150M"; |
fa35b0cb FR |
791 | |
792 | if (g_str_equal(arch, "i386")) { | |
793 | machine_alias = "pc"; | |
794 | } else { | |
795 | machine_alias = "q35"; | |
796 | } | |
3cb9c655 FR |
797 | arch_opts = g_strdup_printf( |
798 | "-drive if=none,id=d0,file=%s,format=raw " | |
799 | "-device ide-hd,drive=d0,secs=1,cyls=1,heads=1", bootpath); | |
e51e711b WH |
800 | start_address = X86_TEST_MEM_START; |
801 | end_address = X86_TEST_MEM_END; | |
5571dc82 | 802 | } else if (g_str_equal(arch, "s390x")) { |
7b6d44cb | 803 | memory_size = "128M"; |
c9961391 | 804 | machine_alias = "s390-ccw-virtio"; |
832c732c | 805 | arch_opts = g_strdup_printf("-bios %s", bootpath); |
5571dc82 TH |
806 | start_address = S390_TEST_MEM_START; |
807 | end_address = S390_TEST_MEM_END; | |
aaf89c8a | 808 | } else if (strcmp(arch, "ppc64") == 0) { |
7b6d44cb | 809 | memory_size = "256M"; |
16c5c692 LV |
810 | start_address = PPC_TEST_MEM_START; |
811 | end_address = PPC_TEST_MEM_END; | |
832c732c | 812 | arch_source = g_strdup_printf("-prom-env 'use-nvramrc?=true' -prom-env " |
68d95609 JQ |
813 | "'nvramrc=hex .\" _\" begin %x %x " |
814 | "do i c@ 1 + i c! 1000 +loop .\" B\" 0 " | |
815 | "until'", end_address, start_address); | |
c9961391 FR |
816 | machine_alias = "pseries"; |
817 | machine_opts = "vsmt=8"; | |
818 | arch_opts = g_strdup("-nodefaults"); | |
c02b3781 | 819 | } else if (strcmp(arch, "aarch64") == 0) { |
7b6d44cb | 820 | memory_size = "150M"; |
c9961391 FR |
821 | machine_alias = "virt"; |
822 | machine_opts = "gic-version=max"; | |
823 | arch_opts = g_strdup_printf("-cpu max -kernel %s", bootpath); | |
c02b3781 WH |
824 | start_address = ARM_TEST_MEM_START; |
825 | end_address = ARM_TEST_MEM_END; | |
aaf89c8a | 826 | } else { |
827 | g_assert_not_reached(); | |
828 | } | |
829 | ||
a4729501 | 830 | if (!getenv("QTEST_LOG") && args->hide_stderr) { |
4dc8be38 | 831 | #ifndef _WIN32 |
1b023718 | 832 | ignore_stderr = "2>/dev/null"; |
4dc8be38 BM |
833 | #else |
834 | /* | |
835 | * On Windows the QEMU executable is created via CreateProcess() and | |
836 | * IO redirection does not work, so don't bother adding IO redirection | |
837 | * to the command line. | |
838 | */ | |
839 | ignore_stderr = ""; | |
840 | #endif | |
1b023718 JQ |
841 | } else { |
842 | ignore_stderr = ""; | |
f96d6651 DDAG |
843 | } |
844 | ||
5d3b575d | 845 | if (args->use_shmem) { |
3ed375e7 JQ |
846 | shmem_path = g_strdup_printf("/dev/shm/qemu-%d", getpid()); |
847 | shmem_opts = g_strdup_printf( | |
848 | "-object memory-backend-file,id=mem0,size=%s" | |
849 | ",mem-path=%s,share=on -numa node,memdev=mem0", | |
850 | memory_size, shmem_path); | |
3ed375e7 JQ |
851 | } |
852 | ||
71d36124 JQ |
853 | if (args->use_dirty_ring) { |
854 | kvm_opts = ",dirty-ring-size=4096"; | |
855 | } | |
856 | ||
6c6d2330 FR |
857 | machine = resolve_machine_version(machine_alias, QEMU_ENV_SRC, |
858 | QEMU_ENV_DST); | |
859 | ||
5050ad2a FR |
860 | g_test_message("Using machine type: %s", machine); |
861 | ||
bc28a611 | 862 | cmd_source = g_strdup_printf("-accel kvm%s -accel tcg " |
c9961391 | 863 | "-machine %s,%s " |
d6b43267 | 864 | "-name source,debug-threads=on " |
7b6d44cb | 865 | "-m %s " |
cd496731 | 866 | "-serial file:%s/src_serial " |
832c732c | 867 | "%s %s %s %s %s", |
71d36124 | 868 | kvm_opts ? kvm_opts : "", |
5050ad2a | 869 | machine, machine_opts, |
cd496731 | 870 | memory_size, tmpfs, |
832c732c JQ |
871 | arch_opts ? arch_opts : "", |
872 | arch_source ? arch_source : "", | |
0368ace8 | 873 | shmem_opts ? shmem_opts : "", |
19da6edf | 874 | args->opts_source ? args->opts_source : "", |
68d95609 | 875 | ignore_stderr); |
d795f474 | 876 | if (!args->only_target) { |
5050ad2a | 877 | *from = qtest_init_with_env(QEMU_ENV_SRC, cmd_source); |
cdf5ab55 | 878 | qtest_qmp_set_event_callback(*from, |
f0649758 SS |
879 | migrate_watch_for_events, |
880 | &src_state); | |
d795f474 | 881 | } |
aaf89c8a | 882 | |
bc28a611 | 883 | cmd_target = g_strdup_printf("-accel kvm%s -accel tcg " |
c9961391 | 884 | "-machine %s,%s " |
d6b43267 | 885 | "-name target,debug-threads=on " |
7b6d44cb | 886 | "-m %s " |
c5f40ff9 | 887 | "-serial file:%s/dest_serial " |
cd496731 | 888 | "-incoming %s " |
832c732c | 889 | "%s %s %s %s %s", |
71d36124 | 890 | kvm_opts ? kvm_opts : "", |
5050ad2a | 891 | machine, machine_opts, |
cd496731 | 892 | memory_size, tmpfs, uri, |
832c732c JQ |
893 | arch_opts ? arch_opts : "", |
894 | arch_target ? arch_target : "", | |
0368ace8 | 895 | shmem_opts ? shmem_opts : "", |
19da6edf DB |
896 | args->opts_target ? args->opts_target : "", |
897 | ignore_stderr); | |
5050ad2a | 898 | *to = qtest_init_with_env(QEMU_ENV_DST, cmd_target); |
266ea334 | 899 | qtest_qmp_set_event_callback(*to, |
f0649758 SS |
900 | migrate_watch_for_events, |
901 | &dst_state); | |
660a9b68 YK |
902 | |
903 | /* | |
904 | * Remove shmem file immediately to avoid memory leak in test failed case. | |
96420a30 | 905 | * It's valid because QEMU has already opened this file |
660a9b68 | 906 | */ |
5d3b575d | 907 | if (args->use_shmem) { |
660a9b68 | 908 | unlink(shmem_path); |
660a9b68 YK |
909 | } |
910 | ||
19da6edf | 911 | return 0; |
7195a871 JQ |
912 | } |
913 | ||
2c9bb297 | 914 | static void test_migrate_end(QTestState *from, QTestState *to, bool test_dest) |
7195a871 JQ |
915 | { |
916 | unsigned char dest_byte_a, dest_byte_b, dest_byte_c, dest_byte_d; | |
917 | ||
918 | qtest_quit(from); | |
919 | ||
2c9bb297 DDAG |
920 | if (test_dest) { |
921 | qtest_memread(to, start_address, &dest_byte_a, 1); | |
7195a871 | 922 | |
2c9bb297 DDAG |
923 | /* Destination still running, wait for a byte to change */ |
924 | do { | |
925 | qtest_memread(to, start_address, &dest_byte_b, 1); | |
926 | usleep(1000 * 10); | |
927 | } while (dest_byte_a == dest_byte_b); | |
928 | ||
855436db | 929 | qtest_qmp_assert_success(to, "{ 'execute' : 'stop'}"); |
7195a871 | 930 | |
2c9bb297 DDAG |
931 | /* With it stopped, check nothing changes */ |
932 | qtest_memread(to, start_address, &dest_byte_c, 1); | |
933 | usleep(1000 * 200); | |
934 | qtest_memread(to, start_address, &dest_byte_d, 1); | |
935 | g_assert_cmpint(dest_byte_c, ==, dest_byte_d); | |
7195a871 | 936 | |
2c9bb297 DDAG |
937 | check_guests_ram(to); |
938 | } | |
7195a871 JQ |
939 | |
940 | qtest_quit(to); | |
941 | ||
7195a871 JQ |
942 | cleanup("migsocket"); |
943 | cleanup("src_serial"); | |
944 | cleanup("dest_serial"); | |
3dc35470 | 945 | cleanup(FILE_TEST_FILENAME); |
7195a871 JQ |
946 | } |
947 | ||
58d25e97 DB |
948 | #ifdef CONFIG_GNUTLS |
949 | struct TestMigrateTLSPSKData { | |
950 | char *workdir; | |
951 | char *workdiralt; | |
952 | char *pskfile; | |
953 | char *pskfilealt; | |
954 | }; | |
955 | ||
956 | static void * | |
957 | test_migrate_tls_psk_start_common(QTestState *from, | |
958 | QTestState *to, | |
959 | bool mismatch) | |
960 | { | |
961 | struct TestMigrateTLSPSKData *data = | |
962 | g_new0(struct TestMigrateTLSPSKData, 1); | |
58d25e97 DB |
963 | |
964 | data->workdir = g_strdup_printf("%s/tlscredspsk0", tmpfs); | |
965 | data->pskfile = g_strdup_printf("%s/%s", data->workdir, | |
966 | QCRYPTO_TLS_CREDS_PSKFILE); | |
413bebc0 | 967 | g_mkdir_with_parents(data->workdir, 0700); |
58d25e97 DB |
968 | test_tls_psk_init(data->pskfile); |
969 | ||
970 | if (mismatch) { | |
971 | data->workdiralt = g_strdup_printf("%s/tlscredspskalt0", tmpfs); | |
972 | data->pskfilealt = g_strdup_printf("%s/%s", data->workdiralt, | |
973 | QCRYPTO_TLS_CREDS_PSKFILE); | |
413bebc0 | 974 | g_mkdir_with_parents(data->workdiralt, 0700); |
58d25e97 DB |
975 | test_tls_psk_init_alt(data->pskfilealt); |
976 | } | |
977 | ||
aca04069 DB |
978 | qtest_qmp_assert_success(from, |
979 | "{ 'execute': 'object-add'," | |
980 | " 'arguments': { 'qom-type': 'tls-creds-psk'," | |
981 | " 'id': 'tlscredspsk0'," | |
982 | " 'endpoint': 'client'," | |
983 | " 'dir': %s," | |
984 | " 'username': 'qemu'} }", | |
985 | data->workdir); | |
986 | ||
987 | qtest_qmp_assert_success(to, | |
988 | "{ 'execute': 'object-add'," | |
989 | " 'arguments': { 'qom-type': 'tls-creds-psk'," | |
990 | " 'id': 'tlscredspsk0'," | |
991 | " 'endpoint': 'server'," | |
992 | " 'dir': %s } }", | |
993 | mismatch ? data->workdiralt : data->workdir); | |
58d25e97 DB |
994 | |
995 | migrate_set_parameter_str(from, "tls-creds", "tlscredspsk0"); | |
996 | migrate_set_parameter_str(to, "tls-creds", "tlscredspsk0"); | |
997 | ||
998 | return data; | |
999 | } | |
1000 | ||
1001 | static void * | |
1002 | test_migrate_tls_psk_start_match(QTestState *from, | |
1003 | QTestState *to) | |
1004 | { | |
1005 | return test_migrate_tls_psk_start_common(from, to, false); | |
1006 | } | |
1007 | ||
1008 | static void * | |
1009 | test_migrate_tls_psk_start_mismatch(QTestState *from, | |
1010 | QTestState *to) | |
1011 | { | |
1012 | return test_migrate_tls_psk_start_common(from, to, true); | |
1013 | } | |
1014 | ||
1015 | static void | |
1016 | test_migrate_tls_psk_finish(QTestState *from, | |
1017 | QTestState *to, | |
1018 | void *opaque) | |
1019 | { | |
1020 | struct TestMigrateTLSPSKData *data = opaque; | |
1021 | ||
1022 | test_tls_psk_cleanup(data->pskfile); | |
1023 | if (data->pskfilealt) { | |
1024 | test_tls_psk_cleanup(data->pskfilealt); | |
1025 | } | |
1026 | rmdir(data->workdir); | |
1027 | if (data->workdiralt) { | |
1028 | rmdir(data->workdiralt); | |
1029 | } | |
1030 | ||
1031 | g_free(data->workdiralt); | |
1032 | g_free(data->pskfilealt); | |
1033 | g_free(data->workdir); | |
1034 | g_free(data->pskfile); | |
1035 | g_free(data); | |
1036 | } | |
d47b83b1 DB |
1037 | |
1038 | #ifdef CONFIG_TASN1 | |
1039 | typedef struct { | |
1040 | char *workdir; | |
1041 | char *keyfile; | |
1042 | char *cacert; | |
1043 | char *servercert; | |
1044 | char *serverkey; | |
1045 | char *clientcert; | |
1046 | char *clientkey; | |
1047 | } TestMigrateTLSX509Data; | |
1048 | ||
1049 | typedef struct { | |
1050 | bool verifyclient; | |
1051 | bool clientcert; | |
1052 | bool hostileclient; | |
1053 | bool authzclient; | |
1054 | const char *certhostname; | |
1055 | const char *certipaddr; | |
1056 | } TestMigrateTLSX509; | |
1057 | ||
1058 | static void * | |
1059 | test_migrate_tls_x509_start_common(QTestState *from, | |
1060 | QTestState *to, | |
1061 | TestMigrateTLSX509 *args) | |
1062 | { | |
1063 | TestMigrateTLSX509Data *data = g_new0(TestMigrateTLSX509Data, 1); | |
d47b83b1 DB |
1064 | |
1065 | data->workdir = g_strdup_printf("%s/tlscredsx5090", tmpfs); | |
1066 | data->keyfile = g_strdup_printf("%s/key.pem", data->workdir); | |
1067 | ||
1068 | data->cacert = g_strdup_printf("%s/ca-cert.pem", data->workdir); | |
1069 | data->serverkey = g_strdup_printf("%s/server-key.pem", data->workdir); | |
1070 | data->servercert = g_strdup_printf("%s/server-cert.pem", data->workdir); | |
1071 | if (args->clientcert) { | |
1072 | data->clientkey = g_strdup_printf("%s/client-key.pem", data->workdir); | |
1073 | data->clientcert = g_strdup_printf("%s/client-cert.pem", data->workdir); | |
1074 | } | |
1075 | ||
413bebc0 | 1076 | g_mkdir_with_parents(data->workdir, 0700); |
d47b83b1 DB |
1077 | |
1078 | test_tls_init(data->keyfile); | |
2549f610 | 1079 | #ifndef _WIN32 |
d47b83b1 | 1080 | g_assert(link(data->keyfile, data->serverkey) == 0); |
2549f610 BM |
1081 | #else |
1082 | g_assert(CreateHardLink(data->serverkey, data->keyfile, NULL) != 0); | |
1083 | #endif | |
d47b83b1 | 1084 | if (args->clientcert) { |
2549f610 | 1085 | #ifndef _WIN32 |
d47b83b1 | 1086 | g_assert(link(data->keyfile, data->clientkey) == 0); |
2549f610 BM |
1087 | #else |
1088 | g_assert(CreateHardLink(data->clientkey, data->keyfile, NULL) != 0); | |
1089 | #endif | |
d47b83b1 DB |
1090 | } |
1091 | ||
1092 | TLS_ROOT_REQ_SIMPLE(cacertreq, data->cacert); | |
1093 | if (args->clientcert) { | |
1094 | TLS_CERT_REQ_SIMPLE_CLIENT(servercertreq, cacertreq, | |
1095 | args->hostileclient ? | |
1096 | QCRYPTO_TLS_TEST_CLIENT_HOSTILE_NAME : | |
1097 | QCRYPTO_TLS_TEST_CLIENT_NAME, | |
1098 | data->clientcert); | |
1099 | } | |
1100 | ||
1101 | TLS_CERT_REQ_SIMPLE_SERVER(clientcertreq, cacertreq, | |
1102 | data->servercert, | |
1103 | args->certhostname, | |
1104 | args->certipaddr); | |
1105 | ||
aca04069 DB |
1106 | qtest_qmp_assert_success(from, |
1107 | "{ 'execute': 'object-add'," | |
1108 | " 'arguments': { 'qom-type': 'tls-creds-x509'," | |
1109 | " 'id': 'tlscredsx509client0'," | |
1110 | " 'endpoint': 'client'," | |
1111 | " 'dir': %s," | |
1112 | " 'sanity-check': true," | |
1113 | " 'verify-peer': true} }", | |
1114 | data->workdir); | |
d47b83b1 DB |
1115 | migrate_set_parameter_str(from, "tls-creds", "tlscredsx509client0"); |
1116 | if (args->certhostname) { | |
1117 | migrate_set_parameter_str(from, "tls-hostname", args->certhostname); | |
1118 | } | |
1119 | ||
aca04069 DB |
1120 | qtest_qmp_assert_success(to, |
1121 | "{ 'execute': 'object-add'," | |
1122 | " 'arguments': { 'qom-type': 'tls-creds-x509'," | |
1123 | " 'id': 'tlscredsx509server0'," | |
1124 | " 'endpoint': 'server'," | |
1125 | " 'dir': %s," | |
1126 | " 'sanity-check': true," | |
1127 | " 'verify-peer': %i} }", | |
1128 | data->workdir, args->verifyclient); | |
d47b83b1 DB |
1129 | migrate_set_parameter_str(to, "tls-creds", "tlscredsx509server0"); |
1130 | ||
1131 | if (args->authzclient) { | |
aca04069 DB |
1132 | qtest_qmp_assert_success(to, |
1133 | "{ 'execute': 'object-add'," | |
1134 | " 'arguments': { 'qom-type': 'authz-simple'," | |
1135 | " 'id': 'tlsauthz0'," | |
1136 | " 'identity': %s} }", | |
1137 | "CN=" QCRYPTO_TLS_TEST_CLIENT_NAME); | |
d47b83b1 DB |
1138 | migrate_set_parameter_str(to, "tls-authz", "tlsauthz0"); |
1139 | } | |
1140 | ||
1141 | return data; | |
1142 | } | |
1143 | ||
1144 | /* | |
1145 | * The normal case: match server's cert hostname against | |
1146 | * whatever host we were telling QEMU to connect to (if any) | |
1147 | */ | |
1148 | static void * | |
1149 | test_migrate_tls_x509_start_default_host(QTestState *from, | |
1150 | QTestState *to) | |
1151 | { | |
1152 | TestMigrateTLSX509 args = { | |
1153 | .verifyclient = true, | |
1154 | .clientcert = true, | |
1155 | .certipaddr = "127.0.0.1" | |
1156 | }; | |
1157 | return test_migrate_tls_x509_start_common(from, to, &args); | |
1158 | } | |
1159 | ||
1160 | /* | |
1161 | * The unusual case: the server's cert is different from | |
1162 | * the address we're telling QEMU to connect to (if any), | |
1163 | * so we must give QEMU an explicit hostname to validate | |
1164 | */ | |
1165 | static void * | |
1166 | test_migrate_tls_x509_start_override_host(QTestState *from, | |
1167 | QTestState *to) | |
1168 | { | |
1169 | TestMigrateTLSX509 args = { | |
1170 | .verifyclient = true, | |
1171 | .clientcert = true, | |
1172 | .certhostname = "qemu.org", | |
1173 | }; | |
1174 | return test_migrate_tls_x509_start_common(from, to, &args); | |
1175 | } | |
1176 | ||
1177 | /* | |
1178 | * The unusual case: the server's cert is different from | |
1179 | * the address we're telling QEMU to connect to, and so we | |
1180 | * expect the client to reject the server | |
1181 | */ | |
1182 | static void * | |
1183 | test_migrate_tls_x509_start_mismatch_host(QTestState *from, | |
1184 | QTestState *to) | |
1185 | { | |
1186 | TestMigrateTLSX509 args = { | |
1187 | .verifyclient = true, | |
1188 | .clientcert = true, | |
1189 | .certipaddr = "10.0.0.1", | |
1190 | }; | |
1191 | return test_migrate_tls_x509_start_common(from, to, &args); | |
1192 | } | |
1193 | ||
1194 | static void * | |
1195 | test_migrate_tls_x509_start_friendly_client(QTestState *from, | |
1196 | QTestState *to) | |
1197 | { | |
1198 | TestMigrateTLSX509 args = { | |
1199 | .verifyclient = true, | |
1200 | .clientcert = true, | |
1201 | .authzclient = true, | |
1202 | .certipaddr = "127.0.0.1", | |
1203 | }; | |
1204 | return test_migrate_tls_x509_start_common(from, to, &args); | |
1205 | } | |
1206 | ||
1207 | static void * | |
1208 | test_migrate_tls_x509_start_hostile_client(QTestState *from, | |
1209 | QTestState *to) | |
1210 | { | |
1211 | TestMigrateTLSX509 args = { | |
1212 | .verifyclient = true, | |
1213 | .clientcert = true, | |
1214 | .hostileclient = true, | |
1215 | .authzclient = true, | |
1216 | .certipaddr = "127.0.0.1", | |
1217 | }; | |
1218 | return test_migrate_tls_x509_start_common(from, to, &args); | |
1219 | } | |
1220 | ||
1221 | /* | |
1222 | * The case with no client certificate presented, | |
1223 | * and no server verification | |
1224 | */ | |
1225 | static void * | |
1226 | test_migrate_tls_x509_start_allow_anon_client(QTestState *from, | |
1227 | QTestState *to) | |
1228 | { | |
1229 | TestMigrateTLSX509 args = { | |
1230 | .certipaddr = "127.0.0.1", | |
1231 | }; | |
1232 | return test_migrate_tls_x509_start_common(from, to, &args); | |
1233 | } | |
1234 | ||
1235 | /* | |
1236 | * The case with no client certificate presented, | |
1237 | * and server verification rejecting | |
1238 | */ | |
1239 | static void * | |
1240 | test_migrate_tls_x509_start_reject_anon_client(QTestState *from, | |
1241 | QTestState *to) | |
1242 | { | |
1243 | TestMigrateTLSX509 args = { | |
1244 | .verifyclient = true, | |
1245 | .certipaddr = "127.0.0.1", | |
1246 | }; | |
1247 | return test_migrate_tls_x509_start_common(from, to, &args); | |
1248 | } | |
1249 | ||
1250 | static void | |
1251 | test_migrate_tls_x509_finish(QTestState *from, | |
1252 | QTestState *to, | |
1253 | void *opaque) | |
1254 | { | |
1255 | TestMigrateTLSX509Data *data = opaque; | |
1256 | ||
1257 | test_tls_cleanup(data->keyfile); | |
0f0a9e4e TH |
1258 | g_free(data->keyfile); |
1259 | ||
d47b83b1 | 1260 | unlink(data->cacert); |
0f0a9e4e | 1261 | g_free(data->cacert); |
d47b83b1 | 1262 | unlink(data->servercert); |
0f0a9e4e | 1263 | g_free(data->servercert); |
d47b83b1 | 1264 | unlink(data->serverkey); |
0f0a9e4e | 1265 | g_free(data->serverkey); |
d47b83b1 | 1266 | |
0f0a9e4e TH |
1267 | if (data->clientcert) { |
1268 | unlink(data->clientcert); | |
1269 | g_free(data->clientcert); | |
1270 | } | |
1271 | if (data->clientkey) { | |
1272 | unlink(data->clientkey); | |
1273 | g_free(data->clientkey); | |
1274 | } | |
1275 | ||
1276 | rmdir(data->workdir); | |
d47b83b1 | 1277 | g_free(data->workdir); |
0f0a9e4e | 1278 | |
d47b83b1 DB |
1279 | g_free(data); |
1280 | } | |
1281 | #endif /* CONFIG_TASN1 */ | |
58d25e97 DB |
1282 | #endif /* CONFIG_GNUTLS */ |
1283 | ||
dc066da8 LS |
1284 | static void * |
1285 | test_migrate_compress_start(QTestState *from, | |
1286 | QTestState *to) | |
1287 | { | |
1288 | migrate_set_parameter_int(from, "compress-level", 1); | |
1289 | migrate_set_parameter_int(from, "compress-threads", 4); | |
1290 | migrate_set_parameter_bool(from, "compress-wait-thread", true); | |
1291 | migrate_set_parameter_int(to, "decompress-threads", 4); | |
1292 | ||
1293 | migrate_set_capability(from, "compress", true); | |
1294 | migrate_set_capability(to, "compress", true); | |
1295 | ||
1296 | return NULL; | |
1297 | } | |
1298 | ||
1299 | static void * | |
1300 | test_migrate_compress_nowait_start(QTestState *from, | |
1301 | QTestState *to) | |
1302 | { | |
1303 | migrate_set_parameter_int(from, "compress-level", 9); | |
1304 | migrate_set_parameter_int(from, "compress-threads", 1); | |
1305 | migrate_set_parameter_bool(from, "compress-wait-thread", false); | |
1306 | migrate_set_parameter_int(to, "decompress-threads", 1); | |
1307 | ||
1308 | migrate_set_capability(from, "compress", true); | |
1309 | migrate_set_capability(to, "compress", true); | |
1310 | ||
1311 | return NULL; | |
1312 | } | |
1313 | ||
d131662a | 1314 | static int migrate_postcopy_prepare(QTestState **from_ptr, |
5d3b575d | 1315 | QTestState **to_ptr, |
d1a27b16 | 1316 | MigrateCommon *args) |
7195a871 | 1317 | { |
863e27a8 | 1318 | QTestState *from, *to; |
7195a871 | 1319 | |
06c48d6b | 1320 | if (test_migrate_start(&from, &to, "defer", &args->start)) { |
d131662a | 1321 | return -1; |
5fd4a9c9 | 1322 | } |
ea0c6d62 | 1323 | |
d1a27b16 PX |
1324 | if (args->start_hook) { |
1325 | args->postcopy_data = args->start_hook(from, to); | |
1326 | } | |
1327 | ||
c44a56d8 MA |
1328 | migrate_set_capability(from, "postcopy-ram", true); |
1329 | migrate_set_capability(to, "postcopy-ram", true); | |
1330 | migrate_set_capability(to, "postcopy-blocktime", true); | |
ea0c6d62 | 1331 | |
8f6fe915 PX |
1332 | if (args->postcopy_preempt) { |
1333 | migrate_set_capability(from, "postcopy-preempt", true); | |
1334 | migrate_set_capability(to, "postcopy-preempt", true); | |
1335 | } | |
1336 | ||
886dfe9d | 1337 | migrate_ensure_non_converge(from); |
ea0c6d62 | 1338 | |
e02f56e3 | 1339 | migrate_prepare_for_dirty_mem(from); |
06c48d6b | 1340 | qtest_qmp_assert_success(to, "{ 'execute': 'migrate-incoming'," |
8e3766ee HG |
1341 | " 'arguments': { " |
1342 | " 'channels': [ { 'channel-type': 'main'," | |
1343 | " 'addr': { 'transport': 'socket'," | |
1344 | " 'type': 'inet'," | |
1345 | " 'host': '127.0.0.1'," | |
1346 | " 'port': '0' } } ] } }"); | |
e02f56e3 | 1347 | |
ea0c6d62 DDAG |
1348 | /* Wait for the first serial output from the source */ |
1349 | wait_for_serial("src_serial"); | |
2b58a8b9 | 1350 | wait_for_suspend(from, &src_state); |
ea0c6d62 | 1351 | |
06c48d6b | 1352 | g_autofree char *uri = migrate_get_socket_address(to, "socket-address"); |
d77799cc | 1353 | migrate_qmp(from, uri, "{}"); |
ea0c6d62 | 1354 | |
e02f56e3 | 1355 | migrate_wait_for_dirty_mem(from, to); |
ea0c6d62 | 1356 | |
d131662a PX |
1357 | *from_ptr = from; |
1358 | *to_ptr = to; | |
ea0c6d62 | 1359 | |
d131662a PX |
1360 | return 0; |
1361 | } | |
ea0c6d62 | 1362 | |
d1a27b16 PX |
1363 | static void migrate_postcopy_complete(QTestState *from, QTestState *to, |
1364 | MigrateCommon *args) | |
d131662a PX |
1365 | { |
1366 | wait_for_migration_complete(from); | |
ea0c6d62 | 1367 | |
2b58a8b9 SS |
1368 | if (args->start.suspend_me) { |
1369 | /* wakeup succeeds only if guest is suspended */ | |
1370 | qtest_qmp_assert_success(to, "{'execute': 'system_wakeup'}"); | |
1371 | } | |
1372 | ||
d131662a | 1373 | /* Make sure we get at least one "B" on destination */ |
ea0c6d62 | 1374 | wait_for_serial("dest_serial"); |
ea0c6d62 | 1375 | |
346f3dab AP |
1376 | if (uffd_feature_thread_id) { |
1377 | read_blocktime(to); | |
1378 | } | |
ea0c6d62 | 1379 | |
d1a27b16 PX |
1380 | if (args->finish_hook) { |
1381 | args->finish_hook(from, to, args->postcopy_data); | |
1382 | args->postcopy_data = NULL; | |
1383 | } | |
1384 | ||
2c9bb297 DDAG |
1385 | test_migrate_end(from, to, true); |
1386 | } | |
1387 | ||
d1a27b16 | 1388 | static void test_postcopy_common(MigrateCommon *args) |
d131662a PX |
1389 | { |
1390 | QTestState *from, *to; | |
1391 | ||
d1a27b16 | 1392 | if (migrate_postcopy_prepare(&from, &to, args)) { |
d131662a PX |
1393 | return; |
1394 | } | |
1395 | migrate_postcopy_start(from, to); | |
d1a27b16 | 1396 | migrate_postcopy_complete(from, to, args); |
d131662a PX |
1397 | } |
1398 | ||
d1a27b16 PX |
1399 | static void test_postcopy(void) |
1400 | { | |
1401 | MigrateCommon args = { }; | |
1402 | ||
1403 | test_postcopy_common(&args); | |
1404 | } | |
1405 | ||
2b58a8b9 SS |
1406 | static void test_postcopy_suspend(void) |
1407 | { | |
1408 | MigrateCommon args = { | |
1409 | .start.suspend_me = true, | |
1410 | }; | |
1411 | ||
1412 | test_postcopy_common(&args); | |
1413 | } | |
1414 | ||
dc066da8 LS |
1415 | static void test_postcopy_compress(void) |
1416 | { | |
1417 | MigrateCommon args = { | |
1418 | .start_hook = test_migrate_compress_start | |
1419 | }; | |
1420 | ||
1421 | test_postcopy_common(&args); | |
1422 | } | |
1423 | ||
8f6fe915 PX |
1424 | static void test_postcopy_preempt(void) |
1425 | { | |
1426 | MigrateCommon args = { | |
1427 | .postcopy_preempt = true, | |
1428 | }; | |
1429 | ||
1430 | test_postcopy_common(&args); | |
1431 | } | |
1432 | ||
d1a27b16 PX |
1433 | #ifdef CONFIG_GNUTLS |
1434 | static void test_postcopy_tls_psk(void) | |
1435 | { | |
1436 | MigrateCommon args = { | |
1437 | .start_hook = test_migrate_tls_psk_start_match, | |
1438 | .finish_hook = test_migrate_tls_psk_finish, | |
1439 | }; | |
1440 | ||
1441 | test_postcopy_common(&args); | |
1442 | } | |
8f6fe915 PX |
1443 | |
1444 | static void test_postcopy_preempt_tls_psk(void) | |
1445 | { | |
1446 | MigrateCommon args = { | |
1447 | .postcopy_preempt = true, | |
1448 | .start_hook = test_migrate_tls_psk_start_match, | |
1449 | .finish_hook = test_migrate_tls_psk_finish, | |
1450 | }; | |
1451 | ||
1452 | test_postcopy_common(&args); | |
1453 | } | |
d1a27b16 PX |
1454 | #endif |
1455 | ||
7bca2bb7 FR |
1456 | static void wait_for_postcopy_status(QTestState *one, const char *status) |
1457 | { | |
1458 | wait_for_migration_status(one, status, | |
1459 | (const char * []) { "failed", "active", | |
1460 | "completed", NULL }); | |
1461 | } | |
1462 | ||
1463 | #ifndef _WIN32 | |
1464 | static void postcopy_recover_fail(QTestState *from, QTestState *to) | |
1465 | { | |
1466 | int ret, pair1[2], pair2[2]; | |
1467 | char c; | |
1468 | ||
1469 | /* Create two unrelated socketpairs */ | |
1470 | ret = qemu_socketpair(PF_LOCAL, SOCK_STREAM, 0, pair1); | |
1471 | g_assert_cmpint(ret, ==, 0); | |
1472 | ||
1473 | ret = qemu_socketpair(PF_LOCAL, SOCK_STREAM, 0, pair2); | |
1474 | g_assert_cmpint(ret, ==, 0); | |
1475 | ||
1476 | /* | |
1477 | * Give the guests unpaired ends of the sockets, so they'll all blocked | |
1478 | * at reading. This mimics a wrong channel established. | |
1479 | */ | |
1480 | qtest_qmp_fds_assert_success(from, &pair1[0], 1, | |
1481 | "{ 'execute': 'getfd'," | |
1482 | " 'arguments': { 'fdname': 'fd-mig' }}"); | |
1483 | qtest_qmp_fds_assert_success(to, &pair2[0], 1, | |
1484 | "{ 'execute': 'getfd'," | |
1485 | " 'arguments': { 'fdname': 'fd-mig' }}"); | |
1486 | ||
1487 | /* | |
1488 | * Write the 1st byte as QEMU_VM_COMMAND (0x8) for the dest socket, to | |
1489 | * emulate the 1st byte of a real recovery, but stops from there to | |
1490 | * keep dest QEMU in RECOVER. This is needed so that we can kick off | |
1491 | * the recover process on dest QEMU (by triggering the G_IO_IN event). | |
1492 | * | |
1493 | * NOTE: this trick is not needed on src QEMUs, because src doesn't | |
1494 | * rely on an pre-existing G_IO_IN event, so it will always trigger the | |
1495 | * upcoming recovery anyway even if it can read nothing. | |
1496 | */ | |
1497 | #define QEMU_VM_COMMAND 0x08 | |
1498 | c = QEMU_VM_COMMAND; | |
1499 | ret = send(pair2[1], &c, 1, 0); | |
1500 | g_assert_cmpint(ret, ==, 1); | |
1501 | ||
1502 | migrate_recover(to, "fd:fd-mig"); | |
1503 | migrate_qmp(from, "fd:fd-mig", "{'resume': true}"); | |
1504 | ||
1505 | /* | |
1506 | * Make sure both QEMU instances will go into RECOVER stage, then test | |
1507 | * kicking them out using migrate-pause. | |
1508 | */ | |
1509 | wait_for_postcopy_status(from, "postcopy-recover"); | |
1510 | wait_for_postcopy_status(to, "postcopy-recover"); | |
1511 | ||
1512 | /* | |
1513 | * This would be issued by the admin upon noticing the hang, we should | |
1514 | * make sure we're able to kick this out. | |
1515 | */ | |
1516 | migrate_pause(from); | |
1517 | wait_for_postcopy_status(from, "postcopy-paused"); | |
1518 | ||
1519 | /* Do the same test on dest */ | |
1520 | migrate_pause(to); | |
1521 | wait_for_postcopy_status(to, "postcopy-paused"); | |
1522 | ||
1523 | close(pair1[0]); | |
1524 | close(pair1[1]); | |
1525 | close(pair2[0]); | |
1526 | close(pair2[1]); | |
1527 | } | |
1528 | #endif /* _WIN32 */ | |
1529 | ||
767fa9cf | 1530 | static void test_postcopy_recovery_common(MigrateCommon *args) |
d5f49640 PX |
1531 | { |
1532 | QTestState *from, *to; | |
ff7b9b56 | 1533 | g_autofree char *uri = NULL; |
d5f49640 | 1534 | |
767fa9cf PX |
1535 | /* Always hide errors for postcopy recover tests since they're expected */ |
1536 | args->start.hide_stderr = true; | |
1537 | ||
1538 | if (migrate_postcopy_prepare(&from, &to, args)) { | |
d5f49640 PX |
1539 | return; |
1540 | } | |
1541 | ||
1542 | /* Turn postcopy speed down, 4K/s is slow enough on any machines */ | |
8f7798f1 | 1543 | migrate_set_parameter_int(from, "max-postcopy-bandwidth", 4096); |
d5f49640 PX |
1544 | |
1545 | /* Now we start the postcopy */ | |
1546 | migrate_postcopy_start(from, to); | |
1547 | ||
1548 | /* | |
1549 | * Wait until postcopy is really started; we can only run the | |
1550 | * migrate-pause command during a postcopy | |
1551 | */ | |
8c51642b | 1552 | wait_for_migration_status(from, "postcopy-active", NULL); |
d5f49640 PX |
1553 | |
1554 | /* | |
1555 | * Manually stop the postcopy migration. This emulates a network | |
1556 | * failure with the migration socket | |
1557 | */ | |
1558 | migrate_pause(from); | |
1559 | ||
1560 | /* | |
1561 | * Wait for destination side to reach postcopy-paused state. The | |
1562 | * migrate-recover command can only succeed if destination machine | |
1563 | * is in the paused state | |
1564 | */ | |
7bca2bb7 FR |
1565 | wait_for_postcopy_status(to, "postcopy-paused"); |
1566 | wait_for_postcopy_status(from, "postcopy-paused"); | |
1567 | ||
1568 | #ifndef _WIN32 | |
1569 | if (args->postcopy_recovery_test_fail) { | |
1570 | /* | |
1571 | * Test when a wrong socket specified for recover, and then the | |
1572 | * ability to kick it out, and continue with a correct socket. | |
1573 | */ | |
1574 | postcopy_recover_fail(from, to); | |
1575 | /* continue with a good recovery */ | |
1576 | } | |
1577 | #endif /* _WIN32 */ | |
d5f49640 PX |
1578 | |
1579 | /* | |
1580 | * Create a new socket to emulate a new channel that is different | |
1581 | * from the broken migration channel; tell the destination to | |
1582 | * listen to the new port | |
1583 | */ | |
1584 | uri = g_strdup_printf("unix:%s/migsocket-recover", tmpfs); | |
1585 | migrate_recover(to, uri); | |
1586 | ||
1587 | /* | |
1588 | * Try to rebuild the migration channel using the resume flag and | |
1589 | * the newly created channel | |
1590 | */ | |
d77799cc | 1591 | migrate_qmp(from, uri, "{'resume': true}"); |
d5f49640 PX |
1592 | |
1593 | /* Restore the postcopy bandwidth to unlimited */ | |
8f7798f1 | 1594 | migrate_set_parameter_int(from, "max-postcopy-bandwidth", 0); |
d5f49640 | 1595 | |
767fa9cf PX |
1596 | migrate_postcopy_complete(from, to, args); |
1597 | } | |
1598 | ||
1599 | static void test_postcopy_recovery(void) | |
1600 | { | |
1601 | MigrateCommon args = { }; | |
1602 | ||
1603 | test_postcopy_recovery_common(&args); | |
d5f49640 PX |
1604 | } |
1605 | ||
dc066da8 LS |
1606 | static void test_postcopy_recovery_compress(void) |
1607 | { | |
1608 | MigrateCommon args = { | |
1609 | .start_hook = test_migrate_compress_start | |
1610 | }; | |
1611 | ||
1612 | test_postcopy_recovery_common(&args); | |
1613 | } | |
1614 | ||
7bca2bb7 FR |
1615 | #ifndef _WIN32 |
1616 | static void test_postcopy_recovery_double_fail(void) | |
1617 | { | |
1618 | MigrateCommon args = { | |
1619 | .postcopy_recovery_test_fail = true, | |
1620 | }; | |
1621 | ||
1622 | test_postcopy_recovery_common(&args); | |
1623 | } | |
1624 | #endif /* _WIN32 */ | |
1625 | ||
767fa9cf PX |
1626 | #ifdef CONFIG_GNUTLS |
1627 | static void test_postcopy_recovery_tls_psk(void) | |
1628 | { | |
1629 | MigrateCommon args = { | |
1630 | .start_hook = test_migrate_tls_psk_start_match, | |
1631 | .finish_hook = test_migrate_tls_psk_finish, | |
1632 | }; | |
1633 | ||
1634 | test_postcopy_recovery_common(&args); | |
1635 | } | |
1636 | #endif | |
1637 | ||
8f6fe915 PX |
1638 | static void test_postcopy_preempt_recovery(void) |
1639 | { | |
1640 | MigrateCommon args = { | |
1641 | .postcopy_preempt = true, | |
1642 | }; | |
1643 | ||
1644 | test_postcopy_recovery_common(&args); | |
1645 | } | |
1646 | ||
1647 | #ifdef CONFIG_GNUTLS | |
1648 | /* This contains preempt+recovery+tls test altogether */ | |
1649 | static void test_postcopy_preempt_all(void) | |
1650 | { | |
1651 | MigrateCommon args = { | |
1652 | .postcopy_preempt = true, | |
1653 | .start_hook = test_migrate_tls_psk_start_match, | |
1654 | .finish_hook = test_migrate_tls_psk_finish, | |
1655 | }; | |
1656 | ||
1657 | test_postcopy_recovery_common(&args); | |
1658 | } | |
dc066da8 | 1659 | |
8f6fe915 PX |
1660 | #endif |
1661 | ||
3af31a34 YK |
1662 | static void test_baddest(void) |
1663 | { | |
19da6edf DB |
1664 | MigrateStart args = { |
1665 | .hide_stderr = true | |
1666 | }; | |
3af31a34 | 1667 | QTestState *from, *to; |
2c9bb297 | 1668 | |
3ff57401 | 1669 | if (test_migrate_start(&from, &to, "tcp:127.0.0.1:0", &args)) { |
3af31a34 YK |
1670 | return; |
1671 | } | |
5e32ffd3 | 1672 | migrate_qmp(from, "tcp:127.0.0.1:0", "{}"); |
3af31a34 | 1673 | wait_for_migration_fail(from, false); |
2c9bb297 | 1674 | test_migrate_end(from, to, false); |
ea0c6d62 DDAG |
1675 | } |
1676 | ||
d864756e FR |
1677 | #ifndef _WIN32 |
1678 | static void test_analyze_script(void) | |
1679 | { | |
1680 | MigrateStart args = { | |
1681 | .opts_source = "-uuid 11111111-1111-1111-1111-111111111111", | |
1682 | }; | |
1683 | QTestState *from, *to; | |
1684 | g_autofree char *uri = NULL; | |
1685 | g_autofree char *file = NULL; | |
1686 | int pid, wstatus; | |
1687 | const char *python = g_getenv("PYTHON"); | |
1688 | ||
1689 | if (!python) { | |
1690 | g_test_skip("PYTHON variable not set"); | |
1691 | return; | |
1692 | } | |
1693 | ||
1694 | /* dummy url */ | |
1695 | if (test_migrate_start(&from, &to, "tcp:127.0.0.1:0", &args)) { | |
1696 | return; | |
1697 | } | |
1698 | ||
1699 | /* | |
1700 | * Setting these two capabilities causes the "configuration" | |
1701 | * vmstate to include subsections for them. The script needs to | |
1702 | * parse those subsections properly. | |
1703 | */ | |
1704 | migrate_set_capability(from, "validate-uuid", true); | |
1705 | migrate_set_capability(from, "x-ignore-shared", true); | |
1706 | ||
1707 | file = g_strdup_printf("%s/migfile", tmpfs); | |
1708 | uri = g_strdup_printf("exec:cat > %s", file); | |
1709 | ||
1710 | migrate_ensure_converge(from); | |
1711 | migrate_qmp(from, uri, "{}"); | |
1712 | wait_for_migration_complete(from); | |
1713 | ||
1714 | pid = fork(); | |
1715 | if (!pid) { | |
1716 | close(1); | |
1717 | open("/dev/null", O_WRONLY); | |
1718 | execl(python, python, ANALYZE_SCRIPT, "-f", file, NULL); | |
1719 | g_assert_not_reached(); | |
1720 | } | |
1721 | ||
1722 | g_assert(waitpid(pid, &wstatus, 0) == pid); | |
1723 | if (WIFEXITED(wstatus) && WEXITSTATUS(wstatus) != 0) { | |
1724 | g_test_message("Failed to analyze the migration stream"); | |
1725 | g_test_fail(); | |
1726 | } | |
1727 | test_migrate_end(from, to, false); | |
1728 | cleanup("migfile"); | |
1729 | } | |
1730 | #endif | |
1731 | ||
ffed54f6 | 1732 | static void test_precopy_common(MigrateCommon *args) |
2884100c | 1733 | { |
2884100c | 1734 | QTestState *from, *to; |
b3caa7b5 | 1735 | void *data_hook = NULL; |
5274274c | 1736 | g_autofree char *connect_uri = NULL; |
2884100c | 1737 | |
ffed54f6 | 1738 | if (test_migrate_start(&from, &to, args->listen_uri, &args->start)) { |
5fd4a9c9 DDAG |
1739 | return; |
1740 | } | |
2884100c | 1741 | |
b3caa7b5 DB |
1742 | if (args->start_hook) { |
1743 | data_hook = args->start_hook(from, to); | |
1744 | } | |
1745 | ||
2884100c | 1746 | /* Wait for the first serial output from the source */ |
e25636a1 TH |
1747 | if (args->result == MIG_TEST_SUCCEED) { |
1748 | wait_for_serial("src_serial"); | |
b1fdd21e | 1749 | wait_for_suspend(from, &src_state); |
e25636a1 | 1750 | } |
2884100c | 1751 | |
3c4fb177 | 1752 | if (args->live) { |
3c4fb177 | 1753 | migrate_ensure_non_converge(from); |
e02f56e3 | 1754 | migrate_prepare_for_dirty_mem(from); |
3c4fb177 DB |
1755 | } else { |
1756 | /* | |
1757 | * Testing non-live migration, we allow it to run at | |
1758 | * full speed to ensure short test case duration. | |
1759 | * For tests expected to fail, we don't need to | |
1760 | * change anything. | |
1761 | */ | |
1762 | if (args->result == MIG_TEST_SUCCEED) { | |
1763 | qtest_qmp_assert_success(from, "{ 'execute' : 'stop'}"); | |
f0649758 | 1764 | wait_for_stop(from, &src_state); |
3c4fb177 DB |
1765 | migrate_ensure_converge(from); |
1766 | } | |
1767 | } | |
1768 | ||
ffed54f6 | 1769 | if (!args->connect_uri) { |
5274274c | 1770 | connect_uri = migrate_get_socket_address(to, "socket-address"); |
ffed54f6 | 1771 | } else { |
5274274c | 1772 | connect_uri = g_strdup(args->connect_uri); |
ffed54f6 DB |
1773 | } |
1774 | ||
5274274c FR |
1775 | if (args->result == MIG_TEST_QMP_ERROR) { |
1776 | migrate_qmp_fail(from, connect_uri, "{}"); | |
1777 | goto finish; | |
1778 | } | |
1779 | ||
1780 | migrate_qmp(from, connect_uri, "{}"); | |
2884100c | 1781 | |
00fbe7f6 DB |
1782 | if (args->result != MIG_TEST_SUCCEED) { |
1783 | bool allow_active = args->result == MIG_TEST_FAIL; | |
1784 | wait_for_migration_fail(from, allow_active); | |
2884100c | 1785 | |
00fbe7f6 | 1786 | if (args->result == MIG_TEST_FAIL_DEST_QUIT_ERR) { |
1b0f1b14 | 1787 | qtest_set_expected_status(to, EXIT_FAILURE); |
00fbe7f6 DB |
1788 | } |
1789 | } else { | |
3c4fb177 | 1790 | if (args->live) { |
e02f56e3 DB |
1791 | /* |
1792 | * For initial iteration(s) we must do a full pass, | |
1793 | * but for the final iteration, we need only wait | |
1794 | * for some dirty mem before switching to converge | |
1795 | */ | |
1796 | while (args->iterations > 1) { | |
83bcba1e | 1797 | wait_for_migration_pass(from); |
e02f56e3 | 1798 | args->iterations--; |
83bcba1e | 1799 | } |
e02f56e3 | 1800 | migrate_wait_for_dirty_mem(from, to); |
2884100c | 1801 | |
3c4fb177 | 1802 | migrate_ensure_converge(from); |
2884100c | 1803 | |
3c4fb177 DB |
1804 | /* |
1805 | * We do this first, as it has a timeout to stop us | |
1806 | * hanging forever if migration didn't converge | |
1807 | */ | |
1808 | wait_for_migration_complete(from); | |
8d4e897a | 1809 | |
f0649758 SS |
1810 | wait_for_stop(from, &src_state); |
1811 | ||
3c4fb177 DB |
1812 | } else { |
1813 | wait_for_migration_complete(from); | |
1814 | /* | |
1815 | * Must wait for dst to finish reading all incoming | |
1816 | * data on the socket before issuing 'cont' otherwise | |
1817 | * it'll be ignored | |
1818 | */ | |
1819 | wait_for_migration_complete(to); | |
1820 | ||
1821 | qtest_qmp_assert_success(to, "{ 'execute' : 'cont'}"); | |
00fbe7f6 | 1822 | } |
2884100c | 1823 | |
f0649758 | 1824 | wait_for_resume(to, &dst_state); |
00fbe7f6 | 1825 | |
b1fdd21e SS |
1826 | if (args->start.suspend_me) { |
1827 | /* wakeup succeeds only if guest is suspended */ | |
1828 | qtest_qmp_assert_success(to, "{'execute': 'system_wakeup'}"); | |
1829 | } | |
1830 | ||
00fbe7f6 | 1831 | wait_for_serial("dest_serial"); |
00fbe7f6 | 1832 | } |
2884100c | 1833 | |
5274274c | 1834 | finish: |
b3caa7b5 DB |
1835 | if (args->finish_hook) { |
1836 | args->finish_hook(from, to, data_hook); | |
1837 | } | |
1838 | ||
00fbe7f6 | 1839 | test_migrate_end(from, to, args->result == MIG_TEST_SUCCEED); |
2884100c JQ |
1840 | } |
1841 | ||
3dc35470 FR |
1842 | static void test_file_common(MigrateCommon *args, bool stop_src) |
1843 | { | |
1844 | QTestState *from, *to; | |
1845 | void *data_hook = NULL; | |
1846 | g_autofree char *connect_uri = g_strdup(args->connect_uri); | |
1847 | ||
1848 | if (test_migrate_start(&from, &to, args->listen_uri, &args->start)) { | |
1849 | return; | |
1850 | } | |
1851 | ||
1852 | /* | |
1853 | * File migration is never live. We can keep the source VM running | |
1854 | * during migration, but the destination will not be running | |
1855 | * concurrently. | |
1856 | */ | |
1857 | g_assert_false(args->live); | |
1858 | ||
1859 | if (args->start_hook) { | |
1860 | data_hook = args->start_hook(from, to); | |
1861 | } | |
1862 | ||
1863 | migrate_ensure_converge(from); | |
1864 | wait_for_serial("src_serial"); | |
1865 | ||
1866 | if (stop_src) { | |
1867 | qtest_qmp_assert_success(from, "{ 'execute' : 'stop'}"); | |
f0649758 | 1868 | wait_for_stop(from, &src_state); |
3dc35470 FR |
1869 | } |
1870 | ||
1871 | if (args->result == MIG_TEST_QMP_ERROR) { | |
1872 | migrate_qmp_fail(from, connect_uri, "{}"); | |
1873 | goto finish; | |
1874 | } | |
1875 | ||
1876 | migrate_qmp(from, connect_uri, "{}"); | |
1877 | wait_for_migration_complete(from); | |
1878 | ||
1879 | /* | |
1880 | * We need to wait for the source to finish before starting the | |
1881 | * destination. | |
1882 | */ | |
1883 | migrate_incoming_qmp(to, connect_uri, "{}"); | |
1884 | wait_for_migration_complete(to); | |
1885 | ||
1886 | if (stop_src) { | |
1887 | qtest_qmp_assert_success(to, "{ 'execute' : 'cont'}"); | |
1888 | } | |
f0649758 | 1889 | wait_for_resume(to, &dst_state); |
3dc35470 FR |
1890 | |
1891 | wait_for_serial("dest_serial"); | |
1892 | ||
1893 | finish: | |
1894 | if (args->finish_hook) { | |
1895 | args->finish_hook(from, to, data_hook); | |
1896 | } | |
1897 | ||
1898 | test_migrate_end(from, to, args->result == MIG_TEST_SUCCEED); | |
1899 | } | |
1900 | ||
58d25e97 | 1901 | static void test_precopy_unix_plain(void) |
1f546b70 | 1902 | { |
ffed54f6 DB |
1903 | g_autofree char *uri = g_strdup_printf("unix:%s/migsocket", tmpfs); |
1904 | MigrateCommon args = { | |
1905 | .listen_uri = uri, | |
1906 | .connect_uri = uri, | |
b861383c PX |
1907 | /* |
1908 | * The simplest use case of precopy, covering smoke tests of | |
1909 | * get-dirty-log dirty tracking. | |
1910 | */ | |
3c4fb177 | 1911 | .live = true, |
ffed54f6 DB |
1912 | }; |
1913 | ||
1914 | test_precopy_common(&args); | |
1f546b70 PX |
1915 | } |
1916 | ||
b1fdd21e SS |
1917 | static void test_precopy_unix_suspend_live(void) |
1918 | { | |
1919 | g_autofree char *uri = g_strdup_printf("unix:%s/migsocket", tmpfs); | |
1920 | MigrateCommon args = { | |
1921 | .listen_uri = uri, | |
1922 | .connect_uri = uri, | |
1923 | /* | |
1924 | * despite being live, the test is fast because the src | |
1925 | * suspends immediately. | |
1926 | */ | |
1927 | .live = true, | |
1928 | .start.suspend_me = true, | |
1929 | }; | |
1930 | ||
1931 | test_precopy_common(&args); | |
1932 | } | |
1933 | ||
1934 | static void test_precopy_unix_suspend_notlive(void) | |
1935 | { | |
1936 | g_autofree char *uri = g_strdup_printf("unix:%s/migsocket", tmpfs); | |
1937 | MigrateCommon args = { | |
1938 | .listen_uri = uri, | |
1939 | .connect_uri = uri, | |
1940 | .start.suspend_me = true, | |
1941 | }; | |
1942 | ||
1943 | test_precopy_common(&args); | |
1944 | } | |
d47b83b1 DB |
1945 | |
1946 | static void test_precopy_unix_dirty_ring(void) | |
1947 | { | |
1948 | g_autofree char *uri = g_strdup_printf("unix:%s/migsocket", tmpfs); | |
1949 | MigrateCommon args = { | |
1950 | .start = { | |
1951 | .use_dirty_ring = true, | |
1952 | }, | |
1953 | .listen_uri = uri, | |
1954 | .connect_uri = uri, | |
b861383c PX |
1955 | /* |
1956 | * Besides the precopy/unix basic test, cover dirty ring interface | |
1957 | * rather than get-dirty-log. | |
1958 | */ | |
3c4fb177 | 1959 | .live = true, |
d47b83b1 DB |
1960 | }; |
1961 | ||
1962 | test_precopy_common(&args); | |
1963 | } | |
1964 | ||
58d25e97 DB |
1965 | #ifdef CONFIG_GNUTLS |
1966 | static void test_precopy_unix_tls_psk(void) | |
1967 | { | |
1968 | g_autofree char *uri = g_strdup_printf("unix:%s/migsocket", tmpfs); | |
1969 | MigrateCommon args = { | |
1970 | .connect_uri = uri, | |
1971 | .listen_uri = uri, | |
1972 | .start_hook = test_migrate_tls_psk_start_match, | |
1973 | .finish_hook = test_migrate_tls_psk_finish, | |
1974 | }; | |
1975 | ||
1976 | test_precopy_common(&args); | |
1977 | } | |
58d25e97 | 1978 | |
d47b83b1 DB |
1979 | #ifdef CONFIG_TASN1 |
1980 | static void test_precopy_unix_tls_x509_default_host(void) | |
1f546b70 | 1981 | { |
ffed54f6 DB |
1982 | g_autofree char *uri = g_strdup_printf("unix:%s/migsocket", tmpfs); |
1983 | MigrateCommon args = { | |
1984 | .start = { | |
d47b83b1 | 1985 | .hide_stderr = true, |
ffed54f6 | 1986 | }, |
d47b83b1 | 1987 | .connect_uri = uri, |
ffed54f6 | 1988 | .listen_uri = uri, |
d47b83b1 DB |
1989 | .start_hook = test_migrate_tls_x509_start_default_host, |
1990 | .finish_hook = test_migrate_tls_x509_finish, | |
1991 | .result = MIG_TEST_FAIL_DEST_QUIT_ERR, | |
1992 | }; | |
1993 | ||
1994 | test_precopy_common(&args); | |
1995 | } | |
1996 | ||
1997 | static void test_precopy_unix_tls_x509_override_host(void) | |
1998 | { | |
1999 | g_autofree char *uri = g_strdup_printf("unix:%s/migsocket", tmpfs); | |
2000 | MigrateCommon args = { | |
ffed54f6 | 2001 | .connect_uri = uri, |
d47b83b1 DB |
2002 | .listen_uri = uri, |
2003 | .start_hook = test_migrate_tls_x509_start_override_host, | |
2004 | .finish_hook = test_migrate_tls_x509_finish, | |
ffed54f6 DB |
2005 | }; |
2006 | ||
2007 | test_precopy_common(&args); | |
1f546b70 | 2008 | } |
d47b83b1 DB |
2009 | #endif /* CONFIG_TASN1 */ |
2010 | #endif /* CONFIG_GNUTLS */ | |
1f546b70 | 2011 | |
660a9b68 YK |
2012 | #if 0 |
2013 | /* Currently upset on aarch64 TCG */ | |
2014 | static void test_ignore_shared(void) | |
2015 | { | |
ff7b9b56 | 2016 | g_autofree char *uri = g_strdup_printf("unix:%s/migsocket", tmpfs); |
660a9b68 YK |
2017 | QTestState *from, *to; |
2018 | ||
3af31a34 | 2019 | if (test_migrate_start(&from, &to, uri, false, true, NULL, NULL)) { |
660a9b68 YK |
2020 | return; |
2021 | } | |
2022 | ||
e02f56e3 DB |
2023 | migrate_ensure_non_converge(from); |
2024 | migrate_prepare_for_dirty_mem(from); | |
2025 | ||
660a9b68 YK |
2026 | migrate_set_capability(from, "x-ignore-shared", true); |
2027 | migrate_set_capability(to, "x-ignore-shared", true); | |
2028 | ||
2029 | /* Wait for the first serial output from the source */ | |
2030 | wait_for_serial("src_serial"); | |
2031 | ||
d77799cc | 2032 | migrate_qmp(from, uri, "{}"); |
660a9b68 | 2033 | |
e02f56e3 | 2034 | migrate_wait_for_dirty_mem(from, to); |
660a9b68 | 2035 | |
f0649758 | 2036 | wait_for_stop(from, &src_state); |
660a9b68 YK |
2037 | |
2038 | qtest_qmp_eventwait(to, "RESUME"); | |
2039 | ||
2040 | wait_for_serial("dest_serial"); | |
2041 | wait_for_migration_complete(from); | |
2042 | ||
2043 | /* Check whether shared RAM has been really skipped */ | |
2044 | g_assert_cmpint(read_ram_property_int(from, "transferred"), <, 1024 * 1024); | |
2045 | ||
2046 | test_migrate_end(from, to, true); | |
660a9b68 YK |
2047 | } |
2048 | #endif | |
2049 | ||
83bcba1e DB |
2050 | static void * |
2051 | test_migrate_xbzrle_start(QTestState *from, | |
2052 | QTestState *to) | |
cdf84229 | 2053 | { |
8f7798f1 | 2054 | migrate_set_parameter_int(from, "xbzrle-cache-size", 33554432); |
cdf84229 | 2055 | |
a1209bb7 DDAG |
2056 | migrate_set_capability(from, "xbzrle", true); |
2057 | migrate_set_capability(to, "xbzrle", true); | |
cdf84229 | 2058 | |
83bcba1e | 2059 | return NULL; |
cdf84229 JQ |
2060 | } |
2061 | ||
83bcba1e | 2062 | static void test_precopy_unix_xbzrle(void) |
cdf84229 | 2063 | { |
ff7b9b56 | 2064 | g_autofree char *uri = g_strdup_printf("unix:%s/migsocket", tmpfs); |
83bcba1e DB |
2065 | MigrateCommon args = { |
2066 | .connect_uri = uri, | |
2067 | .listen_uri = uri, | |
83bcba1e | 2068 | .start_hook = test_migrate_xbzrle_start, |
83bcba1e | 2069 | .iterations = 2, |
b861383c PX |
2070 | /* |
2071 | * XBZRLE needs pages to be modified when doing the 2nd+ round | |
2072 | * iteration to have real data pushed to the stream. | |
2073 | */ | |
3c4fb177 | 2074 | .live = true, |
83bcba1e DB |
2075 | }; |
2076 | ||
2077 | test_precopy_common(&args); | |
cdf84229 JQ |
2078 | } |
2079 | ||
1536d1da LS |
2080 | static void test_precopy_unix_compress(void) |
2081 | { | |
2082 | g_autofree char *uri = g_strdup_printf("unix:%s/migsocket", tmpfs); | |
2083 | MigrateCommon args = { | |
2084 | .connect_uri = uri, | |
2085 | .listen_uri = uri, | |
2086 | .start_hook = test_migrate_compress_start, | |
2087 | /* | |
2088 | * Test that no invalid thread state is left over from | |
2089 | * the previous iteration. | |
2090 | */ | |
2091 | .iterations = 2, | |
b861383c PX |
2092 | /* |
2093 | * We make sure the compressor can always work well even if guest | |
2094 | * memory is changing. See commit 34ab9e9743 where we used to fix | |
2095 | * a bug when only trigger-able with guest memory changing. | |
2096 | */ | |
3c4fb177 | 2097 | .live = true, |
1536d1da LS |
2098 | }; |
2099 | ||
2100 | test_precopy_common(&args); | |
2101 | } | |
2102 | ||
1536d1da LS |
2103 | static void test_precopy_unix_compress_nowait(void) |
2104 | { | |
2105 | g_autofree char *uri = g_strdup_printf("unix:%s/migsocket", tmpfs); | |
2106 | MigrateCommon args = { | |
2107 | .connect_uri = uri, | |
2108 | .listen_uri = uri, | |
2109 | .start_hook = test_migrate_compress_nowait_start, | |
2110 | /* | |
2111 | * Test that no invalid thread state is left over from | |
2112 | * the previous iteration. | |
2113 | */ | |
2114 | .iterations = 2, | |
b861383c | 2115 | /* Same reason for the wait version of precopy compress test */ |
3c4fb177 | 2116 | .live = true, |
1536d1da LS |
2117 | }; |
2118 | ||
2119 | test_precopy_common(&args); | |
2120 | } | |
2121 | ||
3dc35470 FR |
2122 | static void test_precopy_file(void) |
2123 | { | |
2124 | g_autofree char *uri = g_strdup_printf("file:%s/%s", tmpfs, | |
2125 | FILE_TEST_FILENAME); | |
2126 | MigrateCommon args = { | |
2127 | .connect_uri = uri, | |
2128 | .listen_uri = "defer", | |
2129 | }; | |
2130 | ||
2131 | test_file_common(&args, true); | |
2132 | } | |
2133 | ||
2134 | static void file_offset_finish_hook(QTestState *from, QTestState *to, | |
2135 | void *opaque) | |
2136 | { | |
2137 | #if defined(__linux__) | |
2138 | g_autofree char *path = g_strdup_printf("%s/%s", tmpfs, FILE_TEST_FILENAME); | |
2139 | size_t size = FILE_TEST_OFFSET + sizeof(QEMU_VM_FILE_MAGIC); | |
2140 | uintptr_t *addr, *p; | |
2141 | int fd; | |
2142 | ||
2143 | fd = open(path, O_RDONLY); | |
2144 | g_assert(fd != -1); | |
2145 | addr = mmap(NULL, size, PROT_READ, MAP_SHARED, fd, 0); | |
2146 | g_assert(addr != MAP_FAILED); | |
2147 | ||
2148 | /* | |
2149 | * Ensure the skipped offset contains zeros and the migration | |
2150 | * stream starts at the right place. | |
2151 | */ | |
2152 | p = addr; | |
2153 | while (p < addr + FILE_TEST_OFFSET / sizeof(uintptr_t)) { | |
2154 | g_assert(*p == 0); | |
2155 | p++; | |
2156 | } | |
2157 | g_assert_cmpint(cpu_to_be64(*p) >> 32, ==, QEMU_VM_FILE_MAGIC); | |
2158 | ||
2159 | munmap(addr, size); | |
2160 | close(fd); | |
2161 | #endif | |
2162 | } | |
2163 | ||
2164 | static void test_precopy_file_offset(void) | |
2165 | { | |
2166 | g_autofree char *uri = g_strdup_printf("file:%s/%s,offset=%d", tmpfs, | |
2167 | FILE_TEST_FILENAME, | |
2168 | FILE_TEST_OFFSET); | |
2169 | MigrateCommon args = { | |
2170 | .connect_uri = uri, | |
2171 | .listen_uri = "defer", | |
2172 | .finish_hook = file_offset_finish_hook, | |
2173 | }; | |
2174 | ||
2175 | test_file_common(&args, false); | |
2176 | } | |
2177 | ||
2178 | static void test_precopy_file_offset_bad(void) | |
2179 | { | |
2180 | /* using a value not supported by qemu_strtosz() */ | |
2181 | g_autofree char *uri = g_strdup_printf("file:%s/%s,offset=0x20M", | |
2182 | tmpfs, FILE_TEST_FILENAME); | |
2183 | MigrateCommon args = { | |
2184 | .connect_uri = uri, | |
2185 | .listen_uri = "defer", | |
2186 | .result = MIG_TEST_QMP_ERROR, | |
2187 | }; | |
2188 | ||
2189 | test_file_common(&args, false); | |
2190 | } | |
2191 | ||
e7b428d6 SS |
2192 | static void *test_mode_reboot_start(QTestState *from, QTestState *to) |
2193 | { | |
2194 | migrate_set_parameter_str(from, "mode", "cpr-reboot"); | |
2195 | migrate_set_parameter_str(to, "mode", "cpr-reboot"); | |
2196 | ||
2197 | migrate_set_capability(from, "x-ignore-shared", true); | |
2198 | migrate_set_capability(to, "x-ignore-shared", true); | |
2199 | ||
2200 | return NULL; | |
2201 | } | |
2202 | ||
2203 | static void test_mode_reboot(void) | |
2204 | { | |
2205 | g_autofree char *uri = g_strdup_printf("file:%s/%s", tmpfs, | |
2206 | FILE_TEST_FILENAME); | |
2207 | MigrateCommon args = { | |
2208 | .start.use_shmem = true, | |
2209 | .connect_uri = uri, | |
2210 | .listen_uri = "defer", | |
2211 | .start_hook = test_mode_reboot_start | |
2212 | }; | |
2213 | ||
2214 | test_file_common(&args, true); | |
2215 | } | |
2216 | ||
58d25e97 | 2217 | static void test_precopy_tcp_plain(void) |
609d3844 | 2218 | { |
ffed54f6 DB |
2219 | MigrateCommon args = { |
2220 | .listen_uri = "tcp:127.0.0.1:0", | |
2221 | }; | |
609d3844 | 2222 | |
ffed54f6 | 2223 | test_precopy_common(&args); |
609d3844 JQ |
2224 | } |
2225 | ||
7e6a5c73 AH |
2226 | static void *test_migrate_switchover_ack_start(QTestState *from, QTestState *to) |
2227 | { | |
2228 | ||
2229 | migrate_set_capability(from, "return-path", true); | |
2230 | migrate_set_capability(to, "return-path", true); | |
2231 | ||
2232 | migrate_set_capability(from, "switchover-ack", true); | |
2233 | migrate_set_capability(to, "switchover-ack", true); | |
2234 | ||
2235 | return NULL; | |
2236 | } | |
2237 | ||
2238 | static void test_precopy_tcp_switchover_ack(void) | |
2239 | { | |
2240 | MigrateCommon args = { | |
2241 | .listen_uri = "tcp:127.0.0.1:0", | |
2242 | .start_hook = test_migrate_switchover_ack_start, | |
2243 | /* | |
2244 | * Source VM must be running in order to consider the switchover ACK | |
2245 | * when deciding to do switchover or not. | |
2246 | */ | |
2247 | .live = true, | |
2248 | }; | |
2249 | ||
2250 | test_precopy_common(&args); | |
2251 | } | |
2252 | ||
58d25e97 DB |
2253 | #ifdef CONFIG_GNUTLS |
2254 | static void test_precopy_tcp_tls_psk_match(void) | |
2255 | { | |
2256 | MigrateCommon args = { | |
2257 | .listen_uri = "tcp:127.0.0.1:0", | |
2258 | .start_hook = test_migrate_tls_psk_start_match, | |
2259 | .finish_hook = test_migrate_tls_psk_finish, | |
2260 | }; | |
2261 | ||
2262 | test_precopy_common(&args); | |
2263 | } | |
2264 | ||
2265 | static void test_precopy_tcp_tls_psk_mismatch(void) | |
2266 | { | |
2267 | MigrateCommon args = { | |
2268 | .start = { | |
2269 | .hide_stderr = true, | |
2270 | }, | |
2271 | .listen_uri = "tcp:127.0.0.1:0", | |
2272 | .start_hook = test_migrate_tls_psk_start_mismatch, | |
2273 | .finish_hook = test_migrate_tls_psk_finish, | |
2274 | .result = MIG_TEST_FAIL, | |
2275 | }; | |
2276 | ||
2277 | test_precopy_common(&args); | |
2278 | } | |
d47b83b1 DB |
2279 | |
2280 | #ifdef CONFIG_TASN1 | |
2281 | static void test_precopy_tcp_tls_x509_default_host(void) | |
2282 | { | |
2283 | MigrateCommon args = { | |
2284 | .listen_uri = "tcp:127.0.0.1:0", | |
2285 | .start_hook = test_migrate_tls_x509_start_default_host, | |
2286 | .finish_hook = test_migrate_tls_x509_finish, | |
2287 | }; | |
2288 | ||
2289 | test_precopy_common(&args); | |
2290 | } | |
2291 | ||
2292 | static void test_precopy_tcp_tls_x509_override_host(void) | |
2293 | { | |
2294 | MigrateCommon args = { | |
2295 | .listen_uri = "tcp:127.0.0.1:0", | |
2296 | .start_hook = test_migrate_tls_x509_start_override_host, | |
2297 | .finish_hook = test_migrate_tls_x509_finish, | |
2298 | }; | |
2299 | ||
2300 | test_precopy_common(&args); | |
2301 | } | |
2302 | ||
2303 | static void test_precopy_tcp_tls_x509_mismatch_host(void) | |
2304 | { | |
2305 | MigrateCommon args = { | |
2306 | .start = { | |
2307 | .hide_stderr = true, | |
2308 | }, | |
2309 | .listen_uri = "tcp:127.0.0.1:0", | |
2310 | .start_hook = test_migrate_tls_x509_start_mismatch_host, | |
2311 | .finish_hook = test_migrate_tls_x509_finish, | |
2312 | .result = MIG_TEST_FAIL_DEST_QUIT_ERR, | |
2313 | }; | |
2314 | ||
2315 | test_precopy_common(&args); | |
2316 | } | |
2317 | ||
2318 | static void test_precopy_tcp_tls_x509_friendly_client(void) | |
2319 | { | |
2320 | MigrateCommon args = { | |
2321 | .listen_uri = "tcp:127.0.0.1:0", | |
2322 | .start_hook = test_migrate_tls_x509_start_friendly_client, | |
2323 | .finish_hook = test_migrate_tls_x509_finish, | |
2324 | }; | |
2325 | ||
2326 | test_precopy_common(&args); | |
2327 | } | |
2328 | ||
2329 | static void test_precopy_tcp_tls_x509_hostile_client(void) | |
2330 | { | |
2331 | MigrateCommon args = { | |
2332 | .start = { | |
2333 | .hide_stderr = true, | |
2334 | }, | |
2335 | .listen_uri = "tcp:127.0.0.1:0", | |
2336 | .start_hook = test_migrate_tls_x509_start_hostile_client, | |
2337 | .finish_hook = test_migrate_tls_x509_finish, | |
2338 | .result = MIG_TEST_FAIL, | |
2339 | }; | |
2340 | ||
2341 | test_precopy_common(&args); | |
2342 | } | |
2343 | ||
2344 | static void test_precopy_tcp_tls_x509_allow_anon_client(void) | |
2345 | { | |
2346 | MigrateCommon args = { | |
2347 | .listen_uri = "tcp:127.0.0.1:0", | |
2348 | .start_hook = test_migrate_tls_x509_start_allow_anon_client, | |
2349 | .finish_hook = test_migrate_tls_x509_finish, | |
2350 | }; | |
2351 | ||
2352 | test_precopy_common(&args); | |
2353 | } | |
2354 | ||
2355 | static void test_precopy_tcp_tls_x509_reject_anon_client(void) | |
2356 | { | |
2357 | MigrateCommon args = { | |
2358 | .start = { | |
2359 | .hide_stderr = true, | |
2360 | }, | |
2361 | .listen_uri = "tcp:127.0.0.1:0", | |
2362 | .start_hook = test_migrate_tls_x509_start_reject_anon_client, | |
2363 | .finish_hook = test_migrate_tls_x509_finish, | |
2364 | .result = MIG_TEST_FAIL, | |
2365 | }; | |
2366 | ||
2367 | test_precopy_common(&args); | |
2368 | } | |
2369 | #endif /* CONFIG_TASN1 */ | |
58d25e97 DB |
2370 | #endif /* CONFIG_GNUTLS */ |
2371 | ||
d7613ee2 | 2372 | #ifndef _WIN32 |
243e0066 DB |
2373 | static void *test_migrate_fd_start_hook(QTestState *from, |
2374 | QTestState *to) | |
24d5588c | 2375 | { |
24d5588c YK |
2376 | int ret; |
2377 | int pair[2]; | |
24d5588c YK |
2378 | |
2379 | /* Create two connected sockets for migration */ | |
0038e9a2 | 2380 | ret = qemu_socketpair(PF_LOCAL, SOCK_STREAM, 0, pair); |
24d5588c YK |
2381 | g_assert_cmpint(ret, ==, 0); |
2382 | ||
2383 | /* Send the 1st socket to the target */ | |
aca04069 DB |
2384 | qtest_qmp_fds_assert_success(to, &pair[0], 1, |
2385 | "{ 'execute': 'getfd'," | |
2386 | " 'arguments': { 'fdname': 'fd-mig' }}"); | |
24d5588c YK |
2387 | close(pair[0]); |
2388 | ||
2389 | /* Start incoming migration from the 1st socket */ | |
6830e53b | 2390 | migrate_incoming_qmp(to, "fd:fd-mig", "{}"); |
24d5588c YK |
2391 | |
2392 | /* Send the 2nd socket to the target */ | |
aca04069 DB |
2393 | qtest_qmp_fds_assert_success(from, &pair[1], 1, |
2394 | "{ 'execute': 'getfd'," | |
2395 | " 'arguments': { 'fdname': 'fd-mig' }}"); | |
24d5588c YK |
2396 | close(pair[1]); |
2397 | ||
243e0066 DB |
2398 | return NULL; |
2399 | } | |
24d5588c | 2400 | |
243e0066 DB |
2401 | static void test_migrate_fd_finish_hook(QTestState *from, |
2402 | QTestState *to, | |
2403 | void *opaque) | |
2404 | { | |
2405 | QDict *rsp; | |
2406 | const char *error_desc; | |
24d5588c YK |
2407 | |
2408 | /* Test closing fds */ | |
2409 | /* We assume, that QEMU removes named fd from its list, | |
2410 | * so this should fail */ | |
2411 | rsp = qtest_qmp(from, "{ 'execute': 'closefd'," | |
2412 | " 'arguments': { 'fdname': 'fd-mig' }}"); | |
2413 | g_assert_true(qdict_haskey(rsp, "error")); | |
2414 | error_desc = qdict_get_str(qdict_get_qdict(rsp, "error"), "desc"); | |
2415 | g_assert_cmpstr(error_desc, ==, "File descriptor named 'fd-mig' not found"); | |
2416 | qobject_unref(rsp); | |
2417 | ||
2418 | rsp = qtest_qmp(to, "{ 'execute': 'closefd'," | |
2419 | " 'arguments': { 'fdname': 'fd-mig' }}"); | |
2420 | g_assert_true(qdict_haskey(rsp, "error")); | |
2421 | error_desc = qdict_get_str(qdict_get_qdict(rsp, "error"), "desc"); | |
2422 | g_assert_cmpstr(error_desc, ==, "File descriptor named 'fd-mig' not found"); | |
2423 | qobject_unref(rsp); | |
243e0066 | 2424 | } |
24d5588c | 2425 | |
243e0066 DB |
2426 | static void test_migrate_fd_proto(void) |
2427 | { | |
2428 | MigrateCommon args = { | |
2429 | .listen_uri = "defer", | |
2430 | .connect_uri = "fd:fd-mig", | |
2431 | .start_hook = test_migrate_fd_start_hook, | |
2432 | .finish_hook = test_migrate_fd_finish_hook | |
2433 | }; | |
2434 | test_precopy_common(&args); | |
24d5588c | 2435 | } |
d7613ee2 | 2436 | #endif /* _WIN32 */ |
24d5588c | 2437 | |
5d3b575d | 2438 | static void do_test_validate_uuid(MigrateStart *args, bool should_fail) |
3af31a34 | 2439 | { |
ff7b9b56 | 2440 | g_autofree char *uri = g_strdup_printf("unix:%s/migsocket", tmpfs); |
3af31a34 YK |
2441 | QTestState *from, *to; |
2442 | ||
19da6edf | 2443 | if (test_migrate_start(&from, &to, uri, args)) { |
3af31a34 YK |
2444 | return; |
2445 | } | |
2446 | ||
2447 | /* | |
2448 | * UUID validation is at the begin of migration. So, the main process of | |
2449 | * migration is not interesting for us here. Thus, set huge downtime for | |
2450 | * very fast migration. | |
2451 | */ | |
2452 | migrate_set_parameter_int(from, "downtime-limit", 1000000); | |
2453 | migrate_set_capability(from, "validate-uuid", true); | |
2454 | ||
2455 | /* Wait for the first serial output from the source */ | |
2456 | wait_for_serial("src_serial"); | |
2457 | ||
d77799cc | 2458 | migrate_qmp(from, uri, "{}"); |
3af31a34 YK |
2459 | |
2460 | if (should_fail) { | |
1b0f1b14 | 2461 | qtest_set_expected_status(to, EXIT_FAILURE); |
3af31a34 YK |
2462 | wait_for_migration_fail(from, true); |
2463 | } else { | |
2464 | wait_for_migration_complete(from); | |
2465 | } | |
2466 | ||
2467 | test_migrate_end(from, to, false); | |
3af31a34 YK |
2468 | } |
2469 | ||
2470 | static void test_validate_uuid(void) | |
2471 | { | |
19da6edf DB |
2472 | MigrateStart args = { |
2473 | .opts_source = "-uuid 11111111-1111-1111-1111-111111111111", | |
2474 | .opts_target = "-uuid 11111111-1111-1111-1111-111111111111", | |
2475 | }; | |
5d3b575d | 2476 | |
19da6edf | 2477 | do_test_validate_uuid(&args, false); |
3af31a34 YK |
2478 | } |
2479 | ||
2480 | static void test_validate_uuid_error(void) | |
2481 | { | |
19da6edf DB |
2482 | MigrateStart args = { |
2483 | .opts_source = "-uuid 11111111-1111-1111-1111-111111111111", | |
2484 | .opts_target = "-uuid 22222222-2222-2222-2222-222222222222", | |
2485 | .hide_stderr = true, | |
2486 | }; | |
2487 | ||
2488 | do_test_validate_uuid(&args, true); | |
3af31a34 YK |
2489 | } |
2490 | ||
2491 | static void test_validate_uuid_src_not_set(void) | |
2492 | { | |
19da6edf DB |
2493 | MigrateStart args = { |
2494 | .opts_target = "-uuid 22222222-2222-2222-2222-222222222222", | |
2495 | .hide_stderr = true, | |
2496 | }; | |
5d3b575d | 2497 | |
19da6edf | 2498 | do_test_validate_uuid(&args, false); |
3af31a34 YK |
2499 | } |
2500 | ||
2501 | static void test_validate_uuid_dst_not_set(void) | |
2502 | { | |
19da6edf DB |
2503 | MigrateStart args = { |
2504 | .opts_source = "-uuid 11111111-1111-1111-1111-111111111111", | |
2505 | .hide_stderr = true, | |
2506 | }; | |
5d3b575d | 2507 | |
19da6edf | 2508 | do_test_validate_uuid(&args, false); |
3af31a34 YK |
2509 | } |
2510 | ||
74902af7 JQ |
2511 | /* |
2512 | * The way auto_converge works, we need to do too many passes to | |
2513 | * run this test. Auto_converge logic is only run once every | |
2514 | * three iterations, so: | |
2515 | * | |
2516 | * - 3 iterations without auto_converge enabled | |
2517 | * - 3 iterations with pct = 5 | |
2518 | * - 3 iterations with pct = 30 | |
2519 | * - 3 iterations with pct = 55 | |
2520 | * - 3 iterations with pct = 80 | |
2521 | * - 3 iterations with pct = 95 (max(95, 80 + 25)) | |
2522 | * | |
2523 | * To make things even worse, we need to run the initial stage at | |
2524 | * 3MB/s so we enter autoconverge even when host is (over)loaded. | |
2525 | */ | |
8c51642b YK |
2526 | static void test_migrate_auto_converge(void) |
2527 | { | |
ff7b9b56 | 2528 | g_autofree char *uri = g_strdup_printf("unix:%s/migsocket", tmpfs); |
19da6edf | 2529 | MigrateStart args = {}; |
8c51642b | 2530 | QTestState *from, *to; |
219044b8 | 2531 | int64_t percentage; |
8c51642b YK |
2532 | |
2533 | /* | |
2534 | * We want the test to be stable and as fast as possible. | |
96420a30 | 2535 | * E.g., with 1Gb/s bandwidth migration may pass without throttling, |
8c51642b YK |
2536 | * so we need to decrease a bandwidth. |
2537 | */ | |
1bfc8dde | 2538 | const int64_t init_pct = 5, inc_pct = 25, max_pct = 95; |
8c51642b | 2539 | |
3ff57401 | 2540 | if (test_migrate_start(&from, &to, uri, &args)) { |
8c51642b YK |
2541 | return; |
2542 | } | |
2543 | ||
2544 | migrate_set_capability(from, "auto-converge", true); | |
2545 | migrate_set_parameter_int(from, "cpu-throttle-initial", init_pct); | |
2546 | migrate_set_parameter_int(from, "cpu-throttle-increment", inc_pct); | |
2547 | migrate_set_parameter_int(from, "max-cpu-throttle", max_pct); | |
2548 | ||
2549 | /* | |
2550 | * Set the initial parameters so that the migration could not converge | |
2551 | * without throttling. | |
2552 | */ | |
886dfe9d | 2553 | migrate_ensure_non_converge(from); |
8c51642b YK |
2554 | |
2555 | /* To check remaining size after precopy */ | |
2556 | migrate_set_capability(from, "pause-before-switchover", true); | |
2557 | ||
2558 | /* Wait for the first serial output from the source */ | |
2559 | wait_for_serial("src_serial"); | |
2560 | ||
d77799cc | 2561 | migrate_qmp(from, uri, "{}"); |
8c51642b YK |
2562 | |
2563 | /* Wait for throttling begins */ | |
2564 | percentage = 0; | |
1bfc8dde | 2565 | do { |
8c51642b | 2566 | percentage = read_migrate_property_int(from, "cpu-throttle-percentage"); |
1bfc8dde DDAG |
2567 | if (percentage != 0) { |
2568 | break; | |
2569 | } | |
2570 | usleep(20); | |
f0649758 | 2571 | g_assert_false(src_state.stop_seen); |
1bfc8dde DDAG |
2572 | } while (true); |
2573 | /* The first percentage of throttling should be at least init_pct */ | |
2574 | g_assert_cmpint(percentage, >=, init_pct); | |
8c51642b | 2575 | /* Now, when we tested that throttling works, let it converge */ |
219044b8 | 2576 | migrate_ensure_converge(from); |
8c51642b YK |
2577 | |
2578 | /* | |
2579 | * Wait for pre-switchover status to check last throttle percentage | |
2580 | * and remaining. These values will be zeroed later | |
2581 | */ | |
2582 | wait_for_migration_status(from, "pre-switchover", NULL); | |
2583 | ||
2584 | /* The final percentage of throttling shouldn't be greater than max_pct */ | |
2585 | percentage = read_migrate_property_int(from, "cpu-throttle-percentage"); | |
2586 | g_assert_cmpint(percentage, <=, max_pct); | |
8c51642b YK |
2587 | migrate_continue(from, "pre-switchover"); |
2588 | ||
2589 | qtest_qmp_eventwait(to, "RESUME"); | |
2590 | ||
2591 | wait_for_serial("dest_serial"); | |
2592 | wait_for_migration_complete(from); | |
2593 | ||
8c51642b YK |
2594 | test_migrate_end(from, to, true); |
2595 | } | |
2596 | ||
490facff DB |
2597 | static void * |
2598 | test_migrate_precopy_tcp_multifd_start_common(QTestState *from, | |
2599 | QTestState *to, | |
2600 | const char *method) | |
b99784ef | 2601 | { |
b99784ef JQ |
2602 | migrate_set_parameter_int(from, "multifd-channels", 16); |
2603 | migrate_set_parameter_int(to, "multifd-channels", 16); | |
2604 | ||
96eef042 JQ |
2605 | migrate_set_parameter_str(from, "multifd-compression", method); |
2606 | migrate_set_parameter_str(to, "multifd-compression", method); | |
2607 | ||
a1209bb7 DDAG |
2608 | migrate_set_capability(from, "multifd", true); |
2609 | migrate_set_capability(to, "multifd", true); | |
b99784ef JQ |
2610 | |
2611 | /* Start incoming migration from the 1st socket */ | |
6830e53b | 2612 | migrate_incoming_qmp(to, "tcp:127.0.0.1:0", "{}"); |
b99784ef | 2613 | |
490facff DB |
2614 | return NULL; |
2615 | } | |
b99784ef | 2616 | |
490facff DB |
2617 | static void * |
2618 | test_migrate_precopy_tcp_multifd_start(QTestState *from, | |
2619 | QTestState *to) | |
2620 | { | |
2621 | return test_migrate_precopy_tcp_multifd_start_common(from, to, "none"); | |
2622 | } | |
b99784ef | 2623 | |
490facff DB |
2624 | static void * |
2625 | test_migrate_precopy_tcp_multifd_zlib_start(QTestState *from, | |
2626 | QTestState *to) | |
2627 | { | |
2628 | return test_migrate_precopy_tcp_multifd_start_common(from, to, "zlib"); | |
2629 | } | |
b99784ef | 2630 | |
490facff DB |
2631 | #ifdef CONFIG_ZSTD |
2632 | static void * | |
2633 | test_migrate_precopy_tcp_multifd_zstd_start(QTestState *from, | |
2634 | QTestState *to) | |
2635 | { | |
2636 | return test_migrate_precopy_tcp_multifd_start_common(from, to, "zstd"); | |
b99784ef | 2637 | } |
490facff | 2638 | #endif /* CONFIG_ZSTD */ |
b99784ef | 2639 | |
96eef042 JQ |
2640 | static void test_multifd_tcp_none(void) |
2641 | { | |
490facff DB |
2642 | MigrateCommon args = { |
2643 | .listen_uri = "defer", | |
2644 | .start_hook = test_migrate_precopy_tcp_multifd_start, | |
b861383c PX |
2645 | /* |
2646 | * Multifd is more complicated than most of the features, it | |
2647 | * directly takes guest page buffers when sending, make sure | |
2648 | * everything will work alright even if guest page is changing. | |
2649 | */ | |
3c4fb177 | 2650 | .live = true, |
490facff DB |
2651 | }; |
2652 | test_precopy_common(&args); | |
96eef042 JQ |
2653 | } |
2654 | ||
7ec2c2b3 JQ |
2655 | static void test_multifd_tcp_zlib(void) |
2656 | { | |
490facff DB |
2657 | MigrateCommon args = { |
2658 | .listen_uri = "defer", | |
2659 | .start_hook = test_migrate_precopy_tcp_multifd_zlib_start, | |
2660 | }; | |
2661 | test_precopy_common(&args); | |
7ec2c2b3 JQ |
2662 | } |
2663 | ||
87dc6f5f JQ |
2664 | #ifdef CONFIG_ZSTD |
2665 | static void test_multifd_tcp_zstd(void) | |
2666 | { | |
490facff DB |
2667 | MigrateCommon args = { |
2668 | .listen_uri = "defer", | |
2669 | .start_hook = test_migrate_precopy_tcp_multifd_zstd_start, | |
2670 | }; | |
2671 | test_precopy_common(&args); | |
87dc6f5f JQ |
2672 | } |
2673 | #endif | |
2674 | ||
4d6d2e87 DB |
2675 | #ifdef CONFIG_GNUTLS |
2676 | static void * | |
2677 | test_migrate_multifd_tcp_tls_psk_start_match(QTestState *from, | |
2678 | QTestState *to) | |
2679 | { | |
2680 | test_migrate_precopy_tcp_multifd_start_common(from, to, "none"); | |
2681 | return test_migrate_tls_psk_start_match(from, to); | |
2682 | } | |
2683 | ||
2684 | static void * | |
2685 | test_migrate_multifd_tcp_tls_psk_start_mismatch(QTestState *from, | |
2686 | QTestState *to) | |
2687 | { | |
2688 | test_migrate_precopy_tcp_multifd_start_common(from, to, "none"); | |
2689 | return test_migrate_tls_psk_start_mismatch(from, to); | |
2690 | } | |
2691 | ||
ff32f1dd DB |
2692 | #ifdef CONFIG_TASN1 |
2693 | static void * | |
2694 | test_migrate_multifd_tls_x509_start_default_host(QTestState *from, | |
2695 | QTestState *to) | |
2696 | { | |
2697 | test_migrate_precopy_tcp_multifd_start_common(from, to, "none"); | |
2698 | return test_migrate_tls_x509_start_default_host(from, to); | |
2699 | } | |
2700 | ||
2701 | static void * | |
2702 | test_migrate_multifd_tls_x509_start_override_host(QTestState *from, | |
2703 | QTestState *to) | |
2704 | { | |
2705 | test_migrate_precopy_tcp_multifd_start_common(from, to, "none"); | |
2706 | return test_migrate_tls_x509_start_override_host(from, to); | |
2707 | } | |
2708 | ||
2709 | static void * | |
2710 | test_migrate_multifd_tls_x509_start_mismatch_host(QTestState *from, | |
2711 | QTestState *to) | |
2712 | { | |
2713 | test_migrate_precopy_tcp_multifd_start_common(from, to, "none"); | |
2714 | return test_migrate_tls_x509_start_mismatch_host(from, to); | |
2715 | } | |
2716 | ||
2717 | static void * | |
2718 | test_migrate_multifd_tls_x509_start_allow_anon_client(QTestState *from, | |
2719 | QTestState *to) | |
2720 | { | |
2721 | test_migrate_precopy_tcp_multifd_start_common(from, to, "none"); | |
2722 | return test_migrate_tls_x509_start_allow_anon_client(from, to); | |
2723 | } | |
2724 | ||
2725 | static void * | |
2726 | test_migrate_multifd_tls_x509_start_reject_anon_client(QTestState *from, | |
2727 | QTestState *to) | |
2728 | { | |
2729 | test_migrate_precopy_tcp_multifd_start_common(from, to, "none"); | |
2730 | return test_migrate_tls_x509_start_reject_anon_client(from, to); | |
2731 | } | |
2732 | #endif /* CONFIG_TASN1 */ | |
2733 | ||
4d6d2e87 DB |
2734 | static void test_multifd_tcp_tls_psk_match(void) |
2735 | { | |
2736 | MigrateCommon args = { | |
2737 | .listen_uri = "defer", | |
2738 | .start_hook = test_migrate_multifd_tcp_tls_psk_start_match, | |
2739 | .finish_hook = test_migrate_tls_psk_finish, | |
2740 | }; | |
2741 | test_precopy_common(&args); | |
2742 | } | |
2743 | ||
2744 | static void test_multifd_tcp_tls_psk_mismatch(void) | |
2745 | { | |
2746 | MigrateCommon args = { | |
2747 | .start = { | |
2748 | .hide_stderr = true, | |
2749 | }, | |
2750 | .listen_uri = "defer", | |
2751 | .start_hook = test_migrate_multifd_tcp_tls_psk_start_mismatch, | |
2752 | .finish_hook = test_migrate_tls_psk_finish, | |
2753 | .result = MIG_TEST_FAIL, | |
2754 | }; | |
2755 | test_precopy_common(&args); | |
2756 | } | |
ff32f1dd DB |
2757 | |
2758 | #ifdef CONFIG_TASN1 | |
2759 | static void test_multifd_tcp_tls_x509_default_host(void) | |
2760 | { | |
2761 | MigrateCommon args = { | |
2762 | .listen_uri = "defer", | |
2763 | .start_hook = test_migrate_multifd_tls_x509_start_default_host, | |
2764 | .finish_hook = test_migrate_tls_x509_finish, | |
2765 | }; | |
2766 | test_precopy_common(&args); | |
2767 | } | |
2768 | ||
2769 | static void test_multifd_tcp_tls_x509_override_host(void) | |
2770 | { | |
2771 | MigrateCommon args = { | |
2772 | .listen_uri = "defer", | |
2773 | .start_hook = test_migrate_multifd_tls_x509_start_override_host, | |
2774 | .finish_hook = test_migrate_tls_x509_finish, | |
2775 | }; | |
2776 | test_precopy_common(&args); | |
2777 | } | |
2778 | ||
2779 | static void test_multifd_tcp_tls_x509_mismatch_host(void) | |
2780 | { | |
2781 | /* | |
2782 | * This has different behaviour to the non-multifd case. | |
2783 | * | |
2784 | * In non-multifd case when client aborts due to mismatched | |
2785 | * cert host, the server has already started trying to load | |
2786 | * migration state, and so it exits with I/O failure. | |
2787 | * | |
2788 | * In multifd case when client aborts due to mismatched | |
2789 | * cert host, the server is still waiting for the other | |
2790 | * multifd connections to arrive so hasn't started trying | |
2791 | * to load migration state, and thus just aborts the migration | |
2792 | * without exiting. | |
2793 | */ | |
2794 | MigrateCommon args = { | |
2795 | .start = { | |
2796 | .hide_stderr = true, | |
2797 | }, | |
2798 | .listen_uri = "defer", | |
2799 | .start_hook = test_migrate_multifd_tls_x509_start_mismatch_host, | |
2800 | .finish_hook = test_migrate_tls_x509_finish, | |
2801 | .result = MIG_TEST_FAIL, | |
2802 | }; | |
2803 | test_precopy_common(&args); | |
2804 | } | |
2805 | ||
2806 | static void test_multifd_tcp_tls_x509_allow_anon_client(void) | |
2807 | { | |
2808 | MigrateCommon args = { | |
2809 | .listen_uri = "defer", | |
2810 | .start_hook = test_migrate_multifd_tls_x509_start_allow_anon_client, | |
2811 | .finish_hook = test_migrate_tls_x509_finish, | |
2812 | }; | |
2813 | test_precopy_common(&args); | |
2814 | } | |
2815 | ||
2816 | static void test_multifd_tcp_tls_x509_reject_anon_client(void) | |
2817 | { | |
2818 | MigrateCommon args = { | |
2819 | .start = { | |
2820 | .hide_stderr = true, | |
2821 | }, | |
2822 | .listen_uri = "defer", | |
2823 | .start_hook = test_migrate_multifd_tls_x509_start_reject_anon_client, | |
2824 | .finish_hook = test_migrate_tls_x509_finish, | |
2825 | .result = MIG_TEST_FAIL, | |
2826 | }; | |
2827 | test_precopy_common(&args); | |
2828 | } | |
2829 | #endif /* CONFIG_TASN1 */ | |
4d6d2e87 DB |
2830 | #endif /* CONFIG_GNUTLS */ |
2831 | ||
d795f474 JQ |
2832 | /* |
2833 | * This test does: | |
2834 | * source target | |
2835 | * migrate_incoming | |
2836 | * migrate | |
2837 | * migrate_cancel | |
2838 | * launch another target | |
2839 | * migrate | |
2840 | * | |
2841 | * And see that it works | |
2842 | */ | |
d795f474 JQ |
2843 | static void test_multifd_tcp_cancel(void) |
2844 | { | |
19da6edf DB |
2845 | MigrateStart args = { |
2846 | .hide_stderr = true, | |
2847 | }; | |
d795f474 | 2848 | QTestState *from, *to, *to2; |
ff7b9b56 | 2849 | g_autofree char *uri = NULL; |
d795f474 | 2850 | |
3ff57401 | 2851 | if (test_migrate_start(&from, &to, "defer", &args)) { |
d795f474 JQ |
2852 | return; |
2853 | } | |
2854 | ||
886dfe9d | 2855 | migrate_ensure_non_converge(from); |
e02f56e3 | 2856 | migrate_prepare_for_dirty_mem(from); |
d795f474 JQ |
2857 | |
2858 | migrate_set_parameter_int(from, "multifd-channels", 16); | |
2859 | migrate_set_parameter_int(to, "multifd-channels", 16); | |
2860 | ||
a1209bb7 DDAG |
2861 | migrate_set_capability(from, "multifd", true); |
2862 | migrate_set_capability(to, "multifd", true); | |
d795f474 JQ |
2863 | |
2864 | /* Start incoming migration from the 1st socket */ | |
6830e53b | 2865 | migrate_incoming_qmp(to, "tcp:127.0.0.1:0", "{}"); |
d795f474 JQ |
2866 | |
2867 | /* Wait for the first serial output from the source */ | |
2868 | wait_for_serial("src_serial"); | |
2869 | ||
2870 | uri = migrate_get_socket_address(to, "socket-address"); | |
2871 | ||
2872 | migrate_qmp(from, uri, "{}"); | |
2873 | ||
e02f56e3 | 2874 | migrate_wait_for_dirty_mem(from, to); |
d795f474 JQ |
2875 | |
2876 | migrate_cancel(from); | |
2877 | ||
f2d063e6 XC |
2878 | /* Make sure QEMU process "to" exited */ |
2879 | qtest_set_expected_status(to, EXIT_FAILURE); | |
2880 | qtest_wait_qemu(to); | |
2881 | ||
19da6edf DB |
2882 | args = (MigrateStart){ |
2883 | .only_target = true, | |
2884 | }; | |
d795f474 | 2885 | |
3ff57401 | 2886 | if (test_migrate_start(&from, &to2, "defer", &args)) { |
d795f474 JQ |
2887 | return; |
2888 | } | |
2889 | ||
2890 | migrate_set_parameter_int(to2, "multifd-channels", 16); | |
2891 | ||
a1209bb7 | 2892 | migrate_set_capability(to2, "multifd", true); |
d795f474 JQ |
2893 | |
2894 | /* Start incoming migration from the 1st socket */ | |
6830e53b | 2895 | migrate_incoming_qmp(to2, "tcp:127.0.0.1:0", "{}"); |
d795f474 | 2896 | |
e022d473 | 2897 | g_free(uri); |
d795f474 JQ |
2898 | uri = migrate_get_socket_address(to2, "socket-address"); |
2899 | ||
2900 | wait_for_migration_status(from, "cancelled", NULL); | |
2901 | ||
e02f56e3 | 2902 | migrate_ensure_non_converge(from); |
d795f474 JQ |
2903 | |
2904 | migrate_qmp(from, uri, "{}"); | |
2905 | ||
94aaf6d8 | 2906 | migrate_wait_for_dirty_mem(from, to2); |
e02f56e3 DB |
2907 | |
2908 | migrate_ensure_converge(from); | |
d795f474 | 2909 | |
f0649758 | 2910 | wait_for_stop(from, &src_state); |
d795f474 JQ |
2911 | qtest_qmp_eventwait(to2, "RESUME"); |
2912 | ||
2913 | wait_for_serial("dest_serial"); | |
2914 | wait_for_migration_complete(from); | |
2915 | test_migrate_end(from, to2, true); | |
d795f474 JQ |
2916 | } |
2917 | ||
8aff6f50 HH |
2918 | static void calc_dirty_rate(QTestState *who, uint64_t calc_time) |
2919 | { | |
ffd47275 DB |
2920 | qtest_qmp_assert_success(who, |
2921 | "{ 'execute': 'calc-dirty-rate'," | |
2922 | "'arguments': { " | |
2923 | "'calc-time': %" PRIu64 "," | |
2924 | "'mode': 'dirty-ring' }}", | |
2925 | calc_time); | |
8aff6f50 HH |
2926 | } |
2927 | ||
2928 | static QDict *query_dirty_rate(QTestState *who) | |
2929 | { | |
ffd47275 DB |
2930 | return qtest_qmp_assert_success_ref(who, |
2931 | "{ 'execute': 'query-dirty-rate' }"); | |
8aff6f50 HH |
2932 | } |
2933 | ||
2934 | static void dirtylimit_set_all(QTestState *who, uint64_t dirtyrate) | |
2935 | { | |
ffd47275 DB |
2936 | qtest_qmp_assert_success(who, |
2937 | "{ 'execute': 'set-vcpu-dirty-limit'," | |
2938 | "'arguments': { " | |
2939 | "'dirty-rate': %" PRIu64 " } }", | |
2940 | dirtyrate); | |
8aff6f50 HH |
2941 | } |
2942 | ||
2943 | static void cancel_vcpu_dirty_limit(QTestState *who) | |
2944 | { | |
ffd47275 DB |
2945 | qtest_qmp_assert_success(who, |
2946 | "{ 'execute': 'cancel-vcpu-dirty-limit' }"); | |
8aff6f50 HH |
2947 | } |
2948 | ||
2949 | static QDict *query_vcpu_dirty_limit(QTestState *who) | |
2950 | { | |
2951 | QDict *rsp; | |
2952 | ||
2953 | rsp = qtest_qmp(who, "{ 'execute': 'query-vcpu-dirty-limit' }"); | |
2954 | g_assert(!qdict_haskey(rsp, "error")); | |
2955 | g_assert(qdict_haskey(rsp, "return")); | |
2956 | ||
2957 | return rsp; | |
2958 | } | |
2959 | ||
2960 | static bool calc_dirtyrate_ready(QTestState *who) | |
2961 | { | |
2962 | QDict *rsp_return; | |
2963 | gchar *status; | |
2964 | ||
2965 | rsp_return = query_dirty_rate(who); | |
2966 | g_assert(rsp_return); | |
2967 | ||
2968 | status = g_strdup(qdict_get_str(rsp_return, "status")); | |
2969 | g_assert(status); | |
2970 | ||
2971 | return g_strcmp0(status, "measuring"); | |
2972 | } | |
2973 | ||
2974 | static void wait_for_calc_dirtyrate_complete(QTestState *who, | |
2975 | int64_t time_s) | |
2976 | { | |
2977 | int max_try_count = 10000; | |
2978 | usleep(time_s * 1000000); | |
2979 | ||
2980 | while (!calc_dirtyrate_ready(who) && max_try_count--) { | |
2981 | usleep(1000); | |
2982 | } | |
2983 | ||
2984 | /* | |
2985 | * Set the timeout with 10 s(max_try_count * 1000us), | |
2986 | * if dirtyrate measurement not complete, fail test. | |
2987 | */ | |
2988 | g_assert_cmpint(max_try_count, !=, 0); | |
2989 | } | |
2990 | ||
2991 | static int64_t get_dirty_rate(QTestState *who) | |
2992 | { | |
2993 | QDict *rsp_return; | |
2994 | gchar *status; | |
2995 | QList *rates; | |
2996 | const QListEntry *entry; | |
2997 | QDict *rate; | |
2998 | int64_t dirtyrate; | |
2999 | ||
3000 | rsp_return = query_dirty_rate(who); | |
3001 | g_assert(rsp_return); | |
3002 | ||
3003 | status = g_strdup(qdict_get_str(rsp_return, "status")); | |
3004 | g_assert(status); | |
3005 | g_assert_cmpstr(status, ==, "measured"); | |
3006 | ||
3007 | rates = qdict_get_qlist(rsp_return, "vcpu-dirty-rate"); | |
3008 | g_assert(rates && !qlist_empty(rates)); | |
3009 | ||
3010 | entry = qlist_first(rates); | |
3011 | g_assert(entry); | |
3012 | ||
3013 | rate = qobject_to(QDict, qlist_entry_obj(entry)); | |
3014 | g_assert(rate); | |
3015 | ||
3016 | dirtyrate = qdict_get_try_int(rate, "dirty-rate", -1); | |
3017 | ||
3018 | qobject_unref(rsp_return); | |
3019 | return dirtyrate; | |
3020 | } | |
3021 | ||
3022 | static int64_t get_limit_rate(QTestState *who) | |
3023 | { | |
3024 | QDict *rsp_return; | |
3025 | QList *rates; | |
3026 | const QListEntry *entry; | |
3027 | QDict *rate; | |
3028 | int64_t dirtyrate; | |
3029 | ||
3030 | rsp_return = query_vcpu_dirty_limit(who); | |
3031 | g_assert(rsp_return); | |
3032 | ||
3033 | rates = qdict_get_qlist(rsp_return, "return"); | |
3034 | g_assert(rates && !qlist_empty(rates)); | |
3035 | ||
3036 | entry = qlist_first(rates); | |
3037 | g_assert(entry); | |
3038 | ||
3039 | rate = qobject_to(QDict, qlist_entry_obj(entry)); | |
3040 | g_assert(rate); | |
3041 | ||
3042 | dirtyrate = qdict_get_try_int(rate, "limit-rate", -1); | |
3043 | ||
3044 | qobject_unref(rsp_return); | |
3045 | return dirtyrate; | |
3046 | } | |
3047 | ||
3048 | static QTestState *dirtylimit_start_vm(void) | |
3049 | { | |
3050 | QTestState *vm = NULL; | |
5014478e SS |
3051 | g_autofree gchar *cmd = NULL; |
3052 | ||
3053 | bootfile_create(tmpfs, false); | |
8aff6f50 HH |
3054 | cmd = g_strdup_printf("-accel kvm,dirty-ring-size=4096 " |
3055 | "-name dirtylimit-test,debug-threads=on " | |
3056 | "-m 150M -smp 1 " | |
3057 | "-serial file:%s/vm_serial " | |
3058 | "-drive file=%s,format=raw ", | |
3059 | tmpfs, bootpath); | |
3060 | ||
3061 | vm = qtest_init(cmd); | |
3062 | return vm; | |
3063 | } | |
3064 | ||
3065 | static void dirtylimit_stop_vm(QTestState *vm) | |
3066 | { | |
3067 | qtest_quit(vm); | |
8aff6f50 HH |
3068 | cleanup("vm_serial"); |
3069 | } | |
3070 | ||
3071 | static void test_vcpu_dirty_limit(void) | |
3072 | { | |
3073 | QTestState *vm; | |
3074 | int64_t origin_rate; | |
3075 | int64_t quota_rate; | |
3076 | int64_t rate ; | |
3077 | int max_try_count = 20; | |
3078 | int hit = 0; | |
3079 | ||
3080 | /* Start vm for vcpu dirtylimit test */ | |
3081 | vm = dirtylimit_start_vm(); | |
3082 | ||
3083 | /* Wait for the first serial output from the vm*/ | |
3084 | wait_for_serial("vm_serial"); | |
3085 | ||
3086 | /* Do dirtyrate measurement with calc time equals 1s */ | |
3087 | calc_dirty_rate(vm, 1); | |
3088 | ||
3089 | /* Sleep calc time and wait for calc dirtyrate complete */ | |
3090 | wait_for_calc_dirtyrate_complete(vm, 1); | |
3091 | ||
3092 | /* Query original dirty page rate */ | |
3093 | origin_rate = get_dirty_rate(vm); | |
3094 | ||
3095 | /* VM booted from bootsect should dirty memory steadily */ | |
3096 | assert(origin_rate != 0); | |
3097 | ||
3098 | /* Setup quota dirty page rate at half of origin */ | |
3099 | quota_rate = origin_rate / 2; | |
3100 | ||
3101 | /* Set dirtylimit */ | |
3102 | dirtylimit_set_all(vm, quota_rate); | |
3103 | ||
3104 | /* | |
3105 | * Check if set-vcpu-dirty-limit and query-vcpu-dirty-limit | |
3106 | * works literally | |
3107 | */ | |
3108 | g_assert_cmpint(quota_rate, ==, get_limit_rate(vm)); | |
3109 | ||
3110 | /* Sleep a bit to check if it take effect */ | |
3111 | usleep(2000000); | |
3112 | ||
3113 | /* | |
3114 | * Check if dirtylimit take effect realistically, set the | |
3115 | * timeout with 20 s(max_try_count * 1s), if dirtylimit | |
3116 | * doesn't take effect, fail test. | |
3117 | */ | |
3118 | while (--max_try_count) { | |
3119 | calc_dirty_rate(vm, 1); | |
3120 | wait_for_calc_dirtyrate_complete(vm, 1); | |
3121 | rate = get_dirty_rate(vm); | |
3122 | ||
3123 | /* | |
3124 | * Assume hitting if current rate is less | |
3125 | * than quota rate (within accepting error) | |
3126 | */ | |
3127 | if (rate < (quota_rate + DIRTYLIMIT_TOLERANCE_RANGE)) { | |
3128 | hit = 1; | |
3129 | break; | |
3130 | } | |
3131 | } | |
3132 | ||
3133 | g_assert_cmpint(hit, ==, 1); | |
3134 | ||
3135 | hit = 0; | |
3136 | max_try_count = 20; | |
3137 | ||
3138 | /* Check if dirtylimit cancellation take effect */ | |
3139 | cancel_vcpu_dirty_limit(vm); | |
3140 | while (--max_try_count) { | |
3141 | calc_dirty_rate(vm, 1); | |
3142 | wait_for_calc_dirtyrate_complete(vm, 1); | |
3143 | rate = get_dirty_rate(vm); | |
3144 | ||
3145 | /* | |
3146 | * Assume dirtylimit be canceled if current rate is | |
3147 | * greater than quota rate (within accepting error) | |
3148 | */ | |
3149 | if (rate > (quota_rate + DIRTYLIMIT_TOLERANCE_RANGE)) { | |
3150 | hit = 1; | |
3151 | break; | |
3152 | } | |
3153 | } | |
3154 | ||
3155 | g_assert_cmpint(hit, ==, 1); | |
3156 | dirtylimit_stop_vm(vm); | |
3157 | } | |
3158 | ||
17257b90 HH |
3159 | static void migrate_dirty_limit_wait_showup(QTestState *from, |
3160 | const int64_t period, | |
3161 | const int64_t value) | |
3162 | { | |
3163 | /* Enable dirty limit capability */ | |
3164 | migrate_set_capability(from, "dirty-limit", true); | |
3165 | ||
3166 | /* Set dirty limit parameters */ | |
3167 | migrate_set_parameter_int(from, "x-vcpu-dirty-limit-period", period); | |
3168 | migrate_set_parameter_int(from, "vcpu-dirty-limit", value); | |
3169 | ||
3170 | /* Make sure migrate can't converge */ | |
3171 | migrate_ensure_non_converge(from); | |
3172 | ||
3173 | /* To check limit rate after precopy */ | |
3174 | migrate_set_capability(from, "pause-before-switchover", true); | |
3175 | ||
3176 | /* Wait for the serial output from the source */ | |
3177 | wait_for_serial("src_serial"); | |
3178 | } | |
3179 | ||
3180 | /* | |
3181 | * This test does: | |
3182 | * source destination | |
3183 | * start vm | |
3184 | * start incoming vm | |
3185 | * migrate | |
3186 | * wait dirty limit to begin | |
3187 | * cancel migrate | |
3188 | * cancellation check | |
3189 | * restart incoming vm | |
3190 | * migrate | |
3191 | * wait dirty limit to begin | |
3192 | * wait pre-switchover event | |
3193 | * convergence condition check | |
3194 | * | |
3195 | * And see if dirty limit migration works correctly. | |
3196 | * This test case involves many passes, so it runs in slow mode only. | |
3197 | */ | |
3198 | static void test_migrate_dirty_limit(void) | |
3199 | { | |
3200 | g_autofree char *uri = g_strdup_printf("unix:%s/migsocket", tmpfs); | |
3201 | QTestState *from, *to; | |
3202 | int64_t remaining; | |
3203 | uint64_t throttle_us_per_full; | |
3204 | /* | |
3205 | * We want the test to be stable and as fast as possible. | |
8053feaa | 3206 | * E.g., with 1Gb/s bandwidth migration may pass without dirty limit, |
17257b90 HH |
3207 | * so we need to decrease a bandwidth. |
3208 | */ | |
3209 | const int64_t dirtylimit_period = 1000, dirtylimit_value = 50; | |
3210 | const int64_t max_bandwidth = 400000000; /* ~400Mb/s */ | |
3211 | const int64_t downtime_limit = 250; /* 250ms */ | |
3212 | /* | |
3213 | * We migrate through unix-socket (> 500Mb/s). | |
3214 | * Thus, expected migration speed ~= bandwidth limit (< 500Mb/s). | |
3215 | * So, we can predict expected_threshold | |
3216 | */ | |
3217 | const int64_t expected_threshold = max_bandwidth * downtime_limit / 1000; | |
3218 | int max_try_count = 10; | |
3219 | MigrateCommon args = { | |
3220 | .start = { | |
3221 | .hide_stderr = true, | |
3222 | .use_dirty_ring = true, | |
3223 | }, | |
3224 | .listen_uri = uri, | |
3225 | .connect_uri = uri, | |
3226 | }; | |
3227 | ||
3228 | /* Start src, dst vm */ | |
3229 | if (test_migrate_start(&from, &to, args.listen_uri, &args.start)) { | |
3230 | return; | |
3231 | } | |
3232 | ||
3233 | /* Prepare for dirty limit migration and wait src vm show up */ | |
3234 | migrate_dirty_limit_wait_showup(from, dirtylimit_period, dirtylimit_value); | |
3235 | ||
3236 | /* Start migrate */ | |
3237 | migrate_qmp(from, uri, "{}"); | |
3238 | ||
3239 | /* Wait for dirty limit throttle begin */ | |
3240 | throttle_us_per_full = 0; | |
3241 | while (throttle_us_per_full == 0) { | |
3242 | throttle_us_per_full = | |
3243 | read_migrate_property_int(from, "dirty-limit-throttle-time-per-round"); | |
3244 | usleep(100); | |
f0649758 | 3245 | g_assert_false(src_state.stop_seen); |
17257b90 HH |
3246 | } |
3247 | ||
3248 | /* Now cancel migrate and wait for dirty limit throttle switch off */ | |
3249 | migrate_cancel(from); | |
3250 | wait_for_migration_status(from, "cancelled", NULL); | |
3251 | ||
3252 | /* Check if dirty limit throttle switched off, set timeout 1ms */ | |
3253 | do { | |
3254 | throttle_us_per_full = | |
3255 | read_migrate_property_int(from, "dirty-limit-throttle-time-per-round"); | |
3256 | usleep(100); | |
f0649758 | 3257 | g_assert_false(src_state.stop_seen); |
17257b90 HH |
3258 | } while (throttle_us_per_full != 0 && --max_try_count); |
3259 | ||
3260 | /* Assert dirty limit is not in service */ | |
3261 | g_assert_cmpint(throttle_us_per_full, ==, 0); | |
3262 | ||
3263 | args = (MigrateCommon) { | |
3264 | .start = { | |
3265 | .only_target = true, | |
3266 | .use_dirty_ring = true, | |
3267 | }, | |
3268 | .listen_uri = uri, | |
3269 | .connect_uri = uri, | |
3270 | }; | |
3271 | ||
3272 | /* Restart dst vm, src vm already show up so we needn't wait anymore */ | |
3273 | if (test_migrate_start(&from, &to, args.listen_uri, &args.start)) { | |
3274 | return; | |
3275 | } | |
3276 | ||
3277 | /* Start migrate */ | |
3278 | migrate_qmp(from, uri, "{}"); | |
3279 | ||
3280 | /* Wait for dirty limit throttle begin */ | |
3281 | throttle_us_per_full = 0; | |
3282 | while (throttle_us_per_full == 0) { | |
3283 | throttle_us_per_full = | |
3284 | read_migrate_property_int(from, "dirty-limit-throttle-time-per-round"); | |
3285 | usleep(100); | |
f0649758 | 3286 | g_assert_false(src_state.stop_seen); |
17257b90 HH |
3287 | } |
3288 | ||
3289 | /* | |
3290 | * The dirty limit rate should equals the return value of | |
3291 | * query-vcpu-dirty-limit if dirty limit cap set | |
3292 | */ | |
3293 | g_assert_cmpint(dirtylimit_value, ==, get_limit_rate(from)); | |
3294 | ||
3295 | /* Now, we have tested if dirty limit works, let it converge */ | |
3296 | migrate_set_parameter_int(from, "downtime-limit", downtime_limit); | |
3297 | migrate_set_parameter_int(from, "max-bandwidth", max_bandwidth); | |
3298 | ||
3299 | /* | |
3300 | * Wait for pre-switchover status to check if migration | |
3301 | * satisfy the convergence condition | |
3302 | */ | |
3303 | wait_for_migration_status(from, "pre-switchover", NULL); | |
3304 | ||
3305 | remaining = read_ram_property_int(from, "remaining"); | |
3306 | g_assert_cmpint(remaining, <, | |
3307 | (expected_threshold + expected_threshold / 100)); | |
3308 | ||
3309 | migrate_continue(from, "pre-switchover"); | |
3310 | ||
3311 | qtest_qmp_eventwait(to, "RESUME"); | |
3312 | ||
3313 | wait_for_serial("dest_serial"); | |
3314 | wait_for_migration_complete(from); | |
3315 | ||
3316 | test_migrate_end(from, to, true); | |
3317 | } | |
3318 | ||
1f546b70 PX |
3319 | static bool kvm_dirty_ring_supported(void) |
3320 | { | |
61c32485 | 3321 | #if defined(__linux__) && defined(HOST_X86_64) |
1f546b70 PX |
3322 | int ret, kvm_fd = open("/dev/kvm", O_RDONLY); |
3323 | ||
3324 | if (kvm_fd < 0) { | |
3325 | return false; | |
3326 | } | |
3327 | ||
3328 | ret = ioctl(kvm_fd, KVM_CHECK_EXTENSION, KVM_CAP_DIRTY_LOG_RING); | |
3329 | close(kvm_fd); | |
3330 | ||
3331 | /* We test with 4096 slots */ | |
3332 | if (ret < 4096) { | |
3333 | return false; | |
3334 | } | |
3335 | ||
3336 | return true; | |
3337 | #else | |
3338 | return false; | |
3339 | #endif | |
3340 | } | |
3341 | ||
ea0c6d62 DDAG |
3342 | int main(int argc, char **argv) |
3343 | { | |
0c1ae3ff | 3344 | bool has_kvm, has_tcg; |
b1fdd21e | 3345 | bool has_uffd, is_x86; |
e35b9a2e | 3346 | const char *arch; |
e5553c1b | 3347 | g_autoptr(GError) err = NULL; |
5050ad2a FR |
3348 | const char *qemu_src = getenv(QEMU_ENV_SRC); |
3349 | const char *qemu_dst = getenv(QEMU_ENV_DST); | |
ea0c6d62 DDAG |
3350 | int ret; |
3351 | ||
3352 | g_test_init(&argc, &argv, NULL); | |
3353 | ||
5050ad2a FR |
3354 | /* |
3355 | * The default QTEST_QEMU_BINARY must always be provided because | |
3356 | * that is what helpers use to query the accel type and | |
3357 | * architecture. | |
3358 | */ | |
3359 | if (qemu_src && qemu_dst) { | |
3360 | g_test_message("Only one of %s, %s is allowed", | |
3361 | QEMU_ENV_SRC, QEMU_ENV_DST); | |
3362 | exit(1); | |
3363 | } | |
3364 | ||
e35b9a2e | 3365 | has_kvm = qtest_has_accel("kvm"); |
0c1ae3ff FR |
3366 | has_tcg = qtest_has_accel("tcg"); |
3367 | ||
3368 | if (!has_tcg && !has_kvm) { | |
3369 | g_test_skip("No KVM or TCG accelerator available"); | |
3370 | return 0; | |
3371 | } | |
3372 | ||
e35b9a2e DB |
3373 | has_uffd = ufd_version_check(); |
3374 | arch = qtest_get_arch(); | |
b1fdd21e | 3375 | is_x86 = !strcmp(arch, "i386") || !strcmp(arch, "x86_64"); |
e35b9a2e | 3376 | |
880b169a TH |
3377 | /* |
3378 | * On ppc64, the test only works with kvm-hv, but not with kvm-pr and TCG | |
3379 | * is touchy due to race conditions on dirty bits (especially on PPC for | |
3380 | * some reason) | |
3381 | */ | |
1bca64a3 | 3382 | if (g_str_equal(arch, "ppc64") && |
b72c7603 | 3383 | (!has_kvm || access("/sys/module/kvm_hv", F_OK))) { |
880b169a | 3384 | g_test_message("Skipping test: kvm_hv not available"); |
4848cb3d | 3385 | return g_test_run(); |
880b169a TH |
3386 | } |
3387 | ||
d254b392 TH |
3388 | /* |
3389 | * Similar to ppc64, s390x seems to be touchy with TCG, so disable it | |
3390 | * there until the problems are resolved | |
3391 | */ | |
1bca64a3 | 3392 | if (g_str_equal(arch, "s390x") && !has_kvm) { |
b72c7603 | 3393 | g_test_message("Skipping test: s390x host with KVM is required"); |
4848cb3d | 3394 | return g_test_run(); |
d254b392 TH |
3395 | } |
3396 | ||
e5553c1b | 3397 | tmpfs = g_dir_make_tmp("migration-test-XXXXXX", &err); |
ea0c6d62 | 3398 | if (!tmpfs) { |
b1f6208c BM |
3399 | g_test_message("Can't create temporary directory in %s: %s", |
3400 | g_get_tmp_dir(), err->message); | |
ea0c6d62 DDAG |
3401 | } |
3402 | g_assert(tmpfs); | |
3403 | ||
3404 | module_call_init(MODULE_INIT_QOM); | |
3405 | ||
b1fdd21e | 3406 | if (is_x86) { |
6f0771de FR |
3407 | migration_test_add("/migration/precopy/unix/suspend/live", |
3408 | test_precopy_unix_suspend_live); | |
3409 | migration_test_add("/migration/precopy/unix/suspend/notlive", | |
3410 | test_precopy_unix_suspend_notlive); | |
b1fdd21e SS |
3411 | } |
3412 | ||
2649a725 | 3413 | if (has_uffd) { |
6f0771de FR |
3414 | migration_test_add("/migration/postcopy/plain", test_postcopy); |
3415 | migration_test_add("/migration/postcopy/recovery/plain", | |
3416 | test_postcopy_recovery); | |
3417 | migration_test_add("/migration/postcopy/preempt/plain", | |
3418 | test_postcopy_preempt); | |
3419 | migration_test_add("/migration/postcopy/preempt/recovery/plain", | |
3420 | test_postcopy_preempt_recovery); | |
dc066da8 | 3421 | if (getenv("QEMU_TEST_FLAKY_TESTS")) { |
6f0771de FR |
3422 | migration_test_add("/migration/postcopy/compress/plain", |
3423 | test_postcopy_compress); | |
3424 | migration_test_add("/migration/postcopy/recovery/compress/plain", | |
3425 | test_postcopy_recovery_compress); | |
dc066da8 | 3426 | } |
7bca2bb7 | 3427 | #ifndef _WIN32 |
6f0771de FR |
3428 | migration_test_add("/migration/postcopy/recovery/double-failures", |
3429 | test_postcopy_recovery_double_fail); | |
7bca2bb7 | 3430 | #endif /* _WIN32 */ |
2b58a8b9 | 3431 | if (is_x86) { |
6f0771de FR |
3432 | migration_test_add("/migration/postcopy/suspend", |
3433 | test_postcopy_suspend); | |
2b58a8b9 | 3434 | } |
2649a725 | 3435 | } |
d1a27b16 | 3436 | |
6f0771de | 3437 | migration_test_add("/migration/bad_dest", test_baddest); |
d864756e | 3438 | #ifndef _WIN32 |
6f0771de FR |
3439 | if (!g_str_equal(arch, "s390x")) { |
3440 | migration_test_add("/migration/analyze-script", test_analyze_script); | |
3441 | } | |
d864756e | 3442 | #endif |
6f0771de FR |
3443 | migration_test_add("/migration/precopy/unix/plain", |
3444 | test_precopy_unix_plain); | |
3445 | migration_test_add("/migration/precopy/unix/xbzrle", | |
3446 | test_precopy_unix_xbzrle); | |
1536d1da LS |
3447 | /* |
3448 | * Compression fails from time to time. | |
3449 | * Put test here but don't enable it until everything is fixed. | |
3450 | */ | |
3451 | if (getenv("QEMU_TEST_FLAKY_TESTS")) { | |
6f0771de FR |
3452 | migration_test_add("/migration/precopy/unix/compress/wait", |
3453 | test_precopy_unix_compress); | |
3454 | migration_test_add("/migration/precopy/unix/compress/nowait", | |
3455 | test_precopy_unix_compress_nowait); | |
1536d1da | 3456 | } |
3dc35470 | 3457 | |
6f0771de FR |
3458 | migration_test_add("/migration/precopy/file", |
3459 | test_precopy_file); | |
3460 | migration_test_add("/migration/precopy/file/offset", | |
3461 | test_precopy_file_offset); | |
3462 | migration_test_add("/migration/precopy/file/offset/bad", | |
3463 | test_precopy_file_offset_bad); | |
3dc35470 | 3464 | |
e7b428d6 SS |
3465 | /* |
3466 | * Our CI system has problems with shared memory. | |
3467 | * Don't run this test until we find a workaround. | |
3468 | */ | |
3469 | if (getenv("QEMU_TEST_FLAKY_TESTS")) { | |
6f0771de | 3470 | migration_test_add("/migration/mode/reboot", test_mode_reboot); |
e7b428d6 SS |
3471 | } |
3472 | ||
58d25e97 | 3473 | #ifdef CONFIG_GNUTLS |
6f0771de FR |
3474 | migration_test_add("/migration/precopy/unix/tls/psk", |
3475 | test_precopy_unix_tls_psk); | |
2649a725 PX |
3476 | |
3477 | if (has_uffd) { | |
3478 | /* | |
3479 | * NOTE: psk test is enough for postcopy, as other types of TLS | |
3480 | * channels are tested under precopy. Here what we want to test is the | |
3481 | * general postcopy path that has TLS channel enabled. | |
3482 | */ | |
6f0771de FR |
3483 | migration_test_add("/migration/postcopy/tls/psk", |
3484 | test_postcopy_tls_psk); | |
3485 | migration_test_add("/migration/postcopy/recovery/tls/psk", | |
3486 | test_postcopy_recovery_tls_psk); | |
3487 | migration_test_add("/migration/postcopy/preempt/tls/psk", | |
3488 | test_postcopy_preempt_tls_psk); | |
3489 | migration_test_add("/migration/postcopy/preempt/recovery/tls/psk", | |
3490 | test_postcopy_preempt_all); | |
2649a725 | 3491 | } |
d47b83b1 | 3492 | #ifdef CONFIG_TASN1 |
6f0771de FR |
3493 | migration_test_add("/migration/precopy/unix/tls/x509/default-host", |
3494 | test_precopy_unix_tls_x509_default_host); | |
3495 | migration_test_add("/migration/precopy/unix/tls/x509/override-host", | |
3496 | test_precopy_unix_tls_x509_override_host); | |
d47b83b1 | 3497 | #endif /* CONFIG_TASN1 */ |
58d25e97 DB |
3498 | #endif /* CONFIG_GNUTLS */ |
3499 | ||
6f0771de | 3500 | migration_test_add("/migration/precopy/tcp/plain", test_precopy_tcp_plain); |
7e6a5c73 | 3501 | |
6f0771de FR |
3502 | migration_test_add("/migration/precopy/tcp/plain/switchover-ack", |
3503 | test_precopy_tcp_switchover_ack); | |
7e6a5c73 | 3504 | |
58d25e97 | 3505 | #ifdef CONFIG_GNUTLS |
6f0771de FR |
3506 | migration_test_add("/migration/precopy/tcp/tls/psk/match", |
3507 | test_precopy_tcp_tls_psk_match); | |
3508 | migration_test_add("/migration/precopy/tcp/tls/psk/mismatch", | |
3509 | test_precopy_tcp_tls_psk_mismatch); | |
d47b83b1 | 3510 | #ifdef CONFIG_TASN1 |
6f0771de FR |
3511 | migration_test_add("/migration/precopy/tcp/tls/x509/default-host", |
3512 | test_precopy_tcp_tls_x509_default_host); | |
3513 | migration_test_add("/migration/precopy/tcp/tls/x509/override-host", | |
3514 | test_precopy_tcp_tls_x509_override_host); | |
3515 | migration_test_add("/migration/precopy/tcp/tls/x509/mismatch-host", | |
3516 | test_precopy_tcp_tls_x509_mismatch_host); | |
3517 | migration_test_add("/migration/precopy/tcp/tls/x509/friendly-client", | |
3518 | test_precopy_tcp_tls_x509_friendly_client); | |
3519 | migration_test_add("/migration/precopy/tcp/tls/x509/hostile-client", | |
3520 | test_precopy_tcp_tls_x509_hostile_client); | |
3521 | migration_test_add("/migration/precopy/tcp/tls/x509/allow-anon-client", | |
3522 | test_precopy_tcp_tls_x509_allow_anon_client); | |
3523 | migration_test_add("/migration/precopy/tcp/tls/x509/reject-anon-client", | |
3524 | test_precopy_tcp_tls_x509_reject_anon_client); | |
d47b83b1 | 3525 | #endif /* CONFIG_TASN1 */ |
58d25e97 DB |
3526 | #endif /* CONFIG_GNUTLS */ |
3527 | ||
6f0771de | 3528 | /* migration_test_add("/migration/ignore_shared", test_ignore_shared); */ |
d7613ee2 | 3529 | #ifndef _WIN32 |
6f0771de | 3530 | migration_test_add("/migration/fd_proto", test_migrate_fd_proto); |
d7613ee2 | 3531 | #endif |
6f0771de FR |
3532 | migration_test_add("/migration/validate_uuid", test_validate_uuid); |
3533 | migration_test_add("/migration/validate_uuid_error", | |
3534 | test_validate_uuid_error); | |
3535 | migration_test_add("/migration/validate_uuid_src_not_set", | |
3536 | test_validate_uuid_src_not_set); | |
3537 | migration_test_add("/migration/validate_uuid_dst_not_set", | |
3538 | test_validate_uuid_dst_not_set); | |
74902af7 JQ |
3539 | /* |
3540 | * See explanation why this test is slow on function definition | |
3541 | */ | |
3542 | if (g_test_slow()) { | |
6f0771de FR |
3543 | migration_test_add("/migration/auto_converge", |
3544 | test_migrate_auto_converge); | |
17257b90 HH |
3545 | if (g_str_equal(arch, "x86_64") && |
3546 | has_kvm && kvm_dirty_ring_supported()) { | |
6f0771de FR |
3547 | migration_test_add("/migration/dirty_limit", |
3548 | test_migrate_dirty_limit); | |
17257b90 | 3549 | } |
74902af7 | 3550 | } |
6f0771de FR |
3551 | migration_test_add("/migration/multifd/tcp/plain/none", |
3552 | test_multifd_tcp_none); | |
75b1f88c FR |
3553 | migration_test_add("/migration/multifd/tcp/plain/cancel", |
3554 | test_multifd_tcp_cancel); | |
6f0771de FR |
3555 | migration_test_add("/migration/multifd/tcp/plain/zlib", |
3556 | test_multifd_tcp_zlib); | |
87dc6f5f | 3557 | #ifdef CONFIG_ZSTD |
6f0771de FR |
3558 | migration_test_add("/migration/multifd/tcp/plain/zstd", |
3559 | test_multifd_tcp_zstd); | |
87dc6f5f | 3560 | #endif |
4d6d2e87 | 3561 | #ifdef CONFIG_GNUTLS |
6f0771de FR |
3562 | migration_test_add("/migration/multifd/tcp/tls/psk/match", |
3563 | test_multifd_tcp_tls_psk_match); | |
3564 | migration_test_add("/migration/multifd/tcp/tls/psk/mismatch", | |
3565 | test_multifd_tcp_tls_psk_mismatch); | |
ff32f1dd | 3566 | #ifdef CONFIG_TASN1 |
6f0771de FR |
3567 | migration_test_add("/migration/multifd/tcp/tls/x509/default-host", |
3568 | test_multifd_tcp_tls_x509_default_host); | |
3569 | migration_test_add("/migration/multifd/tcp/tls/x509/override-host", | |
3570 | test_multifd_tcp_tls_x509_override_host); | |
3571 | migration_test_add("/migration/multifd/tcp/tls/x509/mismatch-host", | |
3572 | test_multifd_tcp_tls_x509_mismatch_host); | |
3573 | migration_test_add("/migration/multifd/tcp/tls/x509/allow-anon-client", | |
3574 | test_multifd_tcp_tls_x509_allow_anon_client); | |
3575 | migration_test_add("/migration/multifd/tcp/tls/x509/reject-anon-client", | |
3576 | test_multifd_tcp_tls_x509_reject_anon_client); | |
ff32f1dd | 3577 | #endif /* CONFIG_TASN1 */ |
4d6d2e87 | 3578 | #endif /* CONFIG_GNUTLS */ |
8c51642b | 3579 | |
1bca64a3 | 3580 | if (g_str_equal(arch, "x86_64") && has_kvm && kvm_dirty_ring_supported()) { |
6f0771de FR |
3581 | migration_test_add("/migration/dirty_ring", |
3582 | test_precopy_unix_dirty_ring); | |
3583 | migration_test_add("/migration/vcpu_dirty_limit", | |
3584 | test_vcpu_dirty_limit); | |
1f546b70 PX |
3585 | } |
3586 | ||
ea0c6d62 DDAG |
3587 | ret = g_test_run(); |
3588 | ||
3589 | g_assert_cmpint(ret, ==, 0); | |
3590 | ||
0c690d3e | 3591 | bootfile_delete(); |
ea0c6d62 DDAG |
3592 | ret = rmdir(tmpfs); |
3593 | if (ret != 0) { | |
13ee9e30 | 3594 | g_test_message("unable to rmdir: path (%s): %s", |
ea0c6d62 DDAG |
3595 | tmpfs, strerror(errno)); |
3596 | } | |
e5553c1b | 3597 | g_free(tmpfs); |
ea0c6d62 DDAG |
3598 | |
3599 | return ret; | |
3600 | } |