From mboxrd@z Thu Jan 1 00:00:00 1970 Received: from eggs.gnu.org ([2001:4830:134:3::10]:50465) by lists.gnu.org with esmtp (Exim 4.71) (envelope-from ) id 1ZgwrR-0005gK-91 for qemu-devel@nongnu.org; Tue, 29 Sep 2015 11:30:35 -0400 Received: from Debian-exim by eggs.gnu.org with spam-scanned (Exim 4.71) (envelope-from ) id 1ZgwrM-00014N-00 for qemu-devel@nongnu.org; Tue, 29 Sep 2015 11:30:33 -0400 Received: from mx4-phx2.redhat.com ([209.132.183.25]:34890) by eggs.gnu.org with esmtp (Exim 4.71) (envelope-from ) id 1ZgwrL-00013y-Jj for qemu-devel@nongnu.org; Tue, 29 Sep 2015 11:30:27 -0400 Date: Tue, 29 Sep 2015 11:30:18 -0400 (EDT) From: =?utf-8?Q?Marc-Andr=C3=A9?= Lureau Message-ID: <214721760.19922624.1443540618779.JavaMail.zimbra@redhat.com> In-Reply-To: <560AA8C9.7000507@huawei.com> References: <1443094669-4144-1-git-send-email-marcandre.lureau@redhat.com> <1443094669-4144-41-git-send-email-marcandre.lureau@redhat.com> <560AA8C9.7000507@huawei.com> MIME-Version: 1.0 Content-Type: text/plain; charset=utf-8 Content-Transfer-Encoding: quoted-printable Subject: Re: [Qemu-devel] [PATCH v4 40/47] tests: add ivshmem qtest List-Id: List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , To: Claudio Fontana Cc: drjones@redhat.com, qemu-devel@nongnu.org, stefanha@redhat.com, pbonzini@redhat.com, marcandre lureau , cam@cs.ualberta.ca, Andreas =?utf-8?Q?F=C3=A4rber?= ----- Original Message ----- > On 24.09.2015 13:37, marcandre.lureau@redhat.com wrote: > > From: Marc-Andr=C3=A9 Lureau > >=20 > > Adds 4 ivshmemtests: > > - single qemu instance and basic IO > > - pair of instances, check memory sharing > > - pair of instances with server, and MSIX > > - hot plug/unplug > >=20 > > A temporary shm is created as well as a directory to place server > > socket, both should be clear on exit and abort. >=20 > Just one comment below, but in short, what about supporting TMPDIR? I probably followed the trend in existing code. Imho this should be a seperate commit for the whole tests/, do you voluntee= r? :) > Also, have you considered SIGPIPE for your peers reading and writing to/f= rom > pipes? > Is the default behavior the one you want? >=20 What do you mean? if one ends disconnect I should test this case? Imho this is a seperate test I didn't consider, it could be added in a diff= erent patch. > Ciao, >=20 > Claudio >=20 >=20 > >=20 > > Cc: Cam Macdonell > > CC: Andreas F=C3=A4rber > > Signed-off-by: Marc-Andr=C3=A9 Lureau > > --- > > tests/Makefile | 3 + > > tests/ivshmem-test.c | 481 > > +++++++++++++++++++++++++++++++++++++++++++++++++++ > > 2 files changed, 484 insertions(+) > > create mode 100644 tests/ivshmem-test.c > >=20 > > diff --git a/tests/Makefile b/tests/Makefile > > index 4063639..7e6ac43 100644 > > --- a/tests/Makefile > > +++ b/tests/Makefile > > @@ -146,6 +146,8 @@ gcov-files-pci-y +=3D hw/display/virtio-gpu-pci.c > > gcov-files-pci-$(CONFIG_VIRTIO_VGA) +=3D hw/display/virtio-vga.c > > check-qtest-pci-y +=3D tests/intel-hda-test$(EXESUF) > > gcov-files-pci-y +=3D hw/audio/intel-hda.c hw/audio/hda-codec.c > > +check-qtest-pci-$(CONFIG_LINUX) +=3D tests/ivshmem-test$(EXESUF) > > +gcov-files-pci-y +=3D hw/misc/ivshmem.c > > =20 > > check-qtest-i386-y =3D tests/endianness-test$(EXESUF) > > check-qtest-i386-y +=3D tests/fdc-test$(EXESUF) > > @@ -435,6 +437,7 @@ tests/vhost-user-test$(EXESUF): tests/vhost-user-te= st.o > > qemu-char.o qemu-timer.o > > tests/qemu-iotests/socket_scm_helper$(EXESUF): > > tests/qemu-iotests/socket_scm_helper.o > > tests/test-qemu-opts$(EXESUF): tests/test-qemu-opts.o $(test-util-obj-= y) > > tests/test-write-threshold$(EXESUF): tests/test-write-threshold.o > > $(test-block-obj-y) > > +tests/ivshmem-test$(EXESUF): tests/ivshmem-test.o > > contrib/ivshmem-server/ivshmem-server.o $(libqos-pc-obj-y) > > =20 > > ifeq ($(CONFIG_POSIX),y) > > LIBS +=3D -lutil > > diff --git a/tests/ivshmem-test.c b/tests/ivshmem-test.c > > new file mode 100644 > > index 0000000..097de15 > > --- /dev/null > > +++ b/tests/ivshmem-test.c > > @@ -0,0 +1,481 @@ > > +/* > > + * QTest testcase for ivshmem > > + * > > + * Copyright (c) 2015 Red Hat, Inc. > > + * > > + * 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 > > +#include > > +#include > > +#include > > +#include > > +#include > > +#include > > +#include "contrib/ivshmem-server/ivshmem-server.h" > > +#include "libqos/pci-pc.h" > > +#include "libqtest.h" > > +#include "qemu/osdep.h" > > +#include > > + > > +#if GLIB_CHECK_VERSION(2, 32, 0) > > +#define HAVE_THREAD_NEW > > +#endif > > + > > +#define TMPSHMSIZE (1 << 20) > > +static char *tmpshm; > > +static void *tmpshmem; > > +static char *tmpdir; > > +static char *tmpserver; > > + > > +static void save_fn(QPCIDevice *dev, int devfn, void *data) > > +{ > > + QPCIDevice **pdev =3D (QPCIDevice **) data; > > + > > + *pdev =3D dev; > > +} > > + > > +static QPCIDevice *get_device(void) > > +{ > > + QPCIDevice *dev; > > + QPCIBus *pcibus; > > + > > + pcibus =3D qpci_init_pc(); > > + qpci_device_foreach(pcibus, 0x1af4, 0x1110, save_fn, &dev); > > + g_assert(dev !=3D NULL); > > + > > + return dev; > > +} > > + > > +typedef struct _IVState { > > + QTestState *qtest; > > + void *reg_base, *mem_base; > > + QPCIDevice *dev; > > +} IVState; > > + > > +enum Reg { > > + INTRMASK =3D 0, > > + INTRSTATUS =3D 4, > > + IVPOSITION =3D 8, > > + DOORBELL =3D 12, > > +}; > > + > > +static const char* reg2str(enum Reg reg) { > > + switch (reg) { > > + case INTRMASK: > > + return "IntrMask"; > > + case INTRSTATUS: > > + return "IntrStatus"; > > + case IVPOSITION: > > + return "IVPosition"; > > + case DOORBELL: > > + return "DoorBell"; > > + default: > > + return NULL; > > + } > > +} > > + > > +static inline unsigned in_reg(IVState *s, enum Reg reg) > > +{ > > + const char *name =3D reg2str(reg); > > + QTestState *qtest =3D global_qtest; > > + unsigned res; > > + > > + global_qtest =3D s->qtest; > > + res =3D qpci_io_readl(s->dev, s->reg_base + reg); > > + g_test_message("*%s -> %x\n", name, res); > > + global_qtest =3D qtest; > > + > > + return res; > > +} > > + > > +static inline void out_reg(IVState *s, enum Reg reg, unsigned v) > > +{ > > + const char *name =3D reg2str(reg); > > + QTestState *qtest =3D global_qtest; > > + > > + global_qtest =3D s->qtest; > > + g_test_message("%x -> *%s\n", v, name); > > + qpci_io_writel(s->dev, s->reg_base + reg, v); > > + global_qtest =3D qtest; > > +} > > + > > +static void setup_vm_cmd(IVState *s, const char *cmd, bool msix) > > +{ > > + uint64_t barsize; > > + > > + s->qtest =3D qtest_start(cmd); > > + > > + s->dev =3D get_device(); > > + > > + /* FIXME: other bar order fails, mappings changes */ > > + s->mem_base =3D qpci_iomap(s->dev, 2, &barsize); > > + g_assert_nonnull(s->mem_base); > > + g_assert_cmpuint(barsize, =3D=3D, TMPSHMSIZE); > > + > > + if (msix) { > > + qpci_msix_enable(s->dev); > > + } > > + > > + s->reg_base =3D qpci_iomap(s->dev, 0, &barsize); > > + g_assert_nonnull(s->reg_base); > > + g_assert_cmpuint(barsize, =3D=3D, 256); > > + > > + qpci_device_enable(s->dev); > > +} > > + > > +static void setup_vm(IVState *s) > > +{ > > + char *cmd =3D g_strdup_printf("-device ivshmem,shm=3D%s,size=3D1M"= , tmpshm); > > + > > + setup_vm_cmd(s, cmd, false); > > + > > + g_free(cmd); > > +} > > + > > +static void test_ivshmem_single(void) > > +{ > > + IVState state, *s; > > + uint32_t data[1024]; > > + int i; > > + > > + setup_vm(&state); > > + s =3D &state; > > + > > + /* valid io */ > > + out_reg(s, INTRMASK, 0); > > + in_reg(s, INTRSTATUS); > > + in_reg(s, IVPOSITION); > > + > > + out_reg(s, INTRMASK, 0xffffffff); > > + g_assert_cmpuint(in_reg(s, INTRMASK), =3D=3D, 0xffffffff); > > + out_reg(s, INTRSTATUS, 1); > > + /* XXX: intercept IRQ, not seen in resp */ > > + g_assert_cmpuint(in_reg(s, INTRSTATUS), =3D=3D, 1); > > + > > + /* invalid io */ > > + out_reg(s, IVPOSITION, 1); > > + out_reg(s, DOORBELL, 8 << 16); > > + > > + for (i =3D 0; i < G_N_ELEMENTS(data); i++) { > > + data[i] =3D i; > > + } > > + qtest_memwrite(s->qtest, (uintptr_t)s->mem_base, data, sizeof(data= )); > > + > > + for (i =3D 0; i < G_N_ELEMENTS(data); i++) { > > + g_assert_cmpuint(((uint32_t *)tmpshmem)[i], =3D=3D, i); > > + } > > + > > + memset(data, 0, sizeof(data)); > > + > > + qtest_memread(s->qtest, (uintptr_t)s->mem_base, data, sizeof(data)= ); > > + for (i =3D 0; i < G_N_ELEMENTS(data); i++) { > > + g_assert_cmpuint(data[i], =3D=3D, i); > > + } > > + > > + qtest_quit(s->qtest); > > +} > > + > > +static void test_ivshmem_pair(void) > > +{ > > + IVState state1, state2, *s1, *s2; > > + char *data; > > + int i; > > + > > + setup_vm(&state1); > > + s1 =3D &state1; > > + setup_vm(&state2); > > + s2 =3D &state2; > > + > > + data =3D g_malloc0(TMPSHMSIZE); > > + > > + /* host write, guest 1 & 2 read */ > > + memset(tmpshmem, 0x42, TMPSHMSIZE); > > + qtest_memread(s1->qtest, (uintptr_t)s1->mem_base, data, TMPSHMSIZE= ); > > + for (i =3D 0; i < TMPSHMSIZE; i++) { > > + g_assert_cmpuint(data[i], =3D=3D, 0x42); > > + } > > + qtest_memread(s2->qtest, (uintptr_t)s2->mem_base, data, TMPSHMSIZE= ); > > + for (i =3D 0; i < TMPSHMSIZE; i++) { > > + g_assert_cmpuint(data[i], =3D=3D, 0x42); > > + } > > + > > + /* guest 1 write, guest 2 read */ > > + memset(data, 0x43, TMPSHMSIZE); > > + qtest_memwrite(s1->qtest, (uintptr_t)s1->mem_base, data, TMPSHMSIZ= E); > > + memset(data, 0, TMPSHMSIZE); > > + qtest_memread(s2->qtest, (uintptr_t)s2->mem_base, data, TMPSHMSIZE= ); > > + for (i =3D 0; i < TMPSHMSIZE; i++) { > > + g_assert_cmpuint(data[i], =3D=3D, 0x43); > > + } > > + > > + /* guest 2 write, guest 1 read */ > > + memset(data, 0x44, TMPSHMSIZE); > > + qtest_memwrite(s2->qtest, (uintptr_t)s2->mem_base, data, TMPSHMSIZ= E); > > + memset(data, 0, TMPSHMSIZE); > > + qtest_memread(s1->qtest, (uintptr_t)s2->mem_base, data, TMPSHMSIZE= ); > > + for (i =3D 0; i < TMPSHMSIZE; i++) { > > + g_assert_cmpuint(data[i], =3D=3D, 0x44); > > + } > > + > > + qtest_quit(s1->qtest); > > + qtest_quit(s2->qtest); > > + g_free(data); > > +} > > + > > +typedef struct ServerThread { > > + GThread *thread; > > + IvshmemServer *server; > > + int pipe[2]; /* to handle quit */ > > +} ServerThread; > > + > > +static void *server_thread(void *data) > > +{ > > + ServerThread *t =3D data; > > + IvshmemServer *server =3D t->server; > > + > > + while (true) { > > + fd_set fds; > > + int maxfd, ret; > > + > > + FD_ZERO(&fds); > > + FD_SET(t->pipe[0], &fds); > > + maxfd =3D t->pipe[0] + 1; > > + > > + ivshmem_server_get_fds(server, &fds, &maxfd); > > + > > + ret =3D select(maxfd, &fds, NULL, NULL, NULL); > > + > > + if (ret < 0) { > > + if (errno =3D=3D EINTR) { > > + continue; > > + } > > + > > + g_critical("select error: %s\n", strerror(errno)); > > + break; > > + } > > + if (ret =3D=3D 0) { > > + continue; > > + } > > + > > + if (FD_ISSET(t->pipe[0], &fds)) { > > + break; > > + } > > + > > + if (ivshmem_server_handle_fds(server, &fds, maxfd) < 0) { > > + g_critical("ivshmem_server_handle_fds() failed\n"); > > + break; > > + } > > + } > > + > > + return NULL; > > +} > > + > > +static void setup_vm_with_server(IVState *s, int nvectors) > > +{ > > + char *cmd =3D g_strdup_printf("-chardev socket,id=3Dchr0,path=3D%s= ,nowait " > > + "-device > > ivshmem,size=3D1M,chardev=3Dchr0,vectors=3D%d", > > + tmpserver, nvectors); > > + > > + setup_vm_cmd(s, cmd, true); > > + > > + g_free(cmd); > > +} > > + > > +static GThread *thread_new(const gchar *name, GThreadFunc func, gpoint= er > > data) > > +{ > > + GThread *thread =3D NULL; > > + GError *error =3D NULL; > > +#ifdef HAVE_THREAD_NEW > > + thread =3D g_thread_try_new(name, func, data, &error); > > +#else > > + thread =3D g_thread_create(func, data, TRUE, &error); > > +#endif > > + g_assert_no_error(error); > > + return thread; > > +} > > + > > +static void test_ivshmem_server(void) > > +{ > > + IVState state1, state2, *s1, *s2; > > + ServerThread thread; > > + IvshmemServer server; > > + int ret, vm1, vm2; > > + int nvectors =3D 2; > > + > > + memset(tmpshmem, 0x42, TMPSHMSIZE); > > + ret =3D ivshmem_server_init(&server, tmpserver, tmpshm, > > + TMPSHMSIZE, nvectors, > > + getenv("QTEST_LOG") !=3D NULL); > > + g_assert_cmpint(ret, =3D=3D, 0); > > + > > + ret =3D ivshmem_server_start(&server); > > + g_assert_cmpint(ret, =3D=3D, 0); > > + > > + setup_vm_with_server(&state1, nvectors); > > + s1 =3D &state1; > > + setup_vm_with_server(&state2, nvectors); > > + s2 =3D &state2; > > + > > + g_assert_cmpuint(in_reg(s1, IVPOSITION), =3D=3D, 0xffffffff); > > + g_assert_cmpuint(in_reg(s2, IVPOSITION), =3D=3D, 0xffffffff); > > + > > + g_assert_cmpuint(qtest_readb(s1->qtest, (uintptr_t)s1->mem_base), = =3D=3D, > > 0x00); > > + > > + thread.server =3D &server; > > + ret =3D pipe(thread.pipe); > > + g_assert_cmpint(ret, =3D=3D, 0); > > + thread.thread =3D thread_new("ivshmem-server", server_thread, &thr= ead); > > + > > + /* waiting until mapping is done */ > > + while (true) { > > + g_usleep(1000); > > + > > + if (qtest_readb(s1->qtest, (uintptr_t)s1->mem_base) =3D=3D 0x4= 2 && > > + qtest_readb(s2->qtest, (uintptr_t)s2->mem_base) =3D=3D 0x4= 2) { > > + break; > > + } > > + } > > + > > + /* check got different VM ids */ > > + vm1 =3D in_reg(s1, IVPOSITION); > > + vm2 =3D in_reg(s2, IVPOSITION); > > + g_assert_cmpuint(vm1, !=3D, vm2); > > + > > + global_qtest =3D s1->qtest; > > + ret =3D qpci_msix_table_size(s1->dev); > > + g_assert_cmpuint(ret, =3D=3D, nvectors); > > + > > + /* ping vm2 -> vm1 */ > > + ret =3D qpci_msix_pending(s1->dev, 0); > > + g_assert_cmpuint(ret, =3D=3D, 0); > > + out_reg(s2, DOORBELL, vm1 << 16); > > + g_usleep(10000); > > + ret =3D qpci_msix_pending(s1->dev, 0); > > + g_assert_cmpuint(ret, !=3D, 0); > > + > > + /* ping vm1 -> vm2 */ > > + global_qtest =3D s2->qtest; > > + ret =3D qpci_msix_pending(s2->dev, 0); > > + g_assert_cmpuint(ret, =3D=3D, 0); > > + out_reg(s1, DOORBELL, vm2 << 16); > > + g_usleep(10000); > > + ret =3D qpci_msix_pending(s2->dev, 0); > > + g_assert_cmpuint(ret, !=3D, 0); > > + > > + /* remove vm2 */ > > + qtest_quit(s2->qtest); > > + /* XXX wait enough time for vm1 to be notified */ > > + g_usleep(1000); > > + > > + qtest_quit(s1->qtest); > > + > > + write(thread.pipe[1], "q", 1); > > + g_thread_join(thread.thread); > > + > > + ivshmem_server_close(&server); > > + close(thread.pipe[1]); > > + close(thread.pipe[0]); > > +} > > + > > +#define PCI_SLOT_HP 0x06 > > + > > +static void test_ivshmem_hotplug(void) > > +{ > > + gchar *opts; > > + > > + qtest_start(""); > > + > > + opts =3D g_strdup_printf("'shm': '%s', 'size': '1M'", tmpshm); > > + > > + qpci_plug_device_test("ivshmem", "iv1", PCI_SLOT_HP, opts); > > + qpci_unplug_acpi_device_test("iv1", PCI_SLOT_HP); > > + > > + qtest_end(); > > + g_free(opts); > > +} > > + > > +static void cleanup(void) > > +{ > > + if (tmpshmem) { > > + munmap(tmpshmem, TMPSHMSIZE); > > + tmpshmem =3D NULL; > > + } > > + > > + if (tmpshm) { > > + shm_unlink(tmpshm); > > + g_free(tmpshm); > > + tmpshm =3D NULL; > > + } > > + > > + if (tmpserver) { > > + g_unlink(tmpserver); > > + g_free(tmpserver); > > + tmpserver =3D NULL; > > + } > > + > > + if (tmpdir) { > > + g_rmdir(tmpdir); > > + tmpdir =3D NULL; > > + } > > +} > > + > > +static void abrt_handler(void *data) > > +{ > > + cleanup(); > > +} > > + > > +static gchar *mktempshm(int size, int *fd) > > +{ > > + while (true) { > > + gchar *name; > > + > > + name =3D g_strdup_printf("/qtest-%u-%u", getpid(), g_random_in= t()); > > + *fd =3D shm_open(name, O_CREAT|O_RDWR|O_EXCL, > > + S_IRWXU|S_IRWXG|S_IRWXO); > > + if (*fd > 0) { > > + g_assert(ftruncate(*fd, size) =3D=3D 0); > > + return name; > > + } > > + > > + g_free(name); > > + } > > +} > > + > > +int main(int argc, char **argv) > > +{ > > + int ret, fd; > > + static gchar dir[] =3D "/tmp/ivshmem-test.XXXXXX"; >=20 > It forces creation of files in /tmp though, what about respecting user's > desires via TMPDIR? > Granted, the amount of offenders in tests/ is large. >=20 > > + > > +#if !GLIB_CHECK_VERSION(2, 31, 0) > > + if (!g_thread_supported()) { > > + g_thread_init(NULL); > > + } > > +#endif > > + > > + g_test_init(&argc, &argv, NULL); > > + > > + qtest_add_abrt_handler(abrt_handler, NULL); > > + /* shm */ > > + tmpshm =3D mktempshm(TMPSHMSIZE, &fd); > > + tmpshmem =3D mmap(0, TMPSHMSIZE, PROT_READ|PROT_WRITE, MAP_SHARED,= fd, > > 0); > > + g_assert(tmpshmem !=3D MAP_FAILED); > > + /* server */ > > + if (g_mkdtemp_full(dir, 0700) =3D=3D NULL) { > > + g_error("g_mkdtemp_full: %s", g_strerror(errno)); > > + } > > + tmpdir =3D dir; > > + tmpserver =3D g_strconcat(tmpdir, "/server", NULL); > > + > > + qtest_add_func("/ivshmem/single", test_ivshmem_single); > > + qtest_add_func("/ivshmem/pair", test_ivshmem_pair); > > + qtest_add_func("/ivshmem/server", test_ivshmem_server); > > + qtest_add_func("/ivshmem/hotplug", test_ivshmem_hotplug); > > + > > + ret =3D g_test_run(); > > + > > + cleanup(); > > + return ret; > > +} > >=20 >=20 >=20 >=20 >=20