1
0
mirror of https://github.com/ARM-software/devlib.git synced 2025-01-31 02:00:45 +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.host import PACKAGE_BIN_DIRECTORY
from devlib.exception import TargetStableError, HostError from devlib.exception import TargetStableError, HostError
from devlib.utils.misc import check_output, which, memoized from devlib.utils.misc import check_output, which, memoized
from devlib.utils.asyn import asyncf
TRACE_MARKER_START = 'TRACE_MARKER_START' 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.target.write_value(self.function_profile_file, 0, verify=False)
self._reset_needed = False self._reset_needed = False
def start(self): @asyncf
async def start(self):
self.start_time = time.time() self.start_time = time.time()
if self._reset_needed: if self._reset_needed:
self.reset() self.reset()
@ -282,14 +284,17 @@ class FtraceCollector(CollectorBase):
self.target.cpuidle.perturb_cpus() self.target.cpuidle.perturb_cpus()
# Enable kernel function profiling # Enable kernel function profiling
if self.functions and self.tracer is None: if self.functions and self.tracer is None:
self.target.execute('echo nop > {}'.format(self.current_tracer_file), target = self.target
as_root=True) await target.async_manager.concurrently(
self.target.execute('echo 0 > {}'.format(self.function_profile_file), execute.asyn('echo nop > {}'.format(self.current_tracer_file),
as_root=True) as_root=True),
self.target.execute('echo {} > {}'.format(self.function_string, self.ftrace_filter_file), execute.asyn('echo 0 > {}'.format(self.function_profile_file),
as_root=True) as_root=True),
self.target.execute('echo 1 > {}'.format(self.function_profile_file), execute.asyn('echo {} > {}'.format(self.function_string, self.ftrace_filter_file),
as_root=True) as_root=True),
execute.asyn('echo 1 > {}'.format(self.function_profile_file),
as_root=True),
)
def stop(self): def stop(self):

View File

