xref: /dflybsd-src/sys/dev/drm/linux_fence.c (revision 789731325bde747251c28a37e0a00ed4efb88c46)
127d73c48SFrançois Tigeot /*
23f2dd94aSFrançois Tigeot  * Copyright (c) 2019-2020 Jonathan Gray <jsg@openbsd.org>
327d73c48SFrançois Tigeot  * Copyright (c) 2020 François Tigeot <ftigeot@wolfpond.org>
4425e5ce3SFrançois Tigeot  *
527d73c48SFrançois Tigeot  * Permission is hereby granted, free of charge, to any person obtaining a
627d73c48SFrançois Tigeot  * copy of this software and associated documentation files (the "Software"),
727d73c48SFrançois Tigeot  * to deal in the Software without restriction, including without limitation
827d73c48SFrançois Tigeot  * the rights to use, copy, modify, merge, publish, distribute, sublicense,
927d73c48SFrançois Tigeot  * and/or sell copies of the Software, and to permit persons to whom the
1027d73c48SFrançois Tigeot  * Software is furnished to do so, subject to the following conditions:
11425e5ce3SFrançois Tigeot  *
1227d73c48SFrançois Tigeot  * The above copyright notice and this permission notice (including the next
1327d73c48SFrançois Tigeot  * paragraph) shall be included in all copies or substantial portions of the
1427d73c48SFrançois Tigeot  * Software.
15425e5ce3SFrançois Tigeot  *
1627d73c48SFrançois Tigeot  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
1727d73c48SFrançois Tigeot  * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
1827d73c48SFrançois Tigeot  * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL
1927d73c48SFrançois Tigeot  * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
2027d73c48SFrançois Tigeot  * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
2127d73c48SFrançois Tigeot  * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
2227d73c48SFrançois Tigeot  * SOFTWARE.
23425e5ce3SFrançois Tigeot  */
24425e5ce3SFrançois Tigeot 
25425e5ce3SFrançois Tigeot #include <linux/slab.h>
2627d73c48SFrançois Tigeot #include <linux/dma-fence.h>
27425e5ce3SFrançois Tigeot 
28425e5ce3SFrançois Tigeot void
dma_fence_init(struct dma_fence * fence,const struct dma_fence_ops * ops,spinlock_t * lock,u64 context,unsigned seqno)2927d73c48SFrançois Tigeot dma_fence_init(struct dma_fence *fence, const struct dma_fence_ops *ops,
3027d73c48SFrançois Tigeot     spinlock_t *lock, u64 context, unsigned seqno)
31425e5ce3SFrançois Tigeot {
3227d73c48SFrançois Tigeot 	fence->ops = ops;
33425e5ce3SFrançois Tigeot 	fence->lock = lock;
34425e5ce3SFrançois Tigeot 	fence->context = context;
35425e5ce3SFrançois Tigeot 	fence->seqno = seqno;
3627d73c48SFrançois Tigeot 	fence->flags = 0;
3727d73c48SFrançois Tigeot 	fence->error = 0;
3827d73c48SFrançois Tigeot 	kref_init(&fence->refcount);
3927d73c48SFrançois Tigeot 	INIT_LIST_HEAD(&fence->cb_list);
40425e5ce3SFrançois Tigeot }
41425e5ce3SFrançois Tigeot 
42425e5ce3SFrançois Tigeot void
dma_fence_release(struct kref * ref)4327d73c48SFrançois Tigeot dma_fence_release(struct kref *ref)
44425e5ce3SFrançois Tigeot {
4527d73c48SFrançois Tigeot 	struct dma_fence *fence = container_of(ref, struct dma_fence, refcount);
46425e5ce3SFrançois Tigeot 
4727d73c48SFrançois Tigeot 	if (fence->ops && fence->ops->release)
4827d73c48SFrançois Tigeot 		fence->ops->release(fence);
4927d73c48SFrançois Tigeot 	else
50425e5ce3SFrançois Tigeot 		kfree(fence);
51425e5ce3SFrançois Tigeot }
52425e5ce3SFrançois Tigeot 
5327d73c48SFrançois Tigeot long
dma_fence_wait_timeout(struct dma_fence * fence,bool intr,long timeout)5427d73c48SFrançois Tigeot dma_fence_wait_timeout(struct dma_fence *fence, bool intr, long timeout)
55425e5ce3SFrançois Tigeot {
5627d73c48SFrançois Tigeot 	if (timeout < 0)
5727d73c48SFrançois Tigeot 		return -EINVAL;
5827d73c48SFrançois Tigeot 
5927d73c48SFrançois Tigeot 	if (fence->ops->wait)
6027d73c48SFrançois Tigeot 		return fence->ops->wait(fence, intr, timeout);
6127d73c48SFrançois Tigeot 	else
6227d73c48SFrançois Tigeot 		return dma_fence_default_wait(fence, intr, timeout);
63425e5ce3SFrançois Tigeot }
64425e5ce3SFrançois Tigeot 
653f2dd94aSFrançois Tigeot static atomic64_t drm_fence_context_count = ATOMIC_INIT(1);
66425e5ce3SFrançois Tigeot 
673f2dd94aSFrançois Tigeot u64
dma_fence_context_alloc(unsigned num)683f2dd94aSFrançois Tigeot dma_fence_context_alloc(unsigned num)
693f2dd94aSFrançois Tigeot {
703f2dd94aSFrançois Tigeot 	return atomic64_add_return(num, &drm_fence_context_count) - num;
71425e5ce3SFrançois Tigeot }
72425e5ce3SFrançois Tigeot 
7327d73c48SFrançois Tigeot struct default_wait_cb {
7427d73c48SFrançois Tigeot 	struct dma_fence_cb base;
7527d73c48SFrançois Tigeot 	struct task_struct *task;
76425e5ce3SFrançois Tigeot };
77425e5ce3SFrançois Tigeot 
78425e5ce3SFrançois Tigeot static void
dma_fence_default_wait_cb(struct dma_fence * fence,struct dma_fence_cb * cb)7927d73c48SFrançois Tigeot dma_fence_default_wait_cb(struct dma_fence *fence, struct dma_fence_cb *cb)
80425e5ce3SFrançois Tigeot {
8127d73c48SFrançois Tigeot 	struct default_wait_cb *wait =
8227d73c48SFrançois Tigeot 		container_of(cb, struct default_wait_cb, base);
83425e5ce3SFrançois Tigeot 
8427d73c48SFrançois Tigeot 	wake_up_process(wait->task);
85425e5ce3SFrançois Tigeot }
86425e5ce3SFrançois Tigeot 
87425e5ce3SFrançois Tigeot long
dma_fence_default_wait(struct dma_fence * fence,bool intr,signed long timeout)8827d73c48SFrançois Tigeot dma_fence_default_wait(struct dma_fence *fence, bool intr, signed long timeout)
89425e5ce3SFrançois Tigeot {
9027d73c48SFrançois Tigeot 	long ret = timeout ? timeout : 1;
91*78973132SSergey Zigachev 	unsigned long end;
9227d73c48SFrançois Tigeot 	int err;
9327d73c48SFrançois Tigeot 	struct default_wait_cb cb;
9427d73c48SFrançois Tigeot 	bool was_set;
95425e5ce3SFrançois Tigeot 
9627d73c48SFrançois Tigeot 	if (test_bit(DMA_FENCE_FLAG_SIGNALED_BIT, &fence->flags))
97425e5ce3SFrançois Tigeot 		return ret;
98425e5ce3SFrançois Tigeot 
9927d73c48SFrançois Tigeot 	lockmgr(fence->lock, LK_EXCLUSIVE);
10027d73c48SFrançois Tigeot 
10127d73c48SFrançois Tigeot 	was_set = test_and_set_bit(DMA_FENCE_FLAG_ENABLE_SIGNAL_BIT,
10227d73c48SFrançois Tigeot 	    &fence->flags);
10327d73c48SFrançois Tigeot 
10427d73c48SFrançois Tigeot 	if (test_bit(DMA_FENCE_FLAG_SIGNALED_BIT, &fence->flags))
10527d73c48SFrançois Tigeot 		goto out;
10627d73c48SFrançois Tigeot 
10727d73c48SFrançois Tigeot 	if (!was_set && fence->ops->enable_signaling) {
10827d73c48SFrançois Tigeot 		if (!fence->ops->enable_signaling(fence)) {
10927d73c48SFrançois Tigeot 			dma_fence_signal_locked(fence);
11027d73c48SFrançois Tigeot 			goto out;
11127d73c48SFrançois Tigeot 		}
11227d73c48SFrançois Tigeot 	}
11327d73c48SFrançois Tigeot 
1143f2dd94aSFrançois Tigeot 	if (timeout == 0) {
1153f2dd94aSFrançois Tigeot 		ret = 0;
1163f2dd94aSFrançois Tigeot 		goto out;
1173f2dd94aSFrançois Tigeot 	}
1183f2dd94aSFrançois Tigeot 
11927d73c48SFrançois Tigeot 	cb.base.func = dma_fence_default_wait_cb;
12027d73c48SFrançois Tigeot 	cb.task = current;
12127d73c48SFrançois Tigeot 	list_add(&cb.base.node, &fence->cb_list);
12227d73c48SFrançois Tigeot 
123*78973132SSergey Zigachev 	end = jiffies + timeout;
124*78973132SSergey Zigachev 	for (ret = timeout; ret > 0; ret = MAX(0, end - jiffies)) {
125*78973132SSergey Zigachev 		if (test_bit(DMA_FENCE_FLAG_SIGNALED_BIT, &fence->flags)) {
126*78973132SSergey Zigachev 			break;
127*78973132SSergey Zigachev 		}
128*78973132SSergey Zigachev 		if (intr) {
129*78973132SSergey Zigachev 			__set_current_state(TASK_INTERRUPTIBLE);
130*78973132SSergey Zigachev 		}
131*78973132SSergey Zigachev 		else {
132*78973132SSergey Zigachev 			__set_current_state(TASK_UNINTERRUPTIBLE);
133*78973132SSergey Zigachev 		}
13427d73c48SFrançois Tigeot 		/* wake_up_process() directly uses task_struct pointers as sleep identifiers */
135*78973132SSergey Zigachev 		err = lksleep(current, fence->lock, intr ? PCATCH : 0, "dmafence", ret);
13627d73c48SFrançois Tigeot 		if (err == EINTR || err == ERESTART) {
13727d73c48SFrançois Tigeot 			ret = -ERESTARTSYS;
13827d73c48SFrançois Tigeot 			break;
13927d73c48SFrançois Tigeot 		}
14027d73c48SFrançois Tigeot 	}
14127d73c48SFrançois Tigeot 
14227d73c48SFrançois Tigeot 	if (!list_empty(&cb.base.node))
14327d73c48SFrançois Tigeot 		list_del(&cb.base.node);
144*78973132SSergey Zigachev 	__set_current_state(TASK_RUNNING);
14527d73c48SFrançois Tigeot out:
14627d73c48SFrançois Tigeot 	lockmgr(fence->lock, LK_RELEASE);
147*78973132SSergey Zigachev 	return ret;
148*78973132SSergey Zigachev }
14927d73c48SFrançois Tigeot 
150*78973132SSergey Zigachev static bool
dma_fence_test_signaled_any(struct dma_fence ** fences,uint32_t count,uint32_t * idx)151*78973132SSergey Zigachev dma_fence_test_signaled_any(struct dma_fence **fences, uint32_t count,
152*78973132SSergey Zigachev     uint32_t *idx)
153*78973132SSergey Zigachev {
154*78973132SSergey Zigachev 	int i;
155*78973132SSergey Zigachev 
156*78973132SSergey Zigachev 	for (i = 0; i < count; ++i) {
157*78973132SSergey Zigachev 		struct dma_fence *fence = fences[i];
158*78973132SSergey Zigachev 		if (test_bit(DMA_FENCE_FLAG_SIGNALED_BIT, &fence->flags)) {
159*78973132SSergey Zigachev 			if (idx)
160*78973132SSergey Zigachev 				*idx = i;
161*78973132SSergey Zigachev 			return true;
162*78973132SSergey Zigachev 		}
163*78973132SSergey Zigachev 	}
164*78973132SSergey Zigachev 	return false;
165*78973132SSergey Zigachev }
166*78973132SSergey Zigachev 
167*78973132SSergey Zigachev long
dma_fence_wait_any_timeout(struct dma_fence ** fences,uint32_t count,bool intr,long timeout,uint32_t * idx)168*78973132SSergey Zigachev dma_fence_wait_any_timeout(struct dma_fence **fences, uint32_t count,
169*78973132SSergey Zigachev     bool intr, long timeout, uint32_t *idx)
170*78973132SSergey Zigachev {
171*78973132SSergey Zigachev 	struct default_wait_cb *cb;
172*78973132SSergey Zigachev 	long ret = timeout;
173*78973132SSergey Zigachev 	unsigned long end;
174*78973132SSergey Zigachev 	int i, err;
175*78973132SSergey Zigachev 
176*78973132SSergey Zigachev 	if (timeout == 0) {
177*78973132SSergey Zigachev 		for (i = 0; i < count; i++) {
178*78973132SSergey Zigachev 			if (dma_fence_is_signaled(fences[i])) {
179*78973132SSergey Zigachev 				if (idx)
180*78973132SSergey Zigachev 					*idx = i;
181*78973132SSergey Zigachev 				return 1;
182*78973132SSergey Zigachev 			}
183*78973132SSergey Zigachev 		}
184*78973132SSergey Zigachev 		return 0;
185*78973132SSergey Zigachev 	}
186*78973132SSergey Zigachev 
187*78973132SSergey Zigachev 	cb = kcalloc(count, sizeof(struct default_wait_cb), GFP_KERNEL);
188*78973132SSergey Zigachev 	if (cb == NULL)
189*78973132SSergey Zigachev 		return -ENOMEM;
190*78973132SSergey Zigachev 
191*78973132SSergey Zigachev 	for (i = 0; i < count; i++) {
192*78973132SSergey Zigachev 		struct dma_fence *fence = fences[i];
193*78973132SSergey Zigachev 		cb[i].task = current;
194*78973132SSergey Zigachev 		if (dma_fence_add_callback(fence, &cb[i].base,
195*78973132SSergey Zigachev 		    dma_fence_default_wait_cb)) {
196*78973132SSergey Zigachev 			if (idx)
197*78973132SSergey Zigachev 				*idx = i;
198*78973132SSergey Zigachev 			goto cb_cleanup;
199*78973132SSergey Zigachev 		}
200*78973132SSergey Zigachev 	}
201*78973132SSergey Zigachev 
202*78973132SSergey Zigachev 	end = jiffies + timeout;
203*78973132SSergey Zigachev 	for (ret = timeout; ret > 0; ret = MAX(0, end - jiffies)) {
204*78973132SSergey Zigachev 		if (dma_fence_test_signaled_any(fences, count, idx))
205*78973132SSergey Zigachev 			break;
206*78973132SSergey Zigachev 		err = tsleep(current, intr ? PCATCH : 0, "dfwat", ret);
207*78973132SSergey Zigachev 		if (err == EINTR || err == ERESTART) {
208*78973132SSergey Zigachev 			ret = -ERESTARTSYS;
209*78973132SSergey Zigachev 			break;
210*78973132SSergey Zigachev 		}
211*78973132SSergey Zigachev 	}
212*78973132SSergey Zigachev 
213*78973132SSergey Zigachev cb_cleanup:
214*78973132SSergey Zigachev 	while (i-- > 0)
215*78973132SSergey Zigachev 		dma_fence_remove_callback(fences[i], &cb[i].base);
216*78973132SSergey Zigachev 	kfree(cb);
21727d73c48SFrançois Tigeot 	return ret;
21827d73c48SFrançois Tigeot }
21927d73c48SFrançois Tigeot 
22027d73c48SFrançois Tigeot int
dma_fence_signal_locked(struct dma_fence * fence)22127d73c48SFrançois Tigeot dma_fence_signal_locked(struct dma_fence *fence)
22227d73c48SFrançois Tigeot {
22327d73c48SFrançois Tigeot 	struct dma_fence_cb *cur, *tmp;
2243f2dd94aSFrançois Tigeot 	struct list_head cb_list;
22527d73c48SFrançois Tigeot 
22627d73c48SFrançois Tigeot 	if (fence == NULL)
22727d73c48SFrançois Tigeot 		return -EINVAL;
22827d73c48SFrançois Tigeot 
22927d73c48SFrançois Tigeot 	if (test_and_set_bit(DMA_FENCE_FLAG_SIGNALED_BIT, &fence->flags))
23027d73c48SFrançois Tigeot 		return -EINVAL;
23127d73c48SFrançois Tigeot 
2323f2dd94aSFrançois Tigeot 	list_replace(&fence->cb_list, &cb_list);
2333f2dd94aSFrançois Tigeot 
2343f2dd94aSFrançois Tigeot 	fence->timestamp = ktime_get();
2353f2dd94aSFrançois Tigeot 	set_bit(DMA_FENCE_FLAG_TIMESTAMP_BIT, &fence->flags);
2363f2dd94aSFrançois Tigeot 
2373f2dd94aSFrançois Tigeot 	list_for_each_entry_safe(cur, tmp, &cb_list, node) {
2383f2dd94aSFrançois Tigeot 		INIT_LIST_HEAD(&cur->node);
23927d73c48SFrançois Tigeot 		cur->func(fence, cur);
24027d73c48SFrançois Tigeot 	}
24127d73c48SFrançois Tigeot 
24227d73c48SFrançois Tigeot 	return 0;
24327d73c48SFrançois Tigeot }
24427d73c48SFrançois Tigeot 
24527d73c48SFrançois Tigeot int
dma_fence_signal(struct dma_fence * fence)24627d73c48SFrançois Tigeot dma_fence_signal(struct dma_fence *fence)
24727d73c48SFrançois Tigeot {
2483f2dd94aSFrançois Tigeot 	int r;
2493f2dd94aSFrançois Tigeot 
25027d73c48SFrançois Tigeot 	if (fence == NULL)
25127d73c48SFrançois Tigeot 		return -EINVAL;
25227d73c48SFrançois Tigeot 
25327d73c48SFrançois Tigeot 	lockmgr(fence->lock, LK_EXCLUSIVE);
2543f2dd94aSFrançois Tigeot 	r = dma_fence_signal_locked(fence);
25527d73c48SFrançois Tigeot 	lockmgr(fence->lock, LK_RELEASE);
25627d73c48SFrançois Tigeot 
2573f2dd94aSFrançois Tigeot 	return r;
25827d73c48SFrançois Tigeot }
25927d73c48SFrançois Tigeot 
26027d73c48SFrançois Tigeot void
dma_fence_enable_sw_signaling(struct dma_fence * fence)26127d73c48SFrançois Tigeot dma_fence_enable_sw_signaling(struct dma_fence *fence)
26227d73c48SFrançois Tigeot {
26327d73c48SFrançois Tigeot 	if (!test_and_set_bit(DMA_FENCE_FLAG_ENABLE_SIGNAL_BIT, &fence->flags) &&
26427d73c48SFrançois Tigeot 	    !test_bit(DMA_FENCE_FLAG_SIGNALED_BIT, &fence->flags) &&
26527d73c48SFrançois Tigeot 	    fence->ops->enable_signaling) {
26627d73c48SFrançois Tigeot 		lockmgr(fence->lock, LK_EXCLUSIVE);
26727d73c48SFrançois Tigeot 		if (!fence->ops->enable_signaling(fence))
26827d73c48SFrançois Tigeot 			dma_fence_signal_locked(fence);
26927d73c48SFrançois Tigeot 		lockmgr(fence->lock, LK_RELEASE);
27027d73c48SFrançois Tigeot 	}
27127d73c48SFrançois Tigeot }
27227d73c48SFrançois Tigeot 
27327d73c48SFrançois Tigeot int
dma_fence_add_callback(struct dma_fence * fence,struct dma_fence_cb * cb,dma_fence_func_t func)27427d73c48SFrançois Tigeot dma_fence_add_callback(struct dma_fence *fence, struct dma_fence_cb *cb,
27527d73c48SFrançois Tigeot     dma_fence_func_t func)
27627d73c48SFrançois Tigeot {
27727d73c48SFrançois Tigeot 	int ret = 0;
27827d73c48SFrançois Tigeot 	bool was_set;
27927d73c48SFrançois Tigeot 
28027d73c48SFrançois Tigeot 	if (WARN_ON(!fence || !func))
28127d73c48SFrançois Tigeot 		return -EINVAL;
28227d73c48SFrançois Tigeot 
28327d73c48SFrançois Tigeot 	if (test_bit(DMA_FENCE_FLAG_SIGNALED_BIT, &fence->flags)) {
28427d73c48SFrançois Tigeot 		INIT_LIST_HEAD(&cb->node);
28527d73c48SFrançois Tigeot 		return -ENOENT;
28627d73c48SFrançois Tigeot 	}
28727d73c48SFrançois Tigeot 
28827d73c48SFrançois Tigeot 	lockmgr(fence->lock, LK_EXCLUSIVE);
28927d73c48SFrançois Tigeot 
29027d73c48SFrançois Tigeot 	was_set = test_and_set_bit(DMA_FENCE_FLAG_ENABLE_SIGNAL_BIT, &fence->flags);
29127d73c48SFrançois Tigeot 
29227d73c48SFrançois Tigeot 	if (test_bit(DMA_FENCE_FLAG_SIGNALED_BIT, &fence->flags))
29327d73c48SFrançois Tigeot 		ret = -ENOENT;
29427d73c48SFrançois Tigeot 	else if (!was_set && fence->ops->enable_signaling) {
29527d73c48SFrançois Tigeot 		if (!fence->ops->enable_signaling(fence)) {
29627d73c48SFrançois Tigeot 			dma_fence_signal_locked(fence);
29727d73c48SFrançois Tigeot 			ret = -ENOENT;
29827d73c48SFrançois Tigeot 		}
29927d73c48SFrançois Tigeot 	}
30027d73c48SFrançois Tigeot 
30127d73c48SFrançois Tigeot 	if (!ret) {
30227d73c48SFrançois Tigeot 		cb->func = func;
30327d73c48SFrançois Tigeot 		list_add_tail(&cb->node, &fence->cb_list);
30427d73c48SFrançois Tigeot 	} else
30527d73c48SFrançois Tigeot 		INIT_LIST_HEAD(&cb->node);
30627d73c48SFrançois Tigeot 	lockmgr(fence->lock, LK_RELEASE);
30727d73c48SFrançois Tigeot 
30827d73c48SFrançois Tigeot 	return ret;
30927d73c48SFrançois Tigeot }
31027d73c48SFrançois Tigeot 
31127d73c48SFrançois Tigeot bool
dma_fence_remove_callback(struct dma_fence * fence,struct dma_fence_cb * cb)31227d73c48SFrançois Tigeot dma_fence_remove_callback(struct dma_fence *fence, struct dma_fence_cb *cb)
31327d73c48SFrançois Tigeot {
31427d73c48SFrançois Tigeot 	bool ret;
31527d73c48SFrançois Tigeot 
31627d73c48SFrançois Tigeot 	lockmgr(fence->lock, LK_EXCLUSIVE);
31727d73c48SFrançois Tigeot 
31827d73c48SFrançois Tigeot 	ret = !list_empty(&cb->node);
31927d73c48SFrançois Tigeot 	if (ret)
32027d73c48SFrançois Tigeot 		list_del_init(&cb->node);
32127d73c48SFrançois Tigeot 
32227d73c48SFrançois Tigeot 	lockmgr(fence->lock, LK_RELEASE);
32327d73c48SFrançois Tigeot 
32427d73c48SFrançois Tigeot 	return ret;
325425e5ce3SFrançois Tigeot }
326a85cb24fSFrançois Tigeot 
327a85cb24fSFrançois Tigeot void
dma_fence_free(struct dma_fence * fence)328a85cb24fSFrançois Tigeot dma_fence_free(struct dma_fence *fence)
329a85cb24fSFrançois Tigeot {
330a85cb24fSFrançois Tigeot 	kfree(fence);
331a85cb24fSFrançois Tigeot }
332