summaryrefslogtreecommitdiff
path: root/tests/i915/gem_softpin.c
diff options
context:
space:
mode:
authorArkadiusz Hiler <arkadiusz.hiler@intel.com>2018-10-18 14:06:42 +0300
committerArkadiusz Hiler <arkadiusz.hiler@intel.com>2018-10-23 10:55:51 +0300
commit741bf7064c467df725c14cc0b3b8b50436f9ee09 (patch)
tree0ad6fb217dca79a8f1175fb289979b574222fefa /tests/i915/gem_softpin.c
parent78619fde4008424c472906041edb1d204e014f7c (diff)
tests: Introduce i915 directory
We can already move all the tests with distinct prefixes: gem_, gen3_ and i915_. pm_ and drv_ tests will follow in batches, so we can do the adjustments in the reporting/filtering layer of the CI system. v2: Fix test-list.txt generation with meson v3: Fix docs build (Petri) Cc: Chris Wilson <chris@chris-wilson.co.uk> Cc: Petri Latvala <petri.latvala@intel.com> Cc: Martin Peres <martin.peres@linux.intel.com> Signed-off-by: Arkadiusz Hiler <arkadiusz.hiler@intel.com> Reviewed-by: Petri Latvala <petri.latvala@intel.com> Tested-by: Petri Latvala <petri.latvala@intel.com> Acked-by: Daniel Vetter <daniel.vetter@ffwll.ch> Acked-by: Chris Wilson <chris@chris-wilson.co.uk>
Diffstat (limited to 'tests/i915/gem_softpin.c')
-rw-r--r--tests/i915/gem_softpin.c517
1 files changed, 517 insertions, 0 deletions
diff --git a/tests/i915/gem_softpin.c b/tests/i915/gem_softpin.c
new file mode 100644
index 00000000..336008b8
--- /dev/null
+++ b/tests/i915/gem_softpin.c
@@ -0,0 +1,517 @@
+/*
+ * Copyright © 2015 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:
+ * Vinay Belgaumkar <vinay.belgaumkar@intel.com>
+ * Thomas Daniel <thomas.daniel@intel.com>
+ *
+ */
+
+#include "igt.h"
+
+#define EXEC_OBJECT_PINNED (1<<4)
+#define EXEC_OBJECT_SUPPORTS_48B_ADDRESS (1<<3)
+
+/* gen8_canonical_addr
+ * Used to convert any address into canonical form, i.e. [63:48] == [47].
+ * Based on kernel's sign_extend64 implementation.
+ * @address - a virtual address
+*/
+#define GEN8_HIGH_ADDRESS_BIT 47
+static uint64_t gen8_canonical_addr(uint64_t address)
+{
+ __u8 shift = 63 - GEN8_HIGH_ADDRESS_BIT;
+ return (__s64)(address << shift) >> shift;
+}
+
+static void test_invalid(int fd)
+{
+ const uint32_t bbe = MI_BATCH_BUFFER_END;
+ struct drm_i915_gem_execbuffer2 execbuf;
+ struct drm_i915_gem_exec_object2 object;
+
+ memset(&execbuf, 0, sizeof(execbuf));
+ execbuf.buffers_ptr = to_user_pointer(&object);
+ execbuf.buffer_count = 1;
+
+ memset(&object, 0, sizeof(object));
+ object.handle = gem_create(fd, 2*4096);
+ object.flags = EXEC_OBJECT_SUPPORTS_48B_ADDRESS | EXEC_OBJECT_PINNED;
+ gem_write(fd, object.handle, 0, &bbe, sizeof(bbe));
+
+ /* Check invalid alignment */
+ object.offset = 4096;
+ object.alignment = 64*1024;
+ igt_assert_eq(__gem_execbuf(fd, &execbuf), -EINVAL);
+ object.alignment = 0;
+
+ /* Check wraparound */
+ object.offset = -4096ULL;
+ igt_assert_eq(__gem_execbuf(fd, &execbuf), -EINVAL);
+
+ /* Check beyond bounds of aperture */
+ object.offset = gem_aperture_size(fd) - 4096;
+ object.offset = gen8_canonical_addr(object.offset);
+ igt_assert_eq(__gem_execbuf(fd, &execbuf), -EINVAL);
+
+ /* Check gen8 canonical addressing */
+ if (gem_aperture_size(fd) > 1ull<<GEN8_HIGH_ADDRESS_BIT) {
+ object.offset = 1ull << GEN8_HIGH_ADDRESS_BIT;
+ igt_assert_eq(__gem_execbuf(fd, &execbuf), -EINVAL);
+
+ object.offset = gen8_canonical_addr(object.offset);
+ igt_assert_eq(__gem_execbuf(fd, &execbuf), 0);
+ }
+
+ /* Check extended range */
+ if (gem_aperture_size(fd) > 1ull<<32) {
+ object.flags = EXEC_OBJECT_PINNED;
+ object.offset = 1ull<<32;
+ igt_assert_eq(__gem_execbuf(fd, &execbuf), -EINVAL);
+
+ object.offset = gen8_canonical_addr(object.offset);
+ object.flags |= EXEC_OBJECT_SUPPORTS_48B_ADDRESS;
+ igt_assert_eq(__gem_execbuf(fd, &execbuf), 0);
+ }
+}
+
+static void test_softpin(int fd)
+{
+ const uint32_t size = 1024 * 1024;
+ const uint32_t bbe = MI_BATCH_BUFFER_END;
+ struct drm_i915_gem_execbuffer2 execbuf;
+ struct drm_i915_gem_exec_object2 object;
+ uint64_t offset, end;
+ uint32_t last_handle;
+ int loop;
+
+ last_handle = gem_create(fd, size);
+
+ memset(&execbuf, 0, sizeof(execbuf));
+ execbuf.buffers_ptr = to_user_pointer(&object);
+ execbuf.buffer_count = 1;
+ for (loop = 0; loop < 1024; loop++) {
+ memset(&object, 0, sizeof(object));
+ object.handle = gem_create(fd, 2*size);
+ gem_write(fd, object.handle, 0, &bbe, sizeof(bbe));
+
+ /* Find a hole */
+ gem_execbuf(fd, &execbuf);
+ gem_close(fd, object.handle);
+ gem_close(fd, last_handle);
+
+ igt_debug("Made a 2 MiB hole: %08llx\n",
+ object.offset);
+
+ object.handle = gem_create(fd, size);
+ gem_write(fd, object.handle, 0, &bbe, sizeof(bbe));
+ object.flags |= EXEC_OBJECT_PINNED;
+
+ end = object.offset + size;
+ for (offset = object.offset; offset <= end; offset += 4096) {
+ object.offset = offset;
+ gem_execbuf(fd, &execbuf);
+ igt_assert_eq_u64(object.offset, offset);
+ }
+
+ last_handle = object.handle;
+ }
+}
+
+static void test_overlap(int fd)
+{
+ const uint32_t size = 1024 * 1024;
+ const uint32_t bbe = MI_BATCH_BUFFER_END;
+ struct drm_i915_gem_execbuffer2 execbuf;
+ struct drm_i915_gem_exec_object2 object[2];
+ uint64_t offset;
+ uint32_t handle;
+
+ handle = gem_create(fd, 3*size);
+ gem_write(fd, handle, 0, &bbe, sizeof(bbe));
+
+ memset(object, 0, sizeof(object));
+ object[0].handle = handle;
+
+ /* Find a hole */
+ memset(&execbuf, 0, sizeof(execbuf));
+ execbuf.buffers_ptr = to_user_pointer(object);
+ execbuf.buffer_count = 1;
+ gem_execbuf(fd, &execbuf);
+
+ igt_debug("Made a 3x1 MiB hole: %08llx\n",
+ object[0].offset);
+
+ object[0].handle = gem_create(fd, size);
+ object[0].offset += size;
+ object[0].flags |= EXEC_OBJECT_PINNED;
+ object[1].handle = gem_create(fd, size);
+ object[1].flags |= EXEC_OBJECT_PINNED;
+ gem_write(fd, object[1].handle, 0, &bbe, sizeof(bbe));
+ execbuf.buffer_count = 2;
+
+ /* Check that we fit into our hole */
+ object[1].offset = object[0].offset - size;
+ gem_execbuf(fd, &execbuf);
+ igt_assert_eq_u64(object[1].offset + size, object[0].offset);
+
+ object[1].offset = object[0].offset + size;
+ gem_execbuf(fd, &execbuf);
+ igt_assert_eq_u64(object[1].offset - size, object[0].offset);
+
+ /* Try all possible page-aligned overlaps */
+ for (offset = object[0].offset - size + 4096;
+ offset < object[0].offset + size;
+ offset += 4096) {
+ object[1].offset = offset;
+ igt_debug("[0]=[%08llx - %08llx] [1]=[%08llx - %08llx]\n",
+ (long long)object[0].offset,
+ (long long)object[0].offset + size,
+ (long long)object[1].offset,
+ (long long)object[1].offset + size);
+ igt_assert_eq(__gem_execbuf(fd, &execbuf), -EINVAL);
+ igt_assert_eq_u64(object[1].offset, offset);
+ }
+
+ gem_close(fd, object[1].handle);
+ gem_close(fd, object[0].handle);
+ gem_close(fd, handle);
+}
+
+static uint64_t busy_batch(int fd)
+{
+ const int gen = intel_gen(intel_get_drm_devid(fd));
+ const int has_64bit_reloc = gen >= 8;
+ struct drm_i915_gem_execbuffer2 execbuf;
+ struct drm_i915_gem_exec_object2 object[2];
+ uint32_t *map;
+ int factor = 10;
+ int i = 0;
+
+ memset(object, 0, sizeof(object));
+ object[0].handle = gem_create(fd, 1024*1024);
+ object[1].handle = gem_create(fd, 4096);
+ map = gem_mmap__cpu(fd, object[1].handle, 0, 4096, PROT_WRITE);
+ gem_set_domain(fd, object[1].handle,
+ I915_GEM_DOMAIN_CPU, I915_GEM_DOMAIN_CPU);
+
+ *map = MI_BATCH_BUFFER_END;
+
+ memset(&execbuf, 0, sizeof(execbuf));
+ execbuf.buffers_ptr = to_user_pointer(object);
+ execbuf.buffer_count = 2;
+ if (gen >= 6)
+ execbuf.flags = I915_EXEC_BLT;
+ gem_execbuf(fd, &execbuf);
+
+ igt_debug("Active offsets = [%08llx, %08llx]\n",
+ object[0].offset, object[1].offset);
+
+#define COPY_BLT_CMD (2<<29|0x53<<22|0x6)
+#define BLT_WRITE_ALPHA (1<<21)
+#define BLT_WRITE_RGB (1<<20)
+ gem_set_domain(fd, object[1].handle,
+ I915_GEM_DOMAIN_CPU, I915_GEM_DOMAIN_CPU);
+ while (factor--) {
+ /* XY_SRC_COPY */
+ map[i++] = COPY_BLT_CMD | BLT_WRITE_ALPHA | BLT_WRITE_RGB;
+ if (has_64bit_reloc)
+ map[i-1] += 2;
+ map[i++] = 0xcc << 16 | 1 << 25 | 1 << 24 | (4*1024);
+ map[i++] = 0;
+ map[i++] = 256 << 16 | 1024;
+ map[i++] = object[0].offset;
+ if (has_64bit_reloc)
+ map[i++] = object[0].offset >> 32;
+ map[i++] = 0;
+ map[i++] = 4096;
+ map[i++] = object[0].offset;
+ if (has_64bit_reloc)
+ map[i++] = object[0].offset >> 32;
+ }
+ map[i++] = MI_BATCH_BUFFER_END;
+ munmap(map, 4096);
+
+ object[0].flags = EXEC_OBJECT_PINNED | EXEC_OBJECT_WRITE;
+ object[1].flags = EXEC_OBJECT_PINNED;
+ gem_execbuf(fd, &execbuf);
+ gem_close(fd, object[0].handle);
+ gem_close(fd, object[1].handle);
+
+ return object[1].offset;
+}
+
+static void test_evict_active(int fd)
+{
+ const uint32_t bbe = MI_BATCH_BUFFER_END;
+ struct drm_i915_gem_execbuffer2 execbuf;
+ struct drm_i915_gem_exec_object2 object;
+ uint64_t expected;
+
+ memset(&object, 0, sizeof(object));
+ object.handle = gem_create(fd, 4096);
+ gem_write(fd, object.handle, 0, &bbe, sizeof(bbe));
+
+ memset(&execbuf, 0, sizeof(execbuf));
+ execbuf.buffers_ptr = to_user_pointer(&object);
+ execbuf.buffer_count = 1;
+
+ expected = busy_batch(fd);
+ object.offset = expected;
+ object.flags = EXEC_OBJECT_PINNED;
+
+ /* Replace the active batch with ourselves, forcing an eviction */
+ gem_execbuf(fd, &execbuf);
+ igt_assert_eq_u64(object.offset, expected);
+
+ gem_close(fd, object.handle);
+}
+
+static void test_evict_snoop(int fd)
+{
+ const uint32_t bbe = MI_BATCH_BUFFER_END;
+ struct drm_i915_gem_execbuffer2 execbuf;
+ struct drm_i915_gem_exec_object2 object[2];
+ uint64_t hole;
+
+ igt_require(!gem_has_llc(fd));
+ igt_require(!gem_uses_ppgtt(fd));
+
+ memset(&execbuf, 0, sizeof(execbuf));
+ execbuf.buffers_ptr = to_user_pointer(object);
+ execbuf.buffer_count = 1;
+
+ /* Find a hole */
+ memset(object, 0, sizeof(object));
+ object[0].handle = gem_create(fd, 5*4096);
+ gem_write(fd, object[0].handle, 0, &bbe, sizeof(bbe));
+ gem_execbuf(fd, &execbuf);
+ gem_close(fd, object[0].handle);
+ hole = object[0].offset + 4096;
+
+ /* Create a snoop + uncached pair */
+ object[0].handle = gem_create(fd, 4096);
+ object[0].flags = EXEC_OBJECT_PINNED;
+ gem_set_caching(fd, object[0].handle, 1);
+ object[1].handle = gem_create(fd, 4096);
+ object[1].flags = EXEC_OBJECT_PINNED;
+ gem_write(fd, object[1].handle, 4096-sizeof(bbe), &bbe, sizeof(bbe));
+ execbuf.buffer_count = 2;
+
+ /* snoop abutting before uncached -> error */
+ object[0].offset = hole;
+ object[1].offset = hole + 4096;
+ igt_assert_eq(__gem_execbuf(fd, &execbuf), -EINVAL);
+
+ /* snoop abutting after uncached -> error */
+ object[0].offset = hole + 4096;
+ object[1].offset = hole;
+ igt_assert_eq(__gem_execbuf(fd, &execbuf), -EINVAL);
+
+ /* with gap -> okay */
+ object[0].offset = hole + 2*4096;
+ object[1].offset = hole;
+ igt_assert_eq(__gem_execbuf(fd, &execbuf), 0);
+
+ /* And we should force the snoop away (or the GPU may hang) */
+ object[0].flags = 0;
+ object[1].offset = hole + 4096;
+ igt_assert_eq(__gem_execbuf(fd, &execbuf), 0);
+ igt_assert(object[0].offset != hole);
+ igt_assert(object[0].offset != hole + 2*4096);
+
+ gem_close(fd, object[0].handle);
+ gem_close(fd, object[1].handle);
+}
+
+static void test_evict_hang(int fd)
+{
+ const uint32_t bbe = MI_BATCH_BUFFER_END;
+ struct drm_i915_gem_execbuffer2 execbuf;
+ struct drm_i915_gem_exec_object2 object;
+ igt_hang_t hang;
+ uint64_t expected;
+
+ memset(&object, 0, sizeof(object));
+ object.handle = gem_create(fd, 4096);
+ gem_write(fd, object.handle, 0, &bbe, sizeof(bbe));
+
+ memset(&execbuf, 0, sizeof(execbuf));
+ execbuf.buffers_ptr = to_user_pointer(&object);
+ execbuf.buffer_count = 1;
+
+ hang = igt_hang_ctx(fd, 0, 0, 0);
+ expected = hang.spin->obj[1].offset;
+
+ /* Replace the hung batch with ourselves, forcing an eviction */
+ object.offset = expected;
+ object.flags = EXEC_OBJECT_PINNED;
+ gem_execbuf(fd, &execbuf);
+ igt_assert_eq_u64(object.offset, expected);
+
+ igt_post_hang_ring(fd, hang);
+ gem_close(fd, object.handle);
+}
+
+static void xchg_offset(void *array, unsigned i, unsigned j)
+{
+ struct drm_i915_gem_exec_object2 *object = array;
+ uint64_t tmp = object[i].offset;
+ object[i].offset = object[j].offset;
+ object[j].offset = tmp;
+}
+
+enum sleep { NOSLEEP, SUSPEND, HIBERNATE };
+static void test_noreloc(int fd, enum sleep sleep)
+{
+ const int gen = intel_gen(intel_get_drm_devid(fd));
+ const uint32_t size = 4096;
+ const uint32_t bbe = MI_BATCH_BUFFER_END;
+ struct drm_i915_gem_execbuffer2 execbuf;
+ struct drm_i915_gem_exec_object2 object[257];
+ uint64_t offset;
+ uint32_t handle;
+ uint32_t *batch, *b;
+ int i, loop;
+
+ handle = gem_create(fd, (ARRAY_SIZE(object)+1)*size);
+ gem_write(fd, handle, 0, &bbe, sizeof(bbe));
+
+ memset(object, 0, sizeof(object));
+ object[0].handle = handle;
+
+ /* Find a hole */
+ memset(&execbuf, 0, sizeof(execbuf));
+ execbuf.buffers_ptr = to_user_pointer(object);
+ execbuf.buffer_count = 1;
+ if (gen < 6)
+ execbuf.flags |= I915_EXEC_SECURE;
+ gem_execbuf(fd, &execbuf);
+ gem_close(fd, object[0].handle);
+
+ igt_debug("Made a %dx%d KiB hole: %08llx\n",
+ (int)ARRAY_SIZE(object), size/1024, object[0].offset);
+
+ offset = object[0].offset;
+ for (i = 0; i < ARRAY_SIZE(object) - 1; i++) {
+ object[i].handle = gem_create(fd, size);
+ object[i].offset = offset + i*size;
+ object[i].flags = EXEC_OBJECT_PINNED | EXEC_OBJECT_WRITE;
+ }
+ object[i].handle = gem_create(fd, 2*size);
+ object[i].offset = offset + i*size;
+ object[i].flags = EXEC_OBJECT_PINNED;
+
+ b = batch = gem_mmap__cpu(fd, object[i].handle, 0, 2*size, PROT_WRITE);
+ gem_set_domain(fd, object[i].handle,
+ I915_GEM_DOMAIN_CPU, I915_GEM_DOMAIN_CPU);
+ for (i = 0; i < ARRAY_SIZE(object) - 1; i++) {
+ *b++ = MI_STORE_DWORD_IMM | (gen < 6 ? 1 << 22 : 0);
+ if (gen >= 8) {
+ *b++ = object[i].offset;
+ *b++ = object[i].offset >> 32;
+ } else if (gen >= 4) {
+ *b++ = 0;
+ *b++ = object[i].offset;
+ } else {
+ b[-1]--;
+ *b++ = object[i].offset;
+ }
+ *b++ = i;
+ }
+ *b++ = MI_BATCH_BUFFER_END;
+ igt_assert(b - batch <= 2*size/sizeof(uint32_t));
+ munmap(batch, size);
+
+ execbuf.buffer_count = ARRAY_SIZE(object);
+ for (loop = 0; loop < 1024; loop++) {
+ igt_permute_array(object, ARRAY_SIZE(object)-1, xchg_offset);
+ gem_execbuf(fd, &execbuf);
+
+ if ((loop & 127) == 0) {
+ switch (sleep) {
+ case NOSLEEP:
+ break;
+ case SUSPEND:
+ igt_system_suspend_autoresume(SUSPEND_STATE_MEM,
+ SUSPEND_TEST_NONE);
+ break;
+ case HIBERNATE:
+ igt_system_suspend_autoresume(SUSPEND_STATE_DISK,
+ SUSPEND_TEST_NONE);
+ break;
+ }
+ }
+
+ for (i = 0; i < ARRAY_SIZE(object) - 1; i++) {
+ uint32_t val;
+
+ gem_read(fd, object[i].handle, 0, &val, sizeof(val));
+ igt_assert_eq(val, (object[i].offset - offset)/size);
+ }
+ }
+
+ for (i = 0; i < ARRAY_SIZE(object); i++)
+ gem_close(fd, object[i].handle);
+}
+
+igt_main
+{
+ int fd = -1;
+
+ igt_skip_on_simulation();
+
+ igt_fixture {
+ fd = drm_open_driver_master(DRIVER_INTEL);
+ igt_require_gem(fd);
+ igt_require(gem_has_softpin(fd));
+ igt_require(gem_can_store_dword(fd, 0));
+ }
+
+ igt_subtest("invalid")
+ test_invalid(fd);
+ igt_subtest("softpin")
+ test_softpin(fd);
+ igt_subtest("overlap")
+ test_overlap(fd);
+
+ igt_subtest("noreloc")
+ test_noreloc(fd, NOSLEEP);
+ igt_subtest("noreloc-interruptible")
+ igt_while_interruptible(true) test_noreloc(fd, NOSLEEP);
+ igt_subtest("noreloc-S3")
+ test_noreloc(fd, SUSPEND);
+ igt_subtest("noreloc-S4")
+ test_noreloc(fd, HIBERNATE);
+
+ for (int signal = 0; signal <= 1; signal++) {
+ igt_subtest_f("evict-active%s", signal ? "-interruptible" : "")
+ igt_while_interruptible(signal) test_evict_active(fd);
+ igt_subtest_f("evict-snoop%s", signal ? "-interruptible" : "")
+ igt_while_interruptible(signal) test_evict_snoop(fd);
+ }
+ igt_subtest("evict-hang")
+ test_evict_hang(fd);
+
+ igt_fixture
+ close(fd);
+}