|
##/bin/bash |
|
# SPDX-License-Identifier: GPL-2.0 |
|
# Copyright (c) 2000-2002 Silicon Graphics, Inc. All Rights Reserved. |
|
# |
|
# common routines for log testing |
|
|
|
fulldir=$seqres.fulldir |
|
rm -rf $fulldir |
|
|
|
_cleanup_logfiles() |
|
{ |
|
if [ $status -eq 0 ]; then |
|
# don't keep these files around unless something went wrong |
|
rm -rf $fulldir |
|
fi |
|
} |
|
|
|
_full() |
|
{ |
|
echo "" >>$seqres.full |
|
echo "*** $* ***" >>$seqres.full |
|
echo "" >>$seqres.full |
|
} |
|
|
|
_echofull() |
|
{ |
|
echo "" | tee -a $seqres.full |
|
echo "*** $* ***" | tee -a $seqres.full |
|
echo "" | tee -a $seqres.full |
|
} |
|
|
|
# Handle the operations which get split over Log Record |
|
# boundaries. |
|
# Oper (379)..... flags: CONTINUE |
|
# ... |
|
# Oper (0)....... flags: WAS_CONT END |
|
# |
|
# or |
|
# |
|
# Oper (379)..... flags: none |
|
# ... |
|
# Oper (0)....... flags: none |
|
# |
|
_filter_opnum() |
|
{ |
|
$AWK_PROG ' |
|
BEGIN { |
|
debug = 0 |
|
} |
|
/^Oper/ && debug { |
|
printf "line = %s\n", $0 |
|
} |
|
/^Oper/ { |
|
was_cont = 0 |
|
} |
|
/^Oper/ && /flags: CONTINUE/ { |
|
# this will be the first op of split region |
|
$9 = "none" # overwrite CONTINUE flags |
|
print |
|
print "Not printing rest" |
|
was_cont = 1 |
|
next |
|
} |
|
/^Oper/ && /flags: WAS_CONT END/ { |
|
# this will be the last op of split region |
|
# skip over was-continued op |
|
# we assume there can be only 1 |
|
was_cont = 1 |
|
next |
|
} |
|
(was_cont == 1) { |
|
# skip over any continued op stuff |
|
next |
|
} |
|
{print} |
|
' |
|
} |
|
|
|
# |
|
# Filter out things that can change |
|
# We have complexities which change when log is sync'ed at different |
|
# times. |
|
# Example1: DATA FORK EXTENTS |
|
# These will not show up if inode is sync'ed sooner |
|
# /DATA FORK EXTENTS/d; |
|
# /INODE:/s/flags:0x5/flags:0x1/g; |
|
# define XFS_ILOG_CORE 0x001 /* log standard inode fields */ |
|
# define XFS_ILOG_DEXT 0x004 /* log i_df.if_extents */ |
|
# |
|
# |
|
|
|
_filter_logprint() |
|
{ |
|
sed ' |
|
s/ver:[0-9]/ver:<VERS>/; |
|
s/version [0-9] format [0-9]/version <VERS> format <FORMAT>/; |
|
s/data device: 0x[0-9a-f][0-9a-f]*/data device: <DEVICE>/; |
|
s/log device: 0x[0-9a-f][0-9a-f]*/log device: <DEVICE>/; |
|
s/log file: \".*\"/log device: <DEVICE>/; |
|
s/daddr: [0-9][0-9]*/daddr: <DADDR>/; |
|
s/length: [0-9][0-9]*/length: <LENGTH>/; |
|
s/length: [0-9][0-9]*/length: <LENGTH>/; |
|
s/^cycle num overwrites: .*$/cycle num overwrites: <TIDS>/; |
|
s/tid: [0-9a-f][0-9a-f]*/tid: <TID>/; |
|
s/tid:0x[0-9a-f][0-9a-f]*/tid:<TID>/; |
|
s/q:0x[0-9a-f][0-9a-f]*/q:<Q>/; |
|
s/a:0x[0-9a-f][0-9a-f]*/a:<A>/g; |
|
s/blkno:0x[0-9a-f][0-9a-f]*/blkno:<BLKNO>/g; |
|
s/blkno: *[0-9][0-9]* (0x[0-9a-f]*)/blkno: <BLKNO> (<BLKNO>)/g; |
|
s/blkno: *[0-9][0-9]*/blkno: <BLKNO>/g; |
|
s/boff: [0-9][0-9]*/boff: <BOFF>/g; |
|
s/len: *[0-9][0-9]*/len:<LEN>/g; |
|
/BUF:/s/[ ]*flags:.*$//; |
|
/zeroed blocks/s/[0-9][0-9]*/<COUNT>/g; |
|
/cleared blocks/d; |
|
/log tail/s/[0-9][0-9]*/<COUNT>/g; |
|
s/atime:[0-9a-fx]* *mtime:[0-9a-fx]* *ctime:[0-9a-fx]*/atime:<TIME> mtime:<TIME> ctime:<TIME>/; |
|
s/atime 0x[0-9a-f]* mtime 0x[0-9a-f]* ctime 0x[0-9a-f]*/atime <TIME> mtime <TIME> ctime <TIME>/; |
|
s/block [0-9][0-9]*/block <BLOCK>/; |
|
s/icount: *[0-9][0-9]* *ifree: *[0-9][0-9]* *fdblks: *[0-9][0-9]* *frext: *[0-9][0-9]*/icount:<COUNT> ifree:<FREE> fdblks:<BLOCKS> frext:<COUNT>/; |
|
s/sunit: *[0-9][0-9]* *swidth: *[0-9][0-9]*/sunit:<SUNIT> swidth:<SWIDTH>/; |
|
s/1st: *[0-9][0-9]* *last: *[0-9][0-9]* *cnt: *[0-9][0-9]* *freeblks: *[0-9][0-9]* *longest: *[0-9][0-9]*/1st:<NUM> last:<NUM> cnt:<COUNT> freeblks:<COUNT> longest:<NUM>/; |
|
s/^uuid: *[0-9a-f-][0-9a-f-]* *format: *.*$/uuid: <UUID> format: <FORMAT>/; |
|
/flushiter:/d; |
|
/version:/,/h_size:/d; |
|
/override tail/s/[0-9][0-9]*/<TAIL_BLK>/; |
|
/^---*/d; |
|
/^===*/d; |
|
/^~~~*/d; |
|
/extended-header/d; |
|
/LOG REC AT LSN/d; |
|
/DATA FORK EXTENTS/d; |
|
s/BUF: cnt:[1-9][0-9]* total:[1-9][0-9]*.*/BUF: cnt:C total:T/; |
|
s/INO: cnt:[1-9][0-9]* total:[1-9][0-9]*.*/INO: cnt:C total:T/; |
|
s/#regs: *[1-9][0-9]*/#regs:R/; |
|
/INODE:/s/flags:0x5/flags:0x1/g; |
|
s/Oper ([0-9][0-9]*)/Oper (OPNUM)/; |
|
/^[ ]*$/d; |
|
s/ */ /g; |
|
s/ $//; |
|
s/newino: 0x[0-9a-f]*$/newino: <INO>/g |
|
s/newino:0x[0-9a-f]*$/newino:<INO>/g |
|
s/ino: 0x[0-9a-f]* flags:/ino: <INO> flags:/g |
|
s/ino:0x[0-9a-f]* flags:/ino:<INO> flags:/g |
|
s/onlink:[0-9][0-9]*/onlink:<ONLINK>/; |
|
s/gen:-*[0-9][0-9]*/gen:<GEN>/; |
|
s/gen 0x[0-9a-f][0-9a-f]*/gen <GEN>/; |
|
'|\ |
|
awk ' |
|
# collapse BUF DATA group into 1 line |
|
# for Oper data this can be over separate operations...ughh |
|
/BUF DATA/ { |
|
if (!buf_data) { # 1st one |
|
if (oper) { |
|
print oper |
|
oper = 0 |
|
} |
|
print |
|
} |
|
buf_data = 1 |
|
oper = 0 # wont need it now |
|
next |
|
} |
|
/^Oper/ { |
|
# store it as we dont know if 2nd BUF DATA is to follow |
|
if (oper) { |
|
print oper |
|
} |
|
oper = $0 |
|
next |
|
} |
|
/^TRANS/ && dummy_rec == 1 { |
|
# start printing again - dummy transaction over |
|
dummy_rec = 0 |
|
} |
|
/DUMMY1/ { |
|
# filter out dummy transactions |
|
dummy_rec = 1 |
|
next |
|
} |
|
{ |
|
if (dummy_rec) { |
|
next |
|
} |
|
buf_data = 0 |
|
if (oper) { # now we can print out oper |
|
print oper |
|
oper = 0 |
|
} |
|
print |
|
} |
|
' |
|
} |
|
|
|
_check_log() |
|
{ |
|
_full "clean_log : xfs_logprint" |
|
_scratch_xfs_logprint -t | tee -a $seqres.full \ |
|
| head | grep -q "<CLEAN>" || _fail "DIRTY LOG" |
|
} |
|
|
|
_scratch_xfs_logstate() |
|
{ |
|
_scratch_xfs_logprint -t | tee -a $seqres.full | grep -q "<CLEAN>" |
|
echo $? |
|
} |
|
|
|
_scratch_f2fs_logstate() |
|
{ |
|
$DUMP_F2FS_PROG $SCRATCH_DEV | tee -a $seqres.full | grep -q "unmount" |
|
echo $? |
|
} |
|
|
|
_scratch_ext4_logstate() |
|
{ |
|
$DUMPE2FS_PROG -h $SCRATCH_DEV 2> /dev/null | tee -a $seqres.full | \ |
|
grep "^Filesystem features" | grep -q needs_recovery |
|
test $? -ne 0 |
|
echo $? |
|
} |
|
|
|
_scratch_dump_log() |
|
{ |
|
case "$FSTYP" in |
|
xfs) |
|
_scratch_xfs_logprint |
|
;; |
|
f2fs) |
|
$DUMP_F2FS_PROG $SCRATCH_DEV |
|
;; |
|
ext4) |
|
$DEBUGFS_PROG -R "logdump -a" $SCRATCH_DEV |
|
;; |
|
*) |
|
;; |
|
esac |
|
} |
|
|
|
_test_dump_log() |
|
{ |
|
case "$FSTYP" in |
|
xfs) |
|
_test_xfs_logprint |
|
;; |
|
f2fs) |
|
$DUMP_F2FS_PROG $TEST_DEV |
|
;; |
|
ext4) |
|
$DEBUGFS_PROG -R "logdump -a" $TEST_DEV |
|
;; |
|
*) |
|
;; |
|
esac |
|
} |
|
|
|
_print_logstate() |
|
{ |
|
case "$FSTYP" in |
|
xfs) |
|
dirty=$(_scratch_xfs_logstate) |
|
;; |
|
f2fs) |
|
dirty=$(_scratch_f2fs_logstate) |
|
;; |
|
ext4) |
|
dirty=$(_scratch_ext4_logstate) |
|
;; |
|
*) |
|
;; |
|
esac |
|
|
|
if [ $dirty -ne 0 ]; then |
|
echo "dirty log" |
|
else |
|
echo "clean log" |
|
fi |
|
} |
|
|
|
_print_operation() |
|
{ |
|
mkdir $fulldir >/dev/null 2>&1 |
|
mntopt=`echo $MOUNT_OPTIONS | sed 's/ //g'` |
|
mkfsopt=`echo $MKFS_OPTIONS | sed 's/ //g'` |
|
raw=$fulldir/op.mnt$mntopt.mkfs$mkfsopt$sync_suffix.raw |
|
filtered=$fulldir/op.mnt$mntopt.mkfs$mkfsopt$sync_suffix.filtered |
|
|
|
echo "### xfs_logprint output ###" | tee $raw >$filtered |
|
_scratch_xfs_logprint -c 2>&1 \ |
|
| tee -a $raw \ |
|
| _filter_logprint \ |
|
| _filter_opnum \ |
|
>>$filtered |
|
} |
|
|
|
# start at rec#2 "-s 2" so we skip over UMOUNT record which will always |
|
# be a 512b single header at mkfs time |
|
# and may not match with the FS mounted at a different LR size |
|
# => xlog_do_recovery_pass() can not handle the different hdr sizes |
|
# it assumes them all to be the same between the start..finish |
|
|
|
_print_transaction_inode() |
|
{ |
|
_start=$1 |
|
mkdir $fulldir >/dev/null 2>&1 |
|
mntopt=`echo $MOUNT_OPTIONS | sed 's/ //g'` |
|
mkfsopt=`echo $MKFS_OPTIONS | sed 's/ //g'` |
|
raw=$fulldir/trans_inode.mnt$mntopt.mkfs$mkfsopt$sync_suffix.raw |
|
filtered=$fulldir/trans_inode.mnt$mntopt.mkfs$mkfsopt$sync_suffix.filtered |
|
|
|
echo "### xfs_logprint -t -i -s START output ###" | tee $raw >$filtered |
|
_scratch_xfs_logprint -t -i -s $_start 2>&1 \ |
|
| tee -a $raw \ |
|
| _filter_logprint \ |
|
>>$filtered |
|
} |
|
|
|
_print_transaction_buf() |
|
{ |
|
_start=$1 |
|
mkdir $fulldir >/dev/null 2>&1 |
|
mntopt=`echo $MOUNT_OPTIONS | sed 's/ //g'` |
|
mkfsopt=`echo $MKFS_OPTIONS | sed 's/ //g'` |
|
raw=$fulldir/trans_buf.mnt$mntopt.mkfs$mkfsopt$sync_suffix.raw |
|
filtered=$fulldir/trans_buf.mnt$mntopt.mkfs$mkfsopt$sync_suffix.filtered |
|
|
|
echo "### xfs_logprint -t -b -s START output ###" | tee $raw >$filtered |
|
_scratch_xfs_logprint -t -b -s $_start 2>&1 \ |
|
| tee -a $raw \ |
|
| _filter_logprint \ |
|
>>$filtered |
|
} |
|
|
|
_mkfs_log() |
|
{ |
|
# create the FS |
|
# mkfs options to append to log size otion can be specified ($*) |
|
export MKFS_OPTIONS="-l size=2000b -l lazy-count=1 $*" |
|
_full "mkfs" |
|
_scratch_mkfs_xfs >>$seqres.full 2>&1 |
|
if [ $? -ne 0 ] ; then |
|
_echofull "Cannot mkfs for this test using option specified: $MKFS_OPTIONS" |
|
return 1 |
|
fi |
|
|
|
return 0 |
|
} |
|
|
|
|
|
# |
|
# mount fs and create some log traffic |
|
# |
|
_create_log() |
|
{ |
|
# mount the FS |
|
_full "mount" |
|
_try_scratch_mount >>$seqres.full 2>&1 |
|
if [ $? -ne 0 ] ; then |
|
_echofull "mount failed: $MOUNT_OPTIONS" |
|
return 1 |
|
fi |
|
|
|
# generate some log traffic - but not too much - life gets a little |
|
# more complicated if the log wraps around. This traffic is |
|
# pretty much arbitary, but could probably be made better than this. |
|
touch $SCRATCH_MNT/{0,1,2,3,4,5,6,7,8,9}{0,1,2,3,4,5,6,7,8,9} |
|
|
|
# unmount the FS |
|
_full "umount" |
|
_scratch_unmount >>$seqres.full 2>&1 |
|
if [ $? -ne 0 ] ; then |
|
_echofull "umount failed" |
|
return 1 |
|
fi |
|
|
|
return 0 |
|
} |
|
|
|
# |
|
# mount fs and create some log traffic with sync'ing |
|
# |
|
_create_log_sync() |
|
{ |
|
# mount the FS |
|
_full " mount" |
|
_try_scratch_mount >>$seqres.full 2>&1 |
|
if [ $? -ne 0 ] ; then |
|
_echofull "mount failed: $MOUNT_OPTIONS" |
|
return 1 |
|
fi |
|
|
|
# generate some log traffic - but not too much |
|
# add some syncs to get the log flushed to disk |
|
for file in $SCRATCH_MNT/{0,1,2,3,4,5,6,7,8,9}{0,1,2,3,4,5,6,7,8,9}; do |
|
touch $file |
|
sync |
|
done |
|
|
|
# unmount the FS |
|
_full "umount" |
|
_scratch_unmount >>$seqres.full 2>&1 |
|
if [ $? -ne 0 ] ; then |
|
_echofull "umount failed" |
|
return 1 |
|
fi |
|
} |
|
|
|
_cmp_output() |
|
{ |
|
echo "*** compare logprint: $1 with $2" |
|
if ! diff $1 $2 >/dev/null; then |
|
_fail "logprint output $1 differs to $2" |
|
fi |
|
} |
|
|
|
# |
|
# Op data of different Log Record sizes will mean that data is |
|
# split at different points and in op printing it will not |
|
# try and decode the data which has been split up. |
|
# So we do a special diff processing to complain of differences |
|
# if no split is involved. |
|
# |
|
# Example diff with forms of: |
|
# "Left over region from split log item" |
|
# "Not printing rest of data" |
|
# |
|
# 2149c2149 |
|
# < Left over region from split log item |
|
# --- |
|
# > BUF DATA |
|
# 2888c2888,2889 |
|
# < INODE: #regs: 3 Not printing rest of data |
|
# --- |
|
# > INODE: #regs: 3 ino: 0x80 flags: 0x5 dsize: 16 |
|
# > blkno: <BLKNO> len:<LEN> boff: <BOFF> |
|
# |
|
_process_op_diff() |
|
{ |
|
$AWK_PROG <$1 ' |
|
BEGIN { num_splits = 1; max_splits = 50 } |
|
/^[0-9]/ { |
|
|
|
# ensure a split happened in previous difference |
|
if (num_splits < 1 || num_splits > max_splits) { |
|
print num_splits, " split(s) found prior to diff cmd: ", $0 |
|
num_splits = 1 # shut-up end condition |
|
exit 1 |
|
} |
|
num_splits = 0 |
|
|
|
next |
|
} |
|
/Left over region/ || /Not printing rest/ { |
|
num_splits++ |
|
next |
|
} |
|
{ next } |
|
END { |
|
if (num_splits < 1 || num_splits > max_splits) { |
|
print num_splits, " split(s) found prior to diff end" |
|
exit 1 |
|
} |
|
} |
|
' |
|
return $? |
|
} |
|
|
|
_cmp_op_output() |
|
{ |
|
echo "*** compare logprint: $1 with $2" |
|
|
|
diff $1 $2 >$filtered.diff |
|
if ! _process_op_diff $filtered.diff |
|
then |
|
_fail "logprint output $1 differs to $2 considering splits" |
|
fi |
|
} |
|
|
|
# return xfs log version of device |
|
# e.g. |
|
# _log_version /dev/dsk/dks0d1s4 |
|
# |
|
_log_version() |
|
{ |
|
_dev=$1 |
|
vers=`xfs_db -c 'sb 0' -c 'p versionnum' -r $_dev | $AWK_PROG '{print $3}'` |
|
logver=`echo $vers | sed -e 's/0x[0-9a-f]\([0-9a-f]\)[0-9a-f][0-9a-f]/\1/'` |
|
if [ $logver = 4 -o $logver = 5 -o $logver = 6 -o $logver = 7 -o \ |
|
$logver = c -o $logver = d -o $logver = e -o $logver = f ]; then |
|
echo 2 |
|
else |
|
echo 1 |
|
fi |
|
} |
|
|
|
_require_v2log() |
|
{ |
|
# test out mkfs to see if it supports "-l version=2" |
|
export MKFS_OPTIONS="-l version=2" |
|
if ! _scratch_mkfs_xfs >>$seqres.full 2>&1; then |
|
_notrun "mkfs does not support v2 logs" |
|
fi |
|
|
|
# test out mount to see if it mounts a v2 log fs |
|
export MOUNT_OPTIONS="-o logbsize=32k" |
|
if ! _try_scratch_mount >>$seqres.full 2>&1; then |
|
_notrun "mount/kernel does not support v2 logs" |
|
fi |
|
|
|
# check after unmount to see if it is clean |
|
# i.e. it is not a 6.5.25 buggy version checking kernel |
|
touch $SCRATCH_MNT/file |
|
_scratch_unmount >>$seqres.full 2>&1 |
|
if _scratch_xfs_logprint -t | tee -a $seqres.full \ |
|
| head | grep -q "<DIRTY>"; then |
|
_notrun "kernel does not support v2 logs" |
|
fi |
|
|
|
# otherwise presume it does support v2 logs...:) |
|
} |
|
|
|
_require_logstate() |
|
{ |
|
case "$FSTYP" in |
|
xfs) |
|
if [ -z "$XFS_LOGPRINT_PROG" ]; then |
|
_notrun "This test requires xfs_logprint utility." |
|
fi |
|
;; |
|
f2fs) |
|
if [ -z "$DUMP_F2FS_PROG" ]; then |
|
_notrun "This test requires dump.f2fs utility." |
|
fi |
|
;; |
|
ext4) |
|
if [ -z "$DUMPE2FS_PROG" ]; then |
|
_notrun "This test requires dumpe2fs utility." |
|
fi |
|
;; |
|
*) |
|
_notrun "$FSTYP does not support log state probing." |
|
;; |
|
esac |
|
} |
|
|
|
_xfs_log_config() |
|
{ |
|
echo "# mkfs-opt mount-opt" |
|
echo "# ------------------------------" |
|
echo " version=2 logbsize=32k" |
|
echo " version=2,su=4096 logbsize=32k" |
|
echo " version=2,su=32768 logbsize=32k" |
|
echo " version=2,su=32768 logbsize=64k" |
|
echo " version=2 logbsize=64k" |
|
echo " version=2,su=64k logbsize=64k" |
|
echo " version=2 logbsize=128k" |
|
echo " version=2,su=128k logbsize=128k" |
|
echo " version=2 logbsize=256k" |
|
echo " version=2,su=256k logbsize=256k" |
|
} |
|
|
|
_f2fs_log_config() |
|
{ |
|
echo "# mkfs-opt mount-opt" |
|
echo "# ------------------------------" |
|
echo " test1 active_logs=6,background_gc=off" |
|
echo " test2 active_logs=6,background_gc=off,inline_data" |
|
echo " test3 active_logs=6,background_gc=off,inline_dentry" |
|
echo " test4 active_logs=6,background_gc=off,inline_data,inline_dentry" |
|
echo " test5 active_logs=6,background_gc=off,disable_roll_forward" |
|
echo " test6 active_logs=6,background_gc=off,discard,inline_data,inline_dentry" |
|
echo " test7 active_logs=6,background_gc=on" |
|
echo " test8 active_logs=6,background_gc=on,inline_data" |
|
echo " test9 active_logs=6,background_gc=on,inline_data,inline_dentry" |
|
echo " test10 active_logs=6,background_gc=on,discard,inline_data,inline_dentry" |
|
} |
|
|
|
_ext4_log_config() |
|
{ |
|
echo "# mkfs-opt mount-opt" |
|
echo "# ------------------------------" |
|
echo " /dev/null data=writeback" |
|
echo " /dev/null data=ordered" |
|
echo " /dev/null data=journal" |
|
echo " /dev/null data=ordered,data_err=abort" |
|
echo " /dev/null data=writeback,nojournal_checksum" |
|
echo " /dev/null data=ordered,nojournal_checksum" |
|
echo " /dev/null data=journal,nojournal_checksum" |
|
echo " /dev/null data=ordered,data_err=abort,nojournal_checksum" |
|
echo " /dev/null data=writeback,journal_checksum" |
|
echo " /dev/null data=ordered,journal_checksum" |
|
} |
|
|
|
_get_log_configs() |
|
{ |
|
case "$FSTYP" in |
|
xfs) |
|
_xfs_log_config |
|
;; |
|
f2fs) |
|
_f2fs_log_config |
|
;; |
|
ext4) |
|
_ext4_log_config |
|
;; |
|
*) |
|
_notrun "$FSTYP does not support log configs." |
|
;; |
|
esac |
|
} |
|
|
|
# Unmount and remount the scratch device, dumping the log |
|
_scratch_remount_dump_log() |
|
{ |
|
local opts="$1" |
|
|
|
if test -n "$opts"; then |
|
opts="-o $opts" |
|
fi |
|
_scratch_unmount |
|
_scratch_dump_log |
|
_scratch_mount "$opts" |
|
} |
|
|
|
# Unmount and remount the test device, dumping the log |
|
_test_remount_dump_log() |
|
{ |
|
local opts="$1" |
|
|
|
if test -n "$opts"; then |
|
opts="-o $opts" |
|
fi |
|
_test_unmount |
|
_test_dump_log |
|
_test_mount "$opts" |
|
} |
|
|
|
# make sure this script returns success |
|
/bin/true |