#include <linux/uaccess.h>
 
 #include <drm/drmP.h>
+#include <drm/drm_syncobj.h>
 #include <drm/i915_drm.h>
 
 #include "i915_drv.h"
                return false;
 
        /* Kernel clipping was a DRI1 misfeature */
-       if (exec->num_cliprects || exec->cliprects_ptr)
-               return false;
+       if (!(exec->flags & I915_EXEC_FENCE_ARRAY)) {
+               if (exec->num_cliprects || exec->cliprects_ptr)
+                       return false;
+       }
 
        if (exec->DR4 == 0xffffffff) {
                DRM_DEBUG("UXA submitting garbage DR4, fixing up\n");
        return engine;
 }
 
+static void
+__free_fence_array(struct drm_syncobj **fences, unsigned int n)
+{
+       while (n--)
+               drm_syncobj_put(ptr_mask_bits(fences[n], 2));
+       kvfree(fences);
+}
+
+static struct drm_syncobj **
+get_fence_array(struct drm_i915_gem_execbuffer2 *args,
+               struct drm_file *file)
+{
+       const unsigned int nfences = args->num_cliprects;
+       struct drm_i915_gem_exec_fence __user *user;
+       struct drm_syncobj **fences;
+       unsigned int n;
+       int err;
+
+       if (!(args->flags & I915_EXEC_FENCE_ARRAY))
+               return NULL;
+
+       if (nfences > SIZE_MAX / sizeof(*fences))
+               return ERR_PTR(-EINVAL);
+
+       user = u64_to_user_ptr(args->cliprects_ptr);
+       if (!access_ok(VERIFY_READ, user, nfences * 2 * sizeof(u32)))
+               return ERR_PTR(-EFAULT);
+
+       fences = kvmalloc_array(args->num_cliprects, sizeof(*fences),
+                               __GFP_NOWARN | GFP_TEMPORARY);
+       if (!fences)
+               return ERR_PTR(-ENOMEM);
+
+       for (n = 0; n < nfences; n++) {
+               struct drm_i915_gem_exec_fence fence;
+               struct drm_syncobj *syncobj;
+
+               if (__copy_from_user(&fence, user++, sizeof(fence))) {
+                       err = -EFAULT;
+                       goto err;
+               }
+
+               syncobj = drm_syncobj_find(file, fence.handle);
+               if (!syncobj) {
+                       DRM_DEBUG("Invalid syncobj handle provided\n");
+                       err = -ENOENT;
+                       goto err;
+               }
+
+               fences[n] = ptr_pack_bits(syncobj, fence.flags, 2);
+       }
+
+       return fences;
+
+err:
+       __free_fence_array(fences, n);
+       return ERR_PTR(err);
+}
+
+static void
+put_fence_array(struct drm_i915_gem_execbuffer2 *args,
+               struct drm_syncobj **fences)
+{
+       if (fences)
+               __free_fence_array(fences, args->num_cliprects);
+}
+
+static int
+await_fence_array(struct i915_execbuffer *eb,
+                 struct drm_syncobj **fences)
+{
+       const unsigned int nfences = eb->args->num_cliprects;
+       unsigned int n;
+       int err;
+
+       for (n = 0; n < nfences; n++) {
+               struct drm_syncobj *syncobj;
+               struct dma_fence *fence;
+               unsigned int flags;
+
+               syncobj = ptr_unpack_bits(fences[n], &flags, 2);
+               if (!(flags & I915_EXEC_FENCE_WAIT))
+                       continue;
+
+               rcu_read_lock();
+               fence = dma_fence_get_rcu_safe(&syncobj->fence);
+               rcu_read_unlock();
+               if (!fence)
+                       return -EINVAL;
+
+               err = i915_gem_request_await_dma_fence(eb->request, fence);
+               dma_fence_put(fence);
+               if (err < 0)
+                       return err;
+       }
+
+       return 0;
+}
+
+static void
+signal_fence_array(struct i915_execbuffer *eb,
+                  struct drm_syncobj **fences)
+{
+       const unsigned int nfences = eb->args->num_cliprects;
+       struct dma_fence * const fence = &eb->request->fence;
+       unsigned int n;
+
+       for (n = 0; n < nfences; n++) {
+               struct drm_syncobj *syncobj;
+               unsigned int flags;
+
+               syncobj = ptr_unpack_bits(fences[n], &flags, 2);
+               if (!(flags & I915_EXEC_FENCE_SIGNAL))
+                       continue;
+
+               drm_syncobj_replace_fence(syncobj, fence);
+       }
+}
+
 static int
 i915_gem_do_execbuffer(struct drm_device *dev,
                       struct drm_file *file,
                       struct drm_i915_gem_execbuffer2 *args,
-                      struct drm_i915_gem_exec_object2 *exec)
+                      struct drm_i915_gem_exec_object2 *exec,
+                      struct drm_syncobj **fences)
 {
        struct i915_execbuffer eb;
        struct dma_fence *in_fence = NULL;
                        goto err_request;
        }
 
