From 16f34150bc5af0e5a057b0dbd22384f28d642f22 Mon Sep 17 00:00:00 2001 From: Douglas Raillard Date: Thu, 15 May 2025 10:14:38 +0100 Subject: [PATCH 1/3] lisa._kmod: Allow more module source layout flexbility FIX * Avoid None module name reported in logs in some circumstances * Look for the .ko file deeper in the source folder rather than just at the root. --- lisa/_kmod.py | 14 ++++++++++++-- 1 file changed, 12 insertions(+), 2 deletions(-) diff --git a/lisa/_kmod.py b/lisa/_kmod.py index 7253f4112..875df52f2 100644 --- a/lisa/_kmod.py +++ b/lisa/_kmod.py @@ -2596,7 +2596,7 @@ class KmodSrc(Loggable): } self._mod_name = name - self.logger.debug(f'Created {self.__class__.__qualname__} with name {self._mod_name} and sources: {", ".join(self.src.keys())}') + self.logger.debug(f'Created {self.__class__.__qualname__} with name {self.mod_name} and sources: {", ".join(self.src.keys())}') @property def code_files(self): @@ -2666,10 +2666,14 @@ class KmodSrc(Loggable): return self.src['Makefile'] except KeyError: name = self.mod_name + def object_path(filename): + path = Path(filename) + path = path.parent / f'{path.stem}.o' + return str(path) return '\n'.join(( f'obj-m := {name}.o', f'{name}-y := ' + ' '.join( - f'{Path(filename).stem}.o' + object_path(filename) for filename in sorted(self.c_files.keys()) ) )).encode('utf-8') @@ -2736,6 +2740,12 @@ class KmodSrc(Loggable): def find_mod_file(path): filenames = glob.glob(str(path.resolve() / '*.ko')) + if len(filenames) > 1: + filenames = [ + filename + for filename in filenames + if Path(filename).stem == self.mod_name + ] if not filenames: raise FileNotFoundError(f'Could not find .ko file in {path}') -- GitLab From 36e2ae0b8acd09fb148a3967614c31d4356af847 Mon Sep 17 00:00:00 2001 From: Douglas Raillard Date: Tue, 15 Apr 2025 12:07:39 +0100 Subject: [PATCH 2/3] lisa._cli_tools.lisa_load_kmod: Refactor loading code Prepare the ground for building custom sources. --- lisa/_cli_tools/lisa_load_kmod.py | 53 +++++++++++++++++-------------- 1 file changed, 30 insertions(+), 23 deletions(-) diff --git a/lisa/_cli_tools/lisa_load_kmod.py b/lisa/_cli_tools/lisa_load_kmod.py index eade26171..120ea3199 100755 --- a/lisa/_cli_tools/lisa_load_kmod.py +++ b/lisa/_cli_tools/lisa_load_kmod.py @@ -26,11 +26,28 @@ import textwrap import logging import tempfile import shlex +import itertools +import pathlib from lisa.target import Target -from lisa._kmod import LISADynamicKmod +from lisa._kmod import LISADynamicKmod, KmodSrc from lisa.utils import ignore_exceps + +def lisa_kmod(logger, target, args): + features = args.feature + + kmod_params = {} + if features is not None: + kmod_params['features'] = list(features) + + kmod = target.get_kmod(LISADynamicKmod) + pretty_events = ', '.join(kmod.defined_events) + logger.info(f'Kernel module provides the following ftrace events: {pretty_events}') + + return kmod.run(kmod_params=kmod_params) + + def main(): params = { 'feature': dict( @@ -41,7 +58,6 @@ def main(): nargs=argparse.REMAINDER, help='Load the module, run the given command then unload the module. If not command is provided, just load the module and exit.' ) - } args, target = Target.from_custom_cli( @@ -66,53 +82,44 @@ def main(): def _main(args, target): logger = logging.getLogger('lisa-load-kmod') - features = args.feature keep_loaded = not bool(args.cmd) cmd = args.cmd or [] if cmd and cmd[0] == '--': cmd = cmd[1:] - kmod_params = {} - if features is not None: - kmod_params['features'] = list(features) - - kmod = target.get_kmod(LISADynamicKmod) - pretty_events = ', '.join(kmod.defined_events) - logger.info(f'Kernel module provides the following ftrace events: {pretty_events}') + kmod_cm = lisa_kmod(logger, target, args) - _kmod_cm = kmod.run(kmod_params=kmod_params) + def run_cmd(): + if cmd: + pretty_cmd = ' '.join(map(shlex.quote, cmd)) + logger.info(f'Running command: {pretty_cmd}') + return subprocess.run(cmd).returncode + else: + return 0 if keep_loaded: @contextlib.contextmanager def cm(): logger.info('Loading kernel module ...') - yield _kmod_cm.__enter__() + yield kmod_cm.__enter__() logger.info(f'Loaded kernel module as "{kmod.mod_name}"') else: @contextlib.contextmanager def cm(): - with _kmod_cm: + with kmod_cm: logger.info('Loading kernel module ...') try: yield finally: logger.info('Unloading kernel module') - kmod_cm = cm() - def run_cmd(): - if cmd: - pretty_cmd = ' '.join(map(shlex.quote, cmd)) - logger.info(f'Running command: {pretty_cmd}') - return subprocess.run(cmd).returncode - else: - return 0 - - with kmod_cm: + with cm(): ret = run_cmd() return ret + if __name__ == '__main__': sys.exit(main()) -- GitLab From 4466890583821eed3d9ee2f4626d7c6c4614e143 Mon Sep 17 00:00:00 2001 From: Douglas Raillard Date: Thu, 15 May 2025 10:18:00 +0100 Subject: [PATCH 3/3] lisa._cli_tools.lisa_load_kmod: Allow building custom sources FEATURE Add a --src parameter that allows pointing at files or folders containing C sources of a module to build. When --src is used, the lisa module is not built and the provided sources are used instead. --- lisa/_cli_tools/lisa_load_kmod.py | 48 ++++++++++++++++++++++++++++++- 1 file changed, 47 insertions(+), 1 deletion(-) diff --git a/lisa/_cli_tools/lisa_load_kmod.py b/lisa/_cli_tools/lisa_load_kmod.py index 120ea3199..b711b31c3 100755 --- a/lisa/_cli_tools/lisa_load_kmod.py +++ b/lisa/_cli_tools/lisa_load_kmod.py @@ -48,12 +48,55 @@ def lisa_kmod(logger, target, args): return kmod.run(kmod_params=kmod_params) +def custom_kmod(logger, target, args): + def _process_path(path): + path = pathlib.Path(path) + if path.is_dir(): + for _path in path.rglob('*'): + try: + content = _path.read_text() + except IsADirectoryError: + pass + else: + yield (_path.relative_to(path), content) + else: + content = path.read_text() + yield (path, content) + + def process_path(path): + try: + return _process_path(path) + except Exception as e: + raise ValueError(f'Could not read {path}: {e}') + + src = dict(itertools.chain.from_iterable(map(process_path, args.src))) + pretty_paths = '\n'.join(sorted(map(str, src.keys()))) + logger.info(f'Loading custom kernel module sources from:\n{pretty_paths}') + + src = KmodSrc( + src={ + str(name): content + for name, content in src.items() + }, + name=args.name, + ) + kmod = target.get_kmod(src=src) + return kmod.run() + + def main(): params = { 'feature': dict( action='append', help='Enable a specific module feature. Can be repeated. By default, the module will try to enable all features and will log in dmesg the ones that failed to enable' ), + 'src': dict( + action='append', + help='Source file to use for the module, to build dynamically. If a folder is provided, the whole content will be loaded as sources.' + ), + 'name': dict( + help='Name to give to the custom module built with --src' + ), 'cmd': dict( nargs=argparse.REMAINDER, help='Load the module, run the given command then unload the module. If not command is provided, just load the module and exit.' @@ -88,7 +131,10 @@ def _main(args, target): if cmd and cmd[0] == '--': cmd = cmd[1:] - kmod_cm = lisa_kmod(logger, target, args) + if args.src: + kmod_cm = custom_kmod(logger, target, args) + else: + kmod_cm = lisa_kmod(logger, target, args) def run_cmd(): if cmd: -- GitLab