drm/i915: introduce a mechanism to extend execbuf2
authorLionel Landwerlin <lionel.g.landwerlin@intel.com>
Tue, 4 Aug 2020 08:59:53 +0000 (11:59 +0300)
committerRodrigo Vivi <rodrigo.vivi@intel.com>
Mon, 17 Aug 2020 20:16:48 +0000 (16:16 -0400)
We're planning to use this for a couple of new feature where we need
to provide additional parameters to execbuf.

v2: Check for invalid flags in execbuffer2 (Lionel)

v3: Rename I915_EXEC_EXT -> I915_EXEC_USE_EXTENSIONS (Chris)

v4: Rebase
    Move array fence parsing in i915_gem_do_execbuffer()

Signed-off-by: Lionel Landwerlin <lionel.g.landwerlin@intel.com>
Reviewed-by: Daniel Vetter <daniel.vetter@ffwll.ch>
Reviewed-by: Chris Wilson <chris@chris-wilson.co.uk>
Link: https://patchwork.freedesktop.org/patch/msgid/20200804085954.350343-2-lionel.g.landwerlin@intel.com
Link: https://gitlab.freedesktop.org/mesa/mesa/-/merge_requests/2901
Signed-off-by: Rodrigo Vivi <rodrigo.vivi@intel.com>
drivers/gpu/drm/i915/gem/i915_gem_execbuffer.c
include/uapi/drm/i915_drm.h

index 6b4ec66..09d2f95 100644 (file)
@@ -26,6 +26,7 @@
 #include "i915_gem_ioctls.h"
 #include "i915_sw_fence_work.h"
 #include "i915_trace.h"
