1
0
mirror of https://github.com/ARM-software/devlib.git synced 2025-04-17 07:10:03 +01:00

Merge pull request #181 from setrofim/master

Module initialization optimizations.
This commit is contained in:
setrofim 2017-10-04 16:23:18 +01:00 committed by GitHub
commit 4d8da589f8
7 changed files with 191 additions and 82 deletions

View File

@ -195,6 +195,35 @@ cgroups_freezer_set_state() {
exit 1 exit 1
} }
################################################################################
# Hotplug
################################################################################
hotplug_online_all() {
PATHS=(/sys/devices/system/cpu/cpu[0-9]*)
for path in "${PATHS[@]}"; do
if [ $(cat $path/online) -eq 0 ]; then
echo 1 > $path/online
fi
done
}
################################################################################
# Misc
################################################################################
read_tree_values() {
PATH=$1
MAXDEPTH=$2
PATHS=$($BUSYBOX find $PATH -follow -maxdepth $MAXDEPTH)
if [ ${#PATHS[@]} -eq 0 ]; then
echo "ERROR: '$1' does not exist"
else
$BUSYBOX grep -s '' $PATHS
fi
}
################################################################################ ################################################################################
# Main Function Dispatcher # Main Function Dispatcher
################################################################################ ################################################################################
@ -236,6 +265,12 @@ cgroups_freezer_set_state)
ftrace_get_function_stats) ftrace_get_function_stats)
ftrace_get_function_stats ftrace_get_function_stats
;; ;;
hotplug_online_all)
hotplug_online_all
;;
read_tree_values)
read_tree_values $*
;;
*) *)
echo "Command [$CMD] not supported" echo "Command [$CMD] not supported"
exit -1 exit -1

View File

@ -56,7 +56,7 @@ class Module(object):
def __init__(self, target): def __init__(self, target):
self.target = target self.target = target
self.logger = logging.getLogger(self.__class__.__name__) self.logger = logging.getLogger(self.name)
class HardRestModule(Module): # pylint: disable=R0921 class HardRestModule(Module): # pylint: disable=R0921

View File

@ -41,51 +41,17 @@ class CpuidleState(object):
raise ValueError('invalid idle state name: "{}"'.format(self.id)) raise ValueError('invalid idle state name: "{}"'.format(self.id))
return int(self.id[i:]) return int(self.id[i:])
def __init__(self, target, index, path): def __init__(self, target, index, path, name, desc, power, latency, residency):
self.target = target self.target = target
self.index = index self.index = index
self.path = path self.path = path
self.name = name
self.desc = desc
self.power = power
self.latency = latency
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))
@property
@memoized
def desc(self):
return self.get('desc')
@property
@memoized
def name(self):
return self.get('name')
@property
@memoized
def latency(self):
"""Exit latency in uS"""
return self.get('latency')
@property
@memoized
def power(self):
"""Power usage in mW
..note::
This value is not always populated by the kernel and may be garbage.
"""
return self.get('power')
@property
@memoized
def target_residency(self):
"""Target residency in uS
This is the amount of time in the state required to 'break even' on
power - the system should avoid entering the state for less time than
this.
"""
return self.get('residency')
def enable(self): def enable(self):
self.set('disable', 0) self.set('disable', 0)
@ -126,23 +92,47 @@ class Cpuidle(Module):
def probe(target): def probe(target):
return target.file_exists(Cpuidle.root_path) return target.file_exists(Cpuidle.root_path)
def get_driver(self): def __init__(self, target):
return self.target.read_value(self.target.path.join(self.root_path, 'current_driver')) super(Cpuidle, self).__init__(target)
self._states = {}
def get_governor(self): basepath = '/sys/devices/system/cpu/'
return self.target.read_value(self.target.path.join(self.root_path, 'current_governor_ro')) values_tree = self.target.read_tree_values(basepath, depth=4, check_exit_code=False)
i = 0
cpu_id = 'cpu{}'.format(i)
while cpu_id in values_tree:
cpu_node = values_tree[cpu_id]
if 'cpuidle' in cpu_node:
idle_node = cpu_node['cpuidle']
self._states[cpu_id] = []
j = 0
state_id = 'state{}'.format(j)
while state_id in idle_node:
state_node = idle_node[state_id]
state = CpuidleState(
self.target,
index=j,
path=self.target.path.join(basepath, cpu_id, 'cpuidle', state_id),
name=state_node['name'],
desc=state_node['desc'],
power=int(state_node['power']),
latency=int(state_node['latency']),
residency=int(state_node['residency']) if 'residency' in state_node else None,
)
msg = 'Adding {} state {}: {} {}'
self.logger.debug(msg.format(cpu_id, j, state.name, state.desc))
self._states[cpu_id].append(state)
j += 1
state_id = 'state{}'.format(j)
i += 1
cpu_id = 'cpu{}'.format(i)
@memoized
def get_states(self, cpu=0): def get_states(self, cpu=0):
if isinstance(cpu, int): if isinstance(cpu, int):
cpu = 'cpu{}'.format(cpu) cpu = 'cpu{}'.format(cpu)
states_dir = self.target.path.join(self.target.path.dirname(self.root_path), cpu, 'cpuidle') return self._states.get(cpu)
idle_states = []
for state in self.target.list_directory(states_dir):
if state.startswith('state'):
index = int(state[5:])
idle_states.append(CpuidleState(self.target, index, self.target.path.join(states_dir, state)))
return idle_states
def get_state(self, state, cpu=0): def get_state(self, state, cpu=0):
if isinstance(state, int): if isinstance(state, int):
@ -176,3 +166,9 @@ class Cpuidle(Module):
""" """
output = self.target._execute_util('cpuidle_wake_all_cpus') output = self.target._execute_util('cpuidle_wake_all_cpus')
print(output) print(output)
def get_driver(self):
return self.target.read_value(self.target.path.join(self.root_path, 'current_driver'))
def get_governor(self):
return self.target.read_value(self.target.path.join(self.root_path, 'current_governor_ro'))

