[PATCH v2 2/2] drm/xe: Convert xe_user_fence to drm_user_fence

From: Srinivasan Shanmugam

Date: Thu Aug 27 2026 - 09:34:13 EST


Replace the open-coded user fence implementation in xe_sync.c with the
new common drm_user_fence helper.

struct xe_user_fence now embeds struct drm_user_fence as its base.
XE-specific fields (xe_device pointer for the ufence_wq wake-up,
userspace VA, expected value, signalled flag) remain in the wrapper.

The local user_fence_destroy/get/put/worker/kick_ufence/user_fence_cb
functions are removed. Their logic moves to xe_ufence_ops.worker and
xe_ufence_ops.destroy, which are called by drm_user_fence_work().

Cc: Matthew Brost <matthew.brost@xxxxxxxxx>
Cc: Thomas Hellström <thomas.hellstrom@xxxxxxxxxxxxxxx>
Cc: Rodrigo Vivi <rodrigo.vivi@xxxxxxxxx>
Cc: Mika Kuoppala <mika.kuoppala@xxxxxxxxxxxxxxx>
Cc: David Airlie <airlied@xxxxxxxxx>
Cc: Simona Vetter <simona@xxxxxxxx>
Cc: Sumit Semwal <sumit.semwal@xxxxxxxxxx>
Cc: Christian König <christian.koenig@xxxxxxx>
Cc: Alex Deucher <alexander.deucher@xxxxxxx>
Cc: intel-xe@xxxxxxxxxxxxxxxxxxxxx
Cc: dri-devel@xxxxxxxxxxxxxxxxxxxxx
Cc: linux-media@xxxxxxxxxxxxxxx
Cc: linaro-mm-sig@xxxxxxxxxxxxxxxx
Cc: linux-kernel@xxxxxxxxxxxxxxx
Signed-off-by: Srinivasan Shanmugam <srinivasan.shanmugam@xxxxxxx>
---
v2:
- Remove unconditional dma_fence_put(fence) after
drm_user_fence_add_callback() in xe_sync_entry_signal(). The fence
reference is now consumed by drm_user_fence_add_callback() in all
paths — transferred to the callback on success, released internally
on the already-signaled and error paths. (Sashiko review)

drivers/gpu/drm/xe/xe_sync.c | 112 ++++++++++++-----------------
drivers/gpu/drm/xe/xe_sync_types.h | 1 -
2 files changed, 44 insertions(+), 69 deletions(-)

diff --git a/drivers/gpu/drm/xe/xe_sync.c b/drivers/gpu/drm/xe/xe_sync.c
index 37866768d64c..7549123f8486 100644
--- a/drivers/gpu/drm/xe/xe_sync.c
+++ b/drivers/gpu/drm/xe/xe_sync.c
@@ -6,12 +6,11 @@
#include "xe_sync.h"

#include <linux/dma-fence-array.h>
-#include <linux/kthread.h>
-#include <linux/sched/mm.h>
#include <linux/uaccess.h>

#include <drm/drm_print.h>
#include <drm/drm_syncobj.h>
+#include <drm/drm_user_fence.h>
#include <uapi/drm/xe_drm.h>

#include "xe_device.h"
@@ -19,36 +18,51 @@
#include "xe_macros.h"
#include "xe_sched_job_types.h"

+/*
+ * xe_user_fence wraps drm_user_fence with XE-specific fields.
+ * The drm_user_fence base handles MM borrowing and work-item lifetime.
+ */
struct xe_user_fence {
- struct xe_device *xe;
- struct kref refcount;
- struct dma_fence_cb cb;
- struct work_struct worker;
- struct mm_struct *mm;
- u64 __user *addr;
- u64 value;
- int signalled;
+ struct drm_user_fence base;
+ struct xe_device *xe;
+ u64 __user *addr;
+ u64 value;
+ int signalled;
};

-static void user_fence_destroy(struct kref *kref)
+static void xe_ufence_worker(struct drm_user_fence *base, bool mm_ok)
{
- struct xe_user_fence *ufence = container_of(kref, struct xe_user_fence,
- refcount);
+ struct xe_user_fence *ufence = container_of(base, struct xe_user_fence, base);

- mmdrop(ufence->mm);
- kfree(ufence);
-}
+ /*
+ * Mark signalled before waking waiters so UMD can safely reuse
+ * the same ufence without hitting -EBUSY.
+ */
+ WRITE_ONCE(ufence->signalled, 1);

