xref: /qemu/tests/qtest/vhost-user-test.c (revision b6f53ae005a1c05034769beebf799e861b82d48a)
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"
358fcfc823SAlex Bennée #include "standard-headers/linux/virtio_gpio.h"
36*b6f53ae0SMilan Zamazal #include "standard-headers/linux/virtio_scmi.h"
37af3bba76SPaolo Bonzini 
38af3bba76SPaolo Bonzini #ifdef CONFIG_LINUX
39a77e6b14SNikolay Nikolaev #include <sys/vfs.h>
40af3bba76SPaolo Bonzini #endif
41a77e6b14SNikolay Nikolaev 
4230de46dbSGonglei 
43704b2168SMarc-André Lureau #define QEMU_CMD_MEM    " -m %d -object memory-backend-file,id=mem,size=%dM," \
44a77e6b14SNikolay Nikolaev                         "mem-path=%s,share=on -numa node,memdev=mem"
458e029fd6SMarc-André Lureau #define QEMU_CMD_MEMFD  " -m %d -object memory-backend-memfd,id=mem,size=%dM," \
468e029fd6SMarc-André Lureau                         " -numa node,memdev=mem"
474616e359SMarc-André Lureau #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"
49a77e6b14SNikolay Nikolaev 
50a77e6b14SNikolay Nikolaev #define HUGETLBFS_MAGIC       0x958458f6
51a77e6b14SNikolay Nikolaev 
52a77e6b14SNikolay Nikolaev /*********** FROM hw/virtio/vhost-user.c *************************************/
53a77e6b14SNikolay Nikolaev 
54a77e6b14SNikolay Nikolaev #define VHOST_MEMORY_MAX_NREGIONS    8
55026eb179SMaxime Coquelin #define VHOST_MAX_VIRTQUEUES    0x100
56a77e6b14SNikolay Nikolaev 
578a9b6b37SMichael S. Tsirkin #define VHOST_USER_F_PROTOCOL_FEATURES 30
588fcfc823SAlex Bennée #define VIRTIO_F_VERSION_1 32
598fcfc823SAlex Bennée 
60ed0a8d92SMarc-André Lureau #define VHOST_USER_PROTOCOL_F_MQ 0
61b1819747SMarc-André Lureau #define VHOST_USER_PROTOCOL_F_LOG_SHMFD 1
625a583cc5SPaolo Bonzini #define VHOST_USER_PROTOCOL_F_CROSS_ENDIAN   6
638fcfc823SAlex Bennée #define VHOST_USER_PROTOCOL_F_CONFIG 9
64b1819747SMarc-André Lureau 
65b1819747SMarc-André Lureau #define VHOST_LOG_PAGE 0x1000
668a9b6b37SMichael S. Tsirkin 
67a77e6b14SNikolay Nikolaev typedef enum VhostUserRequest {
68a77e6b14SNikolay Nikolaev     VHOST_USER_NONE = 0,
69a77e6b14SNikolay Nikolaev     VHOST_USER_GET_FEATURES = 1,
70a77e6b14SNikolay Nikolaev     VHOST_USER_SET_FEATURES = 2,
71a77e6b14SNikolay Nikolaev     VHOST_USER_SET_OWNER = 3,
7260915dc4SYuanhan Liu     VHOST_USER_RESET_OWNER = 4,
73a77e6b14SNikolay Nikolaev     VHOST_USER_SET_MEM_TABLE = 5,
74a77e6b14SNikolay Nikolaev     VHOST_USER_SET_LOG_BASE = 6,
75a77e6b14SNikolay Nikolaev     VHOST_USER_SET_LOG_FD = 7,
76a77e6b14SNikolay Nikolaev     VHOST_USER_SET_VRING_NUM = 8,
77a77e6b14SNikolay Nikolaev     VHOST_USER_SET_VRING_ADDR = 9,
78a77e6b14SNikolay Nikolaev     VHOST_USER_SET_VRING_BASE = 10,
79a77e6b14SNikolay Nikolaev     VHOST_USER_GET_VRING_BASE = 11,
80a77e6b14SNikolay Nikolaev     VHOST_USER_SET_VRING_KICK = 12,
81a77e6b14SNikolay Nikolaev     VHOST_USER_SET_VRING_CALL = 13,
82a77e6b14SNikolay Nikolaev     VHOST_USER_SET_VRING_ERR = 14,
838a9b6b37SMichael S. Tsirkin     VHOST_USER_GET_PROTOCOL_FEATURES = 15,
848a9b6b37SMichael S. Tsirkin     VHOST_USER_SET_PROTOCOL_FEATURES = 16,
85ed0a8d92SMarc-André Lureau     VHOST_USER_GET_QUEUE_NUM = 17,
8687656d50SMichael S. Tsirkin     VHOST_USER_SET_VRING_ENABLE = 18,
87ff070f60SAlex Bennée     VHOST_USER_GET_CONFIG = 24,
88ff070f60SAlex Bennée     VHOST_USER_SET_CONFIG = 25,
89a77e6b14SNikolay Nikolaev     VHOST_USER_MAX
90a77e6b14SNikolay Nikolaev } VhostUserRequest;
91a77e6b14SNikolay Nikolaev 
92a77e6b14SNikolay Nikolaev typedef struct VhostUserMemoryRegion {
93a77e6b14SNikolay Nikolaev     uint64_t guest_phys_addr;
94a77e6b14SNikolay Nikolaev     uint64_t memory_size;
95a77e6b14SNikolay Nikolaev     uint64_t userspace_addr;
96d6970e3bSNikolay Nikolaev     uint64_t mmap_offset;
97a77e6b14SNikolay Nikolaev } VhostUserMemoryRegion;
98a77e6b14SNikolay Nikolaev 
99a77e6b14SNikolay Nikolaev typedef struct VhostUserMemory {
100a77e6b14SNikolay Nikolaev     uint32_t nregions;
101a77e6b14SNikolay Nikolaev     uint32_t padding;
102a77e6b14SNikolay Nikolaev     VhostUserMemoryRegion regions[VHOST_MEMORY_MAX_NREGIONS];
103a77e6b14SNikolay Nikolaev } VhostUserMemory;
104a77e6b14SNikolay Nikolaev 
1052b8819c6SVictor Kaplansky typedef struct VhostUserLog {
1062b8819c6SVictor Kaplansky     uint64_t mmap_size;
1072b8819c6SVictor Kaplansky     uint64_t mmap_offset;
1082b8819c6SVictor Kaplansky } VhostUserLog;
1092b8819c6SVictor Kaplansky 
110a77e6b14SNikolay Nikolaev typedef struct VhostUserMsg {
111a77e6b14SNikolay Nikolaev     VhostUserRequest request;
112a77e6b14SNikolay Nikolaev 
113a77e6b14SNikolay Nikolaev #define VHOST_USER_VERSION_MASK     (0x3)
114a77e6b14SNikolay Nikolaev #define VHOST_USER_REPLY_MASK       (0x1<<2)
115a77e6b14SNikolay Nikolaev     uint32_t flags;
116a77e6b14SNikolay Nikolaev     uint32_t size; /* the following payload size */
117a77e6b14SNikolay Nikolaev     union {
1182b8819c6SVictor Kaplansky #define VHOST_USER_VRING_IDX_MASK   (0xff)
1192b8819c6SVictor Kaplansky #define VHOST_USER_VRING_NOFD_MASK  (0x1<<8)
120a77e6b14SNikolay Nikolaev         uint64_t u64;
121a77e6b14SNikolay Nikolaev         struct vhost_vring_state state;
122a77e6b14SNikolay Nikolaev         struct vhost_vring_addr addr;
123a77e6b14SNikolay Nikolaev         VhostUserMemory memory;
1242b8819c6SVictor Kaplansky         VhostUserLog log;
12512ebf690SMichael S. Tsirkin     } payload;
126a77e6b14SNikolay Nikolaev } QEMU_PACKED VhostUserMsg;
127a77e6b14SNikolay Nikolaev 
128a77e6b14SNikolay Nikolaev static VhostUserMsg m __attribute__ ((unused));
129a77e6b14SNikolay Nikolaev #define VHOST_USER_HDR_SIZE (sizeof(m.request) \
130a77e6b14SNikolay Nikolaev                             + sizeof(m.flags) \
131a77e6b14SNikolay Nikolaev                             + sizeof(m.size))
132a77e6b14SNikolay Nikolaev 
133a77e6b14SNikolay Nikolaev #define VHOST_USER_PAYLOAD_SIZE (sizeof(m) - VHOST_USER_HDR_SIZE)
134a77e6b14SNikolay Nikolaev 
135a77e6b14SNikolay Nikolaev /* The version of the protocol we support */
136a77e6b14SNikolay Nikolaev #define VHOST_USER_VERSION    (0x1)
137a77e6b14SNikolay Nikolaev /*****************************************************************************/
138a77e6b14SNikolay Nikolaev 
1399294d76cSMarc-André Lureau enum {
1409294d76cSMarc-André Lureau     TEST_FLAGS_OK,
1419294d76cSMarc-André Lureau     TEST_FLAGS_DISCONNECT,
1429294d76cSMarc-André Lureau     TEST_FLAGS_BAD,
1439294d76cSMarc-André Lureau     TEST_FLAGS_END,
1449294d76cSMarc-André Lureau };
1459294d76cSMarc-André Lureau 
146892040dcSDima Stepanov enum {
147892040dcSDima Stepanov     VHOST_USER_NET,
1488fcfc823SAlex Bennée     VHOST_USER_GPIO,
149*b6f53ae0SMilan Zamazal     VHOST_USER_SCMI,
150892040dcSDima Stepanov };
151892040dcSDima Stepanov 
152ae31fb54SMarc-André Lureau typedef struct TestServer {
153ae31fb54SMarc-André Lureau     gchar *socket_path;
154a899b1eaSMarc-André Lureau     gchar *mig_path;
155ae31fb54SMarc-André Lureau     gchar *chr_name;
1564d3f50ebSPaolo Bonzini     gchar *tmpfs;
15732a6ebecSMarc-André Lureau     CharBackend chr;
158ae31fb54SMarc-André Lureau     int fds_num;
159ae31fb54SMarc-André Lureau     int fds[VHOST_MEMORY_MAX_NREGIONS];
160ae31fb54SMarc-André Lureau     VhostUserMemory memory;
1617d0ca3e7SPaolo Bonzini     GMainContext *context;
1627d0ca3e7SPaolo Bonzini     GMainLoop *loop;
1637d0ca3e7SPaolo Bonzini     GThread *thread;
164e7b3af81SDaniel P. Berrangé     GMutex data_mutex;
165e7b3af81SDaniel P. Berrangé     GCond data_cond;
166b1819747SMarc-André Lureau     int log_fd;
167d08e42a1SMichael S. Tsirkin     uint64_t rings;
1685d443f5aSMarc-André Lureau     bool test_fail;
1699294d76cSMarc-André Lureau     int test_flags;
170ed0a8d92SMarc-André Lureau     int queues;
171892040dcSDima Stepanov     struct vhost_user_ops *vu_ops;
172ae31fb54SMarc-André Lureau } TestServer;
173bd95939fSNikolay Nikolaev 
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 
188a3ebd6e0SPaolo Bonzini static const char *init_hugepagefs(void);
189892040dcSDima Stepanov static TestServer *test_server_new(const gchar *name,
190892040dcSDima Stepanov         struct vhost_user_ops *ops);
19183265145SMarc-André Lureau static void test_server_free(TestServer *server);
19283265145SMarc-André Lureau static void test_server_listen(TestServer *server);
19383265145SMarc-André Lureau 
1948e029fd6SMarc-André Lureau enum test_memfd {
1958e029fd6SMarc-André Lureau     TEST_MEMFD_AUTO,
1968e029fd6SMarc-André Lureau     TEST_MEMFD_YES,
1978e029fd6SMarc-André Lureau     TEST_MEMFD_NO,
1988e029fd6SMarc-André Lureau };
1998e029fd6SMarc-André Lureau 
200892040dcSDima Stepanov static void append_vhost_net_opts(TestServer *s, GString *cmd_line,
201a3ebd6e0SPaolo Bonzini                              const char *chr_opts)
2028e029fd6SMarc-André Lureau {
203a3ebd6e0SPaolo Bonzini     g_string_append_printf(cmd_line, QEMU_CMD_CHR QEMU_CMD_NETDEV,
204a3ebd6e0SPaolo Bonzini                            s->chr_name, s->socket_path,
205a3ebd6e0SPaolo Bonzini                            chr_opts, s->chr_name);
206a3ebd6e0SPaolo Bonzini }
207a3ebd6e0SPaolo Bonzini 
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  */
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 
221a3ebd6e0SPaolo Bonzini static void append_mem_opts(TestServer *server, GString *cmd_line,
222a3ebd6e0SPaolo Bonzini                             int size, enum test_memfd memfd)
223a3ebd6e0SPaolo Bonzini {
224a3ebd6e0SPaolo Bonzini     if (memfd == TEST_MEMFD_AUTO) {
2254a66c7a9SIlya Maximets         memfd = qemu_memfd_check(MFD_ALLOW_SEALING) ? TEST_MEMFD_YES
2264a66c7a9SIlya Maximets                                                     : TEST_MEMFD_NO;
2278e029fd6SMarc-André Lureau     }
2288e029fd6SMarc-André Lureau 
2298e029fd6SMarc-André Lureau     if (memfd == TEST_MEMFD_YES) {
230a3ebd6e0SPaolo Bonzini         g_string_append_printf(cmd_line, QEMU_CMD_MEMFD, size, size);
2318e029fd6SMarc-André Lureau     } else {
232a3ebd6e0SPaolo Bonzini         const char *root = init_hugepagefs() ? : server->tmpfs;
233a3ebd6e0SPaolo Bonzini 
234a3ebd6e0SPaolo Bonzini         g_string_append_printf(cmd_line, QEMU_CMD_MEM, size, size, root);
2358e029fd6SMarc-André Lureau     }
2368e029fd6SMarc-André Lureau }
2378e029fd6SMarc-André Lureau 
2383b72ca38SPaolo Bonzini static bool wait_for_fds(TestServer *s)
239a77e6b14SNikolay Nikolaev {
240a77e6b14SNikolay Nikolaev     gint64 end_time;
2413b72ca38SPaolo Bonzini     bool got_region;
2423b72ca38SPaolo Bonzini     int i;
243a77e6b14SNikolay Nikolaev 
244ae31fb54SMarc-André Lureau     g_mutex_lock(&s->data_mutex);
245a77e6b14SNikolay Nikolaev 
246ca06d9ccSPaolo Bonzini     end_time = g_get_monotonic_time() + 5 * G_TIME_SPAN_SECOND;
247ae31fb54SMarc-André Lureau     while (!s->fds_num) {
248ae31fb54SMarc-André Lureau         if (!g_cond_wait_until(&s->data_cond, &s->data_mutex, end_time)) {
249a77e6b14SNikolay Nikolaev             /* timeout has passed */
250ae31fb54SMarc-André Lureau             g_assert(s->fds_num);
251a77e6b14SNikolay Nikolaev             break;
252a77e6b14SNikolay Nikolaev         }
253a77e6b14SNikolay Nikolaev     }
254a77e6b14SNikolay Nikolaev 
255a77e6b14SNikolay Nikolaev     /* check for sanity */
256ae31fb54SMarc-André Lureau     g_assert_cmpint(s->fds_num, >, 0);
257ae31fb54SMarc-André Lureau     g_assert_cmpint(s->fds_num, ==, s->memory.nregions);
258a77e6b14SNikolay Nikolaev 
259ae31fb54SMarc-André Lureau     g_mutex_unlock(&s->data_mutex);
2603b72ca38SPaolo Bonzini 
2613b72ca38SPaolo Bonzini     got_region = false;
2623b72ca38SPaolo Bonzini     for (i = 0; i < s->memory.nregions; ++i) {
2633b72ca38SPaolo Bonzini         VhostUserMemoryRegion *reg = &s->memory.regions[i];
2643b72ca38SPaolo Bonzini         if (reg->guest_phys_addr == 0) {
2653b72ca38SPaolo Bonzini             got_region = true;
2663b72ca38SPaolo Bonzini             break;
2673b72ca38SPaolo Bonzini         }
2683b72ca38SPaolo Bonzini     }
2693b72ca38SPaolo Bonzini     if (!got_region) {
2703b72ca38SPaolo Bonzini         g_test_skip("No memory at address 0x0");
2713b72ca38SPaolo Bonzini     }
2723b72ca38SPaolo Bonzini     return got_region;
273cf72b57fSMarc-André Lureau }
274cf72b57fSMarc-André Lureau 
275bae6b59dSPaolo Bonzini static void read_guest_mem_server(QTestState *qts, TestServer *s)
276cf72b57fSMarc-André Lureau {
2775a583cc5SPaolo Bonzini     uint8_t *guest_mem;
278cf72b57fSMarc-André Lureau     int i, j;
279cf72b57fSMarc-André Lureau     size_t size;
280cf72b57fSMarc-André Lureau 
281ae31fb54SMarc-André Lureau     g_mutex_lock(&s->data_mutex);
282cf72b57fSMarc-André Lureau 
283a77e6b14SNikolay Nikolaev     /* iterate all regions */
284ae31fb54SMarc-André Lureau     for (i = 0; i < s->fds_num; i++) {
285a77e6b14SNikolay Nikolaev 
286ac9fd9b6SMilan Zamazal         /* We'll check only the region starting at 0x0 */
287ae31fb54SMarc-André Lureau         if (s->memory.regions[i].guest_phys_addr != 0x0) {
288a77e6b14SNikolay Nikolaev             continue;
289a77e6b14SNikolay Nikolaev         }
290a77e6b14SNikolay Nikolaev 
291ae31fb54SMarc-André Lureau         g_assert_cmpint(s->memory.regions[i].memory_size, >, 1024);
292a77e6b14SNikolay Nikolaev 
293ae31fb54SMarc-André Lureau         size = s->memory.regions[i].memory_size +
294ae31fb54SMarc-André Lureau             s->memory.regions[i].mmap_offset;
295d6970e3bSNikolay Nikolaev 
296d6970e3bSNikolay Nikolaev         guest_mem = mmap(0, size, PROT_READ | PROT_WRITE,
297ae31fb54SMarc-André Lureau                          MAP_SHARED, s->fds[i], 0);
298d6970e3bSNikolay Nikolaev 
299d6970e3bSNikolay Nikolaev         g_assert(guest_mem != MAP_FAILED);
300ae31fb54SMarc-André Lureau         guest_mem += (s->memory.regions[i].mmap_offset / sizeof(*guest_mem));
301a77e6b14SNikolay Nikolaev 
3025a583cc5SPaolo Bonzini         for (j = 0; j < 1024; j++) {
303bae6b59dSPaolo Bonzini             uint32_t a = qtest_readb(qts, s->memory.regions[i].guest_phys_addr + j);
304a77e6b14SNikolay Nikolaev             uint32_t b = guest_mem[j];
305a77e6b14SNikolay Nikolaev 
306a77e6b14SNikolay Nikolaev             g_assert_cmpint(a, ==, b);
307a77e6b14SNikolay Nikolaev         }
308a77e6b14SNikolay Nikolaev 
309ae31fb54SMarc-André Lureau         munmap(guest_mem, s->memory.regions[i].memory_size);
310a77e6b14SNikolay Nikolaev     }
311a77e6b14SNikolay Nikolaev 
312ae31fb54SMarc-André Lureau     g_mutex_unlock(&s->data_mutex);
313a77e6b14SNikolay Nikolaev }
314a77e6b14SNikolay Nikolaev 
315a77e6b14SNikolay Nikolaev static void *thread_function(void *data)
316a77e6b14SNikolay Nikolaev {
3179732baf6SMarc-André Lureau     GMainLoop *loop = data;
318a77e6b14SNikolay Nikolaev     g_main_loop_run(loop);
319a77e6b14SNikolay Nikolaev     return NULL;
320a77e6b14SNikolay Nikolaev }
321a77e6b14SNikolay Nikolaev 
322a77e6b14SNikolay Nikolaev static int chr_can_read(void *opaque)
323a77e6b14SNikolay Nikolaev {
324a77e6b14SNikolay Nikolaev     return VHOST_USER_HDR_SIZE;
325a77e6b14SNikolay Nikolaev }
326a77e6b14SNikolay Nikolaev 
327a77e6b14SNikolay Nikolaev static void chr_read(void *opaque, const uint8_t *buf, int size)
328a77e6b14SNikolay Nikolaev {
329b2670d1fSMarc-André Lureau     g_autoptr(GError) err = NULL;
330ae31fb54SMarc-André Lureau     TestServer *s = opaque;
33132a6ebecSMarc-André Lureau     CharBackend *chr = &s->chr;
332a77e6b14SNikolay Nikolaev     VhostUserMsg msg;
333a77e6b14SNikolay Nikolaev     uint8_t *p = (uint8_t *) &msg;
33482248cd4SLi Qiang     int fd = -1;
335a77e6b14SNikolay Nikolaev 
3365d443f5aSMarc-André Lureau     if (s->test_fail) {
3375345fdb4SMarc-André Lureau         qemu_chr_fe_disconnect(chr);
3385d443f5aSMarc-André Lureau         /* now switch to non-failure */
3395d443f5aSMarc-André Lureau         s->test_fail = false;
3405d443f5aSMarc-André Lureau     }
3415d443f5aSMarc-André Lureau 
342a77e6b14SNikolay Nikolaev     if (size != VHOST_USER_HDR_SIZE) {
34330ea13e9SAlex Bennée         qos_printf("%s: Wrong message size received %d\n", __func__, size);
344a77e6b14SNikolay Nikolaev         return;
345a77e6b14SNikolay Nikolaev     }
346a77e6b14SNikolay Nikolaev 
347ae31fb54SMarc-André Lureau     g_mutex_lock(&s->data_mutex);
348a77e6b14SNikolay Nikolaev     memcpy(p, buf, VHOST_USER_HDR_SIZE);
349a77e6b14SNikolay Nikolaev 
350a77e6b14SNikolay Nikolaev     if (msg.size) {
351a77e6b14SNikolay Nikolaev         p += VHOST_USER_HDR_SIZE;
3525345fdb4SMarc-André Lureau         size = qemu_chr_fe_read_all(chr, p, msg.size);
3534616e359SMarc-André Lureau         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;
3574616e359SMarc-André Lureau         }
358a77e6b14SNikolay Nikolaev     }
359a77e6b14SNikolay Nikolaev 
360a77e6b14SNikolay Nikolaev     switch (msg.request) {
361a77e6b14SNikolay Nikolaev     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 
365a77e6b14SNikolay Nikolaev         /* send back features to qemu */
366a77e6b14SNikolay Nikolaev         msg.flags |= VHOST_USER_REPLY_MASK;
36712ebf690SMichael S. Tsirkin         msg.size = sizeof(m.payload.u64);
36819d55a19SAlex Bennée 
3699294d76cSMarc-André Lureau         if (s->test_flags >= TEST_FLAGS_BAD) {
3709294d76cSMarc-André Lureau             msg.payload.u64 = 0;
3719294d76cSMarc-André Lureau             s->test_flags = TEST_FLAGS_END;
37219d55a19SAlex Bennée         } else {
37319d55a19SAlex Bennée             msg.payload.u64 = s->vu_ops->get_features(s);
3749294d76cSMarc-André Lureau         }
37519d55a19SAlex Bennée 
37619d55a19SAlex Bennée         qemu_chr_fe_write_all(chr, (uint8_t *) &msg,
37719d55a19SAlex Bennée                               VHOST_USER_HDR_SIZE + msg.size);
3788a9b6b37SMichael S. Tsirkin         break;
3798a9b6b37SMichael S. Tsirkin 
3808a9b6b37SMichael S. Tsirkin     case VHOST_USER_SET_FEATURES:
381892040dcSDima Stepanov         if (s->vu_ops->set_features) {
382892040dcSDima Stepanov             s->vu_ops->set_features(s, chr, &msg);
3839294d76cSMarc-André Lureau         }
3848a9b6b37SMichael S. Tsirkin         break;
3858a9b6b37SMichael S. Tsirkin 
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 
3948a9b6b37SMichael S. Tsirkin     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);
397ed0a8d92SMarc-André Lureau         }
398a77e6b14SNikolay Nikolaev         break;
399a77e6b14SNikolay Nikolaev 
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 
435a77e6b14SNikolay Nikolaev     case VHOST_USER_GET_VRING_BASE:
436a77e6b14SNikolay Nikolaev         /* send back vring base to qemu */
437a77e6b14SNikolay Nikolaev         msg.flags |= VHOST_USER_REPLY_MASK;
43812ebf690SMichael S. Tsirkin         msg.size = sizeof(m.payload.state);
43912ebf690SMichael S. Tsirkin         msg.payload.state.num = 0;
440a77e6b14SNikolay Nikolaev         p = (uint8_t *) &msg;
4415345fdb4SMarc-André Lureau         qemu_chr_fe_write_all(chr, p, VHOST_USER_HDR_SIZE + msg.size);
442d08e42a1SMichael S. Tsirkin 
443ed0a8d92SMarc-André Lureau         assert(msg.payload.state.index < s->queues * 2);
444d08e42a1SMichael S. Tsirkin         s->rings &= ~(0x1ULL << msg.payload.state.index);
445acca950cSPaolo Bonzini         g_cond_broadcast(&s->data_cond);
446a77e6b14SNikolay Nikolaev         break;
447a77e6b14SNikolay Nikolaev 
448a77e6b14SNikolay Nikolaev     case VHOST_USER_SET_MEM_TABLE:
449a77e6b14SNikolay Nikolaev         /* received the mem table */
45012ebf690SMichael S. Tsirkin         memcpy(&s->memory, &msg.payload.memory, sizeof(msg.payload.memory));
4515345fdb4SMarc-André Lureau         s->fds_num = qemu_chr_fe_get_msgfds(chr, s->fds,
45232a6ebecSMarc-André Lureau                                             G_N_ELEMENTS(s->fds));
453a77e6b14SNikolay Nikolaev 
454a77e6b14SNikolay Nikolaev         /* signal the test that it can continue */
45504ad1bf6SPaolo Bonzini         g_cond_broadcast(&s->data_cond);
456a77e6b14SNikolay Nikolaev         break;
457a77e6b14SNikolay Nikolaev 
458a77e6b14SNikolay Nikolaev     case VHOST_USER_SET_VRING_KICK:
459a77e6b14SNikolay Nikolaev     case VHOST_USER_SET_VRING_CALL:
460a77e6b14SNikolay Nikolaev         /* consume the fd */
4615345fdb4SMarc-André Lureau         qemu_chr_fe_get_msgfds(chr, &fd, 1);
462a77e6b14SNikolay Nikolaev         /*
463a77e6b14SNikolay Nikolaev          * This is a non-blocking eventfd.
464a77e6b14SNikolay Nikolaev          * The receive function forces it to be blocking,
465a77e6b14SNikolay Nikolaev          * so revert it back to non-blocking.
466a77e6b14SNikolay Nikolaev          */
467b2670d1fSMarc-André Lureau         g_unix_set_fd_nonblocking(fd, true, &err);
468b2670d1fSMarc-André Lureau         g_assert_no_error(err);
469a77e6b14SNikolay Nikolaev         break;
470b1819747SMarc-André Lureau 
471b1819747SMarc-André Lureau     case VHOST_USER_SET_LOG_BASE:
472b1819747SMarc-André Lureau         if (s->log_fd != -1) {
473b1819747SMarc-André Lureau             close(s->log_fd);
474b1819747SMarc-André Lureau             s->log_fd = -1;
475b1819747SMarc-André Lureau         }
4765345fdb4SMarc-André Lureau         qemu_chr_fe_get_msgfds(chr, &s->log_fd, 1);
477b1819747SMarc-André Lureau         msg.flags |= VHOST_USER_REPLY_MASK;
478b1819747SMarc-André Lureau         msg.size = 0;
479b1819747SMarc-André Lureau         p = (uint8_t *) &msg;
4805345fdb4SMarc-André Lureau         qemu_chr_fe_write_all(chr, p, VHOST_USER_HDR_SIZE);
481b1819747SMarc-André Lureau 
48204ad1bf6SPaolo Bonzini         g_cond_broadcast(&s->data_cond);
483b1819747SMarc-André Lureau         break;
484b1819747SMarc-André Lureau 
485d08e42a1SMichael S. Tsirkin     case VHOST_USER_SET_VRING_BASE:
486ed0a8d92SMarc-André Lureau         assert(msg.payload.state.index < s->queues * 2);
487d08e42a1SMichael S. Tsirkin         s->rings |= 0x1ULL << msg.payload.state.index;
488acca950cSPaolo Bonzini         g_cond_broadcast(&s->data_cond);
4891d9edff7SMarc-André Lureau         break;
4901d9edff7SMarc-André Lureau 
491ed0a8d92SMarc-André Lureau     case VHOST_USER_GET_QUEUE_NUM:
492ed0a8d92SMarc-André Lureau         msg.flags |= VHOST_USER_REPLY_MASK;
493ed0a8d92SMarc-André Lureau         msg.size = sizeof(m.payload.u64);
494ed0a8d92SMarc-André Lureau         msg.payload.u64 = s->queues;
495ed0a8d92SMarc-André Lureau         p = (uint8_t *) &msg;
4965345fdb4SMarc-André Lureau         qemu_chr_fe_write_all(chr, p, VHOST_USER_HDR_SIZE + msg.size);
497ed0a8d92SMarc-André Lureau         break;
498ed0a8d92SMarc-André Lureau 
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 
509a77e6b14SNikolay Nikolaev     default:
51020a4127fSAlex Bennée         qos_printf("vhost-user: un-handled message: %d\n", msg.request);
511a77e6b14SNikolay Nikolaev         break;
512a77e6b14SNikolay Nikolaev     }
513ae31fb54SMarc-André Lureau 
5143ee7f21eSPaolo Bonzini out:
515ae31fb54SMarc-André Lureau     g_mutex_unlock(&s->data_mutex);
516a77e6b14SNikolay Nikolaev }
517a77e6b14SNikolay Nikolaev 
5189ee1bb86SPaolo Bonzini static const char *init_hugepagefs(void)
519a77e6b14SNikolay Nikolaev {
5209ee1bb86SPaolo Bonzini #ifdef CONFIG_LINUX
521a3ebd6e0SPaolo Bonzini     static const char *hugepagefs;
5229ee1bb86SPaolo Bonzini     const char *path = getenv("QTEST_HUGETLBFS_PATH");
523a77e6b14SNikolay Nikolaev     struct statfs fs;
524a77e6b14SNikolay Nikolaev     int ret;
525a77e6b14SNikolay Nikolaev 
526a3ebd6e0SPaolo Bonzini     if (hugepagefs) {
527a3ebd6e0SPaolo Bonzini         return hugepagefs;
528a3ebd6e0SPaolo Bonzini     }
5299ee1bb86SPaolo Bonzini     if (!path) {
5309ee1bb86SPaolo Bonzini         return NULL;
5319ee1bb86SPaolo Bonzini     }
5329ee1bb86SPaolo Bonzini 
533a77e6b14SNikolay Nikolaev     if (access(path, R_OK | W_OK | X_OK)) {
53430ea13e9SAlex Bennée         qos_printf("access on path (%s): %s", path, strerror(errno));
535a3ebd6e0SPaolo Bonzini         g_test_fail();
536a77e6b14SNikolay Nikolaev         return NULL;
537a77e6b14SNikolay Nikolaev     }
538a77e6b14SNikolay Nikolaev 
539a77e6b14SNikolay Nikolaev     do {
540a77e6b14SNikolay Nikolaev         ret = statfs(path, &fs);
541a77e6b14SNikolay Nikolaev     } while (ret != 0 && errno == EINTR);
542a77e6b14SNikolay Nikolaev 
543a77e6b14SNikolay Nikolaev     if (ret != 0) {
54430ea13e9SAlex Bennée         qos_printf("statfs on path (%s): %s", path, strerror(errno));
545a3ebd6e0SPaolo Bonzini         g_test_fail();
546a77e6b14SNikolay Nikolaev         return NULL;
547a77e6b14SNikolay Nikolaev     }
548a77e6b14SNikolay Nikolaev 
549a77e6b14SNikolay Nikolaev     if (fs.f_type != HUGETLBFS_MAGIC) {
55030ea13e9SAlex Bennée         qos_printf("Warning: path not on HugeTLBFS: %s", path);
551a3ebd6e0SPaolo Bonzini         g_test_fail();
552a77e6b14SNikolay Nikolaev         return NULL;
553a77e6b14SNikolay Nikolaev     }
554a77e6b14SNikolay Nikolaev 
555a3ebd6e0SPaolo Bonzini     hugepagefs = path;
556a3ebd6e0SPaolo Bonzini     return hugepagefs;
5579ee1bb86SPaolo Bonzini #else
5589ee1bb86SPaolo Bonzini     return NULL;
559af3bba76SPaolo Bonzini #endif
5609ee1bb86SPaolo Bonzini }
561a77e6b14SNikolay Nikolaev 
562892040dcSDima Stepanov static TestServer *test_server_new(const gchar *name,
563892040dcSDima Stepanov         struct vhost_user_ops *ops)
564ae31fb54SMarc-André Lureau {
565ae31fb54SMarc-André Lureau     TestServer *server = g_new0(TestServer, 1);
566e6efe236SBin Meng     g_autofree const char *tmpfs = NULL;
567e6efe236SBin Meng     GError *err = NULL;
568ae31fb54SMarc-André Lureau 
5697d0ca3e7SPaolo Bonzini     server->context = g_main_context_new();
5707d0ca3e7SPaolo Bonzini     server->loop = g_main_loop_new(server->context, FALSE);
5717d0ca3e7SPaolo Bonzini 
5727d0ca3e7SPaolo Bonzini     /* run the main loop thread so the chardev may operate */
5737d0ca3e7SPaolo Bonzini     server->thread = g_thread_new(NULL, thread_function, server->loop);
5747d0ca3e7SPaolo Bonzini 
575e6efe236SBin Meng     tmpfs = g_dir_make_tmp("vhost-test-XXXXXX", &err);
5764d3f50ebSPaolo Bonzini     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);
5804d3f50ebSPaolo Bonzini     }
5814d3f50ebSPaolo Bonzini     g_assert(tmpfs);
5824d3f50ebSPaolo Bonzini 
5834d3f50ebSPaolo Bonzini     server->tmpfs = g_strdup(tmpfs);
584ae31fb54SMarc-André Lureau     server->socket_path = g_strdup_printf("%s/%s.sock", tmpfs, name);
585a899b1eaSMarc-André Lureau     server->mig_path = g_strdup_printf("%s/%s.mig", tmpfs, name);
586ae31fb54SMarc-André Lureau     server->chr_name = g_strdup_printf("chr-%s", name);
587ae31fb54SMarc-André Lureau 
588ae31fb54SMarc-André Lureau     g_mutex_init(&server->data_mutex);
589ae31fb54SMarc-André Lureau     g_cond_init(&server->data_cond);
590ae31fb54SMarc-André Lureau 
591b1819747SMarc-André Lureau     server->log_fd = -1;
592ed0a8d92SMarc-André Lureau     server->queues = 1;
593892040dcSDima Stepanov     server->vu_ops = ops;
594b1819747SMarc-André Lureau 
595ae31fb54SMarc-André Lureau     return server;
596ae31fb54SMarc-André Lureau }
597ae31fb54SMarc-André Lureau 
598083b266fSPhilippe Mathieu-Daudé static void chr_event(void *opaque, QEMUChrEvent event)
5999294d76cSMarc-André Lureau {
6009294d76cSMarc-André Lureau     TestServer *s = opaque;
6019294d76cSMarc-André Lureau 
6029294d76cSMarc-André Lureau     if (s->test_flags == TEST_FLAGS_END &&
6039294d76cSMarc-André Lureau         event == CHR_EVENT_CLOSED) {
6049294d76cSMarc-André Lureau         s->test_flags = TEST_FLAGS_OK;
6059294d76cSMarc-André Lureau     }
6069294d76cSMarc-André Lureau }
6079294d76cSMarc-André Lureau 
6084616e359SMarc-André Lureau static void test_server_create_chr(TestServer *server, const gchar *opt)
6094616e359SMarc-André Lureau {
61055c26982SAlex Bennée     g_autofree gchar *chr_path = g_strdup_printf("unix:%s%s",
61155c26982SAlex Bennée                                                  server->socket_path, opt);
6120ec7b3e7SMarc-André Lureau     Chardev *chr;
6135345fdb4SMarc-André Lureau 
6147d0ca3e7SPaolo Bonzini     chr = qemu_chr_new(server->chr_name, chr_path, server->context);
61555c26982SAlex Bennée     g_assert(chr);
6164616e359SMarc-André Lureau 
6175345fdb4SMarc-André Lureau     qemu_chr_fe_init(&server->chr, chr, &error_abort);
6185345fdb4SMarc-André Lureau     qemu_chr_fe_set_handlers(&server->chr, chr_can_read, chr_read,
6197d0ca3e7SPaolo Bonzini                              chr_event, NULL, server, server->context, true);
6204616e359SMarc-André Lureau }
6214616e359SMarc-André Lureau 
6224616e359SMarc-André Lureau static void test_server_listen(TestServer *server)
6234616e359SMarc-André Lureau {
624991c180dSPaolo Bonzini     test_server_create_chr(server, ",server=on,wait=off");
6254616e359SMarc-André Lureau }
6264616e359SMarc-André Lureau 
6277d0ca3e7SPaolo Bonzini static void test_server_free(TestServer *server)
628ae31fb54SMarc-André Lureau {
6294d3f50ebSPaolo Bonzini     int i, ret;
630ae31fb54SMarc-André Lureau 
6317d0ca3e7SPaolo Bonzini     /* finish the helper thread and dispatch pending sources */
6327d0ca3e7SPaolo Bonzini     g_main_loop_quit(server->loop);
6337d0ca3e7SPaolo Bonzini     g_thread_join(server->thread);
6347d0ca3e7SPaolo Bonzini     while (g_main_context_pending(NULL)) {
6357d0ca3e7SPaolo Bonzini         g_main_context_iteration(NULL, TRUE);
6367d0ca3e7SPaolo Bonzini     }
6377d0ca3e7SPaolo Bonzini 
6384d3f50ebSPaolo Bonzini     unlink(server->socket_path);
6394d3f50ebSPaolo Bonzini     g_free(server->socket_path);
6404d3f50ebSPaolo Bonzini 
6414d3f50ebSPaolo Bonzini     unlink(server->mig_path);
6424d3f50ebSPaolo Bonzini     g_free(server->mig_path);
6434d3f50ebSPaolo Bonzini 
6444d3f50ebSPaolo Bonzini     ret = rmdir(server->tmpfs);
6454d3f50ebSPaolo Bonzini     if (ret != 0) {
6464d3f50ebSPaolo Bonzini         g_test_message("unable to rmdir: path (%s): %s",
6474d3f50ebSPaolo Bonzini                        server->tmpfs, strerror(errno));
6484d3f50ebSPaolo Bonzini     }
6495411f3d0SMarc-André Lureau     g_free(server->tmpfs);
6504d3f50ebSPaolo Bonzini 
6511ce2610cSMarc-André Lureau     qemu_chr_fe_deinit(&server->chr, true);
652ae31fb54SMarc-André Lureau 
653ae31fb54SMarc-André Lureau     for (i = 0; i < server->fds_num; i++) {
654ae31fb54SMarc-André Lureau         close(server->fds[i]);
655ae31fb54SMarc-André Lureau     }
656ae31fb54SMarc-André Lureau 
657b1819747SMarc-André Lureau     if (server->log_fd != -1) {
658b1819747SMarc-André Lureau         close(server->log_fd);
659b1819747SMarc-André Lureau     }
660b1819747SMarc-André Lureau 
661b1819747SMarc-André Lureau     g_free(server->chr_name);
6620c0eb302SMarc-André Lureau 
6637d0ca3e7SPaolo Bonzini     g_main_loop_unref(server->loop);
6647d0ca3e7SPaolo Bonzini     g_main_context_unref(server->context);
6655411f3d0SMarc-André Lureau     g_cond_clear(&server->data_cond);
6665411f3d0SMarc-André Lureau     g_mutex_clear(&server->data_mutex);
667ae31fb54SMarc-André Lureau     g_free(server);
668ae31fb54SMarc-André Lureau }
669ae31fb54SMarc-André Lureau 
670b1819747SMarc-André Lureau static void wait_for_log_fd(TestServer *s)
671b1819747SMarc-André Lureau {
672b1819747SMarc-André Lureau     gint64 end_time;
673b1819747SMarc-André Lureau 
674b1819747SMarc-André Lureau     g_mutex_lock(&s->data_mutex);
675b1819747SMarc-André Lureau     end_time = g_get_monotonic_time() + 5 * G_TIME_SPAN_SECOND;
676b1819747SMarc-André Lureau     while (s->log_fd == -1) {
677b1819747SMarc-André Lureau         if (!g_cond_wait_until(&s->data_cond, &s->data_mutex, end_time)) {
678b1819747SMarc-André Lureau             /* timeout has passed */
679b1819747SMarc-André Lureau             g_assert(s->log_fd != -1);
680b1819747SMarc-André Lureau             break;
681b1819747SMarc-André Lureau         }
682b1819747SMarc-André Lureau     }
683b1819747SMarc-André Lureau 
684b1819747SMarc-André Lureau     g_mutex_unlock(&s->data_mutex);
685b1819747SMarc-André Lureau }
686b1819747SMarc-André Lureau 
6873a87d009SPeter Maydell static void write_guest_mem(TestServer *s, uint32_t seed)
688b1819747SMarc-André Lureau {
689b1819747SMarc-André Lureau     uint32_t *guest_mem;
690b1819747SMarc-André Lureau     int i, j;
691b1819747SMarc-André Lureau     size_t size;
692b1819747SMarc-André Lureau 
693b1819747SMarc-André Lureau     /* iterate all regions */
694b1819747SMarc-André Lureau     for (i = 0; i < s->fds_num; i++) {
695b1819747SMarc-André Lureau 
696b1819747SMarc-André Lureau         /* We'll write only the region statring at 0x0 */
697b1819747SMarc-André Lureau         if (s->memory.regions[i].guest_phys_addr != 0x0) {
698b1819747SMarc-André Lureau             continue;
699b1819747SMarc-André Lureau         }
700b1819747SMarc-André Lureau 
701b1819747SMarc-André Lureau         g_assert_cmpint(s->memory.regions[i].memory_size, >, 1024);
702b1819747SMarc-André Lureau 
703b1819747SMarc-André Lureau         size = s->memory.regions[i].memory_size +
704b1819747SMarc-André Lureau             s->memory.regions[i].mmap_offset;
705b1819747SMarc-André Lureau 
706b1819747SMarc-André Lureau         guest_mem = mmap(0, size, PROT_READ | PROT_WRITE,
707b1819747SMarc-André Lureau                          MAP_SHARED, s->fds[i], 0);
708b1819747SMarc-André Lureau 
709b1819747SMarc-André Lureau         g_assert(guest_mem != MAP_FAILED);
710b1819747SMarc-André Lureau         guest_mem += (s->memory.regions[i].mmap_offset / sizeof(*guest_mem));
711b1819747SMarc-André Lureau 
712b1819747SMarc-André Lureau         for (j = 0; j < 256; j++) {
713b1819747SMarc-André Lureau             guest_mem[j] = seed + j;
714b1819747SMarc-André Lureau         }
715b1819747SMarc-André Lureau 
716b1819747SMarc-André Lureau         munmap(guest_mem, s->memory.regions[i].memory_size);
717b1819747SMarc-André Lureau         break;
718b1819747SMarc-André Lureau     }
719b1819747SMarc-André Lureau }
720b1819747SMarc-André Lureau 
721b1819747SMarc-André Lureau static guint64 get_log_size(TestServer *s)
722b1819747SMarc-André Lureau {
723b1819747SMarc-André Lureau     guint64 log_size = 0;
724b1819747SMarc-André Lureau     int i;
725b1819747SMarc-André Lureau 
726b1819747SMarc-André Lureau     for (i = 0; i < s->memory.nregions; ++i) {
727b1819747SMarc-André Lureau         VhostUserMemoryRegion *reg = &s->memory.regions[i];
728b1819747SMarc-André Lureau         guint64 last = range_get_last(reg->guest_phys_addr,
729b1819747SMarc-André Lureau                                        reg->memory_size);
730b1819747SMarc-André Lureau         log_size = MAX(log_size, last / (8 * VHOST_LOG_PAGE) + 1);
731b1819747SMarc-André Lureau     }
732b1819747SMarc-André Lureau 
733b1819747SMarc-André Lureau     return log_size;
734b1819747SMarc-André Lureau }
735b1819747SMarc-André Lureau 
7361d9edff7SMarc-André Lureau typedef struct TestMigrateSource {
7371d9edff7SMarc-André Lureau     GSource source;
7381d9edff7SMarc-André Lureau     TestServer *src;
7391d9edff7SMarc-André Lureau     TestServer *dest;
7401d9edff7SMarc-André Lureau } TestMigrateSource;
7411d9edff7SMarc-André Lureau 
7421d9edff7SMarc-André Lureau static gboolean
7431d9edff7SMarc-André Lureau test_migrate_source_check(GSource *source)
7441d9edff7SMarc-André Lureau {
7451d9edff7SMarc-André Lureau     TestMigrateSource *t = (TestMigrateSource *)source;
746d08e42a1SMichael S. Tsirkin     gboolean overlap = t->src->rings && t->dest->rings;
7471d9edff7SMarc-André Lureau 
7481d9edff7SMarc-André Lureau     g_assert(!overlap);
7491d9edff7SMarc-André Lureau 
7501d9edff7SMarc-André Lureau     return FALSE;
7511d9edff7SMarc-André Lureau }
7521d9edff7SMarc-André Lureau 
7531d9edff7SMarc-André Lureau GSourceFuncs test_migrate_source_funcs = {
75445ce5126SMarc-André Lureau     .check = test_migrate_source_check,
7551d9edff7SMarc-André Lureau };
7561d9edff7SMarc-André Lureau 
757a3ebd6e0SPaolo Bonzini static void vhost_user_test_cleanup(void *s)
758e364c703SMaxime Coquelin {
759a3ebd6e0SPaolo Bonzini     TestServer *server = s;
760e364c703SMaxime Coquelin 
761a3ebd6e0SPaolo Bonzini     qos_invalidate_command_line();
762e364c703SMaxime Coquelin     test_server_free(server);
763e364c703SMaxime Coquelin }
764e364c703SMaxime Coquelin 
765a3ebd6e0SPaolo Bonzini static void *vhost_user_test_setup(GString *cmd_line, void *arg)
766b1819747SMarc-André Lureau {
767892040dcSDima Stepanov     TestServer *server = test_server_new("vhost-user-test", arg);
768a3ebd6e0SPaolo Bonzini     test_server_listen(server);
769a3ebd6e0SPaolo Bonzini 
770a3ebd6e0SPaolo Bonzini     append_mem_opts(server, cmd_line, 256, TEST_MEMFD_AUTO);
771892040dcSDima Stepanov     server->vu_ops->append_opts(server, cmd_line, "");
772a3ebd6e0SPaolo Bonzini 
773a3ebd6e0SPaolo Bonzini     g_test_queue_destroy(vhost_user_test_cleanup, server);
774a3ebd6e0SPaolo Bonzini 
775a3ebd6e0SPaolo Bonzini     return server;
776a3ebd6e0SPaolo Bonzini }
777a3ebd6e0SPaolo Bonzini 
778a3ebd6e0SPaolo Bonzini static void *vhost_user_test_setup_memfd(GString *cmd_line, void *arg)
779a3ebd6e0SPaolo Bonzini {
780892040dcSDima Stepanov     TestServer *server = test_server_new("vhost-user-test", arg);
781a3ebd6e0SPaolo Bonzini     test_server_listen(server);
782a3ebd6e0SPaolo Bonzini 
783a3ebd6e0SPaolo Bonzini     append_mem_opts(server, cmd_line, 256, TEST_MEMFD_YES);
784892040dcSDima Stepanov     server->vu_ops->append_opts(server, cmd_line, "");
785a3ebd6e0SPaolo Bonzini 
786a3ebd6e0SPaolo Bonzini     g_test_queue_destroy(vhost_user_test_cleanup, server);
787a3ebd6e0SPaolo Bonzini 
788a3ebd6e0SPaolo Bonzini     return server;
789a3ebd6e0SPaolo Bonzini }
790a3ebd6e0SPaolo Bonzini 
791a3ebd6e0SPaolo Bonzini static void test_read_guest_mem(void *obj, void *arg, QGuestAllocator *alloc)
792a3ebd6e0SPaolo Bonzini {
793a3ebd6e0SPaolo Bonzini     TestServer *server = arg;
794a3ebd6e0SPaolo Bonzini 
795a3ebd6e0SPaolo Bonzini     if (!wait_for_fds(server)) {
796a3ebd6e0SPaolo Bonzini         return;
797a3ebd6e0SPaolo Bonzini     }
798a3ebd6e0SPaolo Bonzini 
799a3ebd6e0SPaolo Bonzini     read_guest_mem_server(global_qtest, server);
800a3ebd6e0SPaolo Bonzini }
801a3ebd6e0SPaolo Bonzini 
802a3ebd6e0SPaolo Bonzini static void test_migrate(void *obj, void *arg, QGuestAllocator *alloc)
803a3ebd6e0SPaolo Bonzini {
804a3ebd6e0SPaolo Bonzini     TestServer *s = arg;
80599fd3178SThomas Huth     TestServer *dest;
80699fd3178SThomas Huth     GString *dest_cmdline;
80799fd3178SThomas Huth     char *uri;
808a3ebd6e0SPaolo Bonzini     QTestState *to;
8091d9edff7SMarc-André Lureau     GSource *source;
810b1819747SMarc-André Lureau     QDict *rsp;
811b1819747SMarc-André Lureau     guint8 *log;
812b1819747SMarc-André Lureau     guint64 size;
813b1819747SMarc-André Lureau 
8143b72ca38SPaolo Bonzini     if (!wait_for_fds(s)) {
815a3ebd6e0SPaolo Bonzini         return;
8163b72ca38SPaolo Bonzini     }
8173b72ca38SPaolo Bonzini 
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 
822b1819747SMarc-André Lureau     size = get_log_size(s);
8230f9fe580SPaolo Bonzini     g_assert_cmpint(size, ==, (256 * 1024 * 1024) / (VHOST_LOG_PAGE * 8));
824b1819747SMarc-André Lureau 
825a3ebd6e0SPaolo Bonzini     test_server_listen(dest);
826a3ebd6e0SPaolo Bonzini     g_string_append_printf(dest_cmdline, " -incoming %s", uri);
827a3ebd6e0SPaolo Bonzini     append_mem_opts(dest, dest_cmdline, 256, TEST_MEMFD_AUTO);
828892040dcSDima Stepanov     dest->vu_ops->append_opts(dest, dest_cmdline, "");
829a3ebd6e0SPaolo Bonzini     to = qtest_init(dest_cmdline->str);
830a3ebd6e0SPaolo Bonzini 
831a3ebd6e0SPaolo Bonzini     /* This would be where you call qos_allocate_objects(to, NULL), if you want
832a3ebd6e0SPaolo Bonzini      * to talk to the QVirtioNet object on the destination.
833a3ebd6e0SPaolo Bonzini      */
834b1819747SMarc-André Lureau 
8351d9edff7SMarc-André Lureau     source = g_source_new(&test_migrate_source_funcs,
8361d9edff7SMarc-André Lureau                           sizeof(TestMigrateSource));
8371d9edff7SMarc-André Lureau     ((TestMigrateSource *)source)->src = s;
8381d9edff7SMarc-André Lureau     ((TestMigrateSource *)source)->dest = dest;
8397d0ca3e7SPaolo Bonzini     g_source_attach(source, s->context);
8401d9edff7SMarc-André Lureau 
841b1819747SMarc-André Lureau     /* slow down migration to have time to fiddle with log */
842b1819747SMarc-André Lureau     /* 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 } }");
845b1819747SMarc-André Lureau     g_assert(qdict_haskey(rsp, "return"));
846cb3e7f08SMarc-André Lureau     qobject_unref(rsp);
847b1819747SMarc-André Lureau 
848015715f5SMarkus Armbruster     rsp = qmp("{ 'execute': 'migrate', 'arguments': { 'uri': %s } }", uri);
849b1819747SMarc-André Lureau     g_assert(qdict_haskey(rsp, "return"));
850cb3e7f08SMarc-André Lureau     qobject_unref(rsp);
851b1819747SMarc-André Lureau 
852b1819747SMarc-André Lureau     wait_for_log_fd(s);
853b1819747SMarc-André Lureau 
854b1819747SMarc-André Lureau     log = mmap(0, size, PROT_READ | PROT_WRITE, MAP_SHARED, s->log_fd, 0);
855b1819747SMarc-André Lureau     g_assert(log != MAP_FAILED);
856b1819747SMarc-André Lureau 
857b1819747SMarc-André Lureau     /* modify first page */
858b1819747SMarc-André Lureau     write_guest_mem(s, 0x42);
859b1819747SMarc-André Lureau     log[0] = 1;
860b1819747SMarc-André Lureau     munmap(log, size);
861b1819747SMarc-André Lureau 
862b1819747SMarc-André Lureau     /* speed things up */
863cbde7be9SDaniel P. Berrangé     rsp = qmp("{ 'execute': 'migrate-set-parameters',"
864cbde7be9SDaniel P. Berrangé               "'arguments': { 'max-bandwidth': 0 } }");
865b1819747SMarc-André Lureau     g_assert(qdict_haskey(rsp, "return"));
866cb3e7f08SMarc-André Lureau     qobject_unref(rsp);
867b1819747SMarc-André Lureau 
868b1819747SMarc-André Lureau     qmp_eventwait("STOP");
869bae6b59dSPaolo Bonzini     qtest_qmp_eventwait(to, "RESUME");
870b1819747SMarc-André Lureau 
871bae6b59dSPaolo Bonzini     g_assert(wait_for_fds(dest));
872bae6b59dSPaolo Bonzini     read_guest_mem_server(to, dest);
873b1819747SMarc-André Lureau 
8741d9edff7SMarc-André Lureau     g_source_destroy(source);
8751d9edff7SMarc-André Lureau     g_source_unref(source);
8761d9edff7SMarc-André Lureau 
877a3ebd6e0SPaolo Bonzini     qtest_quit(to);
878b1819747SMarc-André Lureau     test_server_free(dest);
879a899b1eaSMarc-André Lureau     g_free(uri);
88099fd3178SThomas Huth     g_string_free(dest_cmdline, true);
881b1819747SMarc-André Lureau }
882b1819747SMarc-André Lureau 
8834616e359SMarc-André Lureau static void wait_for_rings_started(TestServer *s, size_t count)
8844616e359SMarc-André Lureau {
8854616e359SMarc-André Lureau     gint64 end_time;
8864616e359SMarc-André Lureau 
8874616e359SMarc-André Lureau     g_mutex_lock(&s->data_mutex);
8884616e359SMarc-André Lureau     end_time = g_get_monotonic_time() + 5 * G_TIME_SPAN_SECOND;
8894616e359SMarc-André Lureau     while (ctpop64(s->rings) != count) {
8904616e359SMarc-André Lureau         if (!g_cond_wait_until(&s->data_cond, &s->data_mutex, end_time)) {
8914616e359SMarc-André Lureau             /* timeout has passed */
8924616e359SMarc-André Lureau             g_assert_cmpint(ctpop64(s->rings), ==, count);
8934616e359SMarc-André Lureau             break;
8944616e359SMarc-André Lureau         }
8954616e359SMarc-André Lureau     }
8964616e359SMarc-André Lureau 
8974616e359SMarc-André Lureau     g_mutex_unlock(&s->data_mutex);
8984616e359SMarc-André Lureau }
8994616e359SMarc-André Lureau 
90020784087SPhilippe Mathieu-Daudé static inline void test_server_connect(TestServer *server)
90120784087SPhilippe Mathieu-Daudé {
90220784087SPhilippe Mathieu-Daudé     test_server_create_chr(server, ",reconnect=1");
90320784087SPhilippe Mathieu-Daudé }
90420784087SPhilippe Mathieu-Daudé 
9054616e359SMarc-André Lureau static gboolean
9064616e359SMarc-André Lureau reconnect_cb(gpointer user_data)
9074616e359SMarc-André Lureau {
9084616e359SMarc-André Lureau     TestServer *s = user_data;
9094616e359SMarc-André Lureau 
9105345fdb4SMarc-André Lureau     qemu_chr_fe_disconnect(&s->chr);
9114616e359SMarc-André Lureau 
9124616e359SMarc-André Lureau     return FALSE;
9134616e359SMarc-André Lureau }
9144616e359SMarc-André Lureau 
9154616e359SMarc-André Lureau static gpointer
9164616e359SMarc-André Lureau connect_thread(gpointer data)
9174616e359SMarc-André Lureau {
9184616e359SMarc-André Lureau     TestServer *s = data;
9194616e359SMarc-André Lureau 
9204616e359SMarc-André Lureau     /* wait for qemu to start before first try, to avoid extra warnings */
9214616e359SMarc-André Lureau     g_usleep(G_USEC_PER_SEC);
9224616e359SMarc-André Lureau     test_server_connect(s);
9234616e359SMarc-André Lureau 
9244616e359SMarc-André Lureau     return NULL;
9254616e359SMarc-André Lureau }
9264616e359SMarc-André Lureau 
927a3ebd6e0SPaolo Bonzini static void *vhost_user_test_setup_reconnect(GString *cmd_line, void *arg)
9284616e359SMarc-André Lureau {
929892040dcSDima Stepanov     TestServer *s = test_server_new("reconnect", arg);
9304616e359SMarc-André Lureau 
9314616e359SMarc-André Lureau     g_thread_new("connect", connect_thread, s);
932a3ebd6e0SPaolo Bonzini     append_mem_opts(s, cmd_line, 256, TEST_MEMFD_AUTO);
933991c180dSPaolo Bonzini     s->vu_ops->append_opts(s, cmd_line, ",server=on");
9344616e359SMarc-André Lureau 
935a3ebd6e0SPaolo Bonzini     g_test_queue_destroy(vhost_user_test_cleanup, s);
936a3ebd6e0SPaolo Bonzini 
937a3ebd6e0SPaolo Bonzini     return s;
938a3ebd6e0SPaolo Bonzini }
939a3ebd6e0SPaolo Bonzini 
940a3ebd6e0SPaolo Bonzini static void test_reconnect(void *obj, void *arg, QGuestAllocator *alloc)
941a3ebd6e0SPaolo Bonzini {
942a3ebd6e0SPaolo Bonzini     TestServer *s = arg;
943a3ebd6e0SPaolo Bonzini     GSource *src;
944a3ebd6e0SPaolo Bonzini 
9453b72ca38SPaolo Bonzini     if (!wait_for_fds(s)) {
946a3ebd6e0SPaolo Bonzini         return;
9473b72ca38SPaolo Bonzini     }
9483b72ca38SPaolo Bonzini 
9494616e359SMarc-André Lureau     wait_for_rings_started(s, 2);
9504616e359SMarc-André Lureau 
9514616e359SMarc-André Lureau     /* reconnect */
9524616e359SMarc-André Lureau     s->fds_num = 0;
9534616e359SMarc-André Lureau     s->rings = 0;
9547d0ca3e7SPaolo Bonzini     src = g_idle_source_new();
9557d0ca3e7SPaolo Bonzini     g_source_set_callback(src, reconnect_cb, s, NULL);
9567d0ca3e7SPaolo Bonzini     g_source_attach(src, s->context);
9577d0ca3e7SPaolo Bonzini     g_source_unref(src);
9583b72ca38SPaolo Bonzini     g_assert(wait_for_fds(s));
9594616e359SMarc-André Lureau     wait_for_rings_started(s, 2);
9604616e359SMarc-André Lureau }
9614616e359SMarc-André Lureau 
962a3ebd6e0SPaolo Bonzini static void *vhost_user_test_setup_connect_fail(GString *cmd_line, void *arg)
9635d443f5aSMarc-André Lureau {
964892040dcSDima Stepanov     TestServer *s = test_server_new("connect-fail", arg);
9655d443f5aSMarc-André Lureau 
9665d443f5aSMarc-André Lureau     s->test_fail = true;
967a3ebd6e0SPaolo Bonzini 
9685d443f5aSMarc-André Lureau     g_thread_new("connect", connect_thread, s);
969a3ebd6e0SPaolo Bonzini     append_mem_opts(s, cmd_line, 256, TEST_MEMFD_AUTO);
970991c180dSPaolo Bonzini     s->vu_ops->append_opts(s, cmd_line, ",server=on");
9715d443f5aSMarc-André Lureau 
972a3ebd6e0SPaolo Bonzini     g_test_queue_destroy(vhost_user_test_cleanup, s);
9735d443f5aSMarc-André Lureau 
974a3ebd6e0SPaolo Bonzini     return s;
9755d443f5aSMarc-André Lureau }
9765d443f5aSMarc-André Lureau 
977a3ebd6e0SPaolo Bonzini static void *vhost_user_test_setup_flags_mismatch(GString *cmd_line, void *arg)
9789294d76cSMarc-André Lureau {
979892040dcSDima Stepanov     TestServer *s = test_server_new("flags-mismatch", arg);
9809294d76cSMarc-André Lureau 
9819294d76cSMarc-André Lureau     s->test_flags = TEST_FLAGS_DISCONNECT;
9829294d76cSMarc-André Lureau 
983a3ebd6e0SPaolo Bonzini     g_thread_new("connect", connect_thread, s);
984a3ebd6e0SPaolo Bonzini     append_mem_opts(s, cmd_line, 256, TEST_MEMFD_AUTO);
985991c180dSPaolo Bonzini     s->vu_ops->append_opts(s, cmd_line, ",server=on");
986a3ebd6e0SPaolo Bonzini 
987a3ebd6e0SPaolo Bonzini     g_test_queue_destroy(vhost_user_test_cleanup, s);
988a3ebd6e0SPaolo Bonzini 
989a3ebd6e0SPaolo Bonzini     return s;
990a3ebd6e0SPaolo Bonzini }
991a3ebd6e0SPaolo Bonzini 
992a3ebd6e0SPaolo Bonzini static void test_vhost_user_started(void *obj, void *arg, QGuestAllocator *alloc)
993a3ebd6e0SPaolo Bonzini {
994a3ebd6e0SPaolo Bonzini     TestServer *s = arg;
995a3ebd6e0SPaolo Bonzini 
9963b72ca38SPaolo Bonzini     if (!wait_for_fds(s)) {
997a3ebd6e0SPaolo Bonzini         return;
9983b72ca38SPaolo Bonzini     }
9999294d76cSMarc-André Lureau     wait_for_rings_started(s, 2);
10009294d76cSMarc-André Lureau }
10019294d76cSMarc-André Lureau 
1002a3ebd6e0SPaolo Bonzini static void *vhost_user_test_setup_multiqueue(GString *cmd_line, void *arg)
10039294d76cSMarc-André Lureau {
1004a3ebd6e0SPaolo Bonzini     TestServer *s = vhost_user_test_setup(cmd_line, arg);
10059294d76cSMarc-André Lureau 
1006459f5d29SMaxime Coquelin     s->queues = 2;
1007a3ebd6e0SPaolo Bonzini     g_string_append_printf(cmd_line,
1008a3ebd6e0SPaolo Bonzini                            " -set netdev.hs0.queues=%d"
1009a3ebd6e0SPaolo Bonzini                            " -global virtio-net-pci.vectors=%d",
1010a3ebd6e0SPaolo Bonzini                            s->queues, s->queues * 2 + 2);
1011ed0a8d92SMarc-André Lureau 
1012a3ebd6e0SPaolo Bonzini     return s;
10138e029fd6SMarc-André Lureau }
1014ed0a8d92SMarc-André Lureau 
1015a3ebd6e0SPaolo Bonzini static void test_multiqueue(void *obj, void *arg, QGuestAllocator *alloc)
1016a3ebd6e0SPaolo Bonzini {
1017a3ebd6e0SPaolo Bonzini     TestServer *s = arg;
1018ed0a8d92SMarc-André Lureau 
1019459f5d29SMaxime Coquelin     wait_for_rings_started(s, s->queues * 2);
1020ed0a8d92SMarc-André Lureau }
1021ed0a8d92SMarc-André Lureau 
102219d55a19SAlex Bennée 
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 
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 
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 
1070a3ebd6e0SPaolo Bonzini static void register_vhost_user_test(void)
1071a77e6b14SNikolay Nikolaev {
1072a3ebd6e0SPaolo Bonzini     QOSGraphTestOptions opts = {
1073a3ebd6e0SPaolo Bonzini         .before = vhost_user_test_setup,
1074a3ebd6e0SPaolo Bonzini         .subprocess = true,
1075892040dcSDima Stepanov         .arg = &g_vu_net_ops,
1076a3ebd6e0SPaolo Bonzini     };
1077a77e6b14SNikolay Nikolaev 
1078ae31fb54SMarc-André Lureau     qemu_add_opts(&qemu_chardev_opts);
1079a77e6b14SNikolay Nikolaev 
1080a3ebd6e0SPaolo Bonzini     qos_add_test("vhost-user/read-guest-mem/memfile",
1081a3ebd6e0SPaolo Bonzini                  "virtio-net",
1082a3ebd6e0SPaolo Bonzini                  test_read_guest_mem, &opts);
1083a3ebd6e0SPaolo Bonzini 
10844a66c7a9SIlya Maximets     if (qemu_memfd_check(MFD_ALLOW_SEALING)) {
1085a3ebd6e0SPaolo Bonzini         opts.before = vhost_user_test_setup_memfd;
1086a3ebd6e0SPaolo Bonzini         qos_add_test("vhost-user/read-guest-mem/memfd",
1087a3ebd6e0SPaolo Bonzini                      "virtio-net",
1088a3ebd6e0SPaolo Bonzini                      test_read_guest_mem, &opts);
10898e029fd6SMarc-André Lureau     }
1090a3ebd6e0SPaolo Bonzini 
1091a3ebd6e0SPaolo Bonzini     qos_add_test("vhost-user/migrate",
1092a3ebd6e0SPaolo Bonzini                  "virtio-net",
1093a3ebd6e0SPaolo Bonzini                  test_migrate, &opts);
109420784087SPhilippe Mathieu-Daudé 
1095a3ebd6e0SPaolo Bonzini     opts.before = vhost_user_test_setup_reconnect;
1096a3ebd6e0SPaolo Bonzini     qos_add_test("vhost-user/reconnect", "virtio-net",
1097a3ebd6e0SPaolo Bonzini                  test_reconnect, &opts);
1098a3ebd6e0SPaolo Bonzini 
1099a3ebd6e0SPaolo Bonzini     opts.before = vhost_user_test_setup_connect_fail;
1100a3ebd6e0SPaolo Bonzini     qos_add_test("vhost-user/connect-fail", "virtio-net",
1101a3ebd6e0SPaolo Bonzini                  test_vhost_user_started, &opts);
1102a3ebd6e0SPaolo Bonzini 
1103a3ebd6e0SPaolo Bonzini     opts.before = vhost_user_test_setup_flags_mismatch;
1104a3ebd6e0SPaolo Bonzini     qos_add_test("vhost-user/flags-mismatch", "virtio-net",
1105a3ebd6e0SPaolo Bonzini                  test_vhost_user_started, &opts);
1106a77e6b14SNikolay Nikolaev 
1107a3ebd6e0SPaolo Bonzini     opts.before = vhost_user_test_setup_multiqueue;
1108a3ebd6e0SPaolo Bonzini     opts.edge.extra_device_opts = "mq=on";
1109a3ebd6e0SPaolo Bonzini     qos_add_test("vhost-user/multiqueue",
1110a3ebd6e0SPaolo Bonzini                  "virtio-net",
1111a3ebd6e0SPaolo Bonzini                  test_multiqueue, &opts);
1112a77e6b14SNikolay Nikolaev }
1113a3ebd6e0SPaolo Bonzini libqos_init(register_vhost_user_test);
11148fcfc823SAlex Bennée 
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  */
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 
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 
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 
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 
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