blob: c4bc14fc71628bec89ccf6acae04e5724b2b01b0 [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):
Patrick Bellasicbe80da2015-10-01 17:59:12 +010032
33 # x86 with Intel P-State driver
Patrick Bellasib19d76d2015-09-15 10:25:15 +010034 if target.abi == 'x86_64':
35 path = '/sys/devices/system/cpu/intel_pstate'
Patrick Bellasicbe80da2015-10-01 17:59:12 +010036 if target.file_exists(path):
37 return True
38
39 # Generic CPUFreq support (single policy)
40 path = '/sys/devices/system/cpu/cpufreq'
41 if target.file_exists(path):
42 return True
43
44 # Generic CPUFreq support (per CPU policy)
45 path = '/sys/devices/system/cpu/cpu0/cpufreq'
Sergei Trofimov4e6afe92015-10-09 09:30:04 +010046 return target.file_exists(path)
47
48 def __init__(self, target):
49 super(CpufreqModule, self).__init__(target)
50 self._governor_tunables = {}
51
52 @memoized
53 def list_governors(self, cpu):
54 """Returns a list of governors supported by the cpu."""
55 if isinstance(cpu, int):
56 cpu = 'cpu{}'.format(cpu)
57 sysfile = '/sys/devices/system/cpu/{}/cpufreq/scaling_available_governors'.format(cpu)
58 output = self.target.read_value(sysfile)
59 return output.strip().split()
60
61 def get_governor(self, cpu):
62 """Returns the governor currently set for the specified CPU."""
63 if isinstance(cpu, int):
64 cpu = 'cpu{}'.format(cpu)
65 sysfile = '/sys/devices/system/cpu/{}/cpufreq/scaling_governor'.format(cpu)
66 return self.target.read_value(sysfile)
67
68 def set_governor(self, cpu, governor, **kwargs):
69 """
70 Set the governor for the specified CPU.
71 See https://www.kernel.org/doc/Documentation/cpu-freq/governors.txt
72
73 :param cpu: The CPU for which the governor is to be set. This must be
74 the full name as it appears in sysfs, e.g. "cpu0".
75 :param governor: The name of the governor to be used. This must be
76 supported by the specific device.
77
78 Additional keyword arguments can be used to specify governor tunables for
79 governors that support them.
80
81 :note: On big.LITTLE all cores in a cluster must be using the same governor.
82 Setting the governor on any core in a cluster will also set it on all
83 other cores in that cluster.
84
85 :raises: TargetError if governor is not supported by the CPU, or if,
86 for some reason, the governor could not be set.
87
88 """
89 if isinstance(cpu, int):
90 cpu = 'cpu{}'.format(cpu)
91 supported = self.list_governors(cpu)
92 if governor not in supported:
93 raise TargetError('Governor {} not supported for cpu {}'.format(governor, cpu))
94 sysfile = '/sys/devices/system/cpu/{}/cpufreq/scaling_governor'.format(cpu)
95 self.target.write_value(sysfile, governor)
96 self.set_governor_tunables(cpu, governor, **kwargs)
97
98 def list_governor_tunables(self, cpu):
99 """Returns a list of tunables available for the governor on the specified CPU."""
100 if isinstance(cpu, int):
101 cpu = 'cpu{}'.format(cpu)
102 governor = self.get_governor(cpu)
103 if governor not in self._governor_tunables:
104 try:
105 tunables_path = '/sys/devices/system/cpu/{}/cpufreq/{}'.format(cpu, governor)
106 self._governor_tunables[governor] = self.target.list_directory(tunables_path)
107 except TargetError: # probably an older kernel
108 try:
109 tunables_path = '/sys/devices/system/cpu/cpufreq/{}'.format(governor)
110 self._governor_tunables[governor] = self.target.list_directory(tunables_path)
111 except TargetError: # governor does not support tunables
112 self._governor_tunables[governor] = []
113 return self._governor_tunables[governor]
114
115 def get_governor_tunables(self, cpu):
116 if isinstance(cpu, int):
117 cpu = 'cpu{}'.format(cpu)
118 governor = self.get_governor(cpu)
119 tunables = {}
120 for tunable in self.list_governor_tunables(cpu):
121 if tunable not in WRITE_ONLY_TUNABLES.get(governor, []):
122 try:
123 path = '/sys/devices/system/cpu/{}/cpufreq/{}/{}'.format(cpu, governor, tunable)
124 tunables[tunable] = self.target.read_value(path)
125 except TargetError: # May be an older kernel
126 path = '/sys/devices/system/cpu/cpufreq/{}/{}'.format(governor, tunable)
127 tunables[tunable] = self.target.read_value(path)
128 return tunables
129
130 def set_governor_tunables(self, cpu, governor=None, **kwargs):
131 """
132 Set tunables for the specified governor. Tunables should be specified as
133 keyword arguments. Which tunables and values are valid depends on the
134 governor.
135
Brendan Jackmana6fb5b52017-04-10 11:51:50 +0100136 :param cpu: The cpu for which the governor will be set. ``int`` or
Sergei Trofimov4e6afe92015-10-09 09:30:04 +0100137 full cpu name as it appears in sysfs, e.g. ``cpu0``.
138 :param governor: The name of the governor. Must be all lower case.
139
140 The rest should be keyword parameters mapping tunable name onto the value to
141 be set for it.
142
143 :raises: TargetError if governor specified is not a valid governor name, or if
144 a tunable specified is not valid for the governor, or if could not set
145 tunable.
146
147 """
148 if isinstance(cpu, int):
149 cpu = 'cpu{}'.format(cpu)
150 if governor is None:
151 governor = self.get_governor(cpu)
152 valid_tunables = self.list_governor_tunables(cpu)
153 for tunable, value in kwargs.iteritems():
154 if tunable in valid_tunables:
Brendan Jackman5c036ea2017-04-10 17:39:13 +0100155 path = '/sys/devices/system/cpu/{}/cpufreq/{}/{}'.format(cpu, governor, tunable)
Sergei Trofimov4e6afe92015-10-09 09:30:04 +0100156 try:
Sergei Trofimov4e6afe92015-10-09 09:30:04 +0100157 self.target.write_value(path, value)
Brendan Jackman5c036ea2017-04-10 17:39:13 +0100158 except TargetError:
159 if self.target.file_exists(path):
160 # File exists but we did something wrong
161 raise
162 # Expected file doesn't exist, try older sysfs layout.
Sergei Trofimov4e6afe92015-10-09 09:30:04 +0100163 path = '/sys/devices/system/cpu/cpufreq/{}/{}'.format(governor, tunable)
164 self.target.write_value(path, value)
165 else:
166 message = 'Unexpected tunable {} for governor {} on {}.\n'.format(tunable, governor, cpu)
167 message += 'Available tunables are: {}'.format(valid_tunables)
168 raise TargetError(message)
169
170 @memoized
171 def list_frequencies(self, cpu):
172 """Returns a list of frequencies supported by the cpu or an empty list
173 if not could be found."""
174 if isinstance(cpu, int):
175 cpu = 'cpu{}'.format(cpu)
176 try:
177 cmd = 'cat /sys/devices/system/cpu/{}/cpufreq/scaling_available_frequencies'.format(cpu)
178 output = self.target.execute(cmd)
179 available_frequencies = map(int, output.strip().split()) # pylint: disable=E1103
180 except TargetError:
181 # On some devices scaling_frequencies is not generated.
182 # http://adrynalyne-teachtofish.blogspot.co.uk/2011/11/how-to-enable-scalingavailablefrequenci.html
183 # Fall back to parsing stats/time_in_state
184 cmd = 'cat /sys/devices/system/cpu/{}/cpufreq/stats/time_in_state'.format(cpu)
185 out_iter = iter(self.target.execute(cmd).strip().split())
186 available_frequencies = map(int, reversed([f for f, _ in zip(out_iter, out_iter)]))
187 return available_frequencies
188
189 def get_min_frequency(self, cpu):
190 """
191 Returns the min frequency currently set for the specified CPU.
192
193 Warning, this method does not check if the cpu is online or not. It will
194 try to read the minimum frequency and the following exception will be
195 raised ::
196
197 :raises: TargetError if for some reason the frequency could not be read.
198
199 """
200 if isinstance(cpu, int):
201 cpu = 'cpu{}'.format(cpu)
202 sysfile = '/sys/devices/system/cpu/{}/cpufreq/scaling_min_freq'.format(cpu)
203 return self.target.read_int(sysfile)
204
205 def set_min_frequency(self, cpu, frequency, exact=True):
206 """
207 Set's the minimum value for CPU frequency. Actual frequency will
208 depend on the Governor used and may vary during execution. The value should be
209 either an int or a string representing an integer. The Value must also be
210 supported by the device. The available frequencies can be obtained by calling
211 get_frequencies() or examining
212
213 /sys/devices/system/cpu/cpuX/cpufreq/scaling_frequencies
214
215 on the device.
216
217 :raises: TargetError if the frequency is not supported by the CPU, or if, for
218 some reason, frequency could not be set.
219 :raises: ValueError if ``frequency`` is not an integer.
220
221 """
222 if isinstance(cpu, int):
223 cpu = 'cpu{}'.format(cpu)
224 available_frequencies = self.list_frequencies(cpu)
225 try:
226 value = int(frequency)
227 if exact and available_frequencies and value not in available_frequencies:
228 raise TargetError('Can\'t set {} frequency to {}\nmust be in {}'.format(cpu,
229 value,
230 available_frequencies))
231 sysfile = '/sys/devices/system/cpu/{}/cpufreq/scaling_min_freq'.format(cpu)
232 self.target.write_value(sysfile, value)
233 except ValueError:
234 raise ValueError('Frequency must be an integer; got: "{}"'.format(frequency))
235
236 def get_frequency(self, cpu):
237 """
238 Returns the current frequency currently set for the specified CPU.
239
240 Warning, this method does not check if the cpu is online or not. It will
241 try to read the current frequency and the following exception will be
242 raised ::
243
244 :raises: TargetError if for some reason the frequency could not be read.
245
246 """
247 if isinstance(cpu, int):
248 cpu = 'cpu{}'.format(cpu)
249 sysfile = '/sys/devices/system/cpu/{}/cpufreq/scaling_cur_freq'.format(cpu)
250 return self.target.read_int(sysfile)
251
252 def set_frequency(self, cpu, frequency, exact=True):
253 """
254 Set's the minimum value for CPU frequency. Actual frequency will
255 depend on the Governor used and may vary during execution. The value should be
256 either an int or a string representing an integer.
257
258 If ``exact`` flag is set (the default), the Value must also be supported by
259 the device. The available frequencies can be obtained by calling
260 get_frequencies() or examining
261
262 /sys/devices/system/cpu/cpuX/cpufreq/scaling_frequencies
263
264 on the device (if it exists).
265
266 :raises: TargetError if the frequency is not supported by the CPU, or if, for
267 some reason, frequency could not be set.
268 :raises: ValueError if ``frequency`` is not an integer.
269
270 """
271 if isinstance(cpu, int):
272 cpu = 'cpu{}'.format(cpu)
273 try:
274 value = int(frequency)
275 if exact:
276 available_frequencies = self.list_frequencies(cpu)
277 if available_frequencies and value not in available_frequencies:
278 raise TargetError('Can\'t set {} frequency to {}\nmust be in {}'.format(cpu,
279 value,
280 available_frequencies))
281 if self.get_governor(cpu) != 'userspace':
282 raise TargetError('Can\'t set {} frequency; governor must be "userspace"'.format(cpu))
283 sysfile = '/sys/devices/system/cpu/{}/cpufreq/scaling_setspeed'.format(cpu)
284 self.target.write_value(sysfile, value, verify=False)
285 except ValueError:
286 raise ValueError('Frequency must be an integer; got: "{}"'.format(frequency))
287
288 def get_max_frequency(self, cpu):
289 """
290 Returns the max frequency currently set for the specified CPU.
291
292 Warning, this method does not check if the cpu is online or not. It will
293 try to read the maximum frequency and the following exception will be
294 raised ::
295
296 :raises: TargetError if for some reason the frequency could not be read.
297 """
298 if isinstance(cpu, int):
299 cpu = 'cpu{}'.format(cpu)
300 sysfile = '/sys/devices/system/cpu/{}/cpufreq/scaling_max_freq'.format(cpu)
301 return self.target.read_int(sysfile)
302
303 def set_max_frequency(self, cpu, frequency, exact=True):
304 """
305 Set's the minimum value for CPU frequency. Actual frequency will
306 depend on the Governor used and may vary during execution. The value should be
307 either an int or a string representing an integer. The Value must also be
308 supported by the device. The available frequencies can be obtained by calling
309 get_frequencies() or examining
310
311 /sys/devices/system/cpu/cpuX/cpufreq/scaling_frequencies
312
313 on the device.
314
315 :raises: TargetError if the frequency is not supported by the CPU, or if, for
316 some reason, frequency could not be set.
317 :raises: ValueError if ``frequency`` is not an integer.
318
319 """
320 if isinstance(cpu, int):
321 cpu = 'cpu{}'.format(cpu)
322 available_frequencies = self.list_frequencies(cpu)
323 try:
324 value = int(frequency)
325 if exact and available_frequencies and value not in available_frequencies:
326 raise TargetError('Can\'t set {} frequency to {}\nmust be in {}'.format(cpu,
327 value,
328 available_frequencies))
329 sysfile = '/sys/devices/system/cpu/{}/cpufreq/scaling_max_freq'.format(cpu)
330 self.target.write_value(sysfile, value)
331 except ValueError:
332 raise ValueError('Frequency must be an integer; got: "{}"'.format(frequency))
Patrick Bellasi4dcb4972015-09-16 18:03:04 +0100333
334 def set_governor_for_cpus(self, cpus, governor, **kwargs):
335 """
336 Set the governor for the specified list of CPUs.
337 See https://www.kernel.org/doc/Documentation/cpu-freq/governors.txt
338
339 :param cpus: The list of CPU for which the governor is to be set.
340 """
Marc Bonnicib444ae62017-03-17 15:37:45 +0000341 for cpu in cpus:
342 self.set_governor(cpu, governor, **kwargs)
Patrick Bellasi4dcb4972015-09-16 18:03:04 +0100343
344 def set_frequency_for_cpus(self, cpus, freq, exact=False):
345 """
346 Set the frequency for the specified list of CPUs.
347 See https://www.kernel.org/doc/Documentation/cpu-freq/governors.txt
348
349 :param cpus: The list of CPU for which the frequency has to be set.
350 """
Marc Bonnicib444ae62017-03-17 15:37:45 +0000351 for cpu in cpus:
Patrick Bellasi4dcb4972015-09-16 18:03:04 +0100352 self.set_frequency(cpu, freq, exact)
353
Patrick Bellasicf761312015-11-27 16:38:30 +0000354 def set_all_frequencies(self, freq):
355 """
356 Set the specified (minimum) frequency for all the (online) CPUs
357 """
358 return self.target._execute_util(
359 'cpufreq_set_all_frequencies {}'.format(freq),
360 as_root=True)
361
Patrick Bellasi51b7f012015-11-27 16:40:58 +0000362 def get_all_frequencies(self):
363 """
364 Get the current frequency for all the (online) CPUs
365 """
366 output = self.target._execute_util(
367 'cpufreq_get_all_frequencies', as_root=True)
368 frequencies = {}
369 for x in output.splitlines():
370 kv = x.split(' ')
371 if kv[0] == '':
372 break
373 frequencies[kv[0]] = kv[1]
374 return frequencies
Patrick Bellasib598b382015-09-17 17:39:32 +0100375
376 def set_all_governors(self, governor):
Patrick Bellasicf761312015-11-27 16:38:30 +0000377 """
378 Set the specified governor for all the (online) CPUs
379 """
Brendan Jackmanaf4214c2016-10-10 12:27:34 +0100380 try:
381 return self.target._execute_util(
Patrick Bellasicf761312015-11-27 16:38:30 +0000382 'cpufreq_set_all_governors {}'.format(governor),
383 as_root=True)
Brendan Jackmanaf4214c2016-10-10 12:27:34 +0100384 except TargetError as e:
Brendan Jackman0cac92a2017-06-06 14:56:57 +0100385 if ("echo: I/O error" in str(e) or
386 "write error: Invalid argument" in str(e)):
387
Brendan Jackmanaf4214c2016-10-10 12:27:34 +0100388 cpus_unsupported = [c for c in self.target.list_online_cpus()
389 if governor not in self.list_governors(c)]
390 raise TargetError("Governor {} unsupported for CPUs {}".format(
391 governor, cpus_unsupported))
392 else:
393 raise
Patrick Bellasicf761312015-11-27 16:38:30 +0000394
Patrick Bellasi51b7f012015-11-27 16:40:58 +0000395 def get_all_governors(self):
396 """
397 Get the current governor for all the (online) CPUs
398 """
399 output = self.target._execute_util(
400 'cpufreq_get_all_governors', as_root=True)
401 governors = {}
402 for x in output.splitlines():
403 kv = x.split(' ')
404 if kv[0] == '':
405 break
406 governors[kv[0]] = kv[1]
407 return governors
Patrick Bellasib598b382015-09-17 17:39:32 +0100408
Patrick Bellasi701e6ad2015-11-16 17:40:46 +0000409 def trace_frequencies(self):
410 """
411 Report current frequencies on trace file
412 """
Patrick Bellasicf761312015-11-27 16:38:30 +0000413 return self.target._execute_util('cpufreq_trace_all_frequencies', as_root=True)
Patrick Bellasi701e6ad2015-11-16 17:40:46 +0000414
Sergei Trofimov36aa3af2017-07-26 13:59:55 +0100415 def get_affected_cpus(self, cpu):
416 """
417 Get the CPUs that share a frequency domain with the given CPU
418 """
419 if isinstance(cpu, int):
420 cpu = 'cpu{}'.format(cpu)
421
422 sysfile = '/sys/devices/system/cpu/{}/cpufreq/affected_cpus'.format(cpu)
423
424 return [int(c) for c in self.target.read_value(sysfile).split()]
425
Brendan Jackmanf1b4bf22016-10-10 14:05:09 +0100426 @memoized
427 def get_domain_cpus(self, cpu):
428 """
429 Get the CPUs that share a frequency domain with the given CPU
430 """
431 if isinstance(cpu, int):
432 cpu = 'cpu{}'.format(cpu)
433
Sergei Trofimov36aa3af2017-07-26 13:59:55 +0100434 sysfile = '/sys/devices/system/cpu/{}/cpufreq/related_cpus'.format(cpu)
Brendan Jackmanf1b4bf22016-10-10 14:05:09 +0100435
436 return [int(c) for c in self.target.read_value(sysfile).split()]
Brendan Jackmandc32fa92017-05-17 10:43:34 +0100437
438 def iter_domains(self):
439 """
440 Iterate over the frequency domains in the system
441 """
442 cpus = set(range(self.target.number_of_cpus))
443 while cpus:
444 cpu = iter(cpus).next()
445 domain = self.target.cpufreq.get_domain_cpus(cpu)
446 yield domain
447 cpus = cpus.difference(domain)