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

dma-buf: add dma_fence_unwrap v2

Add a general purpose helper to deep dive into dma_fence_chain/dma_fence_array
structures and iterate over all the fences in them.

This is useful when we need to flatten out all fences in those structures.

v2: some selftests cleanup, improved function naming and documentation

Signed-off-by: Christian König <christian.koenig@amd.com>
Reviewed-by: Daniel Vetter <daniel.vetter@ffwll.ch>
Link: https://patchwork.freedesktop.org/patch/msgid/20220311110244.1245-1-christian.koenig@amd.com

+368
+6
Documentation/driver-api/dma-buf.rst
··· 185 185 .. kernel-doc:: include/linux/dma-fence-chain.h 186 186 :internal: 187 187 188 + DMA Fence unwrap 189 + ~~~~~~~~~~~~~~~~ 190 + 191 + .. kernel-doc:: include/linux/dma-fence-unwrap.h 192 + :internal: 193 + 188 194 DMA Fence uABI/Sync File 189 195 ~~~~~~~~~~~~~~~~~~~~~~~~ 190 196
+1
drivers/dma-buf/Makefile
··· 12 12 selftest.o \ 13 13 st-dma-fence.o \ 14 14 st-dma-fence-chain.o \ 15 + st-dma-fence-unwrap.o \ 15 16 st-dma-resv.o 16 17 17 18 obj-$(CONFIG_DMABUF_SELFTESTS) += dmabuf_selftests.o
+1
drivers/dma-buf/selftests.h
··· 12 12 selftest(sanitycheck, __sanitycheck__) /* keep first (igt selfcheck) */ 13 13 selftest(dma_fence, dma_fence) 14 14 selftest(dma_fence_chain, dma_fence_chain) 15 + selftest(dma_fence_unwrap, dma_fence_unwrap) 15 16 selftest(dma_resv, dma_resv)
+261
drivers/dma-buf/st-dma-fence-unwrap.c
··· 1 + // SPDX-License-Identifier: MIT 2 + 3 + /* 4 + * Copyright (C) 2022 Advanced Micro Devices, Inc. 5 + */ 6 + 7 + #include <linux/dma-fence-unwrap.h> 8 + #if 0 9 + #include <linux/kernel.h> 10 + #include <linux/kthread.h> 11 + #include <linux/mm.h> 12 + #include <linux/sched/signal.h> 13 + #include <linux/slab.h> 14 + #include <linux/spinlock.h> 15 + #include <linux/random.h> 16 + #endif 17 + 18 + #include "selftest.h" 19 + 20 + #define CHAIN_SZ (4 << 10) 21 + 22 + static inline struct mock_fence { 23 + struct dma_fence base; 24 + spinlock_t lock; 25 + } *to_mock_fence(struct dma_fence *f) { 26 + return container_of(f, struct mock_fence, base); 27 + } 28 + 29 + static const char *mock_name(struct dma_fence *f) 30 + { 31 + return "mock"; 32 + } 33 + 34 + static const struct dma_fence_ops mock_ops = { 35 + .get_driver_name = mock_name, 36 + .get_timeline_name = mock_name, 37 + }; 38 + 39 + static struct dma_fence *mock_fence(void) 40 + { 41 + struct mock_fence *f; 42 + 43 + f = kmalloc(sizeof(*f), GFP_KERNEL); 44 + if (!f) 45 + return NULL; 46 + 47 + spin_lock_init(&f->lock); 48 + dma_fence_init(&f->base, &mock_ops, &f->lock, 0, 0); 49 + 50 + return &f->base; 51 + } 52 + 53 + static struct dma_fence *mock_array(unsigned int num_fences, ...) 54 + { 55 + struct dma_fence_array *array; 56 + struct dma_fence **fences; 57 + va_list valist; 58 + int i; 59 + 60 + fences = kcalloc(num_fences, sizeof(*fences), GFP_KERNEL); 61 + if (!fences) 62 + return NULL; 63 + 64 + va_start(valist, num_fences); 65 + for (i = 0; i < num_fences; ++i) 66 + fences[i] = va_arg(valist, typeof(*fences)); 67 + va_end(valist); 68 + 69 + array = dma_fence_array_create(num_fences, fences, 70 + dma_fence_context_alloc(1), 71 + 1, false); 72 + if (!array) 73 + goto cleanup; 74 + return &array->base; 75 + 76 + cleanup: 77 + for (i = 0; i < num_fences; ++i) 78 + dma_fence_put(fences[i]); 79 + kfree(fences); 80 + return NULL; 81 + } 82 + 83 + static struct dma_fence *mock_chain(struct dma_fence *prev, 84 + struct dma_fence *fence) 85 + { 86 + struct dma_fence_chain *f; 87 + 88 + f = dma_fence_chain_alloc(); 89 + if (!f) { 90 + dma_fence_put(prev); 91 + dma_fence_put(fence); 92 + return NULL; 93 + } 94 + 95 + dma_fence_chain_init(f, prev, fence, 1); 96 + return &f->base; 97 + } 98 + 99 + static int sanitycheck(void *arg) 100 + { 101 + struct dma_fence *f, *chain, *array; 102 + int err = 0; 103 + 104 + f = mock_fence(); 105 + if (!f) 106 + return -ENOMEM; 107 + 108 + array = mock_array(1, f); 109 + if (!array) 110 + return -ENOMEM; 111 + 112 + chain = mock_chain(NULL, array); 113 + if (!chain) 114 + return -ENOMEM; 115 + 116 + dma_fence_signal(f); 117 + dma_fence_put(chain); 118 + return err; 119 + } 120 + 121 + static int unwrap_array(void *arg) 122 + { 123 + struct dma_fence *fence, *f1, *f2, *array; 124 + struct dma_fence_unwrap iter; 125 + int err = 0; 126 + 127 + f1 = mock_fence(); 128 + if (!f1) 129 + return -ENOMEM; 130 + 131 + f2 = mock_fence(); 132 + if (!f2) { 133 + dma_fence_put(f1); 134 + return -ENOMEM; 135 + } 136 + 137 + array = mock_array(2, f1, f2); 138 + if (!array) 139 + return -ENOMEM; 140 + 141 + dma_fence_unwrap_for_each(fence, &iter, array) { 142 + if (fence == f1) { 143 + f1 = NULL; 144 + } else if (fence == f2) { 145 + f2 = NULL; 146 + } else { 147 + pr_err("Unexpected fence!\n"); 148 + err = -EINVAL; 149 + } 150 + } 151 + 152 + if (f1 || f2) { 153 + pr_err("Not all fences seen!\n"); 154 + err = -EINVAL; 155 + } 156 + 157 + dma_fence_signal(f1); 158 + dma_fence_signal(f2); 159 + dma_fence_put(array); 160 + return 0; 161 + } 162 + 163 + static int unwrap_chain(void *arg) 164 + { 165 + struct dma_fence *fence, *f1, *f2, *chain; 166 + struct dma_fence_unwrap iter; 167 + int err = 0; 168 + 169 + f1 = mock_fence(); 170 + if (!f1) 171 + return -ENOMEM; 172 + 173 + f2 = mock_fence(); 174 + if (!f2) { 175 + dma_fence_put(f1); 176 + return -ENOMEM; 177 + } 178 + 179 + chain = mock_chain(f1, f2); 180 + if (!chain) 181 + return -ENOMEM; 182 + 183 + dma_fence_unwrap_for_each(fence, &iter, chain) { 184 + if (fence == f1) { 185 + f1 = NULL; 186 + } else if (fence == f2) { 187 + f2 = NULL; 188 + } else { 189 + pr_err("Unexpected fence!\n"); 190 + err = -EINVAL; 191 + } 192 + } 193 + 194 + if (f1 || f2) { 195 + pr_err("Not all fences seen!\n"); 196 + err = -EINVAL; 197 + } 198 + 199 + dma_fence_signal(f1); 200 + dma_fence_signal(f2); 201 + dma_fence_put(chain); 202 + return 0; 203 + } 204 + 205 + static int unwrap_chain_array(void *arg) 206 + { 207 + struct dma_fence *fence, *f1, *f2, *array, *chain; 208 + struct dma_fence_unwrap iter; 209 + int err = 0; 210 + 211 + f1 = mock_fence(); 212 + if (!f1) 213 + return -ENOMEM; 214 + 215 + f2 = mock_fence(); 216 + if (!f2) { 217 + dma_fence_put(f1); 218 + return -ENOMEM; 219 + } 220 + 221 + array = mock_array(2, f1, f2); 222 + if (!array) 223 + return -ENOMEM; 224 + 225 + chain = mock_chain(NULL, array); 226 + if (!chain) 227 + return -ENOMEM; 228 + 229 + dma_fence_unwrap_for_each(fence, &iter, chain) { 230 + if (fence == f1) { 231 + f1 = NULL; 232 + } else if (fence == f2) { 233 + f2 = NULL; 234 + } else { 235 + pr_err("Unexpected fence!\n"); 236 + err = -EINVAL; 237 + } 238 + } 239 + 240 + if (f1 || f2) { 241 + pr_err("Not all fences seen!\n"); 242 + err = -EINVAL; 243 + } 244 + 245 + dma_fence_signal(f1); 246 + dma_fence_signal(f2); 247 + dma_fence_put(chain); 248 + return 0; 249 + } 250 + 251 + int dma_fence_unwrap(void) 252 + { 253 + static const struct subtest tests[] = { 254 + SUBTEST(sanitycheck), 255 + SUBTEST(unwrap_array), 256 + SUBTEST(unwrap_chain), 257 + SUBTEST(unwrap_chain_array), 258 + }; 259 + 260 + return subtests(tests, NULL); 261 + }
+2
include/linux/dma-fence-array.h
··· 69 69 * 70 70 * Test if @array is a dma_fence_array object and if yes iterate over all fences 71 71 * in the array. If not just iterate over the fence in @array itself. 72 + * 73 + * For a deep dive iterator see dma_fence_unwrap_for_each(). 72 74 */ 73 75 #define dma_fence_array_for_each(fence, index, head) \ 74 76 for (index = 0, fence = dma_fence_array_first(head); fence; \
+2
include/linux/dma-fence-chain.h
··· 112 112 * 113 113 * Iterate over all fences in the chain. We keep a reference to the current 114 114 * fence while inside the loop which must be dropped when breaking out. 115 + * 116 + * For a deep dive iterator see dma_fence_unwrap_for_each(). 115 117 */ 116 118 #define dma_fence_chain_for_each(iter, head) \ 117 119 for (iter = dma_fence_get(head); iter; \
+95
include/linux/dma-fence-unwrap.h
··· 1 + /* SPDX-License-Identifier: GPL-2.0-only */ 2 + /* 3 + * fence-chain: chain fences together in a timeline 4 + * 5 + * Copyright (C) 2022 Advanced Micro Devices, Inc. 6 + * Authors: 7 + * Christian König <christian.koenig@amd.com> 8 + */ 9 + 10 + #ifndef __LINUX_DMA_FENCE_UNWRAP_H 11 + #define __LINUX_DMA_FENCE_UNWRAP_H 12 + 13 + #include <linux/dma-fence-chain.h> 14 + #include <linux/dma-fence-array.h> 15 + 16 + /** 17 + * struct dma_fence_unwrap - cursor into the container structure 18 + * 19 + * Should be used with dma_fence_unwrap_for_each() iterator macro. 20 + */ 21 + struct dma_fence_unwrap { 22 + /** 23 + * @chain: potential dma_fence_chain, but can be other fence as well 24 + */ 25 + struct dma_fence *chain; 26 + /** 27 + * @array: potential dma_fence_array, but can be other fence as well 28 + */ 29 + struct dma_fence *array; 30 + /** 31 + * @index: last returned index if @array is really a dma_fence_array 32 + */ 33 + unsigned int index; 34 + }; 35 + 36 + /* Internal helper to start new array iteration, don't use directly */ 37 + static inline struct dma_fence * 38 + __dma_fence_unwrap_array(struct dma_fence_unwrap * cursor) 39 + { 40 + cursor->array = dma_fence_chain_contained(cursor->chain); 41 + cursor->index = 0; 42 + return dma_fence_array_first(cursor->array); 43 + } 44 + 45 + /** 46 + * dma_fence_unwrap_first - return the first fence from fence containers 47 + * @head: the entrypoint into the containers 48 + * @cursor: current position inside the containers 49 + * 50 + * Unwraps potential dma_fence_chain/dma_fence_array containers and return the 51 + * first fence. 52 + */ 53 + static inline struct dma_fence * 54 + dma_fence_unwrap_first(struct dma_fence *head, struct dma_fence_unwrap *cursor) 55 + { 56 + cursor->chain = dma_fence_get(head); 57 + return __dma_fence_unwrap_array(cursor); 58 + } 59 + 60 + /** 61 + * dma_fence_unwrap_next - return the next fence from a fence containers 62 + * @cursor: current position inside the containers 63 + * 64 + * Continue unwrapping the dma_fence_chain/dma_fence_array containers and return 65 + * the next fence from them. 66 + */ 67 + static inline struct dma_fence * 68 + dma_fence_unwrap_next(struct dma_fence_unwrap *cursor) 69 + { 70 + struct dma_fence *tmp; 71 + 72 + ++cursor->index; 73 + tmp = dma_fence_array_next(cursor->array, cursor->index); 74 + if (tmp) 75 + return tmp; 76 + 77 + cursor->chain = dma_fence_chain_walk(cursor->chain); 78 + return __dma_fence_unwrap_array(cursor); 79 + } 80 + 81 + /** 82 + * dma_fence_unwrap_for_each - iterate over all fences in containers 83 + * @fence: current fence 84 + * @cursor: current position inside the containers 85 + * @head: starting point for the iterator 86 + * 87 + * Unwrap dma_fence_chain and dma_fence_array containers and deep dive into all 88 + * potential fences in them. If @head is just a normal fence only that one is 89 + * returned. 90 + */ 91 + #define dma_fence_unwrap_for_each(fence, cursor, head) \ 92 + for (fence = dma_fence_unwrap_first(head, cursor); fence; \ 93 + fence = dma_fence_unwrap_next(cursor)) 94 + 95 + #endif