From 5969c55e2145368254194edbe0e64880314be69f Mon Sep 17 00:00:00 2001 From: Emre Cecanpunar Date: Tue, 7 Apr 2026 17:25:14 +0300 Subject: [PATCH] platform/x86: hp-wmi: add locking for concurrent hwmon access MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 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: c203c59fb5de ("platform/x86: hp-wmi: implement fan keep-alive") Suggested-by: Ilpo Järvinen Signed-off-by: Emre Cecanpunar Link: https://patch.msgid.link/20260407142515.20683-6-emreleno@gmail.com Reviewed-by: Ilpo Järvinen Signed-off-by: Ilpo Järvinen --- drivers/platform/x86/hp/hp-wmi.c | 15 +++++++++++++-- 1 file changed, 13 insertions(+), 2 deletions(-) diff --git a/drivers/platform/x86/hp/hp-wmi.c b/drivers/platform/x86/hp/hp-wmi.c index 4f49861d3fbe..470c6e48f8e9 100644 --- a/drivers/platform/x86/hp/hp-wmi.c +++ b/drivers/platform/x86/hp/hp-wmi.c @@ -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;