|
@@ -24,6 +24,9 @@
|
|
|
|
|
|
#include "../i915_selftest.h"
|
|
|
|
|
|
+#include "lib_sw_fence.h"
|
|
|
+#include "mock_context.h"
|
|
|
+#include "mock_drm.h"
|
|
|
#include "mock_gem_device.h"
|
|
|
|
|
|
static int populate_ggtt(struct drm_i915_private *i915)
|
|
@@ -325,6 +328,148 @@ cleanup:
|
|
|
return err;
|
|
|
}
|
|
|
|
|
|
+static int igt_evict_contexts(void *arg)
|
|
|
+{
|
|
|
+ const u64 PRETEND_GGTT_SIZE = 16ull << 20;
|
|
|
+ struct drm_i915_private *i915 = arg;
|
|
|
+ struct intel_engine_cs *engine;
|
|
|
+ enum intel_engine_id id;
|
|
|
+ struct reserved {
|
|
|
+ struct drm_mm_node node;
|
|
|
+ struct reserved *next;
|
|
|
+ } *reserved = NULL;
|
|
|
+ struct drm_mm_node hole;
|
|
|
+ unsigned long count;
|
|
|
+ int err;
|
|
|
+
|
|
|
+ /*
|
|
|
+ * The purpose of this test is to verify that we will trigger an
|
|
|
+ * eviction in the GGTT when constructing a request that requires
|
|
|
+ * additional space in the GGTT for pinning the context. This space
|
|
|
+ * is not directly tied to the request so reclaiming it requires
|
|
|
+ * extra work.
|
|
|
+ *
|
|
|
+ * As such this test is only meaningful for full-ppgtt environments
|
|
|
+ * where the GTT space of the request is separate from the GGTT
|
|
|
+ * allocation required to build the request.
|
|
|
+ */
|
|
|
+ if (!USES_FULL_PPGTT(i915))
|
|
|
+ return 0;
|
|
|
+
|
|
|
+ mutex_lock(&i915->drm.struct_mutex);
|
|
|
+
|
|
|
+ /* Reserve a block so that we know we have enough to fit a few rq */
|
|
|
+ memset(&hole, 0, sizeof(hole));
|
|
|
+ err = i915_gem_gtt_insert(&i915->ggtt.base, &hole,
|
|
|
+ PRETEND_GGTT_SIZE, 0, I915_COLOR_UNEVICTABLE,
|
|
|
+ 0, i915->ggtt.base.total,
|
|
|
+ PIN_NOEVICT);
|
|
|
+ if (err)
|
|
|
+ goto out_locked;
|
|
|
+
|
|
|
+ /* Make the GGTT appear small by filling it with unevictable nodes */
|
|
|
+ count = 0;
|
|
|
+ do {
|
|
|
+ struct reserved *r;
|
|
|
+
|
|
|
+ r = kcalloc(1, sizeof(*r), GFP_KERNEL);
|
|
|
+ if (!r) {
|
|
|
+ err = -ENOMEM;
|
|
|
+ goto out_locked;
|
|
|
+ }
|
|
|
+
|
|
|
+ if (i915_gem_gtt_insert(&i915->ggtt.base, &r->node,
|
|
|
+ 1ul << 20, 0, I915_COLOR_UNEVICTABLE,
|
|
|
+ 0, i915->ggtt.base.total,
|
|
|
+ PIN_NOEVICT)) {
|
|
|
+ kfree(r);
|
|
|
+ break;
|
|
|
+ }
|
|
|
+
|
|
|
+ r->next = reserved;
|
|
|
+ reserved = r;
|
|
|
+
|
|
|
+ count++;
|
|
|
+ } while (1);
|
|
|
+ drm_mm_remove_node(&hole);
|
|
|
+ mutex_unlock(&i915->drm.struct_mutex);
|
|
|
+ pr_info("Filled GGTT with %lu 1MiB nodes\n", count);
|
|
|
+
|
|
|
+ /* Overfill the GGTT with context objects and so try to evict one. */
|
|
|
+ for_each_engine(engine, i915, id) {
|
|
|
+ struct i915_sw_fence fence;
|
|
|
+ struct drm_file *file;
|
|
|
+
|
|
|
+ file = mock_file(i915);
|
|
|
+ if (IS_ERR(file))
|
|
|
+ return PTR_ERR(file);
|
|
|
+
|
|
|
+ count = 0;
|
|
|
+ mutex_lock(&i915->drm.struct_mutex);
|
|
|
+ onstack_fence_init(&fence);
|
|
|
+ do {
|
|
|
+ struct drm_i915_gem_request *rq;
|
|
|
+ struct i915_gem_context *ctx;
|
|
|
+
|
|
|
+ ctx = live_context(i915, file);
|
|
|
+ if (!ctx)
|
|
|
+ break;
|
|
|
+
|
|
|
+ /* We will need some GGTT space for the rq's context */
|
|
|
+ igt_evict_ctl.fail_if_busy = true;
|
|
|
+ rq = i915_gem_request_alloc(engine, ctx);
|
|
|
+ igt_evict_ctl.fail_if_busy = false;
|
|
|
+
|
|
|
+ if (IS_ERR(rq)) {
|
|
|
+ /* When full, fail_if_busy will trigger EBUSY */
|
|
|
+ if (PTR_ERR(rq) != -EBUSY) {
|
|
|
+ pr_err("Unexpected error from request alloc (ctx hw id %u, on %s): %d\n",
|
|
|
+ ctx->hw_id, engine->name,
|
|
|
+ (int)PTR_ERR(rq));
|
|
|
+ err = PTR_ERR(rq);
|
|
|
+ }
|
|
|
+ break;
|
|
|
+ }
|
|
|
+
|
|
|
+ /* Keep every request/ctx pinned until we are full */
|
|
|
+ err = i915_sw_fence_await_sw_fence_gfp(&rq->submit,
|
|
|
+ &fence,
|
|
|
+ GFP_KERNEL);
|
|
|
+ if (err < 0)
|
|
|
+ break;
|
|
|
+
|
|
|
+ i915_add_request(rq);
|
|
|
+ count++;
|
|
|
+ err = 0;
|
|
|
+ } while(1);
|
|
|
+ mutex_unlock(&i915->drm.struct_mutex);
|
|
|
+
|
|
|
+ onstack_fence_fini(&fence);
|
|
|
+ pr_info("Submitted %lu contexts/requests on %s\n",
|
|
|
+ count, engine->name);
|
|
|
+
|
|
|
+ mock_file_free(i915, file);
|
|
|
+ if (err)
|
|
|
+ break;
|
|
|
+ }
|
|
|
+
|
|
|
+ mutex_lock(&i915->drm.struct_mutex);
|
|
|
+out_locked:
|
|
|
+ while (reserved) {
|
|
|
+ struct reserved *next = reserved->next;
|
|
|
+
|
|
|
+ drm_mm_remove_node(&reserved->node);
|
|
|
+ kfree(reserved);
|
|
|
+
|
|
|
+ reserved = next;
|
|
|
+ }
|
|
|
+ if (drm_mm_node_allocated(&hole))
|
|
|
+ drm_mm_remove_node(&hole);
|
|
|
+ mutex_unlock(&i915->drm.struct_mutex);
|
|
|
+
|
|
|
+ return err;
|
|
|
+}
|
|
|
+
|
|
|
int i915_gem_evict_mock_selftests(void)
|
|
|
{
|
|
|
static const struct i915_subtest tests[] = {
|
|
@@ -348,3 +493,12 @@ int i915_gem_evict_mock_selftests(void)
|
|
|
drm_dev_unref(&i915->drm);
|
|
|
return err;
|
|
|
}
|
|
|
+
|
|
|
+int i915_gem_evict_live_selftests(struct drm_i915_private *i915)
|
|
|
+{
|
|
|
+ static const struct i915_subtest tests[] = {
|
|
|
+ SUBTEST(igt_evict_contexts),
|
|
|
+ };
|
|
|
+
|
|
|
+ return i915_subtests(tests, i915);
|
|
|
+}
|