vhost-user-test.c 17.3 KB
Newer Older
N
Nikolay Nikolaev 已提交
1 2 3 4 5 6 7 8 9 10
/*
 * QTest testcase for the vhost-user
 *
 * Copyright (c) 2014 Virtual Open Systems Sarl.
 *
 * This work is licensed under the terms of the GNU GPL, version 2 or later.
 * See the COPYING file in the top-level directory.
 *
 */

11 12
#include <glib.h>

N
Nikolay Nikolaev 已提交
13 14
#include "libqtest.h"
#include "qemu/option.h"
15
#include "qemu/range.h"
N
Nikolay Nikolaev 已提交
16 17 18 19 20 21 22 23
#include "sysemu/char.h"
#include "sysemu/sysemu.h"

#include <linux/vhost.h>
#include <sys/mman.h>
#include <sys/vfs.h>
#include <qemu/sockets.h>

24 25 26 27 28 29 30 31 32
/* GLIB version compatibility flags */
#if !GLIB_CHECK_VERSION(2, 26, 0)
#define G_TIME_SPAN_SECOND              (G_GINT64_CONSTANT(1000000))
#endif

#if GLIB_CHECK_VERSION(2, 28, 0)
#define HAVE_MONOTONIC_TIME
#endif

N
Nikolay Nikolaev 已提交
33
#define QEMU_CMD_ACCEL  " -machine accel=tcg"
34
#define QEMU_CMD_MEM    " -m %d -object memory-backend-file,id=mem,size=%dM,"\
N
Nikolay Nikolaev 已提交
35
                        "mem-path=%s,share=on -numa node,memdev=mem"
36 37
#define QEMU_CMD_CHR    " -chardev socket,id=%s,path=%s"
#define QEMU_CMD_NETDEV " -netdev vhost-user,id=net0,chardev=%s,vhostforce"
N
Nikolay Nikolaev 已提交
38 39 40 41 42 43 44 45 46 47 48 49
#define QEMU_CMD_NET    " -device virtio-net-pci,netdev=net0 "
#define QEMU_CMD_ROM    " -option-rom ../pc-bios/pxe-virtio.rom"

#define QEMU_CMD        QEMU_CMD_ACCEL QEMU_CMD_MEM QEMU_CMD_CHR \
                        QEMU_CMD_NETDEV QEMU_CMD_NET QEMU_CMD_ROM

#define HUGETLBFS_MAGIC       0x958458f6

/*********** FROM hw/virtio/vhost-user.c *************************************/

#define VHOST_MEMORY_MAX_NREGIONS    8

50
#define VHOST_USER_F_PROTOCOL_FEATURES 30
51 52 53
#define VHOST_USER_PROTOCOL_F_LOG_SHMFD 1

#define VHOST_LOG_PAGE 0x1000
54

N
Nikolay Nikolaev 已提交
55 56 57 58 59
typedef enum VhostUserRequest {
    VHOST_USER_NONE = 0,
    VHOST_USER_GET_FEATURES = 1,
    VHOST_USER_SET_FEATURES = 2,
    VHOST_USER_SET_OWNER = 3,
60
    VHOST_USER_RESET_OWNER = 4,
N
Nikolay Nikolaev 已提交
61 62 63 64 65 66 67 68 69 70
    VHOST_USER_SET_MEM_TABLE = 5,
    VHOST_USER_SET_LOG_BASE = 6,
    VHOST_USER_SET_LOG_FD = 7,
    VHOST_USER_SET_VRING_NUM = 8,
    VHOST_USER_SET_VRING_ADDR = 9,
    VHOST_USER_SET_VRING_BASE = 10,
    VHOST_USER_GET_VRING_BASE = 11,
    VHOST_USER_SET_VRING_KICK = 12,
    VHOST_USER_SET_VRING_CALL = 13,
    VHOST_USER_SET_VRING_ERR = 14,
71 72
    VHOST_USER_GET_PROTOCOL_FEATURES = 15,
    VHOST_USER_SET_PROTOCOL_FEATURES = 16,
73
    VHOST_USER_SET_VRING_ENABLE = 18,
N
Nikolay Nikolaev 已提交
74 75 76 77 78 79 80
    VHOST_USER_MAX
} VhostUserRequest;

