]> git.ipfire.org Git - thirdparty/kernel/linux.git/commitdiff
platform/x86: hp-wmi: add locking for concurrent hwmon access
authorEmre Cecanpunar <emreleno@gmail.com>
Tue, 7 Apr 2026 14:25:14 +0000 (17:25 +0300)
committerIlpo Järvinen <ilpo.jarvinen@linux.intel.com>
Thu, 9 Apr 2026 11:46:36 +0000 (14:46 +0300)
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 <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>
drivers/platform/x86/hp/hp-wmi.c

index 4f49861d3fbe25328e4666ac926bd1844c1b202e..470c6e48f8e98996163566a91ef98f24749c2df1 100644 (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;