Skip to content

Commit e9e16bb

Browse files
rafaeljwjfvogel
authored andcommitted
ACPI: battery: Add synchronization between interface updates
[ Upstream commit 399dbcadc01ebf0035f325eaa8c264f8b5cd0a14 ] There is no synchronization between different code paths in the ACPI battery driver that update its sysfs interface or its power supply class device interface. In some cases this results to functional failures due to race conditions. One example of this is when two ACPI notifications: - ACPI_BATTERY_NOTIFY_STATUS (0x80) - ACPI_BATTERY_NOTIFY_INFO (0x81) are triggered (by the platform firmware) in a row with a little delay in between after removing and reinserting a laptop battery. Both notifications cause acpi_battery_update() to be called and if the delay between them is sufficiently small, sysfs_add_battery() can be re-entered before battery->bat is set which leads to a duplicate sysfs entry error: sysfs: cannot create duplicate filename '/devices/LNXSYSTM:00/LNXSYBUS:00/PNP0C0A:00/power_supply/BAT1' CPU: 1 UID: 0 PID: 185 Comm: kworker/1:4 Kdump: loaded Not tainted 6.12.38+deb13-amd64 #1 Debian 6.12.38-1 Hardware name: Gateway NV44 /SJV40-MV , BIOS V1.3121 04/08/2009 Workqueue: kacpi_notify acpi_os_execute_deferred Call Trace: <TASK> dump_stack_lvl+0x5d/0x80 sysfs_warn_dup.cold+0x17/0x23 sysfs_create_dir_ns+0xce/0xe0 kobject_add_internal+0xba/0x250 kobject_add+0x96/0xc0 ? get_device_parent+0xde/0x1e0 device_add+0xe2/0x870 __power_supply_register.part.0+0x20f/0x3f0 ? wake_up_q+0x4e/0x90 sysfs_add_battery+0xa4/0x1d0 [battery] acpi_battery_update+0x19e/0x290 [battery] acpi_battery_notify+0x50/0x120 [battery] acpi_ev_notify_dispatch+0x49/0x70 acpi_os_execute_deferred+0x1a/0x30 process_one_work+0x177/0x330 worker_thread+0x251/0x390 ? __pfx_worker_thread+0x10/0x10 kthread+0xd2/0x100 ? __pfx_kthread+0x10/0x10 ret_from_fork+0x34/0x50 ? __pfx_kthread+0x10/0x10 ret_from_fork_asm+0x1a/0x30 </TASK> kobject: kobject_add_internal failed for BAT1 with -EEXIST, don't try to register things with the same name in the same directory. There are also other scenarios in which analogous issues may occur. Address this by using a common lock in all of the code paths leading to updates of driver interfaces: ACPI Notify () handler, system resume callback and post-resume notification, device addition and removal. This new lock replaces sysfs_lock that has been used only in sysfs_remove_battery() which now is going to be always called under the new lock, so it doesn't need any internal locking any more. Fixes: 1066625 ("ACPI: battery: Install Notify() handler directly") Closes: https://lore.kernel.org/linux-acpi/[email protected]/ Reported-by: GuangFei Luo <[email protected]> Tested-by: GuangFei Luo <[email protected]> Cc: 6.6+ <[email protected]> # 6.6+ Signed-off-by: Rafael J. Wysocki <[email protected]> Signed-off-by: Sasha Levin <[email protected]> Signed-off-by: Greg Kroah-Hartman <[email protected]> (cherry picked from commit 237d6e1de0f29e084342a482a04a71f3e77dac5d) Signed-off-by: Jack Vogel <[email protected]>
1 parent 8bdb6c4 commit e9e16bb

File tree

1 file changed

+29
-14
lines changed

1 file changed

+29
-14
lines changed

drivers/acpi/battery.c

