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

Venkata Sandeep Dhanalakota venkata.s.dhanalakota at intel.com
Wed Mar 20 20:01:34 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>
---
 include/drm-uapi/i915_drm.h   |   35 ++
 lib/Makefile.sources          |    2 +
 lib/i915/gem_sparse.c         |  123 ++++
 lib/i915/gem_sparse.h         |   35 ++
 lib/meson.build               |    1 +
 tests/Makefile.sources        |    3 +
 tests/i915/gem_sparseobject.c | 1013 +++++++++++++++++++++++++++++++++
 tests/meson.build             |    1 +
 8 files changed, 1213 insertions(+)
 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..d860dae2 100644
--- a/include/drm-uapi/i915_drm.h
+++ b/include/drm-uapi/i915_drm.h
@@ -239,6 +239,20 @@ typedef struct _drm_i915_sarea {
 
 } drm_i915_sarea_t;
 
+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;
+};
+
 /* due to userspace building against these headers we need some compat here */
 #define planeA_x pipeA_x
 #define planeA_y pipeA_y
@@ -321,6 +335,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)
@@ -380,6 +395,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_IOWR(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.
@@ -666,6 +682,25 @@ struct drm_i915_gem_create {
 	__u32 pad;
 };
 
+struct drm_i915_gem_create_v2 {
+	/**
+	 * 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;
+
+#define I915_CREATE_SPARSE_OBJECT (1 << 1)
+	__u32 flags;
+};
+
 struct drm_i915_gem_pread {
 	/** Handle for the object being read. */
 	__u32 handle;
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..4f091b6d
--- /dev/null
+++ b/lib/i915/gem_sparse.c
@@ -0,0 +1,123 @@
+/*
+ * 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"
+
+#define LOCAL_IOCTL_I915_GEM_CREATE       DRM_IOWR(DRM_COMMAND_BASE + DRM_I915_GEM_CREATE, struct drm_i915_gem_create_v2)
+
+/**
+ * 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_v2 create;
+	int err;
+
+	memset(&create, 0, sizeof(create));
+	create.handle = 0;
+	create.size = size;
+	create.flags = I915_CREATE_SPARSE_OBJECT;
+
+	err = 0;
+	if(igt_ioctl(fd, LOCAL_IOCTL_I915_GEM_CREATE, &create)==0)
+		*out = create.handle;
+	else
+		err = -errno;
+	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/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..8fc00249
--- /dev/null
+++ b/tests/i915/gem_sparseobject.c
@@ -0,0 +1,1013 @@
+/*
+ * 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 CLEAR(s) memset(&s, 0, sizeof(s))
+
+#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 gem_object_type {
+	GEM_OBJ_INTERNAL,
+	GEM_OBJ_USRPTR,
+	GEM_OBJ_STOLEN,
+	GEM_OBJ_SPARSE,
+	GEM_OBJ_LOCAL
+};
+
+uint64_t usrptr[MAX_USRPTR];
+static int usrcount;
+
+static uint32_t batch_create(int fd)
+{
+	const uint32_t bbe = MI_BATCH_BUFFER_END;
+	uint32_t handle;
+
+	handle = gem_create(fd, 4096);
+	gem_write(fd, handle, 0, &bbe, sizeof(bbe));
+
+	return handle;
+}
+
+static uint32_t sparse_batch_create(int fd, uint32_t src_handle)
+{
+	uint32_t sparse_handle;
+
+	sparse_handle = gem_create_sparse(fd, 4096);
+	gem_set_pages(fd, sparse_handle, 0, 0,
+				 src_handle, 0, 0, 1, 1);
+	return sparse_handle;
+}
+
+static void batch_fini(int fd, uint32_t handle)
+{
+	gem_sync(fd, handle); /* catch any GPU hang */
+	gem_close(fd, handle);
+}
+
+static void noop(int fd, uint32_t handle, unsigned ring)
+{
+	struct drm_i915_gem_execbuffer2 execbuf;
+	struct drm_i915_gem_exec_object2 exec;
+	const uint32_t bbe = MI_BATCH_BUFFER_END;
+
+	gem_require_ring(fd, ring);
+
+	memset(&exec, 0, sizeof(exec));
+
+	gem_write(fd, handle, 0, &bbe, sizeof(bbe));
+	igt_until_timeout(5) {
+		memset(&exec, 0, sizeof(exec));
+		exec.handle = sparse_batch_create(fd, handle);
+
+		memset(&execbuf, 0, sizeof(execbuf));
+		execbuf.buffers_ptr = to_user_pointer(&exec);
+		execbuf.buffer_count = 1;
+		execbuf.flags = ring;
+		gem_execbuf(fd, &execbuf);
+
+		batch_fini(fd, exec.handle);
+	}
+}
+
+static void gtt(int fd, uint32_t src_handle, unsigned ring)
+{
+	struct drm_i915_gem_execbuffer2 *execbuf;
+	struct drm_i915_gem_exec_object2 *exec;
+	uint32_t sparse_handle;
+
+	gem_require_ring(fd, ring);
+
+	sparse_handle = gem_create_sparse(fd, 4096);
+	gem_set_pages(fd, sparse_handle, 0, 0,
+		      src_handle, 0, 0, 1, 1);
+	gem_set_domain(fd, sparse_handle, I915_GEM_DOMAIN_GTT,
+		       I915_GEM_DOMAIN_GTT);
+	/*mmap the sparse object */
+	execbuf = gem_mmap__gtt(fd, sparse_handle, 4096, PROT_WRITE);
+	exec = (struct drm_i915_gem_exec_object2 *)(execbuf + 1);
+	gem_close(fd, sparse_handle);
+
+	exec->handle = batch_create(fd);
+
+	execbuf->buffers_ptr = to_user_pointer(exec);
+	execbuf->buffer_count = 1;
+	execbuf->flags = ring;
+
+	gem_execbuf(fd, execbuf);
+
+	batch_fini(fd, exec->handle);
+	munmap(execbuf, 4096);
+}
+
+/**
+ * mmaps on bo and returns a pointer to user
+ */
+static
+uint32_t gem_create_local(int fd, size_t size) {
+	/**
+	 * unitl local memory support is added
+	 * using stolen memory
+	 */
+	return gem_create_stolen(fd, size);
+}
+
+static
+uint32_t gem_create_usrptr(int fd, size_t size) {
+	uint32_t handle;
+	void *ptr;
+	static uint32_t userptr_flags = LOCAL_I915_USERPTR_UNSYNCHRONIZED;
+
+	igt_assert(posix_memalign(&ptr, PAGE_SIZE, size) == 0);
+	gem_userptr(fd, ptr, PAGE_SIZE, 0, userptr_flags, &handle);
+	usrptr[usrcount++] = (uint64_t)ptr;
+	return handle;
+}
+
+static void *
+mmap_bo(int fd, uint32_t handle, enum mmap_type type)
+{
+	void *ptr;
+	switch (type) {
+	case GEM_MMAP_CPU:
+		ptr = gem_mmap__cpu(fd, handle, 0, PAGE_SIZE,
+				    PROT_READ | PROT_WRITE);
+		break;
+	case GEM_MMAP_WC:
+		ptr = gem_mmap__wc(fd, handle, 0, PAGE_SIZE,
+				   PROT_READ | PROT_WRITE);
+		break;
+	case GEM_MMAP_GTT:
+		ptr = gem_mmap__gtt(fd, handle, PAGE_SIZE,
+				    PROT_READ | PROT_WRITE);
+		break;
+	default:
+		return NULL;
+	}
+
+	return ptr;
+}
+
+static void
+fill_sources(int fd, uint32_t sources[], int size)
+{
+	int i;
+	for (i = 0; i < size; i++) {
+		switch(i) {
+		case GEM_OBJ_LOCAL:
+			sources[i] = gem_create_local(fd, OBJ_SIZE);
+			break;
+		case GEM_OBJ_INTERNAL:
+			sources[i] = gem_create(fd, OBJ_SIZE);
+				break;
+		case GEM_OBJ_USRPTR:
+			sources[i] = gem_create_usrptr(fd, OBJ_SIZE);
+				break;
+		case GEM_OBJ_STOLEN:
+			sources[i] = gem_create_stolen(fd, OBJ_SIZE);
+				break;
+		}
+	}
+}
+
+static void
+gem_link_sources(int fd, 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(fd, 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(fd, 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(fd, 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(void)
+{
+	int size;
+	uint32_t sources[GEM_OBJ_SPARSE];
+	uint32_t sparse_handle;
+	int i;
+	int fd = drm_open_driver(DRIVER_INTEL);
+
+	igt_until_timeout(5 * TIMEOUT) {
+		CLEAR(sources);
+		size = rand() % GEM_OBJ_SPARSE;
+		fill_sources(fd, sources, size);
+		sparse_handle = gem_create_sparse(fd, LARGE_OBJECT_SIZE);
+		gem_link_sources(fd, sparse_handle, sources, size);
+		gem_close(fd, sparse_handle);
+		for (i = 0; i < usrcount; i++)
+			free((void *)usrptr[i]);
+		usrcount = 0;
+		CLEAR(usrptr);
+		for (i = 0; i < size; i++)
+			gem_close(fd, sources[i]);
+	}
+	close(fd);
+}
+
+static void
+igt_gem_sparse_link_stolen(void)
+{
+	int fd = drm_open_driver(DRIVER_INTEL);
+	uint32_t stolen_handle, sparse_handle;
+	uint32_t sources[1];
+
+	sparse_handle = gem_create_sparse(fd, OBJ_SIZE);
+	stolen_handle = gem_create_stolen(fd, OBJ_SIZE);
+	sources[0] = stolen_handle;
+	gem_link_sources(fd, sparse_handle, sources, 1);
+	gem_close(fd, sparse_handle);
+	gem_close(fd, stolen_handle);
+	close(fd);
+}
+
+static void
+igt_gem_sparse_link_usrptr(void)
+{
+	int fd = drm_open_driver(DRIVER_INTEL);
+	uint32_t usrptr_handle, sparse_handle;
+	static uint32_t userptr_flags = LOCAL_I915_USERPTR_UNSYNCHRONIZED;
+	void *ptr;
+	uint32_t sources[1];
+
+	igt_assert(posix_memalign(&ptr, OBJ_SIZE, PAGE_SIZE) == 0);
+	sparse_handle = gem_create_sparse(fd, OBJ_SIZE);
+	gem_userptr(fd, ptr, PAGE_SIZE, 0, userptr_flags, &usrptr_handle);
+	sources[0] = usrptr_handle;
+	gem_link_sources(fd, sparse_handle, sources, 1);
+	gem_close(fd, sparse_handle);
+	gem_close(fd, usrptr_handle);
+	free(ptr);
+	close(fd);
+}
+
+/* Smoke test with single destination sparse object */
+static void igt_gem_sparse_smoketest_single_destination(void)
+{
+	uint32_t sparse_handle, src_handle;
+	int fd = drm_open_driver(DRIVER_INTEL);
+	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(fd, 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(fd, OBJ_SIZE);
+		handles[count++] = src_handle;
+		data = expected;
+		/* Write a pattern into src object */
+		while (ht <= height) {
+			gem_write(fd, 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(fd, 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(fd, 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(fd, handles[i]);
+	gem_close(fd, sparse_handle);
+	close(fd);
+}
+
+/* sparse object smoke test with single source */
+static void igt_gem_sparse_smoketest_single_source(void)
+{
+	uint32_t sparse_handle, src_handle;
+	int fd = drm_open_driver(DRIVER_INTEL);
+	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(fd, OBJ_SIZE);
+	src_handle = gem_create(fd, 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(fd, OBJ_SIZE);
+		handles[count++] = sparse_handle;
+		data = expected;
+		while (ht <= height) {
+			gem_write(fd, src_handle,
+				  src_offset + (ht * src_stride * PAGE_SIZE),
+				  data, width * PAGE_SIZE);
+			data += (width * PAGE_SIZE);
+			ht++;
+		}
+		gem_set_pages(fd, sparse_handle, dst_offset, dst_stride,
+			      src_handle, src_offset, src_stride, width, height);
+		ht = 0;
+		data = buf;
+		while (ht <= height) {
+			gem_read(fd, 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(fd, handles[i]);
+	gem_close(fd, src_handle);
+	close(fd);
+}
+
+static void
+igt_gem_sparse_usrptr_execbuf(void)
+{
+	uint32_t src_handle;
+	int fd = drm_open_driver(DRIVER_INTEL);
+	const struct intel_execution_engine *e;
+
+	src_handle = gem_create_usrptr(fd, OBJ_SIZE);
+
+	igt_fork_hang_detector(fd);
+
+	for (e = intel_execution_engines; e->name; e++) {
+		igt_subtest_f("basic-%s", e->name)
+			noop(fd, src_handle, e->exec_id | e->flags);
+		igt_subtest_f("gtt-%s", e->name)
+			gtt(fd, src_handle, e->exec_id | e->flags);
+	}
+
+	igt_stop_hang_detector();
+	gem_close(fd, src_handle);
+	close(fd);
+}
+
+/*
+ * Basic test to verify sparse object read and write
+ * which is linked gem object created from usrptr
+ */
+static void igt_gem_sparse_usrptr_readwrite(void)
+{
+	uint32_t sparse_handle, src_handle;
+	int fd = drm_open_driver(DRIVER_INTEL);
+	char expected[OBJ_SIZE];
+	char buf[OBJ_SIZE];
+
+	sparse_handle = gem_create_sparse(fd, OBJ_SIZE);
+	src_handle = gem_create_usrptr(fd, OBJ_SIZE);
+	memset(expected, 0xfeedbeef, OBJ_SIZE);
+
+	gem_write(fd, src_handle, 0, expected, PAGE_SIZE);
+	gem_set_pages(fd, sparse_handle, 0, 0,
+				 src_handle, 0, 0, 1, 1);
+
+	gem_read(fd, sparse_handle, 0, buf, PAGE_SIZE);
+	igt_assert(memcmp(buf, expected, PAGE_SIZE) == 0);
+
+	gem_close(fd, sparse_handle);
+	gem_close(fd, src_handle);
+	free((void *)usrptr[0]);
+	usrcount = 0;
+	close(fd);
+}
+
+/*
+ * Creating a sparse object mapping the pages
+ * from gem object created with usrptr and pread.
+ */
+static void igt_gem_sparse_usrptr_pread(void)
+{
+	int sparse_handle, src_handle;
+	char buf[PAGE_SIZE];
+	char expected[PAGE_SIZE];
+	int fd = drm_open_driver(DRIVER_INTEL);
+
+	sparse_handle = gem_create_sparse(fd, OBJ_SIZE);
+	src_handle = gem_create_usrptr(fd, OBJ_SIZE);
+	gem_set_pages(fd, sparse_handle, 0, 0,
+				 src_handle, 0, 0, 1, 1);
+	CLEAR(expected);
+
+	gem_read(fd, sparse_handle, 0, buf, PAGE_SIZE);
+
+	gem_close(fd, sparse_handle);
+	gem_close(fd, src_handle);
+	free((void *)usrptr[0]);
+	usrcount = 0;
+	close(fd);
+}
+
+/*
+ * Creating a sparse object which backed by
+ * gem object created from usrptr and test pwrite.
+ */
+static void igt_gem_sparse_usrptr_pwrite(void)
+{
+	int sparse_handle, src_handle;
+	char buf[PAGE_SIZE];
+	int fd = drm_open_driver(DRIVER_INTEL);
+
+	sparse_handle = gem_create_sparse(fd, OBJ_SIZE);
+	src_handle = gem_create_usrptr(fd, OBJ_SIZE);
+	gem_set_pages(fd, sparse_handle, 0, 0,
+				 src_handle, 0, 0, 1, 1);
+	memset(buf, 0xfeedbeef, PAGE_SIZE);
+
+	gem_write(fd, sparse_handle, 0, buf, PAGE_SIZE);
+
+	gem_close(fd, sparse_handle);
+	gem_close(fd, src_handle);
+	free((void *)usrptr[0]);
+	usrcount = 0;
+	close(fd);
+}
+
+/*
+ * igt case to test to mmap an sparse object
+ * with gem object created from usrptr
+ */
+static void igt_gem_sparse_usrptr_mmap(void)
+{
+	int sparse_handle, src_handle;
+	void *data;
+	int type;
+	int fd = drm_open_driver(DRIVER_INTEL);
+
+	sparse_handle = gem_create_sparse(fd, OBJ_SIZE);
+	src_handle = gem_create_usrptr(fd, OBJ_SIZE);
+	gem_set_pages(fd, sparse_handle, 0, 0,
+				 src_handle, 0, 0, 1, 1);
+	for (type = 0; type < GEM_MMAP_NONE; type++) {
+		data = mmap_bo(fd, sparse_handle, type);
+		/*
+		 * valid ptr should be returned for mmapping
+		 * sparse object
+		 */
+		igt_assert(data);
+		gem_munmap(data, PAGE_SIZE);
+	}
+
+	gem_close(fd, sparse_handle);
+	gem_close(fd, src_handle);
+	free((void *)usrptr[0]);
+	usrcount = 0;
+	close(fd);
+}
+
+static void
+igt_gem_sparse_stolen_execbuf(void)
+{
+	uint32_t src_handle;
+	int fd = drm_open_driver(DRIVER_INTEL);
+	const struct intel_execution_engine *e;
+
+	src_handle = gem_create_stolen(fd, OBJ_SIZE);
+
+	igt_fork_hang_detector(fd);
+
+	for (e = intel_execution_engines; e->name; e++) {
+		igt_subtest_f("basic-%s", e->name)
+			noop(fd, src_handle, e->exec_id | e->flags);
+		igt_subtest_f("gtt-%s", e->name)
+			gtt(fd, src_handle, e->exec_id | e->flags);
+	}
+
+	igt_stop_hang_detector();
+	gem_close(fd, src_handle);
+	close(fd);
+}
+
+/*
+ * Basic test to verify sparse object read and write
+ * which is linked stolen memory gem object.
+ */
+static void igt_gem_sparse_stolen_readwrite(void)
+{
+	uint32_t sparse_handle, src_handle;
+	int fd = drm_open_driver(DRIVER_INTEL);
+	char expected[OBJ_SIZE];
+	char buf[OBJ_SIZE];
+
+	sparse_handle = gem_create_sparse(fd, OBJ_SIZE);
+	src_handle = gem_create_stolen(fd, OBJ_SIZE);
+	memset(expected, 0xfeedbeef, OBJ_SIZE);
+
+	gem_write(fd, src_handle, 0, expected, PAGE_SIZE);
+	gem_set_pages(fd, sparse_handle, 0, 0,
+				 src_handle, 0, 0, 1, 1);
+
+	gem_read(fd, sparse_handle, 0, buf, PAGE_SIZE);
+	igt_assert(memcmp(buf, expected, PAGE_SIZE) == 0);
+
+	gem_close(fd, sparse_handle);
+	gem_close(fd, src_handle);
+	close(fd);
+}
+
+/*
+ * Creating a sparse object mapping the pages
+ * from stolen mem gem object and pread should read a zero.
+ */
+static void igt_gem_sparse_stolen_pread(void)
+{
+	int sparse_handle, src_handle;
+	char buf[PAGE_SIZE];
+	char expected[PAGE_SIZE];
+	int fd = drm_open_driver(DRIVER_INTEL);
+
+	sparse_handle = gem_create_sparse(fd, OBJ_SIZE);
+	src_handle = gem_create_stolen(fd, OBJ_SIZE);
+	gem_set_pages(fd, sparse_handle, 0, 0,
+				 src_handle, 0, 0, 1, 1);
+	CLEAR(expected);
+
+	gem_read(fd, sparse_handle, 0, buf, PAGE_SIZE);
+
+	gem_close(fd, sparse_handle);
+	gem_close(fd, src_handle);
+	close(fd);
+}
+
+/*
+ * Creating a sparse object which backed by
+ * stolen memory gem object and test pwrite.
+ */
+static void igt_gem_sparse_stolen_pwrite(void)
+{
+	int sparse_handle, src_handle;
+	char buf[PAGE_SIZE];
+	int fd = drm_open_driver(DRIVER_INTEL);
+
+	sparse_handle = gem_create_sparse(fd, OBJ_SIZE);
+	src_handle = gem_create_stolen(fd, OBJ_SIZE);
+	gem_set_pages(fd, sparse_handle, 0, 0,
+				 src_handle, 0, 0, 1, 1);
+	memset(buf, 0xfeedbeef, PAGE_SIZE);
+
+	gem_write(fd, sparse_handle, 0, buf, PAGE_SIZE);
+
+	gem_close(fd, sparse_handle);
+	gem_close(fd, src_handle);
+	close(fd);
+}
+
+/*
+ * igt case to test if we can mmap an sparse object
+ * linked to stolen memory gem object
+ */
+static void igt_gem_sparse_stolen_mmap(void)
+{
+	int sparse_handle, src_handle;
+	void *data;
+	int type;
+	int fd = drm_open_driver(DRIVER_INTEL);
+
+	sparse_handle = gem_create_sparse(fd, OBJ_SIZE);
+	src_handle = gem_create_stolen(fd, OBJ_SIZE);
+	gem_set_pages(fd, sparse_handle, 0, 0,
+				 src_handle, 0, 0, 1, 1);
+	for (type = 0; type < GEM_MMAP_NONE; type++) {
+		data = mmap_bo(fd, sparse_handle, type);
+		/*
+		 * valid ptr should be returned for mmapping
+		 * sparse object
+		 */
+		igt_assert(!data);
+		gem_munmap(data, PAGE_SIZE);
+	}
+
+	gem_close(fd, sparse_handle);
+	gem_close(fd, src_handle);
+	close(fd);
+}
+
+static void
+igt_gem_sparse_internal_execbuf(void)
+{
+	uint32_t src_handle;
+	int fd = drm_open_driver(DRIVER_INTEL);
+	const struct intel_execution_engine *e;
+
+	src_handle = gem_create(fd, OBJ_SIZE);
+
+	igt_fork_hang_detector(fd);
+
+	for (e = intel_execution_engines; e->name; e++) {
+		igt_subtest_f("basic-%s", e->name)
+			noop(fd, src_handle, e->exec_id | e->flags);
+		igt_subtest_f("gtt-%s", e->name)
+			gtt(fd, src_handle, e->exec_id | e->flags);
+	}
+
+	igt_stop_hang_detector();
+	gem_close(fd, src_handle);
+	close(fd);
+}
+
+/*
+ * Basic test to verify sparse object read and write
+ * which is linked internal gem object.
+ */
+static void igt_gem_sparse_internal_readwrite(void)
+{
+	uint32_t sparse_handle, src_handle;
+	int fd = drm_open_driver(DRIVER_INTEL);
+	char expected[OBJ_SIZE];
+	char buf[OBJ_SIZE];
+
+	sparse_handle = gem_create_sparse(fd, OBJ_SIZE);
+	src_handle = gem_create(fd, OBJ_SIZE);
+	memset(expected, 0xfeedbeef, OBJ_SIZE);
+
+	gem_write(fd, src_handle, 0, expected, PAGE_SIZE);
+	gem_set_pages(fd, sparse_handle, 0, 0,
+				 src_handle, 0, 0, 1, 1);
+
+	gem_read(fd, sparse_handle, 0, buf, PAGE_SIZE);
+	igt_assert(memcmp(buf, expected, PAGE_SIZE) == 0);
+
+	gem_close(fd, sparse_handle);
+	gem_close(fd, src_handle);
+	close(fd);
+}
+
+/*
+ * Creating a sparse object mapping the pages
+ * from gem object and pread.
+ */
+static void igt_gem_sparse_internal_pread(void)
+{
+	int sparse_handle, src_handle;
+	char buf[PAGE_SIZE];
+	char expected[PAGE_SIZE];
+	int fd = drm_open_driver(DRIVER_INTEL);
+
+	sparse_handle = gem_create_sparse(fd, OBJ_SIZE);
+	src_handle = gem_create(fd, OBJ_SIZE);
+	gem_set_pages(fd, sparse_handle, 0, 0,
+				 src_handle, 0, 0, 1, 1);
+	CLEAR(expected);
+
+	gem_read(fd, sparse_handle, 1, buf, PAGE_SIZE);
+
+	gem_close(fd, sparse_handle);
+	gem_close(fd, src_handle);
+	close(fd);
+}
+
+/*
+ * Creating a sparse object which backed by
+ * internal memory gem object and test pwrite.
+ */
+static void igt_gem_sparse_internal_pwrite(void)
+{
+	int sparse_handle, src_handle;
+	char buf[PAGE_SIZE];
+	int fd = drm_open_driver(DRIVER_INTEL);
+
+	sparse_handle = gem_create_sparse(fd, OBJ_SIZE);
+	src_handle = gem_create(fd, OBJ_SIZE);
+	gem_set_pages(fd, sparse_handle, 0, 0,
+				 src_handle, 0, 0, 1, 1);
+	memset(buf, 0xfeedbeef, PAGE_SIZE);
+
+	gem_write(fd, sparse_handle, 0, buf, PAGE_SIZE);
+
+	gem_close(fd, sparse_handle);
+	gem_close(fd, src_handle);
+	close(fd);
+}
+
+/*
+ * igt case to test if we can mmap sparse object
+ * backed by gem object internal memory
+ */
+static void igt_gem_sparse_internal_mmap(void)
+{
+	int sparse_handle, src_handle;
+	void *data;
+	int type;
+	int fd = drm_open_driver(DRIVER_INTEL);
+
+	sparse_handle = gem_create_sparse(fd, OBJ_SIZE);
+	src_handle = gem_create(fd, OBJ_SIZE);
+	gem_set_pages(fd, sparse_handle, 0, 0,
+				 src_handle, 0, 0, 1, 1);
+	for (type = 0; type < GEM_MMAP_NONE; type++) {
+		data = mmap_bo(fd, sparse_handle, type);
+		/*
+		 * valid ptr should be returned for mmapping
+		 * sparse object
+		 */
+		igt_assert(!data);
+		gem_munmap(data, PAGE_SIZE);
+	}
+
+	gem_close(fd, sparse_handle);
+	gem_close(fd, src_handle);
+	close(fd);
+}
+
+/*
+ * Creating a sparse object without mapping the pages
+ * from any source object pread should read a zero.
+ */
+static void igt_gem_sparse_pread(void)
+{
+	int handle;
+	char buf[PAGE_SIZE];
+	char expected[PAGE_SIZE];
+	int fd = drm_open_driver(DRIVER_INTEL);
+
+	handle = gem_create_sparse(fd, OBJ_SIZE);
+	CLEAR(expected);
+
+	gem_read(fd, handle, 0, buf, PAGE_SIZE);
+
+	gem_close(fd, handle);
+	close(fd);
+}
+
+/*
+ * Creating a sparse object without mapping the pages
+ * from any source object pwrite should be backed by
+ * zero page.
+ */
+static void igt_gem_sparse_pwrite(void)
+{
+	int handle;
+	char buf[PAGE_SIZE];
+	int fd = drm_open_driver(DRIVER_INTEL);
+
+	handle = gem_create_sparse(fd, OBJ_SIZE);
+	memset(buf, 0xfeedbeef, PAGE_SIZE);
+
+	/*
+	 * gem_write should fail if it succeeds test should fail,
+	 * since there are no backing pages linked from any src object.
+	 */
+	gem_write(fd, handle, 0, buf, PAGE_SIZE);
+
+	gem_close(fd, handle);
+	close(fd);
+}
+
+/* igt case to test if we can mmap an empty sparse object */
+static void igt_gem_invalid_mmap(void)
+{
+	int handle;
+	void *data;
+	int type;
+	int fd = drm_open_driver(DRIVER_INTEL);
+
+	handle = gem_create_sparse(fd, OBJ_SIZE);
+
+	/**
+	 * These should fail. Hence cannot mmap.
+	 * Test fails if we are able successfully mmap.
+	 */
+	for (type = 0; type < GEM_MMAP_NONE; type++) {
+		data = mmap_bo(fd, handle, type);
+		/*
+		 * NULL should be returned for mmapping sparse object
+		 * without any pages linked from source object
+		 */
+		igt_assert(data);
+		gem_munmap(data, PAGE_SIZE);
+	}
+
+	gem_close(fd, handle);
+	close(fd);
+}
+/* Basic test to ensure we can create a linked object */
+static void igt_gem_sparse_linked(void)
+{
+	int fd = drm_open_driver(DRIVER_INTEL);
+	uint32_t sparse_handle, handle;
+
+	sparse_handle = gem_create_sparse(fd, OBJ_SIZE);
+	handle = gem_create(fd, OBJ_SIZE);
+	gem_set_pages(fd, sparse_handle, 0, 0,
+				 handle, 0, 0, 1, 1);
+
+	gem_close(fd, sparse_handle);
+	gem_close(fd, handle);
+	close(fd);
+}
+
+/* Basic test to ensure we can create an object */
+static void igt_gem_sparse_empty(void)
+{
+	int fd = drm_open_driver(DRIVER_INTEL);
+	uint32_t handle;
+
+	handle = gem_create_sparse(fd, LARGE_OBJECT_SIZE);
+	gem_close(fd, handle);
+	close(fd);
+}
+
+igt_simple_main
+{
+	igt_subtest("create_sparse_object")
+		igt_gem_sparse_empty();
+
+	igt_subtest("pwrite-unset-sparse-object")
+		igt_gem_sparse_pwrite();
+
+	igt_subtest("pread-unset-sparse-object")
+		igt_gem_sparse_pread();
+
+	igt_subtest("mmap-unset-sparse-object")
+		igt_gem_invalid_mmap();
+
+	igt_subtest("link-internel-object-sparse-object")
+		igt_gem_sparse_linked();
+
+	igt_subtest("pread-internal-sparse-object")
+		igt_gem_sparse_internal_pread();
+
+	igt_subtest("pwrite-internal-sparse-object")
+		igt_gem_sparse_internal_pwrite();
+
+	igt_subtest("readwrite-internal-sparse-object")
+		igt_gem_sparse_internal_readwrite();
+
+	igt_subtest("mmap-internal-sparse-object")
+		igt_gem_sparse_internal_mmap();
+
+	igt_subtest("execbuf-internal-linked-sparse-object")
+		igt_gem_sparse_internal_execbuf();
+
+	igt_subtest("link-usrptr-object-sparse-object")
+		igt_gem_sparse_link_usrptr();
+
+	igt_subtest("pread-usrptr-object-sparse-object")
+		igt_gem_sparse_usrptr_pread();
+
+	igt_subtest("pwrite-usrptr-object-sparse-object")
+		igt_gem_sparse_usrptr_pwrite();
+
+	igt_subtest("readwrite-usrptr-object-sparse-object")
+		igt_gem_sparse_usrptr_readwrite();
+
+	igt_subtest("mmap-usrptr-object-sparse-object")
+		igt_gem_sparse_usrptr_mmap();
+
+	igt_subtest("execbuf-usrptr-linked-sparse-object")
+		igt_gem_sparse_usrptr_execbuf();
+
+	igt_subtest("link-stolen-object-sparse-object")
+		igt_gem_sparse_link_stolen();
+
+	igt_subtest("pread-stolen-object-sparse-object")
+		igt_gem_sparse_stolen_pread();
+
+	igt_subtest("pwrite-stolen-object-sparse-object")
+		igt_gem_sparse_stolen_pwrite();
+
+	igt_subtest("readwrite-stolen-object-sparse-object")
+		igt_gem_sparse_stolen_readwrite();
+
+	igt_subtest("mmap-stolen-object-sparse-object")
+		igt_gem_sparse_stolen_mmap();
+
+	igt_subtest("execbuf-stolen-object-sparse-object")
+		igt_gem_sparse_stolen_execbuf();
+
+	igt_subtest("sparse_link_multiple_sources")
+		igt_gem_sparse_link_nsources();
+
+	igt_subtest("sparse_smoke_test_single_source")
+		igt_gem_sparse_smoketest_single_source();
+
+	igt_subtest("sparse_smoke_test_single_destination")
+		igt_gem_sparse_smoketest_single_destination();
+}
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