From 21c27f06587d2c18150d27ca2382a509ec55c482 Mon Sep 17 00:00:00 2001 From: Saravana Kannan Date: Mon, 18 May 2020 23:30:00 -0700 Subject: driver core: Fix SYNC_STATE_ONLY device link implementation When SYNC_STATE_ONLY support was added in commit 05ef983e0d65 ("driver core: Add device link support for SYNC_STATE_ONLY flag"), device_link_add() incorrectly skipped adding the new SYNC_STATE_ONLY device link to the supplier's and consumer's "device link" list. This causes multiple issues: - The device link is lost forever from driver core if the caller didn't keep track of it (caller typically isn't expected to). This is a memory leak. - The device link is also never visible to any other code path after device_link_add() returns. If we fix the "device link" list handling, that exposes a bunch of issues. 1. The device link "status" state management code rightfully doesn't handle the case where a DL_FLAG_MANAGED device link exists between a supplier and consumer, but the consumer manages to probe successfully before the supplier. The addition of DL_FLAG_SYNC_STATE_ONLY links break this assumption. This causes device_links_driver_bound() to throw a warning when this happens. Since DL_FLAG_SYNC_STATE_ONLY device links are mainly used for creating proxy device links for child device dependencies and aren't useful once the consumer device probes successfully, this patch just deletes DL_FLAG_SYNC_STATE_ONLY device links once its consumer device probes. This way, we avoid the warning, free up some memory and avoid complicating the device links "status" state management code. 2. Creating a DL_FLAG_STATELESS device link between two devices that already have a DL_FLAG_SYNC_STATE_ONLY device link will result in the DL_FLAG_STATELESS flag not getting set correctly. This patch also fixes this. Lastly, this patch also fixes minor whitespace issues. Cc: stable@vger.kernel.org Fixes: 05ef983e0d65 ("driver core: Add device link support for SYNC_STATE_ONLY flag") Signed-off-by: Saravana Kannan Reviewed-by: Rafael J. Wysocki Link: https://lore.kernel.org/r/20200519063000.128819-1-saravanak@google.com Signed-off-by: Greg Kroah-Hartman --- drivers/base/core.c | 61 ++++++++++++++++++++++++++++++++++------------------- 1 file changed, 39 insertions(+), 22 deletions(-) diff --git a/drivers/base/core.c b/drivers/base/core.c index 073045cb214e..4e0e430315d9 100644 --- a/drivers/base/core.c +++ b/drivers/base/core.c @@ -360,13 +360,12 @@ struct device_link *device_link_add(struct device *consumer, if (flags & DL_FLAG_STATELESS) { kref_get(&link->kref); + link->flags |= DL_FLAG_STATELESS; if (link->flags & DL_FLAG_SYNC_STATE_ONLY && - !(link->flags & DL_FLAG_STATELESS)) { - link->flags |= DL_FLAG_STATELESS; + !(link->flags & DL_FLAG_STATELESS)) goto reorder; - } else { + else goto out; - } } /* @@ -433,12 +432,16 @@ struct device_link *device_link_add(struct device *consumer, flags & DL_FLAG_PM_RUNTIME) pm_runtime_resume(supplier); + list_add_tail_rcu(&link->s_node, &supplier->links.consumers); + list_add_tail_rcu(&link->c_node, &consumer->links.suppliers); + if (flags & DL_FLAG_SYNC_STATE_ONLY) { dev_dbg(consumer, "Linked as a sync state only consumer to %s\n", dev_name(supplier)); goto out; } + reorder: /* * Move the consumer and all of the devices depending on it to the end @@ -449,12 +452,9 @@ reorder: */ device_reorder_to_tail(consumer, NULL); - list_add_tail_rcu(&link->s_node, &supplier->links.consumers); - list_add_tail_rcu(&link->c_node, &consumer->links.suppliers); - dev_dbg(consumer, "Linked as a consumer to %s\n", dev_name(supplier)); - out: +out: device_pm_unlock(); device_links_write_unlock(); @@ -829,6 +829,13 @@ static void __device_links_supplier_defer_sync(struct device *sup) list_add_tail(&sup->links.defer_sync, &deferred_sync); } +static void device_link_drop_managed(struct device_link *link) +{ + link->flags &= ~DL_FLAG_MANAGED; + WRITE_ONCE(link->status, DL_STATE_NONE); + kref_put(&link->kref, __device_link_del); +} + /** * device_links_driver_bound - Update device links after probing its driver. * @dev: Device to update the links for. @@ -842,7 +849,7 @@ static void __device_links_supplier_defer_sync(struct device *sup) */ void device_links_driver_bound(struct device *dev) { - struct device_link *link; + struct device_link *link, *ln; LIST_HEAD(sync_list); /* @@ -882,18 +889,35 @@ void device_links_driver_bound(struct device *dev) else __device_links_queue_sync_state(dev, &sync_list); - list_for_each_entry(link, &dev->links.suppliers, c_node) { + list_for_each_entry_safe(link, ln, &dev->links.suppliers, c_node) { + struct device *supplier; + if (!(link->flags & DL_FLAG_MANAGED)) continue; - WARN_ON(link->status != DL_STATE_CONSUMER_PROBE); - WRITE_ONCE(link->status, DL_STATE_ACTIVE); + supplier = link->supplier; + if (link->flags & DL_FLAG_SYNC_STATE_ONLY) { + /* + * When DL_FLAG_SYNC_STATE_ONLY is set, it means no + * other DL_MANAGED_LINK_FLAGS have been set. So, it's + * save to drop the managed link completely. + */ + device_link_drop_managed(link); + } else { + WARN_ON(link->status != DL_STATE_CONSUMER_PROBE); + WRITE_ONCE(link->status, DL_STATE_ACTIVE); + } + /* + * This needs to be done even for the deleted + * DL_FLAG_SYNC_STATE_ONLY device link in case it was the last + * device link that was preventing the supplier from getting a + * sync_state() call. + */ if (defer_sync_state_count) - __device_links_supplier_defer_sync(link->supplier); + __device_links_supplier_defer_sync(supplier); else - __device_links_queue_sync_state(link->supplier, - &sync_list); + __device_links_queue_sync_state(supplier, &sync_list); } dev->links.status = DL_DEV_DRIVER_BOUND; @@ -903,13 +927,6 @@ void device_links_driver_bound(struct device *dev) device_links_flush_sync_list(&sync_list, dev); } -static void device_link_drop_managed(struct device_link *link) -{ - link->flags &= ~DL_FLAG_MANAGED; - WRITE_ONCE(link->status, DL_STATE_NONE); - kref_put(&link->kref, __device_link_del); -} - /** * __device_links_no_driver - Update links of a device without a driver. * @dev: Device without a drvier. -- cgit v1.2.3 From 44e960490ddf868fc9135151c4a658936e771dc2 Mon Sep 17 00:00:00 2001 From: Saravana Kannan Date: Tue, 19 May 2020 21:36:26 -0700 Subject: driver core: Fix handling of SYNC_STATE_ONLY + STATELESS device links Commit 21c27f06587d ("driver core: Fix SYNC_STATE_ONLY device link implementation") didn't completely fix STATELESS + SYNC_STATE_ONLY handling. What looks like an optimization in that commit is actually a bug that causes an if condition to always take the else path. This prevents reordering of devices in the dpm_list when a DL_FLAG_STATELESS device link is create on top of an existing DL_FLAG_SYNC_STATE_ONLY device link. Fixes: 21c27f06587d ("driver core: Fix SYNC_STATE_ONLY device link implementation") Signed-off-by: Saravana Kannan Cc: stable Reviewed-by: Rafael J. Wysocki Link: https://lore.kernel.org/r/20200520043626.181820-1-saravanak@google.com Signed-off-by: Greg Kroah-Hartman --- drivers/base/core.c | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/drivers/base/core.c b/drivers/base/core.c index 4e0e430315d9..0cad34f1eede 100644 --- a/drivers/base/core.c +++ b/drivers/base/core.c @@ -360,12 +360,14 @@ struct device_link *device_link_add(struct device *consumer, if (flags & DL_FLAG_STATELESS) { kref_get(&link->kref); - link->flags |= DL_FLAG_STATELESS; if (link->flags & DL_FLAG_SYNC_STATE_ONLY && - !(link->flags & DL_FLAG_STATELESS)) + !(link->flags & DL_FLAG_STATELESS)) { + link->flags |= DL_FLAG_STATELESS; goto reorder; - else + } else { + link->flags |= DL_FLAG_STATELESS; goto out; + } } /* -- cgit v1.2.3 From 4ef12f7198023c09ad6d25b652bd8748c965c7fa Mon Sep 17 00:00:00 2001 From: Heikki Krogerus Date: Wed, 13 May 2020 18:18:40 +0300 Subject: kobject: Make sure the parent does not get released before its children In the function kobject_cleanup(), kobject_del(kobj) is called before the kobj->release(). That makes it possible to release the parent of the kobject before the kobject itself. To fix that, adding function __kboject_del() that does everything that kobject_del() does except release the parent reference. kobject_cleanup() then calls __kobject_del() instead of kobject_del(), and separately decrements the reference count of the parent kobject after kobj->release() has been called. Reported-by: Naresh Kamboju Reported-by: kernel test robot Fixes: 7589238a8cf3 ("Revert "software node: Simplify software_node_release() function"") Suggested-by: "Rafael J. Wysocki" Signed-off-by: Heikki Krogerus Reviewed-by: Rafael J. Wysocki Reviewed-by: Brendan Higgins Tested-by: Brendan Higgins Acked-by: Randy Dunlap Link: https://lore.kernel.org/r/20200513151840.36400-1-heikki.krogerus@linux.intel.com Cc: stable Signed-off-by: Greg Kroah-Hartman --- lib/kobject.c | 30 ++++++++++++++++++++---------- 1 file changed, 20 insertions(+), 10 deletions(-) diff --git a/lib/kobject.c b/lib/kobject.c index 83198cb37d8d..2bd631460e18 100644 --- a/lib/kobject.c +++ b/lib/kobject.c @@ -599,14 +599,7 @@ out: } EXPORT_SYMBOL_GPL(kobject_move); -/** - * kobject_del() - Unlink kobject from hierarchy. - * @kobj: object. - * - * This is the function that should be called to delete an object - * successfully added via kobject_add(). - */ -void kobject_del(struct kobject *kobj) +static void __kobject_del(struct kobject *kobj) { struct kernfs_node *sd; const struct kobj_type *ktype; @@ -625,9 +618,23 @@ void kobject_del(struct kobject *kobj) kobj->state_in_sysfs = 0; kobj_kset_leave(kobj); - kobject_put(kobj->parent); kobj->parent = NULL; } + +/** + * kobject_del() - Unlink kobject from hierarchy. + * @kobj: object. + * + * This is the function that should be called to delete an object + * successfully added via kobject_add(). + */ +void kobject_del(struct kobject *kobj) +{ + struct kobject *parent = kobj->parent; + + __kobject_del(kobj); + kobject_put(parent); +} EXPORT_SYMBOL(kobject_del); /** @@ -663,6 +670,7 @@ EXPORT_SYMBOL(kobject_get_unless_zero); */ static void kobject_cleanup(struct kobject *kobj) { + struct kobject *parent = kobj->parent; struct kobj_type *t = get_ktype(kobj); const char *name = kobj->name; @@ -684,7 +692,7 @@ static void kobject_cleanup(struct kobject *kobj) if (kobj->state_in_sysfs) { pr_debug("kobject: '%s' (%p): auto cleanup kobject_del\n", kobject_name(kobj), kobj); - kobject_del(kobj); + __kobject_del(kobj); } if (t && t->release) { @@ -698,6 +706,8 @@ static void kobject_cleanup(struct kobject *kobj) pr_debug("kobject: '%s': free name\n", name); kfree_const(name); } + + kobject_put(parent); } #ifdef CONFIG_DEBUG_KOBJECT_RELEASE -- cgit v1.2.3 From e6764aa0e5530066dd969eccea2a1a7d177859a8 Mon Sep 17 00:00:00 2001 From: Greg Kroah-Hartman Date: Sat, 23 May 2020 17:11:11 +0200 Subject: Revert "kobject: Make sure the parent does not get released before its children" This reverts commit 4ef12f7198023c09ad6d25b652bd8748c965c7fa. Guenter reports: All my arm64be (arm64 big endian) boot tests crash with this patch applied. Reverting it fixes the problem. Crash log and bisect results (from pending-fixes branch) below. And also: arm64 images don't crash but report lots of "poison overwritten" backtraces like the one below. On arm, I see "refcount_t: underflow", also attached. I didn't bisect those, but given the context I would suspect the same culprit. Reported-by: Guenter Roeck Link: https://lore.kernel.org/r/20200513151840.36400-1-heikki.krogerus@linux.intel.com Cc: Naresh Kamboju Cc: kernel test robot Cc: "Rafael J. Wysocki" Cc: Heikki Krogerus Cc: Brendan Higgins Cc: Randy Dunlap Cc: stable Signed-off-by: Greg Kroah-Hartman --- lib/kobject.c | 30 ++++++++++-------------------- 1 file changed, 10 insertions(+), 20 deletions(-) diff --git a/lib/kobject.c b/lib/kobject.c index 2bd631460e18..83198cb37d8d 100644 --- a/lib/kobject.c +++ b/lib/kobject.c @@ -599,7 +599,14 @@ out: } EXPORT_SYMBOL_GPL(kobject_move); -static void __kobject_del(struct kobject *kobj) +/** + * kobject_del() - Unlink kobject from hierarchy. + * @kobj: object. + * + * This is the function that should be called to delete an object + * successfully added via kobject_add(). + */ +void kobject_del(struct kobject *kobj) { struct kernfs_node *sd; const struct kobj_type *ktype; @@ -618,23 +625,9 @@ static void __kobject_del(struct kobject *kobj) kobj->state_in_sysfs = 0; kobj_kset_leave(kobj); + kobject_put(kobj->parent); kobj->parent = NULL; } - -/** - * kobject_del() - Unlink kobject from hierarchy. - * @kobj: object. - * - * This is the function that should be called to delete an object - * successfully added via kobject_add(). - */ -void kobject_del(struct kobject *kobj) -{ - struct kobject *parent = kobj->parent; - - __kobject_del(kobj); - kobject_put(parent); -} EXPORT_SYMBOL(kobject_del); /** @@ -670,7 +663,6 @@ EXPORT_SYMBOL(kobject_get_unless_zero); */ static void kobject_cleanup(struct kobject *kobj) { - struct kobject *parent = kobj->parent; struct kobj_type *t = get_ktype(kobj); const char *name = kobj->name; @@ -692,7 +684,7 @@ static void kobject_cleanup(struct kobject *kobj) if (kobj->state_in_sysfs) { pr_debug("kobject: '%s' (%p): auto cleanup kobject_del\n", kobject_name(kobj), kobj); - __kobject_del(kobj); + kobject_del(kobj); } if (t && t->release) { @@ -706,8 +698,6 @@ static void kobject_cleanup(struct kobject *kobj) pr_debug("kobject: '%s': free name\n", name); kfree_const(name); } - - kobject_put(parent); } #ifdef CONFIG_DEBUG_KOBJECT_RELEASE -- cgit v1.2.3