From a00f3dea0352a5fb0b67b84c72daeb6563f8e67f Mon Sep 17 00:00:00 2001 From: "Rafael J. Wysocki" Date: Thu, 9 Oct 2025 21:25:56 +0200 Subject: [PATCH 01/10] ACPI: PM: s2idle: Drop acpi_get_lps0_constraint() Drop unused function acpi_get_lps0_constraint(). No functional impact. Signed-off-by: Rafael J. Wysocki Reviewed-by: Mario Limonciello (AMD) Link: https://patch.msgid.link/5032801.GXAFRqVoOG@rafael.j.wysocki --- drivers/acpi/x86/s2idle.c | 24 ------------------------ include/linux/acpi.h | 5 ----- 2 files changed, 29 deletions(-) diff --git a/drivers/acpi/x86/s2idle.c b/drivers/acpi/x86/s2idle.c index dd0b40b9bbe8..ea645853fc20 100644 --- a/drivers/acpi/x86/s2idle.c +++ b/drivers/acpi/x86/s2idle.c @@ -299,30 +299,6 @@ static void lpi_device_get_constraints(void) ACPI_FREE(out_obj); } -/** - * acpi_get_lps0_constraint - Get the LPS0 constraint for a device. - * @adev: Device to get the constraint for. - * - * The LPS0 constraint is the shallowest (minimum) power state in which the - * device can be so as to allow the platform as a whole to achieve additional - * energy conservation by utilizing a system-wide low-power state. - * - * Returns: - * - ACPI power state value of the constraint for @adev on success. - * - Otherwise, ACPI_STATE_UNKNOWN. - */ -int acpi_get_lps0_constraint(struct acpi_device *adev) -{ - struct lpi_constraints *entry; - - for_each_lpi_constraint(entry) { - if (adev->handle == entry->handle) - return entry->min_dstate; - } - - return ACPI_STATE_UNKNOWN; -} - static void lpi_check_constraints(void) { struct lpi_constraints *entry; diff --git a/include/linux/acpi.h b/include/linux/acpi.h index 5ff5d99f6ead..252768d007c7 100644 --- a/include/linux/acpi.h +++ b/include/linux/acpi.h @@ -1146,12 +1146,7 @@ struct acpi_s2idle_dev_ops { #if defined(CONFIG_SUSPEND) && defined(CONFIG_X86) int acpi_register_lps0_dev(struct acpi_s2idle_dev_ops *arg); void acpi_unregister_lps0_dev(struct acpi_s2idle_dev_ops *arg); -int acpi_get_lps0_constraint(struct acpi_device *adev); #else /* CONFIG_SUSPEND && CONFIG_X86 */ -static inline int acpi_get_lps0_constraint(struct device *dev) -{ - return ACPI_STATE_UNKNOWN; -} static inline int acpi_register_lps0_dev(struct acpi_s2idle_dev_ops *arg) { return -ENODEV; From bfc09902debd036e672148343c0caeab20924c9d Mon Sep 17 00:00:00 2001 From: "Rafael J. Wysocki" Date: Thu, 9 Oct 2025 21:26:30 +0200 Subject: [PATCH 02/10] ACPI: PM: s2idle: Staticise LPS0 callback functions The LPS0 callback functions in x86/s2idle.c can be made static, so do that and remove their declarations from sleep.h. While at it, add the _lps0 suffix to their names to indicate that they are LPS0-specific. No functional impact. Signed-off-by: Rafael J. Wysocki Reviewed-by: Mario Limonciello (AMD) Link: https://patch.msgid.link/2254836.irdbgypaU6@rafael.j.wysocki --- drivers/acpi/sleep.h | 3 --- drivers/acpi/x86/s2idle.c | 12 ++++++------ 2 files changed, 6 insertions(+), 9 deletions(-) diff --git a/drivers/acpi/sleep.h b/drivers/acpi/sleep.h index d960a238be4e..9c3cb109c5d2 100644 --- a/drivers/acpi/sleep.h +++ b/drivers/acpi/sleep.h @@ -17,10 +17,7 @@ static inline acpi_status acpi_set_waking_vector(u32 wakeup_address) extern int acpi_s2idle_begin(void); extern int acpi_s2idle_prepare(void); -extern int acpi_s2idle_prepare_late(void); -extern void acpi_s2idle_check(void); extern bool acpi_s2idle_wake(void); -extern void acpi_s2idle_restore_early(void); extern void acpi_s2idle_restore(void); extern void acpi_s2idle_end(void); diff --git a/drivers/acpi/x86/s2idle.c b/drivers/acpi/x86/s2idle.c index ea645853fc20..5ce556b1cfbc 100644 --- a/drivers/acpi/x86/s2idle.c +++ b/drivers/acpi/x86/s2idle.c @@ -515,7 +515,7 @@ static struct acpi_scan_handler lps0_handler = { .attach = lps0_device_attach, }; -int acpi_s2idle_prepare_late(void) +static int acpi_s2idle_prepare_late_lps0(void) { struct acpi_s2idle_dev_ops *handler; @@ -561,7 +561,7 @@ int acpi_s2idle_prepare_late(void) return 0; } -void acpi_s2idle_check(void) +static void acpi_s2idle_check_lps0(void) { struct acpi_s2idle_dev_ops *handler; @@ -574,7 +574,7 @@ void acpi_s2idle_check(void) } } -void acpi_s2idle_restore_early(void) +static void acpi_s2idle_restore_early_lps0(void) { struct acpi_s2idle_dev_ops *handler; @@ -614,10 +614,10 @@ void acpi_s2idle_restore_early(void) static const struct platform_s2idle_ops acpi_s2idle_ops_lps0 = { .begin = acpi_s2idle_begin, .prepare = acpi_s2idle_prepare, - .prepare_late = acpi_s2idle_prepare_late, - .check = acpi_s2idle_check, + .prepare_late = acpi_s2idle_prepare_late_lps0, + .check = acpi_s2idle_check_lps0, .wake = acpi_s2idle_wake, - .restore_early = acpi_s2idle_restore_early, + .restore_early = acpi_s2idle_restore_early_lps0, .restore = acpi_s2idle_restore, .end = acpi_s2idle_end, }; From 32ece31db4df792c36bbabb8a1d263a5e1f5017a Mon Sep 17 00:00:00 2001 From: "Rafael J. Wysocki" Date: Thu, 9 Oct 2025 21:27:11 +0200 Subject: [PATCH 03/10] ACPI: PM: s2idle: Only retrieve constraints when needed The evaluation of LPS0 _DSM Function 1 in lps0_device_attach() may be useless if pm_debug_messages_on is never set. For this reason, instead of evaluating it in lps0_device_attach(), do that in a new .begin() callback for s2idle, acpi_s2idle_begin_lps0(), only when pm_debug_messages_on is set at that point. However, never attempt to evaluate LPS0 _DSM Function 1 more than once to avoid recurring failures. Signed-off-by: Rafael J. Wysocki Reviewed-by: Mario Limonciello (AMD) Link: https://patch.msgid.link/3027060.e9J7NaK4W3@rafael.j.wysocki --- drivers/acpi/x86/s2idle.c | 29 +++++++++++++++++++++++------ 1 file changed, 23 insertions(+), 6 deletions(-) diff --git a/drivers/acpi/x86/s2idle.c b/drivers/acpi/x86/s2idle.c index 5ce556b1cfbc..6d4d06236f61 100644 --- a/drivers/acpi/x86/s2idle.c +++ b/drivers/acpi/x86/s2idle.c @@ -303,6 +303,9 @@ static void lpi_check_constraints(void) { struct lpi_constraints *entry; + if (IS_ERR_OR_NULL(lpi_constraints_table)) + return; + for_each_lpi_constraint(entry) { struct acpi_device *adev = acpi_fetch_acpi_dev(entry->handle); @@ -484,11 +487,6 @@ static int lps0_device_attach(struct acpi_device *adev, lps0_device_handle = adev->handle; - if (acpi_s2idle_vendor_amd()) - lpi_device_get_constraints_amd(); - else - lpi_device_get_constraints(); - /* * Use suspend-to-idle by default if ACPI_FADT_LOW_POWER_S0 is set in * the FADT and the default suspend mode was not set from the command @@ -515,6 +513,25 @@ static struct acpi_scan_handler lps0_handler = { .attach = lps0_device_attach, }; +static int acpi_s2idle_begin_lps0(void) +{ + if (pm_debug_messages_on && !lpi_constraints_table) { + if (acpi_s2idle_vendor_amd()) + lpi_device_get_constraints_amd(); + else + lpi_device_get_constraints(); + + /* + * Try to retrieve the constraints only once because failures + * to do so usually are sticky. + */ + if (!lpi_constraints_table) + lpi_constraints_table = ERR_PTR(-ENODATA); + } + + return acpi_s2idle_begin(); +} + static int acpi_s2idle_prepare_late_lps0(void) { struct acpi_s2idle_dev_ops *handler; @@ -612,7 +629,7 @@ static void acpi_s2idle_restore_early_lps0(void) } static const struct platform_s2idle_ops acpi_s2idle_ops_lps0 = { - .begin = acpi_s2idle_begin, + .begin = acpi_s2idle_begin_lps0, .prepare = acpi_s2idle_prepare, .prepare_late = acpi_s2idle_prepare_late_lps0, .check = acpi_s2idle_check_lps0, From 159e85110891ebc12500d02d4bf214b1d203e305 Mon Sep 17 00:00:00 2001 From: Sakari Ailus Date: Wed, 1 Oct 2025 13:43:18 +0300 Subject: [PATCH 04/10] ACPI: property: Make acpi_get_next_subnode() static acpi_get_next_subnode() is only used in drivers/acpi/property.c. Remove its prototype from include/linux/acpi.h and make it static. Signed-off-by: Sakari Ailus Reviewed-by: Andy Shevchenko Reviewed-by: Laurent Pinchart Reviewed-by: Jonathan Cameron Link: https://patch.msgid.link/20251001104320.1272752-2-sakari.ailus@linux.intel.com Signed-off-by: Rafael J. Wysocki --- drivers/acpi/property.c | 7 ++++--- include/linux/acpi.h | 10 ---------- 2 files changed, 4 insertions(+), 13 deletions(-) diff --git a/drivers/acpi/property.c b/drivers/acpi/property.c index 43d5e457814e..dbf86bee62e1 100644 --- a/drivers/acpi/property.c +++ b/drivers/acpi/property.c @@ -1329,13 +1329,14 @@ static int stop_on_next(struct acpi_device *adev, void *data) return 0; } -/** +/* * acpi_get_next_subnode - Return the next child node handle for a fwnode * @fwnode: Firmware node to find the next child node for. * @child: Handle to one of the device's child nodes or a null handle. */ -struct fwnode_handle *acpi_get_next_subnode(const struct fwnode_handle *fwnode, - struct fwnode_handle *child) +static struct fwnode_handle * +acpi_get_next_subnode(const struct fwnode_handle *fwnode, + struct fwnode_handle *child) { struct acpi_device *adev = to_acpi_device_node(fwnode); diff --git a/include/linux/acpi.h b/include/linux/acpi.h index 5ff5d99f6ead..703323b9fe0c 100644 --- a/include/linux/acpi.h +++ b/include/linux/acpi.h @@ -1349,9 +1349,6 @@ acpi_data_add_props(struct acpi_device_data *data, const guid_t *guid, int acpi_node_prop_get(const struct fwnode_handle *fwnode, const char *propname, void **valptr); -struct fwnode_handle *acpi_get_next_subnode(const struct fwnode_handle *fwnode, - struct fwnode_handle *child); - struct acpi_probe_entry; typedef bool (*acpi_probe_entry_validate_subtbl)(struct acpi_subtable_header *, struct acpi_probe_entry *); @@ -1450,13 +1447,6 @@ static inline int acpi_node_prop_get(const struct fwnode_handle *fwnode, return -ENXIO; } -static inline struct fwnode_handle * -acpi_get_next_subnode(const struct fwnode_handle *fwnode, - struct fwnode_handle *child) -{ - return NULL; -} - static inline struct fwnode_handle * acpi_graph_get_next_endpoint(const struct fwnode_handle *fwnode, struct fwnode_handle *prev) From 5d010473cdeaabf6a2d3a9e2aed2186c1b73c213 Mon Sep 17 00:00:00 2001 From: Sakari Ailus Date: Wed, 1 Oct 2025 13:43:19 +0300 Subject: [PATCH 05/10] ACPI: property: Use ACPI functions in acpi_graph_get_next_endpoint() only Calling fwnode_get_next_child_node() in ACPI implementation of the fwnode property API is somewhat problematic as the latter is used in the impelementation of the former. Instead of using fwnode_get_next_child_node() in acpi_graph_get_next_endpoint(), call acpi_get_next_subnode() directly instead. Signed-off-by: Sakari Ailus Reviewed-by: Laurent Pinchart Reviewed-by: Jonathan Cameron Link: https://patch.msgid.link/20251001104320.1272752-3-sakari.ailus@linux.intel.com Signed-off-by: Rafael J. Wysocki --- drivers/acpi/property.c | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/drivers/acpi/property.c b/drivers/acpi/property.c index dbf86bee62e1..b88bad197fca 100644 --- a/drivers/acpi/property.c +++ b/drivers/acpi/property.c @@ -1473,7 +1473,7 @@ static struct fwnode_handle *acpi_graph_get_next_endpoint( if (!prev) { do { - port = fwnode_get_next_child_node(fwnode, port); + port = acpi_get_next_subnode(fwnode, port); /* * The names of the port nodes begin with "port@" * followed by the number of the port node and they also @@ -1491,13 +1491,13 @@ static struct fwnode_handle *acpi_graph_get_next_endpoint( if (!port) return NULL; - endpoint = fwnode_get_next_child_node(port, prev); + endpoint = acpi_get_next_subnode(port, prev); while (!endpoint) { - port = fwnode_get_next_child_node(fwnode, port); + port = acpi_get_next_subnode(fwnode, port); if (!port) break; if (is_acpi_graph_node(port, "port")) - endpoint = fwnode_get_next_child_node(port, NULL); + endpoint = acpi_get_next_subnode(port, NULL); } /* From b889ed5abf4796671b742692fdca35c4002892ee Mon Sep 17 00:00:00 2001 From: Sakari Ailus Date: Wed, 1 Oct 2025 13:43:20 +0300 Subject: [PATCH 06/10] ACPI: property: Rework acpi_graph_get_next_endpoint() Rework the code obtaining the next endpoint in acpi_graph_get_next_endpoint(). The resulting code removes unnecessary contitionals and should be easier to follow. Suggested-by: Andy Shevchenko Signed-off-by: Sakari Ailus Reviewed-by: Laurent Pinchart Link: https://patch.msgid.link/20251001104320.1272752-4-sakari.ailus@linux.intel.com Signed-off-by: Rafael J. Wysocki --- drivers/acpi/property.c | 17 ++++++++++------- 1 file changed, 10 insertions(+), 7 deletions(-) diff --git a/drivers/acpi/property.c b/drivers/acpi/property.c index b88bad197fca..73171d277ab6 100644 --- a/drivers/acpi/property.c +++ b/drivers/acpi/property.c @@ -1491,14 +1491,17 @@ static struct fwnode_handle *acpi_graph_get_next_endpoint( if (!port) return NULL; - endpoint = acpi_get_next_subnode(port, prev); - while (!endpoint) { - port = acpi_get_next_subnode(fwnode, port); - if (!port) + do { + endpoint = acpi_get_next_subnode(port, prev); + if (endpoint) break; - if (is_acpi_graph_node(port, "port")) - endpoint = acpi_get_next_subnode(port, NULL); - } + + prev = NULL; + + do { + port = acpi_get_next_subnode(fwnode, port); + } while (port && !is_acpi_graph_node(port, "port")); + } while (port); /* * The names of the endpoint nodes begin with "endpoint@" followed by From 86bfd21a0baf0111f62f195c2bff11d25f4bd410 Mon Sep 17 00:00:00 2001 From: "Rafael J. Wysocki" Date: Wed, 15 Oct 2025 15:52:41 +0200 Subject: [PATCH 07/10] ACPI: battery: Drop redundant locking All of the evaluations of objects in the ACPI namespace are carried out under the namespace lock and interpreter lock in ACPICA, so it is not necessary to put any additional locks around them for synchronization. However, the ACPI battery driver does just that, so remove the redundant locking around ACPI object evaluation from it. Signed-off-by: Rafael J. Wysocki Link: https://patch.msgid.link/2344462.iZASKD2KPV@rafael.j.wysocki --- drivers/acpi/battery.c | 12 ------------ 1 file changed, 12 deletions(-) diff --git a/drivers/acpi/battery.c b/drivers/acpi/battery.c index 67b76492c839..34181fa52e93 100644 --- a/drivers/acpi/battery.c +++ b/drivers/acpi/battery.c @@ -91,7 +91,6 @@ enum { }; struct acpi_battery { - struct mutex lock; struct mutex update_lock; struct power_supply *bat; struct power_supply_desc bat_desc; @@ -535,11 +534,9 @@ static int acpi_battery_get_info(struct acpi_battery *battery) struct acpi_buffer buffer = { ACPI_ALLOCATE_BUFFER, NULL }; acpi_status status = AE_ERROR; - mutex_lock(&battery->lock); status = acpi_evaluate_object(battery->device->handle, use_bix ? "_BIX":"_BIF", NULL, &buffer); - mutex_unlock(&battery->lock); if (ACPI_FAILURE(status)) { acpi_handle_info(battery->device->handle, @@ -576,11 +573,8 @@ static int acpi_battery_get_state(struct acpi_battery *battery) msecs_to_jiffies(cache_time))) return 0; - mutex_lock(&battery->lock); status = acpi_evaluate_object(battery->device->handle, "_BST", NULL, &buffer); - mutex_unlock(&battery->lock); - if (ACPI_FAILURE(status)) { acpi_handle_info(battery->device->handle, "_BST evaluation failed: %s", @@ -628,11 +622,8 @@ static int acpi_battery_set_alarm(struct acpi_battery *battery) !test_bit(ACPI_BATTERY_ALARM_PRESENT, &battery->flags)) return -ENODEV; - mutex_lock(&battery->lock); status = acpi_execute_simple_method(battery->device->handle, "_BTP", battery->alarm); - mutex_unlock(&battery->lock); - if (ACPI_FAILURE(status)) return -ENODEV; @@ -1235,9 +1226,6 @@ static int acpi_battery_add(struct acpi_device *device) strscpy(acpi_device_name(device), ACPI_BATTERY_DEVICE_NAME); strscpy(acpi_device_class(device), ACPI_BATTERY_CLASS); device->driver_data = battery; - result = devm_mutex_init(&device->dev, &battery->lock); - if (result) - return result; result = devm_mutex_init(&device->dev, &battery->update_lock); if (result) From 593ee49222a0d751062fd9a5e4a963ade4ec028a Mon Sep 17 00:00:00 2001 From: Haotian Zhang Date: Tue, 11 Nov 2025 15:50:00 +0800 Subject: [PATCH 08/10] ACPI: property: Fix fwnode refcount leak in acpi_fwnode_graph_parse_endpoint() acpi_fwnode_graph_parse_endpoint() calls fwnode_get_parent() to obtain the parent fwnode but returns without calling fwnode_handle_put() on it. This potentially leads to a fwnode refcount leak and prevents the parent node from being released properly. Call fwnode_handle_put() on the parent fwnode before returning to prevent the leak from occurring. Fixes: 3b27d00e7b6d ("device property: Move fwnode graph ops to firmware specific locations") Signed-off-by: Haotian Zhang Reviewed-by: Sakari Ailus [ rjw: Changelog edits ] Link: https://patch.msgid.link/20251111075000.1828-1-vulab@iscas.ac.cn Signed-off-by: Rafael J. Wysocki --- drivers/acpi/property.c | 1 + 1 file changed, 1 insertion(+) diff --git a/drivers/acpi/property.c b/drivers/acpi/property.c index 73171d277ab6..57a9c18ec673 100644 --- a/drivers/acpi/property.c +++ b/drivers/acpi/property.c @@ -1718,6 +1718,7 @@ static int acpi_fwnode_graph_parse_endpoint(const struct fwnode_handle *fwnode, if (fwnode_property_read_u32(fwnode, "reg", &endpoint->id)) fwnode_property_read_u32(fwnode, "endpoint", &endpoint->id); + fwnode_handle_put(port_fwnode); return 0; } From c964081d602f96f8811f99a938e5f8a164060bd7 Mon Sep 17 00:00:00 2001 From: David Laight Date: Wed, 19 Nov 2025 22:41:09 +0000 Subject: [PATCH 09/10] ACPI: property: use min() instead of min_t() min_t(unsigned int, a, b) casts an 'unsigned long' to 'unsigned int'. Use min(a, b) instead as it promotes any 'unsigned int' to 'unsigned long' and so cannot discard significant bits. In this case the 'unsigned long' value is small enough that the result is ok. Detected by an extra check added to min_t(). Signed-off-by: David Laight [ rjw: Subject adjustment ] Link: https://patch.msgid.link/20251119224140.8616-14-david.laight.linux@gmail.com Signed-off-by: Rafael J. Wysocki --- drivers/acpi/property.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/acpi/property.c b/drivers/acpi/property.c index 57a9c18ec673..18e90067d567 100644 --- a/drivers/acpi/property.c +++ b/drivers/acpi/property.c @@ -1280,7 +1280,7 @@ static int acpi_data_prop_read(const struct acpi_device_data *data, ret = acpi_copy_property_array_uint(items, (u64 *)val, nval); break; case DEV_PROP_STRING: - nval = min_t(u32, nval, obj->package.count); + nval = min(nval, obj->package.count); if (nval == 0) return -ENODATA; From 9d6c58dae8f6590c746ac5d0012ffe14a77539f0 Mon Sep 17 00:00:00 2001 From: Cryolitia PukNgae Date: Tue, 25 Nov 2025 16:14:38 +0800 Subject: [PATCH 10/10] ACPICA: Avoid walking the Namespace if start_node is NULL Although commit 0c9992315e73 ("ACPICA: Avoid walking the ACPI Namespace if it is not there") fixed the situation when both start_node and acpi_gbl_root_node are NULL, the Linux kernel mainline now still crashed on Honor Magicbook 14 Pro [1]. That happens due to the access to the member of parent_node in acpi_ns_get_next_node(). The NULL pointer dereference will always happen, no matter whether or not the start_node is equal to ACPI_ROOT_OBJECT, so move the check of start_node being NULL out of the if block. Unfortunately, all the attempts to contact Honor have failed, they refused to provide any technical support for Linux. The bad DSDT table's dump could be found on GitHub [2]. DMI: HONOR FMB-P/FMB-P-PCB, BIOS 1.13 05/08/2025 Link: https://github.com/acpica/acpica/commit/1c1b57b9eba4554cb132ee658dd942c0210ed20d Link: https://gist.github.com/Cryolitia/a860ffc97437dcd2cd988371d5b73ed7 [1] Link: https://github.com/denis-bb/honor-fmb-p-dsdt [2] Signed-off-by: Cryolitia PukNgae Reviewed-by: WangYuli [ rjw: Subject adjustment, changelog edits ] Link: https://patch.msgid.link/20251125-acpica-v1-1-99e63b1b25f8@linux.dev Signed-off-by: Rafael J. Wysocki --- drivers/acpi/acpica/nswalk.c | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/drivers/acpi/acpica/nswalk.c b/drivers/acpi/acpica/nswalk.c index a2ac06a26e92..5670ff5a43cd 100644 --- a/drivers/acpi/acpica/nswalk.c +++ b/drivers/acpi/acpica/nswalk.c @@ -169,9 +169,12 @@ acpi_ns_walk_namespace(acpi_object_type type, if (start_node == ACPI_ROOT_OBJECT) { start_node = acpi_gbl_root_node; - if (!start_node) { - return_ACPI_STATUS(AE_NO_NAMESPACE); - } + } + + /* Avoid walking the namespace if the StartNode is NULL */ + + if (!start_node) { + return_ACPI_STATUS(AE_NO_NAMESPACE); } /* Null child means "get first node" */