--- /dev/null
+// 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;
+}
--- /dev/null
+#! /bin/bash
+# SPDX-License-Identifier: GPL-2.0
+# Copyright (c) 2021 Red Hat Inc. All Rights Reserved.
+#
+# FS QA Test 638
+#
+# 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