diff options
| author | Patrick Mochel <mochel@osdl.org> | 2002-10-16 21:22:36 -0700 |
|---|---|---|
| committer | Patrick Mochel <mochel@osdl.org> | 2002-10-16 21:22:36 -0700 |
| commit | 6b1febf70889295545f50cc0d05312eaf6daa0a2 (patch) | |
| tree | 8c6a3b2144c448c1ea95350bdb06779f31d43572 | |
| parent | 1e510b8fed8c72356c8915a7b0450a4d5271f170 (diff) | |
driver model: power management/shutdown cleanup.
- take device_sem around all global list walks.
- don't modify refcount, as get_device()/put_device() also take device_sem
- But, make sure we check device_present() to make sure device is still around.
(Note device removal will block until device_sem is dropped, so list will remain
intact.)
- Separate out device_shutdown() walk from device_suspend() walk. Even though the
code is nearly identical, it's a lot clearer as to what is going on when they
are autonomous. It was my bad for originally putting that FIXME in there, encouraging
the consolidation.
- Add debugging hooks for my convenience. :)
- Call ->shutdown() when shutting down device, instead of ->remove(). (See ChangeSet
1.799 for description and semantics).
| -rw-r--r-- | drivers/base/power.c | 68 |
1 files changed, 31 insertions, 37 deletions
diff --git a/drivers/base/power.c b/drivers/base/power.c index c98984dfd8ab..176be19b72d1 100644 --- a/drivers/base/power.c +++ b/drivers/base/power.c @@ -8,6 +8,8 @@ * */ +#define DEBUG 0 + #include <linux/device.h> #include <linux/module.h> #include "base.h" @@ -28,34 +30,21 @@ int device_suspend(u32 state, u32 level) { struct list_head * node; - struct device * prev = NULL; int error = 0; - if(level == SUSPEND_POWER_DOWN) - printk(KERN_EMERG "Shutting down devices\n"); - else - printk(KERN_EMERG "Suspending devices\n"); + printk(KERN_EMERG "Suspending devices\n"); - spin_lock(&device_lock); + down(&device_sem); list_for_each(node,&global_device_list) { - struct device * dev = get_device_locked(to_dev(node)); - if (dev) { - spin_unlock(&device_lock); - if(dev->driver) { - if(level == SUSPEND_POWER_DOWN) { - if(dev->driver->remove) - dev->driver->remove(dev); - } else if(dev->driver->suspend) - error = dev->driver->suspend(dev,state,level); - } - if (prev) - put_device(prev); - prev = dev; - spin_lock(&device_lock); + struct device * dev = to_dev(node); + if (device_present(dev) && dev->driver && dev->driver->suspend) { + pr_debug("suspending device %s\n",dev->name); + error = dev->driver->suspend(dev,state,level); + if (error) + printk(KERN_ERR "%s: suspend returned %d\n",dev->name,error); } } - spin_unlock(&device_lock); - + up(&device_sem); return error; } @@ -70,33 +59,38 @@ int device_suspend(u32 state, u32 level) void device_resume(u32 level) { struct list_head * node; - struct device * prev = NULL; - spin_lock(&device_lock); + down(&device_sem); list_for_each_prev(node,&global_device_list) { - struct device * dev = get_device_locked(to_dev(node)); - if (dev) { - spin_unlock(&device_lock); - if (dev->driver && dev->driver->resume) - dev->driver->resume(dev,level); - if (prev) - put_device(prev); - prev = dev; - spin_lock(&device_lock); + struct device * dev = to_dev(node); + if (device_present(dev) && dev->driver && dev->driver->resume) { + pr_debug("resuming device %s\n",dev->name); + dev->driver->resume(dev,level); } } - spin_unlock(&device_lock); + up(&device_sem); printk(KERN_EMERG "Devices Resumed\n"); } /** - * device_shutdown - call device_suspend with status set to shutdown, to - * cause all devices to remove themselves cleanly + * device_shutdown - call ->remove() on each device to shutdown. */ void device_shutdown(void) { - device_suspend(4, SUSPEND_POWER_DOWN); + struct list_head * entry; + + printk(KERN_EMERG "Shutting down devices\n"); + + down(&device_sem); + list_for_each(entry,&global_device_list) { + struct device * dev = to_dev(entry); + if (device_present(dev) && dev->driver && dev->driver->shutdown) { + pr_debug("shutting down %s\n",dev->name); + dev->driver->shutdown(dev); + } + } + up(&device_sem); } EXPORT_SYMBOL(device_suspend); |
