[igt-dev] [PATCH i-g-t 3/3] tests/gem_exec_alignment.c: Add priority inversion test

Dominik Grzegorzek dominik.grzegorzek at intel.com
Thu Mar 12 09:09:33 UTC 2020


Priority inversion test case was implemented, which can point out
that a low priority client causes a delay of a high priority client.

Signed-off-by: Dominik Grzegorzek <dominik.grzegorzek at intel.com>
Cc: Zbigniew Kempczyński <zbigniew.kempczynski at intel.com>
Cc: Chris Wilson <chris at chris-wilson.co.uk>
---
 tests/i915/gem_exec_alignment.c | 189 +++++++++++++++++++++++++++++++-
 1 file changed, 188 insertions(+), 1 deletion(-)

diff --git a/tests/i915/gem_exec_alignment.c b/tests/i915/gem_exec_alignment.c
index 0353d02b..5250f955 100644
--- a/tests/i915/gem_exec_alignment.c
+++ b/tests/i915/gem_exec_alignment.c
@@ -37,9 +37,12 @@
 #include <sys/stat.h>
 #include <sys/time.h>
 #include <signal.h>
+#include <sched.h>
 #include "drm.h"
+#include "semaphore.h"
 
 #define MANY_TIMEOUT 10
+#define LP_COUNT 128
 
 IGT_TEST_DESCRIPTION("Exercises the basic execbuffer using object alignments");
 
@@ -99,6 +102,189 @@ static inline void alignment_reset_timeout(void)
 	setitimer(ITIMER_REAL, &itv, NULL);
 }
 
