diff --git a/src/Makefile b/src/Makefile
index ef7cfa63..5bc33e77 100644
--- a/src/Makefile
+++ b/src/Makefile
@@ -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 \
diff --git a/src/mmap-write-concurrent.c b/src/mmap-write-concurrent.c
new file mode 100644
index 00000000..38364de8
--- /dev/null
+++ b/src/mmap-write-concurrent.c
@@ -0,0 +1,155 @@
+// 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>
+
+struct file_info {
+	char			*mapping;
+	off_t			file_offset;
+	off_t			file_length;
+	int			fd;
+};
+
+int
+main(
+	int			argc,
+	char			*argv[])
+{
+	struct file_info	*fi;
+	size_t			length;
+	char			*endptr;
+	unsigned int		nr_files;
+	unsigned int		i;
+	char			*buf;
+	int			ret;
+
+	if (argc < 4) {
+		printf("Usage: %s len offset file [offset file]...\n", argv[0]);
+		return 1;
+	}
+
+	/* Parse mwrite length. */
+	errno = 0;
+	length = strtoul(argv[1], &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;
+	}
+
+	/* Allocate file info */
+	nr_files = (argc - 2) / 2;
+
+	fi = calloc(nr_files, sizeof(struct file_info));
+	if (!fi) {
+		perror("calloc file info");
+		return 1;
+	}
+
+	buf = malloc(length);
+	if (!buf) {
+		perror("malloc buf");
+		return 1;
+	}
+
+	for (i = 0; i < nr_files; i++) {
+		struct stat	statbuf;
+		char		*offset = argv[((i + 1) * 2)];
+		char		*fname = argv[((i + 1) * 2) + 1];
+
+		/* Open file, create mapping for the range we want. */
+		fi[i].fd = open(fname, O_RDWR);
+		if (fi[i].fd < 0) {
+			perror(fname);
+			return 1;
+		}
+
+		/* Parse mwrite offset */
+		errno = 0;
+		fi[i].file_offset = strtoul(offset, &endptr, 0);
+		if (errno) {
+			perror(argv[1]);
+			return 1;
+		}
+
+		/* Remember file size */
+		ret = fstat(fi[i].fd, &statbuf);
+		if (ret) {
+			perror(fname);
+			return 1;
+		}
+		fi[i].file_length = statbuf.st_size;
+
+		if (fi[i].file_offset + length > fi[i].file_length) {
+			fprintf(stderr, "%s: file must be %llu bytes\n",
+				fname,
+				(unsigned long long)fi[i].file_offset + length);
+			return 1;
+		}
+
+		/* Create the mapping */
+		fi[i].mapping = mmap(NULL, fi[i].file_length,
+				PROT_READ | PROT_WRITE, MAP_SHARED,
+				fi[i].fd, 0);
+		if (fi[i].mapping == 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, fi[i].mapping + fi[i].file_offset, length);
+	}
+
+	/* Dirty the same region in each file to test COW. */
+	for (i = 0; i < nr_files; i++) {
+		memset(buf, 0x62 + i, length);
+		memcpy(fi[i].mapping + fi[i].file_offset, buf, length);
+	}
+	for (i = 0; i < nr_files; i++) {
+		ret = msync(fi[i].mapping, fi[i].file_offset + length, MS_SYNC);
+		if (ret) {
+			perror("msync");
+			return 1;
+		}
+	}
+
+	/* Close everything. */
+	for (i = 0; i < nr_files; i++) {
+		ret = munmap(fi[i].mapping, fi[i].file_length);
+		if (ret) {
+			perror("munmap");
+			return 1;
+		}
+
+		ret = close(fi[i].fd);
+		if (ret) {
+			perror("close");
+			return 1;
+		}
+	}
+
+	/* Free everything. */
+	free(buf);
+	free(fi);
+
+	return 0;
+}
diff --git a/tests/generic/945 b/tests/generic/945
new file mode 100755
index 00000000..38b5883c
--- /dev/null
+++ b/tests/generic/945
@@ -0,0 +1,104 @@
+#! /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() {
+	for i in $(seq 1 8); do
+		md5sum $testdir/file$i | _filter_test_dir
+		echo $testdir/file$i >> $seqres.full
+		od -tx1 -Ad -c $testdir/file$i >> $seqres.full
+	done
+}
+
+testdir=$TEST_DIR/test-$seq
+rm -rf $testdir
+mkdir $testdir
+
+echo "Create the original files"
+blksz=65536
+filesz=$((blksz * 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
+_reflink_range $testdir/file1 0 $testdir/file5 $blksz $filesz >> $seqres.full
+_reflink_range $testdir/file1 0 $testdir/file6 $((blksz * 2)) $filesz >> $seqres.full
+_reflink_range $testdir/file1 0 $testdir/file7 $((blksz * 3)) $filesz >> $seqres.full
+_reflink_range $testdir/file1 0 $testdir/file8 $((blksz * 4)) $filesz >> $seqres.full
+_test_cycle_mount
+
+echo "Compare files before cow" | tee -a $seqres.full
+compare
+
+echo "mwrite all copies" | tee -a $seqres.full
+off=$(( (filesz / 2) - 168 ))
+len=337
+./src/mmap-write-concurrent $len \
+		$off $testdir/file1 \
+		$off $testdir/file2 \
+		$off $testdir/file3 \
+		$off $testdir/file4 \
+		$((off + blksz)) $testdir/file5 \
+		$((off + (blksz * 2))) $testdir/file6 \
+		$((off + (blksz * 3))) $testdir/file7 \
+		$((off + (blksz * 4))) $testdir/file8 \
+		168 $testdir/file1 \
+		$((blksz - 168)) $testdir/file2 \
+		$((filesz - 777)) $testdir/file3 \
+		$(((blksz * 3) - 168)) $testdir/file4 \
+
+
+echo "Compare files before remount" | tee -a $seqres.full
+compare
+_test_cycle_mount
+
+echo "Compare files after remount" | tee -a $seqres.full
+compare
+
+echo "Check for non-shared extents" | tee -a $seqres.full
+$FILEFRAG_PROG -v $testdir/file1 $testdir/file2 $testdir/file3 $testdir/file4 \
+		  $testdir/file5 $testdir/file6 $testdir/file7 $testdir/file8 \
+		  | 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
diff --git a/tests/generic/945.out b/tests/generic/945.out
new file mode 100644
index 00000000..ad1e21ff
--- /dev/null
+++ b/tests/generic/945.out
@@ -0,0 +1,31 @@
+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
+74e6b9b1a03fdf09293c089b002800f8  TEST_DIR/test-945/file5
+c14f20b97155e3fc11a17532d02ad9df  TEST_DIR/test-945/file6
+22eb46e0f4a3742c8d86346845b7bc80  TEST_DIR/test-945/file7
+4d292f06cec9d3f1bece4822cd5ef532  TEST_DIR/test-945/file8
+mwrite all copies
+Compare files before remount
+c1b46135a2620ae6da21bbfd4cbb3cba  TEST_DIR/test-945/file1
+16f0bc0f97c42d2ad903c519095b8126  TEST_DIR/test-945/file2
+eb71e3135ca2abf33bb9081e2b49a876  TEST_DIR/test-945/file3
+2678dfcb77bed4dc29e19836bef82e5b  TEST_DIR/test-945/file4
+2802d75dbee4f29d62124aa7b473edca  TEST_DIR/test-945/file5
+acd58cf3d33ef905e26800a0e049223c  TEST_DIR/test-945/file6
+1b68d203e5a1c1b45a9510bedcd1e126  TEST_DIR/test-945/file7
+9479709b697ced2e3a57c17bc1b97373  TEST_DIR/test-945/file8
+Compare files after remount
+c1b46135a2620ae6da21bbfd4cbb3cba  TEST_DIR/test-945/file1
+16f0bc0f97c42d2ad903c519095b8126  TEST_DIR/test-945/file2
+eb71e3135ca2abf33bb9081e2b49a876  TEST_DIR/test-945/file3
+2678dfcb77bed4dc29e19836bef82e5b  TEST_DIR/test-945/file4
+2802d75dbee4f29d62124aa7b473edca  TEST_DIR/test-945/file5
+acd58cf3d33ef905e26800a0e049223c  TEST_DIR/test-945/file6
+1b68d203e5a1c1b45a9510bedcd1e126  TEST_DIR/test-945/file7
+9479709b697ced2e3a57c17bc1b97373  TEST_DIR/test-945/file8
+Check for non-shared extents
diff --git a/tests/generic/group b/tests/generic/group
index 4584667f..f77c5b21 100644
--- a/tests/generic/group
+++ b/tests/generic/group
@@ -576,3 +576,4 @@
 715 dangerous_norepair
 716 dangerous_norepair
 720 dangerous_norepair
+945 auto quick rw clone
