Commit 6e6de3de authored by Prarit Bhargava's avatar Prarit Bhargava Committed by Jessica Yu

kernel/module.c: Only return -EEXIST for modules that have finished loading

Microsoft HyperV disables the X86_FEATURE_SMCA bit on AMD systems, and
linux guests boot with repeated errors:

amd64_edac_mod: Unknown symbol amd_unregister_ecc_decoder (err -2)
amd64_edac_mod: Unknown symbol amd_register_ecc_decoder (err -2)
amd64_edac_mod: Unknown symbol amd_report_gart_errors (err -2)
amd64_edac_mod: Unknown symbol amd_unregister_ecc_decoder (err -2)
amd64_edac_mod: Unknown symbol amd_register_ecc_decoder (err -2)
amd64_edac_mod: Unknown symbol amd_report_gart_errors (err -2)

The warnings occur because the module code erroneously returns -EEXIST
for modules that have failed to load and are in the process of being
removed from the module list.

module amd64_edac_mod has a dependency on module edac_mce_amd.  Using
modules.dep, systemd will load edac_mce_amd for every request of
amd64_edac_mod.  When the edac_mce_amd module loads, the module has
state MODULE_STATE_UNFORMED and once the module load fails and the state
becomes MODULE_STATE_GOING.  Another request for edac_mce_amd module
executes and add_unformed_module() will erroneously return -EEXIST even
though the previous instance of edac_mce_amd has MODULE_STATE_GOING.
Upon receiving -EEXIST, systemd attempts to load amd64_edac_mod, which
fails because of unknown symbols from edac_mce_amd.

add_unformed_module() must wait to return for any case other than
MODULE_STATE_LIVE to prevent a race between multiple loads of
dependent modules.
Signed-off-by: default avatarPrarit Bhargava <prarit@redhat.com>
Signed-off-by: default avatarBarret Rhoden <brho@google.com>
Cc: David Arcari <darcari@redhat.com>
Cc: Jessica Yu <jeyu@kernel.org>
Cc: Heiko Carstens <heiko.carstens@de.ibm.com>
Signed-off-by: default avatarJessica Yu <jeyu@kernel.org>
parent cd6c84d8
...@@ -3397,8 +3397,7 @@ static bool finished_loading(const char *name) ...@@ -3397,8 +3397,7 @@ static bool finished_loading(const char *name)
sched_annotate_sleep(); sched_annotate_sleep();
mutex_lock(&module_mutex); mutex_lock(&module_mutex);
mod = find_module_all(name, strlen(name), true); mod = find_module_all(name, strlen(name), true);
ret = !mod || mod->state == MODULE_STATE_LIVE ret = !mod || mod->state == MODULE_STATE_LIVE;
|| mod->state == MODULE_STATE_GOING;
mutex_unlock(&module_mutex); mutex_unlock(&module_mutex);
return ret; return ret;
...@@ -3588,8 +3587,7 @@ static int add_unformed_module(struct module *mod) ...@@ -3588,8 +3587,7 @@ static int add_unformed_module(struct module *mod)
mutex_lock(&module_mutex); mutex_lock(&module_mutex);
old = find_module_all(mod->name, strlen(mod->name), true); old = find_module_all(mod->name, strlen(mod->name), true);
if (old != NULL) { if (old != NULL) {
if (old->state == MODULE_STATE_COMING if (old->state != MODULE_STATE_LIVE) {
|| old->state == MODULE_STATE_UNFORMED) {
/* Wait in case it fails to load. */ /* Wait in case it fails to load. */
mutex_unlock(&module_mutex); mutex_unlock(&module_mutex);
err = wait_event_interruptible(module_wq, err = wait_event_interruptible(module_wq,
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment