ntel-gpu-tools/tests/gem_softpin.c
Chris Wilson 6bada3e2d4 igt/gem_softpin: Remove false dependencies on esoteric features
For softpinning, we do not require either userptr or extended ppgtt, so
remove those requirements and make the tests work universally. (Certain
ABI tests require large GTT, or per-process GTT.)

In the process, make the tests more extensive - validate overlapping
handling more careful, explicitly test no-relocation support, validate
more ABI handling. And for fun, cause a kernel GPF.

Signed-off-by: Chris Wilson <chris@chris-wilson.co.uk>
2016-01-20 07:53:22 +00:00

449 lines
12 KiB
C

/*
* 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)
/* has_softpin_support
* Finds if softpin feature is supported
* @fd DRM fd
*/
static bool has_softpin_support(int fd)
{
struct drm_i915_getparam gp;
int val = 0;
memset(&gp, 0, sizeof(gp));
gp.param = 37; /* I915_PARAM_HAS_EXEC_SOFTPIN */
gp.value = &val;
if (drmIoctl(fd, DRM_IOCTL_I915_GETPARAM, &gp))
return 0;
errno = 0;
return (val == 1);
}
/* 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 int __gem_execbuf(int fd, struct drm_i915_gem_execbuffer2 *eb)
{
int err = 0;
if (drmIoctl(fd, DRM_IOCTL_I915_GEM_EXECBUFFER2, eb))
err = -errno;
return err;
}
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 = (unsigned long)&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;
int loop;
memset(&execbuf, 0, sizeof(execbuf));
execbuf.buffers_ptr = (unsigned long)&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);
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);
}
gem_close(fd, 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 = (unsigned long)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 = (unsigned long)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));
expected = busy_batch(fd);
object.offset = expected;
object.flags = EXEC_OBJECT_PINNED;
/* Replace the active batch with ourselves, forcing an eviction */
memset(&execbuf, 0, sizeof(execbuf));
execbuf.buffers_ptr = (unsigned long)&object;
execbuf.buffer_count = 1;
gem_execbuf(fd, &execbuf);
gem_close(fd, object.handle);
igt_assert_eq_u64(object.offset, expected);
}
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;
uint64_t expected;
igt_hang_ring_t hang;
memset(&object, 0, sizeof(object));
object.handle = gem_create(fd, 4096);
gem_write(fd, object.handle, 0, &bbe, sizeof(bbe));
hang = igt_hang_ctx(fd, 0, 0, 0, (uint64_t *)&expected);
object.offset = expected;
object.flags = EXEC_OBJECT_PINNED;
/* Replace the hanging batch with ourselves, forcing an eviction */
memset(&execbuf, 0, sizeof(execbuf));
execbuf.buffers_ptr = (unsigned long)&object;
execbuf.buffer_count = 1;
gem_execbuf(fd, &execbuf);
gem_close(fd, object.handle);
igt_assert_eq_u64(object.offset, expected);
igt_post_hang_ring(fd, hang);
}
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;
}
static void test_noreloc(int fd)
{
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 = (unsigned long)object;
execbuf.buffer_count = 1;
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;
if (gen < 8) {
*b++ = 0;
*b++ = object[i].offset;
} else {
*b++ = object[i].offset;
*b++ = object[i].offset >> 32;
}
*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);
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(DRIVER_INTEL);
igt_require(has_softpin_support(fd));
}
igt_subtest("invalid")
test_invalid(fd);
igt_subtest("softpin")
test_softpin(fd);
igt_subtest("overlap")
test_overlap(fd);
igt_subtest("noreloc")
test_noreloc(fd);
igt_subtest("evict-active")
test_evict_active(fd);
igt_subtest("evict-hang")
test_evict_hang(fd);
igt_fixture
close(fd);
}