xref: /qemu/tests/qtest/vhost-user-test.c (revision ff070f602af6163b6ef4d803d5c3bde735035fed)
1a77e6b14SNikolay Nikolaev /*
2a77e6b14SNikolay Nikolaev  * QTest testcase for the vhost-user
3a77e6b14SNikolay Nikolaev  *
4a77e6b14SNikolay Nikolaev  * Copyright (c) 2014 Virtual Open Systems Sarl.
5a77e6b14SNikolay Nikolaev  *
6a77e6b14SNikolay Nikolaev  * This work is licensed under the terms of the GNU GPL, version 2 or later.
7a77e6b14SNikolay Nikolaev  * See the COPYING file in the top-level directory.
8a77e6b14SNikolay Nikolaev  *
9a77e6b14SNikolay Nikolaev  */
10a77e6b14SNikolay Nikolaev 
11681c28a3SPeter Maydell #include "qemu/osdep.h"
12bd95939fSNikolay Nikolaev 
13dd210749SThomas Huth #include "libqtest-single.h"
145345fdb4SMarc-André Lureau #include "qapi/error.h"
15452fcdbcSMarkus Armbruster #include "qapi/qmp/qdict.h"
16213dcb06SMarc-André Lureau #include "qemu/config-file.h"
17a77e6b14SNikolay Nikolaev #include "qemu/option.h"
18b1819747SMarc-André Lureau #include "qemu/range.h"
19a9c94277SMarkus Armbruster #include "qemu/sockets.h"
204d43a603SMarc-André Lureau #include "chardev/char-fe.h"
218e029fd6SMarc-André Lureau #include "qemu/memfd.h"
220b8fa32fSMarkus Armbruster #include "qemu/module.h"
23a77e6b14SNikolay Nikolaev #include "sysemu/sysemu.h"
24cdafe929SEduardo Habkost #include "libqos/libqos.h"
25cdafe929SEduardo Habkost #include "libqos/pci-pc.h"
26cdafe929SEduardo Habkost #include "libqos/virtio-pci.h"
27a77e6b14SNikolay Nikolaev 
28ed0a8d92SMarc-André Lureau #include "libqos/malloc-pc.h"
2930ea13e9SAlex Bennée #include "libqos/qgraph_internal.h"
30ed0a8d92SMarc-André Lureau #include "hw/virtio/virtio-net.h"
31ed0a8d92SMarc-André Lureau 
32af3bba76SPaolo Bonzini #include "standard-headers/linux/vhost_types.h"
33af3bba76SPaolo Bonzini #include "standard-headers/linux/virtio_ids.h"
34af3bba76SPaolo Bonzini #include "standard-headers/linux/virtio_net.h"
35af3bba76SPaolo Bonzini 
36af3bba76SPaolo Bonzini #ifdef CONFIG_LINUX
37a77e6b14SNikolay Nikolaev #include <sys/vfs.h>
38af3bba76SPaolo Bonzini #endif
39a77e6b14SNikolay Nikolaev 
4030de46dbSGonglei 
41704b2168SMarc-André Lureau #define QEMU_CMD_MEM    " -m %d -object memory-backend-file,id=mem,size=%dM," \
42a77e6b14SNikolay Nikolaev                         "mem-path=%s,share=on -numa node,memdev=mem"
438e029fd6SMarc-André Lureau #define QEMU_CMD_MEMFD  " -m %d -object memory-backend-memfd,id=mem,size=%dM," \
448e029fd6SMarc-André Lureau                         " -numa node,memdev=mem"
454616e359SMarc-André Lureau #define QEMU_CMD_CHR    " -chardev socket,id=%s,path=%s%s"
46d24d1ad3SEric Auger #define QEMU_CMD_NETDEV " -netdev vhost-user,id=hs0,chardev=%s,vhostforce=on"
47a77e6b14SNikolay Nikolaev 
48a77e6b14SNikolay Nikolaev #define HUGETLBFS_MAGIC       0x958458f6
49a77e6b14SNikolay Nikolaev 
50a77e6b14SNikolay Nikolaev /*********** FROM hw/virtio/vhost-user.c *************************************/
51a77e6b14SNikolay Nikolaev 
52a77e6b14SNikolay Nikolaev #define VHOST_MEMORY_MAX_NREGIONS    8
53026eb179SMaxime Coquelin #define VHOST_MAX_VIRTQUEUES    0x100
54a77e6b14SNikolay Nikolaev 
558a9b6b37SMichael S. Tsirkin #define VHOST_USER_F_PROTOCOL_FEATURES 30
56ed0a8d92SMarc-André Lureau #define VHOST_USER_PROTOCOL_F_MQ 0
57b1819747SMarc-André Lureau #define VHOST_USER_PROTOCOL_F_LOG_SHMFD 1
585a583cc5SPaolo Bonzini #define VHOST_USER_PROTOCOL_F_CROSS_ENDIAN   6
59b1819747SMarc-André Lureau 
60b1819747SMarc-André Lureau #define VHOST_LOG_PAGE 0x1000
618a9b6b37SMichael S. Tsirkin 
62a77e6b14SNikolay Nikolaev typedef enum VhostUserRequest {
63a77e6b14SNikolay Nikolaev     VHOST_USER_NONE = 0,
64a77e6b14SNikolay Nikolaev     VHOST_USER_GET_FEATURES = 1,
65a77e6b14SNikolay Nikolaev     VHOST_USER_SET_FEATURES = 2,
66a77e6b14SNikolay Nikolaev     VHOST_USER_SET_OWNER = 3,
6760915dc4SYuanhan Liu     VHOST_USER_RESET_OWNER = 4,
68a77e6b14SNikolay Nikolaev     VHOST_USER_SET_MEM_TABLE = 5,
69a77e6b14SNikolay Nikolaev     VHOST_USER_SET_LOG_BASE = 6,
70a77e6b14SNikolay Nikolaev     VHOST_USER_SET_LOG_FD = 7,
71a77e6b14SNikolay Nikolaev     VHOST_USER_SET_VRING_NUM = 8,
72a77e6b14SNikolay Nikolaev     VHOST_USER_SET_VRING_ADDR = 9,
73a77e6b14SNikolay Nikolaev     VHOST_USER_SET_VRING_BASE = 10,
74a77e6b14SNikolay Nikolaev     VHOST_USER_GET_VRING_BASE = 11,
75a77e6b14SNikolay Nikolaev     VHOST_USER_SET_VRING_KICK = 12,
76a77e6b14SNikolay Nikolaev     VHOST_USER_SET_VRING_CALL = 13,
77a77e6b14SNikolay Nikolaev     VHOST_USER_SET_VRING_ERR = 14,
788a9b6b37SMichael S. Tsirkin     VHOST_USER_GET_PROTOCOL_FEATURES = 15,
798a9b6b37SMichael S. Tsirkin     VHOST_USER_SET_PROTOCOL_FEATURES = 16,
80ed0a8d92SMarc-André Lureau     VHOST_USER_GET_QUEUE_NUM = 17,
8187656d50SMichael S. Tsirkin     VHOST_USER_SET_VRING_ENABLE = 18,
82*ff070f60SAlex Bennée     VHOST_USER_GET_CONFIG = 24,
83*ff070f60SAlex Bennée     VHOST_USER_SET_CONFIG = 25,
84a77e6b14SNikolay Nikolaev     VHOST_USER_MAX
85a77e6b14SNikolay Nikolaev } VhostUserRequest;
86a77e6b14SNikolay Nikolaev 
87a77e6b14SNikolay Nikolaev typedef struct VhostUserMemoryRegion {
88a77e6b14SNikolay Nikolaev     uint64_t guest_phys_addr;
89a77e6b14SNikolay Nikolaev     uint64_t memory_size;
90a77e6b14SNikolay Nikolaev     uint64_t userspace_addr;
91d6970e3bSNikolay Nikolaev     uint64_t mmap_offset;
92a77e6b14SNikolay Nikolaev } VhostUserMemoryRegion;
93a77e6b14SNikolay Nikolaev 
94a77e6b14SNikolay Nikolaev typedef struct VhostUserMemory {
95a77e6b14SNikolay Nikolaev     uint32_t nregions;
96a77e6b14SNikolay Nikolaev     uint32_t padding;
97a77e6b14SNikolay Nikolaev     VhostUserMemoryRegion regions[VHOST_MEMORY_MAX_NREGIONS];
98a77e6b14SNikolay Nikolaev } VhostUserMemory;
99a77e6b14SNikolay Nikolaev 
1002b8819c6SVictor Kaplansky typedef struct VhostUserLog {
1012b8819c6SVictor Kaplansky     uint64_t mmap_size;
1022b8819c6SVictor Kaplansky     uint64_t mmap_offset;
1032b8819c6SVictor Kaplansky } VhostUserLog;
1042b8819c6SVictor Kaplansky 
105a77e6b14SNikolay Nikolaev typedef struct VhostUserMsg {
106a77e6b14SNikolay Nikolaev     VhostUserRequest request;
107a77e6b14SNikolay Nikolaev 
108a77e6b14SNikolay Nikolaev #define VHOST_USER_VERSION_MASK     (0x3)
109a77e6b14SNikolay Nikolaev #define VHOST_USER_REPLY_MASK       (0x1<<2)
110a77e6b14SNikolay Nikolaev     uint32_t flags;
111a77e6b14SNikolay Nikolaev     uint32_t size; /* the following payload size */
112a77e6b14SNikolay Nikolaev     union {
1132b8819c6SVictor Kaplansky #define VHOST_USER_VRING_IDX_MASK   (0xff)
1142b8819c6SVictor Kaplansky #define VHOST_USER_VRING_NOFD_MASK  (0x1<<8)
115a77e6b14SNikolay Nikolaev         uint64_t u64;
116a77e6b14SNikolay Nikolaev         struct vhost_vring_state state;
117a77e6b14SNikolay Nikolaev         struct vhost_vring_addr addr;
118a77e6b14SNikolay Nikolaev         VhostUserMemory memory;
1192b8819c6SVictor Kaplansky         VhostUserLog log;
12012ebf690SMichael S. Tsirkin     } payload;
121a77e6b14SNikolay Nikolaev } QEMU_PACKED VhostUserMsg;
122a77e6b14SNikolay Nikolaev 
123a77e6b14SNikolay Nikolaev static VhostUserMsg m __attribute__ ((unused));
124a77e6b14SNikolay Nikolaev #define VHOST_USER_HDR_SIZE (sizeof(m.request) \
125a77e6b14SNikolay Nikolaev                             + sizeof(m.flags) \
126a77e6b14SNikolay Nikolaev                             + sizeof(m.size))
127a77e6b14SNikolay Nikolaev 
128a77e6b14SNikolay Nikolaev #define VHOST_USER_PAYLOAD_SIZE (sizeof(m) - VHOST_USER_HDR_SIZE)
129a77e6b14SNikolay Nikolaev 
130a77e6b14SNikolay Nikolaev /* The version of the protocol we support */
131a77e6b14SNikolay Nikolaev #define VHOST_USER_VERSION    (0x1)
132a77e6b14SNikolay Nikolaev /*****************************************************************************/
133a77e6b14SNikolay Nikolaev 
1349294d76cSMarc-André Lureau enum {
1359294d76cSMarc-André Lureau     TEST_FLAGS_OK,
1369294d76cSMarc-André Lureau     TEST_FLAGS_DISCONNECT,
1379294d76cSMarc-André Lureau     TEST_FLAGS_BAD,
1389294d76cSMarc-André Lureau     TEST_FLAGS_END,
1399294d76cSMarc-André Lureau };
1409294d76cSMarc-André Lureau 
141892040dcSDima Stepanov enum {
142892040dcSDima Stepanov     VHOST_USER_NET,
143892040dcSDima Stepanov };
144892040dcSDima Stepanov 
145ae31fb54SMarc-André Lureau typedef struct TestServer {
146ae31fb54SMarc-André Lureau     gchar *socket_path;
147a899b1eaSMarc-André Lureau     gchar *mig_path;
148ae31fb54SMarc-André Lureau     gchar *chr_name;
1494d3f50ebSPaolo Bonzini     gchar *tmpfs;
15032a6ebecSMarc-André Lureau     CharBackend chr;
151ae31fb54SMarc-André Lureau     int fds_num;
152ae31fb54SMarc-André Lureau     int fds[VHOST_MEMORY_MAX_NREGIONS];
153ae31fb54SMarc-André Lureau     VhostUserMemory memory;
1547d0ca3e7SPaolo Bonzini     GMainContext *context;
1557d0ca3e7SPaolo Bonzini     GMainLoop *loop;
1567d0ca3e7SPaolo Bonzini     GThread *thread;
157e7b3af81SDaniel P. Berrangé     GMutex data_mutex;
158e7b3af81SDaniel P. Berrangé     GCond data_cond;
159b1819747SMarc-André Lureau     int log_fd;
160d08e42a1SMichael S. Tsirkin     uint64_t rings;
1615d443f5aSMarc-André Lureau     bool test_fail;
1629294d76cSMarc-André Lureau     int test_flags;
163ed0a8d92SMarc-André Lureau     int queues;
164892040dcSDima Stepanov     struct vhost_user_ops *vu_ops;
165ae31fb54SMarc-André Lureau } TestServer;
166bd95939fSNikolay Nikolaev 
167892040dcSDima Stepanov struct vhost_user_ops {
168892040dcSDima Stepanov     /* Device types. */
169892040dcSDima Stepanov     int type;
170892040dcSDima Stepanov     void (*append_opts)(TestServer *s, GString *cmd_line,
171892040dcSDima Stepanov             const char *chr_opts);
172892040dcSDima Stepanov 
173892040dcSDima Stepanov     /* VHOST-USER commands. */
174892040dcSDima Stepanov     void (*set_features)(TestServer *s, CharBackend *chr,
175892040dcSDima Stepanov             VhostUserMsg *msg);
176892040dcSDima Stepanov     void (*get_protocol_features)(TestServer *s,
177892040dcSDima Stepanov             CharBackend *chr, VhostUserMsg *msg);
178892040dcSDima Stepanov };
179892040dcSDima Stepanov 
180a3ebd6e0SPaolo Bonzini static const char *init_hugepagefs(void);
181892040dcSDima Stepanov static TestServer *test_server_new(const gchar *name,
182892040dcSDima Stepanov         struct vhost_user_ops *ops);
18383265145SMarc-André Lureau static void test_server_free(TestServer *server);
18483265145SMarc-André Lureau static void test_server_listen(TestServer *server);
18583265145SMarc-André Lureau 
1868e029fd6SMarc-André Lureau enum test_memfd {
1878e029fd6SMarc-André Lureau     TEST_MEMFD_AUTO,
1888e029fd6SMarc-André Lureau     TEST_MEMFD_YES,
1898e029fd6SMarc-André Lureau     TEST_MEMFD_NO,
1908e029fd6SMarc-André Lureau };
1918e029fd6SMarc-André Lureau 
192892040dcSDima Stepanov static void append_vhost_net_opts(TestServer *s, GString *cmd_line,
193a3ebd6e0SPaolo Bonzini                              const char *chr_opts)
1948e029fd6SMarc-André Lureau {
195a3ebd6e0SPaolo Bonzini     g_string_append_printf(cmd_line, QEMU_CMD_CHR QEMU_CMD_NETDEV,
196a3ebd6e0SPaolo Bonzini                            s->chr_name, s->socket_path,
197a3ebd6e0SPaolo Bonzini                            chr_opts, s->chr_name);
198a3ebd6e0SPaolo Bonzini }
199a3ebd6e0SPaolo Bonzini 
200a3ebd6e0SPaolo Bonzini static void append_mem_opts(TestServer *server, GString *cmd_line,
201a3ebd6e0SPaolo Bonzini                             int size, enum test_memfd memfd)
202a3ebd6e0SPaolo Bonzini {
203a3ebd6e0SPaolo Bonzini     if (memfd == TEST_MEMFD_AUTO) {
2044a66c7a9SIlya Maximets         memfd = qemu_memfd_check(MFD_ALLOW_SEALING) ? TEST_MEMFD_YES
2054a66c7a9SIlya Maximets                                                     : TEST_MEMFD_NO;
2068e029fd6SMarc-André Lureau     }
2078e029fd6SMarc-André Lureau 
2088e029fd6SMarc-André Lureau     if (memfd == TEST_MEMFD_YES) {
209a3ebd6e0SPaolo Bonzini         g_string_append_printf(cmd_line, QEMU_CMD_MEMFD, size, size);
2108e029fd6SMarc-André Lureau     } else {
211a3ebd6e0SPaolo Bonzini         const char *root = init_hugepagefs() ? : server->tmpfs;
212a3ebd6e0SPaolo Bonzini 
213a3ebd6e0SPaolo Bonzini         g_string_append_printf(cmd_line, QEMU_CMD_MEM, size, size, root);
2148e029fd6SMarc-André Lureau     }
2158e029fd6SMarc-André Lureau }
2168e029fd6SMarc-André Lureau 
2173b72ca38SPaolo Bonzini static bool wait_for_fds(TestServer *s)
218a77e6b14SNikolay Nikolaev {
219a77e6b14SNikolay Nikolaev     gint64 end_time;
2203b72ca38SPaolo Bonzini     bool got_region;
2213b72ca38SPaolo Bonzini     int i;
222a77e6b14SNikolay Nikolaev 
223ae31fb54SMarc-André Lureau     g_mutex_lock(&s->data_mutex);
224a77e6b14SNikolay Nikolaev 
225ca06d9ccSPaolo Bonzini     end_time = g_get_monotonic_time() + 5 * G_TIME_SPAN_SECOND;
226ae31fb54SMarc-André Lureau     while (!s->fds_num) {
227ae31fb54SMarc-André Lureau         if (!g_cond_wait_until(&s->data_cond, &s->data_mutex, end_time)) {
228a77e6b14SNikolay Nikolaev             /* timeout has passed */
229ae31fb54SMarc-André Lureau             g_assert(s->fds_num);
230a77e6b14SNikolay Nikolaev             break;
231a77e6b14SNikolay Nikolaev         }
232a77e6b14SNikolay Nikolaev     }
233a77e6b14SNikolay Nikolaev 
234a77e6b14SNikolay Nikolaev     /* check for sanity */
235ae31fb54SMarc-André Lureau     g_assert_cmpint(s->fds_num, >, 0);
236ae31fb54SMarc-André Lureau     g_assert_cmpint(s->fds_num, ==, s->memory.nregions);
237a77e6b14SNikolay Nikolaev 
238ae31fb54SMarc-André Lureau     g_mutex_unlock(&s->data_mutex);
2393b72ca38SPaolo Bonzini 
2403b72ca38SPaolo Bonzini     got_region = false;
2413b72ca38SPaolo Bonzini     for (i = 0; i < s->memory.nregions; ++i) {
2423b72ca38SPaolo Bonzini         VhostUserMemoryRegion *reg = &s->memory.regions[i];
2433b72ca38SPaolo Bonzini         if (reg->guest_phys_addr == 0) {
2443b72ca38SPaolo Bonzini             got_region = true;
2453b72ca38SPaolo Bonzini             break;
2463b72ca38SPaolo Bonzini         }
2473b72ca38SPaolo Bonzini     }
2483b72ca38SPaolo Bonzini     if (!got_region) {
2493b72ca38SPaolo Bonzini         g_test_skip("No memory at address 0x0");
2503b72ca38SPaolo Bonzini     }
2513b72ca38SPaolo Bonzini     return got_region;
252cf72b57fSMarc-André Lureau }
253cf72b57fSMarc-André Lureau 
254bae6b59dSPaolo Bonzini static void read_guest_mem_server(QTestState *qts, TestServer *s)
255cf72b57fSMarc-André Lureau {
2565a583cc5SPaolo Bonzini     uint8_t *guest_mem;
257cf72b57fSMarc-André Lureau     int i, j;
258cf72b57fSMarc-André Lureau     size_t size;
259cf72b57fSMarc-André Lureau 
260ae31fb54SMarc-André Lureau     g_mutex_lock(&s->data_mutex);
261cf72b57fSMarc-André Lureau 
262a77e6b14SNikolay Nikolaev     /* iterate all regions */
263ae31fb54SMarc-André Lureau     for (i = 0; i < s->fds_num; i++) {
264a77e6b14SNikolay Nikolaev 
265a77e6b14SNikolay Nikolaev         /* We'll check only the region statring at 0x0*/
266ae31fb54SMarc-André Lureau         if (s->memory.regions[i].guest_phys_addr != 0x0) {
267a77e6b14SNikolay Nikolaev             continue;
268a77e6b14SNikolay Nikolaev         }
269a77e6b14SNikolay Nikolaev 
270ae31fb54SMarc-André Lureau         g_assert_cmpint(s->memory.regions[i].memory_size, >, 1024);
271a77e6b14SNikolay Nikolaev 
272ae31fb54SMarc-André Lureau         size = s->memory.regions[i].memory_size +
273ae31fb54SMarc-André Lureau             s->memory.regions[i].mmap_offset;
274d6970e3bSNikolay Nikolaev 
275d6970e3bSNikolay Nikolaev         guest_mem = mmap(0, size, PROT_READ | PROT_WRITE,
276ae31fb54SMarc-André Lureau                          MAP_SHARED, s->fds[i], 0);
277d6970e3bSNikolay Nikolaev 
278d6970e3bSNikolay Nikolaev         g_assert(guest_mem != MAP_FAILED);
279ae31fb54SMarc-André Lureau         guest_mem += (s->memory.regions[i].mmap_offset / sizeof(*guest_mem));
280a77e6b14SNikolay Nikolaev 
2815a583cc5SPaolo Bonzini         for (j = 0; j < 1024; j++) {
282bae6b59dSPaolo Bonzini             uint32_t a = qtest_readb(qts, s->memory.regions[i].guest_phys_addr + j);
283a77e6b14SNikolay Nikolaev             uint32_t b = guest_mem[j];
284a77e6b14SNikolay Nikolaev 
285a77e6b14SNikolay Nikolaev             g_assert_cmpint(a, ==, b);
286a77e6b14SNikolay Nikolaev         }
287a77e6b14SNikolay Nikolaev 
288ae31fb54SMarc-André Lureau         munmap(guest_mem, s->memory.regions[i].memory_size);
289a77e6b14SNikolay Nikolaev     }
290a77e6b14SNikolay Nikolaev 
291ae31fb54SMarc-André Lureau     g_mutex_unlock(&s->data_mutex);
292a77e6b14SNikolay Nikolaev }
293a77e6b14SNikolay Nikolaev 
294a77e6b14SNikolay Nikolaev static void *thread_function(void *data)
295a77e6b14SNikolay Nikolaev {
2969732baf6SMarc-André Lureau     GMainLoop *loop = data;
297a77e6b14SNikolay Nikolaev     g_main_loop_run(loop);
298a77e6b14SNikolay Nikolaev     return NULL;
299a77e6b14SNikolay Nikolaev }
300a77e6b14SNikolay Nikolaev 
301a77e6b14SNikolay Nikolaev static int chr_can_read(void *opaque)
302a77e6b14SNikolay Nikolaev {
303a77e6b14SNikolay Nikolaev     return VHOST_USER_HDR_SIZE;
304a77e6b14SNikolay Nikolaev }
305a77e6b14SNikolay Nikolaev 
306a77e6b14SNikolay Nikolaev static void chr_read(void *opaque, const uint8_t *buf, int size)
307a77e6b14SNikolay Nikolaev {
308b2670d1fSMarc-André Lureau     g_autoptr(GError) err = NULL;
309ae31fb54SMarc-André Lureau     TestServer *s = opaque;
31032a6ebecSMarc-André Lureau     CharBackend *chr = &s->chr;
311a77e6b14SNikolay Nikolaev     VhostUserMsg msg;
312a77e6b14SNikolay Nikolaev     uint8_t *p = (uint8_t *) &msg;
31382248cd4SLi Qiang     int fd = -1;
314a77e6b14SNikolay Nikolaev 
3155d443f5aSMarc-André Lureau     if (s->test_fail) {
3165345fdb4SMarc-André Lureau         qemu_chr_fe_disconnect(chr);
3175d443f5aSMarc-André Lureau         /* now switch to non-failure */
3185d443f5aSMarc-André Lureau         s->test_fail = false;
3195d443f5aSMarc-André Lureau     }
3205d443f5aSMarc-André Lureau 
321a77e6b14SNikolay Nikolaev     if (size != VHOST_USER_HDR_SIZE) {
32230ea13e9SAlex Bennée         qos_printf("%s: Wrong message size received %d\n", __func__, size);
323a77e6b14SNikolay Nikolaev         return;
324a77e6b14SNikolay Nikolaev     }
325a77e6b14SNikolay Nikolaev 
326ae31fb54SMarc-André Lureau     g_mutex_lock(&s->data_mutex);
327a77e6b14SNikolay Nikolaev     memcpy(p, buf, VHOST_USER_HDR_SIZE);
328a77e6b14SNikolay Nikolaev 
329a77e6b14SNikolay Nikolaev     if (msg.size) {
330a77e6b14SNikolay Nikolaev         p += VHOST_USER_HDR_SIZE;
3315345fdb4SMarc-André Lureau         size = qemu_chr_fe_read_all(chr, p, msg.size);
3324616e359SMarc-André Lureau         if (size != msg.size) {
33330ea13e9SAlex Bennée             qos_printf("%s: Wrong message size received %d != %d\n",
33430ea13e9SAlex Bennée                        __func__, size, msg.size);
3354616e359SMarc-André Lureau             return;
3364616e359SMarc-André Lureau         }
337a77e6b14SNikolay Nikolaev     }
338a77e6b14SNikolay Nikolaev 
339a77e6b14SNikolay Nikolaev     switch (msg.request) {
340a77e6b14SNikolay Nikolaev     case VHOST_USER_GET_FEATURES:
341a77e6b14SNikolay Nikolaev         /* send back features to qemu */
342a77e6b14SNikolay Nikolaev         msg.flags |= VHOST_USER_REPLY_MASK;
34312ebf690SMichael S. Tsirkin         msg.size = sizeof(m.payload.u64);
34412ebf690SMichael S. Tsirkin         msg.payload.u64 = 0x1ULL << VHOST_F_LOG_ALL |
345b1819747SMarc-André Lureau             0x1ULL << VHOST_USER_F_PROTOCOL_FEATURES;
346ed0a8d92SMarc-André Lureau         if (s->queues > 1) {
347ed0a8d92SMarc-André Lureau             msg.payload.u64 |= 0x1ULL << VIRTIO_NET_F_MQ;
348ed0a8d92SMarc-André Lureau         }
3499294d76cSMarc-André Lureau         if (s->test_flags >= TEST_FLAGS_BAD) {
3509294d76cSMarc-André Lureau             msg.payload.u64 = 0;
3519294d76cSMarc-André Lureau             s->test_flags = TEST_FLAGS_END;
3529294d76cSMarc-André Lureau         }
3538a9b6b37SMichael S. Tsirkin         p = (uint8_t *) &msg;
3545345fdb4SMarc-André Lureau         qemu_chr_fe_write_all(chr, p, VHOST_USER_HDR_SIZE + msg.size);
3558a9b6b37SMichael S. Tsirkin         break;
3568a9b6b37SMichael S. Tsirkin 
3578a9b6b37SMichael S. Tsirkin     case VHOST_USER_SET_FEATURES:
358892040dcSDima Stepanov         if (s->vu_ops->set_features) {
359892040dcSDima Stepanov             s->vu_ops->set_features(s, chr, &msg);
3609294d76cSMarc-André Lureau         }
3618a9b6b37SMichael S. Tsirkin         break;
3628a9b6b37SMichael S. Tsirkin 
36320a4127fSAlex Bennée     case VHOST_USER_SET_OWNER:
36420a4127fSAlex Bennée         /*
36520a4127fSAlex Bennée          * We don't need to do anything here, the remote is just
36620a4127fSAlex Bennée          * letting us know it is in charge. Just log it.
36720a4127fSAlex Bennée          */
36820a4127fSAlex Bennée         qos_printf("set_owner: start of session\n");
36920a4127fSAlex Bennée         break;
37020a4127fSAlex Bennée 
3718a9b6b37SMichael S. Tsirkin     case VHOST_USER_GET_PROTOCOL_FEATURES:
372892040dcSDima Stepanov         if (s->vu_ops->get_protocol_features) {
373892040dcSDima Stepanov             s->vu_ops->get_protocol_features(s, chr, &msg);
374ed0a8d92SMarc-André Lureau         }
375a77e6b14SNikolay Nikolaev         break;
376a77e6b14SNikolay Nikolaev 
377*ff070f60SAlex Bennée     case VHOST_USER_GET_CONFIG:
378*ff070f60SAlex Bennée         /*
379*ff070f60SAlex Bennée          * Treat GET_CONFIG as a NOP and just reply and let the guest
380*ff070f60SAlex Bennée          * consider we have updated its memory. Tests currently don't
381*ff070f60SAlex Bennée          * require working configs.
382*ff070f60SAlex Bennée          */
383*ff070f60SAlex Bennée         msg.flags |= VHOST_USER_REPLY_MASK;
384*ff070f60SAlex Bennée         p = (uint8_t *) &msg;
385*ff070f60SAlex Bennée         qemu_chr_fe_write_all(chr, p, VHOST_USER_HDR_SIZE + msg.size);
386*ff070f60SAlex Bennée         break;
387*ff070f60SAlex Bennée 
38820a4127fSAlex Bennée     case VHOST_USER_SET_PROTOCOL_FEATURES:
38920a4127fSAlex Bennée         /*
39020a4127fSAlex Bennée          * We did set VHOST_USER_F_PROTOCOL_FEATURES so its valid for
39120a4127fSAlex Bennée          * the remote end to send this. There is no handshake reply so
39220a4127fSAlex Bennée          * just log the details for debugging.
39320a4127fSAlex Bennée          */
39420a4127fSAlex Bennée         qos_printf("set_protocol_features: 0x%"PRIx64 "\n", msg.payload.u64);
39520a4127fSAlex Bennée         break;
39620a4127fSAlex Bennée 
39720a4127fSAlex Bennée         /*
39820a4127fSAlex Bennée          * A real vhost-user backend would actually set the size and
39920a4127fSAlex Bennée          * address of the vrings but we can simply report them.
40020a4127fSAlex Bennée          */
40120a4127fSAlex Bennée     case VHOST_USER_SET_VRING_NUM:
40220a4127fSAlex Bennée         qos_printf("set_vring_num: %d/%d\n",
40320a4127fSAlex Bennée                    msg.payload.state.index, msg.payload.state.num);
40420a4127fSAlex Bennée         break;
40520a4127fSAlex Bennée     case VHOST_USER_SET_VRING_ADDR:
40620a4127fSAlex Bennée         qos_printf("set_vring_addr: 0x%"PRIx64"/0x%"PRIx64"/0x%"PRIx64"\n",
40720a4127fSAlex Bennée                    msg.payload.addr.avail_user_addr,
40820a4127fSAlex Bennée                    msg.payload.addr.desc_user_addr,
40920a4127fSAlex Bennée                    msg.payload.addr.used_user_addr);
41020a4127fSAlex Bennée         break;
41120a4127fSAlex Bennée 
412a77e6b14SNikolay Nikolaev     case VHOST_USER_GET_VRING_BASE:
413a77e6b14SNikolay Nikolaev         /* send back vring base to qemu */
414a77e6b14SNikolay Nikolaev         msg.flags |= VHOST_USER_REPLY_MASK;
41512ebf690SMichael S. Tsirkin         msg.size = sizeof(m.payload.state);
41612ebf690SMichael S. Tsirkin         msg.payload.state.num = 0;
417a77e6b14SNikolay Nikolaev         p = (uint8_t *) &msg;
4185345fdb4SMarc-André Lureau         qemu_chr_fe_write_all(chr, p, VHOST_USER_HDR_SIZE + msg.size);
419d08e42a1SMichael S. Tsirkin 
420ed0a8d92SMarc-André Lureau         assert(msg.payload.state.index < s->queues * 2);
421d08e42a1SMichael S. Tsirkin         s->rings &= ~(0x1ULL << msg.payload.state.index);
422acca950cSPaolo Bonzini         g_cond_broadcast(&s->data_cond);
423a77e6b14SNikolay Nikolaev         break;
424a77e6b14SNikolay Nikolaev 
425a77e6b14SNikolay Nikolaev     case VHOST_USER_SET_MEM_TABLE:
426a77e6b14SNikolay Nikolaev         /* received the mem table */
42712ebf690SMichael S. Tsirkin         memcpy(&s->memory, &msg.payload.memory, sizeof(msg.payload.memory));
4285345fdb4SMarc-André Lureau         s->fds_num = qemu_chr_fe_get_msgfds(chr, s->fds,
42932a6ebecSMarc-André Lureau                                             G_N_ELEMENTS(s->fds));
430a77e6b14SNikolay Nikolaev 
431a77e6b14SNikolay Nikolaev         /* signal the test that it can continue */
43204ad1bf6SPaolo Bonzini         g_cond_broadcast(&s->data_cond);
433a77e6b14SNikolay Nikolaev         break;
434a77e6b14SNikolay Nikolaev 
435a77e6b14SNikolay Nikolaev     case VHOST_USER_SET_VRING_KICK:
436a77e6b14SNikolay Nikolaev     case VHOST_USER_SET_VRING_CALL:
437a77e6b14SNikolay Nikolaev         /* consume the fd */
4385345fdb4SMarc-André Lureau         qemu_chr_fe_get_msgfds(chr, &fd, 1);
439a77e6b14SNikolay Nikolaev         /*
440a77e6b14SNikolay Nikolaev          * This is a non-blocking eventfd.
441a77e6b14SNikolay Nikolaev          * The receive function forces it to be blocking,
442a77e6b14SNikolay Nikolaev          * so revert it back to non-blocking.
443a77e6b14SNikolay Nikolaev          */
444b2670d1fSMarc-André Lureau         g_unix_set_fd_nonblocking(fd, true, &err);
445b2670d1fSMarc-André Lureau         g_assert_no_error(err);
446a77e6b14SNikolay Nikolaev         break;
447b1819747SMarc-André Lureau 
448b1819747SMarc-André Lureau     case VHOST_USER_SET_LOG_BASE:
449b1819747SMarc-André Lureau         if (s->log_fd != -1) {
450b1819747SMarc-André Lureau             close(s->log_fd);
451b1819747SMarc-André Lureau             s->log_fd = -1;
452b1819747SMarc-André Lureau         }
4535345fdb4SMarc-André Lureau         qemu_chr_fe_get_msgfds(chr, &s->log_fd, 1);
454b1819747SMarc-André Lureau         msg.flags |= VHOST_USER_REPLY_MASK;
455b1819747SMarc-André Lureau         msg.size = 0;
456b1819747SMarc-André Lureau         p = (uint8_t *) &msg;
4575345fdb4SMarc-André Lureau         qemu_chr_fe_write_all(chr, p, VHOST_USER_HDR_SIZE);
458b1819747SMarc-André Lureau 
45904ad1bf6SPaolo Bonzini         g_cond_broadcast(&s->data_cond);
460b1819747SMarc-André Lureau         break;
461b1819747SMarc-André Lureau 
462d08e42a1SMichael S. Tsirkin     case VHOST_USER_SET_VRING_BASE:
463ed0a8d92SMarc-André Lureau         assert(msg.payload.state.index < s->queues * 2);
464d08e42a1SMichael S. Tsirkin         s->rings |= 0x1ULL << msg.payload.state.index;
465acca950cSPaolo Bonzini         g_cond_broadcast(&s->data_cond);
4661d9edff7SMarc-André Lureau         break;
4671d9edff7SMarc-André Lureau 
468ed0a8d92SMarc-André Lureau     case VHOST_USER_GET_QUEUE_NUM:
469ed0a8d92SMarc-André Lureau         msg.flags |= VHOST_USER_REPLY_MASK;
470ed0a8d92SMarc-André Lureau         msg.size = sizeof(m.payload.u64);
471ed0a8d92SMarc-André Lureau         msg.payload.u64 = s->queues;
472ed0a8d92SMarc-André Lureau         p = (uint8_t *) &msg;
4735345fdb4SMarc-André Lureau         qemu_chr_fe_write_all(chr, p, VHOST_USER_HDR_SIZE + msg.size);
474ed0a8d92SMarc-André Lureau         break;
475ed0a8d92SMarc-André Lureau 
47620a4127fSAlex Bennée     case VHOST_USER_SET_VRING_ENABLE:
47720a4127fSAlex Bennée         /*
47820a4127fSAlex Bennée          * Another case we ignore as we don't need to respond. With a
47920a4127fSAlex Bennée          * fully functioning vhost-user we would enable/disable the
48020a4127fSAlex Bennée          * vring monitoring.
48120a4127fSAlex Bennée          */
48220a4127fSAlex Bennée         qos_printf("set_vring(%d)=%s\n", msg.payload.state.index,
48320a4127fSAlex Bennée                    msg.payload.state.num ? "enabled" : "disabled");
48420a4127fSAlex Bennée         break;
48520a4127fSAlex Bennée 
486a77e6b14SNikolay Nikolaev     default:
48720a4127fSAlex Bennée         qos_printf("vhost-user: un-handled message: %d\n", msg.request);
488a77e6b14SNikolay Nikolaev         break;
489a77e6b14SNikolay Nikolaev     }
490ae31fb54SMarc-André Lureau 
491ae31fb54SMarc-André Lureau     g_mutex_unlock(&s->data_mutex);
492a77e6b14SNikolay Nikolaev }
493a77e6b14SNikolay Nikolaev 
4949ee1bb86SPaolo Bonzini static const char *init_hugepagefs(void)
495a77e6b14SNikolay Nikolaev {
4969ee1bb86SPaolo Bonzini #ifdef CONFIG_LINUX
497a3ebd6e0SPaolo Bonzini     static const char *hugepagefs;
4989ee1bb86SPaolo Bonzini     const char *path = getenv("QTEST_HUGETLBFS_PATH");
499a77e6b14SNikolay Nikolaev     struct statfs fs;
500a77e6b14SNikolay Nikolaev     int ret;
501a77e6b14SNikolay Nikolaev 
502a3ebd6e0SPaolo Bonzini     if (hugepagefs) {
503a3ebd6e0SPaolo Bonzini         return hugepagefs;
504a3ebd6e0SPaolo Bonzini     }
5059ee1bb86SPaolo Bonzini     if (!path) {
5069ee1bb86SPaolo Bonzini         return NULL;
5079ee1bb86SPaolo Bonzini     }
5089ee1bb86SPaolo Bonzini 
509a77e6b14SNikolay Nikolaev     if (access(path, R_OK | W_OK | X_OK)) {
51030ea13e9SAlex Bennée         qos_printf("access on path (%s): %s", path, strerror(errno));
511a3ebd6e0SPaolo Bonzini         g_test_fail();
512a77e6b14SNikolay Nikolaev         return NULL;
513a77e6b14SNikolay Nikolaev     }
514a77e6b14SNikolay Nikolaev 
515a77e6b14SNikolay Nikolaev     do {
516a77e6b14SNikolay Nikolaev         ret = statfs(path, &fs);
517a77e6b14SNikolay Nikolaev     } while (ret != 0 && errno == EINTR);
518a77e6b14SNikolay Nikolaev 
519a77e6b14SNikolay Nikolaev     if (ret != 0) {
52030ea13e9SAlex Bennée         qos_printf("statfs on path (%s): %s", path, strerror(errno));
521a3ebd6e0SPaolo Bonzini         g_test_fail();
522a77e6b14SNikolay Nikolaev         return NULL;
523a77e6b14SNikolay Nikolaev     }
524a77e6b14SNikolay Nikolaev 
525a77e6b14SNikolay Nikolaev     if (fs.f_type != HUGETLBFS_MAGIC) {
52630ea13e9SAlex Bennée         qos_printf("Warning: path not on HugeTLBFS: %s", path);
527a3ebd6e0SPaolo Bonzini         g_test_fail();
528a77e6b14SNikolay Nikolaev         return NULL;
529a77e6b14SNikolay Nikolaev     }
530a77e6b14SNikolay Nikolaev 
531a3ebd6e0SPaolo Bonzini     hugepagefs = path;
532a3ebd6e0SPaolo Bonzini     return hugepagefs;
5339ee1bb86SPaolo Bonzini #else
5349ee1bb86SPaolo Bonzini     return NULL;
535af3bba76SPaolo Bonzini #endif
5369ee1bb86SPaolo Bonzini }
537a77e6b14SNikolay Nikolaev 
538892040dcSDima Stepanov static TestServer *test_server_new(const gchar *name,
539892040dcSDima Stepanov         struct vhost_user_ops *ops)
540ae31fb54SMarc-André Lureau {
541ae31fb54SMarc-André Lureau     TestServer *server = g_new0(TestServer, 1);
542e6efe236SBin Meng     g_autofree const char *tmpfs = NULL;
543e6efe236SBin Meng     GError *err = NULL;
544ae31fb54SMarc-André Lureau 
5457d0ca3e7SPaolo Bonzini     server->context = g_main_context_new();
5467d0ca3e7SPaolo Bonzini     server->loop = g_main_loop_new(server->context, FALSE);
5477d0ca3e7SPaolo Bonzini 
5487d0ca3e7SPaolo Bonzini     /* run the main loop thread so the chardev may operate */
5497d0ca3e7SPaolo Bonzini     server->thread = g_thread_new(NULL, thread_function, server->loop);
5507d0ca3e7SPaolo Bonzini 
551e6efe236SBin Meng     tmpfs = g_dir_make_tmp("vhost-test-XXXXXX", &err);
5524d3f50ebSPaolo Bonzini     if (!tmpfs) {
553e6efe236SBin Meng         g_test_message("g_dir_make_tmp on path (%s): %s", tmpfs,
554e6efe236SBin Meng                        err->message);
555e6efe236SBin Meng         g_error_free(err);
5564d3f50ebSPaolo Bonzini     }
5574d3f50ebSPaolo Bonzini     g_assert(tmpfs);
5584d3f50ebSPaolo Bonzini 
5594d3f50ebSPaolo Bonzini     server->tmpfs = g_strdup(tmpfs);
560ae31fb54SMarc-André Lureau     server->socket_path = g_strdup_printf("%s/%s.sock", tmpfs, name);
561a899b1eaSMarc-André Lureau     server->mig_path = g_strdup_printf("%s/%s.mig", tmpfs, name);
562ae31fb54SMarc-André Lureau     server->chr_name = g_strdup_printf("chr-%s", name);
563ae31fb54SMarc-André Lureau 
564ae31fb54SMarc-André Lureau     g_mutex_init(&server->data_mutex);
565ae31fb54SMarc-André Lureau     g_cond_init(&server->data_cond);
566ae31fb54SMarc-André Lureau 
567b1819747SMarc-André Lureau     server->log_fd = -1;
568ed0a8d92SMarc-André Lureau     server->queues = 1;
569892040dcSDima Stepanov     server->vu_ops = ops;
570b1819747SMarc-André Lureau 
571ae31fb54SMarc-André Lureau     return server;
572ae31fb54SMarc-André Lureau }
573ae31fb54SMarc-André Lureau 
574083b266fSPhilippe Mathieu-Daudé static void chr_event(void *opaque, QEMUChrEvent event)
5759294d76cSMarc-André Lureau {
5769294d76cSMarc-André Lureau     TestServer *s = opaque;
5779294d76cSMarc-André Lureau 
5789294d76cSMarc-André Lureau     if (s->test_flags == TEST_FLAGS_END &&
5799294d76cSMarc-André Lureau         event == CHR_EVENT_CLOSED) {
5809294d76cSMarc-André Lureau         s->test_flags = TEST_FLAGS_OK;
5819294d76cSMarc-André Lureau     }
5829294d76cSMarc-André Lureau }
5839294d76cSMarc-André Lureau 
5844616e359SMarc-André Lureau static void test_server_create_chr(TestServer *server, const gchar *opt)
5854616e359SMarc-André Lureau {
58655c26982SAlex Bennée     g_autofree gchar *chr_path = g_strdup_printf("unix:%s%s",
58755c26982SAlex Bennée                                                  server->socket_path, opt);
5880ec7b3e7SMarc-André Lureau     Chardev *chr;
5895345fdb4SMarc-André Lureau 
5907d0ca3e7SPaolo Bonzini     chr = qemu_chr_new(server->chr_name, chr_path, server->context);
59155c26982SAlex Bennée     g_assert(chr);
5924616e359SMarc-André Lureau 
5935345fdb4SMarc-André Lureau     qemu_chr_fe_init(&server->chr, chr, &error_abort);
5945345fdb4SMarc-André Lureau     qemu_chr_fe_set_handlers(&server->chr, chr_can_read, chr_read,
5957d0ca3e7SPaolo Bonzini                              chr_event, NULL, server, server->context, true);
5964616e359SMarc-André Lureau }
5974616e359SMarc-André Lureau 
5984616e359SMarc-André Lureau static void test_server_listen(TestServer *server)
5994616e359SMarc-André Lureau {
600991c180dSPaolo Bonzini     test_server_create_chr(server, ",server=on,wait=off");
6014616e359SMarc-André Lureau }
6024616e359SMarc-André Lureau 
6037d0ca3e7SPaolo Bonzini static void test_server_free(TestServer *server)
604ae31fb54SMarc-André Lureau {
6054d3f50ebSPaolo Bonzini     int i, ret;
606ae31fb54SMarc-André Lureau 
6077d0ca3e7SPaolo Bonzini     /* finish the helper thread and dispatch pending sources */
6087d0ca3e7SPaolo Bonzini     g_main_loop_quit(server->loop);
6097d0ca3e7SPaolo Bonzini     g_thread_join(server->thread);
6107d0ca3e7SPaolo Bonzini     while (g_main_context_pending(NULL)) {
6117d0ca3e7SPaolo Bonzini         g_main_context_iteration(NULL, TRUE);
6127d0ca3e7SPaolo Bonzini     }
6137d0ca3e7SPaolo Bonzini 
6144d3f50ebSPaolo Bonzini     unlink(server->socket_path);
6154d3f50ebSPaolo Bonzini     g_free(server->socket_path);
6164d3f50ebSPaolo Bonzini 
6174d3f50ebSPaolo Bonzini     unlink(server->mig_path);
6184d3f50ebSPaolo Bonzini     g_free(server->mig_path);
6194d3f50ebSPaolo Bonzini 
6204d3f50ebSPaolo Bonzini     ret = rmdir(server->tmpfs);
6214d3f50ebSPaolo Bonzini     if (ret != 0) {
6224d3f50ebSPaolo Bonzini         g_test_message("unable to rmdir: path (%s): %s",
6234d3f50ebSPaolo Bonzini                        server->tmpfs, strerror(errno));
6244d3f50ebSPaolo Bonzini     }
6255411f3d0SMarc-André Lureau     g_free(server->tmpfs);
6264d3f50ebSPaolo Bonzini 
6271ce2610cSMarc-André Lureau     qemu_chr_fe_deinit(&server->chr, true);
628ae31fb54SMarc-André Lureau 
629ae31fb54SMarc-André Lureau     for (i = 0; i < server->fds_num; i++) {
630ae31fb54SMarc-André Lureau         close(server->fds[i]);
631ae31fb54SMarc-André Lureau     }
632ae31fb54SMarc-André Lureau 
633b1819747SMarc-André Lureau     if (server->log_fd != -1) {
634b1819747SMarc-André Lureau         close(server->log_fd);
635b1819747SMarc-André Lureau     }
636b1819747SMarc-André Lureau 
637b1819747SMarc-André Lureau     g_free(server->chr_name);
6380c0eb302SMarc-André Lureau 
6397d0ca3e7SPaolo Bonzini     g_main_loop_unref(server->loop);
6407d0ca3e7SPaolo Bonzini     g_main_context_unref(server->context);
6415411f3d0SMarc-André Lureau     g_cond_clear(&server->data_cond);
6425411f3d0SMarc-André Lureau     g_mutex_clear(&server->data_mutex);
643ae31fb54SMarc-André Lureau     g_free(server);
644ae31fb54SMarc-André Lureau }
645ae31fb54SMarc-André Lureau 
646b1819747SMarc-André Lureau static void wait_for_log_fd(TestServer *s)
647b1819747SMarc-André Lureau {
648b1819747SMarc-André Lureau     gint64 end_time;
649b1819747SMarc-André Lureau 
650b1819747SMarc-André Lureau     g_mutex_lock(&s->data_mutex);
651b1819747SMarc-André Lureau     end_time = g_get_monotonic_time() + 5 * G_TIME_SPAN_SECOND;
652b1819747SMarc-André Lureau     while (s->log_fd == -1) {
653b1819747SMarc-André Lureau         if (!g_cond_wait_until(&s->data_cond, &s->data_mutex, end_time)) {
654b1819747SMarc-André Lureau             /* timeout has passed */
655b1819747SMarc-André Lureau             g_assert(s->log_fd != -1);
656b1819747SMarc-André Lureau             break;
657b1819747SMarc-André Lureau         }
658b1819747SMarc-André Lureau     }
659b1819747SMarc-André Lureau 
660b1819747SMarc-André Lureau     g_mutex_unlock(&s->data_mutex);
661b1819747SMarc-André Lureau }
662b1819747SMarc-André Lureau 
6633a87d009SPeter Maydell static void write_guest_mem(TestServer *s, uint32_t seed)
664b1819747SMarc-André Lureau {
665b1819747SMarc-André Lureau     uint32_t *guest_mem;
666b1819747SMarc-André Lureau     int i, j;
667b1819747SMarc-André Lureau     size_t size;
668b1819747SMarc-André Lureau 
669b1819747SMarc-André Lureau     /* iterate all regions */
670b1819747SMarc-André Lureau     for (i = 0; i < s->fds_num; i++) {
671b1819747SMarc-André Lureau 
672b1819747SMarc-André Lureau         /* We'll write only the region statring at 0x0 */
673b1819747SMarc-André Lureau         if (s->memory.regions[i].guest_phys_addr != 0x0) {
674b1819747SMarc-André Lureau             continue;
675b1819747SMarc-André Lureau         }
676b1819747SMarc-André Lureau 
677b1819747SMarc-André Lureau         g_assert_cmpint(s->memory.regions[i].memory_size, >, 1024);
678b1819747SMarc-André Lureau 
679b1819747SMarc-André Lureau         size = s->memory.regions[i].memory_size +
680b1819747SMarc-André Lureau             s->memory.regions[i].mmap_offset;
681b1819747SMarc-André Lureau 
682b1819747SMarc-André Lureau         guest_mem = mmap(0, size, PROT_READ | PROT_WRITE,
683b1819747SMarc-André Lureau                          MAP_SHARED, s->fds[i], 0);
684b1819747SMarc-André Lureau 
685b1819747SMarc-André Lureau         g_assert(guest_mem != MAP_FAILED);
686b1819747SMarc-André Lureau         guest_mem += (s->memory.regions[i].mmap_offset / sizeof(*guest_mem));
687b1819747SMarc-André Lureau 
688b1819747SMarc-André Lureau         for (j = 0; j < 256; j++) {
689b1819747SMarc-André Lureau             guest_mem[j] = seed + j;
690b1819747SMarc-André Lureau         }
691b1819747SMarc-André Lureau 
692b1819747SMarc-André Lureau         munmap(guest_mem, s->memory.regions[i].memory_size);
693b1819747SMarc-André Lureau         break;
694b1819747SMarc-André Lureau     }
695b1819747SMarc-André Lureau }
696b1819747SMarc-André Lureau 
697b1819747SMarc-André Lureau static guint64 get_log_size(TestServer *s)
698b1819747SMarc-André Lureau {
699b1819747SMarc-André Lureau     guint64 log_size = 0;
700b1819747SMarc-André Lureau     int i;
701b1819747SMarc-André Lureau 
702b1819747SMarc-André Lureau     for (i = 0; i < s->memory.nregions; ++i) {
703b1819747SMarc-André Lureau         VhostUserMemoryRegion *reg = &s->memory.regions[i];
704b1819747SMarc-André Lureau         guint64 last = range_get_last(reg->guest_phys_addr,
705b1819747SMarc-André Lureau                                        reg->memory_size);
706b1819747SMarc-André Lureau         log_size = MAX(log_size, last / (8 * VHOST_LOG_PAGE) + 1);
707b1819747SMarc-André Lureau     }
708b1819747SMarc-André Lureau 
709b1819747SMarc-André Lureau     return log_size;
710b1819747SMarc-André Lureau }
711b1819747SMarc-André Lureau 
7121d9edff7SMarc-André Lureau typedef struct TestMigrateSource {
7131d9edff7SMarc-André Lureau     GSource source;
7141d9edff7SMarc-André Lureau     TestServer *src;
7151d9edff7SMarc-André Lureau     TestServer *dest;
7161d9edff7SMarc-André Lureau } TestMigrateSource;
7171d9edff7SMarc-André Lureau 
7181d9edff7SMarc-André Lureau static gboolean
7191d9edff7SMarc-André Lureau test_migrate_source_check(GSource *source)
7201d9edff7SMarc-André Lureau {
7211d9edff7SMarc-André Lureau     TestMigrateSource *t = (TestMigrateSource *)source;
722d08e42a1SMichael S. Tsirkin     gboolean overlap = t->src->rings && t->dest->rings;
7231d9edff7SMarc-André Lureau 
7241d9edff7SMarc-André Lureau     g_assert(!overlap);
7251d9edff7SMarc-André Lureau 
7261d9edff7SMarc-André Lureau     return FALSE;
7271d9edff7SMarc-André Lureau }
7281d9edff7SMarc-André Lureau 
7291d9edff7SMarc-André Lureau GSourceFuncs test_migrate_source_funcs = {
73045ce5126SMarc-André Lureau     .check = test_migrate_source_check,
7311d9edff7SMarc-André Lureau };
7321d9edff7SMarc-André Lureau 
733a3ebd6e0SPaolo Bonzini static void vhost_user_test_cleanup(void *s)
734e364c703SMaxime Coquelin {
735a3ebd6e0SPaolo Bonzini     TestServer *server = s;
736e364c703SMaxime Coquelin 
737a3ebd6e0SPaolo Bonzini     qos_invalidate_command_line();
738e364c703SMaxime Coquelin     test_server_free(server);
739e364c703SMaxime Coquelin }
740e364c703SMaxime Coquelin 
741a3ebd6e0SPaolo Bonzini static void *vhost_user_test_setup(GString *cmd_line, void *arg)
742b1819747SMarc-André Lureau {
743892040dcSDima Stepanov     TestServer *server = test_server_new("vhost-user-test", arg);
744a3ebd6e0SPaolo Bonzini     test_server_listen(server);
745a3ebd6e0SPaolo Bonzini 
746a3ebd6e0SPaolo Bonzini     append_mem_opts(server, cmd_line, 256, TEST_MEMFD_AUTO);
747892040dcSDima Stepanov     server->vu_ops->append_opts(server, cmd_line, "");
748a3ebd6e0SPaolo Bonzini 
749a3ebd6e0SPaolo Bonzini     g_test_queue_destroy(vhost_user_test_cleanup, server);
750a3ebd6e0SPaolo Bonzini 
751a3ebd6e0SPaolo Bonzini     return server;
752a3ebd6e0SPaolo Bonzini }
753a3ebd6e0SPaolo Bonzini 
754a3ebd6e0SPaolo Bonzini static void *vhost_user_test_setup_memfd(GString *cmd_line, void *arg)
755a3ebd6e0SPaolo Bonzini {
756892040dcSDima Stepanov     TestServer *server = test_server_new("vhost-user-test", arg);
757a3ebd6e0SPaolo Bonzini     test_server_listen(server);
758a3ebd6e0SPaolo Bonzini 
759a3ebd6e0SPaolo Bonzini     append_mem_opts(server, cmd_line, 256, TEST_MEMFD_YES);
760892040dcSDima Stepanov     server->vu_ops->append_opts(server, cmd_line, "");
761a3ebd6e0SPaolo Bonzini 
762a3ebd6e0SPaolo Bonzini     g_test_queue_destroy(vhost_user_test_cleanup, server);
763a3ebd6e0SPaolo Bonzini 
764a3ebd6e0SPaolo Bonzini     return server;
765a3ebd6e0SPaolo Bonzini }
766a3ebd6e0SPaolo Bonzini 
767a3ebd6e0SPaolo Bonzini static void test_read_guest_mem(void *obj, void *arg, QGuestAllocator *alloc)
768a3ebd6e0SPaolo Bonzini {
769a3ebd6e0SPaolo Bonzini     TestServer *server = arg;
770a3ebd6e0SPaolo Bonzini 
771a3ebd6e0SPaolo Bonzini     if (!wait_for_fds(server)) {
772a3ebd6e0SPaolo Bonzini         return;
773a3ebd6e0SPaolo Bonzini     }
774a3ebd6e0SPaolo Bonzini 
775a3ebd6e0SPaolo Bonzini     read_guest_mem_server(global_qtest, server);
776a3ebd6e0SPaolo Bonzini }
777a3ebd6e0SPaolo Bonzini 
778a3ebd6e0SPaolo Bonzini static void test_migrate(void *obj, void *arg, QGuestAllocator *alloc)
779a3ebd6e0SPaolo Bonzini {
780a3ebd6e0SPaolo Bonzini     TestServer *s = arg;
78199fd3178SThomas Huth     TestServer *dest;
78299fd3178SThomas Huth     GString *dest_cmdline;
78399fd3178SThomas Huth     char *uri;
784a3ebd6e0SPaolo Bonzini     QTestState *to;
7851d9edff7SMarc-André Lureau     GSource *source;
786b1819747SMarc-André Lureau     QDict *rsp;
787b1819747SMarc-André Lureau     guint8 *log;
788b1819747SMarc-André Lureau     guint64 size;
789b1819747SMarc-André Lureau 
7903b72ca38SPaolo Bonzini     if (!wait_for_fds(s)) {
791a3ebd6e0SPaolo Bonzini         return;
7923b72ca38SPaolo Bonzini     }
7933b72ca38SPaolo Bonzini 
794892040dcSDima Stepanov     dest = test_server_new("dest", s->vu_ops);
79599fd3178SThomas Huth     dest_cmdline = g_string_new(qos_get_current_command_line());
79699fd3178SThomas Huth     uri = g_strdup_printf("%s%s", "unix:", dest->mig_path);
79799fd3178SThomas Huth 
798b1819747SMarc-André Lureau     size = get_log_size(s);
7990f9fe580SPaolo Bonzini     g_assert_cmpint(size, ==, (256 * 1024 * 1024) / (VHOST_LOG_PAGE * 8));
800b1819747SMarc-André Lureau 
801a3ebd6e0SPaolo Bonzini     test_server_listen(dest);
802a3ebd6e0SPaolo Bonzini     g_string_append_printf(dest_cmdline, " -incoming %s", uri);
803a3ebd6e0SPaolo Bonzini     append_mem_opts(dest, dest_cmdline, 256, TEST_MEMFD_AUTO);
804892040dcSDima Stepanov     dest->vu_ops->append_opts(dest, dest_cmdline, "");
805a3ebd6e0SPaolo Bonzini     to = qtest_init(dest_cmdline->str);
806a3ebd6e0SPaolo Bonzini 
807a3ebd6e0SPaolo Bonzini     /* This would be where you call qos_allocate_objects(to, NULL), if you want
808a3ebd6e0SPaolo Bonzini      * to talk to the QVirtioNet object on the destination.
809a3ebd6e0SPaolo Bonzini      */
810b1819747SMarc-André Lureau 
8111d9edff7SMarc-André Lureau     source = g_source_new(&test_migrate_source_funcs,
8121d9edff7SMarc-André Lureau                           sizeof(TestMigrateSource));
8131d9edff7SMarc-André Lureau     ((TestMigrateSource *)source)->src = s;
8141d9edff7SMarc-André Lureau     ((TestMigrateSource *)source)->dest = dest;
8157d0ca3e7SPaolo Bonzini     g_source_attach(source, s->context);
8161d9edff7SMarc-André Lureau 
817b1819747SMarc-André Lureau     /* slow down migration to have time to fiddle with log */
818b1819747SMarc-André Lureau     /* TODO: qtest could learn to break on some places */
819cbde7be9SDaniel P. Berrangé     rsp = qmp("{ 'execute': 'migrate-set-parameters',"
820cbde7be9SDaniel P. Berrangé               "'arguments': { 'max-bandwidth': 10 } }");
821b1819747SMarc-André Lureau     g_assert(qdict_haskey(rsp, "return"));
822cb3e7f08SMarc-André Lureau     qobject_unref(rsp);
823b1819747SMarc-André Lureau 
824015715f5SMarkus Armbruster     rsp = qmp("{ 'execute': 'migrate', 'arguments': { 'uri': %s } }", uri);
825b1819747SMarc-André Lureau     g_assert(qdict_haskey(rsp, "return"));
826cb3e7f08SMarc-André Lureau     qobject_unref(rsp);
827b1819747SMarc-André Lureau 
828b1819747SMarc-André Lureau     wait_for_log_fd(s);
829b1819747SMarc-André Lureau 
830b1819747SMarc-André Lureau     log = mmap(0, size, PROT_READ | PROT_WRITE, MAP_SHARED, s->log_fd, 0);
831b1819747SMarc-André Lureau     g_assert(log != MAP_FAILED);
832b1819747SMarc-André Lureau 
833b1819747SMarc-André Lureau     /* modify first page */
834b1819747SMarc-André Lureau     write_guest_mem(s, 0x42);
835b1819747SMarc-André Lureau     log[0] = 1;
836b1819747SMarc-André Lureau     munmap(log, size);
837b1819747SMarc-André Lureau 
838b1819747SMarc-André Lureau     /* speed things up */
839cbde7be9SDaniel P. Berrangé     rsp = qmp("{ 'execute': 'migrate-set-parameters',"
840cbde7be9SDaniel P. Berrangé               "'arguments': { 'max-bandwidth': 0 } }");
841b1819747SMarc-André Lureau     g_assert(qdict_haskey(rsp, "return"));
842cb3e7f08SMarc-André Lureau     qobject_unref(rsp);
843b1819747SMarc-André Lureau 
844b1819747SMarc-André Lureau     qmp_eventwait("STOP");
845bae6b59dSPaolo Bonzini     qtest_qmp_eventwait(to, "RESUME");
846b1819747SMarc-André Lureau 
847bae6b59dSPaolo Bonzini     g_assert(wait_for_fds(dest));
848bae6b59dSPaolo Bonzini     read_guest_mem_server(to, dest);
849b1819747SMarc-André Lureau 
8501d9edff7SMarc-André Lureau     g_source_destroy(source);
8511d9edff7SMarc-André Lureau     g_source_unref(source);
8521d9edff7SMarc-André Lureau 
853a3ebd6e0SPaolo Bonzini     qtest_quit(to);
854b1819747SMarc-André Lureau     test_server_free(dest);
855a899b1eaSMarc-André Lureau     g_free(uri);
85699fd3178SThomas Huth     g_string_free(dest_cmdline, true);
857b1819747SMarc-André Lureau }
858b1819747SMarc-André Lureau 
8594616e359SMarc-André Lureau static void wait_for_rings_started(TestServer *s, size_t count)
8604616e359SMarc-André Lureau {
8614616e359SMarc-André Lureau     gint64 end_time;
8624616e359SMarc-André Lureau 
8634616e359SMarc-André Lureau     g_mutex_lock(&s->data_mutex);
8644616e359SMarc-André Lureau     end_time = g_get_monotonic_time() + 5 * G_TIME_SPAN_SECOND;
8654616e359SMarc-André Lureau     while (ctpop64(s->rings) != count) {
8664616e359SMarc-André Lureau         if (!g_cond_wait_until(&s->data_cond, &s->data_mutex, end_time)) {
8674616e359SMarc-André Lureau             /* timeout has passed */
8684616e359SMarc-André Lureau             g_assert_cmpint(ctpop64(s->rings), ==, count);
8694616e359SMarc-André Lureau             break;
8704616e359SMarc-André Lureau         }
8714616e359SMarc-André Lureau     }
8724616e359SMarc-André Lureau 
8734616e359SMarc-André Lureau     g_mutex_unlock(&s->data_mutex);
8744616e359SMarc-André Lureau }
8754616e359SMarc-André Lureau 
87620784087SPhilippe Mathieu-Daudé static inline void test_server_connect(TestServer *server)
87720784087SPhilippe Mathieu-Daudé {
87820784087SPhilippe Mathieu-Daudé     test_server_create_chr(server, ",reconnect=1");
87920784087SPhilippe Mathieu-Daudé }
88020784087SPhilippe Mathieu-Daudé 
8814616e359SMarc-André Lureau static gboolean
8824616e359SMarc-André Lureau reconnect_cb(gpointer user_data)
8834616e359SMarc-André Lureau {
8844616e359SMarc-André Lureau     TestServer *s = user_data;
8854616e359SMarc-André Lureau 
8865345fdb4SMarc-André Lureau     qemu_chr_fe_disconnect(&s->chr);
8874616e359SMarc-André Lureau 
8884616e359SMarc-André Lureau     return FALSE;
8894616e359SMarc-André Lureau }
8904616e359SMarc-André Lureau 
8914616e359SMarc-André Lureau static gpointer
8924616e359SMarc-André Lureau connect_thread(gpointer data)
8934616e359SMarc-André Lureau {
8944616e359SMarc-André Lureau     TestServer *s = data;
8954616e359SMarc-André Lureau 
8964616e359SMarc-André Lureau     /* wait for qemu to start before first try, to avoid extra warnings */
8974616e359SMarc-André Lureau     g_usleep(G_USEC_PER_SEC);
8984616e359SMarc-André Lureau     test_server_connect(s);
8994616e359SMarc-André Lureau 
9004616e359SMarc-André Lureau     return NULL;
9014616e359SMarc-André Lureau }
9024616e359SMarc-André Lureau 
903a3ebd6e0SPaolo Bonzini static void *vhost_user_test_setup_reconnect(GString *cmd_line, void *arg)
9044616e359SMarc-André Lureau {
905892040dcSDima Stepanov     TestServer *s = test_server_new("reconnect", arg);
9064616e359SMarc-André Lureau 
9074616e359SMarc-André Lureau     g_thread_new("connect", connect_thread, s);
908a3ebd6e0SPaolo Bonzini     append_mem_opts(s, cmd_line, 256, TEST_MEMFD_AUTO);
909991c180dSPaolo Bonzini     s->vu_ops->append_opts(s, cmd_line, ",server=on");
9104616e359SMarc-André Lureau 
911a3ebd6e0SPaolo Bonzini     g_test_queue_destroy(vhost_user_test_cleanup, s);
912a3ebd6e0SPaolo Bonzini 
913a3ebd6e0SPaolo Bonzini     return s;
914a3ebd6e0SPaolo Bonzini }
915a3ebd6e0SPaolo Bonzini 
916a3ebd6e0SPaolo Bonzini static void test_reconnect(void *obj, void *arg, QGuestAllocator *alloc)
917a3ebd6e0SPaolo Bonzini {
918a3ebd6e0SPaolo Bonzini     TestServer *s = arg;
919a3ebd6e0SPaolo Bonzini     GSource *src;
920a3ebd6e0SPaolo Bonzini 
9213b72ca38SPaolo Bonzini     if (!wait_for_fds(s)) {
922a3ebd6e0SPaolo Bonzini         return;
9233b72ca38SPaolo Bonzini     }
9243b72ca38SPaolo Bonzini 
9254616e359SMarc-André Lureau     wait_for_rings_started(s, 2);
9264616e359SMarc-André Lureau 
9274616e359SMarc-André Lureau     /* reconnect */
9284616e359SMarc-André Lureau     s->fds_num = 0;
9294616e359SMarc-André Lureau     s->rings = 0;
9307d0ca3e7SPaolo Bonzini     src = g_idle_source_new();
9317d0ca3e7SPaolo Bonzini     g_source_set_callback(src, reconnect_cb, s, NULL);
9327d0ca3e7SPaolo Bonzini     g_source_attach(src, s->context);
9337d0ca3e7SPaolo Bonzini     g_source_unref(src);
9343b72ca38SPaolo Bonzini     g_assert(wait_for_fds(s));
9354616e359SMarc-André Lureau     wait_for_rings_started(s, 2);
9364616e359SMarc-André Lureau }
9374616e359SMarc-André Lureau 
938a3ebd6e0SPaolo Bonzini static void *vhost_user_test_setup_connect_fail(GString *cmd_line, void *arg)
9395d443f5aSMarc-André Lureau {
940892040dcSDima Stepanov     TestServer *s = test_server_new("connect-fail", arg);
9415d443f5aSMarc-André Lureau 
9425d443f5aSMarc-André Lureau     s->test_fail = true;
943a3ebd6e0SPaolo Bonzini 
9445d443f5aSMarc-André Lureau     g_thread_new("connect", connect_thread, s);
945a3ebd6e0SPaolo Bonzini     append_mem_opts(s, cmd_line, 256, TEST_MEMFD_AUTO);
946991c180dSPaolo Bonzini     s->vu_ops->append_opts(s, cmd_line, ",server=on");
9475d443f5aSMarc-André Lureau 
948a3ebd6e0SPaolo Bonzini     g_test_queue_destroy(vhost_user_test_cleanup, s);
9495d443f5aSMarc-André Lureau 
950a3ebd6e0SPaolo Bonzini     return s;
9515d443f5aSMarc-André Lureau }
9525d443f5aSMarc-André Lureau 
953a3ebd6e0SPaolo Bonzini static void *vhost_user_test_setup_flags_mismatch(GString *cmd_line, void *arg)
9549294d76cSMarc-André Lureau {
955892040dcSDima Stepanov     TestServer *s = test_server_new("flags-mismatch", arg);
9569294d76cSMarc-André Lureau 
9579294d76cSMarc-André Lureau     s->test_flags = TEST_FLAGS_DISCONNECT;
9589294d76cSMarc-André Lureau 
959a3ebd6e0SPaolo Bonzini     g_thread_new("connect", connect_thread, s);
960a3ebd6e0SPaolo Bonzini     append_mem_opts(s, cmd_line, 256, TEST_MEMFD_AUTO);
961991c180dSPaolo Bonzini     s->vu_ops->append_opts(s, cmd_line, ",server=on");
962a3ebd6e0SPaolo Bonzini 
963a3ebd6e0SPaolo Bonzini     g_test_queue_destroy(vhost_user_test_cleanup, s);
964a3ebd6e0SPaolo Bonzini 
965a3ebd6e0SPaolo Bonzini     return s;
966a3ebd6e0SPaolo Bonzini }
967a3ebd6e0SPaolo Bonzini 
968a3ebd6e0SPaolo Bonzini static void test_vhost_user_started(void *obj, void *arg, QGuestAllocator *alloc)
969a3ebd6e0SPaolo Bonzini {
970a3ebd6e0SPaolo Bonzini     TestServer *s = arg;
971a3ebd6e0SPaolo Bonzini 
9723b72ca38SPaolo Bonzini     if (!wait_for_fds(s)) {
973a3ebd6e0SPaolo Bonzini         return;
9743b72ca38SPaolo Bonzini     }
9759294d76cSMarc-André Lureau     wait_for_rings_started(s, 2);
9769294d76cSMarc-André Lureau }
9779294d76cSMarc-André Lureau 
978a3ebd6e0SPaolo Bonzini static void *vhost_user_test_setup_multiqueue(GString *cmd_line, void *arg)
9799294d76cSMarc-André Lureau {
980a3ebd6e0SPaolo Bonzini     TestServer *s = vhost_user_test_setup(cmd_line, arg);
9819294d76cSMarc-André Lureau 
982459f5d29SMaxime Coquelin     s->queues = 2;
983a3ebd6e0SPaolo Bonzini     g_string_append_printf(cmd_line,
984a3ebd6e0SPaolo Bonzini                            " -set netdev.hs0.queues=%d"
985a3ebd6e0SPaolo Bonzini                            " -global virtio-net-pci.vectors=%d",
986a3ebd6e0SPaolo Bonzini                            s->queues, s->queues * 2 + 2);
987ed0a8d92SMarc-André Lureau 
988a3ebd6e0SPaolo Bonzini     return s;
9898e029fd6SMarc-André Lureau }
990ed0a8d92SMarc-André Lureau 
991a3ebd6e0SPaolo Bonzini static void test_multiqueue(void *obj, void *arg, QGuestAllocator *alloc)
992a3ebd6e0SPaolo Bonzini {
993a3ebd6e0SPaolo Bonzini     TestServer *s = arg;
994ed0a8d92SMarc-André Lureau 
995459f5d29SMaxime Coquelin     wait_for_rings_started(s, s->queues * 2);
996ed0a8d92SMarc-André Lureau }
997ed0a8d92SMarc-André Lureau 
998892040dcSDima Stepanov static void vu_net_set_features(TestServer *s, CharBackend *chr,
999892040dcSDima Stepanov         VhostUserMsg *msg)
1000892040dcSDima Stepanov {
1001f48d994fSAlex Bennée     g_assert(msg->payload.u64 & (0x1ULL << VHOST_USER_F_PROTOCOL_FEATURES));
1002892040dcSDima Stepanov     if (s->test_flags == TEST_FLAGS_DISCONNECT) {
1003892040dcSDima Stepanov         qemu_chr_fe_disconnect(chr);
1004892040dcSDima Stepanov         s->test_flags = TEST_FLAGS_BAD;
1005892040dcSDima Stepanov     }
1006892040dcSDima Stepanov }
1007892040dcSDima Stepanov 
1008892040dcSDima Stepanov static void vu_net_get_protocol_features(TestServer *s, CharBackend *chr,
1009892040dcSDima Stepanov         VhostUserMsg *msg)
1010892040dcSDima Stepanov {
1011892040dcSDima Stepanov     /* send back features to qemu */
1012892040dcSDima Stepanov     msg->flags |= VHOST_USER_REPLY_MASK;
1013892040dcSDima Stepanov     msg->size = sizeof(m.payload.u64);
1014892040dcSDima Stepanov     msg->payload.u64 = 1 << VHOST_USER_PROTOCOL_F_LOG_SHMFD;
1015892040dcSDima Stepanov     msg->payload.u64 |= 1 << VHOST_USER_PROTOCOL_F_CROSS_ENDIAN;
1016892040dcSDima Stepanov     if (s->queues > 1) {
1017892040dcSDima Stepanov         msg->payload.u64 |= 1 << VHOST_USER_PROTOCOL_F_MQ;
1018892040dcSDima Stepanov     }
1019892040dcSDima Stepanov     qemu_chr_fe_write_all(chr, (uint8_t *)msg, VHOST_USER_HDR_SIZE + msg->size);
1020892040dcSDima Stepanov }
1021892040dcSDima Stepanov 
1022892040dcSDima Stepanov /* Each VHOST-USER device should have its ops structure defined. */
1023892040dcSDima Stepanov static struct vhost_user_ops g_vu_net_ops = {
1024892040dcSDima Stepanov     .type = VHOST_USER_NET,
1025892040dcSDima Stepanov 
1026892040dcSDima Stepanov     .append_opts = append_vhost_net_opts,
1027892040dcSDima Stepanov 
1028892040dcSDima Stepanov     .set_features = vu_net_set_features,
1029892040dcSDima Stepanov     .get_protocol_features = vu_net_get_protocol_features,
1030892040dcSDima Stepanov };
1031892040dcSDima Stepanov 
1032a3ebd6e0SPaolo Bonzini static void register_vhost_user_test(void)
1033a77e6b14SNikolay Nikolaev {
1034a3ebd6e0SPaolo Bonzini     QOSGraphTestOptions opts = {
1035a3ebd6e0SPaolo Bonzini         .before = vhost_user_test_setup,
1036a3ebd6e0SPaolo Bonzini         .subprocess = true,
1037892040dcSDima Stepanov         .arg = &g_vu_net_ops,
1038a3ebd6e0SPaolo Bonzini     };
1039a77e6b14SNikolay Nikolaev 
1040ae31fb54SMarc-André Lureau     qemu_add_opts(&qemu_chardev_opts);
1041a77e6b14SNikolay Nikolaev 
1042a3ebd6e0SPaolo Bonzini     qos_add_test("vhost-user/read-guest-mem/memfile",
1043a3ebd6e0SPaolo Bonzini                  "virtio-net",
1044a3ebd6e0SPaolo Bonzini                  test_read_guest_mem, &opts);
1045a3ebd6e0SPaolo Bonzini 
10464a66c7a9SIlya Maximets     if (qemu_memfd_check(MFD_ALLOW_SEALING)) {
1047a3ebd6e0SPaolo Bonzini         opts.before = vhost_user_test_setup_memfd;
1048a3ebd6e0SPaolo Bonzini         qos_add_test("vhost-user/read-guest-mem/memfd",
1049a3ebd6e0SPaolo Bonzini                      "virtio-net",
1050a3ebd6e0SPaolo Bonzini                      test_read_guest_mem, &opts);
10518e029fd6SMarc-André Lureau     }
1052a3ebd6e0SPaolo Bonzini 
1053a3ebd6e0SPaolo Bonzini     qos_add_test("vhost-user/migrate",
1054a3ebd6e0SPaolo Bonzini                  "virtio-net",
1055a3ebd6e0SPaolo Bonzini                  test_migrate, &opts);
105620784087SPhilippe Mathieu-Daudé 
1057a3ebd6e0SPaolo Bonzini     opts.before = vhost_user_test_setup_reconnect;
1058a3ebd6e0SPaolo Bonzini     qos_add_test("vhost-user/reconnect", "virtio-net",
1059a3ebd6e0SPaolo Bonzini                  test_reconnect, &opts);
1060a3ebd6e0SPaolo Bonzini 
1061a3ebd6e0SPaolo Bonzini     opts.before = vhost_user_test_setup_connect_fail;
1062a3ebd6e0SPaolo Bonzini     qos_add_test("vhost-user/connect-fail", "virtio-net",
1063a3ebd6e0SPaolo Bonzini                  test_vhost_user_started, &opts);
1064a3ebd6e0SPaolo Bonzini 
1065a3ebd6e0SPaolo Bonzini     opts.before = vhost_user_test_setup_flags_mismatch;
1066a3ebd6e0SPaolo Bonzini     qos_add_test("vhost-user/flags-mismatch", "virtio-net",
1067a3ebd6e0SPaolo Bonzini                  test_vhost_user_started, &opts);
1068a77e6b14SNikolay Nikolaev 
1069a3ebd6e0SPaolo Bonzini     opts.before = vhost_user_test_setup_multiqueue;
1070a3ebd6e0SPaolo Bonzini     opts.edge.extra_device_opts = "mq=on";
1071a3ebd6e0SPaolo Bonzini     qos_add_test("vhost-user/multiqueue",
1072a3ebd6e0SPaolo Bonzini                  "virtio-net",
1073a3ebd6e0SPaolo Bonzini                  test_multiqueue, &opts);
1074a77e6b14SNikolay Nikolaev }
1075a3ebd6e0SPaolo Bonzini libqos_init(register_vhost_user_test);
1076