msm: pil: Synchronize request_firmware() with suspend
Calling request_firmware() during suspend/resume operations is
buggy and error prone. Therefore a check exists in
request_firmware() to see if a suspend/resume operation is in
progress. In testing we've seen that a wakeup interrupt can come
in during suspend and trigger subsystem restart to call
pil_force_boot() before userspace has fully thawed. Since this is
not allowed the request_firmware() call from pil_force_boot()
fails with -EBUSY and the processor is never rebooted.
Register for PM notifiers to avoid calling request_firmware()
when suspend/resume is in progress. Within the notifier
synchronize any request firmware calls with suspend using a
rwsem. In the case where pil is called before suspend, the
suspend notifier should block on the down_write() during
PM_SUSPEND_PREPARE. When suspend is initiated before pil any pil
operations should block on the down_read() until suspend
finishes. Doing this should fix races between suspend/resume and
request_firmware() within PIL.
Change-Id: I8dcafcac5be80ef41d00bca9b8509652ad1108cc
Signed-off-by: Stephen Boyd <sboyd@codeaurora.org>
diff --git a/arch/arm/mach-msm/peripheral-loader.c b/arch/arm/mach-msm/peripheral-loader.c
index 6d1a5f0..d0e45f7 100644
--- a/arch/arm/mach-msm/peripheral-loader.c
+++ b/arch/arm/mach-msm/peripheral-loader.c
@@ -21,6 +21,8 @@
#include <linux/memblock.h>
#include <linux/slab.h>
#include <linux/atomic.h>
+#include <linux/suspend.h>
+#include <linux/rwsem.h>
#include <asm/uaccess.h>
#include <asm/setup.h>
@@ -156,6 +158,9 @@
return (p->p_type & PT_LOAD) && !segment_is_hash(p->p_flags);
}
+/* Sychronize request_firmware() with suspend */
+static DECLARE_RWSEM(pil_pm_rwsem);
+
static int load_image(struct pil_device *pil)
{
int i, ret;
@@ -164,6 +169,7 @@
const struct elf32_phdr *phdr;
const struct firmware *fw;
+ down_read(&pil_pm_rwsem);
snprintf(fw_name, sizeof(fw_name), "%s.mdt", pil->desc->name);
ret = request_firmware(&fw, fw_name, &pil->dev);
if (ret) {
@@ -225,6 +231,7 @@
release_fw:
release_firmware(fw);
out:
+ up_read(&pil_pm_rwsem);
return ret;
}
@@ -511,11 +518,29 @@
}
EXPORT_SYMBOL(msm_pil_unregister);
+static int pil_pm_notify(struct notifier_block *b, unsigned long event, void *p)
+{
+ switch (event) {
+ case PM_SUSPEND_PREPARE:
+ down_write(&pil_pm_rwsem);
+ break;
+ case PM_POST_SUSPEND:
+ up_write(&pil_pm_rwsem);
+ break;
+ }
+ return NOTIFY_DONE;
+}
+
+static struct notifier_block pil_pm_notifier = {
+ .notifier_call = pil_pm_notify,
+};
+
static int __init msm_pil_init(void)
{
int ret = msm_pil_debugfs_init();
if (ret)
return ret;
+ register_pm_notifier(&pil_pm_notifier);
return bus_register(&pil_bus_type);
}
subsys_initcall(msm_pil_init);
@@ -523,6 +548,7 @@
static void __exit msm_pil_exit(void)
{
bus_unregister(&pil_bus_type);
+ unregister_pm_notifier(&pil_pm_notifier);
msm_pil_debugfs_exit();
}
module_exit(msm_pil_exit);