]> git.ipfire.org Git - thirdparty/linux.git/commitdiff
hwmon: (gpd-fan): fix race condition between device removal and sysfs access
authorPei Xiao <xiaopei01@kylinos.cn>
Wed, 10 Jun 2026 01:49:12 +0000 (09:49 +0800)
committerGuenter Roeck <linux@roeck-us.net>
Wed, 10 Jun 2026 13:14:57 +0000 (06:14 -0700)
Replace the manual gpd_fan_remove() callback with a devres-managed
action using devm_add_action_or_reset(). The original remove hook
resets the fan to AUTOMATIC mode, but the hwmon sysfs interface
(registered with devm_hwmon_device_register_with_info()) remains
active until after the remove callback completes. This creates a
race window where a concurrent userspace sysfs access can interleave
with the EC I/O sequence, potentially corrupting EC registers.

Using devm_add_action_or_reset() registers the reset function as a
devres action. Due to the LIFO release order of devres, the hwmon
device is unregistered (sysfs removed) before the reset action
executes, eliminating the race condition.

Fixes: 0ab88e239439 ("hwmon: add GPD devices sensor driver")
Signed-off-by: Pei Xiao <xiaopei01@kylinos.cn>
Link: https://lore.kernel.org/r/4400828422cf3a88adad4db224d9efccdb1049d2.1781055639.git.xiaopei01@kylinos.cn
Signed-off-by: Guenter Roeck <linux@roeck-us.net>
drivers/hwmon/gpd-fan.c

index 1b57a5add934fff23500403db1b446353c0e3fdb..d1993cd645cbc3520fbf5c6bea81c71b53dff0d2 100644 (file)
@@ -609,6 +609,16 @@ static void gpd_init_ec(struct gpd_fan_data *data)
                gpd_win4_init_ec(data);
 }
 
+static void gpd_fan_reset_hardware(void *pdata)
+{
+       struct gpd_fan_data *data = pdata;
+
+       if (data) {
+               data->pwm_enable = AUTOMATIC;
+               gpd_set_pwm_enable(data, AUTOMATIC);
+       }
+}
+
 static int gpd_fan_probe(struct platform_device *pdev)
 {
        struct device *dev = &pdev->dev;
@@ -617,6 +627,7 @@ static int gpd_fan_probe(struct platform_device *pdev)
        struct device *hwdev;
        struct gpd_fan_data *data;
        const struct gpd_fan_drvdata *match;
+       int ret;
 
        res = platform_get_resource(pdev, IORESOURCE_IO, 0);
        if (!res)
@@ -644,6 +655,11 @@ static int gpd_fan_probe(struct platform_device *pdev)
        dev_set_drvdata(dev, data);
 
        gpd_init_ec(data);
+
+       ret = devm_add_action_or_reset(dev, gpd_fan_reset_hardware, data);
+       if (ret)
+               return ret;
+
        hwdev = devm_hwmon_device_register_with_info(dev,
                                                     DRIVER_NAME,
                                                     data,
@@ -655,19 +671,8 @@ static int gpd_fan_probe(struct platform_device *pdev)
        return 0;
 }
 
-static void gpd_fan_remove(struct platform_device *pdev)
-{
-       struct gpd_fan_data *data = dev_get_drvdata(&pdev->dev);
-
-       if (data) {
-               data->pwm_enable = AUTOMATIC;
-               gpd_set_pwm_enable(data, AUTOMATIC);
-       }
-}
-
 static struct platform_driver gpd_fan_driver = {
        .probe = gpd_fan_probe,
-       .remove = gpd_fan_remove,
        .driver = {
                .name = KBUILD_MODNAME,
        },