Line data Source code
1 : // SPDX-License-Identifier: GPL-2.0-only 2 : /* 3 : * dma-fence-util: misc functions for dma_fence objects 4 : * 5 : * Copyright (C) 2022 Advanced Micro Devices, Inc. 6 : * Authors: 7 : * Christian König <christian.koenig@amd.com> 8 : */ 9 : 10 : #include <linux/dma-fence.h> 11 : #include <linux/dma-fence-array.h> 12 : #include <linux/dma-fence-chain.h> 13 : #include <linux/dma-fence-unwrap.h> 14 : #include <linux/slab.h> 15 : 16 : /* Internal helper to start new array iteration, don't use directly */ 17 : static struct dma_fence * 18 0 : __dma_fence_unwrap_array(struct dma_fence_unwrap *cursor) 19 : { 20 0 : cursor->array = dma_fence_chain_contained(cursor->chain); 21 0 : cursor->index = 0; 22 0 : return dma_fence_array_first(cursor->array); 23 : } 24 : 25 : /** 26 : * dma_fence_unwrap_first - return the first fence from fence containers 27 : * @head: the entrypoint into the containers 28 : * @cursor: current position inside the containers 29 : * 30 : * Unwraps potential dma_fence_chain/dma_fence_array containers and return the 31 : * first fence. 32 : */ 33 0 : struct dma_fence *dma_fence_unwrap_first(struct dma_fence *head, 34 : struct dma_fence_unwrap *cursor) 35 : { 36 0 : cursor->chain = dma_fence_get(head); 37 0 : return __dma_fence_unwrap_array(cursor); 38 : } 39 : EXPORT_SYMBOL_GPL(dma_fence_unwrap_first); 40 : 41 : /** 42 : * dma_fence_unwrap_next - return the next fence from a fence containers 43 : * @cursor: current position inside the containers 44 : * 45 : * Continue unwrapping the dma_fence_chain/dma_fence_array containers and return 46 : * the next fence from them. 47 : */ 48 0 : struct dma_fence *dma_fence_unwrap_next(struct dma_fence_unwrap *cursor) 49 : { 50 : struct dma_fence *tmp; 51 : 52 0 : ++cursor->index; 53 0 : tmp = dma_fence_array_next(cursor->array, cursor->index); 54 0 : if (tmp) 55 : return tmp; 56 : 57 0 : cursor->chain = dma_fence_chain_walk(cursor->chain); 58 0 : return __dma_fence_unwrap_array(cursor); 59 : } 60 : EXPORT_SYMBOL_GPL(dma_fence_unwrap_next); 61 : 62 : /* Implementation for the dma_fence_merge() marco, don't use directly */ 63 0 : struct dma_fence *__dma_fence_unwrap_merge(unsigned int num_fences, 64 : struct dma_fence **fences, 65 : struct dma_fence_unwrap *iter) 66 : { 67 : struct dma_fence_array *result; 68 : struct dma_fence *tmp, **array; 69 : unsigned int i; 70 : size_t count; 71 : 72 0 : count = 0; 73 0 : for (i = 0; i < num_fences; ++i) { 74 0 : dma_fence_unwrap_for_each(tmp, &iter[i], fences[i]) 75 0 : if (!dma_fence_is_signaled(tmp)) 76 0 : ++count; 77 : } 78 : 79 0 : if (count == 0) 80 0 : return dma_fence_get_stub(); 81 : 82 0 : array = kmalloc_array(count, sizeof(*array), GFP_KERNEL); 83 0 : if (!array) 84 : return NULL; 85 : 86 : /* 87 : * This trashes the input fence array and uses it as position for the 88 : * following merge loop. This works because the dma_fence_merge() 89 : * wrapper macro is creating this temporary array on the stack together 90 : * with the iterators. 91 : */ 92 0 : for (i = 0; i < num_fences; ++i) 93 0 : fences[i] = dma_fence_unwrap_first(fences[i], &iter[i]); 94 : 95 : count = 0; 96 : do { 97 : unsigned int sel; 98 : 99 : restart: 100 0 : tmp = NULL; 101 0 : for (i = 0; i < num_fences; ++i) { 102 : struct dma_fence *next; 103 : 104 0 : while (fences[i] && dma_fence_is_signaled(fences[i])) 105 0 : fences[i] = dma_fence_unwrap_next(&iter[i]); 106 : 107 0 : next = fences[i]; 108 0 : if (!next) 109 0 : continue; 110 : 111 : /* 112 : * We can't guarantee that inpute fences are ordered by 113 : * context, but it is still quite likely when this 114 : * function is used multiple times. So attempt to order 115 : * the fences by context as we pass over them and merge 116 : * fences with the same context. 117 : */ 118 0 : if (!tmp || tmp->context > next->context) { 119 : tmp = next; 120 : sel = i; 121 : 122 0 : } else if (tmp->context < next->context) { 123 0 : continue; 124 : 125 0 : } else if (dma_fence_is_later(tmp, next)) { 126 0 : fences[i] = dma_fence_unwrap_next(&iter[i]); 127 0 : goto restart; 128 : } else { 129 0 : fences[sel] = dma_fence_unwrap_next(&iter[sel]); 130 0 : goto restart; 131 : } 132 : } 133 : 134 0 : if (tmp) { 135 0 : array[count++] = dma_fence_get(tmp); 136 0 : fences[sel] = dma_fence_unwrap_next(&iter[sel]); 137 : } 138 0 : } while (tmp); 139 : 140 0 : if (count == 0) { 141 0 : tmp = dma_fence_get_stub(); 142 0 : goto return_tmp; 143 : } 144 : 145 0 : if (count == 1) { 146 0 : tmp = array[0]; 147 0 : goto return_tmp; 148 : } 149 : 150 0 : result = dma_fence_array_create(count, array, 151 : dma_fence_context_alloc(1), 152 : 1, false); 153 0 : if (!result) { 154 : tmp = NULL; 155 : goto return_tmp; 156 : } 157 0 : return &result->base; 158 : 159 : return_tmp: 160 0 : kfree(array); 161 0 : return tmp; 162 : } 163 : EXPORT_SYMBOL_GPL(__dma_fence_unwrap_merge);