Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux

dma-buf: add new dma_fence_chain container v7

Lockless container implementation similar to a dma_fence_array, but with
only two elements per node and automatic garbage collection.

v2: properly document dma_fence_chain_for_each, add dma_fence_chain_find_seqno,
drop prev reference during garbage collection if it's not a chain fence.
v3: use head and iterator for dma_fence_chain_for_each
v4: fix reference count in dma_fence_chain_enable_signaling
v5: fix iteration when walking each chain node
v6: add __rcu for member 'prev' of struct chain node
v7: fix rcu warnings from kernel robot

Signed-off-by: Christian König <christian.koenig@amd.com>
Reviewed-by: Lionel Landwerlin <lionel.g.landwerlin@intel.com>
Link: https://patchwork.freedesktop.org/patch/295778/?series=58813&rev=1

authored by

Christian König and committed by
Christian König
7bf60c52 64e1f830

+324 -1
+2 -1
drivers/dma-buf/Makefile
··· 1 - obj-y := dma-buf.o dma-fence.o dma-fence-array.o reservation.o seqno-fence.o 1 + obj-y := dma-buf.o dma-fence.o dma-fence-array.o dma-fence-chain.o \ 2 + reservation.o seqno-fence.o 2 3 obj-$(CONFIG_SYNC_FILE) += sync_file.o 3 4 obj-$(CONFIG_SW_SYNC) += sw_sync.o sync_debug.o 4 5 obj-$(CONFIG_UDMABUF) += udmabuf.o
+241
drivers/dma-buf/dma-fence-chain.c
··· 1 + /* 2 + * fence-chain: chain fences together in a timeline 3 + * 4 + * Copyright (C) 2018 Advanced Micro Devices, Inc. 5 + * Authors: 6 + * Christian König <christian.koenig@amd.com> 7 + * 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. 11 + * 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 15 + * more details. 16 + */ 17 + 18 + #include <linux/dma-fence-chain.h> 19 + 20 + static bool dma_fence_chain_enable_signaling(struct dma_fence *fence); 21 + 22 + /** 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 25 + * 26 + * Use dma_fence_get_rcu_safe to get a reference to the previous fence of the 27 + * chain node. 28 + */ 29 + static struct dma_fence *dma_fence_chain_get_prev(struct dma_fence_chain *chain) 30 + { 31 + struct dma_fence *prev; 32 + 33 + rcu_read_lock(); 34 + prev = dma_fence_get_rcu_safe(&chain->prev); 35 + rcu_read_unlock(); 36 + return prev; 37 + } 38 + 39 + /** 40 + * dma_fence_chain_walk - chain walking function 41 + * @fence: current chain node 42 + * 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 45 + * signaled. 46 + */ 47 + struct dma_fence *dma_fence_chain_walk(struct dma_fence *fence) 48 + { 49 + struct dma_fence_chain *chain, *prev_chain; 50 + struct dma_fence *prev, *replacement, *tmp; 51 + 52 + chain = to_dma_fence_chain(fence); 53 + if (!chain) { 54 + dma_fence_put(fence); 55 + return NULL; 56 + } 57 + 58 + while ((prev = dma_fence_chain_get_prev(chain))) { 59 + 60 + prev_chain = to_dma_fence_chain(prev); 61 + if (prev_chain) { 62 + if (!dma_fence_is_signaled(prev_chain->fence)) 63 + break; 64 + 65 + replacement = dma_fence_chain_get_prev(prev_chain); 66 + } else { 67 + if (!dma_fence_is_signaled(prev)) 68 + break; 69 + 70 + replacement = NULL; 71 + } 72 + 73 + tmp = cmpxchg((void **)&chain->prev, (void *)prev, (void *)replacement); 74 + if (tmp == prev) 75 + dma_fence_put(tmp); 76 + else 77 + dma_fence_put(replacement); 78 + dma_fence_put(prev); 79 + } 80 + 81 + dma_fence_put(fence); 82 + return prev; 83 + } 84 + EXPORT_SYMBOL(dma_fence_chain_walk); 85 + 86 + /** 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 90 + * 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. 93 + * 94 + * Returns EINVAL if the fence is not a chain node or the sequence number has 95 + * not yet advanced far enough. 96 + */ 97 + int dma_fence_chain_find_seqno(struct dma_fence **pfence, uint64_t seqno) 98 + { 99 + struct dma_fence_chain *chain; 100 + 101 + if (!seqno) 102 + return 0; 103 + 104 + chain = to_dma_fence_chain(*pfence); 105 + if (!chain || chain->base.seqno < seqno) 106 + return -EINVAL; 107 + 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) 111 + break; 112 + } 113 + dma_fence_put(&chain->base); 114 + 115 + return 0; 116 + } 117 + EXPORT_SYMBOL(dma_fence_chain_find_seqno); 118 + 119 + static const char *dma_fence_chain_get_driver_name(struct dma_fence *fence) 120 + { 121 + return "dma_fence_chain"; 122 + } 123 + 124 + static const char *dma_fence_chain_get_timeline_name(struct dma_fence *fence) 125 + { 126 + return "unbound"; 127 + } 128 + 129 + static void dma_fence_chain_irq_work(struct irq_work *work) 130 + { 131 + struct dma_fence_chain *chain; 132 + 133 + chain = container_of(work, typeof(*chain), work); 134 + 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); 140 + } 141 + 142 + static void dma_fence_chain_cb(struct dma_fence *f, struct dma_fence_cb *cb) 143 + { 144 + struct dma_fence_chain *chain; 145 + 146 + chain = container_of(cb, typeof(*chain), cb); 147 + irq_work_queue(&chain->work); 148 + dma_fence_put(f); 149 + } 150 + 151 + static bool dma_fence_chain_enable_signaling(struct dma_fence *fence) 152 + { 153 + struct dma_fence_chain *head = to_dma_fence_chain(fence); 154 + 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; 159 + 160 + dma_fence_get(f); 161 + if (!dma_fence_add_callback(f, &head->cb, dma_fence_chain_cb)) { 162 + dma_fence_put(fence); 163 + return true; 164 + } 165 + dma_fence_put(f); 166 + } 167 + dma_fence_put(&head->base); 168 + return false; 169 + } 170 + 171 + static bool dma_fence_chain_signaled(struct dma_fence *fence) 172 + { 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; 176 + 177 + if (!dma_fence_is_signaled(f)) { 178 + dma_fence_put(fence); 179 + return false; 180 + } 181 + } 182 + 183 + return true; 184 + } 185 + 186 + static void dma_fence_chain_release(struct dma_fence *fence) 187 + { 188 + struct dma_fence_chain *chain = to_dma_fence_chain(fence); 189 + 190 + dma_fence_put(rcu_dereference_protected(chain->prev, true)); 191 + dma_fence_put(chain->fence); 192 + dma_fence_free(fence); 193 + } 194 + 195 + const struct dma_fence_ops dma_fence_chain_ops = { 196 + .get_driver_name = dma_fence_chain_get_driver_name, 197 + .get_timeline_name = dma_fence_chain_get_timeline_name, 198 + .enable_signaling = dma_fence_chain_enable_signaling, 199 + .signaled = dma_fence_chain_signaled, 200 + .release = dma_fence_chain_release, 201 + }; 202 + EXPORT_SYMBOL(dma_fence_chain_ops); 203 + 204 + /** 205 + * dma_fence_chain_init - initialize a fence chain 206 + * @chain: the chain node to initialize 207 + * @prev: the previous fence 208 + * @fence: the current fence 209 + * 210 + * Initialize a new chain node and either start a new chain or add the node to 211 + * the existing chain of the previous fence. 212 + */ 213 + void dma_fence_chain_init(struct dma_fence_chain *chain, 214 + struct dma_fence *prev, 215 + struct dma_fence *fence, 216 + uint64_t seqno) 217 + { 218 + struct dma_fence_chain *prev_chain = to_dma_fence_chain(prev); 219 + uint64_t context; 220 + 221 + spin_lock_init(&chain->lock); 222 + rcu_assign_pointer(chain->prev, prev); 223 + chain->fence = fence; 224 + chain->prev_seqno = 0; 225 + init_irq_work(&chain->work, dma_fence_chain_irq_work); 226 + 227 + /* Try to reuse the context of the previous chain node. */ 228 + if (prev_chain && __dma_fence_is_later(seqno, prev->seqno)) { 229 + context = prev->context; 230 + chain->prev_seqno = prev->seqno; 231 + } else { 232 + context = dma_fence_context_alloc(1); 233 + /* Make sure that we always have a valid sequence number. */ 234 + if (prev_chain) 235 + seqno = max(prev->seqno, seqno); 236 + } 237 + 238 + dma_fence_init(&chain->base, &dma_fence_chain_ops, 239 + &chain->lock, context, seqno); 240 + } 241 + EXPORT_SYMBOL(dma_fence_chain_init);
+81
include/linux/dma-fence-chain.h
··· 1 + /* 2 + * fence-chain: chain fences together in a timeline 3 + * 4 + * Copyright (C) 2018 Advanced Micro Devices, Inc. 5 + * Authors: 6 + * Christian König <christian.koenig@amd.com> 7 + * 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. 11 + * 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 15 + * more details. 16 + */ 17 + 18 + #ifndef __LINUX_DMA_FENCE_CHAIN_H 19 + #define __LINUX_DMA_FENCE_CHAIN_H 20 + 21 + #include <linux/dma-fence.h> 22 + #include <linux/irq_work.h> 23 + 24 + /** 25 + * struct dma_fence_chain - fence to represent an node of a fence chain 26 + * @base: fence base class 27 + * @lock: spinlock for fence handling 28 + * @prev: previous fence of the chain 29 + * @prev_seqno: original previous seqno before garbage collection 30 + * @fence: encapsulated fence 31 + * @cb: callback structure for signaling 32 + * @work: irq work item for signaling 33 + */ 34 + struct dma_fence_chain { 35 + struct dma_fence base; 36 + spinlock_t lock; 37 + struct dma_fence __rcu *prev; 38 + u64 prev_seqno; 39 + struct dma_fence *fence; 40 + struct dma_fence_cb cb; 41 + struct irq_work work; 42 + }; 43 + 44 + extern const struct dma_fence_ops dma_fence_chain_ops; 45 + 46 + /** 47 + * to_dma_fence_chain - cast a fence to a dma_fence_chain 48 + * @fence: fence to cast to a dma_fence_array 49 + * 50 + * Returns NULL if the fence is not a dma_fence_chain, 51 + * or the dma_fence_chain otherwise. 52 + */ 53 + static inline struct dma_fence_chain * 54 + to_dma_fence_chain(struct dma_fence *fence) 55 + { 56 + if (!fence || fence->ops != &dma_fence_chain_ops) 57 + return NULL; 58 + 59 + return container_of(fence, struct dma_fence_chain, base); 60 + } 61 + 62 + /** 63 + * dma_fence_chain_for_each - iterate over all fences in chain 64 + * @iter: current fence 65 + * @head: starting point 66 + * 67 + * Iterate over all fences in the chain. We keep a reference to the current 68 + * fence while inside the loop which must be dropped when breaking out. 69 + */ 70 + #define dma_fence_chain_for_each(iter, head) \ 71 + for (iter = dma_fence_get(head); iter; \ 72 + iter = dma_fence_chain_walk(iter)) 73 + 74 + struct dma_fence *dma_fence_chain_walk(struct dma_fence *fence); 75 + int dma_fence_chain_find_seqno(struct dma_fence **pfence, uint64_t seqno); 76 + void dma_fence_chain_init(struct dma_fence_chain *chain, 77 + struct dma_fence *prev, 78 + struct dma_fence *fence, 79 + uint64_t seqno); 80 + 81 + #endif /* __LINUX_DMA_FENCE_CHAIN_H */