]> git.ipfire.org Git - thirdparty/linux.git/commitdiff
drm/xe: Enforce correct user fence signaling order using
authorMatthew Brost <matthew.brost@intel.com>
Fri, 31 Oct 2025 23:40:45 +0000 (16:40 -0700)
committerLucas De Marchi <lucas.demarchi@intel.com>
Fri, 7 Nov 2025 11:55:19 +0000 (03:55 -0800)
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: dd08ebf6c352 ("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
(cherry picked from commit adda4e855ab6409a3edaa585293f1f2069ab7299)
Signed-off-by: Lucas De Marchi <lucas.demarchi@intel.com>
drivers/gpu/drm/xe/xe_exec.c
drivers/gpu/drm/xe/xe_exec_queue.c
drivers/gpu/drm/xe/xe_exec_queue_types.h
drivers/gpu/drm/xe/xe_oa.c
drivers/gpu/drm/xe/xe_oa_types.h
drivers/gpu/drm/xe/xe_sync.c
drivers/gpu/drm/xe/xe_sync.h
drivers/gpu/drm/xe/xe_sync_types.h
drivers/gpu/drm/xe/xe_vm.c

index 7715e74bb94544198d2d24b5be3123d66329e980..a8ab363a804658373a4c213447e75837d031108d 100644 (file)
@@ -165,7 +165,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++) {
                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) ?
                                           SYNC_PARSE_FLAG_LR_MODE : 0));
                if (err)
index 37b2b93b73d6c92cf676646ce1e3eb04ce0ac75d..cb5f204c08ed6e9b73fe9f1baecd784fbecdbfb3 100644 (file)
@@ -10,6 +10,7 @@
 #include <drm/drm_device.h>
 #include <drm/drm_drv.h>
 #include <drm/drm_file.h>
+#include <drm/drm_syncobj.h>
 #include <uapi/drm/xe_drm.h>
 
 #include "xe_dep_scheduler.h"
@@ -324,6 +325,16 @@ struct xe_exec_queue *xe_exec_queue_create_bind(struct xe_device *xe,
        }
        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;
 }
 ALLOW_ERROR_INJECTION(xe_exec_queue_create_bind, ERRNO);
@@ -333,6 +344,9 @@ void xe_exec_queue_destroy(struct kref *ref)
        struct xe_exec_queue *q = container_of(ref, struct xe_exec_queue, refcount);
        struct xe_exec_queue *eq, *next;
 
+       if (q->ufence_syncobj)
+               drm_syncobj_put(q->ufence_syncobj);
+
        if (xe_exec_queue_uses_pxp(q))
                xe_pxp_exec_queue_remove(gt_to_xe(q->gt)->pxp, q);
 
index 27b76cf9da8958e2bb883233aa4a8daf57923cf6..df1c69dc81f17b72303d51b1a19d2328316188cb 100644 (file)
@@ -15,6 +15,7 @@
 #include "xe_hw_fence_types.h"
 #include "xe_lrc_types.h"
 
+struct drm_syncobj;
 struct xe_execlist_exec_queue;
 struct xe_gt;
 struct xe_guc_exec_queue;
@@ -155,6 +156,12 @@ struct xe_exec_queue {
                struct list_head link;
        } 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 */
        const struct xe_exec_queue_ops *ops;
 
index a4894eb0d7f3f36e575e764bc1a84b0d7060f753..125698a9ecf1c41f72d600ee81e3879a7a74cd6c 100644 (file)
@@ -10,6 +10,7 @@
 
 #include <drm/drm_drv.h>
 #include <drm/drm_managed.h>
+#include <drm/drm_syncobj.h>
 #include <uapi/drm/xe_drm.h>
 
 #include <generated/xe_wa_oob.h>
