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