[PATCH v1 1/1] hwmon: (occ) Fix sysfs device unreg deadlock
From: Ninad Palsule
Date: Thu Jun 25 2026 - 12:47:29 EST
Release the driver lock before unregistering the hwmon device to prevent
a deadlock. The device_unregister() call can block waiting for sysfs
operations to complete, but those operations may be blocked waiting for
the same lock held during unregistration.
The deadlock occurs when:
1. Thread A holds the driver lock and calls device_unregister()
2. device_unregister() waits for sysfs operations to drain
3. Thread B is blocked in a sysfs read operation waiting for the driver
lock
4. Neither thread can proceed, causing a hung task
This was observed during power system remote restart operations when
concurrent sysfs accesses occurred during OCC active state transitions.
Kernel log excerpt showing the deadlock:
INFO: task openpower-occ-c:1430 blocked for more than 122 seconds.
Call trace:
schedule+0x28/0xfc
kernfs_drain+0xc8/0x174
__kernfs_remove.part.0+0x138/0x21c
kernfs_remove_by_name_ns+0x7c/0xcc
[...]
device_unregister+0x1c/0x5c
hwmon_device_unregister+0x58/0xb0
occ_active+0x6c/0xe74
occ_active_store+0x54/0x80
Reproducer:
Run these two scripts in parallel:
Script 1 (continuous sysfs read):
while true; do
cat /sys/class/hwmon/hwmon14/power15_cap_user
done
Script 2 (OCC active state toggle):
while true; do
echo 0 > /sys/bus/platform/drivers/occ-hwmon/occ-hwmon.1/occ_active
echo 1 > /sys/bus/platform/drivers/occ-hwmon/occ-hwmon.1/occ_active
sleep 0.2
done
Signed-off-by: Ninad Palsule <ninad@xxxxxxxxxxxxx>
---
drivers/hwmon/occ/common.c | 11 +++++++++--
1 file changed, 9 insertions(+), 2 deletions(-)
diff --git a/drivers/hwmon/occ/common.c b/drivers/hwmon/occ/common.c
index 42cc6068bb08..a5641dbbba2f 100644
--- a/drivers/hwmon/occ/common.c
+++ b/drivers/hwmon/occ/common.c
@@ -1149,15 +1149,22 @@ int occ_active(struct occ *occ, bool active)
goto unlock;
}
} else {
+ struct device *hwmon_dev = occ->hwmon;
+
if (!occ->active) {
rc = -EALREADY;
goto unlock;
}
- if (occ->hwmon)
- hwmon_device_unregister(occ->hwmon);
occ->active = false;
occ->hwmon = NULL;
+
+ mutex_unlock(&occ->lock);
+
+ if (hwmon_dev)
+ hwmon_device_unregister(hwmon_dev);
+
+ return 0;
}
unlock:
--
2.51.0