<div dir="ltr"><div class="gmail_quote"><div dir="ltr" class="gmail_attr"></div><div dir="ltr" class="gmail_attr">On Wed, Aug 21, 2019 at 8:12 AM Lionel Landwerlin <<a href="mailto:lionel.g.landwerlin@intel.com">lionel.g.landwerlin@intel.com</a>> wrote:<br></div><blockquote class="gmail_quote" style="margin:0px 0px 0px 0.8ex;border-left:1px solid rgb(204,204,204);padding-left:1ex">Introduces a new parameters to execbuf so that we can specify syncobj<br>
handles as well as timeline points.<br>
<br>
v2: Reuse i915_user_extension_fn<br>
<br>
v3: Check that the chained extension is only present once (Chris)<br>
<br>
v4: Check that dma_fence_chain_find_seqno returns a non NULL fence (Lionel)<br>
<br>
v5: Use BIT_ULL (Chris)<br>
<br>
v6: Fix issue with already signaled timeline points,<br>
    dma_fence_chain_find_seqno() setting fence to NULL (Chris)<br>
<br>
v7: Report ENOENT with invalid syncobj handle (Lionel)<br>
<br>
v8: Check for out of order timeline point insertion (Chris)<br>
<br>
v9: After explanations on<br>
    <a href="https://lists.freedesktop.org/archives/dri-devel/2019-August/229287.html" rel="noreferrer" target="_blank">https://lists.freedesktop.org/archives/dri-devel/2019-August/229287.html</a><br>
    drop the ordering check from v8 (Lionel)<br>
<br>
Signed-off-by: Lionel Landwerlin <<a href="mailto:lionel.g.landwerlin@intel.com" target="_blank">lionel.g.landwerlin@intel.com</a>><br>
---<br>
 .../gpu/drm/i915/gem/i915_gem_execbuffer.c    | 307 ++++++++++++++----<br>
 drivers/gpu/drm/i915/i915_drv.c               |   3 +-<br>
 drivers/gpu/drm/i915/i915_getparam.c          |   1 +<br>
 include/uapi/drm/i915_drm.h                   |  39 +++<br>
 4 files changed, 293 insertions(+), 57 deletions(-)<br>
