]> git.karo-electronics.de Git - karo-tx-linux.git/blobdiff - drivers/base/firmware_class.c
firmware loader: fix one reqeust_firmware race
[karo-tx-linux.git] / drivers / base / firmware_class.c
index 803cfc1597a9c89c5faf0edd0b76e418eb843d64..f2882511a9c1993bd708be3979803688e292f812 100644 (file)
 #include <linux/firmware.h>
 #include <linux/slab.h>
 #include <linux/sched.h>
+#include <linux/file.h>
+#include <linux/list.h>
+#include <linux/async.h>
+#include <linux/pm.h>
+#include <linux/suspend.h>
+#include <linux/syscore_ops.h>
+
+#include <generated/utsrelease.h>
+
+#include "base.h"
 
 MODULE_AUTHOR("Manuel Estrada Sainz");
 MODULE_DESCRIPTION("Multi purpose firmware loading support");
 MODULE_LICENSE("GPL");
 
+static const char *fw_path[] = {
+       "/lib/firmware/updates/" UTS_RELEASE,
+       "/lib/firmware/updates",
+       "/lib/firmware/" UTS_RELEASE,
+       "/lib/firmware"
+};
+
+/* Don't inline this: 'struct kstat' is biggish */
+static noinline long fw_file_size(struct file *file)
+{
+       struct kstat st;
+       if (vfs_getattr(file->f_path.mnt, file->f_path.dentry, &st))
+               return -1;
+       if (!S_ISREG(st.mode))
+               return -1;
+       if (st.size != (long)st.size)
+               return -1;
+       return st.size;
+}
+
+static bool fw_read_file_contents(struct file *file, struct firmware *fw)
+{
+       long size;
+       char *buf;
+
+       size = fw_file_size(file);
+       if (size < 0)
+               return false;
+       buf = vmalloc(size);
+       if (!buf)
+               return false;
+       if (kernel_read(file, 0, buf, size) != size) {
+               vfree(buf);
+               return false;
+       }
+       fw->data = buf;
+       fw->size = size;
+       return true;
+}
+
+static bool fw_get_filesystem_firmware(struct firmware *fw, const char *name)
+{
+       int i;
+       bool success = false;
+       char *path = __getname();
+
+       for (i = 0; i < ARRAY_SIZE(fw_path); i++) {
+               struct file *file;
+               snprintf(path, PATH_MAX, "%s/%s", fw_path[i], name);
+
+               file = filp_open(path, O_RDONLY, 0);
+               if (IS_ERR(file))
+                       continue;
+               success = fw_read_file_contents(file, fw);
+               fput(file);
+               if (success)
+                       break;
+       }
+       __putname(path);
+       return success;
+}
+
 /* Builtin firmware support */
 
 #ifdef CONFIG_FW_LOADER
@@ -85,23 +157,168 @@ static inline long firmware_loading_timeout(void)
        return loading_timeout > 0 ? loading_timeout * HZ : MAX_SCHEDULE_TIMEOUT;
 }
 
-/* fw_lock could be moved to 'struct firmware_priv' but since it is just
- * guarding for corner cases a global lock should be OK */
-static DEFINE_MUTEX(fw_lock);
+struct firmware_cache {
+       /* firmware_buf instance will be added into the below list */
+       spinlock_t lock;
+       struct list_head head;
+       int state;
+
+#ifdef CONFIG_PM_SLEEP
+       /*
+        * Names of firmware images which have been cached successfully
+        * will be added into the below list so that device uncache
+        * helper can trace which firmware images have been cached
+        * before.
+        */
+       spinlock_t name_lock;
+       struct list_head fw_names;
+
+       wait_queue_head_t wait_queue;
+       int cnt;
+       struct delayed_work work;
+
+       struct notifier_block   pm_notify;
+#endif
+};
 
