<div dir="ltr"><div class="gmail_extra"><div class="gmail_quote">On Wed, Jul 5, 2017 at 2:13 PM, Jason Ekstrand <span dir="ltr"><<a href="mailto:jason@jlekstrand.net" target="_blank">jason@jlekstrand.net</a>></span> wrote:<br><blockquote class="gmail_quote" style="margin:0 0 0 .8ex;border-left:1px #ccc solid;padding-left:1ex"><span class="">This commit adds support for waiting on or signaling DRM syncobjs as<br>
part of execbuf.  It does so by hijacking the currently unused cliprects<br>
pointer to instead point to an array of i915_gem_exec_fence structs<br>
which containe a DRM syncobj and a flags parameter which specifies<br>
whether to wait on it or to signal it.  This implementation<br>
theoretically allows for both flags to be set in which case it waits on<br>
the dma_fence that was in the syncobj and then immediately replaces it<br>
with the dma_fence from the current execbuf.<br>
<br>
</span>v2:<br>
 - Rebase on new syncobj API<br>
v3:<br>
 - Pull everything out into helpers<br>
 - Do all allocation in gem_execbuffer2<br>
 - Pack the flags in the bottom 2 bits of the drm_syncobj*<br>
---<br>
 drivers/gpu/drm/i915/i915_drv.<wbr>c            |   3 +-<br>
 drivers/gpu/drm/i915/i915_gem_<wbr>execbuffer.c | 141 ++++++++++++++++++++++++++++-<br>
 include/uapi/drm/i915_drm.h                |  30 +++++-<br>
 3 files changed, 166 insertions(+), 8 deletions(-)<br>
