From 3c86dbad7129ce6cc0bff618f920fca8377628c9 Mon Sep 17 00:00:00 2001 From: Alper Nebi Yasak Date: Mon, 13 Nov 2023 12:32:33 +0300 Subject: [PATCH] hook-functions: sys_walk_mod_add: Add devlink suppliers as dependencies With MODULES=dep, we walk through sysfs to add device-specific modules, and their dependencies based on `modprobe --show-depends`. However in embedded systems, a lot dependency relations ended up being expressed only in device-tree instead of in the usual ways. This dependency information is exposed in sysfs as a "devlink" [1], where each involved driver is a "consumer" or "supplier" [2] of the link. Not including suppliers for a driver in initramfs can make it defer probing indefinitely, leading to breakage. This led to dracut including them as well [3]. While walking sysfs for modules we want to add, also walk their suppliers and add those as well. The supplier dependencies can have cycles in them, so we can't recursively call sys_walk_mod_add. Instead, do an iterative traversal of the dependency graph to collect all device paths we need to look into, then actually add the modules for those. [1] https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git/tree/Documentation/ABI/testing/sysfs-class-devlink?h=v6.6 [2] https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git/tree/Documentation/ABI/testing/sysfs-devices-supplier?h=v6.6 [3] https://github.com/dracutdevs/dracut/pull/2075 Signed-off-by: Alper Nebi Yasak --- hook-functions | 43 ++++++++++++++++++++++++++++++++++++++----- 1 file changed, 38 insertions(+), 5 deletions(-) diff --git a/hook-functions b/hook-functions index 9c495e9..7797377 100644 --- a/hook-functions +++ b/hook-functions @@ -316,10 +316,45 @@ copy_modules_dir() # walk /sys for relevant modules sys_walk_mod_add() { - local driver_path module device_path modalias - device_path="$1" + local driver_path module device_path modalias devlink - while [ "${device_path}" != "/sys" ]; do + # This is a crude way of storing two stacks in "$@" by using + # "--" as a separator between them, to implement iterative + # graph traversal over parent/supplier dependencies. The first + # part is device paths to traverse, the other is visited ones. + set -- "$1" "--" + while [ "$1" != "--" ]; do + device_path="$(readlink -f "$1")" + shift + + # Don't walk into same path twice, avoid infinite recursion + case "$*" in + "${device_path}") continue ;; + "${device_path} "*) continue ;; + *" ${device_path} "*) continue ;; + *" ${device_path}") continue ;; + esac + + if [ "$device_path" = "/sys" ]; then + continue + fi + + # Keep current path to add module/modalias for + set -- "$@" "$device_path" + + # Walk into suppliers in next iteration + for devlink in "${device_path}/supplier:"*; do + if [ -d "$devlink" ]; then + set -- "${devlink}/supplier/" "$@" + fi + done + + # Walk into parent in next iteration + set -- "$(dirname "$device_path")" "$@" + done + shift + + for device_path in "$@"; do # device modalias if [ -e "${device_path}/modalias" ]; then modalias=$(cat "${device_path}/modalias") @@ -336,8 +371,6 @@ sys_walk_mod_add() manual_add_modules "${module}" fi fi - - device_path="$(dirname "${device_path}")" done }