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 Rewrite nouveau_fence...



details:   https://anonhg.NetBSD.org/src/rev/01c367ed13e6
branches:  trunk
changeset: 365588:01c367ed13e6
user:      riastradh <riastradh%NetBSD.org@localhost>
date:      Thu Aug 23 01:06:50 2018 +0000

description:
Rewrite nouveau_fence in an attempt to make it make sense.

PR kern/53441

XXX pullup-7
XXX pullup-8

diffstat:

 sys/external/bsd/drm2/dist/drm/nouveau/nouveau_fence.c      |  275 +++++++----
 sys/external/bsd/drm2/dist/drm/nouveau/nouveau_fence.h      |   18 +-
 sys/external/bsd/drm2/dist/drm/nouveau/nouveau_nv84_fence.c |   15 +-
 3 files changed, 194 insertions(+), 114 deletions(-)

diffs (truncated from 587 to 300 lines):

diff -r e4f6673cc25f -r 01c367ed13e6 sys/external/bsd/drm2/dist/drm/nouveau/nouveau_fence.c
--- a/sys/external/bsd/drm2/dist/drm/nouveau/nouveau_fence.c    Wed Aug 22 20:08:54 2018 +0000
+++ b/sys/external/bsd/drm2/dist/drm/nouveau/nouveau_fence.c    Thu Aug 23 01:06:50 2018 +0000
@@ -1,4 +1,4 @@
-/*     $NetBSD: nouveau_fence.c,v 1.4 2016/04/13 07:57:15 riastradh Exp $      */
+/*     $NetBSD: nouveau_fence.c,v 1.5 2018/08/23 01:06:50 riastradh Exp $      */
 
 /*
  * Copyright (C) 2007 Ben Skeggs.
@@ -27,7 +27,7 @@
  */
 
 #include <sys/cdefs.h>
-__KERNEL_RCSID(0, "$NetBSD: nouveau_fence.c,v 1.4 2016/04/13 07:57:15 riastradh Exp $");
+__KERNEL_RCSID(0, "$NetBSD: nouveau_fence.c,v 1.5 2018/08/23 01:06:50 riastradh Exp $");
 
 #include <drm/drmP.h>
 
@@ -41,6 +41,12 @@
 
 #include <engine/fifo.h>
 
+/*
+ * struct fence_work
+ *
+ *     State for a work action scheduled when a fence is completed.
+ *     Will call func(data) at some point after that happens.
+ */
 struct fence_work {
        struct work_struct base;
        struct list_head head;
@@ -48,77 +54,119 @@
        void *data;
 };
 
+/*
+ * nouveau_fence_signal(fence)
+ *
+ *     Schedule all the work for fence's completion, and mark it done.
+ *
+ *     Caller must hold fence's channel's fence lock.
+ */
 static void
 nouveau_fence_signal(struct nouveau_fence *fence)
 {
+       struct nouveau_channel *chan __diagused = fence->channel;
+       struct nouveau_fence_chan *fctx __diagused = chan->fence;
        struct fence_work *work, *temp;
 
+       BUG_ON(!spin_is_locked(&fctx->lock));
+       BUG_ON(fence->done);
+
+       /* Schedule all the work for this fence.  */
        list_for_each_entry_safe(work, temp, &fence->work, head) {
                schedule_work(&work->base);
                list_del(&work->head);
        }
 
-       fence->channel = NULL;
+       /* Note that the fence is done.  */
+       fence->done = true;
        list_del(&fence->head);
 }
 
+/*
+ * nouveau_fence_context_del(fctx)
+ *
+ *     Artificially complete all fences in fctx, wait for their work
+ *     to drain, and destroy the memory associated with fctx.
+ */
 void
 nouveau_fence_context_del(struct nouveau_fence_chan *fctx)
 {
        struct nouveau_fence *fence, *fnext;
+
+       /* Signal all the fences in fctx.  */
        spin_lock(&fctx->lock);
        list_for_each_entry_safe(fence, fnext, &fctx->pending, head) {
                nouveau_fence_signal(fence);
+               /* XXX Doesn't this leak fence?  */
        }
        spin_unlock(&fctx->lock);
+
+       /* Wait for the workqueue to drain.  */
+       flush_scheduled_work();
+
+       /* Destroy the fence context.  */
+       DRM_DESTROY_WAITQUEUE(&fctx->waitqueue);
        spin_lock_destroy(&fctx->lock);
 }
 
+/*
+ * nouveau_fence_context_new(fctx)
+ *
+ *     Initialize the state fctx for all fences on a channel.
+ */
 void
 nouveau_fence_context_new(struct nouveau_fence_chan *fctx)
 {
+
        INIT_LIST_HEAD(&fctx->flip);
        INIT_LIST_HEAD(&fctx->pending);
        spin_lock_init(&fctx->lock);
+       DRM_INIT_WAITQUEUE(&fctx->waitqueue, "nvfnchan");
 }
 
+/*
+ * nouveau_fence_work_handler(kwork)
+ *
+ *     Work handler for nouveau_fence_work.
+ */
 static void
 nouveau_fence_work_handler(struct work_struct *kwork)
 {
        struct fence_work *work = container_of(kwork, typeof(*work), base);
+
        work->func(work->data);
        kfree(work);
 }
 
