[PATCH 6/6] hwmon: (pwm-fan) Use devm_thermal_of_cooling_device_register

Marek Szyprowski m.szyprowski at samsung.com
Tue May 21 01:21:20 AEST 2019


Dear All,

On 2019-04-18 21:58, Guenter Roeck wrote:
> Use devm_thermal_of_cooling_device_register() to register the cooling
> device. Also use devm_add_action_or_reset() to stop the fan on device
> removal, and to disable the pwm. Introduce a local 'dev' variable in
> the probe function to make the code easier to read.
>
> As a side effect, this fixes a bug seen if pwm_fan_of_get_cooling_data()
> returned an error. In that situation, the pwm was not disabled, and
> the fan was not stopped. Using devm functions also ensures that the
> pwm is disabled and that the fan is stopped only after the hwmon device
> has been unregistered.
>
> Cc: Lukasz Majewski <l.majewski at samsung.com>
> Signed-off-by: Guenter Roeck <linux at roeck-us.net>


I've noticed the following lockdep warning after this commit during CPU 
hotplug tests on TM2e board (ARM64 Exynos5433). It looks like a false 
positive, but it would be nice to annotate it somehow in the code to 
make lockdep happy:

--->8---

IRQ 8: no longer affine to CPU5
CPU5: shutdown
IRQ 9: no longer affine to CPU6
CPU6: shutdown

======================================================
WARNING: possible circular locking dependency detected
5.2.0-rc1+ #6093 Not tainted
------------------------------------------------------
cpuhp/7/43 is trying to acquire lock:
00000000d1a60be3 (thermal_list_lock){+.+.}, at: 
thermal_cooling_device_unregister+0x34/0x1c0

but task is already holding lock:
00000000a6a56c92 (&policy->rwsem){++++}, at: cpufreq_offline+0x68/0x228

which lock already depends on the new lock.


the existing dependency chain (in reverse order) is:

-> #3 (&policy->rwsem){++++}:
        down_write+0x48/0x98
        cpufreq_cpu_acquire+0x20/0x58
        cpufreq_update_policy+0x28/0xc0
        cpufreq_set_cur_state+0x44/0x70
        thermal_cdev_update+0x7c/0x240
        step_wise_throttle+0x4c/0x88
        handle_thermal_trip+0xc0/0x348
        thermal_zone_device_update.part.7+0x6c/0x250
        thermal_zone_device_update+0x28/0x38
        exynos_tmu_work+0x28/0x70
        process_one_work+0x298/0x6c0
        worker_thread+0x48/0x430
        kthread+0x100/0x130
        ret_from_fork+0x10/0x18

-> #2 (&cdev->lock){+.+.}:
        __mutex_lock+0x90/0x890
        mutex_lock_nested+0x1c/0x28
        thermal_zone_bind_cooling_device+0x2cc/0x3e0
        of_thermal_bind+0x9c/0xf8
        __thermal_cooling_device_register+0x1a4/0x388
        thermal_of_cooling_device_register+0xc/0x18
        __cpufreq_cooling_register+0x360/0x410
        of_cpufreq_cooling_register+0x84/0xf8
        cpufreq_online+0x414/0x720
        cpufreq_add_dev+0x78/0x88
        subsys_interface_register+0xa4/0xf8
        cpufreq_register_driver+0x140/0x1e0
        dt_cpufreq_probe+0xb0/0x130
        platform_drv_probe+0x50/0xa8
        really_probe+0x1b0/0x2a0
        driver_probe_device+0x58/0x100
        __device_attach_driver+0x90/0xc0
        bus_for_each_drv+0x70/0xc8
        __device_attach+0xdc/0x140
        device_initial_probe+0x10/0x18
        bus_probe_device+0x94/0xa0
        device_add+0x39c/0x5c8
        platform_device_add+0x110/0x248
        platform_device_register_full+0x134/0x178
        cpufreq_dt_platdev_init+0x114/0x14c
        do_one_initcall+0x84/0x430
        kernel_init_freeable+0x440/0x534
        kernel_init+0x10/0x108
        ret_from_fork+0x10/0x18

-> #1 (&tz->lock){+.+.}:
        __mutex_lock+0x90/0x890
        mutex_lock_nested+0x1c/0x28
        thermal_zone_bind_cooling_device+0x2b8/0x3e0
        of_thermal_bind+0x9c/0xf8
        __thermal_cooling_device_register+0x1a4/0x388
        thermal_of_cooling_device_register+0xc/0x18
        __cpufreq_cooling_register+0x360/0x410
        of_cpufreq_cooling_register+0x84/0xf8
        cpufreq_online+0x414/0x720
        cpufreq_add_dev+0x78/0x88
        subsys_interface_register+0xa4/0xf8
        cpufreq_register_driver+0x140/0x1e0
        dt_cpufreq_probe+0xb0/0x130
        platform_drv_probe+0x50/0xa8
        really_probe+0x1b0/0x2a0
        driver_probe_device+0x58/0x100
        __device_attach_driver+0x90/0xc0
        bus_for_each_drv+0x70/0xc8
        __device_attach+0xdc/0x140
        device_initial_probe+0x10/0x18
        bus_probe_device+0x94/0xa0
        device_add+0x39c/0x5c8
        platform_device_add+0x110/0x248
        platform_device_register_full+0x134/0x178
        cpufreq_dt_platdev_init+0x114/0x14c
        do_one_initcall+0x84/0x430
        kernel_init_freeable+0x440/0x534
        kernel_init+0x10/0x108
        ret_from_fork+0x10/0x18

