mirror of
https://github.com/ARM-software/devlib.git
synced 2025-01-30 17:50:46 +00:00
devlib: Use async Target API
Make use of the new async API to speedup other parts of devlib.
This commit is contained in:
parent
18ab9f80b0
commit
2c4b16f280
@ -28,6 +28,7 @@ from devlib.collector import (CollectorBase, CollectorOutput,
|
||||
from devlib.host import PACKAGE_BIN_DIRECTORY
|
||||
from devlib.exception import TargetStableError, HostError
|
||||
from devlib.utils.misc import check_output, which, memoized
|
||||
from devlib.utils.asyn import asyncf
|
||||
|
||||
|
||||
TRACE_MARKER_START = 'TRACE_MARKER_START'
|
||||
@ -243,7 +244,8 @@ class FtraceCollector(CollectorBase):
|
||||
self.target.write_value(self.function_profile_file, 0, verify=False)
|
||||
self._reset_needed = False
|
||||
|
||||
def start(self):
|
||||
@asyncf
|
||||
async def start(self):
|
||||
self.start_time = time.time()
|
||||
if self._reset_needed:
|
||||
self.reset()
|
||||
@ -282,14 +284,17 @@ class FtraceCollector(CollectorBase):
|
||||
self.target.cpuidle.perturb_cpus()
|
||||
# Enable kernel function profiling
|
||||
if self.functions and self.tracer is None:
|
||||
self.target.execute('echo nop > {}'.format(self.current_tracer_file),
|
||||
as_root=True)
|
||||
self.target.execute('echo 0 > {}'.format(self.function_profile_file),
|
||||
as_root=True)
|
||||
self.target.execute('echo {} > {}'.format(self.function_string, self.ftrace_filter_file),
|
||||
as_root=True)
|
||||
self.target.execute('echo 1 > {}'.format(self.function_profile_file),
|
||||
as_root=True)
|
||||
target = self.target
|
||||
await target.async_manager.concurrently(
|
||||
execute.asyn('echo nop > {}'.format(self.current_tracer_file),
|
||||
as_root=True),
|
||||
execute.asyn('echo 0 > {}'.format(self.function_profile_file),
|
||||
as_root=True),
|
||||
execute.asyn('echo {} > {}'.format(self.function_string, self.ftrace_filter_file),
|
||||
as_root=True),
|
||||
execute.asyn('echo 1 > {}'.format(self.function_profile_file),
|
||||
as_root=True),
|
||||
)
|
||||
|
||||
|
||||
def stop(self):
|
||||
|
@ -19,11 +19,13 @@ from collections import namedtuple
|
||||
from shlex import quote
|
||||
import itertools
|
||||
import warnings
|
||||
import asyncio
|
||||
|
||||
from devlib.module import Module
|
||||
from devlib.exception import TargetStableError
|
||||
from devlib.utils.misc import list_to_ranges, isiterable
|
||||
from devlib.utils.types import boolean
|
||||
from devlib.utils.asyn import asyncf
|
||||
|
||||
|
||||
class Controller(object):
|
||||
@ -55,7 +57,8 @@ class Controller(object):
|
||||
self.mount_point = None
|
||||
self._cgroups = {}
|
||||
|
||||
def mount(self, target, mount_root):
|
||||
@asyncf
|
||||
async def mount(self, target, mount_root):
|
||||
|
||||
mounted = target.list_file_systems()
|
||||
if self.mount_name in [e.device for e in mounted]:
|
||||
@ -68,16 +71,16 @@ class Controller(object):
|
||||
else:
|
||||
# Mount the controller if not already in use
|
||||
self.mount_point = target.path.join(mount_root, self.mount_name)
|
||||
target.execute('mkdir -p {} 2>/dev/null'\
|
||||
await target.execute.asyn('mkdir -p {} 2>/dev/null'\
|
||||
.format(self.mount_point), as_root=True)
|
||||
target.execute('mount -t cgroup -o {} {} {}'\
|
||||
await target.execute.asyn('mount -t cgroup -o {} {} {}'\
|
||||
.format(','.join(self.clist),
|
||||
self.mount_name,
|
||||
self.mount_point),
|
||||
as_root=True)
|
||||
|
||||
# Check if this controller uses "noprefix" option
|
||||
output = target.execute('mount | grep "{} "'.format(self.mount_name))
|
||||
output = await target.execute.asyn('mount | grep "{} "'.format(self.mount_name))
|
||||
if 'noprefix' in output:
|
||||
self._noprefix = True
|
||||
# self.logger.debug('Controller %s using "noprefix" option',
|
||||
@ -394,11 +397,12 @@ class CgroupsModule(Module):
|
||||
# Initialize controllers
|
||||
self.logger.info('Available controllers:')
|
||||
self.controllers = {}
|
||||
for ss in subsys:
|
||||
|
||||
async def register_controller(ss):
|
||||
hid = ss.hierarchy
|
||||
controller = Controller(ss.name, hid, hierarchy[hid])
|
||||
try:
|
||||
controller.mount(self.target, self.cgroup_root)
|
||||
await controller.mount.asyn(self.target, self.cgroup_root)
|
||||
except TargetStableError:
|
||||
message = 'Failed to mount "{}" controller'
|
||||
raise TargetStableError(message.format(controller.kind))
|
||||
@ -406,6 +410,14 @@ class CgroupsModule(Module):
|
||||
controller.mount_point)
|
||||
self.controllers[ss.name] = controller
|
||||
|
||||
asyncio.run(
|
||||
target.async_manager.map_concurrently(
|
||||
register_controller,
|
||||
subsys,
|
||||
)
|
||||
)
|
||||
|
||||
|
||||
def list_subsystems(self):
|
||||
subsystems = []
|
||||
for line in self.target.execute('{} cat /proc/cgroups'\
|
||||
|
@ -13,10 +13,12 @@
|
||||
# limitations under the License.
|
||||
#
|
||||
from contextlib import contextmanager
|
||||
from operator import itemgetter
|
||||
|
||||
from devlib.module import Module
|
||||
from devlib.exception import TargetStableError
|
||||
from devlib.utils.misc import memoized
|
||||
import devlib.utils.asyn as asyn
|
||||
|
||||
|
||||
# a dict of governor name and a list of it tunables that can't be read
|
||||
@ -30,44 +32,52 @@ class CpufreqModule(Module):
|
||||
name = 'cpufreq'
|
||||
|
||||
@staticmethod
|
||||
def probe(target):
|
||||
@asyn.asyncf
|
||||
async def probe(target):
|
||||
paths = [
|
||||
# x86 with Intel P-State driver
|
||||
(target.abi == 'x86_64', '/sys/devices/system/cpu/intel_pstate'),
|
||||
# Generic CPUFreq support (single policy)
|
||||
(True, '/sys/devices/system/cpu/cpufreq/policy0'),
|
||||
# Generic CPUFreq support (per CPU policy)
|
||||
(True, '/sys/devices/system/cpu/cpu0/cpufreq'),
|
||||
]
|
||||
paths = [
|
||||
path[1] for path in paths
|
||||
if path[0]
|
||||
]
|
||||
|
||||
# x86 with Intel P-State driver
|
||||
if target.abi == 'x86_64':
|
||||
path = '/sys/devices/system/cpu/intel_pstate'
|
||||
if target.file_exists(path):
|
||||
return True
|
||||
exists = await target.async_manager.map_concurrently(
|
||||
target.file_exists.asyn,
|
||||
paths,
|
||||
)
|
||||
|
||||
# Generic CPUFreq support (single policy)
|
||||
path = '/sys/devices/system/cpu/cpufreq/policy0'
|
||||
if target.file_exists(path):
|
||||
return True
|
||||
|
||||
# Generic CPUFreq support (per CPU policy)
|
||||
path = '/sys/devices/system/cpu/cpu0/cpufreq'
|
||||
return target.file_exists(path)
|
||||
return any(exists.values())
|
||||
|
||||
def __init__(self, target):
|
||||
super(CpufreqModule, self).__init__(target)
|
||||
self._governor_tunables = {}
|
||||
|
||||
@memoized
|
||||
def list_governors(self, cpu):
|
||||
@asyn.asyncf
|
||||
async def list_governors(self, cpu):
|
||||
"""Returns a list of governors supported by the cpu."""
|
||||
if isinstance(cpu, int):
|
||||
cpu = 'cpu{}'.format(cpu)
|
||||
sysfile = '/sys/devices/system/cpu/{}/cpufreq/scaling_available_governors'.format(cpu)
|
||||
output = self.target.read_value(sysfile)
|
||||
output = await self.target.read_value.asyn(sysfile)
|
||||
return output.strip().split()
|
||||
|
||||
def get_governor(self, cpu):
|
||||
@asyn.asyncf
|
||||
async def get_governor(self, cpu):
|
||||
"""Returns the governor currently set for the specified CPU."""
|
||||
if isinstance(cpu, int):
|
||||
cpu = 'cpu{}'.format(cpu)
|
||||
sysfile = '/sys/devices/system/cpu/{}/cpufreq/scaling_governor'.format(cpu)
|
||||
return self.target.read_value(sysfile)
|
||||
return await self.target.read_value.asyn(sysfile)
|
||||
|
||||
def set_governor(self, cpu, governor, **kwargs):
|
||||
@asyn.asyncf
|
||||
async def set_governor(self, cpu, governor, **kwargs):
|
||||
"""
|
||||
Set the governor for the specified CPU.
|
||||
See https://www.kernel.org/doc/Documentation/cpu-freq/governors.txt
|
||||
@ -90,15 +100,15 @@ class CpufreqModule(Module):
|
||||
"""
|
||||
if isinstance(cpu, int):
|
||||
cpu = 'cpu{}'.format(cpu)
|
||||
supported = self.list_governors(cpu)
|
||||
supported = await self.list_governors.asyn(cpu)
|
||||
if governor not in supported:
|
||||
raise TargetStableError('Governor {} not supported for cpu {}'.format(governor, cpu))
|
||||
sysfile = '/sys/devices/system/cpu/{}/cpufreq/scaling_governor'.format(cpu)
|
||||
self.target.write_value(sysfile, governor)
|
||||
self.set_governor_tunables(cpu, governor, **kwargs)
|
||||
await self.target.write_value.asyn(sysfile, governor)
|
||||
return await self.set_governor_tunables.asyn(cpu, governor, **kwargs)
|
||||
|
||||
@contextmanager
|
||||
def use_governor(self, governor, cpus=None, **kwargs):
|
||||
@asyn.asynccontextmanager
|
||||
async def use_governor(self, governor, cpus=None, **kwargs):
|
||||
"""
|
||||
Use a given governor, then restore previous governor(s)
|
||||
|
||||
@ -111,66 +121,97 @@ class CpufreqModule(Module):
|
||||
:Keyword Arguments: Governor tunables, See :meth:`set_governor_tunables`
|
||||
"""
|
||||
if not cpus:
|
||||
cpus = self.target.list_online_cpus()
|
||||
cpus = await self.target.list_online_cpus.asyn()
|
||||
|
||||
# Setting a governor & tunables for a cpu will set them for all cpus
|
||||
# in the same clock domain, so only manipulating one cpu per domain
|
||||
# is enough
|
||||
domains = set(self.get_affected_cpus(cpu)[0] for cpu in cpus)
|
||||
prev_governors = {cpu : (self.get_governor(cpu), self.get_governor_tunables(cpu))
|
||||
for cpu in domains}
|
||||
async def get_cpu_info(cpu):
|
||||
return await self.target.async_manager.concurrently((
|
||||
self.get_affected_cpus.asyn(cpu),
|
||||
self.get_governor.asyn(cpu),
|
||||
self.get_governor_tunables.asyn(cpu),
|
||||
# We won't always use the frequency, but it's much quicker to
|
||||
# do concurrently anyway so do it now
|
||||
self.get_frequency.asyn(cpu),
|
||||
))
|
||||
|
||||
# Special case for userspace, frequency is not seen as a tunable
|
||||
userspace_freqs = {}
|
||||
for cpu, (prev_gov, _) in prev_governors.items():
|
||||
if prev_gov == "userspace":
|
||||
userspace_freqs[cpu] = self.get_frequency(cpu)
|
||||
cpus_infos = await self.target.async_manager.map_concurrently(get_cpu_info, cpus)
|
||||
|
||||
for cpu in domains:
|
||||
self.set_governor(cpu, governor, **kwargs)
|
||||
# Setting a governor & tunables for a cpu will set them for all cpus in
|
||||
# the same cpufreq policy, so only manipulating one cpu per domain is
|
||||
# enough
|
||||
domains = set(
|
||||
info[0][0]
|
||||
for info in cpus_infos.values()
|
||||
)
|
||||
|
||||
await self.target.async_manager.concurrently(
|
||||
self.set_governor.asyn(cpu, governor, **kwargs)
|
||||
for cpu in domains
|
||||
)
|
||||
|
||||
try:
|
||||
yield
|
||||
|
||||
finally:
|
||||
for cpu, (prev_gov, tunables) in prev_governors.items():
|
||||
self.set_governor(cpu, prev_gov, **tunables)
|
||||
async def set_gov(cpu):
|
||||
domain, prev_gov, tunables, freq = cpus_infos[cpu]
|
||||
await self.set_governor.asyn(cpu, prev_gov, **tunables)
|
||||
# Special case for userspace, frequency is not seen as a tunable
|
||||
if prev_gov == "userspace":
|
||||
self.set_frequency(cpu, userspace_freqs[cpu])
|
||||
await self.set_frequency.asyn(cpu, freq)
|
||||
|
||||
def list_governor_tunables(self, cpu):
|
||||
await self.target.async_manager.concurrently(
|
||||
set_gov(cpu)
|
||||
for cpu in domains
|
||||
)
|
||||
|
||||
@asyn.asyncf
|
||||
async def list_governor_tunables(self, cpu):
|
||||
"""Returns a list of tunables available for the governor on the specified CPU."""
|
||||
if isinstance(cpu, int):
|
||||
cpu = 'cpu{}'.format(cpu)
|
||||
governor = self.get_governor(cpu)
|
||||
governor = await self.get_governor.asyn(cpu)
|
||||
if governor not in self._governor_tunables:
|
||||
try:
|
||||
tunables_path = '/sys/devices/system/cpu/{}/cpufreq/{}'.format(cpu, governor)
|
||||
self._governor_tunables[governor] = self.target.list_directory(tunables_path)
|
||||
self._governor_tunables[governor] = await self.target.list_directory.asyn(tunables_path)
|
||||
except TargetStableError: # probably an older kernel
|
||||
try:
|
||||
tunables_path = '/sys/devices/system/cpu/cpufreq/{}'.format(governor)
|
||||
self._governor_tunables[governor] = self.target.list_directory(tunables_path)
|
||||
self._governor_tunables[governor] = await self.target.list_directory.asyn(tunables_path)
|
||||
except TargetStableError: # governor does not support tunables
|
||||
self._governor_tunables[governor] = []
|
||||
return self._governor_tunables[governor]
|
||||
|
||||
def get_governor_tunables(self, cpu):
|
||||
@asyn.asyncf
|
||||
async def get_governor_tunables(self, cpu):
|
||||
if isinstance(cpu, int):
|
||||
cpu = 'cpu{}'.format(cpu)
|
||||
governor = self.get_governor(cpu)
|
||||
governor, tunable_list = await self.target.async_manager.concurrently((
|
||||
self.get_governor.asyn(cpu),
|
||||
self.list_governor_tunables.asyn(cpu)
|
||||
))
|
||||
|
||||
write_only = set(WRITE_ONLY_TUNABLES.get(governor, []))
|
||||
tunable_list = [
|
||||
tunable
|
||||
for tunable in tunable_list
|
||||
if tunable not in write_only
|
||||
]
|
||||
|
||||
tunables = {}
|
||||
for tunable in self.list_governor_tunables(cpu):
|
||||
if tunable not in WRITE_ONLY_TUNABLES.get(governor, []):
|
||||
try:
|
||||
path = '/sys/devices/system/cpu/{}/cpufreq/{}/{}'.format(cpu, governor, tunable)
|
||||
tunables[tunable] = self.target.read_value(path)
|
||||
except TargetStableError: # May be an older kernel
|
||||
path = '/sys/devices/system/cpu/cpufreq/{}/{}'.format(governor, tunable)
|
||||
tunables[tunable] = self.target.read_value(path)
|
||||
async def get_tunable(tunable):
|
||||
try:
|
||||
path = '/sys/devices/system/cpu/{}/cpufreq/{}/{}'.format(cpu, governor, tunable)
|
||||
x = await self.target.read_value.asyn(path)
|
||||
except TargetStableError: # May be an older kernel
|
||||
path = '/sys/devices/system/cpu/cpufreq/{}/{}'.format(governor, tunable)
|
||||
x = await self.target.read_value.asyn(path)
|
||||
return x
|
||||
|
||||
tunables = await self.target.async_manager.map_concurrently(get_tunable, tunable_list)
|
||||
return tunables
|
||||
|
||||
def set_governor_tunables(self, cpu, governor=None, **kwargs):
|
||||
@asyn.asyncf
|
||||
async def set_governor_tunables(self, cpu, governor=None, **kwargs):
|
||||
"""
|
||||
Set tunables for the specified governor. Tunables should be specified as
|
||||
keyword arguments. Which tunables and values are valid depends on the
|
||||
@ -191,34 +232,35 @@ class CpufreqModule(Module):
|
||||
if isinstance(cpu, int):
|
||||
cpu = 'cpu{}'.format(cpu)
|
||||
if governor is None:
|
||||
governor = self.get_governor(cpu)
|
||||
valid_tunables = self.list_governor_tunables(cpu)
|
||||
governor = await self.get_governor.asyn(cpu)
|
||||
valid_tunables = await self.list_governor_tunables.asyn(cpu)
|
||||
for tunable, value in kwargs.items():
|
||||
if tunable in valid_tunables:
|
||||
path = '/sys/devices/system/cpu/{}/cpufreq/{}/{}'.format(cpu, governor, tunable)
|
||||
try:
|
||||
self.target.write_value(path, value)
|
||||
await self.target.write_value.asyn(path, value)
|
||||
except TargetStableError:
|
||||
if self.target.file_exists(path):
|
||||
if await self.target.file_exists.asyn(path):
|
||||
# File exists but we did something wrong
|
||||
raise
|
||||
# Expected file doesn't exist, try older sysfs layout.
|
||||
path = '/sys/devices/system/cpu/cpufreq/{}/{}'.format(governor, tunable)
|
||||
self.target.write_value(path, value)
|
||||
await self.target.write_value.asyn(path, value)
|
||||
else:
|
||||
message = 'Unexpected tunable {} for governor {} on {}.\n'.format(tunable, governor, cpu)
|
||||
message += 'Available tunables are: {}'.format(valid_tunables)
|
||||
raise TargetStableError(message)
|
||||
|
||||
@memoized
|
||||
def list_frequencies(self, cpu):
|
||||
@asyn.asyncf
|
||||
async def list_frequencies(self, cpu):
|
||||
"""Returns a sorted list of frequencies supported by the cpu or an empty list
|
||||
if not could be found."""
|
||||
if isinstance(cpu, int):
|
||||
cpu = 'cpu{}'.format(cpu)
|
||||
try:
|
||||
cmd = 'cat /sys/devices/system/cpu/{}/cpufreq/scaling_available_frequencies'.format(cpu)
|
||||
output = self.target.execute(cmd)
|
||||
output = await self.target.execute.asyn(cmd)
|
||||
available_frequencies = list(map(int, output.strip().split())) # pylint: disable=E1103
|
||||
except TargetStableError:
|
||||
# On some devices scaling_frequencies is not generated.
|
||||
@ -226,7 +268,7 @@ class CpufreqModule(Module):
|
||||
# Fall back to parsing stats/time_in_state
|
||||
path = '/sys/devices/system/cpu/{}/cpufreq/stats/time_in_state'.format(cpu)
|
||||
try:
|
||||
out_iter = iter(self.target.read_value(path).split())
|
||||
out_iter = (await self.target.read_value.asyn(path)).split()
|
||||
except TargetStableError:
|
||||
if not self.target.file_exists(path):
|
||||
# Probably intel_pstate. Can't get available freqs.
|
||||
@ -254,7 +296,8 @@ class CpufreqModule(Module):
|
||||
freqs = self.list_frequencies(cpu)
|
||||
return min(freqs) if freqs else None
|
||||
|
||||
def get_min_frequency(self, cpu):
|
||||
@asyn.asyncf
|
||||
async def get_min_frequency(self, cpu):
|
||||
"""
|
||||
Returns the min frequency currently set for the specified CPU.
|
||||
|
||||
@ -268,9 +311,10 @@ class CpufreqModule(Module):
|
||||
if isinstance(cpu, int):
|
||||
cpu = 'cpu{}'.format(cpu)
|
||||
sysfile = '/sys/devices/system/cpu/{}/cpufreq/scaling_min_freq'.format(cpu)
|
||||
return self.target.read_int(sysfile)
|
||||
return await self.target.read_int.asyn(sysfile)
|
||||
|
||||
def set_min_frequency(self, cpu, frequency, exact=True):
|
||||
@asyn.asyncf
|
||||
async def set_min_frequency(self, cpu, frequency, exact=True):
|
||||
"""
|
||||
Set's the minimum value for CPU frequency. Actual frequency will
|
||||
depend on the Governor used and may vary during execution. The value should be
|
||||
@ -289,7 +333,7 @@ class CpufreqModule(Module):
|
||||
"""
|
||||
if isinstance(cpu, int):
|
||||
cpu = 'cpu{}'.format(cpu)
|
||||
available_frequencies = self.list_frequencies(cpu)
|
||||
available_frequencies = await self.list_frequencies.asyn(cpu)
|
||||
try:
|
||||
value = int(frequency)
|
||||
if exact and available_frequencies and value not in available_frequencies:
|
||||
@ -297,11 +341,12 @@ class CpufreqModule(Module):
|
||||
value,
|
||||
available_frequencies))
|
||||
sysfile = '/sys/devices/system/cpu/{}/cpufreq/scaling_min_freq'.format(cpu)
|
||||
self.target.write_value(sysfile, value)
|
||||
await self.target.write_value.asyn(sysfile, value)
|
||||
except ValueError:
|
||||
raise ValueError('Frequency must be an integer; got: "{}"'.format(frequency))
|
||||
|
||||
def get_frequency(self, cpu, cpuinfo=False):
|
||||
@asyn.asyncf
|
||||
async def get_frequency(self, cpu, cpuinfo=False):
|
||||
"""
|
||||
Returns the current frequency currently set for the specified CPU.
|
||||
|
||||
@ -321,9 +366,10 @@ class CpufreqModule(Module):
|
||||
sysfile = '/sys/devices/system/cpu/{}/cpufreq/{}'.format(
|
||||
cpu,
|
||||
'cpuinfo_cur_freq' if cpuinfo else 'scaling_cur_freq')
|
||||
return self.target.read_int(sysfile)
|
||||
return await self.target.read_int.asyn(sysfile)
|
||||
|
||||
def set_frequency(self, cpu, frequency, exact=True):
|
||||
@asyn.asyncf
|
||||
async def set_frequency(self, cpu, frequency, exact=True):
|
||||
"""
|
||||
Set's the minimum value for CPU frequency. Actual frequency will
|
||||
depend on the Governor used and may vary during execution. The value should be
|
||||
@ -347,23 +393,24 @@ class CpufreqModule(Module):
|
||||
try:
|
||||
value = int(frequency)
|
||||
if exact:
|
||||
available_frequencies = self.list_frequencies(cpu)
|
||||
available_frequencies = await self.list_frequencies.asyn(cpu)
|
||||
if available_frequencies and value not in available_frequencies:
|
||||
raise TargetStableError('Can\'t set {} frequency to {}\nmust be in {}'.format(cpu,
|
||||
value,
|
||||
available_frequencies))
|
||||
if self.get_governor(cpu) != 'userspace':
|
||||
if await self.get_governor.asyn(cpu) != 'userspace':
|
||||
raise TargetStableError('Can\'t set {} frequency; governor must be "userspace"'.format(cpu))
|
||||
sysfile = '/sys/devices/system/cpu/{}/cpufreq/scaling_setspeed'.format(cpu)
|
||||
self.target.write_value(sysfile, value, verify=False)
|
||||
cpuinfo = self.get_frequency(cpu, cpuinfo=True)
|
||||
await self.target.write_value.asyn(sysfile, value, verify=False)
|
||||
cpuinfo = await self.get_frequency.asyn(cpu, cpuinfo=True)
|
||||
if cpuinfo != value:
|
||||
self.logger.warning(
|
||||
'The cpufreq value has not been applied properly cpuinfo={} request={}'.format(cpuinfo, value))
|
||||
except ValueError:
|
||||
raise ValueError('Frequency must be an integer; got: "{}"'.format(frequency))
|
||||
|
||||
def get_max_frequency(self, cpu):
|
||||
@asyn.asyncf
|
||||
async def get_max_frequency(self, cpu):
|
||||
"""
|
||||
Returns the max frequency currently set for the specified CPU.
|
||||
|
||||
@ -376,9 +423,10 @@ class CpufreqModule(Module):
|
||||
if isinstance(cpu, int):
|
||||
cpu = 'cpu{}'.format(cpu)
|
||||
sysfile = '/sys/devices/system/cpu/{}/cpufreq/scaling_max_freq'.format(cpu)
|
||||
return self.target.read_int(sysfile)
|
||||
return await self.target.read_int.asyn(sysfile)
|
||||
|
||||
def set_max_frequency(self, cpu, frequency, exact=True):
|
||||
@asyn.asyncf
|
||||
async def set_max_frequency(self, cpu, frequency, exact=True):
|
||||
"""
|
||||
Set's the minimum value for CPU frequency. Actual frequency will
|
||||
depend on the Governor used and may vary during execution. The value should be
|
||||
@ -397,7 +445,7 @@ class CpufreqModule(Module):
|
||||
"""
|
||||
if isinstance(cpu, int):
|
||||
cpu = 'cpu{}'.format(cpu)
|
||||
available_frequencies = self.list_frequencies(cpu)
|
||||
available_frequencies = await self.list_frequencies.asyn(cpu)
|
||||
try:
|
||||
value = int(frequency)
|
||||
if exact and available_frequencies and value not in available_frequencies:
|
||||
@ -405,45 +453,53 @@ class CpufreqModule(Module):
|
||||
value,
|
||||
available_frequencies))
|
||||
sysfile = '/sys/devices/system/cpu/{}/cpufreq/scaling_max_freq'.format(cpu)
|
||||
self.target.write_value(sysfile, value)
|
||||
await self.target.write_value.asyn(sysfile, value)
|
||||
except ValueError:
|
||||
raise ValueError('Frequency must be an integer; got: "{}"'.format(frequency))
|
||||
|
||||
def set_governor_for_cpus(self, cpus, governor, **kwargs):
|
||||
@asyn.asyncf
|
||||
async def set_governor_for_cpus(self, cpus, governor, **kwargs):
|
||||
"""
|
||||
Set the governor for the specified list of CPUs.
|
||||
See https://www.kernel.org/doc/Documentation/cpu-freq/governors.txt
|
||||
|
||||
:param cpus: The list of CPU for which the governor is to be set.
|
||||
"""
|
||||
for cpu in cpus:
|
||||
await self.target.async_manager.map_concurrently(
|
||||
self.set_governor(cpu, governor, **kwargs)
|
||||
for cpu in sorted(set(cpus))
|
||||
)
|
||||
|
||||
def set_frequency_for_cpus(self, cpus, freq, exact=False):
|
||||
@asyn.asyncf
|
||||
async def set_frequency_for_cpus(self, cpus, freq, exact=False):
|
||||
"""
|
||||
Set the frequency for the specified list of CPUs.
|
||||
See https://www.kernel.org/doc/Documentation/cpu-freq/governors.txt
|
||||
|
||||
:param cpus: The list of CPU for which the frequency has to be set.
|
||||
"""
|
||||
for cpu in cpus:
|
||||
await self.target.async_manager.map_concurrently(
|
||||
self.set_frequency(cpu, freq, exact)
|
||||
for cpu in sorted(set(cpus))
|
||||
)
|
||||
|
||||
def set_all_frequencies(self, freq):
|
||||
@asyn.asyncf
|
||||
async def set_all_frequencies(self, freq):
|
||||
"""
|
||||
Set the specified (minimum) frequency for all the (online) CPUs
|
||||
"""
|
||||
# pylint: disable=protected-access
|
||||
return self.target._execute_util(
|
||||
return await self.target._execute_util.asyn(
|
||||
'cpufreq_set_all_frequencies {}'.format(freq),
|
||||
as_root=True)
|
||||
|
||||
def get_all_frequencies(self):
|
||||
@asyn.asyncf
|
||||
async def get_all_frequencies(self):
|
||||
"""
|
||||
Get the current frequency for all the (online) CPUs
|
||||
"""
|
||||
# pylint: disable=protected-access
|
||||
output = self.target._execute_util(
|
||||
output = await self.target._execute_util.asyn(
|
||||
'cpufreq_get_all_frequencies', as_root=True)
|
||||
frequencies = {}
|
||||
for x in output.splitlines():
|
||||
@ -453,32 +509,34 @@ class CpufreqModule(Module):
|
||||
frequencies[kv[0]] = kv[1]
|
||||
return frequencies
|
||||
|
||||
def set_all_governors(self, governor):
|
||||
@asyn.asyncf
|
||||
async def set_all_governors(self, governor):
|
||||
"""
|
||||
Set the specified governor for all the (online) CPUs
|
||||
"""
|
||||
try:
|
||||
# pylint: disable=protected-access
|
||||
return self.target._execute_util(
|
||||
return await self.target._execute_util.asyn(
|
||||
'cpufreq_set_all_governors {}'.format(governor),
|
||||
as_root=True)
|
||||
except TargetStableError as e:
|
||||
if ("echo: I/O error" in str(e) or
|
||||
"write error: Invalid argument" in str(e)):
|
||||
|
||||
cpus_unsupported = [c for c in self.target.list_online_cpus()
|
||||
if governor not in self.list_governors(c)]
|
||||
cpus_unsupported = [c for c in await self.target.list_online_cpus.asyn()
|
||||
if governor not in await self.list_governors.asyn(c)]
|
||||
raise TargetStableError("Governor {} unsupported for CPUs {}".format(
|
||||
governor, cpus_unsupported))
|
||||
else:
|
||||
raise
|
||||
|
||||
def get_all_governors(self):
|
||||
@asyn.asyncf
|
||||
async def get_all_governors(self):
|
||||
"""
|
||||
Get the current governor for all the (online) CPUs
|
||||
"""
|
||||
# pylint: disable=protected-access
|
||||
output = self.target._execute_util(
|
||||
output = await self.target._execute_util.asyn(
|
||||
'cpufreq_get_all_governors', as_root=True)
|
||||
governors = {}
|
||||
for x in output.splitlines():
|
||||
@ -488,14 +546,16 @@ class CpufreqModule(Module):
|
||||
governors[kv[0]] = kv[1]
|
||||
return governors
|
||||
|
||||
def trace_frequencies(self):
|
||||
@asyn.asyncf
|
||||
async def trace_frequencies(self):
|
||||
"""
|
||||
Report current frequencies on trace file
|
||||
"""
|
||||
# pylint: disable=protected-access
|
||||
return self.target._execute_util('cpufreq_trace_all_frequencies', as_root=True)
|
||||
return await self.target._execute_util.asyn('cpufreq_trace_all_frequencies', as_root=True)
|
||||
|
||||
def get_affected_cpus(self, cpu):
|
||||
@asyn.asyncf
|
||||
async def get_affected_cpus(self, cpu):
|
||||
"""
|
||||
Get the online CPUs that share a frequency domain with the given CPU
|
||||
"""
|
||||
@ -504,10 +564,12 @@ class CpufreqModule(Module):
|
||||
|
||||
sysfile = '/sys/devices/system/cpu/{}/cpufreq/affected_cpus'.format(cpu)
|
||||
|
||||
return [int(c) for c in self.target.read_value(sysfile).split()]
|
||||
content = await self.target.read_value.asyn(sysfile)
|
||||
return [int(c) for c in content.split()]
|
||||
|
||||
@memoized
|
||||
def get_related_cpus(self, cpu):
|
||||
@asyn.asyncf
|
||||
@asyn.memoized_method
|
||||
async def get_related_cpus(self, cpu):
|
||||
"""
|
||||
Get the CPUs that share a frequency domain with the given CPU
|
||||
"""
|
||||
@ -516,10 +578,11 @@ class CpufreqModule(Module):
|
||||
|
||||
sysfile = '/sys/devices/system/cpu/{}/cpufreq/related_cpus'.format(cpu)
|
||||
|
||||
return [int(c) for c in self.target.read_value(sysfile).split()]
|
||||
return [int(c) for c in (await self.target.read_value.asyn(sysfile)).split()]
|
||||
|
||||
@memoized
|
||||
def get_driver(self, cpu):
|
||||
@asyn.asyncf
|
||||
@asyn.memoized_method
|
||||
async def get_driver(self, cpu):
|
||||
"""
|
||||
Get the name of the driver used by this cpufreq policy.
|
||||
"""
|
||||
@ -528,15 +591,16 @@ class CpufreqModule(Module):
|
||||
|
||||
sysfile = '/sys/devices/system/cpu/{}/cpufreq/scaling_driver'.format(cpu)
|
||||
|
||||
return self.target.read_value(sysfile).strip()
|
||||
return (await self.target.read_value.asyn(sysfile)).strip()
|
||||
|
||||
def iter_domains(self):
|
||||
@asyn.asyncf
|
||||
async def iter_domains(self):
|
||||
"""
|
||||
Iterate over the frequency domains in the system
|
||||
"""
|
||||
cpus = set(range(self.target.number_of_cpus))
|
||||
while cpus:
|
||||
cpu = next(iter(cpus)) # pylint: disable=stop-iteration-return
|
||||
domain = self.target.cpufreq.get_related_cpus(cpu)
|
||||
domain = await self.target.cpufreq.get_related_cpus.asyn(cpu)
|
||||
yield domain
|
||||
cpus = cpus.difference(domain)
|
||||
|
@ -22,6 +22,7 @@ from devlib.module import Module
|
||||
from devlib.exception import TargetStableError
|
||||
from devlib.utils.types import integer, boolean
|
||||
from devlib.utils.misc import memoized
|
||||
import devlib.utils.asyn as asyn
|
||||
|
||||
|
||||
class CpuidleState(object):
|
||||
@ -59,19 +60,23 @@ class CpuidleState(object):
|
||||
self.id = self.target.path.basename(self.path)
|
||||
self.cpu = self.target.path.basename(self.target.path.dirname(path))
|
||||
|
||||
def enable(self):
|
||||
self.set('disable', 0)
|
||||
@asyn.asyncf
|
||||
async def enable(self):
|
||||
await self.set.asyn('disable', 0)
|
||||
|
||||
def disable(self):
|
||||
self.set('disable', 1)
|
||||
@asyn.asyncf
|
||||
async def disable(self):
|
||||
await self.set.asyn('disable', 1)
|
||||
|
||||
def get(self, prop):
|
||||
@asyn.asyncf
|
||||
async def get(self, prop):
|
||||
property_path = self.target.path.join(self.path, prop)
|
||||
return self.target.read_value(property_path)
|
||||
return await self.target.read_value.asyn(property_path)
|
||||
|
||||
def set(self, prop, value):
|
||||
@asyn.asyncf
|
||||
async def set(self, prop, value):
|
||||
property_path = self.target.path.join(self.path, prop)
|
||||
self.target.write_value(property_path, value)
|
||||
await self.target.write_value.asyn(property_path, value)
|
||||
|
||||
def __eq__(self, other):
|
||||
if isinstance(other, CpuidleState):
|
||||
@ -96,8 +101,9 @@ class Cpuidle(Module):
|
||||
root_path = '/sys/devices/system/cpu/cpuidle'
|
||||
|
||||
@staticmethod
|
||||
def probe(target):
|
||||
return target.file_exists(Cpuidle.root_path)
|
||||
@asyn.asyncf
|
||||
async def probe(target):
|
||||
return await target.file_exists.asyn(Cpuidle.root_path)
|
||||
|
||||
def __init__(self, target):
|
||||
super(Cpuidle, self).__init__(target)
|
||||
@ -148,29 +154,39 @@ class Cpuidle(Module):
|
||||
return s
|
||||
raise ValueError('Cpuidle state {} does not exist'.format(state))
|
||||
|
||||
def enable(self, state, cpu=0):
|
||||
self.get_state(state, cpu).enable()
|
||||
@asyn.asyncf
|
||||
async def enable(self, state, cpu=0):
|
||||
await self.get_state(state, cpu).enable.asyn()
|
||||
|
||||
def disable(self, state, cpu=0):
|
||||
self.get_state(state, cpu).disable()
|
||||
@asyn.asyncf
|
||||
async def disable(self, state, cpu=0):
|
||||
await self.get_state(state, cpu).disable.asyn()
|
||||
|
||||
def enable_all(self, cpu=0):
|
||||
for state in self.get_states(cpu):
|
||||
state.enable()
|
||||
@asyn.asyncf
|
||||
async def enable_all(self, cpu=0):
|
||||
await self.target.async_manager.concurrently(
|
||||
state.enable.asyn()
|
||||
for state in self.get_states(cpu)
|
||||
)
|
||||
|
||||
def disable_all(self, cpu=0):
|
||||
for state in self.get_states(cpu):
|
||||
state.disable()
|
||||
@asyn.asyncf
|
||||
async def disable_all(self, cpu=0):
|
||||
await self.target.async_manager.concurrently(
|
||||
state.disable.asyn()
|
||||
for state in self.get_states(cpu)
|
||||
)
|
||||
|
||||
def perturb_cpus(self):
|
||||
@asyn.asyncf
|
||||
async def perturb_cpus(self):
|
||||
"""
|
||||
Momentarily wake each CPU. Ensures cpu_idle events in trace file.
|
||||
"""
|
||||
# pylint: disable=protected-access
|
||||
self.target._execute_util('cpuidle_wake_all_cpus')
|
||||
await self.target._execute_util.asyn('cpuidle_wake_all_cpus')
|
||||
|
||||
def get_driver(self):
|
||||
return self.target.read_value(self.target.path.join(self.root_path, 'current_driver'))
|
||||
@asyn.asyncf
|
||||
async def get_driver(self):
|
||||
return await self.target.read_value.asyn(self.target.path.join(self.root_path, 'current_driver'))
|
||||
|
||||
@memoized
|
||||
def list_governors(self):
|
||||
@ -179,12 +195,13 @@ class Cpuidle(Module):
|
||||
output = self.target.read_value(sysfile)
|
||||
return output.strip().split()
|
||||
|
||||
def get_governor(self):
|
||||
@asyn.asyncf
|
||||
async def get_governor(self):
|
||||
"""Returns the currently selected idle governor."""
|
||||
path = self.target.path.join(self.root_path, 'current_governor_ro')
|
||||
if not self.target.file_exists(path):
|
||||
if not await self.target.file_exists.asyn(path):
|
||||
path = self.target.path.join(self.root_path, 'current_governor')
|
||||
return self.target.read_value(path)
|
||||
return await self.target.read_value.asyn(path)
|
||||
|
||||
def set_governor(self, governor):
|
||||
"""
|
||||
|
Loading…
x
Reference in New Issue
Block a user