typedef struct VhostUserMemoryRegion {
    uint64_t guest_phys_addr;
    uint64_t memory_size;
    uint64_t userspace_addr;
81
    uint64_t mmap_offset;
N
Nikolay Nikolaev 已提交
82 83 84 85 86 87 88 89
} VhostUserMemoryRegion;

typedef struct VhostUserMemory {
    uint32_t nregions;
    uint32_t padding;
    VhostUserMemoryRegion regions[VHOST_MEMORY_MAX_NREGIONS];
} VhostUserMemory;

90 91 92 93 94
typedef struct VhostUserLog {
    uint64_t mmap_size;
    uint64_t mmap_offset;
} VhostUserLog;

N
Nikolay Nikolaev 已提交
95 96 97 98 99 100 101 102
typedef struct VhostUserMsg {
    VhostUserRequest request;

#define VHOST_USER_VERSION_MASK     (0x3)
#define VHOST_USER_REPLY_MASK       (0x1<<2)
    uint32_t flags;
    uint32_t size; /* the following payload size */
    union {
103 104
#define VHOST_USER_VRING_IDX_MASK   (0xff)
#define VHOST_USER_VRING_NOFD_MASK  (0x1<<8)
N
Nikolay Nikolaev 已提交
105 106 107 108
        uint64_t u64;
        struct vhost_vring_state state;
        struct vhost_vring_addr addr;
        VhostUserMemory memory;
109
        VhostUserLog log;
110
    } payload;
N
Nikolay Nikolaev 已提交
111 112 113 114 115 116 117 118 119 120 121 122 123
} QEMU_PACKED VhostUserMsg;

static VhostUserMsg m __attribute__ ((unused));
#define VHOST_USER_HDR_SIZE (sizeof(m.request) \
                            + sizeof(m.flags) \
                            + sizeof(m.size))

#define VHOST_USER_PAYLOAD_SIZE (sizeof(m) - VHOST_USER_HDR_SIZE)

/* The version of the protocol we support */
#define VHOST_USER_VERSION    (0x1)
/*****************************************************************************/

124 125
typedef struct TestServer {
    gchar *socket_path;
126
    gchar *mig_path;
127 128 129 130 131 132 133
    gchar *chr_name;
    CharDriverState *chr;
    int fds_num;
    int fds[VHOST_MEMORY_MAX_NREGIONS];
    VhostUserMemory memory;
    GMutex data_mutex;
    GCond data_cond;
134
    int log_fd;
135
    uint64_t rings;
136
} TestServer;
137

138 139 140
#if !GLIB_CHECK_VERSION(2, 32, 0)
static gboolean g_cond_wait_until(CompatGCond cond, CompatGMutex mutex,
                                  gint64 end_time)
141 142
{
    gboolean ret = FALSE;
143
    end_time -= g_get_monotonic_time();
144 145 146 147 148 149
    GTimeVal time = { end_time / G_TIME_SPAN_SECOND,
                      end_time % G_TIME_SPAN_SECOND };
    ret = g_cond_timed_wait(cond, mutex, &time);
    return ret;
}
#endif
N
Nikolay Nikolaev 已提交
150

151 152 153
static const char *tmpfs;
static const char *root;