-> #0 (thermal_list_lock){+.+.}:
        lock_acquire+0xdc/0x260
        __mutex_lock+0x90/0x890
        mutex_lock_nested+0x1c/0x28
        thermal_cooling_device_unregister+0x34/0x1c0
        cpufreq_cooling_unregister+0x78/0xd0
        cpufreq_offline+0x200/0x228
        cpuhp_cpufreq_offline+0xc/0x18
        cpuhp_invoke_callback+0xd0/0xcb0
        cpuhp_thread_fun+0x1e8/0x258
        smpboot_thread_fn+0x1b4/0x2d0
        kthread+0x100/0x130
        ret_from_fork+0x10/0x18

other info that might help us debug this:

Chain exists of:
   thermal_list_lock --> &cdev->lock --> &policy->rwsem

  Possible unsafe locking scenario:

        CPU0                    CPU1
        ----                    ----
   lock(&policy->rwsem);
                                lock(&cdev->lock);
                                lock(&policy->rwsem);
   lock(thermal_list_lock);

  *** DEADLOCK ***

3 locks held by cpuhp/7/43:
  #0: 00000000ae30cc2b (cpu_hotplug_lock.rw_sem){++++}, at: 
cpuhp_thread_fun+0x34/0x258
  #1: 00000000a0e2460a (cpuhp_state-down){+.+.}, at: 
cpuhp_thread_fun+0x178/0x258
  #2: 00000000a6a56c92 (&policy->rwsem){++++}, at: 
cpufreq_offline+0x68/0x228

stack backtrace:
CPU: 7 PID: 43 Comm: cpuhp/7 Not tainted 5.2.0-rc1+ #6093
Hardware name: Samsung TM2E board (DT)
Call trace:
  dump_backtrace+0x0/0x158
  show_stack+0x14/0x20
  dump_stack+0xc8/0x114
  print_circular_bug+0x1cc/0x2d8
  __lock_acquire+0x18f4/0x20f8
  lock_acquire+0xdc/0x260
  __mutex_lock+0x90/0x890
  mutex_lock_nested+0x1c/0x28
  thermal_cooling_device_unregister+0x34/0x1c0
  cpufreq_cooling_unregister+0x78/0xd0
  cpufreq_offline+0x200/0x228
  cpuhp_cpufreq_offline+0xc/0x18
  cpuhp_invoke_callback+0xd0/0xcb0
  cpuhp_thread_fun+0x1e8/0x258
  smpboot_thread_fn+0x1b4/0x2d0
  kthread+0x100/0x130
  ret_from_fork+0x10/0x18
IRQ 10: no longer affine to CPU7

--->8---

