Commit 2884fae0 authored by Patrick Mochel's avatar Patrick Mochel

driver model: make driver refcounting similar to devices'.

In the spirit of devices and buses, change driver refcounting model to 
match the way that devices and buses are done.

struct device_driver gets a ->present field, which is set on registration 
and cleared in driver_unregister(). get_device() checks the state of this
flag and returns NULL if cleared. 

Note that the horribly wrong remove_driver() is deprecated and simply BUG()s
when called. Please convert callers to use driver_unregister(). Updates to 
callers will be coming soon.

Note also that this still doesn't fix the race in which a driver module can
be removed while the refcount on a driver > 1. Near future work should help
to remedy it, but no solutions are guaranteed..
parent 4278bccc
...@@ -39,6 +39,43 @@ int driver_for_each_dev(struct device_driver * drv, void * data, ...@@ -39,6 +39,43 @@ int driver_for_each_dev(struct device_driver * drv, void * data,
return error; return error;
} }
struct device_driver * get_driver(struct device_driver * drv)
{
struct device_driver * ret = drv;
spin_lock(&device_lock);
if (drv && drv->present && atomic_read(&drv->refcount) > 0)
atomic_inc(&drv->refcount);
else
ret = NULL;
spin_unlock(&device_lock);
return ret;
}
void remove_driver(struct device_driver * drv)
{
BUG();
}
/**
* put_driver - decrement driver's refcount and clean up if necessary
* @drv: driver in question
*/
void put_driver(struct device_driver * drv)
{
struct bus_type * bus = drv->bus;
if (!atomic_dec_and_lock(&drv->refcount,&device_lock))
return;
list_del_init(&drv->bus_list);
spin_unlock(&device_lock);
BUG_ON(drv->present);
driver_detach(drv);
driver_remove_dir(drv);
if (drv->release)
drv->release(drv);
put_bus(bus);
}
/** /**
* driver_register - register driver with bus * driver_register - register driver with bus
* @drv: driver to register * @drv: driver to register
...@@ -50,12 +87,13 @@ int driver_register(struct device_driver * drv) ...@@ -50,12 +87,13 @@ int driver_register(struct device_driver * drv)
if (!drv->bus) if (!drv->bus)
return -EINVAL; return -EINVAL;
pr_debug("Registering driver '%s' with bus '%s'\n",drv->name,drv->bus->name); pr_debug("driver %s:%s: registering\n",drv->bus->name,drv->name);
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); INIT_LIST_HEAD(&drv->devices);
drv->present = 1;
spin_lock(&device_lock); spin_lock(&device_lock);
list_add(&drv->bus_list,&drv->bus->drivers); list_add(&drv->bus_list,&drv->bus->drivers);
spin_unlock(&device_lock); spin_unlock(&device_lock);
...@@ -65,39 +103,17 @@ int driver_register(struct device_driver * drv) ...@@ -65,39 +103,17 @@ int driver_register(struct device_driver * drv)
return 0; return 0;
} }
static void __remove_driver(struct device_driver * drv) void driver_unregister(struct device_driver * drv)
{
pr_debug("Unregistering driver '%s' from bus '%s'\n",drv->name,drv->bus->name);
driver_detach(drv);
driver_remove_dir(drv);
if (drv->release)
drv->release(drv);
put_bus(drv->bus);
}
void remove_driver(struct device_driver * drv)
{ {
spin_lock(&device_lock); spin_lock(&device_lock);
atomic_set(&drv->refcount,0); drv->present = 0;
list_del_init(&drv->bus_list);
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;
list_del_init(&drv->bus_list);
spin_unlock(&device_lock); spin_unlock(&device_lock);
__remove_driver(drv); pr_debug("driver %s:%s: unregistering\n",drv->bus->name,drv->name);
put_driver(drv);
} }
EXPORT_SYMBOL(driver_for_each_dev); EXPORT_SYMBOL(driver_for_each_dev);
EXPORT_SYMBOL(driver_register); EXPORT_SYMBOL(driver_register);
EXPORT_SYMBOL(driver_unregister);
EXPORT_SYMBOL(get_driver);
EXPORT_SYMBOL(put_driver); EXPORT_SYMBOL(put_driver);
EXPORT_SYMBOL(remove_driver);
...@@ -110,6 +110,7 @@ struct device_driver { ...@@ -110,6 +110,7 @@ struct device_driver {
rwlock_t lock; rwlock_t lock;
atomic_t refcount; atomic_t refcount;
u32 present;
struct list_head bus_list; struct list_head bus_list;
struct list_head class_list; struct list_head class_list;
...@@ -127,16 +128,10 @@ struct device_driver { ...@@ -127,16 +128,10 @@ struct device_driver {
}; };
extern int driver_register(struct device_driver * drv); extern int driver_register(struct device_driver * drv);
extern void driver_unregister(struct device_driver * drv);
static inline struct device_driver * get_driver(struct device_driver * drv) extern struct device_driver * get_driver(struct device_driver * drv);
{
BUG_ON(!atomic_read(&drv->refcount));
atomic_inc(&drv->refcount);
return 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 void remove_driver(struct device_driver * drv);
......
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