mirror of
				https://git.proxmox.com/git/mirror_zfs.git
				synced 2025-10-26 18:05:04 +03:00 
			
		
		
		
	ZTS: 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. Reviewed-by: Pawel Jakub Dawidek <pawel@dawidek.net> Reviewed-by: Brian Behlendorf <behlendorf1@llnl.gov> Reviewed-by: Alexander Motin <mav@FreeBSD.org> Reviewed-by: Ameer Hamza <ahamza@ixsystems.com> Signed-off-by: Umer Saleem <usaleem@ixsystems.com> Closes #15717
This commit is contained in:
		
							parent
							
								
									83c0ccc7cf
								
							
						
					
					
						commit
						d2f7b2e557
					
				| @ -79,7 +79,7 @@ tests = ['block_cloning_copyfilerange', 'block_cloning_copyfilerange_partial', | ||||
|     'block_cloning_cross_enc_dataset', | ||||
|     'block_cloning_copyfilerange_fallback_same_txg', | ||||
|     'block_cloning_replay', 'block_cloning_replay_encrypted', | ||||
|     'block_cloning_lwb_buffer_overflow'] | ||||
|     'block_cloning_lwb_buffer_overflow', 'block_cloning_clone_mmap_write'] | ||||
| tags = ['functional', 'block_cloning'] | ||||
| 
 | ||||
| [tests/functional/bootfs] | ||||
|  | ||||
| @ -287,6 +287,8 @@ elif sys.platform.startswith('linux'): | ||||
|         'bclone/bclone_samefs_data': ['SKIP', cfr_reason], | ||||
|         'bclone/bclone_samefs_embedded': ['SKIP', cfr_reason], | ||||
|         'bclone/bclone_samefs_hole': ['SKIP', cfr_reason], | ||||
|         'block_cloning/block_cloning_clone_mmap_write': | ||||
|             ['SKIP', cfr_reason], | ||||
|         'block_cloning/block_cloning_copyfilerange': | ||||
|             ['SKIP', cfr_reason], | ||||
|         'block_cloning/block_cloning_copyfilerange_cross_dataset': | ||||
|  | ||||
							
								
								
									
										1
									
								
								tests/zfs-tests/cmd/.gitignore
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										1
									
								
								tests/zfs-tests/cmd/.gitignore
									
									
									
									
										vendored
									
									
								
							| @ -2,6 +2,7 @@ | ||||
| /btree_test | ||||
| /chg_usr_exec | ||||
| /clonefile | ||||
| /clone_mmap_write | ||||
| /devname2devid | ||||
| /dir_rd_update | ||||
| /draid | ||||
|  | ||||
| @ -3,6 +3,7 @@ scripts_zfs_tests_bindir = $(datadir)/$(PACKAGE)/zfs-tests/bin | ||||
| 
 | ||||
| scripts_zfs_tests_bin_PROGRAMS  = %D%/chg_usr_exec | ||||
| scripts_zfs_tests_bin_PROGRAMS += %D%/clonefile | ||||
| scripts_zfs_tests_bin_PROGRAMS += %D%/clone_mmap_write | ||||
| scripts_zfs_tests_bin_PROGRAMS += %D%/cp_files | ||||
| scripts_zfs_tests_bin_PROGRAMS += %D%/ctime | ||||
| scripts_zfs_tests_bin_PROGRAMS += %D%/dir_rd_update | ||||
|  | ||||
							
								
								
									
										123
									
								
								tests/zfs-tests/cmd/clone_mmap_write.c
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										123
									
								
								tests/zfs-tests/cmd/clone_mmap_write.c
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,123 @@ | ||||
| /*
 | ||||
|  * CDDL HEADER START | ||||
|  * | ||||
|  * The contents of this file are subject to the terms of the | ||||
|  * Common Development and Distribution License (the "License"). | ||||
|  * You may not use this file except in compliance with the License. | ||||
|  * | ||||
|  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE | ||||
|  * or https://opensource.org/licenses/CDDL-1.0.
 | ||||
|  * See the License for the specific language governing permissions | ||||
|  * and limitations under the License. | ||||
|  * | ||||
|  * When distributing Covered Code, include this CDDL HEADER in each | ||||
|  * file and include the License file at usr/src/OPENSOLARIS.LICENSE. | ||||
|  * If applicable, add the following below this CDDL HEADER, with the | ||||
|  * fields enclosed by brackets "[]" replaced with your own identifying | ||||
|  * information: Portions Copyright [yyyy] [name of copyright owner] | ||||
|  * | ||||
|  * CDDL HEADER END | ||||
|  */ | ||||
| 
 | ||||
