| ##/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) |
| $DUMPE2FS_PROG -h $SCRATCH_DEV |
| ;; |
| *) |
| ;; |
| esac |
| } |
| |
| _test_dump_log() |
| { |
| case "$FSTYP" in |
| xfs) |
| _test_xfs_logprint |
| ;; |
| f2fs) |
| $DUMP_F2FS_PROG $TEST_DEV |
| ;; |
| ext4) |
| $DUMPE2FS_PROG -h $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 |
| } |
| |
| # make sure this script returns success |
| /bin/true |