diff options
| author | Sumit Gupta <sumitg@nvidia.com> | 2021-03-24 10:11:56 -0400 |
|---|---|---|
| committer | mobile promotions <svcmobile_promotions@nvidia.com> | 2021-05-25 10:24:23 -0400 |
| commit | 2c40a6bc48f6701fe44cb7edadca26f6a40892da (patch) | |
| tree | fffbb849a9053acfb87b53d6e260fd45d66a26fc | |
| parent | 6fcc5021a8f8eaef1a2a8e2c342728d1a6ff35d0 (diff) | |
pwm: fan: fix deadlock due to incorrect locking
Bug 3227296
Bug 200695596
Bug 200728417
Signed-off-by: Sumit Gupta <sumitg@nvidia.com>
Change-Id: Ib64915556d285bd798e95da81a261eb2c16b4dab
Reviewed-on: https://git-master.nvidia.com/r/c/linux-nvidia/+/2503884
Reviewed-by: svcacv <svcacv@nvidia.com>
Reviewed-by: svc-mobile-coverity <svc-mobile-coverity@nvidia.com>
Reviewed-by: Bibek Basu <bbasu@nvidia.com>
Reviewed-by: mobile promotions <svcmobile_promotions@nvidia.com>
GVS: Gerrit_Virtual_Submit
Tested-by: Bibek Basu <bbasu@nvidia.com>
Tested-by: mobile promotions <svcmobile_promotions@nvidia.com>
| -rw-r--r-- | drivers/thermal/pwm_fan.c | 67 | ||||
| -rw-r--r-- | include/linux/therm_est.h | 4 |
2 files changed, 47 insertions, 24 deletions
diff --git a/drivers/thermal/pwm_fan.c b/drivers/thermal/pwm_fan.c index 35a09a108..2deb7abf5 100644 --- a/drivers/thermal/pwm_fan.c +++ b/drivers/thermal/pwm_fan.c | |||
| @@ -114,8 +114,7 @@ static void fan_validate_target_rpm(struct fan_dev_data *fan_data) | |||
| 114 | fan_data->next_target_pwm); | 114 | fan_data->next_target_pwm); |
| 115 | 115 | ||
| 116 | fan_data->fan_rpm_in_limits = false; | 116 | fan_data->fan_rpm_in_limits = false; |
| 117 | cancel_delayed_work(&fan_data->fan_ramp_rpm_work); | 117 | cancel_delayed_work_sync(&fan_data->fan_ramp_rpm_work); |
| 118 | mutex_unlock(&fan_data->fan_state_lock); | ||
| 119 | queue_delayed_work(fan_data->workqueue, | 118 | queue_delayed_work(fan_data->workqueue, |
| 120 | &fan_data->fan_ramp_rpm_work, | 119 | &fan_data->fan_ramp_rpm_work, |
| 121 | msecs_to_jiffies(fan_data->fan_ramp_time_ms)); | 120 | msecs_to_jiffies(fan_data->fan_ramp_time_ms)); |
| @@ -126,12 +125,8 @@ static void fan_update_target_pwm(struct fan_dev_data *fan_data, int val) | |||
| 126 | if (fan_data) { | 125 | if (fan_data) { |
| 127 | fan_data->next_target_pwm = min(val, fan_data->fan_cap_pwm); | 126 | fan_data->next_target_pwm = min(val, fan_data->fan_cap_pwm); |
| 128 | 127 | ||
| 129 | /* If a new pwm update request, reset the lock sequence */ | ||
| 130 | if (mutex_is_locked(&fan_data->pwm_set)) | ||
| 131 | mutex_unlock(&fan_data->pwm_set); | ||
| 132 | mutex_lock(&fan_data->pwm_set); | ||
| 133 | if (fan_data->next_target_pwm != fan_data->fan_cur_pwm) { | 128 | if (fan_data->next_target_pwm != fan_data->fan_cur_pwm) { |
| 134 | cancel_delayed_work(&fan_data->fan_ramp_pwm_work); | 129 | cancel_delayed_work_sync(&fan_data->fan_ramp_pwm_work); |
| 135 | queue_delayed_work(fan_data->workqueue, | 130 | queue_delayed_work(fan_data->workqueue, |
| 136 | &fan_data->fan_ramp_pwm_work, | 131 | &fan_data->fan_ramp_pwm_work, |
| 137 | msecs_to_jiffies(fan_data->step_time)); | 132 | msecs_to_jiffies(fan_data->step_time)); |
| @@ -659,16 +654,19 @@ static void fan_ramping_rpm_work_func(struct work_struct *work) | |||
| 659 | struct fan_dev_data, | 654 | struct fan_dev_data, |
| 660 | fan_ramp_rpm_work); | 655 | fan_ramp_rpm_work); |
| 661 | 656 | ||
| 662 | /* | 657 | time_off = fan_data->rpm_invalid_retry_delay; |
| 663 | * If mutex is locked, preempt. Only after pwm work function is done | 658 | if (!wait_for_completion_timeout(&fan_data->pwm_set, |
| 664 | * with its execution, rpm work function should start. | 659 | msecs_to_jiffies(10000))) { |
| 665 | */ | 660 | dev_info(fan_data->dev, "pwm_set completion timedout\n"); |
| 666 | if (mutex_is_locked((&fan_data->pwm_set))) { | 661 | return; |
| 667 | time_off = fan_data->fan_ramp_time_ms; | ||
| 668 | goto reschedule; | ||
| 669 | } | 662 | } |
| 670 | 663 | ||
| 671 | mutex_lock(&fan_data->fan_state_lock); | 664 | if (!mutex_trylock(&fan_data->fan_state_lock)) { |
| 665 | queue_delayed_work(fan_data->workqueue, | ||
| 666 | &fan_data->fan_ramp_rpm_work, | ||
| 667 | msecs_to_jiffies(fan_data->step_time + time_off)); | ||
| 668 | return; | ||
| 669 | } | ||
| 672 | 670 | ||
| 673 | if (!fan_data->fan_temp_control_flag) | 671 | if (!fan_data->fan_temp_control_flag) |
| 674 | goto unlock_mutex; | 672 | goto unlock_mutex; |
| @@ -788,13 +786,30 @@ static void fan_ramping_pwm_work_func(struct work_struct *work) | |||
| 788 | { | 786 | { |
| 789 | int rru, rrd, err; | 787 | int rru, rrd, err; |
| 790 | int cur_pwm, next_pwm; | 788 | int cur_pwm, next_pwm; |
| 789 | int time_off; | ||
| 791 | struct delayed_work *dwork = container_of(work, struct delayed_work, | 790 | struct delayed_work *dwork = container_of(work, struct delayed_work, |
| 792 | work); | 791 | work); |
| 793 | struct fan_dev_data *fan_data = container_of(dwork, struct | 792 | struct fan_dev_data *fan_data = container_of(dwork, struct |
| 794 | fan_dev_data, | 793 | fan_dev_data, |
| 795 | fan_ramp_pwm_work); | 794 | fan_ramp_pwm_work); |
| 796 | 795 | ||
| 797 | mutex_lock(&fan_data->fan_state_lock); | 796 | time_off = fan_data->rpm_invalid_retry_delay; |
| 797 | if (!mutex_trylock(&fan_data->fan_state_lock)) { | ||
| 798 | if (fan_data->pwm_tach_dev) | ||
| 799 | cancel_delayed_work_sync(&fan_data->fan_ramp_rpm_work); | ||
| 800 | |||
| 801 | queue_delayed_work(fan_data->workqueue, | ||
| 802 | &(fan_data->fan_ramp_pwm_work), | ||
| 803 | msecs_to_jiffies(fan_data->step_time)); | ||
| 804 | |||
| 805 | if (fan_data->pwm_tach_dev) | ||
| 806 | queue_delayed_work(fan_data->workqueue, | ||
| 807 | &fan_data->fan_ramp_rpm_work, | ||
| 808 | msecs_to_jiffies(fan_data->step_time + time_off)); | ||
| 809 | return; | ||
| 810 | } | ||
| 811 | |||
| 812 | reinit_completion(&fan_data->pwm_set); | ||
| 798 | 813 | ||
| 799 | cur_pwm = fan_data->fan_cur_pwm; | 814 | cur_pwm = fan_data->fan_cur_pwm; |
| 800 | rru = fan_get_pwm_rru(cur_pwm, fan_data); | 815 | rru = fan_get_pwm_rru(cur_pwm, fan_data); |
| @@ -841,12 +856,21 @@ static void fan_ramping_pwm_work_func(struct work_struct *work) | |||
| 841 | 856 | ||
| 842 | set_pwm_duty_cycle(next_pwm, fan_data); | 857 | set_pwm_duty_cycle(next_pwm, fan_data); |
| 843 | fan_data->fan_cur_pwm = next_pwm; | 858 | fan_data->fan_cur_pwm = next_pwm; |
| 844 | if (fan_data->next_target_pwm != next_pwm) | 859 | if (fan_data->next_target_pwm != next_pwm) { |
| 860 | if (fan_data->pwm_tach_dev) | ||
| 861 | cancel_delayed_work_sync(&fan_data->fan_ramp_rpm_work); | ||
| 862 | |||
| 845 | queue_delayed_work(fan_data->workqueue, | 863 | queue_delayed_work(fan_data->workqueue, |
| 846 | &(fan_data->fan_ramp_pwm_work), | 864 | &(fan_data->fan_ramp_pwm_work), |
| 847 | msecs_to_jiffies(fan_data->step_time)); | 865 | msecs_to_jiffies(fan_data->step_time)); |
| 866 | |||
| 867 | if (fan_data->pwm_tach_dev) | ||
| 868 | queue_delayed_work(fan_data->workqueue, | ||
| 869 | &fan_data->fan_ramp_rpm_work, | ||
| 870 | msecs_to_jiffies(fan_data->step_time + time_off)); | ||
| 871 | } | ||
| 848 | else | 872 | else |
| 849 | mutex_unlock(&fan_data->pwm_set); | 873 | complete(&fan_data->pwm_set); |
| 850 | 874 | ||
| 851 | mutex_unlock(&fan_data->fan_state_lock); | 875 | mutex_unlock(&fan_data->fan_state_lock); |
| 852 | } | 876 | } |
| @@ -1529,7 +1553,7 @@ static int pwm_fan_probe(struct platform_device *pdev) | |||
| 1529 | } | 1553 | } |
| 1530 | 1554 | ||
| 1531 | mutex_init(&fan_data->fan_state_lock); | 1555 | mutex_init(&fan_data->fan_state_lock); |
| 1532 | mutex_init(&fan_data->pwm_set); | 1556 | init_completion(&fan_data->pwm_set); |
| 1533 | spin_lock_init(&fan_data->irq_lock); | 1557 | spin_lock_init(&fan_data->irq_lock); |
| 1534 | 1558 | ||
| 1535 | fan_data->workqueue = alloc_workqueue(dev_name(&pdev->dev), | 1559 | fan_data->workqueue = alloc_workqueue(dev_name(&pdev->dev), |
| @@ -1873,10 +1897,10 @@ static int pwm_fan_resume(struct platform_device *pdev) | |||
| 1873 | { | 1897 | { |
| 1874 | struct fan_dev_data *fan_data = platform_get_drvdata(pdev); | 1898 | struct fan_dev_data *fan_data = platform_get_drvdata(pdev); |
| 1875 | 1899 | ||
| 1876 | mutex_lock(&fan_data->fan_state_lock); | ||
| 1877 | |||
| 1878 | gpio_free(fan_data->pwm_gpio); | 1900 | gpio_free(fan_data->pwm_gpio); |
| 1879 | 1901 | ||
| 1902 | reinit_completion(&fan_data->pwm_set); | ||
| 1903 | |||
| 1880 | queue_delayed_work(fan_data->workqueue, | 1904 | queue_delayed_work(fan_data->workqueue, |
| 1881 | &fan_data->fan_ramp_pwm_work, | 1905 | &fan_data->fan_ramp_pwm_work, |
| 1882 | msecs_to_jiffies(fan_data->step_time)); | 1906 | msecs_to_jiffies(fan_data->step_time)); |
| @@ -1886,7 +1910,6 @@ static int pwm_fan_resume(struct platform_device *pdev) | |||
| 1886 | msecs_to_jiffies(fan_data->step_time)); | 1910 | msecs_to_jiffies(fan_data->step_time)); |
| 1887 | 1911 | ||
| 1888 | fan_data->fan_temp_control_flag = 1; | 1912 | fan_data->fan_temp_control_flag = 1; |
| 1889 | mutex_unlock(&fan_data->fan_state_lock); | ||
| 1890 | return 0; | 1913 | return 0; |
| 1891 | } | 1914 | } |
| 1892 | #endif | 1915 | #endif |
diff --git a/include/linux/therm_est.h b/include/linux/therm_est.h index 85284a5fe..5c5d916ce 100644 --- a/include/linux/therm_est.h +++ b/include/linux/therm_est.h | |||
| @@ -1,7 +1,7 @@ | |||
| 1 | /* | 1 | /* |
| 2 | * include/linux/therm_est.h | 2 | * include/linux/therm_est.h |
| 3 | * | 3 | * |
| 4 | * Copyright (c) 2010-2020, NVIDIA CORPORATION. All rights reserved. | 4 | * Copyright (c) 2010-2021, NVIDIA CORPORATION. All rights reserved. |
| 5 | * | 5 | * |
| 6 | * This software is licensed under the terms of the GNU General Public | 6 | * This software is licensed under the terms of the GNU General Public |
| 7 | * License version 2, as published by the Free Software Foundation, and | 7 | * License version 2, as published by the Free Software Foundation, and |
| @@ -187,7 +187,7 @@ struct fan_dev_data { | |||
| 187 | struct delayed_work fan_ramp_pwm_work; | 187 | struct delayed_work fan_ramp_pwm_work; |
| 188 | struct delayed_work fan_ramp_rpm_work; | 188 | struct delayed_work fan_ramp_rpm_work; |
| 189 | struct device *pwm_tach_dev; | 189 | struct device *pwm_tach_dev; |
| 190 | struct mutex pwm_set; | 190 | struct completion pwm_set; |
| 191 | bool fan_rpm_in_limits; | 191 | bool fan_rpm_in_limits; |
| 192 | int rpm_valid_retry_delay; | 192 | int rpm_valid_retry_delay; |
| 193 | int rpm_invalid_retry_delay; | 193 | int rpm_invalid_retry_delay; |
