qemu/tests/migration-test.c
<<
>>
Prefs
   1/*
   2 * QTest testcase for migration
   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 "chardev/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 = 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 *wait_command(QTestState *who, const char *command)
 227{
 228    const char *event_string;
 229    QDict *response;
 230
 231    response = qtest_qmp(who, command);
 232
 233    while (qdict_haskey(response, "event")) {
 234        /* OK, it was an event */
 235        event_string = qdict_get_str(response, "event");
 236        if (!strcmp(event_string, "STOP")) {
 237            got_stop = true;
 238        }
 239        QDECREF(response);
 240        response = qtest_qmp_receive(who);
 241    }
 242    return response;
 243}
 244
 245
 246/*
 247 * It's tricky to use qemu's migration event capability with qtest,
 248 * events suddenly appearing confuse the qmp()/hmp() responses.
 249 */
 250
 251static uint64_t get_migration_pass(QTestState *who)
 252{
 253    QDict *rsp, *rsp_return, *rsp_ram;
 254    uint64_t result;
 255
 256    rsp = wait_command(who, "{ 'execute': 'query-migrate' }");
 257    rsp_return = qdict_get_qdict(rsp, "return");
 258    if (!qdict_haskey(rsp_return, "ram")) {
 259        /* Still in setup */
 260        result = 0;
 261    } else {
 262        rsp_ram = qdict_get_qdict(rsp_return, "ram");
 263        result = qdict_get_try_int(rsp_ram, "dirty-sync-count", 0);
 264    }
 265    QDECREF(rsp);
 266    return result;
 267}
 268
 269static void wait_for_migration_complete(QTestState *who)
 270{
 271    QDict *rsp, *rsp_return;
 272    bool completed;
 273
 274    do {
 275        const char *status;
 276
 277        rsp = wait_command(who, "{ 'execute': 'query-migrate' }");
 278        rsp_return = qdict_get_qdict(rsp, "return");
 279        status = qdict_get_str(rsp_return, "status");
 280        completed = strcmp(status, "completed") == 0;
 281        g_assert_cmpstr(status, !=,  "failed");
 282        QDECREF(rsp);
 283        usleep(1000 * 100);
 284    } while (!completed);
 285}
 286
 287static void wait_for_migration_pass(QTestState *who)
 288{
 289    uint64_t initial_pass = get_migration_pass(who);
 290    uint64_t pass;
 291
 292    /* Wait for the 1st sync */
 293    do {
 294        initial_pass = get_migration_pass(who);
 295        if (got_stop || initial_pass) {
 296            break;
 297        }
 298        usleep(1000 * 100);
 299    } while (true);
 300
 301    do {
 302        usleep(1000 * 100);
 303        pass = get_migration_pass(who);
 304    } while (pass == initial_pass && !got_stop);
 305}
 306
 307static void check_guests_ram(QTestState *who)
 308{
 309    /* Our ASM test will have been incrementing one byte from each page from
 310     * 1MB to <100MB in order.
 311     * This gives us a constraint that any page's byte should be equal or less
 312     * than the previous pages byte (mod 256); and they should all be equal
 313     * except for one transition at the point where we meet the incrementer.
 314     * (We're running this with the guest stopped).
 315     */
 316    unsigned address;
 317    uint8_t first_byte;
 318    uint8_t last_byte;
 319    bool hit_edge = false;
 320    bool bad = false;
 321
 322    qtest_memread(who, start_address, &first_byte, 1);
 323    last_byte = first_byte;
 324
 325    for (address = start_address + 4096; address < end_address; address += 4096)
 326    {
 327        uint8_t b;
 328        qtest_memread(who, address, &b, 1);
 329        if (b != last_byte) {
 330            if (((b + 1) % 256) == last_byte && !hit_edge) {
 331                /* This is OK, the guest stopped at the point of
 332                 * incrementing the previous page but didn't get
 333                 * to us yet.
 334                 */
 335                hit_edge = true;
 336            } else {
 337                fprintf(stderr, "Memory content inconsistency at %x"
 338                                " first_byte = %x last_byte = %x current = %x"
 339                                " hit_edge = %x\n",
 340                                address, first_byte, last_byte, b, hit_edge);
 341                bad = true;
 342            }
 343        }
 344        last_byte = b;
 345    }
 346    g_assert_false(bad);
 347}
 348
 349static void cleanup(const char *filename)
 350{
 351    char *path = g_strdup_printf("%s/%s", tmpfs, filename);
 352
 353    unlink(path);
 354    g_free(path);
 355}
 356
 357static void migrate_check_parameter(QTestState *who, const char *parameter,
 358                                    const char *value)
 359{
 360    QDict *rsp, *rsp_return;
 361    const char *result;
 362
 363    rsp = wait_command(who, "{ 'execute': 'query-migrate-parameters' }");
 364    rsp_return = qdict_get_qdict(rsp, "return");
 365    result = g_strdup_printf("%" PRId64,
 366                             qdict_get_try_int(rsp_return,  parameter, -1));
 367    g_assert_cmpstr(result, ==, value);
 368    QDECREF(rsp);
 369}
 370
 371static void migrate_set_downtime(QTestState *who, const double value)
 372{
 373    QDict *rsp;
 374    gchar *cmd;
 375    char *expected;
 376    int64_t result_int;
 377
 378    cmd = g_strdup_printf("{ 'execute': 'migrate_set_downtime',"
 379                          "'arguments': { 'value': %g } }", value);
 380    rsp = qtest_qmp(who, cmd);
 381    g_free(cmd);
 382    g_assert(qdict_haskey(rsp, "return"));
 383    QDECREF(rsp);
 384    result_int = value * 1000L;
 385    expected = g_strdup_printf("%" PRId64, result_int);
 386    migrate_check_parameter(who, "downtime-limit", expected);
 387    g_free(expected);
 388}
 389
 390static void migrate_set_speed(QTestState *who, const char *value)
 391{
 392    QDict *rsp;
 393    gchar *cmd;
 394
 395    cmd = g_strdup_printf("{ 'execute': 'migrate_set_speed',"
 396                          "'arguments': { 'value': %s } }", value);
 397    rsp = qtest_qmp(who, cmd);
 398    g_free(cmd);
 399    g_assert(qdict_haskey(rsp, "return"));
 400    QDECREF(rsp);
 401    migrate_check_parameter(who, "max-bandwidth", value);
 402}
 403
 404static void migrate_set_capability(QTestState *who, const char *capability,
 405                                   const char *value)
 406{
 407    QDict *rsp;
 408    gchar *cmd;
 409
 410    cmd = g_strdup_printf("{ 'execute': 'migrate-set-capabilities',"
 411                          "'arguments': { "
 412                          "'capabilities': [ { "
 413                          "'capability': '%s', 'state': %s } ] } }",
 414                          capability, value);
 415    rsp = qtest_qmp(who, cmd);
 416    g_free(cmd);
 417    g_assert(qdict_haskey(rsp, "return"));
 418    QDECREF(rsp);
 419}
 420
 421static void migrate(QTestState *who, const char *uri)
 422{
 423    QDict *rsp;
 424    gchar *cmd;
 425
 426    cmd = g_strdup_printf("{ 'execute': 'migrate',"
 427                          "'arguments': { 'uri': '%s' } }",
 428                          uri);
 429    rsp = qtest_qmp(who, cmd);
 430    g_free(cmd);
 431    g_assert(qdict_haskey(rsp, "return"));
 432    QDECREF(rsp);
 433}
 434
 435static void test_migrate_start(QTestState **from, QTestState **to,
 436                               const char *uri)
 437{
 438    gchar *cmd_src, *cmd_dst;
 439    char *bootpath = g_strdup_printf("%s/bootsect", tmpfs);
 440    const char *arch = qtest_get_arch();
 441
 442    got_stop = false;
 443
 444    if (strcmp(arch, "i386") == 0 || strcmp(arch, "x86_64") == 0) {
 445        init_bootfile_x86(bootpath);
 446        cmd_src = g_strdup_printf("-machine accel=kvm:tcg -m 150M"
 447                                  " -name pcsource,debug-threads=on"
 448                                  " -serial file:%s/src_serial"
 449                                  " -drive file=%s,format=raw",
 450                                  tmpfs, bootpath);
 451        cmd_dst = g_strdup_printf("-machine accel=kvm:tcg -m 150M"
 452                                  " -name pcdest,debug-threads=on"
 453                                  " -serial file:%s/dest_serial"
 454                                  " -drive file=%s,format=raw"
 455                                  " -incoming %s",
 456                                  tmpfs, bootpath, uri);
 457    } else if (strcmp(arch, "ppc64") == 0) {
 458        const char *accel;
 459
 460        /* On ppc64, the test only works with kvm-hv, but not with kvm-pr */
 461        accel = access("/sys/module/kvm_hv", F_OK) ? "tcg" : "kvm:tcg";
 462        init_bootfile_ppc(bootpath);
 463        cmd_src = g_strdup_printf("-machine accel=%s,cap-htm=off -m 256M"
 464                                  " -name pcsource,debug-threads=on"
 465                                  " -serial file:%s/src_serial"
 466                                  " -drive file=%s,if=pflash,format=raw",
 467                                  accel, tmpfs, bootpath);
 468        cmd_dst = g_strdup_printf("-machine accel=%s,cap-htm=off -m 256M"
 469                                  " -name pcdest,debug-threads=on"
 470                                  " -serial file:%s/dest_serial"
 471                                  " -incoming %s",
 472                                  accel, tmpfs, uri);
 473    } else {
 474        g_assert_not_reached();
 475    }
 476
 477    g_free(bootpath);
 478
 479    *from = qtest_start(cmd_src);
 480    g_free(cmd_src);
 481
 482    *to = qtest_init(cmd_dst);
 483    g_free(cmd_dst);
 484}
 485
 486static void test_migrate_end(QTestState *from, QTestState *to)
 487{
 488    unsigned char dest_byte_a, dest_byte_b, dest_byte_c, dest_byte_d;
 489
 490    qtest_quit(from);
 491
 492    qtest_memread(to, start_address, &dest_byte_a, 1);
 493
 494    /* Destination still running, wait for a byte to change */
 495    do {
 496        qtest_memread(to, start_address, &dest_byte_b, 1);
 497        usleep(10 * 1000);
 498    } while (dest_byte_a == dest_byte_b);
 499
 500    qtest_qmp_discard_response(to, "{ 'execute' : 'stop'}");
 501    /* With it stopped, check nothing changes */
 502    qtest_memread(to, start_address, &dest_byte_c, 1);
 503    sleep(1);
 504    qtest_memread(to, start_address, &dest_byte_d, 1);
 505    g_assert_cmpint(dest_byte_c, ==, dest_byte_d);
 506
 507    check_guests_ram(to);
 508
 509    qtest_quit(to);
 510
 511    cleanup("bootsect");
 512    cleanup("migsocket");
 513    cleanup("src_serial");
 514    cleanup("dest_serial");
 515}
 516
 517static void test_migrate(void)
 518{
 519    char *uri = g_strdup_printf("unix:%s/migsocket", tmpfs);
 520    QTestState *from, *to;
 521    QDict *rsp;
 522
 523    test_migrate_start(&from, &to, uri);
 524
 525    migrate_set_capability(from, "postcopy-ram", "true");
 526    migrate_set_capability(to, "postcopy-ram", "true");
 527
 528    /* We want to pick a speed slow enough that the test completes
 529     * quickly, but that it doesn't complete precopy even on a slow
 530     * machine, so also set the downtime.
 531     */
 532    migrate_set_speed(from, "100000000");
 533    migrate_set_downtime(from, 0.001);
 534
 535    /* Wait for the first serial output from the source */
 536    wait_for_serial("src_serial");
 537
 538    migrate(from, uri);
 539
 540    wait_for_migration_pass(from);
 541
 542    rsp = wait_command(from, "{ 'execute': 'migrate-start-postcopy' }");
 543    g_assert(qdict_haskey(rsp, "return"));
 544    QDECREF(rsp);
 545
 546    if (!got_stop) {
 547        qtest_qmp_eventwait(from, "STOP");
 548    }
 549
 550    qtest_qmp_eventwait(to, "RESUME");
 551
 552    wait_for_serial("dest_serial");
 553    wait_for_migration_complete(from);
 554
 555    g_free(uri);
 556
 557    test_migrate_end(from, to);
 558}
 559
 560int main(int argc, char **argv)
 561{
 562    char template[] = "/tmp/migration-test-XXXXXX";
 563    int ret;
 564
 565    g_test_init(&argc, &argv, NULL);
 566
 567    if (!ufd_version_check()) {
 568        return 0;
 569    }
 570
 571    tmpfs = mkdtemp(template);
 572    if (!tmpfs) {
 573        g_test_message("mkdtemp on path (%s): %s\n", template, strerror(errno));
 574    }
 575    g_assert(tmpfs);
 576
 577    module_call_init(MODULE_INIT_QOM);
 578
 579    qtest_add_func("/migration/postcopy/unix", test_migrate);
 580
 581    ret = g_test_run();
 582
 583    g_assert_cmpint(ret, ==, 0);
 584
 585    ret = rmdir(tmpfs);
 586    if (ret != 0) {
 587        g_test_message("unable to rmdir: path (%s): %s\n",
 588                       tmpfs, strerror(errno));
 589    }
 590
 591    return ret;
 592}
 593