From e1f100f1fbe0285c7efdfc30f38bece766e59efe Mon Sep 17 00:00:00 2001 From: Amit Shah Date: Mon, 23 Aug 2010 13:55:16 +0530 Subject: auto-test: Introduce a threaded read/write test Introduce a test that creates a new thread and blocks on a port for read. The parent thread then writes to the port, and later the host writes something to the port, unblocking the read. Signed-off-by: Amit Shah --- Makefile | 1 + auto-virtserial-guest.c | 55 ++++++++++++++++++++++++++++++ auto-virtserial.c | 90 +++++++++++++++++++++++++++++++++++++++++++++++++ virtserial.h | 2 ++ 4 files changed, 148 insertions(+) diff --git a/Makefile b/Makefile index 4cf5dab..6df0a67 100644 --- a/Makefile +++ b/Makefile @@ -1,4 +1,5 @@ CFLAGS=-Wall -g +LDFLAGS=-lpthread all: test-virtserial auto-virtserial auto-virtserial-guest diff --git a/auto-virtserial-guest.c b/auto-virtserial-guest.c index eebf1f0..2c84c27 100644 --- a/auto-virtserial-guest.c +++ b/auto-virtserial-guest.c @@ -24,6 +24,7 @@ #include #include #include +#include #include #include #include @@ -44,6 +45,8 @@ static int g_bigfile_fd; static int g_length; /* The 'name' field in the sysfs port info */ static char g_sysfs_name[1024]; +/* The thread that performs a blocking read operation */ +static pthread_t g_read_thread_id; static ssize_t safewrite(int fd, const void *buf, size_t count) @@ -413,6 +416,50 @@ out: return ret; } +static void *t_blocked_read(void *arg) +{ + int port_nr = *(int *)arg; + int *ret; + + ret = malloc(sizeof(int)); + if (!ret) + return NULL; + + *ret = read_port(port_nr); + + return ret; +} + +static int create_read_thread(int nr) +{ + int ret; + + ret = pthread_create(&g_read_thread_id, NULL, &t_blocked_read, &nr); + if (ret) + return -errno; + + /* Give a chance to the thread to start and block */ + sleep(2); + + return 0; +} + +static int join_read_thread(int nr) +{ + int ret; + void *ret2; + + ret = pthread_join(g_read_thread_id, &ret2); + if (ret) + return ret; + + ret = *(int *)ret2; + + free(ret2); + + return ret; +} + static void send_report(int cfd, int ret) { struct guest_packet gpkt; @@ -539,6 +586,14 @@ back_to_open: ret = send_guest_csum(gpkt.value); send_report(cfd, ret); break; + case KEY_CREATE_READ_THREAD: + ret = create_read_thread(gpkt.value); + send_report(cfd, ret); + break; + case KEY_JOIN_READ_THREAD: + ret = join_read_thread(gpkt.value); + send_report(cfd, ret); + break; } } return 0; diff --git a/auto-virtserial.c b/auto-virtserial.c index 9503f74..f04a834 100644 --- a/auto-virtserial.c +++ b/auto-virtserial.c @@ -248,6 +248,24 @@ static int guest_open_guest_bigfile(int value) return guest_cmd(&gpkt); } +static int guest_create_read_thread(int nr) +{ + struct guest_packet gpkt; + + gpkt.key = KEY_CREATE_READ_THREAD; + gpkt.value = nr; + return guest_cmd(&gpkt); +} + +static int guest_join_read_thread(int nr) +{ + struct guest_packet gpkt; + + gpkt.key = KEY_JOIN_READ_THREAD; + gpkt.value = nr; + return guest_cmd(&gpkt); +} + static void show_stats(void) { fprintf(stderr, "-----\n"); @@ -1095,6 +1113,71 @@ out_close: return err; } +/* + * Tests guest's ability to be blocked on read in one thread while + * writing to the same chardev in another thread + * + * - Open guest port + * - Call threaded test + * - Wait for POLLOUT from guest for a specific timeout + * - Collect result. + * + * If POLLOUT doesn't get set, that means the test failed and the + * guest is stuck in a blocking read anyway. So this should be the + * last test to run. + */ +static int test_threaded_read_write(int nr) +{ + struct pollfd pollfd[1]; + struct guest_packet gpkt; + int err, ret; + + ret = guest_open_port(nr); + err = result(__func__, true, "open", ret, -1, -1, OP_GT, false); + if (err) + return err; + + host_connect_chardev(nr); + + guest_set_length(nr, sizeof(gpkt)); + + ret = guest_create_read_thread(nr); + err = result(__func__, true, "create thread", + ret, -1, -1, OP_GT, false); + if (err) + goto out; + + ret = guest_write(nr, BUF_LENGTH); + err = result(__func__, chardevs[nr].caching, "guest_write", + ret, BUF_LENGTH, BUF_LENGTH, OP_EQ, false); + if (err) + goto out; + + pollfd[0].fd = chardevs[nr].sock; + pollfd[0].events = POLLOUT; + /* Wait for 5s to see if guest writes out something */ + ret = poll(pollfd, 1, 5000); + if (ret == -1) + error(errno, errno, "%s: poll\n", __func__); + if (!(pollfd[0].revents & POLLOUT)) + error(EIO, EIO, "%s: no response from guest\n", __func__); + + /* + * Write out something - doesn't matter what. This should make + * the guest read thread unblock and return. + */ + write(chardevs[nr].sock, &gpkt, sizeof(gpkt)); + + ret = guest_join_read_thread(nr); + err = result(__func__, true, "read", ret, + sizeof(gpkt), sizeof(gpkt), OP_EQ, true); + + host_close_chardev(nr); +out: + guest_close_port(nr); + return err; +} + enum { TEST_OPEN = 0, TEST_CLOSE, @@ -1113,6 +1196,7 @@ enum { TEST_H_FILE_SEND, TEST_G_FILE_SEND, TEST_CONSOLE, + TEST_THREADED_READ_WRITE, TEST_END }; @@ -1189,6 +1273,10 @@ static struct test_parameters { .test_function = test_console, .needs_guestok = false, }, + { + .test_function = test_threaded_read_write, + .needs_guestok = true, + }, }; static void post_test_cleanup(int nr) @@ -1329,6 +1417,8 @@ static int start_tests(void) /* The console test should work in any case. */ run_test(TEST_CONSOLE, 0); + run_test(TEST_THREADED_READ_WRITE, 2); + return 0; } diff --git a/virtserial.h b/virtserial.h index 6ec37ab..298fcb5 100644 --- a/virtserial.h +++ b/virtserial.h @@ -16,6 +16,8 @@ #define KEY_OPEN_GUEST_BIGFILE 16 #define KEY_GUEST_BYTESTREAM 17 #define KEY_GUEST_CSUM 18 +#define KEY_CREATE_READ_THREAD 19 +#define KEY_JOIN_READ_THREAD 20 #define HOST_BIG_FILE "/tmp/amit/host-big-file" #define GUEST_BIG_FILE "/tmp/amit/guest-big-file" -- cgit