From e8b333e4d33b7c6f3f1e3761b0e1ac3210275dc8 Mon Sep 17 00:00:00 2001 From: Alexander Motin Date: Thu, 5 Dec 2024 14:48:08 -0500 Subject: [PATCH] Fix false assertion in dmu_tx_dirty_buf() on cloning Same as writes block cloning can increase block size and number of indirection levels. That means it can dirty block 0 at level 0 or at new top indirection level without explicitly holding them. A block cloning test case for large offsets has been added. Reviewed-by: Rob Norris Reviewed-by: Brian Behlendorf Co-authored-by: Ameer Hamza Signed-off-by: Alexander Motin Sponsored by: iXsystems, Inc. Closes #16825 --- module/zfs/dmu_tx.c | 8 +++ tests/runfiles/common.run | 2 +- tests/test-runner/bin/zts-report.py.in | 2 + tests/zfs-tests/tests/Makefile.am | 1 + .../block_cloning/block_cloning_large_offset.ksh | 83 ++++++++++++++++++++++ 5 files changed, 95 insertions(+), 1 deletion(-) create mode 100755 tests/zfs-tests/tests/functional/block_cloning/block_cloning_large_offset.ksh diff --git a/module/zfs/dmu_tx.c b/module/zfs/dmu_tx.c index 6aee7afb6..e4895a6bc 100644 --- a/module/zfs/dmu_tx.c +++ b/module/zfs/dmu_tx.c @@ -800,6 +800,14 @@ dmu_tx_dirty_buf(dmu_tx_t *tx, dmu_buf_impl_t *db) case THT_CLONE: if (blkid >= beginblk && blkid <= endblk) match_offset = TRUE; + /* + * They might have to increase nlevels, + * thus dirtying the new TLIBs. Or the + * might have to change the block size, + * thus dirying the new lvl=0 blk=0. + */ + if (blkid == 0) + match_offset = TRUE; break; default: cmn_err(CE_PANIC, "bad txh_type %d", diff --git a/tests/runfiles/common.run b/tests/runfiles/common.run index fc4adc42d..a69d36df2 100644 --- a/tests/runfiles/common.run +++ b/tests/runfiles/common.run @@ -82,7 +82,7 @@ tests = ['block_cloning_clone_mmap_cached', 'block_cloning_copyfilerange_fallback_same_txg', 'block_cloning_replay', 'block_cloning_replay_encrypted', 'block_cloning_lwb_buffer_overflow', 'block_cloning_clone_mmap_write', - 'block_cloning_rlimit_fsize'] + 'block_cloning_rlimit_fsize', 'block_cloning_large_offset'] tags = ['functional', 'block_cloning'] [tests/functional/bootfs] diff --git a/tests/test-runner/bin/zts-report.py.in b/tests/test-runner/bin/zts-report.py.in index 07ec2c4b6..0bfc64959 100755 --- a/tests/test-runner/bin/zts-report.py.in +++ b/tests/test-runner/bin/zts-report.py.in @@ -339,6 +339,8 @@ elif sys.platform.startswith('linux'): ['SKIP', cfr_reason], 'block_cloning/block_cloning_rlimit_fsize': ['SKIP', cfr_reason], + 'block_cloning/block_cloning_large_offset': + ['SKIP', cfr_reason], 'cli_root/zfs_rename/zfs_rename_002_pos': ['FAIL', known_reason], 'cli_root/zpool_reopen/zpool_reopen_003_pos': ['FAIL', known_reason], 'cp_files/cp_files_002_pos': ['SKIP', cfr_reason], diff --git a/tests/zfs-tests/tests/Makefile.am b/tests/zfs-tests/tests/Makefile.am index 03d797cc4..67630cb56 100644 --- a/tests/zfs-tests/tests/Makefile.am +++ b/tests/zfs-tests/tests/Makefile.am @@ -482,6 +482,7 @@ nobase_dist_datadir_zfs_tests_tests_SCRIPTS += \ functional/block_cloning/block_cloning_replay_encrypted.ksh \ functional/block_cloning/block_cloning_lwb_buffer_overflow.ksh \ functional/block_cloning/block_cloning_rlimit_fsize.ksh \ + functional/block_cloning/block_cloning_large_offset.ksh \ functional/bootfs/bootfs_001_pos.ksh \ functional/bootfs/bootfs_002_neg.ksh \ functional/bootfs/bootfs_003_pos.ksh \ diff --git a/tests/zfs-tests/tests/functional/block_cloning/block_cloning_large_offset.ksh b/tests/zfs-tests/tests/functional/block_cloning/block_cloning_large_offset.ksh new file mode 100755 index 000000000..1d5a2619e --- /dev/null +++ b/tests/zfs-tests/tests/functional/block_cloning/block_cloning_large_offset.ksh @@ -0,0 +1,83 @@ +#!/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: +# Verify that cloning a file at a large offset is possible. +# +# STRATEGY: +# 1. Create dataset. +# 2. Populate the source file with 1024 blocks at 1024 block offset. +# 3. Clone 1024 blocks at a 1024-block offset. +# 4. Compare the cloned file with the original file. +# + +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 + +claim="The first clone at a large offset is functional" + +log_assert $claim + +function cleanup +{ + datasetexists $TESTPOOL && destroy_pool $TESTPOOL +} + +log_onexit cleanup + +# +# 1. Create dataset. +# +log_must zpool create -o feature@block_cloning=enabled $TESTPOOL $DISKS +sync_pool $TESTPOOL + +# +# 2. Populate the source file with 1024 blocks at 1024 block offset. +# +log_must dd if=/dev/urandom of=/$TESTPOOL/file1 \ + oflag=sync bs=128k count=1024 seek=1024 +sync_pool $TESTPOOL + +# +# 3. Clone 1024 blocks at a 1024-block offset. +# +log_must clonefile -f /$TESTPOOL/file1 /$TESTPOOL/file2 134217728 134217728 \ + 134217728 +sync_pool $TESTPOOL + +# +# 4. Compare the cloned file with the original file. +# +log_must have_same_content /$TESTPOOL/file1 /$TESTPOOL/file2 +typeset blocks=$(get_same_blocks $TESTPOOL file1 $TESTPOOL file2) + +# FreeBSD's seq(1) leaves a trailing space, remove it with sed(1). +log_must [ "$blocks" = "$(seq -s " " 0 1023 | sed 's/ $//')" ] + +log_pass $claim -- 2.11.4.GIT