xref: /OK3568_Linux_fs/kernel/drivers/dma-buf/dma-fence-chain.c (revision 4882a59341e53eb6f0b4789bf948001014eff981)
1*4882a593Smuzhiyun // SPDX-License-Identifier: GPL-2.0-only
2*4882a593Smuzhiyun /*
3*4882a593Smuzhiyun  * fence-chain: chain fences together in a timeline
4*4882a593Smuzhiyun  *
5*4882a593Smuzhiyun  * Copyright (C) 2018 Advanced Micro Devices, Inc.
6*4882a593Smuzhiyun  * Authors:
7*4882a593Smuzhiyun  *	Christian König <christian.koenig@amd.com>
8*4882a593Smuzhiyun  */
9*4882a593Smuzhiyun 
10*4882a593Smuzhiyun #include <linux/dma-fence-chain.h>
11*4882a593Smuzhiyun 
12*4882a593Smuzhiyun static bool dma_fence_chain_enable_signaling(struct dma_fence *fence);
13*4882a593Smuzhiyun 
14*4882a593Smuzhiyun /**
15*4882a593Smuzhiyun  * dma_fence_chain_get_prev - use RCU to get a reference to the previous fence
16*4882a593Smuzhiyun  * @chain: chain node to get the previous node from
17*4882a593Smuzhiyun  *
18*4882a593Smuzhiyun  * Use dma_fence_get_rcu_safe to get a reference to the previous fence of the
19*4882a593Smuzhiyun  * chain node.
20*4882a593Smuzhiyun  */
dma_fence_chain_get_prev(struct dma_fence_chain * chain)21*4882a593Smuzhiyun static struct dma_fence *dma_fence_chain_get_prev(struct dma_fence_chain *chain)
22*4882a593Smuzhiyun {
23*4882a593Smuzhiyun 	struct dma_fence *prev;
24*4882a593Smuzhiyun 
25*4882a593Smuzhiyun 	rcu_read_lock();
26*4882a593Smuzhiyun 	prev = dma_fence_get_rcu_safe(&chain->prev);
27*4882a593Smuzhiyun 	rcu_read_unlock();
28*4882a593Smuzhiyun 	return prev;
29*4882a593Smuzhiyun }
30*4882a593Smuzhiyun 
31*4882a593Smuzhiyun /**
32*4882a593Smuzhiyun  * dma_fence_chain_walk - chain walking function
33*4882a593Smuzhiyun  * @fence: current chain node
34*4882a593Smuzhiyun  *
35*4882a593Smuzhiyun  * Walk the chain to the next node. Returns the next fence or NULL if we are at
36*4882a593Smuzhiyun  * the end of the chain. Garbage collects chain nodes which are already
37*4882a593Smuzhiyun  * signaled.
38*4882a593Smuzhiyun  */
dma_fence_chain_walk(struct dma_fence * fence)39*4882a593Smuzhiyun struct dma_fence *dma_fence_chain_walk(struct dma_fence *fence)
40*4882a593Smuzhiyun {
41*4882a593Smuzhiyun 	struct dma_fence_chain *chain, *prev_chain;
42*4882a593Smuzhiyun 	struct dma_fence *prev, *replacement, *tmp;
43*4882a593Smuzhiyun 
44*4882a593Smuzhiyun 	chain = to_dma_fence_chain(fence);
45*4882a593Smuzhiyun 	if (!chain) {
46*4882a593Smuzhiyun 		dma_fence_put(fence);
47*4882a593Smuzhiyun 		return NULL;
48*4882a593Smuzhiyun 	}
49*4882a593Smuzhiyun 
50*4882a593Smuzhiyun 	while ((prev = dma_fence_chain_get_prev(chain))) {
51*4882a593Smuzhiyun 
52*4882a593Smuzhiyun 		prev_chain = to_dma_fence_chain(prev);
53*4882a593Smuzhiyun 		if (prev_chain) {
54*4882a593Smuzhiyun 			if (!dma_fence_is_signaled(prev_chain->fence))
55*4882a593Smuzhiyun 				break;
56*4882a593Smuzhiyun 
57*4882a593Smuzhiyun 			replacement = dma_fence_chain_get_prev(prev_chain);
58*4882a593Smuzhiyun 		} else {
59*4882a593Smuzhiyun 			if (!dma_fence_is_signaled(prev))
60*4882a593Smuzhiyun 				break;
61*4882a593Smuzhiyun 
62*4882a593Smuzhiyun 			replacement = NULL;
63*4882a593Smuzhiyun 		}
64*4882a593Smuzhiyun 
65*4882a593Smuzhiyun 		tmp = cmpxchg((struct dma_fence __force **)&chain->prev,
66*4882a593Smuzhiyun 			      prev, replacement);
67*4882a593Smuzhiyun 		if (tmp == prev)
68*4882a593Smuzhiyun 			dma_fence_put(tmp);
69*4882a593Smuzhiyun 		else
70*4882a593Smuzhiyun 			dma_fence_put(replacement);
71*4882a593Smuzhiyun 		dma_fence_put(prev);
72*4882a593Smuzhiyun 	}
73*4882a593Smuzhiyun 
74*4882a593Smuzhiyun 	dma_fence_put(fence);
75*4882a593Smuzhiyun 	return prev;
76*4882a593Smuzhiyun }
77*4882a593Smuzhiyun EXPORT_SYMBOL(dma_fence_chain_walk);
78*4882a593Smuzhiyun 
79*4882a593Smuzhiyun /**
80*4882a593Smuzhiyun  * dma_fence_chain_find_seqno - find fence chain node by seqno
81*4882a593Smuzhiyun  * @pfence: pointer to the chain node where to start
82*4882a593Smuzhiyun  * @seqno: the sequence number to search for
83*4882a593Smuzhiyun  *
84*4882a593Smuzhiyun  * Advance the fence pointer to the chain node which will signal this sequence
85*4882a593Smuzhiyun  * number. If no sequence number is provided then this is a no-op.
86*4882a593Smuzhiyun  *
87*4882a593Smuzhiyun  * Returns EINVAL if the fence is not a chain node or the sequence number has
88*4882a593Smuzhiyun  * not yet advanced far enough.
89*4882a593Smuzhiyun  */
dma_fence_chain_find_seqno(struct dma_fence ** pfence,uint64_t seqno)90*4882a593Smuzhiyun int dma_fence_chain_find_seqno(struct dma_fence **pfence, uint64_t seqno)
91*4882a593Smuzhiyun {
92*4882a593Smuzhiyun 	struct dma_fence_chain *chain;
93*4882a593Smuzhiyun 
94*4882a593Smuzhiyun 	if (!seqno)
95*4882a593Smuzhiyun 		return 0;
96*4882a593Smuzhiyun 
97*4882a593Smuzhiyun 	chain = to_dma_fence_chain(*pfence);
98*4882a593Smuzhiyun 	if (!chain || chain->base.seqno < seqno)
99*4882a593Smuzhiyun 		return -EINVAL;
100*4882a593Smuzhiyun 
101*4882a593Smuzhiyun 	dma_fence_chain_for_each(*pfence, &chain->base) {
102*4882a593Smuzhiyun 		if ((*pfence)->context != chain->base.context ||
103*4882a593Smuzhiyun 		    to_dma_fence_chain(*pfence)->prev_seqno < seqno)
104*4882a593Smuzhiyun 			break;
105*4882a593Smuzhiyun 	}
106*4882a593Smuzhiyun 	dma_fence_put(&chain->base);
107*4882a593Smuzhiyun 
108*4882a593Smuzhiyun 	return 0;
109*4882a593Smuzhiyun }
110*4882a593Smuzhiyun EXPORT_SYMBOL(dma_fence_chain_find_seqno);
111*4882a593Smuzhiyun 
dma_fence_chain_get_driver_name(struct dma_fence * fence)112*4882a593Smuzhiyun static const char *dma_fence_chain_get_driver_name(struct dma_fence *fence)
113*4882a593Smuzhiyun {
114*4882a593Smuzhiyun         return "dma_fence_chain";
115*4882a593Smuzhiyun }
116*4882a593Smuzhiyun 
dma_fence_chain_get_timeline_name(struct dma_fence * fence)117*4882a593Smuzhiyun static const char *dma_fence_chain_get_timeline_name(struct dma_fence *fence)
118*4882a593Smuzhiyun {
119*4882a593Smuzhiyun         return "unbound";
120*4882a593Smuzhiyun }
121*4882a593Smuzhiyun 
dma_fence_chain_irq_work(struct irq_work * work)122*4882a593Smuzhiyun static void dma_fence_chain_irq_work(struct irq_work *work)
123*4882a593Smuzhiyun {
124*4882a593Smuzhiyun 	struct dma_fence_chain *chain;
125*4882a593Smuzhiyun 
126*4882a593Smuzhiyun 	chain = container_of(work, typeof(*chain), work);
127*4882a593Smuzhiyun 
128*4882a593Smuzhiyun 	/* Try to rearm the callback */
129*4882a593Smuzhiyun 	if (!dma_fence_chain_enable_signaling(&chain->base))
130*4882a593Smuzhiyun 		/* Ok, we are done. No more unsignaled fences left */
131*4882a593Smuzhiyun 		dma_fence_signal(&chain->base);
132*4882a593Smuzhiyun 	dma_fence_put(&chain->base);
133*4882a593Smuzhiyun }
134*4882a593Smuzhiyun 
dma_fence_chain_cb(struct dma_fence * f,struct dma_fence_cb * cb)135*4882a593Smuzhiyun static void dma_fence_chain_cb(struct dma_fence *f, struct dma_fence_cb *cb)
136*4882a593Smuzhiyun {
137*4882a593Smuzhiyun 	struct dma_fence_chain *chain;
138*4882a593Smuzhiyun 
139*4882a593Smuzhiyun 	chain = container_of(cb, typeof(*chain), cb);
140*4882a593Smuzhiyun 	irq_work_queue(&chain->work);
141*4882a593Smuzhiyun 	dma_fence_put(f);
142*4882a593Smuzhiyun }
143*4882a593Smuzhiyun 
dma_fence_chain_enable_signaling(struct dma_fence * fence)144*4882a593Smuzhiyun static bool dma_fence_chain_enable_signaling(struct dma_fence *fence)
145*4882a593Smuzhiyun {
146*4882a593Smuzhiyun 	struct dma_fence_chain *head = to_dma_fence_chain(fence);
147*4882a593Smuzhiyun 
148*4882a593Smuzhiyun 	dma_fence_get(&head->base);
149*4882a593Smuzhiyun 	dma_fence_chain_for_each(fence, &head->base) {
150*4882a593Smuzhiyun 		struct dma_fence_chain *chain = to_dma_fence_chain(fence);
151*4882a593Smuzhiyun 		struct dma_fence *f = chain ? chain->fence : fence;
152*4882a593Smuzhiyun 
153*4882a593Smuzhiyun 		dma_fence_get(f);
154*4882a593Smuzhiyun 		if (!dma_fence_add_callback(f, &head->cb, dma_fence_chain_cb)) {
155*4882a593Smuzhiyun 			dma_fence_put(fence);
156*4882a593Smuzhiyun 			return true;
157*4882a593Smuzhiyun 		}
158*4882a593Smuzhiyun 		dma_fence_put(f);
159*4882a593Smuzhiyun 	}
160*4882a593Smuzhiyun 	dma_fence_put(&head->base);
161*4882a593Smuzhiyun 	return false;
162*4882a593Smuzhiyun }
163*4882a593Smuzhiyun 
dma_fence_chain_signaled(struct dma_fence * fence)164*4882a593Smuzhiyun static bool dma_fence_chain_signaled(struct dma_fence *fence)
165*4882a593Smuzhiyun {
166*4882a593Smuzhiyun 	dma_fence_chain_for_each(fence, fence) {
167*4882a593Smuzhiyun 		struct dma_fence_chain *chain = to_dma_fence_chain(fence);
168*4882a593Smuzhiyun 		struct dma_fence *f = chain ? chain->fence : fence;
169*4882a593Smuzhiyun 
170*4882a593Smuzhiyun 		if (!dma_fence_is_signaled(f)) {
171*4882a593Smuzhiyun 			dma_fence_put(fence);
172*4882a593Smuzhiyun 			return false;
173*4882a593Smuzhiyun 		}
174*4882a593Smuzhiyun 	}
175*4882a593Smuzhiyun 
176*4882a593Smuzhiyun 	return true;
177*4882a593Smuzhiyun }
178*4882a593Smuzhiyun 
dma_fence_chain_release(struct dma_fence * fence)179*4882a593Smuzhiyun static void dma_fence_chain_release(struct dma_fence *fence)
180*4882a593Smuzhiyun {
181*4882a593Smuzhiyun 	struct dma_fence_chain *chain = to_dma_fence_chain(fence);
182*4882a593Smuzhiyun 	struct dma_fence *prev;
183*4882a593Smuzhiyun 
184*4882a593Smuzhiyun 	/* Manually unlink the chain as much as possible to avoid recursion
185*4882a593Smuzhiyun 	 * and potential stack overflow.
186*4882a593Smuzhiyun 	 */
187*4882a593Smuzhiyun 	while ((prev = rcu_dereference_protected(chain->prev, true))) {
188*4882a593Smuzhiyun 		struct dma_fence_chain *prev_chain;
189*4882a593Smuzhiyun 
190*4882a593Smuzhiyun 		if (kref_read(&prev->refcount) > 1)
191*4882a593Smuzhiyun 		       break;
192*4882a593Smuzhiyun 
193*4882a593Smuzhiyun 		prev_chain = to_dma_fence_chain(prev);
194*4882a593Smuzhiyun 		if (!prev_chain)
195*4882a593Smuzhiyun 			break;
196*4882a593Smuzhiyun 
197*4882a593Smuzhiyun 		/* No need for atomic operations since we hold the last
198*4882a593Smuzhiyun 		 * reference to prev_chain.
199*4882a593Smuzhiyun 		 */
200*4882a593Smuzhiyun 		chain->prev = prev_chain->prev;
201*4882a593Smuzhiyun 		RCU_INIT_POINTER(prev_chain->prev, NULL);
202*4882a593Smuzhiyun 		dma_fence_put(prev);
203*4882a593Smuzhiyun 	}
204*4882a593Smuzhiyun 	dma_fence_put(prev);
205*4882a593Smuzhiyun 
206*4882a593Smuzhiyun 	dma_fence_put(chain->fence);
207*4882a593Smuzhiyun 	dma_fence_free(fence);
208*4882a593Smuzhiyun }
209*4882a593Smuzhiyun 
210*4882a593Smuzhiyun const struct dma_fence_ops dma_fence_chain_ops = {
211*4882a593Smuzhiyun 	.use_64bit_seqno = true,
212*4882a593Smuzhiyun 	.get_driver_name = dma_fence_chain_get_driver_name,
213*4882a593Smuzhiyun 	.get_timeline_name = dma_fence_chain_get_timeline_name,
214*4882a593Smuzhiyun 	.enable_signaling = dma_fence_chain_enable_signaling,
215*4882a593Smuzhiyun 	.signaled = dma_fence_chain_signaled,
216*4882a593Smuzhiyun 	.release = dma_fence_chain_release,
217*4882a593Smuzhiyun };
218*4882a593Smuzhiyun EXPORT_SYMBOL(dma_fence_chain_ops);
219*4882a593Smuzhiyun 
220*4882a593Smuzhiyun /**
221*4882a593Smuzhiyun  * dma_fence_chain_init - initialize a fence chain
222*4882a593Smuzhiyun  * @chain: the chain node to initialize
223*4882a593Smuzhiyun  * @prev: the previous fence
224*4882a593Smuzhiyun  * @fence: the current fence
225*4882a593Smuzhiyun  * @seqno: the sequence number to use for the fence chain
226*4882a593Smuzhiyun  *
227*4882a593Smuzhiyun  * Initialize a new chain node and either start a new chain or add the node to
228*4882a593Smuzhiyun  * the existing chain of the previous fence.
229*4882a593Smuzhiyun  */
dma_fence_chain_init(struct dma_fence_chain * chain,struct dma_fence * prev,struct dma_fence * fence,uint64_t seqno)230*4882a593Smuzhiyun void dma_fence_chain_init(struct dma_fence_chain *chain,
231*4882a593Smuzhiyun 			  struct dma_fence *prev,
232*4882a593Smuzhiyun 			  struct dma_fence *fence,
233*4882a593Smuzhiyun 			  uint64_t seqno)
234*4882a593Smuzhiyun {
235*4882a593Smuzhiyun 	struct dma_fence_chain *prev_chain = to_dma_fence_chain(prev);
236*4882a593Smuzhiyun 	uint64_t context;
237*4882a593Smuzhiyun 
238*4882a593Smuzhiyun 	spin_lock_init(&chain->lock);
239*4882a593Smuzhiyun 	rcu_assign_pointer(chain->prev, prev);
240*4882a593Smuzhiyun 	chain->fence = fence;
241*4882a593Smuzhiyun 	chain->prev_seqno = 0;
242*4882a593Smuzhiyun 	init_irq_work(&chain->work, dma_fence_chain_irq_work);
243*4882a593Smuzhiyun 
244*4882a593Smuzhiyun 	/* Try to reuse the context of the previous chain node. */
245*4882a593Smuzhiyun 	if (prev_chain && __dma_fence_is_later(seqno, prev->seqno, prev->ops)) {
246*4882a593Smuzhiyun 		context = prev->context;
247*4882a593Smuzhiyun 		chain->prev_seqno = prev->seqno;
248*4882a593Smuzhiyun 	} else {
249*4882a593Smuzhiyun 		context = dma_fence_context_alloc(1);
250*4882a593Smuzhiyun 		/* Make sure that we always have a valid sequence number. */
251*4882a593Smuzhiyun 		if (prev_chain)
252*4882a593Smuzhiyun 			seqno = max(prev->seqno, seqno);
253*4882a593Smuzhiyun 	}
254*4882a593Smuzhiyun 
255*4882a593Smuzhiyun 	dma_fence_init(&chain->base, &dma_fence_chain_ops,
256*4882a593Smuzhiyun 		       &chain->lock, context, seqno);
257*4882a593Smuzhiyun }
258*4882a593Smuzhiyun EXPORT_SYMBOL(dma_fence_chain_init);
259