On Thu, Feb 16, 2017 at 09:52:37AM +0100, Ravineet Singh wrote: > The wrapper script; odp_run_app_isolated.sh can be used to run ODP > testcases in a isolated environment. Background noise can also be > generated. > > Signed-off-by: Ravineet Singh <ravineet.si...@linaro.org> > > v2: moved task-isolation dir to odp/scripts, requested my Maxim Uvarov > v3: fixed checkpatch.pl warnings > --- > scripts/task-isolation/README | 22 ++ > scripts/task-isolation/isolate-cpu.sh | 287 > +++++++++++++++++++++ > scripts/task-isolation/isolate-task.sh | 160 ++++++++++++ > .../performance/odp_run_app_isolated.sh | 108 ++++++++ > 4 files changed, 577 insertions(+) > create mode 100644 scripts/task-isolation/README > create mode 100755 scripts/task-isolation/isolate-cpu.sh > create mode 100755 scripts/task-isolation/isolate-task.sh > create mode 100755 test/linux-generic/performance/odp_run_app_isolated.sh > > diff --git a/scripts/task-isolation/README b/scripts/task-isolation/README > new file mode 100644 > index 0000000..cb02056 > --- /dev/null > +++ b/scripts/task-isolation/README > @@ -0,0 +1,22 @@ > +Helper scripts to check and set CPU isolation and execution of application in > +isolated CPU(s) > + > +Files: > +isolate-cpu.sh isolates desired CPUs > +isolate-task.sh uses isolate-cpu.sh to isolate CPUs before running > + the desired task. It also provides the possibility > + to trace kernel disturbance on the isolated CPUs. > + > +isolate-cpu.sh checks the kernel configuration and the kernel cmdline to > + determine if one or several CPUs are isolated, i.e. it checks for; > + CONFIG_NO_HZ_FULL_ALL=y", and CONFIG_RCU_NOCB_CPU_ALL=y" in the kernel > config > + and rcu_nocbs,nohz_full in the kernel cmdline. > + If the desired CPU(s) are not inte the above configuration, it warns but > continues > + to isolate the CPU(s) as much as possibe. The isolation is accomplished by > + - Redirecting all IRQ's away from desired isolated cores > + - Using cset (cpuset) to move all running processes and kernel threads > + away from desired isolated cores > + > +isolate-task.sh uses isolate-cpu.sh to isolate desired CPU(s). In addition > + it starts the supplied application on isolated CPU(s) and optionally traces > + the isolated CPU(s) for kernel interaction. > diff --git a/scripts/task-isolation/isolate-cpu.sh > b/scripts/task-isolation/isolate-cpu.sh > new file mode 100755 > index 0000000..3eaf98a > --- /dev/null > +++ b/scripts/task-isolation/isolate-cpu.sh > @@ -0,0 +1,287 @@ > +#!/bin/bash > +# > +# Copyright (c) 2017, Linaro Limited > +# All rights reserved. > +# > +# SPDX-License-Identifier: BSD-3-Clause > +# > +# Script that passes command line arguments to odp_scheduling after, > +# optionally, isolating CPU > +# > +# This script isolates desired CPUS, i.e. > +# - Checks kernel cmdline and kernel config to determine > +# if the environment is optimised for isolated task execution; > +# - Moves CPU interrupts, kernel threads, tasks etc. away from the > +# targeted CPU. > +# *Note* CPU 0 cannot be isolated, i.e minimum 2 CPU's are required. > + > + > +print_usage() { > + echo "$0 [-h] [-a] [-c <cpu list>] [-l] [-r] [-d]" > + echo > + echo " Isolate CPU(s) from other tasks, kernel threads and IRQs" > + echo " Args:" > + echo " -h Print this message" > + echo " -a Isolate all CPUs (except CPU 0)" > + echo " -c List of CPUs to be isolated." > + echo " -l Show isolation proporties" > + echo " -r Reset isolation" > + echo " -d Show debug printouts" > + echo "" > + echo " Examples:" > + echo " Isolate all CPU(s) (except 0) " > + echo " $0 -a" > + echo > + echo " Isolate CPUs 1-3 " > + echo " $0 -c 1-3" > + echo > + echo " Isolate CPUs 1 and 4 " > + echo " $0 -c 1,4 "
maybe use a here document? > +} > + > +dlog() { > + [ $DEBUG ] && echo "$*" > +} > + > +warn() { > + printf "Warning: $*\n" >&2 > +} > + > +die() { > + printf "Error: $*\n" >&2 > + exit 1 > +} > + > +get_cpu_array() { > + [ $1 ] || die "$FUNCNAME internal error!" > + > + local cpus="" > + IFS=. a=$1; IFS=, a=$a; I don't understand why IFS=. is needed... Also, shouldn't IFS be restored here? > + for str in $a; do > + if [[ $str == *[\-]* ]]; then > + str=$(echo $str| sed 's/-/../g') > + str=$(eval echo {$str}) > + fi nice! > + > + if [ "$cpus" != "" ]; then > + cpus="$cpus $str" > + else > + cpus=$str > + fi you can probably get away with cpus="$cpus $str" > + done > + > + echo $cpus > +} > + > +## > +# Check kernel config and kernel cmdline for rcu callbacs and no_hz > +# *Note* isolcpu= kernel cmdline option isolates CPUs from SMP balancing > +# If needed, this can be done via > +# cpusets/user/cpuset.sched_load_balance > +## > +check_kernel_config() { > + > + eval $(cat /proc/cmdline | grep -o 'nohz_full=[^ ]*') > + > + local configs="/proc/config.gz /boot/config-$(uname -r) /boot/config " > + dlog "Looking for Kernel configs; $configs " > + for config in $configs; do > + if [ -e $config ]; then > + dlog "Kenel configuration found:$config" > + break > + fi > + done > + > + local all_except_0="1-$(($(getconf _NPROCESSORS_ONLN) - 1))" > + if [ $config ]; then I think this will always be evaluated to true, shouldn't it be if [ -e $config ] ? or even -r $config? > + nohz_full=$(zgrep "CONFIG_NO_HZ_FULL_ALL=y" $config 2>/dev/null) \ > + && nohz_full=$all_except_0 > + else > + warn "Kernel config not found, only checking /proc/cmdline for"\ > + " isolation features." > + fi > + > + if ! [ "$nohz_full" ]; then > + eval $(cat /proc/cmdline | grep -o 'nohz_full=[^ ]*') no need to pipe: grep ... /proc/cmdline > + fi > + > + eval $(cat /proc/cmdline | grep -o 'isolcpus=[^ ]*') ditto > + if [ -z "$isolcpus" ]; then > + warn "No CPU is isolated from kernel/user threads, isolcpus= is "\ > + "not set in kernel cmdline." > + else > + gbl_isolated_cpus=$isolcpus > + export gbl_isolated_cpus > + fi > + > + if [ -z "$nohz_full" ]; then > + warn "No CPU is isolated from kernel ticks, CONFIG_NO_HZ_FULL_ALL=y" > \ > + " not set in kernel, nor nohz_full= set in kernel cmdline." > + fi > + > + for i in `pgrep rcu` ; do taskset -pc 0 $i >/dev/null; done can you do this? > + > + dlog "isolcpus:$isolcpus" > + dlog "nohz_full:$nohz_full" > + #dlog "rcu_nocbs:$rcu_nocbs" > + > + return 0 > +} > + > +cpus_valid() { > + local cpus="$1" > + local isolated=$2 > + local iarray=$(get_cpu_array $isolated) > + local carray=$(get_cpu_array $cpus) > + > + for c in $carray; do > + for i in $iarray; do maybe check that the user didn't provide 0 as a CPU to isolate? > + if [ $i = $c ]; then > + yah=$i > + fi > + done > + [ -z "$yah" ] && return 1 > + done > + > + return 0 > +} > + > +check_prequesties() { > + dlog "Checking prequesties; user is root, kernel has cpuset support,"\ > + " and commads; set, zgrep, getconf are available" > + [ $UID -eq 0 ] || die "You need to be root!" > + grep -q -s cpuset /proc/filesystems || die "Kernel does not support > cpuset!" > + which getconf > /dev/null 2>&1 || die "getconf command not found, please > "\ > + "install getconf" > + which cset > /dev/null 2>&1 || die "cset command not found, please "\ > + "install cpuset" > + which zgrep > /dev/null 2>&1 || die "zgrep command not found, please "\ > + "install gzip" > +} > + > +shield_reset() { > + cset shield -r >/dev/null 2>&1 > + sleep 0.1 > +} > + > +shield_list() { > + sets="/cpusets/*/" > + for i in $sets ; do > + if ! [ -e $i ]; then > + continue > + fi > + printf "Domain %s cpus %s, running %d tasks\n" \ > + $(basename $i) $(cat $i/cpuset.cpus) $(cat $i/tasks | wc -l) > + done > +} > + > +shield_cpus() { > + local cpus="$1" > + > + dlog "shielding CPU:s $cpus" > + > + #Reset and create new shield > + shield_reset > + out=$(cset shield -c $cpus -k on 2>&1) || die "cset failed; $out" > + # Delay the annoying vmstat timer far away > + sysctl vm.stat_interval=120 >/dev/null > + > + # Shutdown nmi watchdog as it uses perf events > + sysctl -w kernel.watchdog=0 >/dev/null > + > + # Pin the writeback workqueue to CPU0 > + #Fixme, check that /sys/bus is mounted? > + echo 1 > /sys/bus/workqueue/devices/writeback/cpumask > + > + # Disable load balanser. balancer > + echo 0 > /cpusets/user/cpuset.sched_load_balance > + > + #Fixme, for now just send all irqs to core 0 > + for affinity in /proc/irq/*/smp_affinity; do > + dlog "redirecting $affinity" > + echo 1 > $affinity 2>/dev/null || dlog "$affinity redirection > failed." > + done > + > + > + #Fixme, not implemented. > + if [ $false ]; then > + for affinity in /proc/irq/*/smp_affinity; do > + local old_mask=$(cat $affinity) > + local new_mask=$((oldmask ^ cpus )) > + echo $new_mask > $affinity > + done > + fi > +} > + > +isolate_cpus() { > + > + local cpus="$1" > + > + check_kernel_config > + > + if [ "$gbl_isolated_cpus" ]; then > + cpus_valid $cpus $gbl_isolated_cpus || > + warn "Selected CPU '$cpus' is not inside isolated cpus "\ > + "array:$gbl_isolated_cpus" > + fi > + > + dlog "Isolating CPUs $cpus" > + > + shield_cpus $cpus > + > + # Verfiy cores empty > + for c in $(get_cpu_array $cpus); do > + running=$(ps ax -o pid,psr,comm | \ > + awk -v cpu="$c" '{if($2==cpu){print $3}}') > + if [ "$running" != "" ]; then > + warn "Core $c not empty!" > + dlog "; running tasks:\n$running\n" > + fi > + done indentation (tabs used here but spaces above) > + > + return 0 > +} > + > +## > +# Script entry point > +## > +while getopts hdarlc: arguments > +do > + case $arguments in > + h) > + print_usage > + exit 0 > + ;; > + d) > + DEBUG=1 > + ;; > + a) > + ISOL_CPUS="1-$(($(getconf _NPROCESSORS_ONLN) - 1))" > + ;; > + r) > + shield_reset > + exit 0 > + ;; > + l) > + shield_list > + exit 0 > + ;; > + c) > + [ "$ISOL_CPUS" ] || ISOL_CPUS=$OPTARG > + ;; > + *) > + print_usage > + exit 1 > + ;; > + esac > +done > +#Remove all flags > +shift $((OPTIND-1)) > + > +if ! [ $ISOL_CPUS ]; then > + print_usage > + exit 1 > +fi > + > +check_prequesties > +isolate_cpus $ISOL_CPUS || die "isolate_cpus failed." isolate_cpus() always retuns success... > diff --git a/scripts/task-isolation/isolate-task.sh > b/scripts/task-isolation/isolate-task.sh > new file mode 100755 > index 0000000..3e588bc > --- /dev/null > +++ b/scripts/task-isolation/isolate-task.sh > @@ -0,0 +1,160 @@ > +#!/bin/bash > +# > +# Copyright (c) 2017, Linaro Limited > +# All rights reserved. > +# > +# SPDX-License-Identifier: BSD-3-Clause > +# > +# Script that passes command line arguments to odp_scheduling after, > +# optionally, isolating CPU > +# > +# This script isolates a task on desired CPUs and > +# optionally creates background noise. > + > +print_usage() { > + echo "$0 [-c <cpu list>] [-d] [-h] [-n] <application arg1, arg2, ...>" > + echo > + echo " Isolate CPU(s) from other tasks, kernel threads and IRQs" > + echo " and run an application on isolated CPUs" > + echo " Args:" > + echo " -c List of CPUs to be isolated" > + echo " -d Show debug printouts" > + echo " -h Print this message" > + echo " -n Create background noise (stress)" > + echo "" > + echo "All CPU's, except CPU 0, are isolated unless '-c' specified" > + echo " Examples:" > + echo " Isolate CPU 1,2 and run application in the same." > + echo " $0 -n -c 1,2 /some/path/application" > + echo > + echo " Isolate all possible CPUs and run applicatipon" > + echo " $0 /path/application" > +} > + > +dlog() { > + [ $DEBUG ] && echo "$*" > +} > + > +die() { > + printf "Error: $*\n" >&2 > + exit 1 > +} > + > +trap cleanup INT EXIT > + > +cleanup(){ > + local pids=$(pgrep $MY_STRESS 2>/dev/null) > + local base=$(dirname $0) > + > + $base/isolate-cpu.sh -r > + [ "$pids" != "" ] && kill -9 $pids >/dev/null 2>&1 > + kill -9 $CHILD >/dev/null 2>&1 > + rm -f $MY_STRESS_PATH > +} > + > +wait_app_started () { > + local child=$1 > + local ltasks=0 > + > + while true; do > + sleep 0.01 > + kill -0 $child 2>/dev/null || break > + tasks=$(ls /proc/$child/task | wc -l) > + [ $tasks -eq $ltasks ] && break > + ltasks=$tasks > + done > + dlog "app started, # threads:$ltasks" > +} > + > +create_noise() { > + local mpath=$1 > + local nr=$(grep processor /proc/cpuinfo | wc -l) > + > + ln -sf $(which stress) $mpath || die "ln failed" > + $mpath -c $nr 2>&1 >/dev/null & > + disown $! > +} > + > +isolate_cpu(){ > + local cpus=$1 > + local base=$(dirname $0) > + $base/isolate-cpu.sh -c $cpus || die "$0 failed" > +} > + > +run_application() { > + local app="$1" > + > + dlog "Starting application: $app" > + $app& > + child=$! > + CHILD=$child > + > + echo $child >> /cpusets/user/tasks > + if [ $? -ne 0 ]; then > + kill -9 $child > + die "Failed to isolate task..." > + fi > + > + wait_app_started $child > + wait $child > +} > + > +check_prequesties() { > + local base=$(dirname $0) > + > + [ -e $base/isolate-cpu.sh ] || die "$base/isolate-cpu.sh not found!" > + [ $UID -eq 0 ] || die "You need to be root!" > + which stress > /dev/null 2>&1 || die "stress command not found, "\ > + "please install stress" > +} > + > +## > +# Script entry point > +## > + > +ISOL_CPUS="1-$(($(getconf _NPROCESSORS_ONLN) - 1))" > + > +while getopts hdnc: arguments > +do > + case $arguments in > + h) > + print_usage > + exit 0 > + ;; > + d) > + DEBUG=1 > + ;; > + n) > + NOISE=1 > + ;; > + c) > + ISOL_CPUS=$OPTARG > + ;; > + *) > + print_usage > + exit 1 > + ;; > + esac > +done > +# Remove all flags > +shift $((OPTIND-1)) > + > +if ! [ "$1" ]; then > + print_usage > + exit 1 > +fi > + > +#Isolate and optionally create noise > +command="$*" > +set -- $command > + > +check_prequesties > + > +MY_STRESS=stress-by-$$ > +MY_STRESS_PATH=/tmp/$MY_STRESS > + > +isolate_cpu $ISOL_CPUS || die "isolate cpu failed!" > +[ -z $NOISE ] || create_noise $MY_STRESS_PATH > +run_application "$command" > + > +exit $? > diff --git a/test/linux-generic/performance/odp_run_app_isolated.sh > b/test/linux-generic/performance/odp_run_app_isolated.sh > new file mode 100755 > index 0000000..3bed0a7 > --- /dev/null > +++ b/test/linux-generic/performance/odp_run_app_isolated.sh there is a mix of spaces and tabs for indentation in this script > @@ -0,0 +1,108 @@ > +#!/bin/sh > +# > +# Copyright (c) 2017, Linaro Limited > +# All rights reserved. > +# > +# SPDX-License-Identifier: BSD-3-Clause > +# > + > + > +TEST_DIR="${TEST_DIR:-$(dirname $0)}" > +PERFORMANCE="$TEST_DIR/../../common_plat/performance" > +ISOL_DIR="${TEST_DIR}/../../../scripts/task-isolation" > +APPLICATION="${PERFORMANCE}/odp_pktio_perf${EXEEXT}" > +APPLICATION_ARGS="" > +APPLICATION_BASE="$(basename ${APPLICATION})" > + > +cleanup(){ > + pids=$(pgrep stress 2>/dev/null) > + [ "$pids" != "" ] && kill -9 $pids > +} > + > +print_usage() { > + echo "$0 [-i] [-n] [-h] [<application> <application args>]" > + echo > + echo " Run an application with or without isolation and background noise" > + echo " Flags:" > + echo " -h Print this message" > + echo " -i Isolate CPU prior to running application." > + echo " -n Create background noise (stress)" > + echo "" > + echo " <application> targeted application" > + echo " <args> targeted application arguments" > + echo " *Note* Default application is ${APPLICATION_BASE}" > + echo "" > + echo " Example:" > + echo " Isolate CPU, create background noise and run > ${APPLICATION_BASE}:" > + echo " $0 -i -n" > + echo > + echo " Run ${APPLICATION_BASE}, w/o isolation but with background > noise:" > + echo " $0 -n" > + echo > + echo " Run Myapp, without isolation but with background noise:" > + echo " $0 -n Myapp -s ome args" > +} > + > +run() { > + local isolate=$1 > + local noise=$2 > + if [ ${isolate} -eq 1 ]; then > + [ ${noise} -eq 1 ] && noise_par="-n" > + echo Running ${APPLICATION_BASE} with isolation and background noise > + echo ===================================================== > + $ISOL_DIR/isolate-task.sh ${noise_par} ${APPLICATION} \ > + ${APPLICATION_ARGS} || exit 1 > + #reset isolation > + $ISOL_DIR/isolate-cpu.sh -r > + else > + echo Running ${APPLICATION_BASE} without isolation > + echo ===================================================== > + if [ ${noise} -eq 1 ]; then > + local nr=$(grep processor /proc/cpuinfo | wc -l) > + echo " Creating background noise..." > + stress -c $nr 2>&1 >/dev/null & > + fi > + ${APPLICATION} ${APPLICATION_ARGS} || exit 2 > + fi > +} > + > +trap cleanup INT EXIT > +ISOLATE=0 > +NOISE=0 > +while getopts hni arguments > +do > + case $arguments in > + h) > + print_usage > + exit 0 > + ;; > + n) > + NOISE=1 > + $(which stress > /dev/null 2>&1) > + ret=$? > + if [ ${ret} -ne 0 ]; then or: if ! $(which stress > /dev/null 2>&1); then > + echo "'stress' not found, bailing" >&2 > + exit 3 > + fi > + ;; > + i) > + ISOLATE=1 > + ;; > + *) > + print_usage > + exit 1 > + ;; > + esac > +done > +#Remove flags > +shift $((OPTIND-1)) > + > +if [ $# -gt 0 ]; then > + APPLICATION="$1" > + shift > +fi > +[ $# -gt 0 ] && APPLICATION_ARGS=$* > + > +run ${ISOLATE} ${NOISE} > + > +exit $? not needed, the script will exit with the exit value of the last command executed. > -- > 2.7.4 >