Skip to content

Commit a7f7bda

Browse files
committed
shelldriver: make kernel silencing more reliable
Current fire and forget approach doesn't work reliably in all corner cases for various reasons, like for example serial console bitflip or lost byte issues due to RX fifo overruns. Those issues mostly happen frequently during early boot process, where there is a lot of output going on. So let's make it more reliable by actually checking the kernel log level setting via /proc/sys/kernel/printk and retry if it's not set to desired log level. Following example is from GL.iNet GL-B1300 device: root@(none):/# mg- /bin/ash: mg-: not found root@(none):/# coRC;ct/rcsy/kerne/rnk;eh QZE[ 6.365640] kmodloader: loading kernel modules from /etc/modules.d/* ... dmesg -n 1 /bin/ash: coRC: not found /bin/ash: ct/rcsy/kerne/rnk: not found /bin/ash: eh: not found root@(none):/# echo BZXI; cat /proc/sys/kernel/printk ; echo AUTHSA BZXI 7 4 1 7 AUTHSA root@(none):/# dmesg -n 1 root@(none):/# echo XYTH; cat /proc/sys/kernel/printk ; echo PNCWGM XYTH 1 4 1 7 PNCWGM Signed-off-by: Petr Štetiar <[email protected]>
1 parent 0d77cbd commit a7f7bda

File tree

2 files changed

+31
-1
lines changed

2 files changed

+31
-1
lines changed

doc/usage.rst

+1
Original file line numberDiff line numberDiff line change
@@ -542,6 +542,7 @@ environment config:
542542
'_run',
543543
Mock(return_value=(['OK'], [], 0))
544544
).start()
545+
patch.object(ShellDriver, "_silence_kernel", Mock()).start()
545546

546547
.. testcode:: pytest-example
547548
:hide:

labgrid/driver/shelldriver.py

+30-1
Original file line numberDiff line numberDiff line change
@@ -113,6 +113,35 @@ def _run(self, cmd, *, timeout=30.0, codec="utf-8", decodeerrors="strict"):
113113
def run(self, cmd, timeout=30.0, codec="utf-8", decodeerrors="strict"):
114114
return self._run(cmd, timeout=timeout, codec=codec, decodeerrors=decodeerrors)
115115

116+
def _raw_run(self, cmd, timeout=2, codec="utf-8", decodeerrors="strict"):
117+
"""Runs command without run() wrapper as it's not yet available"""
118+
marker = gen_marker()
119+
self.console.sendline(f"echo {marker[:4]}; {cmd} ; echo {marker[4:]}")
120+
_, _, match, _ = self.console.expect(
121+
rf"{marker[:4]}(.*){marker[4:]}\s+{self.prompt}",
122+
timeout=timeout
123+
)
124+
data = self.re_vt100.sub('', match.group(1).decode(codec, decodeerrors)).split('\r\n')
125+
if data and not data[-1]:
126+
del data[-1]
127+
return data
128+
129+
def _silence_kernel(self, timeout, kernel_log_level=1, codec="utf-8", decodeerrors="strict"):
130+
"""Tries to set kernel log level to KERN_ALERT log level to make it less chatty."""
131+
132+
while True:
133+
self.console.sendline(f"dmesg -n {kernel_log_level}")
134+
try:
135+
data = self._raw_run("cat /proc/sys/kernel/printk")
136+
if data[2] and data[2].startswith(f'{kernel_log_level}\t'):
137+
return
138+
139+
except TIMEOUT:
140+
pass
141+
142+
if timeout.expired:
143+
raise TIMEOUT(f"Timeout of {self.login_timeout} seconds exceeded during waiting for kernel silencer") # pylint: disable=line-too-long
144+
116145
@step()
117146
def _await_login(self):
118147
"""Awaits the login prompt and logs the user in"""
@@ -141,7 +170,7 @@ def _await_login(self):
141170
if index == 0:
142171
if not did_silence_kernel:
143172
# Silence the kernel and wait for another prompt
144-
self.console.sendline("dmesg -n 1")
173+
self._silence_kernel(timeout)
145174
did_silence_kernel = True
146175
else:
147176
# we got a prompt. no need for any further action to

0 commit comments

Comments
 (0)