+       if (fences) {
+               err = await_fence_array(&eb, fences);
+               if (err)
+                       goto err_request;
+       }
+
        if (out_fence_fd != -1) {
                out_fence = sync_file_create(&eb.request->fence);
                if (!out_fence) {
        __i915_add_request(eb.request, err == 0);
        add_to_client(eb.request, file);
 
+       if (fences)
+               signal_fence_array(&eb, fences);
+
        if (out_fence) {
                if (err == 0) {
                        fd_install(out_fence_fd, out_fence->file);
                        exec2_list[i].flags = 0;
        }
 
-       err = i915_gem_do_execbuffer(dev, file, &exec2, exec2_list);
+       err = i915_gem_do_execbuffer(dev, file, &exec2, exec2_list, NULL);
        if (exec2.flags & __EXEC_HAS_RELOC) {
                struct drm_i915_gem_exec_object __user *user_exec_list =
                        u64_to_user_ptr(args->buffers_ptr);
        const size_t sz = sizeof(struct drm_i915_gem_exec_object2);
        struct drm_i915_gem_execbuffer2 *args = data;
        struct drm_i915_gem_exec_object2 *exec2_list;
+       struct drm_syncobj **fences = NULL;
        int err;
 
        if (args->buffer_count < 1 || args->buffer_count > SIZE_MAX / sz - 1) {
                return -EFAULT;
        }
 
-       err = i915_gem_do_execbuffer(dev, file, args, exec2_list);
+       if (args->flags & I915_EXEC_FENCE_ARRAY) {
+               fences = get_fence_array(args, file);
+               if (IS_ERR(fences)) {
+                       kvfree(exec2_list);
+                       return PTR_ERR(fences);
+               }
+       }
+
+       err = i915_gem_do_execbuffer(dev, file, args, exec2_list, fences);
 
        /*
         * Now that we have begun execution of the batchbuffer, we ignore
        }
 
        args->flags &= ~__I915_EXEC_UNKNOWN_FLAGS;
+       put_fence_array(args, fences);
        kvfree(exec2_list);
        return err;
 }
 
  */
 #define I915_PARAM_HAS_EXEC_BATCH_FIRST         48
 
+/* Query whether DRM_I915_GEM_EXECBUFFER2 supports supplying an array of
+ * drm_i915_gem_exec_fence structures.  See I915_EXEC_FENCE_ARRAY.
+ */
+#define I915_PARAM_HAS_EXEC_FENCE_ARRAY  49
+
 typedef struct drm_i915_getparam {
        __s32 param;
        /*
        __u64 rsvd2;
 };
 
+struct drm_i915_gem_exec_fence {
+       /**
+        * User's handle for a drm_syncobj to wait on or signal.
+        */
+       __u32 handle;
+
+#define I915_EXEC_FENCE_WAIT            (1<<0)
+#define I915_EXEC_FENCE_SIGNAL          (1<<1)
+       __u32 flags;
+};
+
 struct drm_i915_gem_execbuffer2 {
        /**
         * List of gem_exec_object2 structs
        __u32 DR1;
        __u32 DR4;
        __u32 num_cliprects;
-       /** This is a struct drm_clip_rect *cliprects */
+       /**
+        * This is a struct drm_clip_rect *cliprects if I915_EXEC_FENCE_ARRAY
+        * is not set.  If I915_EXEC_FENCE_ARRAY is set, then this is a
+        * struct drm_i915_gem_exec_fence *fences.
+        */
        __u64 cliprects_ptr;
 #define I915_EXEC_RING_MASK              (7<<0)
 #define I915_EXEC_DEFAULT                (0<<0)
  * element).
  */
 #define I915_EXEC_BATCH_FIRST          (1<<18)
-#define __I915_EXEC_UNKNOWN_FLAGS (-(I915_EXEC_BATCH_FIRST<<1))
+
+/* Setting I915_FENCE_ARRAY implies that num_cliprects and cliprects_ptr
+ * define an array of i915_gem_exec_fence structures which specify a set of
+ * dma fences to wait upon or signal.
+ */
+#define I915_EXEC_FENCE_ARRAY   (1<<19)
+
+#define __I915_EXEC_UNKNOWN_FLAGS (-(I915_EXEC_FENCE_ARRAY<<1))
 
 #define I915_EXEC_CONTEXT_ID_MASK      (0xffffffff)
 #define i915_execbuffer2_set_context_id(eb2, context) \