aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
author\\\\\\\\\\\\\\\"Mike (mwester)\\\\\\\\\\\\ <mwester@dls.net>2008-11-19 17:10:54 +0000
committerAndy Green <agreen@pads.home.warmcat.com>2008-11-19 17:10:54 +0000
commita0b21c7066eb0301eba21885c177eeb16eed0216 (patch)
tree22211e3350d753893c3fcf7f441c1f88fb385d62
parent27ef48d603cb23afd637071dbf9683155c83e58a (diff)
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>
-rw-r--r--drivers/i2c/chips/pcf50633.c6
-rw-r--r--drivers/mfd/glamo/glamo-core.c5
-rw-r--r--drivers/mfd/glamo/glamo-core.h1
-rw-r--r--include/linux/resume-dependency.h38
4 files changed, 49 insertions, 1 deletions
diff --git a/drivers/i2c/chips/pcf50633.c b/drivers/i2c/chips/pcf50633.c
index 58f85d2ba58..0a8daf40a3e 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
};
@@ -2372,6 +2373,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);
@@ -2468,6 +2471,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;
}
@@ -2600,6 +2605,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 7123080e050..0fea4f4ea0e 100644
--- a/drivers/mfd/glamo/glamo-core.c
+++ b/drivers/mfd/glamo/glamo-core.c
@@ -1255,6 +1255,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);
@@ -1262,12 +1264,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 57214de9e00..ce8d4c7e79e 100644
--- a/drivers/mfd/glamo/glamo-core.h
+++ b/drivers/mfd/glamo/glamo-core.h
@@ -31,6 +31,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/include/linux/resume-dependency.h b/include/linux/resume-dependency.h
index e0c0f33e2c3..959cadd8365 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