* [PATCH v4 1/2] drm: Add common drm_user_fence helper [not found] <20260828062917.4106569-1-srinivasan.shanmugam@amd.com> @ 2026-08-28 6:29 ` Srinivasan Shanmugam 2026-08-28 6:29 ` [PATCH v4 2/2] drm/xe: Convert xe_user_fence to drm_user_fence Srinivasan Shanmugam ` (2 subsequent siblings) 3 siblings, 0 replies; 6+ messages in thread From: Srinivasan Shanmugam @ 2026-08-28 6:29 UTC (permalink / raw) To: matthew.brost Cc: Srinivasan Shanmugam, Christian König, Alex Deucher, Maarten Lankhorst, Maxime Ripard, Thomas Zimmermann, David Airlie, Simona Vetter, Sumit Semwal, Thomas Hellström, dri-devel, intel-xe, linux-media, linaro-mm-sig, linux-kernel, amd-gfx 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 <christian.koenig@amd.com> Cc: Alex Deucher <alexander.deucher@amd.com> Cc: Maarten Lankhorst <maarten.lankhorst@linux.intel.com> Cc: Maxime Ripard <mripard@kernel.org> Cc: Thomas Zimmermann <tzimmermann@suse.de> Cc: David Airlie <airlied@gmail.com> Cc: Simona Vetter <simona@ffwll.ch> Cc: Sumit Semwal <sumit.semwal@linaro.org> Cc: Matthew Brost <matthew.brost@intel.com> Cc: Thomas Hellström <thomas.hellstrom@linux.intel.com> Cc: dri-devel@lists.freedesktop.org Cc: intel-xe@lists.freedesktop.org Cc: linux-media@vger.kernel.org Cc: linaro-mm-sig@lists.linaro.org Cc: linux-kernel@vger.kernel.org Cc: amd-gfx@lists.freedesktop.org Signed-off-by: Srinivasan Shanmugam <srinivasan.shanmugam@amd.com> --- v4: - Check cancel_work_sync() return value in drm_user_fence_cancel_sync() and call drm_user_fence_put() if work was dequeued, fixing a memory leak of the drm_user_fence, mm_struct and stored dma_fence when a pending work item is cancelled. (Sashiko review) drivers/gpu/drm/Makefile | 1 + drivers/gpu/drm/drm_user_fence.c | 223 +++++++++++++++++++++++++++++++ include/drm/drm_user_fence.h | 76 +++++++++++ 3 files changed, 300 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 e97faabcd783..52de1f474535 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..cdc47d092cbb --- /dev/null +++ b/drivers/gpu/drm/drm_user_fence.c @@ -0,0 +1,223 @@ +// SPDX-License-Identifier: MIT +/* + * Copyright © 2024 The Linux Foundation + * + * Common DRM user fence helper. + * + * When a GPU dma-fence signals, drivers often need to write a value to a + * userspace VA or notify userspace via an eventfd. Both operations require + * a valid process MM, which is not available in IRQ context. + * + * This helper queues a work item on fence signal. The work item borrows the + * process MM via kthread_use_mm() and calls ops->worker(), which the driver + * implements to perform the actual userspace access. + */ + +#include <linux/kthread.h> +#include <linux/sched/mm.h> +#include <linux/workqueue.h> + +#include <drm/drm_user_fence.h> + +static void drm_user_fence_destroy(struct kref *kref) +{ + struct drm_user_fence *ufence = + container_of(kref, struct drm_user_fence, refcount); + + /* Release the extra reference stored for cancel() */ + if (ufence->fence) + dma_fence_put(ufence->fence); + + mmdrop(ufence->mm); + ufence->ops->destroy(ufence); +} + +/** + * drm_user_fence_get - Acquire a reference to a user fence + * @ufence: user fence + */ +void drm_user_fence_get(struct drm_user_fence *ufence) +{ + kref_get(&ufence->refcount); +} +EXPORT_SYMBOL_GPL(drm_user_fence_get); + +/** + * drm_user_fence_put - Release a reference to a user fence + * @ufence: user fence + */ +void drm_user_fence_put(struct drm_user_fence *ufence) +{ + kref_put(&ufence->refcount, drm_user_fence_destroy); +} +EXPORT_SYMBOL_GPL(drm_user_fence_put); + +static void drm_user_fence_work(struct work_struct *w) +{ + struct drm_user_fence *ufence = + container_of(w, struct drm_user_fence, work); + bool mm_ok = false; + + if (mmget_not_zero(ufence->mm)) { + kthread_use_mm(ufence->mm); + mm_ok = true; + } + + ufence->ops->worker(ufence, mm_ok); + + if (mm_ok) { + kthread_unuse_mm(ufence->mm); + mmput(ufence->mm); + } + + drm_user_fence_put(ufence); +} + +static void drm_user_fence_cb(struct dma_fence *fence, struct dma_fence_cb *cb) +{ + struct drm_user_fence *ufence = + container_of(cb, struct drm_user_fence, cb); + + queue_work(ufence->wq, &ufence->work); + /* + * Put the transferred reference from add_callback. The stored + * reference in ufence->fence is released in drm_user_fence_destroy(). + */ + dma_fence_put(fence); +} + +/** + * drm_user_fence_init - Initialize a user fence + * @ufence: user fence to initialize + * @wq: workqueue to run the worker on (must be ordered if sequencing matters) + * @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) +{ + kref_init(&ufence->refcount); + ufence->mm = current->mm; + mmgrab(ufence->mm); + ufence->wq = wq; + ufence->ops = ops; + ufence->fence = NULL; + INIT_WORK(&ufence->work, drm_user_fence_work); +} +EXPORT_SYMBOL_GPL(drm_user_fence_init); + +/** + * drm_user_fence_add_callback - Attach a user fence to a dma-fence + * @ufence: user fence + * @fence: dma-fence to watch; ownership of this reference is transferred + * to the callback — caller must NOT put it afterward. + * + * When @fence signals, a work item is queued that calls ops->worker() with + * the process MM active. If @fence has already signaled the work item is + * queued immediately. + * + * An additional reference to @fence is stored internally in @ufence to + * allow drm_user_fence_cancel() to be called safely without the caller + * needing to hold a separate fence reference. + * + * On any return value the caller's fence reference is consumed. + * + * Return: 0 on success, negative errno on error. + */ +int drm_user_fence_add_callback(struct drm_user_fence *ufence, + struct dma_fence *fence) +{ + int err; + + drm_user_fence_get(ufence); + + /* Extra ref stored for cancel() — lives until drm_user_fence_destroy() */ + ufence->fence = dma_fence_get(fence); + + err = dma_fence_add_callback(fence, &ufence->cb, drm_user_fence_cb); + if (err == -ENOENT) { + /* fence already signaled — queue work and release transferred ref */ + queue_work(ufence->wq, &ufence->work); + dma_fence_put(fence); + err = 0; + } else if (err) { + dma_fence_put(ufence->fence); + ufence->fence = NULL; + drm_user_fence_put(ufence); + dma_fence_put(fence); + } + /* on success: transferred ref goes to drm_user_fence_cb */ + + return err; +} +EXPORT_SYMBOL_GPL(drm_user_fence_add_callback); + +/** + * drm_user_fence_cancel - Cancel a pending user fence callback + * @ufence: user fence + * + * Attempts to remove the pending callback before driver context teardown. + * Must be called before the driver tears down its workqueue or ops. + * The caller must hold a reference to @ufence across this call. + * + * If the callback has already fired this returns false and no additional + * action is needed — the callback handles its own reference. + * + * If removal succeeds the callback reference is released internally. + * The caller must still release its own separate reference via + * drm_user_fence_put() when done with the object. + * + * This function is safe to call from atomic context as it only acquires + * the dma-fence spinlock internally. If the caller also needs to wait + * for the worker to finish, use drm_user_fence_cancel_sync() instead, + * which may sleep. + * + * Return: true if callback was removed, false if it had already fired. + */ +bool drm_user_fence_cancel(struct drm_user_fence *ufence) +{ + struct dma_fence *fence = ufence->fence; + + if (!fence) + return false; + + if (dma_fence_remove_callback(fence, &ufence->cb)) { + /* + * Callback will not fire — release the transferred reference + * that would have been put by drm_user_fence_cb(). The stored + * reference in ufence->fence is released in destroy(). + */ + dma_fence_put(fence); + drm_user_fence_put(ufence); + return true; + } + + /* Callback already fired — it handled its own cleanup */ + return false; +} +EXPORT_SYMBOL_GPL(drm_user_fence_cancel); + +/** + * drm_user_fence_cancel_sync - Cancel callback and wait for worker to finish + * @ufence: user fence + * + * Calls drm_user_fence_cancel() then cancel_work_sync() to guarantee + * the worker has fully completed before returning. + * + * This function may sleep. Must not be called from atomic or interrupt + * context. Use drm_user_fence_cancel() instead when sleeping is not + * allowed. + * + * Drivers must call this during teardown before freeing any resources + * accessed by ops->worker(). + */ +void drm_user_fence_cancel_sync(struct drm_user_fence *ufence) +{ + drm_user_fence_cancel(ufence); + if (cancel_work_sync(&ufence->work)) + drm_user_fence_put(ufence); +} +EXPORT_SYMBOL_GPL(drm_user_fence_cancel_sync); diff --git a/include/drm/drm_user_fence.h b/include/drm/drm_user_fence.h new file mode 100644 index 000000000000..02a02266ab93 --- /dev/null +++ b/include/drm/drm_user_fence.h @@ -0,0 +1,75 @@ +/* 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 <linux/kref.h> +#include <linux/workqueue.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. + * + * If @mm_ok is true, kthread_use_mm() is active and userspace memory + * (copy_to_user, eventfd_signal, etc.) may be accessed safely. + * If @mm_ok is false, the process MM was already gone; the driver + * should log a warning and skip the userspace write. + * + * wake_up() or other post-signal housekeeping should also happen here. + */ + 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 - embeddable DRM user fence + * + * Drivers embed this in their own structure and implement + * &drm_user_fence_ops. 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 driver teardown. + */ +struct drm_user_fence { + /** @refcount: Reference count. */ + struct kref refcount; + /** @mm: Process MM grabbed at init time. */ + struct mm_struct *mm; + /** @work: Work item queued when the dma-fence signals. */ + struct work_struct work; + /** @cb: dma-fence callback. */ + struct dma_fence_cb cb; + /** + * @fence: Extra reference held for safe cancel(). Set during + * add_callback, released in destroy(). + */ + struct dma_fence *fence; + /** @wq: Workqueue to run @work on. */ + struct workqueue_struct *wq; + /** @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); +void drm_user_fence_get(struct drm_user_fence *ufence); +void drm_user_fence_put(struct drm_user_fence *ufence); +int drm_user_fence_add_callback(struct drm_user_fence *ufence, + struct dma_fence *fence); +bool drm_user_fence_cancel(struct drm_user_fence *ufence); +void drm_user_fence_cancel_sync(struct drm_user_fence *ufence); + +#endif /* __DRM_USER_FENCE_H__ */ -- 2.34.1 ^ permalink raw reply related [flat|nested] 6+ messages in thread
* [PATCH v4 2/2] drm/xe: Convert xe_user_fence to drm_user_fence [not found] <20260828062917.4106569-1-srinivasan.shanmugam@amd.com> 2026-08-28 6:29 ` [PATCH v4 1/2] drm: Add common drm_user_fence helper Srinivasan Shanmugam @ 2026-08-28 6:29 ` Srinivasan Shanmugam 2026-08-28 6:49 ` sashiko-bot 2026-08-28 6:38 ` ✗ CI.checkpatch: warning for series starting with [v4,1/2] drm: Add common drm_user_fence helper Patchwork 2026-08-28 6:38 ` ✗ CI.KUnit: failure " Patchwork 3 siblings, 1 reply; 6+ messages in thread From: Srinivasan Shanmugam @ 2026-08-28 6:29 UTC (permalink / raw) To: matthew.brost Cc: Srinivasan Shanmugam, Thomas Hellström, Rodrigo Vivi, Mika Kuoppala, David Airlie, Simona Vetter, Sumit Semwal, Christian König, Alex Deucher, intel-xe, dri-devel, linux-media, linaro-mm-sig, linux-kernel 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@intel.com> Cc: Thomas Hellström <thomas.hellstrom@linux.intel.com> Cc: Rodrigo Vivi <rodrigo.vivi@intel.com> Cc: Mika Kuoppala <mika.kuoppala@linux.intel.com> Cc: David Airlie <airlied@gmail.com> Cc: Simona Vetter <simona@ffwll.ch> Cc: Sumit Semwal <sumit.semwal@linaro.org> Cc: Christian König <christian.koenig@amd.com> Cc: Alex Deucher <alexander.deucher@amd.com> Cc: intel-xe@lists.freedesktop.org Cc: dri-devel@lists.freedesktop.org Cc: linux-media@vger.kernel.org Cc: linaro-mm-sig@lists.linaro.org Cc: linux-kernel@vger.kernel.org Signed-off-by: Srinivasan Shanmugam <srinivasan.shanmugam@amd.com> --- v4: - Remove drm_user_fence_cancel_sync() from xe_sync_entry_cleanup(). Cancelling the callback there breaks VM_BIND user fences by disarming the completion signal before the GPU finishes. (Sashiko review) drivers/gpu/drm/xe/xe_sync.c | 136 ++++++++++++++--------------- drivers/gpu/drm/xe/xe_sync.h | 1 + drivers/gpu/drm/xe/xe_sync_types.h | 3 +- drivers/gpu/drm/xe/xe_vm.c | 1 + 4 files changed, 70 insertions(+), 71 deletions(-) diff --git a/drivers/gpu/drm/xe/xe_sync.c b/drivers/gpu/drm/xe/xe_sync.c index 37866768d64c..56f8e290424e 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,60 @@ #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); + /* + * Ensure the signalled store is visible before the user memory write + * on weakly ordered architectures (e.g. ARM64). Without this barrier + * the CPU may reorder stores, causing userspace to observe the user + * memory update before signalled == 1. + */ + smp_wmb(); + + if (mm_ok) { + if (copy_to_user(ufence->addr, &ufence->value, sizeof(ufence->value))) + drm_dbg(&ufence->xe->drm, + "copy_to_user failed, user fence wasn't signaled\n"); + } 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 +86,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 +276,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 */ } } @@ -434,6 +419,19 @@ void xe_sync_ufence_put(struct xe_user_fence *ufence) user_fence_put(ufence); } +/** + * xe_sync_ufence_cancel_sync() - Cancel user fence callback and wait for worker + * @ufence: user fence reference + * + * Cancels any pending dma-fence callback and waits for the worker to fully + * complete before returning. Must be called during teardown before freeing + * any resources accessed by the worker. + */ +void xe_sync_ufence_cancel_sync(struct xe_user_fence *ufence) +{ + drm_user_fence_cancel_sync(&ufence->base); +} + /** * xe_sync_ufence_get_status() - Get user fence status * @ufence: user fence @@ -443,4 +441,3 @@ void xe_sync_ufence_put(struct xe_user_fence *ufence) int xe_sync_ufence_get_status(struct xe_user_fence *ufence) { return READ_ONCE(ufence->signalled); -} +} diff --git a/drivers/gpu/drm/xe/xe_sync.h b/drivers/gpu/drm/xe/xe_sync.h index 6b949194acff..ff47882b3f6f 100644 --- a/drivers/gpu/drm/xe/xe_sync.h +++ b/drivers/gpu/drm/xe/xe_sync.h @@ -44,6 +44,7 @@ static inline bool xe_sync_is_ufence(struct xe_sync_entry *sync) struct xe_user_fence *__xe_sync_ufence_get(struct xe_user_fence *ufence); struct xe_user_fence *xe_sync_ufence_get(struct xe_sync_entry *sync); void xe_sync_ufence_put(struct xe_user_fence *ufence); +void xe_sync_ufence_cancel_sync(struct xe_user_fence *ufence); int xe_sync_ufence_get_status(struct xe_user_fence *ufence); #endif diff --git a/drivers/gpu/drm/xe/xe_sync_types.h b/drivers/gpu/drm/xe/xe_sync_types.h index b88f1833e28c..49df2bfa817c 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; @@ -28,4 +27,3 @@ struct xe_sync_entry { u32 flags; }; -#endif +#endif diff --git a/drivers/gpu/drm/xe/xe_vm.c b/drivers/gpu/drm/xe/xe_vm.c index b01f31ed4417..0a1f7615aed9 100644 --- a/drivers/gpu/drm/xe/xe_vm.c +++ b/drivers/gpu/drm/xe/xe_vm.c @@ -1181,6 +1181,7 @@ static void xe_vma_destroy_late(struct xe_vma *vma) struct xe_bo *bo = xe_vma_bo(vma); if (vma->ufence) { + xe_sync_ufence_cancel_sync(vma->ufence); xe_sync_ufence_put(vma->ufence); vma->ufence = NULL; } -- 2.34.1 ^ permalink raw reply related [flat|nested] 6+ messages in thread
* Re: [PATCH v4 2/2] drm/xe: Convert xe_user_fence to drm_user_fence 2026-08-28 6:29 ` [PATCH v4 2/2] drm/xe: Convert xe_user_fence to drm_user_fence Srinivasan Shanmugam @ 2026-08-28 6:49 ` sashiko-bot 0 siblings, 0 replies; 6+ messages in thread From: sashiko-bot @ 2026-08-28 6:49 UTC (permalink / raw) To: Srinivasan Shanmugam; +Cc: intel-xe, media-ci, dri-devel Thank you for your contribution! Sashiko AI review found 2 potential issue(s) to consider: - [Critical] AB-BA deadlock between `dma_resv_lock` and page fault handling (`mmap_lock`) due to synchronous work cancellation in `xe_vma_destroy_late` - [Low] Missing `#endif` include guard in `xe_sync_types.h` causes compilation error -- commit b5c1e8acd90810b219cbad1a85281b1b796fc3ea Author: Srinivasan Shanmugam <srinivasan.shanmugam@amd.com> drm/xe: Convert xe_user_fence to drm_user_fence This commit replaces the open-coded user fence implementation in xe_sync.c with the new common drm_user_fence helper. The xe_user_fence structure now embeds drm_user_fence, and lifecycle functions are moved to the new ops structure. > diff --git a/drivers/gpu/drm/xe/xe_sync.c b/drivers/gpu/drm/xe/xe_sync.c > index 37866768d64c2..f2eb66b8fe54d 100644 > --- a/drivers/gpu/drm/xe/xe_sync.c > +++ b/drivers/gpu/drm/xe/xe_sync.c [ ... ] > @@ -19,36 +18,60 @@ [ ... ] > +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); [ ... ] > + if (mm_ok) { > + if (copy_to_user(ufence->addr, &ufence->value, sizeof(ufence->value))) [Severity: Critical] Could this copy_to_user() trigger a page fault that attempts to re-acquire the dma_resv_lock? If a userspace application mmaps a BO, creates a user fence pointing into that mapped BO address, and binds it via VM_BIND, this worker will target the mapped BO. When this occurs concurrently with VMA teardown, it appears to lead to a deadlock. See below. [ ... ] > diff --git a/drivers/gpu/drm/xe/xe_sync_types.h b/drivers/gpu/drm/xe/xe_sync_types.h > index b88f1833e28cc..2a75f998558e6 100644 > --- a/drivers/gpu/drm/xe/xe_sync_types.h > +++ b/drivers/gpu/drm/xe/xe_sync_types.h > @@ -28,4 +27,3 @@ struct xe_sync_entry { > u32 flags; > }; > > -#endif [Severity: Low] Does this missing include guard cause a build failure? It looks like the #endif for #ifndef _XE_SYNC_TYPES_H_ was accidentally deleted, which will result in an unterminated #ifndef compiler error for any C file including this header. > diff --git a/drivers/gpu/drm/xe/xe_vm.c b/drivers/gpu/drm/xe/xe_vm.c > index 19b3d0be79282..ef0784601f226 100644 > --- a/drivers/gpu/drm/xe/xe_vm.c > +++ b/drivers/gpu/drm/xe/xe_vm.c > @@ -1214,6 +1214,7 @@ static void xe_vma_destroy_late(struct xe_vma *vma) > struct xe_bo *bo = xe_vma_bo(vma); > > if (vma->ufence) { > + xe_sync_ufence_cancel_sync(vma->ufence); [Severity: Critical] Does this synchronous cancellation create an AB-BA deadlock? During VMA teardown, xe_vma_destroy_unlocked() acquires the BO's dma_resv_lock (and vm->lock) and then synchronously waits for the user fence worker to finish via cancel_work_sync() inside xe_sync_ufence_cancel_sync(). If the worker is concurrently executing copy_to_user() (as noted above) and triggers a page fault, the page fault handler (xe_gem_fault) will attempt to acquire the same dma_resv_lock to resolve the fault. Since the teardown thread already holds this lock while waiting for the worker, the two threads will deadlock. -- Sashiko AI review · https://sashiko.dev/#/patchset/20260828062917.4106569-1-srinivasan.shanmugam@amd.com?part=2 ^ permalink raw reply [flat|nested] 6+ messages in thread
* ✗ CI.checkpatch: warning for series starting with [v4,1/2] drm: Add common drm_user_fence helper [not found] <20260828062917.4106569-1-srinivasan.shanmugam@amd.com> 2026-08-28 6:29 ` [PATCH v4 1/2] drm: Add common drm_user_fence helper Srinivasan Shanmugam 2026-08-28 6:29 ` [PATCH v4 2/2] drm/xe: Convert xe_user_fence to drm_user_fence Srinivasan Shanmugam @ 2026-08-28 6:38 ` Patchwork 2026-08-28 6:38 ` ✗ CI.KUnit: failure " Patchwork 3 siblings, 0 replies; 6+ messages in thread From: Patchwork @ 2026-08-28 6:38 UTC (permalink / raw) To: Srinivasan Shanmugam; +Cc: intel-xe == Series Details == Series: series starting with [v4,1/2] drm: Add common drm_user_fence helper URL : https://patchwork.freedesktop.org/series/172929/ State : warning == Summary == + KERNEL=/kernel + git clone https://gitlab.freedesktop.org/drm/maintainer-tools mt Cloning into 'mt'... warning: redirecting to https://gitlab.freedesktop.org/drm/maintainer-tools.git/ + git -C mt rev-list -n1 origin/master 061140b9bc586ae7f40abc1249c97e1cc72d1b9d + cd /kernel + git config --global --add safe.directory /kernel + git log -n1 commit 4397039113f167e2277eb911c8d4a8af4792251c Author: Srinivasan Shanmugam <srinivasan.shanmugam@amd.com> Date: Fri Aug 28 11:59:17 2026 +0530 drm/xe: Convert xe_user_fence to drm_user_fence 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@intel.com> Cc: Thomas Hellström <thomas.hellstrom@linux.intel.com> Cc: Rodrigo Vivi <rodrigo.vivi@intel.com> Cc: Mika Kuoppala <mika.kuoppala@linux.intel.com> Cc: David Airlie <airlied@gmail.com> Cc: Simona Vetter <simona@ffwll.ch> Cc: Sumit Semwal <sumit.semwal@linaro.org> Cc: Christian König <christian.koenig@amd.com> Cc: Alex Deucher <alexander.deucher@amd.com> Cc: intel-xe@lists.freedesktop.org Cc: dri-devel@lists.freedesktop.org Cc: linux-media@vger.kernel.org Cc: linaro-mm-sig@lists.linaro.org Cc: linux-kernel@vger.kernel.org Signed-off-by: Srinivasan Shanmugam <srinivasan.shanmugam@amd.com> + /mt/dim checkpatch 5901f93eb5a007e8b7c21ca2b5bfae5958467832 drm-intel 9af9e9715c22 drm: Add common drm_user_fence helper -:61: WARNING:FILE_PATH_CHANGES: added, moved or deleted file(s), does MAINTAINERS need updating? #61: new file mode 100644 total: 0 errors, 1 warnings, 0 checks, 305 lines checked 4397039113f1 drm/xe: Convert xe_user_fence to drm_user_fence ^ permalink raw reply [flat|nested] 6+ messages in thread
* ✗ CI.KUnit: failure for series starting with [v4,1/2] drm: Add common drm_user_fence helper [not found] <20260828062917.4106569-1-srinivasan.shanmugam@amd.com> ` (2 preceding siblings ...) 2026-08-28 6:38 ` ✗ CI.checkpatch: warning for series starting with [v4,1/2] drm: Add common drm_user_fence helper Patchwork @ 2026-08-28 6:38 ` Patchwork 3 siblings, 0 replies; 6+ messages in thread From: Patchwork @ 2026-08-28 6:38 UTC (permalink / raw) To: Srinivasan Shanmugam; +Cc: intel-xe == Series Details == Series: series starting with [v4,1/2] drm: Add common drm_user_fence helper URL : https://patchwork.freedesktop.org/series/172929/ State : failure == Summary == + trap cleanup EXIT + /kernel/tools/testing/kunit/kunit.py run --kunitconfig /kernel/drivers/gpu/drm/xe/.kunitconfig ERROR:root:In file included from ../drivers/gpu/drm/xe/xe_sync.h:9, from ../drivers/gpu/drm/xe/xe_exec.c:21: ../drivers/gpu/drm/xe/xe_sync_types.h:6: error: unterminated #ifndef 6 | #ifndef _XE_SYNC_TYPES_H_ | make[7]: *** [../scripts/Makefile.build:289: drivers/gpu/drm/xe/xe_exec.o] Error 1 make[7]: *** Waiting for unfinished jobs.... make[6]: *** [../scripts/Makefile.build:549: drivers/gpu/drm/xe] Error 2 make[6]: *** Waiting for unfinished jobs.... make[5]: *** [../scripts/Makefile.build:549: drivers/gpu/drm] Error 2 make[4]: *** [../scripts/Makefile.build:549: drivers/gpu] Error 2 make[4]: *** Waiting for unfinished jobs.... make[3]: *** [../scripts/Makefile.build:549: drivers] Error 2 make[3]: *** Waiting for unfinished jobs.... make[2]: *** [/kernel/Makefile:2187: .] Error 2 make[1]: *** [/kernel/Makefile:248: __sub-make] Error 2 make: *** [Makefile:248: __sub-make] Error 2 [06:38:13] Configuring KUnit Kernel ... Generating .config ... Populating config with: $ make ARCH=um O=.kunit olddefconfig [06:38:17] Building KUnit Kernel ... Populating config with: $ make ARCH=um O=.kunit olddefconfig Building with: $ make all compile_commands.json scripts_gdb ARCH=um O=.kunit --jobs=48 + cleanup ++ stat -c %u:%g /kernel + chown -R 1003:1003 /kernel ^ permalink raw reply [flat|nested] 6+ messages in thread
[parent not found: <20260828063103.4106629-1-srinivasan.shanmugam@amd.com>]
* [PATCH v4 2/2] drm/xe: Convert xe_user_fence to drm_user_fence [not found] <20260828063103.4106629-1-srinivasan.shanmugam@amd.com> @ 2026-08-28 6:31 ` Srinivasan Shanmugam 0 siblings, 0 replies; 6+ messages in thread From: Srinivasan Shanmugam @ 2026-08-28 6:31 UTC (permalink / raw) To: matthew.brost Cc: Srinivasan Shanmugam, Thomas Hellström, Rodrigo Vivi, Mika Kuoppala, David Airlie, Simona Vetter, Sumit Semwal, Christian König, Alex Deucher, intel-xe, dri-devel, linux-media, linaro-mm-sig, linux-kernel 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@intel.com> Cc: Thomas Hellström <thomas.hellstrom@linux.intel.com> Cc: Rodrigo Vivi <rodrigo.vivi@intel.com> Cc: Mika Kuoppala <mika.kuoppala@linux.intel.com> Cc: David Airlie <airlied@gmail.com> Cc: Simona Vetter <simona@ffwll.ch> Cc: Sumit Semwal <sumit.semwal@linaro.org> Cc: Christian König <christian.koenig@amd.com> Cc: Alex Deucher <alexander.deucher@amd.com> Cc: intel-xe@lists.freedesktop.org Cc: dri-devel@lists.freedesktop.org Cc: linux-media@vger.kernel.org Cc: linaro-mm-sig@lists.linaro.org Cc: linux-kernel@vger.kernel.org Signed-off-by: Srinivasan Shanmugam <srinivasan.shanmugam@amd.com> --- v4: - Remove drm_user_fence_cancel_sync() from xe_sync_entry_cleanup(). Cancelling the callback there breaks VM_BIND user fences by disarming the completion signal before the GPU finishes. (Sashiko review) drivers/gpu/drm/xe/xe_sync.c | 136 ++++++++++++++--------------- drivers/gpu/drm/xe/xe_sync.h | 1 + drivers/gpu/drm/xe/xe_sync_types.h | 3 +- drivers/gpu/drm/xe/xe_vm.c | 1 + 4 files changed, 70 insertions(+), 71 deletions(-) diff --git a/drivers/gpu/drm/xe/xe_sync.c b/drivers/gpu/drm/xe/xe_sync.c index 37866768d64c..56f8e290424e 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,60 @@ #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); + /* + * Ensure the signalled store is visible before the user memory write + * on weakly ordered architectures (e.g. ARM64). Without this barrier + * the CPU may reorder stores, causing userspace to observe the user + * memory update before signalled == 1. + */ + smp_wmb(); + + if (mm_ok) { + if (copy_to_user(ufence->addr, &ufence->value, sizeof(ufence->value))) + drm_dbg(&ufence->xe->drm, + "copy_to_user failed, user fence wasn't signaled\n"); + } 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 +86,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 +276,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 */ } } @@ -434,6 +419,19 @@ void xe_sync_ufence_put(struct xe_user_fence *ufence) user_fence_put(ufence); } +/** + * xe_sync_ufence_cancel_sync() - Cancel user fence callback and wait for worker + * @ufence: user fence reference + * + * Cancels any pending dma-fence callback and waits for the worker to fully + * complete before returning. Must be called during teardown before freeing + * any resources accessed by the worker. + */ +void xe_sync_ufence_cancel_sync(struct xe_user_fence *ufence) +{ + drm_user_fence_cancel_sync(&ufence->base); +} + /** * xe_sync_ufence_get_status() - Get user fence status * @ufence: user fence @@ -443,4 +441,3 @@ void xe_sync_ufence_put(struct xe_user_fence *ufence) int xe_sync_ufence_get_status(struct xe_user_fence *ufence) { return READ_ONCE(ufence->signalled); -} +} diff --git a/drivers/gpu/drm/xe/xe_sync.h b/drivers/gpu/drm/xe/xe_sync.h index 6b949194acff..ff47882b3f6f 100644 --- a/drivers/gpu/drm/xe/xe_sync.h +++ b/drivers/gpu/drm/xe/xe_sync.h @@ -44,6 +44,7 @@ static inline bool xe_sync_is_ufence(struct xe_sync_entry *sync) struct xe_user_fence *__xe_sync_ufence_get(struct xe_user_fence *ufence); struct xe_user_fence *xe_sync_ufence_get(struct xe_sync_entry *sync); void xe_sync_ufence_put(struct xe_user_fence *ufence); +void xe_sync_ufence_cancel_sync(struct xe_user_fence *ufence); int xe_sync_ufence_get_status(struct xe_user_fence *ufence); #endif diff --git a/drivers/gpu/drm/xe/xe_sync_types.h b/drivers/gpu/drm/xe/xe_sync_types.h index b88f1833e28c..49df2bfa817c 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; @@ -28,4 +27,3 @@ struct xe_sync_entry { u32 flags; }; -#endif +#endif diff --git a/drivers/gpu/drm/xe/xe_vm.c b/drivers/gpu/drm/xe/xe_vm.c index b01f31ed4417..0a1f7615aed9 100644 --- a/drivers/gpu/drm/xe/xe_vm.c +++ b/drivers/gpu/drm/xe/xe_vm.c @@ -1181,6 +1181,7 @@ static void xe_vma_destroy_late(struct xe_vma *vma) struct xe_bo *bo = xe_vma_bo(vma); if (vma->ufence) { + xe_sync_ufence_cancel_sync(vma->ufence); xe_sync_ufence_put(vma->ufence); vma->ufence = NULL; } -- 2.34.1 ^ permalink raw reply related [flat|nested] 6+ messages in thread
end of thread, other threads:[~2026-08-28 6:49 UTC | newest]
Thread overview: 6+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
[not found] <20260828062917.4106569-1-srinivasan.shanmugam@amd.com>
2026-08-28 6:29 ` [PATCH v4 1/2] drm: Add common drm_user_fence helper Srinivasan Shanmugam
2026-08-28 6:29 ` [PATCH v4 2/2] drm/xe: Convert xe_user_fence to drm_user_fence Srinivasan Shanmugam
2026-08-28 6:49 ` sashiko-bot
2026-08-28 6:38 ` ✗ CI.checkpatch: warning for series starting with [v4,1/2] drm: Add common drm_user_fence helper Patchwork
2026-08-28 6:38 ` ✗ CI.KUnit: failure " Patchwork
[not found] <20260828063103.4106629-1-srinivasan.shanmugam@amd.com>
2026-08-28 6:31 ` [PATCH v4 2/2] drm/xe: Convert xe_user_fence to drm_user_fence Srinivasan Shanmugam
This is a public inbox, see mirroring instructions for how to clone and mirror all data and code used for this inbox