diff mbox

[39/39] tests: Add ivshmem qtest

Message ID 1435330185-23248-40-git-send-email-marcandre.lureau@gmail.com
State New
Headers show

Commit Message

Marc-André Lureau June 26, 2015, 2:49 p.m. UTC
Cc: Cam Macdonell <cam@cs.ualberta.ca>
CC: Andreas Färber <afaerber@suse.de>
Signed-off-by: Marc-André Lureau <marcandre.lureau@gmail.com>
---
 tests/Makefile       |   3 +
 tests/ivshmem-test.c | 475 +++++++++++++++++++++++++++++++++++++++++++++++++++
 2 files changed, 478 insertions(+)
 create mode 100644 tests/ivshmem-test.c

Comments

Marc-André Lureau June 26, 2015, 3:27 p.m. UTC | #1
Hi

On Fri, Jun 26, 2015 at 5:08 PM, Andreas Färber <afaerber@suse.de> wrote:

>
> For the second time within days you're posting a patch for something
> that's been on the list already... The commit message above is entirely
>

Sorry which patch are you refering to?

Is it wrong to repost a old patch and adding, while adding signoff and
modifications?
(what I just did in this series for David patches)

empty, and at least this patch is not carrying any form of change log
> either. You seem to add more tests than I did at the time, but that
> still does not warrant dropping previous authorship info (Signed-off-by,
> From, copyright).


Sorry, I will try to describe the tests in the commit log (I thought they
where already
self-explanatory, but commit comments is always nice anyway)

Tbh, your patch was just launching one instance of qemu with ivshmem. If you
look at the one I proposed, you'll notice that it is completely different:
I don't think your
signoff or copyright should be added here.
Marc-André Lureau June 26, 2015, 3:44 p.m. UTC | #2
On Fri, Jun 26, 2015 at 5:27 PM, Marc-André Lureau <
marcandre.lureau@gmail.com> wrote:

> Sorry which patch are you refering to?


You probably mean about the missing include: I wasn't aware such patch was
already submitted, and I don't think it's a problem if two people identify
the same issue and report a patch to fix it, quite the contrary.
Andreas Färber June 26, 2015, 3:53 p.m. UTC | #3
Hi,

Please don't use HTML mails, that breaks quoting as you can see below.

Am 26.06.2015 um 17:27 schrieb Marc-André Lureau:
> On Fri, Jun 26, 2015 at 5:08 PM, Andreas Färber <afaerber@suse.de
> <mailto:afaerber@suse.de>> wrote:
> 
> 
>     For the second time within days you're posting a patch for something
>     that's been on the list already... The commit message above is entirely
> 
> 
> Sorry which patch are you refering to?

This one vs. my http://patchwork.ozlabs.org/patch/336367/

And my "[PATCH for-2.3? 0/7] tests: Fix TCG make test" series vs.
your http://patchwork.ozlabs.org/patch/487719/

> Is it wrong to repost a old patch and adding, while adding signoff and
> modifications?
> (what I just did in this series for David patches)

"contrib: add ivshmem client and server" looks (formally) okay except
for the final [...], which should go before your Signed-off-by.

That's not what you've done here though, as I complained. You did not
ask me about the state of my patch either before you posted this.

> 
>     empty, and at least this patch is not carrying any form of change log
>     either. You seem to add more tests than I did at the time, but that
>     still does not warrant dropping previous authorship info (Signed-off-by,
>     From, copyright).
> 
> 
> Sorry, I will try to describe the tests in the commit log (I thought
> they where already
> self-explanatory, but commit comments is always nice anyway)
> 
> Tbh, your patch was just launching one instance of qemu with ivshmem. If you
> look at the one I proposed, you'll notice that it is completely
> different: I don't think your
> signoff or copyright should be added here.

Well, I disagree. Extending a test case does not give you the right to
replace SUSE with Red Hat. Makes it look like an NIH problem.