Lines changed: 29 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -92,7 +92,7 @@ enum {
9292

9393
struct acpi_battery {
9494
struct mutex lock;
95-
struct mutex sysfs_lock;
95+
struct mutex update_lock;
9696
struct power_supply *bat;
9797
struct power_supply_desc bat_desc;
9898
struct acpi_device *device;
@@ -903,15 +903,12 @@ static int sysfs_add_battery(struct acpi_battery *battery)
903903

904904
static void sysfs_remove_battery(struct acpi_battery *battery)
905905
{
906-
mutex_lock(&battery->sysfs_lock);
907-
if (!battery->bat) {
908-
mutex_unlock(&battery->sysfs_lock);
906+
if (!battery->bat)
909907
return;
910-
}
908+
911909
battery_hook_remove_battery(battery);
912910
power_supply_unregister(battery->bat);
913911
battery->bat = NULL;
914-
mutex_unlock(&battery->sysfs_lock);
915912
}
916913

917914
static void find_battery(const struct dmi_header *dm, void *private)
@@ -1071,6 +1068,9 @@ static void acpi_battery_notify(acpi_handle handle, u32 event, void *data)
10711068

10721069
if (!battery)
10731070
return;
1071+
1072+
guard(mutex)(&battery->update_lock);
1073+
10741074
old = battery->bat;
10751075
/*
10761076
* On Acer Aspire V5-573G notifications are sometimes triggered too
@@ -1093,21 +1093,22 @@ static void acpi_battery_notify(acpi_handle handle, u32 event, void *data)
10931093
}
10941094

10951095
static int battery_notify(struct notifier_block *nb,
1096-
unsigned long mode, void *_unused)
1096+
unsigned long mode, void *_unused)
10971097
{
10981098
struct acpi_battery *battery = container_of(nb, struct acpi_battery,
10991099
pm_nb);
1100-
int result;
11011100

1102-
switch (mode) {
1103-
case PM_POST_HIBERNATION:
1104-
case PM_POST_SUSPEND:
1101+
if (mode == PM_POST_SUSPEND || mode == PM_POST_HIBERNATION) {
1102+
guard(mutex)(&battery->update_lock);
1103+
11051104
if (!acpi_battery_present(battery))
11061105
return 0;
11071106

11081107
if (battery->bat) {
11091108
acpi_battery_refresh(battery);
11101109
} else {
1110+
int result;
1111+
11111112
result = acpi_battery_get_info(battery);
11121113
if (result)
11131114
return result;
@@ -1119,7 +1120,6 @@ static int battery_notify(struct notifier_block *nb,
11191120

11201121
acpi_battery_init_alarm(battery);
11211122
acpi_battery_get_state(battery);
1122-
break;
11231123
}
11241124

11251125
return 0;
@@ -1197,6 +1197,8 @@ static int acpi_battery_update_retry(struct acpi_battery *battery)
11971197
{
11981198
int retry, ret;
11991199

1200+
guard(mutex)(&battery->update_lock);
1201+
12001202
for (retry = 5; retry; retry--) {
12011203
ret = acpi_battery_update(battery, false);
12021204
if (!ret)
@@ -1207,6 +1209,13 @@ static int acpi_battery_update_retry(struct acpi_battery *battery)
12071209
return ret;
12081210
}
12091211

1212+
static void sysfs_battery_cleanup(struct acpi_battery *battery)
1213+
{
1214+
guard(mutex)(&battery->update_lock);
1215+
1216+
sysfs_remove_battery(battery);
1217+
}
1218+
12101219
static int acpi_battery_add(struct acpi_device *device)
12111220
{
12121221
int result = 0;
@@ -1229,7 +1238,7 @@ static int acpi_battery_add(struct acpi_device *device)
12291238
if (result)
12301239
return result;
12311240

1232-
result = devm_mutex_init(&device->dev, &battery->sysfs_lock);
1241+
result = devm_mutex_init(&device->dev, &battery->update_lock);
12331242
if (result)
12341243
return result;
12351244

@@ -1259,7 +1268,7 @@ static int acpi_battery_add(struct acpi_device *device)
12591268
device_init_wakeup(&device->dev, 0);
12601269
unregister_pm_notifier(&battery->pm_nb);
12611270
fail:
1262-
sysfs_remove_battery(battery);
1271+
sysfs_battery_cleanup(battery);
12631272

12641273
return result;
12651274
}
@@ -1278,6 +1287,9 @@ static void acpi_battery_remove(struct acpi_device *device)
12781287

12791288
device_init_wakeup(&device->dev, 0);
12801289
unregister_pm_notifier(&battery->pm_nb);
1290+
1291+
guard(mutex)(&battery->update_lock);
1292+
12811293
sysfs_remove_battery(battery);
12821294
}
12831295

@@ -1295,6 +1307,9 @@ static int acpi_battery_resume(struct device *dev)
12951307
return -EINVAL;
12961308

12971309
battery->update_time = 0;
1310+
1311+
guard(mutex)(&battery->update_lock);
1312+
12981313
acpi_battery_update(battery, true);
12991314
return 0;
13001315
}

0 commit comments

Comments
 (0)