[igt-dev] [PATCH i-g-t 03/10] tests/perf: add tests for holding preemption

Lionel Landwerlin
2019-07-25T10:30:56Z

Using timestamps from the engine, verify that when a context has been
flagged as holding preemption it will not be preempted by another
context of higher priority.

Signed-off-by: Lionel Landwerlin <lionel.g.landwerlin@intel.com>
---
lib/intel_reg.h | 1 +
tests/perf.c | 556 ++++++++++++++++++++++++++++++++++++++++++++++++
2 files changed, 557 insertions(+)

diff --git a/lib/intel_reg.h b/lib/intel_reg.h
index 069440cb..1526ff32 100644
--- a/lib/intel_reg.h
+++ b/lib/intel_reg.h
@@ -2596,6 +2596,7 @@ SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
#define MI_BATCH_NON_SECURE (1)
#define MI_BATCH_NON_SECURE_I965 (1 << 8)
#define MI_BATCH_NON_SECURE_HSW (1<<13) /* Additional bit for RCS */
+#define MI_BATCH_PREDICATE_ENABLE_HSW (1<<15) /* RCS only */

#define MAX_DISPLAY_PIPES 2

diff --git a/tests/perf.c b/tests/perf.c
index 5ad8b2db..3bbc898a 100644
--- a/tests/perf.c
+++ b/tests/perf.c
@@ -82,6 +82,86 @@ IGT_TEST_DESCRIPTION("Test the i915 perf metrics streaming interface");
#define PIPE_CONTROL_PPGTT_WRITE (0 << 2)
#define PIPE_CONTROL_GLOBAL_GTT_WRITE (1 << 2)

+#define MI_LOAD_REGISTER_REG (0x2a << 23)
+#define MI_LOAD_REGISTER_IMM_n(n_regs) ((0x22 << 23) | (1 + 2 * (n_regs) - 2))
+#define MI_LOAD_REGISTER_MEM (0x29 << 23)
+#define MI_STORE_REGISTER_MEM (0x24 << 23)
+#define MI_STORE_DATA_IMM (0x20 << 23)
+#define MI_CONDITIONAL_BATCH_BUFFER_END (0x36 << 23)
+
+#define MI_MATH(op_len) ((0x1a << 23) | (1 + (op_len) - 2))
+#define MI_ALU_INSTR(opcode, src1, src2) \
+ ((opcode << 20) | (src1 << 10) | (src2))
+
+#define MI_ALU_OPCODE_NOOP 0
+#define MI_ALU_OPCODE_LOAD 128
+#define MI_ALU_OPCODE_LOADINV 1152
+#define MI_ALU_OPCODE_LOAD0 129
+#define MI_ALU_OPCODE_LOAD1 1153
+#define MI_ALU_OPCODE_ADD 256
+#define MI_ALU_OPCODE_SUB 257
+#define MI_ALU_OPCODE_AND 258
+#define MI_ALU_OPCODE_OR 259
+#define MI_ALU_OPCODE_XOR 260
+#define MI_ALU_OPCODE_STORE 384
+#define MI_ALU_OPCODE_STOREINV 1408
+
+#define MI_ALU_OPERAND_REG0 0
+#define MI_ALU_OPERAND_REG1 1
+#define MI_ALU_OPERAND_REG2 2
+#define MI_ALU_OPERAND_REG3 3
+#define MI_ALU_OPERAND_REG4 4
+#define MI_ALU_OPERAND_REG5 5
+#define MI_ALU_OPERAND_REG6 6
+#define MI_ALU_OPERAND_REG7 7
+#define MI_ALU_OPERAND_REG8 8
+#define MI_ALU_OPERAND_REG9 9
+#define MI_ALU_OPERAND_REG10 10
+#define MI_ALU_OPERAND_REG11 11
+#define MI_ALU_OPERAND_REG12 12
+#define MI_ALU_OPERAND_REG13 13
+#define MI_ALU_OPERAND_REG14 14
+#define MI_ALU_OPERAND_REG15 15
+#define MI_ALU_OPERAND_SRCA 32
+#define MI_ALU_OPERAND_SRCB 33
+#define MI_ALU_OPERAND_ACCU 49
+#define MI_ALU_OPERAND_ZF 50
+#define MI_ALU_OPERAND_CF 51
+
+#define MI_PREDICATE (0xC << 23)
+#define MI_PREDICATE_LOADOP_KEEP (0 << 6)
+#define MI_PREDICATE_LOADOP_LOAD (2 << 6)
+#define MI_PREDICATE_LOADOP_LOADINV (3 << 6)
+#define MI_PREDICATE_COMBINEOP_SET (0 << 3)
+#define MI_PREDICATE_COMBINEOP_AND (1 << 3)
+#define MI_PREDICATE_COMBINEOP_OR (2 << 3)
+#define MI_PREDICATE_COMBINEOP_XOR (3 << 3)
+#define MI_PREDICATE_COMPAREOP_TRUE (0 << 0)
+#define MI_PREDICATE_COMPAREOP_FALSE (1 << 0)
+#define MI_PREDICATE_COMPAREOP_SRCS_EQUAL (2 << 0)
+#define MI_PREDICATE_COMPAREOP_DELTAS_EQUAL (3 << 0)
+
+#define MI_SET_PREDICATE (0x1 << 23)
+#define MI_SET_PREDICATE_NOOP_NEVER (0)
+#define MI_SET_PREDICATE_NOOP_RESULT2_CLEAR (1)
+#define MI_SET_PREDICATE_NOOP_RESULT2_SET (2)
+#define MI_SET_PREDICATE_NOOP_RESULT_CLEAR (3)
+#define MI_SET_PREDICATE_NOOP_RESULT_SET (4)
+#define MI_SET_PREDICATE_1_SLICES (5)
+#define MI_SET_PREDICATE_2_SLICES (6)
+#define MI_SET_PREDICATE_3_SLICES (7)
+
+#define MI_ARB_CHK (0x5 << 23)
+
+#define CS_GPR(n) (0x2600 + (n) * 8)
+#define RCS_TIMESTAMP (0x2000 + 0x358)
+#define MI_PREDICATE_SRC0 0x2400
+#define MI_PREDICATE_SRC1 0x2408
+#define MI_PREDICATE_DATA 0x2410
+#define MI_PREDICATE_RESULT 0x2418
+#define MI_PREDICATE_RESULT_1 0x241C
+#define MI_PREDICATE_RESULT_2 0x2214
+
#define MAX_OA_BUF_SIZE (16 * 1024 * 1024)

