diff options
Diffstat (limited to 'drivers/base/power/main.c')
-rw-r--r-- | drivers/base/power/main.c | 50 |
1 files changed, 49 insertions, 1 deletions
diff --git a/drivers/base/power/main.c b/drivers/base/power/main.c index f1e1a5b8fb04..b4e94fce22d1 100644 --- a/drivers/base/power/main.c +++ b/drivers/base/power/main.c @@ -27,6 +27,7 @@ #include <linux/sched.h> #include <linux/async.h> #include <linux/suspend.h> +#include <linux/timer.h> #include "../base.h" #include "power.h" @@ -49,6 +50,12 @@ LIST_HEAD(dpm_noirq_list); static DEFINE_MUTEX(dpm_list_mtx); static pm_message_t pm_transition; +static void dpm_drv_timeout(unsigned long data); +struct dpm_drv_wd_data { + struct device *dev; + struct task_struct *tsk; +}; + static int async_error; /** @@ -621,6 +628,30 @@ 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 dpm_drv_wd_data *wd_data = (void *)data; + struct device *dev = wd_data->dev; + struct task_struct *tsk = wd_data->tsk; + + printk(KERN_EMERG "**** DPM device timeout: %s (%s)\n", dev_name(dev), + (dev->driver ? dev->driver->name : "no driver")); + + printk(KERN_EMERG "dpm suspend stack:\n"); + show_stack(tsk, NULL); + + BUG(); +} + +/** * dpm_resume - Execute "resume" callbacks for non-sysdev devices. * @state: PM transition of the system being carried out. * @@ -839,8 +870,10 @@ int dpm_suspend_noirq(pm_message_t state) put_device(dev); } mutex_unlock(&dpm_list_mtx); - if (error) + if (error) { + resume_irqs(true); dpm_resume_noirq(resume_event(state)); + } else dpm_show_time(starttime, state, "late"); return error; @@ -878,8 +911,19 @@ static int legacy_suspend(struct device *dev, pm_message_t state, static int __device_suspend(struct device *dev, pm_message_t state, bool async) { int error = 0; + struct timer_list timer; + struct dpm_drv_wd_data data; dpm_wait_for_children(dev, async); + + data.dev = dev; + data.tsk = get_current(); + init_timer_on_stack(&timer); + timer.expires = jiffies + HZ * 12; + timer.function = dpm_drv_timeout; + timer.data = (unsigned long)&data; + add_timer(&timer); + device_lock(dev); if (async_error) @@ -929,6 +973,10 @@ static int __device_suspend(struct device *dev, pm_message_t state, bool async) Unlock: device_unlock(dev); + + del_timer_sync(&timer); + destroy_timer_on_stack(&timer); + complete_all(&dev->power.completion); if (error) |