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