blob: 2c7dc36d07ee5adc399ea0ffd5b261bacf888dc8 [file] [log] [blame]
Sergei Trofimov4e6afe92015-10-09 09:30:04 +01001# Copyright 2014-2015 ARM Limited
2#
3# Licensed under the Apache License, Version 2.0 (the "License");
4# you may not use this file except in compliance with the License.
5# You may obtain a copy of the License at
6#
7# http://www.apache.org/licenses/LICENSE-2.0
8#
9# Unless required by applicable law or agreed to in writing, software
10# distributed under the License is distributed on an "AS IS" BASIS,
11# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12# See the License for the specific language governing permissions and
13# limitations under the License.
14#
15from devlib.module import Module
16from devlib.exception import TargetError
17from devlib.utils.misc import memoized
18
19
20# a dict of governor name and a list of it tunables that can't be read
21WRITE_ONLY_TUNABLES = {
22 'interactive': ['boostpulse']
23}
24
25
26class CpufreqModule(Module):
27
28 name = 'cpufreq'
29
30 @staticmethod
31 def probe(target):
32 path = '/sys/devices/system/cpu/cpufreq'
33 return target.file_exists(path)
34
35 def __init__(self, target):
36 super(CpufreqModule, self).__init__(target)
37 self._governor_tunables = {}
38
39 @memoized
40 def list_governors(self, cpu):
41 """Returns a list of governors supported by the cpu."""
42 if isinstance(cpu, int):
43 cpu = 'cpu{}'.format(cpu)
44 sysfile = '/sys/devices/system/cpu/{}/cpufreq/scaling_available_governors'.format(cpu)
45 output = self.target.read_value(sysfile)
46 return output.strip().split()
47
48 def get_governor(self, cpu):
49 """Returns the governor currently set for the specified CPU."""
50 if isinstance(cpu, int):
51 cpu = 'cpu{}'.format(cpu)
52 sysfile = '/sys/devices/system/cpu/{}/cpufreq/scaling_governor'.format(cpu)
53 return self.target.read_value(sysfile)
54
55 def set_governor(self, cpu, governor, **kwargs):
56 """
57 Set the governor for the specified CPU.
58 See https://www.kernel.org/doc/Documentation/cpu-freq/governors.txt
59
60 :param cpu: The CPU for which the governor is to be set. This must be
61 the full name as it appears in sysfs, e.g. "cpu0".
62 :param governor: The name of the governor to be used. This must be
63 supported by the specific device.
64
65 Additional keyword arguments can be used to specify governor tunables for
66 governors that support them.
67
68 :note: On big.LITTLE all cores in a cluster must be using the same governor.
69 Setting the governor on any core in a cluster will also set it on all
70 other cores in that cluster.
71
72 :raises: TargetError if governor is not supported by the CPU, or if,
73 for some reason, the governor could not be set.
74
75 """
76 if isinstance(cpu, int):
77 cpu = 'cpu{}'.format(cpu)
78 supported = self.list_governors(cpu)
79 if governor not in supported:
80 raise TargetError('Governor {} not supported for cpu {}'.format(governor, cpu))
81 sysfile = '/sys/devices/system/cpu/{}/cpufreq/scaling_governor'.format(cpu)
82 self.target.write_value(sysfile, governor)
83 self.set_governor_tunables(cpu, governor, **kwargs)
84
85 def list_governor_tunables(self, cpu):
86 """Returns a list of tunables available for the governor on the specified CPU."""
87 if isinstance(cpu, int):
88 cpu = 'cpu{}'.format(cpu)
89 governor = self.get_governor(cpu)
90 if governor not in self._governor_tunables:
91 try:
92 tunables_path = '/sys/devices/system/cpu/{}/cpufreq/{}'.format(cpu, governor)
93 self._governor_tunables[governor] = self.target.list_directory(tunables_path)
94 except TargetError: # probably an older kernel
95 try:
96 tunables_path = '/sys/devices/system/cpu/cpufreq/{}'.format(governor)
97 self._governor_tunables[governor] = self.target.list_directory(tunables_path)
98 except TargetError: # governor does not support tunables
99 self._governor_tunables[governor] = []
100 return self._governor_tunables[governor]
101
102 def get_governor_tunables(self, cpu):
103 if isinstance(cpu, int):
104 cpu = 'cpu{}'.format(cpu)
105 governor = self.get_governor(cpu)
106 tunables = {}
107 for tunable in self.list_governor_tunables(cpu):
108 if tunable not in WRITE_ONLY_TUNABLES.get(governor, []):
109 try:
110 path = '/sys/devices/system/cpu/{}/cpufreq/{}/{}'.format(cpu, governor, tunable)
111 tunables[tunable] = self.target.read_value(path)
112 except TargetError: # May be an older kernel
113 path = '/sys/devices/system/cpu/cpufreq/{}/{}'.format(governor, tunable)
114 tunables[tunable] = self.target.read_value(path)
115 return tunables
116
117 def set_governor_tunables(self, cpu, governor=None, **kwargs):
118 """
119 Set tunables for the specified governor. Tunables should be specified as
120 keyword arguments. Which tunables and values are valid depends on the
121 governor.
122
123 :param cpu: The cpu for which the governor will be set. This must be the
124 full cpu name as it appears in sysfs, e.g. ``cpu0``.
125 :param governor: The name of the governor. Must be all lower case.
126
127 The rest should be keyword parameters mapping tunable name onto the value to
128 be set for it.
129
130 :raises: TargetError if governor specified is not a valid governor name, or if
131 a tunable specified is not valid for the governor, or if could not set
132 tunable.
133
134 """
135 if isinstance(cpu, int):
136 cpu = 'cpu{}'.format(cpu)
137 if governor is None:
138 governor = self.get_governor(cpu)
139 valid_tunables = self.list_governor_tunables(cpu)
140 for tunable, value in kwargs.iteritems():
141 if tunable in valid_tunables:
142 try:
143 path = '/sys/devices/system/cpu/{}/cpufreq/{}/{}'.format(cpu, governor, tunable)
144 self.target.write_value(path, value)
145 except TargetError: # May be an older kernel
146 path = '/sys/devices/system/cpu/cpufreq/{}/{}'.format(governor, tunable)
147 self.target.write_value(path, value)
148 else:
149 message = 'Unexpected tunable {} for governor {} on {}.\n'.format(tunable, governor, cpu)
150 message += 'Available tunables are: {}'.format(valid_tunables)
151 raise TargetError(message)
152
153 @memoized
154 def list_frequencies(self, cpu):
155 """Returns a list of frequencies supported by the cpu or an empty list
156 if not could be found."""
157 if isinstance(cpu, int):
158 cpu = 'cpu{}'.format(cpu)
159 try:
160 cmd = 'cat /sys/devices/system/cpu/{}/cpufreq/scaling_available_frequencies'.format(cpu)
161 output = self.target.execute(cmd)
162 available_frequencies = map(int, output.strip().split()) # pylint: disable=E1103
163 except TargetError:
164 # On some devices scaling_frequencies is not generated.
165 # http://adrynalyne-teachtofish.blogspot.co.uk/2011/11/how-to-enable-scalingavailablefrequenci.html
166 # Fall back to parsing stats/time_in_state
167 cmd = 'cat /sys/devices/system/cpu/{}/cpufreq/stats/time_in_state'.format(cpu)
168 out_iter = iter(self.target.execute(cmd).strip().split())
169 available_frequencies = map(int, reversed([f for f, _ in zip(out_iter, out_iter)]))
170 return available_frequencies
171
172 def get_min_frequency(self, cpu):
173 """
174 Returns the min frequency currently set for the specified CPU.
175
176 Warning, this method does not check if the cpu is online or not. It will
177 try to read the minimum frequency and the following exception will be
178 raised ::
179
180 :raises: TargetError if for some reason the frequency could not be read.
181
182 """
183 if isinstance(cpu, int):
184 cpu = 'cpu{}'.format(cpu)
185 sysfile = '/sys/devices/system/cpu/{}/cpufreq/scaling_min_freq'.format(cpu)
186 return self.target.read_int(sysfile)
187
188 def set_min_frequency(self, cpu, frequency, exact=True):
189 """
190 Set's the minimum value for CPU frequency. Actual frequency will
191 depend on the Governor used and may vary during execution. The value should be
192 either an int or a string representing an integer. The Value must also be
193 supported by the device. The available frequencies can be obtained by calling
194 get_frequencies() or examining
195
196 /sys/devices/system/cpu/cpuX/cpufreq/scaling_frequencies
197
198 on the device.
199
200 :raises: TargetError if the frequency is not supported by the CPU, or if, for
201 some reason, frequency could not be set.
202 :raises: ValueError if ``frequency`` is not an integer.
203
204 """
205 if isinstance(cpu, int):
206 cpu = 'cpu{}'.format(cpu)
207 available_frequencies = self.list_frequencies(cpu)
208 try:
209 value = int(frequency)
210 if exact and available_frequencies and value not in available_frequencies:
211 raise TargetError('Can\'t set {} frequency to {}\nmust be in {}'.format(cpu,
212 value,
213 available_frequencies))
214 sysfile = '/sys/devices/system/cpu/{}/cpufreq/scaling_min_freq'.format(cpu)
215 self.target.write_value(sysfile, value)
216 except ValueError:
217 raise ValueError('Frequency must be an integer; got: "{}"'.format(frequency))
218
219 def get_frequency(self, cpu):
220 """
221 Returns the current frequency currently set for the specified CPU.
222
223 Warning, this method does not check if the cpu is online or not. It will
224 try to read the current frequency and the following exception will be
225 raised ::
226
227 :raises: TargetError if for some reason the frequency could not be read.
228
229 """
230 if isinstance(cpu, int):
231 cpu = 'cpu{}'.format(cpu)
232 sysfile = '/sys/devices/system/cpu/{}/cpufreq/scaling_cur_freq'.format(cpu)
233 return self.target.read_int(sysfile)
234
235 def set_frequency(self, cpu, frequency, exact=True):
236 """
237 Set's the minimum value for CPU frequency. Actual frequency will
238 depend on the Governor used and may vary during execution. The value should be
239 either an int or a string representing an integer.
240
241 If ``exact`` flag is set (the default), the Value must also be supported by
242 the device. The available frequencies can be obtained by calling
243 get_frequencies() or examining
244
245 /sys/devices/system/cpu/cpuX/cpufreq/scaling_frequencies
246
247 on the device (if it exists).
248
249 :raises: TargetError if the frequency is not supported by the CPU, or if, for
250 some reason, frequency could not be set.
251 :raises: ValueError if ``frequency`` is not an integer.
252
253 """
254 if isinstance(cpu, int):
255 cpu = 'cpu{}'.format(cpu)
256 try:
257 value = int(frequency)
258 if exact:
259 available_frequencies = self.list_frequencies(cpu)
260 if available_frequencies and value not in available_frequencies:
261 raise TargetError('Can\'t set {} frequency to {}\nmust be in {}'.format(cpu,
262 value,
263 available_frequencies))
264 if self.get_governor(cpu) != 'userspace':
265 raise TargetError('Can\'t set {} frequency; governor must be "userspace"'.format(cpu))
266 sysfile = '/sys/devices/system/cpu/{}/cpufreq/scaling_setspeed'.format(cpu)
267 self.target.write_value(sysfile, value, verify=False)
268 except ValueError:
269 raise ValueError('Frequency must be an integer; got: "{}"'.format(frequency))
270
271 def get_max_frequency(self, cpu):
272 """
273 Returns the max frequency currently set for the specified CPU.
274
275 Warning, this method does not check if the cpu is online or not. It will
276 try to read the maximum frequency and the following exception will be
277 raised ::
278
279 :raises: TargetError if for some reason the frequency could not be read.
280 """
281 if isinstance(cpu, int):
282 cpu = 'cpu{}'.format(cpu)
283 sysfile = '/sys/devices/system/cpu/{}/cpufreq/scaling_max_freq'.format(cpu)
284 return self.target.read_int(sysfile)
285
286 def set_max_frequency(self, cpu, frequency, exact=True):
287 """
288 Set's the minimum value for CPU frequency. Actual frequency will
289 depend on the Governor used and may vary during execution. The value should be
290 either an int or a string representing an integer. The Value must also be
291 supported by the device. The available frequencies can be obtained by calling
292 get_frequencies() or examining
293
294 /sys/devices/system/cpu/cpuX/cpufreq/scaling_frequencies
295
296 on the device.
297
298 :raises: TargetError if the frequency is not supported by the CPU, or if, for
299 some reason, frequency could not be set.
300 :raises: ValueError if ``frequency`` is not an integer.
301
302 """
303 if isinstance(cpu, int):
304 cpu = 'cpu{}'.format(cpu)
305 available_frequencies = self.list_frequencies(cpu)
306 try:
307 value = int(frequency)
308 if exact and available_frequencies and value not in available_frequencies:
309 raise TargetError('Can\'t set {} frequency to {}\nmust be in {}'.format(cpu,
310 value,
311 available_frequencies))
312 sysfile = '/sys/devices/system/cpu/{}/cpufreq/scaling_max_freq'.format(cpu)
313 self.target.write_value(sysfile, value)
314 except ValueError:
315 raise ValueError('Frequency must be an integer; got: "{}"'.format(frequency))