Add a new nouveau_pushbuf_kick_fence function that takes and emits
a sync fence fd.  The fence fd can be waited on, or merged with
other fence fd's, or passed back to kernel as a prerquisite for a
subsequent hw operation.

Signed-off-by: Lauri Peltonen <lpeltonen at nvidia.com>
---
 include/drm/nouveau_drm.h | 10 +++++
 nouveau/nouveau.h         |  2 +
 nouveau/pushbuf.c         | 93 ++++++++++++++++++++++++++++++++---------------
 3 files changed, 76 insertions(+), 29 deletions(-)

diff --git a/include/drm/nouveau_drm.h b/include/drm/nouveau_drm.h
index b18cad0..3e40210 100644
--- a/include/drm/nouveau_drm.h
+++ b/include/drm/nouveau_drm.h
@@ -171,6 +171,15 @@ struct drm_nouveau_gem_pushbuf {
        uint64_t gart_available;
 };

+#define NOUVEAU_GEM_PUSHBUF_2_FENCE_WAIT                             0x00000001
+#define NOUVEAU_GEM_PUSHBUF_2_FENCE_EMIT                             0x00000002
+struct drm_nouveau_gem_pushbuf_2 {
+       struct drm_nouveau_gem_pushbuf base;
+       uint32_t flags;
+       int32_t  fence;
+       uint64_t reserved;
+};
+
 #define NOUVEAU_GEM_CPU_PREP_NOWAIT                                  0x00000001
 #define NOUVEAU_GEM_CPU_PREP_NOBLOCK                                 0x00000002
 #define NOUVEAU_GEM_CPU_PREP_WRITE                                   0x00000004
