Skip to content

Commit

Permalink
libkmod: add weak dependecies
Browse files Browse the repository at this point in the history
It has been seen that for some network mac drivers (i.e. lan78xx) the
related module for the phy is loaded dynamically depending on the current
hardware. In this case, the associated phy is read using mdio bus and then
the associated phy module is loaded during runtime (kernel function
phy_request_driver_module). However, no software dependency is defined, so
the user tools will no be able to get this dependency. For example, if
dracut is used and the hardware is present, lan78xx will be included but no
phy module will be added, and in the next restart the device will not work
from boot because no related phy will be found during initramfs stage.

In order to solve this, we could define a normal 'pre' software dependency
in lan78xx module with all the possible phy modules (there may be some),
but proceeding in that way, all the possible phy modules would be loaded
while only one is necessary.

The idea is to create a new type of dependency, that we are going to call
'weak' to be used only by the user tools that need to detect this situation.
In that way, for example, dracut could check the 'weak' dependency of the
modules involved in order to install these dependencies in initramfs too.
That is, for the commented lan78xx module, defining the 'weak' dependency
with the possible phy modules list, only the necessary phy would be loaded
on demand keeping the same behavior, but all the possible phy modules would
be available from initramfs.

A new function 'kmod_module_get_weakdeps' in libkmod will be added for
this to avoid breaking the API and maintain backward compatibility. This
general procedure could be useful for other similar cases (not only for
dynamic phy loading).

Signed-off-by: Jose Ignacio Tornos Martinez <jtornosm@redhat.com>
Link: https://lore.kernel.org/r/20240327141116.97587-1-jtornosm@redhat.com
  • Loading branch information
jtornosm authored and lucasdemarchi committed May 9, 2024
1 parent 5a8b16b commit 05828b4
Show file tree
Hide file tree
Showing 9 changed files with 326 additions and 4 deletions.
2 changes: 2 additions & 0 deletions libkmod/docs/libkmod-sections.txt
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,7 @@ kmod_config_get_remove_commands
kmod_config_get_aliases
kmod_config_get_options
kmod_config_get_softdeps
kmod_config_get_weakdeps
kmod_config_iter_get_key
kmod_config_iter_get_value
kmod_config_iter_next
Expand All @@ -62,6 +63,7 @@ kmod_module_remove_module
kmod_module_get_module
kmod_module_get_dependencies
kmod_module_get_softdeps
kmod_module_get_weakdeps
kmod_module_apply_filter
kmod_module_get_filtered_blacklist
kmod_module_get_install_commands
Expand Down
212 changes: 212 additions & 0 deletions libkmod/libkmod-config.c
Original file line number Diff line number Diff line change
Expand Up @@ -58,6 +58,12 @@ struct kmod_softdep {
unsigned int n_post;
};

struct kmod_weakdep {
char *name;
const char **weak;
unsigned int n_weak;
};

const char *kmod_blacklist_get_modname(const struct kmod_list *l)
{
return l->data;
Expand Down Expand Up @@ -110,6 +116,16 @@ const char * const *kmod_softdep_get_post(const struct kmod_list *l, unsigned in
return dep->post;
}

const char *kmod_weakdep_get_name(const struct kmod_list *l) {
const struct kmod_weakdep *dep = l->data;
return dep->name;
}

const char * const *kmod_weakdep_get_weak(const struct kmod_list *l, unsigned int *count) {
const struct kmod_weakdep *dep = l->data;
*count = dep->n_weak;
return dep->weak;
}
static int kmod_config_add_command(struct kmod_config *config,
const char *modname,
const char *command,
Expand Down Expand Up @@ -392,6 +408,112 @@ static int kmod_config_add_softdep(struct kmod_config *config,
return 0;
}

static int kmod_config_add_weakdep(struct kmod_config *config,
const char *modname,
const char *line)
{
struct kmod_list *list;
struct kmod_weakdep *dep;
const char *s, *p;
char *itr;
unsigned int n_weak = 0;
size_t modnamelen = strlen(modname) + 1;
size_t buflen = 0;
bool was_space = false;

DBG(config->ctx, "modname=%s\n", modname);

/* analyze and count */
for (p = s = line; ; s++) {
size_t plen;

if (*s != '\0') {
if (!isspace(*s)) {
was_space = false;
continue;
}

if (was_space) {
p = s + 1;
continue;
}
was_space = true;

if (p >= s)
continue;
}
plen = s - p;

if (*s != '\0' || (*s == '\0' && !was_space)) {
buflen += plen + 1;
n_weak++;
}
p = s + 1;
if (*s == '\0')
break;
}

DBG(config->ctx, "%u weak\n", n_weak);

dep = malloc(sizeof(struct kmod_weakdep) + modnamelen +
n_weak * sizeof(const char *) +
buflen);
if (dep == NULL) {
ERR(config->ctx, "out-of-memory modname=%s\n", modname);
return -ENOMEM;
}
dep->n_weak = n_weak;
dep->weak = (const char **)((char *)dep + sizeof(struct kmod_weakdep));
dep->name = (char *)(dep->weak + n_weak);

memcpy(dep->name, modname, modnamelen);

/* copy strings */
itr = dep->name + modnamelen;
n_weak = 0;
was_space = false;
for (p = s = line; ; s++) {
size_t plen;

if (*s != '\0') {
if (!isspace(*s)) {
was_space = false;
continue;
}

if (was_space) {
p = s + 1;
continue;
}
was_space = true;

if (p >= s)
continue;
}
plen = s - p;

if (*s != '\0' || (*s == '\0' && !was_space)) {
dep->weak[n_weak] = itr;
memcpy(itr, p, plen);
itr[plen] = '\0';
itr += plen + 1;
n_weak++;
}
p = s + 1;
if (*s == '\0')
break;
}

list = kmod_list_append(config->weakdeps, dep);
if (list == NULL) {
free(dep);
return -ENOMEM;
}
config->weakdeps = list;

return 0;
}

static char *softdep_to_char(struct kmod_softdep *dep) {
const size_t sz_preprefix = sizeof("pre: ") - 1;
const size_t sz_postprefix = sizeof("post: ") - 1;
Expand Down Expand Up @@ -461,13 +583,58 @@ static char *softdep_to_char(struct kmod_softdep *dep) {
return s;
}

static char *weakdep_to_char(struct kmod_weakdep *dep) {
size_t sz;
const char *start, *end;
char *s, *itr;

/*
* Rely on the fact that dep->weak[] and are strv's that point to a
* contiguous buffer
*/
if (dep->n_weak > 0) {
start = dep->weak[0];
end = dep->weak[dep->n_weak - 1]
+ strlen(dep->weak[dep->n_weak - 1]);
sz = end - start;
} else
sz = 0;

itr = s = malloc(sz);
if (s == NULL)
return NULL;

if (sz) {
char *p;

/* include last '\0' */
memcpy(itr, dep->weak[0], sz + 1);
for (p = itr; p < itr + sz; p++) {
if (*p == '\0')
*p = ' ';
}
itr = p;
}

*itr = '\0';

return s;
}

static void kmod_config_free_softdep(struct kmod_config *config,
struct kmod_list *l)
{
free(l->data);
config->softdeps = kmod_list_remove(l);
}

static void kmod_config_free_weakdep(struct kmod_config *config,
struct kmod_list *l)
{
free(l->data);
config->weakdeps = kmod_list_remove(l);
}

static void kcmdline_parse_result(struct kmod_config *config, char *modname,
char *param, char *value)
{
Expand Down Expand Up @@ -703,6 +870,14 @@ static int kmod_config_parse(struct kmod_config *config, int fd,
goto syntax_error;

kmod_config_add_softdep(config, modname, softdeps);
} else if (streq(cmd, "weakdep")) {
char *modname = strtok_r(NULL, "\t ", &saveptr);
char *weakdeps = strtok_r(NULL, "\0", &saveptr);

if (underscores(modname) < 0 || weakdeps == NULL)
goto syntax_error;

kmod_config_add_weakdep(config, modname, weakdeps);
} else if (streq(cmd, "include")
|| streq(cmd, "config")) {
ERR(ctx, "%s: command %s is deprecated and not parsed anymore\n",
Expand Down Expand Up @@ -746,6 +921,9 @@ void kmod_config_free(struct kmod_config *config)
while (config->softdeps)
kmod_config_free_softdep(config, config->softdeps);

while (config->weakdeps)
kmod_config_free_weakdep(config, config->weakdeps);

for (; config->paths != NULL;
config->paths = kmod_list_remove(config->paths))
free(config->paths->data);
Expand Down Expand Up @@ -889,6 +1067,7 @@ int kmod_config_new(struct kmod_ctx *ctx, struct kmod_config **p_config,
size_t i;

conf_files_insert_sorted(ctx, &list, kmod_get_dirname(ctx), "modules.softdep");
conf_files_insert_sorted(ctx, &list, kmod_get_dirname(ctx), "modules.weakdep");

for (i = 0; config_paths[i] != NULL; i++) {
const char *path = config_paths[i];
Expand Down Expand Up @@ -973,6 +1152,7 @@ enum config_type {
CONFIG_TYPE_ALIAS,
CONFIG_TYPE_OPTION,
CONFIG_TYPE_SOFTDEP,
CONFIG_TYPE_WEAKDEP,
};

struct kmod_config_iter {
Expand All @@ -991,6 +1171,12 @@ static const char *softdep_get_plain_softdep(const struct kmod_list *l)
return s;
}

static const char *weakdep_get_plain_weakdep(const struct kmod_list *l)
{
char *s = weakdep_to_char(l->data);
return s;
}

static struct kmod_config_iter *kmod_config_iter_new(const struct kmod_ctx* ctx,
enum config_type type)
{
Expand Down Expand Up @@ -1033,6 +1219,12 @@ static struct kmod_config_iter *kmod_config_iter_new(const struct kmod_ctx* ctx,
iter->get_value = softdep_get_plain_softdep;
iter->intermediate = true;
break;
case CONFIG_TYPE_WEAKDEP:
iter->list = config->weakdeps;
iter->get_key = kmod_weakdep_get_name;
iter->get_value = weakdep_get_plain_weakdep;
iter->intermediate = true;
break;
}

return iter;
Expand Down Expand Up @@ -1163,6 +1355,26 @@ KMOD_EXPORT struct kmod_config_iter *kmod_config_get_softdeps(const struct kmod_
return kmod_config_iter_new(ctx, CONFIG_TYPE_SOFTDEP);
}

/**
* kmod_config_get_weakdeps:
* @ctx: kmod library context
*
* Retrieve an iterator to deal with the weakdeps maintained inside the
* library. See kmod_config_iter_get_key(), kmod_config_iter_get_value() and
* kmod_config_iter_next(). At least one call to kmod_config_iter_next() must
* be made to initialize the iterator and check if it's valid.
*
* Returns: a new iterator over the weakdeps or NULL on failure. Free it with
* kmod_config_iter_free_iter().
*/
KMOD_EXPORT struct kmod_config_iter *kmod_config_get_weakdeps(const struct kmod_ctx *ctx)
{
if (ctx == NULL)
return NULL;;

return kmod_config_iter_new(ctx, CONFIG_TYPE_WEAKDEP);
}

/**
* kmod_config_iter_get_key:
* @iter: iterator over a certain configuration
Expand Down
3 changes: 3 additions & 0 deletions libkmod/libkmod-internal.h
Original file line number Diff line number Diff line change
Expand Up @@ -128,6 +128,7 @@ struct kmod_config {
struct kmod_list *remove_commands;
struct kmod_list *install_commands;
struct kmod_list *softdeps;
struct kmod_list *weakdeps;

struct kmod_list *paths;
};
Expand All @@ -146,6 +147,8 @@ const char *kmod_softdep_get_name(const struct kmod_list *l) __attribute__((nonn
const char * const *kmod_softdep_get_pre(const struct kmod_list *l, unsigned int *count) __attribute__((nonnull(1, 2)));
const char * const *kmod_softdep_get_post(const struct kmod_list *l, unsigned int *count);

const char *kmod_weakdep_get_name(const struct kmod_list *l) __attribute__((nonnull(1)));
const char * const *kmod_weakdep_get_weak(const struct kmod_list *l, unsigned int *count) __attribute__((nonnull(1, 2)));

/* libkmod-module.c */
int kmod_module_new_from_alias(struct kmod_ctx *ctx, const char *alias, const char *name, struct kmod_module **mod);
Expand Down
Loading

0 comments on commit 05828b4

Please sign in to comment.