diff options
author | Amit Shah <amit.shah@redhat.com> | 2010-08-25 19:00:53 +0530 |
---|---|---|
committer | Amit Shah <amit.shah@redhat.com> | 2010-08-26 09:43:22 +0530 |
commit | f50f751a4980447a465e5039272ae0b0d8708aed (patch) | |
tree | c77458b29b629c9596e2c9b5af05ce660d393f9e | |
parent | d99d351e8b683835b43060b562fd68163902d89a (diff) | |
download | test-virtserial-f50f751a4980447a465e5039272ae0b0d8708aed.tar.gz test-virtserial-f50f751a4980447a465e5039272ae0b0d8708aed.tar.xz test-virtserial-f50f751a4980447a465e5039272ae0b0d8708aed.zip |
auto-virtserial: Add guest SIGIO tests
The guest can receive the SIGIO signal on host connection and
disconnection events. This commit just adds a simple test for one
open/close event. A lot more are to be added, as explained in the
comments.
Signed-off-by: Amit Shah <amit.shah@redhat.com>
-rw-r--r-- | auto-virtserial-guest.c | 110 | ||||
-rw-r--r-- | auto-virtserial.c | 57 | ||||
-rw-r--r-- | virtserial.h | 1 |
3 files changed, 166 insertions, 2 deletions
diff --git a/auto-virtserial-guest.c b/auto-virtserial-guest.c index cef1d26..ce3e6e5 100644 --- a/auto-virtserial-guest.c +++ b/auto-virtserial-guest.c @@ -36,6 +36,7 @@ #include "virtserial.h" #define CONTROL_PORT "/dev/vport0p1" +#define MAX_PORTS 10 /* The fd to work with for read / write requests. Set by the open message */ static int g_fd; @@ -47,7 +48,10 @@ static int g_length; static char g_sysfs_name[1024]; /* The thread that performs a blocking read operation */ static pthread_t g_read_thread_id; - +/* Array to hold fds of all open ports. Used for polling host connect events */ +static int g_open_fds[MAX_PORTS]; +/* Array to hold poll results of g_open_fds from the sigio_handler() */ +static int g_poll_results[MAX_PORTS]; static ssize_t safewrite(int fd, const void *buf, size_t count) { @@ -148,7 +152,7 @@ static char *get_port_dev(unsigned int nr) static int open_port(int nr) { char *buf; - int fd; + int fd, ret; buf = get_port_dev(nr); if (!buf) @@ -159,6 +163,19 @@ static int open_port(int nr) if (fd == -1) return -errno; g_fd = fd; + + ret = fcntl(fd, F_SETOWN, getpid()); + if (ret < 0) { + perror("F_SETOWN"); + } + ret = fcntl(fd, F_GETFL); + ret = fcntl(fd, F_SETFL, ret | O_ASYNC); + if (ret < 0) { + perror("F_SETFL"); + } + + if (nr < MAX_PORTS) + g_open_fds[nr] = fd; return fd; } @@ -220,6 +237,11 @@ static int close_port(int nr) if (ret < 0) ret = -errno; g_length = 0; + + if (nr < MAX_PORTS) { + g_open_fds[nr] = -1; + g_poll_results[nr] = 0; + } return ret; } @@ -462,6 +484,76 @@ static int join_read_thread(int nr) return ret; } +static int get_port_from_fd(int fd) +{ + unsigned int i; + + for (i = 0; i < MAX_PORTS; i++) { + if (g_open_fds[i] == fd) + return i; + } + return -1; +} + +static void sigio_handler(int signal) +{ + struct pollfd pollfds[MAX_PORTS]; + unsigned int i, j; + int ret; + + for (i = 0, j = 0; i < MAX_PORTS; i++) { + if (g_open_fds[i] > -1) { + pollfds[j].fd = g_open_fds[i]; + pollfds[j++].events = POLLIN|POLLOUT; + } + } + ret = safepoll(pollfds, j, 0); + if (ret == -1) { + for (i = 0; i < MAX_PORTS; i++) + g_poll_results[i] = 0; + return; + } + + for (i = 0; i < j; i++) { + int port; + + port = get_port_from_fd(pollfds[i].fd); + if (port == -1) + continue; + g_poll_results[port] = pollfds[i].revents; + } +} + +static int install_sigio_handler(void) +{ + struct sigaction action; + unsigned int i; + int ret; + + action.sa_handler = sigio_handler; + action.sa_flags = 0; + ret = sigemptyset(&action.sa_mask); + if (ret) { + ret = -errno; + goto out; + } + + ret = sigaction(SIGIO, &action, NULL); + if (ret) + ret = -errno; + +out: + for (i = 0; i < MAX_PORTS; i++) + g_poll_results[i] = 0; + + return ret; +} + +static int get_sigio_result(int nr) +{ + return g_poll_results[nr]; +} + static void send_report(int cfd, int ret) { struct guest_packet gpkt; @@ -475,6 +567,7 @@ int main(int argc, char *argv[]) { struct guest_packet gpkt; struct pollfd pollfd[1]; + unsigned int i; int ret, cfd; /* @@ -486,6 +579,12 @@ int main(int argc, char *argv[]) */ spawn_console(0); + /* + * Have to install it right away. Else we'll start getting + * SIGIOs and the default action is to terminate the process. + */ + install_sigio_handler(); + ret = access(CONTROL_PORT, R_OK|W_OK); if (ret == -1) error(errno, errno, "No control port found %s", CONTROL_PORT); @@ -501,6 +600,9 @@ back_to_open: if (ret < 0) error(-ret, -ret, "write control port"); + for (i = 0; i < MAX_PORTS; i++) + g_open_fds[i] = -1; + pollfd[0].fd = cfd; pollfd[0].events = POLLIN; @@ -596,6 +698,10 @@ back_to_open: ret = join_read_thread(gpkt.value); send_report(cfd, ret); break; + case KEY_GET_SIGIO_RESULT: + ret = get_sigio_result(gpkt.value); + send_report(cfd, ret); + break; default: send_report(cfd, -ERANGE); break; diff --git a/auto-virtserial.c b/auto-virtserial.c index 260825b..811bfd5 100644 --- a/auto-virtserial.c +++ b/auto-virtserial.c @@ -266,6 +266,15 @@ static int guest_join_read_thread(int nr) return guest_cmd(&gpkt); } +static int guest_get_sigio_poll_result(int nr) +{ + struct guest_packet gpkt; + + gpkt.key = KEY_GET_SIGIO_RESULT; + gpkt.value = nr; + return guest_cmd(&gpkt); +} + static void show_stats(void) { fprintf(stderr, "-----\n"); @@ -1205,6 +1214,48 @@ out: return err; } +/* + * The virtio_console guest kernel driver sends a SIGIO each time the + * host-side connection status is changed (connected, disconnected) to + * a process that has a virtserial port open. + * + * Tests: + * - open a port in the guest, connect host chardev. Should receive SIGIO. + * - open a port in host, then open in guest, disconnect host chardev. + * - write from the host to a guest port. Should receive SIGIO. + * - open multiple ports in guest, open corresponding host chardevs. + * - similar to above for host chardev disconnects + * - let the guest poll on some fd, then cause SIGIO. Make sure prev. poll + * works fine. + */ +static int test_sigio_handler(int nr) +{ + int ret, err; + + guest_open_port(nr); + /* Give time to the guest to schedule in and perform the open() */ + sleep(2); + + host_connect_chardev(nr); + + /* Give time to the guest to schedule in and to receive the signal */ + sleep(2); + + ret = guest_get_sigio_poll_result(nr); + err = result(__func__, true, "open", + ret, POLLOUT, POLLOUT, OP_EQ, true); + + host_close_chardev(nr); + sleep(2); + + ret = guest_get_sigio_poll_result(nr); + err = result(__func__, true, "close", + ret, POLLHUP, POLLHUP, OP_EQ, true); + + guest_close_port(nr); + return err; +} + enum { TEST_OPEN = 0, TEST_CLOSE, @@ -1224,6 +1275,7 @@ enum { TEST_G_FILE_SEND, TEST_CONSOLE, TEST_THREADED_READ_WRITE, + TEST_SIGIO_HANDLER, TEST_END }; @@ -1304,6 +1356,10 @@ static struct test_parameters { .test_function = test_threaded_read_write, .needs_guestok = true, }, + { + .test_function = test_sigio_handler, + .needs_guestok = true, + }, }; static void post_test_cleanup(int nr) @@ -1446,6 +1502,7 @@ static int start_tests(void) run_test(TEST_THREADED_READ_WRITE, 2); + run_test(TEST_SIGIO_HANDLER, 2); return 0; } diff --git a/virtserial.h b/virtserial.h index 298fcb5..45857ad 100644 --- a/virtserial.h +++ b/virtserial.h @@ -18,6 +18,7 @@ #define KEY_GUEST_CSUM 18 #define KEY_CREATE_READ_THREAD 19 #define KEY_JOIN_READ_THREAD 20 +#define KEY_GET_SIGIO_RESULT 21 #define HOST_BIG_FILE "/tmp/amit/host-big-file" #define GUEST_BIG_FILE "/tmp/amit/guest-big-file" |