Also, see the discussion on my RFC: CONFIG_LINUX is wrong here. It would
need to depend on KVM/eventfd, and for that there is no easy config
option available here, which stopped my patch at the time.

Regards,
Andreas
Marc-André Lureau June 26, 2015, 4:02 p.m. UTC | #4
Hi

On Fri, Jun 26, 2015 at 5:53 PM, Andreas Färber <afaerber@suse.de> wrote:

> Please don't use HTML mails, that breaks quoting as you can see below.
>
>
I mostly use gmail, afaik it's multipart, never had issues with that.

Am 26.06.2015 um 17:27 schrieb Marc-André Lureau:
> > On Fri, Jun 26, 2015 at 5:08 PM, Andreas Färber <afaerber@suse.de
> > <mailto:afaerber@suse.de>> wrote:
> >
> >
> >     For the second time within days you're posting a patch for something
> >     that's been on the list already... The commit message above is
> entirely
> >
> >
> > Sorry which patch are you refering to?
>
> This one vs. my http://patchwork.ozlabs.org/patch/336367/
>
> And my "[PATCH for-2.3? 0/7] tests: Fix TCG make test" series vs.
> your http://patchwork.ozlabs.org/patch/487719/
>

See my previous reply.

>
> > Is it wrong to repost a old patch and adding, while adding signoff and
> > modifications?
> > (what I just did in this series for David patches)
>
> "contrib: add ivshmem client and server" looks (formally) okay except
> for the final [...], which should go before your Signed-off-by.
>

Ok, thanks I'll check that.


> That's not what you've done here though, as I complained. You did not
> ask me about the state of my patch either before you posted this.
>

I don't see what that changes. You can give the status of your patch now.


>
> > Tbh, your patch was just launching one instance of qemu with ivshmem. If
> you
> > look at the one I proposed, you'll notice that it is completely
> > different: I don't think your
> > signoff or copyright should be added here.
>
> Well, I disagree. Extending a test case does not give you the right to
> replace SUSE with Red Hat. Makes it look like an NIH problem.
>

It's really a different patch, sorry.


> Also, see the discussion on my RFC: CONFIG_LINUX is wrong here. It would
> need to depend on KVM/eventfd, and for that there is no easy config
> option available here, which stopped my patch at the time.
>

Right, I don't have good answer for that. I'd need more help.

cheers
Andreas Färber June 26, 2015, 4:05 p.m. UTC | #5
Am 26.06.2015 um 17:44 schrieb Marc-André Lureau:
> On Fri, Jun 26, 2015 at 5:27 PM, Marc-André Lureau
> <marcandre.lureau@gmail.com <mailto:marcandre.lureau@gmail.com>> wrote:
> 
>     Sorry which patch are you refering to?
> 
> 
> You probably mean about the missing include: I wasn't aware such patch
> was already submitted, and I don't think it's a problem if two people
> identify the same issue and report a patch to fix it, quite the contrary.

It can happen occasionally. Problem is when a pattern emerges.

I invested time in this, and I hate having my time wasted by someone
else redoing my work and taking the credit for it, whatever
justification you come up with.

Andreas
Andreas Färber June 26, 2015, 4:13 p.m. UTC | #6
Am 26.06.2015 um 18:02 schrieb Marc-André Lureau:
> On Fri, Jun 26, 2015 at 5:53 PM, Andreas Färber <afaerber@suse.de
> <mailto:afaerber@suse.de>> wrote:
> 
>     Please don't use HTML mails, that breaks quoting as you can see below.
> 
> 
> I mostly use gmail, afaik it's multipart, never had issues with that.

The rule is no HTML on qemu-devel, similar to LKML.
You're definitely not the only Gmail user here.

Regards,
Andreas
Marc-André Lureau July 9, 2015, 2:37 p.m. UTC | #7
Hi Andreas

