2 * QTest testcase for virtio-net failover
4 * See docs/system/virtio-net-failover.rst
6 * Copyright (c) 2021 Red Hat, Inc.
8 * SPDX-License-Identifier: GPL-2.0-or-later
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"
21 #define ACPI_PCIHP_ADDR_ICH9 0x0cc0
22 #define PCI_EJ_BASE 0x0008
23 #define PCI_SEL_BASE 0x0010
25 #define BASE_MACHINE "-M q35 -nodefaults " \
26 "-device pcie-root-port,id=root0,addr=0x1,bus=pcie.0,chassis=1 " \
27 "-device pcie-root-port,id=root1,addr=0x2,bus=pcie.0,chassis=2 "
29 #define MAC_PRIMARY0 "52:54:00:11:11:11"
30 #define MAC_STANDBY0 "52:54:00:22:22:22"
31 #define MAC_PRIMARY1 "52:54:00:33:33:33"
32 #define MAC_STANDBY1 "52:54:00:44:44:44"
34 static QGuestAllocator guest_malloc
;
35 static QPCIBus
*pcibus
;
37 static QTestState
*machine_start(const char *args
, int numbus
)
43 qts
= qtest_init(args
);
45 pc_alloc_init(&guest_malloc
, qts
, 0);
46 pcibus
= qpci_new_pc(qts
, &guest_malloc
);
47 g_assert(qpci_secondary_buses_init(pcibus
) == numbus
);
49 for (bus
= 1; bus
<= numbus
; bus
++) {
50 dev
= qpci_device_find(pcibus
, QPCI_DEVFN(bus
, 0));
51 g_assert_nonnull(dev
);
53 qpci_device_enable(dev
);
54 qpci_iomap(dev
, 4, NULL
);
62 static void machine_stop(QTestState
*qts
)
65 alloc_destroy(&guest_malloc
);
69 static void test_error_id(void)
75 qts
= machine_start(BASE_MACHINE
76 "-device virtio-net,bus=root0,id=standby0,failover=on",
79 resp
= qtest_qmp(qts
, "{'execute': 'device_add',"
81 "'driver': 'virtio-net',"
83 "'failover_pair_id': 'standby0'"
85 g_assert(qdict_haskey(resp
, "error"));
87 err
= qdict_get_qdict(resp
, "error");
88 g_assert(qdict_haskey(err
, "desc"));
90 g_assert_cmpstr(qdict_get_str(err
, "desc"), ==,
91 "Device with failover_pair_id needs to have id");
98 static void test_error_pcie(void)
104 qts
= machine_start(BASE_MACHINE
105 "-device virtio-net,bus=root0,id=standby0,failover=on",
108 resp
= qtest_qmp(qts
, "{'execute': 'device_add',"
110 "'driver': 'virtio-net',"
113 "'failover_pair_id': 'standby0'"
115 g_assert(qdict_haskey(resp
, "error"));
117 err
= qdict_get_qdict(resp
, "error");
118 g_assert(qdict_haskey(err
, "desc"));
120 g_assert_cmpstr(qdict_get_str(err
, "desc"), ==,
121 "Bus 'pcie.0' does not support hotplugging");
128 static QDict
*find_device(QDict
*bus
, const char *name
)
134 devices
= qdict_get_qlist(bus
, "devices");
135 if (devices
== NULL
) {
139 list
= qlist_copy(devices
);
140 while ((obj
= qlist_pop(list
))) {
143 device
= qobject_to(QDict
, obj
);
145 if (qdict_haskey(device
, "pci_bridge")) {
147 QDict
*bridge_device
;
149 bridge
= qdict_get_qdict(device
, "pci_bridge");
151 if (qdict_haskey(bridge
, "devices")) {
152 bridge_device
= find_device(bridge
, name
);
154 qobject_unref(device
);
156 return bridge_device
;
161 if (!qdict_haskey(device
, "qdev_id")) {
162 qobject_unref(device
);
166 if (strcmp(qdict_get_str(device
, "qdev_id"), name
) == 0) {
170 qobject_unref(device
);
177 static QDict
*get_bus(QTestState
*qts
, int num
)
183 resp
= qtest_qmp(qts
, "{ 'execute': 'query-pci' }");
184 g_assert(qdict_haskey(resp
, "return"));
186 ret
= qdict_get_qlist(resp
, "return");
187 g_assert_nonnull(ret
);
189 while ((obj
= qlist_pop(ret
))) {
192 bus
= qobject_to(QDict
, obj
);
193 if (!qdict_haskey(bus
, "bus")) {
197 if (qdict_get_int(bus
, "bus") == num
) {
208 static char *get_mac(QTestState
*qts
, const char *name
)
213 resp
= qtest_qmp(qts
, "{ 'execute': 'qom-get', "
216 "'property': 'mac' } }", name
);
218 g_assert(qdict_haskey(resp
, "return"));
220 mac
= g_strdup(qdict_get_str(resp
, "return"));
227 static void check_one_card(QTestState
*qts
, bool present
,
228 const char *id
, const char *mac
)
234 bus
= get_bus(qts
, 0);
235 device
= find_device(bus
, id
);
239 g_assert_nonnull(device
);
240 qobject_unref(device
);
242 path
= g_strdup_printf("/machine/peripheral/%s", id
);
243 addr
= get_mac(qts
, path
);
245 g_assert_cmpstr(mac
, ==, addr
);
248 g_assert_null(device
);
254 static void test_on(void)
258 qts
= machine_start(BASE_MACHINE
259 "-netdev user,id=hs0 "
260 "-device virtio-net,bus=root0,id=standby0,"
261 "failover=on,netdev=hs0,mac="MAC_STANDBY0
" "
262 "-device virtio-net,bus=root1,id=primary0,"
263 "failover_pair_id=standby0,netdev=hs1,mac="MAC_PRIMARY0
,
266 check_one_card(qts
, true, "standby0", MAC_STANDBY0
);
267 check_one_card(qts
, false, "primary0", MAC_PRIMARY0
);
272 static void test_on_mismatch(void)
276 qts
= machine_start(BASE_MACHINE
277 "-netdev user,id=hs0 "
278 "-device virtio-net,bus=root0,id=standby0,"
279 "failover=on,netdev=hs0,mac="MAC_STANDBY0
" "
280 "-netdev user,id=hs1 "
281 "-device virtio-net,bus=root1,id=primary0,"
282 "failover_pair_id=standby1,netdev=hs1,mac="MAC_PRIMARY0
,
285 check_one_card(qts
, true, "standby0", MAC_STANDBY0
);
286 check_one_card(qts
, true, "primary0", MAC_PRIMARY0
);
291 static void test_off(void)
295 qts
= machine_start(BASE_MACHINE
296 "-netdev user,id=hs0 "
297 "-device virtio-net,bus=root0,id=standby0,"
298 "failover=off,netdev=hs0,mac="MAC_STANDBY0
" "
299 "-netdev user,id=hs1 "
300 "-device virtio-net,bus=root1,id=primary0,"
301 "failover_pair_id=standby0,netdev=hs1,mac="MAC_PRIMARY0
,
304 check_one_card(qts
, true, "standby0", MAC_STANDBY0
);
305 check_one_card(qts
, true, "primary0", MAC_PRIMARY0
);
310 static QDict
*get_failover_negociated_event(QTestState
*qts
)
315 resp
= qtest_qmp_eventwait_ref(qts
, "FAILOVER_NEGOTIATED");
316 g_assert(qdict_haskey(resp
, "data"));
318 data
= qdict_get_qdict(resp
, "data");
319 g_assert(qdict_haskey(data
, "device-id"));
326 static QVirtioPCIDevice
*start_virtio_net(QTestState
*qts
, int bus
, int slot
,
329 QVirtioPCIDevice
*dev
;
334 addr
.devfn
= QPCI_DEVFN((bus
<< 5) + slot
, 0);
335 dev
= virtio_pci_new(pcibus
, &addr
);
336 g_assert_nonnull(dev
);
337 qvirtio_pci_device_enable(dev
);
338 qvirtio_start_device(&dev
->vdev
);
339 features
= qvirtio_get_features(&dev
->vdev
);
340 features
= features
& ~(QVIRTIO_F_BAD_FEATURE
|
341 (1ull << VIRTIO_RING_F_INDIRECT_DESC
) |
342 (1ull << VIRTIO_RING_F_EVENT_IDX
));
343 qvirtio_set_features(&dev
->vdev
, features
);
344 qvirtio_set_driver_ok(&dev
->vdev
);
346 resp
= get_failover_negociated_event(qts
);
347 g_assert_cmpstr(qdict_get_str(resp
, "device-id"), ==, id
);
353 static void test_enabled(void)
356 QVirtioPCIDevice
*vdev
;
358 qts
= machine_start(BASE_MACHINE
359 "-netdev user,id=hs0 "
360 "-device virtio-net,bus=root0,id=standby0,"
361 "failover=on,netdev=hs0,mac="MAC_STANDBY0
" "
362 "-netdev user,id=hs1 "
363 "-device virtio-net,bus=root1,id=primary0,"
364 "failover_pair_id=standby0,netdev=hs1,mac="MAC_PRIMARY0
" ",
367 check_one_card(qts
, true, "standby0", MAC_STANDBY0
);
368 check_one_card(qts
, false, "primary0", MAC_PRIMARY0
);
370 vdev
= start_virtio_net(qts
, 1, 0, "standby0");
372 check_one_card(qts
, true, "standby0", MAC_STANDBY0
);
373 check_one_card(qts
, true, "primary0", MAC_PRIMARY0
);
375 qos_object_destroy((QOSGraphObject
*)vdev
);
379 static void test_hotplug_1(void)
382 QVirtioPCIDevice
*vdev
;
384 qts
= machine_start(BASE_MACHINE
385 "-netdev user,id=hs0 "
386 "-device virtio-net,bus=root0,id=standby0,"
387 "failover=on,netdev=hs0,mac="MAC_STANDBY0
" "
388 "-netdev user,id=hs1 ", 2);
390 check_one_card(qts
, true, "standby0", MAC_STANDBY0
);
391 check_one_card(qts
, false, "primary0", MAC_PRIMARY0
);
393 vdev
= start_virtio_net(qts
, 1, 0, "standby0");
395 check_one_card(qts
, true, "standby0", MAC_STANDBY0
);
396 check_one_card(qts
, false, "primary0", MAC_PRIMARY0
);
398 qtest_qmp_device_add(qts
, "virtio-net", "primary0",
400 "'failover_pair_id': 'standby0',"
402 "'mac': '"MAC_PRIMARY0
"'}");
404 check_one_card(qts
, true, "standby0", MAC_STANDBY0
);
405 check_one_card(qts
, true, "primary0", MAC_PRIMARY0
);
407 qos_object_destroy((QOSGraphObject
*)vdev
);
411 static void test_hotplug_1_reverse(void)
414 QVirtioPCIDevice
*vdev
;
416 qts
= machine_start(BASE_MACHINE
417 "-netdev user,id=hs0 "
418 "-netdev user,id=hs1 "
419 "-device virtio-net,bus=root1,id=primary0,"
420 "failover_pair_id=standby0,netdev=hs1,mac="MAC_PRIMARY0
" ",
423 check_one_card(qts
, false, "standby0", MAC_STANDBY0
);
424 check_one_card(qts
, true, "primary0", MAC_PRIMARY0
);
426 qtest_qmp_device_add(qts
, "virtio-net", "standby0",
430 "'mac': '"MAC_STANDBY0
"'}");
432 check_one_card(qts
, true, "standby0", MAC_STANDBY0
);
433 check_one_card(qts
, true, "primary0", MAC_PRIMARY0
);
435 vdev
= start_virtio_net(qts
, 1, 0, "standby0");
437 check_one_card(qts
, true, "standby0", MAC_STANDBY0
);
438 check_one_card(qts
, true, "primary0", MAC_PRIMARY0
);
440 qos_object_destroy((QOSGraphObject
*)vdev
);
444 static void test_hotplug_2(void)
447 QVirtioPCIDevice
*vdev
;
449 qts
= machine_start(BASE_MACHINE
450 "-netdev user,id=hs0 "
451 "-netdev user,id=hs1 ",
454 check_one_card(qts
, false, "standby0", MAC_STANDBY0
);
455 check_one_card(qts
, false, "primary0", MAC_PRIMARY0
);
457 qtest_qmp_device_add(qts
, "virtio-net", "standby0",
461 "'mac': '"MAC_STANDBY0
"'}");
463 check_one_card(qts
, true, "standby0", MAC_STANDBY0
);
464 check_one_card(qts
, false, "primary0", MAC_PRIMARY0
);
466 vdev
= start_virtio_net(qts
, 1, 0, "standby0");
468 check_one_card(qts
, true, "standby0", MAC_STANDBY0
);
469 check_one_card(qts
, false, "primary0", MAC_PRIMARY0
);
471 qtest_qmp_device_add(qts
, "virtio-net", "primary0",
473 "'failover_pair_id': 'standby0',"
475 "'mac': '"MAC_PRIMARY0
"'}");
477 check_one_card(qts
, true, "standby0", MAC_STANDBY0
);
478 check_one_card(qts
, true, "primary0", MAC_PRIMARY0
);
480 qos_object_destroy((QOSGraphObject
*)vdev
);
484 static void test_hotplug_2_reverse(void)
487 QVirtioPCIDevice
*vdev
;
489 qts
= machine_start(BASE_MACHINE
490 "-netdev user,id=hs0 "
491 "-netdev user,id=hs1 ",
494 check_one_card(qts
, false, "standby0", MAC_STANDBY0
);
495 check_one_card(qts
, false, "primary0", MAC_PRIMARY0
);
497 qtest_qmp_device_add(qts
, "virtio-net", "primary0",
499 "'failover_pair_id': 'standby0',"
501 "'mac': '"MAC_PRIMARY0
"'}");
503 check_one_card(qts
, false, "standby0", MAC_STANDBY0
);
504 check_one_card(qts
, true, "primary0", MAC_PRIMARY0
);
506 qtest_qmp_device_add(qts
, "virtio-net", "standby0",
512 "'mac': '"MAC_STANDBY0
"'}");
515 * XXX: sounds like a bug:
516 * The primary should be hidden until the virtio-net driver
517 * negotiates the VIRTIO_NET_F_STANDBY feature by start_virtio_net()
519 check_one_card(qts
, true, "standby0", MAC_STANDBY0
);
520 check_one_card(qts
, true, "primary0", MAC_PRIMARY0
);
522 vdev
= start_virtio_net(qts
, 1, 0, "standby0");
524 check_one_card(qts
, true, "standby0", MAC_STANDBY0
);
525 check_one_card(qts
, true, "primary0", MAC_PRIMARY0
);
527 qos_object_destroy((QOSGraphObject
*)vdev
);
531 static QDict
*migrate_status(QTestState
*qts
)
535 resp
= qtest_qmp(qts
, "{ 'execute': 'query-migrate' }");
536 g_assert(qdict_haskey(resp
, "return"));
538 ret
= qdict_get_qdict(resp
, "return");
539 g_assert(qdict_haskey(ret
, "status"));
546 static QDict
*get_unplug_primary_event(QTestState
*qts
)
551 resp
= qtest_qmp_eventwait_ref(qts
, "UNPLUG_PRIMARY");
552 g_assert(qdict_haskey(resp
, "data"));
554 data
= qdict_get_qdict(resp
, "data");
555 g_assert(qdict_haskey(data
, "device-id"));
562 static void test_migrate_out(gconstpointer opaque
)
565 QDict
*resp
, *args
, *ret
;
566 g_autofree gchar
*uri
= g_strdup_printf("exec: cat > %s", (gchar
*)opaque
);
568 QVirtioPCIDevice
*vdev
;
570 qts
= machine_start(BASE_MACHINE
571 "-netdev user,id=hs0 "
572 "-netdev user,id=hs1 ",
575 check_one_card(qts
, false, "standby0", MAC_STANDBY0
);
576 check_one_card(qts
, false, "primary0", MAC_PRIMARY0
);
578 qtest_qmp_device_add(qts
, "virtio-net", "standby0",
582 "'mac': '"MAC_STANDBY0
"'}");
584 check_one_card(qts
, true, "standby0", MAC_STANDBY0
);
585 check_one_card(qts
, false, "primary0", MAC_PRIMARY0
);
587 vdev
= start_virtio_net(qts
, 1, 0, "standby0");
589 check_one_card(qts
, true, "standby0", MAC_STANDBY0
);
590 check_one_card(qts
, false, "primary0", MAC_PRIMARY0
);
592 qtest_qmp_device_add(qts
, "virtio-net", "primary0",
594 "'failover_pair_id': 'standby0',"
598 "'mac': '"MAC_PRIMARY0
"'}");
600 check_one_card(qts
, true, "standby0", MAC_STANDBY0
);
601 check_one_card(qts
, true, "primary0", MAC_PRIMARY0
);
603 args
= qdict_from_jsonf_nofail("{}");
604 g_assert_nonnull(args
);
605 qdict_put_str(args
, "uri", uri
);
607 resp
= qtest_qmp(qts
, "{ 'execute': 'migrate', 'arguments': %p}", args
);
608 g_assert(qdict_haskey(resp
, "return"));
611 /* the event is sent when QEMU asks the OS to unplug the card */
612 resp
= get_unplug_primary_event(qts
);
613 g_assert_cmpstr(qdict_get_str(resp
, "device-id"), ==, "primary0");
616 /* wait the end of the migration setup phase */
618 ret
= migrate_status(qts
);
620 status
= qdict_get_str(ret
, "status");
621 if (strcmp(status
, "wait-unplug") == 0) {
626 /* The migration must not start if the card is not ejected */
627 g_assert_cmpstr(status
, !=, "active");
628 g_assert_cmpstr(status
, !=, "completed");
629 g_assert_cmpstr(status
, !=, "failed");
630 g_assert_cmpstr(status
, !=, "cancelling");
631 g_assert_cmpstr(status
, !=, "cancelled");
637 /* check we stay in wait-unplug while the card is not ejected */
638 for (int i
= 0; i
< 5; i
++) {
640 ret
= migrate_status(qts
);
641 status
= qdict_get_str(ret
, "status");
642 g_assert_cmpstr(status
, ==, "wait-unplug");
647 /* OS unplugs the cards, QEMU can move from wait-unplug state */
648 qtest_outl(qts
, ACPI_PCIHP_ADDR_ICH9
+ PCI_EJ_BASE
, 1);
651 ret
= migrate_status(qts
);
653 status
= qdict_get_str(ret
, "status");
654 if (strcmp(status
, "completed") == 0) {
658 g_assert_cmpstr(status
, !=, "failed");
659 g_assert_cmpstr(status
, !=, "cancelling");
660 g_assert_cmpstr(status
, !=, "cancelled");
664 qtest_qmp_eventwait(qts
, "STOP");
667 * in fact, the card is ejected from the point of view of kernel
668 * but not really from QEMU to be able to hotplug it back if
669 * migration fails. So we can't check that:
670 * check_one_card(qts, true, "standby0", MAC_STANDBY0);
671 * check_one_card(qts, false, "primary0", MAC_PRIMARY0);
674 qos_object_destroy((QOSGraphObject
*)vdev
);
678 static QDict
*get_migration_event(QTestState
*qts
)
683 resp
= qtest_qmp_eventwait_ref(qts
, "MIGRATION");
684 g_assert(qdict_haskey(resp
, "data"));
686 data
= qdict_get_qdict(resp
, "data");
687 g_assert(qdict_haskey(data
, "status"));
694 static void test_migrate_in(gconstpointer opaque
)
697 QDict
*resp
, *args
, *ret
;
698 g_autofree gchar
*uri
= g_strdup_printf("exec: cat %s", (gchar
*)opaque
);
700 qts
= machine_start(BASE_MACHINE
701 "-netdev user,id=hs0 "
702 "-netdev user,id=hs1 "
706 check_one_card(qts
, false, "standby0", MAC_STANDBY0
);
707 check_one_card(qts
, false, "primary0", MAC_PRIMARY0
);
709 qtest_qmp_device_add(qts
, "virtio-net", "standby0",
713 "'mac': '"MAC_STANDBY0
"'}");
715 check_one_card(qts
, true, "standby0", MAC_STANDBY0
);
716 check_one_card(qts
, false, "primary0", MAC_PRIMARY0
);
718 qtest_qmp_device_add(qts
, "virtio-net", "primary0",
720 "'failover_pair_id': 'standby0',"
724 "'mac': '"MAC_PRIMARY0
"'}");
726 check_one_card(qts
, true, "standby0", MAC_STANDBY0
);
727 check_one_card(qts
, false, "primary0", MAC_PRIMARY0
);
729 args
= qdict_from_jsonf_nofail("{}");
730 g_assert_nonnull(args
);
731 qdict_put_str(args
, "uri", uri
);
733 resp
= qtest_qmp(qts
, "{ 'execute': 'migrate-incoming', 'arguments': %p}",
735 g_assert(qdict_haskey(resp
, "return"));
738 resp
= get_migration_event(qts
);
739 g_assert_cmpstr(qdict_get_str(resp
, "status"), ==, "setup");
742 resp
= get_failover_negociated_event(qts
);
743 g_assert_cmpstr(qdict_get_str(resp
, "device-id"), ==, "standby0");
746 check_one_card(qts
, true, "standby0", MAC_STANDBY0
);
747 check_one_card(qts
, true, "primary0", MAC_PRIMARY0
);
749 qtest_qmp_eventwait(qts
, "RESUME");
751 ret
= migrate_status(qts
);
752 g_assert_cmpstr(qdict_get_str(ret
, "status"), ==, "completed");
758 static void test_migrate_abort_wait_unplug(gconstpointer opaque
)
761 QDict
*resp
, *args
, *ret
;
762 g_autofree gchar
*uri
= g_strdup_printf("exec: cat > %s", (gchar
*)opaque
);
764 QVirtioPCIDevice
*vdev
;
766 qts
= machine_start(BASE_MACHINE
767 "-netdev user,id=hs0 "
768 "-netdev user,id=hs1 ",
771 check_one_card(qts
, false, "standby0", MAC_STANDBY0
);
772 check_one_card(qts
, false, "primary0", MAC_PRIMARY0
);
774 qtest_qmp_device_add(qts
, "virtio-net", "standby0",
778 "'mac': '"MAC_STANDBY0
"'}");
780 check_one_card(qts
, true, "standby0", MAC_STANDBY0
);
781 check_one_card(qts
, false, "primary0", MAC_PRIMARY0
);
783 vdev
= start_virtio_net(qts
, 1, 0, "standby0");
785 check_one_card(qts
, true, "standby0", MAC_STANDBY0
);
786 check_one_card(qts
, false, "primary0", MAC_PRIMARY0
);
788 qtest_qmp_device_add(qts
, "virtio-net", "primary0",
790 "'failover_pair_id': 'standby0',"
794 "'mac': '"MAC_PRIMARY0
"'}");
796 check_one_card(qts
, true, "standby0", MAC_STANDBY0
);
797 check_one_card(qts
, true, "primary0", MAC_PRIMARY0
);
799 args
= qdict_from_jsonf_nofail("{}");
800 g_assert_nonnull(args
);
801 qdict_put_str(args
, "uri", uri
);
803 resp
= qtest_qmp(qts
, "{ 'execute': 'migrate', 'arguments': %p}", args
);
804 g_assert(qdict_haskey(resp
, "return"));
807 /* the event is sent when QEMU asks the OS to unplug the card */
808 resp
= get_unplug_primary_event(qts
);
809 g_assert_cmpstr(qdict_get_str(resp
, "device-id"), ==, "primary0");
812 resp
= qtest_qmp(qts
, "{ 'execute': 'migrate_cancel' }");
813 g_assert(qdict_haskey(resp
, "return"));
816 /* migration has been cancelled while the unplug was in progress */
818 /* while the card is not ejected, we must be in "cancelling" state */
819 ret
= migrate_status(qts
);
821 status
= qdict_get_str(ret
, "status");
822 g_assert_cmpstr(status
, ==, "cancelling");
825 /* OS unplugs the cards, QEMU can move from wait-unplug state */
826 qtest_outl(qts
, ACPI_PCIHP_ADDR_ICH9
+ PCI_EJ_BASE
, 1);
829 ret
= migrate_status(qts
);
831 status
= qdict_get_str(ret
, "status");
832 if (strcmp(status
, "cancelled") == 0) {
836 g_assert_cmpstr(status
, !=, "failed");
837 g_assert_cmpstr(status
, !=, "active");
841 check_one_card(qts
, true, "standby0", MAC_STANDBY0
);
842 check_one_card(qts
, true, "primary0", MAC_PRIMARY0
);
844 qos_object_destroy((QOSGraphObject
*)vdev
);
848 static void test_migrate_abort_active(gconstpointer opaque
)
851 QDict
*resp
, *args
, *ret
;
852 g_autofree gchar
*uri
= g_strdup_printf("exec: cat > %s", (gchar
*)opaque
);
854 QVirtioPCIDevice
*vdev
;
856 qts
= machine_start(BASE_MACHINE
857 "-netdev user,id=hs0 "
858 "-netdev user,id=hs1 ",
861 check_one_card(qts
, false, "standby0", MAC_STANDBY0
);
862 check_one_card(qts
, false, "primary0", MAC_PRIMARY0
);
864 qtest_qmp_device_add(qts
, "virtio-net", "standby0",
868 "'mac': '"MAC_STANDBY0
"'}");
870 check_one_card(qts
, true, "standby0", MAC_STANDBY0
);
871 check_one_card(qts
, false, "primary0", MAC_PRIMARY0
);
873 vdev
= start_virtio_net(qts
, 1, 0, "standby0");
875 check_one_card(qts
, true, "standby0", MAC_STANDBY0
);
876 check_one_card(qts
, false, "primary0", MAC_PRIMARY0
);
878 qtest_qmp_device_add(qts
, "virtio-net", "primary0",
880 "'failover_pair_id': 'standby0',"
884 "'mac': '"MAC_PRIMARY0
"'}");
886 check_one_card(qts
, true, "standby0", MAC_STANDBY0
);
887 check_one_card(qts
, true, "primary0", MAC_PRIMARY0
);
889 args
= qdict_from_jsonf_nofail("{}");
890 g_assert_nonnull(args
);
891 qdict_put_str(args
, "uri", uri
);
893 resp
= qtest_qmp(qts
, "{ 'execute': 'migrate', 'arguments': %p}", args
);
894 g_assert(qdict_haskey(resp
, "return"));
897 /* the event is sent when QEMU asks the OS to unplug the card */
898 resp
= get_unplug_primary_event(qts
);
899 g_assert_cmpstr(qdict_get_str(resp
, "device-id"), ==, "primary0");
902 /* OS unplugs the cards, QEMU can move from wait-unplug state */
903 qtest_outl(qts
, ACPI_PCIHP_ADDR_ICH9
+ PCI_EJ_BASE
, 1);
906 ret
= migrate_status(qts
);
908 status
= qdict_get_str(ret
, "status");
909 if (strcmp(status
, "wait-unplug") != 0) {
913 g_assert_cmpstr(status
, !=, "failed");
917 resp
= qtest_qmp(qts
, "{ 'execute': 'migrate_cancel' }");
918 g_assert(qdict_haskey(resp
, "return"));
922 ret
= migrate_status(qts
);
924 status
= qdict_get_str(ret
, "status");
925 if (strcmp(status
, "cancelled") == 0) {
929 g_assert_cmpstr(status
, !=, "failed");
930 g_assert_cmpstr(status
, !=, "active");
934 check_one_card(qts
, true, "standby0", MAC_STANDBY0
);
935 check_one_card(qts
, true, "primary0", MAC_PRIMARY0
);
937 qos_object_destroy((QOSGraphObject
*)vdev
);
941 static void test_migrate_abort_timeout(gconstpointer opaque
)
944 QDict
*resp
, *args
, *ret
;
945 g_autofree gchar
*uri
= g_strdup_printf("exec: cat > %s", (gchar
*)opaque
);
948 QVirtioPCIDevice
*vdev
;
950 qts
= machine_start(BASE_MACHINE
951 "-netdev user,id=hs0 "
952 "-netdev user,id=hs1 ",
955 check_one_card(qts
, false, "standby0", MAC_STANDBY0
);
956 check_one_card(qts
, false, "primary0", MAC_PRIMARY0
);
958 qtest_qmp_device_add(qts
, "virtio-net", "standby0",
962 "'mac': '"MAC_STANDBY0
"'}");
964 check_one_card(qts
, true, "standby0", MAC_STANDBY0
);
965 check_one_card(qts
, false, "primary0", MAC_PRIMARY0
);
967 vdev
= start_virtio_net(qts
, 1, 0, "standby0");
969 check_one_card(qts
, true, "standby0", MAC_STANDBY0
);
970 check_one_card(qts
, false, "primary0", MAC_PRIMARY0
);
972 qtest_qmp_device_add(qts
, "virtio-net", "primary0",
974 "'failover_pair_id': 'standby0',"
978 "'mac': '"MAC_PRIMARY0
"'}");
980 check_one_card(qts
, true, "standby0", MAC_STANDBY0
);
981 check_one_card(qts
, true, "primary0", MAC_PRIMARY0
);
983 args
= qdict_from_jsonf_nofail("{}");
984 g_assert_nonnull(args
);
985 qdict_put_str(args
, "uri", uri
);
987 resp
= qtest_qmp(qts
, "{ 'execute': 'migrate', 'arguments': %p}", args
);
988 g_assert(qdict_haskey(resp
, "return"));
991 /* the event is sent when QEMU asks the OS to unplug the card */
992 resp
= get_unplug_primary_event(qts
);
993 g_assert_cmpstr(qdict_get_str(resp
, "device-id"), ==, "primary0");
996 resp
= qtest_qmp(qts
, "{ 'execute': 'migrate_cancel' }");
997 g_assert(qdict_haskey(resp
, "return"));
1000 /* migration has been cancelled while the unplug was in progress */
1002 /* while the card is not ejected, we must be in "cancelling" state */
1006 ret
= migrate_status(qts
);
1008 status
= qdict_get_str(ret
, "status");
1009 if (strcmp(status
, "cancelled") == 0) {
1013 g_assert_cmpstr(status
, ==, "cancelling");
1014 g_assert(qdict_haskey(ret
, "total-time"));
1015 total
= qdict_get_int(ret
, "total-time");
1020 * migration timeout in this case is 30 seconds
1021 * check we exit on the timeout (ms)
1023 g_assert_cmpint(total
, >, 30000);
1025 check_one_card(qts
, true, "standby0", MAC_STANDBY0
);
1026 check_one_card(qts
, true, "primary0", MAC_PRIMARY0
);
1028 qos_object_destroy((QOSGraphObject
*)vdev
);
1032 static void test_multi_out(gconstpointer opaque
)
1035 QDict
*resp
, *args
, *ret
;
1036 g_autofree gchar
*uri
= g_strdup_printf("exec: cat > %s", (gchar
*)opaque
);
1037 const gchar
*status
, *expected
;
1038 QVirtioPCIDevice
*vdev0
, *vdev1
;
1040 qts
= machine_start(BASE_MACHINE
1041 "-device pcie-root-port,id=root2,addr=0x3,bus=pcie.0,chassis=3 "
1042 "-device pcie-root-port,id=root3,addr=0x4,bus=pcie.0,chassis=4 "
1043 "-netdev user,id=hs0 "
1044 "-netdev user,id=hs1 "
1045 "-netdev user,id=hs2 "
1046 "-netdev user,id=hs3 ",
1049 check_one_card(qts
, false, "standby0", MAC_STANDBY0
);
1050 check_one_card(qts
, false, "primary0", MAC_PRIMARY0
);
1051 check_one_card(qts
, false, "standby1", MAC_STANDBY1
);
1052 check_one_card(qts
, false, "primary1", MAC_PRIMARY1
);
1054 qtest_qmp_device_add(qts
, "virtio-net", "standby0",
1058 "'mac': '"MAC_STANDBY0
"'}");
1060 check_one_card(qts
, true, "standby0", MAC_STANDBY0
);
1061 check_one_card(qts
, false, "primary0", MAC_PRIMARY0
);
1062 check_one_card(qts
, false, "standby1", MAC_STANDBY1
);
1063 check_one_card(qts
, false, "primary1", MAC_PRIMARY1
);
1065 qtest_qmp_device_add(qts
, "virtio-net", "primary0",
1067 "'failover_pair_id': 'standby0',"
1071 "'mac': '"MAC_PRIMARY0
"'}");
1073 check_one_card(qts
, true, "standby0", MAC_STANDBY0
);
1074 check_one_card(qts
, false, "primary0", MAC_PRIMARY0
);
1075 check_one_card(qts
, false, "standby1", MAC_STANDBY1
);
1076 check_one_card(qts
, false, "primary1", MAC_PRIMARY1
);
1078 vdev0
= start_virtio_net(qts
, 1, 0, "standby0");
1080 check_one_card(qts
, true, "standby0", MAC_STANDBY0
);
1081 check_one_card(qts
, true, "primary0", MAC_PRIMARY0
);
1082 check_one_card(qts
, false, "standby1", MAC_STANDBY1
);
1083 check_one_card(qts
, false, "primary1", MAC_PRIMARY1
);
1085 qtest_qmp_device_add(qts
, "virtio-net", "standby1",
1089 "'mac': '"MAC_STANDBY1
"'}");
1091 check_one_card(qts
, true, "standby0", MAC_STANDBY0
);
1092 check_one_card(qts
, true, "primary0", MAC_PRIMARY0
);
1093 check_one_card(qts
, true, "standby1", MAC_STANDBY1
);
1094 check_one_card(qts
, false, "primary1", MAC_PRIMARY1
);
1096 qtest_qmp_device_add(qts
, "virtio-net", "primary1",
1098 "'failover_pair_id': 'standby1',"
1102 "'mac': '"MAC_PRIMARY1
"'}");
1104 check_one_card(qts
, true, "standby0", MAC_STANDBY0
);
1105 check_one_card(qts
, true, "primary0", MAC_PRIMARY0
);
1106 check_one_card(qts
, true, "standby1", MAC_STANDBY1
);
1107 check_one_card(qts
, false, "primary1", MAC_PRIMARY1
);
1109 vdev1
= start_virtio_net(qts
, 3, 0, "standby1");
1111 check_one_card(qts
, true, "standby0", MAC_STANDBY0
);
1112 check_one_card(qts
, true, "primary0", MAC_PRIMARY0
);
1113 check_one_card(qts
, true, "standby1", MAC_STANDBY1
);
1114 check_one_card(qts
, true, "primary1", MAC_PRIMARY1
);
1116 args
= qdict_from_jsonf_nofail("{}");
1117 g_assert_nonnull(args
);
1118 qdict_put_str(args
, "uri", uri
);
1120 resp
= qtest_qmp(qts
, "{ 'execute': 'migrate', 'arguments': %p}", args
);
1121 g_assert(qdict_haskey(resp
, "return"));
1122 qobject_unref(resp
);
1124 /* the event is sent when QEMU asks the OS to unplug the card */
1125 resp
= get_unplug_primary_event(qts
);
1126 if (strcmp(qdict_get_str(resp
, "device-id"), "primary0") == 0) {
1127 expected
= "primary1";
1128 } else if (strcmp(qdict_get_str(resp
, "device-id"), "primary1") == 0) {
1129 expected
= "primary0";
1131 g_assert_not_reached();
1133 qobject_unref(resp
);
1135 resp
= get_unplug_primary_event(qts
);
1136 g_assert_cmpstr(qdict_get_str(resp
, "device-id"), ==, expected
);
1137 qobject_unref(resp
);
1139 /* wait the end of the migration setup phase */
1141 ret
= migrate_status(qts
);
1143 status
= qdict_get_str(ret
, "status");
1144 if (strcmp(status
, "wait-unplug") == 0) {
1149 /* The migration must not start if the card is not ejected */
1150 g_assert_cmpstr(status
, !=, "active");
1151 g_assert_cmpstr(status
, !=, "completed");
1152 g_assert_cmpstr(status
, !=, "failed");
1153 g_assert_cmpstr(status
, !=, "cancelling");
1154 g_assert_cmpstr(status
, !=, "cancelled");
1159 /* OS unplugs primary1, but we must wait the second */
1160 qtest_outl(qts
, ACPI_PCIHP_ADDR_ICH9
+ PCI_EJ_BASE
, 1);
1162 ret
= migrate_status(qts
);
1163 status
= qdict_get_str(ret
, "status");
1164 g_assert_cmpstr(status
, ==, "wait-unplug");
1167 if (g_test_slow()) {
1168 /* check we stay in wait-unplug while the card is not ejected */
1169 for (int i
= 0; i
< 5; i
++) {
1171 ret
= migrate_status(qts
);
1172 status
= qdict_get_str(ret
, "status");
1173 g_assert_cmpstr(status
, ==, "wait-unplug");
1178 /* OS unplugs primary0, QEMU can move from wait-unplug state */
1179 qtest_outl(qts
, ACPI_PCIHP_ADDR_ICH9
+ PCI_SEL_BASE
, 2);
1180 qtest_outl(qts
, ACPI_PCIHP_ADDR_ICH9
+ PCI_EJ_BASE
, 1);
1183 ret
= migrate_status(qts
);
1185 status
= qdict_get_str(ret
, "status");
1186 if (strcmp(status
, "completed") == 0) {
1190 g_assert_cmpstr(status
, !=, "failed");
1191 g_assert_cmpstr(status
, !=, "cancelling");
1192 g_assert_cmpstr(status
, !=, "cancelled");
1196 qtest_qmp_eventwait(qts
, "STOP");
1198 qos_object_destroy((QOSGraphObject
*)vdev0
);
1199 qos_object_destroy((QOSGraphObject
*)vdev1
);
1203 static void test_multi_in(gconstpointer opaque
)
1206 QDict
*resp
, *args
, *ret
;
1207 g_autofree gchar
*uri
= g_strdup_printf("exec: cat %s", (gchar
*)opaque
);
1209 qts
= machine_start(BASE_MACHINE
1210 "-device pcie-root-port,id=root2,addr=0x3,bus=pcie.0,chassis=3 "
1211 "-device pcie-root-port,id=root3,addr=0x4,bus=pcie.0,chassis=4 "
1212 "-netdev user,id=hs0 "
1213 "-netdev user,id=hs1 "
1214 "-netdev user,id=hs2 "
1215 "-netdev user,id=hs3 "
1219 check_one_card(qts
, false, "standby0", MAC_STANDBY0
);
1220 check_one_card(qts
, false, "primary0", MAC_PRIMARY0
);
1221 check_one_card(qts
, false, "standby1", MAC_STANDBY1
);
1222 check_one_card(qts
, false, "primary1", MAC_PRIMARY1
);
1224 qtest_qmp_device_add(qts
, "virtio-net", "standby0",
1228 "'mac': '"MAC_STANDBY0
"'}");
1230 check_one_card(qts
, true, "standby0", MAC_STANDBY0
);
1231 check_one_card(qts
, false, "primary0", MAC_PRIMARY0
);
1232 check_one_card(qts
, false, "standby1", MAC_STANDBY1
);
1233 check_one_card(qts
, false, "primary1", MAC_PRIMARY1
);
1235 qtest_qmp_device_add(qts
, "virtio-net", "primary0",
1237 "'failover_pair_id': 'standby0',"
1241 "'mac': '"MAC_PRIMARY0
"'}");
1243 check_one_card(qts
, true, "standby0", MAC_STANDBY0
);
1244 check_one_card(qts
, false, "primary0", MAC_PRIMARY0
);
1245 check_one_card(qts
, false, "standby1", MAC_STANDBY1
);
1246 check_one_card(qts
, false, "primary1", MAC_PRIMARY1
);
1248 qtest_qmp_device_add(qts
, "virtio-net", "standby1",
1252 "'mac': '"MAC_STANDBY1
"'}");
1254 check_one_card(qts
, true, "standby0", MAC_STANDBY0
);
1255 check_one_card(qts
, false, "primary0", MAC_PRIMARY0
);
1256 check_one_card(qts
, true, "standby1", MAC_STANDBY1
);
1257 check_one_card(qts
, false, "primary1", MAC_PRIMARY1
);
1259 qtest_qmp_device_add(qts
, "virtio-net", "primary1",
1261 "'failover_pair_id': 'standby1',"
1265 "'mac': '"MAC_PRIMARY1
"'}");
1267 check_one_card(qts
, true, "standby0", MAC_STANDBY0
);
1268 check_one_card(qts
, false, "primary0", MAC_PRIMARY0
);
1269 check_one_card(qts
, true, "standby1", MAC_STANDBY1
);
1270 check_one_card(qts
, false, "primary1", MAC_PRIMARY1
);
1272 args
= qdict_from_jsonf_nofail("{}");
1273 g_assert_nonnull(args
);
1274 qdict_put_str(args
, "uri", uri
);
1276 resp
= qtest_qmp(qts
, "{ 'execute': 'migrate-incoming', 'arguments': %p}",
1278 g_assert(qdict_haskey(resp
, "return"));
1279 qobject_unref(resp
);
1281 resp
= get_migration_event(qts
);
1282 g_assert_cmpstr(qdict_get_str(resp
, "status"), ==, "setup");
1283 qobject_unref(resp
);
1285 resp
= get_failover_negociated_event(qts
);
1286 g_assert_cmpstr(qdict_get_str(resp
, "device-id"), ==, "standby0");
1287 qobject_unref(resp
);
1289 resp
= get_failover_negociated_event(qts
);
1290 g_assert_cmpstr(qdict_get_str(resp
, "device-id"), ==, "standby1");
1291 qobject_unref(resp
);
1293 check_one_card(qts
, true, "standby0", MAC_STANDBY0
);
1294 check_one_card(qts
, true, "primary0", MAC_PRIMARY0
);
1295 check_one_card(qts
, true, "standby1", MAC_STANDBY1
);
1296 check_one_card(qts
, true, "primary1", MAC_PRIMARY1
);
1298 qtest_qmp_eventwait(qts
, "RESUME");
1300 ret
= migrate_status(qts
);
1301 g_assert_cmpstr(qdict_get_str(ret
, "status"), ==, "completed");
1307 int main(int argc
, char **argv
)
1312 g_test_init(&argc
, &argv
, NULL
);
1314 ret
= g_file_open_tmp("failover_test_migrate-XXXXXX", &tmpfile
, NULL
);
1315 g_assert_true(ret
>= 0);
1318 qtest_add_func("failover-virtio-net/params/error/id", test_error_id
);
1319 qtest_add_func("failover-virtio-net/params/error/pcie", test_error_pcie
);
1320 qtest_add_func("failover-virtio-net/params/on", test_on
);
1321 qtest_add_func("failover-virtio-net/params/on_mismatch",
1323 qtest_add_func("failover-virtio-net/params/off", test_off
);
1324 qtest_add_func("failover-virtio-net/params/enabled", test_enabled
);
1325 qtest_add_func("failover-virtio-net/hotplug_1", test_hotplug_1
);
1326 qtest_add_func("failover-virtio-net/hotplug_1_reverse",
1327 test_hotplug_1_reverse
);
1328 qtest_add_func("failover-virtio-net/hotplug_2", test_hotplug_2
);
1329 qtest_add_func("failover-virtio-net/hotplug_2_reverse",
1330 test_hotplug_2_reverse
);
1331 qtest_add_data_func("failover-virtio-net/migrate/out", tmpfile
,
1333 qtest_add_data_func("failover-virtio-net/migrate/in", tmpfile
,
1335 qtest_add_data_func("failover-virtio-net/migrate/abort/wait-unplug",
1336 tmpfile
, test_migrate_abort_wait_unplug
);
1337 qtest_add_data_func("failover-virtio-net/migrate/abort/active", tmpfile
,
1338 test_migrate_abort_active
);
1339 if (g_test_slow()) {
1340 qtest_add_data_func("failover-virtio-net/migrate/abort/timeout",
1341 tmpfile
, test_migrate_abort_timeout
);
1343 qtest_add_data_func("failover-virtio-net/multi/out",
1344 tmpfile
, test_multi_out
);
1345 qtest_add_data_func("failover-virtio-net/multi/in",
1346 tmpfile
, test_multi_in
);