From 7008e58c63bc8468e8d16154e25d780198b3ecfc Mon Sep 17 00:00:00 2001 From: Saravana Kannan Date: Wed, 4 Nov 2020 12:54:30 -0800 Subject: driver core: Fix lockdep warning on wfs_lock There's a potential deadlock with the following cycle: wfs_lock --> device_links_lock --> kn->count Fix this by simply dropping the lock around a list_empty() check that's just exported to a sysfs file. The sysfs file output is an instantaneous check anyway and the lock doesn't really add any protection. Lockdep log: [ 48.808132] [ 48.808132] the existing dependency chain (in reverse order) is: [ 48.809069] [ 48.809069] -> #2 (kn->count){++++}: [ 48.809707] __kernfs_remove.llvm.7860393000964815146+0x2d4/0x460 [ 48.810537] kernfs_remove_by_name_ns+0x54/0x9c [ 48.811171] sysfs_remove_file_ns+0x18/0x24 [ 48.811762] device_del+0x2b8/0x5a8 [ 48.812269] __device_link_del+0x98/0xb8 [ 48.812829] device_links_driver_bound+0x210/0x2d8 [ 48.813496] driver_bound+0x44/0xf8 [ 48.814000] really_probe+0x340/0x6e0 [ 48.814526] driver_probe_device+0xb8/0x100 [ 48.815117] device_driver_attach+0x78/0xb8 [ 48.815708] __driver_attach+0xe0/0x194 [ 48.816255] bus_for_each_dev+0xa8/0x11c [ 48.816816] driver_attach+0x24/0x30 [ 48.817331] bus_add_driver+0x100/0x1e0 [ 48.817880] driver_register+0x78/0x114 [ 48.818427] __platform_driver_register+0x44/0x50 [ 48.819089] 0xffffffdbb3227038 [ 48.819551] do_one_initcall+0xd8/0x1e0 [ 48.820099] do_init_module+0xd8/0x298 [ 48.820636] load_module+0x3afc/0x44c8 [ 48.821173] __arm64_sys_finit_module+0xbc/0xf0 [ 48.821807] el0_svc_common+0xbc/0x1d0 [ 48.822344] el0_svc_handler+0x74/0x98 [ 48.822882] el0_svc+0x8/0xc [ 48.823310] [ 48.823310] -> #1 (device_links_lock){+.+.}: [ 48.824036] __mutex_lock_common+0xe0/0xe44 [ 48.824626] mutex_lock_nested+0x28/0x34 [ 48.825185] device_link_add+0xd4/0x4ec [ 48.825734] of_link_to_suppliers+0x158/0x204 [ 48.826347] of_fwnode_add_links+0x50/0x64 [ 48.826928] device_link_add_missing_supplier_links+0x90/0x11c [ 48.827725] fw_devlink_resume+0x58/0x130 [ 48.828296] of_platform_default_populate_init+0xb4/0xd0 [ 48.829030] do_one_initcall+0xd8/0x1e0 [ 48.829578] do_initcall_level+0xb8/0xcc [ 48.830137] do_basic_setup+0x60/0x7c [ 48.830662] kernel_init_freeable+0x128/0x1ac [ 48.831275] kernel_init+0x18/0x29c [ 48.831781] ret_from_fork+0x10/0x18 [ 48.832297] [ 48.832297] -> #0 (wfs_lock){+.+.}: [ 48.832922] __lock_acquire+0xe04/0x2e20 [ 48.833480] lock_acquire+0xbc/0xec [ 48.833984] __mutex_lock_common+0xe0/0xe44 [ 48.834577] mutex_lock_nested+0x28/0x34 [ 48.835136] waiting_for_supplier_show+0x3c/0x98 [ 48.835781] dev_attr_show+0x48/0xb4 [ 48.836295] sysfs_kf_seq_show+0xe8/0x184 [ 48.836864] kernfs_seq_show+0x48/0x8c [ 48.837401] seq_read+0x1c8/0x600 [ 48.837884] kernfs_fop_read+0x68/0x204 [ 48.838431] __vfs_read+0x60/0x214 [ 48.838925] vfs_read+0xbc/0x15c [ 48.839397] ksys_read+0x78/0xe4 [ 48.839869] __arm64_sys_read+0x1c/0x28 [ 48.840416] el0_svc_common+0xbc/0x1d0 [ 48.840953] el0_svc_handler+0x74/0x98 [ 48.841490] el0_svc+0x8/0xc [ 48.841917] [ 48.841917] other info that might help us debug this: [ 48.841917] [ 48.842920] Chain exists of: [ 48.842920] wfs_lock --> device_links_lock --> kn->count [ 48.842920] [ 48.844152] Possible unsafe locking scenario: [ 48.844152] [ 48.844895] CPU0 CPU1 [ 48.845463] ---- ---- [ 48.846032] lock(kn->count); [ 48.846417] lock(device_links_lock); [ 48.847203] lock(kn->count); [ 48.847902] lock(wfs_lock); [ 48.848276] [ 48.848276] *** DEADLOCK *** Reported-by: Cheng-Jui.Wang@mediatek.com Signed-off-by: Saravana Kannan Link: https://lore.kernel.org/r/20201104205431.3795207-1-saravanak@google.com Signed-off-by: Greg Kroah-Hartman --- drivers/base/core.c | 2 -- 1 file changed, 2 deletions(-) (limited to 'drivers/base') diff --git a/drivers/base/core.c b/drivers/base/core.c index d661ada1518f..1165a80f8010 100644 --- a/drivers/base/core.c +++ b/drivers/base/core.c @@ -1062,10 +1062,8 @@ static ssize_t waiting_for_supplier_show(struct device *dev, bool val; device_lock(dev); - mutex_lock(&wfs_lock); val = !list_empty(&dev->links.needs_suppliers) && dev->links.need_for_probe; - mutex_unlock(&wfs_lock); device_unlock(dev); return sysfs_emit(buf, "%u\n", val); } -- cgit v1.2.3 From 33c0c9bdf7a59051a654cd98b7d2b48ce0080967 Mon Sep 17 00:00:00 2001 From: Mauro Carvalho Chehab Date: Fri, 23 Oct 2020 18:32:56 +0200 Subject: drivers: base: fix some kernel-doc markups class_create is actually defined at the header. Fix the markup there and add a new one at the right place. While here, also fix some markups for functions that have different names between their prototypes and kernel-doc comments. Signed-off-by: Mauro Carvalho Chehab Link: https://lore.kernel.org/r/2fb6efd6a1f90d69ff73bf579566079cbb051e15.1603469755.git.mchehab+huawei@kernel.org Signed-off-by: Greg Kroah-Hartman --- drivers/base/class.c | 2 +- drivers/base/devres.c | 2 +- drivers/base/firmware_loader/fallback.c | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) (limited to 'drivers/base') diff --git a/drivers/base/class.c b/drivers/base/class.c index c3451481194e..7476f393df97 100644 --- a/drivers/base/class.c +++ b/drivers/base/class.c @@ -210,7 +210,7 @@ static void class_create_release(struct class *cls) } /** - * class_create - create a struct class structure + * __class_create - create a struct class structure * @owner: pointer to the module that is to "own" this struct class * @name: pointer to a string for the name of this class. * @key: the lock_class_key for this class; used by mutex lock debugging diff --git a/drivers/base/devres.c b/drivers/base/devres.c index 586e9a75c840..fb9d5289a620 100644 --- a/drivers/base/devres.c +++ b/drivers/base/devres.c @@ -149,7 +149,7 @@ void * __devres_alloc_node(dr_release_t release, size_t size, gfp_t gfp, int nid EXPORT_SYMBOL_GPL(__devres_alloc_node); #else /** - * devres_alloc - Allocate device resource data + * devres_alloc_node - Allocate device resource data * @release: Release function devres will be associated with * @size: Allocation size * @gfp: Allocation flags diff --git a/drivers/base/firmware_loader/fallback.c b/drivers/base/firmware_loader/fallback.c index 4dec4b79ae06..91899d185e31 100644 --- a/drivers/base/firmware_loader/fallback.c +++ b/drivers/base/firmware_loader/fallback.c @@ -128,7 +128,7 @@ static ssize_t timeout_show(struct class *class, struct class_attribute *attr, } /** - * firmware_timeout_store() - set number of seconds to wait for firmware + * timeout_store() - set number of seconds to wait for firmware * @class: device class pointer * @attr: device attribute pointer * @buf: buffer to scan for timeout value -- cgit v1.2.3 From 784b2c48ac12dcee27db001fb1a3c58c39380cb6 Mon Sep 17 00:00:00 2001 From: Dave Jiang Date: Fri, 4 Dec 2020 09:46:49 -0700 Subject: driver core: auxiliary bus: Fix auxiliary bus shutdown null auxdrv ptr If the probe of the auxdrv failed, the device->driver is set to NULL. During kernel shutdown, the bus shutdown will call auxdrv->shutdown and cause an invalid ptr dereference. Add check to make sure device->driver is not NULL before we proceed. Fixes: 7de3697e9cbd ("Add auxiliary bus support") Cc: Dave Ertman Signed-off-by: Dave Jiang Reviewed-by: Dan Williams Link: https://lore.kernel.org/r/160710040926.1889434.8840329810698403478.stgit@djiang5-desk3.ch.intel.com Signed-off-by: Greg Kroah-Hartman --- drivers/base/auxiliary.c | 11 ++++++++--- 1 file changed, 8 insertions(+), 3 deletions(-) (limited to 'drivers/base') diff --git a/drivers/base/auxiliary.c b/drivers/base/auxiliary.c index f303daadf843..8336535f1e11 100644 --- a/drivers/base/auxiliary.c +++ b/drivers/base/auxiliary.c @@ -92,10 +92,15 @@ static int auxiliary_bus_remove(struct device *dev) static void auxiliary_bus_shutdown(struct device *dev) { - struct auxiliary_driver *auxdrv = to_auxiliary_drv(dev->driver); - struct auxiliary_device *auxdev = to_auxiliary_dev(dev); + struct auxiliary_driver *auxdrv = NULL; + struct auxiliary_device *auxdev; + + if (dev->driver) { + auxdrv = to_auxiliary_drv(dev->driver); + auxdev = to_auxiliary_dev(dev); + } - if (auxdrv->shutdown) + if (auxdrv && auxdrv->shutdown) auxdrv->shutdown(auxdev); } -- cgit v1.2.3 From 66482f640755b31cb94371ff6cef17400cda6db5 Mon Sep 17 00:00:00 2001 From: Takashi Iwai Date: Tue, 8 Dec 2020 20:03:26 +0100 Subject: driver: core: Fix list corruption after device_del() The device_links_purge() function (called from device_del()) tries to remove the links.needs_suppliers list entry, but it's using list_del(), hence it doesn't initialize after the removal. This is OK for normal cases where device_del() is called via device_destroy(). However, it's not guaranteed that the device object will be really deleted soon after device_del(). In a minor case like HD-audio codec reconfiguration that re-initializes the device after device_del(), it may lead to a crash by the corrupted list entry. As a simple fix, replace list_del() with list_del_init() in order to make the list intact after the device_del() call. Fixes: e2ae9bcc4aaa ("driver core: Add support for linking devices during device addition") Cc: Reviewed-by: Rafael J. Wysocki Signed-off-by: Takashi Iwai Link: https://lore.kernel.org/r/20201208190326.27531-1-tiwai@suse.de Cc: Saravana Kannan Signed-off-by: Greg Kroah-Hartman --- drivers/base/core.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers/base') diff --git a/drivers/base/core.c b/drivers/base/core.c index 1165a80f8010..ba5a3cac6571 100644 --- a/drivers/base/core.c +++ b/drivers/base/core.c @@ -1384,7 +1384,7 @@ static void device_links_purge(struct device *dev) return; mutex_lock(&wfs_lock); - list_del(&dev->links.needs_suppliers); + list_del_init(&dev->links.needs_suppliers); mutex_unlock(&wfs_lock); /* -- cgit v1.2.3 From c95d64012ad7de2747923b0caf80e195e940606c Mon Sep 17 00:00:00 2001 From: Saravana Kannan Date: Fri, 20 Nov 2020 18:02:16 -0800 Subject: Revert "driver core: Avoid deferred probe due to fw_devlink_pause/resume()" This reverts commit 2451e746478a6a6e981cfa66b62b791ca93b90c8. fw_devlink_pause/resume() was an incomplete attempt at boot time optimization. That's going to get replaced by a much better optimization at the end of the series. Since fw_devlink_pause/resume() is going away, changes made for that can also go away. Signed-off-by: Saravana Kannan Link: https://lore.kernel.org/r/20201121020232.908850-2-saravanak@google.com Signed-off-by: Greg Kroah-Hartman --- drivers/base/core.c | 21 --------------------- 1 file changed, 21 deletions(-) (limited to 'drivers/base') diff --git a/drivers/base/core.c b/drivers/base/core.c index ba5a3cac6571..58c7d17820a7 100644 --- a/drivers/base/core.c +++ b/drivers/base/core.c @@ -51,7 +51,6 @@ static DEFINE_MUTEX(wfs_lock); static LIST_HEAD(deferred_sync); static unsigned int defer_sync_state_count = 1; static unsigned int defer_fw_devlink_count; -static LIST_HEAD(deferred_fw_devlink); static DEFINE_MUTEX(defer_fw_devlink_lock); static bool fw_devlink_is_permissive(void); @@ -1468,12 +1467,6 @@ static void fw_devlink_link_device(struct device *dev) fw_ret = -EAGAIN; } else { fw_ret = -ENODEV; - /* - * defer_hook is not used to add device to deferred_sync list - * until device is bound. Since deferred fw devlink also blocks - * probing, same list hook can be used for deferred_fw_devlink. - */ - list_add_tail(&dev->links.defer_hook, &deferred_fw_devlink); } if (fw_ret == -ENODEV) @@ -1542,9 +1535,6 @@ void fw_devlink_pause(void) */ void fw_devlink_resume(void) { - struct device *dev, *tmp; - LIST_HEAD(probe_list); - mutex_lock(&defer_fw_devlink_lock); if (!defer_fw_devlink_count) { WARN(true, "Unmatched fw_devlink pause/resume!"); @@ -1556,19 +1546,8 @@ void fw_devlink_resume(void) goto out; device_link_add_missing_supplier_links(); - list_splice_tail_init(&deferred_fw_devlink, &probe_list); out: mutex_unlock(&defer_fw_devlink_lock); - - /* - * bus_probe_device() can cause new devices to get added and they'll - * try to grab defer_fw_devlink_lock. So, this needs to be done outside - * the defer_fw_devlink_lock. - */ - list_for_each_entry_safe(dev, tmp, &probe_list, links.defer_hook) { - list_del_init(&dev->links.defer_hook); - bus_probe_device(dev); - } } /* Device links support end. */ -- cgit v1.2.3 From 3b052a3e30f2eb92dcae9fd89af48d5a13045737 Mon Sep 17 00:00:00 2001 From: Saravana Kannan Date: Fri, 20 Nov 2020 18:02:17 -0800 Subject: Revert "driver core: Rename dev_links_info.defer_sync to defer_hook" This reverts commit ec7bd78498f29680f536451fbdf9464e851273ed. This field rename was done to reuse defer_syc list head for multiple lists. That's not needed anymore and this list head will only be used for defer sync. So revert this patch to avoid conflicts with the other reverts coming after this. Signed-off-by: Saravana Kannan Link: https://lore.kernel.org/r/20201121020232.908850-3-saravanak@google.com Signed-off-by: Greg Kroah-Hartman --- drivers/base/core.c | 22 +++++++++++----------- 1 file changed, 11 insertions(+), 11 deletions(-) (limited to 'drivers/base') diff --git a/drivers/base/core.c b/drivers/base/core.c index 58c7d17820a7..fc4b0bc59935 100644 --- a/drivers/base/core.c +++ b/drivers/base/core.c @@ -959,11 +959,11 @@ static void __device_links_queue_sync_state(struct device *dev, */ dev->state_synced = true; - if (WARN_ON(!list_empty(&dev->links.defer_hook))) + if (WARN_ON(!list_empty(&dev->links.defer_sync))) return; get_device(dev); - list_add_tail(&dev->links.defer_hook, list); + list_add_tail(&dev->links.defer_sync, list); } /** @@ -981,8 +981,8 @@ static void device_links_flush_sync_list(struct list_head *list, { struct device *dev, *tmp; - list_for_each_entry_safe(dev, tmp, list, links.defer_hook) { - list_del_init(&dev->links.defer_hook); + list_for_each_entry_safe(dev, tmp, list, links.defer_sync) { + list_del_init(&dev->links.defer_sync); if (dev != dont_lock_dev) device_lock(dev); @@ -1020,12 +1020,12 @@ void device_links_supplier_sync_state_resume(void) if (defer_sync_state_count) goto out; - list_for_each_entry_safe(dev, tmp, &deferred_sync, links.defer_hook) { + list_for_each_entry_safe(dev, tmp, &deferred_sync, links.defer_sync) { /* * Delete from deferred_sync list before queuing it to - * sync_list because defer_hook is used for both lists. + * sync_list because defer_sync is used for both lists. */ - list_del_init(&dev->links.defer_hook); + list_del_init(&dev->links.defer_sync); __device_links_queue_sync_state(dev, &sync_list); } out: @@ -1043,8 +1043,8 @@ late_initcall(sync_state_resume_initcall); static void __device_links_supplier_defer_sync(struct device *sup) { - if (list_empty(&sup->links.defer_hook) && dev_has_sync_state(sup)) - list_add_tail(&sup->links.defer_hook, &deferred_sync); + if (list_empty(&sup->links.defer_sync) && dev_has_sync_state(sup)) + list_add_tail(&sup->links.defer_sync, &deferred_sync); } static void device_link_drop_managed(struct device_link *link) @@ -1272,7 +1272,7 @@ void device_links_driver_cleanup(struct device *dev) WRITE_ONCE(link->status, DL_STATE_DORMANT); } - list_del_init(&dev->links.defer_hook); + list_del_init(&dev->links.defer_sync); __device_links_no_driver(dev); device_links_write_unlock(); @@ -2405,7 +2405,7 @@ void device_initialize(struct device *dev) INIT_LIST_HEAD(&dev->links.consumers); INIT_LIST_HEAD(&dev->links.suppliers); INIT_LIST_HEAD(&dev->links.needs_suppliers); - INIT_LIST_HEAD(&dev->links.defer_hook); + INIT_LIST_HEAD(&dev->links.defer_sync); dev->links.status = DL_DEV_NO_DRIVER; } EXPORT_SYMBOL_GPL(device_initialize); -- cgit v1.2.3 From 96d8a9168ef5539beba9951dffe6eda07b74833e Mon Sep 17 00:00:00 2001 From: Saravana Kannan Date: Fri, 20 Nov 2020 18:02:18 -0800 Subject: Revert "driver core: Don't do deferred probe in parallel with kernel_init thread" This reverts commit cec72f3efc6272420c2c2c699607f03d09b93e41. Commit cec72f3efc62 ("driver core: Don't do deferred probe in parallel with kernel_init thread") was fixing a commit 716a7a259690 ("driver core: fw_devlink: Add support for batching fwnode parsing"). Since the commit being fixed itself is going to be reverted, the fix can also be reverted. Signed-off-by: Saravana Kannan Link: https://lore.kernel.org/r/20201121020232.908850-4-saravanak@google.com Signed-off-by: Greg Kroah-Hartman --- drivers/base/base.h | 1 + drivers/base/core.c | 1 + drivers/base/dd.c | 5 +++++ 3 files changed, 7 insertions(+) (limited to 'drivers/base') diff --git a/drivers/base/base.h b/drivers/base/base.h index 91cfb8405abd..c3562adf4789 100644 --- a/drivers/base/base.h +++ b/drivers/base/base.h @@ -156,6 +156,7 @@ extern char *make_class_name(const char *name, struct kobject *kobj); extern int devres_release_all(struct device *dev); extern void device_block_probing(void); extern void device_unblock_probing(void); +extern void driver_deferred_probe_force_trigger(void); /* /sys/devices directory */ extern struct kset *devices_kset; diff --git a/drivers/base/core.c b/drivers/base/core.c index fc4b0bc59935..b1189697c29b 100644 --- a/drivers/base/core.c +++ b/drivers/base/core.c @@ -1546,6 +1546,7 @@ void fw_devlink_resume(void) goto out; device_link_add_missing_supplier_links(); + driver_deferred_probe_force_trigger(); out: mutex_unlock(&defer_fw_devlink_lock); } diff --git a/drivers/base/dd.c b/drivers/base/dd.c index 148e81969e04..001caa0b447c 100644 --- a/drivers/base/dd.c +++ b/drivers/base/dd.c @@ -167,6 +167,11 @@ static void driver_deferred_probe_trigger(void) if (!driver_deferred_probe_enable) return; + driver_deferred_probe_force_trigger(); +} + +void driver_deferred_probe_force_trigger(void) +{ /* * A successful probe means that all the devices in the pending list * should be triggered to be reprobed. Move all the deferred devices -- cgit v1.2.3 From 999032ece38be271ca28a6c0ba5f2e4dd4012425 Mon Sep 17 00:00:00 2001 From: Saravana Kannan Date: Fri, 20 Nov 2020 18:02:19 -0800 Subject: Revert "driver core: Remove check in driver_deferred_probe_force_trigger()" This reverts commit fefcfc968723caf93318613a08e1f3ad07a6154f. The reverted commit is fixing commit 716a7a259690 ("driver core: fw_devlink: Add support for batching fwnode parsing"). Since the original commit will be reverted, the fix can be reverted too. Signed-off-by: Saravana Kannan Link: https://lore.kernel.org/r/20201121020232.908850-5-saravanak@google.com Signed-off-by: Greg Kroah-Hartman --- drivers/base/dd.c | 3 +++ 1 file changed, 3 insertions(+) (limited to 'drivers/base') diff --git a/drivers/base/dd.c b/drivers/base/dd.c index 001caa0b447c..b4be35fa7fda 100644 --- a/drivers/base/dd.c +++ b/drivers/base/dd.c @@ -172,6 +172,9 @@ static void driver_deferred_probe_trigger(void) void driver_deferred_probe_force_trigger(void) { + if (!driver_deferred_probe_enable) + return; + /* * A successful probe means that all the devices in the pending list * should be triggered to be reprobed. Move all the deferred devices -- cgit v1.2.3 From c84b90909e475a2eb4934b4d92fdd10e73e75805 Mon Sep 17 00:00:00 2001 From: Saravana Kannan Date: Fri, 20 Nov 2020 18:02:21 -0800 Subject: Revert "driver core: fw_devlink: Add support for batching fwnode parsing" This reverts commit 716a7a25969003d82ab738179c3f1068a120ed11. The fw_devlink_pause/resume() APIs added by the commit being reverted were a first cut attempt at optimizing boot time. But these APIs don't fully solve the problem and are very fragile (can only be used for the top level devices being added). This series replaces them with a much better optimization that works for all device additions and also has the benefit of reducing the complexity of the firmware (DT, EFI) specific code and abstracting out common code to driver core. Signed-off-by: Saravana Kannan Link: https://lore.kernel.org/r/20201121020232.908850-7-saravanak@google.com Signed-off-by: Greg Kroah-Hartman --- drivers/base/base.h | 1 - drivers/base/core.c | 116 ++++------------------------------------------------ drivers/base/dd.c | 8 ---- 3 files changed, 7 insertions(+), 118 deletions(-) (limited to 'drivers/base') diff --git a/drivers/base/base.h b/drivers/base/base.h index c3562adf4789..91cfb8405abd 100644 --- a/drivers/base/base.h +++ b/drivers/base/base.h @@ -156,7 +156,6 @@ extern char *make_class_name(const char *name, struct kobject *kobj); extern int devres_release_all(struct device *dev); extern void device_block_probing(void); extern void device_unblock_probing(void); -extern void driver_deferred_probe_force_trigger(void); /* /sys/devices directory */ extern struct kset *devices_kset; diff --git a/drivers/base/core.c b/drivers/base/core.c index b1189697c29b..670aa452bfe7 100644 --- a/drivers/base/core.c +++ b/drivers/base/core.c @@ -50,9 +50,6 @@ static LIST_HEAD(wait_for_suppliers); static DEFINE_MUTEX(wfs_lock); static LIST_HEAD(deferred_sync); static unsigned int defer_sync_state_count = 1; -static unsigned int defer_fw_devlink_count; -static DEFINE_MUTEX(defer_fw_devlink_lock); -static bool fw_devlink_is_permissive(void); #ifdef CONFIG_SRCU static DEFINE_MUTEX(device_links_lock); @@ -758,7 +755,7 @@ static void device_link_add_missing_supplier_links(void) int ret = fwnode_call_int_op(dev->fwnode, add_links, dev); if (!ret) list_del_init(&dev->links.needs_suppliers); - else if (ret != -ENODEV || fw_devlink_is_permissive()) + else if (ret != -ENODEV) dev->links.need_for_probe = false; } mutex_unlock(&wfs_lock); @@ -1440,116 +1437,17 @@ static void fw_devlink_link_device(struct device *dev) { int fw_ret; - if (!fw_devlink_flags) - return; - - mutex_lock(&defer_fw_devlink_lock); - if (!defer_fw_devlink_count) - device_link_add_missing_supplier_links(); - - /* - * The device's fwnode not having add_links() doesn't affect if other - * consumers can find this device as a supplier. So, this check is - * intentionally placed after device_link_add_missing_supplier_links(). - */ - if (!fwnode_has_op(dev->fwnode, add_links)) - goto out; + device_link_add_missing_supplier_links(); - /* - * If fw_devlink is being deferred, assume all devices have mandatory - * suppliers they need to link to later. Then, when the fw_devlink is - * resumed, all these devices will get a chance to try and link to any - * suppliers they have. - */ - if (!defer_fw_devlink_count) { + if (fw_devlink_flags && fwnode_has_op(dev->fwnode, add_links)) { fw_ret = fwnode_call_int_op(dev->fwnode, add_links, dev); - if (fw_ret == -ENODEV && fw_devlink_is_permissive()) - fw_ret = -EAGAIN; - } else { - fw_ret = -ENODEV; + if (fw_ret == -ENODEV && !fw_devlink_is_permissive()) + device_link_wait_for_mandatory_supplier(dev); + else if (fw_ret) + device_link_wait_for_optional_supplier(dev); } - - if (fw_ret == -ENODEV) - device_link_wait_for_mandatory_supplier(dev); - else if (fw_ret) - device_link_wait_for_optional_supplier(dev); - -out: - mutex_unlock(&defer_fw_devlink_lock); } -/** - * fw_devlink_pause - Pause parsing of fwnode to create device links - * - * Calling this function defers any fwnode parsing to create device links until - * fw_devlink_resume() is called. Both these functions are ref counted and the - * caller needs to match the calls. - * - * While fw_devlink is paused: - * - Any device that is added won't have its fwnode parsed to create device - * links. - * - The probe of the device will also be deferred during this period. - * - Any devices that were already added, but waiting for suppliers won't be - * able to link to newly added devices. - * - * Once fw_devlink_resume(): - * - All the fwnodes that was not parsed will be parsed. - * - All the devices that were deferred probing will be reattempted if they - * aren't waiting for any more suppliers. - * - * This pair of functions, is mainly meant to optimize the parsing of fwnodes - * when a lot of devices that need to link to each other are added in a short - * interval of time. For example, adding all the top level devices in a system. - * - * For example, if N devices are added and: - * - All the consumers are added before their suppliers - * - All the suppliers of the N devices are part of the N devices - * - * Then: - * - * - With the use of fw_devlink_pause() and fw_devlink_resume(), each device - * will only need one parsing of its fwnode because it is guaranteed to find - * all the supplier devices already registered and ready to link to. It won't - * have to do another pass later to find one or more suppliers it couldn't - * find in the first parse of the fwnode. So, we'll only need O(N) fwnode - * parses. - * - * - Without the use of fw_devlink_pause() and fw_devlink_resume(), we would - * end up doing O(N^2) parses of fwnodes because every device that's added is - * guaranteed to trigger a parse of the fwnode of every device added before - * it. This O(N^2) parse is made worse by the fact that when a fwnode of a - * device is parsed, all it descendant devices might need to have their - * fwnodes parsed too (even if the devices themselves aren't added). - */ -void fw_devlink_pause(void) -{ - mutex_lock(&defer_fw_devlink_lock); - defer_fw_devlink_count++; - mutex_unlock(&defer_fw_devlink_lock); -} - -/** fw_devlink_resume - Resume parsing of fwnode to create device links - * - * This function is used in conjunction with fw_devlink_pause() and is ref - * counted. See documentation for fw_devlink_pause() for more details. - */ -void fw_devlink_resume(void) -{ - mutex_lock(&defer_fw_devlink_lock); - if (!defer_fw_devlink_count) { - WARN(true, "Unmatched fw_devlink pause/resume!"); - goto out; - } - - defer_fw_devlink_count--; - if (defer_fw_devlink_count) - goto out; - - device_link_add_missing_supplier_links(); - driver_deferred_probe_force_trigger(); -out: - mutex_unlock(&defer_fw_devlink_lock); -} /* Device links support end. */ int (*platform_notify)(struct device *dev) = NULL; diff --git a/drivers/base/dd.c b/drivers/base/dd.c index b4be35fa7fda..148e81969e04 100644 --- a/drivers/base/dd.c +++ b/drivers/base/dd.c @@ -163,14 +163,6 @@ static bool driver_deferred_probe_enable = false; * again. */ static void driver_deferred_probe_trigger(void) -{ - if (!driver_deferred_probe_enable) - return; - - driver_deferred_probe_force_trigger(); -} - -void driver_deferred_probe_force_trigger(void) { if (!driver_deferred_probe_enable) return; -- cgit v1.2.3 From 01bb86b380a306bd937c96da36f66429f3362137 Mon Sep 17 00:00:00 2001 From: Saravana Kannan Date: Fri, 20 Nov 2020 18:02:22 -0800 Subject: driver core: Add fwnode_init() There are multiple locations in the kernel where a struct fwnode_handle is initialized. Add fwnode_init() so that we have one way of initializing a fwnode_handle. Acked-by: Rob Herring Signed-off-by: Saravana Kannan Link: https://lore.kernel.org/r/20201121020232.908850-8-saravanak@google.com Signed-off-by: Greg Kroah-Hartman --- drivers/base/swnode.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers/base') diff --git a/drivers/base/swnode.c b/drivers/base/swnode.c index 010828fc785b..4a4b2008fbc2 100644 --- a/drivers/base/swnode.c +++ b/drivers/base/swnode.c @@ -653,7 +653,7 @@ swnode_register(const struct software_node *node, struct swnode *parent, swnode->parent = parent; swnode->allocated = allocated; swnode->kobj.kset = swnode_kset; - swnode->fwnode.ops = &software_node_ops; + fwnode_init(&swnode->fwnode, &software_node_ops); ida_init(&swnode->child_ids); INIT_LIST_HEAD(&swnode->entry); -- cgit v1.2.3 From 7b337cb3ebde384cba7405b61dfb84200bf623bf Mon Sep 17 00:00:00 2001 From: Saravana Kannan Date: Fri, 20 Nov 2020 18:02:23 -0800 Subject: driver core: Add fwnode link support Add support for creating supplier-consumer links between fwnodes. It is intended for internal use the driver core and generic firmware support code (eg. Device Tree, ACPI), so it is simple by design and the API provided is limited. Acked-by: Rob Herring Signed-off-by: Saravana Kannan Link: https://lore.kernel.org/r/20201121020232.908850-9-saravanak@google.com Signed-off-by: Greg Kroah-Hartman --- drivers/base/core.c | 98 +++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 98 insertions(+) (limited to 'drivers/base') diff --git a/drivers/base/core.c b/drivers/base/core.c index 670aa452bfe7..972d42dedfc8 100644 --- a/drivers/base/core.c +++ b/drivers/base/core.c @@ -50,6 +50,104 @@ static LIST_HEAD(wait_for_suppliers); static DEFINE_MUTEX(wfs_lock); static LIST_HEAD(deferred_sync); static unsigned int defer_sync_state_count = 1; +static DEFINE_MUTEX(fwnode_link_lock); + +/** + * fwnode_link_add - Create a link between two fwnode_handles. + * @con: Consumer end of the link. + * @sup: Supplier end of the link. + * + * Create a fwnode link between fwnode handles @con and @sup. The fwnode link + * represents the detail that the firmware lists @sup fwnode as supplying a + * resource to @con. + * + * The driver core will use the fwnode link to create a device link between the + * two device objects corresponding to @con and @sup when they are created. The + * driver core will automatically delete the fwnode link between @con and @sup + * after doing that. + * + * Attempts to create duplicate links between the same pair of fwnode handles + * are ignored and there is no reference counting. + */ +int fwnode_link_add(struct fwnode_handle *con, struct fwnode_handle *sup) +{ + struct fwnode_link *link; + int ret = 0; + + mutex_lock(&fwnode_link_lock); + + list_for_each_entry(link, &sup->consumers, s_hook) + if (link->consumer == con) + goto out; + + link = kzalloc(sizeof(*link), GFP_KERNEL); + if (!link) { + ret = -ENOMEM; + goto out; + } + + link->supplier = sup; + INIT_LIST_HEAD(&link->s_hook); + link->consumer = con; + INIT_LIST_HEAD(&link->c_hook); + + list_add(&link->s_hook, &sup->consumers); + list_add(&link->c_hook, &con->suppliers); +out: + mutex_unlock(&fwnode_link_lock); + + return ret; +} + +/** + * fwnode_links_purge_suppliers - Delete all supplier links of fwnode_handle. + * @fwnode: fwnode whose supplier links need to be deleted + * + * Deletes all supplier links connecting directly to @fwnode. + */ +static void fwnode_links_purge_suppliers(struct fwnode_handle *fwnode) +{ + struct fwnode_link *link, *tmp; + + mutex_lock(&fwnode_link_lock); + list_for_each_entry_safe(link, tmp, &fwnode->suppliers, c_hook) { + list_del(&link->s_hook); + list_del(&link->c_hook); + kfree(link); + } + mutex_unlock(&fwnode_link_lock); +} + +/** + * fwnode_links_purge_consumers - Delete all consumer links of fwnode_handle. + * @fwnode: fwnode whose consumer links need to be deleted + * + * Deletes all consumer links connecting directly to @fwnode. + */ +static void fwnode_links_purge_consumers(struct fwnode_handle *fwnode) +{ + struct fwnode_link *link, *tmp; + + mutex_lock(&fwnode_link_lock); + list_for_each_entry_safe(link, tmp, &fwnode->consumers, s_hook) { + list_del(&link->s_hook); + list_del(&link->c_hook); + kfree(link); + } + mutex_unlock(&fwnode_link_lock); +} + +/** + * fwnode_links_purge - Delete all links connected to a fwnode_handle. + * @fwnode: fwnode whose links needs to be deleted + * + * Deletes all links connecting directly to a fwnode. + */ +void fwnode_links_purge(struct fwnode_handle *fwnode) +{ + fwnode_links_purge_suppliers(fwnode); + fwnode_links_purge_consumers(fwnode); +} #ifdef CONFIG_SRCU static DEFINE_MUTEX(device_links_lock); -- cgit v1.2.3 From ac66c5bbb4371073dcace77e47c234d2e36a006b Mon Sep 17 00:00:00 2001 From: Saravana Kannan Date: Fri, 20 Nov 2020 18:02:24 -0800 Subject: driver core: Allow only unprobed consumers for SYNC_STATE_ONLY device links SYNC_STATE_ONLY device links only affect the behavior of sync_state() callbacks. Specifically, they prevent sync_state() only callbacks from being called on a device if one or more of its consumers haven't probed. So, creating a SYNC_STATE_ONLY device link from an already probed consumer is useless. So, don't allow creating such device links. Signed-off-by: Saravana Kannan Link: https://lore.kernel.org/r/20201121020232.908850-10-saravanak@google.com Signed-off-by: Greg Kroah-Hartman --- drivers/base/core.c | 11 +++++++++++ 1 file changed, 11 insertions(+) (limited to 'drivers/base') diff --git a/drivers/base/core.c b/drivers/base/core.c index 972d42dedfc8..edde79fc3d33 100644 --- a/drivers/base/core.c +++ b/drivers/base/core.c @@ -649,6 +649,17 @@ struct device_link *device_link_add(struct device *consumer, goto out; } + /* + * SYNC_STATE_ONLY links are useless once a consumer device has probed. + * So, only create it if the consumer hasn't probed yet. + */ + if (flags & DL_FLAG_SYNC_STATE_ONLY && + consumer->links.status != DL_DEV_NO_DRIVER && + consumer->links.status != DL_DEV_PROBING) { + link = NULL; + goto out; + } + /* * DL_FLAG_AUTOREMOVE_SUPPLIER indicates that the link will be needed * longer than for DL_FLAG_AUTOREMOVE_CONSUMER and setting them both -- cgit v1.2.3 From b5d3e2fbcb10957521af14c4256cd0e5f68b9234 Mon Sep 17 00:00:00 2001 From: Saravana Kannan Date: Fri, 20 Nov 2020 18:02:25 -0800 Subject: device property: Add fwnode_is_ancestor_of() and fwnode_get_next_parent_dev() Add fwnode_is_ancestor_of() helper function to check if a fwnode is an ancestor of another fwnode. Add fwnode_get_next_parent_dev() helper function that take as input a fwnode and finds the closest ancestor fwnode that has a corresponding struct device and returns that struct device. Signed-off-by: Saravana Kannan Link: https://lore.kernel.org/r/20201121020232.908850-11-saravanak@google.com Signed-off-by: Greg Kroah-Hartman --- drivers/base/property.c | 52 +++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 52 insertions(+) (limited to 'drivers/base') diff --git a/drivers/base/property.c b/drivers/base/property.c index 4c43d30145c6..35b95c6ac0c6 100644 --- a/drivers/base/property.c +++ b/drivers/base/property.c @@ -614,6 +614,31 @@ struct fwnode_handle *fwnode_get_next_parent(struct fwnode_handle *fwnode) } EXPORT_SYMBOL_GPL(fwnode_get_next_parent); +/** + * fwnode_get_next_parent_dev - Find device of closest ancestor fwnode + * @fwnode: firmware node + * + * Given a firmware node (@fwnode), this function finds its closest ancestor + * firmware node that has a corresponding struct device and returns that struct + * device. + * + * The caller of this function is expected to call put_device() on the returned + * device when they are done. + */ +struct device *fwnode_get_next_parent_dev(struct fwnode_handle *fwnode) +{ + struct device *dev = NULL; + + fwnode_handle_get(fwnode); + do { + fwnode = fwnode_get_next_parent(fwnode); + if (fwnode) + dev = get_dev_from_fwnode(fwnode); + } while (fwnode && !dev); + fwnode_handle_put(fwnode); + return dev; +} + /** * fwnode_count_parents - Return the number of parents a node has * @fwnode: The node the parents of which are to be counted @@ -660,6 +685,33 @@ struct fwnode_handle *fwnode_get_nth_parent(struct fwnode_handle *fwnode, } EXPORT_SYMBOL_GPL(fwnode_get_nth_parent); +/** + * fwnode_is_ancestor_of - Test if @test_ancestor is ancestor of @test_child + * @test_ancestor: Firmware which is tested for being an ancestor + * @test_child: Firmware which is tested for being the child + * + * A node is considered an ancestor of itself too. + * + * Returns true if @test_ancestor is an ancestor of @test_child. + * Otherwise, returns false. + */ +bool fwnode_is_ancestor_of(struct fwnode_handle *test_ancestor, + struct fwnode_handle *test_child) +{ + if (!test_ancestor) + return false; + + fwnode_handle_get(test_child); + while (test_child) { + if (test_child == test_ancestor) { + fwnode_handle_put(test_child); + return true; + } + test_child = fwnode_get_next_parent(test_child); + } + return false; +} + /** * fwnode_get_next_child_node - Return the next child node handle for a node * @fwnode: Firmware node to find the next child node for. -- cgit v1.2.3 From c2c724c868c42c5166bf7aa644dd0a0c8d30b47a Mon Sep 17 00:00:00 2001 From: Saravana Kannan Date: Fri, 20 Nov 2020 18:02:27 -0800 Subject: driver core: Add fw_devlink_parse_fwtree() This function is a wrapper around fwnode_operations.add_links(). This function parses each node in a fwnode tree and create fwnode links for each of those nodes. The information for creating the fwnode links (the supplier and consumer fwnode) is obtained by parsing the properties in each of the fwnodes. This function also ensures that no fwnode is parsed more than once by marking the fwnodes as parsed. Signed-off-by: Saravana Kannan Link: https://lore.kernel.org/r/20201121020232.908850-13-saravanak@google.com Signed-off-by: Greg Kroah-Hartman --- drivers/base/core.c | 19 +++++++++++++++++++ 1 file changed, 19 insertions(+) (limited to 'drivers/base') diff --git a/drivers/base/core.c b/drivers/base/core.c index edde79fc3d33..92a2dc355d13 100644 --- a/drivers/base/core.c +++ b/drivers/base/core.c @@ -1542,6 +1542,25 @@ static bool fw_devlink_is_permissive(void) return fw_devlink_flags == DL_FLAG_SYNC_STATE_ONLY; } +static void fw_devlink_parse_fwnode(struct fwnode_handle *fwnode) +{ + if (fwnode->flags & FWNODE_FLAG_LINKS_ADDED) + return; + + fwnode_call_int_op(fwnode, add_links, NULL); + fwnode->flags |= FWNODE_FLAG_LINKS_ADDED; +} + +static void fw_devlink_parse_fwtree(struct fwnode_handle *fwnode) +{ + struct fwnode_handle *child = NULL; + + fw_devlink_parse_fwnode(fwnode); + + while ((child = fwnode_get_next_available_child_node(fwnode, child))) + fw_devlink_parse_fwtree(child); +} + static void fw_devlink_link_device(struct device *dev) { int fw_ret; -- cgit v1.2.3 From 25ac86c6dbe62fba9b97e997fa648cdbe2d40173 Mon Sep 17 00:00:00 2001 From: Saravana Kannan Date: Fri, 20 Nov 2020 18:02:28 -0800 Subject: driver core: Use device's fwnode to check if it is waiting for suppliers To check if a device is still waiting for its supplier devices to be added, we used to check if the devices is in a global waiting_for_suppliers list. Since the global list will be deleted in subsequent patches, this patch stops using this check. Instead, this patch uses a more device specific check. It checks if the device's fwnode has any fwnode links that haven't been converted to device links yet. Signed-off-by: Saravana Kannan Link: https://lore.kernel.org/r/20201121020232.908850-14-saravanak@google.com Signed-off-by: Greg Kroah-Hartman --- drivers/base/core.c | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) (limited to 'drivers/base') diff --git a/drivers/base/core.c b/drivers/base/core.c index 92a2dc355d13..8a973d9601d3 100644 --- a/drivers/base/core.c +++ b/drivers/base/core.c @@ -51,6 +51,7 @@ static DEFINE_MUTEX(wfs_lock); static LIST_HEAD(deferred_sync); static unsigned int defer_sync_state_count = 1; static DEFINE_MUTEX(fwnode_link_lock); +static bool fw_devlink_is_permissive(void); /** * fwnode_link_add - Create a link between two fwnode_handles. @@ -995,13 +996,13 @@ int device_links_check_suppliers(struct device *dev) * Device waiting for supplier to become available is not allowed to * probe. */ - mutex_lock(&wfs_lock); - if (!list_empty(&dev->links.needs_suppliers) && - dev->links.need_for_probe) { - mutex_unlock(&wfs_lock); + mutex_lock(&fwnode_link_lock); + if (dev->fwnode && !list_empty(&dev->fwnode->suppliers) && + !fw_devlink_is_permissive()) { + mutex_unlock(&fwnode_link_lock); return -EPROBE_DEFER; } - mutex_unlock(&wfs_lock); + mutex_unlock(&fwnode_link_lock); device_links_write_lock(); @@ -1167,8 +1168,7 @@ static ssize_t waiting_for_supplier_show(struct device *dev, bool val; device_lock(dev); - val = !list_empty(&dev->links.needs_suppliers) - && dev->links.need_for_probe; + val = !list_empty(&dev->fwnode->suppliers); device_unlock(dev); return sysfs_emit(buf, "%u\n", val); } @@ -2200,7 +2200,7 @@ static int device_add_attrs(struct device *dev) goto err_remove_dev_groups; } - if (fw_devlink_flags && !fw_devlink_is_permissive()) { + if (fw_devlink_flags && !fw_devlink_is_permissive() && dev->fwnode) { error = device_create_file(dev, &dev_attr_waiting_for_supplier); if (error) goto err_remove_dev_online; -- cgit v1.2.3 From f9aa460672c9c56896cdc12a521159e3e67000ba Mon Sep 17 00:00:00 2001 From: Saravana Kannan Date: Fri, 20 Nov 2020 18:02:31 -0800 Subject: driver core: Refactor fw_devlink feature The current implementation of fw_devlink is very inefficient because it tries to get away without creating fwnode links in the name of saving memory usage. Past attempts to optimize runtime at the cost of memory usage were blocked with request for data showing that the optimization made significant improvement for real world scenarios. We have those scenarios now. There have been several reports of boot time increase in the order of seconds in this thread [1]. Several OEMs and SoC manufacturers have also privately reported significant (350-400ms) increase in boot time due to all the parsing done by fw_devlink. So this patch uses all the setup done by the previous patches in this series to refactor fw_devlink to be more efficient. Most of the code has been moved out of firmware specific (DT mostly) code into driver core. This brings the following benefits: - Instead of parsing the device tree multiple times during bootup, fw_devlink parses each fwnode node/property only once and creates fwnode links. The rest of the fw_devlink code then just looks at these fwnode links to do rest of the work. - Makes it much easier to debug probe issue due to fw_devlink in the future. fw_devlink=on blocks the probing of devices if they depend on a device that hasn't been added yet. With this refactor, it'll be very easy to tell what that device is because we now have a reference to the fwnode of the device. - Much easier to add fw_devlink support to ACPI and other firmware types. A refactor to move the common bits from DT specific code to driver core was in my TODO list as a prerequisite to adding ACPI support to fw_devlink. This series gets that done. [1] - https://lore.kernel.org/linux-omap/ea02f57e-871d-cd16-4418-c1da4bbc4696@ti.com/ Tested-by: Laurent Pinchart Tested-by: Grygorii Strashko Signed-off-by: Saravana Kannan Link: https://lore.kernel.org/r/20201121020232.908850-17-saravanak@google.com Signed-off-by: Greg Kroah-Hartman --- drivers/base/core.c | 325 ++++++++++++++++++++++++++++++++++++++-------------- 1 file changed, 238 insertions(+), 87 deletions(-) (limited to 'drivers/base') diff --git a/drivers/base/core.c b/drivers/base/core.c index 8a973d9601d3..9edf9084fc98 100644 --- a/drivers/base/core.c +++ b/drivers/base/core.c @@ -46,8 +46,6 @@ early_param("sysfs.deprecated", sysfs_deprecated_setup); #endif /* Device links support. */ -static LIST_HEAD(wait_for_suppliers); -static DEFINE_MUTEX(wfs_lock); static LIST_HEAD(deferred_sync); static unsigned int defer_sync_state_count = 1; static DEFINE_MUTEX(fwnode_link_lock); @@ -803,74 +801,6 @@ out: } EXPORT_SYMBOL_GPL(device_link_add); -/** - * device_link_wait_for_supplier - Add device to wait_for_suppliers list - * @consumer: Consumer device - * - * Marks the @consumer device as waiting for suppliers to become available by - * adding it to the wait_for_suppliers list. The consumer device will never be - * probed until it's removed from the wait_for_suppliers list. - * - * The caller is responsible for adding the links to the supplier devices once - * they are available and removing the @consumer device from the - * wait_for_suppliers list once links to all the suppliers have been created. - * - * This function is NOT meant to be called from the probe function of the - * consumer but rather from code that creates/adds the consumer device. - */ -static void device_link_wait_for_supplier(struct device *consumer, - bool need_for_probe) -{ - mutex_lock(&wfs_lock); - list_add_tail(&consumer->links.needs_suppliers, &wait_for_suppliers); - consumer->links.need_for_probe = need_for_probe; - mutex_unlock(&wfs_lock); -} - -static void device_link_wait_for_mandatory_supplier(struct device *consumer) -{ - device_link_wait_for_supplier(consumer, true); -} - -static void device_link_wait_for_optional_supplier(struct device *consumer) -{ - device_link_wait_for_supplier(consumer, false); -} - -/** - * device_link_add_missing_supplier_links - Add links from consumer devices to - * supplier devices, leaving any - * consumer with inactive suppliers on - * the wait_for_suppliers list - * - * Loops through all consumers waiting on suppliers and tries to add all their - * supplier links. If that succeeds, the consumer device is removed from - * wait_for_suppliers list. Otherwise, they are left in the wait_for_suppliers - * list. Devices left on the wait_for_suppliers list will not be probed. - * - * The fwnode add_links callback is expected to return 0 if it has found and - * added all the supplier links for the consumer device. It should return an - * error if it isn't able to do so. - * - * The caller of device_link_wait_for_supplier() is expected to call this once - * it's aware of potential suppliers becoming available. - */ -static void device_link_add_missing_supplier_links(void) -{ - struct device *dev, *tmp; - - mutex_lock(&wfs_lock); - list_for_each_entry_safe(dev, tmp, &wait_for_suppliers, - links.needs_suppliers) { - int ret = fwnode_call_int_op(dev->fwnode, add_links, dev); - if (!ret) - list_del_init(&dev->links.needs_suppliers); - else if (ret != -ENODEV) - dev->links.need_for_probe = false; - } - mutex_unlock(&wfs_lock); -} - #ifdef CONFIG_SRCU static void __device_link_del(struct kref *kref) { @@ -1195,9 +1125,8 @@ void device_links_driver_bound(struct device *dev) * the device links it needs to or make new device links as it needs * them. So, it no longer needs to wait on any suppliers. */ - mutex_lock(&wfs_lock); - list_del_init(&dev->links.needs_suppliers); - mutex_unlock(&wfs_lock); + if (dev->fwnode && dev->fwnode->dev == dev) + fwnode_links_purge_suppliers(dev->fwnode); device_remove_file(dev, &dev_attr_waiting_for_supplier); device_links_write_lock(); @@ -1488,10 +1417,6 @@ static void device_links_purge(struct device *dev) if (dev->class == &devlink_class) return; - mutex_lock(&wfs_lock); - list_del_init(&dev->links.needs_suppliers); - mutex_unlock(&wfs_lock); - /* * Delete all of the remaining links from this device to any other * devices (either consumers or suppliers). @@ -1561,19 +1486,246 @@ static void fw_devlink_parse_fwtree(struct fwnode_handle *fwnode) fw_devlink_parse_fwtree(child); } -static void fw_devlink_link_device(struct device *dev) +/** + * fw_devlink_create_devlink - Create a device link from a consumer to fwnode + * @con - Consumer device for the device link + * @sup_handle - fwnode handle of supplier + * + * This function will try to create a device link between the consumer device + * @con and the supplier device represented by @sup_handle. + * + * The supplier has to be provided as a fwnode because incorrect cycles in + * fwnode links can sometimes cause the supplier device to never be created. + * This function detects such cases and returns an error if it cannot create a + * device link from the consumer to a missing supplier. + * + * Returns, + * 0 on successfully creating a device link + * -EINVAL if the device link cannot be created as expected + * -EAGAIN if the device link cannot be created right now, but it may be + * possible to do that in the future + */ +static int fw_devlink_create_devlink(struct device *con, + struct fwnode_handle *sup_handle, u32 flags) +{ + struct device *sup_dev; + int ret = 0; + + sup_dev = get_dev_from_fwnode(sup_handle); + if (sup_dev) { + /* + * If this fails, it is due to cycles in device links. Just + * give up on this link and treat it as invalid. + */ + if (!device_link_add(con, sup_dev, flags)) + ret = -EINVAL; + + goto out; + } + + /* + * DL_FLAG_SYNC_STATE_ONLY doesn't block probing and supports + * cycles. So cycle detection isn't necessary and shouldn't be + * done. + */ + if (flags & DL_FLAG_SYNC_STATE_ONLY) + return -EAGAIN; + + /* + * If we can't find the supplier device from its fwnode, it might be + * due to a cyclic dependency between fwnodes. Some of these cycles can + * be broken by applying logic. Check for these types of cycles and + * break them so that devices in the cycle probe properly. + * + * If the supplier's parent is dependent on the consumer, then + * the consumer-supplier dependency is a false dependency. So, + * treat it as an invalid link. + */ + sup_dev = fwnode_get_next_parent_dev(sup_handle); + if (sup_dev && device_is_dependent(con, sup_dev)) { + dev_dbg(con, "Not linking to %pfwP - False link\n", + sup_handle); + ret = -EINVAL; + } else { + /* + * Can't check for cycles or no cycles. So let's try + * again later. + */ + ret = -EAGAIN; + } + +out: + put_device(sup_dev); + return ret; +} + +/** + * __fw_devlink_link_to_consumers - Create device links to consumers of a device + * @dev - Device that needs to be linked to its consumers + * + * This function looks at all the consumer fwnodes of @dev and creates device + * links between the consumer device and @dev (supplier). + * + * If the consumer device has not been added yet, then this function creates a + * SYNC_STATE_ONLY link between @dev (supplier) and the closest ancestor device + * of the consumer fwnode. This is necessary to make sure @dev doesn't get a + * sync_state() callback before the real consumer device gets to be added and + * then probed. + * + * Once device links are created from the real consumer to @dev (supplier), the + * fwnode links are deleted. + */ +static void __fw_devlink_link_to_consumers(struct device *dev) +{ + struct fwnode_handle *fwnode = dev->fwnode; + struct fwnode_link *link, *tmp; + + list_for_each_entry_safe(link, tmp, &fwnode->consumers, s_hook) { + u32 dl_flags = fw_devlink_get_flags(); + struct device *con_dev; + bool own_link = true; + int ret; + + con_dev = get_dev_from_fwnode(link->consumer); + /* + * If consumer device is not available yet, make a "proxy" + * SYNC_STATE_ONLY link from the consumer's parent device to + * the supplier device. This is necessary to make sure the + * supplier doesn't get a sync_state() callback before the real + * consumer can create a device link to the supplier. + * + * This proxy link step is needed to handle the case where the + * consumer's parent device is added before the supplier. + */ + if (!con_dev) { + con_dev = fwnode_get_next_parent_dev(link->consumer); + /* + * However, if the consumer's parent device is also the + * parent of the supplier, don't create a + * consumer-supplier link from the parent to its child + * device. Such a dependency is impossible. + */ + if (con_dev && + fwnode_is_ancestor_of(con_dev->fwnode, fwnode)) { + put_device(con_dev); + con_dev = NULL; + } else { + own_link = false; + dl_flags = DL_FLAG_SYNC_STATE_ONLY; + } + } + + if (!con_dev) + continue; + + ret = fw_devlink_create_devlink(con_dev, fwnode, dl_flags); + put_device(con_dev); + if (!own_link || ret == -EAGAIN) + continue; + + list_del(&link->s_hook); + list_del(&link->c_hook); + kfree(link); + } +} + +/** + * __fw_devlink_link_to_suppliers - Create device links to suppliers of a device + * @dev - The consumer device that needs to be linked to its suppliers + * @fwnode - Root of the fwnode tree that is used to create device links + * + * This function looks at all the supplier fwnodes of fwnode tree rooted at + * @fwnode and creates device links between @dev (consumer) and all the + * supplier devices of the entire fwnode tree at @fwnode. + * + * The function creates normal (non-SYNC_STATE_ONLY) device links between @dev + * and the real suppliers of @dev. Once these device links are created, the + * fwnode links are deleted. When such device links are successfully created, + * this function is called recursively on those supplier devices. This is + * needed to detect and break some invalid cycles in fwnode links. See + * fw_devlink_create_devlink() for more details. + * + * In addition, it also looks at all the suppliers of the entire fwnode tree + * because some of the child devices of @dev that have not been added yet + * (because @dev hasn't probed) might already have their suppliers added to + * driver core. So, this function creates SYNC_STATE_ONLY device links between + * @dev (consumer) and these suppliers to make sure they don't execute their + * sync_state() callbacks before these child devices have a chance to create + * their device links. The fwnode links that correspond to the child devices + * aren't delete because they are needed later to create the device links + * between the real consumer and supplier devices. + */ +static void __fw_devlink_link_to_suppliers(struct device *dev, + struct fwnode_handle *fwnode) { - int fw_ret; + bool own_link = (dev->fwnode == fwnode); + struct fwnode_link *link, *tmp; + struct fwnode_handle *child = NULL; + u32 dl_flags; + + if (own_link) + dl_flags = fw_devlink_get_flags(); + else + dl_flags = DL_FLAG_SYNC_STATE_ONLY; - device_link_add_missing_supplier_links(); + list_for_each_entry_safe(link, tmp, &fwnode->suppliers, c_hook) { + int ret; + struct device *sup_dev; + struct fwnode_handle *sup = link->supplier; + + ret = fw_devlink_create_devlink(dev, sup, dl_flags); + if (!own_link || ret == -EAGAIN) + continue; - if (fw_devlink_flags && fwnode_has_op(dev->fwnode, add_links)) { - fw_ret = fwnode_call_int_op(dev->fwnode, add_links, dev); - if (fw_ret == -ENODEV && !fw_devlink_is_permissive()) - device_link_wait_for_mandatory_supplier(dev); - else if (fw_ret) - device_link_wait_for_optional_supplier(dev); + list_del(&link->s_hook); + list_del(&link->c_hook); + kfree(link); + + /* If no device link was created, nothing more to do. */ + if (ret) + continue; + + /* + * If a device link was successfully created to a supplier, we + * now need to try and link the supplier to all its suppliers. + * + * This is needed to detect and delete false dependencies in + * fwnode links that haven't been converted to a device link + * yet. See comments in fw_devlink_create_devlink() for more + * details on the false dependency. + * + * Without deleting these false dependencies, some devices will + * never probe because they'll keep waiting for their false + * dependency fwnode links to be converted to device links. + */ + sup_dev = get_dev_from_fwnode(sup); + __fw_devlink_link_to_suppliers(sup_dev, sup_dev->fwnode); + put_device(sup_dev); } + + /* + * Make "proxy" SYNC_STATE_ONLY device links to represent the needs of + * all the descendants. This proxy link step is needed to handle the + * case where the supplier is added before the consumer's parent device + * (@dev). + */ + while ((child = fwnode_get_next_available_child_node(fwnode, child))) + __fw_devlink_link_to_suppliers(dev, child); +} + +static void fw_devlink_link_device(struct device *dev) +{ + struct fwnode_handle *fwnode = dev->fwnode; + + if (!fw_devlink_flags) + return; + + fw_devlink_parse_fwtree(fwnode); + + mutex_lock(&fwnode_link_lock); + __fw_devlink_link_to_consumers(dev); + __fw_devlink_link_to_suppliers(dev, fwnode); + mutex_unlock(&fwnode_link_lock); } /* Device links support end. */ @@ -2431,7 +2583,6 @@ void device_initialize(struct device *dev) #endif INIT_LIST_HEAD(&dev->links.consumers); INIT_LIST_HEAD(&dev->links.suppliers); - INIT_LIST_HEAD(&dev->links.needs_suppliers); INIT_LIST_HEAD(&dev->links.defer_sync); dev->links.status = DL_DEV_NO_DRIVER; } -- cgit v1.2.3 From 2d09e6eb4a6f20273959f4905ccf009da8c64c7a Mon Sep 17 00:00:00 2001 From: Saravana Kannan Date: Fri, 20 Nov 2020 18:02:32 -0800 Subject: driver core: Delete pointless parameter in fwnode_operations.add_links The struct device input to add_links() is not used for anything. So delete it. Acked-by: Rob Herring Signed-off-by: Saravana Kannan Link: https://lore.kernel.org/r/20201121020232.908850-18-saravanak@google.com Signed-off-by: Greg Kroah-Hartman --- drivers/base/core.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers/base') diff --git a/drivers/base/core.c b/drivers/base/core.c index 9edf9084fc98..63edb8bd9d7d 100644 --- a/drivers/base/core.c +++ b/drivers/base/core.c @@ -1472,7 +1472,7 @@ static void fw_devlink_parse_fwnode(struct fwnode_handle *fwnode) if (fwnode->flags & FWNODE_FLAG_LINKS_ADDED) return; - fwnode_call_int_op(fwnode, add_links, NULL); + fwnode_call_int_op(fwnode, add_links); fwnode->flags |= FWNODE_FLAG_LINKS_ADDED; } -- cgit v1.2.3 From 5b6164d3465fcc13b5679c860c452963443172a7 Mon Sep 17 00:00:00 2001 From: Thierry Reding Date: Thu, 3 Dec 2020 18:57:56 +0100 Subject: driver core: Reorder devices on successful probe Device drivers usually depend on the fact that the devices that they control are suspended in the same order that they were probed in. In most cases this is already guaranteed via deferred probe. However, there's one case where this can still break: if a device is instantiated before a dependency (for example if it appears before the dependency in device tree) but gets probed only after the dependency is probed. Instantiation order would cause the dependency to get probed later, in which case probe of the original device would be deferred and the suspend/resume queue would get reordered properly. However, if the dependency is provided by a built-in driver and the device depending on that driver is controlled by a loadable module, which may only get loaded after the root filesystem has become available, we can be faced with a situation where the probe order ends up being different from the suspend/resume order. One example where this happens is on Tegra186, where the ACONNECT is listed very early in device tree (sorted by unit-address) and depends on BPMP (listed very late because it has no unit-address) for power domains and clocks/resets. If the ACONNECT driver is built-in, there is no problem because it will be probed before BPMP, causing a probe deferral and that in turn reorders the suspend/resume queue. However, if built as a module, it will end up being probed after BPMP, and therefore not result in a probe deferral, and therefore the suspend/resume queue will stay in the instantiation order. This in turn causes problems because ACONNECT will be resumed before BPMP, which will result in a hang because the ACONNECT's power domain cannot be powered on as long as the BPMP is still suspended. Fix this by always reordering devices on successful probe. This ensures that the suspend/resume queue is always in probe order and hence meets the natural expectations of drivers vs. their dependencies. Reported-by: Jonathan Hunter Acked-by: Rafael. J. Wysocki Signed-off-by: Thierry Reding Link: https://lore.kernel.org/r/20201203175756.1405564-1-thierry.reding@gmail.com Signed-off-by: Greg Kroah-Hartman --- drivers/base/dd.c | 7 +++++++ 1 file changed, 7 insertions(+) (limited to 'drivers/base') diff --git a/drivers/base/dd.c b/drivers/base/dd.c index 148e81969e04..cfc079e738bb 100644 --- a/drivers/base/dd.c +++ b/drivers/base/dd.c @@ -370,6 +370,13 @@ static void driver_bound(struct device *dev) device_pm_check_callbacks(dev); + /* + * Reorder successfully probed devices to the end of the device list. + * This ensures that suspend/resume order matches probe order, which + * is usually what drivers rely on. + */ + device_pm_move_to_tail(dev); + /* * Make sure the device is no longer in one of the deferred lists and * kick off retrying all pending devices -- cgit v1.2.3 From d475f8ea98a039e51d27f5557dc17333cf8a52f6 Mon Sep 17 00:00:00 2001 From: Thierry Reding Date: Fri, 27 Nov 2020 11:46:30 +0100 Subject: driver core: Fix a couple of typos These were just some minor typos that have crept in recently and are easily fixed. Reviewed-by: Rafael J. Wysocki Signed-off-by: Thierry Reding Link: https://lore.kernel.org/r/20201127104630.1839171-1-thierry.reding@gmail.com Signed-off-by: Greg Kroah-Hartman --- drivers/base/core.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'drivers/base') diff --git a/drivers/base/core.c b/drivers/base/core.c index 63edb8bd9d7d..25e08e5f40bd 100644 --- a/drivers/base/core.c +++ b/drivers/base/core.c @@ -561,7 +561,7 @@ postcore_initcall(devlink_class_init); * with runtime PM. First, setting the DL_FLAG_PM_RUNTIME flag will cause the * runtime PM framework to take the link into account. Second, if the * DL_FLAG_RPM_ACTIVE flag is set in addition to it, the supplier devices will - * be forced into the active metastate and reference-counted upon the creation + * be forced into the active meta state and reference-counted upon the creation * of the link. If DL_FLAG_PM_RUNTIME is not set, DL_FLAG_RPM_ACTIVE will be * ignored. * @@ -584,7 +584,7 @@ postcore_initcall(devlink_class_init); * Also, if DL_FLAG_STATELESS, DL_FLAG_AUTOREMOVE_CONSUMER and * DL_FLAG_AUTOREMOVE_SUPPLIER are not set in @flags (that is, a persistent * managed device link is being added), the DL_FLAG_AUTOPROBE_CONSUMER flag can - * be used to request the driver core to automaticall probe for a consmer + * be used to request the driver core to automatically probe for a consumer * driver after successfully binding a driver to the supplier device. * * The combination of DL_FLAG_STATELESS and one of DL_FLAG_AUTOREMOVE_CONSUMER, -- cgit v1.2.3 From 2c3dc6432f3316e97c10eced6b535046a80f73b0 Mon Sep 17 00:00:00 2001 From: Julian Wiedmann Date: Mon, 23 Nov 2020 12:19:38 +0100 Subject: driver core: make driver_probe_device() static It's only used inside drivers/base/dd.c Signed-off-by: Julian Wiedmann Link: https://lore.kernel.org/r/20201123111938.18968-1-jwi@linux.ibm.com Signed-off-by: Greg Kroah-Hartman --- drivers/base/base.h | 1 - drivers/base/dd.c | 2 +- 2 files changed, 1 insertion(+), 2 deletions(-) (limited to 'drivers/base') diff --git a/drivers/base/base.h b/drivers/base/base.h index 91cfb8405abd..f5600a83124f 100644 --- a/drivers/base/base.h +++ b/drivers/base/base.h @@ -133,7 +133,6 @@ extern void device_release_driver_internal(struct device *dev, struct device *parent); extern void driver_detach(struct device_driver *drv); -extern int driver_probe_device(struct device_driver *drv, struct device *dev); extern void driver_deferred_probe_del(struct device *dev); extern void device_set_deferred_probe_reason(const struct device *dev, struct va_format *vaf); diff --git a/drivers/base/dd.c b/drivers/base/dd.c index cfc079e738bb..2f32f38a11ed 100644 --- a/drivers/base/dd.c +++ b/drivers/base/dd.c @@ -724,7 +724,7 @@ EXPORT_SYMBOL_GPL(wait_for_device_probe); * * If the device has a parent, runtime-resume the parent before driver probing. */ -int driver_probe_device(struct device_driver *drv, struct device *dev) +static int driver_probe_device(struct device_driver *drv, struct device *dev) { int ret = 0; -- cgit v1.2.3 From e21d740a3fe5ad2db7b5f5c2331fe2b713b1edba Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Uwe=20Kleine-K=C3=B6nig?= Date: Thu, 19 Nov 2020 13:46:09 +0100 Subject: driver core: platform: reorder functions MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This way all callbacks and structures used to initialize platform_bus_type are defined just before platform_bus_type and in the same order. Also move platform_drv_probe_fail just before it's only user. Signed-off-by: Uwe Kleine-König Link: https://lore.kernel.org/r/20201119124611.2573057-1-u.kleine-koenig@pengutronix.de Signed-off-by: Greg Kroah-Hartman --- drivers/base/platform.c | 333 ++++++++++++++++++++++++------------------------ 1 file changed, 167 insertions(+), 166 deletions(-) (limited to 'drivers/base') diff --git a/drivers/base/platform.c b/drivers/base/platform.c index 88aef93eb4dd..b36ab5831cb6 100644 --- a/drivers/base/platform.c +++ b/drivers/base/platform.c @@ -772,11 +772,6 @@ out: return ret; } -static int platform_drv_probe_fail(struct device *_dev) -{ - return -ENXIO; -} - static int platform_drv_remove(struct device *_dev) { struct platform_driver *drv = to_platform_driver(_dev->driver); @@ -827,6 +822,11 @@ void platform_driver_unregister(struct platform_driver *drv) } EXPORT_SYMBOL_GPL(platform_driver_unregister); +static int platform_drv_probe_fail(struct device *_dev) +{ + return -ENXIO; +} + /** * __platform_driver_probe - register driver for non-hotpluggable device * @drv: platform driver structure @@ -1017,129 +1017,6 @@ void platform_unregister_drivers(struct platform_driver * const *drivers, } EXPORT_SYMBOL_GPL(platform_unregister_drivers); -/* modalias support enables more hands-off userspace setup: - * (a) environment variable lets new-style hotplug events work once system is - * fully running: "modprobe $MODALIAS" - * (b) sysfs attribute lets new-style coldplug recover from hotplug events - * mishandled before system is fully running: "modprobe $(cat modalias)" - */ -static ssize_t modalias_show(struct device *dev, - struct device_attribute *attr, char *buf) -{ - struct platform_device *pdev = to_platform_device(dev); - int len; - - len = of_device_modalias(dev, buf, PAGE_SIZE); - if (len != -ENODEV) - return len; - - len = acpi_device_modalias(dev, buf, PAGE_SIZE - 1); - if (len != -ENODEV) - return len; - - return sysfs_emit(buf, "platform:%s\n", pdev->name); -} -static DEVICE_ATTR_RO(modalias); - -static ssize_t driver_override_store(struct device *dev, - struct device_attribute *attr, - const char *buf, size_t count) -{ - struct platform_device *pdev = to_platform_device(dev); - char *driver_override, *old, *cp; - - /* We need to keep extra room for a newline */ - if (count >= (PAGE_SIZE - 1)) - return -EINVAL; - - driver_override = kstrndup(buf, count, GFP_KERNEL); - if (!driver_override) - return -ENOMEM; - - cp = strchr(driver_override, '\n'); - if (cp) - *cp = '\0'; - - device_lock(dev); - old = pdev->driver_override; - if (strlen(driver_override)) { - pdev->driver_override = driver_override; - } else { - kfree(driver_override); - pdev->driver_override = NULL; - } - device_unlock(dev); - - kfree(old); - - return count; -} - -static ssize_t driver_override_show(struct device *dev, - struct device_attribute *attr, char *buf) -{ - struct platform_device *pdev = to_platform_device(dev); - ssize_t len; - - device_lock(dev); - len = sysfs_emit(buf, "%s\n", pdev->driver_override); - device_unlock(dev); - - return len; -} -static DEVICE_ATTR_RW(driver_override); - -static ssize_t numa_node_show(struct device *dev, - struct device_attribute *attr, char *buf) -{ - return sysfs_emit(buf, "%d\n", dev_to_node(dev)); -} -static DEVICE_ATTR_RO(numa_node); - -static umode_t platform_dev_attrs_visible(struct kobject *kobj, struct attribute *a, - int n) -{ - struct device *dev = container_of(kobj, typeof(*dev), kobj); - - if (a == &dev_attr_numa_node.attr && - dev_to_node(dev) == NUMA_NO_NODE) - return 0; - - return a->mode; -} - -static struct attribute *platform_dev_attrs[] = { - &dev_attr_modalias.attr, - &dev_attr_numa_node.attr, - &dev_attr_driver_override.attr, - NULL, -}; - -static struct attribute_group platform_dev_group = { - .attrs = platform_dev_attrs, - .is_visible = platform_dev_attrs_visible, -}; -__ATTRIBUTE_GROUPS(platform_dev); - -static int platform_uevent(struct device *dev, struct kobj_uevent_env *env) -{ - struct platform_device *pdev = to_platform_device(dev); - int rc; - - /* Some devices have extra OF data and an OF-style MODALIAS */ - rc = of_device_uevent_modalias(dev, env); - if (rc != -ENODEV) - return rc; - - rc = acpi_device_uevent_modalias(dev, env); - if (rc != -ENODEV) - return rc; - - add_uevent_var(env, "MODALIAS=%s%s", PLATFORM_MODULE_PREFIX, - pdev->name); - return 0; -} - static const struct platform_device_id *platform_match_id( const struct platform_device_id *id, struct platform_device *pdev) @@ -1154,44 +1031,6 @@ static const struct platform_device_id *platform_match_id( return NULL; } -/** - * platform_match - bind platform device to platform driver. - * @dev: device. - * @drv: driver. - * - * Platform device IDs are assumed to be encoded like this: - * "", where is a short description of the type of - * device, like "pci" or "floppy", and is the enumerated - * instance of the device, like '0' or '42'. Driver IDs are simply - * "". So, extract the from the platform_device structure, - * and compare it against the name of the driver. Return whether they match - * or not. - */ -static int platform_match(struct device *dev, struct device_driver *drv) -{ - struct platform_device *pdev = to_platform_device(dev); - struct platform_driver *pdrv = to_platform_driver(drv); - - /* When driver_override is set, only bind to the matching driver */ - if (pdev->driver_override) - return !strcmp(pdev->driver_override, drv->name); - - /* Attempt an OF style match first */ - if (of_driver_match_device(dev, drv)) - return 1; - - /* Then try ACPI style match */ - if (acpi_driver_match_device(dev, drv)) - return 1; - - /* Then try to match against the id table */ - if (pdrv->id_table) - return platform_match_id(pdrv->id_table, pdev) != NULL; - - /* fall-back to driver name match */ - return (strcmp(pdev->name, drv->name) == 0); -} - #ifdef CONFIG_PM_SLEEP static int platform_legacy_suspend(struct device *dev, pm_message_t mesg) @@ -1336,6 +1175,168 @@ int platform_pm_restore(struct device *dev) #endif /* CONFIG_HIBERNATE_CALLBACKS */ +/* modalias support enables more hands-off userspace setup: + * (a) environment variable lets new-style hotplug events work once system is + * fully running: "modprobe $MODALIAS" + * (b) sysfs attribute lets new-style coldplug recover from hotplug events + * mishandled before system is fully running: "modprobe $(cat modalias)" + */ +static ssize_t modalias_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct platform_device *pdev = to_platform_device(dev); + int len; + + len = of_device_modalias(dev, buf, PAGE_SIZE); + if (len != -ENODEV) + return len; + + len = acpi_device_modalias(dev, buf, PAGE_SIZE - 1); + if (len != -ENODEV) + return len; + + return sysfs_emit(buf, "platform:%s\n", pdev->name); +} +static DEVICE_ATTR_RO(modalias); + +static ssize_t numa_node_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + return sysfs_emit(buf, "%d\n", dev_to_node(dev)); +} +static DEVICE_ATTR_RO(numa_node); + +static ssize_t driver_override_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct platform_device *pdev = to_platform_device(dev); + ssize_t len; + + device_lock(dev); + len = sysfs_emit(buf, "%s\n", pdev->driver_override); + device_unlock(dev); + + return len; +} + +static ssize_t driver_override_store(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t count) +{ + struct platform_device *pdev = to_platform_device(dev); + char *driver_override, *old, *cp; + + /* We need to keep extra room for a newline */ + if (count >= (PAGE_SIZE - 1)) + return -EINVAL; + + driver_override = kstrndup(buf, count, GFP_KERNEL); + if (!driver_override) + return -ENOMEM; + + cp = strchr(driver_override, '\n'); + if (cp) + *cp = '\0'; + + device_lock(dev); + old = pdev->driver_override; + if (strlen(driver_override)) { + pdev->driver_override = driver_override; + } else { + kfree(driver_override); + pdev->driver_override = NULL; + } + device_unlock(dev); + + kfree(old); + + return count; +} +static DEVICE_ATTR_RW(driver_override); + +static struct attribute *platform_dev_attrs[] = { + &dev_attr_modalias.attr, + &dev_attr_numa_node.attr, + &dev_attr_driver_override.attr, + NULL, +}; + +static umode_t platform_dev_attrs_visible(struct kobject *kobj, struct attribute *a, + int n) +{ + struct device *dev = container_of(kobj, typeof(*dev), kobj); + + if (a == &dev_attr_numa_node.attr && + dev_to_node(dev) == NUMA_NO_NODE) + return 0; + + return a->mode; +} + +static struct attribute_group platform_dev_group = { + .attrs = platform_dev_attrs, + .is_visible = platform_dev_attrs_visible, +}; +__ATTRIBUTE_GROUPS(platform_dev); + + +/** + * platform_match - bind platform device to platform driver. + * @dev: device. + * @drv: driver. + * + * Platform device IDs are assumed to be encoded like this: + * "", where is a short description of the type of + * device, like "pci" or "floppy", and is the enumerated + * instance of the device, like '0' or '42'. Driver IDs are simply + * "". So, extract the from the platform_device structure, + * and compare it against the name of the driver. Return whether they match + * or not. + */ +static int platform_match(struct device *dev, struct device_driver *drv) +{ + struct platform_device *pdev = to_platform_device(dev); + struct platform_driver *pdrv = to_platform_driver(drv); + + /* When driver_override is set, only bind to the matching driver */ + if (pdev->driver_override) + return !strcmp(pdev->driver_override, drv->name); + + /* Attempt an OF style match first */ + if (of_driver_match_device(dev, drv)) + return 1; + + /* Then try ACPI style match */ + if (acpi_driver_match_device(dev, drv)) + return 1; + + /* Then try to match against the id table */ + if (pdrv->id_table) + return platform_match_id(pdrv->id_table, pdev) != NULL; + + /* fall-back to driver name match */ + return (strcmp(pdev->name, drv->name) == 0); +} + +static int platform_uevent(struct device *dev, struct kobj_uevent_env *env) +{ + struct platform_device *pdev = to_platform_device(dev); + int rc; + + /* Some devices have extra OF data and an OF-style MODALIAS */ + rc = of_device_uevent_modalias(dev, env); + if (rc != -ENODEV) + return rc; + + rc = acpi_device_uevent_modalias(dev, env); + if (rc != -ENODEV) + return rc; + + add_uevent_var(env, "MODALIAS=%s%s", PLATFORM_MODULE_PREFIX, + pdev->name); + return 0; +} + int platform_dma_configure(struct device *dev) { enum dev_dma_attr attr; -- cgit v1.2.3 From 16085668eacdc56c46652d0f3bfef81ecace57de Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Uwe=20Kleine-K=C3=B6nig?= Date: Thu, 19 Nov 2020 13:46:10 +0100 Subject: driver core: platform: change logic implementing platform_driver_probe MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Instead of overwriting the core driver's probe function handle probing devices for drivers loaded by platform_driver_probe() in the platform driver probe function. The intended goal is to not have to change the probe function to simplify converting the platform bus to use bus functions. Signed-off-by: Uwe Kleine-König Link: https://lore.kernel.org/r/20201119124611.2573057-2-u.kleine-koenig@pengutronix.de Signed-off-by: Greg Kroah-Hartman --- drivers/base/platform.c | 18 +++++++++++++++--- 1 file changed, 15 insertions(+), 3 deletions(-) (limited to 'drivers/base') diff --git a/drivers/base/platform.c b/drivers/base/platform.c index b36ab5831cb6..b847f5f8f992 100644 --- a/drivers/base/platform.c +++ b/drivers/base/platform.c @@ -743,12 +743,25 @@ err: } EXPORT_SYMBOL_GPL(platform_device_register_full); +static int platform_probe_fail(struct platform_device *pdev); + static int platform_drv_probe(struct device *_dev) { struct platform_driver *drv = to_platform_driver(_dev->driver); struct platform_device *dev = to_platform_device(_dev); int ret; + /* + * A driver registered using platform_driver_probe() cannot be bound + * again later because the probe function usually lives in __init code + * and so is gone. For these drivers .probe is set to + * platform_probe_fail in __platform_driver_probe(). Don't even + * prepare clocks and PM domains for these to match the traditional + * behaviour. + */ + if (unlikely(drv->probe == platform_probe_fail)) + return -ENXIO; + ret = of_clk_set_defaults(_dev->of_node, false); if (ret < 0) return ret; @@ -822,7 +835,7 @@ void platform_driver_unregister(struct platform_driver *drv) } EXPORT_SYMBOL_GPL(platform_driver_unregister); -static int platform_drv_probe_fail(struct device *_dev) +static int platform_probe_fail(struct platform_device *pdev) { return -ENXIO; } @@ -887,10 +900,9 @@ int __init_or_module __platform_driver_probe(struct platform_driver *drv, * new devices fail. */ spin_lock(&drv->driver.bus->p->klist_drivers.k_lock); - drv->probe = NULL; + drv->probe = platform_probe_fail; if (code == 0 && list_empty(&drv->driver.p->klist_devices.k_list)) retval = -ENODEV; - drv->driver.probe = platform_drv_probe_fail; spin_unlock(&drv->driver.bus->p->klist_drivers.k_lock); if (code != retval) -- cgit v1.2.3 From 9c30921fe7994907e0b3e0637b2c8c0fc4b5171f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Uwe=20Kleine-K=C3=B6nig?= Date: Thu, 19 Nov 2020 13:46:11 +0100 Subject: driver core: platform: use bus_type functions MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This works towards the goal mentioned in 2006 in commit 594c8281f905 ("[PATCH] Add bus_type probe, remove, shutdown methods."). The functions are moved to where the other bus_type functions are defined and renamed to match the already established naming scheme. Signed-off-by: Uwe Kleine-König Link: https://lore.kernel.org/r/20201119124611.2573057-3-u.kleine-koenig@pengutronix.de Signed-off-by: Greg Kroah-Hartman --- drivers/base/platform.c | 132 ++++++++++++++++++++++++------------------------ 1 file changed, 65 insertions(+), 67 deletions(-) (limited to 'drivers/base') diff --git a/drivers/base/platform.c b/drivers/base/platform.c index b847f5f8f992..8ad06daa2eaa 100644 --- a/drivers/base/platform.c +++ b/drivers/base/platform.c @@ -743,70 +743,6 @@ err: } EXPORT_SYMBOL_GPL(platform_device_register_full); -static int platform_probe_fail(struct platform_device *pdev); - -static int platform_drv_probe(struct device *_dev) -{ - struct platform_driver *drv = to_platform_driver(_dev->driver); - struct platform_device *dev = to_platform_device(_dev); - int ret; - - /* - * A driver registered using platform_driver_probe() cannot be bound - * again later because the probe function usually lives in __init code - * and so is gone. For these drivers .probe is set to - * platform_probe_fail in __platform_driver_probe(). Don't even - * prepare clocks and PM domains for these to match the traditional - * behaviour. - */ - if (unlikely(drv->probe == platform_probe_fail)) - return -ENXIO; - - ret = of_clk_set_defaults(_dev->of_node, false); - if (ret < 0) - return ret; - - ret = dev_pm_domain_attach(_dev, true); - if (ret) - goto out; - - if (drv->probe) { - ret = drv->probe(dev); - if (ret) - dev_pm_domain_detach(_dev, true); - } - -out: - if (drv->prevent_deferred_probe && ret == -EPROBE_DEFER) { - dev_warn(_dev, "probe deferral not supported\n"); - ret = -ENXIO; - } - - return ret; -} - -static int platform_drv_remove(struct device *_dev) -{ - struct platform_driver *drv = to_platform_driver(_dev->driver); - struct platform_device *dev = to_platform_device(_dev); - int ret = 0; - - if (drv->remove) - ret = drv->remove(dev); - dev_pm_domain_detach(_dev, true); - - return ret; -} - -static void platform_drv_shutdown(struct device *_dev) -{ - struct platform_driver *drv = to_platform_driver(_dev->driver); - struct platform_device *dev = to_platform_device(_dev); - - if (drv->shutdown) - drv->shutdown(dev); -} - /** * __platform_driver_register - register a driver for platform-level devices * @drv: platform driver structure @@ -817,9 +753,6 @@ int __platform_driver_register(struct platform_driver *drv, { drv->driver.owner = owner; drv->driver.bus = &platform_bus_type; - drv->driver.probe = platform_drv_probe; - drv->driver.remove = platform_drv_remove; - drv->driver.shutdown = platform_drv_shutdown; return driver_register(&drv->driver); } @@ -1349,6 +1282,68 @@ static int platform_uevent(struct device *dev, struct kobj_uevent_env *env) return 0; } +static int platform_probe(struct device *_dev) +{ + struct platform_driver *drv = to_platform_driver(_dev->driver); + struct platform_device *dev = to_platform_device(_dev); + int ret; + + /* + * A driver registered using platform_driver_probe() cannot be bound + * again later because the probe function usually lives in __init code + * and so is gone. For these drivers .probe is set to + * platform_probe_fail in __platform_driver_probe(). Don't even prepare + * clocks and PM domains for these to match the traditional behaviour. + */ + if (unlikely(drv->probe == platform_probe_fail)) + return -ENXIO; + + ret = of_clk_set_defaults(_dev->of_node, false); + if (ret < 0) + return ret; + + ret = dev_pm_domain_attach(_dev, true); + if (ret) + goto out; + + if (drv->probe) { + ret = drv->probe(dev); + if (ret) + dev_pm_domain_detach(_dev, true); + } + +out: + if (drv->prevent_deferred_probe && ret == -EPROBE_DEFER) { + dev_warn(_dev, "probe deferral not supported\n"); + ret = -ENXIO; + } + + return ret; +} + +static int platform_remove(struct device *_dev) +{ + struct platform_driver *drv = to_platform_driver(_dev->driver); + struct platform_device *dev = to_platform_device(_dev); + int ret = 0; + + if (drv->remove) + ret = drv->remove(dev); + dev_pm_domain_detach(_dev, true); + + return ret; +} + +static void platform_shutdown(struct device *_dev) +{ + struct platform_driver *drv = to_platform_driver(_dev->driver); + struct platform_device *dev = to_platform_device(_dev); + + if (drv->shutdown) + drv->shutdown(dev); +} + + int platform_dma_configure(struct device *dev) { enum dev_dma_attr attr; @@ -1375,6 +1370,9 @@ struct bus_type platform_bus_type = { .dev_groups = platform_dev_groups, .match = platform_match, .uevent = platform_uevent, + .probe = platform_probe, + .remove = platform_remove, + .shutdown = platform_shutdown, .dma_configure = platform_dma_configure, .pm = &platform_dev_pm_ops, }; -- cgit v1.2.3 From 0b81144a4ac20f0757b7c965d2fa57de367c7db5 Mon Sep 17 00:00:00 2001 From: Krzysztof Kozlowski Date: Mon, 7 Dec 2020 19:59:52 +0100 Subject: soc: fix comment for freeing soc_dev_attr The soc_dev_attr is stored soc_dev->attr during soc_device_register() so it could be used till the cleanup call: soc_device_unregister(). Therefore this memory should not be freed prior, but after unregistering soc device. Signed-off-by: Krzysztof Kozlowski Link: https://lore.kernel.org/r/20201207185952.261697-1-krzk@kernel.org Signed-off-by: Greg Kroah-Hartman --- drivers/base/soc.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers/base') diff --git a/drivers/base/soc.c b/drivers/base/soc.c index d34609bb7386..0af5363a582c 100644 --- a/drivers/base/soc.c +++ b/drivers/base/soc.c @@ -168,7 +168,7 @@ out1: } EXPORT_SYMBOL_GPL(soc_device_register); -/* Ensure soc_dev->attr is freed prior to calling soc_device_unregister. */ +/* Ensure soc_dev->attr is freed after calling soc_device_unregister. */ void soc_device_unregister(struct soc_device *soc_dev) { device_unregister(&soc_dev->dev); -- cgit v1.2.3 From 0aec2da436623abe19b80b21dd9fc5ec9300a152 Mon Sep 17 00:00:00 2001 From: Andy Shevchenko Date: Wed, 9 Dec 2020 22:36:38 +0200 Subject: driver core: platform: Introduce platform_get_mem_or_io() There are at least few existing users of the proposed API which retrieves either MEM or IO resource from platform device. Make it common to utilize in the existing and new users. Cc: Eric Auger Cc: Alex Williamson Cc: kvm@vger.kernel.org Cc: linux-usb@vger.kernel.org Cc: Peng Hao Cc: Arnd Bergmann Reviewed-by: Cornelia Huck Signed-off-by: Andy Shevchenko Link: https://lore.kernel.org/r/20201209203642.27648-1-andriy.shevchenko@linux.intel.com Signed-off-by: Greg Kroah-Hartman --- drivers/base/platform.c | 15 +++++++++++++++ 1 file changed, 15 insertions(+) (limited to 'drivers/base') diff --git a/drivers/base/platform.c b/drivers/base/platform.c index 8ad06daa2eaa..0358dc3ea3ad 100644 --- a/drivers/base/platform.c +++ b/drivers/base/platform.c @@ -63,6 +63,21 @@ struct resource *platform_get_resource(struct platform_device *dev, } EXPORT_SYMBOL_GPL(platform_get_resource); +struct resource *platform_get_mem_or_io(struct platform_device *dev, + unsigned int num) +{ + u32 i; + + for (i = 0; i < dev->num_resources; i++) { + struct resource *r = &dev->resource[i]; + + if ((resource_type(r) & (IORESOURCE_MEM|IORESOURCE_IO)) && num-- == 0) + return r; + } + return NULL; +} +EXPORT_SYMBOL_GPL(platform_get_mem_or_io); + #ifdef CONFIG_HAS_IOMEM /** * devm_platform_get_and_ioremap_resource - call devm_ioremap_resource() for a -- cgit v1.2.3 From 46e85af0cc53f35584e00bb5db7db6893d0e16e5 Mon Sep 17 00:00:00 2001 From: Dmitry Baryshkov Date: Sun, 13 Dec 2020 02:55:33 +0300 Subject: driver core: platform: don't oops in platform_shutdown() on unbound devices MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit On shutdown the driver core calls the bus' shutdown callback also for unbound devices. A driver's shutdown callback however is only called for devices bound to this driver. Commit 9c30921fe799 ("driver core: platform: use bus_type functions") changed the platform bus from driver callbacks to bus callbacks, so the shutdown function must be prepared to be called without a driver. Add the corresponding check in the shutdown function. Fixes: 9c30921fe799 ("driver core: platform: use bus_type functions") Tested-by: Guenter Roeck Reviewed-by: Uwe Kleine-König Signed-off-by: Dmitry Baryshkov Link: https://lore.kernel.org/r/20201212235533.247537-1-dmitry.baryshkov@linaro.org Signed-off-by: Greg Kroah-Hartman --- drivers/base/platform.c | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) (limited to 'drivers/base') diff --git a/drivers/base/platform.c b/drivers/base/platform.c index 0358dc3ea3ad..e9477e0bbca5 100644 --- a/drivers/base/platform.c +++ b/drivers/base/platform.c @@ -1351,9 +1351,13 @@ static int platform_remove(struct device *_dev) static void platform_shutdown(struct device *_dev) { - struct platform_driver *drv = to_platform_driver(_dev->driver); struct platform_device *dev = to_platform_device(_dev); + struct platform_driver *drv; + + if (!_dev->driver) + return; + drv = to_platform_driver(_dev->driver); if (drv->shutdown) drv->shutdown(dev); } -- cgit v1.2.3