[Intel-gfx] [PATCH i-g-t] tests/gem_buffered_svm_test: New tests for buffered SVM feature

Vinay Belgaumkar vinay.belgaumkar at intel.com
Tue Nov 10 07:37:54 PST 2015


v1: These tests exercise the userptr ioctl to create shared buffers
between CPU and GPU. They contain error and normal usage scenarios.
They also contain a couple of stress tests which copy buffers between
CPU and GPU. These tests rely on the softpin patch in order to pin buffers
to a certain VA.

Caveat: These tests were designed to run on 64-bit system. Future work
includes adding logic to ensure these tests can run on 32-bit systems with
PPGTT support. Some tests are currently disabled for 32-bit systems for that
reason.

Testcase: igt/gem_buffered_svm_test
---
 tests/Makefile.sources        |    1 +
 tests/gem_buffered_svm_test.c | 1490 +++++++++++++++++++++++++++++++++++++++++
 2 files changed, 1491 insertions(+)
 create mode 100644 tests/gem_buffered_svm_test.c

diff --git a/tests/Makefile.sources b/tests/Makefile.sources
index 8fb2de8..2ce4216 100644
--- a/tests/Makefile.sources
+++ b/tests/Makefile.sources
@@ -11,6 +11,7 @@ TESTS_progs_M = \
 	drv_hangman \
 	gem_bad_reloc \
 	gem_basic \
+	gem_buffered_svm_test \
 	gem_caching \
 	gem_close_race \
 	gem_concurrent_blit \
