summaryrefslogtreecommitdiff
path: root/nouveau
diff options
context:
space:
mode:
authorThierry Reding <treding@nvidia.com>2017-10-02 15:51:08 +0200
committerThierry Reding <treding@nvidia.com>2018-05-02 17:29:39 +0200
commit248f843f0e36ca7875ad0a252af72d050f27b8c1 (patch)
tree62db24338558b2d1041662496d00697466e441c7 /nouveau
parent1ac3ecde2f2c9afd7110389eccc6860daa6627ca (diff)
nouveau: Support fence FDs
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 FDs, or passed back to the kernel as a prerequisite for a subsequent HW operation. Based heavily on work by Lauri Peltonen <lpeltonen@nvidia.com> Signed-off-by: Thierry Reding <treding@nvidia.com>
Diffstat (limited to 'nouveau')
-rw-r--r--nouveau/nouveau.h2
-rw-r--r--nouveau/pushbuf.c133
2 files changed, 101 insertions, 34 deletions
diff --git a/nouveau/nouveau.h b/nouveau/nouveau.h
index 335ce77d..70d68070 100644
--- a/nouveau/nouveau.h
+++ b/nouveau/nouveau.h
@@ -226,6 +226,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 *chan);
+int nouveau_pushbuf_kick_fence(struct nouveau_pushbuf *,
+ struct nouveau_object *chan, 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 445c966e..fb1daea3 100644
--- a/nouveau/pushbuf.c
+++ b/nouveau/pushbuf.c
@@ -29,6 +29,7 @@
#include <string.h>
#include <assert.h>
#include <errno.h>
+#include <unistd.h>
#include <xf86drm.h>
#include <xf86atomic.h>
@@ -73,7 +74,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,
@@ -168,7 +169,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) {
@@ -303,7 +304,8 @@ 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_fd)
{
struct nouveau_pushbuf_priv *nvpb = nouveau_pushbuf(push);
struct nouveau_pushbuf_krec *krec = nvpb->list;
@@ -311,9 +313,9 @@ pushbuf_submit(struct nouveau_pushbuf *push, struct nouveau_object *chan)
struct nouveau_drm *drm = nouveau_drm(&dev->object);
struct drm_nouveau_gem_pushbuf_bo_presumed *info;
struct drm_nouveau_gem_pushbuf_bo *kref;
- struct drm_nouveau_gem_pushbuf req;
struct nouveau_fifo *fifo = chan->data;
struct nouveau_bo *bo;
+ int fence_out = -1;
int krec_id = 0;
int ret = 0, i;
@@ -326,34 +328,83 @@ pushbuf_submit(struct nouveau_pushbuf *push, struct nouveau_object *chan)
nouveau_pushbuf_data(push, NULL, 0, 0);
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;
-
if (dbg_on(0))
pushbuf_dump(krec, krec_id++, fifo->channel);
+ if (fence_fd) {
+ struct drm_nouveau_gem_pushbuf2 req;
+ struct drm_nouveau_gem_fence fence;
+
+ memset(&req, 0, sizeof(req));
+ 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.flags = 0;
+
+ memset(&fence, 0, sizeof(fence));
+ fence.flags = NOUVEAU_GEM_FENCE_EMIT | NOUVEAU_GEM_FENCE_FD;
+
+ if (*fence_fd >= 0) {
+ fence.flags |= NOUVEAU_GEM_FENCE_WAIT;
+ fence.handle = *fence_fd;
+ }
+
+ req.fences = (uint64_t)(unsigned long)&fence;
+ req.num_fences = 1;
+
+#ifndef SIMULATE
+ ret = drmCommandWriteRead(drm->fd, DRM_NOUVEAU_GEM_PUSHBUF2,
+ &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 *
+ nouveau_device(dev)->gart_limit_percent) / 100;
+#else
+ if (dbg_on(31))
+ ret = -EINVAL;
+#endif
+
+ if (!ret)
+ fence_out = fence.handle;
+ } else {
+ struct drm_nouveau_gem_pushbuf req;
+
+ 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;
+
#ifndef SIMULATE
- ret = drmCommandWriteRead(drm->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 *
- nouveau_device(dev)->gart_limit_percent) / 100;
+ ret = drmCommandWriteRead(drm->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 *
+ nouveau_device(dev)->gart_limit_percent) / 100;
#else
- if (dbg_on(31))
- ret = -EINVAL;
+ if (dbg_on(31))
+ ret = -EINVAL;
#endif
+ }
if (ret) {
err("kernel rejected pushbuf: %s\n", strerror(-ret));
@@ -384,11 +435,18 @@ pushbuf_submit(struct nouveau_pushbuf *push, struct nouveau_object *chan)
krec = krec->next;
}
+ if (!ret && fence_fd) {
+ if (*fence_fd >= 0)
+ close(*fence_fd);
+
+ *fence_fd = 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;
@@ -398,7 +456,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));
@@ -468,7 +526,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);
}
@@ -520,7 +578,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);
}
}
@@ -672,7 +730,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;
}
@@ -768,10 +826,17 @@ nouveau_pushbuf_refd(struct nouveau_pushbuf *push, struct nouveau_bo *bo)
}
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);
}
+
+int
+nouveau_pushbuf_kick(struct nouveau_pushbuf *pushbuf, struct nouveau_object *chan)
+{
+ return nouveau_pushbuf_kick_fence(pushbuf, chan, NULL);
+}