Skip to content

Commit

Permalink
feat(dracut-install): add fw_devlink suppliers as module dependencies
Browse files Browse the repository at this point in the history
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>
  • Loading branch information
athierry1 authored and johannbg committed Feb 13, 2023
1 parent f0c3b68 commit 3de4c73
Showing 1 changed file with 156 additions and 14 deletions.
170 changes: 156 additions & 14 deletions src/install/dracut-install.c
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand All @@ -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)
{
Expand Down Expand Up @@ -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;
Expand Down Expand Up @@ -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;
}
}
Expand All @@ -1547,22 +1652,46 @@ 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;

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;
Expand Down Expand Up @@ -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;
}
}
Expand Down Expand Up @@ -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");
Expand Down Expand Up @@ -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)
Expand Down Expand Up @@ -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)
Expand Down Expand Up @@ -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)
Expand Down Expand Up @@ -2111,9 +2244,18 @@ int main(int argc, char **argv)
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);
Expand Down

0 comments on commit 3de4c73

Please sign in to comment.