Skip to content

Commit fbf6074

Browse files
rafaeljwgregkh
authored andcommitted
ACPI: battery: Add synchronization between interface updates
commit 399dbca upstream. 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: Greg Kroah-Hartman <[email protected]>
1 parent 70aa2ff commit fbf6074

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;
@@ -904,15 +904,12 @@ static int sysfs_add_battery(struct acpi_battery *battery)
904904

905905
static void sysfs_remove_battery(struct acpi_battery *battery)
906906
{
907-
mutex_lock(&battery->sysfs_lock);
908-
if (!battery->bat) {
909-
mutex_unlock(&battery->sysfs_lock);
907+
if (!battery->bat)
910908
return;
911-
}
909+
912910
battery_hook_remove_battery(battery);
913911
power_supply_unregister(battery->bat);
914912
battery->bat = NULL;
915-
mutex_unlock(&battery->sysfs_lock);
916913
}
917914

918915
static void find_battery(const struct dmi_header *dm, void *private)
@@ -1072,6 +1069,9 @@ static void acpi_battery_notify(acpi_handle handle, u32 event, void *data)
10721069

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

10961096
static int battery_notify(struct notifier_block *nb,
1097-
unsigned long mode, void *_unused)
1097+
unsigned long mode, void *_unused)
10981098
{
10991099
struct acpi_battery *battery = container_of(nb, struct acpi_battery,
11001100
pm_nb);
1101-
int result;
11021101

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

11091108
if (battery->bat) {
11101109
acpi_battery_refresh(battery);
11111110
} else {
1111+
int result;
1112+
11121113
result = acpi_battery_get_info(battery);
11131114
if (result)
11141115
return result;
@@ -1120,7 +1121,6 @@ static int battery_notify(struct notifier_block *nb,
11201121

11211122
acpi_battery_init_alarm(battery);
11221123
acpi_battery_get_state(battery);
1123-
break;
11241124
}
11251125

11261126
return 0;
@@ -1198,6 +1198,8 @@ static int acpi_battery_update_retry(struct acpi_battery *battery)
11981198
{
11991199
int retry, ret;
12001200

1201+
guard(mutex)(&battery->update_lock);
1202+
12011203
for (retry = 5; retry; retry--) {
12021204
ret = acpi_battery_update(battery, false);
12031205
if (!ret)
@@ -1208,6 +1210,13 @@ static int acpi_battery_update_retry(struct acpi_battery *battery)
12081210
return ret;
12091211
}
12101212

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

1233-
result = devm_mutex_init(&device->dev, &battery->sysfs_lock);
1242+
result = devm_mutex_init(&device->dev, &battery->update_lock);
12341243
if (result)
12351244
return result;
12361245

@@ -1262,7 +1271,7 @@ static int acpi_battery_add(struct acpi_device *device)
12621271
device_init_wakeup(&device->dev, 0);
12631272
unregister_pm_notifier(&battery->pm_nb);
12641273
fail:
1265-
sysfs_remove_battery(battery);
1274+
sysfs_battery_cleanup(battery);
12661275

12671276
return result;
12681277
}
@@ -1281,6 +1290,9 @@ static void acpi_battery_remove(struct acpi_device *device)
12811290

12821291
device_init_wakeup(&device->dev, 0);
12831292
unregister_pm_notifier(&battery->pm_nb);
1293+
1294+
guard(mutex)(&battery->update_lock);
1295+
12841296
sysfs_remove_battery(battery);
12851297
}
12861298

@@ -1297,6 +1309,9 @@ static int acpi_battery_resume(struct device *dev)
12971309
return -EINVAL;
12981310

12991311
battery->update_time = 0;
1312+
1313+
guard(mutex)(&battery->update_lock);
1314+
13001315
acpi_battery_update(battery, true);
13011316
return 0;
13021317
}

0 commit comments

Comments
 (0)