154
static void wait_for_fds(TestServer *s)
N
Nikolay Nikolaev 已提交
155 156 157
{
    gint64 end_time;

158
    g_mutex_lock(&s->data_mutex);
N
Nikolay Nikolaev 已提交
159

160
    end_time = g_get_monotonic_time() + 5 * G_TIME_SPAN_SECOND;
161 162
    while (!s->fds_num) {
        if (!g_cond_wait_until(&s->data_cond, &s->data_mutex, end_time)) {
N
Nikolay Nikolaev 已提交
163
            /* timeout has passed */
164
            g_assert(s->fds_num);
N
Nikolay Nikolaev 已提交
165 166 167 168 169
            break;
        }
    }

    /* check for sanity */
170 171
    g_assert_cmpint(s->fds_num, >, 0);
    g_assert_cmpint(s->fds_num, ==, s->memory.nregions);
N
Nikolay Nikolaev 已提交
172

173
    g_mutex_unlock(&s->data_mutex);
174 175
}

176
static void read_guest_mem(TestServer *s)
177 178 179 180 181
{
    uint32_t *guest_mem;
    int i, j;
    size_t size;

182
    wait_for_fds(s);
183

184
    g_mutex_lock(&s->data_mutex);
185

N
Nikolay Nikolaev 已提交
186
    /* iterate all regions */
187
    for (i = 0; i < s->fds_num; i++) {
N
Nikolay Nikolaev 已提交
188 189

        /* We'll check only the region statring at 0x0*/
190
        if (s->memory.regions[i].guest_phys_addr != 0x0) {
N
Nikolay Nikolaev 已提交
191 192 193
            continue;
        }

194
        g_assert_cmpint(s->memory.regions[i].memory_size, >, 1024);
N
Nikolay Nikolaev 已提交
195

196 197
        size = s->memory.regions[i].memory_size +
            s->memory.regions[i].mmap_offset;
198 199

        guest_mem = mmap(0, size, PROT_READ | PROT_WRITE,
200
                         MAP_SHARED, s->fds[i], 0);
201 202

        g_assert(guest_mem != MAP_FAILED);
203
        guest_mem += (s->memory.regions[i].mmap_offset / sizeof(*guest_mem));
N
Nikolay Nikolaev 已提交
204 205

        for (j = 0; j < 256; j++) {
206
            uint32_t a = readl(s->memory.regions[i].guest_phys_addr + j*4);
N
Nikolay Nikolaev 已提交
207 208 209 210 211
            uint32_t b = guest_mem[j];

            g_assert_cmpint(a, ==, b);
        }

212
        munmap(guest_mem, s->memory.regions[i].memory_size);
N
Nikolay Nikolaev 已提交
213 214
    }

215
    g_mutex_unlock(&s->data_mutex);
N
Nikolay Nikolaev 已提交
216 217 218 219
}

static void *thread_function(void *data)
{
220
    GMainLoop *loop = data;
N
Nikolay Nikolaev 已提交
221 222 223 224 225 226 227 228 229 230 231
    g_main_loop_run(loop);
    return NULL;
}

static int chr_can_read(void *opaque)
{
    return VHOST_USER_HDR_SIZE;
}

