#!/bin/sh # # PM-QA validation test suite for the power management on Linux # # Copyright (C) 2008-2009 Canonical Ltd. # Copyright (C) 2011-2016, Linaro Limited. # # This program is free software; you can redistribute it and/or # modify it under the terms of the GNU General Public License # as published by the Free Software Foundation; either version 2 # of the License, or (at your option) any later version. # # This program 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 General Public License for more details. # # You should have received a copy of the GNU General Public License # along with this program; if not, write to the Free Software # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. # # Contributors: # Daniel Lezcano (IBM Corporation) # - initial API and implementation # Michael Frey # - initial suspend/resume functions # Andy Whitcroft # - initial suspend/resume functions # Hongbo Zhang # - updated suspend/resume functions # . ../Switches/Switches.sh LOGDIR='/var/lib/pm-utils' LOGFILE="$LOGDIR/stress.log" CPU_PATH="/sys/devices/system/cpu" TEST_NAME=$(basename ${0%.sh}) PREFIX=$TEST_NAME INC=0 cpus=$(ls $CPU_PATH | grep "cpu[0-9].*") pass_count=0 fail_count=0 skip_count=0 test_script_status="pass" NANOSLEEP="../utils/nanosleep" gov_array="governors_backup" freq_array="frequencies_backup" THERMAL_PATH="/sys/devices/virtual/thermal" MAX_ZONE=0-12 MAX_CDEV=0-50 scaling_freq_array="scaling_freq" mode_array="mode_list" thermal_gov_array="thermal_governor_backup" # config options for suspend/resume dry=0 auto=1 pm_trace=1 timer_sleep=20 test_status_show() { if [ $fail_count -ne 0 ]; then test_script_status="fail" else if [ $skip_count -ne 0 ]; then if [ $pass_count -ne 0 ]; then test_script_status="pass" else test_script_status="skip" fi fi fi echo " " if [ "$test_script_status" = "fail" ]; then echo "$TEST_NAME: fail" elif [ "$test_script_status" = "skip" ]; then echo "$TEST_NAME: skip" else echo "$TEST_NAME: pass" fi echo " " } skip_tests() { dir=$1 test_script_list=$(ls ../$1/*.sh | grep -v 'sanity.sh$' | grep -v '00.sh$') for test_script in $test_script_list; do test_case=$(basename $test_script .sh) echo "$test_case: skip" done } log_begin() { printf "%-76s" "$TEST_NAME.$INC$CPU: $@... " INC=$(($INC+1)) } log_end() { printf "$*\n" if [ "$*" = "Err" ]; then fail_count=$((fail_count + 1)) elif [ "$*" = "skip" ]; then skip_count=$((skip_count + 1)) else pass_count=$((pass_count + 1)) fi } log_skip() { log_begin "$@" log_end "skip" } for_each_cpu() { cpu_func=$1 shift 1 for cpu in $cpus; do INC=0 CPU=/$cpu $cpu_func $cpu $@ done return 0 } for_each_governor() { gov_cpu=$1 gov_func=$2 cpufreq_dirpath=$CPU_PATH/$gov_cpu/cpufreq governors=$(cat $cpufreq_dirpath/scaling_available_governors) shift 2 for governor in $governors; do $gov_func $gov_cpu $governor $@ done return 0 } for_each_frequency() { freq_cpu=$1 freq_func=$2 cpufreq_dirpath=$CPU_PATH/$freq_cpu/cpufreq frequencies=$(cat $cpufreq_dirpath/scaling_available_frequencies) shift 2 for frequency in $frequencies; do $freq_func $freq_cpu $frequency $@ done return 0 } set_governor() { gov_cpu=$1 scaling_gov_dirpath=$CPU_PATH/$gov_cpu/cpufreq/scaling_governor newgov=$2 echo $newgov > $scaling_gov_dirpath } get_governor() { gov_cpu=$1 scaling_gov_dirpath=$CPU_PATH/$gov_cpu/cpufreq/scaling_governor cat $scaling_gov_dirpath } wait_latency() { wait_latency_cpu=$1 cpufreq_dirpath=$CPU_PATH/$wait_latency_cpu/cpufreq gov=$(cat $cpufreq_dirpath/scaling_governor) # consider per-policy governor case if [ -e $CPU_PATH/$wait_latency_cpu/cpufreq/$gov ]; then #try one path to see if the sampling_rate can be found if [ -e $CPU_PATH/$wait_latency_cpu/cpufreq/$gov/sampling_rate ]; then sampling_rate=$(cat $CPU_PATH/$wait_latency_cpu/cpufreq/$gov/sampling_rate) else # try another path to get the sampling_rate if [ -e $CPU_PATH/cpufreq/$gov/sampling_rate ]; then sampling_rate=$(cat $CPU_PATH/cpufreq/$gov/sampling_rate) else return 1 fi fi else return 1 fi sampling_rate=$((sampling_rate * 1000)) # unit nsec latency=$(cat $cpufreq_dirpath/cpuinfo_transition_latency) if [ $? -ne 0 ]; then return 1 fi nrfreq=$(cat $cpufreq_dirpath/scaling_available_frequencies | wc -w) if [ $? -ne 0 ]; then return 1 fi nrfreq=$((nrfreq + 1)) sleep_time=$(($latency + $sampling_rate)) $NANOSLEEP $(($nrfreq * $sleep_time)) } frequnit() { freq=$1 ghz=$(echo $freq | awk '{printf "%.1f", ($1 / 1000000)}') mhz=$(echo $freq | awk '{printf "%.1f", ($1 / 1000)}') ghz_value=$(echo $ghz | awk '{printf "%f", ($1 > 1.0)}') if [ "$ghz_value" = "1" ]; then echo $ghz GHz return 0 fi mhz_value=$(echo $mhz | awk '{printf "%f", ($1 > 1.0)}') if [ "$mhz_value" = "1" ];then echo $mhz MHz return 0 fi echo $freq KHz } set_frequency() { freq_cpu=$1 cpufreq_dirpath=$CPU_PATH/$freq_cpu/cpufreq newfreq=$2 setfreqpath=$cpufreq_dirpath/scaling_setspeed echo $newfreq > $setfreqpath wait_latency $freq_cpu } get_frequency() { freq_cpu=$1 scaling_cur_freq=$CPU_PATH/$freq_cpu/cpufreq/scaling_cur_freq cat $scaling_cur_freq } get_max_frequency() { freq_cpu=$1 scaling_max_freq=$CPU_PATH/$freq_cpu/cpufreq/scaling_max_freq cat $scaling_max_freq } get_min_frequency() { freq_cpu=$1 scaling_min_freq=$CPU_PATH/$freq_cpu/cpufreq/scaling_min_freq cat $scaling_min_freq } set_online() { current_cpu=$1 current_cpu_path=$CPU_PATH/$current_cpu if [ "$current_cpu" = "cpu0" ]; then return 0 fi echo 1 > $current_cpu_path/online } set_offline() { current_cpu=$1 current_cpu_path=$CPU_PATH/$current_cpu if [ "$current_cpu" = "cpu0" ]; then return 0 fi echo 0 > $current_cpu_path/online } get_online() { current_cpu=$1 current_cpu_path=$CPU_PATH/$current_cpu cat $current_cpu_path/online } check() { check_descr=$1 check_func=$2 shift 2; log_begin "checking $check_descr" $check_func $@ if [ $? -ne 0 ]; then log_end "Err" return 1 fi log_end "Ok" return 0 } check_file() { file=$1 dir=$2 check "'$file' exists in '$dir'" "test -f" $dir/$file } check_cpufreq_files() { cpu_id=$1 cpufreq_files_dir=$CPU_PATH/$cpu_id/cpufreq shift 1 for i in $@; do check_file $i $cpufreq_files_dir || return 1 done return 0 } check_sched_mc_files() { for i in $@; do check_file $i $CPU_PATH || return 1 done return 0 } check_topology_files() { cpu=$1 topology_files_dir=$CPU_PATH/$cpu/topology shift 1 for i in $@; do check_file $i $topology_files_dir || return 1 done return 0 } check_cpuhotplug_files() { cpuhotplug_files_dir=$CPU_PATH/$1 shift 1 for i in $@; do if [ `echo $cpuhotplug_files_dir | grep -c "cpu0"` -eq 1 ]; then if [ $hotplug_allow_cpu0 -eq 0 ]; then continue fi fi check_file $i $cpuhotplug_files_dir || return 1 done return 0 } save_governors() { index=0 for cpu in $cpus; do scaling_gov_value=$(cat $CPU_PATH/$cpu/cpufreq/scaling_governor) eval $gov_array$index=$scaling_gov_value eval export $gov_array$index done } restore_governors() { index=0 for cpu in $cpus; do oldgov=$(eval echo \$$gov_array$index) echo $oldgov > $CPU_PATH/$cpu/cpufreq/scaling_governor index=$((index + 1)) done } save_frequencies() { index=0 for cpu in $cpus; do freq_value=$(cat $CPU_PATH/$cpu/cpufreq/scaling_cur_freq) eval $freq_array$index=$freq_value eval export $freq_array$index done } restore_frequencies() { index=0 for cpu in $cpus; do oldfreq=$(eval echo \$$freq_array$index) echo $oldfreq > $CPU_PATH/$cpu/cpufreq/scaling_setspeed index=$((index + 1)) done } sigtrap() { exit 255 } # currently we support ubuntu and android get_os() { lsb_release -a 2>&1 | grep -i ubuntu > /dev/null if [ $? -eq 0 ]; then # for ubuntu return 1 else # for android (if needed can look for build.prop) return 2 fi } is_root() { get_os if [ $? -eq 1 ]; then # for ubuntu ret=$(id -u) else # for android ret=$(id | awk '{if ($1) print $1}' | sed 's/[^0-9]*//g') fi return $ret } is_cpu0_hotplug_allowed() { status=$1 if [ $status -eq 1 ]; then return 0 else return 1 fi } check_valid_temp() { file=$1 zone_name=$2 dir=$THERMAL_PATH/$zone_name temp_file=$dir/$file shift 2; temp_val=$(cat $temp_file) descr="'$zone_name'/'$file' ='$temp_val'" log_begin "checking $descr" if [ $temp_val -gt 0 ]; then log_end "Ok" return 0 fi log_end "Err" return 1 } for_each_thermal_zone() { thermal_zones=$(ls $THERMAL_PATH | grep "thermal_zone['$MAX_ZONE']") thermal_func=$1 shift 1 for thermal_zone in $thermal_zones; do INC=0 $thermal_func $thermal_zone $@ done return 0 } get_total_trip_point_of_zone() { zone=$1 zone_path=$THERMAL_PATH/$zone count=0 shift 1 trips=$(ls $zone_path | grep "trip_point_['$MAX_ZONE']_temp") for trip in $trips; do count=$((count + 1)) done return $count } for_each_trip_point_of_zone() { zone_path=$THERMAL_PATH/$1 count=0 func=$2 zone_name=$1 shift 2 trips=$(ls $zone_path | grep "trip_point_['$MAX_ZONE']_temp") for trip in $trips; do $func $zone_name $count count=$((count + 1)) done return 0 } for_each_binding_of_zone() { zone_path=$THERMAL_PATH/$1 count=0 func=$2 zone_name=$1 shift 2 trips=$(ls $zone_path | grep "cdev['$MAX_CDEV']_trip_point") for trip in $trips; do $func $zone_name $count count=$((count + 1)) done return 0 } check_valid_binding() { trip_point=$1 zone_name=$2 dirpath=$THERMAL_PATH/$zone_name temp_file=$zone_name/$trip_point trip_point_val=$(cat $dirpath/$trip_point) get_total_trip_point_of_zone $zone_name trip_point_max=$? descr="'$temp_file' valid binding" shift 2 log_begin "checking $descr" if [ $trip_point_val -ge $trip_point_max ]; then log_end "Err" return 1 fi log_end "Ok" return 0 } validate_trip_bindings() { zone_name=$1 bind_no=$2 dirpath=$THERMAL_PATH/$zone_name trip_point=cdev"$bind_no"_trip_point shift 2 check_file $trip_point $dirpath || return 1 check_valid_binding $trip_point $zone_name || return 1 } validate_trip_level() { zone_name=$1 trip_no=$2 dirpath=$THERMAL_PATH/$zone_name trip_temp=trip_point_"$trip_no"_temp trip_type=trip_point_"$trip_no"_type shift 2 check_file $trip_temp $dirpath || return 1 check_file $trip_type $dirpath || return 1 check_valid_temp $trip_temp $zone_name || return 1 } for_each_cooling_device() { cdev_func=$1 shift 1 cooling_devices=$(ls $THERMAL_PATH | grep "cooling_device['$MAX_CDEV']") if [ "$cooling_devices" = "" ]; then log_skip "no cooling devices" return 0 fi for cooling_device in $cooling_devices; do INC=0 $cdev_func $cooling_device $@ done return 0 } check_scaling_freq() { before_freq_list=$1 after_freq_list=$2 shift 2 index=0 flag=0 for cpu in $cpus; do after_freq=$(eval echo \$$after_freq_list$index) before_freq=$(eval echo \$$before_freq_list$index) if [ $after_freq -ne $before_freq ]; then flag=1 fi index=$((index + 1)) done return $flag } store_scaling_maxfreq() { index=0 for cpu in $cpus; do scaling_freq_max_value=$(cat $CPU_PATH/$cpu/cpufreq/scaling_max_freq) eval $scaling_freq_array$index=$scaling_freq_max_value eval echo $scaling_freq_array$index done return 0 } get_trip_id() { trip_name=$1 shift 1 id1=$(echo $trip_name|cut -c12) id2=$(echo $trip_name|cut -c13) if [ $id2 != "_" ]; then id1=$(($id2 + 10*$id1)) fi return $id1 } disable_all_thermal_zones() { thermal_zones=$(ls $THERMAL_PATH | grep "thermal_zone['$MAX_ZONE']") index=0 for thermal_zone in $thermal_zones; do mode=$(cat $THERMAL_PATH/$thermal_zone/mode) eval $mode_array$index=$mode eval export $mode_array$index index=$((index + 1)) echo -n "disabled" > $THERMAL_PATH/$thermal_zone/mode done return 0 } enable_all_thermal_zones() { thermal_zones=$(ls $THERMAL_PATH | grep "thermal_zone['$MAX_ZONE']") index=0 for thermal_zone in $thermal_zones; do mode=$(eval echo \$$mode_array$index) echo $mode > $THERMAL_PATH/$thermal_zone/mode index=$((index + 1)) done return 0 } GPU_HEAT_BIN=/usr/bin/glmark2 gpu_pid=0 check_for_glmark2() { glmark2_found=$(which glmark2) if [ $? -ne 0 ]; then return 1 else return 0 fi } start_glmark2() { if [ -n "$ANDROID" ]; then am start org.linaro.glmark2/.Glmark2Activity return fi if [ -x $GPU_HEAT_BIN ]; then $GPU_HEAT_BIN & gpu_pid=$(pidof $GPU_HEAT_BIN) # Starting X application from serial console needs this if [ -z "$gpu_pid" ]; then cp /etc/lightdm/lightdm.conf /etc/lightdm/lightdm.conf.bk echo "autologin-user=root" >> /etc/lightdm/lightdm.conf export DISPLAY=localhost:0.0 restart lightdm sleep 5 mv /etc/lightdm/lightdm.conf.bk /etc/lightdm/lightdm.conf $GPU_HEAT_BIN & gpu_pid=$(pidof $GPU_HEAT_BIN) fi test -z "$gpu_pid" && cpu_pid=0 echo "start gpu heat binary $gpu_pid" else echo "glmark2 not found." 1>&2 fi } kill_glmark2() { if [ -n "$ANDROID" ]; then am kill org.linaro.glmark2 return fi if [ "$gpu_pid" -ne 0 ]; then kill -9 $gpu_pid fi } set_thermal_governors() { thermal_zones=$(ls $THERMAL_PATH | grep "thermal_zone['$MAX_ZONE']") gov=$1 index=0 for thermal_zone in $thermal_zones; do policy=$(cat $THERMAL_PATH/$thermal_zone/policy) eval $thermal_gov_array$index=$policy eval export $thermal_gov_array$index index=$((index + 1)) echo $gov > $THERMAL_PATH/$thermal_zone/policy done return 0 } restore_thermal_governors() { thermal_zones=$(ls $THERMAL_PATH | grep "thermal_zone['$MAX_ZONE']") index=0 for thermal_zone in $thermal_zones; do old_policy=$(eval echo \$$thermal_gov_array$index) echo $old_policy > $THERMAL_PATH/$thermal_zone/policy index=$((index + 1)) done return 0 } check_for_thermal_zones() { thermal_zones=$(ls $THERMAL_PATH | grep "thermal_zone['$MAX_ZONE']" 2>/dev/null) if [ ! -z "$thermal_zones" ]; then return 0 else return 1 fi } check_logdir() { if [ ! -f $LOGDIR ]; then mkdir -p $LOGDIR fi } setup_wakeup_timer () { timeout="$1" # Request wakeup from the RTC or ACPI alarm timers. Set the timeout # at 'now' + $timeout seconds. ctl='/sys/class/rtc/rtc0/wakealarm' if [ -f "$ctl" ]; then # Cancel any outstanding timers. echo "0" >"$ctl" # rtcN/wakealarm can use relative time in seconds echo "+$timeout" >"$ctl" return 0 fi ctl='/proc/acpi/alarm' if [ -f "$ctl" ]; then echo `date '+%F %H:%M:%S' -d '+ '$timeout' seconds'` >"$ctl" return 0 fi echo "no method to awaken machine automatically" 1>&2 exit 1 } suspend_system () { if [ "$dry" -eq 1 ]; then echo "DRY-RUN: suspend machine for $timer_sleep" sleep 1 return fi setup_wakeup_timer "$timer_sleep" dmesg >"$LOGFILE.dmesg.A" # Initiate suspend in different ways. case "$1" in dbus) dbus-send --session --type=method_call \ --dest=org.freedesktop.PowerManagement \ /org/freedesktop/PowerManagement \ org.freedesktop.PowerManagement.Suspend \ >> "$LOGFILE" || { ECHO "FAILED: dbus suspend failed" 1>&2 return 1 } ;; pmsuspend) pm-suspend >> "$LOGFILE" ;; mem) `echo "mem" > /sys/power/state` >> "$LOGFILE" ;; esac # Wait on the machine coming back up -- pulling the dmesg over. echo "v---" >>"$LOGFILE" retry=30 while [ "$retry" -gt 0 ]; do retry=$((retry - 1)) # Accumulate the dmesg delta. dmesg >"$LOGFILE.dmesg.B" diff "$LOGFILE.dmesg.A" "$LOGFILE.dmesg.B" | \ grep '^>' >"$LOGFILE.dmesg" mv "$LOGFILE.dmesg.B" "$LOGFILE.dmesg.A" echo "Waiting for suspend to complete $retry to go ..." \ >> "$LOGFILE" cat "$LOGFILE.dmesg" >> "$LOGFILE" if [ "`grep -c 'Back to C!' $LOGFILE.dmesg`" -ne 0 ]; then break; fi sleep 1 done echo "^---" >>"$LOGFILE" rm -f "$LOGFILE.dmesg"* if [ "$retry" -eq 0 ]; then ECHO "SUSPEND FAILED, did not go to sleep" 1>&2 return 1 fi } ECHO () { echo "$@" | tee -a "$LOGFILE" } enable_trace() { if [ -w /sys/power/pm_trace ]; then echo 1 > '/sys/power/pm_trace' fi } disable_trace() { if [ -w /sys/power/pm_trace ]; then echo 0 > '/sys/power/pm_trace' fi } trace_state=-1 save_trace() { if [ -r /sys/power/pm_trace ]; then trace_state=`cat /sys/power/pm_trace` fi } restore_trace() { if [ "$trace_state" -ne -1 -a -w /sys/power/pm_trace ]; then echo "$trace_state" > '/sys/power/pm_trace' fi } battery_count() { cat /proc/acpi/battery/*/state 2>/dev/null | \ awk ' BEGIN { total = 0 } /present:.*yes/ { total += 1 } END { print total } ' } battery_capacity() { cat /proc/acpi/battery/*/state 2>/dev/null | \ awk ' BEGIN { total = 0 } /remaining capacity:/ { total += $3 } END { print total } ' } ac_needed=-1 ac_is=-1 ac_becomes=-1 ac_required() { ac_check ac_needed="$1" ac_becomes="$1" } ac_transitions() { ac_check ac_needed="$1" ac_becomes="$2" } ac_online() { cat /proc/acpi/ac_adapter/*/state 2>/dev/null | \ awk ' BEGIN { online = 0; offline = 0 } /on-line/ { online = 1 } /off-line/ { offline = 1 } END { if (online) { print "1" } else if (offline) { print "0" } else { print "-1" } } ' } ac_check() { ac_current=`ac_online` if [ "$ac_becomes" -ne -1 -a "$ac_current" -ne -1 -a \ "$ac_current" -ne "$ac_becomes" ]; then ECHO "*** WARNING: AC power not in expected state" \ "($ac_becomes) after test" fi ac_is="$ac_becomes" } phase=0 phase_first=1 phase_interactive=1 phase() { phase=$((phase + 1)) if [ "$ac_needed" -ne "$ac_is" ]; then case "$ac_needed" in 0) echo "*** please ensure your AC cord is detached" ;; 1) echo "*** please ensure your AC cord is attached" ;; esac ac_is="$ac_needed" fi if [ "$timer_sleep" -gt 60 ]; then sleep="$timer_sleep / 60" sleep="$sleep minutes" else sleep="$timer_sleep seconds" fi echo "*** machine will suspend for $sleep" if [ "$auto" -eq 1 ]; then : elif [ "$phase_interactive" -eq 1 ]; then echo "*** press return when ready" read x elif [ "$phase_first" -eq 1 ]; then echo "*** NOTE: there will be no further user interaction from this point" echo "*** press return when ready" phase_first=0 read x fi } save_trace if [ "$pm_trace" -eq 1 ]; then enable_trace else disable_trace fi