diff options
author | San Mehat <san@google.com> | 2009-08-13 09:40:42 -0700 |
---|---|---|
committer | Arve Hjønnevåg <arve@android.com> | 2010-07-22 15:12:09 -0700 |
commit | 25b1a437a437223e0b8b8add57c01dee773004df (patch) | |
tree | 1cddcc63f27e1e0d214b2fc4952a86c6041ce7d2 /drivers/base | |
parent | eaa768b89e855db625cffe0fc9974d3057c5f7ac (diff) |
drivers: power: Add watchdog timer to catch drivers which lockup during suspend.
Rather than hard-lock the kernel, we now BUG() when a driver takes
> 3 seconds to suspend. If the underlying platform supports panic dumps,
then the data can be collected for debug.
Signed-off-by: San Mehat <san@google.com>
Diffstat (limited to 'drivers/base')
-rw-r--r-- | drivers/base/power/main.c | 45 |
1 files changed, 45 insertions, 0 deletions
diff --git a/drivers/base/power/main.c b/drivers/base/power/main.c index 941fcb87e52..cb784a0b227 100644 --- a/drivers/base/power/main.c +++ b/drivers/base/power/main.c @@ -26,6 +26,7 @@ #include <linux/interrupt.h> #include <linux/sched.h> #include <linux/async.h> +#include <linux/timer.h> #include "../base.h" #include "power.h" @@ -45,6 +46,9 @@ LIST_HEAD(dpm_list); static DEFINE_MUTEX(dpm_list_mtx); static pm_message_t pm_transition; +static void dpm_drv_timeout(unsigned long data); +static DEFINE_TIMER(dpm_drv_wd, dpm_drv_timeout, 0, 0); + /* * Set once the preparation of devices for a PM transition has started, reset * before starting to resume devices. Protected by dpm_list_mtx. @@ -583,6 +587,45 @@ static bool is_async(struct device *dev) } /** + * dpm_drv_timeout - Driver suspend / resume watchdog handler + * @data: struct device which timed out + * + * Called when a driver has timed out suspending or resuming. + * There's not much we can do here to recover so + * BUG() out for a crash-dump + * + */ +static void dpm_drv_timeout(unsigned long data) +{ + struct device *dev = (struct device *) data; + + printk(KERN_EMERG "**** DPM device timeout: %s (%s)\n", dev_name(dev), + (dev->driver ? dev->driver->name : "no driver")); + BUG(); +} + +/** + * dpm_drv_wdset - Sets up driver suspend/resume watchdog timer. + * @dev: struct device which we're guarding. + * + */ +static void dpm_drv_wdset(struct device *dev) +{ + dpm_drv_wd.data = (unsigned long) dev; + mod_timer(&dpm_drv_wd, jiffies + (HZ * 3)); +} + +/** + * dpm_drv_wdclr - clears driver suspend/resume watchdog timer. + * @dev: struct device which we're no longer guarding. + * + */ +static void dpm_drv_wdclr(struct device *dev) +{ + del_timer_sync(&dpm_drv_wd); +} + +/** * dpm_resume - Execute "resume" callbacks for non-sysdev devices. * @state: PM transition of the system being carried out. * @@ -933,7 +976,9 @@ static int dpm_suspend(pm_message_t state) get_device(dev); mutex_unlock(&dpm_list_mtx); + dpm_drv_wdset(dev); error = device_suspend(dev); + dpm_drv_wdclr(dev); mutex_lock(&dpm_list_mtx); if (error) { |