]> git.ipfire.org Git - thirdparty/dracut.git/commitdiff
feat(dracut-install): add fw_devlink suppliers as module dependencies
authorAdrien Thierry <athierry@redhat.com>
Mon, 5 Dec 2022 21:28:17 +0000 (16:28 -0500)
committerJóhann B. Guðmundsson <johannbg@gmail.com>
Mon, 13 Feb 2023 13:29:12 +0000 (13:29 +0000)
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 <athierry@redhat.com>
src/install/dracut-install.c

index 81fd37f5e58ecd1787fb5d7b4990fbdc28861779..5af3534556caefaab1bc17d697de82ce4fac4d37 100644 (file)
@@ -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);