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