On Fri, Jun 26, 2015 at 6:02 PM, Marc-André Lureau
<marcandre.lureau@gmail.com> wrote:
>> Also, see the discussion on my RFC: CONFIG_LINUX is wrong here. It would
>> need to depend on KVM/eventfd, and for that there is no easy config
>> option available here, which stopped my patch at the time.
>
>
> Right, I don't have good answer for that. I'd need more help.

Actually, ivshmem doesn't use ioeventfd by default. And my tests do
not enable it either.

So the test runs fine with kvm disabled on my host. Is that enough then?

I couldn't find the discussion on "RFC: CONFIG_LINUX", do you have a pointer?

thanks
Andreas Färber July 9, 2015, 3:13 p.m. UTC | #8
Hi Marc-André,

Am 09.07.2015 um 16:37 schrieb Marc-André Lureau:
> On Fri, Jun 26, 2015 at 6:02 PM, Marc-André Lureau
> <marcandre.lureau@gmail.com> wrote:
>>> Also, see the discussion on my RFC: CONFIG_LINUX is wrong here. It would
>>> need to depend on KVM/eventfd, and for that there is no easy config
>>> option available here, which stopped my patch at the time.
>>
>>
>> Right, I don't have good answer for that. I'd need more help.
> 
> Actually, ivshmem doesn't use ioeventfd by default. And my tests do
> not enable it either.
> 
> So the test runs fine with kvm disabled on my host. Is that enough then?
> 
> I couldn't find the discussion on "RFC: CONFIG_LINUX", do you have a pointer?

Pretty sure I pointed you to my RFC already:
http://patchwork.ozlabs.org/patch/336367/

Paolo wrote: "I think ivshmem depends on ioeventfd"
And mst suggested: "maybe it's easier to implement eventfd APIs"
Which I didn't do, and am not aware anyone else did, but maybe I missed it?

I've dug out my old patch with the tempfile name fix but didn't get to
implementing that missing query while dealing with the qom-cpu pull.

Regards,
Andreas
diff mbox

Patch

diff --git a/tests/Makefile b/tests/Makefile
index eff5e11..0b85023 100644
--- a/tests/Makefile
+++ b/tests/Makefile
@@ -137,6 +137,8 @@  gcov-files-pci-y += hw/display/cirrus_vga.c
 gcov-files-pci-y += hw/display/vga-pci.c
 check-qtest-pci-y += tests/intel-hda-test$(EXESUF)
 gcov-files-pci-y += hw/audio/intel-hda.c hw/audio/hda-codec.c
+check-qtest-pci-$(CONFIG_LINUX) += tests/ivshmem-test$(EXESUF)
+gcov-files-pci-y += hw/misc/ivshmem.c
 
 check-qtest-i386-y = tests/endianness-test$(EXESUF)
 check-qtest-i386-y += tests/fdc-test$(EXESUF)
@@ -400,6 +402,7 @@  tests/vhost-user-test$(EXESUF): tests/vhost-user-test.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 libqemuutil.a libqemustub.a
 tests/test-write-threshold$(EXESUF): tests/test-write-threshold.o $(block-obj-y) libqemuutil.a libqemustub.a
+tests/ivshmem-test$(EXESUF): tests/ivshmem-test.o contrib/ivshmem-server/ivshmem-server.o $(libqos-pc-obj-y)
 
 ifeq ($(CONFIG_POSIX),y)
 LIBS += -lutil
