From 2c4b16f280296bc9ab6744f2ffef5fd7d6219640 Mon Sep 17 00:00:00 2001
From: Douglas Raillard <douglas.raillard@arm.com>
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):
         """