drm/nouveau: rework to new fence interface
authorMaarten Lankhorst <maarten.lankhorst@ubuntu.com>
Thu, 9 Jan 2014 10:03:11 +0000 (11:03 +0100)
committerMaarten Lankhorst <maarten.lankhorst@canonical.com>
Tue, 2 Sep 2014 14:41:50 +0000 (16:41 +0200)
Signed-off-by: Maarten Lankhorst <maarten.lankhorst@canonical.com>
Acked-by: Ben Skeggs <bskeggs@redhat.com>
drivers/gpu/drm/nouveau/nouveau_bo.c
drivers/gpu/drm/nouveau/nouveau_display.c
drivers/gpu/drm/nouveau/nouveau_fence.c
drivers/gpu/drm/nouveau/nouveau_fence.h
drivers/gpu/drm/nouveau/nouveau_gem.c
drivers/gpu/drm/nouveau/nv04_fence.c
drivers/gpu/drm/nouveau/nv10_fence.c
drivers/gpu/drm/nouveau/nv17_fence.c
drivers/gpu/drm/nouveau/nv50_fence.c
drivers/gpu/drm/nouveau/nv84_fence.c

index 8d8e5f6340d06a8baf1f28feb04f3ef47c8351bd..2d026c81ca1bafd789a2106caa23d0ba84368668 100644 (file)
@@ -970,7 +970,7 @@ nouveau_bo_move_m2mf(struct ttm_buffer_object *bo, int evict, bool intr,
        }
 
        mutex_lock_nested(&cli->mutex, SINGLE_DEPTH_NESTING);
