Mmap 2 pages of file, write 64 bytes to the first and second pages, copy the data from the first page and then second page to the second page offset with $pagesize - 64. Verify the data at the end. +-----------------------+ | (copy) | | V +---------------+---------------+------------ |AAAA| ........ |AAAA| ... |AAAA|AAAA| +---------------+---------------+------------ | ^ | (copy) | +------------+ This's also a regression test cover kernel commit: 4f06dd92b5d0 ("fuse: fix write deadlock") Signed-off-by: Zorro Lang <zlang@xxxxxxxxxx> --- .gitignore | 1 + src/Makefile | 3 +- src/t_mmap_writev_overlap.c | 134 ++++++++++++++++++++++++++++++++++++ tests/generic/637 | 57 +++++++++++++++ tests/generic/637.out | 2 + tests/generic/group | 1 + 6 files changed, 197 insertions(+), 1 deletion(-) create mode 100644 src/t_mmap_writev_overlap.c create mode 100755 tests/generic/637 create mode 100644 tests/generic/637.out diff --git a/.gitignore b/.gitignore index 4cc9c807..1e56d7ca 100644 --- a/.gitignore +++ b/.gitignore @@ -142,6 +142,7 @@ /src/t_mmap_stale_pmd /src/t_mmap_write_ro /src/t_mmap_writev +/src/t_mmap_writev_overlap /src/t_mtab /src/t_ofd_locks /src/t_open_tmpfiles diff --git a/src/Makefile b/src/Makefile index cc0b9579..1279e4b9 100644 --- a/src/Makefile +++ b/src/Makefile @@ -17,7 +17,8 @@ TARGETS = dirstress fill fill2 getpagesize holes lstat64 \ 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_mmap_collision mmap-write-concurrent \ - t_get_file_time t_create_short_dirs t_create_long_dirs t_enospc + t_get_file_time t_create_short_dirs t_create_long_dirs t_enospc \ + t_mmap_writev_overlap 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/t_mmap_writev_overlap.c b/src/t_mmap_writev_overlap.c new file mode 100644 index 00000000..e327e730 --- /dev/null +++ b/src/t_mmap_writev_overlap.c @@ -0,0 +1,134 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * Copyright (c) 2021 RedHat Inc. All Rights Reserved. + * + * mmap a file, alloc blocks, reading&writing blocks with overlapping. For example: + * + * |<--- block --->|<--- block --->| + * len len + * +---------------+---------------+ + * |AAAA| ........ |AAAA| ........ | + * +---------------+---------------+ + * | | + * | `------------+ + * `-----------------------+ | + * | | + * V V + * +---------------+---------------+----+ + * |AAAA| ........ |AAAA| ... |AAAA|AAAA| + * +---------------+---------------+----+ + */ +#include <sys/types.h> +#include <fcntl.h> +#include <stdio.h> +#include <stdlib.h> +#include <errno.h> +#include <sys/mman.h> +#include <string.h> +#include <sys/uio.h> +#include <unistd.h> + +void usage(char *progname) +{ + fprintf(stderr, "usage: %s [-b blocksize ] [-c count] [-l length] filename\n" + "\tmmap $count * $blocksize bytes memory, pwritev $length bytes in each block. blocksize=4096, count=2, length=12 by default.\n" + "e.g: %s -b 4096 -c 2 -l 12 filename\n", + progname, progname); + exit(1); +} + +int main(int argc, char **argv) +{ + char *filename = NULL; + size_t bsize = 4096; + size_t count = 2; + size_t length = 12; + int fd, i, c; + void *base; + char *buf, *cmp_buf; + struct iovec *iov; + int ret = 0; + + while ((c = getopt(argc, argv, "b:l:c:")) != -1) { + char *endp; + + switch (c) { + case 'b': + bsize = strtoul(optarg, &endp, 0); + break; + case 'c': + count = strtoul(optarg, &endp, 0); + break; + case 'l': + length = strtoul(optarg, &endp, 0); + break; + default: + usage(argv[0]); + } + } + + if (optind == argc - 1) + filename = argv[optind]; + else + usage(argv[0]); + + if (length >= bsize) { + printf("-l length must be less than -b blocksize\n"); + usage(argv[0]); + } + + fd = open(filename, O_RDWR); + if (fd == -1) { + fprintf(stderr, "open %s failed:%s\n", filename, strerror(errno)); + exit(1); + } + base = mmap(NULL, bsize * count, PROT_READ|PROT_WRITE, MAP_SHARED, fd, 0); + if (base == MAP_FAILED) { + fprintf(stderr, "mmap failed %s\n", strerror(errno)); + exit(1); + } + + /* Write each of blocks */ + buf = malloc(length); + memset(buf, 0xAA, length); + for (i=0; i<count; i++) { + ret = pwrite(fd, buf, length, i * bsize); + if (ret == -1) { + fprintf(stderr, "pwrite failed %s\n", strerror(errno)); + exit(1); + } + } + + /* Copy from the beginning of each blocks ... */ + iov = malloc(sizeof(struct iovec) * count); + for (i=0; i<count; i++) { + iov[i].iov_base = base + i * bsize; + iov[i].iov_len = length; + } + /* ... Write to the last block with offset ($bsize - $length) */ + ret = pwritev(fd, iov, count, bsize * count - length); + if (ret == -1) { + fprintf(stderr, "pwritev failed %s\n", strerror(errno)); + exit(1); + } + + /* Verify data */ + cmp_buf = malloc(length); + for (i=0; i<count; i++) { + ret = pread(fd, cmp_buf, length, bsize * count + (i - 1) * length); + if (ret == -1) { + fprintf(stderr, "pread failed %s\n", strerror(errno)); + exit(1); + } + if (memcmp(buf, cmp_buf, length)) + printf("Find corruption\n"); + } + + munmap(base, bsize * count); + free(buf); + free(cmp_buf); + free(iov); + close(fd); + + return 0; +} diff --git a/tests/generic/637 b/tests/generic/637 new file mode 100755 index 00000000..dac2bf2c --- /dev/null +++ b/tests/generic/637 @@ -0,0 +1,57 @@ +#! /bin/bash +# SPDX-License-Identifier: GPL-2.0 +# Copyright (c) 2021 Red Hat Inc. All Rights Reserved. +# +# FS QA Test 637 +# +# This case mmaps several pages of a file, alloc pages, copy data with pages +# overlapping, e.g: +# +-----------------------+ +# | (copy) | +# | V +# +---------------+---------------+------------ +# |AAAA| ........ |AAAA| ... |AAAA|AAAA| +# +---------------+---------------+------------ +# | ^ +# | (copy) | +# +------------+ +# +# This's a regression test cover kernel commit: +# 4f06dd92b5d0 ("fuse: fix write deadlock") +# +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 -f $tmp.* +} + +# get standard environment, filters and checks +. ./common/rc +. ./common/filter + +# remove previous $seqres.full before test +rm -f $seqres.full + +# real QA test starts here +_supported_fs generic +_require_test +_require_test_program "t_mmap_writev_overlap" + +pagesize=`getconf PAGE_SIZE` +testfile=$TEST_DIR/mmap-writev-overlap +$XFS_IO_PROG -f -c "truncate 0" $testfile +$here/src/t_mmap_writev_overlap -b $pagesize -c 2 -l 64 $testfile + +echo "Silence is golden" +# success, all done +status=0 +exit diff --git a/tests/generic/637.out b/tests/generic/637.out new file mode 100644 index 00000000..55a3d825 --- /dev/null +++ b/tests/generic/637.out @@ -0,0 +1,2 @@ +QA output created by 637 +Silence is golden diff --git a/tests/generic/group b/tests/generic/group index 105763c4..b9614c69 100644 --- a/tests/generic/group +++ b/tests/generic/group @@ -639,3 +639,4 @@ 634 auto quick atime bigtime 635 auto quick atime bigtime shutdown 636 auto quick swap +637 auto quick rw -- 2.31.1