blob: 57d5eff791ac558bf001593bc7c237ce6c6cfdc7 [file] [log] [blame] [edit]
#
# Copyright (C) 2018 The Android Open Source Project
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
#
# This script can be used to lock device clocks to stable levels for comparing
# different versions of software. Since the clock levels are not necessarily
# indicative of real world behavior, this should **never** be used to compare
# performance between different device models.
# Fun notes for maintaining this file:
# $((arithmetic expressions)) can deal with ints > INT32_MAX, but if compares cannot. This is
# why we use MHz.
# $((arithmetic expressions)) can sometimes evaluate right-to-left. This is why we use parens.
# Everything below the initial host-check isn't bash - Android uses mksh
# mksh allows `\n` in an echo, bash doesn't
# can't use `awk`
# can't use `sed`
# can't use `cut` on < L
# can't use `expr` on < L
ARG_CORES=${1:-big}
CPU_TARGET_FREQ_PERCENT=50
GPU_TARGET_FREQ_PERCENT=50
MAX_RETRIES=10
if [ "`command -v getprop`" == "" ]; then
if [ -n "`command -v adb`" ]; then
echo ""
echo "Pushing $0 and running it on device..."
dest=/data/local/tmp/`basename $0`
adb push $0 ${dest}
adb shell ${dest} $@
adb shell rm ${dest}
exit
else
echo "Could not find adb. Options are:"
echo " 1. Ensure adb is on your \$PATH"
echo " 2. Use './gradlew lockClocks'"
echo " 3. Manually adb push this script to your device, and run it there"
exit -1
fi
fi
# require root
if [[ `id` != "uid=0"* ]]; then
echo "Not running as root, cannot lock clocks, aborting"
echo "Run 'adb root' and retry"
exit -1
fi
DEVICE=`getprop ro.product.device`
MODEL=`getprop ro.product.model`
if [ "$ARG_CORES" == "big" ]; then
CPU_IDEAL_START_FREQ_KHZ=0
elif [ "$ARG_CORES" == "little" ]; then
CPU_IDEAL_START_FREQ_KHZ=100000000 ## finding min of max freqs, so start at 100M KHz (100 GHz)
else
echo "Invalid argument \$1 for ARG_CORES, should be 'big' or 'little', but was $ARG_CORES"
exit -1
fi
function_setup_go() {
if [ -f /d/fpsgo/common/force_onoff ]; then
# Disable fpsgo
echo 0 > /d/fpsgo/common/force_onoff
fpsgoState=`cat /d/fpsgo/common/force_onoff`
if [ "$fpsgoState" != "0" ] && [ "$fpsgoState" != "force off" ]; then
echo "Failed to disable fpsgo"
exit -1
fi
fi
}
# Disable CPU hotpluging by killing mpdecision service via ctl.stop system property.
# This helper checks the state and existence of the mpdecision service via init.svc.
# Possible values from init.svc: "stopped", "stopping", "running", "restarting"
function_stop_mpdecision() {
MPDECISION_STATUS=`getprop init.svc.mpdecision`
while [ "$MPDECISION_STATUS" == "running" ] || [ "$MPDECISION_STATUS" == "restarting" ]; do
setprop ctl.stop mpdecision
# Give initrc some time to kill the mpdecision service.
sleep 0.1
MPDECISION_STATUS=`getprop init.svc.mpdecision`
done
}
# Given a target frequency and list of available frequencies, selects one closest to the target
function_find_target_mhz() {
targetFreq=$1
availFreq=$2
# Choose a frequency to lock to that's >= $CPU_TARGET_FREQ_PERCENT% of max
# (below, 100M = 1K for KHz->MHz * 100 for %)
targetFreqMhz=$(( ($targetFreq * $CPU_TARGET_FREQ_PERCENT) / 100000 ))
outputFreq=0
outputFreqDiff=100000000
for freq in ${availFreq}; do
freqMhz=$(( ${freq} / 1000 ))
if [ ${freqMhz} -ge ${targetFreqMhz} ]; then
newOutputFreqDiff=$(( $freq - $targetFreqMhz ))
if [ $newOutputFreqDiff -lt $outputFreqDiff ]; then
outputFreq=${freq}
outputFreqDiff=$(( $outputFreq - $targetFreqMhz ))
fi
fi
done
echo $outputFreq
}
# Enable and lock each cpu core to an available frequency that's >=
# $CPU_TARGET_FREQ_PERCENT% of max.
function_lock_cpu() {
CPU_BASE=/sys/devices/system/cpu
GOV=cpufreq/scaling_governor
# Options to make clock locking on go devices more sticky.
function_setup_go
# Stop mpdecision (CPU hotplug service) if it exists. Not available on all devices.
function_stop_mpdecision
# Loop through all available cores; We have to check by the parent folder
# "cpu#" instead of cpu#/online or cpu#/cpufreq directly, since they may
# not be accessible yet.
echo "=================================="
cpu=0
while [ -d ${CPU_BASE}/cpu${cpu} ]; do
# Try to enable core, so we can find its frequencies.
# Note: In cases where the online file is inaccessible, it represents a
# core which cannot be turned off, so we simply assume it is enabled if
# this command fails.
if [ -f "$CPU_BASE/cpu$cpu/online" ]; then
echo 1 > ${CPU_BASE}/cpu${cpu}/online || true
fi
# set userspace governor on all CPUs to ensure freq scaling is disabled
echo userspace > ${CPU_BASE}/cpu${cpu}/${GOV}
cpu=$(($cpu + 1))
done
# Set frequencies in a separate loop, after online and governor have been
# set for all cores - empirically, this improves consistency of the locking
cpu=0
remainingRetries=$MAX_RETRIES
while [ -d ${CPU_BASE}/cpu${cpu} ]; do
maxFreq=`cat ${CPU_BASE}/cpu$cpu/cpufreq/cpuinfo_max_freq`
availFreq=`cat ${CPU_BASE}/cpu$cpu/cpufreq/scaling_available_frequencies`
idealFreq=`function_find_target_mhz "$maxFreq" "$availFreq"`
# scaling_max_freq must be set before scaling_min_freq
freq=${CPU_BASE}/cpu$cpu/cpufreq
echo ${idealFreq} > ${freq}/scaling_max_freq
echo ${idealFreq} > ${freq}/scaling_min_freq
echo ${idealFreq} > ${freq}/scaling_setspeed
# Give system a bit of time to propagate the change to scaling_setspeed.
sleep 0.1
# validate setting the freq worked
obsCur=`cat ${freq}/scaling_cur_freq`
obsMin=`cat ${freq}/scaling_min_freq`
obsMax=`cat ${freq}/scaling_max_freq`
if [ "$obsCur" -ne "$idealFreq" ] || [ "$obsMin" -ne "$idealFreq" ] || [ "$obsMax" -ne "$idealFreq" ]; then
if [ remainingRetries -le 0 ]; then
echo "Failed to set CPU$cpu to $idealFreq Hz! Aborting..."
echo "${freq}/scaling_cur_freq = $obsCur"
echo "${freq}/scaling_min_freq = $obsMin"
echo "${freq}/scaling_max_freq = $obsMax"
exit -1
fi
remainingRetries=$(($remainingRetries - 1))
echo "Failed to set CPU$cpu to $idealFreq Hz! Retrying ($remainingRetries retries left)..."
sleep 0.2
else
echo "Locked CPU ${cpu} to $idealFreq / $maxFreq KHz"
remainingRetries=$MAX_RETRIES
cpu=$(($cpu + 1))
fi
done
echo "=================================="
# Lock wembley clocks using high-priority op code method.
# This block depends on the shell utility awk, which is only available on API 27+
if [ "$DEVICE" == "wembley" ]; then
# Get list of available frequencies to lock to by parsing the op-code list.
AVAIL_OP_FREQS=`cat /proc/cpufreq/MT_CPU_DVFS_LL/cpufreq_oppidx \
| awk '{print $2}' \
| tail -n +3 \
| while read line; do
echo "${line:1:${#line}-2}"
done`
# Compute the closest available frequency to the desired frequency, $idealFreq.
# This assumes the op codes listen in /proc/cpufreq/MT_CPU_DVFS_LL/cpufreq_oppidx are listed
# in order and 0-indexed.
opCode=-1
opFreq=0
currOpCode=-1
for currOpFreq in $AVAIL_OP_FREQS; do
currOpCode=$((currOpCode + 1))
prevDiff=$((idealFreq-opFreq))
prevDiff=`function_abs $prevDiff`
currDiff=$((idealFreq-currOpFreq))
currDiff=`function_abs $currDiff`
if [ $currDiff -lt $prevDiff ]; then
opCode="$currOpCode"
opFreq="$currOpFreq"
fi
done
echo "$opCode" > /proc/ppm/policy/ut_fix_freq_idx
fi
}
# Returns the absolute value of the first arg passed to this helper.
function_abs() {
n=$1
if [ $n -lt 0 ]; then
echo "$((n * -1 ))"
else
echo "$n"
fi
}
# If we have a Qualcomm GPU, find its max frequency, and lock to
# an available frequency that's >= GPU_TARGET_FREQ_PERCENT% of max.
function_lock_gpu_kgsl() {
if [ ! -d /sys/class/kgsl/kgsl-3d0/ ]; then
# not kgsl, abort
echo "Currently don't support locking GPU clocks of $MODEL ($DEVICE)"
return -1
fi
if [ ${DEVICE} == "walleye" ] || [ ${DEVICE} == "taimen" ]; then
# Workaround crash
echo "Unable to lock GPU clocks of $MODEL ($DEVICE)"
return -1
fi
GPU_BASE=/sys/class/kgsl/kgsl-3d0
gpuMaxFreq=0
gpuAvailFreq=`cat $GPU_BASE/devfreq/available_frequencies`
for freq in ${gpuAvailFreq}; do
if [ ${freq} -gt ${gpuMaxFreq} ]; then
gpuMaxFreq=${freq}
fi
done
# (below, 100M = 1M for MHz * 100 for %)
TARGET_FREQ_MHZ=$(( (${gpuMaxFreq} * ${GPU_TARGET_FREQ_PERCENT}) / 100000000 ))
chosenFreq=${gpuMaxFreq}
index=0
chosenIndex=0
for freq in ${gpuAvailFreq}; do
freqMhz=$(( ${freq} / 1000000 ))
if [ ${freqMhz} -ge ${TARGET_FREQ_MHZ} ] && [ ${chosenFreq} -ge ${freq} ]; then
# note avail freq are generally in reverse order, so we don't break out of this loop
chosenFreq=${freq}
chosenIndex=${index}
fi
index=$(($index + 1))
done
lastIndex=$(($index - 1))
firstFreq=`function_cut_first_from_space_seperated_list $gpuAvailFreq`
if [ ${gpuMaxFreq} != ${firstFreq} ]; then
# pwrlevel is index of desired freq among available frequencies, from highest to lowest.
# If gpuAvailFreq appears to be in-order, reverse the index
chosenIndex=$(($lastIndex - $chosenIndex))
fi
echo 0 > ${GPU_BASE}/bus_split
echo 1 > ${GPU_BASE}/force_clk_on
echo 10000 > ${GPU_BASE}/idle_timer
echo performance > ${GPU_BASE}/devfreq/governor
# NOTE: we store in min/max twice, because we don't know if we're increasing
# or decreasing, and it's invalid to try and set min > max, or max < min
echo ${chosenFreq} > ${GPU_BASE}/devfreq/min_freq
echo ${chosenFreq} > ${GPU_BASE}/devfreq/max_freq
echo ${chosenFreq} > ${GPU_BASE}/devfreq/min_freq
echo ${chosenFreq} > ${GPU_BASE}/devfreq/max_freq
echo ${chosenIndex} > ${GPU_BASE}/min_pwrlevel
echo ${chosenIndex} > ${GPU_BASE}/max_pwrlevel
echo ${chosenIndex} > ${GPU_BASE}/min_pwrlevel
echo ${chosenIndex} > ${GPU_BASE}/max_pwrlevel
obsCur=`cat ${GPU_BASE}/devfreq/cur_freq`
obsMin=`cat ${GPU_BASE}/devfreq/min_freq`
obsMax=`cat ${GPU_BASE}/devfreq/max_freq`
if [ obsCur -ne ${chosenFreq} ] || [ obsMin -ne ${chosenFreq} ] || [ obsMax -ne ${chosenFreq} ]; then
echo "Failed to set GPU to $chosenFreq Hz! Aborting..."
echo "cur_freq = $obsCur"
echo "min_freq = $obsMin"
echo "max_freq = $obsMax"
echo "index = $chosenIndex"
exit -1
fi
echo "Locked GPU to $chosenFreq / $gpuMaxFreq Hz"
}
# cut is not available on some devices (Nexus 5 running LRX22C).
function_cut_first_from_space_seperated_list() {
list=$1
for freq in $list; do
echo $freq
break
done
}
# kill processes that manage thermals / scaling
stop thermal-engine || true
stop perfd || true
stop vendor.thermal-engine || true
stop vendor.perfd || true
setprop vendor.powerhal.init 0 || true
setprop ctl.interface_restart android.hardware.power@1.0::IPower/default || true
function_lock_cpu
if [ ${DEVICE} != "wembley" ]; then
function_lock_gpu_kgsl
else
echo "Unable to lock gpu clocks of $MODEL ($DEVICE)."
fi
# Memory bus - hardcoded per-device for now
if [ ${DEVICE} == "marlin" ] || [ ${DEVICE} == "sailfish" ]; then
echo 13763 > /sys/class/devfreq/soc:qcom,gpubw/max_freq
else
echo "Unable to lock memory bus of $MODEL ($DEVICE)."
fi
echo "$DEVICE clocks have been locked - to reset, reboot the device"