/src/metaperf
/src/mkswap
/src/mmapcat
+/src/mmap-write-concurrent
/src/multi_open_unlink
/src/nametest
/src/nsexec
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 \
--- /dev/null
+// 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;
+}
--- /dev/null
+#! /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. 578
+#
+# 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_test_program "mmap-write-concurrent"
+_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
+$here/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
--- /dev/null
+QA output created by 578
+Create the original files
+Compare files before cow
+c946b71bb69c07daf25470742c967e7c TEST_DIR/test-578/file1
+c946b71bb69c07daf25470742c967e7c TEST_DIR/test-578/file2
+c946b71bb69c07daf25470742c967e7c TEST_DIR/test-578/file3
+c946b71bb69c07daf25470742c967e7c TEST_DIR/test-578/file4
+74e6b9b1a03fdf09293c089b002800f8 TEST_DIR/test-578/file5
+c14f20b97155e3fc11a17532d02ad9df TEST_DIR/test-578/file6
+22eb46e0f4a3742c8d86346845b7bc80 TEST_DIR/test-578/file7
+4d292f06cec9d3f1bece4822cd5ef532 TEST_DIR/test-578/file8
+mwrite all copies
+Compare files before remount
+c1b46135a2620ae6da21bbfd4cbb3cba TEST_DIR/test-578/file1
+16f0bc0f97c42d2ad903c519095b8126 TEST_DIR/test-578/file2
+eb71e3135ca2abf33bb9081e2b49a876 TEST_DIR/test-578/file3
+2678dfcb77bed4dc29e19836bef82e5b TEST_DIR/test-578/file4
+2802d75dbee4f29d62124aa7b473edca TEST_DIR/test-578/file5
+acd58cf3d33ef905e26800a0e049223c TEST_DIR/test-578/file6
+1b68d203e5a1c1b45a9510bedcd1e126 TEST_DIR/test-578/file7
+9479709b697ced2e3a57c17bc1b97373 TEST_DIR/test-578/file8
+Compare files after remount
+c1b46135a2620ae6da21bbfd4cbb3cba TEST_DIR/test-578/file1
+16f0bc0f97c42d2ad903c519095b8126 TEST_DIR/test-578/file2
+eb71e3135ca2abf33bb9081e2b49a876 TEST_DIR/test-578/file3
+2678dfcb77bed4dc29e19836bef82e5b TEST_DIR/test-578/file4
+2802d75dbee4f29d62124aa7b473edca TEST_DIR/test-578/file5
+acd58cf3d33ef905e26800a0e049223c TEST_DIR/test-578/file6
+1b68d203e5a1c1b45a9510bedcd1e126 TEST_DIR/test-578/file7
+9479709b697ced2e3a57c17bc1b97373 TEST_DIR/test-578/file8
+Check for non-shared extents
575 auto quick verity
576 auto quick verity encrypt
577 auto quick verity
+578 auto quick rw clone