<br>
diff --git a/drivers/gpu/drm/i915/gem/i915_gem_execbuffer.c b/drivers/gpu/drm/i915/gem/i915_gem_execbuffer.c<br>
index 8d1946556bc0..6d5a234f9f9b 100644<br>
--- a/drivers/gpu/drm/i915/gem/i915_gem_execbuffer.c<br>
+++ b/drivers/gpu/drm/i915/gem/i915_gem_execbuffer.c<br>
@@ -214,6 +214,13 @@ enum {<br>
  * the batchbuffer in trusted mode, otherwise the ioctl is rejected.<br>
  */<br>
<br>
+struct i915_eb_fences {<br>
+       struct drm_syncobj *syncobj; /* Use with ptr_mask_bits() */<br>
+       struct dma_fence *dma_fence;<br>
+       u64 value;<br>
+       struct dma_fence_chain *chain_fence;<br>
+};<br>
+<br>
 struct i915_execbuffer {<br>
        struct drm_i915_private *i915; /** i915 backpointer */<br>
        struct drm_file *file; /** per-file lookup tables and limits */<br>
@@ -275,6 +282,7 @@ struct i915_execbuffer {<br>
<br>
        struct {<br>
                u64 flags; /** Available extensions parameters */<br>
+               struct drm_i915_gem_execbuffer_ext_timeline_fences timeline_fences;<br>
        } extensions;<br>
 };<br>
<br>
@@ -2295,67 +2303,217 @@ eb_pin_engine(struct i915_execbuffer *eb,<br>
 }<br>
<br>
 static void<br>
-__free_fence_array(struct drm_syncobj **fences, unsigned int n)<br>
+__free_fence_array(struct i915_eb_fences *fences, unsigned int n)<br>
 {<br>
-       while (n--)<br>
-               drm_syncobj_put(ptr_mask_bits(fences[n], 2));<br>
+       while (n--) {<br>
+               drm_syncobj_put(ptr_mask_bits(fences[n].syncobj, 2));<br>
+               dma_fence_put(fences[n].dma_fence);<br>
+               kfree(fences[n].chain_fence);<br>
+       }<br>
        kvfree(fences);<br>
 }<br>
<br>
-static struct drm_syncobj **<br>
-get_fence_array(struct drm_i915_gem_execbuffer2 *args,<br>
-               struct drm_file *file)<br>
+static struct i915_eb_fences *<br>
+get_timeline_fence_array(struct i915_execbuffer *eb, int *out_n_fences)<br>
 {<br>
-       const unsigned long nfences = args->num_cliprects;<br>
+       struct drm_i915_gem_execbuffer_ext_timeline_fences *timeline_fences =<br>
+               &eb->extensions.timeline_fences;<br>
+       struct drm_i915_gem_exec_fence __user *user_fences;<br>
+       struct i915_eb_fences *fences;<br>
+       u64 __user *user_values;<br>
+       u64 num_fences, num_user_fences = timeline_fences->fence_count;<br>
+       unsigned long n;<br>
+       int err;<br>
+<br>
+       /* Check multiplication overflow for access_ok() and kvmalloc_array() */<br>
+       BUILD_BUG_ON(sizeof(size_t) > sizeof(unsigned long));<br>
+       if (num_user_fences > min_t(unsigned long,<br>
+                                   ULONG_MAX / sizeof(*user_fences),<br>
+                                   SIZE_MAX / sizeof(*fences)))<br>
+               return ERR_PTR(-EINVAL);<br>
+<br>
+       user_fences = u64_to_user_ptr(timeline_fences->handles_ptr);<br>
+       if (!access_ok(user_fences, num_user_fences * sizeof(*user_fences)))<br>
+               return ERR_PTR(-EFAULT);<br>
+<br>
+       user_values = u64_to_user_ptr(timeline_fences->values_ptr);<br>
+       if (!access_ok(user_values, num_user_fences * sizeof(*user_values)))<br>
+               return ERR_PTR(-EFAULT);<br>
+<br>
+       fences = kvmalloc_array(num_user_fences, sizeof(*fences),<br>
+                               __GFP_NOWARN | GFP_KERNEL);<br>
+       if (!fences)<br>
+               return ERR_PTR(-ENOMEM);<br>
+<br>
+       BUILD_BUG_ON(~(ARCH_KMALLOC_MINALIGN - 1) &<br>
+                    ~__I915_EXEC_FENCE_UNKNOWN_FLAGS);<br>
+<br>
+       for (n = 0, num_fences = 0; n < timeline_fences->fence_count; n++) {<br>
+               struct drm_i915_gem_exec_fence user_fence;<br>
+               struct drm_syncobj *syncobj;<br>
+               struct dma_fence *fence = NULL;<br>
+               u64 point;<br>
+<br>
+               if (__copy_from_user(&user_fence, user_fences++, sizeof(user_fence))) {<br>
+                       err = -EFAULT;<br>
+                       goto err;<br>
+               }<br>
+<br>
+               if (user_fence.flags & __I915_EXEC_FENCE_UNKNOWN_FLAGS) {<br>
+                       err = -EINVAL;<br>
+                       goto err;<br>
+               }<br>
+<br>
+               if (__get_user(point, user_values++)) {<br>
+                       err = -EFAULT;<br>
+                       goto err;<br>
+               }<br>
+<br>
+               syncobj = drm_syncobj_find(eb->file, user_fence.handle);<br>
+               if (!syncobj) {<br>
+                       DRM_DEBUG("Invalid syncobj handle provided\n");<br>
+                       err = -ENOENT;<br>
+                       goto err;<br>
+               }<br>
+<br>
+               if (user_fence.flags & I915_EXEC_FENCE_WAIT) {<br>
+                       fence = drm_syncobj_fence_get(syncobj);<br>
+                       if (!fence) {<br>
+                               DRM_DEBUG("Syncobj handle has no fence\n");<br>
+                               drm_syncobj_put(syncobj);<br>
+                               err = -EINVAL;<br>
+                               goto err;<br>
+                       }<br>
+<br>
+                       err = dma_fence_chain_find_seqno(&fence, point);<br>
+                       if (err) {<br>
+                               DRM_DEBUG("Syncobj handle missing requested point %llu\n", point);<br>
+                               drm_syncobj_put(syncobj);<br>
+                               goto err;<br>
+                       }<br>
+<br>
+                       /* A point might have been signaled already and<br>
+                        * garbage collected from the timeline. In this case<br>
+                        * just ignore the point and carry on.<br>
+                        */<br>
+                       if (!fence) {<br>
+                               drm_syncobj_put(syncobj);<br>
+                               continue;<br>
+                       }<br>
+               }<br>
+<br>
+               /*<br>
+                * For timeline syncobjs we need to preallocate chains for<br>
+                * later signaling.<br>
+                */<br>
+               if (point != 0 && user_fence.flags & I915_EXEC_FENCE_SIGNAL) {<br>
+                       /*<br>
+                        * Waiting and signaling the same point (when point !=<br>
+                        * 0) would break the timeline.<br>
+                        */<br>
+                       if (user_fence.flags & I915_EXEC_FENCE_WAIT) {<br>
+                               DRM_DEBUG("Tring to wait & signal the same timeline point.\n");<br>
+                               err = -EINVAL;<br>
+                               drm_syncobj_put(syncobj);<br>
+                               goto err;<br>
+                       }<br>
+<br>
+                       fences[num_fences].chain_fence =<br>
+                               kmalloc(sizeof(*fences[num_fences].chain_fence),<br>
+                                       GFP_KERNEL);<br>
+                       if (!fences[num_fences].chain_fence) {<br>
+                               drm_syncobj_put(syncobj);<br>
+                               err = -ENOMEM;<br>
+                               DRM_DEBUG("Unable to alloc chain_fence\n");<br>
+                               goto err;<br>
+                       }<br>
+               } else {<br>
+                       fences[num_fences].chain_fence = NULL;<br>
+               }<br>
+<br>
+               fences[num_fences].syncobj = ptr_pack_bits(syncobj, user_fence.flags, 2);<br>
+               fences[num_fences].dma_fence = fence;<br>
+               fences[num_fences].value = point;<br>
+               num_fences++;<br>
+       }<br>
+<br>
+       *out_n_fences = num_fences;<br>
+<br>
+       return fences;<br>
+<br>
+err:<br>
+       __free_fence_array(fences, num_fences);<br>
+       return ERR_PTR(err);<br>
+}<br>
+<br>
+static struct i915_eb_fences *<br>
+get_legacy_fence_array(struct i915_execbuffer *eb,<br>
+                      int *out_n_fences)<br>
+{<br>
+       struct drm_i915_gem_execbuffer2 *args = eb->args;<br>
        struct drm_i915_gem_exec_fence __user *user;<br>
-       struct drm_syncobj **fences;<br>
+       struct i915_eb_fences *fences;<br>
+       const u32 num_fences = args->num_cliprects;<br>
        unsigned long n;<br>
        int err;<br>
<br>
-       if (!(args->flags & I915_EXEC_FENCE_ARRAY))<br>
-               return NULL;<br>
+       *out_n_fences = num_fences;<br>
<br>
        /* Check multiplication overflow for access_ok() and kvmalloc_array() */<br>
        BUILD_BUG_ON(sizeof(size_t) > sizeof(unsigned long));<br>
-       if (nfences > min_t(unsigned long,<br>
-                           ULONG_MAX / sizeof(*user),<br>
-                           SIZE_MAX / sizeof(*fences)))<br>
+       if (*out_n_fences > min_t(unsigned long,<br>
+                                 ULONG_MAX / sizeof(*user),<br>
+                                 SIZE_MAX / sizeof(*fences)))<br>
                return ERR_PTR(-EINVAL);<br>
<br>
        user = u64_to_user_ptr(args->cliprects_ptr);<br>
-       if (!access_ok(user, nfences * sizeof(*user)))<br>
+       if (!access_ok(user, *out_n_fences * sizeof(*user)))<br>
                return ERR_PTR(-EFAULT);<br>
<br>
-       fences = kvmalloc_array(nfences, sizeof(*fences),<br>
+       fences = kvmalloc_array(*out_n_fences, sizeof(*fences),<br>
                                __GFP_NOWARN | GFP_KERNEL);<br>
        if (!fences)<br>
                return ERR_PTR(-ENOMEM);<br>
<br>
-       for (n = 0; n < nfences; n++) {<br>
-               struct drm_i915_gem_exec_fence fence;<br>
+       for (n = 0; n < *out_n_fences; n++) {<br>
+               struct drm_i915_gem_exec_fence user_fence;<br>
                struct drm_syncobj *syncobj;<br>
+               struct dma_fence *fence = NULL;<br>
<br>
-               if (__copy_from_user(&fence, user++, sizeof(fence))) {<br>
+               if (__copy_from_user(&user_fence, user++, sizeof(user_fence))) {<br>
                        err = -EFAULT;<br>
                        goto err;<br>
                }<br>
<br>
-               if (fence.flags & __I915_EXEC_FENCE_UNKNOWN_FLAGS) {<br>
+               if (user_fence.flags & __I915_EXEC_FENCE_UNKNOWN_FLAGS) {<br>
                        err = -EINVAL;<br>
                        goto err;<br>
                }<br>
<br>
-               syncobj = drm_syncobj_find(file, fence.handle);<br>
+               syncobj = drm_syncobj_find(eb->file, user_fence.handle);<br>
                if (!syncobj) {<br>
                        DRM_DEBUG("Invalid syncobj handle provided\n");<br>
                        err = -ENOENT;<br>
                        goto err;<br>
                }<br>
<br>
+               if (user_fence.flags & I915_EXEC_FENCE_WAIT) {<br>
+                       fence = drm_syncobj_fence_get(syncobj);<br>
+                       if (!fence) {<br>
+                               DRM_DEBUG("Syncobj handle has no fence\n");<br>
+                               drm_syncobj_put(syncobj);<br>
+                               err = -EINVAL;<br>
+                               goto err;<br>
+                       }<br>
+               }<br>
+<br>
                BUILD_BUG_ON(~(ARCH_KMALLOC_MINALIGN - 1) &<br>
                             ~__I915_EXEC_FENCE_UNKNOWN_FLAGS);<br>
<br>
-               fences[n] = ptr_pack_bits(syncobj, fence.flags, 2);<br>
+               fences[n].syncobj = ptr_pack_bits(syncobj, user_fence.flags, 2);<br>
+               fences[n].dma_fence = fence;<br>
+               fences[n].value = 0;<br>
+               fences[n].chain_fence = NULL;<br>
        }<br>
<br>
        return fences;<br>
@@ -2365,37 +2523,44 @@ get_fence_array(struct drm_i915_gem_execbuffer2 *args,<br>
        return ERR_PTR(err);<br>
 }<br>
<br>
+static struct i915_eb_fences *<br>
+get_fence_array(struct i915_execbuffer *eb, int *out_n_fences)<br>
+{<br>
+       if (eb->args->flags & I915_EXEC_FENCE_ARRAY)<br>
+               return get_legacy_fence_array(eb, out_n_fences);<br>
+<br>
+       if (eb->extensions.flags & BIT_ULL(DRM_I915_GEM_EXECBUFFER_EXT_TIMELINE_FENCES))<br>
+               return get_timeline_fence_array(eb, out_n_fences);<br>
+<br>
+       *out_n_fences = 0;<br>
+       return NULL;<br>
+}<br>
+<br>
 static void<br>
-put_fence_array(struct drm_i915_gem_execbuffer2 *args,<br>
-               struct drm_syncobj **fences)<br>
+put_fence_array(struct i915_eb_fences *fences, int nfences)<br>
 {<br>
        if (fences)<br>
-               __free_fence_array(fences, args->num_cliprects);<br>
+               __free_fence_array(fences, nfences);<br>
 }<br>
<br>
 static int<br>
 await_fence_array(struct i915_execbuffer *eb,<br>
-                 struct drm_syncobj **fences)<br>
+                 struct i915_eb_fences *fences,<br>
+                 int nfences)<br>
 {<br>
-       const unsigned int nfences = eb->args->num_cliprects;<br>
        unsigned int n;<br>
        int err;<br>
<br>
        for (n = 0; n < nfences; n++) {<br>
                struct drm_syncobj *syncobj;<br>
-               struct dma_fence *fence;<br>
                unsigned int flags;<br>
<br>
-               syncobj = ptr_unpack_bits(fences[n], &flags, 2);<br>
+               syncobj = ptr_unpack_bits(fences[n].syncobj, &flags, 2);<br>
                if (!(flags & I915_EXEC_FENCE_WAIT))<br>
                        continue;<br>
<br>
-               fence = drm_syncobj_fence_get(syncobj);<br>
-               if (!fence)<br>
-                       return -EINVAL;<br>
-<br>
-               err = i915_request_await_dma_fence(eb->request, fence);<br>
-               dma_fence_put(fence);<br>
+               err = i915_request_await_dma_fence(eb->request,<br>
+                                                  fences[n].dma_fence);<br>
                if (err < 0)<br>
                        return err;<br>
        }<br>
@@ -2405,9 +2570,9 @@ await_fence_array(struct i915_execbuffer *eb,<br>
<br>
 static void<br>
 signal_fence_array(struct i915_execbuffer *eb,<br>
-                  struct drm_syncobj **fences)<br>
+                  struct i915_eb_fences *fences,<br>
+                  int nfences)<br>
 {<br>
-       const unsigned int nfences = eb->args->num_cliprects;<br>
        struct dma_fence * const fence = &eb->request->fence;<br>
        unsigned int n;<br>
<br>
@@ -2415,15 +2580,46 @@ signal_fence_array(struct i915_execbuffer *eb,<br>
                struct drm_syncobj *syncobj;<br>
                unsigned int flags;<br>
<br>
-               syncobj = ptr_unpack_bits(fences[n], &flags, 2);<br>
+               syncobj = ptr_unpack_bits(fences[n].syncobj, &flags, 2);<br>
                if (!(flags & I915_EXEC_FENCE_SIGNAL))<br>
                        continue;<br>
<br>
-               drm_syncobj_replace_fence(syncobj, fence);<br>
+               if (fences[n].chain_fence) {<br>
+                       drm_syncobj_add_point(syncobj, fences[n].chain_fence,<br>
+                                             fence, fences[n].value);<br>
+                       /*<br>
+                        * The chain's ownership is transfered to the<br>
+                        * timeline.<br>
+                        */<br>
+                       fences[n].chain_fence = NULL;<br>
+               } else {<br>
+                       drm_syncobj_replace_fence(syncobj, fence);<br>
+               }<br>
        }<br>
 }<br>
<br>
+static int parse_timeline_fences(struct i915_user_extension __user *ext, void *data)<br>
+{<br>
+       struct i915_execbuffer *eb = data;<br>
+<br>
+       /* Timeline fences are incompatible with the fence array flag. */<br>
+       if (eb->args->flags & I915_EXEC_FENCE_ARRAY)<br>
+               return -EINVAL;<br>
+<br>
+       if (eb->extensions.flags & BIT_ULL(DRM_I915_GEM_EXECBUFFER_EXT_TIMELINE_FENCES))<br>
+               return -EINVAL;<br>
+<br>
+       if (copy_from_user(&eb->extensions.timeline_fences, ext,<br>
+                          sizeof(eb->extensions.timeline_fences)))<br>
+               return -EFAULT;<br>
+<br>
+       eb->extensions.flags |= BIT_ULL(DRM_I915_GEM_EXECBUFFER_EXT_TIMELINE_FENCES);<br>
+<br>
+       return 0;<br>
+}<br>
+<br>
 static const i915_user_extension_fn execbuf_extensions[] = {<br>
+        [DRM_I915_GEM_EXECBUFFER_EXT_TIMELINE_FENCES] = parse_timeline_fences,<br>
 };<br>
<br>
 static int<br>
@@ -2454,14 +2650,15 @@ static int<br>
 i915_gem_do_execbuffer(struct drm_device *dev,<br>
                       struct drm_file *file,<br>
                       struct drm_i915_gem_execbuffer2 *args,<br>
-                      struct drm_i915_gem_exec_object2 *exec,<br>
-                      struct drm_syncobj **fences)<br>
+                      struct drm_i915_gem_exec_object2 *exec)<br>
 {<br>
        struct i915_execbuffer eb;<br>
        struct dma_fence *in_fence = NULL;<br>
        struct dma_fence *exec_fence = NULL;<br>
        struct sync_file *out_fence = NULL;<br>
+       struct i915_eb_fences *fences = NULL;<br>
        int out_fence_fd = -1;<br>
+       int nfences = 0;<br>
        int err;<br>
<br>
        BUILD_BUG_ON(__EXEC_INTERNAL_FLAGS & ~__I915_EXEC_ILLEGAL_FLAGS);<br>
@@ -2500,10 +2697,16 @@ i915_gem_do_execbuffer(struct drm_device *dev,<br>
        if (err)<br>
                return err;<br>
<br>
+       fences = get_fence_array(&eb, &nfences);<br>
+       if (IS_ERR(fences))<br>
+               return PTR_ERR(fences);<br>
+<br>
        if (args->flags & I915_EXEC_FENCE_IN) {<br>
                in_fence = sync_file_get_fence(lower_32_bits(args->rsvd2));<br>
-               if (!in_fence)<br>
-                       return -EINVAL;<br>
+               if (!in_fence) {<br>
+                       err = -EINVAL;<br>
+                       goto err_fences;<br>
+               }<br>
        }<br>
<br>
        if (args->flags & I915_EXEC_FENCE_SUBMIT) {<br>
@@ -2648,7 +2851,7 @@ i915_gem_do_execbuffer(struct drm_device *dev,<br>
        }<br>
<br>
        if (fences) {<br>
-               err = await_fence_array(&eb, fences);<br>
+               err = await_fence_array(&eb, fences, nfences);<br>
                if (err)<br>
                        goto err_request;<br>
        }<br>
@@ -2679,7 +2882,7 @@ i915_gem_do_execbuffer(struct drm_device *dev,<br>
        i915_request_add(eb.request);<br>
<br>
        if (fences)<br>
-               signal_fence_array(&eb, fences);<br>
+               signal_fence_array(&eb, fences, nfences);<br>
<br>
        if (out_fence) {<br>
                if (err == 0) {<br>
@@ -2714,6 +2917,8 @@ i915_gem_do_execbuffer(struct drm_device *dev,<br>
        dma_fence_put(exec_fence);<br>
 err_in_fence:<br>
        dma_fence_put(in_fence);<br>
+err_fences:<br>
+       put_fence_array(fences, nfences);<br>
        return err;<br>
 }<br>
<br>
@@ -2807,7 +3012,7 @@ i915_gem_execbuffer_ioctl(struct drm_device *dev, void *data,<br>
                        exec2_list[i].flags = 0;<br>
        }<br>
<br>
-       err = i915_gem_do_execbuffer(dev, file, &exec2, exec2_list, NULL);<br>
+       err = i915_gem_do_execbuffer(dev, file, &exec2, exec2_list);<br>
        if (exec2.flags & __EXEC_HAS_RELOC) {<br>
                struct drm_i915_gem_exec_object __user *user_exec_list =<br>
                        u64_to_user_ptr(args->buffers_ptr);<br>
@@ -2838,7 +3043,6 @@ i915_gem_execbuffer2_ioctl(struct drm_device *dev, void *data,<br>
 {<br>
        struct drm_i915_gem_execbuffer2 *args = data;<br>
        struct drm_i915_gem_exec_object2 *exec2_list;<br>
-       struct drm_syncobj **fences = NULL;<br>
        const size_t count = args->buffer_count;<br>
        int err;<br>
<br>
@@ -2866,15 +3070,7 @@ i915_gem_execbuffer2_ioctl(struct drm_device *dev, void *data,<br>
                return -EFAULT;<br>
        }<br>
<br>
-       if (args->flags & I915_EXEC_FENCE_ARRAY) {<br>
-               fences = get_fence_array(args, file);<br>
-               if (IS_ERR(fences)) {<br>
-                       kvfree(exec2_list);<br>
-                       return PTR_ERR(fences);<br>
-               }<br>
-       }<br>
-<br>
-       err = i915_gem_do_execbuffer(dev, file, args, exec2_list, fences);<br>
+       err = i915_gem_do_execbuffer(dev, file, args, exec2_list);<br>
<br>
        /*<br>
         * Now that we have begun execution of the batchbuffer, we ignore<br>
@@ -2914,7 +3110,6 @@ end:;<br>
        }<br>
<br>
        args->flags &= ~__I915_EXEC_UNKNOWN_FLAGS;<br>
-       put_fence_array(args, fences);<br>
        kvfree(exec2_list);<br>
        return err;<br>
 }<br>
diff --git a/drivers/gpu/drm/i915/i915_drv.c b/drivers/gpu/drm/i915/i915_drv.c<br>
index b5b2a64753e6..4db24f06f323 100644<br>
--- a/drivers/gpu/drm/i915/i915_drv.c<br>
+++ b/drivers/gpu/drm/i915/i915_drv.c<br>
@@ -2851,7 +2851,8 @@ static struct drm_driver driver = {<br>
         */<br>
        .driver_features =<br>
            DRIVER_GEM |<br>
-           DRIVER_RENDER | DRIVER_MODESET | DRIVER_ATOMIC | DRIVER_SYNCOBJ,<br>
+           DRIVER_RENDER | DRIVER_MODESET | DRIVER_ATOMIC | DRIVER_SYNCOBJ |<br>
+           DRIVER_SYNCOBJ_TIMELINE,<br>
        .release = i915_driver_release,<br>
        .open = i915_driver_open,<br>
        .lastclose = i915_driver_lastclose,<br>
diff --git a/drivers/gpu/drm/i915/i915_getparam.c b/drivers/gpu/drm/i915/i915_getparam.c<br>
index 5d9101376a3d..da6faa84e5b8 100644<br>
--- a/drivers/gpu/drm/i915/i915_getparam.c<br>
+++ b/drivers/gpu/drm/i915/i915_getparam.c<br>
@@ -130,6 +130,7 @@ int i915_getparam_ioctl(struct drm_device *dev, void *data,<br>
        case I915_PARAM_HAS_EXEC_BATCH_FIRST:<br>
        case I915_PARAM_HAS_EXEC_FENCE_ARRAY:<br>
        case I915_PARAM_HAS_EXEC_SUBMIT_FENCE:<br>
+       case I915_PARAM_HAS_EXEC_TIMELINE_FENCES:<br>
                /* For the time being all of these are always true;<br>
                 * if some supported hardware does not have one of these<br>
                 * features this value needs to be provided from<br>
diff --git a/include/uapi/drm/i915_drm.h b/include/uapi/drm/i915_drm.h<br>
index 0a99c26730e1..e86691c18a56 100644<br>
--- a/include/uapi/drm/i915_drm.h<br>
+++ b/include/uapi/drm/i915_drm.h<br>
@@ -611,6 +611,13 @@ typedef struct drm_i915_irq_wait {<br>
  * See I915_EXEC_FENCE_OUT and I915_EXEC_FENCE_SUBMIT.<br>
  */<br>
 #define I915_PARAM_HAS_EXEC_SUBMIT_FENCE 53<br>
+<br>
+/* Query whether DRM_I915_GEM_EXECBUFFER2 supports supplying an array of<br>
+ * timeline syncobj through drm_i915_gem_execbuf_ext_timeline_fences. See<br>
+ * I915_EXEC_USE_EXTENSIONS.<br>
+ */<br>
+#define I915_PARAM_HAS_EXEC_TIMELINE_FENCES 54<br>
+<br>
 /* Must be kept compact -- no holes and well documented */<br>
<br>
 typedef struct drm_i915_getparam {<br>
@@ -1008,9 +1015,41 @@ struct drm_i915_gem_exec_fence {<br>
 };<br>
<br>
 enum drm_i915_gem_execbuffer_ext {<br>
+       /**<br>
+        * See drm_i915_gem_execbuf_ext_timeline_fences.<br>
+        */<br>
+       DRM_I915_GEM_EXECBUFFER_EXT_TIMELINE_FENCES = 0,<br></blockquote><div><br></div><div>Just pointed out a "bug" in the userspace code where it memset the drm_i915_gem_execbuffer_ext_timeline_fences struct to 0 and never set the ext name.  It worked because TIMELINE_FENCES magically happens to be 0.  We should make 0 reserved in the extension API and -EINVAL if we ever see it.<br></div><div> </div><blockquote class="gmail_quote" style="margin:0px 0px 0px 0.8ex;border-left:1px solid rgb(204,204,204);padding-left:1ex">
+<br>
        DRM_I915_GEM_EXECBUFFER_EXT_MAX /* non-ABI */<br>
 };<br>
<br>
+/**<br>
+ * This structure describes an array of drm_syncobj and associated points for<br>
+ * timeline variants of drm_syncobj. It is invalid to append this structure to<br>
+ * the execbuf if I915_EXEC_FENCE_ARRAY is set.<br>
+ */<br>
+struct drm_i915_gem_execbuffer_ext_timeline_fences {<br>
+       struct i915_user_extension base;<br>
+<br>
+       /**<br>
+        * Number of element in the handles_ptr & value_ptr arrays.<br>
+        */<br>
+       __u64 fence_count;<br>
+<br>
+       /**<br>
+        * Pointer to an array of struct drm_i915_gem_exec_fence of length<br>
+        * fence_count.<br>
+        */<br>
+       __u64 handles_ptr;<br>
+<br>
+       /**<br>
+        * Pointer to an array of u64 values of length fence_count. Values<br>
+        * must be 0 for a binary drm_syncobj. A Value of 0 for a timeline<br>
+        * drm_syncobj is invalid as it turns a drm_syncobj into a binary one.<br>
+        */<br>
+       __u64 values_ptr;<br>
+};<br>
+<br>
 struct drm_i915_gem_execbuffer2 {<br>
        /**<br>
         * List of gem_exec_object2 structs<br>
-- <br>
2.23.0<br>
<br>
_______________________________________________<br>
Intel-gfx mailing list<br>
<a href="mailto:Intel-gfx@lists.freedesktop.org" target="_blank">Intel-gfx@lists.freedesktop.org</a><br>
<a href="https://lists.freedesktop.org/mailman/listinfo/intel-gfx" rel="noreferrer" target="_blank">https://lists.freedesktop.org/mailman/listinfo/intel-gfx</a></blockquote></div></div>