diff --git a/Runner/suites/Kernel/Baseport/Module_Reload_Validation/Module_Reload_Validation.yaml b/Runner/suites/Kernel/Baseport/Module_Reload_Validation/Module_Reload_Validation.yaml index 4590b8c04..a73187409 100755 --- a/Runner/suites/Kernel/Baseport/Module_Reload_Validation/Module_Reload_Validation.yaml +++ b/Runner/suites/Kernel/Baseport/Module_Reload_Validation/Module_Reload_Validation.yaml @@ -3,7 +3,7 @@ metadata: format: "Lava-Test Test Definition 1.0" description: "Generic profiled kernel module unload/reload regression validation" maintainer: - - Srikanth kumar + - Srikanth Kumar os: - linux scope: @@ -11,6 +11,7 @@ metadata: params: PROFILE: "" + PROFILE_LIST: "" ITERATIONS: "3" MODE: "" TIMEOUT_UNLOAD: "30" @@ -22,7 +23,5 @@ run: steps: - REPO_PATH=$PWD - cd Runner/suites/Kernel/Baseport/Module_Reload_Validation - - SYSRQ_ARG="" - - if [ "${ENABLE_SYSRQ_HANG_DUMP}" = "0" ]; then SYSRQ_ARG="--disable-sysrq-hang-dump"; fi - - ./run.sh --module "${PROFILE}" --iterations "${ITERATIONS}" --mode "${MODE}" --timeout-unload "${TIMEOUT_UNLOAD}" --timeout-load "${TIMEOUT_LOAD}" --timeout-settle "${TIMEOUT_SETTLE}" ${SYSRQ_ARG} || true + - ./run.sh --module "${PROFILE}" --profile-list "${PROFILE_LIST}" --iterations "${ITERATIONS}" --mode "${MODE}" --timeout-unload "${TIMEOUT_UNLOAD}" --timeout-load "${TIMEOUT_LOAD}" --timeout-settle "${TIMEOUT_SETTLE}" --enable-sysrq-hang-dump "${ENABLE_SYSRQ_HANG_DUMP}" || true - $REPO_PATH/Runner/utils/send-to-lava.sh Module_Reload_Validation.res diff --git a/Runner/suites/Kernel/Baseport/Module_Reload_Validation/Module_Reload_Validation_README.md b/Runner/suites/Kernel/Baseport/Module_Reload_Validation/Module_Reload_Validation_README.md index 98d7ab90e..a97c4362b 100644 --- a/Runner/suites/Kernel/Baseport/Module_Reload_Validation/Module_Reload_Validation_README.md +++ b/Runner/suites/Kernel/Baseport/Module_Reload_Validation/Module_Reload_Validation_README.md @@ -1,18 +1,43 @@ # Module_Reload_Validation ## Overview -`Module_Reload_Validation` is a generic, profile-driven kernel module unload/reload regression suite. + +`Module_Reload_Validation` is a generic, profile-driven kernel module unload/reload regression suite for Qualcomm Linux test coverage. It is intended to catch issues such as: + - module unload hangs, - failed reloads, +- missing or incomplete reloads, - service/device rebind regressions after reload, -- issues that reproduce on the 1st, 2nd, or later reload iteration. +- dependency modules left loaded after unload, +- active users keeping a module busy, +- failures that reproduce only on the 1st, 2nd, or later reload iteration. The suite uses: -- a **generic engine** in `run.sh`, -- shared helper logic in `Runner/utils/lib_module_reload.sh`, -- **module-specific profiles** under `profiles/`. + +- a thin orchestration script in `run.sh`, +- shared generic helper logic in `Runner/utils/lib_module_reload.sh`, +- module-specific profile files under `profiles/`, +- profile list files to group base, overlay, or team-specific coverage. + +The design is intentionally profile-driven so new modules can be added without duplicating module-specific logic in `run.sh`. + +## Recent framework updates + +The current framework has been enhanced to support the following behavior: + +- `run.sh` supports both single-profile execution through `--module` and group execution through `--profile-list`. +- `run.sh` logs the selected profile list in the argument summary. +- `lib_module_reload.sh` contains the common reload engine and generic helper functions. +- Profiles are expected to stay minimal and declarative. +- Complex logic should be moved to reusable library helpers instead of being embedded in each profile. +- Profiles can declare service/process/device quiesce metadata separately from warmup/restore metadata. +- Profiles can use `PROFILE_QUIESCE_ONCE="yes"` to avoid repeatedly stopping the same disruptive services on every iteration. +- The framework can snapshot service state and restore previously active services from the test side after profile execution. +- Profiles can skip when conflicting modules are active, for example `ath11k_pci` can skip when `ath11k_ahb` is the active Wi-Fi transport. +- The Qualcomm SoundWire/ASoC profile is minimal and uses a library helper to discover the top-level holder of `snd_soc_qcom_sdw`. +- Modern Adreno GPU/display validation should treat `msm.ko` as the active upstream DRM driver on RB3Gen2/newer platforms. `msm_kgsl` is legacy KGSL and should not be used for modern RB3Gen2-style DRM stacks. ## Folder layout @@ -20,107 +45,645 @@ The suite uses: Runner/suites/Kernel/Baseport/Module_Reload_Validation/ ├── run.sh ├── Module_Reload_Validation.yaml +├── Module_Reload_Validation_README.md ├── profiles/ │ ├── enabled.list -│ └── fastrpc.profile +│ ├── base.list +│ ├── overlay.list +│ ├── fastrpc.profile +│ ├── dwmac_qcom_eth.profile +│ ├── tc956x_pcie_eth.profile +│ ├── ath11k_pci.profile +│ ├── ath11k_ahb.profile +│ ├── ath12k_pci.profile +│ ├── qcedev_mod_dlkm.profile +│ ├── qcrypto_msm_dlkm.profile +│ ├── qrng_dlkm.profile +│ ├── spcom.profile +│ ├── mvm.profile +│ ├── venus_core.profile +│ ├── qcom_iris.profile +│ ├── snd_soc_qcom_sdw.profile +│ ├── msm.profile +│ ├── msm_kgsl.profile +│ ├── camera.profile +│ ├── iris.profile +│ └── audioreach.profile Runner/utils/ └── lib_module_reload.sh ``` +`msm_kgsl.profile` is legacy KGSL coverage and is expected to skip on modern DRM-based images where `msm.ko` is used instead. + ## Main components ### `run.sh` -Thin orchestration layer that: -- parses CLI arguments, -- resolves the selected profile(s), -- invokes the generic library engine, -- writes `Module_Reload_Validation.res`. - -### `lib_module_reload.sh` -Shared module reload engine that handles: -- module state checks, -- timeout-controlled unload/load execution, -- per-iteration evidence collection, -- timeout-path hang evidence, + +`run.sh` is the orchestration layer. It should remain thin. + +Responsibilities: + +- load `init_env`, `functestlib.sh`, and `lib_module_reload.sh`, +- parse CLI arguments, +- resolve selected profile files, +- run each profile through the generic library engine, +- maintain pass/fail/skip counts, +- write `Module_Reload_Validation.res`, +- leave detailed artifacts under `results/Module_Reload_Validation/`. + +`run.sh` should not contain module-specific reload logic. Module-specific behavior belongs in profile files or reusable helper functions in `lib_module_reload.sh`. + +### `Runner/utils/lib_module_reload.sh` + +`lib_module_reload.sh` contains the shared module reload engine. + +Responsibilities: + +- profile variable reset, +- profile validation, +- module presence and built-in checks, +- loaded-module checks, +- timeout-controlled command execution, +- unload/load validation, +- multi-module stack validation, - profile hook dispatch, -- result handling. +- service/process quiesce, +- best-effort restore of previously active services, +- optional quiesce-once behavior, +- evidence collection, +- timeout/hang evidence collection, +- helper functions for profiles with dynamic topology. + +Examples of reusable helpers: + +- module availability checks, +- holder detection using `/sys/module//holders`, +- holder detection using `/proc/modules`, +- generic top-module selection, +- generic stack setup, +- Qualcomm SoundWire/ASoC stack setup. ### `profiles/*.profile` -Each profile provides module-specific metadata and optional hook logic. - -Example profile fields: -- `PROFILE_NAME` -- `PROFILE_DESCRIPTION` -- `MODULE_NAME` -- `PROFILE_MODE_DEFAULT` -- `PROFILE_REQUIRED_CMDS` -- `PROFILE_SERVICES` -- `PROFILE_DEVICE_PATTERNS` -- `PROFILE_SYSFS_PATTERNS` - -Optional hooks: -- `profile_prepare` -- `profile_warmup` -- `profile_quiesce` -- `profile_post_unload` -- `profile_post_load` -- `profile_smoke` -- `profile_finalize` - -## Current starter profile + +Profiles define module-specific metadata and optional hook behavior. + +Profiles should be as declarative as possible. A profile should define what needs to be reloaded and what must be quiesced. The library should define how that is done. + +## Profile variables + +### Required or commonly used fields + +```sh +PROFILE_NAME="example_module" +PROFILE_DESCRIPTION="Example module reload validation" +MODULE_NAME="example_module" +MODULE_RELOAD_SUPPORTED="yes" +PROFILE_MODE_DEFAULT="basic" +PROFILE_REQUIRED_CMDS="modprobe rmmod ps sed grep" +MODULE_UNLOAD_CMD="modprobe -r example_module" +MODULE_LOAD_CMD="modprobe example_module" +PROFILE_EXPECT_ABSENT_AFTER_UNLOAD="example_module" +PROFILE_EXPECT_PRESENT_AFTER_LOAD="example_module" +PROFILE_SYSFS_PATTERNS="/sys/module/example_module" +``` + +### Service and process fields + +```sh +PROFILE_SERVICES="example.service" +PROFILE_PROC_PATTERNS="example-daemon example-client" +PROFILE_DEVICE_PATTERNS="/dev/example*" +PROFILE_SYSFS_PATTERNS="/sys/module/example_module /sys/class/example/*" +``` + +These fields are used for service status logging, evidence collection, and profile lifecycle handling. + +### Quiesce-specific fields + +Use quiesce-specific fields when the services/processes that need to be stopped before unload are different from the services/processes that should be observed or restored. + +```sh +PROFILE_QUIESCE_SERVICES="example.service" +PROFILE_QUIESCE_PROC_PATTERNS="example-daemon example-client" +PROFILE_QUIESCE_DEVICE_PATTERNS="/dev/example*" +``` + +If these are not set, the framework falls back to `PROFILE_SERVICES`, `PROFILE_PROC_PATTERNS`, and `PROFILE_DEVICE_PATTERNS`. + +### Quiesce-once behavior + +Some profiles are expensive or risky to quiesce repeatedly. For those profiles, use: + +```sh +PROFILE_QUIESCE_ONCE="yes" +``` + +This is useful for profiles such as: + +- `fastrpc`, +- `snd_soc_qcom_sdw`, +- `ath11k_ahb`, +- `msm`. + +With `PROFILE_QUIESCE_ONCE="yes"`, the first iteration performs the full quiesce path. Later iterations skip repeated service/process stop work and continue with unload/reload validation. + +### Conflict skip fields + +Use this when two profiles represent alternate transports or mutually exclusive module stacks. + +Example: + +```sh +PROFILE_SKIP_IF_MODULES_LOADED="ath11k_ahb" +``` + +This lets `ath11k_pci.profile` skip cleanly on platforms where `ath11k_ahb` is the active Wi-Fi transport. + +### Dynamic stack fields + +These fields are useful when the module to reload is a top-level holder rather than the originally named module. + +```sh +PROFILE_TOP_MODULE_CANDIDATES="snd_soc_sc8280xp snd_soc_sm8450" +PROFILE_UNLOAD_STACK="snd_soc_sc8280xp snd_soc_qcom_sdw" +PROFILE_EXTRA_UNLOAD_MODULES="" +``` + +For common patterns, prefer a library helper instead of open-coding stack discovery in the profile. + +## Profile hooks + +Profiles may define optional hooks: + +```sh +profile_prepare() { ...; } +profile_warmup() { ...; } +profile_quiesce() { ...; } +profile_post_unload() { ...; } +profile_post_load() { ...; } +profile_smoke() { ...; } +profile_finalize() { ...; } +``` + +Use hooks only when declarative profile variables are not enough. + +Recommended approach: + +1. First try generic fields such as `PROFILE_SERVICES`, `PROFILE_PROC_PATTERNS`, `MODULE_UNLOAD_CMD`, and `MODULE_LOAD_CMD`. +2. If multiple profiles need the same behavior, create a reusable helper in `lib_module_reload.sh`. +3. Keep module-specific profiles small. +4. Avoid copying large shell functions into profile files. + +## Profile lists + +Profile lists let teams or CI jobs run the desired module set without creating a separate YAML for every module. + +### `profiles/enabled.list` + +Default list used when `./run.sh` is called without `--module` or `--profile-list`. + +Keep this conservative for common CI. + +Recommended default: + +```text +fastrpc +``` + +### `profiles/base.list` + +Base-image or upstream-aligned module coverage. + +Example broad base list: + +```text +fastrpc +dwmac_qcom_eth +tc956x_pcie_eth +ath11k_pci +ath11k_ahb +ath12k_pci +qcedev_mod_dlkm +qcrypto_msm_dlkm +qrng_dlkm +spcom +mvm +venus_core +qcom_iris +snd_soc_qcom_sdw +``` + +For RB3Gen2-style targets, a conservative bring-up list can be smaller: + +```text +tc956x_pcie_eth +ath11k_ahb +venus_core +qcom_iris +snd_soc_qcom_sdw +``` + +`fastrpc` is useful but can be disruptive if the unload path hangs. It may be better to run it explicitly during bring-up. + +### `profiles/overlay.list` + +Overlay/downstream package coverage. + +Current recommended overlay list: + +```text +msm +camera +iris +audioreach +``` + +Notes: + +- `msm` represents the modern MSM DRM driver used by RB3Gen2/newer platforms. +- `msm_kgsl` is legacy KGSL and should not be the default for modern DRM-based platforms. +- `camera` and `iris` may mark themselves non-reloadable until a safe reload sequence is validated. +- `audioreach` should skip if downstream AudioReach modules are not present on the image. + +## Base module coverage ### `fastrpc.profile` -Current profile covers FastRPC unload/reload validation and supports service lifecycle-based testing. -Default mode: -- `daemon_lifecycle` +FastRPC reload validation. + +Common services/processes: + +```text +adsprpcd.service +cdsprpcd.service +adsprpcd +cdsprpcd +``` + +Guidance: + +- Stop and mask FastRPC users before unload. +- Kill remaining FastRPC cgroup/process users when required. +- Check open file descriptors for FastRPC device nodes before unload. +- Use timeout evidence if unload hangs. +- Run `fastrpc` as a focused test when debugging DSP unload behavior. +- If unload hangs and the kernel task is stuck, the device may need recovery before broad profile testing can continue. + +### `dwmac_qcom_eth.profile` + +Qualcomm DWMAC/STMMAC Ethernet reload validation. + +Typical stack: + +```text +dwmac_qcom_eth +stmmac_platform +stmmac +``` + +Use this only on platforms where `dwmac_qcom_eth` exists. + +### `tc956x_pcie_eth.profile` + +TC956x PCIe Ethernet reload validation. + +Typical module: + +```text +tc956x_pcie_eth +``` + +This is useful on RB3Gen2-style platforms where Ethernet is provided by the TC956x PCIe Ethernet driver instead of `dwmac_qcom_eth`. + +### `ath11k_pci.profile` + +ath11k PCI Wi-Fi reload validation. + +Typical stack: + +```text +ath11k_pci +ath11k +mac80211 +cfg80211 +``` + +Use `PROFILE_SKIP_IF_MODULES_LOADED="ath11k_ahb"` so this profile skips on platforms where AHB transport is active. + +### `ath11k_ahb.profile` + +ath11k AHB Wi-Fi reload validation. + +Typical stack: + +```text +ath11k_ahb +ath11k +mac80211 +cfg80211 +``` + +Common services to quiesce: + +```text +wpa_supplicant.service +NetworkManager.service +systemd-networkd.service +``` + +Use `PROFILE_QUIESCE_ONCE="yes"` if repeated service stop/start is causing long execution time or unstable network state. + +### `ath12k_pci.profile` + +ath12k PCI Wi-Fi reload validation. + +Typical stack: + +```text +ath12k_pci +ath12k +mac80211 +cfg80211 +``` + +Keep this profile even if it skips on RB3Gen2. Other targets may use ath12k. + +### `qcedev_mod_dlkm.profile` + +QCEDEV crypto DLKM reload validation. + +Typical modules: + +```text +qcedev_mod_dlkm +qce50_dlkm +``` + +### `qcrypto_msm_dlkm.profile` + +QCrypto MSM DLKM reload validation. + +Some builds may have dependency/cyclic unload issues. Capture unload logs and holder state before deciding whether the profile should stay enabled for a specific platform. + +### `qrng_dlkm.profile` + +QRNG DLKM reload validation. + +Some builds may have service naming or service lifecycle issues. Missing service units should not fail the profile by themselves. + +### `spcom.profile` + +Secure Processor communication reload validation. + +Common services to quiesce where present: + +```text +spdaemon.service +qseecomd.service +keymaster-4-0.service +``` + +Missing services are non-fatal. + +### `mvm.profile` + +MVM DLKM reload validation. + +Expected service where present: + +```text +mvm.service +``` + +### `venus_core.profile` + +Upstream Venus video reload validation. + +Typical stack: + +```text +venus_enc +venus_dec +venus_core +``` + +### `qcom_iris.profile` + +Upstream Qualcomm Iris video reload validation. + +Typical module: + +```text +qcom_iris +``` + +Use this on platforms where the active Iris module is `qcom_iris`. + +### `snd_soc_qcom_sdw.profile` + +Qualcomm ASoC/SoundWire reload validation. + +This profile should stay minimal. The topology and unload stack are handled by the library helper: + +```sh +module_reload_qcom_sdw_profile_setup +``` + +The helper detects the top-level holder of `snd_soc_qcom_sdw`, for example: + +```text +snd_soc_sc8280xp -> snd_soc_qcom_sdw +``` + +Then it unloads the top-level machine/card driver before `snd_soc_qcom_sdw`, and reloads the top-level module. + +Recommended profile behavior: + +```sh +PROFILE_NAME="snd_soc_qcom_sdw" +PROFILE_DESCRIPTION="Qualcomm ASoC/SoundWire reload validation" +PROFILE_QUIESCE_ONCE="yes" + +module_reload_qcom_sdw_profile_setup +``` + +Use `QCOM_SDW_TOP_MODULE_CANDIDATES` to extend platform support without changing the profile: + +```sh +QCOM_SDW_TOP_MODULE_CANDIDATES="snd_soc_sc8280xp snd_soc_sm8450 snd_soc_sc7280" \ + ./run.sh --module snd_soc_qcom_sdw +``` + +## Overlay module coverage + +### `msm.profile` + +Modern MSM DRM GPU/display reload validation. + +Typical module: + +```text +msm +``` + +Important distinction: + +- `msm.ko` is the modern DRM driver used by RB3Gen2/newer platforms. +- `msm_kgsl.ko` is legacy KGSL and is not expected on modern DRM-based images. + +`msm` is highly disruptive because it can be held by DRM/KMS clients, display, GPU, Weston, or console users. + +Common consumers to quiesce: + +```text +weston.service +display-manager.service +gpu-service.service +``` + +Common device paths: + +```text +/dev/dri/card* +/dev/dri/renderD* +``` + +Guidance: + +- Do not enable `msm` in broad CI until the platform-specific quiesce path is validated. +- If `modprobe -r msm` fails with `rc=1` and the unload log has no extra detail, inspect `holders.log`, `lsmod.log`, `ps.log`, `/dev/dri/*` users, and DRM clients. +- If the display stack is active, `msm` may be a valid skip/non-reloadable profile for that environment. +- Keep `msm` separate from `msm_kgsl` to avoid confusing modern DRM and legacy KGSL validation. + +### `msm_kgsl.profile` + +Legacy KGSL reload validation. + +Typical module: + +```text +msm_kgsl +``` + +This is only for old KGSL-based systems. It should skip on RB3Gen2/newer DRM systems. + +### `camera.profile` + +CAMX/downstream camera KMD reload validation. -Relevant services: -- `adsprpcd.service` -- `cdsprpcd.service` +Common module candidates: + +```text +camera +camera_kmd +camx_kmd +``` + +Upstream camera on RB3Gen2-style images may use: + +```text +qcom_camss +imx412 +camcc_sc7280 +``` + +Do not treat upstream `qcom_camss` as the same thing as CAMX overlay. If the CAMX module is not present, the CAMX overlay profile should skip. + +### `iris.profile` + +Downstream Iris video overlay reload validation. + +Common module candidates: + +```text +iris +iris_vpu +``` + +Upstream Iris uses `qcom_iris`. Keep downstream `iris` / `iris_vpu` separate from upstream `qcom_iris`. + +### `audioreach.profile` + +Downstream AudioReach reload validation. + +Typical modules: + +```text +snd_soc_qdsp6 +q6asm +q6adm +q6afe +apr +``` + +The profile should skip if downstream AudioReach modules are not present. ## Execution flow + For each selected profile: -1. Validate the profile. -2. Ensure the module is loaded before starting iteration work. -3. Run warmup hook. -4. Capture pre-state logs. -5. Run quiesce hook. -6. Attempt module unload with timeout. -7. Validate module absence. -8. Run post-unload hook. -9. Attempt module reload with timeout. -10. Validate module presence. -11. Run post-load hook. -12. Run smoke hook. -13. Capture post-load state. -14. Repeat for all iterations. -15. Run finalize hook. +1. Reset profile variables. +2. Source the profile file. +3. Run any profile setup helper. +4. Validate the profile. +5. Skip if the module is not present, built-in, marked non-reloadable, or conflicts with an active mutually exclusive module. +6. Ensure the module is loaded before iteration work starts. +7. Run warmup logic when configured. +8. Capture pre-state evidence. +9. Log service/process state for the profile. +10. Quiesce configured services/processes before unload. +11. Optionally skip repeated quiesce if `PROFILE_QUIESCE_ONCE="yes"` was already completed. +12. Execute the unload command with timeout. +13. Validate that `MODULE_NAME` and `PROFILE_EXPECT_ABSENT_AFTER_UNLOAD` are absent. +14. Run post-unload hook. +15. Execute the load command with timeout. +16. Validate that `MODULE_NAME` and `PROFILE_EXPECT_PRESENT_AFTER_LOAD` are present. +17. Run post-load hook. +18. Run smoke hook. +19. Capture post-load evidence. +20. Repeat for all iterations. +21. Run finalize hook and restore previously active services where applicable. -## Hang handling policy -Sysrq dump is **not** triggered on normal passing iterations. +## Result policy -It is triggered only when an unload/load action actually times out. +### PASS -Current behavior: -- normal pass -> no sysrq dump -- quick non-timeout failure -> normal failure evidence only -- timeout / hang -> hang evidence bundle + optional sysrq dump +A profile passes when all requested iterations complete successfully. -Default behavior in current suite: -- sysrq hang dump enabled -- but only used on timeout paths +### FAIL + +A profile fails on: + +- unload timeout, +- load timeout, +- unload command failure, +- load command failure, +- module state validation failure, +- profile hook failure, +- smoke validation failure. + +### SKIP + +A profile skips when: + +- module is not present on the image, +- module is built into the kernel, +- required command is missing, +- profile marks the module as non-reloadable, +- profile declares a conflicting active module through `PROFILE_SKIP_IF_MODULES_LOADED`. ## Evidence collected -Per profile / iteration, the suite can capture: + +Per profile and iteration, the suite can capture: + - command logs, - `lsmod`, - `modinfo`, - `ps`, - `dmesg`, -- service status and recent journal, +- service status, +- recent service journal, - profiled device path presence, - profiled sysfs path presence, - `/sys/module//holders`, @@ -130,98 +693,504 @@ Per profile / iteration, the suite can capture: Results are stored under: ```text -results/Module_Reload_Validation//iter_XX/ +Runner/suites/Kernel/Baseport/Module_Reload_Validation/results/Module_Reload_Validation//iter_XX/ +``` + +Useful files: + +```text +unload.log +load.log +pre_state/lsmod.log +pre_state/holders.log +unload_failure_state/lsmod.log +unload_failure_state/holders.log +hang_evidence/dmesg_after_sysrq.log +``` + +## Hang handling policy + +Sysrq dump is not triggered on normal passing iterations. + +It is triggered only when unload/load times out and sysrq dump policy is enabled. + +Default behavior: + +- normal pass: no sysrq dump, +- quick non-timeout failure: normal failure evidence only, +- timeout/hang: hang evidence bundle plus optional sysrq task/block dumps. + +Disable sysrq dumps when needed: + +```sh +./run.sh --module fastrpc --disable-sysrq-hang-dump ``` ## CLI usage +### Standard repo-style launch + +Most LAVA tests in this repo launch from `Runner`. + +```sh +cd Runner +$PWD/suites/Kernel/Baseport/Module_Reload_Validation/run.sh +$PWD/utils/send-to-lava.sh $PWD/suites/Kernel/Baseport/Module_Reload_Validation/Module_Reload_Validation.res +$PWD/utils/result_parse.sh +``` + +### Direct local launch from the test directory + +```sh +cd Runner/suites/Kernel/Baseport/Module_Reload_Validation +./run.sh +``` + ### Run one profile + ```sh ./run.sh --module fastrpc +./run.sh --module ath11k_ahb +./run.sh --module snd_soc_qcom_sdw +./run.sh --module msm ``` -### Run one profile with more iterations +### Run a profile list + ```sh -./run.sh --module fastrpc --iterations 5 +./run.sh --profile-list profiles/base.list +./run.sh --profile-list profiles/overlay.list +``` + +### Run with more iterations + +```sh +./run.sh --module ath11k_ahb --iterations 5 ``` ### Override mode + ```sh -./run.sh --module fastrpc --mode daemon_lifecycle ./run.sh --module fastrpc --mode basic +./run.sh --module fastrpc --mode daemon_lifecycle ``` +If `--mode` is not provided, the profile default is used. + ### Override timeouts + +```sh +./run.sh --module fastrpc \ + --timeout-unload 60 \ + --timeout-load 60 \ + --timeout-settle 30 +``` + +### Enable verbose shell logging + ```sh -./run.sh --module fastrpc --timeout-unload 60 --timeout-load 60 --timeout-settle 30 +./run.sh --module snd_soc_qcom_sdw --verbose ``` -### Disable sysrq timeout-path dumps +### Disable sysrq timeout dumps + ```sh ./run.sh --module fastrpc --disable-sysrq-hang-dump ``` -### Run all enabled profiles +### Run SoundWire/audio with candidate override + ```sh -./run.sh +QCOM_SDW_TOP_MODULE_CANDIDATES="snd_soc_sc8280xp snd_soc_sm8450 snd_soc_sc7280" \ + ./run.sh --module snd_soc_qcom_sdw --iterations 1 --timeout-unload 60 --timeout-load 60 +``` + +## Team-specific launch examples + +Teams do not need separate YAML files for every profile. They can call the same module reload suite with `--module `. + +### FastRPC team example + +```sh +cd Runner +$PWD/suites/Multimedia/CDSP/fastrpc_test/run.sh || true +$PWD/utils/send-to-lava.sh $PWD/suites/Multimedia/CDSP/fastrpc_test/fastrpc_test.res || true +$PWD/suites/Kernel/Baseport/Module_Reload_Validation/run.sh --module fastrpc || true +$PWD/utils/send-to-lava.sh $PWD/suites/Kernel/Baseport/Module_Reload_Validation/Module_Reload_Validation.res || true +$PWD/utils/result_parse.sh +``` + +### Wi-Fi team examples + +```sh +cd Runner +$PWD/suites/Kernel/Baseport/Module_Reload_Validation/run.sh --module ath11k_ahb || true +$PWD/utils/send-to-lava.sh $PWD/suites/Kernel/Baseport/Module_Reload_Validation/Module_Reload_Validation.res || true +$PWD/utils/result_parse.sh +``` + +```sh +cd Runner +$PWD/suites/Kernel/Baseport/Module_Reload_Validation/run.sh --module ath12k_pci || true +$PWD/utils/send-to-lava.sh $PWD/suites/Kernel/Baseport/Module_Reload_Validation/Module_Reload_Validation.res || true +$PWD/utils/result_parse.sh ``` -If `--module` is empty or not given, the suite runs all profiles listed in `profiles/enabled.list`. +### Audio team example -If `--mode` is empty or not given, the profile default mode is used. +```sh +cd Runner +$PWD/suites/Kernel/Baseport/Module_Reload_Validation/run.sh --module snd_soc_qcom_sdw --iterations 1 --timeout-unload 60 --timeout-load 60 || true +$PWD/utils/send-to-lava.sh $PWD/suites/Kernel/Baseport/Module_Reload_Validation/Module_Reload_Validation.res || true +$PWD/utils/result_parse.sh +``` + +### Overlay bring-up example + +```sh +cd Runner +$PWD/suites/Kernel/Baseport/Module_Reload_Validation/run.sh --profile-list suites/Kernel/Baseport/Module_Reload_Validation/profiles/overlay.list || true +$PWD/utils/send-to-lava.sh $PWD/suites/Kernel/Baseport/Module_Reload_Validation/Module_Reload_Validation.res || true +$PWD/utils/result_parse.sh +``` ## YAML usage in LAVA -Current YAML is generic and takes profile input from the test plan. -Important params: -- `PROFILE` -- `ITERATIONS` -- `MODE` -- `TIMEOUT_UNLOAD` -- `TIMEOUT_LOAD` -- `TIMEOUT_SETTLE` -- `ENABLE_SYSRQ_HANG_DUMP` +`Module_Reload_Validation.yaml` should remain generic. Do not duplicate module-specific reload logic in YAML. + +Recommended params: + +```yaml +params: + PROFILE: "" + PROFILE_LIST: "" + ITERATIONS: "3" + MODE: "" + TIMEOUT_UNLOAD: "30" + TIMEOUT_LOAD: "30" + TIMEOUT_SETTLE: "20" + ENABLE_SYSRQ_HANG_DUMP: "1" +``` + +Recommended repo-aligned run steps: + +```yaml +run: + steps: + - cd Runner + - MRV_ARGS="" + - if [ -n "${PROFILE}" ]; then MRV_ARGS="${MRV_ARGS} --module ${PROFILE}"; fi + - if [ -n "${PROFILE_LIST}" ]; then MRV_ARGS="${MRV_ARGS} --profile-list ${PROFILE_LIST}"; fi + - if [ -n "${ITERATIONS}" ]; then MRV_ARGS="${MRV_ARGS} --iterations ${ITERATIONS}"; fi + - if [ -n "${MODE}" ]; then MRV_ARGS="${MRV_ARGS} --mode ${MODE}"; fi + - if [ -n "${TIMEOUT_UNLOAD}" ]; then MRV_ARGS="${MRV_ARGS} --timeout-unload ${TIMEOUT_UNLOAD}"; fi + - if [ -n "${TIMEOUT_LOAD}" ]; then MRV_ARGS="${MRV_ARGS} --timeout-load ${TIMEOUT_LOAD}"; fi + - if [ -n "${TIMEOUT_SETTLE}" ]; then MRV_ARGS="${MRV_ARGS} --timeout-settle ${TIMEOUT_SETTLE}"; fi + - if [ "${ENABLE_SYSRQ_HANG_DUMP}" = "0" ]; then MRV_ARGS="${MRV_ARGS} --disable-sysrq-hang-dump"; fi + - $PWD/suites/Kernel/Baseport/Module_Reload_Validation/run.sh ${MRV_ARGS} || true + - $PWD/utils/send-to-lava.sh $PWD/suites/Kernel/Baseport/Module_Reload_Validation/Module_Reload_Validation.res || true + - $PWD/utils/result_parse.sh +``` Behavior: -- `PROFILE="fastrpc"` -> runs only `fastrpc.profile` -- `PROFILE=""` -> runs all enabled profiles -- `MODE=""` -> uses the profile default mode + +- `PROFILE="fastrpc"` runs only `fastrpc.profile`. +- `PROFILE="snd_soc_qcom_sdw"` runs only the audio/SoundWire profile. +- `PROFILE_LIST="suites/Kernel/Baseport/Module_Reload_Validation/profiles/base.list"` runs the base list. +- `PROFILE_LIST="suites/Kernel/Baseport/Module_Reload_Validation/profiles/overlay.list"` runs the overlay list. +- If both `PROFILE` and `PROFILE_LIST` are empty, `run.sh` uses `profiles/enabled.list`. +- If both `PROFILE` and `PROFILE_LIST` are set, prefer single-profile behavior through `PROFILE` and avoid setting both in LAVA unless the test definition intentionally supports that combination. + +### Example: generic YAML invocation for FastRPC only + +```yaml +params: + PROFILE: "fastrpc" + PROFILE_LIST: "" + ITERATIONS: "3" +``` + +### Example: generic YAML invocation for base list + +```yaml +params: + PROFILE: "" + PROFILE_LIST: "suites/Kernel/Baseport/Module_Reload_Validation/profiles/base.list" + ITERATIONS: "3" +``` + +### Example: generic YAML invocation for overlay list + +```yaml +params: + PROFILE: "" + PROFILE_LIST: "suites/Kernel/Baseport/Module_Reload_Validation/profiles/overlay.list" + ITERATIONS: "3" +``` + +### Do we need one YAML per profile? + +No. The preferred approach is one generic YAML plus params. + +Use separate YAML files only when a team wants to combine module reload validation with another functional test flow, for example FastRPC functional validation followed by `--module fastrpc` reload validation. ## How to add a new profile -1. Create a new file under `profiles/`, for example: - - `profiles/ath11k_pci.profile` -2. Define the required metadata: - - `PROFILE_NAME` - - `MODULE_NAME` -3. Add hooks only if module-specific lifecycle handling is needed. -4. Add the profile basename to `profiles/enabled.list`. -5. Run locally with: + +1. Create a profile file: + + ```text + profiles/example_module.profile + ``` + +2. Start with minimal metadata: + + ```sh + PROFILE_NAME="example_module" + PROFILE_DESCRIPTION="Example module reload validation" + MODULE_NAME="example_module" + MODULE_RELOAD_SUPPORTED="yes" + PROFILE_MODE_DEFAULT="basic" + PROFILE_REQUIRED_CMDS="modprobe rmmod ps sed grep" + MODULE_UNLOAD_CMD="modprobe -r example_module" + MODULE_LOAD_CMD="modprobe example_module" + PROFILE_EXPECT_ABSENT_AFTER_UNLOAD="example_module" + PROFILE_EXPECT_PRESENT_AFTER_LOAD="example_module" + PROFILE_SYSFS_PATTERNS="/sys/module/example_module" + ``` + +3. Add quiesce data only if the module has active users: + + ```sh + PROFILE_QUIESCE_SERVICES="example.service" + PROFILE_QUIESCE_PROC_PATTERNS="example-daemon example-client" + PROFILE_DEVICE_PATTERNS="/dev/example*" + ``` + +4. Use `PROFILE_QUIESCE_ONCE="yes"` if repeated service quiesce should happen only once per profile run. + +5. Add conflict rules if this profile is mutually exclusive with another profile: + + ```sh + PROFILE_SKIP_IF_MODULES_LOADED="other_transport_module" + ``` + +6. If the module has holders, unload the top-level holder first: + + ```sh + PROFILE_TOP_MODULE_CANDIDATES="example_card example_machine" + module_reload_profile_setup_stack + ``` + + Or create a purpose-built helper in `lib_module_reload.sh` if the logic is reusable. + +7. Add optional hooks only when required. + +8. Add the profile basename to the appropriate list: + + - `enabled.list` for default CI, + - `base.list` for base-image modules, + - `overlay.list` for overlay/downstream modules, + - team-specific lists if needed. + +9. Run locally: + + ```sh + ./run.sh --module example_module --iterations 1 + ``` + +10. Run shellcheck: + + ```sh + shellcheck -s sh Runner/suites/Kernel/Baseport/Module_Reload_Validation/run.sh + shellcheck -s sh Runner/utils/lib_module_reload.sh + shellcheck -s sh Runner/suites/Kernel/Baseport/Module_Reload_Validation/profiles/example_module.profile + ``` + +## Customizing profile lists + +### Create a team-specific list + +Example: + +```text +profiles/wifi.list +``` + +```text +ath11k_pci +ath11k_ahb +ath12k_pci +``` + +Run it: ```sh -./run.sh --module ath11k_pci +./run.sh --profile-list profiles/wifi.list ``` -No YAML duplication is needed for new profiles. +### Create a platform-specific list -## Result policy +Example: -### PASS -- all requested iterations for a profile pass successfully. +```text +profiles/rb3gen2.list +``` -### FAIL -- unload/load timeout, -- unload/load command failure, -- module state validation failure, -- profile hook failure, -- smoke validation failure. +```text +tc956x_pcie_eth +ath11k_ahb +venus_core +qcom_iris +snd_soc_qcom_sdw +``` + +Run it: + +```sh +./run.sh --profile-list profiles/rb3gen2.list +``` + +### Keep disruptive profiles out of default CI + +Do not put every profile into `enabled.list`. + +Profiles that can disrupt board access or display state should be run explicitly or through prepared profile lists: + +- `fastrpc`, +- `ath11k_ahb`, +- `ath11k_pci`, +- `snd_soc_qcom_sdw`, +- `msm`, +- `camera`, +- `iris`, +- `audioreach`. + +## Troubleshooting + +### Profile skips with “module not present on image” + +Check: + +```sh +lsmod | grep +modinfo +find /lib/modules/$(uname -r) /usr/lib/modules/$(uname -r) -name '.ko*' +``` + +This is expected when a profile is valid for another target but not present on the current image. + +### Profile skips with “conflicting active module is loaded” + +This means the profile is for an alternate transport/stack. + +Example: + +```text +ath11k_pci SKIP - conflicting active module is loaded: ath11k_ahb +``` + +Run the active transport profile instead: + +```sh +./run.sh --module ath11k_ahb +``` + +### `modprobe -r ` fails with `rc=1` + +Inspect: + +```sh +cat results/Module_Reload_Validation//iter_01/unload.log +cat results/Module_Reload_Validation//iter_01/unload_failure_state/holders.log +cat results/Module_Reload_Validation//iter_01/unload_failure_state/lsmod.log +cat results/Module_Reload_Validation//iter_01/unload_failure_state/ps.log +``` + +Common causes: + +- another module is holding the target, +- a userspace process has an open device node, +- a service restarted after quiesce, +- the module is part of the active display/audio/network path, +- the profile is targeting the wrong module variant for the platform. + +### `fastrpc` unload times out + +Check: + +```sh +cat results/Module_Reload_Validation/fastrpc/iter_01/hang_evidence/timeout_summary.log +cat results/Module_Reload_Validation/fastrpc/iter_01/hang_evidence/holders.log +cat results/Module_Reload_Validation/fastrpc/iter_01/hang_evidence/ps.log +cat results/Module_Reload_Validation/fastrpc/iter_01/hang_evidence/dmesg_after_sysrq.log +``` + +If the unload task is stuck in kernel space, the framework can capture evidence but cannot always recover the kernel unload path in-place. + +### `snd_soc_qcom_sdw` takes too long + +Use: + +```sh +PROFILE_QUIESCE_ONCE="yes" +``` + +Also run with fewer iterations during bring-up: + +```sh +./run.sh --module snd_soc_qcom_sdw --iterations 1 --timeout-unload 60 --timeout-load 60 +``` + +### `msm` unload fails + +`msm.ko` is the modern DRM display/GPU driver and may be held by active display/GPU clients. + +Inspect: + +```sh +lsmod | grep '^msm ' +ls -l /dev/dri/ +cat results/Module_Reload_Validation/msm/iter_01/unload_failure_state/holders.log +cat results/Module_Reload_Validation/msm/iter_01/unload_failure_state/ps.log +``` + +If active display is required for the test environment, keep `msm` out of broad overlay lists or mark it non-reloadable for that platform until a safe quiesce path is validated. + +## Safety notes + +This suite is intended for profiled, supported modules only. It should not blindly reload every loaded kernel module. + +Some profiles are disruptive: + +- Wi-Fi reload can interrupt network access. +- Ethernet reload can interrupt remote access. +- Display/GPU reload can stop Weston or display-manager. +- Camera reload can stop camera pipelines. +- Audio reload can stop active audio services. +- FastRPC reload can impact DSP clients. + +Keep `enabled.list` conservative for common CI. Run disruptive profiles explicitly or through prepared profile lists when the test plan expects those side effects. + +## Platform notes + +Module names differ across Qualcomm platforms and image combinations. + +Examples: + +- Wi-Fi can appear as `ath11k_pci`, `ath11k_ahb`, or `ath12k_pci`. +- Ethernet can appear as `dwmac_qcom_eth` or `tc956x_pcie_eth`. +- Upstream video can appear as `venus_core` or `qcom_iris`. +- Downstream video overlay can appear as `iris` or `iris_vpu`. +- Modern GPU/display uses `msm.ko`. +- Legacy KGSL uses `msm_kgsl.ko` and is not expected on modern RB3Gen2/newer DRM stacks. +- SoundWire/ASoC reload should target the top-level holder of `snd_soc_qcom_sdw`, not always `snd_soc_qcom_sdw` directly. + +Use these commands before deciding which profile belongs in a platform list: + +```sh +lsmod +ls /sys/module//holders 2>/dev/null +modinfo +find /lib/modules/$(uname -r) /usr/lib/modules/$(uname -r) -name '*.ko*' | grep -E '|' +``` -### SKIP -- module not present, -- module built into the kernel, -- required commands not available, -- profile explicitly not reloadable. - -## Notes -- This suite is intended for **profiled, supported modules**, not blind reload of every loaded kernel module. -- The current structure avoids hidden run.sh-to-library globals as much as possible by passing explicit arguments and hook context. -- Profile hooks receive context arguments from the engine, so module-specific logic can store logs in the correct iteration directory without depending on hidden globals. diff --git a/Runner/suites/Kernel/Baseport/Module_Reload_Validation/profiles/ath11k_ahb.profile b/Runner/suites/Kernel/Baseport/Module_Reload_Validation/profiles/ath11k_ahb.profile new file mode 100755 index 000000000..6c1e1d4cd --- /dev/null +++ b/Runner/suites/Kernel/Baseport/Module_Reload_Validation/profiles/ath11k_ahb.profile @@ -0,0 +1,35 @@ +#!/bin/sh +# SPDX-License-Identifier: BSD-3-Clause + +PROFILE_NAME="ath11k_ahb" +PROFILE_DESCRIPTION="ath11k AHB Wi-Fi module reload validation" +MODULE_NAME="ath11k_ahb" + +MODULE_RELOAD_SUPPORTED="yes" +PROFILE_MODE_DEFAULT="basic" +PROFILE_QUIESCE_ONCE="yes" + +PROFILE_REQUIRED_CMDS="modprobe rmmod systemctl ps sed grep readlink sort awk tr" + +PROFILE_SKIP_IF_MODULES_LOADED="ath11k_pci ath12k_pci" +PROFILE_UNLOAD_STACK="ath11k_ahb ath11k mac80211 cfg80211" + +PROFILE_QUIESCE_SERVICES="wpa_supplicant.service NetworkManager.service systemd-networkd.service" +PROFILE_PROC_PATTERNS="wpa_supplicant NetworkManager iwd" +PROFILE_QUIESCE_PROC_PATTERNS="$PROFILE_PROC_PATTERNS" + +PROFILE_DEVICE_PATTERNS="/sys/class/net/wlan* /sys/class/ieee80211/*" +PROFILE_SYSFS_PATTERNS="/sys/module/ath11k_ahb /sys/module/ath11k /sys/module/mac80211 /sys/module/cfg80211" + +PROFILE_EXPECT_ABSENT_AFTER_UNLOAD="ath11k_ahb ath11k" +PROFILE_EXPECT_PRESENT_AFTER_LOAD="ath11k_ahb" + +module_reload_profile_setup_stack + +profile_quiesce() { + module_reload_profile_quiesce_resources "$PROFILE_NAME" "$1" 20 +} + +profile_smoke() { + module_reload_profile_smoke_modules_present "$PROFILE_NAME" "$PROFILE_EXPECT_PRESENT_AFTER_LOAD" +} diff --git a/Runner/suites/Kernel/Baseport/Module_Reload_Validation/profiles/ath11k_pci.profile b/Runner/suites/Kernel/Baseport/Module_Reload_Validation/profiles/ath11k_pci.profile new file mode 100755 index 000000000..6143d00aa --- /dev/null +++ b/Runner/suites/Kernel/Baseport/Module_Reload_Validation/profiles/ath11k_pci.profile @@ -0,0 +1,25 @@ +#!/bin/sh +#SPDX-License-Identifier: BSD-3-Clause + +PROFILE_NAME="ath11k_pci" +PROFILE_DESCRIPTION="ath11k PCI Wi-Fi module reload validation" +MODULE_NAME="ath11k_pci" + +PROFILE_SKIP_IF_MODULES_LOADED="ath11k_ahb ath12k_pci" +PROFILE_UNLOAD_STACK="ath11k_pci ath11k mac80211 cfg80211" +PROFILE_EXPECT_ABSENT_AFTER_UNLOAD="ath11k_pci ath11k" + +PROFILE_MODE_DEFAULT="basic" +PROFILE_QUIESCE_ONCE="yes" +PROFILE_QUIESCE_SERVICES="wpa_supplicant.service NetworkManager.service systemd-networkd.service" +PROFILE_PROC_PATTERNS="wpa_supplicant NetworkManager iwd" +PROFILE_QUIESCE_PROC_PATTERNS="$PROFILE_PROC_PATTERNS" + +PROFILE_DEVICE_PATTERNS="/sys/class/net/wlan* /sys/class/ieee80211/*" +PROFILE_SYSFS_PATTERNS="/sys/module/ath11k_pci /sys/module/ath11k /sys/module/mac80211 /sys/module/cfg80211" + +module_reload_profile_setup_stack + +profile_quiesce() { + module_reload_profile_quiesce_resources "$PROFILE_NAME" "$1" 20 +} diff --git a/Runner/suites/Kernel/Baseport/Module_Reload_Validation/profiles/ath12k_pci.profile b/Runner/suites/Kernel/Baseport/Module_Reload_Validation/profiles/ath12k_pci.profile new file mode 100755 index 000000000..c3b91064c --- /dev/null +++ b/Runner/suites/Kernel/Baseport/Module_Reload_Validation/profiles/ath12k_pci.profile @@ -0,0 +1,21 @@ +#!/bin/sh +# SPDX-License-Identifier: BSD-3-Clause + +PROFILE_NAME="ath12k_pci" +PROFILE_DESCRIPTION="ath12k PCI Wi-Fi module reload validation" +MODULE_NAME="ath12k_pci" + +MODULE_RELOAD_SUPPORTED="yes" +PROFILE_MODE_DEFAULT="daemon_lifecycle" + +PROFILE_QUIESCE_ONCE="yes" +PROFILE_REQUIRED_CMDS="modprobe rmmod systemctl ps sed" +PROFILE_SERVICES="wpa_supplicant.service NetworkManager.service systemd-networkd.service" +PROFILE_PROC_PATTERNS="wpa_supplicant NetworkManager iwd" + +MODULE_UNLOAD_CMD="modprobe -r ath12k_pci ath12k mac80211 cfg80211" +MODULE_LOAD_CMD="modprobe ath12k_pci" + +PROFILE_EXPECT_ABSENT_AFTER_UNLOAD="ath12k_pci ath12k" +PROFILE_DEVICE_PATTERNS="/sys/class/net/wlan* /sys/class/ieee80211/*" +PROFILE_SYSFS_PATTERNS="/sys/module/ath12k_pci /sys/module/ath12k /sys/module/mac80211 /sys/module/cfg80211" diff --git a/Runner/suites/Kernel/Baseport/Module_Reload_Validation/profiles/audioreach.profile b/Runner/suites/Kernel/Baseport/Module_Reload_Validation/profiles/audioreach.profile new file mode 100755 index 000000000..a1a9e3727 --- /dev/null +++ b/Runner/suites/Kernel/Baseport/Module_Reload_Validation/profiles/audioreach.profile @@ -0,0 +1,19 @@ +#!/bin/sh +# SPDX-License-Identifier: BSD-3-Clause + +PROFILE_NAME="audioreach" +PROFILE_DESCRIPTION="AudioReach downstream audio overlay module reload validation" +MODULE_NAME="snd_soc_qdsp6" + +MODULE_RELOAD_SUPPORTED="yes" +PROFILE_MODE_DEFAULT="daemon_lifecycle" + +PROFILE_REQUIRED_CMDS="modprobe rmmod systemctl ps sed" +PROFILE_SERVICES="audioserver.service pipewire.service pulseaudio.service" +PROFILE_PROC_PATTERNS="audioserver pipewire pulseaudio" +MODULE_UNLOAD_CMD="modprobe -r snd_soc_qdsp6 q6asm q6adm q6afe apr" +MODULE_LOAD_CMD="modprobe snd_soc_qdsp6" + +PROFILE_EXPECT_ABSENT_AFTER_UNLOAD="snd_soc_qdsp6" +PROFILE_DEVICE_PATTERNS="/dev/snd/*" +PROFILE_SYSFS_PATTERNS="/sys/module/snd_soc_qdsp6 /sys/module/q6asm /sys/module/q6adm /sys/module/q6afe /sys/module/apr" diff --git a/Runner/suites/Kernel/Baseport/Module_Reload_Validation/profiles/base.list b/Runner/suites/Kernel/Baseport/Module_Reload_Validation/profiles/base.list new file mode 100755 index 000000000..0325b25d0 --- /dev/null +++ b/Runner/suites/Kernel/Baseport/Module_Reload_Validation/profiles/base.list @@ -0,0 +1,14 @@ +fastrpc +dwmac_qcom_eth +tc956x_pcie_eth +ath11k_pci +ath11k_ahb +ath12k_pci +qcedev_mod_dlkm +qcrypto_msm_dlkm +qrng_dlkm +spcom +mvm +venus_core +qcom_iris +snd_soc_qcom_sdw diff --git a/Runner/suites/Kernel/Baseport/Module_Reload_Validation/profiles/camera.profile b/Runner/suites/Kernel/Baseport/Module_Reload_Validation/profiles/camera.profile new file mode 100755 index 000000000..84f16701b --- /dev/null +++ b/Runner/suites/Kernel/Baseport/Module_Reload_Validation/profiles/camera.profile @@ -0,0 +1,45 @@ +#!/bin/sh +# SPDX-License-Identifier: BSD-3-Clause + +PROFILE_NAME="camera" +PROFILE_DESCRIPTION="CAMX camera KMD overlay reload validation" + +CAMERA_MODULE_NAME="" +for cname in camera camera_kmd camx_kmd; do + if mrv_module_loaded "$cname" || modinfo "$cname" >/dev/null 2>&1; then + CAMERA_MODULE_NAME="$cname" + break + fi +done + +if [ -n "$CAMERA_MODULE_NAME" ]; then + MODULE_NAME="$CAMERA_MODULE_NAME" + MODULE_RELOAD_SUPPORTED="yes" + MODULE_UNLOAD_CMD="modprobe -r $CAMERA_MODULE_NAME camss" + MODULE_LOAD_CMD="modprobe $CAMERA_MODULE_NAME" + PROFILE_EXPECT_ABSENT_AFTER_UNLOAD="$CAMERA_MODULE_NAME" +else + MODULE_NAME="camera" + MODULE_RELOAD_SUPPORTED="no" +fi + +PROFILE_MODE_DEFAULT="daemon_lifecycle" +PROFILE_REQUIRED_CMDS="modprobe rmmod systemctl ps sed" +PROFILE_SERVICES="qmmf-server.service qmmf-recorder.service" +PROFILE_PROC_PATTERNS="qmmf-server qmmf-recorder gst-launch" +PROFILE_DEVICE_PATTERNS="/dev/video* /dev/media*" +PROFILE_SYSFS_PATTERNS="/sys/module/camera /sys/module/camera_kmd /sys/module/camx_kmd /sys/module/camss" + +profile_quiesce() { + iter_dir="$1" + : "${iter_dir:=}" + + module_reload_stop_mask_kill_services "$PROFILE_SERVICES" "$PROFILE_PROC_PATTERNS" 20 "$PROFILE_NAME" || return 1 + + if command -v pkill >/dev/null 2>&1; then + pkill -f "gst-launch.*camera" 2>/dev/null || true + fi + + sleep 2 + return 0 +} diff --git a/Runner/suites/Kernel/Baseport/Module_Reload_Validation/profiles/dwmac_qcom_eth.profile b/Runner/suites/Kernel/Baseport/Module_Reload_Validation/profiles/dwmac_qcom_eth.profile new file mode 100755 index 000000000..9fc7a0a6b --- /dev/null +++ b/Runner/suites/Kernel/Baseport/Module_Reload_Validation/profiles/dwmac_qcom_eth.profile @@ -0,0 +1,17 @@ +#!/bin/sh +# SPDX-License-Identifier: BSD-3-Clause + +PROFILE_NAME="dwmac_qcom_eth" +PROFILE_DESCRIPTION="Qualcomm DWMAC/STMMAC Ethernet module reload validation" +MODULE_NAME="dwmac_qcom_eth" + +MODULE_RELOAD_SUPPORTED="yes" +PROFILE_MODE_DEFAULT="basic" + +PROFILE_REQUIRED_CMDS="modprobe rmmod ps sed" +MODULE_UNLOAD_CMD="modprobe -r dwmac_qcom_eth stmmac_platform stmmac" +MODULE_LOAD_CMD="modprobe dwmac_qcom_eth" + +PROFILE_EXPECT_ABSENT_AFTER_UNLOAD="dwmac_qcom_eth stmmac_platform stmmac" +PROFILE_DEVICE_PATTERNS="/sys/class/net/*" +PROFILE_SYSFS_PATTERNS="/sys/module/dwmac_qcom_eth /sys/module/stmmac_platform /sys/module/stmmac" diff --git a/Runner/suites/Kernel/Baseport/Module_Reload_Validation/profiles/fastrpc.profile b/Runner/suites/Kernel/Baseport/Module_Reload_Validation/profiles/fastrpc.profile index 91efdcc46..8f5f66bcd 100755 --- a/Runner/suites/Kernel/Baseport/Module_Reload_Validation/profiles/fastrpc.profile +++ b/Runner/suites/Kernel/Baseport/Module_Reload_Validation/profiles/fastrpc.profile @@ -1,16 +1,33 @@ #!/bin/sh # Copyright (c) Qualcomm Technologies, Inc. and/or its subsidiaries. -# SPDX-License-Identifier: BSD-3-Clause-Clear +# SPDX-License-Identifier: BSD-3-Clause PROFILE_NAME="fastrpc" PROFILE_DESCRIPTION="FastRPC module reload validation" MODULE_NAME="fastrpc" -MODULE_RELOAD_SUPPORTED="yes" -PROFILE_MODE_DEFAULT="daemon_lifecycle" +PROFILE_MODE_DEFAULT="basic" +PROFILE_REQUIRED_CMDS="modprobe rmmod systemctl ps sed grep readlink sort awk tr" -PROFILE_REQUIRED_CMDS="modprobe rmmod systemctl ps sed" -PROFILE_SERVICES="adsprpcd.service cdsprpcd.service" -PROFILE_PROC_PATTERNS="/usr/bin/adsprpcd /usr/bin/cdsprpcd" -PROFILE_DEVICE_PATTERNS="/dev/fastrpc-* /dev/*dsp*" -PROFILE_SYSFS_PATTERNS="/sys/module/fastrpc /sys/class/remoteproc/*" +PROFILE_QUIESCE_ONCE="yes" +PROFILE_QUIESCE_SERVICES="adsprpcd.service cdsprpcd.service" +PROFILE_PROC_PATTERNS="adsprpcd cdsprpcd fastrpc_shell" +PROFILE_QUIESCE_PROC_PATTERNS="$PROFILE_PROC_PATTERNS" + +PROFILE_DEVICE_PATTERNS="/dev/fastrpc-* /dev/adsprpc-* /dev/cdsprpc-* /dev/*dsp*" +PROFILE_QUIESCE_DEVICE_PATTERNS="$PROFILE_DEVICE_PATTERNS" + +PROFILE_SYSFS_PATTERNS="/sys/module/fastrpc /sys/class/remoteproc/* /sys/kernel/debug/fastrpc* /sys/kernel/debug/adsprpc*" + +PROFILE_EXPECT_ABSENT_AFTER_UNLOAD="fastrpc" +PROFILE_EXPECT_PRESENT_AFTER_LOAD="fastrpc" + +module_reload_profile_setup_stack + +profile_quiesce() { + module_reload_profile_quiesce_resources "$PROFILE_NAME" "$1" 20 +} + +profile_smoke() { + module_reload_profile_smoke_modules_present "$PROFILE_NAME" "$PROFILE_EXPECT_PRESENT_AFTER_LOAD" +} diff --git a/Runner/suites/Kernel/Baseport/Module_Reload_Validation/profiles/iris.profile b/Runner/suites/Kernel/Baseport/Module_Reload_Validation/profiles/iris.profile new file mode 100755 index 000000000..3cd7057ac --- /dev/null +++ b/Runner/suites/Kernel/Baseport/Module_Reload_Validation/profiles/iris.profile @@ -0,0 +1,29 @@ +#!/bin/sh +# SPDX-License-Identifier: BSD-3-Clause + +PROFILE_NAME="iris" +PROFILE_DESCRIPTION="Downstream Iris video overlay module reload validation" + +IRIS_MODULE_NAME="" +for vname in iris iris_vpu; do + if mrv_module_loaded "$vname" || modinfo "$vname" >/dev/null 2>&1; then + IRIS_MODULE_NAME="$vname" + break + fi +done + +if [ -n "$IRIS_MODULE_NAME" ]; then + MODULE_NAME="$IRIS_MODULE_NAME" + MODULE_RELOAD_SUPPORTED="yes" + MODULE_UNLOAD_CMD="modprobe -r $IRIS_MODULE_NAME" + MODULE_LOAD_CMD="modprobe $IRIS_MODULE_NAME" + PROFILE_EXPECT_ABSENT_AFTER_UNLOAD="$IRIS_MODULE_NAME" +else + MODULE_NAME="iris" + MODULE_RELOAD_SUPPORTED="no" +fi + +PROFILE_MODE_DEFAULT="basic" +PROFILE_REQUIRED_CMDS="modprobe rmmod ps sed" +PROFILE_DEVICE_PATTERNS="/dev/video* /dev/media*" +PROFILE_SYSFS_PATTERNS="/sys/module/iris /sys/module/iris_vpu" diff --git a/Runner/suites/Kernel/Baseport/Module_Reload_Validation/profiles/msm.profile b/Runner/suites/Kernel/Baseport/Module_Reload_Validation/profiles/msm.profile new file mode 100644 index 000000000..b72943704 --- /dev/null +++ b/Runner/suites/Kernel/Baseport/Module_Reload_Validation/profiles/msm.profile @@ -0,0 +1,24 @@ +# Copyright (c) Qualcomm Technologies, Inc. and/or its subsidiaries. +# SPDX-License-Identifier: BSD-3-Clause-Clear + +PROFILE_NAME="msm" +PROFILE_DESCRIPTION="Qualcomm MSM DRM / Adreno GPU driver reload validation" +MODULE_NAME="msm" + +PROFILE_MODE_DEFAULT="basic" +PROFILE_REQUIRED_CMDS="modprobe rmmod grep ps sed" + +PROFILE_QUIESCE_ONCE="yes" +PROFILE_QUIESCE_SERVICES="weston.service display-manager.service" +PROFILE_QUIESCE_PROC_PATTERNS="weston Xorg Xwayland kmscube modetest glmark2 glmark2-es2 glmark2-es2-drm es2gears glxgears vkmark" + +PROFILE_DEVICE_PATTERNS="/dev/dri/* /dev/fb*" +PROFILE_QUIESCE_DEVICE_PATTERNS="$PROFILE_DEVICE_PATTERNS" + +PROFILE_SYSFS_PATTERNS="/sys/module/msm /sys/class/drm /sys/kernel/debug/dri/*" + +MODULE_UNLOAD_CMD="modprobe -r msm" +MODULE_LOAD_CMD="modprobe msm" + +PROFILE_EXPECT_ABSENT_AFTER_UNLOAD="msm" +PROFILE_EXPECT_PRESENT_AFTER_LOAD="msm" diff --git a/Runner/suites/Kernel/Baseport/Module_Reload_Validation/profiles/msm_drm.profile b/Runner/suites/Kernel/Baseport/Module_Reload_Validation/profiles/msm_drm.profile new file mode 100755 index 000000000..5a9d0fedd --- /dev/null +++ b/Runner/suites/Kernel/Baseport/Module_Reload_Validation/profiles/msm_drm.profile @@ -0,0 +1,19 @@ +#!/bin/sh +# SPDX-License-Identifier: BSD-3-Clause + +PROFILE_NAME="msm_drm" +PROFILE_DESCRIPTION="Downstream MSM DRM display overlay module reload validation" +MODULE_NAME="msm_drm" + +MODULE_RELOAD_SUPPORTED="yes" +PROFILE_MODE_DEFAULT="daemon_lifecycle" + +PROFILE_REQUIRED_CMDS="modprobe rmmod systemctl ps sed" +PROFILE_SERVICES="weston.service display-manager.service" +PROFILE_PROC_PATTERNS="weston kmscube glmark2" +MODULE_UNLOAD_CMD="modprobe -r msm_drm" +MODULE_LOAD_CMD="modprobe msm_drm" + +PROFILE_EXPECT_ABSENT_AFTER_UNLOAD="msm_drm" +PROFILE_DEVICE_PATTERNS="/dev/dri/*" +PROFILE_SYSFS_PATTERNS="/sys/module/msm_drm /sys/class/drm/*" diff --git a/Runner/suites/Kernel/Baseport/Module_Reload_Validation/profiles/msm_kgsl.profile b/Runner/suites/Kernel/Baseport/Module_Reload_Validation/profiles/msm_kgsl.profile new file mode 100755 index 000000000..0c014c5b9 --- /dev/null +++ b/Runner/suites/Kernel/Baseport/Module_Reload_Validation/profiles/msm_kgsl.profile @@ -0,0 +1,26 @@ +# Copyright (c) Qualcomm Technologies, Inc. and/or its subsidiaries. +# SPDX-License-Identifier: BSD-3-Clause-Clear + +PROFILE_NAME="msm_kgsl" +PROFILE_DESCRIPTION="Legacy Qualcomm KGSL GPU driver reload validation" +MODULE_NAME="msm_kgsl" + +PROFILE_MODE_DEFAULT="basic" +PROFILE_REQUIRED_CMDS="modprobe rmmod grep ps sed" + +if ! mrv_module_available "msm_kgsl"; then + MODULE_RELOAD_SUPPORTED="no" + MODULE_RELOAD_UNSUPPORTED_REASON="legacy KGSL driver msm_kgsl not present; modern Adreno platforms use MSM DRM driver msm" +else + PROFILE_QUIESCE_SERVICES="weston.service display-manager.service" + PROFILE_QUIESCE_PROC_PATTERNS="weston Xorg Xwayland glmark2 kmscube modetest es2gears glxgears vkmark" + + PROFILE_DEVICE_PATTERNS="/dev/kgsl-3d0 /dev/dri/*" + PROFILE_SYSFS_PATTERNS="/sys/module/msm_kgsl /sys/class/kgsl/kgsl-3d0" + + PROFILE_UNLOAD_STACK="governor_msm_adreno_tz governor_gpubw_mon msm_kgsl" + PROFILE_EXPECT_ABSENT_AFTER_UNLOAD="msm_kgsl" + PROFILE_EXPECT_PRESENT_AFTER_LOAD="msm_kgsl" + + module_reload_profile_setup_stack +fi diff --git a/Runner/suites/Kernel/Baseport/Module_Reload_Validation/profiles/mvm.profile b/Runner/suites/Kernel/Baseport/Module_Reload_Validation/profiles/mvm.profile new file mode 100755 index 000000000..03b5be3ea --- /dev/null +++ b/Runner/suites/Kernel/Baseport/Module_Reload_Validation/profiles/mvm.profile @@ -0,0 +1,18 @@ +#!/bin/sh +# SPDX-License-Identifier: BSD-3-Clause + +PROFILE_NAME="mvm" +PROFILE_DESCRIPTION="MVM DLKM reload validation" +MODULE_NAME="mvm" + +MODULE_RELOAD_SUPPORTED="yes" +PROFILE_MODE_DEFAULT="daemon_lifecycle" + +PROFILE_REQUIRED_CMDS="modprobe rmmod systemctl ps sed" +PROFILE_SERVICES="mvm.service" +PROFILE_PROC_PATTERNS="mvm" +MODULE_UNLOAD_CMD="modprobe -r mvm" +MODULE_LOAD_CMD="modprobe mvm" + +PROFILE_EXPECT_ABSENT_AFTER_UNLOAD="mvm" +PROFILE_SYSFS_PATTERNS="/sys/module/mvm" diff --git a/Runner/suites/Kernel/Baseport/Module_Reload_Validation/profiles/overlay.list b/Runner/suites/Kernel/Baseport/Module_Reload_Validation/profiles/overlay.list new file mode 100755 index 000000000..1221ceb10 --- /dev/null +++ b/Runner/suites/Kernel/Baseport/Module_Reload_Validation/profiles/overlay.list @@ -0,0 +1,4 @@ +msm +camera +iris +audioreach diff --git a/Runner/suites/Kernel/Baseport/Module_Reload_Validation/profiles/qcedev_mod_dlkm.profile b/Runner/suites/Kernel/Baseport/Module_Reload_Validation/profiles/qcedev_mod_dlkm.profile new file mode 100755 index 000000000..5b8678c5a --- /dev/null +++ b/Runner/suites/Kernel/Baseport/Module_Reload_Validation/profiles/qcedev_mod_dlkm.profile @@ -0,0 +1,17 @@ +#!/bin/sh +# SPDX-License-Identifier: BSD-3-Clause + +PROFILE_NAME="qcedev_mod_dlkm" +PROFILE_DESCRIPTION="QCEDEV crypto DLKM reload validation" +MODULE_NAME="qcedev_mod_dlkm" + +MODULE_RELOAD_SUPPORTED="yes" +PROFILE_MODE_DEFAULT="basic" + +PROFILE_REQUIRED_CMDS="modprobe rmmod ps sed" +MODULE_UNLOAD_CMD="modprobe -r qcedev_mod_dlkm qce50_dlkm" +MODULE_LOAD_CMD="modprobe qcedev_mod_dlkm" + +PROFILE_EXPECT_ABSENT_AFTER_UNLOAD="qcedev_mod_dlkm qce50_dlkm" +PROFILE_DEVICE_PATTERNS="/dev/qce* /dev/qcedev*" +PROFILE_SYSFS_PATTERNS="/sys/module/qcedev_mod_dlkm /sys/module/qce50_dlkm" diff --git a/Runner/suites/Kernel/Baseport/Module_Reload_Validation/profiles/qcom_iris.profile b/Runner/suites/Kernel/Baseport/Module_Reload_Validation/profiles/qcom_iris.profile new file mode 100755 index 000000000..ef204b3da --- /dev/null +++ b/Runner/suites/Kernel/Baseport/Module_Reload_Validation/profiles/qcom_iris.profile @@ -0,0 +1,18 @@ +#!/bin/sh +# SPDX-License-Identifier: BSD-3-Clause + +PROFILE_NAME="qcom_iris" +PROFILE_DESCRIPTION="Qualcomm Iris video module reload validation" +MODULE_NAME="qcom_iris" + +MODULE_RELOAD_SUPPORTED="yes" +PROFILE_MODE_DEFAULT="basic" + +PROFILE_REQUIRED_CMDS="modprobe rmmod ps sed" + +MODULE_UNLOAD_CMD="modprobe -r qcom_iris" +MODULE_LOAD_CMD="modprobe qcom_iris" + +PROFILE_EXPECT_ABSENT_AFTER_UNLOAD="qcom_iris" +PROFILE_DEVICE_PATTERNS="/dev/video* /dev/media*" +PROFILE_SYSFS_PATTERNS="/sys/module/qcom_iris" diff --git a/Runner/suites/Kernel/Baseport/Module_Reload_Validation/profiles/qcrypto_msm_dlkm.profile b/Runner/suites/Kernel/Baseport/Module_Reload_Validation/profiles/qcrypto_msm_dlkm.profile new file mode 100755 index 000000000..3ca74bc23 --- /dev/null +++ b/Runner/suites/Kernel/Baseport/Module_Reload_Validation/profiles/qcrypto_msm_dlkm.profile @@ -0,0 +1,18 @@ +#!/bin/sh +# SPDX-License-Identifier: BSD-3-Clause + +PROFILE_NAME="qcrypto_msm_dlkm" +PROFILE_DESCRIPTION="QCrypto MSM DLKM reload validation" +MODULE_NAME="qcrypto_msm_dlkm" + +MODULE_RELOAD_SUPPORTED="yes" +PROFILE_MODE_DEFAULT="daemon_lifecycle" + +PROFILE_REQUIRED_CMDS="modprobe rmmod systemctl ps sed" +PROFILE_SERVICES="qcrypto.service" +PROFILE_PROC_PATTERNS="qcrypto" +MODULE_UNLOAD_CMD="modprobe -r qcrypto_msm_dlkm" +MODULE_LOAD_CMD="modprobe qcrypto_msm_dlkm" + +PROFILE_EXPECT_ABSENT_AFTER_UNLOAD="qcrypto_msm_dlkm" +PROFILE_SYSFS_PATTERNS="/sys/module/qcrypto_msm_dlkm" diff --git a/Runner/suites/Kernel/Baseport/Module_Reload_Validation/profiles/qrng_dlkm.profile b/Runner/suites/Kernel/Baseport/Module_Reload_Validation/profiles/qrng_dlkm.profile new file mode 100755 index 000000000..8aae7f13f --- /dev/null +++ b/Runner/suites/Kernel/Baseport/Module_Reload_Validation/profiles/qrng_dlkm.profile @@ -0,0 +1,18 @@ +#!/bin/sh +# SPDX-License-Identifier: BSD-3-Clause + +PROFILE_NAME="qrng_dlkm" +PROFILE_DESCRIPTION="QRNG DLKM reload validation" +MODULE_NAME="qrng_dlkm" + +MODULE_RELOAD_SUPPORTED="yes" +PROFILE_MODE_DEFAULT="daemon_lifecycle" + +PROFILE_REQUIRED_CMDS="modprobe rmmod systemctl ps sed" +PROFILE_SERVICES="qrng.service" +PROFILE_PROC_PATTERNS="qrng" +MODULE_UNLOAD_CMD="modprobe -r qrng_dlkm" +MODULE_LOAD_CMD="modprobe qrng_dlkm" + +PROFILE_EXPECT_ABSENT_AFTER_UNLOAD="qrng_dlkm" +PROFILE_SYSFS_PATTERNS="/sys/module/qrng_dlkm" diff --git a/Runner/suites/Kernel/Baseport/Module_Reload_Validation/profiles/snd_soc_qcom_sdw.profile b/Runner/suites/Kernel/Baseport/Module_Reload_Validation/profiles/snd_soc_qcom_sdw.profile new file mode 100755 index 000000000..ba9d5537b --- /dev/null +++ b/Runner/suites/Kernel/Baseport/Module_Reload_Validation/profiles/snd_soc_qcom_sdw.profile @@ -0,0 +1,32 @@ +#!/bin/sh +#SPDX-License-Identifier: BSD-3-Clause + +PROFILE_NAME="snd_soc_qcom_sdw" +PROFILE_DESCRIPTION="Qualcomm ASoC/SoundWire stack reload validation" +MODULE_NAME="snd_soc_qcom_sdw" + +PROFILE_MODE_DEFAULT="basic" +PROFILE_QUIESCE_ONCE="yes" +PROFILE_REQUIRED_CMDS="modprobe rmmod ps sed grep readlink sort awk tr" + +PROFILE_TOP_MODULE_CANDIDATES="snd_soc_sc8280xp" + +PROFILE_QUIESCE_SERVICES="pipewire.service wireplumber.service pulseaudio.service" +PROFILE_PROC_PATTERNS="pipewire-pulse pipewire wireplumber pulseaudio aplay arecord speaker-test pw-play pw-record gst-launch" +PROFILE_QUIESCE_PROC_PATTERNS="$PROFILE_PROC_PATTERNS" + +PROFILE_DEVICE_PATTERNS="/dev/snd/*" +PROFILE_QUIESCE_DEVICE_PATTERNS="$PROFILE_DEVICE_PATTERNS" + +PROFILE_SYSFS_PATTERNS="/sys/module/snd_soc_sc8280xp /sys/module/snd_soc_qcom_sdw /sys/module/soundwire_qcom /sys/module/snd_soc_wsa883x /sys/bus/soundwire/devices/*" + +module_reload_profile_setup_stack + +profile_quiesce() { + module_reload_profile_quiesce_resources "$PROFILE_NAME" "$1" 20 +} + +profile_smoke() { + module_reload_profile_smoke_modules_present "$PROFILE_NAME" "$PROFILE_EXPECT_PRESENT_AFTER_LOAD" +} + diff --git a/Runner/suites/Kernel/Baseport/Module_Reload_Validation/profiles/spcom.profile b/Runner/suites/Kernel/Baseport/Module_Reload_Validation/profiles/spcom.profile new file mode 100755 index 000000000..d02ad6c22 --- /dev/null +++ b/Runner/suites/Kernel/Baseport/Module_Reload_Validation/profiles/spcom.profile @@ -0,0 +1,19 @@ +#!/bin/sh +# SPDX-License-Identifier: BSD-3-Clause + +PROFILE_NAME="spcom" +PROFILE_DESCRIPTION="Secure Processor communication module reload validation" +MODULE_NAME="spcom" + +MODULE_RELOAD_SUPPORTED="yes" +PROFILE_MODE_DEFAULT="daemon_lifecycle" + +PROFILE_REQUIRED_CMDS="modprobe rmmod systemctl ps sed" +PROFILE_SERVICES="spdaemon.service qseecomd.service keymaster-4-0.service" +PROFILE_PROC_PATTERNS="spdaemon qseecomd keymaster" +MODULE_UNLOAD_CMD="modprobe -r spcom" +MODULE_LOAD_CMD="modprobe spcom" + +PROFILE_EXPECT_ABSENT_AFTER_UNLOAD="spcom" +PROFILE_DEVICE_PATTERNS="/dev/spcom* /dev/qseecom*" +PROFILE_SYSFS_PATTERNS="/sys/module/spcom" diff --git a/Runner/suites/Kernel/Baseport/Module_Reload_Validation/profiles/tc956x_pcie_eth.profile b/Runner/suites/Kernel/Baseport/Module_Reload_Validation/profiles/tc956x_pcie_eth.profile new file mode 100755 index 000000000..660bbdf35 --- /dev/null +++ b/Runner/suites/Kernel/Baseport/Module_Reload_Validation/profiles/tc956x_pcie_eth.profile @@ -0,0 +1,18 @@ +#!/bin/sh +# SPDX-License-Identifier: BSD-3-Clause + +PROFILE_NAME="tc956x_pcie_eth" +PROFILE_DESCRIPTION="TC956x PCIe Ethernet module reload validation" +MODULE_NAME="tc956x_pcie_eth" + +MODULE_RELOAD_SUPPORTED="yes" +PROFILE_MODE_DEFAULT="basic" + +PROFILE_REQUIRED_CMDS="modprobe rmmod ps sed" + +MODULE_UNLOAD_CMD="modprobe -r tc956x_pcie_eth" +MODULE_LOAD_CMD="modprobe tc956x_pcie_eth" + +PROFILE_EXPECT_ABSENT_AFTER_UNLOAD="tc956x_pcie_eth" +PROFILE_DEVICE_PATTERNS="/sys/class/net/*" +PROFILE_SYSFS_PATTERNS="/sys/module/tc956x_pcie_eth" diff --git a/Runner/suites/Kernel/Baseport/Module_Reload_Validation/profiles/venus_core.profile b/Runner/suites/Kernel/Baseport/Module_Reload_Validation/profiles/venus_core.profile new file mode 100755 index 000000000..c40f4c7c7 --- /dev/null +++ b/Runner/suites/Kernel/Baseport/Module_Reload_Validation/profiles/venus_core.profile @@ -0,0 +1,17 @@ +#!/bin/sh +# SPDX-License-Identifier: BSD-3-Clause + +PROFILE_NAME="venus_core" +PROFILE_DESCRIPTION="Upstream Venus video module reload validation" +MODULE_NAME="venus_core" + +MODULE_RELOAD_SUPPORTED="yes" +PROFILE_MODE_DEFAULT="basic" + +PROFILE_REQUIRED_CMDS="modprobe rmmod ps sed" +MODULE_UNLOAD_CMD="modprobe -r venus_enc venus_dec venus_core" +MODULE_LOAD_CMD="modprobe venus_core" + +PROFILE_EXPECT_ABSENT_AFTER_UNLOAD="venus_enc venus_dec venus_core" +PROFILE_DEVICE_PATTERNS="/dev/video* /dev/media*" +PROFILE_SYSFS_PATTERNS="/sys/module/venus_core /sys/module/venus_enc /sys/module/venus_dec" diff --git a/Runner/suites/Kernel/Baseport/Module_Reload_Validation/run.sh b/Runner/suites/Kernel/Baseport/Module_Reload_Validation/run.sh index efba6b83a..d914d5ef1 100755 --- a/Runner/suites/Kernel/Baseport/Module_Reload_Validation/run.sh +++ b/Runner/suites/Kernel/Baseport/Module_Reload_Validation/run.sh @@ -1,6 +1,6 @@ #!/bin/sh # Copyright (c) Qualcomm Technologies, Inc. and/or its subsidiaries. -# SPDX-License-Identifier: BSD-3-Clause-Clear +# SPDX-License-Identifier: BSD-3-Clause SCRIPT_DIR="$( cd "$(dirname "$0")" || exit 1 @@ -60,7 +60,7 @@ Usage: $0 [options] --mode MODE Profile mode override --profile-dir PATH Override profile directory --profile-list FILE Override enabled profile list file - --enable-sysrq-hang-dump Enable sysrq dump on timeout paths + --enable-sysrq-hang-dump [0|1] Enable sysrq dump on timeout paths, optional value accepted --disable-sysrq-hang-dump Disable sysrq dump on timeout paths --verbose Enable verbose shell logging --help|-h Show this help @@ -69,17 +69,39 @@ EOF # shellcheck disable=SC2317 # invoked via trap cleanup() { + if command -v module_reload_restore_recorded_services >/dev/null 2>&1; then + module_reload_restore_recorded_services "$TESTNAME-cleanup" >/dev/null 2>&1 || true + fi + if [ -n "$PROFILE_TMP_LIST" ]; then rm -f "$PROFILE_TMP_LIST" 2>/dev/null fi } - + +parse_sysrq_value() { + value="$1" + + case "$value" in + 1|yes|true|enable|enabled|on) + ENABLE_SYSRQ_HANG_DUMP=1 + ;; + 0|no|false|disable|disabled|off) + ENABLE_SYSRQ_HANG_DUMP=0 + ;; + *) + log_error "Invalid sysrq hang dump value: $value" + usage + exit 1 + ;; + esac +} + trap 'cleanup' EXIT INT TERM while [ $# -gt 0 ]; do case "$1" in --module) - if [ $# -lt 2 ] || [ -z "$2" ]; then + if [ $# -lt 2 ]; then log_error "Missing value for --module" usage exit 1 @@ -124,7 +146,7 @@ while [ $# -gt 0 ]; do shift 2 ;; --mode) - if [ $# -lt 2 ] || [ -z "$2" ]; then + if [ $# -lt 2 ]; then log_error "Missing value for --mode" usage exit 1 @@ -142,7 +164,7 @@ while [ $# -gt 0 ]; do shift 2 ;; --profile-list) - if [ $# -lt 2 ] || [ -z "$2" ]; then + if [ $# -lt 2 ]; then log_error "Missing value for --profile-list" usage exit 1 @@ -151,8 +173,21 @@ while [ $# -gt 0 ]; do shift 2 ;; --enable-sysrq-hang-dump) - ENABLE_SYSRQ_HANG_DUMP=1 - shift + if [ $# -ge 2 ]; then + case "$2" in + --*) + ENABLE_SYSRQ_HANG_DUMP=1 + shift + ;; + *) + parse_sysrq_value "$2" + shift 2 + ;; + esac + else + ENABLE_SYSRQ_HANG_DUMP=1 + shift + fi ;; --disable-sysrq-hang-dump) ENABLE_SYSRQ_HANG_DUMP=0 @@ -174,6 +209,10 @@ while [ $# -gt 0 ]; do esac done +if [ -z "$PROFILE_LIST_FILE" ]; then + PROFILE_LIST_FILE="$PROFILE_LIST_DEFAULT" +fi + if [ "$VERBOSE" -eq 1 ] 2>/dev/null; then set -x fi @@ -190,6 +229,10 @@ esac case "$TIMEOUT_SETTLE" in ''|*[!0-9]*) log_error "Invalid --timeout-settle: $TIMEOUT_SETTLE"; exit 1 ;; esac +case "$ENABLE_SYSRQ_HANG_DUMP" in + 0|1) ;; + *) log_error "Invalid sysrq hang dump setting: $ENABLE_SYSRQ_HANG_DUMP"; exit 1 ;; +esac test_path="$(find_test_case_by_name "$TESTNAME" 2>/dev/null || echo "$SCRIPT_DIR")" if ! cd "$test_path"; then @@ -211,7 +254,7 @@ else log_info "Platform Details: unknown" fi -log_info "Args: module='${TARGET_MODULE:-all-enabled}' iterations=$ITERATIONS unload_timeout=$TIMEOUT_UNLOAD load_timeout=$TIMEOUT_LOAD settle_timeout=$TIMEOUT_SETTLE mode='${PROFILE_MODE:-profile-default}' sysrq_dump=$ENABLE_SYSRQ_HANG_DUMP" +log_info "Args: module='${TARGET_MODULE:-all-enabled}' profile_list='${PROFILE_LIST_FILE}' iterations=$ITERATIONS unload_timeout=$TIMEOUT_UNLOAD load_timeout=$TIMEOUT_LOAD settle_timeout=$TIMEOUT_SETTLE mode='${PROFILE_MODE:-profile-default}' sysrq_dump=$ENABLE_SYSRQ_HANG_DUMP" if [ "$ENABLE_SYSRQ_HANG_DUMP" -eq 1 ] 2>/dev/null; then log_info "Sysrq hang dump policy: enabled on timeout paths only" diff --git a/Runner/utils/lib_module_reload.sh b/Runner/utils/lib_module_reload.sh index a28380d1e..923f31a71 100755 --- a/Runner/utils/lib_module_reload.sh +++ b/Runner/utils/lib_module_reload.sh @@ -1,12 +1,13 @@ #!/bin/sh # Copyright (c) Qualcomm Technologies, Inc. and/or its subsidiaries. -# SPDX-License-Identifier: BSD-3-Clause-Clear +# SPDX-License-Identifier: BSD-3-Clause MRV_LAST_CMD_PID="" MRV_LAST_CMD_ELAPSED="0" MRV_LAST_TIMEOUT_DIR="" MRV_PROFILE_REASON="" +# Reset all profile-provided variables before sourcing a profile. mrv_reset_profile_vars() { PROFILE_NAME="" PROFILE_DESCRIPTION="" @@ -21,17 +22,190 @@ mrv_reset_profile_vars() { PROFILE_DEVICE_PATTERNS="" PROFILE_SYSFS_PATTERNS="" PROFILE_SELECTED_MODE="" + PROFILE_EXPECT_ABSENT_AFTER_UNLOAD="" + PROFILE_EXPECT_PRESENT_AFTER_LOAD="" + PROFILE_SKIP_IF_MODULES_LOADED="" + PROFILE_TOP_MODULE_CANDIDATES="" + PROFILE_UNLOAD_STACK="" + PROFILE_EXTRA_UNLOAD_MODULES="" + PROFILE_QUIESCE_SERVICES="" + PROFILE_QUIESCE_PROC_PATTERNS="" + PROFILE_QUIESCE_DEVICE_PATTERNS="" + PROFILE_QUIESCE_ONCE="no" + MRV_PROFILE_QUIESCED="0" } +# Convert module names to the form used by lsmod and /proc/modules. mrv_module_lsmod_name() { printf '%s\n' "$1" | tr '-' '_' } +# Return success when a module is either loaded or available through modinfo. +mrv_module_available() { + mod="$1" + + if mrv_module_loaded "$mod"; then + return 0 + fi + + if modinfo "$mod" >/dev/null 2>&1; then + return 0 + fi + + return 1 +} + +# Return the first sysfs holder for a module, if any. +mrv_module_first_holder_sysfs() { + mod="$1" + + for holder_path in /sys/module/"$mod"/holders/*; do + [ -e "$holder_path" ] || continue + holder="${holder_path##*/}" + [ -n "$holder" ] || continue + printf '%s\n' "$holder" + return 0 + done + + return 1 +} + +# Return the first /proc/modules holder for a module, if any. +mrv_module_first_holder_proc() { + mod="$1" + + [ -r /proc/modules ] || return 1 + + awk -v target="$mod" ' + $1 == target && $4 != "-" { + n = split($4, holders, ",") + for (i = 1; i <= n; i++) { + if (holders[i] != "") { + print holders[i] + exit + } + } + } + ' /proc/modules 2>/dev/null +} + +# Select the reload target for a stack: holder first, candidates second, primary last. +mrv_select_reload_top_module() { + primary="$1" + candidates="$2" + top_module="" + + top_module="$(mrv_module_first_holder_sysfs "$primary" 2>/dev/null || true)" + if [ -n "$top_module" ]; then + printf '%s\n' "$top_module" + return 0 + fi + + top_module="$(mrv_module_first_holder_proc "$primary" 2>/dev/null || true)" + if [ -n "$top_module" ]; then + printf '%s\n' "$top_module" + return 0 + fi + + for candidate in $candidates; do + [ -n "$candidate" ] || continue + if mrv_module_available "$candidate"; then + printf '%s\n' "$candidate" + return 0 + fi + done + + if mrv_module_available "$primary"; then + printf '%s\n' "$primary" + return 0 + fi + + return 1 +} + +# Configure generic stack reload commands from profile data. +# +# Profiles can define: +# MODULE_NAME primary module +# PROFILE_TOP_MODULE_CANDIDATES optional holder/top-module candidates +# PROFILE_UNLOAD_STACK explicit unload order +# PROFILE_EXTRA_UNLOAD_MODULES extra modules to unload after stack +# +# This helper intentionally avoids ${var:-command-with-${m}} expansion because +# nested braces inside command strings can break POSIX shell parsing. +module_reload_profile_setup_stack() { + primary="${MODULE_NAME:-}" + top_module="" + unload_stack="" + + if [ -z "$primary" ]; then + MODULE_RELOAD_SUPPORTED="no" + return 0 + fi + + top_module="$(mrv_select_reload_top_module "$primary" "$PROFILE_TOP_MODULE_CANDIDATES" 2>/dev/null || true)" + if [ -z "$top_module" ]; then + MODULE_RELOAD_SUPPORTED="no" + return 0 + fi + + MODULE_NAME="$top_module" + MODULE_RELOAD_SUPPORTED="${MODULE_RELOAD_SUPPORTED:-yes}" + + if [ -n "$PROFILE_UNLOAD_STACK" ]; then + unload_stack="$PROFILE_UNLOAD_STACK" + elif [ "$top_module" != "$primary" ]; then + unload_stack="$top_module $primary" + else + unload_stack="$primary" + fi + + if [ -n "$PROFILE_EXTRA_UNLOAD_MODULES" ]; then + unload_stack="$unload_stack $PROFILE_EXTRA_UNLOAD_MODULES" + fi + + if [ -z "$MODULE_UNLOAD_CMD" ]; then + MODULE_UNLOAD_CMD="modprobe -r $unload_stack" + fi + + if [ -z "$MODULE_LOAD_CMD" ]; then + MODULE_LOAD_CMD="modprobe $top_module" + fi + + if [ -z "$PROFILE_EXPECT_ABSENT_AFTER_UNLOAD" ]; then + PROFILE_EXPECT_ABSENT_AFTER_UNLOAD="$top_module" + fi + + if [ -z "$PROFILE_EXPECT_PRESENT_AFTER_LOAD" ]; then + PROFILE_EXPECT_PRESENT_AFTER_LOAD="$top_module" + fi + + return 0 +} + +# Return success when the module is present in lsmod. mrv_module_loaded() { name="$(mrv_module_lsmod_name "$1")" is_module_loaded "$name" } +# Return success when another mutually exclusive module variant is already active. +mrv_should_skip_for_loaded_conflict_modules() { + [ -n "$PROFILE_SKIP_IF_MODULES_LOADED" ] || return 1 + + for conflict_mod in $PROFILE_SKIP_IF_MODULES_LOADED; do + [ -n "$conflict_mod" ] || continue + + if mrv_module_loaded "$conflict_mod"; then + MRV_PROFILE_REASON="conflicting active module is loaded: $conflict_mod" + return 0 + fi + done + + return 1 +} + +# Return success when a module is built into the kernel and cannot be unloaded. mrv_module_builtin() { if [ -f "/lib/modules/$(uname -r)/modules.builtin" ]; then mod_pat="$(printf '%s\n' "$1" | sed 's/_/[-_]/g')" @@ -41,6 +215,7 @@ mrv_module_builtin() { return 1 } +# Wait until a module reaches the requested state: present or absent. mrv_wait_module_state() { want="$1" name="$2" @@ -72,6 +247,7 @@ mrv_wait_module_state() { return 0 } +# Capture existence and metadata for profile-provided path patterns. mrv_capture_patterns() { patterns="$1" outfile="$2" @@ -82,10 +258,12 @@ mrv_capture_patterns() { return 0 fi + # Intentional word splitting: patterns are space-separated profile data. # shellcheck disable=SC2086 set -- $patterns for pat in "$@"; do found=0 + # Intentional glob expansion from profile data. # shellcheck disable=SC2086 for path in $pat; do if [ -e "$path" ] || [ -L "$path" ]; then @@ -100,6 +278,7 @@ mrv_capture_patterns() { done } +# Capture status and recent logs for profile services. mrv_capture_services() { services="$1" outfile="$2" @@ -116,6 +295,7 @@ mrv_capture_services() { return 0 fi + # Intentional word splitting: services are space-separated profile data. # shellcheck disable=SC2086 set -- $services for svc in "$@"; do @@ -128,6 +308,7 @@ mrv_capture_services() { done } +# Capture module, process, service, sysfs, and device state for evidence. mrv_capture_module_state() { outdir="$1" mkdir -p "$outdir" @@ -169,7 +350,7 @@ mrv_capture_module_state() { printf '/sys/module/%s not present\n' "$sys_mod" > "$outdir/holders.log" fi - mrv_capture_services "$PROFILE_SERVICES" "$outdir/services.log" + mrv_capture_services "$PROFILE_SERVICES $PROFILE_QUIESCE_SERVICES" "$outdir/services.log" mrv_capture_patterns "$PROFILE_DEVICE_PATTERNS" "$outdir/devices.log" mrv_capture_patterns "$PROFILE_SYSFS_PATTERNS" "$outdir/profile_sysfs.log" @@ -178,6 +359,7 @@ mrv_capture_module_state() { fi } +# Capture /proc evidence for a command that timed out. mrv_capture_pid_timeout_snapshot() { pid="$1" outdir="$2" @@ -192,6 +374,7 @@ mrv_capture_pid_timeout_snapshot() { fi } +# Execute a shell command with timeout and command evidence logging. mrv_exec_with_timeout() { timeout_s="$1" logfile="$2" @@ -243,6 +426,7 @@ mrv_exec_with_timeout() { return "$rc" } +# Capture hang evidence after unload/load timeout. mrv_capture_hang_evidence() { phase="$1" outdir="$2" @@ -261,9 +445,11 @@ mrv_capture_hang_evidence() { if command -v fuser >/dev/null 2>&1; then : > "$outdir/fuser.log" + # Intentional word splitting: device patterns are profile data. # shellcheck disable=SC2086 set -- $PROFILE_DEVICE_PATTERNS for pat in "$@"; do + # Intentional glob expansion from profile data. # shellcheck disable=SC2086 for path in $pat; do if [ -e "$path" ] || [ -L "$path" ]; then @@ -285,11 +471,13 @@ mrv_capture_hang_evidence() { } > "$outdir/timeout_summary.log" } +# Return success when a profile hook function exists. mrv_profile_override_defined() { fn_name="$1" command -v "$fn_name" >/dev/null 2>&1 } +# Return success when a systemd service is known on the target. module_reload_service_known() { svc="$1" @@ -297,9 +485,10 @@ module_reload_service_known() { return 1 fi - systemctl list-unit-files "$svc" >/dev/null 2>&1 + systemctl show "$svc" >/dev/null 2>&1 } +# Read one systemd service state property. module_reload_service_state_value() { svc="$1" prop="$2" @@ -312,6 +501,86 @@ module_reload_service_state_value() { fi } +# Return the file used to track services stopped/masked by module reload tests. +# +# The file is under RESULT_ROOT so it is shared between the profile subshell and +# the top-level run.sh cleanup trap. +module_reload_restore_services_file() { + printf '%s\n' "${MRV_RESTORE_SERVICES_FILE:-${RESULT_ROOT:-/tmp}/.module_reload_services_to_restore}" +} + +# Record services before stopping/masking them. +# +# We store the previous ActiveState so final restore can avoid starting services +# that were already inactive before this test touched them. +module_reload_record_restore_services() { + services="$1" + restore_file="$(module_reload_restore_services_file)" + restore_dir="$(dirname "$restore_file")" + + [ -n "$services" ] || return 0 + + mkdir -p "$restore_dir" 2>/dev/null || true + + for svc in $services; do + [ -n "$svc" ] || continue + + if module_reload_service_known "$svc"; then + svc_state="$(module_reload_service_state_value "$svc" "ActiveState")" + + if [ ! -f "$restore_file" ] || ! grep -q "^${svc} " "$restore_file" 2>/dev/null; then + printf '%s %s\n' "$svc" "$svc_state" >> "$restore_file" + fi + fi + done + + return 0 +} + +# Restore every service previously stopped/masked by the module reload test. +# +# This is best-effort and should not fail the test. It unblocks the target after +# failed unloads, post-unload validation failures, timeouts, or interrupted runs. +# +# Services that were active before quiesce are started again. Services that were +# inactive before quiesce are only unmasked/reset, not force-started. +module_reload_restore_recorded_services() { + log_tag="${1:-module-reload}" + restore_file="$(module_reload_restore_services_file)" + + [ -f "$restore_file" ] || return 0 + + if ! command -v systemctl >/dev/null 2>&1; then + rm -f "$restore_file" 2>/dev/null || true + return 0 + fi + + while IFS=' ' read -r svc old_state rest || [ -n "$svc" ]; do + : "${rest:=}" + [ -n "$svc" ] || continue + + if module_reload_service_known "$svc"; then + log_info "[$log_tag] restore: unmasking/resetting $svc" + module_reload_run_cmd_quiet_timeout 5 systemctl unmask "$svc" || true + module_reload_run_cmd_quiet_timeout 5 systemctl reset-failed "$svc" || true + + case "$old_state" in + active|activating|reloading) + log_info "[$log_tag] restore: starting previously active service $svc" + module_reload_run_cmd_quiet_timeout 15 systemctl start "$svc" || true + ;; + *) + log_info "[$log_tag] restore: not starting $svc because previous state was ${old_state:-unknown}" + ;; + esac + fi + done < "$restore_file" + + rm -f "$restore_file" 2>/dev/null || true + return 0 +} + +# List process IDs whose command line contains a pattern. module_reload_list_pids_by_pattern() { proc_pat="$1" @@ -334,6 +603,7 @@ module_reload_list_pids_by_pattern() { done } +# Return success when any process matching a pattern is running. module_reload_proc_running_pattern() { proc_pat="$1" pids="$(module_reload_list_pids_by_pattern "$proc_pat")" @@ -344,6 +614,7 @@ module_reload_proc_running_pattern() { return 1 } +# Signal all processes whose command line contains a pattern. module_reload_signal_pattern() { proc_pat="$1" sig="$2" @@ -360,6 +631,7 @@ module_reload_signal_pattern() { return 0 } +# Wait until no processes match any pattern. module_reload_wait_no_procs_patterns() { proc_patterns="$1" timeout_s="${2:-10}" @@ -392,6 +664,7 @@ module_reload_wait_no_procs_patterns() { return 0 } +# Log current service states and matching process command lines. module_reload_log_service_process_summary() { services="$1" proc_patterns="$2" @@ -430,6 +703,7 @@ module_reload_log_service_process_summary() { done } +# Wait until all known services are active. module_reload_wait_services_active() { services="$1" timeout_s="${2:-15}" @@ -466,6 +740,7 @@ module_reload_wait_services_active() { return 0 } +# Wait until all known services are inactive. module_reload_wait_services_inactive() { services="$1" timeout_s="${2:-15}" @@ -502,54 +777,100 @@ module_reload_wait_services_inactive() { return 0 } +module_reload_run_cmd_quiet_timeout() { + timeout_s="$1" + shift + elapsed=0 + + if [ "$#" -eq 0 ]; then + return 1 + fi + + "$@" >/dev/null 2>&1 & + cmd_pid=$! + + while kill -0 "$cmd_pid" 2>/dev/null; do + if [ "$elapsed" -ge "$timeout_s" ] 2>/dev/null; then + kill -TERM "$cmd_pid" 2>/dev/null || true + sleep 1 + + if kill -0 "$cmd_pid" 2>/dev/null; then + kill -KILL "$cmd_pid" 2>/dev/null || true + sleep 1 + fi + + wait "$cmd_pid" 2>/dev/null || true + return 124 + fi + + sleep 1 + elapsed=$((elapsed + 1)) + done + + wait "$cmd_pid" + return $? +} + +# Start and unmask known services, then wait until active. module_reload_start_services() { services="$1" timeout_s="${2:-15}" log_tag="${3:-module-reload}" - + if ! command -v systemctl >/dev/null 2>&1; then return 0 fi - + for svc in $services; do if module_reload_service_known "$svc"; then - log_info "[$log_tag] start: unmasking and starting $svc" - systemctl unmask "$svc" >/dev/null 2>&1 || true - systemctl reset-failed "$svc" >/dev/null 2>&1 || true - systemctl start "$svc" >/dev/null 2>&1 || true + log_info "[$log_tag] start: unmasking $svc" + module_reload_run_cmd_quiet_timeout 5 systemctl unmask "$svc" || true + + log_info "[$log_tag] start: resetting $svc" + module_reload_run_cmd_quiet_timeout 5 systemctl reset-failed "$svc" || true + + log_info "[$log_tag] start: starting $svc" + module_reload_run_cmd_quiet_timeout "$timeout_s" systemctl start "$svc" || true fi done - + module_reload_wait_services_active "$services" "$timeout_s" } +# Stop, mask, and kill known services and matching processes. module_reload_stop_mask_kill_services() { services="$1" proc_patterns="$2" timeout_s="${3:-20}" log_tag="${4:-module-reload}" - + if ! command -v systemctl >/dev/null 2>&1; then return 0 fi - + + module_reload_record_restore_services "$services" + for svc in $services; do if module_reload_service_known "$svc"; then log_info "[$log_tag] quiesce: stopping $svc" - systemctl stop "$svc" >/dev/null 2>&1 || true + if ! module_reload_run_cmd_quiet_timeout "$timeout_s" systemctl stop "$svc"; then + log_warn "[$log_tag] quiesce: stop timed out or failed for $svc" + fi + log_info "[$log_tag] quiesce: masking $svc" - systemctl mask "$svc" >/dev/null 2>&1 || true + module_reload_run_cmd_quiet_timeout 5 systemctl mask "$svc" || true + log_info "[$log_tag] quiesce: killing remaining $svc cgroup processes" - systemctl kill --kill-who=all "$svc" >/dev/null 2>&1 || true + module_reload_run_cmd_quiet_timeout 5 systemctl kill --kill-who=all "$svc" || true fi done - + for proc_pat in $proc_patterns; do module_reload_signal_pattern "$proc_pat" TERM "$log_tag" done - + sleep 2 - + if ! module_reload_wait_no_procs_patterns "$proc_patterns" 5; then log_warn "[$log_tag] quiesce: TERM was not enough, sending SIGKILL to remaining processes" for proc_pat in $proc_patterns; do @@ -557,18 +878,21 @@ module_reload_stop_mask_kill_services() { done sleep 1 fi - + if ! module_reload_wait_services_inactive "$services" "$timeout_s"; then + log_warn "[$log_tag] quiesce: one or more services still not inactive after timeout" return 1 fi - + if ! module_reload_wait_no_procs_patterns "$proc_patterns" 2; then + log_warn "[$log_tag] quiesce: one or more process patterns still present after timeout" return 1 fi - + return 0 } +# Restore known services after a profile finishes. module_reload_restore_services() { services="$1" timeout_s="${2:-15}" @@ -590,6 +914,7 @@ module_reload_restore_services() { module_reload_wait_services_active "$services" "$timeout_s" } +# Dump status for a list of services to a file. module_reload_dump_service_status() { services="$1" svc_log="$2" @@ -611,6 +936,160 @@ module_reload_dump_service_status() { return 0 } +# Scan /proc/*/fd for open descriptors matching path patterns. +module_reload_scan_open_fds_by_path_patterns() { + path_patterns="$1" + out_file="$2" + + : > "$out_file" + + [ -n "$path_patterns" ] || return 1 + + for pid_dir in /proc/[0-9]*; do + [ -d "$pid_dir/fd" ] || continue + pid="${pid_dir##*/}" + + for fd in "$pid_dir"/fd/*; do + [ -e "$fd" ] || continue + + target="$(readlink "$fd" 2>/dev/null || true)" + [ -n "$target" ] || continue + for pat in $path_patterns; do + # Intentional glob pattern match from profile data. + # shellcheck disable=SC2254 + case "$target" in + $pat) + cmd="$(tr '\0' ' ' < "$pid_dir/cmdline" 2>/dev/null || true)" + [ -n "$cmd" ] || cmd="$(cat "$pid_dir/comm" 2>/dev/null || true)" + printf 'pid=%s fd=%s target=%s cmd=%s\n' "$pid" "${fd##*/}" "$target" "$cmd" >> "$out_file" + ;; + esac + done + done + done + + [ -s "$out_file" ] +} + +# Terminate processes that keep matching device paths open. +module_reload_kill_open_fd_users_by_path_patterns() { + path_patterns="$1" + timeout_s="${2:-10}" + log_tag="${3:-module-reload}" + out_file="${4:-/tmp/module_reload_open_fds.log}" + + if ! module_reload_scan_open_fds_by_path_patterns "$path_patterns" "$out_file"; then + return 0 + fi + + log_warn "[$log_tag] open users found for profiled device paths: $out_file" + + awk ' + { + for (i = 1; i <= NF; i++) { + if ($i ~ /^pid=/) { + sub(/^pid=/, "", $i) + print $i + } + } + } + ' "$out_file" | sort -u | while IFS= read -r pid; do + [ -n "$pid" ] || continue + [ "$pid" = "$$" ] && continue + log_warn "[$log_tag] sending SIGTERM to open-fd user pid=$pid" + kill -TERM "$pid" 2>/dev/null || true + done + + elapsed=0 + while [ "$elapsed" -lt "$timeout_s" ] 2>/dev/null; do + if ! module_reload_scan_open_fds_by_path_patterns "$path_patterns" "$out_file"; then + return 0 + fi + + sleep 1 + elapsed=$((elapsed + 1)) + done + + awk ' + { + for (i = 1; i <= NF; i++) { + if ($i ~ /^pid=/) { + sub(/^pid=/, "", $i) + print $i + } + } + } + ' "$out_file" | sort -u | while IFS= read -r pid; do + [ -n "$pid" ] || continue + [ "$pid" = "$$" ] && continue + log_warn "[$log_tag] sending SIGKILL to remaining open-fd user pid=$pid" + kill -KILL "$pid" 2>/dev/null || true + done + + sleep 1 + + if module_reload_scan_open_fds_by_path_patterns "$path_patterns" "$out_file"; then + log_warn "[$log_tag] open users still present after SIGKILL: $out_file" + return 1 + fi + + return 0 +} + +# Generic quiesce helper for services, process patterns, and open device users. +module_reload_profile_quiesce_resources() { + log_tag="${1:-$PROFILE_NAME}" + iter_dir="${2:-}" + timeout_s="${3:-20}" + + services="${PROFILE_QUIESCE_SERVICES:-$PROFILE_SERVICES}" + procs="${PROFILE_QUIESCE_PROC_PATTERNS:-$PROFILE_PROC_PATTERNS}" + devs="${PROFILE_QUIESCE_DEVICE_PATTERNS:-$PROFILE_DEVICE_PATTERNS}" + + module_reload_log_service_process_summary "$services" "$procs" "$log_tag" + + if [ -n "$services" ] || [ -n "$procs" ]; then + if ! module_reload_stop_mask_kill_services "$services" "$procs" "$timeout_s" "$log_tag"; then + log_warn "[$log_tag] service/process quiesce did not fully complete" + module_reload_log_service_process_summary "$services" "$procs" "$log_tag" + fi + fi + + if [ -n "$devs" ]; then + if [ -n "$iter_dir" ]; then + open_fd_log="$iter_dir/open_fds_before_unload.log" + else + open_fd_log="/tmp/module_reload_open_fds_before_unload.log" + fi + + if ! module_reload_kill_open_fd_users_by_path_patterns "$devs" 10 "$log_tag" "$open_fd_log"; then + log_warn "[$log_tag] open device users remain after quiesce; skipping unsafe unload" + return 1 + fi + fi + + sleep 2 + return 0 +} + +# Generic smoke helper that validates expected modules are loaded. +module_reload_profile_smoke_modules_present() { + log_tag="${1:-$PROFILE_NAME}" + modules="$2" + + [ -n "$modules" ] || modules="$MODULE_NAME" + + for mod in $modules; do + if ! mrv_module_loaded "$mod"; then + log_fail "[$log_tag] expected module not loaded after reload: $mod" + return 1 + fi + done + + return 0 +} + +# Dispatch optional profile prepare hook. module_reload_profile_prepare() { profile_root="$1" : "${profile_root:=}" @@ -623,6 +1102,7 @@ module_reload_profile_prepare() { return 0 } +# Dispatch optional warmup hook or generic daemon warmup. module_reload_profile_warmup() { iter_dir="$1" : "${iter_dir:=}" @@ -655,38 +1135,86 @@ module_reload_profile_warmup() { return 0 } +# Best-effort restore for services stopped/masked only for quiesce. +# +# Unlike module_reload_restore_services(), this does not require services to +# become active. This is needed for oneshot or optional services such as +# pulseaudio.service, alsa-restore.service, and distro-specific units. +module_reload_restore_quiesce_services_best_effort() { + services="$1" + log_tag="${2:-module-reload}" + + [ -n "$services" ] || return 0 + + if ! command -v systemctl >/dev/null 2>&1; then + return 0 + fi + + for svc in $services; do + if module_reload_service_known "$svc"; then + log_info "[$log_tag] restore: unmasking/resetting $svc" + systemctl unmask "$svc" >/dev/null 2>&1 || true + systemctl reset-failed "$svc" >/dev/null 2>&1 || true + log_info "[$log_tag] restore: starting $svc" + systemctl start "$svc" >/dev/null 2>&1 || true + fi + done + + return 0 +} + +# Dispatch optional quiesce hook or generic daemon quiesce. module_reload_profile_quiesce() { iter_dir="$1" : "${iter_dir:=}" - + if mrv_profile_override_defined profile_quiesce; then profile_quiesce "$iter_dir" return $? fi - - if ! command -v systemctl >/dev/null 2>&1; then + + quiesce_services="${PROFILE_QUIESCE_SERVICES:-$PROFILE_SERVICES}" + quiesce_proc_patterns="${PROFILE_QUIESCE_PROC_PATTERNS:-$PROFILE_PROC_PATTERNS}" + + if [ -z "$quiesce_services" ] && [ -z "$quiesce_proc_patterns" ]; then return 0 fi - + + if [ "${PROFILE_QUIESCE_ONCE:-no}" = "yes" ] && [ "${MRV_PROFILE_QUIESCED:-0}" = "1" ]; then + log_info "[$PROFILE_NAME] quiesce: already completed once for this profile, skipping repeated quiesce" + return 0 + fi + + module_reload_log_service_process_summary \ + "$quiesce_services" \ + "$quiesce_proc_patterns" \ + "$PROFILE_NAME" + case "$PROFILE_SELECTED_MODE" in - basic) - return 0 - ;; - daemon_lifecycle|service_rebind) - if ! module_reload_stop_mask_kill_services "$PROFILE_SERVICES" "$PROFILE_PROC_PATTERNS" 20 "$PROFILE_NAME"; then - module_reload_log_service_process_summary "$PROFILE_SERVICES" "$PROFILE_PROC_PATTERNS" "$PROFILE_NAME" + basic|daemon_lifecycle|service_rebind) + if ! module_reload_stop_mask_kill_services \ + "$quiesce_services" \ + "$quiesce_proc_patterns" \ + 20 \ + "$PROFILE_NAME"; then + module_reload_log_service_process_summary \ + "$quiesce_services" \ + "$quiesce_proc_patterns" \ + "$PROFILE_NAME" log_error "[$PROFILE_NAME] quiesce: services/processes did not fully stop" return 1 fi + MRV_PROFILE_QUIESCED="1" ;; *) log_warn "[$PROFILE_NAME] unknown mode '$PROFILE_SELECTED_MODE', continuing without quiesce actions" ;; esac - + return 0 } +# Dispatch optional post-unload hook. module_reload_profile_post_unload() { iter_dir="$1" : "${iter_dir:=}" @@ -699,41 +1227,54 @@ module_reload_profile_post_unload() { return 0 } +# Dispatch optional post-load hook or generic daemon restart. module_reload_profile_post_load() { iter_dir="$1" - svc_log="$iter_dir/post_load_services.log" : "${iter_dir:=}" - + svc_log="$iter_dir/post_load_services.log" + if mrv_profile_override_defined profile_post_load; then profile_post_load "$iter_dir" return $? fi - - if ! command -v systemctl >/dev/null 2>&1; then - return 0 - fi - + case "$PROFILE_SELECTED_MODE" in basic) + # Do not restore quiesced services after every basic-mode iteration. + # + # Restoring here causes repeated start/stop cycles for services such as + # pipewire, wireplumber, NetworkManager, and wpa_supplicant. For audio, + # repeated pipewire-pulse stop/start can stall the test. + # + # Services are restored once in module_reload_profile_finalize() or from + # the run.sh cleanup trap if the test exits early. + module_reload_dump_service_status "$PROFILE_QUIESCE_SERVICES" "$svc_log" return 0 ;; daemon_lifecycle|service_rebind) + if ! command -v systemctl >/dev/null 2>&1; then + return 0 + fi + if ! module_reload_start_services "$PROFILE_SERVICES" 15 "$PROFILE_NAME"; then module_reload_log_service_process_summary "$PROFILE_SERVICES" "$PROFILE_PROC_PATTERNS" "$PROFILE_NAME" log_error "[$PROFILE_NAME] post-load: services failed to become active after reload" + module_reload_restore_recorded_services "$PROFILE_NAME" return 1 fi - + module_reload_dump_service_status "$PROFILE_SERVICES" "$svc_log" + module_reload_restore_recorded_services "$PROFILE_NAME" ;; *) log_warn "[$PROFILE_NAME] unknown mode '$PROFILE_SELECTED_MODE', continuing without post-load actions" ;; esac - + return 0 } +# Dispatch optional smoke hook. module_reload_profile_smoke() { iter_dir="$1" : "${iter_dir:=}" @@ -746,27 +1287,33 @@ module_reload_profile_smoke() { return 0 } +# Dispatch optional finalize hook or restore services that were managed. module_reload_profile_finalize() { profile_root="$1" : "${profile_root:=}" - + if mrv_profile_override_defined profile_finalize; then profile_finalize "$profile_root" - return $? + profile_finalize_rc=$? + module_reload_restore_recorded_services "$PROFILE_NAME" + return "$profile_finalize_rc" fi - + + module_reload_restore_recorded_services "$PROFILE_NAME" + if ! command -v systemctl >/dev/null 2>&1; then return 0 fi - + if ! module_reload_restore_services "$PROFILE_SERVICES" 15 "$PROFILE_NAME"; then log_error "[$PROFILE_NAME] finalize: failed to restore services" return 1 fi - + return 0 } +# Validate the profile after it is sourced and before iterations run. mrv_profile_check() { requested_mode="$1" @@ -775,6 +1322,10 @@ mrv_profile_check() { return 2 fi + if mrv_should_skip_for_loaded_conflict_modules; then + return 2 + fi + if [ "${MODULE_RELOAD_SUPPORTED:-yes}" != "yes" ]; then MRV_PROFILE_REASON="profile marks module as non-reloadable" return 2 @@ -804,6 +1355,7 @@ mrv_profile_check() { required="${PROFILE_REQUIRED_CMDS:-}" if [ -n "$required" ]; then + # Intentional word splitting: required commands are profile data. # shellcheck disable=SC2086 set -- $required for req in "$@"; do @@ -821,22 +1373,68 @@ mrv_profile_check() { return 0 } +# Validate that a list of modules is present or absent. +mrv_validate_module_list_state() { + want="$1" + modules="$2" + + [ -n "$modules" ] || return 0 + + for mod in $modules; do + [ -n "$mod" ] || continue + + case "$want" in + absent) + if mrv_module_loaded "$mod"; then + log_fail "[$PROFILE_NAME] expected module absent after unload, but still loaded: $mod" + return 1 + fi + ;; + present) + if ! mrv_module_loaded "$mod"; then + log_fail "[$PROFILE_NAME] expected module present after load, but missing: $mod" + return 1 + fi + ;; + *) + log_error "[$PROFILE_NAME] invalid module state request: $want" + return 1 + ;; + esac + done + + return 0 +} + +# Validate module state after unload completes. mrv_post_unload_validate() { if ! mrv_wait_module_state absent "$MODULE_NAME" "$TIMEOUT_SETTLE"; then log_fail "[$PROFILE_NAME] module still present after unload settle timeout" return 1 fi + + if ! mrv_validate_module_list_state absent "$PROFILE_EXPECT_ABSENT_AFTER_UNLOAD"; then + return 1 + fi + return 0 } +# Validate module state after load completes. mrv_post_load_validate() { if ! mrv_wait_module_state present "$MODULE_NAME" "$TIMEOUT_SETTLE"; then log_fail "[$PROFILE_NAME] module did not reappear after load settle timeout" return 1 fi + + if ! mrv_validate_module_list_state present "$PROFILE_EXPECT_PRESENT_AFTER_LOAD"; then + return 1 + fi + return 0 } +# Execute one unload/reload iteration for the active profile. mrv_run_iteration() { iter="$1" iter_dir="$RESULT_ROOT/$PROFILE_NAME/iter_$(printf '%02d' "$iter")" @@ -967,6 +1565,7 @@ mrv_run_iteration() { return 0 } +# Run one profile in a subshell so profile variables and hooks do not leak. mrv_run_one_profile() ( profile_file="$1" requested_mode="$2" @@ -1021,6 +1620,7 @@ mrv_run_one_profile() ( exit 0 ) +# Resolve explicit module, profile list, enabled list, or all profile files. mrv_resolve_profiles() { target_module="$1" profile_dir="$2" @@ -1059,3 +1659,4 @@ mrv_resolve_profiles() { return 0 } +