From: Greg KH <greg@kroah.com>
To: linux-kernel@vger.kernel.org
Cc: Linus Torvalds <torvalds@osdl.org>, Greg Kroah-Hartman <gregkh@suse.de>
Subject: [PATCH 9/47] Suspend infrastructure cleanup and extension
Date: Mon, 25 Sep 2006 22:37:29 -0700 [thread overview]
Message-ID: <1159249111668-git-send-email-greg@kroah.com> (raw)
In-Reply-To: <11592491082990-git-send-email-greg@kroah.com>
From: Linus Torvalds <torvalds@osdl.org>
Allow devices to participate in the suspend process more intimately,
in particular, allow the final phase (with interrupts disabled) to
also be open to normal devices, not just system devices.
Also, allow classes to participate in device suspend.
Signed-off-by: Linus Torvalds <torvalds@osdl.org>
Signed-off-by: Greg Kroah-Hartman <gregkh@suse.de>
---
drivers/base/power/resume.c | 28 ++++++++--
drivers/base/power/suspend.c | 122 ++++++++++++++++++++++++++++++++----------
include/linux/device.h | 11 +++-
include/linux/pm.h | 1
kernel/power/main.c | 4 +
5 files changed, 130 insertions(+), 36 deletions(-)
diff --git a/drivers/base/power/resume.c b/drivers/base/power/resume.c
index 826093e..48e3d49 100644
--- a/drivers/base/power/resume.c
+++ b/drivers/base/power/resume.c
@@ -38,13 +38,35 @@ int resume_device(struct device * dev)
dev_dbg(dev,"resuming\n");
error = dev->bus->resume(dev);
}
+ if (dev->class && dev->class->resume) {
+ dev_dbg(dev,"class resume\n");
+ error = dev->class->resume(dev);
+ }
up(&dev->sem);
TRACE_RESUME(error);
return error;
}
+static int resume_device_early(struct device * dev)
+{
+ int error = 0;
+ TRACE_DEVICE(dev);
+ TRACE_RESUME(0);
+ if (dev->bus && dev->bus->resume_early) {
+ dev_dbg(dev,"EARLY resume\n");
+ error = dev->bus->resume_early(dev);
+ }
+ TRACE_RESUME(error);
+ return error;
+}
+
+/*
+ * Resume the devices that have either not gone through
+ * the late suspend, or that did go through it but also
+ * went through the early resume
+ */
void dpm_resume(void)
{
down(&dpm_list_sem);
@@ -99,10 +121,8 @@ void dpm_power_up(void)
struct list_head * entry = dpm_off_irq.next;
struct device * dev = to_device(entry);
- get_device(dev);
- list_move_tail(entry, &dpm_active);
- resume_device(dev);
- put_device(dev);
+ list_move_tail(entry, &dpm_off);
+ resume_device_early(dev);
}
}
diff --git a/drivers/base/power/suspend.c b/drivers/base/power/suspend.c
index 69509e0..10e8032 100644
--- a/drivers/base/power/suspend.c
+++ b/drivers/base/power/suspend.c
@@ -65,7 +65,19 @@ int suspend_device(struct device * dev,
dev->power.prev_state = dev->power.power_state;
- if (dev->bus && dev->bus->suspend && !dev->power.power_state.event) {
+ if (dev->class && dev->class->suspend && !dev->power.power_state.event) {
+ dev_dbg(dev, "class %s%s\n",
+ suspend_verb(state.event),
+ ((state.event == PM_EVENT_SUSPEND)
+ && device_may_wakeup(dev))
+ ? ", may wakeup"
+ : ""
+ );
+ error = dev->class->suspend(dev, state);
+ suspend_report_result(dev->class->suspend, error);
+ }
+
+ if (!error && dev->bus && dev->bus->suspend && !dev->power.power_state.event) {
dev_dbg(dev, "%s%s\n",
suspend_verb(state.event),
((state.event == PM_EVENT_SUSPEND)
@@ -81,15 +93,74 @@ int suspend_device(struct device * dev,
}
+/*
+ * This is called with interrupts off, only a single CPU
+ * running. We can't do down() on a semaphore (and we don't
+ * need the protection)
+ */
+static int suspend_device_late(struct device *dev, pm_message_t state)
+{
+ int error = 0;
+
+ if (dev->power.power_state.event) {
+ dev_dbg(dev, "PM: suspend_late %d-->%d\n",
+ dev->power.power_state.event, state.event);
+ }
+
+ if (dev->bus && dev->bus->suspend_late && !dev->power.power_state.event) {
+ dev_dbg(dev, "LATE %s%s\n",
+ suspend_verb(state.event),
+ ((state.event == PM_EVENT_SUSPEND)
+ && device_may_wakeup(dev))
+ ? ", may wakeup"
+ : ""
+ );
+ error = dev->bus->suspend_late(dev, state);
+ suspend_report_result(dev->bus->suspend_late, error);
+ }
+ return error;
+}
+
+/**
+ * device_prepare_suspend - save state and prepare to suspend
+ *
+ * NOTE! Devices cannot detach at this point - not only do we
+ * hold the device list semaphores over the whole prepare, but
+ * the whole point is to do non-invasive preparatory work, not
+ * the actual suspend.
+ */
+int device_prepare_suspend(pm_message_t state)
+{
+ int error = 0;
+ struct device * dev;
+
+ down(&dpm_sem);
+ down(&dpm_list_sem);
+ list_for_each_entry_reverse(dev, &dpm_active, power.entry) {
+ if (!dev->bus || !dev->bus->suspend_prepare)
+ continue;
+ error = dev->bus->suspend_prepare(dev, state);
+ if (error)
+ break;
+ }
+ up(&dpm_list_sem);
+ up(&dpm_sem);
+ return error;
+}
+
/**
* device_suspend - Save state and stop all devices in system.
* @state: Power state to put each device in.
*
* Walk the dpm_active list, call ->suspend() for each device, and move
- * it to dpm_off.
- * Check the return value for each. If it returns 0, then we move the
- * the device to the dpm_off list. If it returns -EAGAIN, we move it to
- * the dpm_off_irq list. If we get a different error, try and back out.
+ * it to the dpm_off list.
+ *
+ * (For historical reasons, if it returns -EAGAIN, that used to mean
+ * that the device would be called again with interrupts disabled.
+ * These days, we use the "suspend_late()" callback for that, so we
+ * print a warning and consider it an error).
+ *
+ * If we get a different error, try and back out.
*
* If we hit a failure with any of the devices, call device_resume()
* above to bring the suspended devices back to life.
@@ -115,39 +186,27 @@ 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 or dpm_off_irq list */
+ /* Move it to the dpm_off list */
if (!error)
list_move(&dev->power.entry, &dpm_off);
- else if (error == -EAGAIN) {
- list_move(&dev->power.entry, &dpm_off_irq);
- error = 0;
- }
}
if (error)
printk(KERN_ERR "Could not suspend device %s: "
- "error %d\n", kobject_name(&dev->kobj), error);
+ "error %d%s\n",
+ kobject_name(&dev->kobj), error,
+ error == -EAGAIN ? " (please convert to suspend_late)" : "");
put_device(dev);
}
up(&dpm_list_sem);
- if (error) {
- /* we failed... before resuming, bring back devices from
- * dpm_off_irq list back to main dpm_off list, we do want
- * to call resume() on them, in case they partially suspended
- * despite returning -EAGAIN
- */
- while (!list_empty(&dpm_off_irq)) {
- struct list_head * entry = dpm_off_irq.next;
- list_move(entry, &dpm_off);
- }
+ if (error)
dpm_resume();
- }
+
up(&dpm_sem);
return error;
}
EXPORT_SYMBOL_GPL(device_suspend);
-
/**
* device_power_down - Shut down special devices.
* @state: Power state to enter.
@@ -162,14 +221,17 @@ int device_power_down(pm_message_t state
int error = 0;
struct device * dev;
- list_for_each_entry_reverse(dev, &dpm_off_irq, power.entry) {
- if ((error = suspend_device(dev, state)))
- break;
+ while (!list_empty(&dpm_off)) {
+ struct list_head * entry = dpm_off.prev;
+
+ dev = to_device(entry);
+ error = suspend_device_late(dev, state);
+ if (error)
+ goto Error;
+ list_move(&dev->power.entry, &dpm_off_irq);
}
- if (error)
- goto Error;
- if ((error = sysdev_suspend(state)))
- goto Error;
+
+ error = sysdev_suspend(state);
Done:
return error;
Error:
diff --git a/include/linux/device.h b/include/linux/device.h
index 8a648cd..b40be6f 100644
--- a/include/linux/device.h
+++ b/include/linux/device.h
@@ -51,8 +51,12 @@ struct bus_type {
int (*probe)(struct device * dev);
int (*remove)(struct device * dev);
void (*shutdown)(struct device * dev);
- int (*suspend)(struct device * dev, pm_message_t state);
- int (*resume)(struct device * dev);
+
+ int (*suspend_prepare)(struct device * dev, pm_message_t state);
+ int (*suspend)(struct device * dev, pm_message_t state);
+ int (*suspend_late)(struct device * dev, pm_message_t state);
+ int (*resume_early)(struct device * dev);
+ int (*resume)(struct device * dev);
};
extern int bus_register(struct bus_type * bus);
@@ -154,6 +158,9 @@ struct class {
void (*release)(struct class_device *dev);
void (*class_release)(struct class *class);
+
+ int (*suspend)(struct device *, pm_message_t state);
+ int (*resume)(struct device *);
};
extern int class_register(struct class *);
diff --git a/include/linux/pm.h b/include/linux/pm.h
index 658c1b9..096fb6f 100644
--- a/include/linux/pm.h
+++ b/include/linux/pm.h
@@ -190,6 +190,7 @@ #ifdef CONFIG_PM
extern suspend_disk_method_t pm_disk_mode;
extern int device_suspend(pm_message_t state);
+extern int device_prepare_suspend(pm_message_t state);
#define device_set_wakeup_enable(dev,val) \
((dev)->power.should_wakeup = !!(val))
diff --git a/kernel/power/main.c b/kernel/power/main.c
index 6d295c7..0c3ed6a 100644
--- a/kernel/power/main.c
+++ b/kernel/power/main.c
@@ -57,6 +57,10 @@ static int suspend_prepare(suspend_state
if (!pm_ops || !pm_ops->enter)
return -EPERM;
+ error = device_prepare_suspend(PMSG_SUSPEND);
+ if (error)
+ return error;
+
pm_prepare_console();
disable_nonboot_cpus();
--
1.4.2.1
next prev parent reply other threads:[~2006-09-26 5:38 UTC|newest]
Thread overview: 72+ messages / expand[flat|nested] mbox.gz Atom feed top
2006-09-26 5:37 [GIT PATCH] Driver Core patches for 2.6.18 Greg KH
2006-09-26 5:37 ` [PATCH 1/47] Documentation/ABI: devfs is not obsolete, but removed! Greg KH
2006-09-26 5:37 ` [PATCH 2/47] deprecate PHYSDEV* keys Greg KH
2006-09-26 5:37 ` [PATCH 3/47] class_device_create(): make fmt argument 'const char *' Greg KH
2006-09-26 5:37 ` [PATCH 4/47] device_create(): " Greg KH
2006-09-26 5:37 ` [PATCH 5/47] Driver core: add const to class_create Greg KH
2006-09-26 5:37 ` [PATCH 6/47] sysfs: Make poll behaviour consistent Greg KH
2006-09-26 5:37 ` [PATCH 7/47] Debugfs: kernel-doc fixes for debugfs Greg KH
2006-09-26 5:37 ` [PATCH 8/47] SYSFS: allow sysfs_create_link to create symlinks in the root of sysfs Greg KH
2006-09-26 5:37 ` Greg KH [this message]
2006-09-26 5:37 ` [PATCH 10/47] Suspend changes for PCI core Greg KH
2006-09-26 5:37 ` [PATCH 11/47] make suspend quieter Greg KH
2006-09-26 5:37 ` [PATCH 12/47] fix broken/dubious driver suspend() methods Greg KH
2006-09-26 5:37 ` [PATCH 13/47] PM: define PM_EVENT_PRETHAW Greg KH
2006-09-26 5:37 ` [PATCH 14/47] PM: PCI and IDE handle PM_EVENT_PRETHAW Greg KH
2006-09-26 5:37 ` [PATCH 15/47] PM: video drivers and PM_EVENT_PRETHAW Greg KH
2006-09-26 5:37 ` [PATCH 16/47] PM: USB HCDs use PM_EVENT_PRETHAW Greg KH
2006-09-26 5:37 ` [PATCH 17/47] PM: issue PM_EVENT_PRETHAW Greg KH
2006-09-26 5:37 ` [PATCH 18/47] updated Documentation/power/devices.txt Greg KH
2006-09-26 5:37 ` [PATCH 19/47] PM: update docs for writing .../power/state Greg KH
2006-09-26 5:37 ` [PATCH 20/47] PM: add kconfig option for deprecated .../power/state files Greg KH
2006-09-26 5:37 ` [PATCH 21/47] PM: schedule /sys/devices/.../power/state for removal Greg KH
2006-09-26 5:37 ` [PATCH 22/47] PM: no suspend_prepare() phase Greg KH
2006-09-26 5:37 ` [PATCH 23/47] PM: add /sys/power documentation to Documentation/ABI Greg KH
2006-09-26 5:37 ` [PATCH 24/47] PM: device_suspend/resume may sleep Greg KH
2006-09-26 5:37 ` [PATCH 25/47] PM: platform_bus and late_suspend/early_resume Greg KH
2006-09-26 5:37 ` [PATCH 26/47] Driver core: add groups support to struct device Greg KH
2006-09-26 5:37 ` [PATCH 27/47] Driver core: allow devices in classes to have no parent Greg KH
2006-09-26 5:37 ` [PATCH 28/47] Driver core: add ability for classes to handle devices properly Greg KH
2006-09-26 5:37 ` [PATCH 29/47] Driver core: add device_rename function Greg KH
2006-09-26 5:37 ` [PATCH 30/47] Driver core: create devices/virtual/ tree Greg KH
2006-09-26 5:37 ` [PATCH 31/47] Class: add support for class interfaces for devices Greg KH
2006-09-26 5:37 ` [PATCH 32/47] Driver core: add ability for devices to create and remove bin files Greg KH
2006-09-26 5:37 ` [PATCH 33/47] kobject: must_check fixes Greg KH
2006-09-26 5:37 ` [PATCH 34/47] sysfs_remove_bin_file: no return value, dump_stack on error Greg KH
2006-09-26 5:37 ` [PATCH 35/47] Driver core: fix comments in drivers/base/power/resume.c Greg KH
2006-09-26 5:37 ` [PATCH 36/47] Driver core: fixed add_bind_files() definition Greg KH
2006-09-26 5:37 ` [PATCH 37/47] add __must_check to device management code Greg KH
2006-09-26 5:37 ` [PATCH 38/47] add CONFIG_ENABLE_MUST_CHECK Greg KH
2006-09-26 5:37 ` [PATCH 39/47] v4l-dev2: handle __must_check Greg KH
2006-09-26 5:38 ` [PATCH 40/47] drivers/base: Platform notify needs to occur before drivers attach to the device Greg KH
2006-09-26 5:38 ` [PATCH 41/47] drivers/base: check errors Greg KH
2006-09-26 5:38 ` [PATCH 42/47] sysfs: add proper sysfs_init() prototype Greg KH
2006-09-26 5:38 ` [PATCH 43/47] Driver Core: add ability for drivers to do a threaded probe Greg KH
2006-09-26 5:38 ` [PATCH 44/47] PCI: enable driver multi-threaded probe Greg KH
2006-09-26 5:38 ` [PATCH 45/47] Driver core: Fix potential deadlock in driver core Greg KH
2006-09-26 5:38 ` [PATCH 46/47] Driver core: Remove unneeded routines from " Greg KH
2006-09-26 5:38 ` [PATCH 47/47] Driver core: Don't call put methods while holding a spinlock Greg KH
2006-09-27 18:51 ` [PATCH 44/47] PCI: enable driver multi-threaded probe Olaf Hering
2006-09-29 23:32 ` Greg KH
2006-09-30 6:07 ` Olaf Hering
2006-09-26 17:23 ` [PATCH 41/47] drivers/base: check errors Dmitry Torokhov
2006-09-27 4:33 ` Greg KH
2006-09-26 13:24 ` [PATCH 30/47] Driver core: create devices/virtual/ tree Dmitry Torokhov
2006-09-26 13:41 ` Greg KH
2006-09-26 13:51 ` Dmitry Torokhov
2006-09-26 14:26 ` Greg KH
2006-09-26 17:15 ` Dmitry Torokhov
2006-09-26 13:20 ` [PATCH 26/47] Driver core: add groups support to struct device Dmitry Torokhov
2006-09-26 13:46 ` Greg KH
2006-09-26 14:01 ` Dmitry Torokhov
2006-09-26 14:23 ` Greg KH
2006-09-26 17:10 ` Dmitry Torokhov
2006-09-27 14:40 ` Pavel Machek
2006-09-26 15:18 ` Marcel Holtmann
2006-09-26 12:34 ` [GIT PATCH] Driver Core patches for 2.6.18 Mike Galbraith
2006-09-26 20:39 ` Greg KH
2006-09-27 8:47 ` Mike Galbraith
2006-09-27 6:58 ` Rafael J. Wysocki
2006-09-27 10:48 ` Mike Galbraith
2006-09-27 13:03 ` Mike Galbraith
2006-09-27 11:42 ` Rafael J. Wysocki
Reply instructions:
You may reply publicly to this message via plain-text email
using any one of the following methods:
* Save the following mbox file, import it into your mail client,
and reply-to-all from there: mbox
Avoid top-posting and favor interleaved quoting:
https://en.wikipedia.org/wiki/Posting_style#Interleaved_style
* Reply using the --to, --cc, and --in-reply-to
switches of git-send-email(1):
git send-email \
--in-reply-to=1159249111668-git-send-email-greg@kroah.com \
--to=greg@kroah.com \
--cc=gregkh@suse.de \
--cc=linux-kernel@vger.kernel.org \
--cc=torvalds@osdl.org \
/path/to/YOUR_REPLY
https://kernel.org/pub/software/scm/git/docs/git-send-email.html
* If your mail client supports setting the In-Reply-To header
via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line
before the message body.
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox