From 419cab3fc69588ebe35b845cc3a584ae172463de Mon Sep 17 00:00:00 2001 From: Dmitry Torokhov Date: Tue, 26 Apr 2005 02:32:54 -0500 Subject: [PATCH] kset_hotplug_ops->name shoudl return const char * kobject: change name() method in kset_hotplug_ops return const char * since users shoudl not try to modify returned data. Signed-off-by: Dmitry Torokhov Signed-off-by: Greg Kroah-Hartman --- drivers/base/class.c | 2 +- drivers/base/core.c | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) (limited to 'drivers/base') diff --git a/drivers/base/class.c b/drivers/base/class.c index d2a2f8f2b4e..ff33ebbf1dc 100644 --- a/drivers/base/class.c +++ b/drivers/base/class.c @@ -262,7 +262,7 @@ static int class_hotplug_filter(struct kset *kset, struct kobject *kobj) return 0; } -static char *class_hotplug_name(struct kset *kset, struct kobject *kobj) +static const char *class_hotplug_name(struct kset *kset, struct kobject *kobj) { struct class_device *class_dev = to_class_dev(kobj); diff --git a/drivers/base/core.c b/drivers/base/core.c index fbc223486f8..32f3f09a42e 100644 --- a/drivers/base/core.c +++ b/drivers/base/core.c @@ -102,7 +102,7 @@ static int dev_hotplug_filter(struct kset *kset, struct kobject *kobj) return 0; } -static char *dev_hotplug_name(struct kset *kset, struct kobject *kobj) +static const char *dev_hotplug_name(struct kset *kset, struct kobject *kobj) { struct device *dev = to_dev(kobj); -- cgit v1.2.3 From 4a0c20bf8c0fe2116f8fd7d3da6122bf8a01f026 Mon Sep 17 00:00:00 2001 From: Dmitry Torokhov Date: Fri, 29 Apr 2005 01:23:47 -0500 Subject: [PATCH] sysfs: (driver/base) if show/store is missing return -EIO sysfs: fix drivers/base so if an attribute doesn't implement show or store method read/write will return -EIO instead of 0. Signed-off-by: Dmitry Torokhov Signed-off-by: Greg Kroah-Hartman --- drivers/base/bus.c | 4 ++-- drivers/base/class.c | 4 ++-- drivers/base/core.c | 4 ++-- drivers/base/sys.c | 4 ++-- 4 files changed, 8 insertions(+), 8 deletions(-) (limited to 'drivers/base') diff --git a/drivers/base/bus.c b/drivers/base/bus.c index 3cb04bb04c2..80ce88de56f 100644 --- a/drivers/base/bus.c +++ b/drivers/base/bus.c @@ -36,7 +36,7 @@ drv_attr_show(struct kobject * kobj, struct attribute * attr, char * buf) { struct driver_attribute * drv_attr = to_drv_attr(attr); struct device_driver * drv = to_driver(kobj); - ssize_t ret = 0; + ssize_t ret = -EIO; if (drv_attr->show) ret = drv_attr->show(drv, buf); @@ -49,7 +49,7 @@ drv_attr_store(struct kobject * kobj, struct attribute * attr, { struct driver_attribute * drv_attr = to_drv_attr(attr); struct device_driver * drv = to_driver(kobj); - ssize_t ret = 0; + ssize_t ret = -EIO; if (drv_attr->store) ret = drv_attr->store(drv, buf, count); diff --git a/drivers/base/class.c b/drivers/base/class.c index ff33ebbf1dc..344b8cd7390 100644 --- a/drivers/base/class.c +++ b/drivers/base/class.c @@ -26,7 +26,7 @@ class_attr_show(struct kobject * kobj, struct attribute * attr, char * buf) { struct class_attribute * class_attr = to_class_attr(attr); struct class * dc = to_class(kobj); - ssize_t ret = 0; + ssize_t ret = -EIO; if (class_attr->show) ret = class_attr->show(dc, buf); @@ -39,7 +39,7 @@ class_attr_store(struct kobject * kobj, struct attribute * attr, { struct class_attribute * class_attr = to_class_attr(attr); struct class * dc = to_class(kobj); - ssize_t ret = 0; + ssize_t ret = -EIO; if (class_attr->store) ret = class_attr->store(dc, buf, count); diff --git a/drivers/base/core.c b/drivers/base/core.c index 32f3f09a42e..a293a788abd 100644 --- a/drivers/base/core.c +++ b/drivers/base/core.c @@ -36,7 +36,7 @@ dev_attr_show(struct kobject * kobj, struct attribute * attr, char * buf) { struct device_attribute * dev_attr = to_dev_attr(attr); struct device * dev = to_dev(kobj); - ssize_t ret = 0; + ssize_t ret = -EIO; if (dev_attr->show) ret = dev_attr->show(dev, buf); @@ -49,7 +49,7 @@ dev_attr_store(struct kobject * kobj, struct attribute * attr, { struct device_attribute * dev_attr = to_dev_attr(attr); struct device * dev = to_dev(kobj); - ssize_t ret = 0; + ssize_t ret = -EIO; if (dev_attr->store) ret = dev_attr->store(dev, buf, count); diff --git a/drivers/base/sys.c b/drivers/base/sys.c index 9102e3756f9..f37a13de804 100644 --- a/drivers/base/sys.c +++ b/drivers/base/sys.c @@ -37,7 +37,7 @@ sysdev_show(struct kobject * kobj, struct attribute * attr, char * buffer) if (sysdev_attr->show) return sysdev_attr->show(sysdev, buffer); - return 0; + return -EIO; } @@ -50,7 +50,7 @@ sysdev_store(struct kobject * kobj, struct attribute * attr, if (sysdev_attr->store) return sysdev_attr->store(sysdev, buffer, count); - return 0; + return -EIO; } static struct sysfs_ops sysfs_ops = { -- cgit v1.2.3 From e9ba6365fd4f0d9e7d022c883bd044fbaa48257f Mon Sep 17 00:00:00 2001 From: "gregkh@suse.de" Date: Tue, 15 Mar 2005 11:54:21 -0800 Subject: [PATCH] CLASS: move a "simple" class logic into the class core. One step on improving the class api so that it can not be used incorrectly. This also fixes the module owner issue with the dev files that happened when the devt logic moved to the class core. Based on a patch originally written by Kay Sievers Signed-off-by: Greg Kroah-Hartman --- drivers/base/class.c | 145 +++++++++++++++++++++++++++++++++++++++++++++++---- 1 file changed, 134 insertions(+), 11 deletions(-) (limited to 'drivers/base') diff --git a/drivers/base/class.c b/drivers/base/class.c index 344b8cd7390..a3b006b6f2b 100644 --- a/drivers/base/class.c +++ b/drivers/base/class.c @@ -16,6 +16,7 @@ #include #include #include +#include #include "base.h" #define to_class_attr(_attr) container_of(_attr, struct class_attribute, attr) @@ -162,6 +163,51 @@ void class_unregister(struct class * cls) subsystem_unregister(&cls->subsys); } +static void class_create_release(struct class *cls) +{ + kfree(cls); +} + +static void class_device_create_release(struct class_device *class_dev) +{ + kfree(class_dev); +} + +struct class *class_create(struct module *owner, char *name) +{ + struct class *cls; + int retval; + + cls = kmalloc(sizeof(struct class), GFP_KERNEL); + if (!cls) { + retval = -ENOMEM; + goto error; + } + memset(cls, 0x00, sizeof(struct class)); + + cls->name = name; + cls->owner = owner; + cls->class_release = class_create_release; + cls->release = class_device_create_release; + + retval = class_register(cls); + if (retval) + goto error; + + return cls; + +error: + kfree(cls); + return ERR_PTR(retval); +} + +void class_destroy(struct class *cls) +{ + if ((cls == NULL) || (IS_ERR(cls))) + return; + + class_unregister(cls); +} /* Class Device Stuff */ @@ -375,7 +421,6 @@ static ssize_t show_dev(struct class_device *class_dev, char *buf) { return print_dev_t(buf, class_dev->devt); } -static CLASS_DEVICE_ATTR(dev, S_IRUGO, show_dev, NULL); void class_device_initialize(struct class_device *class_dev) { @@ -412,7 +457,31 @@ int class_device_add(struct class_device *class_dev) if ((error = kobject_add(&class_dev->kobj))) goto register_done; - /* now take care of our own registration */ + /* add the needed attributes to this device */ + if (MAJOR(class_dev->devt)) { + struct class_device_attribute *attr; + attr = kmalloc(sizeof(*attr), GFP_KERNEL); + if (!attr) { + error = -ENOMEM; + kobject_del(&class_dev->kobj); + goto register_done; + } + memset(attr, sizeof(*attr), 0x00); + attr->attr.name = "dev"; + attr->attr.mode = S_IRUGO; + attr->attr.owner = parent->owner; + attr->show = show_dev; + attr->store = NULL; + class_device_create_file(class_dev, attr); + class_dev->devt_attr = attr; + } + + class_device_add_attrs(class_dev); + if (class_dev->dev) + sysfs_create_link(&class_dev->kobj, + &class_dev->dev->kobj, "device"); + + /* notify any interfaces this device is now here */ if (parent) { down(&parent->sem); list_add_tail(&class_dev->node, &parent->children); @@ -421,16 +490,8 @@ int class_device_add(struct class_device *class_dev) class_intf->add(class_dev); up(&parent->sem); } - - if (MAJOR(class_dev->devt)) - class_device_create_file(class_dev, &class_device_attr_dev); - - class_device_add_attrs(class_dev); - if (class_dev->dev) - sysfs_create_link(&class_dev->kobj, - &class_dev->dev->kobj, "device"); - kobject_hotplug(&class_dev->kobj, KOBJ_ADD); + register_done: if (error && parent) class_put(parent); @@ -444,6 +505,41 @@ int class_device_register(struct class_device *class_dev) return class_device_add(class_dev); } +struct class_device *class_device_create(struct class *cls, dev_t devt, + struct device *device, char *fmt, ...) +{ + va_list args; + struct class_device *class_dev = NULL; + int retval = -ENODEV; + + if (cls == NULL || IS_ERR(cls)) + goto error; + + class_dev = kmalloc(sizeof(struct class_device), GFP_KERNEL); + if (!class_dev) { + retval = -ENOMEM; + goto error; + } + memset(class_dev, 0x00, sizeof(struct class_device)); + + class_dev->devt = devt; + class_dev->dev = device; + class_dev->class = cls; + + va_start(args, fmt); + vsnprintf(class_dev->class_id, BUS_ID_SIZE, fmt, args); + va_end(args); + retval = class_device_register(class_dev); + if (retval) + goto error; + + return class_dev; + +error: + kfree(class_dev); + return ERR_PTR(retval); +} + void class_device_del(struct class_device *class_dev) { struct class * parent = class_dev->class; @@ -460,6 +556,11 @@ void class_device_del(struct class_device *class_dev) if (class_dev->dev) sysfs_remove_link(&class_dev->kobj, "device"); + if (class_dev->devt_attr) { + class_device_remove_file(class_dev, class_dev->devt_attr); + kfree(class_dev->devt_attr); + class_dev->devt_attr = NULL; + } class_device_remove_attrs(class_dev); kobject_hotplug(&class_dev->kobj, KOBJ_REMOVE); @@ -477,6 +578,24 @@ void class_device_unregister(struct class_device *class_dev) class_device_put(class_dev); } +void class_device_destroy(struct class *cls, dev_t devt) +{ + struct class_device *class_dev = NULL; + struct class_device *class_dev_tmp; + + down(&cls->sem); + list_for_each_entry(class_dev_tmp, &cls->children, node) { + if (class_dev_tmp->devt == devt) { + class_dev = class_dev_tmp; + break; + } + } + up(&cls->sem); + + if (class_dev) + class_device_unregister(class_dev); +} + int class_device_rename(struct class_device *class_dev, char *new_name) { int error = 0; @@ -576,6 +695,8 @@ EXPORT_SYMBOL_GPL(class_register); EXPORT_SYMBOL_GPL(class_unregister); EXPORT_SYMBOL_GPL(class_get); EXPORT_SYMBOL_GPL(class_put); +EXPORT_SYMBOL_GPL(class_create); +EXPORT_SYMBOL_GPL(class_destroy); EXPORT_SYMBOL_GPL(class_device_register); EXPORT_SYMBOL_GPL(class_device_unregister); @@ -584,6 +705,8 @@ EXPORT_SYMBOL_GPL(class_device_add); EXPORT_SYMBOL_GPL(class_device_del); EXPORT_SYMBOL_GPL(class_device_get); EXPORT_SYMBOL_GPL(class_device_put); +EXPORT_SYMBOL_GPL(class_device_create); +EXPORT_SYMBOL_GPL(class_device_destroy); EXPORT_SYMBOL_GPL(class_device_create_file); EXPORT_SYMBOL_GPL(class_device_remove_file); EXPORT_SYMBOL_GPL(class_device_create_bin_file); -- cgit v1.2.3 From 2fc68447df5c76cf254037047b4b02551bd5d760 Mon Sep 17 00:00:00 2001 From: "gregkh@suse.de" Date: Wed, 23 Mar 2005 10:02:56 -0800 Subject: [PATCH] class: add kerneldoc for the new class functions. Signed-off-by: Greg Kroah-Hartman --- drivers/base/class.c | 43 +++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 43 insertions(+) (limited to 'drivers/base') diff --git a/drivers/base/class.c b/drivers/base/class.c index a3b006b6f2b..479c1257088 100644 --- a/drivers/base/class.c +++ b/drivers/base/class.c @@ -173,6 +173,17 @@ static void class_device_create_release(struct class_device *class_dev) kfree(class_dev); } +/** + * class_create - create a struct class structure + * @owner: pointer to the module that is to "own" this struct class + * @name: pointer to a string for the name of this class. + * + * This is used to create a struct class pointer that can then be used + * in calls to class_device_create(). + * + * Note, the pointer created here is to be destroyed when finished by + * making a call to class_destroy(). + */ struct class *class_create(struct module *owner, char *name) { struct class *cls; @@ -201,6 +212,13 @@ error: return ERR_PTR(retval); } +/** + * class_destroy - destroys a struct class structure + * @cs: pointer to the struct class that is to be destroyed + * + * Note, the pointer to be destroyed must have been created with a call + * to class_create(). + */ void class_destroy(struct class *cls) { if ((cls == NULL) || (IS_ERR(cls))) @@ -505,6 +523,23 @@ int class_device_register(struct class_device *class_dev) return class_device_add(class_dev); } +/** + * class_device_create - creates a class device and registers it with sysfs + * @cs: pointer to the struct class that this device should be registered to. + * @dev: the dev_t for the char device to be added. + * @device: a pointer to a struct device that is assiociated with this class device. + * @fmt: string for the class device's name + * + * This function can be used by char device classes. A struct + * class_device will be created in sysfs, registered to the specified + * class. A "dev" file will be created, showing the dev_t for the + * device. The pointer to the struct class_device will be returned from + * the call. Any further sysfs files that might be required can be + * created using this pointer. + * + * Note: the struct class passed to this function must have previously + * been created with a call to class_create(). + */ struct class_device *class_device_create(struct class *cls, dev_t devt, struct device *device, char *fmt, ...) { @@ -578,6 +613,14 @@ void class_device_unregister(struct class_device *class_dev) class_device_put(class_dev); } +/** + * class_device_destroy - removes a class device that was created with class_device_create() + * @cls: the pointer to the struct class that this device was registered * with. + * @dev: the dev_t of the device that was previously registered. + * + * This call unregisters and cleans up a class device that was created with a + * call to class_device_create() + */ void class_device_destroy(struct class *cls, dev_t devt) { struct class_device *class_dev = NULL; -- cgit v1.2.3 From cd987d38cc59053e0bab8150ffaca33b109067f3 Mon Sep 17 00:00:00 2001 From: "gregkh@suse.de" Date: Wed, 23 Mar 2005 11:12:38 -0800 Subject: [PATCH] class: remove class_simple code, as no one in the tree is using it anymore. Signed-off-by: Greg Kroah-Hartman --- drivers/base/Makefile | 2 +- drivers/base/class_simple.c | 199 -------------------------------------------- 2 files changed, 1 insertion(+), 200 deletions(-) delete mode 100644 drivers/base/class_simple.c (limited to 'drivers/base') diff --git a/drivers/base/Makefile b/drivers/base/Makefile index a47928a2e57..771f6c80e82 100644 --- a/drivers/base/Makefile +++ b/drivers/base/Makefile @@ -1,7 +1,7 @@ # Makefile for the Linux device tree obj-y := core.o sys.o bus.o \ - driver.o class.o class_simple.o platform.o \ + driver.o class.o platform.o \ cpu.o firmware.o init.o map.o dmapool.o \ attribute_container.o transport_class.o obj-y += power/ diff --git a/drivers/base/class_simple.c b/drivers/base/class_simple.c deleted file mode 100644 index 27699eb20a3..00000000000 --- a/drivers/base/class_simple.c +++ /dev/null @@ -1,199 +0,0 @@ -/* - * class_simple.c - a "simple" interface for classes for simple char devices. - * - * Copyright (c) 2003-2004 Greg Kroah-Hartman - * Copyright (c) 2003-2004 IBM Corp. - * - * This file is released under the GPLv2 - * - */ - -#include -#include -#include - -struct class_simple { - struct class class; -}; -#define to_class_simple(d) container_of(d, struct class_simple, class) - -struct simple_dev { - struct list_head node; - struct class_device class_dev; -}; -#define to_simple_dev(d) container_of(d, struct simple_dev, class_dev) - -static LIST_HEAD(simple_dev_list); -static DEFINE_SPINLOCK(simple_dev_list_lock); - -static void release_simple_dev(struct class_device *class_dev) -{ - struct simple_dev *s_dev = to_simple_dev(class_dev); - kfree(s_dev); -} - -static void class_simple_release(struct class *class) -{ - struct class_simple *cs = to_class_simple(class); - kfree(cs); -} - -/** - * class_simple_create - create a struct class_simple structure - * @owner: pointer to the module that is to "own" this struct class_simple - * @name: pointer to a string for the name of this class. - * - * This is used to create a struct class_simple pointer that can then be used - * in calls to class_simple_device_add(). This is used when you do not wish to - * create a full blown class support for a type of char devices. - * - * Note, the pointer created here is to be destroyed when finished by making a - * call to class_simple_destroy(). - */ -struct class_simple *class_simple_create(struct module *owner, char *name) -{ - struct class_simple *cs; - int retval; - - cs = kmalloc(sizeof(*cs), GFP_KERNEL); - if (!cs) { - retval = -ENOMEM; - goto error; - } - memset(cs, 0x00, sizeof(*cs)); - - cs->class.name = name; - cs->class.class_release = class_simple_release; - cs->class.release = release_simple_dev; - - retval = class_register(&cs->class); - if (retval) - goto error; - - return cs; - -error: - kfree(cs); - return ERR_PTR(retval); -} -EXPORT_SYMBOL(class_simple_create); - -/** - * class_simple_destroy - destroys a struct class_simple structure - * @cs: pointer to the struct class_simple that is to be destroyed - * - * Note, the pointer to be destroyed must have been created with a call to - * class_simple_create(). - */ -void class_simple_destroy(struct class_simple *cs) -{ - if ((cs == NULL) || (IS_ERR(cs))) - return; - - class_unregister(&cs->class); -} -EXPORT_SYMBOL(class_simple_destroy); - -/** - * class_simple_device_add - adds a class device to sysfs for a character driver - * @cs: pointer to the struct class_simple that this device should be registered to. - * @dev: the dev_t for the device to be added. - * @device: a pointer to a struct device that is assiociated with this class device. - * @fmt: string for the class device's name - * - * This function can be used by simple char device classes that do not - * implement their own class device registration. A struct class_device will - * be created in sysfs, registered to the specified class. A "dev" file will - * be created, showing the dev_t for the device. The pointer to the struct - * class_device will be returned from the call. Any further sysfs files that - * might be required can be created using this pointer. - * Note: the struct class_simple passed to this function must have previously been - * created with a call to class_simple_create(). - */ -struct class_device *class_simple_device_add(struct class_simple *cs, dev_t dev, struct device *device, const char *fmt, ...) -{ - va_list args; - struct simple_dev *s_dev = NULL; - int retval; - - if ((cs == NULL) || (IS_ERR(cs))) { - retval = -ENODEV; - goto error; - } - - s_dev = kmalloc(sizeof(*s_dev), GFP_KERNEL); - if (!s_dev) { - retval = -ENOMEM; - goto error; - } - memset(s_dev, 0x00, sizeof(*s_dev)); - - s_dev->class_dev.devt = dev; - s_dev->class_dev.dev = device; - s_dev->class_dev.class = &cs->class; - - va_start(args, fmt); - vsnprintf(s_dev->class_dev.class_id, BUS_ID_SIZE, fmt, args); - va_end(args); - retval = class_device_register(&s_dev->class_dev); - if (retval) - goto error; - - spin_lock(&simple_dev_list_lock); - list_add(&s_dev->node, &simple_dev_list); - spin_unlock(&simple_dev_list_lock); - - return &s_dev->class_dev; - -error: - kfree(s_dev); - return ERR_PTR(retval); -} -EXPORT_SYMBOL(class_simple_device_add); - -/** - * class_simple_set_hotplug - set the hotplug callback in the embedded struct class - * @cs: pointer to the struct class_simple to hold the pointer - * @hotplug: function pointer to the hotplug function - * - * Implement and set a hotplug function to add environment variables specific to this - * class on the hotplug event. - */ -int class_simple_set_hotplug(struct class_simple *cs, - int (*hotplug)(struct class_device *dev, char **envp, int num_envp, char *buffer, int buffer_size)) -{ - if ((cs == NULL) || (IS_ERR(cs))) - return -ENODEV; - cs->class.hotplug = hotplug; - return 0; -} -EXPORT_SYMBOL(class_simple_set_hotplug); - -/** - * class_simple_device_remove - removes a class device that was created with class_simple_device_add() - * @dev: the dev_t of the device that was previously registered. - * - * This call unregisters and cleans up a class device that was created with a - * call to class_device_simple_add() - */ -void class_simple_device_remove(dev_t dev) -{ - struct simple_dev *s_dev = NULL; - int found = 0; - - spin_lock(&simple_dev_list_lock); - list_for_each_entry(s_dev, &simple_dev_list, node) { - if (s_dev->class_dev.devt == dev) { - found = 1; - break; - } - } - if (found) { - list_del(&s_dev->node); - spin_unlock(&simple_dev_list_lock); - class_device_unregister(&s_dev->class_dev); - } else { - spin_unlock(&simple_dev_list_lock); - } -} -EXPORT_SYMBOL(class_simple_device_remove); -- cgit v1.2.3 From af70316af182f4716cc5eec7e0d27fc731d164bd Mon Sep 17 00:00:00 2001 From: "mochel@digitalimplant.org" Date: Mon, 21 Mar 2005 10:41:04 -0800 Subject: [PATCH] Add a semaphore to struct device to synchronize calls to its driver. This adds a per-device semaphore that is taken before every call from the core to a driver method. This prevents e.g. simultaneous calls to the ->suspend() or ->resume() and ->probe() or ->release(), potentially saving a whole lot of headaches. It also moves us a step closer to removing the bus rwsem, since it protects the fields in struct device that are modified by the core. Signed-off-by: Patrick Mochel Signed-off-by: Greg Kroah-Hartman --- drivers/base/bus.c | 14 +++++++++++--- drivers/base/core.c | 1 + drivers/base/power/resume.c | 8 ++++++-- drivers/base/power/suspend.c | 3 ++- 4 files changed, 20 insertions(+), 6 deletions(-) (limited to 'drivers/base') diff --git a/drivers/base/bus.c b/drivers/base/bus.c index 80ce88de56f..aa27f76d28c 100644 --- a/drivers/base/bus.c +++ b/drivers/base/bus.c @@ -283,18 +283,22 @@ void device_bind_driver(struct device * dev) */ int driver_probe_device(struct device_driver * drv, struct device * dev) { + int error = 0; + if (drv->bus->match && !drv->bus->match(dev, drv)) return -ENODEV; + down(&dev->sem); dev->driver = drv; if (drv->probe) { - int error = drv->probe(dev); + error = drv->probe(dev); if (error) { dev->driver = NULL; + up(&dev->sem); return error; } } - + up(&dev->sem); device_bind_driver(dev); return 0; } @@ -385,7 +389,10 @@ void driver_attach(struct device_driver * drv) void device_release_driver(struct device * dev) { - struct device_driver * drv = dev->driver; + struct device_driver * drv; + + down(&dev->sem); + drv = dev->driver; if (drv) { sysfs_remove_link(&drv->kobj, kobject_name(&dev->kobj)); sysfs_remove_link(&dev->kobj, "driver"); @@ -394,6 +401,7 @@ void device_release_driver(struct device * dev) drv->remove(dev); dev->driver = NULL; } + up(&dev->sem); } diff --git a/drivers/base/core.c b/drivers/base/core.c index a293a788abd..93440824b80 100644 --- a/drivers/base/core.c +++ b/drivers/base/core.c @@ -212,6 +212,7 @@ void device_initialize(struct device *dev) INIT_LIST_HEAD(&dev->driver_list); INIT_LIST_HEAD(&dev->bus_list); INIT_LIST_HEAD(&dev->dma_pools); + init_MUTEX(&dev->sem); } /** diff --git a/drivers/base/power/resume.c b/drivers/base/power/resume.c index 26468971ef5..bdd96b03b88 100644 --- a/drivers/base/power/resume.c +++ b/drivers/base/power/resume.c @@ -22,6 +22,9 @@ extern int sysdev_resume(void); int resume_device(struct device * dev) { + int error = 0; + + down(&dev->sem); if (dev->power.pm_parent && dev->power.pm_parent->power.power_state) { dev_err(dev, "PM: resume from %d, parent %s still %d\n", @@ -31,9 +34,10 @@ int resume_device(struct device * dev) } if (dev->bus && dev->bus->resume) { dev_dbg(dev,"resuming\n"); - return dev->bus->resume(dev); + error = dev->bus->resume(dev); } - return 0; + up(&dev->sem); + return error; } diff --git a/drivers/base/power/suspend.c b/drivers/base/power/suspend.c index 0ec44ef840b..807e13fb205 100644 --- a/drivers/base/power/suspend.c +++ b/drivers/base/power/suspend.c @@ -39,6 +39,7 @@ int suspend_device(struct device * dev, pm_message_t state) { int error = 0; + down(&dev->sem); if (dev->power.power_state) { dev_dbg(dev, "PM: suspend %d-->%d\n", dev->power.power_state, state); @@ -58,7 +59,7 @@ int suspend_device(struct device * dev, pm_message_t state) dev_dbg(dev, "suspending\n"); error = dev->bus->suspend(dev, state); } - + up(&dev->sem); return error; } -- cgit v1.2.3 From 07e4a3e27fe414980ddc85a358e5a56abc48b363 Mon Sep 17 00:00:00 2001 From: "mochel@digitalimplant.org" Date: Mon, 21 Mar 2005 10:52:54 -0800 Subject: [PATCH] Move device/driver code to drivers/base/dd.c This relocates the driver binding/unbinding code to drivers/base/dd.c. This is done for two reasons: One, it's not code related to the bus_type itself; it uses some from that, some from devices, and some from drivers. And Two, it will make it easier to do some of the upcoming lock removal on that code.. Signed-off-by: Patrick Mochel Signed-off-by: Greg Kroah-Hartman --- drivers/base/Makefile | 2 +- drivers/base/base.h | 2 + drivers/base/bus.c | 182 --------------------------------------------- drivers/base/dd.c | 200 ++++++++++++++++++++++++++++++++++++++++++++++++++ 4 files changed, 203 insertions(+), 183 deletions(-) create mode 100644 drivers/base/dd.c (limited to 'drivers/base') diff --git a/drivers/base/Makefile b/drivers/base/Makefile index 771f6c80e82..66d9c4643fc 100644 --- a/drivers/base/Makefile +++ b/drivers/base/Makefile @@ -1,6 +1,6 @@ # Makefile for the Linux device tree -obj-y := core.o sys.o bus.o \ +obj-y := core.o sys.o bus.o dd.o \ driver.o class.o platform.o \ cpu.o firmware.o init.o map.o dmapool.o \ attribute_container.o transport_class.o diff --git a/drivers/base/base.h b/drivers/base/base.h index 8d1e8bd4863..645f6269292 100644 --- a/drivers/base/base.h +++ b/drivers/base/base.h @@ -4,6 +4,8 @@ extern void bus_remove_device(struct device * dev); extern int bus_add_driver(struct device_driver *); extern void bus_remove_driver(struct device_driver *); +extern void driver_detach(struct device_driver * drv); + static inline struct class_device *to_class_dev(struct kobject *obj) { return container_of(obj, struct class_device, kobj); diff --git a/drivers/base/bus.c b/drivers/base/bus.c index aa27f76d28c..1b05c1399e3 100644 --- a/drivers/base/bus.c +++ b/drivers/base/bus.c @@ -18,7 +18,6 @@ #include "power/power.h" #define to_dev(node) container_of(node, struct device, bus_list) -#define to_drv(node) container_of(node, struct device_driver, kobj.entry) #define to_bus_attr(_attr) container_of(_attr, struct bus_attribute, attr) #define to_bus(obj) container_of(obj, struct bus_type, subsys.kset.kobj) @@ -243,181 +242,6 @@ int bus_for_each_drv(struct bus_type * bus, struct device_driver * start, return ret; } -/** - * device_bind_driver - bind a driver to one device. - * @dev: device. - * - * Allow manual attachment of a driver to a device. - * Caller must have already set @dev->driver. - * - * Note that this does not modify the bus reference count - * nor take the bus's rwsem. Please verify those are accounted - * for before calling this. (It is ok to call with no other effort - * from a driver's probe() method.) - */ - -void device_bind_driver(struct device * dev) -{ - pr_debug("bound device '%s' to driver '%s'\n", - dev->bus_id, dev->driver->name); - list_add_tail(&dev->driver_list, &dev->driver->devices); - sysfs_create_link(&dev->driver->kobj, &dev->kobj, - kobject_name(&dev->kobj)); - sysfs_create_link(&dev->kobj, &dev->driver->kobj, "driver"); -} - - -/** - * driver_probe_device - attempt to bind device & driver. - * @drv: driver. - * @dev: device. - * - * First, we call the bus's match function, if one present, which - * should compare the device IDs the driver supports with the - * device IDs of the device. Note we don't do this ourselves - * because we don't know the format of the ID structures, nor what - * is to be considered a match and what is not. - * - * If we find a match, we call @drv->probe(@dev) if it exists, and - * call device_bind_driver() above. - */ -int driver_probe_device(struct device_driver * drv, struct device * dev) -{ - int error = 0; - - if (drv->bus->match && !drv->bus->match(dev, drv)) - return -ENODEV; - - down(&dev->sem); - dev->driver = drv; - if (drv->probe) { - error = drv->probe(dev); - if (error) { - dev->driver = NULL; - up(&dev->sem); - return error; - } - } - up(&dev->sem); - device_bind_driver(dev); - return 0; -} - - -/** - * device_attach - try to attach device to a driver. - * @dev: device. - * - * Walk the list of drivers that the bus has and call - * driver_probe_device() for each pair. If a compatible - * pair is found, break out and return. - */ -int device_attach(struct device * dev) -{ - struct bus_type * bus = dev->bus; - struct list_head * entry; - int error; - - if (dev->driver) { - device_bind_driver(dev); - return 1; - } - - if (bus->match) { - list_for_each(entry, &bus->drivers.list) { - struct device_driver * drv = to_drv(entry); - error = driver_probe_device(drv, dev); - if (!error) - /* success, driver matched */ - return 1; - if (error != -ENODEV && error != -ENXIO) - /* driver matched but the probe failed */ - printk(KERN_WARNING - "%s: probe of %s failed with error %d\n", - drv->name, dev->bus_id, error); - } - } - - return 0; -} - - -/** - * driver_attach - try to bind driver to devices. - * @drv: driver. - * - * Walk the list of devices that the bus has on it and try to - * match the driver with each one. If driver_probe_device() - * returns 0 and the @dev->driver is set, we've found a - * compatible pair. - * - * Note that we ignore the -ENODEV error from driver_probe_device(), - * since it's perfectly valid for a driver not to bind to any devices. - */ -void driver_attach(struct device_driver * drv) -{ - struct bus_type * bus = drv->bus; - struct list_head * entry; - int error; - - if (!bus->match) - return; - - list_for_each(entry, &bus->devices.list) { - struct device * dev = container_of(entry, struct device, bus_list); - if (!dev->driver) { - error = driver_probe_device(drv, dev); - if (error && (error != -ENODEV)) - /* driver matched but the probe failed */ - printk(KERN_WARNING - "%s: probe of %s failed with error %d\n", - drv->name, dev->bus_id, error); - } - } -} - - -/** - * device_release_driver - manually detach device from driver. - * @dev: device. - * - * Manually detach device from driver. - * Note that this is called without incrementing the bus - * reference count nor taking the bus's rwsem. Be sure that - * those are accounted for before calling this function. - */ - -void device_release_driver(struct device * dev) -{ - struct device_driver * drv; - - down(&dev->sem); - drv = dev->driver; - if (drv) { - sysfs_remove_link(&drv->kobj, kobject_name(&dev->kobj)); - sysfs_remove_link(&dev->kobj, "driver"); - list_del_init(&dev->driver_list); - if (drv->remove) - drv->remove(dev); - dev->driver = NULL; - } - up(&dev->sem); -} - - -/** - * driver_detach - detach driver from all devices it controls. - * @drv: driver. - */ - -static void driver_detach(struct device_driver * drv) -{ - while (!list_empty(&drv->devices)) { - struct device * dev = container_of(drv->devices.next, struct device, driver_list); - device_release_driver(dev); - } -} - static int device_add_attrs(struct bus_type * bus, struct device * dev) { int error = 0; @@ -757,12 +581,6 @@ int __init buses_init(void) EXPORT_SYMBOL_GPL(bus_for_each_dev); EXPORT_SYMBOL_GPL(bus_for_each_drv); -EXPORT_SYMBOL_GPL(driver_probe_device); -EXPORT_SYMBOL_GPL(device_bind_driver); -EXPORT_SYMBOL_GPL(device_release_driver); -EXPORT_SYMBOL_GPL(device_attach); -EXPORT_SYMBOL_GPL(driver_attach); - EXPORT_SYMBOL_GPL(bus_add_device); EXPORT_SYMBOL_GPL(bus_remove_device); EXPORT_SYMBOL_GPL(bus_register); diff --git a/drivers/base/dd.c b/drivers/base/dd.c new file mode 100644 index 00000000000..b709b1e0cb2 --- /dev/null +++ b/drivers/base/dd.c @@ -0,0 +1,200 @@ +/* + * drivers/base/dd.c - The core device/driver interactions. + * + * This file contains the (sometimes tricky) code that controls the + * interactions between devices and drivers, which primarily includes + * driver binding and unbinding. + * + * All of this code used to exist in drivers/base/bus.c, but was + * relocated to here in the name of compartmentalization (since it wasn't + * strictly code just for the 'struct bus_type'. + * + * Copyright (c) 2002-5 Patrick Mochel + * Copyright (c) 2002-3 Open Source Development Labs + * + * This file is released under the GPLv2 + */ + +#include +#include + +#include "base.h" +#include "power/power.h" + +#define to_drv(node) container_of(node, struct device_driver, kobj.entry) + + +/** + * device_bind_driver - bind a driver to one device. + * @dev: device. + * + * Allow manual attachment of a driver to a device. + * Caller must have already set @dev->driver. + * + * Note that this does not modify the bus reference count + * nor take the bus's rwsem. Please verify those are accounted + * for before calling this. (It is ok to call with no other effort + * from a driver's probe() method.) + */ +void device_bind_driver(struct device * dev) +{ + pr_debug("bound device '%s' to driver '%s'\n", + dev->bus_id, dev->driver->name); + list_add_tail(&dev->driver_list, &dev->driver->devices); + sysfs_create_link(&dev->driver->kobj, &dev->kobj, + kobject_name(&dev->kobj)); + sysfs_create_link(&dev->kobj, &dev->driver->kobj, "driver"); +} + +/** + * driver_probe_device - attempt to bind device & driver. + * @drv: driver. + * @dev: device. + * + * First, we call the bus's match function, if one present, which + * should compare the device IDs the driver supports with the + * device IDs of the device. Note we don't do this ourselves + * because we don't know the format of the ID structures, nor what + * is to be considered a match and what is not. + * + * If we find a match, we call @drv->probe(@dev) if it exists, and + * call device_bind_driver() above. + */ +int driver_probe_device(struct device_driver * drv, struct device * dev) +{ + int error = 0; + + if (drv->bus->match && !drv->bus->match(dev, drv)) + return -ENODEV; + + down(&dev->sem); + dev->driver = drv; + if (drv->probe) { + error = drv->probe(dev); + if (error) { + dev->driver = NULL; + up(&dev->sem); + return error; + } + } + up(&dev->sem); + device_bind_driver(dev); + return 0; +} + +/** + * device_attach - try to attach device to a driver. + * @dev: device. + * + * Walk the list of drivers that the bus has and call + * driver_probe_device() for each pair. If a compatible + * pair is found, break out and return. + */ +int device_attach(struct device * dev) +{ + struct bus_type * bus = dev->bus; + struct list_head * entry; + int error; + + if (dev->driver) { + device_bind_driver(dev); + return 1; + } + + if (bus->match) { + list_for_each(entry, &bus->drivers.list) { + struct device_driver * drv = to_drv(entry); + error = driver_probe_device(drv, dev); + if (!error) + /* success, driver matched */ + return 1; + if (error != -ENODEV && error != -ENXIO) + /* driver matched but the probe failed */ + printk(KERN_WARNING + "%s: probe of %s failed with error %d\n", + drv->name, dev->bus_id, error); + } + } + + return 0; +} + +/** + * driver_attach - try to bind driver to devices. + * @drv: driver. + * + * Walk the list of devices that the bus has on it and try to + * match the driver with each one. If driver_probe_device() + * returns 0 and the @dev->driver is set, we've found a + * compatible pair. + * + * Note that we ignore the -ENODEV error from driver_probe_device(), + * since it's perfectly valid for a driver not to bind to any devices. + */ +void driver_attach(struct device_driver * drv) +{ + struct bus_type * bus = drv->bus; + struct list_head * entry; + int error; + + if (!bus->match) + return; + + list_for_each(entry, &bus->devices.list) { + struct device * dev = container_of(entry, struct device, bus_list); + if (!dev->driver) { + error = driver_probe_device(drv, dev); + if (error && (error != -ENODEV)) + /* driver matched but the probe failed */ + printk(KERN_WARNING + "%s: probe of %s failed with error %d\n", + drv->name, dev->bus_id, error); + } + } +} + +/** + * device_release_driver - manually detach device from driver. + * @dev: device. + * + * Manually detach device from driver. + * Note that this is called without incrementing the bus + * reference count nor taking the bus's rwsem. Be sure that + * those are accounted for before calling this function. + */ +void device_release_driver(struct device * dev) +{ + struct device_driver * drv; + + down(&dev->sem); + drv = dev->driver; + if (drv) { + sysfs_remove_link(&drv->kobj, kobject_name(&dev->kobj)); + sysfs_remove_link(&dev->kobj, "driver"); + list_del_init(&dev->driver_list); + if (drv->remove) + drv->remove(dev); + dev->driver = NULL; + } + up(&dev->sem); +} + +/** + * driver_detach - detach driver from all devices it controls. + * @drv: driver. + */ +void driver_detach(struct device_driver * drv) +{ + while (!list_empty(&drv->devices)) { + struct device * dev = container_of(drv->devices.next, struct device, driver_list); + device_release_driver(dev); + } +} + + +EXPORT_SYMBOL_GPL(driver_probe_device); +EXPORT_SYMBOL_GPL(device_bind_driver); +EXPORT_SYMBOL_GPL(device_release_driver); +EXPORT_SYMBOL_GPL(device_attach); +EXPORT_SYMBOL_GPL(driver_attach); + -- cgit v1.2.3 From fae3cd00255e3e51ffd59fedb1bdb91ec96be395 Mon Sep 17 00:00:00 2001 From: "mochel@digitalimplant.org" Date: Mon, 21 Mar 2005 10:59:56 -0800 Subject: [PATCH] Add driver_for_each_device(). Now there's an iterator for accessing each device bound to a driver. Signed-off-by: Patrick Mochel Signed-off-by: Greg Kroah-Hartman Index: linux-2.6.12-rc2/drivers/base/driver.c =================================================================== --- drivers/base/driver.c | 35 +++++++++++++++++++++++++++++++++++ 1 file changed, 35 insertions(+) (limited to 'drivers/base') diff --git a/drivers/base/driver.c b/drivers/base/driver.c index 3b269f7e521..484fed1985a 100644 --- a/drivers/base/driver.c +++ b/drivers/base/driver.c @@ -18,6 +18,41 @@ #define to_dev(node) container_of(node, struct device, driver_list) #define to_drv(obj) container_of(obj, struct device_driver, kobj) + +/** + * driver_for_each_device - Iterator for devices bound to a driver. + * @drv: Driver we're iterating. + * @data: Data to pass to the callback. + * @fn: Function to call for each device. + * + * Take the bus's rwsem and iterate over the @drv's list of devices, + * calling @fn for each one. + */ + +int driver_for_each_device(struct device_driver * drv, struct device * start, + void * data, int (*fn)(struct device *, void *)) +{ + struct list_head * head; + struct device * dev; + int error = 0; + + down_read(&drv->bus->subsys.rwsem); + head = &drv->devices; + dev = list_prepare_entry(start, head, driver_list); + list_for_each_entry_continue(dev, head, driver_list) { + get_device(dev); + error = fn(dev, data); + put_device(dev); + if (error) + break; + } + up_read(&drv->bus->subsys.rwsem); + return error; +} + +EXPORT_SYMBOL(driver_for_each_device); + + /** * driver_create_file - create sysfs file for driver. * @drv: driver. -- cgit v1.2.3 From 465c7a3a3a5aabcedd2e43612cac5a12f23da19a Mon Sep 17 00:00:00 2001 From: "mochel@digitalimplant.org" Date: Mon, 21 Mar 2005 11:49:14 -0800 Subject: [PATCH] Add a klist to struct bus_type for its devices. - Use it for bus_for_each_dev(). - Use the klist spinlock instead of the bus rwsem. Signed-off-by: Patrick Mochel Signed-off-by: Greg Kroah-Hartman --- drivers/base/bus.c | 54 ++++++++++++++++++++++++------------------------------ 1 file changed, 24 insertions(+), 30 deletions(-) (limited to 'drivers/base') diff --git a/drivers/base/bus.c b/drivers/base/bus.c index 1b05c1399e3..c8da37230b3 100644 --- a/drivers/base/bus.c +++ b/drivers/base/bus.c @@ -134,28 +134,6 @@ static struct kobj_type ktype_bus = { decl_subsys(bus, &ktype_bus, NULL); -static int __bus_for_each_dev(struct bus_type *bus, struct device *start, - void *data, int (*fn)(struct device *, void *)) -{ - struct list_head *head; - struct device *dev; - int error = 0; - - if (!(bus = get_bus(bus))) - return -EINVAL; - - head = &bus->devices.list; - dev = list_prepare_entry(start, head, bus_list); - list_for_each_entry_continue(dev, head, bus_list) { - get_device(dev); - error = fn(dev, data); - put_device(dev); - if (error) - break; - } - put_bus(bus); - return error; -} static int __bus_for_each_drv(struct bus_type *bus, struct device_driver *start, void * data, int (*fn)(struct device_driver *, void *)) @@ -180,6 +158,13 @@ static int __bus_for_each_drv(struct bus_type *bus, struct device_driver *start, return error; } + +static struct device * next_device(struct klist_iter * i) +{ + struct klist_node * n = klist_next(i); + return n ? container_of(n, struct device, knode_bus) : NULL; +} + /** * bus_for_each_dev - device iterator. * @bus: bus type. @@ -203,12 +188,19 @@ static int __bus_for_each_drv(struct bus_type *bus, struct device_driver *start, int bus_for_each_dev(struct bus_type * bus, struct device * start, void * data, int (*fn)(struct device *, void *)) { - int ret; + struct klist_iter i; + struct device * dev; + int error = 0; - down_read(&bus->subsys.rwsem); - ret = __bus_for_each_dev(bus, start, data, fn); - up_read(&bus->subsys.rwsem); - return ret; + if (!bus) + return -EINVAL; + + klist_iter_init_node(&bus->klist_devices, &i, + (start ? &start->knode_bus : NULL)); + while ((dev = next_device(&i)) && !error) + error = fn(dev, data); + klist_iter_exit(&i); + return error; } /** @@ -293,6 +285,7 @@ int bus_add_device(struct device * dev) list_add_tail(&dev->bus_list, &dev->bus->devices.list); device_attach(dev); up_write(&dev->bus->subsys.rwsem); + klist_add_tail(&bus->klist_devices, &dev->knode_bus); device_add_attrs(bus, dev); sysfs_create_link(&bus->devices.kobj, &dev->kobj, dev->bus_id); sysfs_create_link(&dev->kobj, &dev->bus->subsys.kset.kobj, "bus"); @@ -315,6 +308,7 @@ void bus_remove_device(struct device * dev) sysfs_remove_link(&dev->kobj, "bus"); sysfs_remove_link(&dev->bus->devices.kobj, dev->bus_id); device_remove_attrs(dev->bus, dev); + klist_remove(&dev->knode_bus); down_write(&dev->bus->subsys.rwsem); pr_debug("bus %s: remove device %s\n", dev->bus->name, dev->bus_id); device_release_driver(dev); @@ -439,9 +433,7 @@ int bus_rescan_devices(struct bus_type * bus) { int count = 0; - down_write(&bus->subsys.rwsem); - __bus_for_each_dev(bus, NULL, &count, bus_rescan_devices_helper); - up_write(&bus->subsys.rwsem); + bus_for_each_dev(bus, NULL, &count, bus_rescan_devices_helper); return count; } @@ -542,6 +534,8 @@ int bus_register(struct bus_type * bus) retval = kset_register(&bus->drivers); if (retval) goto bus_drivers_fail; + + klist_init(&bus->klist_devices); bus_add_attrs(bus); pr_debug("bus type '%s' registered\n", bus->name); -- cgit v1.2.3 From 38fdac3cdce276554b4484a41f8ec2daf81cb2ff Mon Sep 17 00:00:00 2001 From: "mochel@digitalimplant.org" Date: Mon, 21 Mar 2005 12:00:18 -0800 Subject: [PATCH] Add a klist to struct bus_type for its drivers. - Use it in bus_for_each_drv(). - Use the klist spinlock instead of the bus rwsem. Signed-off-by: Patrick Mochel Signed-off-by: Greg Kroah-Hartman --- drivers/base/bus.c | 52 +++++++++++++++++++++++----------------------------- 1 file changed, 23 insertions(+), 29 deletions(-) (limited to 'drivers/base') diff --git a/drivers/base/bus.c b/drivers/base/bus.c index c8da37230b3..736dc1f5a31 100644 --- a/drivers/base/bus.c +++ b/drivers/base/bus.c @@ -135,30 +135,6 @@ static struct kobj_type ktype_bus = { decl_subsys(bus, &ktype_bus, NULL); -static int __bus_for_each_drv(struct bus_type *bus, struct device_driver *start, - void * data, int (*fn)(struct device_driver *, void *)) -{ - struct list_head *head; - struct device_driver *drv; - int error = 0; - - if (!(bus = get_bus(bus))) - return -EINVAL; - - head = &bus->drivers.list; - drv = list_prepare_entry(start, head, kobj.entry); - list_for_each_entry_continue(drv, head, kobj.entry) { - get_driver(drv); - error = fn(drv, data); - put_driver(drv); - if (error) - break; - } - put_bus(bus); - return error; -} - - static struct device * next_device(struct klist_iter * i) { struct klist_node * n = klist_next(i); @@ -203,6 +179,14 @@ int bus_for_each_dev(struct bus_type * bus, struct device * start, return error; } + + +static struct device_driver * next_driver(struct klist_iter * i) +{ + struct klist_node * n = klist_next(i); + return n ? container_of(n, struct device_driver, knode_bus) : NULL; +} + /** * bus_for_each_drv - driver iterator * @bus: bus we're dealing with. @@ -226,12 +210,19 @@ int bus_for_each_dev(struct bus_type * bus, struct device * start, int bus_for_each_drv(struct bus_type * bus, struct device_driver * start, void * data, int (*fn)(struct device_driver *, void *)) { - int ret; + struct klist_iter i; + struct device_driver * drv; + int error = 0; - down_read(&bus->subsys.rwsem); - ret = __bus_for_each_drv(bus, start, data, fn); - up_read(&bus->subsys.rwsem); - return ret; + if (!bus) + return -EINVAL; + + klist_iter_init_node(&bus->klist_drivers, &i, + start ? &start->knode_bus : NULL); + while ((drv = next_driver(&i)) && !error) + error = fn(drv, data); + klist_iter_exit(&i); + return error; } static int device_add_attrs(struct bus_type * bus, struct device * dev) @@ -376,6 +367,7 @@ int bus_add_driver(struct device_driver * drv) down_write(&bus->subsys.rwsem); driver_attach(drv); up_write(&bus->subsys.rwsem); + klist_add_tail(&bus->klist_drivers, &drv->knode_bus); module_add_driver(drv->owner, drv); driver_add_attrs(bus, drv); @@ -397,6 +389,7 @@ void bus_remove_driver(struct device_driver * drv) { if (drv->bus) { driver_remove_attrs(drv->bus, drv); + klist_remove(&drv->knode_bus); down_write(&drv->bus->subsys.rwsem); pr_debug("bus %s: remove driver %s\n", drv->bus->name, drv->name); driver_detach(drv); @@ -536,6 +529,7 @@ int bus_register(struct bus_type * bus) goto bus_drivers_fail; klist_init(&bus->klist_devices); + klist_init(&bus->klist_drivers); bus_add_attrs(bus); pr_debug("bus type '%s' registered\n", bus->name); -- cgit v1.2.3 From 94e7b1c5ff2055571703e38b059afffe17658432 Mon Sep 17 00:00:00 2001 From: "mochel@digitalimplant.org" Date: Mon, 21 Mar 2005 12:25:36 -0800 Subject: [PATCH] Add a klist to struct device_driver for the devices bound to it. - Use it in driver_for_each_device() instead of the regular list_head and stop using the bus's rwsem for protection. - Use driver_for_each_device() in driver_detach() so we don't deadlock on the bus's rwsem. - Remove ->devices. - Move klist access and sysfs link access out from under device's semaphore, since they're synchronized through other means. Signed-off-by: Patrick Mochel Signed-off-by: Greg Kroah-Hartman --- drivers/base/core.c | 2 +- drivers/base/dd.c | 34 +++++++++++++++++++--------------- drivers/base/driver.c | 27 +++++++++++++++------------ 3 files changed, 35 insertions(+), 28 deletions(-) (limited to 'drivers/base') diff --git a/drivers/base/core.c b/drivers/base/core.c index 93440824b80..bc5bec61a01 100644 --- a/drivers/base/core.c +++ b/drivers/base/core.c @@ -209,8 +209,8 @@ void device_initialize(struct device *dev) kobject_init(&dev->kobj); INIT_LIST_HEAD(&dev->node); INIT_LIST_HEAD(&dev->children); - INIT_LIST_HEAD(&dev->driver_list); INIT_LIST_HEAD(&dev->bus_list); + INIT_LIST_HEAD(&dev->driver_list); INIT_LIST_HEAD(&dev->dma_pools); init_MUTEX(&dev->sem); } diff --git a/drivers/base/dd.c b/drivers/base/dd.c index b709b1e0cb2..47cbb564123 100644 --- a/drivers/base/dd.c +++ b/drivers/base/dd.c @@ -40,7 +40,7 @@ void device_bind_driver(struct device * dev) { pr_debug("bound device '%s' to driver '%s'\n", dev->bus_id, dev->driver->name); - list_add_tail(&dev->driver_list, &dev->driver->devices); + klist_add_tail(&dev->driver->klist_devices, &dev->knode_driver); sysfs_create_link(&dev->driver->kobj, &dev->kobj, kobject_name(&dev->kobj)); sysfs_create_link(&dev->kobj, &dev->driver->kobj, "driver"); @@ -164,31 +164,35 @@ void driver_attach(struct device_driver * drv) */ void device_release_driver(struct device * dev) { - struct device_driver * drv; + struct device_driver * drv = dev->driver; + + if (!drv) + return; + + sysfs_remove_link(&drv->kobj, kobject_name(&dev->kobj)); + sysfs_remove_link(&dev->kobj, "driver"); + klist_remove(&dev->knode_driver); down(&dev->sem); - drv = dev->driver; - if (drv) { - sysfs_remove_link(&drv->kobj, kobject_name(&dev->kobj)); - sysfs_remove_link(&dev->kobj, "driver"); - list_del_init(&dev->driver_list); - if (drv->remove) - drv->remove(dev); - dev->driver = NULL; - } + if (drv->remove) + drv->remove(dev); + dev->driver = NULL; up(&dev->sem); } +static int __remove_driver(struct device * dev, void * unused) +{ + device_release_driver(dev); + return 0; +} + /** * driver_detach - detach driver from all devices it controls. * @drv: driver. */ void driver_detach(struct device_driver * drv) { - while (!list_empty(&drv->devices)) { - struct device * dev = container_of(drv->devices.next, struct device, driver_list); - device_release_driver(dev); - } + driver_for_each_device(drv, NULL, NULL, __remove_driver); } diff --git a/drivers/base/driver.c b/drivers/base/driver.c index 484fed1985a..34bd38aa7eb 100644 --- a/drivers/base/driver.c +++ b/drivers/base/driver.c @@ -19,6 +19,12 @@ #define to_drv(obj) container_of(obj, struct device_driver, kobj) +static struct device * next_device(struct klist_iter * i) +{ + struct klist_node * n = klist_next(i); + return n ? container_of(n, struct device, knode_driver) : NULL; +} + /** * driver_for_each_device - Iterator for devices bound to a driver. * @drv: Driver we're iterating. @@ -32,21 +38,18 @@ int driver_for_each_device(struct device_driver * drv, struct device * start, void * data, int (*fn)(struct device *, void *)) { - struct list_head * head; + struct klist_iter i; struct device * dev; int error = 0; - down_read(&drv->bus->subsys.rwsem); - head = &drv->devices; - dev = list_prepare_entry(start, head, driver_list); - list_for_each_entry_continue(dev, head, driver_list) { - get_device(dev); + if (!drv) + return -EINVAL; + + klist_iter_init_node(&drv->klist_devices, &i, + start ? &start->knode_driver : NULL); + while ((dev = next_device(&i)) && !error) error = fn(dev, data); - put_device(dev); - if (error) - break; - } - up_read(&drv->bus->subsys.rwsem); + klist_iter_exit(&i); return error; } @@ -120,7 +123,7 @@ void put_driver(struct device_driver * drv) */ int driver_register(struct device_driver * drv) { - INIT_LIST_HEAD(&drv->devices); + klist_init(&drv->klist_devices); init_completion(&drv->unloaded); return bus_add_driver(drv); } -- cgit v1.2.3 From cb85b6f1cc811ecb9ed4b950206d8941ba710e68 Mon Sep 17 00:00:00 2001 From: "mochel@digitalimplant.org" Date: Thu, 24 Mar 2005 10:48:35 -0800 Subject: [PATCH] Remove the unused device_find(). Signed-off-by: Patrick Mochel Signed-off-by: Greg Kroah-Hartman --- drivers/base/core.c | 19 ------------------- 1 file changed, 19 deletions(-) (limited to 'drivers/base') diff --git a/drivers/base/core.c b/drivers/base/core.c index bc5bec61a01..0eb1d424bd1 100644 --- a/drivers/base/core.c +++ b/drivers/base/core.c @@ -401,24 +401,6 @@ int device_for_each_child(struct device * dev, void * data, return error; } -/** - * device_find - locate device on a bus by name. - * @name: name of the device. - * @bus: bus to scan for the device. - * - * Call kset_find_obj() to iterate over list of devices on - * a bus to find device by name. Return device if found. - * - * Note that kset_find_obj increments device's reference count. - */ -struct device *device_find(const char *name, struct bus_type *bus) -{ - struct kobject *k = kset_find_obj(&bus->devices, name); - if (k) - return to_dev(k); - return NULL; -} - int __init devices_init(void) { return subsystem_register(&devices_subsys); @@ -434,7 +416,6 @@ EXPORT_SYMBOL_GPL(device_del); EXPORT_SYMBOL_GPL(device_unregister); EXPORT_SYMBOL_GPL(get_device); EXPORT_SYMBOL_GPL(put_device); -EXPORT_SYMBOL_GPL(device_find); EXPORT_SYMBOL_GPL(device_create_file); EXPORT_SYMBOL_GPL(device_remove_file); -- cgit v1.2.3 From 2287c322b61fced7e0c326a1a9606aa73147e3df Mon Sep 17 00:00:00 2001 From: "mochel@digitalimplant.org" Date: Thu, 24 Mar 2005 10:50:24 -0800 Subject: [PATCH] Use bus_for_each_{dev,drv} for driver binding. - Now possible, since the lists are locked using the klist lock and not the global rwsem. Signed-off-by: Patrick Mochel Signed-off-by: Greg Kroah-Hartman --- drivers/base/dd.c | 72 ++++++++++++++++++++++++++++++------------------------- 1 file changed, 39 insertions(+), 33 deletions(-) (limited to 'drivers/base') diff --git a/drivers/base/dd.c b/drivers/base/dd.c index 47cbb564123..85042ada8a5 100644 --- a/drivers/base/dd.c +++ b/drivers/base/dd.c @@ -82,6 +82,28 @@ int driver_probe_device(struct device_driver * drv, struct device * dev) return 0; } +static int __device_attach(struct device_driver * drv, void * data) +{ + struct device * dev = data; + int error; + + error = driver_probe_device(drv, dev); + + if (error == -ENODEV && error == -ENXIO) { + /* Driver matched, but didn't support device + * or device not found. + * Not an error; keep going. + */ + error = 0; + } else { + /* driver matched but the probe failed */ + printk(KERN_WARNING + "%s: probe of %s failed with error %d\n", + drv->name, dev->bus_id, error); + } + return 0; +} + /** * device_attach - try to attach device to a driver. * @dev: device. @@ -92,30 +114,31 @@ int driver_probe_device(struct device_driver * drv, struct device * dev) */ int device_attach(struct device * dev) { - struct bus_type * bus = dev->bus; - struct list_head * entry; - int error; - if (dev->driver) { device_bind_driver(dev); return 1; } - if (bus->match) { - list_for_each(entry, &bus->drivers.list) { - struct device_driver * drv = to_drv(entry); - error = driver_probe_device(drv, dev); - if (!error) - /* success, driver matched */ - return 1; - if (error != -ENODEV && error != -ENXIO) + return bus_for_each_drv(dev->bus, NULL, dev, __device_attach); +} + +static int __driver_attach(struct device * dev, void * data) +{ + struct device_driver * drv = data; + int error = 0; + + if (!dev->driver) { + error = driver_probe_device(drv, dev); + if (error) { + if (error != -ENODEV) { /* driver matched but the probe failed */ printk(KERN_WARNING - "%s: probe of %s failed with error %d\n", - drv->name, dev->bus_id, error); + "%s: probe of %s failed with error %d\n", + drv->name, dev->bus_id, error); + } else + error = 0; } } - return 0; } @@ -133,24 +156,7 @@ int device_attach(struct device * dev) */ void driver_attach(struct device_driver * drv) { - struct bus_type * bus = drv->bus; - struct list_head * entry; - int error; - - if (!bus->match) - return; - - list_for_each(entry, &bus->devices.list) { - struct device * dev = container_of(entry, struct device, bus_list); - if (!dev->driver) { - error = driver_probe_device(drv, dev); - if (error && (error != -ENODEV)) - /* driver matched but the probe failed */ - printk(KERN_WARNING - "%s: probe of %s failed with error %d\n", - drv->name, dev->bus_id, error); - } - } + bus_for_each_dev(drv->bus, NULL, drv, __driver_attach); } /** -- cgit v1.2.3 From 6eded061b1263847aedac7469339e99579aec5e5 Mon Sep 17 00:00:00 2001 From: "mochel@digitalimplant.org" Date: Thu, 24 Mar 2005 13:02:28 -0800 Subject: [PATCH] Fix up bus code and remove use of rwsem. - Don't add devices to bus's embedded kset, since it's not used by anyone anymore. - Don't need to take the bus rwsem when calling {device,driver}_attach(), since those functions use the klists and the klists' spinlocks. Signed-off-by: Patrick Mochel Signed-off-by: Greg Kroah-Hartman --- drivers/base/bus.c | 12 ------------ 1 file changed, 12 deletions(-) (limited to 'drivers/base') diff --git a/drivers/base/bus.c b/drivers/base/bus.c index 736dc1f5a31..4eb19525e06 100644 --- a/drivers/base/bus.c +++ b/drivers/base/bus.c @@ -17,8 +17,6 @@ #include "base.h" #include "power/power.h" -#define to_dev(node) container_of(node, struct device, bus_list) - #define to_bus_attr(_attr) container_of(_attr, struct bus_attribute, attr) #define to_bus(obj) container_of(obj, struct bus_type, subsys.kset.kobj) @@ -271,11 +269,8 @@ int bus_add_device(struct device * dev) int error = 0; if (bus) { - down_write(&dev->bus->subsys.rwsem); pr_debug("bus %s: add device %s\n", bus->name, dev->bus_id); - list_add_tail(&dev->bus_list, &dev->bus->devices.list); device_attach(dev); - up_write(&dev->bus->subsys.rwsem); klist_add_tail(&bus->klist_devices, &dev->knode_bus); device_add_attrs(bus, dev); sysfs_create_link(&bus->devices.kobj, &dev->kobj, dev->bus_id); @@ -300,11 +295,8 @@ void bus_remove_device(struct device * dev) sysfs_remove_link(&dev->bus->devices.kobj, dev->bus_id); device_remove_attrs(dev->bus, dev); klist_remove(&dev->knode_bus); - down_write(&dev->bus->subsys.rwsem); pr_debug("bus %s: remove device %s\n", dev->bus->name, dev->bus_id); device_release_driver(dev); - list_del_init(&dev->bus_list); - up_write(&dev->bus->subsys.rwsem); put_bus(dev->bus); } } @@ -364,9 +356,7 @@ int bus_add_driver(struct device_driver * drv) return error; } - down_write(&bus->subsys.rwsem); driver_attach(drv); - up_write(&bus->subsys.rwsem); klist_add_tail(&bus->klist_drivers, &drv->knode_bus); module_add_driver(drv->owner, drv); @@ -390,10 +380,8 @@ void bus_remove_driver(struct device_driver * drv) if (drv->bus) { driver_remove_attrs(drv->bus, drv); klist_remove(&drv->knode_bus); - down_write(&drv->bus->subsys.rwsem); pr_debug("bus %s: remove driver %s\n", drv->bus->name, drv->name); driver_detach(drv); - up_write(&drv->bus->subsys.rwsem); module_remove_driver(drv); kobject_unregister(&drv->kobj); put_bus(drv->bus); -- cgit v1.2.3 From 7dc35cdf69149a7f2b5216ada9bafe359746ac1c Mon Sep 17 00:00:00 2001 From: "mochel@digitalimplant.org" Date: Thu, 24 Mar 2005 13:03:35 -0800 Subject: [PATCH] Remove struct device::bus_list. Signed-off-by: Patrick Mochel Signed-off-by: Greg Kroah-Hartman --- drivers/base/core.c | 1 - 1 file changed, 1 deletion(-) (limited to 'drivers/base') diff --git a/drivers/base/core.c b/drivers/base/core.c index 0eb1d424bd1..f258a21ed78 100644 --- a/drivers/base/core.c +++ b/drivers/base/core.c @@ -209,7 +209,6 @@ void device_initialize(struct device *dev) kobject_init(&dev->kobj); INIT_LIST_HEAD(&dev->node); INIT_LIST_HEAD(&dev->children); - INIT_LIST_HEAD(&dev->bus_list); INIT_LIST_HEAD(&dev->driver_list); INIT_LIST_HEAD(&dev->dma_pools); init_MUTEX(&dev->sem); -- cgit v1.2.3 From 63c4f204ffc8219696bda88eb20c9873d007a2fc Mon Sep 17 00:00:00 2001 From: "mochel@digitalimplant.org" Date: Thu, 24 Mar 2005 13:08:05 -0800 Subject: [PATCH] Remove struct device::driver_list. Signed-off-by: Patrick Mochel Signed-off-by: Greg Kroah-Hartman --- drivers/base/core.c | 1 - 1 file changed, 1 deletion(-) (limited to 'drivers/base') diff --git a/drivers/base/core.c b/drivers/base/core.c index f258a21ed78..bfa4268289e 100644 --- a/drivers/base/core.c +++ b/drivers/base/core.c @@ -209,7 +209,6 @@ void device_initialize(struct device *dev) kobject_init(&dev->kobj); INIT_LIST_HEAD(&dev->node); INIT_LIST_HEAD(&dev->children); - INIT_LIST_HEAD(&dev->driver_list); INIT_LIST_HEAD(&dev->dma_pools); init_MUTEX(&dev->sem); } -- cgit v1.2.3 From 0956af53afea290c5676c75249fc2c180d831375 Mon Sep 17 00:00:00 2001 From: "mochel@digitalimplant.org" Date: Thu, 24 Mar 2005 18:58:45 -0800 Subject: [PATCH] Call klist_del() instead of klist_remove(). - Can't wait on removing the current item in the list (the positive refcount *because* we are using it causes it to deadlock). Signed-off-by: Patrick Mochel Signed-off-by: Greg Kroah-Hartman --- drivers/base/dd.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers/base') diff --git a/drivers/base/dd.c b/drivers/base/dd.c index 85042ada8a5..159e0623a68 100644 --- a/drivers/base/dd.c +++ b/drivers/base/dd.c @@ -177,7 +177,7 @@ void device_release_driver(struct device * dev) sysfs_remove_link(&drv->kobj, kobject_name(&dev->kobj)); sysfs_remove_link(&dev->kobj, "driver"); - klist_remove(&dev->knode_driver); + klist_del(&dev->knode_driver); down(&dev->sem); if (drv->remove) -- cgit v1.2.3 From 36239577cfb6b9a7c111209536b54200b0252ebf Mon Sep 17 00:00:00 2001 From: "mochel@digitalimplant.org" Date: Thu, 24 Mar 2005 19:08:30 -0800 Subject: [PATCH] Use a klist for device child lists. - Use klist iterator in device_for_each_child(), making it safe to use for removing devices. - Remove unused list_to_dev() function. - Kills all usage of devices_subsys.rwsem. Signed-off-by: Patrick Mochel Signed-off-by: Greg Kroah-Hartman --- drivers/base/core.c | 30 +++++++++++++++--------------- 1 file changed, 15 insertions(+), 15 deletions(-) (limited to 'drivers/base') diff --git a/drivers/base/core.c b/drivers/base/core.c index bfa4268289e..1d8c7790b55 100644 --- a/drivers/base/core.c +++ b/drivers/base/core.c @@ -207,8 +207,7 @@ void device_initialize(struct device *dev) { kobj_set_kset_s(dev, devices_subsys); kobject_init(&dev->kobj); - INIT_LIST_HEAD(&dev->node); - INIT_LIST_HEAD(&dev->children); + klist_init(&dev->klist_children); INIT_LIST_HEAD(&dev->dma_pools); init_MUTEX(&dev->sem); } @@ -249,10 +248,8 @@ int device_add(struct device *dev) goto PMError; if ((error = bus_add_device(dev))) goto BusError; - down_write(&devices_subsys.rwsem); if (parent) - list_add_tail(&dev->node, &parent->children); - up_write(&devices_subsys.rwsem); + klist_add_tail(&parent->klist_children, &dev->knode_parent); /* notify platform of device entry */ if (platform_notify) @@ -335,10 +332,8 @@ void device_del(struct device * dev) { struct device * parent = dev->parent; - down_write(&devices_subsys.rwsem); if (parent) - list_del_init(&dev->node); - up_write(&devices_subsys.rwsem); + klist_remove(&dev->knode_parent); /* Notify the platform of the removal, in case they * need to do anything... @@ -372,6 +367,12 @@ void device_unregister(struct device * dev) } +static struct device * next_device(struct klist_iter * i) +{ + struct klist_node * n = klist_next(i); + return n ? container_of(n, struct device, knode_parent) : NULL; +} + /** * device_for_each_child - device child iterator. * @dev: parent struct device. @@ -384,18 +385,17 @@ void device_unregister(struct device * dev) * We check the return of @fn each time. If it returns anything * other than 0, we break out and return that value. */ -int device_for_each_child(struct device * dev, void * data, +int device_for_each_child(struct device * parent, void * data, int (*fn)(struct device *, void *)) { + struct klist_iter i; struct device * child; int error = 0; - down_read(&devices_subsys.rwsem); - list_for_each_entry(child, &dev->children, node) { - if((error = fn(child, data))) - break; - } - up_read(&devices_subsys.rwsem); + klist_iter_init(&parent->klist_children, &i); + while ((child = next_device(&i)) && !error) + error = fn(child, data); + klist_iter_exit(&i); return error; } -- cgit v1.2.3 From 4d12d2d953ca5e299de6a653f1d0478f670d7bc6 Mon Sep 17 00:00:00 2001 From: "mochel@digitalimplant.org" Date: Thu, 24 Mar 2005 20:08:04 -0800 Subject: [PATCH] Fix up bogus comment. Signed-off-by: Patrick Mochel Signed-off-by: Greg Kroah-Hartman diff -Nru a/drivers/base/driver.c b/drivers/base/driver.c --- drivers/base/driver.c | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) (limited to 'drivers/base') diff --git a/drivers/base/driver.c b/drivers/base/driver.c index 34bd38aa7eb..f789b6cb4b5 100644 --- a/drivers/base/driver.c +++ b/drivers/base/driver.c @@ -31,8 +31,7 @@ static struct device * next_device(struct klist_iter * i) * @data: Data to pass to the callback. * @fn: Function to call for each device. * - * Take the bus's rwsem and iterate over the @drv's list of devices, - * calling @fn for each one. + * Iterate over the @drv's list of devices calling @fn for each one. */ int driver_for_each_device(struct device_driver * drv, struct device * start, -- cgit v1.2.3 From 126eddfbf8cae8a20c22708192bffcbd77c8a889 Mon Sep 17 00:00:00 2001 From: "gregkh@suse.de" Date: Tue, 22 Mar 2005 12:17:13 -0800 Subject: [PATCH] driver core: change export symbol for driver_for_each_device() Signed-off-by: Greg Kroah-Hartman Index: linux-2.6.12-rc2/drivers/base/driver.c =================================================================== --- drivers/base/driver.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers/base') diff --git a/drivers/base/driver.c b/drivers/base/driver.c index f789b6cb4b5..1b645886e9e 100644 --- a/drivers/base/driver.c +++ b/drivers/base/driver.c @@ -52,7 +52,7 @@ int driver_for_each_device(struct device_driver * drv, struct device * start, return error; } -EXPORT_SYMBOL(driver_for_each_device); +EXPORT_SYMBOL_GPL(driver_for_each_device); /** -- cgit v1.2.3 From b86c1df1f98d16c999423a3907eb40a9423f481e Mon Sep 17 00:00:00 2001 From: "gregkh@suse.de" Date: Thu, 31 Mar 2005 12:53:00 -0800 Subject: [PATCH] Driver core: Fix up the driver and device iterators to be quieter Also stops looping over the lists when a match is found. Signed-off-by: Greg Kroah-Hartman name, dev->bus_id, error); + if (error) { + if ((error == -ENODEV) || (error == -ENXIO)) { + /* Driver matched, but didn't support device + * or device not found. + * Not an error; keep going. + */ + error = 0; + } else { + /* driver matched but the probe failed */ + printk(KERN_WARNING + "%s: probe of %s failed with error %d\n", + drv->name, dev->bus_id, error); + } + return error; } - return 0; + /* stop looking, this device is attached */ + return 1; } /** @@ -137,7 +140,10 @@ static int __driver_attach(struct device * dev, void * data) drv->name, dev->bus_id, error); } else error = 0; + return error; } + /* stop looking, this driver is attached */ + return 1; } return 0; } -- cgit v1.2.3 From 0d3e5a2e39b6ba2974e9e7c2a429018c45de8e76 Mon Sep 17 00:00:00 2001 From: Patrick Mochel Date: Tue, 5 Apr 2005 23:46:33 -0700 Subject: [PATCH] Driver Core: fix bk-driver-core kills ppc64 There's no check to see if the device is already bound to a driver, which could do bad things. The first thing to go wrong is that it will try to match a driver with a device already bound to one. In some cases (it appears with USB with drivers/usb/core/usb.c::usb_match_id()), some drivers will match a device based on the class type, so it would be common (especially for HID devices) to match a device that is already bound. The fun comes when ->probe() is called, it fails, then driver_probe_device() does this: dev->driver = NULL; Later on, that pointer could be be dereferenced without checking and cause hell to break loose. This problem could be nasty. It's very hardware dependent, since some devices could have a different set of matching qualifiers than others. Now, I don't quite see exactly where/how you were getting that crash. You're dereferencing bad memory, but I'm not sure which pointer was bad and where it came from, but it could have come from a couple of different places. The patch below will hopefully fix it all up for you. It's against 2.6.12-rc2-mm1, and does the following: - Move logic to driver_probe_device() and comments uncommon returns: 1 - If device is bound 0 - If device not bound, and no error error - If there was an error. - Move locking to caller of that function, since we want to lock a device for the entire time we're trying to bind it to a driver (to prevent against a driver being loaded at the same time). - Update __device_attach() and __driver_attach() to do that locking. - Check if device is already bound in __driver_attach() - Update the converse device_release_driver() so it locks the device around all of the operations. - Mark driver_probe_device() as static and remove export. It's an internal function, it should stay that way, and there are no other callers. If there is ever a need to export it, we can audit it as necessary. Signed-off-by: Andrew Morton --- drivers/base/dd.c | 142 ++++++++++++++++++++++++++++-------------------------- 1 file changed, 74 insertions(+), 68 deletions(-) (limited to 'drivers/base') diff --git a/drivers/base/dd.c b/drivers/base/dd.c index dd2a8a79c12..8510918109e 100644 --- a/drivers/base/dd.c +++ b/drivers/base/dd.c @@ -35,6 +35,8 @@ * nor take the bus's rwsem. Please verify those are accounted * for before calling this. (It is ok to call with no other effort * from a driver's probe() method.) + * + * This function must be called with @dev->sem held. */ void device_bind_driver(struct device * dev) { @@ -57,54 +59,56 @@ void device_bind_driver(struct device * dev) * because we don't know the format of the ID structures, nor what * is to be considered a match and what is not. * - * If we find a match, we call @drv->probe(@dev) if it exists, and - * call device_bind_driver() above. + * + * This function returns 1 if a match is found, an error if one + * occurs (that is not -ENODEV or -ENXIO), and 0 otherwise. + * + * This function must be called with @dev->sem held. */ -int driver_probe_device(struct device_driver * drv, struct device * dev) +static int driver_probe_device(struct device_driver * drv, struct device * dev) { - int error = 0; + int ret = 0; if (drv->bus->match && !drv->bus->match(dev, drv)) - return -ENODEV; + goto Done; - down(&dev->sem); + pr_debug("%s: Matched Device %s with Driver %s\n", + drv->bus->name, dev->bus_id, drv->name); dev->driver = drv; if (drv->probe) { - error = drv->probe(dev); - if (error) { + ret = drv->probe(dev); + if (ret) { dev->driver = NULL; - up(&dev->sem); - return error; + goto ProbeFailed; } } - up(&dev->sem); device_bind_driver(dev); - return 0; + ret = 1; + pr_debug("%s: Bound Device %s to Driver %s\n", + drv->bus->name, dev->bus_id, drv->name); + goto Done; + + ProbeFailed: + if (ret == -ENODEV || ret == -ENXIO) { + /* Driver matched, but didn't support device + * or device not found. + * Not an error; keep going. + */ + ret = 0; + } else { + /* driver matched but the probe failed */ + printk(KERN_WARNING + "%s: probe of %s failed with error %d\n", + drv->name, dev->bus_id, ret); + } + Done: + return ret; } static int __device_attach(struct device_driver * drv, void * data) { struct device * dev = data; - int error; - - error = driver_probe_device(drv, dev); - if (error) { - if ((error == -ENODEV) || (error == -ENXIO)) { - /* Driver matched, but didn't support device - * or device not found. - * Not an error; keep going. - */ - error = 0; - } else { - /* driver matched but the probe failed */ - printk(KERN_WARNING - "%s: probe of %s failed with error %d\n", - drv->name, dev->bus_id, error); - } - return error; - } - /* stop looking, this device is attached */ - return 1; + return driver_probe_device(drv, dev); } /** @@ -114,37 +118,43 @@ static int __device_attach(struct device_driver * drv, void * data) * Walk the list of drivers that the bus has and call * driver_probe_device() for each pair. If a compatible * pair is found, break out and return. + * + * Returns 1 if the device was bound to a driver; 0 otherwise. */ int device_attach(struct device * dev) { + int ret = 0; + + down(&dev->sem); if (dev->driver) { device_bind_driver(dev); - return 1; - } - - return bus_for_each_drv(dev->bus, NULL, dev, __device_attach); + ret = 1; + } else + ret = bus_for_each_drv(dev->bus, NULL, dev, __device_attach); + up(&dev->sem); + return ret; } static int __driver_attach(struct device * dev, void * data) { struct device_driver * drv = data; - int error = 0; - - if (!dev->driver) { - error = driver_probe_device(drv, dev); - if (error) { - if (error != -ENODEV) { - /* driver matched but the probe failed */ - printk(KERN_WARNING - "%s: probe of %s failed with error %d\n", - drv->name, dev->bus_id, error); - } else - error = 0; - return error; - } - /* stop looking, this driver is attached */ - return 1; - } + + /* + * Lock device and try to bind to it. We drop the error + * here and always return 0, because we need to keep trying + * to bind to devices and some drivers will return an error + * simply if it didn't support the device. + * + * driver_probe_device() will spit a warning if there + * is an error. + */ + + down(&dev->sem); + if (!dev->driver) + driver_probe_device(drv, dev); + up(&dev->sem); + + return 0; } @@ -156,9 +166,6 @@ static int __driver_attach(struct device * dev, void * data) * match the driver with each one. If driver_probe_device() * returns 0 and the @dev->driver is set, we've found a * compatible pair. - * - * Note that we ignore the -ENODEV error from driver_probe_device(), - * since it's perfectly valid for a driver not to bind to any devices. */ void driver_attach(struct device_driver * drv) { @@ -176,19 +183,19 @@ void driver_attach(struct device_driver * drv) */ void device_release_driver(struct device * dev) { - struct device_driver * drv = dev->driver; - - if (!drv) - return; - - sysfs_remove_link(&drv->kobj, kobject_name(&dev->kobj)); - sysfs_remove_link(&dev->kobj, "driver"); - klist_del(&dev->knode_driver); + struct device_driver * drv; down(&dev->sem); - if (drv->remove) - drv->remove(dev); - dev->driver = NULL; + if (dev->driver) { + drv = dev->driver; + sysfs_remove_link(&drv->kobj, kobject_name(&dev->kobj)); + sysfs_remove_link(&dev->kobj, "driver"); + klist_del(&dev->knode_driver); + + if (drv->remove) + drv->remove(dev); + dev->driver = NULL; + } up(&dev->sem); } @@ -208,7 +215,6 @@ void driver_detach(struct device_driver * drv) } -EXPORT_SYMBOL_GPL(driver_probe_device); EXPORT_SYMBOL_GPL(device_bind_driver); EXPORT_SYMBOL_GPL(device_release_driver); EXPORT_SYMBOL_GPL(device_attach); -- cgit v1.2.3 From c95a6b057b108c2b7add35cba1354f9af921349e Mon Sep 17 00:00:00 2001 From: Alan Stern Date: Fri, 6 May 2005 15:38:33 -0400 Subject: [PATCH] driver core: Fix races in driver_detach() This patch is intended for your "driver" tree. It fixes several subtle races in driver_detach() and device_release_driver() in the driver-model core. The major change is to use klist_remove() rather than klist_del() when taking a device off its driver's list. There's no other way to guarantee that the list pointers will be updated before some other driver binds to the device. For this to work driver_detach() can't use a klist iterator, so the loop over the devices must be written out in full. In addition the patch protects against the possibility that, when a driver and a device are unregistered at the same time, one may be unloaded from memory before the other is finished using it. Signed-off-by: Alan Stern Signed-off-by: Greg Kroah-Hartman --- drivers/base/dd.c | 51 ++++++++++++++++++++++++++++++++++++++------------- 1 file changed, 38 insertions(+), 13 deletions(-) (limited to 'drivers/base') diff --git a/drivers/base/dd.c b/drivers/base/dd.c index 8510918109e..eab2030c506 100644 --- a/drivers/base/dd.c +++ b/drivers/base/dd.c @@ -177,41 +177,66 @@ void driver_attach(struct device_driver * drv) * @dev: device. * * Manually detach device from driver. - * Note that this is called without incrementing the bus - * reference count nor taking the bus's rwsem. Be sure that - * those are accounted for before calling this function. + * + * __device_release_driver() must be called with @dev->sem held. */ -void device_release_driver(struct device * dev) + +static void __device_release_driver(struct device * dev) { struct device_driver * drv; - down(&dev->sem); - if (dev->driver) { - drv = dev->driver; + drv = dev->driver; + if (drv) { + get_driver(drv); sysfs_remove_link(&drv->kobj, kobject_name(&dev->kobj)); sysfs_remove_link(&dev->kobj, "driver"); - klist_del(&dev->knode_driver); + klist_remove(&dev->knode_driver); if (drv->remove) drv->remove(dev); dev->driver = NULL; + put_driver(drv); } - up(&dev->sem); } -static int __remove_driver(struct device * dev, void * unused) +void device_release_driver(struct device * dev) { - device_release_driver(dev); - return 0; + /* + * If anyone calls device_release_driver() recursively from + * within their ->remove callback for the same device, they + * will deadlock right here. + */ + down(&dev->sem); + __device_release_driver(dev); + up(&dev->sem); } + /** * driver_detach - detach driver from all devices it controls. * @drv: driver. */ void driver_detach(struct device_driver * drv) { - driver_for_each_device(drv, NULL, NULL, __remove_driver); + struct device * dev; + + for (;;) { + spin_lock_irq(&drv->klist_devices.k_lock); + if (list_empty(&drv->klist_devices.k_list)) { + spin_unlock_irq(&drv->klist_devices.k_lock); + break; + } + dev = list_entry(drv->klist_devices.k_list.prev, + struct device, knode_driver.n_node); + get_device(dev); + spin_unlock_irq(&drv->klist_devices.k_lock); + + down(&dev->sem); + if (dev->driver == drv) + __device_release_driver(dev); + up(&dev->sem); + put_device(dev); + } } -- cgit v1.2.3 From 4b45099b75832434c5113b9aed1499f8a69d13d5 Mon Sep 17 00:00:00 2001 From: Keiichiro Tokunaga Date: Sun, 8 May 2005 21:28:53 +0900 Subject: [PATCH] Driver core: unregister_node() for hotplug use This adds a generic function 'unregister_node()'. It is used to remove objects of a node going away for hotplug. All the devices on the node must be unregistered before calling this function. Signed-off-by: Keiichiro Tokunaga Signed-off-by: Greg Kroah-Hartman diff -puN drivers/base/node.c~numa_hp_base drivers/base/node.c --- drivers/base/node.c | 20 ++++++++++++++++++-- 1 file changed, 18 insertions(+), 2 deletions(-) (limited to 'drivers/base') diff --git a/drivers/base/node.c b/drivers/base/node.c index 583d57ec49a..5d4517ccc42 100644 --- a/drivers/base/node.c +++ b/drivers/base/node.c @@ -136,7 +136,7 @@ static SYSDEV_ATTR(distance, S_IRUGO, node_read_distance, NULL); * * Initialize and register the node device. */ -int __init register_node(struct node *node, int num, struct node *parent) +int register_node(struct node *node, int num, struct node *parent) { int error; @@ -153,8 +153,24 @@ int __init register_node(struct node *node, int num, struct node *parent) return error; } +/** + * unregister_node - unregister a node device + * @node: node going away + * + * Unregisters a node device @node. All the devices on the node must be + * unregistered before calling this function. + */ +void unregister_node(struct node *node) +{ + sysdev_remove_file(&node->sysdev, &attr_cpumap); + sysdev_remove_file(&node->sysdev, &attr_meminfo); + sysdev_remove_file(&node->sysdev, &attr_numastat); + sysdev_remove_file(&node->sysdev, &attr_distance); + + sysdev_unregister(&node->sysdev); +} -int __init register_node_type(void) +static int __init register_node_type(void) { return sysdev_class_register(&node_class); } -- cgit v1.2.3 From ca2b94ba12f3c36fd3d6ed9d38b3798d4dad0d8b Mon Sep 17 00:00:00 2001 From: Hannes Reinecke Date: Wed, 18 May 2005 10:42:23 +0200 Subject: [PATCH] driver core: fix error handling in bus_add_device The error handling in bus_add_device() and device_attach() is simply non-existing. This patch propagates any error from device_attach to the upper layers to allow for a proper recovery. From: Hannes Reinecke Signed-off-by: Greg Kroah-Hartman --- drivers/base/bus.c | 13 ++++++++----- drivers/base/dd.c | 3 ++- 2 files changed, 10 insertions(+), 6 deletions(-) (limited to 'drivers/base') diff --git a/drivers/base/bus.c b/drivers/base/bus.c index 4eb19525e06..43722af90bd 100644 --- a/drivers/base/bus.c +++ b/drivers/base/bus.c @@ -270,11 +270,14 @@ int bus_add_device(struct device * dev) if (bus) { pr_debug("bus %s: add device %s\n", bus->name, dev->bus_id); - device_attach(dev); + error = device_attach(dev); klist_add_tail(&bus->klist_devices, &dev->knode_bus); - device_add_attrs(bus, dev); - sysfs_create_link(&bus->devices.kobj, &dev->kobj, dev->bus_id); - sysfs_create_link(&dev->kobj, &dev->bus->subsys.kset.kobj, "bus"); + if (error >= 0) + error = device_add_attrs(bus, dev); + if (!error) { + sysfs_create_link(&bus->devices.kobj, &dev->kobj, dev->bus_id); + sysfs_create_link(&dev->kobj, &dev->bus->subsys.kset.kobj, "bus"); + } } return error; } @@ -394,7 +397,7 @@ static int bus_rescan_devices_helper(struct device *dev, void *data) { int *count = data; - if (!dev->driver && device_attach(dev)) + if (!dev->driver && (device_attach(dev) > 0)) (*count)++; return 0; diff --git a/drivers/base/dd.c b/drivers/base/dd.c index eab2030c506..6db3a789c54 100644 --- a/drivers/base/dd.c +++ b/drivers/base/dd.c @@ -119,7 +119,8 @@ static int __device_attach(struct device_driver * drv, void * data) * driver_probe_device() for each pair. If a compatible * pair is found, break out and return. * - * Returns 1 if the device was bound to a driver; 0 otherwise. + * Returns 1 if the device was bound to a driver; + * 0 if no matching device was found; error code otherwise. */ int device_attach(struct device * dev) { -- cgit v1.2.3 From 54b6f35c99974e99e64c05c2895718355123c55f Mon Sep 17 00:00:00 2001 From: Yani Ioannou Date: Tue, 17 May 2005 06:39:34 -0400 Subject: [PATCH] Driver core: change device_attribute callbacks This patch adds the device_attribute paramerter to the device_attribute store and show sysfs callback functions, and passes a reference to the attribute when the callbacks are called. Signed-off-by: Yani Ioannou Signed-off-by: Greg Kroah-Hartman --- drivers/base/core.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'drivers/base') diff --git a/drivers/base/core.c b/drivers/base/core.c index 1d8c7790b55..86d79755fbf 100644 --- a/drivers/base/core.c +++ b/drivers/base/core.c @@ -39,7 +39,7 @@ dev_attr_show(struct kobject * kobj, struct attribute * attr, char * buf) ssize_t ret = -EIO; if (dev_attr->show) - ret = dev_attr->show(dev, buf); + ret = dev_attr->show(dev, dev_attr, buf); return ret; } @@ -52,7 +52,7 @@ dev_attr_store(struct kobject * kobj, struct attribute * attr, ssize_t ret = -EIO; if (dev_attr->store) - ret = dev_attr->store(dev, buf, count); + ret = dev_attr->store(dev, dev_attr, buf, count); return ret; } -- cgit v1.2.3 From 74880c063b06efd103c924abfe19d9d8fa4864c4 Mon Sep 17 00:00:00 2001 From: Yani Ioannou Date: Tue, 17 May 2005 06:41:12 -0400 Subject: [PATCH] Driver Core: drivers/base - drivers/i2c/chips/adm1026.c: update device attribute callbacks Signed-off-by: Yani Ioannou Signed-off-by: Greg Kroah-Hartman --- drivers/base/dmapool.c | 2 +- drivers/base/power/sysfs.c | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) (limited to 'drivers/base') diff --git a/drivers/base/dmapool.c b/drivers/base/dmapool.c index f48833df61a..c4aebf2f522 100644 --- a/drivers/base/dmapool.c +++ b/drivers/base/dmapool.c @@ -41,7 +41,7 @@ struct dma_page { /* cacheable header for 'allocation' bytes */ static DECLARE_MUTEX (pools_lock); static ssize_t -show_pools (struct device *dev, char *buf) +show_pools (struct device *dev, struct device_attribute *attr, char *buf) { unsigned temp; unsigned size; diff --git a/drivers/base/power/sysfs.c b/drivers/base/power/sysfs.c index 6ac96349a8e..f82b3df9545 100644 --- a/drivers/base/power/sysfs.c +++ b/drivers/base/power/sysfs.c @@ -24,12 +24,12 @@ * low-power state. */ -static ssize_t state_show(struct device * dev, char * buf) +static ssize_t state_show(struct device * dev, struct device_attribute *attr, char * buf) { return sprintf(buf, "%u\n", dev->power.power_state); } -static ssize_t state_store(struct device * dev, const char * buf, size_t n) +static ssize_t state_store(struct device * dev, struct device_attribute *attr, const char * buf, size_t n) { u32 state; char * rest; -- cgit v1.2.3 From 42b16c051c3f462095fb8c9bad1bc05b34518cb9 Mon Sep 17 00:00:00 2001 From: Benjamin Herrenschmidt Date: Tue, 31 May 2005 17:08:49 +1000 Subject: [PATCH] Driver core: Don't "lose" devices on suspend on failure I think we need this patch or we might "lose" devices to the dpm_irq_off list if a failure occurs during the suspend process. Signed-off-by: Benjamin Herrenschmidt Signed-off-by: Greg Kroah-Hartman --- drivers/base/power/suspend.c | 13 ++++++++++++- 1 file changed, 12 insertions(+), 1 deletion(-) (limited to 'drivers/base') diff --git a/drivers/base/power/suspend.c b/drivers/base/power/suspend.c index 807e13fb205..2ccee3763ac 100644 --- a/drivers/base/power/suspend.c +++ b/drivers/base/power/suspend.c @@ -114,8 +114,19 @@ int device_suspend(pm_message_t state) put_device(dev); } up(&dpm_list_sem); - if (error) + if (error) { + /* we failed... before resuming, bring back devices from + * dpm_off_irq list back to main dpm_off list, we do want + * to call resume() on them, in case they partially suspended + * despite returning -EAGAIN + */ + while (!list_empty(&dpm_off_irq)) { + struct list_head * entry = dpm_off_irq.next; + list_del(entry); + list_add(entry, &dpm_off); + } dpm_resume(); + } up(&dpm_sem); return error; } -- cgit v1.2.3