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/dist/drm/nouveau Fences may last longe...



details:   https://anonhg.NetBSD.org/src/rev/b3527a88d652
branches:  trunk
changeset: 992288:b3527a88d652
user:      riastradh <riastradh%NetBSD.org@localhost>
date:      Thu Aug 23 01:10:04 2018 +0000

description:
Fences may last longer than their channels.

- Use a reference count on the nouveau_fence_chan object.
- Acquire it with kpreemption disabled.
- Use xcall to wait for kpreempt-disabled sections to complete.

PR kern/53441

XXX pullup-7
XXX pullup-8

diffstat:

 sys/external/bsd/drm2/dist/drm/nouveau/nouveau_fence.c |  142 ++++++++++++++--
 sys/external/bsd/drm2/dist/drm/nouveau/nouveau_fence.h |    1 +
 2 files changed, 120 insertions(+), 23 deletions(-)

diffs (285 lines):

diff -r b0c1a972355b -r b3527a88d652 sys/external/bsd/drm2/dist/drm/nouveau/nouveau_fence.c
--- a/sys/external/bsd/drm2/dist/drm/nouveau/nouveau_fence.c    Thu Aug 23 01:06:50 2018 +0000
+++ b/sys/external/bsd/drm2/dist/drm/nouveau/nouveau_fence.c    Thu Aug 23 01:10:04 2018 +0000
@@ -1,4 +1,4 @@
-/*     $NetBSD: nouveau_fence.c,v 1.5 2018/08/23 01:06:50 riastradh Exp $      */
+/*     $NetBSD: nouveau_fence.c,v 1.6 2018/08/23 01:10:04 riastradh Exp $      */
 
 /*
  * Copyright (C) 2007 Ben Skeggs.
@@ -27,7 +27,10 @@
  */
 
 #include <sys/cdefs.h>
-__KERNEL_RCSID(0, "$NetBSD: nouveau_fence.c,v 1.5 2018/08/23 01:06:50 riastradh Exp $");
+__KERNEL_RCSID(0, "$NetBSD: nouveau_fence.c,v 1.6 2018/08/23 01:10:04 riastradh Exp $");
+
+#include <sys/types.h>
+#include <sys/xcall.h>
 
 #include <drm/drmP.h>
 
@@ -55,6 +58,60 @@
 };
 
 /*
+ * nouveau_fence_channel_acquire(fence)
+ *
+ *     Try to return the channel associated with fence.
+ */
+static struct nouveau_channel *
+nouveau_fence_channel_acquire(struct nouveau_fence *fence)
+{
+       struct nouveau_channel *chan;
+       struct nouveau_fence_chan *fctx;
+
+       /*
+        * Block cross-calls while we examine fence.  If we observe
+        * that fence->done is false, then the channel cannot be
+        * destroyed even by another CPU until after kpreempt_enable.
+        */
+       kpreempt_disable();
+       if (fence->done) {
+               chan = NULL;
+       } else {
+               chan = fence->channel;
+               fctx = chan->fence;
+               atomic_inc_uint(&fctx->refcnt);
+       }
+       kpreempt_enable();
+
+       return chan;
+}
+
+/*
+ * nouveau_fence_channel_release(channel)
+ *
+ *     Release the channel acquired with nouveau_fence_channel_acquire.
+ */
+static void
+nouveau_fence_channel_release(struct nouveau_channel *chan)
+{
+       struct nouveau_fence_chan *fctx = chan->fence;
+       unsigned old, new;
+
+       do {
+               old = fctx->refcnt;
+               if (old == 0) {
+                       spin_lock(&fctx->lock);
+                       if (atomic_dec_uint_nv(&fctx->refcnt) == 0)
+                               DRM_SPIN_WAKEUP_ALL(&fctx->waitqueue,
+                                   &fctx->lock);
+                       spin_unlock(&fctx->lock);
+                       return;
+               }
+               new = old - 1;
+       } while (atomic_cas_uint(&fctx->refcnt, old, new) != old);
+}
+
+/*
  * nouveau_fence_signal(fence)
  *
  *     Schedule all the work for fence's completion, and mark it done.
@@ -82,6 +139,11 @@
        list_del(&fence->head);
 }
 
+static void
+nouveau_fence_context_del_xc(void *a, void *b)
+{
+}
+
 /*
  * nouveau_fence_context_del(fctx)
  *
@@ -92,6 +154,7 @@
 nouveau_fence_context_del(struct nouveau_fence_chan *fctx)
 {
        struct nouveau_fence *fence, *fnext;
+       int ret __diagused;
 
        /* Signal all the fences in fctx.  */
        spin_lock(&fctx->lock);
@@ -104,6 +167,16 @@
        /* Wait for the workqueue to drain.  */
        flush_scheduled_work();
 
+       /* Wait for nouveau_fence_channel_acquire to complete on all CPUs.  */
+       xc_wait(xc_broadcast(0, nouveau_fence_context_del_xc, NULL, NULL));
+
+       /* Wait for any references to drain.  */
+       spin_lock(&fctx->lock);
+       DRM_SPIN_WAIT_NOINTR_UNTIL(ret, &fctx->waitqueue, &fctx->lock,
+           fctx->refcnt == 0);
+       BUG_ON(ret);
+       spin_unlock(&fctx->lock);
+
        /* Destroy the fence context.  */
        DRM_DESTROY_WAITQUEUE(&fctx->waitqueue);
        spin_lock_destroy(&fctx->lock);
@@ -122,6 +195,7 @@
        INIT_LIST_HEAD(&fctx->pending);
        spin_lock_init(&fctx->lock);
        DRM_INIT_WAITQUEUE(&fctx->waitqueue, "nvfnchan");
