You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
225 lines
4.6 KiB
225 lines
4.6 KiB
// SPDX-License-Identifier: GPL-2.0-or-later
|
|
/*
|
|
* Copyright (c) International Business Machines Corp., 2003
|
|
* Copyright (c) Linux Test Project, 2004-2019
|
|
*
|
|
* AUTHORS
|
|
* Kai Zhao (ltcd3@cn.ibm.com)
|
|
*/
|
|
|
|
#include "config.h"
|
|
#include "tst_test.h"
|
|
|
|
#ifdef HAVE_LIBAIO
|
|
#include <errno.h>
|
|
#include <stdlib.h>
|
|
#include <libaio.h>
|
|
|
|
#define AIO_MAXIO 32
|
|
#define AIO_BLKSIZE (64*1024)
|
|
|
|
static int wait_count = 0;
|
|
|
|
#define DESC_FLAGS_OPR(x, y) .desc = (x == IO_CMD_PWRITE ? "WRITE: " #y: "READ : " #y), \
|
|
.flags = y, .operation = x
|
|
|
|
struct testcase {
|
|
const char *desc;
|
|
int flags;
|
|
int operation;
|
|
} testcases[] = {
|
|
{
|
|
DESC_FLAGS_OPR(IO_CMD_PWRITE, O_WRONLY | O_TRUNC | O_DIRECT | O_LARGEFILE | O_CREAT),
|
|
},
|
|
{
|
|
DESC_FLAGS_OPR(IO_CMD_PREAD, O_RDONLY | O_DIRECT | O_LARGEFILE),
|
|
},
|
|
{
|
|
DESC_FLAGS_OPR(IO_CMD_PWRITE, O_RDWR | O_TRUNC),
|
|
},
|
|
{
|
|
DESC_FLAGS_OPR(IO_CMD_PREAD, O_RDWR),
|
|
},
|
|
{
|
|
DESC_FLAGS_OPR(IO_CMD_PWRITE, O_WRONLY | O_TRUNC),
|
|
},
|
|
{
|
|
DESC_FLAGS_OPR(IO_CMD_PREAD, O_RDONLY),
|
|
},
|
|
};
|
|
|
|
/*
|
|
* Fatal error handler
|
|
*/
|
|
static void io_error(const char *func, int rc)
|
|
{
|
|
if (rc == -ENOSYS)
|
|
tst_brk(TCONF, "AIO not in this kernel\n");
|
|
else if (rc < 0)
|
|
tst_brk(TFAIL, "%s: %s\n", func, strerror(-rc));
|
|
else
|
|
tst_brk(TFAIL, "%s: error %d\n", func, rc);
|
|
}
|
|
|
|
/*
|
|
* write work done
|
|
*/
|
|
static void work_done(io_context_t ctx, struct iocb *iocb, long res, long res2)
|
|
{
|
|
(void) ctx; // silence compiler warning (-Wunused)
|
|
|
|
if (res2 != 0)
|
|
io_error("aio write", res2);
|
|
|
|
if (res != (long)iocb->u.c.nbytes)
|
|
tst_brk(TFAIL, "write missed bytes expect %lu got %ld\n",
|
|
iocb->u.c.nbytes, res);
|
|
|
|
wait_count--;
|
|
}
|
|
|
|
/*
|
|
* io_wait_run() - wait for an io_event and then call the callback.
|
|
*/
|
|
static int io_wait_run(io_context_t ctx, struct timespec *to)
|
|
{
|
|
struct io_event events[AIO_MAXIO];
|
|
struct io_event *ep;
|
|
int ret, n;
|
|
|
|
/*
|
|
* get up to aio_maxio events at a time.
|
|
*/
|
|
ret = n = io_getevents(ctx, 1, AIO_MAXIO, events, to);
|
|
|
|
/*
|
|
* Call the callback functions for each event.
|
|
*/
|
|
for (ep = events; n-- > 0; ep++) {
|
|
io_callback_t cb = (io_callback_t) ep->data;
|
|
struct iocb *iocb = ep->obj;
|
|
cb(ctx, iocb, ep->res, ep->res2);
|
|
}
|
|
return ret;
|
|
}
|
|
|
|
static int io_tio(char *pathname, int flag, int operation)
|
|
{
|
|
int res, fd = 0, i = 0;
|
|
void *bufptr = NULL;
|
|
off_t offset = 0;
|
|
struct timespec timeout;
|
|
struct stat fi_stat;
|
|
size_t alignment;
|
|
|
|
io_context_t myctx;
|
|
struct iocb iocb_array[AIO_MAXIO];
|
|
struct iocb *iocbps[AIO_MAXIO];
|
|
|
|
fd = SAFE_OPEN(pathname, flag, S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH);
|
|
|
|
/* determine the alignment from the blksize of the underlying device */
|
|
SAFE_FSTAT(fd, &fi_stat);
|
|
alignment = fi_stat.st_blksize;
|
|
|
|
res = io_queue_init(AIO_MAXIO, &myctx);
|
|
|
|
for (i = 0; i < AIO_MAXIO; i++) {
|
|
|
|
switch (operation) {
|
|
case IO_CMD_PWRITE:
|
|
if (posix_memalign(&bufptr, alignment, AIO_BLKSIZE)) {
|
|
tst_brk(TBROK | TERRNO, "posix_memalign failed");
|
|
return -1;
|
|
}
|
|
memset(bufptr, 0, AIO_BLKSIZE);
|
|
|
|
io_prep_pwrite(&iocb_array[i], fd, bufptr,
|
|
AIO_BLKSIZE, offset);
|
|
io_set_callback(&iocb_array[i], work_done);
|
|
iocbps[i] = &iocb_array[i];
|
|
offset += AIO_BLKSIZE;
|
|
|
|
break;
|
|
case IO_CMD_PREAD:
|
|
if (posix_memalign(&bufptr, alignment, AIO_BLKSIZE)) {
|
|
tst_brk(TBROK | TERRNO, "posix_memalign failed");
|
|
return -1;
|
|
}
|
|
memset(bufptr, 0, AIO_BLKSIZE);
|
|
|
|
io_prep_pread(&iocb_array[i], fd, bufptr,
|
|
AIO_BLKSIZE, offset);
|
|
io_set_callback(&iocb_array[i], work_done);
|
|
iocbps[i] = &iocb_array[i];
|
|
offset += AIO_BLKSIZE;
|
|
break;
|
|
default:
|
|
tst_res(TFAIL, "Command failed; opcode returned: %d\n", operation);
|
|
return -1;
|
|
break;
|
|
}
|
|
}
|
|
|
|
do {
|
|
res = io_submit(myctx, AIO_MAXIO, iocbps);
|
|
} while (res == -EAGAIN);
|
|
|
|
if (res < 0)
|
|
io_error("io_submit tio", res);
|
|
|
|
/*
|
|
* We have submitted all the i/o requests. Wait for them to complete and
|
|
* call the callbacks.
|
|
*/
|
|
wait_count = AIO_MAXIO;
|
|
|
|
timeout.tv_sec = 30;
|
|
timeout.tv_nsec = 0;
|
|
|
|
switch (operation) {
|
|
case IO_CMD_PREAD:
|
|
case IO_CMD_PWRITE:
|
|
{
|
|
while (wait_count) {
|
|
res = io_wait_run(myctx, &timeout);
|
|
if (res < 0)
|
|
io_error("io_wait_run", res);
|
|
}
|
|
}
|
|
break;
|
|
}
|
|
|
|
SAFE_CLOSE(fd);
|
|
|
|
for (i = 0; i < AIO_MAXIO; i++)
|
|
if (iocb_array[i].u.c.buf != NULL)
|
|
free(iocb_array[i].u.c.buf);
|
|
|
|
io_queue_release(myctx);
|
|
|
|
return 0;
|
|
}
|
|
|
|
static void test_io(unsigned int n)
|
|
{
|
|
int status;
|
|
struct testcase *tc = testcases + n;
|
|
|
|
status = io_tio("file", tc->flags, tc->operation);
|
|
if (status)
|
|
tst_res(TFAIL, "%s, status = %d", tc->desc, status);
|
|
else
|
|
tst_res(TPASS, "%s", tc->desc);
|
|
}
|
|
|
|
static struct tst_test test = {
|
|
.needs_tmpdir = 1,
|
|
.test = test_io,
|
|
.tcnt = ARRAY_SIZE(testcases),
|
|
};
|
|
|
|
#else
|
|
TST_TEST_TCONF("test requires libaio and its development packages");
|
|
#endif
|