• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1#    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
33        # x86 with Intel P-State driver
34        if target.abi == 'x86_64':
35            path = '/sys/devices/system/cpu/intel_pstate'
36            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'
46        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
136        :param cpu: The cpu for which the governor will be set. ``int`` or
137                    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:
155                path = '/sys/devices/system/cpu/{}/cpufreq/{}/{}'.format(cpu, governor, tunable)
156                try:
157                    self.target.write_value(path, value)
158                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.
163                    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))
333
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        """
341        for cpu in cpus:
342            self.set_governor(cpu, governor, **kwargs)
343
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        """
351        for cpu in cpus:
352            self.set_frequency(cpu, freq, exact)
353
354    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
362    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
375
376    def set_all_governors(self, governor):
377        """
378        Set the specified governor for all the (online) CPUs
379        """
380        try:
381            return self.target._execute_util(
382                'cpufreq_set_all_governors {}'.format(governor),
383                as_root=True)
384        except TargetError as e:
385            if ("echo: I/O error" in str(e) or
386                "write error: Invalid argument" in str(e)):
387
388                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
394
395    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
408
409    def trace_frequencies(self):
410        """
411        Report current frequencies on trace file
412        """
413        return self.target._execute_util('cpufreq_trace_all_frequencies', as_root=True)
414
415    @memoized
416    def get_domain_cpus(self, cpu):
417        """
418        Get the CPUs that share a frequency domain with the given CPU
419        """
420        if isinstance(cpu, int):
421            cpu = 'cpu{}'.format(cpu)
422
423        sysfile = '/sys/devices/system/cpu/{}/cpufreq/affected_cpus'.format(cpu)
424
425        return [int(c) for c in self.target.read_value(sysfile).split()]
426
427    def iter_domains(self):
428        """
429        Iterate over the frequency domains in the system
430        """
431        cpus = set(range(self.target.number_of_cpus))
432        while cpus:
433            cpu = iter(cpus).next()
434            domain = self.target.cpufreq.get_domain_cpus(cpu)
435            yield domain
436            cpus = cpus.difference(domain)
437