parallel suspend/resume

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

 



Hi,

I spent some time working with the parallel suspend/resume idea, even
wrote a simple patch (attached), but that obviously does not scale to
large number of devices.

What would be a better approach? 

diff --git a/drivers/base/power/main.c b/drivers/base/power/main.c
index 05dc876..45f5851 100644
--- a/drivers/base/power/main.c
+++ b/drivers/base/power/main.c
@@ -62,6 +62,8 @@ int device_pm_add(struct device * dev)
 	down(&dpm_list_sem);
 	list_add_tail(&dev->power.entry, &dpm_active);
 	device_pm_set_parent(dev, dev->parent);
+	init_waitqueue_head(&dev->power.powered_on);
+	INIT_LIST_HEAD(&dev->power.entry_sus);
 	if ((error = dpm_sysfs_add(dev)))
 		list_del(&dev->power.entry);
 	up(&dpm_list_sem);
diff --git a/drivers/base/power/resume.c b/drivers/base/power/resume.c
index a2c6418..a3de7d6 100644
--- a/drivers/base/power/resume.c
+++ b/drivers/base/power/resume.c
@@ -9,10 +9,13 @@
  */
 
 #include <linux/device.h>
+#include <linux/sched.h>
+#include <linux/kthread.h>
 #include <linux/resume-trace.h>
 #include "../base.h"
 #include "power.h"
 
+LIST_HEAD(suspend_threads);
 
 /**
  *	resume_device - Restore state for one device.
@@ -40,24 +43,61 @@ int resume_device(struct device * dev)
 	if (dev->bus && dev->bus->resume) {
 		dev_dbg(dev,"resuming\n");
 		error = dev->bus->resume(dev);
+		dev_dbg(dev,"finish\n");
 	}
 
 	if (!error && dev->type && dev->type->resume) {
 		dev_dbg(dev,"resuming\n");
 		error = dev->type->resume(dev);
+		dev_dbg(dev,"finish\n");
 	}
 
 	if (!error && dev->class && dev->class->resume) {
 		dev_dbg(dev,"class resume\n");
 		error = dev->class->resume(dev);
+		dev_dbg(dev,"finish\n");
 	}
 
+	dev->power.power_state = PMSG_ON;
+
 	up(&dev->sem);
 
 	TRACE_RESUME(error);
+
 	return error;
 }
 
+static int resume_device_work(void *data)
+{
+	int error;
+	struct device *dev = (struct device *)data;
+
+	if (dev->power.pm_parent
+			&& dev->power.pm_parent->power.power_state.event) {
+		dev_err(dev, "PM: resume from %d, parent %s still %d\n",
+			dev->power.power_state.event,
+			dev->power.pm_parent->bus_id,
+			dev->power.pm_parent->power.power_state.event);
+		wait_event_interruptible(dev->power.pm_parent->power.powered_on,
+					!dev->power.pm_parent->power.power_state.event);
+	}
+
+	error = resume_device(dev);
+	if (!error) 
+		wake_up_all(&dev->power.powered_on);
+
+	put_device(dev);
+	return error;
+}
+
+int span_resume_thread(struct device *dev)
+{
+	dev->power.resume_thread = kthread_run(resume_device_work, dev,
+				"resume-%s", dev_driver_string(dev));
+	list_add(&dev->power.entry_sus, &suspend_threads);
+	return 0;
+}
+
 
 static int resume_device_early(struct device * dev)
 {
@@ -80,6 +120,8 @@ static int resume_device_early(struct de
  */
 void dpm_resume(void)
 {
+	struct dev_pm_info *dev;
+
 	down(&dpm_list_sem);
 	while(!list_empty(&dpm_off)) {
 		struct list_head * entry = dpm_off.next;
@@ -90,11 +132,15 @@ void dpm_resume(void)
 
 		up(&dpm_list_sem);
 		if (!dev->power.prev_state.event)
-			resume_device(dev);
+			span_resume_thread(dev);
 		down(&dpm_list_sem);
-		put_device(dev);
 	}
 	up(&dpm_list_sem);
+
+	list_for_each_entry(dev, &suspend_threads, entry_sus)
+		wait_event_interruptible(dev->powered_on,
+					!dev->power_state.event);
+	INIT_LIST_HEAD(&suspend_threads);
 }
 
 
diff --git a/drivers/base/power/suspend.c b/drivers/base/power/suspend.c
index 42d2b86..3f6e9b0 100644
--- a/drivers/base/power/suspend.c
+++ b/drivers/base/power/suspend.c
@@ -169,8 +169,10 @@ int device_suspend(pm_message_t state)
 		/* Check if the device got removed */
 		if (!list_empty(&dev->power.entry)) {
 			/* Move it to the dpm_off list */
-			if (!error)
+			if (!error) {
+				dev->power.power_state = PMSG_SUSPEND;
 				list_move(&dev->power.entry, &dpm_off);
+			}
 		}
 		if (error)
 			printk(KERN_ERR "Could not suspend device %s: "
diff --git a/include/linux/pm.h b/include/linux/pm.h
index b2c4fde..4bfa8da 100644
--- a/include/linux/pm.h
+++ b/include/linux/pm.h
@@ -24,6 +24,7 @@ #define _LINUX_PM_H
 #ifdef __KERNEL__
 
 #include <linux/list.h>
+#include <linux/wait.h>
 #include <asm/atomic.h>
 
 /*
@@ -271,6 +272,9 @@ #ifdef	CONFIG_PM
 	void			* saved_state;
 	struct device		* pm_parent;
 	struct list_head	entry;
+	struct task_struct 	* resume_thread;
+	wait_queue_head_t 	powered_on;
+	struct list_head entry_sus;
 #endif
 };
 


_______________________________________________
linux-pm mailing list
linux-pm@xxxxxxxxxxxxxxxxxxxxxxxxxx
https://lists.linux-foundation.org/mailman/listinfo/linux-pm

[Index of Archives]     [Linux ACPI]     [Netdev]     [Ethernet Bridging]     [Linux Wireless]     [CPU Freq]     [Kernel Newbies]     [Fedora Kernel]     [Security]     [Linux for Hams]     [Netfilter]     [Bugtraq]     [Yosemite News]     [MIPS Linux]     [ARM Linux]     [Linux RAID]     [Linux Admin]     [Samba]

  Powered by Linux