Home | History | Annotate | Line # | Download | only in linux
      1 /*	$NetBSD: linux_dma_fence_array.c,v 1.4 2021/12/19 12:39:56 riastradh Exp $	*/
      2 
      3 /*-
      4  * Copyright (c) 2021 The NetBSD Foundation, Inc.
      5  * All rights reserved.
      6  *
      7  * This code is derived from software contributed to The NetBSD Foundation
      8  * by Taylor R. Campbell.
      9  *
     10  * Redistribution and use in source and binary forms, with or without
     11  * modification, are permitted provided that the following conditions
     12  * are met:
     13  * 1. Redistributions of source code must retain the above copyright
     14  *    notice, this list of conditions and the following disclaimer.
     15  * 2. Redistributions in binary form must reproduce the above copyright
     16  *    notice, this list of conditions and the following disclaimer in the
     17  *    documentation and/or other materials provided with the distribution.
     18  *
     19  * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
     20  * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
     21  * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
     22  * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
     23  * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
     24  * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
     25  * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
     26  * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
     27  * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
     28  * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
     29  * POSSIBILITY OF SUCH DAMAGE.
     30  */
     31 
     32 #include <sys/cdefs.h>
     33 __KERNEL_RCSID(0, "$NetBSD: linux_dma_fence_array.c,v 1.4 2021/12/19 12:39:56 riastradh Exp $");
     34 
     35 #include <sys/systm.h>
     36 
     37 #include <linux/dma-fence-array.h>
     38 
     39 static const char *
     40 dma_fence_array_driver_name(struct dma_fence *fence)
     41 {
     42 	return "dma_fence_array";
     43 }
     44 
     45 static const char *
     46 dma_fence_array_timeline_name(struct dma_fence *fence)
     47 {
     48 	return "unbound";
     49 }
     50 
     51 static void
     52 dma_fence_array_done1(struct dma_fence *fence, struct dma_fence_cb *cb)
     53 {
     54 	struct dma_fence_array_cb *C =
     55 	    container_of(cb, struct dma_fence_array_cb, dfac_cb);
     56 	struct dma_fence_array *A = C->dfac_array;
     57 
     58 	KASSERT(spin_is_locked(&A->dfa_lock));
     59 
     60 	if (fence->error && A->base.error == 1) {
     61 		KASSERT(fence->error != 1);
     62 		A->base.error = fence->error;
     63 	}
     64 	if (--A->dfa_npending) {
     65 		dma_fence_put(&A->base);
     66 		return;
     67 	}
     68 
     69 	/* Last one out, hit the lights -- dma_fence_array_done.  */
     70 	irq_work_queue(&A->dfa_work);
     71 }
     72 
     73 static void
     74 dma_fence_array_done(struct irq_work *W)
     75 {
     76 	struct dma_fence_array *A = container_of(W, struct dma_fence_array,
     77 	    dfa_work);
     78 
     79 	spin_lock(&A->dfa_lock);
     80 	if (A->base.error == 1)
     81 		A->base.error = 0;
     82 	dma_fence_signal_locked(&A->base);
     83 	spin_unlock(&A->dfa_lock);
     84 
     85 	dma_fence_put(&A->base);
     86 }
     87 
     88 static bool
     89 dma_fence_array_enable_signaling(struct dma_fence *fence)
     90 {
     91 	struct dma_fence_array *A = to_dma_fence_array(fence);
     92 	struct dma_fence_array_cb *C;
     93 	unsigned i;
     94 	int error;
     95 
     96 	KASSERT(spin_is_locked(&A->dfa_lock));
     97 
     98 	for (i = 0; i < A->num_fences; i++) {
     99 		C = &A->dfa_cb[i];
    100 		C->dfac_array = A;
    101 		dma_fence_get(&A->base);
    102 		if (dma_fence_add_callback(A->fences[i], &C->dfac_cb,
    103 			dma_fence_array_done1)) {
    104 			error = A->fences[i]->error;
    105 			if (error) {
    106 				KASSERT(error != 1);
    107 				if (A->base.error == 1)
    108 					A->base.error = error;
    109 			}
    110 			dma_fence_put(&A->base);
    111 			if (--A->dfa_npending == 0) {
    112 				if (A->base.error == 1)
    113 					A->base.error = 0;
    114 				return false;
    115 			}
    116 		}
    117 	}
    118 
    119 	return true;
    120 }
    121 
    122 static bool
    123 dma_fence_array_signaled(struct dma_fence *fence)
    124 {
    125 	struct dma_fence_array *A = to_dma_fence_array(fence);
    126 
    127 	KASSERT(spin_is_locked(&A->dfa_lock));
    128 
    129 	return A->dfa_npending == 0;
    130 }
    131 
    132 static void
    133 dma_fence_array_release(struct dma_fence *fence)
    134 {
    135 	struct dma_fence_array *A = to_dma_fence_array(fence);
    136 	unsigned i;
    137 
    138 	for (i = 0; i < A->num_fences; i++)
    139 		dma_fence_put(A->fences[i]);
    140 
    141 	kfree(A->fences);
    142 	spin_lock_destroy(&A->dfa_lock);
    143 	dma_fence_free(fence);
    144 }
    145 
    146 static const struct dma_fence_ops dma_fence_array_ops = {
    147 	.get_driver_name = dma_fence_array_driver_name,
    148 	.get_timeline_name = dma_fence_array_timeline_name,
    149 	.enable_signaling = dma_fence_array_enable_signaling,
    150 	.signaled = dma_fence_array_signaled,
    151 	.release = dma_fence_array_release,
    152 };
    153 
    154 struct dma_fence_array *
    155 dma_fence_array_create(int num_fences, struct dma_fence **fences,
    156     unsigned context, unsigned seqno, bool signal_on_any)
    157 {
    158 	struct dma_fence_array *A;
    159 
    160 	/*
    161 	 * Must be allocated with kmalloc or equivalent because
    162 	 * dma-fence will free it with kfree.
    163 	 */
    164 	A = kzalloc(struct_size(A, dfa_cb, num_fences), GFP_KERNEL);
    165 	if (A == NULL)
    166 		return NULL;
    167 
    168 	A->fences = fences;
    169 	A->num_fences = num_fences;
    170 	A->dfa_npending = signal_on_any ? 1 : num_fences;
    171 
    172 	spin_lock_init(&A->dfa_lock);
    173 	dma_fence_init(&A->base, &dma_fence_array_ops, &A->dfa_lock,
    174 	    context, seqno);
    175 	init_irq_work(&A->dfa_work, dma_fence_array_done);
    176 
    177 	return A;
    178 }
    179 
    180 bool
    181 dma_fence_is_array(struct dma_fence *fence)
    182 {
    183 
    184 	return fence->ops == &dma_fence_array_ops;
    185 }
    186 
    187 struct dma_fence_array *
    188 to_dma_fence_array(struct dma_fence *fence)
    189 {
    190 
    191 	KASSERT(dma_fence_is_array(fence));
    192 	return container_of(fence, struct dma_fence_array, base);
    193 }
    194