Commit 65d20c54 authored by Patrick Mochel's avatar Patrick Mochel

driver model: make device_unregister() only mark device as !present.

There are races WRT walking the lists of the buses and classes and the 
device being unregistered while it's the current node we're accessing. If 
the node was deleted, ->next would point to itself, and we'd sit in an
inifinite loop (until that memory was freed from underneath us and we Oops'd).

This changes device_unregister() to only mark the device as not present. put_device()
deletes the device from all the lists it belongs to, while holding device_lock,
then drops it to call device_del().

This should guarantee that everything in the device is valid until the last reference
goes away, regardless of whether it's still physically present or not. 
parent 5a7728c6
...@@ -259,34 +259,22 @@ struct device * get_device(struct device * dev) ...@@ -259,34 +259,22 @@ struct device * get_device(struct device * dev)
*/ */
void put_device(struct device * dev) void put_device(struct device * dev)
{ {
struct device * parent;
if (!atomic_dec_and_lock(&dev->refcount,&device_lock)) if (!atomic_dec_and_lock(&dev->refcount,&device_lock))
return; return;
parent = dev->parent; list_del_init(&dev->node);
dev->parent = NULL; list_del_init(&dev->g_list);
list_del_init(&dev->bus_list);
list_del_init(&dev->driver_list);
spin_unlock(&device_lock); spin_unlock(&device_lock);
BUG_ON(dev->present); BUG_ON(dev->present);
if (dev->release) device_del(dev);
dev->release(dev);
if (parent)
put_device(parent);
} }
void device_del(struct device * dev) void device_del(struct device * dev)
{ {
spin_lock(&device_lock); struct device * parent = dev->parent;
dev->present = 0;
list_del_init(&dev->node);
list_del_init(&dev->g_list);
list_del_init(&dev->bus_list);
list_del_init(&dev->driver_list);
spin_unlock(&device_lock);
pr_debug("DEV: Unregistering device. ID = '%s', name = '%s'\n",
dev->bus_id,dev->name);
/* Notify the platform of the removal, in case they /* Notify the platform of the removal, in case they
* need to do anything... * need to do anything...
...@@ -302,6 +290,12 @@ void device_del(struct device * dev) ...@@ -302,6 +290,12 @@ void device_del(struct device * dev)
/* remove the driverfs directory */ /* remove the driverfs directory */
device_remove_dir(dev); device_remove_dir(dev);
if (dev->release)
dev->release(dev);
if (parent)
put_device(parent);
} }
/** /**
...@@ -315,7 +309,12 @@ void device_del(struct device * dev) ...@@ -315,7 +309,12 @@ void device_del(struct device * dev)
*/ */
void device_unregister(struct device * dev) void device_unregister(struct device * dev)
{ {
device_del(dev); spin_lock(&device_lock);
dev->present = 0;
spin_unlock(&device_lock);
pr_debug("DEV: Unregistering device. ID = '%s', name = '%s'\n",
dev->bus_id,dev->name);
put_device(dev); put_device(dev);
} }
......
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