qemu/tests/migration-test.c
<<
>>
Prefs
   1/*
   2 * QTest testcase for migration
   3 *
   4 * Copyright (c) 2016-2018 Red Hat, Inc. and/or its affiliates
   5 *   based on the vhost-user-test.c that is:
   6 *      Copyright (c) 2014 Virtual Open Systems Sarl.
   7 *
   8 * This work is licensed under the terms of the GNU GPL, version 2 or later.
   9 * See the COPYING file in the top-level directory.
  10 *
  11 */
  12
  13#include "qemu/osdep.h"
  14
  15#include "libqtest.h"
  16#include "qapi/qmp/qdict.h"
  17#include "qemu/option.h"
  18#include "qemu/range.h"
  19#include "qemu/sockets.h"
  20#include "chardev/char.h"
  21#include "sysemu/sysemu.h"
  22#include "hw/nvram/chrp_nvram.h"
  23
  24#define MIN_NVRAM_SIZE 8192 /* from spapr_nvram.c */
  25
  26const unsigned start_address = 1024 * 1024;
  27const unsigned end_address = 100 * 1024 * 1024;
  28bool got_stop;
  29
  30#if defined(__linux__)
  31#include <sys/syscall.h>
  32#include <sys/vfs.h>
  33#endif
  34
  35#if defined(__linux__) && defined(__NR_userfaultfd) && defined(CONFIG_EVENTFD)
  36#include <sys/eventfd.h>
  37#include <sys/ioctl.h>
  38#include <linux/userfaultfd.h>
  39
  40static bool ufd_version_check(void)
  41{
  42    struct uffdio_api api_struct;
  43    uint64_t ioctl_mask;
  44
  45    int ufd = syscall(__NR_userfaultfd, O_CLOEXEC);
  46
  47    if (ufd == -1) {
  48        g_test_message("Skipping test: userfaultfd not available");
  49        return false;
  50    }
  51
  52    api_struct.api = UFFD_API;
  53    api_struct.features = 0;
  54    if (ioctl(ufd, UFFDIO_API, &api_struct)) {
  55        g_test_message("Skipping test: UFFDIO_API failed");
  56        return false;
  57    }
  58
  59    ioctl_mask = (__u64)1 << _UFFDIO_REGISTER |
  60                 (__u64)1 << _UFFDIO_UNREGISTER;
  61    if ((api_struct.ioctls & ioctl_mask) != ioctl_mask) {
  62        g_test_message("Skipping test: Missing userfault feature");
  63        return false;
  64    }
  65
  66    return true;
  67}
  68
  69#else
  70static bool ufd_version_check(void)
  71{
  72    g_test_message("Skipping test: Userfault not available (builtdtime)");
  73    return false;
  74}
  75
  76#endif
  77
  78static const char *tmpfs;
  79
  80/* A simple PC boot sector that modifies memory (1-100MB) quickly
  81 * outputting a 'B' every so often if it's still running.
  82 */
  83#include "tests/migration/x86-a-b-bootblock.h"
  84
  85static void init_bootfile_x86(const char *bootpath)
  86{
  87    FILE *bootfile = fopen(bootpath, "wb");
  88
  89    g_assert_cmpint(fwrite(x86_bootsect, 512, 1, bootfile), ==, 1);
  90    fclose(bootfile);
  91}
  92
  93static void init_bootfile_ppc(const char *bootpath)
  94{
  95    FILE *bootfile;
  96    char buf[MIN_NVRAM_SIZE];
  97    ChrpNvramPartHdr *header = (ChrpNvramPartHdr *)buf;
  98
  99    memset(buf, 0, MIN_NVRAM_SIZE);
 100
 101    /* Create a "common" partition in nvram to store boot-command property */
 102
 103    header->signature = CHRP_NVPART_SYSTEM;
 104    memcpy(header->name, "common", 6);
 105    chrp_nvram_finish_partition(header, MIN_NVRAM_SIZE);
 106
 107    /* FW_MAX_SIZE is 4MB, but slof.bin is only 900KB,
 108     * so let's modify memory between 1MB and 100MB
 109     * to do like PC bootsector
 110     */
 111
 112    sprintf(buf + 16,
 113            "boot-command=hex .\" _\" begin %x %x do i c@ 1 + i c! 1000 +loop "
 114            ".\" B\" 0 until", end_address, start_address);
 115
 116    /* Write partition to the NVRAM file */
 117
 118    bootfile = fopen(bootpath, "wb");
 119    g_assert_cmpint(fwrite(buf, MIN_NVRAM_SIZE, 1, bootfile), ==, 1);
 120    fclose(bootfile);
 121}
 122
 123/*
 124 * Wait for some output in the serial output file,
 125 * we get an 'A' followed by an endless string of 'B's
 126 * but on the destination we won't have the A.
 127 */
 128static void wait_for_serial(const char *side)
 129{
 130    char *serialpath = g_strdup_printf("%s/%s", tmpfs, side);
 131    FILE *serialfile = fopen(serialpath, "r");
 132    const char *arch = qtest_get_arch();
 133    int started = (strcmp(side, "src_serial") == 0 &&
 134                   strcmp(arch, "ppc64") == 0) ? 0 : 1;
 135
 136    g_free(serialpath);
 137    do {
 138        int readvalue = fgetc(serialfile);
 139
 140        if (!started) {
 141            /* SLOF prints its banner before starting test,
 142             * to ignore it, mark the start of the test with '_',
 143             * ignore all characters until this marker
 144             */
 145            switch (readvalue) {
 146            case '_':
 147                started = 1;
 148                break;
 149            case EOF:
 150                fseek(serialfile, 0, SEEK_SET);
 151                usleep(1000);
 152                break;
 153            }
 154            continue;
 155        }
 156        switch (readvalue) {
 157        case 'A':
 158            /* Fine */
 159            break;
 160
 161        case 'B':
 162            /* It's alive! */
 163            fclose(serialfile);
 164            return;
 165
 166        case EOF:
 167            started = (strcmp(side, "src_serial") == 0 &&
 168                       strcmp(arch, "ppc64") == 0) ? 0 : 1;
 169            fseek(serialfile, 0, SEEK_SET);
 170            usleep(1000);
 171            break;
 172
 173        default:
 174            fprintf(stderr, "Unexpected %d on %s serial\n", readvalue, side);
 175            g_assert_not_reached();
 176        }
 177    } while (true);
 178}
 179
 180/*
 181 * Events can get in the way of responses we are actually waiting for.
 182 */
 183static QDict *wait_command(QTestState *who, const char *command)
 184{
 185    const char *event_string;
 186    QDict *response;
 187
 188    response = qtest_qmp(who, command);
 189
 190    while (qdict_haskey(response, "event")) {
 191        /* OK, it was an event */
 192        event_string = qdict_get_str(response, "event");
 193        if (!strcmp(event_string, "STOP")) {
 194            got_stop = true;
 195        }
 196        QDECREF(response);
 197        response = qtest_qmp_receive(who);
 198    }
 199    return response;
 200}
 201
 202
 203/*
 204 * It's tricky to use qemu's migration event capability with qtest,
 205 * events suddenly appearing confuse the qmp()/hmp() responses.
 206 */
 207
 208static uint64_t get_migration_pass(QTestState *who)
 209{
 210    QDict *rsp, *rsp_return, *rsp_ram;
 211    uint64_t result;
 212
 213    rsp = wait_command(who, "{ 'execute': 'query-migrate' }");
 214    rsp_return = qdict_get_qdict(rsp, "return");
 215    if (!qdict_haskey(rsp_return, "ram")) {
 216        /* Still in setup */
 217        result = 0;
 218    } else {
 219        rsp_ram = qdict_get_qdict(rsp_return, "ram");
 220        result = qdict_get_try_int(rsp_ram, "dirty-sync-count", 0);
 221    }
 222    QDECREF(rsp);
 223    return result;
 224}
 225
 226static void wait_for_migration_complete(QTestState *who)
 227{
 228    while (true) {
 229        QDict *rsp, *rsp_return;
 230        bool completed;
 231        const char *status;
 232
 233        rsp = wait_command(who, "{ 'execute': 'query-migrate' }");
 234        rsp_return = qdict_get_qdict(rsp, "return");
 235        status = qdict_get_str(rsp_return, "status");
 236        completed = strcmp(status, "completed") == 0;
 237        g_assert_cmpstr(status, !=,  "failed");
 238        QDECREF(rsp);
 239        if (completed) {
 240            return;
 241        }
 242        usleep(1000);
 243    }
 244}
 245
 246static void wait_for_migration_pass(QTestState *who)
 247{
 248    uint64_t initial_pass = get_migration_pass(who);
 249    uint64_t pass;
 250
 251    /* Wait for the 1st sync */
 252    while (!got_stop && !initial_pass) {
 253        usleep(1000);
 254        initial_pass = get_migration_pass(who);
 255    }
 256
 257    do {
 258        usleep(1000);
 259        pass = get_migration_pass(who);
 260    } while (pass == initial_pass && !got_stop);
 261}
 262
 263static void check_guests_ram(QTestState *who)
 264{
 265    /* Our ASM test will have been incrementing one byte from each page from
 266     * 1MB to <100MB in order.
 267     * This gives us a constraint that any page's byte should be equal or less
 268     * than the previous pages byte (mod 256); and they should all be equal
 269     * except for one transition at the point where we meet the incrementer.
 270     * (We're running this with the guest stopped).
 271     */
 272    unsigned address;
 273    uint8_t first_byte;
 274    uint8_t last_byte;
 275    bool hit_edge = false;
 276    bool bad = false;
 277
 278    qtest_memread(who, start_address, &first_byte, 1);
 279    last_byte = first_byte;
 280
 281    for (address = start_address + 4096; address < end_address; address += 4096)
 282    {
 283        uint8_t b;
 284        qtest_memread(who, address, &b, 1);
 285        if (b != last_byte) {
 286            if (((b + 1) % 256) == last_byte && !hit_edge) {
 287                /* This is OK, the guest stopped at the point of
 288                 * incrementing the previous page but didn't get
 289                 * to us yet.
 290                 */
 291                hit_edge = true;
 292            } else {
 293                fprintf(stderr, "Memory content inconsistency at %x"
 294                                " first_byte = %x last_byte = %x current = %x"
 295                                " hit_edge = %x\n",
 296                                address, first_byte, last_byte, b, hit_edge);
 297                bad = true;
 298            }
 299        }
 300        last_byte = b;
 301    }
 302    g_assert_false(bad);
 303}
 304
 305static void cleanup(const char *filename)
 306{
 307    char *path = g_strdup_printf("%s/%s", tmpfs, filename);
 308
 309    unlink(path);
 310    g_free(path);
 311}
 312
 313static void migrate_check_parameter(QTestState *who, const char *parameter,
 314                                    const char *value)
 315{
 316    QDict *rsp, *rsp_return;
 317    char *result;
 318
 319    rsp = wait_command(who, "{ 'execute': 'query-migrate-parameters' }");
 320    rsp_return = qdict_get_qdict(rsp, "return");
 321    result = g_strdup_printf("%" PRId64,
 322                             qdict_get_try_int(rsp_return,  parameter, -1));
 323    g_assert_cmpstr(result, ==, value);
 324    g_free(result);
 325    QDECREF(rsp);
 326}
 327
 328static void migrate_set_parameter(QTestState *who, const char *parameter,
 329                                  const char *value)
 330{
 331    QDict *rsp;
 332    gchar *cmd;
 333
 334    cmd = g_strdup_printf("{ 'execute': 'migrate-set-parameters',"
 335                          "'arguments': { '%s': %s } }",
 336                          parameter, value);
 337    rsp = qtest_qmp(who, cmd);
 338    g_free(cmd);
 339    g_assert(qdict_haskey(rsp, "return"));
 340    QDECREF(rsp);
 341    migrate_check_parameter(who, parameter, value);
 342}
 343
 344static void migrate_set_capability(QTestState *who, const char *capability,
 345                                   const char *value)
 346{
 347    QDict *rsp;
 348    gchar *cmd;
 349
 350    cmd = g_strdup_printf("{ 'execute': 'migrate-set-capabilities',"
 351                          "'arguments': { "
 352                          "'capabilities': [ { "
 353                          "'capability': '%s', 'state': %s } ] } }",
 354                          capability, value);
 355    rsp = qtest_qmp(who, cmd);
 356    g_free(cmd);
 357    g_assert(qdict_haskey(rsp, "return"));
 358    QDECREF(rsp);
 359}
 360
 361static void migrate(QTestState *who, const char *uri)
 362{
 363    QDict *rsp;
 364    gchar *cmd;
 365
 366    cmd = g_strdup_printf("{ 'execute': 'migrate',"
 367                          "'arguments': { 'uri': '%s' } }",
 368                          uri);
 369    rsp = qtest_qmp(who, cmd);
 370    g_free(cmd);
 371    g_assert(qdict_haskey(rsp, "return"));
 372    QDECREF(rsp);
 373}
 374
 375static void migrate_start_postcopy(QTestState *who)
 376{
 377    QDict *rsp;
 378
 379    rsp = wait_command(who, "{ 'execute': 'migrate-start-postcopy' }");
 380    g_assert(qdict_haskey(rsp, "return"));
 381    QDECREF(rsp);
 382}
 383
 384static void test_migrate_start(QTestState **from, QTestState **to,
 385                               const char *uri, bool hide_stderr)
 386{
 387    gchar *cmd_src, *cmd_dst;
 388    char *bootpath = g_strdup_printf("%s/bootsect", tmpfs);
 389    const char *arch = qtest_get_arch();
 390    const char *accel = "kvm:tcg";
 391
 392    got_stop = false;
 393
 394    if (strcmp(arch, "i386") == 0 || strcmp(arch, "x86_64") == 0) {
 395        init_bootfile_x86(bootpath);
 396        cmd_src = g_strdup_printf("-machine accel=%s -m 150M"
 397                                  " -name source,debug-threads=on"
 398                                  " -serial file:%s/src_serial"
 399                                  " -drive file=%s,format=raw",
 400                                  accel, tmpfs, bootpath);
 401        cmd_dst = g_strdup_printf("-machine accel=%s -m 150M"
 402                                  " -name target,debug-threads=on"
 403                                  " -serial file:%s/dest_serial"
 404                                  " -drive file=%s,format=raw"
 405                                  " -incoming %s",
 406                                  accel, tmpfs, bootpath, uri);
 407    } else if (strcmp(arch, "ppc64") == 0) {
 408
 409        /* On ppc64, the test only works with kvm-hv, but not with kvm-pr */
 410        if (access("/sys/module/kvm_hv", F_OK)) {
 411            accel = "tcg";
 412        }
 413        init_bootfile_ppc(bootpath);
 414        cmd_src = g_strdup_printf("-machine accel=%s -m 256M"
 415                                  " -name source,debug-threads=on"
 416                                  " -serial file:%s/src_serial"
 417                                  " -drive file=%s,if=pflash,format=raw",
 418                                  accel, tmpfs, bootpath);
 419        cmd_dst = g_strdup_printf("-machine accel=%s -m 256M"
 420                                  " -name target,debug-threads=on"
 421                                  " -serial file:%s/dest_serial"
 422                                  " -incoming %s",
 423                                  accel, tmpfs, uri);
 424    } else {
 425        g_assert_not_reached();
 426    }
 427
 428    g_free(bootpath);
 429
 430    if (hide_stderr) {
 431        gchar *tmp;
 432        tmp = g_strdup_printf("%s 2>/dev/null", cmd_src);
 433        g_free(cmd_src);
 434        cmd_src = tmp;
 435
 436        tmp = g_strdup_printf("%s 2>/dev/null", cmd_dst);
 437        g_free(cmd_dst);
 438        cmd_dst = tmp;
 439    }
 440
 441    *from = qtest_start(cmd_src);
 442    g_free(cmd_src);
 443
 444    *to = qtest_init(cmd_dst);
 445    g_free(cmd_dst);
 446}
 447
 448static void test_migrate_end(QTestState *from, QTestState *to, bool test_dest)
 449{
 450    unsigned char dest_byte_a, dest_byte_b, dest_byte_c, dest_byte_d;
 451
 452    qtest_quit(from);
 453
 454    if (test_dest) {
 455        qtest_memread(to, start_address, &dest_byte_a, 1);
 456
 457        /* Destination still running, wait for a byte to change */
 458        do {
 459            qtest_memread(to, start_address, &dest_byte_b, 1);
 460            usleep(1000 * 10);
 461        } while (dest_byte_a == dest_byte_b);
 462
 463        qtest_qmp_discard_response(to, "{ 'execute' : 'stop'}");
 464
 465        /* With it stopped, check nothing changes */
 466        qtest_memread(to, start_address, &dest_byte_c, 1);
 467        usleep(1000 * 200);
 468        qtest_memread(to, start_address, &dest_byte_d, 1);
 469        g_assert_cmpint(dest_byte_c, ==, dest_byte_d);
 470
 471        check_guests_ram(to);
 472    }
 473
 474    qtest_quit(to);
 475
 476    cleanup("bootsect");
 477    cleanup("migsocket");
 478    cleanup("src_serial");
 479    cleanup("dest_serial");
 480}
 481
 482static void deprecated_set_downtime(QTestState *who, const double value)
 483{
 484    QDict *rsp;
 485    gchar *cmd;
 486    char *expected;
 487    int64_t result_int;
 488
 489    cmd = g_strdup_printf("{ 'execute': 'migrate_set_downtime',"
 490                          "'arguments': { 'value': %g } }", value);
 491    rsp = qtest_qmp(who, cmd);
 492    g_free(cmd);
 493    g_assert(qdict_haskey(rsp, "return"));
 494    QDECREF(rsp);
 495    result_int = value * 1000L;
 496    expected = g_strdup_printf("%" PRId64, result_int);
 497    migrate_check_parameter(who, "downtime-limit", expected);
 498    g_free(expected);
 499}
 500
 501static void deprecated_set_speed(QTestState *who, const char *value)
 502{
 503    QDict *rsp;
 504    gchar *cmd;
 505
 506    cmd = g_strdup_printf("{ 'execute': 'migrate_set_speed',"
 507                          "'arguments': { 'value': %s } }", value);
 508    rsp = qtest_qmp(who, cmd);
 509    g_free(cmd);
 510    g_assert(qdict_haskey(rsp, "return"));
 511    QDECREF(rsp);
 512    migrate_check_parameter(who, "max-bandwidth", value);
 513}
 514
 515static void test_deprecated(void)
 516{
 517    QTestState *from;
 518
 519    from = qtest_start("");
 520
 521    deprecated_set_downtime(from, 0.12345);
 522    deprecated_set_speed(from, "12345");
 523
 524    qtest_quit(from);
 525}
 526
 527static void test_migrate(void)
 528{
 529    char *uri = g_strdup_printf("unix:%s/migsocket", tmpfs);
 530    QTestState *from, *to;
 531
 532    test_migrate_start(&from, &to, uri, false);
 533
 534    migrate_set_capability(from, "postcopy-ram", "true");
 535    migrate_set_capability(to, "postcopy-ram", "true");
 536
 537    /* We want to pick a speed slow enough that the test completes
 538     * quickly, but that it doesn't complete precopy even on a slow
 539     * machine, so also set the downtime.
 540     */
 541    migrate_set_parameter(from, "max-bandwidth", "100000000");
 542    migrate_set_parameter(from, "downtime-limit", "1");
 543
 544    /* Wait for the first serial output from the source */
 545    wait_for_serial("src_serial");
 546
 547    migrate(from, uri);
 548
 549    wait_for_migration_pass(from);
 550
 551    migrate_start_postcopy(from);
 552
 553    if (!got_stop) {
 554        qtest_qmp_eventwait(from, "STOP");
 555    }
 556
 557    qtest_qmp_eventwait(to, "RESUME");
 558
 559    wait_for_serial("dest_serial");
 560    wait_for_migration_complete(from);
 561
 562    g_free(uri);
 563
 564    test_migrate_end(from, to, true);
 565}
 566
 567static void test_baddest(void)
 568{
 569    QTestState *from, *to;
 570    QDict *rsp, *rsp_return;
 571    const char *status;
 572    bool failed;
 573
 574    test_migrate_start(&from, &to, "tcp:0:0", true);
 575    migrate(from, "tcp:0:0");
 576    do {
 577        rsp = wait_command(from, "{ 'execute': 'query-migrate' }");
 578        rsp_return = qdict_get_qdict(rsp, "return");
 579
 580        status = qdict_get_str(rsp_return, "status");
 581
 582        g_assert(!strcmp(status, "setup") || !(strcmp(status, "failed")));
 583        failed = !strcmp(status, "failed");
 584        QDECREF(rsp);
 585    } while (!failed);
 586
 587    /* Is the machine currently running? */
 588    rsp = wait_command(from, "{ 'execute': 'query-status' }");
 589    g_assert(qdict_haskey(rsp, "return"));
 590    rsp_return = qdict_get_qdict(rsp, "return");
 591    g_assert(qdict_haskey(rsp_return, "running"));
 592    g_assert(qdict_get_bool(rsp_return, "running"));
 593    QDECREF(rsp);
 594
 595    test_migrate_end(from, to, false);
 596}
 597
 598int main(int argc, char **argv)
 599{
 600    char template[] = "/tmp/migration-test-XXXXXX";
 601    int ret;
 602
 603    g_test_init(&argc, &argv, NULL);
 604
 605    if (!ufd_version_check()) {
 606        return 0;
 607    }
 608
 609    tmpfs = mkdtemp(template);
 610    if (!tmpfs) {
 611        g_test_message("mkdtemp on path (%s): %s\n", template, strerror(errno));
 612    }
 613    g_assert(tmpfs);
 614
 615    module_call_init(MODULE_INIT_QOM);
 616
 617    qtest_add_func("/migration/postcopy/unix", test_migrate);
 618    qtest_add_func("/migration/deprecated", test_deprecated);
 619    qtest_add_func("/migration/bad_dest", test_baddest);
 620
 621    ret = g_test_run();
 622
 623    g_assert_cmpint(ret, ==, 0);
 624
 625    ret = rmdir(tmpfs);
 626    if (ret != 0) {
 627        g_test_message("unable to rmdir: path (%s): %s\n",
 628                       tmpfs, strerror(errno));
 629    }
 630
 631    return ret;
 632}
 633