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

Dominik Grzegorzek dominik.grzegorzek at intel.com
Mon Mar 16 12:03:23 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 | 194 +++++++++++++++++++++++++++++++-
 1 file changed, 193 insertions(+), 1 deletion(-)

diff --git a/tests/i915/gem_exec_alignment.c b/tests/i915/gem_exec_alignment.c
index 44ee137a..a5f50d2f 100644
--- a/tests/i915/gem_exec_alignment.c
+++ b/tests/i915/gem_exec_alignment.c
@@ -38,7 +38,9 @@
 #include <sys/time.h>
 #include <sys/ioctl.h>
 #include <signal.h>
+#include <sched.h>
 #include "drm.h"
+#include "semaphore.h"
 
 #define MANY_TIMEOUT 10
 
@@ -108,6 +110,195 @@ static int __execbuf(int fd, struct drm_i915_gem_execbuffer2 *execbuf)
 	return err;
 }
 
+static void prio_inversion(int fd)
+{
+	uint32_t bbe = MI_BATCH_BUFFER_END;
+	struct drm_i915_gem_exec_object2 *execobj, *execobj_hp;
+	struct drm_i915_gem_execbuffer2 execbuf;
+	struct timespec begin, now, timeout;
+	uint64_t gtt_size, ram_size, flags;
+	uint64_t alignment, max_alignment, count, max_count, curr_count, i;
+	uint32_t lp, hp, zero = 0, *res, ret;
+	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));
+	igt_require(gem_scheduler_enabled(fd));
+	igt_require(gem_scheduler_has_ctx_priority(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);
+	max_count = 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);
+
+	/*
+	 * Creating a mess in address space using slow fragmentation loop
+	 * to consume 5s. LP task uses the same objects with next alignment up.
+	 */
+
+	lp = execbuf.rsvd1 = gem_context_create(fd);
+	gem_context_set_priority(fd, execbuf.rsvd1,
+				 I915_CONTEXT_MIN_USER_PRIORITY);
+
+	memset(&timeout, 0, sizeof(struct timespec));
+	for (alignment = 8192; alignment < gtt_size && igt_seconds_elapsed(&timeout) < 5; alignment <<= 1) {
+		if (alignment > max_alignment) {
+			uint64_t factor = alignment / max_alignment;
+			max_count = 2 * count / factor;
+		}
+
+		for (i = 0; i < count; i++)
+			execobj[i].alignment = alignment;
+
+		for (curr_count = 1; curr_count < max_count; curr_count <<= 1) {
+			execbuf.buffer_count = curr_count;
+			execbuf.buffers_ptr =
+			    to_user_pointer(execobj + count - execbuf.buffer_count + 1);
+
+			gem_execbuf(fd, &execbuf);
+			gem_sync(fd, execobj[count].handle);
+			if (igt_seconds_elapsed(&timeout) >= 5)
+				break;
+		}
+	}
+	igt_debug("Low priority address space fragmentation done.\n");
+
+	igt_debug("Starting quiescent\n");
+	gem_quiescent_gpu(fd);
+	igt_debug("Ending quiescent\n");
+
+	for (i = 0; i <= count; i++)
+		execobj[i].alignment = alignment;
+
+	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");
+		memset(&execbuf, 0, sizeof(execbuf));
+		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);
+		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);
+		clock_gettime(CLOCK_MONOTONIC, &now);
+		igt_debug("[H] HP sync\n");
+
+		time = igt_time_elapsed(&begin, &now);
+		igt_debug("[H] HP exec performed in %.6fs\n", time);
+		result[1] = time < 0.001;
+		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.rsvd1 = lp;
+	execbuf.buffer_count = curr_count;
+	execbuf.buffers_ptr =
+	    to_user_pointer(execobj + count - execbuf.buffer_count + 1);
+
+	igt_debug("[L] LP parent executing\n");
+	clock_gettime(CLOCK_MONOTONIC, &begin);
+
+	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);
+	time_lp = igt_time_elapsed(&begin, &now);
+	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; i++)
+		gem_close(fd, execobj[i].handle);
+
+	munmap(res, 4096);
+}
+
 static void many(int fd)
 {
 	uint32_t bbe = MI_BATCH_BUFFER_END;
@@ -267,5 +458,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