| /* |
| * SPDX-License-Identifier: MIT |
| * |
| * Copyright © 2016 Intel Corporation |
| */ |
| |
| #include "i915_drv.h" |
| #include "i915_selftest.h" |
| |
| #include "mock_dmabuf.h" |
| #include "selftests/mock_gem_device.h" |
| |
| static int igt_dmabuf_export(void *arg) |
| { |
| struct drm_i915_private *i915 = arg; |
| struct drm_i915_gem_object *obj; |
| struct dma_buf *dmabuf; |
| |
| obj = i915_gem_object_create_shmem(i915, PAGE_SIZE); |
| if (IS_ERR(obj)) |
| return PTR_ERR(obj); |
| |
| dmabuf = i915_gem_prime_export(&obj->base, 0); |
| i915_gem_object_put(obj); |
| if (IS_ERR(dmabuf)) { |
| pr_err("i915_gem_prime_export failed with err=%d\n", |
| (int)PTR_ERR(dmabuf)); |
| return PTR_ERR(dmabuf); |
| } |
| |
| dma_buf_put(dmabuf); |
| return 0; |
| } |
| |
| static int igt_dmabuf_import_self(void *arg) |
| { |
| struct drm_i915_private *i915 = arg; |
| struct drm_i915_gem_object *obj, *import_obj; |
| struct drm_gem_object *import; |
| struct dma_buf *dmabuf; |
| int err; |
| |
| obj = i915_gem_object_create_shmem(i915, PAGE_SIZE); |
| if (IS_ERR(obj)) |
| return PTR_ERR(obj); |
| |
| dmabuf = i915_gem_prime_export(&obj->base, 0); |
| if (IS_ERR(dmabuf)) { |
| pr_err("i915_gem_prime_export failed with err=%d\n", |
| (int)PTR_ERR(dmabuf)); |
| err = PTR_ERR(dmabuf); |
| goto out; |
| } |
| |
| import = i915_gem_prime_import(&i915->drm, dmabuf); |
| if (IS_ERR(import)) { |
| pr_err("i915_gem_prime_import failed with err=%d\n", |
| (int)PTR_ERR(import)); |
| err = PTR_ERR(import); |
| goto out_dmabuf; |
| } |
| |
| if (import != &obj->base) { |
| pr_err("i915_gem_prime_import created a new object!\n"); |
| err = -EINVAL; |
| goto out_import; |
| } |
| import_obj = to_intel_bo(import); |
| |
| i915_gem_object_lock(import_obj, NULL); |
| err = __i915_gem_object_get_pages(import_obj); |
| i915_gem_object_unlock(import_obj); |
| if (err) { |
| pr_err("Same object dma-buf get_pages failed!\n"); |
| goto out_import; |
| } |
| |
| err = 0; |
| out_import: |
| i915_gem_object_put(import_obj); |
| out_dmabuf: |
| dma_buf_put(dmabuf); |
| out: |
| i915_gem_object_put(obj); |
| return err; |
| } |
| |
| static int igt_dmabuf_import_same_driver_lmem(void *arg) |
| { |
| struct drm_i915_private *i915 = arg; |
| struct intel_memory_region *lmem = i915->mm.regions[INTEL_REGION_LMEM]; |
| struct drm_i915_gem_object *obj; |
| struct drm_gem_object *import; |
| struct dma_buf *dmabuf; |
| int err; |
| |
| if (!lmem) |
| return 0; |
| |
| force_different_devices = true; |
| |
| obj = __i915_gem_object_create_user(i915, PAGE_SIZE, &lmem, 1); |
| if (IS_ERR(obj)) { |
| pr_err("__i915_gem_object_create_user failed with err=%ld\n", |
| PTR_ERR(dmabuf)); |
| err = PTR_ERR(obj); |
| goto out_ret; |
| } |
| |
| dmabuf = i915_gem_prime_export(&obj->base, 0); |
| if (IS_ERR(dmabuf)) { |
| pr_err("i915_gem_prime_export failed with err=%ld\n", |
| PTR_ERR(dmabuf)); |
| err = PTR_ERR(dmabuf); |
| goto out; |
| } |
| |
| /* |
| * We expect an import of an LMEM-only object to fail with |
| * -EOPNOTSUPP because it can't be migrated to SMEM. |
| */ |
| import = i915_gem_prime_import(&i915->drm, dmabuf); |
| if (!IS_ERR(import)) { |
| drm_gem_object_put(import); |
| pr_err("i915_gem_prime_import succeeded when it shouldn't have\n"); |
| err = -EINVAL; |
| } else if (PTR_ERR(import) != -EOPNOTSUPP) { |
| pr_err("i915_gem_prime_import failed with the wrong err=%ld\n", |
| PTR_ERR(import)); |
| err = PTR_ERR(import); |
| } |
| |
| dma_buf_put(dmabuf); |
| out: |
| i915_gem_object_put(obj); |
| out_ret: |
| force_different_devices = false; |
| return err; |
| } |
| |
| static int igt_dmabuf_import_same_driver(struct drm_i915_private *i915, |
| struct intel_memory_region **regions, |
| unsigned int num_regions) |
| { |
| struct drm_i915_gem_object *obj, *import_obj; |
| struct drm_gem_object *import; |
| struct dma_buf *dmabuf; |
| struct dma_buf_attachment *import_attach; |
| struct sg_table *st; |
| long timeout; |
| int err; |
| |
| force_different_devices = true; |
| |
| obj = __i915_gem_object_create_user(i915, PAGE_SIZE, |
| regions, num_regions); |
| if (IS_ERR(obj)) { |
| pr_err("__i915_gem_object_create_user failed with err=%ld\n", |
| PTR_ERR(dmabuf)); |
| err = PTR_ERR(obj); |
| goto out_ret; |
| } |
| |
| dmabuf = i915_gem_prime_export(&obj->base, 0); |
| if (IS_ERR(dmabuf)) { |
| pr_err("i915_gem_prime_export failed with err=%ld\n", |
| PTR_ERR(dmabuf)); |
| err = PTR_ERR(dmabuf); |
| goto out; |
| } |
| |
| import = i915_gem_prime_import(&i915->drm, dmabuf); |
| if (IS_ERR(import)) { |
| pr_err("i915_gem_prime_import failed with err=%ld\n", |
| PTR_ERR(import)); |
| err = PTR_ERR(import); |
| goto out_dmabuf; |
| } |
| |
| if (import == &obj->base) { |
| pr_err("i915_gem_prime_import reused gem object!\n"); |
| err = -EINVAL; |
| goto out_import; |
| } |
| |
| import_obj = to_intel_bo(import); |
| |
| i915_gem_object_lock(import_obj, NULL); |
| err = __i915_gem_object_get_pages(import_obj); |
| if (err) { |
| pr_err("Different objects dma-buf get_pages failed!\n"); |
| i915_gem_object_unlock(import_obj); |
| goto out_import; |
| } |
| |
| /* |
| * If the exported object is not in system memory, something |
| * weird is going on. TODO: When p2p is supported, this is no |
| * longer considered weird. |
| */ |
| if (obj->mm.region != i915->mm.regions[INTEL_REGION_SMEM]) { |
| pr_err("Exported dma-buf is not in system memory\n"); |
| err = -EINVAL; |
| } |
| |
| i915_gem_object_unlock(import_obj); |
| |
| /* Now try a fake an importer */ |
| import_attach = dma_buf_attach(dmabuf, obj->base.dev->dev); |
| if (IS_ERR(import_attach)) { |
| err = PTR_ERR(import_attach); |
| goto out_import; |
| } |
| |
| st = dma_buf_map_attachment(import_attach, DMA_BIDIRECTIONAL); |
| if (IS_ERR(st)) { |
| err = PTR_ERR(st); |
| goto out_detach; |
| } |
| |
| timeout = dma_resv_wait_timeout(dmabuf->resv, false, true, 5 * HZ); |
| if (!timeout) { |
| pr_err("dmabuf wait for exclusive fence timed out.\n"); |
| timeout = -ETIME; |
| } |
| err = timeout > 0 ? 0 : timeout; |
| dma_buf_unmap_attachment(import_attach, st, DMA_BIDIRECTIONAL); |
| out_detach: |
| dma_buf_detach(dmabuf, import_attach); |
| out_import: |
| i915_gem_object_put(import_obj); |
| out_dmabuf: |
| dma_buf_put(dmabuf); |
| out: |
| i915_gem_object_put(obj); |
| out_ret: |
| force_different_devices = false; |
| return err; |
| } |
| |
| static int igt_dmabuf_import_same_driver_smem(void *arg) |
| { |
| struct drm_i915_private *i915 = arg; |
| struct intel_memory_region *smem = i915->mm.regions[INTEL_REGION_SMEM]; |
| |
| return igt_dmabuf_import_same_driver(i915, &smem, 1); |
| } |
| |
| static int igt_dmabuf_import_same_driver_lmem_smem(void *arg) |
| { |
| struct drm_i915_private *i915 = arg; |
| struct intel_memory_region *regions[2]; |
| |
| if (!i915->mm.regions[INTEL_REGION_LMEM]) |
| return 0; |
| |
| regions[0] = i915->mm.regions[INTEL_REGION_LMEM]; |
| regions[1] = i915->mm.regions[INTEL_REGION_SMEM]; |
| return igt_dmabuf_import_same_driver(i915, regions, 2); |
| } |
| |
| static int igt_dmabuf_import(void *arg) |
| { |
| struct drm_i915_private *i915 = arg; |
| struct drm_i915_gem_object *obj; |
| struct dma_buf *dmabuf; |
| void *obj_map, *dma_map; |
| struct dma_buf_map map; |
| u32 pattern[] = { 0, 0xaa, 0xcc, 0x55, 0xff }; |
| int err, i; |
| |
| dmabuf = mock_dmabuf(1); |
| if (IS_ERR(dmabuf)) |
| return PTR_ERR(dmabuf); |
| |
| obj = to_intel_bo(i915_gem_prime_import(&i915->drm, dmabuf)); |
| if (IS_ERR(obj)) { |
| pr_err("i915_gem_prime_import failed with err=%d\n", |
| (int)PTR_ERR(obj)); |
| err = PTR_ERR(obj); |
| goto out_dmabuf; |
| } |
| |
| if (obj->base.dev != &i915->drm) { |
| pr_err("i915_gem_prime_import created a non-i915 object!\n"); |
| err = -EINVAL; |
| goto out_obj; |
| } |
| |
| if (obj->base.size != PAGE_SIZE) { |
| pr_err("i915_gem_prime_import is wrong size found %lld, expected %ld\n", |
| (long long)obj->base.size, PAGE_SIZE); |
| err = -EINVAL; |
| goto out_obj; |
| } |
| |
| err = dma_buf_vmap(dmabuf, &map); |
| dma_map = err ? NULL : map.vaddr; |
| if (!dma_map) { |
| pr_err("dma_buf_vmap failed\n"); |
| err = -ENOMEM; |
| goto out_obj; |
| } |
| |
| if (0) { /* Can not yet map dmabuf */ |
| obj_map = i915_gem_object_pin_map(obj, I915_MAP_WB); |
| if (IS_ERR(obj_map)) { |
| err = PTR_ERR(obj_map); |
| pr_err("i915_gem_object_pin_map failed with err=%d\n", err); |
| goto out_dma_map; |
| } |
| |
| for (i = 0; i < ARRAY_SIZE(pattern); i++) { |
| memset(dma_map, pattern[i], PAGE_SIZE); |
| if (memchr_inv(obj_map, pattern[i], PAGE_SIZE)) { |
| err = -EINVAL; |
| pr_err("imported vmap not all set to %x!\n", pattern[i]); |
| i915_gem_object_unpin_map(obj); |
| goto out_dma_map; |
| } |
| } |
| |
| for (i = 0; i < ARRAY_SIZE(pattern); i++) { |
| memset(obj_map, pattern[i], PAGE_SIZE); |
| if (memchr_inv(dma_map, pattern[i], PAGE_SIZE)) { |
| err = -EINVAL; |
| pr_err("exported vmap not all set to %x!\n", pattern[i]); |
| i915_gem_object_unpin_map(obj); |
| goto out_dma_map; |
| } |
| } |
| |
| i915_gem_object_unpin_map(obj); |
| } |
| |
| err = 0; |
| out_dma_map: |
| dma_buf_vunmap(dmabuf, &map); |
| out_obj: |
| i915_gem_object_put(obj); |
| out_dmabuf: |
| dma_buf_put(dmabuf); |
| return err; |
| } |
| |
| static int igt_dmabuf_import_ownership(void *arg) |
| { |
| struct drm_i915_private *i915 = arg; |
| struct drm_i915_gem_object *obj; |
| struct dma_buf *dmabuf; |
| struct dma_buf_map map; |
| void *ptr; |
| int err; |
| |
| dmabuf = mock_dmabuf(1); |
| if (IS_ERR(dmabuf)) |
| return PTR_ERR(dmabuf); |
| |
| err = dma_buf_vmap(dmabuf, &map); |
| ptr = err ? NULL : map.vaddr; |
| if (!ptr) { |
| pr_err("dma_buf_vmap failed\n"); |
| err = -ENOMEM; |
| goto err_dmabuf; |
| } |
| |
| memset(ptr, 0xc5, PAGE_SIZE); |
| dma_buf_vunmap(dmabuf, &map); |
| |
| obj = to_intel_bo(i915_gem_prime_import(&i915->drm, dmabuf)); |
| if (IS_ERR(obj)) { |
| pr_err("i915_gem_prime_import failed with err=%d\n", |
| (int)PTR_ERR(obj)); |
| err = PTR_ERR(obj); |
| goto err_dmabuf; |
| } |
| |
| dma_buf_put(dmabuf); |
| |
| err = i915_gem_object_pin_pages_unlocked(obj); |
| if (err) { |
| pr_err("i915_gem_object_pin_pages failed with err=%d\n", err); |
| goto out_obj; |
| } |
| |
| err = 0; |
| i915_gem_object_unpin_pages(obj); |
| out_obj: |
| i915_gem_object_put(obj); |
| return err; |
| |
| err_dmabuf: |
| dma_buf_put(dmabuf); |
| return err; |
| } |
| |
| static int igt_dmabuf_export_vmap(void *arg) |
| { |
| struct drm_i915_private *i915 = arg; |
| struct drm_i915_gem_object *obj; |
| struct dma_buf *dmabuf; |
| struct dma_buf_map map; |
| void *ptr; |
| int err; |
| |
| obj = i915_gem_object_create_shmem(i915, PAGE_SIZE); |
| if (IS_ERR(obj)) |
| return PTR_ERR(obj); |
| |
| dmabuf = i915_gem_prime_export(&obj->base, 0); |
| if (IS_ERR(dmabuf)) { |
| pr_err("i915_gem_prime_export failed with err=%d\n", |
| (int)PTR_ERR(dmabuf)); |
| err = PTR_ERR(dmabuf); |
| goto err_obj; |
| } |
| i915_gem_object_put(obj); |
| |
| err = dma_buf_vmap(dmabuf, &map); |
| ptr = err ? NULL : map.vaddr; |
| if (!ptr) { |
| pr_err("dma_buf_vmap failed\n"); |
| err = -ENOMEM; |
| goto out; |
| } |
| |
| if (memchr_inv(ptr, 0, dmabuf->size)) { |
| pr_err("Exported object not initialiased to zero!\n"); |
| err = -EINVAL; |
| goto out; |
| } |
| |
| memset(ptr, 0xc5, dmabuf->size); |
| |
| err = 0; |
| dma_buf_vunmap(dmabuf, &map); |
| out: |
| dma_buf_put(dmabuf); |
| return err; |
| |
| err_obj: |
| i915_gem_object_put(obj); |
| return err; |
| } |
| |
| int i915_gem_dmabuf_mock_selftests(void) |
| { |
| static const struct i915_subtest tests[] = { |
| SUBTEST(igt_dmabuf_export), |
| SUBTEST(igt_dmabuf_import_self), |
| SUBTEST(igt_dmabuf_import), |
| SUBTEST(igt_dmabuf_import_ownership), |
| SUBTEST(igt_dmabuf_export_vmap), |
| }; |
| struct drm_i915_private *i915; |
| int err; |
| |
| i915 = mock_gem_device(); |
| if (!i915) |
| return -ENOMEM; |
| |
| err = i915_subtests(tests, i915); |
| |
| mock_destroy_device(i915); |
| return err; |
| } |
| |
| int i915_gem_dmabuf_live_selftests(struct drm_i915_private *i915) |
| { |
| static const struct i915_subtest tests[] = { |
| SUBTEST(igt_dmabuf_export), |
| SUBTEST(igt_dmabuf_import_same_driver_lmem), |
| SUBTEST(igt_dmabuf_import_same_driver_smem), |
| SUBTEST(igt_dmabuf_import_same_driver_lmem_smem), |
| }; |
| |
| return i915_subtests(tests, i915); |
| } |