Files
apfstests/common/rc
T
Brian Foster f170290a83 xfstests: create a test for xfs log grant head leak detection
Changes in the XFS logging code have lead to small leaks in the log
grant heads that consume log space slowly over time. Such problems have
gone undetected for an unnecessarily long time due to code complexity
and potential for very subtle problems. Losing only a few bytes per
logged item on a reasonably large enough fs (10s of GB) means only the
most continuously stressful workloads will cause a severe enough failure
(deadlock due to log reservation exhaustion) quickly enough to indicate
something is seriously wrong.

Recent changes in XFS export the state of the various log heads through
sysfs to aid in userspace/runtime analysis of the log. This test runs a
workload against an XFS filesystem, quiesces the fs and verifies that
the log reserve and write grant heads have not leaked any space with
respect to the current head of the physical log.

Signed-off-by: Brian Foster <bfoster@redhat.com>
Reviewed-by: Dave Chinner <dchinner@redhat.com>
Signed-off-by: Dave Chinner <david@fromorbit.com>
2014-06-18 09:33:46 +10:00

2283 lines
51 KiB
Plaintext

##/bin/bash
#-----------------------------------------------------------------------
# Copyright (c) 2000-2006 Silicon Graphics, Inc. All Rights Reserved.
# This program is free software; you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation; either version 2 of the License, or
# (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program; if not, write to the Free Software
# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
# USA
#
# Contact information: Silicon Graphics, Inc., 1500 Crittenden Lane,
# Mountain View, CA 94043, USA, or: http://www.sgi.com
#-----------------------------------------------------------------------
BC=$(which bc 2> /dev/null) || BC=
_require_math()
{
if [ -z "$BC" ]; then
_notrun "this test requires 'bc' tool for doing math operations"
fi
}
_math()
{
[ $# -le 0 ] && return
if [ "$BC" ]; then
result=$(LANG=C echo "scale=0; $@" | "$BC" -q 2> /dev/null)
else
_notrun "this test requires 'bc' tool for doing math operations"
fi
echo "$result"
}
dd()
{
if [ "$HOSTOS" == "Linux" ]
then
command dd --help | grep noxfer > /dev/null 2>&1
if [ "$?" -eq 0 ]
then
command dd status=noxfer $@
else
command dd $@
fi
else
command dd $@
fi
}
_btrfs_get_subvolid()
{
mnt=$1
name=$2
$BTRFS_UTIL_PROG sub list $mnt | grep $name | awk '{ print $2 }'
}
# Prints the md5 checksum of a given file
_md5_checksum()
{
md5sum $1 | cut -d ' ' -f1
}
# ls -l w/ selinux sometimes puts a dot at the end:
# -rwxrw-r--. id1 id2 file1
# Also filter out lost+found directory on extN file system if present
_ls_l()
{
ls -l $* | sed "s/\(^[-rwxdlbcpsStT]*\)\. /\1 /" | grep -v 'lost+found'
}
# we need common/config
if [ "$iam" != "check" ]
then
if ! . ./common/config
then
echo "$iam: failed to source common/config"
exit 1
fi
fi
# check for correct setup
case "$FSTYP" in
xfs)
[ "$XFS_LOGPRINT_PROG" = "" ] && _fatal "xfs_logprint not found"
[ "$XFS_REPAIR_PROG" = "" ] && _fatal "xfs_repair not found"
[ "$XFS_DB_PROG" = "" ] && _fatal "xfs_db not found"
[ "$MKFS_XFS_PROG" = "" ] && _fatal "mkfs_xfs not found"
;;
udf)
[ "$MKFS_UDF_PROG" = "" ] && _fatal "mkfs_udf/mkudffs not found"
;;
btrfs)
[ "$MKFS_BTRFS_PROG" = "" ] && _fatal "mkfs.btrfs not found"
;;
nfs)
;;
esac
# make sure we have a standard umask
umask 022
_mount()
{
$MOUNT_PROG `_mount_ops_filter $*`
}
_scratch_options()
{
type=$1
SCRATCH_OPTIONS=""
if [ "$FSTYP" != "xfs" ]; then
return
fi
case $type in
mkfs)
[ "$HOSTOS" != "IRIX" ] && SCRATCH_OPTIONS="$SCRATCH_OPTIONS -f"
rt_opt="-r"
log_opt="-l"
;;
mount)
rt_opt="-o"
log_opt="-o"
;;
esac
[ "$USE_EXTERNAL" = yes -a ! -z "$SCRATCH_RTDEV" ] && \
SCRATCH_OPTIONS="$SCRATCH_OPTIONS ${rt_opt}rtdev=$SCRATCH_RTDEV"
[ "$USE_EXTERNAL" = yes -a ! -z "$SCRATCH_LOGDEV" ] && \
SCRATCH_OPTIONS="$SCRATCH_OPTIONS ${log_opt}logdev=$SCRATCH_LOGDEV"
}
_test_options()
{
type=$1
TEST_OPTIONS=""
if [ "$FSTYP" != "xfs" ]; then
return
fi
case $type in
mkfs)
rt_opt="-r"
log_opt="-l"
;;
mount)
rt_opt="-o"
log_opt="-o"
;;
esac
[ "$USE_EXTERNAL" = yes -a ! -z "$TEST_RTDEV" ] && \
TEST_OPTIONS="$TEST_OPTIONS ${rt_opt}rtdev=$TEST_RTDEV"
[ "$USE_EXTERNAL" = yes -a ! -z "$TEST_LOGDEV" ] && \
TEST_OPTIONS="$TEST_OPTIONS ${log_opt}logdev=$TEST_LOGDEV"
}
_mount_ops_filter()
{
params="$*"
#get mount point to handle dmapi mtpt option correctly
let last_index=$#-1
[ $last_index -gt 0 ] && shift $last_index
FS_ESCAPED=$1
# irix is fussy about how it is fed its mount options
# - multiple -o's are not allowed
# - no spaces between comma delimitered options
# the sed script replaces all -o's (except the first) with a comma
# not required for linux, but won't hurt
echo $params | sed -e 's/[[:space:]]\+-o[[:space:]]*/UnIqUe/1; s/[[:space:]]\+-o[[:space:]]*/,/g; s/UnIqUe/ -o /1' \
| sed -e 's/dmapi/dmi/' \
| $PERL_PROG -ne "s#mtpt=[^,|^\n|^\s]*#mtpt=$FS_ESCAPED\1\2#; print;"
}
_scratch_mount_options()
{
_scratch_options mount
echo $SCRATCH_OPTIONS $MOUNT_OPTIONS $SELINUX_MOUNT_OPTIONS $* $SCRATCH_DEV $SCRATCH_MNT
}
_scratch_mount()
{
_mount -t $FSTYP `_scratch_mount_options $*`
}
_scratch_unmount()
{
$UMOUNT_PROG $SCRATCH_DEV
}
_scratch_remount()
{
_scratch_unmount
_scratch_mount
}
_test_mount()
{
_test_options mount
_mount -t $FSTYP $TEST_OPTIONS $TEST_FS_MOUNT_OPTS $SELINUX_MOUNT_OPTIONS $* $TEST_DEV $TEST_DIR
}
_scratch_mkfs_options()
{
_scratch_options mkfs
echo $SCRATCH_OPTIONS $MKFS_OPTIONS $* $SCRATCH_DEV
}
_scratch_metadump()
{
dumpfile=$1
options=
[ "$USE_EXTERNAL" = yes -a ! -z "$SCRATCH_LOGDEV" ] && \
options="-l $SCRATCH_LOGDEV"
xfs_metadump $options $SCRATCH_DEV $dumpfile
}
_setup_large_xfs_fs()
{
fs_size=$1
local tmp_dir=/tmp/
[ "$LARGE_SCRATCH_DEV" != yes ] && return 0
[ -z "$SCRATCH_DEV_EMPTY_SPACE" ] && SCRATCH_DEV_EMPTY_SPACE=0
[ $SCRATCH_DEV_EMPTY_SPACE -ge $fs_size ] && return 0
# calculate the size of the file we need to allocate.
# Default free space in the FS is 50GB, but you can specify more via
# SCRATCH_DEV_EMPTY_SPACE
file_size=$(($fs_size - 50*1024*1024*1024))
file_size=$(($file_size - $SCRATCH_DEV_EMPTY_SPACE))
# mount the filesystem, create the file, unmount it
_scratch_mount 2>&1 >$tmp_dir/mnt.err
local status=$?
if [ $status -ne 0 ]; then
echo "mount failed"
cat $tmp_dir/mnt.err >&2
rm -f $tmp_dir/mnt.err
return $status
fi
rm -f $tmp_dir/mnt.err
xfs_io -F -f \
-c "truncate $file_size" \
-c "falloc -k 0 $file_size" \
-c "chattr +d" \
$SCRATCH_MNT/.use_space 2>&1 > /dev/null
export NUM_SPACE_FILES=1
status=$?
umount $SCRATCH_MNT
if [ $status -ne 0 ]; then
echo "large file prealloc failed"
cat $tmp_dir/mnt.err >&2
return $status
fi
return 0
}
_scratch_mkfs_xfs_opts()
{
mkfs_opts=$*
_scratch_options mkfs
$MKFS_XFS_PROG $SCRATCH_OPTIONS $mkfs_opts $SCRATCH_DEV
}
_scratch_mkfs_xfs_supported()
{
mkfs_opts=$*
_scratch_options mkfs
$MKFS_XFS_PROG -N $MKFS_OPTIONS $SCRATCH_OPTIONS $mkfs_opts $SCRATCH_DEV
}
_scratch_mkfs_xfs()
{
# extra mkfs options can be added by tests
local extra_mkfs_options=$*
local tmp_dir=/tmp/
# save mkfs output in case conflict means we need to run again.
# only the output for the mkfs that applies should be shown
_scratch_mkfs_xfs_opts $MKFS_OPTIONS $extra_mkfs_options \
2>$tmp_dir.mkfserr 1>$tmp_dir.mkfsstd
local mkfs_status=$?
# a mkfs failure may be caused by conflicts between
# $MKFS_OPTIONS and $extra_mkfs_options
if [ $mkfs_status -ne 0 -a ! -z "$extra_mkfs_options" ]; then
(
echo -n "** mkfs failed with extra mkfs options "
echo "added to \"$MKFS_OPTIONS\" by test $seq **"
echo -n "** attempting to mkfs using only test $seq "
echo "options: $extra_mkfs_options **"
) >> $seqres.full
# running mkfs again. overwrite previous mkfs output files
_scratch_mkfs_xfs_opts $extra_mkfs_options \
2>$tmp_dir.mkfserr 1>$tmp_dir.mkfsstd
local mkfs_status=$?
fi
if [ $mkfs_status -eq 0 -a "$LARGE_SCRATCH_DEV" = yes ]; then
# manually parse the mkfs output to get the fs size in bytes
local fs_size
fs_size=`cat $tmp_dir.mkfsstd | perl -ne '
if (/^data\s+=\s+bsize=(\d+)\s+blocks=(\d+)/) {
my $size = $1 * $2;
print STDOUT "$size\n";
}'`
_setup_large_xfs_fs $fs_size
mkfs_status=$?
fi
# output stored mkfs output
cat $tmp_dir.mkfserr >&2
cat $tmp_dir.mkfsstd
rm -f $tmp_dir.mkfserr $tmp_dir.mkfsstd
return $mkfs_status
}
# xfs_check script is planned to be deprecated. But, we want to
# be able to invoke "xfs_check" behavior in xfstests in order to
# maintain the current verification levels.
_xfs_check()
{
OPTS=" "
DBOPTS=" "
USAGE="Usage: xfs_check [-fsvV] [-l logdev] [-i ino]... [-b bno]... special"
while getopts "b:fi:l:stvV" c
do
case $c in
s) OPTS=$OPTS"-s ";;
t) OPTS=$OPTS"-t ";;
v) OPTS=$OPTS"-v ";;
i) OPTS=$OPTS"-i "$OPTARG" ";;
b) OPTS=$OPTS"-b "$OPTARG" ";;
f) DBOPTS=$DBOPTS" -f";;
l) DBOPTS=$DBOPTS" -l "$OPTARG" ";;
V) $XFS_DB_PROG -p xfs_check -V
return $?
;;
esac
done
set -- extra $@
shift $OPTIND
case $# in
1) ${XFS_DB_PROG}${DBOPTS} -F -i -p xfs_check -c "check$OPTS" $1
status=$?
;;
2) echo $USAGE 1>&1
status=2
;;
esac
return $status
}
_setup_large_ext4_fs()
{
fs_size=$1
local tmp_dir=/tmp/
[ "$LARGE_SCRATCH_DEV" != yes ] && return 0
[ -z "$SCRATCH_DEV_EMPTY_SPACE" ] && SCRATCH_DEV_EMPTY_SPACE=0
[ $SCRATCH_DEV_EMPTY_SPACE -ge $fs_size ] && return 0
# Default free space in the FS is 50GB, but you can specify more via
# SCRATCH_DEV_EMPTY_SPACE
space_to_consume=$(($fs_size - 50*1024*1024*1024 - $SCRATCH_DEV_EMPTY_SPACE))
# mount the filesystem and create 16TB - 4KB files until we consume
# all the necessary space.
_scratch_mount 2>&1 >$tmp_dir/mnt.err
local status=$?
if [ $status -ne 0 ]; then
echo "mount failed"
cat $tmp_dir/mnt.err >&2
rm -f $tmp_dir/mnt.err
return $status
fi
rm -f $tmp_dir/mnt.err
file_size=$((16*1024*1024*1024*1024 - 4096))
nfiles=0
while [ $space_to_consume -gt $file_size ]; do
xfs_io -F -f \
-c "truncate $file_size" \
-c "falloc -k 0 $file_size" \
$SCRATCH_MNT/.use_space.$nfiles 2>&1
status=$?
if [ $status -ne 0 ]; then
break;
fi
space_to_consume=$(( $space_to_consume - $file_size ))
nfiles=$(($nfiles + 1))
done
# consume the remaining space.
if [ $space_to_consume -gt 0 ]; then
xfs_io -F -f \
-c "truncate $space_to_consume" \
-c "falloc -k 0 $space_to_consume" \
$SCRATCH_MNT/.use_space.$nfiles 2>&1
status=$?
fi
export NUM_SPACE_FILES=$nfiles
umount $SCRATCH_MNT
if [ $status -ne 0 ]; then
echo "large file prealloc failed"
cat $tmp_dir/mnt.err >&2
return $status
fi
return 0
}
_scratch_mkfs_ext4()
{
local tmp_dir=/tmp/
/sbin/mkfs -t $FSTYP -- -F $MKFS_OPTIONS $* $SCRATCH_DEV \
2>$tmp_dir.mkfserr 1>$tmp_dir.mkfsstd
local mkfs_status=$?
if [ $mkfs_status -eq 0 -a "$LARGE_SCRATCH_DEV" = yes ]; then
# manually parse the mkfs output to get the fs size in bytes
fs_size=`cat $tmp_dir.mkfsstd | awk ' \
/^Block size/ { split($2, a, "="); bs = a[2] ; } \
/ inodes, / { blks = $3 } \
/reserved for the super user/ { resv = $1 } \
END { fssize = bs * blks - resv; print fssize }'`
_setup_large_ext4_fs $fs_size
mkfs_status=$?
fi
# output stored mkfs output
cat $tmp_dir.mkfserr >&2
cat $tmp_dir.mkfsstd
rm -f $tmp_dir.mkfserr $tmp_dir.mkfsstd
return $mkfs_status
}
_test_mkfs()
{
case $FSTYP in
nfs*)
# do nothing for nfs
;;
udf)
$MKFS_UDF_PROG $MKFS_OPTIONS $* $TEST_DEV > /dev/null
;;
btrfs)
$MKFS_BTRFS_PROG $MKFS_OPTIONS $* $TEST_DEV > /dev/null
;;
*)
yes | $MKFS_PROG -t $FSTYP -- $MKFS_OPTIONS $* $TEST_DEV
;;
esac
}
_scratch_mkfs()
{
case $FSTYP in
xfs)
_scratch_mkfs_xfs $*
;;
nfs*)
# do nothing for nfs
;;
udf)
$MKFS_UDF_PROG $MKFS_OPTIONS $* $SCRATCH_DEV > /dev/null
;;
btrfs)
$MKFS_BTRFS_PROG $MKFS_OPTIONS $* $SCRATCH_DEV > /dev/null
;;
ext4)
_scratch_mkfs_ext4 $*
;;
tmpfs)
# do nothing for tmpfs
;;
*)
yes | $MKFS_PROG -t $FSTYP -- $MKFS_OPTIONS $* $SCRATCH_DEV
;;
esac
}
_scratch_pool_mkfs()
{
case $FSTYP in
btrfs)
$MKFS_BTRFS_PROG $MKFS_OPTIONS $* $SCRATCH_DEV_POOL > /dev/null
;;
*)
echo "_scratch_pool_mkfs is not implemented for $FSTYP" 1>&2
;;
esac
}
# Create fs of certain size on scratch device
# _scratch_mkfs_sized <size in bytes> [optional blocksize]
_scratch_mkfs_sized()
{
fssize=$1
blocksize=$2
[ -z "$blocksize" ] && blocksize=4096
re='^[0-9]+$'
if ! [[ $fssize =~ $re ]] ; then
_notrun "error: _scratch_mkfs_sized: fs size \"$fssize\" not an integer."
fi
if ! [[ $blocksize =~ $re ]] ; then
_notrun "error: _scratch_mkfs_sized: block size \"$blocksize\" not an integer."
fi
blocks=`expr $fssize / $blocksize`
if [ "$HOSTOS" == "Linux" ]; then
devsize=`blockdev --getsize64 $SCRATCH_DEV`
[ "$fssize" -gt "$devsize" ] && _notrun "Scratch device too small"
fi
case $FSTYP in
xfs)
# don't override MKFS_OPTIONS that set a block size.
echo $MKFS_OPTIONS |egrep -q "b?size="
if [ $? -eq 0 ]; then
_scratch_mkfs_xfs -d size=$fssize
else
_scratch_mkfs_xfs -d size=$fssize -b size=$blocksize
fi
;;
ext2|ext3|ext4|ext4dev)
yes | ${MKFS_PROG}.$FSTYP $MKFS_OPTIONS -b $blocksize $SCRATCH_DEV $blocks
;;
udf)
$MKFS_UDF_PROG $MKFS_OPTIONS -b $blocksize $SCRATCH_DEV $blocks
;;
btrfs)
$MKFS_BTRFS_PROG $MKFS_OPTIONS -b $fssize $SCRATCH_DEV
;;
*)
_notrun "Filesystem $FSTYP not supported in _scratch_mkfs_sized"
;;
esac
}
# Emulate an N-data-disk stripe w/ various stripe units
# _scratch_mkfs_geom <sunit bytes> <swidth multiplier> [optional blocksize]
_scratch_mkfs_geom()
{
sunit_bytes=$1
swidth_mult=$2
blocksize=$3
[ -z "$blocksize" ] && blocksize=4096
let sunit_blocks=$sunit_bytes/$blocksize
let swidth_blocks=$sunit_blocks*$swidth_mult
case $FSTYP in
xfs)
MKFS_OPTIONS+=" -b size=$blocksize, -d su=$sunit_bytes,sw=$swidth_mult"
;;
ext4|ext4dev)
MKFS_OPTIONS+=" -b $blocksize -E stride=$sunit_blocks,stripe_width=$swidth_blocks"
;;
*)
_notrun "can't mkfs $FSTYP with geometry"
;;
esac
_scratch_mkfs
}
_scratch_resvblks()
{
case $FSTYP in
xfs)
xfs_io -x -c "resblks $1" $SCRATCH_MNT
;;
*)
;;
esac
}
_scratch_xfs_db_options()
{
SCRATCH_OPTIONS=""
[ "$USE_EXTERNAL" = yes -a ! -z "$SCRATCH_LOGDEV" ] && \
SCRATCH_OPTIONS="-l$SCRATCH_LOGDEV"
echo $SCRATCH_OPTIONS $* $SCRATCH_DEV
}
_scratch_xfs_logprint()
{
SCRATCH_OPTIONS=""
[ "$USE_EXTERNAL" = yes -a ! -z "$SCRATCH_LOGDEV" ] && \
SCRATCH_OPTIONS="-l$SCRATCH_LOGDEV"
$XFS_LOGPRINT_PROG $SCRATCH_OPTIONS $* $SCRATCH_DEV
}
_scratch_xfs_check()
{
SCRATCH_OPTIONS=""
[ "$USE_EXTERNAL" = yes -a ! -z "$SCRATCH_LOGDEV" ] && \
SCRATCH_OPTIONS="-l $SCRATCH_LOGDEV"
[ "$LARGE_SCRATCH_DEV" = yes ] && \
SCRATCH_OPTIONS=$SCRATCH_OPTIONS" -t"
_xfs_check $SCRATCH_OPTIONS $* $SCRATCH_DEV
}
_scratch_xfs_repair()
{
SCRATCH_OPTIONS=""
[ "$USE_EXTERNAL" = yes -a ! -z "$SCRATCH_LOGDEV" ] && \
SCRATCH_OPTIONS="-l$SCRATCH_LOGDEV"
[ "$USE_EXTERNAL" = yes -a ! -z "$SCRATCH_RTDEV" ] && \
SCRATCH_OPTIONS=$SCRATCH_OPTIONS" -r$SCRATCH_RTDEV"
[ "$LARGE_SCRATCH_DEV" = yes ] && SCRATCH_OPTIONS=$SCRATCH_OPTIONS" -t"
$XFS_REPAIR_PROG $SCRATCH_OPTIONS $* $SCRATCH_DEV
}
_get_pids_by_name()
{
if [ $# -ne 1 ]
then
echo "Usage: _get_pids_by_name process-name" 1>&2
exit 1
fi
# Algorithm ... all ps(1) variants have a time of the form MM:SS or
# HH:MM:SS before the psargs field, use this as the search anchor.
#
# Matches with $1 (process-name) occur if the first psarg is $1
# or ends in /$1 ... the matching uses sed's regular expressions,
# so passing a regex into $1 will work.
ps $PS_ALL_FLAGS \
| sed -n \
-e 's/$/ /' \
-e 's/[ ][ ]*/ /g' \
-e 's/^ //' \
-e 's/^[^ ]* //' \
-e "/[0-9]:[0-9][0-9] *[^ ]*\/$1 /s/ .*//p" \
-e "/[0-9]:[0-9][0-9] *$1 /s/ .*//p"
}
# fix malloc libs output
#
_fix_malloc()
{
# filter out the Electric Fence notice
$PERL_PROG -e '
while (<>) {
if (defined $o && /^\s+Electric Fence/) {
chomp($o);
print "$o";
undef $o;
next;
}
print $o if (defined $o);
$o=$_;
}
print $o if (defined $o);
'
}
# check if run as root
#
_need_to_be_root()
{
id=`id | $SED_PROG -e 's/(.*//' -e 's/.*=//'`
if [ "$id" -ne 0 ]
then
echo "Arrgh ... you need to be root (not uid=$id) to run this test"
exit 1
fi
}
#
# _df_device : get an IRIX style df line for a given device
#
# - returns "" if not mounted
# - returns fs type in field two (ala IRIX)
# - joins line together if split by fancy df formatting
# - strips header etc
#
_df_device()
{
if [ $# -ne 1 ]
then
echo "Usage: _df_device device" 1>&2
exit 1
fi
$DF_PROG 2>/dev/null | $AWK_PROG -v what=$1 '
match($1,what) && NF==1 {
v=$1
getline
print v, $0
exit
}
match($1,what) {
print
exit
}
'
}
#
# _df_dir : get an IRIX style df line for device where a directory resides
#
# - returns fs type in field two (ala IRIX)
# - joins line together if split by fancy df formatting
# - strips header etc
#
_df_dir()
{
if [ $# -ne 1 ]
then
echo "Usage: _df_dir device" 1>&2
exit 1
fi
$DF_PROG $1 2>/dev/null | $AWK_PROG -v what=$1 '
NR == 2 && NF==1 {
v=$1
getline
print v, $0;
exit 0
}
NR == 2 {
print;
exit 0
}
{}
'
# otherwise, nada
}
# return percentage used disk space for mounted device
_used()
{
if [ $# -ne 1 ]
then
echo "Usage: _used device" 1>&2
exit 1
fi
_df_device $1 | $AWK_PROG '{ sub("%", "") ; print $6 }'
}
# return the FS type of a mounted device
#
_fs_type()
{
if [ $# -ne 1 ]
then
echo "Usage: _fs_type device" 1>&2
exit 1
fi
#
# The Linux kernel shows NFSv4 filesystems in df output as
# filesystem type nfs4, although we mounted it as nfs earlier.
# Fix the filesystem type up here so that the callers don't
# have to bother with this quirk.
#
_df_device $1 | $AWK_PROG '{ print $2 }' | sed -e 's/nfs4/nfs/'
}
# return the FS mount options of a mounted device
#
# should write a version which just parses the output of mount for IRIX
# compatibility, but since this isn't used at all, at the moment I'll leave
# this for now
#
_fs_options()
{
if [ $# -ne 1 ]
then
echo "Usage: _fs_options device" 1>&2
exit 1
fi
$AWK_PROG -v dev=$1 '
match($1,dev) { print $4 }
' </proc/mounts
}
# returns device number if a file is a block device
#
_is_block_dev()
{
if [ $# -ne 1 ]
then
echo "Usage: _is_block_dev dev" 1>&2
exit 1
fi
_dev=$1
if [ -L "${_dev}" ]; then
_dev=`readlink -f ${_dev}`
fi
if [ -b "${_dev}" ]; then
src/lstat64 ${_dev} | $AWK_PROG '/Device type:/ { print $9 }'
fi
}
# Do a command, log it to $seqres.full, optionally test return status
# and die if command fails. If called with one argument _do executes the
# command, logs it, and returns its exit status. With two arguments _do
# first prints the message passed in the first argument, and then "done"
# or "fail" depending on the return status of the command passed in the
# second argument. If the command fails and the variable _do_die_on_error
# is set to "always" or the two argument form is used and _do_die_on_error
# is set to "message_only" _do will print an error message to
# $seqres.out and exit.
_do()
{
if [ $# -eq 1 ]; then
_cmd=$1
elif [ $# -eq 2 ]; then
_note=$1
_cmd=$2
echo -n "$_note... "
else
echo "Usage: _do [note] cmd" 1>&2
status=1; exit
fi
(eval "echo '---' \"$_cmd\"") >>$seqres.full
(eval "$_cmd") >$tmp._out 2>&1; ret=$?
cat $tmp._out | _fix_malloc >>$seqres.full
if [ $# -eq 2 ]; then
if [ $ret -eq 0 ]; then
echo "done"
else
echo "fail"
fi
fi
if [ $ret -ne 0 ] \
&& [ "$_do_die_on_error" = "always" \
-o \( $# -eq 2 -a "$_do_die_on_error" = "message_only" \) ]
then
[ $# -ne 2 ] && echo
eval "echo \"$_cmd\" failed \(returned $ret\): see $seqres.full"
status=1; exit
fi
return $ret
}
# bail out, setting up .notrun file
#
_notrun()
{
echo "$*" > $seqres.notrun
echo "$seq not run: $*"
status=0
exit
}
# just plain bail out
#
_fail()
{
echo "$*" | tee -a $seqres.full
echo "(see $seqres.full for details)"
status=1
exit 1
}
# tests whether $FSTYP is one of the supported filesystems for a test
#
_supported_fs()
{
for f
do
if [ "$f" = "$FSTYP" -o "$f" = "generic" ]
then
return
fi
done
_notrun "not suitable for this filesystem type: $FSTYP"
}
# tests whether $FSTYP is one of the supported OSes for a test
#
_supported_os()
{
for h
do
if [ "$h" = "$HOSTOS" ]
then
return
fi
done
_notrun "not suitable for this OS: $HOSTOS"
}
# this test needs a scratch partition - check we're ok & unmount it
#
_require_scratch()
{
case "$FSTYP" in
nfs*)
_notrun "requires a scratch device"
;;
tmpfs)
if [ -z "$SCRATCH_DEV" -o ! -d "$SCRATCH_MNT" ];
then
_notrun "this test requires a valid \$SCRATCH_MNT and unique $SCRATCH_DEV"
fi
;;
*)
if [ -z "$SCRATCH_DEV" -o "`_is_block_dev $SCRATCH_DEV`" = "" ]
then
_notrun "this test requires a valid \$SCRATCH_DEV"
fi
if [ "`_is_block_dev $SCRATCH_DEV`" = "`_is_block_dev $TEST_DEV`" ]
then
_notrun "this test requires a valid \$SCRATCH_DEV"
fi
if [ ! -d "$SCRATCH_MNT" ]
then
_notrun "this test requires a valid \$SCRATCH_MNT"
fi
;;
esac
# mounted?
if _mount | grep -q $SCRATCH_DEV
then
# if it's mounted, make sure its on $SCRATCH_MNT
if ! _mount | grep $SCRATCH_DEV | grep -q $SCRATCH_MNT
then
echo "\$SCRATCH_DEV is mounted but not on \$SCRATCH_MNT - aborting"
exit 1
fi
# and then unmount it
if ! $UMOUNT_PROG $SCRATCH_DEV
then
echo "failed to unmount $SCRATCH_DEV"
exit 1
fi
fi
}
# this test needs a logdev
#
_require_logdev()
{
[ -z "$SCRATCH_LOGDEV" -o ! -b "$SCRATCH_LOGDEV" ] && \
_notrun "This test requires a valid \$SCRATCH_LOGDEV"
[ "$USE_EXTERNAL" != yes ] && \
_notrun "This test requires USE_EXTERNAL to be enabled"
# ensure its not mounted
$UMOUNT_PROG $SCRATCH_LOGDEV 2>/dev/null
}
# this test requires loopback device support
#
_require_loop()
{
if [ "$HOSTOS" != "Linux" ]
then
_notrun "This test requires linux for loopback device support"
fi
modprobe loop >/dev/null 2>&1
if grep loop /proc/devices >/dev/null 2>&1
then
:
else
_notrun "This test requires loopback device support"
fi
}
# this test requires ext2 filesystem support
#
_require_ext2()
{
if [ "$HOSTOS" != "Linux" ]
then
_notrun "This test requires linux for ext2 filesystem support"
fi
modprobe ext2 >/dev/null 2>&1
if grep ext2 /proc/filesystems >/dev/null 2>&1
then
:
else
_notrun "This test requires ext2 filesystem support"
fi
}
# this test requires that (large) loopback device files are not in use
#
_require_no_large_scratch_dev()
{
[ "$LARGE_SCRATCH_DEV" = yes ] && \
_notrun "Large filesystem testing in progress, skipped this test"
}
# this test requires that a realtime subvolume is in use, and
# that the kernel supports realtime as well.
#
_require_realtime()
{
[ "$USE_EXTERNAL" = yes ] || \
_notrun "External volumes not in use, skipped this test"
[ "$SCRATCH_RTDEV" = "" ] && \
_notrun "Realtime device required, skipped this test"
}
# this test requires that a specified command (executable) exists
# $1 - command, $2 - name for error message
#
_require_command()
{
[ -n "$1" ] && _cmd="$1" || _cmd="$2"
[ -n "$1" -a -x "$1" ] || _notrun "$_cmd utility required, skipped this test"
}
# this test requires the device mapper flakey target
#
_require_dm_flakey()
{
_require_command $DMSETUP_PROG
modprobe dm-flakey >/dev/null 2>&1
$DMSETUP_PROG targets | grep flakey >/dev/null 2>&1
if [ $? -eq 0 ]
then
:
else
_notrun "This test requires dm flakey support"
fi
}
# this test requires the projid32bit feature to be available in mkfs.xfs.
#
_require_projid32bit()
{
_scratch_mkfs_xfs_supported -i projid32bit=1 >/dev/null 2>&1 \
|| _notrun "mkfs.xfs doesn't have projid32bit feature"
}
_require_projid16bit()
{
_scratch_mkfs_xfs_supported -i projid32bit=0 >/dev/null 2>&1 \
|| _notrun "16 bit project IDs not supported on $SCRATCH_DEV"
}
# this test requires the crc feature to be available in mkfs.xfs
#
_require_xfs_mkfs_crc()
{
_scratch_mkfs_xfs_supported -m crc=1 >/dev/null 2>&1 \
|| _notrun "mkfs.xfs doesn't have crc feature"
}
# this test requires the xfs kernel support crc feature
#
_require_xfs_crc()
{
_scratch_mkfs_xfs -m crc=1 >/dev/null 2>&1
_scratch_mount >/dev/null 2>&1 \
|| _notrun "Kernel doesn't support crc feature"
umount $SCRATCH_MNT
}
# this test requires the finobt feature to be available in mkfs.xfs
#
_require_xfs_mkfs_finobt()
{
_scratch_mkfs_xfs_supported -m crc=1,finobt=1 >/dev/null 2>&1 \
|| _notrun "mkfs.xfs doesn't have finobt feature"
}
# this test requires the xfs kernel support finobt feature
#
_require_xfs_finobt()
{
_scratch_mkfs_xfs -m crc=1,finobt=1 >/dev/null 2>&1
_scratch_mount >/dev/null 2>&1 \
|| _notrun "Kernel doesn't support finobt feature"
umount $SCRATCH_MNT
}
# this test requires xfs sysfs attribute support
#
_require_xfs_sysfs()
{
attr=$1
sysfsdir=/sys/fs/xfs
testdev=`_short_dev $TEST_DEV`
if [ ! -e $sysfsdir ]; then
_notrun "no kernel support for XFS sysfs attributes"
fi
if [ ! -z $1 ] && [ ! -e $sysfsdir/$testdev/$attr ]; then
_notrun "sysfs attribute '$attr' is not supported"
fi
}
# this test requires that external log/realtime devices are not in use
#
_require_nonexternal()
{
[ "$USE_EXTERNAL" = yes ] && \
_notrun "External device testing in progress, skipped this test"
}
# this test requires that a (specified) aio-dio executable exists
# $1 - command (optional)
#
_require_aiodio()
{
if [ -z "$1" ]
then
AIO_TEST=src/aio-dio-regress/aiodio_sparse2
[ -x $AIO_TEST ] || _notrun "aio-dio utilities required"
else
AIO_TEST=src/aio-dio-regress/$1
[ -x $AIO_TEST ] || _notrun "$AIO_TEST not built"
fi
}
# run an aio-dio program
# $1 - command
_run_aiodio()
{
if [ -z "$1" ]
then
echo "usage: _run_aiodio command_name" 2>&1
status=1; exit 1
fi
_require_aiodio $1
local testtemp=$TEST_DIR/aio-testfile
rm -f $testtemp
$AIO_TEST $testtemp 2>&1
status=$?
rm -f $testtemp
return $status
}
# indicate whether YP/NIS is active or not
#
_yp_active()
{
local dn
dn=$(domainname 2>/dev/null)
test -n "${dn}" -a "${dn}" != "(none)"
echo $?
}
# cat the password file
#
_cat_passwd()
{
[ $(_yp_active) -eq 0 ] && ypcat passwd
cat /etc/passwd
}
# cat the group file
#
_cat_group()
{
[ $(_yp_active) -eq 0 ] && ypcat group
cat /etc/group
}
# check for the fsgqa user on the machine
#
_require_user()
{
qa_user=fsgqa
_cat_passwd | grep -q $qa_user
[ "$?" == "0" ] || _notrun "$qa_user user not defined."
echo /bin/true | su $qa_user
[ "$?" == "0" ] || _notrun "$qa_user cannot execute commands."
}
# check for the fsgqa group on the machine
#
_require_group()
{
qa_group=fsgqa
_cat_group | grep -q $qa_group
[ "$?" == "0" ] || _notrun "$qa_group user not defined."
}
_filter_user_do()
{
perl -ne "
s,.*Permission\sdenied.*,Permission denied,;
s,.*no\saccess\sto\stty.*,,;
s,.*no\sjob\scontrol\sin\sthis\sshell.*,,;
s,^\s*$,,;
print;"
}
_user_do()
{
if [ "$HOSTOS" == "IRIX" ]
then
echo $1 | /bin/bash "su $qa_user 2>&1" | _filter_user_do
else
echo $1 | su $qa_user 2>&1 | _filter_user_do
fi
}
_require_xfs_io_command()
{
if [ $# -ne 1 ]
then
echo "Usage: _require_xfs_io_command command" 1>&2
exit 1
fi
command=$1
testfile=$TEST_DIR/$$.xfs_io
case $command in
"falloc" )
testio=`$XFS_IO_PROG -F -f -c "falloc 0 1m" $testfile 2>&1`
;;
"fpunch" | "fcollapse" | "zero" | "fzero" )
testio=`$XFS_IO_PROG -F -f -c "pwrite 0 20k" -c "fsync" \
-c "$command 4k 8k" $testfile 2>&1`
;;
"fiemap")
testio=`$XFS_IO_PROG -F -f -c "pwrite 0 20k" -c "fsync" \
-c "fiemap -v" $testfile 2>&1`
;;
"flink" )
testio=`$XFS_IO_PROG -T -F -c "flink $testfile" \
$TEST_DIR 2>&1`
echo $testio | egrep -q "invalid option|Is a directory" && \
_notrun "xfs_io $command support is missing"
;;
*)
testio=`$XFS_IO_PROG -c "$command help" 2>&1`
esac
rm -f $testfile 2>&1 > /dev/null
echo $testio | grep -q "not found" && \
_notrun "xfs_io $command support is missing"
echo $testio | grep -q "Operation not supported" && \
_notrun "xfs_io $command failed (old kernel/wrong fs?)"
}
# Check that a fs has enough free space (in 1024b blocks)
#
_require_fs_space()
{
MNT=$1
BLOCKS=$2 # in units of 1024
let GB=$BLOCKS/1024/1024
FREE_BLOCKS=`df -klP $MNT | grep -v Filesystem | awk '{print $4}'`
[ $FREE_BLOCKS -lt $BLOCKS ] && \
_notrun "This test requires at least ${GB}GB free on $MNT to run"
}
#
# Check if the filesystem supports sparse files.
#
# Unfortunately there is no better way to do this than a manual black list.
#
_require_sparse_files()
{
case $FSTYP in
hfsplus)
_notrun "Sparse files not supported by this filesystem type: $FSTYP"
;;
*)
;;
esac
}
_require_debugfs()
{
#boot_params always present in debugfs
[ -d "$DEBUGFS_MNT/boot_params" ] || _notrun "Debugfs not mounted"
}
_require_fail_make_request()
{
[ -f "$DEBUGFS_MNT/fail_make_request/probability" ] \
|| _notrun "$DEBUGFS_MNT/fail_make_request \
not found. Seems that CONFIG_FAIL_MAKE_REQUEST kernel config option not enabled"
}
#
# Check if the file system supports seek_data/hole
#
_require_seek_data_hole()
{
testfile=$TEST_DIR/$$.seek
testseek=`$here/src/seek_sanity_test -t $testfile 2>&1`
rm -f $testfile &>/dev/null
echo $testseek | grep -q "Kernel does not support" && \
_notrun "File system does not support llseek(2) SEEK_DATA/HOLE"
}
# check that a FS on a device is mounted
# if so, return mount point
#
_is_mounted()
{
if [ $# -ne 1 ]
then
echo "Usage: _is_mounted device" 1>&2
exit 1
fi
device=$1
if _mount | grep "$device " | $AWK_PROG -v pattern="type $FSTYP" '
pattern { print $3 ; exit 0 }
END { exit 1 }
'
then
echo "_is_mounted: $device is not a mounted $FSTYP FS"
exit 1
fi
}
# remount a FS to a new mode (ro or rw)
#
_remount()
{
if [ $# -ne 2 ]
then
echo "Usage: _remount device ro/rw" 1>&2
exit 1
fi
device=$1
mode=$2
if ! mount -o remount,$mode $device
then
echo "_remount: failed to remount filesystem on $device as $mode"
exit 1
fi
}
# Run the appropriate repair/check on a filesystem
#
# if the filesystem is mounted, it's either remounted ro before being
# checked or it's unmounted and then remounted
#
# If set, we remount ro instead of unmounting for fsck
USE_REMOUNT=0
_umount_or_remount_ro()
{
if [ $# -ne 1 ]
then
echo "Usage: _umount_or_remount_ro <device>" 1>&2
exit 1
fi
device=$1
mountpoint=`_is_mounted $device`
if [ $USE_REMOUNT -eq 0 ]; then
$UMOUNT_PROG $device
else
_remount $device ro
fi
echo "$mountpoint"
}
_mount_or_remount_rw()
{
if [ $# -ne 3 ]
then
echo "Usage: _mount_or_remount_rw <opts> <device> <mountpoint>" 1>&2
exit 1
fi
mount_opts=$1
device=$2
mountpoint=$3
if [ $USE_REMOUNT -eq 0 ]
then
if ! _mount -t $FSTYP $mount_opts $device $mountpoint
then
echo "!!! failed to remount $device on $mountpoint"
return 0 # ok=0
fi
else
_remount $device rw
fi
return 1 # ok=1
}
# Check a generic filesystem in no-op mode; this assumes that the
# underlying fsck program accepts "-n" for a no-op (check-only) run,
# and that it will still return an errno for corruption in this mode.
#
# Filesystems which don't support this will need to define their
# own check routine.
#
_check_generic_filesystem()
{
device=$1
# If type is set, we're mounted
type=`_fs_type $device`
ok=1
if [ "$type" = "$FSTYP" ]
then
# mounted ...
mountpoint=`_umount_or_remount_ro $device`
fi
fsck -t $FSTYP $FSCK_OPTIONS $device >$tmp.fsck 2>&1
if [ $? -ne 0 ]
then
echo "_check_generic_filesystem: filesystem on $device is inconsistent (see $seqres.full)"
echo "_check_generic filesystem: filesystem on $device is inconsistent" >>$seqres.full
echo "*** fsck.$FSTYP output ***" >>$seqres.full
cat $tmp.fsck >>$seqres.full
echo "*** end fsck.$FSTYP output" >>$seqres.full
ok=0
fi
rm -f $tmp.fsck
if [ $ok -eq 0 ]
then
echo "*** mount output ***" >>$seqres.full
_mount >>$seqres.full
echo "*** end mount output" >>$seqres.full
elif [ "$type" = "$FSTYP" ]
then
# was mounted ...
_mount_or_remount_rw "$MOUNT_OPTIONS" $device $mountpoint
ok=$?
fi
if [ $ok -eq 0 ]; then
status=1
exit 1
fi
return 0
}
# run xfs_check and friends on a FS.
_check_xfs_filesystem()
{
if [ $# -ne 3 ]
then
echo "Usage: _check_xfs_filesystem device <logdev>|none <rtdev>|none" 1>&2
exit 1
fi
extra_mount_options=""
device=$1
if [ "$2" != "none" ]; then
extra_log_options="-l$2"
extra_mount_options="-ologdev=$2"
fi
if [ "$3" != "none" ]; then
extra_rt_options="-r$3"
extra_mount_options=$extra_mount_options" -ortdev=$3"
fi
extra_mount_options=$extra_mount_options" $MOUNT_OPTIONS"
[ "$FSTYP" != xfs ] && return 0
type=`_fs_type $device`
ok=1
if [ "$type" = "xfs" ]
then
# mounted ...
mountpoint=`_umount_or_remount_ro $device`
fi
$XFS_LOGPRINT_PROG -t $extra_log_options $device 2>&1 \
| tee $tmp.logprint | grep -q "<CLEAN>"
if [ $? -ne 0 -a "$HOSTOS" = "Linux" ]
then
echo "_check_xfs_filesystem: filesystem on $device has dirty log (see $seqres.full)"
echo "_check_xfs_filesystem: filesystem on $device has dirty log" >>$seqres.full
echo "*** xfs_logprint -t output ***" >>$seqres.full
cat $tmp.logprint >>$seqres.full
echo "*** end xfs_logprint output" >>$seqres.full
ok=0
fi
# xfs_check runs out of memory on large files, so even providing the test
# option (-t) to avoid indexing the free space trees doesn't make it pass on
# large filesystems. Avoid it.
if [ "$LARGE_SCRATCH_DEV" != yes ]; then
_xfs_check $extra_log_options $device 2>&1 |\
_fix_malloc >$tmp.fs_check
fi
if [ -s $tmp.fs_check ]
then
echo "_check_xfs_filesystem: filesystem on $device is inconsistent (c) (see $seqres.full)"
echo "_check_xfs_filesystem: filesystem on $device is inconsistent" >>$seqres.full
echo "*** xfs_check output ***" >>$seqres.full
cat $tmp.fs_check >>$seqres.full
echo "*** end xfs_check output" >>$seqres.full
ok=0
fi
$XFS_REPAIR_PROG -n $extra_log_options $extra_rt_options $device >$tmp.repair 2>&1
if [ $? -ne 0 ]
then
echo "_check_xfs_filesystem: filesystem on $device is inconsistent (r) (see $seqres.full)"
echo "_check_xfs_filesystem: filesystem on $device is inconsistent" >>$seqres.full
echo "*** xfs_repair -n output ***" >>$seqres.full
cat $tmp.repair | _fix_malloc >>$seqres.full
echo "*** end xfs_repair output" >>$seqres.full
ok=0
fi
rm -f $tmp.fs_check $tmp.logprint $tmp.repair
if [ $ok -eq 0 ]
then
echo "*** mount output ***" >>$seqres.full
_mount >>$seqres.full
echo "*** end mount output" >>$seqres.full
elif [ "$type" = "xfs" ]
then
_mount_or_remount_rw "$extra_mount_options" $device $mountpoint
fi
if [ $ok -eq 0 ]; then
status=1
exit 1
fi
return 0
}
# Filter the knowen errors the UDF Verifier reports.
_udf_test_known_error_filter()
{
egrep -v "PVD 60 Error: Interchange Level: 1, Maximum Interchange Level: 0|FSD 28 Error: Interchange Level: 1, Maximum Interchange Level: 1,|PVD 72 Warning: Volume Set Identifier: \"\*IRIX UDF\",|Warning: [0-9]+ unused blocks NOT marked as unallocated."
}
_check_udf_filesystem()
{
[ "$DISABLE_UDF_TEST" == "1" ] && return
if [ $# -ne 1 -a $# -ne 2 ]
then
echo "Usage: _check_udf_filesystem device [last_block]" 1>&2
exit 1
fi
if [ ! -x $here/src/udf_test ]
then
echo "udf_test not installed, please download and build the Philips"
echo "UDF Verification Software from http://www.extra.research.philips.com/udf/."
echo "Then copy the udf_test binary to $here/src/."
echo "If you do not wish to run udf_test then set environment variable DISABLE_UDF_TEST"
echo "to 1."
return
fi
device=$1
if [ $# -eq 2 ];
then
LAST_BLOCK=`expr \( $2 - 1 \)`
OPT_ARG="-lastvalidblock $LAST_BLOCK"
fi
rm -f $seqres.checkfs
sleep 1 # Due to a problem with time stamps in udf_test
$here/src/udf_test $OPT_ARG $device | tee $seqres.checkfs | egrep "Error|Warning" | \
_udf_test_known_error_filter | \
egrep -iv "Error count:.*[0-9]+.*total occurrences:.*[0-9]+|Warning count:.*[0-9]+.*total occurrences:.*[0-9]+" && \
echo "Warning UDF Verifier reported errors see $seqres.checkfs."
}
_check_xfs_test_fs()
{
TEST_LOG="none"
TEST_RT="none"
[ "$USE_EXTERNAL" = yes -a ! -z "$TEST_LOGDEV" ] && \
TEST_LOG="$TEST_LOGDEV"
[ "$USE_EXTERNAL" = yes -a ! -z "$TEST_RTDEV" ] && \
TEST_RT="$TEST_RTDEV"
_check_xfs_filesystem $TEST_DEV $TEST_LOG $TEST_RT
# check for ipath consistency
if $XFS_GROWFS_PROG -n $TEST_DIR | grep -q 'inode-paths=1'; then
# errors go to stderr
xfs_check_ipaths $TEST_DIR >/dev/null
xfs_repair_ipaths -n $TEST_DIR >/dev/null
fi
}
_check_btrfs_filesystem()
{
device=$1
# If type is set, we're mounted
type=`_fs_type $device`
ok=1
if [ "$type" = "$FSTYP" ]
then
# mounted ...
mountpoint=`_umount_or_remount_ro $device`
fi
btrfsck $device >$tmp.fsck 2>&1
if [ $? -ne 0 ]
then
echo "_check_btrfs_filesystem: filesystem on $device is inconsistent (see $seqres.full)"
echo "_check_btrfs_filesystem: filesystem on $device is inconsistent" >>$seqres.full
echo "*** fsck.$FSTYP output ***" >>$seqres.full
cat $tmp.fsck >>$seqres.full
echo "*** end fsck.$FSTYP output" >>$seqres.full
ok=0
fi
rm -f $tmp.fsck
if [ $ok -eq 0 ]
then
echo "*** mount output ***" >>$seqres.full
_mount >>$seqres.full
echo "*** end mount output" >>$seqres.full
elif [ "$type" = "$FSTYP" ]
then
# was mounted ...
_mount_or_remount_rw "$MOUNT_OPTIONS" $device $mountpoint
ok=$?
fi
if [ $ok -eq 0 ]; then
status=1
exit 1
fi
return 0
}
_check_test_fs()
{
case $FSTYP in
xfs)
_check_xfs_test_fs
;;
nfs)
# no way to check consistency for nfs
;;
udf)
# do nothing for now
;;
btrfs)
_check_btrfs_filesystem $TEST_DEV
;;
tmpfs)
# no way to check consistency for tmpfs
;;
*)
_check_generic_filesystem $TEST_DEV
;;
esac
}
_check_scratch_fs()
{
device=$SCRATCH_DEV
[ $# -eq 1 ] && device=$1
case $FSTYP in
xfs)
SCRATCH_LOG="none"
SCRATCH_RT="none"
[ "$USE_EXTERNAL" = yes -a ! -z "$SCRATCH_LOGDEV" ] && \
SCRATCH_LOG="$SCRATCH_LOGDEV"
[ "$USE_EXTERNAL" = yes -a ! -z "$SCRATCH_RTDEV" ] && \
SCRATCH_RT="$SCRATCH_RTDEV"
_check_xfs_filesystem $device $SCRATCH_LOG $SCRATCH_RT
;;
udf)
_check_udf_filesystem $device $udf_fsize
;;
nfs*)
# Don't know how to check an NFS filesystem, yet.
;;
btrfs)
_check_btrfs_filesystem $device
;;
tmpfs)
# no way to check consistency for tmpfs
;;
*)
_check_generic_filesystem $device
;;
esac
}
_full_fstyp_details()
{
[ -z "$FSTYP" ] && FSTYP=xfs
if [ $FSTYP = xfs ]; then
if [ -d /proc/fs/xfs ]; then
if grep -q 'debug 0' /proc/fs/xfs/stat; then
FSTYP="$FSTYP (non-debug)"
elif grep -q 'debug 1' /proc/fs/xfs/stat; then
FSTYP="$FSTYP (debug)"
fi
else
if uname -a | grep -qi 'debug'; then
FSTYP="$FSTYP (debug)"
else
FSTYP="$FSTYP (non-debug)"
fi
fi
fi
echo $FSTYP
}
_full_platform_details()
{
os=`uname -s`
host=`hostname -s`
kernel=`uname -r`
platform=`uname -m`
echo "$os/$platform $host $kernel"
}
_link_out_file()
{
if [ -z "$1" -o -z "$2" ]; then
echo Error must pass src and dst.
exit
fi
rm -f $2
if [ "`uname`" == "IRIX64" ] || [ "`uname`" == "IRIX" ]; then
ln -s $1.irix $2
elif [ "`uname`" == "Linux" ]; then
ln -s $1.linux $2
else
echo Error test $seq does not run on the operating system: `uname`
exit
fi
}
_die()
{
echo $@
exit 1
}
#takes files, randomdata
_nfiles()
{
f=0
while [ $f -lt $1 ]
do
file=f$f
echo > $file
if [ $size -gt 0 ]; then
if [ "$2" == "false" ]; then
dd if=/dev/zero of=$file bs=1024 count=$size 2>&1 | _filter_dd
else
dd if=/dev/urandom of=$file bs=1024 count=$size 2>&1 | _filter_dd
fi
fi
let f=$f+1
done
}
# takes dirname, depth, randomdata
_descend()
{
dirname=$1; depth=$2; randomdata=$3
mkdir $dirname || die "mkdir $dirname failed"
cd $dirname
_nfiles $files $randomdata # files for this dir and data type
[ $depth -eq 0 ] && return
let deep=$depth-1 # go 1 down
[ $verbose = true ] && echo "descending, depth from leaves = $deep"
d=0
while [ $d -lt $dirs ]
do
_descend d$d $deep &
let d=$d+1
wait
done
}
# Populate a filesystem with inodes for performance experiments
#
# usage: populate [-v] [-n ndirs] [-f nfiles] [-d depth] [-r root] [-s size] [-x]
#
_populate_fs()
{
here=`pwd`
dirs=5 # ndirs in each subdir till leaves
size=0 # sizeof files in K
files=100 # num files in _each_ subdir
depth=2 # depth of tree from root to leaves
verbose=false
root=root # path of initial root of directory tree
randomdata=false # -x data type urandom or zero
OPTIND=1
while getopts "d:f:n:r:s:v:x" c
do
case $c in
d) depth=$OPTARG;;
n) dirs=$OPTARG;;
f) files=$OPTARG;;
s) size=$OPTARG;;
v) verbose=true;;
r) root=$OPTARG;;
x) randomdata=true;;
esac
done
_descend $root $depth $randomdata
wait
cd $here
[ $verbose = true ] && echo done
}
# query whether the given file has the given inode flag set
#
_test_inode_flag()
{
flag=$1
file=$2
if which $XFS_IO_PROG >/dev/null; then
if $XFS_IO_PROG -r -c 'lsattr -v' "$file" | grep -q "$flag" ; then
return 0
fi
fi
return 1
}
# query the given files extsize allocator hint in bytes (if any)
#
_test_inode_extsz()
{
file=$1
blocks=""
if which $XFS_IO_PROG >/dev/null; then
blocks=`$XFS_IO_PROG -r -c 'stat' "$file" | \
awk '/^xattr.extsize =/ { print $3 }'`
fi
[ -z "$blocks" ] && blocks="0"
echo $blocks
}
# scratch_dev_pool should contain the disks pool for the btrfs raid
_require_scratch_dev_pool()
{
local i
local ndevs
if [ -z "$SCRATCH_DEV_POOL" ]; then
_notrun "this test requires a valid \$SCRATCH_DEV_POOL"
fi
if [ -z "$1" ]; then
ndevs=2
else
ndevs=$1
fi
# btrfs test case needs ndevs or more scratch_dev_pool; other FS not sure
# so fail it
case $FSTYP in
btrfs)
if [ "`echo $SCRATCH_DEV_POOL|wc -w`" -lt $ndevs ]; then
_notrun "btrfs and this test needs $ndevs or more disks in SCRATCH_DEV_POOL"
fi
;;
*)
_notrun "dev_pool is not supported by fstype \"$FSTYP\""
;;
esac
for i in $SCRATCH_DEV_POOL; do
if [ "`_is_block_dev $i`" = "" ]; then
_notrun "this test requires valid block disk $i"
fi
if [ "`_is_block_dev $i`" = "`_is_block_dev $TEST_DEV`" ]; then
_notrun "$i is part of TEST_DEV, this test requires unique disks"
fi
if _mount | grep -q $i; then
if ! $UMOUNT_PROG $i; then
echo "failed to unmount $i - aborting"
exit 1
fi
fi
# to help better debug when something fails, we remove
# traces of previous btrfs FS on the dev.
dd if=/dev/zero of=$i bs=4096 count=100 > /dev/null 2>&1
done
}
# We will check if the device is deletable
_require_deletable_scratch_dev_pool()
{
local i
local x
for i in $SCRATCH_DEV_POOL; do
x=`echo $i | cut -d"/" -f 3`
if [ ! -f /sys/class/block/${x}/device/delete ]; then
_notrun "$i is a device which is not deletable"
fi
done
}
# We check for btrfs and (optionally) features of the btrfs command
_require_btrfs()
{
cmd=$1
_require_command $BTRFS_UTIL_PROG btrfs
if [ -z "$1" ]; then
return 1;
fi
$BTRFS_UTIL_PROG $cmd --help >/dev/null 2>&1
[ $? -eq 0 ] || _notrun "$BTRFS_UTIL_PROG too old (must support $cmd)"
}
# Check that fio is present, and it is able to execute given jobfile
_require_fio()
{
job=$1
_require_command $FIO_PROG
if [ -z "$1" ]; then
return 1;
fi
$FIO_PROG --warnings-fatal --showcmd $job >> $seqres.full 2>&1
[ $? -eq 0 ] || _notrun "$FIO_PROG too old, see $seqres.full"
}
# Does freeze work on this fs?
_require_freeze()
{
xfs_freeze -f "$TEST_DIR" >/dev/null 2>&1
result=$?
xfs_freeze -u "$TEST_DIR" >/dev/null 2>&1
[ $result -eq 0 ] || _notrun "$FSTYP does not support freezing"
}
# arg 1 is dev to remove and is output of the below eg.
# ls -l /sys/class/block/sdd | rev | cut -d "/" -f 3 | rev
_devmgt_remove()
{
local lun=$1
local disk=$2
echo 1 > /sys/class/scsi_device/${lun}/device/delete || _fail "Remove disk failed"
stat $disk > /dev/null 2>&1
while [ $? -eq 0 ]; do
sleep 1
stat $disk > /dev/null 2>&1
done
}
# arg 1 is dev to add and is output of the below eg.
# ls -l /sys/class/block/sdd | rev | cut -d "/" -f 3 | rev
_devmgt_add()
{
local h
local tdl
# arg 1 will be in h:t:d:l format now in the h and "t d l" format
h=`echo ${1} | cut -d":" -f 1`
tdl=`echo ${1} | cut -d":" -f 2-|sed 's/:/ /g'`
echo ${tdl} > /sys/class/scsi_host/host${h}/scan || _fail "Add disk failed"
}
_require_fstrim()
{
if [ -z "$FSTRIM_PROG" ]; then
_notrun "This test requires fstrim utility."
fi
}
_test_batched_discard()
{
if [ $# -ne 1 ]; then
echo "Usage: _test_batched_discard mnt_point" 1>&2
exit 1
fi
_require_fstrim
$FSTRIM_PROG ${1} &>/dev/null
}
_require_dumpe2fs()
{
if [ -z "$DUMPE2FS_PROG" ]; then
_notrun "This test requires dumpe2fs utility."
fi
}
_require_ugid_map()
{
if [ ! -e /proc/self/uid_map ]; then
_notrun "This test requires procfs uid_map support."
fi
if [ ! -e /proc/self/gid_map ]; then
_notrun "This test requires procfs gid_map support."
fi
}
_require_cp_reflink()
{
cp --help | grep -q reflink || \
_notrun "This test requires a cp with --reflink support."
}
_require_fssum()
{
FSSUM_PROG=$here/src/fssum
[ -x $FSSUM_PROG ] || _notrun "fssum not built"
}
_require_btrfs_cloner()
{
CLONER_PROG=$here/src/cloner
[ -x $CLONER_PROG ] || \
_notrun "cloner binary not present at $CLONER_PROG"
}
# Given 2 files, verify that they have the same mapping but different
# inodes - i.e. an undisturbed reflink
# Silent if so, make noise if not
_verify_reflink()
{
# not a hard link or symlink?
cmp -s <(stat -c '%i' $1) <(stat -c '%i' $2) \
&& echo "$1 and $2 are not reflinks: same inode number"
# same mapping?
diff -u <($XFS_IO_PROG -c "fiemap" $1 | grep -v $1) \
<($XFS_IO_PROG -c "fiemap" $2 | grep -v $2) \
|| echo "$1 and $2 are not reflinks: different extents"
}
_require_relatime()
{
_scratch_mkfs > /dev/null 2>&1
_mount -t $FSTYP -o relatime $SCRATCH_DEV $SCRATCH_MNT || \
_notrun "relatime not supported by the current kernel"
_scratch_unmount
}
_create_loop_device()
{
file=$1
dev=`losetup -f --show $file` || _fail "Cannot assign $file to a loop device"
echo $dev
}
_destroy_loop_device()
{
dev=$1
losetup -d $dev || _fail "Cannot destroy loop device $dev"
}
_scale_fsstress_args()
{
args=""
while [ $# -gt 0 ]; do
case "$1" in
-n) args="$args $1 $(($2 * $TIME_FACTOR))"; shift ;;
-p) args="$args $1 $(($2 * $LOAD_FACTOR))"; shift ;;
*) args="$args $1" ;;
esac
shift
done
echo $args
}
#
# Return the logical block size if running on a block device,
# else substitute the page size.
#
_min_dio_alignment()
{
dev=$1
if [ -b "$dev" ]; then
blockdev --getss $dev
else
$here/src/feature -s
fi
}
run_check()
{
echo "# $@" >> $seqres.full 2>&1
"$@" >> $seqres.full 2>&1 || _fail "failed: '$@'"
}
_run_btrfs_util_prog()
{
run_check $BTRFS_UTIL_PROG $*
}
_require_btrfs_send_stream_version()
{
$BTRFS_UTIL_PROG send 2>&1 | \
grep '^[ \t]*\-\-stream\-version <version>' > /dev/null 2>&1
if [ $? -ne 0 ]; then
_notrun "Missing btrfs-progs send --stream-version command line option, skipped this test"
fi
}
_require_btrfs_mkfs_feature()
{
if [ -z $1 ]; then
echo "Missing feature name argument for _require_btrfs_mkfs_feature"
exit 1
fi
feat=$1
$MKFS_BTRFS_PROG -O list-all 2>&1 | \
grep '^[ \t]*'"$feat"'\b' > /dev/null 2>&1
[ $? -eq 0 ] || \
_notrun "Feature $feat not supported in the available version of mkfs.btrfs"
}
_require_btrfs_fs_feature()
{
if [ -z $1 ]; then
echo "Missing feature name argument for _require_btrfs_fs_feature"
exit 1
fi
feat=$1
modprobe btrfs > /dev/null 2>&1
[ -e /sys/fs/btrfs/features/$feat ] || \
_notrun "Feature $feat not supported by the available btrfs version"
}
init_rc()
{
if [ "$iam" == new ]
then
return
fi
# make some further configuration checks here
if [ "$TEST_DEV" = "" ]
then
echo "common/rc: Error: \$TEST_DEV is not set"
exit 1
fi
# if $TEST_DEV is not mounted, mount it now as XFS
if [ -z "`_fs_type $TEST_DEV`" ]
then
# $TEST_DEV is not mounted
if ! _test_mount
then
echo "common/rc: retrying test device mount with external set"
[ "$USE_EXTERNAL" != "yes" ] && export USE_EXTERNAL=yes
if ! _test_mount
then
echo "common/rc: could not mount $TEST_DEV on $TEST_DIR"
exit 1
fi
fi
fi
if [ "`_fs_type $TEST_DEV`" != "$FSTYP" ]
then
echo "common/rc: Error: \$TEST_DEV ($TEST_DEV) is not a MOUNTED $FSTYP filesystem"
$DF_PROG $TEST_DEV
exit 1
fi
# Figure out if we need to add -F ("foreign", deprecated) option to xfs_io
xfs_io -c stat $TEST_DIR 2>&1 | grep -q "is not on an XFS filesystem" && \
export XFS_IO_PROG="$XFS_IO_PROG -F"
}
# get real device path name by following link
_real_dev()
{
local _dev=$1
if [ -b "$_dev" ] && [ -L "$_dev" ]; then
_dev=`readlink -f "$_dev"`
fi
echo $_dev
}
# basename of a device
_short_dev()
{
echo `basename $(_real_dev $1)`
}
init_rc
################################################################################
# make sure this script returns success
/bin/true