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