Skip to content

Commit ada06e0

Browse files
author
Umer Saleem
committed
Test for clone, mmap and write for block cloning
For block cloning, if we mmap the cloned file and write from the map into the file, it triggers a panic in dbuf_redirty() on Linux. The same scenario causes data corruption on FreeBSD. Both these issues are fixed under PR#15656 and PR#15665. It would be good to add a test for this scenario in ZTS. The test program and issue was produced by @robn. Signed-off-by: Umer Saleem <[email protected]>
1 parent 07e95b4 commit ada06e0

File tree

8 files changed

+212
-0
lines changed

8 files changed

+212
-0
lines changed

tests/runfiles/common.run

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -71,6 +71,10 @@ tests = ['bclone_crossfs_corner_cases_limited',
7171
tags = ['functional', 'bclone']
7272
timeout = 7200
7373

74+
[tests/functional/block_cloning]
75+
tests = ['block_cloning_clone_mmap_write']
76+
tags = ['functional', 'block_cloning']
77+
7478
[tests/functional/bootfs]
7579
tests = ['bootfs_001_pos', 'bootfs_002_neg', 'bootfs_003_pos',
7680
'bootfs_004_neg', 'bootfs_005_neg', 'bootfs_006_pos', 'bootfs_007_pos',

tests/test-runner/bin/zts-report.py.in

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -287,6 +287,8 @@ elif sys.platform.startswith('linux'):
287287
'bclone/bclone_samefs_data': ['SKIP', cfr_reason],
288288
'bclone/bclone_samefs_embedded': ['SKIP', cfr_reason],
289289
'bclone/bclone_samefs_hole': ['SKIP', cfr_reason],
290+
'block_cloning/block_cloning_clone_mmap_write':
291+
['SKIP', cfr_reason],
290292
'block_cloning/block_cloning_copyfilerange':
291293
['SKIP', cfr_reason],
292294
'block_cloning/block_cloning_copyfilerange_cross_dataset':

tests/zfs-tests/cmd/.gitignore

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@
22
/btree_test
33
/chg_usr_exec
44
/clonefile
5+
/clone_mmap_write
56
/devname2devid
67
/dir_rd_update
78
/draid

tests/zfs-tests/cmd/Makefile.am

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@ scripts_zfs_tests_bindir = $(datadir)/$(PACKAGE)/zfs-tests/bin
33

44
scripts_zfs_tests_bin_PROGRAMS = %D%/chg_usr_exec
55
scripts_zfs_tests_bin_PROGRAMS += %D%/clonefile
6+
scripts_zfs_tests_bin_PROGRAMS += %D%/clone_mmap_write
67
scripts_zfs_tests_bin_PROGRAMS += %D%/cp_files
78
scripts_zfs_tests_bin_PROGRAMS += %D%/ctime
89
scripts_zfs_tests_bin_PROGRAMS += %D%/dir_rd_update
Lines changed: 123 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,123 @@
1+
/*
2+
* CDDL HEADER START
3+
*
4+
* The contents of this file are subject to the terms of the
5+
* Common Development and Distribution License (the "License").
6+
* You may not use this file except in compliance with the License.
7+
*
8+
* You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
9+
* or https://opensource.org/licenses/CDDL-1.0.
10+
* See the License for the specific language governing permissions
11+
* and limitations under the License.
12+
*
13+
* When distributing Covered Code, include this CDDL HEADER in each
14+
* file and include the License file at usr/src/OPENSOLARIS.LICENSE.
15+
* If applicable, add the following below this CDDL HEADER, with the
16+
* fields enclosed by brackets "[]" replaced with your own identifying
17+
* information: Portions Copyright [yyyy] [name of copyright owner]
18+
*
19+
* CDDL HEADER END
20+
*/
21+
22+
/*
23+
* This program clones the file, mmap it, and writes from the map into
24+
* file. This scenario triggers a panic on Linux in dbuf_redirty(),
25+
* which is fixed under PR#15656. On FreeBSD, the same test causes data
26+
* corruption, which is fixed by PR#15665.
27+
*
28+
* It would be good to test for this scenario in ZTS. This program and
29+
* issue was initially produced by @robn.
30+
*/
31+
#ifndef _GNU_SOURCE
32+
#define _GNU_SOURCE
33+
#endif
34+
35+
#include <fcntl.h>
36+
#include <string.h>
37+
#include <stdio.h>
38+
#include <stdlib.h>
39+
#include <unistd.h>
40+
#include <errno.h>
41+
#include <sys/stat.h>
42+
#include <sys/mman.h>
43+
44+
#ifdef __FreeBSD__
45+
#define loff_t off_t
46+
#endif
47+
48+
ssize_t
49+
copy_file_range(int, loff_t *, int, loff_t *, size_t, unsigned int)
50+
__attribute__((weak));
51+
52+
static int
53+
open_file(const char *source)
54+
{
55+
int fd;
56+
if ((fd = open(source, O_RDWR | O_APPEND)) < 0) {
57+
(void) fprintf(stderr, "Error opening %s\n", source);
58+
exit(1);
59+
}
60+
sync();
61+
return (fd);
62+
}
63+
64+
static int
65+
clone_file(int sfd, long long size, const char *dest)
66+
{
67+
int dfd;
68+
69+
if ((dfd = open(dest, O_RDWR | O_CREAT, S_IRUSR | S_IWUSR)) < 0) {
70+
(void) fprintf(stderr, "Error opening %s\n", dest);
71+
exit(1);
72+
}
73+
74+
if (copy_file_range(sfd, 0, dfd, 0, size, 0) < 0) {
75+
(void) fprintf(stderr, "copy_file_range failed\n");
76+
exit(1);
77+
}
78+
79+
return (dfd);
80+
}
81+
82+
static void *
83+
map_file(int fd, long long size)
84+
{
85+
void *p = mmap(NULL, size, PROT_READ, MAP_SHARED, fd, 0);
86+
if (p == MAP_FAILED) {
87+
(void) fprintf(stderr, "mmap failed\n");
88+
exit(1);
89+
}
90+
91+
return (p);
92+
}
93+
94+
static void
95+
map_write(void *p, int fd)
96+
{
97+
if (pwrite(fd, p, 1024*128, 0) < 0) {
98+
(void) fprintf(stderr, "write failed\n");
99+
exit(1);
100+
}
101+
}
102+
103+
int
104+
main(int argc, char **argv)
105+
{
106+
int sfd, dfd;
107+
void *p;
108+
struct stat sb;
109+
if (argc != 3) {
110+
(void) printf("usage: %s <input source file> "
111+
"<clone destination file>\n", argv[0]);
112+
exit(1);
113+
}
114+
sfd = open_file(argv[1]);
115+
if (fstat(sfd, &sb) == -1) {
116+
(void) fprintf(stderr, "fstat failed\n");
117+
exit(1);
118+
}
119+
dfd = clone_file(sfd, sb.st_size, argv[2]);
120+
p = map_file(dfd, sb.st_size);
121+
map_write(p, dfd);
122+
return (0);
123+
}

tests/zfs-tests/include/commands.cfg

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -185,6 +185,7 @@ export ZFSTEST_FILES='badsend
185185
btree_test
186186
chg_usr_exec
187187
clonefile
188+
clone_mmap_write
188189
devname2devid
189190
dir_rd_update
190191
draid

tests/zfs-tests/tests/Makefile.am

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -461,6 +461,7 @@ nobase_dist_datadir_zfs_tests_tests_SCRIPTS += \
461461
functional/bclone/setup.ksh \
462462
functional/block_cloning/cleanup.ksh \
463463
functional/block_cloning/setup.ksh \
464+
functional/block_cloning/block_cloning_clone_mmap_write.ksh \
464465
functional/block_cloning/block_cloning_copyfilerange_cross_dataset.ksh \
465466
functional/block_cloning/block_cloning_copyfilerange_fallback.ksh \
466467
functional/block_cloning/block_cloning_copyfilerange_fallback_same_txg.ksh \
Lines changed: 79 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,79 @@
1+
#!/bin/ksh -p
2+
#
3+
# CDDL HEADER START
4+
#
5+
# The contents of this file are subject to the terms of the
6+
# Common Development and Distribution License (the "License").
7+
# You may not use this file except in compliance with the License.
8+
#
9+
# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
10+
# or https://opensource.org/licenses/CDDL-1.0.
11+
# See the License for the specific language governing permissions
12+
# and limitations under the License.
13+
#
14+
# When distributing Covered Code, include this CDDL HEADER in each
15+
# file and include the License file at usr/src/OPENSOLARIS.LICENSE.
16+
# If applicable, add the following below this CDDL HEADER, with the
17+
# fields enclosed by brackets "[]" replaced with your own identifying
18+
# information: Portions Copyright [yyyy] [name of copyright owner]
19+
#
20+
# CDDL HEADER END
21+
#
22+
23+
. $STF_SUITE/include/libtest.shlib
24+
. $STF_SUITE/tests/functional/block_cloning/block_cloning.kshlib
25+
26+
#
27+
# DESCRIPTION:
28+
# A PANIC is triggered in dbuf_redirty() if we clone a file, mmap it
29+
# and write from the map into the file. PR#15656 fixes this scenario.
30+
# This scenario also causes data corruption on FreeBSD, which is fixed
31+
# by PR#15665.
32+
#
33+
# STRATEGY:
34+
# 1. Create a pool
35+
# 2. Create a test file
36+
# 3. Clone, mmap and write to the file using clone_mmap_write
37+
# 5. Synchronize cached writes
38+
# 6. Verfiy data is correctly written to the disk
39+
#
40+
41+
verify_runnable "global"
42+
43+
if is_linux && [[ $(linux_version) -lt $(linux_version "4.5") ]]; then
44+
log_unsupported "copy_file_range not available before Linux 4.5"
45+
fi
46+
47+
VDIR=$TEST_BASE_DIR/disk-bclone
48+
VDEV="$VDIR/a"
49+
50+
function cleanup
51+
{
52+
datasetexists $TESTPOOL && destroy_pool $TESTPOOL
53+
rm -rf $VDIR
54+
}
55+
56+
log_onexit cleanup
57+
58+
log_assert "Test for clone, mmap and write scenario"
59+
60+
log_must rm -rf $VDIR
61+
log_must mkdir -p $VDIR
62+
log_must truncate -s 1G $VDEV
63+
64+
log_must zpool create -o feature@block_cloning=enabled $TESTPOOL $VDEV
65+
log_must zfs create $TESTPOOL/$TESTFS
66+
67+
log_must dd if=/dev/urandom of=/$TESTPOOL/$TESTFS/file bs=1M count=512
68+
log_must clone_mmap_write /$TESTPOOL/$TESTFS/file /$TESTPOOL/$TESTFS/clone
69+
70+
sync_pool $TESTPOOL
71+
log_must sync
72+
73+
log_must have_same_content /$TESTPOOL/$TESTFS/file /$TESTPOOL/$TESTFS/clone
74+
blocks=$(get_same_blocks $TESTPOOL/$TESTFS file $TESTPOOL/$TESTFS clone)
75+
# FreeBSD's seq(1) leaves a trailing space, remove it with sed(1).
76+
log_must [ "$blocks" = "$(seq -s " " 1 4095 | sed 's/ $//')" ]
77+
78+
log_pass "Clone, mmap and write does not cause data corruption or " \
79+
"trigger panic"

0 commit comments

Comments
 (0)