Skip to content

Commit

Permalink
module: extract patient module check into helper
Browse files Browse the repository at this point in the history
The patient module check inside add_unformed_module() is large
enough as we need it. It is a bit hard to read too, so just
move it to a helper and do the inverse checks first to help
shift the code and make it easier to read. The new helper then
is module_patient_check_exists().

To make this work we need to mvoe the finished_loading() up,
we do that without making any functional changes to that routine.

Reviewed-by: David Hildenbrand <[email protected]>
Signed-off-by: Luis Chamberlain <[email protected]>
  • Loading branch information
mcgrof committed Apr 18, 2023
1 parent 25a1b5b commit f71afa6
Showing 1 changed file with 60 additions and 52 deletions.
112 changes: 60 additions & 52 deletions kernel/module/main.c
Original file line number Diff line number Diff line change
Expand Up @@ -2447,27 +2447,6 @@ static int post_relocation(struct module *mod, const struct load_info *info)
return module_finalize(info->hdr, info->sechdrs, mod);
}

/* Is this module of this name done loading? No locks held. */
static bool finished_loading(const char *name)
{
struct module *mod;
bool ret;

/*
* The module_mutex should not be a heavily contended lock;
* if we get the occasional sleep here, we'll go an extra iteration
* in the wait_event_interruptible(), which is harmless.
*/
sched_annotate_sleep();
mutex_lock(&module_mutex);
mod = find_module_all(name, strlen(name), true);
ret = !mod || mod->state == MODULE_STATE_LIVE
|| mod->state == MODULE_STATE_GOING;
mutex_unlock(&module_mutex);

return ret;
}

/* Call module constructors. */
static void do_mod_ctors(struct module *mod)
{
Expand Down Expand Up @@ -2631,6 +2610,63 @@ static int may_init_module(void)
return 0;
}

/* Is this module of this name done loading? No locks held. */
static bool finished_loading(const char *name)
{
struct module *mod;
bool ret;

/*
* The module_mutex should not be a heavily contended lock;
* if we get the occasional sleep here, we'll go an extra iteration
* in the wait_event_interruptible(), which is harmless.
*/
sched_annotate_sleep();
mutex_lock(&module_mutex);
mod = find_module_all(name, strlen(name), true);
ret = !mod || mod->state == MODULE_STATE_LIVE
|| mod->state == MODULE_STATE_GOING;
mutex_unlock(&module_mutex);

return ret;
}

/* Must be called with module_mutex held */
static int module_patient_check_exists(const char *name)
{
struct module *old;
int err = 0;

old = find_module_all(name, strlen(name), true);
if (old == NULL)
return 0;

if (old->state == MODULE_STATE_COMING ||
old->state == MODULE_STATE_UNFORMED) {
/* Wait in case it fails to load. */
mutex_unlock(&module_mutex);
err = wait_event_interruptible(module_wq,
finished_loading(name));
mutex_lock(&module_mutex);
if (err)
return err;

/* The module might have gone in the meantime. */
old = find_module_all(name, strlen(name), true);
}

/*
* We are here only when the same module was being loaded. Do
* not try to load it again right now. It prevents long delays
* caused by serialized module load failures. It might happen
* when more devices of the same type trigger load of
* a particular module.
*/
if (old && old->state == MODULE_STATE_LIVE)
return -EEXIST;
return -EBUSY;
}

/*
* We try to place it in the list now to make sure it's unique before
* we dedicate too many resources. In particular, temporary percpu
Expand All @@ -2639,49 +2675,21 @@ static int may_init_module(void)
static int add_unformed_module(struct module *mod)
{
int err;
struct module *old;

mod->state = MODULE_STATE_UNFORMED;

mutex_lock(&module_mutex);
old = find_module_all(mod->name, strlen(mod->name), true);
if (old != NULL) {
if (old->state == MODULE_STATE_COMING
|| old->state == MODULE_STATE_UNFORMED) {
/* Wait in case it fails to load. */
mutex_unlock(&module_mutex);
err = wait_event_interruptible(module_wq,
finished_loading(mod->name));
if (err)
goto out_unlocked;

/* The module might have gone in the meantime. */
mutex_lock(&module_mutex);
old = find_module_all(mod->name, strlen(mod->name),
true);
}

/*
* We are here only when the same module was being loaded. Do
* not try to load it again right now. It prevents long delays
* caused by serialized module load failures. It might happen
* when more devices of the same type trigger load of
* a particular module.
*/
if (old && old->state == MODULE_STATE_LIVE)
err = -EEXIST;
else
err = -EBUSY;
err = module_patient_check_exists(mod->name);
if (err)
goto out;
}

mod_update_bounds(mod);
list_add_rcu(&mod->list, &modules);
mod_tree_insert(mod);
err = 0;

out:
mutex_unlock(&module_mutex);
out_unlocked:
return err;
}

Expand Down

0 comments on commit f71afa6

Please sign in to comment.