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