From 248f843f0e36ca7875ad0a252af72d050f27b8c1 Mon Sep 17 00:00:00 2001 From: Thierry Reding Date: Mon, 2 Oct 2017 15:51:08 +0200 Subject: 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 Signed-off-by: Thierry Reding --- include/drm/nouveau_drm.h | 32 +++++++++++ nouveau/nouveau.h | 2 + nouveau/pushbuf.c | 133 ++++++++++++++++++++++++++++++++++------------ 3 files changed, 133 insertions(+), 34 deletions(-) diff --git a/include/drm/nouveau_drm.h b/include/drm/nouveau_drm.h index d42105c8..30a55172 100644 --- a/include/drm/nouveau_drm.h +++ b/include/drm/nouveau_drm.h @@ -179,6 +179,37 @@ struct drm_nouveau_gem_pushbuf { __u64 gart_available; }; +#define NOUVEAU_GEM_FENCE_WAIT (1 << 0) +#define NOUVEAU_GEM_FENCE_EMIT (1 << 1) +#define NOUVEAU_GEM_FENCE_FD (1 << 2) +#define NOUVEAU_GEM_FLAGS (NOUVEAU_GEM_FENCE_WAIT | \ + NOUVEAU_GEM_FENCE_EMIT | \ + NOUVEAU_GEM_FENCE_FD) + +struct drm_nouveau_gem_fence { + uint32_t handle; + uint32_t flags; +}; + +#define NOUVEAU_GEM_PUSHBUF_FLAGS 0 + +struct drm_nouveau_gem_pushbuf2 { + uint32_t channel; + uint32_t nr_buffers; + uint64_t buffers; + uint32_t nr_relocs; + uint32_t nr_push; + uint64_t relocs; + uint64_t push; + uint32_t suffix0; + uint32_t suffix1; + uint64_t vram_available; + uint64_t gart_available; + uint32_t flags; + uint32_t num_fences; + uint64_t fences; +}; + #define NOUVEAU_GEM_CPU_PREP_NOWAIT 0x00000001 #define NOUVEAU_GEM_CPU_PREP_NOBLOCK 0x00000002 #define NOUVEAU_GEM_CPU_PREP_WRITE 0x00000004 @@ -213,6 +244,7 @@ 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_PUSHBUF2 0x45 #if defined(__cplusplus) } 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 #include #include +#include #include #include @@ -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); +} -- cgit v1.2.3