@@ -21,6 +21,7 @@ gem_bad_blit
gem_bad_length
gem_bad_reloc
gem_basic
+gem_softpin
gem_caching
gem_close_race
gem_concurrent_all
@@ -11,6 +11,7 @@ TESTS_progs_M = \
drv_hangman \
gem_bad_reloc \
gem_basic \
+ gem_softpin \
gem_caching \
gem_close_race \
gem_concurrent_blit \
new file mode 100644
@@ -0,0 +1,1252 @@
+/*
+ * 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@intel.com>
+ Thomas Daniel <thomas.daniel@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>
+
+#define OBJECT_SIZE 16384
+#define BO_SIZE 4 * 4096
+#define MULTIPAGE_BO_SIZE 4 * BO_SIZE
+#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 void gem_create_userptr_struct(i915_gem_userptr*, void* ptr, __u64 size, bool read_only);
+static void *create_mem_buffer(__u64 size);
+static int gem_call_userptr_ioctl(int fd, i915_gem_userptr *userptr);
+static void gem_basic_test(void);
+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);
+
+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 void gem_create_userptr_struct(i915_gem_userptr *userptr, void* ptr, __u64 size, bool read_only)
+{
+ memset((void*)userptr, 0, sizeof(i915_gem_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;
+}
+
+/* Creates a 4K aligned CPU buffer
+ * @size - size of buffer
+ * RETURNS pointer to buffer of @size
+*/
+
+static void* create_mem_buffer(__u64 size)
+{
+ void* addr;
+ int ret;
+
+ ret = posix_memalign(&addr, 4096, size);
+ igt_assert(ret == 0);
+
+ return addr;
+}
+
+/* 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, __u64 offset)
+{
+ memset(exec, 0, sizeof(struct drm_i915_gem_exec_object2));
+ exec->handle = handle;
+ exec->flags = flags;
+ exec->offset = offset;
+}
+
+/*
+ * gem_store_data_svm - populate batch buffer with MI_STORE_DWORD_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
+ */
+static int gem_store_data_svm(int fd, uint32_t* cmd_buf, uint64_t vaddr,
+ uint32_t data, bool end)
+{
+ int i = 0;
+
+ cmd_buf[i++] = MI_STORE_DWORD_IMM;
+ cmd_buf[i++] = vaddr & 0xFFFFFFFC;
+ cmd_buf[i++] = (vaddr >> 32) & 0xFFFF; /* bits 32:47 */
+
+ 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 - populate batch buffer with MI_STORE_DWORD_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_STORE_DWORD_IMM;
+ 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));
+}
+
+/* Helper function for filling execbuffer struct */
+static void setup_execbuffer(struct drm_i915_gem_execbuffer2 *execbuf, struct drm_i915_gem_exec_object2 *exec_object,
+ int ring, int buffer_count, int batch_length)
+{
+ execbuf->buffers_ptr = (uintptr_t)exec_object;
+ execbuf->buffer_count = buffer_count;
+ execbuf->batch_start_offset = 0;
+ execbuf->batch_len = batch_length;
+ 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;
+}
+
+/* Helper function for exec and sync functions */
+static void submit_and_sync(int fd, struct drm_i915_gem_execbuffer2 *execbuf, uint32_t batch_buf_handle)
+{
+ gem_execbuf(fd, execbuf);
+ gem_sync(fd, batch_buf_handle);
+}
+
+/* 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
+
+ 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 0x1000 address as destination address in batch buffer
+ Set EXEC_OBJECT_PINNED flag in exec object
+ Set 'offset' in exec object to 0x1000
+ 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
+*/
+static void gem_basic_test(void)
+{
+ i915_gem_userptr userptr;
+ 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;
+ uint64_t pinning_offset = 0x1000;
+
+ fd = drm_open_driver(DRIVER_INTEL);
+ batch_buf_handle = gem_create(fd, BO_SIZE);
+
+ /* create cpu buffer, set to all 0xF's */
+ shared_buffer = create_mem_buffer(BO_SIZE);
+
+ /* share with GPU */
+ gem_create_userptr_struct(&userptr, shared_buffer, BO_SIZE, false);
+ ret = gem_call_userptr_ioctl(fd, &userptr);
+ igt_assert_eq(ret, 0);
+
+ /* Get handle for shared buffer */
+ shared_buf_handle = userptr.handle;
+
+ /* create command buffer with write command */
+ len = gem_store_data_svm(fd, batch_buffer, pinning_offset, 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, pinning_offset);
+ 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;
+
+ setup_execbuffer(&execbuf, exec_object2, ring, NUM_EXEC_OBJECTS, len);
+ submit_and_sync(fd, &execbuf, 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_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
+
+*/
+
+#define MAX_NUM_PROCESSES 10
+
+static void gem_multiple_process_test(void)
+{
+ igt_fork(child, MAX_NUM_PROCESSES) {
+ gem_basic_test();
+ }
+ igt_waitchildren();
+}
+
+
+/* gem_repin_test
+ * This test tries to repin a buffer at a previously pinned vma
+ * from a different execbuf.
+
+ * 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 0x1000 address as destination address in batch buffer
+ * Set EXEC_OBJECT_PINNED flag in exec object
+ * Set 'offset' in exec object to 0x1000 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
+*/
+
+static void gem_repin_test(void)
+{
+ i915_gem_userptr userptr;
+ i915_gem_userptr userptr1;
+ 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];
+ 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;
+ uint64_t pinning_offset = 0x1000;
+
+ /* 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 = create_mem_buffer(BO_SIZE);
+ shared_buffer1 = create_mem_buffer(BO_SIZE);
+ shared_buffer[0] = 0x0;
+ shared_buffer1[0] = 0x0;
+
+ /* share with GPU */
+ gem_create_userptr_struct(&userptr, shared_buffer, BO_SIZE, false);
+ ret = gem_call_userptr_ioctl(fd, &userptr);
+ igt_assert_eq(ret, 0);
+
+ //gem_create_userptr_struct(&userptr1, shared_buffer1, BO_SIZE * 2, false);
+ gem_create_userptr_struct(&userptr1, shared_buffer1, BO_SIZE, false);
+ 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;
+
+ /* create command buffer with write command */
+ len = gem_store_data_svm(fd, batch_buffer, pinning_offset, 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, pinning_offset);
+ 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;
+
+ setup_execbuffer(&execbuf, exec_object2, ring, NUM_EXEC_OBJECTS, len);
+ submit_and_sync(fd, &execbuf, batch_buf_handle);
+
+ igt_assert(*shared_buffer == data);
+
+ /* Second buffer */
+ /* create command buffer with write command */
+ pinning_offset = exec_object2[0].offset;
+ len = gem_store_data_svm(fd, batch_buffer, pinning_offset, 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, pinning_offset);
+ 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;
+
+ setup_execbuffer(&execbuf, exec_object2, ring, NUM_EXEC_OBJECTS, len);
+ submit_and_sync(fd, &execbuf, batch_buf_handle);
+
+ igt_assert(*shared_buffer1 == data);
+
+ 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
+
+@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 same virtual address as destination addresses in batch buffer
+ Set EXEC_OBJECT_PINNED flag in both exec objects
+ Set 'offset' in both exec objects to same VMA
+ Submit execbuffer
+ Command should return EINVAL, since we are trying to pin to same VMA
+@endcode
+**/
+static void gem_pin_overlap_test(void)
+{
+ i915_gem_userptr userptr;
+ i915_gem_userptr userptr1;
+ 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;
+ uint64_t pinning_offset = 0x1000;
+
+ fd = drm_open_driver(DRIVER_INTEL);
+ batch_buf_handle = gem_create(fd, BO_SIZE);
+
+ shared_buffer = create_mem_buffer(BO_SIZE);
+ shared_buffer1 = create_mem_buffer(BO_SIZE * 2);
+
+ /* share with GPU */
+ gem_create_userptr_struct(&userptr, shared_buffer, BO_SIZE, false);
+ ret = gem_call_userptr_ioctl(fd, &userptr);
+ igt_assert_eq(ret, 0);
+
+ gem_create_userptr_struct(&userptr1, shared_buffer1, BO_SIZE * 2, false);
+ ret = gem_call_userptr_ioctl(fd, &userptr1);
+ igt_assert_eq(ret, 0);
+
+ shared_buf_handle = userptr.handle;
+ shared_buf_handle1 = userptr1.handle;
+
+ len = gem_store_data_svm(fd, batch_buffer, pinning_offset, data, false);
+ len += gem_store_data_svm(fd, (batch_buffer + len/4), pinning_offset, 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, pinning_offset);
+ setup_exec_obj(&exec_object2[1], shared_buf_handle1, EXEC_OBJECT_PINNED, pinning_offset);
+ 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;
+
+ setup_execbuffer(&execbuf, exec_object2, ring, NUM_EXEC_OBJECTS+1, len);
+
+ ret = drmIoctl(fd,
+ DRM_IOCTL_I915_GEM_EXECBUFFER2,
+ &execbuf);
+
+ /* expect to fail */
+ igt_assert_neq(ret, 0);
+ igt_assert(errno == 22);
+
+ 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?
+
+ 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
+*/
+
+static void gem_evict_test(void)
+{
+ i915_gem_userptr userptr;
+ 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;
+ uint32_t data1, data2;
+ uint32_t value;
+ uint64_t pinning_offset = 0;
+
+ fd = drm_open_driver(DRIVER_INTEL);
+ batch_buf_handle = gem_create(fd, BO_SIZE);
+ unshared_buf_handle = gem_create(fd, BO_SIZE);
+
+ shared_buffer = create_mem_buffer(BO_SIZE);
+ *shared_buffer = 0xFFFFFFFF;
+
+ /* share with GPU */
+ gem_create_userptr_struct(&userptr, shared_buffer, BO_SIZE, false);
+ ret = gem_call_userptr_ioctl(fd, &userptr);
+ igt_assert_eq(ret, 0);
+
+ /* Get handle for shared buffer */
+ shared_buf_handle = userptr.handle;
+
+ /* create command buffer with write commands */
+ data1 = 0x11111111;
+ data2 = 0x22222222;
+ len = gem_store_data(fd, batch_buffer, unshared_buf_handle, data1, reloc, false);
+ len += gem_store_data_svm(fd, batch_buffer + (len/4), pinning_offset, data2, 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, pinning_offset);
+ 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;
+
+ setup_execbuffer(&execbuf, exec_object2, ring, NUM_EXEC_OBJECTS+1, len);
+ submit_and_sync(fd, &execbuf, batch_buf_handle);
+
+ igt_assert(*shared_buffer == data2);
+ gem_read(fd, unshared_buf_handle, 0, (void*)&value, 4);
+ igt_assert(value == data1);
+
+
+ *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;
+ submit_and_sync(fd, &execbuf, batch_buf_handle);
+
+ igt_assert(*shared_buffer == data1);
+ igt_assert(exec_object2[0].offset != exec_object2[1].offset);
+ /* Unshared buffer gets evicted, and retains its value, since no batch buffer writes to it */
+ gem_read(fd, unshared_buf_handle, 0, (void*)&value, 4);
+ igt_assert(value == data1);
+
+ /* 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;
+ submit_and_sync(fd, &execbuf, batch_buf_handle);
+
+ igt_assert(*shared_buffer == data2);
+ *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;
+ submit_and_sync(fd, &execbuf, batch_buf_handle);
+
+ igt_assert(*shared_buffer == data1);
+ igt_assert(exec_object2[0].offset != exec_object2[1].offset);
+ gem_read(fd, unshared_buf_handle, 0, (void*)&value, 4);
+ igt_assert(value == data1);
+
+ gem_close(fd, batch_buf_handle);
+ close(fd);
+
+ free(shared_buffer);
+}
+
+/** gem_softpin_stress_test - Stress test which creates 10K buffers and shares with GPU
+ Create 100K uint32 buffers of size 4K each
+ Share with GPU using userptr ioctl
+ Create batch buffer to write DATA in first element of each buffer
+ Pin each buffer to varying addresses starting from 0x800000000000 going below
+ Execute Batch Buffer on Blit ring STRESS_NUM_LOOPS times
+ Validate every buffer has DATA in first element
+ Rinse and Repeat on Render ring
+**/
+#define STRESS_NUM_BUFFERS 100240
+#define STRESS_NUM_LOOPS 1
+#define STORE_COMMANDS 4 * STRESS_NUM_BUFFERS
+
+static void gem_softpin_stress_test(void)
+{
+ i915_gem_userptr userptr;
+ 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[STORE_COMMANDS + 2]; /* 4 dwords per buffer + 2 for the end of batchbuffer */
+ uint32_t batch_buf_handle;
+ int ring, len, i, j;
+ uint64_t pinning_offset = 0x800000000000;
+
+ 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] = create_mem_buffer(BO_SIZE);
+ *shared_buffer[i] = 0xFFFFFFFF;
+
+ /* share with GPU */
+ gem_create_userptr_struct(&userptr, shared_buffer[i], BO_SIZE, false);
+ ret = gem_call_userptr_ioctl(fd, &userptr);
+ igt_assert_eq(ret, 0);
+
+ /* Get handle for shared buffer */
+ shared_handle[i] = userptr.handle;
+
+ setup_exec_obj(&exec_object2[i], shared_handle[i], EXEC_OBJECT_PINNED, pinning_offset);
+ len += gem_store_data_svm(fd, batch_buffer + (len/4), pinning_offset, i , (i == STRESS_NUM_BUFFERS-1) ? true:false);
+
+ pinning_offset -= 0x200000; /* incremental 4K aligned address */
+ }
+ gem_write(fd, batch_buf_handle, 0, batch_buffer, len);
+
+ /* submit command buffer */
+ setup_exec_obj(&exec_object2[STRESS_NUM_BUFFERS], batch_buf_handle, 0, 0);
+
+ ring = 0;
+ if (HAS_BLT_RING(intel_get_drm_devid(fd)))
+ ring = I915_EXEC_BLT;
+
+ setup_execbuffer(&execbuf, exec_object2, ring, STRESS_NUM_BUFFERS + 1, len);
+
+ for (i = 0; i < STRESS_NUM_LOOPS; i++){
+ submit_and_sync(fd, &execbuf, batch_buf_handle);
+ for(j = 0; j < STRESS_NUM_BUFFERS; j++){
+ igt_fail_on_f(*shared_buffer[j] != j,
+ "Mismatch in buffer %d, iteration %d: 0x%08X\n", j, i, *shared_buffer[j]);
+ }
+ }
+
+ // Now Render Ring
+ ring = I915_EXEC_RENDER;
+ execbuf.flags = ring;
+ for (i = 0; i < STRESS_NUM_LOOPS; i++){
+ submit_and_sync(fd, &execbuf, batch_buf_handle);
+ for(j = 0; j < STRESS_NUM_BUFFERS; j++){
+ igt_fail_on_f(*shared_buffer[j] != j,
+ "Mismatch in buffer %d, iteration %d: 0x%08X\n", j, i, *shared_buffer[j]);
+ }
+ }
+
+ gem_close(fd, batch_buf_handle);
+ close(fd);
+
+ for(i = 0; i < STRESS_NUM_BUFFERS; i++)
+ {
+ free(shared_buffer[i]);
+ }
+}
+
+/* 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.
+
+ 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
+ */
+static void gem_write_multipage_buffer_test(void)
+{
+ i915_gem_userptr userptr;
+ 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, j;
+ uint64_t pinning_offset=0x1000;
+
+ 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 = create_mem_buffer(MULTIPAGE_BO_SIZE);
+ memset(batch_buffer, 0, sizeof(batch_buffer));
+
+ memset(shared_buffer, 0, MULTIPAGE_BO_SIZE);
+
+ /* share with GPU */
+ gem_create_userptr_struct(&userptr, shared_buffer, MULTIPAGE_BO_SIZE, false);
+ ret = gem_call_userptr_ioctl(fd, &userptr);
+ igt_assert_eq(ret, 0);
+
+ /* Get handle for shared buffer */
+ shared_handle = userptr.handle;
+
+ setup_exec_obj(&exec_object2[0], shared_handle, EXEC_OBJECT_PINNED, pinning_offset);
+
+ /* Every element of buffer */
+ for(j=0; j< (BO_SIZE); j++) /* BO_SIZE because it is 16K 4 byte entries */
+ {
+ len += gem_store_data_svm(fd, batch_buffer + (len/4), pinning_offset, j, (j == ((BO_SIZE)-1)) ? true:false);
+ pinning_offset += sizeof(shared_buffer[0]); /* 4 bytes */
+ }
+
+ 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;
+
+ setup_execbuffer(&execbuf, exec_object2, ring, NUM_EXEC_OBJECTS, len);
+ submit_and_sync(fd, &execbuf, batch_buf_handle);
+
+ for(j = 0; j < (BO_SIZE); j++)
+ {
+ igt_fail_on_f(shared_buffer[j] != j,
+ "Mismatch in index %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;
+ 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;
+ uint64_t invalid_address = 0x9000000000000; /* 52 bit address */
+
+ fd = drm_open_driver(DRIVER_INTEL);
+ batch_buf_handle = gem_create(fd, BO_SIZE);
+
+ shared_buffer = create_mem_buffer(BO_SIZE);
+ *shared_buffer = 0xFFFFFFFF;
+
+ // share with GPU
+ gem_create_userptr_struct(&userptr, shared_buffer, BO_SIZE, false);
+ ret = gem_call_userptr_ioctl(fd, &userptr);
+ igt_assert_eq(ret, 0);
+
+ shared_buf_handle = userptr.handle;
+
+ len = gem_store_data_svm(fd, batch_buffer, invalid_address, 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;
+
+ setup_execbuffer(&execbuf, exec_object2, ring, NUM_EXEC_OBJECTS, len);
+
+ /* Expect execbuf to fail */
+ ret = drmIoctl(fd,
+ DRM_IOCTL_I915_GEM_EXECBUFFER2,
+ &execbuf);
+
+ igt_assert_neq(ret, 0);
+
+ 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;
+ 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;
+ uint64_t pinning_offset = 0x1000;
+
+ 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);
+
+ gem_create_userptr_struct(&userptr, shared_buffer, SHMEM_SIZE, false);
+ ret = gem_call_userptr_ioctl(fd, &userptr);
+ igt_assert_eq(ret, 0);
+
+ shared_buf_handle = userptr.handle;
+
+ /* create command buffer with write command */
+ len = gem_store_data_svm(fd, batch_buffer, pinning_offset, 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, pinning_offset);
+ 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;
+
+ setup_execbuffer(&execbuf, exec_object2, ring, NUM_EXEC_OBJECTS, len);
+ submit_and_sync(fd, &execbuf, 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
+
+ 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
+*/
+
+static void gem_pin_high_address_test(void)
+{
+ i915_gem_userptr userptr;
+ 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;
+ uint64_t high_address = 0x1111FFFF000; /* 44 bit 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 = create_mem_buffer(BO_SIZE);
+ *shared_buffer = 0xFFFFFFFF;
+
+ /* share with GPU */
+ gem_create_userptr_struct(&userptr, shared_buffer, BO_SIZE, false);
+ ret = gem_call_userptr_ioctl(fd, &userptr);
+ igt_assert_eq(ret, 0);
+
+ /* Get handle for shared buffer */
+ shared_buf_handle = userptr.handle;
+
+ /* 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;
+
+ setup_execbuffer(&execbuf, exec_object2, ring, NUM_EXEC_OBJECTS, len);
+ submit_and_sync(fd, &execbuf, 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
+
+ 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
+*/
+#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;
+ 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;
+ uint64_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 = create_mem_buffer(BO_SIZE);
+ *shared_buffer = 0xFFFFFFFF;
+
+ /* share with GPU */
+ gem_create_userptr_struct(&userptr, shared_buffer, BO_SIZE, false);
+ ret = gem_call_userptr_ioctl(fd, &userptr);
+ igt_assert_eq(ret, 0);
+
+ /* Get handle for shared buffer */
+ shared_buf_handle = userptr.handle;
+
+ for (high_address = BEGIN_HIGH_ADDRESS; high_address <= END_HIGH_ADDRESS;
+ high_address+=ADDRESS_INCREMENT){
+ /* 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;
+
+
+ setup_execbuffer(&execbuf, exec_object2, ring, NUM_EXEC_OBJECTS, len);
+ submit_and_sync(fd, &execbuf, 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, 0x%"PRIx64"", data, shared_buffer[0], high_address);
+ }
+
+ 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.
+
+ 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 pinning_offset VMA
+ Submit execbuffer
+ Verify value of first DWORD in shared buffer matches DATA
+*/
+void gem_pin_mmap_anonymous_test(void)
+{
+ i915_gem_userptr userptr;
+ 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;
+ uint64_t pinning_offset = 0x1000;
+
+ 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 */
+ gem_create_userptr_struct(&userptr, shared_buffer, BO_SIZE, false);
+ ret = gem_call_userptr_ioctl(fd, &userptr);
+ igt_assert_eq(ret, 0);
+
+ /* Get handle for shared buffer */
+ shared_buf_handle = userptr.handle;
+
+ /* create command buffer with write command */
+ len = gem_store_data_svm(fd, batch_buffer, pinning_offset, 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, pinning_offset);
+ 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;
+
+ setup_execbuffer(&execbuf, exec_object2, ring, NUM_EXEC_OBJECTS, len);
+ submit_and_sync(fd, &execbuf, 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
+
+ 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 pinning_offest VMA
+ Submit execbuffer
+ Verify value of first DWORD in shared buffer matches DATA
+ Close file
+*/
+void gem_pin_mmap_file_test(void)
+{
+ i915_gem_userptr userptr;
+ 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[] = "/tmp/svm_mmap.txt";
+ uint64_t pinning_offset = 0x1000;
+
+ 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;
+
+ gem_create_userptr_struct(&userptr, dest, BO_SIZE, false);
+ ret = gem_call_userptr_ioctl(fd, &userptr);
+ igt_assert_eq(ret, 0);
+ dest_buf_handle = userptr.handle;
+
+ /* create command buffer with write command */
+ len = gem_store_data_svm(fd, batch_buffer, pinning_offset, 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, pinning_offset);
+ 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;
+
+ setup_execbuffer(&execbuf, exec_object2, ring, NUM_EXEC_OBJECTS, len);
+ submit_and_sync(fd, &execbuf, 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_basic"){
+ gem_basic_test();
+ }
+ 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_softpin_stress"){
+ gem_softpin_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();
+ }
+ 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();
+ }
+ igt_subtest("gem_pin_mmap_anon"){
+ gem_pin_mmap_anonymous_test();
+ }
+ igt_subtest("gem_pin_mmap_file"){
+ gem_pin_mmap_file_test();
+ }
+
+ igt_exit();
+}
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. v2: Added cc and signed-off-by fields v3: Fixed review comments, added helper functions. Removed userptr error scenarios covered by existing userptr tests. Renamed to gem_softpin.c. Modified stress test to have 100K buffers, it now runs for ~30 mins, checks every element has been written to correctly, and pins buffers at different VMAs. Cc: Michel Thierry <michel.thierry@intel.com> Signed-off-by: Vinay Belgaumkar <vinay.belgaumkar@intel.com> --- tests/.gitignore | 1 + tests/Makefile.sources | 1 + tests/gem_softpin.c | 1252 ++++++++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 1254 insertions(+) create mode 100644 tests/gem_softpin.c