View File

@ -21,7 +21,8 @@ class HotplugModule(Module):
return target.path.join(cls.base_path, cpu, 'online') return target.path.join(cls.base_path, cpu, 'online')
def online_all(self): def online_all(self):
self.online(*range(self.target.number_of_cpus)) self.target._execute_util('hotplug_online_all',
as_root=self.target.is_rooted)
def online(self, *args): def online(self, *args):
for cpu in args: for cpu in args:

View File

@ -12,6 +12,7 @@
# See the License for the specific language governing permissions and # See the License for the specific language governing permissions and
# limitations under the License. # limitations under the License.
# #
import os
import re import re
from collections import defaultdict from collections import defaultdict
@ -78,16 +79,15 @@ class HwmonDevice(object):
all_sensors.extend(sensors_of_kind.values()) all_sensors.extend(sensors_of_kind.values())
return all_sensors return all_sensors
def __init__(self, target, path): def __init__(self, target, path, name, fields):
self.target = target self.target = target
self.path = path self.path = path
self.name = self.target.read_value(self.target.path.join(self.path, 'name')) self.name = name
self._sensors = defaultdict(dict) self._sensors = defaultdict(dict)
path = self.path path = self.path
if not path.endswith(self.target.path.sep): if not path.endswith(self.target.path.sep):
path += self.target.path.sep path += self.target.path.sep
for entry in self.target.list_directory(path, for entry in fields:
as_root=self.target.is_rooted):
match = HWMON_FILE_REGEX.search(entry) match = HWMON_FILE_REGEX.search(entry)
if match: if match:
kind = match.group('kind') kind = match.group('kind')
@ -117,14 +117,11 @@ class HwmonModule(Module):
@staticmethod @staticmethod
def probe(target): def probe(target):
if not target.file_exists(HWMON_ROOT):
return False
try: try:
target.list_directory(HWMON_ROOT, as_root=target.is_rooted) target.list_directory(HWMON_ROOT, as_root=target.is_rooted)
except TargetError: except TargetError:
# Probably no permissions # Doesn't exist or no permissions
return False return False
return True return True
@property @property
@ -141,11 +138,13 @@ class HwmonModule(Module):
self.scan() self.scan()
def scan(self): def scan(self):
for entry in self.target.list_directory(self.root, values_tree = self.target.read_tree_values(self.root, depth=3)
as_root=self.target.is_rooted): for entry_id, fields in values_tree.iteritems():
if entry.startswith('hwmon'): path = self.target.path.join(self.root, entry_id)
entry_path = self.target.path.join(self.root, entry) name = fields.pop('name', None)
if self.target.file_exists(self.target.path.join(entry_path, 'name')): if name is None:
device = HwmonDevice(self.target, entry_path) continue
self.devices.append(device) self.logger.debug('Adding device {}'.format(name))
device = HwmonDevice(self.target, path, name, fields)
self.devices.append(device)

View File

