1a77e6b14SNikolay Nikolaev /* 2a77e6b14SNikolay Nikolaev * QTest testcase for the vhost-user 3a77e6b14SNikolay Nikolaev * 4a77e6b14SNikolay Nikolaev * Copyright (c) 2014 Virtual Open Systems Sarl. 5a77e6b14SNikolay Nikolaev * 6a77e6b14SNikolay Nikolaev * This work is licensed under the terms of the GNU GPL, version 2 or later. 7a77e6b14SNikolay Nikolaev * See the COPYING file in the top-level directory. 8a77e6b14SNikolay Nikolaev * 9a77e6b14SNikolay Nikolaev */ 10a77e6b14SNikolay Nikolaev 11681c28a3SPeter Maydell #include "qemu/osdep.h" 12bd95939fSNikolay Nikolaev 13dd210749SThomas Huth #include "libqtest-single.h" 145345fdb4SMarc-André Lureau #include "qapi/error.h" 15452fcdbcSMarkus Armbruster #include "qapi/qmp/qdict.h" 16213dcb06SMarc-André Lureau #include "qemu/config-file.h" 17a77e6b14SNikolay Nikolaev #include "qemu/option.h" 18b1819747SMarc-André Lureau #include "qemu/range.h" 19a9c94277SMarkus Armbruster #include "qemu/sockets.h" 204d43a603SMarc-André Lureau #include "chardev/char-fe.h" 218e029fd6SMarc-André Lureau #include "qemu/memfd.h" 220b8fa32fSMarkus Armbruster #include "qemu/module.h" 23a77e6b14SNikolay Nikolaev #include "sysemu/sysemu.h" 24cdafe929SEduardo Habkost #include "libqos/libqos.h" 25cdafe929SEduardo Habkost #include "libqos/pci-pc.h" 26cdafe929SEduardo Habkost #include "libqos/virtio-pci.h" 27a77e6b14SNikolay Nikolaev 28ed0a8d92SMarc-André Lureau #include "libqos/malloc-pc.h" 29ed0a8d92SMarc-André Lureau #include "hw/virtio/virtio-net.h" 30ed0a8d92SMarc-André Lureau 31af3bba76SPaolo Bonzini #include "standard-headers/linux/vhost_types.h" 32af3bba76SPaolo Bonzini #include "standard-headers/linux/virtio_ids.h" 33af3bba76SPaolo Bonzini #include "standard-headers/linux/virtio_net.h" 34af3bba76SPaolo Bonzini 35af3bba76SPaolo Bonzini #ifdef CONFIG_LINUX 36a77e6b14SNikolay Nikolaev #include <sys/vfs.h> 37af3bba76SPaolo Bonzini #endif 38a77e6b14SNikolay Nikolaev 3930de46dbSGonglei 40704b2168SMarc-André Lureau #define QEMU_CMD_MEM " -m %d -object memory-backend-file,id=mem,size=%dM," \ 41a77e6b14SNikolay Nikolaev "mem-path=%s,share=on -numa node,memdev=mem" 428e029fd6SMarc-André Lureau #define QEMU_CMD_MEMFD " -m %d -object memory-backend-memfd,id=mem,size=%dM," \ 438e029fd6SMarc-André Lureau " -numa node,memdev=mem" 444616e359SMarc-André Lureau #define QEMU_CMD_CHR " -chardev socket,id=%s,path=%s%s" 45a3ebd6e0SPaolo Bonzini #define QEMU_CMD_NETDEV " -netdev vhost-user,id=hs0,chardev=%s,vhostforce" 46a77e6b14SNikolay Nikolaev 47a77e6b14SNikolay Nikolaev #define HUGETLBFS_MAGIC 0x958458f6 48a77e6b14SNikolay Nikolaev 49a77e6b14SNikolay Nikolaev /*********** FROM hw/virtio/vhost-user.c *************************************/ 50a77e6b14SNikolay Nikolaev 51a77e6b14SNikolay Nikolaev #define VHOST_MEMORY_MAX_NREGIONS 8 52026eb179SMaxime Coquelin #define VHOST_MAX_VIRTQUEUES 0x100 53a77e6b14SNikolay Nikolaev 548a9b6b37SMichael S. Tsirkin #define VHOST_USER_F_PROTOCOL_FEATURES 30 55ed0a8d92SMarc-André Lureau #define VHOST_USER_PROTOCOL_F_MQ 0 56b1819747SMarc-André Lureau #define VHOST_USER_PROTOCOL_F_LOG_SHMFD 1 575a583cc5SPaolo Bonzini #define VHOST_USER_PROTOCOL_F_CROSS_ENDIAN 6 58b1819747SMarc-André Lureau 59b1819747SMarc-André Lureau #define VHOST_LOG_PAGE 0x1000 608a9b6b37SMichael S. Tsirkin 61a77e6b14SNikolay Nikolaev typedef enum VhostUserRequest { 62a77e6b14SNikolay Nikolaev VHOST_USER_NONE = 0, 63a77e6b14SNikolay Nikolaev VHOST_USER_GET_FEATURES = 1, 64a77e6b14SNikolay Nikolaev VHOST_USER_SET_FEATURES = 2, 65a77e6b14SNikolay Nikolaev VHOST_USER_SET_OWNER = 3, 6660915dc4SYuanhan Liu VHOST_USER_RESET_OWNER = 4, 67a77e6b14SNikolay Nikolaev VHOST_USER_SET_MEM_TABLE = 5, 68a77e6b14SNikolay Nikolaev VHOST_USER_SET_LOG_BASE = 6, 69a77e6b14SNikolay Nikolaev VHOST_USER_SET_LOG_FD = 7, 70a77e6b14SNikolay Nikolaev VHOST_USER_SET_VRING_NUM = 8, 71a77e6b14SNikolay Nikolaev VHOST_USER_SET_VRING_ADDR = 9, 72a77e6b14SNikolay Nikolaev VHOST_USER_SET_VRING_BASE = 10, 73a77e6b14SNikolay Nikolaev VHOST_USER_GET_VRING_BASE = 11, 74a77e6b14SNikolay Nikolaev VHOST_USER_SET_VRING_KICK = 12, 75a77e6b14SNikolay Nikolaev VHOST_USER_SET_VRING_CALL = 13, 76a77e6b14SNikolay Nikolaev VHOST_USER_SET_VRING_ERR = 14, 778a9b6b37SMichael S. Tsirkin VHOST_USER_GET_PROTOCOL_FEATURES = 15, 788a9b6b37SMichael S. Tsirkin VHOST_USER_SET_PROTOCOL_FEATURES = 16, 79ed0a8d92SMarc-André Lureau VHOST_USER_GET_QUEUE_NUM = 17, 8087656d50SMichael S. Tsirkin VHOST_USER_SET_VRING_ENABLE = 18, 81a77e6b14SNikolay Nikolaev VHOST_USER_MAX 82a77e6b14SNikolay Nikolaev } VhostUserRequest; 83a77e6b14SNikolay Nikolaev 84a77e6b14SNikolay Nikolaev typedef struct VhostUserMemoryRegion { 85a77e6b14SNikolay Nikolaev uint64_t guest_phys_addr; 86a77e6b14SNikolay Nikolaev uint64_t memory_size; 87a77e6b14SNikolay Nikolaev uint64_t userspace_addr; 88d6970e3bSNikolay Nikolaev uint64_t mmap_offset; 89a77e6b14SNikolay Nikolaev } VhostUserMemoryRegion; 90a77e6b14SNikolay Nikolaev 91a77e6b14SNikolay Nikolaev typedef struct VhostUserMemory { 92a77e6b14SNikolay Nikolaev uint32_t nregions; 93a77e6b14SNikolay Nikolaev uint32_t padding; 94a77e6b14SNikolay Nikolaev VhostUserMemoryRegion regions[VHOST_MEMORY_MAX_NREGIONS]; 95a77e6b14SNikolay Nikolaev } VhostUserMemory; 96a77e6b14SNikolay Nikolaev 972b8819c6SVictor Kaplansky typedef struct VhostUserLog { 982b8819c6SVictor Kaplansky uint64_t mmap_size; 992b8819c6SVictor Kaplansky uint64_t mmap_offset; 1002b8819c6SVictor Kaplansky } VhostUserLog; 1012b8819c6SVictor Kaplansky 102a77e6b14SNikolay Nikolaev typedef struct VhostUserMsg { 103a77e6b14SNikolay Nikolaev VhostUserRequest request; 104a77e6b14SNikolay Nikolaev 105a77e6b14SNikolay Nikolaev #define VHOST_USER_VERSION_MASK (0x3) 106a77e6b14SNikolay Nikolaev #define VHOST_USER_REPLY_MASK (0x1<<2) 107a77e6b14SNikolay Nikolaev uint32_t flags; 108a77e6b14SNikolay Nikolaev uint32_t size; /* the following payload size */ 109a77e6b14SNikolay Nikolaev union { 1102b8819c6SVictor Kaplansky #define VHOST_USER_VRING_IDX_MASK (0xff) 1112b8819c6SVictor Kaplansky #define VHOST_USER_VRING_NOFD_MASK (0x1<<8) 112a77e6b14SNikolay Nikolaev uint64_t u64; 113a77e6b14SNikolay Nikolaev struct vhost_vring_state state; 114a77e6b14SNikolay Nikolaev struct vhost_vring_addr addr; 115a77e6b14SNikolay Nikolaev VhostUserMemory memory; 1162b8819c6SVictor Kaplansky VhostUserLog log; 11712ebf690SMichael S. Tsirkin } payload; 118a77e6b14SNikolay Nikolaev } QEMU_PACKED VhostUserMsg; 119a77e6b14SNikolay Nikolaev 120a77e6b14SNikolay Nikolaev static VhostUserMsg m __attribute__ ((unused)); 121a77e6b14SNikolay Nikolaev #define VHOST_USER_HDR_SIZE (sizeof(m.request) \ 122a77e6b14SNikolay Nikolaev + sizeof(m.flags) \ 123a77e6b14SNikolay Nikolaev + sizeof(m.size)) 124a77e6b14SNikolay Nikolaev 125a77e6b14SNikolay Nikolaev #define VHOST_USER_PAYLOAD_SIZE (sizeof(m) - VHOST_USER_HDR_SIZE) 126a77e6b14SNikolay Nikolaev 127a77e6b14SNikolay Nikolaev /* The version of the protocol we support */ 128a77e6b14SNikolay Nikolaev #define VHOST_USER_VERSION (0x1) 129a77e6b14SNikolay Nikolaev /*****************************************************************************/ 130a77e6b14SNikolay Nikolaev 1319294d76cSMarc-André Lureau enum { 1329294d76cSMarc-André Lureau TEST_FLAGS_OK, 1339294d76cSMarc-André Lureau TEST_FLAGS_DISCONNECT, 1349294d76cSMarc-André Lureau TEST_FLAGS_BAD, 1359294d76cSMarc-André Lureau TEST_FLAGS_END, 1369294d76cSMarc-André Lureau }; 1379294d76cSMarc-André Lureau 138892040dcSDima Stepanov enum { 139892040dcSDima Stepanov VHOST_USER_NET, 140892040dcSDima Stepanov }; 141892040dcSDima Stepanov 142ae31fb54SMarc-André Lureau typedef struct TestServer { 143ae31fb54SMarc-André Lureau gchar *socket_path; 144a899b1eaSMarc-André Lureau gchar *mig_path; 145ae31fb54SMarc-André Lureau gchar *chr_name; 1464d3f50ebSPaolo Bonzini gchar *tmpfs; 14732a6ebecSMarc-André Lureau CharBackend chr; 148ae31fb54SMarc-André Lureau int fds_num; 149ae31fb54SMarc-André Lureau int fds[VHOST_MEMORY_MAX_NREGIONS]; 150ae31fb54SMarc-André Lureau VhostUserMemory memory; 1517d0ca3e7SPaolo Bonzini GMainContext *context; 1527d0ca3e7SPaolo Bonzini GMainLoop *loop; 1537d0ca3e7SPaolo Bonzini GThread *thread; 154e7b3af81SDaniel P. Berrangé GMutex data_mutex; 155e7b3af81SDaniel P. Berrangé GCond data_cond; 156b1819747SMarc-André Lureau int log_fd; 157d08e42a1SMichael S. Tsirkin uint64_t rings; 1585d443f5aSMarc-André Lureau bool test_fail; 1599294d76cSMarc-André Lureau int test_flags; 160ed0a8d92SMarc-André Lureau int queues; 161892040dcSDima Stepanov struct vhost_user_ops *vu_ops; 162ae31fb54SMarc-André Lureau } TestServer; 163bd95939fSNikolay Nikolaev 164892040dcSDima Stepanov struct vhost_user_ops { 165892040dcSDima Stepanov /* Device types. */ 166892040dcSDima Stepanov int type; 167892040dcSDima Stepanov void (*append_opts)(TestServer *s, GString *cmd_line, 168892040dcSDima Stepanov const char *chr_opts); 169892040dcSDima Stepanov 170892040dcSDima Stepanov /* VHOST-USER commands. */ 171892040dcSDima Stepanov void (*set_features)(TestServer *s, CharBackend *chr, 172892040dcSDima Stepanov VhostUserMsg *msg); 173892040dcSDima Stepanov void (*get_protocol_features)(TestServer *s, 174892040dcSDima Stepanov CharBackend *chr, VhostUserMsg *msg); 175892040dcSDima Stepanov }; 176892040dcSDima Stepanov 177a3ebd6e0SPaolo Bonzini static const char *init_hugepagefs(void); 178892040dcSDima Stepanov static TestServer *test_server_new(const gchar *name, 179892040dcSDima Stepanov struct vhost_user_ops *ops); 18083265145SMarc-André Lureau static void test_server_free(TestServer *server); 18183265145SMarc-André Lureau static void test_server_listen(TestServer *server); 18283265145SMarc-André Lureau 1838e029fd6SMarc-André Lureau enum test_memfd { 1848e029fd6SMarc-André Lureau TEST_MEMFD_AUTO, 1858e029fd6SMarc-André Lureau TEST_MEMFD_YES, 1868e029fd6SMarc-André Lureau TEST_MEMFD_NO, 1878e029fd6SMarc-André Lureau }; 1888e029fd6SMarc-André Lureau 189892040dcSDima Stepanov static void append_vhost_net_opts(TestServer *s, GString *cmd_line, 190a3ebd6e0SPaolo Bonzini const char *chr_opts) 1918e029fd6SMarc-André Lureau { 192a3ebd6e0SPaolo Bonzini g_string_append_printf(cmd_line, QEMU_CMD_CHR QEMU_CMD_NETDEV, 193a3ebd6e0SPaolo Bonzini s->chr_name, s->socket_path, 194a3ebd6e0SPaolo Bonzini chr_opts, s->chr_name); 195a3ebd6e0SPaolo Bonzini } 196a3ebd6e0SPaolo Bonzini 197a3ebd6e0SPaolo Bonzini static void append_mem_opts(TestServer *server, GString *cmd_line, 198a3ebd6e0SPaolo Bonzini int size, enum test_memfd memfd) 199a3ebd6e0SPaolo Bonzini { 200a3ebd6e0SPaolo Bonzini if (memfd == TEST_MEMFD_AUTO) { 2014a66c7a9SIlya Maximets memfd = qemu_memfd_check(MFD_ALLOW_SEALING) ? TEST_MEMFD_YES 2024a66c7a9SIlya Maximets : TEST_MEMFD_NO; 2038e029fd6SMarc-André Lureau } 2048e029fd6SMarc-André Lureau 2058e029fd6SMarc-André Lureau if (memfd == TEST_MEMFD_YES) { 206a3ebd6e0SPaolo Bonzini g_string_append_printf(cmd_line, QEMU_CMD_MEMFD, size, size); 2078e029fd6SMarc-André Lureau } else { 208a3ebd6e0SPaolo Bonzini const char *root = init_hugepagefs() ? : server->tmpfs; 209a3ebd6e0SPaolo Bonzini 210a3ebd6e0SPaolo Bonzini g_string_append_printf(cmd_line, QEMU_CMD_MEM, size, size, root); 2118e029fd6SMarc-André Lureau } 2128e029fd6SMarc-André Lureau } 2138e029fd6SMarc-André Lureau 2143b72ca38SPaolo Bonzini static bool wait_for_fds(TestServer *s) 215a77e6b14SNikolay Nikolaev { 216a77e6b14SNikolay Nikolaev gint64 end_time; 2173b72ca38SPaolo Bonzini bool got_region; 2183b72ca38SPaolo Bonzini int i; 219a77e6b14SNikolay Nikolaev 220ae31fb54SMarc-André Lureau g_mutex_lock(&s->data_mutex); 221a77e6b14SNikolay Nikolaev 222ca06d9ccSPaolo Bonzini end_time = g_get_monotonic_time() + 5 * G_TIME_SPAN_SECOND; 223ae31fb54SMarc-André Lureau while (!s->fds_num) { 224ae31fb54SMarc-André Lureau if (!g_cond_wait_until(&s->data_cond, &s->data_mutex, end_time)) { 225a77e6b14SNikolay Nikolaev /* timeout has passed */ 226ae31fb54SMarc-André Lureau g_assert(s->fds_num); 227a77e6b14SNikolay Nikolaev break; 228a77e6b14SNikolay Nikolaev } 229a77e6b14SNikolay Nikolaev } 230a77e6b14SNikolay Nikolaev 231a77e6b14SNikolay Nikolaev /* check for sanity */ 232ae31fb54SMarc-André Lureau g_assert_cmpint(s->fds_num, >, 0); 233ae31fb54SMarc-André Lureau g_assert_cmpint(s->fds_num, ==, s->memory.nregions); 234a77e6b14SNikolay Nikolaev 235ae31fb54SMarc-André Lureau g_mutex_unlock(&s->data_mutex); 2363b72ca38SPaolo Bonzini 2373b72ca38SPaolo Bonzini got_region = false; 2383b72ca38SPaolo Bonzini for (i = 0; i < s->memory.nregions; ++i) { 2393b72ca38SPaolo Bonzini VhostUserMemoryRegion *reg = &s->memory.regions[i]; 2403b72ca38SPaolo Bonzini if (reg->guest_phys_addr == 0) { 2413b72ca38SPaolo Bonzini got_region = true; 2423b72ca38SPaolo Bonzini break; 2433b72ca38SPaolo Bonzini } 2443b72ca38SPaolo Bonzini } 2453b72ca38SPaolo Bonzini if (!got_region) { 2463b72ca38SPaolo Bonzini g_test_skip("No memory at address 0x0"); 2473b72ca38SPaolo Bonzini } 2483b72ca38SPaolo Bonzini return got_region; 249cf72b57fSMarc-André Lureau } 250cf72b57fSMarc-André Lureau 251bae6b59dSPaolo Bonzini static void read_guest_mem_server(QTestState *qts, TestServer *s) 252cf72b57fSMarc-André Lureau { 2535a583cc5SPaolo Bonzini uint8_t *guest_mem; 254cf72b57fSMarc-André Lureau int i, j; 255cf72b57fSMarc-André Lureau size_t size; 256cf72b57fSMarc-André Lureau 257ae31fb54SMarc-André Lureau g_mutex_lock(&s->data_mutex); 258cf72b57fSMarc-André Lureau 259a77e6b14SNikolay Nikolaev /* iterate all regions */ 260ae31fb54SMarc-André Lureau for (i = 0; i < s->fds_num; i++) { 261a77e6b14SNikolay Nikolaev 262a77e6b14SNikolay Nikolaev /* We'll check only the region statring at 0x0*/ 263ae31fb54SMarc-André Lureau if (s->memory.regions[i].guest_phys_addr != 0x0) { 264a77e6b14SNikolay Nikolaev continue; 265a77e6b14SNikolay Nikolaev } 266a77e6b14SNikolay Nikolaev 267ae31fb54SMarc-André Lureau g_assert_cmpint(s->memory.regions[i].memory_size, >, 1024); 268a77e6b14SNikolay Nikolaev 269ae31fb54SMarc-André Lureau size = s->memory.regions[i].memory_size + 270ae31fb54SMarc-André Lureau s->memory.regions[i].mmap_offset; 271d6970e3bSNikolay Nikolaev 272d6970e3bSNikolay Nikolaev guest_mem = mmap(0, size, PROT_READ | PROT_WRITE, 273ae31fb54SMarc-André Lureau MAP_SHARED, s->fds[i], 0); 274d6970e3bSNikolay Nikolaev 275d6970e3bSNikolay Nikolaev g_assert(guest_mem != MAP_FAILED); 276ae31fb54SMarc-André Lureau guest_mem += (s->memory.regions[i].mmap_offset / sizeof(*guest_mem)); 277a77e6b14SNikolay Nikolaev 2785a583cc5SPaolo Bonzini for (j = 0; j < 1024; j++) { 279bae6b59dSPaolo Bonzini uint32_t a = qtest_readb(qts, s->memory.regions[i].guest_phys_addr + j); 280a77e6b14SNikolay Nikolaev uint32_t b = guest_mem[j]; 281a77e6b14SNikolay Nikolaev 282a77e6b14SNikolay Nikolaev g_assert_cmpint(a, ==, b); 283a77e6b14SNikolay Nikolaev } 284a77e6b14SNikolay Nikolaev 285ae31fb54SMarc-André Lureau munmap(guest_mem, s->memory.regions[i].memory_size); 286a77e6b14SNikolay Nikolaev } 287a77e6b14SNikolay Nikolaev 288ae31fb54SMarc-André Lureau g_mutex_unlock(&s->data_mutex); 289a77e6b14SNikolay Nikolaev } 290a77e6b14SNikolay Nikolaev 291a77e6b14SNikolay Nikolaev static void *thread_function(void *data) 292a77e6b14SNikolay Nikolaev { 2939732baf6SMarc-André Lureau GMainLoop *loop = data; 294a77e6b14SNikolay Nikolaev g_main_loop_run(loop); 295a77e6b14SNikolay Nikolaev return NULL; 296a77e6b14SNikolay Nikolaev } 297a77e6b14SNikolay Nikolaev 298a77e6b14SNikolay Nikolaev static int chr_can_read(void *opaque) 299a77e6b14SNikolay Nikolaev { 300a77e6b14SNikolay Nikolaev return VHOST_USER_HDR_SIZE; 301a77e6b14SNikolay Nikolaev } 302a77e6b14SNikolay Nikolaev 303a77e6b14SNikolay Nikolaev static void chr_read(void *opaque, const uint8_t *buf, int size) 304a77e6b14SNikolay Nikolaev { 305ae31fb54SMarc-André Lureau TestServer *s = opaque; 30632a6ebecSMarc-André Lureau CharBackend *chr = &s->chr; 307a77e6b14SNikolay Nikolaev VhostUserMsg msg; 308a77e6b14SNikolay Nikolaev uint8_t *p = (uint8_t *) &msg; 30982248cd4SLi Qiang int fd = -1; 310a77e6b14SNikolay Nikolaev 3115d443f5aSMarc-André Lureau if (s->test_fail) { 3125345fdb4SMarc-André Lureau qemu_chr_fe_disconnect(chr); 3135d443f5aSMarc-André Lureau /* now switch to non-failure */ 3145d443f5aSMarc-André Lureau s->test_fail = false; 3155d443f5aSMarc-André Lureau } 3165d443f5aSMarc-André Lureau 317a77e6b14SNikolay Nikolaev if (size != VHOST_USER_HDR_SIZE) { 31813ee9e30SThomas Huth g_test_message("Wrong message size received %d", size); 319a77e6b14SNikolay Nikolaev return; 320a77e6b14SNikolay Nikolaev } 321a77e6b14SNikolay Nikolaev 322ae31fb54SMarc-André Lureau g_mutex_lock(&s->data_mutex); 323a77e6b14SNikolay Nikolaev memcpy(p, buf, VHOST_USER_HDR_SIZE); 324a77e6b14SNikolay Nikolaev 325a77e6b14SNikolay Nikolaev if (msg.size) { 326a77e6b14SNikolay Nikolaev p += VHOST_USER_HDR_SIZE; 3275345fdb4SMarc-André Lureau size = qemu_chr_fe_read_all(chr, p, msg.size); 3284616e359SMarc-André Lureau if (size != msg.size) { 32913ee9e30SThomas Huth g_test_message("Wrong message size received %d != %d", 3304616e359SMarc-André Lureau size, msg.size); 3314616e359SMarc-André Lureau return; 3324616e359SMarc-André Lureau } 333a77e6b14SNikolay Nikolaev } 334a77e6b14SNikolay Nikolaev 335a77e6b14SNikolay Nikolaev switch (msg.request) { 336a77e6b14SNikolay Nikolaev case VHOST_USER_GET_FEATURES: 337a77e6b14SNikolay Nikolaev /* send back features to qemu */ 338a77e6b14SNikolay Nikolaev msg.flags |= VHOST_USER_REPLY_MASK; 33912ebf690SMichael S. Tsirkin msg.size = sizeof(m.payload.u64); 34012ebf690SMichael S. Tsirkin msg.payload.u64 = 0x1ULL << VHOST_F_LOG_ALL | 341b1819747SMarc-André Lureau 0x1ULL << VHOST_USER_F_PROTOCOL_FEATURES; 342ed0a8d92SMarc-André Lureau if (s->queues > 1) { 343ed0a8d92SMarc-André Lureau msg.payload.u64 |= 0x1ULL << VIRTIO_NET_F_MQ; 344ed0a8d92SMarc-André Lureau } 3459294d76cSMarc-André Lureau if (s->test_flags >= TEST_FLAGS_BAD) { 3469294d76cSMarc-André Lureau msg.payload.u64 = 0; 3479294d76cSMarc-André Lureau s->test_flags = TEST_FLAGS_END; 3489294d76cSMarc-André Lureau } 3498a9b6b37SMichael S. Tsirkin p = (uint8_t *) &msg; 3505345fdb4SMarc-André Lureau qemu_chr_fe_write_all(chr, p, VHOST_USER_HDR_SIZE + msg.size); 3518a9b6b37SMichael S. Tsirkin break; 3528a9b6b37SMichael S. Tsirkin 3538a9b6b37SMichael S. Tsirkin case VHOST_USER_SET_FEATURES: 354892040dcSDima Stepanov if (s->vu_ops->set_features) { 355892040dcSDima Stepanov s->vu_ops->set_features(s, chr, &msg); 3569294d76cSMarc-André Lureau } 3578a9b6b37SMichael S. Tsirkin break; 3588a9b6b37SMichael S. Tsirkin 3598a9b6b37SMichael S. Tsirkin case VHOST_USER_GET_PROTOCOL_FEATURES: 360892040dcSDima Stepanov if (s->vu_ops->get_protocol_features) { 361892040dcSDima Stepanov s->vu_ops->get_protocol_features(s, chr, &msg); 362ed0a8d92SMarc-André Lureau } 363a77e6b14SNikolay Nikolaev break; 364a77e6b14SNikolay Nikolaev 365a77e6b14SNikolay Nikolaev case VHOST_USER_GET_VRING_BASE: 366a77e6b14SNikolay Nikolaev /* send back vring base to qemu */ 367a77e6b14SNikolay Nikolaev msg.flags |= VHOST_USER_REPLY_MASK; 36812ebf690SMichael S. Tsirkin msg.size = sizeof(m.payload.state); 36912ebf690SMichael S. Tsirkin msg.payload.state.num = 0; 370a77e6b14SNikolay Nikolaev p = (uint8_t *) &msg; 3715345fdb4SMarc-André Lureau qemu_chr_fe_write_all(chr, p, VHOST_USER_HDR_SIZE + msg.size); 372d08e42a1SMichael S. Tsirkin 373ed0a8d92SMarc-André Lureau assert(msg.payload.state.index < s->queues * 2); 374d08e42a1SMichael S. Tsirkin s->rings &= ~(0x1ULL << msg.payload.state.index); 375acca950cSPaolo Bonzini g_cond_broadcast(&s->data_cond); 376a77e6b14SNikolay Nikolaev break; 377a77e6b14SNikolay Nikolaev 378a77e6b14SNikolay Nikolaev case VHOST_USER_SET_MEM_TABLE: 379a77e6b14SNikolay Nikolaev /* received the mem table */ 38012ebf690SMichael S. Tsirkin memcpy(&s->memory, &msg.payload.memory, sizeof(msg.payload.memory)); 3815345fdb4SMarc-André Lureau s->fds_num = qemu_chr_fe_get_msgfds(chr, s->fds, 38232a6ebecSMarc-André Lureau G_N_ELEMENTS(s->fds)); 383a77e6b14SNikolay Nikolaev 384a77e6b14SNikolay Nikolaev /* signal the test that it can continue */ 38504ad1bf6SPaolo Bonzini g_cond_broadcast(&s->data_cond); 386a77e6b14SNikolay Nikolaev break; 387a77e6b14SNikolay Nikolaev 388a77e6b14SNikolay Nikolaev case VHOST_USER_SET_VRING_KICK: 389a77e6b14SNikolay Nikolaev case VHOST_USER_SET_VRING_CALL: 390a77e6b14SNikolay Nikolaev /* consume the fd */ 3915345fdb4SMarc-André Lureau qemu_chr_fe_get_msgfds(chr, &fd, 1); 392a77e6b14SNikolay Nikolaev /* 393a77e6b14SNikolay Nikolaev * This is a non-blocking eventfd. 394a77e6b14SNikolay Nikolaev * The receive function forces it to be blocking, 395a77e6b14SNikolay Nikolaev * so revert it back to non-blocking. 396a77e6b14SNikolay Nikolaev */ 397a77e6b14SNikolay Nikolaev qemu_set_nonblock(fd); 398a77e6b14SNikolay Nikolaev break; 399b1819747SMarc-André Lureau 400b1819747SMarc-André Lureau case VHOST_USER_SET_LOG_BASE: 401b1819747SMarc-André Lureau if (s->log_fd != -1) { 402b1819747SMarc-André Lureau close(s->log_fd); 403b1819747SMarc-André Lureau s->log_fd = -1; 404b1819747SMarc-André Lureau } 4055345fdb4SMarc-André Lureau qemu_chr_fe_get_msgfds(chr, &s->log_fd, 1); 406b1819747SMarc-André Lureau msg.flags |= VHOST_USER_REPLY_MASK; 407b1819747SMarc-André Lureau msg.size = 0; 408b1819747SMarc-André Lureau p = (uint8_t *) &msg; 4095345fdb4SMarc-André Lureau qemu_chr_fe_write_all(chr, p, VHOST_USER_HDR_SIZE); 410b1819747SMarc-André Lureau 41104ad1bf6SPaolo Bonzini g_cond_broadcast(&s->data_cond); 412b1819747SMarc-André Lureau break; 413b1819747SMarc-André Lureau 414d08e42a1SMichael S. Tsirkin case VHOST_USER_SET_VRING_BASE: 415ed0a8d92SMarc-André Lureau assert(msg.payload.state.index < s->queues * 2); 416d08e42a1SMichael S. Tsirkin s->rings |= 0x1ULL << msg.payload.state.index; 417acca950cSPaolo Bonzini g_cond_broadcast(&s->data_cond); 4181d9edff7SMarc-André Lureau break; 4191d9edff7SMarc-André Lureau 420ed0a8d92SMarc-André Lureau case VHOST_USER_GET_QUEUE_NUM: 421ed0a8d92SMarc-André Lureau msg.flags |= VHOST_USER_REPLY_MASK; 422ed0a8d92SMarc-André Lureau msg.size = sizeof(m.payload.u64); 423ed0a8d92SMarc-André Lureau msg.payload.u64 = s->queues; 424ed0a8d92SMarc-André Lureau p = (uint8_t *) &msg; 4255345fdb4SMarc-André Lureau qemu_chr_fe_write_all(chr, p, VHOST_USER_HDR_SIZE + msg.size); 426ed0a8d92SMarc-André Lureau break; 427ed0a8d92SMarc-André Lureau 428a77e6b14SNikolay Nikolaev default: 429a77e6b14SNikolay Nikolaev break; 430a77e6b14SNikolay Nikolaev } 431ae31fb54SMarc-André Lureau 432ae31fb54SMarc-André Lureau g_mutex_unlock(&s->data_mutex); 433a77e6b14SNikolay Nikolaev } 434a77e6b14SNikolay Nikolaev 4359ee1bb86SPaolo Bonzini static const char *init_hugepagefs(void) 436a77e6b14SNikolay Nikolaev { 4379ee1bb86SPaolo Bonzini #ifdef CONFIG_LINUX 438a3ebd6e0SPaolo Bonzini static const char *hugepagefs; 4399ee1bb86SPaolo Bonzini const char *path = getenv("QTEST_HUGETLBFS_PATH"); 440a77e6b14SNikolay Nikolaev struct statfs fs; 441a77e6b14SNikolay Nikolaev int ret; 442a77e6b14SNikolay Nikolaev 443a3ebd6e0SPaolo Bonzini if (hugepagefs) { 444a3ebd6e0SPaolo Bonzini return hugepagefs; 445a3ebd6e0SPaolo Bonzini } 4469ee1bb86SPaolo Bonzini if (!path) { 4479ee1bb86SPaolo Bonzini return NULL; 4489ee1bb86SPaolo Bonzini } 4499ee1bb86SPaolo Bonzini 450a77e6b14SNikolay Nikolaev if (access(path, R_OK | W_OK | X_OK)) { 45113ee9e30SThomas Huth g_test_message("access on path (%s): %s", path, strerror(errno)); 452a3ebd6e0SPaolo Bonzini g_test_fail(); 453a77e6b14SNikolay Nikolaev return NULL; 454a77e6b14SNikolay Nikolaev } 455a77e6b14SNikolay Nikolaev 456a77e6b14SNikolay Nikolaev do { 457a77e6b14SNikolay Nikolaev ret = statfs(path, &fs); 458a77e6b14SNikolay Nikolaev } while (ret != 0 && errno == EINTR); 459a77e6b14SNikolay Nikolaev 460a77e6b14SNikolay Nikolaev if (ret != 0) { 46113ee9e30SThomas Huth g_test_message("statfs on path (%s): %s", path, strerror(errno)); 462a3ebd6e0SPaolo Bonzini g_test_fail(); 463a77e6b14SNikolay Nikolaev return NULL; 464a77e6b14SNikolay Nikolaev } 465a77e6b14SNikolay Nikolaev 466a77e6b14SNikolay Nikolaev if (fs.f_type != HUGETLBFS_MAGIC) { 46713ee9e30SThomas Huth g_test_message("Warning: path not on HugeTLBFS: %s", path); 468a3ebd6e0SPaolo Bonzini g_test_fail(); 469a77e6b14SNikolay Nikolaev return NULL; 470a77e6b14SNikolay Nikolaev } 471a77e6b14SNikolay Nikolaev 472a3ebd6e0SPaolo Bonzini hugepagefs = path; 473a3ebd6e0SPaolo Bonzini return hugepagefs; 4749ee1bb86SPaolo Bonzini #else 4759ee1bb86SPaolo Bonzini return NULL; 476af3bba76SPaolo Bonzini #endif 4779ee1bb86SPaolo Bonzini } 478a77e6b14SNikolay Nikolaev 479892040dcSDima Stepanov static TestServer *test_server_new(const gchar *name, 480892040dcSDima Stepanov struct vhost_user_ops *ops) 481ae31fb54SMarc-André Lureau { 482ae31fb54SMarc-André Lureau TestServer *server = g_new0(TestServer, 1); 4834d3f50ebSPaolo Bonzini char template[] = "/tmp/vhost-test-XXXXXX"; 4844d3f50ebSPaolo Bonzini const char *tmpfs; 485ae31fb54SMarc-André Lureau 4867d0ca3e7SPaolo Bonzini server->context = g_main_context_new(); 4877d0ca3e7SPaolo Bonzini server->loop = g_main_loop_new(server->context, FALSE); 4887d0ca3e7SPaolo Bonzini 4897d0ca3e7SPaolo Bonzini /* run the main loop thread so the chardev may operate */ 4907d0ca3e7SPaolo Bonzini server->thread = g_thread_new(NULL, thread_function, server->loop); 4917d0ca3e7SPaolo Bonzini 4924d3f50ebSPaolo Bonzini tmpfs = mkdtemp(template); 4934d3f50ebSPaolo Bonzini if (!tmpfs) { 4944d3f50ebSPaolo Bonzini g_test_message("mkdtemp on path (%s): %s", template, strerror(errno)); 4954d3f50ebSPaolo Bonzini } 4964d3f50ebSPaolo Bonzini g_assert(tmpfs); 4974d3f50ebSPaolo Bonzini 4984d3f50ebSPaolo Bonzini server->tmpfs = g_strdup(tmpfs); 499ae31fb54SMarc-André Lureau server->socket_path = g_strdup_printf("%s/%s.sock", tmpfs, name); 500a899b1eaSMarc-André Lureau server->mig_path = g_strdup_printf("%s/%s.mig", tmpfs, name); 501ae31fb54SMarc-André Lureau server->chr_name = g_strdup_printf("chr-%s", name); 502ae31fb54SMarc-André Lureau 503ae31fb54SMarc-André Lureau g_mutex_init(&server->data_mutex); 504ae31fb54SMarc-André Lureau g_cond_init(&server->data_cond); 505ae31fb54SMarc-André Lureau 506b1819747SMarc-André Lureau server->log_fd = -1; 507ed0a8d92SMarc-André Lureau server->queues = 1; 508892040dcSDima Stepanov server->vu_ops = ops; 509b1819747SMarc-André Lureau 510ae31fb54SMarc-André Lureau return server; 511ae31fb54SMarc-André Lureau } 512ae31fb54SMarc-André Lureau 513083b266fSPhilippe Mathieu-Daudé static void chr_event(void *opaque, QEMUChrEvent event) 5149294d76cSMarc-André Lureau { 5159294d76cSMarc-André Lureau TestServer *s = opaque; 5169294d76cSMarc-André Lureau 5179294d76cSMarc-André Lureau if (s->test_flags == TEST_FLAGS_END && 5189294d76cSMarc-André Lureau event == CHR_EVENT_CLOSED) { 5199294d76cSMarc-André Lureau s->test_flags = TEST_FLAGS_OK; 5209294d76cSMarc-André Lureau } 5219294d76cSMarc-André Lureau } 5229294d76cSMarc-André Lureau 5234616e359SMarc-André Lureau static void test_server_create_chr(TestServer *server, const gchar *opt) 5244616e359SMarc-André Lureau { 5254616e359SMarc-André Lureau gchar *chr_path; 5260ec7b3e7SMarc-André Lureau Chardev *chr; 5275345fdb4SMarc-André Lureau 5284616e359SMarc-André Lureau chr_path = g_strdup_printf("unix:%s%s", server->socket_path, opt); 5297d0ca3e7SPaolo Bonzini chr = qemu_chr_new(server->chr_name, chr_path, server->context); 5304616e359SMarc-André Lureau g_free(chr_path); 5314616e359SMarc-André Lureau 532642e065aSMarc-André Lureau g_assert_nonnull(chr); 5335345fdb4SMarc-André Lureau qemu_chr_fe_init(&server->chr, chr, &error_abort); 5345345fdb4SMarc-André Lureau qemu_chr_fe_set_handlers(&server->chr, chr_can_read, chr_read, 5357d0ca3e7SPaolo Bonzini chr_event, NULL, server, server->context, true); 5364616e359SMarc-André Lureau } 5374616e359SMarc-André Lureau 5384616e359SMarc-André Lureau static void test_server_listen(TestServer *server) 5394616e359SMarc-André Lureau { 540*991c180dSPaolo Bonzini test_server_create_chr(server, ",server=on,wait=off"); 5414616e359SMarc-André Lureau } 5424616e359SMarc-André Lureau 5437d0ca3e7SPaolo Bonzini static void test_server_free(TestServer *server) 544ae31fb54SMarc-André Lureau { 5454d3f50ebSPaolo Bonzini int i, ret; 546ae31fb54SMarc-André Lureau 5477d0ca3e7SPaolo Bonzini /* finish the helper thread and dispatch pending sources */ 5487d0ca3e7SPaolo Bonzini g_main_loop_quit(server->loop); 5497d0ca3e7SPaolo Bonzini g_thread_join(server->thread); 5507d0ca3e7SPaolo Bonzini while (g_main_context_pending(NULL)) { 5517d0ca3e7SPaolo Bonzini g_main_context_iteration(NULL, TRUE); 5527d0ca3e7SPaolo Bonzini } 5537d0ca3e7SPaolo Bonzini 5544d3f50ebSPaolo Bonzini unlink(server->socket_path); 5554d3f50ebSPaolo Bonzini g_free(server->socket_path); 5564d3f50ebSPaolo Bonzini 5574d3f50ebSPaolo Bonzini unlink(server->mig_path); 5584d3f50ebSPaolo Bonzini g_free(server->mig_path); 5594d3f50ebSPaolo Bonzini 5604d3f50ebSPaolo Bonzini ret = rmdir(server->tmpfs); 5614d3f50ebSPaolo Bonzini if (ret != 0) { 5624d3f50ebSPaolo Bonzini g_test_message("unable to rmdir: path (%s): %s", 5634d3f50ebSPaolo Bonzini server->tmpfs, strerror(errno)); 5644d3f50ebSPaolo Bonzini } 5655411f3d0SMarc-André Lureau g_free(server->tmpfs); 5664d3f50ebSPaolo Bonzini 5671ce2610cSMarc-André Lureau qemu_chr_fe_deinit(&server->chr, true); 568ae31fb54SMarc-André Lureau 569ae31fb54SMarc-André Lureau for (i = 0; i < server->fds_num; i++) { 570ae31fb54SMarc-André Lureau close(server->fds[i]); 571ae31fb54SMarc-André Lureau } 572ae31fb54SMarc-André Lureau 573b1819747SMarc-André Lureau if (server->log_fd != -1) { 574b1819747SMarc-André Lureau close(server->log_fd); 575b1819747SMarc-André Lureau } 576b1819747SMarc-André Lureau 577b1819747SMarc-André Lureau g_free(server->chr_name); 5780c0eb302SMarc-André Lureau 5797d0ca3e7SPaolo Bonzini g_main_loop_unref(server->loop); 5807d0ca3e7SPaolo Bonzini g_main_context_unref(server->context); 5815411f3d0SMarc-André Lureau g_cond_clear(&server->data_cond); 5825411f3d0SMarc-André Lureau g_mutex_clear(&server->data_mutex); 583ae31fb54SMarc-André Lureau g_free(server); 584ae31fb54SMarc-André Lureau } 585ae31fb54SMarc-André Lureau 586b1819747SMarc-André Lureau static void wait_for_log_fd(TestServer *s) 587b1819747SMarc-André Lureau { 588b1819747SMarc-André Lureau gint64 end_time; 589b1819747SMarc-André Lureau 590b1819747SMarc-André Lureau g_mutex_lock(&s->data_mutex); 591b1819747SMarc-André Lureau end_time = g_get_monotonic_time() + 5 * G_TIME_SPAN_SECOND; 592b1819747SMarc-André Lureau while (s->log_fd == -1) { 593b1819747SMarc-André Lureau if (!g_cond_wait_until(&s->data_cond, &s->data_mutex, end_time)) { 594b1819747SMarc-André Lureau /* timeout has passed */ 595b1819747SMarc-André Lureau g_assert(s->log_fd != -1); 596b1819747SMarc-André Lureau break; 597b1819747SMarc-André Lureau } 598b1819747SMarc-André Lureau } 599b1819747SMarc-André Lureau 600b1819747SMarc-André Lureau g_mutex_unlock(&s->data_mutex); 601b1819747SMarc-André Lureau } 602b1819747SMarc-André Lureau 6033a87d009SPeter Maydell static void write_guest_mem(TestServer *s, uint32_t seed) 604b1819747SMarc-André Lureau { 605b1819747SMarc-André Lureau uint32_t *guest_mem; 606b1819747SMarc-André Lureau int i, j; 607b1819747SMarc-André Lureau size_t size; 608b1819747SMarc-André Lureau 609b1819747SMarc-André Lureau /* iterate all regions */ 610b1819747SMarc-André Lureau for (i = 0; i < s->fds_num; i++) { 611b1819747SMarc-André Lureau 612b1819747SMarc-André Lureau /* We'll write only the region statring at 0x0 */ 613b1819747SMarc-André Lureau if (s->memory.regions[i].guest_phys_addr != 0x0) { 614b1819747SMarc-André Lureau continue; 615b1819747SMarc-André Lureau } 616b1819747SMarc-André Lureau 617b1819747SMarc-André Lureau g_assert_cmpint(s->memory.regions[i].memory_size, >, 1024); 618b1819747SMarc-André Lureau 619b1819747SMarc-André Lureau size = s->memory.regions[i].memory_size + 620b1819747SMarc-André Lureau s->memory.regions[i].mmap_offset; 621b1819747SMarc-André Lureau 622b1819747SMarc-André Lureau guest_mem = mmap(0, size, PROT_READ | PROT_WRITE, 623b1819747SMarc-André Lureau MAP_SHARED, s->fds[i], 0); 624b1819747SMarc-André Lureau 625b1819747SMarc-André Lureau g_assert(guest_mem != MAP_FAILED); 626b1819747SMarc-André Lureau guest_mem += (s->memory.regions[i].mmap_offset / sizeof(*guest_mem)); 627b1819747SMarc-André Lureau 628b1819747SMarc-André Lureau for (j = 0; j < 256; j++) { 629b1819747SMarc-André Lureau guest_mem[j] = seed + j; 630b1819747SMarc-André Lureau } 631b1819747SMarc-André Lureau 632b1819747SMarc-André Lureau munmap(guest_mem, s->memory.regions[i].memory_size); 633b1819747SMarc-André Lureau break; 634b1819747SMarc-André Lureau } 635b1819747SMarc-André Lureau } 636b1819747SMarc-André Lureau 637b1819747SMarc-André Lureau static guint64 get_log_size(TestServer *s) 638b1819747SMarc-André Lureau { 639b1819747SMarc-André Lureau guint64 log_size = 0; 640b1819747SMarc-André Lureau int i; 641b1819747SMarc-André Lureau 642b1819747SMarc-André Lureau for (i = 0; i < s->memory.nregions; ++i) { 643b1819747SMarc-André Lureau VhostUserMemoryRegion *reg = &s->memory.regions[i]; 644b1819747SMarc-André Lureau guint64 last = range_get_last(reg->guest_phys_addr, 645b1819747SMarc-André Lureau reg->memory_size); 646b1819747SMarc-André Lureau log_size = MAX(log_size, last / (8 * VHOST_LOG_PAGE) + 1); 647b1819747SMarc-André Lureau } 648b1819747SMarc-André Lureau 649b1819747SMarc-André Lureau return log_size; 650b1819747SMarc-André Lureau } 651b1819747SMarc-André Lureau 6521d9edff7SMarc-André Lureau typedef struct TestMigrateSource { 6531d9edff7SMarc-André Lureau GSource source; 6541d9edff7SMarc-André Lureau TestServer *src; 6551d9edff7SMarc-André Lureau TestServer *dest; 6561d9edff7SMarc-André Lureau } TestMigrateSource; 6571d9edff7SMarc-André Lureau 6581d9edff7SMarc-André Lureau static gboolean 6591d9edff7SMarc-André Lureau test_migrate_source_check(GSource *source) 6601d9edff7SMarc-André Lureau { 6611d9edff7SMarc-André Lureau TestMigrateSource *t = (TestMigrateSource *)source; 662d08e42a1SMichael S. Tsirkin gboolean overlap = t->src->rings && t->dest->rings; 6631d9edff7SMarc-André Lureau 6641d9edff7SMarc-André Lureau g_assert(!overlap); 6651d9edff7SMarc-André Lureau 6661d9edff7SMarc-André Lureau return FALSE; 6671d9edff7SMarc-André Lureau } 6681d9edff7SMarc-André Lureau 6691d9edff7SMarc-André Lureau GSourceFuncs test_migrate_source_funcs = { 67045ce5126SMarc-André Lureau .check = test_migrate_source_check, 6711d9edff7SMarc-André Lureau }; 6721d9edff7SMarc-André Lureau 673a3ebd6e0SPaolo Bonzini static void vhost_user_test_cleanup(void *s) 674e364c703SMaxime Coquelin { 675a3ebd6e0SPaolo Bonzini TestServer *server = s; 676e364c703SMaxime Coquelin 677a3ebd6e0SPaolo Bonzini qos_invalidate_command_line(); 678e364c703SMaxime Coquelin test_server_free(server); 679e364c703SMaxime Coquelin } 680e364c703SMaxime Coquelin 681a3ebd6e0SPaolo Bonzini static void *vhost_user_test_setup(GString *cmd_line, void *arg) 682b1819747SMarc-André Lureau { 683892040dcSDima Stepanov TestServer *server = test_server_new("vhost-user-test", arg); 684a3ebd6e0SPaolo Bonzini test_server_listen(server); 685a3ebd6e0SPaolo Bonzini 686a3ebd6e0SPaolo Bonzini append_mem_opts(server, cmd_line, 256, TEST_MEMFD_AUTO); 687892040dcSDima Stepanov server->vu_ops->append_opts(server, cmd_line, ""); 688a3ebd6e0SPaolo Bonzini 689a3ebd6e0SPaolo Bonzini g_test_queue_destroy(vhost_user_test_cleanup, server); 690a3ebd6e0SPaolo Bonzini 691a3ebd6e0SPaolo Bonzini return server; 692a3ebd6e0SPaolo Bonzini } 693a3ebd6e0SPaolo Bonzini 694a3ebd6e0SPaolo Bonzini static void *vhost_user_test_setup_memfd(GString *cmd_line, void *arg) 695a3ebd6e0SPaolo Bonzini { 696892040dcSDima Stepanov TestServer *server = test_server_new("vhost-user-test", arg); 697a3ebd6e0SPaolo Bonzini test_server_listen(server); 698a3ebd6e0SPaolo Bonzini 699a3ebd6e0SPaolo Bonzini append_mem_opts(server, cmd_line, 256, TEST_MEMFD_YES); 700892040dcSDima Stepanov server->vu_ops->append_opts(server, cmd_line, ""); 701a3ebd6e0SPaolo Bonzini 702a3ebd6e0SPaolo Bonzini g_test_queue_destroy(vhost_user_test_cleanup, server); 703a3ebd6e0SPaolo Bonzini 704a3ebd6e0SPaolo Bonzini return server; 705a3ebd6e0SPaolo Bonzini } 706a3ebd6e0SPaolo Bonzini 707a3ebd6e0SPaolo Bonzini static void test_read_guest_mem(void *obj, void *arg, QGuestAllocator *alloc) 708a3ebd6e0SPaolo Bonzini { 709a3ebd6e0SPaolo Bonzini TestServer *server = arg; 710a3ebd6e0SPaolo Bonzini 711a3ebd6e0SPaolo Bonzini if (!wait_for_fds(server)) { 712a3ebd6e0SPaolo Bonzini return; 713a3ebd6e0SPaolo Bonzini } 714a3ebd6e0SPaolo Bonzini 715a3ebd6e0SPaolo Bonzini read_guest_mem_server(global_qtest, server); 716a3ebd6e0SPaolo Bonzini } 717a3ebd6e0SPaolo Bonzini 718a3ebd6e0SPaolo Bonzini static void test_migrate(void *obj, void *arg, QGuestAllocator *alloc) 719a3ebd6e0SPaolo Bonzini { 720a3ebd6e0SPaolo Bonzini TestServer *s = arg; 72199fd3178SThomas Huth TestServer *dest; 72299fd3178SThomas Huth GString *dest_cmdline; 72399fd3178SThomas Huth char *uri; 724a3ebd6e0SPaolo Bonzini QTestState *to; 7251d9edff7SMarc-André Lureau GSource *source; 726b1819747SMarc-André Lureau QDict *rsp; 727b1819747SMarc-André Lureau guint8 *log; 728b1819747SMarc-André Lureau guint64 size; 729b1819747SMarc-André Lureau 7303b72ca38SPaolo Bonzini if (!wait_for_fds(s)) { 731a3ebd6e0SPaolo Bonzini return; 7323b72ca38SPaolo Bonzini } 7333b72ca38SPaolo Bonzini 734892040dcSDima Stepanov dest = test_server_new("dest", s->vu_ops); 73599fd3178SThomas Huth dest_cmdline = g_string_new(qos_get_current_command_line()); 73699fd3178SThomas Huth uri = g_strdup_printf("%s%s", "unix:", dest->mig_path); 73799fd3178SThomas Huth 738b1819747SMarc-André Lureau size = get_log_size(s); 7390f9fe580SPaolo Bonzini g_assert_cmpint(size, ==, (256 * 1024 * 1024) / (VHOST_LOG_PAGE * 8)); 740b1819747SMarc-André Lureau 741a3ebd6e0SPaolo Bonzini test_server_listen(dest); 742a3ebd6e0SPaolo Bonzini g_string_append_printf(dest_cmdline, " -incoming %s", uri); 743a3ebd6e0SPaolo Bonzini append_mem_opts(dest, dest_cmdline, 256, TEST_MEMFD_AUTO); 744892040dcSDima Stepanov dest->vu_ops->append_opts(dest, dest_cmdline, ""); 745a3ebd6e0SPaolo Bonzini to = qtest_init(dest_cmdline->str); 746a3ebd6e0SPaolo Bonzini 747a3ebd6e0SPaolo Bonzini /* This would be where you call qos_allocate_objects(to, NULL), if you want 748a3ebd6e0SPaolo Bonzini * to talk to the QVirtioNet object on the destination. 749a3ebd6e0SPaolo Bonzini */ 750b1819747SMarc-André Lureau 7511d9edff7SMarc-André Lureau source = g_source_new(&test_migrate_source_funcs, 7521d9edff7SMarc-André Lureau sizeof(TestMigrateSource)); 7531d9edff7SMarc-André Lureau ((TestMigrateSource *)source)->src = s; 7541d9edff7SMarc-André Lureau ((TestMigrateSource *)source)->dest = dest; 7557d0ca3e7SPaolo Bonzini g_source_attach(source, s->context); 7561d9edff7SMarc-André Lureau 757b1819747SMarc-André Lureau /* slow down migration to have time to fiddle with log */ 758b1819747SMarc-André Lureau /* TODO: qtest could learn to break on some places */ 759b1819747SMarc-André Lureau rsp = qmp("{ 'execute': 'migrate_set_speed'," 760b1819747SMarc-André Lureau "'arguments': { 'value': 10 } }"); 761b1819747SMarc-André Lureau g_assert(qdict_haskey(rsp, "return")); 762cb3e7f08SMarc-André Lureau qobject_unref(rsp); 763b1819747SMarc-André Lureau 764015715f5SMarkus Armbruster rsp = qmp("{ 'execute': 'migrate', 'arguments': { 'uri': %s } }", uri); 765b1819747SMarc-André Lureau g_assert(qdict_haskey(rsp, "return")); 766cb3e7f08SMarc-André Lureau qobject_unref(rsp); 767b1819747SMarc-André Lureau 768b1819747SMarc-André Lureau wait_for_log_fd(s); 769b1819747SMarc-André Lureau 770b1819747SMarc-André Lureau log = mmap(0, size, PROT_READ | PROT_WRITE, MAP_SHARED, s->log_fd, 0); 771b1819747SMarc-André Lureau g_assert(log != MAP_FAILED); 772b1819747SMarc-André Lureau 773b1819747SMarc-André Lureau /* modify first page */ 774b1819747SMarc-André Lureau write_guest_mem(s, 0x42); 775b1819747SMarc-André Lureau log[0] = 1; 776b1819747SMarc-André Lureau munmap(log, size); 777b1819747SMarc-André Lureau 778b1819747SMarc-André Lureau /* speed things up */ 779b1819747SMarc-André Lureau rsp = qmp("{ 'execute': 'migrate_set_speed'," 780b1819747SMarc-André Lureau "'arguments': { 'value': 0 } }"); 781b1819747SMarc-André Lureau g_assert(qdict_haskey(rsp, "return")); 782cb3e7f08SMarc-André Lureau qobject_unref(rsp); 783b1819747SMarc-André Lureau 784b1819747SMarc-André Lureau qmp_eventwait("STOP"); 785bae6b59dSPaolo Bonzini qtest_qmp_eventwait(to, "RESUME"); 786b1819747SMarc-André Lureau 787bae6b59dSPaolo Bonzini g_assert(wait_for_fds(dest)); 788bae6b59dSPaolo Bonzini read_guest_mem_server(to, dest); 789b1819747SMarc-André Lureau 7901d9edff7SMarc-André Lureau g_source_destroy(source); 7911d9edff7SMarc-André Lureau g_source_unref(source); 7921d9edff7SMarc-André Lureau 793a3ebd6e0SPaolo Bonzini qtest_quit(to); 794b1819747SMarc-André Lureau test_server_free(dest); 795a899b1eaSMarc-André Lureau g_free(uri); 79699fd3178SThomas Huth g_string_free(dest_cmdline, true); 797b1819747SMarc-André Lureau } 798b1819747SMarc-André Lureau 7994616e359SMarc-André Lureau static void wait_for_rings_started(TestServer *s, size_t count) 8004616e359SMarc-André Lureau { 8014616e359SMarc-André Lureau gint64 end_time; 8024616e359SMarc-André Lureau 8034616e359SMarc-André Lureau g_mutex_lock(&s->data_mutex); 8044616e359SMarc-André Lureau end_time = g_get_monotonic_time() + 5 * G_TIME_SPAN_SECOND; 8054616e359SMarc-André Lureau while (ctpop64(s->rings) != count) { 8064616e359SMarc-André Lureau if (!g_cond_wait_until(&s->data_cond, &s->data_mutex, end_time)) { 8074616e359SMarc-André Lureau /* timeout has passed */ 8084616e359SMarc-André Lureau g_assert_cmpint(ctpop64(s->rings), ==, count); 8094616e359SMarc-André Lureau break; 8104616e359SMarc-André Lureau } 8114616e359SMarc-André Lureau } 8124616e359SMarc-André Lureau 8134616e359SMarc-André Lureau g_mutex_unlock(&s->data_mutex); 8144616e359SMarc-André Lureau } 8154616e359SMarc-André Lureau 81620784087SPhilippe Mathieu-Daudé static inline void test_server_connect(TestServer *server) 81720784087SPhilippe Mathieu-Daudé { 81820784087SPhilippe Mathieu-Daudé test_server_create_chr(server, ",reconnect=1"); 81920784087SPhilippe Mathieu-Daudé } 82020784087SPhilippe Mathieu-Daudé 8214616e359SMarc-André Lureau static gboolean 8224616e359SMarc-André Lureau reconnect_cb(gpointer user_data) 8234616e359SMarc-André Lureau { 8244616e359SMarc-André Lureau TestServer *s = user_data; 8254616e359SMarc-André Lureau 8265345fdb4SMarc-André Lureau qemu_chr_fe_disconnect(&s->chr); 8274616e359SMarc-André Lureau 8284616e359SMarc-André Lureau return FALSE; 8294616e359SMarc-André Lureau } 8304616e359SMarc-André Lureau 8314616e359SMarc-André Lureau static gpointer 8324616e359SMarc-André Lureau connect_thread(gpointer data) 8334616e359SMarc-André Lureau { 8344616e359SMarc-André Lureau TestServer *s = data; 8354616e359SMarc-André Lureau 8364616e359SMarc-André Lureau /* wait for qemu to start before first try, to avoid extra warnings */ 8374616e359SMarc-André Lureau g_usleep(G_USEC_PER_SEC); 8384616e359SMarc-André Lureau test_server_connect(s); 8394616e359SMarc-André Lureau 8404616e359SMarc-André Lureau return NULL; 8414616e359SMarc-André Lureau } 8424616e359SMarc-André Lureau 843a3ebd6e0SPaolo Bonzini static void *vhost_user_test_setup_reconnect(GString *cmd_line, void *arg) 8444616e359SMarc-André Lureau { 845892040dcSDima Stepanov TestServer *s = test_server_new("reconnect", arg); 8464616e359SMarc-André Lureau 8474616e359SMarc-André Lureau g_thread_new("connect", connect_thread, s); 848a3ebd6e0SPaolo Bonzini append_mem_opts(s, cmd_line, 256, TEST_MEMFD_AUTO); 849*991c180dSPaolo Bonzini s->vu_ops->append_opts(s, cmd_line, ",server=on"); 8504616e359SMarc-André Lureau 851a3ebd6e0SPaolo Bonzini g_test_queue_destroy(vhost_user_test_cleanup, s); 852a3ebd6e0SPaolo Bonzini 853a3ebd6e0SPaolo Bonzini return s; 854a3ebd6e0SPaolo Bonzini } 855a3ebd6e0SPaolo Bonzini 856a3ebd6e0SPaolo Bonzini static void test_reconnect(void *obj, void *arg, QGuestAllocator *alloc) 857a3ebd6e0SPaolo Bonzini { 858a3ebd6e0SPaolo Bonzini TestServer *s = arg; 859a3ebd6e0SPaolo Bonzini GSource *src; 860a3ebd6e0SPaolo Bonzini 8613b72ca38SPaolo Bonzini if (!wait_for_fds(s)) { 862a3ebd6e0SPaolo Bonzini return; 8633b72ca38SPaolo Bonzini } 8643b72ca38SPaolo Bonzini 8654616e359SMarc-André Lureau wait_for_rings_started(s, 2); 8664616e359SMarc-André Lureau 8674616e359SMarc-André Lureau /* reconnect */ 8684616e359SMarc-André Lureau s->fds_num = 0; 8694616e359SMarc-André Lureau s->rings = 0; 8707d0ca3e7SPaolo Bonzini src = g_idle_source_new(); 8717d0ca3e7SPaolo Bonzini g_source_set_callback(src, reconnect_cb, s, NULL); 8727d0ca3e7SPaolo Bonzini g_source_attach(src, s->context); 8737d0ca3e7SPaolo Bonzini g_source_unref(src); 8743b72ca38SPaolo Bonzini g_assert(wait_for_fds(s)); 8754616e359SMarc-André Lureau wait_for_rings_started(s, 2); 8764616e359SMarc-André Lureau } 8774616e359SMarc-André Lureau 878a3ebd6e0SPaolo Bonzini static void *vhost_user_test_setup_connect_fail(GString *cmd_line, void *arg) 8795d443f5aSMarc-André Lureau { 880892040dcSDima Stepanov TestServer *s = test_server_new("connect-fail", arg); 8815d443f5aSMarc-André Lureau 8825d443f5aSMarc-André Lureau s->test_fail = true; 883a3ebd6e0SPaolo Bonzini 8845d443f5aSMarc-André Lureau g_thread_new("connect", connect_thread, s); 885a3ebd6e0SPaolo Bonzini append_mem_opts(s, cmd_line, 256, TEST_MEMFD_AUTO); 886*991c180dSPaolo Bonzini s->vu_ops->append_opts(s, cmd_line, ",server=on"); 8875d443f5aSMarc-André Lureau 888a3ebd6e0SPaolo Bonzini g_test_queue_destroy(vhost_user_test_cleanup, s); 8895d443f5aSMarc-André Lureau 890a3ebd6e0SPaolo Bonzini return s; 8915d443f5aSMarc-André Lureau } 8925d443f5aSMarc-André Lureau 893a3ebd6e0SPaolo Bonzini static void *vhost_user_test_setup_flags_mismatch(GString *cmd_line, void *arg) 8949294d76cSMarc-André Lureau { 895892040dcSDima Stepanov TestServer *s = test_server_new("flags-mismatch", arg); 8969294d76cSMarc-André Lureau 8979294d76cSMarc-André Lureau s->test_flags = TEST_FLAGS_DISCONNECT; 8989294d76cSMarc-André Lureau 899a3ebd6e0SPaolo Bonzini g_thread_new("connect", connect_thread, s); 900a3ebd6e0SPaolo Bonzini append_mem_opts(s, cmd_line, 256, TEST_MEMFD_AUTO); 901*991c180dSPaolo Bonzini s->vu_ops->append_opts(s, cmd_line, ",server=on"); 902a3ebd6e0SPaolo Bonzini 903a3ebd6e0SPaolo Bonzini g_test_queue_destroy(vhost_user_test_cleanup, s); 904a3ebd6e0SPaolo Bonzini 905a3ebd6e0SPaolo Bonzini return s; 906a3ebd6e0SPaolo Bonzini } 907a3ebd6e0SPaolo Bonzini 908a3ebd6e0SPaolo Bonzini static void test_vhost_user_started(void *obj, void *arg, QGuestAllocator *alloc) 909a3ebd6e0SPaolo Bonzini { 910a3ebd6e0SPaolo Bonzini TestServer *s = arg; 911a3ebd6e0SPaolo Bonzini 9123b72ca38SPaolo Bonzini if (!wait_for_fds(s)) { 913a3ebd6e0SPaolo Bonzini return; 9143b72ca38SPaolo Bonzini } 9159294d76cSMarc-André Lureau wait_for_rings_started(s, 2); 9169294d76cSMarc-André Lureau } 9179294d76cSMarc-André Lureau 918a3ebd6e0SPaolo Bonzini static void *vhost_user_test_setup_multiqueue(GString *cmd_line, void *arg) 9199294d76cSMarc-André Lureau { 920a3ebd6e0SPaolo Bonzini TestServer *s = vhost_user_test_setup(cmd_line, arg); 9219294d76cSMarc-André Lureau 922459f5d29SMaxime Coquelin s->queues = 2; 923a3ebd6e0SPaolo Bonzini g_string_append_printf(cmd_line, 924a3ebd6e0SPaolo Bonzini " -set netdev.hs0.queues=%d" 925a3ebd6e0SPaolo Bonzini " -global virtio-net-pci.vectors=%d", 926a3ebd6e0SPaolo Bonzini s->queues, s->queues * 2 + 2); 927ed0a8d92SMarc-André Lureau 928a3ebd6e0SPaolo Bonzini return s; 9298e029fd6SMarc-André Lureau } 930ed0a8d92SMarc-André Lureau 931a3ebd6e0SPaolo Bonzini static void test_multiqueue(void *obj, void *arg, QGuestAllocator *alloc) 932a3ebd6e0SPaolo Bonzini { 933a3ebd6e0SPaolo Bonzini TestServer *s = arg; 934ed0a8d92SMarc-André Lureau 935459f5d29SMaxime Coquelin wait_for_rings_started(s, s->queues * 2); 936ed0a8d92SMarc-André Lureau } 937ed0a8d92SMarc-André Lureau 938892040dcSDima Stepanov static void vu_net_set_features(TestServer *s, CharBackend *chr, 939892040dcSDima Stepanov VhostUserMsg *msg) 940892040dcSDima Stepanov { 941892040dcSDima Stepanov g_assert_cmpint(msg->payload.u64 & 942892040dcSDima Stepanov (0x1ULL << VHOST_USER_F_PROTOCOL_FEATURES), !=, 0ULL); 943892040dcSDima Stepanov if (s->test_flags == TEST_FLAGS_DISCONNECT) { 944892040dcSDima Stepanov qemu_chr_fe_disconnect(chr); 945892040dcSDima Stepanov s->test_flags = TEST_FLAGS_BAD; 946892040dcSDima Stepanov } 947892040dcSDima Stepanov } 948892040dcSDima Stepanov 949892040dcSDima Stepanov static void vu_net_get_protocol_features(TestServer *s, CharBackend *chr, 950892040dcSDima Stepanov VhostUserMsg *msg) 951892040dcSDima Stepanov { 952892040dcSDima Stepanov /* send back features to qemu */ 953892040dcSDima Stepanov msg->flags |= VHOST_USER_REPLY_MASK; 954892040dcSDima Stepanov msg->size = sizeof(m.payload.u64); 955892040dcSDima Stepanov msg->payload.u64 = 1 << VHOST_USER_PROTOCOL_F_LOG_SHMFD; 956892040dcSDima Stepanov msg->payload.u64 |= 1 << VHOST_USER_PROTOCOL_F_CROSS_ENDIAN; 957892040dcSDima Stepanov if (s->queues > 1) { 958892040dcSDima Stepanov msg->payload.u64 |= 1 << VHOST_USER_PROTOCOL_F_MQ; 959892040dcSDima Stepanov } 960892040dcSDima Stepanov qemu_chr_fe_write_all(chr, (uint8_t *)msg, VHOST_USER_HDR_SIZE + msg->size); 961892040dcSDima Stepanov } 962892040dcSDima Stepanov 963892040dcSDima Stepanov /* Each VHOST-USER device should have its ops structure defined. */ 964892040dcSDima Stepanov static struct vhost_user_ops g_vu_net_ops = { 965892040dcSDima Stepanov .type = VHOST_USER_NET, 966892040dcSDima Stepanov 967892040dcSDima Stepanov .append_opts = append_vhost_net_opts, 968892040dcSDima Stepanov 969892040dcSDima Stepanov .set_features = vu_net_set_features, 970892040dcSDima Stepanov .get_protocol_features = vu_net_get_protocol_features, 971892040dcSDima Stepanov }; 972892040dcSDima Stepanov 973a3ebd6e0SPaolo Bonzini static void register_vhost_user_test(void) 974a77e6b14SNikolay Nikolaev { 975a3ebd6e0SPaolo Bonzini QOSGraphTestOptions opts = { 976a3ebd6e0SPaolo Bonzini .before = vhost_user_test_setup, 977a3ebd6e0SPaolo Bonzini .subprocess = true, 978892040dcSDima Stepanov .arg = &g_vu_net_ops, 979a3ebd6e0SPaolo Bonzini }; 980a77e6b14SNikolay Nikolaev 981ae31fb54SMarc-André Lureau qemu_add_opts(&qemu_chardev_opts); 982a77e6b14SNikolay Nikolaev 983a3ebd6e0SPaolo Bonzini qos_add_test("vhost-user/read-guest-mem/memfile", 984a3ebd6e0SPaolo Bonzini "virtio-net", 985a3ebd6e0SPaolo Bonzini test_read_guest_mem, &opts); 986a3ebd6e0SPaolo Bonzini 9874a66c7a9SIlya Maximets if (qemu_memfd_check(MFD_ALLOW_SEALING)) { 988a3ebd6e0SPaolo Bonzini opts.before = vhost_user_test_setup_memfd; 989a3ebd6e0SPaolo Bonzini qos_add_test("vhost-user/read-guest-mem/memfd", 990a3ebd6e0SPaolo Bonzini "virtio-net", 991a3ebd6e0SPaolo Bonzini test_read_guest_mem, &opts); 9928e029fd6SMarc-André Lureau } 993a3ebd6e0SPaolo Bonzini 994a3ebd6e0SPaolo Bonzini qos_add_test("vhost-user/migrate", 995a3ebd6e0SPaolo Bonzini "virtio-net", 996a3ebd6e0SPaolo Bonzini test_migrate, &opts); 99720784087SPhilippe Mathieu-Daudé 9987a9ec654SMarc-André Lureau /* keeps failing on build-system since Aug 15 2017 */ 9997a9ec654SMarc-André Lureau if (getenv("QTEST_VHOST_USER_FIXME")) { 1000a3ebd6e0SPaolo Bonzini opts.before = vhost_user_test_setup_reconnect; 1001a3ebd6e0SPaolo Bonzini qos_add_test("vhost-user/reconnect", "virtio-net", 1002a3ebd6e0SPaolo Bonzini test_reconnect, &opts); 1003a3ebd6e0SPaolo Bonzini 1004a3ebd6e0SPaolo Bonzini opts.before = vhost_user_test_setup_connect_fail; 1005a3ebd6e0SPaolo Bonzini qos_add_test("vhost-user/connect-fail", "virtio-net", 1006a3ebd6e0SPaolo Bonzini test_vhost_user_started, &opts); 1007a3ebd6e0SPaolo Bonzini 1008a3ebd6e0SPaolo Bonzini opts.before = vhost_user_test_setup_flags_mismatch; 1009a3ebd6e0SPaolo Bonzini qos_add_test("vhost-user/flags-mismatch", "virtio-net", 1010a3ebd6e0SPaolo Bonzini test_vhost_user_started, &opts); 10117a9ec654SMarc-André Lureau } 1012a77e6b14SNikolay Nikolaev 1013a3ebd6e0SPaolo Bonzini opts.before = vhost_user_test_setup_multiqueue; 1014a3ebd6e0SPaolo Bonzini opts.edge.extra_device_opts = "mq=on"; 1015a3ebd6e0SPaolo Bonzini qos_add_test("vhost-user/multiqueue", 1016a3ebd6e0SPaolo Bonzini "virtio-net", 1017a3ebd6e0SPaolo Bonzini test_multiqueue, &opts); 1018a77e6b14SNikolay Nikolaev } 1019a3ebd6e0SPaolo Bonzini libqos_init(register_vhost_user_test); 1020