diff --git a/module/zfs/zio.c b/module/zfs/zio.c index 5d7ed6d582..d888a584a9 100644 --- a/module/zfs/zio.c +++ b/module/zfs/zio.c @@ -2778,7 +2778,7 @@ zio_write_gang_member_ready(zio_t *zio) ASSERT3U(zio->io_prop.zp_copies, ==, gio->io_prop.zp_copies); ASSERT3U(zio->io_prop.zp_copies, <=, BP_GET_NDVAS(zio->io_bp)); ASSERT3U(pio->io_prop.zp_copies, <=, BP_GET_NDVAS(pio->io_bp)); - ASSERT3U(BP_GET_NDVAS(zio->io_bp), <=, BP_GET_NDVAS(pio->io_bp)); + VERIFY3U(BP_GET_NDVAS(zio->io_bp), <=, BP_GET_NDVAS(pio->io_bp)); mutex_enter(&pio->io_lock); for (int d = 0; d < BP_GET_NDVAS(zio->io_bp); d++) { @@ -2816,18 +2816,20 @@ zio_write_gang_block(zio_t *pio, metaslab_class_t *mc) uint64_t resid = pio->io_size; uint64_t lsize; int copies = gio->io_prop.zp_copies; - int gbh_copies; zio_prop_t zp; int error; boolean_t has_data = !(pio->io_flags & ZIO_FLAG_NODATA); /* - * encrypted blocks need DVA[2] free so encrypted gang headers can't - * have a third copy. + * If one copy was requested, store 2 copies of the GBH, so that we + * can still traverse all the data (e.g. to free or scrub) even if a + * block is damaged. Note that we can't store 3 copies of the GBH in + * all cases, e.g. with encryption, which uses DVA[2] for the IV+salt. */ - gbh_copies = MIN(copies + 1, spa_max_replication(spa)); - if (BP_IS_ENCRYPTED(bp) && gbh_copies >= SPA_DVAS_PER_BP) - gbh_copies = SPA_DVAS_PER_BP - 1; + int gbh_copies = copies; + if (gbh_copies == 1) { + gbh_copies = MIN(2, spa_max_replication(spa)); + } int flags = METASLAB_HINTBP_FAVOR | METASLAB_GANG_HEADER; if (pio->io_flags & ZIO_FLAG_IO_ALLOCATING) { diff --git a/tests/runfiles/common.run b/tests/runfiles/common.run index 005c539fc8..7a7cf927c7 100644 --- a/tests/runfiles/common.run +++ b/tests/runfiles/common.run @@ -704,7 +704,7 @@ tags = ['functional', 'nestedfs'] [tests/functional/no_space] tests = ['enospc_001_pos', 'enospc_002_pos', 'enospc_003_pos', - 'enospc_df', 'enospc_rm'] + 'enospc_df', 'enospc_ganging', 'enospc_rm'] tags = ['functional', 'no_space'] [tests/functional/nopwrite] diff --git a/tests/zfs-tests/tests/Makefile.am b/tests/zfs-tests/tests/Makefile.am index bbe94f9177..ad2ec46705 100644 --- a/tests/zfs-tests/tests/Makefile.am +++ b/tests/zfs-tests/tests/Makefile.am @@ -1539,6 +1539,7 @@ nobase_dist_datadir_zfs_tests_tests_SCRIPTS += \ functional/no_space/enospc_002_pos.ksh \ functional/no_space/enospc_003_pos.ksh \ functional/no_space/enospc_df.ksh \ + functional/no_space/enospc_ganging.ksh \ functional/no_space/enospc_rm.ksh \ functional/no_space/setup.ksh \ functional/online_offline/cleanup.ksh \ diff --git a/tests/zfs-tests/tests/functional/no_space/enospc_ganging.ksh b/tests/zfs-tests/tests/functional/no_space/enospc_ganging.ksh new file mode 100755 index 0000000000..1d35fba5db --- /dev/null +++ b/tests/zfs-tests/tests/functional/no_space/enospc_ganging.ksh @@ -0,0 +1,86 @@ +#!/bin/ksh + +# +# This file and its contents are supplied under the terms of the +# Common Development and Distribution License ("CDDL"), version 1.0. +# You may only use this file in accordance with the terms of version +# 1.0 of the CDDL. +# +# A full copy of the text of the CDDL should have accompanied this +# source. A copy of the CDDL is also available via the Internet at +# http://www.illumos.org/license/CDDL. +# + +. $STF_SUITE/include/libtest.shlib + +# +# DESCRIPTION: +# Exercise gang block IO paths for non-encrypted and encrypted datasets. +# + +verify_runnable "both" +log_assert "Verify IO when file system is full and ganging." + +function cleanup +{ + log_must set_tunable64 METASLAB_FORCE_GANGING $metaslab_force_ganging + default_cleanup_noexit +} + +log_onexit cleanup + +default_setup_noexit $DISKS + +typeset metaslab_force_ganging=$(get_tunable METASLAB_FORCE_GANGING) +shift=$(random_int_between 15 17) +log_must set_tunable64 METASLAB_FORCE_GANGING $((2**$shift)) + +keyfile=/$TESTPOOL/keyencfods +log_must eval "echo 'password' > $keyfile" +bs=1024k +count=512 + +log_must dd if=/dev/urandom of=$TESTDIR/data bs=$bs count=$count +data_checksum=$(sha256digest $TESTDIR/data) + +# Test common large block configuration. +log_must zfs create -o recordsize=1m -o primarycache=metadata $TESTPOOL/gang +mntpnt=$(get_prop mountpoint $TESTPOOL/gang) + +log_must dd if=$TESTDIR/data of=$mntpnt/file bs=$bs count=$count +sync_pool $TESTPOOL +log_must dd if=$mntpnt/file of=$TESTDIR/out bs=$bs count=$count +out_checksum=$(sha256digest $TESTDIR/out) + +if [[ "$data_checksum" != "$out_checksum" ]]; then + log_fail "checksum mismatch ($data_checksum != $out_checksum)" +fi + +log_must rm -f $TESTDIR/out +log_must zfs destroy $TESTPOOL/gang + +# Test common large block configuration with encryption. +log_must zfs create \ + -o recordsize=1m \ + -o primarycache=metadata \ + -o compression=off \ + -o encryption=on \ + -o keyformat=passphrase \ + -o keylocation=file://$keyfile \ + -o copies=2 \ + $TESTPOOL/gang +mntpnt=$(get_prop mountpoint $TESTPOOL/gang) + +log_must dd if=$TESTDIR/data of=$mntpnt/file bs=$bs count=$count +sync_pool $TESTPOOL +log_must dd if=$mntpnt/file of=$TESTDIR/out bs=$bs count=$count +out_checksum=$(sha256digest $TESTDIR/out) + +if [[ "$data_checksum" != "$out_checksum" ]]; then + log_fail "checksum mismatch ($data_checksum != $out_checksum)" +fi + +log_must rm -f $TESTDIR/out +log_must zfs destroy $TESTPOOL/gang + +log_pass "Verified IO when file system is full and ganging."