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