[LTP] [PATCH 3/5] Add test for file data integrity

Martin Doucha mdoucha@suse.cz
Fri Nov 1 15:11:08 CET 2024


Signed-off-by: Martin Doucha <mdoucha@suse.cz>
---
 testcases/kernel/fs/fsplough/.gitignore  |   1 +
 testcases/kernel/fs/fsplough/Makefile    |   8 +
 testcases/kernel/fs/fsplough/fsplough.c  | 279 +++++++++++++++++++++++
 testcases/misc/lvm/datafiles/runfile.tpl |   4 +
 4 files changed, 292 insertions(+)
 create mode 100644 testcases/kernel/fs/fsplough/.gitignore
 create mode 100644 testcases/kernel/fs/fsplough/Makefile
 create mode 100644 testcases/kernel/fs/fsplough/fsplough.c

diff --git a/testcases/kernel/fs/fsplough/.gitignore b/testcases/kernel/fs/fsplough/.gitignore
new file mode 100644
index 000000000..34fee75c7
--- /dev/null
+++ b/testcases/kernel/fs/fsplough/.gitignore
@@ -0,0 +1 @@
+/fsplough
diff --git a/testcases/kernel/fs/fsplough/Makefile b/testcases/kernel/fs/fsplough/Makefile
new file mode 100644
index 000000000..887756d04
--- /dev/null
+++ b/testcases/kernel/fs/fsplough/Makefile
@@ -0,0 +1,8 @@
+# SPDX-License-Identifier: GPL-2.0-or-later
+# Copyright (c) 2017 Linux Test Project
+
+top_srcdir		?= ../../../..
+
+include $(top_srcdir)/include/mk/testcases.mk
+
+include $(top_srcdir)/include/mk/generic_leaf_target.mk
diff --git a/testcases/kernel/fs/fsplough/fsplough.c b/testcases/kernel/fs/fsplough/fsplough.c
new file mode 100644
index 000000000..900aaacab
--- /dev/null
+++ b/testcases/kernel/fs/fsplough/fsplough.c
@@ -0,0 +1,279 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+/*
+ * Copyright (C) 2024 SUSE LLC <mdoucha@suse.cz>
+ */
+
+/*\
+ * [Description]
+ *
+ * Write data into a test file using various methods and verify that file
+ * contents match what was written.
+ */
+
+#define _GNU_SOURCE
+#include <fcntl.h>
+#include <sys/statvfs.h>
+#include "tst_test.h"
+#include "tst_safe_prw.h"
+
+#define MAX_VEC 8
+#define TEST_FILENAME "fsplough.dat"
+
+typedef void (*io_func)(void *buf, size_t offset, size_t size);
+
+static char *workdir_arg;
+static char *directwr_flag;
+static char *directrd_flag;
+static char *loop_arg;
+static int loop_count = 4096;
+
+static int read_fd = -1, write_fd = -1;
+static char *writebuf, *filedata;
+static size_t blocksize, bufsize, filesize;
+
+static void do_write(void *buf, size_t offset, size_t size);
+static void do_pwrite(void *buf, size_t offset, size_t size);
+static void do_writev(void *buf, size_t offset, size_t size);
+static void do_pwritev(void *buf, size_t offset, size_t size);
+static void do_read(void *buf, size_t offset, size_t size);
+static void do_pread(void *buf, size_t offset, size_t size);
+static void do_readv(void *buf, size_t offset, size_t size);
+static void do_preadv(void *buf, size_t offset, size_t size);
+
+static const io_func write_funcs[] = {
+	do_write,
+	do_pwrite,
+	do_writev,
+	do_pwritev
+};
+
+static const io_func read_funcs[] = {
+	do_read,
+	do_pread,
+	do_readv,
+	do_preadv
+};
+
+static size_t fill_buffer(char *buf, size_t size)
+{
+	size_t i, ret = MAX_VEC + 1 + rand() % (size - MAX_VEC);
+
+	/* Align buffer size to block size */
+	if (directwr_flag || directrd_flag)
+		ret = MAX(LTP_ALIGN(ret, blocksize), MAX_VEC * blocksize);
+
+	for (i = 0; i < ret; i++)
+		buf[i] = rand();
+
+	return ret;
+}
+
+static void vectorize_buffer(struct iovec *vec, size_t vec_size, char *buf,
+	size_t buf_size, int align)
+{
+	size_t i, len, chunk = align ? blocksize : 1;
+
+	memset(vec, 0, vec_size * sizeof(struct iovec));
+	buf_size /= chunk;
+
+	for (i = 0; buf_size && i < vec_size; i++) {
+		len = 1 + rand() % (buf_size + i + 1 - vec_size);
+		vec[i].iov_base = buf;
+		vec[i].iov_len = len * chunk;
+		buf += vec[i].iov_len;
+		buf_size -= len;
+	}
+
+	vec[vec_size - 1].iov_len += buf_size * chunk;
+}
+
+static void update_filedata(const void *buf, size_t offset, size_t size)
+{
+	memcpy(filedata + offset, buf, size * sizeof(char));
+}
+
+static void do_write(void *buf, size_t offset, size_t size)
+{
+	SAFE_LSEEK(write_fd, offset, SEEK_SET);
+	SAFE_WRITE(1, write_fd, buf, size);
+}
+
+static void do_pwrite(void *buf, size_t offset, size_t size)
+{
+	SAFE_PWRITE(1, write_fd, buf, size, offset);
+}
+
+static void do_writev(void *buf, size_t offset, size_t size)
+{
+	struct iovec vec[MAX_VEC] = {};
+
+	vectorize_buffer(vec, MAX_VEC, buf, size, !!directwr_flag);
+	SAFE_LSEEK(write_fd, offset, SEEK_SET);
+	SAFE_WRITEV(1, write_fd, vec, MAX_VEC);
+}
+
+static void do_pwritev(void *buf, size_t offset, size_t size)
+{
+	struct iovec vec[MAX_VEC] = {};
+
+	vectorize_buffer(vec, MAX_VEC, buf, size, !!directwr_flag);
+	SAFE_PWRITEV(1, write_fd, vec, MAX_VEC, offset);
+}
+
+static void do_read(void *buf, size_t offset, size_t size)
+{
+	SAFE_LSEEK(read_fd, offset, SEEK_SET);
+	SAFE_READ(1, read_fd, buf, size);
+}
+
+static void do_pread(void *buf, size_t offset, size_t size)
+{
+	SAFE_PREAD(1, read_fd, buf, size, offset);
+}
+
+static void do_readv(void *buf, size_t offset, size_t size)
+{
+	struct iovec vec[MAX_VEC] = {};
+
+	vectorize_buffer(vec, MAX_VEC, buf, size, !!directrd_flag);
+	SAFE_LSEEK(read_fd, offset, SEEK_SET);
+	SAFE_READV(1, read_fd, vec, MAX_VEC);
+}
+
+static void do_preadv(void *buf, size_t offset, size_t size)
+{
+	struct iovec vec[MAX_VEC] = {};
+
+	vectorize_buffer(vec, MAX_VEC, buf, size, !!directrd_flag);
+	SAFE_PREADV(1, read_fd, vec, MAX_VEC, offset);
+}
+
+static int open_testfile(int flags)
+{
+	if ((flags & O_WRONLY) && directwr_flag)
+		flags |= O_DIRECT;
+
+	if ((flags & O_RDONLY) && directrd_flag)
+		flags |= O_DIRECT;
+
+	return SAFE_OPEN(TEST_FILENAME, flags, 0644);
+}
+
+static void setup(void)
+{
+	struct statvfs statbuf;
+	size_t pagesize;
+
+	srand(time(0));
+	pagesize = SAFE_SYSCONF(_SC_PAGESIZE);
+
+	if (workdir_arg)
+		SAFE_CHDIR(workdir_arg);
+
+	if (tst_parse_int(loop_arg, &loop_count, 0, INT_MAX))
+		tst_brk(TBROK, "Invalid write loop count: %s", loop_arg);
+
+	write_fd = open_testfile(O_WRONLY | O_CREAT | O_TRUNC);
+	read_fd = open_testfile(O_RDONLY);
+	TEST(fstatvfs(write_fd, &statbuf));
+
+	if (TST_RET == -1)
+		tst_brk(TBROK | TTERRNO, "fstatvfs() failed");
+	else if (TST_RET)
+		tst_brk(TBROK | TTERRNO, "Invalid fstatvfs() return value");
+
+	blocksize = statbuf.f_bsize;
+	tst_res(TINFO, "Block size: %zu", blocksize);
+	bufsize = 4 * MAX_VEC * MAX(pagesize, blocksize);
+	filesize = 1024 * MAX(pagesize, blocksize);
+	writebuf = SAFE_MMAP(NULL, bufsize, PROT_READ | PROT_WRITE,
+		MAP_PRIVATE | MAP_ANONYMOUS, -1, 0);
+	filedata = SAFE_MALLOC(filesize);
+
+	tst_set_max_runtime(bufsize * loop_count / (8 * 1024 * 1024));
+}
+
+static void run(void)
+{
+	size_t start, length;
+	int i, f, fails = 0;
+
+	/* Test data consistency between random writes */
+	for (i = 0; i < loop_count; i++) {
+		length = fill_buffer(writebuf, bufsize);
+		start = rand() % (filesize + 1 - length);
+
+		/* Align offset to blocksize if needed */
+		if (directrd_flag || directwr_flag)
+			start = (start + blocksize / 2) & ~(blocksize - 1);
+
+		update_filedata(writebuf, start, length);
+		f = rand() % ARRAY_SIZE(write_funcs);
+		write_funcs[f](writebuf, start, length);
+
+		memset(writebuf, 0, length);
+		f = rand() % ARRAY_SIZE(read_funcs);
+		read_funcs[f](writebuf, start, length);
+
+		if (memcmp(writebuf, filedata + start, length)) {
+			tst_res(TFAIL, "Partial data mismatch at [%zu:%zu]",
+				start, start + length);
+			fails++;
+		}
+	}
+
+	if (!fails)
+		tst_res(TPASS, "Partial data are consistent");
+
+	/* Ensure that the testfile has the expected size */
+	do_write(writebuf, filesize - blocksize, blocksize);
+	update_filedata(writebuf, filesize - blocksize, blocksize);
+
+	/* Sync the testfile and clear cache */
+	SAFE_CLOSE(read_fd);
+	SAFE_FSYNC(write_fd);
+	SAFE_FILE_PRINTF("/proc/sys/vm/drop_caches", "1");
+	read_fd = open_testfile(O_RDONLY);
+
+	/* Check final file contents */
+	for (start = 0; start < filesize; start += bufsize) {
+		length = MIN(bufsize, filesize - start);
+		SAFE_READ(1, read_fd, writebuf, length);
+
+		if (memcmp(writebuf, filedata + start, length)) {
+			tst_res(TFAIL, "Final data mismatch at [%zu:%zu]",
+				start, start + length);
+			return;
+		}
+	}
+
+	tst_res(TPASS, "Final data are consistent");
+}
+
+static void cleanup(void)
+{
+	SAFE_MUNMAP(writebuf, bufsize);
+	free(filedata);
+
+	if (read_fd >= 0)
+		SAFE_CLOSE(read_fd);
+
+	if (write_fd >= 0)
+		SAFE_CLOSE(write_fd);
+
+	SAFE_UNLINK(TEST_FILENAME);
+}
+
+static struct tst_test test = {
+	.test_all = run,
+	.setup = setup,
+	.cleanup = cleanup,
+	.needs_tmpdir = 1,
+	.options = (struct tst_option[]) {
+		{"c:", &loop_arg, "Number of write loops (default: 4096)"},
+		{"d:", &workdir_arg, "Path to working directory"},
+		{"W", &directwr_flag, "Use direct I/O for writing"},
+		{"R", &directrd_flag, "Use direct I/O for reading"},
+		{}
+	}
+};
diff --git a/testcases/misc/lvm/datafiles/runfile.tpl b/testcases/misc/lvm/datafiles/runfile.tpl
index 4c80e9391..ee733626f 100644
--- a/testcases/misc/lvm/datafiles/runfile.tpl
+++ b/testcases/misc/lvm/datafiles/runfile.tpl
@@ -29,6 +29,10 @@
 {fsname}_gf28 growfiles -W {fsname}_gf28 -b -D 0 -w -g 16b -C 1 -b -i 1000 -u {tempdir}/{fsname}/gfsparse-2-$$
 {fsname}_gf29 growfiles -W {fsname}_gf29 -b -D 0 -r 1-4096 -R 0-33554432 -i 0 -L 60 -B 805306368 -C 1 -u {tempdir}/{fsname}/gfsparse-3-$$
 {fsname}_gf30 growfiles -W {fsname}_gf30 -D 0 -b -i 0 -L 60 -u -B 1000b -e 1 -o O_RDWR,O_CREAT,O_SYNC -g 20480 -T 10 -t 20480 {tempdir}/{fsname}/gf-sync-$$
+{fsname}_plough01 fsplough -d {tempdir}/{fsname}
+{fsname}_plough02 fsplough -R -d {tempdir}/{fsname}
+{fsname}_plough03 fsplough -W -d {tempdir}/{fsname}
+{fsname}_plough04 fsplough -RW -d {tempdir}/{fsname}
 {fsname}_rwtest01 rwtest -N {fsname}_rwtest01 -c -q -i 60s  -f sync 10%25000:rw-sync-$$ 500b:{tempdir}/{fsname}/rwtest01%f
 {fsname}_rwtest02 rwtest -N {fsname}_rwtest02 -c -q -i 60s  -f buffered 10%25000:rw-buffered-$$ 500b:{tempdir}/{fsname}/rwtest02%f
 {fsname}_rwtest03 rwtest -N {fsname}_rwtest03 -c -q -i 60s -n 2  -f buffered -s mmread,mmwrite -m random -Dv 10%25000:mm-buff-$$ 500b:{tempdir}/{fsname}/rwtest03%f
-- 
2.46.0



More information about the ltp mailing list