+       fctx->refcnt = 0;
 }
 
 /*
@@ -149,29 +223,37 @@
 nouveau_fence_work(struct nouveau_fence *fence,
                   void (*func)(void *), void *data)
 {
-       struct nouveau_channel *chan = fence->channel;
-       struct nouveau_fence_chan *fctx = chan->fence;
+       struct nouveau_channel *chan;
+       struct nouveau_fence_chan *fctx;
        struct fence_work *work = NULL;
 
+       if ((chan = nouveau_fence_channel_acquire(fence)) == NULL)
+               goto now0;
+       fctx = chan->fence;
+
        work = kmalloc(sizeof(*work), GFP_KERNEL);
        if (work == NULL) {
                WARN_ON(nouveau_fence_wait(fence, false, false));
-               func(data);
-               return;
+               goto now1;
        }
 
        spin_lock(&fctx->lock);
        if (fence->done) {
                spin_unlock(&fctx->lock);
-               kfree(work);
-               func(data);
-               return;
+               goto now2;
        }
        INIT_WORK(&work->base, nouveau_fence_work_handler);
        work->func = func;
        work->data = data;
        list_add(&work->head, &fence->work);
+       if (atomic_dec_uint_nv(&fctx->refcnt) == 0)
+               DRM_SPIN_WAKEUP_ALL(&fctx->waitqueue, &fctx->lock);
        spin_unlock(&fctx->lock);
+       return;
+
+now2:  kfree(work);
+now1:  nouveau_fence_channel_release(chan);
+now0:  func(data);
 }
 
 /*
@@ -265,14 +347,20 @@
 bool
 nouveau_fence_done(struct nouveau_fence *fence)
 {
-       struct nouveau_channel *chan = fence->channel;
-       struct nouveau_fence_chan *fctx = chan->fence;
+       struct nouveau_channel *chan;
+       struct nouveau_fence_chan *fctx;
        bool done;
 
+       if ((chan = nouveau_fence_channel_acquire(fence)) == NULL)
+               return true;
+
+       fctx = chan->fence;
        spin_lock(&fctx->lock);
        done = nouveau_fence_done_locked(fence, chan);
        spin_unlock(&fctx->lock);
 
+       nouveau_fence_channel_release(chan);
+
        return done;
 }
 
@@ -297,20 +385,22 @@
 }
 
 /*
- * nouveau_fence_wait_uevent(fence, intr)
+ * nouveau_fence_wait_uevent(fence, chan, intr)
  *
- *     Wait using a nouveau event for completion of fence.  Wait
- *     interruptibly iff intr is true.
+ *     Wait using a nouveau event for completion of fence on chan.
+ *     Wait interruptibly iff intr is true.
  */
 static int
-nouveau_fence_wait_uevent(struct nouveau_fence *fence, bool intr)
+nouveau_fence_wait_uevent(struct nouveau_fence *fence,
+    struct nouveau_channel *chan, bool intr)
 {
-       struct nouveau_channel *chan = fence->channel;
        struct nouveau_fifo *pfifo = nouveau_fifo(chan->drm->device);
        struct nouveau_fence_chan *fctx = chan->fence;
        struct nouveau_eventh *handler;
        int ret = 0;
 
+       BUG_ON(fence->channel != chan);
+
        ret = nouveau_event_new(pfifo->uevent, 0,
                                nouveau_fence_wait_uevent_handler,
                                fctx, &handler);
@@ -374,15 +464,19 @@
 int
 nouveau_fence_wait(struct nouveau_fence *fence, bool lazy, bool intr)
 {
-       struct nouveau_channel *chan = fence->channel;
-       struct nouveau_fence_priv *priv = chan->drm->fence;
+       struct nouveau_channel *chan;
+       struct nouveau_fence_priv *priv;
        unsigned long delay_usec = 1;
        int ret = 0;
 
+       if ((chan = nouveau_fence_channel_acquire(fence)) == NULL)
+               goto out0;
+
+       priv = chan->drm->fence;
        while (priv && priv->uevent && lazy && !nouveau_fence_done(fence)) {
-               ret = nouveau_fence_wait_uevent(fence, intr);
+               ret = nouveau_fence_wait_uevent(fence, chan, intr);
                if (ret < 0)
-                       return ret;
+                       goto out1;
        }
 
        while (!nouveau_fence_done(fence)) {
@@ -402,7 +496,8 @@
                }
        }
 
-       return ret;
+out1:  nouveau_fence_channel_release(chan);
+out0:  return ret;
 }
 
 int
@@ -412,13 +507,14 @@
        struct nouveau_channel *prev;
        int ret = 0;
 
-       prev = fence ? fence->channel : NULL;
-       if (prev) {
+       if (fence != NULL &&
+           (prev = nouveau_fence_channel_acquire(fence)) != NULL) {
                if (unlikely(prev != chan && !nouveau_fence_done(fence))) {
                        ret = fctx->sync(fence, prev, chan);
                        if (unlikely(ret))
                                ret = nouveau_fence_wait(fence, true, false);
                }
+               nouveau_fence_channel_release(prev);
        }
 
        return ret;
diff -r b0c1a972355b -r b3527a88d652 sys/external/bsd/drm2/dist/drm/nouveau/nouveau_fence.h
--- a/sys/external/bsd/drm2/dist/drm/nouveau/nouveau_fence.h    Thu Aug 23 01:06:50 2018 +0000
+++ b/sys/external/bsd/drm2/dist/drm/nouveau/nouveau_fence.h    Thu Aug 23 01:10:04 2018 +0000
@@ -46,6 +46,7 @@
 
        spinlock_t lock;
        drm_waitqueue_t waitqueue;
+       volatile unsigned refcnt;
        u32 sequence;
 };
 



Home | Main Index | Thread Index | Old Index