@ -149,6 +149,12 @@ class Target(object):
else: else:
return None return None
@property
def shutils(self):
if self._shutils is None:
self._setup_shutils()
return self._shutils
def __init__(self, def __init__(self,
connection_settings=None, connection_settings=None,
platform=None, platform=None,
@ -189,6 +195,7 @@ class Target(object):
self._installed_modules = {} self._installed_modules = {}
self._cache = {} self._cache = {}
self._connections = {} self._connections = {}
self._shutils = None
self.busybox = None self.busybox = None
if load_default_modules: if load_default_modules:
@ -229,20 +236,7 @@ class Target(object):
self.execute('mkdir -p {}'.format(self.executables_directory)) self.execute('mkdir -p {}'.format(self.executables_directory))
self.busybox = self.install(os.path.join(PACKAGE_BIN_DIRECTORY, self.abi, 'busybox')) self.busybox = self.install(os.path.join(PACKAGE_BIN_DIRECTORY, self.abi, 'busybox'))
# Setup shutils script for the target self._setup_shutils()
shutils_ifile = os.path.join(PACKAGE_BIN_DIRECTORY, 'scripts', 'shutils.in')
shutils_ofile = os.path.join(PACKAGE_BIN_DIRECTORY, 'scripts', 'shutils')
shell_path = '/bin/sh'
if self.os == 'android':
shell_path = '/system/bin/sh'
with open(shutils_ifile) as fh:
lines = fh.readlines()
with open(shutils_ofile, 'w') as ofile:
for line in lines:
line = line.replace("__DEVLIB_SHELL__", shell_path)
line = line.replace("__DEVLIB_BUSYBOX__", self.busybox)
ofile.write(line)
self.shutils = self.install(os.path.join(PACKAGE_BIN_DIRECTORY, 'scripts', 'shutils'))
for host_exe in (executables or []): # pylint: disable=superfluous-parens for host_exe in (executables or []): # pylint: disable=superfluous-parens
self.install(host_exe) self.install(host_exe)
@ -620,8 +614,37 @@ class Target(object):
timeout = duration + 10 timeout = duration + 10
self.execute('sleep {}'.format(duration), timeout=timeout) self.execute('sleep {}'.format(duration), timeout=timeout)
def read_tree_values_flat(self, path, depth=1, check_exit_code=True):
command = 'read_tree_values {} {}'.format(path, depth)
output = self._execute_util(command, as_root=self.is_rooted,
check_exit_code=check_exit_code)
result = {}
for entry in output.strip().split('\n'):
path, value = entry.strip().split(':', 1)
result[path] = value
return result
def read_tree_values(self, path, depth=1, dictcls=dict, check_exit_code=True):
value_map = self.read_tree_values_flat(path, depth, check_exit_code)
return _build_path_tree(value_map, path, self.path.sep, dictcls)
# internal methods # internal methods
def _setup_shutils(self):
shutils_ifile = os.path.join(PACKAGE_BIN_DIRECTORY, 'scripts', 'shutils.in')
shutils_ofile = os.path.join(PACKAGE_BIN_DIRECTORY, 'scripts', 'shutils')
shell_path = '/bin/sh'
if self.os == 'android':
shell_path = '/system/bin/sh'
with open(shutils_ifile) as fh:
lines = fh.readlines()
with open(shutils_ofile, 'w') as ofile:
for line in lines:
line = line.replace("__DEVLIB_SHELL__", shell_path)
line = line.replace("__DEVLIB_BUSYBOX__", self.busybox)
ofile.write(line)
self._shutils = self.install(os.path.join(PACKAGE_BIN_DIRECTORY, 'scripts', 'shutils'))
def _execute_util(self, command, timeout=None, check_exit_code=True, as_root=False): def _execute_util(self, command, timeout=None, check_exit_code=True, as_root=False):
command = '{} {}'.format(self.shutils, command) command = '{} {}'.format(self.shutils, command)
return self.conn.execute(command, timeout, check_exit_code, as_root) return self.conn.execute(command, timeout, check_exit_code, as_root)
@ -1549,3 +1572,32 @@ def _get_part_name(section):
if name is None: if name is None:
name = '{}/{}/{}'.format(implementer, part, variant) name = '{}/{}/{}'.format(implementer, part, variant)
return name return name
def _build_path_tree(path_map, basepath, sep=os.path.sep, dictcls=dict):
"""
Convert a flat mapping of paths to values into a nested structure of
dict-line object (``dict``'s by default), mirroring the directory hierarchy
represented by the paths relative to ``basepath``.
"""
def process_node(node, path, value):
parts = path.split(sep, 1)
if len(parts) == 1: # leaf
node[parts[0]] = value
else: # branch
if parts[0] not in node:
node[parts[0]] = dictcls()
process_node(node[parts[0]], parts[1], value)
relpath_map = {os.path.relpath(p, basepath): v
for p, v in path_map.iteritems()}
if len(relpath_map) == 1 and relpath_map.keys()[0] == '.':
result = relpath_map.values()[0]
else:
result = dictcls()
for path, value in relpath_map.iteritems():
process_node(result, path, value)
return result

View File

@ -327,6 +327,32 @@ Target
some sysfs entries silently failing to set the written value without some sysfs entries silently failing to set the written value without
returning an error code. returning an error code.
.. method:: Target.read_tree_values(path, depth=1, dictcls=dict):
Read values of all sysfs (or similar) file nodes under ``path``, traversing
up to the maximum depth ``depth``.
Returns a nested structure of dict-like objects (``dict``\ s by default) that
follows the structure of the scanned sub-directory tree. The top-level entry
has a single item who's key is ``path``. If ``path`` points to a single file,
the value of the entry is the value ready from that file node. Otherwise, the
value is a dict-line object with a key for every entry under ``path``
mapping onto its value or further dict-like objects as appropriate.
:param path: sysfs path to scan
:param depth: maximum depth to descend
:param dictcls: a dict-like type to be used for each level of the hierarchy.
.. method:: Target.read_tree_values_flat(path, depth=1):
Read values of all sysfs (or similar) file nodes under ``path``, traversing
up to the maximum depth ``depth``.
Returns a dict mapping paths of file nodes to corresponding values.
:param path: sysfs path to scan
:param depth: maximum depth to descend
.. method:: Target.reset() .. method:: Target.reset()
Soft reset the target. Typically, this means executing ``reboot`` on the Soft reset the target. Typically, this means executing ``reboot`` on the