xref: /netbsd-src/sys/external/bsd/drm2/linux/linux_dma_fence_chain.c (revision f4ab16cdad5ba5a4dfe584e197b8b18edbabd9b2)
1 /*	$NetBSD: linux_dma_fence_chain.c,v 1.4 2022/04/09 23:44:44 riastradh Exp $	*/
2 
3 /*-
4  * Copyright (c) 2021 The NetBSD Foundation, Inc.
5  * All rights reserved.
6  *
7  * Redistribution and use in source and binary forms, with or without
8  * modification, are permitted provided that the following conditions
9  * are met:
10  * 1. Redistributions of source code must retain the above copyright
11  *    notice, this list of conditions and the following disclaimer.
12  * 2. Redistributions in binary form must reproduce the above copyright
13  *    notice, this list of conditions and the following disclaimer in the
14  *    documentation and/or other materials provided with the distribution.
15  *
16  * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
17  * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
18  * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
19  * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
20  * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
21  * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
22  * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
23  * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
24  * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
25  * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
26  * POSSIBILITY OF SUCH DAMAGE.
27  */
28 
29 #include <sys/cdefs.h>
30 __KERNEL_RCSID(0, "$NetBSD: linux_dma_fence_chain.c,v 1.4 2022/04/09 23:44:44 riastradh Exp $");
31 
32 #include <sys/types.h>
33 
34 #include <linux/dma-fence.h>
35 #include <linux/dma-fence-chain.h>
36 #include <linux/spinlock.h>
37 
38 static void dma_fence_chain_irq_work(struct irq_work *);
39 static bool dma_fence_chain_enable_signaling(struct dma_fence *);
40 
41 static const struct dma_fence_ops dma_fence_chain_ops;
42 
43 /*
44  * dma_fence_chain_init(chain, prev, fence, seqno)
45  *
46  *	Initialize a fence chain node.  If prev was already a chain,
47  *	extend it; otherwise; create a new chain context.
48  */
49 void
dma_fence_chain_init(struct dma_fence_chain * chain,struct dma_fence * prev,struct dma_fence * fence,uint64_t seqno)50 dma_fence_chain_init(struct dma_fence_chain *chain, struct dma_fence *prev,
51     struct dma_fence *fence, uint64_t seqno)
52 {
53 	struct dma_fence_chain *prev_chain = to_dma_fence_chain(prev);
54 	uint64_t context;
55 
56 	spin_lock_init(&chain->dfc_lock);
57 	chain->dfc_prev = prev;	  /* consume caller's reference */
58 	chain->dfc_fence = fence; /* consume caller's reference */
59 	init_irq_work(&chain->dfc_irq_work, &dma_fence_chain_irq_work);
60 
61 	if (prev_chain == NULL ||
62 	    !__dma_fence_is_later(seqno, prev->seqno, prev->ops)) {
63 		context = dma_fence_context_alloc(1);
64 		if (prev_chain)
65 			seqno = MAX(prev->seqno, seqno);
66 		chain->prev_seqno = 0;
67 	} else {
68 		context = prev->context;
69 		chain->prev_seqno = prev->seqno;
70 	}
71 
72 	dma_fence_init(&chain->base, &dma_fence_chain_ops, &chain->dfc_lock,
73 	    context, seqno);
74 }
75 
76 static const char *
dma_fence_chain_driver_name(struct dma_fence * fence)77 dma_fence_chain_driver_name(struct dma_fence *fence)
78 {
79 
80 	return "dma_fence_chain";
81 }
82 
83 static const char *
dma_fence_chain_timeline_name(struct dma_fence * fence)84 dma_fence_chain_timeline_name(struct dma_fence *fence)
85 {
86 
87 	return "unbound";
88 }
89 
90 static void
dma_fence_chain_irq_work(struct irq_work * work)91 dma_fence_chain_irq_work(struct irq_work *work)
92 {
93 	struct dma_fence_chain *chain = container_of(work,
94 	    struct dma_fence_chain, dfc_irq_work);
95 
96 	if (!dma_fence_chain_enable_signaling(&chain->base))
97 		dma_fence_signal(&chain->base);
98 	dma_fence_put(&chain->base);
99 }
100 
101 static void
dma_fence_chain_callback(struct dma_fence * fence,struct dma_fence_cb * cb)102 dma_fence_chain_callback(struct dma_fence *fence, struct dma_fence_cb *cb)
103 {
104 	struct dma_fence_chain *chain = container_of(cb,
105 	    struct dma_fence_chain, dfc_callback);
106 
107 	irq_work_queue(&chain->dfc_irq_work);
108 	dma_fence_put(fence);
109 }
110 
111 static bool
dma_fence_chain_enable_signaling(struct dma_fence * fence)112 dma_fence_chain_enable_signaling(struct dma_fence *fence)
113 {
114 	struct dma_fence_chain *chain = to_dma_fence_chain(fence);
115 	struct dma_fence_chain *chain1;
116 	struct dma_fence *f, *f1;
117 
118 	KASSERT(chain);
119 
120 	dma_fence_get(&chain->base);
121 	dma_fence_chain_for_each(f, &chain->base) {
122 		f1 = (chain1 = to_dma_fence_chain(f)) ? chain1->dfc_fence : f;
123 
124 		dma_fence_get(f1);
125 		if (dma_fence_add_callback(f, &chain->dfc_callback,
126 			dma_fence_chain_callback) != 0) {
127 			dma_fence_put(f);
128 			return true;
129 		}
130 		dma_fence_put(f1);
131 	}
132 	dma_fence_put(&chain->base);
133 
134 	return false;
135 }
136 
137 static bool
dma_fence_chain_signaled(struct dma_fence * fence)138 dma_fence_chain_signaled(struct dma_fence *fence)
139 {
140 	struct dma_fence_chain *chain1;
141 	struct dma_fence *f, *f1;
142 
143 	dma_fence_chain_for_each(f, fence) {
144 		f1 = (chain1 = to_dma_fence_chain(f)) ? chain1->dfc_fence : f;
145 
146 		if (!dma_fence_is_signaled(f1)) {
147 			dma_fence_put(f);
148 			return false;
149 		}
150 	}
151 
152 	return true;
153 }
154 
155 static void
dma_fence_chain_release(struct dma_fence * fence)156 dma_fence_chain_release(struct dma_fence *fence)
157 {
158 	struct dma_fence_chain *chain = to_dma_fence_chain(fence);
159 	struct dma_fence_chain *prev_chain;
160 	struct dma_fence *prev;
161 
162 	KASSERT(chain);
163 
164 	/*
165 	 * Release the previous pointer, carefully.  Caller has
166 	 * exclusive access to chain, so no need for atomics here.
167 	 */
168 	while ((prev = chain->dfc_prev) != NULL) {
169 		/*
170 		 * If anyone else still holds a reference to the
171 		 * previous fence, or if it's not a chain, stop here.
172 		 */
173 		if (kref_read(&prev->refcount) > 1)
174 			break;
175 		if ((prev_chain = to_dma_fence_chain(prev)) == NULL)
176 			break;
177 
178 		/*
179 		 * Cut it out and free it.  We have exclusive access to
180 		 * prev so this is safe.  This dma_fence_put triggers
181 		 * recursion into dma_fence_chain_release, but the
182 		 * recursion is bounded to one level.
183 		 */
184 		chain->dfc_prev = prev_chain->dfc_prev;
185 		prev_chain->dfc_prev = NULL;
186 		dma_fence_put(prev);
187 	}
188 	dma_fence_put(prev);
189 
190 	dma_fence_put(chain->dfc_fence);
191 	spin_lock_destroy(&chain->dfc_lock);
192 	dma_fence_free(&chain->base);
193 }
194 
195 static const struct dma_fence_ops dma_fence_chain_ops = {
196 	.use_64bit_seqno = true,
197 	.get_driver_name = dma_fence_chain_driver_name,
198 	.get_timeline_name = dma_fence_chain_timeline_name,
199 	.enable_signaling = dma_fence_chain_enable_signaling,
200 	.signaled = dma_fence_chain_signaled,
201 	.release = dma_fence_chain_release,
202 };
203 
204 /*
205  * to_dma_fence_chain(fence)
206  *
207  *	If fence is nonnull and in a chain, return the chain.
208  *	Otherwise return NULL.
209  */
210 struct dma_fence_chain *
to_dma_fence_chain(struct dma_fence * fence)211 to_dma_fence_chain(struct dma_fence *fence)
212 {
213 
214 	if (fence == NULL || fence->ops != &dma_fence_chain_ops)
215 		return NULL;
216 	return container_of(fence, struct dma_fence_chain, base);
217 }
218 
219 /*
220  * get_prev(chain)
221  *
222  *	Get the previous fence of the chain and add a reference, if
223  *	possible; return NULL otherwise.
224  */
225 static struct dma_fence *
get_prev(struct dma_fence_chain * chain)226 get_prev(struct dma_fence_chain *chain)
227 {
228 	struct dma_fence *prev;
229 
230 	rcu_read_lock();
231 	prev = dma_fence_get_rcu_safe(&chain->dfc_prev);
232 	rcu_read_unlock();
233 
234 	return prev;
235 }
236 
237 /*
238  * dma_fence_chain_walk(fence)
239  *
240  *	Find the first unsignalled fence in the chain, or NULL if fence
241  *	is not a chain node or the chain's fences are all signalled.
242  *	While searching, cull signalled fences.
243  */
244 struct dma_fence *
dma_fence_chain_walk(struct dma_fence * fence)245 dma_fence_chain_walk(struct dma_fence *fence)
246 {
247 	struct dma_fence_chain *chain, *prev_chain;
248 	struct dma_fence *prev, *splice;
249 
250 	if ((chain = to_dma_fence_chain(fence)) == NULL) {
251 		dma_fence_put(fence);
252 		return NULL;
253 	}
254 
255 	while ((prev = get_prev(chain)) != NULL) {
256 		if ((prev_chain = to_dma_fence_chain(prev)) != NULL) {
257 			if (!dma_fence_is_signaled(prev_chain->dfc_fence))
258 				break;
259 			splice = get_prev(prev_chain);
260 		} else {
261 			if (!dma_fence_is_signaled(prev))
262 				break;
263 			splice = NULL;
264 		}
265 		membar_release();	/* pairs with dma_fence_get_rcu_safe */
266 		if (atomic_cas_ptr(&chain->dfc_prev, prev, splice) == prev)
267 			dma_fence_put(prev); /* transferred to splice */
268 		else
269 			dma_fence_put(splice);
270 		dma_fence_put(prev);
271 	}
272 
273 	dma_fence_put(fence);
274 	return prev;
275 }
276 
277 /*
278  * dma_fence_chain_find_seqno(&fence, seqno)
279  *
280  *	If seqno is zero, do nothing and succeed.
281  *
282  *	Otherwise, if fence is not on a chain or if its sequence
283  *	number has not yet reached seqno, fail with EINVAL.
284  *
285  *	Otherwise, set fence to the first fence in the chain which
286  *	will signal this sequence number.
287  */
288 int
dma_fence_chain_find_seqno(struct dma_fence ** fencep,uint64_t seqno)289 dma_fence_chain_find_seqno(struct dma_fence **fencep, uint64_t seqno)
290 {
291 	struct dma_fence_chain *chain;
292 
293 	if (seqno == 0)
294 		return 0;
295 
296 	chain = to_dma_fence_chain(*fencep);
297 	if (chain == NULL || chain->base.seqno < seqno)
298 		return -EINVAL;
299 
300 	dma_fence_chain_for_each(*fencep, &chain->base) {
301 		if ((*fencep)->context != chain->base.context ||
302 		    to_dma_fence_chain(*fencep)->prev_seqno < seqno)
303 			break;
304 	}
305 
306 	/* Release reference acquired by dma_fence_chain_for_each.  */
307 	dma_fence_put(&chain->base);
308 
309 	return 0;
310 }
311