LCOV - code coverage report
Current view: top level - drivers/dma-buf - dma-fence-chain.c (source / functions) Hit Total Coverage
Test: gcov_data_raspi2_qemu_modules_combined.info Lines: 0 58 0.0 %
Date: 2020-09-30 20:25:01 Functions: 0 10 0.0 %
Branches: 0 50 0.0 %

           Branch data     Line data    Source code
       1                 :            : // SPDX-License-Identifier: GPL-2.0-only
       2                 :            : /*
       3                 :            :  * fence-chain: chain fences together in a timeline
       4                 :            :  *
       5                 :            :  * Copyright (C) 2018 Advanced Micro Devices, Inc.
       6                 :            :  * Authors:
       7                 :            :  *      Christian König <christian.koenig@amd.com>
       8                 :            :  */
       9                 :            : 
      10                 :            : #include <linux/dma-fence-chain.h>
      11                 :            : 
      12                 :            : static bool dma_fence_chain_enable_signaling(struct dma_fence *fence);
      13                 :            : 
      14                 :            : /**
      15                 :            :  * dma_fence_chain_get_prev - use RCU to get a reference to the previous fence
      16                 :            :  * @chain: chain node to get the previous node from
      17                 :            :  *
      18                 :            :  * Use dma_fence_get_rcu_safe to get a reference to the previous fence of the
      19                 :            :  * chain node.
      20                 :            :  */
      21                 :            : static struct dma_fence *dma_fence_chain_get_prev(struct dma_fence_chain *chain)
      22                 :            : {
      23                 :            :         struct dma_fence *prev;
      24                 :            : 
      25                 :            :         rcu_read_lock();
      26                 :          0 :         prev = dma_fence_get_rcu_safe(&chain->prev);
      27                 :            :         rcu_read_unlock();
      28                 :            :         return prev;
      29                 :            : }
      30                 :            : 
      31                 :            : /**
      32                 :            :  * dma_fence_chain_walk - chain walking function
      33                 :            :  * @fence: current chain node
      34                 :            :  *
      35                 :            :  * Walk the chain to the next node. Returns the next fence or NULL if we are at
      36                 :            :  * the end of the chain. Garbage collects chain nodes which are already
      37                 :            :  * signaled.
      38                 :            :  */
      39                 :          0 : struct dma_fence *dma_fence_chain_walk(struct dma_fence *fence)
      40                 :            : {
      41                 :            :         struct dma_fence_chain *chain, *prev_chain;
      42                 :            :         struct dma_fence *prev, *replacement, *tmp;
      43                 :            : 
      44                 :            :         chain = to_dma_fence_chain(fence);
      45         [ #  # ]:          0 :         if (!chain) {
      46                 :            :                 dma_fence_put(fence);
      47                 :            :                 return NULL;
      48                 :            :         }
      49                 :            : 
      50         [ #  # ]:          0 :         while ((prev = dma_fence_chain_get_prev(chain))) {
      51                 :            : 
      52                 :            :                 prev_chain = to_dma_fence_chain(prev);
      53         [ #  # ]:          0 :                 if (prev_chain) {
      54         [ #  # ]:          0 :                         if (!dma_fence_is_signaled(prev_chain->fence))
      55                 :            :                                 break;
      56                 :            : 
      57                 :            :                         replacement = dma_fence_chain_get_prev(prev_chain);
      58                 :            :                 } else {
      59         [ #  # ]:          0 :                         if (!dma_fence_is_signaled(prev))
      60                 :            :                                 break;
      61                 :            : 
      62                 :            :                         replacement = NULL;
      63                 :            :                 }
      64                 :            : 
      65                 :          0 :                 tmp = cmpxchg((void **)&chain->prev, (void *)prev, (void *)replacement);
      66         [ #  # ]:          0 :                 if (tmp == prev)
      67                 :            :                         dma_fence_put(tmp);
      68                 :            :                 else
      69                 :            :                         dma_fence_put(replacement);
      70                 :            :                 dma_fence_put(prev);
      71                 :            :         }
      72                 :            : 
      73                 :            :         dma_fence_put(fence);
      74                 :          0 :         return prev;
      75                 :            : }
      76                 :            : EXPORT_SYMBOL(dma_fence_chain_walk);
      77                 :            : 
      78                 :            : /**
      79                 :            :  * dma_fence_chain_find_seqno - find fence chain node by seqno
      80                 :            :  * @pfence: pointer to the chain node where to start
      81                 :            :  * @seqno: the sequence number to search for
      82                 :            :  *
      83                 :            :  * Advance the fence pointer to the chain node which will signal this sequence
      84                 :            :  * number. If no sequence number is provided then this is a no-op.
      85                 :            :  *
      86                 :            :  * Returns EINVAL if the fence is not a chain node or the sequence number has
      87                 :            :  * not yet advanced far enough.
      88                 :            :  */
      89                 :          0 : int dma_fence_chain_find_seqno(struct dma_fence **pfence, uint64_t seqno)
      90                 :            : {
      91                 :            :         struct dma_fence_chain *chain;
      92                 :            : 
      93         [ #  # ]:          0 :         if (!seqno)
      94                 :            :                 return 0;
      95                 :            : 
      96                 :          0 :         chain = to_dma_fence_chain(*pfence);
      97   [ #  #  #  # ]:          0 :         if (!chain || chain->base.seqno < seqno)
      98                 :            :                 return -EINVAL;
      99                 :            : 
     100         [ #  # ]:          0 :         dma_fence_chain_for_each(*pfence, &chain->base) {
     101   [ #  #  #  # ]:          0 :                 if ((*pfence)->context != chain->base.context ||
     102                 :          0 :                     to_dma_fence_chain(*pfence)->prev_seqno < seqno)
     103                 :            :                         break;
     104                 :            :         }
     105                 :            :         dma_fence_put(&chain->base);
     106                 :            : 
     107                 :            :         return 0;
     108                 :            : }
     109                 :            : EXPORT_SYMBOL(dma_fence_chain_find_seqno);
     110                 :            : 
     111                 :          0 : static const char *dma_fence_chain_get_driver_name(struct dma_fence *fence)
     112                 :            : {
     113                 :          0 :         return "dma_fence_chain";
     114                 :            : }
     115                 :            : 
     116                 :          0 : static const char *dma_fence_chain_get_timeline_name(struct dma_fence *fence)
     117                 :            : {
     118                 :          0 :         return "unbound";
     119                 :            : }
     120                 :            : 
     121                 :          0 : static void dma_fence_chain_irq_work(struct irq_work *work)
     122                 :            : {
     123                 :            :         struct dma_fence_chain *chain;
     124                 :            : 
     125                 :            :         chain = container_of(work, typeof(*chain), work);
     126                 :            : 
     127                 :            :         /* Try to rearm the callback */
     128         [ #  # ]:          0 :         if (!dma_fence_chain_enable_signaling(&chain->base))
     129                 :            :                 /* Ok, we are done. No more unsignaled fences left */
     130                 :          0 :                 dma_fence_signal(&chain->base);
     131                 :            :         dma_fence_put(&chain->base);
     132                 :          0 : }
     133                 :            : 
     134                 :          0 : static void dma_fence_chain_cb(struct dma_fence *f, struct dma_fence_cb *cb)
     135                 :            : {
     136                 :            :         struct dma_fence_chain *chain;
     137                 :            : 
     138                 :            :         chain = container_of(cb, typeof(*chain), cb);
     139                 :          0 :         irq_work_queue(&chain->work);
     140                 :            :         dma_fence_put(f);
     141                 :          0 : }
     142                 :            : 
     143                 :          0 : static bool dma_fence_chain_enable_signaling(struct dma_fence *fence)
     144                 :            : {
     145                 :            :         struct dma_fence_chain *head = to_dma_fence_chain(fence);
     146                 :            : 
     147                 :          0 :         dma_fence_get(&head->base);
     148         [ #  # ]:          0 :         dma_fence_chain_for_each(fence, &head->base) {
     149                 :            :                 struct dma_fence_chain *chain = to_dma_fence_chain(fence);
     150         [ #  # ]:          0 :                 struct dma_fence *f = chain ? chain->fence : fence;
     151                 :            : 
     152                 :            :                 dma_fence_get(f);
     153         [ #  # ]:          0 :                 if (!dma_fence_add_callback(f, &head->cb, dma_fence_chain_cb)) {
     154                 :            :                         dma_fence_put(fence);
     155                 :            :                         return true;
     156                 :            :                 }
     157                 :            :                 dma_fence_put(f);
     158                 :            :         }
     159                 :            :         dma_fence_put(&head->base);
     160                 :            :         return false;
     161                 :            : }
     162                 :            : 
     163                 :          0 : static bool dma_fence_chain_signaled(struct dma_fence *fence)
     164                 :            : {
     165         [ #  # ]:          0 :         dma_fence_chain_for_each(fence, fence) {
     166                 :            :                 struct dma_fence_chain *chain = to_dma_fence_chain(fence);
     167         [ #  # ]:          0 :                 struct dma_fence *f = chain ? chain->fence : fence;
     168                 :            : 
     169         [ #  # ]:          0 :                 if (!dma_fence_is_signaled(f)) {
     170                 :            :                         dma_fence_put(fence);
     171                 :            :                         return false;
     172                 :            :                 }
     173                 :            :         }
     174                 :            : 
     175                 :            :         return true;
     176                 :            : }
     177                 :            : 
     178                 :          0 : static void dma_fence_chain_release(struct dma_fence *fence)
     179                 :            : {
     180                 :            :         struct dma_fence_chain *chain = to_dma_fence_chain(fence);
     181                 :            :         struct dma_fence *prev;
     182                 :            : 
     183                 :            :         /* Manually unlink the chain as much as possible to avoid recursion
     184                 :            :          * and potential stack overflow.
     185                 :            :          */
     186         [ #  # ]:          0 :         while ((prev = rcu_dereference_protected(chain->prev, true))) {
     187                 :            :                 struct dma_fence_chain *prev_chain;
     188                 :            : 
     189         [ #  # ]:          0 :                 if (kref_read(&prev->refcount) > 1)
     190                 :            :                        break;
     191                 :            : 
     192                 :            :                 prev_chain = to_dma_fence_chain(prev);
     193         [ #  # ]:          0 :                 if (!prev_chain)
     194                 :            :                         break;
     195                 :            : 
     196                 :            :                 /* No need for atomic operations since we hold the last
     197                 :            :                  * reference to prev_chain.
     198                 :            :                  */
     199                 :          0 :                 chain->prev = prev_chain->prev;
     200                 :            :                 RCU_INIT_POINTER(prev_chain->prev, NULL);
     201                 :            :                 dma_fence_put(prev);
     202                 :            :         }
     203                 :            :         dma_fence_put(prev);
     204                 :            : 
     205                 :          0 :         dma_fence_put(chain->fence);
     206                 :          0 :         dma_fence_free(fence);
     207                 :          0 : }
     208                 :            : 
     209                 :            : const struct dma_fence_ops dma_fence_chain_ops = {
     210                 :            :         .use_64bit_seqno = true,
     211                 :            :         .get_driver_name = dma_fence_chain_get_driver_name,
     212                 :            :         .get_timeline_name = dma_fence_chain_get_timeline_name,
     213                 :            :         .enable_signaling = dma_fence_chain_enable_signaling,
     214                 :            :         .signaled = dma_fence_chain_signaled,
     215                 :            :         .release = dma_fence_chain_release,
     216                 :            : };
     217                 :            : EXPORT_SYMBOL(dma_fence_chain_ops);
     218                 :            : 
     219                 :            : /**
     220                 :            :  * dma_fence_chain_init - initialize a fence chain
     221                 :            :  * @chain: the chain node to initialize
     222                 :            :  * @prev: the previous fence
     223                 :            :  * @fence: the current fence
     224                 :            :  *
     225                 :            :  * Initialize a new chain node and either start a new chain or add the node to
     226                 :            :  * the existing chain of the previous fence.
     227                 :            :  */
     228                 :          0 : void dma_fence_chain_init(struct dma_fence_chain *chain,
     229                 :            :                           struct dma_fence *prev,
     230                 :            :                           struct dma_fence *fence,
     231                 :            :                           uint64_t seqno)
     232                 :            : {
     233                 :            :         struct dma_fence_chain *prev_chain = to_dma_fence_chain(prev);
     234                 :            :         uint64_t context;
     235                 :            : 
     236                 :          0 :         spin_lock_init(&chain->lock);
     237                 :          0 :         rcu_assign_pointer(chain->prev, prev);
     238                 :          0 :         chain->fence = fence;
     239                 :          0 :         chain->prev_seqno = 0;
     240                 :            :         init_irq_work(&chain->work, dma_fence_chain_irq_work);
     241                 :            : 
     242                 :            :         /* Try to reuse the context of the previous chain node. */
     243   [ #  #  #  # ]:          0 :         if (prev_chain && __dma_fence_is_later(seqno, prev->seqno, prev->ops)) {
     244                 :          0 :                 context = prev->context;
     245                 :          0 :                 chain->prev_seqno = prev->seqno;
     246                 :            :         } else {
     247                 :          0 :                 context = dma_fence_context_alloc(1);
     248                 :            :                 /* Make sure that we always have a valid sequence number. */
     249         [ #  # ]:          0 :                 if (prev_chain)
     250                 :          0 :                         seqno = max(prev->seqno, seqno);
     251                 :            :         }
     252                 :            : 
     253                 :          0 :         dma_fence_init(&chain->base, &dma_fence_chain_ops,
     254                 :            :                        &chain->lock, context, seqno);
     255                 :          0 : }
     256                 :            : EXPORT_SYMBOL(dma_fence_chain_init);

Generated by: LCOV version 1.14