#include <atf-c.h>
#include <aio.h>
#include <errno.h>
#include <fcntl.h>
#include <signal.h>
#include <stdint.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/stat.h>
#include <sys/types.h>
#include <unistd.h>
static int mktemp_file(char *, size_t);
static void fill_pattern(uint8_t *, size_t, uint8_t);
static void wait_all(const struct aiocb * const [], size_t);
static int
mktemp_file(char *path, size_t pathlen)
{
int fd, n;
n = snprintf(path, pathlen, "t_aio_lio.XXXXXX");
ATF_REQUIRE(n > 0 && (size_t)n < pathlen);
fd = mkstemp(path);
ATF_REQUIRE(fd >= 0);
return fd;
}
static void
fill_pattern(uint8_t *buf, size_t len, uint8_t seed)
{
size_t i;
for (i = 0; i < len; i++) {
buf[i] = (uint8_t)(seed + (i & 0xff));
}
}
static void
wait_all(const struct aiocb * const list[], size_t nent)
{
size_t i;
int pending, rv;
for (;;) {
pending = 0;
for (i = 0; i < nent; i++) {
int err;
if (list[i] == NULL) {
continue;
}
err = aio_error(list[i]);
if (err == EINPROGRESS) {
pending = 1;
}
}
if (!pending) {
break;
}
rv = aio_suspend(list, (int)nent, NULL);
ATF_REQUIRE_EQ_MSG(0, rv, "aio_suspend failed: %s",
strerror(errno));
}
}
ATF_TC_WITHOUT_HEAD(lio_nowait);
ATF_TC_BODY(lio_nowait, tc)
{
char path[64];
int fd, rv;
#define NW_REQS 8
#define NW_BLKSIZ 8192
uint8_t *bufs[NW_REQS];
struct aiocb cbs[NW_REQS];
struct aiocb *list[NW_REQS];
off_t off;
size_t i;
fd = mktemp_file(path, sizeof(path));
off = 0;
for (i = 0; i < NW_REQS; i++) {
bufs[i] = malloc(NW_BLKSIZ);
ATF_REQUIRE(bufs[i] != NULL);
fill_pattern(bufs[i], NW_BLKSIZ, (uint8_t)i);
memset(&cbs[i], 0, sizeof(cbs[i]));
cbs[i].aio_fildes = fd;
cbs[i].aio_buf = bufs[i];
cbs[i].aio_nbytes = NW_BLKSIZ;
cbs[i].aio_offset = off;
cbs[i].aio_lio_opcode = LIO_WRITE;
list[i] = &cbs[i];
off += (off_t)NW_BLKSIZ;
}
rv = lio_listio(LIO_NOWAIT, list, (int)NW_REQS, NULL);
ATF_REQUIRE_EQ_MSG(0, rv, "lio_listio failed: %s",
strerror(errno));
wait_all((const struct aiocb * const *)list, NW_REQS);
for (i = 0; i < NW_REQS; i++) {
int err;
ssize_t done;
err = aio_error(&cbs[i]);
ATF_REQUIRE_EQ(0, err);
done = aio_return(&cbs[i]);
ATF_REQUIRE_EQ(NW_BLKSIZ, done);
free(bufs[i]);
}
rv = close(fd);
ATF_REQUIRE_EQ(0, rv);
rv = unlink(path);
ATF_REQUIRE_EQ(0, rv);
}
ATF_TC_WITHOUT_HEAD(lio_wait_write_then_read);
ATF_TC_BODY(lio_wait_write_then_read, tc)
{
char path[64];
int fd, rv;
#define WWTR_REQS 4
#define WWTR_BLKSIZ 4096
uint8_t *wbufs[WWTR_REQS];
struct aiocb wcbs[WWTR_REQS];
struct aiocb *wlist[WWTR_REQS];
uint8_t *rbufs[WWTR_REQS];
struct aiocb rcbs[WWTR_REQS];
struct aiocb *rlist[WWTR_REQS];
size_t i;
off_t off;
fd = mktemp_file(path, sizeof(path));
off = 0;
for (i = 0; i < WWTR_REQS; i++) {
wbufs[i] = malloc(WWTR_BLKSIZ);
ATF_REQUIRE(wbufs[i] != NULL);
fill_pattern(wbufs[i], WWTR_BLKSIZ, (uint8_t)(0xA0 + i));
memset(&wcbs[i], 0, sizeof(wcbs[i]));
wcbs[i].aio_fildes = fd;
wcbs[i].aio_buf = wbufs[i];
wcbs[i].aio_nbytes = WWTR_BLKSIZ;
wcbs[i].aio_offset = off;
wcbs[i].aio_lio_opcode = LIO_WRITE;
wlist[i] = &wcbs[i];
off += WWTR_BLKSIZ;
}
rv = lio_listio(LIO_WAIT, wlist, (int)WWTR_REQS, NULL);
ATF_REQUIRE_EQ_MSG(0, rv, "lio_listio write failed: %s",
strerror(errno));
for (i = 0; i < WWTR_REQS; i++) {
int err;
ssize_t done;
err = aio_error(&wcbs[i]);
ATF_REQUIRE_EQ(0, err);
done = aio_return(&wcbs[i]);
ATF_REQUIRE_EQ(WWTR_BLKSIZ, done);
}
for (i = 0; i < WWTR_REQS; i++) {
rbufs[i] = calloc(1, WWTR_BLKSIZ);
ATF_REQUIRE(rbufs[i] != NULL);
memset(&rcbs[i], 0, sizeof(rcbs[i]));
rcbs[i].aio_fildes = fd;
rcbs[i].aio_buf = rbufs[i];
rcbs[i].aio_nbytes = WWTR_BLKSIZ;
rcbs[i].aio_offset = (off_t)i * WWTR_BLKSIZ;
rcbs[i].aio_lio_opcode = LIO_READ;
rlist[i] = &rcbs[i];
}
rv = lio_listio(LIO_NOWAIT, rlist, WWTR_REQS, NULL);
ATF_REQUIRE_EQ_MSG(0, rv, "lio_listio read failed: %s",
strerror(errno));
wait_all((const struct aiocb * const *)rlist, WWTR_REQS);
for (i = 0; i < WWTR_REQS; i++) {
int err;
ssize_t done;
err = aio_error(&rcbs[i]);
ATF_REQUIRE_EQ(0, err);
done = aio_return(&rcbs[i]);
ATF_REQUIRE_EQ(WWTR_BLKSIZ, done);
ATF_REQUIRE_EQ(0, memcmp(wbufs[i], rbufs[i], WWTR_BLKSIZ));
free(wbufs[i]);
free(rbufs[i]);
}
rv = close(fd);
ATF_REQUIRE_EQ(0, rv);
rv = unlink(path);
ATF_REQUIRE_EQ(0, rv);
}
ATF_TP_ADD_TCS(tp)
{
ATF_TP_ADD_TC(tp, lio_nowait);
ATF_TP_ADD_TC(tp, lio_wait_write_then_read);
return atf_no_error();
}