-struct firmware_priv {
+struct firmware_buf {
+       struct kref ref;
+       struct list_head list;
        struct completion completion;
-       struct firmware *fw;
+       struct firmware_cache *fwc;
        unsigned long status;
+       void *data;
+       size_t size;
        struct page **pages;
        int nr_pages;
        int page_array_size;
+       char fw_id[];
+};
+
+struct fw_cache_entry {
+       struct list_head list;
+       char name[];
+};
+
+struct firmware_priv {
        struct timer_list timeout;
-       struct device dev;
        bool nowait;
-       char fw_id[];
+       struct device dev;
+       struct firmware_buf *buf;
+       struct firmware *fw;
+};
+
+struct fw_name_devm {
+       unsigned long magic;
+       char name[];
 };
 
+#define to_fwbuf(d) container_of(d, struct firmware_buf, ref)
+
+#define        FW_LOADER_NO_CACHE      0
+#define        FW_LOADER_START_CACHE   1
+
+static int fw_cache_piggyback_on_request(const char *name);
+
+/* fw_lock could be moved to 'struct firmware_priv' but since it is just
+ * guarding for corner cases a global lock should be OK */
+static DEFINE_MUTEX(fw_lock);
+
+static struct firmware_cache fw_cache;
+
+static struct firmware_buf *__allocate_fw_buf(const char *fw_name,
+                                             struct firmware_cache *fwc)
+{
+       struct firmware_buf *buf;
+
+       buf = kzalloc(sizeof(*buf) + strlen(fw_name) + 1 , GFP_ATOMIC);
+
+       if (!buf)
+               return buf;
+
+       kref_init(&buf->ref);
+       strcpy(buf->fw_id, fw_name);
+       buf->fwc = fwc;
+       init_completion(&buf->completion);
+
+       pr_debug("%s: fw-%s buf=%p\n", __func__, fw_name, buf);
+
+       return buf;
+}
+
+static struct firmware_buf *__fw_lookup_buf(const char *fw_name)
+{
+       struct firmware_buf *tmp;
+       struct firmware_cache *fwc = &fw_cache;
+
+       list_for_each_entry(tmp, &fwc->head, list)
+               if (!strcmp(tmp->fw_id, fw_name))
+                       return tmp;
+       return NULL;
+}
+
+static int fw_lookup_and_allocate_buf(const char *fw_name,
+                                     struct firmware_cache *fwc,
+                                     struct firmware_buf **buf)
+{
+       struct firmware_buf *tmp;
+
+       spin_lock(&fwc->lock);
+       tmp = __fw_lookup_buf(fw_name);
+       if (tmp) {
+               kref_get(&tmp->ref);
+               spin_unlock(&fwc->lock);
+               *buf = tmp;
+               return 1;
+       }
+       tmp = __allocate_fw_buf(fw_name, fwc);
+       if (tmp)
+               list_add(&tmp->list, &fwc->head);
+       spin_unlock(&fwc->lock);
+
+       *buf = tmp;
+
+       return tmp ? 0 : -ENOMEM;
+}
+
+static struct firmware_buf *fw_lookup_buf(const char *fw_name)
+{
+       struct firmware_buf *tmp;
+       struct firmware_cache *fwc = &fw_cache;
+
+       spin_lock(&fwc->lock);
+       tmp = __fw_lookup_buf(fw_name);
+       spin_unlock(&fwc->lock);
+
+       return tmp;
+}
+
+static void __fw_free_buf(struct kref *ref)
+{
+       struct firmware_buf *buf = to_fwbuf(ref);
+       struct firmware_cache *fwc = buf->fwc;
+       int i;
+
+       pr_debug("%s: fw-%s buf=%p data=%p size=%u\n",
+                __func__, buf->fw_id, buf, buf->data,
+                (unsigned int)buf->size);
+
+       spin_lock(&fwc->lock);
+       list_del(&buf->list);
+       spin_unlock(&fwc->lock);
+
+       vunmap(buf->data);
+       for (i = 0; i < buf->nr_pages; i++)
+               __free_page(buf->pages[i]);
+       kfree(buf->pages);
+       kfree(buf);
+}
+
+static void fw_free_buf(struct firmware_buf *buf)
+{
+       kref_put(&buf->ref, __fw_free_buf);
+}
+
 static struct firmware_priv *to_firmware_priv(struct device *dev)
 {
        return container_of(dev, struct firmware_priv, dev);
@@ -109,9 +326,10 @@ static struct firmware_priv *to_firmware_priv(struct device *dev)
 
 static void fw_load_abort(struct firmware_priv *fw_priv)
 {
-       set_bit(FW_STATUS_ABORT, &fw_priv->status);
-       wmb();
-       complete(&fw_priv->completion);
+       struct firmware_buf *buf = fw_priv->buf;
+
+       set_bit(FW_STATUS_ABORT, &buf->status);
+       complete_all(&buf->completion);
 }
 
 static ssize_t firmware_timeout_show(struct class *class,
@@ -154,11 +372,7 @@ static struct class_attribute firmware_class_attrs[] = {
 static void fw_dev_release(struct device *dev)
 {
        struct firmware_priv *fw_priv = to_firmware_priv(dev);
-       int i;
 
-       for (i = 0; i < fw_priv->nr_pages; i++)
-               __free_page(fw_priv->pages[i]);
-       kfree(fw_priv->pages);
        kfree(fw_priv);
 
        module_put(THIS_MODULE);
@@ -168,7 +382,7 @@ static int firmware_uevent(struct device *dev, struct kobj_uevent_env *env)
 {
        struct firmware_priv *fw_priv = to_firmware_priv(dev);
 
-       if (add_uevent_var(env, "FIRMWARE=%s", fw_priv->fw_id))
+       if (add_uevent_var(env, "FIRMWARE=%s", fw_priv->buf->fw_id))
                return -ENOMEM;
        if (add_uevent_var(env, "TIMEOUT=%i", loading_timeout))
                return -ENOMEM;
@@ -189,26 +403,38 @@ static ssize_t firmware_loading_show(struct device *dev,
                                     struct device_attribute *attr, char *buf)
 {
        struct firmware_priv *fw_priv = to_firmware_priv(dev);
-       int loading = test_bit(FW_STATUS_LOADING, &fw_priv->status);
+       int loading = test_bit(FW_STATUS_LOADING, &fw_priv->buf->status);
 
        return sprintf(buf, "%d\n", loading);
 }
 
+/* firmware holds the ownership of pages */
 static void firmware_free_data(const struct firmware *fw)
 {
-       int i;
-       vunmap(fw->data);
-       if (fw->pages) {
-               for (i = 0; i < PFN_UP(fw->size); i++)
-                       __free_page(fw->pages[i]);
-               kfree(fw->pages);
+       /* Loaded directly? */
+       if (!fw->priv) {
+               vfree(fw->data);
+               return;
        }
+       fw_free_buf(fw->priv);
 }
 
 /* Some architectures don't have PAGE_KERNEL_RO */
 #ifndef PAGE_KERNEL_RO
 #define PAGE_KERNEL_RO PAGE_KERNEL
 #endif
+
+/* one pages buffer should be mapped/unmapped only once */
+static int fw_map_pages_buf(struct firmware_buf *buf)
+{
+       if (buf->data)
+               vunmap(buf->data);
+       buf->data = vmap(buf->pages, buf->nr_pages, 0, PAGE_KERNEL_RO);
+       if (!buf->data)
+               return -ENOMEM;
+       return 0;
+}
+
 /**
  * firmware_loading_store - set value in the 'loading' control file
  * @dev: device pointer
@@ -227,45 +453,41 @@ static ssize_t firmware_loading_store(struct device *dev,
                                      const char *buf, size_t count)
 {
        struct firmware_priv *fw_priv = to_firmware_priv(dev);
+       struct firmware_buf *fw_buf = fw_priv->buf;
        int loading = simple_strtol(buf, NULL, 10);
        int i;
 
        mutex_lock(&fw_lock);
 
-       if (!fw_priv->fw)
+       if (!fw_buf)
                goto out;
 
        switch (loading) {
        case 1:
-               firmware_free_data(fw_priv->fw);
-               memset(fw_priv->fw, 0, sizeof(struct firmware));
-               /* If the pages are not owned by 'struct firmware' */
-               for (i = 0; i < fw_priv->nr_pages; i++)
-                       __free_page(fw_priv->pages[i]);
-               kfree(fw_priv->pages);
-               fw_priv->pages = NULL;
-               fw_priv->page_array_size = 0;
-               fw_priv->nr_pages = 0;
-               set_bit(FW_STATUS_LOADING, &fw_priv->status);
+               /* discarding any previous partial load */
+               if (!test_bit(FW_STATUS_DONE, &fw_buf->status)) {
+                       for (i = 0; i < fw_buf->nr_pages; i++)
+                               __free_page(fw_buf->pages[i]);
+                       kfree(fw_buf->pages);
+                       fw_buf->pages = NULL;
+                       fw_buf->page_array_size = 0;
+                       fw_buf->nr_pages = 0;
+                       set_bit(FW_STATUS_LOADING, &fw_buf->status);
+               }
                break;
        case 0:
-               if (test_bit(FW_STATUS_LOADING, &fw_priv->status)) {
-                       vunmap(fw_priv->fw->data);
-                       fw_priv->fw->data = vmap(fw_priv->pages,
-                                                fw_priv->nr_pages,
-                                                0, PAGE_KERNEL_RO);
-                       if (!fw_priv->fw->data) {
-                               dev_err(dev, "%s: vmap() failed\n", __func__);
-                               goto err;
-                       }
-                       /* Pages are now owned by 'struct firmware' */
-                       fw_priv->fw->pages = fw_priv->pages;
-                       fw_priv->pages = NULL;
-
-                       fw_priv->page_array_size = 0;
-                       fw_priv->nr_pages = 0;
-                       complete(&fw_priv->completion);
-                       clear_bit(FW_STATUS_LOADING, &fw_priv->status);
+               if (test_bit(FW_STATUS_LOADING, &fw_buf->status)) {
+                       set_bit(FW_STATUS_DONE, &fw_buf->status);
+                       clear_bit(FW_STATUS_LOADING, &fw_buf->status);
+
+                       /*
+                        * Several loading requests may be pending on
+                        * one same firmware buf, so let all requests
+                        * see the mapped 'buf->data' once the loading
+                        * is completed.
+                        * */
+                       fw_map_pages_buf(fw_buf);
+                       complete_all(&fw_buf->completion);
                        break;
                }
                /* fallthrough */
@@ -273,7 +495,6 @@ static ssize_t firmware_loading_store(struct device *dev,
                dev_err(dev, "%s: unexpected value (%d)\n", __func__, loading);
                /* fallthrough */
        case -1:
-       err:
                fw_load_abort(fw_priv);
                break;
        }
@@ -290,21 +511,21 @@ static ssize_t firmware_data_read(struct file *filp, struct kobject *kobj,
 {
        struct device *dev = kobj_to_dev(kobj);
        struct firmware_priv *fw_priv = to_firmware_priv(dev);
-       struct firmware *fw;
+       struct firmware_buf *buf;
        ssize_t ret_count;
 
        mutex_lock(&fw_lock);
-       fw = fw_priv->fw;
-       if (!fw || test_bit(FW_STATUS_DONE, &fw_priv->status)) {
+       buf = fw_priv->buf;
+       if (!buf || test_bit(FW_STATUS_DONE, &buf->status)) {
                ret_count = -ENODEV;
                goto out;
        }
-       if (offset > fw->size) {
+       if (offset > buf->size) {
                ret_count = 0;
                goto out;
        }
-       if (count > fw->size - offset)
-               count = fw->size - offset;
+       if (count > buf->size - offset)
+               count = buf->size - offset;
 
        ret_count = count;
 
@@ -314,11 +535,11 @@ static ssize_t firmware_data_read(struct file *filp, struct kobject *kobj,
                int page_ofs = offset & (PAGE_SIZE-1);
                int page_cnt = min_t(size_t, PAGE_SIZE - page_ofs, count);
 
-               page_data = kmap(fw_priv->pages[page_nr]);
+               page_data = kmap(buf->pages[page_nr]);
 
                memcpy(buffer, page_data + page_ofs, page_cnt);
 
-               kunmap(fw_priv->pages[page_nr]);
+               kunmap(buf->pages[page_nr]);
                buffer += page_cnt;
                offset += page_cnt;
                count -= page_cnt;
@@ -330,12 +551,13 @@ out:
 
 static int fw_realloc_buffer(struct firmware_priv *fw_priv, int min_size)
 {
+       struct firmware_buf *buf = fw_priv->buf;
        int pages_needed = ALIGN(min_size, PAGE_SIZE) >> PAGE_SHIFT;
 
        /* If the array of pages is too small, grow it... */
-       if (fw_priv->page_array_size < pages_needed) {
+       if (buf->page_array_size < pages_needed) {
                int new_array_size = max(pages_needed,
-                                        fw_priv->page_array_size * 2);
+                                        buf->page_array_size * 2);
                struct page **new_pages;
 
                new_pages = kmalloc(new_array_size * sizeof(void *),
@@ -344,24 +566,24 @@ static int fw_realloc_buffer(struct firmware_priv *fw_priv, int min_size)
                        fw_load_abort(fw_priv);
                        return -ENOMEM;
                }
-               memcpy(new_pages, fw_priv->pages,
-                      fw_priv->page_array_size * sizeof(void *));
-               memset(&new_pages[fw_priv->page_array_size], 0, sizeof(void *) *
-                      (new_array_size - fw_priv->page_array_size));
-               kfree(fw_priv->pages);
-               fw_priv->pages = new_pages;
-               fw_priv->page_array_size = new_array_size;
+               memcpy(new_pages, buf->pages,
+                      buf->page_array_size * sizeof(void *));
+               memset(&new_pages[buf->page_array_size], 0, sizeof(void *) *
+                      (new_array_size - buf->page_array_size));
+               kfree(buf->pages);
+               buf->pages = new_pages;
+               buf->page_array_size = new_array_size;
        }
 
-       while (fw_priv->nr_pages < pages_needed) {
-               fw_priv->pages[fw_priv->nr_pages] =
+       while (buf->nr_pages < pages_needed) {
+               buf->pages[buf->nr_pages] =
                        alloc_page(GFP_KERNEL | __GFP_HIGHMEM);
 
-               if (!fw_priv->pages[fw_priv->nr_pages]) {
+               if (!buf->pages[buf->nr_pages]) {
                        fw_load_abort(fw_priv);
                        return -ENOMEM;
                }
-               fw_priv->nr_pages++;
+               buf->nr_pages++;
        }
        return 0;
 }
@@ -384,18 +606,19 @@ static ssize_t firmware_data_write(struct file *filp, struct kobject *kobj,
 {
        struct device *dev = kobj_to_dev(kobj);
        struct firmware_priv *fw_priv = to_firmware_priv(dev);
-       struct firmware *fw;
+       struct firmware_buf *buf;
        ssize_t retval;
 
        if (!capable(CAP_SYS_RAWIO))
                return -EPERM;
 
        mutex_lock(&fw_lock);
-       fw = fw_priv->fw;
-       if (!fw || test_bit(FW_STATUS_DONE, &fw_priv->status)) {
+       buf = fw_priv->buf;
+       if (!buf || test_bit(FW_STATUS_DONE, &buf->status)) {
                retval = -ENODEV;
                goto out;
        }
+
        retval = fw_realloc_buffer(fw_priv, offset + count);
        if (retval)
                goto out;
@@ -408,17 +631,17 @@ static ssize_t firmware_data_write(struct file *filp, struct kobject *kobj,
                int page_ofs = offset & (PAGE_SIZE - 1);
                int page_cnt = min_t(size_t, PAGE_SIZE - page_ofs, count);
 
-               page_data = kmap(fw_priv->pages[page_nr]);
+               page_data = kmap(buf->pages[page_nr]);
 
                memcpy(page_data + page_ofs, buffer, page_cnt);
 
-               kunmap(fw_priv->pages[page_nr]);
+               kunmap(buf->pages[page_nr]);
                buffer += page_cnt;
                offset += page_cnt;
                count -= page_cnt;
        }
 
-       fw->size = max_t(size_t, offset, fw->size);
+       buf->size = max_t(size_t, offset, buf->size);
 out:
        mutex_unlock(&fw_lock);
        return retval;
@@ -445,35 +668,111 @@ fw_create_instance(struct firmware *firmware, const char *fw_name,
        struct firmware_priv *fw_priv;
        struct device *f_dev;
 
-       fw_priv = kzalloc(sizeof(*fw_priv) + strlen(fw_name) + 1 , GFP_KERNEL);
+       fw_priv = kzalloc(sizeof(*fw_priv), GFP_KERNEL);
        if (!fw_priv) {
                dev_err(device, "%s: kmalloc failed\n", __func__);
-               return ERR_PTR(-ENOMEM);
+               fw_priv = ERR_PTR(-ENOMEM);
+               goto exit;
        }
 
-       fw_priv->fw = firmware;
        fw_priv->nowait = nowait;
-       strcpy(fw_priv->fw_id, fw_name);
-       init_completion(&fw_priv->completion);
+       fw_priv->fw = firmware;
        setup_timer(&fw_priv->timeout,
                    firmware_class_timeout, (u_long) fw_priv);
 
        f_dev = &fw_priv->dev;
 
        device_initialize(f_dev);
-       dev_set_name(f_dev, "%s", dev_name(device));
+       dev_set_name(f_dev, "%s", fw_name);
        f_dev->parent = device;
        f_dev->class = &firmware_class;
-
+exit:
        return fw_priv;
 }
 
+/* store the pages buffer info firmware from buf */
+static void fw_set_page_data(struct firmware_buf *buf, struct firmware *fw)
+{
+       fw->priv = buf;
+       fw->pages = buf->pages;
+       fw->size = buf->size;
+       fw->data = buf->data;
+
+       pr_debug("%s: fw-%s buf=%p data=%p size=%u\n",
+                __func__, buf->fw_id, buf, buf->data,
+                (unsigned int)buf->size);
+}
+
+#ifdef CONFIG_PM_SLEEP
+static void fw_name_devm_release(struct device *dev, void *res)
+{
+       struct fw_name_devm *fwn = res;
+
+       if (fwn->magic == (unsigned long)&fw_cache)
+               pr_debug("%s: fw_name-%s devm-%p released\n",
+                               __func__, fwn->name, res);
+}
+
+static int fw_devm_match(struct device *dev, void *res,
+               void *match_data)
+{
+       struct fw_name_devm *fwn = res;
+
+       return (fwn->magic == (unsigned long)&fw_cache) &&
+               !strcmp(fwn->name, match_data);
+}
+
+static struct fw_name_devm *fw_find_devm_name(struct device *dev,
+               const char *name)
+{
+       struct fw_name_devm *fwn;
+
+       fwn = devres_find(dev, fw_name_devm_release,
+                         fw_devm_match, (void *)name);
+       return fwn;
+}
+
+/* add firmware name into devres list */
+static int fw_add_devm_name(struct device *dev, const char *name)
+{
+       struct fw_name_devm *fwn;
+
+       fwn = fw_find_devm_name(dev, name);
+       if (fwn)
+               return 1;
+
+       fwn = devres_alloc(fw_name_devm_release, sizeof(struct fw_name_devm) +
+                          strlen(name) + 1, GFP_KERNEL);
+       if (!fwn)
+               return -ENOMEM;
+
+       fwn->magic = (unsigned long)&fw_cache;
+       strcpy(fwn->name, name);
+       devres_add(dev, fwn);
+
+       return 0;
+}
+#else
+static int fw_add_devm_name(struct device *dev, const char *name)
+{
+       return 0;
+}
+#endif
+
+static void _request_firmware_cleanup(const struct firmware **firmware_p)
+{
+       release_firmware(*firmware_p);
+       *firmware_p = NULL;
+}
+
 static struct firmware_priv *
 _request_firmware_prepare(const struct firmware **firmware_p, const char *name,
                          struct device *device, bool uevent, bool nowait)
 {
        struct firmware *firmware;
-       struct firmware_priv *fw_priv;
+       struct firmware_priv *fw_priv = NULL;
+       struct firmware_buf *buf;
+       int ret;
 
        if (!firmware_p)
                return ERR_PTR(-EINVAL);
@@ -490,18 +789,51 @@ _request_firmware_prepare(const struct firmware **firmware_p, const char *name,
                return NULL;
        }
 
-       fw_priv = fw_create_instance(firmware, name, device, uevent, nowait);
-       if (IS_ERR(fw_priv)) {
-               release_firmware(firmware);
+       if (fw_get_filesystem_firmware(firmware, name)) {
+               dev_dbg(device, "firmware: direct-loading firmware %s\n", name);
+               return NULL;
+       }
+
+       ret = fw_lookup_and_allocate_buf(name, &fw_cache, &buf);
+       if (!ret)
+               fw_priv = fw_create_instance(firmware, name, device,
+                               uevent, nowait);
+
+       if (IS_ERR(fw_priv) || ret < 0) {
+               kfree(firmware);
                *firmware_p = NULL;
+               return ERR_PTR(-ENOMEM);
+       } else if (fw_priv) {
+               fw_priv->buf = buf;
+
+               /*
+                * bind with 'buf' now to avoid warning in failure path
+                * of requesting firmware.
+                */
+               firmware->priv = buf;
+               return fw_priv;
        }
-       return fw_priv;
-}
 
-static void _request_firmware_cleanup(const struct firmware **firmware_p)
-{
-       release_firmware(*firmware_p);
-       *firmware_p = NULL;
+       /* share the cached buf, which is inprogessing or completed */
+ check_status:
+       mutex_lock(&fw_lock);
+       if (test_bit(FW_STATUS_ABORT, &buf->status)) {
+               fw_priv = ERR_PTR(-ENOENT);
+               firmware->priv = buf;
+               _request_firmware_cleanup(firmware_p);
+               goto exit;
+       } else if (test_bit(FW_STATUS_DONE, &buf->status)) {
+               fw_priv = NULL;
+               fw_set_page_data(buf, firmware);
+               goto exit;
+       }
+       mutex_unlock(&fw_lock);
+       wait_for_completion(&buf->completion);
+       goto check_status;
+
+exit:
+       mutex_unlock(&fw_lock);
+       return fw_priv;
 }
 
 static int _request_firmware_load(struct firmware_priv *fw_priv, bool uevent,
@@ -509,6 +841,8 @@ static int _request_firmware_load(struct firmware_priv *fw_priv, bool uevent,
 {
        int retval = 0;
        struct device *f_dev = &fw_priv->dev;
+       struct firmware_buf *buf = fw_priv->buf;
+       struct firmware_cache *fwc = &fw_cache;
 
        dev_set_uevent_suppress(f_dev, true);
 
@@ -535,7 +869,7 @@ static int _request_firmware_load(struct firmware_priv *fw_priv, bool uevent,
 
        if (uevent) {
                dev_set_uevent_suppress(f_dev, false);
-               dev_dbg(f_dev, "firmware: requesting %s\n", fw_priv->fw_id);
+               dev_dbg(f_dev, "firmware: requesting %s\n", buf->fw_id);
                if (timeout != MAX_SCHEDULE_TIMEOUT)
                        mod_timer(&fw_priv->timeout,
                                  round_jiffies_up(jiffies + timeout));
@@ -543,15 +877,37 @@ static int _request_firmware_load(struct firmware_priv *fw_priv, bool uevent,
                kobject_uevent(&fw_priv->dev.kobj, KOBJ_ADD);
        }
 
-       wait_for_completion(&fw_priv->completion);
+       wait_for_completion(&buf->completion);
 
-       set_bit(FW_STATUS_DONE, &fw_priv->status);
        del_timer_sync(&fw_priv->timeout);
 
        mutex_lock(&fw_lock);
-       if (!fw_priv->fw->size || test_bit(FW_STATUS_ABORT, &fw_priv->status))
+       if (!buf->size || test_bit(FW_STATUS_ABORT, &buf->status))
                retval = -ENOENT;
-       fw_priv->fw = NULL;
+
+       /*
+        * add firmware name into devres list so that we can auto cache
+        * and uncache firmware for device.
+        *
+        * f_dev->parent may has been deleted already, but the problem
+        * should be fixed in devres or driver core.
+        */
+       if (!retval && f_dev->parent)
+               fw_add_devm_name(f_dev->parent, buf->fw_id);
+
+       /*
+        * After caching firmware image is started, let it piggyback
+        * on request firmware.
+        */
+       if (!retval && fwc->state == FW_LOADER_START_CACHE) {
+               if (fw_cache_piggyback_on_request(buf->fw_id))
+                       kref_get(&buf->ref);
+       }
+
+       /* pass the pages buffer to driver at the last minute */
+       fw_set_page_data(buf, fw_priv->fw);
+
+       fw_priv->buf = NULL;
        mutex_unlock(&fw_lock);
 
        device_remove_file(f_dev, &dev_attr_loading);
@@ -578,6 +934,8 @@ err_put_dev:
  *      @name will be used as $FIRMWARE in the uevent environment and
  *      should be distinctive enough not to be confused with any other
  *      firmware image for this or any other device.
+ *
+ *     Caller must hold the reference count of @device.
  **/
 int
 request_firmware(const struct firmware **firmware_p, const char *name,
@@ -659,6 +1017,7 @@ static void request_firmware_work_func(struct work_struct *work)
 
  out:
        fw_work->cont(fw, fw_work->context);
+       put_device(fw_work->device);
 
        module_put(fw_work->module);
        kfree(fw_work);
@@ -677,9 +1036,15 @@ static void request_firmware_work_func(struct work_struct *work)
  * @cont: function will be called asynchronously when the firmware
  *     request is over.
  *
- *     Asynchronous variant of request_firmware() for user contexts where
- *     it is not possible to sleep for long time. It can't be called
- *     in atomic contexts.
+ *     Caller must hold the reference count of @device.
+ *
+ *     Asynchronous variant of request_firmware() for user contexts:
+ *             - sleep for as small periods as possible since it may
+ *             increase kernel boot time of built-in device drivers
+ *             requesting firmware in their ->probe() methods, if
+ *             @gfp is GFP_KERNEL.
+ *
+ *             - can't sleep at all if @gfp is GFP_ATOMIC.
  **/
 int
 request_firmware_nowait(
@@ -705,18 +1070,384 @@ request_firmware_nowait(
                return -EFAULT;
        }
 
+       get_device(fw_work->device);
        INIT_WORK(&fw_work->work, request_firmware_work_func);
        schedule_work(&fw_work->work);
        return 0;
 }
 
+/**
+ * cache_firmware - cache one firmware image in kernel memory space
+ * @fw_name: the firmware image name
+ *
+ * Cache firmware in kernel memory so that drivers can use it when
+ * system isn't ready for them to request firmware image from userspace.
+ * Once it returns successfully, driver can use request_firmware or its
+ * nowait version to get the cached firmware without any interacting
+ * with userspace
+ *
+ * Return 0 if the firmware image has been cached successfully
+ * Return !0 otherwise
+ *
+ */
+int cache_firmware(const char *fw_name)
+{
+       int ret;
+       const struct firmware *fw;
+
+       pr_debug("%s: %s\n", __func__, fw_name);
+
+       ret = request_firmware(&fw, fw_name, NULL);
+       if (!ret)
+               kfree(fw);
+
+       pr_debug("%s: %s ret=%d\n", __func__, fw_name, ret);
+
+       return ret;
+}
+
+/**
+ * uncache_firmware - remove one cached firmware image
+ * @fw_name: the firmware image name
+ *
+ * Uncache one firmware image which has been cached successfully
+ * before.
+ *
+ * Return 0 if the firmware cache has been removed successfully
+ * Return !0 otherwise
+ *
+ */
+int uncache_firmware(const char *fw_name)
+{
+       struct firmware_buf *buf;
+       struct firmware fw;
+
+       pr_debug("%s: %s\n", __func__, fw_name);
+
+       if (fw_get_builtin_firmware(&fw, fw_name))
+               return 0;
+
+       buf = fw_lookup_buf(fw_name);
+       if (buf) {
+               fw_free_buf(buf);
+               return 0;
+       }
+
+       return -EINVAL;
+}
+
+#ifdef CONFIG_PM_SLEEP
+static struct fw_cache_entry *alloc_fw_cache_entry(const char *name)
+{
+       struct fw_cache_entry *fce;
+
+       fce = kzalloc(sizeof(*fce) + strlen(name) + 1, GFP_ATOMIC);
+       if (!fce)
+               goto exit;
+
+       strcpy(fce->name, name);
+exit:
+       return fce;
+}
+
+static int __fw_entry_found(const char *name)
+{
+       struct firmware_cache *fwc = &fw_cache;
+       struct fw_cache_entry *fce;
+
+       list_for_each_entry(fce, &fwc->fw_names, list) {
+               if (!strcmp(fce->name, name))
+                       return 1;
+       }
+       return 0;
+}
+
+static int fw_cache_piggyback_on_request(const char *name)
+{
+       struct firmware_cache *fwc = &fw_cache;
+       struct fw_cache_entry *fce;
+       int ret = 0;
+
+       spin_lock(&fwc->name_lock);
+       if (__fw_entry_found(name))
+               goto found;
+
+       fce = alloc_fw_cache_entry(name);
+       if (fce) {
+               ret = 1;
+               list_add(&fce->list, &fwc->fw_names);
+               pr_debug("%s: fw: %s\n", __func__, name);
+       }
+found:
+       spin_unlock(&fwc->name_lock);
+       return ret;
+}
+
+static void free_fw_cache_entry(struct fw_cache_entry *fce)
+{
+       kfree(fce);
+}
+
+static void __async_dev_cache_fw_image(void *fw_entry,
+                                      async_cookie_t cookie)
+{
+       struct fw_cache_entry *fce = fw_entry;
+       struct firmware_cache *fwc = &fw_cache;
+       int ret;
+
+       ret = cache_firmware(fce->name);
+       if (ret) {
+               spin_lock(&fwc->name_lock);
+               list_del(&fce->list);
+               spin_unlock(&fwc->name_lock);
+
+               free_fw_cache_entry(fce);
+       }
+
+       spin_lock(&fwc->name_lock);
+       fwc->cnt--;
+       spin_unlock(&fwc->name_lock);
+
+       wake_up(&fwc->wait_queue);
+}
+
+/* called with dev->devres_lock held */
+static void dev_create_fw_entry(struct device *dev, void *res,
+                               void *data)
+{
+       struct fw_name_devm *fwn = res;
+       const char *fw_name = fwn->name;
+       struct list_head *head = data;
+       struct fw_cache_entry *fce;
+
+       fce = alloc_fw_cache_entry(fw_name);
+       if (fce)
+               list_add(&fce->list, head);
+}
+
+static int devm_name_match(struct device *dev, void *res,
+                          void *match_data)
+{
+       struct fw_name_devm *fwn = res;
+       return (fwn->magic == (unsigned long)match_data);
+}
+
+static void dev_cache_fw_image(struct device *dev, void *data)
+{
+       LIST_HEAD(todo);
+       struct fw_cache_entry *fce;
+       struct fw_cache_entry *fce_next;
+       struct firmware_cache *fwc = &fw_cache;
+
+       devres_for_each_res(dev, fw_name_devm_release,
+                           devm_name_match, &fw_cache,
+                           dev_create_fw_entry, &todo);
+
+       list_for_each_entry_safe(fce, fce_next, &todo, list) {
+               list_del(&fce->list);
+
+               spin_lock(&fwc->name_lock);
+               /* only one cache entry for one firmware */
+               if (!__fw_entry_found(fce->name)) {
+                       fwc->cnt++;
+                       list_add(&fce->list, &fwc->fw_names);
+               } else {
+                       free_fw_cache_entry(fce);
+                       fce = NULL;
+               }
+               spin_unlock(&fwc->name_lock);
+
+               if (fce)
+                       async_schedule(__async_dev_cache_fw_image,
+                                      (void *)fce);
+       }
+}
+
+static void __device_uncache_fw_images(void)
+{
+       struct firmware_cache *fwc = &fw_cache;
+       struct fw_cache_entry *fce;
+
+       spin_lock(&fwc->name_lock);
+       while (!list_empty(&fwc->fw_names)) {
+               fce = list_entry(fwc->fw_names.next,
+                               struct fw_cache_entry, list);
+               list_del(&fce->list);
+               spin_unlock(&fwc->name_lock);
+
+               uncache_firmware(fce->name);
+               free_fw_cache_entry(fce);
+
+               spin_lock(&fwc->name_lock);
+       }
+       spin_unlock(&fwc->name_lock);
+}
+
+/**
+ * device_cache_fw_images - cache devices' firmware
+ *
+ * If one device called request_firmware or its nowait version
+ * successfully before, the firmware names are recored into the
+ * device's devres link list, so device_cache_fw_images can call
+ * cache_firmware() to cache these firmwares for the device,
+ * then the device driver can load its firmwares easily at
+ * time when system is not ready to complete loading firmware.
+ */
+static void device_cache_fw_images(void)
+{
+       struct firmware_cache *fwc = &fw_cache;
+       int old_timeout;
+       DEFINE_WAIT(wait);
+
+       pr_debug("%s\n", __func__);
+
+       /* cancel uncache work */
+       cancel_delayed_work_sync(&fwc->work);
+
+       /*
+        * use small loading timeout for caching devices' firmware
+        * because all these firmware images have been loaded
+        * successfully at lease once, also system is ready for
+        * completing firmware loading now. The maximum size of
+        * firmware in current distributions is about 2M bytes,
+        * so 10 secs should be enough.
+        */
+       old_timeout = loading_timeout;
+       loading_timeout = 10;
+
+       mutex_lock(&fw_lock);
+       fwc->state = FW_LOADER_START_CACHE;
+       dpm_for_each_dev(NULL, dev_cache_fw_image);
+       mutex_unlock(&fw_lock);
+
+       /* wait for completion of caching firmware for all devices */
+       spin_lock(&fwc->name_lock);
+       for (;;) {
+               prepare_to_wait(&fwc->wait_queue, &wait,
+                               TASK_UNINTERRUPTIBLE);
+               if (!fwc->cnt)
+                       break;
+
+               spin_unlock(&fwc->name_lock);
+
+               schedule();
+
+               spin_lock(&fwc->name_lock);
+       }
+       spin_unlock(&fwc->name_lock);
+       finish_wait(&fwc->wait_queue, &wait);
+
+       loading_timeout = old_timeout;
+}
+
+/**
+ * device_uncache_fw_images - uncache devices' firmware
+ *
+ * uncache all firmwares which have been cached successfully
+ * by device_uncache_fw_images earlier
+ */
+static void device_uncache_fw_images(void)
+{
+       pr_debug("%s\n", __func__);
+       __device_uncache_fw_images();
+}
+
+static void device_uncache_fw_images_work(struct work_struct *work)
+{
+       device_uncache_fw_images();
+}
+
+/**
+ * device_uncache_fw_images_delay - uncache devices firmwares
+ * @delay: number of milliseconds to delay uncache device firmwares
+ *
+ * uncache all devices's firmwares which has been cached successfully
+ * by device_cache_fw_images after @delay milliseconds.
+ */
+static void device_uncache_fw_images_delay(unsigned long delay)
+{
+       schedule_delayed_work(&fw_cache.work,
+                       msecs_to_jiffies(delay));
+}
+
+static int fw_pm_notify(struct notifier_block *notify_block,
+                       unsigned long mode, void *unused)
+{
+       switch (mode) {
+       case PM_HIBERNATION_PREPARE:
+       case PM_SUSPEND_PREPARE:
+               device_cache_fw_images();
+               break;
+
+       case PM_POST_SUSPEND:
+       case PM_POST_HIBERNATION:
+       case PM_POST_RESTORE:
+               /*
+                * In case that system sleep failed and syscore_suspend is
+                * not called.
+                */
+               mutex_lock(&fw_lock);
+               fw_cache.state = FW_LOADER_NO_CACHE;
+               mutex_unlock(&fw_lock);
+
+               device_uncache_fw_images_delay(10 * MSEC_PER_SEC);
+               break;
+       }
+
+       return 0;
+}
+
+/* stop caching firmware once syscore_suspend is reached */
+static int fw_suspend(void)
+{
+       fw_cache.state = FW_LOADER_NO_CACHE;
+       return 0;
+}
+
+static struct syscore_ops fw_syscore_ops = {
+       .suspend = fw_suspend,
+};
+#else
+static int fw_cache_piggyback_on_request(const char *name)
+{
+       return 0;
+}
+#endif
+
+static void __init fw_cache_init(void)
+{
+       spin_lock_init(&fw_cache.lock);
+       INIT_LIST_HEAD(&fw_cache.head);
+       fw_cache.state = FW_LOADER_NO_CACHE;
+
+#ifdef CONFIG_PM_SLEEP
+       spin_lock_init(&fw_cache.name_lock);
+       INIT_LIST_HEAD(&fw_cache.fw_names);
+       fw_cache.cnt = 0;
+
+       init_waitqueue_head(&fw_cache.wait_queue);
+       INIT_DELAYED_WORK(&fw_cache.work,
+                         device_uncache_fw_images_work);
+
+       fw_cache.pm_notify.notifier_call = fw_pm_notify;
+       register_pm_notifier(&fw_cache.pm_notify);
+
+       register_syscore_ops(&fw_syscore_ops);
+#endif
+}
+
 static int __init firmware_class_init(void)
 {
+       fw_cache_init();
        return class_register(&firmware_class);
 }
 
 static void __exit firmware_class_exit(void)
 {
+#ifdef CONFIG_PM_SLEEP
+       unregister_syscore_ops(&fw_syscore_ops);
+       unregister_pm_notifier(&fw_cache.pm_notify);
+#endif
        class_unregister(&firmware_class);
 }
 
@@ -726,3 +1457,5 @@ module_exit(firmware_class_exit);
 EXPORT_SYMBOL(release_firmware);
 EXPORT_SYMBOL(request_firmware);
 EXPORT_SYMBOL(request_firmware_nowait);
+EXPORT_SYMBOL_GPL(cache_firmware);
+EXPORT_SYMBOL_GPL(uncache_firmware);