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