mirror of https://github.com/torvalds/linux.git
drm/xe: Enforce correct user fence signaling order using
Prevent application hangs caused by out-of-order fence signaling when
user fences are attached. Use drm_syncobj (via dma-fence-chain) to
guarantee that each user fence signals in order, regardless of the
signaling order of the attached fences. Ensure user fence writebacks to
user space occur in the correct sequence.
v7:
- Skip drm_syncbj create of error (CI)
Fixes: dd08ebf6c3 ("drm/xe: Introduce a new DRM driver for Intel GPUs")
Signed-off-by: Matthew Brost <matthew.brost@intel.com>
Reviewed-by: Thomas Hellström <thomas.hellstrom@linux.intel.com>
Link: https://patch.msgid.link/20251031234050.3043507-2-matthew.brost@intel.com
This commit is contained in:
parent
a4ff26b7c8
commit
adda4e855a
|
|
@ -173,7 +173,8 @@ int xe_exec_ioctl(struct drm_device *dev, void *data, struct drm_file *file)
|
||||||
|
|
||||||
for (num_syncs = 0; num_syncs < args->num_syncs; num_syncs++) {
|
for (num_syncs = 0; num_syncs < args->num_syncs; num_syncs++) {
|
||||||
err = xe_sync_entry_parse(xe, xef, &syncs[num_syncs],
|
err = xe_sync_entry_parse(xe, xef, &syncs[num_syncs],
|
||||||
&syncs_user[num_syncs], SYNC_PARSE_FLAG_EXEC |
|
&syncs_user[num_syncs], NULL, 0,
|
||||||
|
SYNC_PARSE_FLAG_EXEC |
|
||||||
(xe_vm_in_lr_mode(vm) ?
|
(xe_vm_in_lr_mode(vm) ?
|
||||||
SYNC_PARSE_FLAG_LR_MODE : 0));
|
SYNC_PARSE_FLAG_LR_MODE : 0));
|
||||||
if (err)
|
if (err)
|
||||||
|
|
|
||||||
|
|
@ -10,6 +10,7 @@
|
||||||
#include <drm/drm_device.h>
|
#include <drm/drm_device.h>
|
||||||
#include <drm/drm_drv.h>
|
#include <drm/drm_drv.h>
|
||||||
#include <drm/drm_file.h>
|
#include <drm/drm_file.h>
|
||||||
|
#include <drm/drm_syncobj.h>
|
||||||
#include <uapi/drm/xe_drm.h>
|
#include <uapi/drm/xe_drm.h>
|
||||||
|
|
||||||
#include "xe_dep_scheduler.h"
|
#include "xe_dep_scheduler.h"
|
||||||
|
|
@ -368,6 +369,16 @@ struct xe_exec_queue *xe_exec_queue_create_bind(struct xe_device *xe,
|
||||||
}
|
}
|
||||||
xe_vm_put(migrate_vm);
|
xe_vm_put(migrate_vm);
|
||||||
|
|
||||||
|
if (!IS_ERR(q)) {
|
||||||
|
int err = drm_syncobj_create(&q->ufence_syncobj,
|
||||||
|
DRM_SYNCOBJ_CREATE_SIGNALED,
|
||||||
|
NULL);
|
||||||
|
if (err) {
|
||||||
|
xe_exec_queue_put(q);
|
||||||
|
return ERR_PTR(err);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
return q;
|
return q;
|
||||||
}
|
}
|
||||||
ALLOW_ERROR_INJECTION(xe_exec_queue_create_bind, ERRNO);
|
ALLOW_ERROR_INJECTION(xe_exec_queue_create_bind, ERRNO);
|
||||||
|
|
@ -379,6 +390,9 @@ void xe_exec_queue_destroy(struct kref *ref)
|
||||||
|
|
||||||
xe_assert(gt_to_xe(q->gt), atomic_read(&q->job_cnt) == 0);
|
xe_assert(gt_to_xe(q->gt), atomic_read(&q->job_cnt) == 0);
|
||||||
|
|
||||||
|
if (q->ufence_syncobj)
|
||||||
|
drm_syncobj_put(q->ufence_syncobj);
|
||||||
|
|
||||||
if (xe_exec_queue_uses_pxp(q))
|
if (xe_exec_queue_uses_pxp(q))
|
||||||
xe_pxp_exec_queue_remove(gt_to_xe(q->gt)->pxp, q);
|
xe_pxp_exec_queue_remove(gt_to_xe(q->gt)->pxp, q);
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -15,6 +15,7 @@
|
||||||
#include "xe_hw_fence_types.h"
|
#include "xe_hw_fence_types.h"
|
||||||
#include "xe_lrc_types.h"
|
#include "xe_lrc_types.h"
|
||||||
|
|
||||||
|
struct drm_syncobj;
|
||||||
struct xe_execlist_exec_queue;
|
struct xe_execlist_exec_queue;
|
||||||
struct xe_gt;
|
struct xe_gt;
|
||||||
struct xe_guc_exec_queue;
|
struct xe_guc_exec_queue;
|
||||||
|
|
@ -155,6 +156,12 @@ struct xe_exec_queue {
|
||||||
struct list_head link;
|
struct list_head link;
|
||||||
} pxp;
|
} pxp;
|
||||||
|
|
||||||
|
/** @ufence_syncobj: User fence syncobj */
|
||||||
|
struct drm_syncobj *ufence_syncobj;
|
||||||
|
|
||||||
|
/** @ufence_timeline_value: User fence timeline value */
|
||||||
|
u64 ufence_timeline_value;
|
||||||
|
|
||||||
/** @ops: submission backend exec queue operations */
|
/** @ops: submission backend exec queue operations */
|
||||||
const struct xe_exec_queue_ops *ops;
|
const struct xe_exec_queue_ops *ops;
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -10,6 +10,7 @@
|
||||||
|
|
||||||
#include <drm/drm_drv.h>
|
#include <drm/drm_drv.h>
|
||||||
#include <drm/drm_managed.h>
|
#include <drm/drm_managed.h>
|
||||||
|
#include <drm/drm_syncobj.h>
|
||||||
#include <uapi/drm/xe_drm.h>
|
#include <uapi/drm/xe_drm.h>
|
||||||
|
|
||||||
#include <generated/xe_wa_oob.h>
|
#include <generated/xe_wa_oob.h>
|
||||||
|
|
@ -1390,7 +1391,9 @@ static int xe_oa_user_extensions(struct xe_oa *oa, enum xe_oa_user_extn_from fro
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
static int xe_oa_parse_syncs(struct xe_oa *oa, struct xe_oa_open_param *param)
|
static int xe_oa_parse_syncs(struct xe_oa *oa,
|
||||||
|
struct xe_oa_stream *stream,
|
||||||
|
struct xe_oa_open_param *param)
|
||||||
{
|
{
|
||||||
int ret, num_syncs, num_ufence = 0;
|
int ret, num_syncs, num_ufence = 0;
|
||||||
|
|
||||||
|
|
@ -1410,7 +1413,9 @@ static int xe_oa_parse_syncs(struct xe_oa *oa, struct xe_oa_open_param *param)
|
||||||
|
|
||||||
for (num_syncs = 0; num_syncs < param->num_syncs; num_syncs++) {
|
for (num_syncs = 0; num_syncs < param->num_syncs; num_syncs++) {
|
||||||
ret = xe_sync_entry_parse(oa->xe, param->xef, ¶m->syncs[num_syncs],
|
ret = xe_sync_entry_parse(oa->xe, param->xef, ¶m->syncs[num_syncs],
|
||||||
¶m->syncs_user[num_syncs], 0);
|
¶m->syncs_user[num_syncs],
|
||||||
|
stream->ufence_syncobj,
|
||||||
|
++stream->ufence_timeline_value, 0);
|
||||||
if (ret)
|
if (ret)
|
||||||
goto err_syncs;
|
goto err_syncs;
|
||||||
|
|
||||||
|
|
@ -1540,7 +1545,7 @@ static long xe_oa_config_locked(struct xe_oa_stream *stream, u64 arg)
|
||||||
return -ENODEV;
|
return -ENODEV;
|
||||||
|
|
||||||
param.xef = stream->xef;
|
param.xef = stream->xef;
|
||||||
err = xe_oa_parse_syncs(stream->oa, ¶m);
|
err = xe_oa_parse_syncs(stream->oa, stream, ¶m);
|
||||||
if (err)
|
if (err)
|
||||||
goto err_config_put;
|
goto err_config_put;
|
||||||
|
|
||||||
|
|
@ -1636,6 +1641,7 @@ static void xe_oa_destroy_locked(struct xe_oa_stream *stream)
|
||||||
if (stream->exec_q)
|
if (stream->exec_q)
|
||||||
xe_exec_queue_put(stream->exec_q);
|
xe_exec_queue_put(stream->exec_q);
|
||||||
|
|
||||||
|
drm_syncobj_put(stream->ufence_syncobj);
|
||||||
kfree(stream);
|
kfree(stream);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -1827,6 +1833,7 @@ static int xe_oa_stream_open_ioctl_locked(struct xe_oa *oa,
|
||||||
struct xe_oa_open_param *param)
|
struct xe_oa_open_param *param)
|
||||||
{
|
{
|
||||||
struct xe_oa_stream *stream;
|
struct xe_oa_stream *stream;
|
||||||
|
struct drm_syncobj *ufence_syncobj;
|
||||||
int stream_fd;
|
int stream_fd;
|
||||||
int ret;
|
int ret;
|
||||||
|
|
||||||
|
|
@ -1837,17 +1844,31 @@ static int xe_oa_stream_open_ioctl_locked(struct xe_oa *oa,
|
||||||
goto exit;
|
goto exit;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
ret = drm_syncobj_create(&ufence_syncobj, DRM_SYNCOBJ_CREATE_SIGNALED,
|
||||||
|
NULL);
|
||||||
|
if (ret)
|
||||||
|
goto exit;
|
||||||
|
|
||||||
stream = kzalloc(sizeof(*stream), GFP_KERNEL);
|
stream = kzalloc(sizeof(*stream), GFP_KERNEL);
|
||||||
if (!stream) {
|
if (!stream) {
|
||||||
ret = -ENOMEM;
|
ret = -ENOMEM;
|
||||||
goto exit;
|
goto err_syncobj;
|
||||||
}
|
}
|
||||||
|
stream->ufence_syncobj = ufence_syncobj;
|
||||||
stream->oa = oa;
|
stream->oa = oa;
|
||||||
ret = xe_oa_stream_init(stream, param);
|
|
||||||
|
ret = xe_oa_parse_syncs(oa, stream, param);
|
||||||
if (ret)
|
if (ret)
|
||||||
goto err_free;
|
goto err_free;
|
||||||
|
|
||||||
|
ret = xe_oa_stream_init(stream, param);
|
||||||
|
if (ret) {
|
||||||
|
while (param->num_syncs--)
|
||||||
|
xe_sync_entry_cleanup(¶m->syncs[param->num_syncs]);
|
||||||
|
kfree(param->syncs);
|
||||||
|
goto err_free;
|
||||||
|
}
|
||||||
|
|
||||||
if (!param->disabled) {
|
if (!param->disabled) {
|
||||||
ret = xe_oa_enable_locked(stream);
|
ret = xe_oa_enable_locked(stream);
|
||||||
if (ret)
|
if (ret)
|
||||||
|
|
@ -1871,6 +1892,8 @@ static int xe_oa_stream_open_ioctl_locked(struct xe_oa *oa,
|
||||||
xe_oa_stream_destroy(stream);
|
xe_oa_stream_destroy(stream);
|
||||||
err_free:
|
err_free:
|
||||||
kfree(stream);
|
kfree(stream);
|
||||||
|
err_syncobj:
|
||||||
|
drm_syncobj_put(ufence_syncobj);
|
||||||
exit:
|
exit:
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
@ -2084,22 +2107,14 @@ int xe_oa_stream_open_ioctl(struct drm_device *dev, u64 data, struct drm_file *f
|
||||||
goto err_exec_q;
|
goto err_exec_q;
|
||||||
}
|
}
|
||||||
|
|
||||||
ret = xe_oa_parse_syncs(oa, ¶m);
|
|
||||||
if (ret)
|
|
||||||
goto err_exec_q;
|
|
||||||
|
|
||||||
mutex_lock(¶m.hwe->gt->oa.gt_lock);
|
mutex_lock(¶m.hwe->gt->oa.gt_lock);
|
||||||
ret = xe_oa_stream_open_ioctl_locked(oa, ¶m);
|
ret = xe_oa_stream_open_ioctl_locked(oa, ¶m);
|
||||||
mutex_unlock(¶m.hwe->gt->oa.gt_lock);
|
mutex_unlock(¶m.hwe->gt->oa.gt_lock);
|
||||||
if (ret < 0)
|
if (ret < 0)
|
||||||
goto err_sync_cleanup;
|
goto err_exec_q;
|
||||||
|
|
||||||
return ret;
|
return ret;
|
||||||
|
|
||||||
err_sync_cleanup:
|
|
||||||
while (param.num_syncs--)
|
|
||||||
xe_sync_entry_cleanup(¶m.syncs[param.num_syncs]);
|
|
||||||
kfree(param.syncs);
|
|
||||||
err_exec_q:
|
err_exec_q:
|
||||||
if (param.exec_q)
|
if (param.exec_q)
|
||||||
xe_exec_queue_put(param.exec_q);
|
xe_exec_queue_put(param.exec_q);
|
||||||
|
|
|
||||||
|
|
@ -15,6 +15,8 @@
|
||||||
#include "regs/xe_reg_defs.h"
|
#include "regs/xe_reg_defs.h"
|
||||||
#include "xe_hw_engine_types.h"
|
#include "xe_hw_engine_types.h"
|
||||||
|
|
||||||
|
struct drm_syncobj;
|
||||||
|
|
||||||
#define DEFAULT_XE_OA_BUFFER_SIZE SZ_16M
|
#define DEFAULT_XE_OA_BUFFER_SIZE SZ_16M
|
||||||
|
|
||||||
enum xe_oa_report_header {
|
enum xe_oa_report_header {
|
||||||
|
|
@ -248,6 +250,12 @@ struct xe_oa_stream {
|
||||||
/** @xef: xe_file with which the stream was opened */
|
/** @xef: xe_file with which the stream was opened */
|
||||||
struct xe_file *xef;
|
struct xe_file *xef;
|
||||||
|
|
||||||
|
/** @ufence_syncobj: User fence syncobj */
|
||||||
|
struct drm_syncobj *ufence_syncobj;
|
||||||
|
|
||||||
|
/** @ufence_timeline_value: User fence timeline value */
|
||||||
|
u64 ufence_timeline_value;
|
||||||
|
|
||||||
/** @last_fence: fence to use in stream destroy when needed */
|
/** @last_fence: fence to use in stream destroy when needed */
|
||||||
struct dma_fence *last_fence;
|
struct dma_fence *last_fence;
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -113,6 +113,8 @@ static void user_fence_cb(struct dma_fence *fence, struct dma_fence_cb *cb)
|
||||||
int xe_sync_entry_parse(struct xe_device *xe, struct xe_file *xef,
|
int xe_sync_entry_parse(struct xe_device *xe, struct xe_file *xef,
|
||||||
struct xe_sync_entry *sync,
|
struct xe_sync_entry *sync,
|
||||||
struct drm_xe_sync __user *sync_user,
|
struct drm_xe_sync __user *sync_user,
|
||||||
|
struct drm_syncobj *ufence_syncobj,
|
||||||
|
u64 ufence_timeline_value,
|
||||||
unsigned int flags)
|
unsigned int flags)
|
||||||
{
|
{
|
||||||
struct drm_xe_sync sync_in;
|
struct drm_xe_sync sync_in;
|
||||||
|
|
@ -192,10 +194,15 @@ int xe_sync_entry_parse(struct xe_device *xe, struct xe_file *xef,
|
||||||
if (exec) {
|
if (exec) {
|
||||||
sync->addr = sync_in.addr;
|
sync->addr = sync_in.addr;
|
||||||
} else {
|
} else {
|
||||||
|
sync->ufence_timeline_value = ufence_timeline_value;
|
||||||
sync->ufence = user_fence_create(xe, sync_in.addr,
|
sync->ufence = user_fence_create(xe, sync_in.addr,
|
||||||
sync_in.timeline_value);
|
sync_in.timeline_value);
|
||||||
if (XE_IOCTL_DBG(xe, IS_ERR(sync->ufence)))
|
if (XE_IOCTL_DBG(xe, IS_ERR(sync->ufence)))
|
||||||
return PTR_ERR(sync->ufence);
|
return PTR_ERR(sync->ufence);
|
||||||
|
sync->ufence_chain_fence = dma_fence_chain_alloc();
|
||||||
|
if (!sync->ufence_chain_fence)
|
||||||
|
return -ENOMEM;
|
||||||
|
sync->ufence_syncobj = ufence_syncobj;
|
||||||
}
|
}
|
||||||
|
|
||||||
break;
|
break;
|
||||||
|
|
@ -239,7 +246,12 @@ void xe_sync_entry_signal(struct xe_sync_entry *sync, struct dma_fence *fence)
|
||||||
} else if (sync->ufence) {
|
} else if (sync->ufence) {
|
||||||
int err;
|
int err;
|
||||||
|
|
||||||
dma_fence_get(fence);
|
drm_syncobj_add_point(sync->ufence_syncobj,
|
||||||
|
sync->ufence_chain_fence,
|
||||||
|
fence, sync->ufence_timeline_value);
|
||||||
|
sync->ufence_chain_fence = NULL;
|
||||||
|
|
||||||
|
fence = drm_syncobj_fence_get(sync->ufence_syncobj);
|
||||||
user_fence_get(sync->ufence);
|
user_fence_get(sync->ufence);
|
||||||
err = dma_fence_add_callback(fence, &sync->ufence->cb,
|
err = dma_fence_add_callback(fence, &sync->ufence->cb,
|
||||||
user_fence_cb);
|
user_fence_cb);
|
||||||
|
|
@ -259,7 +271,8 @@ void xe_sync_entry_cleanup(struct xe_sync_entry *sync)
|
||||||
drm_syncobj_put(sync->syncobj);
|
drm_syncobj_put(sync->syncobj);
|
||||||
dma_fence_put(sync->fence);
|
dma_fence_put(sync->fence);
|
||||||
dma_fence_chain_free(sync->chain_fence);
|
dma_fence_chain_free(sync->chain_fence);
|
||||||
if (sync->ufence)
|
dma_fence_chain_free(sync->ufence_chain_fence);
|
||||||
|
if (!IS_ERR_OR_NULL(sync->ufence))
|
||||||
user_fence_put(sync->ufence);
|
user_fence_put(sync->ufence);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -8,6 +8,7 @@
|
||||||
|
|
||||||
#include "xe_sync_types.h"
|
#include "xe_sync_types.h"
|
||||||
|
|
||||||
|
struct drm_syncobj;
|
||||||
struct xe_device;
|
struct xe_device;
|
||||||
struct xe_exec_queue;
|
struct xe_exec_queue;
|
||||||
struct xe_file;
|
struct xe_file;
|
||||||
|
|
@ -21,6 +22,8 @@ struct xe_vm;
|
||||||
int xe_sync_entry_parse(struct xe_device *xe, struct xe_file *xef,
|
int xe_sync_entry_parse(struct xe_device *xe, struct xe_file *xef,
|
||||||
struct xe_sync_entry *sync,
|
struct xe_sync_entry *sync,
|
||||||
struct drm_xe_sync __user *sync_user,
|
struct drm_xe_sync __user *sync_user,
|
||||||
|
struct drm_syncobj *ufence_syncobj,
|
||||||
|
u64 ufence_timeline_value,
|
||||||
unsigned int flags);
|
unsigned int flags);
|
||||||
int xe_sync_entry_add_deps(struct xe_sync_entry *sync,
|
int xe_sync_entry_add_deps(struct xe_sync_entry *sync,
|
||||||
struct xe_sched_job *job);
|
struct xe_sched_job *job);
|
||||||
|
|
|
||||||
|
|
@ -18,9 +18,12 @@ struct xe_sync_entry {
|
||||||
struct drm_syncobj *syncobj;
|
struct drm_syncobj *syncobj;
|
||||||
struct dma_fence *fence;
|
struct dma_fence *fence;
|
||||||
struct dma_fence_chain *chain_fence;
|
struct dma_fence_chain *chain_fence;
|
||||||
|
struct dma_fence_chain *ufence_chain_fence;
|
||||||
|
struct drm_syncobj *ufence_syncobj;
|
||||||
struct xe_user_fence *ufence;
|
struct xe_user_fence *ufence;
|
||||||
u64 addr;
|
u64 addr;
|
||||||
u64 timeline_value;
|
u64 timeline_value;
|
||||||
|
u64 ufence_timeline_value;
|
||||||
u32 type;
|
u32 type;
|
||||||
u32 flags;
|
u32 flags;
|
||||||
};
|
};
|
||||||
|
|
|
||||||
|
|
@ -3633,8 +3633,12 @@ int xe_vm_bind_ioctl(struct drm_device *dev, void *data, struct drm_file *file)
|
||||||
|
|
||||||
syncs_user = u64_to_user_ptr(args->syncs);
|
syncs_user = u64_to_user_ptr(args->syncs);
|
||||||
for (num_syncs = 0; num_syncs < args->num_syncs; num_syncs++) {
|
for (num_syncs = 0; num_syncs < args->num_syncs; num_syncs++) {
|
||||||
|
struct xe_exec_queue *__q = q ?: vm->q[0];
|
||||||
|
|
||||||
err = xe_sync_entry_parse(xe, xef, &syncs[num_syncs],
|
err = xe_sync_entry_parse(xe, xef, &syncs[num_syncs],
|
||||||
&syncs_user[num_syncs],
|
&syncs_user[num_syncs],
|
||||||
|
__q->ufence_syncobj,
|
||||||
|
++__q->ufence_timeline_value,
|
||||||
(xe_vm_in_lr_mode(vm) ?
|
(xe_vm_in_lr_mode(vm) ?
|
||||||
SYNC_PARSE_FLAG_LR_MODE : 0) |
|
SYNC_PARSE_FLAG_LR_MODE : 0) |
|
||||||
(!args->num_binds ?
|
(!args->num_binds ?
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue