From 00ed8e3dda47f8421b11da17e353d7db8c878121 Mon Sep 17 00:00:00 2001 From: Dmitriy Monakhov Date: Sun, 11 Mar 2007 15:36:19 +0300 Subject: driver core: fix device_add error path - At the moment we jump here device was't added to dev->class->devices list yet. Signed-off-by: Monakhov Dmitriy Signed-off-by: Greg Kroah-Hartman --- drivers/base/core.c | 9 --------- 1 file changed, 9 deletions(-) diff --git a/drivers/base/core.c b/drivers/base/core.c index d7fcf823a42..db3a151be4a 100644 --- a/drivers/base/core.c +++ b/drivers/base/core.c @@ -677,15 +677,6 @@ int device_add(struct device *dev) #endif sysfs_remove_link(&dev->kobj, "device"); } - - down(&dev->class->sem); - /* notify any interfaces that the device is now gone */ - list_for_each_entry(class_intf, &dev->class->interfaces, node) - if (class_intf->remove_dev) - class_intf->remove_dev(dev, class_intf); - /* remove the device from the class list */ - list_del_init(&dev->node); - up(&dev->class->sem); } ueventattrError: device_remove_file(dev, &dev->uevent_attr); -- cgit v1.2.3-18-g5258 From 864062457a2e444227bd368ca5f2a2b740de604f Mon Sep 17 00:00:00 2001 From: Kay Sievers Date: Wed, 14 Mar 2007 03:25:56 +0100 Subject: driver core: fix namespace issue with devices assigned to classes - uses a kset in "struct class" to keep track of all directories belonging to this class - merges with the /sys/devices/virtual logic. - removes the namespace-dir if the last member of that class leaves the directory. There may be locking or refcounting fixes left, I stopped when it seemed to work with network and sound modules. :) From: Kay Sievers Signed-off-by: Greg Kroah-Hartman --- drivers/base/class.c | 2 +- drivers/base/core.c | 82 +++++++++++++++++++++++++++++++++++++++---------- include/linux/device.h | 3 +- include/linux/kobject.h | 2 ++ lib/kobject.c | 12 ++++++-- lib/kobject_uevent.c | 16 ++++++---- 6 files changed, 89 insertions(+), 28 deletions(-) diff --git a/drivers/base/class.c b/drivers/base/class.c index d5968128be2..80bbb207463 100644 --- a/drivers/base/class.c +++ b/drivers/base/class.c @@ -145,6 +145,7 @@ int class_register(struct class * cls) INIT_LIST_HEAD(&cls->children); INIT_LIST_HEAD(&cls->devices); INIT_LIST_HEAD(&cls->interfaces); + kset_init(&cls->class_dirs); init_MUTEX(&cls->sem); error = kobject_set_name(&cls->subsys.kset.kobj, "%s", cls->name); if (error) @@ -163,7 +164,6 @@ int class_register(struct class * cls) void class_unregister(struct class * cls) { pr_debug("device class '%s': unregistering\n", cls->name); - kobject_unregister(cls->virtual_dir); remove_class_attrs(cls); subsystem_unregister(&cls->subsys); } diff --git a/drivers/base/core.c b/drivers/base/core.c index db3a151be4a..658eae5dacd 100644 --- a/drivers/base/core.c +++ b/drivers/base/core.c @@ -477,34 +477,58 @@ static struct kobject * get_device_parent(struct device *dev, return NULL; } #else -static struct kobject * virtual_device_parent(struct device *dev) +static struct kobject *virtual_device_parent(struct device *dev) { - if (!dev->class) - return ERR_PTR(-ENODEV); - - if (!dev->class->virtual_dir) { - static struct kobject *virtual_dir = NULL; + static struct kobject *virtual_dir = NULL; - if (!virtual_dir) - virtual_dir = kobject_add_dir(&devices_subsys.kset.kobj, "virtual"); - dev->class->virtual_dir = kobject_add_dir(virtual_dir, dev->class->name); - } + if (!virtual_dir) + virtual_dir = kobject_add_dir(&devices_subsys.kset.kobj, "virtual"); - return dev->class->virtual_dir; + return virtual_dir; } static struct kobject * get_device_parent(struct device *dev, struct device *parent) { - /* if this is a class device, and has no parent, create one */ - if ((dev->class) && (parent == NULL)) { - return virtual_device_parent(dev); - } else if (parent) + if (dev->class) { + struct kobject *kobj = NULL; + struct kobject *parent_kobj; + struct kobject *k; + + /* + * If we have no parent, we live in "virtual". + * Class-devices with a bus-device as parent, live + * in a class-directory to prevent namespace collisions. + */ + if (parent == NULL) + parent_kobj = virtual_device_parent(dev); + else if (parent->class) + return &parent->kobj; + else + parent_kobj = &parent->kobj; + + /* find our class-directory at the parent and reference it */ + spin_lock(&dev->class->class_dirs.list_lock); + list_for_each_entry(k, &dev->class->class_dirs.list, entry) + if (k->parent == parent_kobj) { + kobj = kobject_get(k); + break; + } + spin_unlock(&dev->class->class_dirs.list_lock); + if (kobj) + return kobj; + + /* or create a new class-directory at the parent device */ + return kobject_kset_add_dir(&dev->class->class_dirs, + parent_kobj, dev->class->name); + } + + if (parent) return &parent->kobj; return NULL; } - #endif + static int setup_parent(struct device *dev, struct device *parent) { struct kobject *kobj; @@ -541,7 +565,6 @@ int device_add(struct device *dev) pr_debug("DEV: registering device: ID = '%s'\n", dev->bus_id); parent = get_device(dev->parent); - error = setup_parent(dev, parent); if (error) goto Error; @@ -787,6 +810,31 @@ void device_del(struct device * dev) /* remove the device from the class list */ list_del_init(&dev->node); up(&dev->class->sem); + + /* If we live in a parent class-directory, unreference it */ + if (dev->kobj.parent->kset == &dev->class->class_dirs) { + struct device *d; + int other = 0; + + /* + * if we are the last child of our class, delete + * our class-directory at this parent + */ + down(&dev->class->sem); + list_for_each_entry(d, &dev->class->devices, node) { + if (d == dev) + continue; + if (d->kobj.parent == dev->kobj.parent) { + other = 1; + break; + } + } + if (!other) + kobject_del(dev->kobj.parent); + + kobject_put(dev->kobj.parent); + up(&dev->class->sem); + } } device_remove_file(dev, &dev->uevent_attr); device_remove_groups(dev); diff --git a/include/linux/device.h b/include/linux/device.h index 5cf30e95c8b..de0e73eae6b 100644 --- a/include/linux/device.h +++ b/include/linux/device.h @@ -181,10 +181,9 @@ struct class { struct list_head children; struct list_head devices; struct list_head interfaces; + struct kset class_dirs; struct semaphore sem; /* locks both the children and interfaces lists */ - struct kobject *virtual_dir; - struct class_attribute * class_attrs; struct class_device_attribute * class_dev_attrs; struct device_attribute * dev_attrs; diff --git a/include/linux/kobject.h b/include/linux/kobject.h index b850e031053..d37cd7f10e3 100644 --- a/include/linux/kobject.h +++ b/include/linux/kobject.h @@ -89,6 +89,8 @@ extern void kobject_unregister(struct kobject *); extern struct kobject * kobject_get(struct kobject *); extern void kobject_put(struct kobject *); +extern struct kobject *kobject_kset_add_dir(struct kset *kset, + struct kobject *, const char *); extern struct kobject *kobject_add_dir(struct kobject *, const char *); extern char * kobject_get_path(struct kobject *, gfp_t); diff --git a/lib/kobject.c b/lib/kobject.c index 057921c5945..f6645515560 100644 --- a/lib/kobject.c +++ b/lib/kobject.c @@ -488,13 +488,15 @@ static struct kobj_type dir_ktype = { }; /** - * kobject_add_dir - add sub directory of object. + * kobject__kset_add_dir - add sub directory of object. + * @kset: kset the directory is belongs to. * @parent: object in which a directory is created. * @name: directory name. * * Add a plain directory object as child of given object. */ -struct kobject *kobject_add_dir(struct kobject *parent, const char *name) +struct kobject *kobject_kset_add_dir(struct kset *kset, + struct kobject *parent, const char *name) { struct kobject *k; int ret; @@ -506,6 +508,7 @@ struct kobject *kobject_add_dir(struct kobject *parent, const char *name) if (!k) return NULL; + k->kset = kset; k->parent = parent; k->ktype = &dir_ktype; kobject_set_name(k, name); @@ -520,6 +523,11 @@ struct kobject *kobject_add_dir(struct kobject *parent, const char *name) return k; } +struct kobject *kobject_add_dir(struct kobject *parent, const char *name) +{ + return kobject_kset_add_dir(NULL, parent, name); +} + /** * kset_init - initialize a kset for use * @k: kset diff --git a/lib/kobject_uevent.c b/lib/kobject_uevent.c index 82fc1794b69..4122f38330d 100644 --- a/lib/kobject_uevent.c +++ b/lib/kobject_uevent.c @@ -115,6 +115,16 @@ int kobject_uevent_env(struct kobject *kobj, enum kobject_action action, return 0; } + /* originating subsystem */ + if (uevent_ops && uevent_ops->name) + subsystem = uevent_ops->name(kset, kobj); + else + subsystem = kobject_name(&kset->kobj); + if (!subsystem) { + pr_debug("unset subsytem caused the event to drop!\n"); + return 0; + } + /* environment index */ envp = kzalloc(NUM_ENVP * sizeof (char *), GFP_KERNEL); if (!envp) @@ -134,12 +144,6 @@ int kobject_uevent_env(struct kobject *kobj, enum kobject_action action, goto exit; } - /* originating subsystem */ - if (uevent_ops && uevent_ops->name) - subsystem = uevent_ops->name(kset, kobj); - else - subsystem = kobject_name(&kset->kobj); - /* event environemnt for helper process only */ envp[i++] = "HOME=/"; envp[i++] = "PATH=/sbin:/bin:/usr/sbin:/usr/bin"; -- cgit v1.2.3-18-g5258 From a456b7023e0abf80bb03b0bdf5471b48878e5c49 Mon Sep 17 00:00:00 2001 From: Jean Delvare Date: Fri, 9 Mar 2007 16:33:10 +0100 Subject: dev_printk and new-style class devices As the new-style class devices (as opposed to old-style struct class_device) are becoming more widely used, I noticed that the dev_printk-based functions are not working properly with these. New-style class devices have no driver nor bus, almost by definition, and as a result dev_driver_string(), which is used as the first parameter of dev_printk, resolves to an empty string. This causes entries like the following to show in my logs: i2c-2: adapter [SMBus stub driver] registered Notice the unaesthetical leading whitespace. In order to fix this problem, I suggest that we extend dev_driver_string to deal with new-style class devices: Signed-off-by: Jean Delvare Signed-off-by: Greg Kroah-Hartman --- drivers/base/core.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/drivers/base/core.c b/drivers/base/core.c index 658eae5dacd..9ea12d9b48a 100644 --- a/drivers/base/core.c +++ b/drivers/base/core.c @@ -43,7 +43,8 @@ int (*platform_notify_remove)(struct device * dev) = NULL; const char *dev_driver_string(struct device *dev) { return dev->driver ? dev->driver->name : - (dev->bus ? dev->bus->name : ""); + (dev->bus ? dev->bus->name : + (dev->class ? dev->class->name : "")); } EXPORT_SYMBOL(dev_driver_string); -- cgit v1.2.3-18-g5258 From b8c5cec23d5c33b767a1cddebd4f8813a9563e3c Mon Sep 17 00:00:00 2001 From: Kay Sievers Date: Fri, 16 Feb 2007 17:33:36 +0100 Subject: Driver core: udev triggered device-<>driver binding We get two per-bus sysfs files: ls-l /sys/subsystem/usb drwxr-xr-x 2 root root 0 2007-02-16 16:42 devices drwxr-xr-x 7 root root 0 2007-02-16 14:55 drivers -rw-r--r-- 1 root root 4096 2007-02-16 16:42 drivers_autoprobe --w------- 1 root root 4096 2007-02-16 16:42 drivers_probe The flag "drivers_autoprobe" controls the behavior of the bus to bind devices by default, or just initialize the device and leave it alone. The command "drivers_probe" accepts a bus_id and the bus tries to bind a driver to this device. Systems who want to control the driver binding with udev, switch off the bus initiated probing: echo 0 > /sys/subsystem/usb/drivers_autoprobe echo 0 > /sys/subsystem/pcmcia/drivers_autoprobe ... and initiate the probing with udev rules like: ACTION=="add", SUBSYSTEM=="usb", ATTR{subsystem/drivers_probe}="$kernel" ACTION=="add", SUBSYSTEM=="pcmcia", ATTR{subsystem/drivers_probe}="$kernel" ... Custom driver binding can happen in earlier rules by something like: ACTION=="add", SUBSYSTEM=="usb", \ ATTRS{idVendor}=="1234", ATTRS{idProduct}=="5678" \ ATTR{subsystem/drivers//bind}="$kernel" This is intended to solve the modprobe.conf mess with "install-rules", custom bind/unbind-scripts and all the weird things people invented over the years. It should also provide the functionality "libusual" was supposed to do. With udev, one can just write a udev rule to drive all USB-disks at the third port of USB-hub by the "ub" driver, and everything else by usb-storage. One can also instruct udev to bind different wireless drivers to identical cards - just selected by the pcmcia slot-number, and whatever ... To use the mentioned rules, it needs udev version 106, to be able to write ATTR{}="$kernel" to sysfs files. Signed-off-by: Greg Kroah-Hartman --- drivers/base/bus.c | 84 ++++++++++++++++++++++++++++++++++++++++++++++---- include/linux/device.h | 34 +++++++++++--------- 2 files changed, 97 insertions(+), 21 deletions(-) diff --git a/drivers/base/bus.c b/drivers/base/bus.c index 253868e03c7..9df2e6dff51 100644 --- a/drivers/base/bus.c +++ b/drivers/base/bus.c @@ -27,6 +27,9 @@ #define to_driver(obj) container_of(obj, struct device_driver, kobj) +static int __must_check bus_rescan_devices_helper(struct device *dev, + void *data); + static ssize_t drv_attr_show(struct kobject * kobj, struct attribute * attr, char * buf) { @@ -133,7 +136,6 @@ static decl_subsys(bus, &ktype_bus, NULL); #ifdef CONFIG_HOTPLUG - /* Manually detach a device from its associated driver. */ static int driver_helper(struct device *dev, void *data) { @@ -199,6 +201,33 @@ static ssize_t driver_bind(struct device_driver *drv, } static DRIVER_ATTR(bind, S_IWUSR, NULL, driver_bind); +static ssize_t show_drivers_autoprobe(struct bus_type *bus, char *buf) +{ + return sprintf(buf, "%d\n", bus->drivers_autoprobe); +} + +static ssize_t store_drivers_autoprobe(struct bus_type *bus, + const char *buf, size_t count) +{ + if (buf[0] == '0') + bus->drivers_autoprobe = 0; + else + bus->drivers_autoprobe = 1; + return count; +} + +static ssize_t store_drivers_probe(struct bus_type *bus, + const char *buf, size_t count) +{ + struct device *dev; + + dev = bus_find_device(bus, NULL, (void *)buf, driver_helper); + if (!dev) + return -ENODEV; + if (bus_rescan_devices_helper(dev, NULL) != 0) + return -EINVAL; + return count; +} #endif static struct device * next_device(struct klist_iter * i) @@ -425,7 +454,8 @@ int bus_attach_device(struct device * dev) if (bus) { dev->is_registered = 1; - ret = device_attach(dev); + if (bus->drivers_autoprobe) + ret = device_attach(dev); if (ret >= 0) { klist_add_tail(&dev->knode_bus, &bus->klist_devices); ret = 0; @@ -515,9 +545,41 @@ static void remove_bind_files(struct device_driver *drv) driver_remove_file(drv, &driver_attr_bind); driver_remove_file(drv, &driver_attr_unbind); } + +static int add_probe_files(struct bus_type *bus) +{ + int retval; + + bus->drivers_probe_attr.attr.name = "drivers_probe"; + bus->drivers_probe_attr.attr.mode = S_IWUSR; + bus->drivers_probe_attr.attr.owner = bus->owner; + bus->drivers_probe_attr.store = store_drivers_probe; + retval = bus_create_file(bus, &bus->drivers_probe_attr); + if (retval) + goto out; + + bus->drivers_autoprobe_attr.attr.name = "drivers_autoprobe"; + bus->drivers_autoprobe_attr.attr.mode = S_IWUSR | S_IRUGO; + bus->drivers_autoprobe_attr.attr.owner = bus->owner; + bus->drivers_autoprobe_attr.show = show_drivers_autoprobe; + bus->drivers_autoprobe_attr.store = store_drivers_autoprobe; + retval = bus_create_file(bus, &bus->drivers_autoprobe_attr); + if (retval) + bus_remove_file(bus, &bus->drivers_probe_attr); +out: + return retval; +} + +static void remove_probe_files(struct bus_type *bus) +{ + bus_remove_file(bus, &bus->drivers_autoprobe_attr); + bus_remove_file(bus, &bus->drivers_probe_attr); +} #else static inline int add_bind_files(struct device_driver *drv) { return 0; } static inline void remove_bind_files(struct device_driver *drv) {} +static inline int add_probe_files(struct bus_type *bus) { return 0; } +static inline void remove_probe_files(struct bus_type *bus) {} #endif /** @@ -541,9 +603,11 @@ int bus_add_driver(struct device_driver *drv) if ((error = kobject_register(&drv->kobj))) goto out_put_bus; - error = driver_attach(drv); - if (error) - goto out_unregister; + if (drv->bus->drivers_autoprobe) { + error = driver_attach(drv); + if (error) + goto out_unregister; + } klist_add_tail(&drv->knode_bus, &bus->klist_drivers); module_add_driver(drv->owner, drv); @@ -762,6 +826,12 @@ int bus_register(struct bus_type * bus) klist_init(&bus->klist_devices, klist_devices_get, klist_devices_put); klist_init(&bus->klist_drivers, NULL, NULL); + + bus->drivers_autoprobe = 1; + retval = add_probe_files(bus); + if (retval) + goto bus_probe_files_fail; + retval = bus_add_attrs(bus); if (retval) goto bus_attrs_fail; @@ -770,6 +840,8 @@ int bus_register(struct bus_type * bus) return 0; bus_attrs_fail: + remove_probe_files(bus); +bus_probe_files_fail: kset_unregister(&bus->drivers); bus_drivers_fail: kset_unregister(&bus->devices); @@ -779,7 +851,6 @@ out: return retval; } - /** * bus_unregister - remove a bus from the system * @bus: bus. @@ -791,6 +862,7 @@ void bus_unregister(struct bus_type * bus) { pr_debug("bus %s: unregistering\n", bus->name); bus_remove_attrs(bus); + remove_probe_files(bus); kset_unregister(&bus->drivers); kset_unregister(&bus->devices); subsystem_unregister(&bus->subsys); diff --git a/include/linux/device.h b/include/linux/device.h index de0e73eae6b..9d54fe13eb2 100644 --- a/include/linux/device.h +++ b/include/linux/device.h @@ -34,9 +34,24 @@ struct device; struct device_driver; struct class; struct class_device; +struct bus_type; + +struct bus_attribute { + struct attribute attr; + ssize_t (*show)(struct bus_type *, char * buf); + ssize_t (*store)(struct bus_type *, const char * buf, size_t count); +}; + +#define BUS_ATTR(_name,_mode,_show,_store) \ +struct bus_attribute bus_attr_##_name = __ATTR(_name,_mode,_show,_store) + +extern int __must_check bus_create_file(struct bus_type *, + struct bus_attribute *); +extern void bus_remove_file(struct bus_type *, struct bus_attribute *); struct bus_type { const char * name; + struct module * owner; struct subsystem subsys; struct kset drivers; @@ -49,6 +64,8 @@ struct bus_type { struct bus_attribute * bus_attrs; struct device_attribute * dev_attrs; struct driver_attribute * drv_attrs; + struct bus_attribute drivers_autoprobe_attr; + struct bus_attribute drivers_probe_attr; int (*match)(struct device * dev, struct device_driver * drv); int (*uevent)(struct device *dev, char **envp, @@ -61,6 +78,8 @@ struct bus_type { int (*suspend_late)(struct device * dev, pm_message_t state); int (*resume_early)(struct device * dev); int (*resume)(struct device * dev); + + unsigned int drivers_autoprobe:1; }; extern int __must_check bus_register(struct bus_type * bus); @@ -102,21 +121,6 @@ extern int bus_unregister_notifier(struct bus_type *bus, #define BUS_NOTIFY_UNBIND_DRIVER 0x00000004 /* driver about to be unbound */ -/* sysfs interface for exporting bus attributes */ - -struct bus_attribute { - struct attribute attr; - ssize_t (*show)(struct bus_type *, char * buf); - ssize_t (*store)(struct bus_type *, const char * buf, size_t count); -}; - -#define BUS_ATTR(_name,_mode,_show,_store) \ -struct bus_attribute bus_attr_##_name = __ATTR(_name,_mode,_show,_store) - -extern int __must_check bus_create_file(struct bus_type *, - struct bus_attribute *); -extern void bus_remove_file(struct bus_type *, struct bus_attribute *); - struct device_driver { const char * name; struct bus_type * bus; -- cgit v1.2.3-18-g5258 From 621a1672f7377e08a942f205d6742d8af1292aab Mon Sep 17 00:00:00 2001 From: Dmitry Torokhov Date: Sat, 10 Mar 2007 01:37:34 -0500 Subject: driver core: Use attribute groups in struct device_type Driver core: use attribute groups in struct device_type Attribute groups are more flexible than attribute lists (an attribute list can be represented by anonymous group) so switch struct device_type to use them. Also rework attribute creation for devices so that they all cleaned up properly in case of errors. Signed-off-by: Dmitry Torokhov Cc: Kay Sievers Signed-off-by: Greg Kroah-Hartman --- drivers/base/core.c | 115 +++++++++++++++++++++++++++++-------------------- include/linux/device.h | 2 +- 2 files changed, 70 insertions(+), 47 deletions(-) diff --git a/drivers/base/core.c b/drivers/base/core.c index 9ea12d9b48a..bb2cc37a4d4 100644 --- a/drivers/base/core.c +++ b/drivers/base/core.c @@ -246,64 +246,95 @@ static ssize_t store_uevent(struct device *dev, struct device_attribute *attr, return count; } -static int device_add_groups(struct device *dev) +static int device_add_attributes(struct device *dev, + struct device_attribute *attrs) +{ + int error = 0; + int i; + + if (attrs) { + for (i = 0; attr_name(attrs[i]); i++) { + error = device_create_file(dev, &attrs[i]); + if (error) + break; + } + if (error) + while (--i >= 0) + device_remove_file(dev, &attrs[i]); + } + return error; +} + +static void device_remove_attributes(struct device *dev, + struct device_attribute *attrs) { int i; + + if (attrs) + for (i = 0; attr_name(attrs[i]); i++) + device_remove_file(dev, &attrs[i]); +} + +static int device_add_groups(struct device *dev, + struct attribute_group **groups) +{ int error = 0; + int i; - if (dev->groups) { - for (i = 0; dev->groups[i]; i++) { - error = sysfs_create_group(&dev->kobj, dev->groups[i]); + if (groups) { + for (i = 0; groups[i]; i++) { + error = sysfs_create_group(&dev->kobj, groups[i]); if (error) { while (--i >= 0) - sysfs_remove_group(&dev->kobj, dev->groups[i]); - goto out; + sysfs_remove_group(&dev->kobj, groups[i]); + break; } } } -out: return error; } -static void device_remove_groups(struct device *dev) +static void device_remove_groups(struct device *dev, + struct attribute_group **groups) { int i; - if (dev->groups) { - for (i = 0; dev->groups[i]; i++) { - sysfs_remove_group(&dev->kobj, dev->groups[i]); - } - } + + if (groups) + for (i = 0; groups[i]; i++) + sysfs_remove_group(&dev->kobj, groups[i]); } static int device_add_attrs(struct device *dev) { struct class *class = dev->class; struct device_type *type = dev->type; - int error = 0; - int i; + int error; - if (class && class->dev_attrs) { - for (i = 0; attr_name(class->dev_attrs[i]); i++) { - error = device_create_file(dev, &class->dev_attrs[i]); - if (error) - break; - } + if (class) { + error = device_add_attributes(dev, class->dev_attrs); if (error) - while (--i >= 0) - device_remove_file(dev, &class->dev_attrs[i]); + return error; } - if (type && type->attrs) { - for (i = 0; attr_name(type->attrs[i]); i++) { - error = device_create_file(dev, &type->attrs[i]); - if (error) - break; - } + if (type) { + error = device_add_groups(dev, type->groups); if (error) - while (--i >= 0) - device_remove_file(dev, &type->attrs[i]); + goto err_remove_class_attrs; } + error = device_add_groups(dev, dev->groups); + if (error) + goto err_remove_type_groups; + + return 0; + + err_remove_type_groups: + if (type) + device_remove_groups(dev, type->groups); + err_remove_class_attrs: + if (class) + device_remove_attributes(dev, class->dev_attrs); + return error; } @@ -311,17 +342,14 @@ static void device_remove_attrs(struct device *dev) { struct class *class = dev->class; struct device_type *type = dev->type; - int i; - if (class && class->dev_attrs) { - for (i = 0; attr_name(class->dev_attrs[i]); i++) - device_remove_file(dev, &class->dev_attrs[i]); - } + device_remove_groups(dev, dev->groups); - if (type && type->attrs) { - for (i = 0; attr_name(type->attrs[i]); i++) - device_remove_file(dev, &type->attrs[i]); - } + if (type) + device_remove_groups(dev, type->groups); + + if (class) + device_remove_attributes(dev, class->dev_attrs); } @@ -638,8 +666,6 @@ int device_add(struct device *dev) if ((error = device_add_attrs(dev))) goto AttrsError; - if ((error = device_add_groups(dev))) - goto GroupError; if ((error = device_pm_add(dev))) goto PMError; if ((error = bus_add_device(dev))) @@ -663,7 +689,7 @@ int device_add(struct device *dev) up(&dev->class->sem); } Done: - kfree(class_name); + kfree(class_name); put_device(dev); return error; AttachError: @@ -674,8 +700,6 @@ int device_add(struct device *dev) if (dev->bus) blocking_notifier_call_chain(&dev->bus->bus_notifier, BUS_NOTIFY_DEL_DEVICE, dev); - device_remove_groups(dev); - GroupError: device_remove_attrs(dev); AttrsError: if (dev->devt_attr) { @@ -838,7 +862,6 @@ void device_del(struct device * dev) } } device_remove_file(dev, &dev->uevent_attr); - device_remove_groups(dev); device_remove_attrs(dev); bus_remove_device(dev); diff --git a/include/linux/device.h b/include/linux/device.h index 9d54fe13eb2..3b64fdecd04 100644 --- a/include/linux/device.h +++ b/include/linux/device.h @@ -332,7 +332,7 @@ extern struct class_device *class_device_create(struct class *cls, extern void class_device_destroy(struct class *cls, dev_t devt); struct device_type { - struct device_attribute *attrs; + struct attribute_group **groups; int (*uevent)(struct device *dev, char **envp, int num_envp, char *buffer, int buffer_size); void (*release)(struct device *dev); -- cgit v1.2.3-18-g5258 From 414264f959cf46f49f974b3510400e12ac3624a6 Mon Sep 17 00:00:00 2001 From: Kay Sievers Date: Mon, 12 Mar 2007 21:08:57 +0100 Subject: Driver core: add name to device_type If "name" of a device_type is specified, the uevent will contain the device_type name in the DEVTYPE variable. This helps userspace to distingiush between different types of devices, belonging to the same subsystem. Signed-off-by: Kay Sievers Signed-off-by: Greg Kroah-Hartman --- drivers/base/core.c | 5 +++++ include/linux/device.h | 10 ++++++++++ 2 files changed, 15 insertions(+) diff --git a/drivers/base/core.c b/drivers/base/core.c index bb2cc37a4d4..bffb69e4bde 100644 --- a/drivers/base/core.c +++ b/drivers/base/core.c @@ -157,6 +157,11 @@ static int dev_uevent(struct kset *kset, struct kobject *kobj, char **envp, "MINOR=%u", MINOR(dev->devt)); } + if (dev->type && dev->type->name) + add_uevent_var(envp, num_envp, &i, + buffer, buffer_size, &length, + "DEVTYPE=%s", dev->type->name); + if (dev->driver) add_uevent_var(envp, num_envp, &i, buffer, buffer_size, &length, diff --git a/include/linux/device.h b/include/linux/device.h index 3b64fdecd04..7f63d4de5c4 100644 --- a/include/linux/device.h +++ b/include/linux/device.h @@ -331,7 +331,17 @@ extern struct class_device *class_device_create(struct class *cls, __attribute__((format(printf,5,6))); extern void class_device_destroy(struct class *cls, dev_t devt); +/* + * The type of device, "struct device" is embedded in. A class + * or bus can contain devices of different types + * like "partitions" and "disks", "mouse" and "event". + * This identifies the device type and carries type-specific + * information, equivalent to the kobj_type of a kobject. + * If "name" is specified, the uevent will contain it in + * the DEVTYPE variable. + */ struct device_type { + const char *name; struct attribute_group **groups; int (*uevent)(struct device *dev, char **envp, int num_envp, char *buffer, int buffer_size); -- cgit v1.2.3-18-g5258 From 460f7e9a1bde2c74f060f7ce0a308dab4be6a56b Mon Sep 17 00:00:00 2001 From: Dmitriy Monakhov Date: Sat, 10 Mar 2007 14:00:10 +0300 Subject: kobject: kobject_shadow_add cleanup - correct function name in comments - parrent assignment does metter only inside "if" block, so move it inside this block. Signed-off-by: Monakhov Dmitriy Signed-off-by: Greg Kroah-Hartman --- lib/kobject.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/lib/kobject.c b/lib/kobject.c index f6645515560..bbbfab4145e 100644 --- a/lib/kobject.c +++ b/lib/kobject.c @@ -157,7 +157,7 @@ static void unlink(struct kobject * kobj) } /** - * kobject_add - add an object to the hierarchy. + * kobject_shadow_add - add an object to the hierarchy. * @kobj: object. * @shadow_parent: sysfs directory to add to. */ @@ -190,8 +190,8 @@ int kobject_shadow_add(struct kobject * kobj, struct dentry *shadow_parent) list_add_tail(&kobj->entry,&kobj->kset->list); spin_unlock(&kobj->kset->list_lock); + kobj->parent = parent; } - kobj->parent = parent; error = create_dir(kobj, shadow_parent); if (error) { -- cgit v1.2.3-18-g5258 From 21c7f30b1d3f8a3de3128478daca3ce203fc8733 Mon Sep 17 00:00:00 2001 From: Cornelia Huck Date: Mon, 5 Feb 2007 16:15:25 -0800 Subject: driver core: per-subsystem multithreaded probing Make multithreaded probing work per subsystem instead of per driver. It doesn't make much sense to probe the same device for multiple drivers in parallel (after all, only one driver can bind to the device). Instead, create a probing thread for each device that probes the drivers one after another. Also make the decision to use multi-threaded probe per bus instead of per device and adapt the pci code. Signed-off-by: Cornelia Huck Cc: Benjamin Herrenschmidt Signed-off-by: Andrew Morton Signed-off-by: Greg Kroah-Hartman --- drivers/base/dd.c | 62 ++++++++++++++++++++++++------------------------ drivers/pci/pci-driver.c | 6 +---- include/linux/device.h | 3 +-- include/linux/pci.h | 2 -- 4 files changed, 33 insertions(+), 40 deletions(-) diff --git a/drivers/base/dd.c b/drivers/base/dd.c index 6a48824e43f..616b4bbacf1 100644 --- a/drivers/base/dd.c +++ b/drivers/base/dd.c @@ -94,19 +94,11 @@ int device_bind_driver(struct device *dev) return ret; } -struct stupid_thread_structure { - struct device_driver *drv; - struct device *dev; -}; - static atomic_t probe_count = ATOMIC_INIT(0); static DECLARE_WAIT_QUEUE_HEAD(probe_waitqueue); -static int really_probe(void *void_data) +static int really_probe(struct device *dev, struct device_driver *drv) { - struct stupid_thread_structure *data = void_data; - struct device_driver *drv = data->drv; - struct device *dev = data->dev; int ret = 0; atomic_inc(&probe_count); @@ -154,7 +146,6 @@ probe_failed: */ ret = 0; done: - kfree(data); atomic_dec(&probe_count); wake_up(&probe_waitqueue); return ret; @@ -186,16 +177,14 @@ int driver_probe_done(void) * format of the ID structures, nor what is to be considered a match and * what is not. * - * 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 returns 1 if a match is found, -ENODEV if the device is + * not registered, and 0 otherwise. * * This function must be called with @dev->sem held. When called for a * USB interface, @dev->parent->sem must be held as well. */ int driver_probe_device(struct device_driver * drv, struct device * dev) { - struct stupid_thread_structure *data; - struct task_struct *probe_task; int ret = 0; if (!device_is_registered(dev)) @@ -206,19 +195,7 @@ int driver_probe_device(struct device_driver * drv, struct device * dev) pr_debug("%s: Matched Device %s with Driver %s\n", drv->bus->name, dev->bus_id, drv->name); - data = kmalloc(sizeof(*data), GFP_KERNEL); - if (!data) - return -ENOMEM; - data->drv = drv; - data->dev = dev; - - if (drv->multithread_probe) { - probe_task = kthread_run(really_probe, data, - "probe-%s", dev->bus_id); - if (IS_ERR(probe_task)) - ret = really_probe(data); - } else - ret = really_probe(data); + ret = really_probe(dev, drv); done: return ret; @@ -230,30 +207,53 @@ static int __device_attach(struct device_driver * drv, void * data) return driver_probe_device(drv, dev); } +static int device_probe_drivers(void *data) +{ + struct device *dev = data; + int ret = 0; + + if (dev->bus) { + down(&dev->sem); + ret = bus_for_each_drv(dev->bus, NULL, dev, __device_attach); + up(&dev->sem); + } + return ret; +} + /** * 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. + * pair is found, break out and return. If the bus specifies + * multithreaded probing, walking the list of drivers is done + * on a probing thread. * * Returns 1 if the device was bound to a driver; - * 0 if no matching device was found; error code otherwise. + * 0 if no matching device was found or multithreaded probing is done; + * error code otherwise. * * When called for a USB interface, @dev->parent->sem must be held. */ int device_attach(struct device * dev) { int ret = 0; + struct task_struct *probe_task = ERR_PTR(-ENOMEM); down(&dev->sem); if (dev->driver) { ret = device_bind_driver(dev); if (ret == 0) ret = 1; - } else - ret = bus_for_each_drv(dev->bus, NULL, dev, __device_attach); + } else { + if (dev->bus->multithread_probe) + probe_task = kthread_run(device_probe_drivers, dev, + "probe-%s", dev->bus_id); + if(IS_ERR(probe_task)) + ret = bus_for_each_drv(dev->bus, NULL, dev, + __device_attach); + } up(&dev->sem); return ret; } diff --git a/drivers/pci/pci-driver.c b/drivers/pci/pci-driver.c index a3c1755b2f2..39e80fcef4b 100644 --- a/drivers/pci/pci-driver.c +++ b/drivers/pci/pci-driver.c @@ -434,11 +434,6 @@ int __pci_register_driver(struct pci_driver *drv, struct module *owner, drv->driver.mod_name = mod_name; drv->driver.kobj.ktype = &pci_driver_kobj_type; - if (pci_multithread_probe) - drv->driver.multithread_probe = pci_multithread_probe; - else - drv->driver.multithread_probe = drv->multithread_probe; - spin_lock_init(&drv->dynids.lock); INIT_LIST_HEAD(&drv->dynids.list); @@ -574,6 +569,7 @@ struct bus_type pci_bus_type = { static int __init pci_driver_init(void) { + pci_bus_type.multithread_probe = pci_multithread_probe; return bus_register(&pci_bus_type); } diff --git a/include/linux/device.h b/include/linux/device.h index 7f63d4de5c4..eb1fff0b1d2 100644 --- a/include/linux/device.h +++ b/include/linux/device.h @@ -80,6 +80,7 @@ struct bus_type { int (*resume)(struct device * dev); unsigned int drivers_autoprobe:1; + unsigned int multithread_probe:1; }; extern int __must_check bus_register(struct bus_type * bus); @@ -139,8 +140,6 @@ struct device_driver { void (*shutdown) (struct device * dev); int (*suspend) (struct device * dev, pm_message_t state); int (*resume) (struct device * dev); - - unsigned int multithread_probe:1; }; diff --git a/include/linux/pci.h b/include/linux/pci.h index 481ea0663f1..a3ad76221c6 100644 --- a/include/linux/pci.h +++ b/include/linux/pci.h @@ -361,8 +361,6 @@ struct pci_driver { struct pci_error_handlers *err_handler; struct device_driver driver; struct pci_dynids dynids; - - int multithread_probe; }; #define to_pci_driver(drv) container_of(drv,struct pci_driver, driver) -- cgit v1.2.3-18-g5258 From eed40d3ad2ba652c08422d62a5ff6f62ac0be16d Mon Sep 17 00:00:00 2001 From: Andrew Morton Date: Mon, 5 Feb 2007 16:15:25 -0800 Subject: powerpc: make it compile for multithread change arch/powerpc/kernel/of_platform.c:479: error: unknown field `multithread_probe' specified in initializer Cc: Paul Mackerras Cc: Benjamin Herrenschmidt Signed-off-by: Andrew Morton Signed-off-by: Greg Kroah-Hartman --- arch/powerpc/kernel/of_platform.c | 3 --- 1 file changed, 3 deletions(-) diff --git a/arch/powerpc/kernel/of_platform.c b/arch/powerpc/kernel/of_platform.c index b7345176b39..9e7a4d249f0 100644 --- a/arch/powerpc/kernel/of_platform.c +++ b/arch/powerpc/kernel/of_platform.c @@ -475,9 +475,6 @@ static struct of_platform_driver of_pci_phb_driver = { .name = "of-pci", .match_table = of_pci_phb_ids, .probe = of_pci_phb_probe, - .driver = { - .multithread_probe = 1, - }, }; static __init int of_pci_phb_init(void) -- cgit v1.2.3-18-g5258 From c6a46696f97ff260a4ecce5e287f8de4b9d7fe14 Mon Sep 17 00:00:00 2001 From: Cornelia Huck Date: Mon, 5 Feb 2007 16:15:26 -0800 Subject: driver core: don't fail attaching the device if it cannot be bound Don't fail bus_attach_device() if the device cannot be bound. If dev->driver has been specified, reset it to NULL if device_bind_driver() failed and add the device as an unbound device. As a result, bus_attach_device() now cannot fail, and we can remove some checking from device_add(). Also remove an unneeded check in bus_rescan_devices_helper(). Signed-off-by: Cornelia Huck Signed-off-by: Andrew Morton Signed-off-by: Greg Kroah-Hartman --- drivers/base/base.h | 2 +- drivers/base/bus.c | 11 ++++------- drivers/base/core.c | 5 +---- drivers/base/dd.c | 6 +++++- 4 files changed, 11 insertions(+), 13 deletions(-) diff --git a/drivers/base/base.h b/drivers/base/base.h index de7e1442ce6..d597f2659b2 100644 --- a/drivers/base/base.h +++ b/drivers/base/base.h @@ -16,7 +16,7 @@ extern int cpu_dev_init(void); extern int attribute_container_init(void); extern int bus_add_device(struct device * dev); -extern int bus_attach_device(struct device * dev); +extern void bus_attach_device(struct device * dev); extern void bus_remove_device(struct device * dev); extern struct bus_type *get_bus(struct bus_type * bus); extern void put_bus(struct bus_type * bus); diff --git a/drivers/base/bus.c b/drivers/base/bus.c index 9df2e6dff51..20b6dc8706f 100644 --- a/drivers/base/bus.c +++ b/drivers/base/bus.c @@ -447,7 +447,7 @@ out_put: * - Add device to bus's list of devices. * - Try to attach to driver. */ -int bus_attach_device(struct device * dev) +void bus_attach_device(struct device * dev) { struct bus_type *bus = dev->bus; int ret = 0; @@ -456,13 +456,12 @@ int bus_attach_device(struct device * dev) dev->is_registered = 1; if (bus->drivers_autoprobe) ret = device_attach(dev); - if (ret >= 0) { + WARN_ON(ret < 0); + if (ret >= 0) klist_add_tail(&dev->knode_bus, &bus->klist_devices); - ret = 0; - } else + else dev->is_registered = 0; } - return ret; } /** @@ -669,8 +668,6 @@ static int __must_check bus_rescan_devices_helper(struct device *dev, ret = device_attach(dev); if (dev->parent) up(&dev->parent->sem); - if (ret > 0) - ret = 0; } return ret < 0 ? ret : 0; } diff --git a/drivers/base/core.c b/drivers/base/core.c index bffb69e4bde..be6aeb43079 100644 --- a/drivers/base/core.c +++ b/drivers/base/core.c @@ -677,8 +677,7 @@ int device_add(struct device *dev) goto BusError; if (!dev->uevent_suppress) kobject_uevent(&dev->kobj, KOBJ_ADD); - if ((error = bus_attach_device(dev))) - goto AttachError; + bus_attach_device(dev); if (parent) klist_add_tail(&dev->knode_parent, &parent->klist_children); @@ -697,8 +696,6 @@ int device_add(struct device *dev) kfree(class_name); put_device(dev); return error; - AttachError: - bus_remove_device(dev); BusError: device_pm_remove(dev); PMError: diff --git a/drivers/base/dd.c b/drivers/base/dd.c index 616b4bbacf1..18dba8e78da 100644 --- a/drivers/base/dd.c +++ b/drivers/base/dd.c @@ -232,7 +232,7 @@ static int device_probe_drivers(void *data) * * Returns 1 if the device was bound to a driver; * 0 if no matching device was found or multithreaded probing is done; - * error code otherwise. + * -ENODEV if the device is not registered. * * When called for a USB interface, @dev->parent->sem must be held. */ @@ -246,6 +246,10 @@ int device_attach(struct device * dev) ret = device_bind_driver(dev); if (ret == 0) ret = 1; + else { + dev->driver = NULL; + ret = 0; + } } else { if (dev->bus->multithread_probe) probe_task = kthread_run(device_probe_drivers, dev, -- cgit v1.2.3-18-g5258 From 74e9f5fa1570f956c96dd5d3f1053daedbbf01a0 Mon Sep 17 00:00:00 2001 From: Greg Kroah-Hartman Date: Tue, 9 Apr 2002 12:14:34 -0700 Subject: Driver core: remove unneeded completion from driver release path The completion in the driver release path is due to ancient history in the _very_ early 2.5 days when we were not tracking the module reference count of attributes. It is not needed at all and can be removed. Note, we now have an empty release function for the driver structure. This is due to the fact that drivers are statically allocated in the system at this point in time, something which I want to change in the future. But remember, drivers are really code, which is reference counted by the module, unlike devices, which are data and _must_ be reference counted properly in order to work correctly. Cc: Kay Sievers Signed-off-by: Greg Kroah-Hartman --- drivers/base/bus.c | 15 +++++++++++++-- drivers/base/driver.c | 20 -------------------- include/linux/device.h | 1 - 3 files changed, 13 insertions(+), 23 deletions(-) diff --git a/drivers/base/bus.c b/drivers/base/bus.c index 20b6dc8706f..1a5a350eca1 100644 --- a/drivers/base/bus.c +++ b/drivers/base/bus.c @@ -63,8 +63,19 @@ static struct sysfs_ops driver_sysfs_ops = { static void driver_release(struct kobject * kobj) { - struct device_driver * drv = to_driver(kobj); - complete(&drv->unloaded); + /* + * Yes this is an empty release function, it is this way because struct + * device is always a static object, not a dynamic one. Yes, this is + * not nice and bad, but remember, drivers are code, reference counted + * by the module count, not a device, which is really data. And yes, + * in the future I do want to have all drivers be created dynamically, + * and am working toward that goal, but it will take a bit longer... + * + * But do not let this example give _anyone_ the idea that they can + * create a release function without any code in it at all, to do that + * is almost always wrong. If you have any questions about this, + * please send an email to + */ } static struct kobj_type ktype_driver = { diff --git a/drivers/base/driver.c b/drivers/base/driver.c index 082bfded385..eb11475293e 100644 --- a/drivers/base/driver.c +++ b/drivers/base/driver.c @@ -149,10 +149,6 @@ void put_driver(struct device_driver * drv) * We pass off most of the work to the bus_add_driver() call, * since most of the things we have to do deal with the bus * structures. - * - * The one interesting aspect is that we setup @drv->unloaded - * as a completion that gets complete when the driver reference - * count reaches 0. */ int driver_register(struct device_driver * drv) { @@ -162,35 +158,19 @@ int driver_register(struct device_driver * drv) printk(KERN_WARNING "Driver '%s' needs updating - please use bus_type methods\n", drv->name); } klist_init(&drv->klist_devices, NULL, NULL); - init_completion(&drv->unloaded); return bus_add_driver(drv); } - /** * driver_unregister - remove driver from system. * @drv: driver. * * Again, we pass off most of the work to the bus-level call. - * - * Though, once that is done, we wait until @drv->unloaded is completed. - * This will block until the driver refcount reaches 0, and it is - * released. Only modular drivers will call this function, and we - * have to guarantee that it won't complete, letting the driver - * unload until all references are gone. */ void driver_unregister(struct device_driver * drv) { bus_remove_driver(drv); - /* - * If the driver is a module, we are probably in - * the module unload path, and we want to wait - * for everything to unload before we can actually - * finish the unload. - */ - if (drv->owner) - wait_for_completion(&drv->unloaded); } /** diff --git a/include/linux/device.h b/include/linux/device.h index eb1fff0b1d2..c9dc458e8e5 100644 --- a/include/linux/device.h +++ b/include/linux/device.h @@ -126,7 +126,6 @@ struct device_driver { const char * name; struct bus_type * bus; - struct completion unloaded; struct kobject kobj; struct klist klist_devices; struct klist_node knode_bus; -- cgit v1.2.3-18-g5258 From 1b0b3b9980e482ab7c603430462538334f69f14a Mon Sep 17 00:00:00 2001 From: Oliver Neukum Date: Mon, 2 Apr 2007 14:47:59 +0200 Subject: kref: fix CPU ordering with respect to krefs some atomic operations are only atomic, not ordered. Thus a CPU is allowed to reorder memory references to an object to before the reference is obtained. This fixes it. Signed-off-by: Oliver Neukum Signed-off-by: Greg Kroah-Hartman --- lib/kref.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/lib/kref.c b/lib/kref.c index 0d07cc31c81..a6dc3ec328e 100644 --- a/lib/kref.c +++ b/lib/kref.c @@ -21,6 +21,7 @@ void kref_init(struct kref *kref) { atomic_set(&kref->refcount,1); + smp_mb(); } /** @@ -31,6 +32,7 @@ void kref_get(struct kref *kref) { WARN_ON(!atomic_read(&kref->refcount)); atomic_inc(&kref->refcount); + smp_mb__after_atomic_inc(); } /** -- cgit v1.2.3-18-g5258 From ca2f37dbc5324c7278577731033a358f1f86050a Mon Sep 17 00:00:00 2001 From: Jean Tourrilhes Date: Wed, 7 Mar 2007 10:49:30 -0800 Subject: Driver core: notify userspace of network device renames Provide rename event for when we rename network devices. Signed-off-by: Jean Tourrilhes Cc: Kay Sievers Signed-off-by: Greg Kroah-Hartman --- lib/kobject.c | 30 ++++++++++++++++++++++++++++++ net/core/net-sysfs.c | 11 +++++++++++ 2 files changed, 41 insertions(+) diff --git a/lib/kobject.c b/lib/kobject.c index bbbfab4145e..db1d23707eb 100644 --- a/lib/kobject.c +++ b/lib/kobject.c @@ -311,13 +311,43 @@ EXPORT_SYMBOL(kobject_set_name); int kobject_rename(struct kobject * kobj, const char *new_name) { int error = 0; + const char *devpath = NULL; + char *devpath_string = NULL; + char *envp[2]; kobj = kobject_get(kobj); if (!kobj) return -EINVAL; if (!kobj->parent) return -EINVAL; + + devpath = kobject_get_path(kobj, GFP_KERNEL); + if (!devpath) { + error = -ENOMEM; + goto out; + } + devpath_string = kmalloc(strlen(devpath) + 15, GFP_KERNEL); + if (!devpath_string) { + error = -ENOMEM; + goto out; + } + sprintf(devpath_string, "DEVPATH_OLD=%s", devpath); + envp[0] = devpath_string; + envp[1] = NULL; + /* Note : if we want to send the new name alone, not the full path, + * we could probably use kobject_name(kobj); */ + error = sysfs_rename_dir(kobj, kobj->parent->dentry, new_name); + + /* This function is mostly/only used for network interface. + * Some hotplug package track interfaces by their name and + * therefore want to know when the name is changed by the user. */ + if (!error) + kobject_uevent_env(kobj, KOBJ_MOVE, envp); + +out: + kfree(devpath_string); + kfree(devpath); kobject_put(kobj); return error; diff --git a/net/core/net-sysfs.c b/net/core/net-sysfs.c index 221a64ab64f..e441ec7988c 100644 --- a/net/core/net-sysfs.c +++ b/net/core/net-sysfs.c @@ -421,6 +421,17 @@ static int netdev_uevent(struct device *d, char **envp, buf += n; size -= n; + if ((size <= 0) || (i >= num_envp)) + return -ENOMEM; + + /* pass ifindex to uevent. + * ifindex is useful as it won't change (interface name may change) + * and is what RtNetlink uses natively. */ + envp[i++] = buf; + n = snprintf(buf, size, "IFINDEX=%d", dev->ifindex) + 1; + buf += n; + size -= n; + if ((size <= 0) || (i >= num_envp)) return -ENOMEM; -- cgit v1.2.3-18-g5258 From 83b5fb4cce13b9f7b218ed88ee05387c3f3bdda3 Mon Sep 17 00:00:00 2001 From: Cornelia Huck Date: Thu, 29 Mar 2007 11:12:11 +0200 Subject: Driver core: suppress uevents via filter Suppress uevents for devices if uevent_suppress is set via dev_uevent_filter(). This makes the driver core suppress all device uevents, not just the add event in device_add(). Signed-off-by: Cornelia Huck Cc: Kay Sievers Signed-off-by: Greg Kroah-Hartman --- drivers/base/core.c | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/drivers/base/core.c b/drivers/base/core.c index be6aeb43079..c34a4d8842e 100644 --- a/drivers/base/core.c +++ b/drivers/base/core.c @@ -120,6 +120,8 @@ static int dev_uevent_filter(struct kset *kset, struct kobject *kobj) if (ktype == &ktype_device) { struct device *dev = to_dev(kobj); + if (dev->uevent_suppress) + return 0; if (dev->bus) return 1; if (dev->class) @@ -675,8 +677,7 @@ int device_add(struct device *dev) goto PMError; if ((error = bus_add_device(dev))) goto BusError; - if (!dev->uevent_suppress) - kobject_uevent(&dev->kobj, KOBJ_ADD); + kobject_uevent(&dev->kobj, KOBJ_ADD); bus_attach_device(dev); if (parent) klist_add_tail(&dev->knode_parent, &parent->klist_children); -- cgit v1.2.3-18-g5258 From bdc4960a0b4831a24276b65f1f7afdfc57f2f5cf Mon Sep 17 00:00:00 2001 From: Cornelia Huck Date: Thu, 29 Mar 2007 11:12:14 +0200 Subject: Driver core: switch firmware_class to uevent_suppress. Use uevent_suppress instead of returning an error code in firmware_uevent(). Get rid of the now unneeded FW_STATUS_READY and FW_STATUS_READY_NOHOTPLUG. Signed-off-by: Cornelia Huck Signed-off-by: Greg Kroah-Hartman --- drivers/base/firmware_class.c | 10 ++-------- 1 file changed, 2 insertions(+), 8 deletions(-) diff --git a/drivers/base/firmware_class.c b/drivers/base/firmware_class.c index c0a979a5074..97ab5bd1c4d 100644 --- a/drivers/base/firmware_class.c +++ b/drivers/base/firmware_class.c @@ -31,8 +31,6 @@ enum { FW_STATUS_LOADING, FW_STATUS_DONE, FW_STATUS_ABORT, - FW_STATUS_READY, - FW_STATUS_READY_NOHOTPLUG, }; static int loading_timeout = 60; /* In seconds */ @@ -96,9 +94,6 @@ static int firmware_uevent(struct device *dev, char **envp, int num_envp, struct firmware_priv *fw_priv = dev_get_drvdata(dev); int i = 0, len = 0; - if (!test_bit(FW_STATUS_READY, &fw_priv->status)) - return -ENODEV; - if (add_uevent_var(envp, num_envp, &i, buffer, buffer_size, &len, "FIRMWARE=%s", fw_priv->fw_id)) return -ENOMEM; @@ -333,6 +328,7 @@ static int fw_register_device(struct device **dev_p, const char *fw_name, f_dev->parent = device; f_dev->class = &firmware_class; dev_set_drvdata(f_dev, fw_priv); + f_dev->uevent_suppress = 1; retval = device_register(f_dev); if (retval) { printk(KERN_ERR "%s: device_register failed\n", @@ -382,9 +378,7 @@ static int fw_setup_device(struct firmware *fw, struct device **dev_p, } if (uevent) - set_bit(FW_STATUS_READY, &fw_priv->status); - else - set_bit(FW_STATUS_READY_NOHOTPLUG, &fw_priv->status); + f_dev->uevent_suppress = 0; *dev_p = f_dev; goto out; -- cgit v1.2.3-18-g5258 From bf62456eb91f3d2ef0736081583d09b0b3c8b7ea Mon Sep 17 00:00:00 2001 From: Eric Rannaud Date: Fri, 30 Mar 2007 22:23:12 -0700 Subject: uevent: use add_uevent_var() instead of open coding it Make use of add_uevent_var() instead of (often incorrectly) open coding it. Signed-off-by: Michael Ellerman Signed-off-by: Eric Rannaud Cc: Kay Sievers Cc: Cornelia Huck Signed-off-by: Andrew Morton Signed-off-by: Greg Kroah-Hartman --- drivers/amba/bus.c | 13 ++++--- drivers/ieee1394/nodemgr.c | 14 ++++---- drivers/mmc/mmc_sysfs.c | 27 ++++++--------- drivers/s390/crypto/ap_bus.c | 28 +++++++--------- net/core/net-sysfs.c | 28 ++++++---------- sound/aoa/soundbus/core.c | 80 ++++++++++++++++++-------------------------- 6 files changed, 79 insertions(+), 111 deletions(-) diff --git a/drivers/amba/bus.c b/drivers/amba/bus.c index fd5475071ac..268e301775f 100644 --- a/drivers/amba/bus.c +++ b/drivers/amba/bus.c @@ -47,14 +47,13 @@ static int amba_match(struct device *dev, struct device_driver *drv) static int amba_uevent(struct device *dev, char **envp, int nr_env, char *buf, int bufsz) { struct amba_device *pcdev = to_amba_device(dev); + int retval = 0, i = 0, len = 0; - if (nr_env < 2) - return -ENOMEM; - - snprintf(buf, bufsz, "AMBA_ID=%08x", pcdev->periphid); - *envp++ = buf; - *envp++ = NULL; - return 0; + retval = add_uevent_var(envp, nr_env, &i, + buf, bufsz, &len, + "AMBA_ID=%08x", pcdev->periphid); + envp[i] = NULL; + return retval; } #else #define amba_uevent NULL diff --git a/drivers/ieee1394/nodemgr.c b/drivers/ieee1394/nodemgr.c index c5ace190bfe..1644e6fd2c8 100644 --- a/drivers/ieee1394/nodemgr.c +++ b/drivers/ieee1394/nodemgr.c @@ -1163,6 +1163,7 @@ static int nodemgr_uevent(struct class_device *cdev, char **envp, int num_envp, struct unit_directory *ud; int i = 0; int length = 0; + int retval = 0; /* ieee1394:venNmoNspNverN */ char buf[8 + 1 + 3 + 8 + 2 + 8 + 2 + 8 + 3 + 8 + 1]; @@ -1176,14 +1177,11 @@ static int nodemgr_uevent(struct class_device *cdev, char **envp, int num_envp, #define PUT_ENVP(fmt,val) \ do { \ - int printed; \ - envp[i++] = buffer; \ - printed = snprintf(buffer, buffer_size - length, \ - fmt, val); \ - if ((buffer_size - (length+printed) <= 0) || (i >= num_envp)) \ - return -ENOMEM; \ - length += printed+1; \ - buffer += printed+1; \ + retval = add_uevent_var(envp, num_envp, &i, \ + buffer, buffer_size, &length, \ + fmt, val); \ + if (retval) \ + return retval; \ } while (0) PUT_ENVP("VENDOR_ID=%06x", ud->vendor_id); diff --git a/drivers/mmc/mmc_sysfs.c b/drivers/mmc/mmc_sysfs.c index d32698b02d7..e0e82d849d5 100644 --- a/drivers/mmc/mmc_sysfs.c +++ b/drivers/mmc/mmc_sysfs.c @@ -86,31 +86,26 @@ mmc_bus_uevent(struct device *dev, char **envp, int num_envp, char *buf, { struct mmc_card *card = dev_to_mmc_card(dev); char ccc[13]; - int i = 0; - -#define add_env(fmt,val) \ - ({ \ - int len, ret = -ENOMEM; \ - if (i < num_envp) { \ - envp[i++] = buf; \ - len = snprintf(buf, buf_size, fmt, val) + 1; \ - buf_size -= len; \ - buf += len; \ - if (buf_size >= 0) \ - ret = 0; \ - } \ - ret; \ - }) + int retval = 0, i = 0, length = 0; + +#define add_env(fmt,val) do { \ + retval = add_uevent_var(envp, num_envp, &i, \ + buf, buf_size, &length, \ + fmt, val); \ + if (retval) \ + return retval; \ +} while (0); for (i = 0; i < 12; i++) ccc[i] = card->csd.cmdclass & (1 << i) ? '1' : '0'; ccc[12] = '\0'; - i = 0; add_env("MMC_CCC=%s", ccc); add_env("MMC_MANFID=%06x", card->cid.manfid); add_env("MMC_NAME=%s", mmc_card_name(card)); add_env("MMC_OEMID=%04x", card->cid.oemid); +#undef add_env + envp[i] = NULL; return 0; } diff --git a/drivers/s390/crypto/ap_bus.c b/drivers/s390/crypto/ap_bus.c index bf37cdf43fa..5aac0ec3636 100644 --- a/drivers/s390/crypto/ap_bus.c +++ b/drivers/s390/crypto/ap_bus.c @@ -423,27 +423,25 @@ static int ap_uevent (struct device *dev, char **envp, int num_envp, char *buffer, int buffer_size) { struct ap_device *ap_dev = to_ap_dev(dev); - int length; + int retval = 0, length = 0, i = 0; if (!ap_dev) return -ENODEV; /* Set up DEV_TYPE environment variable. */ - envp[0] = buffer; - length = scnprintf(buffer, buffer_size, "DEV_TYPE=%04X", - ap_dev->device_type); - if (buffer_size - length <= 0) - return -ENOMEM; - buffer += length; - buffer_size -= length; + retval = add_uevent_var(envp, num_envp, &i, + buffer, buffer_size, &length, + "DEV_TYPE=%04X", ap_dev->device_type); + if (retval) + return retval; + /* Add MODALIAS= */ - envp[1] = buffer; - length = scnprintf(buffer, buffer_size, "MODALIAS=ap:t%02X", - ap_dev->device_type); - if (buffer_size - length <= 0) - return -ENOMEM; - envp[2] = NULL; - return 0; + retval = add_uevent_var(envp, num_envp, &i, + buffer, buffer_size, &length, + "MODALIAS=ap:t%02X", ap_dev->device_type); + + envp[i] = NULL; + return retval; } static struct bus_type ap_bus_type = { diff --git a/net/core/net-sysfs.c b/net/core/net-sysfs.c index e441ec7988c..b21307b15b8 100644 --- a/net/core/net-sysfs.c +++ b/net/core/net-sysfs.c @@ -412,31 +412,25 @@ static int netdev_uevent(struct device *d, char **envp, int num_envp, char *buf, int size) { struct net_device *dev = to_net_dev(d); - int i = 0; - int n; + int retval, len = 0, i = 0; /* pass interface to uevent. */ - envp[i++] = buf; - n = snprintf(buf, size, "INTERFACE=%s", dev->name) + 1; - buf += n; - size -= n; - - if ((size <= 0) || (i >= num_envp)) - return -ENOMEM; + retval = add_uevent_var(envp, num_envp, &i, + buf, size, &len, + "INTERFACE=%s", dev->name); + if (retval) + goto exit; /* pass ifindex to uevent. * ifindex is useful as it won't change (interface name may change) * and is what RtNetlink uses natively. */ - envp[i++] = buf; - n = snprintf(buf, size, "IFINDEX=%d", dev->ifindex) + 1; - buf += n; - size -= n; - - if ((size <= 0) || (i >= num_envp)) - return -ENOMEM; + retval = add_uevent_var(envp, num_envp, &i, + buf, size, &len, + "IFINDEX=%d", dev->ifindex); +exit: envp[i] = NULL; - return 0; + return retval; } #endif diff --git a/sound/aoa/soundbus/core.c b/sound/aoa/soundbus/core.c index 47b3e3768df..418a98a10c7 100644 --- a/sound/aoa/soundbus/core.c +++ b/sound/aoa/soundbus/core.c @@ -61,9 +61,9 @@ static int soundbus_uevent(struct device *dev, char **envp, int num_envp, { struct soundbus_dev * soundbus_dev; struct of_device * of; - char *scratch, *compat, *compat2; - int i = 0; - int length, cplen, cplen2, seen = 0; + char *compat; + int retval = 0, i = 0, length = 0; + int cplen, seen = 0; if (!dev) return -ENODEV; @@ -75,63 +75,47 @@ static int soundbus_uevent(struct device *dev, char **envp, int num_envp, of = &soundbus_dev->ofdev; /* stuff we want to pass to /sbin/hotplug */ - envp[i++] = scratch = buffer; - length = scnprintf (scratch, buffer_size, "OF_NAME=%s", of->node->name); - ++length; - buffer_size -= length; - if ((buffer_size <= 0) || (i >= num_envp)) - return -ENOMEM; - scratch += length; - - envp[i++] = scratch; - length = scnprintf (scratch, buffer_size, "OF_TYPE=%s", of->node->type); - ++length; - buffer_size -= length; - if ((buffer_size <= 0) || (i >= num_envp)) - return -ENOMEM; - scratch += length; + retval = add_uevent_var(envp, num_envp, &i, + buffer, buffer_size, &length, + "OF_NAME=%s", of->node->name); + if (retval) + return retval; + + retval = add_uevent_var(envp, num_envp, &i, + buffer, buffer_size, &length, + "OF_TYPE=%s", of->node->type); + if (retval) + return retval; /* Since the compatible field can contain pretty much anything * it's not really legal to split it out with commas. We split it * up using a number of environment variables instead. */ compat = (char *) get_property(of->node, "compatible", &cplen); - compat2 = compat; - cplen2= cplen; while (compat && cplen > 0) { - envp[i++] = scratch; - length = scnprintf (scratch, buffer_size, - "OF_COMPATIBLE_%d=%s", seen, compat); - ++length; - buffer_size -= length; - if ((buffer_size <= 0) || (i >= num_envp)) - return -ENOMEM; - scratch += length; - length = strlen (compat) + 1; - compat += length; - cplen -= length; - seen++; + int tmp = length; + retval = add_uevent_var(envp, num_envp, &i, + buffer, buffer_size, &length, + "OF_COMPATIBLE_%d=%s", seen, compat); + if (retval) + return retval; + compat += length - tmp; + cplen -= length - tmp; + seen += 1; } - envp[i++] = scratch; - length = scnprintf (scratch, buffer_size, "OF_COMPATIBLE_N=%d", seen); - ++length; - buffer_size -= length; - if ((buffer_size <= 0) || (i >= num_envp)) - return -ENOMEM; - scratch += length; - - envp[i++] = scratch; - length = scnprintf (scratch, buffer_size, "MODALIAS=%s", - soundbus_dev->modalias); - - buffer_size -= length; - if ((buffer_size <= 0) || (i >= num_envp)) - return -ENOMEM; + retval = add_uevent_var(envp, num_envp, &i, + buffer, buffer_size, &length, + "OF_COMPATIBLE_N=%d", seen); + if (retval) + return retval; + retval = add_uevent_var(envp, num_envp, &i, + buffer, buffer_size, &length, + "MODALIAS=%s", soundbus_dev->modalias); envp[i] = NULL; - return 0; + return retval; } static int soundbus_device_remove(struct device *dev) -- cgit v1.2.3-18-g5258 From f89cbc399ecd924c4bd879344e662aace2274b4f Mon Sep 17 00:00:00 2001 From: Dmitry Torokhov Date: Tue, 3 Apr 2007 01:08:40 -0400 Subject: Driver core: add suspend() and resume() to struct device_type Driver core: add suspend() and resume() to struct device_type In cases when there are devices of different types in the same class we can't use class's implementation of suspend and resume methods and we need to add them to struct device_type instead. Also fix error handling in resume code (we should not try to call class's resume method iof bus's resume method for the device failed. Signed-off-by: Dmitry Torokhov Signed-off-by: Greg Kroah-Hartman --- drivers/base/power/resume.c | 13 ++++++++++++- drivers/base/power/suspend.c | 12 ++++++++++++ include/linux/device.h | 2 ++ 3 files changed, 26 insertions(+), 1 deletion(-) diff --git a/drivers/base/power/resume.c b/drivers/base/power/resume.c index 020be36705a..a2c64188d71 100644 --- a/drivers/base/power/resume.c +++ b/drivers/base/power/resume.c @@ -26,7 +26,9 @@ int resume_device(struct device * dev) TRACE_DEVICE(dev); TRACE_RESUME(0); + down(&dev->sem); + if (dev->power.pm_parent && dev->power.pm_parent->power.power_state.event) { dev_err(dev, "PM: resume from %d, parent %s still %d\n", @@ -34,15 +36,24 @@ int resume_device(struct device * dev) dev->power.pm_parent->bus_id, dev->power.pm_parent->power.power_state.event); } + if (dev->bus && dev->bus->resume) { dev_dbg(dev,"resuming\n"); error = dev->bus->resume(dev); } - if (dev->class && dev->class->resume) { + + if (!error && dev->type && dev->type->resume) { + dev_dbg(dev,"resuming\n"); + error = dev->type->resume(dev); + } + + if (!error && dev->class && dev->class->resume) { dev_dbg(dev,"class resume\n"); error = dev->class->resume(dev); } + up(&dev->sem); + TRACE_RESUME(error); return error; } diff --git a/drivers/base/power/suspend.c b/drivers/base/power/suspend.c index ece136bf97e..42d2b86ba76 100644 --- a/drivers/base/power/suspend.c +++ b/drivers/base/power/suspend.c @@ -78,6 +78,18 @@ int suspend_device(struct device * dev, pm_message_t state) suspend_report_result(dev->class->suspend, error); } + if (!error && dev->type && dev->type->suspend && !dev->power.power_state.event) { + dev_dbg(dev, "%s%s\n", + suspend_verb(state.event), + ((state.event == PM_EVENT_SUSPEND) + && device_may_wakeup(dev)) + ? ", may wakeup" + : "" + ); + error = dev->type->suspend(dev, state); + suspend_report_result(dev->type->suspend, error); + } + if (!error && dev->bus && dev->bus->suspend && !dev->power.power_state.event) { dev_dbg(dev, "%s%s\n", suspend_verb(state.event), diff --git a/include/linux/device.h b/include/linux/device.h index c9dc458e8e5..af603a13769 100644 --- a/include/linux/device.h +++ b/include/linux/device.h @@ -344,6 +344,8 @@ struct device_type { int (*uevent)(struct device *dev, char **envp, int num_envp, char *buffer, int buffer_size); void (*release)(struct device *dev); + int (*suspend)(struct device * dev, pm_message_t state); + int (*resume)(struct device * dev); }; /* interface for exp