1
2
3
4
5
6
7
8
9
10#include "qemu/osdep.h"
11#include "libqos/libqtest.h"
12#include "libqos/pci.h"
13#include "libqos/pci-pc.h"
14#include "qapi/qmp/qdict.h"
15#include "qapi/qmp/qlist.h"
16#include "qapi/qmp/qjson.h"
17#include "libqos/malloc-pc.h"
18#include "libqos/virtio-pci.h"
19#include "hw/pci/pci.h"
20
21#define VIRTIO_NET_F_STANDBY 62
22
23#define ACPI_PCIHP_ADDR_ICH9 0x0cc0
24#define PCI_EJ_BASE 0x0008
25#define PCI_SEL_BASE 0x0010
26
27#define BASE_MACHINE "-M q35 -nodefaults " \
28 "-device pcie-root-port,id=root0,addr=0x1,bus=pcie.0,chassis=1 " \
29 "-device pcie-root-port,id=root1,addr=0x2,bus=pcie.0,chassis=2 "
30
31#define MAC_PRIMARY0 "52:54:00:11:11:11"
32#define MAC_STANDBY0 "52:54:00:22:22:22"
33#define MAC_PRIMARY1 "52:54:00:33:33:33"
34#define MAC_STANDBY1 "52:54:00:44:44:44"
35
36static QGuestAllocator guest_malloc;
37static QPCIBus *pcibus;
38
39static QTestState *machine_start(const char *args, int numbus)
40{
41 QTestState *qts;
42 QPCIDevice *dev;
43 int bus;
44
45 qts = qtest_init(args);
46
47 pc_alloc_init(&guest_malloc, qts, 0);
48 pcibus = qpci_new_pc(qts, &guest_malloc);
49 g_assert(qpci_secondary_buses_init(pcibus) == numbus);
50
51 for (bus = 1; bus <= numbus; bus++) {
52 dev = qpci_device_find(pcibus, QPCI_DEVFN(bus, 0));
53 g_assert_nonnull(dev);
54
55 qpci_device_enable(dev);
56 qpci_iomap(dev, 4, NULL);
57
58 g_free(dev);
59 }
60
61 return qts;
62}
63
64static void machine_stop(QTestState *qts)
65{
66 qpci_free_pc(pcibus);
67 alloc_destroy(&guest_malloc);
68 qtest_quit(qts);
69}
70
71static void test_error_id(void)
72{
73 QTestState *qts;
74 QDict *resp;
75 QDict *err;
76
77 qts = machine_start(BASE_MACHINE
78 "-device virtio-net,bus=root0,id=standby0,failover=on",
79 2);
80
81 resp = qtest_qmp(qts, "{'execute': 'device_add',"
82 "'arguments': {"
83 "'driver': 'virtio-net',"
84 "'bus': 'root1',"
85 "'failover_pair_id': 'standby0'"
86 "} }");
87 g_assert(qdict_haskey(resp, "error"));
88
89 err = qdict_get_qdict(resp, "error");
90 g_assert(qdict_haskey(err, "desc"));
91
92 g_assert_cmpstr(qdict_get_str(err, "desc"), ==,
93 "Device with failover_pair_id needs to have id");
94
95 qobject_unref(resp);
96
97 machine_stop(qts);
98}
99
100static void test_error_pcie(void)
101{
102 QTestState *qts;
103 QDict *resp;
104 QDict *err;
105
106 qts = machine_start(BASE_MACHINE
107 "-device virtio-net,bus=root0,id=standby0,failover=on",
108 2);
109
110 resp = qtest_qmp(qts, "{'execute': 'device_add',"
111 "'arguments': {"
112 "'driver': 'virtio-net',"
113 "'id': 'primary0',"
114 "'bus': 'pcie.0',"
115 "'failover_pair_id': 'standby0'"
116 "} }");
117 g_assert(qdict_haskey(resp, "error"));
118
119 err = qdict_get_qdict(resp, "error");
120 g_assert(qdict_haskey(err, "desc"));
121
122 g_assert_cmpstr(qdict_get_str(err, "desc"), ==,
123 "Bus 'pcie.0' does not support hotplugging");
124
125 qobject_unref(resp);
126
127 machine_stop(qts);
128}
129
130static QDict *find_device(QDict *bus, const char *name)
131{
132 const QObject *obj;
133 QList *devices;
134 QList *list;
135
136 devices = qdict_get_qlist(bus, "devices");
137 if (devices == NULL) {
138 return NULL;
139 }
140
141 list = qlist_copy(devices);
142 while ((obj = qlist_pop(list))) {
143 QDict *device;
144
145 device = qobject_to(QDict, obj);
146
147 if (qdict_haskey(device, "pci_bridge")) {
148 QDict *bridge;
149 QDict *bridge_device;
150
151 bridge = qdict_get_qdict(device, "pci_bridge");
152
153 if (qdict_haskey(bridge, "devices")) {
154 bridge_device = find_device(bridge, name);
155 if (bridge_device) {
156 qobject_unref(device);
157 qobject_unref(list);
158 return bridge_device;
159 }
160 }
161 }
162
163 if (!qdict_haskey(device, "qdev_id")) {
164 qobject_unref(device);
165 continue;
166 }
167
168 if (strcmp(qdict_get_str(device, "qdev_id"), name) == 0) {
169 qobject_unref(list);
170 return device;
171 }
172 qobject_unref(device);
173 }
174 qobject_unref(list);
175
176 return NULL;
177}
178
179static QDict *get_bus(QTestState *qts, int num)
180{
181 QObject *obj;
182 QDict *resp;
183 QList *ret;
184
185 resp = qtest_qmp(qts, "{ 'execute': 'query-pci' }");
186 g_assert(qdict_haskey(resp, "return"));
187
188 ret = qdict_get_qlist(resp, "return");
189 g_assert_nonnull(ret);
190
191 while ((obj = qlist_pop(ret))) {
192 QDict *bus;
193
194 bus = qobject_to(QDict, obj);
195 if (!qdict_haskey(bus, "bus")) {
196 qobject_unref(bus);
197 continue;
198 }
199 if (qdict_get_int(bus, "bus") == num) {
200 qobject_unref(resp);
201 return bus;
202 }
203 qobject_ref(bus);
204 }
205 qobject_unref(resp);
206
207 return NULL;
208}
209
210static char *get_mac(QTestState *qts, const char *name)
211{
212 QDict *resp;
213 char *mac;
214
215 resp = qtest_qmp(qts, "{ 'execute': 'qom-get', "
216 "'arguments': { "
217 "'path': %s, "
218 "'property': 'mac' } }", name);
219
220 g_assert(qdict_haskey(resp, "return"));
221
222 mac = g_strdup(qdict_get_str(resp, "return"));
223
224 qobject_unref(resp);
225
226 return mac;
227}
228
229#define check_one_card(qts, present, id, mac) \
230do { \
231 QDict *device; \
232 QDict *bus; \
233 char *addr; \
234 bus = get_bus(qts, 0); \
235 device = find_device(bus, id); \
236 if (present) { \
237 char *path; \
238 g_assert_nonnull(device); \
239 qobject_unref(device); \
240 path = g_strdup_printf("/machine/peripheral/%s", id); \
241 addr = get_mac(qts, path); \
242 g_free(path); \
243 g_assert_cmpstr(mac, ==, addr); \
244 g_free(addr); \
245 } else { \
246 g_assert_null(device); \
247 } \
248 qobject_unref(bus); \
249} while (0)
250
251static QDict *get_failover_negociated_event(QTestState *qts)
252{
253 QDict *resp;
254 QDict *data;
255
256 resp = qtest_qmp_eventwait_ref(qts, "FAILOVER_NEGOTIATED");
257 g_assert(qdict_haskey(resp, "data"));
258
259 data = qdict_get_qdict(resp, "data");
260 g_assert(qdict_haskey(data, "device-id"));
261 qobject_ref(data);
262 qobject_unref(resp);
263
264 return data;
265}
266
267static QVirtioPCIDevice *start_virtio_net_internal(QTestState *qts,
268 int bus, int slot,
269 uint64_t *features)
270{
271 QVirtioPCIDevice *dev;
272 QPCIAddress addr;
273
274 addr.devfn = QPCI_DEVFN((bus << 5) + slot, 0);
275 dev = virtio_pci_new(pcibus, &addr);
276 g_assert_nonnull(dev);
277 qvirtio_pci_device_enable(dev);
278 qvirtio_start_device(&dev->vdev);
279 *features &= qvirtio_get_features(&dev->vdev);
280 qvirtio_set_features(&dev->vdev, *features);
281 qvirtio_set_driver_ok(&dev->vdev);
282 return dev;
283}
284
285static QVirtioPCIDevice *start_virtio_net(QTestState *qts, int bus, int slot,
286 const char *id, bool failover)
287{
288 QVirtioPCIDevice *dev;
289 uint64_t features;
290
291 features = ~(QVIRTIO_F_BAD_FEATURE |
292 (1ull << VIRTIO_RING_F_INDIRECT_DESC) |
293 (1ull << VIRTIO_RING_F_EVENT_IDX));
294
295 dev = start_virtio_net_internal(qts, bus, slot, &features);
296
297 g_assert(!!(features & (1ull << VIRTIO_NET_F_STANDBY)) == failover);
298
299 if (failover) {
300 QDict *resp;
301
302 resp = get_failover_negociated_event(qts);
303 g_assert_cmpstr(qdict_get_str(resp, "device-id"), ==, id);
304 qobject_unref(resp);
305 }
306
307 return dev;
308}
309
310static void test_on(void)
311{
312 QTestState *qts;
313
314 qts = machine_start(BASE_MACHINE
315 "-netdev user,id=hs0 "
316 "-device virtio-net,bus=root0,id=standby0,"
317 "failover=on,netdev=hs0,mac="MAC_STANDBY0" "
318 "-netdev user,id=hs1 "
319 "-device virtio-net,bus=root1,id=primary0,"
320 "failover_pair_id=standby0,netdev=hs1,mac="MAC_PRIMARY0,
321 2);
322
323 check_one_card(qts, true, "standby0", MAC_STANDBY0);
324 check_one_card(qts, false, "primary0", MAC_PRIMARY0);
325
326 machine_stop(qts);
327}
328
329static void test_on_mismatch(void)
330{
331 QTestState *qts;
332 QVirtioPCIDevice *vdev;
333
334 qts = machine_start(BASE_MACHINE
335 "-netdev user,id=hs0 "
336 "-device virtio-net,bus=root0,id=standby0,"
337 "failover=on,netdev=hs0,mac="MAC_STANDBY0" "
338 "-netdev user,id=hs1 "
339 "-device virtio-net,bus=root1,id=primary0,"
340 "failover_pair_id=standby1,netdev=hs1,mac="MAC_PRIMARY0,
341 2);
342
343 check_one_card(qts, true, "standby0", MAC_STANDBY0);
344 check_one_card(qts, true, "primary0", MAC_PRIMARY0);
345
346 vdev = start_virtio_net(qts, 1, 0, "standby0", true);
347
348 check_one_card(qts, true, "standby0", MAC_STANDBY0);
349 check_one_card(qts, true, "primary0", MAC_PRIMARY0);
350
351 qos_object_destroy((QOSGraphObject *)vdev);
352 machine_stop(qts);
353}
354
355static void test_off(void)
356{
357 QTestState *qts;
358 QVirtioPCIDevice *vdev;
359
360 qts = machine_start(BASE_MACHINE
361 "-netdev user,id=hs0 "
362 "-device virtio-net,bus=root0,id=standby0,"
363 "failover=off,netdev=hs0,mac="MAC_STANDBY0" "
364 "-netdev user,id=hs1 "
365 "-device virtio-net,bus=root1,id=primary0,"
366 "failover_pair_id=standby0,netdev=hs1,mac="MAC_PRIMARY0,
367 2);
368
369 check_one_card(qts, true, "standby0", MAC_STANDBY0);
370 check_one_card(qts, true, "primary0", MAC_PRIMARY0);
371
372 vdev = start_virtio_net(qts, 1, 0, "standby0", false);
373
374 check_one_card(qts, true, "standby0", MAC_STANDBY0);
375 check_one_card(qts, true, "primary0", MAC_PRIMARY0);
376
377 qos_object_destroy((QOSGraphObject *)vdev);
378 machine_stop(qts);
379}
380
381static void test_enabled(void)
382{
383 QTestState *qts;
384 QVirtioPCIDevice *vdev;
385
386 qts = machine_start(BASE_MACHINE
387 "-netdev user,id=hs0 "
388 "-device virtio-net,bus=root0,id=standby0,"
389 "failover=on,netdev=hs0,mac="MAC_STANDBY0" "
390 "-netdev user,id=hs1 "
391 "-device virtio-net,bus=root1,id=primary0,"
392 "failover_pair_id=standby0,netdev=hs1,mac="MAC_PRIMARY0" ",
393 2);
394
395 check_one_card(qts, true, "standby0", MAC_STANDBY0);
396 check_one_card(qts, false, "primary0", MAC_PRIMARY0);
397
398 vdev = start_virtio_net(qts, 1, 0, "standby0", true);
399
400 check_one_card(qts, true, "standby0", MAC_STANDBY0);
401 check_one_card(qts, true, "primary0", MAC_PRIMARY0);
402
403 qos_object_destroy((QOSGraphObject *)vdev);
404 machine_stop(qts);
405}
406
407static void test_guest_off(void)
408{
409 QTestState *qts;
410 QVirtioPCIDevice *vdev;
411 uint64_t features;
412
413 qts = machine_start(BASE_MACHINE
414 "-netdev user,id=hs0 "
415 "-device virtio-net,bus=root0,id=standby0,"
416 "failover=on,netdev=hs0,mac="MAC_STANDBY0" "
417 "-netdev user,id=hs1 "
418 "-device virtio-net,bus=root1,id=primary0,"
419 "failover_pair_id=standby0,netdev=hs1,mac="MAC_PRIMARY0" ",
420 2);
421
422 check_one_card(qts, true, "standby0", MAC_STANDBY0);
423 check_one_card(qts, false, "primary0", MAC_PRIMARY0);
424
425 features = ~(QVIRTIO_F_BAD_FEATURE |
426 (1ull << VIRTIO_RING_F_INDIRECT_DESC) |
427 (1ull << VIRTIO_RING_F_EVENT_IDX) |
428 (1ull << VIRTIO_NET_F_STANDBY));
429
430 vdev = start_virtio_net_internal(qts, 1, 0, &features);
431
432 check_one_card(qts, true, "standby0", MAC_STANDBY0);
433 check_one_card(qts, false, "primary0", MAC_PRIMARY0);
434
435 qos_object_destroy((QOSGraphObject *)vdev);
436 machine_stop(qts);
437}
438
439static void test_hotplug_1(void)
440{
441 QTestState *qts;
442 QVirtioPCIDevice *vdev;
443
444 qts = machine_start(BASE_MACHINE
445 "-netdev user,id=hs0 "
446 "-device virtio-net,bus=root0,id=standby0,"
447 "failover=on,netdev=hs0,mac="MAC_STANDBY0" "
448 "-netdev user,id=hs1 ", 2);
449
450 check_one_card(qts, true, "standby0", MAC_STANDBY0);
451 check_one_card(qts, false, "primary0", MAC_PRIMARY0);
452
453 vdev = start_virtio_net(qts, 1, 0, "standby0", true);
454
455 check_one_card(qts, true, "standby0", MAC_STANDBY0);
456 check_one_card(qts, false, "primary0", MAC_PRIMARY0);
457
458 qtest_qmp_device_add(qts, "virtio-net", "primary0",
459 "{'bus': 'root1',"
460 "'failover_pair_id': 'standby0',"
461 "'netdev': 'hs1',"
462 "'mac': '"MAC_PRIMARY0"'}");
463
464 check_one_card(qts, true, "standby0", MAC_STANDBY0);
465 check_one_card(qts, true, "primary0", MAC_PRIMARY0);
466
467 qos_object_destroy((QOSGraphObject *)vdev);
468 machine_stop(qts);
469}
470
471static void test_hotplug_1_reverse(void)
472{
473 QTestState *qts;
474 QVirtioPCIDevice *vdev;
475
476 qts = machine_start(BASE_MACHINE
477 "-netdev user,id=hs0 "
478 "-netdev user,id=hs1 "
479 "-device virtio-net,bus=root1,id=primary0,"
480 "failover_pair_id=standby0,netdev=hs1,mac="MAC_PRIMARY0" ",
481 2);
482
483 check_one_card(qts, false, "standby0", MAC_STANDBY0);
484 check_one_card(qts, true, "primary0", MAC_PRIMARY0);
485
486 qtest_qmp_device_add(qts, "virtio-net", "standby0",
487 "{'bus': 'root0',"
488 "'failover': 'on',"
489 "'netdev': 'hs0',"
490 "'mac': '"MAC_STANDBY0"'}");
491
492 check_one_card(qts, true, "standby0", MAC_STANDBY0);
493 check_one_card(qts, true, "primary0", MAC_PRIMARY0);
494
495 vdev = start_virtio_net(qts, 1, 0, "standby0", true);
496
497 check_one_card(qts, true, "standby0", MAC_STANDBY0);
498 check_one_card(qts, true, "primary0", MAC_PRIMARY0);
499
500 qos_object_destroy((QOSGraphObject *)vdev);
501 machine_stop(qts);
502}
503
504static void test_hotplug_2(void)
505{
506 QTestState *qts;
507 QVirtioPCIDevice *vdev;
508
509 qts = machine_start(BASE_MACHINE
510 "-netdev user,id=hs0 "
511 "-netdev user,id=hs1 ",
512 2);
513
514 check_one_card(qts, false, "standby0", MAC_STANDBY0);
515 check_one_card(qts, false, "primary0", MAC_PRIMARY0);
516
517 qtest_qmp_device_add(qts, "virtio-net", "standby0",
518 "{'bus': 'root0',"
519 "'failover': 'on',"
520 "'netdev': 'hs0',"
521 "'mac': '"MAC_STANDBY0"'}");
522
523 check_one_card(qts, true, "standby0", MAC_STANDBY0);
524 check_one_card(qts, false, "primary0", MAC_PRIMARY0);
525
526 vdev = start_virtio_net(qts, 1, 0, "standby0", true);
527
528 check_one_card(qts, true, "standby0", MAC_STANDBY0);
529 check_one_card(qts, false, "primary0", MAC_PRIMARY0);
530
531 qtest_qmp_device_add(qts, "virtio-net", "primary0",
532 "{'bus': 'root1',"
533 "'failover_pair_id': 'standby0',"
534 "'netdev': 'hs1',"
535 "'mac': '"MAC_PRIMARY0"'}");
536
537 check_one_card(qts, true, "standby0", MAC_STANDBY0);
538 check_one_card(qts, true, "primary0", MAC_PRIMARY0);
539
540 qos_object_destroy((QOSGraphObject *)vdev);
541 machine_stop(qts);
542}
543
544static void test_hotplug_2_reverse(void)
545{
546 QTestState *qts;
547 QVirtioPCIDevice *vdev;
548
549 qts = machine_start(BASE_MACHINE
550 "-netdev user,id=hs0 "
551 "-netdev user,id=hs1 ",
552 2);
553
554 check_one_card(qts, false, "standby0", MAC_STANDBY0);
555 check_one_card(qts, false, "primary0", MAC_PRIMARY0);
556
557 qtest_qmp_device_add(qts, "virtio-net", "primary0",
558 "{'bus': 'root1',"
559 "'failover_pair_id': 'standby0',"
560 "'netdev': 'hs1',"
561 "'mac': '"MAC_PRIMARY0"'}");
562
563 check_one_card(qts, false, "standby0", MAC_STANDBY0);
564 check_one_card(qts, true, "primary0", MAC_PRIMARY0);
565
566 qtest_qmp_device_add(qts, "virtio-net", "standby0",
567 "{'bus': 'root0',"
568 "'failover': 'on',"
569 "'netdev': 'hs0',"
570 "'rombar': 0,"
571 "'romfile': '',"
572 "'mac': '"MAC_STANDBY0"'}");
573
574
575
576
577
578
579 check_one_card(qts, true, "standby0", MAC_STANDBY0);
580 check_one_card(qts, true, "primary0", MAC_PRIMARY0);
581
582 vdev = start_virtio_net(qts, 1, 0, "standby0", true);
583
584 check_one_card(qts, true, "standby0", MAC_STANDBY0);
585 check_one_card(qts, true, "primary0", MAC_PRIMARY0);
586
587 qos_object_destroy((QOSGraphObject *)vdev);
588 machine_stop(qts);
589}
590
591static QDict *migrate_status(QTestState *qts)
592{
593 QDict *resp, *ret;
594
595 resp = qtest_qmp(qts, "{ 'execute': 'query-migrate' }");
596 g_assert(qdict_haskey(resp, "return"));
597
598 ret = qdict_get_qdict(resp, "return");
599 g_assert(qdict_haskey(ret, "status"));
600 qobject_ref(ret);
601 qobject_unref(resp);
602
603 return ret;
604}
605
606static QDict *get_unplug_primary_event(QTestState *qts)
607{
608 QDict *resp;
609 QDict *data;
610
611 resp = qtest_qmp_eventwait_ref(qts, "UNPLUG_PRIMARY");
612 g_assert(qdict_haskey(resp, "data"));
613
614 data = qdict_get_qdict(resp, "data");
615 g_assert(qdict_haskey(data, "device-id"));
616 qobject_ref(data);
617 qobject_unref(resp);
618
619 return data;
620}
621
622static void test_migrate_out(gconstpointer opaque)
623{
624 QTestState *qts;
625 QDict *resp, *args, *ret;
626 g_autofree gchar *uri = g_strdup_printf("exec: cat > %s", (gchar *)opaque);
627 const gchar *status;
628 QVirtioPCIDevice *vdev;
629
630 qts = machine_start(BASE_MACHINE
631 "-netdev user,id=hs0 "
632 "-netdev user,id=hs1 ",
633 2);
634
635 check_one_card(qts, false, "standby0", MAC_STANDBY0);
636 check_one_card(qts, false, "primary0", MAC_PRIMARY0);
637
638 qtest_qmp_device_add(qts, "virtio-net", "standby0",
639 "{'bus': 'root0',"
640 "'failover': 'on',"
641 "'netdev': 'hs0',"
642 "'mac': '"MAC_STANDBY0"'}");
643
644 check_one_card(qts, true, "standby0", MAC_STANDBY0);
645 check_one_card(qts, false, "primary0", MAC_PRIMARY0);
646
647 vdev = start_virtio_net(qts, 1, 0, "standby0", true);
648
649 check_one_card(qts, true, "standby0", MAC_STANDBY0);
650 check_one_card(qts, false, "primary0", MAC_PRIMARY0);
651
652 qtest_qmp_device_add(qts, "virtio-net", "primary0",
653 "{'bus': 'root1',"
654 "'failover_pair_id': 'standby0',"
655 "'netdev': 'hs1',"
656 "'rombar': 0,"
657 "'romfile': '',"
658 "'mac': '"MAC_PRIMARY0"'}");
659
660 check_one_card(qts, true, "standby0", MAC_STANDBY0);
661 check_one_card(qts, true, "primary0", MAC_PRIMARY0);
662
663 args = qdict_from_jsonf_nofail("{}");
664 g_assert_nonnull(args);
665 qdict_put_str(args, "uri", uri);
666
667 resp = qtest_qmp(qts, "{ 'execute': 'migrate', 'arguments': %p}", args);
668 g_assert(qdict_haskey(resp, "return"));
669 qobject_unref(resp);
670
671
672 resp = get_unplug_primary_event(qts);
673 g_assert_cmpstr(qdict_get_str(resp, "device-id"), ==, "primary0");
674 qobject_unref(resp);
675
676
677 while (true) {
678 ret = migrate_status(qts);
679
680 status = qdict_get_str(ret, "status");
681 if (strcmp(status, "wait-unplug") == 0) {
682 qobject_unref(ret);
683 break;
684 }
685
686
687 g_assert_cmpstr(status, !=, "active");
688 g_assert_cmpstr(status, !=, "completed");
689 g_assert_cmpstr(status, !=, "failed");
690 g_assert_cmpstr(status, !=, "cancelling");
691 g_assert_cmpstr(status, !=, "cancelled");
692
693 qobject_unref(ret);
694 }
695
696 if (g_test_slow()) {
697
698 for (int i = 0; i < 5; i++) {
699 sleep(1);
700 ret = migrate_status(qts);
701 status = qdict_get_str(ret, "status");
702 g_assert_cmpstr(status, ==, "wait-unplug");
703 qobject_unref(ret);
704 }
705 }
706
707
708 qtest_outl(qts, ACPI_PCIHP_ADDR_ICH9 + PCI_EJ_BASE, 1);
709
710 while (true) {
711 ret = migrate_status(qts);
712
713 status = qdict_get_str(ret, "status");
714 if (strcmp(status, "completed") == 0) {
715 qobject_unref(ret);
716 break;
717 }
718 g_assert_cmpstr(status, !=, "failed");
719 g_assert_cmpstr(status, !=, "cancelling");
720 g_assert_cmpstr(status, !=, "cancelled");
721 qobject_unref(ret);
722 }
723
724 qtest_qmp_eventwait(qts, "STOP");
725
726
727
728
729
730
731
732
733
734 qos_object_destroy((QOSGraphObject *)vdev);
735 machine_stop(qts);
736}
737
738static QDict *get_migration_event(QTestState *qts)
739{
740 QDict *resp;
741 QDict *data;
742
743 resp = qtest_qmp_eventwait_ref(qts, "MIGRATION");
744 g_assert(qdict_haskey(resp, "data"));
745
746 data = qdict_get_qdict(resp, "data");
747 g_assert(qdict_haskey(data, "status"));
748 qobject_ref(data);
749 qobject_unref(resp);
750
751 return data;
752}
753
754static void test_migrate_in(gconstpointer opaque)
755{
756 QTestState *qts;
757 QDict *resp, *args, *ret;
758 g_autofree gchar *uri = g_strdup_printf("exec: cat %s", (gchar *)opaque);
759
760 qts = machine_start(BASE_MACHINE
761 "-netdev user,id=hs0 "
762 "-netdev user,id=hs1 "
763 "-incoming defer ",
764 2);
765
766 check_one_card(qts, false, "standby0", MAC_STANDBY0);
767 check_one_card(qts, false, "primary0", MAC_PRIMARY0);
768
769 qtest_qmp_device_add(qts, "virtio-net", "standby0",
770 "{'bus': 'root0',"
771 "'failover': 'on',"
772 "'netdev': 'hs0',"
773 "'mac': '"MAC_STANDBY0"'}");
774
775 check_one_card(qts, true, "standby0", MAC_STANDBY0);
776 check_one_card(qts, false, "primary0", MAC_PRIMARY0);
777
778 qtest_qmp_device_add(qts, "virtio-net", "primary0",
779 "{'bus': 'root1',"
780 "'failover_pair_id': 'standby0',"
781 "'netdev': 'hs1',"
782 "'rombar': 0,"
783 "'romfile': '',"
784 "'mac': '"MAC_PRIMARY0"'}");
785
786 check_one_card(qts, true, "standby0", MAC_STANDBY0);
787 check_one_card(qts, false, "primary0", MAC_PRIMARY0);
788
789 args = qdict_from_jsonf_nofail("{}");
790 g_assert_nonnull(args);
791 qdict_put_str(args, "uri", uri);
792
793 resp = qtest_qmp(qts, "{ 'execute': 'migrate-incoming', 'arguments': %p}",
794 args);
795 g_assert(qdict_haskey(resp, "return"));
796 qobject_unref(resp);
797
798 resp = get_migration_event(qts);
799 g_assert_cmpstr(qdict_get_str(resp, "status"), ==, "setup");
800 qobject_unref(resp);
801
802 resp = get_failover_negociated_event(qts);
803 g_assert_cmpstr(qdict_get_str(resp, "device-id"), ==, "standby0");
804 qobject_unref(resp);
805
806 check_one_card(qts, true, "standby0", MAC_STANDBY0);
807 check_one_card(qts, true, "primary0", MAC_PRIMARY0);
808
809 qtest_qmp_eventwait(qts, "RESUME");
810
811 ret = migrate_status(qts);
812 g_assert_cmpstr(qdict_get_str(ret, "status"), ==, "completed");
813 qobject_unref(ret);
814
815 machine_stop(qts);
816}
817
818static void test_off_migrate_out(gconstpointer opaque)
819{
820 QTestState *qts;
821 QDict *resp, *args, *ret;
822 g_autofree gchar *uri = g_strdup_printf("exec: cat > %s", (gchar *)opaque);
823 const gchar *status;
824 QVirtioPCIDevice *vdev;
825
826 qts = machine_start(BASE_MACHINE
827 "-netdev user,id=hs0 "
828 "-netdev user,id=hs1 ",
829 2);
830
831 check_one_card(qts, false, "standby0", MAC_STANDBY0);
832 check_one_card(qts, false, "primary0", MAC_PRIMARY0);
833
834 qtest_qmp_device_add(qts, "virtio-net", "standby0",
835 "{'bus': 'root0',"
836 "'failover': 'off',"
837 "'netdev': 'hs0',"
838 "'mac': '"MAC_STANDBY0"'}");
839
840 check_one_card(qts, true, "standby0", MAC_STANDBY0);
841 check_one_card(qts, false, "primary0", MAC_PRIMARY0);
842
843 qtest_qmp_device_add(qts, "virtio-net", "primary0",
844 "{'bus': 'root1',"
845 "'failover_pair_id': 'standby0',"
846 "'netdev': 'hs1',"
847 "'rombar': 0,"
848 "'romfile': '',"
849 "'mac': '"MAC_PRIMARY0"'}");
850
851 check_one_card(qts, true, "standby0", MAC_STANDBY0);
852 check_one_card(qts, true, "primary0", MAC_PRIMARY0);
853
854 vdev = start_virtio_net(qts, 1, 0, "standby0", false);
855
856 check_one_card(qts, true, "standby0", MAC_STANDBY0);
857 check_one_card(qts, true, "primary0", MAC_PRIMARY0);
858
859 args = qdict_from_jsonf_nofail("{}");
860 g_assert_nonnull(args);
861 qdict_put_str(args, "uri", uri);
862
863 resp = qtest_qmp(qts, "{ 'execute': 'migrate', 'arguments': %p}", args);
864 g_assert(qdict_haskey(resp, "return"));
865 qobject_unref(resp);
866
867 while (true) {
868 ret = migrate_status(qts);
869
870 status = qdict_get_str(ret, "status");
871 if (strcmp(status, "completed") == 0) {
872 qobject_unref(ret);
873 break;
874 }
875 g_assert_cmpstr(status, !=, "failed");
876 g_assert_cmpstr(status, !=, "cancelling");
877 g_assert_cmpstr(status, !=, "cancelled");
878 qobject_unref(ret);
879 }
880
881 qtest_qmp_eventwait(qts, "STOP");
882
883 qos_object_destroy((QOSGraphObject *)vdev);
884 machine_stop(qts);
885}
886
887static void test_off_migrate_in(gconstpointer opaque)
888{
889 QTestState *qts;
890 QDict *resp, *args, *ret;
891 g_autofree gchar *uri = g_strdup_printf("exec: cat %s", (gchar *)opaque);
892
893 qts = machine_start(BASE_MACHINE
894 "-netdev user,id=hs0 "
895 "-netdev user,id=hs1 "
896 "-incoming defer ",
897 2);
898
899 check_one_card(qts, false, "standby0", MAC_STANDBY0);
900 check_one_card(qts, false, "primary0", MAC_PRIMARY0);
901
902 qtest_qmp_device_add(qts, "virtio-net", "standby0",
903 "{'bus': 'root0',"
904 "'failover': 'off',"
905 "'netdev': 'hs0',"
906 "'mac': '"MAC_STANDBY0"'}");
907
908 check_one_card(qts, true, "standby0", MAC_STANDBY0);
909 check_one_card(qts, false, "primary0", MAC_PRIMARY0);
910
911 qtest_qmp_device_add(qts, "virtio-net", "primary0",
912 "{'bus': 'root1',"
913 "'failover_pair_id': 'standby0',"
914 "'netdev': 'hs1',"
915 "'rombar': 0,"
916 "'romfile': '',"
917 "'mac': '"MAC_PRIMARY0"'}");
918
919 check_one_card(qts, true, "standby0", MAC_STANDBY0);
920 check_one_card(qts, true, "primary0", MAC_PRIMARY0);
921
922 args = qdict_from_jsonf_nofail("{}");
923 g_assert_nonnull(args);
924 qdict_put_str(args, "uri", uri);
925
926 resp = qtest_qmp(qts, "{ 'execute': 'migrate-incoming', 'arguments': %p}",
927 args);
928 g_assert(qdict_haskey(resp, "return"));
929 qobject_unref(resp);
930
931 resp = get_migration_event(qts);
932 g_assert_cmpstr(qdict_get_str(resp, "status"), ==, "setup");
933 qobject_unref(resp);
934
935 check_one_card(qts, true, "standby0", MAC_STANDBY0);
936 check_one_card(qts, true, "primary0", MAC_PRIMARY0);
937
938 qtest_qmp_eventwait(qts, "RESUME");
939
940 ret = migrate_status(qts);
941 g_assert_cmpstr(qdict_get_str(ret, "status"), ==, "completed");
942 qobject_unref(ret);
943
944 machine_stop(qts);
945}
946
947static void test_guest_off_migrate_out(gconstpointer opaque)
948{
949 QTestState *qts;
950 QDict *resp, *args, *ret;
951 g_autofree gchar *uri = g_strdup_printf("exec: cat > %s", (gchar *)opaque);
952 const gchar *status;
953 QVirtioPCIDevice *vdev;
954 uint64_t features;
955
956 qts = machine_start(BASE_MACHINE
957 "-netdev user,id=hs0 "
958 "-netdev user,id=hs1 ",
959 2);
960
961 check_one_card(qts, false, "standby0", MAC_STANDBY0);
962 check_one_card(qts, false, "primary0", MAC_PRIMARY0);
963
964 qtest_qmp_device_add(qts, "virtio-net", "standby0",
965 "{'bus': 'root0',"
966 "'failover': 'on',"
967 "'netdev': 'hs0',"
968 "'mac': '"MAC_STANDBY0"'}");
969
970 check_one_card(qts, true, "standby0", MAC_STANDBY0);
971 check_one_card(qts, false, "primary0", MAC_PRIMARY0);
972
973 qtest_qmp_device_add(qts, "virtio-net", "primary0",
974 "{'bus': 'root1',"
975 "'failover_pair_id': 'standby0',"
976 "'netdev': 'hs1',"
977 "'rombar': 0,"
978 "'romfile': '',"
979 "'mac': '"MAC_PRIMARY0"'}");
980
981 check_one_card(qts, true, "standby0", MAC_STANDBY0);
982 check_one_card(qts, false, "primary0", MAC_PRIMARY0);
983
984 features = ~(QVIRTIO_F_BAD_FEATURE |
985 (1ull << VIRTIO_RING_F_INDIRECT_DESC) |
986 (1ull << VIRTIO_RING_F_EVENT_IDX) |
987 (1ull << VIRTIO_NET_F_STANDBY));
988
989 vdev = start_virtio_net_internal(qts, 1, 0, &features);
990
991 check_one_card(qts, true, "standby0", MAC_STANDBY0);
992 check_one_card(qts, false, "primary0", MAC_PRIMARY0);
993
994 args = qdict_from_jsonf_nofail("{}");
995 g_assert_nonnull(args);
996 qdict_put_str(args, "uri", uri);
997
998 resp = qtest_qmp(qts, "{ 'execute': 'migrate', 'arguments': %p}", args);
999 g_assert(qdict_haskey(resp, "return"));
1000 qobject_unref(resp);
1001
1002 while (true) {
1003 ret = migrate_status(qts);
1004
1005 status = qdict_get_str(ret, "status");
1006 if (strcmp(status, "completed") == 0) {
1007 qobject_unref(ret);
1008 break;
1009 }
1010 g_assert_cmpstr(status, !=, "failed");
1011 g_assert_cmpstr(status, !=, "cancelling");
1012 g_assert_cmpstr(status, !=, "cancelled");
1013 qobject_unref(ret);
1014 }
1015
1016 qtest_qmp_eventwait(qts, "STOP");
1017
1018 check_one_card(qts, true, "standby0", MAC_STANDBY0);
1019 check_one_card(qts, false, "primary0", MAC_PRIMARY0);
1020
1021 qos_object_destroy((QOSGraphObject *)vdev);
1022 machine_stop(qts);
1023}
1024
1025static void test_guest_off_migrate_in(gconstpointer opaque)
1026{
1027 QTestState *qts;
1028 QDict *resp, *args, *ret;
1029 g_autofree gchar *uri = g_strdup_printf("exec: cat %s", (gchar *)opaque);
1030
1031 qts = machine_start(BASE_MACHINE
1032 "-netdev user,id=hs0 "
1033 "-netdev user,id=hs1 "
1034 "-incoming defer ",
1035 2);
1036
1037 check_one_card(qts, false, "standby0", MAC_STANDBY0);
1038 check_one_card(qts, false, "primary0", MAC_PRIMARY0);
1039
1040 qtest_qmp_device_add(qts, "virtio-net", "standby0",
1041 "{'bus': 'root0',"
1042 "'failover': 'on',"
1043 "'netdev': 'hs0',"
1044 "'mac': '"MAC_STANDBY0"'}");
1045
1046 check_one_card(qts, true, "standby0", MAC_STANDBY0);
1047 check_one_card(qts, false, "primary0", MAC_PRIMARY0);
1048
1049 qtest_qmp_device_add(qts, "virtio-net", "primary0",
1050 "{'bus': 'root1',"
1051 "'failover_pair_id': 'standby0',"
1052 "'netdev': 'hs1',"
1053 "'rombar': 0,"
1054 "'romfile': '',"
1055 "'mac': '"MAC_PRIMARY0"'}");
1056
1057 check_one_card(qts, true, "standby0", MAC_STANDBY0);
1058 check_one_card(qts, false, "primary0", MAC_PRIMARY0);
1059
1060 args = qdict_from_jsonf_nofail("{}");
1061 g_assert_nonnull(args);
1062 qdict_put_str(args, "uri", uri);
1063
1064 resp = qtest_qmp(qts, "{ 'execute': 'migrate-incoming', 'arguments': %p}",
1065 args);
1066 g_assert(qdict_haskey(resp, "return"));
1067 qobject_unref(resp);
1068
1069 resp = get_migration_event(qts);
1070 g_assert_cmpstr(qdict_get_str(resp, "status"), ==, "setup");
1071 qobject_unref(resp);
1072
1073 check_one_card(qts, true, "standby0", MAC_STANDBY0);
1074 check_one_card(qts, false, "primary0", MAC_PRIMARY0);
1075
1076 qtest_qmp_eventwait(qts, "RESUME");
1077
1078 ret = migrate_status(qts);
1079 g_assert_cmpstr(qdict_get_str(ret, "status"), ==, "completed");
1080 qobject_unref(ret);
1081
1082 machine_stop(qts);
1083}
1084
1085static void test_migrate_guest_off_abort(gconstpointer opaque)
1086{
1087 QTestState *qts;
1088 QDict *resp, *args, *ret;
1089 g_autofree gchar *uri = g_strdup_printf("exec: cat > %s", (gchar *)opaque);
1090 const gchar *status;
1091 QVirtioPCIDevice *vdev;
1092 uint64_t features;
1093
1094 qts = machine_start(BASE_MACHINE
1095 "-netdev user,id=hs0 "
1096 "-netdev user,id=hs1 ",
1097 2);
1098
1099 check_one_card(qts, false, "standby0", MAC_STANDBY0);
1100 check_one_card(qts, false, "primary0", MAC_PRIMARY0);
1101
1102 qtest_qmp_device_add(qts, "virtio-net", "standby0",
1103 "{'bus': 'root0',"
1104 "'failover': 'on',"
1105 "'netdev': 'hs0',"
1106 "'mac': '"MAC_STANDBY0"'}");
1107
1108 check_one_card(qts, true, "standby0", MAC_STANDBY0);
1109 check_one_card(qts, false, "primary0", MAC_PRIMARY0);
1110
1111 qtest_qmp_device_add(qts, "virtio-net", "primary0",
1112 "{'bus': 'root1',"
1113 "'failover_pair_id': 'standby0',"
1114 "'netdev': 'hs1',"
1115 "'rombar': 0,"
1116 "'romfile': '',"
1117 "'mac': '"MAC_PRIMARY0"'}");
1118
1119 check_one_card(qts, true, "standby0", MAC_STANDBY0);
1120 check_one_card(qts, false, "primary0", MAC_PRIMARY0);
1121
1122 features = ~(QVIRTIO_F_BAD_FEATURE |
1123 (1ull << VIRTIO_RING_F_INDIRECT_DESC) |
1124 (1ull << VIRTIO_RING_F_EVENT_IDX) |
1125 (1ull << VIRTIO_NET_F_STANDBY));
1126
1127 vdev = start_virtio_net_internal(qts, 1, 0, &features);
1128
1129 check_one_card(qts, true, "standby0", MAC_STANDBY0);
1130 check_one_card(qts, false, "primary0", MAC_PRIMARY0);
1131
1132 args = qdict_from_jsonf_nofail("{}");
1133 g_assert_nonnull(args);
1134 qdict_put_str(args, "uri", uri);
1135
1136 resp = qtest_qmp(qts, "{ 'execute': 'migrate', 'arguments': %p}", args);
1137 g_assert(qdict_haskey(resp, "return"));
1138 qobject_unref(resp);
1139
1140 while (true) {
1141 ret = migrate_status(qts);
1142
1143 status = qdict_get_str(ret, "status");
1144 if (strcmp(status, "completed") == 0) {
1145 g_test_skip("Failed to cancel the migration");
1146 qobject_unref(ret);
1147 goto out;
1148 }
1149 if (strcmp(status, "active") == 0) {
1150 qobject_unref(ret);
1151 break;
1152 }
1153 g_assert_cmpstr(status, !=, "failed");
1154 qobject_unref(ret);
1155 }
1156
1157 resp = qtest_qmp(qts, "{ 'execute': 'migrate_cancel' }");
1158 g_assert(qdict_haskey(resp, "return"));
1159 qobject_unref(resp);
1160
1161 while (true) {
1162 ret = migrate_status(qts);
1163 status = qdict_get_str(ret, "status");
1164 if (strcmp(status, "completed") == 0) {
1165 g_test_skip("Failed to cancel the migration");
1166 qobject_unref(ret);
1167 goto out;
1168 }
1169 if (strcmp(status, "cancelled") == 0) {
1170 qobject_unref(ret);
1171 break;
1172 }
1173 g_assert_cmpstr(status, !=, "failed");
1174 g_assert_cmpstr(status, !=, "active");
1175 qobject_unref(ret);
1176 }
1177
1178 check_one_card(qts, true, "standby0", MAC_STANDBY0);
1179 check_one_card(qts, false, "primary0", MAC_PRIMARY0);
1180
1181out:
1182 qos_object_destroy((QOSGraphObject *)vdev);
1183 machine_stop(qts);
1184}
1185
1186static void test_migrate_abort_wait_unplug(gconstpointer opaque)
1187{
1188 QTestState *qts;
1189 QDict *resp, *args, *ret;
1190 g_autofree gchar *uri = g_strdup_printf("exec: cat > %s", (gchar *)opaque);
1191 const gchar *status;
1192 QVirtioPCIDevice *vdev;
1193
1194 qts = machine_start(BASE_MACHINE
1195 "-netdev user,id=hs0 "
1196 "-netdev user,id=hs1 ",
1197 2);
1198
1199 check_one_card(qts, false, "standby0", MAC_STANDBY0);
1200 check_one_card(qts, false, "primary0", MAC_PRIMARY0);
1201
1202 qtest_qmp_device_add(qts, "virtio-net", "standby0",
1203 "{'bus': 'root0',"
1204 "'failover': 'on',"
1205 "'netdev': 'hs0',"
1206 "'mac': '"MAC_STANDBY0"'}");
1207
1208 check_one_card(qts, true, "standby0", MAC_STANDBY0);
1209 check_one_card(qts, false, "primary0", MAC_PRIMARY0);
1210
1211 vdev = start_virtio_net(qts, 1, 0, "standby0", true);
1212
1213 check_one_card(qts, true, "standby0", MAC_STANDBY0);
1214 check_one_card(qts, false, "primary0", MAC_PRIMARY0);
1215
1216 qtest_qmp_device_add(qts, "virtio-net", "primary0",
1217 "{'bus': 'root1',"
1218 "'failover_pair_id': 'standby0',"
1219 "'netdev': 'hs1',"
1220 "'rombar': 0,"
1221 "'romfile': '',"
1222 "'mac': '"MAC_PRIMARY0"'}");
1223
1224 check_one_card(qts, true, "standby0", MAC_STANDBY0);
1225 check_one_card(qts, true, "primary0", MAC_PRIMARY0);
1226
1227 args = qdict_from_jsonf_nofail("{}");
1228 g_assert_nonnull(args);
1229 qdict_put_str(args, "uri", uri);
1230
1231 resp = qtest_qmp(qts, "{ 'execute': 'migrate', 'arguments': %p}", args);
1232 g_assert(qdict_haskey(resp, "return"));
1233 qobject_unref(resp);
1234
1235
1236 resp = get_unplug_primary_event(qts);
1237 g_assert_cmpstr(qdict_get_str(resp, "device-id"), ==, "primary0");
1238 qobject_unref(resp);
1239
1240 resp = qtest_qmp(qts, "{ 'execute': 'migrate_cancel' }");
1241 g_assert(qdict_haskey(resp, "return"));
1242 qobject_unref(resp);
1243
1244
1245
1246
1247 ret = migrate_status(qts);
1248
1249 status = qdict_get_str(ret, "status");
1250 g_assert_cmpstr(status, ==, "cancelling");
1251 qobject_unref(ret);
1252
1253
1254 qtest_outl(qts, ACPI_PCIHP_ADDR_ICH9 + PCI_EJ_BASE, 1);
1255
1256 while (true) {
1257 ret = migrate_status(qts);
1258
1259 status = qdict_get_str(ret, "status");
1260 if (strcmp(status, "cancelled") == 0) {
1261 qobject_unref(ret);
1262 break;
1263 }
1264 g_assert_cmpstr(status, ==, "cancelling");
1265 qobject_unref(ret);
1266 }
1267
1268 check_one_card(qts, true, "standby0", MAC_STANDBY0);
1269 check_one_card(qts, true, "primary0", MAC_PRIMARY0);
1270
1271 qos_object_destroy((QOSGraphObject *)vdev);
1272 machine_stop(qts);
1273}
1274
1275static void test_migrate_abort_active(gconstpointer opaque)
1276{
1277 QTestState *qts;
1278 QDict *resp, *args, *ret;
1279 g_autofree gchar *uri = g_strdup_printf("exec: cat > %s", (gchar *)opaque);
1280 const gchar *status;
1281 QVirtioPCIDevice *vdev;
1282
1283 qts = machine_start(BASE_MACHINE
1284 "-netdev user,id=hs0 "
1285 "-netdev user,id=hs1 ",
1286 2);
1287
1288 check_one_card(qts, false, "standby0", MAC_STANDBY0);
1289 check_one_card(qts, false, "primary0", MAC_PRIMARY0);
1290
1291 qtest_qmp_device_add(qts, "virtio-net", "standby0",
1292 "{'bus': 'root0',"
1293 "'failover': 'on',"
1294 "'netdev': 'hs0',"
1295 "'mac': '"MAC_STANDBY0"'}");
1296
1297 check_one_card(qts, true, "standby0", MAC_STANDBY0);
1298 check_one_card(qts, false, "primary0", MAC_PRIMARY0);
1299
1300 vdev = start_virtio_net(qts, 1, 0, "standby0", true);
1301
1302 check_one_card(qts, true, "standby0", MAC_STANDBY0);
1303 check_one_card(qts, false, "primary0", MAC_PRIMARY0);
1304
1305 qtest_qmp_device_add(qts, "virtio-net", "primary0",
1306 "{'bus': 'root1',"
1307 "'failover_pair_id': 'standby0',"
1308 "'netdev': 'hs1',"
1309 "'rombar': 0,"
1310 "'romfile': '',"
1311 "'mac': '"MAC_PRIMARY0"'}");
1312
1313 check_one_card(qts, true, "standby0", MAC_STANDBY0);
1314 check_one_card(qts, true, "primary0", MAC_PRIMARY0);
1315
1316 args = qdict_from_jsonf_nofail("{}");
1317 g_assert_nonnull(args);
1318 qdict_put_str(args, "uri", uri);
1319
1320 resp = qtest_qmp(qts, "{ 'execute': 'migrate', 'arguments': %p}", args);
1321 g_assert(qdict_haskey(resp, "return"));
1322 qobject_unref(resp);
1323
1324
1325 resp = get_unplug_primary_event(qts);
1326 g_assert_cmpstr(qdict_get_str(resp, "device-id"), ==, "primary0");
1327 qobject_unref(resp);
1328
1329
1330 qtest_outl(qts, ACPI_PCIHP_ADDR_ICH9 + PCI_EJ_BASE, 1);
1331
1332 while (true) {
1333 ret = migrate_status(qts);
1334
1335 status = qdict_get_str(ret, "status");
1336 g_assert_cmpstr(status, !=, "failed");
1337 if (strcmp(status, "wait-unplug") != 0) {
1338 qobject_unref(ret);
1339 break;
1340 }
1341 qobject_unref(ret);
1342 }
1343
1344 resp = qtest_qmp(qts, "{ 'execute': 'migrate_cancel' }");
1345 g_assert(qdict_haskey(resp, "return"));
1346 qobject_unref(resp);
1347
1348 while (true) {
1349 ret = migrate_status(qts);
1350
1351 status = qdict_get_str(ret, "status");
1352 if (strcmp(status, "completed") == 0) {
1353 g_test_skip("Failed to cancel the migration");
1354 qobject_unref(ret);
1355 goto out;
1356 }
1357 if (strcmp(status, "cancelled") == 0) {
1358 qobject_unref(ret);
1359 break;
1360 }
1361 g_assert_cmpstr(status, !=, "failed");
1362 g_assert_cmpstr(status, !=, "active");
1363 qobject_unref(ret);
1364 }
1365
1366 check_one_card(qts, true, "standby0", MAC_STANDBY0);
1367 check_one_card(qts, true, "primary0", MAC_PRIMARY0);
1368
1369out:
1370 qos_object_destroy((QOSGraphObject *)vdev);
1371 machine_stop(qts);
1372}
1373
1374static void test_migrate_off_abort(gconstpointer opaque)
1375{
1376 QTestState *qts;
1377 QDict *resp, *args, *ret;
1378 g_autofree gchar *uri = g_strdup_printf("exec: cat > %s", (gchar *)opaque);
1379 const gchar *status;
1380 QVirtioPCIDevice *vdev;
1381
1382 qts = machine_start(BASE_MACHINE
1383 "-netdev user,id=hs0 "
1384 "-netdev user,id=hs1 ",
1385 2);
1386
1387 check_one_card(qts, false, "standby0", MAC_STANDBY0);
1388 check_one_card(qts, false, "primary0", MAC_PRIMARY0);
1389
1390 qtest_qmp_device_add(qts, "virtio-net", "standby0",
1391 "{'bus': 'root0',"
1392 "'failover': 'off',"
1393 "'netdev': 'hs0',"
1394 "'mac': '"MAC_STANDBY0"'}");
1395
1396 check_one_card(qts, true, "standby0", MAC_STANDBY0);
1397 check_one_card(qts, false, "primary0", MAC_PRIMARY0);
1398
1399 vdev = start_virtio_net(qts, 1, 0, "standby0", false);
1400
1401 check_one_card(qts, true, "standby0", MAC_STANDBY0);
1402 check_one_card(qts, false, "primary0", MAC_PRIMARY0);
1403
1404 qtest_qmp_device_add(qts, "virtio-net", "primary0",
1405 "{'bus': 'root1',"
1406 "'failover_pair_id': 'standby0',"
1407 "'netdev': 'hs1',"
1408 "'rombar': 0,"
1409 "'romfile': '',"
1410 "'mac': '"MAC_PRIMARY0"'}");
1411
1412 check_one_card(qts, true, "standby0", MAC_STANDBY0);
1413 check_one_card(qts, true, "primary0", MAC_PRIMARY0);
1414
1415 args = qdict_from_jsonf_nofail("{}");
1416 g_assert_nonnull(args);
1417 qdict_put_str(args, "uri", uri);
1418
1419 resp = qtest_qmp(qts, "{ 'execute': 'migrate', 'arguments': %p}", args);
1420 g_assert(qdict_haskey(resp, "return"));
1421 qobject_unref(resp);
1422
1423 while (true) {
1424 ret = migrate_status(qts);
1425
1426 status = qdict_get_str(ret, "status");
1427 if (strcmp(status, "active") == 0) {
1428 qobject_unref(ret);
1429 break;
1430 }
1431 g_assert_cmpstr(status, !=, "failed");
1432 qobject_unref(ret);
1433 }
1434
1435 resp = qtest_qmp(qts, "{ 'execute': 'migrate_cancel' }");
1436 g_assert(qdict_haskey(resp, "return"));
1437 qobject_unref(resp);
1438
1439 while (true) {
1440 ret = migrate_status(qts);
1441
1442 status = qdict_get_str(ret, "status");
1443 if (strcmp(status, "completed") == 0) {
1444 g_test_skip("Failed to cancel the migration");
1445 qobject_unref(ret);
1446 goto out;
1447 }
1448 if (strcmp(status, "cancelled") == 0) {
1449 qobject_unref(ret);
1450 break;
1451 }
1452 g_assert_cmpstr(status, !=, "failed");
1453 g_assert_cmpstr(status, !=, "active");
1454 qobject_unref(ret);
1455 }
1456
1457 check_one_card(qts, true, "standby0", MAC_STANDBY0);
1458 check_one_card(qts, true, "primary0", MAC_PRIMARY0);
1459
1460out:
1461 qos_object_destroy((QOSGraphObject *)vdev);
1462 machine_stop(qts);
1463}
1464
1465static void test_migrate_abort_timeout(gconstpointer opaque)
1466{
1467 QTestState *qts;
1468 QDict *resp, *args, *ret;
1469 g_autofree gchar *uri = g_strdup_printf("exec: cat > %s", (gchar *)opaque);
1470 const gchar *status;
1471 int total;
1472 QVirtioPCIDevice *vdev;
1473
1474 qts = machine_start(BASE_MACHINE
1475 "-netdev user,id=hs0 "
1476 "-netdev user,id=hs1 ",
1477 2);
1478
1479 check_one_card(qts, false, "standby0", MAC_STANDBY0);
1480 check_one_card(qts, false, "primary0", MAC_PRIMARY0);
1481
1482 qtest_qmp_device_add(qts, "virtio-net", "standby0",
1483 "{'bus': 'root0',"
1484 "'failover': 'on',"
1485 "'netdev': 'hs0',"
1486 "'mac': '"MAC_STANDBY0"'}");
1487
1488 check_one_card(qts, true, "standby0", MAC_STANDBY0);
1489 check_one_card(qts, false, "primary0", MAC_PRIMARY0);
1490
1491 vdev = start_virtio_net(qts, 1, 0, "standby0", true);
1492
1493 check_one_card(qts, true, "standby0", MAC_STANDBY0);
1494 check_one_card(qts, false, "primary0", MAC_PRIMARY0);
1495
1496 qtest_qmp_device_add(qts, "virtio-net", "primary0",
1497 "{'bus': 'root1',"
1498 "'failover_pair_id': 'standby0',"
1499 "'netdev': 'hs1',"
1500 "'rombar': 0,"
1501 "'romfile': '',"
1502 "'mac': '"MAC_PRIMARY0"'}");
1503
1504 check_one_card(qts, true, "standby0", MAC_STANDBY0);
1505 check_one_card(qts, true, "primary0", MAC_PRIMARY0);
1506
1507 args = qdict_from_jsonf_nofail("{}");
1508 g_assert_nonnull(args);
1509 qdict_put_str(args, "uri", uri);
1510
1511 resp = qtest_qmp(qts, "{ 'execute': 'migrate', 'arguments': %p}", args);
1512 g_assert(qdict_haskey(resp, "return"));
1513 qobject_unref(resp);
1514
1515
1516 resp = get_unplug_primary_event(qts);
1517 g_assert_cmpstr(qdict_get_str(resp, "device-id"), ==, "primary0");
1518 qobject_unref(resp);
1519
1520 resp = qtest_qmp(qts, "{ 'execute': 'migrate_cancel' }");
1521 g_assert(qdict_haskey(resp, "return"));
1522 qobject_unref(resp);
1523
1524
1525
1526
1527
1528 total = 0;
1529 while (true) {
1530 ret = migrate_status(qts);
1531
1532 status = qdict_get_str(ret, "status");
1533 if (strcmp(status, "cancelled") == 0) {
1534 qobject_unref(ret);
1535 break;
1536 }
1537 g_assert_cmpstr(status, ==, "cancelling");
1538 g_assert(qdict_haskey(ret, "total-time"));
1539 total = qdict_get_int(ret, "total-time");
1540 qobject_unref(ret);
1541 }
1542
1543
1544
1545
1546
1547 g_assert_cmpint(total, >, 30000);
1548
1549 check_one_card(qts, true, "standby0", MAC_STANDBY0);
1550 check_one_card(qts, true, "primary0", MAC_PRIMARY0);
1551
1552 qos_object_destroy((QOSGraphObject *)vdev);
1553 machine_stop(qts);
1554}
1555
1556static void test_multi_out(gconstpointer opaque)
1557{
1558 QTestState *qts;
1559 QDict *resp, *args, *ret;
1560 g_autofree gchar *uri = g_strdup_printf("exec: cat > %s", (gchar *)opaque);
1561 const gchar *status, *expected;
1562 QVirtioPCIDevice *vdev0, *vdev1;
1563
1564 qts = machine_start(BASE_MACHINE
1565 "-device pcie-root-port,id=root2,addr=0x3,bus=pcie.0,chassis=3 "
1566 "-device pcie-root-port,id=root3,addr=0x4,bus=pcie.0,chassis=4 "
1567 "-netdev user,id=hs0 "
1568 "-netdev user,id=hs1 "
1569 "-netdev user,id=hs2 "
1570 "-netdev user,id=hs3 ",
1571 4);
1572
1573 check_one_card(qts, false, "standby0", MAC_STANDBY0);
1574 check_one_card(qts, false, "primary0", MAC_PRIMARY0);
1575 check_one_card(qts, false, "standby1", MAC_STANDBY1);
1576 check_one_card(qts, false, "primary1", MAC_PRIMARY1);
1577
1578 qtest_qmp_device_add(qts, "virtio-net", "standby0",
1579 "{'bus': 'root0',"
1580 "'failover': 'on',"
1581 "'netdev': 'hs0',"
1582 "'mac': '"MAC_STANDBY0"'}");
1583
1584 check_one_card(qts, true, "standby0", MAC_STANDBY0);
1585 check_one_card(qts, false, "primary0", MAC_PRIMARY0);
1586 check_one_card(qts, false, "standby1", MAC_STANDBY1);
1587 check_one_card(qts, false, "primary1", MAC_PRIMARY1);
1588
1589 qtest_qmp_device_add(qts, "virtio-net", "primary0",
1590 "{'bus': 'root1',"
1591 "'failover_pair_id': 'standby0',"
1592 "'netdev': 'hs1',"
1593 "'rombar': 0,"
1594 "'romfile': '',"
1595 "'mac': '"MAC_PRIMARY0"'}");
1596
1597 check_one_card(qts, true, "standby0", MAC_STANDBY0);
1598 check_one_card(qts, false, "primary0", MAC_PRIMARY0);
1599 check_one_card(qts, false, "standby1", MAC_STANDBY1);
1600 check_one_card(qts, false, "primary1", MAC_PRIMARY1);
1601
1602 vdev0 = start_virtio_net(qts, 1, 0, "standby0", true);
1603
1604 check_one_card(qts, true, "standby0", MAC_STANDBY0);
1605 check_one_card(qts, true, "primary0", MAC_PRIMARY0);
1606 check_one_card(qts, false, "standby1", MAC_STANDBY1);
1607 check_one_card(qts, false, "primary1", MAC_PRIMARY1);
1608
1609 qtest_qmp_device_add(qts, "virtio-net", "standby1",
1610 "{'bus': 'root2',"
1611 "'failover': 'on',"
1612 "'netdev': 'hs2',"
1613 "'mac': '"MAC_STANDBY1"'}");
1614
1615 check_one_card(qts, true, "standby0", MAC_STANDBY0);
1616 check_one_card(qts, true, "primary0", MAC_PRIMARY0);
1617 check_one_card(qts, true, "standby1", MAC_STANDBY1);
1618 check_one_card(qts, false, "primary1", MAC_PRIMARY1);
1619
1620 qtest_qmp_device_add(qts, "virtio-net", "primary1",
1621 "{'bus': 'root3',"
1622 "'failover_pair_id': 'standby1',"
1623 "'netdev': 'hs3',"
1624 "'rombar': 0,"
1625 "'romfile': '',"
1626 "'mac': '"MAC_PRIMARY1"'}");
1627
1628 check_one_card(qts, true, "standby0", MAC_STANDBY0);
1629 check_one_card(qts, true, "primary0", MAC_PRIMARY0);
1630 check_one_card(qts, true, "standby1", MAC_STANDBY1);
1631 check_one_card(qts, false, "primary1", MAC_PRIMARY1);
1632
1633 vdev1 = start_virtio_net(qts, 3, 0, "standby1", true);
1634
1635 check_one_card(qts, true, "standby0", MAC_STANDBY0);
1636 check_one_card(qts, true, "primary0", MAC_PRIMARY0);
1637 check_one_card(qts, true, "standby1", MAC_STANDBY1);
1638 check_one_card(qts, true, "primary1", MAC_PRIMARY1);
1639
1640 args = qdict_from_jsonf_nofail("{}");
1641 g_assert_nonnull(args);
1642 qdict_put_str(args, "uri", uri);
1643
1644 resp = qtest_qmp(qts, "{ 'execute': 'migrate', 'arguments': %p}", args);
1645 g_assert(qdict_haskey(resp, "return"));
1646 qobject_unref(resp);
1647
1648
1649 resp = get_unplug_primary_event(qts);
1650 if (strcmp(qdict_get_str(resp, "device-id"), "primary0") == 0) {
1651 expected = "primary1";
1652 } else if (strcmp(qdict_get_str(resp, "device-id"), "primary1") == 0) {
1653 expected = "primary0";
1654 } else {
1655 g_assert_not_reached();
1656 }
1657 qobject_unref(resp);
1658
1659 resp = get_unplug_primary_event(qts);
1660 g_assert_cmpstr(qdict_get_str(resp, "device-id"), ==, expected);
1661 qobject_unref(resp);
1662
1663
1664 while (true) {
1665 ret = migrate_status(qts);
1666
1667 status = qdict_get_str(ret, "status");
1668 if (strcmp(status, "wait-unplug") == 0) {
1669 qobject_unref(ret);
1670 break;
1671 }
1672
1673
1674 g_assert_cmpstr(status, !=, "active");
1675 g_assert_cmpstr(status, !=, "completed");
1676 g_assert_cmpstr(status, !=, "failed");
1677 g_assert_cmpstr(status, !=, "cancelling");
1678 g_assert_cmpstr(status, !=, "cancelled");
1679
1680 qobject_unref(ret);
1681 }
1682
1683
1684 qtest_outl(qts, ACPI_PCIHP_ADDR_ICH9 + PCI_EJ_BASE, 1);
1685
1686 ret = migrate_status(qts);
1687 status = qdict_get_str(ret, "status");
1688 g_assert_cmpstr(status, ==, "wait-unplug");
1689 qobject_unref(ret);
1690
1691 if (g_test_slow()) {
1692
1693 for (int i = 0; i < 5; i++) {
1694 sleep(1);
1695 ret = migrate_status(qts);
1696 status = qdict_get_str(ret, "status");
1697 g_assert_cmpstr(status, ==, "wait-unplug");
1698 qobject_unref(ret);
1699 }
1700 }
1701
1702
1703 qtest_outl(qts, ACPI_PCIHP_ADDR_ICH9 + PCI_SEL_BASE, 2);
1704 qtest_outl(qts, ACPI_PCIHP_ADDR_ICH9 + PCI_EJ_BASE, 1);
1705
1706 while (true) {
1707 ret = migrate_status(qts);
1708
1709 status = qdict_get_str(ret, "status");
1710 if (strcmp(status, "completed") == 0) {
1711 qobject_unref(ret);
1712 break;
1713 }
1714 g_assert_cmpstr(status, !=, "failed");
1715 g_assert_cmpstr(status, !=, "cancelling");
1716 g_assert_cmpstr(status, !=, "cancelled");
1717 qobject_unref(ret);
1718 }
1719
1720 qtest_qmp_eventwait(qts, "STOP");
1721
1722 qos_object_destroy((QOSGraphObject *)vdev0);
1723 qos_object_destroy((QOSGraphObject *)vdev1);
1724 machine_stop(qts);
1725}
1726
1727static void test_multi_in(gconstpointer opaque)
1728{
1729 QTestState *qts;
1730 QDict *resp, *args, *ret;
1731 g_autofree gchar *uri = g_strdup_printf("exec: cat %s", (gchar *)opaque);
1732
1733 qts = machine_start(BASE_MACHINE
1734 "-device pcie-root-port,id=root2,addr=0x3,bus=pcie.0,chassis=3 "
1735 "-device pcie-root-port,id=root3,addr=0x4,bus=pcie.0,chassis=4 "
1736 "-netdev user,id=hs0 "
1737 "-netdev user,id=hs1 "
1738 "-netdev user,id=hs2 "
1739 "-netdev user,id=hs3 "
1740 "-incoming defer ",
1741 4);
1742
1743 check_one_card(qts, false, "standby0", MAC_STANDBY0);
1744 check_one_card(qts, false, "primary0", MAC_PRIMARY0);
1745 check_one_card(qts, false, "standby1", MAC_STANDBY1);
1746 check_one_card(qts, false, "primary1", MAC_PRIMARY1);
1747
1748 qtest_qmp_device_add(qts, "virtio-net", "standby0",
1749 "{'bus': 'root0',"
1750 "'failover': 'on',"
1751 "'netdev': 'hs0',"
1752 "'mac': '"MAC_STANDBY0"'}");
1753
1754 check_one_card(qts, true, "standby0", MAC_STANDBY0);
1755 check_one_card(qts, false, "primary0", MAC_PRIMARY0);
1756 check_one_card(qts, false, "standby1", MAC_STANDBY1);
1757 check_one_card(qts, false, "primary1", MAC_PRIMARY1);
1758
1759 qtest_qmp_device_add(qts, "virtio-net", "primary0",
1760 "{'bus': 'root1',"
1761 "'failover_pair_id': 'standby0',"
1762 "'netdev': 'hs1',"
1763 "'rombar': 0,"
1764 "'romfile': '',"
1765 "'mac': '"MAC_PRIMARY0"'}");
1766
1767 check_one_card(qts, true, "standby0", MAC_STANDBY0);
1768 check_one_card(qts, false, "primary0", MAC_PRIMARY0);
1769 check_one_card(qts, false, "standby1", MAC_STANDBY1);
1770 check_one_card(qts, false, "primary1", MAC_PRIMARY1);
1771
1772 qtest_qmp_device_add(qts, "virtio-net", "standby1",
1773 "{'bus': 'root2',"
1774 "'failover': 'on',"
1775 "'netdev': 'hs2',"
1776 "'mac': '"MAC_STANDBY1"'}");
1777
1778 check_one_card(qts, true, "standby0", MAC_STANDBY0);
1779 check_one_card(qts, false, "primary0", MAC_PRIMARY0);
1780 check_one_card(qts, true, "standby1", MAC_STANDBY1);
1781 check_one_card(qts, false, "primary1", MAC_PRIMARY1);
1782
1783 qtest_qmp_device_add(qts, "virtio-net", "primary1",
1784 "{'bus': 'root3',"
1785 "'failover_pair_id': 'standby1',"
1786 "'netdev': 'hs3',"
1787 "'rombar': 0,"
1788 "'romfile': '',"
1789 "'mac': '"MAC_PRIMARY1"'}");
1790
1791 check_one_card(qts, true, "standby0", MAC_STANDBY0);
1792 check_one_card(qts, false, "primary0", MAC_PRIMARY0);
1793 check_one_card(qts, true, "standby1", MAC_STANDBY1);
1794 check_one_card(qts, false, "primary1", MAC_PRIMARY1);
1795
1796 args = qdict_from_jsonf_nofail("{}");
1797 g_assert_nonnull(args);
1798 qdict_put_str(args, "uri", uri);
1799
1800 resp = qtest_qmp(qts, "{ 'execute': 'migrate-incoming', 'arguments': %p}",
1801 args);
1802 g_assert(qdict_haskey(resp, "return"));
1803 qobject_unref(resp);
1804
1805 resp = get_migration_event(qts);
1806 g_assert_cmpstr(qdict_get_str(resp, "status"), ==, "setup");
1807 qobject_unref(resp);
1808
1809 resp = get_failover_negociated_event(qts);
1810 g_assert_cmpstr(qdict_get_str(resp, "device-id"), ==, "standby0");
1811 qobject_unref(resp);
1812
1813 resp = get_failover_negociated_event(qts);
1814 g_assert_cmpstr(qdict_get_str(resp, "device-id"), ==, "standby1");
1815 qobject_unref(resp);
1816
1817 check_one_card(qts, true, "standby0", MAC_STANDBY0);
1818 check_one_card(qts, true, "primary0", MAC_PRIMARY0);
1819 check_one_card(qts, true, "standby1", MAC_STANDBY1);
1820 check_one_card(qts, true, "primary1", MAC_PRIMARY1);
1821
1822 qtest_qmp_eventwait(qts, "RESUME");
1823
1824 ret = migrate_status(qts);
1825 g_assert_cmpstr(qdict_get_str(ret, "status"), ==, "completed");
1826 qobject_unref(ret);
1827
1828 machine_stop(qts);
1829}
1830
1831int main(int argc, char **argv)
1832{
1833 gchar *tmpfile;
1834 int ret;
1835
1836 g_test_init(&argc, &argv, NULL);
1837
1838 ret = g_file_open_tmp("failover_test_migrate-XXXXXX", &tmpfile, NULL);
1839 g_assert_true(ret >= 0);
1840 close(ret);
1841
1842
1843 qtest_add_func("failover-virtio-net/params/error/id", test_error_id);
1844 qtest_add_func("failover-virtio-net/params/error/pcie", test_error_pcie);
1845 qtest_add_func("failover-virtio-net/params/on", test_on);
1846 qtest_add_func("failover-virtio-net/params/on_mismatch",
1847 test_on_mismatch);
1848 qtest_add_func("failover-virtio-net/params/off", test_off);
1849 qtest_add_func("failover-virtio-net/params/enabled", test_enabled);
1850 qtest_add_func("failover-virtio-net/params/guest_off", test_guest_off);
1851
1852
1853 qtest_add_func("failover-virtio-net/hotplug/1", test_hotplug_1);
1854 qtest_add_func("failover-virtio-net/hotplug/1_reverse",
1855 test_hotplug_1_reverse);
1856 qtest_add_func("failover-virtio-net/hotplug/2", test_hotplug_2);
1857 qtest_add_func("failover-virtio-net/hotplug/2_reverse",
1858 test_hotplug_2_reverse);
1859
1860
1861 qtest_add_data_func("failover-virtio-net/migrate/on/out", tmpfile,
1862 test_migrate_out);
1863 qtest_add_data_func("failover-virtio-net/migrate/on/in", tmpfile,
1864 test_migrate_in);
1865 qtest_add_data_func("failover-virtio-net/migrate/off/out", tmpfile,
1866 test_off_migrate_out);
1867 qtest_add_data_func("failover-virtio-net/migrate/off/in", tmpfile,
1868 test_off_migrate_in);
1869 qtest_add_data_func("failover-virtio-net/migrate/off/abort", tmpfile,
1870 test_migrate_off_abort);
1871 qtest_add_data_func("failover-virtio-net/migrate/guest_off/out", tmpfile,
1872 test_guest_off_migrate_out);
1873 qtest_add_data_func("failover-virtio-net/migrate/guest_off/in", tmpfile,
1874 test_guest_off_migrate_in);
1875 qtest_add_data_func("failover-virtio-net/migrate/guest_off/abort", tmpfile,
1876 test_migrate_guest_off_abort);
1877 qtest_add_data_func("failover-virtio-net/migrate/abort/wait-unplug",
1878 tmpfile, test_migrate_abort_wait_unplug);
1879 qtest_add_data_func("failover-virtio-net/migrate/abort/active", tmpfile,
1880 test_migrate_abort_active);
1881 if (g_test_slow()) {
1882 qtest_add_data_func("failover-virtio-net/migrate/abort/timeout",
1883 tmpfile, test_migrate_abort_timeout);
1884 }
1885 qtest_add_data_func("failover-virtio-net/migrate/multi/out",
1886 tmpfile, test_multi_out);
1887 qtest_add_data_func("failover-virtio-net/migrate/multi/in",
1888 tmpfile, test_multi_in);
1889
1890 ret = g_test_run();
1891
1892 unlink(tmpfile);
1893 g_free(tmpfile);
1894
1895 return ret;
1896}
1897