PM: Change dpm watchdog to support async suspend

Exclude from the watchdog the time spent waiting for children that
are resumed asynchronously and time every devices, whether or not they
resumed synchronously.

Change-Id: I84209dfd5df72842e045096c906fd61e20e6d183
Signed-off-by: Benoit Goby <benoit@android.com>
diff --git a/drivers/base/power/main.c b/drivers/base/power/main.c
index 3e4d364..4b65d5c 100644
--- a/drivers/base/power/main.c
+++ b/drivers/base/power/main.c
@@ -51,11 +51,10 @@
 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);
-static struct {
+struct dpm_drv_wd_data {
 	struct device *dev;
 	struct task_struct *tsk;
-} dpm_drv_wd_data;
+};
 
 static int async_error;
 
@@ -592,8 +591,9 @@
  */
 static void dpm_drv_timeout(unsigned long data)
 {
-	struct device *dev = dpm_drv_wd_data.dev;
-	struct task_struct *tsk = dpm_drv_wd_data.tsk;
+	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"));
@@ -605,29 +605,6 @@
 }
 
 /**
- *	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.dev = dev;
-	dpm_drv_wd_data.tsk = get_current();
-	dpm_drv_wd.data = (unsigned long) &dpm_drv_wd_data;
-	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.
  *
@@ -885,8 +862,19 @@
 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 * 3;
+	timer.function = dpm_drv_timeout;
+	timer.data = (unsigned long)&data;
+	add_timer(&timer);
+
 	device_lock(dev);
 
 	if (async_error)
@@ -933,6 +921,10 @@
 
  End:
 	device_unlock(dev);
+
+	del_timer_sync(&timer);
+	destroy_timer_on_stack(&timer);
+
 	complete_all(&dev->power.completion);
 
 	if (error)
@@ -986,9 +978,7 @@
 		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) {