Commit 52954edd authored by Chris Wilson's avatar Chris Wilson

drm/i915: Allocate a status page for each timeline

Allocate a page for use as a status page by a group of timelines, as we
only need a dword of storage for each (rounded up to the cacheline for
safety) we can pack multiple timelines into the same page. Each timeline
will then be able to track its own HW seqno.

v2: Reuse the common per-engine HWSP for the solitary ringbuffer
timeline, so that we do not have to emit (using per-gen specialised
vfuncs) the breadcrumb into the distinct timeline HWSP and instead can
keep on using the common MI_STORE_DWORD_INDEX. However, to maintain the
sleight-of-hand for the global/per-context seqno switchover, we will
store both temporarily (and so use a custom offset for the shared timeline
HWSP until the switch over).

v3: Keep things simple and allocate a page for each timeline, page
sharing comes next.

v4: I was caught repeating the same MI_STORE_DWORD_IMM over and over
again in selftests.

v5: And caught red handed copying create timeline + check.
Signed-off-by: default avatarChris Wilson <chris@chris-wilson.co.uk>
Reviewed-by: default avatarTvrtko Ursulin <tvrtko.ursulin@intel.com>
Link: https://patchwork.freedesktop.org/patch/msgid/20190128181812.22804-3-chris@chris-wilson.co.uk
parent b18fe4be
...@@ -9,28 +9,78 @@ ...@@ -9,28 +9,78 @@
#include "i915_timeline.h" #include "i915_timeline.h"
#include "i915_syncmap.h" #include "i915_syncmap.h"
void i915_timeline_init(struct drm_i915_private *i915, static struct i915_vma *__hwsp_alloc(struct drm_i915_private *i915)
{
struct drm_i915_gem_object *obj;
struct i915_vma *vma;
obj = i915_gem_object_create_internal(i915, PAGE_SIZE);
if (IS_ERR(obj))
return ERR_CAST(obj);
i915_gem_object_set_cache_coherency(obj, I915_CACHE_LLC);
vma = i915_vma_instance(obj, &i915->ggtt.vm, NULL);
if (IS_ERR(vma))
i915_gem_object_put(obj);
return vma;
}
static int hwsp_alloc(struct i915_timeline *timeline)
{
struct i915_vma *vma;
vma = __hwsp_alloc(timeline->i915);
if (IS_ERR(vma))
return PTR_ERR(vma);
timeline->hwsp_ggtt = vma;
timeline->hwsp_offset = 0;
return 0;
}
int i915_timeline_init(struct drm_i915_private *i915,
struct i915_timeline *timeline, struct i915_timeline *timeline,
const char *name) const char *name,
struct i915_vma *global_hwsp)
{ {
struct i915_gt_timelines *gt = &i915->gt.timelines; struct i915_gt_timelines *gt = &i915->gt.timelines;
void *vaddr;
int err;
/* /*
* Ideally we want a set of engines on a single leaf as we expect * Ideally we want a set of engines on a single leaf as we expect
* to mostly be tracking synchronisation between engines. It is not * to mostly be tracking synchronisation between engines. It is not
* a huge issue if this is not the case, but we may want to mitigate * a huge issue if this is not the case, but we may want to mitigate
* any page crossing penalties if they become an issue. * any page crossing penalties if they become an issue.
*
* Called during early_init before we know how many engines there are.
*/ */
BUILD_BUG_ON(KSYNCMAP < I915_NUM_ENGINES); BUILD_BUG_ON(KSYNCMAP < I915_NUM_ENGINES);
timeline->i915 = i915; timeline->i915 = i915;
timeline->name = name; timeline->name = name;
timeline->pin_count = 0;
if (global_hwsp) {
timeline->hwsp_ggtt = i915_vma_get(global_hwsp);
timeline->hwsp_offset = I915_GEM_HWS_SEQNO_ADDR;
} else {
err = hwsp_alloc(timeline);
if (err)
return err;
}
mutex_lock(&gt->mutex); vaddr = i915_gem_object_pin_map(timeline->hwsp_ggtt->obj, I915_MAP_WB);
list_add(&timeline->link, &gt->list); if (IS_ERR(vaddr)) {
mutex_unlock(&gt->mutex); i915_vma_put(timeline->hwsp_ggtt);
return PTR_ERR(vaddr);
}
/* Called during early_init before we know how many engines there are */ timeline->hwsp_seqno =
memset(vaddr + timeline->hwsp_offset, 0, CACHELINE_BYTES);
timeline->fence_context = dma_fence_context_alloc(1); timeline->fence_context = dma_fence_context_alloc(1);
...@@ -40,6 +90,12 @@ void i915_timeline_init(struct drm_i915_private *i915, ...@@ -40,6 +90,12 @@ void i915_timeline_init(struct drm_i915_private *i915,
INIT_LIST_HEAD(&timeline->requests); INIT_LIST_HEAD(&timeline->requests);
i915_syncmap_init(&timeline->sync); i915_syncmap_init(&timeline->sync);
mutex_lock(&gt->mutex);
list_add(&timeline->link, &gt->list);
mutex_unlock(&gt->mutex);
return 0;
} }
void i915_timelines_init(struct drm_i915_private *i915) void i915_timelines_init(struct drm_i915_private *i915)
...@@ -85,6 +141,7 @@ void i915_timeline_fini(struct i915_timeline *timeline) ...@@ -85,6 +141,7 @@ void i915_timeline_fini(struct i915_timeline *timeline)
{ {
struct i915_gt_timelines *gt = &timeline->i915->gt.timelines; struct i915_gt_timelines *gt = &timeline->i915->gt.timelines;
GEM_BUG_ON(timeline->pin_count);
GEM_BUG_ON(!list_empty(&timeline->requests)); GEM_BUG_ON(!list_empty(&timeline->requests));
i915_syncmap_free(&timeline->sync); i915_syncmap_free(&timeline->sync);
...@@ -92,23 +149,69 @@ void i915_timeline_fini(struct i915_timeline *timeline) ...@@ -92,23 +149,69 @@ void i915_timeline_fini(struct i915_timeline *timeline)
mutex_lock(&gt->mutex); mutex_lock(&gt->mutex);
list_del(&timeline->link); list_del(&timeline->link);
mutex_unlock(&gt->mutex); mutex_unlock(&gt->mutex);
i915_gem_object_unpin_map(timeline->hwsp_ggtt->obj);
i915_vma_put(timeline->hwsp_ggtt);
} }
struct i915_timeline * struct i915_timeline *
i915_timeline_create(struct drm_i915_private *i915, const char *name) i915_timeline_create(struct drm_i915_private *i915,
const char *name,
struct i915_vma *global_hwsp)
{ {
struct i915_timeline *timeline; struct i915_timeline *timeline;
int err;
timeline = kzalloc(sizeof(*timeline), GFP_KERNEL); timeline = kzalloc(sizeof(*timeline), GFP_KERNEL);
if (!timeline) if (!timeline)
return ERR_PTR(-ENOMEM); return ERR_PTR(-ENOMEM);
i915_timeline_init(i915, timeline, name); err = i915_timeline_init(i915, timeline, name, global_hwsp);
if (err) {
kfree(timeline);
return ERR_PTR(err);
}
kref_init(&timeline->kref); kref_init(&timeline->kref);
return timeline; return timeline;
} }
int i915_timeline_pin(struct i915_timeline *tl)
{
int err;
if (tl->pin_count++)
return 0;
GEM_BUG_ON(!tl->pin_count);
err = i915_vma_pin(tl->hwsp_ggtt, 0, 0, PIN_GLOBAL | PIN_HIGH);
if (err)
goto unpin;
return 0;
unpin:
tl->pin_count = 0;
return err;
}
void i915_timeline_unpin(struct i915_timeline *tl)
{
GEM_BUG_ON(!tl->pin_count);
if (--tl->pin_count)
return;
/*
* Since this timeline is idle, all bariers upon which we were waiting
* must also be complete and so we can discard the last used barriers
* without loss of information.
*/
i915_syncmap_free(&tl->sync);
__i915_vma_unpin(tl->hwsp_ggtt);
}
void __i915_timeline_free(struct kref *kref) void __i915_timeline_free(struct kref *kref)
{ {
struct i915_timeline *timeline = struct i915_timeline *timeline =
......
...@@ -32,6 +32,8 @@ ...@@ -32,6 +32,8 @@
#include "i915_syncmap.h" #include "i915_syncmap.h"
#include "i915_utils.h" #include "i915_utils.h"
struct i915_vma;
struct i915_timeline { struct i915_timeline {
u64 fence_context; u64 fence_context;
u32 seqno; u32 seqno;
...@@ -40,6 +42,11 @@ struct i915_timeline { ...@@ -40,6 +42,11 @@ struct i915_timeline {
#define TIMELINE_CLIENT 0 /* default subclass */ #define TIMELINE_CLIENT 0 /* default subclass */
#define TIMELINE_ENGINE 1 #define TIMELINE_ENGINE 1
unsigned int pin_count;
const u32 *hwsp_seqno;
struct i915_vma *hwsp_ggtt;
u32 hwsp_offset;
/** /**
* List of breadcrumbs associated with GPU requests currently * List of breadcrumbs associated with GPU requests currently
* outstanding. * outstanding.
...@@ -71,9 +78,10 @@ struct i915_timeline { ...@@ -71,9 +78,10 @@ struct i915_timeline {
struct kref kref; struct kref kref;
}; };
void i915_timeline_init(struct drm_i915_private *i915, int i915_timeline_init(struct drm_i915_private *i915,
struct i915_timeline *tl, struct i915_timeline *tl,
const char *name); const char *name,
struct i915_vma *hwsp);
void i915_timeline_fini(struct i915_timeline *tl); void i915_timeline_fini(struct i915_timeline *tl);
static inline void static inline void
...@@ -96,7 +104,9 @@ i915_timeline_set_subclass(struct i915_timeline *timeline, ...@@ -96,7 +104,9 @@ i915_timeline_set_subclass(struct i915_timeline *timeline,
} }
struct i915_timeline * struct i915_timeline *
i915_timeline_create(struct drm_i915_private *i915, const char *name); i915_timeline_create(struct drm_i915_private *i915,
const char *name,
struct i915_vma *global_hwsp);
static inline struct i915_timeline * static inline struct i915_timeline *
i915_timeline_get(struct i915_timeline *timeline) i915_timeline_get(struct i915_timeline *timeline)
...@@ -135,6 +145,9 @@ static inline bool i915_timeline_sync_is_later(struct i915_timeline *tl, ...@@ -135,6 +145,9 @@ static inline bool i915_timeline_sync_is_later(struct i915_timeline *tl,
return __i915_timeline_sync_is_later(tl, fence->context, fence->seqno); return __i915_timeline_sync_is_later(tl, fence->context, fence->seqno);
} }
int i915_timeline_pin(struct i915_timeline *tl);
void i915_timeline_unpin(struct i915_timeline *tl);
void i915_timelines_init(struct drm_i915_private *i915); void i915_timelines_init(struct drm_i915_private *i915);
void i915_timelines_park(struct drm_i915_private *i915); void i915_timelines_park(struct drm_i915_private *i915);
void i915_timelines_fini(struct drm_i915_private *i915); void i915_timelines_fini(struct drm_i915_private *i915);
......
...@@ -484,26 +484,6 @@ static void intel_engine_init_execlist(struct intel_engine_cs *engine) ...@@ -484,26 +484,6 @@ static void intel_engine_init_execlist(struct intel_engine_cs *engine)
execlists->queue = RB_ROOT_CACHED; execlists->queue = RB_ROOT_CACHED;
} }
/**
* intel_engines_setup_common - setup engine state not requiring hw access
* @engine: Engine to setup.
*
* Initializes @engine@ structure members shared between legacy and execlists
* submission modes which do not require hardware access.
*
* Typically done early in the submission mode specific engine setup stage.
*/
void intel_engine_setup_common(struct intel_engine_cs *engine)
{
i915_timeline_init(engine->i915, &engine->timeline, engine->name);
i915_timeline_set_subclass(&engine->timeline, TIMELINE_ENGINE);
intel_engine_init_execlist(engine);
intel_engine_init_hangcheck(engine);
intel_engine_init_batch_pool(engine);
intel_engine_init_cmd_parser(engine);
}
static void cleanup_status_page(struct intel_engine_cs *engine) static void cleanup_status_page(struct intel_engine_cs *engine)
{ {
struct i915_vma *vma; struct i915_vma *vma;
...@@ -601,6 +581,44 @@ static int init_status_page(struct intel_engine_cs *engine) ...@@ -601,6 +581,44 @@ static int init_status_page(struct intel_engine_cs *engine)
return ret; return ret;
} }
/**
* intel_engines_setup_common - setup engine state not requiring hw access
* @engine: Engine to setup.
*
* Initializes @engine@ structure members shared between legacy and execlists
* submission modes which do not require hardware access.
*
* Typically done early in the submission mode specific engine setup stage.
*/
int intel_engine_setup_common(struct intel_engine_cs *engine)
{
int err;
err = init_status_page(engine);
if (err)
return err;
err = i915_timeline_init(engine->i915,
&engine->timeline,
engine->name,
engine->status_page.vma);
if (err)
goto err_hwsp;
i915_timeline_set_subclass(&engine->timeline, TIMELINE_ENGINE);
intel_engine_init_execlist(engine);
intel_engine_init_hangcheck(engine);
intel_engine_init_batch_pool(engine);
intel_engine_init_cmd_parser(engine);
return 0;
err_hwsp:
cleanup_status_page(engine);
return err;
}
static void __intel_context_unpin(struct i915_gem_context *ctx, static void __intel_context_unpin(struct i915_gem_context *ctx,
struct intel_engine_cs *engine) struct intel_engine_cs *engine)
{ {
...@@ -617,7 +635,7 @@ struct measure_breadcrumb { ...@@ -617,7 +635,7 @@ struct measure_breadcrumb {
static int measure_breadcrumb_dw(struct intel_engine_cs *engine) static int measure_breadcrumb_dw(struct intel_engine_cs *engine)
{ {
struct measure_breadcrumb *frame; struct measure_breadcrumb *frame;
unsigned int dw; int dw = -ENOMEM;
GEM_BUG_ON(!engine->i915->gt.scratch); GEM_BUG_ON(!engine->i915->gt.scratch);
...@@ -625,7 +643,10 @@ static int measure_breadcrumb_dw(struct intel_engine_cs *engine) ...@@ -625,7 +643,10 @@ static int measure_breadcrumb_dw(struct intel_engine_cs *engine)
if (!frame) if (!frame)
return -ENOMEM; return -ENOMEM;
i915_timeline_init(engine->i915, &frame->timeline, "measure"); if (i915_timeline_init(engine->i915,
&frame->timeline, "measure",
engine->status_page.vma))
goto out_frame;
INIT_LIST_HEAD(&frame->ring.request_list); INIT_LIST_HEAD(&frame->ring.request_list);
frame->ring.timeline = &frame->timeline; frame->ring.timeline = &frame->timeline;
...@@ -642,8 +663,9 @@ static int measure_breadcrumb_dw(struct intel_engine_cs *engine) ...@@ -642,8 +663,9 @@ static int measure_breadcrumb_dw(struct intel_engine_cs *engine)
dw = engine->emit_breadcrumb(&frame->rq, frame->cs) - frame->cs; dw = engine->emit_breadcrumb(&frame->rq, frame->cs) - frame->cs;
i915_timeline_fini(&frame->timeline); i915_timeline_fini(&frame->timeline);
kfree(frame);
out_frame:
kfree(frame);
return dw; return dw;
} }
...@@ -693,20 +715,14 @@ int intel_engine_init_common(struct intel_engine_cs *engine) ...@@ -693,20 +715,14 @@ int intel_engine_init_common(struct intel_engine_cs *engine)
if (ret) if (ret)
goto err_unpin_preempt; goto err_unpin_preempt;
ret = init_status_page(engine);
if (ret)
goto err_breadcrumbs;
ret = measure_breadcrumb_dw(engine); ret = measure_breadcrumb_dw(engine);
if (ret < 0) if (ret < 0)
goto err_status_page; goto err_breadcrumbs;
engine->emit_breadcrumb_dw = ret; engine->emit_breadcrumb_dw = ret;
return 0; return 0;
err_status_page:
cleanup_status_page(engine);
err_breadcrumbs: err_breadcrumbs:
intel_engine_fini_breadcrumbs(engine); intel_engine_fini_breadcrumbs(engine);
err_unpin_preempt: err_unpin_preempt:
......
...@@ -2206,10 +2206,14 @@ logical_ring_default_irqs(struct intel_engine_cs *engine) ...@@ -2206,10 +2206,14 @@ logical_ring_default_irqs(struct intel_engine_cs *engine)
engine->irq_keep_mask = GT_CONTEXT_SWITCH_INTERRUPT << shift; engine->irq_keep_mask = GT_CONTEXT_SWITCH_INTERRUPT << shift;
} }
static void static int
logical_ring_setup(struct intel_engine_cs *engine) logical_ring_setup(struct intel_engine_cs *engine)
{ {
intel_engine_setup_common(engine); int err;
err = intel_engine_setup_common(engine);
if (err)
return err;
/* Intentionally left blank. */ /* Intentionally left blank. */
engine->buffer = NULL; engine->buffer = NULL;
...@@ -2219,6 +2223,8 @@ logical_ring_setup(struct intel_engine_cs *engine) ...@@ -2219,6 +2223,8 @@ logical_ring_setup(struct intel_engine_cs *engine)
logical_ring_default_vfuncs(engine); logical_ring_default_vfuncs(engine);
logical_ring_default_irqs(engine); logical_ring_default_irqs(engine);
return 0;
} }
static int logical_ring_init(struct intel_engine_cs *engine) static int logical_ring_init(struct intel_engine_cs *engine)
...@@ -2267,7 +2273,9 @@ int logical_render_ring_init(struct intel_engine_cs *engine) ...@@ -2267,7 +2273,9 @@ int logical_render_ring_init(struct intel_engine_cs *engine)
{ {
int ret; int ret;
logical_ring_setup(engine); ret = logical_ring_setup(engine);
if (ret)
return ret;
/* Override some for render ring. */ /* Override some for render ring. */
engine->init_context = gen8_init_rcs_context; engine->init_context = gen8_init_rcs_context;
...@@ -2296,7 +2304,11 @@ int logical_render_ring_init(struct intel_engine_cs *engine) ...@@ -2296,7 +2304,11 @@ int logical_render_ring_init(struct intel_engine_cs *engine)
int logical_xcs_ring_init(struct intel_engine_cs *engine) int logical_xcs_ring_init(struct intel_engine_cs *engine)
{ {
logical_ring_setup(engine); int err;
err = logical_ring_setup(engine);
if (err)
return err;
return logical_ring_init(engine); return logical_ring_init(engine);
} }
...@@ -2629,7 +2641,7 @@ static int execlists_context_deferred_alloc(struct i915_gem_context *ctx, ...@@ -2629,7 +2641,7 @@ static int execlists_context_deferred_alloc(struct i915_gem_context *ctx,
goto error_deref_obj; goto error_deref_obj;
} }
timeline = i915_timeline_create(ctx->i915, ctx->name); timeline = i915_timeline_create(ctx->i915, ctx->name, NULL);
if (IS_ERR(timeline)) { if (IS_ERR(timeline)) {
ret = PTR_ERR(timeline); ret = PTR_ERR(timeline);
goto error_deref_obj; goto error_deref_obj;
......
...@@ -1545,9 +1545,13 @@ static int intel_init_ring_buffer(struct intel_engine_cs *engine) ...@@ -1545,9 +1545,13 @@ static int intel_init_ring_buffer(struct intel_engine_cs *engine)
struct intel_ring *ring; struct intel_ring *ring;
int err; int err;
intel_engine_setup_common(engine); err = intel_engine_setup_common(engine);
if (err)
return err;
timeline = i915_timeline_create(engine->i915, engine->name); timeline = i915_timeline_create(engine->i915,
engine->name,
engine->status_page.vma);
if (IS_ERR(timeline)) { if (IS_ERR(timeline)) {
err = PTR_ERR(timeline); err = PTR_ERR(timeline);
goto err; goto err;
...@@ -1571,6 +1575,8 @@ static int intel_init_ring_buffer(struct intel_engine_cs *engine) ...@@ -1571,6 +1575,8 @@ static int intel_init_ring_buffer(struct intel_engine_cs *engine)
if (err) if (err)
goto err_unpin; goto err_unpin;
GEM_BUG_ON(ring->timeline->hwsp_ggtt != engine->status_page.vma);
return 0; return 0;
err_unpin: err_unpin:
......
...@@ -712,7 +712,9 @@ intel_write_status_page(struct intel_engine_cs *engine, int reg, u32 value) ...@@ -712,7 +712,9 @@ intel_write_status_page(struct intel_engine_cs *engine, int reg, u32 value)
#define I915_GEM_HWS_INDEX_ADDR (I915_GEM_HWS_INDEX * sizeof(u32)) #define I915_GEM_HWS_INDEX_ADDR (I915_GEM_HWS_INDEX * sizeof(u32))
#define I915_GEM_HWS_PREEMPT 0x32 #define I915_GEM_HWS_PREEMPT 0x32
#define I915_GEM_HWS_PREEMPT_ADDR (I915_GEM_HWS_PREEMPT * sizeof(u32)) #define I915_GEM_HWS_PREEMPT_ADDR (I915_GEM_HWS_PREEMPT * sizeof(u32))
#define I915_GEM_HWS_SCRATCH 0x40 #define I915_GEM_HWS_SEQNO 0x40
#define I915_GEM_HWS_SEQNO_ADDR (I915_GEM_HWS_SEQNO * sizeof(u32))
#define I915_GEM_HWS_SCRATCH 0x80
#define I915_GEM_HWS_SCRATCH_ADDR (I915_GEM_HWS_SCRATCH * sizeof(u32)) #define I915_GEM_HWS_SCRATCH_ADDR (I915_GEM_HWS_SCRATCH * sizeof(u32))
#define I915_HWS_CSB_BUF0_INDEX 0x10 #define I915_HWS_CSB_BUF0_INDEX 0x10
...@@ -818,7 +820,7 @@ intel_ring_set_tail(struct intel_ring *ring, unsigned int tail) ...@@ -818,7 +820,7 @@ intel_ring_set_tail(struct intel_ring *ring, unsigned int tail)
void intel_engine_write_global_seqno(struct intel_engine_cs *engine, u32 seqno); void intel_engine_write_global_seqno(struct intel_engine_cs *engine, u32 seqno);
void intel_engine_setup_common(struct intel_engine_cs *engine); int intel_engine_setup_common(struct intel_engine_cs *engine);
int intel_engine_init_common(struct intel_engine_cs *engine); int intel_engine_init_common(struct intel_engine_cs *engine);
void intel_engine_cleanup_common(struct intel_engine_cs *engine); void intel_engine_cleanup_common(struct intel_engine_cs *engine);
......
...@@ -13,6 +13,7 @@ selftest(sanitycheck, i915_live_sanitycheck) /* keep first (igt selfcheck) */ ...@@ -13,6 +13,7 @@ selftest(sanitycheck, i915_live_sanitycheck) /* keep first (igt selfcheck) */
selftest(uncore, intel_uncore_live_selftests) selftest(uncore, intel_uncore_live_selftests)
selftest(workarounds, intel_workarounds_live_selftests) selftest(workarounds, intel_workarounds_live_selftests)
selftest(requests, i915_request_live_selftests) selftest(requests, i915_request_live_selftests)
selftest(timelines, i915_timeline_live_selftests)
selftest(objects, i915_gem_object_live_selftests) selftest(objects, i915_gem_object_live_selftests)
selftest(dmabuf, i915_gem_dmabuf_live_selftests) selftest(dmabuf, i915_gem_dmabuf_live_selftests)
selftest(coherency, i915_gem_coherency_live_selftests) selftest(coherency, i915_gem_coherency_live_selftests)
......
...@@ -16,7 +16,7 @@ selftest(syncmap, i915_syncmap_mock_selftests) ...@@ -16,7 +16,7 @@ selftest(syncmap, i915_syncmap_mock_selftests)
selftest(uncore, intel_uncore_mock_selftests) selftest(uncore, intel_uncore_mock_selftests)
selftest(engine, intel_engine_cs_mock_selftests) selftest(engine, intel_engine_cs_mock_selftests)
selftest(breadcrumbs, intel_breadcrumbs_mock_selftests) selftest(breadcrumbs, intel_breadcrumbs_mock_selftests)
selftest(timelines, i915_gem_timeline_mock_selftests) selftest(timelines, i915_timeline_mock_selftests)
selftest(requests, i915_request_mock_selftests) selftest(requests, i915_request_mock_selftests)
selftest(objects, i915_gem_object_mock_selftests) selftest(objects, i915_gem_object_mock_selftests)
selftest(dmabuf, i915_gem_dmabuf_mock_selftests) selftest(dmabuf, i915_gem_dmabuf_mock_selftests)
......
...@@ -7,6 +7,7 @@ ...@@ -7,6 +7,7 @@
#include "../i915_selftest.h" #include "../i915_selftest.h"
#include "i915_random.h" #include "i915_random.h"
#include "igt_flush_test.h"
#include "mock_gem_device.h" #include "mock_gem_device.h"
#include "mock_timeline.h" #include "mock_timeline.h"
...@@ -256,7 +257,7 @@ static int bench_sync(void *arg) ...@@ -256,7 +257,7 @@ static int bench_sync(void *arg)
return 0; return 0;
} }
int i915_gem_timeline_mock_selftests(void) int i915_timeline_mock_selftests(void)
{ {
static const struct i915_subtest tests[] = { static const struct i915_subtest tests[] = {
SUBTEST(igt_sync), SUBTEST(igt_sync),
...@@ -265,3 +266,326 @@ int i915_gem_timeline_mock_selftests(void) ...@@ -265,3 +266,326 @@ int i915_gem_timeline_mock_selftests(void)
return i915_subtests(tests, NULL); return i915_subtests(tests, NULL);
} }
static int emit_ggtt_store_dw(struct i915_request *rq, u32 addr, u32 value)
{
u32 *cs;
cs = intel_ring_begin(rq, 4);
if (IS_ERR(cs))
return PTR_ERR(cs);
if (INTEL_GEN(rq->i915) >= 8) {
*cs++ = MI_STORE_DWORD_IMM_GEN4 | MI_USE_GGTT;
*cs++ = addr;
*cs++ = 0;
*cs++ = value;
} else if (INTEL_GEN(rq->i915) >= 4) {
*cs++ = MI_STORE_DWORD_IMM_GEN4 | MI_USE_GGTT;
*cs++ = 0;
*cs++ = addr;
*cs++ = value;
} else {
*cs++ = MI_STORE_DWORD_IMM | MI_MEM_VIRTUAL;
*cs++ = addr;
*cs++ = value;
*cs++ = MI_NOOP;
}
intel_ring_advance(rq, cs);
return 0;
}
static u32 hwsp_address(const struct i915_timeline *tl)
{
return i915_ggtt_offset(tl->hwsp_ggtt) + tl->hwsp_offset;
}
static struct i915_request *
tl_write(struct i915_timeline *tl, struct intel_engine_cs *engine, u32 value)
{
struct i915_request *rq;
int err;
lockdep_assert_held(&tl->i915->drm.struct_mutex); /* lazy rq refs */
err = i915_timeline_pin(tl);
if (err) {
rq = ERR_PTR(err);
goto out;
}
rq = i915_request_alloc(engine, engine->i915->kernel_context);
if (IS_ERR(rq))
goto out_unpin;
err = emit_ggtt_store_dw(rq, hwsp_address(tl), value);
i915_request_add(rq);
if (err)
rq = ERR_PTR(err);
out_unpin:
i915_timeline_unpin(tl);
out:
if (IS_ERR(rq))
pr_err("Failed to write to timeline!\n");
return rq;
}
static struct i915_timeline *
checked_i915_timeline_create(struct drm_i915_private *i915)
{
struct i915_timeline *tl;
tl = i915_timeline_create(i915, "live", NULL);
if (IS_ERR(tl))
return tl;
if (*tl->hwsp_seqno != tl->seqno) {
pr_err("Timeline created with incorrect breadcrumb, found %x, expected %x\n",
*tl->hwsp_seqno, tl->seqno);
i915_timeline_put(tl);
return ERR_PTR(-EINVAL);
}
return tl;
}
static int live_hwsp_engine(void *arg)
{
#define NUM_TIMELINES 4096
struct drm_i915_private *i915 = arg;
struct i915_timeline **timelines;
struct intel_engine_cs *engine;
enum intel_engine_id id;
intel_wakeref_t wakeref;
unsigned long count, n;
int err = 0;
/*
* Create a bunch of timelines and check we can write
* independently to each of their breadcrumb slots.
*/
timelines = kvmalloc_array(NUM_TIMELINES * I915_NUM_ENGINES,
sizeof(*timelines),
GFP_KERNEL);
if (!timelines)
return -ENOMEM;
mutex_lock(&i915->drm.struct_mutex);
wakeref = intel_runtime_pm_get(i915);
count = 0;
for_each_engine(engine, i915, id) {
if (!intel_engine_can_store_dword(engine))
continue;
for (n = 0; n < NUM_TIMELINES; n++) {
struct i915_timeline *tl;
struct i915_request *rq;
tl = checked_i915_timeline_create(i915);
if (IS_ERR(tl)) {
err = PTR_ERR(tl);
goto out;
}
rq = tl_write(tl, engine, count);
if (IS_ERR(rq)) {
i915_timeline_put(tl);
err = PTR_ERR(rq);
goto out;
}
timelines[count++] = tl;
}
}
out:
if (igt_flush_test(i915, I915_WAIT_LOCKED))
err = -EIO;
for (n = 0; n < count; n++) {
struct i915_timeline *tl = timelines[n];
if (!err && *tl->hwsp_seqno != n) {
pr_err("Invalid seqno stored in timeline %lu, found 0x%x\n",
n, *tl->hwsp_seqno);
err = -EINVAL;
}
i915_timeline_put(tl);
}
intel_runtime_pm_put(i915, wakeref);
mutex_unlock(&i915->drm.struct_mutex);
kvfree(timelines);
return err;
#undef NUM_TIMELINES
}
static int live_hwsp_alternate(void *arg)
{
#define NUM_TIMELINES 4096
struct drm_i915_private *i915 = arg;
struct i915_timeline **timelines;
struct intel_engine_cs *engine;
enum intel_engine_id id;
intel_wakeref_t wakeref;
unsigned long count, n;
int err = 0;
/*
* Create a bunch of timelines and check we can write
* independently to each of their breadcrumb slots with adjacent
* engines.
*/
timelines = kvmalloc_array(NUM_TIMELINES * I915_NUM_ENGINES,
sizeof(*timelines),
GFP_KERNEL);
if (!timelines)
return -ENOMEM;
mutex_lock(&i915->drm.struct_mutex);
wakeref = intel_runtime_pm_get(i915);
count = 0;
for (n = 0; n < NUM_TIMELINES; n++) {
for_each_engine(engine, i915, id) {
struct i915_timeline *tl;
struct i915_request *rq;
if (!intel_engine_can_store_dword(engine))
continue;
tl = checked_i915_timeline_create(i915);
if (IS_ERR(tl)) {
err = PTR_ERR(tl);
goto out;
}
rq = tl_write(tl, engine, count);
if (IS_ERR(rq)) {
i915_timeline_put(tl);
err = PTR_ERR(rq);
goto out;
}
timelines[count++] = tl;
}
}
out:
if (igt_flush_test(i915, I915_WAIT_LOCKED))
err = -EIO;
for (n = 0; n < count; n++) {
struct i915_timeline *tl = timelines[n];
if (!err && *tl->hwsp_seqno != n) {
pr_err("Invalid seqno stored in timeline %lu, found 0x%x\n",
n, *tl->hwsp_seqno);
err = -EINVAL;
}
i915_timeline_put(tl);
}
intel_runtime_pm_put(i915, wakeref);
mutex_unlock(&i915->drm.struct_mutex);
kvfree(timelines);
return err;
#undef NUM_TIMELINES
}
static int live_hwsp_recycle(void *arg)
{
struct drm_i915_private *i915 = arg;
struct intel_engine_cs *engine;
enum intel_engine_id id;
intel_wakeref_t wakeref;
unsigned long count;
int err = 0;
/*
* Check seqno writes into one timeline at a time. We expect to
* recycle the breadcrumb slot between iterations and neither
* want to confuse ourselves or the GPU.
*/
mutex_lock(&i915->drm.struct_mutex);
wakeref = intel_runtime_pm_get(i915);
count = 0;
for_each_engine(engine, i915, id) {
IGT_TIMEOUT(end_time);
if (!intel_engine_can_store_dword(engine))
continue;
do {
struct i915_timeline *tl;
struct i915_request *rq;
tl = checked_i915_timeline_create(i915);
if (IS_ERR(tl)) {
err = PTR_ERR(tl);
goto out;
}
rq = tl_write(tl, engine, count);
if (IS_ERR(rq)) {
i915_timeline_put(tl);
err = PTR_ERR(rq);
goto out;
}
if (i915_request_wait(rq,
I915_WAIT_LOCKED,
HZ / 5) < 0) {
pr_err("Wait for timeline writes timed out!\n");
i915_timeline_put(tl);
err = -EIO;
goto out;
}
if (*tl->hwsp_seqno != count) {
pr_err("Invalid seqno stored in timeline %lu, found 0x%x\n",
count, *tl->hwsp_seqno);
err = -EINVAL;
}
i915_timeline_put(tl);
count++;
if (err)
goto out;
i915_timelines_park(i915); /* Encourage recycling! */
} while (!__igt_timeout(end_time, NULL));
}
out:
if (igt_flush_test(i915, I915_WAIT_LOCKED))
err = -EIO;
intel_runtime_pm_put(i915, wakeref);
mutex_unlock(&i915->drm.struct_mutex);
return err;
}
int i915_timeline_live_selftests(struct drm_i915_private *i915)
{
static const struct i915_subtest tests[] = {
SUBTEST(live_hwsp_recycle),
SUBTEST(live_hwsp_engine),
SUBTEST(live_hwsp_alternate),
};
return i915_subtests(tests, i915);
}
...@@ -39,7 +39,12 @@ static struct intel_ring *mock_ring(struct intel_engine_cs *engine) ...@@ -39,7 +39,12 @@ static struct intel_ring *mock_ring(struct intel_engine_cs *engine)
if (!ring) if (!ring)
return NULL; return NULL;
i915_timeline_init(engine->i915, &ring->timeline, engine->name); if (i915_timeline_init(engine->i915,
&ring->timeline, engine->name,
NULL)) {
kfree(ring);
return NULL;
}
ring->base.size = sz; ring->base.size = sz;
ring->base.effective_size = sz; ring->base.effective_size = sz;
...@@ -208,7 +213,11 @@ struct intel_engine_cs *mock_engine(struct drm_i915_private *i915, ...@@ -208,7 +213,11 @@ struct intel_engine_cs *mock_engine(struct drm_i915_private *i915,
engine->base.emit_breadcrumb = mock_emit_breadcrumb; engine->base.emit_breadcrumb = mock_emit_breadcrumb;
engine->base.submit_request = mock_submit_request; engine->base.submit_request = mock_submit_request;
i915_timeline_init(i915, &engine->base.timeline, engine->base.name); if (i915_timeline_init(i915,
&engine->base.timeline,
engine->base.name,
NULL))
goto err_free;
i915_timeline_set_subclass(&engine->base.timeline, TIMELINE_ENGINE); i915_timeline_set_subclass(&engine->base.timeline, TIMELINE_ENGINE);
intel_engine_init_breadcrumbs(&engine->base); intel_engine_init_breadcrumbs(&engine->base);
...@@ -226,6 +235,7 @@ struct intel_engine_cs *mock_engine(struct drm_i915_private *i915, ...@@ -226,6 +235,7 @@ struct intel_engine_cs *mock_engine(struct drm_i915_private *i915,
err_breadcrumbs: err_breadcrumbs:
intel_engine_fini_breadcrumbs(&engine->base); intel_engine_fini_breadcrumbs(&engine->base);
i915_timeline_fini(&engine->base.timeline); i915_timeline_fini(&engine->base.timeline);
err_free:
kfree(engine); kfree(engine);
return NULL; return NULL;
} }
......
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