Commit d6022129 authored by Patrick Mochel's avatar Patrick Mochel

device model udpate:

- make sure drv->devices is initialized on registration (from Peter Osterlund)
- add remove_driver for forcing removal of driver

There was a potential race with the module unload code. When a pci driver was unloaded, it would call pci_unregister_driver, which would simply call put_driver.
If the driver's refcount wasn't 0, it wouldn't unbind it from devices, but the module unload would still continue. 
If something tried to access the driver later (since everyone thinks its still there), Bad Things would happen. 
This fixes it until there can be tighter integration between the device model and module unload code.
parent a9907091
...@@ -3,6 +3,8 @@ ...@@ -3,6 +3,8 @@
* *
*/ */
#define DEBUG 0
#include <linux/device.h> #include <linux/device.h>
#include <linux/module.h> #include <linux/module.h>
#include <linux/errno.h> #include <linux/errno.h>
...@@ -67,6 +69,7 @@ int driver_register(struct device_driver * drv) ...@@ -67,6 +69,7 @@ int driver_register(struct device_driver * drv)
get_bus(drv->bus); get_bus(drv->bus);
atomic_set(&drv->refcount,2); atomic_set(&drv->refcount,2);
rwlock_init(&drv->lock); rwlock_init(&drv->lock);
INIT_LIST_HEAD(&drv->devices);
write_lock(&drv->bus->lock); write_lock(&drv->bus->lock);
list_add(&drv->bus_list,&drv->bus->drivers); list_add(&drv->bus_list,&drv->bus->drivers);
write_unlock(&drv->bus->lock); write_unlock(&drv->bus->lock);
...@@ -76,16 +79,8 @@ int driver_register(struct device_driver * drv) ...@@ -76,16 +79,8 @@ int driver_register(struct device_driver * drv)
return 0; return 0;
} }
/** static void __remove_driver(struct device_driver * drv)
* put_driver - decrement driver's refcount and clean up if necessary
* @drv: driver in question
*/
void put_driver(struct device_driver * drv)
{ {
if (!atomic_dec_and_lock(&drv->refcount,&device_lock))
return;
spin_unlock(&device_lock);
if (drv->bus) { if (drv->bus) {
pr_debug("Unregistering driver '%s' from bus '%s'\n",drv->name,drv->bus->name); pr_debug("Unregistering driver '%s' from bus '%s'\n",drv->name,drv->bus->name);
...@@ -101,6 +96,28 @@ void put_driver(struct device_driver * drv) ...@@ -101,6 +96,28 @@ void put_driver(struct device_driver * drv)
drv->release(drv); drv->release(drv);
} }
void remove_driver(struct device_driver * drv)
{
spin_lock(&device_lock);
atomic_set(&drv->refcount,0);
spin_unlock(&device_lock);
__remove_driver(drv);
}
/**
* put_driver - decrement driver's refcount and clean up if necessary
* @drv: driver in question
*/
void put_driver(struct device_driver * drv)
{
if (!atomic_dec_and_lock(&drv->refcount,&device_lock))
return;
spin_unlock(&device_lock);
__remove_driver(drv);
}
EXPORT_SYMBOL(driver_for_each_dev); EXPORT_SYMBOL(driver_for_each_dev);
EXPORT_SYMBOL(driver_register); EXPORT_SYMBOL(driver_register);
EXPORT_SYMBOL(put_driver); EXPORT_SYMBOL(put_driver);
EXPORT_SYMBOL(remove_driver);
...@@ -118,6 +118,7 @@ static inline struct device_driver * get_driver(struct device_driver * drv) ...@@ -118,6 +118,7 @@ static inline struct device_driver * get_driver(struct device_driver * drv)
} }
extern void put_driver(struct device_driver * drv); extern void put_driver(struct device_driver * drv);
extern void remove_driver(struct device_driver * drv);
extern int driver_for_each_dev(struct device_driver * drv, void * data, extern int driver_for_each_dev(struct device_driver * drv, void * data,
int (*callback)(struct device * dev, void * data)); int (*callback)(struct device * dev, void * data));
......
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