On Wed, Sep 02, 2026 at 08:50:47PM +0530, Srinivasan Shanmugam wrote:
> Introduce a common DRM user fence helper providing the kref-managed,
> MM-borrowing dma-fence-callback-to-workqueue pattern used by drivers
> that must access userspace memory from a kthread context when a GPU
> fence signals.
>
> XE uses this pattern (xe_sync.c) to write a fence completion value
> to a userspace VA. AMDGPU will use the same pattern to signal a
> per-queue eventfd from a user-queue EOP fence callback.
>
> The helper provides:
> - struct drm_user_fence: embeddable base structure
> - struct drm_user_fence_ops: worker/destroy callbacks
> - drm_user_fence_init(): initialize and grab the process MM
> - drm_user_fence_get/put(): reference counting
> - drm_user_fence_add_callback(): attach to a dma-fence
>
> The worker callback receives a bool indicating whether the process
> MM was successfully obtained, allowing drivers to handle the
> unavailable-MM case (log, skip the userspace write, etc.) without
> duplicating the mmget/kthread_use_mm/mmput boilerplate.
>
> Suggested-by: Christian König <[email protected]>
> Cc: Matthew Brost <[email protected]>
> Cc: Maarten Lankhorst <[email protected]>
> Cc: [email protected]
> Cc: [email protected]
> Cc: [email protected]
> Signed-off-by: Srinivasan Shanmugam <[email protected]>
> Change-Id: I09da42c688392326ed78235b302fba893e570eff
> ---
> drivers/gpu/drm/Makefile | 1 +
> drivers/gpu/drm/drm_user_fence.c | 70 ++++++++++++++++++
> include/drm/drm_user_fence.h | 122 +++++++++++++++++++++++++++++++
> 3 files changed, 193 insertions(+)
> create mode 100644 drivers/gpu/drm/drm_user_fence.c
> create mode 100644 include/drm/drm_user_fence.h
>
> diff --git a/drivers/gpu/drm/Makefile b/drivers/gpu/drm/Makefile
> index c5be8e80d0c8..ddb770738992 100644
> --- a/drivers/gpu/drm/Makefile
> +++ b/drivers/gpu/drm/Makefile
> @@ -69,6 +69,7 @@ drm-y := \
> drm_syncobj.o \
> drm_sysfs.o \
> drm_trace_points.o \
> + drm_user_fence.o \
> drm_vblank.o \
> drm_vblank_work.o \
> drm_vma_manager.o \
> diff --git a/drivers/gpu/drm/drm_user_fence.c
> b/drivers/gpu/drm/drm_user_fence.c
> new file mode 100644
> index 000000000000..0f229b7210a9
> --- /dev/null
> +++ b/drivers/gpu/drm/drm_user_fence.c
> @@ -0,0 +1,70 @@
> +// SPDX-License-Identifier: MIT
> +/*
> + * Copyright © 2024 The Linux Foundation
Same comment as previous patch about odd license.
> + *
> + * DRM user fence — extends drm_work_fence with kthread_use_mm() support.
> + *
> + * Use this when a GPU fence signals and work needs to access userspace
> + * memory (copy_to_user, fault-able operations) from a kthread context.
> + * For work that does not require userspace memory access, use
> + * drm_work_fence directly.
> + */
> +
> +#include <linux/kthread.h>
> +#include <linux/sched/mm.h>
> +
> +#include <drm/drm_user_fence.h>
> +
> +static void drm_user_fence_do_destroy(struct drm_work_fence *wfence)
> +{
> + struct drm_user_fence *ufence =
> + container_of(wfence, struct drm_user_fence, base);
> + struct mm_struct *mm = ufence->mm;
> +
> + ufence->ops->destroy(ufence);
> + mmdrop(mm);
> +}
> +
> +static void drm_user_fence_do_work(struct drm_work_fence *wfence)
> +{
> + struct drm_user_fence *ufence =
> + container_of(wfence, struct drm_user_fence, base);
> + struct mm_struct *mm = NULL;
> +
> + if (mmget_not_zero(ufence->mm)) {
> + mm = ufence->mm;
> + kthread_use_mm(mm);
> + }
> +
> + ufence->ops->worker(ufence, !!mm);
I think you inverted the naming vfuncs for drm_work_fence vs drm_user_fence.
I'd expect drm_work_fence.worker and drm_user_fence.writeback.
> +
> + if (mm) {
> + kthread_unuse_mm(mm);
> + mmput_async(mm);
> + }
> +}
> +
> +static const struct drm_work_fence_ops drm_user_fence_wfence_ops = {
> + .writeback = drm_user_fence_do_work,
> + .destroy = drm_user_fence_do_destroy,
> +};
> +
> +/**
> + * drm_user_fence_init - Initialize a user fence
> + * @ufence: user fence to initialize
> + * @wq: workqueue on which to run the worker
> + * @ops: driver operations
> + *
> + * Must be called from process context with a valid current->mm.
> + * Grabs a reference to current->mm via mmgrab().
> + */
> +void drm_user_fence_init(struct drm_user_fence *ufence,
> + struct workqueue_struct *wq,
> + const struct drm_user_fence_ops *ops)
> +{
> + drm_work_fence_init(&ufence->base, wq, &drm_user_fence_wfence_ops);
> + ufence->mm = current->mm;
> + mmgrab(ufence->mm);
> + ufence->ops = ops;
> +}
> +EXPORT_SYMBOL_GPL(drm_user_fence_init);
Same comment as previous patch about EXPORT_SYMBOL vs EXPORT_SYMBOL_GPL.
Matt
> diff --git a/include/drm/drm_user_fence.h b/include/drm/drm_user_fence.h
> new file mode 100644
> index 000000000000..d35438eaa9e2
> --- /dev/null
> +++ b/include/drm/drm_user_fence.h
> @@ -0,0 +1,122 @@
> +/* SPDX-License-Identifier: MIT */
> +/*
> + * Copyright © 2024 The Linux Foundation
> + */
> +
> +#ifndef __DRM_USER_FENCE_H__
> +#define __DRM_USER_FENCE_H__
> +
> +#include <linux/dma-fence.h>
> +
> +#include <drm/drm_work_fence.h>
> +
> +struct drm_user_fence;
> +
> +/**
> + * struct drm_user_fence_ops - driver callbacks for a DRM user fence
> + */
> +struct drm_user_fence_ops {
> + /**
> + * @worker: Called from workqueue context with the process MM active.
> + *
> + * If @mm_ok is true, kthread_use_mm() is active and userspace memory
> + * (copy_to_user, etc.) may be accessed safely.
> + * If @mm_ok is false, the process MM was already gone; skip the
> + * userspace write.
> + *
> + * wake_up() or other post-signal housekeeping should also happen here.
> + *
> + * WARNING: Fault-able operations such as copy_to_user() may block
> + * indefinitely if userspace registers the target address with
> + * userfaultfd or backs it with a FUSE mount. Drivers that cannot
> + * tolerate blocking should use copy_to_user_nofault() instead.
> + */
> + void (*worker)(struct drm_user_fence *ufence, bool mm_ok);
> +
> + /**
> + * @destroy: Called when the last reference is dropped.
> + * Free the containing structure here.
> + */
> + void (*destroy)(struct drm_user_fence *ufence);
> +};
> +
> +/**
> + * struct drm_user_fence - DRM user fence with MM borrowing
> + *
> + * Extends drm_work_fence with kthread_use_mm() support for drivers
> + * that need to access userspace memory when a GPU fence signals.
> + *
> + * Call drm_user_fence_init() at creation and drm_user_fence_add_callback()
> + * to arm on a dma-fence. Call drm_user_fence_cancel_sync() before teardown.
> + */
> +struct drm_user_fence {
> + /** @base: Base work fence. Must be first. */
> + struct drm_work_fence base;
> + /** @mm: Process MM grabbed at init time. */
> + struct mm_struct *mm;
> + /** @ops: Driver operations. */
> + const struct drm_user_fence_ops *ops;
> +};
> +
> +void drm_user_fence_init(struct drm_user_fence *ufence,
> + struct workqueue_struct *wq,
> + const struct drm_user_fence_ops *ops);
> +
> +/**
> + * drm_user_fence_get - Acquire a reference to a user fence
> + * @ufence: user fence
> + */
> +static inline void drm_user_fence_get(struct drm_user_fence *ufence)
> +{
> + drm_work_fence_get(&ufence->base);
> +}
> +
> +/**
> + * drm_user_fence_put - Release a reference to a user fence
> + * @ufence: user fence
> + */
> +static inline void drm_user_fence_put(struct drm_user_fence *ufence)
> +{
> + drm_work_fence_put(&ufence->base);
> +}
> +
> +/**
> + * drm_user_fence_add_callback - Attach a user fence to a dma-fence
> + * @ufence: user fence; caller retains their reference and must release
> + * it via drm_user_fence_put() when no longer needed
> + * @fence: dma-fence to watch; one reference is consumed on any return value
> + *
> + * When @fence signals, ops->worker() is called from workqueue context.
> + * If @fence has already signaled, the worker is queued immediately.
> + *
> + * Return: 0 on success, negative errno on error.
> + */
> +static inline int drm_user_fence_add_callback(struct drm_user_fence *ufence,
> + struct dma_fence *fence)
> +{
> + return drm_work_fence_add_callback(&ufence->base, fence);
> +}
> +
> +/**
> + * drm_user_fence_cancel - Cancel a pending user fence callback
> + * @ufence: user fence
> + *
> + * Return: true if callback was removed, false if it had already fired.
> + */
> +static inline bool drm_user_fence_cancel(struct drm_user_fence *ufence)
> +{
> + return drm_work_fence_cancel(&ufence->base);
> +}
> +
> +/**
> + * drm_user_fence_cancel_sync - Cancel callback and wait for worker to finish
> + * @ufence: user fence
> + *
> + * Must be called during teardown before freeing resources. May sleep.
> + */
> +static inline void drm_user_fence_cancel_sync(struct drm_user_fence *ufence)
> +{
> + drm_work_fence_cancel_sync(&ufence->base);
> +}
> +
> +#endif /* __DRM_USER_FENCE_H__ */
> --
> 2.34.1
>