Permalink
Browse files

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 <mail@gmelikov.ru>
Reviewed-by: loli10K <ezomori.nozomu@gmail.com>
Signed-off-by: Brian Behlendorf <behlendorf1@llnl.gov>
Closes #6603
  • Loading branch information...
behlendorf authored and tonyhutter committed Sep 13, 2017
1 parent 45d1abc commit a2a04409185efc3e90aa9b8f16ace49190f01f4b
@@ -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
@@ -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"
@@ -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',
@@ -36,6 +36,7 @@ export SYSTEM_FILES='arp
egrep
exportfs
expr
fallocate
false
fdisk
file
@@ -140,7 +140,7 @@ function ismounted

[[ "$1" == "$dir" || "$1" == "$name" ]] && return 0
;;
ext2)
ext*)
out=$(df -t $fstype $1 2>/dev/null)
return $?
;;
@@ -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
@@ -73,16 +73,16 @@ 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" | \
newfs -v $ZVOL_DEVDIR/$VOL > /dev/null 2>&1"

[[ ! -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
@@ -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
@@ -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
@@ -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
@@ -48,26 +48,31 @@
# 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
# <at this stage TESTFS is empty again and unfrozen, and the
# intent log contains a complete set of deltas to replay it>
# 6. Remount TESTFS <which replays the intent log>
# 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)
#
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."
Oops, something went wrong.

0 comments on commit a2a0440

Please sign in to comment.