1
0
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:
Douglas Raillard 2021-11-15 14:47:16 +00:00 committed by Marc Bonnici
parent 18ab9f80b0
commit 2c4b16f280
4 changed files with 248 additions and 150 deletions

View File

@ -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):

View File

@ -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'\

View File

@ -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)

View File

@ -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):
"""