Source-Changes-HG archive

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index][Old Index]

[src/trunk]: src/sys/external/bsd/drm2/linux drm: Implement dma fence chains.



details:   https://anonhg.NetBSD.org/src/rev/ca70978b2cae
branches:  trunk
changeset: 1028990:ca70978b2cae
user:      riastradh <riastradh%NetBSD.org@localhost>
date:      Sun Dec 19 12:39:32 2021 +0000

description:
drm: Implement dma fence chains.

diffstat:

 sys/external/bsd/drm2/include/linux/dma-fence-chain.h |    8 +-
 sys/external/bsd/drm2/linux/linux_dma_fence_chain.c   |  199 ++++++++++++++++-
 2 files changed, 190 insertions(+), 17 deletions(-)

diffs (297 lines):

diff -r 9af1346b68ed -r ca70978b2cae sys/external/bsd/drm2/include/linux/dma-fence-chain.h
--- a/sys/external/bsd/drm2/include/linux/dma-fence-chain.h     Sun Dec 19 12:39:24 2021 +0000
+++ b/sys/external/bsd/drm2/include/linux/dma-fence-chain.h     Sun Dec 19 12:39:32 2021 +0000
@@ -1,4 +1,4 @@
-/*     $NetBSD: dma-fence-chain.h,v 1.3 2021/12/19 10:47:06 riastradh Exp $    */
+/*     $NetBSD: dma-fence-chain.h,v 1.4 2021/12/19 12:39:32 riastradh Exp $    */
 
 /*-
  * Copyright (c) 2020 The NetBSD Foundation, Inc.
@@ -32,12 +32,18 @@
 #include <sys/types.h>
 
 #include <linux/dma-fence.h>
+#include <linux/irq_work.h>
 
 struct dma_fence_chain {
+       /* Linux API */
        struct dma_fence        base;
        uint64_t                prev_seqno;
 
        spinlock_t              dfc_lock;
+       struct dma_fence        *volatile dfc_prev;
+       struct dma_fence        *dfc_fence;
+       struct dma_fence_cb     dfc_callback;
+       struct irq_work         dfc_irq_work;
 };
 
 #define        dma_fence_chain_find_seqno      linux_dma_fence_chain_find_seqno
diff -r 9af1346b68ed -r ca70978b2cae sys/external/bsd/drm2/linux/linux_dma_fence_chain.c
--- a/sys/external/bsd/drm2/linux/linux_dma_fence_chain.c       Sun Dec 19 12:39:24 2021 +0000
+++ b/sys/external/bsd/drm2/linux/linux_dma_fence_chain.c       Sun Dec 19 12:39:32 2021 +0000
@@ -1,12 +1,9 @@
-/*     $NetBSD: linux_dma_fence_chain.c,v 1.2 2021/12/19 12:39:16 riastradh Exp $      */
+/*     $NetBSD: linux_dma_fence_chain.c,v 1.3 2021/12/19 12:39:32 riastradh Exp $      */
 
 /*-
- * Copyright (c) 2020 The NetBSD Foundation, Inc.
+ * 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:
@@ -30,7 +27,7 @@
  */
 
 #include <sys/cdefs.h>
-__KERNEL_RCSID(0, "$NetBSD: linux_dma_fence_chain.c,v 1.2 2021/12/19 12:39:16 riastradh Exp $");
+__KERNEL_RCSID(0, "$NetBSD: linux_dma_fence_chain.c,v 1.3 2021/12/19 12:39:32 riastradh Exp $");
 
 #include <sys/types.h>
 
@@ -38,42 +35,169 @@
 #include <linux/dma-fence-chain.h>
 #include <linux/spinlock.h>
 