> ---
>   drivers/hwmon/pwm-fan.c | 73 ++++++++++++++++++++-----------------------------
>   1 file changed, 29 insertions(+), 44 deletions(-)
>
> diff --git a/drivers/hwmon/pwm-fan.c b/drivers/hwmon/pwm-fan.c
> index 167221c7628a..0243ba70107e 100644
> --- a/drivers/hwmon/pwm-fan.c
> +++ b/drivers/hwmon/pwm-fan.c
> @@ -207,33 +207,44 @@ static int pwm_fan_of_get_cooling_data(struct device *dev,
>   	return 0;
>   }
>   
> +static void pwm_fan_regulator_disable(void *data)
> +{
> +	regulator_disable(data);
> +}
> +
> +static void pwm_fan_pwm_disable(void *data)
> +{
> +	pwm_disable(data);
> +}
> +
>   static int pwm_fan_probe(struct platform_device *pdev)
>   {
>   	struct thermal_cooling_device *cdev;
> +	struct device *dev = &pdev->dev;
>   	struct pwm_fan_ctx *ctx;
>   	struct device *hwmon;
>   	int ret;
>   	struct pwm_state state = { };
>   
> -	ctx = devm_kzalloc(&pdev->dev, sizeof(*ctx), GFP_KERNEL);
> +	ctx = devm_kzalloc(dev, sizeof(*ctx), GFP_KERNEL);
>   	if (!ctx)
>   		return -ENOMEM;
>   
>   	mutex_init(&ctx->lock);
>   
> -	ctx->pwm = devm_of_pwm_get(&pdev->dev, pdev->dev.of_node, NULL);
> +	ctx->pwm = devm_of_pwm_get(dev, dev->of_node, NULL);
>   	if (IS_ERR(ctx->pwm)) {
>   		ret = PTR_ERR(ctx->pwm);
>   
>   		if (ret != -EPROBE_DEFER)
> -			dev_err(&pdev->dev, "Could not get PWM: %d\n", ret);
> +			dev_err(dev, "Could not get PWM: %d\n", ret);
>   
>   		return ret;
>   	}
>   
>   	platform_set_drvdata(pdev, ctx);
>   
> -	ctx->reg_en = devm_regulator_get_optional(&pdev->dev, "fan");
> +	ctx->reg_en = devm_regulator_get_optional(dev, "fan");
>   	if (IS_ERR(ctx->reg_en)) {
>   		if (PTR_ERR(ctx->reg_en) != -ENODEV)
>   			return PTR_ERR(ctx->reg_en);
> @@ -242,10 +253,11 @@ static int pwm_fan_probe(struct platform_device *pdev)
>   	} else {
>   		ret = regulator_enable(ctx->reg_en);
>   		if (ret) {
> -			dev_err(&pdev->dev,
> -				"Failed to enable fan supply: %d\n", ret);
> +			dev_err(dev, "Failed to enable fan supply: %d\n", ret);
>   			return ret;
>   		}
> +		devm_add_action_or_reset(dev, pwm_fan_regulator_disable,
> +					 ctx->reg_en);
>   	}
>   
>   	ctx->pwm_value = MAX_PWM;
> @@ -257,62 +269,36 @@ static int pwm_fan_probe(struct platform_device *pdev)
>   
>   	ret = pwm_apply_state(ctx->pwm, &state);
>   	if (ret) {
> -		dev_err(&pdev->dev, "Failed to configure PWM\n");
> -		goto err_reg_disable;
> +		dev_err(dev, "Failed to configure PWM\n");
> +		return ret;
>   	}
> +	devm_add_action_or_reset(dev, pwm_fan_pwm_disable, ctx->pwm);
>   
> -	hwmon = devm_hwmon_device_register_with_groups(&pdev->dev, "pwmfan",
> +	hwmon = devm_hwmon_device_register_with_groups(dev, "pwmfan",
>   						       ctx, pwm_fan_groups);
>   	if (IS_ERR(hwmon)) {
> -		dev_err(&pdev->dev, "Failed to register hwmon device\n");
> -		ret = PTR_ERR(hwmon);
> -		goto err_pwm_disable;
> +		dev_err(dev, "Failed to register hwmon device\n");
> +		return PTR_ERR(hwmon);
>   	}
>   
> -	ret = pwm_fan_of_get_cooling_data(&pdev->dev, ctx);
> +	ret = pwm_fan_of_get_cooling_data(dev, ctx);
>   	if (ret)
>   		return ret;
>   
>   	ctx->pwm_fan_state = ctx->pwm_fan_max_state;
>   	if (IS_ENABLED(CONFIG_THERMAL)) {
> -		cdev = thermal_of_cooling_device_register(pdev->dev.of_node,
> -							  "pwm-fan", ctx,
> -							  &pwm_fan_cooling_ops);
> +		cdev = devm_thermal_of_cooling_device_register(dev,
> +			dev->of_node, "pwm-fan", ctx, &pwm_fan_cooling_ops);
>   		if (IS_ERR(cdev)) {
> -			dev_err(&pdev->dev,
> +			dev_err(dev,
>   				"Failed to register pwm-fan as cooling device");
> -			ret = PTR_ERR(cdev);
> -			goto err_pwm_disable;
> +			return PTR_ERR(cdev);
>   		}
>   		ctx->cdev = cdev;
>   		thermal_cdev_update(cdev);
>   	}
>   
>   	return 0;
> -
> -err_pwm_disable:
> -	state.enabled = false;
> -	pwm_apply_state(ctx->pwm, &state);
> -
> -err_reg_disable:
> -	if (ctx->reg_en)
> -		regulator_disable(ctx->reg_en);
> -
> -	return ret;
> -}
> -
> -static int pwm_fan_remove(struct platform_device *pdev)
> -{
> -	struct pwm_fan_ctx *ctx = platform_get_drvdata(pdev);
> -
> -	thermal_cooling_device_unregister(ctx->cdev);
> -	if (ctx->pwm_value)
> -		pwm_disable(ctx->pwm);
> -
> -	if (ctx->reg_en)
> -		regulator_disable(ctx->reg_en);
> -
> -	return 0;
>   }
>   
>   #ifdef CONFIG_PM_SLEEP
> @@ -380,7 +366,6 @@ MODULE_DEVICE_TABLE(of, of_pwm_fan_match);
>   
>   static struct platform_driver pwm_fan_driver = {
>   	.probe		= pwm_fan_probe,
> -	.remove		= pwm_fan_remove,
>   	.driver	= {
>   		.name		= "pwm-fan",
>   		.pm		= &pwm_fan_pm,

Best regards
-- 
Marek Szyprowski, PhD
Samsung R&D Institute Poland



More information about the Linux-aspeed mailing list