xref: /openbmc/qemu/tests/qtest/vhost-user-test.c (revision b6f53ae0)
11e8a1faeSThomas Huth /*
21e8a1faeSThomas Huth  * QTest testcase for the vhost-user
31e8a1faeSThomas Huth  *
41e8a1faeSThomas Huth  * Copyright (c) 2014 Virtual Open Systems Sarl.
51e8a1faeSThomas Huth  *
61e8a1faeSThomas Huth  * This work is licensed under the terms of the GNU GPL, version 2 or later.
71e8a1faeSThomas Huth  * See the COPYING file in the top-level directory.
81e8a1faeSThomas Huth  *
91e8a1faeSThomas Huth  */
101e8a1faeSThomas Huth 
111e8a1faeSThomas Huth #include "qemu/osdep.h"
121e8a1faeSThomas Huth 
131e8a1faeSThomas Huth #include "libqtest-single.h"
141e8a1faeSThomas Huth #include "qapi/error.h"
151e8a1faeSThomas Huth #include "qapi/qmp/qdict.h"
161e8a1faeSThomas Huth #include "qemu/config-file.h"
171e8a1faeSThomas Huth #include "qemu/option.h"
181e8a1faeSThomas Huth #include "qemu/range.h"
191e8a1faeSThomas Huth #include "qemu/sockets.h"
201e8a1faeSThomas Huth #include "chardev/char-fe.h"
211e8a1faeSThomas Huth #include "qemu/memfd.h"
221e8a1faeSThomas Huth #include "qemu/module.h"
231e8a1faeSThomas Huth #include "sysemu/sysemu.h"
241e8a1faeSThomas Huth #include "libqos/libqos.h"
251e8a1faeSThomas Huth #include "libqos/pci-pc.h"
261e8a1faeSThomas Huth #include "libqos/virtio-pci.h"
271e8a1faeSThomas Huth 
281e8a1faeSThomas Huth #include "libqos/malloc-pc.h"
2930ea13e9SAlex Bennée #include "libqos/qgraph_internal.h"
301e8a1faeSThomas Huth #include "hw/virtio/virtio-net.h"
311e8a1faeSThomas Huth 
321e8a1faeSThomas Huth #include "standard-headers/linux/vhost_types.h"
331e8a1faeSThomas Huth #include "standard-headers/linux/virtio_ids.h"
341e8a1faeSThomas Huth #include "standard-headers/linux/virtio_net.h"
358fcfc823SAlex Bennée #include "standard-headers/linux/virtio_gpio.h"
36*b6f53ae0SMilan Zamazal #include "standard-headers/linux/virtio_scmi.h"
371e8a1faeSThomas Huth 
381e8a1faeSThomas Huth #ifdef CONFIG_LINUX
391e8a1faeSThomas Huth #include <sys/vfs.h>
401e8a1faeSThomas Huth #endif
411e8a1faeSThomas Huth 
421e8a1faeSThomas Huth 
431e8a1faeSThomas Huth #define QEMU_CMD_MEM    " -m %d -object memory-backend-file,id=mem,size=%dM," \
441e8a1faeSThomas Huth                         "mem-path=%s,share=on -numa node,memdev=mem"
451e8a1faeSThomas Huth #define QEMU_CMD_MEMFD  " -m %d -object memory-backend-memfd,id=mem,size=%dM," \
461e8a1faeSThomas Huth                         " -numa node,memdev=mem"
471e8a1faeSThomas Huth #define QEMU_CMD_CHR    " -chardev socket,id=%s,path=%s%s"
48d24d1ad3SEric Auger #define QEMU_CMD_NETDEV " -netdev vhost-user,id=hs0,chardev=%s,vhostforce=on"
491e8a1faeSThomas Huth 
501e8a1faeSThomas Huth #define HUGETLBFS_MAGIC       0x958458f6
511e8a1faeSThomas Huth 
521e8a1faeSThomas Huth /*********** FROM hw/virtio/vhost-user.c *************************************/
531e8a1faeSThomas Huth 
541e8a1faeSThomas Huth #define VHOST_MEMORY_MAX_NREGIONS    8
551e8a1faeSThomas Huth #define VHOST_MAX_VIRTQUEUES    0x100
561e8a1faeSThomas Huth 
571e8a1faeSThomas Huth #define VHOST_USER_F_PROTOCOL_FEATURES 30
588fcfc823SAlex Bennée #define VIRTIO_F_VERSION_1 32
598fcfc823SAlex Bennée 
601e8a1faeSThomas Huth #define VHOST_USER_PROTOCOL_F_MQ 0
611e8a1faeSThomas Huth #define VHOST_USER_PROTOCOL_F_LOG_SHMFD 1
621e8a1faeSThomas Huth #define VHOST_USER_PROTOCOL_F_CROSS_ENDIAN   6
638fcfc823SAlex Bennée #define VHOST_USER_PROTOCOL_F_CONFIG 9
641e8a1faeSThomas Huth 
651e8a1faeSThomas Huth #define VHOST_LOG_PAGE 0x1000
661e8a1faeSThomas Huth 
671e8a1faeSThomas Huth typedef enum VhostUserRequest {
681e8a1faeSThomas Huth     VHOST_USER_NONE = 0,
691e8a1faeSThomas Huth     VHOST_USER_GET_FEATURES = 1,
701e8a1faeSThomas Huth     VHOST_USER_SET_FEATURES = 2,
711e8a1faeSThomas Huth     VHOST_USER_SET_OWNER = 3,
721e8a1faeSThomas Huth     VHOST_USER_RESET_OWNER = 4,
731e8a1faeSThomas Huth     VHOST_USER_SET_MEM_TABLE = 5,
741e8a1faeSThomas Huth     VHOST_USER_SET_LOG_BASE = 6,
751e8a1faeSThomas Huth     VHOST_USER_SET_LOG_FD = 7,
761e8a1faeSThomas Huth     VHOST_USER_SET_VRING_NUM = 8,
771e8a1faeSThomas Huth     VHOST_USER_SET_VRING_ADDR = 9,
781e8a1faeSThomas Huth     VHOST_USER_SET_VRING_BASE = 10,
791e8a1faeSThomas Huth     VHOST_USER_GET_VRING_BASE = 11,
801e8a1faeSThomas Huth     VHOST_USER_SET_VRING_KICK = 12,
811e8a1faeSThomas Huth     VHOST_USER_SET_VRING_CALL = 13,
821e8a1faeSThomas Huth     VHOST_USER_SET_VRING_ERR = 14,
831e8a1faeSThomas Huth     VHOST_USER_GET_PROTOCOL_FEATURES = 15,
841e8a1faeSThomas Huth     VHOST_USER_SET_PROTOCOL_FEATURES = 16,
851e8a1faeSThomas Huth     VHOST_USER_GET_QUEUE_NUM = 17,
861e8a1faeSThomas Huth     VHOST_USER_SET_VRING_ENABLE = 18,
87ff070f60SAlex Bennée     VHOST_USER_GET_CONFIG = 24,
88ff070f60SAlex Bennée     VHOST_USER_SET_CONFIG = 25,
891e8a1faeSThomas Huth     VHOST_USER_MAX
901e8a1faeSThomas Huth } VhostUserRequest;
911e8a1faeSThomas Huth 
921e8a1faeSThomas Huth typedef struct VhostUserMemoryRegion {
931e8a1faeSThomas Huth     uint64_t guest_phys_addr;
941e8a1faeSThomas Huth     uint64_t memory_size;
951e8a1faeSThomas Huth     uint64_t userspace_addr;
961e8a1faeSThomas Huth     uint64_t mmap_offset;
971e8a1faeSThomas Huth } VhostUserMemoryRegion;
981e8a1faeSThomas Huth 
991e8a1faeSThomas Huth typedef struct VhostUserMemory {
1001e8a1faeSThomas Huth     uint32_t nregions;
1011e8a1faeSThomas Huth     uint32_t padding;
1021e8a1faeSThomas Huth     VhostUserMemoryRegion regions[VHOST_MEMORY_MAX_NREGIONS];
1031e8a1faeSThomas Huth } VhostUserMemory;
1041e8a1faeSThomas Huth 
1051e8a1faeSThomas Huth typedef struct VhostUserLog {
1061e8a1faeSThomas Huth     uint64_t mmap_size;
1071e8a1faeSThomas Huth     uint64_t mmap_offset;
1081e8a1faeSThomas Huth } VhostUserLog;
1091e8a1faeSThomas Huth 
1101e8a1faeSThomas Huth typedef struct VhostUserMsg {
1111e8a1faeSThomas Huth     VhostUserRequest request;
1121e8a1faeSThomas Huth 
1131e8a1faeSThomas Huth #define VHOST_USER_VERSION_MASK     (0x3)
1141e8a1faeSThomas Huth #define VHOST_USER_REPLY_MASK       (0x1<<2)
1151e8a1faeSThomas Huth     uint32_t flags;
1161e8a1faeSThomas Huth     uint32_t size; /* the following payload size */
1171e8a1faeSThomas Huth     union {
1181e8a1faeSThomas Huth #define VHOST_USER_VRING_IDX_MASK   (0xff)
1191e8a1faeSThomas Huth #define VHOST_USER_VRING_NOFD_MASK  (0x1<<8)
1201e8a1faeSThomas Huth         uint64_t u64;
1211e8a1faeSThomas Huth         struct vhost_vring_state state;
1221e8a1faeSThomas Huth         struct vhost_vring_addr addr;
1231e8a1faeSThomas Huth         VhostUserMemory memory;
1241e8a1faeSThomas Huth         VhostUserLog log;
1251e8a1faeSThomas Huth     } payload;
1261e8a1faeSThomas Huth } QEMU_PACKED VhostUserMsg;
1271e8a1faeSThomas Huth 
1281e8a1faeSThomas Huth static VhostUserMsg m __attribute__ ((unused));
1291e8a1faeSThomas Huth #define VHOST_USER_HDR_SIZE (sizeof(m.request) \
1301e8a1faeSThomas Huth                             + sizeof(m.flags) \
1311e8a1faeSThomas Huth                             + sizeof(m.size))
1321e8a1faeSThomas Huth 
1331e8a1faeSThomas Huth #define VHOST_USER_PAYLOAD_SIZE (sizeof(m) - VHOST_USER_HDR_SIZE)
1341e8a1faeSThomas Huth 
1351e8a1faeSThomas Huth /* The version of the protocol we support */
1361e8a1faeSThomas Huth #define VHOST_USER_VERSION    (0x1)
1371e8a1faeSThomas Huth /*****************************************************************************/
1381e8a1faeSThomas Huth 
1391e8a1faeSThomas Huth enum {
1401e8a1faeSThomas Huth     TEST_FLAGS_OK,
1411e8a1faeSThomas Huth     TEST_FLAGS_DISCONNECT,
1421e8a1faeSThomas Huth     TEST_FLAGS_BAD,
1431e8a1faeSThomas Huth     TEST_FLAGS_END,
1441e8a1faeSThomas Huth };
1451e8a1faeSThomas Huth 
146892040dcSDima Stepanov enum {
147892040dcSDima Stepanov     VHOST_USER_NET,
1488fcfc823SAlex Bennée     VHOST_USER_GPIO,
149*b6f53ae0SMilan Zamazal     VHOST_USER_SCMI,
150892040dcSDima Stepanov };
151892040dcSDima Stepanov 
1521e8a1faeSThomas Huth typedef struct TestServer {
1531e8a1faeSThomas Huth     gchar *socket_path;
1541e8a1faeSThomas Huth     gchar *mig_path;
1551e8a1faeSThomas Huth     gchar *chr_name;
1561e8a1faeSThomas Huth     gchar *tmpfs;
1571e8a1faeSThomas Huth     CharBackend chr;
1581e8a1faeSThomas Huth     int fds_num;
1591e8a1faeSThomas Huth     int fds[VHOST_MEMORY_MAX_NREGIONS];
1601e8a1faeSThomas Huth     VhostUserMemory memory;
1611e8a1faeSThomas Huth     GMainContext *context;
1621e8a1faeSThomas Huth     GMainLoop *loop;
1631e8a1faeSThomas Huth     GThread *thread;
1641e8a1faeSThomas Huth     GMutex data_mutex;
1651e8a1faeSThomas Huth     GCond data_cond;
1661e8a1faeSThomas Huth     int log_fd;
1671e8a1faeSThomas Huth     uint64_t rings;
1681e8a1faeSThomas Huth     bool test_fail;
1691e8a1faeSThomas Huth     int test_flags;
1701e8a1faeSThomas Huth     int queues;
171892040dcSDima Stepanov     struct vhost_user_ops *vu_ops;
1721e8a1faeSThomas Huth } TestServer;
1731e8a1faeSThomas Huth 
174892040dcSDima Stepanov struct vhost_user_ops {
175892040dcSDima Stepanov     /* Device types. */
176892040dcSDima Stepanov     int type;
177892040dcSDima Stepanov     void (*append_opts)(TestServer *s, GString *cmd_line,
178892040dcSDima Stepanov             const char *chr_opts);
179892040dcSDima Stepanov 
180892040dcSDima Stepanov     /* VHOST-USER commands. */
18119d55a19SAlex Bennée     uint64_t (*get_features)(TestServer *s);
182892040dcSDima Stepanov     void (*set_features)(TestServer *s, CharBackend *chr,
183892040dcSDima Stepanov                          VhostUserMsg *msg);
184892040dcSDima Stepanov     void (*get_protocol_features)(TestServer *s,
185892040dcSDima Stepanov                                   CharBackend *chr, VhostUserMsg *msg);
186892040dcSDima Stepanov };
187892040dcSDima Stepanov 
1881e8a1faeSThomas Huth static const char *init_hugepagefs(void);
189892040dcSDima Stepanov static TestServer *test_server_new(const gchar *name,
190892040dcSDima Stepanov         struct vhost_user_ops *ops);
1911e8a1faeSThomas Huth static void test_server_free(TestServer *server);
1921e8a1faeSThomas Huth static void test_server_listen(TestServer *server);
1931e8a1faeSThomas Huth 
1941e8a1faeSThomas Huth enum test_memfd {
1951e8a1faeSThomas Huth     TEST_MEMFD_AUTO,
1961e8a1faeSThomas Huth     TEST_MEMFD_YES,
1971e8a1faeSThomas Huth     TEST_MEMFD_NO,
1981e8a1faeSThomas Huth };
1991e8a1faeSThomas Huth 
append_vhost_net_opts(TestServer * s,GString * cmd_line,const char * chr_opts)200892040dcSDima Stepanov static void append_vhost_net_opts(TestServer *s, GString *cmd_line,
2011e8a1faeSThomas Huth                              const char *chr_opts)
2021e8a1faeSThomas Huth {
2031e8a1faeSThomas Huth     g_string_append_printf(cmd_line, QEMU_CMD_CHR QEMU_CMD_NETDEV,
2041e8a1faeSThomas Huth                            s->chr_name, s->socket_path,
2051e8a1faeSThomas Huth                            chr_opts, s->chr_name);
2061e8a1faeSThomas Huth }
2071e8a1faeSThomas Huth 
2088fcfc823SAlex Bennée /*
2098fcfc823SAlex Bennée  * For GPIO there are no other magic devices we need to add (like
2108fcfc823SAlex Bennée  * block or netdev) so all we need to worry about is the vhost-user
2118fcfc823SAlex Bennée  * chardev socket.
2128fcfc823SAlex Bennée  */
append_vhost_gpio_opts(TestServer * s,GString * cmd_line,const char * chr_opts)2138fcfc823SAlex Bennée static void append_vhost_gpio_opts(TestServer *s, GString *cmd_line,
2148fcfc823SAlex Bennée                              const char *chr_opts)
2158fcfc823SAlex Bennée {
2168fcfc823SAlex Bennée     g_string_append_printf(cmd_line, QEMU_CMD_CHR,
2178fcfc823SAlex Bennée                            s->chr_name, s->socket_path,
2188fcfc823SAlex Bennée                            chr_opts);
2198fcfc823SAlex Bennée }
2208fcfc823SAlex Bennée 
append_mem_opts(TestServer * server,GString * cmd_line,int size,enum test_memfd memfd)2211e8a1faeSThomas Huth static void append_mem_opts(TestServer *server, GString *cmd_line,
2221e8a1faeSThomas Huth                             int size, enum test_memfd memfd)
2231e8a1faeSThomas Huth {
2241e8a1faeSThomas Huth     if (memfd == TEST_MEMFD_AUTO) {
2251e8a1faeSThomas Huth         memfd = qemu_memfd_check(MFD_ALLOW_SEALING) ? TEST_MEMFD_YES
2261e8a1faeSThomas Huth                                                     : TEST_MEMFD_NO;
2271e8a1faeSThomas Huth     }
2281e8a1faeSThomas Huth 
2291e8a1faeSThomas Huth     if (memfd == TEST_MEMFD_YES) {
2301e8a1faeSThomas Huth         g_string_append_printf(cmd_line, QEMU_CMD_MEMFD, size, size);
2311e8a1faeSThomas Huth     } else {
2321e8a1faeSThomas Huth         const char *root = init_hugepagefs() ? : server->tmpfs;
2331e8a1faeSThomas Huth 
2341e8a1faeSThomas Huth         g_string_append_printf(cmd_line, QEMU_CMD_MEM, size, size, root);
2351e8a1faeSThomas Huth     }
2361e8a1faeSThomas Huth }
2371e8a1faeSThomas Huth 
wait_for_fds(TestServer * s)2381e8a1faeSThomas Huth static bool wait_for_fds(TestServer *s)
2391e8a1faeSThomas Huth {
2401e8a1faeSThomas Huth     gint64 end_time;
2411e8a1faeSThomas Huth     bool got_region;
2421e8a1faeSThomas Huth     int i;
2431e8a1faeSThomas Huth 
2441e8a1faeSThomas Huth     g_mutex_lock(&s->data_mutex);
2451e8a1faeSThomas Huth 
2461e8a1faeSThomas Huth     end_time = g_get_monotonic_time() + 5 * G_TIME_SPAN_SECOND;
2471e8a1faeSThomas Huth     while (!s->fds_num) {
2481e8a1faeSThomas Huth         if (!g_cond_wait_until(&s->data_cond, &s->data_mutex, end_time)) {
2491e8a1faeSThomas Huth             /* timeout has passed */
2501e8a1faeSThomas Huth             g_assert(s->fds_num);
2511e8a1faeSThomas Huth             break;
2521e8a1faeSThomas Huth         }
2531e8a1faeSThomas Huth     }
2541e8a1faeSThomas Huth 
2551e8a1faeSThomas Huth     /* check for sanity */
2561e8a1faeSThomas Huth     g_assert_cmpint(s->fds_num, >, 0);
2571e8a1faeSThomas Huth     g_assert_cmpint(s->fds_num, ==, s->memory.nregions);
2581e8a1faeSThomas Huth 
2591e8a1faeSThomas Huth     g_mutex_unlock(&s->data_mutex);
2601e8a1faeSThomas Huth 
2611e8a1faeSThomas Huth     got_region = false;
2621e8a1faeSThomas Huth     for (i = 0; i < s->memory.nregions; ++i) {
2631e8a1faeSThomas Huth         VhostUserMemoryRegion *reg = &s->memory.regions[i];
2641e8a1faeSThomas Huth         if (reg->guest_phys_addr == 0) {
2651e8a1faeSThomas Huth             got_region = true;
2661e8a1faeSThomas Huth             break;
2671e8a1faeSThomas Huth         }
2681e8a1faeSThomas Huth     }
2691e8a1faeSThomas Huth     if (!got_region) {
2701e8a1faeSThomas Huth         g_test_skip("No memory at address 0x0");
2711e8a1faeSThomas Huth     }
2721e8a1faeSThomas Huth     return got_region;
2731e8a1faeSThomas Huth }
2741e8a1faeSThomas Huth 
read_guest_mem_server(QTestState * qts,TestServer * s)2751e8a1faeSThomas Huth static void read_guest_mem_server(QTestState *qts, TestServer *s)
2761e8a1faeSThomas Huth {
2771e8a1faeSThomas Huth     uint8_t *guest_mem;
2781e8a1faeSThomas Huth     int i, j;
2791e8a1faeSThomas Huth     size_t size;
2801e8a1faeSThomas Huth 
2811e8a1faeSThomas Huth     g_mutex_lock(&s->data_mutex);
2821e8a1faeSThomas Huth 
2831e8a1faeSThomas Huth     /* iterate all regions */
2841e8a1faeSThomas Huth     for (i = 0; i < s->fds_num; i++) {
2851e8a1faeSThomas Huth 
286ac9fd9b6SMilan Zamazal         /* We'll check only the region starting at 0x0 */
2871e8a1faeSThomas Huth         if (s->memory.regions[i].guest_phys_addr != 0x0) {
2881e8a1faeSThomas Huth             continue;
2891e8a1faeSThomas Huth         }
2901e8a1faeSThomas Huth 
2911e8a1faeSThomas Huth         g_assert_cmpint(s->memory.regions[i].memory_size, >, 1024);
2921e8a1faeSThomas Huth 
2931e8a1faeSThomas Huth         size = s->memory.regions[i].memory_size +
2941e8a1faeSThomas Huth             s->memory.regions[i].mmap_offset;
2951e8a1faeSThomas Huth 
2961e8a1faeSThomas Huth         guest_mem = mmap(0, size, PROT_READ | PROT_WRITE,
2971e8a1faeSThomas Huth                          MAP_SHARED, s->fds[i], 0);
2981e8a1faeSThomas Huth 
2991e8a1faeSThomas Huth         g_assert(guest_mem != MAP_FAILED);
3001e8a1faeSThomas Huth         guest_mem += (s->memory.regions[i].mmap_offset / sizeof(*guest_mem));
3011e8a1faeSThomas Huth 
3021e8a1faeSThomas Huth         for (j = 0; j < 1024; j++) {
3031e8a1faeSThomas Huth             uint32_t a = qtest_readb(qts, s->memory.regions[i].guest_phys_addr + j);
3041e8a1faeSThomas Huth             uint32_t b = guest_mem[j];
3051e8a1faeSThomas Huth 
3061e8a1faeSThomas Huth             g_assert_cmpint(a, ==, b);
3071e8a1faeSThomas Huth         }
3081e8a1faeSThomas Huth 
3091e8a1faeSThomas Huth         munmap(guest_mem, s->memory.regions[i].memory_size);
3101e8a1faeSThomas Huth     }
3111e8a1faeSThomas Huth 
3121e8a1faeSThomas Huth     g_mutex_unlock(&s->data_mutex);
3131e8a1faeSThomas Huth }
3141e8a1faeSThomas Huth 
thread_function(void * data)3151e8a1faeSThomas Huth static void *thread_function(void *data)
3161e8a1faeSThomas Huth {
3171e8a1faeSThomas Huth     GMainLoop *loop = data;
3181e8a1faeSThomas Huth     g_main_loop_run(loop);
3191e8a1faeSThomas Huth     return NULL;
3201e8a1faeSThomas Huth }
3211e8a1faeSThomas Huth 
chr_can_read(void * opaque)3221e8a1faeSThomas Huth static int chr_can_read(void *opaque)
3231e8a1faeSThomas Huth {
3241e8a1faeSThomas Huth     return VHOST_USER_HDR_SIZE;
3251e8a1faeSThomas Huth }
3261e8a1faeSThomas Huth 
chr_read(void * opaque,const uint8_t * buf,int size)3271e8a1faeSThomas Huth static void chr_read(void *opaque, const uint8_t *buf, int size)
3281e8a1faeSThomas Huth {
329b2670d1fSMarc-André Lureau     g_autoptr(GError) err = NULL;
3301e8a1faeSThomas Huth     TestServer *s = opaque;
3311e8a1faeSThomas Huth     CharBackend *chr = &s->chr;
3321e8a1faeSThomas Huth     VhostUserMsg msg;
3331e8a1faeSThomas Huth     uint8_t *p = (uint8_t *) &msg;
3341e8a1faeSThomas Huth     int fd = -1;
3351e8a1faeSThomas Huth 
3361e8a1faeSThomas Huth     if (s->test_fail) {
3371e8a1faeSThomas Huth         qemu_chr_fe_disconnect(chr);
3381e8a1faeSThomas Huth         /* now switch to non-failure */
3391e8a1faeSThomas Huth         s->test_fail = false;
3401e8a1faeSThomas Huth     }
3411e8a1faeSThomas Huth 
3421e8a1faeSThomas Huth     if (size != VHOST_USER_HDR_SIZE) {
34330ea13e9SAlex Bennée         qos_printf("%s: Wrong message size received %d\n", __func__, size);
3441e8a1faeSThomas Huth         return;
3451e8a1faeSThomas Huth     }
3461e8a1faeSThomas Huth 
3471e8a1faeSThomas Huth     g_mutex_lock(&s->data_mutex);
3481e8a1faeSThomas Huth     memcpy(p, buf, VHOST_USER_HDR_SIZE);
3491e8a1faeSThomas Huth 
3501e8a1faeSThomas Huth     if (msg.size) {
3511e8a1faeSThomas Huth         p += VHOST_USER_HDR_SIZE;
3521e8a1faeSThomas Huth         size = qemu_chr_fe_read_all(chr, p, msg.size);
3531e8a1faeSThomas Huth         if (size != msg.size) {
35430ea13e9SAlex Bennée             qos_printf("%s: Wrong message size received %d != %d\n",
35530ea13e9SAlex Bennée                        __func__, size, msg.size);
3563ee7f21eSPaolo Bonzini             goto out;
3571e8a1faeSThomas Huth         }
3581e8a1faeSThomas Huth     }
3591e8a1faeSThomas Huth 
3601e8a1faeSThomas Huth     switch (msg.request) {
3611e8a1faeSThomas Huth     case VHOST_USER_GET_FEATURES:
36219d55a19SAlex Bennée         /* Mandatory for tests to define get_features */
36319d55a19SAlex Bennée         g_assert(s->vu_ops->get_features);
36419d55a19SAlex Bennée 
3651e8a1faeSThomas Huth         /* send back features to qemu */
3661e8a1faeSThomas Huth         msg.flags |= VHOST_USER_REPLY_MASK;
3671e8a1faeSThomas Huth         msg.size = sizeof(m.payload.u64);
36819d55a19SAlex Bennée 
3691e8a1faeSThomas Huth         if (s->test_flags >= TEST_FLAGS_BAD) {
3701e8a1faeSThomas Huth             msg.payload.u64 = 0;
3711e8a1faeSThomas Huth             s->test_flags = TEST_FLAGS_END;
37219d55a19SAlex Bennée         } else {
37319d55a19SAlex Bennée             msg.payload.u64 = s->vu_ops->get_features(s);
3741e8a1faeSThomas Huth         }
37519d55a19SAlex Bennée 
37619d55a19SAlex Bennée         qemu_chr_fe_write_all(chr, (uint8_t *) &msg,
37719d55a19SAlex Bennée                               VHOST_USER_HDR_SIZE + msg.size);
3781e8a1faeSThomas Huth         break;
3791e8a1faeSThomas Huth 
3801e8a1faeSThomas Huth     case VHOST_USER_SET_FEATURES:
381892040dcSDima Stepanov         if (s->vu_ops->set_features) {
382892040dcSDima Stepanov             s->vu_ops->set_features(s, chr, &msg);
3831e8a1faeSThomas Huth         }
3841e8a1faeSThomas Huth         break;
3851e8a1faeSThomas Huth 
38620a4127fSAlex Bennée     case VHOST_USER_SET_OWNER:
38720a4127fSAlex Bennée         /*
38820a4127fSAlex Bennée          * We don't need to do anything here, the remote is just
38920a4127fSAlex Bennée          * letting us know it is in charge. Just log it.
39020a4127fSAlex Bennée          */
39120a4127fSAlex Bennée         qos_printf("set_owner: start of session\n");
39220a4127fSAlex Bennée         break;
39320a4127fSAlex Bennée 
3941e8a1faeSThomas Huth     case VHOST_USER_GET_PROTOCOL_FEATURES:
395892040dcSDima Stepanov         if (s->vu_ops->get_protocol_features) {
396892040dcSDima Stepanov             s->vu_ops->get_protocol_features(s, chr, &msg);
3971e8a1faeSThomas Huth         }
3981e8a1faeSThomas Huth         break;
3991e8a1faeSThomas Huth 
400ff070f60SAlex Bennée     case VHOST_USER_GET_CONFIG:
401ff070f60SAlex Bennée         /*
402ff070f60SAlex Bennée          * Treat GET_CONFIG as a NOP and just reply and let the guest
403ff070f60SAlex Bennée          * consider we have updated its memory. Tests currently don't
404ff070f60SAlex Bennée          * require working configs.
405ff070f60SAlex Bennée          */
406ff070f60SAlex Bennée         msg.flags |= VHOST_USER_REPLY_MASK;
407ff070f60SAlex Bennée         p = (uint8_t *) &msg;
408ff070f60SAlex Bennée         qemu_chr_fe_write_all(chr, p, VHOST_USER_HDR_SIZE + msg.size);
409ff070f60SAlex Bennée         break;
410ff070f60SAlex Bennée 
41120a4127fSAlex Bennée     case VHOST_USER_SET_PROTOCOL_FEATURES:
41220a4127fSAlex Bennée         /*
41320a4127fSAlex Bennée          * We did set VHOST_USER_F_PROTOCOL_FEATURES so its valid for
41420a4127fSAlex Bennée          * the remote end to send this. There is no handshake reply so
41520a4127fSAlex Bennée          * just log the details for debugging.
41620a4127fSAlex Bennée          */
41720a4127fSAlex Bennée         qos_printf("set_protocol_features: 0x%"PRIx64 "\n", msg.payload.u64);
41820a4127fSAlex Bennée         break;
41920a4127fSAlex Bennée 
42020a4127fSAlex Bennée         /*
42120a4127fSAlex Bennée          * A real vhost-user backend would actually set the size and
42220a4127fSAlex Bennée          * address of the vrings but we can simply report them.
42320a4127fSAlex Bennée          */
42420a4127fSAlex Bennée     case VHOST_USER_SET_VRING_NUM:
42520a4127fSAlex Bennée         qos_printf("set_vring_num: %d/%d\n",
42620a4127fSAlex Bennée                    msg.payload.state.index, msg.payload.state.num);
42720a4127fSAlex Bennée         break;
42820a4127fSAlex Bennée     case VHOST_USER_SET_VRING_ADDR:
42920a4127fSAlex Bennée         qos_printf("set_vring_addr: 0x%"PRIx64"/0x%"PRIx64"/0x%"PRIx64"\n",
43020a4127fSAlex Bennée                    msg.payload.addr.avail_user_addr,
43120a4127fSAlex Bennée                    msg.payload.addr.desc_user_addr,
43220a4127fSAlex Bennée                    msg.payload.addr.used_user_addr);
43320a4127fSAlex Bennée         break;
43420a4127fSAlex Bennée 
4351e8a1faeSThomas Huth     case VHOST_USER_GET_VRING_BASE:
4361e8a1faeSThomas Huth         /* send back vring base to qemu */
4371e8a1faeSThomas Huth         msg.flags |= VHOST_USER_REPLY_MASK;
4381e8a1faeSThomas Huth         msg.size = sizeof(m.payload.state);
4391e8a1faeSThomas Huth         msg.payload.state.num = 0;
4401e8a1faeSThomas Huth         p = (uint8_t *) &msg;
4411e8a1faeSThomas Huth         qemu_chr_fe_write_all(chr, p, VHOST_USER_HDR_SIZE + msg.size);
4421e8a1faeSThomas Huth 
4431e8a1faeSThomas Huth         assert(msg.payload.state.index < s->queues * 2);
4441e8a1faeSThomas Huth         s->rings &= ~(0x1ULL << msg.payload.state.index);
4451e8a1faeSThomas Huth         g_cond_broadcast(&s->data_cond);
4461e8a1faeSThomas Huth         break;
4471e8a1faeSThomas Huth 
4481e8a1faeSThomas Huth     case VHOST_USER_SET_MEM_TABLE:
4491e8a1faeSThomas Huth         /* received the mem table */
4501e8a1faeSThomas Huth         memcpy(&s->memory, &msg.payload.memory, sizeof(msg.payload.memory));
4511e8a1faeSThomas Huth         s->fds_num = qemu_chr_fe_get_msgfds(chr, s->fds,
4521e8a1faeSThomas Huth                                             G_N_ELEMENTS(s->fds));
4531e8a1faeSThomas Huth 
4541e8a1faeSThomas Huth         /* signal the test that it can continue */
4551e8a1faeSThomas Huth         g_cond_broadcast(&s->data_cond);
4561e8a1faeSThomas Huth         break;
4571e8a1faeSThomas Huth 
4581e8a1faeSThomas Huth     case VHOST_USER_SET_VRING_KICK:
4591e8a1faeSThomas Huth     case VHOST_USER_SET_VRING_CALL:
4601e8a1faeSThomas Huth         /* consume the fd */
4611e8a1faeSThomas Huth         qemu_chr_fe_get_msgfds(chr, &fd, 1);
4621e8a1faeSThomas Huth         /*
4631e8a1faeSThomas Huth          * This is a non-blocking eventfd.
4641e8a1faeSThomas Huth          * The receive function forces it to be blocking,
4651e8a1faeSThomas Huth          * so revert it back to non-blocking.
4661e8a1faeSThomas Huth          */
467b2670d1fSMarc-André Lureau         g_unix_set_fd_nonblocking(fd, true, &err);
468b2670d1fSMarc-André Lureau         g_assert_no_error(err);
4691e8a1faeSThomas Huth         break;
4701e8a1faeSThomas Huth 
4711e8a1faeSThomas Huth     case VHOST_USER_SET_LOG_BASE:
4721e8a1faeSThomas Huth         if (s->log_fd != -1) {
4731e8a1faeSThomas Huth             close(s->log_fd);
4741e8a1faeSThomas Huth             s->log_fd = -1;
4751e8a1faeSThomas Huth         }
4761e8a1faeSThomas Huth         qemu_chr_fe_get_msgfds(chr, &s->log_fd, 1);
4771e8a1faeSThomas Huth         msg.flags |= VHOST_USER_REPLY_MASK;
4781e8a1faeSThomas Huth         msg.size = 0;
4791e8a1faeSThomas Huth         p = (uint8_t *) &msg;
4801e8a1faeSThomas Huth         qemu_chr_fe_write_all(chr, p, VHOST_USER_HDR_SIZE);
4811e8a1faeSThomas Huth 
4821e8a1faeSThomas Huth         g_cond_broadcast(&s->data_cond);
4831e8a1faeSThomas Huth         break;
4841e8a1faeSThomas Huth 
4851e8a1faeSThomas Huth     case VHOST_USER_SET_VRING_BASE:
4861e8a1faeSThomas Huth         assert(msg.payload.state.index < s->queues * 2);
4871e8a1faeSThomas Huth         s->rings |= 0x1ULL << msg.payload.state.index;
4881e8a1faeSThomas Huth         g_cond_broadcast(&s->data_cond);
4891e8a1faeSThomas Huth         break;
4901e8a1faeSThomas Huth 
4911e8a1faeSThomas Huth     case VHOST_USER_GET_QUEUE_NUM:
4921e8a1faeSThomas Huth         msg.flags |= VHOST_USER_REPLY_MASK;
4931e8a1faeSThomas Huth         msg.size = sizeof(m.payload.u64);
4941e8a1faeSThomas Huth         msg.payload.u64 = s->queues;
4951e8a1faeSThomas Huth         p = (uint8_t *) &msg;
4961e8a1faeSThomas Huth         qemu_chr_fe_write_all(chr, p, VHOST_USER_HDR_SIZE + msg.size);
4971e8a1faeSThomas Huth         break;
4981e8a1faeSThomas Huth 
49920a4127fSAlex Bennée     case VHOST_USER_SET_VRING_ENABLE:
50020a4127fSAlex Bennée         /*
50120a4127fSAlex Bennée          * Another case we ignore as we don't need to respond. With a
50220a4127fSAlex Bennée          * fully functioning vhost-user we would enable/disable the
50320a4127fSAlex Bennée          * vring monitoring.
50420a4127fSAlex Bennée          */
50520a4127fSAlex Bennée         qos_printf("set_vring(%d)=%s\n", msg.payload.state.index,
50620a4127fSAlex Bennée                    msg.payload.state.num ? "enabled" : "disabled");
50720a4127fSAlex Bennée         break;
50820a4127fSAlex Bennée 
5091e8a1faeSThomas Huth     default:
51020a4127fSAlex Bennée         qos_printf("vhost-user: un-handled message: %d\n", msg.request);
5111e8a1faeSThomas Huth         break;
5121e8a1faeSThomas Huth     }
5131e8a1faeSThomas Huth 
5143ee7f21eSPaolo Bonzini out:
5151e8a1faeSThomas Huth     g_mutex_unlock(&s->data_mutex);
5161e8a1faeSThomas Huth }
5171e8a1faeSThomas Huth 
init_hugepagefs(void)5181e8a1faeSThomas Huth static const char *init_hugepagefs(void)
5191e8a1faeSThomas Huth {
5201e8a1faeSThomas Huth #ifdef CONFIG_LINUX
5211e8a1faeSThomas Huth     static const char *hugepagefs;
5221e8a1faeSThomas Huth     const char *path = getenv("QTEST_HUGETLBFS_PATH");
5231e8a1faeSThomas Huth     struct statfs fs;
5241e8a1faeSThomas Huth     int ret;
5251e8a1faeSThomas Huth 
5261e8a1faeSThomas Huth     if (hugepagefs) {
5271e8a1faeSThomas Huth         return hugepagefs;
5281e8a1faeSThomas Huth     }
5291e8a1faeSThomas Huth     if (!path) {
5301e8a1faeSThomas Huth         return NULL;
5311e8a1faeSThomas Huth     }
5321e8a1faeSThomas Huth 
5331e8a1faeSThomas Huth     if (access(path, R_OK | W_OK | X_OK)) {
53430ea13e9SAlex Bennée         qos_printf("access on path (%s): %s", path, strerror(errno));
5351e8a1faeSThomas Huth         g_test_fail();
5361e8a1faeSThomas Huth         return NULL;
5371e8a1faeSThomas Huth     }
5381e8a1faeSThomas Huth 
5391e8a1faeSThomas Huth     do {
5401e8a1faeSThomas Huth         ret = statfs(path, &fs);
5411e8a1faeSThomas Huth     } while (ret != 0 && errno == EINTR);
5421e8a1faeSThomas Huth 
5431e8a1faeSThomas Huth     if (ret != 0) {
54430ea13e9SAlex Bennée         qos_printf("statfs on path (%s): %s", path, strerror(errno));
5451e8a1faeSThomas Huth         g_test_fail();
5461e8a1faeSThomas Huth         return NULL;
5471e8a1faeSThomas Huth     }
5481e8a1faeSThomas Huth 
5491e8a1faeSThomas Huth     if (fs.f_type != HUGETLBFS_MAGIC) {
55030ea13e9SAlex Bennée         qos_printf("Warning: path not on HugeTLBFS: %s", path);
5511e8a1faeSThomas Huth         g_test_fail();
5521e8a1faeSThomas Huth         return NULL;
5531e8a1faeSThomas Huth     }
5541e8a1faeSThomas Huth 
5551e8a1faeSThomas Huth     hugepagefs = path;
5561e8a1faeSThomas Huth     return hugepagefs;
5571e8a1faeSThomas Huth #else
5581e8a1faeSThomas Huth     return NULL;
5591e8a1faeSThomas Huth #endif
5601e8a1faeSThomas Huth }
5611e8a1faeSThomas Huth 
test_server_new(const gchar * name,struct vhost_user_ops * ops)562892040dcSDima Stepanov static TestServer *test_server_new(const gchar *name,
563892040dcSDima Stepanov         struct vhost_user_ops *ops)
5641e8a1faeSThomas Huth {
5651e8a1faeSThomas Huth     TestServer *server = g_new0(TestServer, 1);
566e6efe236SBin Meng     g_autofree const char *tmpfs = NULL;
567e6efe236SBin Meng     GError *err = NULL;
5681e8a1faeSThomas Huth 
5691e8a1faeSThomas Huth     server->context = g_main_context_new();
5701e8a1faeSThomas Huth     server->loop = g_main_loop_new(server->context, FALSE);
5711e8a1faeSThomas Huth 
5721e8a1faeSThomas Huth     /* run the main loop thread so the chardev may operate */
5731e8a1faeSThomas Huth     server->thread = g_thread_new(NULL, thread_function, server->loop);
5741e8a1faeSThomas Huth 
575e6efe236SBin Meng     tmpfs = g_dir_make_tmp("vhost-test-XXXXXX", &err);
5761e8a1faeSThomas Huth     if (!tmpfs) {
5771c324bf9SBin Meng         g_test_message("Can't create temporary directory in %s: %s",
5781c324bf9SBin Meng                        g_get_tmp_dir(), err->message);
579e6efe236SBin Meng         g_error_free(err);
5801e8a1faeSThomas Huth     }
5811e8a1faeSThomas Huth     g_assert(tmpfs);
5821e8a1faeSThomas Huth 
5831e8a1faeSThomas Huth     server->tmpfs = g_strdup(tmpfs);
5841e8a1faeSThomas Huth     server->socket_path = g_strdup_printf("%s/%s.sock", tmpfs, name);
5851e8a1faeSThomas Huth     server->mig_path = g_strdup_printf("%s/%s.mig", tmpfs, name);
5861e8a1faeSThomas Huth     server->chr_name = g_strdup_printf("chr-%s", name);
5871e8a1faeSThomas Huth 
5881e8a1faeSThomas Huth     g_mutex_init(&server->data_mutex);
5891e8a1faeSThomas Huth     g_cond_init(&server->data_cond);
5901e8a1faeSThomas Huth 
5911e8a1faeSThomas Huth     server->log_fd = -1;
5921e8a1faeSThomas Huth     server->queues = 1;
593892040dcSDima Stepanov     server->vu_ops = ops;
5941e8a1faeSThomas Huth 
5951e8a1faeSThomas Huth     return server;
5961e8a1faeSThomas Huth }
5971e8a1faeSThomas Huth 
chr_event(void * opaque,QEMUChrEvent event)598981c9b88SPeter Maydell static void chr_event(void *opaque, QEMUChrEvent event)
5991e8a1faeSThomas Huth {
6001e8a1faeSThomas Huth     TestServer *s = opaque;
6011e8a1faeSThomas Huth 
6021e8a1faeSThomas Huth     if (s->test_flags == TEST_FLAGS_END &&
6031e8a1faeSThomas Huth         event == CHR_EVENT_CLOSED) {
6041e8a1faeSThomas Huth         s->test_flags = TEST_FLAGS_OK;
6051e8a1faeSThomas Huth     }
6061e8a1faeSThomas Huth }
6071e8a1faeSThomas Huth 
test_server_create_chr(TestServer * server,const gchar * opt)6081e8a1faeSThomas Huth static void test_server_create_chr(TestServer *server, const gchar *opt)
6091e8a1faeSThomas Huth {
61055c26982SAlex Bennée     g_autofree gchar *chr_path = g_strdup_printf("unix:%s%s",
61155c26982SAlex Bennée                                                  server->socket_path, opt);
6121e8a1faeSThomas Huth     Chardev *chr;
6131e8a1faeSThomas Huth 
6141e8a1faeSThomas Huth     chr = qemu_chr_new(server->chr_name, chr_path, server->context);
61555c26982SAlex Bennée     g_assert(chr);
6161e8a1faeSThomas Huth 
6171e8a1faeSThomas Huth     qemu_chr_fe_init(&server->chr, chr, &error_abort);
6181e8a1faeSThomas Huth     qemu_chr_fe_set_handlers(&server->chr, chr_can_read, chr_read,
6191e8a1faeSThomas Huth                              chr_event, NULL, server, server->context, true);
6201e8a1faeSThomas Huth }
6211e8a1faeSThomas Huth 
test_server_listen(TestServer * server)6221e8a1faeSThomas Huth static void test_server_listen(TestServer *server)
6231e8a1faeSThomas Huth {
624991c180dSPaolo Bonzini     test_server_create_chr(server, ",server=on,wait=off");
6251e8a1faeSThomas Huth }
6261e8a1faeSThomas Huth 
test_server_free(TestServer * server)6271e8a1faeSThomas Huth static void test_server_free(TestServer *server)
6281e8a1faeSThomas Huth {
6291e8a1faeSThomas Huth     int i, ret;
6301e8a1faeSThomas Huth 
6311e8a1faeSThomas Huth     /* finish the helper thread and dispatch pending sources */
6321e8a1faeSThomas Huth     g_main_loop_quit(server->loop);
6331e8a1faeSThomas Huth     g_thread_join(server->thread);
6341e8a1faeSThomas Huth     while (g_main_context_pending(NULL)) {
6351e8a1faeSThomas Huth         g_main_context_iteration(NULL, TRUE);
6361e8a1faeSThomas Huth     }
6371e8a1faeSThomas Huth 
6381e8a1faeSThomas Huth     unlink(server->socket_path);
6391e8a1faeSThomas Huth     g_free(server->socket_path);
6401e8a1faeSThomas Huth 
6411e8a1faeSThomas Huth     unlink(server->mig_path);
6421e8a1faeSThomas Huth     g_free(server->mig_path);
6431e8a1faeSThomas Huth 
6441e8a1faeSThomas Huth     ret = rmdir(server->tmpfs);
6451e8a1faeSThomas Huth     if (ret != 0) {
6461e8a1faeSThomas Huth         g_test_message("unable to rmdir: path (%s): %s",
6471e8a1faeSThomas Huth                        server->tmpfs, strerror(errno));
6481e8a1faeSThomas Huth     }
6491e8a1faeSThomas Huth     g_free(server->tmpfs);
6501e8a1faeSThomas Huth 
6511e8a1faeSThomas Huth     qemu_chr_fe_deinit(&server->chr, true);
6521e8a1faeSThomas Huth 
6531e8a1faeSThomas Huth     for (i = 0; i < server->fds_num; i++) {
6541e8a1faeSThomas Huth         close(server->fds[i]);
6551e8a1faeSThomas Huth     }
6561e8a1faeSThomas Huth 
6571e8a1faeSThomas Huth     if (server->log_fd != -1) {
6581e8a1faeSThomas Huth         close(server->log_fd);
6591e8a1faeSThomas Huth     }
6601e8a1faeSThomas Huth 
6611e8a1faeSThomas Huth     g_free(server->chr_name);
6621e8a1faeSThomas Huth 
6631e8a1faeSThomas Huth     g_main_loop_unref(server->loop);
6641e8a1faeSThomas Huth     g_main_context_unref(server->context);
6651e8a1faeSThomas Huth     g_cond_clear(&server->data_cond);
6661e8a1faeSThomas Huth     g_mutex_clear(&server->data_mutex);
6671e8a1faeSThomas Huth     g_free(server);
6681e8a1faeSThomas Huth }
6691e8a1faeSThomas Huth 
wait_for_log_fd(TestServer * s)6701e8a1faeSThomas Huth static void wait_for_log_fd(TestServer *s)
6711e8a1faeSThomas Huth {
6721e8a1faeSThomas Huth     gint64 end_time;
6731e8a1faeSThomas Huth 
6741e8a1faeSThomas Huth     g_mutex_lock(&s->data_mutex);
6751e8a1faeSThomas Huth     end_time = g_get_monotonic_time() + 5 * G_TIME_SPAN_SECOND;
6761e8a1faeSThomas Huth     while (s->log_fd == -1) {
6771e8a1faeSThomas Huth         if (!g_cond_wait_until(&s->data_cond, &s->data_mutex, end_time)) {
6781e8a1faeSThomas Huth             /* timeout has passed */
6791e8a1faeSThomas Huth             g_assert(s->log_fd != -1);
6801e8a1faeSThomas Huth             break;
6811e8a1faeSThomas Huth         }
6821e8a1faeSThomas Huth     }
6831e8a1faeSThomas Huth 
6841e8a1faeSThomas Huth     g_mutex_unlock(&s->data_mutex);
6851e8a1faeSThomas Huth }
6861e8a1faeSThomas Huth 
write_guest_mem(TestServer * s,uint32_t seed)6871e8a1faeSThomas Huth static void write_guest_mem(TestServer *s, uint32_t seed)
6881e8a1faeSThomas Huth {
6891e8a1faeSThomas Huth     uint32_t *guest_mem;
6901e8a1faeSThomas Huth     int i, j;
6911e8a1faeSThomas Huth     size_t size;
6921e8a1faeSThomas Huth 
6931e8a1faeSThomas Huth     /* iterate all regions */
6941e8a1faeSThomas Huth     for (i = 0; i < s->fds_num; i++) {
6951e8a1faeSThomas Huth 
6961e8a1faeSThomas Huth         /* We'll write only the region statring at 0x0 */
6971e8a1faeSThomas Huth         if (s->memory.regions[i].guest_phys_addr != 0x0) {
6981e8a1faeSThomas Huth             continue;
6991e8a1faeSThomas Huth         }
7001e8a1faeSThomas Huth 
7011e8a1faeSThomas Huth         g_assert_cmpint(s->memory.regions[i].memory_size, >, 1024);
7021e8a1faeSThomas Huth 
7031e8a1faeSThomas Huth         size = s->memory.regions[i].memory_size +
7041e8a1faeSThomas Huth             s->memory.regions[i].mmap_offset;
7051e8a1faeSThomas Huth 
7061e8a1faeSThomas Huth         guest_mem = mmap(0, size, PROT_READ | PROT_WRITE,
7071e8a1faeSThomas Huth                          MAP_SHARED, s->fds[i], 0);
7081e8a1faeSThomas Huth 
7091e8a1faeSThomas Huth         g_assert(guest_mem != MAP_FAILED);
7101e8a1faeSThomas Huth         guest_mem += (s->memory.regions[i].mmap_offset / sizeof(*guest_mem));
7111e8a1faeSThomas Huth 
7121e8a1faeSThomas Huth         for (j = 0; j < 256; j++) {
7131e8a1faeSThomas Huth             guest_mem[j] = seed + j;
7141e8a1faeSThomas Huth         }
7151e8a1faeSThomas Huth 
7161e8a1faeSThomas Huth         munmap(guest_mem, s->memory.regions[i].memory_size);
7171e8a1faeSThomas Huth         break;
7181e8a1faeSThomas Huth     }
7191e8a1faeSThomas Huth }
7201e8a1faeSThomas Huth 
get_log_size(TestServer * s)7211e8a1faeSThomas Huth static guint64 get_log_size(TestServer *s)
7221e8a1faeSThomas Huth {
7231e8a1faeSThomas Huth     guint64 log_size = 0;
7241e8a1faeSThomas Huth     int i;
7251e8a1faeSThomas Huth 
7261e8a1faeSThomas Huth     for (i = 0; i < s->memory.nregions; ++i) {
7271e8a1faeSThomas Huth         VhostUserMemoryRegion *reg = &s->memory.regions[i];
7281e8a1faeSThomas Huth         guint64 last = range_get_last(reg->guest_phys_addr,
7291e8a1faeSThomas Huth                                        reg->memory_size);
7301e8a1faeSThomas Huth         log_size = MAX(log_size, last / (8 * VHOST_LOG_PAGE) + 1);
7311e8a1faeSThomas Huth     }
7321e8a1faeSThomas Huth 
7331e8a1faeSThomas Huth     return log_size;
7341e8a1faeSThomas Huth }
7351e8a1faeSThomas Huth 
7361e8a1faeSThomas Huth typedef struct TestMigrateSource {
7371e8a1faeSThomas Huth     GSource source;
7381e8a1faeSThomas Huth     TestServer *src;
7391e8a1faeSThomas Huth     TestServer *dest;
7401e8a1faeSThomas Huth } TestMigrateSource;
7411e8a1faeSThomas Huth 
7421e8a1faeSThomas Huth static gboolean
test_migrate_source_check(GSource * source)7431e8a1faeSThomas Huth test_migrate_source_check(GSource *source)
7441e8a1faeSThomas Huth {
7451e8a1faeSThomas Huth     TestMigrateSource *t = (TestMigrateSource *)source;
7461e8a1faeSThomas Huth     gboolean overlap = t->src->rings && t->dest->rings;
7471e8a1faeSThomas Huth 
7481e8a1faeSThomas Huth     g_assert(!overlap);
7491e8a1faeSThomas Huth 
7501e8a1faeSThomas Huth     return FALSE;
7511e8a1faeSThomas Huth }
7521e8a1faeSThomas Huth 
7531e8a1faeSThomas Huth GSourceFuncs test_migrate_source_funcs = {
7541e8a1faeSThomas Huth     .check = test_migrate_source_check,
7551e8a1faeSThomas Huth };
7561e8a1faeSThomas Huth 
vhost_user_test_cleanup(void * s)7571e8a1faeSThomas Huth static void vhost_user_test_cleanup(void *s)
7581e8a1faeSThomas Huth {
7591e8a1faeSThomas Huth     TestServer *server = s;
7601e8a1faeSThomas Huth 
7611e8a1faeSThomas Huth     qos_invalidate_command_line();
7621e8a1faeSThomas Huth     test_server_free(server);
7631e8a1faeSThomas Huth }
7641e8a1faeSThomas Huth 
vhost_user_test_setup(GString * cmd_line,void * arg)7651e8a1faeSThomas Huth static void *vhost_user_test_setup(GString *cmd_line, void *arg)
7661e8a1faeSThomas Huth {
767892040dcSDima Stepanov     TestServer *server = test_server_new("vhost-user-test", arg);
7681e8a1faeSThomas Huth     test_server_listen(server);
7691e8a1faeSThomas Huth 
7701e8a1faeSThomas Huth     append_mem_opts(server, cmd_line, 256, TEST_MEMFD_AUTO);
771892040dcSDima Stepanov     server->vu_ops->append_opts(server, cmd_line, "");
7721e8a1faeSThomas Huth 
7731e8a1faeSThomas Huth     g_test_queue_destroy(vhost_user_test_cleanup, server);
7741e8a1faeSThomas Huth 
7751e8a1faeSThomas Huth     return server;
7761e8a1faeSThomas Huth }
7771e8a1faeSThomas Huth 
vhost_user_test_setup_memfd(GString * cmd_line,void * arg)7781e8a1faeSThomas Huth static void *vhost_user_test_setup_memfd(GString *cmd_line, void *arg)
7791e8a1faeSThomas Huth {
780892040dcSDima Stepanov     TestServer *server = test_server_new("vhost-user-test", arg);
7811e8a1faeSThomas Huth     test_server_listen(server);
7821e8a1faeSThomas Huth 
7831e8a1faeSThomas Huth     append_mem_opts(server, cmd_line, 256, TEST_MEMFD_YES);
784892040dcSDima Stepanov     server->vu_ops->append_opts(server, cmd_line, "");
7851e8a1faeSThomas Huth 
7861e8a1faeSThomas Huth     g_test_queue_destroy(vhost_user_test_cleanup, server);
7871e8a1faeSThomas Huth 
7881e8a1faeSThomas Huth     return server;
7891e8a1faeSThomas Huth }
7901e8a1faeSThomas Huth 
test_read_guest_mem(void * obj,void * arg,QGuestAllocator * alloc)7911e8a1faeSThomas Huth static void test_read_guest_mem(void *obj, void *arg, QGuestAllocator *alloc)
7921e8a1faeSThomas Huth {
7931e8a1faeSThomas Huth     TestServer *server = arg;
7941e8a1faeSThomas Huth 
7951e8a1faeSThomas Huth     if (!wait_for_fds(server)) {
7961e8a1faeSThomas Huth         return;
7971e8a1faeSThomas Huth     }
7981e8a1faeSThomas Huth 
7991e8a1faeSThomas Huth     read_guest_mem_server(global_qtest, server);
8001e8a1faeSThomas Huth }
8011e8a1faeSThomas Huth 
test_migrate(void * obj,void * arg,QGuestAllocator * alloc)8021e8a1faeSThomas Huth static void test_migrate(void *obj, void *arg, QGuestAllocator *alloc)
8031e8a1faeSThomas Huth {
8041e8a1faeSThomas Huth     TestServer *s = arg;
80599fd3178SThomas Huth     TestServer *dest;
80699fd3178SThomas Huth     GString *dest_cmdline;
80799fd3178SThomas Huth     char *uri;
8081e8a1faeSThomas Huth     QTestState *to;
8091e8a1faeSThomas Huth     GSource *source;
8101e8a1faeSThomas Huth     QDict *rsp;
8111e8a1faeSThomas Huth     guint8 *log;
8121e8a1faeSThomas Huth     guint64 size;
8131e8a1faeSThomas Huth 
8141e8a1faeSThomas Huth     if (!wait_for_fds(s)) {
8151e8a1faeSThomas Huth         return;
8161e8a1faeSThomas Huth     }
8171e8a1faeSThomas Huth 
818892040dcSDima Stepanov     dest = test_server_new("dest", s->vu_ops);
81999fd3178SThomas Huth     dest_cmdline = g_string_new(qos_get_current_command_line());
82099fd3178SThomas Huth     uri = g_strdup_printf("%s%s", "unix:", dest->mig_path);
82199fd3178SThomas Huth 
8221e8a1faeSThomas Huth     size = get_log_size(s);
8231e8a1faeSThomas Huth     g_assert_cmpint(size, ==, (256 * 1024 * 1024) / (VHOST_LOG_PAGE * 8));
8241e8a1faeSThomas Huth 
8251e8a1faeSThomas Huth     test_server_listen(dest);
8261e8a1faeSThomas Huth     g_string_append_printf(dest_cmdline, " -incoming %s", uri);
8271e8a1faeSThomas Huth     append_mem_opts(dest, dest_cmdline, 256, TEST_MEMFD_AUTO);
828892040dcSDima Stepanov     dest->vu_ops->append_opts(dest, dest_cmdline, "");
8291e8a1faeSThomas Huth     to = qtest_init(dest_cmdline->str);
8301e8a1faeSThomas Huth 
8311e8a1faeSThomas Huth     /* This would be where you call qos_allocate_objects(to, NULL), if you want
8321e8a1faeSThomas Huth      * to talk to the QVirtioNet object on the destination.
8331e8a1faeSThomas Huth      */
8341e8a1faeSThomas Huth 
8351e8a1faeSThomas Huth     source = g_source_new(&test_migrate_source_funcs,
8361e8a1faeSThomas Huth                           sizeof(TestMigrateSource));
8371e8a1faeSThomas Huth     ((TestMigrateSource *)source)->src = s;
8381e8a1faeSThomas Huth     ((TestMigrateSource *)source)->dest = dest;
8391e8a1faeSThomas Huth     g_source_attach(source, s->context);
8401e8a1faeSThomas Huth 
8411e8a1faeSThomas Huth     /* slow down migration to have time to fiddle with log */
8421e8a1faeSThomas Huth     /* TODO: qtest could learn to break on some places */
843cbde7be9SDaniel P. Berrangé     rsp = qmp("{ 'execute': 'migrate-set-parameters',"
844cbde7be9SDaniel P. Berrangé               "'arguments': { 'max-bandwidth': 10 } }");
8451e8a1faeSThomas Huth     g_assert(qdict_haskey(rsp, "return"));
8461e8a1faeSThomas Huth     qobject_unref(rsp);
8471e8a1faeSThomas Huth 
8481e8a1faeSThomas Huth     rsp = qmp("{ 'execute': 'migrate', 'arguments': { 'uri': %s } }", uri);
8491e8a1faeSThomas Huth     g_assert(qdict_haskey(rsp, "return"));
8501e8a1faeSThomas Huth     qobject_unref(rsp);
8511e8a1faeSThomas Huth 
8521e8a1faeSThomas Huth     wait_for_log_fd(s);
8531e8a1faeSThomas Huth 
8541e8a1faeSThomas Huth     log = mmap(0, size, PROT_READ | PROT_WRITE, MAP_SHARED, s->log_fd, 0);
8551e8a1faeSThomas Huth     g_assert(log != MAP_FAILED);
8561e8a1faeSThomas Huth 
8571e8a1faeSThomas Huth     /* modify first page */
8581e8a1faeSThomas Huth     write_guest_mem(s, 0x42);
8591e8a1faeSThomas Huth     log[0] = 1;
8601e8a1faeSThomas Huth     munmap(log, size);
8611e8a1faeSThomas Huth 
8621e8a1faeSThomas Huth     /* speed things up */
863cbde7be9SDaniel P. Berrangé     rsp = qmp("{ 'execute': 'migrate-set-parameters',"
864cbde7be9SDaniel P. Berrangé               "'arguments': { 'max-bandwidth': 0 } }");
8651e8a1faeSThomas Huth     g_assert(qdict_haskey(rsp, "return"));
8661e8a1faeSThomas Huth     qobject_unref(rsp);
8671e8a1faeSThomas Huth 
8681e8a1faeSThomas Huth     qmp_eventwait("STOP");
8691e8a1faeSThomas Huth     qtest_qmp_eventwait(to, "RESUME");
8701e8a1faeSThomas Huth 
8711e8a1faeSThomas Huth     g_assert(wait_for_fds(dest));
8721e8a1faeSThomas Huth     read_guest_mem_server(to, dest);
8731e8a1faeSThomas Huth 
8741e8a1faeSThomas Huth     g_source_destroy(source);
8751e8a1faeSThomas Huth     g_source_unref(source);
8761e8a1faeSThomas Huth 
8771e8a1faeSThomas Huth     qtest_quit(to);
8781e8a1faeSThomas Huth     test_server_free(dest);
8791e8a1faeSThomas Huth     g_free(uri);
88099fd3178SThomas Huth     g_string_free(dest_cmdline, true);
8811e8a1faeSThomas Huth }
8821e8a1faeSThomas Huth 
wait_for_rings_started(TestServer * s,size_t count)8831e8a1faeSThomas Huth static void wait_for_rings_started(TestServer *s, size_t count)
8841e8a1faeSThomas Huth {
8851e8a1faeSThomas Huth     gint64 end_time;
8861e8a1faeSThomas Huth 
8871e8a1faeSThomas Huth     g_mutex_lock(&s->data_mutex);
8881e8a1faeSThomas Huth     end_time = g_get_monotonic_time() + 5 * G_TIME_SPAN_SECOND;
8891e8a1faeSThomas Huth     while (ctpop64(s->rings) != count) {
8901e8a1faeSThomas Huth         if (!g_cond_wait_until(&s->data_cond, &s->data_mutex, end_time)) {
8911e8a1faeSThomas Huth             /* timeout has passed */
8921e8a1faeSThomas Huth             g_assert_cmpint(ctpop64(s->rings), ==, count);
8931e8a1faeSThomas Huth             break;
8941e8a1faeSThomas Huth         }
8951e8a1faeSThomas Huth     }
8961e8a1faeSThomas Huth 
8971e8a1faeSThomas Huth     g_mutex_unlock(&s->data_mutex);
8981e8a1faeSThomas Huth }
8991e8a1faeSThomas Huth 
test_server_connect(TestServer * server)9001e8a1faeSThomas Huth static inline void test_server_connect(TestServer *server)
9011e8a1faeSThomas Huth {
9021e8a1faeSThomas Huth     test_server_create_chr(server, ",reconnect=1");
9031e8a1faeSThomas Huth }
9041e8a1faeSThomas Huth 
9051e8a1faeSThomas Huth static gboolean
reconnect_cb(gpointer user_data)9061e8a1faeSThomas Huth reconnect_cb(gpointer user_data)
9071e8a1faeSThomas Huth {
9081e8a1faeSThomas Huth     TestServer *s = user_data;
9091e8a1faeSThomas Huth 
9101e8a1faeSThomas Huth     qemu_chr_fe_disconnect(&s->chr);
9111e8a1faeSThomas Huth 
9121e8a1faeSThomas Huth     return FALSE;
9131e8a1faeSThomas Huth }
9141e8a1faeSThomas Huth 
9151e8a1faeSThomas Huth static gpointer
connect_thread(gpointer data)9161e8a1faeSThomas Huth connect_thread(gpointer data)
9171e8a1faeSThomas Huth {
9181e8a1faeSThomas Huth     TestServer *s = data;
9191e8a1faeSThomas Huth 
9201e8a1faeSThomas Huth     /* wait for qemu to start before first try, to avoid extra warnings */
9211e8a1faeSThomas Huth     g_usleep(G_USEC_PER_SEC);
9221e8a1faeSThomas Huth     test_server_connect(s);
9231e8a1faeSThomas Huth 
9241e8a1faeSThomas Huth     return NULL;
9251e8a1faeSThomas Huth }
9261e8a1faeSThomas Huth 
vhost_user_test_setup_reconnect(GString * cmd_line,void * arg)9271e8a1faeSThomas Huth static void *vhost_user_test_setup_reconnect(GString *cmd_line, void *arg)
9281e8a1faeSThomas Huth {
929892040dcSDima Stepanov     TestServer *s = test_server_new("reconnect", arg);
9301e8a1faeSThomas Huth 
9311e8a1faeSThomas Huth     g_thread_new("connect", connect_thread, s);
9321e8a1faeSThomas Huth     append_mem_opts(s, cmd_line, 256, TEST_MEMFD_AUTO);
933991c180dSPaolo Bonzini     s->vu_ops->append_opts(s, cmd_line, ",server=on");
9341e8a1faeSThomas Huth 
9351e8a1faeSThomas Huth     g_test_queue_destroy(vhost_user_test_cleanup, s);
9361e8a1faeSThomas Huth 
9371e8a1faeSThomas Huth     return s;
9381e8a1faeSThomas Huth }
9391e8a1faeSThomas Huth 
test_reconnect(void * obj,void * arg,QGuestAllocator * alloc)9401e8a1faeSThomas Huth static void test_reconnect(void *obj, void *arg, QGuestAllocator *alloc)
9411e8a1faeSThomas Huth {
9421e8a1faeSThomas Huth     TestServer *s = arg;
9431e8a1faeSThomas Huth     GSource *src;
9441e8a1faeSThomas Huth 
9451e8a1faeSThomas Huth     if (!wait_for_fds(s)) {
9461e8a1faeSThomas Huth         return;
9471e8a1faeSThomas Huth     }
9481e8a1faeSThomas Huth 
9491e8a1faeSThomas Huth     wait_for_rings_started(s, 2);
9501e8a1faeSThomas Huth 
9511e8a1faeSThomas Huth     /* reconnect */
9521e8a1faeSThomas Huth     s->fds_num = 0;
9531e8a1faeSThomas Huth     s->rings = 0;
9541e8a1faeSThomas Huth     src = g_idle_source_new();
9551e8a1faeSThomas Huth     g_source_set_callback(src, reconnect_cb, s, NULL);
9561e8a1faeSThomas Huth     g_source_attach(src, s->context);
9571e8a1faeSThomas Huth     g_source_unref(src);
9581e8a1faeSThomas Huth     g_assert(wait_for_fds(s));
9591e8a1faeSThomas Huth     wait_for_rings_started(s, 2);
9601e8a1faeSThomas Huth }
9611e8a1faeSThomas Huth 
vhost_user_test_setup_connect_fail(GString * cmd_line,void * arg)9621e8a1faeSThomas Huth static void *vhost_user_test_setup_connect_fail(GString *cmd_line, void *arg)
9631e8a1faeSThomas Huth {
964892040dcSDima Stepanov     TestServer *s = test_server_new("connect-fail", arg);
9651e8a1faeSThomas Huth 
9661e8a1faeSThomas Huth     s->test_fail = true;
9671e8a1faeSThomas Huth 
9681e8a1faeSThomas Huth     g_thread_new("connect", connect_thread, s);
9691e8a1faeSThomas Huth     append_mem_opts(s, cmd_line, 256, TEST_MEMFD_AUTO);
970991c180dSPaolo Bonzini     s->vu_ops->append_opts(s, cmd_line, ",server=on");
9711e8a1faeSThomas Huth 
9721e8a1faeSThomas Huth     g_test_queue_destroy(vhost_user_test_cleanup, s);
9731e8a1faeSThomas Huth 
9741e8a1faeSThomas Huth     return s;
9751e8a1faeSThomas Huth }
9761e8a1faeSThomas Huth 
vhost_user_test_setup_flags_mismatch(GString * cmd_line,void * arg)9771e8a1faeSThomas Huth static void *vhost_user_test_setup_flags_mismatch(GString *cmd_line, void *arg)
9781e8a1faeSThomas Huth {
979892040dcSDima Stepanov     TestServer *s = test_server_new("flags-mismatch", arg);
9801e8a1faeSThomas Huth 
9811e8a1faeSThomas Huth     s->test_flags = TEST_FLAGS_DISCONNECT;
9821e8a1faeSThomas Huth 
9831e8a1faeSThomas Huth     g_thread_new("connect", connect_thread, s);
9841e8a1faeSThomas Huth     append_mem_opts(s, cmd_line, 256, TEST_MEMFD_AUTO);
985991c180dSPaolo Bonzini     s->vu_ops->append_opts(s, cmd_line, ",server=on");
9861e8a1faeSThomas Huth 
9871e8a1faeSThomas Huth     g_test_queue_destroy(vhost_user_test_cleanup, s);
9881e8a1faeSThomas Huth 
9891e8a1faeSThomas Huth     return s;
9901e8a1faeSThomas Huth }
9911e8a1faeSThomas Huth 
test_vhost_user_started(void * obj,void * arg,QGuestAllocator * alloc)9921e8a1faeSThomas Huth static void test_vhost_user_started(void *obj, void *arg, QGuestAllocator *alloc)
9931e8a1faeSThomas Huth {
9941e8a1faeSThomas Huth     TestServer *s = arg;
9951e8a1faeSThomas Huth 
9961e8a1faeSThomas Huth     if (!wait_for_fds(s)) {
9971e8a1faeSThomas Huth         return;
9981e8a1faeSThomas Huth     }
9991e8a1faeSThomas Huth     wait_for_rings_started(s, 2);
10001e8a1faeSThomas Huth }
10011e8a1faeSThomas Huth 
vhost_user_test_setup_multiqueue(GString * cmd_line,void * arg)10021e8a1faeSThomas Huth static void *vhost_user_test_setup_multiqueue(GString *cmd_line, void *arg)
10031e8a1faeSThomas Huth {
10041e8a1faeSThomas Huth     TestServer *s = vhost_user_test_setup(cmd_line, arg);
10051e8a1faeSThomas Huth 
10061e8a1faeSThomas Huth     s->queues = 2;
10071e8a1faeSThomas Huth     g_string_append_printf(cmd_line,
10081e8a1faeSThomas Huth                            " -set netdev.hs0.queues=%d"
10091e8a1faeSThomas Huth                            " -global virtio-net-pci.vectors=%d",
10101e8a1faeSThomas Huth                            s->queues, s->queues * 2 + 2);
10111e8a1faeSThomas Huth 
10121e8a1faeSThomas Huth     return s;
10131e8a1faeSThomas Huth }
10141e8a1faeSThomas Huth 
test_multiqueue(void * obj,void * arg,QGuestAllocator * alloc)10151e8a1faeSThomas Huth static void test_multiqueue(void *obj, void *arg, QGuestAllocator *alloc)
10161e8a1faeSThomas Huth {
10171e8a1faeSThomas Huth     TestServer *s = arg;
10181e8a1faeSThomas Huth 
10191e8a1faeSThomas Huth     wait_for_rings_started(s, s->queues * 2);
10201e8a1faeSThomas Huth }
10211e8a1faeSThomas Huth 
102219d55a19SAlex Bennée 
vu_net_get_features(TestServer * s)102319d55a19SAlex Bennée static uint64_t vu_net_get_features(TestServer *s)
102419d55a19SAlex Bennée {
102519d55a19SAlex Bennée     uint64_t features = 0x1ULL << VHOST_F_LOG_ALL |
102619d55a19SAlex Bennée         0x1ULL << VHOST_USER_F_PROTOCOL_FEATURES;
102719d55a19SAlex Bennée 
102819d55a19SAlex Bennée     if (s->queues > 1) {
102919d55a19SAlex Bennée         features |= 0x1ULL << VIRTIO_NET_F_MQ;
103019d55a19SAlex Bennée     }
103119d55a19SAlex Bennée 
103219d55a19SAlex Bennée     return features;
103319d55a19SAlex Bennée }
103419d55a19SAlex Bennée 
vu_net_set_features(TestServer * s,CharBackend * chr,VhostUserMsg * msg)1035892040dcSDima Stepanov static void vu_net_set_features(TestServer *s, CharBackend *chr,
1036892040dcSDima Stepanov                                 VhostUserMsg *msg)
1037892040dcSDima Stepanov {
1038f48d994fSAlex Bennée     g_assert(msg->payload.u64 & (0x1ULL << VHOST_USER_F_PROTOCOL_FEATURES));
1039892040dcSDima Stepanov     if (s->test_flags == TEST_FLAGS_DISCONNECT) {
1040892040dcSDima Stepanov         qemu_chr_fe_disconnect(chr);
1041892040dcSDima Stepanov         s->test_flags = TEST_FLAGS_BAD;
1042892040dcSDima Stepanov     }
1043892040dcSDima Stepanov }
1044892040dcSDima Stepanov 
vu_net_get_protocol_features(TestServer * s,CharBackend * chr,VhostUserMsg * msg)1045892040dcSDima Stepanov static void vu_net_get_protocol_features(TestServer *s, CharBackend *chr,
1046892040dcSDima Stepanov         VhostUserMsg *msg)
1047892040dcSDima Stepanov {
1048892040dcSDima Stepanov     /* send back features to qemu */
1049892040dcSDima Stepanov     msg->flags |= VHOST_USER_REPLY_MASK;
1050892040dcSDima Stepanov     msg->size = sizeof(m.payload.u64);
1051892040dcSDima Stepanov     msg->payload.u64 = 1 << VHOST_USER_PROTOCOL_F_LOG_SHMFD;
1052892040dcSDima Stepanov     msg->payload.u64 |= 1 << VHOST_USER_PROTOCOL_F_CROSS_ENDIAN;
1053892040dcSDima Stepanov     if (s->queues > 1) {
1054892040dcSDima Stepanov         msg->payload.u64 |= 1 << VHOST_USER_PROTOCOL_F_MQ;
1055892040dcSDima Stepanov     }
1056892040dcSDima Stepanov     qemu_chr_fe_write_all(chr, (uint8_t *)msg, VHOST_USER_HDR_SIZE + msg->size);
1057892040dcSDima Stepanov }
1058892040dcSDima Stepanov 
1059892040dcSDima Stepanov /* Each VHOST-USER device should have its ops structure defined. */
1060892040dcSDima Stepanov static struct vhost_user_ops g_vu_net_ops = {
1061892040dcSDima Stepanov     .type = VHOST_USER_NET,
1062892040dcSDima Stepanov 
1063892040dcSDima Stepanov     .append_opts = append_vhost_net_opts,
1064892040dcSDima Stepanov 
106519d55a19SAlex Bennée     .get_features = vu_net_get_features,
1066892040dcSDima Stepanov     .set_features = vu_net_set_features,
1067892040dcSDima Stepanov     .get_protocol_features = vu_net_get_protocol_features,
1068892040dcSDima Stepanov };
1069892040dcSDima Stepanov 
register_vhost_user_test(void)10701e8a1faeSThomas Huth static void register_vhost_user_test(void)
10711e8a1faeSThomas Huth {
10721e8a1faeSThomas Huth     QOSGraphTestOptions opts = {
10731e8a1faeSThomas Huth         .before = vhost_user_test_setup,
10741e8a1faeSThomas Huth         .subprocess = true,
1075892040dcSDima Stepanov         .arg = &g_vu_net_ops,
10761e8a1faeSThomas Huth     };
10771e8a1faeSThomas Huth 
10781e8a1faeSThomas Huth     qemu_add_opts(&qemu_chardev_opts);
10791e8a1faeSThomas Huth 
10801e8a1faeSThomas Huth     qos_add_test("vhost-user/read-guest-mem/memfile",
10811e8a1faeSThomas Huth                  "virtio-net",
10821e8a1faeSThomas Huth                  test_read_guest_mem, &opts);
10831e8a1faeSThomas Huth 
10841e8a1faeSThomas Huth     if (qemu_memfd_check(MFD_ALLOW_SEALING)) {
10851e8a1faeSThomas Huth         opts.before = vhost_user_test_setup_memfd;
10861e8a1faeSThomas Huth         qos_add_test("vhost-user/read-guest-mem/memfd",
10871e8a1faeSThomas Huth                      "virtio-net",
10881e8a1faeSThomas Huth                      test_read_guest_mem, &opts);
10891e8a1faeSThomas Huth     }
10901e8a1faeSThomas Huth 
10911e8a1faeSThomas Huth     qos_add_test("vhost-user/migrate",
10921e8a1faeSThomas Huth                  "virtio-net",
10931e8a1faeSThomas Huth                  test_migrate, &opts);
10941e8a1faeSThomas Huth 
10951e8a1faeSThomas Huth     opts.before = vhost_user_test_setup_reconnect;
10961e8a1faeSThomas Huth     qos_add_test("vhost-user/reconnect", "virtio-net",
10971e8a1faeSThomas Huth                  test_reconnect, &opts);
10981e8a1faeSThomas Huth 
10991e8a1faeSThomas Huth     opts.before = vhost_user_test_setup_connect_fail;
11001e8a1faeSThomas Huth     qos_add_test("vhost-user/connect-fail", "virtio-net",
11011e8a1faeSThomas Huth                  test_vhost_user_started, &opts);
11021e8a1faeSThomas Huth 
11031e8a1faeSThomas Huth     opts.before = vhost_user_test_setup_flags_mismatch;
11041e8a1faeSThomas Huth     qos_add_test("vhost-user/flags-mismatch", "virtio-net",
11051e8a1faeSThomas Huth                  test_vhost_user_started, &opts);
11061e8a1faeSThomas Huth 
11071e8a1faeSThomas Huth     opts.before = vhost_user_test_setup_multiqueue;
11081e8a1faeSThomas Huth     opts.edge.extra_device_opts = "mq=on";
11091e8a1faeSThomas Huth     qos_add_test("vhost-user/multiqueue",
11101e8a1faeSThomas Huth                  "virtio-net",
11111e8a1faeSThomas Huth                  test_multiqueue, &opts);
11121e8a1faeSThomas Huth }
11131e8a1faeSThomas Huth libqos_init(register_vhost_user_test);
11148fcfc823SAlex Bennée 
vu_gpio_get_features(TestServer * s)11158fcfc823SAlex Bennée static uint64_t vu_gpio_get_features(TestServer *s)
11168fcfc823SAlex Bennée {
11178fcfc823SAlex Bennée     return 0x1ULL << VIRTIO_F_VERSION_1 |
11188fcfc823SAlex Bennée         0x1ULL << VIRTIO_GPIO_F_IRQ |
11198fcfc823SAlex Bennée         0x1ULL << VHOST_USER_F_PROTOCOL_FEATURES;
11208fcfc823SAlex Bennée }
11218fcfc823SAlex Bennée 
11228fcfc823SAlex Bennée /*
11238fcfc823SAlex Bennée  * This stub can't handle all the message types but we should reply
11248fcfc823SAlex Bennée  * that we support VHOST_USER_PROTOCOL_F_CONFIG as gpio would use it
11258fcfc823SAlex Bennée  * talking to a read vhost-user daemon.
11268fcfc823SAlex Bennée  */
vu_gpio_get_protocol_features(TestServer * s,CharBackend * chr,VhostUserMsg * msg)11278fcfc823SAlex Bennée static void vu_gpio_get_protocol_features(TestServer *s, CharBackend *chr,
11288fcfc823SAlex Bennée                                           VhostUserMsg *msg)
11298fcfc823SAlex Bennée {
11308fcfc823SAlex Bennée     /* send back features to qemu */
11318fcfc823SAlex Bennée     msg->flags |= VHOST_USER_REPLY_MASK;
11328fcfc823SAlex Bennée     msg->size = sizeof(m.payload.u64);
11338fcfc823SAlex Bennée     msg->payload.u64 = 1ULL << VHOST_USER_PROTOCOL_F_CONFIG;
11348fcfc823SAlex Bennée 
11358fcfc823SAlex Bennée     qemu_chr_fe_write_all(chr, (uint8_t *)msg, VHOST_USER_HDR_SIZE + msg->size);
11368fcfc823SAlex Bennée }
11378fcfc823SAlex Bennée 
11388fcfc823SAlex Bennée static struct vhost_user_ops g_vu_gpio_ops = {
11398fcfc823SAlex Bennée     .type = VHOST_USER_GPIO,
11408fcfc823SAlex Bennée 
11418fcfc823SAlex Bennée     .append_opts = append_vhost_gpio_opts,
11428fcfc823SAlex Bennée 
11438fcfc823SAlex Bennée     .get_features = vu_gpio_get_features,
11448fcfc823SAlex Bennée     .set_features = vu_net_set_features,
11458fcfc823SAlex Bennée     .get_protocol_features = vu_gpio_get_protocol_features,
11468fcfc823SAlex Bennée };
11478fcfc823SAlex Bennée 
register_vhost_gpio_test(void)11488fcfc823SAlex Bennée static void register_vhost_gpio_test(void)
11498fcfc823SAlex Bennée {
11508fcfc823SAlex Bennée     QOSGraphTestOptions opts = {
11518fcfc823SAlex Bennée         .before = vhost_user_test_setup,
11528fcfc823SAlex Bennée         .subprocess = true,
11538fcfc823SAlex Bennée         .arg = &g_vu_gpio_ops,
11548fcfc823SAlex Bennée     };
11558fcfc823SAlex Bennée 
11568fcfc823SAlex Bennée     qemu_add_opts(&qemu_chardev_opts);
11578fcfc823SAlex Bennée 
11588fcfc823SAlex Bennée     qos_add_test("read-guest-mem/memfile",
11598fcfc823SAlex Bennée                  "vhost-user-gpio", test_read_guest_mem, &opts);
11608fcfc823SAlex Bennée }
11618fcfc823SAlex Bennée libqos_init(register_vhost_gpio_test);
1162*b6f53ae0SMilan Zamazal 
vu_scmi_get_features(TestServer * s)1163*b6f53ae0SMilan Zamazal static uint64_t vu_scmi_get_features(TestServer *s)
1164*b6f53ae0SMilan Zamazal {
1165*b6f53ae0SMilan Zamazal     return 0x1ULL << VIRTIO_F_VERSION_1 |
1166*b6f53ae0SMilan Zamazal         0x1ULL << VIRTIO_SCMI_F_P2A_CHANNELS |
1167*b6f53ae0SMilan Zamazal         0x1ULL << VHOST_USER_F_PROTOCOL_FEATURES;
1168*b6f53ae0SMilan Zamazal }
1169*b6f53ae0SMilan Zamazal 
vu_scmi_get_protocol_features(TestServer * s,CharBackend * chr,VhostUserMsg * msg)1170*b6f53ae0SMilan Zamazal static void vu_scmi_get_protocol_features(TestServer *s, CharBackend *chr,
1171*b6f53ae0SMilan Zamazal                                           VhostUserMsg *msg)
1172*b6f53ae0SMilan Zamazal {
1173*b6f53ae0SMilan Zamazal     msg->flags |= VHOST_USER_REPLY_MASK;
1174*b6f53ae0SMilan Zamazal     msg->size = sizeof(m.payload.u64);
1175*b6f53ae0SMilan Zamazal     msg->payload.u64 = 1ULL << VHOST_USER_PROTOCOL_F_MQ;
1176*b6f53ae0SMilan Zamazal 
1177*b6f53ae0SMilan Zamazal     qemu_chr_fe_write_all(chr, (uint8_t *)msg, VHOST_USER_HDR_SIZE + msg->size);
1178*b6f53ae0SMilan Zamazal }
1179*b6f53ae0SMilan Zamazal 
1180*b6f53ae0SMilan Zamazal static struct vhost_user_ops g_vu_scmi_ops = {
1181*b6f53ae0SMilan Zamazal     .type = VHOST_USER_SCMI,
1182*b6f53ae0SMilan Zamazal 
1183*b6f53ae0SMilan Zamazal     .append_opts = append_vhost_gpio_opts,
1184*b6f53ae0SMilan Zamazal 
1185*b6f53ae0SMilan Zamazal     .get_features = vu_scmi_get_features,
1186*b6f53ae0SMilan Zamazal     .set_features = vu_net_set_features,
1187*b6f53ae0SMilan Zamazal     .get_protocol_features = vu_scmi_get_protocol_features,
1188*b6f53ae0SMilan Zamazal };
1189*b6f53ae0SMilan Zamazal 
register_vhost_scmi_test(void)1190*b6f53ae0SMilan Zamazal static void register_vhost_scmi_test(void)
1191*b6f53ae0SMilan Zamazal {
1192*b6f53ae0SMilan Zamazal     QOSGraphTestOptions opts = {
1193*b6f53ae0SMilan Zamazal         .before = vhost_user_test_setup,
1194*b6f53ae0SMilan Zamazal         .subprocess = true,
1195*b6f53ae0SMilan Zamazal         .arg = &g_vu_scmi_ops,
1196*b6f53ae0SMilan Zamazal     };
1197*b6f53ae0SMilan Zamazal 
1198*b6f53ae0SMilan Zamazal     qemu_add_opts(&qemu_chardev_opts);
1199*b6f53ae0SMilan Zamazal 
1200*b6f53ae0SMilan Zamazal     qos_add_test("scmi/read-guest-mem/memfile",
1201*b6f53ae0SMilan Zamazal                  "vhost-user-scmi", test_read_guest_mem, &opts);
1202*b6f53ae0SMilan Zamazal }
1203*b6f53ae0SMilan Zamazal libqos_init(register_vhost_scmi_test);
1204