blob: fc63d3a32c8abbae15099e3bf6793606a4c82411 [file]
##/bin/bash
# SPDX-License-Identifier: GPL-2.0+
# Copyright (c) 2016 Oracle. All Rights Reserved.
#
# Routines for injecting errors into filesystems
. ./common/log
# Tests whether $FSTYP is one of the filesystems that supports error injection
_require_error_injection()
{
case "$FSTYP" in
"xfs")
grep -q 'debug 1' /proc/fs/xfs/stat || \
_notrun "XFS error injection requires CONFIG_XFS_DEBUG"
;;
*)
_notrun "Error injection not supported on filesystem type: $FSTYP"
esac
}
# Find the errortag injection knob in sysfs for a given xfs mount's
# block device.
_find_xfs_mountdev_errortag_knob()
{
dev="$1"
knob="$2"
shortdev="$(_short_dev "${dev}")"
tagfile="/sys/fs/xfs/${shortdev}/errortag/${knob}"
# Some of the new sysfs errortag knobs were previously available via
# another sysfs path.
case "${knob}" in
"log_bad_crc")
if [ ! -w "${tagfile}" ]; then
tagfile="/sys/fs/xfs/${shortdev}/log/log_badcrc_factor"
fi
;;
"drop_writes")
if [ ! -w "${tagfile}" ]; then
tagfile="/sys/fs/xfs/${shortdev}/drop_writes"
fi
if [ ! -w "${tagfile}" ]; then
tagfile="/sys/fs/xfs/${shortdev}/fail_writes"
fi
;;
*)
;;
esac
echo "${tagfile}"
}
# Requires that xfs_io inject command knows about this error type
_require_xfs_io_error_injection()
{
type="$1"
_require_error_injection
# Can we find the error injection knobs via the new errortag
# configuration mechanism?
knob="$(_find_xfs_mountdev_errortag_knob "${TEST_DEV}" "${type}")"
test -w "${knob}" && return
# If the directory containing the sysfs error injection knob exists
# but the knob itself isn't usable, this kernel doesn't know about
# the knob. Skip the test.
if [ -d "$(dirname "${knob}")" ]; then
_notrun "XFS error injection $type unknown on this kernel."
fi
# NOTE: We can't actually test error injection here because xfs
# hasn't always range checked the argument to xfs_errortag_add.
# We also don't want to trip an error before we're ready to deal
# with it.
$XFS_IO_PROG -x -c 'inject' $TEST_DIR | grep -q "$type" || \
_notrun "XFS error injection $type unknown."
}
# Inject an error into the test fs
_test_inject_error()
{
type="$1"
value="$2"
knob="$(_find_xfs_mountdev_errortag_knob "${TEST_DEV}" "${type}")"
if [ -w "${knob}" ]; then
test -z "${value}" && value="default"
echo -n "${value}" > "${knob}"
elif [ -z "${value}" ] || [ "${value}" = "default" ]; then
$XFS_IO_PROG -x -c "inject $type" $TEST_DIR
else
_fail "Cannot inject error ${type} value ${value}."
fi
}
# Inject an error into the scratch fs
_scratch_inject_error()
{
type="$1"
value="$2"
knob="$(_find_xfs_mountdev_errortag_knob "${SCRATCH_DEV}" "${type}")"
if [ -w "${knob}" ]; then
test -z "${value}" && value="default"
echo -n "${value}" > "${knob}"
elif [ -z "${value}" ] || [ "${value}" = "default" ]; then
$XFS_IO_PROG -x -c "inject $type" $SCRATCH_MNT
else
_fail "Cannot inject error ${type} value ${value}."
fi
}
# Requires the kernel fail_function facility (CONFIG_FUNCTION_ERROR_INJECTION
# and CONFIG_FAIL_FUNCTION) and, if a function name is given, that the function
# is annotated with ALLOW_ERROR_INJECTION().
_require_function_error_injection()
{
local func="$1"
_require_debugfs
test -d "$DEBUGFS_MNT/fail_function" || \
_notrun "$DEBUGFS_MNT/fail_function not found; CONFIG_FAIL_FUNCTION not enabled"
if [ -n "$func" ]; then
grep -qw "$func" "$DEBUGFS_MNT/error_injection/list" 2>/dev/null || \
_notrun "$func is not registered for error injection"
fi
}
# Inject a fixed return value into a kernel function via fail_function.
# $1 - function name (must be ALLOW_ERROR_INJECTION annotated)
# $2 - return value to inject (e.g. -12 for -ENOMEM)
# $3 - number of times to fail (default 1)
_inject_function_error()
{
local func="$1"
local retval="$2"
local times="${3:-1}"
# Writing the function name to "inject" creates its per-function dir.
echo "$func" > "$DEBUGFS_MNT/fail_function/inject"
# The retval file is an unsigned hex attribute (DEFINE_DEBUGFS_ATTRIBUTE
# "%llx"), so a negative errno like -12 must be written as its unsigned
# 64-bit (two's-complement) hex form; a bare "-12" is rejected with EINVAL.
printf '%#x\n' "$retval" > "$DEBUGFS_MNT/fail_function/$func/retval"
echo 100 > "$DEBUGFS_MNT/fail_function/probability"
echo 0 > "$DEBUGFS_MNT/fail_function/interval"
echo 0 > "$DEBUGFS_MNT/fail_function/space"
echo 0 > "$DEBUGFS_MNT/fail_function/verbose"
echo "$times" > "$DEBUGFS_MNT/fail_function/times"
}
# Stop injecting errors into a kernel function set up by
# _inject_function_error().
_uninject_function_error()
{
local func="$1"
echo 0 > "$DEBUGFS_MNT/fail_function/times" 2>/dev/null
echo 0 > "$DEBUGFS_MNT/fail_function/probability" 2>/dev/null
echo "!$func" > "$DEBUGFS_MNT/fail_function/inject" 2>/dev/null
}