static void chr_read(void *opaque, const uint8_t *buf, int size)
{
232 233
    TestServer *s = opaque;
    CharDriverState *chr = s->chr;
N
Nikolay Nikolaev 已提交
234 235 236 237 238 239 240 241 242
    VhostUserMsg msg;
    uint8_t *p = (uint8_t *) &msg;
    int fd;

    if (size != VHOST_USER_HDR_SIZE) {
        g_test_message("Wrong message size received %d\n", size);
        return;
    }

243
    g_mutex_lock(&s->data_mutex);
N
Nikolay Nikolaev 已提交
244 245 246 247
    memcpy(p, buf, VHOST_USER_HDR_SIZE);

    if (msg.size) {
        p += VHOST_USER_HDR_SIZE;
248
        g_assert_cmpint(qemu_chr_fe_read_all(chr, p, msg.size), ==, msg.size);
N
Nikolay Nikolaev 已提交
249 250 251 252
    }

    switch (msg.request) {
    case VHOST_USER_GET_FEATURES:
253 254
        /* send back features to qemu */
        msg.flags |= VHOST_USER_REPLY_MASK;
255 256
        msg.size = sizeof(m.payload.u64);
        msg.payload.u64 = 0x1ULL << VHOST_F_LOG_ALL |
257
            0x1ULL << VHOST_USER_F_PROTOCOL_FEATURES;
258 259 260 261 262
        p = (uint8_t *) &msg;
        qemu_chr_fe_write_all(chr, p, VHOST_USER_HDR_SIZE + msg.size);
        break;

    case VHOST_USER_SET_FEATURES:
263
	g_assert_cmpint(msg.payload.u64 & (0x1ULL << VHOST_USER_F_PROTOCOL_FEATURES),
264 265 266 267
			!=, 0ULL);
        break;

    case VHOST_USER_GET_PROTOCOL_FEATURES:
N
Nikolay Nikolaev 已提交
268 269
        /* send back features to qemu */
        msg.flags |= VHOST_USER_REPLY_MASK;
270 271
        msg.size = sizeof(m.payload.u64);
        msg.payload.u64 = 1 << VHOST_USER_PROTOCOL_F_LOG_SHMFD;
N
Nikolay Nikolaev 已提交
272 273 274 275 276 277 278
        p = (uint8_t *) &msg;
        qemu_chr_fe_write_all(chr, p, VHOST_USER_HDR_SIZE + msg.size);
        break;

    case VHOST_USER_GET_VRING_BASE:
        /* send back vring base to qemu */
        msg.flags |= VHOST_USER_REPLY_MASK;
279 280
        msg.size = sizeof(m.payload.state);
        msg.payload.state.num = 0;
N
Nikolay Nikolaev 已提交
281 282
        p = (uint8_t *) &msg;
        qemu_chr_fe_write_all(chr, p, VHOST_USER_HDR_SIZE + msg.size);
283 284 285

        assert(msg.payload.state.index < 2);
        s->rings &= ~(0x1ULL << msg.payload.state.index);
N
Nikolay Nikolaev 已提交
286 287 288 289
        break;

    case VHOST_USER_SET_MEM_TABLE:
        /* received the mem table */
290
        memcpy(&s->memory, &msg.payload.memory, sizeof(msg.payload.memory));
291
        s->fds_num = qemu_chr_fe_get_msgfds(chr, s->fds, G_N_ELEMENTS(s->fds));
N
Nikolay Nikolaev 已提交
292 293

        /* signal the test that it can continue */
294
        g_cond_signal(&s->data_cond);
N
Nikolay Nikolaev 已提交
295 296 297 298 299 300 301 302 303 304 305 306 307
        break;

    case VHOST_USER_SET_VRING_KICK:
    case VHOST_USER_SET_VRING_CALL:
        /* consume the fd */
        qemu_chr_fe_get_msgfds(chr, &fd, 1);
        /*
         * This is a non-blocking eventfd.
         * The receive function forces it to be blocking,
         * so revert it back to non-blocking.
         */
        qemu_set_nonblock(fd);
        break;
308 309 310 311 312 313 314 315 316 317 318 319 320 321 322

    case VHOST_USER_SET_LOG_BASE:
        if (s->log_fd != -1) {
            close(s->log_fd);
            s->log_fd = -1;
        }
        qemu_chr_fe_get_msgfds(chr, &s->log_fd, 1);
        msg.flags |= VHOST_USER_REPLY_MASK;
        msg.size = 0;
        p = (uint8_t *) &msg;
        qemu_chr_fe_write_all(chr, p, VHOST_USER_HDR_SIZE);

        g_cond_signal(&s->data_cond);
        break;

323 324 325
    case VHOST_USER_SET_VRING_BASE:
        assert(msg.payload.state.index < 2);
        s->rings |= 0x1ULL << msg.payload.state.index;
326 327
        break;

N
Nikolay Nikolaev 已提交
328 329 330
    default:
        break;
    }
331 332

    g_mutex_unlock(&s->data_mutex);
N
Nikolay Nikolaev 已提交
333 334
}

