aboutsummaryrefslogtreecommitdiffstats
path: root/drivers
diff options
context:
space:
mode:
authorBenjamin Herrenschmidt <benh@kernel.crashing.org>2006-10-24 23:44:59 -0400
committerGreg Kroah-Hartman <gregkh@suse.de>2006-12-01 17:51:58 -0500
commit116af378201ef793424cd10508ccf18b06d8a021 (patch)
tree2de792e7b9d8a122d88241d1ecfbe7bc92b9b8fe /drivers
parent0215ffb08ce99e2bb59eca114a99499a4d06e704 (diff)
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 <benh@kernel.crashing.org> Signed-off-by: Greg Kroah-Hartman <gregkh@suse.de>
Diffstat (limited to 'drivers')
-rw-r--r--drivers/base/bus.c14
-rw-r--r--drivers/base/core.c12
-rw-r--r--drivers/base/dd.c10
3 files changed, 36 insertions, 0 deletions
diff --git a/drivers/base/bus.c b/drivers/base/bus.c
index 7d8a7ce73fb3..ed3e8a2be64a 100644
--- a/drivers/base/bus.c
+++ b/drivers/base/bus.c
@@ -724,6 +724,8 @@ int bus_register(struct bus_type * bus)
724{ 724{
725 int retval; 725 int retval;
726 726
727 BLOCKING_INIT_NOTIFIER_HEAD(&bus->bus_notifier);
728
727 retval = kobject_set_name(&bus->subsys.kset.kobj, "%s", bus->name); 729 retval = kobject_set_name(&bus->subsys.kset.kobj, "%s", bus->name);
728 if (retval) 730 if (retval)
729 goto out; 731 goto out;
@@ -782,6 +784,18 @@ void bus_unregister(struct bus_type * bus)
782 subsystem_unregister(&bus->subsys); 784 subsystem_unregister(&bus->subsys);
783} 785}
784 786
787int bus_register_notifier(struct bus_type *bus, struct notifier_block *nb)
788{
789 return blocking_notifier_chain_register(&bus->bus_notifier, nb);
790}
791EXPORT_SYMBOL_GPL(bus_register_notifier);
792
793int bus_unregister_notifier(struct bus_type *bus, struct notifier_block *nb)
794{
795 return blocking_notifier_chain_unregister(&bus->bus_notifier, nb);
796}
797EXPORT_SYMBOL_GPL(bus_unregister_notifier);
798
785int __init buses_init(void) 799int __init buses_init(void)
786{ 800{
787 return subsystem_register(&bus_subsys); 801 return subsystem_register(&bus_subsys);
diff --git a/drivers/base/core.c b/drivers/base/core.c
index 002fde46d38d..d4f35d8902a2 100644
--- a/drivers/base/core.c
+++ b/drivers/base/core.c
@@ -17,6 +17,7 @@
17#include <linux/slab.h> 17#include <linux/slab.h>
18#include <linux/string.h> 18#include <linux/string.h>
19#include <linux/kdev_t.h> 19#include <linux/kdev_t.h>
20#include <linux/notifier.h>
20 21
21#include <asm/semaphore.h> 22#include <asm/semaphore.h>
22 23
@@ -428,6 +429,11 @@ int device_add(struct device *dev)
428 if (platform_notify) 429 if (platform_notify)
429 platform_notify(dev); 430 platform_notify(dev);
430 431
432 /* notify clients of device entry (new way) */
433 if (dev->bus)
434 blocking_notifier_call_chain(&dev->bus->bus_notifier,
435 BUS_NOTIFY_ADD_DEVICE, dev);
436
431 dev->uevent_attr.attr.name = "uevent"; 437 dev->uevent_attr.attr.name = "uevent";
432 dev->uevent_attr.attr.mode = S_IWUSR; 438 dev->uevent_attr.attr.mode = S_IWUSR;
433 if (dev->driver) 439 if (dev->driver)
@@ -504,6 +510,9 @@ int device_add(struct device *dev)
504 BusError: 510 BusError:
505 device_pm_remove(dev); 511 device_pm_remove(dev);
506 PMError: 512 PMError:
513 if (dev->bus)
514 blocking_notifier_call_chain(&dev->bus->bus_notifier,
515 BUS_NOTIFY_DEL_DEVICE, dev);
507 device_remove_groups(dev); 516 device_remove_groups(dev);
508 GroupError: 517 GroupError:
509 device_remove_attrs(dev); 518 device_remove_attrs(dev);
@@ -622,6 +631,9 @@ void device_del(struct device * dev)
622 */ 631 */
623 if (platform_notify_remove) 632 if (platform_notify_remove)
624 platform_notify_remove(dev); 633 platform_notify_remove(dev);
634 if (dev->bus)
635 blocking_notifier_call_chain(&dev->bus->bus_notifier,
636 BUS_NOTIFY_DEL_DEVICE, dev);
625 bus_remove_device(dev); 637 bus_remove_device(dev);
626 device_pm_remove(dev); 638 device_pm_remove(dev);
627 kobject_uevent(&dev->kobj, KOBJ_REMOVE); 639 kobject_uevent(&dev->kobj, KOBJ_REMOVE);
diff --git a/drivers/base/dd.c b/drivers/base/dd.c
index c5d6bb4290ad..9c88b1e34bc3 100644
--- a/drivers/base/dd.c
+++ b/drivers/base/dd.c
@@ -52,6 +52,11 @@ int device_bind_driver(struct device *dev)
52 52
53 pr_debug("bound device '%s' to driver '%s'\n", 53 pr_debug("bound device '%s' to driver '%s'\n",
54 dev->bus_id, dev->driver->name); 54 dev->bus_id, dev->driver->name);
55
56 if (dev->bus)
57 blocking_notifier_call_chain(&dev->bus->bus_notifier,
58 BUS_NOTIFY_BOUND_DRIVER, dev);
59
55 klist_add_tail(&dev->knode_driver, &dev->driver->klist_devices); 60 klist_add_tail(&dev->knode_driver, &dev->driver->klist_devices);
56 ret = sysfs_create_link(&dev->driver->kobj, &dev->kobj, 61 ret = sysfs_create_link(&dev->driver->kobj, &dev->kobj,
57 kobject_name(&dev->kobj)); 62 kobject_name(&dev->kobj));
@@ -288,6 +293,11 @@ static void __device_release_driver(struct device * dev)
288 sysfs_remove_link(&dev->kobj, "driver"); 293 sysfs_remove_link(&dev->kobj, "driver");
289 klist_remove(&dev->knode_driver); 294 klist_remove(&dev->knode_driver);
290 295
296 if (dev->bus)
297 blocking_notifier_call_chain(&dev->bus->bus_notifier,
298 BUS_NOTIFY_UNBIND_DRIVER,
299 dev);
300
291 if (dev->bus && dev->bus->remove) 301 if (dev->bus && dev->bus->remove)
292 dev->bus->remove(dev); 302 dev->bus->remove(dev);
293 else if (drv->remove) 303 else if (drv->remove)