299 lines
6.0 KiB
C
299 lines
6.0 KiB
C
|
// SPDX-License-Identifier: MIT
|
||
|
/*
|
||
|
* Copyright © 2020 Intel Corporation
|
||
|
*/
|
||
|
|
||
|
#include "intel_engine_pm.h"
|
||
|
#include "selftests/igt_flush_test.h"
|
||
|
|
||
|
static struct i915_vma *create_wally(struct intel_engine_cs *engine)
|
||
|
{
|
||
|
struct drm_i915_gem_object *obj;
|
||
|
struct i915_vma *vma;
|
||
|
u32 *cs;
|
||
|
int err;
|
||
|
|
||
|
obj = i915_gem_object_create_internal(engine->i915, 4096);
|
||
|
if (IS_ERR(obj))
|
||
|
return ERR_CAST(obj);
|
||
|
|
||
|
vma = i915_vma_instance(obj, engine->gt->vm, NULL);
|
||
|
if (IS_ERR(vma)) {
|
||
|
i915_gem_object_put(obj);
|
||
|
return vma;
|
||
|
}
|
||
|
|
||
|
err = i915_vma_pin(vma, 0, 0, PIN_USER | PIN_HIGH);
|
||
|
if (err) {
|
||
|
i915_gem_object_put(obj);
|
||
|
return ERR_PTR(err);
|
||
|
}
|
||
|
|
||
|
err = i915_vma_sync(vma);
|
||
|
if (err) {
|
||
|
i915_gem_object_put(obj);
|
||
|
return ERR_PTR(err);
|
||
|
}
|
||
|
|
||
|
cs = i915_gem_object_pin_map_unlocked(obj, I915_MAP_WC);
|
||
|
if (IS_ERR(cs)) {
|
||
|
i915_gem_object_put(obj);
|
||
|
return ERR_CAST(cs);
|
||
|
}
|
||
|
|
||
|
if (GRAPHICS_VER(engine->i915) >= 6) {
|
||
|
*cs++ = MI_STORE_DWORD_IMM_GEN4;
|
||
|
*cs++ = 0;
|
||
|
} else if (GRAPHICS_VER(engine->i915) >= 4) {
|
||
|
*cs++ = MI_STORE_DWORD_IMM_GEN4 | MI_USE_GGTT;
|
||
|
*cs++ = 0;
|
||
|
} else {
|
||
|
*cs++ = MI_STORE_DWORD_IMM | MI_MEM_VIRTUAL;
|
||
|
}
|
||
|
*cs++ = vma->node.start + 4000;
|
||
|
*cs++ = STACK_MAGIC;
|
||
|
|
||
|
*cs++ = MI_BATCH_BUFFER_END;
|
||
|
|
||
|
i915_gem_object_flush_map(obj);
|
||
|
i915_gem_object_unpin_map(obj);
|
||
|
|
||
|
vma->private = intel_context_create(engine); /* dummy residuals */
|
||
|
if (IS_ERR(vma->private)) {
|
||
|
vma = ERR_CAST(vma->private);
|
||
|
i915_gem_object_put(obj);
|
||
|
}
|
||
|
|
||
|
return vma;
|
||
|
}
|
||
|
|
||
|
static int context_sync(struct intel_context *ce)
|
||
|
{
|
||
|
struct i915_request *rq;
|
||
|
int err = 0;
|
||
|
|
||
|
rq = intel_context_create_request(ce);
|
||
|
if (IS_ERR(rq))
|
||
|
return PTR_ERR(rq);
|
||
|
|
||
|
i915_request_get(rq);
|
||
|
i915_request_add(rq);
|
||
|
|
||
|
if (i915_request_wait(rq, 0, HZ / 5) < 0)
|
||
|
err = -ETIME;
|
||
|
i915_request_put(rq);
|
||
|
|
||
|
return err;
|
||
|
}
|
||
|
|
||
|
static int new_context_sync(struct intel_engine_cs *engine)
|
||
|
{
|
||
|
struct intel_context *ce;
|
||
|
int err;
|
||
|
|
||
|
ce = intel_context_create(engine);
|
||
|
if (IS_ERR(ce))
|
||
|
return PTR_ERR(ce);
|
||
|
|
||
|
err = context_sync(ce);
|
||
|
intel_context_put(ce);
|
||
|
|
||
|
return err;
|
||
|
}
|
||
|
|
||
|
static int mixed_contexts_sync(struct intel_engine_cs *engine, u32 *result)
|
||
|
{
|
||
|
int pass;
|
||
|
int err;
|
||
|
|
||
|
for (pass = 0; pass < 2; pass++) {
|
||
|
WRITE_ONCE(*result, 0);
|
||
|
err = context_sync(engine->kernel_context);
|
||
|
if (err || READ_ONCE(*result)) {
|
||
|
if (!err) {
|
||
|
pr_err("pass[%d] wa_bb emitted for the kernel context\n",
|
||
|
pass);
|
||
|
err = -EINVAL;
|
||
|
}
|
||
|
return err;
|
||
|
}
|
||
|
|
||
|
WRITE_ONCE(*result, 0);
|
||
|
err = new_context_sync(engine);
|
||
|
if (READ_ONCE(*result) != STACK_MAGIC) {
|
||
|
if (!err) {
|
||
|
pr_err("pass[%d] wa_bb *NOT* emitted after the kernel context\n",
|
||
|
pass);
|
||
|
err = -EINVAL;
|
||
|
}
|
||
|
return err;
|
||
|
}
|
||
|
|
||
|
WRITE_ONCE(*result, 0);
|
||
|
err = new_context_sync(engine);
|
||
|
if (READ_ONCE(*result) != STACK_MAGIC) {
|
||
|
if (!err) {
|
||
|
pr_err("pass[%d] wa_bb *NOT* emitted for the user context switch\n",
|
||
|
pass);
|
||
|
err = -EINVAL;
|
||
|
}
|
||
|
return err;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
return 0;
|
||
|
}
|
||
|
|
||
|
static int double_context_sync_00(struct intel_engine_cs *engine, u32 *result)
|
||
|
{
|
||
|
struct intel_context *ce;
|
||
|
int err, i;
|
||
|
|
||
|
ce = intel_context_create(engine);
|
||
|
if (IS_ERR(ce))
|
||
|
return PTR_ERR(ce);
|
||
|
|
||
|
for (i = 0; i < 2; i++) {
|
||
|
WRITE_ONCE(*result, 0);
|
||
|
err = context_sync(ce);
|
||
|
if (err)
|
||
|
break;
|
||
|
}
|
||
|
intel_context_put(ce);
|
||
|
if (err)
|
||
|
return err;
|
||
|
|
||
|
if (READ_ONCE(*result)) {
|
||
|
pr_err("wa_bb emitted between the same user context\n");
|
||
|
return -EINVAL;
|
||
|
}
|
||
|
|
||
|
return 0;
|
||
|
}
|
||
|
|
||
|
static int kernel_context_sync_00(struct intel_engine_cs *engine, u32 *result)
|
||
|
{
|
||
|
struct intel_context *ce;
|
||
|
int err, i;
|
||
|
|
||
|
ce = intel_context_create(engine);
|
||
|
if (IS_ERR(ce))
|
||
|
return PTR_ERR(ce);
|
||
|
|
||
|
for (i = 0; i < 2; i++) {
|
||
|
WRITE_ONCE(*result, 0);
|
||
|
err = context_sync(ce);
|
||
|
if (err)
|
||
|
break;
|
||
|
|
||
|
err = context_sync(engine->kernel_context);
|
||
|
if (err)
|
||
|
break;
|
||
|
}
|
||
|
intel_context_put(ce);
|
||
|
if (err)
|
||
|
return err;
|
||
|
|
||
|
if (READ_ONCE(*result)) {
|
||
|
pr_err("wa_bb emitted between the same user context [with intervening kernel]\n");
|
||
|
return -EINVAL;
|
||
|
}
|
||
|
|
||
|
return 0;
|
||
|
}
|
||
|
|
||
|
static int __live_ctx_switch_wa(struct intel_engine_cs *engine)
|
||
|
{
|
||
|
struct i915_vma *bb;
|
||
|
u32 *result;
|
||
|
int err;
|
||
|
|
||
|
bb = create_wally(engine);
|
||
|
if (IS_ERR(bb))
|
||
|
return PTR_ERR(bb);
|
||
|
|
||
|
result = i915_gem_object_pin_map_unlocked(bb->obj, I915_MAP_WC);
|
||
|
if (IS_ERR(result)) {
|
||
|
intel_context_put(bb->private);
|
||
|
i915_vma_unpin_and_release(&bb, 0);
|
||
|
return PTR_ERR(result);
|
||
|
}
|
||
|
result += 1000;
|
||
|
|
||
|
engine->wa_ctx.vma = bb;
|
||
|
|
||
|
err = mixed_contexts_sync(engine, result);
|
||
|
if (err)
|
||
|
goto out;
|
||
|
|
||
|
err = double_context_sync_00(engine, result);
|
||
|
if (err)
|
||
|
goto out;
|
||
|
|
||
|
err = kernel_context_sync_00(engine, result);
|
||
|
if (err)
|
||
|
goto out;
|
||
|
|
||
|
out:
|
||
|
intel_context_put(engine->wa_ctx.vma->private);
|
||
|
i915_vma_unpin_and_release(&engine->wa_ctx.vma, I915_VMA_RELEASE_MAP);
|
||
|
return err;
|
||
|
}
|
||
|
|
||
|
static int live_ctx_switch_wa(void *arg)
|
||
|
{
|
||
|
struct intel_gt *gt = arg;
|
||
|
struct intel_engine_cs *engine;
|
||
|
enum intel_engine_id id;
|
||
|
|
||
|
/*
|
||
|
* Exercise the inter-context wa batch.
|
||
|
*
|
||
|
* Between each user context we run a wa batch, and since it may
|
||
|
* have implications for user visible state, we have to check that
|
||
|
* we do actually execute it.
|
||
|
*
|
||
|
* The trick we use is to replace the normal wa batch with a custom
|
||
|
* one that writes to a marker within it, and we can then look for
|
||
|
* that marker to confirm if the batch was run when we expect it,
|
||
|
* and equally important it was wasn't run when we don't!
|
||
|
*/
|
||
|
|
||
|
for_each_engine(engine, gt, id) {
|
||
|
struct i915_vma *saved_wa;
|
||
|
int err;
|
||
|
|
||
|
if (!intel_engine_can_store_dword(engine))
|
||
|
continue;
|
||
|
|
||
|
if (IS_GRAPHICS_VER(gt->i915, 4, 5))
|
||
|
continue; /* MI_STORE_DWORD is privileged! */
|
||
|
|
||
|
saved_wa = fetch_and_zero(&engine->wa_ctx.vma);
|
||
|
|
||
|
intel_engine_pm_get(engine);
|
||
|
err = __live_ctx_switch_wa(engine);
|
||
|
intel_engine_pm_put(engine);
|
||
|
if (igt_flush_test(gt->i915))
|
||
|
err = -EIO;
|
||
|
|
||
|
engine->wa_ctx.vma = saved_wa;
|
||
|
if (err)
|
||
|
return err;
|
||
|
}
|
||
|
|
||
|
return 0;
|
||
|
}
|
||
|
|
||
|
int intel_ring_submission_live_selftests(struct drm_i915_private *i915)
|
||
|
{
|
||
|
static const struct i915_subtest tests[] = {
|
||
|
SUBTEST(live_ctx_switch_wa),
|
||
|
};
|
||
|
|
||
|
if (to_gt(i915)->submission_method > INTEL_SUBMISSION_RING)
|
||
|
return 0;
|
||
|
|
||
|
return intel_gt_live_subtests(tests, to_gt(i915));
|
||
|
}
|