struct accumulator {
@@ -367,6 +447,12 @@ timebase_scale(uint32_t u32_delta)
return ((uint64_t)u32_delta * NSEC_PER_SEC) / timestamp_frequency;
}

+static uint64_t
+time_to_gpu_ticks(uint64_t ns)
+{
+ return (ns * timestamp_frequency) / NSEC_PER_SEC;
+}
+
/* Returns: the largest OA exponent that will still result in a sampling period
* less than or equal to the given @period.
*/
@@ -3952,6 +4038,461 @@ test_whitelisted_registers_userspace_config(void)
i915_perf_remove_config(drm_fd, config_id);
}

+static bool has_i915_perf_disable_preemption_support(int fd)
+{
+ struct drm_i915_getparam gp;
+ int perf_version = -1;
+
+ memset(&gp, 0, sizeof(gp));
+ gp.param = I915_PARAM_PERF_REVISION;
+ gp.value = &perf_version;
+ igt_ioctl(fd, DRM_IOCTL_I915_GETPARAM, &gp);
+
+ return perf_version >= 2;
+}
+
+static uint32_t *
+fill_relocation(uint32_t *batch,
+ struct drm_i915_gem_relocation_entry *reloc,
+ uint32_t gem_handle, uint32_t delta, /* in bytes */
+ uint32_t offset, /* in dwords */
+ uint32_t read_domains, uint32_t write_domains)
+{
+ reloc->target_handle = gem_handle;
+ reloc->delta = delta;
+ reloc->offset = offset * sizeof(uint32_t);
+ reloc->presumed_offset = 0;
+ reloc->read_domains = read_domains;
+ reloc->write_domain = write_domains;
+
+ *batch++ = delta;
+ *batch++ = 0;
+
+ return batch;
+}
+
+static uint32_t
+busy_loop(uint32_t context, uint32_t duration_ns, int perf_fd)
+{
+ struct drm_i915_gem_execbuffer_ext_perf execbuf_perf;
+ struct drm_i915_gem_execbuffer2 execbuf;
+ struct drm_i915_gem_exec_object2 obj[4];
+ struct drm_i915_gem_relocation_entry start_relocs[1];
+ struct drm_i915_gem_relocation_entry chain_relocs[20];
+ uint32_t *batch, *b;
+ uint32_t data_handle;
+ int i, n_relocs;
+
+ memset(obj, 0, sizeof(obj));
+ obj[0].handle = data_handle = gem_create(drm_fd, 4096); /* data */
+ obj[1].handle = gem_create(drm_fd, 4096 * 10); /* loop batch */
+ obj[2].handle = gem_create(drm_fd, 4096); /* end batch */
+ obj[3].handle = gem_create(drm_fd, 4096); /* start batch */
+
+ batch = gem_mmap__cpu(drm_fd, obj[0].handle, 0, 4096,
+ PROT_READ | PROT_WRITE);
+ memset(batch, 0, 4096);
+ gem_munmap(batch, 4096);
+
+ /* start batch */
+ n_relocs = 0;
+ batch = b = gem_mmap__cpu(drm_fd, obj[3].handle, 0, 4096,
+ PROT_READ | PROT_WRITE);
+
+ *b++ = MI_LOAD_REGISTER_REG | (3 - 2);
+ *b++ = RCS_TIMESTAMP;
+ *b++ = CS_GPR(0);
+
+ *b++ = MI_BATCH_BUFFER_START | (1 << 8) | (3 - 2);
+ b = fill_relocation(b, &start_relocs[n_relocs++], obj[1].handle,
+ 0, b - batch,
+ I915_GEM_DOMAIN_INSTRUCTION, I915_GEM_DOMAIN_INSTRUCTION);
+
+ *b++ = MI_NOOP;
+ *b++ = MI_BATCH_BUFFER_END;
+
+ gem_munmap(batch, 4096);
+
+ obj[3].relocation_count = n_relocs;
+ obj[3].relocs_ptr = to_user_pointer(start_relocs);
+
+ /* end batch */
+ n_relocs = 0;
+ batch = b = gem_mmap__cpu(drm_fd, obj[2].handle, 0, 4096,
+ PROT_READ | PROT_WRITE);
+
+ *b++ = MI_BATCH_BUFFER_END;
+ *b++ = MI_NOOP;
+
+ gem_munmap(batch, 4096);
+
+ /* loop batch */
+ n_relocs = 0;
+ batch = b = gem_mmap__cpu(drm_fd, obj[1].handle, 0, 4096 * 10,
+ PROT_READ | PROT_WRITE);
+
+ *b++ = MI_LOAD_REGISTER_REG | (3 - 2);
+ *b++ = RCS_TIMESTAMP;
+ *b++ = CS_GPR(1);
+
+ *b++ = MI_LOAD_REGISTER_IMM_n(1);
+ *b++ = CS_GPR(2);
+ *b++ = time_to_gpu_ticks(duration_ns);
+
+ *b++ = MI_LOAD_REGISTER_IMM_n(1);
+ *b++ = CS_GPR(3);
+ *b++ = 1;
+
+ *b++ = MI_LOAD_REGISTER_MEM | (4 - 2);
+ *b++ = CS_GPR(4);
+ b = fill_relocation(b, &chain_relocs[n_relocs++], obj[0].handle,
+ 16, b - batch,
+ I915_GEM_DOMAIN_RENDER, I915_GEM_DOMAIN_RENDER);
+
+ *b++ = MI_MATH(4);
+ *b++ = MI_ALU_INSTR(MI_ALU_OPCODE_LOAD,
+ MI_ALU_OPERAND_SRCA,
+ MI_ALU_OPERAND_REG1);
+ *b++ = MI_ALU_INSTR(MI_ALU_OPCODE_LOAD,
+ MI_ALU_OPERAND_SRCB,
+ MI_ALU_OPERAND_REG0);
+ *b++ = MI_ALU_INSTR(MI_ALU_OPCODE_SUB,
+ MI_ALU_OPERAND_SRCA,
+ MI_ALU_OPERAND_SRCB);
+ *b++ = MI_ALU_INSTR(MI_ALU_OPCODE_STORE,
+ MI_ALU_OPERAND_REG5,
+ MI_ALU_OPERAND_ACCU);
+
+ *b++ = MI_MATH(5);
+ *b++ = MI_ALU_INSTR(MI_ALU_OPCODE_LOAD,
+ MI_ALU_OPERAND_SRCA,
+ MI_ALU_OPERAND_REG2);
+ *b++ = MI_ALU_INSTR(MI_ALU_OPCODE_LOAD,
+ MI_ALU_OPERAND_SRCB,
+ MI_ALU_OPERAND_REG5);
+ *b++ = MI_ALU_INSTR(MI_ALU_OPCODE_SUB,
+ MI_ALU_OPERAND_SRCA,
+ MI_ALU_OPERAND_SRCB);
+ *b++ = MI_ALU_INSTR(MI_ALU_OPCODE_STORE,
+ MI_ALU_OPERAND_REG6,
+ MI_ALU_OPERAND_ACCU);
+ *b++ = MI_ALU_INSTR(MI_ALU_OPCODE_STORE,
+ MI_ALU_OPERAND_REG7,
+ MI_ALU_OPERAND_CF);
+
+ *b++ = MI_MATH(4);
+ *b++ = MI_ALU_INSTR(MI_ALU_OPCODE_LOAD,
+ MI_ALU_OPERAND_SRCA,
+ MI_ALU_OPERAND_REG3);
+ *b++ = MI_ALU_INSTR(MI_ALU_OPCODE_LOAD,
+ MI_ALU_OPERAND_SRCB,
+ MI_ALU_OPERAND_REG4);
+ *b++ = MI_ALU_INSTR(MI_ALU_OPCODE_ADD,
+ MI_ALU_OPERAND_SRCA,
+ MI_ALU_OPERAND_SRCB);
+ *b++ = MI_ALU_INSTR(MI_ALU_OPCODE_STORE,
+ MI_ALU_OPERAND_REG4,
+ MI_ALU_OPERAND_ACCU);
+
+
+ *b++ = MI_STORE_REGISTER_MEM | (4 - 2);
+ *b++ = CS_GPR(0);
+ b = fill_relocation(b, &chain_relocs[n_relocs++], obj[0].handle,
+ 0, b - batch,
+ I915_GEM_DOMAIN_RENDER, I915_GEM_DOMAIN_RENDER);
+
+ *b++ = MI_STORE_REGISTER_MEM | (4 - 2);
+ *b++ = CS_GPR(1);
+ b = fill_relocation(b, &chain_relocs[n_relocs++], obj[0].handle,
+ 4, b - batch,
+ I915_GEM_DOMAIN_RENDER, I915_GEM_DOMAIN_RENDER);
+
+ *b++ = MI_STORE_REGISTER_MEM | (4 - 2);
+ *b++ = CS_GPR(2);
+ b = fill_relocation(b, &chain_relocs[n_relocs++], obj[0].handle,
+ 8, b - batch,
+ I915_GEM_DOMAIN_RENDER, I915_GEM_DOMAIN_RENDER);
+
+ *b++ = MI_STORE_REGISTER_MEM | (4 - 2);
+ *b++ = CS_GPR(3);
+ b = fill_relocation(b, &chain_relocs[n_relocs++], obj[0].handle,
+ 12, b - batch,
+ I915_GEM_DOMAIN_RENDER, I915_GEM_DOMAIN_RENDER);
+
+ *b++ = MI_STORE_REGISTER_MEM | (4 - 2);
+ *b++ = CS_GPR(4);
+ b = fill_relocation(b, &chain_relocs[n_relocs++], obj[0].handle,
+ 16, b - batch,
+ I915_GEM_DOMAIN_RENDER, I915_GEM_DOMAIN_RENDER);
+
+ *b++ = MI_STORE_REGISTER_MEM | (4 - 2);
+ *b++ = CS_GPR(5);
+ b = fill_relocation(b, &chain_relocs[n_relocs++], obj[0].handle,
+ 20, b - batch,
+ I915_GEM_DOMAIN_RENDER, I915_GEM_DOMAIN_RENDER);
+
+ *b++ = MI_STORE_REGISTER_MEM | (4 - 2);
+ *b++ = CS_GPR(7);
+ b = fill_relocation(b, &chain_relocs[n_relocs++], obj[0].handle,
+ 28, b - batch,
+ I915_GEM_DOMAIN_RENDER, I915_GEM_DOMAIN_RENDER);
+
+ igt_debug("busy delay: ns=%u ticks=%lu\n",
+ duration_ns, time_to_gpu_ticks(duration_ns));
+
+ *b++ = MI_LOAD_REGISTER_REG | (3 - 2);
+ *b++ = CS_GPR(7);
+ *b++ = MI_PREDICATE_RESULT_1;
+
+ *b++ = MI_BATCH_BUFFER_START |
+ (1 << 8) | 1 |
+ MI_BATCH_PREDICATE_ENABLE_HSW;
+ b = fill_relocation(b, &chain_relocs[n_relocs++], obj[2].handle,
+ 0, b - batch,
+ I915_GEM_DOMAIN_INSTRUCTION, I915_GEM_DOMAIN_INSTRUCTION);
+
+ *b++ = MI_ARB_CHK; /* Give some ability to preempt */
+ for (i = 0; i < 1000; i++)
+ *b++ = MI_NOOP;
+
+ *b++ = MI_STORE_REGISTER_MEM | (4 - 2);
+ *b++ = CS_GPR(6);
+ b = fill_relocation(b, &chain_relocs[n_relocs++], obj[0].handle,
+ 24, b - batch,
+ I915_GEM_DOMAIN_RENDER, I915_GEM_DOMAIN_RENDER);
+
+ *b++ = MI_BATCH_BUFFER_START |
+ (1 << 8) | 1;
+ b = fill_relocation(b, &chain_relocs[n_relocs++], obj[1].handle,
+ 0, b - batch,
+ I915_GEM_DOMAIN_INSTRUCTION, I915_GEM_DOMAIN_INSTRUCTION);
+ *b++ = MI_NOOP;
+ *b++ = MI_BATCH_BUFFER_END;
+ *b++ = MI_NOOP;
+
+ gem_munmap(batch, 4096 * 10);
+
+ obj[1].relocation_count = n_relocs;
+ obj[1].relocs_ptr = to_user_pointer(chain_relocs);
+
+ /*
+ * Now submit with the submission flagged as containing perf
+ * queries if perf_fd != -1.
+ */
+ memset(&execbuf_perf, 0, sizeof(execbuf_perf));
+ execbuf_perf.base.name = DRM_I915_GEM_EXECBUFFER_EXT_PERF;
+ execbuf_perf.perf_fd = perf_fd;
+ execbuf_perf.oa_config = test_metric_set_id;
+
+ memset(&execbuf, 0, sizeof(execbuf));
+ execbuf.buffers_ptr = to_user_pointer(obj);
+ execbuf.buffer_count = ARRAY_SIZE(obj);
+ if (perf_fd >= 0) {
+ execbuf.flags |= I915_EXEC_EXT;
+ execbuf.cliprects_ptr = to_user_pointer(&execbuf_perf);
+ }
+ i915_execbuffer2_set_context_id(execbuf, context);
+
+ gem_execbuf(drm_fd, &execbuf);
+
+ for (i = 1 /* skip data_handle */; i < ARRAY_SIZE(obj); i++)
+ gem_close(drm_fd, obj[i].handle);
+
+ return data_handle;
+}
+
+#define NSECS_PER_SEC (1000000000ull)
+
+/*
+ * Verify that preemption is put on hold for the context we filter
+ * with when the perf stream is opened with the
+ * DRM_I915_PERF_PROP_HOLD_PREEMPTION property.
+ */
+static void
+test_single_ctx_counters_disabled_preemption(void)
+{
+ uint64_t properties[] = {
+ DRM_I915_PERF_PROP_CTX_HANDLE, UINT64_MAX, /* updated below */
+ DRM_I915_PERF_PROP_SAMPLE_OA, true,
+ DRM_I915_PERF_PROP_HOLD_PREEMPTION, true,
+
+ /* OA unit configuration */
+ DRM_I915_PERF_PROP_OA_METRICS_SET, test_metric_set_id,
+ DRM_I915_PERF_PROP_OA_FORMAT, test_oa_format,
+ DRM_I915_PERF_PROP_OA_EXPONENT, max_oa_exponent_for_period_lte(1000000),
+ };
+ struct drm_i915_perf_open_param param = {
+ .flags = I915_PERF_FLAG_FD_CLOEXEC,
+ .num_properties = sizeof(properties) / 16,
+ .properties_ptr = to_user_pointer(properties),
+ };
+ uint32_t perf_context = gem_context_create(drm_fd);
+ uint32_t preempt_context = gem_context_create(drm_fd);
+ uint32_t perf_data_handle, preempt_data_handle;
+ uint32_t perf_data[10], preempt_data[10];
+ int i, perf_fd, retries = 0;
+ bool timestamp_loop = false;
+
+ gem_context_set_priority(drm_fd, perf_context,
+ I915_CONTEXT_MIN_USER_PRIORITY);
+ gem_context_set_priority(drm_fd, preempt_context,
+ I915_CONTEXT_MAX_USER_PRIORITY);
+
+ do {
+ /*
+ * First run without perf enabled, preemption should
+ * happen.
+ */
+ perf_data_handle = busy_loop(perf_context,
+ 1ULL * NSECS_PER_SEC, -1);
+
+ /* Wait 500ms before kicking off another busy loop. */
+ usleep(500000);
+ preempt_data_handle = busy_loop(preempt_context, 1ULL * 1000, -1);
+
+ gem_read(drm_fd, perf_data_handle, 0,
+ perf_data, sizeof(perf_data));
+ gem_read(drm_fd, preempt_data_handle, 0,
+ preempt_data, sizeof(preempt_data));
+
+ for (i = 0; i < ARRAY_SIZE(perf_data); i++)
+ igt_debug("perf val%i=0x%x\n", i, perf_data[i]);
+ for (i = 0; i < ARRAY_SIZE(preempt_data); i++)
+ igt_debug("preempt_perf val%i=0x%x\n", i, preempt_data[i]);
+
+ gem_close(drm_fd, perf_data_handle);
+
+ timestamp_loop = perf_data[1] < perf_data[0];
+ if (timestamp_loop) {
+ igt_assert(retries < 2);
+ retries++;
+ } else {
+ igt_assert_lte(perf_data[0], preempt_data[0]);
+ igt_assert_lte(perf_data[0], preempt_data[1]);
+ igt_assert_lte(preempt_data[0], perf_data[1]);
+ igt_assert_lte(preempt_data[1], perf_data[1]);
+ }
+ } while (timestamp_loop);
+
+ /*
+ * Now run with perf enabled, preemption shouldn't happen.
+ */
+
+ properties[1] = perf_context;
+ perf_fd = __perf_open(drm_fd, &param, false);
+
+ do {
+ perf_data_handle = busy_loop(perf_context,
+ 1ULL * 1000 * 1000 * 1000, perf_fd);
+
+ /* Wait 500ms before kicking off another busy loop. */
+ usleep(500000);
+ preempt_data_handle = busy_loop(preempt_context, 1ULL * 1000, -1);
+
+ gem_read(drm_fd, perf_data_handle, 0, perf_data, sizeof(perf_data));
+ gem_read(drm_fd, preempt_data_handle, 0,
+ preempt_data, sizeof(preempt_data));
+
+ for (i = 0; i < ARRAY_SIZE(perf_data); i++)
+ igt_debug("perf val%i=0x%x\n", i, perf_data[i]);
+ for (i = 0; i < ARRAY_SIZE(preempt_data); i++)
+ igt_debug("preempt_perf val%i=0x%x\n", i, preempt_data[i]);
+
+ gem_close(drm_fd, perf_data_handle);
+
+ timestamp_loop = preempt_data[1] < perf_data[0];
+ if (timestamp_loop) {
+ igt_assert(retries < 2);
+ retries++;
+ } else {
+ igt_assert_lte(perf_data[0], preempt_data[0]);
+ igt_assert_lte(perf_data[0], preempt_data[1]);
+ igt_assert_lte(perf_data[1], preempt_data[0]);
+ igt_assert_lte(perf_data[1], preempt_data[1]);
+ }
+ } while (timestamp_loop);
+
+ __perf_close(perf_fd);
+
+ gem_context_destroy(drm_fd, perf_context);
+ gem_context_destroy(drm_fd, preempt_context);
+}
+
+/*
+ * Verify that holding preemption is not available for normal users
+ * unless they perf_stream_paranoid is off.
+ */
+static void
+test_unprivileged_single_ctx_counters_disabled_preemption(void)
+{
+ uint64_t properties[] = {
+ DRM_I915_PERF_PROP_CTX_HANDLE, UINT64_MAX, /* updated below */
+ DRM_I915_PERF_PROP_SAMPLE_OA, true,
+ DRM_I915_PERF_PROP_HOLD_PREEMPTION, true,
+
+ /* OA unit configuration */
+ DRM_I915_PERF_PROP_OA_METRICS_SET, test_metric_set_id,
+ DRM_I915_PERF_PROP_OA_FORMAT, test_oa_format,
+ DRM_I915_PERF_PROP_OA_EXPONENT, max_oa_exponent_for_period_lte(1000000),
+ };
+ struct drm_i915_perf_open_param param = {
+ .flags = I915_PERF_FLAG_FD_CLOEXEC,
+ .num_properties = sizeof(properties) / 16,
+ .properties_ptr = to_user_pointer(properties),
+ };
+ uint32_t perf_context = gem_context_create(drm_fd);
+
+ properties[1] = perf_context;
+
+ igt_fork(child, 1) {
+ write_u64_file("/proc/sys/dev/i915/perf_stream_paranoid", 1);
+
+ igt_drop_root();
+
+ do_ioctl_err(drm_fd, DRM_IOCTL_I915_PERF_OPEN, &param, EACCES);
+ }
+
+ igt_waitchildren();
+
+ igt_fork(child, 1) {
+ write_u64_file("/proc/sys/dev/i915/perf_stream_paranoid", 0);
+
+ igt_drop_root();
+
+ stream_fd = __perf_open(drm_fd, &param, false);
+ __perf_close(stream_fd);
+ }
+
+ igt_waitchildren();
+
+ gem_context_destroy(drm_fd, perf_context);
+}
+
+/*
+ * Invalid cases in which to disable preemption.
+ */
+static void
+test_invalid_disabled_preemption(void)
+{
+ uint64_t properties[] = {
+ /* Missing context */
+ DRM_I915_PERF_PROP_SAMPLE_OA, true,
+ DRM_I915_PERF_PROP_HOLD_PREEMPTION, true,
+
+ /* OA unit configuration */
+ DRM_I915_PERF_PROP_OA_METRICS_SET, test_metric_set_id,
+ DRM_I915_PERF_PROP_OA_FORMAT, test_oa_format,
+ DRM_I915_PERF_PROP_OA_EXPONENT, max_oa_exponent_for_period_lte(1000000),
+ };
+ struct drm_i915_perf_open_param param = {
+ .flags = I915_PERF_FLAG_FD_CLOEXEC,
+ .num_properties = sizeof(properties) / 16,
+ .properties_ptr = to_user_pointer(properties),
+ };
+
+ do_ioctl_err(drm_fd, DRM_IOCTL_I915_PERF_OPEN, &param, EINVAL);
+}
+
static unsigned
read_i915_module_ref(void)
{
@@ -4190,6 +4731,21 @@ igt_main
igt_subtest("whitelisted-registers-userspace-config")
test_whitelisted_registers_userspace_config();

+ igt_subtest_group {
+ igt_fixture {
+ igt_require(has_i915_perf_disable_preemption_support(drm_fd));
+ }
+
+ igt_subtest("single-ctx-counters-disabled-preemption")
+ test_single_ctx_counters_disabled_preemption();
+
+ igt_subtest("unprivileged-single-ctx-counters-disabled-preemption")
+ test_unprivileged_single_ctx_counters_disabled_preemption();
+
+ igt_subtest("invalid-disabled-preemption")
+ test_invalid_disabled_preemption();
+ }
+
igt_fixture {
/* leave sysctl options in their default state... */
write_u64_file("/proc/sys/dev/i915/oa_max_sample_rate", 100000);