Commit 8861b6ce authored by Patrick Mochel's avatar Patrick Mochel

[driver model] Make sure system device drivers are added if registered late.

From Jeremy Fitzhardinge:

With the current system device changes (I picked them up in 2.5.70-mm8),
the system device class assumes that all system device drivers are
registered before any system devices are registered.

Unfortunately, this is often not the case.  CPU devices are registered
very early, but cpufreq registers drivers for them; since cpufreq
drivers can be loaded as modules, they clearly can't be registered
before the device is.

This patch keeps a list of all registered devices hanging off the system
device class.  When a new driver is registered, it calls the driver's
add() function with all existing devices.

Conversely, when a driver is unregistered, it calls the driver's
remove() function for all existing devices so the driver can clean up.

Note: the list in the class's embedded kset is used, rather than creating
a new field.
parent 1a90740a
...@@ -117,9 +117,16 @@ int sysdev_driver_register(struct sysdev_class * cls, ...@@ -117,9 +117,16 @@ int sysdev_driver_register(struct sysdev_class * cls,
struct sysdev_driver * drv) struct sysdev_driver * drv)
{ {
down_write(&system_subsys.rwsem); down_write(&system_subsys.rwsem);
if (cls && kset_get(&cls->kset)) if (cls && kset_get(&cls->kset)) {
list_add_tail(&drv->entry,&cls->drivers); list_add_tail(&drv->entry,&cls->drivers);
else
/* If devices of this class already exist, tell the driver */
if (drv->add) {
struct sys_device *dev;
list_for_each_entry(dev, &cls->kset.list, kobj.entry)
drv->add(dev);
}
} else
list_add_tail(&drv->entry,&global_drivers); list_add_tail(&drv->entry,&global_drivers);
up_write(&system_subsys.rwsem); up_write(&system_subsys.rwsem);
return 0; return 0;
...@@ -136,8 +143,14 @@ void sysdev_driver_unregister(struct sysdev_class * cls, ...@@ -136,8 +143,14 @@ void sysdev_driver_unregister(struct sysdev_class * cls,
{ {
down_write(&system_subsys.rwsem); down_write(&system_subsys.rwsem);
list_del_init(&drv->entry); list_del_init(&drv->entry);
if (cls) if (cls) {
if (drv->remove) {
struct sys_device *dev;
list_for_each_entry(dev, &cls->kset.list, kobj.entry)
drv->remove(dev);
}
kset_put(&cls->kset); kset_put(&cls->kset);
}
up_write(&system_subsys.rwsem); up_write(&system_subsys.rwsem);
} }
...@@ -170,7 +183,7 @@ int sys_device_register(struct sys_device * sysdev) ...@@ -170,7 +183,7 @@ int sys_device_register(struct sys_device * sysdev)
if (!error) { if (!error) {
struct sysdev_driver * drv; struct sysdev_driver * drv;
down_read(&system_subsys.rwsem); down_write(&system_subsys.rwsem);
/* Generic notification is implicit, because it's that /* Generic notification is implicit, because it's that
* code that should have called us. * code that should have called us.
*/ */
...@@ -186,7 +199,7 @@ int sys_device_register(struct sys_device * sysdev) ...@@ -186,7 +199,7 @@ int sys_device_register(struct sys_device * sysdev)
if (drv->add) if (drv->add)
drv->add(sysdev); drv->add(sysdev);
} }
up_read(&system_subsys.rwsem); up_write(&system_subsys.rwsem);
} }
return error; return error;
} }
...@@ -195,7 +208,7 @@ void sys_device_unregister(struct sys_device * sysdev) ...@@ -195,7 +208,7 @@ void sys_device_unregister(struct sys_device * sysdev)
{ {
struct sysdev_driver * drv; struct sysdev_driver * drv;
down_read(&system_subsys.rwsem); down_write(&system_subsys.rwsem);
list_for_each_entry(drv,&global_drivers,entry) { list_for_each_entry(drv,&global_drivers,entry) {
if (drv->remove) if (drv->remove)
drv->remove(sysdev); drv->remove(sysdev);
...@@ -205,7 +218,10 @@ void sys_device_unregister(struct sys_device * sysdev) ...@@ -205,7 +218,10 @@ void sys_device_unregister(struct sys_device * sysdev)
if (drv->remove) if (drv->remove)
drv->remove(sysdev); drv->remove(sysdev);
} }
up_read(&system_subsys.rwsem);
list_del_init(&sysdev->entry);
up_write(&system_subsys.rwsem);
kobject_unregister(&sysdev->kobj); kobject_unregister(&sysdev->kobj);
} }
......
...@@ -72,6 +72,7 @@ struct sys_device { ...@@ -72,6 +72,7 @@ struct sys_device {
u32 id; u32 id;
struct sysdev_class * cls; struct sysdev_class * cls;
struct kobject kobj; struct kobject kobj;
struct list_head entry;
}; };
extern int sys_device_register(struct sys_device *); extern int sys_device_register(struct sys_device *);
......
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