Commit a70a9e99 authored by Chris Wilson's avatar Chris Wilson Committed by Joonas Lahtinen

drm/i915: Defer rc6 shutdown to suspend_late

Currently we shutdown rc6 during i915_gem_resume() but this is called
during the preparation phase (i915_drm_prepare) for all suspend paths,
but we only want to shutdown rc6 for S3+. Move the actual shutdown to
i915_gem_suspend_late().

We then need to differentiate between suspend targets, to distinguish S0
(s2idle) where the device is kept awake but needs to be in a low power
mode (the same as runtime suspend) from the device suspend levels where
we lose control of HW and so must disable any HW access to dangling
memory.

Bugzilla: https://bugs.freedesktop.org/show_bug.cgi?id=111909
Fixes: c1132367 ("drm/i915: Extract GT render sleep (rc6) management")
Testcase: igt/gem_exec_suspend/power-S0
Signed-off-by: default avatarChris Wilson <chris@chris-wilson.co.uk>
Cc: Andi Shyti <andi.shyti@intel.com>
Acked-by: default avatarAndi Shyti <andi.shyti@intel.com>
Link: https://patchwork.freedesktop.org/patch/msgid/20191101141009.15581-4-chris@chris-wilson.co.uk
(cherry picked from commit c601cb21)
Signed-off-by: default avatarJoonas Lahtinen <joonas.lahtinen@linux.intel.com>
parent d4033a9b
......@@ -27,7 +27,7 @@ void i915_gem_suspend(struct drm_i915_private *i915)
* state. Fortunately, the kernel_context is disposable and we do
* not rely on its state.
*/
intel_gt_suspend(&i915->gt);
intel_gt_suspend_prepare(&i915->gt);
i915_gem_drain_freed_objects(i915);
}
......@@ -69,6 +69,8 @@ void i915_gem_suspend_late(struct drm_i915_private *i915)
* machine in an unusable condition.
*/
intel_gt_suspend_late(&i915->gt);
spin_lock_irqsave(&i915->mm.obj_lock, flags);
for (phase = phases; *phase; phase++) {
LIST_HEAD(keep);
......
......@@ -4,6 +4,8 @@
* Copyright © 2019 Intel Corporation
*/
#include <linux/suspend.h>
#include "i915_drv.h"
#include "i915_globals.h"
#include "i915_params.h"
......@@ -236,8 +238,11 @@ int intel_gt_resume(struct intel_gt *gt)
return err;
}
static void wait_for_idle(struct intel_gt *gt)
static void wait_for_suspend(struct intel_gt *gt)
{
if (!intel_gt_pm_is_awake(gt))
return;
if (intel_gt_wait_for_idle(gt, I915_GEM_IDLE_TIMEOUT) == -ETIME) {
/*
* Forcibly cancel outstanding work and leave
......@@ -246,19 +251,46 @@ static void wait_for_idle(struct intel_gt *gt)
intel_gt_set_wedged(gt);
}
GEM_BUG_ON(atomic_read(&gt->user_wakeref));
intel_gt_pm_wait_for_idle(gt);
}
void intel_gt_suspend(struct intel_gt *gt)
void intel_gt_suspend_prepare(struct intel_gt *gt)
{
intel_wakeref_t wakeref;
user_forcewake(gt, true);
wait_for_suspend(gt);
intel_uc_suspend(&gt->uc);
}
static suspend_state_t pm_suspend_target(void)
{
#if IS_ENABLED(CONFIG_PM_SLEEP)
return pm_suspend_target_state;
#else
return PM_SUSPEND_TO_IDLE;
#endif
}
void intel_gt_suspend_late(struct intel_gt *gt)
{
intel_wakeref_t wakeref;
/* We expect to be idle already; but also want to be independent */
wait_for_idle(gt);
wait_for_suspend(gt);
intel_uc_suspend(&gt->uc);
/*
* On disabling the device, we want to turn off HW access to memory
* that we no longer own.
*
* However, not all suspend-states disable the device. S0 (s2idle)
* is effectively runtime-suspend, the device is left powered on
* but needs to be put into a low power state. We need to keep
* powermanagement enabled, but we also retain system state and so
* it remains safe to keep on using our allocated memory.
*/
if (pm_suspend_target() == PM_SUSPEND_TO_IDLE)
return;
with_intel_runtime_pm(gt->uncore->rpm, wakeref) {
intel_rps_disable(&gt->rps);
......
......@@ -43,8 +43,9 @@ void intel_gt_pm_fini(struct intel_gt *gt);
void intel_gt_sanitize(struct intel_gt *gt, bool force);
void intel_gt_suspend_prepare(struct intel_gt *gt);
void intel_gt_suspend_late(struct intel_gt *gt);
int intel_gt_resume(struct intel_gt *gt);
void intel_gt_suspend(struct intel_gt *gt);
void intel_gt_runtime_suspend(struct intel_gt *gt);
int intel_gt_runtime_resume(struct intel_gt *gt);
......
......@@ -525,6 +525,11 @@ void intel_rc6_init(struct intel_rc6 *rc6)
void intel_rc6_sanitize(struct intel_rc6 *rc6)
{
if (rc6->enabled) { /* unbalanced suspend/resume */
rpm_get(rc6);
rc6->enabled = false;
}
if (rc6->supported)
__intel_rc6_disable(rc6);
}
......
......@@ -15,7 +15,7 @@ static int live_gt_resume(void *arg)
/* Do several suspend/resume cycles to check we don't explode! */
do {
intel_gt_suspend(gt);
intel_gt_suspend_late(gt);
if (gt->rc6.enabled) {
pr_err("rc6 still enabled after suspend!\n");
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment