drm/i915/selftests: Context SSEU reconfiguration tests
authorTvrtko Ursulin <tvrtko.ursulin@intel.com>
Tue, 5 Feb 2019 09:50:32 +0000 (09:50 +0000)
committerTvrtko Ursulin <tvrtko.ursulin@intel.com>
Tue, 5 Feb 2019 11:32:03 +0000 (11:32 +0000)
Exercise the context image reconfiguration logic for idle and busy
contexts, with the resets thrown into the mix as well.

Free from the uAPI restrictions this test runs on all Gen9+ platforms
with slice power gating.

v2:
 * Rename some helpers for clarity.
 * Include subtest names in error logs.
 * Remove unnecessary function export.

v3:
 * Rebase for RUNTIME_INFO.

v4:
 * Fix incomplete unexport from v2. (Chris Wilson)

v5:
 * Rebased for runtime pm api changes.

v6:
 * Rebased for i915_reset.c.

v7:
 * Tidy checkpatch warnings.
 * Consolidate error checking and logging a bit.
 * Skip idle test phase if something failed before it.

v8:
 (Chris Wilson)
 * Fix i915_request_wait error handling.
 * No need to PIN_HIGH the VMA.
 * Remove pointless GEM_BUG_ON before pointer dereference.

v9:
 * Avoid rq leak if rpcs query fails. (Chris)

Signed-off-by: Tvrtko Ursulin <tvrtko.ursulin@intel.com>
Reviewed-by: Chris Wilson <chris@chris-wilson.co.uk>
Reviewed-by: Joonas Lahtinen <joonas.lahtinen@linux.intel.com> # v6
Link: https://patchwork.freedesktop.org/patch/msgid/20190205095032.22673-5-tvrtko.ursulin@linux.intel.com
drivers/gpu/drm/i915/i915_gem_context.c
drivers/gpu/drm/i915/selftests/i915_gem_context.c

index 2d3e1ce9cc76717d4c6a30aa4e89d2f4fdd37d40..93ab287f44b6dfffe16d69f780cc8214af2b49b2 100644 (file)
@@ -1052,23 +1052,19 @@ out_put:
 }
 
 static int
