Skip to content

Commit

Permalink
target: Speedup Target.write_value()
Browse files Browse the repository at this point in the history
Avoid an execute() by doing the check in the same command. This also
allows to return early if the write is fast, and to extend for longer if
the write is slow. The speed at which you can observe a write in sysfs
depends on the backing kernel handlers, so there is a wide variety of
situations.

Also, make a more fine grained error detection by allowing the write
itself to fail, which can happen when writing invalid values to sysfs.
  • Loading branch information
douglas-raillard-arm authored and marcbonnici committed Oct 8, 2021
1 parent 0c18787 commit e979baf
Showing 1 changed file with 37 additions and 5 deletions.
42 changes: 37 additions & 5 deletions devlib/target.py
Original file line number Diff line number Diff line change
Expand Up @@ -50,7 +50,7 @@
from devlib.exception import (DevlibTransientError, TargetStableError,
TargetNotRespondingError, TimeoutError,
TargetTransientError, KernelConfigKeyError,
TargetError, HostError) # pylint: disable=redefined-builtin
TargetError, HostError, TargetCalledProcessError) # pylint: disable=redefined-builtin
from devlib.utils.ssh import SshConnection
from devlib.utils.android import AdbConnection, AndroidProperties, LogcatMonitor, adb_command, adb_disconnect, INTENT_FLAGS
from devlib.utils.misc import memoized, isiterable, convert_new_lines, groupby_value
Expand Down Expand Up @@ -848,12 +848,44 @@ def batch_revertable_write_value(self, kwargs_list):

def write_value(self, path, value, verify=True):
value = str(value)
self.execute('echo {} > {}'.format(quote(value), quote(path)), check_exit_code=False, as_root=True)

if verify:
output = self.read_value(path)
if not output == value:
message = 'Could not set the value of {} to "{}" (read "{}")'.format(path, value, output)
# Check in a loop for a while since updates to sysfs files can take
# some time to be observed, typically when a write triggers a
# lengthy kernel-side request, and the read is based on some piece
# of state that may take some time to be updated by the write
# request, such as hotplugging a CPU.
cmd = '''
orig=$(cat {path} 2>/dev/null || printf "")
printf "%s" {value} > {path} || exit 10
if [ {value} != "$orig" ]; then
trials=0
while [ "$(cat {path} 2>/dev/null)" != {value} ]; do
if [ $trials -ge 10 ]; then
cat {path}
exit 11
fi
sleep 0.01
trials=$((trials + 1))
done
fi
'''
else:
cmd = '{busybox} printf "%s" {value} > {path}'
cmd = cmd.format(busybox=quote(self.busybox), path=quote(path), value=quote(value))

try:
self.execute(cmd, check_exit_code=True, as_root=True)
except TargetCalledProcessError as e:
if e.returncode == 10:
raise TargetStableError('Could not write "{value}" to {path}: {e.output}'.format(
value=value, path=path, e=e))
elif verify and e.returncode == 11:
out = e.output
message = 'Could not set the value of {} to "{}" (read "{}")'.format(path, value, out)
raise TargetStableError(message)
else:
raise

def reset(self):
try:
Expand Down

0 comments on commit e979baf

Please sign in to comment.