335
static const char *init_hugepagefs(const char *path)
N
Nikolay Nikolaev 已提交
336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 361
{
    struct statfs fs;
    int ret;

    if (access(path, R_OK | W_OK | X_OK)) {
        g_test_message("access on path (%s): %s\n", path, strerror(errno));
        return NULL;
    }

    do {
        ret = statfs(path, &fs);
    } while (ret != 0 && errno == EINTR);

    if (ret != 0) {
        g_test_message("statfs on path (%s): %s\n", path, strerror(errno));
        return NULL;
    }

    if (fs.f_type != HUGETLBFS_MAGIC) {
        g_test_message("Warning: path not on HugeTLBFS: %s\n", path);
        return NULL;
    }

    return path;
}

362
static TestServer *test_server_new(const gchar *name)
363 364 365 366 367
{
    TestServer *server = g_new0(TestServer, 1);
    gchar *chr_path;

    server->socket_path = g_strdup_printf("%s/%s.sock", tmpfs, name);
368
    server->mig_path = g_strdup_printf("%s/%s.mig", tmpfs, name);
369 370 371 372 373 374 375 376 377 378 379

    chr_path = g_strdup_printf("unix:%s,server,nowait", server->socket_path);
    server->chr_name = g_strdup_printf("chr-%s", name);
    server->chr = qemu_chr_new(server->chr_name, chr_path, NULL);
    g_free(chr_path);

    qemu_chr_add_handlers(server->chr, chr_can_read, chr_read, NULL, server);

    g_mutex_init(&server->data_mutex);
    g_cond_init(&server->data_cond);

380 381
    server->log_fd = -1;

382 383 384
    return server;
}

385 386 387
#define GET_QEMU_CMD(s)                                                        \
    g_strdup_printf(QEMU_CMD, 512, 512, (root), (s)->chr_name,                 \
                    (s)->socket_path, (s)->chr_name)
388

