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

Venkata Sandeep Dhanalakota venkata.s.dhanalakota at intel.com
Fri Mar 22 14:09:04 UTC 2019


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>
---
 include/drm-uapi/i915_drm.h   |  42 +++
 lib/Makefile.sources          |   2 +
 lib/i915/gem_sparse.c         | 121 +++++++
 lib/i915/gem_sparse.h         |  39 +++
 lib/ioctl_wrappers.c          |  19 +-
 lib/ioctl_wrappers.h          |   1 +
 lib/meson.build               |   1 +
 tests/Makefile.sources        |   3 +
 tests/i915/gem_sparseobject.c | 615 ++++++++++++++++++++++++++++++++++
 tests/meson.build             |   1 +
 10 files changed, 837 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 4ae1c6ff..3f614c85 100644
--- a/include/drm-uapi/i915_drm.h
+++ b/include/drm-uapi/i915_drm.h
@@ -321,6 +321,7 @@ typedef struct _drm_i915_sarea {
 #define DRM_I915_PERF_ADD_CONFIG	0x37
 #define DRM_I915_PERF_REMOVE_CONFIG	0x38
 #define DRM_I915_QUERY			0x39
+#define DRM_I915_GEM_SET_PAGES         0x3a
 /* Must be kept compact -- no holes */
 
 #define DRM_IOCTL_I915_INIT		DRM_IOW( DRM_COMMAND_BASE + DRM_I915_INIT, drm_i915_init_t)
@@ -353,6 +354,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)
@@ -380,6 +382,7 @@ typedef struct _drm_i915_sarea {
 #define DRM_IOCTL_I915_PERF_ADD_CONFIG	DRM_IOW(DRM_COMMAND_BASE + DRM_I915_PERF_ADD_CONFIG, struct drm_i915_perf_oa_config)
 #define DRM_IOCTL_I915_PERF_REMOVE_CONFIG	DRM_IOW(DRM_COMMAND_BASE + DRM_I915_PERF_REMOVE_CONFIG, __u64)
 #define DRM_IOCTL_I915_QUERY			DRM_IOWR(DRM_COMMAND_BASE + DRM_I915_QUERY, struct drm_i915_query)
+#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.
@@ -563,6 +566,12 @@ typedef struct drm_i915_irq_wait {
  */
 #define I915_PARAM_MMAP_GTT_COHERENT	52
 
+/*
+ * 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      53
 /* Must be kept compact -- no holes and well documented */
 
 typedef struct drm_i915_getparam {
@@ -666,6 +675,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;
@@ -1311,6 +1339,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
diff --git a/lib/Makefile.sources b/lib/Makefile.sources
index cf272098..32d51529 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..8f2a8a84
--- /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;
+}
+
+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..3248becf
--- /dev/null
+++ b/lib/i915/gem_sparse.h
@@ -0,0 +1,39 @@
+/*
+ * 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);
+
+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);
+#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 0eb5585d..7baefa2c 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',
 	'igt_color_encoding.c',
diff --git a/tests/Makefile.sources b/tests/Makefile.sources
index 71ccf00a..96233a8b 100644
--- a/tests/Makefile.sources
+++ b/tests/Makefile.sources
@@ -270,6 +270,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..2117198a
--- /dev/null
+++ b/tests/i915/gem_sparseobject.c
@@ -0,0 +1,615 @@
+/*
+ * 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 U64_MAX         ((uint64_t)~0ULL)
+#define U32_MAX         ((uint32_t)~0ULL)
+#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
+#define MAX_SRC_OBJECTS 20
+#define MAX_PATTERN	3
+
+enum source_type {
+	UNSET,
+	SHMEM,
+	USERPTR,
+	__LAST_SOURCE__
+};
+
+void *__userptr[4096];
+
+uint32_t patterns[] = { 0x0,
+			0xdeadbeef,
+		        0xfeedbeef};
+
+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;
+}
+
+static void
+fill_sources(int i915, uint32_t sources[], int size, bool fill)
+{
+	enum source_type type;
+	type = SHMEM;
+	for (int i = 0; i < size; i++, type++) {
+		if ((i % (__LAST_SOURCE__ - 1) == 0))
+			type = SHMEM;
+		sources[i] = source_create(i915, type, OBJ_SIZE);
+		if (fill)
+			gem_write(i915, sources[i], 0,
+				  &patterns[type], sizeof(uint32_t));
+	}
+}
+
+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);
+	i = 0;
+	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 sparse */
+		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++;
+	}
+}
+
+static void
+set_verify_pages(int i915, uint32_t sparse, uint32_t sources[], int size)
+{
+	uint32_t type, x, i;
+
+	for (i = 0; i < size; i++)
+		gem_set_pages(i915, sparse, i, 0,
+			      sources[i], 0, 0, 1, 1);
+	type = 1;
+	for (i = 0; i < size; i++) {
+		gem_read(i915, sparse, PAGE_SIZE * i,
+			 &x, sizeof(x));
+		igt_assert_neq_u32(x, patterns[type]);
+		if (++type == MAX_PATTERN)
+			type = 1;
+	}
+}
+
+/*
+ * Randomly picks the source objects and links with
+ * sparse object.
+ */
+static void
+igt_gem_sparse_link_nsources(int i915)
+{
+	int size;
+	uint32_t sources[MAX_SRC_OBJECTS];
+	uint32_t sparse;
+	int i;
+
+	sparse = gem_create_sparse(i915, U64_MAX);
+	igt_until_timeout(5 * TIMEOUT) {
+		size = rand() % MAX_SRC_OBJECTS;
+		fill_sources(i915, sources, size, true);
+		set_verify_pages(i915, sparse, sources, size);
+		gem_close(i915, sparse);
+		for (i = 0; i < size; i++)
+			source_destroy(i915, sources[i]);
+	}
+}
+
+/* Smoke test with single destination sparse object N:1 smoke */
+static void igt_gem_sparse_smoketest_single_destination(int i915)
+{
+	uint32_t sparse;
+	uint32_t sources[MAX_HANDLES];
+	int size, i;
+
+	sparse = gem_create_sparse(i915, OBJ_SIZE);
+	igt_until_timeout(TIMEOUT) {
+		size = rand() % MAX_SRC_OBJECTS;
+		fill_sources(i915, sources, size, false);
+		gem_link_sources(i915, sparse, sources, size);
+		for (i = 0; i < size; i++)
+			source_destroy(i915, sources[i]);
+		gem_close(i915, sparse);
+	}
+}
+
+/* sparse object smoke test with single source 1:N smoketest */
+static void igt_gem_sparse_smoketest_single_source(int i915)
+{
+	uint32_t sparse, src[1];
+	uint32_t handles[MAX_HANDLES];
+	int count, i;
+
+	src[0] = gem_create(i915, OBJ_SIZE);
+	count = 0;
+	igt_until_timeout(TIMEOUT) {
+		sparse = gem_create_sparse(i915, OBJ_SIZE);
+		handles[count++] = sparse;
+		gem_link_sources(i915, sparse, src, 1);
+	}
+	for (i = 0; i < count; i++)
+		gem_close(i915, handles[i]);
+	source_destroy(i915, src[0]);
+}
+
+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_invalid_sparse_read(int i915)
+{
+	uint32_t sparse;
+	struct drm_i915_gem_pread gem_pread;
+	char buf[256];
+
+	sparse = gem_create_sparse(i915, OBJ_SIZE);
+
+	memset(&gem_pread, 0, sizeof(gem_pread));
+	gem_pread.handle = sparse;
+	gem_pread.offset = OBJ_SIZE;
+	gem_pread.size = 256;
+	gem_pread.data_ptr = to_user_pointer(buf);
+
+	igt_fail_on(!drmIoctl(i915, DRM_IOCTL_I915_GEM_PREAD, &gem_pread));
+}
+
+static void igt_sparse_invalid_link(int i915)
+{
+	uint32_t sparse, src;
+	uint32_t npages = OBJ_SIZE / PAGE_SIZE;
+
+	sparse = gem_create_sparse(i915, OBJ_SIZE);
+	src = gem_create(i915, OBJ_SIZE);
+
+	/* width > src_stride */
+	igt_fail_on(!__gem_set_pages(i915, sparse, 0, 3,
+		      src, 0, 1, 2, 2));
+
+	/* width > dest_stride */
+	igt_fail_on(!__gem_set_pages(i915, sparse, 0, 1,
+		      src, 0, 3, 2, 2));
+
+	/* width cannot be zero */
+	igt_fail_on(!__gem_set_pages(i915, sparse, 0, 1,
+		      src, 0, 1, -1, 0));
+
+	/* overflows */
+	igt_fail_on(!__gem_set_pages(i915, sparse, 0, U32_MAX,
+		      src, 0, 2, 2, 2));
+
+	/* link outside of dst object */
+	igt_fail_on(!__gem_set_pages(i915, sparse, npages - 1, 2,
+		      src, 0, 2, 3, 3));
+
+	/* linking pages which are outside of src object */
+	igt_fail_on(!__gem_set_pages(i915, sparse, 0, 2,
+		      src, npages - 1, 2, 3, 3));
+}
+
+static void igt_sparse_invalid_source(int i915)
+{
+	uint32_t sparse, src;
+
+	sparse = gem_create_sparse(i915, OBJ_SIZE);
+	src = 0x10101001;
+	igt_fail_on(!__gem_set_pages(i915, sparse, 0, 0,
+		      src, 0, 0, 1, 1));
+	gem_close(i915, sparse);
+}
+
+static void igt_sparse_invalid_size(int i915)
+{
+	struct drm_i915_gem_create_ext create = {
+		.size = 0,
+		.flags = I915_GEM_CREATE_SPARSE,
+	};
+	igt_fail_on(!igt_ioctl(i915, DRM_IOCTL_I915_GEM_CREATE_EXT,
+			       &create));
+}
+
+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);
+
+	/* sparse_size = 0 */
+	igt_subtest("sparse_invalid_size")
+		igt_sparse_invalid_size(i915);
+
+	/* src_handle = -1 */
+	igt_subtest("sparse_invalid_source")
+		igt_sparse_invalid_source(i915);
+
+	igt_subtest("sparse_invalid_link")
+		igt_sparse_invalid_link(i915);
+
+	/* reading outside sizeof sparse */
+	igt_subtest("sparse_invalid_pread")
+		igt_sparse_invalid_sparse_read(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);
+		}
+	}
+
+	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);
+
+	igt_fixture {
+		close(i915);
+	}
+}
diff --git a/tests/meson.build b/tests/meson.build
index 9015f809..9bf12118 100644
--- a/tests/meson.build
+++ b/tests/meson.build
@@ -153,6 +153,7 @@ i915_progs = [
 	'gem_gtt_hog',
 	'gem_gtt_speed',
 	'gem_largeobject',
+	'gem_sparseobject',
 	'gem_linear_blits',
 	'gem_lut_handle',
 	'gem_madvise',
-- 
2.17.1



More information about the igt-dev mailing list