|
19 | 19 | MSR_IA32_MISC_ENABLE_TURBO_DISABLE_BIT = 38
|
20 | 20 |
|
21 | 21 | OS_LINUX = sys.platform.startswith('linux')
|
| 22 | +PLATFORM_X86 = platform.machine() in ('x86', 'x86_64', 'amd64') |
22 | 23 |
|
23 | 24 |
|
24 | 25 | def is_root():
|
@@ -119,18 +120,113 @@ def write(self, tune):
|
119 | 120 | pass
|
120 | 121 |
|
121 | 122 |
|
| 123 | +class IntelPstateOperation(Operation): |
| 124 | + @staticmethod |
| 125 | + def available(): |
| 126 | + return use_intel_pstate() |
| 127 | + |
| 128 | + |
| 129 | +class TurboBoostIntelPstate(IntelPstateOperation): |
| 130 | + """ |
| 131 | + Get/Set Turbo Boost mode of Intel CPUs by reading from/writing into |
| 132 | + /sys/devices/system/cpu/intel_pstate/no_turbo of the intel_pstate driver. |
| 133 | + """ |
| 134 | + |
| 135 | + def __init__(self, system): |
| 136 | + IntelPstateOperation.__init__(self, 'Turbo Boost (intel_pstate)', system) |
| 137 | + self.path = sysfs_path("devices/system/cpu/intel_pstate/no_turbo") |
| 138 | + self.enabled = None |
| 139 | + |
| 140 | + def read_turbo_boost(self): |
| 141 | + no_turbo = self.read_first_line(self.path) |
| 142 | + if no_turbo == '1': |
| 143 | + self.enabled = False |
| 144 | + elif no_turbo == '0': |
| 145 | + self.enabled = True |
| 146 | + else: |
| 147 | + self.error("Invalid no_turbo value: %r" % no_turbo) |
| 148 | + self.enabled = None |
| 149 | + |
| 150 | + def show(self): |
| 151 | + self.read_turbo_boost() |
| 152 | + if self.enabled is not None: |
| 153 | + state = 'enabled' if self.enabled else 'disabled' |
| 154 | + self.log_state("Turbo Boost %s" % state) |
| 155 | + |
| 156 | + self.tuned_for_benchmarks = (not self.enabled) |
| 157 | + if self.enabled: |
| 158 | + self.advice('Disable Turbo Boost to get more reliable ' |
| 159 | + 'CPU frequency') |
| 160 | + |
| 161 | + def write(self, tune): |
| 162 | + enable = (not tune) |
| 163 | + |
| 164 | + self.read_turbo_boost() |
| 165 | + if self.enabled == enable: |
| 166 | + # no_turbo already set to the expected value |
| 167 | + return |
| 168 | + |
| 169 | + content = '0' if enable else '1' |
| 170 | + try: |
| 171 | + write_text(self.path, content) |
| 172 | + except OSError as exc: |
| 173 | + # don't log a permission error if the user is root: permission |
| 174 | + # error as root means that Turbo Boost is disabled in the BIOS |
| 175 | + if not is_root(): |
| 176 | + self.check_permission_error(exc) |
| 177 | + |
| 178 | + action = 'enable' if enable else 'disable' |
| 179 | + msg = "Failed to %s Turbo Boost" % action |
| 180 | + disabled_in_bios = is_permission_error(exc) and is_root() |
| 181 | + if disabled_in_bios: |
| 182 | + msg += " (Turbo Boost disabled in the BIOS?)" |
| 183 | + msg = "%s: failed to write into %s: %s" % (msg, self.path, exc) |
| 184 | + |
| 185 | + if disabled_in_bios: |
| 186 | + self.log_action("WARNING: %s" % msg) |
| 187 | + else: |
| 188 | + self.error(msg) |
| 189 | + return |
| 190 | + |
| 191 | + msg = "%r written into %s" % (content, self.path) |
| 192 | + action = 'enabled' if enable else 'disabled' |
| 193 | + self.log_action("Turbo Boost %s: %s" % (action, msg)) |
| 194 | + |
| 195 | + |
| 196 | +class CheckNOHZFullIntelPstate(IntelPstateOperation): |
| 197 | + |
| 198 | + def __init__(self, system): |
| 199 | + IntelPstateOperation.__init__(self, 'Check nohz_full', system) |
| 200 | + |
| 201 | + def show(self): |
| 202 | + nohz_full = self.read_first_line(sysfs_path('devices/system/cpu/nohz_full')) |
| 203 | + if not nohz_full: |
| 204 | + return |
| 205 | + |
| 206 | + nohz_full = parse_cpu_list(nohz_full) |
| 207 | + if not nohz_full: |
| 208 | + return |
| 209 | + |
| 210 | + used = set(self.system.cpus) | set(nohz_full) |
| 211 | + if not used: |
| 212 | + return |
| 213 | + |
| 214 | + self.advice("WARNING: nohz_full is enabled on CPUs %s which use the " |
| 215 | + "intel_pstate driver, whereas intel_pstate is incompatible " |
| 216 | + "with nohz_full" |
| 217 | + % format_cpu_list(used)) |
| 218 | + self.advice("See https://bugzilla.redhat.com/show_bug.cgi?id=1378529") |
| 219 | + self.tuned_for_benchmarks = False |
| 220 | + |
| 221 | + |
122 | 222 | class TurboBoostMSR(Operation):
|
123 | 223 | """
|
124 |
| - Get/Set Turbo Boost mode of Intel CPUs using /dev/cpu/N/msr. |
| 224 | + Get/Set Turbo Boost mode of X86 CPUs using /dev/cpu/N/msr |
125 | 225 | """
|
126 | 226 |
|
127 | 227 | @staticmethod
|
128 | 228 | def available():
|
129 |
| - return ( |
130 |
| - OS_LINUX and |
131 |
| - not use_intel_pstate() and |
132 |
| - platform.machine() in ('x86', 'x86_64', 'amd64') |
133 |
| - ) |
| 229 | + return OS_LINUX and PLATFORM_X86 and not use_intel_pstate() |
134 | 230 |
|
135 | 231 | def __init__(self, system):
|
136 | 232 | Operation.__init__(self, 'Turbo Boost (MSR)', system)
|
@@ -266,125 +362,92 @@ def write(self, tune):
|
266 | 362 | break
|
267 | 363 |
|
268 | 364 |
|
269 |
| -class TurboBoostIntelPstate(Operation): |
| 365 | +class CPUGovernor(Operation): |
270 | 366 | """
|
271 |
| - Get/Set Turbo Boost mode of Intel CPUs by reading from/writing into |
272 |
| - /sys/devices/system/cpu/intel_pstate/no_turbo of the intel_pstate driver. |
| 367 | + Get/Set CPU scaling governor |
273 | 368 | """
|
| 369 | + BENCHMARK_GOVERNOR = 'performance' |
274 | 370 |
|
275 |
| - @staticmethod |
276 |
| - def available(): |
277 |
| - return use_intel_pstate() |
| 371 | + @classmethod |
| 372 | + def available(cls): |
| 373 | + return os.path.exists( |
| 374 | + sysfs_path("devices/system/cpu/cpu0/cpufreq/scaling_governor") |
| 375 | + ) |
278 | 376 |
|
279 | 377 | def __init__(self, system):
|
280 |
| - Operation.__init__(self, 'Turbo Boost (intel_pstate)', system) |
281 |
| - self.path = sysfs_path("devices/system/cpu/intel_pstate/no_turbo") |
282 |
| - self.enabled = None |
283 |
| - |
284 |
| - def read_turbo_boost(self): |
285 |
| - no_turbo = self.read_first_line(self.path) |
286 |
| - if no_turbo == '1': |
287 |
| - self.enabled = False |
288 |
| - elif no_turbo == '0': |
289 |
| - self.enabled = True |
290 |
| - else: |
291 |
| - self.error("Invalid no_turbo value: %r" % no_turbo) |
292 |
| - self.enabled = None |
293 |
| - |
294 |
| - def show(self): |
295 |
| - self.read_turbo_boost() |
296 |
| - if self.enabled is not None: |
297 |
| - state = 'enabled' if self.enabled else 'disabled' |
298 |
| - self.log_state("Turbo Boost %s" % state) |
299 |
| - |
300 |
| - self.tuned_for_benchmarks = (not self.enabled) |
301 |
| - if self.enabled: |
302 |
| - self.advice('Disable Turbo Boost to get more reliable ' |
303 |
| - 'CPU frequency') |
304 |
| - |
305 |
| - def write(self, tune): |
306 |
| - enable = (not tune) |
307 |
| - |
308 |
| - self.read_turbo_boost() |
309 |
| - if self.enabled == enable: |
310 |
| - # no_turbo already set to the expected value |
311 |
| - return |
| 378 | + Operation.__init__(self, 'CPU scaling governor', system) |
| 379 | + self.device_syspath = sysfs_path("devices/system/cpu") |
312 | 380 |
|
313 |
| - content = '0' if enable else '1' |
| 381 | + def read_governor(self, cpu): |
| 382 | + filename = os.path.join(self.device_syspath, 'cpu%s/cpufreq/scaling_governor' % cpu) |
314 | 383 | try:
|
315 |
| - write_text(self.path, content) |
| 384 | + with open(filename, "r") as fp: |
| 385 | + return fp.readline().rstrip() |
316 | 386 | except OSError as exc:
|
317 |
| - # don't log a permission error if the user is root: permission |
318 |
| - # error as root means that Turbo Boost is disabled in the BIOS |
319 |
| - if not is_root(): |
320 |
| - self.check_permission_error(exc) |
| 387 | + self.check_permission_error(exc) |
| 388 | + self.error("Unable to read CPU scaling governor from %s" % filename) |
| 389 | + return None |
321 | 390 |
|
322 |
| - action = 'enable' if enable else 'disable' |
323 |
| - msg = "Failed to %s Turbo Boost" % action |
324 |
| - disabled_in_bios = is_permission_error(exc) and is_root() |
325 |
| - if disabled_in_bios: |
326 |
| - msg += " (Turbo Boost disabled in the BIOS?)" |
327 |
| - msg = "%s: failed to write into %s: %s" % (msg, self.path, exc) |
| 391 | + def show(self): |
| 392 | + cpus = {} |
| 393 | + for cpu in range(self.system.logical_cpu_count): |
| 394 | + governor = self.read_governor(cpu) |
| 395 | + if governor is not None: |
| 396 | + cpus[cpu] = governor |
328 | 397 |
|
329 |
| - if disabled_in_bios: |
330 |
| - self.log_action("WARNING: %s" % msg) |
331 |
| - else: |
332 |
| - self.error(msg) |
| 398 | + infos = format_cpu_infos(cpus) |
| 399 | + if not infos: |
333 | 400 | return
|
| 401 | + self.log_state('; '.join(infos)) |
334 | 402 |
|
335 |
| - msg = "%r written into %s" % (content, self.path) |
336 |
| - action = 'enabled' if enable else 'disabled' |
337 |
| - self.log_action("Turbo Boost %s: %s" % (action, msg)) |
338 |
| - |
| 403 | + self.tuned_for_benchmarks = all( |
| 404 | + governor == self.BENCHMARK_GOVERNOR for governor in cpus.values() |
| 405 | + ) |
| 406 | + if not self.tuned_for_benchmarks: |
| 407 | + self.advice('Use CPU scaling governor %r' |
| 408 | + % self.BENCHMARK_GOVERNOR) |
339 | 409 |
|
340 |
| -class CPUGovernorIntelPstate(Operation): |
341 |
| - """ |
342 |
| - Get/Set CPU scaling governor of the intel_pstate driver. |
343 |
| - """ |
344 |
| - BENCHMARK_GOVERNOR = 'performance' |
| 410 | + def write_governor(self, filename, new_governor): |
| 411 | + with open(filename, "r") as fp: |
| 412 | + governor = fp.readline().rstrip() |
345 | 413 |
|
346 |
| - @staticmethod |
347 |
| - def available(): |
348 |
| - return use_intel_pstate() |
| 414 | + if new_governor == governor: |
| 415 | + return False |
349 | 416 |
|
350 |
| - def __init__(self, system): |
351 |
| - Operation.__init__(self, 'CPU scaling governor (intel_pstate)', |
352 |
| - system) |
353 |
| - self.path = sysfs_path("devices/system/cpu/cpu0/cpufreq/scaling_governor") |
354 |
| - self.governor = None |
355 |
| - |
356 |
| - def read_governor(self): |
357 |
| - governor = self.read_first_line(self.path) |
358 |
| - if governor: |
359 |
| - self.governor = governor |
360 |
| - else: |
361 |
| - self.error("Unable to read CPU scaling governor from %s" % self.path) |
| 417 | + with open(filename, "w") as fp: |
| 418 | + fp.write(new_governor) |
| 419 | + return True |
362 | 420 |
|
363 |
| - def show(self): |
364 |
| - self.read_governor() |
365 |
| - if not self.governor: |
366 |
| - return |
| 421 | + def write_cpu(self, cpu, tune): |
| 422 | + governor = self.read_governor(cpu) |
| 423 | + if not governor: |
| 424 | + self.warning("Unable to read governor of CPU %s" % (cpu)) |
| 425 | + return False |
367 | 426 |
|
368 |
| - self.log_state(self.governor) |
369 |
| - self.tuned_for_benchmarks = (self.governor == self.BENCHMARK_GOVERNOR) |
370 |
| - if not self.tuned_for_benchmarks: |
371 |
| - self.advice('Use CPU scaling governor %r' |
372 |
| - % self.BENCHMARK_GOVERNOR) |
| 427 | + new_governor = self.BENCHMARK_GOVERNOR if tune else "powersave" |
| 428 | + filename = os.path.join(self.device_syspath, 'cpu%s/cpufreq/scaling_governor' % cpu) |
| 429 | + try: |
| 430 | + return self.write_governor(filename, new_governor) |
| 431 | + except OSError as exc: |
| 432 | + self.check_permission_error(exc) |
| 433 | + self.error("Unable to write governor of CPU %s: %s" |
| 434 | + % (cpu, exc)) |
373 | 435 |
|
374 | 436 | def write(self, tune):
|
375 |
| - self.read_governor() |
376 |
| - if not self.governor: |
377 |
| - return |
| 437 | + modified = [] |
| 438 | + for cpu in self.system.cpus: |
| 439 | + if self.write_cpu(cpu, tune): |
| 440 | + modified.append(cpu) |
| 441 | + if self.permission_error: |
| 442 | + break |
378 | 443 |
|
379 |
| - new_governor = 'performance' if tune else 'powersave' |
380 |
| - if new_governor == self.governor: |
381 |
| - return |
382 |
| - try: |
383 |
| - write_text(self.path, new_governor) |
384 |
| - except OSError as exc: |
385 |
| - self.error("Failed to set the CPU scaling governor: %s" % exc) |
386 |
| - else: |
387 |
| - self.log_action("CPU scaling governor set to %s" % new_governor) |
| 444 | + if modified: |
| 445 | + cpus = format_cpu_list(modified) |
| 446 | + if tune: |
| 447 | + action = "set to performance" |
| 448 | + else: |
| 449 | + action = "reset to powersave" |
| 450 | + self.log_action("CPU scaling governor of CPUs %s %s" % (cpus, action)) |
388 | 451 |
|
389 | 452 |
|
390 | 453 | class LinuxScheduler(Operation):
|
@@ -828,35 +891,6 @@ def write(self, tune):
|
828 | 891 | self.write_irqs(cpus)
|
829 | 892 |
|
830 | 893 |
|
831 |
| -class CheckNOHZFullIntelPstate(Operation): |
832 |
| - @staticmethod |
833 |
| - def available(): |
834 |
| - return use_intel_pstate() |
835 |
| - |
836 |
| - def __init__(self, system): |
837 |
| - Operation.__init__(self, 'Check nohz_full', system) |
838 |
| - |
839 |
| - def show(self): |
840 |
| - nohz_full = self.read_first_line(sysfs_path('devices/system/cpu/nohz_full')) |
841 |
| - if not nohz_full: |
842 |
| - return |
843 |
| - |
844 |
| - nohz_full = parse_cpu_list(nohz_full) |
845 |
| - if not nohz_full: |
846 |
| - return |
847 |
| - |
848 |
| - used = set(self.system.cpus) | set(nohz_full) |
849 |
| - if not used: |
850 |
| - return |
851 |
| - |
852 |
| - self.advice("WARNING: nohz_full is enabled on CPUs %s which use the " |
853 |
| - "intel_pstate driver, whereas intel_pstate is incompatible " |
854 |
| - "with nohz_full" |
855 |
| - % format_cpu_list(used)) |
856 |
| - self.advice("See https://bugzilla.redhat.com/show_bug.cgi?id=1378529") |
857 |
| - self.tuned_for_benchmarks = False |
858 |
| - |
859 |
| - |
860 | 894 | class PowerSupply(Operation):
|
861 | 895 | path = sysfs_path('class/power_supply')
|
862 | 896 |
|
@@ -950,18 +984,21 @@ def write(self, tune):
|
950 | 984 |
|
951 | 985 |
|
952 | 986 | OPERATIONS = [
|
| 987 | + # Generic operations |
953 | 988 | PerfEvent,
|
954 | 989 | ASLR,
|
955 | 990 | LinuxScheduler,
|
956 | 991 | CPUFrequency,
|
| 992 | + IRQAffinity, |
| 993 | + PowerSupply, |
957 | 994 | # Setting the CPU scaling governor resets no_turbo
|
958 | 995 | # and so must be set before Turbo Boost
|
959 |
| - CPUGovernorIntelPstate, |
| 996 | + CPUGovernor, |
| 997 | + # Intel Pstate Operations |
960 | 998 | TurboBoostIntelPstate,
|
961 | 999 | CheckNOHZFullIntelPstate,
|
| 1000 | + # X86 Operations |
962 | 1001 | TurboBoostMSR,
|
963 |
| - IRQAffinity, |
964 |
| - PowerSupply, |
965 | 1002 | ]
|
966 | 1003 |
|
967 | 1004 |
|
|
0 commit comments