On Thu, Jun 17, 2010 at 7:02 PM, Rafael J. Wysocki <rjw at sisk.pl> wrote:
> From: Rafael J. Wysocki <rjw at sisk.pl>
>
> There is a regression from 2.6.34 related to the recent radeon power
> management changes, caused by attempting to cancel a delayed work
> item that's never been scheduled. ?However, the code as is has some
> other issues potentially leading to visible problems.
>
> First, the mutex around cancel_delayed_work() in radeon_pm_suspend()
> doesn't really serve any purpose, because cancel_delayed_work() only
> tries to delete the work's timer. ?Moreover, it doesn't prevent the
> work handler from running, so the handler can do some wrong things if
> it wins the race and in that case it will rearm itself to do some
> more wrong things going forward. ?So, I think it's better to wait for
> the handler to return in case it's already been queued up for
> execution. ?Also, it should be prevented from rearming itself in that
> case.
>
> Second, in radeon_set_pm_method() the cancel_delayed_work() is not
> sufficient to prevent the work handler from running and queing up
> itself for the next run (the failure scenario is that
> cancel_delayed_work() returns 0, so the handler is run, it waits on
> the mutex and then rearms itself after the mutex has been released),
> so again the work handler should be prevented from rearming itself in
> that case..
>
> Finally, there's a potential deadlock in radeon_pm_fini(), because
> cancel_delayed_work_sync() is called under rdev->pm.mutex, but the
> work handler tries to acquire the same mutex (if it wins the race).
>
> Fix the issues described above.
>
> Signed-off-by: Rafael J. Wysocki <rjw at sisk.pl>
> Reviewed-by: Alex Deucher <alexdeucher at gmail.com>
> ---
> ?drivers/gpu/drm/radeon/radeon.h ? ?| ? ?3 +-
> ?drivers/gpu/drm/radeon/radeon_pm.c | ? 41 
> ++++++++++++++++++++++++++++++-------
> ?2 files changed, 36 insertions(+), 8 deletions(-)
>
> Index: linux-2.6/drivers/gpu/drm/radeon/radeon_pm.c
> ===================================================================
> --- linux-2.6.orig/drivers/gpu/drm/radeon/radeon_pm.c
> +++ linux-2.6/drivers/gpu/drm/radeon/radeon_pm.c
> @@ -397,13 +397,20 @@ static ssize_t radeon_set_pm_method(stru
> ? ? ? ? ? ? ? ?rdev->pm.dynpm_planned_action = DYNPM_ACTION_DEFAULT;
> ? ? ? ? ? ? ? ?mutex_unlock(&rdev->pm.mutex);
> ? ? ? ?} else if (strncmp("profile", buf, strlen("profile")) == 0) {
> + ? ? ? ? ? ? ? bool flush_wq = false;
> +
> ? ? ? ? ? ? ? ?mutex_lock(&rdev->pm.mutex);
> - ? ? ? ? ? ? ? rdev->pm.pm_method = PM_METHOD_PROFILE;
> + ? ? ? ? ? ? ? if (rdev->pm.pm_method == PM_METHOD_DYNPM) {
> + ? ? ? ? ? ? ? ? ? ? ? cancel_delayed_work(&rdev->pm.dynpm_idle_work);
> + ? ? ? ? ? ? ? ? ? ? ? flush_wq = true;
> + ? ? ? ? ? ? ? }
> ? ? ? ? ? ? ? ?/* disable dynpm */
> ? ? ? ? ? ? ? ?rdev->pm.dynpm_state = DYNPM_STATE_DISABLED;
> ? ? ? ? ? ? ? ?rdev->pm.dynpm_planned_action = DYNPM_ACTION_NONE;
> - ? ? ? ? ? ? ? cancel_delayed_work(&rdev->pm.dynpm_idle_work);
> + ? ? ? ? ? ? ? rdev->pm.pm_method = PM_METHOD_PROFILE;
> ? ? ? ? ? ? ? ?mutex_unlock(&rdev->pm.mutex);
> + ? ? ? ? ? ? ? if (flush_wq)
> + ? ? ? ? ? ? ? ? ? ? ? flush_workqueue(rdev->wq);
> ? ? ? ?} else {
> ? ? ? ? ? ? ? ?DRM_ERROR("invalid power method!\n");
> ? ? ? ? ? ? ? ?goto fail;
> @@ -418,9 +425,18 @@ static DEVICE_ATTR(power_method, S_IRUGO
>
> ?void radeon_pm_suspend(struct radeon_device *rdev)
> ?{
> + ? ? ? bool flush_wq = false;
> +
> ? ? ? ?mutex_lock(&rdev->pm.mutex);
> - ? ? ? cancel_delayed_work(&rdev->pm.dynpm_idle_work);
> + ? ? ? if (rdev->pm.pm_method == PM_METHOD_DYNPM) {
> + ? ? ? ? ? ? ? cancel_delayed_work(&rdev->pm.dynpm_idle_work);
> + ? ? ? ? ? ? ? if (rdev->pm.dynpm_state == DYNPM_STATE_ACTIVE)
> + ? ? ? ? ? ? ? ? ? ? ? rdev->pm.dynpm_state = DYNPM_STATE_SUSPENDED;
> + ? ? ? ? ? ? ? flush_wq = true;
> + ? ? ? }
> ? ? ? ?mutex_unlock(&rdev->pm.mutex);
> + ? ? ? if (flush_wq)
> + ? ? ? ? ? ? ? flush_workqueue(rdev->wq);
> ?}
>
> ?void radeon_pm_resume(struct radeon_device *rdev)
> @@ -432,6 +448,12 @@ void radeon_pm_resume(struct radeon_devi
> ? ? ? ?rdev->pm.current_sclk = rdev->clock.default_sclk;
> ? ? ? ?rdev->pm.current_mclk = rdev->clock.default_mclk;
> ? ? ? ?rdev->pm.current_vddc = 
> rdev->pm.power_state[rdev->pm.default_power_state_index].clock_info[0].voltage.voltage;
> + ? ? ? if (rdev->pm.pm_method == PM_METHOD_DYNPM
> + ? ? ? ? ? && rdev->pm.dynpm_state == DYNPM_STATE_SUSPENDED) {
> + ? ? ? ? ? ? ? rdev->pm.dynpm_state = DYNPM_STATE_ACTIVE;
> + ? ? ? ? ? ? ? queue_delayed_work(rdev->wq, &rdev->pm.dynpm_idle_work,
> + ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? 
> msecs_to_jiffies(RADEON_IDLE_LOOP_MS));
> + ? ? ? }
> ? ? ? ?mutex_unlock(&rdev->pm.mutex);
> ? ? ? ?radeon_pm_compute_clocks(rdev);
> ?}
> @@ -486,6 +508,8 @@ int radeon_pm_init(struct radeon_device
> ?void radeon_pm_fini(struct radeon_device *rdev)
> ?{
> ? ? ? ?if (rdev->pm.num_power_states > 1) {
> + ? ? ? ? ? ? ? bool flush_wq = false;
> +
> ? ? ? ? ? ? ? ?mutex_lock(&rdev->pm.mutex);
> ? ? ? ? ? ? ? ?if (rdev->pm.pm_method == PM_METHOD_PROFILE) {
> ? ? ? ? ? ? ? ? ? ? ? ?rdev->pm.profile = PM_PROFILE_DEFAULT;
> @@ -493,13 +517,16 @@ void radeon_pm_fini(struct radeon_device
> ? ? ? ? ? ? ? ? ? ? ? ?radeon_pm_set_clocks(rdev);
> ? ? ? ? ? ? ? ?} else if (rdev->pm.pm_method == PM_METHOD_DYNPM) {
> ? ? ? ? ? ? ? ? ? ? ? ?/* cancel work */
> - ? ? ? ? ? ? ? ? ? ? ? cancel_delayed_work_sync(&rdev->pm.dynpm_idle_work);
> + ? ? ? ? ? ? ? ? ? ? ? cancel_delayed_work(&rdev->pm.dynpm_idle_work);
> + ? ? ? ? ? ? ? ? ? ? ? flush_wq = true;
> ? ? ? ? ? ? ? ? ? ? ? ?/* reset default clocks */
> ? ? ? ? ? ? ? ? ? ? ? ?rdev->pm.dynpm_state = DYNPM_STATE_DISABLED;
> ? ? ? ? ? ? ? ? ? ? ? ?rdev->pm.dynpm_planned_action = DYNPM_ACTION_DEFAULT;
> ? ? ? ? ? ? ? ? ? ? ? ?radeon_pm_set_clocks(rdev);
> ? ? ? ? ? ? ? ?}
> ? ? ? ? ? ? ? ?mutex_unlock(&rdev->pm.mutex);
> + ? ? ? ? ? ? ? if (flush_wq)
> + ? ? ? ? ? ? ? ? ? ? ? flush_workqueue(rdev->wq);
>
> ? ? ? ? ? ? ? ?device_remove_file(rdev->dev, &dev_attr_power_profile);
> ? ? ? ? ? ? ? ?device_remove_file(rdev->dev, &dev_attr_power_method);
> @@ -720,12 +747,12 @@ static void radeon_dynpm_idle_work_handl
> ? ? ? ? ? ? ? ? ? ? ? ?radeon_pm_get_dynpm_state(rdev);
> ? ? ? ? ? ? ? ? ? ? ? ?radeon_pm_set_clocks(rdev);
> ? ? ? ? ? ? ? ?}
> +
> + ? ? ? ? ? ? ? queue_delayed_work(rdev->wq, &rdev->pm.dynpm_idle_work,
> + ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? 
> msecs_to_jiffies(RADEON_IDLE_LOOP_MS));
> ? ? ? ?}
> ? ? ? ?mutex_unlock(&rdev->pm.mutex);
> ? ? ? ?ttm_bo_unlock_delayed_workqueue(&rdev->mman.bdev, resched);
> -
> - ? ? ? queue_delayed_work(rdev->wq, &rdev->pm.dynpm_idle_work,
> - ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? 
> msecs_to_jiffies(RADEON_IDLE_LOOP_MS));
> ?}
>
> ?/*
> Index: linux-2.6/drivers/gpu/drm/radeon/radeon.h
> ===================================================================
> --- linux-2.6.orig/drivers/gpu/drm/radeon/radeon.h
> +++ linux-2.6/drivers/gpu/drm/radeon/radeon.h
> @@ -619,7 +619,8 @@ enum radeon_dynpm_state {
> ? ? ? ?DYNPM_STATE_DISABLED,
> ? ? ? ?DYNPM_STATE_MINIMUM,
> ? ? ? ?DYNPM_STATE_PAUSED,
> - ? ? ? DYNPM_STATE_ACTIVE
> + ? ? ? DYNPM_STATE_ACTIVE,
> + ? ? ? DYNPM_STATE_SUSPENDED,
> ?};
> ?enum radeon_dynpm_action {
> ? ? ? ?DYNPM_ACTION_NONE,
> _______________________________________________
> dri-devel mailing list
> dri-devel at lists.freedesktop.org
> http://lists.freedesktop.org/mailman/listinfo/dri-devel

Probably want to shorten the commit title a bit.

Matt

Reply via email to