summaryrefslogtreecommitdiff
path: root/lib/igt_gt.c
diff options
context:
space:
mode:
authorDaniel Vetter <daniel.vetter@ffwll.ch>2015-02-10 17:46:43 +0100
committerDaniel Vetter <daniel.vetter@ffwll.ch>2015-02-13 09:35:36 +0100
commit3cd45dec2e4be3edacdfc233089b6bd3651fa595 (patch)
treeae12a86adb402916187eca5bf4715d12546788a4 /lib/igt_gt.c
parent25c4347078f181a938eaaefbed7e2e27c9dc1a3f (diff)
lib/igt_gt: Document and consolidate
Also move forcewake and stop_rings code from igt_debugfs to igt_gt since it fits better. And move the hang injection fork helpers from igt_aux to igt_gt, too. Also push the intel_gen call into igt_hang_ring while at it. Signed-off-by: Daniel Vetter <daniel.vetter@intel.com>
Diffstat (limited to 'lib/igt_gt.c')
-rw-r--r--lib/igt_gt.c240
1 files changed, 238 insertions, 2 deletions
diff --git a/lib/igt_gt.c b/lib/igt_gt.c
index e02219ac..26c347b4 100644
--- a/lib/igt_gt.c
+++ b/lib/igt_gt.c
@@ -22,8 +22,13 @@
*/
#include <string.h>
+#include <signal.h>
#include <errno.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <fcntl.h>
+#include "drmtest.h"
#include "igt_core.h"
#include "igt_gt.h"
#include "igt_debugfs.h"
@@ -31,13 +36,46 @@
#include "intel_reg.h"
#include "intel_chipset.h"
+/**
+ * SECTION:igt_gt
+ * @short_description: GT support library
+ * @title: i-g-t gt
+ * @include: igt_gt.h
+ *
+ * This library provides various auxiliary helper functions to handle general
+ * interactions with the GT like forcewake handling, injecting hangs or stopping
+ * engines.
+ */
+
+
+/**
+ * igt_require_hang_ring:
+ * @fd: open i915 drm file descriptor
+ * @ring: execbuf ring flag
+ *
+ * Convenience helper to check whether advanced hang injection is supported by
+ * the kernel. Uses igt_skip to automatically skip the test/subtest if this
+ * isn't the case.
+ */
void igt_require_hang_ring(int fd, int ring)
{
gem_context_require_param(fd, LOCAL_CONTEXT_PARAM_BAN_PERIOD);
igt_require(intel_gen(intel_get_drm_devid(fd)) >= 5);
}
-struct igt_hang_ring igt_hang_ring(int fd, int gen, int ring)
+/**
+ * igt_hang_ring:
+ * @fd: open i915 drm file descriptor
+ * @ring: execbuf ring flag
+ *
+ * This helper function injects a hanging batch into @ring. It returns a
+ * #igt_hang_ring_t structure which must be passed to igt_post_hang_ring() for
+ * hang post-processing (after the gpu hang interaction has been tested.
+ *
+ * Returns:
+ * Structure with helper internal state for igt_post_hang_ring().
+ */
+igt_hang_ring_t igt_hang_ring(int fd, int ring)
{
struct drm_i915_gem_relocation_entry reloc;
struct drm_i915_gem_execbuffer2 execbuf;
@@ -66,7 +104,7 @@ struct igt_hang_ring igt_hang_ring(int fd, int gen, int ring)
exec.relocs_ptr = (uintptr_t)&reloc;
len = 2;
- if (gen >= 8)
+ if (intel_gen(intel_get_drm_devid(fd)) >= 8)
len++;
b[0] = MI_BATCH_BUFFER_START | (len - 2);
b[len] = MI_BATCH_BUFFER_END;
@@ -86,6 +124,14 @@ struct igt_hang_ring igt_hang_ring(int fd, int gen, int ring)
return (struct igt_hang_ring){ exec.handle, ban };
}
+/**
+ * igt_hang_ring:
+ * @fd: open i915 drm file descriptor
+ * @arg: hang state from igt_hang_ring()
+ *
+ * This function does the necessary post-processing after a gpu hang injected
+ * with igt_hang_ring().
+ */
void igt_post_hang_ring(int fd, struct igt_hang_ring arg)
{
struct local_i915_gem_context_param param;
@@ -103,3 +149,193 @@ void igt_post_hang_ring(int fd, struct igt_hang_ring arg)
param.value = arg.ban;
gem_context_set_param(fd, &param);
}
+
+/* GPU abusers */
+static struct igt_helper_process hang_helper;
+static void __attribute__((noreturn))
+hang_helper_process(pid_t pid, int fd)
+{
+ while (1) {
+ if (kill(pid, 0)) /* Parent has died, so must we. */
+ exit(0);
+
+ igt_post_hang_ring(fd,
+ igt_hang_ring(fd, I915_EXEC_DEFAULT));
+
+ sleep(1);
+ }
+}
+
+/**
+ * igt_fork_hang_helper:
+ *
+ * Fork a child process using #igt_fork_helper to hang the default engine
+ * of the GPU at regular intervals.
+ *
+ * This is useful to exercise slow running code (such as aperture placement)
+ * which needs to be robust against a GPU reset.
+ *
+ * In tests with subtests this function can be called outside of failure
+ * catching code blocks like #igt_fixture or #igt_subtest.
+ */
+int igt_fork_hang_helper(void)
+{
+ int fd, gen;
+
+ if (igt_only_list_subtests())
+ return 1;
+
+ fd = drm_open_any();
+ if (fd == -1)
+ return 0;
+
+ gen = intel_gen(intel_get_drm_devid(fd));
+ if (gen < 5) {
+ close(fd);
+ return 0;
+ }
+
+ igt_fork_helper(&hang_helper)
+ hang_helper_process(getppid(), fd);
+
+ close(fd);
+ return 1;
+}
+
+/**
+ * igt_stop_hang_helper:
+ *
+ * Stops the child process spawned with igt_fork_hang_helper().
+ *
+ * In tests with subtests this function can be called outside of failure
+ * catching code blocks like #igt_fixture or #igt_subtest.
+ */
+void igt_stop_hang_helper(void)
+{
+ if (igt_only_list_subtests())
+ return;
+
+ igt_stop_helper(&hang_helper);
+}
+
+/**
+ * igt_open_forcewake_handle:
+ *
+ * This functions opens the debugfs forcewake file and so prevents the GT from
+ * suspending. The reference is automatically dropped when the is closed.
+ *
+ * Returns:
+ * The file descriptor of the forcewake handle or -1 if that didn't work out.
+ */
+int igt_open_forcewake_handle(void)
+{
+ if (getenv("IGT_NO_FORCEWAKE"))
+ return -1;
+ return igt_debugfs_open("i915_forcewake_user", O_WRONLY);
+}
+
+/**
+ * igt_to_stop_ring_flag:
+ * @ring: the specified ring flag from execbuf ioctl (I915_EXEC_*)
+ *
+ * This converts the specified ring to a ring flag to be used
+ * with igt_get_stop_rings() and igt_set_stop_rings().
+ *
+ * Returns:
+ * Ring flag for the given ring.
+ */
+enum stop_ring_flags igt_to_stop_ring_flag(int ring) {
+ if (ring == I915_EXEC_DEFAULT)
+ return STOP_RING_RENDER;
+
+ igt_assert(ring && ((ring & ~I915_EXEC_RING_MASK) == 0));
+ return 1 << (ring - 1);
+}
+
+static void stop_rings_write(uint32_t mask)
+{
+ int fd;
+ char buf[80];
+
+ igt_assert(snprintf(buf, sizeof(buf), "0x%08x", mask) == 10);
+ fd = igt_debugfs_open("i915_ring_stop", O_WRONLY);
+ igt_assert(fd >= 0);
+
+ igt_assert(write(fd, buf, strlen(buf)) == strlen(buf));
+ close(fd);
+}
+
+/**
+ * igt_get_stop_rings:
+ *
+ * Read current ring flags from 'i915_ring_stop' debugfs entry.
+ *
+ * Returns:
+ * Current ring flags.
+ */
+enum stop_ring_flags igt_get_stop_rings(void)
+{
+ int fd;
+ char buf[80];
+ int l;
+ unsigned long long ring_mask;
+
+ fd = igt_debugfs_open("i915_ring_stop", O_RDONLY);
+ igt_assert(fd >= 0);
+ l = read(fd, buf, sizeof(buf)-1);
+ igt_assert(l > 0);
+ igt_assert(l < sizeof(buf));
+
+ buf[l] = '\0';
+
+ close(fd);
+
+ errno = 0;
+ ring_mask = strtoull(buf, NULL, 0);
+ igt_assert(errno == 0);
+ return ring_mask;
+}
+
+/**
+ * igt_set_stop_rings:
+ * @flags: Ring flags to write
+ *
+ * This writes @flags to 'i915_ring_stop' debugfs entry. Driver will
+ * prevent the CPU from writing tail pointer for the ring that @flags
+ * specify. Note that the ring is not stopped right away. Instead any
+ * further command emissions won't be executed after the flag is set.
+ *
+ * This is the least invasive way to make the GPU stuck. Hence you must
+ * set this after a batch submission with it's own invalid or endless
+ * looping instructions. In this case it is merely for giving notification
+ * for the driver that this was simulated hang, as the batch would have
+ * caused hang in any case. On the other hand if you use a valid or noop
+ * batch and want to hang the ring (GPU), you must set corresponding flag
+ * before submitting the batch.
+ *
+ * Driver checks periodically if a ring is making any progress, and if
+ * it is not, it will declare the ring to be hung and will reset the GPU.
+ * After reset, the driver will clear flags in 'i915_ring_stop'
+ *
+ * Note: Always when hanging the GPU, use igt_set_stop_rings() to
+ * notify the driver. Driver controls hang log messaging based on
+ * these flags and thus prevents false positives on logs.
+ */
+void igt_set_stop_rings(enum stop_ring_flags flags)
+{
+ enum stop_ring_flags current;
+
+ igt_assert((flags & ~(STOP_RING_ALL |
+ STOP_RING_ALLOW_BAN |
+ STOP_RING_ALLOW_ERRORS)) == 0);
+
+ current = igt_get_stop_rings();
+ igt_assert_f(flags == 0 || current == 0,
+ "previous i915_ring_stop is still 0x%x\n", current);
+
+ stop_rings_write(flags);
+ current = igt_get_stop_rings();
+ igt_warn_on_f(current != flags,
+ "i915_ring_stop readback mismatch 0x%x vs 0x%x\n",
+ flags, current);
+}