+static void dma_fence_chain_irq_work(struct irq_work *);
+static bool dma_fence_chain_enable_signaling(struct dma_fence *);
+
 static const struct dma_fence_ops dma_fence_chain_ops;
 
 /*
  * dma_fence_chain_init(chain, prev, fence, seqno)
  *
- *     Initialize a new fence chain, and either start 
+ *     Initialize a fence chain node.  If prev was already a chain,
+ *     extend it; otherwise; create a new chain context.
  */
 void
 dma_fence_chain_init(struct dma_fence_chain *chain, struct dma_fence *prev,
     struct dma_fence *fence, uint64_t seqno)
 {
-       uint64_t context = -1;  /* XXX */
+       struct dma_fence_chain *prev_chain = to_dma_fence_chain(prev);
+       uint64_t context;
 
-       chain->prev_seqno = 0;
        spin_lock_init(&chain->dfc_lock);
+       chain->dfc_prev = prev;   /* consume caller's reference */
+       chain->dfc_fence = fence; /* consume caller's reference */
+       init_irq_work(&chain->dfc_irq_work, &dma_fence_chain_irq_work);
 
-       /* XXX we don't use these yet */
-       dma_fence_put(prev);
-       dma_fence_put(fence);
+       if (prev_chain == NULL ||
+           !__dma_fence_is_later(seqno, prev->seqno, prev->ops)) {
+               context = dma_fence_context_alloc(1);
+               if (prev_chain)
+                       seqno = MAX(prev->seqno, seqno);
+               chain->prev_seqno = 0;
+       } else {
+               context = prev->context;
+               chain->prev_seqno = prev->seqno;
+       }
 
        dma_fence_init(&chain->base, &dma_fence_chain_ops, &chain->dfc_lock,
            context, seqno);
 }
 
+static const char *
+dma_fence_chain_driver_name(struct dma_fence *fence)
+{
+
+       return "dma_fence_chain";
+}
+
+static const char *
+dma_fence_chain_timeline_name(struct dma_fence *fence)
+{
+
+       return "unbound";
+}
+
+static void
+dma_fence_chain_irq_work(struct irq_work *work)
+{
+       struct dma_fence_chain *chain = container_of(work,
+           struct dma_fence_chain, dfc_irq_work);
+
+       if (!dma_fence_chain_enable_signaling(&chain->base))
+               dma_fence_signal(&chain->base);
+       dma_fence_put(&chain->base);
+}
+
+static void
+dma_fence_chain_callback(struct dma_fence *fence, struct dma_fence_cb *cb)
+{
+       struct dma_fence_chain *chain = container_of(cb,
+           struct dma_fence_chain, dfc_callback);
+
+       irq_work_queue(&chain->dfc_irq_work);
+       dma_fence_put(fence);
+}
+
+static bool
+dma_fence_chain_enable_signaling(struct dma_fence *fence)
+{
+       struct dma_fence_chain *chain = to_dma_fence_chain(fence);
+       struct dma_fence_chain *chain1;
+       struct dma_fence *f, *f1;
+
+       KASSERT(chain);
+
+       dma_fence_get(&chain->base);
+       dma_fence_chain_for_each(f, &chain->base) {
+               f1 = (chain1 = to_dma_fence_chain(f)) ? chain1->dfc_fence : f;
+
+               dma_fence_get(f1);
+               if (dma_fence_add_callback(f, &chain->dfc_callback,
+                       dma_fence_chain_callback) != 0) {
+                       dma_fence_put(f);
+                       return true;
+               }
+               dma_fence_put(f1);
+       }
+       dma_fence_put(&chain->base);
+
+       return false;
+}
+
+static bool
+dma_fence_chain_signaled(struct dma_fence *fence)
+{
+       struct dma_fence_chain *chain1;
+       struct dma_fence *f, *f1;
+
+       dma_fence_chain_for_each(f, fence) {
+               f1 = (chain1 = to_dma_fence_chain(f)) ? chain1->dfc_fence : f;
+
+               if (!dma_fence_is_signaled(f1)) {
+                       dma_fence_put(f);
+                       return false;
+               }
+       }
+
+       return true;
+}
+
 static void
 dma_fence_chain_release(struct dma_fence *fence)
 {
        struct dma_fence_chain *chain = to_dma_fence_chain(fence);
+       struct dma_fence_chain *prev_chain;
+       struct dma_fence *prev;
 
        KASSERT(chain);
 
+       /*
+        * Release the previous pointer, carefully.  Caller has
+        * exclusive access to chain, so no need for atomics here.
+        */
+       while ((prev = chain->dfc_prev) != NULL) {
+               /*
+                * If anyone else still holds a reference to the
+                * previous fence, or if it's not a chain, stop here.
+                */
+               if (kref_read(&prev->refcount) > 1)
+                       break;
+               if ((prev_chain = to_dma_fence_chain(prev)) == NULL)
+                       break;
+
+               /*
+                * Cut it out and free it.  We have exclusive access to
+                * prev so this is safe.  This dma_fence_put triggers
+                * recursion into dma_fence_chain_release, but the
+                * recursion is bounded to one level.
+                */
+               chain->dfc_prev = prev_chain->dfc_prev;
+               prev_chain->dfc_prev = NULL;
+               dma_fence_put(prev);
+       }
+       dma_fence_put(prev);
+
+       dma_fence_put(chain->dfc_fence);
        spin_lock_destroy(&chain->dfc_lock);
        dma_fence_free(&chain->base);
 }
 
 static const struct dma_fence_ops dma_fence_chain_ops = {
+       .use_64bit_seqno = true,
+       .get_driver_name = dma_fence_chain_driver_name,
+       .get_timeline_name = dma_fence_chain_timeline_name,
+       .enable_signaling = dma_fence_chain_enable_signaling,
+       .signaled = dma_fence_chain_signaled,
        .release = dma_fence_chain_release,
 };
 
