| # |
| # 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" |