13
13
# limitations under the License.
14
14
#
15
15
from contextlib import contextmanager
16
+ from operator import itemgetter
16
17
17
18
from devlib .module import Module
18
19
from devlib .exception import TargetStableError
19
20
from devlib .utils .misc import memoized
21
+ import devlib .utils .asyn as asyn
20
22
21
23
22
24
# a dict of governor name and a list of it tunables that can't be read
@@ -52,22 +54,25 @@ def __init__(self, target):
52
54
self ._governor_tunables = {}
53
55
54
56
@memoized
55
- def list_governors (self , cpu ):
57
+ @asyn .asyncf
58
+ async def list_governors (self , cpu ):
56
59
"""Returns a list of governors supported by the cpu."""
57
60
if isinstance (cpu , int ):
58
61
cpu = 'cpu{}' .format (cpu )
59
62
sysfile = '/sys/devices/system/cpu/{}/cpufreq/scaling_available_governors' .format (cpu )
60
- output = self .target .read_value (sysfile )
63
+ output = await self .target .read_value . asyn (sysfile )
61
64
return output .strip ().split ()
62
65
63
- def get_governor (self , cpu ):
66
+ @asyn .asyncf
67
+ async def get_governor (self , cpu ):
64
68
"""Returns the governor currently set for the specified CPU."""
65
69
if isinstance (cpu , int ):
66
70
cpu = 'cpu{}' .format (cpu )
67
71
sysfile = '/sys/devices/system/cpu/{}/cpufreq/scaling_governor' .format (cpu )
68
- return self .target .read_value (sysfile )
72
+ return await self .target .read_value . asyn (sysfile )
69
73
70
- def set_governor (self , cpu , governor , ** kwargs ):
74
+ @asyn .asyncf
75
+ async def set_governor (self , cpu , governor , ** kwargs ):
71
76
"""
72
77
Set the governor for the specified CPU.
73
78
See https://www.kernel.org/doc/Documentation/cpu-freq/governors.txt
@@ -90,15 +95,15 @@ def set_governor(self, cpu, governor, **kwargs):
90
95
"""
91
96
if isinstance (cpu , int ):
92
97
cpu = 'cpu{}' .format (cpu )
93
- supported = self .list_governors (cpu )
98
+ supported = await self .list_governors . asyn (cpu )
94
99
if governor not in supported :
95
100
raise TargetStableError ('Governor {} not supported for cpu {}' .format (governor , cpu ))
96
101
sysfile = '/sys/devices/system/cpu/{}/cpufreq/scaling_governor' .format (cpu )
97
- self .target .write_value (sysfile , governor )
98
- self .set_governor_tunables (cpu , governor , ** kwargs )
102
+ await self .target .write_value . asyn (sysfile , governor )
103
+ return await self .set_governor_tunables . asyn (cpu , governor , ** kwargs )
99
104
100
- @contextmanager
101
- def use_governor (self , governor , cpus = None , ** kwargs ):
105
+ @asyn . asynccontextmanager
106
+ async def use_governor (self , governor , cpus = None , ** kwargs ):
102
107
"""
103
108
Use a given governor, then restore previous governor(s)
104
109
@@ -111,66 +116,97 @@ def use_governor(self, governor, cpus=None, **kwargs):
111
116
:Keyword Arguments: Governor tunables, See :meth:`set_governor_tunables`
112
117
"""
113
118
if not cpus :
114
- cpus = self .target .list_online_cpus ()
115
-
116
- # Setting a governor & tunables for a cpu will set them for all cpus
117
- # in the same clock domain, so only manipulating one cpu per domain
118
- # is enough
119
- domains = set (self .get_affected_cpus (cpu )[0 ] for cpu in cpus )
120
- prev_governors = {cpu : (self .get_governor (cpu ), self .get_governor_tunables (cpu ))
121
- for cpu in domains }
122
-
123
- # Special case for userspace, frequency is not seen as a tunable
124
- userspace_freqs = {}
125
- for cpu , (prev_gov , _ ) in prev_governors .items ():
126
- if prev_gov == "userspace" :
127
- userspace_freqs [cpu ] = self .get_frequency (cpu )
128
-
129
- for cpu in domains :
130
- self .set_governor (cpu , governor , ** kwargs )
119
+ cpus = await self .target .list_online_cpus .asyn ()
120
+
121
+ async def get_cpu_info (cpu ):
122
+ return await asyn .parallel ((
123
+ self .get_affected_cpus .asyn (cpu ),
124
+ self .get_governor .asyn (cpu ),
125
+ self .get_governor_tunables .asyn (cpu ),
126
+ # We won't always use the frequency, but it's much quicker to
127
+ # do in parallel at the same time anyway so do it now
128
+ self .get_frequency .asyn (cpu ),
129
+ ))
130
+
131
+ cpus_infos = await asyn .parallel_dict (cpus , get_cpu_info )
132
+
133
+ # Setting a governor & tunables for a cpu will set them for all cpus in
134
+ # the same cpufreq policy, so only manipulating one cpu per domain is
135
+ # enough
136
+ domains = set (
137
+ info [0 ][0 ]
138
+ for info in cpus_infos .values ()
139
+ )
140
+
141
+ await asyn .parallel (
142
+ self .set_governor .asyn (cpu , governor , ** kwargs )
143
+ for cpu in domains
144
+ )
131
145
132
146
try :
133
147
yield
134
-
135
148
finally :
136
- for cpu , (prev_gov , tunables ) in prev_governors .items ():
137
- self .set_governor (cpu , prev_gov , ** tunables )
149
+ async def set_gov (cpu ):
150
+ domain , prev_gov , tunables , freq = cpus_infos [cpu ]
151
+ await self .set_governor .asyn (cpu , prev_gov , ** tunables )
152
+ # Special case for userspace, frequency is not seen as a tunable
138
153
if prev_gov == "userspace" :
139
- self .set_frequency (cpu , userspace_freqs [cpu ])
154
+ await self .set_frequency .asyn (cpu , freq )
155
+
156
+ await asyn .parallel (
157
+ set_gov (cpu )
158
+ for cpu in domains
159
+ )
140
160
141
- def list_governor_tunables (self , cpu ):
161
+ @asyn .asyncf
162
+ async def list_governor_tunables (self , cpu ):
142
163
"""Returns a list of tunables available for the governor on the specified CPU."""
143
164
if isinstance (cpu , int ):
144
165
cpu = 'cpu{}' .format (cpu )
145
- governor = self .get_governor (cpu )
166
+ governor = await self .get_governor . asyn (cpu )
146
167
if governor not in self ._governor_tunables :
147
168
try :
148
169
tunables_path = '/sys/devices/system/cpu/{}/cpufreq/{}' .format (cpu , governor )
149
- self ._governor_tunables [governor ] = self .target .list_directory (tunables_path )
170
+ self ._governor_tunables [governor ] = await self .target .list_directory . asyn (tunables_path )
150
171
except TargetStableError : # probably an older kernel
151
172
try :
152
173
tunables_path = '/sys/devices/system/cpu/cpufreq/{}' .format (governor )
153
- self ._governor_tunables [governor ] = self .target .list_directory (tunables_path )
174
+ self ._governor_tunables [governor ] = await self .target .list_directory . asyn (tunables_path )
154
175
except TargetStableError : # governor does not support tunables
155
176
self ._governor_tunables [governor ] = []
156
177
return self ._governor_tunables [governor ]
157
178
158
- def get_governor_tunables (self , cpu ):
179
+ @asyn .asyncf
180
+ async def get_governor_tunables (self , cpu ):
159
181
if isinstance (cpu , int ):
160
182
cpu = 'cpu{}' .format (cpu )
161
- governor = self .get_governor (cpu )
183
+ governor , tunable_list = await asyn .parallel ((
184
+ self .get_governor .asyn (cpu ),
185
+ self .list_governor_tunables .asyn (cpu )
186
+ ))
187
+
188
+ write_only = set (WRITE_ONLY_TUNABLES .get (governor , []))
189
+ tunable_list = [
190
+ tunable
191
+ for tunable in tunable_list
192
+ if tunable not in write_only
193
+ ]
194
+
162
195
tunables = {}
163
- for tunable in self .list_governor_tunables (cpu ):
164
- if tunable not in WRITE_ONLY_TUNABLES .get (governor , []):
165
- try :
166
- path = '/sys/devices/system/cpu/{}/cpufreq/{}/{}' .format (cpu , governor , tunable )
167
- tunables [tunable ] = self .target .read_value (path )
168
- except TargetStableError : # May be an older kernel
169
- path = '/sys/devices/system/cpu/cpufreq/{}/{}' .format (governor , tunable )
170
- tunables [tunable ] = self .target .read_value (path )
196
+ async def get_tunable (tunable ):
197
+ try :
198
+ path = '/sys/devices/system/cpu/{}/cpufreq/{}/{}' .format (cpu , governor , tunable )
199
+ x = await self .target .read_value .asyn (path )
200
+ except TargetStableError : # May be an older kernel
201
+ path = '/sys/devices/system/cpu/cpufreq/{}/{}' .format (governor , tunable )
202
+ x = await self .target .read_value .asyn (path )
203
+ return x
204
+
205
+ tunables = await asyn .parallel_dict (tunable_list , get_tunable )
171
206
return tunables
172
207
173
- def set_governor_tunables (self , cpu , governor = None , ** kwargs ):
208
+ @asyn .asyncf
209
+ async def set_governor_tunables (self , cpu , governor = None , ** kwargs ):
174
210
"""
175
211
Set tunables for the specified governor. Tunables should be specified as
176
212
keyword arguments. Which tunables and values are valid depends on the
@@ -191,20 +227,20 @@ def set_governor_tunables(self, cpu, governor=None, **kwargs):
191
227
if isinstance (cpu , int ):
192
228
cpu = 'cpu{}' .format (cpu )
193
229
if governor is None :
194
- governor = self .get_governor (cpu )
195
- valid_tunables = self .list_governor_tunables (cpu )
230
+ governor = await self .get_governor . asyn (cpu )
231
+ valid_tunables = await self .list_governor_tunables . asyn (cpu )
196
232
for tunable , value in kwargs .items ():
197
233
if tunable in valid_tunables :
198
234
path = '/sys/devices/system/cpu/{}/cpufreq/{}/{}' .format (cpu , governor , tunable )
199
235
try :
200
- self .target .write_value (path , value )
236
+ await self .target .write_value . asyn (path , value )
201
237
except TargetStableError :
202
- if self .target .file_exists (path ):
238
+ if await self .target .file_exists . asyn (path ):
203
239
# File exists but we did something wrong
204
240
raise
205
241
# Expected file doesn't exist, try older sysfs layout.
206
242
path = '/sys/devices/system/cpu/cpufreq/{}/{}' .format (governor , tunable )
207
- self .target .write_value (path , value )
243
+ await self .target .write_value . asyn (path , value )
208
244
else :
209
245
message = 'Unexpected tunable {} for governor {} on {}.\n ' .format (tunable , governor , cpu )
210
246
message += 'Available tunables are: {}' .format (valid_tunables )
@@ -301,7 +337,8 @@ def set_min_frequency(self, cpu, frequency, exact=True):
301
337
except ValueError :
302
338
raise ValueError ('Frequency must be an integer; got: "{}"' .format (frequency ))
303
339
304
- def get_frequency (self , cpu , cpuinfo = False ):
340
+ @asyn .asyncf
341
+ async def get_frequency (self , cpu , cpuinfo = False ):
305
342
"""
306
343
Returns the current frequency currently set for the specified CPU.
307
344
@@ -321,9 +358,10 @@ def get_frequency(self, cpu, cpuinfo=False):
321
358
sysfile = '/sys/devices/system/cpu/{}/cpufreq/{}' .format (
322
359
cpu ,
323
360
'cpuinfo_cur_freq' if cpuinfo else 'scaling_cur_freq' )
324
- return self .target .read_int (sysfile )
361
+ return await self .target .read_int . asyn (sysfile )
325
362
326
- def set_frequency (self , cpu , frequency , exact = True ):
363
+ @asyn .asyncf
364
+ async def set_frequency (self , cpu , frequency , exact = True ):
327
365
"""
328
366
Set's the minimum value for CPU frequency. Actual frequency will
329
367
depend on the Governor used and may vary during execution. The value should be
@@ -347,16 +385,16 @@ def set_frequency(self, cpu, frequency, exact=True):
347
385
try :
348
386
value = int (frequency )
349
387
if exact :
350
- available_frequencies = self .list_frequencies (cpu )
388
+ available_frequencies = await self .list_frequencies . asyn (cpu )
351
389
if available_frequencies and value not in available_frequencies :
352
390
raise TargetStableError ('Can\' t set {} frequency to {}\n must be in {}' .format (cpu ,
353
391
value ,
354
392
available_frequencies ))
355
- if self .get_governor (cpu ) != 'userspace' :
393
+ if await self .get_governor . asyn (cpu ) != 'userspace' :
356
394
raise TargetStableError ('Can\' t set {} frequency; governor must be "userspace"' .format (cpu ))
357
395
sysfile = '/sys/devices/system/cpu/{}/cpufreq/scaling_setspeed' .format (cpu )
358
- self .target .write_value (sysfile , value , verify = False )
359
- cpuinfo = self .get_frequency (cpu , cpuinfo = True )
396
+ await self .target .write_value . asyn (sysfile , value , verify = False )
397
+ cpuinfo = await self .get_frequency . asyn (cpu , cpuinfo = True )
360
398
if cpuinfo != value :
361
399
self .logger .warning (
362
400
'The cpufreq value has not been applied properly cpuinfo={} request={}' .format (cpuinfo , value ))
@@ -495,7 +533,8 @@ def trace_frequencies(self):
495
533
# pylint: disable=protected-access
496
534
return self .target ._execute_util ('cpufreq_trace_all_frequencies' , as_root = True )
497
535
498
- def get_affected_cpus (self , cpu ):
536
+ @asyn .asyncf
537
+ async def get_affected_cpus (self , cpu ):
499
538
"""
500
539
Get the online CPUs that share a frequency domain with the given CPU
501
540
"""
@@ -504,7 +543,8 @@ def get_affected_cpus(self, cpu):
504
543
505
544
sysfile = '/sys/devices/system/cpu/{}/cpufreq/affected_cpus' .format (cpu )
506
545
507
- return [int (c ) for c in self .target .read_value (sysfile ).split ()]
546
+ content = await self .target .read_value .asyn (sysfile )
547
+ return [int (c ) for c in content .split ()]
508
548
509
549
@memoized
510
550
def get_related_cpus (self , cpu ):
0 commit comments