qemu/tests/vhost-user-test.c
<<
>>
Prefs
   1/*
   2 * QTest testcase for the vhost-user
   3 *
   4 * Copyright (c) 2014 Virtual Open Systems Sarl.
   5 *
   6 * This work is licensed under the terms of the GNU GPL, version 2 or later.
   7 * See the COPYING file in the top-level directory.
   8 *
   9 */
  10
  11#include "qemu/osdep.h"
  12
  13#include "libqtest.h"
  14#include "qemu/option.h"
  15#include "qemu/range.h"
  16#include "qemu/sockets.h"
  17#include "sysemu/char.h"
  18#include "sysemu/sysemu.h"
  19
  20#include <linux/vhost.h>
  21#include <sys/vfs.h>
  22
  23/* GLIB version compatibility flags */
  24#if !GLIB_CHECK_VERSION(2, 26, 0)
  25#define G_TIME_SPAN_SECOND              (G_GINT64_CONSTANT(1000000))
  26#endif
  27
  28#if GLIB_CHECK_VERSION(2, 28, 0)
  29#define HAVE_MONOTONIC_TIME
  30#endif
  31
  32#define QEMU_CMD_ACCEL  " -machine accel=tcg"
  33#define QEMU_CMD_MEM    " -m %d -object memory-backend-file,id=mem,size=%dM,"\
  34                        "mem-path=%s,share=on -numa node,memdev=mem"
  35#define QEMU_CMD_CHR    " -chardev socket,id=%s,path=%s%s"
  36#define QEMU_CMD_NETDEV " -netdev vhost-user,id=net0,chardev=%s,vhostforce"
  37#define QEMU_CMD_NET    " -device virtio-net-pci,netdev=net0,romfile=./pc-bios/pxe-virtio.rom"
  38
  39#define QEMU_CMD        QEMU_CMD_ACCEL QEMU_CMD_MEM QEMU_CMD_CHR \
  40                        QEMU_CMD_NETDEV QEMU_CMD_NET
  41
  42#define HUGETLBFS_MAGIC       0x958458f6
  43
  44/*********** FROM hw/virtio/vhost-user.c *************************************/
  45
  46#define VHOST_MEMORY_MAX_NREGIONS    8
  47
  48#define VHOST_USER_F_PROTOCOL_FEATURES 30
  49#define VHOST_USER_PROTOCOL_F_LOG_SHMFD 1
  50
  51#define VHOST_LOG_PAGE 0x1000
  52
  53typedef enum VhostUserRequest {
  54    VHOST_USER_NONE = 0,
  55    VHOST_USER_GET_FEATURES = 1,
  56    VHOST_USER_SET_FEATURES = 2,
  57    VHOST_USER_SET_OWNER = 3,
  58    VHOST_USER_RESET_OWNER = 4,
  59    VHOST_USER_SET_MEM_TABLE = 5,
  60    VHOST_USER_SET_LOG_BASE = 6,
  61    VHOST_USER_SET_LOG_FD = 7,
  62    VHOST_USER_SET_VRING_NUM = 8,
  63    VHOST_USER_SET_VRING_ADDR = 9,
  64    VHOST_USER_SET_VRING_BASE = 10,
  65    VHOST_USER_GET_VRING_BASE = 11,
  66    VHOST_USER_SET_VRING_KICK = 12,
  67    VHOST_USER_SET_VRING_CALL = 13,
  68    VHOST_USER_SET_VRING_ERR = 14,
  69    VHOST_USER_GET_PROTOCOL_FEATURES = 15,
  70    VHOST_USER_SET_PROTOCOL_FEATURES = 16,
  71    VHOST_USER_SET_VRING_ENABLE = 18,
  72    VHOST_USER_MAX
  73} VhostUserRequest;
  74
  75typedef struct VhostUserMemoryRegion {
  76    uint64_t guest_phys_addr;
  77    uint64_t memory_size;
  78    uint64_t userspace_addr;
  79    uint64_t mmap_offset;
  80} VhostUserMemoryRegion;
  81
  82typedef struct VhostUserMemory {
  83    uint32_t nregions;
  84    uint32_t padding;
  85    VhostUserMemoryRegion regions[VHOST_MEMORY_MAX_NREGIONS];
  86} VhostUserMemory;
  87
  88typedef struct VhostUserLog {
  89    uint64_t mmap_size;
  90    uint64_t mmap_offset;
  91} VhostUserLog;
  92
  93typedef struct VhostUserMsg {
  94    VhostUserRequest request;
  95
  96#define VHOST_USER_VERSION_MASK     (0x3)
  97#define VHOST_USER_REPLY_MASK       (0x1<<2)
  98    uint32_t flags;
  99    uint32_t size; /* the following payload size */
 100    union {
 101#define VHOST_USER_VRING_IDX_MASK   (0xff)
 102#define VHOST_USER_VRING_NOFD_MASK  (0x1<<8)
 103        uint64_t u64;
 104        struct vhost_vring_state state;
 105        struct vhost_vring_addr addr;
 106        VhostUserMemory memory;
 107        VhostUserLog log;
 108    } payload;
 109} QEMU_PACKED VhostUserMsg;
 110
 111static VhostUserMsg m __attribute__ ((unused));
 112#define VHOST_USER_HDR_SIZE (sizeof(m.request) \
 113                            + sizeof(m.flags) \
 114                            + sizeof(m.size))
 115
 116#define VHOST_USER_PAYLOAD_SIZE (sizeof(m) - VHOST_USER_HDR_SIZE)
 117
 118/* The version of the protocol we support */
 119#define VHOST_USER_VERSION    (0x1)
 120/*****************************************************************************/
 121
 122typedef struct TestServer {
 123    gchar *socket_path;
 124    gchar *mig_path;
 125    gchar *chr_name;
 126    CharDriverState *chr;
 127    int fds_num;
 128    int fds[VHOST_MEMORY_MAX_NREGIONS];
 129    VhostUserMemory memory;
 130    CompatGMutex data_mutex;
 131    CompatGCond data_cond;
 132    int log_fd;
 133    uint64_t rings;
 134} TestServer;
 135
 136static const char *tmpfs;
 137static const char *root;
 138
 139static void wait_for_fds(TestServer *s)
 140{
 141    gint64 end_time;
 142
 143    g_mutex_lock(&s->data_mutex);
 144
 145    end_time = g_get_monotonic_time() + 5 * G_TIME_SPAN_SECOND;
 146    while (!s->fds_num) {
 147        if (!g_cond_wait_until(&s->data_cond, &s->data_mutex, end_time)) {
 148            /* timeout has passed */
 149            g_assert(s->fds_num);
 150            break;
 151        }
 152    }
 153
 154    /* check for sanity */
 155    g_assert_cmpint(s->fds_num, >, 0);
 156    g_assert_cmpint(s->fds_num, ==, s->memory.nregions);
 157
 158    g_mutex_unlock(&s->data_mutex);
 159}
 160
 161static void read_guest_mem(const void *data)
 162{
 163    TestServer *s = (void *)data;
 164    uint32_t *guest_mem;
 165    int i, j;
 166    size_t size;
 167
 168    wait_for_fds(s);
 169
 170    g_mutex_lock(&s->data_mutex);
 171
 172    /* iterate all regions */
 173    for (i = 0; i < s->fds_num; i++) {
 174
 175        /* We'll check only the region statring at 0x0*/
 176        if (s->memory.regions[i].guest_phys_addr != 0x0) {
 177            continue;
 178        }
 179
 180        g_assert_cmpint(s->memory.regions[i].memory_size, >, 1024);
 181
 182        size = s->memory.regions[i].memory_size +
 183            s->memory.regions[i].mmap_offset;
 184
 185        guest_mem = mmap(0, size, PROT_READ | PROT_WRITE,
 186                         MAP_SHARED, s->fds[i], 0);
 187
 188        g_assert(guest_mem != MAP_FAILED);
 189        guest_mem += (s->memory.regions[i].mmap_offset / sizeof(*guest_mem));
 190
 191        for (j = 0; j < 256; j++) {
 192            uint32_t a = readl(s->memory.regions[i].guest_phys_addr + j*4);
 193            uint32_t b = guest_mem[j];
 194
 195            g_assert_cmpint(a, ==, b);
 196        }
 197
 198        munmap(guest_mem, s->memory.regions[i].memory_size);
 199    }
 200
 201    g_mutex_unlock(&s->data_mutex);
 202}
 203
 204static void *thread_function(void *data)
 205{
 206    GMainLoop *loop = data;
 207    g_main_loop_run(loop);
 208    return NULL;
 209}
 210
 211static int chr_can_read(void *opaque)
 212{
 213    return VHOST_USER_HDR_SIZE;
 214}
 215
 216static void chr_read(void *opaque, const uint8_t *buf, int size)
 217{
 218    TestServer *s = opaque;
 219    CharDriverState *chr = s->chr;
 220    VhostUserMsg msg;
 221    uint8_t *p = (uint8_t *) &msg;
 222    int fd;
 223
 224    if (size != VHOST_USER_HDR_SIZE) {
 225        g_test_message("Wrong message size received %d\n", size);
 226        return;
 227    }
 228
 229    g_mutex_lock(&s->data_mutex);
 230    memcpy(p, buf, VHOST_USER_HDR_SIZE);
 231
 232    if (msg.size) {
 233        p += VHOST_USER_HDR_SIZE;
 234        size = qemu_chr_fe_read_all(chr, p, msg.size);
 235        if (size != msg.size) {
 236            g_test_message("Wrong message size received %d != %d\n",
 237                           size, msg.size);
 238            return;
 239        }
 240    }
 241
 242    switch (msg.request) {
 243    case VHOST_USER_GET_FEATURES:
 244        /* send back features to qemu */
 245        msg.flags |= VHOST_USER_REPLY_MASK;
 246        msg.size = sizeof(m.payload.u64);
 247        msg.payload.u64 = 0x1ULL << VHOST_F_LOG_ALL |
 248            0x1ULL << VHOST_USER_F_PROTOCOL_FEATURES;
 249        p = (uint8_t *) &msg;
 250        qemu_chr_fe_write_all(chr, p, VHOST_USER_HDR_SIZE + msg.size);
 251        break;
 252
 253    case VHOST_USER_SET_FEATURES:
 254        g_assert_cmpint(msg.payload.u64 & (0x1ULL << VHOST_USER_F_PROTOCOL_FEATURES),
 255                        !=, 0ULL);
 256        break;
 257
 258    case VHOST_USER_GET_PROTOCOL_FEATURES:
 259        /* send back features to qemu */
 260        msg.flags |= VHOST_USER_REPLY_MASK;
 261        msg.size = sizeof(m.payload.u64);
 262        msg.payload.u64 = 1 << VHOST_USER_PROTOCOL_F_LOG_SHMFD;
 263        p = (uint8_t *) &msg;
 264        qemu_chr_fe_write_all(chr, p, VHOST_USER_HDR_SIZE + msg.size);
 265        break;
 266
 267    case VHOST_USER_GET_VRING_BASE:
 268        /* send back vring base to qemu */
 269        msg.flags |= VHOST_USER_REPLY_MASK;
 270        msg.size = sizeof(m.payload.state);
 271        msg.payload.state.num = 0;
 272        p = (uint8_t *) &msg;
 273        qemu_chr_fe_write_all(chr, p, VHOST_USER_HDR_SIZE + msg.size);
 274
 275        assert(msg.payload.state.index < 2);
 276        s->rings &= ~(0x1ULL << msg.payload.state.index);
 277        break;
 278
 279    case VHOST_USER_SET_MEM_TABLE:
 280        /* received the mem table */
 281        memcpy(&s->memory, &msg.payload.memory, sizeof(msg.payload.memory));
 282        s->fds_num = qemu_chr_fe_get_msgfds(chr, s->fds, G_N_ELEMENTS(s->fds));
 283
 284        /* signal the test that it can continue */
 285        g_cond_signal(&s->data_cond);
 286        break;
 287
 288    case VHOST_USER_SET_VRING_KICK:
 289    case VHOST_USER_SET_VRING_CALL:
 290        /* consume the fd */
 291        qemu_chr_fe_get_msgfds(chr, &fd, 1);
 292        /*
 293         * This is a non-blocking eventfd.
 294         * The receive function forces it to be blocking,
 295         * so revert it back to non-blocking.
 296         */
 297        qemu_set_nonblock(fd);
 298        break;
 299
 300    case VHOST_USER_SET_LOG_BASE:
 301        if (s->log_fd != -1) {
 302            close(s->log_fd);
 303            s->log_fd = -1;
 304        }
 305        qemu_chr_fe_get_msgfds(chr, &s->log_fd, 1);
 306        msg.flags |= VHOST_USER_REPLY_MASK;
 307        msg.size = 0;
 308        p = (uint8_t *) &msg;
 309        qemu_chr_fe_write_all(chr, p, VHOST_USER_HDR_SIZE);
 310
 311        g_cond_signal(&s->data_cond);
 312        break;
 313
 314    case VHOST_USER_SET_VRING_BASE:
 315        assert(msg.payload.state.index < 2);
 316        s->rings |= 0x1ULL << msg.payload.state.index;
 317        break;
 318
 319    default:
 320        break;
 321    }
 322
 323    g_mutex_unlock(&s->data_mutex);
 324}
 325
 326static const char *init_hugepagefs(const char *path)
 327{
 328    struct statfs fs;
 329    int ret;
 330
 331    if (access(path, R_OK | W_OK | X_OK)) {
 332        g_test_message("access on path (%s): %s\n", path, strerror(errno));
 333        return NULL;
 334    }
 335
 336    do {
 337        ret = statfs(path, &fs);
 338    } while (ret != 0 && errno == EINTR);
 339
 340    if (ret != 0) {
 341        g_test_message("statfs on path (%s): %s\n", path, strerror(errno));
 342        return NULL;
 343    }
 344
 345    if (fs.f_type != HUGETLBFS_MAGIC) {
 346        g_test_message("Warning: path not on HugeTLBFS: %s\n", path);
 347        return NULL;
 348    }
 349
 350    return path;
 351}
 352
 353static TestServer *test_server_new(const gchar *name)
 354{
 355    TestServer *server = g_new0(TestServer, 1);
 356
 357    server->socket_path = g_strdup_printf("%s/%s.sock", tmpfs, name);
 358    server->mig_path = g_strdup_printf("%s/%s.mig", tmpfs, name);
 359    server->chr_name = g_strdup_printf("chr-%s", name);
 360
 361    g_mutex_init(&server->data_mutex);
 362    g_cond_init(&server->data_cond);
 363
 364    server->log_fd = -1;
 365
 366    return server;
 367}
 368
 369static void test_server_create_chr(TestServer *server, const gchar *opt)
 370{
 371    gchar *chr_path;
 372
 373    chr_path = g_strdup_printf("unix:%s%s", server->socket_path, opt);
 374    server->chr = qemu_chr_new(server->chr_name, chr_path, NULL);
 375    g_free(chr_path);
 376
 377    qemu_chr_add_handlers(server->chr, chr_can_read, chr_read, NULL, server);
 378}
 379
 380static void test_server_listen(TestServer *server)
 381{
 382    test_server_create_chr(server, ",server,nowait");
 383}
 384
 385static inline void test_server_connect(TestServer *server)
 386{
 387    test_server_create_chr(server, ",reconnect=1");
 388}
 389
 390#define GET_QEMU_CMD(s)                                         \
 391    g_strdup_printf(QEMU_CMD, 512, 512, (root), (s)->chr_name,  \
 392                    (s)->socket_path, "", (s)->chr_name)
 393
 394#define GET_QEMU_CMDE(s, mem, chr_opts, extra, ...)                     \
 395    g_strdup_printf(QEMU_CMD extra, (mem), (mem), (root), (s)->chr_name, \
 396                    (s)->socket_path, (chr_opts), (s)->chr_name, ##__VA_ARGS__)
 397
 398static gboolean _test_server_free(TestServer *server)
 399{
 400    int i;
 401
 402    qemu_chr_delete(server->chr);
 403
 404    for (i = 0; i < server->fds_num; i++) {
 405        close(server->fds[i]);
 406    }
 407
 408    if (server->log_fd != -1) {
 409        close(server->log_fd);
 410    }
 411
 412    unlink(server->socket_path);
 413    g_free(server->socket_path);
 414
 415    unlink(server->mig_path);
 416    g_free(server->mig_path);
 417
 418    g_free(server->chr_name);
 419    g_free(server);
 420
 421    return FALSE;
 422}
 423
 424static void test_server_free(TestServer *server)
 425{
 426    g_idle_add((GSourceFunc)_test_server_free, server);
 427}
 428
 429static void wait_for_log_fd(TestServer *s)
 430{
 431    gint64 end_time;
 432
 433    g_mutex_lock(&s->data_mutex);
 434    end_time = g_get_monotonic_time() + 5 * G_TIME_SPAN_SECOND;
 435    while (s->log_fd == -1) {
 436        if (!g_cond_wait_until(&s->data_cond, &s->data_mutex, end_time)) {
 437            /* timeout has passed */
 438            g_assert(s->log_fd != -1);
 439            break;
 440        }
 441    }
 442
 443    g_mutex_unlock(&s->data_mutex);
 444}
 445
 446static void write_guest_mem(TestServer *s, uint32_t seed)
 447{
 448    uint32_t *guest_mem;
 449    int i, j;
 450    size_t size;
 451
 452    wait_for_fds(s);
 453
 454    /* iterate all regions */
 455    for (i = 0; i < s->fds_num; i++) {
 456
 457        /* We'll write only the region statring at 0x0 */
 458        if (s->memory.regions[i].guest_phys_addr != 0x0) {
 459            continue;
 460        }
 461
 462        g_assert_cmpint(s->memory.regions[i].memory_size, >, 1024);
 463
 464        size = s->memory.regions[i].memory_size +
 465            s->memory.regions[i].mmap_offset;
 466
 467        guest_mem = mmap(0, size, PROT_READ | PROT_WRITE,
 468                         MAP_SHARED, s->fds[i], 0);
 469
 470        g_assert(guest_mem != MAP_FAILED);
 471        guest_mem += (s->memory.regions[i].mmap_offset / sizeof(*guest_mem));
 472
 473        for (j = 0; j < 256; j++) {
 474            guest_mem[j] = seed + j;
 475        }
 476
 477        munmap(guest_mem, s->memory.regions[i].memory_size);
 478        break;
 479    }
 480}
 481
 482static guint64 get_log_size(TestServer *s)
 483{
 484    guint64 log_size = 0;
 485    int i;
 486
 487    for (i = 0; i < s->memory.nregions; ++i) {
 488        VhostUserMemoryRegion *reg = &s->memory.regions[i];
 489        guint64 last = range_get_last(reg->guest_phys_addr,
 490                                       reg->memory_size);
 491        log_size = MAX(log_size, last / (8 * VHOST_LOG_PAGE) + 1);
 492    }
 493
 494    return log_size;
 495}
 496
 497typedef struct TestMigrateSource {
 498    GSource source;
 499    TestServer *src;
 500    TestServer *dest;
 501} TestMigrateSource;
 502
 503static gboolean
 504test_migrate_source_check(GSource *source)
 505{
 506    TestMigrateSource *t = (TestMigrateSource *)source;
 507    gboolean overlap = t->src->rings && t->dest->rings;
 508
 509    g_assert(!overlap);
 510
 511    return FALSE;
 512}
 513
 514#if !GLIB_CHECK_VERSION(2,36,0)
 515/* this callback is unnecessary with glib >2.36, the default
 516 * prepare for the source does the same */
 517static gboolean
 518test_migrate_source_prepare(GSource *source, gint *timeout)
 519{
 520    *timeout = -1;
 521    return FALSE;
 522}
 523#endif
 524
 525GSourceFuncs test_migrate_source_funcs = {
 526#if !GLIB_CHECK_VERSION(2,36,0)
 527    .prepare = test_migrate_source_prepare,
 528#endif
 529    .check = test_migrate_source_check,
 530};
 531
 532static void test_migrate(void)
 533{
 534    TestServer *s = test_server_new("src");
 535    TestServer *dest = test_server_new("dest");
 536    char *uri = g_strdup_printf("%s%s", "unix:", dest->mig_path);
 537    QTestState *global = global_qtest, *from, *to;
 538    GSource *source;
 539    gchar *cmd;
 540    QDict *rsp;
 541    guint8 *log;
 542    guint64 size;
 543
 544    test_server_listen(s);
 545    test_server_listen(dest);
 546
 547    cmd = GET_QEMU_CMDE(s, 2, "", "");
 548    from = qtest_start(cmd);
 549    g_free(cmd);
 550
 551    wait_for_fds(s);
 552    size = get_log_size(s);
 553    g_assert_cmpint(size, ==, (2 * 1024 * 1024) / (VHOST_LOG_PAGE * 8));
 554
 555    cmd = GET_QEMU_CMDE(dest, 2, "", " -incoming %s", uri);
 556    to = qtest_init(cmd);
 557    g_free(cmd);
 558
 559    source = g_source_new(&test_migrate_source_funcs,
 560                          sizeof(TestMigrateSource));
 561    ((TestMigrateSource *)source)->src = s;
 562    ((TestMigrateSource *)source)->dest = dest;
 563    g_source_attach(source, NULL);
 564
 565    /* slow down migration to have time to fiddle with log */
 566    /* TODO: qtest could learn to break on some places */
 567    rsp = qmp("{ 'execute': 'migrate_set_speed',"
 568              "'arguments': { 'value': 10 } }");
 569    g_assert(qdict_haskey(rsp, "return"));
 570    QDECREF(rsp);
 571
 572    cmd = g_strdup_printf("{ 'execute': 'migrate',"
 573                          "'arguments': { 'uri': '%s' } }",
 574                          uri);
 575    rsp = qmp(cmd);
 576    g_free(cmd);
 577    g_assert(qdict_haskey(rsp, "return"));
 578    QDECREF(rsp);
 579
 580    wait_for_log_fd(s);
 581
 582    log = mmap(0, size, PROT_READ | PROT_WRITE, MAP_SHARED, s->log_fd, 0);
 583    g_assert(log != MAP_FAILED);
 584
 585    /* modify first page */
 586    write_guest_mem(s, 0x42);
 587    log[0] = 1;
 588    munmap(log, size);
 589
 590    /* speed things up */
 591    rsp = qmp("{ 'execute': 'migrate_set_speed',"
 592              "'arguments': { 'value': 0 } }");
 593    g_assert(qdict_haskey(rsp, "return"));
 594    QDECREF(rsp);
 595
 596    qmp_eventwait("STOP");
 597
 598    global_qtest = to;
 599    qmp_eventwait("RESUME");
 600
 601    read_guest_mem(dest);
 602
 603    g_source_destroy(source);
 604    g_source_unref(source);
 605
 606    qtest_quit(to);
 607    test_server_free(dest);
 608    qtest_quit(from);
 609    test_server_free(s);
 610    g_free(uri);
 611
 612    global_qtest = global;
 613}
 614
 615#ifdef CONFIG_HAS_GLIB_SUBPROCESS_TESTS
 616static void wait_for_rings_started(TestServer *s, size_t count)
 617{
 618    gint64 end_time;
 619
 620    g_mutex_lock(&s->data_mutex);
 621    end_time = g_get_monotonic_time() + 5 * G_TIME_SPAN_SECOND;
 622    while (ctpop64(s->rings) != count) {
 623        if (!g_cond_wait_until(&s->data_cond, &s->data_mutex, end_time)) {
 624            /* timeout has passed */
 625            g_assert_cmpint(ctpop64(s->rings), ==, count);
 626            break;
 627        }
 628    }
 629
 630    g_mutex_unlock(&s->data_mutex);
 631}
 632
 633static gboolean
 634reconnect_cb(gpointer user_data)
 635{
 636    TestServer *s = user_data;
 637
 638    qemu_chr_disconnect(s->chr);
 639
 640    return FALSE;
 641}
 642
 643static gpointer
 644connect_thread(gpointer data)
 645{
 646    TestServer *s = data;
 647
 648    /* wait for qemu to start before first try, to avoid extra warnings */
 649    g_usleep(G_USEC_PER_SEC);
 650    test_server_connect(s);
 651
 652    return NULL;
 653}
 654
 655static void test_reconnect_subprocess(void)
 656{
 657    TestServer *s = test_server_new("reconnect");
 658    char *cmd;
 659
 660    g_thread_new("connect", connect_thread, s);
 661    cmd = GET_QEMU_CMDE(s, 2, ",server", "");
 662    qtest_start(cmd);
 663    g_free(cmd);
 664
 665    wait_for_fds(s);
 666    wait_for_rings_started(s, 2);
 667
 668    /* reconnect */
 669    s->fds_num = 0;
 670    s->rings = 0;
 671    g_idle_add(reconnect_cb, s);
 672    wait_for_fds(s);
 673    wait_for_rings_started(s, 2);
 674
 675    qtest_end();
 676    test_server_free(s);
 677    return;
 678}
 679
 680static void test_reconnect(void)
 681{
 682    gchar *path = g_strdup_printf("/%s/vhost-user/reconnect/subprocess",
 683                                  qtest_get_arch());
 684    g_test_trap_subprocess(path, 0, 0);
 685    g_test_trap_assert_passed();
 686    g_free(path);
 687}
 688#endif
 689
 690int main(int argc, char **argv)
 691{
 692    QTestState *s = NULL;
 693    TestServer *server = NULL;
 694    const char *hugefs;
 695    char *qemu_cmd = NULL;
 696    int ret;
 697    char template[] = "/tmp/vhost-test-XXXXXX";
 698    GMainLoop *loop;
 699    GThread *thread;
 700
 701    g_test_init(&argc, &argv, NULL);
 702
 703    module_call_init(MODULE_INIT_QOM);
 704    qemu_add_opts(&qemu_chardev_opts);
 705
 706    tmpfs = mkdtemp(template);
 707    if (!tmpfs) {
 708        g_test_message("mkdtemp on path (%s): %s\n", template, strerror(errno));
 709    }
 710    g_assert(tmpfs);
 711
 712    hugefs = getenv("QTEST_HUGETLBFS_PATH");
 713    if (hugefs) {
 714        root = init_hugepagefs(hugefs);
 715        g_assert(root);
 716    } else {
 717        root = tmpfs;
 718    }
 719
 720    server = test_server_new("test");
 721    test_server_listen(server);
 722
 723    loop = g_main_loop_new(NULL, FALSE);
 724    /* run the main loop thread so the chardev may operate */
 725    thread = g_thread_new(NULL, thread_function, loop);
 726
 727    qemu_cmd = GET_QEMU_CMD(server);
 728
 729    s = qtest_start(qemu_cmd);
 730    g_free(qemu_cmd);
 731
 732    qtest_add_data_func("/vhost-user/read-guest-mem", server, read_guest_mem);
 733    qtest_add_func("/vhost-user/migrate", test_migrate);
 734#ifdef CONFIG_HAS_GLIB_SUBPROCESS_TESTS
 735    qtest_add_func("/vhost-user/reconnect/subprocess",
 736                   test_reconnect_subprocess);
 737    qtest_add_func("/vhost-user/reconnect", test_reconnect);
 738#endif
 739
 740    ret = g_test_run();
 741
 742    if (s) {
 743        qtest_quit(s);
 744    }
 745
 746    /* cleanup */
 747    test_server_free(server);
 748
 749    /* finish the helper thread and dispatch pending sources */
 750    g_main_loop_quit(loop);
 751    g_thread_join(thread);
 752    while (g_main_context_pending(NULL)) {
 753        g_main_context_iteration (NULL, TRUE);
 754    }
 755    g_main_loop_unref(loop);
 756
 757    ret = rmdir(tmpfs);
 758    if (ret != 0) {
 759        g_test_message("unable to rmdir: path (%s): %s\n",
 760                       tmpfs, strerror(errno));
 761    }
 762    g_assert_cmpint(ret, ==, 0);
 763
 764    return ret;
 765}
 766