From 8602289225135fba7f3be943ff6905c03e94def9 Mon Sep 17 00:00:00 2001 From: Douglas Raillard Date: Thu, 30 Jan 2025 12:20:52 +0000 Subject: [PATCH 1/3] lisa._kmod: Fix pre-installed module detection FIX Ensure we deal correctly with the case where no pre-installed module exists, rather than attempting "insmod" with an empty path passed to it. --- lisa/_kmod.py | 29 +++++++++++++++++------------ 1 file changed, 17 insertions(+), 12 deletions(-) diff --git a/lisa/_kmod.py b/lisa/_kmod.py index ba2c7cc7f..4769a6a9c 100644 --- a/lisa/_kmod.py +++ b/lisa/_kmod.py @@ -3434,18 +3434,23 @@ class LISADynamicKmod(FtraceDynamicKmod): return preinstalled_unsuitable() else: kmod_path = kmod_path.strip() - if len((kmod_paths := kmod_path.splitlines())) > 1: - return preinstalled_unsuitable(ValueError(f'Multiple paths found for {kmod_filename}: {", ".join(kmod_paths)}')) - else: - # We found an installed module that could maybe be suitable, so - # we try to load it. - try: - return self._install(nullcontext(kmod_path), kmod_params=kmod_params) - except (subprocess.CalledProcessError, KmodVersionError) as e: - # Turns out to not be suitable, so we build our own - return preinstalled_unsuitable(e) + if kmod_path: + if len((kmod_paths := kmod_path.splitlines())) > 1: + return preinstalled_unsuitable(ValueError(f'Multiple paths found for {kmod_filename}: {", ".join(kmod_paths)}')) else: - logger.warning(f'Loaded "{self.mod_name}" module from pre-installed location: {kmod_path}. This implies that the module was compiled by a 3rd party, which is available but unsupported. If you experience issues related to module version mismatch in the future, please contact them for updating the module. This may break at any time, without notice, and regardless of the general backward compatibility policy of LISA.') - return None + # We found an installed module that could maybe be suitable, so + # we try to load it. + try: + return self._install(nullcontext(kmod_path), kmod_params=kmod_params) + except (subprocess.CalledProcessError, KmodVersionError) as e: + # Turns out to not be suitable, so we build our own + return preinstalled_unsuitable(e) + else: + logger.warning(f'Loaded "{self.mod_name}" module from pre-installed location: {kmod_path}. This implies that the module was compiled by a 3rd party, which is available but unsupported. If you experience issues related to module version mismatch in the future, please contact them for updating the module. This may break at any time, without notice, and regardless of the general backward compatibility policy of LISA.') + return None + # If base_path exists, busybox find will simply an empty stdout + # rather than return with a non-zero exit status. + else: + return preinstalled_unsuitable() # vim :set tabstop=4 shiftwidth=4 expandtab textwidth=80 -- GitLab From 85a0ac70e04e6c7ed0aa7327571fbb3e3487afe3 Mon Sep 17 00:00:00 2001 From: Douglas Raillard Date: Thu, 30 Jan 2025 17:00:08 +0000 Subject: [PATCH 2/3] lisa._kmod: Cleanly disconnect from Target in subprocess FIX When we run in a subprocess with separate user namespace in order to gain root privileges, ensure we disconnect from the target when we are done as this target is a copy (via pickle) of the one we got from the parent process. Failing to disconnect can result in exceptions as connections get closed upon garbage collection during interpreter shutdown as part of the stdlib is not available anymore. --- lisa/_kmod.py | 14 ++++++++++++-- 1 file changed, 12 insertions(+), 2 deletions(-) diff --git a/lisa/_kmod.py b/lisa/_kmod.py index 4769a6a9c..00afd4d8f 100644 --- a/lisa/_kmod.py +++ b/lisa/_kmod.py @@ -3003,9 +3003,10 @@ class DynamicKmod(Loggable): def _memoized_compile(self, make_vars): make_vars = dict(make_vars) - compile_ = self._do_compile.__func__ if self._compile_needs_root: - compile_ = ensure_root(compile_, inline=True) + compile_ = ensure_root(self._do_compile_subprocess.__func__, inline=True) + else: + compile_ = self._do_compile.__func__ bin_, spec = compile_(self, make_vars=make_vars) # Get back _KernelBuildEnv._to_spec() and update the _KernelBuildEnv we have in @@ -3016,6 +3017,15 @@ class DynamicKmod(Loggable): self.kernel_build_env._update_spec(spec) return bin_ + def _do_compile_subprocess(self, *args, **kwargs): + # We are running in a separate subprocess, so the target we got was + # pickled and sent to us. It is best to disconnect explicitly to avoid + # running into problems when objects are garbage collected as the + # interpreter is shutting down and parts of the stdlib are not + # available anymore. + with self.target.closing(): + return self._do_compile(*args, **kwargs) + def _do_compile(self, make_vars=None): kernel_build_env = self.kernel_build_env -- GitLab From 401e78714ce3631b93a7a163d16510dfd8d7ee25 Mon Sep 17 00:00:00 2001 From: Douglas Raillard Date: Thu, 30 Jan 2025 17:10:11 +0000 Subject: [PATCH 3/3] lisa._cli_tools.lisa_load_kmod: Use a custome name for root logger Avoid having the name "root" for the logger we use in the CLI tool for nicer output. --- lisa/_cli_tools/lisa_load_kmod.py | 13 +++++++------ 1 file changed, 7 insertions(+), 6 deletions(-) diff --git a/lisa/_cli_tools/lisa_load_kmod.py b/lisa/_cli_tools/lisa_load_kmod.py index 0d4b8a65b..eade26171 100755 --- a/lisa/_cli_tools/lisa_load_kmod.py +++ b/lisa/_cli_tools/lisa_load_kmod.py @@ -64,6 +64,7 @@ def main(): def _main(args, target): + logger = logging.getLogger('lisa-load-kmod') features = args.feature keep_loaded = not bool(args.cmd) @@ -78,31 +79,31 @@ def _main(args, target): kmod = target.get_kmod(LISADynamicKmod) pretty_events = ', '.join(kmod.defined_events) - logging.info(f'Kernel module provides the following ftrace events: {pretty_events}') + logger.info(f'Kernel module provides the following ftrace events: {pretty_events}') _kmod_cm = kmod.run(kmod_params=kmod_params) if keep_loaded: @contextlib.contextmanager def cm(): - logging.info('Compiling and loading kernel module ...') + logger.info('Loading kernel module ...') yield _kmod_cm.__enter__() - logging.info(f'Loaded kernel module as "{kmod.mod_name}"') + logger.info(f'Loaded kernel module as "{kmod.mod_name}"') else: @contextlib.contextmanager def cm(): with _kmod_cm: - logging.info('Compiling and loading kernel module ...') + logger.info('Loading kernel module ...') try: yield finally: - logging.info('Unloading kernel module') + logger.info('Unloading kernel module') kmod_cm = cm() def run_cmd(): if cmd: pretty_cmd = ' '.join(map(shlex.quote, cmd)) - logging.info(f'Running command: {pretty_cmd}') + logger.info(f'Running command: {pretty_cmd}') return subprocess.run(cmd).returncode else: return 0 -- GitLab