mirror of
https://github.com/linux-apfs/apfstests.git
synced 2026-05-01 15:01:44 -07:00
d0f42706da
We have generic dm-logwrites with fsstress test case (generic/482), but it doesn't cover fs specific operations like btrfs snapshot creation and deletion. Furthermore, that test is not heavy enough to bump btrfs tree height by its short runtime. And finally, btrfs check doesn't consider dirty log as an error, unlike ext*/xfs, that's to say we don't need to mount the fs to replay the log, but just running btrfs check on the fs is enough. So introduce a similar test case but for btrfs only. The test case will stress btrfs by: - Use small nodesize to bump tree height - Create a base tree which is already high enough - Trim tree blocks to find possible trim bugs - Call snapshot creation and deletion along with fsstress Also it includes certain workaround for btrfs: - Allow _log_writes_mkfs to accept extra mkfs options - Use no-holes feature To avoid missing hole file extents. Although that behavior doesn't follow the on-disk format spec, it doesn't cause data loss. And will follow the new on-disk format spec of no-holes feature, so it's better to workaround it. And an optimization for btrfs only: - Use replay-log --fsck/--check command Since dm-log-writes records bios sequentially, there is no way to locate certain entry unless we iterate all entries. This is becoming a big performance penalty if we replay certain a range, check the fs, then re-execute replay-log to replay another range. We need to records the previous entry location, or we need to re-iterate all previous entries. Thankfully, replay-log has already address it by providing --fsck and --check command, thus we don't need to break replay-log command. Please note, for fast storage (e.g. fast NVME or unsafe cache mode), it's recommended to use log devices larger than 15G, or we can't record the full log of just 30s fsstress run. Signed-off-by: Qu Wenruo <wqu@suse.com> Reviewed-by: Filipe Manana <fdmanana@suse.com> Signed-off-by: Eryu Guan <guaneryu@gmail.com>
178 lines
4.8 KiB
Plaintext
178 lines
4.8 KiB
Plaintext
##/bin/bash
|
|
# SPDX-License-Identifier: GPL-2.0
|
|
# Copyright (c) 2015 Facebook, Inc. All Rights Reserved.
|
|
#
|
|
# common functions for setting up and tearing down a dm log-writes device
|
|
|
|
_require_log_writes()
|
|
{
|
|
[ -z "$LOGWRITES_DEV" -o ! -b "$LOGWRITES_DEV" ] && \
|
|
_notrun "This test requires a valid \$LOGWRITES_DEV"
|
|
|
|
_exclude_scratch_mount_option dax
|
|
_require_dm_target log-writes
|
|
_require_test_program "log-writes/replay-log"
|
|
}
|
|
|
|
# Starting from v4.15-rc1, DAX support was added to dm-log-writes, but note
|
|
# that it doesn't track the data that we write via the mmap(), so we can't do
|
|
# any data integrity checking. We can only verify that the metadata writes for
|
|
# the page faults happened, e.g. when mmap(2) with MAP_SYNC flag.
|
|
#
|
|
# Introduce a new helper to check if dm-log-writes target supports DAX
|
|
# explicitly. But this is considered as a temporary workaround, we want to move
|
|
# all the DAX check back to _require_log_writes when dm-log-writes gains full
|
|
# DAX support and remove this helper.
|
|
_require_log_writes_dax()
|
|
{
|
|
[ -z "$LOGWRITES_DEV" -o ! -b "$LOGWRITES_DEV" ] && \
|
|
_notrun "This test requires a valid \$LOGWRITES_DEV"
|
|
|
|
_require_dm_target log-writes
|
|
_require_test_program "log-writes/replay-log"
|
|
|
|
local ret=0
|
|
_log_writes_init $SCRATCH_DEV
|
|
_log_writes_mkfs > /dev/null 2>&1
|
|
_log_writes_mount -o dax > /dev/null 2>&1
|
|
# Check options to be sure. XFS ignores dax option
|
|
# and goes on if dev underneath does not support dax.
|
|
_fs_options $LOGWRITES_DMDEV | grep -qw "dax"
|
|
ret=$?
|
|
_log_writes_cleanup
|
|
if [ $ret -ne 0 ]; then
|
|
_notrun "$LOGWRITES_DMDEV $FSTYP does not support -o dax"
|
|
fi
|
|
}
|
|
|
|
_log_writes_init()
|
|
{
|
|
blkdev=$1
|
|
|
|
[ -z "$blkdev" ] && _fail \
|
|
"block dev must be specified for _log_writes_init"
|
|
|
|
local BLK_DEV_SIZE=`blockdev --getsz $blkdev`
|
|
LOGWRITES_NAME=logwrites-test
|
|
LOGWRITES_DMDEV=/dev/mapper/$LOGWRITES_NAME
|
|
LOGWRITES_TABLE="0 $BLK_DEV_SIZE log-writes $blkdev $LOGWRITES_DEV"
|
|
_dmsetup_create $LOGWRITES_NAME --table "$LOGWRITES_TABLE" || \
|
|
_fail "failed to create log-writes device"
|
|
}
|
|
|
|
_log_writes_mark()
|
|
{
|
|
[ $# -ne 1 ] && _fail "_log_writes_mark takes one argument"
|
|
$DMSETUP_PROG message $LOGWRITES_NAME 0 mark $1
|
|
}
|
|
|
|
_log_writes_mkfs()
|
|
{
|
|
_scratch_options mkfs
|
|
_mkfs_dev $SCRATCH_OPTIONS $@ $LOGWRITES_DMDEV
|
|
_log_writes_mark mkfs
|
|
}
|
|
|
|
_log_writes_mount()
|
|
{
|
|
_scratch_options mount
|
|
$MOUNT_PROG -t $FSTYP `_common_dev_mount_options $*` $SCRATCH_OPTIONS \
|
|
$LOGWRITES_DMDEV $SCRATCH_MNT
|
|
}
|
|
|
|
_log_writes_unmount()
|
|
{
|
|
$UMOUNT_PROG $SCRATCH_MNT
|
|
}
|
|
|
|
# _log_writes_replay_log <mark>
|
|
#
|
|
# This replays the log contained on $LOGWRITES_DEV onto blkdev upto the
|
|
# mark passed in.
|
|
_log_writes_replay_log()
|
|
{
|
|
_mark=$1
|
|
_blkdev=$2
|
|
|
|
[ -z "$_blkdev" ] && _fail \
|
|
"block dev must be specified for _log_writes_replay_log"
|
|
|
|
$here/src/log-writes/replay-log --log $LOGWRITES_DEV --find \
|
|
--end-mark $_mark >> $seqres.full 2>&1
|
|
[ $? -ne 0 ] && _fail "mark '$_mark' does not exist"
|
|
|
|
$here/src/log-writes/replay-log --log $LOGWRITES_DEV --replay $_blkdev \
|
|
--end-mark $_mark >> $seqres.full 2>&1
|
|
[ $? -ne 0 ] && _fail "replay failed"
|
|
}
|
|
|
|
_log_writes_remove()
|
|
{
|
|
_dmsetup_remove $LOGWRITES_NAME
|
|
}
|
|
|
|
_log_writes_cleanup()
|
|
{
|
|
$UMOUNT_PROG $SCRATCH_MNT > /dev/null 2>&1
|
|
_log_writes_remove
|
|
}
|
|
|
|
# Convert log writes mark to entry number
|
|
# Result entry number is output to stdout, could be empty if not found
|
|
_log_writes_mark_to_entry_number()
|
|
{
|
|
local mark=$1
|
|
local ret
|
|
|
|
[ -z "$mark" ] && _fatal \
|
|
"mark must be given for _log_writes_mark_to_entry_number"
|
|
|
|
ret=$($here/src/log-writes/replay-log --find --log $LOGWRITES_DEV \
|
|
--end-mark $mark 2> /dev/null)
|
|
[ -z "$ret" ] && return
|
|
ret=$(echo "$ret" | cut -f1 -d\@)
|
|
echo "mark $mark has entry number $ret" >> $seqres.full
|
|
echo "$ret"
|
|
}
|
|
|
|
# Find next fua write entry number
|
|
# Result entry number is output to stdout, could be empty if not found
|
|
_log_writes_find_next_fua()
|
|
{
|
|
local start_entry=$1
|
|
local ret
|
|
|
|
[ -z "$start_entry" ] && start_entry=0
|
|
ret=$($here/src/log-writes/replay-log --find --log $LOGWRITES_DEV \
|
|
--next-fua --start-entry $start_entry 2> /dev/null)
|
|
[ -z "$ret" ] && return
|
|
|
|
# Result should be something like "1024@offset" where 1024 is the
|
|
# entry number we need
|
|
ret=$(echo "$ret" | cut -f1 -d\@)
|
|
echo "next fua is entry number $ret" >> $seqres.full
|
|
echo "$ret"
|
|
}
|
|
|
|
# Replay log range to specified entry
|
|
# $1: End entry. The entry with this number *WILL* be replayed
|
|
_log_writes_replay_log_range()
|
|
{
|
|
local end=$1
|
|
local blkdev=$2
|
|
|
|
[ -z "$end" ] && _fail \
|
|
"end entry must be specified for _log_writes_replay_log_range"
|
|
[ -z "$blkdev" ] && _fail \
|
|
"block dev must be specified for _log_writes_replay_log_range"
|
|
|
|
# To ensure we replay the last entry,
|
|
# we need to manually increase the end entry number to ensure
|
|
# it's played
|
|
echo "=== replay to $end ===" >> $seqres.full
|
|
$here/src/log-writes/replay-log --log $LOGWRITES_DEV \
|
|
--replay $blkdev --limit $(($end + 1)) \
|
|
>> $seqres.full 2>&1
|
|
[ $? -ne 0 ] && _fail "replay failed"
|
|
}
|