12989f645SChris Wilson /* SPDX-License-Identifier: MIT */
22989f645SChris Wilson 
32989f645SChris Wilson /*
42989f645SChris Wilson  * Copyright © 2019 Intel Corporation
52989f645SChris Wilson  */
62989f645SChris Wilson 
72989f645SChris Wilson #include <linux/delay.h>
82989f645SChris Wilson #include <linux/dma-fence.h>
92989f645SChris Wilson #include <linux/kernel.h>
102989f645SChris Wilson #include <linux/kthread.h>
112989f645SChris Wilson #include <linux/sched/signal.h>
122989f645SChris Wilson #include <linux/slab.h>
132989f645SChris Wilson #include <linux/spinlock.h>
142989f645SChris Wilson 
152989f645SChris Wilson #include "selftest.h"
162989f645SChris Wilson 
172989f645SChris Wilson static struct kmem_cache *slab_fences;
182989f645SChris Wilson 
192989f645SChris Wilson static struct mock_fence {
202989f645SChris Wilson 	struct dma_fence base;
212989f645SChris Wilson 	struct spinlock lock;
to_mock_fence(struct dma_fence * f)222989f645SChris Wilson } *to_mock_fence(struct dma_fence *f) {
232989f645SChris Wilson 	return container_of(f, struct mock_fence, base);
242989f645SChris Wilson }
252989f645SChris Wilson 
mock_name(struct dma_fence * f)262989f645SChris Wilson static const char *mock_name(struct dma_fence *f)
272989f645SChris Wilson {
282989f645SChris Wilson 	return "mock";
292989f645SChris Wilson }
302989f645SChris Wilson 
mock_fence_release(struct dma_fence * f)312989f645SChris Wilson static void mock_fence_release(struct dma_fence *f)
322989f645SChris Wilson {
332989f645SChris Wilson 	kmem_cache_free(slab_fences, to_mock_fence(f));
342989f645SChris Wilson }
352989f645SChris Wilson 
362989f645SChris Wilson struct wait_cb {
372989f645SChris Wilson 	struct dma_fence_cb cb;
382989f645SChris Wilson 	struct task_struct *task;
392989f645SChris Wilson };
402989f645SChris Wilson 
mock_wakeup(struct dma_fence * f,struct dma_fence_cb * cb)412989f645SChris Wilson static void mock_wakeup(struct dma_fence *f, struct dma_fence_cb *cb)
422989f645SChris Wilson {
432989f645SChris Wilson 	wake_up_process(container_of(cb, struct wait_cb, cb)->task);
442989f645SChris Wilson }
452989f645SChris Wilson 
mock_wait(struct dma_fence * f,bool intr,long timeout)462989f645SChris Wilson static long mock_wait(struct dma_fence *f, bool intr, long timeout)
472989f645SChris Wilson {
482989f645SChris Wilson 	const int state = intr ? TASK_INTERRUPTIBLE : TASK_UNINTERRUPTIBLE;
492989f645SChris Wilson 	struct wait_cb cb = { .task = current };
502989f645SChris Wilson 
512989f645SChris Wilson 	if (dma_fence_add_callback(f, &cb.cb, mock_wakeup))
522989f645SChris Wilson 		return timeout;
532989f645SChris Wilson 
542989f645SChris Wilson 	while (timeout) {
552989f645SChris Wilson 		set_current_state(state);
562989f645SChris Wilson 
572989f645SChris Wilson 		if (test_bit(DMA_FENCE_FLAG_SIGNALED_BIT, &f->flags))
582989f645SChris Wilson 			break;
592989f645SChris Wilson 
602989f645SChris Wilson 		if (signal_pending_state(state, current))
612989f645SChris Wilson 			break;
622989f645SChris Wilson 
632989f645SChris Wilson 		timeout = schedule_timeout(timeout);
642989f645SChris Wilson 	}
652989f645SChris Wilson 	__set_current_state(TASK_RUNNING);
662989f645SChris Wilson 
672989f645SChris Wilson 	if (!dma_fence_remove_callback(f, &cb.cb))
682989f645SChris Wilson 		return timeout;
692989f645SChris Wilson 
702989f645SChris Wilson 	if (signal_pending_state(state, current))
712989f645SChris Wilson 		return -ERESTARTSYS;
722989f645SChris Wilson 
732989f645SChris Wilson 	return -ETIME;
742989f645SChris Wilson }
752989f645SChris Wilson 
762989f645SChris Wilson static const struct dma_fence_ops mock_ops = {
772989f645SChris Wilson 	.get_driver_name = mock_name,
782989f645SChris Wilson 	.get_timeline_name = mock_name,
792989f645SChris Wilson 	.wait = mock_wait,
802989f645SChris Wilson 	.release = mock_fence_release,
812989f645SChris Wilson };
822989f645SChris Wilson 
mock_fence(void)832989f645SChris Wilson static struct dma_fence *mock_fence(void)
842989f645SChris Wilson {
852989f645SChris Wilson 	struct mock_fence *f;
862989f645SChris Wilson 
872989f645SChris Wilson 	f = kmem_cache_alloc(slab_fences, GFP_KERNEL);
882989f645SChris Wilson 	if (!f)
892989f645SChris Wilson 		return NULL;
902989f645SChris Wilson 
912989f645SChris Wilson 	spin_lock_init(&f->lock);
922989f645SChris Wilson 	dma_fence_init(&f->base, &mock_ops, &f->lock, 0, 0);
932989f645SChris Wilson 
942989f645SChris Wilson 	return &f->base;
952989f645SChris Wilson }
962989f645SChris Wilson 
sanitycheck(void * arg)972989f645SChris Wilson static int sanitycheck(void *arg)
982989f645SChris Wilson {
992989f645SChris Wilson 	struct dma_fence *f;
1002989f645SChris Wilson 
1012989f645SChris Wilson 	f = mock_fence();
1022989f645SChris Wilson 	if (!f)
1032989f645SChris Wilson 		return -ENOMEM;
1042989f645SChris Wilson 
105*d62c43a9SArvind Yadav 	dma_fence_enable_sw_signaling(f);
106*d62c43a9SArvind Yadav 
1072989f645SChris Wilson 	dma_fence_signal(f);
1082989f645SChris Wilson 	dma_fence_put(f);
1092989f645SChris Wilson 
1102989f645SChris Wilson 	return 0;
1112989f645SChris Wilson }
1122989f645SChris Wilson 
test_signaling(void * arg)1132989f645SChris Wilson static int test_signaling(void *arg)
1142989f645SChris Wilson {
1152989f645SChris Wilson 	struct dma_fence *f;
1162989f645SChris Wilson 	int err = -EINVAL;
1172989f645SChris Wilson 
1182989f645SChris Wilson 	f = mock_fence();
1192989f645SChris Wilson 	if (!f)
1202989f645SChris Wilson 		return -ENOMEM;
1212989f645SChris Wilson 
122*d62c43a9SArvind Yadav 	dma_fence_enable_sw_signaling(f);
123*d62c43a9SArvind Yadav 
1242989f645SChris Wilson 	if (dma_fence_is_signaled(f)) {
1252989f645SChris Wilson 		pr_err("Fence unexpectedly signaled on creation\n");
1262989f645SChris Wilson 		goto err_free;
1272989f645SChris Wilson 	}
1282989f645SChris Wilson 
1292989f645SChris Wilson 	if (dma_fence_signal(f)) {
1302989f645SChris Wilson 		pr_err("Fence reported being already signaled\n");
1312989f645SChris Wilson 		goto err_free;
1322989f645SChris Wilson 	}
1332989f645SChris Wilson 
1342989f645SChris Wilson 	if (!dma_fence_is_signaled(f)) {
1352989f645SChris Wilson 		pr_err("Fence not reporting signaled\n");
1362989f645SChris Wilson 		goto err_free;
1372989f645SChris Wilson 	}
1382989f645SChris Wilson 
1392989f645SChris Wilson 	if (!dma_fence_signal(f)) {
1402989f645SChris Wilson 		pr_err("Fence reported not being already signaled\n");
1412989f645SChris Wilson 		goto err_free;
1422989f645SChris Wilson 	}
1432989f645SChris Wilson 
1442989f645SChris Wilson 	err = 0;
1452989f645SChris Wilson err_free:
1462989f645SChris Wilson 	dma_fence_put(f);
1472989f645SChris Wilson 	return err;
1482989f645SChris Wilson }
1492989f645SChris Wilson 
1502989f645SChris Wilson struct simple_cb {
1512989f645SChris Wilson 	struct dma_fence_cb cb;
1522989f645SChris Wilson 	bool seen;
1532989f645SChris Wilson };
1542989f645SChris Wilson 
simple_callback(struct dma_fence * f,struct dma_fence_cb * cb)1552989f645SChris Wilson static void simple_callback(struct dma_fence *f, struct dma_fence_cb *cb)
1562989f645SChris Wilson {
1572989f645SChris Wilson 	smp_store_mb(container_of(cb, struct simple_cb, cb)->seen, true);
1582989f645SChris Wilson }
1592989f645SChris Wilson 
test_add_callback(void * arg)1602989f645SChris Wilson static int test_add_callback(void *arg)
1612989f645SChris Wilson {
1622989f645SChris Wilson 	struct simple_cb cb = {};
1632989f645SChris Wilson 	struct dma_fence *f;
1642989f645SChris Wilson 	int err = -EINVAL;
1652989f645SChris Wilson 
1662989f645SChris Wilson 	f = mock_fence();
1672989f645SChris Wilson 	if (!f)
1682989f645SChris Wilson 		return -ENOMEM;
1692989f645SChris Wilson 
1702989f645SChris Wilson 	if (dma_fence_add_callback(f, &cb.cb, simple_callback)) {
1712989f645SChris Wilson 		pr_err("Failed to add callback, fence already signaled!\n");
1722989f645SChris Wilson 		goto err_free;
1732989f645SChris Wilson 	}
1742989f645SChris Wilson 
1752989f645SChris Wilson 	dma_fence_signal(f);
1762989f645SChris Wilson 	if (!cb.seen) {
1772989f645SChris Wilson 		pr_err("Callback failed!\n");
1782989f645SChris Wilson 		goto err_free;
1792989f645SChris Wilson 	}
1802989f645SChris Wilson 
1812989f645SChris Wilson 	err = 0;
1822989f645SChris Wilson err_free:
1832989f645SChris Wilson 	dma_fence_put(f);
1842989f645SChris Wilson 	return err;
1852989f645SChris Wilson }
1862989f645SChris Wilson 
test_late_add_callback(void * arg)1872989f645SChris Wilson static int test_late_add_callback(void *arg)
1882989f645SChris Wilson {
1892989f645SChris Wilson 	struct simple_cb cb = {};
1902989f645SChris Wilson 	struct dma_fence *f;
1912989f645SChris Wilson 	int err = -EINVAL;
1922989f645SChris Wilson 
1932989f645SChris Wilson 	f = mock_fence();
1942989f645SChris Wilson 	if (!f)
1952989f645SChris Wilson 		return -ENOMEM;
1962989f645SChris Wilson 
197*d62c43a9SArvind Yadav 	dma_fence_enable_sw_signaling(f);
198*d62c43a9SArvind Yadav 
1992989f645SChris Wilson 	dma_fence_signal(f);
2002989f645SChris Wilson 
2012989f645SChris Wilson 	if (!dma_fence_add_callback(f, &cb.cb, simple_callback)) {
2022989f645SChris Wilson 		pr_err("Added callback, but fence was already signaled!\n");
2032989f645SChris Wilson 		goto err_free;
2042989f645SChris Wilson 	}
2052989f645SChris Wilson 
2062989f645SChris Wilson 	dma_fence_signal(f);
2072989f645SChris Wilson 	if (cb.seen) {
2082989f645SChris Wilson 		pr_err("Callback called after failed attachment !\n");
2092989f645SChris Wilson 		goto err_free;
2102989f645SChris Wilson 	}
2112989f645SChris Wilson 
2122989f645SChris Wilson 	err = 0;
2132989f645SChris Wilson err_free:
2142989f645SChris Wilson 	dma_fence_put(f);
2152989f645SChris Wilson 	return err;
2162989f645SChris Wilson }
2172989f645SChris Wilson 
test_rm_callback(void * arg)2182989f645SChris Wilson static int test_rm_callback(void *arg)
2192989f645SChris Wilson {
2202989f645SChris Wilson 	struct simple_cb cb = {};
2212989f645SChris Wilson 	struct dma_fence *f;
2222989f645SChris Wilson 	int err = -EINVAL;
2232989f645SChris Wilson 
2242989f645SChris Wilson 	f = mock_fence();
2252989f645SChris Wilson 	if (!f)
2262989f645SChris Wilson 		return -ENOMEM;
2272989f645SChris Wilson 
2282989f645SChris Wilson 	if (dma_fence_add_callback(f, &cb.cb, simple_callback)) {
2292989f645SChris Wilson 		pr_err("Failed to add callback, fence already signaled!\n");
2302989f645SChris Wilson 		goto err_free;
2312989f645SChris Wilson 	}
2322989f645SChris Wilson 
2332989f645SChris Wilson 	if (!dma_fence_remove_callback(f, &cb.cb)) {
2342989f645SChris Wilson 		pr_err("Failed to remove callback!\n");
2352989f645SChris Wilson 		goto err_free;
2362989f645SChris Wilson 	}
2372989f645SChris Wilson 
2382989f645SChris Wilson 	dma_fence_signal(f);
2392989f645SChris Wilson 	if (cb.seen) {
2402989f645SChris Wilson 		pr_err("Callback still signaled after removal!\n");
2412989f645SChris Wilson 		goto err_free;
2422989f645SChris Wilson 	}
2432989f645SChris Wilson 
2442989f645SChris Wilson 	err = 0;
2452989f645SChris Wilson err_free:
2462989f645SChris Wilson 	dma_fence_put(f);
2472989f645SChris Wilson 	return err;
2482989f645SChris Wilson }
2492989f645SChris Wilson 
test_late_rm_callback(void * arg)2502989f645SChris Wilson static int test_late_rm_callback(void *arg)
2512989f645SChris Wilson {
2522989f645SChris Wilson 	struct simple_cb cb = {};
2532989f645SChris Wilson 	struct dma_fence *f;
2542989f645SChris Wilson 	int err = -EINVAL;
2552989f645SChris Wilson 
2562989f645SChris Wilson 	f = mock_fence();
2572989f645SChris Wilson 	if (!f)
2582989f645SChris Wilson 		return -ENOMEM;
2592989f645SChris Wilson 
2602989f645SChris Wilson 	if (dma_fence_add_callback(f, &cb.cb, simple_callback)) {
2612989f645SChris Wilson 		pr_err("Failed to add callback, fence already signaled!\n");
2622989f645SChris Wilson 		goto err_free;
2632989f645SChris Wilson 	}
2642989f645SChris Wilson 
2652989f645SChris Wilson 	dma_fence_signal(f);
2662989f645SChris Wilson 	if (!cb.seen) {
2672989f645SChris Wilson 		pr_err("Callback failed!\n");
2682989f645SChris Wilson 		goto err_free;
2692989f645SChris Wilson 	}
2702989f645SChris Wilson 
2712989f645SChris Wilson 	if (dma_fence_remove_callback(f, &cb.cb)) {
2722989f645SChris Wilson 		pr_err("Callback removal succeed after being executed!\n");
2732989f645SChris Wilson 		goto err_free;
2742989f645SChris Wilson 	}
2752989f645SChris Wilson 
2762989f645SChris Wilson 	err = 0;
2772989f645SChris Wilson err_free:
2782989f645SChris Wilson 	dma_fence_put(f);
2792989f645SChris Wilson 	return err;
2802989f645SChris Wilson }
2812989f645SChris Wilson 
test_status(void * arg)2822989f645SChris Wilson static int test_status(void *arg)
2832989f645SChris Wilson {
2842989f645SChris Wilson 	struct dma_fence *f;
2852989f645SChris Wilson 	int err = -EINVAL;
2862989f645SChris Wilson 
2872989f645SChris Wilson 	f = mock_fence();
2882989f645SChris Wilson 	if (!f)
2892989f645SChris Wilson 		return -ENOMEM;
2902989f645SChris Wilson 
291*d62c43a9SArvind Yadav 	dma_fence_enable_sw_signaling(f);
292*d62c43a9SArvind Yadav 
2932989f645SChris Wilson 	if (dma_fence_get_status(f)) {
2942989f645SChris Wilson 		pr_err("Fence unexpectedly has signaled status on creation\n");
2952989f645SChris Wilson 		goto err_free;
2962989f645SChris Wilson 	}
2972989f645SChris Wilson 
2982989f645SChris Wilson 	dma_fence_signal(f);
2992989f645SChris Wilson 	if (!dma_fence_get_status(f)) {
3002989f645SChris Wilson 		pr_err("Fence not reporting signaled status\n");
3012989f645SChris Wilson 		goto err_free;
3022989f645SChris Wilson 	}
3032989f645SChris Wilson 
3042989f645SChris Wilson 	err = 0;
3052989f645SChris Wilson err_free:
3062989f645SChris Wilson 	dma_fence_put(f);
3072989f645SChris Wilson 	return err;
3082989f645SChris Wilson }
3092989f645SChris Wilson 
test_error(void * arg)3102989f645SChris Wilson static int test_error(void *arg)
3112989f645SChris Wilson {
3122989f645SChris Wilson 	struct dma_fence *f;
3132989f645SChris Wilson 	int err = -EINVAL;
3142989f645SChris Wilson 
3152989f645SChris Wilson 	f = mock_fence();
3162989f645SChris Wilson 	if (!f)
3172989f645SChris Wilson 		return -ENOMEM;
3182989f645SChris Wilson 
319*d62c43a9SArvind Yadav 	dma_fence_enable_sw_signaling(f);
320*d62c43a9SArvind Yadav 
3212989f645SChris Wilson 	dma_fence_set_error(f, -EIO);
3222989f645SChris Wilson 
3232989f645SChris Wilson 	if (dma_fence_get_status(f)) {
3242989f645SChris Wilson 		pr_err("Fence unexpectedly has error status before signal\n");
3252989f645SChris Wilson 		goto err_free;
3262989f645SChris Wilson 	}
3272989f645SChris Wilson 
3282989f645SChris Wilson 	dma_fence_signal(f);
3292989f645SChris Wilson 	if (dma_fence_get_status(f) != -EIO) {
3302989f645SChris Wilson 		pr_err("Fence not reporting error status, got %d\n",
3312989f645SChris Wilson 		       dma_fence_get_status(f));
3322989f645SChris Wilson 		goto err_free;
3332989f645SChris Wilson 	}
3342989f645SChris Wilson 
3352989f645SChris Wilson 	err = 0;
3362989f645SChris Wilson err_free:
3372989f645SChris Wilson 	dma_fence_put(f);
3382989f645SChris Wilson 	return err;
3392989f645SChris Wilson }
3402989f645SChris Wilson 
test_wait(void * arg)3412989f645SChris Wilson static int test_wait(void *arg)
3422989f645SChris Wilson {
3432989f645SChris Wilson 	struct dma_fence *f;
3442989f645SChris Wilson 	int err = -EINVAL;
3452989f645SChris Wilson 
3462989f645SChris Wilson 	f = mock_fence();
3472989f645SChris Wilson 	if (!f)
3482989f645SChris Wilson 		return -ENOMEM;
3492989f645SChris Wilson 
350*d62c43a9SArvind Yadav 	dma_fence_enable_sw_signaling(f);
351*d62c43a9SArvind Yadav 
3522989f645SChris Wilson 	if (dma_fence_wait_timeout(f, false, 0) != -ETIME) {
3532989f645SChris Wilson 		pr_err("Wait reported complete before being signaled\n");
3542989f645SChris Wilson 		goto err_free;
3552989f645SChris Wilson 	}
3562989f645SChris Wilson 
3572989f645SChris Wilson 	dma_fence_signal(f);
3582989f645SChris Wilson 
3592989f645SChris Wilson 	if (dma_fence_wait_timeout(f, false, 0) != 0) {
3602989f645SChris Wilson 		pr_err("Wait reported incomplete after being signaled\n");
3612989f645SChris Wilson 		goto err_free;
3622989f645SChris Wilson 	}
3632989f645SChris Wilson 
3642989f645SChris Wilson 	err = 0;
3652989f645SChris Wilson err_free:
3662989f645SChris Wilson 	dma_fence_signal(f);
3672989f645SChris Wilson 	dma_fence_put(f);
3682989f645SChris Wilson 	return err;
3692989f645SChris Wilson }
3702989f645SChris Wilson 
3712989f645SChris Wilson struct wait_timer {
3722989f645SChris Wilson 	struct timer_list timer;
3732989f645SChris Wilson 	struct dma_fence *f;
3742989f645SChris Wilson };
3752989f645SChris Wilson 
wait_timer(struct timer_list * timer)3762989f645SChris Wilson static void wait_timer(struct timer_list *timer)
3772989f645SChris Wilson {
3782989f645SChris Wilson 	struct wait_timer *wt = from_timer(wt, timer, timer);
3792989f645SChris Wilson 
3802989f645SChris Wilson 	dma_fence_signal(wt->f);
3812989f645SChris Wilson }
3822989f645SChris Wilson 
test_wait_timeout(void * arg)3832989f645SChris Wilson static int test_wait_timeout(void *arg)
3842989f645SChris Wilson {
3852989f645SChris Wilson 	struct wait_timer wt;
3862989f645SChris Wilson 	int err = -EINVAL;
3872989f645SChris Wilson 
3886ac3a0ebSChris Wilson 	timer_setup_on_stack(&wt.timer, wait_timer, 0);
3892989f645SChris Wilson 
3902989f645SChris Wilson 	wt.f = mock_fence();
3912989f645SChris Wilson 	if (!wt.f)
3922989f645SChris Wilson 		return -ENOMEM;
3932989f645SChris Wilson 
394*d62c43a9SArvind Yadav 	dma_fence_enable_sw_signaling(wt.f);
395*d62c43a9SArvind Yadav 
3962989f645SChris Wilson 	if (dma_fence_wait_timeout(wt.f, false, 1) != -ETIME) {
3972989f645SChris Wilson 		pr_err("Wait reported complete before being signaled\n");
3982989f645SChris Wilson 		goto err_free;
3992989f645SChris Wilson 	}
4002989f645SChris Wilson 
4012989f645SChris Wilson 	mod_timer(&wt.timer, jiffies + 1);
4022989f645SChris Wilson 
4032989f645SChris Wilson 	if (dma_fence_wait_timeout(wt.f, false, 2) == -ETIME) {
4042989f645SChris Wilson 		if (timer_pending(&wt.timer)) {
4052989f645SChris Wilson 			pr_notice("Timer did not fire within the jiffie!\n");
4062989f645SChris Wilson 			err = 0; /* not our fault! */
4072989f645SChris Wilson 		} else {
4082989f645SChris Wilson 			pr_err("Wait reported incomplete after timeout\n");
4092989f645SChris Wilson 		}
4102989f645SChris Wilson 		goto err_free;
4112989f645SChris Wilson 	}
4122989f645SChris Wilson 
4132989f645SChris Wilson 	err = 0;
4142989f645SChris Wilson err_free:
4152989f645SChris Wilson 	del_timer_sync(&wt.timer);
4166ac3a0ebSChris Wilson 	destroy_timer_on_stack(&wt.timer);
4172989f645SChris Wilson 	dma_fence_signal(wt.f);
4182989f645SChris Wilson 	dma_fence_put(wt.f);
4192989f645SChris Wilson 	return err;
4202989f645SChris Wilson }
4212989f645SChris Wilson 
test_stub(void * arg)4222989f645SChris Wilson static int test_stub(void *arg)
4232989f645SChris Wilson {
4242989f645SChris Wilson 	struct dma_fence *f[64];
4252989f645SChris Wilson 	int err = -EINVAL;
4262989f645SChris Wilson 	int i;
4272989f645SChris Wilson 
4282989f645SChris Wilson 	for (i = 0; i < ARRAY_SIZE(f); i++) {
4292989f645SChris Wilson 		f[i] = dma_fence_get_stub();
4302989f645SChris Wilson 		if (!dma_fence_is_signaled(f[i])) {
4312989f645SChris Wilson 			pr_err("Obtained unsignaled stub fence!\n");
4322989f645SChris Wilson 			goto err;
4332989f645SChris Wilson 		}
4342989f645SChris Wilson 	}
4352989f645SChris Wilson 
4362989f645SChris Wilson 	err = 0;
4372989f645SChris Wilson err:
4382989f645SChris Wilson 	while (i--)
4392989f645SChris Wilson 		dma_fence_put(f[i]);
4402989f645SChris Wilson 	return err;
4412989f645SChris Wilson }
4422989f645SChris Wilson 
4432989f645SChris Wilson /* Now off to the races! */
4442989f645SChris Wilson 
4452989f645SChris Wilson struct race_thread {
4462989f645SChris Wilson 	struct dma_fence __rcu **fences;
4472989f645SChris Wilson 	struct task_struct *task;
4482989f645SChris Wilson 	bool before;
4492989f645SChris Wilson 	int id;
4502989f645SChris Wilson };
4512989f645SChris Wilson 
__wait_for_callbacks(struct dma_fence * f)4522989f645SChris Wilson static void __wait_for_callbacks(struct dma_fence *f)
4532989f645SChris Wilson {
4542989f645SChris Wilson 	spin_lock_irq(f->lock);
4552989f645SChris Wilson 	spin_unlock_irq(f->lock);
4562989f645SChris Wilson }
4572989f645SChris Wilson 
thread_signal_callback(void * arg)4582989f645SChris Wilson static int thread_signal_callback(void *arg)
4592989f645SChris Wilson {
4602989f645SChris Wilson 	const struct race_thread *t = arg;
4612989f645SChris Wilson 	unsigned long pass = 0;
4622989f645SChris Wilson 	unsigned long miss = 0;
4632989f645SChris Wilson 	int err = 0;
4642989f645SChris Wilson 
4652989f645SChris Wilson 	while (!err && !kthread_should_stop()) {
4662989f645SChris Wilson 		struct dma_fence *f1, *f2;
4672989f645SChris Wilson 		struct simple_cb cb;
4682989f645SChris Wilson 
4692989f645SChris Wilson 		f1 = mock_fence();
4702989f645SChris Wilson 		if (!f1) {
4712989f645SChris Wilson 			err = -ENOMEM;
4722989f645SChris Wilson 			break;
4732989f645SChris Wilson 		}
4742989f645SChris Wilson 
475*d62c43a9SArvind Yadav 		dma_fence_enable_sw_signaling(f1);
476*d62c43a9SArvind Yadav 
4772989f645SChris Wilson 		rcu_assign_pointer(t->fences[t->id], f1);
4782989f645SChris Wilson 		smp_wmb();
4792989f645SChris Wilson 
4802989f645SChris Wilson 		rcu_read_lock();
4812989f645SChris Wilson 		do {
4822989f645SChris Wilson 			f2 = dma_fence_get_rcu_safe(&t->fences[!t->id]);
4832989f645SChris Wilson 		} while (!f2 && !kthread_should_stop());
4842989f645SChris Wilson 		rcu_read_unlock();
4852989f645SChris Wilson 
4862989f645SChris Wilson 		if (t->before)
4872989f645SChris Wilson 			dma_fence_signal(f1);
4882989f645SChris Wilson 
4892989f645SChris Wilson 		smp_store_mb(cb.seen, false);
49046d4a938SJoe Perches 		if (!f2 ||
49146d4a938SJoe Perches 		    dma_fence_add_callback(f2, &cb.cb, simple_callback)) {
49246d4a938SJoe Perches 			miss++;
49346d4a938SJoe Perches 			cb.seen = true;
49446d4a938SJoe Perches 		}
4952989f645SChris Wilson 
4962989f645SChris Wilson 		if (!t->before)
4972989f645SChris Wilson 			dma_fence_signal(f1);
4982989f645SChris Wilson 
4992989f645SChris Wilson 		if (!cb.seen) {
5002989f645SChris Wilson 			dma_fence_wait(f2, false);
5012989f645SChris Wilson 			__wait_for_callbacks(f2);
5022989f645SChris Wilson 		}
5032989f645SChris Wilson 
5042989f645SChris Wilson 		if (!READ_ONCE(cb.seen)) {
5052989f645SChris Wilson 			pr_err("Callback not seen on thread %d, pass %lu (%lu misses), signaling %s add_callback; fence signaled? %s\n",
5062989f645SChris Wilson 			       t->id, pass, miss,
5072989f645SChris Wilson 			       t->before ? "before" : "after",
5082989f645SChris Wilson 			       dma_fence_is_signaled(f2) ? "yes" : "no");
5092989f645SChris Wilson 			err = -EINVAL;
5102989f645SChris Wilson 		}
5112989f645SChris Wilson 
5122989f645SChris Wilson 		dma_fence_put(f2);
5132989f645SChris Wilson 
5142989f645SChris Wilson 		rcu_assign_pointer(t->fences[t->id], NULL);
5152989f645SChris Wilson 		smp_wmb();
5162989f645SChris Wilson 
5172989f645SChris Wilson 		dma_fence_put(f1);
5182989f645SChris Wilson 
5192989f645SChris Wilson 		pass++;
5202989f645SChris Wilson 	}
5212989f645SChris Wilson 
5222989f645SChris Wilson 	pr_info("%s[%d] completed %lu passes, %lu misses\n",
5232989f645SChris Wilson 		__func__, t->id, pass, miss);
5242989f645SChris Wilson 	return err;
5252989f645SChris Wilson }
5262989f645SChris Wilson 
race_signal_callback(void * arg)5272989f645SChris Wilson static int race_signal_callback(void *arg)
5282989f645SChris Wilson {
5292989f645SChris Wilson 	struct dma_fence __rcu *f[2] = {};
5302989f645SChris Wilson 	int ret = 0;
5312989f645SChris Wilson 	int pass;
5322989f645SChris Wilson 
5332989f645SChris Wilson 	for (pass = 0; !ret && pass <= 1; pass++) {
5342989f645SChris Wilson 		struct race_thread t[2];
5352989f645SChris Wilson 		int i;
5362989f645SChris Wilson 
5372989f645SChris Wilson 		for (i = 0; i < ARRAY_SIZE(t); i++) {
5382989f645SChris Wilson 			t[i].fences = f;
5392989f645SChris Wilson 			t[i].id = i;
5402989f645SChris Wilson 			t[i].before = pass;
5412989f645SChris Wilson 			t[i].task = kthread_run(thread_signal_callback, &t[i],
5422989f645SChris Wilson 						"dma-fence:%d", i);
5432989f645SChris Wilson 			get_task_struct(t[i].task);
5442989f645SChris Wilson 		}
5452989f645SChris Wilson 
5462989f645SChris Wilson 		msleep(50);
5472989f645SChris Wilson 
5482989f645SChris Wilson 		for (i = 0; i < ARRAY_SIZE(t); i++) {
5492989f645SChris Wilson 			int err;
5502989f645SChris Wilson 
5512989f645SChris Wilson 			err = kthread_stop(t[i].task);
5522989f645SChris Wilson 			if (err && !ret)
5532989f645SChris Wilson 				ret = err;
5542989f645SChris Wilson 
5552989f645SChris Wilson 			put_task_struct(t[i].task);
5562989f645SChris Wilson 		}
5572989f645SChris Wilson 	}
5582989f645SChris Wilson 
5592989f645SChris Wilson 	return ret;
5602989f645SChris Wilson }
5612989f645SChris Wilson 
dma_fence(void)5622989f645SChris Wilson int dma_fence(void)
5632989f645SChris Wilson {
5642989f645SChris Wilson 	static const struct subtest tests[] = {
5652989f645SChris Wilson 		SUBTEST(sanitycheck),
5662989f645SChris Wilson 		SUBTEST(test_signaling),
5672989f645SChris Wilson 		SUBTEST(test_add_callback),
5682989f645SChris Wilson 		SUBTEST(test_late_add_callback),
5692989f645SChris Wilson 		SUBTEST(test_rm_callback),
5702989f645SChris Wilson 		SUBTEST(test_late_rm_callback),
5712989f645SChris Wilson 		SUBTEST(test_status),
5722989f645SChris Wilson 		SUBTEST(test_error),
5732989f645SChris Wilson 		SUBTEST(test_wait),
5742989f645SChris Wilson 		SUBTEST(test_wait_timeout),
5752989f645SChris Wilson 		SUBTEST(test_stub),
5762989f645SChris Wilson 		SUBTEST(race_signal_callback),
5772989f645SChris Wilson 	};
5782989f645SChris Wilson 	int ret;
5792989f645SChris Wilson 
580ea4e537aSChris Wilson 	pr_info("sizeof(dma_fence)=%zu\n", sizeof(struct dma_fence));
5812989f645SChris Wilson 
5822989f645SChris Wilson 	slab_fences = KMEM_CACHE(mock_fence,
5832989f645SChris Wilson 				 SLAB_TYPESAFE_BY_RCU |
5842989f645SChris Wilson 				 SLAB_HWCACHE_ALIGN);
5852989f645SChris Wilson 	if (!slab_fences)
5862989f645SChris Wilson 		return -ENOMEM;
5872989f645SChris Wilson 
5882989f645SChris Wilson 	ret = subtests(tests, NULL);
5892989f645SChris Wilson 
5902989f645SChris Wilson 	kmem_cache_destroy(slab_fences);
5912989f645SChris Wilson 
5922989f645SChris Wilson 	return ret;
5932989f645SChris Wilson }
594