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

This Linux kernel change "kernel/module.c: Only return -EEXIST for modules that have finished loading" is included in the Linux 4.14.137 release. This change is authored by Prarit Bhargava <prarit [at] redhat.com> on Wed May 29 07:26:25 2019 -0400. The commit for this change in Linux stable tree is 902d0ba (patch) which is from upstream commit 6e6de3d. The same Linux upstream change may have been applied to various maintained Linux releases and you can find all Linux releases containing changes from upstream 6e6de3d.

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

[ Upstream commit 6e6de3dee51a439f76eb73c22ae2ffd2c9384712 ]

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: Prarit Bhargava <prarit@redhat.com>
Signed-off-by: Barret 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: Jessica Yu <jeyu@kernel.org>
Signed-off-by: Sasha Levin <sashal@kernel.org>

There are 6 lines of Linux source code added/deleted in this change. Code changes to Linux kernel are as follows.

 kernel/module.c | 6 ++----
 1 file changed, 2 insertions(+), 4 deletions(-)

diff --git a/kernel/module.c b/kernel/module.c
index 94528b8..4b372c1 100644
--- a/kernel/module.c
+++ b/kernel/module.c
@@ -3391,8 +3391,7 @@ static bool finished_loading(const char *name)
    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;
+   ret = !mod || mod->state == MODULE_STATE_LIVE;
    mutex_unlock(&module_mutex);

    return ret;
@@ -3560,8 +3559,7 @@ static int add_unformed_module(struct module *mod)
    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) {
+       if (old->state != MODULE_STATE_LIVE) {
            /* Wait in case it fails to load. */
            mutex_unlock(&module_mutex);
            err = wait_event_interruptible(module_wq,

Leave a Reply

Your email address will not be published. Required fields are marked *