X-Git-Url: http://git.rot13.org/?a=blobdiff_plain;f=drivers%2Fbase%2Fdd.c;h=6a48824e43ff6559ff386d4eb23c301e5293d753;hb=9ac7849e35f705830f7b016ff272b0ff1f7ff759;hp=db01b95a47a501d5976c04d66052a8809f026f29;hpb=7c2aed4b5632fb536721b8494d1a4a65852ffb49;p=powerpc.git diff --git a/drivers/base/dd.c b/drivers/base/dd.c index db01b95a47..6a48824e43 100644 --- a/drivers/base/dd.c +++ b/drivers/base/dd.c @@ -18,6 +18,7 @@ #include #include #include +#include #include "base.h" #include "power/power.h" @@ -25,33 +26,28 @@ #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.) - * - * This function must be called with @dev->sem held. - */ -int device_bind_driver(struct device *dev) +static void driver_bound(struct device *dev) { - int ret; - if (klist_node_attached(&dev->knode_driver)) { printk(KERN_WARNING "%s: device %s already bound\n", __FUNCTION__, kobject_name(&dev->kobj)); - return 0; + return; } 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); +} + +static int driver_sysfs_add(struct device *dev) +{ + int ret; + ret = sysfs_create_link(&dev->driver->kobj, &dev->kobj, kobject_name(&dev->kobj)); if (ret == 0) { @@ -64,12 +60,48 @@ int device_bind_driver(struct device *dev) return ret; } +static void driver_sysfs_remove(struct device *dev) +{ + struct device_driver *drv = dev->driver; + + if (drv) { + sysfs_remove_link(&drv->kobj, kobject_name(&dev->kobj)); + sysfs_remove_link(&dev->kobj, "driver"); + } +} + +/** + * 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.) + * + * This function must be called with @dev->sem held. + */ +int device_bind_driver(struct device *dev) +{ + int ret; + + ret = driver_sysfs_add(dev); + if (!ret) + driver_bound(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) { struct stupid_thread_structure *data = void_data; @@ -80,47 +112,51 @@ static int really_probe(void *void_data) atomic_inc(&probe_count); pr_debug("%s: Probing driver %s with device %s\n", drv->bus->name, drv->name, dev->bus_id); + WARN_ON(!list_empty(&dev->devres_head)); dev->driver = drv; + if (driver_sysfs_add(dev)) { + printk(KERN_ERR "%s: driver_sysfs_add(%s) failed\n", + __FUNCTION__, dev->bus_id); + goto probe_failed; + } + if (dev->bus->probe) { ret = dev->bus->probe(dev); - if (ret) { - dev->driver = NULL; + if (ret) goto probe_failed; - } } else if (drv->probe) { ret = drv->probe(dev); - if (ret) { - dev->driver = NULL; + if (ret) goto probe_failed; - } - } - if (device_bind_driver(dev)) { - printk(KERN_ERR "%s: device_bind_driver(%s) failed\n", - __FUNCTION__, dev->bus_id); - /* How does undo a ->probe? We're screwed. */ } + + driver_bound(dev); ret = 1; pr_debug("%s: Bound Device %s to Driver %s\n", drv->bus->name, dev->bus_id, drv->name); goto done; probe_failed: - if (ret == -ENODEV || ret == -ENXIO) { - /* Driver matched, but didn't support device - * or device not found. - * Not an error; keep going. - */ - ret = 0; - } else { + devres_release_all(dev); + driver_sysfs_remove(dev); + dev->driver = NULL; + + if (ret != -ENODEV && ret != -ENXIO) { /* driver matched but the probe failed */ printk(KERN_WARNING "%s: probe of %s failed with error %d\n", drv->name, dev->bus_id, ret); } + /* + * Ignore errors returned by ->probe so that the next driver can try + * its luck. + */ + ret = 0; done: kfree(data); atomic_dec(&probe_count); + wake_up(&probe_waitqueue); return ret; } @@ -280,14 +316,20 @@ static void __device_release_driver(struct device * dev) drv = dev->driver; if (drv) { get_driver(drv); - sysfs_remove_link(&drv->kobj, kobject_name(&dev->kobj)); + driver_sysfs_remove(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) drv->remove(dev); + devres_release_all(dev); dev->driver = NULL; put_driver(drv); } @@ -337,6 +379,32 @@ void driver_detach(struct device_driver * drv) } } +#ifdef CONFIG_PCI_MULTITHREAD_PROBE +static int __init wait_for_probes(void) +{ + DEFINE_WAIT(wait); + + printk(KERN_INFO "%s: waiting for %d threads\n", __FUNCTION__, + atomic_read(&probe_count)); + if (!atomic_read(&probe_count)) + return 0; + while (atomic_read(&probe_count)) { + prepare_to_wait(&probe_waitqueue, &wait, TASK_UNINTERRUPTIBLE); + if (atomic_read(&probe_count)) + schedule(); + } + finish_wait(&probe_waitqueue, &wait); + return 0; +} + +core_initcall_sync(wait_for_probes); +postcore_initcall_sync(wait_for_probes); +arch_initcall_sync(wait_for_probes); +subsys_initcall_sync(wait_for_probes); +fs_initcall_sync(wait_for_probes); +device_initcall_sync(wait_for_probes); +late_initcall_sync(wait_for_probes); +#endif EXPORT_SYMBOL_GPL(device_bind_driver); EXPORT_SYMBOL_GPL(device_release_driver);