@@ -93,18 +217,61 @@
 }
 
 /*
+ * get_prev(chain)
+ *
+ *     Get the previous fence of the chain and add a reference, if
+ *     possible; return NULL otherwise.
+ */
+static struct dma_fence *
+get_prev(struct dma_fence_chain *chain)
+{
+       struct dma_fence *prev;
+
+       rcu_read_lock();
+       prev = dma_fence_get_rcu_safe(&chain->dfc_prev);
+       rcu_read_unlock();
+
+       return prev;
+}
+
+/*
  * dma_fence_chain_walk(fence)
  *
- *     Return the next fence in the chain, or NULL if end of chain,
- *     after releasing any fences that have already been signalled.
+ *     Find the first unsignalled fence in the chain, or NULL if fence
+ *     is not a chain node or the chain's fences are all signalled.
+ *     While searching, cull signalled fences.
  */
 struct dma_fence *
 dma_fence_chain_walk(struct dma_fence *fence)
 {
+       struct dma_fence_chain *chain, *prev_chain;
+       struct dma_fence *prev, *splice;
 
-       /* XXX */
+       if ((chain = to_dma_fence_chain(fence)) == NULL) {
+               dma_fence_put(fence);
+               return NULL;
+       }
+
+       while ((prev = get_prev(chain)) != NULL) {
+               if ((prev_chain = to_dma_fence_chain(prev)) != NULL) {
+                       if (!dma_fence_is_signaled(prev_chain->dfc_fence))
+                               break;
+                       splice = get_prev(prev_chain);
+               } else {
+                       if (!dma_fence_is_signaled(prev))
+                               break;
+                       splice = NULL;
+               }
+               membar_exit();  /* pairs with dma_fence_get_rcu_safe */
+               if (atomic_cas_ptr(&chain->dfc_prev, prev, splice) == prev)
+                       dma_fence_put(prev); /* transferred to splice */
+               else
+                       dma_fence_put(splice);
+               dma_fence_put(prev);
+       }
+
        dma_fence_put(fence);
-       return NULL;
+       return prev;
 }
 
 /*



Home | Main Index | Thread Index | Old Index