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 <glib.h> 12 13 #include "libqtest.h" 14 #include "qemu/option.h" 15 #include "sysemu/char.h" 16 #include "sysemu/sysemu.h" 17 18 #include <linux/vhost.h> 19 #include <sys/mman.h> 20 #include <sys/vfs.h> 21 #include <qemu/sockets.h> 22 23 /* GLIB version compatibility flags */ 24 #if !GLIB_CHECK_VERSION(2, 26, 0) 25 #define G_TIME_SPAN_SECOND (G_GINT64_CONSTANT(1000000)) 26 #endif 27 28 #if GLIB_CHECK_VERSION(2, 28, 0) 29 #define HAVE_MONOTONIC_TIME 30 #endif 31 32 #define QEMU_CMD_ACCEL " -machine accel=tcg" 33 #define QEMU_CMD_MEM " -m 512 -object memory-backend-file,id=mem,size=512M,"\ 34 "mem-path=%s,share=on -numa node,memdev=mem" 35 #define QEMU_CMD_CHR " -chardev socket,id=chr0,path=%s" 36 #define QEMU_CMD_NETDEV " -netdev vhost-user,id=net0,chardev=chr0,vhostforce" 37 #define QEMU_CMD_NET " -device virtio-net-pci,netdev=net0 " 38 #define QEMU_CMD_ROM " -option-rom ../pc-bios/pxe-virtio.rom" 39 40 #define QEMU_CMD QEMU_CMD_ACCEL QEMU_CMD_MEM QEMU_CMD_CHR \ 41 QEMU_CMD_NETDEV QEMU_CMD_NET QEMU_CMD_ROM 42 43 #define HUGETLBFS_MAGIC 0x958458f6 44 45 /*********** FROM hw/virtio/vhost-user.c *************************************/ 46 47 #define VHOST_MEMORY_MAX_NREGIONS 8 48 49 typedef enum VhostUserRequest { 50 VHOST_USER_NONE = 0, 51 VHOST_USER_GET_FEATURES = 1, 52 VHOST_USER_SET_FEATURES = 2, 53 VHOST_USER_SET_OWNER = 3, 54 VHOST_USER_RESET_DEVICE = 4, 55 VHOST_USER_SET_MEM_TABLE = 5, 56 VHOST_USER_SET_LOG_BASE = 6, 57 VHOST_USER_SET_LOG_FD = 7, 58 VHOST_USER_SET_VRING_NUM = 8, 59 VHOST_USER_SET_VRING_ADDR = 9, 60 VHOST_USER_SET_VRING_BASE = 10, 61 VHOST_USER_GET_VRING_BASE = 11, 62 VHOST_USER_SET_VRING_KICK = 12, 63 VHOST_USER_SET_VRING_CALL = 13, 64 VHOST_USER_SET_VRING_ERR = 14, 65 VHOST_USER_MAX 66 } VhostUserRequest; 67 68 typedef struct VhostUserMemoryRegion { 69 uint64_t guest_phys_addr; 70 uint64_t memory_size; 71 uint64_t userspace_addr; 72 uint64_t mmap_offset; 73 } VhostUserMemoryRegion; 74 75 typedef struct VhostUserMemory { 76 uint32_t nregions; 77 uint32_t padding; 78 VhostUserMemoryRegion regions[VHOST_MEMORY_MAX_NREGIONS]; 79 } VhostUserMemory; 80 81 typedef struct VhostUserMsg { 82 VhostUserRequest request; 83 84 #define VHOST_USER_VERSION_MASK (0x3) 85 #define VHOST_USER_REPLY_MASK (0x1<<2) 86 uint32_t flags; 87 uint32_t size; /* the following payload size */ 88 union { 89 uint64_t u64; 90 struct vhost_vring_state state; 91 struct vhost_vring_addr addr; 92 VhostUserMemory memory; 93 }; 94 } QEMU_PACKED VhostUserMsg; 95 96 static VhostUserMsg m __attribute__ ((unused)); 97 #define VHOST_USER_HDR_SIZE (sizeof(m.request) \ 98 + sizeof(m.flags) \ 99 + sizeof(m.size)) 100 101 #define VHOST_USER_PAYLOAD_SIZE (sizeof(m) - VHOST_USER_HDR_SIZE) 102 103 /* The version of the protocol we support */ 104 #define VHOST_USER_VERSION (0x1) 105 /*****************************************************************************/ 106 107 int fds_num = 0, fds[VHOST_MEMORY_MAX_NREGIONS]; 108 static VhostUserMemory memory; 109 static CompatGMutex data_mutex; 110 static CompatGCond data_cond; 111 112 #if !GLIB_CHECK_VERSION(2, 32, 0) 113 static gboolean g_cond_wait_until(CompatGCond cond, CompatGMutex mutex, 114 gint64 end_time) 115 { 116 gboolean ret = FALSE; 117 end_time -= g_get_monotonic_time(); 118 GTimeVal time = { end_time / G_TIME_SPAN_SECOND, 119 end_time % G_TIME_SPAN_SECOND }; 120 ret = g_cond_timed_wait(cond, mutex, &time); 121 return ret; 122 } 123 #endif 124 125 static void read_guest_mem(void) 126 { 127 uint32_t *guest_mem; 128 gint64 end_time; 129 int i, j; 130 size_t size; 131 132 g_mutex_lock(&data_mutex); 133 134 end_time = g_get_monotonic_time() + 5 * G_TIME_SPAN_SECOND; 135 while (!fds_num) { 136 if (!g_cond_wait_until(&data_cond, &data_mutex, end_time)) { 137 /* timeout has passed */ 138 g_assert(fds_num); 139 break; 140 } 141 } 142 143 /* check for sanity */ 144 g_assert_cmpint(fds_num, >, 0); 145 g_assert_cmpint(fds_num, ==, memory.nregions); 146 147 /* iterate all regions */ 148 for (i = 0; i < fds_num; i++) { 149 150 /* We'll check only the region statring at 0x0*/ 151 if (memory.regions[i].guest_phys_addr != 0x0) { 152 continue; 153 } 154 155 g_assert_cmpint(memory.regions[i].memory_size, >, 1024); 156 157 size = memory.regions[i].memory_size + memory.regions[i].mmap_offset; 158 159 guest_mem = mmap(0, size, PROT_READ | PROT_WRITE, 160 MAP_SHARED, fds[i], 0); 161 162 g_assert(guest_mem != MAP_FAILED); 163 guest_mem += (memory.regions[i].mmap_offset / sizeof(*guest_mem)); 164 165 for (j = 0; j < 256; j++) { 166 uint32_t a = readl(memory.regions[i].guest_phys_addr + j*4); 167 uint32_t b = guest_mem[j]; 168 169 g_assert_cmpint(a, ==, b); 170 } 171 172 munmap(guest_mem, memory.regions[i].memory_size); 173 } 174 175 g_assert_cmpint(1, ==, 1); 176 g_mutex_unlock(&data_mutex); 177 } 178 179 static void *thread_function(void *data) 180 { 181 GMainLoop *loop; 182 loop = g_main_loop_new(NULL, FALSE); 183 g_main_loop_run(loop); 184 return NULL; 185 } 186 187 static int chr_can_read(void *opaque) 188 { 189 return VHOST_USER_HDR_SIZE; 190 } 191 192 static void chr_read(void *opaque, const uint8_t *buf, int size) 193 { 194 CharDriverState *chr = opaque; 195 VhostUserMsg msg; 196 uint8_t *p = (uint8_t *) &msg; 197 int fd; 198 199 if (size != VHOST_USER_HDR_SIZE) { 200 g_test_message("Wrong message size received %d\n", size); 201 return; 202 } 203 204 g_mutex_lock(&data_mutex); 205 memcpy(p, buf, VHOST_USER_HDR_SIZE); 206 207 if (msg.size) { 208 p += VHOST_USER_HDR_SIZE; 209 qemu_chr_fe_read_all(chr, p, msg.size); 210 } 211 212 switch (msg.request) { 213 case VHOST_USER_GET_FEATURES: 214 /* send back features to qemu */ 215 msg.flags |= VHOST_USER_REPLY_MASK; 216 msg.size = sizeof(m.u64); 217 msg.u64 = 0; 218 p = (uint8_t *) &msg; 219 qemu_chr_fe_write_all(chr, p, VHOST_USER_HDR_SIZE + msg.size); 220 break; 221 222 case VHOST_USER_GET_VRING_BASE: 223 /* send back vring base to qemu */ 224 msg.flags |= VHOST_USER_REPLY_MASK; 225 msg.size = sizeof(m.state); 226 msg.state.num = 0; 227 p = (uint8_t *) &msg; 228 qemu_chr_fe_write_all(chr, p, VHOST_USER_HDR_SIZE + msg.size); 229 break; 230 231 case VHOST_USER_SET_MEM_TABLE: 232 /* received the mem table */ 233 memcpy(&memory, &msg.memory, sizeof(msg.memory)); 234 fds_num = qemu_chr_fe_get_msgfds(chr, fds, sizeof(fds) / sizeof(int)); 235 236 /* signal the test that it can continue */ 237 g_cond_signal(&data_cond); 238 break; 239 240 case VHOST_USER_SET_VRING_KICK: 241 case VHOST_USER_SET_VRING_CALL: 242 /* consume the fd */ 243 qemu_chr_fe_get_msgfds(chr, &fd, 1); 244 /* 245 * This is a non-blocking eventfd. 246 * The receive function forces it to be blocking, 247 * so revert it back to non-blocking. 248 */ 249 qemu_set_nonblock(fd); 250 break; 251 default: 252 break; 253 } 254 g_mutex_unlock(&data_mutex); 255 } 256 257 static const char *init_hugepagefs(void) 258 { 259 const char *path; 260 struct statfs fs; 261 int ret; 262 263 path = getenv("QTEST_HUGETLBFS_PATH"); 264 if (!path) { 265 path = "/hugetlbfs"; 266 } 267 268 if (access(path, R_OK | W_OK | X_OK)) { 269 g_test_message("access on path (%s): %s\n", path, strerror(errno)); 270 return NULL; 271 } 272 273 do { 274 ret = statfs(path, &fs); 275 } while (ret != 0 && errno == EINTR); 276 277 if (ret != 0) { 278 g_test_message("statfs on path (%s): %s\n", path, strerror(errno)); 279 return NULL; 280 } 281 282 if (fs.f_type != HUGETLBFS_MAGIC) { 283 g_test_message("Warning: path not on HugeTLBFS: %s\n", path); 284 return NULL; 285 } 286 287 return path; 288 } 289 290 int main(int argc, char **argv) 291 { 292 QTestState *s = NULL; 293 CharDriverState *chr = NULL; 294 const char *hugefs = 0; 295 char *socket_path = 0; 296 char *qemu_cmd = 0; 297 char *chr_path = 0; 298 int ret; 299 300 g_test_init(&argc, &argv, NULL); 301 302 module_call_init(MODULE_INIT_QOM); 303 304 hugefs = init_hugepagefs(); 305 if (!hugefs) { 306 return 0; 307 } 308 309 socket_path = g_strdup_printf("/tmp/vhost-%d.sock", getpid()); 310 311 /* create char dev and add read handlers */ 312 qemu_add_opts(&qemu_chardev_opts); 313 chr_path = g_strdup_printf("unix:%s,server,nowait", socket_path); 314 chr = qemu_chr_new("chr0", chr_path, NULL); 315 g_free(chr_path); 316 qemu_chr_add_handlers(chr, chr_can_read, chr_read, NULL, chr); 317 318 /* run the main loop thread so the chardev may operate */ 319 g_mutex_init(&data_mutex); 320 g_cond_init(&data_cond); 321 g_thread_new(NULL, thread_function, NULL); 322 323 qemu_cmd = g_strdup_printf(QEMU_CMD, hugefs, socket_path); 324 s = qtest_start(qemu_cmd); 325 g_free(qemu_cmd); 326 327 qtest_add_func("/vhost-user/read-guest-mem", read_guest_mem); 328 329 ret = g_test_run(); 330 331 if (s) { 332 qtest_quit(s); 333 } 334 335 /* cleanup */ 336 unlink(socket_path); 337 g_free(socket_path); 338 339 return ret; 340 } 341