The thing is that built-in modules are being inited before rootfs mount. Some of the modules can request firmware loading from another thread using async 'request_firmware_nowait' call on inition, so we can catch this kind of race: rootfs does not exist yet, but we are going to open and load firmware file requesting it from the kernel thread.
Solution is simple: before any rootfs access firmware loader must wait for rootfs mount. There are few questions which remain unanswered, but in this patch I do not want to complicate things and want to fix exactly the race. So here are the questions: 1. can 'request_firmware' (sync variant) be called on module inition directly, without any threads? if can, that means firmware will never be loaded for the driver at first try in case of built-in module, because rootfs will be mounted only at the end of kernel init sequence. 2. in case of separated '/lib' mount point we still have the problem: firmware will be loaded only on further tries, but the first attempt will always fail, because '/lib' mount point does not exist. Seems to me this can't be simply fixed, and firmware_request logic needs some refactoring. Probably, there are other good questions which I do not see because of shallow understanding of init and firmware loading sequences. Signed-off-by: Roman Pen <r.peni...@gmail.com> Cc: Ming Lei <ming....@canonical.com> Cc: Greg Kroah-Hartman <gre...@linuxfoundation.org> Cc: Andrew Morton <a...@linux-foundation.org> Cc: Oleg Nesterov <o...@redhat.com> Cc: linux-kernel@vger.kernel.org --- drivers/base/firmware_class.c | 6 ++++++ include/linux/init.h | 1 + init/main.c | 33 +++++++++++++++++++++++++++++++-- 3 files changed, 38 insertions(+), 2 deletions(-) diff --git a/drivers/base/firmware_class.c b/drivers/base/firmware_class.c index bf42430..450c4ae 100644 --- a/drivers/base/firmware_class.c +++ b/drivers/base/firmware_class.c @@ -29,6 +29,7 @@ #include <linux/syscore_ops.h> #include <linux/reboot.h> #include <linux/security.h> +#include <linux/init.h> #include <generated/utsrelease.h> @@ -324,6 +325,11 @@ static int fw_get_filesystem_firmware(struct device *device, int rc = -ENOENT; char *path = __getname(); + /* Before any file access we have to wait for rootfs. + In case of built-in module we can race with kernel init + thread, which has not mounted rootfs yet */ + wait_for_rootfs(); + for (i = 0; i < ARRAY_SIZE(fw_path); i++) { struct file *file; diff --git a/include/linux/init.h b/include/linux/init.h index 2df8e8d..83233ae 100644 --- a/include/linux/init.h +++ b/include/linux/init.h @@ -152,6 +152,7 @@ void setup_arch(char **); void prepare_namespace(void); void __init load_default_modules(void); int __init init_rootfs(void); +void wait_for_rootfs(void); extern void (*late_time_init)(void); diff --git a/init/main.c b/init/main.c index bb1aed9..2cfe187 100644 --- a/init/main.c +++ b/init/main.c @@ -973,6 +973,33 @@ static int __ref kernel_init(void *unused) "See Linux Documentation/init.txt for guidance."); } +static DECLARE_WAIT_QUEUE_HEAD(rootfs_waitq); +static bool rootfs_mounted; + +void wait_for_rootfs(void) +{ + /* Here we try to protect from a few things: + * 1. Avoid waiting for ourselves, when init thread has not + * mounted rootfs yet. + * 2. Avoid warning if call chain was initiated from userspace + * /sbin/init. For example when /sbin/init loads the driver, + * which, in turn, wants to access rootfs (e.g. firmware loading), + * thus it has to be sure, that rootfs has been successfully + * mounted. + */ + if (rootfs_mounted || WARN_ON(is_global_init(current))) + return; + else + wait_event(rootfs_waitq, rootfs_mounted); +} +EXPORT_SYMBOL(wait_for_rootfs); + +static inline void wake_up_rootfs_waiters(void) +{ + rootfs_mounted = true; + wake_up_all(&rootfs_waitq); +} + static noinline void __init kernel_init_freeable(void) { /* @@ -1025,9 +1052,11 @@ static noinline void __init kernel_init_freeable(void) /* * Ok, we have completed the initial bootup, and - * we're essentially up and running. Get rid of the - * initmem segments and start the user-mode stuff.. + * we're essentially up and running. Wake up the + * rootfs mount waiters, get rid of the initmem + * segments, and start the user-mode stuff.. */ + wake_up_rootfs_waiters(); /* rootfs is available now, try loading default modules */ load_default_modules(); -- 2.0.0 -- To unsubscribe from this list: send the line "unsubscribe linux-kernel" in the body of a message to majord...@vger.kernel.org More majordomo info at http://vger.kernel.org/majordomo-info.html Please read the FAQ at http://www.tux.org/lkml/