drm/i915/gt: Manual rc6 entry upon parking
authorChris Wilson <chris@chris-wilson.co.uk>
Wed, 27 Nov 2019 09:56:57 +0000 (09:56 +0000)
committerChris Wilson <chris@chris-wilson.co.uk>
Wed, 27 Nov 2019 12:53:27 +0000 (12:53 +0000)
Now that we rapidly park the GT when the GPU idles, we often find
ourselves idling faster than the RC6 promotion timer. Thus if we tell
the GPU to enter RC6 manually as we park, we can do so quicker (by
around 50ms, half an EI on average) and marginally increase our
powersaving across all execlists platforms.

v2: Now with a selftest to check we can enter RC6 manually

Signed-off-by: Chris Wilson <chris@chris-wilson.co.uk>
Cc: Andi Shyti <andi.shyti@intel.com>
Cc: Mika Kuoppala <mika.kuoppala@linux.intel.com>
Cc: Imre Deak <imre.deak@intel.com>
Acked-by: Imre Deak <imre.deak@intel.com>
Reviewed-by: Andi Shyti <andi.shyti@intel.com>
Link: https://patchwork.freedesktop.org/patch/msgid/20191127095657.3209854-1-chris@chris-wilson.co.uk
drivers/gpu/drm/i915/gt/intel_gt_pm.c
drivers/gpu/drm/i915/gt/intel_rc6.c
drivers/gpu/drm/i915/gt/intel_rc6.h
drivers/gpu/drm/i915/gt/intel_rc6_types.h
drivers/gpu/drm/i915/gt/selftest_gt_pm.c
drivers/gpu/drm/i915/gt/selftest_rc6.c
drivers/gpu/drm/i915/gt/selftest_rc6.h

index 9d3045096e3798abe7fb059aa40df55a941039e1..2000018b6464719f48be8904b036c17e26b53255 100644 (file)
@@ -64,6 +64,7 @@ static int __gt_unpark(struct intel_wakeref *wf)
        if (NEEDS_RC6_CTX_CORRUPTION_WA(i915))
                intel_uncore_forcewake_get(gt->uncore, FORCEWAKE_ALL);
 
+       intel_rc6_unpark(&gt->rc6);
        intel_rps_unpark(&gt->rps);
        i915_pmu_gt_unparked(i915);
 
@@ -85,6 +86,7 @@ static int __gt_park(struct intel_wakeref *wf)
        i915_vma_parked(gt);
        i915_pmu_gt_parked(i915);
        intel_rps_park(&gt->rps);
+       intel_rc6_park(&gt->rc6);
 
        /* Everything switched off, flush any residual interrupt just in case */
        intel_synchronize_irq(i915);
index 7a0bc6dde009f64e9bd1b76fc6fdfa48d77d008a..c4379c49c20dd4cb542f9ad2d87f8ad398243d7d 100644 (file)
@@ -96,10 +96,10 @@ static void gen11_rc6_enable(struct intel_rc6 *rc6)
        set(uncore, GEN9_RENDER_PG_IDLE_HYSTERESIS, 60);
 
        /* 3a: Enable RC6 */
-       set(uncore, GEN6_RC_CONTROL,
-           GEN6_RC_CTL_HW_ENABLE |
-           GEN6_RC_CTL_RC6_ENABLE |
-           GEN6_RC_CTL_EI_MODE(1));
+       rc6->ctl_enable =
+               GEN6_RC_CTL_HW_ENABLE |
+               GEN6_RC_CTL_RC6_ENABLE |
+               GEN6_RC_CTL_EI_MODE(1);
 
        set(uncore, GEN9_PG_ENABLE,
            GEN9_RENDER_PG_ENABLE |
@@ -170,10 +170,10 @@ static void gen9_rc6_enable(struct intel_rc6 *rc6)
        else
                rc6_mode = GEN6_RC_CTL_EI_MODE(1);
 
-       set(uncore, GEN6_RC_CONTROL,
-           GEN6_RC_CTL_HW_ENABLE |
-           GEN6_RC_CTL_RC6_ENABLE |
-           rc6_mode);
+       rc6->ctl_enable =
+               GEN6_RC_CTL_HW_ENABLE |
+               GEN6_RC_CTL_RC6_ENABLE |
+               rc6_mode;
 
        /*
         * WaRsDisableCoarsePowerGating:skl,cnl
@@ -200,10 +200,10 @@ static void gen8_rc6_enable(struct intel_rc6 *rc6)
        set(uncore, GEN6_RC6_THRESHOLD, 625); /* 800us/1.28 for TO */
 
        /* 3: Enable RC6 */
-       set(uncore, GEN6_RC_CONTROL,
+       rc6->ctl_enable =
            GEN6_RC_CTL_HW_ENABLE |
            GEN7_RC_CTL_TO_MODE |
-           GEN6_RC_CTL_RC6_ENABLE);
+           GEN6_RC_CTL_RC6_ENABLE;
 }
 
 static void gen6_rc6_enable(struct intel_rc6 *rc6)
