svn commit: r300626 - in head: sys/kern tests/sys/aio
John Baldwin
jhb at FreeBSD.org
Tue May 24 21:09:06 UTC 2016
Author: jhb
Date: Tue May 24 21:09:05 2016
New Revision: 300626
URL: https://svnweb.freebsd.org/changeset/base/300626
Log:
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
Modified:
head/sys/kern/sys_socket.c
head/tests/sys/aio/aio_test.c
Modified: head/sys/kern/sys_socket.c
==============================================================================
--- head/sys/kern/sys_socket.c Tue May 24 20:06:41 2016 (r300625)
+++ head/sys/kern/sys_socket.c Tue May 24 21:09:05 2016 (r300626)
@@ -721,6 +721,7 @@ soo_aio_cancel(struct kaiocb *job)
{
struct socket *so;
struct sockbuf *sb;
+ long done;
int opcode;
so = job->fd_file->f_data;
@@ -739,7 +740,11 @@ soo_aio_cancel(struct kaiocb *job)
sb->sb_flags &= ~SB_AIO;
SOCKBUF_UNLOCK(sb);
- aio_cancel(job);
+ done = job->uaiocb._aiocb_private.status;
+ if (done != 0)
+ aio_complete(job, done, 0);
+ else
+ aio_cancel(job);
}
static int
Modified: head/tests/sys/aio/aio_test.c
==============================================================================
--- head/tests/sys/aio/aio_test.c Tue May 24 20:06:41 2016 (r300625)
+++ head/tests/sys/aio/aio_test.c Tue May 24 21:09:05 2016 (r300626)
@@ -845,6 +845,74 @@ ATF_TC_BODY(aio_socket_blocking_short_wr
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());
}
More information about the svn-src-head
mailing list