From 8afbb4273977f055db219a22daaafe64be10fb95 Mon Sep 17 00:00:00 2001 From: Greg Kroah-Hartman Date: Mon, 9 Jan 2023 18:58:05 +0100 Subject: driver core: make bus_get_device_klist() static No one calls this function outside of drivers/base/bus.c so make it static so it does not need to be exported anymore. Reviewed-by: Rafael J. Wysocki Link: https://lore.kernel.org/r/20230109175810.2965448-1-gregkh@linuxfoundation.org Signed-off-by: Greg Kroah-Hartman --- drivers/base/bus.c | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) (limited to 'drivers/base') diff --git a/drivers/base/bus.c b/drivers/base/bus.c index 4ec6dbab73be..4425f962bf51 100644 --- a/drivers/base/bus.c +++ b/drivers/base/bus.c @@ -897,11 +897,10 @@ struct kset *bus_get_kset(struct bus_type *bus) } EXPORT_SYMBOL_GPL(bus_get_kset); -struct klist *bus_get_device_klist(struct bus_type *bus) +static struct klist *bus_get_device_klist(struct bus_type *bus) { return &bus->p->klist_devices; } -EXPORT_SYMBOL_GPL(bus_get_device_klist); /* * Yes, this forcibly breaks the klist abstraction temporarily. It -- cgit v1.2.3 From a9efdd2519edd7df84afd075b65ca6428dcb0039 Mon Sep 17 00:00:00 2001 From: Greg Kroah-Hartman Date: Mon, 9 Jan 2023 18:58:06 +0100 Subject: driver core: remove subsys_find_device_by_id() This function has not been called by any code in the kernel tree in many many years so remove it as it is unused. Reviewed-by: Rafael J. Wysocki Link: https://lore.kernel.org/r/20230109175810.2965448-2-gregkh@linuxfoundation.org Signed-off-by: Greg Kroah-Hartman --- drivers/base/bus.c | 41 ----------------------------------------- 1 file changed, 41 deletions(-) (limited to 'drivers/base') diff --git a/drivers/base/bus.c b/drivers/base/bus.c index 4425f962bf51..f8eb882bbf37 100644 --- a/drivers/base/bus.c +++ b/drivers/base/bus.c @@ -339,47 +339,6 @@ struct device *bus_find_device(struct bus_type *bus, } EXPORT_SYMBOL_GPL(bus_find_device); -/** - * subsys_find_device_by_id - find a device with a specific enumeration number - * @subsys: subsystem - * @id: index 'id' in struct device - * @hint: device to check first - * - * Check the hint's next object and if it is a match return it directly, - * otherwise, fall back to a full list search. Either way a reference for - * the returned object is taken. - */ -struct device *subsys_find_device_by_id(struct bus_type *subsys, unsigned int id, - struct device *hint) -{ - struct klist_iter i; - struct device *dev; - - if (!subsys) - return NULL; - - if (hint) { - klist_iter_init_node(&subsys->p->klist_devices, &i, &hint->p->knode_bus); - dev = next_device(&i); - if (dev && dev->id == id && get_device(dev)) { - klist_iter_exit(&i); - return dev; - } - klist_iter_exit(&i); - } - - klist_iter_init_node(&subsys->p->klist_devices, &i, NULL); - while ((dev = next_device(&i))) { - if (dev->id == id && get_device(dev)) { - klist_iter_exit(&i); - return dev; - } - } - klist_iter_exit(&i); - return NULL; -} -EXPORT_SYMBOL_GPL(subsys_find_device_by_id); - static struct device_driver *next_driver(struct klist_iter *i) { struct klist_node *n = klist_next(i); -- cgit v1.2.3 From 2e45fc5502af9826617a96fe63aee055002cac97 Mon Sep 17 00:00:00 2001 From: Greg Kroah-Hartman Date: Mon, 9 Jan 2023 18:58:07 +0100 Subject: driver core: make subsys_dev_iter_init() static No one outside of drivers/base/bus.c calls this function so make it static and remove the exported symbol. Reviewed-by: Rafael J. Wysocki Link: https://lore.kernel.org/r/20230109175810.2965448-3-gregkh@linuxfoundation.org Signed-off-by: Greg Kroah-Hartman --- drivers/base/bus.c | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) (limited to 'drivers/base') diff --git a/drivers/base/bus.c b/drivers/base/bus.c index f8eb882bbf37..f781124fdf31 100644 --- a/drivers/base/bus.c +++ b/drivers/base/bus.c @@ -923,8 +923,8 @@ EXPORT_SYMBOL_GPL(bus_sort_breadthfirst); * otherwise if it is NULL, the iteration starts at the beginning of * the list. */ -void subsys_dev_iter_init(struct subsys_dev_iter *iter, struct bus_type *subsys, - struct device *start, const struct device_type *type) +static void subsys_dev_iter_init(struct subsys_dev_iter *iter, struct bus_type *subsys, + struct device *start, const struct device_type *type) { struct klist_node *start_knode = NULL; @@ -933,7 +933,6 @@ void subsys_dev_iter_init(struct subsys_dev_iter *iter, struct bus_type *subsys, klist_iter_init_node(&subsys->p->klist_devices, &iter->ki, start_knode); iter->type = type; } -EXPORT_SYMBOL_GPL(subsys_dev_iter_init); /** * subsys_dev_iter_next - iterate to the next device -- cgit v1.2.3 From 38cdadefa2feecc9e7aa5f67bc4aea5b9a8ca59f Mon Sep 17 00:00:00 2001 From: Greg Kroah-Hartman Date: Mon, 9 Jan 2023 18:58:08 +0100 Subject: driver core: make subsys_dev_iter_next() static The function subsys_dev_iter_next() is only used in drivers/base/bus.c so make it static to that file and remove the global export. Reviewed-by: Rafael J. Wysocki Link: https://lore.kernel.org/r/20230109175810.2965448-4-gregkh@linuxfoundation.org Signed-off-by: Greg Kroah-Hartman --- drivers/base/bus.c | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) (limited to 'drivers/base') diff --git a/drivers/base/bus.c b/drivers/base/bus.c index f781124fdf31..f0aacb3b1bc6 100644 --- a/drivers/base/bus.c +++ b/drivers/base/bus.c @@ -946,7 +946,7 @@ static void subsys_dev_iter_init(struct subsys_dev_iter *iter, struct bus_type * * free to do whatever it wants to do with the device including * calling back into subsys code. */ -struct device *subsys_dev_iter_next(struct subsys_dev_iter *iter) +static struct device *subsys_dev_iter_next(struct subsys_dev_iter *iter) { struct klist_node *knode; struct device *dev; @@ -960,7 +960,6 @@ struct device *subsys_dev_iter_next(struct subsys_dev_iter *iter) return dev; } } -EXPORT_SYMBOL_GPL(subsys_dev_iter_next); /** * subsys_dev_iter_exit - finish iteration -- cgit v1.2.3 From af6d0743599e594cb2cec3f1a6d2600a57d1d375 Mon Sep 17 00:00:00 2001 From: Greg Kroah-Hartman Date: Mon, 9 Jan 2023 18:58:09 +0100 Subject: driver core: make subsys_dev_iter_exit() static The function subsys_dev_iter_exit() is not used outside of drivers/base/bus.c so make it static to that file and remove the global export. Reviewed-by: Rafael J. Wysocki Link: https://lore.kernel.org/r/20230109175810.2965448-5-gregkh@linuxfoundation.org Signed-off-by: Greg Kroah-Hartman --- drivers/base/bus.c | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) (limited to 'drivers/base') diff --git a/drivers/base/bus.c b/drivers/base/bus.c index f0aacb3b1bc6..bde52fd1d3e3 100644 --- a/drivers/base/bus.c +++ b/drivers/base/bus.c @@ -968,11 +968,10 @@ static struct device *subsys_dev_iter_next(struct subsys_dev_iter *iter) * Finish an iteration. Always call this function after iteration is * complete whether the iteration ran till the end or not. */ -void subsys_dev_iter_exit(struct subsys_dev_iter *iter) +static void subsys_dev_iter_exit(struct subsys_dev_iter *iter) { klist_iter_exit(&iter->ki); } -EXPORT_SYMBOL_GPL(subsys_dev_iter_exit); int subsys_interface_register(struct subsys_interface *sif) { -- cgit v1.2.3 From b0a8a59a1c44c07807afe50c6bd21a33c9ec98b7 Mon Sep 17 00:00:00 2001 From: Greg Kroah-Hartman Date: Mon, 9 Jan 2023 18:58:10 +0100 Subject: driver core: move struct subsys_dev_iter to a local file struct subsys_dev_iter is not used by any code outside of drivers/base/bus.c so move it into that file and out of the global bus.h file. Reviewed-by: Rafael J. Wysocki Link: https://lore.kernel.org/r/20230109175810.2965448-6-gregkh@linuxfoundation.org Signed-off-by: Greg Kroah-Hartman --- drivers/base/bus.c | 5 +++++ 1 file changed, 5 insertions(+) (limited to 'drivers/base') diff --git a/drivers/base/bus.c b/drivers/base/bus.c index bde52fd1d3e3..428c26c6b615 100644 --- a/drivers/base/bus.c +++ b/drivers/base/bus.c @@ -911,6 +911,11 @@ void bus_sort_breadthfirst(struct bus_type *bus, } EXPORT_SYMBOL_GPL(bus_sort_breadthfirst); +struct subsys_dev_iter { + struct klist_iter ki; + const struct device_type *type; +}; + /** * subsys_dev_iter_init - initialize subsys device iterator * @iter: subsys iterator to initialize -- cgit v1.2.3 From f6837f34a34973ef6600c08195ed300e24e97317 Mon Sep 17 00:00:00 2001 From: Yang Yingliang Date: Mon, 5 Dec 2022 11:49:04 +0800 Subject: driver core: fix potential null-ptr-deref in device_add() I got the following null-ptr-deref report while doing fault injection test: BUG: kernel NULL pointer dereference, address: 0000000000000058 CPU: 2 PID: 278 Comm: 37-i2c-ds2482 Tainted: G B W N 6.1.0-rc3+ RIP: 0010:klist_put+0x2d/0xd0 Call Trace: klist_remove+0xf1/0x1c0 device_release_driver_internal+0x196/0x210 bus_remove_device+0x1bd/0x240 device_add+0xd3d/0x1100 w1_add_master_device+0x476/0x490 [wire] ds2482_probe+0x303/0x3e0 [ds2482] This is how it happened: w1_alloc_dev() // The dev->driver is set to w1_master_driver. memcpy(&dev->dev, device, sizeof(struct device)); device_add() bus_add_device() dpm_sysfs_add() // It fails, calls bus_remove_device. // error path bus_remove_device() // The dev->driver is not null, but driver is not bound. __device_release_driver() klist_remove(&dev->p->knode_driver) <-- It causes null-ptr-deref. // normal path bus_probe_device() // It's not called yet. device_bind_driver() If dev->driver is set, in the error path after calling bus_add_device() in device_add(), bus_remove_device() is called, then the device will be detached from driver. But device_bind_driver() is not called yet, so it causes null-ptr-deref while access the 'knode_driver'. To fix this, set dev->driver to null in the error path before calling bus_remove_device(). Fixes: 57eee3d23e88 ("Driver core: Call device_pm_add() after bus_add_device() in device_add()") Signed-off-by: Yang Yingliang Link: https://lore.kernel.org/r/20221205034904.2077765-1-yangyingliang@huawei.com Signed-off-by: Greg Kroah-Hartman --- drivers/base/core.c | 1 + 1 file changed, 1 insertion(+) (limited to 'drivers/base') diff --git a/drivers/base/core.c b/drivers/base/core.c index a3e14143ec0c..506289e6b04d 100644 --- a/drivers/base/core.c +++ b/drivers/base/core.c @@ -3513,6 +3513,7 @@ done: device_pm_remove(dev); dpm_sysfs_remove(dev); DPMError: + dev->driver = NULL; bus_remove_device(dev); BusError: device_remove_attrs(dev); -- cgit v1.2.3 From 504fa212d7030fb1c042290dc2eb92b21515573a Mon Sep 17 00:00:00 2001 From: Javier Martinez Canillas Date: Wed, 28 Dec 2022 00:21:52 +0100 Subject: driver core: Make driver_deferred_probe_timeout a static variable It is not used outside of its compilation unit, so there's no need to export this variable. Signed-off-by: Javier Martinez Canillas Reviewed-by: Andrew Halaney Acked-by: John Stultz Link: https://lore.kernel.org/r/20221227232152.3094584-1-javierm@redhat.com Signed-off-by: Greg Kroah-Hartman --- drivers/base/dd.c | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) (limited to 'drivers/base') diff --git a/drivers/base/dd.c b/drivers/base/dd.c index e9b2f9c25efe..69daf9d414eb 100644 --- a/drivers/base/dd.c +++ b/drivers/base/dd.c @@ -257,13 +257,11 @@ static int deferred_devs_show(struct seq_file *s, void *data) DEFINE_SHOW_ATTRIBUTE(deferred_devs); #ifdef CONFIG_MODULES -int driver_deferred_probe_timeout = 10; +static int driver_deferred_probe_timeout = 10; #else -int driver_deferred_probe_timeout; +static int driver_deferred_probe_timeout; #endif -EXPORT_SYMBOL_GPL(driver_deferred_probe_timeout); - static int __init deferred_probe_timeout_setup(char *str) { int timeout; -- cgit v1.2.3 From 7bbb89b420d9e290cb34864832de8fcdf2c140dc Mon Sep 17 00:00:00 2001 From: Greg Kroah-Hartman Date: Wed, 11 Jan 2023 10:33:27 +0100 Subject: driver core: change to_subsys_private() to use container_of_const() The macro to_subsys_private() needs to switch to using container_of_const() as it turned out to being incorrectly casting a const pointer to a non-const one. Make this change and fix up the one offending user to be correctly handling a const pointer properly. Cc: "Rafael J. Wysocki" Reviewed-by: Rafael J. Wysocki Signed-off-by: Greg Kroah-Hartman Link: https://lore.kernel.org/r/20230111093327.3955063-1-gregkh@linuxfoundation.org Signed-off-by: Greg Kroah-Hartman --- drivers/base/base.h | 2 +- drivers/base/class.c | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) (limited to 'drivers/base') diff --git a/drivers/base/base.h b/drivers/base/base.h index 7d4803c03d3e..2eb722d89f10 100644 --- a/drivers/base/base.h +++ b/drivers/base/base.h @@ -53,7 +53,7 @@ struct subsys_private { struct kset glue_dirs; struct class *class; }; -#define to_subsys_private(obj) container_of(obj, struct subsys_private, subsys.kobj) +#define to_subsys_private(obj) container_of_const(obj, struct subsys_private, subsys.kobj) struct driver_private { struct kobject kobj; diff --git a/drivers/base/class.c b/drivers/base/class.c index 86ec554cfe60..3366da9c3ff6 100644 --- a/drivers/base/class.c +++ b/drivers/base/class.c @@ -64,7 +64,7 @@ static void class_release(struct kobject *kobj) static const struct kobj_ns_type_operations *class_child_ns_type(const struct kobject *kobj) { - struct subsys_private *cp = to_subsys_private(kobj); + const struct subsys_private *cp = to_subsys_private(kobj); struct class *class = cp->class; return class->ns_type; -- cgit v1.2.3 From 5c5a7680e67ba6fbbb5f4d79fa41485450c1985c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Uwe=20Kleine-K=C3=B6nig?= Date: Fri, 9 Dec 2022 16:09:14 +0100 Subject: platform: Provide a remove callback that returns no value MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit struct platform_driver::remove returning an integer made driver authors expect that returning an error code was proper error handling. However the driver core ignores the error and continues to remove the device because there is nothing the core could do anyhow and reentering the remove callback again is only calling for trouble. So this is an source for errors typically yielding resource leaks in the error path. As there are too many platform drivers to neatly convert them all to return void in a single go, do it in several steps after this patch: a) Convert all drivers to implement .remove_new() returning void instead of .remove() returning int; b) Change struct platform_driver::remove() to return void and so make it identical to .remove_new(); c) Change all drivers back to .remove() now with the better prototype; d) drop struct platform_driver::remove_new(). While this touches all drivers eventually twice, steps a) and c) can be done one driver after another and so reduces coordination efforts immensely and simplifies review. Signed-off-by: Uwe Kleine-König Link: https://lore.kernel.org/r/20221209150914.3557650-1-u.kleine-koenig@pengutronix.de Signed-off-by: Greg Kroah-Hartman --- drivers/base/platform.c | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) (limited to 'drivers/base') diff --git a/drivers/base/platform.c b/drivers/base/platform.c index 968f3d71eeab..a4938d1c8fe1 100644 --- a/drivers/base/platform.c +++ b/drivers/base/platform.c @@ -1416,7 +1416,9 @@ static void platform_remove(struct device *_dev) struct platform_driver *drv = to_platform_driver(_dev->driver); struct platform_device *dev = to_platform_device(_dev); - if (drv->remove) { + if (drv->remove_new) { + drv->remove_new(dev); + } else if (drv->remove) { int ret = drv->remove(dev); if (ret) -- cgit v1.2.3 From d903bca1897e27e6bebb3522d44697c478cc0c4b Mon Sep 17 00:00:00 2001 From: Andy Shevchenko Date: Wed, 28 Dec 2022 11:49:21 +0200 Subject: software node: Switch property entry test to a new API Switch property entry test to use software_node_register_node_group() API. The current one is going to be removed soon. Signed-off-by: Andy Shevchenko Acked-by: Greg Kroah-Hartman Acked-by: Sakari Ailus Tested-by: Daniel Scally Acked-by: Heikki Krogerus Link: https://lore.kernel.org/r/20221228094922.84119-4-andriy.shevchenko@linux.intel.com Signed-off-by: Greg Kroah-Hartman --- drivers/base/test/property-entry-test.c | 30 ++++++++++++++---------------- 1 file changed, 14 insertions(+), 16 deletions(-) (limited to 'drivers/base') diff --git a/drivers/base/test/property-entry-test.c b/drivers/base/test/property-entry-test.c index 6071d5bc128c..dd2b606d76a3 100644 --- a/drivers/base/test/property-entry-test.c +++ b/drivers/base/test/property-entry-test.c @@ -405,20 +405,18 @@ static void pe_test_move_inline_str(struct kunit *test) /* Handling of reference properties */ static void pe_test_reference(struct kunit *test) { - static const struct software_node nodes[] = { - { .name = "1", }, - { .name = "2", }, - { } - }; + static const struct software_node node1 = { .name = "1" }; + static const struct software_node node2 = { .name = "2" }; + static const struct software_node *group[] = { &node1, &node2, NULL }; static const struct software_node_ref_args refs[] = { - SOFTWARE_NODE_REFERENCE(&nodes[0]), - SOFTWARE_NODE_REFERENCE(&nodes[1], 3, 4), + SOFTWARE_NODE_REFERENCE(&node1), + SOFTWARE_NODE_REFERENCE(&node2, 3, 4), }; const struct property_entry entries[] = { - PROPERTY_ENTRY_REF("ref-1", &nodes[0]), - PROPERTY_ENTRY_REF("ref-2", &nodes[1], 1, 2), + PROPERTY_ENTRY_REF("ref-1", &node1), + PROPERTY_ENTRY_REF("ref-2", &node2, 1, 2), PROPERTY_ENTRY_REF_ARRAY("ref-3", refs), { } }; @@ -427,7 +425,7 @@ static void pe_test_reference(struct kunit *test) struct fwnode_reference_args ref; int error; - error = software_node_register_nodes(nodes); + error = software_node_register_node_group(group); KUNIT_ASSERT_EQ(test, error, 0); node = fwnode_create_software_node(entries, NULL); @@ -436,7 +434,7 @@ static void pe_test_reference(struct kunit *test) error = fwnode_property_get_reference_args(node, "ref-1", NULL, 0, 0, &ref); KUNIT_ASSERT_EQ(test, error, 0); - KUNIT_EXPECT_PTR_EQ(test, to_software_node(ref.fwnode), &nodes[0]); + KUNIT_EXPECT_PTR_EQ(test, to_software_node(ref.fwnode), &node1); KUNIT_EXPECT_EQ(test, ref.nargs, 0U); /* wrong index */ @@ -447,7 +445,7 @@ static void pe_test_reference(struct kunit *test) error = fwnode_property_get_reference_args(node, "ref-2", NULL, 1, 0, &ref); KUNIT_ASSERT_EQ(test, error, 0); - KUNIT_EXPECT_PTR_EQ(test, to_software_node(ref.fwnode), &nodes[1]); + KUNIT_EXPECT_PTR_EQ(test, to_software_node(ref.fwnode), &node2); KUNIT_EXPECT_EQ(test, ref.nargs, 1U); KUNIT_EXPECT_EQ(test, ref.args[0], 1LLU); @@ -455,7 +453,7 @@ static void pe_test_reference(struct kunit *test) error = fwnode_property_get_reference_args(node, "ref-2", NULL, 3, 0, &ref); KUNIT_ASSERT_EQ(test, error, 0); - KUNIT_EXPECT_PTR_EQ(test, to_software_node(ref.fwnode), &nodes[1]); + KUNIT_EXPECT_PTR_EQ(test, to_software_node(ref.fwnode), &node2); KUNIT_EXPECT_EQ(test, ref.nargs, 3U); KUNIT_EXPECT_EQ(test, ref.args[0], 1LLU); KUNIT_EXPECT_EQ(test, ref.args[1], 2LLU); @@ -470,14 +468,14 @@ static void pe_test_reference(struct kunit *test) error = fwnode_property_get_reference_args(node, "ref-3", NULL, 0, 0, &ref); KUNIT_ASSERT_EQ(test, error, 0); - KUNIT_EXPECT_PTR_EQ(test, to_software_node(ref.fwnode), &nodes[0]); + KUNIT_EXPECT_PTR_EQ(test, to_software_node(ref.fwnode), &node1); KUNIT_EXPECT_EQ(test, ref.nargs, 0U); /* second reference in the array */ error = fwnode_property_get_reference_args(node, "ref-3", NULL, 2, 1, &ref); KUNIT_ASSERT_EQ(test, error, 0); - KUNIT_EXPECT_PTR_EQ(test, to_software_node(ref.fwnode), &nodes[1]); + KUNIT_EXPECT_PTR_EQ(test, to_software_node(ref.fwnode), &node2); KUNIT_EXPECT_EQ(test, ref.nargs, 2U); KUNIT_EXPECT_EQ(test, ref.args[0], 3LLU); KUNIT_EXPECT_EQ(test, ref.args[1], 4LLU); @@ -488,7 +486,7 @@ static void pe_test_reference(struct kunit *test) KUNIT_EXPECT_NE(test, error, 0); fwnode_remove_software_node(node); - software_node_unregister_nodes(nodes); + software_node_unregister_node_group(group); } static struct kunit_case property_entry_test_cases[] = { -- cgit v1.2.3 From 3dbdd92014a410778de8406e9e3253e353f51c2b Mon Sep 17 00:00:00 2001 From: Andy Shevchenko Date: Wed, 28 Dec 2022 11:49:22 +0200 Subject: software node: Remove unused APIs There are no more users of software_node_register_nodes() and software_node_unregister_nodes(). Remove them. Signed-off-by: Andy Shevchenko Acked-by: Greg Kroah-Hartman Acked-by: Sakari Ailus Tested-by: Daniel Scally Acked-by: Heikki Krogerus Link: https://lore.kernel.org/r/20221228094922.84119-5-andriy.shevchenko@linux.intel.com Signed-off-by: Greg Kroah-Hartman --- drivers/base/swnode.c | 61 --------------------------------------------------- 1 file changed, 61 deletions(-) (limited to 'drivers/base') diff --git a/drivers/base/swnode.c b/drivers/base/swnode.c index 0a482212c7e8..da3c1c2cb9a8 100644 --- a/drivers/base/swnode.c +++ b/drivers/base/swnode.c @@ -819,67 +819,6 @@ swnode_register(const struct software_node *node, struct swnode *parent, return &swnode->fwnode; } -/** - * software_node_register_nodes - Register an array of software nodes - * @nodes: Zero terminated array of software nodes to be registered - * - * Register multiple software nodes at once. If any node in the array - * has its .parent pointer set (which can only be to another software_node), - * then its parent **must** have been registered before it is; either outside - * of this function or by ordering the array such that parent comes before - * child. - */ -int software_node_register_nodes(const struct software_node *nodes) -{ - int ret; - int i; - - for (i = 0; nodes[i].name; i++) { - const struct software_node *parent = nodes[i].parent; - - if (parent && !software_node_to_swnode(parent)) { - ret = -EINVAL; - goto err_unregister_nodes; - } - - ret = software_node_register(&nodes[i]); - if (ret) - goto err_unregister_nodes; - } - - return 0; - -err_unregister_nodes: - software_node_unregister_nodes(nodes); - return ret; -} -EXPORT_SYMBOL_GPL(software_node_register_nodes); - -/** - * software_node_unregister_nodes - Unregister an array of software nodes - * @nodes: Zero terminated array of software nodes to be unregistered - * - * Unregister multiple software nodes at once. If parent pointers are set up - * in any of the software nodes then the array **must** be ordered such that - * parents come before their children. - * - * NOTE: If you are uncertain whether the array is ordered such that - * parents will be unregistered before their children, it is wiser to - * remove the nodes individually, in the correct order (child before - * parent). - */ -void software_node_unregister_nodes(const struct software_node *nodes) -{ - unsigned int i = 0; - - while (nodes[i].name) - i++; - - while (i--) - software_node_unregister(&nodes[i]); -} -EXPORT_SYMBOL_GPL(software_node_unregister_nodes); - /** * software_node_register_node_group - Register a group of software nodes * @node_group: NULL terminated array of software node pointers to be registered -- cgit v1.2.3 From 64f797423379a333e3ba5373ee01353d7029727e Mon Sep 17 00:00:00 2001 From: Umang Jain Date: Tue, 20 Dec 2022 14:21:16 +0530 Subject: platform: Document platform_add_devices() return value platform_add_devices() returns 0 on success and negative errno on failure. Document it. Signed-off-by: Umang Jain Link: https://lore.kernel.org/r/20221220085116.19837-1-umang.jain@ideasonboard.com Signed-off-by: Greg Kroah-Hartman --- drivers/base/platform.c | 2 ++ 1 file changed, 2 insertions(+) (limited to 'drivers/base') diff --git a/drivers/base/platform.c b/drivers/base/platform.c index a4938d1c8fe1..d6690d705314 100644 --- a/drivers/base/platform.c +++ b/drivers/base/platform.c @@ -499,6 +499,8 @@ EXPORT_SYMBOL_GPL(platform_get_irq_byname_optional); * platform_add_devices - add a numbers of platform devices * @devs: array of platform devices to add * @num: number of platform devices in array + * + * Return: 0 on success, negative error number on failure. */ int platform_add_devices(struct platform_device **devs, int num) { -- cgit v1.2.3 From 9dd4541b1693e9aa124a167b40e294aa6906433f Mon Sep 17 00:00:00 2001 From: Soha Jin Date: Fri, 11 Nov 2022 17:45:42 +0800 Subject: platform: remove useless if-branch in __platform_get_irq_byname() When CONFIG_OF_IRQ is not enabled, there will be a stub method that always returns 0 when getting IRQ. Thus, the if-branch can be removed safely. Signed-off-by: Soha Jin Link: https://lore.kernel.org/r/20221111094542.270540-1-soha@lohu.info Signed-off-by: Greg Kroah-Hartman --- drivers/base/platform.c | 8 +++----- 1 file changed, 3 insertions(+), 5 deletions(-) (limited to 'drivers/base') diff --git a/drivers/base/platform.c b/drivers/base/platform.c index d6690d705314..b10a130cd433 100644 --- a/drivers/base/platform.c +++ b/drivers/base/platform.c @@ -441,11 +441,9 @@ static int __platform_get_irq_byname(struct platform_device *dev, struct resource *r; int ret; - if (!dev->dev.of_node || IS_ENABLED(CONFIG_OF_IRQ)) { - ret = fwnode_irq_get_byname(dev_fwnode(&dev->dev), name); - if (ret > 0 || ret == -EPROBE_DEFER) - return ret; - } + ret = fwnode_irq_get_byname(dev_fwnode(&dev->dev), name); + if (ret > 0 || ret == -EPROBE_DEFER) + return ret; r = platform_get_resource_byname(dev, IORESOURCE_IRQ, name); if (r) { -- cgit v1.2.3 From c3719bd9eeb2edf84bd263d662e36ca0ba262a23 Mon Sep 17 00:00:00 2001 From: Pierre Gondois Date: Wed, 4 Jan 2023 19:30:24 +0100 Subject: cacheinfo: Use RISC-V's init_cache_level() as generic OF implementation RISC-V's implementation of init_of_cache_level() is following the Devicetree Specification v0.3 regarding caches, cf.: - s3.7.3 'Internal (L1) Cache Properties' - s3.8 'Multi-level and Shared Cache Nodes' Allow reusing the implementation by moving it. Also make 'levels', 'leaves' and 'level' unsigned int. Signed-off-by: Pierre Gondois Reviewed-by: Conor Dooley Acked-by: Palmer Dabbelt Link: https://lore.kernel.org/r/20230104183033.755668-2-pierre.gondois@arm.com Signed-off-by: Sudeep Holla --- drivers/base/cacheinfo.c | 44 ++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 44 insertions(+) (limited to 'drivers/base') diff --git a/drivers/base/cacheinfo.c b/drivers/base/cacheinfo.c index 950b22cdb5f7..b60788aacfef 100644 --- a/drivers/base/cacheinfo.c +++ b/drivers/base/cacheinfo.c @@ -229,8 +229,52 @@ static int cache_setup_of_node(unsigned int cpu) return 0; } + +int init_of_cache_level(unsigned int cpu) +{ + struct cpu_cacheinfo *this_cpu_ci = get_cpu_cacheinfo(cpu); + struct device_node *np = of_cpu_device_node_get(cpu); + struct device_node *prev = NULL; + unsigned int levels = 0, leaves = 0, level; + + if (of_property_read_bool(np, "cache-size")) + ++leaves; + if (of_property_read_bool(np, "i-cache-size")) + ++leaves; + if (of_property_read_bool(np, "d-cache-size")) + ++leaves; + if (leaves > 0) + levels = 1; + + prev = np; + while ((np = of_find_next_cache_node(np))) { + of_node_put(prev); + prev = np; + if (!of_device_is_compatible(np, "cache")) + break; + if (of_property_read_u32(np, "cache-level", &level)) + break; + if (level <= levels) + break; + if (of_property_read_bool(np, "cache-size")) + ++leaves; + if (of_property_read_bool(np, "i-cache-size")) + ++leaves; + if (of_property_read_bool(np, "d-cache-size")) + ++leaves; + levels = level; + } + + of_node_put(np); + this_cpu_ci->num_levels = levels; + this_cpu_ci->num_leaves = leaves; + + return 0; +} + #else static inline int cache_setup_of_node(unsigned int cpu) { return 0; } +int init_of_cache_level(unsigned int cpu) { return 0; } #endif int __weak cache_setup_acpi(unsigned int cpu) -- cgit v1.2.3 From 8844c3df001bc1d8397fddea341308da63855d53 Mon Sep 17 00:00:00 2001 From: Pierre Gondois Date: Wed, 4 Jan 2023 19:30:25 +0100 Subject: cacheinfo: Return error code in init_of_cache_level() Make init_of_cache_level() return an error code when the cache information parsing fails to help detecting missing information. init_of_cache_level() is only called for riscv. Returning an error code instead of 0 will prevent detect_cache_attributes() to allocate memory if an incomplete DT is parsed. Signed-off-by: Pierre Gondois Acked-by: Palmer Dabbelt Link: https://lore.kernel.org/r/20230104183033.755668-3-pierre.gondois@arm.com Signed-off-by: Sudeep Holla --- drivers/base/cacheinfo.c | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) (limited to 'drivers/base') diff --git a/drivers/base/cacheinfo.c b/drivers/base/cacheinfo.c index b60788aacfef..0a96d0b9338a 100644 --- a/drivers/base/cacheinfo.c +++ b/drivers/base/cacheinfo.c @@ -251,11 +251,11 @@ int init_of_cache_level(unsigned int cpu) of_node_put(prev); prev = np; if (!of_device_is_compatible(np, "cache")) - break; + goto err_out; if (of_property_read_u32(np, "cache-level", &level)) - break; + goto err_out; if (level <= levels) - break; + goto err_out; if (of_property_read_bool(np, "cache-size")) ++leaves; if (of_property_read_bool(np, "i-cache-size")) @@ -270,6 +270,10 @@ int init_of_cache_level(unsigned int cpu) this_cpu_ci->num_leaves = leaves; return 0; + +err_out: + of_node_put(np); + return -EINVAL; } #else -- cgit v1.2.3 From ed9f918174cb35ba51d2fc86a613305dd8bc4cfe Mon Sep 17 00:00:00 2001 From: Greg Kroah-Hartman Date: Wed, 11 Jan 2023 10:23:31 +0100 Subject: driver core: bus: move bus notifier logic into bus.c The logic to touch the bus notifier was open-coded in numberous places in the driver core. Clean that up by creating a local bus_notify() function and have everyone call this function instead, making the reading of the caller code simpler and easier to maintain over time. Reviewed-by: Rafael J. Wysocki Link: https://lore.kernel.org/r/20230111092331.3946745-2-gregkh@linuxfoundation.org Signed-off-by: Greg Kroah-Hartman --- drivers/base/base.h | 1 + drivers/base/bus.c | 8 ++++++++ drivers/base/core.c | 13 +++---------- drivers/base/dd.c | 28 +++++++--------------------- 4 files changed, 19 insertions(+), 31 deletions(-) (limited to 'drivers/base') diff --git a/drivers/base/base.h b/drivers/base/base.h index 2eb722d89f10..3d1da1027206 100644 --- a/drivers/base/base.h +++ b/drivers/base/base.h @@ -130,6 +130,7 @@ struct kobject *virtual_device_parent(struct device *dev); extern int bus_add_device(struct device *dev); extern void bus_probe_device(struct device *dev); extern void bus_remove_device(struct device *dev); +void bus_notify(struct device *dev, enum bus_notifier_event value); extern int bus_add_driver(struct device_driver *drv); extern void bus_remove_driver(struct device_driver *drv); diff --git a/drivers/base/bus.c b/drivers/base/bus.c index 428c26c6b615..cf1b8f00b4c0 100644 --- a/drivers/base/bus.c +++ b/drivers/base/bus.c @@ -850,6 +850,14 @@ int bus_unregister_notifier(struct bus_type *bus, struct notifier_block *nb) } EXPORT_SYMBOL_GPL(bus_unregister_notifier); +void bus_notify(struct device *dev, enum bus_notifier_event value) +{ + struct bus_type *bus = dev->bus; + + if (bus) + blocking_notifier_call_chain(&bus->p->bus_notifier, value, dev); +} + struct kset *bus_get_kset(struct bus_type *bus) { return &bus->p->subsys; diff --git a/drivers/base/core.c b/drivers/base/core.c index 506289e6b04d..110dbd4fb570 100644 --- a/drivers/base/core.c +++ b/drivers/base/core.c @@ -3453,10 +3453,7 @@ int device_add(struct device *dev) /* Notify clients of device addition. This call must come * after dpm_sysfs_add() and before kobject_uevent(). */ - if (dev->bus) - blocking_notifier_call_chain(&dev->bus->p->bus_notifier, - BUS_NOTIFY_ADD_DEVICE, dev); - + bus_notify(dev, BUS_NOTIFY_ADD_DEVICE); kobject_uevent(&dev->kobj, KOBJ_ADD); /* @@ -3637,9 +3634,7 @@ void device_del(struct device *dev) * before dpm_sysfs_remove(). */ noio_flag = memalloc_noio_save(); - if (dev->bus) - blocking_notifier_call_chain(&dev->bus->p->bus_notifier, - BUS_NOTIFY_DEL_DEVICE, dev); + bus_notify(dev, BUS_NOTIFY_DEL_DEVICE); dpm_sysfs_remove(dev); if (parent) @@ -3670,9 +3665,7 @@ void device_del(struct device *dev) device_platform_notify_remove(dev); device_links_purge(dev); - if (dev->bus) - blocking_notifier_call_chain(&dev->bus->p->bus_notifier, - BUS_NOTIFY_REMOVED_DEVICE, dev); + bus_notify(dev, BUS_NOTIFY_REMOVED_DEVICE); kobject_uevent(&dev->kobj, KOBJ_REMOVE); glue_dir = get_glue_dir(dev); kobject_del(&dev->kobj); diff --git a/drivers/base/dd.c b/drivers/base/dd.c index 69daf9d414eb..817ef27a78f7 100644 --- a/drivers/base/dd.c +++ b/drivers/base/dd.c @@ -411,10 +411,7 @@ static void driver_bound(struct device *dev) driver_deferred_probe_del(dev); driver_deferred_probe_trigger(); - if (dev->bus) - blocking_notifier_call_chain(&dev->bus->p->bus_notifier, - BUS_NOTIFY_BOUND_DRIVER, dev); - + bus_notify(dev, BUS_NOTIFY_BOUND_DRIVER); kobject_uevent(&dev->kobj, KOBJ_BIND); } @@ -433,9 +430,7 @@ static int driver_sysfs_add(struct device *dev) { int ret; - if (dev->bus) - blocking_notifier_call_chain(&dev->bus->p->bus_notifier, - BUS_NOTIFY_BIND_DRIVER, dev); + bus_notify(dev, BUS_NOTIFY_BIND_DRIVER); ret = sysfs_create_link(&dev->driver->p->kobj, &dev->kobj, kobject_name(&dev->kobj)); @@ -500,9 +495,8 @@ int device_bind_driver(struct device *dev) device_links_force_bind(dev); driver_bound(dev); } - else if (dev->bus) - blocking_notifier_call_chain(&dev->bus->p->bus_notifier, - BUS_NOTIFY_DRIVER_NOT_BOUND, dev); + else + bus_notify(dev, BUS_NOTIFY_DRIVER_NOT_BOUND); return ret; } EXPORT_SYMBOL_GPL(device_bind_driver); @@ -693,9 +687,7 @@ dev_groups_failed: probe_failed: driver_sysfs_remove(dev); sysfs_failed: - if (dev->bus) - blocking_notifier_call_chain(&dev->bus->p->bus_notifier, - BUS_NOTIFY_DRIVER_NOT_BOUND, dev); + bus_notify(dev, BUS_NOTIFY_DRIVER_NOT_BOUND); if (dev->bus && dev->bus->dma_cleanup) dev->bus->dma_cleanup(dev); pinctrl_bind_failed: @@ -1241,10 +1233,7 @@ static void __device_release_driver(struct device *dev, struct device *parent) driver_sysfs_remove(dev); - if (dev->bus) - blocking_notifier_call_chain(&dev->bus->p->bus_notifier, - BUS_NOTIFY_UNBIND_DRIVER, - dev); + bus_notify(dev, BUS_NOTIFY_UNBIND_DRIVER); pm_runtime_put_sync(dev); @@ -1258,11 +1247,8 @@ static void __device_release_driver(struct device *dev, struct device *parent) klist_remove(&dev->p->knode_driver); device_pm_check_callbacks(dev); - if (dev->bus) - blocking_notifier_call_chain(&dev->bus->p->bus_notifier, - BUS_NOTIFY_UNBOUND_DRIVER, - dev); + bus_notify(dev, BUS_NOTIFY_UNBOUND_DRIVER); kobject_uevent(&dev->kobj, KOBJ_UNBIND); } } -- cgit v1.2.3 From de0df442ee49cb1f6ee58f3fec5dcb5e5eb70aab Mon Sep 17 00:00:00 2001 From: Pierre Gondois Date: Wed, 4 Jan 2023 19:30:26 +0100 Subject: cacheinfo: Check 'cache-unified' property to count cache leaves The DeviceTree Specification v0.3 specifies that the cache node '[d-|i-|]cache-size' property is required. The 'cache-unified' property is specifies whether the cache level is separate or unified. If the cache-size property is missing, no cache leaves is accounted. This can lead to a 'BUG: KASAN: slab-out-of-bounds' [1] bug. Check 'cache-unified' property and always account for at least one cache leaf when parsing the device tree. [1] https://lore.kernel.org/all/0f19cb3f-d6cf-4032-66d2-dedc9d09a0e3@linaro.org/ Reported-by: Krzysztof Kozlowski Signed-off-by: Pierre Gondois Tested-by: Krzysztof Kozlowski Link: https://lore.kernel.org/r/20230104183033.755668-4-pierre.gondois@arm.com Signed-off-by: Sudeep Holla --- drivers/base/cacheinfo.c | 37 ++++++++++++++++++++++++++----------- 1 file changed, 26 insertions(+), 11 deletions(-) (limited to 'drivers/base') diff --git a/drivers/base/cacheinfo.c b/drivers/base/cacheinfo.c index 0a96d0b9338a..9ad4403f2dab 100644 --- a/drivers/base/cacheinfo.c +++ b/drivers/base/cacheinfo.c @@ -230,12 +230,9 @@ static int cache_setup_of_node(unsigned int cpu) return 0; } -int init_of_cache_level(unsigned int cpu) +static int of_count_cache_leaves(struct device_node *np) { - struct cpu_cacheinfo *this_cpu_ci = get_cpu_cacheinfo(cpu); - struct device_node *np = of_cpu_device_node_get(cpu); - struct device_node *prev = NULL; - unsigned int levels = 0, leaves = 0, level; + unsigned int leaves = 0; if (of_property_read_bool(np, "cache-size")) ++leaves; @@ -243,6 +240,28 @@ int init_of_cache_level(unsigned int cpu) ++leaves; if (of_property_read_bool(np, "d-cache-size")) ++leaves; + + if (!leaves) { + /* The '[i-|d-|]cache-size' property is required, but + * if absent, fallback on the 'cache-unified' property. + */ + if (of_property_read_bool(np, "cache-unified")) + return 1; + else + return 2; + } + + return leaves; +} + +int init_of_cache_level(unsigned int cpu) +{ + struct cpu_cacheinfo *this_cpu_ci = get_cpu_cacheinfo(cpu); + struct device_node *np = of_cpu_device_node_get(cpu); + struct device_node *prev = NULL; + unsigned int levels = 0, leaves, level; + + leaves = of_count_cache_leaves(np); if (leaves > 0) levels = 1; @@ -256,12 +275,8 @@ int init_of_cache_level(unsigned int cpu) goto err_out; if (level <= levels) goto err_out; - if (of_property_read_bool(np, "cache-size")) - ++leaves; - if (of_property_read_bool(np, "i-cache-size")) - ++leaves; - if (of_property_read_bool(np, "d-cache-size")) - ++leaves; + + leaves += of_count_cache_leaves(np); levels = level; } -- cgit v1.2.3 From 5944ce092b97caed5d86d961e963b883b5c44ee2 Mon Sep 17 00:00:00 2001 From: Pierre Gondois Date: Wed, 4 Jan 2023 19:30:29 +0100 Subject: arch_topology: Build cacheinfo from primary CPU commit 3fcbf1c77d08 ("arch_topology: Fix cache attributes detection in the CPU hotplug path") adds a call to detect_cache_attributes() to populate the cacheinfo before updating the siblings mask. detect_cache_attributes() allocates memory and can take the PPTT mutex (on ACPI platforms). On PREEMPT_RT kernels, on secondary CPUs, this triggers a: 'BUG: sleeping function called from invalid context' [1] as the code is executed with preemption and interrupts disabled. The primary CPU was previously storing the cache information using the now removed (struct cpu_topology).llc_id: commit 5b8dc787ce4a ("arch_topology: Drop LLC identifier stash from the CPU topology") allocate_cache_info() tries to build the cacheinfo from the primary CPU prior secondary CPUs boot, if the DT/ACPI description contains cache information. If allocate_cache_info() fails, then fallback to the current state for the cacheinfo allocation. [1] will be triggered in such case. When unplugging a CPU, the cacheinfo memory cannot be freed. If it was, then the memory would be allocated early by the re-plugged CPU and would trigger [1]. Note that populate_cache_leaves() might be called multiple times due to populate_leaves being moved up. This is required since detect_cache_attributes() might be called with per_cpu_cacheinfo(cpu) being allocated but not populated. [1]: | BUG: sleeping function called from invalid context at kernel/locking/spinlock_rt.c:46 | in_atomic(): 1, irqs_disabled(): 128, non_block: 0, pid: 0, name: swapper/111 | preempt_count: 1, expected: 0 | RCU nest depth: 1, expected: 1 | 3 locks held by swapper/111/0: | #0: (&pcp->lock){+.+.}-{3:3}, at: get_page_from_freelist+0x218/0x12c8 | #1: (rcu_read_lock){....}-{1:3}, at: rt_spin_trylock+0x48/0xf0 | #2: (&zone->lock){+.+.}-{3:3}, at: rmqueue_bulk+0x64/0xa80 | irq event stamp: 0 | hardirqs last enabled at (0): 0x0 | hardirqs last disabled at (0): copy_process+0x5dc/0x1ab8 | softirqs last enabled at (0): copy_process+0x5dc/0x1ab8 | softirqs last disabled at (0): 0x0 | Preemption disabled at: | migrate_enable+0x30/0x130 | CPU: 111 PID: 0 Comm: swapper/111 Tainted: G W 6.0.0-rc4-rt6-[...] | Call trace: | __kmalloc+0xbc/0x1e8 | detect_cache_attributes+0x2d4/0x5f0 | update_siblings_masks+0x30/0x368 | store_cpu_topology+0x78/0xb8 | secondary_start_kernel+0xd0/0x198 | __secondary_switched+0xb0/0xb4 Signed-off-by: Pierre Gondois Reviewed-by: Sudeep Holla Acked-by: Palmer Dabbelt Link: https://lore.kernel.org/r/20230104183033.755668-7-pierre.gondois@arm.com Signed-off-by: Sudeep Holla --- drivers/base/arch_topology.c | 12 ++++++-- drivers/base/cacheinfo.c | 71 +++++++++++++++++++++++++++++++++----------- 2 files changed, 64 insertions(+), 19 deletions(-) (limited to 'drivers/base') diff --git a/drivers/base/arch_topology.c b/drivers/base/arch_topology.c index e7d6e6657ffa..b1c1dd38ab01 100644 --- a/drivers/base/arch_topology.c +++ b/drivers/base/arch_topology.c @@ -736,7 +736,7 @@ void update_siblings_masks(unsigned int cpuid) ret = detect_cache_attributes(cpuid); if (ret && ret != -ENOENT) - pr_info("Early cacheinfo failed, ret = %d\n", ret); + pr_info("Early cacheinfo allocation failed, ret = %d\n", ret); /* update core and thread sibling masks */ for_each_online_cpu(cpu) { @@ -825,7 +825,7 @@ __weak int __init parse_acpi_topology(void) #if defined(CONFIG_ARM64) || defined(CONFIG_RISCV) void __init init_cpu_topology(void) { - int ret; + int cpu, ret; reset_cpu_topology(); ret = parse_acpi_topology(); @@ -840,6 +840,14 @@ void __init init_cpu_topology(void) reset_cpu_topology(); return; } + + for_each_possible_cpu(cpu) { + ret = fetch_cache_info(cpu); + if (ret) { + pr_err("Early cacheinfo failed, ret = %d\n", ret); + break; + } + } } void store_cpu_topology(unsigned int cpuid) diff --git a/drivers/base/cacheinfo.c b/drivers/base/cacheinfo.c index 9ad4403f2dab..c9bcecf76c7f 100644 --- a/drivers/base/cacheinfo.c +++ b/drivers/base/cacheinfo.c @@ -389,10 +389,6 @@ static void free_cache_attributes(unsigned int cpu) return; cache_shared_cpu_map_remove(cpu); - - kfree(per_cpu_cacheinfo(cpu)); - per_cpu_cacheinfo(cpu) = NULL; - cache_leaves(cpu) = 0; } int __weak init_cache_level(unsigned int cpu) @@ -405,29 +401,71 @@ int __weak populate_cache_leaves(unsigned int cpu) return -ENOENT; } +static inline +int allocate_cache_info(int cpu) +{ + per_cpu_cacheinfo(cpu) = kcalloc(cache_leaves(cpu), + sizeof(struct cacheinfo), GFP_ATOMIC); + if (!per_cpu_cacheinfo(cpu)) { + cache_leaves(cpu) = 0; + return -ENOMEM; + } + + return 0; +} + +int fetch_cache_info(unsigned int cpu) +{ + struct cpu_cacheinfo *this_cpu_ci; + unsigned int levels, split_levels; + int ret; + + if (acpi_disabled) { + ret = init_of_cache_level(cpu); + if (ret < 0) + return ret; + } else { + ret = acpi_get_cache_info(cpu, &levels, &split_levels); + if (ret < 0) + return ret; + + this_cpu_ci = get_cpu_cacheinfo(cpu); + this_cpu_ci->num_levels = levels; + /* + * This assumes that: + * - there cannot be any split caches (data/instruction) + * above a unified cache + * - data/instruction caches come by pair + */ + this_cpu_ci->num_leaves = levels + split_levels; + } + if (!cache_leaves(cpu)) + return -ENOENT; + + return allocate_cache_info(cpu); +} + int detect_cache_attributes(unsigned int cpu) { int ret; - /* Since early detection of the cacheinfo is allowed via this - * function and this also gets called as CPU hotplug callbacks via - * cacheinfo_cpu_online, the initialisation can be skipped and only - * CPU maps can be updated as the CPU online status would be update - * if called via cacheinfo_cpu_online path. + /* Since early initialization/allocation of the cacheinfo is allowed + * via fetch_cache_info() and this also gets called as CPU hotplug + * callbacks via cacheinfo_cpu_online, the init/alloc can be skipped + * as it will happen only once (the cacheinfo memory is never freed). + * Just populate the cacheinfo. */ if (per_cpu_cacheinfo(cpu)) - goto update_cpu_map; + goto populate_leaves; if (init_cache_level(cpu) || !cache_leaves(cpu)) return -ENOENT; - per_cpu_cacheinfo(cpu) = kcalloc(cache_leaves(cpu), - sizeof(struct cacheinfo), GFP_ATOMIC); - if (per_cpu_cacheinfo(cpu) == NULL) { - cache_leaves(cpu) = 0; - return -ENOMEM; - } + ret = allocate_cache_info(cpu); + if (ret) + return ret; +populate_leaves: /* * populate_cache_leaves() may completely setup the cache leaves and * shared_cpu_map or it may leave it partially setup. @@ -436,7 +474,6 @@ int detect_cache_attributes(unsigned int cpu) if (ret) goto free_ci; -update_cpu_map: /* * For systems using DT for cache hierarchy, fw_token * and shared_cpu_map will be set up here only if they are -- cgit v1.2.3 From 198102c9103fc78d8478495971947af77edb05c1 Mon Sep 17 00:00:00 2001 From: Yong-Xuan Wang Date: Tue, 17 Jan 2023 10:51:33 +0000 Subject: cacheinfo: Fix shared_cpu_map to handle shared caches at different levels The cacheinfo sets up the shared_cpu_map by checking whether the caches with the same index are shared between CPUs. However, this will trigger slab-out-of-bounds access if the CPUs do not have the same cache hierarchy. Another problem is the mismatched shared_cpu_map when the shared cache does not have the same index between CPUs. CPU0 I D L3 index 0 1 2 x ^ ^ ^ ^ index 0 1 2 3 CPU1 I D L2 L3 This patch checks each cache is shared with all caches on other CPUs. Reviewed-by: Pierre Gondois Signed-off-by: Yong-Xuan Wang Link: https://lore.kernel.org/r/20230117105133.4445-2-yongxuan.wang@sifive.com Signed-off-by: Sudeep Holla --- drivers/base/cacheinfo.c | 27 +++++++++++++++++---------- 1 file changed, 17 insertions(+), 10 deletions(-) (limited to 'drivers/base') diff --git a/drivers/base/cacheinfo.c b/drivers/base/cacheinfo.c index c9bcecf76c7f..418a18acc8f9 100644 --- a/drivers/base/cacheinfo.c +++ b/drivers/base/cacheinfo.c @@ -319,7 +319,7 @@ static int cache_shared_cpu_map_setup(unsigned int cpu) { struct cpu_cacheinfo *this_cpu_ci = get_cpu_cacheinfo(cpu); struct cacheinfo *this_leaf, *sib_leaf; - unsigned int index; + unsigned int index, sib_index; int ret = 0; if (this_cpu_ci->cpu_map_populated) @@ -347,11 +347,13 @@ static int cache_shared_cpu_map_setup(unsigned int cpu) if (i == cpu || !sib_cpu_ci->info_list) continue;/* skip if itself or no cacheinfo */ - - sib_leaf = per_cpu_cacheinfo_idx(i, index); - if (cache_leaves_are_shared(this_leaf, sib_leaf)) { - cpumask_set_cpu(cpu, &sib_leaf->shared_cpu_map); - cpumask_set_cpu(i, &this_leaf->shared_cpu_map); + for (sib_index = 0; sib_index < cache_leaves(i); sib_index++) { + sib_leaf = per_cpu_cacheinfo_idx(i, sib_index); + if (cache_leaves_are_shared(this_leaf, sib_leaf)) { + cpumask_set_cpu(cpu, &sib_leaf->shared_cpu_map); + cpumask_set_cpu(i, &this_leaf->shared_cpu_map); + break; + } } } /* record the maximum cache line size */ @@ -365,7 +367,7 @@ static int cache_shared_cpu_map_setup(unsigned int cpu) static void cache_shared_cpu_map_remove(unsigned int cpu) { struct cacheinfo *this_leaf, *sib_leaf; - unsigned int sibling, index; + unsigned int sibling, index, sib_index; for (index = 0; index < cache_leaves(cpu); index++) { this_leaf = per_cpu_cacheinfo_idx(cpu, index); @@ -376,9 +378,14 @@ static void cache_shared_cpu_map_remove(unsigned int cpu) if (sibling == cpu || !sib_cpu_ci->info_list) continue;/* skip if itself or no cacheinfo */ - sib_leaf = per_cpu_cacheinfo_idx(sibling, index); - cpumask_clear_cpu(cpu, &sib_leaf->shared_cpu_map); - cpumask_clear_cpu(sibling, &this_leaf->shared_cpu_map); + for (sib_index = 0; sib_index < cache_leaves(sibling); sib_index++) { + sib_leaf = per_cpu_cacheinfo_idx(sibling, sib_index); + if (cache_leaves_are_shared(this_leaf, sib_leaf)) { + cpumask_clear_cpu(cpu, &sib_leaf->shared_cpu_map); + cpumask_clear_cpu(sibling, &this_leaf->shared_cpu_map); + break; + } + } } } } -- cgit v1.2.3 From 7c09f4281cb6ec0fc202f53924ed6c389c61bf0e Mon Sep 17 00:00:00 2001 From: Gavin Shan Date: Fri, 20 Jan 2023 13:57:26 +0800 Subject: drivers/base/memory: Fix comments for phys_index_show() According to 'admin-guide/mm/memory-hotplug.rst', the memory block ID, instead of the section index, is shown by '/sys/devices/system/memory/ memoryX/phys_index'. Fix the comments to match with 'admin-guide/mm/memory-hotplug.rst'. Besides, use the existing helper memory_block_id() to convert the section index to the memory block index. No functional change intended. Signed-off-by: Gavin Shan Link: https://lore.kernel.org/r/20230120055727.355483-2-gshan@redhat.com Signed-off-by: Greg Kroah-Hartman --- drivers/base/memory.c | 9 ++------- 1 file changed, 2 insertions(+), 7 deletions(-) (limited to 'drivers/base') diff --git a/drivers/base/memory.c b/drivers/base/memory.c index fe98fb8d94e5..b456ac213610 100644 --- a/drivers/base/memory.c +++ b/drivers/base/memory.c @@ -115,18 +115,13 @@ unsigned long __weak memory_block_size_bytes(void) } EXPORT_SYMBOL_GPL(memory_block_size_bytes); -/* - * Show the first physical section index (number) of this memory block. - */ +/* Show the memory block ID, relative to the memory block size */ static ssize_t phys_index_show(struct device *dev, struct device_attribute *attr, char *buf) { struct memory_block *mem = to_memory_block(dev); - unsigned long phys_index; - - phys_index = mem->start_section_nr / sections_per_block; - return sysfs_emit(buf, "%08lx\n", phys_index); + return sysfs_emit(buf, "%08lx\n", memory_block_id(mem->start_section_nr)); } /* -- cgit v1.2.3 From 6977b1a5d67097eaa4d02b0c126c04cc6e8917c0 Mon Sep 17 00:00:00 2001 From: Zhengchao Shao Date: Wed, 23 Nov 2022 09:20:42 +0800 Subject: driver core: fix resource leak in device_add() When calling kobject_add() failed in device_add(), it will call cleanup_glue_dir() to free resource. But in kobject_add(), dev->kobj.parent has been set to NULL. This will cause resource leak. The process is as follows: device_add() get_device_parent() class_dir_create_and_add() kobject_add() //kobject_get() ... dev->kobj.parent = kobj; ... kobject_add() //failed, but set dev->kobj.parent = NULL ... glue_dir = get_glue_dir(dev) //glue_dir = NULL, and goto //"Error" label ... cleanup_glue_dir() //becaues glue_dir is NULL, not call //kobject_put() The preceding problem may cause insmod mac80211_hwsim.ko to failed. sysfs: cannot create duplicate filename '/devices/virtual/mac80211_hwsim' Call Trace: dump_stack_lvl+0x8e/0xd1 sysfs_warn_dup.cold+0x1c/0x29 sysfs_create_dir_ns+0x224/0x280 kobject_add_internal+0x2aa/0x880 kobject_add+0x135/0x1a0 get_device_parent+0x3d7/0x590 device_add+0x2aa/0x1cb0 device_create_groups_vargs+0x1eb/0x260 device_create+0xdc/0x110 mac80211_hwsim_new_radio+0x31e/0x4790 [mac80211_hwsim] init_mac80211_hwsim+0x48d/0x1000 [mac80211_hwsim] do_one_initcall+0x10f/0x630 do_init_module+0x19f/0x5e0 load_module+0x64b7/0x6eb0 __do_sys_finit_module+0x140/0x200 do_syscall_64+0x35/0x80 entry_SYSCALL_64_after_hwframe+0x46/0xb0 kobject_add_internal failed for mac80211_hwsim with -EEXIST, don't try to register things with the same name in the same directory. Fixes: cebf8fd16900 ("driver core: fix race between creating/querying glue dir and its cleanup") Signed-off-by: Zhengchao Shao Link: https://lore.kernel.org/r/20221123012042.335252-1-shaozhengchao@huawei.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 110dbd4fb570..39dbd532cfb3 100644 --- a/drivers/base/core.c +++ b/drivers/base/core.c @@ -3413,7 +3413,7 @@ int device_add(struct device *dev) /* we require the name to be set before, and pass NULL */ error = kobject_add(&dev->kobj, dev->kobj.parent, NULL); if (error) { - glue_dir = get_glue_dir(dev); + glue_dir = kobj; goto Error; } -- cgit v1.2.3 From 0d150f967e8410e1e6712484543eec709356a65d Mon Sep 17 00:00:00 2001 From: Hanjun Guo Date: Tue, 22 Nov 2022 15:37:28 +0800 Subject: driver core: location: Free struct acpi_pld_info *pld before return false struct acpi_pld_info *pld should be freed before the return of allocation failure, to prevent memory leak, add the ACPI_FREE() to fix it. Fixes: bc443c31def5 ("driver core: location: Check for allocations failure") Signed-off-by: Hanjun Guo Link: https://lore.kernel.org/r/1669102648-11517-1-git-send-email-guohanjun@huawei.com Signed-off-by: Greg Kroah-Hartman --- drivers/base/physical_location.c | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) (limited to 'drivers/base') diff --git a/drivers/base/physical_location.c b/drivers/base/physical_location.c index 87af641cfe1a..951819e71b4a 100644 --- a/drivers/base/physical_location.c +++ b/drivers/base/physical_location.c @@ -24,8 +24,11 @@ bool dev_add_physical_location(struct device *dev) dev->physical_location = kzalloc(sizeof(*dev->physical_location), GFP_KERNEL); - if (!dev->physical_location) + if (!dev->physical_location) { + ACPI_FREE(pld); return false; + } + dev->physical_location->panel = pld->panel; dev->physical_location->vertical_position = pld->vertical_position; dev->physical_location->horizontal_position = pld->horizontal_position; -- cgit v1.2.3 From e5da06b27ff5a148e42265c8e306670a9d913969 Mon Sep 17 00:00:00 2001 From: Yang Yingliang Date: Tue, 15 Nov 2022 11:16:38 +0800 Subject: drivers: base: transport_class: fix resource leak when transport_add_device() fails The normal call sequence of using transport class is: Add path: transport_setup_device() transport_setup_classdev() // call sas_host_setup() here transport_add_device() // if fails, need call transport_destroy_device() transport_configure_device() Remove path: transport_remove_device() transport_remove_classdev // call sas_host_remove() here transport_destroy_device() If transport_add_device() fails, need call transport_destroy_device() to free memory, but in this case, ->remove() is not called, and the resources allocated in ->setup() are leaked. So fix these leaks by calling ->remove() in transport_add_class_device() if it returns error. Fixes: 1da177e4c3f4 ("Linux-2.6.12-rc2") Signed-off-by: Yang Yingliang Link: https://lore.kernel.org/r/20221115031638.3816551-1-yangyingliang@huawei.com Signed-off-by: Greg Kroah-Hartman --- drivers/base/transport_class.c | 17 ++++++++++++++++- 1 file changed, 16 insertions(+), 1 deletion(-) (limited to 'drivers/base') diff --git a/drivers/base/transport_class.c b/drivers/base/transport_class.c index ccc86206e508..09ee2a1e35bb 100644 --- a/drivers/base/transport_class.c +++ b/drivers/base/transport_class.c @@ -155,12 +155,27 @@ static int transport_add_class_device(struct attribute_container *cont, struct device *dev, struct device *classdev) { + struct transport_class *tclass = class_to_transport_class(cont->class); int error = attribute_container_add_class_device(classdev); struct transport_container *tcont = attribute_container_to_transport_container(cont); - if (!error && tcont->statistics) + if (error) + goto err_remove; + + if (tcont->statistics) { error = sysfs_create_group(&classdev->kobj, tcont->statistics); + if (error) + goto err_del; + } + + return 0; + +err_del: + attribute_container_class_device_del(classdev); +err_remove: + if (tclass->remove) + tclass->remove(tcont, dev, classdev); return error; } -- cgit v1.2.3 From 0b2a1a3938aad80fb19cb53cbbd4679eeb37e3a8 Mon Sep 17 00:00:00 2001 From: "Rafael J. Wysocki" Date: Fri, 20 Jan 2023 20:46:57 +0100 Subject: driver core: class: Clear private pointer on registration failures Clear the class private pointer if __class_register() fails for it, so as to allow its users to verify that the class is usable by checking the value of that pointer. For consistency, clear that pointer before freeing the object pointed to by it in class_release(). Signed-off-by: Rafael J. Wysocki Link: https://lore.kernel.org/r/4463268.LvFx2qVVIh@kreacher Signed-off-by: Greg Kroah-Hartman --- drivers/base/class.c | 16 +++++++++++----- 1 file changed, 11 insertions(+), 5 deletions(-) (limited to 'drivers/base') diff --git a/drivers/base/class.c b/drivers/base/class.c index 3366da9c3ff6..a38b0d7d681d 100644 --- a/drivers/base/class.c +++ b/drivers/base/class.c @@ -53,6 +53,8 @@ static void class_release(struct kobject *kobj) pr_debug("class '%s': release.\n", class->name); + class->p = NULL; + if (class->class_release) class->class_release(class); else @@ -186,17 +188,21 @@ int __class_register(struct class *cls, struct lock_class_key *key) cls->p = cp; error = kset_register(&cp->subsys); - if (error) { - kfree(cp); - return error; - } + if (error) + goto err_out; + error = class_add_groups(class_get(cls), cls->class_groups); class_put(cls); if (error) { kobject_del(&cp->subsys.kobj); kfree_const(cp->subsys.kobj.name); - kfree(cp); + goto err_out; } + return 0; + +err_out: + kfree(cp); + cls->p = NULL; return error; } EXPORT_SYMBOL_GPL(__class_register); -- cgit v1.2.3 From 42bb5be8936f40a1d0e618766645e7fd0cbfe591 Mon Sep 17 00:00:00 2001 From: Greg Kroah-Hartman Date: Wed, 11 Jan 2023 12:30:09 +0100 Subject: driver core: device_get_devnode() should take a const * device_get_devnode() should take a constant * to struct device as it does not modify it in any way, so modify the function definition to do this and move it out of device.h as it does not need to be exposed to the whole kernel tree. Cc: Thomas Gleixner Cc: Jason Gunthorpe Cc: Ira Weiny Cc: Dan Williams Cc: Won Chung Acked-by: Rafael J. Wysocki Reviewed-by: Andy Shevchenko Acked-by: Hans de Goede Link: https://lore.kernel.org/r/20230111113018.459199-8-gregkh@linuxfoundation.org Signed-off-by: Greg Kroah-Hartman --- drivers/base/base.h | 2 ++ drivers/base/core.c | 2 +- 2 files changed, 3 insertions(+), 1 deletion(-) (limited to 'drivers/base') diff --git a/drivers/base/base.h b/drivers/base/base.h index 3d1da1027206..2208af509ce8 100644 --- a/drivers/base/base.h +++ b/drivers/base/base.h @@ -159,6 +159,8 @@ extern void device_block_probing(void); extern void device_unblock_probing(void); extern void deferred_probe_extend_timeout(void); extern void driver_deferred_probe_trigger(void); +const char *device_get_devnode(const struct device *dev, umode_t *mode, + kuid_t *uid, kgid_t *gid, const char **tmp); /* /sys/devices directory */ extern struct kset *devices_kset; diff --git a/drivers/base/core.c b/drivers/base/core.c index 39dbd532cfb3..e39b6fa0eda6 100644 --- a/drivers/base/core.c +++ b/drivers/base/core.c @@ -3733,7 +3733,7 @@ static struct device *next_device(struct klist_iter *i) * a name. This memory is returned in tmp and needs to be * freed by the caller. */ -const char *device_get_devnode(struct device *dev, +const char *device_get_devnode(const struct device *dev, umode_t *mode, kuid_t *uid, kgid_t *gid, const char **tmp) { -- cgit v1.2.3 From 2a81ada32f0e584fc0c943e0d3a8c9f4fae411d6 Mon Sep 17 00:00:00 2001 From: Greg Kroah-Hartman Date: Wed, 11 Jan 2023 12:30:17 +0100 Subject: driver core: make struct bus_type.uevent() take a const * The uevent() callback in struct bus_type should not be modifying the device that is passed into it, so mark it as a const * and propagate the function signature changes out into all relevant subsystems that use this callback. Acked-by: Rafael J. Wysocki Acked-by: Hans de Goede Link: https://lore.kernel.org/r/20230111113018.459199-16-gregkh@linuxfoundation.org Signed-off-by: Greg Kroah-Hartman --- drivers/base/auxiliary.c | 2 +- drivers/base/cpu.c | 2 +- drivers/base/platform.c | 4 ++-- 3 files changed, 4 insertions(+), 4 deletions(-) (limited to 'drivers/base') diff --git a/drivers/base/auxiliary.c b/drivers/base/auxiliary.c index 8c5e65930617..4d4c2c8d26c4 100644 --- a/drivers/base/auxiliary.c +++ b/drivers/base/auxiliary.c @@ -185,7 +185,7 @@ static int auxiliary_match(struct device *dev, struct device_driver *drv) return !!auxiliary_match_id(auxdrv->id_table, auxdev); } -static int auxiliary_uevent(struct device *dev, struct kobj_uevent_env *env) +static int auxiliary_uevent(const struct device *dev, struct kobj_uevent_env *env) { const char *name, *p; diff --git a/drivers/base/cpu.c b/drivers/base/cpu.c index 4c98849577d4..441eb5bdec7d 100644 --- a/drivers/base/cpu.c +++ b/drivers/base/cpu.c @@ -336,7 +336,7 @@ static ssize_t print_cpu_modalias(struct device *dev, return len; } -static int cpu_uevent(struct device *dev, struct kobj_uevent_env *env) +static int cpu_uevent(const struct device *dev, struct kobj_uevent_env *env) { char *buf = kzalloc(PAGE_SIZE, GFP_KERNEL); if (buf) { diff --git a/drivers/base/platform.c b/drivers/base/platform.c index b10a130cd433..262555b83bed 100644 --- a/drivers/base/platform.c +++ b/drivers/base/platform.c @@ -1353,9 +1353,9 @@ static int platform_match(struct device *dev, struct device_driver *drv) return (strcmp(pdev->name, drv->name) == 0); } -static int platform_uevent(struct device *dev, struct kobj_uevent_env *env) +static int platform_uevent(const struct device *dev, struct kobj_uevent_env *env) { - struct platform_device *pdev = to_platform_device(dev); + const struct platform_device *pdev = to_platform_device(dev); int rc; /* Some devices have extra OF data and an OF-style MODALIAS */ -- cgit v1.2.3 From 56d5f362ad0f8f60bc7c6fd5d7bc2b528d6963f5 Mon Sep 17 00:00:00 2001 From: Greg Kroah-Hartman Date: Wed, 11 Jan 2023 12:30:18 +0100 Subject: kobject: kset_uevent_ops: make uevent() callback take a const * The uevent() callback in struct kset_uevent_ops does not modify the kobject passed into it, so make the pointer const to enforce this restriction. When doing so, fix up all existing uevent() callbacks to have the correct signature to preserve the build. Cc: Christine Caulfield Cc: David Teigland Cc: Bob Peterson Cc: Andreas Gruenbacher Acked-by: Rafael J. Wysocki Acked-by: Hans de Goede Link: https://lore.kernel.org/r/20230111113018.459199-17-gregkh@linuxfoundation.org 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 e39b6fa0eda6..e4b18f7ac58a 100644 --- a/drivers/base/core.c +++ b/drivers/base/core.c @@ -2387,9 +2387,9 @@ static const char *dev_uevent_name(const struct kobject *kobj) return NULL; } -static int dev_uevent(struct kobject *kobj, struct kobj_uevent_env *env) +static int dev_uevent(const struct kobject *kobj, struct kobj_uevent_env *env) { - struct device *dev = kobj_to_dev(kobj); + const struct device *dev = kobj_to_dev(kobj); int retval = 0; /* add device node properties if present */ -- cgit v1.2.3 From 90be1f15c39858a4f0f4346c39fa150697d231c1 Mon Sep 17 00:00:00 2001 From: Greg Kroah-Hartman Date: Mon, 30 Jan 2023 18:10:59 +0100 Subject: driver core: soc: remove layering violation for the soc_bus The soc_bus code pokes around in the internal bus structures assuming that it "knows" if a field is not set that it has not been registered yet. That isn't a safe assumption, so just remove the layering violation entirely and keep track if the bus has been registered or not ourselves. Reviewed-by: Rafael J. Wysocki Link: https://lore.kernel.org/r/20230130171059.1784057-1-gregkh@linuxfoundation.org Signed-off-by: Greg Kroah-Hartman --- drivers/base/soc.c | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) (limited to 'drivers/base') diff --git a/drivers/base/soc.c b/drivers/base/soc.c index 22130b5f789d..0fb1d4ab9d8a 100644 --- a/drivers/base/soc.c +++ b/drivers/base/soc.c @@ -30,6 +30,7 @@ struct soc_device { static struct bus_type soc_bus_type = { .name = "soc", }; +static bool soc_bus_registered; static DEVICE_ATTR(machine, 0444, soc_info_show, NULL); static DEVICE_ATTR(family, 0444, soc_info_show, NULL); @@ -117,7 +118,7 @@ struct soc_device *soc_device_register(struct soc_device_attribute *soc_dev_attr const struct attribute_group **soc_attr_groups; int ret; - if (!soc_bus_type.p) { + if (!soc_bus_registered) { if (early_soc_dev_attr) return ERR_PTR(-EBUSY); early_soc_dev_attr = soc_dev_attr; @@ -183,6 +184,7 @@ static int __init soc_bus_register(void) ret = bus_register(&soc_bus_type); if (ret) return ret; + soc_bus_registered = true; if (early_soc_dev_attr) return PTR_ERR(soc_device_register(early_soc_dev_attr)); -- cgit v1.2.3 From ecaef469920fd6d2c7687f19081946f47684a423 Mon Sep 17 00:00:00 2001 From: Pierre Gondois Date: Tue, 24 Jan 2023 16:40:46 +0100 Subject: cacheinfo: Initialize variables in fetch_cache_info() Set potentially uninitialized variables to 0. This is particularly relevant when CONFIG_ACPI_PPTT is not set. Reported-by: kernel test robot Link: https://lore.kernel.org/all/202301052307.JYt1GWaJ-lkp@intel.com/ Reported-by: Dan Carpenter Link: https://lore.kernel.org/all/Y86iruJPuwNN7rZw@kili/ Fixes: 5944ce092b97 ("arch_topology: Build cacheinfo from primary CPU") Signed-off-by: Pierre Gondois Reviewed-by: Conor Dooley Link: https://lore.kernel.org/r/20230124154053.355376-2-pierre.gondois@arm.com Signed-off-by: Greg Kroah-Hartman --- drivers/base/cacheinfo.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers/base') diff --git a/drivers/base/cacheinfo.c b/drivers/base/cacheinfo.c index 418a18acc8f9..f6573c335f4c 100644 --- a/drivers/base/cacheinfo.c +++ b/drivers/base/cacheinfo.c @@ -424,7 +424,7 @@ int allocate_cache_info(int cpu) int fetch_cache_info(unsigned int cpu) { struct cpu_cacheinfo *this_cpu_ci; - unsigned int levels, split_levels; + unsigned int levels = 0, split_levels = 0; int ret; if (acpi_disabled) { -- cgit v1.2.3 From b4ce0bf7ab02c46676276ca1e8d4c5789528b439 Mon Sep 17 00:00:00 2001 From: Greg Kroah-Hartman Date: Tue, 31 Jan 2023 09:24:58 +0100 Subject: driver core: platform: removed unneeded variable from __platform_driver_probe() In the reworking of the function __platform_driver_probe() over the years, it turns out that the variable 'code' does not actually do anything or mean anything anymore and can be removed to simplify the logic when trying to read and understand what this function is actually doing. Cc: "Rafael J. Wysocki" Link: https://lore.kernel.org/r/20230131082459.301603-1-gregkh@linuxfoundation.org Signed-off-by: Greg Kroah-Hartman --- drivers/base/platform.c | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) (limited to 'drivers/base') diff --git a/drivers/base/platform.c b/drivers/base/platform.c index 262555b83bed..423874269f9d 100644 --- a/drivers/base/platform.c +++ b/drivers/base/platform.c @@ -906,7 +906,7 @@ static int platform_probe_fail(struct platform_device *pdev) int __init_or_module __platform_driver_probe(struct platform_driver *drv, int (*probe)(struct platform_device *), struct module *module) { - int retval, code; + int retval; if (drv->driver.probe_type == PROBE_PREFER_ASYNCHRONOUS) { pr_err("%s: drivers registered with %s can not be probed asynchronously\n", @@ -932,7 +932,7 @@ int __init_or_module __platform_driver_probe(struct platform_driver *drv, /* temporary section violation during probe() */ drv->probe = probe; - retval = code = __platform_driver_register(drv, module); + retval = __platform_driver_register(drv, module); if (retval) return retval; @@ -944,11 +944,11 @@ int __init_or_module __platform_driver_probe(struct platform_driver *drv, */ spin_lock(&drv->driver.bus->p->klist_drivers.k_lock); drv->probe = platform_probe_fail; - if (code == 0 && list_empty(&drv->driver.p->klist_devices.k_list)) + if (list_empty(&drv->driver.p->klist_devices.k_list)) retval = -ENODEV; spin_unlock(&drv->driver.bus->p->klist_drivers.k_lock); - if (code != retval) + if (retval) platform_driver_unregister(drv); return retval; } -- cgit v1.2.3 From 40b3880dc29b89c39139eba11eba2b3107dc2e38 Mon Sep 17 00:00:00 2001 From: Greg Kroah-Hartman Date: Tue, 31 Jan 2023 09:24:59 +0100 Subject: driver core: platform: simplify __platform_driver_probe() __platform_driver_probe() pokes around in some bus and driver private lists and locks in a way that is not needed at all. The code only wants to know if a device was bound to the driver that was registered, so walk all devices on the bus to see if there was a match. If there is not a match, return an error. This is the same logic as was originally present, but just done in a simpler and more obvious way that is not a layering violation. Cc: "Rafael J. Wysocki" Link: https://lore.kernel.org/r/20230131082459.301603-2-gregkh@linuxfoundation.org Signed-off-by: Greg Kroah-Hartman --- drivers/base/platform.c | 26 +++++++++++++++----------- 1 file changed, 15 insertions(+), 11 deletions(-) (limited to 'drivers/base') diff --git a/drivers/base/platform.c b/drivers/base/platform.c index 423874269f9d..77510e4f47de 100644 --- a/drivers/base/platform.c +++ b/drivers/base/platform.c @@ -883,6 +883,13 @@ static int platform_probe_fail(struct platform_device *pdev) return -ENXIO; } +static int is_bound_to_driver(struct device *dev, void *driver) +{ + if (dev->driver == driver) + return 1; + return 0; +} + /** * __platform_driver_probe - register driver for non-hotpluggable device * @drv: platform driver structure @@ -936,20 +943,17 @@ int __init_or_module __platform_driver_probe(struct platform_driver *drv, if (retval) return retval; - /* - * Fixup that section violation, being paranoid about code scanning - * the list of drivers in order to probe new devices. Check to see - * if the probe was successful, and make sure any forced probes of - * new devices fail. - */ - spin_lock(&drv->driver.bus->p->klist_drivers.k_lock); + /* Force all new probes of this driver to fail */ drv->probe = platform_probe_fail; - if (list_empty(&drv->driver.p->klist_devices.k_list)) - retval = -ENODEV; - spin_unlock(&drv->driver.bus->p->klist_drivers.k_lock); - if (retval) + /* Walk all platform devices and see if any actually bound to this driver. + * If not, return an error as the device should have done so by now. + */ + if (!bus_for_each_dev(&platform_bus_type, NULL, &drv->driver, is_bound_to_driver)) { + retval = -ENODEV; platform_driver_unregister(drv); + } + return retval; } EXPORT_SYMBOL_GPL(__platform_driver_probe); -- cgit v1.2.3 From 37e98d9bedb50644654fd196e38acad49903fadc Mon Sep 17 00:00:00 2001 From: Greg Kroah-Hartman Date: Wed, 1 Feb 2023 09:33:49 +0100 Subject: driver core: bus: move lock_class_key into dynamic structure Move the lock_class_key structure out of struct bus_type and into the dynamic structure we create already for all bus_types registered with the kernel. This saves on static space and removes one more writable field in struct bus_type. In the future, the same field can be moved out of the struct class logic because it shares this same private structure. Most everyone will never notice this change, as lockdep is not enabled in real systems so no memory or logic changes are happening for them. Cc: "Rafael J. Wysocki" Acked-by: Rafael J. Wysocki Signed-off-by: Greg Kroah-Hartman Link: https://lore.kernel.org/r/20230201083349.4038660-1-gregkh@linuxfoundation.org Signed-off-by: Greg Kroah-Hartman --- drivers/base/base.h | 2 ++ drivers/base/bus.c | 5 ++++- 2 files changed, 6 insertions(+), 1 deletion(-) (limited to 'drivers/base') diff --git a/drivers/base/base.h b/drivers/base/base.h index 2208af509ce8..0e806f641079 100644 --- a/drivers/base/base.h +++ b/drivers/base/base.h @@ -52,6 +52,8 @@ struct subsys_private { struct kset glue_dirs; struct class *class; + + struct lock_class_key lock_key; }; #define to_subsys_private(obj) container_of_const(obj, struct subsys_private, subsys.kobj) diff --git a/drivers/base/bus.c b/drivers/base/bus.c index cf1b8f00b4c0..aa70b3a7d778 100644 --- a/drivers/base/bus.c +++ b/drivers/base/bus.c @@ -154,6 +154,7 @@ static void bus_release(struct kobject *kobj) struct subsys_private *priv = to_subsys_private(kobj); struct bus_type *bus = priv->bus; + lockdep_unregister_key(&priv->lock_key); kfree(priv); bus->p = NULL; } @@ -743,7 +744,7 @@ int bus_register(struct bus_type *bus) { int retval; struct subsys_private *priv; - struct lock_class_key *key = &bus->lock_key; + struct lock_class_key *key; priv = kzalloc(sizeof(struct subsys_private), GFP_KERNEL); if (!priv) @@ -785,6 +786,8 @@ int bus_register(struct bus_type *bus) } INIT_LIST_HEAD(&priv->interfaces); + key = &priv->lock_key; + lockdep_register_key(key); __mutex_init(&priv->mutex, "subsys mutex", key); klist_init(&priv->klist_devices, klist_devices_get, klist_devices_put); klist_init(&priv->klist_drivers, NULL, NULL); -- cgit v1.2.3 From 5cdc03c5cf471a215c57e540cc86be613c0ba457 Mon Sep 17 00:00:00 2001 From: Longlong Xia Date: Thu, 2 Feb 2023 03:32:01 +0000 Subject: devtmpfs: convert to pr_fmt Use the pr_fmt() macro to prefix all the output with "devtmpfs: ". while at it, convert printk() to pr_(). Signed-off-by: Longlong Xia Link: https://lore.kernel.org/r/20230202033203.1239239-2-xialonglong1@huawei.com Signed-off-by: Greg Kroah-Hartman --- drivers/base/devtmpfs.c | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) (limited to 'drivers/base') diff --git a/drivers/base/devtmpfs.c b/drivers/base/devtmpfs.c index e4bffeabf344..95ebc18ded50 100644 --- a/drivers/base/devtmpfs.c +++ b/drivers/base/devtmpfs.c @@ -13,6 +13,8 @@ * overwrite the default setting if needed. */ +#define pr_fmt(fmt) "devtmpfs: " fmt + #include #include #include @@ -376,9 +378,9 @@ int __init devtmpfs_mount(void) err = init_mount("devtmpfs", "dev", "devtmpfs", DEVTMPFS_MFLAGS, NULL); if (err) - printk(KERN_INFO "devtmpfs: error mounting %i\n", err); + pr_info("error mounting %d\n", err); else - printk(KERN_INFO "devtmpfs: mounted\n"); + pr_info("mounted\n"); return err; } @@ -460,14 +462,12 @@ int __init devtmpfs_init(void) mnt = vfs_kern_mount(&internal_fs_type, 0, "devtmpfs", opts); if (IS_ERR(mnt)) { - printk(KERN_ERR "devtmpfs: unable to create devtmpfs %ld\n", - PTR_ERR(mnt)); + pr_err("unable to create devtmpfs %ld\n", PTR_ERR(mnt)); return PTR_ERR(mnt); } err = register_filesystem(&dev_fs_type); if (err) { - printk(KERN_ERR "devtmpfs: unable to register devtmpfs " - "type %i\n", err); + pr_err("unable to register devtmpfs type %d\n", err); return err; } @@ -480,12 +480,12 @@ int __init devtmpfs_init(void) } if (err) { - printk(KERN_ERR "devtmpfs: unable to create devtmpfs %i\n", err); + pr_err("unable to create devtmpfs %d\n", err); unregister_filesystem(&dev_fs_type); thread = NULL; return err; } - printk(KERN_INFO "devtmpfs: initialized\n"); + pr_info("initialized\n"); return 0; } -- cgit v1.2.3 From 8deb87b1e810dd558371e88ffd44339fbef27870 Mon Sep 17 00:00:00 2001 From: Greg Kroah-Hartman Date: Thu, 2 Feb 2023 15:16:20 +0100 Subject: drivers: base: component: fix memory leak with using debugfs_lookup() When calling debugfs_lookup() the result must have dput() called on it, otherwise the memory will leak over time. To make things simpler, just call debugfs_lookup_and_remove() instead which handles all of the logic at once. Cc: "Rafael J. Wysocki" Link: https://lore.kernel.org/r/20230202141621.2296458-1-gregkh@linuxfoundation.org Signed-off-by: Greg Kroah-Hartman --- drivers/base/component.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers/base') diff --git a/drivers/base/component.c b/drivers/base/component.c index 5eadeac6c532..7dbf14a1d915 100644 --- a/drivers/base/component.c +++ b/drivers/base/component.c @@ -125,7 +125,7 @@ static void component_debugfs_add(struct aggregate_device *m) static void component_debugfs_del(struct aggregate_device *m) { - debugfs_remove(debugfs_lookup(dev_name(m->parent), component_debugfs_dir)); + debugfs_lookup_and_remove(dev_name(m->parent), component_debugfs_dir); } #else -- cgit v1.2.3 From 36c893d3a759ae7c91ee7d4871ebfc7504f08c40 Mon Sep 17 00:00:00 2001 From: Greg Kroah-Hartman Date: Thu, 2 Feb 2023 15:16:21 +0100 Subject: drivers: base: dd: fix memory leak with using debugfs_lookup() When calling debugfs_lookup() the result must have dput() called on it, otherwise the memory will leak over time. To make things simpler, just call debugfs_lookup_and_remove() instead which handles all of the logic at once. Cc: "Rafael J. Wysocki" Link: https://lore.kernel.org/r/20230202141621.2296458-2-gregkh@linuxfoundation.org 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 817ef27a78f7..8def2ba08a82 100644 --- a/drivers/base/dd.c +++ b/drivers/base/dd.c @@ -370,7 +370,7 @@ late_initcall(deferred_probe_initcall); static void __exit deferred_probe_exit(void) { - debugfs_remove_recursive(debugfs_lookup("devices_deferred", NULL)); + debugfs_lookup_and_remove("devices_deferred", NULL); } __exitcall(deferred_probe_exit); -- cgit v1.2.3 From c83d9ab42f1ef70d81bb3c637a12c098a9a05057 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Thomas=20Wei=C3=9Fschuh?= Date: Sat, 4 Feb 2023 22:36:58 +0000 Subject: driver core: make kobj_type structures constant MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Since commit ee6d3dd4ed48 ("driver core: make kobj_type constant.") the driver core allows the usage of const struct kobj_type. Take advantage of this to constify the structure definitions to prevent modification at runtime. Signed-off-by: Thomas Weißschuh Link: https://lore.kernel.org/r/20230204-kobj_type-driver-core-v1-1-b9f809419f2c@weissschuh.net Signed-off-by: Greg Kroah-Hartman --- drivers/base/bus.c | 4 ++-- drivers/base/class.c | 2 +- drivers/base/core.c | 4 ++-- drivers/base/swnode.c | 2 +- 4 files changed, 6 insertions(+), 6 deletions(-) (limited to 'drivers/base') diff --git a/drivers/base/bus.c b/drivers/base/bus.c index aa70b3a7d778..d6b4a5d5a43c 100644 --- a/drivers/base/bus.c +++ b/drivers/base/bus.c @@ -91,7 +91,7 @@ static void driver_release(struct kobject *kobj) kfree(drv_priv); } -static struct kobj_type driver_ktype = { +static const struct kobj_type driver_ktype = { .sysfs_ops = &driver_sysfs_ops, .release = driver_release, }; @@ -159,7 +159,7 @@ static void bus_release(struct kobject *kobj) bus->p = NULL; } -static struct kobj_type bus_ktype = { +static const struct kobj_type bus_ktype = { .sysfs_ops = &bus_sysfs_ops, .release = bus_release, }; diff --git a/drivers/base/class.c b/drivers/base/class.c index a38b0d7d681d..89d4528fcc1a 100644 --- a/drivers/base/class.c +++ b/drivers/base/class.c @@ -77,7 +77,7 @@ static const struct sysfs_ops class_sysfs_ops = { .store = class_attr_store, }; -static struct kobj_type class_ktype = { +static const struct kobj_type class_ktype = { .sysfs_ops = &class_sysfs_ops, .release = class_release, .child_ns_type = class_child_ns_type, diff --git a/drivers/base/core.c b/drivers/base/core.c index e4b18f7ac58a..3c4acf4b34d3 100644 --- a/drivers/base/core.c +++ b/drivers/base/core.c @@ -2354,7 +2354,7 @@ static void device_get_ownership(const struct kobject *kobj, kuid_t *uid, kgid_t dev->class->get_ownership(dev, uid, gid); } -static struct kobj_type device_ktype = { +static const struct kobj_type device_ktype = { .release = device_release, .sysfs_ops = &dev_sysfs_ops, .namespace = device_namespace, @@ -2992,7 +2992,7 @@ struct kobj_ns_type_operations *class_dir_child_ns_type(const struct kobject *ko return dir->class->ns_type; } -static struct kobj_type class_dir_ktype = { +static const struct kobj_type class_dir_ktype = { .release = class_dir_release, .sysfs_ops = &kobj_sysfs_ops, .child_ns_type = class_dir_child_ns_type diff --git a/drivers/base/swnode.c b/drivers/base/swnode.c index da3c1c2cb9a8..1886995a0b3a 100644 --- a/drivers/base/swnode.c +++ b/drivers/base/swnode.c @@ -760,7 +760,7 @@ static void software_node_release(struct kobject *kobj) kfree(swnode); } -static struct kobj_type software_node_type = { +static const struct kobj_type software_node_type = { .release = software_node_release, .sysfs_ops = &kobj_sysfs_ops, }; -- cgit v1.2.3 From 3a2dbc510c437ca392516b0105bad8e7970e6614 Mon Sep 17 00:00:00 2001 From: Saravana Kannan Date: Mon, 6 Feb 2023 17:41:53 -0800 Subject: driver core: fw_devlink: Don't purge child fwnode's consumer links When a device X is bound successfully to a driver, if it has a child firmware node Y that doesn't have a struct device created by then, we delete fwnode links where the child firmware node Y is the supplier. We did this to avoid blocking the consumers of the child firmware node Y from deferring probe indefinitely. While that a step in the right direction, it's better to make the consumers of the child firmware node Y to be consumers of the device X because device X is probably implementing whatever functionality is represented by child firmware node Y. By doing this, we capture the device dependencies more accurately and ensure better probe/suspend/resume ordering. Signed-off-by: Saravana Kannan Tested-by: Colin Foster Tested-by: Sudeep Holla Tested-by: Douglas Anderson Tested-by: Geert Uytterhoeven Tested-by: Luca Weiss # qcom/sm7225-fairphone-fp4 Link: https://lore.kernel.org/r/20230207014207.1678715-2-saravanak@google.com Signed-off-by: Greg Kroah-Hartman --- drivers/base/core.c | 97 +++++++++++++++++++++++++++++++++++++++++++---------- 1 file changed, 79 insertions(+), 18 deletions(-) (limited to 'drivers/base') diff --git a/drivers/base/core.c b/drivers/base/core.c index 3c4acf4b34d3..040c8072fd3e 100644 --- a/drivers/base/core.c +++ b/drivers/base/core.c @@ -54,11 +54,12 @@ 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); +static void __fw_devlink_link_to_consumers(struct device *dev); static bool fw_devlink_drv_reg_done; static bool fw_devlink_best_effort; /** - * fwnode_link_add - Create a link between two fwnode_handles. + * __fwnode_link_add - Create a link between two fwnode_handles. * @con: Consumer end of the link. * @sup: Supplier end of the link. * @@ -74,22 +75,18 @@ static bool fw_devlink_best_effort; * 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) +static 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; + return 0; link = kzalloc(sizeof(*link), GFP_KERNEL); - if (!link) { - ret = -ENOMEM; - goto out; - } + if (!link) + return -ENOMEM; link->supplier = sup; INIT_LIST_HEAD(&link->s_hook); @@ -100,9 +97,17 @@ int fwnode_link_add(struct fwnode_handle *con, struct fwnode_handle *sup) list_add(&link->c_hook, &con->suppliers); pr_debug("%pfwP Linked as a fwnode consumer to %pfwP\n", con, sup); -out: - mutex_unlock(&fwnode_link_lock); + return 0; +} + +int fwnode_link_add(struct fwnode_handle *con, struct fwnode_handle *sup) +{ + int ret; + + mutex_lock(&fwnode_link_lock); + ret = __fwnode_link_add(con, sup); + mutex_unlock(&fwnode_link_lock); return ret; } @@ -181,6 +186,51 @@ void fw_devlink_purge_absent_suppliers(struct fwnode_handle *fwnode) } EXPORT_SYMBOL_GPL(fw_devlink_purge_absent_suppliers); +/** + * __fwnode_links_move_consumers - Move consumer from @from to @to fwnode_handle + * @from: move consumers away from this fwnode + * @to: move consumers to this fwnode + * + * Move all consumer links from @from fwnode to @to fwnode. + */ +static void __fwnode_links_move_consumers(struct fwnode_handle *from, + struct fwnode_handle *to) +{ + struct fwnode_link *link, *tmp; + + list_for_each_entry_safe(link, tmp, &from->consumers, s_hook) { + __fwnode_link_add(link->consumer, to); + __fwnode_link_del(link); + } +} + +/** + * __fw_devlink_pickup_dangling_consumers - Pick up dangling consumers + * @fwnode: fwnode from which to pick up dangling consumers + * @new_sup: fwnode of new supplier + * + * If the @fwnode has a corresponding struct device and the device supports + * probing (that is, added to a bus), then we want to let fw_devlink create + * MANAGED device links to this device, so leave @fwnode and its descendant's + * fwnode links alone. + * + * Otherwise, move its consumers to the new supplier @new_sup. + */ +static void __fw_devlink_pickup_dangling_consumers(struct fwnode_handle *fwnode, + struct fwnode_handle *new_sup) +{ + struct fwnode_handle *child; + + if (fwnode->dev && fwnode->dev->bus) + return; + + fwnode->flags |= FWNODE_FLAG_NOT_DEVICE; + __fwnode_links_move_consumers(fwnode, new_sup); + + fwnode_for_each_available_child_node(fwnode, child) + __fw_devlink_pickup_dangling_consumers(child, new_sup); +} + #ifdef CONFIG_SRCU static DEFINE_MUTEX(device_links_lock); DEFINE_STATIC_SRCU(device_links_srcu); @@ -1267,16 +1317,23 @@ void device_links_driver_bound(struct device *dev) * them. So, fw_devlink no longer needs to create device links to any * of the device's suppliers. * - * Also, if a child firmware node of this bound device is not added as - * a device by now, assume it is never going to be added and make sure - * other devices don't defer probe indefinitely by waiting for such a - * child device. + * Also, if a child firmware node of this bound device is not added as a + * device by now, assume it is never going to be added. Make this bound + * device the fallback supplier to the dangling consumers of the child + * firmware node because this bound device is probably implementing the + * child firmware node functionality and we don't want the dangling + * consumers to defer probe indefinitely waiting for a device for the + * child firmware node. */ if (dev->fwnode && dev->fwnode->dev == dev) { struct fwnode_handle *child; fwnode_links_purge_suppliers(dev->fwnode); + mutex_lock(&fwnode_link_lock); fwnode_for_each_available_child_node(dev->fwnode, child) - fw_devlink_purge_absent_suppliers(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); @@ -1855,7 +1912,11 @@ static int fw_devlink_create_devlink(struct device *con, fwnode_is_ancestor_of(sup_handle, con->fwnode)) return -EINVAL; - sup_dev = get_dev_from_fwnode(sup_handle); + if (sup_handle->flags & FWNODE_FLAG_NOT_DEVICE) + sup_dev = fwnode_get_next_parent_dev(sup_handle); + else + sup_dev = get_dev_from_fwnode(sup_handle); + if (sup_dev) { /* * If it's one of those drivers that don't actually bind to -- cgit v1.2.3 From 411c0d58ca6faa9bc4b9f5382118a31c7bb92a6f Mon Sep 17 00:00:00 2001 From: Saravana Kannan Date: Mon, 6 Feb 2023 17:41:54 -0800 Subject: driver core: fw_devlink: Improve check for fwnode with no device/driver fw_devlink shouldn't defer the probe of a device to wait on a supplier that'll never have a struct device or will never be probed by a driver. We currently check if a supplier falls into this category, but don't check its ancestors. We need to check the ancestors too because if the ancestor will never probe, then the supplier will never probe either. Signed-off-by: Saravana Kannan Tested-by: Colin Foster Tested-by: Sudeep Holla Tested-by: Douglas Anderson Tested-by: Geert Uytterhoeven Tested-by: Luca Weiss # qcom/sm7225-fairphone-fp4 Link: https://lore.kernel.org/r/20230207014207.1678715-3-saravanak@google.com Signed-off-by: Greg Kroah-Hartman --- drivers/base/core.c | 40 ++++++++++++++++++++++++++++++++++++++-- 1 file changed, 38 insertions(+), 2 deletions(-) (limited to 'drivers/base') diff --git a/drivers/base/core.c b/drivers/base/core.c index 040c8072fd3e..3daaa7ffe751 100644 --- a/drivers/base/core.c +++ b/drivers/base/core.c @@ -1867,6 +1867,35 @@ static int fw_devlink_relax_cycle(struct device *con, void *sup) return ret; } +static bool fwnode_init_without_drv(struct fwnode_handle *fwnode) +{ + struct device *dev; + bool ret; + + if (!(fwnode->flags & FWNODE_FLAG_INITIALIZED)) + return false; + + dev = get_dev_from_fwnode(fwnode); + ret = !dev || dev->links.status == DL_DEV_NO_DRIVER; + put_device(dev); + + return ret; +} + +static bool fwnode_ancestor_init_without_drv(struct fwnode_handle *fwnode) +{ + struct fwnode_handle *parent; + + fwnode_for_each_parent_node(fwnode, parent) { + if (fwnode_init_without_drv(parent)) { + fwnode_handle_put(parent); + return true; + } + } + + return false; +} + /** * fw_devlink_create_devlink - Create a device link from a consumer to fwnode * @con: consumer device for the device link @@ -1948,9 +1977,16 @@ static int fw_devlink_create_devlink(struct device *con, goto out; } - /* Supplier that's already initialized without a struct device. */ - if (sup_handle->flags & FWNODE_FLAG_INITIALIZED) + /* + * Supplier or supplier's ancestor already initialized without a struct + * device or being probed by a driver. + */ + if (fwnode_init_without_drv(sup_handle) || + fwnode_ancestor_init_without_drv(sup_handle)) { + dev_dbg(con, "Not linking %pfwP - Might never probe\n", + sup_handle); return -EINVAL; + } /* * DL_FLAG_SYNC_STATE_ONLY doesn't block probing and supports -- cgit v1.2.3 From 67cad5c67019c38126b749621665b6723d3ae7e6 Mon Sep 17 00:00:00 2001 From: Saravana Kannan Date: Mon, 6 Feb 2023 17:41:57 -0800 Subject: driver core: fw_devlink: Add DL_FLAG_CYCLE support to device links fw_devlink uses DL_FLAG_SYNC_STATE_ONLY device link flag for two purposes: 1. To allow a parent device to proxy its child device's dependency on a supplier so that the supplier doesn't get its sync_state() callback before the child device/consumer can be added and probed. In this usage scenario, we need to ignore cycles for ensure correctness of sync_state() callbacks. 2. When there are dependency cycles in firmware, we don't know which of those dependencies are valid. So, we have to ignore them all wrt probe ordering while still making sure the sync_state() callbacks come correctly. However, when detecting dependency cycles, there can be multiple dependency cycles between two devices that we need to detect. For example: A -> B -> A and A -> C -> B -> A. To detect multiple cycles correct, we need to be able to differentiate DL_FLAG_SYNC_STATE_ONLY device links used for (1) vs (2) above. To allow this differentiation, add a DL_FLAG_CYCLE that can be use to mark use case (2). We can then use the DL_FLAG_CYCLE to decide which DL_FLAG_SYNC_STATE_ONLY device links to follow when looking for dependency cycles. Fixes: 2de9d8e0d2fe ("driver core: fw_devlink: Improve handling of cyclic dependencies") Signed-off-by: Saravana Kannan Tested-by: Colin Foster Tested-by: Sudeep Holla Tested-by: Douglas Anderson Tested-by: Geert Uytterhoeven Tested-by: Luca Weiss # qcom/sm7225-fairphone-fp4 Link: https://lore.kernel.org/r/20230207014207.1678715-6-saravanak@google.com Signed-off-by: Greg Kroah-Hartman --- drivers/base/core.c | 28 ++++++++++++++++++---------- 1 file changed, 18 insertions(+), 10 deletions(-) (limited to 'drivers/base') diff --git a/drivers/base/core.c b/drivers/base/core.c index 3daaa7ffe751..ac19752422e0 100644 --- a/drivers/base/core.c +++ b/drivers/base/core.c @@ -322,6 +322,12 @@ static bool device_is_ancestor(struct device *dev, struct device *target) return false; } +static inline bool device_link_flag_is_sync_state_only(u32 flags) +{ + return (flags & ~(DL_FLAG_INFERRED | DL_FLAG_CYCLE)) == + (DL_FLAG_SYNC_STATE_ONLY | DL_FLAG_MANAGED); +} + /** * device_is_dependent - Check if one device depends on another one * @dev: Device to check dependencies for. @@ -348,8 +354,7 @@ int device_is_dependent(struct device *dev, void *target) return ret; list_for_each_entry(link, &dev->links.consumers, s_node) { - if ((link->flags & ~DL_FLAG_INFERRED) == - (DL_FLAG_SYNC_STATE_ONLY | DL_FLAG_MANAGED)) + if (device_link_flag_is_sync_state_only(link->flags)) continue; if (link->consumer == target) @@ -422,8 +427,7 @@ static int device_reorder_to_tail(struct device *dev, void *not_used) device_for_each_child(dev, NULL, device_reorder_to_tail); list_for_each_entry(link, &dev->links.consumers, s_node) { - if ((link->flags & ~DL_FLAG_INFERRED) == - (DL_FLAG_SYNC_STATE_ONLY | DL_FLAG_MANAGED)) + if (device_link_flag_is_sync_state_only(link->flags)) continue; device_reorder_to_tail(link->consumer, NULL); } @@ -684,7 +688,8 @@ postcore_initcall(devlink_class_init); DL_FLAG_AUTOREMOVE_SUPPLIER | \ DL_FLAG_AUTOPROBE_CONSUMER | \ DL_FLAG_SYNC_STATE_ONLY | \ - DL_FLAG_INFERRED) + DL_FLAG_INFERRED | \ + DL_FLAG_CYCLE) #define DL_ADD_VALID_FLAGS (DL_MANAGED_LINK_FLAGS | DL_FLAG_STATELESS | \ DL_FLAG_PM_RUNTIME | DL_FLAG_RPM_ACTIVE) @@ -753,8 +758,6 @@ struct device_link *device_link_add(struct device *consumer, if (!consumer || !supplier || consumer == supplier || flags & ~DL_ADD_VALID_FLAGS || (flags & DL_FLAG_STATELESS && flags & DL_MANAGED_LINK_FLAGS) || - (flags & DL_FLAG_SYNC_STATE_ONLY && - (flags & ~DL_FLAG_INFERRED) != DL_FLAG_SYNC_STATE_ONLY) || (flags & DL_FLAG_AUTOPROBE_CONSUMER && flags & (DL_FLAG_AUTOREMOVE_CONSUMER | DL_FLAG_AUTOREMOVE_SUPPLIER))) @@ -770,6 +773,10 @@ struct device_link *device_link_add(struct device *consumer, if (!(flags & DL_FLAG_STATELESS)) flags |= DL_FLAG_MANAGED; + if (flags & DL_FLAG_SYNC_STATE_ONLY && + !device_link_flag_is_sync_state_only(flags)) + return NULL; + device_links_write_lock(); device_pm_lock(); @@ -1729,7 +1736,7 @@ static void fw_devlink_relax_link(struct device_link *link) if (!(link->flags & DL_FLAG_INFERRED)) return; - if (link->flags == (DL_FLAG_MANAGED | FW_DEVLINK_FLAGS_PERMISSIVE)) + if (device_link_flag_is_sync_state_only(link->flags)) return; pm_runtime_drop_link(link); @@ -1853,8 +1860,8 @@ static int fw_devlink_relax_cycle(struct device *con, void *sup) return ret; list_for_each_entry(link, &con->links.consumers, s_node) { - if ((link->flags & ~DL_FLAG_INFERRED) == - (DL_FLAG_SYNC_STATE_ONLY | DL_FLAG_MANAGED)) + if (!(link->flags & DL_FLAG_CYCLE) && + device_link_flag_is_sync_state_only(link->flags)) continue; if (!fw_devlink_relax_cycle(link->consumer, sup)) @@ -1863,6 +1870,7 @@ static int fw_devlink_relax_cycle(struct device *con, void *sup) ret = 1; fw_devlink_relax_link(link); + link->flags |= DL_FLAG_CYCLE; } return ret; } -- cgit v1.2.3 From 6a6dfdf8b3ff337be5a447e9f4e71969f18370ad Mon Sep 17 00:00:00 2001 From: Saravana Kannan Date: Mon, 6 Feb 2023 17:41:58 -0800 Subject: driver core: fw_devlink: Allow marking a fwnode link as being part of a cycle To improve detection and handling of dependency cycles, we need to be able to mark fwnode links as being part of cycles. fwnode links marked as being part of a cycle should not block their consumers from probing. Fixes: 2de9d8e0d2fe ("driver core: fw_devlink: Improve handling of cyclic dependencies") Signed-off-by: Saravana Kannan Tested-by: Colin Foster Tested-by: Sudeep Holla Tested-by: Douglas Anderson Tested-by: Geert Uytterhoeven Tested-by: Luca Weiss # qcom/sm7225-fairphone-fp4 Link: https://lore.kernel.org/r/20230207014207.1678715-7-saravanak@google.com Signed-off-by: Greg Kroah-Hartman --- drivers/base/core.c | 50 ++++++++++++++++++++++++++++++++++++++++---------- 1 file changed, 40 insertions(+), 10 deletions(-) (limited to 'drivers/base') diff --git a/drivers/base/core.c b/drivers/base/core.c index ac19752422e0..6639f90cf301 100644 --- a/drivers/base/core.c +++ b/drivers/base/core.c @@ -76,13 +76,15 @@ static bool fw_devlink_best_effort; * are ignored and there is no reference counting. */ static int __fwnode_link_add(struct fwnode_handle *con, - struct fwnode_handle *sup) + struct fwnode_handle *sup, u8 flags) { struct fwnode_link *link; list_for_each_entry(link, &sup->consumers, s_hook) - if (link->consumer == con) + if (link->consumer == con) { + link->flags |= flags; return 0; + } link = kzalloc(sizeof(*link), GFP_KERNEL); if (!link) @@ -92,6 +94,7 @@ static int __fwnode_link_add(struct fwnode_handle *con, INIT_LIST_HEAD(&link->s_hook); link->consumer = con; INIT_LIST_HEAD(&link->c_hook); + link->flags = flags; list_add(&link->s_hook, &sup->consumers); list_add(&link->c_hook, &con->suppliers); @@ -106,7 +109,7 @@ int fwnode_link_add(struct fwnode_handle *con, struct fwnode_handle *sup) int ret; mutex_lock(&fwnode_link_lock); - ret = __fwnode_link_add(con, sup); + ret = __fwnode_link_add(con, sup, 0); mutex_unlock(&fwnode_link_lock); return ret; } @@ -126,6 +129,19 @@ static void __fwnode_link_del(struct fwnode_link *link) kfree(link); } +/** + * __fwnode_link_cycle - Mark a fwnode link as being part of a cycle. + * @link: the fwnode_link to be marked + * + * The fwnode_link_lock needs to be held when this function is called. + */ +static void __fwnode_link_cycle(struct fwnode_link *link) +{ + pr_debug("%pfwf: Relaxing link with %pfwf\n", + link->consumer, link->supplier); + link->flags |= FWLINK_FLAG_CYCLE; +} + /** * fwnode_links_purge_suppliers - Delete all supplier links of fwnode_handle. * @fwnode: fwnode whose supplier links need to be deleted @@ -199,7 +215,7 @@ static void __fwnode_links_move_consumers(struct fwnode_handle *from, struct fwnode_link *link, *tmp; list_for_each_entry_safe(link, tmp, &from->consumers, s_hook) { - __fwnode_link_add(link->consumer, to); + __fwnode_link_add(link->consumer, to, link->flags); __fwnode_link_del(link); } } @@ -1041,6 +1057,21 @@ static bool dev_is_best_effort(struct device *dev) (dev->fwnode && (dev->fwnode->flags & FWNODE_FLAG_BEST_EFFORT)); } +static struct fwnode_handle *fwnode_links_check_suppliers( + struct fwnode_handle *fwnode) +{ + struct fwnode_link *link; + + if (!fwnode || fw_devlink_is_permissive()) + return NULL; + + list_for_each_entry(link, &fwnode->suppliers, c_hook) + if (!(link->flags & FWLINK_FLAG_CYCLE)) + return link->supplier; + + return NULL; +} + /** * device_links_check_suppliers - Check presence of supplier drivers. * @dev: Consumer device. @@ -1068,11 +1099,8 @@ int device_links_check_suppliers(struct device *dev) * probe. */ mutex_lock(&fwnode_link_lock); - if (dev->fwnode && !list_empty(&dev->fwnode->suppliers) && - !fw_devlink_is_permissive()) { - sup_fw = list_first_entry(&dev->fwnode->suppliers, - struct fwnode_link, - c_hook)->supplier; + 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, @@ -1261,7 +1289,9 @@ static ssize_t waiting_for_supplier_show(struct device *dev, bool val; device_lock(dev); - val = !list_empty(&dev->fwnode->suppliers); + mutex_lock(&fwnode_link_lock); + val = !!fwnode_links_check_suppliers(dev->fwnode); + mutex_unlock(&fwnode_link_lock); device_unlock(dev); return sysfs_emit(buf, "%u\n", val); } -- cgit v1.2.3 From cd115c0409f283edde94bd5a9a42dc42bee0aba8 Mon Sep 17 00:00:00 2001 From: Saravana Kannan Date: Mon, 6 Feb 2023 17:41:59 -0800 Subject: driver core: fw_devlink: Consolidate device link flag computation Consolidate the code that computes the flags to be used when creating a device link from a fwnode link. Fixes: 2de9d8e0d2fe ("driver core: fw_devlink: Improve handling of cyclic dependencies") Signed-off-by: Saravana Kannan Tested-by: Colin Foster Tested-by: Sudeep Holla Tested-by: Douglas Anderson Tested-by: Geert Uytterhoeven Tested-by: Luca Weiss # qcom/sm7225-fairphone-fp4 Link: https://lore.kernel.org/r/20230207014207.1678715-8-saravanak@google.com Signed-off-by: Greg Kroah-Hartman --- drivers/base/core.c | 28 +++++++++++++++------------- 1 file changed, 15 insertions(+), 13 deletions(-) (limited to 'drivers/base') diff --git a/drivers/base/core.c b/drivers/base/core.c index 6639f90cf301..0a8a5ab344c1 100644 --- a/drivers/base/core.c +++ b/drivers/base/core.c @@ -1727,8 +1727,11 @@ static int __init fw_devlink_strict_setup(char *arg) } early_param("fw_devlink.strict", fw_devlink_strict_setup); -u32 fw_devlink_get_flags(void) +static inline u32 fw_devlink_get_flags(u8 fwlink_flags) { + if (fwlink_flags & FWLINK_FLAG_CYCLE) + return FW_DEVLINK_FLAGS_PERMISSIVE | DL_FLAG_CYCLE; + return fw_devlink_flags; } @@ -1938,7 +1941,7 @@ static bool fwnode_ancestor_init_without_drv(struct fwnode_handle *fwnode) * 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 - * @flags: devlink flags + * @link: fwnode link that's being converted to a device link * * This function will try to create a device link between the consumer device * @con and the supplier device represented by @sup_handle. @@ -1955,10 +1958,17 @@ static bool fwnode_ancestor_init_without_drv(struct fwnode_handle *fwnode) * possible to do that in the future */ static int fw_devlink_create_devlink(struct device *con, - struct fwnode_handle *sup_handle, u32 flags) + struct fwnode_handle *sup_handle, + struct fwnode_link *link) { struct device *sup_dev; int ret = 0; + u32 flags; + + if (con->fwnode == link->consumer) + flags = fw_devlink_get_flags(link->flags); + else + flags = FW_DEVLINK_FLAGS_PERMISSIVE; /* * In some cases, a device P might also be a supplier to its child node @@ -2091,7 +2101,6 @@ static void __fw_devlink_link_to_consumers(struct device *dev) 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; @@ -2121,14 +2130,13 @@ static void __fw_devlink_link_to_consumers(struct device *dev) con_dev = NULL; } else { own_link = false; - dl_flags = FW_DEVLINK_FLAGS_PERMISSIVE; } } if (!con_dev) continue; - ret = fw_devlink_create_devlink(con_dev, fwnode, dl_flags); + ret = fw_devlink_create_devlink(con_dev, fwnode, link); put_device(con_dev); if (!own_link || ret == -EAGAIN) continue; @@ -2169,19 +2177,13 @@ static void __fw_devlink_link_to_suppliers(struct device *dev, 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 = FW_DEVLINK_FLAGS_PERMISSIVE; 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); + ret = fw_devlink_create_devlink(dev, sup, link); if (!own_link || ret == -EAGAIN) continue; -- cgit v1.2.3 From 3fb16866b51ded6c016b664caad53f8d4fd9dc56 Mon Sep 17 00:00:00 2001 From: Saravana Kannan Date: Mon, 6 Feb 2023 17:42:00 -0800 Subject: driver core: fw_devlink: Make cycle detection more robust fw_devlink could only detect a single and simple cycle because it relied mainly on device link cycle detection code that only checked for cycles between devices. The expectation was that the firmware wouldn't have complicated cycles and multiple cycles between devices. That expectation has been proven to be wrong. For example, fw_devlink could handle: +-+ +-+ |A+------> |B+ +-+ +++ ^ | | | +----------+ But it couldn't handle even something as "simple" as: +---------------------+ | | v | +-+ +-+ +++ |A+------> |B+------> |C| +-+ +++ +-+ ^ | | | +----------+ But firmware has even more complicated cycles like: +---------------------+ | | v | +-+ +---+ +++ +--+A+------>| B +-----> |C|<--+ | +-+ ++--+ +++ | | ^ | ^ | | | | | | | | | +---------+ +---------+ | | | +------------------------------+ And this is without including parent child dependencies or nodes in the cycle that are just firmware nodes that'll never have a struct device created for them. The proper way to treat these devices it to not force any probe ordering between them, while still enforce dependencies between node in the cycles (A, B and C) and their consumers. So this patch goes all out and just deals with all types of cycles. It does this by: 1. Following dependencies across device links, parent-child and fwnode links. 2. When it find cycles, it mark the device links and fwnode links as such instead of just deleting them or making the indistinguishable from proxy SYNC_STATE_ONLY device links. This way, when new nodes get added, we can immediately find and mark any new cycles whether the new node is a device or firmware node. Fixes: 2de9d8e0d2fe ("driver core: fw_devlink: Improve handling of cyclic dependencies") Signed-off-by: Saravana Kannan Tested-by: Colin Foster Tested-by: Sudeep Holla Tested-by: Douglas Anderson Tested-by: Geert Uytterhoeven Tested-by: Luca Weiss # qcom/sm7225-fairphone-fp4 Link: https://lore.kernel.org/r/20230207014207.1678715-9-saravanak@google.com Signed-off-by: Greg Kroah-Hartman --- drivers/base/core.c | 248 +++++++++++++++++++++++++++------------------------- 1 file changed, 129 insertions(+), 119 deletions(-) (limited to 'drivers/base') diff --git a/drivers/base/core.c b/drivers/base/core.c index 0a8a5ab344c1..2712a1a1e959 100644 --- a/drivers/base/core.c +++ b/drivers/base/core.c @@ -1866,47 +1866,6 @@ static void fw_devlink_unblock_consumers(struct device *dev) device_links_write_unlock(); } -/** - * fw_devlink_relax_cycle - Convert cyclic links to SYNC_STATE_ONLY links - * @con: Device to check dependencies for. - * @sup: Device to check against. - * - * Check if @sup depends on @con or any device dependent on it (its child or - * its consumer etc). When such a cyclic dependency is found, convert all - * device links created solely by fw_devlink into SYNC_STATE_ONLY device links. - * This is the equivalent of doing fw_devlink=permissive just between the - * devices in the cycle. We need to do this because, at this point, fw_devlink - * can't tell which of these dependencies is not a real dependency. - * - * Return 1 if a cycle is found. Otherwise, return 0. - */ -static int fw_devlink_relax_cycle(struct device *con, void *sup) -{ - struct device_link *link; - int ret; - - if (con == sup) - return 1; - - ret = device_for_each_child(con, sup, fw_devlink_relax_cycle); - if (ret) - return ret; - - list_for_each_entry(link, &con->links.consumers, s_node) { - if (!(link->flags & DL_FLAG_CYCLE) && - device_link_flag_is_sync_state_only(link->flags)) - continue; - - if (!fw_devlink_relax_cycle(link->consumer, sup)) - continue; - - ret = 1; - - fw_devlink_relax_link(link); - link->flags |= DL_FLAG_CYCLE; - } - return ret; -} static bool fwnode_init_without_drv(struct fwnode_handle *fwnode) { @@ -1937,6 +1896,111 @@ static bool fwnode_ancestor_init_without_drv(struct fwnode_handle *fwnode) return false; } +/** + * __fw_devlink_relax_cycles - Relax and mark dependency cycles. + * @con: Potential consumer device. + * @sup_handle: Potential supplier's fwnode. + * + * Needs to be called with fwnode_lock and device link lock held. + * + * Check if @sup_handle or any of its ancestors or suppliers direct/indirectly + * depend on @con. This function can detect multiple cyles between @sup_handle + * and @con. When such dependency cycles are found, convert all device links + * created solely by fw_devlink into SYNC_STATE_ONLY device links. Also, mark + * all fwnode links in the cycle with FWLINK_FLAG_CYCLE so that when they are + * converted into a device link in the future, they are created as + * SYNC_STATE_ONLY device links. This is the equivalent of doing + * fw_devlink=permissive just between the devices in the cycle. We need to do + * this because, at this point, fw_devlink can't tell which of these + * dependencies is not a real dependency. + * + * Return true if one or more cycles were found. Otherwise, return false. + */ +static bool __fw_devlink_relax_cycles(struct device *con, + struct fwnode_handle *sup_handle) +{ + struct device *sup_dev = NULL, *par_dev = NULL; + struct fwnode_link *link; + struct device_link *dev_link; + bool ret = false; + + if (!sup_handle) + return false; + + /* + * We aren't trying to find all cycles. Just a cycle between con and + * sup_handle. + */ + if (sup_handle->flags & FWNODE_FLAG_VISITED) + return false; + + sup_handle->flags |= FWNODE_FLAG_VISITED; + + sup_dev = get_dev_from_fwnode(sup_handle); + + /* Termination condition. */ + if (sup_dev == con) { + ret = true; + goto out; + } + + /* + * If sup_dev is bound to a driver and @con hasn't started binding to a + * driver, sup_dev can't be a consumer of @con. So, no need to check + * further. + */ + if (sup_dev && sup_dev->links.status == DL_DEV_DRIVER_BOUND && + con->links.status == DL_DEV_NO_DRIVER) { + ret = false; + goto out; + } + + list_for_each_entry(link, &sup_handle->suppliers, c_hook) { + if (__fw_devlink_relax_cycles(con, link->supplier)) { + __fwnode_link_cycle(link); + ret = true; + } + } + + /* + * Give priority to device parent over fwnode parent to account for any + * quirks in how fwnodes are converted to devices. + */ + if (sup_dev) + par_dev = get_device(sup_dev->parent); + else + par_dev = fwnode_get_next_parent_dev(sup_handle); + + if (par_dev && __fw_devlink_relax_cycles(con, par_dev->fwnode)) + ret = true; + + if (!sup_dev) + goto out; + + list_for_each_entry(dev_link, &sup_dev->links.suppliers, c_node) { + /* + * Ignore a SYNC_STATE_ONLY flag only if it wasn't marked as + * such due to a cycle. + */ + if (device_link_flag_is_sync_state_only(dev_link->flags) && + !(dev_link->flags & DL_FLAG_CYCLE)) + continue; + + if (__fw_devlink_relax_cycles(con, + dev_link->supplier->fwnode)) { + fw_devlink_relax_link(dev_link); + dev_link->flags |= DL_FLAG_CYCLE; + ret = true; + } + } + +out: + sup_handle->flags &= ~FWNODE_FLAG_VISITED; + put_device(sup_dev); + put_device(par_dev); + return ret; +} + /** * fw_devlink_create_devlink - Create a device link from a consumer to fwnode * @con: consumer device for the device link @@ -1989,6 +2053,21 @@ static int fw_devlink_create_devlink(struct device *con, fwnode_is_ancestor_of(sup_handle, con->fwnode)) return -EINVAL; + /* + * SYNC_STATE_ONLY device links don't block probing and supports cycles. + * So cycle detection isn't necessary and shouldn't be done. + */ + if (!(flags & DL_FLAG_SYNC_STATE_ONLY)) { + device_links_write_lock(); + if (__fw_devlink_relax_cycles(con, sup_handle)) { + __fwnode_link_cycle(link); + flags = fw_devlink_get_flags(link->flags); + dev_info(con, "Fixed dependency cycle(s) with %pfwf\n", + sup_handle); + } + device_links_write_unlock(); + } + if (sup_handle->flags & FWNODE_FLAG_NOT_DEVICE) sup_dev = fwnode_get_next_parent_dev(sup_handle); else @@ -2002,23 +2081,16 @@ static int fw_devlink_create_devlink(struct device *con, */ if (sup_dev->links.status == DL_DEV_NO_DRIVER && sup_handle->flags & FWNODE_FLAG_INITIALIZED) { + dev_dbg(con, + "Not linking %pfwf - dev might never probe\n", + sup_handle); ret = -EINVAL; goto out; } - /* - * 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) && - !(flags & DL_FLAG_SYNC_STATE_ONLY)) { - dev_info(con, "Fixing up cyclic dependency with %s\n", - dev_name(sup_dev)); - device_links_write_lock(); - fw_devlink_relax_cycle(con, sup_dev); - device_links_write_unlock(); - device_link_add(con, sup_dev, - FW_DEVLINK_FLAGS_PERMISSIVE); + if (!device_link_add(con, sup_dev, flags)) { + dev_err(con, "Failed to create device link with %s\n", + dev_name(sup_dev)); ret = -EINVAL; } @@ -2031,49 +2103,12 @@ static int fw_devlink_create_devlink(struct device *con, */ if (fwnode_init_without_drv(sup_handle) || fwnode_ancestor_init_without_drv(sup_handle)) { - dev_dbg(con, "Not linking %pfwP - Might never probe\n", + dev_dbg(con, "Not linking %pfwf - might never become dev\n", sup_handle); return -EINVAL; } - /* - * 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 and supplier have a cyclic dependency. Since fw_devlink - * can't tell which of the inferred dependencies are incorrect, don't - * enforce probe ordering between any of the devices in this cyclic - * dependency. Do this by relaxing all the fw_devlink device links in - * this cycle and by treating the fwnode link between the consumer and - * the supplier as an invalid dependency. - */ - sup_dev = fwnode_get_next_parent_dev(sup_handle); - if (sup_dev && device_is_dependent(con, sup_dev)) { - dev_info(con, "Fixing up cyclic dependency with %pfwP (%s)\n", - sup_handle, dev_name(sup_dev)); - device_links_write_lock(); - fw_devlink_relax_cycle(con, sup_dev); - device_links_write_unlock(); - ret = -EINVAL; - } else { - /* - * Can't check for cycles or no cycles. So let's try - * again later. - */ - ret = -EAGAIN; - } - + ret = -EAGAIN; out: put_device(sup_dev); return ret; @@ -2156,10 +2191,7 @@ static void __fw_devlink_link_to_consumers(struct device *dev) * * 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. + * fwnode links are deleted. * * 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 @@ -2180,7 +2212,6 @@ static void __fw_devlink_link_to_suppliers(struct device *dev, 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, link); @@ -2188,27 +2219,6 @@ static void __fw_devlink_link_to_suppliers(struct device *dev, continue; __fwnode_link_del(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); } /* -- cgit v1.2.3 From 789be03a600842ef461968cc5a2d458f51f319b2 Mon Sep 17 00:00:00 2001 From: Greg Kroah-Hartman Date: Wed, 8 Feb 2023 12:13:10 +0100 Subject: driver core: add local subsys_get and subsys_put functions We need to control the reference count of the subsys private structure instead of directly manipulating the kset reference count of it, so wrap that logic up in a subsys_get() and subsys_put() function to make it more obvious as to what is happening. Cc: "Rafael J. Wysocki" Link: https://lore.kernel.org/r/20230208111330.439504-2-gregkh@linuxfoundation.org Signed-off-by: Greg Kroah-Hartman --- drivers/base/base.h | 13 +++++++++++++ 1 file changed, 13 insertions(+) (limited to 'drivers/base') diff --git a/drivers/base/base.h b/drivers/base/base.h index 0e806f641079..9e06c18c7a64 100644 --- a/drivers/base/base.h +++ b/drivers/base/base.h @@ -57,6 +57,19 @@ struct subsys_private { }; #define to_subsys_private(obj) container_of_const(obj, struct subsys_private, subsys.kobj) +static inline struct subsys_private *subsys_get(struct subsys_private *sp) +{ + if (sp) + kset_get(&sp->subsys); + return sp; +} + +static inline void subsys_put(struct subsys_private *sp) +{ + if (sp) + kset_put(&sp->subsys); +} + struct driver_private { struct kobject kobj; struct klist klist_devices; -- cgit v1.2.3 From 273afac615adccf97bd4e70ebfb27074f1ff22b9 Mon Sep 17 00:00:00 2001 From: Greg Kroah-Hartman Date: Wed, 8 Feb 2023 12:13:11 +0100 Subject: driver core: bus: implement bus_get/put() without the private pointer In the quest to make 'struct bus_type' constant and in read-only memory, we need to stop using the private pointer to the subsys_private structure. First step in doing this is to create a helper function that turns a 'struct bus_type' into 'struct subsys_private' called bus_to_subsys(). bus_to_subsys() walks the list of registered busses in the system and finds the matching one based on the pointer to the bus_type itself. As this is a short list, and this function is not on any fast path, it should not be noticable. Implement bus_get() and bus_put() using this new helper function. Cc: "Rafael J. Wysocki" Link: https://lore.kernel.org/r/20230208111330.439504-3-gregkh@linuxfoundation.org Signed-off-by: Greg Kroah-Hartman --- drivers/base/bus.c | 61 +++++++++++++++++++++++++++++++++++++++++++++++------- 1 file changed, 53 insertions(+), 8 deletions(-) (limited to 'drivers/base') diff --git a/drivers/base/bus.c b/drivers/base/bus.c index d6b4a5d5a43c..8f46ab84e81a 100644 --- a/drivers/base/bus.c +++ b/drivers/base/bus.c @@ -24,6 +24,9 @@ /* /sys/devices/system */ static struct kset *system_kset; +/* /sys/bus */ +static struct kset *bus_kset; + #define to_bus_attr(_attr) container_of(_attr, struct bus_attribute, attr) /* @@ -39,19 +42,63 @@ static struct kset *system_kset; static int __must_check bus_rescan_devices_helper(struct device *dev, void *data); +/** + * bus_to_subsys - Turn a struct bus_type into a struct subsys_private + * + * @bus: pointer to the struct bus_type to look up + * + * The driver core internals needs to work on the subsys_private structure, not + * the external struct bus_type pointer. This function walks the list of + * registered busses in the system and finds the matching one and returns the + * internal struct subsys_private that relates to that bus. + * + * Note, the reference count of the return value is INCREMENTED if it is not + * NULL. A call to subsys_put() must be done when finished with the pointer in + * order for it to be properly freed. + */ +static struct subsys_private *bus_to_subsys(const struct bus_type *bus) +{ + struct subsys_private *sp = NULL; + struct kobject *kobj; + + if (!bus) + return NULL; + + spin_lock(&bus_kset->list_lock); + + if (list_empty(&bus_kset->list)) + goto done; + + list_for_each_entry(kobj, &bus_kset->list, entry) { + struct kset *kset = container_of(kobj, struct kset, kobj); + + sp = container_of_const(kset, struct subsys_private, subsys); + if (sp->bus == bus) + goto done; + } + sp = NULL; +done: + sp = subsys_get(sp); + spin_unlock(&bus_kset->list_lock); + return sp; +} + static struct bus_type *bus_get(struct bus_type *bus) { - if (bus) { - kset_get(&bus->p->subsys); + struct subsys_private *sp = bus_to_subsys(bus); + + if (sp) return bus; - } return NULL; } -static void bus_put(struct bus_type *bus) +static void bus_put(const struct bus_type *bus) { - if (bus) - kset_put(&bus->p->subsys); + struct subsys_private *sp = bus_to_subsys(bus); + + /* two puts are required as the call to bus_to_subsys incremented it again */ + subsys_put(sp); + subsys_put(sp); } static ssize_t drv_attr_show(struct kobject *kobj, struct attribute *attr, @@ -177,8 +224,6 @@ static const struct kset_uevent_ops bus_uevent_ops = { .filter = bus_uevent_filter, }; -static struct kset *bus_kset; - /* Manually detach a device from its associated driver. */ static ssize_t unbind_store(struct device_driver *drv, const char *buf, size_t count) -- cgit v1.2.3 From e0766ea4c8f8f94a605e291aa3672a703dc1d2e4 Mon Sep 17 00:00:00 2001 From: Greg Kroah-Hartman Date: Wed, 8 Feb 2023 12:13:12 +0100 Subject: driver core: bus: constantify the bus_find_* functions All of the bus find and iterator functions do not modify the struct bus_type passed to them, so mark them as constant to enforce this rule. Cc: "Rafael J. Wysocki" Link: https://lore.kernel.org/r/20230208111330.439504-4-gregkh@linuxfoundation.org Signed-off-by: Greg Kroah-Hartman --- drivers/base/bus.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) (limited to 'drivers/base') diff --git a/drivers/base/bus.c b/drivers/base/bus.c index 8f46ab84e81a..b27ddf957f8b 100644 --- a/drivers/base/bus.c +++ b/drivers/base/bus.c @@ -331,7 +331,7 @@ static struct device *next_device(struct klist_iter *i) * to retain this data, it should do so, and increment the reference * count in the supplied callback. */ -int bus_for_each_dev(struct bus_type *bus, struct device *start, +int bus_for_each_dev(const struct bus_type *bus, struct device *start, void *data, int (*fn)(struct device *, void *)) { struct klist_iter i; @@ -365,7 +365,7 @@ EXPORT_SYMBOL_GPL(bus_for_each_dev); * if it does. If the callback returns non-zero, this function will * return to the caller and not iterate over any more devices. */ -struct device *bus_find_device(struct bus_type *bus, +struct device *bus_find_device(const struct bus_type *bus, struct device *start, const void *data, int (*match)(struct device *dev, const void *data)) { @@ -416,7 +416,7 @@ static struct device_driver *next_driver(struct klist_iter *i) * in the callback. It must also be sure to increment the refcount * so it doesn't disappear before returning to the caller. */ -int bus_for_each_drv(struct bus_type *bus, struct device_driver *start, +int bus_for_each_drv(const struct bus_type *bus, struct device_driver *start, void *data, int (*fn)(struct device_driver *, void *)) { struct klist_iter i; -- cgit v1.2.3 From 0396f2863f7af3c588033d270f7d979d11cd4708 Mon Sep 17 00:00:00 2001 From: Greg Kroah-Hartman Date: Wed, 8 Feb 2023 12:13:13 +0100 Subject: driver core: bus: convert bus_create/remove_file to be constant bus_create_file() and bus_remove_file() can be made to take a constant bus pointer, as it should not be modifying anything in the bus structure. Make this change and move the functions to use the internal subsys_get/put() logic as well, to prevent the use of the back-pointer in struct bus_type. Cc: "Rafael J. Wysocki" Link: https://lore.kernel.org/r/20230208111330.439504-5-gregkh@linuxfoundation.org Signed-off-by: Greg Kroah-Hartman --- drivers/base/bus.c | 28 +++++++++++++++++----------- 1 file changed, 17 insertions(+), 11 deletions(-) (limited to 'drivers/base') diff --git a/drivers/base/bus.c b/drivers/base/bus.c index b27ddf957f8b..6552d385fcb9 100644 --- a/drivers/base/bus.c +++ b/drivers/base/bus.c @@ -175,24 +175,30 @@ static const struct sysfs_ops bus_sysfs_ops = { .store = bus_attr_store, }; -int bus_create_file(struct bus_type *bus, struct bus_attribute *attr) +int bus_create_file(const struct bus_type *bus, struct bus_attribute *attr) { + struct subsys_private *sp = bus_to_subsys(bus); int error; - if (bus_get(bus)) { - error = sysfs_create_file(&bus->p->subsys.kobj, &attr->attr); - bus_put(bus); - } else - error = -EINVAL; + + if (!sp) + return -EINVAL; + + error = sysfs_create_file(&sp->subsys.kobj, &attr->attr); + + subsys_put(sp); return error; } EXPORT_SYMBOL_GPL(bus_create_file); -void bus_remove_file(struct bus_type *bus, struct bus_attribute *attr) +void bus_remove_file(const struct bus_type *bus, struct bus_attribute *attr) { - if (bus_get(bus)) { - sysfs_remove_file(&bus->p->subsys.kobj, &attr->attr); - bus_put(bus); - } + struct subsys_private *sp = bus_to_subsys(bus); + + if (!sp) + return; + + sysfs_remove_file(&sp->subsys.kobj, &attr->attr); + subsys_put(sp); } EXPORT_SYMBOL_GPL(bus_remove_file); -- cgit v1.2.3 From a00fdb988d81200e4d81b81e525b8f90e7d51591 Mon Sep 17 00:00:00 2001 From: Greg Kroah-Hartman Date: Wed, 8 Feb 2023 12:13:14 +0100 Subject: driver core: bus: sysfs function cleanups Convert the drivers_autoprobe show/store and uevent sysfs callbacks to use bus_to_subsys() and not use the back-pointer to the private structure. Cc: "Rafael J. Wysocki" Link: https://lore.kernel.org/r/20230208111330.439504-6-gregkh@linuxfoundation.org Signed-off-by: Greg Kroah-Hartman --- drivers/base/bus.c | 35 +++++++++++++++++++++++++++++------ 1 file changed, 29 insertions(+), 6 deletions(-) (limited to 'drivers/base') diff --git a/drivers/base/bus.c b/drivers/base/bus.c index 6552d385fcb9..d30a07ff8ab2 100644 --- a/drivers/base/bus.c +++ b/drivers/base/bus.c @@ -277,16 +277,31 @@ static DRIVER_ATTR_IGNORE_LOCKDEP(bind, 0200, NULL, bind_store); static ssize_t drivers_autoprobe_show(struct bus_type *bus, char *buf) { - return sysfs_emit(buf, "%d\n", bus->p->drivers_autoprobe); + struct subsys_private *sp = bus_to_subsys(bus); + int ret; + + if (!sp) + return -EINVAL; + + ret = sysfs_emit(buf, "%d\n", sp->drivers_autoprobe); + subsys_put(sp); + return ret; } static ssize_t drivers_autoprobe_store(struct bus_type *bus, const char *buf, size_t count) { + struct subsys_private *sp = bus_to_subsys(bus); + + if (!sp) + return -EINVAL; + if (buf[0] == '0') - bus->p->drivers_autoprobe = 0; + sp->drivers_autoprobe = 0; else - bus->p->drivers_autoprobe = 1; + sp->drivers_autoprobe = 1; + + subsys_put(sp); return count; } @@ -769,10 +784,18 @@ static void klist_devices_put(struct klist_node *n) static ssize_t bus_uevent_store(struct bus_type *bus, const char *buf, size_t count) { - int rc; + struct subsys_private *sp = bus_to_subsys(bus); + int ret; - rc = kobject_synth_uevent(&bus->p->subsys.kobj, buf, count); - return rc ? rc : count; + if (!sp) + return -EINVAL; + + ret = kobject_synth_uevent(&sp->subsys.kobj, buf, count); + subsys_put(sp); + + if (ret) + return ret; + return count; } /* * "open code" the old BUS_ATTR() macro here. We want to use BUS_ATTR_WO() -- cgit v1.2.3 From 5221b82d46f26903cf3161fb7d1fc006170811e0 Mon Sep 17 00:00:00 2001 From: Greg Kroah-Hartman Date: Wed, 8 Feb 2023 12:13:15 +0100 Subject: driver core: bus: bus_add/probe/remove_device() cleanups Convert the bus_add_device(), bus_probe_device(), and bus_remove_device() functions to use bus_to_subsys() and not use the back-pointer to the private structure. Cc: "Rafael J. Wysocki" Link: https://lore.kernel.org/r/20230208111330.439504-7-gregkh@linuxfoundation.org Signed-off-by: Greg Kroah-Hartman --- drivers/base/bus.c | 87 +++++++++++++++++++++++++++++++++--------------------- 1 file changed, 54 insertions(+), 33 deletions(-) (limited to 'drivers/base') diff --git a/drivers/base/bus.c b/drivers/base/bus.c index d30a07ff8ab2..a32975028aee 100644 --- a/drivers/base/bus.c +++ b/drivers/base/bus.c @@ -466,32 +466,46 @@ EXPORT_SYMBOL_GPL(bus_for_each_drv); */ int bus_add_device(struct device *dev) { - struct bus_type *bus = bus_get(dev->bus); - int error = 0; + struct subsys_private *sp = bus_to_subsys(dev->bus); + int error; - if (bus) { - pr_debug("bus: '%s': add device %s\n", bus->name, dev_name(dev)); - error = device_add_groups(dev, bus->dev_groups); - if (error) - goto out_put; - error = sysfs_create_link(&bus->p->devices_kset->kobj, - &dev->kobj, dev_name(dev)); - if (error) - goto out_groups; - error = sysfs_create_link(&dev->kobj, - &dev->bus->p->subsys.kobj, "subsystem"); - if (error) - goto out_subsys; - klist_add_tail(&dev->p->knode_bus, &bus->p->klist_devices); + if (!sp) { + /* + * This is a normal operation for many devices that do not + * have a bus assigned to them, just say that all went + * well. + */ + return 0; } + + /* + * Reference in sp is now incremented and will be dropped when + * the device is removed from the bus + */ + + pr_debug("bus: '%s': add device %s\n", sp->bus->name, dev_name(dev)); + + error = device_add_groups(dev, sp->bus->dev_groups); + if (error) + goto out_put; + + error = sysfs_create_link(&sp->devices_kset->kobj, &dev->kobj, dev_name(dev)); + if (error) + goto out_groups; + + error = sysfs_create_link(&dev->kobj, &sp->subsys.kobj, "subsystem"); + if (error) + goto out_subsys; + + klist_add_tail(&dev->p->knode_bus, &sp->klist_devices); return 0; out_subsys: - sysfs_remove_link(&bus->p->devices_kset->kobj, dev_name(dev)); + sysfs_remove_link(&sp->devices_kset->kobj, dev_name(dev)); out_groups: - device_remove_groups(dev, bus->dev_groups); + device_remove_groups(dev, sp->bus->dev_groups); out_put: - bus_put(dev->bus); + subsys_put(sp); return error; } @@ -503,20 +517,21 @@ out_put: */ void bus_probe_device(struct device *dev) { - struct bus_type *bus = dev->bus; + struct subsys_private *sp = bus_to_subsys(dev->bus); struct subsys_interface *sif; - if (!bus) + if (!sp) return; - if (bus->p->drivers_autoprobe) + if (sp->drivers_autoprobe) device_initial_probe(dev); - mutex_lock(&bus->p->mutex); - list_for_each_entry(sif, &bus->p->interfaces, node) + mutex_lock(&sp->mutex); + list_for_each_entry(sif, &sp->interfaces, node) if (sif->add_dev) sif->add_dev(dev, sif); - mutex_unlock(&bus->p->mutex); + mutex_unlock(&sp->mutex); + subsys_put(sp); } /** @@ -531,21 +546,20 @@ void bus_probe_device(struct device *dev) */ void bus_remove_device(struct device *dev) { - struct bus_type *bus = dev->bus; + struct subsys_private *sp = bus_to_subsys(dev->bus); struct subsys_interface *sif; - if (!bus) + if (!sp) return; - mutex_lock(&bus->p->mutex); - list_for_each_entry(sif, &bus->p->interfaces, node) + mutex_lock(&sp->mutex); + list_for_each_entry(sif, &sp->interfaces, node) if (sif->remove_dev) sif->remove_dev(dev, sif); - mutex_unlock(&bus->p->mutex); + mutex_unlock(&sp->mutex); sysfs_remove_link(&dev->kobj, "subsystem"); - sysfs_remove_link(&dev->bus->p->devices_kset->kobj, - dev_name(dev)); + sysfs_remove_link(&sp->devices_kset->kobj, dev_name(dev)); device_remove_groups(dev, dev->bus->dev_groups); if (klist_node_attached(&dev->p->knode_bus)) klist_del(&dev->p->knode_bus); @@ -553,7 +567,14 @@ void bus_remove_device(struct device *dev) pr_debug("bus: '%s': remove device %s\n", dev->bus->name, dev_name(dev)); device_release_driver(dev); - bus_put(dev->bus); + + /* + * Decrement the reference count twice, once for the bus_to_subsys() + * call in the start of this function, and the second one from the + * reference increment in bus_add_device() + */ + subsys_put(sp); + subsys_put(sp); } static int __must_check add_bind_files(struct device_driver *drv) -- cgit v1.2.3 From 3465e2e4a24eef9bc9f21aa29d83fd7000c1da48 Mon Sep 17 00:00:00 2001 From: Greg Kroah-Hartman Date: Wed, 8 Feb 2023 12:13:16 +0100 Subject: driver core: bus: bus_register/unregister() cleanups Convert the bus_register() and bus_unregister() functions to use bus_to_subsys() and not use the back-pointer to the private structure. Because bus_add_groups() and bus_remove_groups() were only called in one place, remove those one-line-wrapper functions and call the real sysfs group function where it is needed instead, saving another layer of indirection. Cc: "Rafael J. Wysocki" Link: https://lore.kernel.org/r/20230208111330.439504-8-gregkh@linuxfoundation.org Signed-off-by: Greg Kroah-Hartman --- drivers/base/bus.c | 54 ++++++++++++++++++++++++++---------------------------- 1 file changed, 26 insertions(+), 28 deletions(-) (limited to 'drivers/base') diff --git a/drivers/base/bus.c b/drivers/base/bus.c index a32975028aee..d515a8363522 100644 --- a/drivers/base/bus.c +++ b/drivers/base/bus.c @@ -774,18 +774,6 @@ int device_reprobe(struct device *dev) } EXPORT_SYMBOL_GPL(device_reprobe); -static int bus_add_groups(struct bus_type *bus, - const struct attribute_group **groups) -{ - return sysfs_create_groups(&bus->p->subsys.kobj, groups); -} - -static void bus_remove_groups(struct bus_type *bus, - const struct attribute_group **groups) -{ - sysfs_remove_groups(&bus->p->subsys.kobj, groups); -} - static void klist_devices_get(struct klist_node *n) { struct device_private *dev_prv = to_device_private_bus(n); @@ -839,6 +827,7 @@ int bus_register(struct bus_type *bus) { int retval; struct subsys_private *priv; + struct kobject *bus_kobj; struct lock_class_key *key; priv = kzalloc(sizeof(struct subsys_private), GFP_KERNEL); @@ -850,12 +839,13 @@ int bus_register(struct bus_type *bus) BLOCKING_INIT_NOTIFIER_HEAD(&priv->bus_notifier); - retval = kobject_set_name(&priv->subsys.kobj, "%s", bus->name); + bus_kobj = &priv->subsys.kobj; + retval = kobject_set_name(bus_kobj, "%s", bus->name); if (retval) goto out; - priv->subsys.kobj.kset = bus_kset; - priv->subsys.kobj.ktype = &bus_ktype; + bus_kobj->kset = bus_kset; + bus_kobj->ktype = &bus_ktype; priv->drivers_autoprobe = 1; retval = kset_register(&priv->subsys); @@ -866,15 +856,13 @@ int bus_register(struct bus_type *bus) if (retval) goto bus_uevent_fail; - priv->devices_kset = kset_create_and_add("devices", NULL, - &priv->subsys.kobj); + priv->devices_kset = kset_create_and_add("devices", NULL, bus_kobj); if (!priv->devices_kset) { retval = -ENOMEM; goto bus_devices_fail; } - priv->drivers_kset = kset_create_and_add("drivers", NULL, - &priv->subsys.kobj); + priv->drivers_kset = kset_create_and_add("drivers", NULL, bus_kobj); if (!priv->drivers_kset) { retval = -ENOMEM; goto bus_drivers_fail; @@ -891,7 +879,7 @@ int bus_register(struct bus_type *bus) if (retval) goto bus_probe_files_fail; - retval = bus_add_groups(bus, bus->bus_groups); + retval = sysfs_create_groups(bus_kobj, bus->bus_groups); if (retval) goto bus_groups_fail; @@ -901,15 +889,15 @@ int bus_register(struct bus_type *bus) bus_groups_fail: remove_probe_files(bus); bus_probe_files_fail: - kset_unregister(bus->p->drivers_kset); + kset_unregister(priv->drivers_kset); bus_drivers_fail: - kset_unregister(bus->p->devices_kset); + kset_unregister(priv->devices_kset); bus_devices_fail: bus_remove_file(bus, &bus_attr_uevent); bus_uevent_fail: - kset_unregister(&bus->p->subsys); + kset_unregister(&priv->subsys); out: - kfree(bus->p); + kfree(priv); bus->p = NULL; return retval; } @@ -924,15 +912,25 @@ EXPORT_SYMBOL_GPL(bus_register); */ void bus_unregister(struct bus_type *bus) { + struct subsys_private *sp = bus_to_subsys(bus); + struct kobject *bus_kobj; + + if (!sp) + return; + pr_debug("bus: '%s': unregistering\n", bus->name); if (bus->dev_root) device_unregister(bus->dev_root); - bus_remove_groups(bus, bus->bus_groups); + + bus_kobj = &sp->subsys.kobj; + sysfs_remove_groups(bus_kobj, bus->bus_groups); remove_probe_files(bus); - kset_unregister(bus->p->drivers_kset); - kset_unregister(bus->p->devices_kset); bus_remove_file(bus, &bus_attr_uevent); - kset_unregister(&bus->p->subsys); + + kset_unregister(sp->drivers_kset); + kset_unregister(sp->devices_kset); + kset_unregister(&sp->subsys); + subsys_put(sp); } EXPORT_SYMBOL_GPL(bus_unregister); -- cgit v1.2.3 From adac0375380a7d3e50194cc5b03b676b0321bded Mon Sep 17 00:00:00 2001 From: Greg Kroah-Hartman Date: Wed, 8 Feb 2023 12:13:17 +0100 Subject: driver core: bus: subsys_interface_register/unregister() cleanups Convert the subsys_interface_register and subsys_interface_unregister() functions to use bus_to_subsys() and not use the back-pointer to the private structure. This also requires changing the parameters on subsys_dev_iter_init() to iterate over the list properly. Cc: "Rafael J. Wysocki" Link: https://lore.kernel.org/r/20230208111330.439504-9-gregkh@linuxfoundation.org Signed-off-by: Greg Kroah-Hartman --- drivers/base/bus.c | 45 +++++++++++++++++++++++++++++---------------- 1 file changed, 29 insertions(+), 16 deletions(-) (limited to 'drivers/base') diff --git a/drivers/base/bus.c b/drivers/base/bus.c index d515a8363522..b831173071ec 100644 --- a/drivers/base/bus.c +++ b/drivers/base/bus.c @@ -1023,7 +1023,7 @@ struct subsys_dev_iter { /** * subsys_dev_iter_init - initialize subsys device iterator * @iter: subsys iterator to initialize - * @subsys: the subsys we wanna iterate over + * @sp: the subsys private (i.e. bus) we wanna iterate over * @start: the device to start iterating from, if any * @type: device_type of the devices to iterate over, NULL for all * @@ -1032,14 +1032,14 @@ struct subsys_dev_iter { * otherwise if it is NULL, the iteration starts at the beginning of * the list. */ -static void subsys_dev_iter_init(struct subsys_dev_iter *iter, struct bus_type *subsys, +static void subsys_dev_iter_init(struct subsys_dev_iter *iter, struct subsys_private *sp, struct device *start, const struct device_type *type) { struct klist_node *start_knode = NULL; if (start) start_knode = &start->p->knode_bus; - klist_iter_init_node(&subsys->p->klist_devices, &iter->ki, start_knode); + klist_iter_init_node(&sp->klist_devices, &iter->ki, start_knode); iter->type = type; } @@ -1084,26 +1084,31 @@ static void subsys_dev_iter_exit(struct subsys_dev_iter *iter) int subsys_interface_register(struct subsys_interface *sif) { - struct bus_type *subsys; + struct subsys_private *sp; struct subsys_dev_iter iter; struct device *dev; if (!sif || !sif->subsys) return -ENODEV; - subsys = bus_get(sif->subsys); - if (!subsys) + sp = bus_to_subsys(sif->subsys); + if (!sp) return -EINVAL; - mutex_lock(&subsys->p->mutex); - list_add_tail(&sif->node, &subsys->p->interfaces); + /* + * Reference in sp is now incremented and will be dropped when + * the interface is removed from the bus + */ + + mutex_lock(&sp->mutex); + list_add_tail(&sif->node, &sp->interfaces); if (sif->add_dev) { - subsys_dev_iter_init(&iter, subsys, NULL, NULL); + subsys_dev_iter_init(&iter, sp, NULL, NULL); while ((dev = subsys_dev_iter_next(&iter))) sif->add_dev(dev, sif); subsys_dev_iter_exit(&iter); } - mutex_unlock(&subsys->p->mutex); + mutex_unlock(&sp->mutex); return 0; } @@ -1111,26 +1116,34 @@ EXPORT_SYMBOL_GPL(subsys_interface_register); void subsys_interface_unregister(struct subsys_interface *sif) { - struct bus_type *subsys; + struct subsys_private *sp; struct subsys_dev_iter iter; struct device *dev; if (!sif || !sif->subsys) return; - subsys = sif->subsys; + sp = bus_to_subsys(sif->subsys); + if (!sp) + return; - mutex_lock(&subsys->p->mutex); + mutex_lock(&sp->mutex); list_del_init(&sif->node); if (sif->remove_dev) { - subsys_dev_iter_init(&iter, subsys, NULL, NULL); + subsys_dev_iter_init(&iter, sp, NULL, NULL); while ((dev = subsys_dev_iter_next(&iter))) sif->remove_dev(dev, sif); subsys_dev_iter_exit(&iter); } - mutex_unlock(&subsys->p->mutex); + mutex_unlock(&sp->mutex); - bus_put(subsys); + /* + * Decrement the reference count twice, once for the bus_to_subsys() + * call in the start of this function, and the second one from the + * reference increment in subsys_interface_register() + */ + subsys_put(sp); + subsys_put(sp); } EXPORT_SYMBOL_GPL(subsys_interface_unregister); -- cgit v1.2.3 From beea7892d4339e50f15e5131d51a95503678fdf6 Mon Sep 17 00:00:00 2001 From: Greg Kroah-Hartman Date: Wed, 8 Feb 2023 12:13:18 +0100 Subject: driver core: bus: bus_get_kset() cleanup Convert the bus_get_kset() function function to use bus_to_subsys() and not use the back-pointer to the private structure. Cc: "Rafael J. Wysocki" Link: https://lore.kernel.org/r/20230208111330.439504-10-gregkh@linuxfoundation.org Signed-off-by: Greg Kroah-Hartman --- drivers/base/bus.c | 11 ++++++++++- 1 file changed, 10 insertions(+), 1 deletion(-) (limited to 'drivers/base') diff --git a/drivers/base/bus.c b/drivers/base/bus.c index b831173071ec..105b6af924af 100644 --- a/drivers/base/bus.c +++ b/drivers/base/bus.c @@ -956,7 +956,16 @@ void bus_notify(struct device *dev, enum bus_notifier_event value) struct kset *bus_get_kset(struct bus_type *bus) { - return &bus->p->subsys; + struct subsys_private *sp = bus_to_subsys(bus); + struct kset *kset; + + if (!sp) + return NULL; + + kset = &sp->subsys; + subsys_put(sp); + + return kset; } EXPORT_SYMBOL_GPL(bus_get_kset); -- cgit v1.2.3 From 32a8121a19c4936672440c96371f6a8c2b0c3d4d Mon Sep 17 00:00:00 2001 From: Greg Kroah-Hartman Date: Wed, 8 Feb 2023 12:13:19 +0100 Subject: driver core: bus: bus_register/unregister_notifier() cleanups Convert the bus_register_notifier() and bus_unregister_notifier() public functions to use bus_to_subsys() and not use the back-pointer to the private structure as well as the bus_notify() function. Cc: "Rafael J. Wysocki" Link: https://lore.kernel.org/r/20230208111330.439504-11-gregkh@linuxfoundation.org Signed-off-by: Greg Kroah-Hartman --- drivers/base/bus.c | 28 +++++++++++++++++++++++----- 1 file changed, 23 insertions(+), 5 deletions(-) (limited to 'drivers/base') diff --git a/drivers/base/bus.c b/drivers/base/bus.c index 105b6af924af..81c0f7ff29a2 100644 --- a/drivers/base/bus.c +++ b/drivers/base/bus.c @@ -936,22 +936,40 @@ EXPORT_SYMBOL_GPL(bus_unregister); int bus_register_notifier(struct bus_type *bus, struct notifier_block *nb) { - return blocking_notifier_chain_register(&bus->p->bus_notifier, nb); + struct subsys_private *sp = bus_to_subsys(bus); + int retval; + + if (!sp) + return -EINVAL; + + retval = blocking_notifier_chain_register(&sp->bus_notifier, nb); + subsys_put(sp); + return retval; } EXPORT_SYMBOL_GPL(bus_register_notifier); int bus_unregister_notifier(struct bus_type *bus, struct notifier_block *nb) { - return blocking_notifier_chain_unregister(&bus->p->bus_notifier, nb); + struct subsys_private *sp = bus_to_subsys(bus); + int retval; + + if (!sp) + return -EINVAL; + retval = blocking_notifier_chain_unregister(&sp->bus_notifier, nb); + subsys_put(sp); + return retval; } EXPORT_SYMBOL_GPL(bus_unregister_notifier); void bus_notify(struct device *dev, enum bus_notifier_event value) { - struct bus_type *bus = dev->bus; + struct subsys_private *sp = bus_to_subsys(dev->bus); - if (bus) - blocking_notifier_call_chain(&bus->p->bus_notifier, value, dev); + if (!sp) + return; + + blocking_notifier_call_chain(&sp->bus_notifier, value, dev); + subsys_put(sp); } struct kset *bus_get_kset(struct bus_type *bus) -- cgit v1.2.3 From e4f056825ff5939254701d02ff3833ff27116225 Mon Sep 17 00:00:00 2001 From: Greg Kroah-Hartman Date: Wed, 8 Feb 2023 12:13:20 +0100 Subject: driver core: bus: bus_add/remove_driver() cleanups Convert the bus_add_driver() and bus_remove_driver() functions to use bus_to_subsys() and not use the back-pointer to the private structure. Cc: "Rafael J. Wysocki" Link: https://lore.kernel.org/r/20230208111330.439504-12-gregkh@linuxfoundation.org Signed-off-by: Greg Kroah-Hartman --- drivers/base/bus.c | 39 ++++++++++++++++++++++++++------------- 1 file changed, 26 insertions(+), 13 deletions(-) (limited to 'drivers/base') diff --git a/drivers/base/bus.c b/drivers/base/bus.c index 81c0f7ff29a2..eb8fb42afd4b 100644 --- a/drivers/base/bus.c +++ b/drivers/base/bus.c @@ -636,15 +636,18 @@ static DRIVER_ATTR_WO(uevent); */ int bus_add_driver(struct device_driver *drv) { - struct bus_type *bus; + struct subsys_private *sp = bus_to_subsys(drv->bus); struct driver_private *priv; int error = 0; - bus = bus_get(drv->bus); - if (!bus) + if (!sp) return -EINVAL; - pr_debug("bus: '%s': add driver %s\n", bus->name, drv->name); + /* + * Reference in sp is now incremented and will be dropped when + * the driver is removed from the bus + */ + pr_debug("bus: '%s': add driver %s\n", sp->bus->name, drv->name); priv = kzalloc(sizeof(*priv), GFP_KERNEL); if (!priv) { @@ -654,14 +657,14 @@ int bus_add_driver(struct device_driver *drv) klist_init(&priv->klist_devices, NULL, NULL); priv->driver = drv; drv->p = priv; - priv->kobj.kset = bus->p->drivers_kset; + priv->kobj.kset = sp->drivers_kset; error = kobject_init_and_add(&priv->kobj, &driver_ktype, NULL, "%s", drv->name); if (error) goto out_unregister; - klist_add_tail(&priv->knode_bus, &bus->p->klist_drivers); - if (drv->bus->p->drivers_autoprobe) { + klist_add_tail(&priv->knode_bus, &sp->klist_drivers); + if (sp->drivers_autoprobe) { error = driver_attach(drv); if (error) goto out_del_list; @@ -673,7 +676,7 @@ int bus_add_driver(struct device_driver *drv) printk(KERN_ERR "%s: uevent attr (%s) failed\n", __func__, drv->name); } - error = driver_add_groups(drv, bus->drv_groups); + error = driver_add_groups(drv, sp->bus->drv_groups); if (error) { /* How the hell do we get out of this pickle? Give up */ printk(KERN_ERR "%s: driver_add_groups(%s) failed\n", @@ -698,7 +701,7 @@ out_unregister: /* drv->p is freed in driver_release() */ drv->p = NULL; out_put_bus: - bus_put(bus); + subsys_put(sp); return error; } @@ -712,19 +715,29 @@ out_put_bus: */ void bus_remove_driver(struct device_driver *drv) { - if (!drv->bus) + struct subsys_private *sp = bus_to_subsys(drv->bus); + + if (!sp) return; + pr_debug("bus: '%s': remove driver %s\n", sp->bus->name, drv->name); + if (!drv->suppress_bind_attrs) remove_bind_files(drv); - driver_remove_groups(drv, drv->bus->drv_groups); + driver_remove_groups(drv, sp->bus->drv_groups); driver_remove_file(drv, &driver_attr_uevent); klist_remove(&drv->p->knode_bus); - pr_debug("bus: '%s': remove driver %s\n", drv->bus->name, drv->name); driver_detach(drv); module_remove_driver(drv); kobject_put(&drv->p->kobj); - bus_put(drv->bus); + + /* + * Decrement the reference count twice, once for the bus_to_subsys() + * call in the start of this function, and the second one from the + * reference increment in bus_add_driver() + */ + subsys_put(sp); + subsys_put(sp); } /* Helper for bus_rescan_devices's iter */ -- cgit v1.2.3 From 83b9148df2c95e23d0fcf1f714dec88ce5a0468b Mon Sep 17 00:00:00 2001 From: Greg Kroah-Hartman Date: Wed, 8 Feb 2023 12:13:21 +0100 Subject: driver core: bus: bus iterator cleanups Convert the bus_for_each_dev(), bus_find_device, and bus_for_each_drv() functions to use bus_to_subsys() and not use the back-pointer to the private structure. Cc: "Rafael J. Wysocki" Link: https://lore.kernel.org/r/20230208111330.439504-13-gregkh@linuxfoundation.org Signed-off-by: Greg Kroah-Hartman --- drivers/base/bus.c | 18 ++++++++++++------ 1 file changed, 12 insertions(+), 6 deletions(-) (limited to 'drivers/base') diff --git a/drivers/base/bus.c b/drivers/base/bus.c index eb8fb42afd4b..c6d310fd0ecf 100644 --- a/drivers/base/bus.c +++ b/drivers/base/bus.c @@ -355,18 +355,20 @@ static struct device *next_device(struct klist_iter *i) int bus_for_each_dev(const struct bus_type *bus, struct device *start, void *data, int (*fn)(struct device *, void *)) { + struct subsys_private *sp = bus_to_subsys(bus); struct klist_iter i; struct device *dev; int error = 0; - if (!bus || !bus->p) + if (!sp) return -EINVAL; - klist_iter_init_node(&bus->p->klist_devices, &i, + klist_iter_init_node(&sp->klist_devices, &i, (start ? &start->p->knode_bus : NULL)); while (!error && (dev = next_device(&i))) error = fn(dev, data); klist_iter_exit(&i); + subsys_put(sp); return error; } EXPORT_SYMBOL_GPL(bus_for_each_dev); @@ -390,18 +392,20 @@ struct device *bus_find_device(const struct bus_type *bus, struct device *start, const void *data, int (*match)(struct device *dev, const void *data)) { + struct subsys_private *sp = bus_to_subsys(bus); struct klist_iter i; struct device *dev; - if (!bus || !bus->p) + if (!sp) return NULL; - klist_iter_init_node(&bus->p->klist_devices, &i, + klist_iter_init_node(&sp->klist_devices, &i, (start ? &start->p->knode_bus : NULL)); while ((dev = next_device(&i))) if (match(dev, data) && get_device(dev)) break; klist_iter_exit(&i); + subsys_put(sp); return dev; } EXPORT_SYMBOL_GPL(bus_find_device); @@ -440,18 +444,20 @@ static struct device_driver *next_driver(struct klist_iter *i) int bus_for_each_drv(const struct bus_type *bus, struct device_driver *start, void *data, int (*fn)(struct device_driver *, void *)) { + struct subsys_private *sp = bus_to_subsys(bus); struct klist_iter i; struct device_driver *drv; int error = 0; - if (!bus) + if (!sp) return -EINVAL; - klist_iter_init_node(&bus->p->klist_drivers, &i, + klist_iter_init_node(&sp->klist_drivers, &i, start ? &start->p->knode_bus : NULL); while ((drv = next_driver(&i)) && !error) error = fn(drv, data); klist_iter_exit(&i); + subsys_put(sp); return error; } EXPORT_SYMBOL_GPL(bus_for_each_drv); -- cgit v1.2.3 From b5aaecb82ac0d008ddec8132979398b9b4e925fc Mon Sep 17 00:00:00 2001 From: Greg Kroah-Hartman Date: Wed, 8 Feb 2023 12:13:22 +0100 Subject: driver core: bus: clean up bus_sort_breadthfirst() Convert the bus_sort_breadthfirst() function to use bus_to_subsys() and not use the back-pointer to the private structure. This also allows us to get rid of bus_get_device_klist() which was only being used by this one internal function. Cc: "Rafael J. Wysocki" Link: https://lore.kernel.org/r/20230208111330.439504-14-gregkh@linuxfoundation.org Signed-off-by: Greg Kroah-Hartman --- drivers/base/bus.c | 11 +++++------ 1 file changed, 5 insertions(+), 6 deletions(-) (limited to 'drivers/base') diff --git a/drivers/base/bus.c b/drivers/base/bus.c index c6d310fd0ecf..87c44661dcad 100644 --- a/drivers/base/bus.c +++ b/drivers/base/bus.c @@ -1006,11 +1006,6 @@ struct kset *bus_get_kset(struct bus_type *bus) } EXPORT_SYMBOL_GPL(bus_get_kset); -static struct klist *bus_get_device_klist(struct bus_type *bus) -{ - return &bus->p->klist_devices; -} - /* * Yes, this forcibly breaks the klist abstraction temporarily. It * just wants to sort the klist, not change reference counts and @@ -1042,13 +1037,16 @@ void bus_sort_breadthfirst(struct bus_type *bus, int (*compare)(const struct device *a, const struct device *b)) { + struct subsys_private *sp = bus_to_subsys(bus); LIST_HEAD(sorted_devices); struct klist_node *n, *tmp; struct device_private *dev_prv; struct device *dev; struct klist *device_klist; - device_klist = bus_get_device_klist(bus); + if (!sp) + return; + device_klist = &sp->klist_devices; spin_lock(&device_klist->k_lock); list_for_each_entry_safe(n, tmp, &device_klist->k_list, n_node) { @@ -1058,6 +1056,7 @@ void bus_sort_breadthfirst(struct bus_type *bus, } list_splice(&sorted_devices, &device_klist->k_list); spin_unlock(&device_klist->k_lock); + subsys_put(sp); } EXPORT_SYMBOL_GPL(bus_sort_breadthfirst); -- cgit v1.2.3 From adc18506941dcafe216aba80766ab51c890358e8 Mon Sep 17 00:00:00 2001 From: Greg Kroah-Hartman Date: Wed, 8 Feb 2023 12:13:23 +0100 Subject: driver core: move driver_find() to bus.c This function really is a bus function, not a driver one, so move it from driver.c to bus.c so that we can clean up some internal bus logic easier. Cc: "Rafael J. Wysocki" Link: https://lore.kernel.org/r/20230208111330.439504-15-gregkh@linuxfoundation.org Signed-off-by: Greg Kroah-Hartman --- drivers/base/bus.c | 27 +++++++++++++++++++++++++++ drivers/base/driver.c | 27 --------------------------- 2 files changed, 27 insertions(+), 27 deletions(-) (limited to 'drivers/base') diff --git a/drivers/base/bus.c b/drivers/base/bus.c index 87c44661dcad..1040c724e50c 100644 --- a/drivers/base/bus.c +++ b/drivers/base/bus.c @@ -1288,6 +1288,33 @@ int subsys_virtual_register(struct bus_type *subsys, } EXPORT_SYMBOL_GPL(subsys_virtual_register); +/** + * driver_find - locate driver on a bus by its name. + * @name: name of the driver. + * @bus: bus to scan for the driver. + * + * Call kset_find_obj() to iterate over list of drivers on + * a bus to find driver by name. Return driver if found. + * + * This routine provides no locking to prevent the driver it returns + * from being unregistered or unloaded while the caller is using it. + * The caller is responsible for preventing this. + */ +struct device_driver *driver_find(const char *name, struct bus_type *bus) +{ + struct kobject *k = kset_find_obj(bus->p->drivers_kset, name); + struct driver_private *priv; + + if (k) { + /* Drop reference added by kset_find_obj() */ + kobject_put(k); + priv = to_driver(k); + return priv->driver; + } + return NULL; +} +EXPORT_SYMBOL_GPL(driver_find); + int __init buses_init(void) { bus_kset = kset_create_and_add("bus", &bus_uevent_ops, NULL); diff --git a/drivers/base/driver.c b/drivers/base/driver.c index 676b6275d5b5..aa5e5166a671 100644 --- a/drivers/base/driver.c +++ b/drivers/base/driver.c @@ -274,30 +274,3 @@ void driver_unregister(struct device_driver *drv) bus_remove_driver(drv); } EXPORT_SYMBOL_GPL(driver_unregister); - -/** - * driver_find - locate driver on a bus by its name. - * @name: name of the driver. - * @bus: bus to scan for the driver. - * - * Call kset_find_obj() to iterate over list of drivers on - * a bus to find driver by name. Return driver if found. - * - * This routine provides no locking to prevent the driver it returns - * from being unregistered or unloaded while the caller is using it. - * The caller is responsible for preventing this. - */ -struct device_driver *driver_find(const char *name, struct bus_type *bus) -{ - struct kobject *k = kset_find_obj(bus->p->drivers_kset, name); - struct driver_private *priv; - - if (k) { - /* Drop reference added by kset_find_obj() */ - kobject_put(k); - priv = to_driver(k); - return priv->driver; - } - return NULL; -} -EXPORT_SYMBOL_GPL(driver_find); -- cgit v1.2.3 From fb451966ae7d7e6d6802de39bda5fa2f15781244 Mon Sep 17 00:00:00 2001 From: Greg Kroah-Hartman Date: Wed, 8 Feb 2023 12:13:24 +0100 Subject: driver core: bus: clean up driver_find() Convert the driver_find() function to use bus_to_subsys() and not use the back-pointer to the private structure. Cc: "Rafael J. Wysocki" Link: https://lore.kernel.org/r/20230208111330.439504-16-gregkh@linuxfoundation.org Signed-off-by: Greg Kroah-Hartman --- drivers/base/bus.c | 23 +++++++++++++++-------- 1 file changed, 15 insertions(+), 8 deletions(-) (limited to 'drivers/base') diff --git a/drivers/base/bus.c b/drivers/base/bus.c index 1040c724e50c..98edc7aa4fc4 100644 --- a/drivers/base/bus.c +++ b/drivers/base/bus.c @@ -1302,16 +1302,23 @@ EXPORT_SYMBOL_GPL(subsys_virtual_register); */ struct device_driver *driver_find(const char *name, struct bus_type *bus) { - struct kobject *k = kset_find_obj(bus->p->drivers_kset, name); + struct subsys_private *sp = bus_to_subsys(bus); + struct kobject *k; struct driver_private *priv; - if (k) { - /* Drop reference added by kset_find_obj() */ - kobject_put(k); - priv = to_driver(k); - return priv->driver; - } - return NULL; + if (!sp) + return NULL; + + k = kset_find_obj(sp->drivers_kset, name); + subsys_put(sp); + if (!k) + return NULL; + + priv = to_driver(k); + + /* Drop reference added by kset_find_obj() */ + kobject_put(k); + return priv->driver; } EXPORT_SYMBOL_GPL(driver_find); -- cgit v1.2.3 From 63b823d7d3cd275c3347233f95bdf966a595dbc8 Mon Sep 17 00:00:00 2001 From: Greg Kroah-Hartman Date: Wed, 8 Feb 2023 12:13:25 +0100 Subject: driver core: create bus_is_registered() A local function to the driver core to determine if a bus really is registered with the kernel or not. To be used only by the driver core code, as part of the driver registration path as it's not really "safe" because the bus could be unregistered instantly after being called. Cc: "Rafael J. Wysocki" Link: https://lore.kernel.org/r/20230208111330.439504-17-gregkh@linuxfoundation.org Signed-off-by: Greg Kroah-Hartman --- drivers/base/base.h | 1 + drivers/base/bus.c | 16 ++++++++++++++++ drivers/base/driver.c | 2 +- 3 files changed, 18 insertions(+), 1 deletion(-) (limited to 'drivers/base') diff --git a/drivers/base/base.h b/drivers/base/base.h index 9e06c18c7a64..726a12a244c0 100644 --- a/drivers/base/base.h +++ b/drivers/base/base.h @@ -146,6 +146,7 @@ extern int bus_add_device(struct device *dev); extern void bus_probe_device(struct device *dev); extern void bus_remove_device(struct device *dev); void bus_notify(struct device *dev, enum bus_notifier_event value); +bool bus_is_registered(const struct bus_type *bus); extern int bus_add_driver(struct device_driver *drv); extern void bus_remove_driver(struct device_driver *drv); diff --git a/drivers/base/bus.c b/drivers/base/bus.c index 98edc7aa4fc4..c82c65fde8a1 100644 --- a/drivers/base/bus.c +++ b/drivers/base/bus.c @@ -1322,6 +1322,22 @@ struct device_driver *driver_find(const char *name, struct bus_type *bus) } EXPORT_SYMBOL_GPL(driver_find); +/* + * Warning, the value could go to "removed" instantly after calling this function, so be very + * careful when calling it... + */ +bool bus_is_registered(const struct bus_type *bus) +{ + struct subsys_private *sp = bus_to_subsys(bus); + bool is_initialized = false; + + if (sp) { + is_initialized = true; + subsys_put(sp); + } + return is_initialized; +} + int __init buses_init(void) { bus_kset = kset_create_and_add("bus", &bus_uevent_ops, NULL); diff --git a/drivers/base/driver.c b/drivers/base/driver.c index aa5e5166a671..c8436c26ed6a 100644 --- a/drivers/base/driver.c +++ b/drivers/base/driver.c @@ -224,7 +224,7 @@ int driver_register(struct device_driver *drv) int ret; struct device_driver *other; - if (!drv->bus->p) { + if (!bus_is_registered(drv->bus)) { pr_err("Driver '%s' was unable to register with bus_type '%s' because the bus was not initialized.\n", drv->name, drv->bus->name); return -EINVAL; -- cgit v1.2.3 From d2bf38c088e0d5467a0e8a2055d6f95dff5c2125 Mon Sep 17 00:00:00 2001 From: Greg Kroah-Hartman Date: Wed, 8 Feb 2023 12:13:26 +0100 Subject: driver core: remove private pointer from struct bus_type Now that the driver code has been refactored to not rely on the pointer from a struct bus_type to the private structure it can be safely removed from the structure entirely. This will allow most bus_type structures to now be marked as const. Cc: "Rafael J. Wysocki" Link: https://lore.kernel.org/r/20230208111330.439504-18-gregkh@linuxfoundation.org Signed-off-by: Greg Kroah-Hartman --- drivers/base/bus.c | 4 ---- 1 file changed, 4 deletions(-) (limited to 'drivers/base') diff --git a/drivers/base/bus.c b/drivers/base/bus.c index c82c65fde8a1..719cd3a1354c 100644 --- a/drivers/base/bus.c +++ b/drivers/base/bus.c @@ -205,11 +205,9 @@ EXPORT_SYMBOL_GPL(bus_remove_file); static void bus_release(struct kobject *kobj) { struct subsys_private *priv = to_subsys_private(kobj); - struct bus_type *bus = priv->bus; lockdep_unregister_key(&priv->lock_key); kfree(priv); - bus->p = NULL; } static const struct kobj_type bus_ktype = { @@ -854,7 +852,6 @@ int bus_register(struct bus_type *bus) return -ENOMEM; priv->bus = bus; - bus->p = priv; BLOCKING_INIT_NOTIFIER_HEAD(&priv->bus_notifier); @@ -917,7 +914,6 @@ bus_uevent_fail: kset_unregister(&priv->subsys); out: kfree(priv); - bus->p = NULL; return retval; } EXPORT_SYMBOL_GPL(bus_register); -- cgit v1.2.3 From bc8b7931012f0511f016e2f048d1f4222db8e08f Mon Sep 17 00:00:00 2001 From: Greg Kroah-Hartman Date: Wed, 8 Feb 2023 12:13:27 +0100 Subject: driver core: bus: constify bus_register/unregister_notifier() The bus_register_notifier() and bus_unregister_notifier() functions should be taking a const * to bus_type, not just a * so fix that up. Cc: "Rafael J. Wysocki" Link: https://lore.kernel.org/r/20230208111330.439504-19-gregkh@linuxfoundation.org Signed-off-by: Greg Kroah-Hartman --- drivers/base/bus.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'drivers/base') diff --git a/drivers/base/bus.c b/drivers/base/bus.c index 719cd3a1354c..0920fb117896 100644 --- a/drivers/base/bus.c +++ b/drivers/base/bus.c @@ -949,7 +949,7 @@ void bus_unregister(struct bus_type *bus) } EXPORT_SYMBOL_GPL(bus_unregister); -int bus_register_notifier(struct bus_type *bus, struct notifier_block *nb) +int bus_register_notifier(const struct bus_type *bus, struct notifier_block *nb) { struct subsys_private *sp = bus_to_subsys(bus); int retval; @@ -963,7 +963,7 @@ int bus_register_notifier(struct bus_type *bus, struct notifier_block *nb) } EXPORT_SYMBOL_GPL(bus_register_notifier); -int bus_unregister_notifier(struct bus_type *bus, struct notifier_block *nb) +int bus_unregister_notifier(const struct bus_type *bus, struct notifier_block *nb) { struct subsys_private *sp = bus_to_subsys(bus); int retval; -- cgit v1.2.3 From f91482be9b480dfce2616f5ba3fa548b8ed41efb Mon Sep 17 00:00:00 2001 From: Greg Kroah-Hartman Date: Wed, 8 Feb 2023 12:13:28 +0100 Subject: driver core: bus: constify bus_get_kset() The bus_get_kset() function should be taking a const * to bus_type, not just a * so fix that up. Cc: "Rafael J. Wysocki" Link: https://lore.kernel.org/r/20230208111330.439504-20-gregkh@linuxfoundation.org Signed-off-by: Greg Kroah-Hartman --- drivers/base/bus.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers/base') diff --git a/drivers/base/bus.c b/drivers/base/bus.c index 0920fb117896..c394727b8253 100644 --- a/drivers/base/bus.c +++ b/drivers/base/bus.c @@ -987,7 +987,7 @@ void bus_notify(struct device *dev, enum bus_notifier_event value) subsys_put(sp); } -struct kset *bus_get_kset(struct bus_type *bus) +struct kset *bus_get_kset(const struct bus_type *bus) { struct subsys_private *sp = bus_to_subsys(bus); struct kset *kset; -- cgit v1.2.3 From 4dd1f3f8f99ec51dbcfeffe95b8ab68161a332a4 Mon Sep 17 00:00:00 2001 From: Greg Kroah-Hartman Date: Wed, 8 Feb 2023 12:13:29 +0100 Subject: driver core: bus: constify some internal functions The functions add_probe_files() and remove_probe_files() should be taking a const * to bus_type, not just a *, so fix that up. These functions should really be removed entirely and an attribute group used instead, but for now, make this change so that other const work can continue. Cc: "Rafael J. Wysocki" Link: https://lore.kernel.org/r/20230208111330.439504-21-gregkh@linuxfoundation.org Signed-off-by: Greg Kroah-Hartman --- drivers/base/bus.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'drivers/base') diff --git a/drivers/base/bus.c b/drivers/base/bus.c index c394727b8253..bcff24b11bb3 100644 --- a/drivers/base/bus.c +++ b/drivers/base/bus.c @@ -603,7 +603,7 @@ static void remove_bind_files(struct device_driver *drv) static BUS_ATTR_WO(drivers_probe); static BUS_ATTR_RW(drivers_autoprobe); -static int add_probe_files(struct bus_type *bus) +static int add_probe_files(const struct bus_type *bus) { int retval; @@ -618,7 +618,7 @@ out: return retval; } -static void remove_probe_files(struct bus_type *bus) +static void remove_probe_files(const struct bus_type *bus) { bus_remove_file(bus, &bus_attr_drivers_autoprobe); bus_remove_file(bus, &bus_attr_drivers_probe); -- cgit v1.2.3 From ad8685d0f61a6fc1dc2e5874f4924ff5028c5954 Mon Sep 17 00:00:00 2001 From: Greg Kroah-Hartman Date: Wed, 8 Feb 2023 12:13:30 +0100 Subject: driver core: bus: constify bus_unregister() The bus_unregister() function can now take a const * to bus_type, not just a * so fix that up. Cc: "Rafael J. Wysocki" Link: https://lore.kernel.org/r/20230208111330.439504-22-gregkh@linuxfoundation.org Signed-off-by: Greg Kroah-Hartman --- drivers/base/bus.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers/base') diff --git a/drivers/base/bus.c b/drivers/base/bus.c index bcff24b11bb3..301b9c6ece86 100644 --- a/drivers/base/bus.c +++ b/drivers/base/bus.c @@ -925,7 +925,7 @@ EXPORT_SYMBOL_GPL(bus_register); * Unregister the child subsystems and the bus itself. * Finally, we call bus_put() to release the refcount */ -void bus_unregister(struct bus_type *bus) +void bus_unregister(const struct bus_type *bus) { struct subsys_private *sp = bus_to_subsys(bus); struct kobject *bus_kobj; -- cgit v1.2.3 From 8c99377e614f8abfd881c34611002b2af5ab1ee8 Mon Sep 17 00:00:00 2001 From: Greg Kroah-Hartman Date: Thu, 9 Feb 2023 10:35:56 +0100 Subject: driver core: bus: add bus_get_dev_root() function Instead of poking around in the struct bus_type directly for the dev_root pointer, provide a function to return it properly reference counted, if it is present in the bus. This will be needed to move the pointer out of struct bus_type in the future. Use the function in the driver core code at the same time it is introduced to verify that it works properly. Cc: "Rafael J. Wysocki" Link: https://lore.kernel.org/r/20230209093556.19132-1-gregkh@linuxfoundation.org Signed-off-by: Greg Kroah-Hartman --- drivers/base/bus.c | 20 ++++++++++++++++++++ drivers/base/core.c | 14 +++++++++++--- drivers/base/cpu.c | 10 +++++++--- 3 files changed, 38 insertions(+), 6 deletions(-) (limited to 'drivers/base') diff --git a/drivers/base/bus.c b/drivers/base/bus.c index 301b9c6ece86..def1e009ad9d 100644 --- a/drivers/base/bus.c +++ b/drivers/base/bus.c @@ -1334,6 +1334,26 @@ bool bus_is_registered(const struct bus_type *bus) return is_initialized; } +/** + * bus_get_dev_root - return a pointer to the "device root" of a bus + * @bus: bus to return the device root of. + * + * If a bus has a "device root" structure, return it, WITH THE REFERENCE + * COUNT INCREMENTED. + * + * Note, when finished with the device, a call to put_device() is required. + * + * If the device root is not present (or bus is not a valid pointer), NULL + * will be returned. + */ +struct device *bus_get_dev_root(const struct bus_type *bus) +{ + if (bus) + return get_device(bus->dev_root); + return NULL; +} +EXPORT_SYMBOL_GPL(bus_get_dev_root); + int __init buses_init(void) { bus_kset = kset_create_and_add("bus", &bus_uevent_ops, NULL); diff --git a/drivers/base/core.c b/drivers/base/core.c index 2712a1a1e959..f9297c68214a 100644 --- a/drivers/base/core.c +++ b/drivers/base/core.c @@ -3173,8 +3173,9 @@ static DEFINE_MUTEX(gdp_mutex); static struct kobject *get_device_parent(struct device *dev, struct device *parent) { + struct kobject *kobj = NULL; + if (dev->class) { - struct kobject *kobj = NULL; struct kobject *parent_kobj; struct kobject *k; @@ -3222,8 +3223,15 @@ static struct kobject *get_device_parent(struct device *dev, } /* subsystems can specify a default root directory for their devices */ - if (!parent && dev->bus && dev->bus->dev_root) - return &dev->bus->dev_root->kobj; + if (!parent && dev->bus) { + struct device *dev_root = bus_get_dev_root(dev->bus); + + if (dev_root) { + kobj = &dev_root->kobj; + put_device(dev_root); + return kobj; + } + } if (parent) return &parent->kobj; diff --git a/drivers/base/cpu.c b/drivers/base/cpu.c index 441eb5bdec7d..8bb623039bb2 100644 --- a/drivers/base/cpu.c +++ b/drivers/base/cpu.c @@ -610,9 +610,13 @@ static const struct attribute_group cpu_root_vulnerabilities_group = { static void __init cpu_register_vulnerabilities(void) { - if (sysfs_create_group(&cpu_subsys.dev_root->kobj, - &cpu_root_vulnerabilities_group)) - pr_err("Unable to register CPU vulnerabilities\n"); + struct device *dev = bus_get_dev_root(&cpu_subsys); + + if (dev) { + if (sysfs_create_group(&dev->kobj, &cpu_root_vulnerabilities_group)) + pr_err("Unable to register CPU vulnerabilities\n"); + put_device(dev); + } } #else -- cgit v1.2.3 From ccfc901f01170721554963745f7bd94c15235c96 Mon Sep 17 00:00:00 2001 From: Greg Kroah-Hartman Date: Fri, 10 Feb 2023 10:13:18 +0100 Subject: driver core: bus: update my copyright notice There's been some work done recently to the drivers/base/bus.c file so update the copyright notice in it to make those who track those types of things have an easier job. Cc: "Rafael J. Wysocki" Link: https://lore.kernel.org/r/20230210091318.733561-1-gregkh@linuxfoundation.org Signed-off-by: Greg Kroah-Hartman --- drivers/base/bus.c | 1 + 1 file changed, 1 insertion(+) (limited to 'drivers/base') diff --git a/drivers/base/bus.c b/drivers/base/bus.c index def1e009ad9d..cfe8615d5106 100644 --- a/drivers/base/bus.c +++ b/drivers/base/bus.c @@ -6,6 +6,7 @@ * Copyright (c) 2002-3 Open Source Development Labs * Copyright (c) 2007 Greg Kroah-Hartman * Copyright (c) 2007 Novell Inc. + * Copyright (c) 2023 Greg Kroah-Hartman */ #include -- cgit v1.2.3 From 31b4b6730fd4f5d503c9f23619c920ce7b794754 Mon Sep 17 00:00:00 2001 From: Longlong Xia Date: Fri, 10 Feb 2023 09:54:42 +0000 Subject: driver core: add error handling for devtmpfs_create_node() In some cases, devtmpfs_create_node() can return error value. So, make use of it. Signed-off-by: Longlong Xia Link: https://lore.kernel.org/r/20230210095444.4067307-2-xialonglong1@huawei.com Signed-off-by: Greg Kroah-Hartman --- drivers/base/core.c | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) (limited to 'drivers/base') diff --git a/drivers/base/core.c b/drivers/base/core.c index f9297c68214a..4fc6f27641bf 100644 --- a/drivers/base/core.c +++ b/drivers/base/core.c @@ -3602,7 +3602,9 @@ int device_add(struct device *dev) if (error) goto SysEntryError; - devtmpfs_create_node(dev); + error = devtmpfs_create_node(dev); + if (error) + goto DevtmpfsError; } /* Notify clients of device addition. This call must come @@ -3658,6 +3660,8 @@ int device_add(struct device *dev) done: put_device(dev); return error; + DevtmpfsError: + device_remove_sys_dev_entry(dev); SysEntryError: if (MAJOR(dev->devt)) device_remove_file(dev, &dev_attr_dev); -- cgit v1.2.3 From 90a9d5ff225267b3376f73c19f21174e3b6d7746 Mon Sep 17 00:00:00 2001 From: Longlong Xia Date: Fri, 10 Feb 2023 09:54:43 +0000 Subject: devtmpfs: add debug info to handle() Because handle() is the core function for processing devtmpfs requests, Let's add some debug info in handle() to help users know why failed. Signed-off-by: Longlong Xia Link: https://lore.kernel.org/r/20230210095444.4067307-3-xialonglong1@huawei.com Signed-off-by: Greg Kroah-Hartman --- drivers/base/devtmpfs.c | 12 ++++++++++-- 1 file changed, 10 insertions(+), 2 deletions(-) (limited to 'drivers/base') diff --git a/drivers/base/devtmpfs.c b/drivers/base/devtmpfs.c index 95ebc18ded50..f273bccd5494 100644 --- a/drivers/base/devtmpfs.c +++ b/drivers/base/devtmpfs.c @@ -389,10 +389,18 @@ static __initdata DECLARE_COMPLETION(setup_done); static int handle(const char *name, umode_t mode, kuid_t uid, kgid_t gid, struct device *dev) { + int ret; + if (mode) - return handle_create(name, mode, uid, gid, dev); + ret = handle_create(name, mode, uid, gid, dev); else - return handle_remove(name, dev); + ret = handle_remove(name, dev); + + if (ret) + dev_err(dev, "failed to %s %s, ret = %d\n", + mode ? "create" : "remove", name, ret); + + return ret; } static void __noreturn devtmpfs_work_loop(void) -- cgit v1.2.3 From 9d3fe6aa6b9517408064c7c3134187e8ec77dbf7 Mon Sep 17 00:00:00 2001 From: Longlong Xia Date: Fri, 10 Feb 2023 09:54:44 +0000 Subject: devtmpfs: remove return value of devtmpfs_delete_node() The only caller of device_del() does not check the return value. And there's nothing we can do when cleaning things up on a remove path. Let's make it a void function. Signed-off-by: Longlong Xia Link: https://lore.kernel.org/r/20230210095444.4067307-4-xialonglong1@huawei.com Signed-off-by: Greg Kroah-Hartman --- drivers/base/base.h | 4 ++-- drivers/base/devtmpfs.c | 8 ++++---- 2 files changed, 6 insertions(+), 6 deletions(-) (limited to 'drivers/base') diff --git a/drivers/base/base.h b/drivers/base/base.h index 726a12a244c0..80cdf3fcb458 100644 --- a/drivers/base/base.h +++ b/drivers/base/base.h @@ -215,10 +215,10 @@ void device_pm_move_to_tail(struct device *dev); #ifdef CONFIG_DEVTMPFS int devtmpfs_create_node(struct device *dev); -int devtmpfs_delete_node(struct device *dev); +void devtmpfs_delete_node(struct device *dev); #else static inline int devtmpfs_create_node(struct device *dev) { return 0; } -static inline int devtmpfs_delete_node(struct device *dev) { return 0; } +static inline void devtmpfs_delete_node(struct device *dev) { } #endif void software_node_notify(struct device *dev); diff --git a/drivers/base/devtmpfs.c b/drivers/base/devtmpfs.c index f273bccd5494..a3a2657b9587 100644 --- a/drivers/base/devtmpfs.c +++ b/drivers/base/devtmpfs.c @@ -147,22 +147,22 @@ int devtmpfs_create_node(struct device *dev) return devtmpfs_submit_req(&req, tmp); } -int devtmpfs_delete_node(struct device *dev) +void devtmpfs_delete_node(struct device *dev) { const char *tmp = NULL; struct req req; if (!thread) - return 0; + return; req.name = device_get_devnode(dev, NULL, NULL, NULL, &tmp); if (!req.name) - return -ENOMEM; + return; req.mode = 0; req.dev = dev; - return devtmpfs_submit_req(&req, tmp); + devtmpfs_submit_req(&req, tmp); } static int dev_mkdir(const char *name, umode_t mode) -- cgit v1.2.3 From 2bc19066bda340540468ff88c2b1a798983bfd28 Mon Sep 17 00:00:00 2001 From: Greg Kroah-Hartman Date: Fri, 10 Feb 2023 11:24:08 +0100 Subject: driver core: cpu: don't hand-override the uevent bus_type callback. Instead of having to change the uevent bus_type callback by hand at runtime, set it at build time based on the build configuration options, making this much simpler to maintain and understand (and allow to make the structure constant.) Cc: "Rafael J. Wysocki" Link: https://lore.kernel.org/r/20230210102408.1083177-1-gregkh@linuxfoundation.org Signed-off-by: Greg Kroah-Hartman --- drivers/base/cpu.c | 28 ++++++++++++++-------------- 1 file changed, 14 insertions(+), 14 deletions(-) (limited to 'drivers/base') diff --git a/drivers/base/cpu.c b/drivers/base/cpu.c index 8bb623039bb2..182c6122f815 100644 --- a/drivers/base/cpu.c +++ b/drivers/base/cpu.c @@ -125,17 +125,6 @@ static DEVICE_ATTR(release, S_IWUSR, NULL, cpu_release_store); #endif /* CONFIG_ARCH_CPU_PROBE_RELEASE */ #endif /* CONFIG_HOTPLUG_CPU */ -struct bus_type cpu_subsys = { - .name = "cpu", - .dev_name = "cpu", - .match = cpu_subsys_match, -#ifdef CONFIG_HOTPLUG_CPU - .online = cpu_subsys_online, - .offline = cpu_subsys_offline, -#endif -}; -EXPORT_SYMBOL_GPL(cpu_subsys); - #ifdef CONFIG_KEXEC #include @@ -348,6 +337,20 @@ static int cpu_uevent(const struct device *dev, struct kobj_uevent_env *env) } #endif +struct bus_type cpu_subsys = { + .name = "cpu", + .dev_name = "cpu", + .match = cpu_subsys_match, +#ifdef CONFIG_HOTPLUG_CPU + .online = cpu_subsys_online, + .offline = cpu_subsys_offline, +#endif +#ifdef CONFIG_GENERIC_CPU_AUTOPROBE + .uevent = cpu_uevent, +#endif +}; +EXPORT_SYMBOL_GPL(cpu_subsys); + /* * register_cpu - Setup a sysfs device for a CPU. * @cpu - cpu->hotpluggable field set to 1 will generate a control file in @@ -368,9 +371,6 @@ int register_cpu(struct cpu *cpu, int num) cpu->dev.offline_disabled = !cpu->hotpluggable; cpu->dev.offline = !cpu_online(num); cpu->dev.of_node = of_get_cpu_node(num, NULL); -#ifdef CONFIG_GENERIC_CPU_AUTOPROBE - cpu->dev.bus->uevent = cpu_uevent; -#endif cpu->dev.groups = common_cpu_attr_groups; if (cpu->hotpluggable) cpu->dev.groups = hotplugable_cpu_attr_groups; -- cgit v1.2.3 From d3583f06782cae72374464f9c29b2056fa0bd012 Mon Sep 17 00:00:00 2001 From: Greg Kroah-Hartman Date: Tue, 14 Feb 2023 08:59:13 +0100 Subject: Revert "devtmpfs: remove return value of devtmpfs_delete_node()" This reverts commit 9d3fe6aa6b9517408064c7c3134187e8ec77dbf7 as it is reported to cause boot regressions. Link: https://lore.kernel.org/r/Y+rSXg14z1Myd8Px@dev-arch.thelio-3990X Reported-by: Nathan Chancellor Cc: Longlong Xia Signed-off-by: Greg Kroah-Hartman --- drivers/base/base.h | 4 ++-- drivers/base/devtmpfs.c | 8 ++++---- 2 files changed, 6 insertions(+), 6 deletions(-) (limited to 'drivers/base') diff --git a/drivers/base/base.h b/drivers/base/base.h index 80cdf3fcb458..726a12a244c0 100644 --- a/drivers/base/base.h +++ b/drivers/base/base.h @@ -215,10 +215,10 @@ void device_pm_move_to_tail(struct device *dev); #ifdef CONFIG_DEVTMPFS int devtmpfs_create_node(struct device *dev); -void devtmpfs_delete_node(struct device *dev); +int devtmpfs_delete_node(struct device *dev); #else static inline int devtmpfs_create_node(struct device *dev) { return 0; } -static inline void devtmpfs_delete_node(struct device *dev) { } +static inline int devtmpfs_delete_node(struct device *dev) { return 0; } #endif void software_node_notify(struct device *dev); diff --git a/drivers/base/devtmpfs.c b/drivers/base/devtmpfs.c index a3a2657b9587..f273bccd5494 100644 --- a/drivers/base/devtmpfs.c +++ b/drivers/base/devtmpfs.c @@ -147,22 +147,22 @@ int devtmpfs_create_node(struct device *dev) return devtmpfs_submit_req(&req, tmp); } -void devtmpfs_delete_node(struct device *dev) +int devtmpfs_delete_node(struct device *dev) { const char *tmp = NULL; struct req req; if (!thread) - return; + return 0; req.name = device_get_devnode(dev, NULL, NULL, NULL, &tmp); if (!req.name) - return; + return -ENOMEM; req.mode = 0; req.dev = dev; - devtmpfs_submit_req(&req, tmp); + return devtmpfs_submit_req(&req, tmp); } static int dev_mkdir(const char *name, umode_t mode) -- cgit v1.2.3 From 48c9899affd51f7acfc07a3f4d777b6eeb73a451 Mon Sep 17 00:00:00 2001 From: Greg Kroah-Hartman Date: Tue, 14 Feb 2023 08:59:22 +0100 Subject: Revert "devtmpfs: add debug info to handle()" This reverts commit 90a9d5ff225267b3376f73c19f21174e3b6d7746 as it is reported to cause boot regressions. Link: https://lore.kernel.org/r/Y+rSXg14z1Myd8Px@dev-arch.thelio-3990X Reported-by: Nathan Chancellor Cc: Longlong Xia Signed-off-by: Greg Kroah-Hartman --- drivers/base/devtmpfs.c | 12 ++---------- 1 file changed, 2 insertions(+), 10 deletions(-) (limited to 'drivers/base') diff --git a/drivers/base/devtmpfs.c b/drivers/base/devtmpfs.c index f273bccd5494..95ebc18ded50 100644 --- a/drivers/base/devtmpfs.c +++ b/drivers/base/devtmpfs.c @@ -389,18 +389,10 @@ static __initdata DECLARE_COMPLETION(setup_done); static int handle(const char *name, umode_t mode, kuid_t uid, kgid_t gid, struct device *dev) { - int ret; - if (mode) - ret = handle_create(name, mode, uid, gid, dev); + return handle_create(name, mode, uid, gid, dev); else - ret = handle_remove(name, dev); - - if (ret) - dev_err(dev, "failed to %s %s, ret = %d\n", - mode ? "create" : "remove", name, ret); - - return ret; + return handle_remove(name, dev); } static void __noreturn devtmpfs_work_loop(void) -- cgit v1.2.3 From 17c45768fdf970b8a2ea9745783ff6a0512fca11 Mon Sep 17 00:00:00 2001 From: Greg Kroah-Hartman Date: Tue, 14 Feb 2023 08:59:33 +0100 Subject: Revert "driver core: add error handling for devtmpfs_create_node()" This reverts commit 31b4b6730fd4f5d503c9f23619c920ce7b794754 as it is reported to cause boot regressions. Link: https://lore.kernel.org/r/Y+rSXg14z1Myd8Px@dev-arch.thelio-3990X Reported-by: Nathan Chancellor Cc: Longlong Xia Signed-off-by: Greg Kroah-Hartman --- drivers/base/core.c | 6 +----- 1 file changed, 1 insertion(+), 5 deletions(-) (limited to 'drivers/base') diff --git a/drivers/base/core.c b/drivers/base/core.c index 4fc6f27641bf..f9297c68214a 100644 --- a/drivers/base/core.c +++ b/drivers/base/core.c @@ -3602,9 +3602,7 @@ int device_add(struct device *dev) if (error) goto SysEntryError; - error = devtmpfs_create_node(dev); - if (error) - goto DevtmpfsError; + devtmpfs_create_node(dev); } /* Notify clients of device addition. This call must come @@ -3660,8 +3658,6 @@ int device_add(struct device *dev) done: put_device(dev); return error; - DevtmpfsError: - device_remove_sys_dev_entry(dev); SysEntryError: if (MAJOR(dev->devt)) device_remove_file(dev, &dev_attr_dev); -- cgit v1.2.3 From 779aeb73d925586945b5dacab7453824766f149d Mon Sep 17 00:00:00 2001 From: Greg Kroah-Hartman Date: Tue, 14 Feb 2023 15:41:17 +0100 Subject: driver core: class: move EXPORT_SYMBOL_GPL() lines to the correct place For some reason, the drivers/base/class.c file still had the "old style" of exports, at the end of the file. Move the exports to the proper location, right after the function, to be correct. Reviewed-by: Rafael J. Wysocki Link: https://lore.kernel.org/r/20230214144117.158956-1-gregkh@linuxfoundation.org Signed-off-by: Greg Kroah-Hartman --- drivers/base/class.c | 14 ++++++-------- 1 file changed, 6 insertions(+), 8 deletions(-) (limited to 'drivers/base') diff --git a/drivers/base/class.c b/drivers/base/class.c index 89d4528fcc1a..2373b3e210d8 100644 --- a/drivers/base/class.c +++ b/drivers/base/class.c @@ -99,6 +99,7 @@ int class_create_file_ns(struct class *cls, const struct class_attribute *attr, error = -EINVAL; return error; } +EXPORT_SYMBOL_GPL(class_create_file_ns); void class_remove_file_ns(struct class *cls, const struct class_attribute *attr, const void *ns) @@ -106,6 +107,7 @@ void class_remove_file_ns(struct class *cls, const struct class_attribute *attr, if (cls) sysfs_remove_file_ns(&cls->p->subsys.kobj, &attr->attr, ns); } +EXPORT_SYMBOL_GPL(class_remove_file_ns); static struct class *class_get(struct class *cls) { @@ -213,6 +215,7 @@ void class_unregister(struct class *cls) class_remove_groups(cls, cls->class_groups); kset_unregister(&cls->p->subsys); } +EXPORT_SYMBOL_GPL(class_unregister); static void class_create_release(struct class *cls) { @@ -276,6 +279,7 @@ void class_destroy(struct class *cls) class_unregister(cls); } +EXPORT_SYMBOL_GPL(class_destroy); /** * class_dev_iter_init - initialize class device iterator @@ -460,6 +464,7 @@ int class_interface_register(struct class_interface *class_intf) return 0; } +EXPORT_SYMBOL_GPL(class_interface_register); void class_interface_unregister(struct class_interface *class_intf) { @@ -482,6 +487,7 @@ void class_interface_unregister(struct class_interface *class_intf) class_put(parent); } +EXPORT_SYMBOL_GPL(class_interface_unregister); ssize_t show_class_attr_string(struct class *class, struct class_attribute *attr, char *buf) @@ -588,11 +594,3 @@ int __init classes_init(void) return -ENOMEM; return 0; } - -EXPORT_SYMBOL_GPL(class_create_file_ns); -EXPORT_SYMBOL_GPL(class_remove_file_ns); -EXPORT_SYMBOL_GPL(class_unregister); -EXPORT_SYMBOL_GPL(class_destroy); - -EXPORT_SYMBOL_GPL(class_interface_register); -EXPORT_SYMBOL_GPL(class_interface_unregister); -- cgit v1.2.3