From patchwork Thu Nov 19 22:29:03 2015 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 8bit X-Patchwork-Submitter: Vinay Belgaumkar X-Patchwork-Id: 7664641 Return-Path: X-Original-To: patchwork-intel-gfx@patchwork.kernel.org Delivered-To: patchwork-parsemail@patchwork2.web.kernel.org Received: from mail.kernel.org (mail.kernel.org [198.145.29.136]) by patchwork2.web.kernel.org (Postfix) with ESMTP id 8A7A7BF90C for ; Fri, 20 Nov 2015 06:29:52 +0000 (UTC) Received: from mail.kernel.org (localhost [127.0.0.1]) by mail.kernel.org (Postfix) with ESMTP id A29AF20416 for ; Fri, 20 Nov 2015 06:29:49 +0000 (UTC) Received: from gabe.freedesktop.org (gabe.freedesktop.org [131.252.210.177]) by mail.kernel.org (Postfix) with ESMTP id A96D520462 for ; Fri, 20 Nov 2015 06:29:46 +0000 (UTC) Received: from gabe.freedesktop.org (localhost [127.0.0.1]) by gabe.freedesktop.org (Postfix) with ESMTP id A8BA16E5FB; Thu, 19 Nov 2015 22:29:45 -0800 (PST) X-Original-To: intel-gfx@lists.freedesktop.org Delivered-To: intel-gfx@lists.freedesktop.org Received: from mga03.intel.com (mga03.intel.com [134.134.136.65]) by gabe.freedesktop.org (Postfix) with ESMTP id CA3436E5FB for ; Thu, 19 Nov 2015 22:29:44 -0800 (PST) Received: from fmsmga001.fm.intel.com ([10.253.24.23]) by orsmga103.jf.intel.com with ESMTP; 19 Nov 2015 22:29:44 -0800 X-ExtLoop1: 1 X-IronPort-AV: E=Sophos;i="5.20,321,1444719600"; d="scan'208";a="843067095" Received: from vinaysb-desktop.fm.intel.com ([10.19.82.43]) by fmsmga001.fm.intel.com with ESMTP; 19 Nov 2015 22:29:44 -0800 From: Vinay Belgaumkar To: intel-gfx@lists.freedesktop.org Date: Thu, 19 Nov 2015 14:29:03 -0800 Message-Id: <1447972143-27118-1-git-send-email-vinay.belgaumkar@intel.com> X-Mailer: git-send-email 1.9.1 MIME-Version: 1.0 Cc: Vinay Belgaumkar Subject: [Intel-gfx] [PATCH i-g-t] tests/gem_softpin: New tests for softpin feature X-BeenThere: intel-gfx@lists.freedesktop.org X-Mailman-Version: 2.1.18 Precedence: list List-Id: Intel graphics driver community testing & development List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Errors-To: intel-gfx-bounces@lists.freedesktop.org Sender: "Intel-gfx" X-Spam-Status: No, score=-3.2 required=5.0 tests=BAYES_00, DATE_IN_PAST_06_12, RCVD_IN_DNSWL_MED, RP_MATCHES_RCVD, UNPARSEABLE_RELAY autolearn=unavailable version=3.3.1 X-Spam-Checker-Version: SpamAssassin 3.3.1 (2010-03-16) on mail.kernel.org X-Virus-Scanned: ClamAV using ClamSMTP 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. 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. v4: Changed name to gem_softpin Cc: Michel Thierry Signed-off-by: Vinay Belgaumkar --- tests/.gitignore | 1 + tests/Makefile.sources | 1 + tests/gem_softpin.c | 1252 ++++++++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 1254 insertions(+) create mode 100644 tests/gem_softpin.c diff --git a/tests/.gitignore b/tests/.gitignore index 80af9a7..424870b 100644 --- a/tests/.gitignore +++ b/tests/.gitignore @@ -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 diff --git a/tests/Makefile.sources b/tests/Makefile.sources index 8fb2de8..2008d4a 100644 --- a/tests/Makefile.sources +++ b/tests/Makefile.sources @@ -11,6 +11,7 @@ TESTS_progs_M = \ drv_hangman \ gem_bad_reloc \ gem_basic \ + gem_softpin \ gem_caching \ gem_close_race \ gem_concurrent_blit \ diff --git a/tests/gem_softpin.c b/tests/gem_softpin.c new file mode 100644 index 0000000..aed607c --- /dev/null +++ b/tests/gem_softpin.c @@ -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 + Thomas Daniel + * + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "drm.h" +#include "ioctl_wrappers.h" +#include "drmtest.h" +#include "intel_chipset.h" +#include "intel_io.h" +#include "i915_drm.h" +#include +#include +#include +#include +#include "igt_kms.h" +#include +#include +#include + +#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(); +}