[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