From 2c4b16f280296bc9ab6744f2ffef5fd7d6219640 Mon Sep 17 00:00:00 2001 From: Douglas Raillard Date: Mon, 15 Nov 2021 14:47:16 +0000 Subject: [PATCH] devlib: Use async Target API Make use of the new async API to speedup other parts of devlib. --- devlib/collector/ftrace.py | 23 +-- devlib/module/cgroups.py | 24 +++- devlib/module/cpufreq.py | 280 +++++++++++++++++++++++-------------- devlib/module/cpuidle.py | 71 ++++++---- 4 files changed, 248 insertions(+), 150 deletions(-) diff --git a/devlib/collector/ftrace.py b/devlib/collector/ftrace.py index 9849f4e..292aa82 100644 --- a/devlib/collector/ftrace.py +++ b/devlib/collector/ftrace.py @@ -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): diff --git a/devlib/module/cgroups.py b/devlib/module/cgroups.py index b3cdb1d..ece52c2 100644 --- a/devlib/module/cgroups.py +++ b/devlib/module/cgroups.py @@ -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'\ diff --git a/devlib/module/cpufreq.py b/devlib/module/cpufreq.py index f559ef6..f147d41 100644 --- a/devlib/module/cpufreq.py +++ b/devlib/module/cpufreq.py @@ -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) diff --git a/devlib/module/cpuidle.py b/devlib/module/cpuidle.py index 7b1704c..a7d0fef 100644 --- a/devlib/module/cpuidle.py +++ b/devlib/module/cpuidle.py @@ -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): """