mirror of
https://github.com/AuxXxilium/linux_dsm_epyc7002.git
synced 2024-12-28 11:18:45 +07:00
8ab3a3812a
In commit5ba32c7be8
("drm/i915/execlists: Always force a context reload when rewinding RING_TAIL"), we placed the check for rewinding a context on actually submitting the next request in that context. This was so that we only had to check once, and could do so with precision avoiding as many forced restores as possible. For example, to ensure that we can resubmit the same request a couple of times, we include a small wa_tail such that on the next submission, the ring->tail will appear to move forwards when resubmitting the same request. This is very common as it will happen for every lite-restore to fill the second port after a context switch. However, intel_ring_direction() is limited in precision to movements of upto half the ring size. The consequence being that if we tried to unwind many requests, we could exceed half the ring and flip the sense of the direction, so missing a force restore. As no request can be greater than half the ring (i.e. 2048 bytes in the smallest case), we can check for rollback incrementally. As we check against the tail that would be submitted, we do not lose any sensitivity and allow lite restores for the simple case. We still need to double check upon submitting the context, to allow for multiple preemptions and resubmissions. Fixes:5ba32c7be8
("drm/i915/execlists: Always force a context reload when rewinding RING_TAIL") Signed-off-by: Chris Wilson <chris@chris-wilson.co.uk> Cc: Mika Kuoppala <mika.kuoppala@linux.intel.com> Cc: <stable@vger.kernel.org> # v5.4+ Reviewed-by: Bruce Chang <yu.bruce.chang@intel.com> Reviewed-by: Mika Kuoppala <mika.kuoppala@linux.intel.com> Link: https://patchwork.freedesktop.org/patch/msgid/20200609151723.12971-1-chris@chris-wilson.co.uk (cherry picked from commite36ba817fa
) Signed-off-by: Joonas Lahtinen <joonas.lahtinen@linux.intel.com>
446 lines
9.0 KiB
C
446 lines
9.0 KiB
C
/*
|
|
* SPDX-License-Identifier: MIT
|
|
*
|
|
* Copyright © 2019 Intel Corporation
|
|
*/
|
|
|
|
#include "gt/intel_engine_pm.h"
|
|
#include "i915_selftest.h"
|
|
|
|
#include "gem/selftests/mock_context.h"
|
|
#include "selftests/igt_reset.h"
|
|
#include "selftests/igt_spinner.h"
|
|
|
|
struct live_mocs {
|
|
struct drm_i915_mocs_table mocs;
|
|
struct drm_i915_mocs_table l3cc;
|
|
struct i915_vma *scratch;
|
|
void *vaddr;
|
|
};
|
|
|
|
static struct intel_context *mocs_context_create(struct intel_engine_cs *engine)
|
|
{
|
|
struct intel_context *ce;
|
|
|
|
ce = intel_context_create(engine);
|
|
if (IS_ERR(ce))
|
|
return ce;
|
|
|
|
/* We build large requests to read the registers from the ring */
|
|
ce->ring = __intel_context_ring_size(SZ_16K);
|
|
|
|
return ce;
|
|
}
|
|
|
|
static int request_add_sync(struct i915_request *rq, int err)
|
|
{
|
|
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 request_add_spin(struct i915_request *rq, struct igt_spinner *spin)
|
|
{
|
|
int err = 0;
|
|
|
|
i915_request_get(rq);
|
|
i915_request_add(rq);
|
|
if (spin && !igt_wait_for_spinner(spin, rq))
|
|
err = -ETIME;
|
|
i915_request_put(rq);
|
|
|
|
return err;
|
|
}
|
|
|
|
static struct i915_vma *create_scratch(struct intel_gt *gt)
|
|
{
|
|
struct drm_i915_gem_object *obj;
|
|
struct i915_vma *vma;
|
|
int err;
|
|
|
|
obj = i915_gem_object_create_internal(gt->i915, PAGE_SIZE);
|
|
if (IS_ERR(obj))
|
|
return ERR_CAST(obj);
|
|
|
|
i915_gem_object_set_cache_coherency(obj, I915_CACHING_CACHED);
|
|
|
|
vma = i915_vma_instance(obj, >->ggtt->vm, NULL);
|
|
if (IS_ERR(vma)) {
|
|
i915_gem_object_put(obj);
|
|
return vma;
|
|
}
|
|
|
|
err = i915_vma_pin(vma, 0, 0, PIN_GLOBAL);
|
|
if (err) {
|
|
i915_gem_object_put(obj);
|
|
return ERR_PTR(err);
|
|
}
|
|
|
|
return vma;
|
|
}
|
|
|
|
static int live_mocs_init(struct live_mocs *arg, struct intel_gt *gt)
|
|
{
|
|
struct drm_i915_mocs_table table;
|
|
unsigned int flags;
|
|
int err;
|
|
|
|
memset(arg, 0, sizeof(*arg));
|
|
|
|
flags = get_mocs_settings(gt->i915, &table);
|
|
if (!flags)
|
|
return -EINVAL;
|
|
|
|
if (flags & HAS_RENDER_L3CC)
|
|
arg->l3cc = table;
|
|
|
|
if (flags & (HAS_GLOBAL_MOCS | HAS_ENGINE_MOCS))
|
|
arg->mocs = table;
|
|
|
|
arg->scratch = create_scratch(gt);
|
|
if (IS_ERR(arg->scratch))
|
|
return PTR_ERR(arg->scratch);
|
|
|
|
arg->vaddr = i915_gem_object_pin_map(arg->scratch->obj, I915_MAP_WB);
|
|
if (IS_ERR(arg->vaddr)) {
|
|
err = PTR_ERR(arg->vaddr);
|
|
goto err_scratch;
|
|
}
|
|
|
|
return 0;
|
|
|
|
err_scratch:
|
|
i915_vma_unpin_and_release(&arg->scratch, 0);
|
|
return err;
|
|
}
|
|
|
|
static void live_mocs_fini(struct live_mocs *arg)
|
|
{
|
|
i915_vma_unpin_and_release(&arg->scratch, I915_VMA_RELEASE_MAP);
|
|
}
|
|
|
|
static int read_regs(struct i915_request *rq,
|
|
u32 addr, unsigned int count,
|
|
uint32_t *offset)
|
|
{
|
|
unsigned int i;
|
|
u32 *cs;
|
|
|
|
GEM_BUG_ON(!IS_ALIGNED(*offset, sizeof(u32)));
|
|
|
|
cs = intel_ring_begin(rq, 4 * count);
|
|
if (IS_ERR(cs))
|
|
return PTR_ERR(cs);
|
|
|
|
for (i = 0; i < count; i++) {
|
|
*cs++ = MI_STORE_REGISTER_MEM_GEN8 | MI_USE_GGTT;
|
|
*cs++ = addr;
|
|
*cs++ = *offset;
|
|
*cs++ = 0;
|
|
|
|
addr += sizeof(u32);
|
|
*offset += sizeof(u32);
|
|
}
|
|
|
|
intel_ring_advance(rq, cs);
|
|
|
|
return 0;
|
|
}
|
|
|
|
static int read_mocs_table(struct i915_request *rq,
|
|
const struct drm_i915_mocs_table *table,
|
|
uint32_t *offset)
|
|
{
|
|
u32 addr;
|
|
|
|
if (HAS_GLOBAL_MOCS_REGISTERS(rq->i915))
|
|
addr = global_mocs_offset();
|
|
else
|
|
addr = mocs_offset(rq->engine);
|
|
|
|
return read_regs(rq, addr, table->n_entries, offset);
|
|
}
|
|
|
|
static int read_l3cc_table(struct i915_request *rq,
|
|
const struct drm_i915_mocs_table *table,
|
|
uint32_t *offset)
|
|
{
|
|
u32 addr = i915_mmio_reg_offset(GEN9_LNCFCMOCS(0));
|
|
|
|
return read_regs(rq, addr, (table->n_entries + 1) / 2, offset);
|
|
}
|
|
|
|
static int check_mocs_table(struct intel_engine_cs *engine,
|
|
const struct drm_i915_mocs_table *table,
|
|
uint32_t **vaddr)
|
|
{
|
|
unsigned int i;
|
|
u32 expect;
|
|
|
|
for_each_mocs(expect, table, i) {
|
|
if (**vaddr != expect) {
|
|
pr_err("%s: Invalid MOCS[%d] entry, found %08x, expected %08x\n",
|
|
engine->name, i, **vaddr, expect);
|
|
return -EINVAL;
|
|
}
|
|
++*vaddr;
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
static bool mcr_range(struct drm_i915_private *i915, u32 offset)
|
|
{
|
|
/*
|
|
* Registers in this range are affected by the MCR selector
|
|
* which only controls CPU initiated MMIO. Routing does not
|
|
* work for CS access so we cannot verify them on this path.
|
|
*/
|
|
return INTEL_GEN(i915) >= 8 && offset >= 0xb000 && offset <= 0xb4ff;
|
|
}
|
|
|
|
static int check_l3cc_table(struct intel_engine_cs *engine,
|
|
const struct drm_i915_mocs_table *table,
|
|
uint32_t **vaddr)
|
|
{
|
|
/* Can we read the MCR range 0xb00 directly? See intel_workarounds! */
|
|
u32 reg = i915_mmio_reg_offset(GEN9_LNCFCMOCS(0));
|
|
unsigned int i;
|
|
u32 expect;
|
|
|
|
for_each_l3cc(expect, table, i) {
|
|
if (!mcr_range(engine->i915, reg) && **vaddr != expect) {
|
|
pr_err("%s: Invalid L3CC[%d] entry, found %08x, expected %08x\n",
|
|
engine->name, i, **vaddr, expect);
|
|
return -EINVAL;
|
|
}
|
|
++*vaddr;
|
|
reg += 4;
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
static int check_mocs_engine(struct live_mocs *arg,
|
|
struct intel_context *ce)
|
|
{
|
|
struct i915_vma *vma = arg->scratch;
|
|
struct i915_request *rq;
|
|
u32 offset;
|
|
u32 *vaddr;
|
|
int err;
|
|
|
|
memset32(arg->vaddr, STACK_MAGIC, PAGE_SIZE / sizeof(u32));
|
|
|
|
rq = intel_context_create_request(ce);
|
|
if (IS_ERR(rq))
|
|
return PTR_ERR(rq);
|
|
|
|
i915_vma_lock(vma);
|
|
err = i915_request_await_object(rq, vma->obj, true);
|
|
if (!err)
|
|
err = i915_vma_move_to_active(vma, rq, EXEC_OBJECT_WRITE);
|
|
i915_vma_unlock(vma);
|
|
|
|
/* Read the mocs tables back using SRM */
|
|
offset = i915_ggtt_offset(vma);
|
|
if (!err)
|
|
err = read_mocs_table(rq, &arg->mocs, &offset);
|
|
if (!err && ce->engine->class == RENDER_CLASS)
|
|
err = read_l3cc_table(rq, &arg->l3cc, &offset);
|
|
offset -= i915_ggtt_offset(vma);
|
|
GEM_BUG_ON(offset > PAGE_SIZE);
|
|
|
|
err = request_add_sync(rq, err);
|
|
if (err)
|
|
return err;
|
|
|
|
/* Compare the results against the expected tables */
|
|
vaddr = arg->vaddr;
|
|
if (!err)
|
|
err = check_mocs_table(ce->engine, &arg->mocs, &vaddr);
|
|
if (!err && ce->engine->class == RENDER_CLASS)
|
|
err = check_l3cc_table(ce->engine, &arg->l3cc, &vaddr);
|
|
if (err)
|
|
return err;
|
|
|
|
GEM_BUG_ON(arg->vaddr + offset != vaddr);
|
|
return 0;
|
|
}
|
|
|
|
static int live_mocs_kernel(void *arg)
|
|
{
|
|
struct intel_gt *gt = arg;
|
|
struct intel_engine_cs *engine;
|
|
enum intel_engine_id id;
|
|
struct live_mocs mocs;
|
|
int err;
|
|
|
|
/* Basic check the system is configured with the expected mocs table */
|
|
|
|
err = live_mocs_init(&mocs, gt);
|
|
if (err)
|
|
return err;
|
|
|
|
for_each_engine(engine, gt, id) {
|
|
intel_engine_pm_get(engine);
|
|
err = check_mocs_engine(&mocs, engine->kernel_context);
|
|
intel_engine_pm_put(engine);
|
|
if (err)
|
|
break;
|
|
}
|
|
|
|
live_mocs_fini(&mocs);
|
|
return err;
|
|
}
|
|
|
|
static int live_mocs_clean(void *arg)
|
|
{
|
|
struct intel_gt *gt = arg;
|
|
struct intel_engine_cs *engine;
|
|
enum intel_engine_id id;
|
|
struct live_mocs mocs;
|
|
int err;
|
|
|
|
/* Every new context should see the same mocs table */
|
|
|
|
err = live_mocs_init(&mocs, gt);
|
|
if (err)
|
|
return err;
|
|
|
|
for_each_engine(engine, gt, id) {
|
|
struct intel_context *ce;
|
|
|
|
ce = mocs_context_create(engine);
|
|
if (IS_ERR(ce)) {
|
|
err = PTR_ERR(ce);
|
|
break;
|
|
}
|
|
|
|
err = check_mocs_engine(&mocs, ce);
|
|
intel_context_put(ce);
|
|
if (err)
|
|
break;
|
|
}
|
|
|
|
live_mocs_fini(&mocs);
|
|
return err;
|
|
}
|
|
|
|
static int active_engine_reset(struct intel_context *ce,
|
|
const char *reason)
|
|
{
|
|
struct igt_spinner spin;
|
|
struct i915_request *rq;
|
|
int err;
|
|
|
|
err = igt_spinner_init(&spin, ce->engine->gt);
|
|
if (err)
|
|
return err;
|
|
|
|
rq = igt_spinner_create_request(&spin, ce, MI_NOOP);
|
|
if (IS_ERR(rq)) {
|
|
igt_spinner_fini(&spin);
|
|
return PTR_ERR(rq);
|
|
}
|
|
|
|
err = request_add_spin(rq, &spin);
|
|
if (err == 0)
|
|
err = intel_engine_reset(ce->engine, reason);
|
|
|
|
igt_spinner_end(&spin);
|
|
igt_spinner_fini(&spin);
|
|
|
|
return err;
|
|
}
|
|
|
|
static int __live_mocs_reset(struct live_mocs *mocs,
|
|
struct intel_context *ce)
|
|
{
|
|
int err;
|
|
|
|
err = intel_engine_reset(ce->engine, "mocs");
|
|
if (err)
|
|
return err;
|
|
|
|
err = check_mocs_engine(mocs, ce);
|
|
if (err)
|
|
return err;
|
|
|
|
err = active_engine_reset(ce, "mocs");
|
|
if (err)
|
|
return err;
|
|
|
|
err = check_mocs_engine(mocs, ce);
|
|
if (err)
|
|
return err;
|
|
|
|
intel_gt_reset(ce->engine->gt, ce->engine->mask, "mocs");
|
|
|
|
err = check_mocs_engine(mocs, ce);
|
|
if (err)
|
|
return err;
|
|
|
|
return 0;
|
|
}
|
|
|
|
static int live_mocs_reset(void *arg)
|
|
{
|
|
struct intel_gt *gt = arg;
|
|
struct intel_engine_cs *engine;
|
|
enum intel_engine_id id;
|
|
struct live_mocs mocs;
|
|
int err = 0;
|
|
|
|
/* Check the mocs setup is retained over per-engine and global resets */
|
|
|
|
if (!intel_has_reset_engine(gt))
|
|
return 0;
|
|
|
|
err = live_mocs_init(&mocs, gt);
|
|
if (err)
|
|
return err;
|
|
|
|
igt_global_reset_lock(gt);
|
|
for_each_engine(engine, gt, id) {
|
|
struct intel_context *ce;
|
|
|
|
ce = mocs_context_create(engine);
|
|
if (IS_ERR(ce)) {
|
|
err = PTR_ERR(ce);
|
|
break;
|
|
}
|
|
|
|
intel_engine_pm_get(engine);
|
|
err = __live_mocs_reset(&mocs, ce);
|
|
intel_engine_pm_put(engine);
|
|
|
|
intel_context_put(ce);
|
|
if (err)
|
|
break;
|
|
}
|
|
igt_global_reset_unlock(gt);
|
|
|
|
live_mocs_fini(&mocs);
|
|
return err;
|
|
}
|
|
|
|
int intel_mocs_live_selftests(struct drm_i915_private *i915)
|
|
{
|
|
static const struct i915_subtest tests[] = {
|
|
SUBTEST(live_mocs_kernel),
|
|
SUBTEST(live_mocs_clean),
|
|
SUBTEST(live_mocs_reset),
|
|
};
|
|
struct drm_i915_mocs_table table;
|
|
|
|
if (!get_mocs_settings(i915, &table))
|
|
return 0;
|
|
|
|
return intel_gt_live_subtests(tests, &i915->gt);
|
|
}
|