<span class=""><br>
diff --git a/drivers/gpu/drm/i915/i915_<wbr>drv.c b/drivers/gpu/drm/i915/i915_<wbr>drv.c<br>
index 9167a73..e6f7f49 100644<br>
--- a/drivers/gpu/drm/i915/i915_<wbr>drv.c<br>
+++ b/drivers/gpu/drm/i915/i915_<wbr>drv.c<br>
@@ -384,6 +384,7 @@ static int i915_getparam(struct drm_device *dev, void *data,<br>
        case I915_PARAM_HAS_EXEC_FENCE:<br>
        case I915_PARAM_HAS_EXEC_CAPTURE:<br>
        case I915_PARAM_HAS_EXEC_BATCH_<wbr>FIRST:<br>
+       case I915_PARAM_HAS_EXEC_FENCE_<wbr>ARRAY:<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>
@@ -2734,7 +2735,7 @@ static struct drm_driver driver = {<br>
         */<br>
        .driver_features =<br>
            DRIVER_HAVE_IRQ | DRIVER_IRQ_SHARED | DRIVER_GEM | DRIVER_PRIME |<br>
-           DRIVER_RENDER | DRIVER_MODESET | DRIVER_ATOMIC,<br>
+           DRIVER_RENDER | DRIVER_MODESET | DRIVER_ATOMIC | DRIVER_SYNCOBJ,<br>
        .release = i915_driver_release,<br>
        .open = i915_driver_open,<br>
        .lastclose = i915_driver_lastclose,<br>
diff --git a/drivers/gpu/drm/i915/i915_<wbr>gem_execbuffer.c b/drivers/gpu/drm/i915/i915_<wbr>gem_execbuffer.c<br>
</span>index 929f275..4f97649 100644<br>
<span class="">--- a/drivers/gpu/drm/i915/i915_<wbr>gem_execbuffer.c<br>
+++ b/drivers/gpu/drm/i915/i915_<wbr>gem_execbuffer.c<br>
@@ -33,6 +33,7 @@<br>
<br>
 #include <drm/drmP.h><br>
 #include <drm/i915_drm.h><br>
+#include <drm/drm_syncobj.h><br>
<br>
 #include "i915_drv.h"<br>
 #include "i915_gem_clflush.h"<br>
@@ -1882,8 +1883,10 @@ static bool i915_gem_check_execbuffer(<wbr>struct drm_i915_gem_execbuffer2 *exec)<br>
                return false;<br>
<br>
        /* Kernel clipping was a DRI1 misfeature */<br>
-       if (exec->num_cliprects || exec->cliprects_ptr)<br>
-               return false;<br>
+       if (!(exec->flags & I915_EXEC_FENCE_ARRAY)) {<br>
+               if (exec->num_cliprects || exec->cliprects_ptr)<br>
+                       return false;<br>
+       }<br>
<br>
        if (exec->DR4 == 0xffffffff) {<br>
                DRM_DEBUG("UXA submitting garbage DR4, fixing up\n");<br>
</span>@@ -2114,11 +2117,120 @@ eb_select_engine(struct drm_i915_private *dev_priv,<br>
        return engine;<br>
 }<br>
<br>
+static void __free_fence_array(struct drm_syncobj **fences, unsigned int n)<br>
+{<br>
+       while (n--)<br>
+               drm_syncobj_put(ptr_mask_bits(<wbr>fences[n], 2));<br>
+       kvfree(fences);<br>
+}<br>
+<br>
+static struct drm_syncobj **get_fence_array(struct drm_i915_gem_execbuffer2 *args,<br>
+                                           struct drm_file *file)<br>
+{<br>
+       const unsigned int nfences = args->num_cliprects;<br>
+       struct drm_i915_gem_exec_fence __user *user;<br>
+       struct drm_syncobj **fences;<br>
+       unsigned int n;<br>
+       int err;<br>
+<br>
+       if (!(args->flags & I915_EXEC_FENCE_ARRAY))<br>
+               return NULL;<br>
+<br>
+       if (nfences > SIZE_MAX / sizeof(*fences))<br>
+               return ERR_PTR(-EINVAL);<br>
+<br>
+       user = u64_to_user_ptr(args-><wbr>cliprects_ptr);<br>
+       if (!access_ok(VERIFY_READ, user, nfences * 2 * sizeof(u32)))<br>
+               return ERR_PTR(-EFAULT);<br>
+<br>
+       fences = kvmalloc_array(args->num_<wbr>cliprects, sizeof(*fences),<br>
+                               __GFP_NOWARN | GFP_TEMPORARY);<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>
+               struct drm_syncobj *syncobj;<br>
+<br>
+               if (__copy_from_user(&fence, user++, sizeof(fence))) {<br>
+                       err = -EFAULT;<br>
+                       goto err;<br>
+               }<br>
+<br>
+               syncobj = drm_syncobj_find(file, fence.handle);<br>
+               if (!syncobj) {<br>
<span class="">+                       DRM_DEBUG("Invalid syncobj handle provided\n");<br>
+                       err = -EINVAL;<br>
</span>+                       goto err;<br>
+               }<br>
+<br>
+               fences[n] = ptr_pack_bits(syncobj, fence.flags, 2);<br>
+       }<br>
+<br>
+       return fences;<br>
+<br>
+err:<br>
+       __free_fence_array(fences, n);<br>
+       return ERR_PTR(err);<br>
+}<br>
+<br>
+static void put_fence_array(struct drm_i915_gem_execbuffer2 *args,<br>
+                           struct drm_syncobj **fences)<br>
+{<br>
+       if (!fences)<br>
+               return;<br>
+       __free_fence_array(fences, args->num_cliprects);<br>
+}<br>
+<br>
+static int await_fence_array(struct i915_execbuffer *eb,<br>
+                            struct drm_syncobj **fences)<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>
+               unsigned int flags;<br>
+<br>
+               syncobj = ptr_unpack_bits(fences[n], &flags, 2);<br>
+               if (!(flags & I915_EXEC_FENCE_WAIT))<br>
+                       continue;<br>
+<br>
+               err = i915_gem_request_await_dma_<wbr>fence(eb->request,<br>
+                                                      syncobj->fence);<br></blockquote><div><br></div><div>Is there a race here?  What happens if some other process replaces the fence between the syncobj->fence lookup and gem_request_await_dma_fence taking its reference?<br></div><div> </div><blockquote class="gmail_quote" style="margin:0 0 0 .8ex;border-left:1px #ccc solid;padding-left:1ex">
+               if (err < 0)<br>
+                       return err;<br>
+       }<br>
+<br>
+       return 0;<br>
+}<br>
+<br>
+static void signal_fence_array(struct i915_execbuffer *eb,<br>
+                              struct drm_syncobj **fences)<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>
+       for (n = 0; n < nfences; n++) {<br>
+               struct drm_syncobj *syncobj;<br>
+               unsigned int flags;<br>
+<br>
+               syncobj = ptr_unpack_bits(fences[n], &flags, 2);<br>
+               if (!(flags & I915_EXEC_FENCE_SIGNAL))<br>
+                       continue;<br>
+<br>
+               drm_syncobj_replace_fence(<wbr>NULL, syncobj, fence);<br>
+       }<br>
+}<br>
+<br>
<span class=""> 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_i915_gem_exec_object2 *exec,<br>
</span>+                      struct drm_syncobj **fences)<br>
<span class=""> {<br>
        struct i915_execbuffer eb;<br>
        struct dma_fence *in_fence = NULL;<br>
</span>@@ -2304,6 +2416,12 @@ i915_gem_do_execbuffer(struct drm_device *dev,<br>
                        goto err_request;<br>
        }<br>
<br>
+       if (fences) {<br>
+               err = await_fence_array(&eb, fences);<br>
+               if (err < 0)<br>
<span class="">+                       goto err_request;<br>
+       }<br>
+<br>
</span><span class="">        if (out_fence_fd != -1) {<br>
                out_fence = sync_file_create(&eb.request-><wbr>fence);<br>
                if (!out_fence) {<br>
</span>@@ -2327,6 +2445,9 @@ i915_gem_do_execbuffer(struct drm_device *dev,<br>
        __i915_add_request(eb.request, err == 0);<br>
        add_to_client(eb.request, file);<br>
<br>
+       if (fences)<br>
+               signal_fence_array(&eb, fences);<br>
+<br>
        if (out_fence) {<br>
                if (err == 0) {<br>
                        fd_install(out_fence_fd, out_fence->file);<br>
@@ -2428,7 +2549,7 @@ i915_gem_execbuffer(struct drm_device *dev, void *data,<br>
<span class="">                        exec2_list[i].flags = 0;<br>
        }<br>
<br>
-       err = i915_gem_do_execbuffer(dev, file, &exec2, exec2_list);<br>
+       err = i915_gem_do_execbuffer(dev, file, &exec2, exec2_list, NULL);<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_<wbr>ptr);<br>
</span>@@ -2460,6 +2581,7 @@ i915_gem_execbuffer2(struct drm_device *dev, void *data,<br>
<span class="">        const size_t sz = sizeof(struct drm_i915_gem_exec_object2);<br>
        struct drm_i915_gem_execbuffer2 *args = data;<br>
        struct drm_i915_gem_exec_object2 *exec2_list;<br>
</span>+       struct drm_syncobj **fences = NULL;<br>
<span class="">        int err;<br>
<br>
        if (args->buffer_count < 1 || args->buffer_count > SIZE_MAX / sz - 1) {<br>
</span>@@ -2486,7 +2608,15 @@ i915_gem_execbuffer2(struct drm_device *dev, void *data,<br>
<span class="">                return -EFAULT;<br>
        }<br>
<br>
-       err = i915_gem_do_execbuffer(dev, file, args, exec2_list);<br>
+       if (args->flags & I915_EXEC_FENCE_ARRAY) {<br>
</span>+               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>
<span class=""><br>
        /*<br>
         * Now that we have begun execution of the batchbuffer, we ignore<br>
</span>@@ -2517,5 +2647,6 @@ i915_gem_execbuffer2(struct drm_device *dev, void *data,<br>
<span class=""><br>
        args->flags &= ~__I915_EXEC_UNKNOWN_FLAGS;<br>
        kvfree(exec2_list);<br>
</span>+       put_fence_array(args, fences);<br>
<div class="HOEnZb"><div class="h5">        return err;<br>
 }<br>
diff --git a/include/uapi/drm/i915_drm.h b/include/uapi/drm/i915_drm.h<br>
index 7ccbd6a..5b5f033 100644<br>
--- a/include/uapi/drm/i915_drm.h<br>
+++ b/include/uapi/drm/i915_drm.h<br>
@@ -431,6 +431,11 @@ typedef struct drm_i915_irq_wait {<br>
  */<br>
 #define I915_PARAM_HAS_EXEC_BATCH_<wbr>FIRST         48<br>
<br>
+/* Query whether DRM_I915_GEM_EXECBUFFER2 supports supplying an array of<br>
+ * drm_i915_gem_exec_fence structures.  See I915_EXEC_FENCE_ARRAY.<br>
+ */<br>
+#define I915_PARAM_HAS_EXEC_FENCE_<wbr>ARRAY  49<br>
+<br>
 typedef struct drm_i915_getparam {<br>
        __s32 param;<br>
        /*<br>
@@ -812,6 +817,17 @@ struct drm_i915_gem_exec_object2 {<br>
        __u64 rsvd2;<br>
 };<br>
<br>
+struct drm_i915_gem_exec_fence {<br>
+       /**<br>
+        * User's handle for a dma-fence to wait on or signal.<br>
+        */<br>
+       __u32 handle;<br>
+<br>
+#define I915_EXEC_FENCE_WAIT            (1<<0)<br>
+#define I915_EXEC_FENCE_SIGNAL          (1<<1)<br>
+       __u32 flags;<br>
+};<br>
+<br>
 struct drm_i915_gem_execbuffer2 {<br>
        /**<br>
         * List of gem_exec_object2 structs<br>
@@ -826,7 +842,10 @@ struct drm_i915_gem_execbuffer2 {<br>
        __u32 DR1;<br>
        __u32 DR4;<br>
        __u32 num_cliprects;<br>
-       /** This is a struct drm_clip_rect *cliprects */<br>
+       /** This is a struct drm_clip_rect *cliprects if I915_EXEC_FENCE_ARRAY<br>
+     * is not set.  If I915_EXEC_FENCE_ARRAY is set, then this is a<br>
+     * struct drm_i915_gem_exec_fence *fences.<br>
+     */<br>
        __u64 cliprects_ptr;<br>
 #define I915_EXEC_RING_MASK              (7<<0)<br>
 #define I915_EXEC_DEFAULT                (0<<0)<br>
@@ -927,7 +946,14 @@ struct drm_i915_gem_execbuffer2 {<br>
  * element).<br>
  */<br>
 #define I915_EXEC_BATCH_FIRST          (1<<18)<br>
-#define __I915_EXEC_UNKNOWN_FLAGS (-(I915_EXEC_BATCH_FIRST<<1))<br>
+<br>
+/* Setting I915_FENCE_ARRAY implies that num_cliprects and cliprects_ptr<br>
+ * define an array of i915_gem_exec_fence structures which specify a set of<br>
+ * dma fences to wait upon or signal.<br>
+ */<br>
+#define I915_EXEC_FENCE_ARRAY   (1<<19)<br>
+<br>
+#define __I915_EXEC_UNKNOWN_FLAGS (-(I915_EXEC_FENCE_ARRAY<<1))<br>
<br>
 #define I915_EXEC_CONTEXT_ID_MASK      (0xffffffff)<br>
 #define i915_execbuffer2_set_context_<wbr>id(eb2, context) \<br>
--<br>
2.5.0.400.gff86faf<br>
<br>
</div></div></blockquote></div><br></div></div>