]> Git Repo - qemu.git/blob - tests/vhost-user-test.c
Merge remote-tracking branch 'remotes/mst/tags/for_upstream' into staging
[qemu.git] / tests / vhost-user-test.c
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 "qemu/range.h"
16 #include "sysemu/char.h"
17 #include "sysemu/sysemu.h"
18
19 #include <linux/vhost.h>
20 #include <sys/mman.h>
21 #include <sys/vfs.h>
22 #include <qemu/sockets.h>
23
24 /* GLIB version compatibility flags */
25 #if !GLIB_CHECK_VERSION(2, 26, 0)
26 #define G_TIME_SPAN_SECOND              (G_GINT64_CONSTANT(1000000))
27 #endif
28
29 #if GLIB_CHECK_VERSION(2, 28, 0)
30 #define HAVE_MONOTONIC_TIME
31 #endif
32
33 #define QEMU_CMD_ACCEL  " -machine accel=tcg"
34 #define QEMU_CMD_MEM    " -m %d -object memory-backend-file,id=mem,size=%dM,"\
35                         "mem-path=%s,share=on -numa node,memdev=mem"
36 #define QEMU_CMD_CHR    " -chardev socket,id=%s,path=%s"
37 #define QEMU_CMD_NETDEV " -netdev vhost-user,id=net0,chardev=%s,vhostforce"
38 #define QEMU_CMD_NET    " -device virtio-net-pci,netdev=net0 "
39 #define QEMU_CMD_ROM    " -option-rom ../pc-bios/pxe-virtio.rom"
40
41 #define QEMU_CMD        QEMU_CMD_ACCEL QEMU_CMD_MEM QEMU_CMD_CHR \
42                         QEMU_CMD_NETDEV QEMU_CMD_NET QEMU_CMD_ROM
43
44 #define HUGETLBFS_MAGIC       0x958458f6
45
46 /*********** FROM hw/virtio/vhost-user.c *************************************/
47
48 #define VHOST_MEMORY_MAX_NREGIONS    8
49
50 #define VHOST_USER_F_PROTOCOL_FEATURES 30
51 #define VHOST_USER_PROTOCOL_F_LOG_SHMFD 1
52
53 #define VHOST_LOG_PAGE 0x1000
54
55 typedef enum VhostUserRequest {
56     VHOST_USER_NONE = 0,
57     VHOST_USER_GET_FEATURES = 1,
58     VHOST_USER_SET_FEATURES = 2,
59     VHOST_USER_SET_OWNER = 3,
60     VHOST_USER_RESET_DEVICE = 4,
61     VHOST_USER_SET_MEM_TABLE = 5,
62     VHOST_USER_SET_LOG_BASE = 6,
63     VHOST_USER_SET_LOG_FD = 7,
64     VHOST_USER_SET_VRING_NUM = 8,
65     VHOST_USER_SET_VRING_ADDR = 9,
66     VHOST_USER_SET_VRING_BASE = 10,
67     VHOST_USER_GET_VRING_BASE = 11,
68     VHOST_USER_SET_VRING_KICK = 12,
69     VHOST_USER_SET_VRING_CALL = 13,
70     VHOST_USER_SET_VRING_ERR = 14,
71     VHOST_USER_GET_PROTOCOL_FEATURES = 15,
72     VHOST_USER_SET_PROTOCOL_FEATURES = 16,
73     VHOST_USER_MAX
74 } VhostUserRequest;
75
76 typedef struct VhostUserMemoryRegion {
77     uint64_t guest_phys_addr;
78     uint64_t memory_size;
79     uint64_t userspace_addr;
80     uint64_t mmap_offset;
81 } VhostUserMemoryRegion;
82
83 typedef struct VhostUserMemory {
84     uint32_t nregions;
85     uint32_t padding;
86     VhostUserMemoryRegion regions[VHOST_MEMORY_MAX_NREGIONS];
87 } VhostUserMemory;
88
89 typedef struct VhostUserMsg {
90     VhostUserRequest request;
91
92 #define VHOST_USER_VERSION_MASK     (0x3)
93 #define VHOST_USER_REPLY_MASK       (0x1<<2)
94     uint32_t flags;
95     uint32_t size; /* the following payload size */
96     union {
97         uint64_t u64;
98         struct vhost_vring_state state;
99         struct vhost_vring_addr addr;
100         VhostUserMemory memory;
101     };
102 } QEMU_PACKED VhostUserMsg;
103
104 static VhostUserMsg m __attribute__ ((unused));
105 #define VHOST_USER_HDR_SIZE (sizeof(m.request) \
106                             + sizeof(m.flags) \
107                             + sizeof(m.size))
108
109 #define VHOST_USER_PAYLOAD_SIZE (sizeof(m) - VHOST_USER_HDR_SIZE)
110
111 /* The version of the protocol we support */
112 #define VHOST_USER_VERSION    (0x1)
113 /*****************************************************************************/
114
115 typedef struct TestServer {
116     gchar *socket_path;
117     gchar *chr_name;
118     CharDriverState *chr;
119     int fds_num;
120     int fds[VHOST_MEMORY_MAX_NREGIONS];
121     VhostUserMemory memory;
122     GMutex data_mutex;
123     GCond data_cond;
124     int log_fd;
125 } TestServer;
126
127 #if !GLIB_CHECK_VERSION(2, 32, 0)
128 static gboolean g_cond_wait_until(CompatGCond cond, CompatGMutex mutex,
129                                   gint64 end_time)
130 {
131     gboolean ret = FALSE;
132     end_time -= g_get_monotonic_time();
133     GTimeVal time = { end_time / G_TIME_SPAN_SECOND,
134                       end_time % G_TIME_SPAN_SECOND };
135     ret = g_cond_timed_wait(cond, mutex, &time);
136     return ret;
137 }
138 #endif
139
140 static const char *tmpfs;
141 static const char *root;
142
143 static void wait_for_fds(TestServer *s)
144 {
145     gint64 end_time;
146
147     g_mutex_lock(&s->data_mutex);
148
149     end_time = g_get_monotonic_time() + 5 * G_TIME_SPAN_SECOND;
150     while (!s->fds_num) {
151         if (!g_cond_wait_until(&s->data_cond, &s->data_mutex, end_time)) {
152             /* timeout has passed */
153             g_assert(s->fds_num);
154             break;
155         }
156     }
157
158     /* check for sanity */
159     g_assert_cmpint(s->fds_num, >, 0);
160     g_assert_cmpint(s->fds_num, ==, s->memory.nregions);
161
162     g_mutex_unlock(&s->data_mutex);
163 }
164
165 static void read_guest_mem(TestServer *s)
166 {
167     uint32_t *guest_mem;
168     int i, j;
169     size_t size;
170
171     wait_for_fds(s);
172
173     g_mutex_lock(&s->data_mutex);
174
175     /* iterate all regions */
176     for (i = 0; i < s->fds_num; i++) {
177
178         /* We'll check only the region statring at 0x0*/
179         if (s->memory.regions[i].guest_phys_addr != 0x0) {
180             continue;
181         }
182
183         g_assert_cmpint(s->memory.regions[i].memory_size, >, 1024);
184
185         size = s->memory.regions[i].memory_size +
186             s->memory.regions[i].mmap_offset;
187
188         guest_mem = mmap(0, size, PROT_READ | PROT_WRITE,
189                          MAP_SHARED, s->fds[i], 0);
190
191         g_assert(guest_mem != MAP_FAILED);
192         guest_mem += (s->memory.regions[i].mmap_offset / sizeof(*guest_mem));
193
194         for (j = 0; j < 256; j++) {
195             uint32_t a = readl(s->memory.regions[i].guest_phys_addr + j*4);
196             uint32_t b = guest_mem[j];
197
198             g_assert_cmpint(a, ==, b);
199         }
200
201         munmap(guest_mem, s->memory.regions[i].memory_size);
202     }
203
204     g_mutex_unlock(&s->data_mutex);
205 }
206
207 static void *thread_function(void *data)
208 {
209     GMainLoop *loop;
210     loop = g_main_loop_new(NULL, FALSE);
211     g_main_loop_run(loop);
212     return NULL;
213 }
214
215 static int chr_can_read(void *opaque)
216 {
217     return VHOST_USER_HDR_SIZE;
218 }
219
220 static void chr_read(void *opaque, const uint8_t *buf, int size)
221 {
222     TestServer *s = opaque;
223     CharDriverState *chr = s->chr;
224     VhostUserMsg msg;
225     uint8_t *p = (uint8_t *) &msg;
226     int fd;
227
228     if (size != VHOST_USER_HDR_SIZE) {
229         g_test_message("Wrong message size received %d\n", size);
230         return;
231     }
232
233     g_mutex_lock(&s->data_mutex);
234     memcpy(p, buf, VHOST_USER_HDR_SIZE);
235
236     if (msg.size) {
237         p += VHOST_USER_HDR_SIZE;
238         g_assert_cmpint(qemu_chr_fe_read_all(chr, p, msg.size), ==, msg.size);
239     }
240
241     switch (msg.request) {
242     case VHOST_USER_GET_FEATURES:
243         /* send back features to qemu */
244         msg.flags |= VHOST_USER_REPLY_MASK;
245         msg.size = sizeof(m.u64);
246         msg.u64 = 0x1ULL << VHOST_F_LOG_ALL |
247             0x1ULL << VHOST_USER_F_PROTOCOL_FEATURES;
248         p = (uint8_t *) &msg;
249         qemu_chr_fe_write_all(chr, p, VHOST_USER_HDR_SIZE + msg.size);
250         break;
251
252     case VHOST_USER_SET_FEATURES:
253         g_assert_cmpint(msg.u64 & (0x1ULL << VHOST_USER_F_PROTOCOL_FEATURES),
254                         !=, 0ULL);
255         break;
256
257     case VHOST_USER_GET_PROTOCOL_FEATURES:
258         /* send back features to qemu */
259         msg.flags |= VHOST_USER_REPLY_MASK;
260         msg.size = sizeof(m.u64);
261         msg.u64 = 1 << VHOST_USER_PROTOCOL_F_LOG_SHMFD;
262         p = (uint8_t *) &msg;
263         qemu_chr_fe_write_all(chr, p, VHOST_USER_HDR_SIZE + msg.size);
264         break;
265
266     case VHOST_USER_GET_VRING_BASE:
267         /* send back vring base to qemu */
268         msg.flags |= VHOST_USER_REPLY_MASK;
269         msg.size = sizeof(m.state);
270         msg.state.num = 0;
271         p = (uint8_t *) &msg;
272         qemu_chr_fe_write_all(chr, p, VHOST_USER_HDR_SIZE + msg.size);
273         break;
274
275     case VHOST_USER_SET_MEM_TABLE:
276         /* received the mem table */
277         memcpy(&s->memory, &msg.memory, sizeof(msg.memory));
278         s->fds_num = qemu_chr_fe_get_msgfds(chr, s->fds, G_N_ELEMENTS(s->fds));
279
280         /* signal the test that it can continue */
281         g_cond_signal(&s->data_cond);
282         break;
283
284     case VHOST_USER_SET_VRING_KICK:
285     case VHOST_USER_SET_VRING_CALL:
286         /* consume the fd */
287         qemu_chr_fe_get_msgfds(chr, &fd, 1);
288         /*
289          * This is a non-blocking eventfd.
290          * The receive function forces it to be blocking,
291          * so revert it back to non-blocking.
292          */
293         qemu_set_nonblock(fd);
294         break;
295
296     case VHOST_USER_SET_LOG_BASE:
297         if (s->log_fd != -1) {
298             close(s->log_fd);
299             s->log_fd = -1;
300         }
301         qemu_chr_fe_get_msgfds(chr, &s->log_fd, 1);
302         msg.flags |= VHOST_USER_REPLY_MASK;
303         msg.size = 0;
304         p = (uint8_t *) &msg;
305         qemu_chr_fe_write_all(chr, p, VHOST_USER_HDR_SIZE);
306
307         g_cond_signal(&s->data_cond);
308         break;
309
310     case VHOST_USER_RESET_DEVICE:
311         s->fds_num = 0;
312         break;
313
314     default:
315         break;
316     }
317
318     g_mutex_unlock(&s->data_mutex);
319 }
320
321 static const char *init_hugepagefs(const char *path)
322 {
323     struct statfs fs;
324     int ret;
325
326     if (access(path, R_OK | W_OK | X_OK)) {
327         g_test_message("access on path (%s): %s\n", path, strerror(errno));
328         return NULL;
329     }
330
331     do {
332         ret = statfs(path, &fs);
333     } while (ret != 0 && errno == EINTR);
334
335     if (ret != 0) {
336         g_test_message("statfs on path (%s): %s\n", path, strerror(errno));
337         return NULL;
338     }
339
340     if (fs.f_type != HUGETLBFS_MAGIC) {
341         g_test_message("Warning: path not on HugeTLBFS: %s\n", path);
342         return NULL;
343     }
344
345     return path;
346 }
347
348 static TestServer *test_server_new(const gchar *name)
349 {
350     TestServer *server = g_new0(TestServer, 1);
351     gchar *chr_path;
352
353     server->socket_path = g_strdup_printf("%s/%s.sock", tmpfs, name);
354
355     chr_path = g_strdup_printf("unix:%s,server,nowait", server->socket_path);
356     server->chr_name = g_strdup_printf("chr-%s", name);
357     server->chr = qemu_chr_new(server->chr_name, chr_path, NULL);
358     g_free(chr_path);
359
360     qemu_chr_add_handlers(server->chr, chr_can_read, chr_read, NULL, server);
361
362     g_mutex_init(&server->data_mutex);
363     g_cond_init(&server->data_cond);
364
365     server->log_fd = -1;
366
367     return server;
368 }
369
370 #define GET_QEMU_CMD(s)                                                        \
371     g_strdup_printf(QEMU_CMD, 512, 512, (root), (s)->chr_name,                 \
372                     (s)->socket_path, (s)->chr_name)
373
374 #define GET_QEMU_CMDE(s, mem, extra, ...)                                      \
375     g_strdup_printf(QEMU_CMD extra, (mem), (mem), (root), (s)->chr_name,       \
376                     (s)->socket_path, (s)->chr_name, ##__VA_ARGS__)
377
378 static void test_server_free(TestServer *server)
379 {
380     int i;
381
382     qemu_chr_delete(server->chr);
383
384     for (i = 0; i < server->fds_num; i++) {
385         close(server->fds[i]);
386     }
387
388     if (server->log_fd != -1) {
389         close(server->log_fd);
390     }
391
392     unlink(server->socket_path);
393     g_free(server->socket_path);
394
395
396     g_free(server->chr_name);
397     g_free(server);
398 }
399
400 static void wait_for_log_fd(TestServer *s)
401 {
402     gint64 end_time;
403
404     g_mutex_lock(&s->data_mutex);
405     end_time = g_get_monotonic_time() + 5 * G_TIME_SPAN_SECOND;
406     while (s->log_fd == -1) {
407         if (!g_cond_wait_until(&s->data_cond, &s->data_mutex, end_time)) {
408             /* timeout has passed */
409             g_assert(s->log_fd != -1);
410             break;
411         }
412     }
413
414     g_mutex_unlock(&s->data_mutex);
415 }
416
417 static void write_guest_mem(TestServer *s, uint32 seed)
418 {
419     uint32_t *guest_mem;
420     int i, j;
421     size_t size;
422
423     wait_for_fds(s);
424
425     /* iterate all regions */
426     for (i = 0; i < s->fds_num; i++) {
427
428         /* We'll write only the region statring at 0x0 */
429         if (s->memory.regions[i].guest_phys_addr != 0x0) {
430             continue;
431         }
432
433         g_assert_cmpint(s->memory.regions[i].memory_size, >, 1024);
434
435         size = s->memory.regions[i].memory_size +
436             s->memory.regions[i].mmap_offset;
437
438         guest_mem = mmap(0, size, PROT_READ | PROT_WRITE,
439                          MAP_SHARED, s->fds[i], 0);
440
441         g_assert(guest_mem != MAP_FAILED);
442         guest_mem += (s->memory.regions[i].mmap_offset / sizeof(*guest_mem));
443
444         for (j = 0; j < 256; j++) {
445             guest_mem[j] = seed + j;
446         }
447
448         munmap(guest_mem, s->memory.regions[i].memory_size);
449         break;
450     }
451 }
452
453 static guint64 get_log_size(TestServer *s)
454 {
455     guint64 log_size = 0;
456     int i;
457
458     for (i = 0; i < s->memory.nregions; ++i) {
459         VhostUserMemoryRegion *reg = &s->memory.regions[i];
460         guint64 last = range_get_last(reg->guest_phys_addr,
461                                        reg->memory_size);
462         log_size = MAX(log_size, last / (8 * VHOST_LOG_PAGE) + 1);
463     }
464
465     return log_size;
466 }
467
468 typedef struct TestMigrateSource {
469     GSource source;
470     TestServer *src;
471     TestServer *dest;
472 } TestMigrateSource;
473
474 static gboolean
475 test_migrate_source_check(GSource *source)
476 {
477     TestMigrateSource *t = (TestMigrateSource *)source;
478     gboolean overlap = t->src->fds_num > 0 && t->dest->fds_num > 0;
479
480     g_assert(!overlap);
481
482     return FALSE;
483 }
484
485 GSourceFuncs test_migrate_source_funcs = {
486     NULL,
487     test_migrate_source_check,
488     NULL,
489     NULL
490 };
491
492 static void test_migrate(void)
493 {
494     TestServer *s = test_server_new("src");
495     TestServer *dest = test_server_new("dest");
496     const char *uri = "tcp:127.0.0.1:1234";
497     QTestState *global = global_qtest, *from, *to;
498     GSource *source;
499     gchar *cmd;
500     QDict *rsp;
501     guint8 *log;
502     guint64 size;
503
504     cmd = GET_QEMU_CMDE(s, 2, "");
505     from = qtest_start(cmd);
506     g_free(cmd);
507
508     wait_for_fds(s);
509     size = get_log_size(s);
510     g_assert_cmpint(size, ==, (2 * 1024 * 1024) / (VHOST_LOG_PAGE * 8));
511
512     cmd = GET_QEMU_CMDE(dest, 2, " -incoming %s", uri);
513     to = qtest_init(cmd);
514     g_free(cmd);
515
516     source = g_source_new(&test_migrate_source_funcs,
517                           sizeof(TestMigrateSource));
518     ((TestMigrateSource *)source)->src = s;
519     ((TestMigrateSource *)source)->dest = dest;
520     g_source_attach(source, NULL);
521
522     /* slow down migration to have time to fiddle with log */
523     /* TODO: qtest could learn to break on some places */
524     rsp = qmp("{ 'execute': 'migrate_set_speed',"
525               "'arguments': { 'value': 10 } }");
526     g_assert(qdict_haskey(rsp, "return"));
527     QDECREF(rsp);
528
529     cmd = g_strdup_printf("{ 'execute': 'migrate',"
530                           "'arguments': { 'uri': '%s' } }",
531                           uri);
532     rsp = qmp(cmd);
533     g_free(cmd);
534     g_assert(qdict_haskey(rsp, "return"));
535     QDECREF(rsp);
536
537     wait_for_log_fd(s);
538
539     log = mmap(0, size, PROT_READ | PROT_WRITE, MAP_SHARED, s->log_fd, 0);
540     g_assert(log != MAP_FAILED);
541
542     /* modify first page */
543     write_guest_mem(s, 0x42);
544     log[0] = 1;
545     munmap(log, size);
546
547     /* speed things up */
548     rsp = qmp("{ 'execute': 'migrate_set_speed',"
549               "'arguments': { 'value': 0 } }");
550     g_assert(qdict_haskey(rsp, "return"));
551     QDECREF(rsp);
552
553     qmp_eventwait("STOP");
554
555     global_qtest = to;
556     qmp_eventwait("RESUME");
557
558     read_guest_mem(dest);
559
560     g_source_destroy(source);
561     g_source_unref(source);
562
563     qtest_quit(to);
564     test_server_free(dest);
565     qtest_quit(from);
566     test_server_free(s);
567
568     global_qtest = global;
569 }
570
571 int main(int argc, char **argv)
572 {
573     QTestState *s = NULL;
574     TestServer *server = NULL;
575     const char *hugefs;
576     char *qemu_cmd = NULL;
577     int ret;
578     char template[] = "/tmp/vhost-test-XXXXXX";
579
580     g_test_init(&argc, &argv, NULL);
581
582     module_call_init(MODULE_INIT_QOM);
583     qemu_add_opts(&qemu_chardev_opts);
584
585     tmpfs = mkdtemp(template);
586     if (!tmpfs) {
587         g_test_message("mkdtemp on path (%s): %s\n", template, strerror(errno));
588     }
589     g_assert(tmpfs);
590
591     hugefs = getenv("QTEST_HUGETLBFS_PATH");
592     if (hugefs) {
593         root = init_hugepagefs(hugefs);
594         g_assert(root);
595     } else {
596         root = tmpfs;
597     }
598
599     server = test_server_new("test");
600
601     /* run the main loop thread so the chardev may operate */
602     g_thread_new(NULL, thread_function, NULL);
603
604     qemu_cmd = GET_QEMU_CMD(server);
605
606     s = qtest_start(qemu_cmd);
607     g_free(qemu_cmd);
608
609     qtest_add_data_func("/vhost-user/read-guest-mem", server, read_guest_mem);
610     qtest_add_func("/vhost-user/migrate", test_migrate);
611
612     ret = g_test_run();
613
614     if (s) {
615         qtest_quit(s);
616     }
617
618     /* cleanup */
619     test_server_free(server);
620
621     ret = rmdir(tmpfs);
622     if (ret != 0) {
623         g_test_message("unable to rmdir: path (%s): %s\n",
624                        tmpfs, strerror(errno));
625     }
626     g_assert_cmpint(ret, ==, 0);
627
628     return ret;
629 }
This page took 0.058711 seconds and 4 git commands to generate.