Commit d9b24a11 authored by Patrick Mochel's avatar Patrick Mochel

driver model: add per-class rwsem and protect list accesses with it.

This is similar to struct bus_type's rwsem, though classes aren't doing 
anything nearly as fancy with it. We just make sure to take it when ever
we add or remove any devices or drivers. 
parent f21c8fc3
...@@ -10,27 +10,29 @@ static LIST_HEAD(class_list); ...@@ -10,27 +10,29 @@ static LIST_HEAD(class_list);
int devclass_add_driver(struct device_driver * drv) int devclass_add_driver(struct device_driver * drv)
{ {
if (drv->devclass) { struct device_class * cls = get_devclass(drv->devclass);
pr_debug("Registering driver %s:%s with class %s\n", if (cls) {
drv->bus->name,drv->name,drv->devclass->name); down_write(&cls->rwsem);
pr_debug("device class %s: adding driver %s:%s\n",
spin_lock(&device_lock); cls->name,drv->bus->name,drv->name);
list_add_tail(&drv->class_list,&drv->devclass->drivers); list_add_tail(&drv->class_list,&cls->drivers);
spin_unlock(&device_lock);
devclass_drv_link(drv); devclass_drv_link(drv);
up_write(&cls->rwsem);
} }
return 0; return 0;
} }
void devclass_remove_driver(struct device_driver * drv) void devclass_remove_driver(struct device_driver * drv)
{ {
if (drv->devclass) { struct device_class * cls = drv->devclass;
pr_debug("Removing driver %s:%s:%s\n", if (cls) {
drv->devclass->name,drv->bus->name,drv->name); down_write(&cls->rwsem);
spin_lock(&device_lock); pr_debug("device class %s: removing driver %s:%s\n",
cls->name,drv->bus->name,drv->name);
list_del_init(&drv->class_list); list_del_init(&drv->class_list);
spin_unlock(&device_lock);
devclass_drv_unlink(drv); devclass_drv_unlink(drv);
up_write(&cls->rwsem);
put_devclass(cls);
} }
} }
...@@ -38,9 +40,7 @@ void devclass_remove_driver(struct device_driver * drv) ...@@ -38,9 +40,7 @@ void devclass_remove_driver(struct device_driver * drv)
static void enum_device(struct device_class * cls, struct device * dev) static void enum_device(struct device_class * cls, struct device * dev)
{ {
u32 val; u32 val;
spin_lock(&device_lock);
val = cls->devnum++; val = cls->devnum++;
spin_unlock(&device_lock);
dev->class_num = val; dev->class_num = val;
devclass_dev_link(cls,dev); devclass_dev_link(cls,dev);
} }
...@@ -51,22 +51,44 @@ static void unenum_device(struct device_class * cls, struct device * dev) ...@@ -51,22 +51,44 @@ static void unenum_device(struct device_class * cls, struct device * dev)
dev->class_num = 0; dev->class_num = 0;
} }
/**
* devclass_add_device - register device with device class
* @dev: device to be registered
*
* This is called when a device is either registered with the
* core, or after the a driver module is loaded and bound to
* the device.
* The class is determined by looking at @dev's driver, so one
* way or another, it must be bound to something. Once the
* class is determined, it's set to prevent against concurrent
* calls for the same device stomping on each other.
*
* /sbin/hotplug should be called once the device is added to
* class and all the interfaces.
*/
int devclass_add_device(struct device * dev) int devclass_add_device(struct device * dev)
{ {
struct device_class * cls; struct device_class * cls;
int error = 0; int error = 0;
if (dev->driver) { if (dev->driver) {
cls = dev->driver->devclass; cls = get_devclass(dev->driver->devclass);
if (cls) { if (cls) {
pr_debug("adding device '%s' to class '%s'\n", down_write(&cls->rwsem);
dev->name,cls->name); pr_debug("device class %s: adding device %s\n",
cls->name,dev->name);
if (cls->add_device) if (cls->add_device)
error = cls->add_device(dev); error = cls->add_device(dev);
if (!error) { if (!error) {
enum_device(cls,dev); enum_device(cls,dev);
interface_add(cls,dev); interface_add(cls,dev);
} }
/* notify userspace (call /sbin/hotplug) here */
up_write(&cls->rwsem);
if (error)
put_devclass(cls);
} }
} }
return error; return error;
...@@ -74,14 +96,21 @@ int devclass_add_device(struct device * dev) ...@@ -74,14 +96,21 @@ int devclass_add_device(struct device * dev)
void devclass_remove_device(struct device * dev) void devclass_remove_device(struct device * dev)
{ {
struct device_class * cls = dev->driver->devclass; struct device_class * cls;
if (cls) {
pr_debug("removing device '%s' from class '%s'\n", if (dev->driver) {
dev->name,cls->name); cls = dev->driver->devclass;
interface_remove(cls,dev); if (cls) {
unenum_device(cls,dev); down_write(&cls->rwsem);
if (cls->remove_device) pr_debug("device class %s: removing device %s\n",
cls->remove_device(dev); cls->name,dev->name);
interface_remove(cls,dev);
unenum_device(cls,dev);
if (cls->remove_device)
cls->remove_device(dev);
up_write(&cls->rwsem);
put_devclass(cls);
}
} }
} }
...@@ -111,6 +140,7 @@ int devclass_register(struct device_class * cls) ...@@ -111,6 +140,7 @@ int devclass_register(struct device_class * cls)
{ {
INIT_LIST_HEAD(&cls->drivers); INIT_LIST_HEAD(&cls->drivers);
INIT_LIST_HEAD(&cls->intf_list); INIT_LIST_HEAD(&cls->intf_list);
init_rwsem(&cls->rwsem);
atomic_set(&cls->refcount,2); atomic_set(&cls->refcount,2);
cls->present = 1; cls->present = 1;
pr_debug("device class '%s': registering\n",cls->name); pr_debug("device class '%s': registering\n",cls->name);
......
...@@ -163,6 +163,8 @@ extern void driver_remove_file(struct device_driver *, struct driver_attribute * ...@@ -163,6 +163,8 @@ extern void driver_remove_file(struct device_driver *, struct driver_attribute *
*/ */
struct device_class { struct device_class {
char * name; char * name;
struct rw_semaphore rwsem;
atomic_t refcount; atomic_t refcount;
u32 present; u32 present;
......
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