[Intel-gfx] [PATCH 7/7] drm/i915/selftests: Context SSEU reconfiguration tests

Chris Wilson chris at chris-wilson.co.uk
Fri Dec 14 13:22:19 UTC 2018


Quoting Tvrtko Ursulin (2018-12-14 12:34:49)
> +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_address_space *vm;
> +       struct i915_request *rq;
> +       struct i915_vma *batch;
> +       struct i915_vma *vma;
> +       int err;
> +
> +       GEM_BUG_ON(!ctx->ppgtt);
> +       GEM_BUG_ON(!intel_engine_can_store_dword(engine));
> +
> +       vm = &ctx->ppgtt->vm;
> +
> +       vma = i915_vma_instance(obj, 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_HIGH | PIN_USER);
> +       if (err)
> +               return err;
> +
> +       batch = rpcs_query_batch(vma);
> +       if (IS_ERR(batch)) {
> +               err = PTR_ERR(batch);
> +               goto err_vma;
> +       }

Simpler would be to just emit the SRM into the ring. One less object+vma
to handle.

> +
> +       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      (1 << 0)
> +#define TEST_BUSY      (1 << 1)
> +#define TEST_RESET     (1 << 2)
> +
> +int
> +__i915_gem_context_reconfigure_sseu(struct i915_gem_context *ctx,
> +                                   struct intel_engine_cs *engine,
> +                                   struct intel_sseu sseu);
> +
> +static int
> +__pre_set(struct drm_i915_private *i915,
> +         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("Spinner failed to start!\n");
> +                       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);
> +       if (ret <= 0)
> +               return ret < 0 ? ret : -ETIME;
> +
> +       i915_request_put(rq);
> +
> +       buf = i915_gem_object_pin_map(obj, I915_MAP_WB);
> +       if (IS_ERR(buf)) {
> +               ret = PTR_ERR(buf);
> +               return ret;
> +       }

You can replace the get + request_wait + put with
i915_gem_obj_prepare_shmem_read. Yes the _shmem_ there is misleading.

> +
> +       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 void __err_rpcs(u32 rpcs, unsigned int slices)
> +{
> +       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) ? "*" : "");
> +}
> +
> +static int
> +__post_set(struct drm_i915_private *i915,
> +          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);
> +       if (ret < 0) {
> +               goto out;
> +       } else if (ret != expected) {
> +               pr_err("Context slice count %d is not %u!\n",
> +                      ret, expected);

Include stage + phase name (idle, busy, reset, etc) in the error message.

> +               __err_rpcs(rpcs, ret);
> +               ret = -EINVAL;
> +               goto out;
> +       } else {
> +               ret = 0;
> +       }
> +
> +       ret = __read_slice_count(i915, kctx, engine, obj, NULL, &rpcs);
> +       if (ret < 0) {
> +               goto out;
> +       } else if (ret != slices) {
> +               pr_err("Kernel context slice count %d is not %u!\n",
> +                      ret, slices);
> +               __err_rpcs(rpcs, ret);
> +               ret = -EINVAL;
> +               goto out;
> +       } else {
> +               ret = 0;
> +       }
> +
> +out:
> +       if (spin)
> +               igt_spinner_end(spin);
> +
> +       if (flags & TEST_IDLE) {
> +               ret = i915_gem_wait_for_idle(i915,
> +                                            I915_WAIT_LOCKED,
> +                                            MAX_SCHEDULE_TIMEOUT);
> +               if (ret)
> +                       return ret;

Do we want to go full idle (rpm) here? That shouldn't matter for ctx
images, so forget that.

Hmm, but we would like to kickstart the powercontext I guess. I wonder
if we can call __i915_gem_park directly...

> +
> +               ret = __read_slice_count(i915, ctx, engine, obj, NULL, &rpcs);
> +               if (ret < 0) {
> +                       return ret;
> +               } else if (ret != expected) {
> +                       pr_err("Context slice count %d is not %u after idle!\n",
> +                              ret, expected);
> +                       __err_rpcs(rpcs, ret);
> +                       return -EINVAL;
> +               } else {
> +                       ret = 0;
> +               }
> +       }
> +
> +       return ret;

Ok, that should check across context switches and restoration from
reset. And reset should work so long as rpcs SRM as separate requests
from user batches.

> +
> +}
> +
> +static int
> +__sseu_test(struct drm_i915_private *i915,
> +           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 = __pre_set(i915, flags, ctx, engine, &spin);

__sseu_prepare & __sseu_finish

Just thinking that pre_set is a little generic and you'll wish you
called it something else next time you add a selftest.

> +       if (ret)
> +               goto out;
> +
> +       ret = __i915_gem_context_reconfigure_sseu(ctx, engine, sseu);
> +       if (ret)
> +               goto out;
> +

> +       ret = __post_set(i915, 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, 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;
> +       struct drm_file *file;
> +       int ret;
> +
> +       if (INTEL_GEN(i915) < 9)
> +               return 0;
> +
> +       if (!INTEL_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("SSE subtest flags=%x, def_slices=%u, pg_slices=%u\n",
> +               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;
> +       }
> +
> +       intel_runtime_pm_get(i915);
> +
> +       /* First set the default mask. */
> +       ret = __sseu_test(i915, flags, ctx, engine, obj, default_sseu);
> +       if (ret)
> +               goto out_fail;
> +
> +       /* Then set a power-gated configuration. */
> +       ret = __sseu_test(i915, flags, ctx, engine, obj, pg_sseu);
> +       if (ret)
> +               goto out_fail;
> +
> +       /* Back to defaults. */
> +       ret = __sseu_test(i915, flags, ctx, engine, obj, default_sseu);
> +       if (ret)
> +               goto out_fail;
> +
> +       /* One last power-gated configuration for the road. */
> +       ret = __sseu_test(i915, 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);
> +
> +out_unlock:
> +       mutex_unlock(&i915->drm.struct_mutex);
> +
> +       if (flags & TEST_RESET)
> +               igt_global_reset_unlock(i915);
> +
> +       mock_file_free(i915, file);
> +
> +       return ret;
> +}
> +
> +static int igt_ctx_sseu(void *arg)
> +{
> +       return __igt_ctx_sseu(arg, 0);
> +}
> +
> +static int igt_ctx_sseu_idle(void *arg)
> +{
> +       return __igt_ctx_sseu(arg, TEST_IDLE);
> +}
> +
> +static int igt_ctx_sseu_busy(void *arg)
> +{
> +       return __igt_ctx_sseu(arg, TEST_BUSY);
> +}
> +
> +static int igt_ctx_sseu_busy_reset(void *arg)
> +{
> +       return __igt_ctx_sseu(arg, TEST_BUSY | TEST_RESET);
> +}
> +
> +static int igt_ctx_sseu_busy_idle(void *arg)
> +{
> +       return __igt_ctx_sseu(arg, TEST_BUSY | TEST_IDLE);
> +}
> +
> +static int igt_ctx_sseu_reset_idle(void *arg)
> +{
> +       return __igt_ctx_sseu(arg, TEST_RESET | TEST_IDLE);
> +}

Looks ok. The only point I would like to reiterate is to pass down the
subtest names so that we can include them in the error messages for
easier comprehension.
-Chris


More information about the Intel-gfx mailing list