summaryrefslogtreecommitdiffstats
path: root/tests
diff options
context:
space:
mode:
authorjhb <jhb@FreeBSD.org>2016-05-24 21:09:05 +0000
committerjhb <jhb@FreeBSD.org>2016-05-24 21:09:05 +0000
commitf913b0e3d54d3521ae7eb9f7b68ec2c4d357eb55 (patch)
treee8c4f47accb21440768f43e2a8cf680e6a3f2710 /tests
parent3e61cae63a0cae47555a546715abf14de1d21520 (diff)
downloadFreeBSD-src-f913b0e3d54d3521ae7eb9f7b68ec2c4d357eb55.zip
FreeBSD-src-f913b0e3d54d3521ae7eb9f7b68ec2c4d357eb55.tar.gz
Return the correct status when a partially completed request is cancelled.
After the previous changes to fix requests on blocking sockets to complete across multiple operations, an edge case exists where a request can be cancelled after it has partially completed. POSIX doesn't appear to dictate exactly how to handle this case, but in general I feel that aio_cancel() should arrange to cancel any request it can, but that any partially completed requests should return a partial completion rather than ECANCELED. To that end, fix the socket AIO cancellation routine to return a short read/write if a partially completed request is cancelled rather than ECANCELED. Sponsored by: Chelsio Communications
Diffstat (limited to 'tests')
-rw-r--r--tests/sys/aio/aio_test.c69
1 files changed, 69 insertions, 0 deletions
diff --git a/tests/sys/aio/aio_test.c b/tests/sys/aio/aio_test.c
index c81b8d6..0a3e3ab 100644
--- a/tests/sys/aio/aio_test.c
+++ b/tests/sys/aio/aio_test.c
@@ -845,6 +845,74 @@ ATF_TC_BODY(aio_socket_blocking_short_write, tc)
close(s[0]);
}
+/*
+ * This test verifies that cancelling a partially completed socket write
+ * returns a short write rather than ECANCELED.
+ */
+ATF_TC_WITHOUT_HEAD(aio_socket_short_write_cancel);
+ATF_TC_BODY(aio_socket_short_write_cancel, tc)
+{
+ struct aiocb iocb, *iocbp;
+ char *buffer[2];
+ ssize_t done;
+ int buffer_size, sb_size;
+ socklen_t len;
+ int s[2];
+
+ ATF_REQUIRE_KERNEL_MODULE("aio");
+
+ ATF_REQUIRE(socketpair(PF_UNIX, SOCK_STREAM, 0, s) != -1);
+
+ len = sizeof(sb_size);
+ ATF_REQUIRE(getsockopt(s[0], SOL_SOCKET, SO_RCVBUF, &sb_size, &len) !=
+ -1);
+ ATF_REQUIRE(len == sizeof(sb_size));
+ buffer_size = sb_size;
+
+ ATF_REQUIRE(getsockopt(s[1], SOL_SOCKET, SO_SNDBUF, &sb_size, &len) !=
+ -1);
+ ATF_REQUIRE(len == sizeof(sb_size));
+ if (sb_size > buffer_size)
+ buffer_size = sb_size;
+
+ /*
+ * Use three times the size of the MAX(receive buffer, send
+ * buffer) for the write to ensure that the write is split up
+ * into multiple writes internally. The recv() ensures that
+ * the write has partially completed, but a remaining size of
+ * two buffers should ensure that the write has not completed
+ * fully when it is cancelled.
+ */
+ buffer[0] = malloc(buffer_size);
+ ATF_REQUIRE(buffer[0] != NULL);
+ buffer[1] = malloc(buffer_size * 3);
+ ATF_REQUIRE(buffer[1] != NULL);
+
+ srandomdev();
+ aio_fill_buffer(buffer[1], buffer_size * 3, random());
+
+ memset(&iocb, 0, sizeof(iocb));
+ iocb.aio_fildes = s[1];
+ iocb.aio_buf = buffer[1];
+ iocb.aio_nbytes = buffer_size * 3;
+ ATF_REQUIRE(aio_write(&iocb) == 0);
+
+ done = recv(s[0], buffer[0], buffer_size, MSG_WAITALL);
+ ATF_REQUIRE(done == buffer_size);
+
+ ATF_REQUIRE(aio_error(&iocb) == EINPROGRESS);
+ ATF_REQUIRE(aio_cancel(s[1], &iocb) == AIO_NOTCANCELED);
+
+ done = aio_waitcomplete(&iocbp, NULL);
+ ATF_REQUIRE(iocbp == &iocb);
+ ATF_REQUIRE(done >= buffer_size && done <= buffer_size * 2);
+
+ ATF_REQUIRE(memcmp(buffer[0], buffer[1], buffer_size) == 0);
+
+ close(s[1]);
+ close(s[0]);
+}
+
ATF_TP_ADD_TCS(tp)
{
@@ -857,6 +925,7 @@ ATF_TP_ADD_TCS(tp)
ATF_TP_ADD_TC(tp, aio_large_read_test);
ATF_TP_ADD_TC(tp, aio_socket_two_reads);
ATF_TP_ADD_TC(tp, aio_socket_blocking_short_write);
+ ATF_TP_ADD_TC(tp, aio_socket_short_write_cancel);
return (atf_no_error());
}
OpenPOWER on IntegriCloud