Exercise basic creation and swapping between new address spaces. v2: Check isolation that the same vm_id on different fd are indeed different VM. v3: Cross-over check with CREATE_EXT_SETPARAM Signed-off-by: Chris Wilson <chris@xxxxxxxxxxxxxxxxxx> Cc: Tvrtko Ursulin <tvrtko.ursulin@xxxxxxxxx> Reviewed-by: Tvrtko Ursulin <tvrtko.ursulin@xxxxxxxxx> --- lib/Makefile.sources | 2 + lib/i915/gem_vm.c | 130 ++++++++++++ lib/i915/gem_vm.h | 38 ++++ lib/meson.build | 1 + tests/Makefile.sources | 1 + tests/i915/gem_vm_create.c | 412 +++++++++++++++++++++++++++++++++++++ tests/meson.build | 1 + 7 files changed, 585 insertions(+) create mode 100644 lib/i915/gem_vm.c create mode 100644 lib/i915/gem_vm.h create mode 100644 tests/i915/gem_vm_create.c diff --git a/lib/Makefile.sources b/lib/Makefile.sources index 976858238..891f65b96 100644 --- a/lib/Makefile.sources +++ b/lib/Makefile.sources @@ -13,6 +13,8 @@ lib_source_list = \ i915/gem_ring.c \ i915/gem_mman.c \ i915/gem_mman.h \ + i915/gem_vm.c \ + i915/gem_vm.h \ i915_3d.h \ i915_reg.h \ i915_pciids.h \ diff --git a/lib/i915/gem_vm.c b/lib/i915/gem_vm.c new file mode 100644 index 000000000..9a022a56c --- /dev/null +++ b/lib/i915/gem_vm.c @@ -0,0 +1,130 @@ +/* + * Copyright © 2019 Intel Corporation + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice (including the next + * paragraph) shall be included in all copies or substantial portions of the + * Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS + * IN THE SOFTWARE. + */ + +#include <errno.h> +#include <string.h> + +#include "ioctl_wrappers.h" +#include "drmtest.h" + +#include "i915/gem_vm.h" + +/** + * SECTION:gem_vm + * @short_description: Helpers for dealing with address spaces (vm/GTT) + * @title: GEM Virtual Memory + * + * This helper library contains functions used for handling gem address + * spaces. + */ + +/** + * gem_has_vm: + * @i915: open i915 drm file descriptor + * + * Returns: whether VM creation is supported or not. + */ +bool gem_has_vm(int i915) +{ + uint32_t vm_id = 0; + + __gem_vm_create(i915, &vm_id); + if (vm_id) + gem_vm_destroy(i915, vm_id); + + return vm_id; +} + +/** + * gem_require_vm: + * @i915: open i915 drm file descriptor + * + * This helper will automatically skip the test on platforms where address + * space creation is not available. + */ +void gem_require_vm(int i915) +{ + igt_require(gem_has_vm(i915)); +} + +int __gem_vm_create(int i915, uint32_t *vm_id) +{ + struct drm_i915_gem_vm_control ctl = {}; + int err = 0; + + if (igt_ioctl(i915, DRM_IOCTL_I915_GEM_VM_CREATE, &ctl) == 0) { + *vm_id = ctl.vm_id; + } else { + err = -errno; + igt_assume(err != 0); + } + + errno = 0; + return err; +} + +/** + * gem_vm_create: + * @i915: open i915 drm file descriptor + * + * This wraps the VM_CREATE ioctl, which is used to allocate a new + * address space for use with GEM contexts. + * + * Returns: The id of the allocated address space. + */ +uint32_t gem_vm_create(int i915) +{ + uint32_t vm_id; + + igt_assert_eq(__gem_vm_create(i915, &vm_id), 0); + igt_assert(vm_id != 0); + + return vm_id; +} + +int __gem_vm_destroy(int i915, uint32_t vm_id) +{ + struct drm_i915_gem_vm_control ctl = { .vm_id = vm_id }; + int err = 0; + + if (igt_ioctl(i915, DRM_IOCTL_I915_GEM_VM_DESTROY, &ctl)) { + err = -errno; + igt_assume(err); + } + + errno = 0; + return err; +} + +/** + * gem_vm_destroy: + * @i915: open i915 drm file descriptor + * @vm_id: i915 VM id + * + * This wraps the VM_DESTROY ioctl, which is used to free an address space + * handle. + */ +void gem_vm_destroy(int i915, uint32_t vm_id) +{ + igt_assert_eq(__gem_vm_destroy(i915, vm_id), 0); +} diff --git a/lib/i915/gem_vm.h b/lib/i915/gem_vm.h new file mode 100644 index 000000000..27af899d4 --- /dev/null +++ b/lib/i915/gem_vm.h @@ -0,0 +1,38 @@ +/* + * Copyright © 2019 Intel Corporation + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice (including the next + * paragraph) shall be included in all copies or substantial portions of the + * Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS + * IN THE SOFTWARE. + */ + +#ifndef GEM_VM_H +#define GEM_VM_H + +#include <stdint.h> + +bool gem_has_vm(int i915); +void gem_require_vm(int i915); + +uint32_t gem_vm_create(int i915); +int __gem_vm_create(int i915, uint32_t *vm_id); + +void gem_vm_destroy(int i915, uint32_t vm_id); +int __gem_vm_destroy(int i915, uint32_t vm_id); + +#endif /* GEM_VM_H */ diff --git a/lib/meson.build b/lib/meson.build index 807368686..2989e11b3 100644 --- a/lib/meson.build +++ b/lib/meson.build @@ -5,6 +5,7 @@ lib_sources = [ 'i915/gem_submission.c', 'i915/gem_ring.c', 'i915/gem_mman.c', + 'i915/gem_vm.c', 'igt_color_encoding.c', 'igt_debugfs.c', 'igt_device.c', diff --git a/tests/Makefile.sources b/tests/Makefile.sources index 2d5c929e3..cbd4b5289 100644 --- a/tests/Makefile.sources +++ b/tests/Makefile.sources @@ -21,6 +21,7 @@ TESTS_progs = \ drm_import_export \ drm_mm \ drm_read \ + i915/gem_vm_create \ kms_3d \ kms_addfb_basic \ kms_atomic \ diff --git a/tests/i915/gem_vm_create.c b/tests/i915/gem_vm_create.c new file mode 100644 index 000000000..cbd273d9d --- /dev/null +++ b/tests/i915/gem_vm_create.c @@ -0,0 +1,412 @@ +/* + * Copyright © 2019 Intel Corporation + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice (including the next + * paragraph) shall be included in all copies or substantial portions of the + * Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS + * IN THE SOFTWARE. + */ + +#include "igt.h" +#include "igt_dummyload.h" +#include "i915/gem_vm.h" + +static int vm_create_ioctl(int i915, struct drm_i915_gem_vm_control *ctl) +{ + int err = 0; + if (igt_ioctl(i915, DRM_IOCTL_I915_GEM_VM_CREATE, ctl)) { + err = -errno; + igt_assume(err); + } + errno = 0; + return err; +} + +static int vm_destroy_ioctl(int i915, struct drm_i915_gem_vm_control *ctl) +{ + int err = 0; + if (igt_ioctl(i915, DRM_IOCTL_I915_GEM_VM_DESTROY, ctl)) { + err = -errno; + igt_assume(err); + } + errno = 0; + return err; +} + +static int ctx_create_ioctl(int i915, + struct drm_i915_gem_context_create_ext *arg) +{ + int err = 0; + if (igt_ioctl(i915, DRM_IOCTL_I915_GEM_CONTEXT_CREATE_EXT, arg)) { + err = -errno; + igt_assume(err); + } + errno = 0; + return err; +} + +static bool has_vm(int i915) +{ + struct drm_i915_gem_vm_control ctl = {}; + int err; + + err = vm_create_ioctl(i915, &ctl); + switch (err) { + case -EINVAL: /* unknown ioctl */ + case -ENODEV: /* !full-ppgtt */ + return false; + + case 0: + gem_vm_destroy(i915, ctl.vm_id); + return true; + + default: + igt_fail_on_f(err, "Unknown response from VM_CREATE\n"); + return false; + } +} + +static void invalid_create(int i915) +{ + struct drm_i915_gem_vm_control ctl = {}; + struct i915_user_extension ext = { .name = -1 }; + + igt_assert_eq(vm_create_ioctl(i915, &ctl), 0); + gem_vm_destroy(i915, ctl.vm_id); + + ctl.vm_id = 0xdeadbeef; + igt_assert_eq(vm_create_ioctl(i915, &ctl), 0); + gem_vm_destroy(i915, ctl.vm_id); + ctl.vm_id = 0; + + ctl.flags = -1; + igt_assert_eq(vm_create_ioctl(i915, &ctl), -EINVAL); + ctl.flags = 0; + + ctl.extensions = -1; + igt_assert_eq(vm_create_ioctl(i915, &ctl), -EFAULT); + ctl.extensions = to_user_pointer(&ext); + igt_assert_eq(vm_create_ioctl(i915, &ctl), -EINVAL); + ctl.extensions = 0; +} + +static void invalid_destroy(int i915) +{ + struct drm_i915_gem_vm_control ctl = {}; + + igt_assert_eq(vm_destroy_ioctl(i915, &ctl), -ENOENT); + + igt_assert_eq(vm_create_ioctl(i915, &ctl), 0); + igt_assert_eq(vm_destroy_ioctl(i915, &ctl), 0); + igt_assert_eq(vm_destroy_ioctl(i915, &ctl), -ENOENT); + + igt_assert_eq(vm_create_ioctl(i915, &ctl), 0); + ctl.vm_id = ctl.vm_id + 1; /* assumes no one else allocated */ + igt_assert_eq(vm_destroy_ioctl(i915, &ctl), -ENOENT); + ctl.vm_id = ctl.vm_id - 1; + igt_assert_eq(vm_destroy_ioctl(i915, &ctl), 0); + + igt_assert_eq(vm_create_ioctl(i915, &ctl), 0); + ctl.flags = -1; + igt_assert_eq(vm_destroy_ioctl(i915, &ctl), -EINVAL); + ctl.flags = 0; + igt_assert_eq(vm_destroy_ioctl(i915, &ctl), 0); + + igt_assert_eq(vm_create_ioctl(i915, &ctl), 0); + ctl.extensions = -1; + igt_assert_eq(vm_destroy_ioctl(i915, &ctl), -EINVAL); + ctl.extensions = 0; + igt_assert_eq(vm_destroy_ioctl(i915, &ctl), 0); +} + +static uint32_t __batch_create(int i915, uint32_t offset) +{ + const uint32_t bbe = MI_BATCH_BUFFER_END; + uint32_t handle; + + handle = gem_create(i915, ALIGN(offset + 4, 4096)); + gem_write(i915, handle, offset, &bbe, sizeof(bbe)); + + return handle; +} + +static uint32_t batch_create(int i915) +{ + return __batch_create(i915, 0); +} + +static void check_same_vm(int i915, uint32_t ctx_a, uint32_t ctx_b) +{ + struct drm_i915_gem_exec_object2 batch = { + .handle = batch_create(i915), + }; + struct drm_i915_gem_execbuffer2 eb = { + .buffers_ptr = to_user_pointer(&batch), + .buffer_count = 1, + }; + + /* First verify that we try to use "softpinning" by default */ + batch.offset = 48 << 20; + eb.rsvd1 = ctx_a; + gem_execbuf(i915, &eb); + igt_assert_eq_u64(batch.offset, 48 << 20); + + /* An already active VMA will try to keep its offset */ + batch.offset = 0; + eb.rsvd1 = ctx_b; + gem_execbuf(i915, &eb); + igt_assert_eq_u64(batch.offset, 48 << 20); + + gem_sync(i915, batch.handle); + gem_close(i915, batch.handle); +} + +static void create_ext(int i915) +{ + struct drm_i915_gem_context_create_ext_setparam ext = { + { .name = I915_CONTEXT_CREATE_EXT_SETPARAM }, + { .param = I915_CONTEXT_PARAM_VM } + }; + struct drm_i915_gem_context_create_ext create = { + .flags = I915_CONTEXT_CREATE_FLAGS_USE_EXTENSIONS + }; + uint32_t ctx[2]; + + igt_require(ctx_create_ioctl(i915, &create) == 0); + gem_context_destroy(i915, create.ctx_id); + + create.extensions = to_user_pointer(&ext); + + ext.param.value = gem_vm_create(i915); + + igt_assert_eq(ctx_create_ioctl(i915, &create), 0); + ctx[0] = create.ctx_id; + + igt_assert_eq(ctx_create_ioctl(i915, &create), 0); + ctx[1] = create.ctx_id; + + gem_vm_destroy(i915, ext.param.value); + + check_same_vm(i915, ctx[0], ctx[1]); + + gem_context_destroy(i915, ctx[1]); + gem_context_destroy(i915, ctx[0]); +} + +static void execbuf(int i915) +{ + struct drm_i915_gem_exec_object2 batch = { + .handle = batch_create(i915), + }; + struct drm_i915_gem_execbuffer2 eb = { + .buffers_ptr = to_user_pointer(&batch), + .buffer_count = 1, + }; + struct drm_i915_gem_context_param arg = { + .param = I915_CONTEXT_PARAM_VM, + }; + + /* First verify that we try to use "softpinning" by default */ + batch.offset = 48 << 20; + gem_execbuf(i915, &eb); + igt_assert_eq_u64(batch.offset, 48 << 20); + + arg.value = gem_vm_create(i915); + gem_context_set_param(i915, &arg); + gem_execbuf(i915, &eb); + igt_assert_eq_u64(batch.offset, 48 << 20); + gem_vm_destroy(i915, arg.value); + + arg.value = gem_vm_create(i915); + gem_context_set_param(i915, &arg); + batch.offset = 0; + gem_execbuf(i915, &eb); + igt_assert_eq_u64(batch.offset, 0); + gem_vm_destroy(i915, arg.value); + + gem_sync(i915, batch.handle); + gem_close(i915, batch.handle); +} + +static void +write_to_address(int fd, uint32_t ctx, uint64_t addr, uint32_t value) +{ + const int gen = intel_gen(intel_get_drm_devid(fd)); + struct drm_i915_gem_exec_object2 batch = { + .handle = gem_create(fd, 4096) + }; + struct drm_i915_gem_execbuffer2 eb = { + .buffers_ptr = to_user_pointer(&batch), + .buffer_count = 1, + .rsvd1 = ctx, + }; + uint32_t cs[16]; + int i; + + i = 0; + cs[i] = MI_STORE_DWORD_IMM | (gen < 6 ? 1 << 22 : 0); + if (gen >= 8) { + cs[++i] = addr; + cs[++i] = addr >> 32; + } else if (gen >= 4) { + cs[++i] = 0; + cs[++i] = addr; + } else { + cs[i]--; + cs[++i] = addr; + } + cs[++i] = value; + cs[++i] = MI_BATCH_BUFFER_END; + gem_write(fd, batch.handle, 0, cs, sizeof(cs)); + + gem_execbuf(fd, &eb); + igt_assert(batch.offset != addr); + + gem_sync(fd, batch.handle); + gem_close(fd, batch.handle); +} + +static void isolation(int i915) +{ + struct drm_i915_gem_exec_object2 obj[2] = { + { + .handle = gem_create(i915, 4096), + .offset = 1 << 20 + }, + { .handle = batch_create(i915), } + }; + struct drm_i915_gem_execbuffer2 eb = { + .buffers_ptr = to_user_pointer(obj), + .buffer_count = 2, + }; + struct drm_i915_gem_context_param arg = { + .param = I915_CONTEXT_PARAM_VM, + }; + int other = gem_reopen_driver(i915); + uint32_t ctx[2], vm[2], result; + int loops = 4096; + + /* An vm_id on one fd is not the same on another fd */ + igt_assert_neq(i915, other); + + ctx[0] = gem_context_create(i915); + ctx[1] = gem_context_create(other); + + vm[0] = gem_vm_create(i915); + do { + vm[1] = gem_vm_create(other); + } while (vm[1] != vm[0] && loops-- > 0); + igt_assert(loops); + + arg.ctx_id = ctx[0]; + arg.value = vm[0]; + gem_context_set_param(i915, &arg); + + arg.ctx_id = ctx[1]; + arg.value = vm[1]; + gem_context_set_param(other, &arg); + + eb.rsvd1 = ctx[0]; + gem_execbuf(i915, &eb); /* bind object into vm[0] */ + + /* Verify the trick with the assumed target address works */ + write_to_address(i915, ctx[0], obj[0].offset, 1); + gem_read(i915, obj[0].handle, 0, &result, sizeof(result)); + igt_assert_eq(result, 1); + + /* Now check that we can't write to vm[0] from second fd/vm */ + write_to_address(other, ctx[1], obj[0].offset, 2); + gem_read(i915, obj[0].handle, 0, &result, sizeof(result)); + igt_assert_eq(result, 1); + + close(other); + + gem_close(i915, obj[1].handle); + gem_close(i915, obj[0].handle); + + gem_context_destroy(i915, ctx[0]); + gem_vm_destroy(i915, vm[0]); +} + +static void async_destroy(int i915) +{ + struct drm_i915_gem_context_param arg = { + .ctx_id = gem_context_create(i915), + .value = gem_vm_create(i915), + .param = I915_CONTEXT_PARAM_VM, + }; + igt_spin_t *spin[2]; + + spin[0] = igt_spin_new(i915, + .ctx = arg.ctx_id, + .flags = IGT_SPIN_POLL_RUN); + igt_spin_busywait_until_started(spin[0]); + + gem_context_set_param(i915, &arg); + spin[1] = __igt_spin_new(i915, .ctx = arg.ctx_id); + + igt_spin_end(spin[0]); + gem_sync(i915, spin[0]->handle); + + gem_vm_destroy(i915, arg.value); + gem_context_destroy(i915, arg.ctx_id); + + igt_spin_end(spin[1]); + gem_sync(i915, spin[1]->handle); + + for (int i = 0; i < ARRAY_SIZE(spin); i++) + igt_spin_free(i915, spin[i]); +} + +igt_main +{ + int i915 = -1; + + igt_fixture { + i915 = drm_open_driver(DRIVER_INTEL); + igt_require_gem(i915); + igt_require(has_vm(i915)); + } + + igt_subtest("invalid-create") + invalid_create(i915); + + igt_subtest("invalid-destroy") + invalid_destroy(i915); + + igt_subtest_group { + igt_fixture { + gem_context_require_param(i915, I915_CONTEXT_PARAM_VM); + } + + igt_subtest("execbuf") + execbuf(i915); + + igt_subtest("isolation") + isolation(i915); + + igt_subtest("create-ext") + create_ext(i915); + + igt_subtest("async-destroy") + async_destroy(i915); + } + + igt_fixture { + close(i915); + } +} diff --git a/tests/meson.build b/tests/meson.build index 640018ae8..b96a9ba82 100644 --- a/tests/meson.build +++ b/tests/meson.build @@ -215,6 +215,7 @@ i915_progs = [ 'gem_unfence_active_buffers', 'gem_unref_active_buffers', 'gem_userptr_blits', + 'gem_vm_create', 'gem_wait', 'gem_workarounds', 'gem_write_read_ring_switch', -- 2.20.1 _______________________________________________ Intel-gfx mailing list Intel-gfx@xxxxxxxxxxxxxxxxxxxxx https://lists.freedesktop.org/mailman/listinfo/intel-gfx