@@ -16,7 +16,7 @@ TARGETS = dirstress fill fill2 getpagesize holes lstat64 \
holetest t_truncate_self t_mmap_dio af_unix t_mmap_stale_pmd \
t_mmap_cow_race t_mmap_fallocate fsync-err t_mmap_write_ro \
t_ext4_dax_journal_corruption t_ext4_dax_inline_corruption \
- t_ofd_locks t_locks_execve t_mmap_collision
+ t_ofd_locks t_locks_execve t_mmap_collision mmap-write-concurrent
LINUX_TARGETS = xfsctl bstat t_mtab getdevicesize preallo_rw_pattern_reader \
preallo_rw_pattern_writer ftrunc trunc fs_perms testx looptest \
new file mode 100644
@@ -0,0 +1,157 @@
+// SPDX-License-Identifier: GPL-2.0-or-newer
+/*
+ * Copyright (c) 2019 Oracle.
+ * All Rights Reserved.
+ *
+ * Create writable mappings to multiple files and write them all to test
+ * concurrent mmap writes to the same shared blocks.
+ */
+#include <sys/mman.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <unistd.h>
+#include <fcntl.h>
+#include <stdio.h>
+#include <errno.h>
+#include <stdlib.h>
+#include <string.h>
+
+int main(int argc, char *argv[])
+{
+ struct stat *statbuf;
+ off_t offset;
+ size_t length;
+ char *endptr;
+ char **mappings;
+ int *fds;
+ char *buf;
+ int i;
+ int ret;
+
+ if (argc < 4) {
+ printf("Usage: %s offset len file [files...]\n", argv[0]);
+ return 1;
+ }
+
+ /* Parse mwrite offset. */
+ errno = 0;
+ offset = strtoul(argv[1], &endptr, 0);
+ if (errno) {
+ perror(argv[1]);
+ return 1;
+ }
+ if (*endptr != '\0') {
+ fprintf(stderr, "%s: not a proper file offset?\n", argv[1]);
+ return 1;
+ }
+
+ /* Parse mwrite length. */
+ errno = 0;
+ length = strtoul(argv[2], &endptr, 0);
+ if (errno) {
+ perror(argv[1]);
+ return 1;
+ }
+ if (*endptr != '\0') {
+ fprintf(stderr, "%s: not a proper file length?\n", argv[2]);
+ return 1;
+ }
+
+ mappings = calloc(argc - 3, sizeof(char *));
+ if (!mappings) {
+ perror("calloc maps");
+ return 1;
+ }
+
+ fds = calloc(argc - 3, sizeof(int));
+ if (!fds) {
+ perror("calloc fds");
+ return 1;
+ }
+
+ buf = malloc(length);
+ if (!buf) {
+ perror("malloc buf");
+ return 1;
+ }
+
+ statbuf = calloc(argc - 3, sizeof(struct stat));
+ if (!statbuf) {
+ perror("calloc statbuf");
+ return 1;
+ }
+
+ for (i = 0; i < argc - 3; i++) {
+ char *fname = argv[i + 3];
+
+ /* Open file, create mapping for the range we want. */
+ fds[i] = open(fname, O_RDWR);
+ if (fds[i] < 0) {
+ perror(fname);
+ return 1;
+ }
+
+ ret = fstat(fds[i], &statbuf[i]);
+ if (ret) {
+ perror(fname);
+ return 1;
+ }
+
+ if (offset + length > statbuf[i].st_size) {
+ fprintf(stderr, "%s: file must be %llu bytes\n",
+ fname,
+ (unsigned long long)offset + length);
+ return 1;
+ }
+
+ mappings[i] = mmap(NULL, statbuf[i].st_size,
+ PROT_READ | PROT_WRITE, MAP_SHARED,
+ fds[i], 0);
+ if (mappings[i] == MAP_FAILED) {
+ perror(fname);
+ return 1;
+ }
+
+ /*
+ * Make sure the mapping for region we're going to write is
+ * already populated in the page cache.
+ */
+ memcpy(buf, mappings[i] + offset, length);
+ }
+
+ /* Dirty the same region in each file to test COW. */
+ for (i = 0; i < argc - 3; i++) {
+ memset(buf, 0x58 + i, length);
+ memcpy(mappings[i] + offset, buf, length);
+ }
+ for (i = 0; i < argc - 3; i++) {
+ ret = msync(mappings[i], offset + length, MS_SYNC);
+ if (ret) {
+ perror("msync");
+ return 1;
+ }
+ }
+
+ /* Close everything. */
+ for (i = 0; i < argc - 3; i++) {
+ ret = munmap(mappings[i], statbuf[i].st_size);
+ if (ret) {
+ perror("munmap");
+ return 1;
+ }
+
+ ret = close(fds[i]);
+ if (ret) {
+ perror("close");
+ return 1;
+ }
+ }
+
+ /* Free everything. */
+ free(statbuf);
+ free(buf);
+ free(fds);
+ free(mappings);
+
+ return 0;
+}
new file mode 100755
@@ -0,0 +1,89 @@
+#! /bin/bash
+# SPDX-License-Identifier: GPL-2.0-or-newer
+# Copyright (c) 2019, Oracle and/or its affiliates. All Rights Reserved.
+#
+# FS QA Test No. 945
+#
+# Make sure that we can handle multiple mmap writers to the same file.
+
+seq=`basename $0`
+seqres=$RESULT_DIR/$seq
+echo "QA output created by $seq"
+
+here=`pwd`
+tmp=/tmp/$$
+status=1 # failure is the default!
+trap "_cleanup; exit \$status" 0 1 2 3 15
+
+_cleanup()
+{
+ cd /
+ rm -rf $tmp.* $testdir
+}
+
+# get standard environment, filters and checks
+. ./common/rc
+. ./common/filter
+. ./common/reflink
+
+# real QA test starts here
+_supported_os Linux
+_supported_fs generic
+_require_command "$FILEFRAG_PROG" filefrag
+_require_test_reflink
+_require_cp_reflink
+
+rm -f $seqres.full
+
+compare() {
+ md5sum $testdir/file1 | _filter_test_dir
+ md5sum $testdir/file2 | _filter_test_dir
+ md5sum $testdir/file3 | _filter_test_dir
+ md5sum $testdir/file4 | _filter_test_dir
+
+ cmp -s $testdir/file1 $testdir/file2 || echo "Files 1-2 do not match"
+ cmp -s $testdir/file1 $testdir/file3 || echo "Files 1-3 do not match"
+ cmp -s $testdir/file1 $testdir/file4 || echo "Files 1-4 do not match"
+}
+
+testdir=$TEST_DIR/test-$seq
+rm -rf $testdir
+mkdir $testdir
+
+echo "Create the original files"
+filesz=$((65536 * 4))
+_pwrite_byte 0x61 0 $filesz $testdir/file1 >> $seqres.full
+_cp_reflink $testdir/file1 $testdir/file2 >> $seqres.full
+_cp_reflink $testdir/file1 $testdir/file3 >> $seqres.full
+_cp_reflink $testdir/file1 $testdir/file4 >> $seqres.full
+_test_cycle_mount
+
+echo "Compare files before cow"
+compare
+
+echo "mwrite all copies"
+off=$(( (filesz / 2) - 168 ))
+len=337
+./src/mmap-write-concurrent $off $len $testdir/file1 $testdir/file2 \
+ $testdir/file3 $testdir/file4
+
+echo "Compare files before remount"
+compare
+_test_cycle_mount
+
+echo "Compare files after remount"
+compare
+
+echo "Check for non-shared extents"
+$FILEFRAG_PROG -v $testdir/file1 $testdir/file2 $testdir/file3 \
+ $testdir/file4 | grep '^[[:space:]]*[0-9]*:' > $testdir/fiemap
+cat $testdir/fiemap >> $seqres.full
+grep -q 'shared' $testdir/fiemap || \
+ echo "Expected to find shared extents"
+
+grep -q -v 'shared' $testdir/fiemap || \
+ echo "Expected to find non-shared extents"
+
+# success, all done
+status=0
+exit
new file mode 100644
@@ -0,0 +1,25 @@
+QA output created by 945
+Create the original files
+Compare files before cow
+c946b71bb69c07daf25470742c967e7c TEST_DIR/test-945/file1
+c946b71bb69c07daf25470742c967e7c TEST_DIR/test-945/file2
+c946b71bb69c07daf25470742c967e7c TEST_DIR/test-945/file3
+c946b71bb69c07daf25470742c967e7c TEST_DIR/test-945/file4
+mwrite all copies
+Compare files before remount
+83f84225313027ef51fea4f86239d432 TEST_DIR/test-945/file1
+d9d86b794e7d626ccee90392518c9048 TEST_DIR/test-945/file2
+c0dae8b7541aad8ef4ed3b9ffd20e9a3 TEST_DIR/test-945/file3
+094457223ea2751265e617631f5f4aa9 TEST_DIR/test-945/file4
+Files 1-2 do not match
+Files 1-3 do not match
+Files 1-4 do not match
+Compare files after remount
+83f84225313027ef51fea4f86239d432 TEST_DIR/test-945/file1
+d9d86b794e7d626ccee90392518c9048 TEST_DIR/test-945/file2
+c0dae8b7541aad8ef4ed3b9ffd20e9a3 TEST_DIR/test-945/file3
+094457223ea2751265e617631f5f4aa9 TEST_DIR/test-945/file4
+Files 1-2 do not match
+Files 1-3 do not match
+Files 1-4 do not match
+Check for non-shared extents
@@ -576,3 +576,4 @@
715 dangerous_norepair
716 dangerous_norepair
720 dangerous_norepair
+945 auto quick rw clone