[PATCH 1/2] generic/340: Add mmap race test

[Date Prev] [Date Next] [Thread Prev] [Thread Next] [Date Index] [Thread Index]



Add test which spawns two threads racing to write to file via mmap and
checks the result. This is mainly interesting to uncover races in DAX
fault handling.

Signed-off-by: Jan Kara <jack@xxxxxxx>
---
 src/Makefile          |   5 +-
 src/holetest.c        | 308 ++++++++++++++++++++++++++++++++++++++++++++++++++
 tests/generic/340     |  58 ++++++++++
 tests/generic/340.out |  55 +++++++++
 tests/generic/group   |   1 +
 5 files changed, 425 insertions(+), 2 deletions(-)
 create mode 100644 src/holetest.c
 create mode 100755 tests/generic/340
 create mode 100644 tests/generic/340.out

diff --git a/src/Makefile b/src/Makefile
index 31102086a5f6..1bf318bd068b 100644
--- a/src/Makefile
+++ b/src/Makefile
@@ -11,7 +11,8 @@ TARGETS = dirstress fill fill2 getpagesize holes lstat64 \
 	devzero feature alloc fault fstest t_access_root \
 	godown resvtest writemod makeextents itrash rename \
 	multi_open_unlink dmiperf unwritten_sync genhashnames t_holes \
-	t_mmap_writev t_truncate_cmtime dirhash_collide t_rename_overwrite
+	t_mmap_writev t_truncate_cmtime dirhash_collide t_rename_overwrite \
+	holetest
 
 LINUX_TARGETS = xfsctl bstat t_mtab getdevicesize preallo_rw_pattern_reader \
 	preallo_rw_pattern_writer ftrunc trunc fs_perms testx looptest \
@@ -23,7 +24,7 @@ LINUX_TARGETS = xfsctl bstat t_mtab getdevicesize preallo_rw_pattern_reader \
 
 SUBDIRS =
 
-LLDLIBS = $(LIBATTR) $(LIBHANDLE) $(LIBACL)
+LLDLIBS = $(LIBATTR) $(LIBHANDLE) $(LIBACL) -lpthread
 
 ifeq ($(HAVE_XLOG_ASSIGN_LSN), true)
 LINUX_TARGETS += loggen
