[igt-dev] [RFC i-g-t 3/7] tests/i915/svm: Add basic SVM RT allocator test support
Niranjana Vishwanathapura
niranjana.vishwanathapura at intel.com
Fri Dec 13 21:54:25 UTC 2019
Add basic tests for Shared Virtual Memory (SVM) runtime (RT) allocator
functionality. Explicitly bind the buffer objects in device page table
using a shared virtual address and have GPU copy the data from a source
buffer object to destination buffer object. Softpin the batch buffer.
Test for different buffer sizes, allocation method and with multiple
contexts.
Cc: Joonas Lahtinen <joonas.lahtinen at linux.intel.com>
Cc: Jon Bloomfield <jon.bloomfield at intel.com>
Cc: Daniel Vetter <daniel.vetter at intel.com>
Cc: Sudeep Dutt <sudeep.dutt at intel.com>
Signed-off-by: Niranjana Vishwanathapura <niranjana.vishwanathapura at intel.com>
---
tests/Makefile.sources | 3 +
tests/i915/i915_svm_basic.c | 447 ++++++++++++++++++++++++++++++++++++
tests/meson.build | 1 +
3 files changed, 451 insertions(+)
create mode 100644 tests/i915/i915_svm_basic.c
diff --git a/tests/Makefile.sources b/tests/Makefile.sources
index 806eb02d..40f05605 100644
--- a/tests/Makefile.sources
+++ b/tests/Makefile.sources
@@ -478,6 +478,9 @@ gem_workarounds_SOURCES = i915/gem_workarounds.c
TESTS_progs += gem_write_read_ring_switch
gem_write_read_ring_switch_SOURCES = i915/gem_write_read_ring_switch.c
+TESTS_progs += i915_svm_basic
+i915_svm_basic_SOURCES = i915/i915_svm_basic.c
+
TESTS_progs += gen3_mixed_blits
gen3_mixed_blits_SOURCES = i915/gen3_mixed_blits.c
diff --git a/tests/i915/i915_svm_basic.c b/tests/i915/i915_svm_basic.c
new file mode 100644
index 00000000..66949039
--- /dev/null
+++ b/tests/i915/i915_svm_basic.c
@@ -0,0 +1,447 @@
+// SPDX-License-Identifier: MIT
+/*
+ * Copyright(c) 2019 Intel Corporation. All rights reserved.
+ */
+
+/** @file i915_svm_basic.c
+ *
+ * This is the basic test for Shared Virtual Memory (SVM) functionality.
+ *
+ * The goal is to simply ensure that basics work.
+ * This test in part is derived from gem_exec_blt.c
+ */
+
+#include "igt.h"
+#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 "drm.h"
+#include "i915/gem_vm.h"
+
+#define PAGE_SIZE 4096
+#define PAGE_SHIFT 12
+
+#define COPY_BLT_CMD (2<<29|0x53<<22|0x6)
+#define BLT_WRITE_ALPHA (1<<21)
+#define BLT_WRITE_RGB (1<<20)
+
+#define LOCAL_I915_EXEC_NO_RELOC (1<<11)
+#define LOCAL_I915_EXEC_HANDLE_LUT (1<<12)
+
+#define DEFAULT_BUFF_SIZE (4 * PAGE_SIZE)
+#define BATCH_VA_STRIDE PAGE_SIZE
+
+#define MAX_CTXTS 4
+
+#define svm_info igt_info
+#define svm_debug igt_debug
+
+/* gen8_canonical_addr
+ * Used to convert any address into canonical form, i.e. [63:48] == [47].
+ * Based on kernel's sign_extend64 implementation.
+ * @address - a virtual address
+ */
+#define GEN8_HIGH_ADDRESS_BIT 47
+static uint64_t gen8_canonical_addr(uint64_t address)
+{
+ __u8 shift = 63 - GEN8_HIGH_ADDRESS_BIT;
+ return (__s64)(address << shift) >> shift;
+}
+
+static inline uint32_t lower_32_bits(uint64_t x)
+{
+ return x & 0xffffffff;
+}
+
+static inline uint32_t upper_32_bits(uint64_t x)
+{
+ return x >> 32;
+}
+
+static void print_buffer(void *buf, uint32_t size,
+ const char *str, bool full)
+{
+ uint32_t i = 0;
+
+ svm_debug("Printing %s 0x%lx size 0x%x\n", str, (uint64_t)buf, size);
+ while (i < size) {
+ uint32_t *b = buf + i;
+
+ svm_debug("\t%s[0x%04x]: 0x%08x 0x%08x 0x%08x 0x%08x %s\n",
+ str, i, b[0], b[1], b[2], b[3], full ? "" : "...");
+ i += full ? 16 : PAGE_SIZE;
+ }
+}
+
+static void print_object(int fd, uint32_t handle, uint32_t size,
+ const char *str, bool full)
+{
+ void *buf;
+
+ buf = malloc(size);
+ gem_read(fd, handle, 0, buf, size);
+ print_buffer(buf, size, str, full);
+ free(buf);
+}
+
+static int objcmp(int fd, uint32_t src, uint32_t dst, uint32_t size)
+{
+ void *buf_src, *buf_dst;
+ int ret;
+
+ buf_src = malloc(size);
+ buf_dst = malloc(size);
+ gem_read(fd, src, 0, buf_src, size);
+ gem_read(fd, dst, 0, buf_dst, size);
+ ret = memcmp(buf_src, buf_dst, size);
+ free(buf_src);
+ free(buf_dst);
+ return ret;
+}
+
+static int gem_linear_blt(int devid, uint32_t *batch, void *src,
+ void *dst, uint32_t length)
+{
+ uint32_t *b = batch;
+ int height = length / (16 * 1024);
+ uint64_t src_va = (uint64_t)src;
+ uint64_t dst_va = (uint64_t)dst;
+
+ igt_assert_lte(height, 1 << 16);
+
+ if (height) {
+ int i = 0;
+ b[i++] = COPY_BLT_CMD | BLT_WRITE_ALPHA | BLT_WRITE_RGB;
+ if (intel_gen(devid) >= 8)
+ b[i-1]+=2;
+ b[i++] = 0xcc << 16 | 1 << 25 | 1 << 24 | (16*1024);
+ b[i++] = 0;
+ b[i++] = height << 16 | (4*1024);
+ b[i++] = lower_32_bits(dst_va);
+ if (intel_gen(devid) >= 8)
+ b[i++] = upper_32_bits(dst_va);
+
+ b[i++] = 0;
+ b[i++] = 16*1024;
+ b[i++] = lower_32_bits(src_va);
+ if (intel_gen(devid) >= 8)
+ b[i++] = upper_32_bits(src_va);
+
+ b += i;
+ length -= height * 16*1024;
+ }
+
+ if (length) {
+ int i = 0;
+ b[i++] = COPY_BLT_CMD | BLT_WRITE_ALPHA | BLT_WRITE_RGB;
+ if (intel_gen(devid) >= 8)
+ b[i-1]+=2;
+ b[i++] = 0xcc << 16 | 1 << 25 | 1 << 24 | (16*1024);
+ b[i++] = height << 16;
+ b[i++] = (1+height) << 16 | (length / 4);
+ b[i++] = lower_32_bits(dst_va);
+ if (intel_gen(devid) >= 8)
+ b[i++] = upper_32_bits(dst_va);
+
+ b[i++] = height << 16;
+ b[i++] = 16*1024;
+ b[i++] = lower_32_bits(src_va);
+ if (intel_gen(devid) >= 8)
+ b[i++] = upper_32_bits(src_va);
+
+ b += i;
+ }
+
+ b[0] = MI_BATCH_BUFFER_END;
+ b[1] = 0;
+
+ return (b + 2 - batch) * sizeof(uint32_t);
+}
+
+/* Softpin batch buffer in non-conflicting higher address space */
+static uint64_t get_batch_va(int fd)
+{
+ struct drm_i915_gem_context_param arg = { 0 };
+
+ arg.param = I915_CONTEXT_PARAM_GTT_SIZE;
+ gem_context_get_param(fd, &arg);
+ return gen8_canonical_addr(arg.value >> 1);
+}
+
+static void __gem_copy(int fd, uint32_t src_obj, uint32_t dst_obj,
+ void *src, void *dst, uint32_t offset, uint32_t size,
+ uint32_t ctx_id, uint32_t handle, uint64_t batch_va)
+{
+ struct drm_i915_gem_exec_object2 exec[3] = { 0 };
+ struct drm_i915_gem_execbuffer2 execbuf;
+ uint32_t buf[20], len, i = 0, ring = 0;
+ int devid = intel_get_drm_devid(fd);
+
+ len = gem_linear_blt(devid, buf, src + offset, dst + offset, size);
+
+ gem_write(fd, handle, 0, buf, len);
+ print_buffer(buf, len, "batch", true);
+ if (src_obj) {
+ exec[i].handle = src_obj;
+ exec[i].offset = (uint64_t)src;
+ exec[i++].flags = EXEC_OBJECT_PINNED |
+ EXEC_OBJECT_SUPPORTS_48B_ADDRESS;
+ }
+ if (dst_obj) {
+ exec[i].handle = dst_obj;
+ exec[i].offset = (uint64_t)dst;
+ exec[i++].flags = EXEC_OBJECT_PINNED |
+ EXEC_OBJECT_SUPPORTS_48B_ADDRESS;
+ }
+ exec[i].handle = handle;
+ exec[i].offset = batch_va;
+ exec[i++].flags = EXEC_OBJECT_PINNED | EXEC_OBJECT_SUPPORTS_48B_ADDRESS;
+ if (HAS_BLT_RING(devid))
+ ring = I915_EXEC_BLT;
+
+ memset(&execbuf, 0, sizeof(execbuf));
+ execbuf.buffers_ptr = to_user_pointer(&exec);
+ execbuf.buffer_count = i;
+ execbuf.batch_len = len;
+ execbuf.flags = ring;
+ execbuf.flags |= LOCAL_I915_EXEC_HANDLE_LUT;
+ execbuf.flags |= LOCAL_I915_EXEC_NO_RELOC;
+ i915_execbuffer2_set_context_id(execbuf, ctx_id);
+ gem_execbuf(fd, &execbuf);
+}
+
+static void gem_copy(int fd, uint32_t src_obj, uint32_t dst_obj,
+ void *src, void *dst, uint32_t size,
+ uint32_t *ctx_id, int num_ctxts)
+{
+ uint32_t i, handle[MAX_CTXTS], rem = size;
+ uint32_t delta, npages = size / PAGE_SIZE;
+ uint64_t batch_va = get_batch_va(fd);
+
+ delta = (npages / num_ctxts) * PAGE_SIZE;
+ delta += (npages % num_ctxts) ? PAGE_SIZE : 0;
+ for (i = 0; i < num_ctxts; i++) {
+ handle[i] = gem_create(fd, PAGE_SIZE);
+ svm_info("Issuing gem copy on ctx 0x%x\n", ctx_id[i]);
+ __gem_copy(fd, src_obj, dst_obj, src, dst, (i * delta),
+ min(rem, delta), ctx_id[i], handle[i], batch_va);
+ rem -= delta;
+ batch_va += BATCH_VA_STRIDE;
+ }
+
+ for (i = 0; i < num_ctxts; i++) {
+ gem_sync(fd, handle[i]);
+ svm_info("gem copy completed on ctx 0x%x\n", ctx_id[i]);
+ gem_close(fd, handle[i]);
+ }
+}
+
+static void run_rt(int fd, uint32_t size, bool migrate, bool copy,
+ bool bind, bool unbind, int32_t num_ctxts)
+{
+ uint32_t i, npages = size / PAGE_SIZE;
+ uint32_t shared_vm_id, vm_id[MAX_CTXTS];
+ uint32_t ctx_id[MAX_CTXTS];
+ uint64_t src_va, dst_va;
+ uint32_t src, dst;
+ bool share_vm;
+ void *buf;
+
+ /* Fix parmeters; -ve num_ctxts means all contexts share the vm */
+ num_ctxts = num_ctxts ? : 1;
+ share_vm = num_ctxts < 0;
+ if (num_ctxts < 0)
+ num_ctxts = -num_ctxts;
+
+ /* For shared VM, we need to bind,unbind,en/disable SVM only once */
+ if (share_vm)
+ shared_vm_id = gem_vm_create(fd);
+
+ /* Create contexts and enable svm */
+ num_ctxts = min(MAX_CTXTS, num_ctxts);
+ for (i = 0; i < num_ctxts; i++) {
+ ctx_id[i] = gem_context_create(fd);
+ if (share_vm) {
+ vm_id[i] = shared_vm_id;
+ gem_ctx_set_vm(fd, ctx_id[i], vm_id[i]);
+ } else {
+ vm_id[i] = gem_ctx_get_vm(fd, ctx_id[i]);
+ }
+ }
+ for (i = 0; i < num_ctxts; i++) {
+ gem_vm_enable_svm(fd, vm_id[i]);
+ if (share_vm)
+ break;
+ }
+
+ /* Create objects */
+ src = gem_create(fd, size);
+ dst = gem_create(fd, size);
+
+ /* Static assignment */
+ src_va = 0xa000000;
+ dst_va = 0xb000000;
+
+ /* Allocate buffer and fill pattern */
+ buf = malloc(size);
+ for (i = 0; i < npages; i++)
+ memset(buf + i * PAGE_SIZE, i + 1, PAGE_SIZE);
+ gem_write(fd, src, 0, buf, size);
+ print_buffer(buf, size, "src_obj", false);
+ free(buf);
+
+ if (migrate) {
+ svm_info("Migrating obj 0x%x to smem region\n", src);
+ gem_migrate_to_smem(fd, src);
+
+ svm_info("Migrating obj 0x%x to smem region\n", dst);
+ gem_migrate_to_smem(fd, dst);
+ }
+
+ /* Bind the buffers to device page table */
+ /* XXX: Test READ_ONLY bindings */
+ for (i = 0; bind && (i < num_ctxts); i++) {
+ svm_info("Binding obj 0x%x at 0x%lx size 0x%x vm 0x%x\n",
+ src, src_va, size, vm_id[i]);
+ gem_svm_bind(fd, src_va, src, vm_id[i], false);
+
+ svm_info("Binding obj 0x%x at 0x%lx size 0x%x vm 0x%x\n",
+ dst, dst_va, size, vm_id[i]);
+ gem_svm_bind(fd, dst_va, dst, vm_id[i], false);
+
+ if (share_vm)
+ break;
+ }
+
+ /* Have GPU do the copy */
+ if (copy) {
+ if (bind)
+ gem_copy(fd, 0, 0, (void *)src_va, (void *)dst_va,
+ size, ctx_id, num_ctxts);
+ else
+ gem_copy(fd, src, dst, (void *)src_va, (void *)dst_va,
+ size, ctx_id, num_ctxts);
+ }
+
+ /*
+ * Unbind buffers from device page table.
+ * If not, it should get unbound while freeing the buffer.
+ */
+ for (i = 0; unbind && (i < num_ctxts); i++) {
+ svm_info("Unbinding obj 0x%x vm 0x%x\n", src, vm_id[i]);
+ gem_svm_unbind(fd, src, vm_id[i]);
+
+ svm_info("Unbinding obj 0x%x vm 0x%x\n", dst, vm_id[i]);
+ gem_svm_unbind(fd, dst, vm_id[i]);
+
+ if (share_vm)
+ break;
+ }
+
+ if (migrate) {
+ svm_info("Migrating obj 0x%x to lmem region\n", src);
+ gem_migrate_to_lmem(fd, src);
+
+ svm_info("Migrating obj 0x%x to lmem region\n", dst);
+ gem_migrate_to_lmem(fd, dst);
+ }
+
+ print_object(fd, dst, size, "dst_obj", false);
+
+ /* Validate */
+ if (copy)
+ igt_assert(objcmp(fd, src, dst, size) == 0);
+
+ /* Free the objects */
+ svm_debug("Closing object 0x%x\n", src);
+ gem_close(fd, src);
+ svm_debug("Closing object 0x%x\n", dst);
+ gem_close(fd, dst);
+
+ sleep(2); /* Wait for handles to get freed */
+
+ /* Done with the contexts */
+ for (i = 0; i < num_ctxts; i++) {
+ gem_vm_disable_svm(fd, vm_id[i]);
+ if (share_vm)
+ break;
+ }
+ for (i = 0; i < num_ctxts; i++) {
+ svm_debug("Destroying context 0x%x\n", ctx_id[i]);
+ gem_context_destroy(fd, ctx_id[i]);
+ }
+
+ if (share_vm)
+ gem_vm_destroy(fd, shared_vm_id);
+}
+
+igt_main
+{
+ struct {
+ const char *name;
+ uint32_t size;
+ bool migrate;
+ bool copy;
+ bool bind;
+ bool unbind;
+ int32_t num_ctxts;
+ } *r, rt_tests[] = {
+ /* Basic runtime allocator test */
+ {"rt_basic", 0, false, true, true, true, 1},
+
+ /* Skip GPU copy */
+ {"rt_no_gpu_copy", 0, false, false, true, true, 1},
+
+ /* Skip unbinding */
+ {"rt_no_unbind", 0, false, true, true, false, 1},
+
+ /* Skip explicit binding and bind in the execbuf path */
+ {"rt_no_bind", 0, false, true, false, true, 1},
+
+ /* Use multiple contexts */
+ {"rt_multi_ctxts", 0, false, true, true, true, 2},
+
+ /* Use multiple contexts and share vm (-ve num_ctxts) */
+ {"rt_multi_ctxts_share_vm", 0, false, true, true, true, -2},
+
+ /* Use 64K buffers */
+ {"rt_64K", (16 * PAGE_SIZE), false, true, true, true, 1},
+
+ /* Use 2M buffers */
+ {"rt_2M", (512 * PAGE_SIZE), false, true, true, true, 1},
+ };
+ int fd, idx;
+ uint32_t def_size;
+ bool has_lmem;
+
+ igt_fixture {
+ fd = drm_open_driver(DRIVER_INTEL);
+ igt_require_gem(fd);
+ igt_require_svm(fd);
+ has_lmem = gem_has_lmem(fd);
+ def_size = DEFAULT_BUFF_SIZE;
+ }
+
+ /* Below are runtime (rt) allocator tests */
+ for (idx = 0, r = rt_tests; idx < ARRAY_SIZE(rt_tests); idx++, r++) {
+ bool migrate = has_lmem ? r->migrate : false;
+ uint32_t size = r->size ? : def_size;
+
+ igt_subtest_f("%s",r->name)
+ run_rt(fd, size, migrate, r->copy, r->bind, r->unbind,
+ r->num_ctxts);
+ }
+
+ igt_fixture {
+ close(fd);
+ }
+
+ igt_exit();
+}
diff --git a/tests/meson.build b/tests/meson.build
index 570de545..87021902 100644
--- a/tests/meson.build
+++ b/tests/meson.build
@@ -226,6 +226,7 @@ i915_progs = [
'gem_wait',
'gem_workarounds',
'gem_write_read_ring_switch',
+ 'i915_svm_basic',
'i915_fb_tiling',
'i915_getparams_basic',
'i915_hangman',
--
2.21.0.rc0.32.g243a4c7e27
More information about the igt-dev
mailing list