From 1bb6881acae1c4f11a6e86f04df32ba45e95031d Mon Sep 17 00:00:00 2001 From: Cornelia Huck Date: Fri, 22 Sep 2006 11:37:04 +0200 Subject: driver core fixes: bus_add_attrs() retval check Check return value of bus_add_attrs() in bus_register(). Signed-off-by: Cornelia Huck Signed-off-by: Greg Kroah-Hartman --- drivers/base/bus.c | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) (limited to 'drivers/base/bus.c') diff --git a/drivers/base/bus.c b/drivers/base/bus.c index 12173d16bea..b90f6e6f644 100644 --- a/drivers/base/bus.c +++ b/drivers/base/bus.c @@ -732,11 +732,15 @@ 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_add_attrs(bus); + retval = bus_add_attrs(bus); + if (retval) + goto bus_attrs_fail; pr_debug("bus type '%s' registered\n", bus->name); return 0; +bus_attrs_fail: + kset_unregister(&bus->drivers); bus_drivers_fail: kset_unregister(&bus->devices); bus_devices_fail: -- cgit v1.2.3-70-g09d2 From 513e7337adc32cdfbffecb99953e45a44e812c2d Mon Sep 17 00:00:00 2001 From: Cornelia Huck Date: Fri, 22 Sep 2006 11:37:08 +0200 Subject: driver core fixes: bus_add_device() cleanup on error Correct cleanup in the error path of bus_add_device(). Signed-off-by: Cornelia Huck Signed-off-by: Greg Kroah-Hartman --- drivers/base/bus.c | 19 +++++++++++++++---- 1 file changed, 15 insertions(+), 4 deletions(-) (limited to 'drivers/base/bus.c') diff --git a/drivers/base/bus.c b/drivers/base/bus.c index b90f6e6f644..d516f7d5f16 100644 --- a/drivers/base/bus.c +++ b/drivers/base/bus.c @@ -372,19 +372,30 @@ int bus_add_device(struct device * dev) pr_debug("bus %s: add device %s\n", bus->name, dev->bus_id); error = device_add_attrs(bus, dev); if (error) - goto out; + goto out_put; error = sysfs_create_link(&bus->devices.kobj, &dev->kobj, dev->bus_id); if (error) - goto out; + goto out_id; error = sysfs_create_link(&dev->kobj, &dev->bus->subsys.kset.kobj, "subsystem"); if (error) - goto out; + goto out_subsys; error = sysfs_create_link(&dev->kobj, &dev->bus->subsys.kset.kobj, "bus"); + if (error) + goto out_deprecated; } -out: + return 0; + +out_deprecated: + sysfs_remove_link(&dev->kobj, "subsystem"); +out_subsys: + sysfs_remove_link(&bus->devices.kobj, dev->bus_id); +out_id: + device_remove_attrs(bus, dev); +out_put: + put_bus(dev->bus); return error; } -- cgit v1.2.3-70-g09d2 From f70fa6296c2ec8f541f0a9b406ccc2d9d127d639 Mon Sep 17 00:00:00 2001 From: Alan Stern Date: Thu, 5 Oct 2006 17:03:24 -0400 Subject: Driver core: Don't ignore error returns from probing This patch (as797) fixes device_add() in the driver core. It needs to pay attention when the driver for a new device reports an error. At the same time, since bus_remove_device() undoes the effects of both bus_add_device() and bus_attach_device(), it needs to check whether the bus_attach_device step failed. Signed-off-by: Alan Stern Signed-off-by: Greg Kroah-Hartman --- drivers/base/bus.c | 6 ++++-- drivers/base/core.c | 5 ++++- 2 files changed, 8 insertions(+), 3 deletions(-) (limited to 'drivers/base/bus.c') diff --git a/drivers/base/bus.c b/drivers/base/bus.c index d516f7d5f16..d7c5ea246a0 100644 --- a/drivers/base/bus.c +++ b/drivers/base/bus.c @@ -439,8 +439,10 @@ 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); - dev->is_registered = 0; - klist_del(&dev->knode_bus); + if (dev->is_registered) { + dev->is_registered = 0; + klist_del(&dev->knode_bus); + } pr_debug("bus %s: remove device %s\n", dev->bus->name, dev->bus_id); device_release_driver(dev); put_bus(dev->bus); diff --git a/drivers/base/core.c b/drivers/base/core.c index 41f3dca55cd..68ad11af22b 100644 --- a/drivers/base/core.c +++ b/drivers/base/core.c @@ -479,7 +479,8 @@ int device_add(struct device *dev) if ((error = bus_add_device(dev))) goto BusError; kobject_uevent(&dev->kobj, KOBJ_ADD); - bus_attach_device(dev); + if ((error = bus_attach_device(dev))) + goto AttachError; if (parent) klist_add_tail(&dev->knode_parent, &parent->klist_children); @@ -498,6 +499,8 @@ 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: -- cgit v1.2.3-70-g09d2 From d9fd4d3b317a231e47f31d64d66c8cc7765d458f Mon Sep 17 00:00:00 2001 From: Jeff Garzik Date: Wed, 4 Oct 2006 07:48:03 -0400 Subject: Driver core: bus: remove indentation level Before potentially fixing up these functions, this cosmetic change reduces the indentation level to make the code easier to read and maintain. No functional changes at all. Signed-off-by: Jeff Garzik Signed-off-by: Greg Kroah-Hartman --- drivers/base/bus.c | 77 ++++++++++++++++++++++++++++-------------------------- 1 file changed, 40 insertions(+), 37 deletions(-) (limited to 'drivers/base/bus.c') diff --git a/drivers/base/bus.c b/drivers/base/bus.c index d7c5ea246a0..7d8a7ce73fb 100644 --- a/drivers/base/bus.c +++ b/drivers/base/bus.c @@ -518,34 +518,36 @@ int bus_add_driver(struct device_driver *drv) struct bus_type * bus = get_bus(drv->bus); int error = 0; - if (bus) { - pr_debug("bus %s: add driver %s\n", bus->name, drv->name); - error = kobject_set_name(&drv->kobj, "%s", drv->name); - if (error) - goto out_put_bus; - drv->kobj.kset = &bus->drivers; - if ((error = kobject_register(&drv->kobj))) - goto out_put_bus; - - error = driver_attach(drv); - if (error) - goto out_unregister; - klist_add_tail(&drv->knode_bus, &bus->klist_drivers); - module_add_driver(drv->owner, drv); - - error = driver_add_attrs(bus, drv); - if (error) { - /* How the hell do we get out of this pickle? Give up */ - printk(KERN_ERR "%s: driver_add_attrs(%s) failed\n", - __FUNCTION__, drv->name); - } - error = add_bind_files(drv); - if (error) { - /* Ditto */ - printk(KERN_ERR "%s: add_bind_files(%s) failed\n", - __FUNCTION__, drv->name); - } + if (!bus) + return 0; + + pr_debug("bus %s: add driver %s\n", bus->name, drv->name); + error = kobject_set_name(&drv->kobj, "%s", drv->name); + if (error) + goto out_put_bus; + drv->kobj.kset = &bus->drivers; + if ((error = kobject_register(&drv->kobj))) + goto out_put_bus; + + error = driver_attach(drv); + if (error) + goto out_unregister; + klist_add_tail(&drv->knode_bus, &bus->klist_drivers); + module_add_driver(drv->owner, drv); + + error = driver_add_attrs(bus, drv); + if (error) { + /* How the hell do we get out of this pickle? Give up */ + printk(KERN_ERR "%s: driver_add_attrs(%s) failed\n", + __FUNCTION__, drv->name); + } + error = add_bind_files(drv); + if (error) { + /* Ditto */ + printk(KERN_ERR "%s: add_bind_files(%s) failed\n", + __FUNCTION__, drv->name); } + return error; out_unregister: kobject_unregister(&drv->kobj); @@ -565,16 +567,17 @@ out_put_bus: void bus_remove_driver(struct device_driver * drv) { - if (drv->bus) { - remove_bind_files(drv); - driver_remove_attrs(drv->bus, drv); - klist_remove(&drv->knode_bus); - pr_debug("bus %s: remove driver %s\n", drv->bus->name, drv->name); - driver_detach(drv); - module_remove_driver(drv); - kobject_unregister(&drv->kobj); - put_bus(drv->bus); - } + if (!drv->bus) + return; + + remove_bind_files(drv); + driver_remove_attrs(drv->bus, drv); + klist_remove(&drv->knode_bus); + pr_debug("bus %s: remove driver %s\n", drv->bus->name, drv->name); + driver_detach(drv); + module_remove_driver(drv); + kobject_unregister(&drv->kobj); + put_bus(drv->bus); } -- cgit v1.2.3-70-g09d2 From 116af378201ef793424cd10508ccf18b06d8a021 Mon Sep 17 00:00:00 2001 From: Benjamin Herrenschmidt Date: Wed, 25 Oct 2006 13:44:59 +1000 Subject: Driver core: add notification of bus events I finally did as you suggested and added the notifier to the struct bus_type itself. There are still problems to be expected is something attaches to a bus type where the code can hook in different struct device sub-classes (which is imho a big bogosity but I won't even try to argue that case now) but it will solve nicely a number of issues I've had so far. That also means that clients interested in registering for such notifications have to do it before devices are added and after bus types are registered. Fortunately, most bus types that matter for the various usage scenarios I have in mind are registerd at postcore_initcall time, which means I have a really nice spot at arch_initcall time to add my notifiers. There are 4 notifications provided. Device being added (before hooked to the bus) and removed (failure of previous case or after being unhooked from the bus), along with driver being bound to a device and about to be unbound. The usage I have for these are: - The 2 first ones are used to maintain a struct device_ext that is hooked to struct device.firmware_data. This structure contains for now a pointer to the Open Firmware node related to the device (if any), the NUMA node ID (for quick access to it) and the DMA operations pointers & iommu table instance for DMA to/from this device. For bus types I own (like IBM VIO or EBUS), I just maintain that structure directly from the bus code when creating the devices. But for bus types managed by generic code like PCI or platform (actually, of_platform which is a variation of platform linked to Open Firmware device-tree), I need this notifier. - The other two ones have a completely different usage scenario. I have cases where multiple devices and their drivers depend on each other. For example, the IBM EMAC network driver needs to attach to a MAL DMA engine which is a separate device, and a PHY interface which is also a separate device. They are all of_platform_device's (well, about to be with my upcoming patches) but there is no say in what precise order the core will "probe" them and instanciate the various modules. The solution I found for that is to have the drivers for emac to use multithread_probe, and wait for a driver to be bound to the target MAL and PHY control devices (the device-tree contains reference to the MAL and PHY interface nodes, which I can then match to of_platform_devices). Right now, I've been polling, but with that notifier, I can more cleanly wait (with a timeout of course). Signed-off-by: Benjamin Herrenschmidt Signed-off-by: Greg Kroah-Hartman --- drivers/base/bus.c | 14 ++++++++++++++ drivers/base/core.c | 12 ++++++++++++ drivers/base/dd.c | 10 ++++++++++ include/linux/device.h | 25 +++++++++++++++++++++++++ 4 files changed, 61 insertions(+) (limited to 'drivers/base/bus.c') diff --git a/drivers/base/bus.c b/drivers/base/bus.c index 7d8a7ce73fb..ed3e8a2be64 100644 --- a/drivers/base/bus.c +++ b/drivers/base/bus.c @@ -724,6 +724,8 @@ int bus_register(struct bus_type * bus) { int retval; + BLOCKING_INIT_NOTIFIER_HEAD(&bus->bus_notifier); + retval = kobject_set_name(&bus->subsys.kset.kobj, "%s", bus->name); if (retval) goto out; @@ -782,6 +784,18 @@ void bus_unregister(struct bus_type * bus) subsystem_unregister(&bus->subsys); } +int bus_register_notifier(struct bus_type *bus, struct notifier_block *nb) +{ + return blocking_notifier_chain_register(&bus->bus_notifier, nb); +} +EXPORT_SYMBOL_GPL(bus_register_notifier); + +int bus_unregister_notifier(struct bus_type *bus, struct notifier_block *nb) +{ + return blocking_notifier_chain_unregister(&bus->bus_notifier, nb); +} +EXPORT_SYMBOL_GPL(bus_unregister_notifier); + int __init buses_init(void) { return subsystem_register(&bus_subsys); diff --git a/drivers/base/core.c b/drivers/base/core.c index 002fde46d38..d4f35d8902a 100644 --- a/drivers/base/core.c +++ b/drivers/base/core.c @@ -17,6 +17,7 @@ #include #include #include +#include #include @@ -428,6 +429,11 @@ int device_add(struct device *dev) if (platform_notify) platform_notify(dev); + /* notify clients of device entry (new way) */ + if (dev->bus) + blocking_notifier_call_chain(&dev->bus->bus_notifier, + BUS_NOTIFY_ADD_DEVICE, dev); + dev->uevent_attr.attr.name = "uevent"; dev->uevent_attr.attr.mode = S_IWUSR; if (dev->driver) @@ -504,6 +510,9 @@ int device_add(struct device *dev) BusError: device_pm_remove(dev); PMError: + 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); @@ -622,6 +631,9 @@ void device_del(struct device * dev) */ if (platform_notify_remove) platform_notify_remove(dev); + if (dev->bus) + blocking_notifier_call_chain(&dev->bus->bus_notifier, + BUS_NOTIFY_DEL_DEVICE, dev); bus_remove_device(dev); device_pm_remove(dev); kobject_uevent(&dev->kobj, KOBJ_REMOVE); diff --git a/drivers/base/dd.c b/drivers/base/dd.c index c5d6bb4290a..9c88b1e34bc 100644 --- a/drivers/base/dd.c +++ b/drivers/base/dd.c @@ -52,6 +52,11 @@ int device_bind_driver(struct device *dev) pr_debug("bound device '%s' to driver '%s'\n", dev->bus_id, dev->driver->name); + + if (dev->bus) + blocking_notifier_call_chain(&dev->bus->bus_notifier, + BUS_NOTIFY_BOUND_DRIVER, dev); + klist_add_tail(&dev->knode_driver, &dev->driver->klist_devices); ret = sysfs_create_link(&dev->driver->kobj, &dev->kobj, kobject_name(&dev->kobj)); @@ -288,6 +293,11 @@ static void __device_release_driver(struct device * dev) sysfs_remove_link(&dev->kobj, "driver"); klist_remove(&dev->knode_driver); + if (dev->bus) + blocking_notifier_call_chain(&dev->bus->bus_notifier, + BUS_NOTIFY_UNBIND_DRIVER, + dev); + if (dev->bus && dev->bus->remove) dev->bus->remove(dev); else if (drv->remove) diff --git a/include/linux/device.h b/include/linux/device.h index 9d4f6a96393..b00e0271139 100644 --- a/include/linux/device.h +++ b/include/linux/device.h @@ -42,6 +42,8 @@ struct bus_type { struct klist klist_devices; struct klist klist_drivers; + struct blocking_notifier_head bus_notifier; + struct bus_attribute * bus_attrs; struct device_attribute * dev_attrs; struct driver_attribute * drv_attrs; @@ -75,6 +77,29 @@ int __must_check bus_for_each_drv(struct bus_type *bus, struct device_driver *start, void *data, int (*fn)(struct device_driver *, void *)); +/* + * Bus notifiers: Get notified of addition/removal of devices + * and binding/unbinding of drivers to devices. + * In the long run, it should be a replacement for the platform + * notify hooks. + */ +struct notifier_block; + +extern int bus_register_notifier(struct bus_type *bus, + struct notifier_block *nb); +extern int bus_unregister_notifier(struct bus_type *bus, + struct notifier_block *nb); + +/* All 4 notifers below get called with the target struct device * + * as an argument. Note that those functions are likely to be called + * with the device semaphore held in the core, so be careful. + */ +#define BUS_NOTIFY_ADD_DEVICE 0x00000001 /* device added */ +#define BUS_NOTIFY_DEL_DEVICE 0x00000002 /* device removed */ +#define BUS_NOTIFY_BOUND_DRIVER 0x00000003 /* driver bound to device */ +#define BUS_NOTIFY_UNBIND_DRIVER 0x00000004 /* driver about to be + unbound */ + /* driverfs interface for exporting bus attributes */ struct bus_attribute { -- cgit v1.2.3-70-g09d2 From b9cafc7d5b8af0c71896f60dfcff40c71bd38a9a Mon Sep 17 00:00:00 2001 From: Kay Sievers Date: Thu, 14 Sep 2006 11:23:28 +0200 Subject: CONFIG_SYSFS_DEPRECATED - bus symlinks Turn off the bus symlinks if CONFIG_SYSFS_DEPRECATED is enabled Signed-off-by: Kay Sievers Signed-off-by: Greg Kroah-Hartman --- drivers/base/bus.c | 20 +++++++++++++++++--- 1 file changed, 17 insertions(+), 3 deletions(-) (limited to 'drivers/base/bus.c') diff --git a/drivers/base/bus.c b/drivers/base/bus.c index ed3e8a2be64..472810f8e6e 100644 --- a/drivers/base/bus.c +++ b/drivers/base/bus.c @@ -355,6 +355,21 @@ static void device_remove_attrs(struct bus_type * bus, struct device * dev) } } +#ifdef CONFIG_SYSFS_DEPRECATED +static int make_deprecated_bus_links(struct device *dev) +{ + return sysfs_create_link(&dev->kobj, + &dev->bus->subsys.kset.kobj, "bus"); +} + +static void remove_deprecated_bus_links(struct device *dev) +{ + sysfs_remove_link(&dev->kobj, "bus"); +} +#else +static inline int make_deprecated_bus_links(struct device *dev) { return 0; } +static inline void remove_deprecated_bus_links(struct device *dev) { } +#endif /** * bus_add_device - add device to bus @@ -381,8 +396,7 @@ int bus_add_device(struct device * dev) &dev->bus->subsys.kset.kobj, "subsystem"); if (error) goto out_subsys; - error = sysfs_create_link(&dev->kobj, - &dev->bus->subsys.kset.kobj, "bus"); + error = make_deprecated_bus_links(dev); if (error) goto out_deprecated; } @@ -436,7 +450,7 @@ void bus_remove_device(struct device * dev) { if (dev->bus) { sysfs_remove_link(&dev->kobj, "subsystem"); - sysfs_remove_link(&dev->kobj, "bus"); + remove_deprecated_bus_links(dev); sysfs_remove_link(&dev->bus->devices.kobj, dev->bus_id); device_remove_attrs(dev->bus, dev); if (dev->is_registered) { -- cgit v1.2.3-70-g09d2