+/*
+ * nouveau_fence_work(fence, func, data)
+ *
+ *     Arrange to call func(data) after fence is completed.  If fence
+ *     is already completed, call it immediately.  If memory is
+ *     scarce, synchronously wait for the fence and call it.
+ */
 void
 nouveau_fence_work(struct nouveau_fence *fence,
                   void (*func)(void *), void *data)
 {
        struct nouveau_channel *chan = fence->channel;
-       struct nouveau_fence_chan *fctx;
+       struct nouveau_fence_chan *fctx = chan->fence;
        struct fence_work *work = NULL;
 
-       if (nouveau_fence_done(fence)) {
-               func(data);
-               return;
-       }
-
-       fctx = chan->fence;
        work = kmalloc(sizeof(*work), GFP_KERNEL);
-       if (!work) {
+       if (work == NULL) {
                WARN_ON(nouveau_fence_wait(fence, false, false));
                func(data);
                return;
        }
 
        spin_lock(&fctx->lock);
-       if (!fence->channel) {
+       if (fence->done) {
                spin_unlock(&fctx->lock);
                kfree(work);
                func(data);
                return;
        }
-
        INIT_WORK(&work->base, nouveau_fence_work_handler);
        work->func = func;
        work->data = data;
@@ -126,23 +174,39 @@
        spin_unlock(&fctx->lock);
 }
 
+/*
+ * nouveau_fence_update(chan)
+ *
+ *     Test all fences on chan for completion.  For any that are
+ *     completed, mark them as such and schedule work for them.
+ *
+ *     Caller must hold chan's fence lock.
+ */
 static void
 nouveau_fence_update(struct nouveau_channel *chan)
 {
        struct nouveau_fence_chan *fctx = chan->fence;
        struct nouveau_fence *fence, *fnext;
 
-       spin_lock(&fctx->lock);
+       BUG_ON(!spin_is_locked(&fctx->lock));
        list_for_each_entry_safe(fence, fnext, &fctx->pending, head) {
                if (fctx->read(chan) < fence->sequence)
                        break;
-
                nouveau_fence_signal(fence);
                nouveau_fence_unref(&fence);
        }
-       spin_unlock(&fctx->lock);
+       BUG_ON(!spin_is_locked(&fctx->lock));
 }
 
+/*
+ * nouveau_fence_emit(fence, chan)
+ *
+ *     - Initialize fence.
+ *     - Set its timeout to 15 sec from now.
+ *     - Assign it the next sequence number on channel.
+ *     - Submit it to the device with the device-specific emit routine.
+ *     - If that succeeds, add it to the list of pending fences on chan.
+ */
 int
 nouveau_fence_emit(struct nouveau_fence *fence, struct nouveau_channel *chan)
 {
@@ -151,7 +215,9 @@
 
        fence->channel  = chan;
        fence->timeout  = jiffies + (15 * HZ);
+       spin_lock(&fctx->lock);
        fence->sequence = ++fctx->sequence;
+       spin_unlock(&fctx->lock);
 
        ret = fctx->emit(fence);
        if (!ret) {
@@ -164,42 +230,90 @@
        return ret;
 }
 
+/*
+ * nouveau_fence_done_locked(fence, chan)
+ *
+ *     Test whether fence, which must be on chan, is done.  If it is
+ *     not marked as done, poll all fences on chan first.
+ *
+ *     Caller must hold chan's fence lock.
+ */
+static bool
+nouveau_fence_done_locked(struct nouveau_fence *fence,
+    struct nouveau_channel *chan)
+{
+       struct nouveau_fence_chan *fctx __diagused = chan->fence;
+
+       BUG_ON(!spin_is_locked(&fctx->lock));
+       BUG_ON(fence->channel != chan);
+
+       /* If it's not done, poll it for changes.  */
+       if (!fence->done)
+               nouveau_fence_update(chan);
+
+       /* Check, possibly again, whether it is done now.  */
+       return fence->done;
+}
+
+/*
+ * nouveau_fence_done(fence)
+ *
+ *     Test whether fence is done.  If it is not marked as done, poll
+ *     all fences on its channel first.  Caller MUST NOT hold the
+ *     fence lock.
+ */
 bool
 nouveau_fence_done(struct nouveau_fence *fence)
 {
-       if (fence->channel)
-               nouveau_fence_update(fence->channel);
-       return !fence->channel;
+       struct nouveau_channel *chan = fence->channel;
+       struct nouveau_fence_chan *fctx = chan->fence;
+       bool done;
+
+       spin_lock(&fctx->lock);
+       done = nouveau_fence_done_locked(fence, chan);
+       spin_unlock(&fctx->lock);
+
+       return done;
 }
 
+/*
+ * nouveau_fence_wait_uevent_handler(data, index)
+ *
+ *     Nouveau uevent handler for fence completion.  data is a
+ *     nouveau_fence_chan pointer.  Simply wake up all threads waiting
+ *     for completion of any fences on the channel.  Does not mark
+ *     fences as completed -- threads must poll fences for completion.
+ */
 static int
 nouveau_fence_wait_uevent_handler(void *data, int index)
 {
-       struct nouveau_fence_priv *priv = data;
-#ifdef __NetBSD__
-       spin_lock(&priv->waitlock);
-       /* XXX Set a flag...  */
-       DRM_SPIN_WAKEUP_ALL(&priv->waitqueue, &priv->waitlock);
-       spin_unlock(&priv->waitlock);
-#else
-       wake_up_all(&priv->waiting);
-#endif
+       struct nouveau_fence_chan *fctx = data;
+
+       spin_lock(&fctx->lock);
+       DRM_SPIN_WAKEUP_ALL(&fctx->waitqueue, &fctx->lock);
+       spin_unlock(&fctx->lock);
+
        return NVKM_EVENT_KEEP;
 }
 
+/*
+ * nouveau_fence_wait_uevent(fence, intr)
+ *



Home | Main Index | Thread Index | Old Index