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#
15import json
16from devlib.module import Module
17from devlib.exception import TargetError
18from devlib.utils.misc import memoized
19
20
21# a dict of governor name and a list of it tunables that can't be read
22WRITE_ONLY_TUNABLES = {
23    'interactive': ['boostpulse']
24}
25
26
27class CpufreqModule(Module):
28
29    name = 'cpufreq'
30
31    @staticmethod
32    def probe(target):
33
34        # x86 with Intel P-State driver
35        if target.abi == 'x86_64':
36            path = '/sys/devices/system/cpu/intel_pstate'
37            if target.file_exists(path):
38                return True
39
40        # Generic CPUFreq support (single policy)
41        path = '/sys/devices/system/cpu/cpufreq'
42        if target.file_exists(path):
43            return True
44
45        # Generic CPUFreq support (per CPU policy)
46        path = '/sys/devices/system/cpu/cpu0/cpufreq'
47        return target.file_exists(path)
48
49    def __init__(self, target):
50        super(CpufreqModule, self).__init__(target)
51        self._governor_tunables = {}
52
53    @memoized
54    def list_governors(self, cpu):
55        """Returns a list of governors supported by the cpu."""
56        if isinstance(cpu, int):
57            cpu = 'cpu{}'.format(cpu)
58        sysfile = '/sys/devices/system/cpu/{}/cpufreq/scaling_available_governors'.format(cpu)
59        output = self.target.read_value(sysfile)
60        return output.strip().split()
61
62    def get_governor(self, cpu):
63        """Returns the governor currently set for the specified CPU."""
64        if isinstance(cpu, int):
65            cpu = 'cpu{}'.format(cpu)
66        sysfile = '/sys/devices/system/cpu/{}/cpufreq/scaling_governor'.format(cpu)
67        return self.target.read_value(sysfile)
68
69    def set_governor(self, cpu, governor, **kwargs):
70        """
71        Set the governor for the specified CPU.
72        See https://www.kernel.org/doc/Documentation/cpu-freq/governors.txt
73
74        :param cpu: The CPU for which the governor is to be set. This must be
75                    the full name as it appears in sysfs, e.g. "cpu0".
76        :param governor: The name of the governor to be used. This must be
77                         supported by the specific device.
78
79        Additional keyword arguments can be used to specify governor tunables for
80        governors that support them.
81
82        :note: On big.LITTLE all cores in a cluster must be using the same governor.
83               Setting the governor on any core in a cluster will also set it on all
84               other cores in that cluster.
85
86        :raises: TargetError if governor is not supported by the CPU, or if,
87                 for some reason, the governor could not be set.
88
89        """
90        if isinstance(cpu, int):
91            cpu = 'cpu{}'.format(cpu)
92        supported = self.list_governors(cpu)
93        if governor not in supported:
94            raise TargetError('Governor {} not supported for cpu {}'.format(governor, cpu))
95        sysfile = '/sys/devices/system/cpu/{}/cpufreq/scaling_governor'.format(cpu)
96        self.target.write_value(sysfile, governor)
97        self.set_governor_tunables(cpu, governor, **kwargs)
98
99    def list_governor_tunables(self, cpu):
100        """Returns a list of tunables available for the governor on the specified CPU."""
101        if isinstance(cpu, int):
102            cpu = 'cpu{}'.format(cpu)
103        governor = self.get_governor(cpu)
104        if governor not in self._governor_tunables:
105            try:
106                tunables_path = '/sys/devices/system/cpu/{}/cpufreq/{}'.format(cpu, governor)
107                self._governor_tunables[governor] = self.target.list_directory(tunables_path)
108            except TargetError:  # probably an older kernel
109                try:
110                    tunables_path = '/sys/devices/system/cpu/cpufreq/{}'.format(governor)
111                    self._governor_tunables[governor] = self.target.list_directory(tunables_path)
112                except TargetError:  # governor does not support tunables
113                    self._governor_tunables[governor] = []
114        return self._governor_tunables[governor]
115
116    def get_governor_tunables(self, cpu):
117        if isinstance(cpu, int):
118            cpu = 'cpu{}'.format(cpu)
119        governor = self.get_governor(cpu)
120        tunables = {}
121        for tunable in self.list_governor_tunables(cpu):
122            if tunable not in WRITE_ONLY_TUNABLES.get(governor, []):
123                try:
124                    path = '/sys/devices/system/cpu/{}/cpufreq/{}/{}'.format(cpu, governor, tunable)
125                    tunables[tunable] = self.target.read_value(path)
126                except TargetError:  # May be an older kernel
127                    path = '/sys/devices/system/cpu/cpufreq/{}/{}'.format(governor, tunable)
128                    tunables[tunable] = self.target.read_value(path)
129        return tunables
130
131    def set_governor_tunables(self, cpu, governor=None, **kwargs):
132        """
133        Set tunables for the specified governor. Tunables should be specified as
134        keyword arguments. Which tunables and values are valid depends on the
135        governor.
136
137        :param cpu: The cpu for which the governor will be set. ``int`` or
138                    full cpu name as it appears in sysfs, e.g. ``cpu0``.
139        :param governor: The name of the governor. Must be all lower case.
140
141        The rest should be keyword parameters mapping tunable name onto the value to
142        be set for it.
143
144        :raises: TargetError if governor specified is not a valid governor name, or if
145                 a tunable specified is not valid for the governor, or if could not set
146                 tunable.
147
148        """
149        if isinstance(cpu, int):
150            cpu = 'cpu{}'.format(cpu)
151        if governor is None:
152            governor = self.get_governor(cpu)
153        valid_tunables = self.list_governor_tunables(cpu)
154        for tunable, value in kwargs.iteritems():
155            if tunable in valid_tunables:
156                path = '/sys/devices/system/cpu/{}/cpufreq/{}/{}'.format(cpu, governor, tunable)
157                try:
158                    self.target.write_value(path, value)
159                except TargetError:
160                    if self.target.file_exists(path):
161                        # File exists but we did something wrong
162                        raise
163                    # Expected file doesn't exist, try older sysfs layout.
164                    path = '/sys/devices/system/cpu/cpufreq/{}/{}'.format(governor, tunable)
165                    self.target.write_value(path, value)
166            else:
167                message = 'Unexpected tunable {} for governor {} on {}.\n'.format(tunable, governor, cpu)
168                message += 'Available tunables are: {}'.format(valid_tunables)
169                raise TargetError(message)
170
171    @memoized
172    def list_frequencies(self, cpu):
173        """Returns a list of frequencies supported by the cpu or an empty list
174        if not could be found."""
175        if isinstance(cpu, int):
176            cpu = 'cpu{}'.format(cpu)
177        try:
178            cmd = 'cat /sys/devices/system/cpu/{}/cpufreq/scaling_available_frequencies'.format(cpu)
179            output = self.target.execute(cmd)
180            available_frequencies = map(int, output.strip().split())  # pylint: disable=E1103
181        except TargetError:
182            # On some devices scaling_frequencies  is not generated.
183            # http://adrynalyne-teachtofish.blogspot.co.uk/2011/11/how-to-enable-scalingavailablefrequenci.html
184            # Fall back to parsing stats/time_in_state
185            cmd = 'cat /sys/devices/system/cpu/{}/cpufreq/stats/time_in_state'.format(cpu)
186            out_iter = iter(self.target.execute(cmd).strip().split())
187            available_frequencies = map(int, reversed([f for f, _ in zip(out_iter, out_iter)]))
188        return available_frequencies
189
190    def get_min_frequency(self, cpu):
191        """
192        Returns the min frequency currently set for the specified CPU.
193
194        Warning, this method does not check if the cpu is online or not. It will
195        try to read the minimum frequency and the following exception will be
196        raised ::
197
198        :raises: TargetError if for some reason the frequency could not be read.
199
200        """
201        if isinstance(cpu, int):
202            cpu = 'cpu{}'.format(cpu)
203        sysfile = '/sys/devices/system/cpu/{}/cpufreq/scaling_min_freq'.format(cpu)
204        return self.target.read_int(sysfile)
205
206    def set_min_frequency(self, cpu, frequency, exact=True):
207        """
208        Set's the minimum value for CPU frequency. Actual frequency will
209        depend on the Governor used and may vary during execution. The value should be
210        either an int or a string representing an integer. The Value must also be
211        supported by the device. The available frequencies can be obtained by calling
212        get_frequencies() or examining
213
214        /sys/devices/system/cpu/cpuX/cpufreq/scaling_frequencies
215
216        on the device.
217
218        :raises: TargetError if the frequency is not supported by the CPU, or if, for
219                 some reason, frequency could not be set.
220        :raises: ValueError if ``frequency`` is not an integer.
221
222        """
223        if isinstance(cpu, int):
224            cpu = 'cpu{}'.format(cpu)
225        available_frequencies = self.list_frequencies(cpu)
226        try:
227            value = int(frequency)
228            if exact and available_frequencies and value not in available_frequencies:
229                raise TargetError('Can\'t set {} frequency to {}\nmust be in {}'.format(cpu,
230                                                                                        value,
231                                                                                        available_frequencies))
232            sysfile = '/sys/devices/system/cpu/{}/cpufreq/scaling_min_freq'.format(cpu)
233            self.target.write_value(sysfile, value)
234        except ValueError:
235            raise ValueError('Frequency must be an integer; got: "{}"'.format(frequency))
236
237    def get_frequency(self, cpu):
238        """
239        Returns the current frequency currently set for the specified CPU.
240
241        Warning, this method does not check if the cpu is online or not. It will
242        try to read the current frequency and the following exception will be
243        raised ::
244
245        :raises: TargetError if for some reason the frequency could not be read.
246
247        """
248        if isinstance(cpu, int):
249            cpu = 'cpu{}'.format(cpu)
250        sysfile = '/sys/devices/system/cpu/{}/cpufreq/scaling_cur_freq'.format(cpu)
251        return self.target.read_int(sysfile)
252
253    def set_frequency(self, cpu, frequency, exact=True):
254        """
255        Set's the minimum value for CPU frequency. Actual frequency will
256        depend on the Governor used and may vary during execution. The value should be
257        either an int or a string representing an integer.
258
259        If ``exact`` flag is set (the default), the Value must also be supported by
260        the device. The available frequencies can be obtained by calling
261        get_frequencies() or examining
262
263        /sys/devices/system/cpu/cpuX/cpufreq/scaling_frequencies
264
265        on the device (if it exists).
266
267        :raises: TargetError if the frequency is not supported by the CPU, or if, for
268                 some reason, frequency could not be set.
269        :raises: ValueError if ``frequency`` is not an integer.
270
271        """
272        if isinstance(cpu, int):
273            cpu = 'cpu{}'.format(cpu)
274        try:
275            value = int(frequency)
276            if exact:
277                available_frequencies = self.list_frequencies(cpu)
278                if available_frequencies and value not in available_frequencies:
279                    raise TargetError('Can\'t set {} frequency to {}\nmust be in {}'.format(cpu,
280                                                                                            value,
281                                                                                            available_frequencies))
282            if self.get_governor(cpu) != 'userspace':
283                raise TargetError('Can\'t set {} frequency; governor must be "userspace"'.format(cpu))
284            sysfile = '/sys/devices/system/cpu/{}/cpufreq/scaling_setspeed'.format(cpu)
285            self.target.write_value(sysfile, value, verify=False)
286        except ValueError:
287            raise ValueError('Frequency must be an integer; got: "{}"'.format(frequency))
288
289    def get_max_frequency(self, cpu):
290        """
291        Returns the max frequency currently set for the specified CPU.
292
293        Warning, this method does not check if the cpu is online or not. It will
294        try to read the maximum frequency and the following exception will be
295        raised ::
296
297        :raises: TargetError if for some reason the frequency could not be read.
298        """
299        if isinstance(cpu, int):
300            cpu = 'cpu{}'.format(cpu)
301        sysfile = '/sys/devices/system/cpu/{}/cpufreq/scaling_max_freq'.format(cpu)
302        return self.target.read_int(sysfile)
303
304    def set_max_frequency(self, cpu, frequency, exact=True):
305        """
306        Set's the minimum value for CPU frequency. Actual frequency will
307        depend on the Governor used and may vary during execution. The value should be
308        either an int or a string representing an integer. The Value must also be
309        supported by the device. The available frequencies can be obtained by calling
310        get_frequencies() or examining
311
312        /sys/devices/system/cpu/cpuX/cpufreq/scaling_frequencies
313
314        on the device.
315
316        :raises: TargetError if the frequency is not supported by the CPU, or if, for
317                 some reason, frequency could not be set.
318        :raises: ValueError if ``frequency`` is not an integer.
319
320        """
321        if isinstance(cpu, int):
322            cpu = 'cpu{}'.format(cpu)
323        available_frequencies = self.list_frequencies(cpu)
324        try:
325            value = int(frequency)
326            if exact and available_frequencies and value not in available_frequencies:
327                raise TargetError('Can\'t set {} frequency to {}\nmust be in {}'.format(cpu,
328                                                                                        value,
329                                                                                        available_frequencies))
330            sysfile = '/sys/devices/system/cpu/{}/cpufreq/scaling_max_freq'.format(cpu)
331            self.target.write_value(sysfile, value)
332        except ValueError:
333            raise ValueError('Frequency must be an integer; got: "{}"'.format(frequency))
334
335    def set_governor_for_cpus(self, cpus, governor, **kwargs):
336        """
337        Set the governor for the specified list of CPUs.
338        See https://www.kernel.org/doc/Documentation/cpu-freq/governors.txt
339
340        :param cpus: The list of CPU for which the governor is to be set.
341        """
342        for cpu in cpus:
343            self.set_governor(cpu, governor, **kwargs)
344
345    def set_frequency_for_cpus(self, cpus, freq, exact=False):
346        """
347        Set the frequency for the specified list of CPUs.
348        See https://www.kernel.org/doc/Documentation/cpu-freq/governors.txt
349
350        :param cpus: The list of CPU for which the frequency has to be set.
351        """
352        for cpu in cpus:
353            self.set_frequency(cpu, freq, exact)
354
355    def set_all_frequencies(self, freq):
356        """
357        Set the specified (minimum) frequency for all the (online) CPUs
358        """
359        return self.target._execute_util(
360                'cpufreq_set_all_frequencies {}'.format(freq),
361                as_root=True)
362
363    def get_all_frequencies(self):
364        """
365        Get the current frequency for all the (online) CPUs
366        """
367        output = self.target._execute_util(
368                'cpufreq_get_all_frequencies', as_root=True)
369        frequencies = {}
370        for x in output.splitlines():
371            kv = x.split(' ')
372            if kv[0] == '':
373                break
374            frequencies[kv[0]] = kv[1]
375        return frequencies
376
377    def set_all_governors(self, governor):
378        """
379        Set the specified governor for all the (online) CPUs
380        """
381        try:
382            return self.target._execute_util(
383                'cpufreq_set_all_governors {}'.format(governor),
384                as_root=True)
385        except TargetError as e:
386            if ("echo: I/O error" in str(e) or
387                "write error: Invalid argument" in str(e)):
388
389                cpus_unsupported = [c for c in self.target.list_online_cpus()
390                                    if governor not in self.list_governors(c)]
391                raise TargetError("Governor {} unsupported for CPUs {}".format(
392                    governor, cpus_unsupported))
393            else:
394                raise
395
396    def get_all_governors(self):
397        """
398        Get the current governor for all the (online) CPUs
399        """
400        output = self.target._execute_util(
401                'cpufreq_get_all_governors', as_root=True)
402        governors = {}
403        for x in output.splitlines():
404            kv = x.split(' ')
405            if kv[0] == '':
406                break
407            governors[kv[0]] = kv[1]
408        return governors
409
410    def trace_frequencies(self):
411        """
412        Report current frequencies on trace file
413        """
414        return self.target._execute_util('cpufreq_trace_all_frequencies', as_root=True)
415
416    def get_affected_cpus(self, cpu):
417        """
418        Get the online 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    @memoized
428    def get_related_cpus(self, cpu):
429        """
430        Get the CPUs that share a frequency domain with the given CPU
431        """
432        if isinstance(cpu, int):
433            cpu = 'cpu{}'.format(cpu)
434
435        sysfile = '/sys/devices/system/cpu/{}/cpufreq/related_cpus'.format(cpu)
436
437        return [int(c) for c in self.target.read_value(sysfile).split()]
438
439    def iter_domains(self):
440        """
441        Iterate over the frequency domains in the system
442        """
443        cpus = set(range(self.target.number_of_cpus))
444        while cpus:
445            cpu = iter(cpus).next()
446            domain = self.target.cpufreq.get_related_cpus(cpu)
447            yield domain
448            cpus = cpus.difference(domain)
449
450    def get_time_in_state(self, clusters):
451        """
452        Gets the time at each frequency on each cluster
453        :param clusters: A list of clusters on the device. Each cluster is a
454                    list of cpus on that cluster.
455        """
456        time_in_state_by_cluster = {}
457
458        for i, cluster in enumerate(clusters):
459            frequencies = self.list_frequencies(cluster[0])
460            time_in_state = dict((str(freq), 0) for freq in frequencies)
461
462            for cpu in cluster:
463                stats = self.target.execute('cat '\
464                        '/sys/devices/system/cpu/cpu{}/cpufreq/stats'
465                        '/time_in_state'.format(cpu))
466                for entry in stats.split('\n'):
467                    if len(entry) > 0:
468                        freq, time = entry.split(' ')
469                        time_in_state[freq] += int(time)
470
471            time_in_state_by_cluster[str(i)] = time_in_state
472
473        return time_in_state_by_cluster
474
475    def dump_time_in_state_delta(self, start, clusters, dump_file):
476        """
477        Dumps the time between the stop and start by cluster by frequency
478        :param start: The inital output from a call to cpufreq.get_time_in_state
479        :param clusters: A list of clusters on the device. Each cluster is a
480                    list of cpus on that cluster.
481        :param dump_file: A file to dump the delta time_in_state_delta to.
482        """
483        stop = self.get_time_in_state(clusters)
484
485        time_in_state_delta = {}
486
487        for cl in start:
488            time_in_state_delta[cl] = {}
489
490            for freq in start[cl].keys():
491                time_in_start = start[cl][freq]
492                time_in_stop = stop[cl][freq]
493                time_in_state_delta[cl][freq] = time_in_stop - time_in_start
494
495        output = {'time_delta' : time_in_state_delta,
496                'clusters' : {str(i) : [str(c) for c in cl]
497                        for i, cl in enumerate(clusters)}}
498
499        with open(dump_file, 'w') as dfile:
500            json.dump(output, dfile, indent=4, sort_keys=True)
501