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): |
Patrick Bellasi | cbe80da | 2015-10-01 17:59:12 +0100 | [diff] [blame] | 32 | |
| 33 | # x86 with Intel P-State driver |
Patrick Bellasi | b19d76d | 2015-09-15 10:25:15 +0100 | [diff] [blame] | 34 | if target.abi == 'x86_64': |
| 35 | path = '/sys/devices/system/cpu/intel_pstate' |
Patrick Bellasi | cbe80da | 2015-10-01 17:59:12 +0100 | [diff] [blame] | 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' |
Sergei Trofimov | 4e6afe9 | 2015-10-09 09:30:04 +0100 | [diff] [blame] | 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 | |
Brendan Jackman | a6fb5b5 | 2017-04-10 11:51:50 +0100 | [diff] [blame] | 136 | :param cpu: The cpu for which the governor will be set. ``int`` or |
Sergei Trofimov | 4e6afe9 | 2015-10-09 09:30:04 +0100 | [diff] [blame] | 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: |
Brendan Jackman | 5c036ea | 2017-04-10 17:39:13 +0100 | [diff] [blame] | 155 | path = '/sys/devices/system/cpu/{}/cpufreq/{}/{}'.format(cpu, governor, tunable) |
Sergei Trofimov | 4e6afe9 | 2015-10-09 09:30:04 +0100 | [diff] [blame] | 156 | try: |
Sergei Trofimov | 4e6afe9 | 2015-10-09 09:30:04 +0100 | [diff] [blame] | 157 | self.target.write_value(path, value) |
Brendan Jackman | 5c036ea | 2017-04-10 17:39:13 +0100 | [diff] [blame] | 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. |
Sergei Trofimov | 4e6afe9 | 2015-10-09 09:30:04 +0100 | [diff] [blame] | 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)) |
Patrick Bellasi | 4dcb497 | 2015-09-16 18:03:04 +0100 | [diff] [blame] | 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 | """ |
Marc Bonnici | b444ae6 | 2017-03-17 15:37:45 +0000 | [diff] [blame] | 341 | for cpu in cpus: |
| 342 | self.set_governor(cpu, governor, **kwargs) |
Patrick Bellasi | 4dcb497 | 2015-09-16 18:03:04 +0100 | [diff] [blame] | 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 | """ |
Marc Bonnici | b444ae6 | 2017-03-17 15:37:45 +0000 | [diff] [blame] | 351 | for cpu in cpus: |
Patrick Bellasi | 4dcb497 | 2015-09-16 18:03:04 +0100 | [diff] [blame] | 352 | self.set_frequency(cpu, freq, exact) |
| 353 | |
Patrick Bellasi | cf76131 | 2015-11-27 16:38:30 +0000 | [diff] [blame] | 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 | |
Patrick Bellasi | 51b7f01 | 2015-11-27 16:40:58 +0000 | [diff] [blame] | 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 |
Patrick Bellasi | b598b38 | 2015-09-17 17:39:32 +0100 | [diff] [blame] | 375 | |
| 376 | def set_all_governors(self, governor): |
Patrick Bellasi | cf76131 | 2015-11-27 16:38:30 +0000 | [diff] [blame] | 377 | """ |
| 378 | Set the specified governor for all the (online) CPUs |
| 379 | """ |
Brendan Jackman | af4214c | 2016-10-10 12:27:34 +0100 | [diff] [blame] | 380 | try: |
| 381 | return self.target._execute_util( |
Patrick Bellasi | cf76131 | 2015-11-27 16:38:30 +0000 | [diff] [blame] | 382 | 'cpufreq_set_all_governors {}'.format(governor), |
| 383 | as_root=True) |
Brendan Jackman | af4214c | 2016-10-10 12:27:34 +0100 | [diff] [blame] | 384 | except TargetError as e: |
Brendan Jackman | 0cac92a | 2017-06-06 14:56:57 +0100 | [diff] [blame] | 385 | if ("echo: I/O error" in str(e) or |
| 386 | "write error: Invalid argument" in str(e)): |
| 387 | |
Brendan Jackman | af4214c | 2016-10-10 12:27:34 +0100 | [diff] [blame] | 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 |
Patrick Bellasi | cf76131 | 2015-11-27 16:38:30 +0000 | [diff] [blame] | 394 | |
Patrick Bellasi | 51b7f01 | 2015-11-27 16:40:58 +0000 | [diff] [blame] | 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 |
Patrick Bellasi | b598b38 | 2015-09-17 17:39:32 +0100 | [diff] [blame] | 408 | |
Patrick Bellasi | 701e6ad | 2015-11-16 17:40:46 +0000 | [diff] [blame] | 409 | def trace_frequencies(self): |
| 410 | """ |
| 411 | Report current frequencies on trace file |
| 412 | """ |
Patrick Bellasi | cf76131 | 2015-11-27 16:38:30 +0000 | [diff] [blame] | 413 | return self.target._execute_util('cpufreq_trace_all_frequencies', as_root=True) |
Patrick Bellasi | 701e6ad | 2015-11-16 17:40:46 +0000 | [diff] [blame] | 414 | |
Sergei Trofimov | 36aa3af | 2017-07-26 13:59:55 +0100 | [diff] [blame] | 415 | def get_affected_cpus(self, cpu): |
| 416 | """ |
Sergei Trofimov | e206e9b | 2017-07-26 14:20:58 +0100 | [diff] [blame] | 417 | Get the online CPUs that share a frequency domain with the given CPU |
Sergei Trofimov | 36aa3af | 2017-07-26 13:59:55 +0100 | [diff] [blame] | 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 Jackman | f1b4bf2 | 2016-10-10 14:05:09 +0100 | [diff] [blame] | 426 | @memoized |
Sergei Trofimov | 63e6040 | 2017-07-26 14:21:55 +0100 | [diff] [blame^] | 427 | def get_related_cpus(self, cpu): |
Brendan Jackman | f1b4bf2 | 2016-10-10 14:05:09 +0100 | [diff] [blame] | 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 Trofimov | 36aa3af | 2017-07-26 13:59:55 +0100 | [diff] [blame] | 434 | sysfile = '/sys/devices/system/cpu/{}/cpufreq/related_cpus'.format(cpu) |
Brendan Jackman | f1b4bf2 | 2016-10-10 14:05:09 +0100 | [diff] [blame] | 435 | |
| 436 | return [int(c) for c in self.target.read_value(sysfile).split()] |
Brendan Jackman | dc32fa9 | 2017-05-17 10:43:34 +0100 | [diff] [blame] | 437 | |
| 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() |
Sergei Trofimov | 63e6040 | 2017-07-26 14:21:55 +0100 | [diff] [blame^] | 445 | domain = self.target.cpufreq.get_related_cpus(cpu) |
Brendan Jackman | dc32fa9 | 2017-05-17 10:43:34 +0100 | [diff] [blame] | 446 | yield domain |
| 447 | cpus = cpus.difference(domain) |