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