| From 21eb93f432b1a785df193df1a56a59e9eb3a985f Mon Sep 17 00:00:00 2001 |
| From: Saravana Kannan <saravanak@google.com> |
| Date: Fri, 21 Feb 2020 00:05:08 -0800 |
| Subject: driver core: Call sync_state() even if supplier has no consumers |
| |
| From: Saravana Kannan <saravanak@google.com> |
| |
| commit 21eb93f432b1a785df193df1a56a59e9eb3a985f upstream. |
| |
| The initial patch that added sync_state() support didn't handle the case |
| where a supplier has no consumers. This was because when a device is |
| successfully bound with a driver, only its suppliers were checked to see |
| if they are eligible to get a sync_state(). This is not sufficient for |
| devices that have no consumers but still need to do device state clean |
| up. So fix this. |
| |
| Fixes: fc5a251d0fd7ca90 (driver core: Add sync_state driver/bus callback) |
| Signed-off-by: Saravana Kannan <saravanak@google.com> |
| Cc: stable <stable@vger.kernel.org> |
| Link: https://lore.kernel.org/r/20200221080510.197337-2-saravanak@google.com |
| Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org> |
| |
| --- |
| drivers/base/core.c | 23 +++++++++++++++++------ |
| 1 file changed, 17 insertions(+), 6 deletions(-) |
| |
| --- a/drivers/base/core.c |
| +++ b/drivers/base/core.c |
| @@ -745,25 +745,31 @@ static void __device_links_queue_sync_st |
| /** |
| * device_links_flush_sync_list - Call sync_state() on a list of devices |
| * @list: List of devices to call sync_state() on |
| + * @dont_lock_dev: Device for which lock is already held by the caller |
| * |
| * Calls sync_state() on all the devices that have been queued for it. This |
| - * function is used in conjunction with __device_links_queue_sync_state(). |
| + * function is used in conjunction with __device_links_queue_sync_state(). The |
| + * @dont_lock_dev parameter is useful when this function is called from a |
| + * context where a device lock is already held. |
| */ |
| -static void device_links_flush_sync_list(struct list_head *list) |
| +static void device_links_flush_sync_list(struct list_head *list, |
| + struct device *dont_lock_dev) |
| { |
| struct device *dev, *tmp; |
| |
| list_for_each_entry_safe(dev, tmp, list, links.defer_sync) { |
| list_del_init(&dev->links.defer_sync); |
| |
| - device_lock(dev); |
| + if (dev != dont_lock_dev) |
| + device_lock(dev); |
| |
| if (dev->bus->sync_state) |
| dev->bus->sync_state(dev); |
| else if (dev->driver && dev->driver->sync_state) |
| dev->driver->sync_state(dev); |
| |
| - device_unlock(dev); |
| + if (dev != dont_lock_dev) |
| + device_unlock(dev); |
| |
| put_device(dev); |
| } |
| @@ -801,7 +807,7 @@ void device_links_supplier_sync_state_re |
| out: |
| device_links_write_unlock(); |
| |
| - device_links_flush_sync_list(&sync_list); |
| + device_links_flush_sync_list(&sync_list, NULL); |
| } |
| |
| static int sync_state_resume_initcall(void) |
| @@ -865,6 +871,11 @@ void device_links_driver_bound(struct de |
| driver_deferred_probe_add(link->consumer); |
| } |
| |
| + if (defer_sync_state_count) |
| + __device_links_supplier_defer_sync(dev); |
| + else |
| + __device_links_queue_sync_state(dev, &sync_list); |
| + |
| list_for_each_entry(link, &dev->links.suppliers, c_node) { |
| if (!(link->flags & DL_FLAG_MANAGED)) |
| continue; |
| @@ -883,7 +894,7 @@ void device_links_driver_bound(struct de |
| |
| device_links_write_unlock(); |
| |
| - device_links_flush_sync_list(&sync_list); |
| + device_links_flush_sync_list(&sync_list, dev); |
| } |
| |
| static void device_link_drop_managed(struct device_link *link) |