11802d0beSThomas Gleixner // SPDX-License-Identifier: GPL-2.0-only
27bf60c52SChristian König /*
37bf60c52SChristian König  * fence-chain: chain fences together in a timeline
47bf60c52SChristian König  *
57bf60c52SChristian König  * Copyright (C) 2018 Advanced Micro Devices, Inc.
67bf60c52SChristian König  * Authors:
77bf60c52SChristian König  *	Christian König <christian.koenig@amd.com>
87bf60c52SChristian König  */
97bf60c52SChristian König 
107bf60c52SChristian König #include <linux/dma-fence-chain.h>
117bf60c52SChristian König 
127bf60c52SChristian König static bool dma_fence_chain_enable_signaling(struct dma_fence *fence);
137bf60c52SChristian König 
147bf60c52SChristian König /**
157bf60c52SChristian König  * dma_fence_chain_get_prev - use RCU to get a reference to the previous fence
167bf60c52SChristian König  * @chain: chain node to get the previous node from
177bf60c52SChristian König  *
187bf60c52SChristian König  * Use dma_fence_get_rcu_safe to get a reference to the previous fence of the
197bf60c52SChristian König  * chain node.
207bf60c52SChristian König  */
dma_fence_chain_get_prev(struct dma_fence_chain * chain)217bf60c52SChristian König static struct dma_fence *dma_fence_chain_get_prev(struct dma_fence_chain *chain)
227bf60c52SChristian König {
237bf60c52SChristian König 	struct dma_fence *prev;
247bf60c52SChristian König 
257bf60c52SChristian König 	rcu_read_lock();
267bf60c52SChristian König 	prev = dma_fence_get_rcu_safe(&chain->prev);
277bf60c52SChristian König 	rcu_read_unlock();
287bf60c52SChristian König 	return prev;
297bf60c52SChristian König }
307bf60c52SChristian König 
317bf60c52SChristian König /**
327bf60c52SChristian König  * dma_fence_chain_walk - chain walking function
337bf60c52SChristian König  * @fence: current chain node
347bf60c52SChristian König  *
357bf60c52SChristian König  * Walk the chain to the next node. Returns the next fence or NULL if we are at
367bf60c52SChristian König  * the end of the chain. Garbage collects chain nodes which are already
377bf60c52SChristian König  * signaled.
387bf60c52SChristian König  */
dma_fence_chain_walk(struct dma_fence * fence)397bf60c52SChristian König struct dma_fence *dma_fence_chain_walk(struct dma_fence *fence)
407bf60c52SChristian König {
417bf60c52SChristian König 	struct dma_fence_chain *chain, *prev_chain;
427bf60c52SChristian König 	struct dma_fence *prev, *replacement, *tmp;
437bf60c52SChristian König 
447bf60c52SChristian König 	chain = to_dma_fence_chain(fence);
457bf60c52SChristian König 	if (!chain) {
467bf60c52SChristian König 		dma_fence_put(fence);
477bf60c52SChristian König 		return NULL;
487bf60c52SChristian König 	}
497bf60c52SChristian König 
507bf60c52SChristian König 	while ((prev = dma_fence_chain_get_prev(chain))) {
517bf60c52SChristian König 
527bf60c52SChristian König 		prev_chain = to_dma_fence_chain(prev);
537bf60c52SChristian König 		if (prev_chain) {
547bf60c52SChristian König 			if (!dma_fence_is_signaled(prev_chain->fence))
557bf60c52SChristian König 				break;
567bf60c52SChristian König 
577bf60c52SChristian König 			replacement = dma_fence_chain_get_prev(prev_chain);
587bf60c52SChristian König 		} else {
597bf60c52SChristian König 			if (!dma_fence_is_signaled(prev))
607bf60c52SChristian König 				break;
617bf60c52SChristian König 
627bf60c52SChristian König 			replacement = NULL;
637bf60c52SChristian König 		}
647bf60c52SChristian König 
6514374e3eSChristian König 		tmp = unrcu_pointer(cmpxchg(&chain->prev, RCU_INITIALIZER(prev),
6614374e3eSChristian König 					     RCU_INITIALIZER(replacement)));
677bf60c52SChristian König 		if (tmp == prev)
687bf60c52SChristian König 			dma_fence_put(tmp);
697bf60c52SChristian König 		else
707bf60c52SChristian König 			dma_fence_put(replacement);
717bf60c52SChristian König 		dma_fence_put(prev);
727bf60c52SChristian König 	}
737bf60c52SChristian König 
747bf60c52SChristian König 	dma_fence_put(fence);
757bf60c52SChristian König 	return prev;
767bf60c52SChristian König }
777bf60c52SChristian König EXPORT_SYMBOL(dma_fence_chain_walk);
787bf60c52SChristian König 
797bf60c52SChristian König /**
807bf60c52SChristian König  * dma_fence_chain_find_seqno - find fence chain node by seqno
817bf60c52SChristian König  * @pfence: pointer to the chain node where to start
827bf60c52SChristian König  * @seqno: the sequence number to search for
837bf60c52SChristian König  *
847bf60c52SChristian König  * Advance the fence pointer to the chain node which will signal this sequence
857bf60c52SChristian König  * number. If no sequence number is provided then this is a no-op.
867bf60c52SChristian König  *
877bf60c52SChristian König  * Returns EINVAL if the fence is not a chain node or the sequence number has
887bf60c52SChristian König  * not yet advanced far enough.
897bf60c52SChristian König  */
dma_fence_chain_find_seqno(struct dma_fence ** pfence,uint64_t seqno)907bf60c52SChristian König int dma_fence_chain_find_seqno(struct dma_fence **pfence, uint64_t seqno)
917bf60c52SChristian König {
927bf60c52SChristian König 	struct dma_fence_chain *chain;
937bf60c52SChristian König 
947bf60c52SChristian König 	if (!seqno)
957bf60c52SChristian König 		return 0;
967bf60c52SChristian König 
977bf60c52SChristian König 	chain = to_dma_fence_chain(*pfence);
987bf60c52SChristian König 	if (!chain || chain->base.seqno < seqno)
997bf60c52SChristian König 		return -EINVAL;
1007bf60c52SChristian König 
1017bf60c52SChristian König 	dma_fence_chain_for_each(*pfence, &chain->base) {
1027bf60c52SChristian König 		if ((*pfence)->context != chain->base.context ||
1037bf60c52SChristian König 		    to_dma_fence_chain(*pfence)->prev_seqno < seqno)
1047bf60c52SChristian König 			break;
1057bf60c52SChristian König 	}
1067bf60c52SChristian König 	dma_fence_put(&chain->base);
1077bf60c52SChristian König 
1087bf60c52SChristian König 	return 0;
1097bf60c52SChristian König }
1107bf60c52SChristian König EXPORT_SYMBOL(dma_fence_chain_find_seqno);
1117bf60c52SChristian König 
dma_fence_chain_get_driver_name(struct dma_fence * fence)1127bf60c52SChristian König static const char *dma_fence_chain_get_driver_name(struct dma_fence *fence)
1137bf60c52SChristian König {
1147bf60c52SChristian König         return "dma_fence_chain";
1157bf60c52SChristian König }
1167bf60c52SChristian König 
dma_fence_chain_get_timeline_name(struct dma_fence * fence)1177bf60c52SChristian König static const char *dma_fence_chain_get_timeline_name(struct dma_fence *fence)
1187bf60c52SChristian König {
1197bf60c52SChristian König         return "unbound";
1207bf60c52SChristian König }
1217bf60c52SChristian König 
dma_fence_chain_irq_work(struct irq_work * work)1227bf60c52SChristian König static void dma_fence_chain_irq_work(struct irq_work *work)
1237bf60c52SChristian König {
1247bf60c52SChristian König 	struct dma_fence_chain *chain;
1257bf60c52SChristian König 
1267bf60c52SChristian König 	chain = container_of(work, typeof(*chain), work);
1277bf60c52SChristian König 
1287bf60c52SChristian König 	/* Try to rearm the callback */
1297bf60c52SChristian König 	if (!dma_fence_chain_enable_signaling(&chain->base))
1307bf60c52SChristian König 		/* Ok, we are done. No more unsignaled fences left */
1317bf60c52SChristian König 		dma_fence_signal(&chain->base);
1327bf60c52SChristian König 	dma_fence_put(&chain->base);
1337bf60c52SChristian König }
1347bf60c52SChristian König 
dma_fence_chain_cb(struct dma_fence * f,struct dma_fence_cb * cb)1357bf60c52SChristian König static void dma_fence_chain_cb(struct dma_fence *f, struct dma_fence_cb *cb)
1367bf60c52SChristian König {
1377bf60c52SChristian König 	struct dma_fence_chain *chain;
1387bf60c52SChristian König 
1397bf60c52SChristian König 	chain = container_of(cb, typeof(*chain), cb);
1409c61e789SChristian König 	init_irq_work(&chain->work, dma_fence_chain_irq_work);
1417bf60c52SChristian König 	irq_work_queue(&chain->work);
1427bf60c52SChristian König 	dma_fence_put(f);
1437bf60c52SChristian König }
1447bf60c52SChristian König 
dma_fence_chain_enable_signaling(struct dma_fence * fence)1457bf60c52SChristian König static bool dma_fence_chain_enable_signaling(struct dma_fence *fence)
1467bf60c52SChristian König {
1477bf60c52SChristian König 	struct dma_fence_chain *head = to_dma_fence_chain(fence);
1487bf60c52SChristian König 
1497bf60c52SChristian König 	dma_fence_get(&head->base);
1507bf60c52SChristian König 	dma_fence_chain_for_each(fence, &head->base) {
15118f5fad2SChristian König 		struct dma_fence *f = dma_fence_chain_contained(fence);
1527bf60c52SChristian König 
1537bf60c52SChristian König 		dma_fence_get(f);
1547bf60c52SChristian König 		if (!dma_fence_add_callback(f, &head->cb, dma_fence_chain_cb)) {
1557bf60c52SChristian König 			dma_fence_put(fence);
1567bf60c52SChristian König 			return true;
1577bf60c52SChristian König 		}
1587bf60c52SChristian König 		dma_fence_put(f);
1597bf60c52SChristian König 	}
1607bf60c52SChristian König 	dma_fence_put(&head->base);
1617bf60c52SChristian König 	return false;
1627bf60c52SChristian König }
1637bf60c52SChristian König 
dma_fence_chain_signaled(struct dma_fence * fence)1647bf60c52SChristian König static bool dma_fence_chain_signaled(struct dma_fence *fence)
1657bf60c52SChristian König {
1667bf60c52SChristian König 	dma_fence_chain_for_each(fence, fence) {
16718f5fad2SChristian König 		struct dma_fence *f = dma_fence_chain_contained(fence);
1687bf60c52SChristian König 
1697bf60c52SChristian König 		if (!dma_fence_is_signaled(f)) {
1707bf60c52SChristian König 			dma_fence_put(fence);
1717bf60c52SChristian König 			return false;
1727bf60c52SChristian König 		}
1737bf60c52SChristian König 	}
1747bf60c52SChristian König 
1757bf60c52SChristian König 	return true;
1767bf60c52SChristian König }
1777bf60c52SChristian König 
dma_fence_chain_release(struct dma_fence * fence)1787bf60c52SChristian König static void dma_fence_chain_release(struct dma_fence *fence)
1797bf60c52SChristian König {
1807bf60c52SChristian König 	struct dma_fence_chain *chain = to_dma_fence_chain(fence);
18192cb3e59SChristian König 	struct dma_fence *prev;
1827bf60c52SChristian König 
18392cb3e59SChristian König 	/* Manually unlink the chain as much as possible to avoid recursion
18492cb3e59SChristian König 	 * and potential stack overflow.
18592cb3e59SChristian König 	 */
18692cb3e59SChristian König 	while ((prev = rcu_dereference_protected(chain->prev, true))) {
18792cb3e59SChristian König 		struct dma_fence_chain *prev_chain;
18892cb3e59SChristian König 
18992cb3e59SChristian König 		if (kref_read(&prev->refcount) > 1)
19092cb3e59SChristian König 		       break;
19192cb3e59SChristian König 
19292cb3e59SChristian König 		prev_chain = to_dma_fence_chain(prev);
19392cb3e59SChristian König 		if (!prev_chain)
19492cb3e59SChristian König 			break;
19592cb3e59SChristian König 
19692cb3e59SChristian König 		/* No need for atomic operations since we hold the last
19792cb3e59SChristian König 		 * reference to prev_chain.
19892cb3e59SChristian König 		 */
19992cb3e59SChristian König 		chain->prev = prev_chain->prev;
20092cb3e59SChristian König 		RCU_INIT_POINTER(prev_chain->prev, NULL);
20192cb3e59SChristian König 		dma_fence_put(prev);
20292cb3e59SChristian König 	}
20392cb3e59SChristian König 	dma_fence_put(prev);
20492cb3e59SChristian König 
2057bf60c52SChristian König 	dma_fence_put(chain->fence);
2067bf60c52SChristian König 	dma_fence_free(fence);
2077bf60c52SChristian König }
2087bf60c52SChristian König 
209*786119ffSRob Clark 
dma_fence_chain_set_deadline(struct dma_fence * fence,ktime_t deadline)210*786119ffSRob Clark static void dma_fence_chain_set_deadline(struct dma_fence *fence,
211*786119ffSRob Clark 					 ktime_t deadline)
212*786119ffSRob Clark {
213*786119ffSRob Clark 	dma_fence_chain_for_each(fence, fence) {
214*786119ffSRob Clark 		struct dma_fence *f = dma_fence_chain_contained(fence);
215*786119ffSRob Clark 
216*786119ffSRob Clark 		dma_fence_set_deadline(f, deadline);
217*786119ffSRob Clark 	}
218*786119ffSRob Clark }
219*786119ffSRob Clark 
2207bf60c52SChristian König const struct dma_fence_ops dma_fence_chain_ops = {
2215e498abfSChristian König 	.use_64bit_seqno = true,
2227bf60c52SChristian König 	.get_driver_name = dma_fence_chain_get_driver_name,
2237bf60c52SChristian König 	.get_timeline_name = dma_fence_chain_get_timeline_name,
2247bf60c52SChristian König 	.enable_signaling = dma_fence_chain_enable_signaling,
2257bf60c52SChristian König 	.signaled = dma_fence_chain_signaled,
2267bf60c52SChristian König 	.release = dma_fence_chain_release,
227*786119ffSRob Clark 	.set_deadline = dma_fence_chain_set_deadline,
2287bf60c52SChristian König };
2297bf60c52SChristian König EXPORT_SYMBOL(dma_fence_chain_ops);
2307bf60c52SChristian König 
2317bf60c52SChristian König /**
2327bf60c52SChristian König  * dma_fence_chain_init - initialize a fence chain
2337bf60c52SChristian König  * @chain: the chain node to initialize
2347bf60c52SChristian König  * @prev: the previous fence
2357bf60c52SChristian König  * @fence: the current fence
2368d0441cfSKrzysztof Kozlowski  * @seqno: the sequence number to use for the fence chain
2377bf60c52SChristian König  *
2387bf60c52SChristian König  * Initialize a new chain node and either start a new chain or add the node to
2397bf60c52SChristian König  * the existing chain of the previous fence.
2407bf60c52SChristian König  */
dma_fence_chain_init(struct dma_fence_chain * chain,struct dma_fence * prev,struct dma_fence * fence,uint64_t seqno)2417bf60c52SChristian König void dma_fence_chain_init(struct dma_fence_chain *chain,
2427bf60c52SChristian König 			  struct dma_fence *prev,
2437bf60c52SChristian König 			  struct dma_fence *fence,
2447bf60c52SChristian König 			  uint64_t seqno)
2457bf60c52SChristian König {
2467bf60c52SChristian König 	struct dma_fence_chain *prev_chain = to_dma_fence_chain(prev);
2477bf60c52SChristian König 	uint64_t context;
2487bf60c52SChristian König 
2497bf60c52SChristian König 	spin_lock_init(&chain->lock);
2507bf60c52SChristian König 	rcu_assign_pointer(chain->prev, prev);
2517bf60c52SChristian König 	chain->fence = fence;
2527bf60c52SChristian König 	chain->prev_seqno = 0;
2537bf60c52SChristian König 
2547bf60c52SChristian König 	/* Try to reuse the context of the previous chain node. */
2555e498abfSChristian König 	if (prev_chain && __dma_fence_is_later(seqno, prev->seqno, prev->ops)) {
2567bf60c52SChristian König 		context = prev->context;
2577bf60c52SChristian König 		chain->prev_seqno = prev->seqno;
2587bf60c52SChristian König 	} else {
2597bf60c52SChristian König 		context = dma_fence_context_alloc(1);
2607bf60c52SChristian König 		/* Make sure that we always have a valid sequence number. */
2617bf60c52SChristian König 		if (prev_chain)
2627bf60c52SChristian König 			seqno = max(prev->seqno, seqno);
2637bf60c52SChristian König 	}
2647bf60c52SChristian König 
2657bf60c52SChristian König 	dma_fence_init(&chain->base, &dma_fence_chain_ops,
2667bf60c52SChristian König 		       &chain->lock, context, seqno);
267270b48bbSChristian König 
268270b48bbSChristian König 	/*
269270b48bbSChristian König 	 * Chaining dma_fence_chain container together is only allowed through
270270b48bbSChristian König 	 * the prev fence and not through the contained fence.
271270b48bbSChristian König 	 *
272270b48bbSChristian König 	 * The correct way of handling this is to flatten out the fence
273270b48bbSChristian König 	 * structure into a dma_fence_array by the caller instead.
274270b48bbSChristian König 	 */
275270b48bbSChristian König 	WARN_ON(dma_fence_is_chain(fence));
2767bf60c52SChristian König }
2777bf60c52SChristian König EXPORT_SYMBOL(dma_fence_chain_init);
278