Commit b12cf4ff authored by Patrick Mochel's avatar Patrick Mochel

driver model: exploit kobject contstructs.

This makes the driver model core (for devices) exploit the kobject
infrastructure more and make the resulting code quite a bit simpler. 

For one, device_register() mimmicks kobject_register() in that it now only
calls device_initialize() and device_add() back to back. Similarly, 
device_unregister() calls device_del() and put_device() consecutively. 

device_del() no longer removes and frees the device, it only removes them.
It also removes the devices from the global and sibling lists. This was 
previously done by device_put(), but moved here to be symmetrical with 
device_add().

The device's parent is now only incremented in device_add() and decremented
in device_del(), fixing a bug in which the parent's refcount was incremented
twice. 

Because of these simplifications, the core can easily be converted to use 
the kobject reference counting infrastructure. get_device() now simply 
forwards the call to kobject_get() and ditto for put_device(). 
device_release() is implemented to handle the freeing of devices once their
reference count reaches 0.

Since we're using the kobject refcounting model, we no longer need the 
checking or setting of the device state field, so it has been removed. 
The only users of it were the power routines. In those, it is implicit that
we have a valid device, since we've already taken device_sem, and we're 
walking the list (all modifications are protected by device_sem).

struct device::lock, and the helpers to lock/unlock have been removed. No
one has ever used them, and no one is likely to use them.
parent ca01359a
...@@ -69,15 +69,42 @@ static struct sysfs_ops dev_sysfs_ops = { ...@@ -69,15 +69,42 @@ static struct sysfs_ops dev_sysfs_ops = {
.store = dev_attr_store, .store = dev_attr_store,
}; };
/**
* device_release - free device structure.
* @kobj: device's kobject.
*
* This is called once the reference count for the object
* reaches 0. We forward the call to the device's release
* method, which should handle actually freeing the structure.
*/
static void device_release(struct kobject * kobj)
{
struct device * dev = to_dev(kobj);
if (dev->release)
dev->release(dev);
}
/**
* device_subsys - structure to be registered with kobject core.
*/
struct subsystem device_subsys = { struct subsystem device_subsys = {
.kobj = { .kobj = {
.name = "devices", .name = "devices",
}, },
.release = device_release,
.sysfs_ops = &dev_sysfs_ops, .sysfs_ops = &dev_sysfs_ops,
.default_attrs = dev_default_attrs, .default_attrs = dev_default_attrs,
}; };
/**
* device_create_file - create sysfs attribute file for device.
* @dev: device.
* @attr: device attribute descriptor.
*/
int device_create_file(struct device * dev, struct device_attribute * attr) int device_create_file(struct device * dev, struct device_attribute * attr)
{ {
int error = 0; int error = 0;
...@@ -88,6 +115,12 @@ int device_create_file(struct device * dev, struct device_attribute * attr) ...@@ -88,6 +115,12 @@ int device_create_file(struct device * dev, struct device_attribute * attr)
return error; return error;
} }
/**
* device_remove_file - remove sysfs attribute file.
* @dev: device.
* @attr: device attribute descriptor.
*/
void device_remove_file(struct device * dev, struct device_attribute * attr) void device_remove_file(struct device * dev, struct device_attribute * attr)
{ {
if (get_device(dev)) { if (get_device(dev)) {
...@@ -96,32 +129,72 @@ void device_remove_file(struct device * dev, struct device_attribute * attr) ...@@ -96,32 +129,72 @@ void device_remove_file(struct device * dev, struct device_attribute * attr)
} }
} }
/**
* device_initialize - init device structure.
* @dev: device.
*
* This prepares the device for use by other layers,
* including adding it to the device hierarchy.
* It is the first half of device_register(), if called by
* that, though it can also be called separately, so one
* may use @dev's fields (e.g. the refcount).
*/
void device_initialize(struct device *dev)
{
kobject_init(&dev->kobj);
INIT_LIST_HEAD(&dev->node);
INIT_LIST_HEAD(&dev->children);
INIT_LIST_HEAD(&dev->g_list);
INIT_LIST_HEAD(&dev->driver_list);
INIT_LIST_HEAD(&dev->bus_list);
INIT_LIST_HEAD(&dev->intf_list);
// spin_lock_init(&dev->lock);
}
/**
* device_add - add device to device hierarchy.
* @dev: device.
*
* This is part 2 of device_register(), though may be called
* separately _iff_ device_initialize() has been called separately.
*
* This adds it to the kobject hierarchy via kobject_add(), adds it
* to the global and sibling lists for the device, then
* adds it to the other relevant subsystems of the driver model.
*/
int device_add(struct device *dev) int device_add(struct device *dev)
{ {
struct device * parent;
int error; int error;
if (!dev || !strlen(dev->bus_id)) if (!dev || !strlen(dev->bus_id))
return -EINVAL; return -EINVAL;
down(&device_sem); parent = get_device(dev->parent);
dev->state = DEVICE_REGISTERED;
if (dev->parent) {
list_add_tail(&dev->g_list,&dev->parent->g_list);
list_add_tail(&dev->node,&dev->parent->children);
} else
list_add_tail(&dev->g_list,&global_device_list);
up(&device_sem);
pr_debug("DEV: registering device: ID = '%s', name = %s\n", pr_debug("DEV: registering device: ID = '%s', name = %s\n",
dev->bus_id, dev->name); dev->bus_id, dev->name);
/* first, register with generic layer. */
strncpy(dev->kobj.name,dev->bus_id,KOBJ_NAME_LEN); strncpy(dev->kobj.name,dev->bus_id,KOBJ_NAME_LEN);
if (dev->parent)
dev->kobj.parent = &dev->parent->kobj;
dev->kobj.subsys = &device_subsys; dev->kobj.subsys = &device_subsys;
if ((error = kobject_register(&dev->kobj))) if (parent)
dev->kobj.parent = &parent->kobj;
if ((error = kobject_add(&dev->kobj)))
goto register_done; goto register_done;
/* now take care of our own registration */
down(&device_sem);
if (parent) {
list_add_tail(&dev->g_list,&dev->parent->g_list);
list_add_tail(&dev->node,&parent->children);
} else
list_add_tail(&dev->g_list,&global_device_list);
up(&device_sem);
bus_add_device(dev); bus_add_device(dev);
/* notify platform of device entry */ /* notify platform of device entry */
...@@ -133,95 +206,79 @@ int device_add(struct device *dev) ...@@ -133,95 +206,79 @@ int device_add(struct device *dev)
devclass_add_device(dev); devclass_add_device(dev);
register_done: register_done:
if (error) { if (error && parent)
down(&device_sem); put_device(parent);
list_del_init(&dev->g_list);
list_del_init(&dev->node);
up(&device_sem);
}
return error; return error;
} }
void device_initialize(struct device *dev)
{
kobject_init(&dev->kobj);
INIT_LIST_HEAD(&dev->node);
INIT_LIST_HEAD(&dev->children);
INIT_LIST_HEAD(&dev->g_list);
INIT_LIST_HEAD(&dev->driver_list);
INIT_LIST_HEAD(&dev->bus_list);
INIT_LIST_HEAD(&dev->intf_list);
spin_lock_init(&dev->lock);
atomic_set(&dev->refcount,1);
dev->state = DEVICE_INITIALIZED;
if (dev->parent)
get_device(dev->parent);
}
/** /**
* device_register - register a device * device_register - register a device with the system.
* @dev: pointer to the device structure * @dev: pointer to the device structure
* *
* First, make sure that the device has a parent, create * This happens in two clean steps - initialize the device
* a directory for it, then add it to the parent's list of * and add it to the system. The two steps can be called
* children. * separately, but this is the easiest and most common.
* * I.e. you should only call the two helpers separately if
* Maintains a global list of all devices, in depth-first ordering. * have a clearly defined need to use and refcount the device
* The head for that list is device_root.g_list. * before it is added to the hierarchy.
*/ */
int device_register(struct device *dev) int device_register(struct device *dev)
{ {
int error;
if (!dev || !strlen(dev->bus_id))
return -EINVAL;
device_initialize(dev); device_initialize(dev);
if (dev->parent) return device_add(dev);
get_device(dev->parent);
error = device_add(dev);
if (error && dev->parent)
put_device(dev->parent);
return error;
} }
/**
* get_device - increment reference count for device.
* @dev: device.
*
* This simply forwards the call to kobject_get(), though
* we do take care to provide for the case that we get a NULL
* pointer passed in.
*/
struct device * get_device(struct device * dev) struct device * get_device(struct device * dev)
{ {
struct device * ret = dev; return dev ? to_dev(kobject_get(&dev->kobj)) : NULL;
down(&device_sem);
if (device_present(dev) && atomic_read(&dev->refcount) > 0)
atomic_inc(&dev->refcount);
else
ret = NULL;
up(&device_sem);
return ret;
} }
/** /**
* put_device - decrement reference count, and clean up when it hits 0 * put_device - decrement reference count.
* @dev: device in question * @dev: device in question.
*/ */
void put_device(struct device * dev) void put_device(struct device * dev)
{ {
down(&device_sem); kobject_put(&dev->kobj);
if (!atomic_dec_and_test(&dev->refcount)) { }
up(&device_sem);
return;
}
list_del_init(&dev->node);
list_del_init(&dev->g_list);
up(&device_sem);
WARN_ON(dev->state == DEVICE_REGISTERED);
if (dev->state == DEVICE_GONE) /**
device_del(dev); * device_del - delete device from system.
} * @dev: device.
*
* This is the first part of the device unregistration
* sequence. This removes the device from the lists we control
* from here, has it removed from the other driver model
* subsystems it was added to in device_add(), and removes it
* from the kobject hierarchy.
*
* NOTE: this should be called manually _iff_ device_add() was
* also called manually.
*/
void device_del(struct device * dev) void device_del(struct device * dev)
{ {
struct device * parent = dev->parent; struct device * parent = dev->parent;
down(&device_sem);
list_del_init(&dev->node);
list_del_init(&dev->g_list);
up(&device_sem);
/* Notify the platform of the removal, in case they /* Notify the platform of the removal, in case they
* need to do anything... * need to do anything...
*/ */
...@@ -233,31 +290,29 @@ void device_del(struct device * dev) ...@@ -233,31 +290,29 @@ void device_del(struct device * dev)
bus_remove_device(dev); bus_remove_device(dev);
if (dev->release) kobject_del(&dev->kobj);
dev->release(dev);
if (parent) if (parent)
put_device(parent); put_device(parent);
} }
/** /**
* device_unregister - unlink device * device_unregister - unregister device from system.
* @dev: device going away * @dev: device going away.
* *
* The device has been removed from the system, so we disavow knowledge * We do this in two parts, like we do device_register(). First,
* of it. It might not be the final reference to the device, so we mark * we remove it from all the subsystems with device_del(), then
* it as !present, so no more references to it can be acquired. * we decrement the reference count via put_device(). If that
* In the end, we decrement the final reference count for it. * is the final reference count, the device will be cleaned up
* via device_release() above. Otherwise, the structure will
* stick around until the final reference to the device is dropped.
*/ */
void device_unregister(struct device * dev) void device_unregister(struct device * dev)
{ {
down(&device_sem);
dev->state = DEVICE_GONE;
up(&device_sem);
pr_debug("DEV: Unregistering device. ID = '%s', name = '%s'\n", pr_debug("DEV: Unregistering device. ID = '%s', name = '%s'\n",
dev->bus_id,dev->name); dev->bus_id,dev->name);
kobject_unregister(&dev->kobj); device_del(dev);
put_device(dev); put_device(dev);
} }
...@@ -268,7 +323,11 @@ static int __init device_subsys_init(void) ...@@ -268,7 +323,11 @@ static int __init device_subsys_init(void)
core_initcall(device_subsys_init); core_initcall(device_subsys_init);
EXPORT_SYMBOL(device_initialize);
EXPORT_SYMBOL(device_add);
EXPORT_SYMBOL(device_register); EXPORT_SYMBOL(device_register);
EXPORT_SYMBOL(device_del);
EXPORT_SYMBOL(device_unregister); EXPORT_SYMBOL(device_unregister);
EXPORT_SYMBOL(get_device); EXPORT_SYMBOL(get_device);
EXPORT_SYMBOL(put_device); EXPORT_SYMBOL(put_device);
......
...@@ -38,7 +38,7 @@ int device_suspend(u32 state, u32 level) ...@@ -38,7 +38,7 @@ int device_suspend(u32 state, u32 level)
down(&device_sem); down(&device_sem);
list_for_each(node,&global_device_list) { list_for_each(node,&global_device_list) {
struct device * dev = to_dev(node); struct device * dev = to_dev(node);
if (device_present(dev) && dev->driver && dev->driver->suspend) { if (dev->driver && dev->driver->suspend) {
pr_debug("suspending device %s\n",dev->name); pr_debug("suspending device %s\n",dev->name);
error = dev->driver->suspend(dev,state,level); error = dev->driver->suspend(dev,state,level);
if (error) if (error)
...@@ -64,7 +64,7 @@ void device_resume(u32 level) ...@@ -64,7 +64,7 @@ void device_resume(u32 level)
down(&device_sem); down(&device_sem);
list_for_each_prev(node,&global_device_list) { list_for_each_prev(node,&global_device_list) {
struct device * dev = to_dev(node); struct device * dev = to_dev(node);
if (device_present(dev) && dev->driver && dev->driver->resume) { if (dev->driver && dev->driver->resume) {
pr_debug("resuming device %s\n",dev->name); pr_debug("resuming device %s\n",dev->name);
dev->driver->resume(dev,level); dev->driver->resume(dev,level);
} }
...@@ -86,7 +86,7 @@ void device_shutdown(void) ...@@ -86,7 +86,7 @@ void device_shutdown(void)
down(&device_sem); down(&device_sem);
list_for_each(entry,&global_device_list) { list_for_each(entry,&global_device_list) {
struct device * dev = to_dev(entry); struct device * dev = to_dev(entry);
if (device_present(dev) && dev->driver && dev->driver->shutdown) { if (dev->driver && dev->driver->shutdown) {
pr_debug("shutting down %s\n",dev->name); pr_debug("shutting down %s\n",dev->name);
dev->driver->shutdown(dev); dev->driver->shutdown(dev);
} }
......
...@@ -279,12 +279,6 @@ struct device { ...@@ -279,12 +279,6 @@ struct device {
char name[DEVICE_NAME_SIZE]; /* descriptive ascii string */ char name[DEVICE_NAME_SIZE]; /* descriptive ascii string */
char bus_id[BUS_ID_SIZE]; /* position on parent bus */ char bus_id[BUS_ID_SIZE]; /* position on parent bus */
spinlock_t lock; /* lock for the device to ensure two
different layers don't access it at
the same time. */
atomic_t refcount; /* refcount to make sure the device
* persists for the right amount of time */
struct bus_type * bus; /* type of bus device is on */ struct bus_type * bus; /* type of bus device is on */
struct device_driver *driver; /* which driver has allocated this struct device_driver *driver; /* which driver has allocated this
device */ device */
...@@ -296,7 +290,6 @@ struct device { ...@@ -296,7 +290,6 @@ struct device {
void *platform_data; /* Platform specific data (e.g. ACPI, void *platform_data; /* Platform specific data (e.g. ACPI,
BIOS data relevant to device) */ BIOS data relevant to device) */
enum device_state state;
u32 power_state; /* Current operating state. In u32 power_state; /* Current operating state. In
ACPI-speak, this is D0-D3, D0 ACPI-speak, this is D0-D3, D0
being fully functional, and D3 being fully functional, and D3
...@@ -369,24 +362,6 @@ extern int (*platform_notify)(struct device * dev); ...@@ -369,24 +362,6 @@ extern int (*platform_notify)(struct device * dev);
extern int (*platform_notify_remove)(struct device * dev); extern int (*platform_notify_remove)(struct device * dev);
static inline int device_present(struct device * dev)
{
return (dev && (dev->state == DEVICE_INITIALIZED || dev->state == DEVICE_REGISTERED));
}
/* device and bus locking helpers.
*
* FIXME: Is there anything else we need to do?
*/
static inline void lock_device(struct device * dev)
{
spin_lock(&dev->lock);
}
static inline void unlock_device(struct device * dev)
{
spin_unlock(&dev->lock);
}
/** /**
* get_device - atomically increment the reference count for the device. * get_device - atomically increment the reference count for the 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