]> git.apps.os.sepia.ceph.com Git - xfstests-dev.git/commitdiff
generic/338: Add mmap race test
authorJan Kara <jack@suse.cz>
Tue, 5 Apr 2016 01:44:05 +0000 (11:44 +1000)
committerDave Chinner <david@fromorbit.com>
Tue, 5 Apr 2016 01:44:05 +0000 (11:44 +1000)
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@suse.cz>
Acked-by: Brian Boylston <brian.boylston@hpe.com>
Reviewed-by: Dave Chinner <dchinner@redhat.com>
Signed-off-by: Dave Chinner <david@fromorbit.com>
src/Makefile
src/holetest.c [new file with mode: 0644]
tests/generic/340 [new file with mode: 0644]
tests/generic/340.out [new file with mode: 0644]
tests/generic/group

index 31102086a5f6c02bd85dcbbaddf8073d777575d2..1bf318bd068b2b04b0dffacbd957267d8d3d70a0 100644 (file)
@@ -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 (file)
index 0000000..c0a2c67
--- /dev/null
@@ -0,0 +1,342 @@
+/*
+ * 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
+        * 3. 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);
+       }
+
+       /*
+        * fallocated
+        */
+       printf("\nINFO: fallocate test...\n");
+
+#ifdef HAVE_FALLOCATE
+       /* create the file */
+       fd = open(path, O_RDWR | O_EXCL | O_CREAT, 0644);
+       if (fd < 0) {
+               perror(path);
+               exit(11);
+       }
+
+       /* fill it to size */
+       if (fallocate(fd, 0, 0, sz)) {
+               perror("fallocate()");
+               exit(12);
+       }
+
+       /* test it */
+       errcnt = test_this(fd, sz);
+       toterr += errcnt;
+       close(fd);
+       if (stoponerror && errcnt > 0)
+               exit(13);
+
+       /* cleanup */
+       if (unlink(path)) {
+               perror("unlink()");
+               exit(14);
+       }
+#endif
+
+       /*
+        * 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 100644 (file)
index 0000000..69794ae
--- /dev/null
@@ -0,0 +1,59 @@
+#! /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@suse.cz>
+#
+# 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"
+_require_xfs_io_command "falloc"
+
+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 (file)
index 0000000..2bfc722
--- /dev/null
@@ -0,0 +1,73 @@
+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: 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: 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: 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
index cd2a2b76b2b269000469a6cf96485bc42bfb14e5..8a68093faa9dccd1b1fad7b515377eb02a9a9aed 100644 (file)
 337 auto quick metadata
 338 auto quick rw
 339 auto dir
+340 auto