new file mode 100644
@@ -0,0 +1,56 @@
+/*
+ * Common utilities for testing ivshmem devices
+ *
+ * SPDX-FileCopyrightText: 2012 SUSE LINUX Products GmbH
+ * SPDX-FileCopyrightText: 2021 Red Hat, Inc.
+ * SPDX-FileCopyrightText: 2023 Linaro Ltd.
+ * SPDX-License-Identifier: GPL-2.0-or-later
+ *
+ */
+
+#ifndef QTEST_IVSHMEM_UTILS_H
+#define QTEST_IVSHMEM_UTILS_H
+
+#include "qemu/osdep.h"
+#include <glib/gstdio.h>
+#include "contrib/ivshmem-server/ivshmem-server.h"
+#include "libqtest.h"
+
+enum Reg {
+ INTRMASK = 0,
+ INTRSTATUS = 4,
+ IVPOSITION = 8,
+ DOORBELL = 12,
+};
+
+enum ServerStartStatus {
+ SERVER = 1, /* Ivshmem server started */
+ THREAD = 2, /* Thread for monitoring fds created */
+ PIPE = 4, /* Pipe created */
+};
+
+typedef struct ServerThread {
+ GThread *thread;
+ IvshmemServer server;
+ /*
+ * Pipe is used to communicate with the thread, asking it to terminate on
+ * receiving 'q'.
+ */
+ int pipe[2];
+ /*
+ * Server statuses are used to keep track of thread/server/pipe start since
+ * test_ivshmem_server_stop can be called at any time on a test error,
+ * even from test_ivshmem_server_start itself, therefore, they are used for
+ * proper service termination.
+ */
+ enum ServerStartStatus status;
+} ServerThread;
+
+gchar *mktempshm(int size, int *fd);
+gchar *mktempsocket(void);
+void test_ivshmem_server_start(ServerThread *thread,
+ const char *server_socket_path,
+ const char *shm_rel_path, unsigned num_vectors);
+void test_ivshmem_server_stop(ServerThread *thread);
+
+#endif /* QTEST_IVSHMEM_UTILS_H */
@@ -3,17 +3,17 @@
*
* Copyright (c) 2014 SUSE LINUX Products GmbH
* Copyright (c) 2015 Red Hat, Inc.
+ * Copyright (c) 2023 Linaro Ltd.
*
* 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 "qemu/osdep.h"
-#include <glib/gstdio.h>
-#include "contrib/ivshmem-server/ivshmem-server.h"
+#include "ivshmem-utils.h"
#include "libqos/libqos-pc.h"
#include "libqos/libqos-spapr.h"
-#include "libqtest.h"
+
+static ServerThread thread;
#define TMPSHMSIZE (1 << 20)
static char *tmpshm;
@@ -45,13 +45,6 @@ typedef struct _IVState {
QPCIDevice *dev;
} IVState;
-enum Reg {
- INTRMASK = 0,
- INTRSTATUS = 4,
- IVPOSITION = 8,
- DOORBELL = 12,
-};
-
static const char* reg2str(enum Reg reg) {
switch (reg) {
case INTRMASK:
@@ -241,54 +234,6 @@ static void test_ivshmem_pair(void)
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;
@@ -304,27 +249,12 @@ static void setup_vm_with_server(IVState *s, int nvectors)
static void test_ivshmem_server(void)
{
- g_autoptr(GError) err = NULL;
IVState state1, state2, *s1, *s2;
- ServerThread thread;
- IvshmemServer server;
int ret, vm1, vm2;
int nvectors = 2;
guint64 end_time = g_get_monotonic_time() + 5 * G_TIME_SPAN_SECOND;
- ret = ivshmem_server_init(&server, tmpserver, tmpshm, true,
- TMPSHMSIZE, nvectors,
- g_test_verbose());
- g_assert_cmpint(ret, ==, 0);
-
- ret = ivshmem_server_start(&server);
- g_assert_cmpint(ret, ==, 0);
-
- thread.server = &server;
- g_unix_open_pipe(thread.pipe, FD_CLOEXEC, &err);
- g_assert_no_error(err);
- thread.thread = g_thread_new("ivshmem-server", server_thread, &thread);
- g_assert(thread.thread != NULL);
+ test_ivshmem_server_start(&thread, tmpserver, tmpshm, nvectors);
setup_vm_with_server(&state1, nvectors);
s1 = &state1;
@@ -367,15 +297,7 @@ static void test_ivshmem_server(void)
cleanup_vm(s2);
cleanup_vm(s1);
- if (qemu_write_full(thread.pipe[1], "q", 1) != 1) {
- g_error("qemu_write_full: %s", g_strerror(errno));
- }
-
- g_thread_join(thread.thread);
-
- ivshmem_server_close(&server);
- close(thread.pipe[1]);
- close(thread.pipe[0]);
+ test_ivshmem_server_stop(&thread);
}
static void test_ivshmem_hotplug_q35(void)
@@ -454,31 +376,10 @@ static void cleanup(void)
static void abrt_handler(void *data)
{
+ test_ivshmem_server_stop(&thread);
cleanup();
}
-static gchar *mktempshm(int size, int *fd)
-{
- while (true) {
- gchar *name;
-
- name = g_strdup_printf("/qtest-%u-%u", getpid(), g_test_rand_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);
-
- if (errno != EEXIST) {
- perror("shm_open");
- return NULL;
- }
- }
-}
-
int main(int argc, char **argv)
{
int ret, fd;
new file mode 100644
@@ -0,0 +1,155 @@
+/*
+ * Common utilities for testing ivshmem devices
+ *
+ * SPDX-FileCopyrightText: 2012 SUSE LINUX Products GmbH
+ * SPDX-FileCopyrightText: 2021 Red Hat, Inc.
+ * SPDX-FileCopyrightText: 2023 Linaro Ltd.
+ * SPDX-License-Identifier: GPL-2.0-or-later
+ *
+ */
+
+#include "ivshmem-utils.h"
+
+gchar *mktempshm(int size, int *fd)
+{
+ while (true) {
+ /* Relative path to the shm filesystem, e.g. '/dev/shm'. */
+ gchar *shm_rel_path;
+
+ shm_rel_path = g_strdup_printf("/ivshmem_qtest-%u-%u", getpid(),
+ g_test_rand_int());
+ *fd = shm_open(shm_rel_path, O_CREAT | O_RDWR | O_EXCL,
+ S_IRWXU | S_IRWXG | S_IRWXO);
+ if (*fd > 0) {
+ g_assert(ftruncate(*fd, size) == 0);
+ return shm_rel_path;
+ }
+
+ g_free(shm_rel_path);
+
+ if (errno != EEXIST) {
+ perror("shm_open");
+ return NULL;
+ }
+ }
+}
+
+gchar *mktempsocket(void)
+{
+ gchar *server_socket_path;
+
+ server_socket_path = g_strdup_printf("/tmp/ivshmem_socket_qtest-%u-%u",
+ getpid(), g_test_rand_int());
+ return server_socket_path;
+}
+
+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;
+}
+
+void test_ivshmem_server_start(ServerThread *thread,
+ const char *server_socket_path,
+ const char *shm_rel_path, unsigned num_vectors)
+{
+ g_autoptr(GError) err = NULL;
+ int ret;
+ struct stat shm_st;
+ char *shm_path;
+
+ g_assert(thread != NULL);
+ g_assert(server_socket_path != NULL);
+ g_assert_cmpint(num_vectors, >, 0);
+ g_assert(shm_rel_path != NULL);
+
+ /*
+ * Find out shm size. shm_open() deals with relative paths but stat() needs
+ * the full path to the shm file.
+ */
+ shm_path = g_strdup_printf("/dev/shm%s", shm_rel_path);
+ ret = stat(shm_path, &shm_st);
+ g_assert_cmpint(ret, ==, 0);
+ g_assert_cmpint(shm_st.st_size, >, 0);
+
+ ret = ivshmem_server_init(&thread->server, server_socket_path, shm_rel_path,
+ true, shm_st.st_size, num_vectors, g_test_verbose());
+ g_assert_cmpint(ret, ==, 0);
+ ret = ivshmem_server_start(&thread->server);
+ g_assert_cmpint(ret, ==, 0);
+ thread->status = SERVER;
+
+ g_unix_open_pipe(thread->pipe, FD_CLOEXEC, &err);
+ g_assert_no_error(err);
+ thread->status |= PIPE;
+
+ thread->thread = g_thread_new("ivshmem-server", server_thread, thread);
+ g_assert(thread->thread != NULL);
+ thread->status |= THREAD;
+}
+
+void test_ivshmem_server_stop(ServerThread *thread)
+{
+ /*
+ * This function can be called any time on a test error/abort (e.g., it can
+ * be called from the abort handler), including from the
+ * test_ivshmem_server_start(). Therefore, the start steps (server started,
+ * pipe created, and thread created) are tracked when the server starts and
+ * then checked below accordingly for proper termination.
+ */
+
+ if (thread->status & THREAD) {
+ /* Ask to exit from thread. */
+ if (qemu_write_full(thread->pipe[1], "q", 1) != 1) {
+ g_error("qemu_write_full: %s", g_strerror(errno));
+ }
+
+ /* Wait thread to exit. */
+ g_thread_join(thread->thread);
+ }
+
+ if (thread->status & PIPE) {
+ close(thread->pipe[1]);
+ close(thread->pipe[0]);
+ }
+
+ if (thread->status & SERVER) {
+ ivshmem_server_close(&thread->server);
+ }
+}
@@ -59,9 +59,9 @@ qtests_i386 = \
(config_all_devices.has_key('CONFIG_PVPANIC_ISA') ? ['pvpanic-test'] : []) + \
(config_all_devices.has_key('CONFIG_PVPANIC_PCI') ? ['pvpanic-pci-test'] : []) + \
(config_all_devices.has_key('CONFIG_HDA') ? ['intel-hda-test'] : []) + \
- (config_all_devices.has_key('CONFIG_I82801B11') ? ['i82801b11-test'] : []) + \
+ (config_all_devices.has_key('CONFIG_I82801B11') ? ['i82801b11-test'] : []) + \
(config_all_devices.has_key('CONFIG_IOH3420') ? ['ioh3420-test'] : []) + \
- (config_all_devices.has_key('CONFIG_LPC_ICH9') ? ['lpc-ich9-test'] : []) + \
+ (config_all_devices.has_key('CONFIG_LPC_ICH9') ? ['lpc-ich9-test'] : []) + \
(config_all_devices.has_key('CONFIG_USB_UHCI') ? ['usb-hcd-uhci-test'] : []) + \
(config_all_devices.has_key('CONFIG_USB_UHCI') and \
config_all_devices.has_key('CONFIG_USB_EHCI') ? ['usb-hcd-ehci-test'] : []) + \
@@ -319,7 +319,7 @@ qtests = {
'cdrom-test': files('boot-sector.c'),
'dbus-vmstate-test': files('migration-helpers.c') + dbus_vmstate1,
'erst-test': files('erst-test.c'),
- 'ivshmem-test': [rt, '../../contrib/ivshmem-server/ivshmem-server.c'],
+ 'ivshmem-test': ['ivshmem-utils.c', '../../contrib/ivshmem-server/ivshmem-server.c'],
'migration-test': migration_files,
'pxe-test': files('boot-sector.c'),
'qos-test': [chardev, io, qos_test_ss.apply({}).sources()],