Index: tests/freebsd_test_suite/macros.h =================================================================== --- tests/freebsd_test_suite/macros.h +++ tests/freebsd_test_suite/macros.h @@ -52,6 +52,19 @@ } \ } while(0) +#define ATF_REQUIRE_OSRELDATE(_version) do { \ + int _osreldate; \ + size_t _len; \ + \ + _len = sizeof(_osreldate); \ + if (sysctlbyname("kern.osreldate", &_osreldate, &_len, NULL, \ + 0) == -1) \ + atf_libc_error(errno, "Failed to read kern.osreldate"); \ + if (_osreldate < (_version)) \ + atf_tc_skip("kernel version %d is too old (%d required)", \ + _osreldate, _version); \ +} while (0) + #define PLAIN_REQUIRE_FEATURE(_feature_name, _exit_code) do { \ if (feature_present(_feature_name) == 0) { \ printf("kernel feature (%s) not present\n", \ @@ -68,4 +81,22 @@ } \ } while(0) +#define PLAIN_REQUIRE_OSRELDATE(_version, _exit_code) do { \ + int _osreldate; \ + size_t _len; \ + \ + _len = sizeof(_osreldate); \ + if (sysctlbyname("kern.osreldate", &_osreldate, &_len, NULL, \ + 0) == -1) { \ + printf("Failed to read kern.osreldate: %s\n", \ + strerror(errno)); \ + _exit(1); \ + } \ + if (_osreldate < (_version)) { \ + printf("kernel version %d is too old (%d required)", \ + _osreldate, _version); \ + _exit(_exit_code); \ + } \ +} while (0) + #endif Index: tests/sys/aio/aio_test.c =================================================================== --- tests/sys/aio/aio_test.c +++ tests/sys/aio/aio_test.c @@ -724,6 +724,62 @@ 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"); + ATF_REQUIRE_OSRELDATE(1100101); + + 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) { @@ -734,6 +790,7 @@ 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()); }