From a2a04409185efc3e90aa9b8f16ace49190f01f4b Mon Sep 17 00:00:00 2001 From: Brian Behlendorf Date: Wed, 13 Sep 2017 16:04:16 -0700 Subject: [PATCH] Fix volume WR_INDIRECT log replay (#6620) The portion of the zvol_replay_write() handler responsible for replaying indirect log records for some reason never existed. As a result indirect log records were not being correctly replayed. This went largely unnoticed since the majority of zvol log records were of the type WR_COPIED or WR_NEED_COPY prior to OpenZFS 7578. This patch updates zvol_replay_write() to correctly handle these log records and adds a new test case which verifies volume replay to prevent any regression. The existing test case which verified replay on filesystem was renamed slog_replay_fs.ksh for clarity. Reviewed-by: George Melikov Reviewed-by: loli10K Signed-off-by: Brian Behlendorf Closes #6603 --- module/zfs/zvol.c | 23 ++- scripts/zfs-tests.sh | 4 +- tests/runfiles/linux.run | 2 +- tests/zfs-tests/include/commands.cfg | 1 + tests/zfs-tests/include/libtest.shlib | 2 +- .../cli_root/zfs_copies/cleanup.ksh | 2 +- .../zfs_rollback/zfs_rollback_common.kshlib | 6 +- .../cli_root/zpool_create/zpool_create.shlib | 6 +- .../tests/functional/slog/Makefile.am | 3 +- .../zfs-tests/tests/functional/slog/setup.ksh | 2 +- .../{slog_015_pos.ksh => slog_replay_fs.ksh} | 35 ++-- .../functional/slog/slog_replay_volume.ksh | 164 ++++++++++++++++++ .../tests/functional/xattr/xattr_004_pos.ksh | 16 +- 13 files changed, 223 insertions(+), 43 deletions(-) rename tests/zfs-tests/tests/functional/slog/{slog_015_pos.ksh => slog_replay_fs.ksh} (89%) create mode 100755 tests/zfs-tests/tests/functional/slog/slog_replay_volume.ksh diff --git a/module/zfs/zvol.c b/module/zfs/zvol.c index 89cd216a4..96e44842b 100644 --- a/module/zfs/zvol.c +++ b/module/zfs/zvol.c @@ -599,26 +599,37 @@ static int zvol_replay_write(zvol_state_t *zv, lr_write_t *lr, boolean_t byteswap) { objset_t *os = zv->zv_objset; - char *data = (char *)(lr + 1); /* data follows lr_write_t */ - uint64_t off = lr->lr_offset; - uint64_t len = lr->lr_length; + char *data = (char *)(lr + 1); /* data follows lr_write_t */ + uint64_t offset, length; dmu_tx_t *tx; int error; if (byteswap) byteswap_uint64_array(lr, sizeof (*lr)); + offset = lr->lr_offset; + length = lr->lr_length; + + /* If it's a dmu_sync() block, write the whole block */ + if (lr->lr_common.lrc_reclen == sizeof (lr_write_t)) { + uint64_t blocksize = BP_GET_LSIZE(&lr->lr_blkptr); + if (length < blocksize) { + offset -= offset % blocksize; + length = blocksize; + } + } + tx = dmu_tx_create(os); - dmu_tx_hold_write(tx, ZVOL_OBJ, off, len); + dmu_tx_hold_write(tx, ZVOL_OBJ, offset, length); error = dmu_tx_assign(tx, TXG_WAIT); if (error) { dmu_tx_abort(tx); } else { - dmu_write(os, ZVOL_OBJ, off, len, data, tx); + dmu_write(os, ZVOL_OBJ, offset, length, data, tx); dmu_tx_commit(tx); } - return (SET_ERROR(error)); + return (error); } static int diff --git a/scripts/zfs-tests.sh b/scripts/zfs-tests.sh index eb2ce3828..1d959ae33 100755 --- a/scripts/zfs-tests.sh +++ b/scripts/zfs-tests.sh @@ -224,8 +224,8 @@ constrain_path() { # Exceptions ln -fs "$STF_PATH/awk" "$STF_PATH/nawk" - ln -fs /sbin/fsck.ext2 "$STF_PATH/fsck" - ln -fs /sbin/mkfs.ext2 "$STF_PATH/newfs" + ln -fs /sbin/fsck.ext4 "$STF_PATH/fsck" + ln -fs /sbin/mkfs.ext4 "$STF_PATH/newfs" ln -fs "$STF_PATH/gzip" "$STF_PATH/compress" ln -fs "$STF_PATH/gunzip" "$STF_PATH/uncompress" ln -fs "$STF_PATH/exportfs" "$STF_PATH/share" diff --git a/tests/runfiles/linux.run b/tests/runfiles/linux.run index 472f85dd4..a12fc2d43 100644 --- a/tests/runfiles/linux.run +++ b/tests/runfiles/linux.run @@ -511,7 +511,7 @@ tests = ['scrub_mirror_001_pos', 'scrub_mirror_002_pos', tests = ['slog_001_pos', 'slog_002_pos', 'slog_003_pos', 'slog_004_pos', 'slog_005_pos', 'slog_006_pos', 'slog_007_pos', 'slog_008_neg', 'slog_009_neg', 'slog_010_neg', 'slog_011_neg', 'slog_012_neg', - 'slog_013_pos', 'slog_014_pos', 'slog_015_pos'] + 'slog_013_pos', 'slog_014_pos', 'slog_replay_fs', 'slog_replay_volume'] [tests/functional/snapshot] tests = ['clone_001_pos', 'rollback_001_pos', 'rollback_002_pos', diff --git a/tests/zfs-tests/include/commands.cfg b/tests/zfs-tests/include/commands.cfg index 32749028c..f6fd239de 100644 --- a/tests/zfs-tests/include/commands.cfg +++ b/tests/zfs-tests/include/commands.cfg @@ -36,6 +36,7 @@ export SYSTEM_FILES='arp egrep exportfs expr + fallocate false fdisk file diff --git a/tests/zfs-tests/include/libtest.shlib b/tests/zfs-tests/include/libtest.shlib index be71c0d75..66abfd696 100644 --- a/tests/zfs-tests/include/libtest.shlib +++ b/tests/zfs-tests/include/libtest.shlib @@ -140,7 +140,7 @@ function ismounted [[ "$1" == "$dir" || "$1" == "$name" ]] && return 0 ;; - ext2) + ext*) out=$(df -t $fstype $1 2>/dev/null) return $? ;; diff --git a/tests/zfs-tests/tests/functional/cli_root/zfs_copies/cleanup.ksh b/tests/zfs-tests/tests/functional/cli_root/zfs_copies/cleanup.ksh index b89456041..f5e862593 100755 --- a/tests/zfs-tests/tests/functional/cli_root/zfs_copies/cleanup.ksh +++ b/tests/zfs-tests/tests/functional/cli_root/zfs_copies/cleanup.ksh @@ -33,7 +33,7 @@ . $STF_SUITE/tests/functional/cli_root/zfs_copies/zfs_copies.cfg # -# umount the ufs|ext2 fs if there is timedout in the ufs|ext2 test +# umount the ufs|ext fs if there is timedout in the ufs|ext test # if ismounted $FS_MNTPOINT $NEWFS_DEFAULT_FS ; then diff --git a/tests/zfs-tests/tests/functional/cli_root/zfs_rollback/zfs_rollback_common.kshlib b/tests/zfs-tests/tests/functional/cli_root/zfs_rollback/zfs_rollback_common.kshlib index 6097afa81..5b157d11c 100644 --- a/tests/zfs-tests/tests/functional/cli_root/zfs_rollback/zfs_rollback_common.kshlib +++ b/tests/zfs-tests/tests/functional/cli_root/zfs_rollback/zfs_rollback_common.kshlib @@ -73,7 +73,7 @@ function setup_snap_env [[ $type == 'volume' ]]; then # # At the first time, Make a UFS file system in volume and - # mount it. Otherwise, only check if this ufs|ext2 file system + # mount it. Otherwise, only check if this ufs|ext file system # was mounted. # log_must eval "echo "y" | \ @@ -81,8 +81,8 @@ function setup_snap_env [[ ! -d $TESTDIR1 ]] && log_must mkdir $TESTDIR1 - # Make sure the ufs|ext2 filesystem hasn't been mounted, - # then mount the new ufs|ext2 filesystem. + # Make sure the ufs|ext filesystem hasn't been mounted, + # then mount the new ufs|ext filesystem. if ! ismounted $TESTDIR1 $NEWFS_DEFAULT_FS; then log_must mount \ $ZVOL_DEVDIR/$TESTPOOL/$TESTVOL $TESTDIR1 diff --git a/tests/zfs-tests/tests/functional/cli_root/zpool_create/zpool_create.shlib b/tests/zfs-tests/tests/functional/cli_root/zpool_create/zpool_create.shlib index b347bc9bf..d65144369 100644 --- a/tests/zfs-tests/tests/functional/cli_root/zpool_create/zpool_create.shlib +++ b/tests/zfs-tests/tests/functional/cli_root/zpool_create/zpool_create.shlib @@ -53,11 +53,11 @@ function create_pool_test } # -# Create a ufs|ext2 file system and make a file within the file +# Create a ufs|ext file system and make a file within the file # system for storage pool vdev # $1, file size # $2, file name -# $3, disk name to create ufs|ext2 file system +# $3, disk name to create ufs|ext file system # function create_blockfile { @@ -83,7 +83,7 @@ function create_blockfile } # -# Umount the ext2|ufs filesystem and remove the mountpoint +# Umount the ufs|ext filesystem and remove the mountpoint # $1, the mount point # function clean_blockfile diff --git a/tests/zfs-tests/tests/functional/slog/Makefile.am b/tests/zfs-tests/tests/functional/slog/Makefile.am index 23c07b297..f446facfe 100644 --- a/tests/zfs-tests/tests/functional/slog/Makefile.am +++ b/tests/zfs-tests/tests/functional/slog/Makefile.am @@ -18,4 +18,5 @@ dist_pkgdata_SCRIPTS = \ slog_012_neg.ksh \ slog_013_pos.ksh \ slog_014_pos.ksh \ - slog_015_pos.ksh + slog_replay_fs.ksh \ + slog_replay_volume.ksh diff --git a/tests/zfs-tests/tests/functional/slog/setup.ksh b/tests/zfs-tests/tests/functional/slog/setup.ksh index 022ad1fd6..f30824d3e 100755 --- a/tests/zfs-tests/tests/functional/slog/setup.ksh +++ b/tests/zfs-tests/tests/functional/slog/setup.ksh @@ -45,6 +45,6 @@ if [[ -d $VDEV2 ]]; then log_must rm -rf $VDIR2 fi log_must mkdir -p $VDIR $VDIR2 -log_must mkfile $MINVDEVSIZE $VDEV $SDEV $LDEV $VDEV2 $SDEV2 $LDEV2 +log_must truncate -s $MINVDEVSIZE $VDEV $SDEV $LDEV $VDEV2 $SDEV2 $LDEV2 log_pass diff --git a/tests/zfs-tests/tests/functional/slog/slog_015_pos.ksh b/tests/zfs-tests/tests/functional/slog/slog_replay_fs.ksh similarity index 89% rename from tests/zfs-tests/tests/functional/slog/slog_015_pos.ksh rename to tests/zfs-tests/tests/functional/slog/slog_replay_fs.ksh index 6727b5f05..5f281a756 100755 --- a/tests/zfs-tests/tests/functional/slog/slog_015_pos.ksh +++ b/tests/zfs-tests/tests/functional/slog/slog_replay_fs.ksh @@ -48,18 +48,24 @@ # 1. Create an empty file system (TESTFS) # 2. Freeze TESTFS # 3. Run various user commands that create files, directories and ACLs -# 4. Copy TESTFS to temporary location (TESTDIR) +# 4. Copy TESTFS to temporary location (TESTDIR/copy) # 5. Unmount filesystem # # 6. Remount TESTFS -# 7. Compare TESTFS against the TESTDIR copy +# 7. Compare TESTFS against the TESTDIR/copy # verify_runnable "global" +function cleanup_fs +{ + rm -f $TESTDIR/checksum + cleanup +} + log_assert "Replay of intent log succeeds." -log_onexit cleanup +log_onexit cleanup_fs # # 1. Create an empty file system (TESTFS) @@ -67,7 +73,6 @@ log_onexit cleanup log_must zpool create $TESTPOOL $VDEV log mirror $LDEV log_must zfs set compression=on $TESTPOOL log_must zfs create $TESTPOOL/$TESTFS -log_must mkdir -p $TESTDIR # # This dd command works around an issue where ZIL records aren't created @@ -107,8 +112,9 @@ log_must mkdir /$TESTPOOL/$TESTFS/dir_to_delete log_must rmdir /$TESTPOOL/$TESTFS/dir_to_delete # Create a simple validation payload +log_must mkdir -p $TESTDIR log_must dd if=/dev/urandom of=/$TESTPOOL/$TESTFS/payload bs=1k count=8 -CHECKSUM_BEFORE=$(sha256sum -b /$TESTPOOL/$TESTFS/payload) +log_must eval "sha256sum -b /$TESTPOOL/$TESTFS/payload >$TESTDIR/checksum" # TX_WRITE (small file with ordering) log_must mkfile 1k /$TESTPOOL/$TESTFS/small_file @@ -132,7 +138,7 @@ log_must truncate -s 0 /$TESTPOOL/$TESTFS/truncated_file log_must dd if=/dev/urandom of=/$TESTPOOL/$TESTFS/large \ bs=128k count=64 oflag=sync -# Write zeroes, which compresss to holes, in the middle of a file +# Write zeros, which compress to holes, in the middle of a file log_must dd if=/dev/urandom of=/$TESTPOOL/$TESTFS/holes.1 bs=128k count=8 log_must dd if=/dev/zero of=/$TESTPOOL/$TESTFS/holes.1 bs=128k count=2 @@ -155,15 +161,16 @@ log_must attr -qs tmpattr -V HelloWorld /$TESTPOOL/$TESTFS/xattr.file log_must attr -qr tmpattr /$TESTPOOL/$TESTFS/xattr.file # -# 4. Copy TESTFS to temporary location (TESTDIR) +# 4. Copy TESTFS to temporary location (TESTDIR/copy) # -log_must cp -a /$TESTPOOL/$TESTFS/* $TESTDIR +log_must mkdir -p $TESTDIR/copy +log_must cp -a /$TESTPOOL/$TESTFS/* $TESTDIR/copy/ # # 5. Unmount filesystem and export the pool # -# At this stage TESTFS is empty again and unfrozen, and the -# intent log contains a complete set of deltas to replay it. +# At this stage TESTFS is empty again and frozen, the intent log contains +# a complete set of deltas to replay. # log_must zfs unmount /$TESTPOOL/$TESTFS @@ -181,7 +188,7 @@ log_must zpool export $TESTPOOL log_must zpool import -f -d $VDIR $TESTPOOL # -# 7. Compare TESTFS against the TESTDIR copy +# 7. Compare TESTFS against the TESTDIR/copy # log_note "Verify current block usage:" log_must zdb -bcv $TESTPOOL @@ -191,11 +198,9 @@ log_must attr -l /$TESTPOOL/$TESTFS/xattr.dir log_must attr -l /$TESTPOOL/$TESTFS/xattr.file log_note "Verify working set diff:" -log_must diff -r /$TESTPOOL/$TESTFS $TESTDIR >/dev/null || \ - diff -r /$TESTPOOL/$TESTFS $TESTDIR +log_must diff -r /$TESTPOOL/$TESTFS $TESTDIR/copy log_note "Verify file checksum:" -log_note "$CHECKSUM_BEFORE" -log_must echo "$CHECKSUM_BEFORE" | sha256sum -c +log_must sha256sum -c $TESTDIR/checksum log_pass "Replay of intent log succeeds." diff --git a/tests/zfs-tests/tests/functional/slog/slog_replay_volume.ksh b/tests/zfs-tests/tests/functional/slog/slog_replay_volume.ksh new file mode 100755 index 000000000..54ca70bf7 --- /dev/null +++ b/tests/zfs-tests/tests/functional/slog/slog_replay_volume.ksh @@ -0,0 +1,164 @@ +#!/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 http://www.opensolaris.org/os/licensing. +# 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 +# + +# +# Copyright 2007 Sun Microsystems, Inc. All rights reserved. +# Copyright (c) 2017 by Lawrence Livermore National Security, LLC. +# Use is subject to license terms. +# + +. $STF_SUITE/tests/functional/slog/slog.kshlib + +# +# DESCRIPTION: +# Verify slogs are replayed correctly for a volume. +# +# The general idea is to build up an intent log from a bunch of +# diverse user commands without actually committing them to the +# file system. Then generate checksums for files and volume, +# replay the intent log and verify the checksums. +# +# To enable this automated testing of the intent log some minimal +# support is required of the file system. In particular, a +# "freeze" command is required to flush the in-flight transactions; +# to stop the actual committing of transactions; and to ensure no +# deltas are discarded. All deltas past a freeze point are kept +# for replay and comparison later. Here is the flow: +# +# STRATEGY: +# 1. Create an empty volume (TESTVOL), set sync=always, and format +# it with an ext4 filesystem and mount it. +# 2. Freeze TESTVOL. +# 3. Create log records of various types to verify replay. +# 4. Generate checksums for all ext4 files. +# 5. Unmount filesystem and export the pool +# +# 6. Import TESTVOL and mount it. +# 7. Verify the stored checksums +# + +verify_runnable "global" + +VOLUME=$ZVOL_DEVDIR/$TESTPOOL/$TESTVOL +MNTPNT=$TESTDIR/$TESTVOL + +function cleanup_volume +{ + if ismounted $MNTPNT ext4; then + log_must umount $MNTPNT + rmdir $MNTPNT + fi + + rm -f $TESTDIR/checksum.files + + cleanup +} + +log_assert "Replay of intent log succeeds." +log_onexit cleanup_volume + +# +# 1. Create an empty volume (TESTVOL), set sync=always, and format +# it with an ext4 filesystem and mount it. +# +log_must zpool create $TESTPOOL $VDEV log mirror $LDEV +log_must zfs create -V 128M $TESTPOOL/$TESTVOL +log_must zfs set compression=on $TESTPOOL/$TESTVOL +log_must zfs set sync=always $TESTPOOL/$TESTVOL +log_must mkdir -p $TESTDIR +log_must block_device_wait +echo "y" | newfs -t ext4 -v $VOLUME +log_must mkdir -p $MNTPNT +log_must mount -o discard $VOLUME $MNTPNT +log_must rmdir $MNTPNT/lost+found +log_must zpool sync + +# +# 2. Freeze TESTVOL +# +log_must zpool freeze $TESTPOOL + +# +# 3. Create log records of various types to verify replay. +# + +# TX_WRITE +log_must dd if=/dev/urandom of=$MNTPNT/latency-8k bs=8k count=1 oflag=sync +log_must dd if=/dev/urandom of=$MNTPNT/latency-128k bs=128k count=1 oflag=sync + +# TX_WRITE (WR_INDIRECT) +log_must zfs set logbias=throughput $TESTPOOL/$TESTVOL +log_must dd if=/dev/urandom of=$MNTPNT/throughput-8k bs=8k count=1 +log_must dd if=/dev/urandom of=$MNTPNT/throughput-128k bs=128k count=1 + +# TX_WRITE (holes) +log_must dd if=/dev/urandom of=$MNTPNT/holes bs=128k count=8 +log_must dd if=/dev/zero of=$MNTPNT/holes bs=128k count=2 seek=2 conv=notrunc + +# TX_TRUNCATE +if fallocate --punch-hole 2>&1 | grep -q "unrecognized option"; then + log_note "fallocate(1) does not support --punch-hole" +else + log_must dd if=/dev/urandom of=$MNTPNT/discard bs=128k count=16 + log_must fallocate --punch-hole -l 128K -o 512K $MNTPNT/discard + log_must fallocate --punch-hole -l 512K -o 1M $MNTPNT/discard +fi + +# +# 4. Generate checksums for all ext4 files. +# +log_must sha256sum -b $MNTPNT/* >$TESTDIR/checksum + +# +# 5. Unmount filesystem and export the pool +# +# At this stage TESTVOL is initialized with the random data and frozen, +# the intent log contains a complete set of deltas to replay. +# +log_must umount $MNTPNT + +log_note "Verify transactions to replay:" +log_must zdb -iv $TESTPOOL/$TESTVOL + +log_must zpool export $TESTPOOL + +# +# 6. Import TESTPOOL, the intent log is replayed during minor creation. +# +# Import the pool to unfreeze it and claim log blocks. It has to be +# `zpool import -f` because we can't write a frozen pool's labels! +# +log_must zpool import -f -d $VDIR $TESTPOOL +log_must block_device_wait +log_must mount $VOLUME $MNTPNT + +# +# 7. Verify the stored checksums +# +log_note "Verify current block usage:" +log_must zdb -bcv $TESTPOOL + +log_note "Verify checksums" +log_must sha256sum -c $TESTDIR/checksum + +log_pass "Replay of intent log succeeds." diff --git a/tests/zfs-tests/tests/functional/xattr/xattr_004_pos.ksh b/tests/zfs-tests/tests/functional/xattr/xattr_004_pos.ksh index e5f61d3bf..41320a138 100755 --- a/tests/zfs-tests/tests/functional/xattr/xattr_004_pos.ksh +++ b/tests/zfs-tests/tests/functional/xattr/xattr_004_pos.ksh @@ -33,23 +33,21 @@ # # DESCRIPTION: # -# Creating files on ufs|ext2 and tmpfs, and copying those files to ZFS with +# Creating files on ufs|ext and tmpfs, and copying those files to ZFS with # appropriate cp flags, the xattrs will still be readable. # # STRATEGY: -# 1. Create files in ufs|ext2 and tmpfs with xattrs -# 2. Copy those files to zfs +# 1. Create files in ufs|ext and tmpfs with xattrs +# 2. Copy those files to zfs # 3. Ensure the xattrs can be read and written # 4. Do the same in reverse. # -# we need to be able to create zvols to hold our test -# ufs|ext2 filesystem. +# we need to be able to create zvols to hold our test ufs|ext filesystem. verify_runnable "global" # Make sure we clean up properly function cleanup { - if ismounted /tmp/$NEWFS_DEFAULT_FS.$$ $NEWFS_DEFAULT_FS; then log_must umount /tmp/$NEWFS_DEFAULT_FS.$$ log_must rm -rf /tmp/$NEWFS_DEFAULT_FS.$$ @@ -59,7 +57,7 @@ function cleanup { log_assert "Files from $NEWFS_DEFAULT_FS,tmpfs with xattrs copied to zfs retain xattr info." log_onexit cleanup -# Create a UFS|EXT2 file system that we can work in +# Create a ufs|ext file system that we can work in log_must zfs create -V128m $TESTPOOL/$TESTFS/zvol block_device_wait log_must eval "echo y | newfs $ZVOL_DEVDIR/$TESTPOOL/$TESTFS/zvol > /dev/null 2>&1" @@ -69,8 +67,8 @@ if is_linux; then log_must mount -o user_xattr \ $ZVOL_DEVDIR/$TESTPOOL/$TESTFS/zvol /tmp/$NEWFS_DEFAULT_FS.$$ - # Create files in ext2 and tmpfs, and set some xattrs on them. - # Use small values for xattrs for ext2 compatibility. + # Create files in ext and tmpfs, and set some xattrs on them. + # Use small values for xattrs for ext compatibility. log_must touch /tmp/$NEWFS_DEFAULT_FS.$$/$NEWFS_DEFAULT_FS-file.$$ log_must touch /tmp/tmpfs-file.$$