diff mbox series

dma-buf: Extend selftests to exercise dma-fence-array

Message ID 20190824133806.8473-1-chris@chris-wilson.co.uk (mailing list archive)
State New, archived
Headers show
Series dma-buf: Extend selftests to exercise dma-fence-array | expand

Commit Message

Chris Wilson Aug. 24, 2019, 1:38 p.m. UTC
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

Comments

Chris Wilson Aug. 24, 2019, 2:21 p.m. UTC | #1
Quoting Patchwork (2019-08-24 15:18:25)
> == Series Details ==
> 
> Series: dma-buf: Extend selftests to exercise dma-fence-array
> URL   : https://patchwork.freedesktop.org/series/65736/
> State : success
> 
> == Summary ==
> 
> CI Bug Log - changes from CI_DRM_6783 -> Patchwork_14182
> ====================================================
> 
> Summary
> -------
> 
>   **SUCCESS**
> 
>   No regressions found.
> 
>   External URL: https://intel-gfx-ci.01.org/tree/drm-tip/Patchwork_14182/
> 
> New tests
> ---------
> 
>   New tests have been introduced between CI_DRM_6783 and Patchwork_14182:
> 
> ### New IGT tests (1) ###
> 
>   * igt@dmabuf@dma_fence_array:
>     - Statuses : 42 pass(s)
>     - Exec time: [0.02, 0.07] s

Pass?

<6> [535.797646] [IGT] dmabuf: starting subtest dma_fence_array
<6> [535.798913] dma-buf: Running dma_fence_array
<6> [535.799112] dma-buf: Running dma_fence_array/empty
<6> [535.799168] dma-buf: Running dma_fence_array/stub
<6> [535.799181] dma-buf: Running dma_fence_array/single
<6> [535.799238] dma-buf: Running dma_fence_array/pair
<6> [535.799264] dma-buf: Running dma_fence_array/repeat
<6> [535.799284] dma-buf: Running dma_fence_array/recurse_stub
<4> [535.799297]
<4> [535.799299] ============================================
<4> [535.799300] WARNING: possible recursive locking detected
<4> [535.799301] 5.3.0-rc5-CI-Patchwork_14182+ #1 Tainted: G     U
<4> [535.799302] --------------------------------------------
<4> [535.799303] dmabuf/5021 is trying to acquire lock:
<4> [535.799304] 0000000064cf0cf8 (&(&array->lock)->rlock){-...}, at: dma_fence_add_callback+0x39/0x1f0
<4> [535.799308]
but task is already holding lock:
<4> [535.799309] 0000000045940bf4 (&(&array->lock)->rlock){-...}, at: dma_fence_enable_sw_signaling+0x45/0x130
<4> [535.799311]
other info that might help us debug this:
<4> [535.799312]  Possible unsafe locking scenario:

<4> [535.799313]        CPU0
<4> [535.799313]        ----
<4> [535.799314]   lock(&(&array->lock)->rlock);
<4> [535.799315]   lock(&(&array->lock)->rlock);
<4> [535.799316]
 *** DEADLOCK ***

<4> [535.799316]  May be due to missing lock nesting notation

