18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only 28c2ecf20Sopenharmony_ci/* 38c2ecf20Sopenharmony_ci * dma-fence-array: aggregate fences to be waited together 48c2ecf20Sopenharmony_ci * 58c2ecf20Sopenharmony_ci * Copyright (C) 2016 Collabora Ltd 68c2ecf20Sopenharmony_ci * Copyright (C) 2016 Advanced Micro Devices, Inc. 78c2ecf20Sopenharmony_ci * Authors: 88c2ecf20Sopenharmony_ci * Gustavo Padovan <gustavo@padovan.org> 98c2ecf20Sopenharmony_ci * Christian König <christian.koenig@amd.com> 108c2ecf20Sopenharmony_ci */ 118c2ecf20Sopenharmony_ci 128c2ecf20Sopenharmony_ci#include <linux/export.h> 138c2ecf20Sopenharmony_ci#include <linux/slab.h> 148c2ecf20Sopenharmony_ci#include <linux/dma-fence-array.h> 158c2ecf20Sopenharmony_ci 168c2ecf20Sopenharmony_ci#define PENDING_ERROR 1 178c2ecf20Sopenharmony_ci 188c2ecf20Sopenharmony_cistatic const char *dma_fence_array_get_driver_name(struct dma_fence *fence) 198c2ecf20Sopenharmony_ci{ 208c2ecf20Sopenharmony_ci return "dma_fence_array"; 218c2ecf20Sopenharmony_ci} 228c2ecf20Sopenharmony_ci 238c2ecf20Sopenharmony_cistatic const char *dma_fence_array_get_timeline_name(struct dma_fence *fence) 248c2ecf20Sopenharmony_ci{ 258c2ecf20Sopenharmony_ci return "unbound"; 268c2ecf20Sopenharmony_ci} 278c2ecf20Sopenharmony_ci 288c2ecf20Sopenharmony_cistatic void dma_fence_array_set_pending_error(struct dma_fence_array *array, 298c2ecf20Sopenharmony_ci int error) 308c2ecf20Sopenharmony_ci{ 318c2ecf20Sopenharmony_ci /* 328c2ecf20Sopenharmony_ci * Propagate the first error reported by any of our fences, but only 338c2ecf20Sopenharmony_ci * before we ourselves are signaled. 348c2ecf20Sopenharmony_ci */ 358c2ecf20Sopenharmony_ci if (error) 368c2ecf20Sopenharmony_ci cmpxchg(&array->base.error, PENDING_ERROR, error); 378c2ecf20Sopenharmony_ci} 388c2ecf20Sopenharmony_ci 398c2ecf20Sopenharmony_cistatic void dma_fence_array_clear_pending_error(struct dma_fence_array *array) 408c2ecf20Sopenharmony_ci{ 418c2ecf20Sopenharmony_ci /* Clear the error flag if not actually set. */ 428c2ecf20Sopenharmony_ci cmpxchg(&array->base.error, PENDING_ERROR, 0); 438c2ecf20Sopenharmony_ci} 448c2ecf20Sopenharmony_ci 458c2ecf20Sopenharmony_cistatic void irq_dma_fence_array_work(struct irq_work *wrk) 468c2ecf20Sopenharmony_ci{ 478c2ecf20Sopenharmony_ci struct dma_fence_array *array = container_of(wrk, typeof(*array), work); 488c2ecf20Sopenharmony_ci 498c2ecf20Sopenharmony_ci dma_fence_array_clear_pending_error(array); 508c2ecf20Sopenharmony_ci 518c2ecf20Sopenharmony_ci dma_fence_signal(&array->base); 528c2ecf20Sopenharmony_ci dma_fence_put(&array->base); 538c2ecf20Sopenharmony_ci} 548c2ecf20Sopenharmony_ci 558c2ecf20Sopenharmony_cistatic void dma_fence_array_cb_func(struct dma_fence *f, 568c2ecf20Sopenharmony_ci struct dma_fence_cb *cb) 578c2ecf20Sopenharmony_ci{ 588c2ecf20Sopenharmony_ci struct dma_fence_array_cb *array_cb = 598c2ecf20Sopenharmony_ci container_of(cb, struct dma_fence_array_cb, cb); 608c2ecf20Sopenharmony_ci struct dma_fence_array *array = array_cb->array; 618c2ecf20Sopenharmony_ci 628c2ecf20Sopenharmony_ci dma_fence_array_set_pending_error(array, f->error); 638c2ecf20Sopenharmony_ci 648c2ecf20Sopenharmony_ci if (atomic_dec_and_test(&array->num_pending)) 658c2ecf20Sopenharmony_ci irq_work_queue(&array->work); 668c2ecf20Sopenharmony_ci else 678c2ecf20Sopenharmony_ci dma_fence_put(&array->base); 688c2ecf20Sopenharmony_ci} 698c2ecf20Sopenharmony_ci 708c2ecf20Sopenharmony_cistatic bool dma_fence_array_enable_signaling(struct dma_fence *fence) 718c2ecf20Sopenharmony_ci{ 728c2ecf20Sopenharmony_ci struct dma_fence_array *array = to_dma_fence_array(fence); 738c2ecf20Sopenharmony_ci struct dma_fence_array_cb *cb = (void *)(&array[1]); 748c2ecf20Sopenharmony_ci unsigned i; 758c2ecf20Sopenharmony_ci 768c2ecf20Sopenharmony_ci for (i = 0; i < array->num_fences; ++i) { 778c2ecf20Sopenharmony_ci cb[i].array = array; 788c2ecf20Sopenharmony_ci /* 798c2ecf20Sopenharmony_ci * As we may report that the fence is signaled before all 808c2ecf20Sopenharmony_ci * callbacks are complete, we need to take an additional 818c2ecf20Sopenharmony_ci * reference count on the array so that we do not free it too 828c2ecf20Sopenharmony_ci * early. The core fence handling will only hold the reference 838c2ecf20Sopenharmony_ci * until we signal the array as complete (but that is now 848c2ecf20Sopenharmony_ci * insufficient). 858c2ecf20Sopenharmony_ci */ 868c2ecf20Sopenharmony_ci dma_fence_get(&array->base); 878c2ecf20Sopenharmony_ci if (dma_fence_add_callback(array->fences[i], &cb[i].cb, 888c2ecf20Sopenharmony_ci dma_fence_array_cb_func)) { 898c2ecf20Sopenharmony_ci int error = array->fences[i]->error; 908c2ecf20Sopenharmony_ci 918c2ecf20Sopenharmony_ci dma_fence_array_set_pending_error(array, error); 928c2ecf20Sopenharmony_ci dma_fence_put(&array->base); 938c2ecf20Sopenharmony_ci if (atomic_dec_and_test(&array->num_pending)) { 948c2ecf20Sopenharmony_ci dma_fence_array_clear_pending_error(array); 958c2ecf20Sopenharmony_ci return false; 968c2ecf20Sopenharmony_ci } 978c2ecf20Sopenharmony_ci } 988c2ecf20Sopenharmony_ci } 998c2ecf20Sopenharmony_ci 1008c2ecf20Sopenharmony_ci return true; 1018c2ecf20Sopenharmony_ci} 1028c2ecf20Sopenharmony_ci 1038c2ecf20Sopenharmony_cistatic bool dma_fence_array_signaled(struct dma_fence *fence) 1048c2ecf20Sopenharmony_ci{ 1058c2ecf20Sopenharmony_ci struct dma_fence_array *array = to_dma_fence_array(fence); 1068c2ecf20Sopenharmony_ci 1078c2ecf20Sopenharmony_ci if (atomic_read(&array->num_pending) > 0) 1088c2ecf20Sopenharmony_ci return false; 1098c2ecf20Sopenharmony_ci 1108c2ecf20Sopenharmony_ci dma_fence_array_clear_pending_error(array); 1118c2ecf20Sopenharmony_ci return true; 1128c2ecf20Sopenharmony_ci} 1138c2ecf20Sopenharmony_ci 1148c2ecf20Sopenharmony_cistatic void dma_fence_array_release(struct dma_fence *fence) 1158c2ecf20Sopenharmony_ci{ 1168c2ecf20Sopenharmony_ci struct dma_fence_array *array = to_dma_fence_array(fence); 1178c2ecf20Sopenharmony_ci unsigned i; 1188c2ecf20Sopenharmony_ci 1198c2ecf20Sopenharmony_ci for (i = 0; i < array->num_fences; ++i) 1208c2ecf20Sopenharmony_ci dma_fence_put(array->fences[i]); 1218c2ecf20Sopenharmony_ci 1228c2ecf20Sopenharmony_ci kfree(array->fences); 1238c2ecf20Sopenharmony_ci dma_fence_free(fence); 1248c2ecf20Sopenharmony_ci} 1258c2ecf20Sopenharmony_ci 1268c2ecf20Sopenharmony_ciconst struct dma_fence_ops dma_fence_array_ops = { 1278c2ecf20Sopenharmony_ci .get_driver_name = dma_fence_array_get_driver_name, 1288c2ecf20Sopenharmony_ci .get_timeline_name = dma_fence_array_get_timeline_name, 1298c2ecf20Sopenharmony_ci .enable_signaling = dma_fence_array_enable_signaling, 1308c2ecf20Sopenharmony_ci .signaled = dma_fence_array_signaled, 1318c2ecf20Sopenharmony_ci .release = dma_fence_array_release, 1328c2ecf20Sopenharmony_ci}; 1338c2ecf20Sopenharmony_ciEXPORT_SYMBOL(dma_fence_array_ops); 1348c2ecf20Sopenharmony_ci 1358c2ecf20Sopenharmony_ci/** 1368c2ecf20Sopenharmony_ci * dma_fence_array_create - Create a custom fence array 1378c2ecf20Sopenharmony_ci * @num_fences: [in] number of fences to add in the array 1388c2ecf20Sopenharmony_ci * @fences: [in] array containing the fences 1398c2ecf20Sopenharmony_ci * @context: [in] fence context to use 1408c2ecf20Sopenharmony_ci * @seqno: [in] sequence number to use 1418c2ecf20Sopenharmony_ci * @signal_on_any: [in] signal on any fence in the array 1428c2ecf20Sopenharmony_ci * 1438c2ecf20Sopenharmony_ci * Allocate a dma_fence_array object and initialize the base fence with 1448c2ecf20Sopenharmony_ci * dma_fence_init(). 1458c2ecf20Sopenharmony_ci * In case of error it returns NULL. 1468c2ecf20Sopenharmony_ci * 1478c2ecf20Sopenharmony_ci * The caller should allocate the fences array with num_fences size 1488c2ecf20Sopenharmony_ci * and fill it with the fences it wants to add to the object. Ownership of this 1498c2ecf20Sopenharmony_ci * array is taken and dma_fence_put() is used on each fence on release. 1508c2ecf20Sopenharmony_ci * 1518c2ecf20Sopenharmony_ci * If @signal_on_any is true the fence array signals if any fence in the array 1528c2ecf20Sopenharmony_ci * signals, otherwise it signals when all fences in the array signal. 1538c2ecf20Sopenharmony_ci */ 1548c2ecf20Sopenharmony_cistruct dma_fence_array *dma_fence_array_create(int num_fences, 1558c2ecf20Sopenharmony_ci struct dma_fence **fences, 1568c2ecf20Sopenharmony_ci u64 context, unsigned seqno, 1578c2ecf20Sopenharmony_ci bool signal_on_any) 1588c2ecf20Sopenharmony_ci{ 1598c2ecf20Sopenharmony_ci struct dma_fence_array *array; 1608c2ecf20Sopenharmony_ci size_t size = sizeof(*array); 1618c2ecf20Sopenharmony_ci 1628c2ecf20Sopenharmony_ci /* Allocate the callback structures behind the array. */ 1638c2ecf20Sopenharmony_ci size += num_fences * sizeof(struct dma_fence_array_cb); 1648c2ecf20Sopenharmony_ci array = kzalloc(size, GFP_KERNEL); 1658c2ecf20Sopenharmony_ci if (!array) 1668c2ecf20Sopenharmony_ci return NULL; 1678c2ecf20Sopenharmony_ci 1688c2ecf20Sopenharmony_ci spin_lock_init(&array->lock); 1698c2ecf20Sopenharmony_ci dma_fence_init(&array->base, &dma_fence_array_ops, &array->lock, 1708c2ecf20Sopenharmony_ci context, seqno); 1718c2ecf20Sopenharmony_ci init_irq_work(&array->work, irq_dma_fence_array_work); 1728c2ecf20Sopenharmony_ci 1738c2ecf20Sopenharmony_ci array->num_fences = num_fences; 1748c2ecf20Sopenharmony_ci atomic_set(&array->num_pending, signal_on_any ? 1 : num_fences); 1758c2ecf20Sopenharmony_ci array->fences = fences; 1768c2ecf20Sopenharmony_ci 1778c2ecf20Sopenharmony_ci array->base.error = PENDING_ERROR; 1788c2ecf20Sopenharmony_ci 1798c2ecf20Sopenharmony_ci return array; 1808c2ecf20Sopenharmony_ci} 1818c2ecf20Sopenharmony_ciEXPORT_SYMBOL(dma_fence_array_create); 1828c2ecf20Sopenharmony_ci 1838c2ecf20Sopenharmony_ci/** 1848c2ecf20Sopenharmony_ci * dma_fence_match_context - Check if all fences are from the given context 1858c2ecf20Sopenharmony_ci * @fence: [in] fence or fence array 1868c2ecf20Sopenharmony_ci * @context: [in] fence context to check all fences against 1878c2ecf20Sopenharmony_ci * 1888c2ecf20Sopenharmony_ci * Checks the provided fence or, for a fence array, all fences in the array 1898c2ecf20Sopenharmony_ci * against the given context. Returns false if any fence is from a different 1908c2ecf20Sopenharmony_ci * context. 1918c2ecf20Sopenharmony_ci */ 1928c2ecf20Sopenharmony_cibool dma_fence_match_context(struct dma_fence *fence, u64 context) 1938c2ecf20Sopenharmony_ci{ 1948c2ecf20Sopenharmony_ci struct dma_fence_array *array = to_dma_fence_array(fence); 1958c2ecf20Sopenharmony_ci unsigned i; 1968c2ecf20Sopenharmony_ci 1978c2ecf20Sopenharmony_ci if (!dma_fence_is_array(fence)) 1988c2ecf20Sopenharmony_ci return fence->context == context; 1998c2ecf20Sopenharmony_ci 2008c2ecf20Sopenharmony_ci for (i = 0; i < array->num_fences; i++) { 2018c2ecf20Sopenharmony_ci if (array->fences[i]->context != context) 2028c2ecf20Sopenharmony_ci return false; 2038c2ecf20Sopenharmony_ci } 2048c2ecf20Sopenharmony_ci 2058c2ecf20Sopenharmony_ci return true; 2068c2ecf20Sopenharmony_ci} 2078c2ecf20Sopenharmony_ciEXPORT_SYMBOL(dma_fence_match_context); 208