xref: /qemu/tests/qtest/vhost-user-test.c (revision 55c269829de131d611103a7e643d48fe3bd08299)
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"
29ed0a8d92SMarc-André Lureau #include "hw/virtio/virtio-net.h"
30ed0a8d92SMarc-André Lureau 
31af3bba76SPaolo Bonzini #include "standard-headers/linux/vhost_types.h"
32af3bba76SPaolo Bonzini #include "standard-headers/linux/virtio_ids.h"
33af3bba76SPaolo Bonzini #include "standard-headers/linux/virtio_net.h"
34af3bba76SPaolo Bonzini 
35af3bba76SPaolo Bonzini #ifdef CONFIG_LINUX
36a77e6b14SNikolay Nikolaev #include <sys/vfs.h>
37af3bba76SPaolo Bonzini #endif
38a77e6b14SNikolay Nikolaev 
3930de46dbSGonglei 
40704b2168SMarc-André Lureau #define QEMU_CMD_MEM    " -m %d -object memory-backend-file,id=mem,size=%dM," \
41a77e6b14SNikolay Nikolaev                         "mem-path=%s,share=on -numa node,memdev=mem"
428e029fd6SMarc-André Lureau #define QEMU_CMD_MEMFD  " -m %d -object memory-backend-memfd,id=mem,size=%dM," \
438e029fd6SMarc-André Lureau                         " -numa node,memdev=mem"
444616e359SMarc-André Lureau #define QEMU_CMD_CHR    " -chardev socket,id=%s,path=%s%s"
45d24d1ad3SEric Auger #define QEMU_CMD_NETDEV " -netdev vhost-user,id=hs0,chardev=%s,vhostforce=on"
46a77e6b14SNikolay Nikolaev 
47a77e6b14SNikolay Nikolaev #define HUGETLBFS_MAGIC       0x958458f6
48a77e6b14SNikolay Nikolaev 
49a77e6b14SNikolay Nikolaev /*********** FROM hw/virtio/vhost-user.c *************************************/
50a77e6b14SNikolay Nikolaev 
51a77e6b14SNikolay Nikolaev #define VHOST_MEMORY_MAX_NREGIONS    8
52026eb179SMaxime Coquelin #define VHOST_MAX_VIRTQUEUES    0x100
53a77e6b14SNikolay Nikolaev 
548a9b6b37SMichael S. Tsirkin #define VHOST_USER_F_PROTOCOL_FEATURES 30
55ed0a8d92SMarc-André Lureau #define VHOST_USER_PROTOCOL_F_MQ 0
56b1819747SMarc-André Lureau #define VHOST_USER_PROTOCOL_F_LOG_SHMFD 1
575a583cc5SPaolo Bonzini #define VHOST_USER_PROTOCOL_F_CROSS_ENDIAN   6
58b1819747SMarc-André Lureau 
59b1819747SMarc-André Lureau #define VHOST_LOG_PAGE 0x1000
608a9b6b37SMichael S. Tsirkin 
61a77e6b14SNikolay Nikolaev typedef enum VhostUserRequest {
62a77e6b14SNikolay Nikolaev     VHOST_USER_NONE = 0,
63a77e6b14SNikolay Nikolaev     VHOST_USER_GET_FEATURES = 1,
64a77e6b14SNikolay Nikolaev     VHOST_USER_SET_FEATURES = 2,
65a77e6b14SNikolay Nikolaev     VHOST_USER_SET_OWNER = 3,
6660915dc4SYuanhan Liu     VHOST_USER_RESET_OWNER = 4,
67a77e6b14SNikolay Nikolaev     VHOST_USER_SET_MEM_TABLE = 5,
68a77e6b14SNikolay Nikolaev     VHOST_USER_SET_LOG_BASE = 6,
69a77e6b14SNikolay Nikolaev     VHOST_USER_SET_LOG_FD = 7,
70a77e6b14SNikolay Nikolaev     VHOST_USER_SET_VRING_NUM = 8,
71a77e6b14SNikolay Nikolaev     VHOST_USER_SET_VRING_ADDR = 9,
72a77e6b14SNikolay Nikolaev     VHOST_USER_SET_VRING_BASE = 10,
73a77e6b14SNikolay Nikolaev     VHOST_USER_GET_VRING_BASE = 11,
74a77e6b14SNikolay Nikolaev     VHOST_USER_SET_VRING_KICK = 12,
75a77e6b14SNikolay Nikolaev     VHOST_USER_SET_VRING_CALL = 13,
76a77e6b14SNikolay Nikolaev     VHOST_USER_SET_VRING_ERR = 14,
778a9b6b37SMichael S. Tsirkin     VHOST_USER_GET_PROTOCOL_FEATURES = 15,
788a9b6b37SMichael S. Tsirkin     VHOST_USER_SET_PROTOCOL_FEATURES = 16,
79ed0a8d92SMarc-André Lureau     VHOST_USER_GET_QUEUE_NUM = 17,
8087656d50SMichael S. Tsirkin     VHOST_USER_SET_VRING_ENABLE = 18,
81a77e6b14SNikolay Nikolaev     VHOST_USER_MAX
82a77e6b14SNikolay Nikolaev } VhostUserRequest;
83a77e6b14SNikolay Nikolaev 
84a77e6b14SNikolay Nikolaev typedef struct VhostUserMemoryRegion {
85a77e6b14SNikolay Nikolaev     uint64_t guest_phys_addr;
86a77e6b14SNikolay Nikolaev     uint64_t memory_size;
87a77e6b14SNikolay Nikolaev     uint64_t userspace_addr;
88d6970e3bSNikolay Nikolaev     uint64_t mmap_offset;
89a77e6b14SNikolay Nikolaev } VhostUserMemoryRegion;
90a77e6b14SNikolay Nikolaev 
91a77e6b14SNikolay Nikolaev typedef struct VhostUserMemory {
92a77e6b14SNikolay Nikolaev     uint32_t nregions;
93a77e6b14SNikolay Nikolaev     uint32_t padding;
94a77e6b14SNikolay Nikolaev     VhostUserMemoryRegion regions[VHOST_MEMORY_MAX_NREGIONS];
95a77e6b14SNikolay Nikolaev } VhostUserMemory;
96a77e6b14SNikolay Nikolaev 
972b8819c6SVictor Kaplansky typedef struct VhostUserLog {
982b8819c6SVictor Kaplansky     uint64_t mmap_size;
992b8819c6SVictor Kaplansky     uint64_t mmap_offset;
1002b8819c6SVictor Kaplansky } VhostUserLog;
1012b8819c6SVictor Kaplansky 
102a77e6b14SNikolay Nikolaev typedef struct VhostUserMsg {
103a77e6b14SNikolay Nikolaev     VhostUserRequest request;
104a77e6b14SNikolay Nikolaev 
105a77e6b14SNikolay Nikolaev #define VHOST_USER_VERSION_MASK     (0x3)
106a77e6b14SNikolay Nikolaev #define VHOST_USER_REPLY_MASK       (0x1<<2)
107a77e6b14SNikolay Nikolaev     uint32_t flags;
108a77e6b14SNikolay Nikolaev     uint32_t size; /* the following payload size */
109a77e6b14SNikolay Nikolaev     union {
1102b8819c6SVictor Kaplansky #define VHOST_USER_VRING_IDX_MASK   (0xff)
1112b8819c6SVictor Kaplansky #define VHOST_USER_VRING_NOFD_MASK  (0x1<<8)
112a77e6b14SNikolay Nikolaev         uint64_t u64;
113a77e6b14SNikolay Nikolaev         struct vhost_vring_state state;
114a77e6b14SNikolay Nikolaev         struct vhost_vring_addr addr;
115a77e6b14SNikolay Nikolaev         VhostUserMemory memory;
1162b8819c6SVictor Kaplansky         VhostUserLog log;
11712ebf690SMichael S. Tsirkin     } payload;
118a77e6b14SNikolay Nikolaev } QEMU_PACKED VhostUserMsg;
119a77e6b14SNikolay Nikolaev 
120a77e6b14SNikolay Nikolaev static VhostUserMsg m __attribute__ ((unused));
121a77e6b14SNikolay Nikolaev #define VHOST_USER_HDR_SIZE (sizeof(m.request) \
122a77e6b14SNikolay Nikolaev                             + sizeof(m.flags) \
123a77e6b14SNikolay Nikolaev                             + sizeof(m.size))
124a77e6b14SNikolay Nikolaev 
125a77e6b14SNikolay Nikolaev #define VHOST_USER_PAYLOAD_SIZE (sizeof(m) - VHOST_USER_HDR_SIZE)
126a77e6b14SNikolay Nikolaev 
127a77e6b14SNikolay Nikolaev /* The version of the protocol we support */
128a77e6b14SNikolay Nikolaev #define VHOST_USER_VERSION    (0x1)
129a77e6b14SNikolay Nikolaev /*****************************************************************************/
130a77e6b14SNikolay Nikolaev 
1319294d76cSMarc-André Lureau enum {
1329294d76cSMarc-André Lureau     TEST_FLAGS_OK,
1339294d76cSMarc-André Lureau     TEST_FLAGS_DISCONNECT,
1349294d76cSMarc-André Lureau     TEST_FLAGS_BAD,
1359294d76cSMarc-André Lureau     TEST_FLAGS_END,
1369294d76cSMarc-André Lureau };
1379294d76cSMarc-André Lureau 
138892040dcSDima Stepanov enum {
139892040dcSDima Stepanov     VHOST_USER_NET,
140892040dcSDima Stepanov };
141892040dcSDima Stepanov 
142ae31fb54SMarc-André Lureau typedef struct TestServer {
143ae31fb54SMarc-André Lureau     gchar *socket_path;
144a899b1eaSMarc-André Lureau     gchar *mig_path;
145ae31fb54SMarc-André Lureau     gchar *chr_name;
1464d3f50ebSPaolo Bonzini     gchar *tmpfs;
14732a6ebecSMarc-André Lureau     CharBackend chr;
148ae31fb54SMarc-André Lureau     int fds_num;
149ae31fb54SMarc-André Lureau     int fds[VHOST_MEMORY_MAX_NREGIONS];
150ae31fb54SMarc-André Lureau     VhostUserMemory memory;
1517d0ca3e7SPaolo Bonzini     GMainContext *context;
1527d0ca3e7SPaolo Bonzini     GMainLoop *loop;
1537d0ca3e7SPaolo Bonzini     GThread *thread;
154e7b3af81SDaniel P. Berrangé     GMutex data_mutex;
155e7b3af81SDaniel P. Berrangé     GCond data_cond;
156b1819747SMarc-André Lureau     int log_fd;
157d08e42a1SMichael S. Tsirkin     uint64_t rings;
1585d443f5aSMarc-André Lureau     bool test_fail;
1599294d76cSMarc-André Lureau     int test_flags;
160ed0a8d92SMarc-André Lureau     int queues;
161892040dcSDima Stepanov     struct vhost_user_ops *vu_ops;
162ae31fb54SMarc-André Lureau } TestServer;
163bd95939fSNikolay Nikolaev 
164892040dcSDima Stepanov struct vhost_user_ops {
165892040dcSDima Stepanov     /* Device types. */
166892040dcSDima Stepanov     int type;
167892040dcSDima Stepanov     void (*append_opts)(TestServer *s, GString *cmd_line,
168892040dcSDima Stepanov             const char *chr_opts);
169892040dcSDima Stepanov 
170892040dcSDima Stepanov     /* VHOST-USER commands. */
171892040dcSDima Stepanov     void (*set_features)(TestServer *s, CharBackend *chr,
172892040dcSDima Stepanov             VhostUserMsg *msg);
173892040dcSDima Stepanov     void (*get_protocol_features)(TestServer *s,
174892040dcSDima Stepanov             CharBackend *chr, VhostUserMsg *msg);
175892040dcSDima Stepanov };
176892040dcSDima Stepanov 
177a3ebd6e0SPaolo Bonzini static const char *init_hugepagefs(void);
178892040dcSDima Stepanov static TestServer *test_server_new(const gchar *name,
179892040dcSDima Stepanov         struct vhost_user_ops *ops);
18083265145SMarc-André Lureau static void test_server_free(TestServer *server);
18183265145SMarc-André Lureau static void test_server_listen(TestServer *server);
18283265145SMarc-André Lureau 
1838e029fd6SMarc-André Lureau enum test_memfd {
1848e029fd6SMarc-André Lureau     TEST_MEMFD_AUTO,
1858e029fd6SMarc-André Lureau     TEST_MEMFD_YES,
1868e029fd6SMarc-André Lureau     TEST_MEMFD_NO,
1878e029fd6SMarc-André Lureau };
1888e029fd6SMarc-André Lureau 
189892040dcSDima Stepanov static void append_vhost_net_opts(TestServer *s, GString *cmd_line,
190a3ebd6e0SPaolo Bonzini                              const char *chr_opts)
1918e029fd6SMarc-André Lureau {
192a3ebd6e0SPaolo Bonzini     g_string_append_printf(cmd_line, QEMU_CMD_CHR QEMU_CMD_NETDEV,
193a3ebd6e0SPaolo Bonzini                            s->chr_name, s->socket_path,
194a3ebd6e0SPaolo Bonzini                            chr_opts, s->chr_name);
195a3ebd6e0SPaolo Bonzini }
196a3ebd6e0SPaolo Bonzini 
197a3ebd6e0SPaolo Bonzini static void append_mem_opts(TestServer *server, GString *cmd_line,
198a3ebd6e0SPaolo Bonzini                             int size, enum test_memfd memfd)
199a3ebd6e0SPaolo Bonzini {
200a3ebd6e0SPaolo Bonzini     if (memfd == TEST_MEMFD_AUTO) {
2014a66c7a9SIlya Maximets         memfd = qemu_memfd_check(MFD_ALLOW_SEALING) ? TEST_MEMFD_YES
2024a66c7a9SIlya Maximets                                                     : TEST_MEMFD_NO;
2038e029fd6SMarc-André Lureau     }
2048e029fd6SMarc-André Lureau 
2058e029fd6SMarc-André Lureau     if (memfd == TEST_MEMFD_YES) {
206a3ebd6e0SPaolo Bonzini         g_string_append_printf(cmd_line, QEMU_CMD_MEMFD, size, size);
2078e029fd6SMarc-André Lureau     } else {
208a3ebd6e0SPaolo Bonzini         const char *root = init_hugepagefs() ? : server->tmpfs;
209a3ebd6e0SPaolo Bonzini 
210a3ebd6e0SPaolo Bonzini         g_string_append_printf(cmd_line, QEMU_CMD_MEM, size, size, root);
2118e029fd6SMarc-André Lureau     }
2128e029fd6SMarc-André Lureau }
2138e029fd6SMarc-André Lureau 
2143b72ca38SPaolo Bonzini static bool wait_for_fds(TestServer *s)
215a77e6b14SNikolay Nikolaev {
216a77e6b14SNikolay Nikolaev     gint64 end_time;
2173b72ca38SPaolo Bonzini     bool got_region;
2183b72ca38SPaolo Bonzini     int i;
219a77e6b14SNikolay Nikolaev 
220ae31fb54SMarc-André Lureau     g_mutex_lock(&s->data_mutex);
221a77e6b14SNikolay Nikolaev 
222ca06d9ccSPaolo Bonzini     end_time = g_get_monotonic_time() + 5 * G_TIME_SPAN_SECOND;
223ae31fb54SMarc-André Lureau     while (!s->fds_num) {
224ae31fb54SMarc-André Lureau         if (!g_cond_wait_until(&s->data_cond, &s->data_mutex, end_time)) {
225a77e6b14SNikolay Nikolaev             /* timeout has passed */
226ae31fb54SMarc-André Lureau             g_assert(s->fds_num);
227a77e6b14SNikolay Nikolaev             break;
228a77e6b14SNikolay Nikolaev         }
229a77e6b14SNikolay Nikolaev     }
230a77e6b14SNikolay Nikolaev 
231a77e6b14SNikolay Nikolaev     /* check for sanity */
232ae31fb54SMarc-André Lureau     g_assert_cmpint(s->fds_num, >, 0);
233ae31fb54SMarc-André Lureau     g_assert_cmpint(s->fds_num, ==, s->memory.nregions);
234a77e6b14SNikolay Nikolaev 
235ae31fb54SMarc-André Lureau     g_mutex_unlock(&s->data_mutex);
2363b72ca38SPaolo Bonzini 
2373b72ca38SPaolo Bonzini     got_region = false;
2383b72ca38SPaolo Bonzini     for (i = 0; i < s->memory.nregions; ++i) {
2393b72ca38SPaolo Bonzini         VhostUserMemoryRegion *reg = &s->memory.regions[i];
2403b72ca38SPaolo Bonzini         if (reg->guest_phys_addr == 0) {
2413b72ca38SPaolo Bonzini             got_region = true;
2423b72ca38SPaolo Bonzini             break;
2433b72ca38SPaolo Bonzini         }
2443b72ca38SPaolo Bonzini     }
2453b72ca38SPaolo Bonzini     if (!got_region) {
2463b72ca38SPaolo Bonzini         g_test_skip("No memory at address 0x0");
2473b72ca38SPaolo Bonzini     }
2483b72ca38SPaolo Bonzini     return got_region;
249cf72b57fSMarc-André Lureau }
250cf72b57fSMarc-André Lureau 
251bae6b59dSPaolo Bonzini static void read_guest_mem_server(QTestState *qts, TestServer *s)
252cf72b57fSMarc-André Lureau {
2535a583cc5SPaolo Bonzini     uint8_t *guest_mem;
254cf72b57fSMarc-André Lureau     int i, j;
255cf72b57fSMarc-André Lureau     size_t size;
256cf72b57fSMarc-André Lureau 
257ae31fb54SMarc-André Lureau     g_mutex_lock(&s->data_mutex);
258cf72b57fSMarc-André Lureau 
259a77e6b14SNikolay Nikolaev     /* iterate all regions */
260ae31fb54SMarc-André Lureau     for (i = 0; i < s->fds_num; i++) {
261a77e6b14SNikolay Nikolaev 
262a77e6b14SNikolay Nikolaev         /* We'll check only the region statring at 0x0*/
263ae31fb54SMarc-André Lureau         if (s->memory.regions[i].guest_phys_addr != 0x0) {
264a77e6b14SNikolay Nikolaev             continue;
265a77e6b14SNikolay Nikolaev         }
266a77e6b14SNikolay Nikolaev 
267ae31fb54SMarc-André Lureau         g_assert_cmpint(s->memory.regions[i].memory_size, >, 1024);
268a77e6b14SNikolay Nikolaev 
269ae31fb54SMarc-André Lureau         size = s->memory.regions[i].memory_size +
270ae31fb54SMarc-André Lureau             s->memory.regions[i].mmap_offset;
271d6970e3bSNikolay Nikolaev 
272d6970e3bSNikolay Nikolaev         guest_mem = mmap(0, size, PROT_READ | PROT_WRITE,
273ae31fb54SMarc-André Lureau                          MAP_SHARED, s->fds[i], 0);
274d6970e3bSNikolay Nikolaev 
275d6970e3bSNikolay Nikolaev         g_assert(guest_mem != MAP_FAILED);
276ae31fb54SMarc-André Lureau         guest_mem += (s->memory.regions[i].mmap_offset / sizeof(*guest_mem));
277a77e6b14SNikolay Nikolaev 
2785a583cc5SPaolo Bonzini         for (j = 0; j < 1024; j++) {
279bae6b59dSPaolo Bonzini             uint32_t a = qtest_readb(qts, s->memory.regions[i].guest_phys_addr + j);
280a77e6b14SNikolay Nikolaev             uint32_t b = guest_mem[j];
281a77e6b14SNikolay Nikolaev 
282a77e6b14SNikolay Nikolaev             g_assert_cmpint(a, ==, b);
283a77e6b14SNikolay Nikolaev         }
284a77e6b14SNikolay Nikolaev 
285ae31fb54SMarc-André Lureau         munmap(guest_mem, s->memory.regions[i].memory_size);
286a77e6b14SNikolay Nikolaev     }
287a77e6b14SNikolay Nikolaev 
288ae31fb54SMarc-André Lureau     g_mutex_unlock(&s->data_mutex);
289a77e6b14SNikolay Nikolaev }
290a77e6b14SNikolay Nikolaev 
291a77e6b14SNikolay Nikolaev static void *thread_function(void *data)
292a77e6b14SNikolay Nikolaev {
2939732baf6SMarc-André Lureau     GMainLoop *loop = data;
294a77e6b14SNikolay Nikolaev     g_main_loop_run(loop);
295a77e6b14SNikolay Nikolaev     return NULL;
296a77e6b14SNikolay Nikolaev }
297a77e6b14SNikolay Nikolaev 
298a77e6b14SNikolay Nikolaev static int chr_can_read(void *opaque)
299a77e6b14SNikolay Nikolaev {
300a77e6b14SNikolay Nikolaev     return VHOST_USER_HDR_SIZE;
301a77e6b14SNikolay Nikolaev }
302a77e6b14SNikolay Nikolaev 
303a77e6b14SNikolay Nikolaev static void chr_read(void *opaque, const uint8_t *buf, int size)
304a77e6b14SNikolay Nikolaev {
305b2670d1fSMarc-André Lureau     g_autoptr(GError) err = NULL;
306ae31fb54SMarc-André Lureau     TestServer *s = opaque;
30732a6ebecSMarc-André Lureau     CharBackend *chr = &s->chr;
308a77e6b14SNikolay Nikolaev     VhostUserMsg msg;
309a77e6b14SNikolay Nikolaev     uint8_t *p = (uint8_t *) &msg;
31082248cd4SLi Qiang     int fd = -1;
311a77e6b14SNikolay Nikolaev 
3125d443f5aSMarc-André Lureau     if (s->test_fail) {
3135345fdb4SMarc-André Lureau         qemu_chr_fe_disconnect(chr);
3145d443f5aSMarc-André Lureau         /* now switch to non-failure */
3155d443f5aSMarc-André Lureau         s->test_fail = false;
3165d443f5aSMarc-André Lureau     }
3175d443f5aSMarc-André Lureau 
318a77e6b14SNikolay Nikolaev     if (size != VHOST_USER_HDR_SIZE) {
31913ee9e30SThomas Huth         g_test_message("Wrong message size received %d", size);
320a77e6b14SNikolay Nikolaev         return;
321a77e6b14SNikolay Nikolaev     }
322a77e6b14SNikolay Nikolaev 
323ae31fb54SMarc-André Lureau     g_mutex_lock(&s->data_mutex);
324a77e6b14SNikolay Nikolaev     memcpy(p, buf, VHOST_USER_HDR_SIZE);
325a77e6b14SNikolay Nikolaev 
326a77e6b14SNikolay Nikolaev     if (msg.size) {
327a77e6b14SNikolay Nikolaev         p += VHOST_USER_HDR_SIZE;
3285345fdb4SMarc-André Lureau         size = qemu_chr_fe_read_all(chr, p, msg.size);
3294616e359SMarc-André Lureau         if (size != msg.size) {
33013ee9e30SThomas Huth             g_test_message("Wrong message size received %d != %d",
3314616e359SMarc-André Lureau                            size, msg.size);
3324616e359SMarc-André Lureau             return;
3334616e359SMarc-André Lureau         }
334a77e6b14SNikolay Nikolaev     }
335a77e6b14SNikolay Nikolaev 
336a77e6b14SNikolay Nikolaev     switch (msg.request) {
337a77e6b14SNikolay Nikolaev     case VHOST_USER_GET_FEATURES:
338a77e6b14SNikolay Nikolaev         /* send back features to qemu */
339a77e6b14SNikolay Nikolaev         msg.flags |= VHOST_USER_REPLY_MASK;
34012ebf690SMichael S. Tsirkin         msg.size = sizeof(m.payload.u64);
34112ebf690SMichael S. Tsirkin         msg.payload.u64 = 0x1ULL << VHOST_F_LOG_ALL |
342b1819747SMarc-André Lureau             0x1ULL << VHOST_USER_F_PROTOCOL_FEATURES;
343ed0a8d92SMarc-André Lureau         if (s->queues > 1) {
344ed0a8d92SMarc-André Lureau             msg.payload.u64 |= 0x1ULL << VIRTIO_NET_F_MQ;
345ed0a8d92SMarc-André Lureau         }
3469294d76cSMarc-André Lureau         if (s->test_flags >= TEST_FLAGS_BAD) {
3479294d76cSMarc-André Lureau             msg.payload.u64 = 0;
3489294d76cSMarc-André Lureau             s->test_flags = TEST_FLAGS_END;
3499294d76cSMarc-André Lureau         }
3508a9b6b37SMichael S. Tsirkin         p = (uint8_t *) &msg;
3515345fdb4SMarc-André Lureau         qemu_chr_fe_write_all(chr, p, VHOST_USER_HDR_SIZE + msg.size);
3528a9b6b37SMichael S. Tsirkin         break;
3538a9b6b37SMichael S. Tsirkin 
3548a9b6b37SMichael S. Tsirkin     case VHOST_USER_SET_FEATURES:
355892040dcSDima Stepanov         if (s->vu_ops->set_features) {
356892040dcSDima Stepanov             s->vu_ops->set_features(s, chr, &msg);
3579294d76cSMarc-André Lureau         }
3588a9b6b37SMichael S. Tsirkin         break;
3598a9b6b37SMichael S. Tsirkin 
3608a9b6b37SMichael S. Tsirkin     case VHOST_USER_GET_PROTOCOL_FEATURES:
361892040dcSDima Stepanov         if (s->vu_ops->get_protocol_features) {
362892040dcSDima Stepanov             s->vu_ops->get_protocol_features(s, chr, &msg);
363ed0a8d92SMarc-André Lureau         }
364a77e6b14SNikolay Nikolaev         break;
365a77e6b14SNikolay Nikolaev 
366a77e6b14SNikolay Nikolaev     case VHOST_USER_GET_VRING_BASE:
367a77e6b14SNikolay Nikolaev         /* send back vring base to qemu */
368a77e6b14SNikolay Nikolaev         msg.flags |= VHOST_USER_REPLY_MASK;
36912ebf690SMichael S. Tsirkin         msg.size = sizeof(m.payload.state);
37012ebf690SMichael S. Tsirkin         msg.payload.state.num = 0;
371a77e6b14SNikolay Nikolaev         p = (uint8_t *) &msg;
3725345fdb4SMarc-André Lureau         qemu_chr_fe_write_all(chr, p, VHOST_USER_HDR_SIZE + msg.size);
373d08e42a1SMichael S. Tsirkin 
374ed0a8d92SMarc-André Lureau         assert(msg.payload.state.index < s->queues * 2);
375d08e42a1SMichael S. Tsirkin         s->rings &= ~(0x1ULL << msg.payload.state.index);
376acca950cSPaolo Bonzini         g_cond_broadcast(&s->data_cond);
377a77e6b14SNikolay Nikolaev         break;
378a77e6b14SNikolay Nikolaev 
379a77e6b14SNikolay Nikolaev     case VHOST_USER_SET_MEM_TABLE:
380a77e6b14SNikolay Nikolaev         /* received the mem table */
38112ebf690SMichael S. Tsirkin         memcpy(&s->memory, &msg.payload.memory, sizeof(msg.payload.memory));
3825345fdb4SMarc-André Lureau         s->fds_num = qemu_chr_fe_get_msgfds(chr, s->fds,
38332a6ebecSMarc-André Lureau                                             G_N_ELEMENTS(s->fds));
384a77e6b14SNikolay Nikolaev 
385a77e6b14SNikolay Nikolaev         /* signal the test that it can continue */
38604ad1bf6SPaolo Bonzini         g_cond_broadcast(&s->data_cond);
387a77e6b14SNikolay Nikolaev         break;
388a77e6b14SNikolay Nikolaev 
389a77e6b14SNikolay Nikolaev     case VHOST_USER_SET_VRING_KICK:
390a77e6b14SNikolay Nikolaev     case VHOST_USER_SET_VRING_CALL:
391a77e6b14SNikolay Nikolaev         /* consume the fd */
3925345fdb4SMarc-André Lureau         qemu_chr_fe_get_msgfds(chr, &fd, 1);
393a77e6b14SNikolay Nikolaev         /*
394a77e6b14SNikolay Nikolaev          * This is a non-blocking eventfd.
395a77e6b14SNikolay Nikolaev          * The receive function forces it to be blocking,
396a77e6b14SNikolay Nikolaev          * so revert it back to non-blocking.
397a77e6b14SNikolay Nikolaev          */
398b2670d1fSMarc-André Lureau         g_unix_set_fd_nonblocking(fd, true, &err);
399b2670d1fSMarc-André Lureau         g_assert_no_error(err);
400a77e6b14SNikolay Nikolaev         break;
401b1819747SMarc-André Lureau 
402b1819747SMarc-André Lureau     case VHOST_USER_SET_LOG_BASE:
403b1819747SMarc-André Lureau         if (s->log_fd != -1) {
404b1819747SMarc-André Lureau             close(s->log_fd);
405b1819747SMarc-André Lureau             s->log_fd = -1;
406b1819747SMarc-André Lureau         }
4075345fdb4SMarc-André Lureau         qemu_chr_fe_get_msgfds(chr, &s->log_fd, 1);
408b1819747SMarc-André Lureau         msg.flags |= VHOST_USER_REPLY_MASK;
409b1819747SMarc-André Lureau         msg.size = 0;
410b1819747SMarc-André Lureau         p = (uint8_t *) &msg;
4115345fdb4SMarc-André Lureau         qemu_chr_fe_write_all(chr, p, VHOST_USER_HDR_SIZE);
412b1819747SMarc-André Lureau 
41304ad1bf6SPaolo Bonzini         g_cond_broadcast(&s->data_cond);
414b1819747SMarc-André Lureau         break;
415b1819747SMarc-André Lureau 
416d08e42a1SMichael S. Tsirkin     case VHOST_USER_SET_VRING_BASE:
417ed0a8d92SMarc-André Lureau         assert(msg.payload.state.index < s->queues * 2);
418d08e42a1SMichael S. Tsirkin         s->rings |= 0x1ULL << msg.payload.state.index;
419acca950cSPaolo Bonzini         g_cond_broadcast(&s->data_cond);
4201d9edff7SMarc-André Lureau         break;
4211d9edff7SMarc-André Lureau 
422ed0a8d92SMarc-André Lureau     case VHOST_USER_GET_QUEUE_NUM:
423ed0a8d92SMarc-André Lureau         msg.flags |= VHOST_USER_REPLY_MASK;
424ed0a8d92SMarc-André Lureau         msg.size = sizeof(m.payload.u64);
425ed0a8d92SMarc-André Lureau         msg.payload.u64 = s->queues;
426ed0a8d92SMarc-André Lureau         p = (uint8_t *) &msg;
4275345fdb4SMarc-André Lureau         qemu_chr_fe_write_all(chr, p, VHOST_USER_HDR_SIZE + msg.size);
428ed0a8d92SMarc-André Lureau         break;
429ed0a8d92SMarc-André Lureau 
430a77e6b14SNikolay Nikolaev     default:
431a77e6b14SNikolay Nikolaev         break;
432a77e6b14SNikolay Nikolaev     }
433ae31fb54SMarc-André Lureau 
434ae31fb54SMarc-André Lureau     g_mutex_unlock(&s->data_mutex);
435a77e6b14SNikolay Nikolaev }
436a77e6b14SNikolay Nikolaev 
4379ee1bb86SPaolo Bonzini static const char *init_hugepagefs(void)
438a77e6b14SNikolay Nikolaev {
4399ee1bb86SPaolo Bonzini #ifdef CONFIG_LINUX
440a3ebd6e0SPaolo Bonzini     static const char *hugepagefs;
4419ee1bb86SPaolo Bonzini     const char *path = getenv("QTEST_HUGETLBFS_PATH");
442a77e6b14SNikolay Nikolaev     struct statfs fs;
443a77e6b14SNikolay Nikolaev     int ret;
444a77e6b14SNikolay Nikolaev 
445a3ebd6e0SPaolo Bonzini     if (hugepagefs) {
446a3ebd6e0SPaolo Bonzini         return hugepagefs;
447a3ebd6e0SPaolo Bonzini     }
4489ee1bb86SPaolo Bonzini     if (!path) {
4499ee1bb86SPaolo Bonzini         return NULL;
4509ee1bb86SPaolo Bonzini     }
4519ee1bb86SPaolo Bonzini 
452a77e6b14SNikolay Nikolaev     if (access(path, R_OK | W_OK | X_OK)) {
45313ee9e30SThomas Huth         g_test_message("access on path (%s): %s", path, strerror(errno));
454a3ebd6e0SPaolo Bonzini         g_test_fail();
455a77e6b14SNikolay Nikolaev         return NULL;
456a77e6b14SNikolay Nikolaev     }
457a77e6b14SNikolay Nikolaev 
458a77e6b14SNikolay Nikolaev     do {
459a77e6b14SNikolay Nikolaev         ret = statfs(path, &fs);
460a77e6b14SNikolay Nikolaev     } while (ret != 0 && errno == EINTR);
461a77e6b14SNikolay Nikolaev 
462a77e6b14SNikolay Nikolaev     if (ret != 0) {
46313ee9e30SThomas Huth         g_test_message("statfs on path (%s): %s", path, strerror(errno));
464a3ebd6e0SPaolo Bonzini         g_test_fail();
465a77e6b14SNikolay Nikolaev         return NULL;
466a77e6b14SNikolay Nikolaev     }
467a77e6b14SNikolay Nikolaev 
468a77e6b14SNikolay Nikolaev     if (fs.f_type != HUGETLBFS_MAGIC) {
46913ee9e30SThomas Huth         g_test_message("Warning: path not on HugeTLBFS: %s", path);
470a3ebd6e0SPaolo Bonzini         g_test_fail();
471a77e6b14SNikolay Nikolaev         return NULL;
472a77e6b14SNikolay Nikolaev     }
473a77e6b14SNikolay Nikolaev 
474a3ebd6e0SPaolo Bonzini     hugepagefs = path;
475a3ebd6e0SPaolo Bonzini     return hugepagefs;
4769ee1bb86SPaolo Bonzini #else
4779ee1bb86SPaolo Bonzini     return NULL;
478af3bba76SPaolo Bonzini #endif
4799ee1bb86SPaolo Bonzini }
480a77e6b14SNikolay Nikolaev 
481892040dcSDima Stepanov static TestServer *test_server_new(const gchar *name,
482892040dcSDima Stepanov         struct vhost_user_ops *ops)
483ae31fb54SMarc-André Lureau {
484ae31fb54SMarc-André Lureau     TestServer *server = g_new0(TestServer, 1);
4854d3f50ebSPaolo Bonzini     char template[] = "/tmp/vhost-test-XXXXXX";
4864d3f50ebSPaolo Bonzini     const char *tmpfs;
487ae31fb54SMarc-André Lureau 
4887d0ca3e7SPaolo Bonzini     server->context = g_main_context_new();
4897d0ca3e7SPaolo Bonzini     server->loop = g_main_loop_new(server->context, FALSE);
4907d0ca3e7SPaolo Bonzini 
4917d0ca3e7SPaolo Bonzini     /* run the main loop thread so the chardev may operate */
4927d0ca3e7SPaolo Bonzini     server->thread = g_thread_new(NULL, thread_function, server->loop);
4937d0ca3e7SPaolo Bonzini 
4944d3f50ebSPaolo Bonzini     tmpfs = mkdtemp(template);
4954d3f50ebSPaolo Bonzini     if (!tmpfs) {
4964d3f50ebSPaolo Bonzini         g_test_message("mkdtemp on path (%s): %s", template, strerror(errno));
4974d3f50ebSPaolo Bonzini     }
4984d3f50ebSPaolo Bonzini     g_assert(tmpfs);
4994d3f50ebSPaolo Bonzini 
5004d3f50ebSPaolo Bonzini     server->tmpfs = g_strdup(tmpfs);
501ae31fb54SMarc-André Lureau     server->socket_path = g_strdup_printf("%s/%s.sock", tmpfs, name);
502a899b1eaSMarc-André Lureau     server->mig_path = g_strdup_printf("%s/%s.mig", tmpfs, name);
503ae31fb54SMarc-André Lureau     server->chr_name = g_strdup_printf("chr-%s", name);
504ae31fb54SMarc-André Lureau 
505ae31fb54SMarc-André Lureau     g_mutex_init(&server->data_mutex);
506ae31fb54SMarc-André Lureau     g_cond_init(&server->data_cond);
507ae31fb54SMarc-André Lureau 
508b1819747SMarc-André Lureau     server->log_fd = -1;
509ed0a8d92SMarc-André Lureau     server->queues = 1;
510892040dcSDima Stepanov     server->vu_ops = ops;
511b1819747SMarc-André Lureau 
512ae31fb54SMarc-André Lureau     return server;
513ae31fb54SMarc-André Lureau }
514ae31fb54SMarc-André Lureau 
515083b266fSPhilippe Mathieu-Daudé static void chr_event(void *opaque, QEMUChrEvent event)
5169294d76cSMarc-André Lureau {
5179294d76cSMarc-André Lureau     TestServer *s = opaque;
5189294d76cSMarc-André Lureau 
5199294d76cSMarc-André Lureau     if (s->test_flags == TEST_FLAGS_END &&
5209294d76cSMarc-André Lureau         event == CHR_EVENT_CLOSED) {
5219294d76cSMarc-André Lureau         s->test_flags = TEST_FLAGS_OK;
5229294d76cSMarc-André Lureau     }
5239294d76cSMarc-André Lureau }
5249294d76cSMarc-André Lureau 
5254616e359SMarc-André Lureau static void test_server_create_chr(TestServer *server, const gchar *opt)
5264616e359SMarc-André Lureau {
527*55c26982SAlex Bennée     g_autofree gchar *chr_path = g_strdup_printf("unix:%s%s",
528*55c26982SAlex Bennée                                                  server->socket_path, opt);
5290ec7b3e7SMarc-André Lureau     Chardev *chr;
5305345fdb4SMarc-André Lureau 
5317d0ca3e7SPaolo Bonzini     chr = qemu_chr_new(server->chr_name, chr_path, server->context);
532*55c26982SAlex Bennée     g_assert(chr);
5334616e359SMarc-André Lureau 
5345345fdb4SMarc-André Lureau     qemu_chr_fe_init(&server->chr, chr, &error_abort);
5355345fdb4SMarc-André Lureau     qemu_chr_fe_set_handlers(&server->chr, chr_can_read, chr_read,
5367d0ca3e7SPaolo Bonzini                              chr_event, NULL, server, server->context, true);
5374616e359SMarc-André Lureau }
5384616e359SMarc-André Lureau 
5394616e359SMarc-André Lureau static void test_server_listen(TestServer *server)
5404616e359SMarc-André Lureau {
541991c180dSPaolo Bonzini     test_server_create_chr(server, ",server=on,wait=off");
5424616e359SMarc-André Lureau }
5434616e359SMarc-André Lureau 
5447d0ca3e7SPaolo Bonzini static void test_server_free(TestServer *server)
545ae31fb54SMarc-André Lureau {
5464d3f50ebSPaolo Bonzini     int i, ret;
547ae31fb54SMarc-André Lureau 
5487d0ca3e7SPaolo Bonzini     /* finish the helper thread and dispatch pending sources */
5497d0ca3e7SPaolo Bonzini     g_main_loop_quit(server->loop);
5507d0ca3e7SPaolo Bonzini     g_thread_join(server->thread);
5517d0ca3e7SPaolo Bonzini     while (g_main_context_pending(NULL)) {
5527d0ca3e7SPaolo Bonzini         g_main_context_iteration(NULL, TRUE);
5537d0ca3e7SPaolo Bonzini     }
5547d0ca3e7SPaolo Bonzini 
5554d3f50ebSPaolo Bonzini     unlink(server->socket_path);
5564d3f50ebSPaolo Bonzini     g_free(server->socket_path);
5574d3f50ebSPaolo Bonzini 
5584d3f50ebSPaolo Bonzini     unlink(server->mig_path);
5594d3f50ebSPaolo Bonzini     g_free(server->mig_path);
5604d3f50ebSPaolo Bonzini 
5614d3f50ebSPaolo Bonzini     ret = rmdir(server->tmpfs);
5624d3f50ebSPaolo Bonzini     if (ret != 0) {
5634d3f50ebSPaolo Bonzini         g_test_message("unable to rmdir: path (%s): %s",
5644d3f50ebSPaolo Bonzini                        server->tmpfs, strerror(errno));
5654d3f50ebSPaolo Bonzini     }
5665411f3d0SMarc-André Lureau     g_free(server->tmpfs);
5674d3f50ebSPaolo Bonzini 
5681ce2610cSMarc-André Lureau     qemu_chr_fe_deinit(&server->chr, true);
569ae31fb54SMarc-André Lureau 
570ae31fb54SMarc-André Lureau     for (i = 0; i < server->fds_num; i++) {
571ae31fb54SMarc-André Lureau         close(server->fds[i]);
572ae31fb54SMarc-André Lureau     }
573ae31fb54SMarc-André Lureau 
574b1819747SMarc-André Lureau     if (server->log_fd != -1) {
575b1819747SMarc-André Lureau         close(server->log_fd);
576b1819747SMarc-André Lureau     }
577b1819747SMarc-André Lureau 
578b1819747SMarc-André Lureau     g_free(server->chr_name);
5790c0eb302SMarc-André Lureau 
5807d0ca3e7SPaolo Bonzini     g_main_loop_unref(server->loop);
5817d0ca3e7SPaolo Bonzini     g_main_context_unref(server->context);
5825411f3d0SMarc-André Lureau     g_cond_clear(&server->data_cond);
5835411f3d0SMarc-André Lureau     g_mutex_clear(&server->data_mutex);
584ae31fb54SMarc-André Lureau     g_free(server);
585ae31fb54SMarc-André Lureau }
586ae31fb54SMarc-André Lureau 
587b1819747SMarc-André Lureau static void wait_for_log_fd(TestServer *s)
588b1819747SMarc-André Lureau {
589b1819747SMarc-André Lureau     gint64 end_time;
590b1819747SMarc-André Lureau 
591b1819747SMarc-André Lureau     g_mutex_lock(&s->data_mutex);
592b1819747SMarc-André Lureau     end_time = g_get_monotonic_time() + 5 * G_TIME_SPAN_SECOND;
593b1819747SMarc-André Lureau     while (s->log_fd == -1) {
594b1819747SMarc-André Lureau         if (!g_cond_wait_until(&s->data_cond, &s->data_mutex, end_time)) {
595b1819747SMarc-André Lureau             /* timeout has passed */
596b1819747SMarc-André Lureau             g_assert(s->log_fd != -1);
597b1819747SMarc-André Lureau             break;
598b1819747SMarc-André Lureau         }
599b1819747SMarc-André Lureau     }
600b1819747SMarc-André Lureau 
601b1819747SMarc-André Lureau     g_mutex_unlock(&s->data_mutex);
602b1819747SMarc-André Lureau }
603b1819747SMarc-André Lureau 
6043a87d009SPeter Maydell static void write_guest_mem(TestServer *s, uint32_t seed)
605b1819747SMarc-André Lureau {
606b1819747SMarc-André Lureau     uint32_t *guest_mem;
607b1819747SMarc-André Lureau     int i, j;
608b1819747SMarc-André Lureau     size_t size;
609b1819747SMarc-André Lureau 
610b1819747SMarc-André Lureau     /* iterate all regions */
611b1819747SMarc-André Lureau     for (i = 0; i < s->fds_num; i++) {
612b1819747SMarc-André Lureau 
613b1819747SMarc-André Lureau         /* We'll write only the region statring at 0x0 */
614b1819747SMarc-André Lureau         if (s->memory.regions[i].guest_phys_addr != 0x0) {
615b1819747SMarc-André Lureau             continue;
616b1819747SMarc-André Lureau         }
617b1819747SMarc-André Lureau 
618b1819747SMarc-André Lureau         g_assert_cmpint(s->memory.regions[i].memory_size, >, 1024);
619b1819747SMarc-André Lureau 
620b1819747SMarc-André Lureau         size = s->memory.regions[i].memory_size +
621b1819747SMarc-André Lureau             s->memory.regions[i].mmap_offset;
622b1819747SMarc-André Lureau 
623b1819747SMarc-André Lureau         guest_mem = mmap(0, size, PROT_READ | PROT_WRITE,
624b1819747SMarc-André Lureau                          MAP_SHARED, s->fds[i], 0);
625b1819747SMarc-André Lureau 
626b1819747SMarc-André Lureau         g_assert(guest_mem != MAP_FAILED);
627b1819747SMarc-André Lureau         guest_mem += (s->memory.regions[i].mmap_offset / sizeof(*guest_mem));
628b1819747SMarc-André Lureau 
629b1819747SMarc-André Lureau         for (j = 0; j < 256; j++) {
630b1819747SMarc-André Lureau             guest_mem[j] = seed + j;
631b1819747SMarc-André Lureau         }
632b1819747SMarc-André Lureau 
633b1819747SMarc-André Lureau         munmap(guest_mem, s->memory.regions[i].memory_size);
634b1819747SMarc-André Lureau         break;
635b1819747SMarc-André Lureau     }
636b1819747SMarc-André Lureau }
637b1819747SMarc-André Lureau 
638b1819747SMarc-André Lureau static guint64 get_log_size(TestServer *s)
639b1819747SMarc-André Lureau {
640b1819747SMarc-André Lureau     guint64 log_size = 0;
641b1819747SMarc-André Lureau     int i;
642b1819747SMarc-André Lureau 
643b1819747SMarc-André Lureau     for (i = 0; i < s->memory.nregions; ++i) {
644b1819747SMarc-André Lureau         VhostUserMemoryRegion *reg = &s->memory.regions[i];
645b1819747SMarc-André Lureau         guint64 last = range_get_last(reg->guest_phys_addr,
646b1819747SMarc-André Lureau                                        reg->memory_size);
647b1819747SMarc-André Lureau         log_size = MAX(log_size, last / (8 * VHOST_LOG_PAGE) + 1);
648b1819747SMarc-André Lureau     }
649b1819747SMarc-André Lureau 
650b1819747SMarc-André Lureau     return log_size;
651b1819747SMarc-André Lureau }
652b1819747SMarc-André Lureau 
6531d9edff7SMarc-André Lureau typedef struct TestMigrateSource {
6541d9edff7SMarc-André Lureau     GSource source;
6551d9edff7SMarc-André Lureau     TestServer *src;
6561d9edff7SMarc-André Lureau     TestServer *dest;
6571d9edff7SMarc-André Lureau } TestMigrateSource;
6581d9edff7SMarc-André Lureau 
6591d9edff7SMarc-André Lureau static gboolean
6601d9edff7SMarc-André Lureau test_migrate_source_check(GSource *source)
6611d9edff7SMarc-André Lureau {
6621d9edff7SMarc-André Lureau     TestMigrateSource *t = (TestMigrateSource *)source;
663d08e42a1SMichael S. Tsirkin     gboolean overlap = t->src->rings && t->dest->rings;
6641d9edff7SMarc-André Lureau 
6651d9edff7SMarc-André Lureau     g_assert(!overlap);
6661d9edff7SMarc-André Lureau 
6671d9edff7SMarc-André Lureau     return FALSE;
6681d9edff7SMarc-André Lureau }
6691d9edff7SMarc-André Lureau 
6701d9edff7SMarc-André Lureau GSourceFuncs test_migrate_source_funcs = {
67145ce5126SMarc-André Lureau     .check = test_migrate_source_check,
6721d9edff7SMarc-André Lureau };
6731d9edff7SMarc-André Lureau 
674a3ebd6e0SPaolo Bonzini static void vhost_user_test_cleanup(void *s)
675e364c703SMaxime Coquelin {
676a3ebd6e0SPaolo Bonzini     TestServer *server = s;
677e364c703SMaxime Coquelin 
678a3ebd6e0SPaolo Bonzini     qos_invalidate_command_line();
679e364c703SMaxime Coquelin     test_server_free(server);
680e364c703SMaxime Coquelin }
681e364c703SMaxime Coquelin 
682a3ebd6e0SPaolo Bonzini static void *vhost_user_test_setup(GString *cmd_line, void *arg)
683b1819747SMarc-André Lureau {
684892040dcSDima Stepanov     TestServer *server = test_server_new("vhost-user-test", arg);
685a3ebd6e0SPaolo Bonzini     test_server_listen(server);
686a3ebd6e0SPaolo Bonzini 
687a3ebd6e0SPaolo Bonzini     append_mem_opts(server, cmd_line, 256, TEST_MEMFD_AUTO);
688892040dcSDima Stepanov     server->vu_ops->append_opts(server, cmd_line, "");
689a3ebd6e0SPaolo Bonzini 
690a3ebd6e0SPaolo Bonzini     g_test_queue_destroy(vhost_user_test_cleanup, server);
691a3ebd6e0SPaolo Bonzini 
692a3ebd6e0SPaolo Bonzini     return server;
693a3ebd6e0SPaolo Bonzini }
694a3ebd6e0SPaolo Bonzini 
695a3ebd6e0SPaolo Bonzini static void *vhost_user_test_setup_memfd(GString *cmd_line, void *arg)
696a3ebd6e0SPaolo Bonzini {
697892040dcSDima Stepanov     TestServer *server = test_server_new("vhost-user-test", arg);
698a3ebd6e0SPaolo Bonzini     test_server_listen(server);
699a3ebd6e0SPaolo Bonzini 
700a3ebd6e0SPaolo Bonzini     append_mem_opts(server, cmd_line, 256, TEST_MEMFD_YES);
701892040dcSDima Stepanov     server->vu_ops->append_opts(server, cmd_line, "");
702a3ebd6e0SPaolo Bonzini 
703a3ebd6e0SPaolo Bonzini     g_test_queue_destroy(vhost_user_test_cleanup, server);
704a3ebd6e0SPaolo Bonzini 
705a3ebd6e0SPaolo Bonzini     return server;
706a3ebd6e0SPaolo Bonzini }
707a3ebd6e0SPaolo Bonzini 
708a3ebd6e0SPaolo Bonzini static void test_read_guest_mem(void *obj, void *arg, QGuestAllocator *alloc)
709a3ebd6e0SPaolo Bonzini {
710a3ebd6e0SPaolo Bonzini     TestServer *server = arg;
711a3ebd6e0SPaolo Bonzini 
712a3ebd6e0SPaolo Bonzini     if (!wait_for_fds(server)) {
713a3ebd6e0SPaolo Bonzini         return;
714a3ebd6e0SPaolo Bonzini     }
715a3ebd6e0SPaolo Bonzini 
716a3ebd6e0SPaolo Bonzini     read_guest_mem_server(global_qtest, server);
717a3ebd6e0SPaolo Bonzini }
718a3ebd6e0SPaolo Bonzini 
719a3ebd6e0SPaolo Bonzini static void test_migrate(void *obj, void *arg, QGuestAllocator *alloc)
720a3ebd6e0SPaolo Bonzini {
721a3ebd6e0SPaolo Bonzini     TestServer *s = arg;
72299fd3178SThomas Huth     TestServer *dest;
72399fd3178SThomas Huth     GString *dest_cmdline;
72499fd3178SThomas Huth     char *uri;
725a3ebd6e0SPaolo Bonzini     QTestState *to;
7261d9edff7SMarc-André Lureau     GSource *source;
727b1819747SMarc-André Lureau     QDict *rsp;
728b1819747SMarc-André Lureau     guint8 *log;
729b1819747SMarc-André Lureau     guint64 size;
730b1819747SMarc-André Lureau 
7313b72ca38SPaolo Bonzini     if (!wait_for_fds(s)) {
732a3ebd6e0SPaolo Bonzini         return;
7333b72ca38SPaolo Bonzini     }
7343b72ca38SPaolo Bonzini 
735892040dcSDima Stepanov     dest = test_server_new("dest", s->vu_ops);
73699fd3178SThomas Huth     dest_cmdline = g_string_new(qos_get_current_command_line());
73799fd3178SThomas Huth     uri = g_strdup_printf("%s%s", "unix:", dest->mig_path);
73899fd3178SThomas Huth 
739b1819747SMarc-André Lureau     size = get_log_size(s);
7400f9fe580SPaolo Bonzini     g_assert_cmpint(size, ==, (256 * 1024 * 1024) / (VHOST_LOG_PAGE * 8));
741b1819747SMarc-André Lureau 
742a3ebd6e0SPaolo Bonzini     test_server_listen(dest);
743a3ebd6e0SPaolo Bonzini     g_string_append_printf(dest_cmdline, " -incoming %s", uri);
744a3ebd6e0SPaolo Bonzini     append_mem_opts(dest, dest_cmdline, 256, TEST_MEMFD_AUTO);
745892040dcSDima Stepanov     dest->vu_ops->append_opts(dest, dest_cmdline, "");
746a3ebd6e0SPaolo Bonzini     to = qtest_init(dest_cmdline->str);
747a3ebd6e0SPaolo Bonzini 
748a3ebd6e0SPaolo Bonzini     /* This would be where you call qos_allocate_objects(to, NULL), if you want
749a3ebd6e0SPaolo Bonzini      * to talk to the QVirtioNet object on the destination.
750a3ebd6e0SPaolo Bonzini      */
751b1819747SMarc-André Lureau 
7521d9edff7SMarc-André Lureau     source = g_source_new(&test_migrate_source_funcs,
7531d9edff7SMarc-André Lureau                           sizeof(TestMigrateSource));
7541d9edff7SMarc-André Lureau     ((TestMigrateSource *)source)->src = s;
7551d9edff7SMarc-André Lureau     ((TestMigrateSource *)source)->dest = dest;
7567d0ca3e7SPaolo Bonzini     g_source_attach(source, s->context);
7571d9edff7SMarc-André Lureau 
758b1819747SMarc-André Lureau     /* slow down migration to have time to fiddle with log */
759b1819747SMarc-André Lureau     /* TODO: qtest could learn to break on some places */
760cbde7be9SDaniel P. Berrangé     rsp = qmp("{ 'execute': 'migrate-set-parameters',"
761cbde7be9SDaniel P. Berrangé               "'arguments': { 'max-bandwidth': 10 } }");
762b1819747SMarc-André Lureau     g_assert(qdict_haskey(rsp, "return"));
763cb3e7f08SMarc-André Lureau     qobject_unref(rsp);
764b1819747SMarc-André Lureau 
765015715f5SMarkus Armbruster     rsp = qmp("{ 'execute': 'migrate', 'arguments': { 'uri': %s } }", uri);
766b1819747SMarc-André Lureau     g_assert(qdict_haskey(rsp, "return"));
767cb3e7f08SMarc-André Lureau     qobject_unref(rsp);
768b1819747SMarc-André Lureau 
769b1819747SMarc-André Lureau     wait_for_log_fd(s);
770b1819747SMarc-André Lureau 
771b1819747SMarc-André Lureau     log = mmap(0, size, PROT_READ | PROT_WRITE, MAP_SHARED, s->log_fd, 0);
772b1819747SMarc-André Lureau     g_assert(log != MAP_FAILED);
773b1819747SMarc-André Lureau 
774b1819747SMarc-André Lureau     /* modify first page */
775b1819747SMarc-André Lureau     write_guest_mem(s, 0x42);
776b1819747SMarc-André Lureau     log[0] = 1;
777b1819747SMarc-André Lureau     munmap(log, size);
778b1819747SMarc-André Lureau 
779b1819747SMarc-André Lureau     /* speed things up */
780cbde7be9SDaniel P. Berrangé     rsp = qmp("{ 'execute': 'migrate-set-parameters',"
781cbde7be9SDaniel P. Berrangé               "'arguments': { 'max-bandwidth': 0 } }");
782b1819747SMarc-André Lureau     g_assert(qdict_haskey(rsp, "return"));
783cb3e7f08SMarc-André Lureau     qobject_unref(rsp);
784b1819747SMarc-André Lureau 
785b1819747SMarc-André Lureau     qmp_eventwait("STOP");
786bae6b59dSPaolo Bonzini     qtest_qmp_eventwait(to, "RESUME");
787b1819747SMarc-André Lureau 
788bae6b59dSPaolo Bonzini     g_assert(wait_for_fds(dest));
789bae6b59dSPaolo Bonzini     read_guest_mem_server(to, dest);
790b1819747SMarc-André Lureau 
7911d9edff7SMarc-André Lureau     g_source_destroy(source);
7921d9edff7SMarc-André Lureau     g_source_unref(source);
7931d9edff7SMarc-André Lureau 
794a3ebd6e0SPaolo Bonzini     qtest_quit(to);
795b1819747SMarc-André Lureau     test_server_free(dest);
796a899b1eaSMarc-André Lureau     g_free(uri);
79799fd3178SThomas Huth     g_string_free(dest_cmdline, true);
798b1819747SMarc-André Lureau }
799b1819747SMarc-André Lureau 
8004616e359SMarc-André Lureau static void wait_for_rings_started(TestServer *s, size_t count)
8014616e359SMarc-André Lureau {
8024616e359SMarc-André Lureau     gint64 end_time;
8034616e359SMarc-André Lureau 
8044616e359SMarc-André Lureau     g_mutex_lock(&s->data_mutex);
8054616e359SMarc-André Lureau     end_time = g_get_monotonic_time() + 5 * G_TIME_SPAN_SECOND;
8064616e359SMarc-André Lureau     while (ctpop64(s->rings) != count) {
8074616e359SMarc-André Lureau         if (!g_cond_wait_until(&s->data_cond, &s->data_mutex, end_time)) {
8084616e359SMarc-André Lureau             /* timeout has passed */
8094616e359SMarc-André Lureau             g_assert_cmpint(ctpop64(s->rings), ==, count);
8104616e359SMarc-André Lureau             break;
8114616e359SMarc-André Lureau         }
8124616e359SMarc-André Lureau     }
8134616e359SMarc-André Lureau 
8144616e359SMarc-André Lureau     g_mutex_unlock(&s->data_mutex);
8154616e359SMarc-André Lureau }
8164616e359SMarc-André Lureau 
81720784087SPhilippe Mathieu-Daudé static inline void test_server_connect(TestServer *server)
81820784087SPhilippe Mathieu-Daudé {
81920784087SPhilippe Mathieu-Daudé     test_server_create_chr(server, ",reconnect=1");
82020784087SPhilippe Mathieu-Daudé }
82120784087SPhilippe Mathieu-Daudé 
8224616e359SMarc-André Lureau static gboolean
8234616e359SMarc-André Lureau reconnect_cb(gpointer user_data)
8244616e359SMarc-André Lureau {
8254616e359SMarc-André Lureau     TestServer *s = user_data;
8264616e359SMarc-André Lureau 
8275345fdb4SMarc-André Lureau     qemu_chr_fe_disconnect(&s->chr);
8284616e359SMarc-André Lureau 
8294616e359SMarc-André Lureau     return FALSE;
8304616e359SMarc-André Lureau }
8314616e359SMarc-André Lureau 
8324616e359SMarc-André Lureau static gpointer
8334616e359SMarc-André Lureau connect_thread(gpointer data)
8344616e359SMarc-André Lureau {
8354616e359SMarc-André Lureau     TestServer *s = data;
8364616e359SMarc-André Lureau 
8374616e359SMarc-André Lureau     /* wait for qemu to start before first try, to avoid extra warnings */
8384616e359SMarc-André Lureau     g_usleep(G_USEC_PER_SEC);
8394616e359SMarc-André Lureau     test_server_connect(s);
8404616e359SMarc-André Lureau 
8414616e359SMarc-André Lureau     return NULL;
8424616e359SMarc-André Lureau }
8434616e359SMarc-André Lureau 
844a3ebd6e0SPaolo Bonzini static void *vhost_user_test_setup_reconnect(GString *cmd_line, void *arg)
8454616e359SMarc-André Lureau {
846892040dcSDima Stepanov     TestServer *s = test_server_new("reconnect", arg);
8474616e359SMarc-André Lureau 
8484616e359SMarc-André Lureau     g_thread_new("connect", connect_thread, s);
849a3ebd6e0SPaolo Bonzini     append_mem_opts(s, cmd_line, 256, TEST_MEMFD_AUTO);
850991c180dSPaolo Bonzini     s->vu_ops->append_opts(s, cmd_line, ",server=on");
8514616e359SMarc-André Lureau 
852a3ebd6e0SPaolo Bonzini     g_test_queue_destroy(vhost_user_test_cleanup, s);
853a3ebd6e0SPaolo Bonzini 
854a3ebd6e0SPaolo Bonzini     return s;
855a3ebd6e0SPaolo Bonzini }
856a3ebd6e0SPaolo Bonzini 
857a3ebd6e0SPaolo Bonzini static void test_reconnect(void *obj, void *arg, QGuestAllocator *alloc)
858a3ebd6e0SPaolo Bonzini {
859a3ebd6e0SPaolo Bonzini     TestServer *s = arg;
860a3ebd6e0SPaolo Bonzini     GSource *src;
861a3ebd6e0SPaolo Bonzini 
8623b72ca38SPaolo Bonzini     if (!wait_for_fds(s)) {
863a3ebd6e0SPaolo Bonzini         return;
8643b72ca38SPaolo Bonzini     }
8653b72ca38SPaolo Bonzini 
8664616e359SMarc-André Lureau     wait_for_rings_started(s, 2);
8674616e359SMarc-André Lureau 
8684616e359SMarc-André Lureau     /* reconnect */
8694616e359SMarc-André Lureau     s->fds_num = 0;
8704616e359SMarc-André Lureau     s->rings = 0;
8717d0ca3e7SPaolo Bonzini     src = g_idle_source_new();
8727d0ca3e7SPaolo Bonzini     g_source_set_callback(src, reconnect_cb, s, NULL);
8737d0ca3e7SPaolo Bonzini     g_source_attach(src, s->context);
8747d0ca3e7SPaolo Bonzini     g_source_unref(src);
8753b72ca38SPaolo Bonzini     g_assert(wait_for_fds(s));
8764616e359SMarc-André Lureau     wait_for_rings_started(s, 2);
8774616e359SMarc-André Lureau }
8784616e359SMarc-André Lureau 
879a3ebd6e0SPaolo Bonzini static void *vhost_user_test_setup_connect_fail(GString *cmd_line, void *arg)
8805d443f5aSMarc-André Lureau {
881892040dcSDima Stepanov     TestServer *s = test_server_new("connect-fail", arg);
8825d443f5aSMarc-André Lureau 
8835d443f5aSMarc-André Lureau     s->test_fail = true;
884a3ebd6e0SPaolo Bonzini 
8855d443f5aSMarc-André Lureau     g_thread_new("connect", connect_thread, s);
886a3ebd6e0SPaolo Bonzini     append_mem_opts(s, cmd_line, 256, TEST_MEMFD_AUTO);
887991c180dSPaolo Bonzini     s->vu_ops->append_opts(s, cmd_line, ",server=on");
8885d443f5aSMarc-André Lureau 
889a3ebd6e0SPaolo Bonzini     g_test_queue_destroy(vhost_user_test_cleanup, s);
8905d443f5aSMarc-André Lureau 
891a3ebd6e0SPaolo Bonzini     return s;
8925d443f5aSMarc-André Lureau }
8935d443f5aSMarc-André Lureau 
894a3ebd6e0SPaolo Bonzini static void *vhost_user_test_setup_flags_mismatch(GString *cmd_line, void *arg)
8959294d76cSMarc-André Lureau {
896892040dcSDima Stepanov     TestServer *s = test_server_new("flags-mismatch", arg);
8979294d76cSMarc-André Lureau 
8989294d76cSMarc-André Lureau     s->test_flags = TEST_FLAGS_DISCONNECT;
8999294d76cSMarc-André Lureau 
900a3ebd6e0SPaolo Bonzini     g_thread_new("connect", connect_thread, s);
901a3ebd6e0SPaolo Bonzini     append_mem_opts(s, cmd_line, 256, TEST_MEMFD_AUTO);
902991c180dSPaolo Bonzini     s->vu_ops->append_opts(s, cmd_line, ",server=on");
903a3ebd6e0SPaolo Bonzini 
904a3ebd6e0SPaolo Bonzini     g_test_queue_destroy(vhost_user_test_cleanup, s);
905a3ebd6e0SPaolo Bonzini 
906a3ebd6e0SPaolo Bonzini     return s;
907a3ebd6e0SPaolo Bonzini }
908a3ebd6e0SPaolo Bonzini 
909a3ebd6e0SPaolo Bonzini static void test_vhost_user_started(void *obj, void *arg, QGuestAllocator *alloc)
910a3ebd6e0SPaolo Bonzini {
911a3ebd6e0SPaolo Bonzini     TestServer *s = arg;
912a3ebd6e0SPaolo Bonzini 
9133b72ca38SPaolo Bonzini     if (!wait_for_fds(s)) {
914a3ebd6e0SPaolo Bonzini         return;
9153b72ca38SPaolo Bonzini     }
9169294d76cSMarc-André Lureau     wait_for_rings_started(s, 2);
9179294d76cSMarc-André Lureau }
9189294d76cSMarc-André Lureau 
919a3ebd6e0SPaolo Bonzini static void *vhost_user_test_setup_multiqueue(GString *cmd_line, void *arg)
9209294d76cSMarc-André Lureau {
921a3ebd6e0SPaolo Bonzini     TestServer *s = vhost_user_test_setup(cmd_line, arg);
9229294d76cSMarc-André Lureau 
923459f5d29SMaxime Coquelin     s->queues = 2;
924a3ebd6e0SPaolo Bonzini     g_string_append_printf(cmd_line,
925a3ebd6e0SPaolo Bonzini                            " -set netdev.hs0.queues=%d"
926a3ebd6e0SPaolo Bonzini                            " -global virtio-net-pci.vectors=%d",
927a3ebd6e0SPaolo Bonzini                            s->queues, s->queues * 2 + 2);
928ed0a8d92SMarc-André Lureau 
929a3ebd6e0SPaolo Bonzini     return s;
9308e029fd6SMarc-André Lureau }
931ed0a8d92SMarc-André Lureau 
932a3ebd6e0SPaolo Bonzini static void test_multiqueue(void *obj, void *arg, QGuestAllocator *alloc)
933a3ebd6e0SPaolo Bonzini {
934a3ebd6e0SPaolo Bonzini     TestServer *s = arg;
935ed0a8d92SMarc-André Lureau 
936459f5d29SMaxime Coquelin     wait_for_rings_started(s, s->queues * 2);
937ed0a8d92SMarc-André Lureau }
938ed0a8d92SMarc-André Lureau 
939892040dcSDima Stepanov static void vu_net_set_features(TestServer *s, CharBackend *chr,
940892040dcSDima Stepanov         VhostUserMsg *msg)
941892040dcSDima Stepanov {
942892040dcSDima Stepanov     g_assert_cmpint(msg->payload.u64 &
943892040dcSDima Stepanov             (0x1ULL << VHOST_USER_F_PROTOCOL_FEATURES), !=, 0ULL);
944892040dcSDima Stepanov     if (s->test_flags == TEST_FLAGS_DISCONNECT) {
945892040dcSDima Stepanov         qemu_chr_fe_disconnect(chr);
946892040dcSDima Stepanov         s->test_flags = TEST_FLAGS_BAD;
947892040dcSDima Stepanov     }
948892040dcSDima Stepanov }
949892040dcSDima Stepanov 
950892040dcSDima Stepanov static void vu_net_get_protocol_features(TestServer *s, CharBackend *chr,
951892040dcSDima Stepanov         VhostUserMsg *msg)
952892040dcSDima Stepanov {
953892040dcSDima Stepanov     /* send back features to qemu */
954892040dcSDima Stepanov     msg->flags |= VHOST_USER_REPLY_MASK;
955892040dcSDima Stepanov     msg->size = sizeof(m.payload.u64);
956892040dcSDima Stepanov     msg->payload.u64 = 1 << VHOST_USER_PROTOCOL_F_LOG_SHMFD;
957892040dcSDima Stepanov     msg->payload.u64 |= 1 << VHOST_USER_PROTOCOL_F_CROSS_ENDIAN;
958892040dcSDima Stepanov     if (s->queues > 1) {
959892040dcSDima Stepanov         msg->payload.u64 |= 1 << VHOST_USER_PROTOCOL_F_MQ;
960892040dcSDima Stepanov     }
961892040dcSDima Stepanov     qemu_chr_fe_write_all(chr, (uint8_t *)msg, VHOST_USER_HDR_SIZE + msg->size);
962892040dcSDima Stepanov }
963892040dcSDima Stepanov 
964892040dcSDima Stepanov /* Each VHOST-USER device should have its ops structure defined. */
965892040dcSDima Stepanov static struct vhost_user_ops g_vu_net_ops = {
966892040dcSDima Stepanov     .type = VHOST_USER_NET,
967892040dcSDima Stepanov 
968892040dcSDima Stepanov     .append_opts = append_vhost_net_opts,
969892040dcSDima Stepanov 
970892040dcSDima Stepanov     .set_features = vu_net_set_features,
971892040dcSDima Stepanov     .get_protocol_features = vu_net_get_protocol_features,
972892040dcSDima Stepanov };
973892040dcSDima Stepanov 
974a3ebd6e0SPaolo Bonzini static void register_vhost_user_test(void)
975a77e6b14SNikolay Nikolaev {
976a3ebd6e0SPaolo Bonzini     QOSGraphTestOptions opts = {
977a3ebd6e0SPaolo Bonzini         .before = vhost_user_test_setup,
978a3ebd6e0SPaolo Bonzini         .subprocess = true,
979892040dcSDima Stepanov         .arg = &g_vu_net_ops,
980a3ebd6e0SPaolo Bonzini     };
981a77e6b14SNikolay Nikolaev 
982ae31fb54SMarc-André Lureau     qemu_add_opts(&qemu_chardev_opts);
983a77e6b14SNikolay Nikolaev 
984a3ebd6e0SPaolo Bonzini     qos_add_test("vhost-user/read-guest-mem/memfile",
985a3ebd6e0SPaolo Bonzini                  "virtio-net",
986a3ebd6e0SPaolo Bonzini                  test_read_guest_mem, &opts);
987a3ebd6e0SPaolo Bonzini 
9884a66c7a9SIlya Maximets     if (qemu_memfd_check(MFD_ALLOW_SEALING)) {
989a3ebd6e0SPaolo Bonzini         opts.before = vhost_user_test_setup_memfd;
990a3ebd6e0SPaolo Bonzini         qos_add_test("vhost-user/read-guest-mem/memfd",
991a3ebd6e0SPaolo Bonzini                      "virtio-net",
992a3ebd6e0SPaolo Bonzini                      test_read_guest_mem, &opts);
9938e029fd6SMarc-André Lureau     }
994a3ebd6e0SPaolo Bonzini 
995a3ebd6e0SPaolo Bonzini     qos_add_test("vhost-user/migrate",
996a3ebd6e0SPaolo Bonzini                  "virtio-net",
997a3ebd6e0SPaolo Bonzini                  test_migrate, &opts);
99820784087SPhilippe Mathieu-Daudé 
999a3ebd6e0SPaolo Bonzini     opts.before = vhost_user_test_setup_reconnect;
1000a3ebd6e0SPaolo Bonzini     qos_add_test("vhost-user/reconnect", "virtio-net",
1001a3ebd6e0SPaolo Bonzini                  test_reconnect, &opts);
1002a3ebd6e0SPaolo Bonzini 
1003a3ebd6e0SPaolo Bonzini     opts.before = vhost_user_test_setup_connect_fail;
1004a3ebd6e0SPaolo Bonzini     qos_add_test("vhost-user/connect-fail", "virtio-net",
1005a3ebd6e0SPaolo Bonzini                  test_vhost_user_started, &opts);
1006a3ebd6e0SPaolo Bonzini 
1007a3ebd6e0SPaolo Bonzini     opts.before = vhost_user_test_setup_flags_mismatch;
1008a3ebd6e0SPaolo Bonzini     qos_add_test("vhost-user/flags-mismatch", "virtio-net",
1009a3ebd6e0SPaolo Bonzini                  test_vhost_user_started, &opts);
1010a77e6b14SNikolay Nikolaev 
1011a3ebd6e0SPaolo Bonzini     opts.before = vhost_user_test_setup_multiqueue;
1012a3ebd6e0SPaolo Bonzini     opts.edge.extra_device_opts = "mq=on";
1013a3ebd6e0SPaolo Bonzini     qos_add_test("vhost-user/multiqueue",
1014a3ebd6e0SPaolo Bonzini                  "virtio-net",
1015a3ebd6e0SPaolo Bonzini                  test_multiqueue, &opts);
1016a77e6b14SNikolay Nikolaev }
1017a3ebd6e0SPaolo Bonzini libqos_init(register_vhost_user_test);
1018