diff --git a/tests/gem_buffered_svm_test.c b/tests/gem_buffered_svm_test.c
new file mode 100644
index 0000000..44d342a
--- /dev/null
+++ b/tests/gem_buffered_svm_test.c
@@ -0,0 +1,1490 @@
+/*
+ * Copyright © 2015 Intel Corporation
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a
+ * copy of this software and associated documentation files (the "Software"),
+ * to deal in the Software without restriction, including without limitation
+ * the rights to use, copy, modify, merge, publish, distribute, sublicense,
+ * and/or sell copies of the Software, and to permit persons to whom the
+ * Software is furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice (including the next
+ * paragraph) shall be included in all copies or substantial portions of the
+ * Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL
+ * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+ * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
+ * IN THE SOFTWARE.
+ *
+ * Authors:
+ *    Vinay Belgaumkar <vinay.belgaumkar at intel.com?
+      Thomas Daniel <thomas.daniel at intel.com>
+ *
+ */
+
+#include <unistd.h>
+#include <stdlib.h>
+#include <stdint.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 <sys/time.h>
+#include <malloc.h>
+#include "drm.h"
+#include "ioctl_wrappers.h"
+#include "drmtest.h"
+#include "intel_chipset.h"
+#include "intel_io.h"
+#include "i915_drm.h"
+#include <assert.h>
+#include <sys/wait.h>
+#include <sys/ipc.h>
+#include <sys/shm.h>
+#include "igt_kms.h"
+#include <inttypes.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+
+
+#if (INTPTR_MAX == INT32_MAX)
+	#define IS_32BIT_USER
+#endif
+
+#define OBJECT_SIZE 16384
+#define BO_SIZE 4 * 4096
+#define STORE_BATCH_BUFFER_SIZE 6
+#define STRESS_BATCH_BUFFER_SIZE 5
+#define EXEC_OBJECT_PINNED	(1<<4)
+#define EXEC_OBJECT_SUPPORTS_48B_ADDRESS (1<<3)
+#define SHARED_BUFFER_SIZE 4096
+#define NUM_EXEC_OBJECTS 2
+
+typedef struct drm_i915_gem_userptr i915_gem_userptr;
+static i915_gem_userptr* gem_create_userptr_struct(void* ptr, int size, int read_only);
+
+static void* gem_create_mem_buffer(int size);
+static void gem_invalid_userptr_test(void);
+static int gem_call_userptr_ioctl(int fd, i915_gem_userptr* userptr);
+static void gem_basic_test(bool);
+static void gem_pin_invalid_vma_test(void);
+static void gem_pin_overlap_test(void);
+static void gem_shmem_test(void);
+static void gem_pin_high_address_test(void);
+static void gem_pin_mmap_anonymous_test(void);
+static void gem_pin_mmap_file_test(void);
+
+/* incorrect args to userptr ioctl
+   - Write to Read Only buffer
+*/
+
+static int gem_call_userptr_ioctl(int fd, i915_gem_userptr* userptr)
+{
+	int ret;
+
+	ret = drmIoctl(fd, DRM_IOCTL_I915_GEM_USERPTR, userptr);
+
+	if (ret)
+		ret = errno;
+
+	return ret;
+}
+
+static i915_gem_userptr* gem_create_userptr_struct(void* ptr, int size, int read_only)
+{
+	i915_gem_userptr* userptr;
+
+	userptr = (i915_gem_userptr*)calloc(1, sizeof(i915_gem_userptr));
+
+	igt_assert(userptr != NULL);
+
+	if (userptr)
+	{
+		userptr->user_ptr = (uintptr_t)ptr;
+		userptr->user_size = size;
+		userptr->flags = I915_USERPTR_UNSYNCHRONIZED;
+
+		if (read_only)
+			userptr->flags |= I915_USERPTR_READ_ONLY;
+	}
+
+	return userptr;
+}
+
+
+
+/** Creates a CPU buffer 
+ *  @size - size of buffer
+ *  RETURNS pointer to buffer of @size
+*/
+
+static void* gem_create_mem_buffer(int size)
+{
+	void* addr;
+
+	addr = memalign(4096, size);
+	igt_assert(addr != NULL);
+
+	return addr;
+}
+
+/**
+ * This test will send an invalid userptr struct to the ioctl
+ * Expected result is ioctl call should return FAILURE
+*/
+static void gem_invalid_userptr_test(void)
+{
+	i915_gem_userptr* userptr = NULL;
+	int fd, ret;
+
+	fd = drm_open_driver(DRIVER_INTEL);
+
+	ret = gem_call_userptr_ioctl(fd, userptr);
+
+	/* Expect to fail */
+	igt_assert(ret);
+}
+
+/** setup_exec_obj - populate exec object
+ * @exec - exec object
+ * @handle - handle to gem buffer
+ * flags - any flags
+ * offset - requested VMA
+ */
+static void setup_exec_obj(struct drm_i915_gem_exec_object2 *exec, __u32 handle, __u32 flags, uint32_t* offset)
+{
+	memset(exec, 0, sizeof(struct drm_i915_gem_exec_object2));
+	exec->handle = handle;
+	exec->flags = flags;
+	exec->offset = (uintptr_t)offset;
+}
+
+/** 
+ * gem_store_data_svm - populate batch buffer with MI_STORE_DATA_IMM command
+ * @fd: drm file descriptor
+ * @buf: batch buffer
+ * @buffer_size: size of buffer
+ * @addr: destination Virtual address
+ * @data: data to be store at destination
+ * @end: whether to end batch buffer or not
+ */
+#define MI_COMMAND 0
+#define MI_STORE_DATA_IMM 0x20 << 23
+#define USE_PPGTT 0x0 << 22
+#define DWORD_LENGTH 0x2
+static int gem_store_data_svm(int fd, uint32_t* cmd_buf, uint32_t* vaddr,
+			      uint32_t data, bool end)
+{
+	int i = 0;
+
+	cmd_buf[i++] = MI_COMMAND | MI_STORE_DATA_IMM | USE_PPGTT | DWORD_LENGTH;
+	cmd_buf[i++] = (uintptr_t)vaddr & 0xFFFFFFFC;
+	#ifdef IS_32BIT_USER
+		cmd_buf[i++] = 0;
+	#else // 64 bit
+		cmd_buf[i++] = ((uintptr_t)vaddr >> 32) & 0xFFFF; /* bits 32:47 */
+	#endif
+
+	cmd_buf[i++] = data;
+	if (end) 
+	{
+		cmd_buf[i++] = MI_BATCH_BUFFER_END;
+		cmd_buf[i++] = 0;
+	}
+
+	return (i * sizeof(uint32_t));
+}
+
+/** 
+ * gem_store_data_svm - populate batch buffer with MI_STORE_DATA_IMM command
+ * This one fills up reloc buffer as well 
+ * @fd: drm file descriptor
+ * @buf: batch buffer
+ * @buffer_size: size of buffer
+ * @addr: destination Virtual address
+ * @data: data to be store at destination
+ * @reloc - relocation entry
+ * @end: whether to end batch buffer or not
+ */
+
+static int gem_store_data(int fd, uint32_t* cmd_buf,
+			      uint32_t handle, uint32_t data,
+			      struct drm_i915_gem_relocation_entry *reloc,
+			      bool end)
+{
+	int i = 0;
+
+	cmd_buf[i++] = MI_COMMAND | MI_STORE_DATA_IMM | USE_PPGTT | DWORD_LENGTH;
+	cmd_buf[i++] = 0; /* lower 31 bits of 48 bit address - 0 because reloc is needed */
+	cmd_buf[i++] = 0; /* upper 15 bits of 48 bit address - 0 because reloc is needed */
+	reloc->offset = 1 * sizeof(uint32_t);
+	reloc->delta = 0;
+	reloc->target_handle = handle;
+	reloc->read_domains = I915_GEM_DOMAIN_RENDER;
+	reloc->write_domain = I915_GEM_DOMAIN_RENDER;
+	reloc->presumed_offset = 0;
+	reloc++;
+	cmd_buf[i++] = data;
+	if (end) 
+	{
+		cmd_buf[i++] = MI_BATCH_BUFFER_END;
+		cmd_buf[i++] = 0;
+	}
+
+	return (i * sizeof(uint32_t));
+}
+
+
+/** gem_basic_test - This test will create a shared buffer, and create a command
+ *  for GPU to write data in it
+ *  CPU will read and make sure expected value is obtained
+ *  @valid_shared_buffer - whether test with valid malloc'd buffer or not
+
+ at code
+   if (valid_shared_buffer == true)
+   Malloc a 4K buffer
+   Share buffer with with GPU by using userptr ioctl
+   Create batch buffer to write DATA to first dword of buffer
+   Use virtual address of buffer as destination address in batch buffer
+   Set EXEC_OBJECT_PINNED flag in exec object
+   Set 'offset' in exec object to shared buffer VMA
+   Submit execbuffer
+   Verify value of first DWORD in shared buffer matches DATA
+
+   if (valid_shared_buffer == false)
+   Declare null buffer
+   Call Userptr ioctl with null buffer
+   Run Basic Test
+   Test should fail at submit execbuffer
+ at endcode
+*/
+static void gem_basic_test(bool valid_shared_buffer)
+{
+	i915_gem_userptr* userptr = NULL;
+	int fd, ret;
+	uint32_t* shared_buffer = NULL;
+	struct drm_i915_gem_execbuffer2 execbuf;
+	struct drm_i915_gem_exec_object2 exec_object2[NUM_EXEC_OBJECTS];
+	uint32_t batch_buffer[STORE_BATCH_BUFFER_SIZE];
+	uint32_t batch_buf_handle, shared_buf_handle;
+	int ring, len;
+	const uint32_t data = 0x12345678;
+
+	fd = drm_open_driver(DRIVER_INTEL);
+	batch_buf_handle = gem_create(fd, BO_SIZE);
+
+	/* create cpu buffer, set to all 0xF's */
+	if (valid_shared_buffer)
+	{
+		shared_buffer = gem_create_mem_buffer(BO_SIZE);
+		//*shared_buffer = 0xFFFFFFFF;
+	}
+	
+	/* share with GPU */
+	userptr = gem_create_userptr_struct(shared_buffer, BO_SIZE, 0);
+	ret = gem_call_userptr_ioctl(fd, userptr);
+	igt_assert_eq(ret, 0);
+
+	/* Get handle for shared buffer */
+	shared_buf_handle = userptr->handle;
+	free(userptr);
+
+	/* create command buffer with write command */
+	len = gem_store_data_svm(fd, batch_buffer, shared_buffer, data, true);
+	igt_assert_lte(len, STORE_BATCH_BUFFER_SIZE * 4);
+
+	gem_write(fd, batch_buf_handle, 0, batch_buffer, len);
+
+	/* submit command buffer */
+	setup_exec_obj(&exec_object2[0], shared_buf_handle, EXEC_OBJECT_PINNED, shared_buffer);
+	setup_exec_obj(&exec_object2[1], batch_buf_handle, 0, 0);
+
+	ring = 0;
+	if (HAS_BLT_RING(intel_get_drm_devid(fd)))
+		ring = I915_EXEC_BLT;
+
+	execbuf.buffers_ptr = (uintptr_t)exec_object2;
+	execbuf.buffer_count = NUM_EXEC_OBJECTS;
+	execbuf.batch_start_offset = 0;
+	execbuf.batch_len = len;
+	execbuf.cliprects_ptr = 0;
+	execbuf.num_cliprects = 0;
+	execbuf.DR1 = 0;
+	execbuf.DR4 = 0;
+	execbuf.flags = ring;
+	i915_execbuffer2_set_context_id(execbuf, 0);
+	execbuf.rsvd2 = 0;
+
+	if (valid_shared_buffer)
+	{
+		gem_execbuf(fd, &execbuf);
+	}
+	else
+	{
+		/* Expect execbuf to fail */
+		ret = drmIoctl(fd,
+		       DRM_IOCTL_I915_GEM_EXECBUFFER2,
+		       &execbuf);
+		igt_assert_neq(ret, 0);
+	}
+	gem_sync(fd, batch_buf_handle); 
+	gem_close(fd, batch_buf_handle);
+	close(fd);
+
+	// check on CPU to see if value changes
+	if (valid_shared_buffer)
+	{
+		igt_fail_on_f(shared_buffer[0] != data,
+			      "\nCPU read does not match GPU write, expected: 0x%x, got: 0x%x\n", data, shared_buffer[0]);
+
+		free(shared_buffer);
+	}
+}
+
+/** gem_multiple_process_test - Run basic test simultaneously with multiple processes
+*   This will test pinning same VA separately in each process
+
+    fork();
+    Execute basic test in parent/child processes
+
+**/
+
+static void gem_multiple_process_test(void)
+{
+	int status;
+	pid_t child_pid, wait_pid;
+	child_pid = fork();
+
+	if (child_pid == 0)
+	{
+		gem_basic_test(true);
+		_exit(0);
+	}
+	else 
+	{
+		gem_basic_test(true);
+		wait_pid = wait(&status); 
+		igt_assert(wait_pid != -1);
+	}
+}
+
+
+/** gem_repin_test
+ * This test tries to repin a buffer at a previously pinned vma 
+ * from a different execbuf. 
+
+ at code
+   Malloc a 4K buffer
+   Share buffer with with GPU by using userptr ioctl
+   Create batch buffer to write DATA to first dword of buffer
+   Use virtual address of buffer as destination address in batch buffer
+   Set EXEC_OBJECT_PINNED flag in exec object
+   Set 'offset' in exec object to shared buffer VMA
+   Submit execbuffer
+   Verify value of first DWORD in shared buffer matches DATA
+
+   Create second shared buffer
+   Follow all steps above
+   Execpt, for offset, use VMA of first buffer above 
+   Submit execbuffer
+   Verify value of first DWORD in second shared buffer matches DATA
+
+ at endcode
+**/
+
+static void gem_repin_test(void)
+{
+	i915_gem_userptr* userptr = NULL;
+	i915_gem_userptr* userptr1 = NULL;
+	int fd, ret;
+	uint32_t* shared_buffer = NULL;
+	uint32_t* shared_buffer1 = NULL;
+	struct drm_i915_gem_execbuffer2 execbuf, execbuf1;
+	struct drm_i915_gem_exec_object2 exec_object2[NUM_EXEC_OBJECTS];
+	uint32_t batch_buffer[STORE_BATCH_BUFFER_SIZE];
+	uint32_t batch_buf_handle, shared_buf_handle, shared_buf_handle1;
+	int ring, len;
+	const uint32_t data = 0x12345678;
+
+	// Create gem object
+	fd = drm_open_driver(DRIVER_INTEL);
+	batch_buf_handle = gem_create(fd, BO_SIZE);
+
+	// create cpu buffer, set to all 0xF's
+	shared_buffer = gem_create_mem_buffer(BO_SIZE);
+	shared_buffer1 = gem_create_mem_buffer(BO_SIZE * 2);
+
+	// share with GPU
+	userptr = gem_create_userptr_struct(shared_buffer, BO_SIZE, 0);
+	ret = gem_call_userptr_ioctl(fd, userptr);
+	igt_assert_eq(ret, 0);
+
+	userptr1 = gem_create_userptr_struct(shared_buffer1, BO_SIZE * 2, 0);
+	ret = gem_call_userptr_ioctl(fd, userptr1);
+	igt_assert_eq(ret, 0);
+
+	// Get handle for shared buffer
+	shared_buf_handle = userptr->handle;
+	shared_buf_handle1 = userptr1->handle;
+	free(userptr);
+	free(userptr1);
+
+	// create command buffer with write command
+	len = gem_store_data_svm(fd, batch_buffer, shared_buffer, data, true);
+	gem_write(fd, batch_buf_handle, 0, batch_buffer, len);
+
+	// submit command buffer
+	setup_exec_obj(&exec_object2[0], shared_buf_handle, EXEC_OBJECT_PINNED, shared_buffer);
+	setup_exec_obj(&exec_object2[1], batch_buf_handle, 0, 0);
+
+	ring = 0;
+	if (HAS_BLT_RING(intel_get_drm_devid(fd)))
+		ring = I915_EXEC_BLT;
+
+	execbuf.buffers_ptr = (uintptr_t)exec_object2;
+	execbuf.buffer_count = NUM_EXEC_OBJECTS;
+	execbuf.batch_start_offset = 0;
+	execbuf.batch_len = len;
+	execbuf.cliprects_ptr = 0;
+	execbuf.num_cliprects = 0;
+	execbuf.DR1 = 0;
+	execbuf.DR4 = 0;
+	execbuf.flags = ring;
+	i915_execbuffer2_set_context_id(execbuf, 0);
+	execbuf.rsvd2 = 0;
+
+	gem_execbuf(fd, &execbuf);
+	gem_sync(fd, batch_buf_handle); 
+
+
+	// Second buffer
+	// create command buffer with write command
+	len = gem_store_data_svm(fd, batch_buffer, shared_buffer1, data, true);
+	gem_write(fd, batch_buf_handle, 0, batch_buffer, len);
+
+	// submit command buffer
+	// Pin at shared_buffer, not shared_buffer1
+	setup_exec_obj(&exec_object2[0], shared_buf_handle1, EXEC_OBJECT_PINNED, shared_buffer);
+	setup_exec_obj(&exec_object2[1], batch_buf_handle, 0, 0);
+
+	ring = 0;
+	if (HAS_BLT_RING(intel_get_drm_devid(fd)))
+		ring = I915_EXEC_BLT;
+
+	execbuf1.buffers_ptr = (uintptr_t)exec_object2;
+	execbuf1.buffer_count = NUM_EXEC_OBJECTS;
+	execbuf1.batch_start_offset = 0;
+	execbuf1.batch_len = len;
+	execbuf1.cliprects_ptr = 0;
+	execbuf1.num_cliprects = 0;
+	execbuf1.DR1 = 0;
+	execbuf1.DR4 = 0;
+	execbuf1.flags = ring;
+	i915_execbuffer2_set_context_id(execbuf1, 0);
+	execbuf1.rsvd2 = 0;
+
+	gem_execbuf(fd, &execbuf1);
+	gem_sync(fd, batch_buf_handle); 
+
+	gem_close(fd, batch_buf_handle);
+	close(fd);
+
+	free(shared_buffer);
+	free(shared_buffer1);
+}
+
+
+/** gem_repin_overlap_test
+ *  This test will attempt to pin two buffers at the same VMA as part of the same
+    execbuffer object
+
+ at code
+   Malloc a 4K buffer
+   Share buffer with with GPU by using userptr ioctl
+   Create second shared buffer
+   Create batch buffer to write DATA to first dword of each buffer
+   Use virtual address of each buffer as destination addresses in batch buffer
+   Set EXEC_OBJECT_PINNED flag in both exec objects
+   Set 'offset' in both exec objects to first shared buffer VMA
+   Submit execbuffer
+   Command should return EINVAL, since we are trying to pin to same VMA
+ at endcode
+**/
+static void gem_pin_overlap_test(void)
+{
+	i915_gem_userptr* userptr = NULL;
+	i915_gem_userptr* userptr1 = NULL;
+	int fd, ret;
+	uint32_t* shared_buffer = NULL;
+	uint32_t* shared_buffer1 = NULL;
+	struct drm_i915_gem_execbuffer2 execbuf;
+	struct drm_i915_gem_exec_object2 exec_object2[NUM_EXEC_OBJECTS + 1];
+	uint32_t batch_buffer[BO_SIZE];
+	uint32_t batch_buf_handle, shared_buf_handle, shared_buf_handle1;
+	int ring, len;
+	const uint32_t data = 0x12345678;
+
+	fd = drm_open_driver(DRIVER_INTEL);
+	batch_buf_handle = gem_create(fd, BO_SIZE);
+
+	shared_buffer = gem_create_mem_buffer(BO_SIZE);
+	shared_buffer1 = gem_create_mem_buffer(BO_SIZE * 2);
+
+	/* share with GPU */
+	userptr = gem_create_userptr_struct(shared_buffer, BO_SIZE, 0);
+	ret = gem_call_userptr_ioctl(fd, userptr);
+	igt_assert_eq(ret, 0);
+
+	userptr1 = gem_create_userptr_struct(shared_buffer1, BO_SIZE * 2, 0);
+	ret = gem_call_userptr_ioctl(fd, userptr1);
+	igt_assert_eq(ret, 0);
+
+	shared_buf_handle = userptr->handle;
+	shared_buf_handle1 = userptr1->handle;
+	free(userptr);
+	free(userptr1);
+
+	len = gem_store_data_svm(fd, batch_buffer, shared_buffer, data, false);
+	len += gem_store_data_svm(fd, (batch_buffer + len/4), shared_buffer1, data, true);
+	gem_write(fd, batch_buf_handle, 0, batch_buffer, len);
+
+	/* submit command buffer */
+	setup_exec_obj(&exec_object2[0], shared_buf_handle, EXEC_OBJECT_PINNED, shared_buffer);
+	setup_exec_obj(&exec_object2[1], shared_buf_handle1, EXEC_OBJECT_PINNED, shared_buffer);
+	setup_exec_obj(&exec_object2[2], batch_buf_handle, 0, 0);
+
+	ring = 0;
+	if (HAS_BLT_RING(intel_get_drm_devid(fd)))
+		ring = I915_EXEC_BLT;
+
+	execbuf.buffers_ptr = (uintptr_t)exec_object2;
+	execbuf.buffer_count = NUM_EXEC_OBJECTS + 1;
+	execbuf.batch_start_offset = 0;
+	execbuf.batch_len = len;
+	execbuf.cliprects_ptr = 0;
+	execbuf.num_cliprects = 0;
+	execbuf.DR1 = 0;
+	execbuf.DR4 = 0;
+	execbuf.flags = ring;
+	i915_execbuffer2_set_context_id(execbuf, 0);
+	execbuf.rsvd2 = 0;
+
+	ret = drmIoctl(fd,
+	      DRM_IOCTL_I915_GEM_EXECBUFFER2,
+	      &execbuf);
+
+	/* expect to fail */
+	igt_assert(errno == 22);
+	igt_assert_neq(ret, 0);
+
+	gem_close(fd, batch_buf_handle);
+	close(fd);
+
+	free(shared_buffer);
+	free(shared_buffer1);
+}
+
+
+/** gem_evict_test
+ *  create shared buffer, pin it
+ *  create normal buffer
+ *  try and relocate to shared location
+ *  Opens: How to detect eviction occured? 
+ *  i915_gem_gtt - debugfs api - grep it for the page? 
+
+ at code
+    Create a gem buffer of 4K
+    Malloc a 4K buffer
+    Share buffer with GPU using userptr ioctl
+    Create a batch buffer to write 0x11111111 and 0x22222222 in above 2 buffers
+    Pin Shared buffer to offset '0' in GTT
+    Create reloc buffer to ensure gem buffer is relocated to GTT
+    Submit execbuffer
+    Verify shared buffer has 0x22222222 as expected
+    Obtain offset of where gem object has been placed from exec object field
+    Try to pin shared buffer at that address using 'offset' field in exec object
+    Prevent relocation by setting relocation_count = 0
+    Submit execbuffer
+    Shared buffer will be pinned to previous address of gem object
+    Unshared buffer will be evicted, since relocation is not allowed
+    Second batch buffer will write 0x11111111 to shared buffer instead of unshared
+    Verify shared buffer contains 0x11111111
+    Reverse order of instructions in batch buffer to write to unshared first
+ at endcode
+
+**/
+
+static void gem_evict_test(void)
+{
+	i915_gem_userptr* userptr = NULL;
+	int fd, ret;
+	uint32_t* shared_buffer = NULL;
+	struct drm_i915_gem_relocation_entry reloc[4];
+	struct drm_i915_gem_execbuffer2 execbuf;
+	struct drm_i915_gem_exec_object2 exec_object2[NUM_EXEC_OBJECTS + 1];
+	uint32_t batch_buffer[STORE_BATCH_BUFFER_SIZE * 2];
+	uint32_t batch_buf_handle, shared_buf_handle, unshared_buf_handle;
+	int ring, len;
+
+	fd = drm_open_driver(DRIVER_INTEL);
+	batch_buf_handle = gem_create(fd, BO_SIZE);
+	unshared_buf_handle = gem_create(fd, BO_SIZE);
+
+	shared_buffer = gem_create_mem_buffer(BO_SIZE);
+	*shared_buffer = 0xFFFFFFFF;
+
+	// share with GPU
+	userptr = gem_create_userptr_struct(shared_buffer, BO_SIZE, 0);
+	ret = gem_call_userptr_ioctl(fd, userptr);
+	igt_assert_eq(ret, 0);
+
+	// Get handle for shared buffer
+	shared_buf_handle = userptr->handle;
+	free(userptr);
+
+	// create command buffer with write commands
+	len = gem_store_data(fd, batch_buffer, unshared_buf_handle, 0x11111111, reloc, false);
+	len += gem_store_data_svm(fd, batch_buffer + (len/4), NULL, 0x22222222, true);
+	igt_assert_lte(len, STORE_BATCH_BUFFER_SIZE * 2 * 4);
+	gem_write(fd, batch_buf_handle, 0, batch_buffer, len);
+
+	// submit command buffer
+	setup_exec_obj(&exec_object2[0], shared_buf_handle, EXEC_OBJECT_PINNED, 0);
+	setup_exec_obj(&exec_object2[1], unshared_buf_handle, 0, 0);
+	setup_exec_obj(&exec_object2[2], batch_buf_handle, 0, 0);
+
+	exec_object2[2].relocation_count = 1;
+	exec_object2[2].relocs_ptr = (uintptr_t)reloc;
+
+	ring = 0;
+	if (HAS_BLT_RING(intel_get_drm_devid(fd)))
+		ring = I915_EXEC_BLT;
+
+	execbuf.buffers_ptr = (uintptr_t)exec_object2;
+	execbuf.buffer_count = NUM_EXEC_OBJECTS + 1;
+	execbuf.batch_start_offset = 0;
+	execbuf.batch_len = len;
+	execbuf.cliprects_ptr = 0;
+	execbuf.num_cliprects = 0;
+	execbuf.DR1 = 0;
+	execbuf.DR4 = 0;
+	execbuf.flags = ring;
+	i915_execbuffer2_set_context_id(execbuf, 0);
+	execbuf.rsvd2 = 0;
+
+	gem_execbuf(fd, &execbuf);
+	gem_sync(fd, batch_buf_handle); 
+	igt_assert(*shared_buffer == 0x22222222);
+	*shared_buffer = 0xffffffff;
+	// Now cause eviction of unshared buffer by pinning shared buffer there
+	exec_object2[0].offset = exec_object2[1].offset;
+	// Prevent relocation
+	exec_object2[2].relocation_count = 0;
+
+	gem_execbuf(fd, &execbuf);
+	gem_sync(fd, batch_buf_handle); 
+	igt_assert(*shared_buffer == 0x11111111);
+	igt_assert(exec_object2[0].offset != exec_object2[1].offset);
+
+	// Now lets do it again with the objects listed in reverse order...
+	*shared_buffer = 0xffffffff;
+	setup_exec_obj(&exec_object2[0], unshared_buf_handle, 0, 0);
+	setup_exec_obj(&exec_object2[1], shared_buf_handle, EXEC_OBJECT_PINNED, 0);
+	exec_object2[2].relocation_count = 1;
+	gem_execbuf(fd, &execbuf);
+	gem_sync(fd, batch_buf_handle);
+	igt_assert(*shared_buffer == 0x22222222);
+	*shared_buffer = 0xffffffff;
+	// Now cause eviction of unshared buffer by pinning shared buffer there
+	exec_object2[1].offset = exec_object2[0].offset;
+	// Prevent relocation
+	exec_object2[2].relocation_count = 0;
+
+	gem_execbuf(fd, &execbuf);
+	gem_sync(fd, batch_buf_handle);
+	igt_assert(*shared_buffer == 0x11111111);
+	igt_assert(exec_object2[0].offset != exec_object2[1].offset);
+
+	gem_close(fd, batch_buf_handle);
+	close(fd);
+
+	free(shared_buffer);
+}
+
+/** gem_stress_test - Stress test which creates 10K buffers and shares with GPU 
+ at code
+   Create 10K uint32 buffers of size 4K each
+   Share with GPU using userptr ioctl
+   Create batch buffer to write DATA in first element of each buffer
+   Execute Batch Buffer on Blit ring STRESS_NUM_LOOPS times
+   Validate every buffer has DATA in first element
+   Repeat on Render ring
+ at endcode
+**/
+#define STRESS_NUM_BUFFERS 10240
+#define STRESS_NUM_LOOPS 1000
+
+static void gem_stress_test(void)
+{
+	i915_gem_userptr* userptr = NULL;
+	int fd, ret;
+	uint32_t* shared_buffer[STRESS_NUM_BUFFERS];
+	uint32_t shared_handle[STRESS_NUM_BUFFERS];
+	struct drm_i915_gem_execbuffer2 execbuf;
+	struct drm_i915_gem_exec_object2 exec_object2[STRESS_NUM_BUFFERS + 1];
+	uint32_t batch_buffer[4*STRESS_NUM_BUFFERS + 2];
+	uint32_t batch_buf_handle;
+	int ring, len, i, j;
+
+	fd = drm_open_driver(DRIVER_INTEL);
+	batch_buf_handle = gem_create(fd, sizeof(batch_buffer));
+
+	// create command buffer with write commands
+	len = 0;
+	for(i = 0; i < STRESS_NUM_BUFFERS; i++) 
+	{
+		shared_buffer[i] = gem_create_mem_buffer(BO_SIZE);
+		*shared_buffer[i] = 0xFFFFFFFF;
+
+		// share with GPU
+		userptr = gem_create_userptr_struct(shared_buffer[i], BO_SIZE, 0);
+		ret = gem_call_userptr_ioctl(fd, userptr);
+		igt_assert_eq(ret, 0);
+
+		// Get handle for shared buffer
+		shared_handle[i] = userptr->handle;
+		free(userptr);
+
+		setup_exec_obj(&exec_object2[i], shared_handle[i], EXEC_OBJECT_PINNED, shared_buffer[i]);
+		len += gem_store_data_svm(fd, batch_buffer + (len/4), shared_buffer[i], i , (i == STRESS_NUM_BUFFERS-1) ? true:false);
+	}
+	gem_write(fd, batch_buf_handle, 0, batch_buffer, len);
+
+	// submit command buffer
+
+	exec_object2[STRESS_NUM_BUFFERS].handle = batch_buf_handle;
+	exec_object2[STRESS_NUM_BUFFERS].relocation_count = 0;
+	exec_object2[STRESS_NUM_BUFFERS].relocs_ptr = 0;
+	exec_object2[STRESS_NUM_BUFFERS].alignment = 0;
+	exec_object2[STRESS_NUM_BUFFERS].offset = 0;
+	exec_object2[STRESS_NUM_BUFFERS].flags = 0;
+	exec_object2[STRESS_NUM_BUFFERS].rsvd1 = 0;
+	exec_object2[STRESS_NUM_BUFFERS].rsvd2 = 0;
+
+	ring = 0;
+	if (HAS_BLT_RING(intel_get_drm_devid(fd)))
+		ring = I915_EXEC_BLT;
+
+	execbuf.buffers_ptr = (uintptr_t)exec_object2;
+	execbuf.buffer_count = STRESS_NUM_BUFFERS + 1;
+	execbuf.batch_start_offset = 0;
+	execbuf.batch_len = len;
+	execbuf.cliprects_ptr = 0;
+	execbuf.num_cliprects = 0;
+	execbuf.DR1 = 0;
+	execbuf.DR4 = 0;
+	execbuf.flags = ring;
+	i915_execbuffer2_set_context_id(execbuf, 0);
+	execbuf.rsvd2 = 0;
+
+	for (i = 0; i < STRESS_NUM_LOOPS; i++) 
+	{
+		gem_execbuf(fd, &execbuf);
+		gem_sync(fd, batch_buf_handle);
+		for(j = 0; j < STRESS_NUM_BUFFERS; j++) 
+		{
+			igt_fail_on_f(*shared_buffer[j] != j,
+				      "Mismatch in object %d, iteration %d: 0x%08X\n", j, i, *shared_buffer[j]);
+			*shared_buffer[j] = (j<<2)+1;
+		}
+	}
+
+	// Now Render Ring
+	ring = I915_EXEC_BLT;
+	execbuf.flags = ring;
+	for (i = 0; i < STRESS_NUM_LOOPS; i++) 
+	{
+		gem_execbuf(fd, &execbuf);
+		gem_sync(fd, batch_buf_handle);
+		for(j = 0; j < STRESS_NUM_BUFFERS; j++) 
+		{
+			igt_fail_on_f(*shared_buffer[j] != j,
+				      "Mismatch in object %d, iteration %d: 0x%08X\n", j, i, *shared_buffer[j]);
+			*shared_buffer[j] = (j<<2)+1;
+		}
+	}
+
+	gem_close(fd, batch_buf_handle);
+	close(fd);
+
+	for(i = 0; i < STRESS_NUM_BUFFERS; i++) 
+	{
+		free(shared_buffer[i]);
+	}
+}
+
+#define STRESS2_NUM_BUFFERS 1
+#define STRESS2_NUM_LOOPS 256
+
+/** gem_write_multipage_buffer - Create a buffer spanning multiple
+    pages, and share with GPU. Write to every element of the buffer
+    and verify correct contents.
+
+ at code
+   Create 16K uint32 buffer
+   Share with GPU using userptr ioctl
+   Create batch buffer to write DATA in all elements of buffer
+   Execute Batch Buffer
+   Validate every element has DATA
+ at endcode
+**/
+static void gem_write_multipage_buffer_test(void)
+{
+	i915_gem_userptr* userptr = NULL;
+	int fd, ret;
+	uint32_t* shared_buffer;
+	uint32_t shared_handle;
+	struct drm_i915_gem_execbuffer2 execbuf;
+	struct drm_i915_gem_exec_object2 exec_object2[NUM_EXEC_OBJECTS];
+	uint32_t batch_buffer[(4 * BO_SIZE) + 2];
+	uint32_t batch_buf_handle;
+	int ring, len, i, j;
+
+	fd = drm_open_driver(DRIVER_INTEL);
+	batch_buf_handle = gem_create(fd, sizeof(batch_buffer));
+
+	/* create command buffer with write commands */
+	len = 0;
+	shared_buffer = gem_create_mem_buffer(BO_SIZE * 4);
+	memset(batch_buffer, 0, sizeof(batch_buffer));
+
+	for(i = 0; i< BO_SIZE; i++)
+	{
+		shared_buffer[i] = 0;
+	}
+
+	/* share with GPU */
+	userptr = gem_create_userptr_struct(shared_buffer, BO_SIZE * 4, 0);
+	ret = gem_call_userptr_ioctl(fd, userptr);
+	igt_assert_eq(ret, 0);
+
+	/* Get handle for shared buffer */
+	shared_handle = userptr->handle;
+	free(userptr);
+
+	setup_exec_obj(&exec_object2[0], shared_handle, EXEC_OBJECT_PINNED, shared_buffer);
+
+	for(j=0; j< (BO_SIZE); j++)
+	{
+		len += gem_store_data_svm(fd, batch_buffer + (len/4), &shared_buffer[j], j, (j == ((BO_SIZE)-1)) ? true:false);
+	}
+	
+	gem_write(fd, batch_buf_handle, 0, batch_buffer, len);
+
+	// submit command buffer
+	setup_exec_obj(&exec_object2[1], batch_buf_handle, 0, 0);
+
+	ring = 0;
+	if (HAS_BLT_RING(intel_get_drm_devid(fd)))
+		ring = I915_EXEC_BLT;
+
+	execbuf.buffers_ptr = (uintptr_t)exec_object2;
+	execbuf.buffer_count = NUM_EXEC_OBJECTS;
+	execbuf.batch_start_offset = 0;
+	execbuf.batch_len = len;
+	execbuf.cliprects_ptr = 0;
+	execbuf.num_cliprects = 0;
+	execbuf.DR1 = 0;
+	execbuf.DR4 = 0;
+	execbuf.flags = ring;
+	i915_execbuffer2_set_context_id(execbuf, 0);
+	execbuf.rsvd2 = 0;
+
+	gem_execbuf(fd, &execbuf);
+	gem_sync(fd, batch_buf_handle);
+	for(j = 0; j < (BO_SIZE); j++) 
+	{
+		igt_fail_on_f(shared_buffer[j] != j,
+			      "Mismatch in object %d: 0x%08X\n", j, shared_buffer[j]);
+	}
+
+	gem_close(fd, batch_buf_handle);
+	close(fd);
+
+	free(shared_buffer);
+}
+
+/** This test will request to pin a shared buffer to an invalid
+    VMA  > 48-bit address
+
+   Create shared buffer of size 4K
+   Try and Pin object to address 0x9000000000000
+**/
+static void gem_pin_invalid_vma_test(void)
+{
+	i915_gem_userptr* userptr = NULL;
+	int fd, ret;
+	uint32_t* shared_buffer = NULL;
+	struct drm_i915_gem_execbuffer2 execbuf;
+	struct drm_i915_gem_exec_object2 exec_object2[NUM_EXEC_OBJECTS];
+	uint32_t batch_buffer[BO_SIZE];
+	uint32_t batch_buf_handle, shared_buf_handle;
+	int ring, len;
+	const uint32_t data = 0x12345678;
+	uint32_t* invalid_address = (uint32_t*)0x9000000000000;
+
+	fd = drm_open_driver(DRIVER_INTEL);
+	batch_buf_handle = gem_create(fd, BO_SIZE);
+
+	shared_buffer = gem_create_mem_buffer(BO_SIZE);
+	*shared_buffer = 0xFFFFFFFF;
+	
+	// share with GPU
+	userptr = gem_create_userptr_struct(shared_buffer, BO_SIZE, 0);
+	ret = gem_call_userptr_ioctl(fd, userptr);
+	igt_assert_eq(ret, 0);
+
+	shared_buf_handle = userptr->handle;
+	free(userptr);
+
+	len = gem_store_data_svm(fd, batch_buffer, shared_buffer, data, true);
+	gem_write(fd, batch_buf_handle, 0, batch_buffer, len);
+
+	setup_exec_obj(&exec_object2[0], shared_buf_handle, EXEC_OBJECT_PINNED, invalid_address);
+	setup_exec_obj(&exec_object2[1], batch_buf_handle, 0, 0);
+
+	ring = 0;
+	if (HAS_BLT_RING(intel_get_drm_devid(fd)))
+		ring = I915_EXEC_BLT;
+
+	execbuf.buffers_ptr = (uintptr_t)exec_object2;
+	execbuf.buffer_count = NUM_EXEC_OBJECTS;
+	execbuf.batch_start_offset = 0;
+	execbuf.batch_len = len;
+	execbuf.cliprects_ptr = 0;
+	execbuf.num_cliprects = 0;
+	execbuf.DR1 = 0;
+	execbuf.DR4 = 0;
+	execbuf.flags = ring;
+	i915_execbuffer2_set_context_id(execbuf, 0);
+	execbuf.rsvd2 = 0;
+
+	/* Expect execbuf to fail */
+	ret = drmIoctl(fd,
+	       DRM_IOCTL_I915_GEM_EXECBUFFER2,
+	       &execbuf);
+
+	igt_assert_neq(ret, 0);
+	
+	gem_sync(fd, batch_buf_handle); 
+	gem_close(fd, batch_buf_handle);
+	close(fd);
+
+	free(shared_buffer);
+}
+
+#define SHM_KEY 56789
+#define SHMEM_SIZE 4096
+/** gem_shmem_svm_test - Test userptr ioctl with shared memory
+ * This test creates a sysV IPC buffer and shares with GPU. 
+ * It will send GPU commands to write DATA in the buffer and 
+ * validate it on the CPU side when the command completes.
+
+ * Create arbitrary shmem id
+ * Use shmat to attach a 4K uint32 buffer to above id
+ * Share buffer with GPU using userptr ioctl
+ * Create Batch buffer to write DATA in the first element
+ * submit execbuffer
+ * Validate on CPU side that DATA was indeed written
+ */
+static void gem_shmem_test(void)
+{
+	int shmid;
+	i915_gem_userptr* userptr = NULL;
+	int fd, ret;
+	uint32_t* shared_buffer = NULL;
+	struct drm_i915_gem_execbuffer2 execbuf;
+	struct drm_i915_gem_exec_object2 exec_object2[NUM_EXEC_OBJECTS];
+	uint32_t batch_buffer[BO_SIZE];
+	uint32_t batch_buf_handle, shared_buf_handle;
+	int ring, len;
+	const uint32_t data = 0x12345678;
+
+	shmid = shmget(SHM_KEY, SHMEM_SIZE, IPC_CREAT);
+	igt_assert_neq(shmid, -1);
+
+	shared_buffer = shmat(shmid, NULL, 0);	      
+	igt_assert(shared_buffer != (void*)-1);
+
+	memset(shared_buffer, 0, SHMEM_SIZE);
+	shared_buffer[0] = 0xFFFFFFFF;
+
+	fd = drm_open_driver(DRIVER_INTEL);
+	batch_buf_handle = gem_create(fd, BO_SIZE);
+
+	userptr = gem_create_userptr_struct(shared_buffer, SHMEM_SIZE, 0);
+	ret = gem_call_userptr_ioctl(fd, userptr);
+	igt_assert_eq(ret, 0);
+
+	shared_buf_handle = userptr->handle;
+	free(userptr);
+	
+	/* create command buffer with write command */
+	len = gem_store_data_svm(fd, batch_buffer, shared_buffer, data, true);
+	gem_write(fd, batch_buf_handle, 0, batch_buffer, len);
+
+	/* submit command buffer */
+	setup_exec_obj(&exec_object2[0], shared_buf_handle, EXEC_OBJECT_PINNED, shared_buffer);
+	setup_exec_obj(&exec_object2[1], batch_buf_handle, 0, 0);
+
+	ring = 0;
+	if (HAS_BLT_RING(intel_get_drm_devid(fd)))
+		ring = I915_EXEC_BLT;
+
+	execbuf.buffers_ptr = (uintptr_t)exec_object2;
+	execbuf.buffer_count = NUM_EXEC_OBJECTS;
+	execbuf.batch_start_offset = 0;
+	execbuf.batch_len = len;
+	execbuf.cliprects_ptr = 0;
+	execbuf.num_cliprects = 0;
+	execbuf.DR1 = 0;
+	execbuf.DR4 = 0;
+	execbuf.flags = ring;
+	i915_execbuffer2_set_context_id(execbuf, 0);
+	execbuf.rsvd2 = 0;
+
+	gem_execbuf(fd, &execbuf);
+	gem_sync(fd, batch_buf_handle); 
+
+	gem_close(fd, batch_buf_handle);
+	close(fd);
+
+	/* check on CPU to see if value changes */
+	igt_fail_on_f(shared_buffer[0] != data,
+		      "\nCPU read does not match GPU write, expected: 0x%x, got: 0x%x\n", data, shared_buffer[0]);
+
+	ret = shmdt(shared_buffer);
+	igt_assert_eq(ret, 0);
+
+}
+
+/** gem_pin_high_address_test - This test will create a shared buffer, and create a command
+ *  for GPU to write data in it. It will attempt to pin the buffer at address > 32 bits.
+ *  CPU will read and make sure expected value is obtained
+
+ at code
+   Malloc a 4K buffer
+   Share buffer with with GPU by using userptr ioctl
+   Create batch buffer to write DATA to first dword of buffer
+   Use virtual address of buffer as 0x1100000000 (> 32 bit)
+   Set EXEC_OBJECT_PINNED flag in exec object
+   Set 'offset' in exec object to shared buffer VMA
+   Submit execbuffer
+   Verify value of first DWORD in shared buffer matches DATA
+ at endcode
+*/
+
+static void gem_pin_high_address_test(void)
+{
+	i915_gem_userptr* userptr = NULL;
+	int fd, ret;
+	uint32_t* shared_buffer = NULL;
+	struct drm_i915_gem_execbuffer2 execbuf;
+	struct drm_i915_gem_exec_object2 exec_object2[NUM_EXEC_OBJECTS];
+	uint32_t batch_buffer[BO_SIZE];
+	uint32_t batch_buf_handle, shared_buf_handle;
+	int ring, len;
+	const uint32_t data = 0x12345678;
+        uint32_t* high_address = (uint32_t*)0x1111FFFF000;
+
+	fd = drm_open_driver(DRIVER_INTEL);
+	batch_buf_handle = gem_create(fd, BO_SIZE);
+
+	/* create cpu buffer, set to all 0xF's */
+	shared_buffer = gem_create_mem_buffer(BO_SIZE);
+	*shared_buffer = 0xFFFFFFFF;
+
+	/* share with GPU */
+	userptr = gem_create_userptr_struct(shared_buffer, BO_SIZE, 0);
+	ret = gem_call_userptr_ioctl(fd, userptr);
+	igt_assert_eq(ret, 0);
+
+	/* Get handle for shared buffer */
+	shared_buf_handle = userptr->handle;
+	free(userptr);
+ 
+	/* create command buffer with write command */
+	len = gem_store_data_svm(fd, batch_buffer, high_address, data, true);
+	gem_write(fd, batch_buf_handle, 0, batch_buffer, len);
+
+	/* submit command buffer */
+	setup_exec_obj(&exec_object2[0], shared_buf_handle, EXEC_OBJECT_PINNED | EXEC_OBJECT_SUPPORTS_48B_ADDRESS, high_address);
+	setup_exec_obj(&exec_object2[1], batch_buf_handle, 0, 0);
+
+	ring = 0;
+	if (HAS_BLT_RING(intel_get_drm_devid(fd)))
+		ring = I915_EXEC_BLT;
+
+	execbuf.buffers_ptr = (uintptr_t)exec_object2;
+	execbuf.buffer_count = NUM_EXEC_OBJECTS;
+	execbuf.batch_start_offset = 0;
+	execbuf.batch_len = len;
+	execbuf.cliprects_ptr = 0;
+	execbuf.num_cliprects = 0;
+	execbuf.DR1 = 0;
+	execbuf.DR4 = 0;
+	execbuf.flags = ring;
+	i915_execbuffer2_set_context_id(execbuf, 0);
+	execbuf.rsvd2 = 0;
+
+	gem_execbuf(fd, &execbuf);
+	gem_sync(fd, batch_buf_handle);
+
+	// check on CPU to see if value changes
+	igt_fail_on_f(shared_buffer[0] != data,
+		      "\nCPU read does not match GPU write, expected: 0x%x, got: 0x%x\n", data, shared_buffer[0]);
+
+	gem_close(fd, batch_buf_handle);
+	close(fd);
+	free(shared_buffer);
+}
+
+/** gem_pin_near_48Bit_test - This test will create a shared buffer, and create a command
+ *  for GPU to write data in it. It will attempt to pin the buffer at address > 47 bits <= 48-bit.
+ *  CPU will read and make sure expected value is obtained
+
+ at code
+   Malloc a 4K buffer
+   Share buffer with with GPU by using userptr ioctl
+   Create batch buffer to write DATA to first dword of buffer
+   Use virtual address of buffer as range between 47-bit and 48-bit
+   Set EXEC_OBJECT_PINNED flag in exec object
+   Set 'offset' in exec object to shared buffer VMA
+   Submit execbuffer
+   Verify value of first DWORD in shared buffer matches DATA
+ at endcode
+*/
+#define BEGIN_HIGH_ADDRESS 0x7FFFFFFFF000 
+#define END_HIGH_ADDRESS 0xFFFFFFFFC000
+#define ADDRESS_INCREMENT 0x2000000000
+static void gem_pin_near_48Bit_test(void)
+{
+	i915_gem_userptr* userptr = NULL;
+	int fd, ret;
+	uint32_t* shared_buffer = NULL;
+	struct drm_i915_gem_execbuffer2 execbuf;
+	struct drm_i915_gem_exec_object2 exec_object2[NUM_EXEC_OBJECTS];
+	uint32_t batch_buffer[BO_SIZE];
+	uint32_t batch_buf_handle, shared_buf_handle;
+	int ring, len;
+	const uint32_t data = 0x12345678;
+        uint32_t* high_address;
+
+	fd = drm_open_driver(DRIVER_INTEL);
+	batch_buf_handle = gem_create(fd, BO_SIZE);
+
+	/* create cpu buffer, set to all 0xF's */
+	shared_buffer = gem_create_mem_buffer(BO_SIZE);
+	*shared_buffer = 0xFFFFFFFF;
+
+	/* share with GPU */
+	userptr = gem_create_userptr_struct(shared_buffer, BO_SIZE, 0);
+	ret = gem_call_userptr_ioctl(fd, userptr);
+	igt_assert_eq(ret, 0);
+
+	/* Get handle for shared buffer */
+	shared_buf_handle = userptr->handle;
+	free(userptr);
+
+	for (high_address = (uint32_t*)BEGIN_HIGH_ADDRESS; high_address <= (uint32_t*)END_HIGH_ADDRESS; 
+						high_address+=ADDRESS_INCREMENT)
+	{
+		/* create command buffer with write command */
+		len = gem_store_data_svm(fd, batch_buffer, (uint32_t*)high_address, 
+					data, true);
+		gem_write(fd, batch_buf_handle, 0, batch_buffer, len);
+
+		/* submit command buffer */
+		setup_exec_obj(&exec_object2[0], shared_buf_handle, 
+				EXEC_OBJECT_PINNED | EXEC_OBJECT_SUPPORTS_48B_ADDRESS, 
+				high_address);
+		setup_exec_obj(&exec_object2[1], batch_buf_handle, 0, 0);
+
+		ring = 0;
+		if (HAS_BLT_RING(intel_get_drm_devid(fd)))
+			ring = I915_EXEC_BLT;
+
+		execbuf.buffers_ptr = (uintptr_t)exec_object2;
+		execbuf.buffer_count = NUM_EXEC_OBJECTS;
+		execbuf.batch_start_offset = 0;
+		execbuf.batch_len = len;
+		execbuf.cliprects_ptr = 0;
+		execbuf.num_cliprects = 0;
+		execbuf.DR1 = 0;
+		execbuf.DR4 = 0;
+		execbuf.flags = ring;
+		i915_execbuffer2_set_context_id(execbuf, 0);
+		execbuf.rsvd2 = 0;
+
+		gem_execbuf(fd, &execbuf);
+		gem_sync(fd, batch_buf_handle);
+
+		// check on CPU to see if value changes
+		if (shared_buffer[0] != data)
+		{
+			#ifdef IS_32BIT_USER
+			igt_info("\nCPU read does not match GPU write, expected: 0x%"PRIx32", got: 0x%"PRIx32", address: 0x%"PRIx32"\n", data, shared_buffer[0], (uintptr_t)high_address);
+			#else
+			igt_info("\nCPU read does not match GPU write, expected: 0x%"PRIx32", got: 0x%"PRIx32", address: 0x%"PRIx64"\n", data, shared_buffer[0], (uintptr_t)high_address);
+			#endif
+
+			igt_fail(1);
+		}
+	}
+
+	gem_close(fd, batch_buf_handle);
+	close(fd);
+	free(shared_buffer);
+}
+
+/** gem_pin_mmap_anonymous_test - This test will create a mmap anonymous buffer and
+ *  share with GPU. It will run basic test on this buffer. 
+
+ at code
+   Create a anonymous mmap buffer
+   Share buffer with with GPU by using userptr ioctl
+   Create batch buffer to write DATA to first dword of buffer
+   Set EXEC_OBJECT_PINNED flag in exec object
+   Set 'offset' in exec object to shared buffer VMA
+   Submit execbuffer
+   Verify value of first DWORD in shared buffer matches DATA
+ at endcode
+*/
+void gem_pin_mmap_anonymous_test(void)
+{
+	i915_gem_userptr* userptr = NULL;
+	int fd, ret;
+	uint32_t* shared_buffer = NULL;
+	struct drm_i915_gem_execbuffer2 execbuf;
+	struct drm_i915_gem_exec_object2 exec_object2[NUM_EXEC_OBJECTS];
+	uint32_t batch_buffer[BO_SIZE];
+	uint32_t batch_buf_handle, shared_buf_handle;
+	int ring, len;
+	const uint32_t data = 0x12345678;
+
+	fd = drm_open_driver(DRIVER_INTEL);
+	batch_buf_handle = gem_create(fd, BO_SIZE);
+
+	/* create anonymus mmap buffer, set to all 0xF's */
+	shared_buffer = mmap(NULL, BO_SIZE, PROT_WRITE, MAP_PRIVATE | MAP_ANONYMOUS, -1, 0);
+	igt_fail_on_f(shared_buffer == (void *)-1,
+		      "mmap call failed with %s\n", strerror(errno));
+
+	*shared_buffer = 0xFFFFFFFF;
+
+	/* share with GPU */
+	userptr = gem_create_userptr_struct(shared_buffer, BO_SIZE, 0);
+	ret = gem_call_userptr_ioctl(fd, userptr);
+	igt_assert_eq(ret, 0);
+
+	/* Get handle for shared buffer */
+	shared_buf_handle = userptr->handle;
+	free(userptr);
+
+	/* create command buffer with write command */
+	len = gem_store_data_svm(fd, batch_buffer, shared_buffer, data, true);
+	gem_write(fd, batch_buf_handle, 0, batch_buffer, len);
+
+	/* submit command buffer */
+	setup_exec_obj(&exec_object2[0], shared_buf_handle, EXEC_OBJECT_PINNED, shared_buffer);
+	setup_exec_obj(&exec_object2[1], batch_buf_handle, 0, 0);
+
+	ring = 0;
+	if (HAS_BLT_RING(intel_get_drm_devid(fd)))
+		ring = I915_EXEC_BLT;
+
+	execbuf.buffers_ptr = (uintptr_t)exec_object2;
+	execbuf.buffer_count = NUM_EXEC_OBJECTS;
+	execbuf.batch_start_offset = 0;
+	execbuf.batch_len = len;
+	execbuf.cliprects_ptr = 0;
+	execbuf.num_cliprects = 0;
+	execbuf.DR1 = 0;
+	execbuf.DR4 = 0;
+	execbuf.flags = ring;
+	i915_execbuffer2_set_context_id(execbuf, 0);
+	execbuf.rsvd2 = 0;
+
+	gem_execbuf(fd, &execbuf);
+	gem_sync(fd, batch_buf_handle);
+
+	// check on CPU to see if value changes
+	igt_fail_on_f(shared_buffer[0] != data,
+		      "\nCPU read does not match GPU write, expected: 0x%x, got: 0x%x\n", data, shared_buffer[0]);
+
+	gem_close(fd, batch_buf_handle);
+	close(fd);
+	igt_fail_on_f(munmap(shared_buffer, BO_SIZE) != 0,
+		      "munmap failed with: %s", strerror(errno));
+}
+
+/** gem_pin_mmap_file_test - This test will use mmap command to map
+ *  a file in memory. It will then attempt to share the buffer with GPU
+ *  using the userptr ioctl. It will verify if CPU/GPU writes are consistent
+
+ at code
+   open/create a file
+   lseek into the file and write some arbitrary data
+   this allows the mmap'd page to become resident
+   use mmap command to map the file into memory
+   Share buffer with with GPU by using userptr ioctl
+   Create batch buffer to write DATA to first dword of buffer
+   Set EXEC_OBJECT_PINNED flag in exec object
+   Set 'offset' in exec object to shared buffer VMA
+   Submit execbuffer
+   Verify value of first DWORD in shared buffer matches DATA
+   Close file
+ at endcode
+*/
+void gem_pin_mmap_file_test(void)
+{
+	i915_gem_userptr* userptr = NULL;
+	int fd, ret;
+	struct drm_i915_gem_execbuffer2 execbuf;
+	struct drm_i915_gem_exec_object2 exec_object2[NUM_EXEC_OBJECTS];
+	uint32_t batch_buffer[BO_SIZE];
+	uint32_t batch_buf_handle, dest_buf_handle;
+	int ring, len;
+	const uint32_t data = 0x12345678;
+        int fdout;
+	uint32_t *dest;
+	const char filename[] = "svm_mmap.txt";
+
+	fdout = open(filename, O_RDWR | O_CREAT | O_TRUNC, 0640);
+	igt_fail_on_f(fdout < 0, "Cannot open output file\n");
+
+	// Do this to ensure backing physical memory for the file
+	/* go to the location corresponding to the last byte */
+	if (lseek (fdout, BO_SIZE, SEEK_SET) == -1)
+		igt_info("lseek error");
+
+	/* write a dummy byte at the last location */
+	if (write (fdout, "", 1) != 1)
+		igt_info("write error");
+
+	fd = drm_open_driver(DRIVER_INTEL);
+	batch_buf_handle = gem_create(fd, BO_SIZE);
+
+	/* create anonymus mmap buffer, set to all 0xF's */
+	dest = mmap(0, BO_SIZE, PROT_WRITE, MAP_SHARED, fdout, 0);
+	igt_fail_on_f(dest == (void *)-1,
+		      "mmap call failed with %s\n", strerror(errno));
+	*dest = 0x11111111;
+
+	userptr = gem_create_userptr_struct(dest, BO_SIZE, 0);
+	ret = gem_call_userptr_ioctl(fd, userptr);
+	igt_assert_eq(ret, 0);
+	dest_buf_handle = userptr->handle;
+	free(userptr);
+
+	/* create command buffer with write command */
+	len = gem_store_data_svm(fd, batch_buffer, dest, data, true);
+	gem_write(fd, batch_buf_handle, 0, batch_buffer, len);
+
+	/* submit command buffer */
+	setup_exec_obj(&exec_object2[0], dest_buf_handle, EXEC_OBJECT_PINNED, dest);
+	setup_exec_obj(&exec_object2[1], batch_buf_handle, 0, 0);
+
+	ring = 0;
+	if (HAS_BLT_RING(intel_get_drm_devid(fd)))
+		ring = I915_EXEC_RENDER;
+
+	execbuf.buffers_ptr = (uintptr_t)exec_object2;
+	execbuf.buffer_count = NUM_EXEC_OBJECTS;
+	execbuf.batch_start_offset = 0;
+	execbuf.batch_len = len;
+	execbuf.cliprects_ptr = 0;
+	execbuf.num_cliprects = 0;
+	execbuf.DR1 = 0;
+	execbuf.DR4 = 0;
+	execbuf.flags = ring;
+	i915_execbuffer2_set_context_id(execbuf, 0);
+	execbuf.rsvd2 = 0;
+
+	gem_execbuf(fd, &execbuf);
+	gem_sync(fd, batch_buf_handle);
+
+	// check on CPU to see if value changes
+	igt_fail_on_f(*dest != data,
+		      "\nCPU read does not match GPU write, expected: 0x%x, got: 0x%x\n", data, dest[0]);
+
+	gem_close(fd, batch_buf_handle);
+	close(fd);
+	igt_fail_on_f(munmap(dest, BO_SIZE) != 0,
+		      "munmap failed with: %s", strerror(errno));
+	close(fdout);
+}
+
+
+int main(int argc, char* argv[])
+{
+	igt_subtest_init(argc, argv);
+	igt_skip_on_simulation();
+
+	igt_subtest("gem_null_buffer"){
+		gem_basic_test(false);
+	}
+	igt_subtest("gem_invalid_userptr"){
+		gem_invalid_userptr_test();
+	}
+	igt_subtest("gem_basic"){
+		gem_basic_test(true);
+	}
+	igt_subtest("gem_multiple_process"){
+		gem_multiple_process_test();
+	}
+	igt_subtest("gem_repin"){
+		gem_repin_test();
+	}
+	igt_subtest("gem_evict"){
+		gem_evict_test();
+	}
+	igt_subtest("gem_stress"){
+		gem_stress_test();
+	}
+	igt_subtest("gem_pin_overlap"){
+		gem_pin_overlap_test();
+	}
+	igt_subtest("gem_shmem"){
+		gem_shmem_test();
+	}
+	igt_subtest("gem_write_multipage_buffer"){
+		gem_write_multipage_buffer_test();
+	}
+#ifndef IS_32BIT_USER
+	igt_subtest("gem_pin_high_address"){
+		gem_pin_high_address_test();
+	}
+	igt_subtest("gem_pin_near_48Bit"){
+		gem_pin_near_48Bit_test();
+	}
+	igt_subtest("gem_pin_invalid_vma"){
+		gem_pin_invalid_vma_test();
+	}
+#endif
+	igt_subtest("gem_pin_mmap_anon"){
+		gem_pin_mmap_anonymous_test();
+	}
+	igt_subtest("gem_pin_mmap_file"){
+		gem_pin_mmap_file_test();
+	}
+
+	igt_exit();
+}
-- 
1.9.1



More information about the Intel-gfx mailing list