summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorVenkata Sandeep, Dhanalakota <venkata.s.dhanalakota@intel.com>2019-03-20 13:01:34 -0700
committerChris Wilson <chris@chris-wilson.co.uk>2019-03-21 00:09:14 +0000
commitd562b894e0a62920086bb377fd6b9630af53d6e1 (patch)
tree862af996548b1018fe4df7ca1ce57fbdac5fad8f
parentfd9458ffd595f8c7c36361a930913d12f37135ca (diff)
i915/gem_sparseobject: Add sparse object IGT testsfor-sandeep
Sparse object is a proxy object that can link the pages from other objects. Typical use case are huge continuos framebuffer that is too big to handle scanout. Sparse object can used as any other regular object after the pages from source object are linked. This patch introduces igt test to exercise various usages of the sparse object, which include: 1) read/write/mmap sparse object after it is linked to various source objects which have backing pages in stolen memory, internal memory etc. 2) Feeding sparse object to execbuf after it is linked with source object. Driver changes are posted here https://cgit.freedesktop.org/~ickle/linux-2.6/ commit/?h=wip-sparseobject&id=cdd6f93f05c6606d7d01cb0e8596e21b2e7eef04 This is still WIP gets updated. Compile-tested only. Signed-off-by: Venkata Sandeep, Dhanalakota <venkata.s.dhanalakota@intel.com>
-rw-r--r--include/drm-uapi/i915_drm.h71
-rw-r--r--lib/Makefile.sources2
-rw-r--r--lib/i915/gem_sparse.c121
-rw-r--r--lib/i915/gem_sparse.h35
-rw-r--r--lib/ioctl_wrappers.c19
-rw-r--r--lib/ioctl_wrappers.h1
-rw-r--r--lib/meson.build1
-rw-r--r--tests/Makefile.sources3
-rw-r--r--tests/i915/gem_sparseobject.c608
-rw-r--r--tests/meson.build1
10 files changed, 855 insertions, 7 deletions
diff --git a/include/drm-uapi/i915_drm.h b/include/drm-uapi/i915_drm.h
index cb6d6617..3f32b372 100644
--- a/include/drm-uapi/i915_drm.h
+++ b/include/drm-uapi/i915_drm.h
@@ -348,6 +348,7 @@ typedef struct _drm_i915_sarea {
#define DRM_I915_QUERY 0x39
#define DRM_I915_GEM_VM_CREATE 0x3a
#define DRM_I915_GEM_VM_DESTROY 0x3b
+#define DRM_I915_GEM_SET_PAGES 0x3c
/* Must be kept compact -- no holes */
#define DRM_IOCTL_I915_INIT DRM_IOW( DRM_COMMAND_BASE + DRM_I915_INIT, drm_i915_init_t)
@@ -380,6 +381,7 @@ typedef struct _drm_i915_sarea {
#define DRM_IOCTL_I915_GEM_ENTERVT DRM_IO(DRM_COMMAND_BASE + DRM_I915_GEM_ENTERVT)
#define DRM_IOCTL_I915_GEM_LEAVEVT DRM_IO(DRM_COMMAND_BASE + DRM_I915_GEM_LEAVEVT)
#define DRM_IOCTL_I915_GEM_CREATE DRM_IOWR(DRM_COMMAND_BASE + DRM_I915_GEM_CREATE, struct drm_i915_gem_create)
+#define DRM_IOCTL_I915_GEM_CREATE_EXT DRM_IOWR(DRM_COMMAND_BASE + DRM_I915_GEM_CREATE, struct drm_i915_gem_create_ext)
#define DRM_IOCTL_I915_GEM_PREAD DRM_IOW (DRM_COMMAND_BASE + DRM_I915_GEM_PREAD, struct drm_i915_gem_pread)
#define DRM_IOCTL_I915_GEM_PWRITE DRM_IOW (DRM_COMMAND_BASE + DRM_I915_GEM_PWRITE, struct drm_i915_gem_pwrite)
#define DRM_IOCTL_I915_GEM_MMAP DRM_IOWR(DRM_COMMAND_BASE + DRM_I915_GEM_MMAP, struct drm_i915_gem_mmap)
@@ -410,6 +412,7 @@ typedef struct _drm_i915_sarea {
#define DRM_IOCTL_I915_QUERY DRM_IOWR(DRM_COMMAND_BASE + DRM_I915_QUERY, struct drm_i915_query)
#define DRM_IOCTL_I915_GEM_VM_CREATE DRM_IOWR(DRM_COMMAND_BASE + DRM_I915_GEM_VM_CREATE, struct drm_i915_gem_vm_control)
#define DRM_IOCTL_I915_GEM_VM_DESTROY DRM_IOW (DRM_COMMAND_BASE + DRM_I915_GEM_VM_DESTROY, struct drm_i915_gem_vm_control)
+#define DRM_IOCTL_I915_GEM_SET_PAGES DRM_IOW(DRM_COMMAND_BASE + DRM_I915_GEM_SET_PAGES, struct drm_i915_gem_set_pages)
/* Allow drivers to submit batchbuffers directly to hardware, relying
* on the security mechanisms provided by hardware.
@@ -599,6 +602,14 @@ typedef struct drm_i915_irq_wait {
* See I915_EXEC_FENCE_OUT and I915_EXEC_FENCE_SUBMIT.
*/
#define I915_PARAM_HAS_EXEC_SUBMIT_FENCE 53
+
+/*
+ * Query the version of DRM_I915_GEM_CREATE supported.
+ * v0 - Initial version
+ * v1 - Adds flags and support for creating of sparse objects
+ */
+#define I915_PARAM_CREATE_VERSION 54
+
/* Must be kept compact -- no holes and well documented */
typedef struct drm_i915_getparam {
@@ -702,6 +713,25 @@ struct drm_i915_gem_create {
__u32 pad;
};
+struct drm_i915_gem_create_ext {
+ /**
+ * Requested size for the object.
+ *
+ * The (page-aligned) allocated size for the object will be returned.
+ */
+ __u64 size;
+ /**
+ * Returned handle for the object.
+ *
+ * Object handles are nonzero.
+ */
+ __u32 handle;
+ __u32 pad;
+
+ __u64 flags;
+#define I915_GEM_CREATE_SPARSE 0x1
+};
+
struct drm_i915_gem_pread {
/** Handle for the object being read. */
__u32 handle;
@@ -1356,6 +1386,20 @@ struct drm_i915_gem_madvise {
__u32 retained;
};
+struct drm_i915_gem_set_pages {
+ __u32 dst_handle;
+ __u32 src_handle;
+
+ __u64 dst_offset;
+ __u64 src_offset;
+
+ __u64 dst_stride;
+ __u64 src_stride;
+
+ __u64 width;
+ __u64 height;
+};
+
/* flags */
#define I915_OVERLAY_TYPE_MASK 0xff
#define I915_OVERLAY_YUV_PLANAR 0x01
@@ -1982,6 +2026,33 @@ struct drm_i915_query_item {
__u64 data_ptr;
};
+struct i915_engine_hw_info_v1 {
+ __u64 flags;
+
+ __u16 uabi_class;
+ __u16 hw_class;
+ __u16 hw_id;
+ __u16 instance;
+
+ __u32 mmio_base;
+ __u32 context_size;
+
+ __u32 rsvd[10];
+};
+
+struct drm_i915_query_engine_hw_info_v1 {
+ __u32 num_engines;
+ __u32 flags;
+#define I915_QUERY_HW_INFO_HAS_HW_CLASS (1u << 0)
+#define I915_QUERY_HW_INFO_HAS_HW_INSTANCE (1u << 1)
+#define I915_QUERY_HW_INFO_HAS_HW_ID (1u << 2)
+#define I915_QUERY_HW_INFO_HAS_MMIO_BASE (1u << 3)
+#define I915_QUERY_HW_INFO_HAS_CONTEXT_SIZE (1u << 4)
+ __u32 rsvd[14]; /* mbz */
+
+ struct i915_engine_hw_info_v1 engines[0];
+};
+
struct drm_i915_query {
__u32 num_items;
diff --git a/lib/Makefile.sources b/lib/Makefile.sources
index a7074209..aab8003e 100644
--- a/lib/Makefile.sources
+++ b/lib/Makefile.sources
@@ -9,6 +9,8 @@ lib_source_list = \
i915/gem_scheduler.h \
i915/gem_submission.c \
i915/gem_submission.h \
+ i915/gem_sparse.h \
+ i915/gem_sparse.c \
i915/gem_ring.h \
i915/gem_ring.c \
i915/gem_mman.c \
diff --git a/lib/i915/gem_sparse.c b/lib/i915/gem_sparse.c
new file mode 100644
index 00000000..0b0dc7d1
--- /dev/null
+++ b/lib/i915/gem_sparse.c
@@ -0,0 +1,121 @@
+/*
+ * Copyright © 2017 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.
+ */
+
+#include <errno.h>
+#include <string.h>
+
+#include "ioctl_wrappers.h"
+#include "drmtest.h"
+
+#include "gem_sparse.h"
+
+/**
+ * gem_create_sparse:
+ * @fd: open i915 drm file descriptor
+ * @size: desired size of the buffer
+ *
+ * This wraps the new GEM_CREATE ioctl, which allocates a new sparse gem buffer
+ * object of @size.
+ *
+ * Returns: The file-private handle of the created buffer object
+ */
+
+static int __gem_create_sparse(int fd, uint64_t size, uint32_t *out)
+{
+ struct drm_i915_gem_create_ext create = {
+ .size = size,
+ .flags = I915_GEM_CREATE_SPARSE,
+ };
+ int err;
+
+ if (igt_ioctl(fd, DRM_IOCTL_I915_GEM_CREATE_EXT, &create) == 0) {
+ *out = create.handle;
+ err = 0;
+ } else {
+ err = -errno;
+ igt_assume(err);
+ }
+
+ errno = 0;
+ return err;
+}
+
+uint32_t gem_create_sparse(int i915, uint64_t size)
+{
+ uint32_t handle = 0;
+
+ igt_assert_eq(__gem_create_sparse(i915, size, &handle), 0);
+ igt_assert(handle);
+
+ return handle;
+}
+static
+int __gem_set_pages(int fd, uint32_t dst_handle, uint32_t dst_offset,
+ uint32_t dst_stride, uint32_t src_handle,
+ uint32_t src_offset, uint32_t src_stride,
+ uint32_t width, uint32_t height)
+{
+ struct drm_i915_gem_set_pages set_pages;
+ int err;
+
+ set_pages.dst_handle = dst_handle;
+ set_pages.dst_offset = dst_offset;
+ set_pages.dst_stride = dst_stride;
+ set_pages.src_handle = src_handle;
+ set_pages.src_offset = src_offset;
+ set_pages.src_stride = src_stride;
+ set_pages.width = width;
+ set_pages.height = height;
+
+ err = 0;
+ if(igt_ioctl(fd, DRM_IOCTL_I915_GEM_SET_PAGES, &set_pages))
+ err = -errno;
+ errno = 0;
+ return err;
+}
+
+/**
+ * gem_set_pages:
+ * All parameters other than fd and handles are in units of pages.
+ * @fd: open i915 drm file descriptor
+ * @dst_handle: destination sparse object handle
+ * @dst_offset: offset in destination object where mapping is done
+ * @dst_stride: destination object stride
+ * @src_handle: source object handle
+ * @src_offset: offset in the source object
+ * @src_stride: src object stride
+ * @width: width of rectangle to map
+ * @height: height of rectange to map
+ *
+ * This wraps SET_PAGES ioctl, which maps pages from src object to the
+ * destination object.
+ */
+void gem_set_pages(int fd, uint32_t dst_handle, uint32_t dst_offset,
+ uint32_t dst_stride, uint32_t src_handle,
+ uint32_t src_offset, uint32_t src_stride,
+ uint32_t width, uint32_t height)
+{
+ igt_assert_eq(__gem_set_pages(fd, dst_handle, dst_offset, dst_stride,
+ src_handle, src_offset, src_stride,
+ width, height), 0);
+}
diff --git a/lib/i915/gem_sparse.h b/lib/i915/gem_sparse.h
new file mode 100644
index 00000000..71b35fb6
--- /dev/null
+++ b/lib/i915/gem_sparse.h
@@ -0,0 +1,35 @@
+/*
+ * Copyright © 2017 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.
+ */
+
+#ifndef GEM_SPARSE_H
+#define GEM_SPARSE_H
+
+#include <stdint.h>
+
+uint32_t gem_create_sparse(int i915, uint64_t size);
+void gem_set_pages(int fd, uint32_t dst_handle, uint32_t dst_offset,
+ uint32_t dst_stride, uint32_t src_handle,
+ uint32_t src_offset, uint32_t src_stride,
+ uint32_t width, uint32_t height);
+
+#endif /* GEM_SPARSE_H */
diff --git a/lib/ioctl_wrappers.c b/lib/ioctl_wrappers.c
index a66eb4bc..aa432fa9 100644
--- a/lib/ioctl_wrappers.c
+++ b/lib/ioctl_wrappers.c
@@ -305,6 +305,17 @@ uint32_t gem_flink(int fd, uint32_t handle)
return flink.name;
}
+int __gem_close(int fd, uint32_t handle)
+{
+ int err = 0;
+
+ if (igt_ioctl(fd, DRM_IOCTL_GEM_CLOSE, &handle))
+ err = -errno;
+
+ errno = 0;
+ return err;
+}
+
/**
* gem_close:
* @fd: open i915 drm file descriptor
@@ -315,13 +326,7 @@ uint32_t gem_flink(int fd, uint32_t handle)
*/
void gem_close(int fd, uint32_t handle)
{
- struct drm_gem_close close_bo;
-
- igt_assert_neq(handle, 0);
-
- memset(&close_bo, 0, sizeof(close_bo));
- close_bo.handle = handle;
- do_ioctl(fd, DRM_IOCTL_GEM_CLOSE, &close_bo);
+ igt_assert_eq(__gem_close(fd, handle), 0);
}
int __gem_write(int fd, uint32_t handle, uint64_t offset, const void *buf, uint64_t length)
diff --git a/lib/ioctl_wrappers.h b/lib/ioctl_wrappers.h
index ad93daff..1361215b 100644
--- a/lib/ioctl_wrappers.h
+++ b/lib/ioctl_wrappers.h
@@ -66,6 +66,7 @@ void gem_set_caching(int fd, uint32_t handle, uint32_t caching);
uint32_t gem_get_caching(int fd, uint32_t handle);
uint32_t gem_flink(int fd, uint32_t handle);
uint32_t gem_open(int fd, uint32_t name);
+int __gem_close(int fd, uint32_t handle);
void gem_close(int fd, uint32_t handle);
int __gem_write(int fd, uint32_t handle, uint64_t offset, const void *buf, uint64_t length);
void gem_write(int fd, uint32_t handle, uint64_t offset, const void *buf, uint64_t length);
diff --git a/lib/meson.build b/lib/meson.build
index f9592233..45a6f424 100644
--- a/lib/meson.build
+++ b/lib/meson.build
@@ -3,6 +3,7 @@ lib_sources = [
'i915/gem_context.c',
'i915/gem_scheduler.c',
'i915/gem_submission.c',
+ 'i915/gem_sparse.c',
'i915/gem_ring.c',
'i915/gem_mman.c',
'i915/gem_vm.c',
diff --git a/tests/Makefile.sources b/tests/Makefile.sources
index 8981a8ae..cdd81cd3 100644
--- a/tests/Makefile.sources
+++ b/tests/Makefile.sources
@@ -275,6 +275,9 @@ gem_gtt_speed_SOURCES = i915/gem_gtt_speed.c
TESTS_progs += gem_largeobject
gem_largeobject_SOURCES = i915/gem_largeobject.c
+TESTS_progs += gem_sparseobject
+gem_sparseobject_SOURCES = i915/gem_sparseobject.c
+
TESTS_progs += gem_linear_blits
gem_linear_blits_SOURCES = i915/gem_linear_blits.c
diff --git a/tests/i915/gem_sparseobject.c b/tests/i915/gem_sparseobject.c
new file mode 100644
index 00000000..a063abb8
--- /dev/null
+++ b/tests/i915/gem_sparseobject.c
@@ -0,0 +1,608 @@
+/*
+ * Copyright © 2008 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.
+ *
+ *
+ */
+
+#include "igt.h"
+#include "i915/gem_sparse.h"
+
+#include <stdlib.h>
+#include <stdio.h>
+#include <string.h>
+#include <fcntl.h>
+#include <inttypes.h>
+#include <errno.h>
+#include <sys/stat.h>
+#include <sys/ioctl.h>
+#include "drm.h"
+
+IGT_TEST_DESCRIPTION("This is a test to exercise sparse object and backing it with,"
+ " allocation of object from stolen memory and shmem.");
+
+#define OBJ_SIZE (4 * 4096)
+#define PAGE_SIZE 4096
+#define TIMEOUT 20
+#define MAX_HANDLES 4096
+#define LARGE_OBJECT_SIZE (16 * 1024 * 1024)
+#define MAX_USRPTR 20
+
+enum mmap_type{
+ GEM_MMAP_CPU,
+ GEM_MMAP_WC,
+ GEM_MMAP_GTT,
+ GEM_MMAP_NONE
+};
+
+enum source_type {
+ UNSET,
+ SHMEM,
+ USERPTR,
+ __LAST_SOURCE__
+};
+
+void *__userptr[4096];
+
+/**
+ * mmaps on bo and returns a pointer to user
+ */
+static uint32_t gem_create_userptr(int i915, uint64_t size)
+{
+ uint32_t handle;
+ void *ptr;
+
+ igt_assert(posix_memalign(&ptr, PAGE_SIZE, size) == 0);
+
+ gem_userptr(i915, ptr, size, 0, 0, &handle);
+
+ igt_assert(handle < ARRAY_SIZE(__userptr));
+ __userptr[handle] = ptr;
+
+ return handle;
+}
+
+static const char *source_name(enum source_type type)
+{
+ switch (type) {
+ case UNSET: return "unset";
+ case SHMEM: return "shmem";
+ case USERPTR: return "userptr";
+ default: igt_assert(0); return "unknown";
+ }
+}
+
+static uint32_t source_create(int i915, enum source_type type, uint64_t size)
+{
+ switch(type) {
+ case UNSET: return 0;
+ case SHMEM: return gem_create(i915, size);
+ case USERPTR: return gem_create_userptr(i915, size);
+ default: igt_assert(0); return 0;
+ }
+}
+
+static void source_destroy(int i915, uint32_t handle)
+{
+ if (!handle)
+ return;
+
+ gem_close(i915, handle);
+
+ free(__userptr[handle]);
+ __userptr[handle] = NULL;
+}
+
+#if 0
+static void *
+mmap_bo(int i915, uint32_t handle, enum mmap_type type)
+{
+ void *ptr;
+ switch (type) {
+ case GEM_MMAP_CPU:
+ ptr = gem_mmap__cpu(i915, handle, 0, PAGE_SIZE,
+ PROT_READ | PROT_WRITE);
+ break;
+ case GEM_MMAP_WC:
+ ptr = gem_mmap__wc(i915, handle, 0, PAGE_SIZE,
+ PROT_READ | PROT_WRITE);
+ break;
+ case GEM_MMAP_GTT:
+ ptr = gem_mmap__gtt(i915, handle, PAGE_SIZE,
+ PROT_READ | PROT_WRITE);
+ break;
+ default:
+ return NULL;
+ }
+
+ return ptr;
+}
+
+static void
+fill_sources(int i915, uint32_t sources[], int size)
+{
+ for (int i = 0; i < size; i++)
+ sources[i] = source_create(i915, i, OBJ_SIZE);
+}
+
+static void
+gem_link_sources(int i915, uint32_t sparse_handle, uint32_t sources[], int sz)
+{
+ int i;
+ char expected[OBJ_SIZE];
+ char buf[OBJ_SIZE];
+ int npages = OBJ_SIZE/PAGE_SIZE;
+
+ memset(expected, 0xfeedbeef, OBJ_SIZE);
+ while(i < sz) {
+ uint32_t ht = 0;
+ uint32_t src_stride = (rand() % (npages - 1)) + 1;
+ uint32_t dst_stride = (rand() % (npages - 1)) + 1;
+ uint32_t width = rand() % min(src_stride, dst_stride);
+ uint32_t height = rand() % max(src_stride, dst_stride);
+ uint32_t src_offset = rand() % (npages - (src_stride *
+ (height - 1)) - width);
+ uint32_t dst_offset = rand() % (npages - (dst_stride *
+ (height - 1)) - width);
+ char *data;
+ data = expected;
+ /* Write a pattern into src object */
+ while (ht <= height) {
+ gem_write(i915, sources[i],
+ src_offset + (ht * src_stride * PAGE_SIZE),
+ data, width * PAGE_SIZE);
+ data += (width * PAGE_SIZE);
+ ht++;
+ }
+
+ /* ioctl to link pages from src to sparse object */
+ gem_set_pages(i915, sparse_handle, dst_offset, dst_stride,
+ sources[i], src_offset, src_stride, width, height);
+ ht = 0;
+ data = buf;
+ /* read back the data from mapped from sources */
+ while (ht <= height) {
+ gem_read(i915, sparse_handle,
+ dst_offset + (ht * dst_stride),
+ data, (width * PAGE_SIZE));
+ data += (width * PAGE_SIZE);
+ ht++;
+ }
+ igt_assert(memcmp(buf, expected, width * height * PAGE_SIZE) == 0);
+ i++;
+ }
+}
+
+/*
+ * Randominly picks the source objects and links with
+ * sparse object at random offsets
+ */
+static void
+igt_gem_sparse_link_nsources(int i915)
+{
+ int size;
+ uint32_t sources[GEM_OBJ_SPARSE];
+ uint32_t sparse_handle;
+ int i;
+
+ igt_until_timeout(5 * TIMEOUT) {
+ size = rand() % GEM_OBJ_SPARSE;
+ fill_sources(i915, sources, size);
+ sparse_handle = gem_create_sparse(i915, LARGE_OBJECT_SIZE);
+ gem_link_sources(i915, sparse_handle, sources, size);
+ gem_close(i915, sparse_handle);
+ for (i = 0; i < usrcount; i++)
+ free((void *)usrptr[i]);
+ usrcount = 0;
+ for (i = 0; i < size; i++)
+ gem_close(i915, sources[i]);
+ }
+}
+
+/* Smoke test with single destination sparse object */
+static void igt_gem_sparse_smoketest_single_destination(int i915)
+{
+ uint32_t sparse_handle, src_handle;
+ int npages = OBJ_SIZE/PAGE_SIZE;
+ char expected[OBJ_SIZE];
+ char buf[OBJ_SIZE];
+ char *data;
+ uint32_t handles[MAX_HANDLES];
+ int count, i;
+
+ sparse_handle = gem_create_sparse(i915, OBJ_SIZE);
+ memset(expected, 0xfeedbeef, OBJ_SIZE);
+
+ igt_until_timeout(TIMEOUT) {
+ uint32_t src_stride = (rand() % (npages - 1)) + 1;
+ uint32_t dst_stride = (rand() % (npages - 1)) + 1;
+ uint32_t width = rand() % min(src_stride, dst_stride);
+ uint32_t height = rand() % max(src_stride, dst_stride);
+ uint32_t src_offset = rand() % (npages - (src_stride *
+ (height - 1)) - width);
+ uint32_t dst_offset = rand() % (npages - (dst_stride *
+ (height - 1)) - width);
+ uint32_t ht = 0;
+
+ src_handle = gem_create(i915, OBJ_SIZE);
+ handles[count++] = src_handle;
+ data = expected;
+ /* Write a pattern into src object */
+ while (ht <= height) {
+ gem_write(i915, src_handle,
+ src_offset + (ht * src_stride * PAGE_SIZE),
+ data, width * PAGE_SIZE);
+ data += (width * PAGE_SIZE);
+ ht++;
+ }
+
+ /* ioctl to link pages from src to sparse object */
+ gem_set_pages(i915, sparse_handle, dst_offset, dst_stride,
+ src_handle, src_offset, src_stride, width, height);
+ ht = 0;
+ data = buf;
+ /* read back the data from mapped from sources */
+ while (ht <= height) {
+ gem_read(i915, sparse_handle,
+ dst_offset + (ht * dst_stride * PAGE_SIZE),
+ data, (width * PAGE_SIZE));
+ data += (width * PAGE_SIZE);
+ ht++;
+ }
+ igt_assert(memcmp(buf, expected, width * height * PAGE_SIZE) == 0);
+ }
+ for (i = 0; i<count; i++)
+ gem_close(i915, handles[i]);
+ gem_close(i915, sparse_handle);
+}
+
+/* sparse object smoke test with single source */
+static void igt_gem_sparse_smoketest_single_source(int i915)
+{
+ uint32_t sparse_handle, src_handle;
+ int npages = OBJ_SIZE / PAGE_SIZE;
+ char expected[OBJ_SIZE];
+ char buf[OBJ_SIZE];
+ char *data;
+ uint32_t handles[MAX_HANDLES];
+ int count, i;
+
+ sparse_handle = gem_create_sparse(i915, OBJ_SIZE);
+ src_handle = gem_create(i915, OBJ_SIZE);
+ memset(expected, 0xfeedbeef, OBJ_SIZE);
+
+ igt_until_timeout(TIMEOUT) {
+ uint32_t src_stride = (rand() % (npages - 1)) + 1;
+ uint32_t dst_stride = (rand() % (npages - 1)) + 1;
+ uint32_t width = rand() % min(src_stride, dst_stride);
+ uint32_t height = rand() % max(src_stride, dst_stride);
+ uint32_t src_offset = rand() % (npages - (src_stride *
+ (height - 1)) - width);
+ uint32_t dst_offset = rand() % (npages - (dst_stride *
+ (height - 1)) - width);
+ uint32_t ht = 0;
+
+ sparse_handle = gem_create_sparse(i915, OBJ_SIZE);
+ handles[count++] = sparse_handle;
+ data = expected;
+ while (ht <= height) {
+ gem_write(i915, src_handle,
+ src_offset + (ht * src_stride * PAGE_SIZE),
+ data, width * PAGE_SIZE);
+ data += (width * PAGE_SIZE);
+ ht++;
+ }
+ gem_set_pages(i915, sparse_handle, dst_offset, dst_stride,
+ src_handle, src_offset, src_stride, width, height);
+ ht = 0;
+ data = buf;
+ while (ht <= height) {
+ gem_read(i915, sparse_handle,
+ dst_offset + (ht * dst_stride * PAGE_SIZE),
+ data, (width * PAGE_SIZE));
+ data += (width * PAGE_SIZE);
+ ht++;
+ }
+ igt_assert(memcmp(buf, expected, (width * PAGE_SIZE) * height) == 0);
+ }
+ for (i = 0; i<count; i++)
+ gem_close(i915, handles[i]);
+ gem_close(i915, src_handle);
+}
+#endif
+
+static void igt_sparse_read(int i915, uint32_t src)
+{
+ uint32_t sparse;
+
+ sparse = gem_create_sparse(i915, OBJ_SIZE);
+
+ /* forwards */
+ if (src) {
+ for (uint32_t n = 0; n < 4; n++) {
+ gem_set_pages(i915,
+ sparse, n, 0,
+ src, n, 0,
+ 1, 1);
+ gem_write(i915, src, PAGE_SIZE * n, &n, sizeof(n));
+ }
+ }
+
+ for (uint32_t n = 0; n < 4; n++) {
+ uint32_t expected = src ? n : 0;
+ uint32_t x;
+
+ gem_read(i915, sparse, PAGE_SIZE * n , &x, sizeof(x));
+ igt_assert_eq(x, expected);
+ }
+
+ if (src) {
+ for (uint32_t n = 0; n < 4; n++) {
+ gem_set_pages(i915,
+ sparse, n, 0,
+ src, 4 - n -1, 0,
+ 1, 1);
+ gem_write(i915, src,
+ PAGE_SIZE * n + 1024, &n, sizeof(n));
+ }
+ }
+
+ for (uint32_t n = 0; n < 4; n++) {
+ uint32_t expected = src ? 4 - n - 1 : 0;
+ uint32_t x;
+
+ gem_read(i915, sparse, PAGE_SIZE * n + 1024, &x, sizeof(x));
+ igt_assert_eq(x, expected);
+ }
+
+ gem_close(i915, sparse);
+}
+
+static void igt_sparse_write(int i915, uint32_t src)
+{
+ uint32_t sparse;
+
+ igt_require(src);
+
+ sparse = gem_create_sparse(i915, OBJ_SIZE);
+
+ /* forwards */
+ for (uint32_t n = 0; n < 4; n++) {
+ uint32_t ex = n, x;
+
+ gem_set_pages(i915,
+ sparse, n, 0,
+ src, n, 0,
+ 1, 1);
+
+ gem_write(i915, sparse, PAGE_SIZE * n , &ex, sizeof(ex));
+ gem_read(i915, src, PAGE_SIZE * n, &x, sizeof(x));
+
+ igt_assert_eq(x, ex);
+ }
+
+ for (uint32_t n = 0; n < 4; n++) {
+ uint32_t ex = 4 - n - 1, x;
+
+ gem_set_pages(i915,
+ sparse, n, 0,
+ src, 4 - n - 1, 0,
+ 1, 1);
+
+ gem_write(i915, sparse, PAGE_SIZE * n + 1024, &ex, sizeof(ex));
+ gem_read(i915, src, PAGE_SIZE * ex + 1024, &x, sizeof(x));
+
+ igt_assert_eq(x, ex);
+ }
+
+ gem_close(i915, sparse);
+}
+
+static void igt_sparse_mmap(int i915, uint32_t src)
+{
+ uint32_t sparse;
+ uint32_t *ptr;
+
+ igt_require(src);
+
+ sparse = gem_create_sparse(i915, OBJ_SIZE);
+ ptr = gem_mmap__gtt(i915, sparse, OBJ_SIZE, PROT_READ);
+
+ for (uint32_t n = 0; n < 4; n++) {
+ uint32_t ex = n;
+
+ gem_set_pages(i915,
+ sparse, n, 0,
+ src, n, 0,
+ 1, 1);
+
+ gem_write(i915, src, PAGE_SIZE * n , &ex, sizeof(ex));
+ igt_assert_eq(ptr[PAGE_SIZE * n / sizeof(*ptr)], ex);
+
+ }
+
+ munmap(ptr, OBJ_SIZE);
+ gem_close(i915, sparse);
+}
+
+static void store_dword(int i915, unsigned engine,
+ uint32_t dst, uint32_t offset,
+ uint32_t value)
+{
+ const int gen = intel_gen(intel_get_drm_devid(i915));
+ struct drm_i915_gem_exec_object2 obj[2];
+ struct drm_i915_gem_relocation_entry reloc;
+ struct drm_i915_gem_execbuffer2 execbuf;
+ uint32_t batch[16];
+ int i;
+
+ memset(&execbuf, 0, sizeof(execbuf));
+ execbuf.buffers_ptr = to_user_pointer(obj);
+ execbuf.buffer_count = 2;
+ execbuf.flags = engine;
+ if (gen < 6)
+ execbuf.flags |= I915_EXEC_SECURE;
+
+ memset(obj, 0, sizeof(obj));
+ obj[0].handle = dst;
+ obj[1].handle = gem_create(i915, 4096);
+
+ memset(&reloc, 0, sizeof(reloc));
+ reloc.target_handle = obj[0].handle;
+ reloc.presumed_offset = 0;
+ reloc.offset = sizeof(uint32_t);
+ reloc.delta = offset;
+ reloc.read_domains = I915_GEM_DOMAIN_RENDER;
+ reloc.write_domain = I915_GEM_DOMAIN_RENDER;
+ obj[1].relocs_ptr = to_user_pointer(&reloc);
+ obj[1].relocation_count = 1;
+
+ i = 0;
+ batch[i] = MI_STORE_DWORD_IMM | (gen < 6 ? 1 << 22 : 0);
+ if (gen >= 8) {
+ batch[++i] = offset;
+ batch[++i] = 0;
+ } else if (gen >= 4) {
+ batch[++i] = 0;
+ batch[++i] = offset;
+ reloc.offset += sizeof(uint32_t);
+ } else {
+ batch[i]--;
+ batch[++i] = offset;
+ }
+ batch[++i] = value;
+ batch[++i] = MI_BATCH_BUFFER_END;
+ gem_write(i915, obj[1].handle, 0, batch, sizeof(batch));
+ gem_execbuf(i915, &execbuf);
+ gem_close(i915, obj[1].handle);
+}
+
+static void igt_sparse_execbuf(int i915, uint32_t src)
+{
+ uint32_t sparse;
+
+ igt_require(src);
+
+ sparse = gem_create_sparse(i915, OBJ_SIZE);
+
+ for (uint32_t n = 0; n < 4; n++) {
+ unsigned int engine;
+
+ gem_set_pages(i915,
+ sparse, n, 0,
+ src, n, 0,
+ 1, 1);
+
+ for_each_physical_engine(i915, engine) {
+ uint32_t ex = engine, x;
+
+ if (!gem_can_store_dword(i915, engine))
+ continue;
+
+ x = ~ex;
+ gem_write(i915, src, PAGE_SIZE * n , &x, sizeof(x));
+
+ store_dword(i915, engine, sparse, PAGE_SIZE * n, ex);
+ gem_sync(i915, sparse);
+
+ gem_read(i915, src, PAGE_SIZE * n , &x, sizeof(x));
+
+ igt_assert_eq(x, ex);
+ }
+ }
+
+ gem_close(i915, sparse);
+}
+
+static void igt_sparse_huge(int i915)
+{
+ uint32_t handle =
+ gem_create_sparse(i915, intel_get_total_ram_mb() << 24);
+
+ gem_set_domain(i915, handle, I915_GEM_DOMAIN_CPU, 0);
+ gem_close(i915, handle);
+}
+
+static int create_version(int i915)
+{
+ int val = 0;
+ struct drm_i915_getparam gp = {
+ gp.param = 54, /* CREATE_VERSION */
+ gp.value = &val,
+ };
+
+ ioctl(i915, DRM_IOCTL_I915_GETPARAM, &gp);
+ return val;
+}
+
+igt_main
+{
+ int i915 = -1;
+
+ igt_fixture {
+ i915 = drm_open_driver(DRIVER_INTEL);
+ igt_require_gem(i915);
+ igt_require(create_version(i915) >= 1);
+ }
+
+ igt_subtest("huge")
+ igt_sparse_huge(i915);
+
+ for (enum source_type type = UNSET; type < __LAST_SOURCE__; type++) {
+ const char *name = source_name(type);
+ uint32_t handle = 0;
+
+ igt_fixture {
+ handle = source_create(i915, type, OBJ_SIZE);
+ }
+
+ igt_subtest_f("%s-sparse-read", name)
+ igt_sparse_read(i915, handle);
+
+ igt_subtest_f("%s-sparse-write", name)
+ igt_sparse_write(i915, handle);
+
+ igt_subtest_f("%s-sparse-mmap", name)
+ igt_sparse_mmap(i915, handle);
+
+ igt_subtest_f("%s-sparse-execbuf", name)
+ igt_sparse_execbuf(i915, handle);
+
+ igt_fixture {
+ source_destroy(i915, handle);
+ }
+ }
+
+#if 0
+ igt_subtest("sparse_link_multiple_sources")
+ igt_gem_sparse_link_nsources(i915);
+
+ igt_subtest("sparse_smoke_test_single_source")
+ igt_gem_sparse_smoketest_single_source(i915);
+
+ igt_subtest("sparse_smoke_test_single_destination")
+ igt_gem_sparse_smoketest_single_destination(i915);
+#endif
+
+ igt_fixture {
+ close(i915);
+ }
+}
diff --git a/tests/meson.build b/tests/meson.build
index 718d3eaf..e61a58db 100644
--- a/tests/meson.build
+++ b/tests/meson.build
@@ -156,6 +156,7 @@ i915_progs = [
'gem_gtt_hog',
'gem_gtt_speed',
'gem_largeobject',
+ 'gem_sparseobject',
'gem_linear_blits',
'gem_lut_handle',
'gem_madvise',