drm/i915: Add some mock tests for dmabuf interop
authorChris Wilson <chris@chris-wilson.co.uk>
Mon, 13 Feb 2017 17:15:36 +0000 (17:15 +0000)
committerChris Wilson <chris@chris-wilson.co.uk>
Mon, 13 Feb 2017 20:45:50 +0000 (20:45 +0000)
Check that we can create both dmabuf and objects from dmabuf.

v2: Cleanups, correct include, fix unpin on dead path and prevent
explosion on dmabuf init failure

Signed-off-by: Chris Wilson <chris@chris-wilson.co.uk>
Reviewed-by: Matthew Auld <matthew.auld@intel.com>
Link: http://patchwork.freedesktop.org/patch/msgid/20170213171558.20942-25-chris@chris-wilson.co.uk
drivers/gpu/drm/i915/i915_gem_dmabuf.c
drivers/gpu/drm/i915/selftests/i915_gem_dmabuf.c [new file with mode: 0644]
drivers/gpu/drm/i915/selftests/i915_mock_selftests.h
drivers/gpu/drm/i915/selftests/mock_dmabuf.c [new file with mode: 0644]
drivers/gpu/drm/i915/selftests/mock_dmabuf.h [new file with mode: 0644]

index d037adcda6f20b71566b6af36dd59eee7df94971..3e276eee0450ad506f0e77b6cbb7b007896602a5 100644 (file)
@@ -307,3 +307,8 @@ fail_detach:
 
        return ERR_PTR(ret);
 }
