base/firmware retry firmware load after rootfs mount 63/200163/2
authorChristoph Manszewski <c.manszewski@samsung.com>
Fri, 15 Feb 2019 14:50:00 +0000 (15:50 +0100)
committerSeung-Woo Kim <sw0312.kim@samsung.com>
Wed, 27 Feb 2019 00:27:40 +0000 (00:27 +0000)
Return -EPROBE_DEFER for request_firmware() until root filesystem is
mounted. In case of request_firmware_nowait() create a list of deferred
firmware load requests, and retry firmware load after root filesytem is
mounted.

This allows to have wifi drivers build into the kernel, but
the firmware files shipped on the root filesystem.

Signed-off-by: Marek Szyprowski <m.szyprowski@samsung.com>
Signed-off-by: Christoph Manszewski <c.manszewski@samsung.com>
Change-Id: I40c0a2a98ca9b1a95fb743a848d2366250cae839

drivers/base/dd.c
drivers/base/firmware_class.c
include/linux/device.h
include/linux/firmware.h
init/do_mounts.c

index d928cc6..ed4cb84 100644 (file)
@@ -180,7 +180,7 @@ static bool driver_deferred_probe_enable = false;
  * changes in the midst of a probe, then deferred processing should be triggered
  * again.
  */
-static void driver_deferred_probe_trigger(void)
+void driver_deferred_probe_trigger(void)
 {
        if (!driver_deferred_probe_enable)
                return;
index 4b57cf5..5eaa0ff 100644 (file)
@@ -32,6 +32,7 @@
 #include <linux/syscore_ops.h>
 #include <linux/reboot.h>
 #include <linux/security.h>
+#include <linux/root_dev.h>
 
 #include <generated/utsrelease.h>
 
@@ -392,6 +393,9 @@ fw_get_filesystem_firmware(struct device *device, struct firmware_buf *buf)
        enum kernel_read_file_id id = READING_FIRMWARE;
        size_t msize = INT_MAX;
 
+       if (ROOT_DEV == 0)
+               return -EPROBE_DEFER;
+
        /* Already populated data member means we're loading into a buffer */
        if (buf->data) {
                id = READING_FIRMWARE_PREALLOC_BUFFER;
@@ -1351,17 +1355,28 @@ struct firmware_work {
        void *context;
        void (*cont)(const struct firmware *fw, void *context);
        unsigned int opt_flags;
+       struct list_head node;
 };
+static LIST_HEAD(firmware_work_list);
+static DEFINE_SPINLOCK(firmware_work_list_lock);
 
 static void request_firmware_work_func(struct work_struct *work)
 {
        struct firmware_work *fw_work;
        const struct firmware *fw;
+       int err = 0;
 
        fw_work = container_of(work, struct firmware_work, work);
 
-       _request_firmware(&fw, fw_work->name, fw_work->device, NULL, 0,
-                         fw_work->opt_flags);
+       err = _request_firmware(&fw, fw_work->name, fw_work->device, NULL, 0,
+                       fw_work->opt_flags);
+       if (err == -EPROBE_DEFER) {
+               spin_lock(&firmware_work_list_lock);
+               list_add_tail(&fw_work->node, &firmware_work_list);
+               spin_unlock(&firmware_work_list_lock);
+               return;
+       }
+
        fw_work->cont(fw, fw_work->context);
        put_device(fw_work->device); /* taken in request_firmware_nowait() */
 
@@ -1370,6 +1385,24 @@ static void request_firmware_work_func(struct work_struct *work)
        kfree(fw_work);
 }
 
+void retry_request_firmware(void)
+{
+       struct firmware_work *fw_work;
+
+       spin_lock(&firmware_work_list_lock);
+       while (!list_empty(&firmware_work_list)) {
+               fw_work = list_first_entry(&firmware_work_list,
+                                          struct firmware_work, node);
+               list_del(&fw_work->node);
+               spin_unlock(&firmware_work_list_lock);
+
+               request_firmware_work_func(&fw_work->work);
+
+               spin_lock(&firmware_work_list_lock);
+       }
+       spin_unlock(&firmware_work_list_lock);
+}
+
 /**
  * request_firmware_nowait - asynchronous version of request_firmware
  * @module: module requesting the firmware
@@ -1416,6 +1449,7 @@ request_firmware_nowait(
        fw_work->cont = cont;
        fw_work->opt_flags = FW_OPT_NOWAIT | FW_OPT_FALLBACK |
                (uevent ? FW_OPT_UEVENT : FW_OPT_USERHELPER);
+       INIT_LIST_HEAD(&fw_work->node);
 
        if (!try_module_get(module)) {
                kfree_const(fw_work->name);
index 66fe271..79c3774 100644 (file)
@@ -294,6 +294,7 @@ extern void driver_unregister(struct device_driver *drv);
 
 extern struct device_driver *driver_find(const char *name,
                                         struct bus_type *bus);
+extern void driver_deferred_probe_trigger(void);
 extern int driver_probe_done(void);
 extern void wait_for_device_probe(void);
 
index d450808..740d77e 100644 (file)
@@ -42,6 +42,7 @@ struct builtin_fw {
 #if defined(CONFIG_FW_LOADER) || (defined(CONFIG_FW_LOADER_MODULE) && defined(MODULE))
 int request_firmware(const struct firmware **fw, const char *name,
                     struct device *device);
+void retry_request_firmware(void);
 int request_firmware_nowait(
        struct module *module, bool uevent,
        const char *name, struct device *device, gfp_t gfp, void *context,
@@ -59,6 +60,11 @@ static inline int request_firmware(const struct firmware **fw,
 {
        return -EINVAL;
 }
+
+static inline void retry_request_firmware(void)
+{
+}
+
 static inline int request_firmware_nowait(
        struct module *module, bool uevent,
        const char *name, struct device *device, gfp_t gfp, void *context,
index 7cf4f6d..8364445 100644 (file)
@@ -28,6 +28,7 @@
 #include <linux/slab.h>
 #include <linux/ramfs.h>
 #include <linux/shmem_fs.h>
+#include <linux/firmware.h>
 
 #include <linux/nfs_fs.h>
 #include <linux/nfs_fs_sb.h>
@@ -601,6 +602,8 @@ out:
        devtmpfs_mount("dev");
        sys_mount(".", "/", NULL, MS_MOVE, NULL);
        sys_chroot(".");
+       driver_deferred_probe_trigger();
+       retry_request_firmware();
 }
 
 static bool is_tmpfs;