[Intel-gfx] [PATCH i-g-t] i915/gem_sparseobject: Add sparse object IGT tests

Chris Wilson chris at chris-wilson.co.uk
Thu Mar 21 00:13:16 UTC 2019


From: "Venkata Sandeep, Dhanalakota" <venkata.s.dhanalakota at intel.com>

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 at intel.com>
---
I spent a bit of time gutting the test and run them against the actual
ioctl. Please use this as a base to rebuild the smoketests, 1:N, M:1
linkage, and consider some negative testing. Feel free to be creative!
-Chris
---
 include/drm-uapi/i915_drm.h   |  71 ++++
 lib/Makefile.sources          |   2 +
 lib/i915/gem_sparse.c         | 121 +++++++
 lib/i915/gem_sparse.h         |  35 ++
 lib/ioctl_wrappers.c          |  19 +-
 lib/ioctl_wrappers.h          |   1 +
 lib/meson.build               |   1 +
 tests/Makefile.sources        |   3 +
 tests/i915/gem_sparseobject.c | 608 ++++++++++++++++++++++++++++++++++
 tests/meson.build             |   1 +
 10 files changed, 855 insertions(+), 7 deletions(-)
 create mode 100644 lib/i915/gem_sparse.c
 create mode 100644 lib/i915/gem_sparse.h
 create mode 100644 tests/i915/gem_sparseobject.c

diff --git a/include/drm-uapi/i915_drm.h b/include/drm-uapi/i915_drm.h
index cb6d66178..3f32b3722 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 a7074209a..aab8003e8 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 000000000..0b0dc7d12
--- /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 000000000..71b35fb60
--- /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 a66eb4bc0..aa432fa9f 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 ad93daffc..1361215b1 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 f95922330..45a6f424b 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 8981a8ae7..cdd81cd30 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 000000000..a063abb8d
--- /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 718d3eaff..e61a58dbb 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',
-- 
2.20.1



More information about the Intel-gfx mailing list