platform/x86: hp-wmi: add locking for concurrent hwmon access

hp_wmi_hwmon_priv.mode and .pwm are written by hp_wmi_hwmon_write() in
sysfs context and read by hp_wmi_hwmon_keep_alive_handler() in a
workqueue. A concurrent write and keep-alive expiry can observe an
inconsistent mode/pwm pair (e.g. mode=MANUAL with a stale pwm).

Add a mutex to hp_wmi_hwmon_priv protecting mode and pwm. Hold it in
hp_wmi_hwmon_write() across the field update and apply call, and in
hp_wmi_hwmon_keep_alive_handler() before calling apply.

In hp_wmi_hwmon_read(), only the pwm_enable path reads priv->mode; use
scoped_guard() there to avoid holding the lock across unrelated WMI
calls.

Fixes: c203c59fb5 ("platform/x86: hp-wmi: implement fan keep-alive")
Suggested-by: Ilpo Järvinen <ilpo.jarvinen@linux.intel.com>
Signed-off-by: Emre Cecanpunar <emreleno@gmail.com>
Link: https://patch.msgid.link/20260407142515.20683-6-emreleno@gmail.com
Reviewed-by: Ilpo Järvinen <ilpo.jarvinen@linux.intel.com>
Signed-off-by: Ilpo Järvinen <ilpo.jarvinen@linux.intel.com>
This commit is contained in:
Emre Cecanpunar 2026-04-07 17:25:14 +03:00 committed by Ilpo Järvinen
parent cb4daa450f
commit 5969c55e21
No known key found for this signature in database
GPG Key ID: 59AC4F6153E5CE31

View File

@ -453,6 +453,7 @@ enum pwm_modes {
};
struct hp_wmi_hwmon_priv {
struct mutex lock; /* protects mode, pwm */
u8 min_rpm;
u8 max_rpm;
int gpu_delta;
@ -2422,6 +2423,7 @@ static int hp_wmi_hwmon_read(struct device *dev, enum hwmon_sensor_types type,
{
struct hp_wmi_hwmon_priv *priv;
int rpm, ret;
u8 mode;
priv = dev_get_drvdata(dev);
switch (type) {
@ -2445,11 +2447,13 @@ static int hp_wmi_hwmon_read(struct device *dev, enum hwmon_sensor_types type,
*val = rpm_to_pwm(rpm / 100, priv);
return 0;
}
switch (priv->mode) {
scoped_guard(mutex, &priv->lock)
mode = priv->mode;
switch (mode) {
case PWM_MODE_MAX:
case PWM_MODE_MANUAL:
case PWM_MODE_AUTO:
*val = priv->mode;
*val = mode;
return 0;
default:
/* shouldn't happen */
@ -2467,6 +2471,7 @@ static int hp_wmi_hwmon_write(struct device *dev, enum hwmon_sensor_types type,
int rpm;
priv = dev_get_drvdata(dev);
guard(mutex)(&priv->lock);
switch (type) {
case hwmon_pwm:
if (attr == hwmon_pwm_input) {
@ -2535,6 +2540,8 @@ static void hp_wmi_hwmon_keep_alive_handler(struct work_struct *work)
dwork = to_delayed_work(work);
priv = container_of(dwork, struct hp_wmi_hwmon_priv, keep_alive_dwork);
guard(mutex)(&priv->lock);
/*
* Re-apply the current hwmon context settings.
* NOTE: hp_wmi_apply_fan_settings will handle the re-scheduling.
@ -2591,6 +2598,10 @@ static int hp_wmi_hwmon_init(void)
if (!priv)
return -ENOMEM;
ret = devm_mutex_init(dev, &priv->lock);
if (ret)
return ret;
ret = hp_wmi_setup_fan_settings(priv);
if (ret)
return ret;