Re: [PATCH v3 5/5] platform/x86: hp-wmi: add locking for concurrent hwmon access

From: Ilpo Järvinen

Date: Tue Apr 07 2026 - 04:08:09 EST


On Sun, 5 Apr 2026, Emre Cecanpunar wrote:

> 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")
> Signed-off-by: Emre Cecanpunar <emreleno@xxxxxxxxx>
> ---
> drivers/platform/x86/hp/hp-wmi.c | 13 +++++++++++--
> 1 file changed, 11 insertions(+), 2 deletions(-)
>
> diff --git a/drivers/platform/x86/hp/hp-wmi.c b/drivers/platform/x86/hp/hp-wmi.c
> index db72ad9da0a5..1096fb46cbcd 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.
> @@ -2592,6 +2599,8 @@ static int hp_wmi_hwmon_init(void)
> if (!priv)
> return -ENOMEM;
>
> + mutex_init(&priv->lock);

Please use devm_mutex_init() so you don't need to deinit it manually with
mutex_destroy(). While changing that, don't forget that devm_*() can fail
so remember to handle the error.

--
i.