+
+#if IS_ENABLED(CONFIG_DRM_I915_SELFTEST)
+#include "selftests/mock_dmabuf.c"
+#include "selftests/i915_gem_dmabuf.c"
+#endif
diff --git a/drivers/gpu/drm/i915/selftests/i915_gem_dmabuf.c b/drivers/gpu/drm/i915/selftests/i915_gem_dmabuf.c
new file mode 100644 (file)
index 0000000..a2393fc
--- /dev/null
@@ -0,0 +1,294 @@
+/*
+ * Copyright © 2016 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 "../i915_selftest.h"
+
+#include "mock_gem_device.h"
+#include "mock_dmabuf.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(i915, PAGE_SIZE);
+       if (IS_ERR(obj))
+               return PTR_ERR(obj);
+
+       dmabuf = i915_gem_prime_export(&i915->drm, &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;
+       struct drm_gem_object *import;
+       struct dma_buf *dmabuf;
+       int err;
+
+       obj = i915_gem_object_create(i915, PAGE_SIZE);
+       if (IS_ERR(obj))
+               return PTR_ERR(obj);
+
+       dmabuf = i915_gem_prime_export(&i915->drm, &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;
+       }
+
+       err = 0;
+out_import:
+       i915_gem_object_put(to_intel_bo(import));
+out_dmabuf:
+       dma_buf_put(dmabuf);
+out:
+       i915_gem_object_put(obj);
+       return err;
+}
+
+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;
+       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;
+       }
+
+       dma_map = dma_buf_vmap(dmabuf);
+       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, dma_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;
+       void *ptr;
+       int err;
+
+       dmabuf = mock_dmabuf(1);
+       if (IS_ERR(dmabuf))
+               return PTR_ERR(dmabuf);
+
+       ptr = dma_buf_vmap(dmabuf);
+       if (!ptr) {
+               pr_err("dma_buf_vmap failed\n");
+               err = -ENOMEM;
+               goto err_dmabuf;
+       }
+
+       memset(ptr, 0xc5, PAGE_SIZE);
+       dma_buf_vunmap(dmabuf, ptr);
+
+       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(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;
+       void *ptr;
+       int err;
+
+       obj = i915_gem_object_create(i915, PAGE_SIZE);
+       if (IS_ERR(obj))
+               return PTR_ERR(obj);
+
+       dmabuf = i915_gem_prime_export(&i915->drm, &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);
+
+       ptr = dma_buf_vmap(dmabuf);
+       if (IS_ERR(ptr)) {
+               err = PTR_ERR(ptr);
+               pr_err("dma_buf_vmap failed with err=%d\n", err);
+               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, ptr);
+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);
+
+       drm_dev_unref(&i915->drm);
+       return err;
+}
index c61e08de7913b366513db3cb94ed709c892871b1..955a4d6ccdaf8152f6d57afba6e54310ff4ac5d9 100644 (file)
@@ -14,3 +14,4 @@ selftest(uncore, intel_uncore_mock_selftests)
 selftest(breadcrumbs, intel_breadcrumbs_mock_selftests)
 selftest(requests, i915_gem_request_mock_selftests)
 selftest(objects, i915_gem_object_mock_selftests)
+selftest(dmabuf, i915_gem_dmabuf_mock_selftests)
diff --git a/drivers/gpu/drm/i915/selftests/mock_dmabuf.c b/drivers/gpu/drm/i915/selftests/mock_dmabuf.c
new file mode 100644 (file)
index 0000000..99da8f4
--- /dev/null
@@ -0,0 +1,176 @@
+/*
+ * Copyright © 2016 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 "mock_dmabuf.h"
+
+static struct sg_table *mock_map_dma_buf(struct dma_buf_attachment *attachment,
+                                        enum dma_data_direction dir)
+{
+       struct mock_dmabuf *mock = to_mock(attachment->dmabuf);
+       struct sg_table *st;
+       struct scatterlist *sg;
+       int i, err;
+
+       st = kmalloc(sizeof(*st), GFP_KERNEL);
+       if (!st)
+               return ERR_PTR(-ENOMEM);
+
+       err = sg_alloc_table(st, mock->npages, GFP_KERNEL);
+       if (err)
+               goto err_free;
+
+       sg = st->sgl;
+       for (i = 0; i < mock->npages; i++) {
+               sg_set_page(sg, mock->pages[i], PAGE_SIZE, 0);
+               sg = sg_next(sg);
+       }
+
+       if (!dma_map_sg(attachment->dev, st->sgl, st->nents, dir)) {
+               err = -ENOMEM;
+               goto err_st;
+       }
+
+       return st;
+
+err_st:
+       sg_free_table(st);
+err_free:
+       kfree(st);
+       return ERR_PTR(err);
+}
+
+static void mock_unmap_dma_buf(struct dma_buf_attachment *attachment,
+                              struct sg_table *st,
+                              enum dma_data_direction dir)
+{
+       dma_unmap_sg(attachment->dev, st->sgl, st->nents, dir);
+       sg_free_table(st);
+       kfree(st);
+}
+
+static void mock_dmabuf_release(struct dma_buf *dma_buf)
+{
+       struct mock_dmabuf *mock = to_mock(dma_buf);
+       int i;
+
+       for (i = 0; i < mock->npages; i++)
+               put_page(mock->pages[i]);
+
+       kfree(mock);
+}
+
+static void *mock_dmabuf_vmap(struct dma_buf *dma_buf)
+{
+       struct mock_dmabuf *mock = to_mock(dma_buf);
+
+       return vm_map_ram(mock->pages, mock->npages, 0, PAGE_KERNEL);
+}
+
+static void mock_dmabuf_vunmap(struct dma_buf *dma_buf, void *vaddr)
+{
+       struct mock_dmabuf *mock = to_mock(dma_buf);
+
+       vm_unmap_ram(vaddr, mock->npages);
+}
+
+static void *mock_dmabuf_kmap_atomic(struct dma_buf *dma_buf, unsigned long page_num)
+{
+       struct mock_dmabuf *mock = to_mock(dma_buf);
+
+       return kmap_atomic(mock->pages[page_num]);
+}
+
+static void mock_dmabuf_kunmap_atomic(struct dma_buf *dma_buf, unsigned long page_num, void *addr)
+{
+       kunmap_atomic(addr);
+}
+
+static void *mock_dmabuf_kmap(struct dma_buf *dma_buf, unsigned long page_num)
+{
+       struct mock_dmabuf *mock = to_mock(dma_buf);
+
+       return kmap(mock->pages[page_num]);
+}
+
+static void mock_dmabuf_kunmap(struct dma_buf *dma_buf, unsigned long page_num, void *addr)
+{
+       struct mock_dmabuf *mock = to_mock(dma_buf);
+
+       return kunmap(mock->pages[page_num]);
+}
+
+static int mock_dmabuf_mmap(struct dma_buf *dma_buf, struct vm_area_struct *vma)
+{
+       return -ENODEV;
+}
+
+static const struct dma_buf_ops mock_dmabuf_ops =  {
+       .map_dma_buf = mock_map_dma_buf,
+       .unmap_dma_buf = mock_unmap_dma_buf,
+       .release = mock_dmabuf_release,
+       .kmap = mock_dmabuf_kmap,
+       .kmap_atomic = mock_dmabuf_kmap_atomic,
+       .kunmap = mock_dmabuf_kunmap,
+       .kunmap_atomic = mock_dmabuf_kunmap_atomic,
+       .mmap = mock_dmabuf_mmap,
+       .vmap = mock_dmabuf_vmap,
+       .vunmap = mock_dmabuf_vunmap,
+};
+
+static struct dma_buf *mock_dmabuf(int npages)
+{
+       struct mock_dmabuf *mock;
+       DEFINE_DMA_BUF_EXPORT_INFO(exp_info);
+       struct dma_buf *dmabuf;
+       int i;
+
+       mock = kmalloc(sizeof(*mock) + npages * sizeof(struct page *),
+                      GFP_KERNEL);
+       if (!mock)
+               return ERR_PTR(-ENOMEM);
+
+       mock->npages = npages;
+       for (i = 0; i < npages; i++) {
+               mock->pages[i] = alloc_page(GFP_KERNEL);
+               if (!mock->pages[i])
+                       goto err;
+       }
+
+       exp_info.ops = &mock_dmabuf_ops;
+       exp_info.size = npages * PAGE_SIZE;
+       exp_info.flags = O_CLOEXEC;
+       exp_info.priv = mock;
+
+       dmabuf = dma_buf_export(&exp_info);
+       if (IS_ERR(dmabuf))
+               goto err;
+
+       return dmabuf;
+
+err:
+       while (i--)
+               put_page(mock->pages[i]);
+       kfree(mock);
+       return ERR_PTR(-ENOMEM);
+}
diff --git a/drivers/gpu/drm/i915/selftests/mock_dmabuf.h b/drivers/gpu/drm/i915/selftests/mock_dmabuf.h
new file mode 100644 (file)
index 0000000..ec80613
--- /dev/null
@@ -0,0 +1,41 @@
+
+/*
+ * Copyright © 2016 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 __MOCK_DMABUF_H__
+#define __MOCK_DMABUF_H__
+
+#include <linux/dma-buf.h>
+
+struct mock_dmabuf {
+       int npages;
+       struct page *pages[];
+};
+
+static struct mock_dmabuf *to_mock(struct dma_buf *buf)
+{
+       return buf->priv;
+}
+
+#endif /* !__MOCK_DMABUF_H__ */