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 "qapi/error.h"
  15#include "qapi/qmp/qdict.h"
  16#include "qemu/config-file.h"
  17#include "qemu/option.h"
  18#include "qemu/range.h"
  19#include "qemu/sockets.h"
  20#include "chardev/char-fe.h"
  21#include "qemu/memfd.h"
  22#include "qemu/module.h"
  23#include "sysemu/sysemu.h"
  24#include "libqos/libqos.h"
  25#include "libqos/pci-pc.h"
  26#include "libqos/virtio-pci.h"
  27
  28#include "libqos/malloc-pc.h"
  29#include "hw/virtio/virtio-net.h"
  30
  31#include "standard-headers/linux/vhost_types.h"
  32#include "standard-headers/linux/virtio_ids.h"
  33#include "standard-headers/linux/virtio_net.h"
  34
  35#ifdef CONFIG_LINUX
  36#include <sys/vfs.h>
  37#endif
  38
  39
  40#define QEMU_CMD_MEM    " -m %d -object memory-backend-file,id=mem,size=%dM," \
  41                        "mem-path=%s,share=on -numa node,memdev=mem"
  42#define QEMU_CMD_MEMFD  " -m %d -object memory-backend-memfd,id=mem,size=%dM," \
  43                        " -numa node,memdev=mem"
  44#define QEMU_CMD_CHR    " -chardev socket,id=%s,path=%s%s"
  45#define QEMU_CMD_NETDEV " -netdev vhost-user,id=hs0,chardev=%s,vhostforce"
  46
  47#define HUGETLBFS_MAGIC       0x958458f6
  48
  49/*********** FROM hw/virtio/vhost-user.c *************************************/
  50
  51#define VHOST_MEMORY_MAX_NREGIONS    8
  52#define VHOST_MAX_VIRTQUEUES    0x100
  53
  54#define VHOST_USER_F_PROTOCOL_FEATURES 30
  55#define VHOST_USER_PROTOCOL_F_MQ 0
  56#define VHOST_USER_PROTOCOL_F_LOG_SHMFD 1
  57#define VHOST_USER_PROTOCOL_F_CROSS_ENDIAN   6
  58
  59#define VHOST_LOG_PAGE 0x1000
  60
  61typedef enum VhostUserRequest {
  62    VHOST_USER_NONE = 0,
  63    VHOST_USER_GET_FEATURES = 1,
  64    VHOST_USER_SET_FEATURES = 2,
  65    VHOST_USER_SET_OWNER = 3,
  66    VHOST_USER_RESET_OWNER = 4,
  67    VHOST_USER_SET_MEM_TABLE = 5,
  68    VHOST_USER_SET_LOG_BASE = 6,
  69    VHOST_USER_SET_LOG_FD = 7,
  70    VHOST_USER_SET_VRING_NUM = 8,
  71    VHOST_USER_SET_VRING_ADDR = 9,
  72    VHOST_USER_SET_VRING_BASE = 10,
  73    VHOST_USER_GET_VRING_BASE = 11,
  74    VHOST_USER_SET_VRING_KICK = 12,
  75    VHOST_USER_SET_VRING_CALL = 13,
  76    VHOST_USER_SET_VRING_ERR = 14,
  77    VHOST_USER_GET_PROTOCOL_FEATURES = 15,
  78    VHOST_USER_SET_PROTOCOL_FEATURES = 16,
  79    VHOST_USER_GET_QUEUE_NUM = 17,
  80    VHOST_USER_SET_VRING_ENABLE = 18,
  81    VHOST_USER_MAX
  82} VhostUserRequest;
  83
  84typedef struct VhostUserMemoryRegion {
  85    uint64_t guest_phys_addr;
  86    uint64_t memory_size;
  87    uint64_t userspace_addr;
  88    uint64_t mmap_offset;
  89} VhostUserMemoryRegion;
  90
  91typedef struct VhostUserMemory {
  92    uint32_t nregions;
  93    uint32_t padding;
  94    VhostUserMemoryRegion regions[VHOST_MEMORY_MAX_NREGIONS];
  95} VhostUserMemory;
  96
  97typedef struct VhostUserLog {
  98    uint64_t mmap_size;
  99    uint64_t mmap_offset;
 100} VhostUserLog;
 101
 102typedef struct VhostUserMsg {
 103    VhostUserRequest request;
 104
 105#define VHOST_USER_VERSION_MASK     (0x3)
 106#define VHOST_USER_REPLY_MASK       (0x1<<2)
 107    uint32_t flags;
 108    uint32_t size; /* the following payload size */
 109    union {
 110#define VHOST_USER_VRING_IDX_MASK   (0xff)
 111#define VHOST_USER_VRING_NOFD_MASK  (0x1<<8)
 112        uint64_t u64;
 113        struct vhost_vring_state state;
 114        struct vhost_vring_addr addr;
 115        VhostUserMemory memory;
 116        VhostUserLog log;
 117    } payload;
 118} QEMU_PACKED VhostUserMsg;
 119
 120static VhostUserMsg m __attribute__ ((unused));
 121#define VHOST_USER_HDR_SIZE (sizeof(m.request) \
 122                            + sizeof(m.flags) \
 123                            + sizeof(m.size))
 124
 125#define VHOST_USER_PAYLOAD_SIZE (sizeof(m) - VHOST_USER_HDR_SIZE)
 126
 127/* The version of the protocol we support */
 128#define VHOST_USER_VERSION    (0x1)
 129/*****************************************************************************/
 130
 131enum {
 132    TEST_FLAGS_OK,
 133    TEST_FLAGS_DISCONNECT,
 134    TEST_FLAGS_BAD,
 135    TEST_FLAGS_END,
 136};
 137
 138typedef struct TestServer {
 139    gchar *socket_path;
 140    gchar *mig_path;
 141    gchar *chr_name;
 142    gchar *tmpfs;
 143    CharBackend chr;
 144    int fds_num;
 145    int fds[VHOST_MEMORY_MAX_NREGIONS];
 146    VhostUserMemory memory;
 147    GMainContext *context;
 148    GMainLoop *loop;
 149    GThread *thread;
 150    GMutex data_mutex;
 151    GCond data_cond;
 152    int log_fd;
 153    uint64_t rings;
 154    bool test_fail;
 155    int test_flags;
 156    int queues;
 157} TestServer;
 158
 159static const char *init_hugepagefs(void);
 160static TestServer *test_server_new(const gchar *name);
 161static void test_server_free(TestServer *server);
 162static void test_server_listen(TestServer *server);
 163
 164enum test_memfd {
 165    TEST_MEMFD_AUTO,
 166    TEST_MEMFD_YES,
 167    TEST_MEMFD_NO,
 168};
 169
 170static void append_vhost_opts(TestServer *s, GString *cmd_line,
 171                             const char *chr_opts)
 172{
 173    g_string_append_printf(cmd_line, QEMU_CMD_CHR QEMU_CMD_NETDEV,
 174                           s->chr_name, s->socket_path,
 175                           chr_opts, s->chr_name);
 176}
 177
 178static void append_mem_opts(TestServer *server, GString *cmd_line,
 179                            int size, enum test_memfd memfd)
 180{
 181    if (memfd == TEST_MEMFD_AUTO) {
 182        memfd = qemu_memfd_check(MFD_ALLOW_SEALING) ? TEST_MEMFD_YES
 183                                                    : TEST_MEMFD_NO;
 184    }
 185
 186    if (memfd == TEST_MEMFD_YES) {
 187        g_string_append_printf(cmd_line, QEMU_CMD_MEMFD, size, size);
 188    } else {
 189        const char *root = init_hugepagefs() ? : server->tmpfs;
 190
 191        g_string_append_printf(cmd_line, QEMU_CMD_MEM, size, size, root);
 192    }
 193}
 194
 195static bool wait_for_fds(TestServer *s)
 196{
 197    gint64 end_time;
 198    bool got_region;
 199    int i;
 200
 201    g_mutex_lock(&s->data_mutex);
 202
 203    end_time = g_get_monotonic_time() + 5 * G_TIME_SPAN_SECOND;
 204    while (!s->fds_num) {
 205        if (!g_cond_wait_until(&s->data_cond, &s->data_mutex, end_time)) {
 206            /* timeout has passed */
 207            g_assert(s->fds_num);
 208            break;
 209        }
 210    }
 211
 212    /* check for sanity */
 213    g_assert_cmpint(s->fds_num, >, 0);
 214    g_assert_cmpint(s->fds_num, ==, s->memory.nregions);
 215
 216    g_mutex_unlock(&s->data_mutex);
 217
 218    got_region = false;
 219    for (i = 0; i < s->memory.nregions; ++i) {
 220        VhostUserMemoryRegion *reg = &s->memory.regions[i];
 221        if (reg->guest_phys_addr == 0) {
 222            got_region = true;
 223            break;
 224        }
 225    }
 226    if (!got_region) {
 227        g_test_skip("No memory at address 0x0");
 228    }
 229    return got_region;
 230}
 231
 232static void read_guest_mem_server(QTestState *qts, TestServer *s)
 233{
 234    uint8_t *guest_mem;
 235    int i, j;
 236    size_t size;
 237
 238    g_mutex_lock(&s->data_mutex);
 239
 240    /* iterate all regions */
 241    for (i = 0; i < s->fds_num; i++) {
 242
 243        /* We'll check only the region statring at 0x0*/
 244        if (s->memory.regions[i].guest_phys_addr != 0x0) {
 245            continue;
 246        }
 247
 248        g_assert_cmpint(s->memory.regions[i].memory_size, >, 1024);
 249
 250        size = s->memory.regions[i].memory_size +
 251            s->memory.regions[i].mmap_offset;
 252
 253        guest_mem = mmap(0, size, PROT_READ | PROT_WRITE,
 254                         MAP_SHARED, s->fds[i], 0);
 255
 256        g_assert(guest_mem != MAP_FAILED);
 257        guest_mem += (s->memory.regions[i].mmap_offset / sizeof(*guest_mem));
 258
 259        for (j = 0; j < 1024; j++) {
 260            uint32_t a = qtest_readb(qts, s->memory.regions[i].guest_phys_addr + j);
 261            uint32_t b = guest_mem[j];
 262
 263            g_assert_cmpint(a, ==, b);
 264        }
 265
 266        munmap(guest_mem, s->memory.regions[i].memory_size);
 267    }
 268
 269    g_mutex_unlock(&s->data_mutex);
 270}
 271
 272static void *thread_function(void *data)
 273{
 274    GMainLoop *loop = data;
 275    g_main_loop_run(loop);
 276    return NULL;
 277}
 278
 279static int chr_can_read(void *opaque)
 280{
 281    return VHOST_USER_HDR_SIZE;
 282}
 283
 284static void chr_read(void *opaque, const uint8_t *buf, int size)
 285{
 286    TestServer *s = opaque;
 287    CharBackend *chr = &s->chr;
 288    VhostUserMsg msg;
 289    uint8_t *p = (uint8_t *) &msg;
 290    int fd = -1;
 291
 292    if (s->test_fail) {
 293        qemu_chr_fe_disconnect(chr);
 294        /* now switch to non-failure */
 295        s->test_fail = false;
 296    }
 297
 298    if (size != VHOST_USER_HDR_SIZE) {
 299        g_test_message("Wrong message size received %d", size);
 300        return;
 301    }
 302
 303    g_mutex_lock(&s->data_mutex);
 304    memcpy(p, buf, VHOST_USER_HDR_SIZE);
 305
 306    if (msg.size) {
 307        p += VHOST_USER_HDR_SIZE;
 308        size = qemu_chr_fe_read_all(chr, p, msg.size);
 309        if (size != msg.size) {
 310            g_test_message("Wrong message size received %d != %d",
 311                           size, msg.size);
 312            return;
 313        }
 314    }
 315
 316    switch (msg.request) {
 317    case VHOST_USER_GET_FEATURES:
 318        /* send back features to qemu */
 319        msg.flags |= VHOST_USER_REPLY_MASK;
 320        msg.size = sizeof(m.payload.u64);
 321        msg.payload.u64 = 0x1ULL << VHOST_F_LOG_ALL |
 322            0x1ULL << VHOST_USER_F_PROTOCOL_FEATURES;
 323        if (s->queues > 1) {
 324            msg.payload.u64 |= 0x1ULL << VIRTIO_NET_F_MQ;
 325        }
 326        if (s->test_flags >= TEST_FLAGS_BAD) {
 327            msg.payload.u64 = 0;
 328            s->test_flags = TEST_FLAGS_END;
 329        }
 330        p = (uint8_t *) &msg;
 331        qemu_chr_fe_write_all(chr, p, VHOST_USER_HDR_SIZE + msg.size);
 332        break;
 333
 334    case VHOST_USER_SET_FEATURES:
 335        g_assert_cmpint(msg.payload.u64 & (0x1ULL << VHOST_USER_F_PROTOCOL_FEATURES),
 336                        !=, 0ULL);
 337        if (s->test_flags == TEST_FLAGS_DISCONNECT) {
 338            qemu_chr_fe_disconnect(chr);
 339            s->test_flags = TEST_FLAGS_BAD;
 340        }
 341        break;
 342
 343    case VHOST_USER_GET_PROTOCOL_FEATURES:
 344        /* send back features to qemu */
 345        msg.flags |= VHOST_USER_REPLY_MASK;
 346        msg.size = sizeof(m.payload.u64);
 347        msg.payload.u64 = 1 << VHOST_USER_PROTOCOL_F_LOG_SHMFD;
 348        msg.payload.u64 |= 1 << VHOST_USER_PROTOCOL_F_CROSS_ENDIAN;
 349        if (s->queues > 1) {
 350            msg.payload.u64 |= 1 << VHOST_USER_PROTOCOL_F_MQ;
 351        }
 352        p = (uint8_t *) &msg;
 353        qemu_chr_fe_write_all(chr, p, VHOST_USER_HDR_SIZE + msg.size);
 354        break;
 355
 356    case VHOST_USER_GET_VRING_BASE:
 357        /* send back vring base to qemu */
 358        msg.flags |= VHOST_USER_REPLY_MASK;
 359        msg.size = sizeof(m.payload.state);
 360        msg.payload.state.num = 0;
 361        p = (uint8_t *) &msg;
 362        qemu_chr_fe_write_all(chr, p, VHOST_USER_HDR_SIZE + msg.size);
 363
 364        assert(msg.payload.state.index < s->queues * 2);
 365        s->rings &= ~(0x1ULL << msg.payload.state.index);
 366        g_cond_broadcast(&s->data_cond);
 367        break;
 368
 369    case VHOST_USER_SET_MEM_TABLE:
 370        /* received the mem table */
 371        memcpy(&s->memory, &msg.payload.memory, sizeof(msg.payload.memory));
 372        s->fds_num = qemu_chr_fe_get_msgfds(chr, s->fds,
 373                                            G_N_ELEMENTS(s->fds));
 374
 375        /* signal the test that it can continue */
 376        g_cond_broadcast(&s->data_cond);
 377        break;
 378
 379    case VHOST_USER_SET_VRING_KICK:
 380    case VHOST_USER_SET_VRING_CALL:
 381        /* consume the fd */
 382        qemu_chr_fe_get_msgfds(chr, &fd, 1);
 383        /*
 384         * This is a non-blocking eventfd.
 385         * The receive function forces it to be blocking,
 386         * so revert it back to non-blocking.
 387         */
 388        qemu_set_nonblock(fd);
 389        break;
 390
 391    case VHOST_USER_SET_LOG_BASE:
 392        if (s->log_fd != -1) {
 393            close(s->log_fd);
 394            s->log_fd = -1;
 395        }
 396        qemu_chr_fe_get_msgfds(chr, &s->log_fd, 1);
 397        msg.flags |= VHOST_USER_REPLY_MASK;
 398        msg.size = 0;
 399        p = (uint8_t *) &msg;
 400        qemu_chr_fe_write_all(chr, p, VHOST_USER_HDR_SIZE);
 401
 402        g_cond_broadcast(&s->data_cond);
 403        break;
 404
 405    case VHOST_USER_SET_VRING_BASE:
 406        assert(msg.payload.state.index < s->queues * 2);
 407        s->rings |= 0x1ULL << msg.payload.state.index;
 408        g_cond_broadcast(&s->data_cond);
 409        break;
 410
 411    case VHOST_USER_GET_QUEUE_NUM:
 412        msg.flags |= VHOST_USER_REPLY_MASK;
 413        msg.size = sizeof(m.payload.u64);
 414        msg.payload.u64 = s->queues;
 415        p = (uint8_t *) &msg;
 416        qemu_chr_fe_write_all(chr, p, VHOST_USER_HDR_SIZE + msg.size);
 417        break;
 418
 419    default:
 420        break;
 421    }
 422
 423    g_mutex_unlock(&s->data_mutex);
 424}
 425
 426static const char *init_hugepagefs(void)
 427{
 428#ifdef CONFIG_LINUX
 429    static const char *hugepagefs;
 430    const char *path = getenv("QTEST_HUGETLBFS_PATH");
 431    struct statfs fs;
 432    int ret;
 433
 434    if (hugepagefs) {
 435        return hugepagefs;
 436    }
 437    if (!path) {
 438        return NULL;
 439    }
 440
 441    if (access(path, R_OK | W_OK | X_OK)) {
 442        g_test_message("access on path (%s): %s", path, strerror(errno));
 443        g_test_fail();
 444        return NULL;
 445    }
 446
 447    do {
 448        ret = statfs(path, &fs);
 449    } while (ret != 0 && errno == EINTR);
 450
 451    if (ret != 0) {
 452        g_test_message("statfs on path (%s): %s", path, strerror(errno));
 453        g_test_fail();
 454        return NULL;
 455    }
 456
 457    if (fs.f_type != HUGETLBFS_MAGIC) {
 458        g_test_message("Warning: path not on HugeTLBFS: %s", path);
 459        g_test_fail();
 460        return NULL;
 461    }
 462
 463    hugepagefs = path;
 464    return hugepagefs;
 465#else
 466    return NULL;
 467#endif
 468}
 469
 470static TestServer *test_server_new(const gchar *name)
 471{
 472    TestServer *server = g_new0(TestServer, 1);
 473    char template[] = "/tmp/vhost-test-XXXXXX";
 474    const char *tmpfs;
 475
 476    server->context = g_main_context_new();
 477    server->loop = g_main_loop_new(server->context, FALSE);
 478
 479    /* run the main loop thread so the chardev may operate */
 480    server->thread = g_thread_new(NULL, thread_function, server->loop);
 481
 482    tmpfs = mkdtemp(template);
 483    if (!tmpfs) {
 484        g_test_message("mkdtemp on path (%s): %s", template, strerror(errno));
 485    }
 486    g_assert(tmpfs);
 487
 488    server->tmpfs = g_strdup(tmpfs);
 489    server->socket_path = g_strdup_printf("%s/%s.sock", tmpfs, name);
 490    server->mig_path = g_strdup_printf("%s/%s.mig", tmpfs, name);
 491    server->chr_name = g_strdup_printf("chr-%s", name);
 492
 493    g_mutex_init(&server->data_mutex);
 494    g_cond_init(&server->data_cond);
 495
 496    server->log_fd = -1;
 497    server->queues = 1;
 498
 499    return server;
 500}
 501
 502static void chr_event(void *opaque, int event)
 503{
 504    TestServer *s = opaque;
 505
 506    if (s->test_flags == TEST_FLAGS_END &&
 507        event == CHR_EVENT_CLOSED) {
 508        s->test_flags = TEST_FLAGS_OK;
 509    }
 510}
 511
 512static void test_server_create_chr(TestServer *server, const gchar *opt)
 513{
 514    gchar *chr_path;
 515    Chardev *chr;
 516
 517    chr_path = g_strdup_printf("unix:%s%s", server->socket_path, opt);
 518    chr = qemu_chr_new(server->chr_name, chr_path, server->context);
 519    g_free(chr_path);
 520
 521    g_assert_nonnull(chr);
 522    qemu_chr_fe_init(&server->chr, chr, &error_abort);
 523    qemu_chr_fe_set_handlers(&server->chr, chr_can_read, chr_read,
 524                             chr_event, NULL, server, server->context, true);
 525}
 526
 527static void test_server_listen(TestServer *server)
 528{
 529    test_server_create_chr(server, ",server,nowait");
 530}
 531
 532static void test_server_free(TestServer *server)
 533{
 534    int i, ret;
 535
 536    /* finish the helper thread and dispatch pending sources */
 537    g_main_loop_quit(server->loop);
 538    g_thread_join(server->thread);
 539    while (g_main_context_pending(NULL)) {
 540        g_main_context_iteration(NULL, TRUE);
 541    }
 542
 543    unlink(server->socket_path);
 544    g_free(server->socket_path);
 545
 546    unlink(server->mig_path);
 547    g_free(server->mig_path);
 548
 549    ret = rmdir(server->tmpfs);
 550    if (ret != 0) {
 551        g_test_message("unable to rmdir: path (%s): %s",
 552                       server->tmpfs, strerror(errno));
 553    }
 554    g_free(server->tmpfs);
 555
 556    qemu_chr_fe_deinit(&server->chr, true);
 557
 558    for (i = 0; i < server->fds_num; i++) {
 559        close(server->fds[i]);
 560    }
 561
 562    if (server->log_fd != -1) {
 563        close(server->log_fd);
 564    }
 565
 566    g_free(server->chr_name);
 567
 568    g_main_loop_unref(server->loop);
 569    g_main_context_unref(server->context);
 570    g_cond_clear(&server->data_cond);
 571    g_mutex_clear(&server->data_mutex);
 572    g_free(server);
 573}
 574
 575static void wait_for_log_fd(TestServer *s)
 576{
 577    gint64 end_time;
 578
 579    g_mutex_lock(&s->data_mutex);
 580    end_time = g_get_monotonic_time() + 5 * G_TIME_SPAN_SECOND;
 581    while (s->log_fd == -1) {
 582        if (!g_cond_wait_until(&s->data_cond, &s->data_mutex, end_time)) {
 583            /* timeout has passed */
 584            g_assert(s->log_fd != -1);
 585            break;
 586        }
 587    }
 588
 589    g_mutex_unlock(&s->data_mutex);
 590}
 591
 592static void write_guest_mem(TestServer *s, uint32_t seed)
 593{
 594    uint32_t *guest_mem;
 595    int i, j;
 596    size_t size;
 597
 598    /* iterate all regions */
 599    for (i = 0; i < s->fds_num; i++) {
 600
 601        /* We'll write only the region statring at 0x0 */
 602        if (s->memory.regions[i].guest_phys_addr != 0x0) {
 603            continue;
 604        }
 605
 606        g_assert_cmpint(s->memory.regions[i].memory_size, >, 1024);
 607
 608        size = s->memory.regions[i].memory_size +
 609            s->memory.regions[i].mmap_offset;
 610
 611        guest_mem = mmap(0, size, PROT_READ | PROT_WRITE,
 612                         MAP_SHARED, s->fds[i], 0);
 613
 614        g_assert(guest_mem != MAP_FAILED);
 615        guest_mem += (s->memory.regions[i].mmap_offset / sizeof(*guest_mem));
 616
 617        for (j = 0; j < 256; j++) {
 618            guest_mem[j] = seed + j;
 619        }
 620
 621        munmap(guest_mem, s->memory.regions[i].memory_size);
 622        break;
 623    }
 624}
 625
 626static guint64 get_log_size(TestServer *s)
 627{
 628    guint64 log_size = 0;
 629    int i;
 630
 631    for (i = 0; i < s->memory.nregions; ++i) {
 632        VhostUserMemoryRegion *reg = &s->memory.regions[i];
 633        guint64 last = range_get_last(reg->guest_phys_addr,
 634                                       reg->memory_size);
 635        log_size = MAX(log_size, last / (8 * VHOST_LOG_PAGE) + 1);
 636    }
 637
 638    return log_size;
 639}
 640
 641typedef struct TestMigrateSource {
 642    GSource source;
 643    TestServer *src;
 644    TestServer *dest;
 645} TestMigrateSource;
 646
 647static gboolean
 648test_migrate_source_check(GSource *source)
 649{
 650    TestMigrateSource *t = (TestMigrateSource *)source;
 651    gboolean overlap = t->src->rings && t->dest->rings;
 652
 653    g_assert(!overlap);
 654
 655    return FALSE;
 656}
 657
 658GSourceFuncs test_migrate_source_funcs = {
 659    .check = test_migrate_source_check,
 660};
 661
 662static void vhost_user_test_cleanup(void *s)
 663{
 664    TestServer *server = s;
 665
 666    qos_invalidate_command_line();
 667    test_server_free(server);
 668}
 669
 670static void *vhost_user_test_setup(GString *cmd_line, void *arg)
 671{
 672    TestServer *server = test_server_new("vhost-user-test");
 673    test_server_listen(server);
 674
 675    append_mem_opts(server, cmd_line, 256, TEST_MEMFD_AUTO);
 676    append_vhost_opts(server, cmd_line, "");
 677
 678    g_test_queue_destroy(vhost_user_test_cleanup, server);
 679
 680    return server;
 681}
 682
 683static void *vhost_user_test_setup_memfd(GString *cmd_line, void *arg)
 684{
 685    TestServer *server = test_server_new("vhost-user-test");
 686    test_server_listen(server);
 687
 688    append_mem_opts(server, cmd_line, 256, TEST_MEMFD_YES);
 689    append_vhost_opts(server, cmd_line, "");
 690
 691    g_test_queue_destroy(vhost_user_test_cleanup, server);
 692
 693    return server;
 694}
 695
 696static void test_read_guest_mem(void *obj, void *arg, QGuestAllocator *alloc)
 697{
 698    TestServer *server = arg;
 699
 700    if (!wait_for_fds(server)) {
 701        return;
 702    }
 703
 704    read_guest_mem_server(global_qtest, server);
 705}
 706
 707static void test_migrate(void *obj, void *arg, QGuestAllocator *alloc)
 708{
 709    TestServer *s = arg;
 710    TestServer *dest = test_server_new("dest");
 711    GString *dest_cmdline = g_string_new(qos_get_current_command_line());
 712    char *uri = g_strdup_printf("%s%s", "unix:", dest->mig_path);
 713    QTestState *to;
 714    GSource *source;
 715    QDict *rsp;
 716    guint8 *log;
 717    guint64 size;
 718
 719    if (!wait_for_fds(s)) {
 720        return;
 721    }
 722
 723    size = get_log_size(s);
 724    g_assert_cmpint(size, ==, (256 * 1024 * 1024) / (VHOST_LOG_PAGE * 8));
 725
 726    test_server_listen(dest);
 727    g_string_append_printf(dest_cmdline, " -incoming %s", uri);
 728    append_mem_opts(dest, dest_cmdline, 256, TEST_MEMFD_AUTO);
 729    append_vhost_opts(dest, dest_cmdline, "");
 730    to = qtest_init(dest_cmdline->str);
 731
 732    /* This would be where you call qos_allocate_objects(to, NULL), if you want
 733     * to talk to the QVirtioNet object on the destination.
 734     */
 735
 736    source = g_source_new(&test_migrate_source_funcs,
 737                          sizeof(TestMigrateSource));
 738    ((TestMigrateSource *)source)->src = s;
 739    ((TestMigrateSource *)source)->dest = dest;
 740    g_source_attach(source, s->context);
 741
 742    /* slow down migration to have time to fiddle with log */
 743    /* TODO: qtest could learn to break on some places */
 744    rsp = qmp("{ 'execute': 'migrate_set_speed',"
 745              "'arguments': { 'value': 10 } }");
 746    g_assert(qdict_haskey(rsp, "return"));
 747    qobject_unref(rsp);
 748
 749    rsp = qmp("{ 'execute': 'migrate', 'arguments': { 'uri': %s } }", uri);
 750    g_assert(qdict_haskey(rsp, "return"));
 751    qobject_unref(rsp);
 752
 753    wait_for_log_fd(s);
 754
 755    log = mmap(0, size, PROT_READ | PROT_WRITE, MAP_SHARED, s->log_fd, 0);
 756    g_assert(log != MAP_FAILED);
 757
 758    /* modify first page */
 759    write_guest_mem(s, 0x42);
 760    log[0] = 1;
 761    munmap(log, size);
 762
 763    /* speed things up */
 764    rsp = qmp("{ 'execute': 'migrate_set_speed',"
 765              "'arguments': { 'value': 0 } }");
 766    g_assert(qdict_haskey(rsp, "return"));
 767    qobject_unref(rsp);
 768
 769    qmp_eventwait("STOP");
 770    qtest_qmp_eventwait(to, "RESUME");
 771
 772    g_assert(wait_for_fds(dest));
 773    read_guest_mem_server(to, dest);
 774
 775    g_source_destroy(source);
 776    g_source_unref(source);
 777
 778    qtest_quit(to);
 779    test_server_free(dest);
 780    g_free(uri);
 781}
 782
 783static void wait_for_rings_started(TestServer *s, size_t count)
 784{
 785    gint64 end_time;
 786
 787    g_mutex_lock(&s->data_mutex);
 788    end_time = g_get_monotonic_time() + 5 * G_TIME_SPAN_SECOND;
 789    while (ctpop64(s->rings) != count) {
 790        if (!g_cond_wait_until(&s->data_cond, &s->data_mutex, end_time)) {
 791            /* timeout has passed */
 792            g_assert_cmpint(ctpop64(s->rings), ==, count);
 793            break;
 794        }
 795    }
 796
 797    g_mutex_unlock(&s->data_mutex);
 798}
 799
 800static inline void test_server_connect(TestServer *server)
 801{
 802    test_server_create_chr(server, ",reconnect=1");
 803}
 804
 805static gboolean
 806reconnect_cb(gpointer user_data)
 807{
 808    TestServer *s = user_data;
 809
 810    qemu_chr_fe_disconnect(&s->chr);
 811
 812    return FALSE;
 813}
 814
 815static gpointer
 816connect_thread(gpointer data)
 817{
 818    TestServer *s = data;
 819
 820    /* wait for qemu to start before first try, to avoid extra warnings */
 821    g_usleep(G_USEC_PER_SEC);
 822    test_server_connect(s);
 823
 824    return NULL;
 825}
 826
 827static void *vhost_user_test_setup_reconnect(GString *cmd_line, void *arg)
 828{
 829    TestServer *s = test_server_new("reconnect");
 830
 831    g_thread_new("connect", connect_thread, s);
 832    append_mem_opts(s, cmd_line, 256, TEST_MEMFD_AUTO);
 833    append_vhost_opts(s, cmd_line, ",server");
 834
 835    g_test_queue_destroy(vhost_user_test_cleanup, s);
 836
 837    return s;
 838}
 839
 840static void test_reconnect(void *obj, void *arg, QGuestAllocator *alloc)
 841{
 842    TestServer *s = arg;
 843    GSource *src;
 844
 845    if (!wait_for_fds(s)) {
 846        return;
 847    }
 848
 849    wait_for_rings_started(s, 2);
 850
 851    /* reconnect */
 852    s->fds_num = 0;
 853    s->rings = 0;
 854    src = g_idle_source_new();
 855    g_source_set_callback(src, reconnect_cb, s, NULL);
 856    g_source_attach(src, s->context);
 857    g_source_unref(src);
 858    g_assert(wait_for_fds(s));
 859    wait_for_rings_started(s, 2);
 860}
 861
 862static void *vhost_user_test_setup_connect_fail(GString *cmd_line, void *arg)
 863{
 864    TestServer *s = test_server_new("connect-fail");
 865
 866    s->test_fail = true;
 867
 868    g_thread_new("connect", connect_thread, s);
 869    append_mem_opts(s, cmd_line, 256, TEST_MEMFD_AUTO);
 870    append_vhost_opts(s, cmd_line, ",server");
 871
 872    g_test_queue_destroy(vhost_user_test_cleanup, s);
 873
 874    return s;
 875}
 876
 877static void *vhost_user_test_setup_flags_mismatch(GString *cmd_line, void *arg)
 878{
 879    TestServer *s = test_server_new("flags-mismatch");
 880
 881    s->test_flags = TEST_FLAGS_DISCONNECT;
 882
 883    g_thread_new("connect", connect_thread, s);
 884    append_mem_opts(s, cmd_line, 256, TEST_MEMFD_AUTO);
 885    append_vhost_opts(s, cmd_line, ",server");
 886
 887    g_test_queue_destroy(vhost_user_test_cleanup, s);
 888
 889    return s;
 890}
 891
 892static void test_vhost_user_started(void *obj, void *arg, QGuestAllocator *alloc)
 893{
 894    TestServer *s = arg;
 895
 896    if (!wait_for_fds(s)) {
 897        return;
 898    }
 899    wait_for_rings_started(s, 2);
 900}
 901
 902static void *vhost_user_test_setup_multiqueue(GString *cmd_line, void *arg)
 903{
 904    TestServer *s = vhost_user_test_setup(cmd_line, arg);
 905
 906    s->queues = 2;
 907    g_string_append_printf(cmd_line,
 908                           " -set netdev.hs0.queues=%d"
 909                           " -global virtio-net-pci.vectors=%d",
 910                           s->queues, s->queues * 2 + 2);
 911
 912    return s;
 913}
 914
 915static void test_multiqueue(void *obj, void *arg, QGuestAllocator *alloc)
 916{
 917    TestServer *s = arg;
 918
 919    wait_for_rings_started(s, s->queues * 2);
 920}
 921
 922static void register_vhost_user_test(void)
 923{
 924    QOSGraphTestOptions opts = {
 925        .before = vhost_user_test_setup,
 926        .subprocess = true,
 927    };
 928
 929    qemu_add_opts(&qemu_chardev_opts);
 930
 931    qos_add_test("vhost-user/read-guest-mem/memfile",
 932                 "virtio-net",
 933                 test_read_guest_mem, &opts);
 934
 935    if (qemu_memfd_check(MFD_ALLOW_SEALING)) {
 936        opts.before = vhost_user_test_setup_memfd;
 937        qos_add_test("vhost-user/read-guest-mem/memfd",
 938                     "virtio-net",
 939                     test_read_guest_mem, &opts);
 940    }
 941
 942    qos_add_test("vhost-user/migrate",
 943                 "virtio-net",
 944                 test_migrate, &opts);
 945
 946    /* keeps failing on build-system since Aug 15 2017 */
 947    if (getenv("QTEST_VHOST_USER_FIXME")) {
 948        opts.before = vhost_user_test_setup_reconnect;
 949        qos_add_test("vhost-user/reconnect", "virtio-net",
 950                     test_reconnect, &opts);
 951
 952        opts.before = vhost_user_test_setup_connect_fail;
 953        qos_add_test("vhost-user/connect-fail", "virtio-net",
 954                     test_vhost_user_started, &opts);
 955
 956        opts.before = vhost_user_test_setup_flags_mismatch;
 957        qos_add_test("vhost-user/flags-mismatch", "virtio-net",
 958                     test_vhost_user_started, &opts);
 959    }
 960
 961    opts.before = vhost_user_test_setup_multiqueue;
 962    opts.edge.extra_device_opts = "mq=on";
 963    qos_add_test("vhost-user/multiqueue",
 964                 "virtio-net",
 965                 test_multiqueue, &opts);
 966}
 967libqos_init(register_vhost_user_test);
 968