drm/amd/pm: fix the deadlock observed on performance_level setting
authorEvan Quan <evan.quan@amd.com>
Tue, 25 Jan 2022 08:09:00 +0000 (16:09 +0800)
committerAlex Deucher <alexander.deucher@amd.com>
Thu, 27 Jan 2022 20:48:22 +0000 (15:48 -0500)
The sub-routine(amdgpu_gfx_off_ctrl) tried to obtain the lock
adev->pm.mutex which was actually hold by amdgpu_dpm_force_performance_level.
A deadlock happened then.

Signed-off-by: Evan Quan <evan.quan@amd.com>
Reviewed-by: Alex Deucher <alexander.deucher@amd.com>
Signed-off-by: Alex Deucher <alexander.deucher@amd.com>
drivers/gpu/drm/amd/pm/amdgpu_dpm.c

index 5fc33893a68c2be81073a32adf4f30823db451b2..ef574c96b41ca14edda6483fb2e318bb83fa1020 100644 (file)
@@ -692,25 +692,16 @@ void amdgpu_dpm_set_power_state(struct amdgpu_device *adev,
                amdgpu_dpm_compute_clocks(adev);
 }
 
-static enum amd_dpm_forced_level amdgpu_dpm_get_performance_level_locked(struct amdgpu_device *adev)
+enum amd_dpm_forced_level amdgpu_dpm_get_performance_level(struct amdgpu_device *adev)
 {
        const struct amd_pm_funcs *pp_funcs = adev->powerplay.pp_funcs;
        enum amd_dpm_forced_level level;
 
+       mutex_lock(&adev->pm.mutex);
        if (pp_funcs->get_performance_level)
                level = pp_funcs->get_performance_level(adev->powerplay.pp_handle);
        else
                level = adev->pm.dpm.forced_level;
-
-       return level;
-}
-
-enum amd_dpm_forced_level amdgpu_dpm_get_performance_level(struct amdgpu_device *adev)
-{
-       enum amd_dpm_forced_level level;
-
-       mutex_lock(&adev->pm.mutex);
-       level = amdgpu_dpm_get_performance_level_locked(adev);
        mutex_unlock(&adev->pm.mutex);
 
        return level;
@@ -725,23 +716,16 @@ int amdgpu_dpm_force_performance_level(struct amdgpu_device *adev,
                                        AMD_DPM_FORCED_LEVEL_PROFILE_MIN_SCLK |
                                        AMD_DPM_FORCED_LEVEL_PROFILE_MIN_MCLK |
                                        AMD_DPM_FORCED_LEVEL_PROFILE_PEAK;
-       int ret = 0;
 
        if (!pp_funcs->force_performance_level)
                return 0;
 
-       mutex_lock(&adev->pm.mutex);
-
-       if (adev->pm.dpm.thermal_active) {
-               ret = -EINVAL;
-               goto out;
-       }
+       if (adev->pm.dpm.thermal_active)
+               return -EINVAL;
 
-       current_level = amdgpu_dpm_get_performance_level_locked(adev);
-       if (current_level == level) {
-               ret = 0;
-               goto out;
-       }
+       current_level = amdgpu_dpm_get_performance_level(adev);
+       if (current_level == level)
+               return 0;
 
        if (adev->asic_type == CHIP_RAVEN) {
                if (!(adev->apu_flags & AMD_APU_IS_RAVEN2)) {
@@ -755,10 +739,8 @@ int amdgpu_dpm_force_performance_level(struct amdgpu_device *adev,
        }
 
        if (!(current_level & profile_mode_mask) &&
-           (level == AMD_DPM_FORCED_LEVEL_PROFILE_EXIT)) {
-               ret = -EINVAL;
-               goto out;
-       }
+           (level == AMD_DPM_FORCED_LEVEL_PROFILE_EXIT))
+               return -EINVAL;
 
        if (!(current_level & profile_mode_mask) &&
              (level & profile_mode_mask)) {
@@ -780,17 +762,19 @@ int amdgpu_dpm_force_performance_level(struct amdgpu_device *adev,
                                                       AMD_PG_STATE_GATE);
        }
 
+       mutex_lock(&adev->pm.mutex);
+
        if (pp_funcs->force_performance_level(adev->powerplay.pp_handle,
-                                             level))
-               ret = -EINVAL;
+                                             level)) {
+               mutex_unlock(&adev->pm.mutex);
+               return -EINVAL;
+       }
 
-       if (!ret)
-               adev->pm.dpm.forced_level = level;
+       adev->pm.dpm.forced_level = level;
 
-out:
        mutex_unlock(&adev->pm.mutex);
 
-       return ret;
+       return 0;
 }
 
 int amdgpu_dpm_get_pp_num_states(struct amdgpu_device *adev,