Commit 6b1febf7 authored by Patrick Mochel's avatar Patrick Mochel

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).
parent 1e510b8f
...@@ -8,6 +8,8 @@ ...@@ -8,6 +8,8 @@
* *
*/ */
#define DEBUG 0
#include <linux/device.h> #include <linux/device.h>
#include <linux/module.h> #include <linux/module.h>
#include "base.h" #include "base.h"
...@@ -28,34 +30,21 @@ ...@@ -28,34 +30,21 @@
int device_suspend(u32 state, u32 level) int device_suspend(u32 state, u32 level)
{ {
struct list_head * node; struct list_head * node;
struct device * prev = NULL;
int error = 0; 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) { list_for_each(node,&global_device_list) {
struct device * dev = get_device_locked(to_dev(node)); struct device * dev = to_dev(node);
if (dev) { if (device_present(dev) && dev->driver && dev->driver->suspend) {
spin_unlock(&device_lock); pr_debug("suspending device %s\n",dev->name);
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); error = dev->driver->suspend(dev,state,level);
} if (error)
if (prev) printk(KERN_ERR "%s: suspend returned %d\n",dev->name,error);
put_device(prev);
prev = dev;
spin_lock(&device_lock);
} }
} }
spin_unlock(&device_lock); up(&device_sem);
return error; return error;
} }
...@@ -70,33 +59,38 @@ int device_suspend(u32 state, u32 level) ...@@ -70,33 +59,38 @@ int device_suspend(u32 state, u32 level)
void device_resume(u32 level) void device_resume(u32 level)
{ {
struct list_head * node; struct list_head * node;
struct device * prev = NULL;
spin_lock(&device_lock); down(&device_sem);
list_for_each_prev(node,&global_device_list) { list_for_each_prev(node,&global_device_list) {
struct device * dev = get_device_locked(to_dev(node)); struct device * dev = to_dev(node);
if (dev) { if (device_present(dev) && dev->driver && dev->driver->resume) {
spin_unlock(&device_lock); pr_debug("resuming device %s\n",dev->name);
if (dev->driver && dev->driver->resume)
dev->driver->resume(dev,level); dev->driver->resume(dev,level);
if (prev)
put_device(prev);
prev = dev;
spin_lock(&device_lock);
} }
} }
spin_unlock(&device_lock); up(&device_sem);
printk(KERN_EMERG "Devices Resumed\n"); printk(KERN_EMERG "Devices Resumed\n");
} }
/** /**
* device_shutdown - call device_suspend with status set to shutdown, to * device_shutdown - call ->remove() on each device to shutdown.
* cause all devices to remove themselves cleanly
*/ */
void device_shutdown(void) 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); EXPORT_SYMBOL(device_suspend);
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment