@@ -58,7 +58,6 @@ def __init__(self, target):
58
58
super (CpufreqModule , self ).__init__ (target )
59
59
self ._governor_tunables = {}
60
60
61
- @memoized
62
61
@asyn .asyncf
63
62
async def list_governors (self , cpu ):
64
63
"""Returns a list of governors supported by the cpu."""
@@ -105,7 +104,7 @@ async def set_governor(self, cpu, governor, **kwargs):
105
104
raise TargetStableError ('Governor {} not supported for cpu {}' .format (governor , cpu ))
106
105
sysfile = '/sys/devices/system/cpu/{}/cpufreq/scaling_governor' .format (cpu )
107
106
await self .target .write_value .asyn (sysfile , governor )
108
- return await self .set_governor_tunables .asyn (cpu , governor , ** kwargs )
107
+ await self .set_governor_tunables .asyn (cpu , governor , ** kwargs )
109
108
110
109
@asyn .asynccontextmanager
111
110
async def use_governor (self , governor , cpus = None , ** kwargs ):
@@ -151,35 +150,74 @@ async def get_cpu_info(cpu):
151
150
try :
152
151
yield
153
152
finally :
154
- async def set_gov (cpu ):
153
+ async def set_per_cpu_tunables (cpu ):
155
154
domain , prev_gov , tunables , freq = cpus_infos [cpu ]
156
- await self .set_governor .asyn (cpu , prev_gov , ** tunables )
155
+ # Per-cpu tunables are safe to set concurrently
156
+ await self .set_governor_tunables .asyn (cpu , prev_gov , per_cpu = True , ** tunables )
157
157
# Special case for userspace, frequency is not seen as a tunable
158
158
if prev_gov == "userspace" :
159
159
await self .set_frequency .asyn (cpu , freq )
160
160
161
+ per_cpu_tunables = self .target .async_manager .concurrently (
162
+ set_per_cpu_tunables (cpu )
163
+ for cpu in domains
164
+ )
165
+
166
+ # Non-per-cpu tunables have to be set one after the other, for each
167
+ # governor that we had to deal with.
168
+ global_tunables = {
169
+ prev_gov : (cpu , tunables )
170
+ for cpu , (domain , prev_gov , tunables , freq ) in cpus_infos .items ()
171
+ }
172
+
173
+ global_tunables = self .target .async_manager .concurrently (
174
+ self .set_governor_tunables .asyn (cpu , gov , per_cpu = False , ** tunables )
175
+ for gov , (cpu , tunables ) in global_tunables .items ()
176
+ )
177
+
178
+ # Set the governor first
161
179
await self .target .async_manager .concurrently (
162
- set_gov (cpu )
180
+ self . set_governor . asyn (cpu , cpufs_infos [ cpu ][ 1 ] )
163
181
for cpu in domains
164
182
)
183
+ # And then set all the tunables concurrently. Each task has a
184
+ # specific and non-overlapping set of file to write.
185
+ await self .target .async_manager .concurrently (
186
+ (per_cpu_tunables , global_tunables )
187
+ )
165
188
166
189
@asyn .asyncf
167
- async def list_governor_tunables (self , cpu ):
168
- """Returns a list of tunables available for the governor on the specified CPU."""
190
+ async def _list_governor_tunables (self , cpu ):
169
191
if isinstance (cpu , int ):
170
192
cpu = 'cpu{}' .format (cpu )
171
193
governor = await self .get_governor .asyn (cpu )
172
- if governor not in self ._governor_tunables :
173
- try :
174
- tunables_path = '/sys/devices/system/cpu/{}/cpufreq/{}' .format (cpu , governor )
175
- self ._governor_tunables [governor ] = await self .target .list_directory .asyn (tunables_path )
176
- except TargetStableError : # probably an older kernel
194
+
195
+ try :
196
+ return self ._governor_tunables [governor ]
197
+ except KeyError :
198
+ for per_cpu , path in (
199
+ (True , '/sys/devices/system/cpu/{}/cpufreq/{}' .format (cpu , governor )),
200
+ # On old kernels
201
+ (False , '/sys/devices/system/cpu/cpufreq/{}' .format (governor )),
202
+ ):
177
203
try :
178
- tunables_path = '/sys/devices/system/cpu/cpufreq/{}' .format (governor )
179
- self ._governor_tunables [governor ] = await self .target .list_directory .asyn (tunables_path )
180
- except TargetStableError : # governor does not support tunables
181
- self ._governor_tunables [governor ] = []
182
- return self ._governor_tunables [governor ]
204
+ tunables = await self .target .list_directory .asyn (tunables_path )
205
+ except TargetStableError :
206
+ continue
207
+ else :
208
+ break
209
+ else :
210
+ per_cpu = False
211
+ tunables = []
212
+
213
+ self ._governor_tunables [governor ] = (per_cpu , tunables )
214
+ return (per_cpu , tunables )
215
+
216
+ @asyn .asyncf
217
+ async def list_governor_tunables (self , cpu ):
218
+ """Returns a list of tunables available for the governor on the specified CPU."""
219
+ _ , tunables = await self ._list_governor_tunables .asyn (cpu )
220
+ return tunables
183
221
184
222
@asyn .asyncf
185
223
async def get_governor_tunables (self , cpu ):
@@ -211,7 +249,7 @@ async def get_tunable(tunable):
211
249
return tunables
212
250
213
251
@asyn .asyncf
214
- async def set_governor_tunables (self , cpu , governor = None , ** kwargs ):
252
+ async def set_governor_tunables (self , cpu , governor = None , per_cpu = None , ** kwargs ):
215
253
"""
216
254
Set tunables for the specified governor. Tunables should be specified as
217
255
keyword arguments. Which tunables and values are valid depends on the
@@ -220,6 +258,9 @@ async def set_governor_tunables(self, cpu, governor=None, **kwargs):
220
258
:param cpu: The cpu for which the governor will be set. ``int`` or
221
259
full cpu name as it appears in sysfs, e.g. ``cpu0``.
222
260
:param governor: The name of the governor. Must be all lower case.
261
+ :param per_cpu: If ``None``, both per-cpu and global governor tunables
262
+ will be set. If ``True``, only per-CPU tunables will be set and if
263
+ ``False``, only global tunables will be set.
223
264
224
265
The rest should be keyword parameters mapping tunable name onto the value to
225
266
be set for it.
@@ -229,29 +270,29 @@ async def set_governor_tunables(self, cpu, governor=None, **kwargs):
229
270
tunable.
230
271
231
272
"""
273
+ if not kwargs :
274
+ return
232
275
if isinstance (cpu , int ):
233
276
cpu = 'cpu{}' .format (cpu )
234
277
if governor is None :
235
278
governor = await self .get_governor .asyn (cpu )
236
- valid_tunables = await self .list_governor_tunables .asyn (cpu )
279
+ gov_per_cpu , valid_tunables = await self ._list_governor_tunables .asyn (cpu )
237
280
for tunable , value in kwargs .items ():
238
281
if tunable in valid_tunables :
239
- path = '/sys/devices/system/cpu/{}/cpufreq/{}/{}' .format (cpu , governor , tunable )
240
- try :
241
- await self .target .write_value .asyn (path , value )
242
- except TargetStableError :
243
- if await self .target .file_exists .asyn (path ):
244
- # File exists but we did something wrong
245
- raise
246
- # Expected file doesn't exist, try older sysfs layout.
282
+ if per_cpu is not None and gov_per_cpu != per_cpu :
283
+ pass
284
+
285
+ if gov_per_cpu :
286
+ path = '/sys/devices/system/cpu/{}/cpufreq/{}/{}' .format (cpu , governor , tunable )
287
+ else :
247
288
path = '/sys/devices/system/cpu/cpufreq/{}/{}' .format (governor , tunable )
248
- await self .target .write_value .asyn (path , value )
289
+
290
+ await self .target .write_value .asyn (path , value )
249
291
else :
250
292
message = 'Unexpected tunable {} for governor {} on {}.\n ' .format (tunable , governor , cpu )
251
293
message += 'Available tunables are: {}' .format (valid_tunables )
252
294
raise TargetStableError (message )
253
295
254
- @memoized
255
296
@asyn .asyncf
256
297
async def list_frequencies (self , cpu ):
257
298
"""Returns a sorted list of frequencies supported by the cpu or an empty list
0 commit comments