vhost-user-test: do not reinvent glib-compat.h
[qemu/ar7.git] / tests / vhost-user-test.c
blob0e04f06839b3661e87dc783572cdd76bb8db51d3
1 /*
2 * QTest testcase for the vhost-user
4 * Copyright (c) 2014 Virtual Open Systems Sarl.
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.
9 */
11 #include <glib.h>
13 #include "libqtest.h"
14 #include "qemu/option.h"
15 #include "sysemu/char.h"
16 #include "sysemu/sysemu.h"
18 #include <linux/vhost.h>
19 #include <sys/mman.h>
20 #include <sys/vfs.h>
21 #include <qemu/sockets.h>
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
28 #if GLIB_CHECK_VERSION(2, 28, 0)
29 #define HAVE_MONOTONIC_TIME
30 #endif
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"
40 #define QEMU_CMD QEMU_CMD_ACCEL QEMU_CMD_MEM QEMU_CMD_CHR \
41 QEMU_CMD_NETDEV QEMU_CMD_NET QEMU_CMD_ROM
43 #define HUGETLBFS_MAGIC 0x958458f6
45 /*********** FROM hw/virtio/vhost-user.c *************************************/
47 #define VHOST_MEMORY_MAX_NREGIONS 8
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;
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;
75 typedef struct VhostUserMemory {
76 uint32_t nregions;
77 uint32_t padding;
78 VhostUserMemoryRegion regions[VHOST_MEMORY_MAX_NREGIONS];
79 } VhostUserMemory;
81 typedef struct VhostUserMsg {
82 VhostUserRequest request;
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;
94 } QEMU_PACKED VhostUserMsg;
96 static VhostUserMsg m __attribute__ ((unused));
97 #define VHOST_USER_HDR_SIZE (sizeof(m.request) \
98 + sizeof(m.flags) \
99 + sizeof(m.size))
101 #define VHOST_USER_PAYLOAD_SIZE (sizeof(m) - VHOST_USER_HDR_SIZE)
103 /* The version of the protocol we support */
104 #define VHOST_USER_VERSION (0x1)
105 /*****************************************************************************/
107 int fds_num = 0, fds[VHOST_MEMORY_MAX_NREGIONS];
108 static VhostUserMemory memory;
109 static CompatGMutex data_mutex;
110 static CompatGCond data_cond;
112 #if !GLIB_CHECK_VERSION(2, 32, 0)
113 static gboolean g_cond_wait_until(CompatGCond cond, CompatGMutex mutex,
114 gint64 end_time)
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;
123 #endif
125 static void read_guest_mem(void)
127 uint32_t *guest_mem;
128 gint64 end_time;
129 int i, j;
130 size_t size;
132 g_mutex_lock(&data_mutex);
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;
143 /* check for sanity */
144 g_assert_cmpint(fds_num, >, 0);
145 g_assert_cmpint(fds_num, ==, memory.nregions);
147 /* iterate all regions */
148 for (i = 0; i < fds_num; i++) {
150 /* We'll check only the region statring at 0x0*/
151 if (memory.regions[i].guest_phys_addr != 0x0) {
152 continue;
155 g_assert_cmpint(memory.regions[i].memory_size, >, 1024);
157 size = memory.regions[i].memory_size + memory.regions[i].mmap_offset;
159 guest_mem = mmap(0, size, PROT_READ | PROT_WRITE,
160 MAP_SHARED, fds[i], 0);
162 g_assert(guest_mem != MAP_FAILED);
163 guest_mem += (memory.regions[i].mmap_offset / sizeof(*guest_mem));
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];
169 g_assert_cmpint(a, ==, b);
172 munmap(guest_mem, memory.regions[i].memory_size);
175 g_assert_cmpint(1, ==, 1);
176 g_mutex_unlock(&data_mutex);
179 static void *thread_function(void *data)
181 GMainLoop *loop;
182 loop = g_main_loop_new(NULL, FALSE);
183 g_main_loop_run(loop);
184 return NULL;
187 static int chr_can_read(void *opaque)
189 return VHOST_USER_HDR_SIZE;
192 static void chr_read(void *opaque, const uint8_t *buf, int size)
194 CharDriverState *chr = opaque;
195 VhostUserMsg msg;
196 uint8_t *p = (uint8_t *) &msg;
197 int fd;
199 if (size != VHOST_USER_HDR_SIZE) {
200 g_test_message("Wrong message size received %d\n", size);
201 return;
204 g_mutex_lock(&data_mutex);
205 memcpy(p, buf, VHOST_USER_HDR_SIZE);
207 if (msg.size) {
208 p += VHOST_USER_HDR_SIZE;
209 qemu_chr_fe_read_all(chr, p, msg.size);
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;
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;
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));
236 /* signal the test that it can continue */
237 g_cond_signal(&data_cond);
238 break;
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);
245 * This is a non-blocking eventfd.
246 * The receive function forces it to be blocking,
247 * so revert it back to non-blocking.
249 qemu_set_nonblock(fd);
250 break;
251 default:
252 break;
254 g_mutex_unlock(&data_mutex);
257 static const char *init_hugepagefs(void)
259 const char *path;
260 struct statfs fs;
261 int ret;
263 path = getenv("QTEST_HUGETLBFS_PATH");
264 if (!path) {
265 path = "/hugetlbfs";
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;
273 do {
274 ret = statfs(path, &fs);
275 } while (ret != 0 && errno == EINTR);
277 if (ret != 0) {
278 g_test_message("statfs on path (%s): %s\n", path, strerror(errno));
279 return NULL;
282 if (fs.f_type != HUGETLBFS_MAGIC) {
283 g_test_message("Warning: path not on HugeTLBFS: %s\n", path);
284 return NULL;
287 return path;
290 int main(int argc, char **argv)
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;
300 g_test_init(&argc, &argv, NULL);
302 module_call_init(MODULE_INIT_QOM);
304 hugefs = init_hugepagefs();
305 if (!hugefs) {
306 return 0;
309 socket_path = g_strdup_printf("/tmp/vhost-%d.sock", getpid());
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);
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);
323 qemu_cmd = g_strdup_printf(QEMU_CMD, hugefs, socket_path);
324 s = qtest_start(qemu_cmd);
325 g_free(qemu_cmd);
327 qtest_add_func("/vhost-user/read-guest-mem", read_guest_mem);
329 ret = g_test_run();
331 if (s) {
332 qtest_quit(s);
335 /* cleanup */
336 unlink(socket_path);
337 g_free(socket_path);
339 return ret;