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