diff --git a/tests/ivshmem-test.c b/tests/ivshmem-test.c
new file mode 100644
index 0000000..3ebf572
--- /dev/null
+++ b/tests/ivshmem-test.c
@@ -0,0 +1,475 @@ 
+/*
+ * 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 <errno.h>
+#include <fcntl.h>
+#include <glib.h>
+#include <glib/gstdio.h>
+#include <string.h>
+#include <sys/mman.h>
+#include <unistd.h>
+#include "contrib/ivshmem-server/ivshmem-server.h"
+#include "libqos/pci-pc.h"
+#include "libqtest.h"
+#include "qemu/osdep.h"
+#include <stdlib.h>
+
+#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 = (QPCIDevice **) data;
+
+    *pdev = dev;
+}
+
+static QPCIDevice *get_device(void)
+{
+    QPCIDevice *dev;
+    QPCIBus *pcibus;
+
+    pcibus = qpci_init_pc();
+    qpci_device_foreach(pcibus, 0x1af4, 0x1110, save_fn, &dev);
+    g_assert(dev != NULL);
+
+    return dev;
+}
+
+typedef struct _IVState {
+    QTestState *qtest;
+    void *reg_base, *mem_base;
+    QPCIDevice *dev;
+} IVState;
+
+#define REG(name, len, val)                                     \
+    static inline unsigned in_##name(IVState *s)                \
+    {                                                           \
+        QTestState *qtest = global_qtest;                       \
+        unsigned res;                                           \
+        global_qtest = s->qtest;                                \
+        res = qpci_io_read##len(s->dev, s->reg_base+(val));     \
+        g_test_message("*%s -> %x\n", #name, res);              \
+        global_qtest = qtest;                                   \
+        return res;                                             \
+    }                                                           \
+    static inline void out_##name(IVState *s, unsigned v)       \
+    {                                                           \
+        QTestState *qtest = global_qtest;                       \
+        global_qtest = s->qtest;                                \
+        g_test_message("%x -> *%s\n", v, #name);                \
+        qpci_io_write##len(s->dev, s->reg_base+(val), v);       \
+        global_qtest = qtest;                                   \
+    }
+
+REG(IntrMask, l, 0)
+REG(IntrStatus, l, 4)
+REG(IVPosition, l, 8)
+REG(DoorBell, l, 12)
+
+#if 0
+static void info_qtree(void)
+{
+    QDict *response;
+
+    response = qmp("{'execute': 'human-monitor-command',"
+                   " 'arguments': {"
+                   "   'command-line': 'info qtree'"
+                   "}}");
+    g_assert(response);
+    g_debug(qdict_get_try_str(response, "return"));
+    QDECREF(response);
+}
+#endif
+
+static void setup_vm_cmd(IVState *s, const char *cmd, bool msix)
+{
+    uint64_t barsize;
+
+    s->qtest = qtest_start(cmd);
+
+    s->dev = get_device();
+
+    /* FIXME: other bar order fails, mappings changes */
+    s->mem_base = qpci_iomap(s->dev, 2, &barsize);
+    g_assert_nonnull(s->mem_base);
+    g_assert_cmpuint(barsize, ==, TMPSHMSIZE);
+
+    if (msix) {
+        qpci_msix_enable(s->dev);
+    }
+
+    s->reg_base = qpci_iomap(s->dev, 0, &barsize);
+    g_assert_nonnull(s->reg_base);
+    g_assert_cmpuint(barsize, ==, 256);
+
+    qpci_device_enable(s->dev);
+}
+
+static void setup_vm(IVState *s)
+{
+    char *cmd = g_strdup_printf("-device ivshmem,shm=%s,size=1M", 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 = &state;
+
+    /* valid io */
+    out_IntrMask(s, 0);
+    in_IntrStatus(s);
+    in_IVPosition(s);
+
+    out_IntrMask(s, 0xffffffff);
+    g_assert_cmpuint(in_IntrMask(s), ==, 0xffffffff);
+    out_IntrStatus(s, 1);
+    /* XXX: intercept IRQ, not seen in resp */
+    g_assert_cmpuint(in_IntrStatus(s), ==, 1);
+
+    /* invalid io */
+    out_IVPosition(s, 1);
+    out_DoorBell(s, 8 << 16);
+
+    for (i = 0; i < G_N_ELEMENTS(data); i++) {
+        data[i] = i;
+    }
+    qtest_memwrite(s->qtest, (uintptr_t)s->mem_base, data, sizeof(data));
+
+    for (i = 0; i < G_N_ELEMENTS(data); i++) {
+        g_assert_cmpuint(((uint32_t *)tmpshmem)[i], ==, i);
+    }
+
+    memset(data, 0, sizeof(data));
+
+    qtest_memread(s->qtest, (uintptr_t)s->mem_base, data, sizeof(data));
+    for (i = 0; i < G_N_ELEMENTS(data); i++) {
+        g_assert_cmpuint(data[i], ==, i);
+    }
+
+    qtest_quit(s->qtest);
+}
+
+static void test_ivshmem_pair(void)
+{
+    IVState state1, state2, *s1, *s2;
+    char *data;
+    int i;
+
+    setup_vm(&state1);
+    s1 = &state1;
+    setup_vm(&state2);
+    s2 = &state2;
+
+    data = 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 = 0; i < TMPSHMSIZE; i++) {
+        g_assert_cmpuint(data[i], ==, 0x42);
+    }
+    qtest_memread(s2->qtest, (uintptr_t)s2->mem_base, data, TMPSHMSIZE);
+    for (i = 0; i < TMPSHMSIZE; i++) {
+        g_assert_cmpuint(data[i], ==, 0x42);
+    }
+
+    /* guest 1 write, guest 2 read */
+    memset(data, 0x43, TMPSHMSIZE);
+    qtest_memwrite(s1->qtest, (uintptr_t)s1->mem_base, data, TMPSHMSIZE);
+    memset(data, 0, TMPSHMSIZE);
+    qtest_memread(s2->qtest, (uintptr_t)s2->mem_base, data, TMPSHMSIZE);
+    for (i = 0; i < TMPSHMSIZE; i++) {
+        g_assert_cmpuint(data[i], ==, 0x43);
+    }
+
+    /* guest 2 write, guest 1 read */
+    memset(data, 0x44, TMPSHMSIZE);
+    qtest_memwrite(s2->qtest, (uintptr_t)s2->mem_base, data, TMPSHMSIZE);
+    memset(data, 0, TMPSHMSIZE);
+    qtest_memread(s1->qtest, (uintptr_t)s2->mem_base, data, TMPSHMSIZE);
+    for (i = 0; i < TMPSHMSIZE; i++) {
+        g_assert_cmpuint(data[i], ==, 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 = data;
+    IvshmemServer *server = t->server;
+
+    while (true) {
+        fd_set fds;
+        int maxfd, ret;
+
+        FD_ZERO(&fds);
+        FD_SET(t->pipe[0], &fds);
+        maxfd = t->pipe[0] + 1;
+
+        ivshmem_server_get_fds(server, &fds, &maxfd);
+
+        ret = select(maxfd, &fds, NULL, NULL, NULL);
+
+        if (ret < 0) {
+            if (errno == EINTR) {
+                continue;
+            }
+
+            g_critical("select error: %s\n", strerror(errno));
+            break;
+        }
+        if (ret == 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 = g_strdup_printf("-chardev socket,id=chr0,path=%s,nowait "
+                                "-device ivshmem,size=1M,chardev=chr0,vectors=%d",
+                                tmpserver, nvectors);
+
+    setup_vm_cmd(s, cmd, true);
+
+    g_free(cmd);
+}
+
+static GThread *thread_new(const gchar *name, GThreadFunc func, gpointer data)
+{
+    GThread *thread = NULL;
+    GError *error = NULL;
+#ifdef HAVE_THREAD_NEW
+    thread = g_thread_try_new(name, func, data, &error);
+#else
+    thread = 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 = 2;
+
+    memset(tmpshmem, 0x42, TMPSHMSIZE);
+    ret = ivshmem_server_init(&server, tmpserver, tmpshm,
+                              TMPSHMSIZE, nvectors,
+                              getenv("QTEST_LOG") != NULL);
+    g_assert_cmpint(ret, ==, 0);
+
+    ret = ivshmem_server_start(&server);
+    g_assert_cmpint(ret, ==, 0);
+
+    setup_vm_with_server(&state1, nvectors);
+    s1 = &state1;
+    setup_vm_with_server(&state2, nvectors);
+    s2 = &state2;
+
+    g_assert_cmpuint(in_IVPosition(s1), ==, 0xffffffff);
+    g_assert_cmpuint(in_IVPosition(s2), ==, 0xffffffff);
+
+    g_assert_cmpuint(qtest_readb(s1->qtest, (uintptr_t)s1->mem_base), ==, 0x00);
+
+    thread.server = &server;
+    ret = pipe(thread.pipe);
+    g_assert_cmpint(ret, ==, 0);
+    thread.thread = thread_new("ivshmem-server", server_thread, &thread);
+
+    /* waiting until mapping is done */
+    while (true) {
+        g_usleep(1000);
+
+        if (qtest_readb(s1->qtest, (uintptr_t)s1->mem_base) == 0x42 &&
+            qtest_readb(s2->qtest, (uintptr_t)s2->mem_base) == 0x42) {
+            break;
+        }
+    }
+
+    /* check got different VM ids */
+    vm1 = in_IVPosition(s1);
+    vm2 = in_IVPosition(s2);
+    g_assert_cmpuint(vm1, !=, vm2);
+
+    global_qtest = s1->qtest;
+    ret = qpci_msix_table_size(s1->dev);
+    g_assert_cmpuint(ret, ==, nvectors);
+
+    /* ping vm2 -> vm1 */
+    ret = qpci_msix_pending(s1->dev, 0);
+    g_assert_cmpuint(ret, ==, 0);
+    out_DoorBell(s2, vm1 << 16);
+    g_usleep(10000);
+    ret = qpci_msix_pending(s1->dev, 0);
+    g_assert_cmpuint(ret, !=, 0);
+
+    /* ping vm1 -> vm2 */
+    global_qtest = s2->qtest;
+    ret = qpci_msix_pending(s2->dev, 0);
+    g_assert_cmpuint(ret, ==, 0);
+    out_DoorBell(s1, vm2 << 16);
+    g_usleep(10000);
+    ret = qpci_msix_pending(s2->dev, 0);
+    g_assert_cmpuint(ret, !=, 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)
+{
+    QDict *response;
+    gchar *opts;
+
+    qtest_start("");
+
+    opts = 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 = NULL;
+    }
+
+    if (tmpshm) {
+        shm_unlink(tmpshm);
+        g_free(tmpshm);
+        tmpshm = NULL;
+    }
+
+    if (tmpserver) {
+        g_unlink(tmpserver);
+        g_free(tmpserver);
+        tmpserver = NULL;
+    }
+
+    if (tmpdir) {
+        g_rmdir(tmpdir);
+        tmpdir = NULL;
+    }
+}
+
+static void abrt_handler(void *data)
+{
+    cleanup();
+}
+
+static gchar *mktempshm(int size, int *fd)
+{
+    while (true) {
+        gchar *name;
+
+        name = g_strdup_printf("/qtest-%u-%u", getpid(), g_random_int());
+        *fd = shm_open(name, O_CREAT|O_RDWR|O_EXCL,
+                       S_IRWXU|S_IRWXG|S_IRWXO);
+        if (*fd > 0) {
+            g_assert(ftruncate(*fd, size) == 0);
+            return name;
+        }
+
+        g_free(name);
+    }
+}
+
+int main(int argc, char **argv)
+{
+    int ret, fd;
+    static gchar dir[] = "/tmp/ivshmem-test.XXXXXX";
+
+#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 = mktempshm(TMPSHMSIZE, &fd);
+    tmpshmem = mmap(0, TMPSHMSIZE, PROT_READ|PROT_WRITE, MAP_SHARED, fd, 0);
+    g_assert(tmpshmem != MAP_FAILED);
+    /* server */
+    if (g_mkdtemp_full(dir, 0700) == NULL) {
+        g_error("g_mkdtemp_full: %s", g_strerror(errno));
+    }
+    tmpdir = dir;
+    tmpserver = 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 = g_test_run();
+
+    cleanup();
+    return ret;
+}