-static void user_fence_get(struct xe_user_fence *ufence)
-{
- kref_get(&ufence->refcount);
+ if (mm_ok) {
+ if (copy_to_user(ufence->addr, &ufence->value, sizeof(ufence->value)))
+ XE_WARN_ON("Copy to user failed");
+ } else {
+ drm_dbg(&ufence->xe->drm,
+ "mmget_not_zero() failed, ufence wasn't signaled\n");
+ }
+
+ wake_up_all(&ufence->xe->ufence_wq);
}

-static void user_fence_put(struct xe_user_fence *ufence)
+static void xe_ufence_destroy(struct drm_user_fence *base)
{
- kref_put(&ufence->refcount, user_fence_destroy);
+ struct xe_user_fence *ufence = container_of(base, struct xe_user_fence, base);
+
+ kfree(ufence);
}

+static const struct drm_user_fence_ops xe_ufence_ops = {
+ .worker = xe_ufence_worker,
+ .destroy = xe_ufence_destroy,
+};
+
static struct xe_user_fence *user_fence_create(struct xe_device *xe, u64 addr,
u64 value)
{
@@ -63,51 +77,22 @@ static struct xe_user_fence *user_fence_create(struct xe_device *xe, u64 addr,
if (!ufence)
return ERR_PTR(-ENOMEM);

- ufence->xe = xe;
- kref_init(&ufence->refcount);
- ufence->addr = ptr;
+ ufence->xe = xe;
+ ufence->addr = ptr;
ufence->value = value;
- ufence->mm = current->mm;
- mmgrab(ufence->mm);
+ drm_user_fence_init(&ufence->base, xe->ordered_wq, &xe_ufence_ops);

return ufence;
}

-static void user_fence_worker(struct work_struct *w)
-{
- struct xe_user_fence *ufence = container_of(w, struct xe_user_fence, worker);
-
- WRITE_ONCE(ufence->signalled, 1);
- if (mmget_not_zero(ufence->mm)) {
- kthread_use_mm(ufence->mm);
- if (copy_to_user(ufence->addr, &ufence->value, sizeof(ufence->value)))
- XE_WARN_ON("Copy to user failed");
- kthread_unuse_mm(ufence->mm);
- mmput(ufence->mm);
- } else {
- drm_dbg(&ufence->xe->drm, "mmget_not_zero() failed, ufence wasn't signaled\n");
- }
-
- /*
- * Wake up waiters only after updating the ufence state, allowing the UMD
- * to safely reuse the same ufence without encountering -EBUSY errors.
- */
- wake_up_all(&ufence->xe->ufence_wq);
- user_fence_put(ufence);
-}
-
-static void kick_ufence(struct xe_user_fence *ufence, struct dma_fence *fence)
+static void user_fence_get(struct xe_user_fence *ufence)
{
- INIT_WORK(&ufence->worker, user_fence_worker);
- queue_work(ufence->xe->ordered_wq, &ufence->worker);
- dma_fence_put(fence);
+ drm_user_fence_get(&ufence->base);
}

-static void user_fence_cb(struct dma_fence *fence, struct dma_fence_cb *cb)
+static void user_fence_put(struct xe_user_fence *ufence)
{
- struct xe_user_fence *ufence = container_of(cb, struct xe_user_fence, cb);
-
- kick_ufence(ufence, fence);
+ drm_user_fence_put(&ufence->base);
}

int xe_sync_entry_parse(struct xe_device *xe, struct xe_file *xef,
@@ -282,24 +267,15 @@ void xe_sync_entry_signal(struct xe_sync_entry *sync, struct dma_fence *fence)
} else if (sync->syncobj) {
drm_syncobj_replace_fence(sync->syncobj, fence);
} else if (sync->ufence) {
- int err;
-
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);
- if (err == -ENOENT) {
- kick_ufence(sync->ufence, fence);
- } else if (err) {
+ if (drm_user_fence_add_callback(&sync->ufence->base, fence))
XE_WARN_ON("failed to add user fence");
- user_fence_put(sync->ufence);
- dma_fence_put(fence);
- }
+ /* fence ref consumed by drm_user_fence_add_callback */
}
}

diff --git a/drivers/gpu/drm/xe/xe_sync_types.h b/drivers/gpu/drm/xe/xe_sync_types.h
index b88f1833e28c..67048199b070 100644
--- a/drivers/gpu/drm/xe/xe_sync_types.h
+++ b/drivers/gpu/drm/xe/xe_sync_types.h
@@ -12,7 +12,6 @@ struct drm_syncobj;
struct dma_fence;
struct dma_fence_chain;
struct drm_xe_sync;
-struct user_fence;

struct xe_sync_entry {
struct drm_syncobj *syncobj;
--
2.34.1