blob: 911f27230b95c4a70acf6d11ebe17691fa9a229b [file] [log] [blame]
// SPDX-License-Identifier: GPL-2.0
/*
* Copyright (c) 2013 Alibaba Group.
* Copyright (c) 2017 Red Hat Inc.
* All Rights Reserved.
*
* This is a normal case that we do some append dio writes and meanwhile
* we do some dio reads. Currently in vfs we don't ensure that i_size
* is updated properly. Hence the reader will read some data with '0'.
* But we expect that the reader should read nothing or data with 'a'.
*/
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <errno.h>
#include <libaio.h>
#include <pthread.h>
struct io_data {
int fd;
size_t blksize;
off_t offset;
char *buf;
int use_aio;
ssize_t read_sz;
};
int reader_ready = 0;
static void usage(const char *prog)
{
fprintf(stderr, "usage: %s [-a] <file>\n", prog);
fprintf(stderr, "\t-a\tuse aio-dio\n");
exit(EXIT_FAILURE);
}
static void *reader(void *arg)
{
struct io_data *data = (struct io_data *)arg;
memset(data->buf, 'b', data->blksize);
reader_ready = 1;
do {
data->read_sz = pread(data->fd, data->buf, data->blksize,
data->offset);
if (data->read_sz < 0)
perror("read file");
} while (data->read_sz <= 0);
return NULL;
}
#define fail(fmt , args...) do { \
fprintf(stderr, fmt , ##args); \
exit(1); \
} while (0)
static void *writer(struct io_data *data)
{
int ret;
while (!reader_ready)
usleep(1);
if (data->use_aio) {
struct io_context *ctx = NULL;
struct io_event evs[1];
struct iocb iocb;
struct iocb *iocbs[] = { &iocb };
ret = io_setup(1, &ctx);
if (ret)
fail("error %s during io_setup\n", strerror(ret));
io_prep_pwrite(&iocb, data->fd, data->buf, data->blksize, data->offset);
ret = io_submit(ctx, 1, iocbs);
if (ret != 1)
fail("error %s during io_submit\n", strerror(ret));
ret = io_getevents(ctx, 1, 1, evs, NULL);
if (ret != 1)
fail("error %s during io_getevents\n", strerror(ret));
if ((signed)evs[0].res < 0)
fail("error %s during write\n", strerror(-evs[0].res));
if ((signed)evs[0].res2 < 0)
fail("secondary error %s during write\n", strerror(-evs[0].res2));
} else {
ret = pwrite(data->fd, data->buf, data->blksize, data->offset);
if (ret < 0)
fail("write file failed: %s", strerror(errno));
}
return NULL;
}
int main(int argc, char *argv[])
{
pthread_t tid;
struct io_data wdata;
struct io_data rdata;
size_t max_blocks = 128; /* 128 */
size_t blksize = 1 * 1024 * 1024; /* 1M */
char *rbuf = NULL, *wbuf = NULL;
int rfd = 0, wfd = 0;
int i, j, c;
int use_aio = 1;
int ret = 0;
int io_align = 4096;
char *prog;
char *testfile;
prog = basename(argv[0]);
while ((c = getopt(argc, argv, "a:d")) != -1) {
switch (c) {
case 'a':
io_align = strtol(optarg, NULL, 0);
break;
case 'd':
use_aio = 0;
break;
default:
usage(prog);
}
}
if (optind != argc - 1)
usage(prog);
testfile = argv[optind];
wfd = open(testfile, O_CREAT|O_DIRECT|O_WRONLY|O_TRUNC, 0644);
if (wfd < 0) {
perror("open for write");
exit(1);
}
rfd = open(testfile, O_DIRECT|O_RDONLY, 0644);
if (rfd < 0) {
perror("open for read");
ret = 1;
goto err;
}
ret = posix_memalign((void **)&wbuf, io_align, blksize);
if (ret) {
fail("failed to alloc memory: %s\n", strerror(ret));
ret = 1;
goto err;
}
ret = posix_memalign((void **)&rbuf, io_align, blksize);
if (ret) {
fail("failed to alloc memory: %s\n", strerror(ret));
ret = 1;
goto err;
}
memset(wbuf, 'a', blksize);
wdata.fd = wfd;
wdata.blksize = blksize;
wdata.buf = wbuf;
wdata.use_aio = use_aio;
rdata.fd = rfd;
rdata.blksize = blksize;
rdata.buf = rbuf;
for (i = 0; i < max_blocks; i++) {
wdata.offset = rdata.offset = i * blksize;
/* reset reader_ready, it will be set in reader thread */
reader_ready = 0;
ret = pthread_create(&tid, NULL, reader, &rdata);
if (ret) {
fail("create reader thread failed: %s\n", strerror(ret));
ret = 1;
goto err;
}
writer(&wdata);
ret = pthread_join(tid, NULL);
if (ret) {
fail("pthread join reader failed: %s\n", strerror(ret));
ret = 1;
goto err;
}
for (j = 0; j < rdata.read_sz; j++) {
if (rdata.buf[j] != 'a') {
fail("encounter an error: "
"block %d offset %d, content %x\n",
i, j, rbuf[j]);
ret = 1;
goto err;
}
}
}
err:
if (rfd)
close(rfd);
if (wfd)
close(wfd);
if (rbuf)
free(rbuf);
if (wbuf)
free(wbuf);
exit(ret);
}