diff --git a/src/holetest.c b/src/holetest.c
new file mode 100644
index 000000000000..661599954e4f
--- /dev/null
+++ b/src/holetest.c
@@ -0,0 +1,308 @@
+/*
+ * holetest -- test simultaneous page faults on hole-backed pages
+ * Copyright (C) 2015  Hewlett Packard Enterprise Development LP
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
+ */
+
+
+/*
+ * holetest
+ *
+ * gcc -Wall -pthread -o holetest holetest.c
+ *
+ * This test tool exercises page faults on hole-y portions of an mmapped
+ * file.  The file is created, sized using various methods, mmapped, and
+ * then two threads race to write a marker to different offsets within
+ * each mapped page.  Once the threads have finished marking each page,
+ * the pages are checked for the presence of the markers.
+ *
+ * The file is sized four different ways: explicitly zero-filled by the
+ * test, posix_fallocate(), fallocate(), and ftruncate().  The explicit
+ * zero-fill does not really test simultaneous page faults on hole-backed
+ * pages, but rather serves as control of sorts.
+ *
+ * Usage:
+ *
+ *   holetest [-f] FILENAME FILESIZEinMB
+ *
+ * Where:
+ *
+ *   FILENAME is the name of a non-existent test file to create
+ *
+ *   FILESIZEinMB is the desired size of the test file in MiB
+ *
+ * If the test is successful, FILENAME will be unlinked.  By default,
+ * if the test detects an error in the page markers, then the test exits
+ * immediately and FILENAME is left.  If -f is given, then the test
+ * continues after a marker error and FILENAME is unlinked, but will
+ * still exit with a non-0 status.
+ */
+
+#include <stdlib.h>
+#include <stdio.h>
+#include <errno.h>
+#include <inttypes.h>
+#include <sys/mman.h>
+#include <fcntl.h>
+#include <unistd.h>
+#include <pthread.h>
+#include <string.h>
+
+#define THREADS 2
+
+long page_size;
+long page_offs[THREADS];
+
+void *pt_page_marker(void *args)
+{
+	void **a = args;
+	char *va = (char *)a[0];
+	long npages = (long)a[1];
+	long pgoff = (long)a[2];
+	uint64_t tid = (uint64_t)pthread_self();
+
+	va += pgoff;
+
+	/* mark pages */
+	for (; npages > 0; va += page_size, npages--)
+		*(uint64_t *)(va) = tid;
+
+	return NULL;
+}  /* pt_page_marker() */
+
+
+int test_this(int fd, loff_t sz)
+{
+	long npages;
+	char *vastart;
+	char *va;
+	void *targs[THREADS][3];
+	pthread_t t[THREADS];
+	uint64_t tid[THREADS];
+	int errcnt;
+	int i;
+
+	npages = sz / page_size;
+	printf("INFO: sz = %llu\n", (unsigned long long)sz);
+
+	/* mmap it */
+	vastart = mmap(NULL, sz, PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0);
+	if (MAP_FAILED == vastart) {
+		perror("mmap()");
+		exit(20);
+	}
+
+	/* prepare the thread args */
+	for (i = 0; i < THREADS; i++) {
+		targs[i][0] = vastart;
+		targs[i][1] = (void *)npages;
+		targs[i][2] = (void *)page_offs[i];
+	}
+
+	for (i = 0; i < THREADS; i++) {
+		/* start two threads */
+		if (pthread_create(&t[i], NULL, pt_page_marker, &targs[i])) {
+			perror("pthread_create");
+			exit(21);
+		}
+		tid[i] = (uint64_t)t[i];
+		printf("INFO: thread %d created\n", i);
+	}
+
+	/* wait for them to finish */
+	for (i = 0; i < THREADS; i++)
+		pthread_join(t[i], NULL);
+
+	/* check markers on each page */
+	errcnt = 0;
+	for (va = vastart; npages > 0; va += page_size, npages--) {
+		for (i = 0; i < THREADS; i++) {
+			if (*(uint64_t*)(va + page_offs[i]) != tid[i]) {
+				printf("ERROR: thread %d, "
+				       "offset %08lx, %08lx != %08lx\n", i,
+				       (va + page_offs[i] - vastart),
+				       *(uint64_t*)(va + page_offs[i]), tid[i]);
+				errcnt += 1;
+			}
+		}
+	}
+
+	printf("INFO: %d error(s) detected\n", errcnt);
+
+	munmap(vastart, sz);
+
+	return errcnt;
+}
+
+int main(int argc, char **argv)
+{
+	int stoponerror = 1;
+	char *path;
+	loff_t sz;
+	int fd;
+	int errcnt;
+	int toterr = 0;
+	int i, step;
+	char *endch;
+
+	page_size = getpagesize();
+	step = page_size / THREADS;
+	page_offs[0] = step / 2;
+	for (i = 1; i < THREADS; i++)
+		page_offs[i] = page_offs[i-1] + step;
+
+	/* process command line */
+	argc--; argv++;
+	/* ignore errors? */
+	if ((argc == 3) && !strcmp(argv[0], "-f")) {
+		stoponerror = 0;
+		argc--;
+		argv++;
+	}
+	/* file name and size */
+	if (argc != 2 || argv[0][0] == '-') {
+		fprintf(stderr, "ERROR: usage: holetest [-f] "
+			"FILENAME FILESIZEinMB\n");
+		exit(1);
+	}
+	path = argv[0];
+	sz = strtol(argv[1], &endch, 10);
+	if (*endch || sz < 1) {
+		fprintf(stderr, "ERROR: bad FILESIZEinMB\n");
+		exit(1);
+	}
+	sz <<= 20;
+
+	/*
+	 * we're going to run our test in several different ways:
+	 *
+	 * 1. explictly zero-filled
+	 * 2. posix_fallocated
+	 * 4. ftruncated
+	 */
+
+
+	/*
+	 * explicitly zero-filled
+	 */
+	printf("\nINFO: zero-filled test...\n");
+
+	/* create the file */
+	fd = open(path, O_RDWR | O_EXCL | O_CREAT, 0644);
+	if (fd < 0) {
+		perror(path);
+		exit(2);
+	}
+
+	/* truncate it to size */
+	if (ftruncate(fd, sz)) {
+		perror("ftruncate()");
+		exit(3);
+	}
+
+	/* explicitly zero-fill */
+	{
+		char*   va = mmap(NULL, sz, PROT_READ | PROT_WRITE,
+				  MAP_SHARED, fd, 0);
+		if (MAP_FAILED == va) {
+			perror("mmap()");
+			exit(4);
+		}
+		memset(va, 0, sz);
+		munmap(va, sz);
+	}
+
+	/* test it */
+	errcnt = test_this(fd, sz);
+	toterr += errcnt;
+	close(fd);
+	if (stoponerror && errcnt > 0)
+		exit(5);
+
+	/* cleanup */
+	if (unlink(path)) {
+		perror("unlink()");
+		exit(6);
+	}
+
+
+	/*
+	 * posix_fallocated
+	 */
+	printf("\nINFO: posix_fallocate test...\n");
+
+	/* create the file */
+	fd = open(path, O_RDWR | O_EXCL | O_CREAT, 0644);
+	if (fd < 0) {
+		perror(path);
+		exit(7);
+	}
+
+	/* fill it to size */
+	if (posix_fallocate(fd, 0, sz)) {
+		perror("posix_fallocate()");
+		exit(8);
+	}
+
+	/* test it */
+	errcnt = test_this(fd, sz);
+	toterr += errcnt;
+	close(fd);
+	if (stoponerror && errcnt > 0)
+		exit(9);
+
+	/* cleanup */
+	if (unlink(path)) {
+		perror("unlink()");
+		exit(10);
+	}
+
+	/*
+	 * ftruncated
+	 */
+	printf("\nINFO: ftruncate test...\n");
+
+	/* create the file */
+	fd = open(path, O_RDWR | O_EXCL | O_CREAT, 0644);
+	if (fd < 0) {
+		perror(path);
+		exit(15);
+	}
+
+	/* truncate it to size */
+	if (ftruncate(fd, sz)) {
+		perror("ftruncate()");
+		exit(16);
+	}
+
+	/* test it */
+	errcnt = test_this(fd, sz);
+	toterr += errcnt;
+	close(fd);
+	if (stoponerror && errcnt > 0)
+		exit(17);
+
+	/* cleanup */
+	if (unlink(path)) {
+		perror("unlink()");
+		exit(18);
+	}
+
+	/* done */
+	if (toterr > 0)
+		exit(19);
+	return 0;
+}
diff --git a/tests/generic/340 b/tests/generic/340
new file mode 100755
index 000000000000..2ba34eb59724
--- /dev/null
+++ b/tests/generic/340
@@ -0,0 +1,58 @@
+#! /bin/bash
+# FSQA Test No. 340
+#
+# Test mmap writing races from racing threads
+#
+#-----------------------------------------------------------------------
+#
+# Copyright (C) 2016 SUSE Linux Products GmbH. All Rights Reserved.
+# Author: Jan Kara <jack@xxxxxxx>
+#
+# This program is free software; you can redistribute it and/or
+# modify it under the terms of the GNU General Public License as
+# published by the Free Software Foundation.
+#
+# This program is distributed in the hope that it would be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program; if not, write the Free Software Foundation,
+# Inc.,  51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
+#-----------------------------------------------------------------------
+#
+
+seq=`basename $0`
+seqres=$RESULT_DIR/$seq
+echo "QA output created by $seq"
+tmp=/tmp/$$
+status=1	# failure is the default!
+trap "_cleanup; exit \$status" 0 1 2 3 15
+
+_cleanup()
+{
+	cd /
+	rm -f $tmp.*
+}
+
+# get standard environment and checks
+. ./common/rc
+
+# real QA test starts here
+_supported_fs generic
+_supported_os Linux
+_require_scratch
+_require_test_program "holetest"
+
+rm -f $seqres.full
+
+_scratch_mkfs >>$seqres.full 2>&1
+_scratch_mount
+
+src/holetest -f $SCRATCH_MNT/testfile 1
+src/holetest -f $SCRATCH_MNT/testfile 16
+src/holetest -f $SCRATCH_MNT/testfile 256
+
+status=0
+exit
diff --git a/tests/generic/340.out b/tests/generic/340.out
new file mode 100644
index 000000000000..0eac9ed654dc
--- /dev/null
+++ b/tests/generic/340.out
@@ -0,0 +1,55 @@
+QA output created by 340
+
+INFO: zero-filled test...
+INFO: sz = 1048576
+INFO: thread 0 created
+INFO: thread 1 created
+INFO: 0 error(s) detected
+
+INFO: posix_fallocate test...
+INFO: sz = 1048576
+INFO: thread 0 created
+INFO: thread 1 created
+INFO: 0 error(s) detected
+
+INFO: ftruncate test...
+INFO: sz = 1048576
+INFO: thread 0 created
+INFO: thread 1 created
+INFO: 0 error(s) detected
+
+INFO: zero-filled test...
+INFO: sz = 16777216
+INFO: thread 0 created
+INFO: thread 1 created
+INFO: 0 error(s) detected
+
+INFO: posix_fallocate test...
+INFO: sz = 16777216
+INFO: thread 0 created
+INFO: thread 1 created
+INFO: 0 error(s) detected
+
+INFO: ftruncate test...
+INFO: sz = 16777216
+INFO: thread 0 created
+INFO: thread 1 created
+INFO: 0 error(s) detected
+
+INFO: zero-filled test...
+INFO: sz = 268435456
+INFO: thread 0 created
+INFO: thread 1 created
+INFO: 0 error(s) detected
+
+INFO: posix_fallocate test...
+INFO: sz = 268435456
+INFO: thread 0 created
+INFO: thread 1 created
+INFO: 0 error(s) detected
+
+INFO: ftruncate test...
+INFO: sz = 268435456
+INFO: thread 0 created
+INFO: thread 1 created
+INFO: 0 error(s) detected
diff --git a/tests/generic/group b/tests/generic/group
index 727648c68785..d0ca807b3695 100644
--- a/tests/generic/group
+++ b/tests/generic/group
@@ -340,3 +340,4 @@
 335 auto quick metadata
 336 auto quick metadata
 337 auto quick metadata
+340 auto
-- 
2.6.2

--
To unsubscribe from this list: send the line "unsubscribe fstests" in
the body of a message to majordomo@xxxxxxxxxxxxxxx
More majordomo info at  http://vger.kernel.org/majordomo-info.html



[Index of Archives]     [Linux Filesystems Development]     [Linux NFS]     [Linux NILFS]     [Linux USB Devel]     [Linux Audio Users]     [Yosemite News]     [Linux Kernel]     [Linux SCSI]

  Powered by Linux