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

#!/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 $?
}