From 4ea5e9deda3f4cbd471d29e6e99106e51be19c86 Mon Sep 17 00:00:00 2001 From: Zijun Hu Date: Fri, 12 Jul 2024 19:52:31 +0800 Subject: driver core: Fix size calculation of symlink name for devlink_(add|remove)_symlinks() devlink_(add|remove)_symlinks() kzalloc() memory to save symlink name for both supplier and consumer, but do not explicitly take into account consumer's prefix "consumer:", so cause disadvantages listed below: 1) it seems wrong for the algorithm to calculate memory size 2) readers maybe need to count characters one by one of both prefix strings to confirm calculated memory size 3) it is relatively easy to introduce new bug if either prefix string is modified in future solved by taking into account consumer's prefix as well. Signed-off-by: Zijun Hu Link: https://lore.kernel.org/r/20240712-devlink_fix-v3-1-fa1c5172ffc7@quicinc.com Signed-off-by: Greg Kroah-Hartman --- drivers/base/core.c | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) (limited to 'drivers/base') diff --git a/drivers/base/core.c b/drivers/base/core.c index 730cae66607c..3e82eaba4932 100644 --- a/drivers/base/core.c +++ b/drivers/base/core.c @@ -572,7 +572,11 @@ static int devlink_add_symlinks(struct device *dev) len = max(strlen(dev_bus_name(sup)) + strlen(dev_name(sup)), strlen(dev_bus_name(con)) + strlen(dev_name(con))); len += strlen(":"); - len += strlen("supplier:") + 1; + /* + * we kzalloc() memory for symlink name of both supplier and + * consumer, so explicitly take into account both prefix. + */ + len += max(strlen("supplier:"), strlen("consumer:")) + 1; buf = kzalloc(len, GFP_KERNEL); if (!buf) return -ENOMEM; @@ -623,7 +627,7 @@ static void devlink_remove_symlinks(struct device *dev) len = max(strlen(dev_bus_name(sup)) + strlen(dev_name(sup)), strlen(dev_bus_name(con)) + strlen(dev_name(con))); len += strlen(":"); - len += strlen("supplier:") + 1; + len += max(strlen("supplier:"), strlen("consumer:")) + 1; buf = kzalloc(len, GFP_KERNEL); if (!buf) { WARN(1, "Unable to properly free device link symlinks!\n"); -- cgit v1.2.3-70-g09d2 From 6d8249ac29bc23260dfa9747eb398ce76012d73c Mon Sep 17 00:00:00 2001 From: Zijun Hu Date: Mon, 22 Jul 2024 22:48:10 +0800 Subject: driver core: Fix error handling in driver API device_rename() For class-device, device_rename() failure maybe cause unexpected link name within its class folder as explained below: /sys/class/.../old_name -> /sys/devices/.../old_name device_rename(..., new_name) and failed /sys/class/.../new_name -> /sys/devices/.../old_name Fixed by undoing renaming link if renaming kobject failed. Fixes: f349cf34731c ("driver core: Implement ns directory support for device classes.") Signed-off-by: Zijun Hu Link: https://lore.kernel.org/r/20240722-device_rename_fix-v2-1-77de1a6c6495@quicinc.com Signed-off-by: Greg Kroah-Hartman --- drivers/base/core.c | 15 ++++++++++----- 1 file changed, 10 insertions(+), 5 deletions(-) (limited to 'drivers/base') diff --git a/drivers/base/core.c b/drivers/base/core.c index 3e82eaba4932..72798133ed63 100644 --- a/drivers/base/core.c +++ b/drivers/base/core.c @@ -4516,9 +4516,11 @@ EXPORT_SYMBOL_GPL(device_destroy); */ int device_rename(struct device *dev, const char *new_name) { + struct subsys_private *sp = NULL; struct kobject *kobj = &dev->kobj; char *old_device_name = NULL; int error; + bool is_link_renamed = false; dev = get_device(dev); if (!dev) @@ -4533,7 +4535,7 @@ int device_rename(struct device *dev, const char *new_name) } if (dev->class) { - struct subsys_private *sp = class_to_subsys(dev->class); + sp = class_to_subsys(dev->class); if (!sp) { error = -EINVAL; @@ -4542,16 +4544,19 @@ int device_rename(struct device *dev, const char *new_name) error = sysfs_rename_link_ns(&sp->subsys.kobj, kobj, old_device_name, new_name, kobject_namespace(kobj)); - subsys_put(sp); if (error) goto out; + + is_link_renamed = true; } error = kobject_rename(kobj, new_name); - if (error) - goto out; - out: + if (error && is_link_renamed) + sysfs_rename_link_ns(&sp->subsys.kobj, kobj, new_name, + old_device_name, kobject_namespace(kobj)); + subsys_put(sp); + put_device(dev); kfree(old_device_name); -- cgit v1.2.3-70-g09d2 From c0fd973c108cdc22a384854bc4b3e288a9717bb2 Mon Sep 17 00:00:00 2001 From: Zijun Hu Date: Wed, 24 Jul 2024 21:54:48 +0800 Subject: driver core: bus: Return -EIO instead of 0 when show/store invalid bus attribute Return -EIO instead of 0 for below erroneous bus attribute operations: - read a bus attribute without show(). - write a bus attribute without store(). Signed-off-by: Zijun Hu Link: https://lore.kernel.org/r/20240724-bus_fix-v2-1-5adbafc698fb@quicinc.com Signed-off-by: Greg Kroah-Hartman --- drivers/base/bus.c | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) (limited to 'drivers/base') diff --git a/drivers/base/bus.c b/drivers/base/bus.c index ffea0728b8b2..e5073fa82b95 100644 --- a/drivers/base/bus.c +++ b/drivers/base/bus.c @@ -152,7 +152,8 @@ static ssize_t bus_attr_show(struct kobject *kobj, struct attribute *attr, { struct bus_attribute *bus_attr = to_bus_attr(attr); struct subsys_private *subsys_priv = to_subsys_private(kobj); - ssize_t ret = 0; + /* return -EIO for reading a bus attribute without show() */ + ssize_t ret = -EIO; if (bus_attr->show) ret = bus_attr->show(subsys_priv->bus, buf); @@ -164,7 +165,8 @@ static ssize_t bus_attr_store(struct kobject *kobj, struct attribute *attr, { struct bus_attribute *bus_attr = to_bus_attr(attr); struct subsys_private *subsys_priv = to_subsys_private(kobj); - ssize_t ret = 0; + /* return -EIO for writing a bus attribute without store() */ + ssize_t ret = -EIO; if (bus_attr->store) ret = bus_attr->store(subsys_priv->bus, buf, count); -- cgit v1.2.3-70-g09d2 From 0314647dec70bf0e856303dc70d00e9f1ba568ba Mon Sep 17 00:00:00 2001 From: Zijun Hu Date: Thu, 25 Jul 2024 23:40:55 +0800 Subject: driver core: Remove unused parameter for virtual_device_parent() Function struct kobject *virtual_device_parent(struct device *dev) does not use its parameter @dev, and the kobject returned also has nothing deal with specific device, so remove the unused parameter. Signed-off-by: Zijun Hu Link: https://lore.kernel.org/r/20240725-virtual_kobj_fix-v1-1-36335cae4544@quicinc.com Signed-off-by: Greg Kroah-Hartman --- drivers/base/base.h | 2 +- drivers/base/bus.c | 2 +- drivers/base/core.c | 4 ++-- 3 files changed, 4 insertions(+), 4 deletions(-) (limited to 'drivers/base') diff --git a/drivers/base/base.h b/drivers/base/base.h index 0b53593372d7..8cf04a557bdb 100644 --- a/drivers/base/base.h +++ b/drivers/base/base.h @@ -145,7 +145,7 @@ void auxiliary_bus_init(void); static inline void auxiliary_bus_init(void) { } #endif -struct kobject *virtual_device_parent(struct device *dev); +struct kobject *virtual_device_parent(void); int bus_add_device(struct device *dev); void bus_probe_device(struct device *dev); diff --git a/drivers/base/bus.c b/drivers/base/bus.c index e5073fa82b95..8bf04d5ef51d 100644 --- a/drivers/base/bus.c +++ b/drivers/base/bus.c @@ -1296,7 +1296,7 @@ int subsys_virtual_register(const struct bus_type *subsys, { struct kobject *virtual_dir; - virtual_dir = virtual_device_parent(NULL); + virtual_dir = virtual_device_parent(); if (!virtual_dir) return -ENOMEM; diff --git a/drivers/base/core.c b/drivers/base/core.c index 72798133ed63..1688e76cb64b 100644 --- a/drivers/base/core.c +++ b/drivers/base/core.c @@ -3171,7 +3171,7 @@ void device_initialize(struct device *dev) } EXPORT_SYMBOL_GPL(device_initialize); -struct kobject *virtual_device_parent(struct device *dev) +struct kobject *virtual_device_parent(void) { static struct kobject *virtual_dir = NULL; @@ -3249,7 +3249,7 @@ static struct kobject *get_device_parent(struct device *dev, * in a "glue" directory to prevent namespace collisions. */ if (parent == NULL) - parent_kobj = virtual_device_parent(dev); + parent_kobj = virtual_device_parent(); else if (parent->class && !dev->class->ns_type) { subsys_put(sp); return &parent->kobj; -- cgit v1.2.3-70-g09d2 From 2bdf3b83515ead3b3fdf93610e4a3bb9a89bc852 Mon Sep 17 00:00:00 2001 From: Zijun Hu Date: Sat, 27 Jul 2024 14:08:34 +0800 Subject: driver core: bus: Add simple error handling for buses_init() Add simple error handling for buses_init() since it is easy to do. Signed-off-by: Zijun Hu Link: https://lore.kernel.org/r/20240727-buses_init-v1-1-e863295a2c0e@quicinc.com Signed-off-by: Greg Kroah-Hartman --- drivers/base/bus.c | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) (limited to 'drivers/base') diff --git a/drivers/base/bus.c b/drivers/base/bus.c index 8bf04d5ef51d..5bb1dca96242 100644 --- a/drivers/base/bus.c +++ b/drivers/base/bus.c @@ -1387,8 +1387,13 @@ int __init buses_init(void) return -ENOMEM; system_kset = kset_create_and_add("system", NULL, &devices_kset->kobj); - if (!system_kset) + if (!system_kset) { + /* Do error handling here as devices_init() do */ + kset_unregister(bus_kset); + bus_kset = NULL; + pr_err("%s: failed to create and add kset 'bus'\n", __func__); return -ENOMEM; + } return 0; } -- cgit v1.2.3-70-g09d2 From bfa54a793ba77ef696755b66f3ac4ed00c7d1248 Mon Sep 17 00:00:00 2001 From: Zijun Hu Date: Sat, 27 Jul 2024 16:34:01 +0800 Subject: driver core: bus: Fix double free in driver API bus_register() For bus_register(), any error which happens after kset_register() will cause that @priv are freed twice, fixed by setting @priv with NULL after the first free. Signed-off-by: Zijun Hu Link: https://lore.kernel.org/r/20240727-bus_register_fix-v1-1-fed8dd0dba7a@quicinc.com Signed-off-by: Greg Kroah-Hartman --- drivers/base/bus.c | 2 ++ 1 file changed, 2 insertions(+) (limited to 'drivers/base') diff --git a/drivers/base/bus.c b/drivers/base/bus.c index 5bb1dca96242..abf090ace833 100644 --- a/drivers/base/bus.c +++ b/drivers/base/bus.c @@ -922,6 +922,8 @@ bus_devices_fail: bus_remove_file(bus, &bus_attr_uevent); bus_uevent_fail: kset_unregister(&priv->subsys); + /* Above kset_unregister() will kfree @priv */ + priv = NULL; out: kfree(priv); return retval; -- cgit v1.2.3-70-g09d2 From 18ec12c97b39ff6aa15beb8d2b25d15cd44b87d8 Mon Sep 17 00:00:00 2001 From: Jinjie Ruan Date: Mon, 12 Aug 2024 16:06:58 +0800 Subject: driver core: Fix a potential null-ptr-deref in module_add_driver() Inject fault while probing of-fpga-region, if kasprintf() fails in module_add_driver(), the second sysfs_remove_link() in exit path will cause null-ptr-deref as below because kernfs_name_hash() will call strlen() with NULL driver_name. Fix it by releasing resources based on the exit path sequence. KASAN: null-ptr-deref in range [0x0000000000000000-0x0000000000000007] Mem abort info: ESR = 0x0000000096000005 EC = 0x25: DABT (current EL), IL = 32 bits SET = 0, FnV = 0 EA = 0, S1PTW = 0 FSC = 0x05: level 1 translation fault Data abort info: ISV = 0, ISS = 0x00000005, ISS2 = 0x00000000 CM = 0, WnR = 0, TnD = 0, TagAccess = 0 GCS = 0, Overlay = 0, DirtyBit = 0, Xs = 0 [dfffffc000000000] address between user and kernel address ranges Internal error: Oops: 0000000096000005 [#1] PREEMPT SMP Dumping ftrace buffer: (ftrace buffer empty) Modules linked in: of_fpga_region(+) fpga_region fpga_bridge cfg80211 rfkill 8021q garp mrp stp llc ipv6 [last unloaded: of_fpga_region] CPU: 2 UID: 0 PID: 2036 Comm: modprobe Not tainted 6.11.0-rc2-g6a0e38264012 #295 Hardware name: linux,dummy-virt (DT) pstate: 60000005 (nZCv daif -PAN -UAO -TCO -DIT -SSBS BTYPE=--) pc : strlen+0x24/0xb0 lr : kernfs_name_hash+0x1c/0xc4 sp : ffffffc081f97380 x29: ffffffc081f97380 x28: ffffffc081f97b90 x27: ffffff80c821c2a0 x26: ffffffedac0be418 x25: 0000000000000000 x24: ffffff80c09d2000 x23: 0000000000000000 x22: 0000000000000000 x21: 0000000000000000 x20: 0000000000000000 x19: 0000000000000000 x18: 0000000000001840 x17: 0000000000000000 x16: 0000000000000000 x15: 1ffffff8103f2e42 x14: 00000000f1f1f1f1 x13: 0000000000000004 x12: ffffffb01812d61d x11: 1ffffff01812d61c x10: ffffffb01812d61c x9 : dfffffc000000000 x8 : 0000004fe7ed29e4 x7 : ffffff80c096b0e7 x6 : 0000000000000001 x5 : ffffff80c096b0e0 x4 : 1ffffffdb990efa2 x3 : 0000000000000000 x2 : 0000000000000000 x1 : dfffffc000000000 x0 : 0000000000000000 Call trace: strlen+0x24/0xb0 kernfs_name_hash+0x1c/0xc4 kernfs_find_ns+0x118/0x2e8 kernfs_remove_by_name_ns+0x80/0x100 sysfs_remove_link+0x74/0xa8 module_add_driver+0x278/0x394 bus_add_driver+0x1f0/0x43c driver_register+0xf4/0x3c0 __platform_driver_register+0x60/0x88 of_fpga_region_init+0x20/0x1000 [of_fpga_region] do_one_initcall+0x110/0x788 do_init_module+0x1dc/0x5c8 load_module+0x3c38/0x4cac init_module_from_file+0xd4/0x128 idempotent_init_module+0x2cc/0x528 __arm64_sys_finit_module+0xac/0x100 invoke_syscall+0x6c/0x258 el0_svc_common.constprop.0+0x160/0x22c do_el0_svc+0x44/0x5c el0_svc+0x48/0xb8 el0t_64_sync_handler+0x13c/0x158 el0t_64_sync+0x190/0x194 Code: f2fbffe1 a90157f4 12000802 aa0003f5 (38e16861) ---[ end trace 0000000000000000 ]--- Kernel panic - not syncing: Oops: Fatal exception Fixes: 85d2b0aa1703 ("module: don't ignore sysfs_create_link() failures") Signed-off-by: Jinjie Ruan Link: https://lore.kernel.org/r/20240812080658.2791982-1-ruanjinjie@huawei.com Signed-off-by: Greg Kroah-Hartman --- drivers/base/module.c | 14 +++++++++----- 1 file changed, 9 insertions(+), 5 deletions(-) (limited to 'drivers/base') diff --git a/drivers/base/module.c b/drivers/base/module.c index f742ad2a21da..c4eaa1158d54 100644 --- a/drivers/base/module.c +++ b/drivers/base/module.c @@ -66,27 +66,31 @@ int module_add_driver(struct module *mod, const struct device_driver *drv) driver_name = make_driver_name(drv); if (!driver_name) { ret = -ENOMEM; - goto out; + goto out_remove_kobj; } module_create_drivers_dir(mk); if (!mk->drivers_dir) { ret = -EINVAL; - goto out; + goto out_free_driver_name; } ret = sysfs_create_link(mk->drivers_dir, &drv->p->kobj, driver_name); if (ret) - goto out; + goto out_remove_drivers_dir; kfree(driver_name); return 0; -out: - sysfs_remove_link(&drv->p->kobj, "module"); + +out_remove_drivers_dir: sysfs_remove_link(mk->drivers_dir, driver_name); + +out_free_driver_name: kfree(driver_name); +out_remove_kobj: + sysfs_remove_link(&drv->p->kobj, "module"); return ret; } -- cgit v1.2.3-70-g09d2 From d11f2a1ab8f6dcfda827f8f0c47d8483b8669165 Mon Sep 17 00:00:00 2001 From: Andy Shevchenko Date: Wed, 21 Aug 2024 18:48:19 +0300 Subject: driver core: Sort headers Sort the headers in alphabetic order in order to ease the maintenance for this part. Signed-off-by: Andy Shevchenko Link: https://lore.kernel.org/r/20240821154839.604259-2-andriy.shevchenko@linux.intel.com Signed-off-by: Greg Kroah-Hartman --- drivers/base/core.c | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) (limited to 'drivers/base') diff --git a/drivers/base/core.c b/drivers/base/core.c index 6d3897492285..54f10a8325f8 100644 --- a/drivers/base/core.c +++ b/drivers/base/core.c @@ -9,29 +9,29 @@ */ #include +#include #include #include +#include /* for dma_default_coherent */ #include #include #include +#include #include #include -#include -#include +#include +#include #include #include #include -#include -#include #include -#include #include -#include #include +#include +#include #include #include #include -#include /* for dma_default_coherent */ #include "base.h" #include "physical_location.h" -- cgit v1.2.3-70-g09d2 From a355a4655ec660fc68b60b909776290cb88e1124 Mon Sep 17 00:00:00 2001 From: Andy Shevchenko Date: Wed, 21 Aug 2024 18:48:20 +0300 Subject: driver core: Use kasprintf() instead of fixed buffer formatting Improve readability and maintainability by replacing a hardcoded string allocation and formatting by the use of the kasprintf() helper. Signed-off-by: Andy Shevchenko Link: https://lore.kernel.org/r/20240821154839.604259-3-andriy.shevchenko@linux.intel.com Signed-off-by: Greg Kroah-Hartman --- drivers/base/core.c | 70 ++++++++++++++++++++++++----------------------------- 1 file changed, 32 insertions(+), 38 deletions(-) (limited to 'drivers/base') diff --git a/drivers/base/core.c b/drivers/base/core.c index 54f10a8325f8..67f3da47f63c 100644 --- a/drivers/base/core.c +++ b/drivers/base/core.c @@ -10,6 +10,7 @@ #include #include +#include #include #include #include /* for dma_default_coherent */ @@ -563,24 +564,11 @@ static struct class devlink_class = { static int devlink_add_symlinks(struct device *dev) { + char *buf_con __free(kfree) = NULL, *buf_sup __free(kfree) = NULL; int ret; - size_t len; struct device_link *link = to_devlink(dev); struct device *sup = link->supplier; struct device *con = link->consumer; - char *buf; - - len = max(strlen(dev_bus_name(sup)) + strlen(dev_name(sup)), - strlen(dev_bus_name(con)) + strlen(dev_name(con))); - len += strlen(":"); - /* - * we kzalloc() memory for symlink name of both supplier and - * consumer, so explicitly take into account both prefix. - */ - len += max(strlen("supplier:"), strlen("consumer:")) + 1; - buf = kzalloc(len, GFP_KERNEL); - if (!buf) - return -ENOMEM; ret = sysfs_create_link(&link->link_dev.kobj, &sup->kobj, "supplier"); if (ret) @@ -590,58 +578,64 @@ static int devlink_add_symlinks(struct device *dev) if (ret) goto err_con; - snprintf(buf, len, "consumer:%s:%s", dev_bus_name(con), dev_name(con)); - ret = sysfs_create_link(&sup->kobj, &link->link_dev.kobj, buf); + buf_con = kasprintf(GFP_KERNEL, "consumer:%s:%s", dev_bus_name(con), dev_name(con)); + if (!buf_con) { + ret = -ENOMEM; + goto err_con_dev; + } + + ret = sysfs_create_link(&sup->kobj, &link->link_dev.kobj, buf_con); if (ret) goto err_con_dev; - snprintf(buf, len, "supplier:%s:%s", dev_bus_name(sup), dev_name(sup)); - ret = sysfs_create_link(&con->kobj, &link->link_dev.kobj, buf); + buf_sup = kasprintf(GFP_KERNEL, "supplier:%s:%s", dev_bus_name(sup), dev_name(sup)); + if (!buf_sup) { + ret = -ENOMEM; + goto err_sup_dev; + } + + ret = sysfs_create_link(&con->kobj, &link->link_dev.kobj, buf_sup); if (ret) goto err_sup_dev; goto out; err_sup_dev: - snprintf(buf, len, "consumer:%s:%s", dev_bus_name(con), dev_name(con)); - sysfs_remove_link(&sup->kobj, buf); + sysfs_remove_link(&sup->kobj, buf_con); err_con_dev: sysfs_remove_link(&link->link_dev.kobj, "consumer"); err_con: sysfs_remove_link(&link->link_dev.kobj, "supplier"); out: - kfree(buf); return ret; } static void devlink_remove_symlinks(struct device *dev) { + char *buf_con __free(kfree) = NULL, *buf_sup __free(kfree) = NULL; struct device_link *link = to_devlink(dev); - size_t len; struct device *sup = link->supplier; struct device *con = link->consumer; - char *buf; sysfs_remove_link(&link->link_dev.kobj, "consumer"); sysfs_remove_link(&link->link_dev.kobj, "supplier"); - len = max(strlen(dev_bus_name(sup)) + strlen(dev_name(sup)), - strlen(dev_bus_name(con)) + strlen(dev_name(con))); - len += strlen(":"); - len += max(strlen("supplier:"), strlen("consumer:")) + 1; - buf = kzalloc(len, GFP_KERNEL); - if (!buf) { - WARN(1, "Unable to properly free device link symlinks!\n"); - return; - } - if (device_is_registered(con)) { - snprintf(buf, len, "supplier:%s:%s", dev_bus_name(sup), dev_name(sup)); - sysfs_remove_link(&con->kobj, buf); + buf_sup = kasprintf(GFP_KERNEL, "supplier:%s:%s", dev_bus_name(sup), dev_name(sup)); + if (!buf_sup) + goto out; + sysfs_remove_link(&con->kobj, buf_sup); } - snprintf(buf, len, "consumer:%s:%s", dev_bus_name(con), dev_name(con)); - sysfs_remove_link(&sup->kobj, buf); - kfree(buf); + + buf_con = kasprintf(GFP_KERNEL, "consumer:%s:%s", dev_bus_name(con), dev_name(con)); + if (!buf_con) + goto out; + sysfs_remove_link(&sup->kobj, buf_con); + + return; + +out: + WARN(1, "Unable to properly free device link symlinks!\n"); } static struct class_interface devlink_class_intf = { -- cgit v1.2.3-70-g09d2 From adcae2048df15ae9647d792b09f8742d6ebe459b Mon Sep 17 00:00:00 2001 From: Andy Shevchenko Date: Wed, 21 Aug 2024 18:48:21 +0300 Subject: driver core: Use guards for simple mutex locks Guards can help to make the code more readable. So use it wherever they do so. Signed-off-by: Andy Shevchenko Link: https://lore.kernel.org/r/20240821154839.604259-4-andriy.shevchenko@linux.intel.com Signed-off-by: Greg Kroah-Hartman --- drivers/base/core.c | 50 ++++++++++++++++++++++---------------------------- 1 file changed, 22 insertions(+), 28 deletions(-) (limited to 'drivers/base') diff --git a/drivers/base/core.c b/drivers/base/core.c index 67f3da47f63c..271a88a6311e 100644 --- a/drivers/base/core.c +++ b/drivers/base/core.c @@ -98,12 +98,9 @@ static int __fwnode_link_add(struct fwnode_handle *con, int fwnode_link_add(struct fwnode_handle *con, struct fwnode_handle *sup, u8 flags) { - int ret; + guard(mutex)(&fwnode_link_lock); - mutex_lock(&fwnode_link_lock); - ret = __fwnode_link_add(con, sup, flags); - mutex_unlock(&fwnode_link_lock); - return ret; + return __fwnode_link_add(con, sup, flags); } /** @@ -144,10 +141,10 @@ static void fwnode_links_purge_suppliers(struct fwnode_handle *fwnode) { struct fwnode_link *link, *tmp; - mutex_lock(&fwnode_link_lock); + guard(mutex)(&fwnode_link_lock); + list_for_each_entry_safe(link, tmp, &fwnode->suppliers, c_hook) __fwnode_link_del(link); - mutex_unlock(&fwnode_link_lock); } /** @@ -160,10 +157,10 @@ static void fwnode_links_purge_consumers(struct fwnode_handle *fwnode) { struct fwnode_link *link, *tmp; - mutex_lock(&fwnode_link_lock); + guard(mutex)(&fwnode_link_lock); + list_for_each_entry_safe(link, tmp, &fwnode->consumers, s_hook) __fwnode_link_del(link); - mutex_unlock(&fwnode_link_lock); } /** @@ -1059,20 +1056,16 @@ int device_links_check_suppliers(struct device *dev) * Device waiting for supplier to become available is not allowed to * probe. */ - mutex_lock(&fwnode_link_lock); - sup_fw = fwnode_links_check_suppliers(dev->fwnode); - if (sup_fw) { - if (!dev_is_best_effort(dev)) { - fwnode_ret = -EPROBE_DEFER; - dev_err_probe(dev, -EPROBE_DEFER, - "wait for supplier %pfwf\n", sup_fw); - } else { - fwnode_ret = -EAGAIN; + scoped_guard(mutex, &fwnode_link_lock) { + sup_fw = fwnode_links_check_suppliers(dev->fwnode); + if (sup_fw) { + if (dev_is_best_effort(dev)) + fwnode_ret = -EAGAIN; + else + return dev_err_probe(dev, -EPROBE_DEFER, + "wait for supplier %pfwf\n", sup_fw); } } - mutex_unlock(&fwnode_link_lock); - if (fwnode_ret == -EPROBE_DEFER) - return fwnode_ret; device_links_write_lock(); @@ -1247,9 +1240,8 @@ static ssize_t waiting_for_supplier_show(struct device *dev, bool val; device_lock(dev); - mutex_lock(&fwnode_link_lock); - val = !!fwnode_links_check_suppliers(dev->fwnode); - mutex_unlock(&fwnode_link_lock); + scoped_guard(mutex, &fwnode_link_lock) + val = !!fwnode_links_check_suppliers(dev->fwnode); device_unlock(dev); return sysfs_emit(buf, "%u\n", val); } @@ -1322,13 +1314,15 @@ void device_links_driver_bound(struct device *dev) */ if (dev->fwnode && dev->fwnode->dev == dev) { struct fwnode_handle *child; + fwnode_links_purge_suppliers(dev->fwnode); - mutex_lock(&fwnode_link_lock); + + guard(mutex)(&fwnode_link_lock); + fwnode_for_each_available_child_node(dev->fwnode, child) __fw_devlink_pickup_dangling_consumers(child, dev->fwnode); __fw_devlink_link_to_consumers(dev); - mutex_unlock(&fwnode_link_lock); } device_remove_file(dev, &dev_attr_waiting_for_supplier); @@ -2337,10 +2331,10 @@ static void fw_devlink_link_device(struct device *dev) fw_devlink_parse_fwtree(fwnode); - mutex_lock(&fwnode_link_lock); + guard(mutex)(&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. */ -- cgit v1.2.3-70-g09d2 From d1363030d824b244744399ddd85a52cf615dee4c Mon Sep 17 00:00:00 2001 From: Andy Shevchenko Date: Wed, 21 Aug 2024 18:48:22 +0300 Subject: driver core: Make use of returned value of dev_err_probe() Instead of assigning ret explicitly to the same value that is supplied to dev_err_probe(), make use of returned value of the latter. Signed-off-by: Andy Shevchenko Link: https://lore.kernel.org/r/20240821154839.604259-5-andriy.shevchenko@linux.intel.com Signed-off-by: Greg Kroah-Hartman --- drivers/base/core.c | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) (limited to 'drivers/base') diff --git a/drivers/base/core.c b/drivers/base/core.c index 271a88a6311e..980c08901cd0 100644 --- a/drivers/base/core.c +++ b/drivers/base/core.c @@ -1084,10 +1084,8 @@ int device_links_check_suppliers(struct device *dev) } device_links_missing_supplier(dev); - dev_err_probe(dev, -EPROBE_DEFER, - "supplier %s not ready\n", - dev_name(link->supplier)); - ret = -EPROBE_DEFER; + ret = dev_err_probe(dev, -EPROBE_DEFER, + "supplier %s not ready\n", dev_name(link->supplier)); break; } WRITE_ONCE(link->status, DL_STATE_CONSUMER_PROBE); -- cgit v1.2.3-70-g09d2 From 888f67e621dda5c2804a696524e28d0ca4cf0a80 Mon Sep 17 00:00:00 2001 From: Andy Shevchenko Date: Wed, 21 Aug 2024 18:48:23 +0300 Subject: driver core: Use 2-argument strscpy() Use 2-argument strscpy(), which is not only shorter but also provides an additional check that destination buffer is an array. Signed-off-by: Andy Shevchenko Link: https://lore.kernel.org/r/20240821154839.604259-6-andriy.shevchenko@linux.intel.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 980c08901cd0..4bc8b88d697e 100644 --- a/drivers/base/core.c +++ b/drivers/base/core.c @@ -4867,7 +4867,7 @@ set_dev_info(const struct device *dev, struct dev_printk_info *dev_info) else return; - strscpy(dev_info->subsystem, subsys, sizeof(dev_info->subsystem)); + strscpy(dev_info->subsystem, subsys); /* * Add device identifier DEVICE=: -- cgit v1.2.3-70-g09d2 From f0e5311aa8022107d63c54e2f03684ec097d1394 Mon Sep 17 00:00:00 2001 From: Jann Horn Date: Wed, 28 Aug 2024 01:45:48 +0200 Subject: firmware_loader: Block path traversal Most firmware names are hardcoded strings, or are constructed from fairly constrained format strings where the dynamic parts are just some hex numbers or such. However, there are a couple codepaths in the kernel where firmware file names contain string components that are passed through from a device or semi-privileged userspace; the ones I could find (not counting interfaces that require root privileges) are: - lpfc_sli4_request_firmware_update() seems to construct the firmware filename from "ModelName", a string that was previously parsed out of some descriptor ("Vital Product Data") in lpfc_fill_vpd() - nfp_net_fw_find() seems to construct a firmware filename from a model name coming from nfp_hwinfo_lookup(pf->hwinfo, "nffw.partno"), which I think parses some descriptor that was read from the device. (But this case likely isn't exploitable because the format string looks like "netronome/nic_%s", and there shouldn't be any *folders* starting with "netronome/nic_". The previous case was different because there, the "%s" is *at the start* of the format string.) - module_flash_fw_schedule() is reachable from the ETHTOOL_MSG_MODULE_FW_FLASH_ACT netlink command, which is marked as GENL_UNS_ADMIN_PERM (meaning CAP_NET_ADMIN inside a user namespace is enough to pass the privilege check), and takes a userspace-provided firmware name. (But I think to reach this case, you need to have CAP_NET_ADMIN over a network namespace that a special kind of ethernet device is mapped into, so I think this is not a viable attack path in practice.) Fix it by rejecting any firmware names containing ".." path components. For what it's worth, I went looking and haven't found any USB device drivers that use the firmware loader dangerously. Cc: stable@vger.kernel.org Reviewed-by: Danilo Krummrich Fixes: abb139e75c2c ("firmware: teach the kernel to load firmware files directly from the filesystem") Signed-off-by: Jann Horn Acked-by: Luis Chamberlain Link: https://lore.kernel.org/r/20240828-firmware-traversal-v3-1-c76529c63b5f@google.com Signed-off-by: Greg Kroah-Hartman --- drivers/base/firmware_loader/main.c | 30 ++++++++++++++++++++++++++++++ 1 file changed, 30 insertions(+) (limited to 'drivers/base') diff --git a/drivers/base/firmware_loader/main.c b/drivers/base/firmware_loader/main.c index a03ee4b11134..324a9a3c087a 100644 --- a/drivers/base/firmware_loader/main.c +++ b/drivers/base/firmware_loader/main.c @@ -849,6 +849,26 @@ static void fw_log_firmware_info(const struct firmware *fw, const char *name, {} #endif +/* + * Reject firmware file names with ".." path components. + * There are drivers that construct firmware file names from device-supplied + * strings, and we don't want some device to be able to tell us "I would like to + * be sent my firmware from ../../../etc/shadow, please". + * + * Search for ".." surrounded by either '/' or start/end of string. + * + * This intentionally only looks at the firmware name, not at the firmware base + * directory or at symlink contents. + */ +static bool name_contains_dotdot(const char *name) +{ + size_t name_len = strlen(name); + + return strcmp(name, "..") == 0 || strncmp(name, "../", 3) == 0 || + strstr(name, "/../") != NULL || + (name_len >= 3 && strcmp(name+name_len-3, "/..") == 0); +} + /* called from request_firmware() and request_firmware_work_func() */ static int _request_firmware(const struct firmware **firmware_p, const char *name, @@ -869,6 +889,14 @@ _request_firmware(const struct firmware **firmware_p, const char *name, goto out; } + if (name_contains_dotdot(name)) { + dev_warn(device, + "Firmware load for '%s' refused, path contains '..' component\n", + name); + ret = -EINVAL; + goto out; + } + ret = _request_firmware_prepare(&fw, name, device, buf, size, offset, opt_flags); if (ret <= 0) /* error or already assigned */ @@ -946,6 +974,8 @@ out: * @name will be used as $FIRMWARE in the uevent environment and * should be distinctive enough not to be confused with any other * firmware image for this or any other device. + * It must not contain any ".." path components - "foo/bar..bin" is + * allowed, but "foo/../bar.bin" is not. * * Caller must hold the reference count of @device. * -- cgit v1.2.3-70-g09d2 From b45ed06f46737f8c2ee65698f4305409f2386674 Mon Sep 17 00:00:00 2001 From: Zijun Hu Date: Tue, 13 Aug 2024 22:19:32 +0800 Subject: drivers/base: Introduce device_match_t for device finding APIs There are several drivers/base APIs for finding a specific device, and they currently use the following good type for the @match parameter: int (*match)(struct device *dev, const void *data) Since these operations do not modify the caller-provided @*data, this type is worthy of a dedicated typedef: typedef int (*device_match_t)(struct device *dev, const void *data) Advantages of using device_match_t: - Shorter API declarations and definitions - Prevent further APIs from using a bad type for @match So introduce device_match_t and apply it to the existing (bus|class|driver|auxiliary)_find_device() APIs. Signed-off-by: Zijun Hu Link: https://lore.kernel.org/r/20240813-dev_match_api-v3-1-6c6878a99b9f@quicinc.com Signed-off-by: Greg Kroah-Hartman --- drivers/base/auxiliary.c | 2 +- drivers/base/bus.c | 2 +- drivers/base/class.c | 3 +-- drivers/base/driver.c | 2 +- include/linux/auxiliary_bus.h | 2 +- include/linux/device/bus.h | 6 ++++-- include/linux/device/class.h | 2 +- include/linux/device/driver.h | 2 +- 8 files changed, 11 insertions(+), 10 deletions(-) (limited to 'drivers/base') diff --git a/drivers/base/auxiliary.c b/drivers/base/auxiliary.c index 54b92839e05c..7823888af4f6 100644 --- a/drivers/base/auxiliary.c +++ b/drivers/base/auxiliary.c @@ -352,7 +352,7 @@ EXPORT_SYMBOL_GPL(__auxiliary_device_add); */ struct auxiliary_device *auxiliary_find_device(struct device *start, const void *data, - int (*match)(struct device *dev, const void *data)) + device_match_t match) { struct device *dev; diff --git a/drivers/base/bus.c b/drivers/base/bus.c index abf090ace833..657c93c38b0d 100644 --- a/drivers/base/bus.c +++ b/drivers/base/bus.c @@ -391,7 +391,7 @@ EXPORT_SYMBOL_GPL(bus_for_each_dev); */ struct device *bus_find_device(const struct bus_type *bus, struct device *start, const void *data, - int (*match)(struct device *dev, const void *data)) + device_match_t match) { struct subsys_private *sp = bus_to_subsys(bus); struct klist_iter i; diff --git a/drivers/base/class.c b/drivers/base/class.c index 7b38fdf8e1d7..ae22fa992c04 100644 --- a/drivers/base/class.c +++ b/drivers/base/class.c @@ -433,8 +433,7 @@ EXPORT_SYMBOL_GPL(class_for_each_device); * code. There's no locking restriction. */ struct device *class_find_device(const struct class *class, const struct device *start, - const void *data, - int (*match)(struct device *, const void *)) + const void *data, device_match_t match) { struct subsys_private *sp = class_to_subsys(class); struct class_dev_iter iter; diff --git a/drivers/base/driver.c b/drivers/base/driver.c index 88c6fd1f1992..b4eb5b89c4ee 100644 --- a/drivers/base/driver.c +++ b/drivers/base/driver.c @@ -150,7 +150,7 @@ EXPORT_SYMBOL_GPL(driver_for_each_device); */ struct device *driver_find_device(const struct device_driver *drv, struct device *start, const void *data, - int (*match)(struct device *dev, const void *data)) + device_match_t match) { struct klist_iter i; struct device *dev; diff --git a/include/linux/auxiliary_bus.h b/include/linux/auxiliary_bus.h index 662b8ae54b6a..31762324bcc9 100644 --- a/include/linux/auxiliary_bus.h +++ b/include/linux/auxiliary_bus.h @@ -271,6 +271,6 @@ void auxiliary_driver_unregister(struct auxiliary_driver *auxdrv); struct auxiliary_device *auxiliary_find_device(struct device *start, const void *data, - int (*match)(struct device *dev, const void *data)); + device_match_t match); #endif /* _AUXILIARY_BUS_H_ */ diff --git a/include/linux/device/bus.h b/include/linux/device/bus.h index 807831d6bf0f..cdc4757217f9 100644 --- a/include/linux/device/bus.h +++ b/include/linux/device/bus.h @@ -126,6 +126,9 @@ struct bus_attribute { int __must_check bus_create_file(const struct bus_type *bus, struct bus_attribute *attr); void bus_remove_file(const struct bus_type *bus, struct bus_attribute *attr); +/* Matching function type for drivers/base APIs to find a specific device */ +typedef int (*device_match_t)(struct device *dev, const void *data); + /* Generic device matching functions that all busses can use to match with */ int device_match_name(struct device *dev, const void *name); int device_match_of_node(struct device *dev, const void *np); @@ -139,8 +142,7 @@ int device_match_any(struct device *dev, const void *unused); int bus_for_each_dev(const struct bus_type *bus, struct device *start, void *data, int (*fn)(struct device *dev, void *data)); struct device *bus_find_device(const struct bus_type *bus, struct device *start, - const void *data, - int (*match)(struct device *dev, const void *data)); + const void *data, device_match_t match); /** * bus_find_device_by_name - device iterator for locating a particular device * of a specific name. diff --git a/include/linux/device/class.h b/include/linux/device/class.h index c576b49c55c2..518c9c83d64b 100644 --- a/include/linux/device/class.h +++ b/include/linux/device/class.h @@ -95,7 +95,7 @@ void class_dev_iter_exit(struct class_dev_iter *iter); int class_for_each_device(const struct class *class, const struct device *start, void *data, int (*fn)(struct device *dev, void *data)); struct device *class_find_device(const struct class *class, const struct device *start, - const void *data, int (*match)(struct device *, const void *)); + const void *data, device_match_t match); /** * class_find_device_by_name - device iterator for locating a particular device diff --git a/include/linux/device/driver.h b/include/linux/device/driver.h index 1fc8b68786de..5c04b8e3833b 100644 --- a/include/linux/device/driver.h +++ b/include/linux/device/driver.h @@ -157,7 +157,7 @@ int __must_check driver_for_each_device(struct device_driver *drv, struct device void *data, int (*fn)(struct device *dev, void *)); struct device *driver_find_device(const struct device_driver *drv, struct device *start, const void *data, - int (*match)(struct device *dev, const void *data)); + device_match_t match); /** * driver_find_device_by_name - device iterator for locating a particular device -- cgit v1.2.3-70-g09d2 From 4a74f22386ccb1ddd06eb242bdd02548a7199bda Mon Sep 17 00:00:00 2001 From: Yuesong Li Date: Wed, 21 Aug 2024 12:04:32 +0800 Subject: driver:base:core: Adding a "Return:" line in comment for device_link_add() The original document doesn't explain the return value directly which leads to confusing in error checking. You can find the reason here: Link: https://lore.kernel.org/all/1d4c39e109bcf288d5900670e024a315.sboyd@kernel.org/ Signed-off-by: Yuesong Li Link: https://lore.kernel.org/r/20240821040432.4049183-1-liyuesong@vivo.com Signed-off-by: Greg Kroah-Hartman --- drivers/base/core.c | 3 +++ 1 file changed, 3 insertions(+) (limited to 'drivers/base') diff --git a/drivers/base/core.c b/drivers/base/core.c index 4bc8b88d697e..ec2197aec0b7 100644 --- a/drivers/base/core.c +++ b/drivers/base/core.c @@ -673,6 +673,9 @@ postcore_initcall(devlink_class_init); * @supplier: Supplier end of the link. * @flags: Link flags. * + * Return: On success, a device_link struct will be returned. + * On error or invalid flag settings, NULL will be returned. + * * The caller is responsible for the proper synchronization of the link creation * 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 -- cgit v1.2.3-70-g09d2 From a169a663bfa8198f33a5c1002634cc89e5128025 Mon Sep 17 00:00:00 2001 From: Zijun Hu Date: Thu, 22 Aug 2024 20:38:35 +0800 Subject: driver core: class: Check namespace relevant parameters in class_register() Device class has two namespace relevant fields which are usually associated by the following usage: struct class { ... const struct kobj_ns_type_operations *ns_type; const void *(*namespace)(const struct device *dev); ... } if (dev->class && dev->class->ns_type) dev->class->namespace(dev); (1) The usage looks weird since it checks @ns_type but calls namespace() (2) The usage implies both fields have dependency but their dependency is not currently enforced yet. It is found for all existing class definitions that the other filed is also assigned once one is assigned in current kernel tree. Fixed by enforcing above existing dependency that both fields are required for a device class to support namespace via parameter checks. Signed-off-by: Zijun Hu Link: https://lore.kernel.org/r/20240822-class_fix-v1-1-2a6d38ba913a@quicinc.com Signed-off-by: Greg Kroah-Hartman --- drivers/base/class.c | 11 +++++++++++ drivers/base/core.c | 2 +- 2 files changed, 12 insertions(+), 1 deletion(-) (limited to 'drivers/base') diff --git a/drivers/base/class.c b/drivers/base/class.c index ae22fa992c04..cb5359235c70 100644 --- a/drivers/base/class.c +++ b/drivers/base/class.c @@ -183,6 +183,17 @@ int class_register(const struct class *cls) pr_debug("device class '%s': registering\n", cls->name); + if (cls->ns_type && !cls->namespace) { + pr_err("%s: class '%s' does not have namespace\n", + __func__, cls->name); + return -EINVAL; + } + if (!cls->ns_type && cls->namespace) { + pr_err("%s: class '%s' does not have ns_type\n", + __func__, cls->name); + return -EINVAL; + } + cp = kzalloc(sizeof(*cp), GFP_KERNEL); if (!cp) return -ENOMEM; diff --git a/drivers/base/core.c b/drivers/base/core.c index ec2197aec0b7..30408a4a4927 100644 --- a/drivers/base/core.c +++ b/drivers/base/core.c @@ -2584,7 +2584,7 @@ static const void *device_namespace(const struct kobject *kobj) const struct device *dev = kobj_to_dev(kobj); const void *ns = NULL; - if (dev->class && dev->class->ns_type) + if (dev->class && dev->class->namespace) ns = dev->class->namespace(dev); return ns; -- cgit v1.2.3-70-g09d2 From 24e041e1e48d06f25a12caaf73728a4ec2e511fe Mon Sep 17 00:00:00 2001 From: Kunwu Chan Date: Fri, 23 Aug 2024 15:55:44 +0800 Subject: platform: Make platform_bus_type constant Since commit d492cc2573a0 ("driver core: device.h: make struct bus_type a const *"), the driver core can properly handle constant struct bus_type, move the platform_bus_type variable to be a constant structure as well, placing it into read-only memory which can not be modified at runtime. Cc: Greg Kroah-Hartman Suggested-by: Greg Kroah-Hartman Signed-off-by: Kunwu Chan Link: https://lore.kernel.org/r/20240823075544.144426-1-kunwu.chan@linux.dev Signed-off-by: Greg Kroah-Hartman --- drivers/base/platform.c | 2 +- include/linux/platform_device.h | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) (limited to 'drivers/base') diff --git a/drivers/base/platform.c b/drivers/base/platform.c index 4c3ee6521ba5..6f2a33722c52 100644 --- a/drivers/base/platform.c +++ b/drivers/base/platform.c @@ -1474,7 +1474,7 @@ static const struct dev_pm_ops platform_dev_pm_ops = { USE_PLATFORM_PM_SLEEP_OPS }; -struct bus_type platform_bus_type = { +const struct bus_type platform_bus_type = { .name = "platform", .dev_groups = platform_dev_groups, .match = platform_match, diff --git a/include/linux/platform_device.h b/include/linux/platform_device.h index d422db6eec63..7132623e4658 100644 --- a/include/linux/platform_device.h +++ b/include/linux/platform_device.h @@ -52,7 +52,7 @@ struct platform_device { extern int platform_device_register(struct platform_device *); extern void platform_device_unregister(struct platform_device *); -extern struct bus_type platform_bus_type; +extern const struct bus_type platform_bus_type; extern struct device platform_bus; extern struct resource *platform_get_resource(struct platform_device *, -- cgit v1.2.3-70-g09d2 From ba6353748e71bd1d7e422fec2b5c2e2dfc2e3bd9 Mon Sep 17 00:00:00 2001 From: Stuart Hayes Date: Thu, 22 Aug 2024 15:28:02 -0500 Subject: driver core: don't always lock parent in shutdown Don't lock a parent device unless it is needed in device_shutdown. This is in preparation for making device shutdown asynchronous, when it will be needed to allow children of a common parent to shut down simultaneously. Signed-off-by: Stuart Hayes Signed-off-by: David Jeffery Reviewed-by: Sagi Grimberg Reviewed-by: Christoph Hellwig Reviewed-by: Keith Busch Tested-by: Keith Busch Link: https://lore.kernel.org/r/20240822202805.6379-2-stuart.w.hayes@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 30408a4a4927..988aff79ee7f 100644 --- a/drivers/base/core.c +++ b/drivers/base/core.c @@ -4816,7 +4816,7 @@ void device_shutdown(void) spin_unlock(&devices_kset->list_lock); /* hold lock to avoid race with probe/release */ - if (parent) + if (parent && dev->bus && dev->bus->need_parent_lock) device_lock(parent); device_lock(dev); @@ -4840,7 +4840,7 @@ void device_shutdown(void) } device_unlock(dev); - if (parent) + if (parent && dev->bus && dev->bus->need_parent_lock) device_unlock(parent); put_device(dev); -- cgit v1.2.3-70-g09d2 From 95dc7565253a8564911190ebd1e4ffceb4de208a Mon Sep 17 00:00:00 2001 From: Stuart Hayes Date: Thu, 22 Aug 2024 15:28:03 -0500 Subject: driver core: separate function to shutdown one device Make a separate function for the part of device_shutdown() that does the shutown for a single device. This is in preparation for making device shutdown asynchronous. Signed-off-by: Stuart Hayes Signed-off-by: David Jeffery Reviewed-by: Christoph Hellwig Reviewed-by: Sagi Grimberg Reviewed-by: Keith Busch Tested-by: Keith Busch Link: https://lore.kernel.org/r/20240822202805.6379-3-stuart.w.hayes@gmail.com Signed-off-by: Greg Kroah-Hartman --- drivers/base/core.c | 66 +++++++++++++++++++++++++++++------------------------ 1 file changed, 36 insertions(+), 30 deletions(-) (limited to 'drivers/base') diff --git a/drivers/base/core.c b/drivers/base/core.c index 988aff79ee7f..8598421cbb7f 100644 --- a/drivers/base/core.c +++ b/drivers/base/core.c @@ -4779,6 +4779,41 @@ out: } EXPORT_SYMBOL_GPL(device_change_owner); +static void shutdown_one_device(struct device *dev) +{ + /* hold lock to avoid race with probe/release */ + if (dev->parent && dev->bus && dev->bus->need_parent_lock) + device_lock(dev->parent); + device_lock(dev); + + /* Don't allow any more runtime suspends */ + pm_runtime_get_noresume(dev); + pm_runtime_barrier(dev); + + if (dev->class && dev->class->shutdown_pre) { + if (initcall_debug) + dev_info(dev, "shutdown_pre\n"); + dev->class->shutdown_pre(dev); + } + if (dev->bus && dev->bus->shutdown) { + if (initcall_debug) + dev_info(dev, "shutdown\n"); + dev->bus->shutdown(dev); + } else if (dev->driver && dev->driver->shutdown) { + if (initcall_debug) + dev_info(dev, "shutdown\n"); + dev->driver->shutdown(dev); + } + + device_unlock(dev); + if (dev->parent && dev->bus && dev->bus->need_parent_lock) + device_unlock(dev->parent); + + put_device(dev); + if (dev->parent) + put_device(dev->parent); +} + /** * device_shutdown - call ->shutdown() on each device to shutdown. */ @@ -4815,36 +4850,7 @@ void device_shutdown(void) list_del_init(&dev->kobj.entry); spin_unlock(&devices_kset->list_lock); - /* hold lock to avoid race with probe/release */ - if (parent && dev->bus && dev->bus->need_parent_lock) - device_lock(parent); - device_lock(dev); - - /* Don't allow any more runtime suspends */ - pm_runtime_get_noresume(dev); - pm_runtime_barrier(dev); - - if (dev->class && dev->class->shutdown_pre) { - if (initcall_debug) - dev_info(dev, "shutdown_pre\n"); - dev->class->shutdown_pre(dev); - } - if (dev->bus && dev->bus->shutdown) { - if (initcall_debug) - dev_info(dev, "shutdown\n"); - dev->bus->shutdown(dev); - } else if (dev->driver && dev->driver->shutdown) { - if (initcall_debug) - dev_info(dev, "shutdown\n"); - dev->driver->shutdown(dev); - } - - device_unlock(dev); - if (parent && dev->bus && dev->bus->need_parent_lock) - device_unlock(parent); - - put_device(dev); - put_device(parent); + shutdown_one_device(dev); spin_lock(&devices_kset->list_lock); } -- cgit v1.2.3-70-g09d2 From 8064952c65045f05ee2671fe437770e50c151776 Mon Sep 17 00:00:00 2001 From: Stuart Hayes Date: Thu, 22 Aug 2024 15:28:04 -0500 Subject: driver core: shut down devices asynchronously Add code to allow asynchronous shutdown of devices, ensuring that each device is shut down before its parents & suppliers. Only devices with drivers that have async_shutdown_enable enabled will be shut down asynchronously. This can dramatically reduce system shutdown/reboot time on systems that have multiple devices that take many seconds to shut down (like certain NVMe drives). On one system tested, the shutdown time went from 11 minutes without this patch to 55 seconds with the patch. Signed-off-by: Stuart Hayes Signed-off-by: David Jeffery Reviewed-by: Christoph Hellwig Reviewed-by: Sagi Grimberg Reviewed-by: Keith Busch Tested-by: Keith Busch Link: https://lore.kernel.org/r/20240822202805.6379-4-stuart.w.hayes@gmail.com Signed-off-by: Greg Kroah-Hartman --- drivers/base/base.h | 4 ++++ drivers/base/core.c | 54 ++++++++++++++++++++++++++++++++++++++++++- include/linux/device/driver.h | 2 ++ 3 files changed, 59 insertions(+), 1 deletion(-) (limited to 'drivers/base') diff --git a/drivers/base/base.h b/drivers/base/base.h index 8cf04a557bdb..ea18aa70f151 100644 --- a/drivers/base/base.h +++ b/drivers/base/base.h @@ -10,6 +10,7 @@ * shared outside of the drivers/base/ directory. * */ +#include #include /** @@ -97,6 +98,8 @@ struct driver_private { * the device; typically because it depends on another driver getting * probed first. * @async_driver - pointer to device driver awaiting probe via async_probe + * @shutdown_after - used during device shutdown to ensure correct shutdown + * ordering. * @device - pointer back to the struct device that this structure is * associated with. * @dead - This device is currently either in the process of or has been @@ -114,6 +117,7 @@ struct device_private { struct list_head deferred_probe; const struct device_driver *async_driver; char *deferred_probe_reason; + async_cookie_t shutdown_after; struct device *device; u8 dead:1; }; diff --git a/drivers/base/core.c b/drivers/base/core.c index 8598421cbb7f..6113bc1092ae 100644 --- a/drivers/base/core.c +++ b/drivers/base/core.c @@ -9,6 +9,7 @@ */ #include +#include #include #include #include @@ -3524,6 +3525,7 @@ static int device_private_init(struct device *dev) klist_init(&dev->p->klist_children, klist_children_get, klist_children_put); INIT_LIST_HEAD(&dev->p->deferred_probe); + dev->p->shutdown_after = 0; return 0; } @@ -4779,6 +4781,8 @@ out: } EXPORT_SYMBOL_GPL(device_change_owner); +static ASYNC_DOMAIN(sd_domain); + static void shutdown_one_device(struct device *dev) { /* hold lock to avoid race with probe/release */ @@ -4814,12 +4818,34 @@ static void shutdown_one_device(struct device *dev) put_device(dev->parent); } +/** + * shutdown_one_device_async + * @data: the pointer to the struct device to be shutdown + * @cookie: not used + * + * Shuts down one device, after waiting for shutdown_after to complete. + * shutdown_after should be set to the cookie of the last child or consumer + * of this device to be shutdown (if any), or to the cookie of the previous + * device to be shut down for devices that don't enable asynchronous shutdown. + */ +static void shutdown_one_device_async(void *data, async_cookie_t cookie) +{ + struct device *dev = data; + + async_synchronize_cookie_domain(dev->p->shutdown_after + 1, &sd_domain); + + shutdown_one_device(dev); +} + /** * device_shutdown - call ->shutdown() on each device to shutdown. */ void device_shutdown(void) { struct device *dev, *parent; + async_cookie_t cookie = 0; + struct device_link *link; + int idx; wait_for_device_probe(); device_block_probing(); @@ -4850,11 +4876,37 @@ void device_shutdown(void) list_del_init(&dev->kobj.entry); spin_unlock(&devices_kset->list_lock); - shutdown_one_device(dev); + + /* + * Set cookie for devices that will be shut down synchronously + */ + if (!dev->driver || !dev->driver->async_shutdown_enable) + dev->p->shutdown_after = cookie; + + get_device(dev); + get_device(parent); + + cookie = async_schedule_domain(shutdown_one_device_async, + dev, &sd_domain); + /* + * Ensure parent & suppliers wait for this device to shut down + */ + if (parent) { + parent->p->shutdown_after = cookie; + put_device(parent); + } + + idx = device_links_read_lock(); + list_for_each_entry_rcu(link, &dev->links.suppliers, c_node, + device_links_read_lock_held()) + link->supplier->p->shutdown_after = cookie; + device_links_read_unlock(idx); + put_device(dev); spin_lock(&devices_kset->list_lock); } spin_unlock(&devices_kset->list_lock); + async_synchronize_full_domain(&sd_domain); } /* diff --git a/include/linux/device/driver.h b/include/linux/device/driver.h index 5c04b8e3833b..14c9211b82d6 100644 --- a/include/linux/device/driver.h +++ b/include/linux/device/driver.h @@ -56,6 +56,7 @@ enum probe_type { * @mod_name: Used for built-in modules. * @suppress_bind_attrs: Disables bind/unbind via sysfs. * @probe_type: Type of the probe (synchronous or asynchronous) to use. + * @async_shutdown_enable: Enables devices to be shutdown asynchronously. * @of_match_table: The open firmware table. * @acpi_match_table: The ACPI match table. * @probe: Called to query the existence of a specific device, @@ -102,6 +103,7 @@ struct device_driver { bool suppress_bind_attrs; /* disables bind/unbind via sysfs */ enum probe_type probe_type; + bool async_shutdown_enable; const struct of_device_id *of_match_table; const struct acpi_device_id *acpi_match_table; -- cgit v1.2.3-70-g09d2 From 903c44939abc02e2f3d6f2ad65fa090f7e5df5b6 Mon Sep 17 00:00:00 2001 From: Zijun Hu Date: Sat, 24 Aug 2024 17:07:43 +0800 Subject: driver core: Make parameter check consistent for API cluster device_(for_each|find)_child() The following API cluster takes the same type parameter list, but do not have consistent parameter check as shown below. device_for_each_child(struct device *parent, ...) // check (!parent->p) device_for_each_child_reverse(struct device *parent, ...) // same as above device_find_child(struct device *parent, ...) // check (!parent) Fixed by using consistent check (!parent || !parent->p) which covers both existing checks for the cluster. Signed-off-by: Zijun Hu Link: https://lore.kernel.org/r/20240824-const_dfc_prepare-v3-1-32127ea32bba@quicinc.com Signed-off-by: Greg Kroah-Hartman --- drivers/base/core.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) (limited to 'drivers/base') diff --git a/drivers/base/core.c b/drivers/base/core.c index 6113bc1092ae..b69b82da8837 100644 --- a/drivers/base/core.c +++ b/drivers/base/core.c @@ -3998,7 +3998,7 @@ int device_for_each_child(struct device *parent, void *data, struct device *child; int error = 0; - if (!parent->p) + if (!parent || !parent->p) return 0; klist_iter_init(&parent->p->klist_children, &i); @@ -4028,7 +4028,7 @@ int device_for_each_child_reverse(struct device *parent, void *data, struct device *child; int error = 0; - if (!parent->p) + if (!parent || !parent->p) return 0; klist_iter_init(&parent->p->klist_children, &i); @@ -4062,7 +4062,7 @@ struct device *device_find_child(struct device *parent, void *data, struct klist_iter i; struct device *child; - if (!parent) + if (!parent || !parent->p) return NULL; klist_iter_init(&parent->p->klist_children, &i); -- cgit v1.2.3-70-g09d2 From fea64fa04c31426eae512751e0c5342345c5741c Mon Sep 17 00:00:00 2001 From: Uros Bizjak Date: Fri, 30 Aug 2024 10:33:52 +0200 Subject: devres: Correclty strip percpu address space of devm_free_percpu() argument MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit devm_free_percpu() calls devres_release() with a pointer in percpu address space. devres_release() expects pointers in the generic address space, so address space needs to be stripped from the argument. When strict percpu address space checks are enabled, then the current direct cast from the percpu address space to the generic address space fails the compilation on x86_64 with: devres.c:1234:32: error: cast to generic address space pointer from disjoint ‘__seg_gs’ address space pointer Add intermediate casts to unsigned long to remove address space of the pointer before casting it to the generic AS, as advised in [1] and [2]. Side note: sparse still requires __force, although the documentation [2] allows casts to unsigned long without __force attribute. Found by GCC's named address space checks. There were no changes in the resulting object file. [1] https://gcc.gnu.org/onlinedocs/gcc/Named-Address-Spaces.html#x86-Named-Address-Spaces [2] https://sparse.docs.kernel.org/en/latest/annotations.html#address-space-name Signed-off-by: Uros Bizjak Cc: Greg Kroah-Hartman Cc: Rafael J. Wysocki Link: https://lore.kernel.org/r/20240830083406.9695-1-ubizjak@gmail.com Signed-off-by: Greg Kroah-Hartman --- drivers/base/devres.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers/base') diff --git a/drivers/base/devres.c b/drivers/base/devres.c index a2ce0ead06a6..2152eec0c135 100644 --- a/drivers/base/devres.c +++ b/drivers/base/devres.c @@ -1231,6 +1231,6 @@ void devm_free_percpu(struct device *dev, void __percpu *pdata) * devm_free_pages() does. */ WARN_ON(devres_release(dev, devm_percpu_release, devm_percpu_match, - (__force void *)pdata)); + (void *)(__force unsigned long)pdata)); } EXPORT_SYMBOL_GPL(devm_free_percpu); -- cgit v1.2.3-70-g09d2 From efb0b309fa0d8a92f9b303d292944cda08349eed Mon Sep 17 00:00:00 2001 From: Zijun Hu Date: Sun, 8 Sep 2024 10:48:47 +0800 Subject: driver core: Trivially simplify ((struct device_private *)curr)->device->p to @curr Trivially simplify ((struct device_private *)curr)->device->p to @curr in deferred_devs_show() since both are same. Signed-off-by: Zijun Hu Link: https://lore.kernel.org/r/20240908-trivial_simpli-v1-1-53e0f1363299@quicinc.com Signed-off-by: Greg Kroah-Hartman --- drivers/base/dd.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers/base') diff --git a/drivers/base/dd.c b/drivers/base/dd.c index 9b745ba54de1..a7cc7ff0923b 100644 --- a/drivers/base/dd.c +++ b/drivers/base/dd.c @@ -248,7 +248,7 @@ static int deferred_devs_show(struct seq_file *s, void *data) list_for_each_entry(curr, &deferred_probe_pending_list, deferred_probe) seq_printf(s, "%s\t%s", dev_name(curr->device), - curr->device->p->deferred_probe_reason ?: "\n"); + curr->deferred_probe_reason ?: "\n"); mutex_unlock(&deferred_probe_mutex); -- cgit v1.2.3-70-g09d2 From 6a36d828bdef0e02b1e6c12e2160f5b83be6aab5 Mon Sep 17 00:00:00 2001 From: "Dr. David Alan Gilbert" Date: Fri, 13 Sep 2024 02:09:55 +0100 Subject: driver core: attribute_container: Remove unused functions I can't find any use of 'attribute_container_add_class_device_adapter' or 'attribute_container_trigger' in git history. Their export decls went in 2006: commit 1740757e8f94 ("[PATCH] Driver Core: remove unused exports") and their docs disappeared in 2016: commit 47cb398dd75a ("Docs: sphinxify device-drivers.tmpl") Remove them. Signed-off-by: Dr. David Alan Gilbert Link: https://lore.kernel.org/r/20240913010955.1393995-1-linux@treblig.org Signed-off-by: Greg Kroah-Hartman --- drivers/base/attribute_container.c | 48 +------------------------------------ include/linux/attribute_container.h | 6 ----- 2 files changed, 1 insertion(+), 53 deletions(-) (limited to 'drivers/base') diff --git a/drivers/base/attribute_container.c b/drivers/base/attribute_container.c index 01ef796c2055..b6f941a6ab69 100644 --- a/drivers/base/attribute_container.c +++ b/drivers/base/attribute_container.c @@ -346,8 +346,7 @@ attribute_container_device_trigger_safe(struct device *dev, * @fn: the function to execute for each classdev. * * This function is for executing a trigger when you need to know both - * the container and the classdev. If you only care about the - * container, then use attribute_container_trigger() instead. + * the container and the classdev. */ void attribute_container_device_trigger(struct device *dev, @@ -378,33 +377,6 @@ attribute_container_device_trigger(struct device *dev, mutex_unlock(&attribute_container_mutex); } -/** - * attribute_container_trigger - trigger a function for each matching container - * - * @dev: The generic device to activate the trigger for - * @fn: the function to trigger - * - * This routine triggers a function that only needs to know the - * matching containers (not the classdev) associated with a device. - * It is more lightweight than attribute_container_device_trigger, so - * should be used in preference unless the triggering function - * actually needs to know the classdev. - */ -void -attribute_container_trigger(struct device *dev, - int (*fn)(struct attribute_container *, - struct device *)) -{ - struct attribute_container *cont; - - mutex_lock(&attribute_container_mutex); - list_for_each_entry(cont, &attribute_container_list, node) { - if (cont->match(cont, dev)) - fn(cont, dev); - } - mutex_unlock(&attribute_container_mutex); -} - /** * attribute_container_add_attrs - add attributes * @@ -458,24 +430,6 @@ attribute_container_add_class_device(struct device *classdev) return attribute_container_add_attrs(classdev); } -/** - * attribute_container_add_class_device_adapter - simple adapter for triggers - * - * @cont: the container to register. - * @dev: the generic device to activate the trigger for - * @classdev: the class device to add - * - * This function is identical to attribute_container_add_class_device except - * that it is designed to be called from the triggers - */ -int -attribute_container_add_class_device_adapter(struct attribute_container *cont, - struct device *dev, - struct device *classdev) -{ - return attribute_container_add_class_device(classdev); -} - /** * attribute_container_remove_attrs - remove any attribute files * diff --git a/include/linux/attribute_container.h b/include/linux/attribute_container.h index e4004d1e6725..b3643de9931d 100644 --- a/include/linux/attribute_container.h +++ b/include/linux/attribute_container.h @@ -61,14 +61,8 @@ int attribute_container_device_trigger_safe(struct device *dev, int (*undo)(struct attribute_container *, struct device *, struct device *)); -void attribute_container_trigger(struct device *dev, - int (*fn)(struct attribute_container *, - struct device *)); int attribute_container_add_attrs(struct device *classdev); int attribute_container_add_class_device(struct device *classdev); -int attribute_container_add_class_device_adapter(struct attribute_container *cont, - struct device *dev, - struct device *classdev); void attribute_container_remove_attrs(struct device *classdev); void attribute_container_class_device_del(struct device *classdev); struct attribute_container *attribute_container_classdev_to_container(struct device *); -- cgit v1.2.3-70-g09d2 From 4f2c346e621624315e2a1405e98616a0c5ac146f Mon Sep 17 00:00:00 2001 From: Stuart Hayes Date: Wed, 18 Sep 2024 23:31:43 -0500 Subject: driver core: fix async device shutdown hang Modify device_shutdown() so that supplier devices do not wait for consumer devices to be shut down first when the devlink is sync state only, since the consumer is not dependent on the supplier in this case. Without this change, a circular dependency could hang the system. Fixes: 8064952c6504 ("driver core: shut down devices asynchronously") Signed-off-by: Stuart Hayes Tested-by: Laurence Oberman Tested-by: Nathan Chancellor Link: https://lore.kernel.org/r/20240919043143.1194950-1-stuart.w.hayes@gmail.com Signed-off-by: Greg Kroah-Hartman --- drivers/base/core.c | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) (limited to 'drivers/base') diff --git a/drivers/base/core.c b/drivers/base/core.c index b69b82da8837..76513e360496 100644 --- a/drivers/base/core.c +++ b/drivers/base/core.c @@ -4898,8 +4898,16 @@ void device_shutdown(void) idx = device_links_read_lock(); list_for_each_entry_rcu(link, &dev->links.suppliers, c_node, - device_links_read_lock_held()) + device_links_read_lock_held()) { + /* + * sync_state_only suppliers don't need to wait, + * aren't reordered on devices_kset, so making them + * wait could result in a hang + */ + if (device_link_flag_is_sync_state_only(link->flags)) + continue; link->supplier->p->shutdown_after = cookie; + } device_links_read_unlock(idx); put_device(dev); -- cgit v1.2.3-70-g09d2 From e11daafdbf5b683a5da33a080862769b696b1621 Mon Sep 17 00:00:00 2001 From: Greg Kroah-Hartman Date: Wed, 25 Sep 2024 10:56:57 +0200 Subject: Revert "driver core: fix async device shutdown hang" This reverts commit 4f2c346e621624315e2a1405e98616a0c5ac146f. The series is being reverted before -rc1 as there are still reports of lockups on shutdown, so it's not quite ready for "prime time." Reported-by: Andrey Skvortsov Link: https://lore.kernel.org/r/ZvMkkhyJrohaajuk@skv.local Cc: Christoph Hellwig Cc: David Jeffery Cc: Keith Busch Cc: Laurence Oberman Cc: Nathan Chancellor Cc: Sagi Grimberg Cc: Stuart Hayes Signed-off-by: Greg Kroah-Hartman --- drivers/base/core.c | 10 +--------- 1 file changed, 1 insertion(+), 9 deletions(-) (limited to 'drivers/base') diff --git a/drivers/base/core.c b/drivers/base/core.c index 76513e360496..b69b82da8837 100644 --- a/drivers/base/core.c +++ b/drivers/base/core.c @@ -4898,16 +4898,8 @@ void device_shutdown(void) idx = device_links_read_lock(); list_for_each_entry_rcu(link, &dev->links.suppliers, c_node, - device_links_read_lock_held()) { - /* - * sync_state_only suppliers don't need to wait, - * aren't reordered on devices_kset, so making them - * wait could result in a hang - */ - if (device_link_flag_is_sync_state_only(link->flags)) - continue; + device_links_read_lock_held()) link->supplier->p->shutdown_after = cookie; - } device_links_read_unlock(idx); put_device(dev); -- cgit v1.2.3-70-g09d2 From 2efddb5575cd9f5f4d61ad417c92365a5f18d2f1 Mon Sep 17 00:00:00 2001 From: Greg Kroah-Hartman Date: Wed, 25 Sep 2024 10:57:00 +0200 Subject: Revert "driver core: shut down devices asynchronously" This reverts commit 8064952c65045f05ee2671fe437770e50c151776. The series is being reverted before -rc1 as there are still reports of lockups on shutdown, so it's not quite ready for "prime time." Reported-by: Andrey Skvortsov Link: https://lore.kernel.org/r/ZvMkkhyJrohaajuk@skv.local Cc: Christoph Hellwig Cc: David Jeffery Cc: Keith Busch Cc: Laurence Oberman Cc: Nathan Chancellor Cc: Sagi Grimberg Cc: Stuart Hayes Signed-off-by: Greg Kroah-Hartman --- drivers/base/base.h | 4 ---- drivers/base/core.c | 54 +------------------------------------------ include/linux/device/driver.h | 2 -- 3 files changed, 1 insertion(+), 59 deletions(-) (limited to 'drivers/base') diff --git a/drivers/base/base.h b/drivers/base/base.h index ea18aa70f151..8cf04a557bdb 100644 --- a/drivers/base/base.h +++ b/drivers/base/base.h @@ -10,7 +10,6 @@ * shared outside of the drivers/base/ directory. * */ -#include #include /** @@ -98,8 +97,6 @@ struct driver_private { * the device; typically because it depends on another driver getting * probed first. * @async_driver - pointer to device driver awaiting probe via async_probe - * @shutdown_after - used during device shutdown to ensure correct shutdown - * ordering. * @device - pointer back to the struct device that this structure is * associated with. * @dead - This device is currently either in the process of or has been @@ -117,7 +114,6 @@ struct device_private { struct list_head deferred_probe; const struct device_driver *async_driver; char *deferred_probe_reason; - async_cookie_t shutdown_after; struct device *device; u8 dead:1; }; diff --git a/drivers/base/core.c b/drivers/base/core.c index b69b82da8837..4482382fb947 100644 --- a/drivers/base/core.c +++ b/drivers/base/core.c @@ -9,7 +9,6 @@ */ #include -#include #include #include #include @@ -3525,7 +3524,6 @@ static int device_private_init(struct device *dev) klist_init(&dev->p->klist_children, klist_children_get, klist_children_put); INIT_LIST_HEAD(&dev->p->deferred_probe); - dev->p->shutdown_after = 0; return 0; } @@ -4781,8 +4779,6 @@ out: } EXPORT_SYMBOL_GPL(device_change_owner); -static ASYNC_DOMAIN(sd_domain); - static void shutdown_one_device(struct device *dev) { /* hold lock to avoid race with probe/release */ @@ -4818,34 +4814,12 @@ static void shutdown_one_device(struct device *dev) put_device(dev->parent); } -/** - * shutdown_one_device_async - * @data: the pointer to the struct device to be shutdown - * @cookie: not used - * - * Shuts down one device, after waiting for shutdown_after to complete. - * shutdown_after should be set to the cookie of the last child or consumer - * of this device to be shutdown (if any), or to the cookie of the previous - * device to be shut down for devices that don't enable asynchronous shutdown. - */ -static void shutdown_one_device_async(void *data, async_cookie_t cookie) -{ - struct device *dev = data; - - async_synchronize_cookie_domain(dev->p->shutdown_after + 1, &sd_domain); - - shutdown_one_device(dev); -} - /** * device_shutdown - call ->shutdown() on each device to shutdown. */ void device_shutdown(void) { struct device *dev, *parent; - async_cookie_t cookie = 0; - struct device_link *link; - int idx; wait_for_device_probe(); device_block_probing(); @@ -4876,37 +4850,11 @@ void device_shutdown(void) list_del_init(&dev->kobj.entry); spin_unlock(&devices_kset->list_lock); - - /* - * Set cookie for devices that will be shut down synchronously - */ - if (!dev->driver || !dev->driver->async_shutdown_enable) - dev->p->shutdown_after = cookie; - - get_device(dev); - get_device(parent); - - cookie = async_schedule_domain(shutdown_one_device_async, - dev, &sd_domain); - /* - * Ensure parent & suppliers wait for this device to shut down - */ - if (parent) { - parent->p->shutdown_after = cookie; - put_device(parent); - } - - idx = device_links_read_lock(); - list_for_each_entry_rcu(link, &dev->links.suppliers, c_node, - device_links_read_lock_held()) - link->supplier->p->shutdown_after = cookie; - device_links_read_unlock(idx); - put_device(dev); + shutdown_one_device(dev); spin_lock(&devices_kset->list_lock); } spin_unlock(&devices_kset->list_lock); - async_synchronize_full_domain(&sd_domain); } /* diff --git a/include/linux/device/driver.h b/include/linux/device/driver.h index 14c9211b82d6..5c04b8e3833b 100644 --- a/include/linux/device/driver.h +++ b/include/linux/device/driver.h @@ -56,7 +56,6 @@ enum probe_type { * @mod_name: Used for built-in modules. * @suppress_bind_attrs: Disables bind/unbind via sysfs. * @probe_type: Type of the probe (synchronous or asynchronous) to use. - * @async_shutdown_enable: Enables devices to be shutdown asynchronously. * @of_match_table: The open firmware table. * @acpi_match_table: The ACPI match table. * @probe: Called to query the existence of a specific device, @@ -103,7 +102,6 @@ struct device_driver { bool suppress_bind_attrs; /* disables bind/unbind via sysfs */ enum probe_type probe_type; - bool async_shutdown_enable; const struct of_device_id *of_match_table; const struct acpi_device_id *acpi_match_table; -- cgit v1.2.3-70-g09d2 From 56d16d44fe8d8012dabd32700ea143c7caa35ba3 Mon Sep 17 00:00:00 2001 From: Greg Kroah-Hartman Date: Wed, 25 Sep 2024 10:57:01 +0200 Subject: Revert "driver core: separate function to shutdown one device" This reverts commit 95dc7565253a8564911190ebd1e4ffceb4de208a. The series is being reverted before -rc1 as there are still reports of lockups on shutdown, so it's not quite ready for "prime time." Reported-by: Andrey Skvortsov Link: https://lore.kernel.org/r/ZvMkkhyJrohaajuk@skv.local Cc: Christoph Hellwig Cc: David Jeffery Cc: Keith Busch Cc: Laurence Oberman Cc: Nathan Chancellor Cc: Sagi Grimberg Cc: Stuart Hayes Signed-off-by: Greg Kroah-Hartman --- drivers/base/core.c | 66 ++++++++++++++++++++++++----------------------------- 1 file changed, 30 insertions(+), 36 deletions(-) (limited to 'drivers/base') diff --git a/drivers/base/core.c b/drivers/base/core.c index 4482382fb947..2bf9730db056 100644 --- a/drivers/base/core.c +++ b/drivers/base/core.c @@ -4779,41 +4779,6 @@ out: } EXPORT_SYMBOL_GPL(device_change_owner); -static void shutdown_one_device(struct device *dev) -{ - /* hold lock to avoid race with probe/release */ - if (dev->parent && dev->bus && dev->bus->need_parent_lock) - device_lock(dev->parent); - device_lock(dev); - - /* Don't allow any more runtime suspends */ - pm_runtime_get_noresume(dev); - pm_runtime_barrier(dev); - - if (dev->class && dev->class->shutdown_pre) { - if (initcall_debug) - dev_info(dev, "shutdown_pre\n"); - dev->class->shutdown_pre(dev); - } - if (dev->bus && dev->bus->shutdown) { - if (initcall_debug) - dev_info(dev, "shutdown\n"); - dev->bus->shutdown(dev); - } else if (dev->driver && dev->driver->shutdown) { - if (initcall_debug) - dev_info(dev, "shutdown\n"); - dev->driver->shutdown(dev); - } - - device_unlock(dev); - if (dev->parent && dev->bus && dev->bus->need_parent_lock) - device_unlock(dev->parent); - - put_device(dev); - if (dev->parent) - put_device(dev->parent); -} - /** * device_shutdown - call ->shutdown() on each device to shutdown. */ @@ -4850,7 +4815,36 @@ void device_shutdown(void) list_del_init(&dev->kobj.entry); spin_unlock(&devices_kset->list_lock); - shutdown_one_device(dev); + /* hold lock to avoid race with probe/release */ + if (parent && dev->bus && dev->bus->need_parent_lock) + device_lock(parent); + device_lock(dev); + + /* Don't allow any more runtime suspends */ + pm_runtime_get_noresume(dev); + pm_runtime_barrier(dev); + + if (dev->class && dev->class->shutdown_pre) { + if (initcall_debug) + dev_info(dev, "shutdown_pre\n"); + dev->class->shutdown_pre(dev); + } + if (dev->bus && dev->bus->shutdown) { + if (initcall_debug) + dev_info(dev, "shutdown\n"); + dev->bus->shutdown(dev); + } else if (dev->driver && dev->driver->shutdown) { + if (initcall_debug) + dev_info(dev, "shutdown\n"); + dev->driver->shutdown(dev); + } + + device_unlock(dev); + if (parent && dev->bus && dev->bus->need_parent_lock) + device_unlock(parent); + + put_device(dev); + put_device(parent); spin_lock(&devices_kset->list_lock); } -- cgit v1.2.3-70-g09d2 From eb46cb321f1f3f3102f4ad3d61dd5c8c06cdbf17 Mon Sep 17 00:00:00 2001 From: Greg Kroah-Hartman Date: Wed, 25 Sep 2024 10:57:02 +0200 Subject: Revert "driver core: don't always lock parent in shutdown" This reverts commit ba6353748e71bd1d7e422fec2b5c2e2dfc2e3bd9. The series is being reverted before -rc1 as there are still reports of lockups on shutdown, so it's not quite ready for "prime time." Reported-by: Andrey Skvortsov Link: https://lore.kernel.org/r/ZvMkkhyJrohaajuk@skv.local Cc: Christoph Hellwig Cc: David Jeffery Cc: Keith Busch Cc: Laurence Oberman Cc: Nathan Chancellor Cc: Sagi Grimberg Cc: Stuart Hayes 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 2bf9730db056..a4c853411a6b 100644 --- a/drivers/base/core.c +++ b/drivers/base/core.c @@ -4816,7 +4816,7 @@ void device_shutdown(void) spin_unlock(&devices_kset->list_lock); /* hold lock to avoid race with probe/release */ - if (parent && dev->bus && dev->bus->need_parent_lock) + if (parent) device_lock(parent); device_lock(dev); @@ -4840,7 +4840,7 @@ void device_shutdown(void) } device_unlock(dev); - if (parent && dev->bus && dev->bus->need_parent_lock) + if (parent) device_unlock(parent); put_device(dev); -- cgit v1.2.3-70-g09d2