/* $NetBSD: linux_dma_fence_array.c,v 1.4 2021/12/19 12:39:56 riastradh Exp $ */ /*- * Copyright (c) 2021 The NetBSD Foundation, Inc. * All rights reserved. * * This code is derived from software contributed to The NetBSD Foundation * by Taylor R. Campbell. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE * POSSIBILITY OF SUCH DAMAGE. */ #include __KERNEL_RCSID(0, "$NetBSD: linux_dma_fence_array.c,v 1.4 2021/12/19 12:39:56 riastradh Exp $"); #include #include static const char * dma_fence_array_driver_name(struct dma_fence *fence) { return "dma_fence_array"; } static const char * dma_fence_array_timeline_name(struct dma_fence *fence) { return "unbound"; } static void dma_fence_array_done1(struct dma_fence *fence, struct dma_fence_cb *cb) { struct dma_fence_array_cb *C = container_of(cb, struct dma_fence_array_cb, dfac_cb); struct dma_fence_array *A = C->dfac_array; KASSERT(spin_is_locked(&A->dfa_lock)); if (fence->error && A->base.error == 1) { KASSERT(fence->error != 1); A->base.error = fence->error; } if (--A->dfa_npending) { dma_fence_put(&A->base); return; } /* Last one out, hit the lights -- dma_fence_array_done. */ irq_work_queue(&A->dfa_work); } static void dma_fence_array_done(struct irq_work *W) { struct dma_fence_array *A = container_of(W, struct dma_fence_array, dfa_work); spin_lock(&A->dfa_lock); if (A->base.error == 1) A->base.error = 0; dma_fence_signal_locked(&A->base); spin_unlock(&A->dfa_lock); dma_fence_put(&A->base); } static bool dma_fence_array_enable_signaling(struct dma_fence *fence) { struct dma_fence_array *A = to_dma_fence_array(fence); struct dma_fence_array_cb *C; unsigned i; int error; KASSERT(spin_is_locked(&A->dfa_lock)); for (i = 0; i < A->num_fences; i++) { C = &A->dfa_cb[i]; C->dfac_array = A; dma_fence_get(&A->base); if (dma_fence_add_callback(A->fences[i], &C->dfac_cb, dma_fence_array_done1)) { error = A->fences[i]->error; if (error) { KASSERT(error != 1); if (A->base.error == 1) A->base.error = error; } dma_fence_put(&A->base); if (--A->dfa_npending == 0) { if (A->base.error == 1) A->base.error = 0; return false; } } } return true; } static bool dma_fence_array_signaled(struct dma_fence *fence) { struct dma_fence_array *A = to_dma_fence_array(fence); KASSERT(spin_is_locked(&A->dfa_lock)); return A->dfa_npending == 0; } static void dma_fence_array_release(struct dma_fence *fence) { struct dma_fence_array *A = to_dma_fence_array(fence); unsigned i; for (i = 0; i < A->num_fences; i++) dma_fence_put(A->fences[i]); kfree(A->fences); spin_lock_destroy(&A->dfa_lock); dma_fence_free(fence); } static const struct dma_fence_ops dma_fence_array_ops = { .get_driver_name = dma_fence_array_driver_name, .get_timeline_name = dma_fence_array_timeline_name, .enable_signaling = dma_fence_array_enable_signaling, .signaled = dma_fence_array_signaled, .release = dma_fence_array_release, }; struct dma_fence_array * dma_fence_array_create(int num_fences, struct dma_fence **fences, unsigned context, unsigned seqno, bool signal_on_any) { struct dma_fence_array *A; /* * Must be allocated with kmalloc or equivalent because * dma-fence will free it with kfree. */ A = kzalloc(struct_size(A, dfa_cb, num_fences), GFP_KERNEL); if (A == NULL) return NULL; A->fences = fences; A->num_fences = num_fences; A->dfa_npending = signal_on_any ? 1 : num_fences; spin_lock_init(&A->dfa_lock); dma_fence_init(&A->base, &dma_fence_array_ops, &A->dfa_lock, context, seqno); init_irq_work(&A->dfa_work, dma_fence_array_done); return A; } bool dma_fence_is_array(struct dma_fence *fence) { return fence->ops == &dma_fence_array_ops; } struct dma_fence_array * to_dma_fence_array(struct dma_fence *fence) { KASSERT(dma_fence_is_array(fence)); return container_of(fence, struct dma_fence_array, base); }