Commit 3c8d66aa authored by Patrick Mochel's avatar Patrick Mochel

driver model: update and clean bus and driver support.

This a multi-pronged attack aimed at exploiting the kobject infrastructure mor. 

- Remove bus_driver_list, in favor of list in bus_subys.

- Remove bus_for_each_* and driver_for_each_dev(). They're not being used by
anyone, have questionable locking semantics, and really don't provide that
much use, as the function returns once the callback fails, with no indication
of where it failed. Forget them, at least for now. 

- Make sure that we return success from bus_match() if device matches, but
doesn't have a probe method.

- Remove extraneous get_{device,driver}s from bus routines that are serialized
by the bus's rwsem. bus_{add,remove}_{device,driver} all take the rwsem, so there 
is no way we can get a non-existant object when in those functions.

- Use the rwsem in the struct subsystem the bus has embedded in it, and kill the
separate one in struct bus_type.

- Move bulk of driver_register() into bus_add_driver(), which holds the bus's
rwsem during the entirety. this will prevent the driver from being unloaded while
it's being registered, and two drivers with the same name getting registered
at the same time. 

- Ditto for driver_unregister() and bus_remove_driver().

- Add driver_release() method for the driver bus driver subsystems. (Explained later)

- Use only the refcounts in the buses' kobjects, and kill the one in struct bus_type.

- Kill struct bus_type::present and struct device_driver::present. These didn't 
work out the way we intended them to. The idea was to not let a user obtain a 
rerference count to the object if it was in the process of being unregistered. 
All the code paths should be fixed now such that their registration is protected with
a semaphore, so no partially initialized objects can be removed, and enough 
infrastructure is moved to the kobject model so that once the object is publically
visible, it should be usable by other sources.

- Add a bus_sem to serialize bus registration and unregistration. 

- Add struct device_driver::unload_sem to prevent unloading of drivers 
with a positive reference count.

The driver model has always had a bug that would allow a driver with a 
positive reference count to be unloaded. It would decrement the reference 
count and return, letting the module be unloaded, without accounting for 
the other users of the object. This has been discussed many times, though 
never resolved cleanly. This should fix the problem in the simplest manner.

struct device_driver gets unload_sem, which is initialized to _locked_. When
the reference count for the driver reaches 0, the semaphore is unlocked.
driver_unregister() blocks on acquiring this lock before it exits. In the 
normal case that driver_unregister() drops the last reference to the driver,
the lock will be acquired immediately, and the module will unload. 

In the case that someone else is using the driver object, driver_unregister()
will not be able to acquire the lock, since the refcount has not reached 0,
and the lock has not been released. 

