* FAILED: patch "[PATCH] ACPI: battery: Protect all properties with a separated mutex" failed to apply to 7.2-stable tree
@ 2026-09-03 13:43 gregkh
2026-09-08 1:38 ` [PATCH 7.2.y 1/2] ACPI: battery: Use kstrtoul() over sscanf("%lu\n") Sasha Levin
0 siblings, 1 reply; 3+ messages in thread
From: gregkh @ 2026-09-03 13:43 UTC (permalink / raw)
To: i, anhollander516, rafael.j.wysocki, rickk1166, stable; +Cc: stable
The patch below does not apply to the 7.2-stable tree.
If someone wants it applied there, or to any other stable or longterm
tree, then please email the backport, including the original git commit
id to <stable@vger.kernel.org>.
To reproduce the conflict and resubmit, you may use the following commands:
git fetch https://git.kernel.org/pub/scm/linux/kernel/git/stable/linux.git/ linux-7.2.y
git checkout FETCH_HEAD
git cherry-pick -x 9e409f1dff7841634e4b101111d6427f979c0aac
# <resolve conflicts, build, test, etc.>
git commit -s
git send-email --to '<stable@vger.kernel.org>' --in-reply-to '2026090357-tweet-veal-37b7@gregkh' --subject-prefix 'PATCH 7.2.y' 'HEAD^..'
Possible dependencies:
thanks,
greg k-h
------------------ original commit in Linus's tree ------------------
From 9e409f1dff7841634e4b101111d6427f979c0aac Mon Sep 17 00:00:00 2001
From: Rong Zhang <i@rong.moe>
Date: Sun, 9 Aug 2026 07:43:55 +0800
Subject: [PATCH] ACPI: battery: Protect all properties with a separated mutex
The acpi_battery_get_property() callback calls acpi_battery_get_state()
without any lock held. On some devices, it happens that the property
cache has expired before a uevent reaches userspace, triggering
simultaneous attempts to evaluate _BST. See [1] for an analysis to sysrq
stacktraces on one of the these devices.
In a few cases, including when the AML is sleeping or acquiring a mutex,
ACPICA drops the namespace and interpreter locks and allows the
evaluation of _BST to start while another task is still evaluating it.
This could somehow confuse the interpreter and lead to chaos in AML
mutexes on some devices, see [2] for an example.
Not holding the lock is also prone to race conditions, for example:
CPU0 | CPU1
acpi_battery_get_property() |
acpi_battery_get_state() |
[update_time expired] |
extract_package() | acpi_battery_get_property()
battery->update_time = jiffies | acpi_battery_get_state()
kfree() | [up to date]
| [read capacity_now]
[fix capacity_now due to quirk] |
where CPU1 gets raw capacity_now before CPU0 fixes it to a meaningful
value.
The existing mutex update_lock is not applicapable for
acpi_battery_get_property(), as some code path could call or wait for
acpi_battery_get_property() while holding update_lock.
Therefore, introduce a mutex called property_lock to protect all
accesses to battery properties, so that acpi_battery_get_property() can
take the advantage of the mutex and synchronize itself. With the mutex,
acpi_battery_get_state() are synchronized in all code paths calling it,
and its cache mechanism can always clamp the frequency of _BST
evaluations according to cache_time.
The helper function acpi_battery_handle_discharging() for quirky devices
has to be inlined due to the change, as the mutex must be unlocked
before calling the expensive power_supply_is_system_supplied() helper
function.
Fixes: 86bfd21a0baf ("ACPI: battery: Drop redundant locking")
Reported-by: Rick <rickk1166@gmail.com>
Closes: https://bugzilla.kernel.org/show_bug.cgi?id=221065#c85 [1]
Reported-by: Avraham Hollander <anhollander516@gmail.com>
Tested-by: Avraham Hollander <anhollander516@gmail.com>
Closes: https://lore.kernel.org/linux-acpi/CAP1mzZReJCn6df5DwEPu-JCQUyr=Pu1cg5xKCMttWZkHCQtVmQ@mail.gmail.com [2]
Signed-off-by: Rong Zhang <i@rong.moe>
Cc: All applicable <stable@vger.kernel.org>
Link: https://patch.msgid.link/20260809-b4-acpi-battery-notification-v5-1-788d54fa2e35@rong.moe
Signed-off-by: Rafael J. Wysocki <rafael.j.wysocki@intel.com>
diff --git a/drivers/acpi/battery.c b/drivers/acpi/battery.c
index 0084f308b790..670853ec3a4d 100644
--- a/drivers/acpi/battery.c
+++ b/drivers/acpi/battery.c
@@ -17,6 +17,7 @@
#include <linux/kernel.h>
#include <linux/kfifo.h>
#include <linux/list.h>
+#include <linux/lockdep.h>
#include <linux/module.h>
#include <linux/mutex.h>
#include <linux/platform_device.h>
@@ -105,6 +106,9 @@ struct acpi_battery {
struct delayed_work acpi_notif_dwork;
struct notifier_block pm_nb;
struct list_head list;
+ unsigned long flags;
+
+ struct mutex property_lock; /* Protects properties below. */
unsigned long update_time;
int revision;
int rate_now;
@@ -131,7 +135,6 @@ struct acpi_battery {
char oem_info[MAX_STRING_LENGTH];
int state;
int power_unit;
- unsigned long flags;
};
#define to_acpi_battery(x) power_supply_get_drvdata(x)
@@ -189,20 +192,6 @@ static bool acpi_battery_is_degraded(struct acpi_battery *battery)
battery->full_charge_capacity < battery->design_capacity;
}
-static int acpi_battery_handle_discharging(struct acpi_battery *battery)
-{
- /*
- * Some devices wrongly report discharging if the battery's charge level
- * was above the device's start charging threshold atm the AC adapter
- * was plugged in and the device thus did not start a new charge cycle.
- */
- if ((battery_ac_is_broken || power_supply_is_system_supplied()) &&
- battery->rate_now == 0)
- return POWER_SUPPLY_STATUS_NOT_CHARGING;
-
- return POWER_SUPPLY_STATUS_DISCHARGING;
-}
-
static int acpi_battery_get_property(struct power_supply *psy,
enum power_supply_property psp,
union power_supply_propval *val)
@@ -210,15 +199,41 @@ static int acpi_battery_get_property(struct power_supply *psy,
int full_capacity = ACPI_BATTERY_VALUE_UNKNOWN, ret = 0;
struct acpi_battery *battery = to_acpi_battery(psy);
- if (acpi_battery_present(battery)) {
- /* run battery update only if it is present */
- acpi_battery_get_state(battery);
- } else if (psp != POWER_SUPPLY_PROP_PRESENT)
- return -ENODEV;
+ /* run battery update only if it is present */
+ if (!acpi_battery_present(battery)) {
+ switch (psp) {
+ case POWER_SUPPLY_PROP_PRESENT:
+ val->intval = 0;
+ return 0;
+ default:
+ return -ENODEV;
+ }
+ }
+
+ mutex_lock(&battery->property_lock);
+
+ acpi_battery_get_state(battery);
+
switch (psp) {
case POWER_SUPPLY_PROP_STATUS:
+ /*
+ * Some devices wrongly report discharging if the battery's charge level
+ * was above the device's start charging threshold atm the AC adapter
+ * was plugged in and the device thus did not start a new charge cycle.
+ */
if (battery->state & ACPI_BATTERY_STATE_DISCHARGING)
- val->intval = acpi_battery_handle_discharging(battery);
+ if (battery->rate_now != 0) {
+ val->intval = POWER_SUPPLY_STATUS_DISCHARGING;
+ } else if (battery_ac_is_broken) {
+ val->intval = POWER_SUPPLY_STATUS_NOT_CHARGING;
+ } else {
+ mutex_unlock(&battery->property_lock);
+
+ val->intval = power_supply_is_system_supplied()
+ ? POWER_SUPPLY_STATUS_NOT_CHARGING
+ : POWER_SUPPLY_STATUS_DISCHARGING;
+ return 0;
+ }
else if (battery->state & ACPI_BATTERY_STATE_CHARGING)
/* Check the rate and capacity to validate the status. */
if (!acpi_battery_is_full(battery) ||
@@ -321,6 +336,8 @@ static int acpi_battery_get_property(struct power_supply *psy,
default:
ret = -EINVAL;
}
+
+ mutex_unlock(&battery->property_lock);
return ret;
}
@@ -556,6 +573,8 @@ static int acpi_battery_get_info(struct acpi_battery *battery)
int use_bix;
int result = -ENODEV;
+ lockdep_assert_held(&battery->property_lock);
+
if (!acpi_battery_present(battery))
return 0;
@@ -595,6 +614,8 @@ static int acpi_battery_get_state(struct acpi_battery *battery)
acpi_status status = 0;
struct acpi_buffer buffer = { ACPI_ALLOCATE_BUFFER, NULL };
+ lockdep_assert_held(&battery->property_lock);
+
if (!acpi_battery_present(battery))
return 0;
@@ -648,6 +669,8 @@ static int acpi_battery_set_alarm(struct acpi_battery *battery)
{
acpi_status status = 0;
+ lockdep_assert_held(&battery->property_lock);
+
if (!acpi_battery_present(battery) ||
!test_bit(ACPI_BATTERY_ALARM_PRESENT, &battery->flags))
return -ENODEV;
@@ -665,6 +688,8 @@ static int acpi_battery_set_alarm(struct acpi_battery *battery)
static int acpi_battery_init_alarm(struct acpi_battery *battery)
{
+ lockdep_assert_held(&battery->property_lock);
+
/* See if alarms are supported, and if so, set default */
if (!acpi_has_method(battery->device->handle, "_BTP")) {
clear_bit(ACPI_BATTERY_ALARM_PRESENT, &battery->flags);
@@ -682,6 +707,8 @@ static ssize_t acpi_battery_alarm_show(struct device *dev,
{
struct acpi_battery *battery = to_acpi_battery(dev_get_drvdata(dev));
+ guard(mutex)(&battery->property_lock);
+
return sysfs_emit(buf, "%d\n", battery->alarm * 1000);
}
@@ -697,6 +724,8 @@ static ssize_t acpi_battery_alarm_store(struct device *dev,
if (err)
return err;
+ guard(mutex)(&battery->property_lock);
+
battery->alarm = x / 1000;
if (acpi_battery_present(battery))
acpi_battery_set_alarm(battery);
@@ -881,12 +910,17 @@ static int sysfs_add_battery(struct acpi_battery *battery)
.no_wakeup_source = true,
};
bool full_cap_broken = false;
+ int power_unit;
- if (!ACPI_BATTERY_CAPACITY_VALID(battery->full_charge_capacity) &&
- !ACPI_BATTERY_CAPACITY_VALID(battery->design_capacity))
- full_cap_broken = true;
+ scoped_guard(mutex, &battery->property_lock) {
+ power_unit = battery->power_unit;
- if (battery->power_unit == ACPI_BATTERY_POWER_UNIT_MA) {
+ if (!ACPI_BATTERY_CAPACITY_VALID(battery->full_charge_capacity) &&
+ !ACPI_BATTERY_CAPACITY_VALID(battery->design_capacity))
+ full_cap_broken = true;
+ }
+
+ if (power_unit == ACPI_BATTERY_POWER_UNIT_MA) {
if (full_cap_broken) {
battery->bat_desc.properties =
charge_battery_full_cap_broken_props;
@@ -940,6 +974,9 @@ static void sysfs_remove_battery(struct acpi_battery *battery)
static void find_battery(const struct dmi_header *dm, void *private)
{
struct acpi_battery *battery = (struct acpi_battery *)private;
+
+ lockdep_assert_held(&battery->property_lock);
+
/* Note: the hardcoded offsets below have been extracted from
* the source code of dmidecode.
*/
@@ -971,6 +1008,8 @@ static void find_battery(const struct dmi_header *dm, void *private)
*/
static void acpi_battery_quirks(struct acpi_battery *battery)
{
+ lockdep_assert_held(&battery->property_lock);
+
if (test_bit(ACPI_BATTERY_QUIRK_PERCENTAGE_CAPACITY, &battery->flags))
return;
@@ -1023,30 +1062,38 @@ static void acpi_battery_quirks(struct acpi_battery *battery)
static int acpi_battery_update(struct acpi_battery *battery, bool resume)
{
int result = acpi_battery_get_status(battery);
+ bool wakeup;
if (result)
return result;
if (!acpi_battery_present(battery)) {
sysfs_remove_battery(battery);
- battery->update_time = 0;
+ scoped_guard(mutex, &battery->property_lock)
+ battery->update_time = 0;
return 0;
}
if (resume)
return 0;
- if (!battery->update_time) {
- result = acpi_battery_get_info(battery);
+ scoped_guard(mutex, &battery->property_lock) {
+ if (!battery->update_time) {
+ result = acpi_battery_get_info(battery);
+ if (result)
+ return result;
+ acpi_battery_init_alarm(battery);
+ }
+
+ result = acpi_battery_get_state(battery);
if (result)
return result;
- acpi_battery_init_alarm(battery);
- }
+ acpi_battery_quirks(battery);
- result = acpi_battery_get_state(battery);
- if (result)
- return result;
- acpi_battery_quirks(battery);
+ wakeup = ((battery->state & ACPI_BATTERY_STATE_CRITICAL) ||
+ (test_bit(ACPI_BATTERY_ALARM_PRESENT, &battery->flags) &&
+ (battery->capacity_now <= battery->alarm)));
+ }
if (!battery->bat) {
result = sysfs_add_battery(battery);
@@ -1058,9 +1105,7 @@ static int acpi_battery_update(struct acpi_battery *battery, bool resume)
* Wakeup the system if battery is critical low
* or lower than the alarm level
*/
- if ((battery->state & ACPI_BATTERY_STATE_CRITICAL) ||
- (test_bit(ACPI_BATTERY_ALARM_PRESENT, &battery->flags) &&
- (battery->capacity_now <= battery->alarm)))
+ if (wakeup)
acpi_pm_wakeup_event(battery->phys_dev);
return result;
@@ -1073,12 +1118,14 @@ static void acpi_battery_refresh(struct acpi_battery *battery)
if (!battery->bat)
return;
- power_unit = battery->power_unit;
+ scoped_guard(mutex, &battery->property_lock) {
+ power_unit = battery->power_unit;
- acpi_battery_get_info(battery);
+ acpi_battery_get_info(battery);
- if (power_unit == battery->power_unit)
- return;
+ if (power_unit == battery->power_unit)
+ return;
+ }
/* The battery has changed its reporting units. */
sysfs_remove_battery(battery);
@@ -1170,17 +1217,21 @@ static int battery_notify(struct notifier_block *nb,
} else {
int result;
- result = acpi_battery_get_info(battery);
- if (result)
- return result;
+ scoped_guard(mutex, &battery->property_lock) {
+ result = acpi_battery_get_info(battery);
+ if (result)
+ return result;
+ }
result = sysfs_add_battery(battery);
if (result)
return result;
}
- acpi_battery_init_alarm(battery);
- acpi_battery_get_state(battery);
+ scoped_guard(mutex, &battery->property_lock) {
+ acpi_battery_init_alarm(battery);
+ acpi_battery_get_state(battery);
+ }
}
return 0;
@@ -1345,6 +1396,10 @@ static int acpi_battery_probe(struct platform_device *pdev)
if (result)
return result;
+ result = devm_mutex_init(&pdev->dev, &battery->property_lock);
+ if (result)
+ return result;
+
if (acpi_has_method(battery->device->handle, "_BIX"))
set_bit(ACPI_BATTERY_XINFO_PRESENT, &battery->flags);
^ permalink raw reply related [flat|nested] 3+ messages in thread
* [PATCH 7.2.y 1/2] ACPI: battery: Use kstrtoul() over sscanf("%lu\n")
2026-09-03 13:43 FAILED: patch "[PATCH] ACPI: battery: Protect all properties with a separated mutex" failed to apply to 7.2-stable tree gregkh
@ 2026-09-08 1:38 ` Sasha Levin
2026-09-08 1:38 ` [PATCH 7.2.y 2/2] ACPI: battery: Protect all properties with a separated mutex Sasha Levin
0 siblings, 1 reply; 3+ messages in thread
From: Sasha Levin @ 2026-09-08 1:38 UTC (permalink / raw)
To: stable; +Cc: Rong Zhang, Avraham Hollander, Rafael J. Wysocki, Sasha Levin
From: Rong Zhang <i@rong.moe>
[ Upstream commit 57346c4d78d38b357dbe9ef16d3f63bf4610c039 ]
It is more preferred to use kstrto*() to parse a single number. The
function family properly returns an errno on error and is the correct
mechanism to parse data from sysfs.
The number base is set to 10 in order not to break the ABI.
Tested-by: Avraham Hollander <anhollander516@gmail.com>
Signed-off-by: Rong Zhang <i@rong.moe>
Link: https://patch.msgid.link/20260718-b4-acpi-battery-notification-v4-2-599c8ed1072f@rong.moe
Signed-off-by: Rafael J. Wysocki <rafael.j.wysocki@intel.com>
Stable-dep-of: 9e409f1dff78 ("ACPI: battery: Protect all properties with a separated mutex")
Signed-off-by: Sasha Levin <sashal@kernel.org>
---
drivers/acpi/battery.c | 8 ++++++--
1 file changed, 6 insertions(+), 2 deletions(-)
diff --git a/drivers/acpi/battery.c b/drivers/acpi/battery.c
index f5e0eb299610b..605c47efdf06b 100644
--- a/drivers/acpi/battery.c
+++ b/drivers/acpi/battery.c
@@ -668,9 +668,13 @@ static ssize_t acpi_battery_alarm_store(struct device *dev,
{
unsigned long x;
struct acpi_battery *battery = to_acpi_battery(dev_get_drvdata(dev));
+ int err;
- if (sscanf(buf, "%lu\n", &x) == 1)
- battery->alarm = x/1000;
+ err = kstrtoul(buf, 10, &x);
+ if (err)
+ return err;
+
+ battery->alarm = x / 1000;
if (acpi_battery_present(battery))
acpi_battery_set_alarm(battery);
return count;
--
2.53.0
^ permalink raw reply related [flat|nested] 3+ messages in thread
* [PATCH 7.2.y 2/2] ACPI: battery: Protect all properties with a separated mutex
2026-09-08 1:38 ` [PATCH 7.2.y 1/2] ACPI: battery: Use kstrtoul() over sscanf("%lu\n") Sasha Levin
@ 2026-09-08 1:38 ` Sasha Levin
0 siblings, 0 replies; 3+ messages in thread
From: Sasha Levin @ 2026-09-08 1:38 UTC (permalink / raw)
To: stable; +Cc: Rong Zhang, Rick, Avraham Hollander, Rafael J. Wysocki,
Sasha Levin
From: Rong Zhang <i@rong.moe>
[ Upstream commit 9e409f1dff7841634e4b101111d6427f979c0aac ]
The acpi_battery_get_property() callback calls acpi_battery_get_state()
without any lock held. On some devices, it happens that the property
cache has expired before a uevent reaches userspace, triggering
simultaneous attempts to evaluate _BST. See [1] for an analysis to sysrq
stacktraces on one of the these devices.
In a few cases, including when the AML is sleeping or acquiring a mutex,
ACPICA drops the namespace and interpreter locks and allows the
evaluation of _BST to start while another task is still evaluating it.
This could somehow confuse the interpreter and lead to chaos in AML
mutexes on some devices, see [2] for an example.
Not holding the lock is also prone to race conditions, for example:
CPU0 | CPU1
acpi_battery_get_property() |
acpi_battery_get_state() |
[update_time expired] |
extract_package() | acpi_battery_get_property()
battery->update_time = jiffies | acpi_battery_get_state()
kfree() | [up to date]
| [read capacity_now]
[fix capacity_now due to quirk] |
where CPU1 gets raw capacity_now before CPU0 fixes it to a meaningful
value.
The existing mutex update_lock is not applicapable for
acpi_battery_get_property(), as some code path could call or wait for
acpi_battery_get_property() while holding update_lock.
Therefore, introduce a mutex called property_lock to protect all
accesses to battery properties, so that acpi_battery_get_property() can
take the advantage of the mutex and synchronize itself. With the mutex,
acpi_battery_get_state() are synchronized in all code paths calling it,
and its cache mechanism can always clamp the frequency of _BST
evaluations according to cache_time.
The helper function acpi_battery_handle_discharging() for quirky devices
has to be inlined due to the change, as the mutex must be unlocked
before calling the expensive power_supply_is_system_supplied() helper
function.
Fixes: 86bfd21a0baf ("ACPI: battery: Drop redundant locking")
Reported-by: Rick <rickk1166@gmail.com>
Closes: https://bugzilla.kernel.org/show_bug.cgi?id=221065#c85 [1]
Reported-by: Avraham Hollander <anhollander516@gmail.com>
Tested-by: Avraham Hollander <anhollander516@gmail.com>
Closes: https://lore.kernel.org/linux-acpi/CAP1mzZReJCn6df5DwEPu-JCQUyr=Pu1cg5xKCMttWZkHCQtVmQ@mail.gmail.com [2]
Signed-off-by: Rong Zhang <i@rong.moe>
Cc: All applicable <stable@vger.kernel.org>
Link: https://patch.msgid.link/20260809-b4-acpi-battery-notification-v5-1-788d54fa2e35@rong.moe
Signed-off-by: Rafael J. Wysocki <rafael.j.wysocki@intel.com>
Signed-off-by: Sasha Levin <sashal@kernel.org>
---
drivers/acpi/battery.c | 147 ++++++++++++++++++++++++++++-------------
1 file changed, 101 insertions(+), 46 deletions(-)
diff --git a/drivers/acpi/battery.c b/drivers/acpi/battery.c
index 605c47efdf06b..f8fa0d9a1f4c7 100644
--- a/drivers/acpi/battery.c
+++ b/drivers/acpi/battery.c
@@ -15,6 +15,7 @@
#include <linux/jiffies.h>
#include <linux/kernel.h>
#include <linux/list.h>
+#include <linux/lockdep.h>
#include <linux/module.h>
#include <linux/mutex.h>
#include <linux/platform_device.h>
@@ -97,6 +98,9 @@ struct acpi_battery {
struct device *phys_dev;
struct notifier_block pm_nb;
struct list_head list;
+ unsigned long flags;
+
+ struct mutex property_lock; /* Protects properties below. */
unsigned long update_time;
int revision;
int rate_now;
@@ -123,7 +127,6 @@ struct acpi_battery {
char oem_info[MAX_STRING_LENGTH];
int state;
int power_unit;
- unsigned long flags;
};
#define to_acpi_battery(x) power_supply_get_drvdata(x)
@@ -180,20 +183,6 @@ static bool acpi_battery_is_degraded(struct acpi_battery *battery)
battery->full_charge_capacity < battery->design_capacity;
}
-static int acpi_battery_handle_discharging(struct acpi_battery *battery)
-{
- /*
- * Some devices wrongly report discharging if the battery's charge level
- * was above the device's start charging threshold atm the AC adapter
- * was plugged in and the device thus did not start a new charge cycle.
- */
- if ((battery_ac_is_broken || power_supply_is_system_supplied()) &&
- battery->rate_now == 0)
- return POWER_SUPPLY_STATUS_NOT_CHARGING;
-
- return POWER_SUPPLY_STATUS_DISCHARGING;
-}
-
static int acpi_battery_get_property(struct power_supply *psy,
enum power_supply_property psp,
union power_supply_propval *val)
@@ -201,15 +190,41 @@ static int acpi_battery_get_property(struct power_supply *psy,
int full_capacity = ACPI_BATTERY_VALUE_UNKNOWN, ret = 0;
struct acpi_battery *battery = to_acpi_battery(psy);
- if (acpi_battery_present(battery)) {
- /* run battery update only if it is present */
- acpi_battery_get_state(battery);
- } else if (psp != POWER_SUPPLY_PROP_PRESENT)
- return -ENODEV;
+ /* run battery update only if it is present */
+ if (!acpi_battery_present(battery)) {
+ switch (psp) {
+ case POWER_SUPPLY_PROP_PRESENT:
+ val->intval = 0;
+ return 0;
+ default:
+ return -ENODEV;
+ }
+ }
+
+ mutex_lock(&battery->property_lock);
+
+ acpi_battery_get_state(battery);
+
switch (psp) {
case POWER_SUPPLY_PROP_STATUS:
+ /*
+ * Some devices wrongly report discharging if the battery's charge level
+ * was above the device's start charging threshold atm the AC adapter
+ * was plugged in and the device thus did not start a new charge cycle.
+ */
if (battery->state & ACPI_BATTERY_STATE_DISCHARGING)
- val->intval = acpi_battery_handle_discharging(battery);
+ if (battery->rate_now != 0) {
+ val->intval = POWER_SUPPLY_STATUS_DISCHARGING;
+ } else if (battery_ac_is_broken) {
+ val->intval = POWER_SUPPLY_STATUS_NOT_CHARGING;
+ } else {
+ mutex_unlock(&battery->property_lock);
+
+ val->intval = power_supply_is_system_supplied()
+ ? POWER_SUPPLY_STATUS_NOT_CHARGING
+ : POWER_SUPPLY_STATUS_DISCHARGING;
+ return 0;
+ }
else if (battery->state & ACPI_BATTERY_STATE_CHARGING)
/* Validate the status by checking the current. */
if (battery->rate_now != ACPI_BATTERY_VALUE_UNKNOWN &&
@@ -311,6 +326,8 @@ static int acpi_battery_get_property(struct power_supply *psy,
default:
ret = -EINVAL;
}
+
+ mutex_unlock(&battery->property_lock);
return ret;
}
@@ -533,6 +550,8 @@ static int acpi_battery_get_info(struct acpi_battery *battery)
int use_bix;
int result = -ENODEV;
+ lockdep_assert_held(&battery->property_lock);
+
if (!acpi_battery_present(battery))
return 0;
@@ -572,6 +591,8 @@ static int acpi_battery_get_state(struct acpi_battery *battery)
acpi_status status = 0;
struct acpi_buffer buffer = { ACPI_ALLOCATE_BUFFER, NULL };
+ lockdep_assert_held(&battery->property_lock);
+
if (!acpi_battery_present(battery))
return 0;
@@ -625,6 +646,8 @@ static int acpi_battery_set_alarm(struct acpi_battery *battery)
{
acpi_status status = 0;
+ lockdep_assert_held(&battery->property_lock);
+
if (!acpi_battery_present(battery) ||
!test_bit(ACPI_BATTERY_ALARM_PRESENT, &battery->flags))
return -ENODEV;
@@ -642,6 +665,8 @@ static int acpi_battery_set_alarm(struct acpi_battery *battery)
static int acpi_battery_init_alarm(struct acpi_battery *battery)
{
+ lockdep_assert_held(&battery->property_lock);
+
/* See if alarms are supported, and if so, set default */
if (!acpi_has_method(battery->device->handle, "_BTP")) {
clear_bit(ACPI_BATTERY_ALARM_PRESENT, &battery->flags);
@@ -659,6 +684,8 @@ static ssize_t acpi_battery_alarm_show(struct device *dev,
{
struct acpi_battery *battery = to_acpi_battery(dev_get_drvdata(dev));
+ guard(mutex)(&battery->property_lock);
+
return sysfs_emit(buf, "%d\n", battery->alarm * 1000);
}
@@ -674,6 +701,8 @@ static ssize_t acpi_battery_alarm_store(struct device *dev,
if (err)
return err;
+ guard(mutex)(&battery->property_lock);
+
battery->alarm = x / 1000;
if (acpi_battery_present(battery))
acpi_battery_set_alarm(battery);
@@ -858,12 +887,17 @@ static int sysfs_add_battery(struct acpi_battery *battery)
.no_wakeup_source = true,
};
bool full_cap_broken = false;
+ int power_unit;
- if (!ACPI_BATTERY_CAPACITY_VALID(battery->full_charge_capacity) &&
- !ACPI_BATTERY_CAPACITY_VALID(battery->design_capacity))
- full_cap_broken = true;
+ scoped_guard(mutex, &battery->property_lock) {
+ power_unit = battery->power_unit;
- if (battery->power_unit == ACPI_BATTERY_POWER_UNIT_MA) {
+ if (!ACPI_BATTERY_CAPACITY_VALID(battery->full_charge_capacity) &&
+ !ACPI_BATTERY_CAPACITY_VALID(battery->design_capacity))
+ full_cap_broken = true;
+ }
+
+ if (power_unit == ACPI_BATTERY_POWER_UNIT_MA) {
if (full_cap_broken) {
battery->bat_desc.properties =
charge_battery_full_cap_broken_props;
@@ -917,6 +951,9 @@ static void sysfs_remove_battery(struct acpi_battery *battery)
static void find_battery(const struct dmi_header *dm, void *private)
{
struct acpi_battery *battery = (struct acpi_battery *)private;
+
+ lockdep_assert_held(&battery->property_lock);
+
/* Note: the hardcoded offsets below have been extracted from
* the source code of dmidecode.
*/
@@ -948,6 +985,8 @@ static void find_battery(const struct dmi_header *dm, void *private)
*/
static void acpi_battery_quirks(struct acpi_battery *battery)
{
+ lockdep_assert_held(&battery->property_lock);
+
if (test_bit(ACPI_BATTERY_QUIRK_PERCENTAGE_CAPACITY, &battery->flags))
return;
@@ -1000,30 +1039,38 @@ static void acpi_battery_quirks(struct acpi_battery *battery)
static int acpi_battery_update(struct acpi_battery *battery, bool resume)
{
int result = acpi_battery_get_status(battery);
+ bool wakeup;
if (result)
return result;
if (!acpi_battery_present(battery)) {
sysfs_remove_battery(battery);
- battery->update_time = 0;
+ scoped_guard(mutex, &battery->property_lock)
+ battery->update_time = 0;
return 0;
}
if (resume)
return 0;
- if (!battery->update_time) {
- result = acpi_battery_get_info(battery);
+ scoped_guard(mutex, &battery->property_lock) {
+ if (!battery->update_time) {
+ result = acpi_battery_get_info(battery);
+ if (result)
+ return result;
+ acpi_battery_init_alarm(battery);
+ }
+
+ result = acpi_battery_get_state(battery);
if (result)
return result;
- acpi_battery_init_alarm(battery);
- }
+ acpi_battery_quirks(battery);
- result = acpi_battery_get_state(battery);
- if (result)
- return result;
- acpi_battery_quirks(battery);
+ wakeup = ((battery->state & ACPI_BATTERY_STATE_CRITICAL) ||
+ (test_bit(ACPI_BATTERY_ALARM_PRESENT, &battery->flags) &&
+ (battery->capacity_now <= battery->alarm)));
+ }
if (!battery->bat) {
result = sysfs_add_battery(battery);
@@ -1035,9 +1082,7 @@ static int acpi_battery_update(struct acpi_battery *battery, bool resume)
* Wakeup the system if battery is critical low
* or lower than the alarm level
*/
- if ((battery->state & ACPI_BATTERY_STATE_CRITICAL) ||
- (test_bit(ACPI_BATTERY_ALARM_PRESENT, &battery->flags) &&
- (battery->capacity_now <= battery->alarm)))
+ if (wakeup)
acpi_pm_wakeup_event(battery->phys_dev);
return result;
@@ -1050,12 +1095,14 @@ static void acpi_battery_refresh(struct acpi_battery *battery)
if (!battery->bat)
return;
- power_unit = battery->power_unit;
+ scoped_guard(mutex, &battery->property_lock) {
+ power_unit = battery->power_unit;
- acpi_battery_get_info(battery);
+ acpi_battery_get_info(battery);
- if (power_unit == battery->power_unit)
- return;
+ if (power_unit == battery->power_unit)
+ return;
+ }
/* The battery has changed its reporting units. */
sysfs_remove_battery(battery);
@@ -1110,17 +1157,21 @@ static int battery_notify(struct notifier_block *nb,
} else {
int result;
- result = acpi_battery_get_info(battery);
- if (result)
- return result;
+ scoped_guard(mutex, &battery->property_lock) {
+ result = acpi_battery_get_info(battery);
+ if (result)
+ return result;
+ }
result = sysfs_add_battery(battery);
if (result)
return result;
}
- acpi_battery_init_alarm(battery);
- acpi_battery_get_state(battery);
+ scoped_guard(mutex, &battery->property_lock) {
+ acpi_battery_init_alarm(battery);
+ acpi_battery_get_state(battery);
+ }
}
return 0;
@@ -1262,6 +1313,10 @@ static int acpi_battery_probe(struct platform_device *pdev)
if (result)
return result;
+ result = devm_mutex_init(&pdev->dev, &battery->property_lock);
+ if (result)
+ return result;
+
if (acpi_has_method(battery->device->handle, "_BIX"))
set_bit(ACPI_BATTERY_XINFO_PRESENT, &battery->flags);
--
2.53.0
^ permalink raw reply related [flat|nested] 3+ messages in thread
end of thread, other threads:[~2026-09-08 1:38 UTC | newest]
Thread overview: 3+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2026-09-03 13:43 FAILED: patch "[PATCH] ACPI: battery: Protect all properties with a separated mutex" failed to apply to 7.2-stable tree gregkh
2026-09-08 1:38 ` [PATCH 7.2.y 1/2] ACPI: battery: Use kstrtoul() over sscanf("%lu\n") Sasha Levin
2026-09-08 1:38 ` [PATCH 7.2.y 2/2] ACPI: battery: Protect all properties with a separated mutex Sasha Levin
This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.