summaryrefslogtreecommitdiffstats
path: root/drivers/gpu/drm/i915/i915_gem_execbuffer.c
diff options
context:
space:
mode:
authorJason Ekstrand2017-08-15 16:57:33 +0200
committerChris Wilson2017-08-15 17:46:57 +0200
commitcf6e7bac6357f0ccca51fcb5eb325e724f6b4c95 (patch)
treeae47537858f7c9b10ae2a3f748c939ec85290216 /drivers/gpu/drm/i915/i915_gem_execbuffer.c
parentdrm/i915: Handle full s64 precision for wait-ioctl (diff)
downloadkernel-qcow2-linux-cf6e7bac6357f0ccca51fcb5eb325e724f6b4c95.tar.gz
kernel-qcow2-linux-cf6e7bac6357f0ccca51fcb5eb325e724f6b4c95.tar.xz
kernel-qcow2-linux-cf6e7bac6357f0ccca51fcb5eb325e724f6b4c95.zip
drm/i915: Add support for drm syncobjs
This commit adds support for waiting on or signaling DRM syncobjs as part of execbuf. It does so by hijacking the currently unused cliprects pointer to instead point to an array of i915_gem_exec_fence structs which containe a DRM syncobj and a flags parameter which specifies whether to wait on it or to signal it. This implementation theoretically allows for both flags to be set in which case it waits on the dma_fence that was in the syncobj and then immediately replaces it with the dma_fence from the current execbuf. v2: - Rebase on new syncobj API v3: - Pull everything out into helpers - Do all allocation in gem_execbuffer2 - Pack the flags in the bottom 2 bits of the drm_syncobj* v4: - Prevent a potential race on syncobj->fence Testcase: igt/gem_exec_fence/syncobj* Signed-off-by: Jason Ekstrand <jason@jlekstrand.net> Link: https://patchwork.freedesktop.org/patch/msgid/1499289202-25441-1-git-send-email-jason.ekstrand@intel.com Reviewed-by: Chris Wilson <chris@chris-wilson.co.uk> Signed-off-by: Chris Wilson <chris@chris-wilson.co.uk> Link: https://patchwork.freedesktop.org/patch/msgid/20170815145733.4562-1-chris@chris-wilson.co.uk
Diffstat (limited to 'drivers/gpu/drm/i915/i915_gem_execbuffer.c')
-rw-r--r--drivers/gpu/drm/i915/i915_gem_execbuffer.c152
1 files changed, 147 insertions, 5 deletions
diff --git a/drivers/gpu/drm/i915/i915_gem_execbuffer.c b/drivers/gpu/drm/i915/i915_gem_execbuffer.c
index 99520b2fac7c..8e8bc7aefd9c 100644
--- a/drivers/gpu/drm/i915/i915_gem_execbuffer.c
+++ b/drivers/gpu/drm/i915/i915_gem_execbuffer.c
@@ -32,6 +32,7 @@
#include <linux/uaccess.h>
#include <drm/drmP.h>
+#include <drm/drm_syncobj.h>
#include <drm/i915_drm.h>
#include "i915_drv.h"
@@ -1896,8 +1897,10 @@ static bool i915_gem_check_execbuffer(struct drm_i915_gem_execbuffer2 *exec)
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");
@@ -2128,11 +2131,131 @@ eb_select_engine(struct drm_i915_private *dev_priv,
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;
@@ -2318,6 +2441,12 @@ i915_gem_do_execbuffer(struct drm_device *dev,
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) {
@@ -2341,6 +2470,9 @@ err_request:
__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);
@@ -2442,7 +2574,7 @@ i915_gem_execbuffer(struct drm_device *dev, void *data,
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);
@@ -2474,6 +2606,7 @@ i915_gem_execbuffer2(struct drm_device *dev, void *data,
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) {
@@ -2500,7 +2633,15 @@ i915_gem_execbuffer2(struct drm_device *dev, void *data,
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
@@ -2530,6 +2671,7 @@ end_user:
}
args->flags &= ~__I915_EXEC_UNKNOWN_FLAGS;
+ put_fence_array(args, fences);
kvfree(exec2_list);
return err;
}