@ -19,11 +19,13 @@ from collections import namedtuple
from shlex import quote from shlex import quote
import itertools import itertools
import warnings import warnings
import asyncio
from devlib.module import Module from devlib.module import Module
from devlib.exception import TargetStableError from devlib.exception import TargetStableError
from devlib.utils.misc import list_to_ranges, isiterable from devlib.utils.misc import list_to_ranges, isiterable
from devlib.utils.types import boolean from devlib.utils.types import boolean
from devlib.utils.asyn import asyncf
class Controller(object): class Controller(object):
@ -55,7 +57,8 @@ class Controller(object):
self.mount_point = None self.mount_point = None
self._cgroups = {} self._cgroups = {}
def mount(self, target, mount_root): @asyncf
async def mount(self, target, mount_root):
mounted = target.list_file_systems() mounted = target.list_file_systems()
if self.mount_name in [e.device for e in mounted]: if self.mount_name in [e.device for e in mounted]:
@ -68,16 +71,16 @@ class Controller(object):
else: else:
# Mount the controller if not already in use # Mount the controller if not already in use
self.mount_point = target.path.join(mount_root, self.mount_name) 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) .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), .format(','.join(self.clist),
self.mount_name, self.mount_name,
self.mount_point), self.mount_point),
as_root=True) as_root=True)
# Check if this controller uses "noprefix" option # 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: if 'noprefix' in output:
self._noprefix = True self._noprefix = True
# self.logger.debug('Controller %s using "noprefix" option', # self.logger.debug('Controller %s using "noprefix" option',
@ -394,11 +397,12 @@ class CgroupsModule(Module):
# Initialize controllers # Initialize controllers
self.logger.info('Available controllers:') self.logger.info('Available controllers:')
self.controllers = {} self.controllers = {}
for ss in subsys:
async def register_controller(ss):
hid = ss.hierarchy hid = ss.hierarchy
controller = Controller(ss.name, hid, hierarchy[hid]) controller = Controller(ss.name, hid, hierarchy[hid])
try: try:
controller.mount(self.target, self.cgroup_root) await controller.mount.asyn(self.target, self.cgroup_root)
except TargetStableError: except TargetStableError:
message = 'Failed to mount "{}" controller' message = 'Failed to mount "{}" controller'
raise TargetStableError(message.format(controller.kind)) raise TargetStableError(message.format(controller.kind))
@ -406,6 +410,14 @@ class CgroupsModule(Module):
controller.mount_point) controller.mount_point)
self.controllers[ss.name] = controller self.controllers[ss.name] = controller
asyncio.run(
target.async_manager.map_concurrently(
register_controller,
subsys,
)
)
def list_subsystems(self): def list_subsystems(self):
subsystems = [] subsystems = []
for line in self.target.execute('{} cat /proc/cgroups'\ for line in self.target.execute('{} cat /proc/cgroups'\

View File

@ -13,10 +13,12 @@
# limitations under the License. # limitations under the License.
# #
from contextlib import contextmanager from contextlib import contextmanager
from operator import itemgetter
from devlib.module import Module from devlib.module import Module
from devlib.exception import TargetStableError from devlib.exception import TargetStableError
from devlib.utils.misc import memoized 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 # 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' name = 'cpufreq'
@staticmethod @staticmethod
def probe(target): @asyn.asyncf
async def probe(target):
paths = [
# x86 with Intel P-State driver # x86 with Intel P-State driver
if target.abi == 'x86_64': (target.abi == 'x86_64', '/sys/devices/system/cpu/intel_pstate'),
path = '/sys/devices/system/cpu/intel_pstate'
if target.file_exists(path):
return True
# Generic CPUFreq support (single policy) # Generic CPUFreq support (single policy)
path = '/sys/devices/system/cpu/cpufreq/policy0' (True, '/sys/devices/system/cpu/cpufreq/policy0'),
if target.file_exists(path):
return True
# Generic CPUFreq support (per CPU policy) # Generic CPUFreq support (per CPU policy)
path = '/sys/devices/system/cpu/cpu0/cpufreq' (True, '/sys/devices/system/cpu/cpu0/cpufreq'),
return target.file_exists(path) ]
paths = [
path[1] for path in paths
if path[0]
]
exists = await target.async_manager.map_concurrently(
target.file_exists.asyn,
paths,
)
return any(exists.values())
def __init__(self, target): def __init__(self, target):
super(CpufreqModule, self).__init__(target) super(CpufreqModule, self).__init__(target)
self._governor_tunables = {} self._governor_tunables = {}
@memoized @memoized
def list_governors(self, cpu): @asyn.asyncf
async def list_governors(self, cpu):
"""Returns a list of governors supported by the cpu.""" """Returns a list of governors supported by the cpu."""
if isinstance(cpu, int): if isinstance(cpu, int):
cpu = 'cpu{}'.format(cpu) cpu = 'cpu{}'.format(cpu)
sysfile = '/sys/devices/system/cpu/{}/cpufreq/scaling_available_governors'.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() 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.""" """Returns the governor currently set for the specified CPU."""
if isinstance(cpu, int): if isinstance(cpu, int):
cpu = 'cpu{}'.format(cpu) cpu = 'cpu{}'.format(cpu)
sysfile = '/sys/devices/system/cpu/{}/cpufreq/scaling_governor'.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. Set the governor for the specified CPU.
See https://www.kernel.org/doc/Documentation/cpu-freq/governors.txt See https://www.kernel.org/doc/Documentation/cpu-freq/governors.txt
@ -90,15 +100,15 @@ class CpufreqModule(Module):
""" """
if isinstance(cpu, int): if isinstance(cpu, int):
cpu = 'cpu{}'.format(cpu) cpu = 'cpu{}'.format(cpu)
supported = self.list_governors(cpu) supported = await self.list_governors.asyn(cpu)
if governor not in supported: if governor not in supported:
raise TargetStableError('Governor {} not supported for cpu {}'.format(governor, cpu)) raise TargetStableError('Governor {} not supported for cpu {}'.format(governor, cpu))
sysfile = '/sys/devices/system/cpu/{}/cpufreq/scaling_governor'.format(cpu) sysfile = '/sys/devices/system/cpu/{}/cpufreq/scaling_governor'.format(cpu)
self.target.write_value(sysfile, governor) await self.target.write_value.asyn(sysfile, governor)
self.set_governor_tunables(cpu, governor, **kwargs) return await self.set_governor_tunables.asyn(cpu, governor, **kwargs)
@contextmanager @asyn.asynccontextmanager
def use_governor(self, governor, cpus=None, **kwargs): async def use_governor(self, governor, cpus=None, **kwargs):
""" """
Use a given governor, then restore previous governor(s) 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` :Keyword Arguments: Governor tunables, See :meth:`set_governor_tunables`
""" """
if not cpus: 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 async def get_cpu_info(cpu):
# in the same clock domain, so only manipulating one cpu per domain return await self.target.async_manager.concurrently((
# is enough self.get_affected_cpus.asyn(cpu),
domains = set(self.get_affected_cpus(cpu)[0] for cpu in cpus) self.get_governor.asyn(cpu),
prev_governors = {cpu : (self.get_governor(cpu), self.get_governor_tunables(cpu)) self.get_governor_tunables.asyn(cpu),
for cpu in domains} # 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 cpus_infos = await self.target.async_manager.map_concurrently(get_cpu_info, cpus)
userspace_freqs = {}
for cpu, (prev_gov, _) in prev_governors.items():
if prev_gov == "userspace":
userspace_freqs[cpu] = self.get_frequency(cpu)
for cpu in domains: # Setting a governor & tunables for a cpu will set them for all cpus in
self.set_governor(cpu, governor, **kwargs) # 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: try:
yield yield
finally: finally:
for cpu, (prev_gov, tunables) in prev_governors.items(): async def set_gov(cpu):
self.set_governor(cpu, prev_gov, **tunables) 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": 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.""" """Returns a list of tunables available for the governor on the specified CPU."""
if isinstance(cpu, int): if isinstance(cpu, int):
cpu = 'cpu{}'.format(cpu) cpu = 'cpu{}'.format(cpu)
governor = self.get_governor(cpu) governor = await self.get_governor.asyn(cpu)
if governor not in self._governor_tunables: if governor not in self._governor_tunables:
try: try:
tunables_path = '/sys/devices/system/cpu/{}/cpufreq/{}'.format(cpu, governor) 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 except TargetStableError: # probably an older kernel
try: try:
tunables_path = '/sys/devices/system/cpu/cpufreq/{}'.format(governor) 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 except TargetStableError: # governor does not support tunables
self._governor_tunables[governor] = [] self._governor_tunables[governor] = []
return 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): if isinstance(cpu, int):
cpu = 'cpu{}'.format(cpu) 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 = {} tunables = {}
for tunable in self.list_governor_tunables(cpu): async def get_tunable(tunable):
if tunable not in WRITE_ONLY_TUNABLES.get(governor, []):
try: try:
path = '/sys/devices/system/cpu/{}/cpufreq/{}/{}'.format(cpu, governor, tunable) path = '/sys/devices/system/cpu/{}/cpufreq/{}/{}'.format(cpu, governor, tunable)
tunables[tunable] = self.target.read_value(path) x = await self.target.read_value.asyn(path)
except TargetStableError: # May be an older kernel except TargetStableError: # May be an older kernel
path = '/sys/devices/system/cpu/cpufreq/{}/{}'.format(governor, tunable) path = '/sys/devices/system/cpu/cpufreq/{}/{}'.format(governor, tunable)
tunables[tunable] = self.target.read_value(path) x = await self.target.read_value.asyn(path)
return x
tunables = await self.target.async_manager.map_concurrently(get_tunable, tunable_list)
return tunables 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 Set tunables for the specified governor. Tunables should be specified as
keyword arguments. Which tunables and values are valid depends on the keyword arguments. Which tunables and values are valid depends on the
@ -191,34 +232,35 @@ class CpufreqModule(Module):
if isinstance(cpu, int): if isinstance(cpu, int):
cpu = 'cpu{}'.format(cpu) cpu = 'cpu{}'.format(cpu)
if governor is None: if governor is None:
governor = self.get_governor(cpu) governor = await self.get_governor.asyn(cpu)
valid_tunables = self.list_governor_tunables(cpu) valid_tunables = await self.list_governor_tunables.asyn(cpu)
for tunable, value in kwargs.items(): for tunable, value in kwargs.items():
if tunable in valid_tunables: if tunable in valid_tunables:
path = '/sys/devices/system/cpu/{}/cpufreq/{}/{}'.format(cpu, governor, tunable) path = '/sys/devices/system/cpu/{}/cpufreq/{}/{}'.format(cpu, governor, tunable)
try: try:
self.target.write_value(path, value) await self.target.write_value.asyn(path, value)
except TargetStableError: except TargetStableError:
if self.target.file_exists(path): if await self.target.file_exists.asyn(path):
# File exists but we did something wrong # File exists but we did something wrong
raise raise
# Expected file doesn't exist, try older sysfs layout. # Expected file doesn't exist, try older sysfs layout.
path = '/sys/devices/system/cpu/cpufreq/{}/{}'.format(governor, tunable) path = '/sys/devices/system/cpu/cpufreq/{}/{}'.format(governor, tunable)
self.target.write_value(path, value) await self.target.write_value.asyn(path, value)
else: else:
message = 'Unexpected tunable {} for governor {} on {}.\n'.format(tunable, governor, cpu) message = 'Unexpected tunable {} for governor {} on {}.\n'.format(tunable, governor, cpu)
message += 'Available tunables are: {}'.format(valid_tunables) message += 'Available tunables are: {}'.format(valid_tunables)
raise TargetStableError(message) raise TargetStableError(message)
@memoized @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 """Returns a sorted list of frequencies supported by the cpu or an empty list
if not could be found.""" if not could be found."""
if isinstance(cpu, int): if isinstance(cpu, int):
cpu = 'cpu{}'.format(cpu) cpu = 'cpu{}'.format(cpu)
try: try:
cmd = 'cat /sys/devices/system/cpu/{}/cpufreq/scaling_available_frequencies'.format(cpu) 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 available_frequencies = list(map(int, output.strip().split())) # pylint: disable=E1103
except TargetStableError: except TargetStableError:
# On some devices scaling_frequencies is not generated. # On some devices scaling_frequencies is not generated.
@ -226,7 +268,7 @@ class CpufreqModule(Module):
# Fall back to parsing stats/time_in_state # Fall back to parsing stats/time_in_state
path = '/sys/devices/system/cpu/{}/cpufreq/stats/time_in_state'.format(cpu) path = '/sys/devices/system/cpu/{}/cpufreq/stats/time_in_state'.format(cpu)
try: try:
out_iter = iter(self.target.read_value(path).split()) out_iter = (await self.target.read_value.asyn(path)).split()
except TargetStableError: except TargetStableError:
if not self.target.file_exists(path): if not self.target.file_exists(path):
# Probably intel_pstate. Can't get available freqs. # Probably intel_pstate. Can't get available freqs.
@ -254,7 +296,8 @@ class CpufreqModule(Module):
freqs = self.list_frequencies(cpu) freqs = self.list_frequencies(cpu)
return min(freqs) if freqs else None 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. Returns the min frequency currently set for the specified CPU.
@ -268,9 +311,10 @@ class CpufreqModule(Module):
if isinstance(cpu, int): if isinstance(cpu, int):
cpu = 'cpu{}'.format(cpu) cpu = 'cpu{}'.format(cpu)
sysfile = '/sys/devices/system/cpu/{}/cpufreq/scaling_min_freq'.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 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 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): if isinstance(cpu, int):
cpu = 'cpu{}'.format(cpu) cpu = 'cpu{}'.format(cpu)
available_frequencies = self.list_frequencies(cpu) available_frequencies = await self.list_frequencies.asyn(cpu)
try: try:
value = int(frequency) value = int(frequency)
if exact and available_frequencies and value not in available_frequencies: if exact and available_frequencies and value not in available_frequencies:
@ -297,11 +341,12 @@ class CpufreqModule(Module):
value, value,
available_frequencies)) available_frequencies))
sysfile = '/sys/devices/system/cpu/{}/cpufreq/scaling_min_freq'.format(cpu) 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: except ValueError:
raise ValueError('Frequency must be an integer; got: "{}"'.format(frequency)) 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. Returns the current frequency currently set for the specified CPU.
@ -321,9 +366,10 @@ class CpufreqModule(Module):
sysfile = '/sys/devices/system/cpu/{}/cpufreq/{}'.format( sysfile = '/sys/devices/system/cpu/{}/cpufreq/{}'.format(
cpu, cpu,
'cpuinfo_cur_freq' if cpuinfo else 'scaling_cur_freq') '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 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 depend on the Governor used and may vary during execution. The value should be
@ -347,23 +393,24 @@ class CpufreqModule(Module):
try: try:
value = int(frequency) value = int(frequency)
if exact: 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: if available_frequencies and value not in available_frequencies:
raise TargetStableError('Can\'t set {} frequency to {}\nmust be in {}'.format(cpu, raise TargetStableError('Can\'t set {} frequency to {}\nmust be in {}'.format(cpu,
value, value,
available_frequencies)) 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)) raise TargetStableError('Can\'t set {} frequency; governor must be "userspace"'.format(cpu))
sysfile = '/sys/devices/system/cpu/{}/cpufreq/scaling_setspeed'.format(cpu) sysfile = '/sys/devices/system/cpu/{}/cpufreq/scaling_setspeed'.format(cpu)
self.target.write_value(sysfile, value, verify=False) await self.target.write_value.asyn(sysfile, value, verify=False)
cpuinfo = self.get_frequency(cpu, cpuinfo=True) cpuinfo = await self.get_frequency.asyn(cpu, cpuinfo=True)
if cpuinfo != value: if cpuinfo != value:
self.logger.warning( self.logger.warning(
'The cpufreq value has not been applied properly cpuinfo={} request={}'.format(cpuinfo, value)) 'The cpufreq value has not been applied properly cpuinfo={} request={}'.format(cpuinfo, value))
except ValueError: except ValueError:
raise ValueError('Frequency must be an integer; got: "{}"'.format(frequency)) 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. Returns the max frequency currently set for the specified CPU.
@ -376,9 +423,10 @@ class CpufreqModule(Module):
if isinstance(cpu, int): if isinstance(cpu, int):
cpu = 'cpu{}'.format(cpu) cpu = 'cpu{}'.format(cpu)
sysfile = '/sys/devices/system/cpu/{}/cpufreq/scaling_max_freq'.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 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 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): if isinstance(cpu, int):
cpu = 'cpu{}'.format(cpu) cpu = 'cpu{}'.format(cpu)
available_frequencies = self.list_frequencies(cpu) available_frequencies = await self.list_frequencies.asyn(cpu)
try: try:
value = int(frequency) value = int(frequency)
if exact and available_frequencies and value not in available_frequencies: if exact and available_frequencies and value not in available_frequencies:
@ -405,45 +453,53 @@ class CpufreqModule(Module):
value, value,
available_frequencies)) available_frequencies))
sysfile = '/sys/devices/system/cpu/{}/cpufreq/scaling_max_freq'.format(cpu) 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: except ValueError:
raise ValueError('Frequency must be an integer; got: "{}"'.format(frequency)) 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. Set the governor for the specified list of CPUs.
See https://www.kernel.org/doc/Documentation/cpu-freq/governors.txt 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. :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) 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. Set the frequency for the specified list of CPUs.
See https://www.kernel.org/doc/Documentation/cpu-freq/governors.txt 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. :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) 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 Set the specified (minimum) frequency for all the (online) CPUs
""" """
# pylint: disable=protected-access # pylint: disable=protected-access
return self.target._execute_util( return await self.target._execute_util.asyn(
'cpufreq_set_all_frequencies {}'.format(freq), 'cpufreq_set_all_frequencies {}'.format(freq),
as_root=True) 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 Get the current frequency for all the (online) CPUs
""" """
# pylint: disable=protected-access # pylint: disable=protected-access
output = self.target._execute_util( output = await self.target._execute_util.asyn(
'cpufreq_get_all_frequencies', as_root=True) 'cpufreq_get_all_frequencies', as_root=True)
frequencies = {} frequencies = {}
for x in output.splitlines(): for x in output.splitlines():
@ -453,32 +509,34 @@ class CpufreqModule(Module):
frequencies[kv[0]] = kv[1] frequencies[kv[0]] = kv[1]
return frequencies 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 Set the specified governor for all the (online) CPUs
""" """
try: try:
# pylint: disable=protected-access # pylint: disable=protected-access
return self.target._execute_util( return await self.target._execute_util.asyn(
'cpufreq_set_all_governors {}'.format(governor), 'cpufreq_set_all_governors {}'.format(governor),
as_root=True) as_root=True)
except TargetStableError as e: except TargetStableError as e:
if ("echo: I/O error" in str(e) or if ("echo: I/O error" in str(e) or
"write error: Invalid argument" in str(e)): "write error: Invalid argument" in str(e)):
cpus_unsupported = [c for c in self.target.list_online_cpus() cpus_unsupported = [c for c in await self.target.list_online_cpus.asyn()
if governor not in self.list_governors(c)] if governor not in await self.list_governors.asyn(c)]
raise TargetStableError("Governor {} unsupported for CPUs {}".format( raise TargetStableError("Governor {} unsupported for CPUs {}".format(
governor, cpus_unsupported)) governor, cpus_unsupported))
else: else:
raise raise
def get_all_governors(self): @asyn.asyncf
async def get_all_governors(self):
""" """
Get the current governor for all the (online) CPUs Get the current governor for all the (online) CPUs
""" """
# pylint: disable=protected-access # pylint: disable=protected-access
output = self.target._execute_util( output = await self.target._execute_util.asyn(
'cpufreq_get_all_governors', as_root=True) 'cpufreq_get_all_governors', as_root=True)
governors = {} governors = {}
for x in output.splitlines(): for x in output.splitlines():
@ -488,14 +546,16 @@ class CpufreqModule(Module):
governors[kv[0]] = kv[1] governors[kv[0]] = kv[1]
return governors return governors
def trace_frequencies(self): @asyn.asyncf
async def trace_frequencies(self):
""" """
Report current frequencies on trace file Report current frequencies on trace file
""" """
# pylint: disable=protected-access # 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 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) 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 @asyn.asyncf
def get_related_cpus(self, cpu): @asyn.memoized_method
async def get_related_cpus(self, cpu):
""" """
Get the CPUs that share a frequency domain with the given 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) 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 @asyn.asyncf
def get_driver(self, cpu): @asyn.memoized_method
async def get_driver(self, cpu):
""" """
Get the name of the driver used by this cpufreq policy. 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) 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 Iterate over the frequency domains in the system
""" """
cpus = set(range(self.target.number_of_cpus)) cpus = set(range(self.target.number_of_cpus))
while cpus: while cpus:
cpu = next(iter(cpus)) # pylint: disable=stop-iteration-return 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 yield domain
cpus = cpus.difference(domain) cpus = cpus.difference(domain)

View File

@ -22,6 +22,7 @@ from devlib.module import Module
from devlib.exception import TargetStableError from devlib.exception import TargetStableError
from devlib.utils.types import integer, boolean from devlib.utils.types import integer, boolean
from devlib.utils.misc import memoized from devlib.utils.misc import memoized
import devlib.utils.asyn as asyn
class CpuidleState(object): class CpuidleState(object):
@ -59,19 +60,23 @@ class CpuidleState(object):
self.id = self.target.path.basename(self.path) self.id = self.target.path.basename(self.path)
self.cpu = self.target.path.basename(self.target.path.dirname(path)) self.cpu = self.target.path.basename(self.target.path.dirname(path))
def enable(self): @asyn.asyncf
self.set('disable', 0) async def enable(self):
await self.set.asyn('disable', 0)
def disable(self): @asyn.asyncf
self.set('disable', 1) 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) 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) 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): def __eq__(self, other):
if isinstance(other, CpuidleState): if isinstance(other, CpuidleState):
@ -96,8 +101,9 @@ class Cpuidle(Module):
root_path = '/sys/devices/system/cpu/cpuidle' root_path = '/sys/devices/system/cpu/cpuidle'
@staticmethod @staticmethod
def probe(target): @asyn.asyncf
return target.file_exists(Cpuidle.root_path) async def probe(target):
return await target.file_exists.asyn(Cpuidle.root_path)
def __init__(self, target): def __init__(self, target):
super(Cpuidle, self).__init__(target) super(Cpuidle, self).__init__(target)
@ -148,29 +154,39 @@ class Cpuidle(Module):
return s return s
raise ValueError('Cpuidle state {} does not exist'.format(state)) raise ValueError('Cpuidle state {} does not exist'.format(state))
def enable(self, state, cpu=0): @asyn.asyncf
self.get_state(state, cpu).enable() async def enable(self, state, cpu=0):
await self.get_state(state, cpu).enable.asyn()
def disable(self, state, cpu=0): @asyn.asyncf
self.get_state(state, cpu).disable() async def disable(self, state, cpu=0):
await self.get_state(state, cpu).disable.asyn()
def enable_all(self, cpu=0): @asyn.asyncf
for state in self.get_states(cpu): async def enable_all(self, cpu=0):
state.enable() await self.target.async_manager.concurrently(
state.enable.asyn()
for state in self.get_states(cpu)
)
def disable_all(self, cpu=0): @asyn.asyncf
for state in self.get_states(cpu): async def disable_all(self, cpu=0):
state.disable() 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. Momentarily wake each CPU. Ensures cpu_idle events in trace file.
""" """
# pylint: disable=protected-access # 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): @asyn.asyncf
return self.target.read_value(self.target.path.join(self.root_path, 'current_driver')) async def get_driver(self):
return await self.target.read_value.asyn(self.target.path.join(self.root_path, 'current_driver'))
@memoized @memoized
def list_governors(self): def list_governors(self):
@ -179,12 +195,13 @@ class Cpuidle(Module):
output = self.target.read_value(sysfile) output = self.target.read_value(sysfile)
return output.strip().split() return output.strip().split()
def get_governor(self): @asyn.asyncf
async def get_governor(self):
"""Returns the currently selected idle governor.""" """Returns the currently selected idle governor."""
path = self.target.path.join(self.root_path, 'current_governor_ro') 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') 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): def set_governor(self, governor):
""" """