<4> [535.799317] 1 lock held by dmabuf/5021:
<4> [535.799318]  #0: 0000000045940bf4 (&(&array->lock)->rlock){-...}, at: dma_fence_enable_sw_signaling+0x45/0x130
<4> [535.799320]
stack backtrace:
<4> [535.799321] CPU: 7 PID: 5021 Comm: dmabuf Tainted: G     U            5.3.0-rc5-CI-Patchwork_14182+ #1
<4> [535.799323] Hardware name: Intel Corporation WhiskeyLake Client Platform/CometLake U DDR4 HR ERB, BIOS CMLSFWR1.R00.1125.D00.1903221424 03/22/2019
<4> [535.799323] Call Trace:
<4> [535.799326]  dump_stack+0x67/0x9b
<4> [535.799329]  __lock_acquire+0x12a7/0x1e90
<4> [535.799332]  lock_acquire+0xa6/0x1c0
<4> [535.799333]  ? dma_fence_add_callback+0x39/0x1f0
<4> [535.799335]  ? dma_fence_array_create+0xc0/0xc0
<4> [535.799337]  _raw_spin_lock_irqsave+0x33/0x50
<4> [535.799338]  ? dma_fence_add_callback+0x39/0x1f0
<4> [535.799340]  dma_fence_add_callback+0x39/0x1f0
<4> [535.799341]  dma_fence_array_enable_signaling+0x87/0xe0
<4> [535.799343]  dma_fence_enable_sw_signaling+0x58/0x130
<4> [535.799346]  recurse_stub+0x9e/0x170 [dmabuf_selftests]
<4> [535.799348]  __subtests+0x187/0x1c0 [dmabuf_selftests]
<4> [535.799351]  dma_fence_array+0x45/0x5e [dmabuf_selftests]
<4> [535.799353]  st_init+0x74/0x1000 [dmabuf_selftests]
<4> [535.799354]  ? 0xffffffffa0049000
<4> [535.799356]  do_one_initcall+0x58/0x300
<4> [535.799358]  ? do_init_module+0x1d/0x1f6
<4> [535.799360]  ? rcu_read_lock_sched_held+0x6f/0x80
<4> [535.799363]  ? kmem_cache_alloc_trace+0x2d1/0x300
<4> [535.799365]  do_init_module+0x56/0x1f6
<4> [535.799366]  load_module+0x25bd/0x2a40
<4> [535.799370]  ? __se_sys_finit_module+0xd3/0xf0
<4> [535.799371]  __se_sys_finit_module+0xd3/0xf0
<4> [535.799374]  do_syscall_64+0x55/0x1c0
<4> [535.799376]  entry_SYSCALL_64_after_hwframe+0x49/0xbe
<4> [535.799377] RIP: 0033:0x7fc774a2d839
<4> [535.799379] Code: 00 f3 c3 66 2e 0f 1f 84 00 00 00 00 00 0f 1f 40 00 48 89 f8 48 89 f7 48 89 d6 48 89 ca 4d 89 c2 4d 89 c8 4c 8b 4c 24 08 0f 05 <48> 3d 01 f0 ff ff 73 01 c3 48 8b 0d 1f f6 2c 00 f7 d8 64 89 01 48
<4> [535.799380] RSP: 002b:00007fff97dadfb8 EFLAGS: 00000246 ORIG_RAX: 0000000000000139
<4> [535.799381] RAX: ffffffffffffffda RBX: 00005587a9f8ed70 RCX: 00007fc774a2d839
<4> [535.799382] RDX: 0000000000000000 RSI: 00005587a9f8dc20 RDI: 0000000000000006
<4> [535.799383] RBP: 00005587a9f8dc20 R08: 0000000000000010 R09: 0000000000000000
<4> [535.799384] R10: 00007fff97dae200 R11: 0000000000000246 R12: 0000000000000000
<4> [535.799385] R13: 00005587a9f8b000 R14: 0000000000000020 R15: 000000000000001b
diff mbox series

Patch

diff --git a/drivers/dma-buf/Makefile b/drivers/dma-buf/Makefile
index 03479da06422..822fb65b14e4 100644
--- a/drivers/dma-buf/Makefile
+++ b/drivers/dma-buf/Makefile
@@ -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
diff --git a/drivers/dma-buf/selftests.h b/drivers/dma-buf/selftests.h
index 5320386f02e5..12241b2c03f2 100644
--- a/drivers/dma-buf/selftests.h
+++ b/drivers/dma-buf/selftests.h
@@ -11,3 +11,4 @@ 
  */
 selftest(sanitycheck, __sanitycheck__) /* keep first (igt selfcheck) */
 selftest(dma_fence, dma_fence)
+selftest(dma_fence_array, dma_fence_array)
diff --git a/drivers/dma-buf/st-dma-fence-array.c b/drivers/dma-buf/st-dma-fence-array.c
new file mode 100644
index 000000000000..685a564a2826
--- /dev/null
+++ b/drivers/dma-buf/st-dma-fence-array.c
@@ -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;
+}