You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
1000 lines
23 KiB
1000 lines
23 KiB
#!/bin/bash
|
|
# Copyright (c) International Business Machines Corp., 2008
|
|
# Author: Matt Helsley <matthltc@us.ibm.com>
|
|
#
|
|
# This library is free software; you can redistribute it and/or
|
|
# modify it under the terms of the GNU Lesser General Public
|
|
# License as published by the Free Software Foundation; either
|
|
# version 2.1 of the License, or (at your option) any later version.
|
|
#
|
|
# This library 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
|
|
# Lesser General Public License for more details.
|
|
#
|
|
# You should have received a copy of the GNU Lesser General Public
|
|
# License along with this library; if not, write to the Free Software
|
|
# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
|
#
|
|
#
|
|
|
|
# A library of cgroup test functions for testing the cgroup freezer and,
|
|
# if present, a cgroup signal subsystem.
|
|
#
|
|
# Most of these assume the current directory is the cgroup of interest.
|
|
# mount_{freezer|signal} and make_sample_cgroup are the exceptions to this rule.
|
|
#
|
|
# On failure, a message indicating what failed is printed and the
|
|
# exit status of the failing command is returned. If "result" is unset
|
|
# then we assign the exit status of the failed command to it.
|
|
#
|
|
# The variable "result" holds the exit status of the first command that failed,
|
|
# $UNFINISHED if no command has failed yet, or $FINISHED if no command
|
|
# has failed and we've finished all significant parts of the test. Note:
|
|
# significant parts usually do not include the cleanup of the test.
|
|
#
|
|
|
|
|
|
# xargs 4.1.20 only accepts -i instead of -I
|
|
# However -I is added and -i deprecated somewhere between (4.1.20, 4.2.32]
|
|
XRGSV=$(xargs --version | sed -e 's/^[^[:digit:]]*//')
|
|
export XARGS_REPL_STR="%"
|
|
case ${XRGSV} in
|
|
[456789].[23456789][0-9]*.*|[1-9][0-9][0-9]*.*.*) # version > 4.1.*
|
|
export XARGS_REPL_OPT="-I${XARGS_REPL_STR}"
|
|
;;
|
|
4.1.*|*)
|
|
export XARGS_REPL_OPT="-i${XARGS_REPL_STR}"
|
|
;;
|
|
esac
|
|
unset XRGSV
|
|
|
|
export UNFINISHED=""
|
|
export FINISHED=0
|
|
export TMP=${TMP:-/tmp}
|
|
|
|
#
|
|
# Tests are either running or cleaning up. Cleanup phases do not emit TFAIL.
|
|
#
|
|
export LIB_TEST_STATE="running"
|
|
|
|
export max_state_samples=5 # number of times to sample
|
|
export sample_state_period=1 # number of seconds between samplings
|
|
export sample_sleep=5 # how long the sample program should sleep
|
|
export result="$UNFINISHED"
|
|
|
|
# These are echo'd to freezer.state.
|
|
export FREEZE='FROZEN'
|
|
export THAW='THAWED'
|
|
|
|
# We use /bin/echo to write to cgroup files because it's exit status will not
|
|
# hide write errors (bash's echo does not indicate if the write succeeded).
|
|
export CG_FILE_WRITE=/bin/echo
|
|
|
|
declare -r UNFINISHED FINISHED FREEZE THAW max_state_samples sample_state_period
|
|
|
|
# When we're running we want to issue failure results when things go wrong.
|
|
function running_cgroup_test()
|
|
{
|
|
export LIB_TEST_STATE="TFAIL"
|
|
}
|
|
|
|
# But when we're cleaning up we want to issue warnings (if not TINFO).
|
|
function cleanup_cgroup_test()
|
|
{
|
|
export LIB_TEST_STATE="TWARN"
|
|
}
|
|
|
|
# Mounts the cgroup filesystem somewhere and pushes pwd onto the dir stack
|
|
function mount_cgroup_subsys()
|
|
{
|
|
local rc=0
|
|
|
|
mkdir -p $TMP/${cgroup_subsys}_test > /dev/null 2>&1
|
|
rc=$?
|
|
|
|
# Don't test status because we don't care if the directory already
|
|
# exists.
|
|
|
|
if [ ! -d $TMP/${cgroup_subsys}_test ]; then
|
|
result=${result:-$rc}
|
|
tst_brkm TBROK "Failed to make mount point for cgroup filesystem"
|
|
return $result
|
|
fi
|
|
|
|
mount -t cgroup -o${cgroup_subsys} test_cgroup_${cgroup_subsys} $TMP/${cgroup_subsys}_test
|
|
rc=$?
|
|
if [ $rc -ne 0 ]; then
|
|
result=${result:-$rc}
|
|
tst_resm TINFO "Failed to mount cgroup filesystem with ${cgroup_subsys} subsystem."
|
|
rmdir $TMP/${cgroup_subsys}_test 2> /dev/null
|
|
return $rc
|
|
fi
|
|
|
|
export mount_cgroup_freezer_saved_dir="$(pwd)"
|
|
cd $TMP/${cgroup_subsys}_test > /dev/null 2>&1
|
|
rc=$?
|
|
if [ $rc -ne 0 ]; then
|
|
result=${result:-$rc}
|
|
tst_brkm TBROK "Failed to change working directory into cgroup filesystem (pwd: \"$(pwd)\")"
|
|
umount $TMP/${cgroup_subsys}_test || umount -l $TMP/${cgroup_subsys}_test || tst_brkm TBROK "Failed to unmount cgroup filesystem with ${cgroup_subsys} subsystem"
|
|
rmdir $TMP/${cgroup_subsys}_test 2> /dev/null
|
|
return $rc
|
|
fi
|
|
|
|
return 0
|
|
}
|
|
|
|
function mount_freezer()
|
|
{
|
|
export cgroup_subsys=freezer
|
|
mount_cgroup_subsys
|
|
}
|
|
|
|
function mount_signal()
|
|
{
|
|
export cgroup_subsys=signal
|
|
mount_cgroup_subsys
|
|
}
|
|
|
|
# umounts the cgroup filesystem and pops the dir stack
|
|
function umount_cgroup_subsys()
|
|
{
|
|
cd "${mount_cgroup_freezer_saved_dir}"
|
|
local cwd_result=$?
|
|
umount $TMP/${cgroup_subsys}_test > /dev/null 2>&1 \
|
|
|| umount -l $TMP/${cgroup_subsys}_test || \
|
|
tst_brkm TBROK "Failed to unmount cgroup filesystem (umount exit status: $?)"
|
|
local umnt_result=$?
|
|
local rc=0
|
|
|
|
if [ $cwd_result -ne 0 ]; then
|
|
result=${result:-$cwd_result}
|
|
rc=$cwd_result
|
|
elif [ $umnt_result -ne 0 ]; then
|
|
result=${result:-$umnt_result}
|
|
rc=$umnt_result
|
|
elif [ $umnt_result -eq 0 -a $cwd_result -eq 0 ]; then
|
|
rmdir $TMP/${cgroup_subsys}_test
|
|
return 0
|
|
fi
|
|
|
|
return $rc
|
|
}
|
|
|
|
function umount_freezer()
|
|
{
|
|
[ "${cgroup_subsys}" != "freezer" ] && {
|
|
result=${result:-5}
|
|
exit -1
|
|
}
|
|
umount_cgroup_subsys
|
|
unset cgroup_subsys
|
|
}
|
|
|
|
function cleanup_freezer()
|
|
{
|
|
local save_result="${result}"
|
|
local save_pwd="$(pwd)"
|
|
|
|
mount_freezer && {
|
|
# Run these commands in bash because we need $(cmd subst) and
|
|
# we need to redirect to different freezer.state files for each
|
|
# group
|
|
# Kill any leftover tasks
|
|
disown -a
|
|
find $TMP/${cgroup_subsys}_test -mindepth 1 -depth -type d -print0 | \
|
|
xargs -0r -n 1 ${XARGS_REPL_OPT} /bin/bash -c 'kill $(cat "'"${XARGS_REPL_STR}"'/tasks") 2> /dev/null'
|
|
|
|
# For each group in the freezer hierarch, that its tasks
|
|
find $TMP/${cgroup_subsys}_test -mindepth 1 -depth -type d -print0 | \
|
|
xargs -0r -n 1 ${XARGS_REPL_OPT} /bin/bash -c "\"${CG_FILE_WRITE}\" \"${THAW}\" > '${XARGS_REPL_STR}/freezer.state'"
|
|
|
|
# Kill any leftover tasks
|
|
find $TMP/${cgroup_subsys}_test -mindepth 1 -depth -type d -print0 | \
|
|
xargs -0r -n 1 ${XARGS_REPL_OPT} /bin/bash -c 'kill $(cat "'"${XARGS_REPL_STR}"'/tasks") 2> /dev/null'
|
|
|
|
sleep 2
|
|
|
|
# Really kill any leftover tasks
|
|
find $TMP/${cgroup_subsys}_test -mindepth 1 -depth -type d -print0 | \
|
|
xargs -0r -n 1 ${XARGS_REPL_OPT} /bin/bash -c 'kill -s SIGKILL $(cat "'"${XARGS_REPL_STR}"'/tasks") 2> /dev/null'
|
|
|
|
# Don't need to run these xargs commands in bash since we want
|
|
# to see what's left on stdout
|
|
LINES=$(find $TMP/${cgroup_subsys}_test -mindepth 1 -depth -type d -print0 | \
|
|
xargs -0r -n 1 ${XARGS_REPL_OPT} cat "${XARGS_REPL_STR}/tasks" | wc -l)
|
|
if (( LINES == 0 )); then
|
|
# Remove the empty groups
|
|
find $TMP/${cgroup_subsys}_test -mindepth 1 -depth -type d -print0 | xargs -r0 rmdir
|
|
else
|
|
tst_resm TWARN "Could not cleanup:"
|
|
find $TMP/${cgroup_subsys}_test -mindepth 1 -depth -type d -print0 | xargs -0r -n 1 ${XARGS_REPL_OPT} ls -ld "${XARGS_REPL_STR}/tasks"
|
|
fi
|
|
|
|
umount_freezer
|
|
}
|
|
|
|
if [ "$save_pwd" != `pwd` ]; then
|
|
tst_resm TWARN "libcgroup_subsys: cleanup_freezer() is broken"
|
|
cd "$save_pwd"
|
|
fi
|
|
|
|
result="${save_result}"
|
|
}
|
|
|
|
function umount_signal()
|
|
{
|
|
[ "${cgroup_subsys}" != "signal" ] && {
|
|
result=${result:-6}
|
|
exit -1
|
|
}
|
|
umount_cgroup_subsys
|
|
unset cgroup_subsys
|
|
}
|
|
|
|
function cleanup_signal()
|
|
{
|
|
local save_result="${result}"
|
|
local save_pwd="$(pwd)"
|
|
|
|
mount_signal && {
|
|
find $TMP/${cgroup_subsys}_test -mindepth 1 -depth -type d -print0 | xargs -r0 rmdir
|
|
umount_signal
|
|
}
|
|
|
|
if [ "$save_pwd" != `pwd` ]; then
|
|
tst_resm TWARN "libcgroup_subsys: cleanup_signal() is broken"
|
|
cd "$save_pwd"
|
|
fi
|
|
result="${save_result}"
|
|
}
|
|
|
|
function assert_cgroup_rwfile()
|
|
{
|
|
local file="$1"
|
|
local descr="$2"
|
|
local rc=0
|
|
|
|
if [ ! -e "${file}" ]; then
|
|
tst_resm ${LIB_TEST_STATE} "$descr missing"
|
|
rc=1
|
|
fi
|
|
|
|
if [ ! -f "${file}" ]; then
|
|
tst_resm ${LIB_TEST_STATE} "$descr is not a regular file"
|
|
rc=2
|
|
fi
|
|
|
|
if [ ! -r "${file}" ]; then
|
|
tst_resm ${LIB_TEST_STATE} "$descr is not readable"
|
|
rc=3
|
|
fi
|
|
|
|
if [ ! -w "${file}" ]; then
|
|
tst_resm ${LIB_TEST_STATE} "$descr is not writeable"
|
|
rc=4
|
|
fi
|
|
|
|
[ $rc -ne 0 ] && {
|
|
result=${result:-$rc}
|
|
local s="$(stat "${file}")"
|
|
tst_resm ${LIB_TEST_STATE} "${s}"
|
|
}
|
|
|
|
return $rc
|
|
}
|
|
|
|
function assert_cgroup_tasks_rwfile()
|
|
{
|
|
assert_cgroup_rwfile "tasks" "task list"
|
|
return $?
|
|
}
|
|
|
|
function dump_named_cgroup_tasks()
|
|
{
|
|
local cgroup_name="$1"
|
|
local tasks
|
|
|
|
tasks=( $(cat "${cgroup_name}/tasks") ) # don't assign directly (bash bug)
|
|
if [ -z "${tasks[*]}" ]; then
|
|
return 0
|
|
fi
|
|
ps -p "${tasks[*]}" -o 'pid,ppid,pgid,tid,tpgid,blocked,ignored,pending,stat,tty,args'
|
|
}
|
|
|
|
function dump_cgroup_tasks()
|
|
{
|
|
dump_named_cgroup_tasks "$(pwd)"
|
|
}
|
|
|
|
function assert_cgroup_tasks_empty()
|
|
{
|
|
local nlines=$(( `cat tasks | wc -l` + 0))
|
|
local rc=$?
|
|
|
|
[ $rc -eq 0 -a $nlines -eq 0 ] && return 0
|
|
rc=$?
|
|
result=${result:-$rc}
|
|
tst_resm ${LIB_TEST_STATE} "cgroup task list is not empty: "
|
|
dump_cgroup_tasks 1>&2
|
|
return $rc
|
|
}
|
|
|
|
function assert_task_in_named_cgroup()
|
|
{
|
|
local task_pid=$1
|
|
local cgroup_name="$2"
|
|
|
|
cat "${cgroup_name}/tasks" | grep -E "^${task_pid}\$" > /dev/null 2>&1 && return 0
|
|
local rc=$?
|
|
result=${result:-$rc}
|
|
tst_resm ${LIB_TEST_STATE} "Expected pid ${task_pid} is not in \"${cgroup_name}\" task list"
|
|
dump_named_cgroup_tasks "${cgroup_name}" 1>&2
|
|
return $rc
|
|
}
|
|
|
|
function assert_task_not_in_named_cgroup()
|
|
{
|
|
local task_pid=$1
|
|
local cgroup_name="$2"
|
|
|
|
cat "${cgroup_name}/tasks" | grep -E "^${task_pid}\$" > /dev/null 2>&1 || return 0
|
|
local rc=1 # $? == 0 is an error in this case
|
|
result=${result:-$rc}
|
|
tst_resm ${LIB_TEST_STATE} "Expected pid ${task_pid} is in \"${cgroup_name}\" task list"
|
|
dump_named_cgroup_tasks "${cgroup_name}" 1>&2
|
|
return $rc
|
|
}
|
|
|
|
function assert_task_in_cgroup()
|
|
{
|
|
assert_task_in_named_cgroup $1 "$(pwd)"
|
|
return $?
|
|
}
|
|
|
|
function assert_task_not_in_cgroup()
|
|
{
|
|
assert_task_not_in_named_cgroup $1 "$(pwd)"
|
|
return $?
|
|
}
|
|
|
|
function assert_sample_proc_in_cgroup()
|
|
{
|
|
assert_task_in_cgroup $sample_proc
|
|
return $?
|
|
}
|
|
|
|
function assert_sample_proc_not_in_cgroup()
|
|
{
|
|
assert_task_not_in_cgroup $sample_proc
|
|
return $?
|
|
}
|
|
|
|
function assert_sample_proc_in_named_cgroup()
|
|
{
|
|
assert_task_in_named_cgroup $sample_proc "$1"
|
|
return $?
|
|
}
|
|
|
|
function assert_sample_proc_not_in_named_cgroup()
|
|
{
|
|
assert_task_not_in_named_cgroup $sample_proc "$1"
|
|
return $?
|
|
}
|
|
|
|
function get_task_state()
|
|
{
|
|
ps -p $1 -o 'state=' 2>/dev/null
|
|
}
|
|
|
|
# TODO Check: Do these need to ignore case differences?
|
|
function assert_task_state()
|
|
{
|
|
local task_pid=$1
|
|
local expected_state="$2"
|
|
local ps_state="$(get_task_state ${task_pid})"
|
|
local rc=$?
|
|
|
|
[ $rc -eq 0 -a "$ps_state" == "${expected_state}" ] && return 0
|
|
rc=$?
|
|
result=${result:-$rc}
|
|
tst_resm ${LIB_TEST_STATE} "Expected task ${task_pid} to be in state \"${expected_state}\""
|
|
return $rc
|
|
}
|
|
|
|
#
|
|
# Check that the specified task is not in the specified state
|
|
#
|
|
function assert_task_not_in_state()
|
|
{
|
|
local task_pid=$1
|
|
local expected_state="$2"
|
|
local ps_state="$(get_task_state ${task_pid})"
|
|
local rc=$?
|
|
|
|
[ $rc -eq 0 -a "$ps_state" != "${expected_state}" ] && return 0
|
|
rc=$?
|
|
result=${result:-$rc}
|
|
tst_resm ${LIB_TEST_STATE} "Expected task ${task_pid} to not be in state \"${expected_state}\""
|
|
return $rc
|
|
}
|
|
|
|
#
|
|
# Frozen tasks are in the "D" state according to ps
|
|
# tasks in "T" state may also be in a "frozen" state
|
|
#
|
|
function assert_task_not_frozen()
|
|
{
|
|
local task_pid=$1
|
|
local ps_state="$(ps -p $task_pid -o 'state=')"
|
|
local rc=$?
|
|
|
|
[ $rc -eq 0 -a "$ps_state" != "D" ] && return 0
|
|
rc=$?
|
|
result=${result:-$rc}
|
|
tst_resm ${LIB_TEST_STATE} "Expected task ${task_pid} is not frozen (unexpected task state: \"$ps_state\")"
|
|
return $rc
|
|
}
|
|
|
|
function assert_task_is_frozen()
|
|
{
|
|
local task_pid=$1
|
|
local ps_state="$(ps -p $task_pid -o 'state=')"
|
|
local rc=$?
|
|
|
|
[ $rc -eq 0 -a "$ps_state" == "D" -o "$ps_state" == "T" ] && return 0
|
|
rc=$?
|
|
result=${result:-$rc}
|
|
tst_resm ${LIB_TEST_STATE} "Expected task ${task_pid} to be frozen (unexpected task state: \"$ps_state\")"
|
|
return $rc
|
|
}
|
|
|
|
function assert_sample_proc_not_frozen()
|
|
{
|
|
assert_task_not_frozen $sample_proc
|
|
return $?
|
|
}
|
|
|
|
function assert_sample_proc_is_frozen()
|
|
{
|
|
assert_task_is_frozen $sample_proc
|
|
return $?
|
|
}
|
|
|
|
function assert_sample_proc_stopped()
|
|
{
|
|
assert_task_state $sample_proc 'T'
|
|
return $?
|
|
}
|
|
|
|
function assert_sample_proc_not_stopped()
|
|
{
|
|
assert_task_not_in_state $sample_proc 'T'
|
|
return $?
|
|
}
|
|
|
|
function assert_sample_proc_sleeping()
|
|
{
|
|
assert_task_state $sample_proc 'S'
|
|
return $?
|
|
}
|
|
|
|
function assert_sample_proc_not_sleeping()
|
|
{
|
|
assert_task_not_in_state $sample_proc 'S'
|
|
return $?
|
|
}
|
|
|
|
function assert_cgroup_subsys_state_rwfile()
|
|
{
|
|
if [ "${cgroup_subsys}" == "freezer" ]; then
|
|
assert_cgroup_rwfile "freezer.state" "freezer state"
|
|
return $?
|
|
elif [ "${cgroup_subsys}" == "freezer" ]; then
|
|
assert_cgroup_rwfile "signal.kill" "signal file"
|
|
return $?
|
|
else
|
|
return -1
|
|
fi
|
|
}
|
|
|
|
function get_freezer_state()
|
|
{
|
|
local state="$(cat freezer.state)"
|
|
local rc=$?
|
|
|
|
if [ $rc -ne 0 ]; then
|
|
result=${result:-$rc}
|
|
tst_resm ${LIB_TEST_STATE} "Failed to read freezer state."
|
|
return $rc
|
|
fi
|
|
echo "${state}"
|
|
return 0
|
|
}
|
|
|
|
function assert_cgroup_freezer_state()
|
|
{
|
|
local goal_state="$1"
|
|
local state="$(get_freezer_state)"
|
|
local rc=$?
|
|
|
|
[ $rc -eq 0 -a "${state}" == "${goal_state}" ] && return 0
|
|
rc=$?
|
|
result=${result:-$rc}
|
|
tst_resm ${LIB_TEST_STATE} "Expected freezer state \"$2\" but found freezer state: \"$state\""
|
|
return $rc
|
|
}
|
|
|
|
function make_sample_cgroup_named()
|
|
{
|
|
local name="$1"
|
|
local saved_dir="$(pwd)"
|
|
mkdir "${name}"
|
|
local rc=$?
|
|
|
|
# So long as we made the directory we don't care
|
|
if [ ! -d "${name}" -a $rc -ne 0 ]; then
|
|
# But if it doesn't exist report the exit status of mkdir
|
|
result=${result:-$rc}
|
|
return $rc
|
|
fi
|
|
|
|
cd "${name}" > /dev/null 2>&1
|
|
|
|
rc=$?
|
|
if [ $rc -ne 0 ]; then
|
|
result=${result:-$rc}
|
|
return $rc
|
|
fi
|
|
|
|
assert_cgroup_tasks_rwfile || {
|
|
cd "${saved_dir}"
|
|
return $?
|
|
}
|
|
assert_cgroup_tasks_empty || {
|
|
cd "${saved_dir}"
|
|
return $?
|
|
}
|
|
assert_cgroup_subsys_state_rwfile || {
|
|
cd "${saved_dir}"
|
|
return $?
|
|
}
|
|
cd "${saved_dir}"
|
|
return 0
|
|
}
|
|
|
|
function make_sample_cgroup()
|
|
{
|
|
make_sample_cgroup_named "child"
|
|
local rc=$?
|
|
|
|
# So long as we made the directory we don't care
|
|
if [ $rc -ne 0 ]; then
|
|
return $rc
|
|
fi
|
|
|
|
cd "child" # we know this will succeed since make_sample_cgroup_named
|
|
# tested this
|
|
return 0
|
|
}
|
|
|
|
function rm_sample_cgroup_named()
|
|
{
|
|
local cgroup_name="$1"
|
|
local saved_dir="$(pwd)"
|
|
local rc=0
|
|
|
|
cd "${cgroup_name}" && {
|
|
assert_cgroup_tasks_rwfile || {
|
|
cd "${saved_dir}"
|
|
return $?
|
|
}
|
|
assert_cgroup_tasks_empty || {
|
|
cd "${saved_dir}"
|
|
return $?
|
|
}
|
|
assert_cgroup_subsys_state_rwfile || {
|
|
cd "${saved_dir}"
|
|
return $?
|
|
}
|
|
cd "${saved_dir}"
|
|
} || {
|
|
rc=$?
|
|
result=${result:-$rc}
|
|
return $rc
|
|
}
|
|
|
|
[ -d "${cgroup_name}" ] && rmdir "${cgroup_name}" && return 0
|
|
rc=$?
|
|
tst_resm TWARN "Failed to remove cgroup \"${cgroup_name}\""
|
|
result=${result:-$rc}
|
|
return $rc
|
|
}
|
|
|
|
function rm_sample_cgroup()
|
|
{
|
|
local cgroup_name="$(basename $(pwd))"
|
|
local rc=0
|
|
|
|
cd .. || {
|
|
rc=$?
|
|
result=${result:-$rc}
|
|
return $rc
|
|
}
|
|
rm_sample_cgroup_named "${cgroup_name}"
|
|
return $?
|
|
}
|
|
|
|
function ls_pids()
|
|
{
|
|
ps -e -o 'pid=' | sed -e 's/[[:space:]]\+//g'
|
|
}
|
|
|
|
function assert_task_exists()
|
|
{
|
|
local task_pid=$1
|
|
|
|
ls_pids | grep -E "^${task_pid}\$" > /dev/null 2>&1 && return 0
|
|
local rc=$?
|
|
|
|
result=${result:-$rc}
|
|
tst_resm ${LIB_TEST_STATE} "Expected pid ${task_pid} does not exist"
|
|
return $rc
|
|
}
|
|
|
|
function assert_task_does_not_exist()
|
|
{
|
|
local task_pid=$1
|
|
|
|
ls_pids | grep -E "^${task_pid}\$" > /dev/null 2>&1 || return 0
|
|
local rc=1 # $? == 0 is an error in this case
|
|
|
|
result=${result:-$rc}
|
|
tst_resm ${LIB_TEST_STATE} "Did not expect pid ${task_pid} to exist"
|
|
return $rc
|
|
}
|
|
|
|
function assert_sample_proc_exists()
|
|
{
|
|
assert_task_exists $sample_proc
|
|
return $?
|
|
}
|
|
|
|
function assert_sample_proc_does_not_exist()
|
|
{
|
|
assert_task_does_not_exist $sample_proc
|
|
return $rc
|
|
}
|
|
|
|
function start_sample_proc()
|
|
{
|
|
local sample_cmd="/bin/sleep"
|
|
local args
|
|
|
|
args=( $sample_sleep ) # can't assign directly because of bash v2/v3 inconsistency
|
|
if [ $# -gt 0 ]; then
|
|
sample_cmd="$1"
|
|
shift 1
|
|
args=( "$@" )
|
|
fi
|
|
|
|
[ -n "$sample_proc" ] && assert_sample_proc_does_not_exist
|
|
|
|
"$sample_cmd" "${args[@]}" &
|
|
local rc=$?
|
|
export sample_proc=$!
|
|
assert_sample_proc_exists
|
|
|
|
return $rc
|
|
}
|
|
|
|
function add_sample_proc_to_named_cgroup()
|
|
{
|
|
local cgroup_name="$1"
|
|
|
|
assert_sample_proc_exists
|
|
"${CG_FILE_WRITE}" $sample_proc > "${cgroup_name}/tasks"
|
|
local rc=$?
|
|
if [ $rc -ne 0 ]; then
|
|
result=${result:-$rc}
|
|
tst_resm ${LIB_TEST_STATE} "Failed to add sample process $sample_proc to cgroup \"${cgroup_name}\""
|
|
return $rc
|
|
fi
|
|
assert_task_in_named_cgroup $sample_proc "${cgroup_name}"
|
|
return $?
|
|
}
|
|
|
|
function add_sample_proc_to_cgroup()
|
|
{
|
|
add_sample_proc_to_named_cgroup "$(pwd)"
|
|
return $?
|
|
}
|
|
|
|
function kill_sample_proc()
|
|
{
|
|
if [ -z "$sample_proc" ]; then
|
|
# It's no longer running or never started.
|
|
# If it was supposed to have started but did not then that
|
|
# should be determined by checking start_sample_proc results.
|
|
return 0
|
|
fi
|
|
|
|
# Hey, bash, don't print out any of your messy job status notices
|
|
disown -a
|
|
|
|
if [ "$(get_task_state $sample_proc)" == "D" ]; then
|
|
tst_resm TWARN "sample process is frozen stiff"
|
|
kill $sample_proc
|
|
local rc=$?
|
|
result=${result:-$rc}
|
|
return $rc
|
|
fi
|
|
|
|
# kill child processes of the sample process
|
|
while pgrep -P $sample_proc ; do
|
|
pkill -SIGTERM -P $sample_proc
|
|
sleep 1
|
|
pkill -SIGKILL -P $sample_proc
|
|
|
|
ps -p $(pgrep -P $sample_proc) -o 'state=' | grep -v D && continue
|
|
# Give up if all the child processes are frozen in D state or
|
|
# if there aren't any more child processes
|
|
break
|
|
done
|
|
# DEBUG dump pstree under $sample_proc:
|
|
# pstree -A -p $sample_proc
|
|
kill $sample_proc > /dev/null 2>&1 || kill -s SIGKILL $sample_proc > /dev/null 2>&1 || {
|
|
local rc=$?
|
|
|
|
ps -p $sample_proc -o 'state=' > /dev/null 2>&1
|
|
if [ $? -eq 1 ]; then
|
|
# It's dead. We're OK.
|
|
return 0
|
|
fi
|
|
# It's still alive somehow! Give up.
|
|
result=${result:-$rc}
|
|
tst_resm TWARN "Failed to kill sample process $sample_proc (kill exit status: $rc)"
|
|
}
|
|
assert_sample_proc_not_in_cgroup
|
|
assert_sample_proc_does_not_exist
|
|
return $?
|
|
}
|
|
|
|
function issue_freeze_cmd()
|
|
{
|
|
local goal_state="FROZEN"
|
|
local sample_state_count=1
|
|
local state="$(get_freezer_state)"
|
|
local rc=$?
|
|
|
|
if [ $rc -ne 0 ]; then
|
|
return $rc
|
|
fi
|
|
|
|
while [ "${state}" != "${goal_state}" ]; do
|
|
"${CG_FILE_WRITE}" "${FREEZE}" > freezer.state
|
|
sleep $sample_state_period
|
|
state="$(get_freezer_state)"
|
|
rc=$?
|
|
if [ $rc -ne 0 ]; then
|
|
break
|
|
fi
|
|
|
|
((sample_state_count++))
|
|
if [ "$sample_state_count" -ge "$max_state_samples" ]; then
|
|
break
|
|
fi
|
|
done
|
|
|
|
if [ "${state}" == "${goal_state}" ]; then
|
|
return 0
|
|
fi
|
|
|
|
result=${result:-$rc}
|
|
tst_resm ${LIB_TEST_STATE} "Failed to issue freeze command (freezer state: \"`get_freezer_state`\")."
|
|
return $rc
|
|
}
|
|
|
|
# If we're trying to "freeze" tasks with SIGTOP
|
|
function issue_stop_as_freeze_cmd()
|
|
{
|
|
local goal_state="T"
|
|
local sample_state_count=1
|
|
local ps_state="$(get_task_state ${task_pid})"
|
|
local rc=$?
|
|
|
|
if [ $rc -ne 0 ]; then
|
|
return $rc
|
|
fi
|
|
|
|
while [ "${ps_state}" != "${goal_state}" ]; do
|
|
kill -s SIGSTOP $sample_proc
|
|
sleep $sample_state_period
|
|
ps_state="$(get_task_state ${task_pid})"
|
|
rc=$?
|
|
if [ $rc -ne 0 ]; then
|
|
break
|
|
fi
|
|
|
|
((sample_state_count++))
|
|
if [ "$sample_state_count" -ge "$max_state_samples" ]; then
|
|
break
|
|
fi
|
|
done
|
|
|
|
if [ "${ps_state}" == "${goal_state}" ]; then
|
|
return 0
|
|
fi
|
|
|
|
result=${result:-$rc}
|
|
tst_resm ${LIB_TEST_STATE} "Failed to issue stop (freeze) command (task state: \"${ps_state}\")."
|
|
return $rc
|
|
}
|
|
|
|
function send_signal()
|
|
{
|
|
"${CG_FILE_WRITE}" $1 > 'signal.kill' && return 0
|
|
local rc=$?
|
|
result=${result:-$rc}
|
|
tst_resm ${LIB_TEST_STATE} "Failed to send signal: $1 to tasks in cgroup (rc: $rc)"
|
|
return $rc
|
|
}
|
|
|
|
function wait_until_goal_state_or_timeout()
|
|
{
|
|
local goal_state="$1"
|
|
local sample_state_count=1
|
|
local state="$(get_freezer_state)"
|
|
local rc=$?
|
|
|
|
if [ $rc -ne 0 ]; then
|
|
return $rc
|
|
fi
|
|
|
|
while [ "${state}" != "${goal_state}" ]; do
|
|
sleep $sample_state_period
|
|
state="$(get_freezer_state)"
|
|
rc=$?
|
|
if [ $rc -ne 0 ]; then
|
|
break
|
|
fi
|
|
|
|
((sample_state_count++))
|
|
if [ "$sample_state_count" -ge "$max_state_samples" ]; then
|
|
break
|
|
fi
|
|
done
|
|
return $rc
|
|
}
|
|
|
|
# TODO convert signal scripts -- insert task between until and goal
|
|
function wait_until_sample_proc_goal_state_or_timeout()
|
|
{
|
|
local goal_state="$1"
|
|
local sample_state_count=1
|
|
local ps_state="$(get_task_state ${sample_proc})"
|
|
local rc=$?
|
|
|
|
while [ $rc -eq 0 -a "${ps_state}" != "${goal_state}" -a \
|
|
"$sample_state_count" -lt "$max_state_samples" ]; do
|
|
sleep $sample_state_period
|
|
ps_state="$(get_task_state ${sample_proc})"
|
|
rc=$?
|
|
if [ $rc -ne 0 ]; then
|
|
result=${result:-$rc}
|
|
tst_resm ${LIB_TEST_STATE} "Failed to read process state."
|
|
break
|
|
fi
|
|
|
|
((sample_state_count++))
|
|
done
|
|
return $rc
|
|
}
|
|
|
|
# TODO convert signal scripts -- insert task between until and not
|
|
function wait_until_sample_proc_not_goal_state_or_timeout()
|
|
{
|
|
local goal_state="$1"
|
|
local sample_state_count=1
|
|
local ps_state="$(get_task_state ${sample_proc})"
|
|
local rc=$?
|
|
|
|
while [ $rc -eq 0 -a "${ps_state}" == "${goal_state}" -a \
|
|
"$sample_state_count" -lt "$max_state_samples" ]; do
|
|
sleep $sample_state_period
|
|
ps_state="$(get_task_state ${sample_proc})"
|
|
rc=$?
|
|
if [ $rc -ne 0 ]; then
|
|
result=${result:-$rc}
|
|
tst_resm ${LIB_TEST_STATE} "Failed to read process state."
|
|
break
|
|
fi
|
|
|
|
((sample_state_count++))
|
|
done
|
|
return $rc
|
|
}
|
|
|
|
function wait_until_frozen()
|
|
{
|
|
wait_until_goal_state_or_timeout "FROZEN" || return $?
|
|
assert_cgroup_freezer_state "FROZEN" "ERROR: failed to freeze cgroup"
|
|
# TODO assert all tasks in cgroup are in 'D' or 'T' state
|
|
# TODO assert that trying to add a task to cgroup results in EBUSY
|
|
return $?
|
|
}
|
|
|
|
function issue_thaw_cmd()
|
|
{
|
|
"${CG_FILE_WRITE}" "${THAW}" > freezer.state && return 0
|
|
local rc=$?
|
|
result=${result:-$rc}
|
|
tst_resm ${LIB_TEST_STATE} "Failed to issue thaw command."
|
|
return $rc
|
|
}
|
|
|
|
function issue_cont_as_thaw_cmd()
|
|
{
|
|
local goal_state="T"
|
|
local sample_state_count=1
|
|
local ps_state="$(get_task_state ${task_pid})"
|
|
local rc=$?
|
|
|
|
if [ $rc -ne 0 ]; then
|
|
return $rc
|
|
fi
|
|
|
|
while [ "${ps_state}" == "${goal_state}" ]; do
|
|
kill -s SIGCONT $sample_proc
|
|
sleep $sample_state_period
|
|
ps_state="$(get_task_state ${task_pid})"
|
|
rc=$?
|
|
if [ $rc -ne 0 ]; then
|
|
break
|
|
fi
|
|
|
|
((sample_state_count++))
|
|
if [ "$sample_state_count" -ge "$max_state_samples" ]; then
|
|
break
|
|
fi
|
|
done
|
|
|
|
if [ "${ps_state}" != "${goal_state}" ]; then
|
|
return 0
|
|
fi
|
|
|
|
result=${result:-$rc}
|
|
tst_resm ${LIB_TEST_STATE} "Failed to issue continue (thaw) command (task state: \"${ps_state}\")."
|
|
return $rc
|
|
}
|
|
|
|
function wait_until_thawed()
|
|
{
|
|
wait_until_goal_state_or_timeout "THAWED" || return $?
|
|
assert_cgroup_freezer_state "THAWED" "ERROR: Failed to thaw cgroup."
|
|
return $?
|
|
}
|
|
|
|
function wait_until_freezing()
|
|
{
|
|
wait_until_goal_state_or_timeout "FREEZING"
|
|
# Time critical -- we race with the kernel as it freezes tasks in the
|
|
# cgroup. So rather than assert "FREEZING" we just return
|
|
return $?
|
|
}
|
|
|
|
function wait_until_sample_proc_stopped()
|
|
{
|
|
wait_until_sample_proc_state_or_timeout 'T' || return $?
|
|
assert_sample_proc_stopped
|
|
return $?
|
|
}
|
|
|
|
function wait_until_sample_proc_not_stopped()
|
|
{
|
|
wait_until_sample_proc_not_goal_state_or_timeout 'T' || return $?
|
|
assert_sample_proc_not_stopped
|
|
return $?
|
|
}
|