Commit e7aa531e authored by Chris Wilson's avatar Chris Wilson

drm/i915/selftest: Analyse timestamp behaviour across context switches

Check that the CTX_TIMESTAMP is monotonic across context save/restore
and upon preemption.

References: https://gitlab.freedesktop.org/drm/intel/issues/1233Signed-off-by: default avatarChris Wilson <chris@chris-wilson.co.uk>
Reviewed-by: default avatarRamalingam C <ramalingam.c@intel.com>
Link: https://patchwork.freedesktop.org/patch/msgid/20200219112004.1412791-1-chris@chris-wilson.co.uk
parent b04002f4
...@@ -3985,6 +3985,36 @@ static void hexdump(const void *buf, size_t len) ...@@ -3985,6 +3985,36 @@ static void hexdump(const void *buf, size_t len)
} }
} }
static int emit_semaphore_signal(struct intel_context *ce, void *slot)
{
const u32 offset =
i915_ggtt_offset(ce->engine->status_page.vma) +
offset_in_page(slot);
struct i915_request *rq;
u32 *cs;
rq = intel_context_create_request(ce);
if (IS_ERR(rq))
return PTR_ERR(rq);
cs = intel_ring_begin(rq, 4);
if (IS_ERR(cs)) {
i915_request_add(rq);
return PTR_ERR(cs);
}
*cs++ = MI_STORE_DWORD_IMM_GEN4 | MI_USE_GGTT;
*cs++ = offset;
*cs++ = 0;
*cs++ = 1;
intel_ring_advance(rq, cs);
rq->sched.attr.priority = I915_PRIORITY_BARRIER;
i915_request_add(rq);
return 0;
}
static int live_lrc_layout(void *arg) static int live_lrc_layout(void *arg)
{ {
struct intel_gt *gt = arg; struct intel_gt *gt = arg;
...@@ -4455,6 +4485,204 @@ static int live_gpr_clear(void *arg) ...@@ -4455,6 +4485,204 @@ static int live_gpr_clear(void *arg)
return err; return err;
} }
static struct i915_request *
create_timestamp(struct intel_context *ce, void *slot, int idx)
{
const u32 offset =
i915_ggtt_offset(ce->engine->status_page.vma) +
offset_in_page(slot);
struct i915_request *rq;
u32 *cs;
int err;
rq = intel_context_create_request(ce);
if (IS_ERR(rq))
return rq;
cs = intel_ring_begin(rq, 10);
if (IS_ERR(cs)) {
err = PTR_ERR(cs);
goto err;
}
*cs++ = MI_ARB_ON_OFF | MI_ARB_ENABLE;
*cs++ = MI_NOOP;
*cs++ = MI_SEMAPHORE_WAIT |
MI_SEMAPHORE_GLOBAL_GTT |
MI_SEMAPHORE_POLL |
MI_SEMAPHORE_SAD_NEQ_SDD;
*cs++ = 0;
*cs++ = offset;
*cs++ = 0;
*cs++ = MI_STORE_REGISTER_MEM_GEN8 | MI_USE_GGTT;
*cs++ = i915_mmio_reg_offset(RING_CTX_TIMESTAMP(rq->engine->mmio_base));
*cs++ = offset + idx * sizeof(u32);
*cs++ = 0;
intel_ring_advance(rq, cs);
rq->sched.attr.priority = I915_PRIORITY_MASK;
err = 0;
err:
i915_request_get(rq);
i915_request_add(rq);
if (err) {
i915_request_put(rq);
return ERR_PTR(err);
}
return rq;
}
struct lrc_timestamp {
struct intel_engine_cs *engine;
struct intel_context *ce[2];
u32 poison;
};
static bool timestamp_advanced(u32 start, u32 end)
{
return (s32)(end - start) > 0;
}
static int __lrc_timestamp(const struct lrc_timestamp *arg, bool preempt)
{
u32 *slot = memset32(arg->engine->status_page.addr + 1000, 0, 4);
struct i915_request *rq;
u32 timestamp;
int err = 0;
arg->ce[0]->lrc_reg_state[CTX_TIMESTAMP] = arg->poison;
rq = create_timestamp(arg->ce[0], slot, 1);
if (IS_ERR(rq))
return PTR_ERR(rq);
err = wait_for_submit(rq->engine, rq, HZ / 2);
if (err)
goto err;
if (preempt) {
arg->ce[1]->lrc_reg_state[CTX_TIMESTAMP] = 0xdeadbeef;
err = emit_semaphore_signal(arg->ce[1], slot);
if (err)
goto err;
} else {
slot[0] = 1;
wmb();
}
if (i915_request_wait(rq, 0, HZ / 2) < 0) {
err = -ETIME;
goto err;
}
/* and wait for switch to kernel */
if (igt_flush_test(arg->engine->i915)) {
err = -EIO;
goto err;
}
rmb();
if (!timestamp_advanced(arg->poison, slot[1])) {
pr_err("%s(%s): invalid timestamp on restore, context:%x, request:%x\n",
arg->engine->name, preempt ? "preempt" : "simple",
arg->poison, slot[1]);
err = -EINVAL;
}
timestamp = READ_ONCE(arg->ce[0]->lrc_reg_state[CTX_TIMESTAMP]);
if (!timestamp_advanced(slot[1], timestamp)) {
pr_err("%s(%s): invalid timestamp on save, request:%x, context:%x\n",
arg->engine->name, preempt ? "preempt" : "simple",
slot[1], timestamp);
err = -EINVAL;
}
err:
memset32(slot, -1, 4);
i915_request_put(rq);
return err;
}
static int live_lrc_timestamp(void *arg)
{
struct intel_gt *gt = arg;
enum intel_engine_id id;
struct lrc_timestamp data;
const u32 poison[] = {
0,
S32_MAX,
(u32)S32_MAX + 1,
U32_MAX,
};
/*
* We want to verify that the timestamp is saved and restore across
* context switches and is monotonic.
*
* So we do this with a little bit of LRC poisoning to check various
* boundary conditions, and see what happens if we preempt the context
* with a second request (carrying more poison into the timestamp).
*/
for_each_engine(data.engine, gt, id) {
unsigned long heartbeat;
int i, err = 0;
engine_heartbeat_disable(data.engine, &heartbeat);
for (i = 0; i < ARRAY_SIZE(data.ce); i++) {
struct intel_context *tmp;
tmp = intel_context_create(data.engine);
if (IS_ERR(tmp)) {
err = PTR_ERR(tmp);
goto err;
}
err = intel_context_pin(tmp);
if (err) {
intel_context_put(tmp);
goto err;
}
data.ce[i] = tmp;
}
for (i = 0; i < ARRAY_SIZE(poison); i++) {
data.poison = poison[i];
err = __lrc_timestamp(&data, false);
if (err)
break;
err = __lrc_timestamp(&data, true);
if (err)
break;
}
err:
engine_heartbeat_enable(data.engine, heartbeat);
for (i = 0; i < ARRAY_SIZE(data.ce); i++) {
if (!data.ce[i])
break;
intel_context_unpin(data.ce[i]);
intel_context_put(data.ce[i]);
}
if (igt_flush_test(gt->i915))
err = -EIO;
if (err)
return err;
}
return 0;
}
static int __live_pphwsp_runtime(struct intel_engine_cs *engine) static int __live_pphwsp_runtime(struct intel_engine_cs *engine)
{ {
struct intel_context *ce; struct intel_context *ce;
...@@ -4552,6 +4780,7 @@ int intel_lrc_live_selftests(struct drm_i915_private *i915) ...@@ -4552,6 +4780,7 @@ int intel_lrc_live_selftests(struct drm_i915_private *i915)
SUBTEST(live_lrc_fixed), SUBTEST(live_lrc_fixed),
SUBTEST(live_lrc_state), SUBTEST(live_lrc_state),
SUBTEST(live_gpr_clear), SUBTEST(live_gpr_clear),
SUBTEST(live_lrc_timestamp),
SUBTEST(live_pphwsp_runtime), SUBTEST(live_pphwsp_runtime),
}; };
......
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