@@ -7,6 +7,7 @@ obj-$(CONFIG_UDMABUF) += udmabuf.o
dmabuf_selftests-y := \
selftest.o \
- st-dma-fence.o
+ st-dma-fence.o \
+ st-dma-fence-array.o
obj-$(CONFIG_DMABUF_SELFTESTS) += dmabuf_selftests.o
@@ -11,3 +11,4 @@
*/
selftest(sanitycheck, __sanitycheck__) /* keep first (igt selfcheck) */
selftest(dma_fence, dma_fence)
+selftest(dma_fence_array, dma_fence_array)
new file mode 100644
@@ -0,0 +1,392 @@
+/* SPDX-License-Identifier: MIT */
+
+/*
+ * Copyright © 2019 Intel Corporation
+ */
+
+#include <linux/delay.h>
+#include <linux/dma-fence.h>
+#include <linux/dma-fence-array.h>
+#include <linux/kernel.h>
+#include <linux/kthread.h>
+#include <linux/sched/signal.h>
+#include <linux/slab.h>
+#include <linux/spinlock.h>
+
+#include "selftest.h"
+
+static struct kmem_cache *slab_fences;
+
+static struct mock_fence {
+ struct dma_fence base;
+ struct spinlock lock;
+} *to_mock_fence(struct dma_fence *f) {
+ return container_of(f, struct mock_fence, base);
+}
+
+static const char *mock_name(struct dma_fence *f)
+{
+ return "mock";
+}
+
+static void mock_fence_release(struct dma_fence *f)
+{
+ kmem_cache_free(slab_fences, to_mock_fence(f));
+}
+
+static const struct dma_fence_ops mock_ops = {
+ .get_driver_name = mock_name,
+ .get_timeline_name = mock_name,
+ .release = mock_fence_release,
+};
+
+static struct dma_fence *mock_fence(void *arg)
+{
+ struct mock_fence *f;
+
+ f = kmem_cache_alloc(slab_fences, GFP_KERNEL);
+ if (!f)
+ return NULL;
+
+ spin_lock_init(&f->lock);
+ dma_fence_init(&f->base, &mock_ops, &f->lock, 0, 0);
+
+ return &f->base;
+}
+
+static struct dma_fence *stub_fence(void *arg)
+{
+ return dma_fence_get_stub();
+}
+
+static struct dma_fence *same_fence(void *arg)
+{
+ return dma_fence_get(arg);
+}
+
+static int empty(void *arg)
+{
+ struct dma_fence_array *arr;
+ int err = 0;
+
+ arr = dma_fence_array_create(0, NULL, 0, 0, false);
+ if (!arr)
+ return -ENOMEM;
+
+ if (!dma_fence_is_signaled(&arr->base)) {
+ pr_err("Empty dma-fence-array is not signaled!\n");
+ err = -EINVAL;
+ }
+
+ dma_fence_put(&arr->base);
+ return err;
+}
+
+static void free_fences(int count, struct dma_fence **fences)
+{
+ while (count--)
+ dma_fence_put(fences[count]);
+ kfree(fences);
+}
+
+static struct dma_fence **
+create_fences(int count, struct dma_fence *(*ctor)(void *arg), void *arg)
+{
+ struct dma_fence **fences;
+ int i;
+
+ fences = kmalloc_array(count, sizeof(*fences), GFP_KERNEL);
+ if (!fences)
+ return NULL;
+
+ for (i = 0; i < count; i++) {
+ fences[i] = ctor(arg);
+ if (!fences[i]) {
+ free_fences(i, fences);
+ return NULL;
+ }
+ }
+
+ return fences;
+}
+
+static int stub(void *arg)
+{
+ struct dma_fence_array *arr;
+ struct dma_fence **fences;
+ int err = 0;
+
+ fences = create_fences(1, stub_fence, NULL);
+ if (!fences)
+ return -ENOMEM;
+
+ arr = dma_fence_array_create(1, fences, 0, 0, false);
+ if (!arr) {
+ free_fences(1, fences);
+ return -ENOMEM;
+ }
+
+ dma_fence_enable_sw_signaling(&arr->base);
+
+ if (!dma_fence_is_signaled(&arr->base)) {
+ pr_err("dma-fence-array(stub) is not signaled!\n");
+ err = -EINVAL;
+ }
+
+ dma_fence_put(&arr->base);
+ return err;
+}
+
+static int single(void *arg)
+{
+ struct dma_fence_array *arr;
+ struct dma_fence **fences;
+ int err = 0;
+
+ fences = create_fences(1, mock_fence, NULL);
+ if (!fences)
+ return -ENOMEM;
+
+ arr = dma_fence_array_create(1, fences, 0, 0, false);
+ if (!arr) {
+ free_fences(1, fences);
+ return -ENOMEM;
+ }
+
+ dma_fence_enable_sw_signaling(&arr->base);
+
+ if (dma_fence_is_signaled(&arr->base)) {
+ pr_err("dma-fence-array(single) is signaled upon creation!\n");
+ err = -EINVAL;
+ }
+
+ dma_fence_signal(fences[0]);
+
+ if (!dma_fence_is_signaled(&arr->base)) {
+ pr_err("dma-fence-array(single) is not signaled\n");
+ err = -EINVAL;
+ }
+
+ dma_fence_put(&arr->base);
+ return err;
+}
+
+static int pair(void *arg)
+{
+ struct dma_fence_array *arr;
+ struct dma_fence **fences;
+ int err = 0;
+
+ fences = create_fences(2, mock_fence, NULL);
+ if (!fences)
+ return -ENOMEM;
+
+ arr = dma_fence_array_create(2, fences, 0, 0, false);
+ if (!arr) {
+ free_fences(2, fences);
+ return -ENOMEM;
+ }
+
+ dma_fence_enable_sw_signaling(&arr->base);
+
+ if (dma_fence_is_signaled(&arr->base)) {
+ pr_err("dma-fence-array(pair) is signaled upon creation!\n");
+ err = -EINVAL;
+ }
+
+ dma_fence_signal(fences[0]);
+
+ if (dma_fence_is_signaled(&arr->base)) {
+ pr_err("dma-fence-array(pair) is signaled after one signal!\n");
+ err = -EINVAL;
+ }
+
+ dma_fence_signal(fences[0]);
+
+ if (dma_fence_is_signaled(&arr->base)) {
+ pr_err("dma-fence-array(pair) is signaled after a repeated signal!\n");
+ err = -EINVAL;
+ }
+
+ dma_fence_signal(fences[1]);
+
+ if (!dma_fence_is_signaled(&arr->base)) {
+ pr_err("dma-fence-array(pair) is not signaled\n");
+ err = -EINVAL;
+ }
+
+ dma_fence_put(&arr->base);
+ return err;
+}
+
+static int repeat(void *arg)
+{
+ struct dma_fence_array *arr;
+ struct dma_fence **fences;
+ struct dma_fence *f;
+ int err = 0;
+
+ f = mock_fence(NULL);
+ if (!f)
+ return -ENOMEM;
+
+ fences = create_fences(2, same_fence, f);
+ dma_fence_put(f);
+ if (!fences)
+ return -ENOMEM;
+
+ arr = dma_fence_array_create(2, fences, 0, 0, false);
+ if (!arr) {
+ free_fences(2, fences);
+ return -ENOMEM;
+ }
+
+ dma_fence_enable_sw_signaling(&arr->base);
+
+ if (dma_fence_is_signaled(&arr->base)) {
+ pr_err("dma-fence-array(pair) is signaled upon creation!\n");
+ err = -EINVAL;
+ }
+
+ dma_fence_signal(fences[0]);
+
+ if (!dma_fence_is_signaled(&arr->base)) {
+ pr_err("dma-fence-array(pair) is not signaled\n");
+ err = -EINVAL;
+ }
+
+ dma_fence_put(&arr->base);
+ return err;
+}
+
+static struct dma_fence *create_stub_array(void *arg)
+{
+ struct dma_fence_array *arr;
+ struct dma_fence **fences;
+
+ fences = create_fences(1, stub_fence, NULL);
+ if (!fences)
+ return NULL;
+
+ arr = dma_fence_array_create(1, fences, 0, 0, false);
+ if (!arr) {
+ free_fences(1, fences);
+ return NULL;
+ }
+
+ return &arr->base;
+}
+
+static int recurse_stub(void *arg)
+{
+ struct dma_fence_array *arr;
+ struct dma_fence **fences;
+ int err = 0;
+
+ fences = create_fences(1, create_stub_array, NULL);
+ if (!fences)
+ return -ENOMEM;
+
+ arr = dma_fence_array_create(1, fences, 0, 0, false);
+ if (!arr) {
+ free_fences(1, fences);
+ return -ENOMEM;
+ }
+
+ dma_fence_enable_sw_signaling(&arr->base);
+
+ if (!dma_fence_is_signaled(&arr->base)) {
+ pr_err("dma-fence-array(recurse-stub) is not signaled!\n");
+ err = -EINVAL;
+ }
+
+ dma_fence_put(&arr->base);
+ return err;
+}
+
+static struct dma_fence *create_mock_array(void *arg)
+{
+ struct dma_fence_array *arr;
+ struct dma_fence **fences;
+
+ fences = create_fences(1, same_fence, arg);
+ if (!fences)
+ return NULL;
+
+ arr = dma_fence_array_create(1, fences, 0, 0, false);
+ if (!arr) {
+ free_fences(1, fences);
+ return NULL;
+ }
+
+ return &arr->base;
+}
+
+static int recurse_mock(void *arg)
+{
+ struct dma_fence_array *arr;
+ struct dma_fence **fences;
+ struct dma_fence *f;
+ int err = 0;
+
+ f = mock_fence(NULL);
+ if (!f)
+ return -ENOMEM;
+
+ fences = create_fences(1, create_mock_array, f);
+ if (!fences) {
+ dma_fence_put(f);
+ return -ENOMEM;
+ }
+
+ arr = dma_fence_array_create(1, fences, 0, 0, false);
+ if (!arr) {
+ free_fences(1, fences);
+ dma_fence_put(f);
+ return -ENOMEM;
+ }
+
+ dma_fence_enable_sw_signaling(&arr->base);
+
+ if (dma_fence_is_signaled(&arr->base)) {
+ pr_err("dma-fence-array(recurse-mock) is signaled on construction!\n");
+ err = -EINVAL;
+ }
+
+ dma_fence_signal(f);
+
+ if (!dma_fence_is_signaled(&arr->base)) {
+ pr_err("dma-fence-array(recurse-mock) is not signaled!\n");
+ err = -EINVAL;
+ }
+
+ dma_fence_put(&arr->base);
+ dma_fence_put(f);
+ return err;
+}
+
+int dma_fence_array(void)
+{
+ static const struct subtest tests[] = {
+ SUBTEST(empty),
+ SUBTEST(stub),
+ SUBTEST(single),
+ SUBTEST(pair),
+ SUBTEST(repeat),
+ SUBTEST(recurse_stub),
+ SUBTEST(recurse_mock),
+ };
+ int ret;
+
+ slab_fences = KMEM_CACHE(mock_fence,
+ SLAB_TYPESAFE_BY_RCU |
+ SLAB_HWCACHE_ALIGN);
+ if (!slab_fences)
+ return -ENOMEM;
+
+ ret = subtests(tests, NULL);
+
+ kmem_cache_destroy(slab_fences);
+
+ return ret;
+}
A preliminary set of tests to exercise the basic dma-fence API on top of struct dma_fence_array. Signed-off-by: Chris Wilson <chris@chris-wilson.co.uk> --- drivers/dma-buf/Makefile | 3 +- drivers/dma-buf/selftests.h | 1 + drivers/dma-buf/st-dma-fence-array.c | 392 +++++++++++++++++++++++++++ 3 files changed, 395 insertions(+), 1 deletion(-) create mode 100644 drivers/dma-buf/st-dma-fence-array.c