389 390 391
#define GET_QEMU_CMDE(s, mem, extra, ...)                                      \
    g_strdup_printf(QEMU_CMD extra, (mem), (mem), (root), (s)->chr_name,       \
                    (s)->socket_path, (s)->chr_name, ##__VA_ARGS__)
392

393
static gboolean _test_server_free(TestServer *server)
394 395 396 397 398 399 400 401 402
{
    int i;

    qemu_chr_delete(server->chr);

    for (i = 0; i < server->fds_num; i++) {
        close(server->fds[i]);
    }

403 404 405 406
    if (server->log_fd != -1) {
        close(server->log_fd);
    }

407 408 409
    unlink(server->socket_path);
    g_free(server->socket_path);

410 411 412
    unlink(server->mig_path);
    g_free(server->mig_path);

413
    g_free(server->chr_name);
414
    g_free(server);
415 416 417 418 419 420 421

    return FALSE;
}

static void test_server_free(TestServer *server)
{
    g_idle_add((GSourceFunc)_test_server_free, server);
422 423
}

424 425 426 427 428 429 430 431 432 433 434 435 436 437 438 439 440 441 442 443 444 445 446 447 448 449 450 451 452 453 454 455 456 457 458 459 460 461 462 463 464 465 466 467 468 469 470 471 472 473 474 475 476 477 478 479 480 481 482 483 484 485 486 487 488 489 490 491
static void wait_for_log_fd(TestServer *s)
{
    gint64 end_time;

    g_mutex_lock(&s->data_mutex);
    end_time = g_get_monotonic_time() + 5 * G_TIME_SPAN_SECOND;
    while (s->log_fd == -1) {
        if (!g_cond_wait_until(&s->data_cond, &s->data_mutex, end_time)) {
            /* timeout has passed */
            g_assert(s->log_fd != -1);
            break;
        }
    }

    g_mutex_unlock(&s->data_mutex);
}

static void write_guest_mem(TestServer *s, uint32 seed)
{
    uint32_t *guest_mem;
    int i, j;
    size_t size;

    wait_for_fds(s);

    /* iterate all regions */
    for (i = 0; i < s->fds_num; i++) {

        /* We'll write only the region statring at 0x0 */
        if (s->memory.regions[i].guest_phys_addr != 0x0) {
            continue;
        }

        g_assert_cmpint(s->memory.regions[i].memory_size, >, 1024);

        size = s->memory.regions[i].memory_size +
            s->memory.regions[i].mmap_offset;

        guest_mem = mmap(0, size, PROT_READ | PROT_WRITE,
                         MAP_SHARED, s->fds[i], 0);

        g_assert(guest_mem != MAP_FAILED);
        guest_mem += (s->memory.regions[i].mmap_offset / sizeof(*guest_mem));

        for (j = 0; j < 256; j++) {
            guest_mem[j] = seed + j;
        }

        munmap(guest_mem, s->memory.regions[i].memory_size);
        break;
    }
}

static guint64 get_log_size(TestServer *s)
{
    guint64 log_size = 0;
    int i;

    for (i = 0; i < s->memory.nregions; ++i) {
        VhostUserMemoryRegion *reg = &s->memory.regions[i];
        guint64 last = range_get_last(reg->guest_phys_addr,
                                       reg->memory_size);
        log_size = MAX(log_size, last / (8 * VHOST_LOG_PAGE) + 1);
    }

    return log_size;
}

492 493 494 495 496 497 498 499 500 501
typedef struct TestMigrateSource {
    GSource source;
    TestServer *src;
    TestServer *dest;
} TestMigrateSource;

static gboolean
test_migrate_source_check(GSource *source)
{
    TestMigrateSource *t = (TestMigrateSource *)source;
502
    gboolean overlap = t->src->rings && t->dest->rings;
503 504 505 506 507 508 509 510 511 512 513 514 515

    g_assert(!overlap);

    return FALSE;
}

GSourceFuncs test_migrate_source_funcs = {
    NULL,
    test_migrate_source_check,
    NULL,
    NULL
};

516 517 518 519
static void test_migrate(void)
{
    TestServer *s = test_server_new("src");
    TestServer *dest = test_server_new("dest");
520
    char *uri = g_strdup_printf("%s%s", "unix:", dest->mig_path);
521
    QTestState *global = global_qtest, *from, *to;
522
    GSource *source;
523 524 525 526 527 528 529 530 531 532 533 534 535 536 537 538 539
    gchar *cmd;
    QDict *rsp;
    guint8 *log;
    guint64 size;

    cmd = GET_QEMU_CMDE(s, 2, "");
    from = qtest_start(cmd);
    g_free(cmd);

    wait_for_fds(s);
    size = get_log_size(s);
    g_assert_cmpint(size, ==, (2 * 1024 * 1024) / (VHOST_LOG_PAGE * 8));

    cmd = GET_QEMU_CMDE(dest, 2, " -incoming %s", uri);
    to = qtest_init(cmd);
    g_free(cmd);

540 541 542 543 544 545
    source = g_source_new(&test_migrate_source_funcs,
                          sizeof(TestMigrateSource));
    ((TestMigrateSource *)source)->src = s;
    ((TestMigrateSource *)source)->dest = dest;
    g_source_attach(source, NULL);

546 547 548 549 550 551 552 553 554 555 556 557 558 559 560 561 562 563 564 565 566 567 568 569 570 571 572 573 574 575 576 577 578 579 580 581 582 583
    /* slow down migration to have time to fiddle with log */
    /* TODO: qtest could learn to break on some places */
    rsp = qmp("{ 'execute': 'migrate_set_speed',"
              "'arguments': { 'value': 10 } }");
    g_assert(qdict_haskey(rsp, "return"));
    QDECREF(rsp);

    cmd = g_strdup_printf("{ 'execute': 'migrate',"
                          "'arguments': { 'uri': '%s' } }",
                          uri);
    rsp = qmp(cmd);
    g_free(cmd);
    g_assert(qdict_haskey(rsp, "return"));
    QDECREF(rsp);

    wait_for_log_fd(s);

    log = mmap(0, size, PROT_READ | PROT_WRITE, MAP_SHARED, s->log_fd, 0);
    g_assert(log != MAP_FAILED);

    /* modify first page */
    write_guest_mem(s, 0x42);
    log[0] = 1;
    munmap(log, size);

    /* speed things up */
    rsp = qmp("{ 'execute': 'migrate_set_speed',"
              "'arguments': { 'value': 0 } }");
    g_assert(qdict_haskey(rsp, "return"));
    QDECREF(rsp);

    qmp_eventwait("STOP");

    global_qtest = to;
    qmp_eventwait("RESUME");

    read_guest_mem(dest);

584 585 586
    g_source_destroy(source);
    g_source_unref(source);

587 588 589 590
    qtest_quit(to);
    test_server_free(dest);
    qtest_quit(from);
    test_server_free(s);
591
    g_free(uri);
592 593 594 595

    global_qtest = global;
}

N
Nikolay Nikolaev 已提交
596 597 598
int main(int argc, char **argv)
{
    QTestState *s = NULL;
599
    TestServer *server = NULL;
600
    const char *hugefs;
601
    char *qemu_cmd = NULL;
N
Nikolay Nikolaev 已提交
602
    int ret;
603
    char template[] = "/tmp/vhost-test-XXXXXX";
604 605
    GMainLoop *loop;
    GThread *thread;
N
Nikolay Nikolaev 已提交
606 607 608 609

    g_test_init(&argc, &argv, NULL);

    module_call_init(MODULE_INIT_QOM);
610
    qemu_add_opts(&qemu_chardev_opts);
N
Nikolay Nikolaev 已提交
611

612 613
    tmpfs = mkdtemp(template);
    if (!tmpfs) {
614
        g_test_message("mkdtemp on path (%s): %s\n", template, strerror(errno));
615 616 617 618 619 620 621 622 623
    }
    g_assert(tmpfs);

    hugefs = getenv("QTEST_HUGETLBFS_PATH");
    if (hugefs) {
        root = init_hugepagefs(hugefs);
        g_assert(root);
    } else {
        root = tmpfs;
N
Nikolay Nikolaev 已提交
624 625
    }

626
    server = test_server_new("test");
N
Nikolay Nikolaev 已提交
627

628
    loop = g_main_loop_new(NULL, FALSE);
N
Nikolay Nikolaev 已提交
629
    /* run the main loop thread so the chardev may operate */
630
    thread = g_thread_new(NULL, thread_function, loop);
N
Nikolay Nikolaev 已提交
631

632
    qemu_cmd = GET_QEMU_CMD(server);
633

N
Nikolay Nikolaev 已提交
634 635 636
    s = qtest_start(qemu_cmd);
    g_free(qemu_cmd);

637
    qtest_add_data_func("/vhost-user/read-guest-mem", server, read_guest_mem);
638
    qtest_add_func("/vhost-user/migrate", test_migrate);
N
Nikolay Nikolaev 已提交
639 640 641 642 643 644 645 646

    ret = g_test_run();

    if (s) {
        qtest_quit(s);
    }

    /* cleanup */
647
    test_server_free(server);
N
Nikolay Nikolaev 已提交
648

649 650 651 652 653 654 655 656
    /* finish the helper thread and dispatch pending sources */
    g_main_loop_quit(loop);
    g_thread_join(thread);
    while (g_main_context_pending(NULL)) {
        g_main_context_iteration (NULL, TRUE);
    }
    g_main_loop_unref(loop);

657 658 659 660 661 662 663
    ret = rmdir(tmpfs);
    if (ret != 0) {
        g_test_message("unable to rmdir: path (%s): %s\n",
                       tmpfs, strerror(errno));
    }
    g_assert_cmpint(ret, ==, 0);

N
Nikolay Nikolaev 已提交
664 665
    return ret;
}