generic: check reflink multiple mmap write

Add a test to make sure that we can handle multiple memory mappings to a
physical storage extent shared by multiple files, and that we can handle
the copy on write operation without error.  Make sure we can also handle
mappings at different offsets in the page cache.

Signed-off-by: Darrick J. Wong <darrick.wong@oracle.com>
Reviewed-by: Christoph Hellwig <hch@lst.de>
Signed-off-by: Eryu Guan <guaneryu@gmail.com>
This commit is contained in:
Darrick J. Wong
2019-10-11 14:41:20 -07:00
committed by Eryu Guan
parent 96b5068aa2
commit 22b06b010c
6 changed files with 294 additions and 1 deletions
+1
View File
@@ -96,6 +96,7 @@
/src/metaperf
/src/mkswap
/src/mmapcat
/src/mmap-write-concurrent
/src/multi_open_unlink
/src/nametest
/src/nsexec
+1 -1
View File
@@ -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 \
+155
View File
@@ -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;
}
+105
View File
@@ -0,0 +1,105 @@
#! /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
+31
View File
@@ -0,0 +1,31 @@
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
+1
View File
@@ -580,3 +580,4 @@
575 auto quick verity
576 auto quick verity encrypt
577 auto quick verity
578 auto quick rw clone