#include "libqtest.h"
#include "qapi/error.h"
+#include "qapi/qmp/qdict.h"
#include "qemu/config-file.h"
#include "qemu/option.h"
#include "qemu/range.h"
#include "qemu/sockets.h"
#include "chardev/char-fe.h"
+#include "qemu/memfd.h"
#include "sysemu/sysemu.h"
#include "libqos/libqos.h"
#include "libqos/pci-pc.h"
#include "libqos/virtio-pci.h"
-#include "qapi/error.h"
#include "libqos/malloc-pc.h"
#include "hw/virtio/virtio-net.h"
#include <linux/virtio_net.h>
#include <sys/vfs.h>
-#define VHOST_USER_NET_TESTS_WORKING 0 /* broken as of 2.10.0 */
-
-/* GLIB version compatibility flags */
-#if !GLIB_CHECK_VERSION(2, 26, 0)
-#define G_TIME_SPAN_SECOND (G_GINT64_CONSTANT(1000000))
-#endif
-#if GLIB_CHECK_VERSION(2, 28, 0)
-#define HAVE_MONOTONIC_TIME
-#endif
-
-#define QEMU_CMD_MEM " -m %d -object memory-backend-file,id=mem,size=%dM,"\
+#define QEMU_CMD_MEM " -m %d -object memory-backend-file,id=mem,size=%dM," \
"mem-path=%s,share=on -numa node,memdev=mem"
+#define QEMU_CMD_MEMFD " -m %d -object memory-backend-memfd,id=mem,size=%dM," \
+ " -numa node,memdev=mem"
#define QEMU_CMD_CHR " -chardev socket,id=%s,path=%s%s"
#define QEMU_CMD_NETDEV " -netdev vhost-user,id=net0,chardev=%s,vhostforce"
#define QEMU_CMD_NET " -device virtio-net-pci,netdev=net0"
-#define QEMU_CMD QEMU_CMD_MEM QEMU_CMD_CHR \
- QEMU_CMD_NETDEV QEMU_CMD_NET
-
#define HUGETLBFS_MAGIC 0x958458f6
/*********** FROM hw/virtio/vhost-user.c *************************************/
#define VHOST_MEMORY_MAX_NREGIONS 8
+#define VHOST_MAX_VIRTQUEUES 0x100
#define VHOST_USER_F_PROTOCOL_FEATURES 30
#define VHOST_USER_PROTOCOL_F_MQ 0
typedef struct TestServer {
QPCIBus *bus;
+ QVirtioPCIDevice *dev;
+ QVirtQueue *vq[VHOST_MAX_VIRTQUEUES];
gchar *socket_path;
gchar *mig_path;
gchar *chr_name;
int fds_num;
int fds[VHOST_MEMORY_MAX_NREGIONS];
VhostUserMemory memory;
- CompatGMutex data_mutex;
- CompatGCond data_cond;
+ GMutex data_mutex;
+ GCond data_cond;
int log_fd;
uint64_t rings;
bool test_fail;
int test_flags;
int queues;
+ QGuestAllocator *alloc;
} TestServer;
+static TestServer *test_server_new(const gchar *name);
+static void test_server_free(TestServer *server);
+static void test_server_listen(TestServer *server);
+
static const char *tmpfs;
static const char *root;
-static void init_virtio_dev(TestServer *s)
+enum test_memfd {
+ TEST_MEMFD_AUTO,
+ TEST_MEMFD_YES,
+ TEST_MEMFD_NO,
+};
+
+static char *get_qemu_cmd(TestServer *s,
+ int mem, enum test_memfd memfd, const char *mem_path,
+ const char *chr_opts, const char *extra)
+{
+ if (memfd == TEST_MEMFD_AUTO && qemu_memfd_check(0)) {
+ memfd = TEST_MEMFD_YES;
+ }
+
+ if (memfd == TEST_MEMFD_YES) {
+ return g_strdup_printf(QEMU_CMD_MEMFD QEMU_CMD_CHR
+ QEMU_CMD_NETDEV QEMU_CMD_NET "%s", mem, mem,
+ s->chr_name, s->socket_path,
+ chr_opts, s->chr_name, extra);
+ } else {
+ return g_strdup_printf(QEMU_CMD_MEM QEMU_CMD_CHR
+ QEMU_CMD_NETDEV QEMU_CMD_NET "%s", mem, mem,
+ mem_path, s->chr_name, s->socket_path,
+ chr_opts, s->chr_name, extra);
+ }
+}
+
+static void init_virtio_dev(TestServer *s, uint32_t features_mask)
{
- QVirtioPCIDevice *dev;
uint32_t features;
+ int i;
- s->bus = qpci_init_pc(NULL);
+ s->bus = qpci_init_pc(global_qtest, NULL);
g_assert_nonnull(s->bus);
- dev = qvirtio_pci_device_find(s->bus, VIRTIO_ID_NET);
- g_assert_nonnull(dev);
+ s->dev = qvirtio_pci_device_find(s->bus, VIRTIO_ID_NET);
+ g_assert_nonnull(s->dev);
+
+ qvirtio_pci_device_enable(s->dev);
+ qvirtio_reset(&s->dev->vdev);
+ qvirtio_set_acknowledge(&s->dev->vdev);
+ qvirtio_set_driver(&s->dev->vdev);
- qvirtio_pci_device_enable(dev);
- qvirtio_reset(&dev->vdev);
- qvirtio_set_acknowledge(&dev->vdev);
- qvirtio_set_driver(&dev->vdev);
+ s->alloc = pc_alloc_init(global_qtest);
- features = qvirtio_get_features(&dev->vdev);
- features = features & VIRTIO_NET_F_MAC;
- qvirtio_set_features(&dev->vdev, features);
+ for (i = 0; i < s->queues * 2; i++) {
+ s->vq[i] = qvirtqueue_setup(&s->dev->vdev, s->alloc, i);
+ }
+
+ features = qvirtio_get_features(&s->dev->vdev);
+ features = features & features_mask;
+ qvirtio_set_features(&s->dev->vdev, features);
- qvirtio_set_driver_ok(&dev->vdev);
- qvirtio_pci_device_free(dev);
+ qvirtio_set_driver_ok(&s->dev->vdev);
+}
+
+static void uninit_virtio_dev(TestServer *s)
+{
+ int i;
+
+ for (i = 0; i < s->queues * 2; i++) {
+ qvirtqueue_cleanup(s->dev->vdev.bus, s->vq[i], s->alloc);
+ }
+ pc_alloc_uninit(s->alloc);
+
+ qvirtio_pci_device_free(s->dev);
}
static void wait_for_fds(TestServer *s)
g_mutex_unlock(&s->data_mutex);
}
-static void read_guest_mem(const void *data)
+static void read_guest_mem_server(TestServer *s)
{
- TestServer *s = (void *)data;
uint32_t *guest_mem;
int i, j;
size_t size;
break;
case VHOST_USER_SET_FEATURES:
- g_assert_cmpint(msg.payload.u64 & (0x1ULL << VHOST_USER_F_PROTOCOL_FEATURES),
- !=, 0ULL);
+ g_assert_cmpint(msg.payload.u64 & (0x1ULL << VHOST_USER_F_PROTOCOL_FEATURES),
+ !=, 0ULL);
if (s->test_flags == TEST_FLAGS_DISCONNECT) {
qemu_chr_fe_disconnect(chr);
s->test_flags = TEST_FLAGS_BAD;
chr = qemu_chr_new(server->chr_name, chr_path);
g_free(chr_path);
+ g_assert_nonnull(chr);
qemu_chr_fe_init(&server->chr, chr, &error_abort);
qemu_chr_fe_set_handlers(&server->chr, chr_can_read, chr_read,
chr_event, NULL, server, NULL, true);
test_server_create_chr(server, ",server,nowait");
}
-#define GET_QEMU_CMD(s) \
- g_strdup_printf(QEMU_CMD, 512, 512, (root), (s)->chr_name, \
- (s)->socket_path, "", (s)->chr_name)
-
-#define GET_QEMU_CMDE(s, mem, chr_opts, extra, ...) \
- g_strdup_printf(QEMU_CMD extra, (mem), (mem), (root), (s)->chr_name, \
- (s)->socket_path, (chr_opts), (s)->chr_name, ##__VA_ARGS__)
-
static gboolean _test_server_free(TestServer *server)
{
int i;
g_free(server->mig_path);
g_free(server->chr_name);
+ g_assert(server->bus);
qpci_free_pc(server->bus);
g_free(server);
return FALSE;
}
-#if !GLIB_CHECK_VERSION(2,36,0)
-/* this callback is unnecessary with glib >2.36, the default
- * prepare for the source does the same */
-static gboolean
-test_migrate_source_prepare(GSource *source, gint *timeout)
-{
- *timeout = -1;
- return FALSE;
-}
-#endif
-
GSourceFuncs test_migrate_source_funcs = {
-#if !GLIB_CHECK_VERSION(2,36,0)
- .prepare = test_migrate_source_prepare,
-#endif
.check = test_migrate_source_check,
};
+static void test_read_guest_mem(const void *arg)
+{
+ enum test_memfd memfd = GPOINTER_TO_INT(arg);
+ TestServer *server = NULL;
+ char *qemu_cmd = NULL;
+ QTestState *s = NULL;
+
+ server = test_server_new(memfd == TEST_MEMFD_YES ?
+ "read-guest-memfd" : "read-guest-mem");
+ test_server_listen(server);
+
+ qemu_cmd = get_qemu_cmd(server, 512, memfd, root, "", "");
+
+ s = qtest_start(qemu_cmd);
+ g_free(qemu_cmd);
+
+ init_virtio_dev(server, 1u << VIRTIO_NET_F_MAC);
+
+ read_guest_mem_server(server);
+
+ uninit_virtio_dev(server);
+
+ qtest_quit(s);
+ test_server_free(server);
+}
+
static void test_migrate(void)
{
TestServer *s = test_server_new("src");
char *uri = g_strdup_printf("%s%s", "unix:", dest->mig_path);
QTestState *global = global_qtest, *from, *to;
GSource *source;
- gchar *cmd;
+ gchar *cmd, *tmp;
QDict *rsp;
guint8 *log;
guint64 size;
test_server_listen(s);
test_server_listen(dest);
- cmd = GET_QEMU_CMDE(s, 2, "", "");
+ cmd = get_qemu_cmd(s, 2, TEST_MEMFD_AUTO, root, "", "");
from = qtest_start(cmd);
g_free(cmd);
- init_virtio_dev(s);
+ init_virtio_dev(s, 1u << VIRTIO_NET_F_MAC);
+ init_virtio_dev(dest, 1u << VIRTIO_NET_F_MAC);
wait_for_fds(s);
size = get_log_size(s);
g_assert_cmpint(size, ==, (2 * 1024 * 1024) / (VHOST_LOG_PAGE * 8));
- cmd = GET_QEMU_CMDE(dest, 2, "", " -incoming %s", uri);
+ tmp = g_strdup_printf(" -incoming %s", uri);
+ cmd = get_qemu_cmd(dest, 2, TEST_MEMFD_AUTO, root, "", tmp);
+ g_free(tmp);
to = qtest_init(cmd);
g_free(cmd);
rsp = qmp("{ 'execute': 'migrate_set_speed',"
"'arguments': { 'value': 10 } }");
g_assert(qdict_haskey(rsp, "return"));
- QDECREF(rsp);
+ qobject_unref(rsp);
- cmd = g_strdup_printf("{ 'execute': 'migrate',"
- "'arguments': { 'uri': '%s' } }",
- uri);
- rsp = qmp(cmd);
- g_free(cmd);
+ rsp = qmp("{ 'execute': 'migrate', 'arguments': { 'uri': %s } }", uri);
g_assert(qdict_haskey(rsp, "return"));
- QDECREF(rsp);
+ qobject_unref(rsp);
wait_for_log_fd(s);
rsp = qmp("{ 'execute': 'migrate_set_speed',"
"'arguments': { 'value': 0 } }");
g_assert(qdict_haskey(rsp, "return"));
- QDECREF(rsp);
+ qobject_unref(rsp);
qmp_eventwait("STOP");
global_qtest = to;
qmp_eventwait("RESUME");
- read_guest_mem(dest);
+ read_guest_mem_server(dest);
+
+ uninit_virtio_dev(s);
+ uninit_virtio_dev(dest);
g_source_destroy(source);
g_source_unref(source);
g_mutex_unlock(&s->data_mutex);
}
-#if VHOST_USER_NET_TESTS_WORKING && defined(CONFIG_HAS_GLIB_SUBPROCESS_TESTS)
static inline void test_server_connect(TestServer *server)
{
test_server_create_chr(server, ",reconnect=1");
char *cmd;
g_thread_new("connect", connect_thread, s);
- cmd = GET_QEMU_CMDE(s, 2, ",server", "");
+ cmd = get_qemu_cmd(s, 2, TEST_MEMFD_AUTO, root, ",server", "");
qtest_start(cmd);
g_free(cmd);
- init_virtio_dev(s);
+ init_virtio_dev(s, 1u << VIRTIO_NET_F_MAC);
wait_for_fds(s);
wait_for_rings_started(s, 2);
wait_for_fds(s);
wait_for_rings_started(s, 2);
+ uninit_virtio_dev(s);
+
qtest_end();
test_server_free(s);
return;
s->test_fail = true;
g_thread_new("connect", connect_thread, s);
- cmd = GET_QEMU_CMDE(s, 2, ",server", "");
+ cmd = get_qemu_cmd(s, 2, TEST_MEMFD_AUTO, root, ",server", "");
qtest_start(cmd);
g_free(cmd);
- init_virtio_dev(s);
+ init_virtio_dev(s, 1u << VIRTIO_NET_F_MAC);
wait_for_fds(s);
wait_for_rings_started(s, 2);
+ uninit_virtio_dev(s);
+
qtest_end();
test_server_free(s);
}
s->test_flags = TEST_FLAGS_DISCONNECT;
g_thread_new("connect", connect_thread, s);
- cmd = GET_QEMU_CMDE(s, 2, ",server", "");
+ cmd = get_qemu_cmd(s, 2, TEST_MEMFD_AUTO, root, ",server", "");
qtest_start(cmd);
g_free(cmd);
- init_virtio_dev(s);
+ init_virtio_dev(s, 1u << VIRTIO_NET_F_MAC);
wait_for_fds(s);
wait_for_rings_started(s, 2);
+ uninit_virtio_dev(s);
+
qtest_end();
test_server_free(s);
}
g_free(path);
}
-#endif
-
-static QVirtioPCIDevice *virtio_net_pci_init(QPCIBus *bus, int slot)
-{
- QVirtioPCIDevice *dev;
-
- dev = qvirtio_pci_device_find(bus, VIRTIO_ID_NET);
- g_assert(dev != NULL);
- g_assert_cmphex(dev->vdev.device_type, ==, VIRTIO_ID_NET);
-
- qvirtio_pci_device_enable(dev);
- qvirtio_reset(&dev->vdev);
- qvirtio_set_acknowledge(&dev->vdev);
- qvirtio_set_driver(&dev->vdev);
-
- return dev;
-}
-
-static void driver_init(QVirtioDevice *dev)
-{
- uint32_t features;
-
- features = qvirtio_get_features(dev);
- features = features & ~(QVIRTIO_F_BAD_FEATURE |
- (1u << VIRTIO_RING_F_INDIRECT_DESC) |
- (1u << VIRTIO_RING_F_EVENT_IDX));
- qvirtio_set_features(dev, features);
-
- qvirtio_set_driver_ok(dev);
-}
-
-#define PCI_SLOT 0x04
static void test_multiqueue(void)
{
- const int queues = 2;
TestServer *s = test_server_new("mq");
- QVirtioPCIDevice *dev;
- QPCIBus *bus;
- QVirtQueuePCI *vq[queues * 2];
- QGuestAllocator *alloc;
char *cmd;
- int i;
-
- s->queues = queues;
+ uint32_t features_mask = ~(QVIRTIO_F_BAD_FEATURE |
+ (1u << VIRTIO_RING_F_INDIRECT_DESC) |
+ (1u << VIRTIO_RING_F_EVENT_IDX));
+ s->queues = 2;
test_server_listen(s);
- cmd = g_strdup_printf(QEMU_CMD_MEM QEMU_CMD_CHR QEMU_CMD_NETDEV ",queues=%d "
- "-device virtio-net-pci,netdev=net0,mq=on,vectors=%d",
- 512, 512, root, s->chr_name,
- s->socket_path, "", s->chr_name,
- queues, queues * 2 + 2);
+ if (qemu_memfd_check(0)) {
+ cmd = g_strdup_printf(
+ QEMU_CMD_MEMFD QEMU_CMD_CHR QEMU_CMD_NETDEV ",queues=%d "
+ "-device virtio-net-pci,netdev=net0,mq=on,vectors=%d",
+ 512, 512, s->chr_name,
+ s->socket_path, "", s->chr_name,
+ s->queues, s->queues * 2 + 2);
+ } else {
+ cmd = g_strdup_printf(
+ QEMU_CMD_MEM QEMU_CMD_CHR QEMU_CMD_NETDEV ",queues=%d "
+ "-device virtio-net-pci,netdev=net0,mq=on,vectors=%d",
+ 512, 512, root, s->chr_name,
+ s->socket_path, "", s->chr_name,
+ s->queues, s->queues * 2 + 2);
+ }
qtest_start(cmd);
g_free(cmd);
- bus = qpci_init_pc(NULL);
- dev = virtio_net_pci_init(bus, PCI_SLOT);
+ init_virtio_dev(s, features_mask);
- alloc = pc_alloc_init();
- for (i = 0; i < queues * 2; i++) {
- vq[i] = (QVirtQueuePCI *)qvirtqueue_setup(&dev->vdev, alloc, i);
- }
+ wait_for_rings_started(s, s->queues * 2);
- driver_init(&dev->vdev);
- wait_for_rings_started(s, queues * 2);
+ uninit_virtio_dev(s);
- /* End test */
- for (i = 0; i < queues * 2; i++) {
- qvirtqueue_cleanup(dev->vdev.bus, &vq[i]->vq, alloc);
- }
- pc_alloc_uninit(alloc);
- qvirtio_pci_device_disable(dev);
- g_free(dev->pdev);
- g_free(dev);
- qpci_free_pc(bus);
qtest_end();
test_server_free(s);
int main(int argc, char **argv)
{
- QTestState *s = NULL;
- TestServer *server = NULL;
const char *hugefs;
- char *qemu_cmd = NULL;
int ret;
char template[] = "/tmp/vhost-test-XXXXXX";
GMainLoop *loop;
root = tmpfs;
}
- server = test_server_new("test");
- test_server_listen(server);
-
loop = g_main_loop_new(NULL, FALSE);
/* run the main loop thread so the chardev may operate */
thread = g_thread_new(NULL, thread_function, loop);
- qemu_cmd = GET_QEMU_CMD(server);
-
- s = qtest_start(qemu_cmd);
- g_free(qemu_cmd);
- init_virtio_dev(server);
-
- qtest_add_data_func("/vhost-user/read-guest-mem", server, read_guest_mem);
+ if (qemu_memfd_check(0)) {
+ qtest_add_data_func("/vhost-user/read-guest-mem/memfd",
+ GINT_TO_POINTER(TEST_MEMFD_YES),
+ test_read_guest_mem);
+ }
+ qtest_add_data_func("/vhost-user/read-guest-mem/memfile",
+ GINT_TO_POINTER(TEST_MEMFD_NO), test_read_guest_mem);
qtest_add_func("/vhost-user/migrate", test_migrate);
qtest_add_func("/vhost-user/multiqueue", test_multiqueue);
-#if VHOST_USER_NET_TESTS_WORKING && defined(CONFIG_HAS_GLIB_SUBPROCESS_TESTS)
- qtest_add_func("/vhost-user/reconnect/subprocess",
- test_reconnect_subprocess);
- qtest_add_func("/vhost-user/reconnect", test_reconnect);
- qtest_add_func("/vhost-user/connect-fail/subprocess",
- test_connect_fail_subprocess);
- qtest_add_func("/vhost-user/connect-fail", test_connect_fail);
- qtest_add_func("/vhost-user/flags-mismatch/subprocess",
- test_flags_mismatch_subprocess);
- qtest_add_func("/vhost-user/flags-mismatch", test_flags_mismatch);
-#endif
+ /* keeps failing on build-system since Aug 15 2017 */
+ if (getenv("QTEST_VHOST_USER_FIXME")) {
+ qtest_add_func("/vhost-user/reconnect/subprocess",
+ test_reconnect_subprocess);
+ qtest_add_func("/vhost-user/reconnect", test_reconnect);
+ qtest_add_func("/vhost-user/connect-fail/subprocess",
+ test_connect_fail_subprocess);
+ qtest_add_func("/vhost-user/connect-fail", test_connect_fail);
+ qtest_add_func("/vhost-user/flags-mismatch/subprocess",
+ test_flags_mismatch_subprocess);
+ qtest_add_func("/vhost-user/flags-mismatch", test_flags_mismatch);
+ }
ret = g_test_run();
- if (s) {
- qtest_quit(s);
- }
-
/* cleanup */
- test_server_free(server);
/* finish the helper thread and dispatch pending sources */
g_main_loop_quit(loop);