drm/i915: introduce a mechanism to extend execbuf2
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>
This commit is contained in:
parent
3f8210fd22
commit
cda9edd024
2 changed files with 102 additions and 54 deletions
|
@ -26,6 +26,7 @@
|
||||||
#include "i915_gem_ioctls.h"
|
#include "i915_gem_ioctls.h"
|
||||||
#include "i915_sw_fence_work.h"
|
#include "i915_sw_fence_work.h"
|
||||||
#include "i915_trace.h"
|
#include "i915_trace.h"
|
||||||
|
#include "i915_user_extensions.h"
|
||||||
|
|
||||||
struct eb_vma {
|
struct eb_vma {
|
||||||
struct i915_vma *vma;
|
struct i915_vma *vma;
|
||||||
|
@ -281,6 +282,13 @@ struct i915_execbuffer {
|
||||||
int lut_size;
|
int lut_size;
|
||||||
struct hlist_head *buckets; /** ht for relocation handles */
|
struct hlist_head *buckets; /** ht for relocation handles */
|
||||||
struct eb_vma_array *array;
|
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)
|
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;
|
return -EINVAL;
|
||||||
|
|
||||||
/* Kernel clipping was a DRI1 misfeature */
|
/* 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)
|
if (exec->num_cliprects || exec->cliprects_ptr)
|
||||||
return -EINVAL;
|
return -EINVAL;
|
||||||
}
|
}
|
||||||
|
@ -2201,41 +2210,41 @@ eb_pin_engine(struct i915_execbuffer *eb,
|
||||||
}
|
}
|
||||||
|
|
||||||
static void
|
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--)
|
while (n--)
|
||||||
drm_syncobj_put(ptr_mask_bits(fences[n], 2));
|
drm_syncobj_put(ptr_mask_bits(fences[n].syncobj, 2));
|
||||||
kvfree(fences);
|
kvfree(fences);
|
||||||
}
|
}
|
||||||
|
|
||||||
static struct drm_syncobj **
|
static int
|
||||||
get_fence_array(struct drm_i915_gem_execbuffer2 *args,
|
get_fence_array(struct drm_i915_gem_execbuffer2 *args,
|
||||||
struct drm_file *file)
|
struct i915_execbuffer *eb)
|
||||||
{
|
{
|
||||||
const unsigned long nfences = args->num_cliprects;
|
const unsigned long nfences = args->num_cliprects;
|
||||||
struct drm_i915_gem_exec_fence __user *user;
|
struct drm_i915_gem_exec_fence __user *user;
|
||||||
struct drm_syncobj **fences;
|
struct i915_eb_fence *fences;
|
||||||
unsigned long n;
|
unsigned long n;
|
||||||
int err;
|
int err;
|
||||||
|
|
||||||
if (!(args->flags & I915_EXEC_FENCE_ARRAY))
|
if (!(args->flags & I915_EXEC_FENCE_ARRAY))
|
||||||
return NULL;
|
return 0;
|
||||||
|
|
||||||
/* Check multiplication overflow for access_ok() and kvmalloc_array() */
|
/* Check multiplication overflow for access_ok() and kvmalloc_array() */
|
||||||
BUILD_BUG_ON(sizeof(size_t) > sizeof(unsigned long));
|
BUILD_BUG_ON(sizeof(size_t) > sizeof(unsigned long));
|
||||||
if (nfences > min_t(unsigned long,
|
if (nfences > min_t(unsigned long,
|
||||||
ULONG_MAX / sizeof(*user),
|
ULONG_MAX / sizeof(*user),
|
||||||
SIZE_MAX / sizeof(*fences)))
|
SIZE_MAX / sizeof(*fences)))
|
||||||
return ERR_PTR(-EINVAL);
|
return -EINVAL;
|
||||||
|
|
||||||
user = u64_to_user_ptr(args->cliprects_ptr);
|
user = u64_to_user_ptr(args->cliprects_ptr);
|
||||||
if (!access_ok(user, nfences * sizeof(*user)))
|
if (!access_ok(user, nfences * sizeof(*user)))
|
||||||
return ERR_PTR(-EFAULT);
|
return -EFAULT;
|
||||||
|
|
||||||
fences = kvmalloc_array(nfences, sizeof(*fences),
|
fences = kvmalloc_array(nfences, sizeof(*fences),
|
||||||
__GFP_NOWARN | GFP_KERNEL);
|
__GFP_NOWARN | GFP_KERNEL);
|
||||||
if (!fences)
|
if (!fences)
|
||||||
return ERR_PTR(-ENOMEM);
|
return -ENOMEM;
|
||||||
|
|
||||||
for (n = 0; n < nfences; n++) {
|
for (n = 0; n < nfences; n++) {
|
||||||
struct drm_i915_gem_exec_fence fence;
|
struct drm_i915_gem_exec_fence fence;
|
||||||
|
@ -2251,7 +2260,7 @@ get_fence_array(struct drm_i915_gem_execbuffer2 *args,
|
||||||
goto err;
|
goto err;
|
||||||
}
|
}
|
||||||
|
|
||||||
syncobj = drm_syncobj_find(file, fence.handle);
|
syncobj = drm_syncobj_find(eb->file, fence.handle);
|
||||||
if (!syncobj) {
|
if (!syncobj) {
|
||||||
DRM_DEBUG("Invalid syncobj handle provided\n");
|
DRM_DEBUG("Invalid syncobj handle provided\n");
|
||||||
err = -ENOENT;
|
err = -ENOENT;
|
||||||
|
@ -2261,38 +2270,31 @@ get_fence_array(struct drm_i915_gem_execbuffer2 *args,
|
||||||
BUILD_BUG_ON(~(ARCH_KMALLOC_MINALIGN - 1) &
|
BUILD_BUG_ON(~(ARCH_KMALLOC_MINALIGN - 1) &
|
||||||
~__I915_EXEC_FENCE_UNKNOWN_FLAGS);
|
~__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:
|
err:
|
||||||
__free_fence_array(fences, n);
|
__free_fence_array(fences, n);
|
||||||
return ERR_PTR(err);
|
return 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
|
static int
|
||||||
await_fence_array(struct i915_execbuffer *eb,
|
await_fence_array(struct i915_execbuffer *eb)
|
||||||
struct drm_syncobj **fences)
|
|
||||||
{
|
{
|
||||||
const unsigned int nfences = eb->args->num_cliprects;
|
|
||||||
unsigned int n;
|
unsigned int n;
|
||||||
int err;
|
int err;
|
||||||
|
|
||||||
for (n = 0; n < nfences; n++) {
|
for (n = 0; n < eb->n_fences; n++) {
|
||||||
struct drm_syncobj *syncobj;
|
struct drm_syncobj *syncobj;
|
||||||
struct dma_fence *fence;
|
struct dma_fence *fence;
|
||||||
unsigned int flags;
|
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))
|
if (!(flags & I915_EXEC_FENCE_WAIT))
|
||||||
continue;
|
continue;
|
||||||
|
|
||||||
|
@ -2310,18 +2312,16 @@ await_fence_array(struct i915_execbuffer *eb,
|
||||||
}
|
}
|
||||||
|
|
||||||
static void
|
static void
|
||||||
signal_fence_array(struct i915_execbuffer *eb,
|
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;
|
struct dma_fence * const fence = &eb->request->fence;
|
||||||
unsigned int n;
|
unsigned int n;
|
||||||
|
|
||||||
for (n = 0; n < nfences; n++) {
|
for (n = 0; n < eb->n_fences; n++) {
|
||||||
struct drm_syncobj *syncobj;
|
struct drm_syncobj *syncobj;
|
||||||
unsigned int flags;
|
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))
|
if (!(flags & I915_EXEC_FENCE_SIGNAL))
|
||||||
continue;
|
continue;
|
||||||
|
|
||||||
|
@ -2370,12 +2370,38 @@ static void eb_request_add(struct i915_execbuffer *eb)
|
||||||
mutex_unlock(&tl->mutex);
|
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
|
static int
|
||||||
i915_gem_do_execbuffer(struct drm_device *dev,
|
i915_gem_do_execbuffer(struct drm_device *dev,
|
||||||
struct drm_file *file,
|
struct drm_file *file,
|
||||||
struct drm_i915_gem_execbuffer2 *args,
|
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 drm_i915_private *i915 = to_i915(dev);
|
struct drm_i915_private *i915 = to_i915(dev);
|
||||||
struct i915_execbuffer eb;
|
struct i915_execbuffer eb;
|
||||||
|
@ -2405,6 +2431,9 @@ i915_gem_do_execbuffer(struct drm_device *dev,
|
||||||
eb.batch_len = args->batch_len;
|
eb.batch_len = args->batch_len;
|
||||||
eb.trampoline = NULL;
|
eb.trampoline = NULL;
|
||||||
|
|
||||||
|
eb.fences = NULL;
|
||||||
|
eb.n_fences = 0;
|
||||||
|
|
||||||
eb.batch_flags = 0;
|
eb.batch_flags = 0;
|
||||||
if (args->flags & I915_EXEC_SECURE) {
|
if (args->flags & I915_EXEC_SECURE) {
|
||||||
if (INTEL_GEN(i915) >= 11)
|
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)
|
if (err)
|
||||||
goto err_out_fence;
|
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);
|
GEM_BUG_ON(!eb.lut_size);
|
||||||
|
|
||||||
err = eb_select_context(&eb);
|
err = eb_select_context(&eb);
|
||||||
|
@ -2539,8 +2576,8 @@ i915_gem_do_execbuffer(struct drm_device *dev,
|
||||||
goto err_request;
|
goto err_request;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (fences) {
|
if (eb.n_fences) {
|
||||||
err = await_fence_array(&eb, fences);
|
err = await_fence_array(&eb);
|
||||||
if (err)
|
if (err)
|
||||||
goto err_request;
|
goto err_request;
|
||||||
}
|
}
|
||||||
|
@ -2571,8 +2608,8 @@ err_request:
|
||||||
i915_request_get(eb.request);
|
i915_request_get(eb.request);
|
||||||
eb_request_add(&eb);
|
eb_request_add(&eb);
|
||||||
|
|
||||||
if (fences)
|
if (eb.n_fences)
|
||||||
signal_fence_array(&eb, fences);
|
signal_fence_array(&eb);
|
||||||
|
|
||||||
if (out_fence) {
|
if (out_fence) {
|
||||||
if (err == 0) {
|
if (err == 0) {
|
||||||
|
@ -2600,6 +2637,8 @@ err_context:
|
||||||
i915_gem_context_put(eb.gem_context);
|
i915_gem_context_put(eb.gem_context);
|
||||||
err_destroy:
|
err_destroy:
|
||||||
eb_destroy(&eb);
|
eb_destroy(&eb);
|
||||||
|
err_arr_fence:
|
||||||
|
__free_fence_array(eb.fences, eb.n_fences);
|
||||||
err_out_fence:
|
err_out_fence:
|
||||||
if (out_fence_fd != -1)
|
if (out_fence_fd != -1)
|
||||||
put_unused_fd(out_fence_fd);
|
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;
|
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) {
|
if (exec2.flags & __EXEC_HAS_RELOC) {
|
||||||
struct drm_i915_gem_exec_object __user *user_exec_list =
|
struct drm_i915_gem_exec_object __user *user_exec_list =
|
||||||
u64_to_user_ptr(args->buffers_ptr);
|
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_private *i915 = to_i915(dev);
|
||||||
struct drm_i915_gem_execbuffer2 *args = data;
|
struct drm_i915_gem_execbuffer2 *args = data;
|
||||||
struct drm_i915_gem_exec_object2 *exec2_list;
|
struct drm_i915_gem_exec_object2 *exec2_list;
|
||||||
struct drm_syncobj **fences = NULL;
|
|
||||||
const size_t count = args->buffer_count;
|
const size_t count = args->buffer_count;
|
||||||
int err;
|
int err;
|
||||||
|
|
||||||
|
@ -2759,15 +2797,7 @@ i915_gem_execbuffer2_ioctl(struct drm_device *dev, void *data,
|
||||||
return -EFAULT;
|
return -EFAULT;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (args->flags & I915_EXEC_FENCE_ARRAY) {
|
err = i915_gem_do_execbuffer(dev, file, args, exec2_list);
|
||||||
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
|
* Now that we have begun execution of the batchbuffer, we ignore
|
||||||
|
@ -2808,7 +2838,6 @@ end:;
|
||||||
}
|
}
|
||||||
|
|
||||||
args->flags &= ~__I915_EXEC_UNKNOWN_FLAGS;
|
args->flags &= ~__I915_EXEC_UNKNOWN_FLAGS;
|
||||||
put_fence_array(args, fences);
|
|
||||||
kvfree(exec2_list);
|
kvfree(exec2_list);
|
||||||
return err;
|
return err;
|
||||||
}
|
}
|
||||||
|
|
|
@ -1046,6 +1046,10 @@ struct drm_i915_gem_exec_fence {
|
||||||
__u32 flags;
|
__u32 flags;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
enum drm_i915_gem_execbuffer_ext {
|
||||||
|
DRM_I915_GEM_EXECBUFFER_EXT_MAX /* non-ABI */
|
||||||
|
};
|
||||||
|
|
||||||
struct drm_i915_gem_execbuffer2 {
|
struct drm_i915_gem_execbuffer2 {
|
||||||
/**
|
/**
|
||||||
* List of gem_exec_object2 structs
|
* List of gem_exec_object2 structs
|
||||||
|
@ -1062,8 +1066,14 @@ struct drm_i915_gem_execbuffer2 {
|
||||||
__u32 num_cliprects;
|
__u32 num_cliprects;
|
||||||
/**
|
/**
|
||||||
* This is a struct drm_clip_rect *cliprects if I915_EXEC_FENCE_ARRAY
|
* 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
|
* & I915_EXEC_USE_EXTENSIONS are not set.
|
||||||
* struct drm_i915_gem_exec_fence *fences.
|
*
|
||||||
|
* 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;
|
__u64 cliprects_ptr;
|
||||||
#define I915_EXEC_RING_MASK (0x3f)
|
#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_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_EXEC_CONTEXT_ID_MASK (0xffffffff)
|
||||||
#define i915_execbuffer2_set_context_id(eb2, context) \
|
#define i915_execbuffer2_set_context_id(eb2, context) \
|
||||||
|
|
Loading…
Add table
Reference in a new issue