@@ -239,10 +239,10 @@ static void gen6_rc6_enable(struct intel_rc6 *rc6)
                rc6_mask |= GEN6_RC_CTL_RC6p_ENABLE;
        if (HAS_RC6pp(i915))
                rc6_mask |= GEN6_RC_CTL_RC6pp_ENABLE;
-       set(uncore, GEN6_RC_CONTROL,
+       rc6->ctl_enable =
            rc6_mask |
            GEN6_RC_CTL_EI_MODE(1) |
-           GEN6_RC_CTL_HW_ENABLE);
+           GEN6_RC_CTL_HW_ENABLE;
 
        rc6vids = 0;
        ret = sandybridge_pcode_read(i915, GEN6_PCODE_READ_RC6VIDS,
@@ -360,7 +360,7 @@ static void chv_rc6_enable(struct intel_rc6 *rc6)
                               VLV_RENDER_RC6_COUNT_EN));
 
        /* 3: Enable RC6 */
-       set(uncore, GEN6_RC_CONTROL, GEN7_RC_CTL_TO_MODE);
+       rc6->ctl_enable = GEN7_RC_CTL_TO_MODE;
 }
 
 static void vlv_rc6_enable(struct intel_rc6 *rc6)
@@ -386,8 +386,8 @@ static void vlv_rc6_enable(struct intel_rc6 *rc6)
                               VLV_MEDIA_RC6_COUNT_EN |
                               VLV_RENDER_RC6_COUNT_EN));
 
-       set(uncore, GEN6_RC_CONTROL,
-           GEN7_RC_CTL_TO_MODE | VLV_RC_CTL_CTX_RST_PARALLEL);
+       rc6->ctl_enable =
+           GEN7_RC_CTL_TO_MODE | VLV_RC_CTL_CTX_RST_PARALLEL;
 }
 
 static bool bxt_check_bios_rc6_setup(struct intel_rc6 *rc6)
@@ -634,6 +634,29 @@ void intel_rc6_enable(struct intel_rc6 *rc6)
        rc6->enabled = true;
 }
 
+void intel_rc6_unpark(struct intel_rc6 *rc6)
+{
+       struct intel_uncore *uncore = rc6_to_uncore(rc6);
+
+       if (!rc6->enabled)
+               return;
+
+       /* Restore HW timers for automatic RC6 entry while busy */
+       set(uncore, GEN6_RC_CONTROL, rc6->ctl_enable);
+}
+
+void intel_rc6_park(struct intel_rc6 *rc6)
+{
+       struct intel_uncore *uncore = rc6_to_uncore(rc6);
+
+       if (!rc6->enabled)
+               return;
+
+       /* Turn off the HW timers and go directly to rc6 */
+       set(uncore, GEN6_RC_CONTROL, GEN6_RC_CTL_RC6_ENABLE);
+       set(uncore, GEN6_RC_STATE, 0x4 << RC_SW_TARGET_STATE_SHIFT);
+}
+
 void intel_rc6_disable(struct intel_rc6 *rc6)
 {
        if (!rc6->enabled)
index 1370f6834a4cc293beb7814d32605226f2a37308..20d740ff7e5565ef82a97239a0f0c48c7b939c37 100644 (file)
@@ -15,6 +15,9 @@ struct intel_rc6;
 void intel_rc6_init(struct intel_rc6 *rc6);
 void intel_rc6_fini(struct intel_rc6 *rc6);
 
+void intel_rc6_unpark(struct intel_rc6 *rc6);
+void intel_rc6_park(struct intel_rc6 *rc6);
+
 void intel_rc6_sanitize(struct intel_rc6 *rc6);
 void intel_rc6_enable(struct intel_rc6 *rc6);
 void intel_rc6_disable(struct intel_rc6 *rc6);
index 89ad5697a8d4cfb4b9a640189818ba413942a19e..9f3d56817c9764782cd99aa152727771c24b7879 100644 (file)
@@ -18,6 +18,8 @@ struct intel_rc6 {
        u64 prev_hw_residency[4];
        u64 cur_residency[4];
 
+       u32 ctl_enable;
+
        struct drm_i915_gem_object *pctx;
 
        bool supported : 1;
index 5e563b877368580873dcab97fa331c0578026132..09ff8e4f88af8d76382213a0ea0283b38f62ab10 100644 (file)
@@ -51,6 +51,7 @@ static int live_gt_resume(void *arg)
 int intel_gt_pm_live_selftests(struct drm_i915_private *i915)
 {
        static const struct i915_subtest tests[] = {
+               SUBTEST(live_rc6_manual),
                SUBTEST(live_gt_resume),
        };
 
index 67b7a6bc64f58268ca7abbea2903c7c5759096bd..f8b7691be4d1ac6cb6d677250b942b200b906e94 100644 (file)
 
 #include "selftests/i915_random.h"
 
+int live_rc6_manual(void *arg)
+{
+       struct intel_gt *gt = arg;
+       struct intel_rc6 *rc6 = &gt->rc6;
+       intel_wakeref_t wakeref;
+       u64 res[2];
+       int err = 0;
+
+       /*
+        * Our claim is that we can "encourage" the GPU to enter rc6 at will.
+        * Let's try it!
+        */
+
+       if (!rc6->enabled)
+               return 0;
+
+       /* bsw/byt use a PCU and decouple RC6 from our manual control */
+       if (IS_VALLEYVIEW(gt->i915) || IS_CHERRYVIEW(gt->i915))
+               return 0;
+
+       wakeref = intel_runtime_pm_get(gt->uncore->rpm);
+
+       /* Force RC6 off for starters */
+       __intel_rc6_disable(rc6);
+       msleep(1); /* wakeup is not immediate, takes about 100us on icl */
+
+       res[0] = intel_rc6_residency_ns(rc6, GEN6_GT_GFX_RC6);
+       msleep(250);
+       res[1] = intel_rc6_residency_ns(rc6, GEN6_GT_GFX_RC6);
+       if ((res[1] - res[0]) >> 10) {
+               pr_err("RC6 residency increased by %lldus while disabled for 250ms!\n",
+                      (res[1] - res[0]) >> 10);
+               err = -EINVAL;
+               goto out_unlock;
+       }
+
+       /* Manually enter RC6 */
+       intel_rc6_park(rc6);
+
+       res[0] = intel_rc6_residency_ns(rc6, GEN6_GT_GFX_RC6);
+       msleep(100);
+       res[1] = intel_rc6_residency_ns(rc6, GEN6_GT_GFX_RC6);
+
+       if (res[1] == res[0]) {
+               pr_err("Did not enter RC6! RC6_STATE=%08x, RC6_CONTROL=%08x\n",
+                      intel_uncore_read_fw(gt->uncore, GEN6_RC_STATE),
+                      intel_uncore_read_fw(gt->uncore, GEN6_RC_CONTROL));
+               err = -EINVAL;
+       }
+
+       /* Restore what should have been the original state! */
+       intel_rc6_unpark(rc6);
+
+out_unlock:
+       intel_runtime_pm_put(gt->uncore->rpm, wakeref);
+       return err;
+}
+
 static const u32 *__live_rc6_ctx(struct intel_context *ce)
 {
        struct i915_request *rq;
index f907e7b035abed13c50ecd8f60308078638b4b6d..762fd442d7b2efc13e2b0ba85d5c8078ce764413 100644 (file)
@@ -8,5 +8,6 @@
 #define SELFTEST_RC6_H
 
 int live_rc6_ctx_wa(void *arg);
+int live_rc6_manual(void *arg);
 
 #endif /* SELFTEST_RC6_H */