summaryrefslogtreecommitdiff
path: root/tests/i915/i915_hangman.c
diff options
context:
space:
mode:
Diffstat (limited to 'tests/i915/i915_hangman.c')
-rw-r--r--tests/i915/i915_hangman.c291
1 files changed, 291 insertions, 0 deletions
diff --git a/tests/i915/i915_hangman.c b/tests/i915/i915_hangman.c
new file mode 100644
index 00000000..df1e0afe
--- /dev/null
+++ b/tests/i915/i915_hangman.c
@@ -0,0 +1,291 @@
+/*
+ * Copyright © 2014 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.
+ *
+ * Authors:
+ * Mika Kuoppala <mika.kuoppala@intel.com>
+ * Oscar Mateo <oscar.mateo@intel.com>
+ *
+ */
+
+#include "igt.h"
+#include <limits.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <fcntl.h>
+
+#include "igt_sysfs.h"
+#include "igt_debugfs.h"
+
+#ifndef I915_PARAM_CMD_PARSER_VERSION
+#define I915_PARAM_CMD_PARSER_VERSION 28
+#endif
+
+static int device = -1;
+static int sysfs = -1;
+
+static bool has_error_state(int dir)
+{
+ bool result;
+ int fd;
+
+ fd = openat(dir, "error", O_RDONLY);
+ if (fd < 0)
+ return false;
+
+ if (read(fd, &result, sizeof(result)) < 0)
+ result = false;
+ else
+ result = true;
+
+ close(fd);
+ return result;
+}
+
+static void assert_entry(const char *s, bool expect)
+{
+ char *error;
+
+ error = igt_sysfs_get(sysfs, "error");
+ igt_assert(error);
+
+ igt_assert_f(!!strcasecmp(error, s) != expect,
+ "contents of error: '%s' (expected %s '%s')\n",
+ error, expect ? "": "not", s);
+
+ free(error);
+}
+
+static void assert_error_state_clear(void)
+{
+ assert_entry("no error state collected", true);
+}
+
+static void assert_error_state_collected(void)
+{
+ assert_entry("no error state collected", false);
+}
+
+static void clear_error_state(void)
+{
+ igt_sysfs_write(sysfs, "error", "", 1);
+}
+
+static void test_error_state_basic(void)
+{
+ int fd;
+
+ clear_error_state();
+ assert_error_state_clear();
+
+ /* Manually trigger a hang by request a reset */
+ fd = igt_debugfs_open(device, "i915_wedged", O_WRONLY);
+ igt_ignore_warn(write(fd, "1\n", 2));
+ close(fd);
+
+ assert_error_state_collected();
+
+ clear_error_state();
+ assert_error_state_clear();
+}
+
+static FILE *open_error(void)
+{
+ int fd;
+
+ fd = openat(sysfs, "error", O_RDONLY);
+ if (fd < 0)
+ return NULL;
+
+ return fdopen(fd, "r");
+}
+
+static bool uses_cmd_parser(void)
+{
+ int parser_version = 0;
+ drm_i915_getparam_t gp;
+
+ gp.param = I915_PARAM_CMD_PARSER_VERSION;
+ gp.value = &parser_version;
+ drmIoctl(device, DRM_IOCTL_I915_GETPARAM, &gp);
+
+ return parser_version > 0;
+}
+
+static void check_error_state(const char *expected_ring_name,
+ uint64_t expected_offset,
+ const uint32_t *batch)
+{
+ bool cmd_parser = uses_cmd_parser();
+ FILE *file = open_error();
+ char *line = NULL;
+ size_t line_size = 0;
+ bool found = false;
+
+ igt_debug("%s(expected ring name=%s, expected offset=%"PRIx64")\n",
+ __func__, expected_ring_name, expected_offset);
+ igt_debugfs_dump(device, "i915_error_state");
+
+ igt_assert(getline(&line, &line_size, file) != -1);
+ igt_assert(strcasecmp(line, "No error state collected"));
+
+ while (getline(&line, &line_size, file) > 0) {
+ char *dashes;
+ uint32_t gtt_offset_upper, gtt_offset_lower;
+ int matched;
+
+ dashes = strstr(line, "---");
+ if (!dashes)
+ continue;
+
+ matched = sscanf(dashes, "--- gtt_offset = 0x%08x %08x\n",
+ &gtt_offset_upper, &gtt_offset_lower);
+ if (matched) {
+ char expected_line[128];
+ uint64_t gtt_offset;
+ int i;
+
+ strncpy(expected_line, line, dashes - line);
+ expected_line[dashes - line - 1] = '\0';
+ igt_assert(strstr(expected_line, expected_ring_name));
+
+ gtt_offset = gtt_offset_upper;
+ if (matched == 2) {
+ gtt_offset <<= 32;
+ gtt_offset |= gtt_offset_lower;
+ }
+ if (!cmd_parser)
+ igt_assert_eq_u64(gtt_offset, expected_offset);
+
+ for (i = 0; i < 1024; i++) {
+ igt_assert(getline(&line, &line_size, file) > 0);
+ if (line[0] == ':' || line[0] == '~')
+ break;
+
+ snprintf(expected_line, sizeof(expected_line),
+ "%08x : %08x",
+ 4*i, batch[i]);
+ igt_assert(strstr(line, expected_line));
+ }
+
+ found = true;
+ break;
+ }
+ }
+
+ free(line);
+ fclose(file);
+
+ clear_error_state();
+
+ igt_assert(found);
+}
+
+static void test_error_state_capture(unsigned ring_id,
+ const char *ring_name)
+{
+ uint32_t *batch;
+ igt_hang_t hang;
+ uint64_t offset;
+
+ clear_error_state();
+
+ hang = igt_hang_ctx(device, 0, ring_id, HANG_ALLOW_CAPTURE);
+ offset = hang.spin->obj[1].offset;
+
+ batch = gem_mmap__cpu(device, hang.spin->handle, 0, 4096, PROT_READ);
+ gem_set_domain(device, hang.spin->handle, I915_GEM_DOMAIN_CPU, 0);
+
+ igt_post_hang_ring(device, hang);
+
+ check_error_state(ring_name, offset, batch);
+ munmap(batch, 4096);
+}
+
+/* This test covers the case where we end up in an uninitialised area of the
+ * ppgtt and keep executing through it. This is particularly relevant if 48b
+ * ppgtt is enabled because the ppgtt is massively bigger compared to the 32b
+ * case and it takes a lot more time to wrap, so the acthd can potentially keep
+ * increasing for a long time
+ */
+static void hangcheck_unterminated(void)
+{
+ /* timeout needs to be greater than ~5*hangcheck */
+ int64_t timeout_ns = 100ull * NSEC_PER_SEC; /* 100 seconds */
+ struct drm_i915_gem_execbuffer2 execbuf;
+ struct drm_i915_gem_exec_object2 gem_exec;
+ uint32_t handle;
+
+ igt_require(gem_uses_full_ppgtt(device));
+ igt_require_hang_ring(device, 0);
+
+ handle = gem_create(device, 4096);
+
+ memset(&gem_exec, 0, sizeof(gem_exec));
+ gem_exec.handle = handle;
+
+ memset(&execbuf, 0, sizeof(execbuf));
+ execbuf.buffers_ptr = (uintptr_t)&gem_exec;
+ execbuf.buffer_count = 1;
+
+ gem_execbuf(device, &execbuf);
+ if (gem_wait(device, handle, &timeout_ns) != 0) {
+ /* need to manually trigger an hang to clean before failing */
+ igt_force_gpu_reset(device);
+ igt_assert_f(0, "unterminated batch did not trigger an hang!");
+ }
+}
+
+igt_main
+{
+ const struct intel_execution_engine *e;
+
+ igt_skip_on_simulation();
+
+ igt_fixture {
+ int idx;
+
+ device = drm_open_driver(DRIVER_INTEL);
+ igt_require_gem(device);
+
+ sysfs = igt_sysfs_open(device, &idx);
+ igt_assert(sysfs != -1);
+
+ igt_require(has_error_state(sysfs));
+ }
+
+ igt_subtest("error-state-basic")
+ test_error_state_basic();
+
+ for (e = intel_execution_engines; e->name; e++) {
+ if (e->exec_id == 0)
+ continue;
+
+ igt_subtest_f("error-state-capture-%s", e->name) {
+ igt_require(gem_ring_has_physical_engine(device, e->exec_id | e->flags));
+ test_error_state_capture(e->exec_id | e->flags,
+ e->full_name);
+ }
+ }
+
+ igt_subtest("hangcheck-unterminated")
+ hangcheck_unterminated();
+}