1# 2# Copyright (C) 2018 The Android Open Source Project 3# 4# Licensed under the Apache License, Version 2.0 (the "License"); 5# you may not use this file except in compliance with the License. 6# You may obtain a copy of the License at 7# 8# http://www.apache.org/licenses/LICENSE-2.0 9# 10# Unless required by applicable law or agreed to in writing, software 11# distributed under the License is distributed on an "AS IS" BASIS, 12# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13# See the License for the specific language governing permissions and 14# limitations under the License. 15# 16 17# This script can be used to lock device clocks to stable levels for comparing 18# different versions of software. Since the clock levels are not necessarily 19# indicative of real world behavior, this should **never** be used to compare 20# performance between different device models. 21 22# Fun notes for maintaining this file: 23# $((arithmetic expressions)) can deal with ints > INT32_MAX, but if compares cannot. This is 24# why we use MHz. 25# $((arithmetic expressions)) can sometimes evaluate right-to-left. This is why we use parens. 26# Everything below the initial host-check isn't bash - Android uses mksh 27# mksh allows `\n` in an echo, bash doesn't 28# can't use `awk` 29# can't use `sed` 30# can't use `cut` on < L 31# can't use `expr` on < L 32 33ARG_CORES=${1:-big} 34 35CPU_TARGET_FREQ_PERCENT=50 36GPU_TARGET_FREQ_PERCENT=50 37MAX_RETRIES=10 38 39if [ "`command -v getprop`" == "" ]; then 40 if [ -n "`command -v adb`" ]; then 41 echo "" 42 echo "Pushing $0 and running it on device..." 43 dest=/data/local/tmp/`basename $0` 44 adb push $0 ${dest} 45 adb shell ${dest} $@ 46 adb shell rm ${dest} 47 exit 48 else 49 echo "Could not find adb. Options are:" 50 echo " 1. Ensure adb is on your \$PATH" 51 echo " 2. Use './gradlew lockClocks'" 52 echo " 3. Manually adb push this script to your device, and run it there" 53 exit -1 54 fi 55fi 56 57# require root 58if [[ `id` != "uid=0"* ]]; then 59 echo "Not running as root, cannot lock clocks, aborting" 60 echo "Run 'adb root' and retry" 61 exit -1 62fi 63 64DEVICE=`getprop ro.product.device` 65MODEL=`getprop ro.product.model` 66 67if [ "$ARG_CORES" == "big" ]; then 68 CPU_IDEAL_START_FREQ_KHZ=0 69elif [ "$ARG_CORES" == "little" ]; then 70 CPU_IDEAL_START_FREQ_KHZ=100000000 ## finding min of max freqs, so start at 100M KHz (100 GHz) 71else 72 echo "Invalid argument \$1 for ARG_CORES, should be 'big' or 'little', but was $ARG_CORES" 73 exit -1 74fi 75 76function_setup_go() { 77 if [ -f /d/fpsgo/common/force_onoff ]; then 78 # Disable fpsgo 79 echo 0 > /d/fpsgo/common/force_onoff 80 fpsgoState=`cat /d/fpsgo/common/force_onoff` 81 if [ "$fpsgoState" != "0" ] && [ "$fpsgoState" != "force off" ]; then 82 echo "Failed to disable fpsgo" 83 exit -1 84 fi 85 fi 86} 87 88# Disable CPU hotpluging by killing mpdecision service via ctl.stop system property. 89# This helper checks the state and existence of the mpdecision service via init.svc. 90# Possible values from init.svc: "stopped", "stopping", "running", "restarting" 91function_stop_mpdecision() { 92 MPDECISION_STATUS=`getprop init.svc.mpdecision` 93 while [ "$MPDECISION_STATUS" == "running" ] || [ "$MPDECISION_STATUS" == "restarting" ]; do 94 setprop ctl.stop mpdecision 95 # Give initrc some time to kill the mpdecision service. 96 sleep 0.1 97 MPDECISION_STATUS=`getprop init.svc.mpdecision` 98 done 99} 100 101# Given a target frequency and list of available frequencies, selects one closest to the target 102function_find_target_mhz() { 103 targetFreq=$1 104 availFreq=$2 105 106 # Choose a frequency to lock to that's >= $CPU_TARGET_FREQ_PERCENT% of max 107 # (below, 100M = 1K for KHz->MHz * 100 for %) 108 targetFreqMhz=$(( ($targetFreq * $CPU_TARGET_FREQ_PERCENT) / 100000 )) 109 outputFreq=0 110 outputFreqDiff=100000000 111 for freq in ${availFreq}; do 112 freqMhz=$(( ${freq} / 1000 )) 113 if [ ${freqMhz} -ge ${targetFreqMhz} ]; then 114 newOutputFreqDiff=$(( $freq - $targetFreqMhz )) 115 if [ $newOutputFreqDiff -lt $outputFreqDiff ]; then 116 outputFreq=${freq} 117 outputFreqDiff=$(( $outputFreq - $targetFreqMhz )) 118 fi 119 fi 120 done 121 122 echo $outputFreq 123} 124 125# Enable and lock each cpu core to an available frequency that's >= 126# $CPU_TARGET_FREQ_PERCENT% of max. 127function_lock_cpu() { 128 CPU_BASE=/sys/devices/system/cpu 129 GOV=cpufreq/scaling_governor 130 131 # Options to make clock locking on go devices more sticky. 132 function_setup_go 133 134 135 # Stop mpdecision (CPU hotplug service) if it exists. Not available on all devices. 136 function_stop_mpdecision 137 138 # Loop through all available cores; We have to check by the parent folder 139 # "cpu#" instead of cpu#/online or cpu#/cpufreq directly, since they may 140 # not be accessible yet. 141 echo "==================================" 142 cpu=0 143 while [ -d ${CPU_BASE}/cpu${cpu} ]; do 144 # Try to enable core, so we can find its frequencies. 145 # Note: In cases where the online file is inaccessible, it represents a 146 # core which cannot be turned off, so we simply assume it is enabled if 147 # this command fails. 148 if [ -f "$CPU_BASE/cpu$cpu/online" ]; then 149 echo 1 > ${CPU_BASE}/cpu${cpu}/online || true 150 fi 151 152 153 # set userspace governor on all CPUs to ensure freq scaling is disabled 154 echo userspace > ${CPU_BASE}/cpu${cpu}/${GOV} 155 156 cpu=$(($cpu + 1)) 157 done 158 159 # Set frequencies in a separate loop, after online and governor have been 160 # set for all cores - empirically, this improves consistency of the locking 161 cpu=0 162 remainingRetries=$MAX_RETRIES 163 while [ -d ${CPU_BASE}/cpu${cpu} ]; do 164 maxFreq=`cat ${CPU_BASE}/cpu$cpu/cpufreq/cpuinfo_max_freq` 165 availFreq=`cat ${CPU_BASE}/cpu$cpu/cpufreq/scaling_available_frequencies` 166 idealFreq=`function_find_target_mhz "$maxFreq" "$availFreq"` 167 168 # scaling_max_freq must be set before scaling_min_freq 169 freq=${CPU_BASE}/cpu$cpu/cpufreq 170 echo ${idealFreq} > ${freq}/scaling_max_freq 171 echo ${idealFreq} > ${freq}/scaling_min_freq 172 echo ${idealFreq} > ${freq}/scaling_setspeed 173 174 # Give system a bit of time to propagate the change to scaling_setspeed. 175 sleep 0.1 176 177 # validate setting the freq worked 178 obsCur=`cat ${freq}/scaling_cur_freq` 179 obsMin=`cat ${freq}/scaling_min_freq` 180 obsMax=`cat ${freq}/scaling_max_freq` 181 if [ "$obsCur" -ne "$idealFreq" ] || [ "$obsMin" -ne "$idealFreq" ] || [ "$obsMax" -ne "$idealFreq" ]; then 182 if [ remainingRetries -le 0 ]; then 183 echo "Failed to set CPU$cpu to $idealFreq Hz! Aborting..." 184 echo "${freq}/scaling_cur_freq = $obsCur" 185 echo "${freq}/scaling_min_freq = $obsMin" 186 echo "${freq}/scaling_max_freq = $obsMax" 187 exit -1 188 fi 189 remainingRetries=$(($remainingRetries - 1)) 190 echo "Failed to set CPU$cpu to $idealFreq Hz! Retrying ($remainingRetries retries left)..." 191 sleep 0.2 192 else 193 echo "Locked CPU ${cpu} to $idealFreq / $maxFreq KHz" 194 remainingRetries=$MAX_RETRIES 195 cpu=$(($cpu + 1)) 196 fi 197 done 198 echo "==================================" 199 200 201 # Lock wembley clocks using high-priority op code method. 202 # This block depends on the shell utility awk, which is only available on API 27+ 203 if [ "$DEVICE" == "wembley" ]; then 204 # Get list of available frequencies to lock to by parsing the op-code list. 205 AVAIL_OP_FREQS=`cat /proc/cpufreq/MT_CPU_DVFS_LL/cpufreq_oppidx \ 206 | awk '{print $2}' \ 207 | tail -n +3 \ 208 | while read line; do 209 echo "${line:1:${#line}-2}" 210 done` 211 212 # Compute the closest available frequency to the desired frequency, $idealFreq. 213 # This assumes the op codes listen in /proc/cpufreq/MT_CPU_DVFS_LL/cpufreq_oppidx are listed 214 # in order and 0-indexed. 215 opCode=-1 216 opFreq=0 217 currOpCode=-1 218 for currOpFreq in $AVAIL_OP_FREQS; do 219 currOpCode=$((currOpCode + 1)) 220 221 prevDiff=$((idealFreq-opFreq)) 222 prevDiff=`function_abs $prevDiff` 223 currDiff=$((idealFreq-currOpFreq)) 224 currDiff=`function_abs $currDiff` 225 if [ $currDiff -lt $prevDiff ]; then 226 opCode="$currOpCode" 227 opFreq="$currOpFreq" 228 fi 229 done 230 231 echo "$opCode" > /proc/ppm/policy/ut_fix_freq_idx 232 fi 233} 234 235# Returns the absolute value of the first arg passed to this helper. 236function_abs() { 237 n=$1 238 if [ $n -lt 0 ]; then 239 echo "$((n * -1 ))" 240 else 241 echo "$n" 242 fi 243} 244 245# If we have a Qualcomm GPU, find its max frequency, and lock to 246# an available frequency that's >= GPU_TARGET_FREQ_PERCENT% of max. 247function_lock_gpu_kgsl() { 248 if [ ! -d /sys/class/kgsl/kgsl-3d0/ ]; then 249 # not kgsl, abort 250 echo "Currently don't support locking GPU clocks of $MODEL ($DEVICE)" 251 return -1 252 fi 253 if [ ${DEVICE} == "walleye" ] || [ ${DEVICE} == "taimen" ]; then 254 # Workaround crash 255 echo "Unable to lock GPU clocks of $MODEL ($DEVICE)" 256 return -1 257 fi 258 259 GPU_BASE=/sys/class/kgsl/kgsl-3d0 260 261 gpuMaxFreq=0 262 gpuAvailFreq=`cat $GPU_BASE/devfreq/available_frequencies` 263 for freq in ${gpuAvailFreq}; do 264 if [ ${freq} -gt ${gpuMaxFreq} ]; then 265 gpuMaxFreq=${freq} 266 fi 267 done 268 269 # (below, 100M = 1M for MHz * 100 for %) 270 TARGET_FREQ_MHZ=$(( (${gpuMaxFreq} * ${GPU_TARGET_FREQ_PERCENT}) / 100000000 )) 271 272 chosenFreq=${gpuMaxFreq} 273 index=0 274 chosenIndex=0 275 for freq in ${gpuAvailFreq}; do 276 freqMhz=$(( ${freq} / 1000000 )) 277 if [ ${freqMhz} -ge ${TARGET_FREQ_MHZ} ] && [ ${chosenFreq} -ge ${freq} ]; then 278 # note avail freq are generally in reverse order, so we don't break out of this loop 279 chosenFreq=${freq} 280 chosenIndex=${index} 281 fi 282 index=$(($index + 1)) 283 done 284 lastIndex=$(($index - 1)) 285 286 firstFreq=`function_cut_first_from_space_seperated_list $gpuAvailFreq` 287 288 if [ ${gpuMaxFreq} != ${firstFreq} ]; then 289 # pwrlevel is index of desired freq among available frequencies, from highest to lowest. 290 # If gpuAvailFreq appears to be in-order, reverse the index 291 chosenIndex=$(($lastIndex - $chosenIndex)) 292 fi 293 294 echo 0 > ${GPU_BASE}/bus_split 295 echo 1 > ${GPU_BASE}/force_clk_on 296 echo 10000 > ${GPU_BASE}/idle_timer 297 298 echo performance > ${GPU_BASE}/devfreq/governor 299 300 # NOTE: we store in min/max twice, because we don't know if we're increasing 301 # or decreasing, and it's invalid to try and set min > max, or max < min 302 echo ${chosenFreq} > ${GPU_BASE}/devfreq/min_freq 303 echo ${chosenFreq} > ${GPU_BASE}/devfreq/max_freq 304 echo ${chosenFreq} > ${GPU_BASE}/devfreq/min_freq 305 echo ${chosenFreq} > ${GPU_BASE}/devfreq/max_freq 306 echo ${chosenIndex} > ${GPU_BASE}/min_pwrlevel 307 echo ${chosenIndex} > ${GPU_BASE}/max_pwrlevel 308 echo ${chosenIndex} > ${GPU_BASE}/min_pwrlevel 309 echo ${chosenIndex} > ${GPU_BASE}/max_pwrlevel 310 311 obsCur=`cat ${GPU_BASE}/devfreq/cur_freq` 312 obsMin=`cat ${GPU_BASE}/devfreq/min_freq` 313 obsMax=`cat ${GPU_BASE}/devfreq/max_freq` 314 if [ obsCur -ne ${chosenFreq} ] || [ obsMin -ne ${chosenFreq} ] || [ obsMax -ne ${chosenFreq} ]; then 315 echo "Failed to set GPU to $chosenFreq Hz! Aborting..." 316 echo "cur_freq = $obsCur" 317 echo "min_freq = $obsMin" 318 echo "max_freq = $obsMax" 319 echo "index = $chosenIndex" 320 exit -1 321 fi 322 echo "Locked GPU to $chosenFreq / $gpuMaxFreq Hz" 323} 324 325# cut is not available on some devices (Nexus 5 running LRX22C). 326function_cut_first_from_space_seperated_list() { 327 list=$1 328 329 for freq in $list; do 330 echo $freq 331 break 332 done 333} 334 335# kill processes that manage thermals / scaling 336stop thermal-engine || true 337stop perfd || true 338stop vendor.thermal-engine || true 339stop vendor.perfd || true 340setprop vendor.powerhal.init 0 || true 341setprop ctl.interface_restart android.hardware.power@1.0::IPower/default || true 342 343function_lock_cpu 344 345if [ ${DEVICE} != "wembley" ]; then 346 function_lock_gpu_kgsl 347else 348 echo "Unable to lock gpu clocks of $MODEL ($DEVICE)." 349fi 350 351# Memory bus - hardcoded per-device for now 352if [ ${DEVICE} == "marlin" ] || [ ${DEVICE} == "sailfish" ]; then 353 echo 13763 > /sys/class/devfreq/soc:qcom,gpubw/max_freq 354else 355 echo "Unable to lock memory bus of $MODEL ($DEVICE)." 356fi 357 358echo "$DEVICE clocks have been locked - to reset, reboot the device" 359