From d20c38c796134b43b46dc97faf9d09d3df437041 Mon Sep 17 00:00:00 2001 From: San Mehat Date: Thu, 13 Aug 2009 09:40:42 -0700 Subject: [PATCH] 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 --- drivers/base/power/main.c | 45 +++++++++++++++++++++++++++++++++++++++ 1 file changed, 45 insertions(+) diff --git a/drivers/base/power/main.c b/drivers/base/power/main.c index 8aa2443182d5..55b9e6bd4f1b 100644 --- a/drivers/base/power/main.c +++ b/drivers/base/power/main.c @@ -25,6 +25,7 @@ #include #include #include +#include #include "../base.h" #include "power.h" @@ -43,6 +44,9 @@ LIST_HEAD(dpm_list); static DEFINE_MUTEX(dpm_list_mtx); +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. @@ -431,6 +435,45 @@ static int device_resume(struct device *dev, pm_message_t state) return error; } +/** + * 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. @@ -689,7 +732,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, state); + dpm_drv_wdclr(dev); mutex_lock(&dpm_list_mtx); if (error) { -- 2.34.1