This means that rmmod(8) will block while drivers' sysfs files are open. 
There are no sysfs files for drivers yet, but note this when they do have 
some.
parent 1b867c36
This diff is collapsed.
...@@ -3,7 +3,7 @@ ...@@ -3,7 +3,7 @@
* *
*/ */
#undef DEBUG #define DEBUG
#include <linux/device.h> #include <linux/device.h>
#include <linux/module.h> #include <linux/module.h>
...@@ -12,9 +12,12 @@ ...@@ -12,9 +12,12 @@
#include "base.h" #include "base.h"
#define to_dev(node) container_of(node,struct device,driver_list) #define to_dev(node) container_of(node,struct device,driver_list)
#define to_drv(obj) container_of(obj,struct device_driver,kobj)
/* /**
* helpers for creating driver attributes in sysfs * driver_create_file - create sysfs file for driver.
* @drv: driver.
* @attr: driver attribute descriptor.
*/ */
int driver_create_file(struct device_driver * drv, struct driver_attribute * attr) int driver_create_file(struct device_driver * drv, struct driver_attribute * attr)
...@@ -28,6 +31,13 @@ int driver_create_file(struct device_driver * drv, struct driver_attribute * att ...@@ -28,6 +31,13 @@ int driver_create_file(struct device_driver * drv, struct driver_attribute * att
return error; return error;
} }
/**
* driver_remove_file - remove sysfs file for driver.
* @drv: driver.
* @attr: driver attribute descriptor.
*/
void driver_remove_file(struct device_driver * drv, struct driver_attribute * attr) void driver_remove_file(struct device_driver * drv, struct driver_attribute * attr)
{ {
if (get_driver(drv)) { if (get_driver(drv)) {
...@@ -36,106 +46,67 @@ void driver_remove_file(struct device_driver * drv, struct driver_attribute * at ...@@ -36,106 +46,67 @@ void driver_remove_file(struct device_driver * drv, struct driver_attribute * at
} }
} }
int driver_for_each_dev(struct device_driver * drv, void * data,
int (*callback)(struct device *, void * ))
{
struct list_head * node;
int error = 0;
drv = get_driver(drv);
if (drv) {
down_read(&drv->bus->rwsem);
list_for_each(node,&drv->devices) {
struct device * dev = get_device(to_dev(node));
if (dev) {
error = callback(dev,data);
put_device(dev);
if (error)
break;
}
}
up_read(&drv->bus->rwsem);
put_driver(drv);
}
return error;
}
/**
* get_driver - increment driver reference count.
* @drv: driver.
*/
struct device_driver * get_driver(struct device_driver * drv) struct device_driver * get_driver(struct device_driver * drv)
{ {
struct device_driver * ret = drv; return drv ? to_drv(kobject_get(&drv->kobj)) : NULL;
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 * put_driver - decrement driver's refcount.
* @drv: driver in question * @drv: driver.
*/ */
void put_driver(struct device_driver * drv) void put_driver(struct device_driver * drv)
{ {
struct bus_type * bus = drv->bus; kobject_put(&drv->kobj);
if (!atomic_dec_and_lock(&drv->refcount,&device_lock))
return;
spin_unlock(&device_lock);
BUG_ON(drv->present);
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
* *
* Add to bus's list of devices * We pass off most of the work to the bus_add_driver() call,
* since most of the things we have to do deal with the bus
* structures.
*
* The one interesting aspect is that we initialize @drv->unload_sem
* to a locked state here. It will be unlocked when the driver
* reference count reaches 0.
*/ */
int driver_register(struct device_driver * drv) int driver_register(struct device_driver * drv)
{ {
if (!drv->bus)
return -EINVAL;
pr_debug("driver %s:%s: registering\n",drv->bus->name,drv->name);
kobject_init(&drv->kobj);
strncpy(drv->kobj.name,drv->name,KOBJ_NAME_LEN);
drv->kobj.subsys = &drv->bus->drvsubsys;
kobject_register(&drv->kobj);
get_bus(drv->bus);
atomic_set(&drv->refcount,2);
rwlock_init(&drv->lock);
INIT_LIST_HEAD(&drv->devices); INIT_LIST_HEAD(&drv->devices);
drv->present = 1; init_MUTEX_LOCKED(&drv->unload_sem);
bus_add_driver(drv); return bus_add_driver(drv);
devclass_add_driver(drv);
put_driver(drv);
return 0;
} }
/**
* driver_unregister - remove driver from system.
* @drv: driver.
*
* Again, we pass off most of the work to the bus-level call.
*
* Though, once that is done, we attempt to take @drv->unload_sem.
* This will block until the driver refcount reaches 0, and it is
* released. Only modular drivers will call this function, and we
* have to guarantee that it won't complete, letting the driver
* unload until all references are gone.
*/
void driver_unregister(struct device_driver * drv) void driver_unregister(struct device_driver * drv)
{ {
spin_lock(&device_lock);
drv->present = 0;
spin_unlock(&device_lock);
pr_debug("driver %s:%s: unregistering\n",drv->bus->name,drv->name);
bus_remove_driver(drv); bus_remove_driver(drv);
devclass_remove_driver(drv); down(&drv->unload_sem);
kobject_unregister(&drv->kobj); up(&drv->unload_sem);
put_driver(drv);
} }
EXPORT_SYMBOL(driver_for_each_dev);
EXPORT_SYMBOL(driver_register); EXPORT_SYMBOL(driver_register);
EXPORT_SYMBOL(driver_unregister); EXPORT_SYMBOL(driver_unregister);
EXPORT_SYMBOL(get_driver); EXPORT_SYMBOL(get_driver);
......
...@@ -64,14 +64,10 @@ struct device_class; ...@@ -64,14 +64,10 @@ struct device_class;
struct bus_type { struct bus_type {
char * name; char * name;
struct rw_semaphore rwsem;
atomic_t refcount;
u32 present;
struct subsystem subsys; struct subsystem subsys;
struct subsystem drvsubsys; struct subsystem drvsubsys;
struct subsystem devsubsys; struct subsystem devsubsys;
struct list_head node;
struct list_head devices; struct list_head devices;
struct list_head drivers; struct list_head drivers;
...@@ -88,11 +84,6 @@ extern void bus_unregister(struct bus_type * bus); ...@@ -88,11 +84,6 @@ extern void bus_unregister(struct bus_type * bus);
extern struct bus_type * get_bus(struct bus_type * bus); extern struct bus_type * get_bus(struct bus_type * bus);
extern void put_bus(struct bus_type * bus); extern void put_bus(struct bus_type * bus);
extern int bus_for_each_dev(struct bus_type * bus, void * data,
int (*callback)(struct device * dev, void * data));
extern int bus_for_each_drv(struct bus_type * bus, void * data,
int (*callback)(struct device_driver * drv, void * data));
/* driverfs interface for exporting bus attributes */ /* driverfs interface for exporting bus attributes */
...@@ -117,10 +108,7 @@ struct device_driver { ...@@ -117,10 +108,7 @@ struct device_driver {
struct bus_type * bus; struct bus_type * bus;
struct device_class * devclass; struct device_class * devclass;
rwlock_t lock; struct semaphore unload_sem;
atomic_t refcount;
u32 present;
struct kobject kobj; struct kobject kobj;
struct list_head bus_list; struct list_head bus_list;
struct list_head class_list; struct list_head class_list;
...@@ -131,8 +119,6 @@ struct device_driver { ...@@ -131,8 +119,6 @@ struct device_driver {
void (*shutdown) (struct device * dev); void (*shutdown) (struct device * dev);
int (*suspend) (struct device * dev, u32 state, u32 level); int (*suspend) (struct device * dev, u32 state, u32 level);
int (*resume) (struct device * dev, u32 level); int (*resume) (struct device * dev, u32 level);
void (*release) (struct device_driver * drv);
}; };
...@@ -141,10 +127,6 @@ extern void driver_unregister(struct device_driver * drv); ...@@ -141,10 +127,6 @@ extern void driver_unregister(struct device_driver * drv);
extern struct device_driver * get_driver(struct device_driver * drv); extern 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,
int (*callback)(struct device * dev, void * data));
/* driverfs interface for exporting driver attributes */ /* driverfs interface for exporting driver attributes */
......
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