* [PATCH 1/2] rust: DmaFence: Don't drop on signal
2026-11-02 15:06 [PATCH 0/2] rust: Adjust dma_buf::DriverFence::drop() Philipp Stanner
@ 2026-11-02 15:06 ` Philipp Stanner
2026-11-02 15:06 ` [PATCH 2/2] rust: DmaFence: Replace call_rcu() with synchronize_rcu() Philipp Stanner
1 sibling, 0 replies; 3+ messages in thread
From: Philipp Stanner @ 2026-11-02 15:06 UTC (permalink / raw)
To: Danilo Krummrich, Alice Ryhl, Sumit Semwal, Christian König,
Philipp Stanner, Miguel Ojeda, Boqun Feng, Gary Guo,
Björn Roy Baron, Benno Lossin, Andreas Hindborg,
Trevor Gross, Daniel Almeida, Tamir Duberstein, Alexandre Courbot,
Onur Özkan
Cc: linux-media, dri-devel, rust-for-linux, linux-kernel
The original design for DmaFence intended to comply as strictly as
possible with the "dma_fence contract", which, according to some, says
that a fence must only be signaled once. Thus, the implementation was
made so that a DriverFence drops on signal.
While extending JobQueue's capabilities, it was discovered that this
characteristic causes - unnecessary - trouble: fence-data must be
dropped in an RCU-deferred manner because the C backend demands that.
However, fences are often signaled in atomic context (e.g., interrupt
handler) where you don't want to drop, but signal as quickly as
possible.
Actually, there is no real hard reason to enforce that a fence can only
get signaled once, because the C backend is robust against multiple
signal attempts.
What is decisive is that a) all fences signal and b) they only drop no
earlier than 1 RCU grace period after signaling.
Don't drop a DriverFence on signal().
Signed-off-by: Philipp Stanner <phasta@kernel.org>
---
rust/kernel/dma_buf/dma_fence.rs | 11 ++++++-----
1 file changed, 6 insertions(+), 5 deletions(-)
diff --git a/rust/kernel/dma_buf/dma_fence.rs b/rust/kernel/dma_buf/dma_fence.rs
index a1d851656562..3022bdb5f7c3 100644
--- a/rust/kernel/dma_buf/dma_fence.rs
+++ b/rust/kernel/dma_buf/dma_fence.rs
@@ -805,7 +805,7 @@ pub fn as_fence(&self) -> &Fence {
}
/// Signal the fence. This will invoke all registered callbacks.
- pub fn signal(self, res: Result) {
+ pub fn signal(&self, res: Result) {
let fence = self.as_fence().lock();
// SAFETY: `fence` is valid because `self` is valid. The lock must be
@@ -816,14 +816,15 @@ pub fn signal(self, res: Result) {
// must not have been signaled yet, which we check directly above.
unsafe { bindings::dma_fence_set_error(fence.as_raw(), err.to_errno()) };
}
+
+ // SAFETY: `self.data` is valid because `self` is valid.
+ let fctx = unsafe { self.data.as_ref().fctx };
+ let _ = fctx.nr_of_unsignaled_fences.fetch_sub(1, Relaxed);
+
// SAFETY: `fence` is valid because `self` is valid. The lock must
// be held, which we acquired above.
unsafe { bindings::dma_fence_signal_locked(fence.as_raw()) };
}
-
- // SAFETY: `self.data` is valid because `self` is valid.
- let fctx = unsafe { self.data.as_ref().fctx };
- let _ = fctx.nr_of_unsignaled_fences.fetch_sub(1, Relaxed);
}
}
--
2.56.0
^ permalink raw reply related [flat|nested] 3+ messages in thread* [PATCH 2/2] rust: DmaFence: Replace call_rcu() with synchronize_rcu()
2026-11-02 15:06 [PATCH 0/2] rust: Adjust dma_buf::DriverFence::drop() Philipp Stanner
2026-11-02 15:06 ` [PATCH 1/2] rust: DmaFence: Don't drop on signal Philipp Stanner
@ 2026-11-02 15:06 ` Philipp Stanner
1 sibling, 0 replies; 3+ messages in thread
From: Philipp Stanner @ 2026-11-02 15:06 UTC (permalink / raw)
To: Danilo Krummrich, Alice Ryhl, Sumit Semwal, Christian König,
Philipp Stanner, Miguel Ojeda, Boqun Feng, Gary Guo,
Björn Roy Baron, Benno Lossin, Andreas Hindborg,
Trevor Gross, Daniel Almeida, Tamir Duberstein, Alexandre Courbot,
Onur Özkan
Cc: linux-media, dri-devel, rust-for-linux, linux-kernel
The C dma_fence contract demands that a fence's data disappears no
earlier than 1 RCU grace period after the DriverFence was signaled.
So far, we achieved this with dropping the DriverFence's data in a
deferred manner with a manual, raw binding call to call_rcu().
However, this call_rcu() does not solve one problem: It is conceivable
that some driver data must not drop in atomic context; but call_rcu()'s
payload *can* be executed in atomic context.
Moreover, the current developments in drm::JobQueue, where a DriverFence
is always coupled 1:1 with a Job, allows for dropping job and fence
through a work item.
The undesirable blocking behavior of synchronize_rcu() would, thus, not
be annoying any thread anymore. The direct users of DmaFence could
achieve the same by dropping their DriverFences through work items.
The only conceivable other solution, queue_rcu_work(), would, for many
users, schedule a work_item from another work_item, which seems
undesirable.
Additionally, using the existing synchronize_rcu() abstraction has the
advantage of us getting rid of a raw bindings call.
Replace DriverFence::drop()'s call_rcu() with synchronize_rcu().
Signed-off-by: Philipp Stanner <phasta@kernel.org>
---
rust/kernel/dma_buf/dma_fence.rs | 89 ++++++++++----------------------
1 file changed, 26 insertions(+), 63 deletions(-)
diff --git a/rust/kernel/dma_buf/dma_fence.rs b/rust/kernel/dma_buf/dma_fence.rs
index 3022bdb5f7c3..ad9cebf8428c 100644
--- a/rust/kernel/dma_buf/dma_fence.rs
+++ b/rust/kernel/dma_buf/dma_fence.rs
@@ -42,7 +42,10 @@
Atomic,
Relaxed, //
},
- rcu::rcu_barrier, //
+ rcu::{
+ rcu_barrier,
+ synchronize_rcu, //
+ }, //
}, //
};
@@ -65,7 +68,7 @@ pub struct FenceContext<T: FenceContextOps + Send + Sync> {
/// The sequence number for the next fence created.
seqno: Atomic<u64>,
// The name parameters can be accessed by the dma_fence backend_ops. UAF
- // errors are prevented by the `call_rcu()` in `drop_driver_fence_data()`.
+ // errors are prevented by the `synchronize_rcu()` in `drop_driver_fence_data()`.
/// The name of the driver this FenceContext's fences belong to.
driver_name: CString,
/// The name of the timeline this FenceContext's fences belong to.
@@ -142,7 +145,6 @@ pub fn new_fence_allocation(
data: T::FenceDataType,
) -> Result<DriverFenceAllocation<'_, T>> {
let fence_data = DriverFenceData {
- rcu_head: Default::default(),
// `inner` remains uninitialized until a `DriverFence` takes over.
inner: Fence {
inner: Opaque::uninit(),
@@ -600,8 +602,6 @@ struct DriverFenceData<'a, T: Send + Sync + FenceContextOps> {
// necessary so that the C backend can free the allocation (coming from our
// Rust code) with kfree_rcu().
inner: Fence,
- /// Callback head for dropping this in a deferred manner through RCU.
- rcu_head: bindings::callback_head,
/// Reference to access the FenceContext.
fctx: &'a FenceContext<T>,
/// The API user's data. It is essential that the data only performs
@@ -644,6 +644,10 @@ struct DriverFenceData<'a, T: Send + Sync + FenceContextOps> {
///
/// In the unlikely case of such violations, a panic is thrown.
///
+/// For requirements imposed by the C backend, a [`DriverFence`] might perform
+/// a call to [`rcu::synchronize_rcu`] when it drops. Thus, avoid dropping a
+/// [`DriverFence`] in atomic context.
+///
/// # Examples
///
/// ```
@@ -940,15 +944,15 @@ fn drop(&mut self) {
// dma_fence_is_signaled_locked() because the C backend wants to get rid
// of the latter.
- // SAFETY: `guard` is valid until the `call_rcu()` below.
+ // SAFETY: `guard` is valid.
let signaled: bool = unsafe { bindings::dma_fence_test_signaled_flag(guard.as_raw()) };
if !signaled {
pr_err!("DriverFence drops unsignaled. Danger of memory corruption!\n");
- // SAFETY: `guard` is valid until the `call_rcu()` below. The fence
- // must not have been signaled yet, which we check directly above.
+ // SAFETY: `guard` is valid. The fence must not have been signaled
+ // yet, which we check directly above.
unsafe { bindings::dma_fence_set_error(guard.as_raw(), ECANCELED.to_errno()) };
- // SAFETY: `guard` is valid until the `call_rcu()` below. The lock
- // must be held, which we acquired above.
+ // SAFETY: `guard` is valid. The lock must be held, which we
+ // acquired above.
unsafe { bindings::dma_fence_signal_locked(guard.as_raw()) };
// SAFETY: `self.data` is valid because `self` is valid.
@@ -973,59 +977,18 @@ fn drop(&mut self) {
return;
}
- // SAFETY: Valid because `self` is valid.
- let rcu_head_ptr = unsafe { &raw mut (*self.data.as_ptr()).rcu_head };
+ // Make sure none of the fence backend_ops can access data anymore.
+ //
+ // TODO:
+ // This would not be necessary if the C dma_fence backend were using a
+ // spinlock to properly synchronize its signaled state. Fix it in C and
+ // then remove synchronize_rcu().
+ synchronize_rcu();
- // SAFETY: `call_rcu()` is always safe to be called. `rcu_head_ptr` was
- // created validly above. The module must perform a `synchronize_rcu()`
- // or `rcu_barrier()` call to guard against module unload.
- unsafe { bindings::call_rcu(rcu_head_ptr, Some(drop_driver_fence_data::<T>)) };
+ // SAFETY: Valid because `self` is valid.
+ unsafe { drop_in_place(&raw mut self.data) };
+
+ // SAFETY: The `synchronize_rcu()` above ensures all accessors are gone.
+ unsafe { bindings::dma_fence_put(self.as_raw()) };
}
}
-
-// TODO:
-// The entire call_rcu() mechanism in the drop above and the code below would be
-// unnecessary if C's dma_fence_signal() could be reworked in a way that after it
-// ran, the caller knows that no fence_ops callbacks can be running anymore.
-// In other words, if the dma_fence backend would use its spinlock for full
-// synchronization.
-//
-// Then we could move the drop_in_place() and dma_fence_put() upwards into the
-// drop() implementation and call it a day.
-
-/// Finally really drop this `DriverFence<T>`
-///
-/// # Safety
-///
-/// `head` references the `rcu_head` field of an `DriverFenceData<T>`. All
-/// accessors to that `DriverFenceData<T>` must be gone by now. This must be
-/// ensured by signalling the associated `DriverFence<T>` and then waiting
-/// for a grace period until calling this function here.
-unsafe extern "C" fn drop_driver_fence_data<T: Send + Sync + FenceContextOps>(
- head: *mut bindings::callback_head,
-) {
- // SAFETY: Caller provides a pointer to the `rcu_head` field of a `DriverFenceData<C>`.
- let fence_data = unsafe { container_of!(head, DriverFenceData<'_, T>, rcu_head) };
-
- // SAFETY: `fence_data` was created validly above. All the fence's data will
- // only drop below, but the raw pointer to the raw C `dma_fence` remains
- // valid because the reference count is only decremented at the end of the
- // function.
- let fence = unsafe { (*fence_data).inner.inner.get() };
-
- // SAFETY: `fence_data` was created validly above. The user has already
- // dropped the only conventional accessor to the user data, the `DriverFence`,
- // one grace period ago. All accessors are gone now.
- unsafe { drop_in_place(&raw mut (*fence_data).data) };
-
- // The inner `Fence` explicitly does not get dropped because there may be
- // many more users / consumers, each holding their own reference.
-
- // SAFETY: Once a `DriverFence` is initialized, the inner `fence` is valid
- // and initialized. It is valid until the refcount drops to 0, which can
- // earliest happen once we drop the `DriverFence`'s reference here.
- unsafe { bindings::dma_fence_put(fence) };
-
- // The actual memory the data associated with a `DriverFence` lives in
- // gets freed by the C dma_fence backend once the fence's refcount reaches 0.
-}
--
2.56.0
^ permalink raw reply related [flat|nested] 3+ messages in thread