summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorChris Wilson <chris@chris-wilson.co.uk>2020-01-18 17:36:24 +0000
committerChris Wilson <chris@chris-wilson.co.uk>2020-12-13 12:01:31 +0000
commite31a5224a86dc335686f2185f9ea0641c4124236 (patch)
tree32ef89d05c134cb1e4716b9c8b2072e0c5c4f715
parent24b5ca48b34a05554135b6fb68126f1a24eb9679 (diff)
i915: Exercise VM_WAIT ioctl
Signed-off-by: Chris Wilson <chris@chris-wilson.co.uk>
-rw-r--r--include/drm-uapi/i915_drm.h37
-rw-r--r--lib/igt_dummyload.c2
-rw-r--r--lib/igt_dummyload.h11
-rw-r--r--tests/Makefile.sources3
-rw-r--r--tests/i915/gem_vm_wait.c548
-rw-r--r--tests/meson.build1
6 files changed, 597 insertions, 5 deletions
diff --git a/include/drm-uapi/i915_drm.h b/include/drm-uapi/i915_drm.h
index 27a3a441..f05fe660 100644
--- a/include/drm-uapi/i915_drm.h
+++ b/include/drm-uapi/i915_drm.h
@@ -359,6 +359,7 @@ typedef struct _drm_i915_sarea {
#define DRM_I915_QUERY 0x39
#define DRM_I915_GEM_VM_CREATE 0x3a
#define DRM_I915_GEM_VM_DESTROY 0x3b
+#define DRM_I915_GEM_VM_WAIT 0x3c
/* Must be kept compact -- no holes */
#define DRM_IOCTL_I915_INIT DRM_IOW( DRM_COMMAND_BASE + DRM_I915_INIT, drm_i915_init_t)
@@ -422,6 +423,7 @@ typedef struct _drm_i915_sarea {
#define DRM_IOCTL_I915_QUERY DRM_IOWR(DRM_COMMAND_BASE + DRM_I915_QUERY, struct drm_i915_query)
#define DRM_IOCTL_I915_GEM_VM_CREATE DRM_IOWR(DRM_COMMAND_BASE + DRM_I915_GEM_VM_CREATE, struct drm_i915_gem_vm_control)
#define DRM_IOCTL_I915_GEM_VM_DESTROY DRM_IOW (DRM_COMMAND_BASE + DRM_I915_GEM_VM_DESTROY, struct drm_i915_gem_vm_control)
+#define DRM_IOCTL_I915_GEM_VM_WAIT DRM_IOWR(DRM_COMMAND_BASE + DRM_I915_GEM_VM_WAIT, struct drm_i915_gem_vm_wait)
/* Allow drivers to submit batchbuffers directly to hardware, relying
* on the security mechanisms provided by hardware.
@@ -1900,6 +1902,41 @@ struct drm_i915_gem_vm_control {
__u32 vm_id;
};
+/*
+ * (*IOVA & MASK) OP (VALUE & MASK)
+ *
+ * OP:
+ * - EQ, NEQ
+ * - GT, GTE
+ * - LT, LTE
+ * - BEFORE, AFTER
+ *
+ */
+struct drm_i915_gem_vm_wait {
+ __u64 extensions;
+ __u64 iova;
+ __u32 vm_id;
+ __u16 op;
+#define I915_VM_WAIT_EQ 0
+#define I915_VM_WAIT_NEQ 1
+#define I915_VM_WAIT_GT 2
+#define I915_VM_WAIT_GTE 3
+#define I915_VM_WAIT_LT 4
+#define I915_VM_WAIT_LTE 5
+#define I915_VM_WAIT_BEFORE 6
+#define I915_VM_WAIT_AFTER 7
+#define I915_VM_WAIT_PASSED 8
+ __u16 flags;
+#define I915_VM_WAIT_ABSTIME 0x1
+ __u64 value;
+ __u64 mask;
+#define I915_VM_WAIT_U8 0xffu
+#define I915_VM_WAIT_U16 0xffffu
+#define I915_VM_WAIT_U32 0xfffffffful
+#define I915_VM_WAIT_U64 0xffffffffffffffffull
+ __u64 timeout;
+};
+
struct drm_i915_reg_read {
/*
* Register offset.
diff --git a/lib/igt_dummyload.c b/lib/igt_dummyload.c
index 4ef79cc4..17fe8a84 100644
--- a/lib/igt_dummyload.c
+++ b/lib/igt_dummyload.c
@@ -229,6 +229,8 @@ emit_recursive_batch(igt_spin_t *spin,
}
*cs++ = 1;
+ if (opts->flags & IGT_SPIN_WAKE_RUN)
+ *cs++ = 0x2 << 23;
execbuf->buffer_count++;
}
diff --git a/lib/igt_dummyload.h b/lib/igt_dummyload.h
index 3ece70a5..c38f9efd 100644
--- a/lib/igt_dummyload.h
+++ b/lib/igt_dummyload.h
@@ -68,11 +68,12 @@ struct igt_spin_factory {
#define IGT_SPIN_FENCE_SUBMIT (1 << 1)
#define IGT_SPIN_FENCE_OUT (1 << 2)
#define IGT_SPIN_POLL_RUN (1 << 3)
-#define IGT_SPIN_FAST (1 << 4)
-#define IGT_SPIN_NO_PREEMPTION (1 << 5)
-#define IGT_SPIN_INVALID_CS (1 << 6)
-#define IGT_SPIN_USERPTR (1 << 7)
-#define IGT_SPIN_SOFTDEP (1 << 8)
+#define IGT_SPIN_WAKE_RUN (1 << 4)
+#define IGT_SPIN_FAST (1 << 5)
+#define IGT_SPIN_NO_PREEMPTION (1 << 6)
+#define IGT_SPIN_INVALID_CS (1 << 7)
+#define IGT_SPIN_USERPTR (1 << 8)
+#define IGT_SPIN_SOFTDEP (1 << 9)
igt_spin_t *
__igt_spin_factory(int fd, const struct igt_spin_factory *opts);
diff --git a/tests/Makefile.sources b/tests/Makefile.sources
index 17c8325f..b4a47262 100644
--- a/tests/Makefile.sources
+++ b/tests/Makefile.sources
@@ -464,6 +464,9 @@ gem_userptr_blits_SOURCES = i915/gem_userptr_blits.c
TESTS_progs += gem_wait
gem_wait_SOURCES = i915/gem_wait.c
+TESTS_progs += gem_vm_wait
+gem_vm_wait_SOURCES = i915/gem_vm_wait.c
+
TESTS_progs += gem_workarounds
gem_workarounds_SOURCES = i915/gem_workarounds.c
diff --git a/tests/i915/gem_vm_wait.c b/tests/i915/gem_vm_wait.c
new file mode 100644
index 00000000..5486d96e
--- /dev/null
+++ b/tests/i915/gem_vm_wait.c
@@ -0,0 +1,548 @@
+/*
+ * Copyright © 2020 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.
+ */
+
+#include "i915/gem.h"
+#include "i915/gem_vm.h"
+#include "igt.h"
+
+static int __gem_vm_wait(int i915, struct drm_i915_gem_vm_wait *w)
+{
+ int err;
+
+ err = 0;
+ if (igt_ioctl(i915, DRM_IOCTL_I915_GEM_VM_WAIT, w)) {
+ err = -errno;
+ igt_assume(err);
+ }
+
+ return err;
+}
+
+static uint32_t get_vm(int i915, uint32_t ctx)
+{
+ struct drm_i915_gem_context_param arg = {
+ .ctx_id = ctx,
+ .param = I915_CONTEXT_PARAM_VM,
+ };
+
+ gem_context_get_param(i915, &arg);
+ return arg.value;
+}
+
+static uint32_t __batch_create(int i915, uint32_t offset)
+{
+ const uint32_t bbe = MI_BATCH_BUFFER_END;
+ uint32_t handle;
+
+ handle = gem_create(i915, offset + 4);
+ gem_write(i915, handle, offset, &bbe, sizeof(bbe));
+
+ return handle;
+}
+
+static uint32_t batch_create(int i915)
+{
+ return __batch_create(i915, 0);
+}
+
+static struct drm_i915_gem_exec_object2
+vm_bind(int i915, uint32_t ctx, uint32_t handle, uint64_t offset)
+{
+ struct drm_i915_gem_exec_object2 obj[2] = {
+ {
+ .handle = handle,
+ .offset = offset,
+ .flags = EXEC_OBJECT_PINNED,
+ },
+ { .handle = batch_create(i915), }
+ };
+ struct drm_i915_gem_execbuffer2 eb = {
+ .buffers_ptr = to_user_pointer(obj),
+ .buffer_count = 2,
+ .rsvd1 = ctx,
+ };
+
+ gem_execbuf(i915, &eb);
+ gem_close(i915, obj[1].handle);
+
+ igt_assert_eq_u64(obj[0].offset, offset);
+ return obj[0];
+}
+
+static void invalid_mask(int i915)
+{
+ struct drm_i915_gem_vm_wait wait;
+ uint32_t handle = gem_create(i915, 4096);
+
+ vm_bind(i915, 0, handle, 0);
+
+ memset(&wait, 0, sizeof(wait));
+ wait.vm_id = get_vm(i915, 0);
+ wait.op = I915_VM_WAIT_EQ;
+ wait.mask = I915_VM_WAIT_U32;
+ igt_assert_eq(__gem_vm_wait(i915, &wait), 0);
+
+ /* Having proved we have an otherwise valid arg... */
+ wait.mask = 0;
+ igt_assert_eq(__gem_vm_wait(i915, &wait), -EINVAL);
+
+ gem_vm_destroy(i915, wait.vm_id);
+ gem_close(i915, handle);
+}
+
+static void invalid_flags(int i915)
+{
+ struct drm_i915_gem_vm_wait wait;
+ uint32_t handle = gem_create(i915, 4096);
+
+ vm_bind(i915, 0, handle, 0);
+
+ memset(&wait, 0, sizeof(wait));
+ wait.vm_id = get_vm(i915, 0);
+ wait.op = I915_VM_WAIT_EQ;
+ wait.mask = I915_VM_WAIT_U32;
+ igt_assert_eq(__gem_vm_wait(i915, &wait), 0);
+
+ /* Having proved we have an otherwise valid arg... */
+ wait.flags = 0xffff;
+ igt_assert_eq(__gem_vm_wait(i915, &wait), -EINVAL);
+
+ gem_vm_destroy(i915, wait.vm_id);
+ gem_close(i915, handle);
+}
+
+static void invalid_ops(int i915)
+{
+ struct drm_i915_gem_vm_wait wait;
+ uint32_t handle = gem_create(i915, 4096);
+
+ vm_bind(i915, 0, handle, 0);
+
+ memset(&wait, 0, sizeof(wait));
+ wait.vm_id = get_vm(i915, 0);
+ wait.op = I915_VM_WAIT_EQ;
+ wait.mask = I915_VM_WAIT_U32;
+ igt_assert_eq(__gem_vm_wait(i915, &wait), 0);
+
+ /* Having proved we have an otherwise valid arg... */
+ wait.op = -1;
+ igt_assert_eq(__gem_vm_wait(i915, &wait), -EINVAL);
+
+ gem_vm_destroy(i915, wait.vm_id);
+ gem_close(i915, handle);
+}
+
+static void invalid_iova(int i915)
+{
+ struct drm_i915_gem_vm_wait wait;
+ uint32_t handle = gem_create(i915, 4096);
+
+ vm_bind(i915, 0, handle, 0);
+
+ memset(&wait, 0, sizeof(wait));
+ wait.vm_id = get_vm(i915, 0);
+ wait.op = I915_VM_WAIT_EQ;
+ wait.mask = I915_VM_WAIT_U32;
+ igt_assert_eq(__gem_vm_wait(i915, &wait), 0);
+
+ /* Natural alignments */
+ wait.mask = I915_VM_WAIT_U8;
+ wait.iova = 0x1;
+ igt_assert_eq(__gem_vm_wait(i915, &wait), 0);
+
+ wait.mask = I915_VM_WAIT_U16;
+ igt_assert_eq(__gem_vm_wait(i915, &wait), -EINVAL);
+ wait.iova = 0x2;
+ igt_assert_eq(__gem_vm_wait(i915, &wait), 0);
+
+ wait.mask = I915_VM_WAIT_U32;
+ igt_assert_eq(__gem_vm_wait(i915, &wait), -EINVAL);
+ wait.iova = 0x4;
+ igt_assert_eq(__gem_vm_wait(i915, &wait), 0);
+
+ wait.mask = I915_VM_WAIT_U64;
+ igt_assert_eq(__gem_vm_wait(i915, &wait), -EINVAL);
+
+ gem_vm_destroy(i915, wait.vm_id);
+ gem_close(i915, handle);
+}
+
+static const char *op_repr(int op)
+{
+ switch (op) {
+#define CASE(x) case I915_VM_WAIT_##x: return #x
+ CASE(EQ);
+ CASE(NEQ);
+ CASE(GT);
+ CASE(GTE);
+ CASE(LT);
+ CASE(LTE);
+ CASE(BEFORE);
+ CASE(AFTER);
+ CASE(PASSED);
+ default: return "unknown";
+ }
+}
+
+static void basic(int i915)
+{
+ static const struct {
+ unsigned int op;
+ uint32_t a, b;
+ int result;
+#define GOOD 0
+#define BAD -ETIME
+ } ops[] = {
+ { I915_VM_WAIT_EQ, 0, 0, GOOD },
+ { I915_VM_WAIT_EQ, 0, 1, BAD },
+ { I915_VM_WAIT_EQ, 1, 0, BAD },
+ { I915_VM_WAIT_EQ, 1, 1, GOOD },
+ { I915_VM_WAIT_EQ, 0, -1, BAD },
+ { I915_VM_WAIT_EQ, -1, 0, BAD },
+ { I915_VM_WAIT_EQ, -1, -1, GOOD },
+
+ { I915_VM_WAIT_NEQ, 0, 0, BAD },
+ { I915_VM_WAIT_NEQ, 0, 1, GOOD },
+ { I915_VM_WAIT_NEQ, 1, 0, GOOD },
+ { I915_VM_WAIT_NEQ, 1, 1, BAD },
+ { I915_VM_WAIT_NEQ, 0, -1, GOOD },
+ { I915_VM_WAIT_NEQ, -1, 0, GOOD },
+ { I915_VM_WAIT_NEQ, -1, -1, BAD },
+
+ { I915_VM_WAIT_GT, 0, 0, BAD },
+ { I915_VM_WAIT_GT, 1, 0, GOOD },
+ { I915_VM_WAIT_GT, 1, 1, BAD },
+ { I915_VM_WAIT_GT, 0, 1, BAD },
+ { I915_VM_WAIT_GT, -1, 0, GOOD },
+ { I915_VM_WAIT_GT, 0, -1, BAD },
+
+ { I915_VM_WAIT_GTE, 0, 0, GOOD },
+ { I915_VM_WAIT_GTE, 1, 0, GOOD },
+ { I915_VM_WAIT_GTE, 1, 1, GOOD },
+ { I915_VM_WAIT_GTE, 0, 1, BAD },
+ { I915_VM_WAIT_GTE, -1, 0, GOOD },
+ { I915_VM_WAIT_GTE, 0, -1, BAD },
+
+ { I915_VM_WAIT_LT, 0, 0, BAD },
+ { I915_VM_WAIT_LT, 1, 0, BAD },
+ { I915_VM_WAIT_LT, 1, 1, BAD },
+ { I915_VM_WAIT_LT, 0, 1, GOOD },
+ { I915_VM_WAIT_LT, -1, 0, BAD },
+ { I915_VM_WAIT_LT, 0, -1, GOOD },
+
+ { I915_VM_WAIT_LTE, 0, 0, GOOD },
+ { I915_VM_WAIT_LTE, 1, 0, BAD },
+ { I915_VM_WAIT_LTE, 1, 1, GOOD },
+ { I915_VM_WAIT_LTE, 0, 1, GOOD },
+ { I915_VM_WAIT_LTE, -1, 0, BAD },
+ { I915_VM_WAIT_LTE, 0, -1, GOOD },
+
+ { I915_VM_WAIT_BEFORE, 0, 0, BAD },
+ { I915_VM_WAIT_BEFORE, 1, 0, BAD },
+ { I915_VM_WAIT_BEFORE, 1, 1, BAD },
+ { I915_VM_WAIT_BEFORE, 0, 1, GOOD },
+ { I915_VM_WAIT_BEFORE, -1, 0, GOOD },
+ { I915_VM_WAIT_BEFORE, 0, -1, BAD },
+
+ { I915_VM_WAIT_AFTER, 0, 0, BAD },
+ { I915_VM_WAIT_AFTER, 1, 0, GOOD },
+ { I915_VM_WAIT_AFTER, 1, 1, BAD },
+ { I915_VM_WAIT_AFTER, 0, 1, BAD },
+ { I915_VM_WAIT_AFTER, -1, 0, BAD },
+ { I915_VM_WAIT_AFTER, 0, -1, GOOD },
+
+ { I915_VM_WAIT_PASSED, 0, 0, GOOD },
+ { I915_VM_WAIT_PASSED, 1, 0, GOOD },
+ { I915_VM_WAIT_PASSED, 1, 1, GOOD },
+ { I915_VM_WAIT_PASSED, 0, 1, BAD },
+ { I915_VM_WAIT_PASSED, -1, 0, BAD },
+ { I915_VM_WAIT_PASSED, 0, -1, GOOD },
+ { I915_VM_WAIT_PASSED, -1, -1, GOOD },
+ };
+ struct drm_i915_gem_vm_wait wait;
+ uint32_t handle = gem_create(i915, 4096);
+ uint32_t *x = gem_mmap__wc(i915, handle, 0, 4096, PROT_WRITE);
+
+ vm_bind(i915, 0, handle, 0);
+
+ memset(&wait, 0, sizeof(wait));
+ wait.vm_id = get_vm(i915, 0);
+ wait.op = I915_VM_WAIT_EQ;
+ wait.mask = I915_VM_WAIT_U32;
+ igt_assert_eq(__gem_vm_wait(i915, &wait), 0);
+ wait.op = I915_VM_WAIT_NEQ;
+ igt_assert_eq(__gem_vm_wait(i915, &wait), -ETIME);
+
+ for (int i = 0; i < ARRAY_SIZE(ops); i++) {
+ *x = ops[i].a;
+ wait.value = ops[i].b;
+ wait.op = ops[i].op;
+ igt_assert_f(__gem_vm_wait(i915, &wait) == ops[i].result,
+ "*iova: %08x wait: { op:%s, value:%08x, }, result: %d, expected: %d\n",
+ ops[i].a, op_repr(ops[i].op), ops[i].b,
+ __gem_vm_wait(i915, &wait),
+ ops[i].result);
+ }
+
+ gem_vm_destroy(i915, wait.vm_id);
+ munmap(x, 4096);
+ gem_close(i915, handle);
+}
+
+static void store_dword(int i915,
+ const struct intel_execution_engine2 *e,
+ const struct drm_i915_gem_exec_object2 *target,
+ int offset, uint32_t value)
+{
+ const int gen = intel_gen(intel_get_drm_devid(i915));
+ struct drm_i915_gem_exec_object2 obj[2];
+ struct drm_i915_gem_execbuffer2 execbuf;
+ uint32_t batch[16];
+ int i;
+
+ memset(&execbuf, 0, sizeof(execbuf));
+ execbuf.buffers_ptr = to_user_pointer(obj);
+ execbuf.buffer_count = 2;
+ execbuf.flags = e->flags;
+ if (gen > 3 && gen < 6)
+ execbuf.flags |= I915_EXEC_SECURE;
+
+ memset(obj, 0, sizeof(obj));
+ memcpy(obj, target, sizeof(*target));
+ obj[1].handle = gem_create(i915, 4096);
+
+ i = 0;
+ batch[i] = MI_STORE_DWORD_IMM | (gen < 6 ? 1 << 22 : 0);
+ if (gen >= 8) {
+ batch[++i] = target->offset + offset;
+ batch[++i] = (target->offset + offset) >> 32;
+ } else if (gen >= 4) {
+ batch[++i] = 0;
+ batch[++i] = target->offset + offset;
+ } else {
+ batch[i]--;
+ batch[++i] = target->offset + offset;
+ }
+ batch[++i] = value;
+ batch[++i] = MI_BATCH_BUFFER_END;
+ gem_write(i915, obj[1].handle, 0, batch, sizeof(batch));
+
+ gem_execbuf(i915, &execbuf);
+ gem_close(i915, obj[1].handle);
+}
+
+static void signal(int i915, const struct intel_execution_engine2 *e)
+{
+ struct drm_i915_gem_vm_wait wait;
+ struct drm_i915_gem_exec_object2 obj =
+ vm_bind(i915, 0, gem_create(i915, 4096), 0);
+
+ memset(&wait, 0, sizeof(wait));
+ wait.vm_id = get_vm(i915, 0);
+ wait.iova = obj.offset;
+ wait.op = I915_VM_WAIT_EQ;
+ wait.mask = I915_VM_WAIT_U32;
+ igt_assert_eq(__gem_vm_wait(i915, &wait), 0);
+
+ wait.op = I915_VM_WAIT_NEQ;
+ igt_assert_eq(__gem_vm_wait(i915, &wait), -ETIME);
+
+ store_dword(i915, e, &obj, 0, 1);
+ gem_sync(i915, obj.handle);
+
+ wait.op = I915_VM_WAIT_NEQ;
+ igt_assert_eq(__gem_vm_wait(i915, &wait), 0);
+
+ wait.op = I915_VM_WAIT_EQ;
+ wait.value = 1;
+ igt_assert_eq(__gem_vm_wait(i915, &wait), 0);
+
+ wait.op = I915_VM_WAIT_NEQ;
+ igt_assert_eq(__gem_vm_wait(i915, &wait), -ETIME);
+
+ igt_fork(child, 1) {
+ usleep(50000);
+ store_dword(i915, e, &obj, 0, 2);
+ }
+
+ wait.timeout = NSEC_PER_SEC;
+ igt_assert_eq(__gem_vm_wait(i915, &wait), 0);
+ igt_waitchildren();
+
+ gem_vm_destroy(i915, wait.vm_id);
+ gem_close(i915, obj.handle);
+}
+
+static void spin(int i915, const struct intel_execution_engine2 *e)
+{
+ struct drm_i915_gem_vm_wait wait;
+ igt_spin_t *spin;
+
+ spin = igt_spin_new(i915,
+ .engine = e->flags,
+ .flags = IGT_SPIN_POLL_RUN | IGT_SPIN_WAKE_RUN);
+ igt_spin_busywait_until_started(spin);
+
+ memset(&wait, 0, sizeof(wait));
+ wait.vm_id = get_vm(i915, 0);
+ wait.iova = spin->obj[0].offset;
+ wait.op = I915_VM_WAIT_NEQ;
+ wait.mask = I915_VM_WAIT_U32;
+ igt_assert_eq(__gem_vm_wait(i915, &wait), 0);
+
+ igt_spin_end(spin);
+ igt_spin_reset(spin);
+ igt_assert_eq(__gem_vm_wait(i915, &wait), -ETIME);
+
+ igt_fork(child, 1) {
+ usleep(50000);
+ gem_execbuf(i915, &spin->execbuf);
+ }
+
+ wait.timeout = NSEC_PER_SEC;
+ igt_assert_eq(__gem_vm_wait(i915, &wait), 0);
+
+ igt_waitchildren();
+ igt_spin_free(i915, spin);
+
+ gem_vm_destroy(i915, wait.vm_id);
+}
+
+static void hang(int i915, const struct intel_execution_engine2 *e)
+{
+ struct drm_i915_gem_vm_wait wait;
+ igt_spin_t *spin;
+
+ spin = igt_spin_new(i915,
+ .engine = e->flags,
+ .flags = (IGT_SPIN_POLL_RUN |
+ IGT_SPIN_NO_PREEMPTION));
+ igt_spin_busywait_until_started(spin);
+
+ memset(&wait, 0, sizeof(wait));
+ wait.vm_id = get_vm(i915, 0);
+ wait.iova = spin->obj[0].offset;
+ wait.op = I915_VM_WAIT_NEQ;
+ wait.mask = I915_VM_WAIT_U32;
+ igt_assert_eq(__gem_vm_wait(i915, &wait), 0);
+
+ igt_spin_end(spin);
+ igt_spin_reset(spin);
+ igt_assert_eq(__gem_vm_wait(i915, &wait), -ETIME);
+
+ igt_fork(child, 1) {
+ usleep(50000);
+ gem_execbuf(i915, &spin->execbuf);
+ }
+
+ wait.timeout = -1;
+ igt_assert_eq(__gem_vm_wait(i915, &wait), 0);
+
+ igt_waitchildren();
+ igt_spin_free(i915, spin);
+
+ gem_vm_destroy(i915, wait.vm_id);
+}
+
+static bool has_vm_wait(int i915)
+{
+ struct drm_i915_gem_vm_wait wait = { .mask = -1ull };
+
+ return __gem_vm_wait(i915, &wait) == -ENOENT;
+}
+
+static bool has_vm(int i915)
+{
+ struct drm_i915_gem_context_param arg = {
+ .param = I915_CONTEXT_PARAM_VM,
+ };
+
+ if (__gem_context_get_param(i915, &arg))
+ return false;
+
+ gem_vm_destroy(i915, arg.value);
+ return true;
+}
+
+igt_main
+{
+ const struct intel_execution_engine2 *e;
+ int i915 = -1;
+
+ igt_fixture {
+ i915 = drm_open_driver_master(DRIVER_INTEL);
+ igt_require(has_vm(i915));
+ igt_require(has_vm_wait(i915));
+ igt_require_gem(i915);
+ }
+
+ igt_subtest("invalid-mask")
+ invalid_mask(i915);
+
+ igt_subtest("invalid-flags")
+ invalid_flags(i915);
+
+ igt_subtest("invalid-ops")
+ invalid_ops(i915);
+
+ igt_subtest("invalid-iova")
+ invalid_iova(i915);
+
+ igt_subtest("basic")
+ basic(i915);
+
+ igt_subtest_with_dynamic("signal") {
+ __for_each_physical_engine(i915, e) {
+ igt_dynamic_f("%s", e->name)
+ signal(i915, e);
+ }
+ }
+
+ igt_subtest_with_dynamic("spin") {
+ __for_each_physical_engine(i915, e) {
+ igt_dynamic_f("%s", e->name)
+ spin(i915, e);
+ }
+ }
+
+ igt_subtest_group {
+ igt_hang_t hh;
+
+ igt_fixture
+ hh = igt_allow_hang(i915, 0, 0);
+
+ igt_subtest_with_dynamic("hang") {
+ __for_each_physical_engine(i915, e) {
+ igt_dynamic_f("%s", e->name)
+ hang(i915, e);
+ }
+ }
+
+ igt_fixture
+ igt_disallow_hang(i915, hh);
+ }
+
+ igt_fixture {
+ close(i915);
+ }
+}
diff --git a/tests/meson.build b/tests/meson.build
index 32870234..86dbae95 100644
--- a/tests/meson.build
+++ b/tests/meson.build
@@ -221,6 +221,7 @@ i915_progs = [
'gem_unref_active_buffers',
'gem_userptr_blits',
'gem_vm_create',
+ 'gem_vm_wait',
'gem_wait',
'gem_workarounds',
'i915_fb_tiling',