diff --git a/include/drm/nouveau_drm.h b/include/drm/nouveau_drm.h
index cb077821c43f..e7f1a2aefd22 100644
--- a/include/drm/nouveau_drm.h
+++ b/include/drm/nouveau_drm.h
@@ -178,6 +178,28 @@ struct drm_nouveau_gem_pushbuf {
 	__u64 gart_available;
 };
 
+#define NOUVEAU_GEM_PUSHBUF_FENCE_WAIT (1 << 0)
+#define NOUVEAU_GEM_PUSHBUF_FENCE_EMIT (1 << 1)
+#define NOUVEAU_GEM_PUSHBUF_FLAGS (NOUVEAU_GEM_PUSHBUF_FENCE_WAIT | \
+                                   NOUVEAU_GEM_PUSHBUF_FENCE_EMIT)
+
+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;
+    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
@@ -212,6 +234,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 335ce77dca77..70d680700faf 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 035e3019f2cd..f13804db7534 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) {
@@ -307,7 +308,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)
 {
 	struct nouveau_pushbuf_priv *nvpb = nouveau_pushbuf(push);
 	struct nouveau_pushbuf_krec *krec = nvpb->list;
@@ -315,9 +317,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;
 
@@ -330,35 +332,81 @@ 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);
 
+		/* TODO If fence is requested, force kickoff. */
+		if (fence) {
+			struct drm_nouveau_gem_pushbuf2 req;
+
+			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;
+
+			if (*fence >= 0)
+				req.flags |= NOUVEAU_GEM_PUSHBUF_FENCE_WAIT;
+
+			req.flags |= NOUVEAU_GEM_PUSHBUF_FENCE_EMIT;
+
+			req.fence = *fence;
+			req.reserved = 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_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;
+			if (dbg_on(31))
+				ret = -EINVAL;
 #endif
 
+			if (!ret)
+				fence_out = req.fence;
+		} 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;
+#else
+			if (dbg_on(31))
+				ret = -EINVAL;
+#endif
+        }
+
 		if (ret) {
 			err("kernel rejected pushbuf: %s\n", strerror(-ret));
 			pushbuf_dump(krec, krec_id++, fifo->channel);
@@ -388,11 +436,18 @@ 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;
@@ -402,7 +457,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));
@@ -472,7 +527,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);
 		}
@@ -524,7 +579,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);
 		}
 	}
@@ -676,7 +731,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;
 	}
 
@@ -772,10 +827,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);
+}
