From d1c3414c2a9d10ef7f0f7665f5d2947cd088c093 Mon Sep 17 00:00:00 2001 From: Grant Likely Date: Mon, 5 Mar 2012 08:47:41 -0700 Subject: drivercore: Add driver probe deferral mechanism Allow drivers to report at probe time that they cannot get all the resources required by the device, and should be retried at a later time. This should completely solve the problem of getting devices initialized in the right order. Right now this is mostly handled by mucking about with initcall ordering which is a complete hack, and doesn't even remotely handle the case where device drivers are in modules. This approach completely sidesteps the issues by allowing driver registration to occur in any order, and any driver can request to be retried after a few more other drivers get probed. v4: - Integrate Manjunath's addition of a separate workqueue - Change -EAGAIN to -EPROBE_DEFER for drivers to trigger deferral - Update comment blocks to reflect how the code really works v3: - Hold off workqueue scheduling until late_initcall so that the bulk of driver probes are complete before we start retrying deferred devices. - Tested with simple use cases. Still needs more testing though. Using it to get rid of the gpio early_initcall madness, or to replace the ASoC internal probe deferral code would be ideal. v2: - added locking so it should no longer be utterly broken in that regard - remove device from deferred list at device_del time. - Still completely untested with any real use case, but has been boot tested. Signed-off-by: Grant Likely Cc: Mark Brown Cc: Arnd Bergmann Cc: Dilan Lee Cc: Manjunath GKondaiah Cc: Alan Stern Cc: Tony Lindgren Cc: Alan Cox Reviewed-by: Mark Brown Acked-by: David Daney Reviewed-by: Arnd Bergmann Signed-off-by: Greg Kroah-Hartman --- drivers/base/core.c | 2 ++ 1 file changed, 2 insertions(+) (limited to 'drivers/base/core.c') diff --git a/drivers/base/core.c b/drivers/base/core.c index 74dda4f697f9..d4ff7adce38c 100644 --- a/drivers/base/core.c +++ b/drivers/base/core.c @@ -644,6 +644,7 @@ void device_initialize(struct device *dev) { dev->kobj.kset = devices_kset; kobject_init(&dev->kobj, &device_ktype); + INIT_LIST_HEAD(&dev->deferred_probe); INIT_LIST_HEAD(&dev->dma_pools); mutex_init(&dev->mutex); lockdep_set_novalidate_class(&dev->mutex); @@ -1188,6 +1189,7 @@ void device_del(struct device *dev) device_remove_file(dev, &uevent_attr); device_remove_attrs(dev); bus_remove_device(dev); + driver_deferred_probe_del(dev); /* * Some platform devices are driven without driver attached -- cgit v1.3-14-g43fede From ef8a3fd6e5e12e8989dae97ba5491c2e39369af9 Mon Sep 17 00:00:00 2001 From: Greg Kroah-Hartman Date: Thu, 8 Mar 2012 12:17:22 -0800 Subject: driver core: move the deferred probe pointer into the private area Nothing outside of the driver core needs to get to the deferred probe pointer, so move it inside the private area of 'struct device' so no one tries to mess around with it. Cc: Grant Likely Signed-off-by: Greg Kroah-Hartman --- drivers/base/base.h | 5 +++++ drivers/base/core.c | 2 +- drivers/base/dd.c | 18 ++++++++++-------- include/linux/device.h | 5 ----- 4 files changed, 16 insertions(+), 14 deletions(-) (limited to 'drivers/base/core.c') diff --git a/drivers/base/base.h b/drivers/base/base.h index 2c13deae5f82..6ee17bb391a9 100644 --- a/drivers/base/base.h +++ b/drivers/base/base.h @@ -59,6 +59,10 @@ struct driver_private { * @knode_parent - node in sibling list * @knode_driver - node in driver list * @knode_bus - node in bus list + * @deferred_probe - entry in deferred_probe_list which is used to retry the + * binding of drivers which were unable to get all the resources needed by + * the device; typically because it depends on another driver getting + * probed first. * @driver_data - private pointer for driver specific info. Will turn into a * list soon. * @device - pointer back to the struct class that this structure is @@ -71,6 +75,7 @@ struct device_private { struct klist_node knode_parent; struct klist_node knode_driver; struct klist_node knode_bus; + struct list_head deferred_probe; void *driver_data; struct device *device; }; diff --git a/drivers/base/core.c b/drivers/base/core.c index d4ff7adce38c..7050a75dde38 100644 --- a/drivers/base/core.c +++ b/drivers/base/core.c @@ -644,7 +644,6 @@ void device_initialize(struct device *dev) { dev->kobj.kset = devices_kset; kobject_init(&dev->kobj, &device_ktype); - INIT_LIST_HEAD(&dev->deferred_probe); INIT_LIST_HEAD(&dev->dma_pools); mutex_init(&dev->mutex); lockdep_set_novalidate_class(&dev->mutex); @@ -922,6 +921,7 @@ int device_private_init(struct device *dev) dev->p->device = dev; klist_init(&dev->p->klist_children, klist_children_get, klist_children_put); + INIT_LIST_HEAD(&dev->p->deferred_probe); return 0; } diff --git a/drivers/base/dd.c b/drivers/base/dd.c index 442b7641a086..9fa888e08059 100644 --- a/drivers/base/dd.c +++ b/drivers/base/dd.c @@ -45,7 +45,7 @@ * retry them. * * The deferred_probe_mutex must be held any time the deferred_probe_*_list - * of the (struct device*)->deferred_probe pointers are manipulated + * of the (struct device*)->p->deferred_probe pointers are manipulated */ static DEFINE_MUTEX(deferred_probe_mutex); static LIST_HEAD(deferred_probe_pending_list); @@ -58,6 +58,7 @@ static struct workqueue_struct *deferred_wq; static void deferred_probe_work_func(struct work_struct *work) { struct device *dev; + struct device_private *private; /* * This block processes every device in the deferred 'active' list. * Each device is removed from the active list and passed to @@ -72,9 +73,10 @@ static void deferred_probe_work_func(struct work_struct *work) */ mutex_lock(&deferred_probe_mutex); while (!list_empty(&deferred_probe_active_list)) { - dev = list_first_entry(&deferred_probe_active_list, - typeof(*dev), deferred_probe); - list_del_init(&dev->deferred_probe); + private = list_first_entry(&deferred_probe_active_list, + typeof(*dev->p), deferred_probe); + dev = private->device; + list_del_init(&private->deferred_probe); get_device(dev); @@ -94,9 +96,9 @@ static DECLARE_WORK(deferred_probe_work, deferred_probe_work_func); static void driver_deferred_probe_add(struct device *dev) { mutex_lock(&deferred_probe_mutex); - if (list_empty(&dev->deferred_probe)) { + if (list_empty(&dev->p->deferred_probe)) { dev_dbg(dev, "Added to deferred list\n"); - list_add(&dev->deferred_probe, &deferred_probe_pending_list); + list_add(&dev->p->deferred_probe, &deferred_probe_pending_list); } mutex_unlock(&deferred_probe_mutex); } @@ -104,9 +106,9 @@ static void driver_deferred_probe_add(struct device *dev) void driver_deferred_probe_del(struct device *dev) { mutex_lock(&deferred_probe_mutex); - if (!list_empty(&dev->deferred_probe)) { + if (!list_empty(&dev->p->deferred_probe)) { dev_dbg(dev, "Removed from deferred list\n"); - list_del_init(&dev->deferred_probe); + list_del_init(&dev->p->deferred_probe); } mutex_unlock(&deferred_probe_mutex); } diff --git a/include/linux/device.h b/include/linux/device.h index 22d6938ddbb4..f62e21689fdd 100644 --- a/include/linux/device.h +++ b/include/linux/device.h @@ -585,10 +585,6 @@ struct device_dma_parameters { * @mutex: Mutex to synchronize calls to its driver. * @bus: Type of bus device is on. * @driver: Which driver has allocated this - * @deferred_probe: entry in deferred_probe_list which is used to retry the - * binding of drivers which were unable to get all the resources - * needed by the device; typically because it depends on another - * driver getting probed first. * @platform_data: Platform data specific to the device. * Example: For devices on custom boards, as typical of embedded * and SOC based hardware, Linux often uses platform_data to point @@ -648,7 +644,6 @@ struct device { struct bus_type *bus; /* type of bus device is on */ struct device_driver *driver; /* which driver has allocated this device */ - struct list_head deferred_probe; void *platform_data; /* Platform specific data, device core doesn't touch it */ struct dev_pm_info power; -- cgit v1.3-14-g43fede