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
This commit is contained in:
parent
3e61cae63a
commit
f913b0e3d5
@ -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
|
||||
|
@ -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());
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user