@@ -204,5 +213,6 @@ struct drm_nouveau_sarea {
 #define DRM_NOUVEAU_GEM_CPU_PREP       0x42
 #define DRM_NOUVEAU_GEM_CPU_FINI       0x43
 #define DRM_NOUVEAU_GEM_INFO           0x44
+#define DRM_NOUVEAU_GEM_PUSHBUF_2      0x45

 #endif /* __NOUVEAU_DRM_H__ */
diff --git a/nouveau/nouveau.h b/nouveau/nouveau.h
index a55e2b0..281420f 100644
--- a/nouveau/nouveau.h
+++ b/nouveau/nouveau.h
@@ -225,6 +225,8 @@ void nouveau_pushbuf_reloc(struct nouveau_pushbuf *, struct 
nouveau_bo *,
 int  nouveau_pushbuf_validate(struct nouveau_pushbuf *);
 uint32_t nouveau_pushbuf_refd(struct nouveau_pushbuf *, struct nouveau_bo *);
 int  nouveau_pushbuf_kick(struct nouveau_pushbuf *, struct nouveau_object 
*channel);
+int  nouveau_pushbuf_kick_fence(struct nouveau_pushbuf *,
+                               struct nouveau_object *channel, int *fence);
 struct nouveau_bufctx *
 nouveau_pushbuf_bufctx(struct nouveau_pushbuf *, struct nouveau_bufctx *);

diff --git a/nouveau/pushbuf.c b/nouveau/pushbuf.c
index 6e703a4..8667d05 100644
--- a/nouveau/pushbuf.c
+++ b/nouveau/pushbuf.c
@@ -33,6 +33,7 @@
 #include <string.h>
 #include <assert.h>
 #include <errno.h>
+#include <unistd.h>

 #include <xf86drm.h>
 #include <xf86atomic.h>
@@ -77,7 +78,7 @@ nouveau_pushbuf(struct nouveau_pushbuf *push)
 }

 static int pushbuf_validate(struct nouveau_pushbuf *, bool);
-static int pushbuf_flush(struct nouveau_pushbuf *);
+static int pushbuf_flush(struct nouveau_pushbuf *, int *);

 static bool
 pushbuf_kref_fits(struct nouveau_pushbuf *push, struct nouveau_bo *bo,
@@ -172,7 +173,7 @@ pushbuf_kref(struct nouveau_pushbuf *push, struct 
nouveau_bo *bo,
         */
        fpush = cli_push_get(push->client, bo);
        if (fpush && fpush != push)
-               pushbuf_flush(fpush);
+               pushbuf_flush(fpush, NULL);

        kref = cli_kref_get(push->client, bo);
        if (kref) {
@@ -305,18 +306,21 @@ pushbuf_dump(struct nouveau_pushbuf_krec *krec, int 
krec_id, int chid)
 }

 static int
-pushbuf_submit(struct nouveau_pushbuf *push, struct nouveau_object *chan)
+pushbuf_submit(struct nouveau_pushbuf *push, struct nouveau_object *chan,
+              int *fence)
 {
        struct nouveau_pushbuf_priv *nvpb = nouveau_pushbuf(push);
        struct nouveau_pushbuf_krec *krec = nvpb->list;
        struct nouveau_device *dev = push->client->device;
        struct drm_nouveau_gem_pushbuf_bo_presumed *info;
        struct drm_nouveau_gem_pushbuf_bo *kref;
-       struct drm_nouveau_gem_pushbuf req;
+       struct drm_nouveau_gem_pushbuf_2 req_2;
+       struct drm_nouveau_gem_pushbuf *req = &req_2.base;
        struct nouveau_fifo *fifo = chan->data;
        struct nouveau_bo *bo;
        int krec_id = 0;
        int ret = 0, i;
+       int fence_out = -1;

        if (chan->oclass != NOUVEAU_FIFO_CHANNEL_CLASS)
                return -EINVAL;
@@ -326,30 +330,42 @@ pushbuf_submit(struct nouveau_pushbuf *push, struct 
nouveau_object *chan)

        nouveau_pushbuf_data(push, NULL, 0, 0);

+       /* TODO: If fence is requested, force kickoff. */
        while (krec && krec->nr_push) {
-               req.channel = fifo->channel;
-               req.nr_buffers = krec->nr_buffer;
-               req.buffers = (uint64_t)(unsigned long)krec->buffer;
-               req.nr_relocs = krec->nr_reloc;
-               req.nr_push = krec->nr_push;
-               req.relocs = (uint64_t)(unsigned long)krec->reloc;
-               req.push = (uint64_t)(unsigned long)krec->push;
-               req.suffix0 = nvpb->suffix0;
-               req.suffix1 = nvpb->suffix1;
-               req.vram_available = 0; /* for valgrind */
-               req.gart_available = 0;
+               req->channel = fifo->channel;
+               req->nr_buffers = krec->nr_buffer;
+               req->buffers = (uint64_t)(unsigned long)krec->buffer;
+               req->nr_relocs = krec->nr_reloc;
+               req->nr_push = krec->nr_push;
+               req->relocs = (uint64_t)(unsigned long)krec->reloc;
+               req->push = (uint64_t)(unsigned long)krec->push;
+               req->suffix0 = nvpb->suffix0;
+               req->suffix1 = nvpb->suffix1;
+               req->vram_available = 0; /* for valgrind */
+               req->gart_available = 0;
+               if (fence) {
+                       req_2.flags = NOUVEAU_GEM_PUSHBUF_2_FENCE_EMIT;
+                       if (*fence >= 0)
+                               req_2.flags |= NOUVEAU_GEM_PUSHBUF_2_FENCE_WAIT;
+                       req_2.fence = *fence;
+                       req_2.reserved = 0;
+               }

                if (dbg_on(0))
                        pushbuf_dump(krec, krec_id++, fifo->channel);

 #ifndef SIMULATE
-               ret = drmCommandWriteRead(dev->fd, DRM_NOUVEAU_GEM_PUSHBUF,
-                                         &req, sizeof(req));
-               nvpb->suffix0 = req.suffix0;
-               nvpb->suffix1 = req.suffix1;
-               dev->vram_limit = (req.vram_available *
+               if (fence)
+                       ret = drmCommandWriteRead(dev->fd, 
DRM_NOUVEAU_GEM_PUSHBUF_2,
+                                                 &req_2, sizeof(req_2));
+               else
+                       ret = drmCommandWriteRead(dev->fd, 
DRM_NOUVEAU_GEM_PUSHBUF,
+                                                 req, sizeof(*req));
+               nvpb->suffix0 = req->suffix0;
+               nvpb->suffix1 = req->suffix1;
+               dev->vram_limit = (req->vram_available *
                                nouveau_device(dev)->vram_limit_percent) / 100;
-               dev->gart_limit = (req.gart_available *
+               dev->gart_limit = (req->gart_available *
                                nouveau_device(dev)->gart_limit_percent) / 100;
 #else
                if (dbg_on(31))
@@ -362,6 +378,12 @@ pushbuf_submit(struct nouveau_pushbuf *push, struct 
nouveau_object *chan)
                        break;
                }

+               if (fence) {
+                       if (fence_out >= 0)
+                               close(fence_out);
+                       fence_out = req_2.fence;
+               }
+
                kref = krec->buffer;
                for (i = 0; i < krec->nr_buffer; i++, kref++) {
                        bo = (void *)(unsigned long)kref->user_priv;
@@ -385,11 +407,17 @@ pushbuf_submit(struct nouveau_pushbuf *push, struct 
nouveau_object *chan)
                krec = krec->next;
        }

+       if (!ret && fence) {
+               if (*fence >= 0)
+                       close(*fence);
+               *fence = fence_out;
+       }
+
        return ret;
 }

 static int
-pushbuf_flush(struct nouveau_pushbuf *push)
+pushbuf_flush(struct nouveau_pushbuf *push, int *fence)
 {
        struct nouveau_pushbuf_priv *nvpb = nouveau_pushbuf(push);
        struct nouveau_pushbuf_krec *krec = nvpb->krec;
@@ -399,7 +427,7 @@ pushbuf_flush(struct nouveau_pushbuf *push)
        int ret = 0, i;

        if (push->channel) {
-               ret = pushbuf_submit(push, push->channel);
+               ret = pushbuf_submit(push, push->channel, fence);
        } else {
                nouveau_pushbuf_data(push, NULL, 0, 0);
                krec->next = malloc(sizeof(*krec));
@@ -469,7 +497,7 @@ pushbuf_refn(struct nouveau_pushbuf *push, bool retry,
        if (ret) {
                pushbuf_refn_fail(push, sref, krec->nr_reloc);
                if (retry) {
-                       pushbuf_flush(push);
+                       pushbuf_flush(push, NULL);
                        nouveau_pushbuf_space(push, 0, 0, 0);
                        return pushbuf_refn(push, false, refs, nr);
                }
@@ -521,7 +549,7 @@ pushbuf_validate(struct nouveau_pushbuf *push, bool retry)
        if (ret) {
                pushbuf_refn_fail(push, sref, srel);
                if (retry) {
-                       pushbuf_flush(push);
+                       pushbuf_flush(push, NULL);
                        return pushbuf_validate(push, false);
                }
        }
@@ -673,7 +701,7 @@ nouveau_pushbuf_space(struct nouveau_pushbuf *push,
            krec->nr_reloc + relocs >= NOUVEAU_GEM_MAX_RELOCS ||
            krec->nr_push + pushes >= NOUVEAU_GEM_MAX_PUSH) {
                if (nvpb->bo && krec->nr_buffer)
-                       pushbuf_flush(push);
+                       pushbuf_flush(push, NULL);
                flushed = true;
        }

@@ -767,10 +795,17 @@ nouveau_pushbuf_refd(struct nouveau_pushbuf *push, struct 
nouveau_bo *bo)
 }

 drm_public int
-nouveau_pushbuf_kick(struct nouveau_pushbuf *push, struct nouveau_object *chan)
+nouveau_pushbuf_kick_fence(struct nouveau_pushbuf *push,
+                          struct nouveau_object *chan, int *fence)
 {
        if (!push->channel)
-               return pushbuf_submit(push, chan);
-       pushbuf_flush(push);
+               return pushbuf_submit(push, chan, fence);
+       pushbuf_flush(push, fence);
        return pushbuf_validate(push, false);
 }
+
+drm_public int
+nouveau_pushbuf_kick(struct nouveau_pushbuf *push, struct nouveau_object *chan)
+{
+       return nouveau_pushbuf_kick_fence(push, chan, NULL);
+}
-- 
1.8.1.5

Reply via email to