qemu/tests/postcopy-test.c
<<
>>
Prefs
   1/*
   2 * QTest testcase for postcopy
   3 *
   4 * Copyright (c) 2016 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 "qemu/option.h"
  17#include "qemu/range.h"
  18#include "qemu/sockets.h"
  19#include "sysemu/char.h"
  20#include "sysemu/sysemu.h"
  21#include "hw/nvram/chrp_nvram.h"
  22
  23#define MIN_NVRAM_SIZE 8192 /* from spapr_nvram.c */
  24
  25const unsigned start_address = 1024 * 1024;
  26const unsigned end_address = 100 * 1024 * 1024;
  27bool got_stop;
  28
  29#if defined(__linux__)
  30#include <sys/syscall.h>
  31#include <sys/vfs.h>
  32#endif
  33
  34#if defined(__linux__) && defined(__NR_userfaultfd) && defined(CONFIG_EVENTFD)
  35#include <sys/eventfd.h>
  36#include <sys/ioctl.h>
  37#include <linux/userfaultfd.h>
  38
  39static bool ufd_version_check(void)
  40{
  41    struct uffdio_api api_struct;
  42    uint64_t ioctl_mask;
  43
  44    int ufd = ufd = syscall(__NR_userfaultfd, O_CLOEXEC);
  45
  46    if (ufd == -1) {
  47        g_test_message("Skipping test: userfaultfd not available");
  48        return false;
  49    }
  50
  51    api_struct.api = UFFD_API;
  52    api_struct.features = 0;
  53    if (ioctl(ufd, UFFDIO_API, &api_struct)) {
  54        g_test_message("Skipping test: UFFDIO_API failed");
  55        return false;
  56    }
  57
  58    ioctl_mask = (__u64)1 << _UFFDIO_REGISTER |
  59                 (__u64)1 << _UFFDIO_UNREGISTER;
  60    if ((api_struct.ioctls & ioctl_mask) != ioctl_mask) {
  61        g_test_message("Skipping test: Missing userfault feature");
  62        return false;
  63    }
  64
  65    return true;
  66}
  67
  68#else
  69static bool ufd_version_check(void)
  70{
  71    g_test_message("Skipping test: Userfault not available (builtdtime)");
  72    return false;
  73}
  74
  75#endif
  76
  77static const char *tmpfs;
  78
  79/* A simple PC boot sector that modifies memory (1-100MB) quickly
  80 * outputing a 'B' every so often if it's still running.
  81 */
  82unsigned char bootsect[] = {
  83  0xfa, 0x0f, 0x01, 0x16, 0x74, 0x7c, 0x66, 0xb8, 0x01, 0x00, 0x00, 0x00,
  84  0x0f, 0x22, 0xc0, 0x66, 0xea, 0x20, 0x7c, 0x00, 0x00, 0x08, 0x00, 0x00,
  85  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xe4, 0x92, 0x0c, 0x02,
  86  0xe6, 0x92, 0xb8, 0x10, 0x00, 0x00, 0x00, 0x8e, 0xd8, 0x66, 0xb8, 0x41,
  87  0x00, 0x66, 0xba, 0xf8, 0x03, 0xee, 0xb3, 0x00, 0xb8, 0x00, 0x00, 0x10,
  88  0x00, 0xfe, 0x00, 0x05, 0x00, 0x10, 0x00, 0x00, 0x3d, 0x00, 0x00, 0x40,
  89  0x06, 0x7c, 0xf2, 0xfe, 0xc3, 0x75, 0xe9, 0x66, 0xb8, 0x42, 0x00, 0x66,
  90  0xba, 0xf8, 0x03, 0xee, 0xeb, 0xde, 0x66, 0x90, 0x00, 0x00, 0x00, 0x00,
  91  0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0x00, 0x00, 0x00, 0x9a, 0xcf, 0x00,
  92  0xff, 0xff, 0x00, 0x00, 0x00, 0x92, 0xcf, 0x00, 0x27, 0x00, 0x5c, 0x7c,
  93  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
  94  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
  95  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
  96  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
  97  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
  98  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
  99  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
 100  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
 101  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
 102  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
 103  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
 104  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
 105  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
 106  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
 107  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
 108  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
 109  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
 110  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
 111  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
 112  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
 113  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
 114  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
 115  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
 116  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
 117  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
 118  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
 119  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
 120  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
 121  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
 122  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
 123  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
 124  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
 125  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x55, 0xaa
 126};
 127
 128static void init_bootfile_x86(const char *bootpath)
 129{
 130    FILE *bootfile = fopen(bootpath, "wb");
 131
 132    g_assert_cmpint(fwrite(bootsect, 512, 1, bootfile), ==, 1);
 133    fclose(bootfile);
 134}
 135
 136static void init_bootfile_ppc(const char *bootpath)
 137{
 138    FILE *bootfile;
 139    char buf[MIN_NVRAM_SIZE];
 140    ChrpNvramPartHdr *header = (ChrpNvramPartHdr *)buf;
 141
 142    memset(buf, 0, MIN_NVRAM_SIZE);
 143
 144    /* Create a "common" partition in nvram to store boot-command property */
 145
 146    header->signature = CHRP_NVPART_SYSTEM;
 147    memcpy(header->name, "common", 6);
 148    chrp_nvram_finish_partition(header, MIN_NVRAM_SIZE);
 149
 150    /* FW_MAX_SIZE is 4MB, but slof.bin is only 900KB,
 151     * so let's modify memory between 1MB and 100MB
 152     * to do like PC bootsector
 153     */
 154
 155    sprintf(buf + 16,
 156            "boot-command=hex .\" _\" begin %x %x do i c@ 1 + i c! 1000 +loop "
 157            ".\" B\" 0 until", end_address, start_address);
 158
 159    /* Write partition to the NVRAM file */
 160
 161    bootfile = fopen(bootpath, "wb");
 162    g_assert_cmpint(fwrite(buf, MIN_NVRAM_SIZE, 1, bootfile), ==, 1);
 163    fclose(bootfile);
 164}
 165
 166/*
 167 * Wait for some output in the serial output file,
 168 * we get an 'A' followed by an endless string of 'B's
 169 * but on the destination we won't have the A.
 170 */
 171static void wait_for_serial(const char *side)
 172{
 173    char *serialpath = g_strdup_printf("%s/%s", tmpfs, side);
 174    FILE *serialfile = fopen(serialpath, "r");
 175    const char *arch = qtest_get_arch();
 176    int started = (strcmp(side, "src_serial") == 0 &&
 177                   strcmp(arch, "ppc64") == 0) ? 0 : 1;
 178
 179    g_free(serialpath);
 180    do {
 181        int readvalue = fgetc(serialfile);
 182
 183        if (!started) {
 184            /* SLOF prints its banner before starting test,
 185             * to ignore it, mark the start of the test with '_',
 186             * ignore all characters until this marker
 187             */
 188            switch (readvalue) {
 189            case '_':
 190                started = 1;
 191                break;
 192            case EOF:
 193                fseek(serialfile, 0, SEEK_SET);
 194                usleep(1000);
 195                break;
 196            }
 197            continue;
 198        }
 199        switch (readvalue) {
 200        case 'A':
 201            /* Fine */
 202            break;
 203
 204        case 'B':
 205            /* It's alive! */
 206            fclose(serialfile);
 207            return;
 208
 209        case EOF:
 210            started = (strcmp(side, "src_serial") == 0 &&
 211                       strcmp(arch, "ppc64") == 0) ? 0 : 1;
 212            fseek(serialfile, 0, SEEK_SET);
 213            usleep(1000);
 214            break;
 215
 216        default:
 217            fprintf(stderr, "Unexpected %d on %s serial\n", readvalue, side);
 218            g_assert_not_reached();
 219        }
 220    } while (true);
 221}
 222
 223/*
 224 * Events can get in the way of responses we are actually waiting for.
 225 */
 226static QDict *return_or_event(QDict *response)
 227{
 228    const char *event_string;
 229    if (!qdict_haskey(response, "event")) {
 230        return response;
 231    }
 232
 233    /* OK, it was an event */
 234    event_string = qdict_get_str(response, "event");
 235    if (!strcmp(event_string, "STOP")) {
 236        got_stop = true;
 237    }
 238    QDECREF(response);
 239    return return_or_event(qtest_qmp_receive(global_qtest));
 240}
 241
 242
 243/*
 244 * It's tricky to use qemu's migration event capability with qtest,
 245 * events suddenly appearing confuse the qmp()/hmp() responses.
 246 * so wait for a couple of passes to have happened before
 247 * going postcopy.
 248 */
 249
 250static uint64_t get_migration_pass(void)
 251{
 252    QDict *rsp, *rsp_return, *rsp_ram;
 253    uint64_t result;
 254
 255    rsp = return_or_event(qmp("{ 'execute': 'query-migrate' }"));
 256    rsp_return = qdict_get_qdict(rsp, "return");
 257    if (!qdict_haskey(rsp_return, "ram")) {
 258        /* Still in setup */
 259        result = 0;
 260    } else {
 261        rsp_ram = qdict_get_qdict(rsp_return, "ram");
 262        result = qdict_get_try_int(rsp_ram, "dirty-sync-count", 0);
 263    }
 264    QDECREF(rsp);
 265    return result;
 266}
 267
 268static void wait_for_migration_complete(void)
 269{
 270    QDict *rsp, *rsp_return;
 271    bool completed;
 272
 273    do {
 274        const char *status;
 275
 276        rsp = return_or_event(qmp("{ 'execute': 'query-migrate' }"));
 277        rsp_return = qdict_get_qdict(rsp, "return");
 278        status = qdict_get_str(rsp_return, "status");
 279        completed = strcmp(status, "completed") == 0;
 280        g_assert_cmpstr(status, !=,  "failed");
 281        QDECREF(rsp);
 282        usleep(1000 * 100);
 283    } while (!completed);
 284}
 285
 286static void wait_for_migration_pass(void)
 287{
 288    uint64_t initial_pass = get_migration_pass();
 289    uint64_t pass;
 290
 291    /* Wait for the 1st sync */
 292    do {
 293        initial_pass = get_migration_pass();
 294        if (got_stop || initial_pass) {
 295            break;
 296        }
 297        usleep(1000 * 100);
 298    } while (true);
 299
 300    do {
 301        usleep(1000 * 100);
 302        pass = get_migration_pass();
 303    } while (pass == initial_pass && !got_stop);
 304}
 305
 306static void check_guests_ram(void)
 307{
 308    /* Our ASM test will have been incrementing one byte from each page from
 309     * 1MB to <100MB in order.
 310     * This gives us a constraint that any page's byte should be equal or less
 311     * than the previous pages byte (mod 256); and they should all be equal
 312     * except for one transition at the point where we meet the incrementer.
 313     * (We're running this with the guest stopped).
 314     */
 315    unsigned address;
 316    uint8_t first_byte;
 317    uint8_t last_byte;
 318    bool hit_edge = false;
 319    bool bad = false;
 320
 321    qtest_memread(global_qtest, start_address, &first_byte, 1);
 322    last_byte = first_byte;
 323
 324    for (address = start_address + 4096; address < end_address; address += 4096)
 325    {
 326        uint8_t b;
 327        qtest_memread(global_qtest, address, &b, 1);
 328        if (b != last_byte) {
 329            if (((b + 1) % 256) == last_byte && !hit_edge) {
 330                /* This is OK, the guest stopped at the point of
 331                 * incrementing the previous page but didn't get
 332                 * to us yet.
 333                 */
 334                hit_edge = true;
 335            } else {
 336                fprintf(stderr, "Memory content inconsistency at %x"
 337                                " first_byte = %x last_byte = %x current = %x"
 338                                " hit_edge = %x\n",
 339                                address, first_byte, last_byte, b, hit_edge);
 340                bad = true;
 341            }
 342        }
 343        last_byte = b;
 344    }
 345    g_assert_false(bad);
 346}
 347
 348static void cleanup(const char *filename)
 349{
 350    char *path = g_strdup_printf("%s/%s", tmpfs, filename);
 351
 352    unlink(path);
 353    g_free(path);
 354}
 355
 356static void test_migrate(void)
 357{
 358    char *uri = g_strdup_printf("unix:%s/migsocket", tmpfs);
 359    QTestState *global = global_qtest, *from, *to;
 360    unsigned char dest_byte_a, dest_byte_b, dest_byte_c, dest_byte_d;
 361    gchar *cmd, *cmd_src, *cmd_dst;
 362    QDict *rsp;
 363
 364    char *bootpath = g_strdup_printf("%s/bootsect", tmpfs);
 365    const char *arch = qtest_get_arch();
 366
 367    got_stop = false;
 368
 369    if (strcmp(arch, "i386") == 0 || strcmp(arch, "x86_64") == 0) {
 370        init_bootfile_x86(bootpath);
 371        cmd_src = g_strdup_printf("-machine accel=kvm:tcg -m 150M"
 372                                  " -name pcsource,debug-threads=on"
 373                                  " -serial file:%s/src_serial"
 374                                  " -drive file=%s,format=raw",
 375                                  tmpfs, bootpath);
 376        cmd_dst = g_strdup_printf("-machine accel=kvm:tcg -m 150M"
 377                                  " -name pcdest,debug-threads=on"
 378                                  " -serial file:%s/dest_serial"
 379                                  " -drive file=%s,format=raw"
 380                                  " -incoming %s",
 381                                  tmpfs, bootpath, uri);
 382    } else if (strcmp(arch, "ppc64") == 0) {
 383        const char *accel;
 384
 385        /* On ppc64, the test only works with kvm-hv, but not with kvm-pr */
 386        accel = access("/sys/module/kvm_hv", F_OK) ? "tcg" : "kvm:tcg";
 387        init_bootfile_ppc(bootpath);
 388        cmd_src = g_strdup_printf("-machine accel=%s -m 256M"
 389                                  " -name pcsource,debug-threads=on"
 390                                  " -serial file:%s/src_serial"
 391                                  " -drive file=%s,if=pflash,format=raw",
 392                                  accel, tmpfs, bootpath);
 393        cmd_dst = g_strdup_printf("-machine accel=%s -m 256M"
 394                                  " -name pcdest,debug-threads=on"
 395                                  " -serial file:%s/dest_serial"
 396                                  " -incoming %s",
 397                                  accel, tmpfs, uri);
 398    } else {
 399        g_assert_not_reached();
 400    }
 401
 402    g_free(bootpath);
 403
 404    from = qtest_start(cmd_src);
 405    g_free(cmd_src);
 406
 407    to = qtest_init(cmd_dst);
 408    g_free(cmd_dst);
 409
 410    global_qtest = from;
 411    rsp = qmp("{ 'execute': 'migrate-set-capabilities',"
 412                  "'arguments': { "
 413                      "'capabilities': [ {"
 414                          "'capability': 'postcopy-ram',"
 415                          "'state': true } ] } }");
 416    g_assert(qdict_haskey(rsp, "return"));
 417    QDECREF(rsp);
 418
 419    global_qtest = to;
 420    rsp = qmp("{ 'execute': 'migrate-set-capabilities',"
 421                  "'arguments': { "
 422                      "'capabilities': [ {"
 423                          "'capability': 'postcopy-ram',"
 424                          "'state': true } ] } }");
 425    g_assert(qdict_haskey(rsp, "return"));
 426    QDECREF(rsp);
 427
 428    /* We want to pick a speed slow enough that the test completes
 429     * quickly, but that it doesn't complete precopy even on a slow
 430     * machine, so also set the downtime.
 431     */
 432    global_qtest = from;
 433    rsp = qmp("{ 'execute': 'migrate_set_speed',"
 434              "'arguments': { 'value': 100000000 } }");
 435    g_assert(qdict_haskey(rsp, "return"));
 436    QDECREF(rsp);
 437
 438    /* 1ms downtime - it should never finish precopy */
 439    rsp = qmp("{ 'execute': 'migrate_set_downtime',"
 440              "'arguments': { 'value': 0.001 } }");
 441    g_assert(qdict_haskey(rsp, "return"));
 442    QDECREF(rsp);
 443
 444
 445    /* Wait for the first serial output from the source */
 446    wait_for_serial("src_serial");
 447
 448    cmd = g_strdup_printf("{ 'execute': 'migrate',"
 449                          "'arguments': { 'uri': '%s' } }",
 450                          uri);
 451    rsp = qmp(cmd);
 452    g_free(cmd);
 453    g_assert(qdict_haskey(rsp, "return"));
 454    QDECREF(rsp);
 455
 456    wait_for_migration_pass();
 457
 458    rsp = return_or_event(qmp("{ 'execute': 'migrate-start-postcopy' }"));
 459    g_assert(qdict_haskey(rsp, "return"));
 460    QDECREF(rsp);
 461
 462    if (!got_stop) {
 463        qmp_eventwait("STOP");
 464    }
 465
 466    global_qtest = to;
 467    qmp_eventwait("RESUME");
 468
 469    wait_for_serial("dest_serial");
 470    global_qtest = from;
 471    wait_for_migration_complete();
 472
 473    qtest_quit(from);
 474
 475    global_qtest = to;
 476
 477    qtest_memread(to, start_address, &dest_byte_a, 1);
 478
 479    /* Destination still running, wait for a byte to change */
 480    do {
 481        qtest_memread(to, start_address, &dest_byte_b, 1);
 482        usleep(10 * 1000);
 483    } while (dest_byte_a == dest_byte_b);
 484
 485    qmp("{ 'execute' : 'stop'}");
 486    /* With it stopped, check nothing changes */
 487    qtest_memread(to, start_address, &dest_byte_c, 1);
 488    sleep(1);
 489    qtest_memread(to, start_address, &dest_byte_d, 1);
 490    g_assert_cmpint(dest_byte_c, ==, dest_byte_d);
 491
 492    check_guests_ram();
 493
 494    qtest_quit(to);
 495    g_free(uri);
 496
 497    global_qtest = global;
 498
 499    cleanup("bootsect");
 500    cleanup("migsocket");
 501    cleanup("src_serial");
 502    cleanup("dest_serial");
 503}
 504
 505int main(int argc, char **argv)
 506{
 507    char template[] = "/tmp/postcopy-test-XXXXXX";
 508    int ret;
 509
 510    g_test_init(&argc, &argv, NULL);
 511
 512    if (!ufd_version_check()) {
 513        return 0;
 514    }
 515
 516    tmpfs = mkdtemp(template);
 517    if (!tmpfs) {
 518        g_test_message("mkdtemp on path (%s): %s\n", template, strerror(errno));
 519    }
 520    g_assert(tmpfs);
 521
 522    module_call_init(MODULE_INIT_QOM);
 523
 524    qtest_add_func("/postcopy", test_migrate);
 525
 526    ret = g_test_run();
 527
 528    g_assert_cmpint(ret, ==, 0);
 529
 530    ret = rmdir(tmpfs);
 531    if (ret != 0) {
 532        g_test_message("unable to rmdir: path (%s): %s\n",
 533                       tmpfs, strerror(errno));
 534    }
 535
 536    return ret;
 537}
 538