summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--tests/Makefile.sources1
-rw-r--r--tests/gem_ctx_param_basic.c2
-rw-r--r--tests/gem_exec_preempt.c462
3 files changed, 465 insertions, 0 deletions
diff --git a/tests/Makefile.sources b/tests/Makefile.sources
index 0c0f877f5274..a6613d1d496a 100644
--- a/tests/Makefile.sources
+++ b/tests/Makefile.sources
@@ -144,6 +144,7 @@ TESTS_progs = \
gem_exec_big \
gem_exec_blt \
gem_exec_lut_handle \
+ gem_exec_preempt \
gem_fd_exhaustion \
gem_gtt_cpu_tlb \
gem_gtt_hog \
diff --git a/tests/gem_ctx_param_basic.c b/tests/gem_ctx_param_basic.c
index 1a10c018a1c7..001e6ddfe45f 100644
--- a/tests/gem_ctx_param_basic.c
+++ b/tests/gem_ctx_param_basic.c
@@ -60,7 +60,9 @@ igt_main
igt_subtest("basic") {
ctx_param.context = ctx;
TEST_SUCCESS(LOCAL_IOCTL_I915_GEM_CONTEXT_GETPARAM);
+printf( "\x1B[33;1mBoo!\x1B[0m\n" );
TEST_SUCCESS(LOCAL_IOCTL_I915_GEM_CONTEXT_SETPARAM);
+printf( "\x1B[33mBaa!\x1B[0m\n" );
}
igt_subtest("basic-default") {
diff --git a/tests/gem_exec_preempt.c b/tests/gem_exec_preempt.c
new file mode 100644
index 000000000000..d60c543a9c45
--- /dev/null
+++ b/tests/gem_exec_preempt.c
@@ -0,0 +1,462 @@
+/*
+ * Copyright © 2011 Intel Corporation
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a
+ * copy of this software and associated documentation files (the "Software"),
+ * to deal in the Software without restriction, including without limitation
+ * the rights to use, copy, modify, merge, publish, distribute, sublicense,
+ * and/or sell copies of the Software, and to permit persons to whom the
+ * Software is furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice (including the next
+ * paragraph) shall be included in all copies or substantial portions of the
+ * Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
+ * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+ * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
+ * IN THE SOFTWARE.
+ *
+ * Authors:
+ * Chris Wilson <chris@chris-wilson.co.uk>
+ * Dave Gordon <david.s.gordon@intel.com>
+ *
+ */
+
+#include <unistd.h>
+#include <stdlib.h>
+#include <stdint.h>
+#include <stdio.h>
+#include <string.h>
+#include <fcntl.h>
+#include <inttypes.h>
+#include <errno.h>
+#include <sys/stat.h>
+#include <sys/ioctl.h>
+#include <sys/time.h>
+#include "drm.h"
+#include "i915_drm.h"
+#include "drmtest.h"
+#include "../lib/ioctl_wrappers.h"
+/*#include "intel_gpu_tools.h"*/
+
+// #define I915_CONTEXT_PARAM_PRIORITY 0x3
+// #define LOCAL_IOCTL_I915_GEM_CONTEXT_SETPARAM DRM_IOWR (DRM_COMMAND_BASE + DRM_I915_GEM_CONTEXT_SETPARAM, struct drm_i915_gem_context_param)
+
+
+#define LOCAL_I915_EXEC_VEBOX (4<<0)
+
+#define BATCH_WORDS 1024
+#define BATCH_TOP 1000
+uint32_t batch[BATCH_WORDS];
+uint32_t handle0, handle1, handle2;
+uint32_t ctx0, ctx1;
+int drm_fd;
+
+extern time_t time(time_t *t);
+
+#if 0
+#define CONTEXT_DESTROY_IOCTL DRM_IOWR(DRM_COMMAND_BASE + 0x2e, struct local_drm_i915_gem_context_destroy)
+#ifndef DRM_IOCTL_I915_GEM_CONTEXT_DESTROY
+#define DRM_IOCTL_I915_GEM_CONTEXT_DESTROY DRM_IOW(DRM_COMMAND_BASE + DRM_I915_GEM_CONTEXT_DESTROY, struct drm_i915_gem_context_destroy)
+#endif
+#else
+#define MI_INSTR(x, y) (((x) << 23) | (y))
+#define MI_ARB_CHECK MI_INSTR(0x05, 0)
+#define MI_STORE_DWORD_INDEX MI_INSTR(0x21, 1)
+#define MI_LOAD_REGISTER_IMM(x) MI_INSTR(0x22, 2*(x)-1)
+#define REG_NOPID 0x2094
+#endif
+
+static void
+context_destroy(int fd, uint32_t ctx_id)
+{
+ struct drm_i915_gem_context_destroy destroy = { .ctx_id = ctx_id };
+ int ret;
+
+ errno = 0;
+ ret = drmIoctl(fd, DRM_IOCTL_I915_GEM_CONTEXT_DESTROY, &destroy);
+#if 1
+ fprintf(stderr, "%s(%d, 0x%x): drmIoctl(%d, 0x%lx, %p) = %d, errno %d\n",
+ __func__, fd, ctx_id, fd, DRM_IOCTL_I915_GEM_CONTEXT_DESTROY, &destroy, ret, errno);
+#endif
+}
+
+static double
+elapsed(const struct timeval *start, const struct timeval *end, int loops)
+{
+ return (1e6*(end->tv_sec - start->tv_sec) + (end->tv_usec - start->tv_usec))/loops;
+}
+
+/*
+ * Do an ioctl; retry on certain (non-)errors. There's an
+ * implicit one-second timeout (10000*100us) to prevent lockups.
+ */
+static int
+checked_ioctl(int fd, int cmd, void *ptr)
+{
+ int i, ret;
+
+ for (i = 0; i < 10000; ++i) {
+ errno = 0;
+ ret = ioctl(fd, cmd, ptr);
+ if (ret == 0)
+ return ret;
+
+ if (ret > 0) {
+ fprintf(stderr, "checked_ioctl: ioctl(%d, 0x%x, %p) = %d, errno %d\n",
+ fd, cmd, ptr, ret, errno);
+ return ret;
+ }
+
+ /* ret < 0 :-( */
+ switch (errno) {
+ case EAGAIN:
+ case EBUSY:
+ usleep(100);
+ case EINTR:
+ continue;
+
+ default:
+ fprintf(stderr, "checked_ioctl: failed ioctl(%d, 0x%x, %p) = %d, errno %d\n",
+ fd, cmd, ptr, ret, errno);
+ return errno;
+ }
+ }
+
+ fprintf(stderr, "checked_ioctl: timeout ioctl(%d, 0x%x, %p) = %d, errno %d\n",
+ fd, cmd, ptr, ret, errno);
+ return errno;
+}
+
+/*
+ * Do the EXECBUFFER2 ioctl.
+ */
+static int
+exec_ioctl(int fd, struct drm_i915_gem_execbuffer2 *eb2p)
+{
+ return checked_ioctl(fd, DRM_IOCTL_I915_GEM_EXECBUFFER2, eb2p);
+}
+
+/*
+ * Wait for the completion of a buffer, by trying to change its domain
+ */
+static void
+exec_sync(int fd, uint32_t handle)
+{
+ struct drm_i915_gem_set_domain set_domain;
+
+ set_domain.handle = handle;
+ set_domain.read_domains = I915_GEM_DOMAIN_GTT;
+ set_domain.write_domain = I915_GEM_DOMAIN_GTT;
+
+ igt_assert(checked_ioctl(fd, DRM_IOCTL_I915_GEM_SET_DOMAIN, &set_domain) == 0);
+}
+
+/*struct drm_i915_gem_context_param {
+ __u32 ctx_id;
+ __u32 size;
+ __u64 param;
+#define I915_CONTEXT_PARAM_BAN_PERIOD 0x1
+#define I915_CONTEXT_PARAM_NO_ZEROMAP 0x2
+#define I915_CONTEXT_PARAM_PRIORITY 0x3
+ __u64 value;
+};*/
+
+static void enable_preempt( int fd, uint32_t ctx_id, bool enable )
+{
+ struct drm_i915_gem_context_param param;
+
+ memset(&param, 0, sizeof(param));
+
+ param.ctx_id = ctx_id;
+ param.size = 0;
+ param.param = I915_CONTEXT_PARAM_PRIORITY;
+ param.value = enable ? 1000 : -100;
+ igt_assert(checked_ioctl(fd, DRM_IOCTL_I915_GEM_CONTEXT_SETPARAM, &param) == 0);
+}
+
+/*
+ * Submit the predefined batch for execution some number of times,
+ * optionally followed by a preemptive batch and then maybe some
+ * more non-preemptive batches.
+ */
+static int
+exec_batch(int fd, int loops, unsigned ring_id, int preempt, bool mid)
+{
+ int ret = 0;
+ struct drm_i915_gem_exec_object2 gx0 =
+ {
+ .handle = handle0,
+ };
+ struct drm_i915_gem_execbuffer2 eb0 =
+ { /* Preemptive batch */
+ .buffers_ptr = (uintptr_t)&gx0,
+ .buffer_count = 1,
+ .batch_start_offset = BATCH_TOP*sizeof(batch[0]),
+ .batch_len = sizeof(batch) - BATCH_TOP*sizeof(batch[0]),
+ .flags = ring_id,
+ };
+
+ struct drm_i915_gem_exec_object2 gx1 =
+ {
+ .handle = handle1,
+ };
+ struct drm_i915_gem_execbuffer2 eb1 =
+ { /* Preemptible batch */
+ .buffers_ptr = (uintptr_t)&gx1,
+ .buffer_count = 1,
+ .batch_start_offset = 0,
+ .batch_len = sizeof(batch),
+ .flags = ring_id,
+ };
+
+ struct drm_i915_gem_exec_object2 gx2 =
+ {
+ .handle = handle2,
+ };
+ struct drm_i915_gem_execbuffer2 eb2 =
+ { /* Mid-preemptible batch */
+ .buffers_ptr = (uintptr_t)&gx2,
+ .buffer_count = 1,
+ .batch_start_offset = 0,
+ .batch_len = sizeof(batch),
+ .flags = ring_id,
+ };
+
+ /* Only the render ring supports non-default contexts */
+ if (ring_id == I915_EXEC_RENDER) {
+ i915_execbuffer2_set_context_id(eb0, ctx0);
+ i915_execbuffer2_set_context_id(eb1, ctx1);
+ i915_execbuffer2_set_context_id(eb2, ctx1);
+ }
+
+ /* Submit some non-preemptive batches */
+ while (ret == 0 && loops-- > 0)
+ if (mid && loops%4 == 0)
+ ret = exec_ioctl(fd, &eb2);
+ else
+ ret = exec_ioctl(fd, &eb1);
+
+ if (ret == 0 && preempt != 0) {
+ enable_preempt(fd, ctx0, true);
+
+ /* This time, invoke preemption */
+ ret = exec_ioctl(fd, &eb0);
+
+ enable_preempt(fd, ctx0, false);
+
+ /* Then maybe submit some more non-preemptive batches */
+ for (; ret == 0 && preempt > 0; --preempt)
+ ret = exec_ioctl(fd, &eb1);
+
+ /* Wait for preemption to finish */
+ if (preempt >= -1)
+ exec_sync(fd, handle0);
+ }
+
+ /* Wait for execution to finish */
+ if (preempt >= -1)
+ exec_sync(fd, handle1);
+ return ret;
+}
+
+static void
+loop_exec_batch(int fd, unsigned ring_id, const char *ring_name, int preempt, bool mid)
+{
+ int count;
+
+ gem_require_ring(fd, ring_id);
+
+ for (count = 1; count <= SLOW_QUICK(1<<17, 1<<4); count <<= 1) {
+ struct timeval start, end;
+
+ gettimeofday(&start, NULL);
+ if (exec_batch(fd, count, ring_id, preempt, mid))
+ igt_fail(1);
+ gettimeofday(&end, NULL);
+ printf("Time to exec x %d: %7.3fµs (ring=%s)\n",
+ count, elapsed(&start, &end, count), ring_name);
+ fflush(stdout);
+ }
+}
+
+static void
+exec_one_preemptive(int fd, unsigned ring_id, const char *ring_name)
+{
+ struct timeval start, end;
+
+ gem_require_ring(fd, ring_id);
+
+ gettimeofday(&start, NULL);
+ if (exec_batch(fd, 0, ring_id, -2, 0))
+ igt_fail(1);
+ gettimeofday(&end, NULL);
+ printf("Time to exec x %d: %7.3fµs (ring=%s)\n",
+ 1, elapsed(&start, &end, 1), ring_name);
+ fflush(stdout);
+}
+
+static void
+batch_preempt_setup(int fd)
+{
+ int i, j, k, t;
+
+ /*
+ * The batch buffer is filled with NOOPs nearly up to the end,
+ * followed by a BATCH BUFFER END command and one more NOOP.
+ * Then we overwrite one of the NOOPs (at offset BATCH_TOP-1)
+ * with another BATCH BUFFER END. This effectively gives us two
+ * different sequences within the same buffer, one at offsets
+ * 0..BATCH_TOP-1, and another at BATCH_TOP..BATCH_WORDS, and
+ * we can then select either by choosing the offset from which
+ * to start execution. As coded above, we use the longer head
+ * section for the default batch, and the shorter tail section
+ * for preemptive batches.
+ */
+ for (i = 0; i < BATCH_WORDS-2; ++i)
+ batch[i] = MI_NOOP | MI_NOOP_WRITE_ID | ((0x1000+i) & MI_NOOP_ID_MASK);
+ batch[i] = MI_BATCH_BUFFER_END;
+ batch[++i] = MI_NOOP;
+
+ /* handle0 = preemptive batch */
+ gem_write(fd, handle0, 0, batch, sizeof(batch));
+
+ /* handle1 = default batch */
+#if 1
+ for (i = 7; i < BATCH_TOP-2; i += 16)
+ batch[i] = MI_ARB_CHECK;
+#endif /*1*/
+ batch[BATCH_TOP-1] = MI_BATCH_BUFFER_END;
+ gem_write(fd, handle1, 0, batch, sizeof(batch));
+
+ /*
+ * Now we create a completly different batch of commands for
+ * testing mid-batch preemption. This batch first clears a
+ * range of words in the Hardware Status Page, then writes
+ * a distinct value to each in turn, allowing preemption after
+ * each write.
+ */
+#define HWSP_TGT_MIN 0x40
+#define HWSP_TGT_MAX 0x80
+ /* These instructions start at offset 0 in the batch buffer */
+ for (i = 0, j = HWSP_TGT_MIN; j < HWSP_TGT_MAX; ++j)
+ {
+#if 0
+ batch[i++] = MI_STORE_DWORD_INDEX;
+ batch[i++] = j*sizeof(int);
+ batch[i++] = 0;
+#else
+ batch[i++] = MI_LOAD_REGISTER_IMM(1);
+ batch[i++] = REG_NOPID;
+ batch[i++] = j*sizeof(int);
+#endif
+ }
+ /* These instructions start at offset (3*64*4 = 0x300) */
+ for (k = 0; k < 3; ++k)
+ {
+ for (j = HWSP_TGT_MIN; j < HWSP_TGT_MAX; ++j)
+ {
+ t = 0xd5900000 + (i << 8) + j;
+#if 0
+ batch[i++] = MI_STORE_DWORD_INDEX;
+ batch[i++] = j*sizeof(int);
+ batch[i++] = t;
+#else
+ batch[i++] = MI_LOAD_REGISTER_IMM(1);
+ batch[i++] = REG_NOPID;
+ batch[i++] = t;
+#endif
+ batch[i++] = MI_ARB_CHECK;
+ }
+ }
+ /* These instructions end at offset ((3+4+4+4)*64*4 = 0xf00) */
+ gem_write(fd, handle2, 0, batch, sizeof(batch));
+}
+
+static void
+global_init(void)
+{
+ errno = 0;
+ drm_fd = drm_open_driver(DRIVER_INTEL);
+#if 1
+ fprintf(stderr, "opened fd %d, errno %d\n", drm_fd, errno);
+#endif
+
+ errno = 0;
+ ctx0 = gem_context_create(drm_fd);
+ ctx1 = gem_context_create(drm_fd);
+#if 1
+ fprintf(stderr, "created contexts 0x%x and 0x%x, errno %d\n",
+ ctx0, ctx1, errno);
+#endif
+
+ errno = 0;
+ handle0 = gem_create(drm_fd, sizeof(batch));
+ handle1 = gem_create(drm_fd, sizeof(batch));
+ handle2 = gem_create(drm_fd, sizeof(batch));
+#if 1
+ fprintf(stderr, "created handles %d, %d and %d, errno %d\n",
+ handle0, handle1, handle2, errno);
+#endif
+}
+
+static void
+global_fini(void)
+{
+ context_destroy(drm_fd, ctx1);
+ context_destroy(drm_fd, ctx0);
+ gem_close(drm_fd, handle2);
+ gem_close(drm_fd, handle1);
+ gem_close(drm_fd, handle0);
+ close(drm_fd);
+}
+
+int
+main(int argc, char **argv)
+{
+ igt_subtest_init(argc, argv);
+
+ igt_fixture {
+ global_init();
+ batch_preempt_setup(drm_fd);
+ }
+
+/*
+ igt_subtest("render")
+ loop_exec_batch(drm_fd, I915_EXEC_RENDER, "render", 0, 0);
+
+ igt_subtest("bsd")
+ loop_exec_batch(drm_fd, I915_EXEC_BSD, "bsd", 0, 0);
+
+ igt_subtest("blt")
+ loop_exec_batch(drm_fd, I915_EXEC_BLT, "blt", 0, 0);
+
+ igt_subtest("vebox")
+ loop_exec_batch(drm_fd, LOCAL_I915_EXEC_VEBOX, "vebox", 0, 0);
+*/
+
+ igt_subtest("preempt_0")
+ loop_exec_batch(drm_fd, I915_EXEC_RENDER, "render", -1, 0);
+
+ igt_subtest("preempt_1")
+ loop_exec_batch(drm_fd, I915_EXEC_RENDER, "render", 1, 0);
+
+ igt_subtest("preempt_2")
+ loop_exec_batch(drm_fd, I915_EXEC_RENDER, "render", 8, 0);
+
+ igt_subtest("preempt_mid")
+ loop_exec_batch(drm_fd, I915_EXEC_RENDER, "render", 1, 1);
+
+ igt_subtest("preempt_nowait")
+ exec_one_preemptive(drm_fd, I915_EXEC_RENDER, "render");
+
+ igt_fixture {
+ global_fini();
+ }
+
+ igt_exit();
+}