From: Ming Lei <ming.lei@canonical.com>
To: Linus Torvalds <torvalds@linux-foundation.org>,
Greg Kroah-Hartman <gregkh@linuxfoundation.org>
Cc: "Rafael J. Wysocki" <rjw@sisk.pl>,
Borislav Petkov <borislav.petkov@amd.com>,
linux-kernel@vger.kernel.org, Ming Lei <ming.lei@canonical.com>
Subject: [RFC PATCH 11/13] driver core: firmware: introduce devices_cache/uncache_firmwares
Date: Wed, 25 Jul 2012 01:00:11 +0800 [thread overview]
Message-ID: <1343149213-10160-12-git-send-email-ming.lei@canonical.com> (raw)
In-Reply-To: <1343149213-10160-1-git-send-email-ming.lei@canonical.com>
This patches introduces the three helpers below:
void device_cache_firmwares(void)
void device_uncache_firmwares(void)
void device_uncache_firmwares_delay(unsigned long)
so we can call device_cache_firmwares() to cache firmwares for
all devices which need firmwares to work, and the device driver
can get the firmware easily from kernel memory when system isn't
readly for completing their requests of loading firmwares.
When system is ready for completing firmware loading, driver core
can call device_uncache_firmwares() or its delay version to free
the cached firmwares.
The above helpers should be used to cache device firmwares during
system suspend/resume cycle in the following patches.
Signed-off-by: Ming Lei <ming.lei@canonical.com>
---
drivers/base/firmware_class.c | 236 +++++++++++++++++++++++++++++++++++++++--
1 file changed, 230 insertions(+), 6 deletions(-)
diff --git a/drivers/base/firmware_class.c b/drivers/base/firmware_class.c
index c181e6d..7a96e75 100644
--- a/drivers/base/firmware_class.c
+++ b/drivers/base/firmware_class.c
@@ -22,6 +22,10 @@
#include <linux/slab.h>
#include <linux/sched.h>
#include <linux/list.h>
+#include <linux/async.h>
+#include <linux/pm.h>
+
+#include "base.h"
MODULE_AUTHOR("Manuel Estrada Sainz");
MODULE_DESCRIPTION("Multi purpose firmware loading support");
@@ -91,6 +95,17 @@ struct firmware_cache {
/* firmware_buf instance will be added into the below list */
spinlock_t lock;
struct list_head head;
+
+ /*
+ * Name of firmware which has been cached successfully will be
+ * added into the below list so that device uncache helper can
+ * trace which firmware has been cached before.
+ */
+ spinlock_t name_lock;
+ struct list_head name_head;
+ wait_queue_head_t wait_queue;
+ int cnt;
+ struct delayed_work work;
};
struct firmware_buf {
@@ -107,6 +122,11 @@ struct firmware_buf {
char fw_id[];
};
+struct fw_name_for_cache {
+ struct list_head list;
+ char name[];
+};
+
struct firmware_priv {
struct timer_list timeout;
bool nowait;
@@ -214,12 +234,6 @@ static void fw_free_buf(struct firmware_buf *buf)
kref_put(&buf->ref, __fw_free_buf);
}
-static void fw_cache_init(void)
-{
- spin_lock_init(&fw_cache.lock);
- INIT_LIST_HEAD(&fw_cache.head);
-}
-
static struct firmware_priv *to_firmware_priv(struct device *dev)
{
return container_of(dev, struct firmware_priv, dev);
@@ -981,6 +995,216 @@ int uncache_firmware(const char *fw_name)
return -EINVAL;
}
+static struct fw_name_for_cache *alloc_fw_name_cache(const char *name)
+{
+ struct fw_name_for_cache *nc;
+
+ nc = kzalloc(sizeof(nc) + strlen(name) + 1, GFP_KERNEL);
+ if (!nc)
+ goto exit;
+
+ strcpy(nc->name, name);
+exit:
+ return nc;
+}
+
+static void free_fw_name_cache(struct fw_name_for_cache *nc)
+{
+ kfree(nc);
+}
+
+static void __async_dev_cache_firmware(void *fw_name,
+ async_cookie_t cookie)
+{
+ struct fw_name_for_cache *nc;
+ struct firmware_cache *fwc = &fw_cache;
+ char *curr_name;
+ int ret;
+
+ /* 'fw_name' is stored in devres, and it may be released,
+ * so allocate buffer to store the firmware name
+ */
+ curr_name = kstrdup((const char *)fw_name, GFP_KERNEL);
+ if (!curr_name) {
+ ret = -ENOMEM;
+ goto drop_ref;
+ }
+
+ strcpy(curr_name, fw_name);
+
+ ret = cache_firmware(curr_name);
+
+ if (!ret) {
+ /*
+ * allocate/all the instance of alloc_fw_name_cache
+ * for uncaching later if cache_firmware returns
+ * successfully
+ */
+ nc = alloc_fw_name_cache(curr_name);
+
+ /*
+ * have to uncache firmware in case of allocation
+ * failure since we can't trace the firmware cache
+ * any more without the firmware name.
+ */
+ if (!nc) {
+ uncache_firmware(curr_name);
+ } else {
+ spin_lock(&fwc->name_lock);
+ list_add(&nc->list, &fwc->name_head);
+ spin_unlock(&fwc->name_lock);
+ }
+ }
+ kfree(curr_name);
+
+drop_ref:
+ spin_lock(&fwc->name_lock);
+ fwc->cnt--;
+ spin_unlock(&fwc->name_lock);
+ wake_up(&fwc->wait_queue);
+}
+
+static void __dev_cache_firmware(struct device *dev, void *res)
+{
+ struct fw_name_devm *fwn = res;
+ const char *fw_name = fwn->name;
+ struct firmware_cache *fwc = &fw_cache;
+
+ dev_dbg(dev, "fw-%s %d\n", fw_name, fwc->cnt);
+
+ spin_lock(&fwc->name_lock);
+ fwc->cnt++;
+ spin_unlock(&fwc->name_lock);
+
+ async_schedule(__async_dev_cache_firmware, (void *)fw_name);
+}
+
+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_firmware(struct device *dev)
+{
+ devres_for_each_res(dev, fw_name_devm_release,
+ devm_name_match, &fw_cache,
+ __dev_cache_firmware);
+}
+
+static void __device_uncache_firmwares(void)
+{
+ struct firmware_cache *fwc = &fw_cache;
+ struct fw_name_for_cache *nc;
+
+ spin_lock(&fwc->name_lock);
+ while (!list_empty(&fwc->name_head)) {
+ nc = list_entry(fwc->name_head.next,
+ struct fw_name_for_cache, list);
+ list_del(&nc->list);
+ spin_unlock(&fwc->name_lock);
+
+ uncache_firmware(nc->name);
+ free_fw_name_cache(nc);
+
+ spin_lock(&fwc->name_lock);
+ }
+ spin_unlock(&fwc->name_lock);
+}
+
+extern struct list_head dpm_list;
+/**
+ * device_cache_firmwares - cache devices' firmwares
+ *
+ * For each devices, if they called request_firmware or
+ * request_firmware_nowait successfully before, their firmware
+ * name will be recored into these devices' devres link list, so
+ * device_cache_firmwares can call cache_firmware() to cache these
+ * firmwares for these devices, then these device drivers can load
+ * their firmwares easily at any time even when system is not ready
+ * to complete loading firmwares.
+ *
+ */
+static void device_cache_firmwares(void)
+{
+ struct firmware_cache *fwc = &fw_cache;
+ struct device *dev;
+ DEFINE_WAIT(wait);
+
+ pr_debug("%s\n", __func__);
+
+ device_pm_lock();
+ list_for_each_entry(dev, &dpm_list, power.entry)
+ dev_cache_firmware(dev);
+ device_pm_unlock();
+
+ pr_debug("%s firmwares %d\n", __func__, fwc->cnt);
+
+ /* 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);
+}
+
+/**
+ * device_uncache_firmwares - uncache devices' firmwares
+ *
+ * uncache all firmwares which have been cached successfully
+ * by device_uncache_firmwares
+ *
+ */
+static void device_uncache_firmwares(void)
+{
+ pr_debug("%s\n", __func__);
+ __device_uncache_firmwares();
+}
+
+static void device_uncache_firmwares_work(struct work_struct *work)
+{
+ device_uncache_firmwares();
+}
+
+/**
+ * device_uncache_firmwares_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_firmwares after @delay milliseconds.
+ *
+ */
+static void device_uncache_firmwares_delay(unsigned long delay)
+{
+ schedule_delayed_work(&fw_cache.work,
+ msecs_to_jiffies(delay));
+}
+
+static void __init fw_cache_init(void)
+{
+ spin_lock_init(&fw_cache.lock);
+ INIT_LIST_HEAD(&fw_cache.head);
+
+ spin_lock_init(&fw_cache.name_lock);
+ INIT_LIST_HEAD(&fw_cache.name_head);
+ fw_cache.cnt = 0;
+
+ init_waitqueue_head(&fw_cache.wait_queue);
+ INIT_DELAYED_WORK(&fw_cache.work,
+ device_uncache_firmwares_work);
+}
+
static int __init firmware_class_init(void)
{
fw_cache_init();
--
1.7.9.5
next prev parent reply other threads:[~2012-07-24 17:01 UTC|newest]
Thread overview: 62+ messages / expand[flat|nested] mbox.gz Atom feed top
2012-07-24 17:00 [RFC PATCH 00/13] firmware loader: introduce cache/uncache firmware Ming Lei
2012-07-24 17:00 ` [RFC PATCH 01/13] driver core: firmware loader: simplify pages ownership transfer Ming Lei
2012-07-24 18:10 ` Borislav Petkov
2012-07-25 2:49 ` Ming Lei
2012-07-24 17:00 ` [RFC PATCH 02/13] driver core: firmware loader: fix races during loading firmware Ming Lei
2012-07-24 17:00 ` [RFC PATCH 03/13] driver core: firmware loader: remove unnecessary wmb() Ming Lei
2012-07-24 17:00 ` [RFC PATCH 04/13] driver core: firmware loader: fix creation failure of fw loader device Ming Lei
2012-07-24 17:00 ` [RFC PATCH 05/13] driver core: firmware loader: introduce firmware_buf Ming Lei
2012-07-25 13:59 ` Borislav Petkov
2012-07-26 2:51 ` Ming Lei
2012-07-26 10:08 ` Borislav Petkov
2012-07-24 17:00 ` [RFC PATCH 06/13] driver core: firmware loader: always let firmware_buf own the pages buffer Ming Lei
2012-07-25 7:55 ` Stephen Boyd
2012-07-25 14:37 ` Borislav Petkov
2012-08-03 8:34 ` Ming Lei
2012-07-25 16:02 ` Borislav Petkov
2012-07-25 16:13 ` Borislav Petkov
2012-07-24 17:00 ` [RFC PATCH 07/13] driver core: firmware loader: introduce cache_firmware and uncache_firmware Ming Lei
2012-07-25 7:54 ` Stephen Boyd
2012-07-26 2:34 ` Ming Lei
2012-07-25 15:52 ` Borislav Petkov
2012-07-26 2:40 ` Ming Lei
2012-07-24 17:00 ` [RFC PATCH 08/13] driver core: firmware loader: fix device lifetime Ming Lei
2012-07-25 16:04 ` Borislav Petkov
2012-07-26 2:59 ` Ming Lei
2012-07-26 12:20 ` Borislav Petkov
2012-07-26 15:44 ` Ming Lei
2012-07-26 17:46 ` Borislav Petkov
2012-07-27 1:30 ` Ming Lei
2012-07-27 10:32 ` Borislav Petkov
2012-07-28 14:04 ` Ming Lei
2012-07-24 17:00 ` [RFC PATCH 09/13] driver core: firmware loader: store firmware name into devres list Ming Lei
2012-07-25 16:15 ` Borislav Petkov
2012-07-26 15:15 ` Ming Lei
2012-07-24 17:00 ` [RFC PATCH 10/13] driver core: devres: introduce devres_for_each_res Ming Lei
2012-07-25 16:25 ` Borislav Petkov
2012-07-26 16:51 ` Ming Lei
2012-07-24 17:00 ` Ming Lei [this message]
2012-07-25 16:52 ` [RFC PATCH 11/13] driver core: firmware: introduce devices_cache/uncache_firmwares Borislav Petkov
2012-07-26 15:36 ` Ming Lei
2012-07-24 17:00 ` [RFC PATCH 12/13] driver core: firmware loader: use small timeout for cache device firmware Ming Lei
2012-07-26 12:36 ` Borislav Petkov
2012-07-26 15:48 ` Ming Lei
2012-07-26 17:54 ` Borislav Petkov
2012-07-27 1:54 ` Ming Lei
2012-07-27 10:35 ` Borislav Petkov
2012-07-28 13:58 ` Ming Lei
2012-07-24 17:00 ` [RFC PATCH 13/13] driver core: firmware loader: cache devices firmware during suspend/resume cycle Ming Lei
2012-07-26 12:43 ` Borislav Petkov
2012-07-26 15:49 ` Ming Lei
2012-07-24 17:08 ` [RFC PATCH 00/13] firmware loader: introduce cache/uncache firmware Ming Lei
2012-07-24 17:16 ` Linus Torvalds
2012-07-24 17:47 ` Ming Lei
2012-07-24 17:53 ` Linus Torvalds
2012-07-24 17:54 ` Linus Torvalds
2012-07-25 12:35 ` Ming Lei
2012-07-25 12:43 ` Oliver Neukum
2012-07-25 12:50 ` Ming Lei
2012-07-25 12:59 ` Ming Lei
2012-07-25 17:23 ` Linus Torvalds
2012-07-25 19:02 ` Rafael J. Wysocki
2012-07-26 2:29 ` Ming Lei
Reply instructions:
You may reply publicly to this message via plain-text email
using any one of the following methods:
* Save the following mbox file, import it into your mail client,
and reply-to-all from there: mbox
Avoid top-posting and favor interleaved quoting:
https://en.wikipedia.org/wiki/Posting_style#Interleaved_style
* Reply using the --to, --cc, and --in-reply-to
switches of git-send-email(1):
git send-email \
--in-reply-to=1343149213-10160-12-git-send-email-ming.lei@canonical.com \
--to=ming.lei@canonical.com \
--cc=borislav.petkov@amd.com \
--cc=gregkh@linuxfoundation.org \
--cc=linux-kernel@vger.kernel.org \
--cc=rjw@sisk.pl \
--cc=torvalds@linux-foundation.org \
/path/to/YOUR_REPLY
https://kernel.org/pub/software/scm/git/docs/git-send-email.html
* If your mail client supports setting the In-Reply-To header
via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line
before the message body.
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for NNTP newsgroup(s).