#!/bin/sh # # Identifies potential sources issues when using isolate. # # (c) 2017 Bernard Blackham # usage() { cat <&2 Usage: $0 [-q|--quiet] [-e|--execute] Use this script to identify sources of run-time variability and other issues on Linux machines which may affect isolate. If --execute is not specified, the recommended actions are written to stdout as an executable shell script, otherwise, using --execute will attempt to make changes to make the system behave more deterministically. The changes performed by --execute persist only until a reboot. To persist across reboots, the standard output from this script should be added to /etc/rc.local or some other script that is run on each boot. Alternately, you could add the following line to /etc/rc.local to automatically apply these changes on boot, but use this with caution as not all issues can be resolved in this way. isolate-check-environment --quiet --execute The exit status of this script will be 0 if all checks pass, or 1 if some checks have failed. Note that there are more strategies to reduce run-time variability further. See the man page of isolate for details under REPRODUCIBILITY. EOT exit 2 } # Parse options. args=$(getopt -o "ehq" --long "execute,help,quiet" -- "$@") || usage eval set -- "$args" quiet= execute= while : ; do case "$1" in -q|--quiet) quiet=1 ; shift ;; -e|--execute) execute=1 ; shift ;; -h|--help) usage ;; --) shift ; break ;; *) usage ;; esac done [ -n "$*" ] && usage # Some helper boilerplate machinery. exit_status=0 red=$(tput setaf 1) green=$(tput setaf 2) yellow=$(tput setaf 3) normal=$(tput sgr0) # Return true (0) if we are being quiet. quiet() { [ -n "$quiet" ] } # Print all arguments to stderr as warning. warn() { quiet || echo WARNING: "$*" >&2 } # Print first argument to stderr as warning, and second argument to stdout as # the recommended remedial action, or execute if --execute is given. action() { quiet || warn "$1" if [ -n "$execute" ] ; then quiet || echo "+ $2" sh -c "$2" else quiet || echo $2 fi } print_start_check() { quiet && return print_check_status=1 echo -n "Checking for $@ ... " >&2 } print_fail() { exit_status=1 quiet && return [ -n "$print_check_status" ] && echo "${red}FAIL${normal}" >&2 print_check_status= } print_dubious() { exit_status=1 quiet && return [ -n "$print_check_status" ] && echo "${yellow}CAUTION${normal}" >&2 print_check_status= } print_skipped() { quiet && return [ -n "$print_check_status" ] && echo "SKIPPED (not detected)" >&2 print_check_status= } print_finish() { quiet && return [ -n "$print_check_status" ] && echo "${green}PASS${normal}" >&2 print_check_status= } # Check that cgroups are enabled. cgroup_check() { local cgroup=$1 print_start_check "cgroup support for $cgroup" if ! test -f "/sys/fs/cgroup/$cgroup/tasks" ; then print_dubious warn "the $cgroup is not present. isolate --cg cannot be used." fi print_finish } cgroup_check memory cgroup_check cpuacct cgroup_check cpuset # Check that swap is either disabled or accounted for. swap_check() { print_start_check "swap" # If swap is disabled, there is nothing to worry about. local swaps swaps=$(swapon --noheadings) if [ -n "$swaps" ] ; then # Swap is enabled. We had better have the memsw support in the memory # cgroup. if ! test -f "/sys/fs/cgroup/memory/memory.memsw.usage_in_bytes" ; then print_fail action \ "swap is enabled, but swap accounting is not. isolate will not be able to enforce memory limits." \ "swapoff -a" else print_dubious warn "swap is enabled, and although accounted for, may still give run-time variability under memory pressure." fi fi print_finish } swap_check # Check that CPU frequency scaling is disabled. cpufreq_check() { print_start_check "CPU frequency scaling" local anycpus policy anycpus= # Ensure cpufreq governor is set to performance on all CPUs for cpufreq_file in $(find /sys/devices/system/cpu/cpufreq/ -name scaling_governor) ; do policy=$(cat $cpufreq_file) if [ "$policy" != "performance" ] ; then print_fail action \ "cpufreq governor set to '$policy', but 'performance' would be better" \ "echo performance > $cpufreq_file" fi anycpus=1 done [ -z "$anycpus" ] && print_skipped print_finish } cpufreq_check # Check that address space layout randomisation is disabled. aslr_check() { print_start_check "kernel address space randomisation" local val if val=$(cat /proc/sys/kernel/randomize_va_space 2>/dev/null) ; then if [ "$val" -ne 0 ] ; then print_fail action \ "address space randomisation is enabled." \ "echo 0 > /proc/sys/kernel/randomize_va_space" fi else print_skipped fi print_finish } aslr_check # Check that transparent huge-pages are disabled, as this leads to # non-determinism depending on whether the kernel can allocate 2 MiB pages or # not. thp_check() { print_start_check "transparent hugepage support" local val if val=$(cat /sys/kernel/mm/transparent_hugepage/enabled 2>/dev/null) ; then case $val in *'[never]'*) ;; *) print_fail action \ "transparent hugepages are enabled." \ "echo never > /sys/kernel/mm/transparent_hugepage/enabled" ;; esac fi if val=$(cat /sys/kernel/mm/transparent_hugepage/defrag 2>/dev/null) ; then case $val in *'[never]'*) ;; *) print_fail action \ "transparent hugepage defrag is enabled." \ "echo never > /sys/kernel/mm/transparent_hugepage/defrag" ;; esac fi if val=$(cat /sys/kernel/mm/transparent_hugepage/khugepaged/defrag 2>/dev/null) ; then if [ "$val" -ne 0 ] ; then print_fail action \ "khugepaged defrag is enabled." \ "echo 0 > /sys/kernel/mm/transparent_hugepage/khugepaged/defrag" fi fi print_finish } thp_check exit $exit_status