-       ret = nouveau_fence_sync(bo->sync_obj, chan);
+       ret = nouveau_fence_sync(nouveau_bo(bo), chan);
        if (ret == 0) {
                ret = drm->ttm.move(chan, bo, &bo->mem, new_mem);
                if (ret == 0) {
@@ -1464,10 +1464,12 @@ nouveau_bo_fence_unref(void **sync_obj)
 void
 nouveau_bo_fence(struct nouveau_bo *nvbo, struct nouveau_fence *fence)
 {
-       lockdep_assert_held(&nvbo->bo.resv->lock.base);
+       struct reservation_object *resv = nvbo->bo.resv;
 
        nouveau_bo_fence_unref(&nvbo->bo.sync_obj);
        nvbo->bo.sync_obj = nouveau_fence_ref(fence);
+
+       reservation_object_add_excl_fence(resv, &fence->base);
 }
 
 static void *
index e6867b9ebb469273e5727ab9694cc3ccad50284d..ec1960a9412cac154074b601e0f41058c9ed1373 100644 (file)
@@ -658,7 +658,7 @@ nouveau_page_flip_emit(struct nouveau_channel *chan,
        spin_unlock_irqrestore(&dev->event_lock, flags);
 
        /* Synchronize with the old framebuffer */
-       ret = nouveau_fence_sync(old_bo->bo.sync_obj, chan);
+       ret = nouveau_fence_sync(old_bo, chan);
        if (ret)
                goto fail;
 
@@ -722,7 +722,7 @@ nouveau_crtc_page_flip(struct drm_crtc *crtc, struct drm_framebuffer *fb,
                goto fail_unpin;
 
        /* synchronise rendering channel with the kernel's channel */
-       ret = nouveau_fence_sync(new_bo->bo.sync_obj, chan);
+       ret = nouveau_fence_sync(new_bo, chan);
        if (ret) {
                ttm_bo_unreserve(&new_bo->bo);
                goto fail_unpin;
index 0a93114158cde37b41a311d612e6205eb7a98ccb..3beb3bf130e2d42004cfff3460f281add65e026b 100644 (file)
@@ -28,6 +28,7 @@
 
 #include <linux/ktime.h>
 #include <linux/hrtimer.h>
+#include <trace/events/fence.h>
 
 #include <nvif/notify.h>
 #include <nvif/event.h>
 #include "nouveau_dma.h"
 #include "nouveau_fence.h"
 
-struct fence_work {
-       struct work_struct base;
-       struct list_head head;
-       void (*func)(void *);
-       void *data;
-};
+static const struct fence_ops nouveau_fence_ops_uevent;
+static const struct fence_ops nouveau_fence_ops_legacy;
+
+static inline struct nouveau_fence *
+from_fence(struct fence *fence)
+{
+       return container_of(fence, struct nouveau_fence, base);
+}
+
+static inline struct nouveau_fence_chan *
+nouveau_fctx(struct nouveau_fence *fence)
+{
+       return container_of(fence->base.lock, struct nouveau_fence_chan, lock);
+}
 
 static void
 nouveau_fence_signal(struct nouveau_fence *fence)
 {
-       struct fence_work *work, *temp;
+       fence_signal_locked(&fence->base);
+       list_del(&fence->head);
+
+       if (test_bit(FENCE_FLAG_USER_BITS, &fence->base.flags)) {
+               struct nouveau_fence_chan *fctx = nouveau_fctx(fence);
 
-       list_for_each_entry_safe(work, temp, &fence->work, head) {
-               schedule_work(&work->base);
-               list_del(&work->head);
+               if (!--fctx->notify_ref)
+                       nvif_notify_put(&fctx->notify);
        }
 
-       fence->channel = NULL;
-       list_del(&fence->head);
+       fence_put(&fence->base);
+}
+
+static struct nouveau_fence *
+nouveau_local_fence(struct fence *fence, struct nouveau_drm *drm) {
+       struct nouveau_fence_priv *priv = (void*)drm->fence;
+
+       if (fence->ops != &nouveau_fence_ops_legacy &&
+           fence->ops != &nouveau_fence_ops_uevent)
+               return NULL;
+
+       if (fence->context < priv->context_base ||
+           fence->context >= priv->context_base + priv->contexts)
+               return NULL;
+
+       return from_fence(fence);
 }
 
 void
 nouveau_fence_context_del(struct nouveau_fence_chan *fctx)
 {
-       struct nouveau_fence *fence, *fnext;
-       spin_lock(&fctx->lock);
-       list_for_each_entry_safe(fence, fnext, &fctx->pending, head) {
+       struct nouveau_fence *fence;
+
+       nvif_notify_fini(&fctx->notify);
+
+       spin_lock_irq(&fctx->lock);
+       while (!list_empty(&fctx->pending)) {
+               fence = list_entry(fctx->pending.next, typeof(*fence), head);
+
                nouveau_fence_signal(fence);
+               fence->channel = NULL;
        }
-       spin_unlock(&fctx->lock);
+       spin_unlock_irq(&fctx->lock);
+}
+
+static void
+nouveau_fence_update(struct nouveau_channel *chan, struct nouveau_fence_chan *fctx)
+{
+       struct nouveau_fence *fence;
+
+       u32 seq = fctx->read(chan);
+
+       while (!list_empty(&fctx->pending)) {
+               fence = list_entry(fctx->pending.next, typeof(*fence), head);
+
+               if ((int)(seq - fence->base.seqno) < 0)
+                       return;
+
+               nouveau_fence_signal(fence);
+       }
+}
+
+static int
+nouveau_fence_wait_uevent_handler(struct nvif_notify *notify)
+{
+       struct nouveau_fence_chan *fctx =
+               container_of(notify, typeof(*fctx), notify);
+       unsigned long flags;
+
+       spin_lock_irqsave(&fctx->lock, flags);
+       if (!list_empty(&fctx->pending)) {
+               struct nouveau_fence *fence;
+
+               fence = list_entry(fctx->pending.next, typeof(*fence), head);
+               nouveau_fence_update(fence->channel, fctx);
+       }
+       spin_unlock_irqrestore(&fctx->lock, flags);
+
+       /* Always return keep here. NVIF refcount is handled with nouveau_fence_update */
+       return NVIF_NOTIFY_KEEP;
 }
 
 void
-nouveau_fence_context_new(struct nouveau_fence_chan *fctx)
+nouveau_fence_context_new(struct nouveau_channel *chan, struct nouveau_fence_chan *fctx)
 {
+       struct nouveau_fence_priv *priv = (void*)chan->drm->fence;
+       int ret;
+
        INIT_LIST_HEAD(&fctx->flip);
        INIT_LIST_HEAD(&fctx->pending);
        spin_lock_init(&fctx->lock);
+       fctx->context = priv->context_base + chan->chid;
+
+       if (!priv->uevent)
+               return;
+
+       ret = nvif_notify_init(chan->object, NULL,
+                        nouveau_fence_wait_uevent_handler, false,
+                        G82_CHANNEL_DMA_V0_NTFY_UEVENT,
+                        &(struct nvif_notify_uevent_req) { },
+                        sizeof(struct nvif_notify_uevent_req),
+                        sizeof(struct nvif_notify_uevent_rep),
+                        &fctx->notify);
+
+       WARN_ON(ret);
 }
 
+struct nouveau_fence_work {
+       struct work_struct work;
+       struct fence_cb cb;
+       void (*func)(void *);
+       void *data;
+};
+
 static void
 nouveau_fence_work_handler(struct work_struct *kwork)
 {
-       struct fence_work *work = container_of(kwork, typeof(*work), base);
+       struct nouveau_fence_work *work = container_of(kwork, typeof(*work), work);
        work->func(work->data);
        kfree(work);
 }
 
+static void nouveau_fence_work_cb(struct fence *fence, struct fence_cb *cb)
+{
+       struct nouveau_fence_work *work = container_of(cb, typeof(*work), cb);
+
+       schedule_work(&work->work);
+}
+
 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 fence_work *work = NULL;
+       struct nouveau_fence_work *work;
 
-       if (nouveau_fence_done(fence)) {
-               func(data);
-               return;
-       }
+       if (fence_is_signaled(&fence->base))
+               goto err;
 
-       fctx = chan->fence;
        work = kmalloc(sizeof(*work), GFP_KERNEL);
        if (!work) {
                WARN_ON(nouveau_fence_wait(fence, false, false));
-               func(data);
-               return;
+               goto err;
        }
 
-       spin_lock(&fctx->lock);
-       if (!fence->channel) {
-               spin_unlock(&fctx->lock);
-               kfree(work);
-               func(data);
-               return;
-       }
-
-       INIT_WORK(&work->base, nouveau_fence_work_handler);
+       INIT_WORK(&work->work, nouveau_fence_work_handler);
        work->func = func;
        work->data = data;
-       list_add(&work->head, &fence->work);
-       spin_unlock(&fctx->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);
-       list_for_each_entry_safe(fence, fnext, &fctx->pending, head) {
-               if (fctx->read(chan) < fence->sequence)
-                       break;
+       if (fence_add_callback(&fence->base, &work->cb, nouveau_fence_work_cb) < 0)
+               goto err_free;
+       return;
 
-               nouveau_fence_signal(fence);
-               nouveau_fence_unref(&fence);
-       }
-       spin_unlock(&fctx->lock);
+err_free:
+       kfree(work);
+err:
+       func(data);
 }
 
 int
 nouveau_fence_emit(struct nouveau_fence *fence, struct nouveau_channel *chan)
 {
        struct nouveau_fence_chan *fctx = chan->fence;
+       struct nouveau_fence_priv *priv = (void*)chan->drm->fence;
        int ret;
 
        fence->channel  = chan;
        fence->timeout  = jiffies + (15 * HZ);
-       fence->sequence = ++fctx->sequence;
 
+       if (priv->uevent)
+               fence_init(&fence->base, &nouveau_fence_ops_uevent,
+                          &fctx->lock,
+                          priv->context_base + chan->chid, ++fctx->sequence);
+       else
+               fence_init(&fence->base, &nouveau_fence_ops_legacy,
+                          &fctx->lock,
+                          priv->context_base + chan->chid, ++fctx->sequence);
+
+       trace_fence_emit(&fence->base);
        ret = fctx->emit(fence);
        if (!ret) {
-               kref_get(&fence->kref);
-               spin_lock(&fctx->lock);
+               fence_get(&fence->base);
+               spin_lock_irq(&fctx->lock);
+               nouveau_fence_update(chan, fctx);
                list_add_tail(&fence->head, &fctx->pending);
-               spin_unlock(&fctx->lock);
+               spin_unlock_irq(&fctx->lock);
        }
 
        return ret;
@@ -161,114 +248,70 @@ nouveau_fence_emit(struct nouveau_fence *fence, struct nouveau_channel *chan)
 bool
 nouveau_fence_done(struct nouveau_fence *fence)
 {
-       if (fence->channel)
-               nouveau_fence_update(fence->channel);
-       return !fence->channel;
-}
+       if (fence->base.ops == &nouveau_fence_ops_legacy ||
+           fence->base.ops == &nouveau_fence_ops_uevent) {
+               struct nouveau_fence_chan *fctx = nouveau_fctx(fence);
+               unsigned long flags;
 
-struct nouveau_fence_wait {
-       struct nouveau_fence_priv *priv;
-       struct nvif_notify notify;
-};
+               if (test_bit(FENCE_FLAG_SIGNALED_BIT, &fence->base.flags))
+                       return true;
 
-static int
-nouveau_fence_wait_uevent_handler(struct nvif_notify *notify)
-{
-       struct nouveau_fence_wait *wait =
-               container_of(notify, typeof(*wait), notify);
-       wake_up_all(&wait->priv->waiting);
-       return NVIF_NOTIFY_KEEP;
+               spin_lock_irqsave(&fctx->lock, flags);
+               nouveau_fence_update(fence->channel, fctx);
+               spin_unlock_irqrestore(&fctx->lock, flags);
+       }
+       return fence_is_signaled(&fence->base);
 }
 
-static int
-nouveau_fence_wait_uevent(struct nouveau_fence *fence, bool intr)
-
+static long
+nouveau_fence_wait_legacy(struct fence *f, bool intr, long wait)
 {
-       struct nouveau_channel *chan = fence->channel;
-       struct nouveau_fence_priv *priv = chan->drm->fence;
-       struct nouveau_fence_wait wait = { .priv = priv };
-       int ret = 0;
+       struct nouveau_fence *fence = from_fence(f);
+       unsigned long sleep_time = NSEC_PER_MSEC / 1000;
+       unsigned long t = jiffies, timeout = t + wait;
 
-       ret = nvif_notify_init(chan->object, NULL,
-                              nouveau_fence_wait_uevent_handler, false,
-                              G82_CHANNEL_DMA_V0_NTFY_UEVENT,
-                              &(struct nvif_notify_uevent_req) {
-                              },
-                              sizeof(struct nvif_notify_uevent_req),
-                              sizeof(struct nvif_notify_uevent_rep),
-                              &wait.notify);
-       if (ret)
-               return ret;
+       while (!nouveau_fence_done(fence)) {
+               ktime_t kt;
 
-       nvif_notify_get(&wait.notify);
-
-       if (fence->timeout) {
-               unsigned long timeout = fence->timeout - jiffies;
-
-               if (time_before(jiffies, fence->timeout)) {
-                       if (intr) {
-                               ret = wait_event_interruptible_timeout(
-                                               priv->waiting,
-                                               nouveau_fence_done(fence),
-                                               timeout);
-                       } else {
-                               ret = wait_event_timeout(priv->waiting,
-                                               nouveau_fence_done(fence),
-                                               timeout);
-                       }
-               }
+               t = jiffies;
 
-               if (ret >= 0) {
-                       fence->timeout = jiffies + ret;
-                       if (time_after_eq(jiffies, fence->timeout))
-                               ret = -EBUSY;
-               }
-       } else {
-               if (intr) {
-                       ret = wait_event_interruptible(priv->waiting,
-                                       nouveau_fence_done(fence));
-               } else {
-                       wait_event(priv->waiting, nouveau_fence_done(fence));
+               if (wait != MAX_SCHEDULE_TIMEOUT && time_after_eq(t, timeout)) {
+                       __set_current_state(TASK_RUNNING);
+                       return 0;
                }
+
+               __set_current_state(intr ? TASK_INTERRUPTIBLE :
+                                          TASK_UNINTERRUPTIBLE);
+
+               kt = ktime_set(0, sleep_time);
+               schedule_hrtimeout(&kt, HRTIMER_MODE_REL);
+               sleep_time *= 2;
+               if (sleep_time > NSEC_PER_MSEC)
+                       sleep_time = NSEC_PER_MSEC;
+
+               if (intr && signal_pending(current))
+                       return -ERESTARTSYS;
        }
 
-       nvif_notify_fini(&wait.notify);
-       if (unlikely(ret < 0))
-               return ret;
+       __set_current_state(TASK_RUNNING);
 
-       return 0;
+       return timeout - t;
 }
 
-int
-nouveau_fence_wait(struct nouveau_fence *fence, bool lazy, bool intr)
+static int
+nouveau_fence_wait_busy(struct nouveau_fence *fence, bool intr)
 {
-       struct nouveau_channel *chan = fence->channel;
-       struct nouveau_fence_priv *priv = chan ? chan->drm->fence : NULL;
-       unsigned long sleep_time = NSEC_PER_MSEC / 1000;
-       ktime_t t;
        int ret = 0;
 
-       while (priv && priv->uevent && lazy && !nouveau_fence_done(fence)) {
-               ret = nouveau_fence_wait_uevent(fence, intr);
-               if (ret < 0)
-                       return ret;
-       }
-
        while (!nouveau_fence_done(fence)) {
-               if (fence->timeout && time_after_eq(jiffies, fence->timeout)) {
+               if (time_after_eq(jiffies, fence->timeout)) {
                        ret = -EBUSY;
                        break;
                }
 
-               __set_current_state(intr ? TASK_INTERRUPTIBLE :
-                                          TASK_UNINTERRUPTIBLE);
-               if (lazy) {
-                       t = ktime_set(0, sleep_time);
-                       schedule_hrtimeout(&t, HRTIMER_MODE_REL);
-                       sleep_time *= 2;
-                       if (sleep_time > NSEC_PER_MSEC)
-                               sleep_time = NSEC_PER_MSEC;
-               }
+               __set_current_state(intr ?
+                                   TASK_INTERRUPTIBLE :
+                                   TASK_UNINTERRUPTIBLE);
 
                if (intr && signal_pending(current)) {
                        ret = -ERESTARTSYS;
@@ -281,36 +324,77 @@ nouveau_fence_wait(struct nouveau_fence *fence, bool lazy, bool intr)
 }
 
 int
-nouveau_fence_sync(struct nouveau_fence *fence, struct nouveau_channel *chan)
+nouveau_fence_wait(struct nouveau_fence *fence, bool lazy, bool intr)
+{
+       long ret;
+
+       if (!lazy)
+               return nouveau_fence_wait_busy(fence, intr);
+
+       ret = fence_wait_timeout(&fence->base, intr, 15 * HZ);
+       if (ret < 0)
+               return ret;
+       else if (!ret)
+               return -EBUSY;
+       else
+               return 0;
+}
+
+int
+nouveau_fence_sync(struct nouveau_bo *nvbo, struct nouveau_channel *chan)
 {
        struct nouveau_fence_chan *fctx = chan->fence;
-       struct nouveau_channel *prev;
-       int ret = 0;
+       struct fence *fence = NULL;
+       struct reservation_object *resv = nvbo->bo.resv;
+       struct reservation_object_list *fobj;
+       int ret = 0, i;
+
+       fence = nvbo->bo.sync_obj;
+       if (fence && fence_is_signaled(fence)) {
+               nouveau_fence_unref((struct nouveau_fence **)
+                                   &nvbo->bo.sync_obj);
+               fence = NULL;
+       }
+
+       if (fence) {
+               struct nouveau_fence *f = from_fence(fence);
+               struct nouveau_channel *prev = f->channel;
 
-       prev = fence ? fence->channel : NULL;
-       if (prev) {
-               if (unlikely(prev != chan && !nouveau_fence_done(fence))) {
-                       ret = fctx->sync(fence, prev, chan);
+               if (prev != chan) {
+                       ret = fctx->sync(f, prev, chan);
                        if (unlikely(ret))
-                               ret = nouveau_fence_wait(fence, true, false);
+                               ret = nouveau_fence_wait(f, true, true);
                }
        }
 
-       return ret;
-}
+       if (ret)
+               return ret;
 
-static void
-nouveau_fence_del(struct kref *kref)
-{
-       struct nouveau_fence *fence = container_of(kref, typeof(*fence), kref);
-       kfree(fence);
+       fence = reservation_object_get_excl(resv);
+       if (fence && !nouveau_local_fence(fence, chan->drm))
+               ret = fence_wait(fence, true);
+
+       fobj = reservation_object_get_list(resv);
+       if (!fobj || ret)
+               return ret;
+
+       for (i = 0; i < fobj->shared_count && !ret; ++i) {
+               fence = rcu_dereference_protected(fobj->shared[i],
+                                               reservation_object_held(resv));
+
+               /* should always be true, for now */
+               if (!nouveau_local_fence(fence, chan->drm))
+                       ret = fence_wait(fence, true);
+       }
+
+       return ret;
 }
 
 void
 nouveau_fence_unref(struct nouveau_fence **pfence)
 {
        if (*pfence)
-               kref_put(&(*pfence)->kref, nouveau_fence_del);
+               fence_put(&(*pfence)->base);
        *pfence = NULL;
 }
 
@@ -318,7 +402,7 @@ struct nouveau_fence *
 nouveau_fence_ref(struct nouveau_fence *fence)
 {
        if (fence)
-               kref_get(&fence->kref);
+               fence_get(&fence->base);
        return fence;
 }
 
@@ -336,9 +420,7 @@ nouveau_fence_new(struct nouveau_channel *chan, bool sysmem,
        if (!fence)
                return -ENOMEM;
 
-       INIT_LIST_HEAD(&fence->work);
        fence->sysmem = sysmem;
-       kref_init(&fence->kref);
 
        ret = nouveau_fence_emit(fence, chan);
        if (ret)
@@ -347,3 +429,92 @@ nouveau_fence_new(struct nouveau_channel *chan, bool sysmem,
        *pfence = fence;
        return ret;
 }
+
+static const char *nouveau_fence_get_get_driver_name(struct fence *fence)
+{
+       return "nouveau";
+}
+
+static const char *nouveau_fence_get_timeline_name(struct fence *f)
+{
+       struct nouveau_fence *fence = from_fence(f);
+       struct nouveau_fence_chan *fctx = nouveau_fctx(fence);
+
+       return fence->channel ? fctx->name : "dead channel";
+}
+
+/*
+ * In an ideal world, read would not assume the channel context is still alive.
+ * This function may be called from another device, running into free memory as a
+ * result. The drm node should still be there, so we can derive the index from
+ * the fence context.
+ */
+static bool nouveau_fence_is_signaled(struct fence *f)
+{
+       struct nouveau_fence *fence = from_fence(f);
+       struct nouveau_fence_chan *fctx = nouveau_fctx(fence);
+       struct nouveau_channel *chan = fence->channel;
+
+       return (int)(fctx->read(chan) - fence->base.seqno) >= 0;
+}
+
+static bool nouveau_fence_no_signaling(struct fence *f)
+{
+       struct nouveau_fence *fence = from_fence(f);
+
+       /*
+        * caller should have a reference on the fence,
+        * else fence could get freed here
+        */
+       WARN_ON(atomic_read(&fence->base.refcount.refcount) <= 1);
+
+       /*
+        * This needs uevents to work correctly, but fence_add_callback relies on
+        * being able to enable signaling. It will still get signaled eventually,
+        * just not right away.
+        */
+       if (nouveau_fence_is_signaled(f)) {
+               list_del(&fence->head);
+
+               fence_put(&fence->base);
+               return false;
+       }
+
+       return true;
+}
+
+static const struct fence_ops nouveau_fence_ops_legacy = {
+       .get_driver_name = nouveau_fence_get_get_driver_name,
+       .get_timeline_name = nouveau_fence_get_timeline_name,
+       .enable_signaling = nouveau_fence_no_signaling,
+       .signaled = nouveau_fence_is_signaled,
+       .wait = nouveau_fence_wait_legacy,
+       .release = NULL
+};
+
+static bool nouveau_fence_enable_signaling(struct fence *f)
+{
+       struct nouveau_fence *fence = from_fence(f);
+       struct nouveau_fence_chan *fctx = nouveau_fctx(fence);
+       bool ret;
+
+       if (!fctx->notify_ref++)
+               nvif_notify_get(&fctx->notify);
+
+       ret = nouveau_fence_no_signaling(f);
+       if (ret)
+               set_bit(FENCE_FLAG_USER_BITS, &fence->base.flags);
+       else if (!--fctx->notify_ref)
+               nvif_notify_put(&fctx->notify);
+
+       return ret;
+}
+
+static const struct fence_ops nouveau_fence_ops_uevent = {
+       .get_driver_name = nouveau_fence_get_get_driver_name,
+       .get_timeline_name = nouveau_fence_get_timeline_name,
+       .enable_signaling = nouveau_fence_enable_signaling,
+       .signaled = nouveau_fence_is_signaled,
+       .wait = fence_default_wait,
+       .release = NULL
+};
index c57bb61da58c162d241be806028dcf78809ff4eb..44efd8c7426c7aa91ece3c70fb8dd69535014734 100644 (file)
@@ -1,18 +1,21 @@
 #ifndef __NOUVEAU_FENCE_H__
 #define __NOUVEAU_FENCE_H__
 
+#include <linux/fence.h>
+#include <nvif/notify.h>
+
 struct nouveau_drm;
+struct nouveau_bo;
 
 struct nouveau_fence {
+       struct fence base;
+
        struct list_head head;
-       struct list_head work;
-       struct kref kref;
 
        bool sysmem;
 
        struct nouveau_channel *channel;
        unsigned long timeout;
-       u32 sequence;
 };
 
 int  nouveau_fence_new(struct nouveau_channel *, bool sysmem,
@@ -25,9 +28,10 @@ int  nouveau_fence_emit(struct nouveau_fence *, struct nouveau_channel *);
 bool nouveau_fence_done(struct nouveau_fence *);
 void nouveau_fence_work(struct nouveau_fence *, void (*)(void *), void *);
 int  nouveau_fence_wait(struct nouveau_fence *, bool lazy, bool intr);
-int  nouveau_fence_sync(struct nouveau_fence *, struct nouveau_channel *);
+int  nouveau_fence_sync(struct nouveau_bo *, struct nouveau_channel *);
 
 struct nouveau_fence_chan {
+       spinlock_t lock;
        struct list_head pending;
        struct list_head flip;
 
@@ -38,8 +42,12 @@ struct nouveau_fence_chan {
        int  (*emit32)(struct nouveau_channel *, u64, u32);
        int  (*sync32)(struct nouveau_channel *, u64, u32);
 
-       spinlock_t lock;
        u32 sequence;
+       u32 context;
+       char name[24];
+
+       struct nvif_notify notify;
+       int notify_ref;
 };
 
 struct nouveau_fence_priv {
@@ -49,13 +57,13 @@ struct nouveau_fence_priv {
        int  (*context_new)(struct nouveau_channel *);
        void (*context_del)(struct nouveau_channel *);
 
-       wait_queue_head_t waiting;
+       u32 contexts, context_base;
        bool uevent;
 };
 
 #define nouveau_fence(drm) ((struct nouveau_fence_priv *)(drm)->fence)
 
-void nouveau_fence_context_new(struct nouveau_fence_chan *);
+void nouveau_fence_context_new(struct nouveau_channel *, struct nouveau_fence_chan *);
 void nouveau_fence_context_del(struct nouveau_fence_chan *);
 
 int nv04_fence_create(struct nouveau_drm *);
index 1650c0bdb0fce2d93dc39455f380c534d34b030c..d68c9656e4094760eb305cfe8e54bad8aeb4cff5 100644 (file)
@@ -425,18 +425,6 @@ retry:
        return 0;
 }
 
-static int
-validate_sync(struct nouveau_channel *chan, struct nouveau_bo *nvbo)
-{
-       struct nouveau_fence *fence = nvbo->bo.sync_obj;
-       int ret = 0;
-
-       if (fence)
-               ret = nouveau_fence_sync(fence, chan);
-
-       return ret;
-}
-
 static int
 validate_list(struct nouveau_channel *chan, struct nouveau_cli *cli,
              struct list_head *list, struct drm_nouveau_gem_pushbuf_bo *pbbo,
@@ -466,9 +454,10 @@ validate_list(struct nouveau_channel *chan, struct nouveau_cli *cli,
                        return ret;
                }
 
-               ret = validate_sync(chan, nvbo);
+               ret = nouveau_fence_sync(nvbo, chan);
                if (unlikely(ret)) {
-                       NV_PRINTK(error, cli, "fail post-validate sync\n");
+                       if (ret != -ERESTARTSYS)
+                               NV_PRINTK(error, cli, "fail post-validate sync\n");
                        return ret;
                }
 
index 239c2c5a96159498f2ea2fee1ff235db6c9ecf56..4484131d826a45597a6f6732b1fa88810b507bc8 100644 (file)
@@ -41,7 +41,7 @@ nv04_fence_emit(struct nouveau_fence *fence)
        int ret = RING_SPACE(chan, 2);
        if (ret == 0) {
                BEGIN_NV04(chan, NvSubSw, 0x0150, 1);
-               OUT_RING  (chan, fence->sequence);
+               OUT_RING  (chan, fence->base.seqno);
                FIRE_RING (chan);
        }
        return ret;
@@ -75,7 +75,7 @@ nv04_fence_context_new(struct nouveau_channel *chan)
 {
        struct nv04_fence_chan *fctx = kzalloc(sizeof(*fctx), GFP_KERNEL);
        if (fctx) {
-               nouveau_fence_context_new(&fctx->base);
+               nouveau_fence_context_new(chan, &fctx->base);
                fctx->base.emit = nv04_fence_emit;
                fctx->base.sync = nv04_fence_sync;
                fctx->base.read = nv04_fence_read;
@@ -105,5 +105,7 @@ nv04_fence_create(struct nouveau_drm *drm)
        priv->base.dtor = nv04_fence_destroy;
        priv->base.context_new = nv04_fence_context_new;
        priv->base.context_del = nv04_fence_context_del;
+       priv->base.contexts = 15;
+       priv->base.context_base = fence_context_alloc(priv->base.contexts);
        return 0;
 }
index 4faaf0acf5d7d919769650662e1788aa861904e2..737d066ffc60e3109a106ff9c7a299678ba15591 100644 (file)
@@ -33,7 +33,7 @@ nv10_fence_emit(struct nouveau_fence *fence)
        int ret = RING_SPACE(chan, 2);
        if (ret == 0) {
                BEGIN_NV04(chan, 0, NV10_SUBCHAN_REF_CNT, 1);
-               OUT_RING  (chan, fence->sequence);
+               OUT_RING  (chan, fence->base.seqno);
                FIRE_RING (chan);
        }
        return ret;
@@ -75,7 +75,7 @@ nv10_fence_context_new(struct nouveau_channel *chan)
        if (!fctx)
                return -ENOMEM;
 
-       nouveau_fence_context_new(&fctx->base);
+       nouveau_fence_context_new(chan, &fctx->base);
        fctx->base.emit = nv10_fence_emit;
        fctx->base.read = nv10_fence_read;
        fctx->base.sync = nv10_fence_sync;
@@ -106,6 +106,8 @@ nv10_fence_create(struct nouveau_drm *drm)
        priv->base.dtor = nv10_fence_destroy;
        priv->base.context_new = nv10_fence_context_new;
        priv->base.context_del = nv10_fence_context_del;
+       priv->base.contexts = 31;
+       priv->base.context_base = fence_context_alloc(priv->base.contexts);
        spin_lock_init(&priv->lock);
        return 0;
 }
index ca907479f92fc6c42ee0647c11e1881e079ea74d..6f9a1f8e2d0f1e0f59460c37d1cf70b06cc5d8f5 100644 (file)
@@ -84,7 +84,7 @@ nv17_fence_context_new(struct nouveau_channel *chan)
        if (!fctx)
                return -ENOMEM;
 
-       nouveau_fence_context_new(&fctx->base);
+       nouveau_fence_context_new(chan, &fctx->base);
        fctx->base.emit = nv10_fence_emit;
        fctx->base.read = nv10_fence_read;
        fctx->base.sync = nv17_fence_sync;
@@ -124,6 +124,8 @@ nv17_fence_create(struct nouveau_drm *drm)
        priv->base.resume = nv17_fence_resume;
        priv->base.context_new = nv17_fence_context_new;
        priv->base.context_del = nv10_fence_context_del;
+       priv->base.contexts = 31;
+       priv->base.context_base = fence_context_alloc(priv->base.contexts);
        spin_lock_init(&priv->lock);
 
        ret = nouveau_bo_new(drm->dev, 4096, 0x1000, TTM_PL_FLAG_VRAM,
index 195cf51a7c31e187717f11b5542656247905a28b..08fad3668a1c098d78d1769faf76301d5cc06ad9 100644 (file)
@@ -46,7 +46,7 @@ nv50_fence_context_new(struct nouveau_channel *chan)
        if (!fctx)
                return -ENOMEM;
 
-       nouveau_fence_context_new(&fctx->base);
+       nouveau_fence_context_new(chan, &fctx->base);
        fctx->base.emit = nv10_fence_emit;
        fctx->base.read = nv10_fence_read;
        fctx->base.sync = nv17_fence_sync;
@@ -95,6 +95,8 @@ nv50_fence_create(struct nouveau_drm *drm)
        priv->base.resume = nv17_fence_resume;
        priv->base.context_new = nv50_fence_context_new;
        priv->base.context_del = nv10_fence_context_del;
+       priv->base.contexts = 127;
+       priv->base.context_base = fence_context_alloc(priv->base.contexts);
        spin_lock_init(&priv->lock);
 
        ret = nouveau_bo_new(drm->dev, 4096, 0x1000, TTM_PL_FLAG_VRAM,
index 933a779c93ab7c132b82eed2f0e2bb5692462cc0..a2f28082c272bf2e573ff0420ded222881084fbc 100644 (file)
@@ -82,7 +82,7 @@ nv84_fence_emit(struct nouveau_fence *fence)
        else
                addr += fctx->vma.offset;
 
-       return fctx->base.emit32(chan, addr, fence->sequence);
+       return fctx->base.emit32(chan, addr, fence->base.seqno);
 }
 
 static int
@@ -97,7 +97,7 @@ nv84_fence_sync(struct nouveau_fence *fence,
        else
                addr += fctx->vma.offset;
 
-       return fctx->base.sync32(chan, addr, fence->sequence);
+       return fctx->base.sync32(chan, addr, fence->base.seqno);
 }
 
 static u32
@@ -139,12 +139,13 @@ nv84_fence_context_new(struct nouveau_channel *chan)
        if (!fctx)
                return -ENOMEM;
 
-       nouveau_fence_context_new(&fctx->base);
+       nouveau_fence_context_new(chan, &fctx->base);
        fctx->base.emit = nv84_fence_emit;
        fctx->base.sync = nv84_fence_sync;
        fctx->base.read = nv84_fence_read;
        fctx->base.emit32 = nv84_fence_emit32;
        fctx->base.sync32 = nv84_fence_sync32;
+       fctx->base.sequence = nv84_fence_read(chan);
 
        ret = nouveau_bo_vma_add(priv->bo, cli->vm, &fctx->vma);
        if (ret == 0) {
@@ -168,13 +169,12 @@ nv84_fence_context_new(struct nouveau_channel *chan)
 static bool
 nv84_fence_suspend(struct nouveau_drm *drm)
 {
-       struct nouveau_fifo *pfifo = nvkm_fifo(&drm->device);
        struct nv84_fence_priv *priv = drm->fence;
        int i;
 
-       priv->suspend = vmalloc((pfifo->max + 1) * sizeof(u32));
+       priv->suspend = vmalloc(priv->base.contexts * sizeof(u32));
        if (priv->suspend) {
-               for (i = 0; i <= pfifo->max; i++)
+               for (i = 0; i < priv->base.contexts; i++)
                        priv->suspend[i] = nouveau_bo_rd32(priv->bo, i*4);
        }
 
@@ -184,12 +184,11 @@ nv84_fence_suspend(struct nouveau_drm *drm)
 static void
 nv84_fence_resume(struct nouveau_drm *drm)
 {
-       struct nouveau_fifo *pfifo = nvkm_fifo(&drm->device);
        struct nv84_fence_priv *priv = drm->fence;
        int i;
 
        if (priv->suspend) {
-               for (i = 0; i <= pfifo->max; i++)
+               for (i = 0; i < priv->base.contexts; i++)
                        nouveau_bo_wr32(priv->bo, i*4, priv->suspend[i]);
                vfree(priv->suspend);
                priv->suspend = NULL;
@@ -229,10 +228,11 @@ nv84_fence_create(struct nouveau_drm *drm)
        priv->base.context_new = nv84_fence_context_new;
        priv->base.context_del = nv84_fence_context_del;
 
-       init_waitqueue_head(&priv->base.waiting);
+       priv->base.contexts = pfifo->max + 1;
+       priv->base.context_base = fence_context_alloc(priv->base.contexts);
        priv->base.uevent = true;
 
-       ret = nouveau_bo_new(drm->dev, 16 * (pfifo->max + 1), 0,
+       ret = nouveau_bo_new(drm->dev, 16 * priv->base.contexts, 0,
                             TTM_PL_FLAG_VRAM, 0, 0, NULL, &priv->bo);
        if (ret == 0) {
                ret = nouveau_bo_pin(priv->bo, TTM_PL_FLAG_VRAM);
@@ -246,7 +246,7 @@ nv84_fence_create(struct nouveau_drm *drm)
        }
 
        if (ret == 0)
-               ret = nouveau_bo_new(drm->dev, 16 * (pfifo->max + 1), 0,
+               ret = nouveau_bo_new(drm->dev, 16 * priv->base.contexts, 0,
                                     TTM_PL_FLAG_TT, 0, 0, NULL,
                                     &priv->bo_gart);
        if (ret == 0) {