From: Adrien Thierry Date: Mon, 5 Dec 2022 21:28:17 +0000 (-0500) Subject: feat(dracut-install): add fw_devlink suppliers as module dependencies X-Git-Url: http://git.ipfire.org/cgi-bin/gitweb.cgi?a=commitdiff_plain;h=3de4c7313260fb600507c9b87f780390b874c870;p=thirdparty%2Fdracut.git feat(dracut-install): add fw_devlink suppliers as module dependencies Dracut currently finds kernel module dependencies using two methods: depmod and softdeps. However, these are often insufficient on embedded systems where a lot of driver dependencies are only described in the device tree. This is often the case for low-level devices such as clocks, regulators, pinctrls, etc. This patch allows dracut to find those device tree dependencies, by parsing the sysfs supplier nodes populated by fw_devlink. Signed-off-by: Adrien Thierry --- diff --git a/src/install/dracut-install.c b/src/install/dracut-install.c index 81fd37f5e..5af353455 100644 --- a/src/install/dracut-install.c +++ b/src/install/dracut-install.c @@ -82,6 +82,7 @@ FILE *logfile_f = NULL; static Hashmap *items = NULL; static Hashmap *items_failed = NULL; static Hashmap *modules_loaded = NULL; +static Hashmap *modules_suppliers = NULL; static regex_t mod_filter_path; static regex_t mod_filter_nopath; static regex_t mod_filter_symbol; @@ -94,7 +95,7 @@ static bool arg_mod_filter_nosymbol = false; static bool arg_mod_filter_noname = false; static int dracut_install(const char *src, const char *dst, bool isdir, bool resolvedeps, bool hashdst); -static int install_dependent_modules(struct kmod_list *modlist); +static int install_dependent_modules(struct kmod_ctx *ctx, struct kmod_list *modlist, Hashmap *suppliers_paths); static void item_free(char *i) { @@ -1499,7 +1500,111 @@ static bool check_module_path(const char *path) return true; } -static int install_dependent_module(struct kmod_module *mod, int *err) +static int find_kmod_module_from_sysfs_node(struct kmod_ctx *ctx, const char *sysfs_node, int sysfs_node_len, + struct kmod_list **modules) +{ + char modalias_path[PATH_MAX]; + if (snprintf(modalias_path, sizeof(modalias_path), "%.*s/modalias", sysfs_node_len, + sysfs_node) >= sizeof(modalias_path)) + return -1; + + _cleanup_close_ int modalias_file = -1; + if ((modalias_file = open(modalias_path, O_RDONLY | O_CLOEXEC)) == -1) + return 0; + + char alias[page_size()]; + ssize_t len = read(modalias_file, alias, sizeof(alias)); + alias[len - 1] = '\0'; + + return kmod_module_new_from_lookup(ctx, alias, modules); +} + +static int find_modules_from_sysfs_node(struct kmod_ctx *ctx, const char *sysfs_node, Hashmap *modules) +{ + _cleanup_kmod_module_unref_list_ struct kmod_list *list = NULL; + struct kmod_list *l = NULL; + + if (find_kmod_module_from_sysfs_node(ctx, sysfs_node, strlen(sysfs_node), &list) >= 0) { + kmod_list_foreach(l, list) { + struct kmod_module *mod = kmod_module_get_module(l); + char *module = strdup(kmod_module_get_name(mod)); + kmod_module_unref(mod); + + if (hashmap_put(modules, module, module) < 0) + free(module); + } + } + + return 0; +} + +static void find_suppliers_for_sys_node(struct kmod_ctx *ctx, Hashmap *suppliers, const char *node_path_raw, + size_t node_path_len) +{ + char node_path[PATH_MAX]; + char real_path[PATH_MAX]; + + memcpy(node_path, node_path_raw, node_path_len); + node_path[node_path_len] = '\0'; + + DIR *d; + struct dirent *dir; + while (realpath(node_path, real_path) != NULL && strcmp(real_path, "/sys/devices")) { + d = opendir(node_path); + if (d) { + size_t real_path_len = strlen(real_path); + while ((dir = readdir(d)) != NULL) { + if (strstr(dir->d_name, "supplier:platform") != NULL) { + if (snprintf(real_path + real_path_len, sizeof(real_path) - real_path_len, "/%s/supplier", + dir->d_name) < sizeof(real_path) - real_path_len) { + char *real_supplier_path = realpath(real_path, NULL); + if (real_supplier_path != NULL) + if (hashmap_put(suppliers, real_supplier_path, real_supplier_path) < 0) + free(real_supplier_path); + } + } + } + closedir(d); + } + strncat(node_path, "/..", 3); // Also find suppliers of parents + } +} + +static void find_suppliers(struct kmod_ctx *ctx) +{ + _cleanup_fts_close_ FTS *fts; + char *paths[] = { "/sys/devices/platform", NULL }; + fts = fts_open(paths, FTS_NOSTAT | FTS_PHYSICAL, NULL); + + for (FTSENT *ftsent = fts_read(fts); ftsent != NULL; ftsent = fts_read(fts)) { + if (strcmp(ftsent->fts_name, "modalias") == 0) { + _cleanup_kmod_module_unref_list_ struct kmod_list *list = NULL; + struct kmod_list *l; + + if (find_kmod_module_from_sysfs_node(ctx, ftsent->fts_parent->fts_path, ftsent->fts_parent->fts_pathlen, &list) < 0) + continue; + + kmod_list_foreach(l, list) { + _cleanup_kmod_module_unref_ struct kmod_module *mod = kmod_module_get_module(l); + const char *name = kmod_module_get_name(mod); + Hashmap *suppliers = hashmap_get(modules_suppliers, name); + if (suppliers == NULL) { + suppliers = hashmap_new(string_hash_func, string_compare_func); + hashmap_put(modules_suppliers, strdup(name), suppliers); + } + + find_suppliers_for_sys_node(ctx, suppliers, ftsent->fts_parent->fts_path, ftsent->fts_parent->fts_pathlen); + } + } + } +} + +static Hashmap *find_suppliers_paths_for_module(const char *module) +{ + return hashmap_get(modules_suppliers, module); +} + +static int install_dependent_module(struct kmod_ctx *ctx, struct kmod_module *mod, Hashmap *suppliers_paths, int *err) { const char *path = NULL; const char *name = NULL; @@ -1530,13 +1635,13 @@ static int install_dependent_module(struct kmod_module *mod, int *err) log_debug("dracut_install '%s' '%s' OK", path, &path[kerneldirlen]); install_firmware(mod); modlist = kmod_module_get_dependencies(mod); - *err = install_dependent_modules(modlist); + *err = install_dependent_modules(ctx, modlist, suppliers_paths); if (*err == 0) { *err = kmod_module_get_softdeps(mod, &modpre, &modpost); if (*err == 0) { int r; - *err = install_dependent_modules(modpre); - r = install_dependent_modules(modpost); + *err = install_dependent_modules(ctx, modpre, NULL); + r = install_dependent_modules(ctx, modpost, NULL); *err = *err ? : r; } } @@ -1547,7 +1652,7 @@ static int install_dependent_module(struct kmod_module *mod, int *err) return 0; } -static int install_dependent_modules(struct kmod_list *modlist) +static int install_dependent_modules(struct kmod_ctx *ctx, struct kmod_list *modlist, Hashmap *suppliers_paths) { struct kmod_list *itr = NULL; int ret = 0; @@ -1555,14 +1660,38 @@ static int install_dependent_modules(struct kmod_list *modlist) kmod_list_foreach(itr, modlist) { _cleanup_kmod_module_unref_ struct kmod_module *mod = NULL; mod = kmod_module_get_module(itr); - if (install_dependent_module(mod, &ret)) + if (install_dependent_module(ctx, mod, find_suppliers_paths_for_module(kmod_module_get_name(mod)), &ret)) return -1; } + const char *supplier_path; + Iterator i; + HASHMAP_FOREACH(supplier_path, suppliers_paths, i) { + _cleanup_destroy_hashmap_ Hashmap *modules = hashmap_new(string_hash_func, string_compare_func); + find_modules_from_sysfs_node(ctx, supplier_path, modules); + + _cleanup_destroy_hashmap_ Hashmap *suppliers = hashmap_new(string_hash_func, string_compare_func); + find_suppliers_for_sys_node(ctx, suppliers, supplier_path, strlen(supplier_path)); + + if (!hashmap_isempty(modules)) { // Supplier is a module + const char *module; + Iterator j; + HASHMAP_FOREACH(module, modules, j) { + _cleanup_kmod_module_unref_ struct kmod_module *mod = NULL; + if (!kmod_module_new_from_name(ctx, module, &mod)) { + if (install_dependent_module(ctx, mod, suppliers, &ret)) + return -1; + } + } + } else { // Supplier is builtin + install_dependent_modules(ctx, NULL, suppliers); + } + } + return ret; } -static int install_module(struct kmod_module *mod) +static int install_module(struct kmod_ctx *ctx, struct kmod_module *mod) { int ret = 0; _cleanup_kmod_module_unref_list_ struct kmod_list *modlist = NULL; @@ -1612,15 +1741,16 @@ static int install_module(struct kmod_module *mod) } install_firmware(mod); + Hashmap *suppliers = find_suppliers_paths_for_module(name); modlist = kmod_module_get_dependencies(mod); - ret = install_dependent_modules(modlist); + ret = install_dependent_modules(ctx, modlist, suppliers); if (ret == 0) { ret = kmod_module_get_softdeps(mod, &modpre, &modpost); if (ret == 0) { int r; - ret = install_dependent_modules(modpre); - r = install_dependent_modules(modpost); + ret = install_dependent_modules(ctx, modpre, NULL); + r = install_dependent_modules(ctx, modpost, NULL); ret = ret ? : r; } } @@ -1731,6 +1861,9 @@ static int install_modules(int argc, char **argv) if (p != NULL) kerneldirlen = p - abskpath; + modules_suppliers = hashmap_new(string_hash_func, string_compare_func); + find_suppliers(ctx); + if (arg_hostonly) { char *modalias_file; modalias_file = getenv("DRACUT_KERNEL_MODALIASES"); @@ -1818,7 +1951,7 @@ static int install_modules(int argc, char **argv) } kmod_list_foreach(itr, modlist) { mod = kmod_module_get_module(itr); - r = install_module(mod); + r = install_module(ctx, mod); kmod_module_unref(mod); if ((r < 0) && !arg_optional) { if (!arg_silent) @@ -1897,7 +2030,7 @@ static int install_modules(int argc, char **argv) } kmod_list_foreach(itr, modlist) { mod = kmod_module_get_module(itr); - r = install_module(mod); + r = install_module(ctx, mod); kmod_module_unref(mod); if ((r < 0) && !arg_optional) { if (!arg_silent) @@ -1948,7 +2081,7 @@ static int install_modules(int argc, char **argv) } kmod_list_foreach(itr, modlist) { mod = kmod_module_get_module(itr); - r = install_module(mod); + r = install_module(ctx, mod); kmod_module_unref(mod); if ((r < 0) && !arg_optional) { if (!arg_silent) @@ -2111,9 +2244,18 @@ finish2: while ((i = hashmap_steal_first(items_failed))) item_free(i); + Hashmap *h; + while ((h = hashmap_steal_first(modules_suppliers))) { + while ((i = hashmap_steal_first(h))) { + item_free(i); + } + hashmap_free(h); + } + hashmap_free(items); hashmap_free(items_failed); hashmap_free(modules_loaded); + hashmap_free(modules_suppliers); strv_free(firmwaredirs); strv_free(pathdirs);