2 * fence-chain: chain fences together in a timeline
4 * Copyright (C) 2018 Advanced Micro Devices, Inc.
6 * Christian König <christian.koenig@amd.com>
8 * This program is free software; you can redistribute it and/or modify it
9 * under the terms of the GNU General Public License version 2 as published by
10 * the Free Software Foundation.
12 * This program is distributed in the hope that it will be useful, but WITHOUT
13 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
14 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
18 #include <linux/dma-fence-chain.h>
20 static bool dma_fence_chain_enable_signaling(struct dma_fence *fence);
23 * dma_fence_chain_get_prev - use RCU to get a reference to the previous fence
24 * @chain: chain node to get the previous node from
26 * Use dma_fence_get_rcu_safe to get a reference to the previous fence of the
29 static struct dma_fence *dma_fence_chain_get_prev(struct dma_fence_chain *chain)
31 struct dma_fence *prev;
34 prev = dma_fence_get_rcu_safe(&chain->prev);
40 * dma_fence_chain_walk - chain walking function
41 * @fence: current chain node
43 * Walk the chain to the next node. Returns the next fence or NULL if we are at
44 * the end of the chain. Garbage collects chain nodes which are already
47 struct dma_fence *dma_fence_chain_walk(struct dma_fence *fence)
49 struct dma_fence_chain *chain, *prev_chain;
50 struct dma_fence *prev, *replacement, *tmp;
52 chain = to_dma_fence_chain(fence);
58 while ((prev = dma_fence_chain_get_prev(chain))) {
60 prev_chain = to_dma_fence_chain(prev);
62 if (!dma_fence_is_signaled(prev_chain->fence))
65 replacement = dma_fence_chain_get_prev(prev_chain);
67 if (!dma_fence_is_signaled(prev))
73 tmp = cmpxchg((void **)&chain->prev, (void *)prev, (void *)replacement);
77 dma_fence_put(replacement);
84 EXPORT_SYMBOL(dma_fence_chain_walk);
87 * dma_fence_chain_find_seqno - find fence chain node by seqno
88 * @pfence: pointer to the chain node where to start
89 * @seqno: the sequence number to search for
91 * Advance the fence pointer to the chain node which will signal this sequence
92 * number. If no sequence number is provided then this is a no-op.
94 * Returns EINVAL if the fence is not a chain node or the sequence number has
95 * not yet advanced far enough.
97 int dma_fence_chain_find_seqno(struct dma_fence **pfence, uint64_t seqno)
99 struct dma_fence_chain *chain;
104 chain = to_dma_fence_chain(*pfence);
105 if (!chain || chain->base.seqno < seqno)
108 dma_fence_chain_for_each(*pfence, &chain->base) {
109 if ((*pfence)->context != chain->base.context ||
110 to_dma_fence_chain(*pfence)->prev_seqno < seqno)
113 dma_fence_put(&chain->base);
117 EXPORT_SYMBOL(dma_fence_chain_find_seqno);
119 static const char *dma_fence_chain_get_driver_name(struct dma_fence *fence)
121 return "dma_fence_chain";
124 static const char *dma_fence_chain_get_timeline_name(struct dma_fence *fence)
129 static void dma_fence_chain_irq_work(struct irq_work *work)
131 struct dma_fence_chain *chain;
133 chain = container_of(work, typeof(*chain), work);
135 /* Try to rearm the callback */
136 if (!dma_fence_chain_enable_signaling(&chain->base))
137 /* Ok, we are done. No more unsignaled fences left */
138 dma_fence_signal(&chain->base);
139 dma_fence_put(&chain->base);
142 static void dma_fence_chain_cb(struct dma_fence *f, struct dma_fence_cb *cb)
144 struct dma_fence_chain *chain;
146 chain = container_of(cb, typeof(*chain), cb);
147 irq_work_queue(&chain->work);
151 static bool dma_fence_chain_enable_signaling(struct dma_fence *fence)
153 struct dma_fence_chain *head = to_dma_fence_chain(fence);
155 dma_fence_get(&head->base);
156 dma_fence_chain_for_each(fence, &head->base) {
157 struct dma_fence_chain *chain = to_dma_fence_chain(fence);
158 struct dma_fence *f = chain ? chain->fence : fence;
161 if (!dma_fence_add_callback(f, &head->cb, dma_fence_chain_cb)) {
162 dma_fence_put(fence);
167 dma_fence_put(&head->base);
171 static bool dma_fence_chain_signaled(struct dma_fence *fence)
173 dma_fence_chain_for_each(fence, fence) {
174 struct dma_fence_chain *chain = to_dma_fence_chain(fence);
175 struct dma_fence *f = chain ? chain->fence : fence;
177 if (!dma_fence_is_signaled(f)) {
178 dma_fence_put(fence);
186 static void dma_fence_chain_release(struct dma_fence *fence)
188 struct dma_fence_chain *chain = to_dma_fence_chain(fence);
190 dma_fence_put(rcu_dereference_protected(chain->prev, true));
191 dma_fence_put(chain->fence);
192 dma_fence_free(fence);
195 const struct dma_fence_ops dma_fence_chain_ops = {
196 .use_64bit_seqno = true,
197 .get_driver_name = dma_fence_chain_get_driver_name,
198 .get_timeline_name = dma_fence_chain_get_timeline_name,
199 .enable_signaling = dma_fence_chain_enable_signaling,
200 .signaled = dma_fence_chain_signaled,
201 .release = dma_fence_chain_release,
203 EXPORT_SYMBOL(dma_fence_chain_ops);
206 * dma_fence_chain_init - initialize a fence chain
207 * @chain: the chain node to initialize
208 * @prev: the previous fence
209 * @fence: the current fence
211 * Initialize a new chain node and either start a new chain or add the node to
212 * the existing chain of the previous fence.
214 void dma_fence_chain_init(struct dma_fence_chain *chain,
215 struct dma_fence *prev,
216 struct dma_fence *fence,
219 struct dma_fence_chain *prev_chain = to_dma_fence_chain(prev);
222 spin_lock_init(&chain->lock);
223 rcu_assign_pointer(chain->prev, prev);
224 chain->fence = fence;
225 chain->prev_seqno = 0;
226 init_irq_work(&chain->work, dma_fence_chain_irq_work);
228 /* Try to reuse the context of the previous chain node. */
229 if (prev_chain && __dma_fence_is_later(seqno, prev->seqno, prev->ops)) {
230 context = prev->context;
231 chain->prev_seqno = prev->seqno;
233 context = dma_fence_context_alloc(1);
234 /* Make sure that we always have a valid sequence number. */
236 seqno = max(prev->seqno, seqno);
239 dma_fence_init(&chain->base, &dma_fence_chain_ops,
240 &chain->lock, context, seqno);
242 EXPORT_SYMBOL(dma_fence_chain_init);