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/openbios_firmware_abi.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    struct OpenBIOS_nvpart_v1 *header = (struct OpenBIOS_nvpart_v1 *)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 = OPENBIOS_PART_SYSTEM;
 147    memcpy(header->name, "common", 6);
 148    OpenBIOS_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    do {
 180        int readvalue = fgetc(serialfile);
 181
 182        if (!started) {
 183            /* SLOF prints its banner before starting test,
 184             * to ignore it, mark the start of the test with '_',
 185             * ignore all characters until this marker
 186             */
 187            switch (readvalue) {
 188            case '_':
 189                started = 1;
 190                break;
 191            case EOF:
 192                fseek(serialfile, 0, SEEK_SET);
 193                usleep(1000);
 194                break;
 195            }
 196            continue;
 197        }
 198        switch (readvalue) {
 199        case 'A':
 200            /* Fine */
 201            break;
 202
 203        case 'B':
 204            /* It's alive! */
 205            fclose(serialfile);
 206            g_free(serialpath);
 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        QDECREF(rsp);
 264    }
 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}
 354
 355static void test_migrate(void)
 356{
 357    char *uri = g_strdup_printf("unix:%s/migsocket", tmpfs);
 358    QTestState *global = global_qtest, *from, *to;
 359    unsigned char dest_byte_a, dest_byte_b, dest_byte_c, dest_byte_d;
 360    gchar *cmd, *cmd_src, *cmd_dst;
 361    QDict *rsp;
 362
 363    char *bootpath = g_strdup_printf("%s/bootsect", tmpfs);
 364    const char *arch = qtest_get_arch();
 365
 366    got_stop = false;
 367
 368    if (strcmp(arch, "i386") == 0 || strcmp(arch, "x86_64") == 0) {
 369        init_bootfile_x86(bootpath);
 370        cmd_src = g_strdup_printf("-machine accel=kvm:tcg -m 150M"
 371                                  " -name pcsource,debug-threads=on"
 372                                  " -serial file:%s/src_serial"
 373                                  " -drive file=%s,format=raw",
 374                                  tmpfs, bootpath);
 375        cmd_dst = g_strdup_printf("-machine accel=kvm:tcg -m 150M"
 376                                  " -name pcdest,debug-threads=on"
 377                                  " -serial file:%s/dest_serial"
 378                                  " -drive file=%s,format=raw"
 379                                  " -incoming %s",
 380                                  tmpfs, bootpath, uri);
 381    } else if (strcmp(arch, "ppc64") == 0) {
 382        init_bootfile_ppc(bootpath);
 383        cmd_src = g_strdup_printf("-machine accel=kvm:tcg -m 256M"
 384                                  " -name pcsource,debug-threads=on"
 385                                  " -serial file:%s/src_serial"
 386                                  " -drive file=%s,if=pflash,format=raw",
 387                                  tmpfs, bootpath);
 388        cmd_dst = g_strdup_printf("-machine accel=kvm:tcg -m 256M"
 389                                  " -name pcdest,debug-threads=on"
 390                                  " -serial file:%s/dest_serial"
 391                                  " -incoming %s",
 392                                  tmpfs, uri);
 393    } else {
 394        g_assert_not_reached();
 395    }
 396
 397    from = qtest_start(cmd_src);
 398    g_free(cmd_src);
 399
 400    to = qtest_init(cmd_dst);
 401    g_free(cmd_dst);
 402
 403    global_qtest = from;
 404    rsp = qmp("{ 'execute': 'migrate-set-capabilities',"
 405                  "'arguments': { "
 406                      "'capabilities': [ {"
 407                          "'capability': 'postcopy-ram',"
 408                          "'state': true } ] } }");
 409    g_assert(qdict_haskey(rsp, "return"));
 410    QDECREF(rsp);
 411
 412    global_qtest = to;
 413    rsp = qmp("{ 'execute': 'migrate-set-capabilities',"
 414                  "'arguments': { "
 415                      "'capabilities': [ {"
 416                          "'capability': 'postcopy-ram',"
 417                          "'state': true } ] } }");
 418    g_assert(qdict_haskey(rsp, "return"));
 419    QDECREF(rsp);
 420
 421    /* We want to pick a speed slow enough that the test completes
 422     * quickly, but that it doesn't complete precopy even on a slow
 423     * machine, so also set the downtime.
 424     */
 425    global_qtest = from;
 426    rsp = qmp("{ 'execute': 'migrate_set_speed',"
 427              "'arguments': { 'value': 100000000 } }");
 428    g_assert(qdict_haskey(rsp, "return"));
 429    QDECREF(rsp);
 430
 431    /* 1ms downtime - it should never finish precopy */
 432    rsp = qmp("{ 'execute': 'migrate_set_downtime',"
 433              "'arguments': { 'value': 0.001 } }");
 434    g_assert(qdict_haskey(rsp, "return"));
 435    QDECREF(rsp);
 436
 437
 438    /* Wait for the first serial output from the source */
 439    wait_for_serial("src_serial");
 440
 441    cmd = g_strdup_printf("{ 'execute': 'migrate',"
 442                          "'arguments': { 'uri': '%s' } }",
 443                          uri);
 444    rsp = qmp(cmd);
 445    g_free(cmd);
 446    g_assert(qdict_haskey(rsp, "return"));
 447    QDECREF(rsp);
 448
 449    wait_for_migration_pass();
 450
 451    rsp = return_or_event(qmp("{ 'execute': 'migrate-start-postcopy' }"));
 452    g_assert(qdict_haskey(rsp, "return"));
 453    QDECREF(rsp);
 454
 455    if (!got_stop) {
 456        qmp_eventwait("STOP");
 457    }
 458
 459    global_qtest = to;
 460    qmp_eventwait("RESUME");
 461
 462    wait_for_serial("dest_serial");
 463    global_qtest = from;
 464    wait_for_migration_complete();
 465
 466    qtest_quit(from);
 467
 468    global_qtest = to;
 469
 470    qtest_memread(to, start_address, &dest_byte_a, 1);
 471
 472    /* Destination still running, wait for a byte to change */
 473    do {
 474        qtest_memread(to, start_address, &dest_byte_b, 1);
 475        usleep(10 * 1000);
 476    } while (dest_byte_a == dest_byte_b);
 477
 478    qmp("{ 'execute' : 'stop'}");
 479    /* With it stopped, check nothing changes */
 480    qtest_memread(to, start_address, &dest_byte_c, 1);
 481    sleep(1);
 482    qtest_memread(to, start_address, &dest_byte_d, 1);
 483    g_assert_cmpint(dest_byte_c, ==, dest_byte_d);
 484
 485    check_guests_ram();
 486
 487    qtest_quit(to);
 488    g_free(uri);
 489
 490    global_qtest = global;
 491
 492    cleanup("bootsect");
 493    cleanup("migsocket");
 494    cleanup("src_serial");
 495    cleanup("dest_serial");
 496}
 497
 498int main(int argc, char **argv)
 499{
 500    char template[] = "/tmp/postcopy-test-XXXXXX";
 501    int ret;
 502
 503    g_test_init(&argc, &argv, NULL);
 504
 505    if (!ufd_version_check()) {
 506        return 0;
 507    }
 508
 509    tmpfs = mkdtemp(template);
 510    if (!tmpfs) {
 511        g_test_message("mkdtemp on path (%s): %s\n", template, strerror(errno));
 512    }
 513    g_assert(tmpfs);
 514
 515    module_call_init(MODULE_INIT_QOM);
 516
 517    qtest_add_func("/postcopy", test_migrate);
 518
 519    ret = g_test_run();
 520
 521    g_assert_cmpint(ret, ==, 0);
 522
 523    ret = rmdir(tmpfs);
 524    if (ret != 0) {
 525        g_test_message("unable to rmdir: path (%s): %s\n",
 526                       tmpfs, strerror(errno));
 527    }
 528
 529    return ret;
 530}
 531