-
Notifications
You must be signed in to change notification settings - Fork 5
/
lockClocks.sh
343 lines (343 loc) · 13.8 KB
/
lockClocks.sh
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
#
# 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
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"
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_core_check() {
if [ "$ARG_CORES" == "big" ]; then
[ $1 -gt $2 ]
elif [ "$ARG_CORES" == "little" ]; then
[ $1 -lt $2 ]
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
}
# Find the min or max (little vs big) of CPU max frequency, and lock cores of the selected type to
# an available frequency that's >= $CPU_TARGET_FREQ_PERCENT% of max. Disable other cores.
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
# Find max CPU freq, and associated list of available freqs
cpuIdealFreq=$CPU_IDEAL_START_FREQ_KHZ
cpuAvailFreqCmpr=0
cpuAvailFreq=0
enableIndices=''
disableIndices=''
cpu=0
# 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.
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}
maxFreq=`cat ${CPU_BASE}/cpu$cpu/cpufreq/cpuinfo_max_freq`
availFreq=`cat ${CPU_BASE}/cpu$cpu/cpufreq/scaling_available_frequencies`
availFreqCmpr=${availFreq// /-}
if (function_core_check $maxFreq $cpuIdealFreq); then
# new min/max of max freq, look for cpus with same max freq and same avail freq list
cpuIdealFreq=${maxFreq}
cpuAvailFreq=${availFreq}
cpuAvailFreqCmpr=${availFreqCmpr}
if [ -z "$disableIndices" ]; then
disableIndices="$enableIndices"
else
disableIndices="$disableIndices $enableIndices"
fi
enableIndices=${cpu}
elif [ ${maxFreq} == ${cpuIdealFreq} ] && [ ${availFreqCmpr} == ${cpuAvailFreqCmpr} ]; then
enableIndices="$enableIndices $cpu"
else
if [ -z "$disableIndices" ]; then
disableIndices="$cpu"
else
disableIndices="$disableIndices $cpu"
fi
fi
cpu=$(($cpu + 1))
done
# check that some CPUs will be enabled
if [ -z "$enableIndices" ]; then
echo "Failed to find any $ARG_CORES cores to enable, aborting."
exit -1
fi
# Chose a frequency to lock to that's >= $CPU_TARGET_FREQ_PERCENT% of max
# (below, 100M = 1K for KHz->MHz * 100 for %)
TARGET_FREQ_MHZ=$(( ($cpuIdealFreq * $CPU_TARGET_FREQ_PERCENT) / 100000 ))
chosenFreq=0
chosenFreqDiff=100000000
for freq in ${cpuAvailFreq}; do
freqMhz=$(( ${freq} / 1000 ))
if [ ${freqMhz} -ge ${TARGET_FREQ_MHZ} ]; then
newChosenFreqDiff=$(( $freq - $TARGET_FREQ_MHZ ))
if [ $newChosenFreqDiff -lt $chosenFreqDiff ]; then
chosenFreq=${freq}
chosenFreqDiff=$(( $chosenFreq - $TARGET_FREQ_MHZ ))
fi
fi
done
# 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, $chosenFreq.
# 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=$((chosenFreq-opFreq))
prevDiff=`function_abs $prevDiff`
currDiff=$((chosenFreq-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
# enable 'big' CPUs
for cpu in ${enableIndices}; do
freq=${CPU_BASE}/cpu$cpu/cpufreq
# 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
# scaling_max_freq must be set before scaling_min_freq
echo ${chosenFreq} > ${freq}/scaling_max_freq
echo ${chosenFreq} > ${freq}/scaling_min_freq
echo ${chosenFreq} > ${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 "$chosenFreq" ] || [ "$obsMin" -ne "$chosenFreq" ] || [ "$obsMax" -ne "$chosenFreq" ]; then
echo "Failed to set CPU$cpu to $chosenFreq Hz! Aborting..."
echo "scaling_cur_freq = $obsCur"
echo "scaling_min_freq = $obsMin"
echo "scaling_max_freq = $obsMax"
exit -1
fi
done
# disable other CPUs (Note: important to enable big cores first!)
for cpu in ${disableIndices}; do
echo 0 > ${CPU_BASE}/cpu${cpu}/online
done
echo "=================================="
echo "Locked CPUs ${enableIndices// /,} to $chosenFreq / $cpuIdealFreq KHz"
echo "Disabled CPUs ${disableIndices// /,}"
echo "=================================="
}
# 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 [email protected]::IPower/default || true
function_lock_cpu
if [ "$DEVICE" -ne "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"