+#include "i915_user_extensions.h"
 
 struct eb_vma {
        struct i915_vma *vma;
@@ -281,6 +282,13 @@ struct i915_execbuffer {
        int lut_size;
        struct hlist_head *buckets; /** ht for relocation handles */
        struct eb_vma_array *array;
+
+       struct i915_eb_fence {
+               struct drm_syncobj *syncobj; /* Use with ptr_mask_bits() */
+       } *fences;
+       u32 n_fences;
+
+       u64 extension_flags; /** Available extensions parameters */
 };
 
 static inline bool eb_use_cmdparser(const struct i915_execbuffer *eb)
@@ -1622,7 +1630,8 @@ static int i915_gem_check_execbuffer(struct drm_i915_gem_execbuffer2 *exec)
                return -EINVAL;
 
        /* Kernel clipping was a DRI1 misfeature */
-       if (!(exec->flags & I915_EXEC_FENCE_ARRAY)) {
+       if (!(exec->flags & (I915_EXEC_FENCE_ARRAY |
+                            I915_EXEC_USE_EXTENSIONS))) {
                if (exec->num_cliprects || exec->cliprects_ptr)
                        return -EINVAL;
        }
@@ -2201,41 +2210,41 @@ eb_pin_engine(struct i915_execbuffer *eb,
 }
 
 static void
-__free_fence_array(struct drm_syncobj **fences, unsigned int n)
+__free_fence_array(struct i915_eb_fence *fences, unsigned int n)
 {
        while (n--)
-               drm_syncobj_put(ptr_mask_bits(fences[n], 2));
+               drm_syncobj_put(ptr_mask_bits(fences[n].syncobj, 2));
        kvfree(fences);
 }
 
-static struct drm_syncobj **
+static int
 get_fence_array(struct drm_i915_gem_execbuffer2 *args,
-               struct drm_file *file)
+               struct i915_execbuffer *eb)
 {
        const unsigned long nfences = args->num_cliprects;
        struct drm_i915_gem_exec_fence __user *user;
-       struct drm_syncobj **fences;
+       struct i915_eb_fence *fences;
        unsigned long n;
        int err;
 
        if (!(args->flags & I915_EXEC_FENCE_ARRAY))
-               return NULL;
+               return 0;
 
        /* Check multiplication overflow for access_ok() and kvmalloc_array() */
        BUILD_BUG_ON(sizeof(size_t) > sizeof(unsigned long));
        if (nfences > min_t(unsigned long,
                            ULONG_MAX / sizeof(*user),
                            SIZE_MAX / sizeof(*fences)))
-               return ERR_PTR(-EINVAL);
+               return -EINVAL;
 
        user = u64_to_user_ptr(args->cliprects_ptr);
        if (!access_ok(user, nfences * sizeof(*user)))
-               return ERR_PTR(-EFAULT);
+               return -EFAULT;
 
        fences = kvmalloc_array(nfences, sizeof(*fences),
                                __GFP_NOWARN | GFP_KERNEL);
        if (!fences)
-               return ERR_PTR(-ENOMEM);
+               return -ENOMEM;
 
        for (n = 0; n < nfences; n++) {
                struct drm_i915_gem_exec_fence fence;
@@ -2251,7 +2260,7 @@ get_fence_array(struct drm_i915_gem_execbuffer2 *args,
                        goto err;
                }
 
-               syncobj = drm_syncobj_find(file, fence.handle);
+               syncobj = drm_syncobj_find(eb->file, fence.handle);
                if (!syncobj) {
                        DRM_DEBUG("Invalid syncobj handle provided\n");
                        err = -ENOENT;
@@ -2261,38 +2270,31 @@ get_fence_array(struct drm_i915_gem_execbuffer2 *args,
                BUILD_BUG_ON(~(ARCH_KMALLOC_MINALIGN - 1) &
                             ~__I915_EXEC_FENCE_UNKNOWN_FLAGS);
 
-               fences[n] = ptr_pack_bits(syncobj, fence.flags, 2);
+               fences[n].syncobj = ptr_pack_bits(syncobj, fence.flags, 2);
        }
 
-       return fences;
+       eb->fences = fences;
+       eb->n_fences = nfences;
+
+       return 0;
 
 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);
+       return err;
 }
 
 static int
-await_fence_array(struct i915_execbuffer *eb,
-                 struct drm_syncobj **fences)
+await_fence_array(struct i915_execbuffer *eb)
 {
-       const unsigned int nfences = eb->args->num_cliprects;
        unsigned int n;
        int err;
 
-       for (n = 0; n < nfences; n++) {
+       for (n = 0; n < eb->n_fences; n++) {
                struct drm_syncobj *syncobj;
                struct dma_fence *fence;
                unsigned int flags;
 
-               syncobj = ptr_unpack_bits(fences[n], &flags, 2);
+               syncobj = ptr_unpack_bits(eb->fences[n].syncobj, &flags, 2);
                if (!(flags & I915_EXEC_FENCE_WAIT))
                        continue;
 
@@ -2310,18 +2312,16 @@ await_fence_array(struct i915_execbuffer *eb,
 }
 
 static void
-signal_fence_array(struct i915_execbuffer *eb,
-                  struct drm_syncobj **fences)
+signal_fence_array(struct i915_execbuffer *eb)
 {
-       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++) {
+       for (n = 0; n < eb->n_fences; n++) {
                struct drm_syncobj *syncobj;
                unsigned int flags;
 
-               syncobj = ptr_unpack_bits(fences[n], &flags, 2);
+               syncobj = ptr_unpack_bits(eb->fences[n].syncobj, &flags, 2);
                if (!(flags & I915_EXEC_FENCE_SIGNAL))
                        continue;
 
@@ -2370,12 +2370,38 @@ static void eb_request_add(struct i915_execbuffer *eb)
        mutex_unlock(&tl->mutex);
 }
 
+static const i915_user_extension_fn execbuf_extensions[] = {
+};
+
+static int
+parse_execbuf2_extensions(struct drm_i915_gem_execbuffer2 *args,
+                         struct i915_execbuffer *eb)
+{
+       eb->extension_flags = 0;
+
+       if (!(args->flags & I915_EXEC_USE_EXTENSIONS))
+               return 0;
+
+       /* The execbuf2 extension mechanism reuses cliprects_ptr. So we cannot
+        * have another flag also using it at the same time.
+        */
+       if (eb->args->flags & I915_EXEC_FENCE_ARRAY)
+               return -EINVAL;
+
+       if (args->num_cliprects != 0)
+               return -EINVAL;
+
+       return i915_user_extensions(u64_to_user_ptr(args->cliprects_ptr),
+                                   execbuf_extensions,
+                                   ARRAY_SIZE(execbuf_extensions),
+                                   eb);
+}
+
 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_syncobj **fences)
+                      struct drm_i915_gem_exec_object2 *exec)
 {
        struct drm_i915_private *i915 = to_i915(dev);
        struct i915_execbuffer eb;
@@ -2405,6 +2431,9 @@ i915_gem_do_execbuffer(struct drm_device *dev,
        eb.batch_len = args->batch_len;
        eb.trampoline = NULL;
 
+       eb.fences = NULL;
+       eb.n_fences = 0;
+
        eb.batch_flags = 0;
        if (args->flags & I915_EXEC_SECURE) {
                if (INTEL_GEN(i915) >= 11)
@@ -2441,10 +2470,18 @@ i915_gem_do_execbuffer(struct drm_device *dev,
                }
        }
 
-       err = eb_create(&eb);
+       err = parse_execbuf2_extensions(args, &eb);
        if (err)
                goto err_out_fence;
 
+       err = get_fence_array(args, &eb);
+       if (err)
+               goto err_arr_fence;
+
+       err = eb_create(&eb);
+       if (err)
+               goto err_arr_fence;
+
        GEM_BUG_ON(!eb.lut_size);
 
        err = eb_select_context(&eb);
@@ -2539,8 +2576,8 @@ i915_gem_do_execbuffer(struct drm_device *dev,
                        goto err_request;
        }
 
-       if (fences) {
-               err = await_fence_array(&eb, fences);
+       if (eb.n_fences) {
+               err = await_fence_array(&eb);
                if (err)
                        goto err_request;
        }
@@ -2571,8 +2608,8 @@ err_request:
        i915_request_get(eb.request);
        eb_request_add(&eb);
 
-       if (fences)
-               signal_fence_array(&eb, fences);
+       if (eb.n_fences)
+               signal_fence_array(&eb);
 
        if (out_fence) {
                if (err == 0) {
@@ -2600,6 +2637,8 @@ err_context:
        i915_gem_context_put(eb.gem_context);
 err_destroy:
        eb_destroy(&eb);
+err_arr_fence:
+       __free_fence_array(eb.fences, eb.n_fences);
 err_out_fence:
        if (out_fence_fd != -1)
                put_unused_fd(out_fence_fd);
@@ -2699,7 +2738,7 @@ i915_gem_execbuffer_ioctl(struct drm_device *dev, void *data,
                        exec2_list[i].flags = 0;
        }
 
-       err = i915_gem_do_execbuffer(dev, file, &exec2, exec2_list, NULL);
+       err = i915_gem_do_execbuffer(dev, file, &exec2, exec2_list);
        if (exec2.flags & __EXEC_HAS_RELOC) {
                struct drm_i915_gem_exec_object __user *user_exec_list =
                        u64_to_user_ptr(args->buffers_ptr);
@@ -2731,7 +2770,6 @@ i915_gem_execbuffer2_ioctl(struct drm_device *dev, void *data,
        struct drm_i915_private *i915 = to_i915(dev);
        struct drm_i915_gem_execbuffer2 *args = data;
        struct drm_i915_gem_exec_object2 *exec2_list;
-       struct drm_syncobj **fences = NULL;
        const size_t count = args->buffer_count;
        int err;
 
@@ -2759,15 +2797,7 @@ i915_gem_execbuffer2_ioctl(struct drm_device *dev, void *data,
                return -EFAULT;
        }
 
-       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);
+       err = i915_gem_do_execbuffer(dev, file, args, exec2_list);
 
        /*
         * Now that we have begun execution of the batchbuffer, we ignore
@@ -2808,7 +2838,6 @@ end:;
        }
 
        args->flags &= ~__I915_EXEC_UNKNOWN_FLAGS;
-       put_fence_array(args, fences);
        kvfree(exec2_list);
        return err;
 }
index 0054606..dcada8c 100644 (file)
@@ -1046,6 +1046,10 @@ struct drm_i915_gem_exec_fence {
        __u32 flags;
 };
 
+enum drm_i915_gem_execbuffer_ext {
+       DRM_I915_GEM_EXECBUFFER_EXT_MAX /* non-ABI */
+};
+
 struct drm_i915_gem_execbuffer2 {
        /**
         * List of gem_exec_object2 structs
@@ -1062,8 +1066,14 @@ struct drm_i915_gem_execbuffer2 {
        __u32 num_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.
+        * & I915_EXEC_USE_EXTENSIONS are not set.
+        *
+        * If I915_EXEC_FENCE_ARRAY is set, then this is a pointer to an array
+        * of struct drm_i915_gem_exec_fence and num_cliprects is the length
+        * of the array.
+        *
+        * If I915_EXEC_USE_EXTENSIONS is set, then this is a pointer to a
+        * single struct i915_user_extension and num_cliprects is 0.
         */
        __u64 cliprects_ptr;
 #define I915_EXEC_RING_MASK              (0x3f)
@@ -1181,7 +1191,16 @@ struct drm_i915_gem_execbuffer2 {
  */
 #define I915_EXEC_FENCE_SUBMIT         (1 << 20)
 
-#define __I915_EXEC_UNKNOWN_FLAGS (-(I915_EXEC_FENCE_SUBMIT << 1))
+/*
+ * Setting I915_EXEC_USE_EXTENSIONS implies that
+ * drm_i915_gem_execbuffer2.cliprects_ptr is treated as a pointer to an linked
+ * list of i915_user_extension. Each i915_user_extension node is the base of a
+ * larger structure. The list of supported structures are listed in the
+ * drm_i915_gem_execbuffer_ext enum.
+ */
+#define I915_EXEC_USE_EXTENSIONS       (1 << 21)
+
+#define __I915_EXEC_UNKNOWN_FLAGS (-(I915_EXEC_USE_EXTENSIONS << 1))
 
 #define I915_EXEC_CONTEXT_ID_MASK      (0xffffffff)
 #define i915_execbuffer2_set_context_id(eb2, context) \