-i915_gem_context_reconfigure_sseu(struct i915_gem_context *ctx,
-                                 struct intel_engine_cs *engine,
-                                 struct intel_sseu sseu)
+__i915_gem_context_reconfigure_sseu(struct i915_gem_context *ctx,
+                                   struct intel_engine_cs *engine,
+                                   struct intel_sseu sseu)
 {
        struct intel_context *ce = to_intel_context(ctx, engine);
-       int ret;
+       int ret = 0;
 
        GEM_BUG_ON(INTEL_GEN(ctx->i915) < 8);
        GEM_BUG_ON(engine->id != RCS);
 
-       ret = mutex_lock_interruptible(&ctx->i915->drm.struct_mutex);
-       if (ret)
-               return ret;
-
        /* Nothing to do if unmodified. */
        if (!memcmp(&ce->sseu, &sseu, sizeof(sseu)))
-               goto out;
+               return 0;
 
        /*
         * If context is not idle we have to submit an ordered request to modify
@@ -1081,7 +1077,22 @@ i915_gem_context_reconfigure_sseu(struct i915_gem_context *ctx,
        if (!ret)
                ce->sseu = sseu;
 
-out:
+       return ret;
+}
+
+static int
+i915_gem_context_reconfigure_sseu(struct i915_gem_context *ctx,
+                                 struct intel_engine_cs *engine,
+                                 struct intel_sseu sseu)
+{
+       int ret;
+
+       ret = mutex_lock_interruptible(&ctx->i915->drm.struct_mutex);
+       if (ret)
+               return ret;
+
+       ret = __i915_gem_context_reconfigure_sseu(ctx, engine, sseu);
+
        mutex_unlock(&ctx->i915->drm.struct_mutex);
 
        return ret;
index e2c1f0bc2abe4685c8c5b2877597ee7e8b754366..d00d0bb07784229a323145336803802479c6ebb9 100644 (file)
 
 #include <linux/prime_numbers.h>
 
+#include "../i915_reset.h"
 #include "../i915_selftest.h"
 #include "i915_random.h"
 #include "igt_flush_test.h"
 #include "igt_live_test.h"
+#include "igt_reset.h"
+#include "igt_spinner.h"
 
 #include "mock_drm.h"
 #include "mock_gem_device.h"
@@ -576,6 +579,469 @@ out_unlock:
        return err;
 }
 
+static struct i915_vma *rpcs_query_batch(struct i915_vma *vma)
+{
+       struct drm_i915_gem_object *obj;
+       u32 *cmd;
+       int err;
+
+       if (INTEL_GEN(vma->vm->i915) < 8)
+               return ERR_PTR(-EINVAL);
+
+       obj = i915_gem_object_create_internal(vma->vm->i915, PAGE_SIZE);
+       if (IS_ERR(obj))
+               return ERR_CAST(obj);
+
+       cmd = i915_gem_object_pin_map(obj, I915_MAP_WB);
+       if (IS_ERR(cmd)) {
+               err = PTR_ERR(cmd);
+               goto err;
+       }
+
+       *cmd++ = MI_STORE_REGISTER_MEM_GEN8;
+       *cmd++ = i915_mmio_reg_offset(GEN8_R_PWR_CLK_STATE);
+       *cmd++ = lower_32_bits(vma->node.start);
+       *cmd++ = upper_32_bits(vma->node.start);
+       *cmd = MI_BATCH_BUFFER_END;
+
+       i915_gem_object_unpin_map(obj);
+
+       err = i915_gem_object_set_to_gtt_domain(obj, false);
+       if (err)
+               goto err;
+
+       vma = i915_vma_instance(obj, vma->vm, NULL);
+       if (IS_ERR(vma)) {
+               err = PTR_ERR(vma);
+               goto err;
+       }
+
+       err = i915_vma_pin(vma, 0, 0, PIN_USER);
+       if (err)
+               goto err;
+
+       return vma;
+
+err:
+       i915_gem_object_put(obj);
+       return ERR_PTR(err);
+}
+
+static int
+emit_rpcs_query(struct drm_i915_gem_object *obj,
+               struct i915_gem_context *ctx,
+               struct intel_engine_cs *engine,
+               struct i915_request **rq_out)
+{
+       struct i915_request *rq;
+       struct i915_vma *batch;
+       struct i915_vma *vma;
+       int err;
+
+       GEM_BUG_ON(!intel_engine_can_store_dword(engine));
+
+       vma = i915_vma_instance(obj, &ctx->ppgtt->vm, NULL);
+       if (IS_ERR(vma))
+               return PTR_ERR(vma);
+
+       err = i915_gem_object_set_to_gtt_domain(obj, false);
+       if (err)
+               return err;
+
+       err = i915_vma_pin(vma, 0, 0, PIN_USER);
+       if (err)
+               return err;
+
+       batch = rpcs_query_batch(vma);
+       if (IS_ERR(batch)) {
+               err = PTR_ERR(batch);
+               goto err_vma;
+       }
+
+       rq = i915_request_alloc(engine, ctx);
+       if (IS_ERR(rq)) {
+               err = PTR_ERR(rq);
+               goto err_batch;
+       }
+
+       err = engine->emit_bb_start(rq, batch->node.start, batch->node.size, 0);
+       if (err)
+               goto err_request;
+
+       err = i915_vma_move_to_active(batch, rq, 0);
+       if (err)
+               goto skip_request;
+
+       err = i915_vma_move_to_active(vma, rq, EXEC_OBJECT_WRITE);
+       if (err)
+               goto skip_request;
+
+       i915_gem_object_set_active_reference(batch->obj);
+       i915_vma_unpin(batch);
+       i915_vma_close(batch);
+
+       i915_vma_unpin(vma);
+
+       *rq_out = i915_request_get(rq);
+
+       i915_request_add(rq);
+
+       return 0;
+
+skip_request:
+       i915_request_skip(rq, err);
+err_request:
+       i915_request_add(rq);
+err_batch:
+       i915_vma_unpin(batch);
+err_vma:
+       i915_vma_unpin(vma);
+
+       return err;
+}
+
+#define TEST_IDLE      BIT(0)
+#define TEST_BUSY      BIT(1)
+#define TEST_RESET     BIT(2)
+
+static int
+__sseu_prepare(struct drm_i915_private *i915,
+              const char *name,
+              unsigned int flags,
+              struct i915_gem_context *ctx,
+              struct intel_engine_cs *engine,
+              struct igt_spinner **spin_out)
+{
+       int ret = 0;
+
+       if (flags & (TEST_BUSY | TEST_RESET)) {
+               struct igt_spinner *spin;
+               struct i915_request *rq;
+
+               spin = kzalloc(sizeof(*spin), GFP_KERNEL);
+               if (!spin) {
+                       ret = -ENOMEM;
+                       goto out;
+               }
+
+               ret = igt_spinner_init(spin, i915);
+               if (ret)
+                       return ret;
+
+               rq = igt_spinner_create_request(spin, ctx, engine, MI_NOOP);
+               if (IS_ERR(rq)) {
+                       ret = PTR_ERR(rq);
+                       igt_spinner_fini(spin);
+                       kfree(spin);
+                       goto out;
+               }
+
+               i915_request_add(rq);
+
+               if (!igt_wait_for_spinner(spin, rq)) {
+                       pr_err("%s: Spinner failed to start!\n", name);
+                       igt_spinner_end(spin);
+                       igt_spinner_fini(spin);
+                       kfree(spin);
+                       ret = -ETIMEDOUT;
+                       goto out;
+               }
+
+               *spin_out = spin;
+       }
+
+out:
+       return ret;
+}
+
+static int
+__read_slice_count(struct drm_i915_private *i915,
+                  struct i915_gem_context *ctx,
+                  struct intel_engine_cs *engine,
+                  struct drm_i915_gem_object *obj,
+                  struct igt_spinner *spin,
+                  u32 *rpcs)
+{
+       struct i915_request *rq = NULL;
+       u32 s_mask, s_shift;
+       unsigned int cnt;
+       u32 *buf, val;
+       long ret;
+
+       ret = emit_rpcs_query(obj, ctx, engine, &rq);
+       if (ret)
+               return ret;
+
+       if (spin)
+               igt_spinner_end(spin);
+
+       ret = i915_request_wait(rq, I915_WAIT_LOCKED, MAX_SCHEDULE_TIMEOUT);
+       i915_request_put(rq);
+       if (ret < 0)
+               return ret;
+
+       buf = i915_gem_object_pin_map(obj, I915_MAP_WB);
+       if (IS_ERR(buf)) {
+               ret = PTR_ERR(buf);
+               return ret;
+       }
+
+       if (INTEL_GEN(i915) >= 11) {
+               s_mask = GEN11_RPCS_S_CNT_MASK;
+               s_shift = GEN11_RPCS_S_CNT_SHIFT;
+       } else {
+               s_mask = GEN8_RPCS_S_CNT_MASK;
+               s_shift = GEN8_RPCS_S_CNT_SHIFT;
+       }
+
+       val = *buf;
+       cnt = (val & s_mask) >> s_shift;
+       *rpcs = val;
+
+       i915_gem_object_unpin_map(obj);
+
+       return cnt;
+}
+
+static int
+__check_rpcs(const char *name, u32 rpcs, int slices, unsigned int expected,
+            const char *prefix, const char *suffix)
+{
+       if (slices == expected)
+               return 0;
+
+       if (slices < 0) {
+               pr_err("%s: %s read slice count failed with %d%s\n",
+                      name, prefix, slices, suffix);
+               return slices;
+       }
+
+       pr_err("%s: %s slice count %d is not %u%s\n",
+              name, prefix, slices, expected, suffix);
+
+       pr_info("RPCS=0x%x; %u%sx%u%s\n",
+               rpcs, slices,
+               (rpcs & GEN8_RPCS_S_CNT_ENABLE) ? "*" : "",
+               (rpcs & GEN8_RPCS_SS_CNT_MASK) >> GEN8_RPCS_SS_CNT_SHIFT,
+               (rpcs & GEN8_RPCS_SS_CNT_ENABLE) ? "*" : "");
+
+       return -EINVAL;
+}
+
+static int
+__sseu_finish(struct drm_i915_private *i915,
+             const char *name,
+             unsigned int flags,
+             struct i915_gem_context *ctx,
+             struct i915_gem_context *kctx,
+             struct intel_engine_cs *engine,
+             struct drm_i915_gem_object *obj,
+             unsigned int expected,
+             struct igt_spinner *spin)
+{
+       unsigned int slices =
+               hweight32(intel_device_default_sseu(i915).slice_mask);
+       u32 rpcs = 0;
+       int ret = 0;
+
+       if (flags & TEST_RESET) {
+               ret = i915_reset_engine(engine, "sseu");
+               if (ret)
+                       goto out;
+       }
+
+       ret = __read_slice_count(i915, ctx, engine, obj,
+                                flags & TEST_RESET ? NULL : spin, &rpcs);
+       ret = __check_rpcs(name, rpcs, ret, expected, "Context", "!");
+       if (ret)
+               goto out;
+
+       ret = __read_slice_count(i915, kctx, engine, obj, NULL, &rpcs);
+       ret = __check_rpcs(name, rpcs, ret, slices, "Kernel context", "!");
+
+out:
+       if (spin)
+               igt_spinner_end(spin);
+
+       if ((flags & TEST_IDLE) && ret == 0) {
+               ret = i915_gem_wait_for_idle(i915,
+                                            I915_WAIT_LOCKED,
+                                            MAX_SCHEDULE_TIMEOUT);
+               if (ret)
+                       return ret;
+
+               ret = __read_slice_count(i915, ctx, engine, obj, NULL, &rpcs);
+               ret = __check_rpcs(name, rpcs, ret, expected,
+                                  "Context", " after idle!");
+       }
+
+       return ret;
+}
+
+static int
+__sseu_test(struct drm_i915_private *i915,
+           const char *name,
+           unsigned int flags,
+           struct i915_gem_context *ctx,
+           struct intel_engine_cs *engine,
+           struct drm_i915_gem_object *obj,
+           struct intel_sseu sseu)
+{
+       struct igt_spinner *spin = NULL;
+       struct i915_gem_context *kctx;
+       int ret;
+
+       kctx = kernel_context(i915);
+       if (IS_ERR(kctx))
+               return PTR_ERR(kctx);
+
+       ret = __sseu_prepare(i915, name, flags, ctx, engine, &spin);
+       if (ret)
+               goto out;
+
+       ret = __i915_gem_context_reconfigure_sseu(ctx, engine, sseu);
+       if (ret)
+               goto out;
+
+       ret = __sseu_finish(i915, name, flags, ctx, kctx, engine, obj,
+                           hweight32(sseu.slice_mask), spin);
+
+out:
+       if (spin) {
+               igt_spinner_end(spin);
+               igt_spinner_fini(spin);
+               kfree(spin);
+       }
+
+       kernel_context_close(kctx);
+
+       return ret;
+}
+
+static int
+__igt_ctx_sseu(struct drm_i915_private *i915,
+              const char *name,
+              unsigned int flags)
+{
+       struct intel_sseu default_sseu = intel_device_default_sseu(i915);
+       struct intel_engine_cs *engine = i915->engine[RCS];
+       struct drm_i915_gem_object *obj;
+       struct i915_gem_context *ctx;
+       struct intel_sseu pg_sseu;
+       intel_wakeref_t wakeref;
+       struct drm_file *file;
+       int ret;
+
+       if (INTEL_GEN(i915) < 9)
+               return 0;
+
+       if (!RUNTIME_INFO(i915)->sseu.has_slice_pg)
+               return 0;
+
+       if (hweight32(default_sseu.slice_mask) < 2)
+               return 0;
+
+       /*
+        * Gen11 VME friendly power-gated configuration with half enabled
+        * sub-slices.
+        */
+       pg_sseu = default_sseu;
+       pg_sseu.slice_mask = 1;
+       pg_sseu.subslice_mask =
+               ~(~0 << (hweight32(default_sseu.subslice_mask) / 2));
+
+       pr_info("SSEU subtest '%s', flags=%x, def_slices=%u, pg_slices=%u\n",
+               name, flags, hweight32(default_sseu.slice_mask),
+               hweight32(pg_sseu.slice_mask));
+
+       file = mock_file(i915);
+       if (IS_ERR(file))
+               return PTR_ERR(file);
+
+       if (flags & TEST_RESET)
+               igt_global_reset_lock(i915);
+
+       mutex_lock(&i915->drm.struct_mutex);
+
+       ctx = i915_gem_create_context(i915, file->driver_priv);
+       if (IS_ERR(ctx)) {
+               ret = PTR_ERR(ctx);
+               goto out_unlock;
+       }
+
+       obj = i915_gem_object_create_internal(i915, PAGE_SIZE);
+       if (IS_ERR(obj)) {
+               ret = PTR_ERR(obj);
+               goto out_unlock;
+       }
+
+       wakeref = intel_runtime_pm_get(i915);
+
+       /* First set the default mask. */
+       ret = __sseu_test(i915, name, flags, ctx, engine, obj, default_sseu);
+       if (ret)
+               goto out_fail;
+
+       /* Then set a power-gated configuration. */
+       ret = __sseu_test(i915, name, flags, ctx, engine, obj, pg_sseu);
+       if (ret)
+               goto out_fail;
+
+       /* Back to defaults. */
+       ret = __sseu_test(i915, name, flags, ctx, engine, obj, default_sseu);
+       if (ret)
+               goto out_fail;
+
+       /* One last power-gated configuration for the road. */
+       ret = __sseu_test(i915, name, flags, ctx, engine, obj, pg_sseu);
+       if (ret)
+               goto out_fail;
+
+out_fail:
+       if (igt_flush_test(i915, I915_WAIT_LOCKED))
+               ret = -EIO;
+
+       i915_gem_object_put(obj);
+
+       intel_runtime_pm_put(i915, wakeref);
+
+out_unlock:
+       mutex_unlock(&i915->drm.struct_mutex);
+
+       if (flags & TEST_RESET)
+               igt_global_reset_unlock(i915);
+
+       mock_file_free(i915, file);
+
+       if (ret)
+               pr_err("%s: Failed with %d!\n", name, ret);
+
+       return ret;
+}
+
+static int igt_ctx_sseu(void *arg)
+{
+       struct {
+               const char *name;
+               unsigned int flags;
+       } *phase, phases[] = {
+               { .name = "basic", .flags = 0 },
+               { .name = "idle", .flags = TEST_IDLE },
+               { .name = "busy", .flags = TEST_BUSY },
+               { .name = "busy-reset", .flags = TEST_BUSY | TEST_RESET },
+               { .name = "busy-idle", .flags = TEST_BUSY | TEST_IDLE },
+               { .name = "reset-idle", .flags = TEST_RESET | TEST_IDLE },
+       };
+       unsigned int i;
+       int ret = 0;
+
+       for (i = 0, phase = phases; ret == 0 && i < ARRAY_SIZE(phases);
+            i++, phase++)
+               ret = __igt_ctx_sseu(arg, phase->name, phase->flags);
+
+       return ret;
+}
+
 static int igt_ctx_readonly(void *arg)
 {
        struct drm_i915_private *i915 = arg;
@@ -1162,6 +1628,7 @@ int i915_gem_context_live_selftests(struct drm_i915_private *dev_priv)
                SUBTEST(live_nop_switch),
                SUBTEST(igt_ctx_exec),
                SUBTEST(igt_ctx_readonly),
+               SUBTEST(igt_ctx_sseu),
                SUBTEST(igt_vm_isolation),
        };