1#!/usr/bin/env python3

2

3 from glob import glob

4 from os.path import basename

5 from math import ceil

6 from time import sleep

7 import sys

8

9

10 def offline_cpu(cpu_name):

11 with open("/sys/devices/system/cpu/{}/online".format(cpu_name), "wt") as f:

12 f.write("0\n")

13

14

15 def online_cpu(cpu_name):

16 with open("/sys/devices/system/cpu/{}/online".format(cpu_name), "wt") as f:

17 f.write("1\n")

18

19

20 def is_cpu_online(cpu_name):

21 # use the same heuristic as original `cpu_offlining` test used which is to

22 # check if cpu is mentioned in /proc/interrupts

23 with open("/proc/interrupts", "rt") as f:

24 header = f.readline().lower().split()

25 return cpu_name in header

26

27

28 def main():

29 cpus = [basename(x) for x in glob("/sys/devices/system/cpu/cpu[0-9]*")]

30 # sort *numerically* cpus by their number, ignoring first 3 characters

31 # so ['cpu1', 'cpu11', 'cpu2'] is sorted to ['cpu1', 'cpu2', 'cpu11']

32 cpus.sort(key=lambda x: int(x[3:]))

33 with open("/proc/interrupts", "rt") as f:

34 interrupts_count = len(f.readlines()) - 1 # first line is a header

35

36 # there is an arch limit on how many interrupts one cpu can handle

37 # according to LP: 1682328 it's 224. So we have to reserve some CPUs for

38 # handling them

39 max_ints_per_cpu = 224

40 reserved_cpus_count = ceil(interrupts_count / max_ints_per_cpu)

41

42 failed_offlines = []

43

44 for cpu in cpus[reserved_cpus_count:]:

45 offline_cpu(cpu)

46 sleep(0.5)

47 if is_cpu_online(cpu):

48 print("ERROR: Failed to offline {}".format(cpu), file=sys.stderr)

49 failed_offlines.append(cpu)

50

51 failed_onlines = []

52

53 for cpu in cpus[reserved_cpus_count:]:

54 online_cpu(cpu)

55 sleep(0.5)

56 if not is_cpu_online(cpu):

57 print("ERROR: Failed to online {}".format(cpu), file=sys.stderr)

58 failed_onlines.append(cpu)

59

60 if not failed_offlines and not failed_onlines:

61 print(

62 "Successfully turned {} cores off and back on".format(

63 len(cpus) - reserved_cpus_count

64 )

65 )

66 return 0

67 else:

68 print(

69 "Error with offlining one or more cores. CPU offline may not "

70 "work if this is an ARM system.",

71 file=sys.stderr,

72 )

73 print(" ".join(failed_offlines))

74 print(" ".join(failed_onlines))

75 return 1

76

77

78 if __name__ == "__main__":

79 main()