aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorJohn Baldwin <jhb@FreeBSD.org>2016-04-16 00:01:16 +0000
committerJohn Baldwin <jhb@FreeBSD.org>2016-04-16 00:01:16 +0000
commitd1ad1a7394330d70efc2f2c58862a02dd8900acf (patch)
tree6170830cb5b37234b5381b6e485d109455b90c41
parent80c7cc1c8f027fcf5d5f0a2df4b9aef6904ed079 (diff)
downloadsrc-d1ad1a7394330d70efc2f2c58862a02dd8900acf.tar.gz
src-d1ad1a7394330d70efc2f2c58862a02dd8900acf.zip
Add a test for cancelling an active AIO request on a socket.
The older AIO code awakened all pending AIO requests on a socket when any data arrived. This could result in AIO daemons blocking on an empty socket buffer. These requests could not be cancelled which led to a deadlock during process exit. This test reproduces this case. The newer AIO code is able to cancel the pending AIO request correctly. Reviewed by: ngie (-ish) Sponsored by: Chelsio Communications Differential Revision: https://reviews.freebsd.org/D4363
Notes
Notes: svn path=/head/; revision=298090
-rw-r--r--tests/sys/aio/aio_test.c60
1 files changed, 60 insertions, 0 deletions
diff --git a/tests/sys/aio/aio_test.c b/tests/sys/aio/aio_test.c
index 5515161ee489..1b1008829352 100644
--- a/tests/sys/aio/aio_test.c
+++ b/tests/sys/aio/aio_test.c
@@ -722,6 +722,65 @@ finished:
close(fd);
}
+/*
+ * This tests for a bug where arriving socket data can wakeup multiple
+ * AIO read requests resulting in an uncancellable request.
+ */
+ATF_TC_WITHOUT_HEAD(aio_socket_two_reads);
+ATF_TC_BODY(aio_socket_two_reads, tc)
+{
+ struct ioreq {
+ struct aiocb iocb;
+ char buffer[1024];
+ } ioreq[2];
+ struct aiocb *iocb;
+ unsigned i;
+ int s[2];
+ char c;
+
+ ATF_REQUIRE_KERNEL_MODULE("aio");
+#if __FreeBSD_version < 1100101
+ aft_tc_skip("kernel version %d is too old (%d required)",
+ __FreeBSD_version, 1100101);
+#endif
+
+ ATF_REQUIRE(socketpair(PF_UNIX, SOCK_STREAM, 0, s) != -1);
+
+ /* Queue two read requests. */
+ memset(&ioreq, 0, sizeof(ioreq));
+ for (i = 0; i < nitems(ioreq); i++) {
+ ioreq[i].iocb.aio_nbytes = sizeof(ioreq[i].buffer);
+ ioreq[i].iocb.aio_fildes = s[0];
+ ioreq[i].iocb.aio_buf = ioreq[i].buffer;
+ ATF_REQUIRE(aio_read(&ioreq[i].iocb) == 0);
+ }
+
+ /* Send a single byte. This should complete one request. */
+ c = 0xc3;
+ ATF_REQUIRE(write(s[1], &c, sizeof(c)) == 1);
+
+ ATF_REQUIRE(aio_waitcomplete(&iocb, NULL) == 1);
+
+ /* Determine which request completed and verify the data was read. */
+ if (iocb == &ioreq[0].iocb)
+ i = 0;
+ else
+ i = 1;
+ ATF_REQUIRE(ioreq[i].buffer[0] == c);
+
+ i ^= 1;
+
+ /*
+ * Try to cancel the other request. On broken systems this
+ * will fail and the process will hang on exit.
+ */
+ ATF_REQUIRE(aio_error(&ioreq[i].iocb) == EINPROGRESS);
+ ATF_REQUIRE(aio_cancel(s[0], &ioreq[i].iocb) == AIO_CANCELED);
+
+ close(s[1]);
+ close(s[0]);
+}
+
ATF_TP_ADD_TCS(tp)
{
@@ -732,6 +791,7 @@ ATF_TP_ADD_TCS(tp)
ATF_TP_ADD_TC(tp, aio_pipe_test);
ATF_TP_ADD_TC(tp, aio_md_test);
ATF_TP_ADD_TC(tp, aio_large_read_test);
+ ATF_TP_ADD_TC(tp, aio_socket_two_reads);
return (atf_no_error());
}