blob: d3a2e5fcb9d22cdb6bb2ce2b5809aacf87aa1ba9 [file] [log] [blame]
// SPDX-License-Identifier: GPL-2.0-or-newer
/*
* Copyright (c) 2019 Oracle.
* All Rights Reserved.
*
* Race appending aio dio and fallocate to make sure we get the correct file
* size afterwards.
*/
#include <stdio.h>
#include <pthread.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <unistd.h>
#include <string.h>
#include <errno.h>
#include <libaio.h>
#include <stdlib.h>
#include <stdbool.h>
#include <limits.h>
static int fd;
static int blocksize;
static void *
falloc_thread(
void *p)
{
int ret;
ret = fallocate(fd, 0, 0, blocksize);
if (ret)
perror("falloc");
return NULL;
}
static int
test(
const char *fname,
unsigned int iteration,
unsigned int *passed)
{
struct stat sbuf;
pthread_t thread;
io_context_t ioctx = 0;
struct iocb iocb;
struct iocb *iocbp = &iocb;
struct io_event event;
char *buf;
bool wait_thread = false;
int ret;
/* Truncate file, allocate resources for doing IO. */
fd = open(fname, O_DIRECT | O_RDWR | O_TRUNC | O_CREAT, 0644);
if (fd < 0) {
perror(fname);
return -1;
}
ret = fstat(fd, &sbuf);
if (ret) {
perror(fname);
goto out;
}
blocksize = sbuf.st_blksize;
ret = posix_memalign((void **)&buf, sysconf(_SC_PAGESIZE), blocksize);
if (ret) {
errno = ret;
perror("buffer");
goto out;
}
memset(buf, 'X', blocksize);
memset(&event, 0, sizeof(event));
ret = io_queue_init(1, &ioctx);
if (ret) {
errno = -ret;
perror("io_queue_init");
goto out_buf;
}
/*
* Set ourselves up to race fallocate(0..blocksize) with aio dio
* pwrite(blocksize..blocksize * 2). This /should/ give us a file
* with length (2 * blocksize).
*/
io_prep_pwrite(&iocb, fd, buf, blocksize, blocksize);
ret = pthread_create(&thread, NULL, falloc_thread, NULL);
if (ret) {
errno = ret;
perror("pthread");
goto out_io;
}
wait_thread = true;
ret = io_submit(ioctx, 1, &iocbp);
if (ret != 1) {
errno = -ret;
perror("io_submit");
goto out_join;
}
ret = io_getevents(ioctx, 1, 1, &event, NULL);
if (ret != 1) {
errno = -ret;
perror("io_getevents");
goto out_join;
}
if (event.res < 0) {
errno = -event.res;
perror("io_event.res");
goto out_join;
}
if (event.res2 < 0) {
errno = -event.res2;
perror("io_event.res2");
goto out_join;
}
wait_thread = false;
ret = pthread_join(thread, NULL);
if (ret) {
errno = ret;
perror("join");
goto out_io;
}
/* Make sure we actually got a file of size (2 * blocksize). */
ret = fstat(fd, &sbuf);
if (ret) {
perror(fname);
goto out_buf;
}
if (sbuf.st_size != 2 * blocksize) {
fprintf(stderr, "[%u]: sbuf.st_size=%llu, expected %llu.\n",
iteration,
(unsigned long long)sbuf.st_size,
(unsigned long long)2 * blocksize);
} else {
printf("[%u]: passed.\n", iteration);
(*passed)++;
}
out_join:
if (wait_thread) {
ret = pthread_join(thread, NULL);
if (ret) {
errno = ret;
perror("join");
goto out_io;
}
}
out_io:
ret = io_queue_release(ioctx);
if (ret) {
errno = -ret;
perror("io_queue_release");
}
out_buf:
free(buf);
out:
ret = close(fd);
fd = -1;
if (ret) {
perror("close");
return -1;
}
return 0;
}
int main(int argc, char *argv[])
{
int ret;
long l;
unsigned int i;
unsigned int passed = 0;
if (argc != 3) {
printf("Usage: %s filename iterations\n", argv[0]);
return 1;
}
errno = 0;
l = strtol(argv[2], NULL, 0);
if (errno) {
perror(argv[2]);
return 1;
}
if (l < 1 || l > UINT_MAX) {
fprintf(stderr, "%ld: must be between 1 and %u.\n",
l, UINT_MAX);
return 1;
}
for (i = 0; i < l; i++) {
ret = test(argv[1], i, &passed);
if (ret)
return 1;
}
printf("pass rate: %u/%u (%.2f%%)\n", passed, i, 100.0 * passed / i);
return 0;
}