| /*
 | ||||
|  * This program clones the file, mmap it, and writes from the map into | ||||
|  * file. This scenario triggers a panic on Linux in dbuf_redirty(), | ||||
|  * which is fixed under PR#15656. On FreeBSD, the same test causes data | ||||
|  * corruption, which is fixed by PR#15665. | ||||
|  * | ||||
|  * It would be good to test for this scenario in ZTS. This program and | ||||
|  * issue was initially produced by @robn. | ||||
|  */ | ||||
| #ifndef _GNU_SOURCE | ||||
| #define	_GNU_SOURCE | ||||
| #endif | ||||
| 
 | ||||
| #include <fcntl.h> | ||||
| #include <string.h> | ||||
| #include <stdio.h> | ||||
| #include <stdlib.h> | ||||
| #include <unistd.h> | ||||
| #include <errno.h> | ||||
| #include <sys/stat.h> | ||||
| #include <sys/mman.h> | ||||
| 
 | ||||
| #ifdef __FreeBSD__ | ||||
| #define	loff_t	off_t | ||||
| #endif | ||||
| 
 | ||||
| ssize_t | ||||
| copy_file_range(int, loff_t *, int, loff_t *, size_t, unsigned int) | ||||
|     __attribute__((weak)); | ||||
| 
 | ||||
| static int | ||||
| open_file(const char *source) | ||||
| { | ||||
| 	int fd; | ||||
| 	if ((fd = open(source, O_RDWR | O_APPEND)) < 0) { | ||||
| 		(void) fprintf(stderr, "Error opening %s\n", source); | ||||
| 		exit(1); | ||||
| 	} | ||||
| 	sync(); | ||||
| 	return (fd); | ||||
| } | ||||
| 
 | ||||
| static int | ||||
| clone_file(int sfd, long long size, const char *dest) | ||||
| { | ||||
| 	int dfd; | ||||
| 
 | ||||
| 	if ((dfd = open(dest, O_RDWR | O_CREAT, S_IRUSR | S_IWUSR)) < 0) { | ||||
| 		(void) fprintf(stderr, "Error opening %s\n", dest); | ||||
| 		exit(1); | ||||
| 	} | ||||
| 
 | ||||
| 	if (copy_file_range(sfd, 0, dfd, 0, size, 0) < 0) { | ||||
| 		(void) fprintf(stderr, "copy_file_range failed\n"); | ||||
| 		exit(1); | ||||
| 	} | ||||
| 
 | ||||
| 	return (dfd); | ||||
| } | ||||
| 
 | ||||
| static void * | ||||
| map_file(int fd, long long size) | ||||
| { | ||||
| 	void *p = mmap(NULL, size, PROT_READ, MAP_SHARED, fd, 0); | ||||
| 	if (p == MAP_FAILED) { | ||||
| 		(void) fprintf(stderr, "mmap failed\n"); | ||||
| 		exit(1); | ||||
| 	} | ||||
| 
 | ||||
| 	return (p); | ||||
| } | ||||
| 
 | ||||
| static void | ||||
| map_write(void *p, int fd) | ||||
| { | ||||
| 	if (pwrite(fd, p, 1024*128, 0) < 0) { | ||||
| 		(void) fprintf(stderr, "write failed\n"); | ||||
| 		exit(1); | ||||
| 	} | ||||
| } | ||||
| 
 | ||||
| int | ||||
| main(int argc, char **argv) | ||||
| { | ||||
| 	int sfd, dfd; | ||||
| 	void *p; | ||||
| 	struct stat sb; | ||||
| 	if (argc != 3) { | ||||
| 		(void) printf("usage: %s <input source file> " | ||||
| 		    "<clone destination file>\n", argv[0]); | ||||
| 		exit(1); | ||||
| 	} | ||||
| 	sfd = open_file(argv[1]); | ||||
| 	if (fstat(sfd, &sb) == -1) { | ||||
| 		(void) fprintf(stderr, "fstat failed\n"); | ||||
| 		exit(1); | ||||
| 	} | ||||
| 	dfd = clone_file(sfd, sb.st_size, argv[2]); | ||||
| 	p = map_file(dfd, sb.st_size); | ||||
| 	map_write(p, dfd); | ||||
| 	return (0); | ||||
| } | ||||
| @ -185,6 +185,7 @@ export ZFSTEST_FILES='badsend | ||||
|     btree_test | ||||
|     chg_usr_exec | ||||
|     clonefile | ||||
|     clone_mmap_write | ||||
|     devname2devid | ||||
|     dir_rd_update | ||||
|     draid | ||||
|  | ||||
| @ -461,6 +461,7 @@ nobase_dist_datadir_zfs_tests_tests_SCRIPTS += \ | ||||
| 	functional/bclone/setup.ksh \
 | ||||
| 	functional/block_cloning/cleanup.ksh \
 | ||||
