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
> 

Reply via email to