@@ -1389,7 +1390,9 @@ static int xe_oa_user_extensions(struct xe_oa *oa, enum xe_oa_user_extn_from fro
        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;
 
@@ -1409,7 +1412,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++) {
                ret = xe_sync_entry_parse(oa->xe, param->xef, &param->syncs[num_syncs],
-                                         &param->syncs_user[num_syncs], 0);
+                                         &param->syncs_user[num_syncs],
+                                         stream->ufence_syncobj,
+                                         ++stream->ufence_timeline_value, 0);
                if (ret)
                        goto err_syncs;
 
@@ -1539,7 +1544,7 @@ static long xe_oa_config_locked(struct xe_oa_stream *stream, u64 arg)
                return -ENODEV;
 
        param.xef = stream->xef;
-       err = xe_oa_parse_syncs(stream->oa, &param);
+       err = xe_oa_parse_syncs(stream->oa, stream, &param);
        if (err)
                goto err_config_put;
 
@@ -1635,6 +1640,7 @@ static void xe_oa_destroy_locked(struct xe_oa_stream *stream)
        if (stream->exec_q)
                xe_exec_queue_put(stream->exec_q);
 
+       drm_syncobj_put(stream->ufence_syncobj);
        kfree(stream);
 }
 
@@ -1826,6 +1832,7 @@ static int xe_oa_stream_open_ioctl_locked(struct xe_oa *oa,
                                          struct xe_oa_open_param *param)
 {
        struct xe_oa_stream *stream;
+       struct drm_syncobj *ufence_syncobj;
        int stream_fd;
        int ret;
 
@@ -1836,17 +1843,31 @@ static int xe_oa_stream_open_ioctl_locked(struct xe_oa *oa,
                goto exit;
        }
 
+       ret = drm_syncobj_create(&ufence_syncobj, DRM_SYNCOBJ_CREATE_SIGNALED,
+                                NULL);
+       if (ret)
+               goto exit;
+
        stream = kzalloc(sizeof(*stream), GFP_KERNEL);
        if (!stream) {
                ret = -ENOMEM;
-               goto exit;
+               goto err_syncobj;
        }
-
+       stream->ufence_syncobj = ufence_syncobj;
        stream->oa = oa;
-       ret = xe_oa_stream_init(stream, param);
+
+       ret = xe_oa_parse_syncs(oa, stream, param);
        if (ret)
                goto err_free;
 
+       ret = xe_oa_stream_init(stream, param);
+       if (ret) {
+               while (param->num_syncs--)
+                       xe_sync_entry_cleanup(&param->syncs[param->num_syncs]);
+               kfree(param->syncs);
+               goto err_free;
+       }
+
        if (!param->disabled) {
                ret = xe_oa_enable_locked(stream);
                if (ret)
@@ -1870,6 +1891,8 @@ err_destroy:
        xe_oa_stream_destroy(stream);
 err_free:
        kfree(stream);
+err_syncobj:
+       drm_syncobj_put(ufence_syncobj);
 exit:
        return ret;
 }
@@ -2083,22 +2106,14 @@ int xe_oa_stream_open_ioctl(struct drm_device *dev, u64 data, struct drm_file *f
                goto err_exec_q;
        }
 
-       ret = xe_oa_parse_syncs(oa, &param);
-       if (ret)
-               goto err_exec_q;
-
        mutex_lock(&param.hwe->gt->oa.gt_lock);
        ret = xe_oa_stream_open_ioctl_locked(oa, &param);
        mutex_unlock(&param.hwe->gt->oa.gt_lock);
        if (ret < 0)
-               goto err_sync_cleanup;
+               goto err_exec_q;
 
        return ret;
 
-err_sync_cleanup:
-       while (param.num_syncs--)
-               xe_sync_entry_cleanup(&param.syncs[param.num_syncs]);
-       kfree(param.syncs);
 err_exec_q:
        if (param.exec_q)
                xe_exec_queue_put(param.exec_q);
index 2628f78c4e8dc46d282d1de6aeef72fc3b44da53..daf701b5d48b9d5d05c461dce815d71b50281987 100644 (file)
@@ -15,6 +15,8 @@
 #include "regs/xe_reg_defs.h"
 #include "xe_hw_engine_types.h"
 
+struct drm_syncobj;
+
 #define DEFAULT_XE_OA_BUFFER_SIZE SZ_16M
 
 enum xe_oa_report_header {
@@ -248,6 +250,12 @@ struct xe_oa_stream {
        /** @xef: xe_file with which the stream was opened */
        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 */
        struct dma_fence *last_fence;
 
index 82872a51f0983aa9654a0276c285a64094dcaed1..d48ab7b32ca51c7408ce752ee260eb5dc08ac977 100644 (file)
@@ -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,
                        struct xe_sync_entry *sync,
                        struct drm_xe_sync __user *sync_user,
+                       struct drm_syncobj *ufence_syncobj,
+                       u64 ufence_timeline_value,
                        unsigned int flags)
 {
        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) {
                        sync->addr = sync_in.addr;
                } else {
+                       sync->ufence_timeline_value = ufence_timeline_value;
                        sync->ufence = user_fence_create(xe, sync_in.addr,
                                                         sync_in.timeline_value);
                        if (XE_IOCTL_DBG(xe, IS_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;
@@ -239,7 +246,12 @@ void xe_sync_entry_signal(struct xe_sync_entry *sync, struct dma_fence *fence)
        } else if (sync->ufence) {
                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);
                err = dma_fence_add_callback(fence, &sync->ufence->cb,
                                             user_fence_cb);
@@ -259,7 +271,8 @@ void xe_sync_entry_cleanup(struct xe_sync_entry *sync)
                drm_syncobj_put(sync->syncobj);
        dma_fence_put(sync->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);
 }
 
index 256ffc1e54dc7cb421766718e8bc6482c2af1335..51f2d803e9776600a054022bc3dee74464a30019 100644 (file)
@@ -8,6 +8,7 @@
 
 #include "xe_sync_types.h"
 
+struct drm_syncobj;
 struct xe_device;
 struct xe_exec_queue;
 struct xe_file;
@@ -21,6 +22,8 @@ struct xe_vm;
 int xe_sync_entry_parse(struct xe_device *xe, struct xe_file *xef,
                        struct xe_sync_entry *sync,
                        struct drm_xe_sync __user *sync_user,
+                       struct drm_syncobj *ufence_syncobj,
+                       u64 ufence_timeline_value,
                        unsigned int flags);
 int xe_sync_entry_add_deps(struct xe_sync_entry *sync,
                           struct xe_sched_job *job);
index 30ac3f51993b944e3dd86ccb059c75441f87f5e1..b88f1833e28cc2a0f8a2887ce7dec50f493b3c30 100644 (file)
@@ -18,9 +18,12 @@ struct xe_sync_entry {
        struct drm_syncobj *syncobj;
        struct dma_fence *fence;
        struct dma_fence_chain *chain_fence;
+       struct dma_fence_chain *ufence_chain_fence;
+       struct drm_syncobj *ufence_syncobj;
        struct xe_user_fence *ufence;
        u64 addr;
        u64 timeline_value;
+       u64 ufence_timeline_value;
        u32 type;
        u32 flags;
 };
index 63c65e3d207ba6ff26fc9cec4df87c4f91302642..ccb09ef4ec9ea22975ea3410045db6e5983f1cdf 100644 (file)
@@ -3606,8 +3606,12 @@ int xe_vm_bind_ioctl(struct drm_device *dev, void *data, struct drm_file *file)
 
        syncs_user = u64_to_user_ptr(args->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],
                                          &syncs_user[num_syncs],
+                                         __q->ufence_syncobj,
+                                         ++__q->ufence_timeline_value,
                                          (xe_vm_in_lr_mode(vm) ?
                                           SYNC_PARSE_FLAG_LR_MODE : 0) |
                                          (!args->num_binds ?