| 	functional/block_cloning/setup.ksh \
 | ||||
| 	functional/block_cloning/block_cloning_clone_mmap_write.ksh \
 | ||||
| 	functional/block_cloning/block_cloning_copyfilerange_cross_dataset.ksh \
 | ||||
| 	functional/block_cloning/block_cloning_copyfilerange_fallback.ksh \
 | ||||
| 	functional/block_cloning/block_cloning_copyfilerange_fallback_same_txg.ksh \
 | ||||
|  | ||||
| @ -0,0 +1,79 @@ | ||||
| #!/bin/ksh -p | ||||
| # | ||||
| # CDDL HEADER START | ||||
| # | ||||
| # The contents of this file are subject to the terms of the | ||||
| # Common Development and Distribution License (the "License"). | ||||
| # You may not use this file except in compliance with the License. | ||||
| # | ||||
| # You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE | ||||
| # or https://opensource.org/licenses/CDDL-1.0. | ||||
| # See the License for the specific language governing permissions | ||||
| # and limitations under the License. | ||||
| # | ||||
| # When distributing Covered Code, include this CDDL HEADER in each | ||||
| # file and include the License file at usr/src/OPENSOLARIS.LICENSE. | ||||
| # If applicable, add the following below this CDDL HEADER, with the | ||||
| # fields enclosed by brackets "[]" replaced with your own identifying | ||||
| # information: Portions Copyright [yyyy] [name of copyright owner] | ||||
| # | ||||
| # CDDL HEADER END | ||||
| # | ||||
| 
 | ||||
| . $STF_SUITE/include/libtest.shlib | ||||
| . $STF_SUITE/tests/functional/block_cloning/block_cloning.kshlib | ||||
| 
 | ||||
| # | ||||
| # DESCRIPTION: | ||||
| #	A PANIC is triggered in dbuf_redirty() if we clone a file, mmap it | ||||
| #	and write from the map into the file. PR#15656 fixes this scenario. | ||||
| #	This scenario also causes data corruption on FreeBSD, which is fixed | ||||
| #	by PR#15665. | ||||
| # | ||||
| # STRATEGY: | ||||
| #	1. Create a pool | ||||
| #	2. Create a test file  | ||||
| #	3. Clone, mmap and write to the file using clone_mmap_write | ||||
| #	5. Synchronize cached writes | ||||
| #	6. Verfiy data is correctly written to the disk | ||||
| # | ||||
| 
 | ||||
| verify_runnable "global" | ||||
| 
 | ||||
| if is_linux && [[ $(linux_version) -lt $(linux_version "4.5") ]]; then | ||||
|   log_unsupported "copy_file_range not available before Linux 4.5" | ||||
| fi | ||||
| 
 | ||||
| VDIR=$TEST_BASE_DIR/disk-bclone | ||||
| VDEV="$VDIR/a" | ||||
| 
 | ||||
| function cleanup | ||||
| { | ||||
| 	datasetexists $TESTPOOL && destroy_pool $TESTPOOL | ||||
| 	rm -rf $VDIR | ||||
| } | ||||
| 
 | ||||
| log_onexit cleanup | ||||
| 
 | ||||
| log_assert "Test for clone, mmap and write scenario" | ||||
| 
 | ||||
| log_must rm -rf $VDIR | ||||
| log_must mkdir -p $VDIR | ||||
| log_must truncate -s 1G $VDEV | ||||
| 
 | ||||
| log_must zpool create -o feature@block_cloning=enabled $TESTPOOL $VDEV | ||||
| log_must zfs create $TESTPOOL/$TESTFS | ||||
| 
 | ||||
| log_must dd if=/dev/urandom of=/$TESTPOOL/$TESTFS/file bs=1M count=512 | ||||
| log_must clone_mmap_write /$TESTPOOL/$TESTFS/file /$TESTPOOL/$TESTFS/clone | ||||
| 
 | ||||
| sync_pool $TESTPOOL | ||||
| log_must sync | ||||
| 
 | ||||
| log_must have_same_content /$TESTPOOL/$TESTFS/file /$TESTPOOL/$TESTFS/clone | ||||
| blocks=$(get_same_blocks $TESTPOOL/$TESTFS file $TESTPOOL/$TESTFS clone) | ||||
| # FreeBSD's seq(1) leaves a trailing space, remove it with sed(1). | ||||
| log_must [ "$blocks" = "$(seq -s " " 1 4095 | sed 's/ $//')" ] | ||||
| 
 | ||||
| log_pass "Clone, mmap and write does not cause data corruption or " \ | ||||
| 	"trigger panic" | ||||
		Loading…
	
		Reference in New Issue
	
	Block a user
	 Umer Saleem
						Umer Saleem