[Intel-gfx] [PATCH igt] igt: Repalce drv_missed_irq_hang script with a C-equivalent

Chris Wilson chris at chris-wilson.co.uk
Fri Jun 17 14:41:22 UTC 2016


In order to control some of the finer detail of detecting when we missed
an interrupt, replace the shell script with C.

This version submits a hanging batch and uses a child process to unhang
it, whilst the parent sleeps. As the child process is prevented from
running whilst the parent is alive (as the child is a low priority
normal process whereas the parent is a RT process), the child can only
unhang the parent after i915_wait_request() has spun up and tried to
enable the interrupt. In contrast, the shell script guessed a workload
that should take long enough for the i915_spin_request() to miss the
completion, but that was not guaranteed.

A minor convenience of the C version is that we don't have to worry
about install paths to find the binaries underneath.

Signed-off-by: Chris Wilson <chris at chris-wilson.co.uk>
---
 tests/Makefile.sources    |   1 +
 tests/drv_missed_irq.c    | 170 ++++++++++++++++++++++++++++++++++++++++++++++
 tests/drv_missed_irq_hang |  80 ----------------------
 3 files changed, 171 insertions(+), 80 deletions(-)
 create mode 100644 tests/drv_missed_irq.c
 delete mode 100755 tests/drv_missed_irq_hang

diff --git a/tests/Makefile.sources b/tests/Makefile.sources
index 941568d..d74a93f 100644
--- a/tests/Makefile.sources
+++ b/tests/Makefile.sources
@@ -142,6 +142,7 @@ TESTS_progs = \
 	drm_vma_limiter_cached \
 	drm_vma_limiter_cpu \
 	drm_vma_limiter_gtt \
+	drv_missed_irq \
 	gem_bad_length \
 	gem_cpu_reloc \
 	gem_cs_prefetch \
diff --git a/tests/drv_missed_irq.c b/tests/drv_missed_irq.c
new file mode 100644
index 0000000..0b1cfb8
--- /dev/null
+++ b/tests/drv_missed_irq.c
@@ -0,0 +1,170 @@
+/*
+ * Copyright © 2013 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.
+ *
+ */
+
+#define _GNU_SOURCE
+#include <sched.h>
+
+#include "igt.h"
+#include "igt_stats.h"
+
+IGT_TEST_DESCRIPTION("Inject missed interrupts and make sure they are caught");
+
+static void trigger_missed_interrupt(int fd, unsigned ring)
+{
+	const int gen = intel_gen(intel_get_drm_devid(fd));
+	struct drm_i915_gem_exec_object2 obj;
+	struct drm_i915_gem_relocation_entry reloc;
+	struct drm_i915_gem_execbuffer2 execbuf;
+	uint32_t *batch;
+	int i;
+
+	memset(&obj, 0, sizeof(obj));
+	obj.handle = gem_create(fd, 4096);
+	obj.relocs_ptr = (uintptr_t)&reloc;
+	obj.relocation_count = 1;
+
+	memset(&reloc, 0, sizeof(reloc));
+	reloc.target_handle = obj.handle; /* recurse */
+	reloc.presumed_offset = 0;
+	reloc.offset = sizeof(uint32_t);
+	reloc.delta = 0;
+	reloc.read_domains = I915_GEM_DOMAIN_COMMAND;
+	reloc.write_domain = 0;
+
+	batch = gem_mmap__wc(fd, obj.handle, 0, 4096, PROT_WRITE);
+	gem_set_domain(fd, obj.handle,
+			I915_GEM_DOMAIN_GTT, I915_GEM_DOMAIN_GTT);
+
+	i = 0;
+	batch[i] = MI_BATCH_BUFFER_START;
+	if (gen >= 8) {
+		batch[i] |= 1 << 8 | 1;
+		batch[++i] = 0;
+		batch[++i] = 0;
+	} else if (gen >= 6) {
+		batch[i] |= 1 << 8;
+		batch[++i] = 0;
+	} else {
+		batch[i] |= 2 << 6;
+		batch[++i] = 0;
+		if (gen < 4) {
+			batch[i] |= 1;
+			reloc.delta = 1;
+		}
+	}
+
+	memset(&execbuf, 0, sizeof(execbuf));
+	execbuf.buffers_ptr = (uintptr_t)&obj;
+	execbuf.buffer_count = 1;
+	execbuf.flags = ring;
+
+	execbuf.flags = ring;
+	if (__gem_execbuf(fd, &execbuf))
+		goto out;
+
+	igt_fork(child, 1) {
+		/* We are now a low priority child on the *same* CPU as the
+		 * parent. We will have to wait for our parent to sleep
+		 * (gem_sync -> i915_wait_request) before we run.
+		 */
+		*batch = MI_BATCH_BUFFER_END;
+		__sync_synchronize();
+	}
+
+	gem_sync(fd, obj.handle);
+	igt_waitchildren();
+
+out:
+	gem_close(fd, obj.handle);
+	munmap(batch, 4096);
+}
+
+static void bind_to_cpu(int cpu)
+{
+	const int ncpus = sysconf(_SC_NPROCESSORS_ONLN);
+	struct sched_param rt = {.sched_priority = 99 };
+	cpu_set_t allowed;
+
+	igt_assert(sched_setscheduler(getpid(), SCHED_RR | SCHED_RESET_ON_FORK, &rt) == 0);
+
+	CPU_ZERO(&allowed);
+	CPU_SET(cpu % ncpus, &allowed);
+	igt_assert(sched_setaffinity(getpid(), sizeof(cpu_set_t), &allowed) == 0);
+}
+
+igt_simple_main
+{
+	const struct intel_execution_engine *e;
+	FILE *file;
+	unsigned expect_rings;
+	unsigned missed_rings;
+	int fd;
+
+	igt_skip_on_simulation();
+
+	fd = drm_open_driver(DRIVER_INTEL);
+	gem_require_mmap_wc(fd);
+
+	file = igt_debugfs_fopen("i915_ring_test_irq", "w");
+	fprintf(file, "0x%x", -1);
+	fclose(file);
+
+	file = igt_debugfs_fopen("i915_ring_test_irq", "r");
+	fscanf(file, "%x", &expect_rings);
+	fclose(file);
+
+	igt_debug("Testing rings %x\n", expect_rings);
+
+	igt_fork_hang_detector(fd);
+	intel_detect_and_clear_missed_interrupts(fd);
+
+	bind_to_cpu(0);
+	for (e = intel_execution_engines; e->name; e++) {
+		if (expect_rings == -1 && e->exec_id)
+			continue;
+
+		if (expect_rings != -1 && e->exec_id == 0)
+			continue;
+
+		igt_debug("Executing on ring %s [%x]\n",
+			  e->name, e->exec_id | e->flags);
+
+		trigger_missed_interrupt(fd, e->exec_id | e->flags);
+	}
+
+	missed_rings = intel_detect_and_clear_missed_interrupts(fd);
+	igt_stop_hang_detector();
+
+	file = igt_debugfs_fopen("i915_ring_test_irq", "w");
+	fprintf(file, "%x", 0);
+	fclose(file);
+
+	if (expect_rings == -1) {
+		igt_assert_eq_u32(missed_rings, 1);
+	} else {
+		igt_assert_eq_u32(missed_rings, expect_rings);
+	}
+
+	close(fd);
+}
diff --git a/tests/drv_missed_irq_hang b/tests/drv_missed_irq_hang
deleted file mode 100755
index e76c7db..0000000
--- a/tests/drv_missed_irq_hang
+++ /dev/null
@@ -1,80 +0,0 @@
-#!/bin/bash
-#
-# Testcase: Simulate missed breadcrumb interrupts
-#
-
-SOURCE_DIR="$( dirname "${BASH_SOURCE[0]}" )"
-. $SOURCE_DIR/drm_lib.sh
-
-oldpath=`pwd`
-
-cd $i915_dfs_path
-
-function blt_wait {
-	$oldpath/$SOURCE_DIR/../benchmarks/gem_blt -r 1 -b 64 -t 1 -S > /dev/null
-}
-
-function check_for_missed_irq {
-	test `cat i915_ring_missed_irq` != 0x00000000
-}
-
-function check_for_hang {
-	if cat i915_error_state | grep -v "no error state collected" > /dev/null ; then
-		echo "gpu hang reported"
-		exit $IGT_EXIT_FAILURE
-	fi
-}
-
-if [ ! -f i915_ring_missed_irq ] ; then
-	echo "kernel doesn't support interrupt masking"
-	exit $IGT_EXIT_SKIP
-fi
-
-# clear error state first
-echo > i915_error_state
-blt_wait
-if check_for_missed_irq; then
-	echo "missed interrupts detected before starting test"
-	exit $IGT_EXIT_SKOP
-fi
-check_for_hang
-
-echo 0xf > i915_ring_test_irq
-echo "Interrupts masked"
-if test `cat i915_ring_test_irq` != 0x0000000f; then
-	echo "Failed to set interrupt mask"
-	exit $IGT_EXIT_FAILURE
-fi
-
-blt_wait
-if ! check_for_missed_irq; then
-	echo "missed interrupts undetected"
-	exit $IGT_EXIT_FAILURE
-fi
-check_for_hang
-
-echo 0 > i915_ring_test_irq
-echo "Interrupts unmasked"
-if test `cat i915_ring_test_irq` != 0x00000000; then
-	echo "Failed to clear interrupt mask"
-	exit $IGT_EXIT_FAILURE
-fi
-
-blt_wait
-check_for_hang
-
-echo 0 > i915_ring_missed_irq
-echo "Cleared missed interrupts"
-if test `cat i915_ring_missed_irq` != 0x00000000; then
-	echo "Failed to clear missed interrupts"
-	exit $IGT_EXIT_FAILURE
-fi
-
-blt_wait
-if check_for_missed_irq; then
-	echo "missed interrupts detected afterwards"
-	exit $IGT_EXIT_FAILURE
-fi
-check_for_hang
-
-exit $IGT_EXIT_SUCCESS
-- 
2.8.1



More information about the Intel-gfx mailing list