mirror of
git://git.openwrt.org/openwrt/openwrt.git
synced 2024-12-09 08:20:39 +00:00
256 lines
9.3 KiB
Diff
256 lines
9.3 KiB
Diff
|
From 36879992de8774cbf8686740bbda383cc6fbdcbb Mon Sep 17 00:00:00 2001
|
||
|
From: \\\\\\\"Mike (mwester)\\\\\\ <mwester@dls.net>
|
||
|
Date: Mon, 11 Aug 2008 20:16:09 +0100
|
||
|
Subject: [PATCH] always-call-resume-dependencies.patch
|
||
|
|
||
|
Attached is a patch that has greatly reduced the frequency of failures
|
||
|
to resume (due to an oops from the glamo resume handler), and the
|
||
|
dreaded "white screen after resume". I can't say that it fixes all of
|
||
|
these, although I have yet to see the white-screen since applying this
|
||
|
patch and suspending/resuming several hundred times (with the 30-second
|
||
|
suspend on the 2008.8 image and the endless stream of GSM error messages
|
||
|
generated by something in that image, it has proved to be very useful to
|
||
|
do an automated stress test!)
|
||
|
|
||
|
This patch will apply to stable, and should make stable slightly more,
|
||
|
well, "stable".
|
||
|
|
||
|
[Feel free to remove the debug messages if someone feels strongly about
|
||
|
that; I left them in because I think they might be useful in triaging
|
||
|
further crashes; I'm not at all convinced that this patch will fix all
|
||
|
the cases of resume failures.]
|
||
|
|
||
|
[[And, yes, this is ugly, really ugly.]]
|
||
|
|
||
|
[[[Oh yeah - there's still one extreme case that will result in an oops:
|
||
|
if a dependent driver is built as a module, and it is unloaded, and it
|
||
|
happened that the preceding suspend/resume was aborted, and that abort
|
||
|
happened between the dependent driver and the driver upon which it is
|
||
|
dependent, then a list entry will be left behind referencing the
|
||
|
unloaded module. There's just no good way to fix that given the way the
|
||
|
resume dependency plumbing is connected up right now, so just avoid
|
||
|
using modules for any of the drivers involved in the resume dependency
|
||
|
stuff.]]]
|
||
|
|
||
|
Regards,
|
||
|
Mike (mwester)
|
||
|
commit 905d2fc9c45f622418ce9ef4e67c23453aab7571
|
||
|
Author: Mike Westerhof <mwester@dls.net>
|
||
|
Date: Mon Aug 11 11:11:25 2008 -0500
|
||
|
|
||
|
always-call-resume-dependencies.patch
|
||
|
|
||
|
Ensure that a dependent resume handler is always executed,
|
||
|
even if the resume handler for driver upon which it is
|
||
|
dependent never suspends (and therefore never resumes either).
|
||
|
Also make sure that we do not end up with duplicate
|
||
|
dependencies registered, something that can happen if the
|
||
|
suspend is aborted due to driver failure or an early resume
|
||
|
(such as occurs when the GSM interrupts during suspend).
|
||
|
|
||
|
Signed-off-by: Mike Westerhof <mwester@dls.net>
|
||
|
---
|
||
|
drivers/i2c/chips/pcf50633.c | 6 +++++
|
||
|
drivers/mfd/glamo/glamo-core.c | 5 ++++
|
||
|
drivers/mfd/glamo/glamo-core.h | 1 +
|
||
|
drivers/serial/s3c2410.c | 7 ++++++
|
||
|
include/linux/resume-dependency.h | 38 ++++++++++++++++++++++++++++++++++++-
|
||
|
5 files changed, 56 insertions(+), 1 deletions(-)
|
||
|
|
||
|
diff --git a/drivers/i2c/chips/pcf50633.c b/drivers/i2c/chips/pcf50633.c
|
||
|
index 98c254c..b90ea8c 100644
|
||
|
--- a/drivers/i2c/chips/pcf50633.c
|
||
|
+++ b/drivers/i2c/chips/pcf50633.c
|
||
|
@@ -183,6 +183,7 @@ struct pcf50633_data {
|
||
|
} standby_regs;
|
||
|
|
||
|
struct resume_dependency resume_dependency;
|
||
|
+ int is_suspended;
|
||
|
|
||
|
#endif
|
||
|
};
|
||
|
@@ -2366,6 +2367,8 @@ void pcf50633_register_resume_dependency(struct pcf50633_data *pcf,
|
||
|
struct resume_dependency *dep)
|
||
|
{
|
||
|
register_resume_dependency(&pcf->resume_dependency, dep);
|
||
|
+ if (pcf->is_suspended)
|
||
|
+ activate_all_resume_dependencies(&pcf->resume_dependency);
|
||
|
}
|
||
|
EXPORT_SYMBOL_GPL(pcf50633_register_resume_dependency);
|
||
|
|
||
|
@@ -2462,6 +2465,8 @@ static int pcf50633_suspend(struct device *dev, pm_message_t state)
|
||
|
|
||
|
mutex_unlock(&pcf->lock);
|
||
|
|
||
|
+ pcf->is_suspended = 1;
|
||
|
+ activate_all_resume_dependencies(&pcf->resume_dependency);
|
||
|
return 0;
|
||
|
}
|
||
|
|
||
|
@@ -2590,6 +2595,7 @@ static int pcf50633_resume(struct device *dev)
|
||
|
get_device(&pcf->client.dev);
|
||
|
pcf50633_work(&pcf->work);
|
||
|
|
||
|
+ pcf->is_suspended = 0;
|
||
|
callback_all_resume_dependencies(&pcf->resume_dependency);
|
||
|
|
||
|
return 0;
|
||
|
diff --git a/drivers/mfd/glamo/glamo-core.c b/drivers/mfd/glamo/glamo-core.c
|
||
|
index c094a8c..e310382 100644
|
||
|
--- a/drivers/mfd/glamo/glamo-core.c
|
||
|
+++ b/drivers/mfd/glamo/glamo-core.c
|
||
|
@@ -1248,6 +1248,8 @@ void glamo_register_resume_dependency(struct resume_dependency *
|
||
|
{
|
||
|
register_resume_dependency(&glamo_handle->resume_dependency,
|
||
|
resume_dependency);
|
||
|
+ if (glamo_handle->is_suspended)
|
||
|
+ activate_all_resume_dependencies(&glamo_handle->resume_dependency);
|
||
|
}
|
||
|
EXPORT_SYMBOL_GPL(glamo_register_resume_dependency);
|
||
|
|
||
|
@@ -1255,12 +1257,15 @@ EXPORT_SYMBOL_GPL(glamo_register_resume_dependency);
|
||
|
static int glamo_suspend(struct platform_device *pdev, pm_message_t state)
|
||
|
{
|
||
|
glamo_power(glamo_handle, GLAMO_POWER_SUSPEND);
|
||
|
+ glamo_handle->is_suspended = 1;
|
||
|
+ activate_all_resume_dependencies(&glamo_handle->resume_dependency);
|
||
|
return 0;
|
||
|
}
|
||
|
|
||
|
static int glamo_resume(struct platform_device *pdev)
|
||
|
{
|
||
|
glamo_power(glamo_handle, GLAMO_POWER_ON);
|
||
|
+ glamo_handle->is_suspended = 0;
|
||
|
callback_all_resume_dependencies(&glamo_handle->resume_dependency);
|
||
|
|
||
|
return 0;
|
||
|
diff --git a/drivers/mfd/glamo/glamo-core.h b/drivers/mfd/glamo/glamo-core.h
|
||
|
index d3f4309..ac5eacf 100644
|
||
|
--- a/drivers/mfd/glamo/glamo-core.h
|
||
|
+++ b/drivers/mfd/glamo/glamo-core.h
|
||
|
@@ -32,6 +32,7 @@ struct glamo_core {
|
||
|
struct resume_dependency resume_dependency;
|
||
|
u32 engine_enabled_bitfield;
|
||
|
u32 engine_enabled_bitfield_suspend;
|
||
|
+ int is_suspended;
|
||
|
};
|
||
|
|
||
|
struct glamo_script {
|
||
|
diff --git a/drivers/serial/s3c2410.c b/drivers/serial/s3c2410.c
|
||
|
index 2a388cf..3b1c07b 100644
|
||
|
--- a/drivers/serial/s3c2410.c
|
||
|
+++ b/drivers/serial/s3c2410.c
|
||
|
@@ -116,6 +116,7 @@ struct s3c24xx_uart_port {
|
||
|
struct uart_port port;
|
||
|
|
||
|
struct resume_dependency resume_dependency;
|
||
|
+ int is_suspended;
|
||
|
};
|
||
|
|
||
|
|
||
|
@@ -1188,10 +1189,13 @@ static int s3c24xx_serial_remove(struct platform_device *dev)
|
||
|
static int s3c24xx_serial_suspend(struct platform_device *dev, pm_message_t state)
|
||
|
{
|
||
|
struct uart_port *port = s3c24xx_dev_to_port(&dev->dev);
|
||
|
+ struct s3c24xx_uart_port *ourport = to_ourport(port);
|
||
|
|
||
|
if (port)
|
||
|
uart_suspend_port(&s3c24xx_uart_drv, port);
|
||
|
|
||
|
+ activate_all_resume_dependencies(&ourport->resume_dependency);
|
||
|
+ ourport->is_suspended = 1;
|
||
|
return 0;
|
||
|
}
|
||
|
|
||
|
@@ -1202,6 +1206,8 @@ void s3c24xx_serial_register_resume_dependency(struct resume_dependency *
|
||
|
|
||
|
register_resume_dependency(&ourport->resume_dependency,
|
||
|
resume_dependency);
|
||
|
+ if (ourport->is_suspended)
|
||
|
+ activate_all_resume_dependencies(&ourport->resume_dependency);
|
||
|
}
|
||
|
EXPORT_SYMBOL(s3c24xx_serial_register_resume_dependency);
|
||
|
|
||
|
@@ -1218,6 +1224,7 @@ static int s3c24xx_serial_resume(struct platform_device *dev)
|
||
|
uart_resume_port(&s3c24xx_uart_drv, port);
|
||
|
}
|
||
|
|
||
|
+ ourport->is_suspended = 0;
|
||
|
callback_all_resume_dependencies(&ourport->resume_dependency);
|
||
|
|
||
|
return 0;
|
||
|
diff --git a/include/linux/resume-dependency.h b/include/linux/resume-dependency.h
|
||
|
index e0c0f33..959cadd 100644
|
||
|
--- a/include/linux/resume-dependency.h
|
||
|
+++ b/include/linux/resume-dependency.h
|
||
|
@@ -38,6 +38,7 @@ struct resume_dependency {
|
||
|
*/
|
||
|
|
||
|
#define init_resume_dependency_list(_head) \
|
||
|
+ printk(KERN_ERR "##### init_resume_dependency_list(head=%p)\n", (_head)); \
|
||
|
INIT_LIST_HEAD(&(_head)->list);
|
||
|
|
||
|
|
||
|
@@ -48,7 +49,18 @@ struct resume_dependency {
|
||
|
*/
|
||
|
|
||
|
#define register_resume_dependency(_head, _dep) { \
|
||
|
- (_dep)->called_flag = 0; \
|
||
|
+ struct list_head *_pos, *_q; \
|
||
|
+ struct resume_dependency *_d; \
|
||
|
+\
|
||
|
+ printk(KERN_ERR "##### register_resume_dependency(head=%p, dep=%p)\n", (_head), (_dep)); \
|
||
|
+ (_dep)->called_flag = 1; \
|
||
|
+ list_for_each_safe(_pos, _q, &((_head)->list)) { \
|
||
|
+ _d = list_entry(_pos, struct resume_dependency, list); \
|
||
|
+ if (_d == (_dep)) { \
|
||
|
+ list_del(_pos); \
|
||
|
+ printk(KERN_ERR "##### duplicate dependency removed first\n"); \
|
||
|
+ } \
|
||
|
+ } \
|
||
|
list_add(&(_dep)->list, &(_head)->list); \
|
||
|
}
|
||
|
|
||
|
@@ -61,14 +73,38 @@ struct resume_dependency {
|
||
|
struct list_head *_pos, *_q; \
|
||
|
struct resume_dependency *_dep; \
|
||
|
\
|
||
|
+ printk(KERN_ERR "##### callback_all_resume_dependencies(head=%p)\n", (_head)); \
|
||
|
list_for_each_safe(_pos, _q, &((_head)->list)) { \
|
||
|
_dep = list_entry(_pos, struct resume_dependency, list); \
|
||
|
+ printk(KERN_ERR "##### callback list entry (head=%p, dep=%p)\n", (_head), (_dep)); \
|
||
|
_dep->called_flag = 1; \
|
||
|
+ printk(KERN_ERR "##### callback=%p(context=%p))\n", (_dep->callback),(_dep->context)); \
|
||
|
(_dep->callback)(_dep->context); \
|
||
|
list_del(_pos); \
|
||
|
} \
|
||
|
}
|
||
|
|
||
|
+/* When a dependency is added, it is not actually active; the dependent resume
|
||
|
+ * handler will function as normal. The dependency is activated by the suspend
|
||
|
+ * handler for the driver that will be doing the callbacks. This ensures that
|
||
|
+ * if the suspend is aborted for any reason (error, driver busy, etc), that all
|
||
|
+ * suspended drivers will resume, even if the driver upon which they are dependent
|
||
|
+ * did not suspend, and hence will not resume, and thus would be unable to perform
|
||
|
+ * the callbacks.
|
||
|
+ */
|
||
|
+
|
||
|
+#define activate_all_resume_dependencies(_head) { \
|
||
|
+ struct list_head *_pos, *_q; \
|
||
|
+ struct resume_dependency *_dep; \
|
||
|
+\
|
||
|
+ printk(KERN_ERR "##### activate_all_resume_dependencies(head=%p)\n", (_head)); \
|
||
|
+ list_for_each_safe(_pos, _q, &((_head)->list)) { \
|
||
|
+ _dep = list_entry(_pos, struct resume_dependency, list); \
|
||
|
+ printk(KERN_ERR "##### activating callback list entry (head=%p, dep=%p)\n", (_head), (_dep)); \
|
||
|
+ _dep->called_flag = 0; \
|
||
|
+ } \
|
||
|
+}
|
||
|
+
|
||
|
/* if your resume action is dependent on multiple drivers being resumed already,
|
||
|
* register the same callback with each driver you are dependent on, and check
|
||
|
* .called_flag for all of the struct resume_dependency. When they are all 1
|
||
|
--
|
||
|
1.5.6.5
|
||
|
|