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 */ 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 */ 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 6529da47cfSChris Wilson tmp = cmpxchg((struct dma_fence __force **)&chain->prev, 6629da47cfSChris Wilson prev, 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 */ 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 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 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 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 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 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) { 151*18f5fad2SChristian 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 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) { 167*18f5fad2SChristian 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 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 2097bf60c52SChristian König const struct dma_fence_ops dma_fence_chain_ops = { 2105e498abfSChristian König .use_64bit_seqno = true, 2117bf60c52SChristian König .get_driver_name = dma_fence_chain_get_driver_name, 2127bf60c52SChristian König .get_timeline_name = dma_fence_chain_get_timeline_name, 2137bf60c52SChristian König .enable_signaling = dma_fence_chain_enable_signaling, 2147bf60c52SChristian König .signaled = dma_fence_chain_signaled, 2157bf60c52SChristian König .release = dma_fence_chain_release, 2167bf60c52SChristian König }; 2177bf60c52SChristian König EXPORT_SYMBOL(dma_fence_chain_ops); 2187bf60c52SChristian König 2197bf60c52SChristian König /** 2207bf60c52SChristian König * dma_fence_chain_init - initialize a fence chain 2217bf60c52SChristian König * @chain: the chain node to initialize 2227bf60c52SChristian König * @prev: the previous fence 2237bf60c52SChristian König * @fence: the current fence 2248d0441cfSKrzysztof Kozlowski * @seqno: the sequence number to use for the fence chain 2257bf60c52SChristian König * 2267bf60c52SChristian König * Initialize a new chain node and either start a new chain or add the node to 2277bf60c52SChristian König * the existing chain of the previous fence. 2287bf60c52SChristian König */ 2297bf60c52SChristian König void dma_fence_chain_init(struct dma_fence_chain *chain, 2307bf60c52SChristian König struct dma_fence *prev, 2317bf60c52SChristian König struct dma_fence *fence, 2327bf60c52SChristian König uint64_t seqno) 2337bf60c52SChristian König { 2347bf60c52SChristian König struct dma_fence_chain *prev_chain = to_dma_fence_chain(prev); 2357bf60c52SChristian König uint64_t context; 2367bf60c52SChristian König 2377bf60c52SChristian König spin_lock_init(&chain->lock); 2387bf60c52SChristian König rcu_assign_pointer(chain->prev, prev); 2397bf60c52SChristian König chain->fence = fence; 2407bf60c52SChristian König chain->prev_seqno = 0; 2417bf60c52SChristian König 2427bf60c52SChristian König /* Try to reuse the context of the previous chain node. */ 2435e498abfSChristian König if (prev_chain && __dma_fence_is_later(seqno, prev->seqno, prev->ops)) { 2447bf60c52SChristian König context = prev->context; 2457bf60c52SChristian König chain->prev_seqno = prev->seqno; 2467bf60c52SChristian König } else { 2477bf60c52SChristian König context = dma_fence_context_alloc(1); 2487bf60c52SChristian König /* Make sure that we always have a valid sequence number. */ 2497bf60c52SChristian König if (prev_chain) 2507bf60c52SChristian König seqno = max(prev->seqno, seqno); 2517bf60c52SChristian König } 2527bf60c52SChristian König 2537bf60c52SChristian König dma_fence_init(&chain->base, &dma_fence_chain_ops, 2547bf60c52SChristian König &chain->lock, context, seqno); 255270b48bbSChristian König 256270b48bbSChristian König /* 257270b48bbSChristian König * Chaining dma_fence_chain container together is only allowed through 258270b48bbSChristian König * the prev fence and not through the contained fence. 259270b48bbSChristian König * 260270b48bbSChristian König * The correct way of handling this is to flatten out the fence 261270b48bbSChristian König * structure into a dma_fence_array by the caller instead. 262270b48bbSChristian König */ 263270b48bbSChristian König WARN_ON(dma_fence_is_chain(fence)); 2647bf60c52SChristian König } 2657bf60c52SChristian König EXPORT_SYMBOL(dma_fence_chain_init); 266