+static void prio_inversion(int fd)
+{
+	uint32_t bbe = MI_BATCH_BUFFER_END;
+	struct drm_i915_gem_exec_object2 *execobj, *execobj_hp, *empty;
+	struct drm_i915_gem_execbuffer2 execbuf;
+	struct timespec begin, now, begin_lp, now_lp;
+	uint64_t gtt_size, ram_size;
+	uint64_t max_alignment, count, i, flags;
+	uint32_t lp, hp, zero = 0, *res;
+	volatile uint32_t *result;
+	double time, time_lp;
+	sem_t *sem;
+
+	/*
+	 * First low priority client create mass of holes in their
+	 * own address space, then launch a batch with oodles of object with
+	 * alignment that doesn't match previous one. While lp execbufer
+	 * is performing we want to start high priority task
+	 * and we expect it will not be blocked.
+	 */
+
+	igt_require(gem_uses_full_ppgtt(fd));
+
+	/* Using two pointers to avoid warnings about volatile discarding. */
+	result = res = mmap(NULL, 4096, PROT_WRITE, MAP_SHARED | MAP_ANON, -1, 0);
+	memset(res, 0, 4096);
+	igt_assert(result != MAP_FAILED);
+
+	memset(&execbuf, 0, sizeof(execbuf));
+
+	/* Calc number of objects */
+	gtt_size = gem_aperture_size(fd); /* We have to *share* our GTT! */
+	ram_size = intel_get_total_ram_mb();
+	ram_size *= 1024 * 1024;
+	count = ram_size / 4096;
+
+	if (count > file_max()) /* vfs cap */
+		count = file_max();
+	max_alignment = find_last_bit(gtt_size / count);
+	if (max_alignment <= 13)
+		max_alignment = 4096;
+	else
+		max_alignment = 1ull << (max_alignment - 1);
+	count = gtt_size / max_alignment / 2;
+
+	flags = (gtt_size-1) >> 32 ? 1<<3 : 0; /* EXEC_OBJECT_SUPPORTS_48B_ADDRESS */
+
+	execobj = calloc(sizeof(*execobj), count + 1);
+	igt_assert(execobj);
+
+	execobj_hp = calloc(sizeof(*execobj_hp), 1);
+	igt_assert(execobj_hp);
+
+	/* Fill the low-priority address space */
+	for (i = 0; i < count; i++) {
+		execobj[i].handle = gem_create(fd, 4096);
+		gem_write(fd, execobj[i].handle, 0, &zero, sizeof(zero));
+		execobj[i].flags = flags;
+		execobj[i].alignment = 4096;
+	}
+	execobj[i].handle = gem_create(fd, 4096);
+	execobj[i].alignment = 4096;
+	execobj[i].flags = flags;
+
+	gem_write(fd, execobj[i].handle, 0, &bbe, sizeof(bbe));
+	memset(&execbuf, 0, sizeof(execbuf));
+	execbuf.buffers_ptr = to_user_pointer(execobj + count);
+	execbuf.buffer_count = 1;
+
+	/* Warm up both (hi/lo) contexts */
+	hp = execbuf.rsvd1 = gem_context_create(fd);
+	gem_context_set_priority(fd, execbuf.rsvd1,
+				 I915_CONTEXT_MAX_USER_PRIORITY);
+	gem_execbuf(fd, &execbuf);
+	gem_sync(fd, execobj[i].handle);
+
+	/*
+	 * Create holes in address space by running a batch with 4Kb alignment
+	 * and then closing objects that dont match 8Kb alignment.
+	 * Finally create new LP_COUNT of objects.
+	 */
+
+	lp = execbuf.rsvd1 = gem_context_create(fd);
+	gem_context_set_priority(fd, execbuf.rsvd1,
+				 I915_CONTEXT_MIN_USER_PRIORITY);
+	execbuf.buffer_count = count + 1;
+	execbuf.buffers_ptr = to_user_pointer(execobj);
+	gem_execbuf(fd, &execbuf);
+	gem_sync(fd, execobj[count].handle);
+	igt_debug("Filled low-priority address space with %ld pages\n", count);
+
+	empty = &execobj[1];
+	for (i = 0; i < count - 2; i += 2) {
+		gem_close(fd, execobj[i+1].handle);
+		*empty++ = execobj[i+2];
+	}
+
+	for (i = 0; i < LP_COUNT; i++)
+		empty++->handle = gem_create(fd, 4096);
+
+	*empty = execobj[count];
+
+	memset(&execbuf, 0, sizeof(execbuf));
+	igt_debug("Starting quiescent\n");
+	gem_quiescent_gpu(fd);
+	igt_debug("Ending quiescent\n");
+
+	for (i = 0; i <= count/2 + LP_COUNT; i++)
+		execobj[i].alignment = 8192;
+
+	sem = sem_open("test_semaphore", O_CREAT|O_EXCL, 0, 1);
+	sem_unlink("test_semaphore");
+	sem_wait(sem);
+
+	igt_fork(child, 1) {
+		igt_debug("[H] In fork\n");
+		execobj_hp[0].handle = gem_create(fd, 4096);
+		igt_debug("[H] After create\n");
+
+		gem_write(fd, execobj_hp[0].handle, 0, &bbe, sizeof(bbe));
+		result[0] = hp != execbuf.rsvd1;
+
+		execbuf.rsvd1 = hp;
+		execbuf.buffer_count = 1;
+		execbuf.buffers_ptr =
+				to_user_pointer(execobj_hp);
+
+		/* Child sleeps waiting for hp task to start. */
+		sem_wait(sem);
+		sched_yield();
+		sem_post(sem);
+
+		usleep(50000);
+		clock_gettime(CLOCK_MONOTONIC, &begin);
+		alignment_set_timeout(0, 10000);
+		igt_debug("[H] HP child executing\n");
+		gem_execbuf(fd, &execbuf);
+		igt_debug("[H] HP exec submitted\n");
+		gem_sync(fd, execobj_hp[0].handle);
+		igt_debug("[H] HP sync\n");
+
+		alignment_reset_timeout();
+		clock_gettime(CLOCK_MONOTONIC, &now);
+		time = igt_time_elapsed(&begin, &now);
+		igt_debug("[H] HP exec performed in %.6fs\n", time);
+
+		result[1] = timed_out;
+		gem_close(fd, execobj_hp[0].handle);
+	}
+
+	/* Relinquish CPU just to allow child to create a context */
+	sleep(1);
+	igt_assert_f(result[0], "HP context (child) not created\n");
+
+	execbuf.buffer_count = count/2 + LP_COUNT + 1;
+	execbuf.buffers_ptr = to_user_pointer(execobj);
+	execbuf.rsvd1 = lp;
+
+	igt_debug("[L] LP parent executing\n");
+	clock_gettime(CLOCK_MONOTONIC, &begin_lp);
+
+	alignment_set_timeout(0, 100000);
+	sem_post(sem);
+	gem_execbuf(fd, &execbuf);
+	gem_sync(fd, execobj[count].handle);
+	alignment_reset_timeout();
+
+	clock_gettime(CLOCK_MONOTONIC, &now_lp);
+	time_lp = igt_time_elapsed(&begin_lp, &now_lp);
+	igt_debug("[L] LP exec performed in %.6fs\n", time_lp);
+
+	igt_waitchildren();
+	igt_assert_f(!result[1], "HP child unable to submit within 10ms\n");
+
+	gem_context_destroy(fd, lp);
+	gem_context_destroy(fd, hp);
+
+	for (i = 0; i <= count/2 + LP_COUNT; i++)
+		gem_close(fd, execobj[i].handle);
+
+	munmap(res, 4096);
+}
+
 static void many(int fd)
 {
 	uint32_t bbe = MI_BATCH_BUFFER_END;
@@ -258,5 +444,6 @@ igt_main
 		single(fd);
 	igt_subtest("many")
 		many(fd);
-
+	igt_subtest("pi")
+		prio_inversion(fd);
 }
-- 
2.20.1



More information about the igt-dev mailing list