blob: bbf21760a40fe8cb5dc6e4dbe9404d7109ec0bdf [file] [log] [blame]
// SPDX-License-Identifier: GPL-2.0+
/*
* Copyright (C) 2017 Red Hat, Inc. All Rights reserved.
*
* Directly AIO re-write a file with different content again and again.
* And check the data integrity.
*/
#include <sys/stat.h>
#include <sys/types.h>
#include <errno.h>
#include <fcntl.h>
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <ctype.h>
#include <libaio.h>
unsigned long buf_size = 0;
unsigned long loop_count = 0;
void *test_buf[2];
void *cmp_buf;
#define IO_PATTERN1 0x55
#define IO_PATTERN2 0xaa
void usage(char *progname)
{
fprintf(stderr, "usage: %s [-c loop_count] [-b bufsize] filename\n"
"\t-c loopcount: specify how many times to test"
"\t-b bufsize: keep writing from offset 0 to this size",
progname);
exit(1);
}
void dump_buffer(void *buf, off64_t offset, ssize_t len)
{
int i, j;
char *p;
int new;
for (i = 0, p = (char *)buf; i < len; i += 16) {
char *s = p;
if (i && !memcmp(p, p - 16, 16)) {
new = 0;
} else {
if (i)
fprintf(stderr, "*\n");
new = 1;
}
if (!new) {
p += 16;
continue;
}
fprintf(stderr, "%08llx ", (unsigned long long)offset + i);
for (j = 0; j < 16 && i + j < len; j++, p++)
fprintf(stderr, "%02x ", *p);
fprintf(stderr, " ");
for (j = 0; j < 16 && i + j < len; j++, s++) {
if (isalnum((int)*s))
fprintf(stderr, "%c", *s);
else
fprintf(stderr, ".");
}
fprintf(stderr, "\n");
}
fprintf(stderr, "%08llx\n", (unsigned long long)offset + i);
}
int init_test(char *filename)
{
int fd;
int err = 0;
fd = open(filename, O_DIRECT | O_CREAT | O_RDWR, 0600);
if (fd == -1) {
perror("open");
exit(1);
}
ftruncate(fd, buf_size);
/* fill test_buf[0] with IO_PATTERN1 */
err = posix_memalign(&(test_buf[0]), getpagesize(), buf_size);
if (err) {
fprintf(stderr, "error %s during posix_memalign\n",
strerror(err));
exit(1);
}
memset(test_buf[0], IO_PATTERN1, buf_size);
/* fill test_buf[1] with IO_PATTERN2 */
err = posix_memalign(&(test_buf[1]), getpagesize(), buf_size);
if (err) {
fprintf(stderr, "error %s during posix_memalign\n",
strerror(err));
exit(1);
}
memset(test_buf[1], IO_PATTERN2, buf_size);
/* fill test file with IO_PATTERN1 */
if (pwrite(fd, test_buf[0], buf_size, 0) != buf_size) {
perror("pwrite");
exit(1);
}
/* cmp_buf is used to compare with test_buf */
err = posix_memalign(&cmp_buf, getpagesize(), buf_size);
if (err) {
fprintf(stderr, "error %s during posix_memalign\n",
strerror(err));
exit(1);
}
memset(cmp_buf, 0, buf_size);
fsync(fd);
close(fd);
return 0;
}
/*
* Read file content back, then compare with the 'buf' which
* point to the original buffer was written to file.
*/
int file_check(char *filename, void *buf)
{
int fd;
int rc = 0;
/* check cached data with buffer read */
fd = open(filename, O_RDONLY, 0600);
if (fd == -1) {
perror("open");
exit(1);
}
if (pread(fd, cmp_buf, buf_size, 0) != buf_size) {
perror("pread");
exit(1);
}
if (memcmp(buf, cmp_buf, buf_size)) {
fprintf(stderr, "get stale data from buffer read\n");
dump_buffer(cmp_buf, 0, buf_size);
rc++;
}
close(fd);
/*
* check on-disk data with direct-IO read
*/
fd = open(filename, O_DIRECT|O_RDONLY, 0600);
if (fd < 0) {
perror("open with direct-IO");
exit(1);
}
if (pread(fd, cmp_buf, buf_size, 0) != buf_size) {
perror("pread");
exit(1);
}
if (memcmp(buf, cmp_buf, buf_size)) {
fprintf(stderr, "get stale data from DIO read\n");
dump_buffer(cmp_buf, 0, buf_size);
rc++;
}
close(fd);
return rc;
}
int main(int argc, char *argv[])
{
struct io_context *ctx = NULL;
struct io_event evs[1];
struct iocb iocb1;
struct iocb *iocbs[] = { &iocb1 };
int fd, err = 0;
int i;
int c;
char *filename = NULL;
while ((c = getopt(argc, argv, "c:b:")) != -1) {
char *endp;
switch (c) {
case 'c': /* the number of testing cycles */
loop_count = strtol(optarg, &endp, 0);
break;
case 'b': /* buffer size */
buf_size = strtol(optarg, &endp, 0);
break;
default:
usage(argv[0]);
}
}
if (loop_count == 0)
loop_count = 1600;
if (buf_size == 0) /* default minimum buffer size is 65536 bytes */
buf_size = 65536;
if (optind == argc - 1)
filename = argv[optind];
else
usage(argv[0]);
init_test(filename);
err = io_setup(1, &ctx);
if (err) {
fprintf(stderr, "error %s during io_setup\n",
strerror(err));
return 1;
}
i = 0;
/*
* Keep running until loop_count times, fill the file with IO_PATTERN1
* or IO_PATTERN2 one by one, then read the file data back to check if
* there's stale data.
*/
while (loop_count--) {
i++;
i %= 2;
fd = open(filename, O_DIRECT | O_CREAT | O_RDWR, 0600);
if (fd == -1) {
perror("open");
return 1;
}
io_prep_pwrite(&iocb1, fd, test_buf[i], buf_size, 0);
err = io_submit(ctx, 1, iocbs);
if (err != 1) {
fprintf(stderr, "error %s during io_submit\n",
strerror(err));
return 1;
}
err = io_getevents(ctx, 1, 1, evs, NULL);
if (err != 1) {
fprintf(stderr, "error %s during io_getevents\n",
strerror(err));
return 1;
}
close(fd);
err = file_check(filename, test_buf[i]);
if (err != 0)
break;
}
return 0;
}