--- a/sys/dev/drm/radeon/radeon_pm.c +++ b/sys/dev/drm/radeon/radeon_pm.c @@ -1255,6 +1255,10 @@ static void radeon_pm_suspend_dpm(struct radeon_device *rdev) { + /* dpm init failed and state was scrubbed: nothing to disable */ + if (!rdev->pm.dpm.boot_ps) + return; + mutex_lock(&rdev->pm.mutex); /* disable dpm */ radeon_dpm_disable(rdev); @@ -1315,6 +1319,10 @@ { int ret; + /* dpm init failed and state was scrubbed: cannot re-enable */ + if (!rdev->pm.dpm.boot_ps) + return; + /* asic init will reset to the boot state */ mutex_lock(&rdev->pm.mutex); rdev->pm.dpm.current_ps = rdev->pm.dpm.requested_ps = rdev->pm.dpm.boot_ps; @@ -1445,7 +1453,18 @@ INIT_WORK(&rdev->pm.dpm.thermal.work, radeon_dpm_thermal_work_handler); mutex_lock(&rdev->pm.mutex); - radeon_dpm_init(rdev); + ret = radeon_dpm_init(rdev); + if (ret) + goto dpm_init_failed; + if (!rdev->pm.dpm.boot_ps) { + /* + * The VBIOS power table has no BOOT-classified state + * (or ucStateEntrySize<=1): every per-asic dpm + * enable/disable/fini path derefs boot_ps. + */ + ret = -EINVAL; + goto dpm_init_failed; + } rdev->pm.dpm.current_ps = rdev->pm.dpm.requested_ps = rdev->pm.dpm.boot_ps; if (radeon_dpm == 1) radeon_dpm_print_power_states(rdev); @@ -1466,6 +1485,22 @@ return 0; +dpm_init_failed: + /* + * dpm.init failed or there is no BOOT-classified state. The + * per-asic parse error paths may already have kfree'd + * rdev->pm.dpm.ps without NULLing it (see the DF-4117/DF-4125/ + * DF-4144 twins): scrub the dangling pointers so radeon_dpm_fini() + * at unload cannot double free, and so the suspend/resume/fini + * paths skip the dpm paths entirely (the asic *_init files still + * ignore radeon_pm_init's return, so pm_method stays DPM). + */ + rdev->pm.dpm.ps = NULL; + rdev->pm.dpm.num_ps = 0; + rdev->pm.dpm.boot_ps = NULL; + rdev->pm.dpm.current_ps = rdev->pm.dpm.requested_ps = NULL; + mutex_unlock(&rdev->pm.mutex); + goto dpm_failed; dpm_failed: rdev->pm.dpm_enabled = false; if ((rdev->family >= CHIP_BARTS) && @@ -1689,7 +1724,7 @@ static void radeon_pm_fini_dpm(struct radeon_device *rdev) { - if (rdev->pm.num_power_states > 1) { + if (rdev->pm.num_power_states > 1 && rdev->pm.dpm.boot_ps) { mutex_lock(&rdev->pm.mutex); radeon_dpm_disable(rdev); mutex_unlock(&rdev->pm.mutex);