From mboxrd@z Thu Jan 1 00:00:00 1970 Received: from eggs.gnu.org ([2001:4830:134:3::10]:55902) by lists.gnu.org with esmtp (Exim 4.71) (envelope-from ) id 1WKuFe-0000Yi-42 for qemu-devel@nongnu.org; Tue, 04 Mar 2014 13:39:44 -0500 Received: from Debian-exim by eggs.gnu.org with spam-scanned (Exim 4.71) (envelope-from ) id 1WKuFX-0001Xa-VB for qemu-devel@nongnu.org; Tue, 04 Mar 2014 13:39:38 -0500 Received: from cantor2.suse.de ([195.135.220.15]:35929 helo=mx2.suse.de) by eggs.gnu.org with esmtp (Exim 4.71) (envelope-from ) id 1WKuFX-0001XM-LS for qemu-devel@nongnu.org; Tue, 04 Mar 2014 13:39:31 -0500 Message-ID: <53161DDE.6070004@suse.de> Date: Tue, 04 Mar 2014 19:39:26 +0100 From: =?ISO-8859-15?Q?Andreas_F=E4rber?= MIME-Version: 1.0 References: <1393957383-16685-1-git-send-email-a.motakis@virtualopensystems.com> <1393957383-16685-21-git-send-email-a.motakis@virtualopensystems.com> In-Reply-To: <1393957383-16685-21-git-send-email-a.motakis@virtualopensystems.com> Content-Type: text/plain; charset=ISO-8859-15 Content-Transfer-Encoding: quoted-printable Subject: Re: [Qemu-devel] [PATCH v9 20/20] Add qtest for vhost-user List-Id: List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , To: Antonios Motakis , qemu-devel@nongnu.org, snabb-devel@googlegroups.com Cc: Kevin Wolf , Anthony Liguori , mst@redhat.com, Markus Armbruster , n.nikolaev@virtualopensystems.com, Stefan Hajnoczi , lukego@gmail.com, tech@virtualopensystems.com Am 04.03.2014 19:23, schrieb Antonios Motakis: > This test creates a 'server' chardev to listen for vhost-user messages. > Once VHOST_USER_SET_MEM_TABLE is received it mmaps each received region= , > and read 1k bytes from it. The read data is compared to data from readl= . >=20 > The test requires hugetlbfs to be already mounted and writable. The mou= nt > point defaults to '/hugetlbfs' and can be specified via the environment > variable QTEST_HUGETLBFS_PATH. >=20 > The rom pc-bios/pxe-virtio.rom is used to instantiate a virtio pcicontr= oller. >=20 > Signed-off-by: Antonios Motakis > Signed-off-by: Nikolay Nikolaev > --- > tests/Makefile | 4 + > tests/vhost-user-test.c | 309 ++++++++++++++++++++++++++++++++++++++++= ++++++++ > 2 files changed, 313 insertions(+) > create mode 100644 tests/vhost-user-test.c >=20 > diff --git a/tests/Makefile b/tests/Makefile > index b17d41e..85bcae5 100644 > --- a/tests/Makefile > +++ b/tests/Makefile > @@ -110,6 +110,7 @@ check-qtest-i386-y +=3D tests/vmxnet3-test$(EXESUF) > gcov-files-i386-y +=3D hw/net/vmxnet3.c > gcov-files-i386-y +=3D hw/net/vmxnet_rx_pkt.c > gcov-files-i386-y +=3D hw/net/vmxnet_tx_pkt.c > +check-qtest-i386-y +=3D tests/vhost-user-test$(EXESUF) Not sure if I've asked already, but doesn't this test depend on certain Linux host support (hugetlbfs, vhost module)? One more comment below: > check-qtest-x86_64-y =3D $(check-qtest-i386-y) > gcov-files-i386-y +=3D i386-softmmu/hw/timer/mc146818rtc.c > gcov-files-x86_64-y =3D $(subst i386-softmmu/,x86_64-softmmu/,$(gcov-f= iles-i386-y)) > @@ -241,8 +242,11 @@ tests/ipoctal232-test$(EXESUF): tests/ipoctal232-t= est.o > tests/qom-test$(EXESUF): tests/qom-test.o > tests/blockdev-test$(EXESUF): tests/blockdev-test.o $(libqos-pc-obj-y) > tests/qdev-monitor-test$(EXESUF): tests/qdev-monitor-test.o $(libqos-p= c-obj-y) > +tests/vhost-user-test$(EXESUF): tests/vhost-user-test.o qemu-char.o qe= mu-timer.o libqemuutil.a libqemustub.a > tests/qemu-iotests/socket_scm_helper$(EXESUF): tests/qemu-iotests/sock= et_scm_helper.o > =20 > +LIBS+=3D -lutil > + > # QTest rules > =20 > TARGETS=3D$(patsubst %-softmmu,%, $(filter %-softmmu,$(TARGET_DIRS))) > diff --git a/tests/vhost-user-test.c b/tests/vhost-user-test.c > new file mode 100644 > index 0000000..a7160f8 > --- /dev/null > +++ b/tests/vhost-user-test.c > @@ -0,0 +1,309 @@ > +/* > + * QTest testcase for the vhost-user > + * > + * Copyright (c) 2014 Virtual Open Systems Sarl. > + * > + * This work is licensed under the terms of the GNU GPL, version 2 or = later. > + * See the COPYING file in the top-level directory. > + * > + */ > + > +#include "libqtest.h" > +#include "qemu/option.h" > +#include "sysemu/char.h" > +#include "sysemu/sysemu.h" > + > +#include > +#include > +#include > +#include > +#include > + > +#define QEMU_CMD_ACCEL " -machine accel=3Dtcg" > +#define QEMU_CMD_MEM " -mem-path %s,share=3Don" > +#define QEMU_CMD_CHR " -chardev socket,id=3Dchr0,path=3D%s" > +#define QEMU_CMD_NETDEV " -netdev vhost-user,id=3Dnet0,chardev=3Dchr0,= vhostforce" > +#define QEMU_CMD_NET " -device virtio-net-pci,netdev=3Dnet0 " > +#define QEMU_CMD_ROM " -option-rom ../pc-bios/pxe-virtio.rom" > + > +#define QEMU_CMD QEMU_CMD_ACCEL QEMU_CMD_MEM QEMU_CMD_CHR \ > + QEMU_CMD_NETDEV QEMU_CMD_NET QEMU_CMD_ROM > + > +#define HUGETLBFS_MAGIC 0x958458f6 > + > +/*********** FROM hw/virtio/vhost-user.c *****************************= ********/ > + > +#define VHOST_MEMORY_MAX_NREGIONS 8 > + > +typedef enum VhostUserRequest { > + VHOST_USER_NONE =3D 0, > + VHOST_USER_GET_FEATURES =3D 1, > + VHOST_USER_SET_FEATURES =3D 2, > + VHOST_USER_SET_OWNER =3D 3, > + VHOST_USER_RESET_OWNER =3D 4, > + VHOST_USER_SET_MEM_TABLE =3D 5, > + VHOST_USER_SET_LOG_BASE =3D 6, > + VHOST_USER_SET_LOG_FD =3D 7, > + VHOST_USER_SET_VRING_NUM =3D 8, > + VHOST_USER_SET_VRING_ADDR =3D 9, > + VHOST_USER_SET_VRING_BASE =3D 10, > + VHOST_USER_GET_VRING_BASE =3D 11, > + VHOST_USER_SET_VRING_KICK =3D 12, > + VHOST_USER_SET_VRING_CALL =3D 13, > + VHOST_USER_SET_VRING_ERR =3D 14, > + VHOST_USER_MAX > +} VhostUserRequest; > + > +typedef struct VhostUserMemoryRegion { > + uint64_t guest_phys_addr; > + uint64_t memory_size; > + uint64_t userspace_addr; > +} VhostUserMemoryRegion; > + > +typedef struct VhostUserMemory { > + uint32_t nregions; > + uint32_t padding; > + VhostUserMemoryRegion regions[VHOST_MEMORY_MAX_NREGIONS]; > +} VhostUserMemory; > + > +typedef struct VhostUserMsg { > + VhostUserRequest request; > + > +#define VHOST_USER_VERSION_MASK (0x3) > +#define VHOST_USER_REPLY_MASK (0x1<<2) > + uint32_t flags; > + uint32_t size; /* the following payload size */ > + union { > + uint64_t u64; > + struct vhost_vring_state state; > + struct vhost_vring_addr addr; > + VhostUserMemory memory; > + }; > +} QEMU_PACKED VhostUserMsg; > + > +static VhostUserMsg m __attribute__ ((unused)); > +#define VHOST_USER_HDR_SIZE (sizeof(m.request) \ > + + sizeof(m.flags) \ > + + sizeof(m.size)) > + > +#define VHOST_USER_PAYLOAD_SIZE (sizeof(m) - VHOST_USER_HDR_SIZE) > + > +/* The version of the protocol we support */ > +#define VHOST_USER_VERSION (0x1) > +/*********************************************************************= ********/ > + > +int fds_num =3D 0, fds[VHOST_MEMORY_MAX_NREGIONS]; > +static VhostUserMemory memory; > +static GMutex data_mutex; > +static GCond data_cond; > + > +static void read_guest_mem(void) > +{ > + uint32_t *guest_mem; > + gint64 end_time; > + int i, j; > + > + g_mutex_lock(&data_mutex); > + > + end_time =3D g_get_monotonic_time() + 5 * G_TIME_SPAN_SECOND; > + while (!fds_num) { > + if (!g_cond_wait_until(&data_cond, &data_mutex, end_time)) { > + /* timeout has passed */ > + g_assert(fds_num); > + break; > + } > + } > + > + /* check for sanity */ > + g_assert_cmpint(fds_num, >, 0); > + g_assert_cmpint(fds_num, =3D=3D, memory.nregions); > + > + /* iterate all regions */ > + for (i =3D 0; i < fds_num; i++) { > + > + /* We'll check only he region statring at 0x0, suppose it is *= / > + if (memory.regions[i].guest_phys_addr !=3D 0x0) { > + continue; > + } > + > + g_assert_cmpint(memory.regions[i].memory_size, >, 1024); > + > + guest_mem =3D mmap(0, memory.regions[i].memory_size, > + PROT_READ | PROT_WRITE, MAP_SHARED, fds[i], 0); > + > + for (j =3D 0; j < 256; j++) { > + uint32_t a =3D readl(memory.regions[i].guest_phys_addr + j= *4); > + uint32_t b =3D guest_mem[j]; > + > + g_assert_cmpint(a, =3D=3D, b); > + } > + > + munmap(guest_mem, memory.regions[i].memory_size); > + } > + > + g_assert_cmpint(1, =3D=3D, 1); > + g_mutex_unlock(&data_mutex); > +} > + > +static void *thread_function(void *data) > +{ > + GMainLoop *loop; > + loop =3D g_main_loop_new(NULL, FALSE); > + g_main_loop_run(loop); > + return NULL; > +} > + > +static int chr_can_read(void *opaque) > +{ > + return VHOST_USER_HDR_SIZE; > +} > + > +static void chr_read(void *opaque, const uint8_t *buf, int size) > +{ > + CharDriverState *chr =3D opaque; > + VhostUserMsg msg; > + uint8_t *p =3D (uint8_t *) &msg; > + int fd; > + > + if (size !=3D VHOST_USER_HDR_SIZE) { > + g_test_message("Wrong message size received %d\n", size); > + return; > + } > + > + memcpy(p, buf, VHOST_USER_HDR_SIZE); > + > + if (msg.size) { > + p +=3D VHOST_USER_HDR_SIZE; > + qemu_chr_fe_read_all(chr, p, msg.size); > + } > + > + switch (msg.request) { > + case VHOST_USER_GET_FEATURES: > + /* send back features to qemu */ > + msg.flags |=3D VHOST_USER_REPLY_MASK; > + msg.size =3D sizeof(m.u64); > + msg.u64 =3D 0; > + p =3D (uint8_t *) &msg; > + qemu_chr_fe_write_all(chr, p, VHOST_USER_HDR_SIZE + msg.size); > + break; > + > + case VHOST_USER_GET_VRING_BASE: > + /* send back vring base to qemu */ > + msg.flags |=3D VHOST_USER_REPLY_MASK; > + msg.size =3D sizeof(m.state); > + msg.state.num =3D 0; > + p =3D (uint8_t *) &msg; > + qemu_chr_fe_write_all(chr, p, VHOST_USER_HDR_SIZE + msg.size); > + break; > + > + case VHOST_USER_SET_MEM_TABLE: > + /* received the mem table */ > + memcpy(&memory, &msg.memory, sizeof(msg.memory)); > + fds_num =3D qemu_chr_fe_get_msgfds(chr, fds, sizeof(fds) / siz= eof(int)); > + > + /* signal the test that it can continue */ > + g_cond_signal(&data_cond); > + g_mutex_unlock(&data_mutex); > + break; > + > + case VHOST_USER_SET_VRING_KICK: > + case VHOST_USER_SET_VRING_CALL: > + /* consume the fd */ > + qemu_chr_fe_get_msgfds(chr, &fd, 1); > + /* > + * This is a non-blocking eventfd. > + * The receive function forces it to be blocking, > + * so revert it back to non-blocking. > + */ > + qemu_set_nonblock(fd); > + break; > + default: > + break; > + } > +} > + > +static const char *init_hugepagefs(void) > +{ > + const char *path; > + struct statfs fs; > + int ret; > + > + path =3D getenv("QTEST_HUGETLBFS_PATH"); > + if (!path) { > + path =3D "/hugetlbfs"; > + } > + > + if (access(path, R_OK | W_OK | X_OK)) { > + g_test_message("access on path (%s): %s\n", path, strerror(err= no)); > + return 0; > + } > + > + do { > + ret =3D statfs(path, &fs); > + } while (ret !=3D 0 && errno =3D=3D EINTR); > + > + if (ret !=3D 0) { > + g_test_message("statfs on path (%s): %s\n", path, strerror(err= no)); > + return 0; > + } > + > + if (fs.f_type !=3D HUGETLBFS_MAGIC) { > + g_test_message("Warning: path not on HugeTLBFS: %s\n", path); > + return 0; > + } > + > + return path; > +} > + > +int main(int argc, char **argv) > +{ > + QTestState *s =3D NULL; > + CharDriverState *chr =3D NULL; > + const char *hugefs =3D 0; > + char *socket_path =3D 0; > + char *qemu_cmd =3D 0; > + char *chr_path =3D 0; > + int ret; > + > + g_test_init(&argc, &argv, NULL); > + > + module_call_init(MODULE_INIT_QOM); > + > + hugefs =3D init_hugepagefs(); > + g_assert(hugefs); > + > + socket_path =3D g_strdup_printf("/tmp/vhost-%d.sock", getpid()); > + > + /* create char dev and add read handlers */ > + qemu_add_opts(&qemu_chardev_opts); > + chr_path =3D g_strdup_printf("unix:%s,server,nowait", socket_path)= ; > + chr =3D qemu_chr_new("chr0", chr_path, NULL); > + g_free(chr_path); > + qemu_chr_add_handlers(chr, chr_can_read, chr_read, NULL, chr); > + > + /* run the main loop thread so the chardev may operate */ > + g_mutex_init(&data_mutex); > + g_cond_init(&data_cond); > + g_mutex_lock(&data_mutex); > + g_thread_new(NULL, thread_function, NULL); > + > + qemu_cmd =3D g_strdup_printf(QEMU_CMD, hugefs, socket_path); > + s =3D qtest_start(qemu_cmd); > + g_free(qemu_cmd); > + > + qtest_add_func("/vhost-user/read-guest-mem", read_guest_mem); > + > + ret =3D g_test_run(); > + > + if (s) { > + qtest_quit(s); > + } > + > + /* cleanup */ > + unlink(socket_path); > + g_free(socket_path); I think this is probably too late to unlink in case the test fails, should probably be moved to right after qtest_start()? Thanks for your efforts on this test! Regards, Andreas > + g_cond_clear(&data_cond); > + g_mutex_clear(&data_mutex); > + > + return ret; > +} >=20 --=20 SUSE LINUX Products GmbH, Maxfeldstr. 5, 90409 N=FCrnberg, Germany GF: Jeff Hawn, Jennifer Guild, Felix Imend=F6rffer; HRB 16746 AG N=FCrnbe= rg