Source-Changes-HG archive

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

[src/netbsd-8]: src/sys/external/bsd/drm2/dist/drm/nouveau Pull up following ...



details:   https://anonhg.NetBSD.org/src/rev/2ce1f0471320
branches:  netbsd-8
changeset: 851961:2ce1f0471320
user:      martin <martin%NetBSD.org@localhost>
date:      Fri Aug 31 17:35:51 2018 +0000

description:
Pull up following revision(s) (requested by riastradh in ticket #996):

        sys/external/bsd/drm2/dist/drm/nouveau/nouveau_nv84_fence.c: revision 1.3
        sys/external/bsd/drm2/dist/drm/nouveau/nouveau_fence.h: revision 1.3
        sys/external/bsd/drm2/dist/drm/nouveau/nouveau_fence.h: revision 1.4
        sys/external/bsd/drm2/dist/drm/nouveau/nouveau_fence.h: revision 1.5
        sys/external/bsd/drm2/dist/drm/nouveau/nouveau_fence.c: revision 1.5
        sys/external/bsd/drm2/dist/drm/nouveau/nouveau_fence.c: revision 1.6
        sys/external/bsd/drm2/dist/drm/nouveau/nouveau_fence.c: revision 1.7
        sys/external/bsd/drm2/dist/drm/nouveau/nouveau_fence.c: revision 1.8
        sys/external/bsd/drm2/dist/drm/nouveau/nouveau_fence.c: revision 1.9

Rewrite nouveau_fence in an attempt to make it make sense.
PR kern/53441
XXX pullup-7
XXX pullup-8


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


Defer nouveau_fence_unref until spin unlock.
- kfree while holding a spin lock is not a good idea.
- Make sure we GC every time we might signal fences.
PR kern/53441
XXX pullup-7
XXX pullup-8


Attempt to make sense of return values of nouveau_fence_wait.
PR kern/53441
XXX pullup-7
XXX pullup-8


Fix edge case of reference counting, oops.
PR kern/53441
XXX pullup-7
XXX pullup-8

diffstat:

 sys/external/bsd/drm2/dist/drm/nouveau/nouveau_fence.c      |  486 +++++++++--
 sys/external/bsd/drm2/dist/drm/nouveau/nouveau_fence.h      |   20 +-
 sys/external/bsd/drm2/dist/drm/nouveau/nouveau_nv84_fence.c |   15 +-
 3 files changed, 389 insertions(+), 132 deletions(-)

diffs (truncated from 811 to 300 lines):

diff -r 50953b2a73cb -r 2ce1f0471320 sys/external/bsd/drm2/dist/drm/nouveau/nouveau_fence.c
--- a/sys/external/bsd/drm2/dist/drm/nouveau/nouveau_fence.c    Tue Aug 28 16:25:19 2018 +0000
+++ b/sys/external/bsd/drm2/dist/drm/nouveau/nouveau_fence.c    Fri Aug 31 17:35:51 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.4.10.1 2018/08/31 17:35:51 martin Exp $    */
 
 /*
  * Copyright (C) 2007 Ben Skeggs.
@@ -27,7 +27,10 @@
  */
 
 #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.4.10.1 2018/08/31 17:35:51 martin Exp $");
+
+#include <sys/types.h>
+#include <sys/xcall.h>
 
 #include <drm/drmP.h>
 
@@ -41,6 +44,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,101 +57,291 @@
        void *data;
 };
 
+/*
+ * 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_gc_grab(fctx, list)
+ *
+ *     Move all of channel's done fences to list.
+ *
+ *     Caller must hold channel's fence lock.
+ */
+static void
+nouveau_fence_gc_grab(struct nouveau_fence_chan *fctx, struct list_head *list)
+{
+       struct list_head *node, *next;
+
+       BUG_ON(!spin_is_locked(&fctx->lock));
+
+       list_for_each_safe(node, next, &fctx->done) {
+               list_move_tail(node, list);
+       }
+}
+
+/*
+ * nouveau_fence_gc_free(list)
+ *
+ *     Unreference all of the fences in the list.
+ *
+ *     Caller MUST NOT hold the fences' channel's fence lock.
+ */
+static void
+nouveau_fence_gc_free(struct list_head *list)
+{
+       struct nouveau_fence *fence, *next;
+
+       list_for_each_entry_safe(fence, next, list, head) {
+               list_del(&fence->head);
+               nouveau_fence_unref(&fence);
+       }
+}
+
+/*
+ * 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 == 1) {
+                       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, mark it done, and
+ *     move it from the pending list to the done list.
+ *
+ *     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;
-       list_del(&fence->head);
+       /* Note that the fence is done.  */
+       fence->done = true;
+
+       /* Move it from the pending list to the done list.  */
+       list_move_tail(&fence->head, &fctx->done);
 }
 
+static void
+nouveau_fence_context_del_xc(void *a, void *b)
+{
+}
+
+/*
+ * 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;
+       struct list_head done_list;
+       int ret __diagused;
+
+       INIT_LIST_HEAD(&done_list);
+
+       /* Signal all the fences in fctx.  */
        spin_lock(&fctx->lock);
        list_for_each_entry_safe(fence, fnext, &fctx->pending, head) {
                nouveau_fence_signal(fence);
        }
+       nouveau_fence_gc_grab(fctx, &done_list);
        spin_unlock(&fctx->lock);
+
+       /* Release any fences that we signalled.  */
+       nouveau_fence_gc_free(&done_list);
+
+       /* 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));
+
+       /* Release our reference and wait for any others to drain.  */
+       spin_lock(&fctx->lock);
+       KASSERT(fctx->refcnt > 0);
+       atomic_dec_uint(&fctx->refcnt);
+       DRM_SPIN_WAIT_NOINTR_UNTIL(ret, &fctx->waitqueue, &fctx->lock,
+           fctx->refcnt == 0);
+       BUG_ON(ret);
+       spin_unlock(&fctx->lock);
+
+       /* Make sure there are no more fences on the list.  */
+       BUG_ON(!list_empty(&fctx->done));
+       BUG_ON(!list_empty(&fctx->flip));
+       BUG_ON(!list_empty(&fctx->pending));
+
+       /* 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);
+       INIT_LIST_HEAD(&fctx->done);
        spin_lock_init(&fctx->lock);
+       DRM_INIT_WAITQUEUE(&fctx->waitqueue, "nvfnchan");
+       fctx->refcnt = 1;
 }
 
+/*
+ * 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_channel *chan;
        struct nouveau_fence_chan *fctx;
        struct fence_work *work = NULL;
 
-       if (nouveau_fence_done(fence)) {
-               func(data);
-               return;
-       }
-
+       if ((chan = nouveau_fence_channel_acquire(fence)) == NULL)
+               goto now0;
        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;
+               goto now1;
        }
 
        spin_lock(&fctx->lock);
-       if (!fence->channel) {
+       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);



Home | Main Index | Thread Index | Old Index