Sergei Trofimov | 4e6afe9 | 2015-10-09 09:30:04 +0100 | [diff] [blame^] | 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 | # |
| 15 | from devlib.module import Module |
| 16 | from devlib.exception import TargetError |
| 17 | from devlib.utils.misc import memoized |
| 18 | |
| 19 | |
| 20 | # a dict of governor name and a list of it tunables that can't be read |
| 21 | WRITE_ONLY_TUNABLES = { |
| 22 | 'interactive': ['boostpulse'] |
| 23 | } |
| 24 | |
| 25 | |
| 26 | class 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)) |