From 36bca207462d491e5d61cefeb041acf0fce506a7 Mon Sep 17 00:00:00 2001 From: "Rafael J. Wysocki" Date: Mon, 31 Aug 2026 22:04:32 +0200 Subject: [PATCH 01/14] ACPI: scan: Drop useless and noisy debug statement The debug message printing statement in handle_to_device() is super-noisy because it triggers whenever the presence of an attached struct acpi_device is checked for the given object in the ACPI namespace and it turns out that there is none. That is a perfectly valid situation and the lack of an attached struct acpi_device is not particularly interesting, so drop that message. Signed-off-by: Rafael J. Wysocki Link: https://patch.msgid.link/2861358.mvXUDI8C0e@rafael.j.wysocki --- drivers/acpi/scan.c | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/drivers/acpi/scan.c b/drivers/acpi/scan.c index 163a3cccf197..ec3a802e9238 100644 --- a/drivers/acpi/scan.c +++ b/drivers/acpi/scan.c @@ -637,10 +637,9 @@ static struct acpi_device *handle_to_device(acpi_handle handle, status = acpi_get_data_full(handle, acpi_scan_drop_device, (void **)&adev, callback); - if (ACPI_FAILURE(status) || !adev) { - acpi_handle_debug(handle, "No context!\n"); + if (ACPI_FAILURE(status) || !adev) return NULL; - } + return adev; } From 5636eebb7bb1c6f06f0058d48353686304d4bb47 Mon Sep 17 00:00:00 2001 From: "Rafael J. Wysocki" Date: Thu, 3 Sep 2026 18:58:34 +0200 Subject: [PATCH 02/14] ACPI: PM: Drop parent state update from acpi_device_get_power() The parent state update in acpi_device_get_power(), that occurs when the child device turns out to be in D0 and the parent's power state is still unknown, is highly questionable because it may cause the reference counters of the power resources used by the parent in D0 (if any) to underflow when the parent goes into a low-power state later. Moreover, there is no reason to do it on reads from the real_power_state sysfs attribute of the child. That check had been added by commit 8f7412a792bc ("ACPI / PM: Infer parent power state from child if unknown, v2") before starting to handle the "missing _PSC and no power resources" case directly in acpi_bus_init_power(), as of commit b3785492268f ("ACPI / PM: Do not power manage devices in unknown initial states"). It is not necessary any more and commit b3785492268f should have removed it. Drop it now. Fixes: b3785492268f ("ACPI / PM: Do not power manage devices in unknown initial states") Signed-off-by: Rafael J. Wysocki Tested-by: Peixin Xie Tested-by: Sakari Ailus Link: https://patch.msgid.link/5147279.31r3eYUQgx@rafael.j.wysocki Signed-off-by: Rafael J. Wysocki --- drivers/acpi/device_pm.c | 15 ++------------- 1 file changed, 2 insertions(+), 13 deletions(-) diff --git a/drivers/acpi/device_pm.c b/drivers/acpi/device_pm.c index aa55ecfc2923..4269735aadde 100644 --- a/drivers/acpi/device_pm.c +++ b/drivers/acpi/device_pm.c @@ -75,15 +75,14 @@ static int acpi_dev_pm_explicit_get(struct acpi_device *device, int *state) int acpi_device_get_power(struct acpi_device *device, int *state) { int result = ACPI_STATE_UNKNOWN; - struct acpi_device *parent; int error; if (!device || !state) return -EINVAL; - parent = acpi_dev_parent(device); - if (!device->flags.power_manageable) { + struct acpi_device *parent = acpi_dev_parent(device); + /* TBD: Non-recursive algorithm for walking up hierarchy. */ *state = parent ? parent->power.state : ACPI_STATE_D0; goto out; @@ -119,16 +118,6 @@ int acpi_device_get_power(struct acpi_device *device, int *state) result = psc > ACPI_STATE_D2 ? ACPI_STATE_D3_HOT : psc; } - /* - * If we were unsure about the device parent's power state up to this - * point, the fact that the device is in D0 implies that the parent has - * to be in D0 too, except if ignore_parent is set. - */ - if (!device->power.flags.ignore_parent && parent && - parent->power.state == ACPI_STATE_UNKNOWN && - result == ACPI_STATE_D0) - parent->power.state = ACPI_STATE_D0; - *state = result; out: From 2f4ddf2bd1baf0ee3c8542ec6f382a3585cae607 Mon Sep 17 00:00:00 2001 From: "Rafael J. Wysocki" Date: Thu, 3 Sep 2026 19:05:37 +0200 Subject: [PATCH 03/14] ACPI: scan: Stop calling acpi_bus_init_power() early There is a problem, introduced by commit 9d9bcae47fd5 ("ACPI: delay enumeration of devices with a _DEP pointing to an INT3472 device") inadvertently, that devices with missing dependencies may be put into power state D0 prematurely [1]. Namely, acpi_bus_init_power() called by acpi_bus_get_power_flags() during the early initialization of ACPI device objects, may discover that all of the power resources needed by the given device to be in power state D0 are initially on, so it will reference count those power resources and transition the device into D0. Later, if acpi_bus_attach() running for that device notices that it has missing dependencies, the enumeration of it will be deferred and its power_manageable flag will be cleared, even though it is still in D0 at that point. After the dependencies in question have been met, acpi_bus_attach() runs again for the device and now it calls acpi_bus_init_power() that takes additional references to the power resources used by the device in D0. These additional references prevent the power resources from being turned off when the device goes into D3hot/D3cold. Another problem, related to the previous one, is that ACPI power state initialization may be carried out for devices whose parents are not ready for enumeration which may lead to initialization ordering issues. To address both, stop calling acpi_bus_init_power() from acpi_bus_get_power_flags(), but also take the initialization of PCI devices into account, which needs to be done because they are initialized and bound to their ACPI companions before acpi_bus_attach() is called for the latter. To that end, notice that acpi_power_up_if_adr_present() is used for powering-up PCI devices in D3cold before walking the bus in order to discover them and the initial ACPI power state of those devices needs to be known for this purpose, so add an acpi_bus_init_power() invocation to that function. [The debug statement printed by it duplicates the debug statements printed during the acpi_bus_init_power() execution, so drop it.] Moreover, since the ACPI companions of PCI devices are associated with the corresponding PCI devices found on the bus before acpi_bus_attach() is called for them, it is not necessary or even useful to skip them in acpi_bus_attach() due to an ACPI status mismatch, so avoid doing that and complain if the ACPI status does not match the observed situation. Also use the ACPI power state tracking to decide whether or not the device's power state needs to be initialized in acpi_bus_attach() instead of using the "initialized" flag of the ACPI device object for this purpose, which is fragile and inconvenient, and clear the power_manageable flag on failure in acpi_bus_init_power() (additionally, poison the device ACPI power state as "invalid" if the initialization of it fails). That allows the clearing of the power_manageable flag for devices with unmet dependencies to be dropped. While at it, add a debug message pringing statement to acpi_bus_init_power() to facilitate diagnostics. Fixes: 9d9bcae47fd5 ("ACPI: delay enumeration of devices with a _DEP pointing to an INT3472 device") Link: https://lore.kernel.org/linux-acpi/20260820-acpi-power-resource-ref-fix-v2-1-29818173ea13@linux.spacemit.com/ [1] Signed-off-by: Rafael J. Wysocki Tested-by: Peixin Xie Tested-by: Sakari Ailus Link: https://patch.msgid.link/2295263.irdbgypaU6@rafael.j.wysocki Signed-off-by: Rafael J. Wysocki --- drivers/acpi/device_pm.c | 62 ++++++++++++++++++++++++++++++++-------- drivers/acpi/scan.c | 40 +++++++++++++++++--------- 2 files changed, 76 insertions(+), 26 deletions(-) diff --git a/drivers/acpi/device_pm.c b/drivers/acpi/device_pm.c index 4269735aadde..76104f715efa 100644 --- a/drivers/acpi/device_pm.c +++ b/drivers/acpi/device_pm.c @@ -23,6 +23,8 @@ #include "fan.h" #include "internal.h" +#define ACPI_D_STATE_INVALID ACPI_D_STATE_COUNT + /** * acpi_power_state_string - String representation of ACPI device power state. * @state: ACPI device power state to return the string representation of. @@ -293,24 +295,28 @@ int acpi_bus_set_power(acpi_handle handle, int state) } EXPORT_SYMBOL(acpi_bus_set_power); -int acpi_bus_init_power(struct acpi_device *device) +static int acpi_device_init_power(struct acpi_device *device) { int state; int result; - if (!device) - return -EINVAL; - - device->power.state = ACPI_STATE_UNKNOWN; - if (!acpi_device_is_present(device)) { - device->flags.initialized = false; - return -ENXIO; - } - result = acpi_device_get_power(device, &state); if (result) return result; + /* + * If the current power state of the device is D0 and it has a parent + * whose power state is not ignored, and the parent's power state + * initialization has failed, the parent's power state can be updated to + * D0 for consistency. + */ + if (!device->power.flags.ignore_parent && state == ACPI_STATE_D0) { + struct acpi_device *parent = acpi_dev_parent(device); + + if (parent && parent->power.state == ACPI_D_STATE_INVALID) + parent->power.state = ACPI_STATE_D0; + } + if (state < ACPI_STATE_D3_COLD && device->power.flags.power_resources) { /* Reference count the power resources. */ result = acpi_power_on_resources(device, state); @@ -340,9 +346,36 @@ int acpi_bus_init_power(struct acpi_device *device) state = ACPI_STATE_D0; } device->power.state = state; + + acpi_handle_debug(device->handle, "Initial power state: %s\n", + acpi_power_state_string(state)); + return 0; } +int acpi_bus_init_power(struct acpi_device *device) +{ + int result; + + if (device->power.state != ACPI_STATE_UNKNOWN) + return 0; + + /* + * The ACPI device power state can be only initialized once. If this + * fails, ACPI power management will not be used for the device going + * forward. + */ + result = acpi_device_init_power(device); + if (result) { + device->flags.power_manageable = 0; + device->power.state = ACPI_D_STATE_INVALID; + acpi_handle_info(device->handle, + "Initial power state undetermined, ACPI PM disabled\n"); + } + + return result; +} + /** * acpi_device_fix_up_power - Force device with missing _PSC into D0. * @device: Device object whose power state is to be fixed up. @@ -464,8 +497,13 @@ static int acpi_power_up_if_adr_present(struct acpi_device *adev, void *not_used if (!(adev->flags.power_manageable && adev->pnp.type.bus_address)) return 0; - acpi_handle_debug(adev->handle, "Power state: %s\n", - acpi_power_state_string(adev->power.state)); + /* + * This is done during the PCI root initialization which occurs before + * acpi_bus_attach() is called for the device, so the ACPI power state + * of the device needs to be initialized here. + */ + if (acpi_bus_init_power(adev)) + return 0; if (adev->power.state == ACPI_STATE_D3_COLD) return acpi_device_set_power(adev, ACPI_STATE_D0); diff --git a/drivers/acpi/scan.c b/drivers/acpi/scan.c index ec3a802e9238..13d1e77c9973 100644 --- a/drivers/acpi/scan.c +++ b/drivers/acpi/scan.c @@ -20,6 +20,7 @@ #include #include #include +#include #include #include #include @@ -1142,8 +1143,7 @@ static void acpi_bus_get_power_flags(struct acpi_device *device) device->power.states[ACPI_STATE_D3_COLD].flags.valid = 1; } - if (acpi_bus_init_power(device)) - device->flags.power_manageable = 0; + device->power.state = ACPI_STATE_UNKNOWN; } static void acpi_bus_get_flags(struct acpi_device *device) @@ -2337,38 +2337,50 @@ static int acpi_scan_attach_handler(struct acpi_device *device) static int acpi_bus_attach(struct acpi_device *device, void *first_pass) { bool skip = !first_pass && device->flags.visited; + struct pci_dev *pci; acpi_handle ejd; int ret; if (skip) goto ok; + device->flags.initialized = true; + if (ACPI_SUCCESS(acpi_bus_get_ejd(device->handle, &ejd))) register_dock_dependent_device(device, ejd); acpi_bus_get_status(device); - /* Skip devices that are not ready for enumeration (e.g. not present) */ - if (!acpi_dev_ready_for_enumeration(device)) { - device->flags.initialized = false; + /* + * If the given ACPI device object has been already associated with a + * PCI device found on the bus, its status is effectively "present and + * enabled", and dependencies are not tracked for PCI devices, so it is + * not necessary or even useful to check the device's readiness in that + * case. + */ + pci = acpi_dev_get_pci_dev(device); + if (pci) { + acpi_handle_debug(device->handle, "PCI companion %s found\n", + pci_name(pci)); + + if (!acpi_device_is_present(device)) + pci_info(pci, FW_BUG "ACPI status differs from reality\n"); + + pci_dev_put(pci); + } else if (!acpi_dev_ready_for_enumeration(device)) { + /* The device is not ready (e.g. not present), so skip it. */ acpi_device_clear_enumerated(device); - device->flags.power_manageable = 0; return 0; } + if (device->handler) goto ok; acpi_ec_register_opregions(device); - if (!device->flags.initialized) { - device->flags.power_manageable = - device->power.states[ACPI_STATE_D0].flags.valid; - if (acpi_bus_init_power(device)) - device->flags.power_manageable = 0; + acpi_bus_init_power(device); - device->flags.initialized = true; - } else if (device->flags.visited) { + if (device->flags.visited) goto ok; - } ret = acpi_scan_attach_handler(device); if (ret < 0) From 811268084ef2f9ead128c3d31b5b646510b7c180 Mon Sep 17 00:00:00 2001 From: "Rafael J. Wysocki" Date: Thu, 3 Sep 2026 19:06:32 +0200 Subject: [PATCH 04/14] ACPI: PM: Move acpi_bus_init_power() declaration to internal header file Since acpi_bus_init_power() is only used internally in the core ACPI device enumeration and power management code, it need not be visible outside, so move its declaration to an internal header file. No functional impact. Signed-off-by: Rafael J. Wysocki Tested-by: Peixin Xie Tested-by: Sakari Ailus Link: https://patch.msgid.link/3079955.e9J7NaK4W3@rafael.j.wysocki Signed-off-by: Rafael J. Wysocki --- drivers/acpi/internal.h | 1 + include/acpi/acpi_bus.h | 1 - 2 files changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/acpi/internal.h b/drivers/acpi/internal.h index 40f875b265a9..011da4ed6a80 100644 --- a/drivers/acpi/internal.h +++ b/drivers/acpi/internal.h @@ -157,6 +157,7 @@ void acpi_turn_off_unused_power_resources(void); Device Power Management -------------------------------------------------------------------------- */ int acpi_device_get_power(struct acpi_device *device, int *state); +int acpi_bus_init_power(struct acpi_device *device); int acpi_wakeup_device_init(void); /* -------------------------------------------------------------------------- diff --git a/include/acpi/acpi_bus.h b/include/acpi/acpi_bus.h index a10a591c18b2..9da74b3d4774 100644 --- a/include/acpi/acpi_bus.h +++ b/include/acpi/acpi_bus.h @@ -613,7 +613,6 @@ int acpi_bus_get_status(struct acpi_device *device); int acpi_bus_set_power(acpi_handle handle, int state); const char *acpi_power_state_string(int state); int acpi_device_set_power(struct acpi_device *device, int state); -int acpi_bus_init_power(struct acpi_device *device); int acpi_device_fix_up_power(struct acpi_device *device); void acpi_device_fix_up_power_extended(struct acpi_device *adev); void acpi_device_fix_up_power_children(struct acpi_device *adev); From dc2447880b728993a40881bf4ad482dbb93c14f2 Mon Sep 17 00:00:00 2001 From: "Rafael J. Wysocki" Date: Thu, 3 Sep 2026 19:09:22 +0200 Subject: [PATCH 05/14] ACPI: scan: Combine two conditionals in acpi_bus_attach() There are two conditionals in acpi_bus_attach() that can be combined, which slightly reduces the overhead and makes the code a bit easier to follow, so do that. No intentional functional impact. Signed-off-by: Rafael J. Wysocki Reviewed-by: Andy Shevchenko Tested-by: Peixin Xie Tested-by: Sakari Ailus Link: https://patch.msgid.link/8746939.T7Z3S40VBb@rafael.j.wysocki Signed-off-by: Rafael J. Wysocki --- drivers/acpi/scan.c | 9 ++------- 1 file changed, 2 insertions(+), 7 deletions(-) diff --git a/drivers/acpi/scan.c b/drivers/acpi/scan.c index 13d1e77c9973..30bfef67cbaa 100644 --- a/drivers/acpi/scan.c +++ b/drivers/acpi/scan.c @@ -2386,13 +2386,8 @@ static int acpi_bus_attach(struct acpi_device *device, void *first_pass) if (ret < 0) return 0; - if (ret > 0 && !device->flags.enumeration_by_parent) { - acpi_device_set_enumerated(device); - goto ok; - } - - if (device->pnp.type.platform_id || device->pnp.type.backlight || - device->flags.enumeration_by_parent) + if (device->flags.enumeration_by_parent || + (!ret && (device->pnp.type.platform_id || device->pnp.type.backlight))) acpi_default_enumeration(device); else acpi_device_set_enumerated(device); From 5bc1e72f80da4c20ce8b57851be4fad67e855ebd Mon Sep 17 00:00:00 2001 From: "Rafael J. Wysocki" Date: Sun, 6 Sep 2026 17:51:23 +0200 Subject: [PATCH 06/14] ACPI: glue: Reduce debug noise from acpi_device_notify() When debug is enabled in the ACPI glue code that handles binding devices to ACPI companions, acpi_device_notify() produces a lot of message noise related to devices that have no ACPI companions. Reduce that noise by checking the most obvious case, ACPI device objects, directly and returning from acpi_device_notify() in that case without printing any debug messages. Also avoid printing a debug message when there is no matching ACPI bus type definition for the given device, which is the case for the vast majority of devices. Additionally, make the debug messages that get printed more informative. Signed-off-by: Rafael J. Wysocki Reviewed-by: Andy Shevchenko Link: https://patch.msgid.link/6325978.lOV4Wx5bFT@rafael.j.wysocki Signed-off-by: Rafael J. Wysocki --- drivers/acpi/glue.c | 20 ++++++++++---------- 1 file changed, 10 insertions(+), 10 deletions(-) diff --git a/drivers/acpi/glue.c b/drivers/acpi/glue.c index b1776809279d..a2a394c226b3 100644 --- a/drivers/acpi/glue.c +++ b/drivers/acpi/glue.c @@ -354,22 +354,27 @@ void acpi_device_notify(struct device *dev) struct acpi_device *adev; int ret; + /* ACPI devices have no ACPI companions. */ + if (dev->bus == &acpi_bus_type) + return; + ret = acpi_bind_one(dev, NULL); if (ret) { struct acpi_bus_type *type = acpi_get_bus_type(dev); if (!type) - goto err; + return; adev = type->find_companion(dev); if (!adev) { dev_dbg(dev, "ACPI companion not found\n"); - goto err; + return; } ret = acpi_bind_one(dev, adev); - if (ret) - goto err; - + if (ret) { + dev_dbg(dev, "Binding to ACPI companion failed\n"); + return; + } if (type->setup) { type->setup(dev); goto done; @@ -391,11 +396,6 @@ void acpi_device_notify(struct device *dev) done: acpi_handle_debug(ACPI_HANDLE(dev), "Bound to device %s\n", dev_name(dev)); - - return; - -err: - dev_dbg(dev, "No ACPI support\n"); } void acpi_device_notify_remove(struct device *dev) From aef7792cddcac7ab400912c2947a595401d4a7e8 Mon Sep 17 00:00:00 2001 From: "Rafael J. Wysocki" Date: Sun, 6 Sep 2026 17:54:10 +0200 Subject: [PATCH 07/14] ACPI: glue: Rework the success message in acpi_device_notify() Using an ACPI handle in the acpi_device_notify() success message is somewhat misleading because the "physical" device is not bound to an ACPI namespace object in that function, but to an ACPI device object attached to it. Moreover, the message is outright confusing for the "physical" devices bound to ACPI devices without ACPI namespace objects (for example, fixed event buttons). Address this by turning that message into a dev_dbg() one printed for the "physical" device and mentioning the name of the ACPI companion. Fixes: ab06eb920401 ("ACPI: scan: Register platform devices for fixed event buttons") Signed-off-by: Rafael J. Wysocki Reviewed-by: Andy Shevchenko Link: https://patch.msgid.link/2448150.ElGaqSPkdT@rafael.j.wysocki Signed-off-by: Rafael J. Wysocki --- drivers/acpi/glue.c | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/drivers/acpi/glue.c b/drivers/acpi/glue.c index a2a394c226b3..a47cccc4efd3 100644 --- a/drivers/acpi/glue.c +++ b/drivers/acpi/glue.c @@ -394,8 +394,7 @@ void acpi_device_notify(struct device *dev) adev->handler->bind(dev); done: - acpi_handle_debug(ACPI_HANDLE(dev), "Bound to device %s\n", - dev_name(dev)); + dev_dbg(dev, "Bound to ACPI device %s\n", acpi_dev_name(adev)); } void acpi_device_notify_remove(struct device *dev) From f8113f311cf8be566d809598e967bf5ceb35b567 Mon Sep 17 00:00:00 2001 From: Andy Shevchenko Date: Wed, 9 Sep 2026 18:39:18 +0200 Subject: [PATCH 08/14] ACPI: bus: Reduce runtime memory footprint of struct acpi_device The ACPI handle is heavily used by the ACPI code. Making it as the first member in the struct acpi_device reduces code size. For instance, the drivers/acpi/scan.o (with `make allyesconfig`) gives this statistics: add/remove: 0/0 grow/shrink: 3/8 up/down: 50/-169 (-119) Function old new delta acpi_device_add 2294 2319 +25 acpi_add_single_object 6523 6544 +21 acpi_dev_get_next_consumer_dev 602 606 +4 acpi_tie_acpi_dev 169 165 -4 acpi_scan_drop_device 319 315 -4 acpi_dev_clear_dependencies 1151 1147 -4 acpi_bus_attach 2898 2894 -4 acpi_dma_get_range 1420 1415 -5 acpi_init_device_object 5497 5485 -12 acpi_bus_check_add 1940 1923 -17 acpi_device_hotplug 3407 3288 -119 Total: Before=77509, After=77390, chg -0.15% Signed-off-by: Andy Shevchenko [ rjw: Changelog tweaks ] Link: https://patch.msgid.link/20260909163918.368017-1-andriy.shevchenko@linux.intel.com Signed-off-by: Rafael J. Wysocki --- include/acpi/acpi_bus.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/include/acpi/acpi_bus.h b/include/acpi/acpi_bus.h index a10a591c18b2..64ae66ca456e 100644 --- a/include/acpi/acpi_bus.h +++ b/include/acpi/acpi_bus.h @@ -435,9 +435,9 @@ struct acpi_device_software_nodes { /* Device */ struct acpi_device { + acpi_handle handle; /* no handle for fixed hardware */ u32 pld_crc; int device_type; - acpi_handle handle; /* no handle for fixed hardware */ struct fwnode_handle fwnode; struct list_head wakeup_list; struct list_head del_list; From c94619c8a5b5d3b980433d247ac87a2d1c827cb6 Mon Sep 17 00:00:00 2001 From: "Rafael J. Wysocki" Date: Thu, 10 Sep 2026 19:56:04 +0200 Subject: [PATCH 09/14] ACPI: glue: Carry out companion lookup under bus_type_sem When acpi_device_notify() looks up an ACPI companion for the given device, it invokes callbacks from struct acpi_bus_type() without synchronization which may lead to a use-after-free if the driver module containing those callbacks is unloaded at the same time. Address this by holding bus_type_sem throughout the entire ACPI companion lookup and the execution of the .setup() callback in struct acpi_bus_type (if present) instead of dropping the semaphore (prematurely) after finding a matching struct acpi_bus_type. For this purpose, rename acpi_get_bus_type() to acpi_companion_lookup(), make it return a struct acpi_device pointer, and move the relevant code from acpi_device_notify() to it. Also notice that the only case in which the .bind() callback from an ACPI scan handler may need to be invoked is when the given device is a platform one, so adjust acpi_device_notify() accordingly and drop the "done" label that is not used any more from it. Fixes: 2ef5236660b6 ("ACPI: glue: Look for ACPI bus type only if ACPI companion is not known") Signed-off-by: Rafael J. Wysocki Link: https://patch.msgid.link/3089807.e9J7NaK4W3@rafael.j.wysocki --- drivers/acpi/glue.c | 61 +++++++++++++++++++++------------------------ 1 file changed, 29 insertions(+), 32 deletions(-) diff --git a/drivers/acpi/glue.c b/drivers/acpi/glue.c index a47cccc4efd3..40e6513a9942 100644 --- a/drivers/acpi/glue.c +++ b/drivers/acpi/glue.c @@ -59,19 +59,34 @@ int unregister_acpi_bus_type(struct acpi_bus_type *type) } EXPORT_SYMBOL_GPL(unregister_acpi_bus_type); -static struct acpi_bus_type *acpi_get_bus_type(struct device *dev) +static struct acpi_device *acpi_companion_lookup(struct device *dev) { - struct acpi_bus_type *tmp, *ret = NULL; + struct acpi_bus_type *type; - down_read(&bus_type_sem); - list_for_each_entry(tmp, &bus_type_list, list) { - if (tmp->match(dev)) { - ret = tmp; - break; + guard(rwsem_read)(&bus_type_sem); + + list_for_each_entry(type, &bus_type_list, list) { + struct acpi_device *adev; + + if (!type->match(dev)) + continue; + + adev = type->find_companion(dev); + if (!adev) { + dev_dbg(dev, "ACPI companion not found\n"); + return NULL; } + if (acpi_bind_one(dev, adev)) { + dev_dbg(dev, "Binding to ACPI companion failed\n"); + return NULL; + } + if (type->setup) + type->setup(dev); + + return adev; } - up_read(&bus_type_sem); - return ret; + + return NULL; } #define FIND_CHILD_MIN_SCORE 1 @@ -360,40 +375,22 @@ void acpi_device_notify(struct device *dev) ret = acpi_bind_one(dev, NULL); if (ret) { - struct acpi_bus_type *type = acpi_get_bus_type(dev); - - if (!type) + adev = acpi_companion_lookup(dev); + if (!adev) return; - - adev = type->find_companion(dev); - if (!adev) { - dev_dbg(dev, "ACPI companion not found\n"); - return; - } - ret = acpi_bind_one(dev, adev); - if (ret) { - dev_dbg(dev, "Binding to ACPI companion failed\n"); - return; - } - if (type->setup) { - type->setup(dev); - goto done; - } } else { adev = ACPI_COMPANION(dev); if (dev_is_pci(dev)) { pci_acpi_setup(dev, adev); - goto done; } else if (dev_is_platform(dev)) { acpi_configure_pmsi_domain(dev); + + if (adev->handler && adev->handler->bind) + adev->handler->bind(dev); } } - if (adev->handler && adev->handler->bind) - adev->handler->bind(dev); - -done: dev_dbg(dev, "Bound to ACPI device %s\n", acpi_dev_name(adev)); } From fcfb2b562115632e0a60a0928dca495f98eb335c Mon Sep 17 00:00:00 2001 From: "Rafael J. Wysocki" Date: Thu, 10 Sep 2026 19:56:10 +0200 Subject: [PATCH 10/14] ACPI: glue: Rearrange acpi_bind_one() to avoid breakage Rearange the code in acpi_bind_one() to avoid situations in which the existing ACPI companion of the given device would be replaced with NULL due to a memory allocation error or because somebody tries to bind a physical device with an ACPI companion to a different ACPI device erroneously. Fixes: 7b1998116bbb ("ACPI / driver core: Store an ACPI device pointer in struct acpi_dev_node") Signed-off-by: Rafael J. Wysocki [ rjw: Merged two conditionals to avoid redundant check ] Link: https://patch.msgid.link/3465482.44csPzL39Z@rafael.j.wysocki Signed-off-by: Rafael J. Wysocki --- drivers/acpi/glue.c | 58 +++++++++++++++++++-------------------------- 1 file changed, 24 insertions(+), 34 deletions(-) diff --git a/drivers/acpi/glue.c b/drivers/acpi/glue.c index 40e6513a9942..1e9e7150b95a 100644 --- a/drivers/acpi/glue.c +++ b/drivers/acpi/glue.c @@ -243,31 +243,25 @@ static void acpi_physnode_link_name(char *buf, unsigned int node_id) int acpi_bind_one(struct device *dev, struct acpi_device *acpi_dev) { struct acpi_device_physical_node *physical_node, *pn; + struct acpi_device *comp_dev = ACPI_COMPANION(dev); char physical_node_name[PHYSICAL_NODE_NAME_SIZE]; struct list_head *physnode_list; unsigned int node_id; int retval = -EINVAL; - if (has_acpi_companion(dev)) { - if (acpi_dev) { - dev_warn(dev, "ACPI companion already set\n"); + if (!acpi_dev) { + if (!comp_dev) return -EINVAL; - } else { - acpi_dev = ACPI_COMPANION(dev); - } - } - if (!acpi_dev) - return -EINVAL; - acpi_dev_get(acpi_dev); - get_device(dev); - physical_node = kzalloc_obj(*physical_node); - if (!physical_node) { - retval = -ENOMEM; - goto err; + /* If the companion has been set upfront, pick it up. */ + acpi_dev = comp_dev; + } else if (comp_dev && acpi_dev != comp_dev) { + dev_warn(dev, "ACPI companion already set to %s which is not %s\n", + acpi_dev_name(comp_dev), acpi_dev_name(acpi_dev)); + return -EEXIST; } - mutex_lock(&acpi_dev->physical_node_lock); + guard(mutex)(&acpi_dev->physical_node_lock); /* * Keep the list sorted by node_id so that the IDs of removed nodes can @@ -278,15 +272,12 @@ int acpi_bind_one(struct device *dev, struct acpi_device *acpi_dev) list_for_each_entry(pn, &acpi_dev->physical_node_list, node) { /* Sanity check. */ if (pn->dev == dev) { - mutex_unlock(&acpi_dev->physical_node_lock); - - dev_warn(dev, "Already associated with ACPI node\n"); - kfree(physical_node); - if (ACPI_COMPANION(dev) != acpi_dev) - goto err; - - put_device(dev); - acpi_dev_put(acpi_dev); + if (!comp_dev) { + /* Really unexpected. */ + ACPI_COMPANION_SET(dev, acpi_dev); + dev_warn(&acpi_dev->dev, + "Physical device list corruption fixed up\n"); + } return 0; } if (pn->node_id == node_id) { @@ -295,12 +286,19 @@ int acpi_bind_one(struct device *dev, struct acpi_device *acpi_dev) } } + physical_node = kzalloc_obj(*physical_node); + if (!physical_node) + return -ENOMEM; + + acpi_dev_get(acpi_dev); + get_device(dev); + physical_node->node_id = node_id; physical_node->dev = dev; list_add(&physical_node->node, physnode_list); acpi_dev->physical_node_count++; - if (!has_acpi_companion(dev)) + if (!comp_dev) ACPI_COMPANION_SET(dev, acpi_dev); acpi_physnode_link_name(physical_node_name, node_id); @@ -316,18 +314,10 @@ int acpi_bind_one(struct device *dev, struct acpi_device *acpi_dev) dev_err(dev, "Failed to create link firmware_node (%d)\n", retval); - mutex_unlock(&acpi_dev->physical_node_lock); - if (acpi_dev->wakeup.flags.valid) device_set_wakeup_capable(dev, true); return 0; - - err: - ACPI_COMPANION_SET(dev, NULL); - put_device(dev); - acpi_dev_put(acpi_dev); - return retval; } EXPORT_SYMBOL_GPL(acpi_bind_one); From 693546852656075e0247ac637b05625ead134eff Mon Sep 17 00:00:00 2001 From: "Rafael J. Wysocki" Date: Thu, 10 Sep 2026 19:56:16 +0200 Subject: [PATCH 11/14] ACPI: glue: Fix up and adjust acpi_unbind_one() Since none of the acpi_unbind_one() callers check its return value and it always returns 0 anyway, make it void. Also notice that unlocking physical_node_lock for the given ACPI device should be carried out before dropping the reference to it in case that reference is the last one (highly unlikely), so rearrange the code to make that happen. Fixes: 3e3327837c18 ("ACPI: Use list_for_each_entry() in acpi_unbind_one()") Signed-off-by: Rafael J. Wysocki Link: https://patch.msgid.link/10940500.nUPlyArG6x@rafael.j.wysocki --- drivers/acpi/glue.c | 10 ++++++---- include/acpi/acpi_bus.h | 2 +- 2 files changed, 7 insertions(+), 5 deletions(-) diff --git a/drivers/acpi/glue.c b/drivers/acpi/glue.c index 1e9e7150b95a..e55daa4221da 100644 --- a/drivers/acpi/glue.c +++ b/drivers/acpi/glue.c @@ -321,13 +321,13 @@ int acpi_bind_one(struct device *dev, struct acpi_device *acpi_dev) } EXPORT_SYMBOL_GPL(acpi_bind_one); -int acpi_unbind_one(struct device *dev) +void acpi_unbind_one(struct device *dev) { struct acpi_device *acpi_dev = ACPI_COMPANION(dev); struct acpi_device_physical_node *entry; if (!acpi_dev) - return 0; + return; mutex_lock(&acpi_dev->physical_node_lock); @@ -342,15 +342,17 @@ int acpi_unbind_one(struct device *dev) sysfs_remove_link(&acpi_dev->dev.kobj, physnode_name); sysfs_remove_link(&dev->kobj, "firmware_node"); ACPI_COMPANION_SET(dev, NULL); + + mutex_unlock(&acpi_dev->physical_node_lock); + /* Drop references taken by acpi_bind_one(). */ put_device(dev); acpi_dev_put(acpi_dev); kfree(entry); - break; + return; } mutex_unlock(&acpi_dev->physical_node_lock); - return 0; } EXPORT_SYMBOL_GPL(acpi_unbind_one); diff --git a/include/acpi/acpi_bus.h b/include/acpi/acpi_bus.h index a10a591c18b2..93b00635ba3d 100644 --- a/include/acpi/acpi_bus.h +++ b/include/acpi/acpi_bus.h @@ -665,7 +665,7 @@ struct acpi_bus_type { int register_acpi_bus_type(struct acpi_bus_type *); int unregister_acpi_bus_type(struct acpi_bus_type *); int acpi_bind_one(struct device *dev, struct acpi_device *adev); -int acpi_unbind_one(struct device *dev); +void acpi_unbind_one(struct device *dev); enum acpi_bridge_type { ACPI_BRIDGE_TYPE_PCIE = 1, From ea9102cae4a634a752749dda4914cb235910134e Mon Sep 17 00:00:00 2001 From: "Rafael J. Wysocki" Date: Thu, 10 Sep 2026 19:56:22 +0200 Subject: [PATCH 12/14] ACPI: glue: Skip devices with no type in acpi_device_notify() Since all of the users of struct acpi_bus_type use the device type for matching, devices without a type will not be matched by any of them, so they can be skipped early in acpi_device_notify(). Update the code accordingly. Signed-off-by: Rafael J. Wysocki Link: https://patch.msgid.link/2078562.PYKUYFuaPT@rafael.j.wysocki --- drivers/acpi/glue.c | 3 +++ 1 file changed, 3 insertions(+) diff --git a/drivers/acpi/glue.c b/drivers/acpi/glue.c index e55daa4221da..0959f58cb77c 100644 --- a/drivers/acpi/glue.c +++ b/drivers/acpi/glue.c @@ -63,6 +63,9 @@ static struct acpi_device *acpi_companion_lookup(struct device *dev) { struct acpi_bus_type *type; + if (!dev->type) + return NULL; + guard(rwsem_read)(&bus_type_sem); list_for_each_entry(type, &bus_type_list, list) { From 68a385dbe05fd8da69ec8a7753591fd653f97d89 Mon Sep 17 00:00:00 2001 From: Ruslan Vagner Date: Tue, 15 Sep 2026 05:56:13 -0700 Subject: [PATCH 13/14] ACPI: sysfs: use strscpy() instead of strcpy() strcpy() is deprecated in the kernel, so use strscpy() with the compile-time known size of the trace_method_name buffer instead of it. The existing length check already rejects overlong input, so the behavior is unchanged. Signed-off-by: Ruslan Vagner [ rjw: Changelog edits ] Link: https://patch.msgid.link/CAF9RqJ4ip_egVwRKnwn3=X-Qxuwz0u_9pNrgfujBw8RSf2DTPQ@mail.gmail.com Signed-off-by: Rafael J. Wysocki --- drivers/acpi/sysfs.c | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/drivers/acpi/sysfs.c b/drivers/acpi/sysfs.c index dd4a99f09efe..a99373547126 100644 --- a/drivers/acpi/sysfs.c +++ b/drivers/acpi/sysfs.c @@ -11,6 +11,7 @@ #include #include #include +#include #include "internal.h" @@ -181,10 +182,10 @@ static int param_set_trace_method_name(const char *val, /* This is a hack. We can't kmalloc in early boot. */ if (is_abs_path) - strcpy(trace_method_name, val); + strscpy(trace_method_name, val, sizeof(trace_method_name)); else { trace_method_name[0] = '\\'; - strcpy(trace_method_name+1, val); + strscpy(trace_method_name + 1, val, sizeof(trace_method_name) - 1); } /* Restore the original tracer state */ From 57c59c0bda8b19d8cbb877f61dbc8372e5f97749 Mon Sep 17 00:00:00 2001 From: "Rafael J. Wysocki" Date: Fri, 25 Sep 2026 11:23:35 +0200 Subject: [PATCH 14/14] driver core/ACPI: Introduce companion_bus_register() The ACPI bus type does not allow drivers to be matched to devices, so the sysfs attributes related to drivers created for it are useless and their existence is confusing. Moreover, it is better to prevent drivers from being registered and looked up for a bus like that. To allow skipping the creation of those sysfs attributes and preventing driver registration and lookup for the ACPI bus type, introduce a "companion" bus type concept and add a special registration function for registering "companion" bus types, companion_bus_register(). The "drivers" directory under the ACPI bus type is still needed because there are versions of systemd that depend on it [1]. Link: https://lore.kernel.org/linux-acpi/SN6PR02MB41575266A4580339E186E5D9D4812@SN6PR02MB4157.namprd02.prod.outlook.com/ [1] Signed-off-by: Rafael J. Wysocki Acked-by: Greg Kroah-Hartman Reviewed-by: Danilo Krummrich Tested-by: Michael Kelley Reviewed-by: Michael Kelley [ rjw: Add comment regarding drivers_kset creation in bus_register_internal() ] Link: https://patch.msgid.link/12985239.O9o76ZdvQC@rafael.j.wysocki Signed-off-by: Rafael J. Wysocki --- drivers/acpi/bus.c | 8 +---- drivers/base/base.h | 2 ++ drivers/base/bus.c | 67 ++++++++++++++++++++++++++++++-------- include/linux/device/bus.h | 1 + 4 files changed, 58 insertions(+), 20 deletions(-) diff --git a/drivers/acpi/bus.c b/drivers/acpi/bus.c index 808c6746be14..48f0b4809f94 100644 --- a/drivers/acpi/bus.c +++ b/drivers/acpi/bus.c @@ -1120,11 +1120,6 @@ EXPORT_SYMBOL_GPL(acpi_driver_match_device); ACPI Bus operations -------------------------------------------------------------------------- */ -static int acpi_bus_match(struct device *dev, const struct device_driver *drv) -{ - return 0; -} - static int acpi_device_uevent(const struct device *dev, struct kobj_uevent_env *env) { return __acpi_device_uevent_modalias(to_acpi_device(dev), env); @@ -1132,7 +1127,6 @@ static int acpi_device_uevent(const struct device *dev, struct kobj_uevent_env * const struct bus_type acpi_bus_type = { .name = "acpi", - .match = acpi_bus_match, .uevent = acpi_device_uevent, }; @@ -1451,7 +1445,7 @@ static int __init acpi_bus_init(void) */ acpi_root_dir = proc_mkdir(ACPI_BUS_FILE_ROOT, NULL); - result = bus_register(&acpi_bus_type); + result = companion_bus_register(&acpi_bus_type); if (!result) return 0; diff --git a/drivers/base/base.h b/drivers/base/base.h index a5b7abc10ff0..21c7d244f8aa 100644 --- a/drivers/base/base.h +++ b/drivers/base/base.h @@ -27,6 +27,7 @@ * @drivers_autoprobe: gate whether new devices are automatically attached to * registered drivers, or new drivers automatically attach * to existing devices. + * @no_drivers: gate whether drivers can be registered. * @bus: pointer back to the struct bus_type that this structure is associated * with. * @dev_root: Default device to use as the parent. @@ -51,6 +52,7 @@ struct subsys_private { struct klist klist_drivers; struct blocking_notifier_head bus_notifier; unsigned int drivers_autoprobe:1; + unsigned int no_drivers:1; const struct bus_type *bus; struct device *dev_root; diff --git a/drivers/base/bus.c b/drivers/base/bus.c index d17bd91490ee..d89333806a4e 100644 --- a/drivers/base/bus.c +++ b/drivers/base/bus.c @@ -738,6 +738,11 @@ int bus_add_driver(struct device_driver *drv) if (!sp) return -EINVAL; + if (sp->no_drivers) { + error = -ENXIO; + goto out_put_bus; + } + /* * Reference in sp is now incremented and will be dropped when * the driver is removed from the bus @@ -930,15 +935,7 @@ static ssize_t bus_uevent_store(const struct bus_type *bus, static struct bus_attribute bus_attr_uevent = __ATTR(uevent, 0200, NULL, bus_uevent_store); -/** - * bus_register - register a driver-core subsystem - * @bus: bus to register - * - * Once we have that, we register the bus with the kobject - * infrastructure, then register the children subsystems it has: - * the devices and drivers that belong to the subsystem. - */ -int bus_register(const struct bus_type *bus) +static int bus_register_internal(const struct bus_type *bus, bool use_drivers) { int retval; struct subsys_private *priv; @@ -960,7 +957,8 @@ int bus_register(const struct bus_type *bus) bus_kobj->kset = bus_kset; bus_kobj->ktype = &bus_ktype; - priv->drivers_autoprobe = 1; + priv->drivers_autoprobe = use_drivers; + priv->no_drivers = !use_drivers; retval = kset_register(&priv->subsys); if (retval) @@ -976,6 +974,10 @@ int bus_register(const struct bus_type *bus) goto bus_devices_fail; } + /* + * Create the drivers kset even if use_drivers is unset to prevent some + * older versions of systemd from failing. + */ priv->drivers_kset = kset_create_and_add("drivers", NULL, bus_kobj); if (!priv->drivers_kset) { retval = -ENOMEM; @@ -989,9 +991,11 @@ int bus_register(const struct bus_type *bus) klist_init(&priv->klist_devices, klist_devices_get, klist_devices_put); klist_init(&priv->klist_drivers, NULL, NULL); - retval = add_probe_files(bus); - if (retval) - goto bus_probe_files_fail; + if (use_drivers) { + retval = add_probe_files(bus); + if (retval) + goto bus_probe_files_fail; + } retval = sysfs_create_groups(bus_kobj, bus->bus_groups); if (retval) @@ -1016,8 +1020,40 @@ int bus_register(const struct bus_type *bus) kfree(priv); return retval; } + +/** + * bus_register - register a driver-core subsystem + * @bus: bus to register + * + * Once we have that, we register the bus with the kobject + * infrastructure, then register the children subsystems it has: + * the devices and drivers that belong to the subsystem. + */ +int bus_register(const struct bus_type *bus) +{ + return bus_register_internal(bus, true); +} EXPORT_SYMBOL_GPL(bus_register); +/** + * companion_bus_register - register a companion bus type + * @bus: companion bus to register + * + * A companion bus is a bus without drivers. Devices that belong to it can be + * bound to other devices as their "companions" and represent interfaces that + * can be used by the drivers of those other devices. They may also be used for + * the enumeration of those other devices. + * + * The ACPI bus is a specific example of a companion bus. + * + * Registering a companion bus is like registering a regular bus except that it + * skips the creation of sysfs interfaces related to drivers for @bus. + */ +int companion_bus_register(const struct bus_type *bus) +{ + return bus_register_internal(bus, false); +} + /** * bus_unregister - remove a bus from the system * @bus: bus. @@ -1415,6 +1451,11 @@ struct device_driver *driver_find(const char *name, const struct bus_type *bus) if (!sp) return NULL; + if (sp->no_drivers) { + subsys_put(sp); + return NULL; + } + k = kset_find_obj(sp->drivers_kset, name); subsys_put(sp); if (!k) diff --git a/include/linux/device/bus.h b/include/linux/device/bus.h index a38f7229b8f4..89e156b9acb5 100644 --- a/include/linux/device/bus.h +++ b/include/linux/device/bus.h @@ -113,6 +113,7 @@ struct bus_type { bool need_parent_lock; }; +int __must_check companion_bus_register(const struct bus_type *bus); int __must_check bus_register(const struct bus_type *bus); void bus_unregister(const struct bus_type *bus);