All of lore.kernel.org
 help / color / mirror / Atom feed
* [PATCH v2 0/6] rust / dma_buf: Add abstractions for dma_fence
@ 2026-06-16  8:28 Philipp Stanner
  2026-06-16  8:28 ` [PATCH v2 1/6] rust: types: implement ForeignOwnable for ARef<T> Philipp Stanner
                   ` (6 more replies)
  0 siblings, 7 replies; 19+ messages in thread
From: Philipp Stanner @ 2026-06-16  8:28 UTC (permalink / raw)
  To: Miguel Ojeda, Boqun Feng, Gary Guo, Björn Roy Baron,
	Benno Lossin, Andreas Hindborg, Alice Ryhl, Trevor Gross,
	Danilo Krummrich, Sumit Semwal, Christian König,
	Philipp Stanner, Paul E. McKenney, Frederic Weisbecker,
	Neeraj Upadhyay, Joel Fernandes, Josh Triplett, Uladzislau Rezki,
	Steven Rostedt, Mathieu Desnoyers, Lai Jiangshan, Zqiang,
	Daniel Almeida, Greg Kroah-Hartman, Igor Korotin, Lorenzo Stoakes,
	Alexandre Courbot, FUJITA Tomonori, Krishna Ketan Rai,
	Shankari Anand, manos, Boris Brezillon
  Cc: linux-kernel, rust-for-linux, linux-media, dri-devel, rcu

New version. Most significant change is regarding RCU. See changelog
below. Also see [1].

Changes since v1:
  - Remove unnecessary mutable references (Alice)
  - Split up unsafe comments where possible (Danilo)
  - Remove PhantomData + implement FenceCtx ops trait (Boris)
  - Consistently call FenceCtx generic data `T`. FenceDataType is
    derived from that. (Boris)
  - Add abstractions for call_rcu() and synchronize_rcu() (Danilo)
  - Add ECANCELED error code in Rust (Alice)
  - Remove the rcu_barrier() from FenceCtx::drop() – because we now use
    call_rcu(), there can be no UAF access to the FenceCtx anymore. In
    any case, it is illegal to use either call_rcu() or
    synchronize_rcu() in FenceCtx::drop(), because our new
    drop_driver_fence_data() can run in atomic context and might put the
    last fence_ctx reference.
    So we now only have to guard against module unload, which it seems
    either the driver or Rust driver-core / module unload infrastructure
    must solve.
  - Minor formatting etc. changes
  - Add C helpers to MAINTAINERS. (Danilo)
  - Ensure that `Fence::is_signaled()` is fully synchronized, i.e., all
    callbacks really have run. See [1] and [2]. (Myself, Christian
    König)

Changes since the RFCs:
  - Include support for ForeignOwnable for ARef, so that a Fence can be
    stuffed into an XArray et al. (Code by Danilo)
  - Implement ForeignOwnable (with new borrow type) for DriverFence, so
    that it can be stuffed into an XArray.
  - Include the rcu::RcuBox data type to defer dropping data with RCU
    (Cody by Alice)
  - Port DmaFence to RcuBox to make UAF bugs through later, new dma_fence
    callbacks (backend_ops) impossible.
  - Force users to pass their fence data in an RcuBox (or have it not
    need drop()) through a Sealed trait.
  - Document the rules for the user's DriverFence::data's drop
    implementation very clearly (deadlock danger).
  - rustfmt, Clippy.
  - Various style suggestions, safety comments, etc. (Önur)
  - Add __rust_helper prefix to helper functions. (Önur)

Changes in RFC v3:
  - Omit JobQueue patches for now
  - Completely redesign the memory layout: Instead of a Fence
    refcounting a DriverFence, both now live in the same allocation to
    allow for future support the dma_fence backend_ops callbacks which
    need to do container_of. (mostly Boris's feedback)
  - Allow for pre-allocating fences to avoid deadlocks when submitting
    jobs to a GPU. (Boris)
  - Simultaneously, allow for pre-preparing fence callback objects, so
    the driver can allocate them when it sees fit. (code largely stolen
    and inspired by Daniel).
  - Signal fences on drop, ensure synchronization.
  - Force users to set an error code when signalling.
  - Write more documentation
  - A ton of minor other changes.


[1] https://lore.kernel.org/dri-devel/20260608142436.265820-2-phasta@kernel.org/
[2] https://lore.kernel.org/dri-devel/20260612104251.2264707-2-phasta@kernel.org/


Alright, so since the last RFCs did not reveal significant design
issues, I decided to transition this series to a v1 and hope that we can
get it upstream.

This now includes code for more common infrastructure that dma_fence
needs, contributed by Danilo and Alice.

---

Old cover letter for RFC:

So, this is the spiritual successor of the first / second RFC [1]. v2
also contained code for drm::JobQueue, but mostly to show how the fence
code would be used. JobQueue is under heavy rework right now, so I don't
want to bother your eyes with it. The docstring examples should show how
Rust fences are supposed to be used, though.

This v3 contains a huge amount of highly valuable feedback from a
variety of people, notably Boris, but also from Alice, Gary and Danilo.

There are some TODOs open (a better trait for fence backend_ops and RCU
support), but my hope is that this effort is now finally approaching its
end.

I would greatly appreciate feedback and especially more information
about what might be missing to make this usable, which is obviously
where Daniel's and Boris's feedback will be valuable once more.

Please regard this patch just as what it's titled: an RFC, to discuss a
bit more and to inform a broader community about what the current state
is and where this is heading at.

Many regards,
Philipp

[1] https://lore.kernel.org/rust-for-linux/20260203081403.68733-2-phasta@kernel.org/

Danilo Krummrich (1):
  rust: types: implement ForeignOwnable for ARef<T>

Philipp Stanner (5):
  rust: sync: Add abstraction for rcu_barrier()
  rust: sync: Add abstraction for synchronize_rcu()
  rust: error: Add ECANCELED error code
  rust: Add dma_fence abstractions
  MAINTAINERS: Add entry for Rust dma-buf

 MAINTAINERS                      |   3 +
 rust/bindings/bindings_helper.h  |   1 +
 rust/helpers/dma_fence.c         |  48 ++
 rust/helpers/helpers.c           |   1 +
 rust/kernel/dma_buf/dma_fence.rs | 884 +++++++++++++++++++++++++++++++
 rust/kernel/dma_buf/mod.rs       |  14 +
 rust/kernel/error.rs             |   1 +
 rust/kernel/lib.rs               |   1 +
 rust/kernel/sync/aref.rs         |  39 ++
 rust/kernel/sync/rcu.rs          |  14 +
 10 files changed, 1006 insertions(+)
 create mode 100644 rust/helpers/dma_fence.c
 create mode 100644 rust/kernel/dma_buf/dma_fence.rs
 create mode 100644 rust/kernel/dma_buf/mod.rs

-- 
2.54.0


^ permalink raw reply	[flat|nested] 19+ messages in thread

* [PATCH v2 1/6] rust: types: implement ForeignOwnable for ARef<T>
  2026-06-16  8:28 [PATCH v2 0/6] rust / dma_buf: Add abstractions for dma_fence Philipp Stanner
@ 2026-06-16  8:28 ` Philipp Stanner
  2026-06-16  8:38   ` sashiko-bot
  2026-06-16  8:28 ` [PATCH v2 2/6] rust: sync: Add abstraction for rcu_barrier() Philipp Stanner
                   ` (5 subsequent siblings)
  6 siblings, 1 reply; 19+ messages in thread
From: Philipp Stanner @ 2026-06-16  8:28 UTC (permalink / raw)
  To: Miguel Ojeda, Boqun Feng, Gary Guo, Björn Roy Baron,
	Benno Lossin, Andreas Hindborg, Alice Ryhl, Trevor Gross,
	Danilo Krummrich, Sumit Semwal, Christian König,
	Philipp Stanner, Paul E. McKenney, Frederic Weisbecker,
	Neeraj Upadhyay, Joel Fernandes, Josh Triplett, Uladzislau Rezki,
	Steven Rostedt, Mathieu Desnoyers, Lai Jiangshan, Zqiang,
	Daniel Almeida, Greg Kroah-Hartman, Igor Korotin, Lorenzo Stoakes,
	Alexandre Courbot, FUJITA Tomonori, Krishna Ketan Rai,
	Shankari Anand, manos, Boris Brezillon
  Cc: linux-kernel, rust-for-linux, linux-media, dri-devel, rcu

From: Danilo Krummrich <dakr@kernel.org>

Implement ForeignOwnable for ARef<T>, making it possible for C code to
own an ARef<T>.

Since ARef represents shared ownership, BorrowedMut is &T rather than
&mut T, matching the semantics of the underlying reference-counted type.

Signed-off-by: Danilo Krummrich <dakr@kernel.org>
---
 rust/kernel/sync/aref.rs | 39 +++++++++++++++++++++++++++++++++++++++
 1 file changed, 39 insertions(+)

diff --git a/rust/kernel/sync/aref.rs b/rust/kernel/sync/aref.rs
index 9989f56d0605..82907383c44b 100644
--- a/rust/kernel/sync/aref.rs
+++ b/rust/kernel/sync/aref.rs
@@ -17,6 +17,10 @@
 //! [`Arc`]: crate::sync::Arc
 //! [`Arc<T>`]: crate::sync::Arc
 
+use crate::{
+    prelude::*,
+    types::ForeignOwnable, //
+};
 use core::{marker::PhantomData, mem::ManuallyDrop, ops::Deref, ptr::NonNull};
 
 /// Types that are _always_ reference counted.
@@ -183,6 +187,41 @@ fn eq(&self, other: &ARef<U>) -> bool {
 }
 impl<T: AlwaysRefCounted + Eq> Eq for ARef<T> {}
 
+// SAFETY: `into_foreign` returns a pointer from `NonNull::as_ptr`, so it's non-null. The
+// `ARef` invariant guarantees that `ptr` points to a valid `T`, so it's aligned to `T`.
+unsafe impl<T: AlwaysRefCounted + 'static> ForeignOwnable for ARef<T> {
+    const FOREIGN_ALIGN: usize = core::mem::align_of::<T>();
+
+    type Borrowed<'a> = &'a T;
+    type BorrowedMut<'a> = &'a T;
+
+    fn into_foreign(self) -> *mut c_void {
+        ARef::into_raw(self).as_ptr().cast()
+    }
+
+    unsafe fn from_foreign(ptr: *mut c_void) -> Self {
+        // SAFETY: The safety requirements of this function ensure that `ptr` comes from a previous
+        // call to `Self::into_foreign`.
+        let ptr = unsafe { NonNull::new_unchecked(ptr.cast()) };
+
+        // SAFETY: `ptr` came from `into_foreign`, which consumed an `ARef` without decrementing
+        // the refcount, so we can transfer the ownership to the new `ARef`.
+        unsafe { ARef::from_raw(ptr) }
+    }
+
+    unsafe fn borrow<'a>(ptr: *mut c_void) -> &'a T {
+        // SAFETY: The safety requirements of this method ensure that the object remains alive and
+        // immutable for the duration of 'a.
+        unsafe { &*ptr.cast() }
+    }
+
+    unsafe fn borrow_mut<'a>(ptr: *mut c_void) -> &'a T {
+        // SAFETY: The safety requirements for `borrow_mut` are a superset of the safety
+        // requirements for `borrow`.
+        unsafe { <Self as ForeignOwnable>::borrow(ptr) }
+    }
+}
+
 impl<T, U> PartialEq<&'_ U> for ARef<T>
 where
     T: AlwaysRefCounted + PartialEq<U>,
-- 
2.54.0


^ permalink raw reply related	[flat|nested] 19+ messages in thread

* [PATCH v2 2/6] rust: sync: Add abstraction for rcu_barrier()
  2026-06-16  8:28 [PATCH v2 0/6] rust / dma_buf: Add abstractions for dma_fence Philipp Stanner
  2026-06-16  8:28 ` [PATCH v2 1/6] rust: types: implement ForeignOwnable for ARef<T> Philipp Stanner
@ 2026-06-16  8:28 ` Philipp Stanner
  2026-06-16  8:35   ` sashiko-bot
  2026-06-16  8:28 ` [PATCH v2 3/6] rust: sync: Add abstraction for synchronize_rcu() Philipp Stanner
                   ` (4 subsequent siblings)
  6 siblings, 1 reply; 19+ messages in thread
From: Philipp Stanner @ 2026-06-16  8:28 UTC (permalink / raw)
  To: Miguel Ojeda, Boqun Feng, Gary Guo, Björn Roy Baron,
	Benno Lossin, Andreas Hindborg, Alice Ryhl, Trevor Gross,
	Danilo Krummrich, Sumit Semwal, Christian König,
	Philipp Stanner, Paul E. McKenney, Frederic Weisbecker,
	Neeraj Upadhyay, Joel Fernandes, Josh Triplett, Uladzislau Rezki,
	Steven Rostedt, Mathieu Desnoyers, Lai Jiangshan, Zqiang,
	Daniel Almeida, Greg Kroah-Hartman, Igor Korotin, Lorenzo Stoakes,
	Alexandre Courbot, FUJITA Tomonori, Krishna Ketan Rai,
	Shankari Anand, manos, Boris Brezillon
  Cc: linux-kernel, rust-for-linux, linux-media, dri-devel, rcu

rcu_barrier() is a frequently used C function which is always safe to be
called.

Add a safe abstraction for rcu_barrier().

Signed-off-by: Philipp Stanner <phasta@kernel.org>
---
 rust/kernel/sync/rcu.rs | 6 ++++++
 1 file changed, 6 insertions(+)

diff --git a/rust/kernel/sync/rcu.rs b/rust/kernel/sync/rcu.rs
index a32bef6e490b..eef34bf86259 100644
--- a/rust/kernel/sync/rcu.rs
+++ b/rust/kernel/sync/rcu.rs
@@ -50,3 +50,9 @@ fn drop(&mut self) {
 pub fn read_lock() -> Guard {
     Guard::new()
 }
+
+/// Wait for all pending call_rcu() callbacks, if there are any.
+pub fn rcu_barrier() {
+    // SAFETY: `rcu_barrier()` is always safe to be called. It just might wait for a grace period.
+    unsafe { bindings::rcu_barrier() };
+}
-- 
2.54.0


^ permalink raw reply related	[flat|nested] 19+ messages in thread

* [PATCH v2 3/6] rust: sync: Add abstraction for synchronize_rcu()
  2026-06-16  8:28 [PATCH v2 0/6] rust / dma_buf: Add abstractions for dma_fence Philipp Stanner
  2026-06-16  8:28 ` [PATCH v2 1/6] rust: types: implement ForeignOwnable for ARef<T> Philipp Stanner
  2026-06-16  8:28 ` [PATCH v2 2/6] rust: sync: Add abstraction for rcu_barrier() Philipp Stanner
@ 2026-06-16  8:28 ` Philipp Stanner
  2026-06-16  8:36   ` sashiko-bot
  2026-06-16  8:28 ` [PATCH v2 4/6] rust: error: Add ECANCELED error code Philipp Stanner
                   ` (3 subsequent siblings)
  6 siblings, 1 reply; 19+ messages in thread
From: Philipp Stanner @ 2026-06-16  8:28 UTC (permalink / raw)
  To: Miguel Ojeda, Boqun Feng, Gary Guo, Björn Roy Baron,
	Benno Lossin, Andreas Hindborg, Alice Ryhl, Trevor Gross,
	Danilo Krummrich, Sumit Semwal, Christian König,
	Philipp Stanner, Paul E. McKenney, Frederic Weisbecker,
	Neeraj Upadhyay, Joel Fernandes, Josh Triplett, Uladzislau Rezki,
	Steven Rostedt, Mathieu Desnoyers, Lai Jiangshan, Zqiang,
	Daniel Almeida, Greg Kroah-Hartman, Igor Korotin, Lorenzo Stoakes,
	Alexandre Courbot, FUJITA Tomonori, Krishna Ketan Rai,
	Shankari Anand, manos, Boris Brezillon
  Cc: linux-kernel, rust-for-linux, linux-media, dri-devel, rcu

synchronize_rcu() is a frequently used C function which is always safe
to be called.

Add a safe abstraction for synchronize_rcu().

Signed-off-by: Philipp Stanner <phasta@kernel.org>
---
 rust/kernel/sync/rcu.rs | 8 ++++++++
 1 file changed, 8 insertions(+)

diff --git a/rust/kernel/sync/rcu.rs b/rust/kernel/sync/rcu.rs
index eef34bf86259..d86d615467b1 100644
--- a/rust/kernel/sync/rcu.rs
+++ b/rust/kernel/sync/rcu.rs
@@ -56,3 +56,11 @@ pub fn rcu_barrier() {
     // SAFETY: `rcu_barrier()` is always safe to be called. It just might wait for a grace period.
     unsafe { bindings::rcu_barrier() };
 }
+
+/// Wait for one RCU grace period.
+///
+/// You typically do this to wait for everyone holding a [`Guard`].
+pub fn synchronize_rcu() {
+    // SAFETY: `synchronize_rcu()` is always safe to be called. It just waits for a grace period.
+    unsafe { bindings::synchronize_rcu() };
+}
-- 
2.54.0


^ permalink raw reply related	[flat|nested] 19+ messages in thread

* [PATCH v2 4/6] rust: error: Add ECANCELED error code
  2026-06-16  8:28 [PATCH v2 0/6] rust / dma_buf: Add abstractions for dma_fence Philipp Stanner
                   ` (2 preceding siblings ...)
  2026-06-16  8:28 ` [PATCH v2 3/6] rust: sync: Add abstraction for synchronize_rcu() Philipp Stanner
@ 2026-06-16  8:28 ` Philipp Stanner
  2026-06-16  8:28 ` [PATCH v2 5/6] rust: Add dma_fence abstractions Philipp Stanner
                   ` (2 subsequent siblings)
  6 siblings, 0 replies; 19+ messages in thread
From: Philipp Stanner @ 2026-06-16  8:28 UTC (permalink / raw)
  To: Miguel Ojeda, Boqun Feng, Gary Guo, Björn Roy Baron,
	Benno Lossin, Andreas Hindborg, Alice Ryhl, Trevor Gross,
	Danilo Krummrich, Sumit Semwal, Christian König,
	Philipp Stanner, Paul E. McKenney, Frederic Weisbecker,
	Neeraj Upadhyay, Joel Fernandes, Josh Triplett, Uladzislau Rezki,
	Steven Rostedt, Mathieu Desnoyers, Lai Jiangshan, Zqiang,
	Daniel Almeida, Greg Kroah-Hartman, Igor Korotin, Lorenzo Stoakes,
	Alexandre Courbot, FUJITA Tomonori, Krishna Ketan Rai,
	Shankari Anand, manos, Boris Brezillon
  Cc: linux-kernel, rust-for-linux, linux-media, dri-devel, rcu

The Rust dma_fence abstractions need the ECANCELED error code.

Add ECANCELED error code.

Signed-off-by: Philipp Stanner <phasta@kernel.org>
---
 rust/kernel/error.rs | 1 +
 1 file changed, 1 insertion(+)

diff --git a/rust/kernel/error.rs b/rust/kernel/error.rs
index 05cf869ac090..dda15db7cddf 100644
--- a/rust/kernel/error.rs
+++ b/rust/kernel/error.rs
@@ -88,6 +88,7 @@ macro_rules! declare_err {
     declare_err!(EIOCBQUEUED, "iocb queued, will get completion event.");
     declare_err!(ERECALLCONFLICT, "Conflict with recalled state.");
     declare_err!(ENOGRACE, "NFS file lock reclaim refused.");
+    declare_err!(ECANCELED, "Request has been canceled.");
 }
 
 /// Generic integer kernel error.
-- 
2.54.0


^ permalink raw reply related	[flat|nested] 19+ messages in thread

* [PATCH v2 5/6] rust: Add dma_fence abstractions
  2026-06-16  8:28 [PATCH v2 0/6] rust / dma_buf: Add abstractions for dma_fence Philipp Stanner
                   ` (3 preceding siblings ...)
  2026-06-16  8:28 ` [PATCH v2 4/6] rust: error: Add ECANCELED error code Philipp Stanner
@ 2026-06-16  8:28 ` Philipp Stanner
  2026-06-16  8:44   ` sashiko-bot
  2026-06-16 12:47   ` Onur Özkan
  2026-06-16  8:28 ` [PATCH v2 6/6] MAINTAINERS: Add entry for Rust dma-buf Philipp Stanner
  2026-06-16 11:02 ` [PATCH v2 0/6] rust / dma_buf: Add abstractions for dma_fence Danilo Krummrich
  6 siblings, 2 replies; 19+ messages in thread
From: Philipp Stanner @ 2026-06-16  8:28 UTC (permalink / raw)
  To: Miguel Ojeda, Boqun Feng, Gary Guo, Björn Roy Baron,
	Benno Lossin, Andreas Hindborg, Alice Ryhl, Trevor Gross,
	Danilo Krummrich, Sumit Semwal, Christian König,
	Philipp Stanner, Paul E. McKenney, Frederic Weisbecker,
	Neeraj Upadhyay, Joel Fernandes, Josh Triplett, Uladzislau Rezki,
	Steven Rostedt, Mathieu Desnoyers, Lai Jiangshan, Zqiang,
	Daniel Almeida, Greg Kroah-Hartman, Igor Korotin, Lorenzo Stoakes,
	Alexandre Courbot, FUJITA Tomonori, Krishna Ketan Rai,
	Shankari Anand, manos, Boris Brezillon
  Cc: linux-kernel, rust-for-linux, linux-media, dri-devel, rcu

C's dma_fence's are synchronisation primitives that will be needed by all
Rust GPU drivers.

The dma_fence framework sets a number of rules, notably:
  - fences must only be signalled once
  - all fences must be signalled at some point
  - fence error codes must only be set before signalling
  - every pointer to a fence must be backed by a reference

All those rules are being addressed by these abstractions.

To cleanly decouple fence issuers and consumers, two types are provided:
  - DriverFence: the only fence type that can be signalled and that
    carries driver-specific data.
  - Fence: the fence type to be shared with other drivers and / or
    userspace. The only type callbacks can be registered on.
    Cannot be signalled.

Hereby, a Fence lives in the same chunk of memory as a DriverFence. Both
share the refcount of the underlying C dma_fence. Since this
implementation does not provide a custom dma_fence_backend_ops.release()
function, the memory is freed by the dma_fence backend once the refcount
drops to 0.

To create a DriverFence, the user must first allocate a
DriverFenceAllocation, so that the creation of the DriverFence later on
can always succeed. Otherwise, deadlocks could occur if fences need to
be created in a GPU job submission path.

Synchronization is ensured by the dma_fence backend.

All DriverFence's created through this abstraction must be signalled by
the creator with an error code. In case a DriverFence drops without
being signalled beforehand, it is signalled with -ECANCELLED as its
error and a warning is printed. This allows the Rust abstraction to very
cleanly decouple fence issuer and consumer by relying on the decoupling
mechanisms in the C backend, which ensures through RCU and the
'signalled' fence-flag that dma_fence_backend_ops functions cannot
access the potentially unloaded driver code anymore.

Signalling fences on drop thus grants many advantages. Not signalling
fences on drop would risk deadlock and does not grant real advantages:
By definition only the drivers can ensure that a fence always represents
the hardware's state correctly.

This implementation models a DmaFenceCtx (fence context) object on which
fences are to be created, thereby ensuring correct sequence numbering
according to the timeline.

dma_fence supports a variety of callbacks. The mandatory callbacks
(get_timeline_name() and get_driver_name()) are implemented in this
patch. For convenience, they store those name parameters in the fence
context, saving the driver from implementing these two callbacks.

Support for other callbacks (like for hardware signalling) is prepared
for through the fact that both DriverFence and Fence live in the same
allocation, allowing for usage of container_of from the callback to
access the driver-specific data.

Synchronization for backend_ops callbacks is ensured by only running the
Rust deconstructor delayed with call_rcu(), which prevents UAF-bugs
should a DriverFence drop while a Fence callback is currently operating
on the associated driver data.

Add abstractions for dma_fence in Rust.

Signed-off-by: Philipp Stanner <phasta@kernel.org>
---
 rust/bindings/bindings_helper.h  |   1 +
 rust/helpers/dma_fence.c         |  48 ++
 rust/helpers/helpers.c           |   1 +
 rust/kernel/dma_buf/dma_fence.rs | 884 +++++++++++++++++++++++++++++++
 rust/kernel/dma_buf/mod.rs       |  14 +
 rust/kernel/lib.rs               |   1 +
 6 files changed, 949 insertions(+)
 create mode 100644 rust/helpers/dma_fence.c
 create mode 100644 rust/kernel/dma_buf/dma_fence.rs
 create mode 100644 rust/kernel/dma_buf/mod.rs

diff --git a/rust/bindings/bindings_helper.h b/rust/bindings/bindings_helper.h
index 446dbeaf0866..814d7740e686 100644
--- a/rust/bindings/bindings_helper.h
+++ b/rust/bindings/bindings_helper.h
@@ -52,6 +52,7 @@
 #include <linux/debugfs.h>
 #include <linux/device/faux.h>
 #include <linux/dma-direction.h>
+#include <linux/dma-fence.h>
 #include <linux/dma-mapping.h>
 #include <linux/dma-resv.h>
 #include <linux/errname.h>
diff --git a/rust/helpers/dma_fence.c b/rust/helpers/dma_fence.c
new file mode 100644
index 000000000000..0e08411098fa
--- /dev/null
+++ b/rust/helpers/dma_fence.c
@@ -0,0 +1,48 @@
+// SPDX-License-Identifier: GPL-2.0
+
+#include <linux/dma-fence.h>
+
+__rust_helper void rust_helper_dma_fence_get(struct dma_fence *f)
+{
+	dma_fence_get(f);
+}
+
+__rust_helper void rust_helper_dma_fence_put(struct dma_fence *f)
+{
+	dma_fence_put(f);
+}
+
+__rust_helper bool rust_helper_dma_fence_begin_signalling(void)
+{
+	return dma_fence_begin_signalling();
+}
+
+__rust_helper void rust_helper_dma_fence_end_signalling(bool cookie)
+{
+	dma_fence_end_signalling(cookie);
+}
+
+__rust_helper bool rust_helper_dma_fence_is_signaled(struct dma_fence *f)
+{
+	return dma_fence_is_signaled(f);
+}
+
+__rust_helper bool rust_helper_dma_fence_test_signaled_flag(struct dma_fence *f)
+{
+	return dma_fence_test_signaled_flag(f);
+}
+
+__rust_helper void rust_helper_dma_fence_lock_irqsave(struct dma_fence *f, unsigned long *flags)
+{
+	dma_fence_lock_irqsave(f, *flags);
+}
+
+__rust_helper void rust_helper_dma_fence_unlock_irqrestore(struct dma_fence *f, unsigned long *flags)
+{
+	dma_fence_unlock_irqrestore(f, *flags);
+}
+
+__rust_helper void rust_helper_dma_fence_set_error(struct dma_fence *f, int error)
+{
+	dma_fence_set_error(f, error);
+}
diff --git a/rust/helpers/helpers.c b/rust/helpers/helpers.c
index 625921e27dfb..d9114d0b3c8f 100644
--- a/rust/helpers/helpers.c
+++ b/rust/helpers/helpers.c
@@ -57,6 +57,7 @@
 #include "cred.c"
 #include "device.c"
 #include "dma.c"
+#include "dma_fence.c"
 #include "dma-resv.c"
 #include "drm.c"
 #include "err.c"
diff --git a/rust/kernel/dma_buf/dma_fence.rs b/rust/kernel/dma_buf/dma_fence.rs
new file mode 100644
index 000000000000..3af2dee58e13
--- /dev/null
+++ b/rust/kernel/dma_buf/dma_fence.rs
@@ -0,0 +1,884 @@
+// SPDX-License-Identifier: GPL-2.0
+//
+// Copyright (T) 2025, 2026 Red Hat Inc.:
+//   - Philipp Stanner <pstanner@redhat.com>
+
+//! DriverFence support.
+//!
+//! Reference: <https://docs.kernel.org/driver-api/dma-buf.html#c.dma_fence>
+//!
+//! T header: [`include/linux/dma-fence.h`](srctree/include/linux/dma-fence.h)
+
+use crate::{
+    alloc::AllocError,
+    bindings,
+    container_of,
+    error::to_result,
+    prelude::*,
+    types::ForeignOwnable,
+    types::Opaque,
+    warn_on, //
+};
+
+use pin_init::pin_init_from_closure;
+
+use core::{
+    mem::ManuallyDrop,
+    ops::Deref,
+    ptr,
+    ptr::{
+        drop_in_place,
+        NonNull, //
+    },
+    sync::atomic::{
+        AtomicU64,
+        Ordering, //
+    }, //
+};
+
+use kernel::{
+    str::CString,
+    sync::{
+        aref::{
+            ARef,
+            AlwaysRefCounted, //
+        },
+        Arc,
+        ArcBorrow, //
+    }, //
+};
+
+/// VTable for dma_fence backend_ops callbacks.
+//
+// Mandatory dma_fence backend_ops are implemented implicitly through
+// [`FenceCtx`]. Additional ones shall get implemented on this trait, which then
+// shall be demanded for the fence context data.
+pub trait FenceCtxOps {
+    /// The generic payload data for [`DriverFence`]s created on this fctx.
+    type FenceDataType: Send + Sync;
+}
+
+/// A dma-fence context. A fence context takes care of associating related fences with each other,
+/// providing each with raising sequence numbers and a common identifier.
+#[pin_data]
+pub struct FenceCtx<T: Send + Sync> {
+    /// The fence context number.
+    nr: u64,
+    /// The sequence number for the next fence created.
+    seqno: AtomicU64,
+    // 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()`.
+    /// The name of the driver this FenceCtx's fences belong to.
+    driver_name: CString,
+    /// The name of the timeline this FenceCtx's fences belong to.
+    timeline_name: CString,
+    #[pin]
+    data: T,
+}
+
+#[allow(unused_unsafe)]
+impl<T: Send + Sync + FenceCtxOps> FenceCtx<T> {
+    // This can later be extended as a vtable in case other parties need support
+    // for the more "exotic" callbacks.
+    const OPS: bindings::dma_fence_ops = bindings::dma_fence_ops {
+        get_driver_name: Some(Self::get_driver_name),
+        get_timeline_name: Some(Self::get_timeline_name),
+        enable_signaling: None,
+        signaled: None,
+        wait: None,
+        release: None,
+        set_deadline: None,
+    };
+
+    /// Create a new `FenceCtx`.
+    pub fn new(
+        driver_name: CString,
+        timeline_name: CString,
+        data: impl PinInit<T>,
+    ) -> Result<Arc<Self>> {
+        let ctx = pin_init!(Self {
+            // SAFETY: `dma_fence_context_alloc()` merely works on a global atomic. Parameter `1`
+            // is the number of contexts we want to allocate.
+            nr: unsafe { bindings::dma_fence_context_alloc(1) },
+            seqno: AtomicU64::new(0),
+            driver_name,
+            timeline_name,
+            data <- data,
+        });
+
+        Arc::pin_init(ctx, GFP_KERNEL)
+    }
+
+    fn get_next_fence_seqno(&self) -> u64 {
+        self.seqno.fetch_add(1, Ordering::Relaxed)
+    }
+
+    /// Allocate the memory for a [`DriverFence`] and already store `data` inside.
+    ///
+    /// This is needed because many times, creation of a [`DriverFence`] must not
+    /// fail, and allocating might deadlock in some situations.
+    ///
+    /// The `data` you pass here must not perform any operations that are illegal
+    /// in atomic context in its [`Drop`] implementation.
+    pub fn new_fence_allocation(
+        self: ArcBorrow<'_, Self>,
+        data: T::FenceDataType,
+    ) -> Result<DriverFenceAllocation<T>> {
+        let fctx = Arc::<Self>::from(self);
+        let fence_data = DriverFenceData {
+            rcu_head: Default::default(),
+            // `inner` remains uninitialized until a [`DriverFence`] takes over.
+            inner: Fence {
+                inner: Opaque::uninit(),
+            },
+            fctx,
+            data,
+        };
+
+        // In order to support the C dma_fence callbacks, it is necessary for
+        // a `Fence` and a `DriverFence` to live in the same allocation,
+        // because the C backend passes a dma_fence, from which the driver most
+        // likely wants to be able to access its `data` in `DriverFence`.
+        //
+        // Hence, we need the manage the memory manually. It will be freed by the
+        // C backend automatically once the refcount within `Fence` drops to 0.
+        let data = KBox::new(fence_data, GFP_KERNEL | __GFP_ZERO)?;
+
+        Ok(DriverFenceAllocation { data })
+    }
+
+    /// Create a new fence, consuming `data`.
+    ///
+    /// The fence will increment the refcount of the fence context associated with this
+    /// [`FenceCtx`].
+    pub fn new_fence(&self, memory: DriverFenceAllocation<T>) -> DriverFence<T> {
+        let seqno: u64 = self.get_next_fence_seqno();
+
+        // We feed the C dma_fence backend a NULL for the spinlock so that it
+        // uses per-fence locks automatically.
+        let null_ptr: *mut bindings::spinlock = ptr::null_mut();
+        let fence_ptr = memory.as_raw();
+        // SAFETY: `fence_ptr` has been created directly above. It will live
+        // at least as long as `Self`. The same applies to `&Self::OPS`.
+        unsafe { bindings::dma_fence_init(fence_ptr, &Self::OPS, null_ptr, self.nr, seqno) };
+
+        // A `DriverFenceAllocation`'s purpose is to carry allocated memory, so that
+        // `DriverFence`s can always be created without allocating. In this
+        // method, ownership over that memory is transferred to the new
+        // `DriverFence` and managed through refcounting. The C dma_fence
+        // backend will ultimately free the memory once the refcount reaches 0.
+        let ptr = KBox::into_raw(memory.data);
+        // SAFETY: `ptr` was just created validly directly above.
+        let ptr = unsafe { NonNull::new_unchecked(ptr) };
+
+        DriverFence { data: ptr }
+    }
+
+    extern "C" fn get_driver_name(ptr: *mut bindings::dma_fence) -> *const c_char {
+        // SAFETY: The C backend only invokes this callback with `ptr` pointing
+        // to a valid, unsignaled `bindings::dma_fence`. All fences created
+        // in this module always reside within `Fence` which always resides in
+        // a `DriverFenceData`, thus satisfying the function's safety requirements.
+        let fctx = unsafe { Self::from_raw_fence(ptr) };
+
+        fctx.driver_name.as_char_ptr()
+    }
+
+    extern "C" fn get_timeline_name(ptr: *mut bindings::dma_fence) -> *const c_char {
+        // SAFETY: The C backend only invokes this callback with `ptr` pointing
+        // to a valid, unsignaled `bindings::dma_fence`. All fences created
+        // in this module always reside within `Fence` which always resides in
+        // a `DriverFenceData`, thus satisfying the function's safety requirements.
+        let fctx = unsafe { Self::from_raw_fence(ptr) };
+
+        fctx.timeline_name.as_char_ptr()
+    }
+
+    /// Create a [`FenceCtx`] from an associated [`bindings::dma_fence`].
+    ///
+    /// # Safety
+    ///
+    /// `ptr` must be a valid pointer to a dma_fence which resides within a [`Fence`],
+    /// which in turn resides in a [`DriverFenceData`].
+    unsafe fn from_raw_fence<'a>(ptr: *mut bindings::dma_fence) -> &'a Self {
+        let opaque_fence = Opaque::cast_from(ptr);
+
+        // SAFETY: Safe due to the function's overall safety requirements.
+        let fence_ptr = unsafe { container_of!(opaque_fence, Fence, inner) };
+
+        // DriverFenceData is repr(C) and a Fence is its first member.
+        let fence_data_ptr = fence_ptr as *mut DriverFenceData<T>;
+
+        // SAFETY: Safe because of the safety comment directly above.
+        let fence_data = unsafe { &*fence_data_ptr };
+
+        &fence_data.fctx
+    }
+}
+
+/// Error type for fence callback registration.
+///
+/// Generic over `T` so that `AlreadySignaled` can return the callback to the
+/// caller, allowing it to reclaim any resources owned by the callback (e.g.,
+/// a fence handle that needs to be signaled).
+#[derive(Debug)]
+pub enum CallbackError<T = ()> {
+    /// The fence was already signaled. The callback is returned so the caller
+    /// can extract owned resources without losing them.
+    AlreadySignaled(T),
+    /// Some other error occurred during registration.
+    Other(Error),
+}
+
+impl<T> From<CallbackError<T>> for Error {
+    fn from(err: CallbackError<T>) -> Self {
+        match err {
+            CallbackError::AlreadySignaled(_) => ENOENT,
+            CallbackError::Other(e) => e,
+        }
+    }
+}
+
+impl<T> From<AllocError> for CallbackError<T> {
+    fn from(e: AllocError) -> Self {
+        CallbackError::Other(Error::from(e))
+    }
+}
+
+/// Trait for callbacks that can be registered on fences.
+///
+/// When the fence signals, the callback will be invoked.
+///
+/// # Example
+///
+/// ```rust
+/// use kernel::dma_buf::FenceCb;
+///
+/// struct MyCallback {
+///     // Your callback state here
+/// }
+///
+/// impl FenceCb for MyCallback {
+///     fn called(&mut self) {
+///         pr_info!("Fence signaled!");
+///         // Handle fence completion
+///     }
+/// }
+/// ```
+pub trait FenceCb: Send + 'static {
+    /// Called when the fence is signaled.
+    ///
+    /// This is called from the fence signaling path, which may be in interrupt
+    /// context or with locks held, which is why `self` is only borrowed, so that
+    /// it cannot drop. Implementations must not sleep or perform
+    /// long-running operations.
+    ///
+    /// An implementation likely wants to inform itself (e.g., through a work item)
+    /// within this callback that the associated [`FenceCbRegistration`] can now be
+    /// dropped.
+    fn called(&mut self);
+}
+
+/// A callback registration on a fence.
+///
+/// When this object is dropped, the callback is automatically removed if it
+/// hasn't been called yet.
+///
+/// # Invariants
+///
+/// If `callback` is `Some`, then `cb` is registered with the fence and the
+/// callback hasn't been invoked yet. If `None`, the callback has been invoked
+/// or the fence was already signaled when we tried to register.
+#[pin_data(PinnedDrop)]
+pub struct FenceCbRegistration<T: FenceCb + 'static> {
+    #[pin]
+    cb: Opaque<bindings::dma_fence_cb>,
+    callback: T,
+    fence: ARef<Fence>,
+}
+
+impl<T: FenceCb> FenceCbRegistration<T> {
+    /// Register a callback on a fence.
+    ///
+    /// On success the callback is pinned in place and will fire when the fence
+    /// signals. On `AlreadySignaled` the callback is returned to the caller so
+    /// that owned resources can be reclaimed.
+    pub fn new<'a>(fence: &'a Fence, callback: T) -> impl PinInit<Self, CallbackError<T>> + 'a
+    where
+        T: 'a,
+    {
+        // Uses `pin_init_from_closure` instead of `try_pin_init!` so that on
+        // `-ENOENT` (already signaled) the callback can be read back from the
+        // partially-initialized slot and returned through the error.
+        //
+        // SAFETY: `pin_init_from_closure` requires:
+        // - On `Ok(())`: the slot is fully initialized and valid for `Drop`.
+        // - On `Err(_)`: the slot is clean, i.e.: no partially-initialized fields
+        //   remain, and the slot can be deallocated without dropping.
+        //
+        // We uphold this as follows:
+        // - On success: all three fields are initialized. Ok(()) is returned.
+        // - On ENOENT (already signaled): `callback` and `fence` are read back
+        //   from the slot via `ptr::read`, leaving the slot clean. `cb` was
+        //   initialized by `dma_fence_add_callback` (it calls
+        //   `INIT_LIST_HEAD(&cb->node)` even on error), but `cb` is
+        //   `Opaque<dma_fence_cb>` which has no `Drop`, so not dropping it is
+        //   fine. The callback is returned through `AlreadySignaled(T)`.
+        // - On other errors: same cleanup as ENOENT, error returned as
+        //   `Other(e)`.
+        unsafe {
+            pin_init_from_closure(move |slot: *mut Self| {
+                let slot_callback = &raw mut (*slot).callback;
+                let slot_fence = &raw mut (*slot).fence;
+                let slot_cb = &raw mut (*slot).cb;
+
+                // Write callback and fence first — must be visible before
+                // dma_fence_add_callback makes the registration live.
+                core::ptr::write(slot_callback, callback);
+                core::ptr::write(slot_fence, ARef::from(fence));
+
+                let ret = to_result(bindings::dma_fence_add_callback(
+                    fence.inner.get(),
+                    Opaque::cast_into(slot_cb),
+                    Some(Self::dma_fence_callback),
+                ));
+
+                match ret {
+                    Ok(()) => Ok(()),
+                    Err(e) => {
+                        // Read back what we wrote to leave the slot clean.
+                        let cb_back = core::ptr::read(slot_callback);
+
+                        if e.to_errno() == ENOENT.to_errno() {
+                            Err(CallbackError::AlreadySignaled(cb_back))
+                        } else {
+                            Err(CallbackError::Other(e))
+                        }
+                    }
+                }
+            })
+        }
+    }
+
+    /// Raw dma fence callback that is called by the C code.
+    ///
+    /// # Safety
+    ///
+    /// This is only called by the dma_fence subsystem with valid pointers.
+    unsafe extern "C" fn dma_fence_callback(
+        _fence: *mut bindings::dma_fence,
+        cb: *mut bindings::dma_fence_cb,
+    ) {
+        let ptr = Opaque::cast_from(cb).cast_mut();
+
+        // SAFETY: All `cb` we can receive here have been created in such a way
+        // that they are embedded into a `FenceCbRegistration`. The backend
+        // ensures synchronisation so whoever holds the registration object
+        // cannot drop it while this code is running. See `FenceCbRegistration::drop`.
+        unsafe {
+            let reg: *mut Self = container_of!(ptr, Self, cb);
+
+            (*reg).callback.called();
+        }
+    }
+
+    /// Returns a reference to the fence this callback is registered on.
+    pub fn fence(self: Pin<&Self>) -> &Fence {
+        &self.get_ref().fence
+    }
+}
+
+#[pinned_drop]
+impl<T: FenceCb> PinnedDrop for FenceCbRegistration<T> {
+    fn drop(self: Pin<&mut Self>) {
+        // Always call dma_fence_remove_callback, even if `callback` has already
+        // been taken by `dma_fence_callback`.  This is necessary for
+        // synchronization: `dma_fence_remove_callback` acquires `fence->lock`,
+        // which ensures that any in-flight `dma_fence_signal` (which calls our
+        // callback while holding the same lock) has completed before we free
+        // the struct.
+        //
+        // Without this, Drop can race with a concurrent signal:
+        //   CPU0 (signal, lock held): take() -> signaled(fence_ref) (in progress)
+        //   CPU1 (drop): sees is_some()==false -> skips lock -> frees struct
+        //   CPU0: accesses fence_ref -> use-after-free
+        //
+        // When the callback has already fired, the signal path detached the
+        // list node via INIT_LIST_HEAD, so dma_fence_remove_callback just sees
+        // an empty node and returns false — the lock acquisition is the only
+        // thing that matters.
+        //
+        // SAFETY: The fence pointer is valid and the cb was initialized by
+        // dma_fence_add_callback during construction.
+        unsafe {
+            bindings::dma_fence_remove_callback(self.fence.as_raw(), self.cb.get());
+        }
+    }
+}
+
+// SAFETY: FenceCbRegistration can be sent between threads
+unsafe impl<T: FenceCb> Send for FenceCbRegistration<T> {}
+
+// SAFETY: &FenceCbRegistration can be shared between threads if &T can.
+unsafe impl<T: FenceCb> Sync for FenceCbRegistration<T> where T: Sync {}
+
+/// The receiving counterpart of a [`DriverFence`], designed to register callbacks
+/// on, check the signalled state etc. A [`Fence`] cannot be signalled.
+/// A [`Fence`] is always refcounted.
+pub struct Fence {
+    /// The actual dma_fence passed to C.
+    inner: Opaque<bindings::dma_fence>,
+}
+
+// SAFETY: Fences are literally designed to be shared between threads.
+unsafe impl Send for Fence {}
+// SAFETY: Fences are literally designed to be shared between threads.
+unsafe impl Sync for Fence {}
+
+impl Fence {
+    /// Check whether the fence was signalled at the moment of the function call.
+    ///
+    /// Note that this can return `true` for a [`Fence`] whose [`DriverFence`]
+    /// has not yet been dropped. The reason is that the fence ops callbacks can
+    /// cause the fence to get signaled by the C backend.
+    pub fn is_signaled(&self) -> bool {
+        let fence = self.as_raw();
+        let mut fence_flags: usize = 0;
+        let flag_ptr = &raw mut fence_flags;
+
+        // We shouuld not use `dma_fence_is_signaled_locked()` here, because
+        // according to the C backend's recommendations, that function is problematic
+        // and we should avoid calling that function with a lock held.
+
+        // SAFETY: `self` is valid by definition. We take the spinlock above.
+        let ret = unsafe { bindings::dma_fence_is_signaled(fence) };
+
+        // To guarantee that an API caller can 100% rely on the signalling being
+        // completed (i.e., all fence callbacks ran), we have to take the lock.
+        //
+        // The reason is that the C dma_fence backend currently does not carefully
+        // synchronize the `dma_fence_is_signaled()` function with the proper
+        // spinlock. This can lead to the function returning `true` while fence
+        // callbacks are still being executed. This can be mitigated by guarding
+        // the entire function with the spinlock.
+        //
+        // See commit c8a5d5ea3ba6a.
+
+        // SAFETY: `fence` is valid because `self` is valid. `flag_ptr` is
+        // merely a pointer to an integer, which lives as long as this function.
+        unsafe { bindings::dma_fence_lock_irqsave(fence, flag_ptr) };
+        // SAFETY: `fence` is valid because `self` is valid. `flag_ptr` is
+        // merely a pointer to an integer, which lives as long as this function.
+        // The lock was taken above.
+        unsafe { bindings::dma_fence_unlock_irqrestore(fence, flag_ptr) };
+
+        ret
+    }
+
+    fn as_raw(&self) -> *mut bindings::dma_fence {
+        self.inner.get()
+    }
+
+    /// Create a [`Fence`] from a raw C [`bindings::dma_fence`].
+    ///
+    /// # Safety
+    ///
+    /// `ptr` must point to an initialized fence that is embedded into a [`Fence`].
+    pub unsafe fn from_raw<'a>(ptr: *mut bindings::dma_fence) -> &'a Self {
+        // SAFETY: Safe as per the function's overall safety requirements.
+        unsafe { &*ptr.cast() }
+    }
+}
+
+// SAFETY: These implement the C backends refcounting methods which are proven to work correctly.
+unsafe impl AlwaysRefCounted for Fence {
+    fn inc_ref(&self) {
+        // SAFETY: `self.as_raw()` is a pointer to a valid `struct dma_fence`.
+        unsafe { bindings::dma_fence_get(self.as_raw()) }
+    }
+
+    /// # Safety
+    ///
+    /// `ptr`must be a valid pointer to a [`DriverFence`].
+    unsafe fn dec_ref(ptr: NonNull<Self>) {
+        // SAFETY: `ptr` is never a NULL pointer; and when `dec_ref()` is called
+        // the fence is by definition still valid.
+        let fence = unsafe { (*ptr.as_ptr()).inner.get() };
+
+        // SAFETY: `fence` was created validly above. When `dec_ref()` is called,
+        // there is by definition still a reference alive that can be put.
+        unsafe { bindings::dma_fence_put(fence) }
+    }
+}
+
+#[repr(C)] // Necessary to guarantee that `inner` always comes first so that we can cast.
+#[pin_data]
+struct DriverFenceData<T: Send + Sync + FenceCtxOps> {
+    /// Callback head for dropping this in a deferred manner through RCU.
+    rcu_head: bindings::callback_head,
+    #[pin]
+    /// The inner fence.
+    inner: Fence,
+    /// Pointer to access the FenceCtx. Useful for obtaining name parameters.
+    fctx: Arc<FenceCtx<T>>,
+    /// The API user's data. This must either not need drop, or must delay its
+    /// drop by a grace period. It is essential that the data only performs
+    /// operations legal in atomic context in its [`Drop`] implementation.
+    #[pin]
+    data: T::FenceDataType,
+}
+
+/// A synchronization primitive mainly for GPU drivers.
+///
+/// Fences are always reference counted. The typical use case is that one side registers
+/// callbacks on the fence which will perform a certain action (such as queueing work) once the
+/// other side signals the fence.
+///
+/// # Examples
+///
+/// ```
+/// use kernel::dma_buf::{
+///     DriverFence,
+///     FenceCtx,
+///     FenceCtxOps,
+///     FenceCb,
+///     FenceCbRegistration, //
+/// };
+/// use kernel::str::CString;
+/// use kernel::sync::aref::ARef;
+/// use core::fmt::Display;
+///
+/// struct CallbackData { }
+///
+/// impl FenceCb for CallbackData {
+///     fn called(&mut self) {
+///         pr_info!("DmaFence callback executed.\n");
+///     }
+/// }
+///
+/// struct FenceCtxData {}
+///
+/// impl FenceCtxOps for FenceCtxData {
+///     type FenceDataType = FenceData;
+/// }
+///
+/// let fctx_data = FenceCtxData {};
+///
+/// let driver_name = CString::try_from_fmt(fmt!("dummy_driver"))?;
+/// let timeline_name = CString::try_from_fmt(fmt!("dummy_timeline"))?;
+///
+/// let fctx = FenceCtx::new(driver_name, timeline_name, fctx_data)?;
+///
+/// struct FenceData {
+///     data: CString,
+/// }
+///
+/// let fence_data = CString::try_from_fmt(fmt!("dummy_data"))?;
+/// let fence_data = FenceData { data: fence_data };
+///
+/// let fence_alloc = fctx.as_arc_borrow().new_fence_allocation(fence_data)?;
+/// let mut fence = fctx.new_fence(fence_alloc);
+///
+/// let cb_data = CallbackData { };
+/// let waiting_fence = ARef::from(fence.as_fence());
+/// let cb_reg = FenceCbRegistration::new(&waiting_fence, cb_data);
+/// let cb_reg = KBox::pin_init(cb_reg, GFP_KERNEL)?;
+///
+/// // TODO signalling guards
+/// fence.signal(Ok(()));
+/// assert_eq!(waiting_fence.is_signaled(), true);
+///
+/// Ok::<(), Error>(())
+/// ```
+pub struct DriverFence<T: Send + Sync + FenceCtxOps> {
+    /// The actual content of the fence. Lives in a raw pointer so that its
+    /// memory can be managed independently. Valid until both the [`DriverFence`]
+    /// and all associated [`Fence`]s have disappeared.
+    data: NonNull<DriverFenceData<T>>,
+}
+
+/// A pre-prepared DMA fence, carrying the user's data and the memory it and the
+/// fence reside in. Only useful for creating a [`DriverFence`]. Splitting
+/// allocation and full initialization is necessary because fences cannot be
+/// allocated dynamically in some circumstances (deadlock).
+pub struct DriverFenceAllocation<T: Send + Sync + FenceCtxOps> {
+    /// The memory for the actual content of the fence.
+    /// Handed over to a [`DriverFence`], or deallocated once the
+    /// [`DriverFenceAllocation`] drops.
+    data: KBox<DriverFenceData<T>>,
+}
+
+impl<T: Send + Sync + FenceCtxOps> DriverFenceAllocation<T> {
+    fn as_raw(&self) -> *mut bindings::dma_fence {
+        self.data.inner.inner.get()
+    }
+}
+
+impl<T: Send + Sync + FenceCtxOps> DriverFence<T> {
+    fn as_raw(&self) -> *mut bindings::dma_fence {
+        // SAFETY: Valid because `self` is valid.
+        let fence_data = unsafe { &*self.data.as_ptr() };
+
+        fence_data.inner.inner.get()
+    }
+
+    /// Create a [`DriverFence`] from a raw pointer to a [`bindings::dma_fence`].
+    ///
+    /// # Safety
+    ///
+    /// `ptr` must be a valid pointer to a `dma_fence` that was obtained through
+    /// a [`DriverFence`] with matching generic data for both fence and associated
+    /// [`FenceCtx`].
+    unsafe fn from_raw(ptr: *mut bindings::dma_fence) -> Self {
+        let opaque_fence = Opaque::cast_from(ptr);
+
+        // SAFETY: Safe due to the function's overall safety requirements.
+        let fence_ptr = unsafe { container_of!(opaque_fence, Fence, inner) };
+
+        // DriverFenceData is repr(C) and a Fence is its first member.
+        let fence_data_ptr = fence_ptr as *mut DriverFenceData<T>;
+
+        // SAFETY: `fence_data_ptr` was created validly above.
+        let data = unsafe { NonNull::new_unchecked(fence_data_ptr) };
+
+        Self { data }
+    }
+
+    /// Return the underlying [`Fence`].
+    pub fn as_fence(&self) -> &Fence {
+        // SAFETY: `self` is by definition still valid, and it cannot drop until
+        // this new reference is gone.
+        unsafe { Fence::from_raw(self.as_raw()) }
+    }
+
+    /// Signal the fence. This will invoke all registered callbacks.
+    pub fn signal(self, res: Result) {
+        let fence = self.as_raw();
+        let mut fence_flags: usize = 0;
+        let flag_ptr = &raw mut fence_flags;
+
+        // SAFETY: `fence` is valid because `self` is valid. `flag_ptr` is
+        // merely a pointer to an integer, which lives as long as this function.
+        unsafe { bindings::dma_fence_lock_irqsave(fence, flag_ptr) };
+        // SAFETY: `fence` is valid because `self` is valid. The lock must be
+        // held, which we acquired directly above.
+        if !unsafe { bindings::dma_fence_test_signaled_flag(fence) } {
+            if let Err(err) = res {
+                // SAFETY: `fence` is valid because `self` is valid. The fence
+                // must not have been signaled yet, which we check directly above.
+                unsafe { bindings::dma_fence_set_error(fence, err.to_errno()) };
+            }
+            // SAFETY: `fence` is valid because `self` is valid. The lock must
+            // be held, which we acquired above.
+            unsafe { bindings::dma_fence_signal_locked(fence) };
+        }
+        // SAFETY: `fence` is valid because `self` is valid. `flag_ptr` is
+        // merely a pointer to an integer, which lives as long as this function.
+        // The lock must be held, which we acquired above.
+        unsafe { bindings::dma_fence_unlock_irqrestore(fence, flag_ptr) };
+    }
+}
+
+// SAFETY: Fences are literally designed to be shared between threads.
+unsafe impl<T: Send + Sync + FenceCtxOps> Send for DriverFence<T> {}
+
+impl<T: Send + Sync + FenceCtxOps> Deref for DriverFence<T> {
+    type Target = T::FenceDataType;
+
+    fn deref(&self) -> &Self::Target {
+        // SAFETY: Thanks to refcounting, `data` is always valid as long as `self` is.
+        let data = unsafe { &*self.data.as_ptr() };
+
+        &data.data
+    }
+}
+
+/// A borrowed [`DriverFence`]. All you can do with it is access your user data
+/// and obtain a [`Fence`].
+pub struct DriverFenceBorrow<T: Send + Sync + FenceCtxOps> {
+    /// The actual content of the fence. Lives in a raw pointer so that its
+    /// memory can be managed independently. Valid until both the [`DriverFence`]
+    /// and all associated [`Fence`]s have disappeared.
+    data: NonNull<DriverFenceData<T>>,
+}
+
+impl<T: Send + Sync + FenceCtxOps> Deref for DriverFenceBorrow<T> {
+    type Target = T::FenceDataType;
+
+    fn deref(&self) -> &Self::Target {
+        // SAFETY: Thanks to refcounting, `data` is always valid as long as `self` is.
+        let data = unsafe { &*self.data.as_ptr() };
+
+        &data.data
+    }
+}
+
+impl<T: Send + Sync + FenceCtxOps> DriverFenceBorrow<T> {
+    fn as_raw(&self) -> *mut bindings::dma_fence {
+        // SAFETY: Valid because `self` is valid.
+        let fence_data = unsafe { &*self.data.as_ptr() };
+
+        fence_data.inner.inner.get()
+    }
+
+    /// Return the underlying [`Fence`].
+    pub fn as_fence(&self) -> &Fence {
+        // SAFETY: `self` is by definition still valid, and it cannot drop until
+        // this new reference is gone.
+        unsafe { Fence::from_raw(self.as_raw()) }
+    }
+
+    /// Get a [`DriverFenceBorrow`] from a raw pointer.
+    ///
+    /// # Safety
+    ///
+    /// `ptr` must point to a raw dma_fence within a [`Fence`] within a [`DriverFenceData`].
+    unsafe fn from_raw(ptr: *mut bindings::dma_fence) -> Self {
+        let opaque_fence = Opaque::cast_from(ptr);
+
+        // SAFETY: Safe due to the function's overall safety requirements.
+        let fence_ptr = unsafe { container_of!(opaque_fence, Fence, inner) };
+
+        // DriverFenceData is repr(C) and a Fence is its first member.
+        let fence_data_ptr = fence_ptr as *mut DriverFenceData<T>;
+
+        // SAFETY: `fence_data_ptr` was created validly above.
+        let data = unsafe { NonNull::new_unchecked(fence_data_ptr) };
+
+        Self { data }
+    }
+}
+
+// SAFETY: The Rust dma_fence abstractions are already designed around the inner
+// C `dma_fence`, which can serve safely as the identification point when being
+// owned by C. Moreover, safety is ensured by not dropping `DriverFence` and by
+// only allowing operations without side effects on the Borrowed type.
+unsafe impl<T: Send + Sync + FenceCtxOps + 'static> ForeignOwnable for DriverFence<T> {
+    // `DriverFence` is merely a wrapper around a raw pointer. Thus, we can just
+    // use it directly.
+    type Borrowed<'a> = DriverFenceBorrow<T>;
+    type BorrowedMut<'a> = DriverFenceBorrow<T>;
+
+    const FOREIGN_ALIGN: usize = core::mem::align_of::<bindings::dma_fence>();
+
+    fn into_foreign(self) -> *mut c_void {
+        let fence = self;
+
+        let ptr = fence.as_raw();
+
+        // DriverFence must not drop.
+        let _ = ManuallyDrop::new(fence);
+
+        ptr.cast()
+    }
+
+    unsafe fn from_foreign(ptr: *mut c_void) -> Self {
+        // SAFETY: Safe because the trait implementation only invokes this with
+        // a valid `ptr`, associated to a `DriverFence` with matching generic data.
+        unsafe { Self::from_raw(ptr.cast()) }
+    }
+
+    unsafe fn borrow<'a>(ptr: *mut c_void) -> Self::Borrowed<'a> {
+        // SAFETY: The trait implementation ensures that `ptr` always resides
+        // within a [`Fence`] within a [`DriverFenceData`].
+        unsafe { DriverFenceBorrow::from_raw(ptr.cast()) }
+    }
+
+    unsafe fn borrow_mut<'a>(ptr: *mut c_void) -> Self::BorrowedMut<'a> {
+        // SAFETY: The trait implementation ensures that `ptr` always resides
+        // within a [`Fence`] within a [`DriverFenceData`].
+        unsafe { DriverFenceBorrow::from_raw(ptr.cast()) }
+    }
+}
+
+impl<T: Send + Sync + FenceCtxOps> Drop for DriverFence<T> {
+    fn drop(&mut self) {
+        let fence = self.as_raw();
+        let mut fence_flags: usize = 0;
+        let flag_ptr = &raw mut fence_flags;
+
+        // SAFETY: `fence` is valid until the `call_rcu()` below. `flag_ptr` is
+        // merely a pointer to an integer, which lives as long as this function.
+        unsafe { bindings::dma_fence_lock_irqsave(fence, flag_ptr) };
+
+        // Use dma_fence_test_signaled_flag() instead of dma_fence_is_signaled_locked()
+        // because the C backend wants to get rid of the latter.
+
+        // SAFETY: `fence` is valid until the `call_rcu()` below.
+        let signaled: bool = unsafe { bindings::dma_fence_test_signaled_flag(fence) };
+        if warn_on!(!signaled) {
+            // SAFETY: `fence` is valid until the `call_rcu()` below. The fence
+            // must not have been signaled yet, which we check directly above.
+            unsafe { bindings::dma_fence_set_error(fence, ECANCELED.to_errno()) };
+            // SAFETY: `fence` is valid until the `call_rcu()` below. The lock must
+            // be held, which we acquired above.
+            unsafe { bindings::dma_fence_signal_locked(fence) };
+        }
+        // SAFETY: `fence` is valid until the `call_rcu()` below. `flag_ptr` is
+        // merely a pointer to an integer, which lives as long as this function.
+        // The lock must be held, which we acquired above.
+        unsafe { bindings::dma_fence_unlock_irqrestore(fence, flag_ptr) };
+
+        // SAFETY: Valid because `self` is valid.
+        let rcu_head_ptr = unsafe { &raw mut (*self.data.as_ptr()).rcu_head };
+
+        // `DriverFenceData` but could be accessed through some dma_fence callbacks
+        // right now. Access is being revoked in principle above by signalling
+        // the fence, but since the C backend does not guarantee perfect full
+        // synchronization, we have to wait for one grace period to ensure that
+        // all accessors of `DriverFenceData` (through the dma_fence_ops accessible
+        // through a `Fence`) are gone.
+
+        // 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>)) };
+    }
+}
+
+// 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 + FenceCtxOps>(
+    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: A grace period has passed. All accessors to that `DriverFenceData`
+    // are gone now. We have exclusive access and can now drop it.
+    unsafe { drop_in_place(fence_data) };
+
+    // The `drop_in_place()` above might drop the `FenceCtx` by putting the last
+    // 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.
+}
diff --git a/rust/kernel/dma_buf/mod.rs b/rust/kernel/dma_buf/mod.rs
new file mode 100644
index 000000000000..fb353ce042ce
--- /dev/null
+++ b/rust/kernel/dma_buf/mod.rs
@@ -0,0 +1,14 @@
+// SPDX-License-Identifier: GPL-2.0 OR MIT
+
+//! DMA-buf subsystem abstractions.
+
+pub mod dma_fence;
+
+pub use self::dma_fence::{
+    DriverFence,
+    Fence,
+    FenceCb,
+    FenceCbRegistration,
+    FenceCtx,
+    FenceCtxOps, //
+};
diff --git a/rust/kernel/lib.rs b/rust/kernel/lib.rs
index b72b2fbe046d..a05ccaa7598c 100644
--- a/rust/kernel/lib.rs
+++ b/rust/kernel/lib.rs
@@ -63,6 +63,7 @@
 pub mod device_id;
 pub mod devres;
 pub mod dma;
+pub mod dma_buf;
 pub mod driver;
 #[cfg(CONFIG_DRM = "y")]
 pub mod drm;
-- 
2.54.0


^ permalink raw reply related	[flat|nested] 19+ messages in thread

* [PATCH v2 6/6] MAINTAINERS: Add entry for Rust dma-buf
  2026-06-16  8:28 [PATCH v2 0/6] rust / dma_buf: Add abstractions for dma_fence Philipp Stanner
                   ` (4 preceding siblings ...)
  2026-06-16  8:28 ` [PATCH v2 5/6] rust: Add dma_fence abstractions Philipp Stanner
@ 2026-06-16  8:28 ` Philipp Stanner
  2026-06-16 11:02 ` [PATCH v2 0/6] rust / dma_buf: Add abstractions for dma_fence Danilo Krummrich
  6 siblings, 0 replies; 19+ messages in thread
From: Philipp Stanner @ 2026-06-16  8:28 UTC (permalink / raw)
  To: Miguel Ojeda, Boqun Feng, Gary Guo, Björn Roy Baron,
	Benno Lossin, Andreas Hindborg, Alice Ryhl, Trevor Gross,
	Danilo Krummrich, Sumit Semwal, Christian König,
	Philipp Stanner, Paul E. McKenney, Frederic Weisbecker,
	Neeraj Upadhyay, Joel Fernandes, Josh Triplett, Uladzislau Rezki,
	Steven Rostedt, Mathieu Desnoyers, Lai Jiangshan, Zqiang,
	Daniel Almeida, Greg Kroah-Hartman, Igor Korotin, Lorenzo Stoakes,
	Alexandre Courbot, FUJITA Tomonori, Krishna Ketan Rai,
	Shankari Anand, manos, Boris Brezillon
  Cc: linux-kernel, rust-for-linux, linux-media, dri-devel, rcu

Rust does now have abstractions for dma_fence. These abstractions are
quite complicated and require expertise with both the C and the Rust
side. Therefore, using the existing entry also for maintenance of the
Rust code appears reasonable.

Philipp volunteers to help maintain the dma_fence abstractions. Add a
corresponding MAINTAINERS entry.

Signed-off-by: Philipp Stanner <phasta@kernel.org>
---
 MAINTAINERS | 3 +++
 1 file changed, 3 insertions(+)

diff --git a/MAINTAINERS b/MAINTAINERS
index 2e8d160babc2..5d4289099771 100644
--- a/MAINTAINERS
+++ b/MAINTAINERS
@@ -7521,6 +7521,7 @@ F:	fs/dlm/
 DMA BUFFER SHARING FRAMEWORK
 M:	Sumit Semwal <sumit.semwal@linaro.org>
 M:	Christian König <christian.koenig@amd.com>
+M:	Philipp Stanner <phasta@kernel.org>
 L:	linux-media@vger.kernel.org
 L:	dri-devel@lists.freedesktop.org
 L:	linaro-mm-sig@lists.linaro.org (moderated for non-subscribers)
@@ -7534,6 +7535,8 @@ F:	include/linux/dma-buf.h
 F:	include/linux/dma-buf/
 F:	include/linux/dma-resv.h
 F:	rust/helpers/dma-resv.c
+F:	rust/helpers/dma_fence.c
+F:	rust/kernel/dma_buf/
 K:	\bdma_(?:buf|fence|resv)\b
 
 DMA GENERIC OFFLOAD ENGINE SUBSYSTEM
-- 
2.54.0


^ permalink raw reply related	[flat|nested] 19+ messages in thread

* Re: [PATCH v2 2/6] rust: sync: Add abstraction for rcu_barrier()
  2026-06-16  8:28 ` [PATCH v2 2/6] rust: sync: Add abstraction for rcu_barrier() Philipp Stanner
@ 2026-06-16  8:35   ` sashiko-bot
  0 siblings, 0 replies; 19+ messages in thread
From: sashiko-bot @ 2026-06-16  8:35 UTC (permalink / raw)
  To: Philipp Stanner; +Cc: dri-devel, ojeda

Thank you for your contribution! Sashiko AI review found 1 potential issue(s) to consider:
- [Low] Missing `#[inline]` annotation on the `rcu_barrier` abstraction function.
--

commit 429e3c9d9e99f90b82501282309c58b5cf00107c
Author: Philipp Stanner <phasta@kernel.org>

rust: sync: Add abstraction for rcu_barrier()

This commit introduces a safe Rust abstraction for the C function
rcu_barrier(), allowing Rust code to wait for all pending call_rcu()
callbacks to complete.

> diff --git a/rust/kernel/sync/rcu.rs b/rust/kernel/sync/rcu.rs
> index a32bef6e490b0..eef34bf862596 100644
> --- a/rust/kernel/sync/rcu.rs
> +++ b/rust/kernel/sync/rcu.rs
[ ... ]
> +/// Wait for all pending call_rcu() callbacks, if there are any.
> +pub fn rcu_barrier() {

[Severity: Low]
This isn't a bug, but should this abstraction function be annotated with
#[inline]? The Rust subsystem guidelines indicate that small functions
or those that simply forward to a binding call should include the inline
annotation.

> +    // SAFETY: `rcu_barrier()` is always safe to be called. It just might wait for a grace period.
> +    unsafe { bindings::rcu_barrier() };
> +}

-- 
Sashiko AI review · https://sashiko.dev/#/patchset/20260616082819.2943886-2-phasta@kernel.org?part=2

^ permalink raw reply	[flat|nested] 19+ messages in thread

* Re: [PATCH v2 3/6] rust: sync: Add abstraction for synchronize_rcu()
  2026-06-16  8:28 ` [PATCH v2 3/6] rust: sync: Add abstraction for synchronize_rcu() Philipp Stanner
@ 2026-06-16  8:36   ` sashiko-bot
  0 siblings, 0 replies; 19+ messages in thread
From: sashiko-bot @ 2026-06-16  8:36 UTC (permalink / raw)
  To: Philipp Stanner; +Cc: ojeda, dri-devel

Thank you for your contribution! Sashiko AI review found 2 potential issue(s) to consider:
- [Low] Missing `#[inline]` annotation on the `synchronize_rcu()` abstraction.
- [Low] Existing uses of `bindings::synchronize_rcu()` were not updated to use the new safe abstraction.
--

commit 3f3ebd383ebd5385e032e8ee41ba8e2dc2c9f4e9
Author: Philipp Stanner <phasta@kernel.org>

rust: sync: Add abstraction for synchronize_rcu()

This commit adds a safe Rust abstraction for the synchronize_rcu() function
to wait for a grace period.

> diff --git a/rust/kernel/sync/rcu.rs b/rust/kernel/sync/rcu.rs
> index eef34bf862596..d86d615467b1e 100644
> --- a/rust/kernel/sync/rcu.rs
> +++ b/rust/kernel/sync/rcu.rs
[ ... ]
> @@ -56,3 +56,11 @@ pub fn rcu_barrier() {
>      // SAFETY: `rcu_barrier()` is always safe to be called. It just might wait for a grace period.
>      unsafe { bindings::rcu_barrier() };
>  }
> +
> +/// Wait for one RCU grace period.
> +///
> +/// You typically do this to wait for everyone holding a [`Guard`].
> +pub fn synchronize_rcu() {

[Severity: Low]
This isn't a bug, but should this function have an #[inline] annotation?

According to the Rust subsystem guidelines, functions in abstractions that
are small or simply forward to a binding call should be annotated with
#[inline].

Additionally, should the existing usages of the raw C binding be updated
to use this new safe abstraction?

For example, drop() in rust/kernel/sync/poll.rs currently does:

    unsafe { bindings::synchronize_rcu() };

And revoke_internal() in rust/kernel/revocable.rs:

    unsafe { bindings::synchronize_rcu() };

Updating these existing callers would help reduce unnecessary unsafe blocks
across the codebase now that a safe wrapper is available.

> +    // SAFETY: `synchronize_rcu()` is always safe to be called. It just waits for a grace period.
> +    unsafe { bindings::synchronize_rcu() };
> +}

-- 
Sashiko AI review · https://sashiko.dev/#/patchset/20260616082819.2943886-2-phasta@kernel.org?part=3

^ permalink raw reply	[flat|nested] 19+ messages in thread

* Re: [PATCH v2 1/6] rust: types: implement ForeignOwnable for ARef<T>
  2026-06-16  8:28 ` [PATCH v2 1/6] rust: types: implement ForeignOwnable for ARef<T> Philipp Stanner
@ 2026-06-16  8:38   ` sashiko-bot
  0 siblings, 0 replies; 19+ messages in thread
From: sashiko-bot @ 2026-06-16  8:38 UTC (permalink / raw)
  To: Philipp Stanner; +Cc: ojeda, dri-devel

Thank you for your contribution! Sashiko AI review found 1 potential issue(s) to consider:
- [Low] Missing `#[inline]` attributes on small forwarding functions in abstraction crate.
--

commit a8487c7d01286e697fa7028c9540c62f1483807e
Author: Danilo Krummrich <dakr@kernel.org>

rust: types: implement ForeignOwnable for ARef<T>

Implement ForeignOwnable for ARef<T>, making it possible for C code to
own an ARef<T>.

> diff --git a/rust/kernel/sync/aref.rs b/rust/kernel/sync/aref.rs
> index 9989f56d06052..82907383c44b5 100644
> --- a/rust/kernel/sync/aref.rs
> +++ b/rust/kernel/sync/aref.rs
[ ... ]
> @@ -183,6 +187,41 @@ fn eq(&self, other: &ARef<U>) -> bool {
>  }
>  impl<T: AlwaysRefCounted + Eq> Eq for ARef<T> {}
>  
> +// SAFETY: `into_foreign` returns a pointer from `NonNull::as_ptr`, so it's non-null. The
> +// `ARef` invariant guarantees that `ptr` points to a valid `T`, so it's aligned to `T`.
> +unsafe impl<T: AlwaysRefCounted + 'static> ForeignOwnable for ARef<T> {
> +    const FOREIGN_ALIGN: usize = core::mem::align_of::<T>();
> +
> +    type Borrowed<'a> = &'a T;
> +    type BorrowedMut<'a> = &'a T;
> +
> +    fn into_foreign(self) -> *mut c_void {
> +        ARef::into_raw(self).as_ptr().cast()
> +    }

[Severity: Low]
This isn't a bug, but should into_foreign(), from_foreign(), borrow(),
and borrow_mut() be annotated with #[inline]?

The Rust subsystem guidelines suggest that small or forwarding functions
in kernel abstractions should use the #[inline] attribute.

> +
> +    unsafe fn from_foreign(ptr: *mut c_void) -> Self {
> +        // SAFETY: The safety requirements of this function ensure that `ptr` comes from a previous
> +        // call to `Self::into_foreign`.
> +        let ptr = unsafe { NonNull::new_unchecked(ptr.cast()) };
> +
> +        // SAFETY: `ptr` came from `into_foreign`, which consumed an `ARef` without decrementing
> +        // the refcount, so we can transfer the ownership to the new `ARef`.
> +        unsafe { ARef::from_raw(ptr) }
> +    }
> +
> +    unsafe fn borrow<'a>(ptr: *mut c_void) -> &'a T {
> +        // SAFETY: The safety requirements of this method ensure that the object remains alive and
> +        // immutable for the duration of 'a.
> +        unsafe { &*ptr.cast() }
> +    }
> +
> +    unsafe fn borrow_mut<'a>(ptr: *mut c_void) -> &'a T {
> +        // SAFETY: The safety requirements for `borrow_mut` are a superset of the safety
> +        // requirements for `borrow`.
> +        unsafe { <Self as ForeignOwnable>::borrow(ptr) }
> +    }
> +}

-- 
Sashiko AI review · https://sashiko.dev/#/patchset/20260616082819.2943886-2-phasta@kernel.org?part=1

^ permalink raw reply	[flat|nested] 19+ messages in thread

* Re: [PATCH v2 5/6] rust: Add dma_fence abstractions
  2026-06-16  8:28 ` [PATCH v2 5/6] rust: Add dma_fence abstractions Philipp Stanner
@ 2026-06-16  8:44   ` sashiko-bot
  2026-06-16 12:47   ` Onur Özkan
  1 sibling, 0 replies; 19+ messages in thread
From: sashiko-bot @ 2026-06-16  8:44 UTC (permalink / raw)
  To: Philipp Stanner; +Cc: ojeda, dri-devel

Thank you for your contribution! Sashiko AI review found 5 potential issue(s) to consider:
- [Critical] Incorrect struct layout in `DriverFenceData` places `rcu_head` before `inner: Fence`, causing misaligned pointer casts and SLUB corruption when the C backend frees the interior pointer.
- [Critical] `DriverFence::drop` prematurely destroys `FenceCtx` and user data before the `dma_fence` reference count reaches 0, causing Use-After-Free.
- [High] `FenceCtx<T>` and `DriverFence<T>` lack a `'static` bound on `T`, allowing safe Rust code to erase lifetimes and cause Use-After-Free.
- [High] `FenceCbRegistration::new` leaks an `ARef<Fence>` reference count on the error path.
- [High] Dropping the last reference to `FenceCtx` from the RCU callback will execute the user's `T` (context data) `Drop` implementation in softirq context.
--

commit 95eb8ea421a40fbf4b3b019ea0a7b2c3928e1e11
Author: Philipp Stanner <phasta@kernel.org>
rust: Add dma_fence abstractions

This commit introduces Rust abstractions for the C dma_fence framework.
It provides types such as DriverFence, Fence, and FenceCtx to cleanly 
decouple fence issuers and consumers while interacting with the C backend.

> diff --git a/rust/kernel/dma_buf/dma_fence.rs b/rust/kernel/dma_buf/dma_fence.rs
> new file mode 100644
> index 0000000000000..3af2dee58e13f
> --- /dev/null
> +++ b/rust/kernel/dma_buf/dma_fence.rs
> @@ -0,0 +1,884 @@
[ ... ]
> +/// A dma-fence context. A fence context takes care of associating related fences with each other,
> +/// providing each with raising sequence numbers and a common identifier.
> +#[pin_data]
> +pub struct FenceCtx<T: Send + Sync> {

[Severity: High]
Does T need a static bound here?

Fence contains an Opaque<bindings::dma_fence> without lifetime parameters,
making it implicitly static. DriverFence::as_fence allows extracting an
owned ARef<Fence>. Because it is static, safe Rust code could send this
ARef to threads that outlive T. 

If T contains non-static references, the ARef<Fence> will keep
DriverFenceData alive beyond T's lifetime. Could this allow lifetime
erasure and a subsequent use-after-free?

> +    /// The fence context number.
> +    nr: u64,
> +    /// The sequence number for the next fence created.
> +    seqno: AtomicU64,
[ ... ]
> +#[allow(unused_unsafe)]
> +impl<T: Send + Sync + FenceCtxOps> FenceCtx<T> {
> +    // This can later be extended as a vtable in case other parties need support
> +    // for the more "exotic" callbacks.
> +    const OPS: bindings::dma_fence_ops = bindings::dma_fence_ops {
> +        get_driver_name: Some(Self::get_driver_name),
> +        get_timeline_name: Some(Self::get_timeline_name),
> +        enable_signaling: None,
> +        signaled: None,
> +        wait: None,
> +        release: None,
> +        set_deadline: None,
> +    };

[Severity: Critical]
Will the C backend crash when trying to free this fence? 

By leaving release as None, the dma_fence backend defaults to using
dma_fence_free(). As defined later in DriverFenceData, the dma_fence is
embedded at a non-zero offset. dma_fence_free() will call kfree_rcu() on
this interior pointer. Does this trigger a bad pointer panic in the SLUB
allocator?

[ ... ]
> +    /// Create a [`FenceCtx`] from an associated [`bindings::dma_fence`].
> +    ///
> +    /// # Safety
> +    ///
> +    /// `ptr` must be a valid pointer to a dma_fence which resides within a [`Fence`],
> +    /// which in turn resides in a [`DriverFenceData`].
> +    unsafe fn from_raw_fence<'a>(ptr: *mut bindings::dma_fence) -> &'a Self {
> +        let opaque_fence = Opaque::cast_from(ptr);
> +
> +        // SAFETY: Safe due to the function's overall safety requirements.
> +        let fence_ptr = unsafe { container_of!(opaque_fence, Fence, inner) };
> +
> +        // DriverFenceData is repr(C) and a Fence is its first member.
> +        let fence_data_ptr = fence_ptr as *mut DriverFenceData<T>;

[Severity: Critical]
Can this direct cast lead to misaligned structure access? 

The comment states Fence is the first member, but rcu_head is actually
defined before it in DriverFenceData. Casting the pointer directly to
DriverFenceData without accounting for the 16-byte offset of the inner
field could cause memory corruption when fctx is accessed later.

[ ... ]
> +    pub fn new<'a>(fence: &'a Fence, callback: T) -> impl PinInit<Self, CallbackError<T>> + 'a
> +    where
> +        T: 'a,
> +    {
[ ... ]
> +        unsafe {
> +            pin_init_from_closure(move |slot: *mut Self| {
> +                let slot_callback = &raw mut (*slot).callback;
> +                let slot_fence = &raw mut (*slot).fence;
> +                let slot_cb = &raw mut (*slot).cb;
> +
> +                // Write callback and fence first — must be visible before
> +                // dma_fence_add_callback makes the registration live.
> +                core::ptr::write(slot_callback, callback);
> +                core::ptr::write(slot_fence, ARef::from(fence));
> +
> +                let ret = to_result(bindings::dma_fence_add_callback(
> +                    fence.inner.get(),
> +                    Opaque::cast_into(slot_cb),
> +                    Some(Self::dma_fence_callback),
> +                ));
> +
> +                match ret {
> +                    Ok(()) => Ok(()),
> +                    Err(e) => {
> +                        // Read back what we wrote to leave the slot clean.
> +                        let cb_back = core::ptr::read(slot_callback);
> +
> +                        if e.to_errno() == ENOENT.to_errno() {
> +                            Err(CallbackError::AlreadySignaled(cb_back))
> +                        } else {
> +                            Err(CallbackError::Other(e))
> +                        }
> +                    }
> +                }
> +            })
> +        }
> +    }

[Severity: High]
Does this error path leak the ARef reference count?

Earlier in this closure, ARef::from(fence) is written into slot_fence,
which increments the reference count. If dma_fence_add_callback() fails,
the error path reads back slot_callback to return it, but slot_fence is
neither read back nor manually dropped. 

Since pin_init_from_closure assumes the slot is uninitialized on error
and won't run drop on it, will this permanently leak the ARef<Fence> and
its memory allocation?

[ ... ]
> +#[repr(C)] // Necessary to guarantee that `inner` always comes first so that we can cast.
> +#[pin_data]
> +struct DriverFenceData<T: Send + Sync + FenceCtxOps> {
> +    /// Callback head for dropping this in a deferred manner through RCU.
> +    rcu_head: bindings::callback_head,
> +    #[pin]
> +    /// The inner fence.
> +    inner: Fence,

[Severity: Critical]
Is the inner Fence guaranteed to be at offset 0 here? 

The struct is marked repr(C), but rcu_head precedes inner. This shifts
the embedded Fence by the size of the callback_head, directly conflicting
with the assumption in from_raw_fence that Fence is the first member.

[ ... ]
> +pub struct DriverFence<T: Send + Sync + FenceCtxOps> {
> +    /// The actual content of the fence. Lives in a raw pointer so that its
> +    /// memory can be managed independently. Valid until both the [`DriverFence`]
> +    /// and all associated [`Fence`]s have disappeared.
> +    data: NonNull<DriverFenceData<T>>,
> +}

[Severity: High]
Similar to FenceCtx, does DriverFence also require a static bound on T to
prevent safe code from erasing lifetimes and causing a use-after-free?

[ ... ]
> +/// 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 + FenceCtxOps>(
> +    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: A grace period has passed. All accessors to that `DriverFenceData`
> +    // are gone now. We have exclusive access and can now drop it.
> +    unsafe { drop_in_place(fence_data) };

[Severity: High]
Could this drop the user's data T in softirq context?

Since drop_driver_fence_data runs as an RCU callback, it executes in atomic
(softirq) context. If this drop_in_place() puts the last reference to the
Arc<FenceCtx<T>>, it will trigger the deallocation of the context and drop
its inner data T.

There appear to be no trait bounds or documentation enforcing that T must
only perform operations legal in atomic context. If a user provides context
data whose Drop implementation sleeps (like acquiring a mutex or freeing
large structures), would this trigger a sleeping in atomic context kernel
panic?

[Severity: Critical]
Does this prematurely destroy the user data and FenceCtx before the
dma_fence reference count actually reaches zero?

The dma_fence refcount is shared with ARef<Fence>. If an ARef<Fence> is
still alive when DriverFence drops, this RCU callback will execute
drop_in_place(fence_data). This destroys the Arc<FenceCtx> and the
user data. However, the C dma_fence is still alive with a non-zero
refcount.

When the refcount eventually hits zero later on, the C backend could
trigger callbacks like get_driver_name() via trace_dma_fence_destroy().
Could those callbacks access the already-freed fence_data.fctx, leading
to a use-after-free? 

Should the Rust data instead be dropped in a custom release callback in
dma_fence_ops when the refcount actually reaches 0?

> +
> +    // The `drop_in_place()` above might drop the `FenceCtx` by putting the last
> +    // 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.
> +}

-- 
Sashiko AI review · https://sashiko.dev/#/patchset/20260616082819.2943886-2-phasta@kernel.org?part=5

^ permalink raw reply	[flat|nested] 19+ messages in thread

* Re: [PATCH v2 0/6] rust / dma_buf: Add abstractions for dma_fence
  2026-06-16  8:28 [PATCH v2 0/6] rust / dma_buf: Add abstractions for dma_fence Philipp Stanner
                   ` (5 preceding siblings ...)
  2026-06-16  8:28 ` [PATCH v2 6/6] MAINTAINERS: Add entry for Rust dma-buf Philipp Stanner
@ 2026-06-16 11:02 ` Danilo Krummrich
  2026-06-16 11:28   ` Philipp Stanner
  6 siblings, 1 reply; 19+ messages in thread
From: Danilo Krummrich @ 2026-06-16 11:02 UTC (permalink / raw)
  To: Philipp Stanner
  Cc: Miguel Ojeda, Boqun Feng, Gary Guo, Björn Roy Baron,
	Benno Lossin, Andreas Hindborg, Alice Ryhl, Trevor Gross,
	Sumit Semwal, Christian König, Paul E. McKenney,
	Frederic Weisbecker, Neeraj Upadhyay, Joel Fernandes,
	Josh Triplett, Uladzislau Rezki, Steven Rostedt,
	Mathieu Desnoyers, Lai Jiangshan, Zqiang, Daniel Almeida,
	Greg Kroah-Hartman, Igor Korotin, Lorenzo Stoakes,
	Alexandre Courbot, FUJITA Tomonori, Krishna Ketan Rai,
	Shankari Anand, manos, Boris Brezillon, linux-kernel,
	rust-for-linux, linux-media, dri-devel, rcu

On Tue Jun 16, 2026 at 10:28 AM CEST, Philipp Stanner wrote:
> Changes since v1:

It seems that some of the comments from v1 [1][2] are not addressed, did you
forget them?

In particular the pin_init_from_closure() looks unnecessary and can probably
replaced with something like (untested):

	let mut callback = Some(callback);
	
	try_pin_init!(Self {
	    cb <- Opaque::try_ffi_init(|ptr| {
	        // SAFETY: `fence.inner.get()` is a valid, initialized `struct dma_fence`. `ptr`
	        // points to the `struct dma_fence_cb` field within the pinned allocation, so it
	        // remains valid until `dma_fence_remove_callback()` in `PinnedDrop` or until the
	        // callback fires.
	        let ret = unsafe {
	            to_result(bindings::dma_fence_add_callback(
	                fence.inner.get(),
	                ptr,
	                Some(Self::dma_fence_callback),
	            ))
	        };
	        match ret {
	            Ok(()) => Ok(()),
	            Err(e) => {
	                let cb_back = callback.take().expect("success path did not run");
	                if e == ENOENT {
	                    Err(CallbackError::AlreadySignaled(cb_back))
	                } else {
	                    Err(CallbackError::Other(e))
	                }
	            }
	        }
	    }),
	    callback: callback.take().expect("error path did not run"),
	    fence: ARef::from(fence),
	}? CallbackError<T>)

Please also see my previous comments regarding DriverFenceBorrow, it still seems
to miss the lifetime and it should probably use ManuallyDrop<DriverFence>.

It also seems the patch series was created without --base, what's the base
revision?

[1] https://lore.kernel.org/all/DIW3ZK5NLKU3.1QYMQB0ISHFBG@kernel.org/
[2] https://lore.kernel.org/all/DIW42TO5HY6H.2RLL8V8H48A5A@kernel.org/

^ permalink raw reply	[flat|nested] 19+ messages in thread

* Re: [PATCH v2 0/6] rust / dma_buf: Add abstractions for dma_fence
  2026-06-16 11:02 ` [PATCH v2 0/6] rust / dma_buf: Add abstractions for dma_fence Danilo Krummrich
@ 2026-06-16 11:28   ` Philipp Stanner
  0 siblings, 0 replies; 19+ messages in thread
From: Philipp Stanner @ 2026-06-16 11:28 UTC (permalink / raw)
  To: Danilo Krummrich, Philipp Stanner
  Cc: Miguel Ojeda, Boqun Feng, Gary Guo, Björn Roy Baron,
	Benno Lossin, Andreas Hindborg, Alice Ryhl, Trevor Gross,
	Sumit Semwal, Christian König, Paul E. McKenney,
	Frederic Weisbecker, Neeraj Upadhyay, Joel Fernandes,
	Josh Triplett, Uladzislau Rezki, Steven Rostedt,
	Mathieu Desnoyers, Lai Jiangshan, Zqiang, Daniel Almeida,
	Greg Kroah-Hartman, Igor Korotin, Lorenzo Stoakes,
	Alexandre Courbot, FUJITA Tomonori, Krishna Ketan Rai,
	Shankari Anand, manos, Boris Brezillon, linux-kernel,
	rust-for-linux, linux-media, dri-devel, rcu

On Tue, 2026-06-16 at 13:02 +0200, Danilo Krummrich wrote:
> On Tue Jun 16, 2026 at 10:28 AM CEST, Philipp Stanner wrote:
> > Changes since v1:
> 
> It seems that some of the comments from v1 [1][2] are not addressed, did you
> forget them?

yeah, a bunch of stuff might have slipped me. Mea culpa. Need to
address some more issues in v3 anyways; but wanted to present the rough
RCU idea here as a heads-up.

> 
> In particular the pin_init_from_closure() looks unnecessary and can probably
> replaced with something like (untested):
> 
> 	let mut callback = Some(callback);
> 	
> 	try_pin_init!(Self {
> 	    cb <- Opaque::try_ffi_init(|ptr| {
> 	        // SAFETY: `fence.inner.get()` is a valid, initialized `struct dma_fence`. `ptr`
> 	        // points to the `struct dma_fence_cb` field within the pinned allocation, so it
> 	        // remains valid until `dma_fence_remove_callback()` in `PinnedDrop` or until the
> 	        // callback fires.
> 	        let ret = unsafe {
> 	            to_result(bindings::dma_fence_add_callback(
> 	                fence.inner.get(),
> 	                ptr,
> 	                Some(Self::dma_fence_callback),
> 	            ))
> 	        };
> 	        match ret {
> 	            Ok(()) => Ok(()),
> 	            Err(e) => {
> 	                let cb_back = callback.take().expect("success path did not run");
> 	                if e == ENOENT {
> 	                    Err(CallbackError::AlreadySignaled(cb_back))
> 	                } else {
> 	                    Err(CallbackError::Other(e))
> 	                }
> 	            }
> 	        }
> 	    }),
> 	    callback: callback.take().expect("error path did not run"),
> 	    fence: ARef::from(fence),
> 	}? CallbackError<T>)
> 
> Please also see my previous comments regarding DriverFenceBorrow, it still seems
> to miss the lifetime and it should probably use ManuallyDrop<DriverFence>.

ACK.

> 
> It also seems the patch series was created without --base, what's the base
> revision?

Branched out from drm-misc-next for development purposes (since it
heavily relies on dma-fence.c)

I can rebase on drm-rust-next for the next revision.

> 
> [1] https://lore.kernel.org/all/DIW3ZK5NLKU3.1QYMQB0ISHFBG@kernel.org/
> [2] https://lore.kernel.org/all/DIW42TO5HY6H.2RLL8V8H48A5A@kernel.org/

^ permalink raw reply	[flat|nested] 19+ messages in thread

* Re: [PATCH v2 5/6] rust: Add dma_fence abstractions
  2026-06-16  8:28 ` [PATCH v2 5/6] rust: Add dma_fence abstractions Philipp Stanner
  2026-06-16  8:44   ` sashiko-bot
@ 2026-06-16 12:47   ` Onur Özkan
  2026-06-16 14:38     ` Philipp Stanner
  1 sibling, 1 reply; 19+ messages in thread
From: Onur Özkan @ 2026-06-16 12:47 UTC (permalink / raw)
  To: Philipp Stanner
  Cc: Miguel Ojeda, Boqun Feng, Gary Guo, Björn Roy Baron,
	Benno Lossin, Andreas Hindborg, Alice Ryhl, Trevor Gross,
	Danilo Krummrich, Sumit Semwal, Christian König,
	Paul E. McKenney, Frederic Weisbecker, Neeraj Upadhyay,
	Joel Fernandes, Josh Triplett, Uladzislau Rezki, Steven Rostedt,
	Mathieu Desnoyers, Lai Jiangshan, Zqiang, Daniel Almeida,
	Greg Kroah-Hartman, Igor Korotin, Lorenzo Stoakes,
	Alexandre Courbot, FUJITA Tomonori, Krishna Ketan Rai,
	Shankari Anand, manos, Boris Brezillon, linux-kernel,
	rust-for-linux, linux-media, dri-devel, rcu

On Tue, 16 Jun 2026 10:28:17 +0200
Philipp Stanner <phasta@kernel.org> wrote:

> C's dma_fence's are synchronisation primitives that will be needed by all
> Rust GPU drivers.
> 
> The dma_fence framework sets a number of rules, notably:
>   - fences must only be signalled once
>   - all fences must be signalled at some point
>   - fence error codes must only be set before signalling
>   - every pointer to a fence must be backed by a reference
> 
> All those rules are being addressed by these abstractions.
> 
> To cleanly decouple fence issuers and consumers, two types are provided:
>   - DriverFence: the only fence type that can be signalled and that
>     carries driver-specific data.
>   - Fence: the fence type to be shared with other drivers and / or
>     userspace. The only type callbacks can be registered on.
>     Cannot be signalled.
> 
> Hereby, a Fence lives in the same chunk of memory as a DriverFence. Both
> share the refcount of the underlying C dma_fence. Since this
> implementation does not provide a custom dma_fence_backend_ops.release()
> function, the memory is freed by the dma_fence backend once the refcount
> drops to 0.
> 
> To create a DriverFence, the user must first allocate a
> DriverFenceAllocation, so that the creation of the DriverFence later on
> can always succeed. Otherwise, deadlocks could occur if fences need to
> be created in a GPU job submission path.
> 
> Synchronization is ensured by the dma_fence backend.
> 
> All DriverFence's created through this abstraction must be signalled by
> the creator with an error code. In case a DriverFence drops without
> being signalled beforehand, it is signalled with -ECANCELLED as its
> error and a warning is printed. This allows the Rust abstraction to very
> cleanly decouple fence issuer and consumer by relying on the decoupling
> mechanisms in the C backend, which ensures through RCU and the
> 'signalled' fence-flag that dma_fence_backend_ops functions cannot
> access the potentially unloaded driver code anymore.
> 
> Signalling fences on drop thus grants many advantages. Not signalling
> fences on drop would risk deadlock and does not grant real advantages:
> By definition only the drivers can ensure that a fence always represents
> the hardware's state correctly.
> 
> This implementation models a DmaFenceCtx (fence context) object on which
> fences are to be created, thereby ensuring correct sequence numbering
> according to the timeline.
> 
> dma_fence supports a variety of callbacks. The mandatory callbacks
> (get_timeline_name() and get_driver_name()) are implemented in this
> patch. For convenience, they store those name parameters in the fence
> context, saving the driver from implementing these two callbacks.
> 
> Support for other callbacks (like for hardware signalling) is prepared
> for through the fact that both DriverFence and Fence live in the same
> allocation, allowing for usage of container_of from the callback to
> access the driver-specific data.
> 
> Synchronization for backend_ops callbacks is ensured by only running the
> Rust deconstructor delayed with call_rcu(), which prevents UAF-bugs
> should a DriverFence drop while a Fence callback is currently operating
> on the associated driver data.
> 
> Add abstractions for dma_fence in Rust.
> 
> Signed-off-by: Philipp Stanner <phasta@kernel.org>
> ---
>  rust/bindings/bindings_helper.h  |   1 +
>  rust/helpers/dma_fence.c         |  48 ++
>  rust/helpers/helpers.c           |   1 +
>  rust/kernel/dma_buf/dma_fence.rs | 884 +++++++++++++++++++++++++++++++
>  rust/kernel/dma_buf/mod.rs       |  14 +
>  rust/kernel/lib.rs               |   1 +
>  6 files changed, 949 insertions(+)
>  create mode 100644 rust/helpers/dma_fence.c
>  create mode 100644 rust/kernel/dma_buf/dma_fence.rs
>  create mode 100644 rust/kernel/dma_buf/mod.rs
> 
> diff --git a/rust/bindings/bindings_helper.h b/rust/bindings/bindings_helper.h
> index 446dbeaf0866..814d7740e686 100644
> --- a/rust/bindings/bindings_helper.h
> +++ b/rust/bindings/bindings_helper.h
> @@ -52,6 +52,7 @@
>  #include <linux/debugfs.h>
>  #include <linux/device/faux.h>
>  #include <linux/dma-direction.h>
> +#include <linux/dma-fence.h>
>  #include <linux/dma-mapping.h>
>  #include <linux/dma-resv.h>
>  #include <linux/errname.h>
> diff --git a/rust/helpers/dma_fence.c b/rust/helpers/dma_fence.c
> new file mode 100644
> index 000000000000..0e08411098fa
> --- /dev/null
> +++ b/rust/helpers/dma_fence.c
> @@ -0,0 +1,48 @@
> +// SPDX-License-Identifier: GPL-2.0
> +
> +#include <linux/dma-fence.h>
> +
> +__rust_helper void rust_helper_dma_fence_get(struct dma_fence *f)
> +{
> +	dma_fence_get(f);
> +}
> +
> +__rust_helper void rust_helper_dma_fence_put(struct dma_fence *f)
> +{
> +	dma_fence_put(f);
> +}

[...]

> +
> +    /// Create a [`FenceCtx`] from an associated [`bindings::dma_fence`].
> +    ///
> +    /// # Safety
> +    ///
> +    /// `ptr` must be a valid pointer to a dma_fence which resides within a [`Fence`],
> +    /// which in turn resides in a [`DriverFenceData`].
> +    unsafe fn from_raw_fence<'a>(ptr: *mut bindings::dma_fence) -> &'a Self {
> +        let opaque_fence = Opaque::cast_from(ptr);
> +
> +        // SAFETY: Safe due to the function's overall safety requirements.
> +        let fence_ptr = unsafe { container_of!(opaque_fence, Fence, inner) };
> +
> +        // DriverFenceData is repr(C) and a Fence is its first member.
> +        let fence_data_ptr = fence_ptr as *mut DriverFenceData<T>;

Either the field ordering on the type or this code is wrong because the first
member of DriverFenceData is `rcu_head`.

> +
> +        // SAFETY: Safe because of the safety comment directly above.
> +        let fence_data = unsafe { &*fence_data_ptr };
> +
> +        &fence_data.fctx
> +    }
> +}
> +
> +/// Error type for fence callback registration.
> +///
> +/// Generic over `T` so that `AlreadySignaled` can return the callback to the
> +/// caller, allowing it to reclaim any resources owned by the callback (e.g.,
> +/// a fence handle that needs to be signaled).
> +#[derive(Debug)]
> +pub enum CallbackError<T = ()> {
> +    /// The fence was already signaled. The callback is returned so the caller
> +    /// can extract owned resources without losing them.

[...]

> +        //
> +        // Without this, Drop can race with a concurrent signal:
> +        //   CPU0 (signal, lock held): take() -> signaled(fence_ref) (in progress)
> +        //   CPU1 (drop): sees is_some()==false -> skips lock -> frees struct
> +        //   CPU0: accesses fence_ref -> use-after-free
> +        //
> +        // When the callback has already fired, the signal path detached the
> +        // list node via INIT_LIST_HEAD, so dma_fence_remove_callback just sees
> +        // an empty node and returns false — the lock acquisition is the only
> +        // thing that matters.
> +        //
> +        // SAFETY: The fence pointer is valid and the cb was initialized by
> +        // dma_fence_add_callback during construction.
> +        unsafe {
> +            bindings::dma_fence_remove_callback(self.fence.as_raw(), self.cb.get());
> +        }
> +    }
> +}
> +
> +// SAFETY: FenceCbRegistration can be sent between threads
> +unsafe impl<T: FenceCb> Send for FenceCbRegistration<T> {}
> +
> +// SAFETY: &FenceCbRegistration can be shared between threads if &T can.
> +unsafe impl<T: FenceCb> Sync for FenceCbRegistration<T> where T: Sync {}
> +
> +/// The receiving counterpart of a [`DriverFence`], designed to register callbacks
> +/// on, check the signalled state etc. A [`Fence`] cannot be signalled.
> +/// A [`Fence`] is always refcounted.
> +pub struct Fence {
> +    /// The actual dma_fence passed to C.
> +    inner: Opaque<bindings::dma_fence>,
> +}

I am unsure whether it is safe to cast the pointer in Fence::from_raw without
Fence being #[repr(transparent)] as the layout compatibility is not guaranteed
explicitly.

Regards,
Onur

> +
> +// SAFETY: Fences are literally designed to be shared between threads.
> +unsafe impl Send for Fence {}
> +// SAFETY: Fences are literally designed to be shared between threads.
> +unsafe impl Sync for Fence {}
> +
> +impl Fence {
> +    /// Check whether the fence was signalled at the moment of the function call.
> +    ///
> +    /// Note that this can return `true` for a [`Fence`] whose [`DriverFence`]
> +    /// has not yet been dropped. The reason is that the fence ops callbacks can
> +    /// cause the fence to get signaled by the C backend.
> +    pub fn is_signaled(&self) -> bool {
> +        let fence = self.as_raw();
> +        let mut fence_flags: usize = 0;
> +        let flag_ptr = &raw mut fence_flags;
> +
> +        // We shouuld not use `dma_fence_is_signaled_locked()` here, because
> +        // according to the C backend's recommendations, that function is problematic
> +        // and we should avoid calling that function with a lock held.
> +
> +        // SAFETY: `self` is valid by definition. We take the spinlock above.
> +        let ret = unsafe { bindings::dma_fence_is_signaled(fence) };
> +
> +        // To guarantee that an API caller can 100% rely on the signalling being

[...]

> +    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.
> +}
> diff --git a/rust/kernel/dma_buf/mod.rs b/rust/kernel/dma_buf/mod.rs
> new file mode 100644
> index 000000000000..fb353ce042ce
> --- /dev/null
> +++ b/rust/kernel/dma_buf/mod.rs
> @@ -0,0 +1,14 @@
> +// SPDX-License-Identifier: GPL-2.0 OR MIT
> +
> +//! DMA-buf subsystem abstractions.
> +
> +pub mod dma_fence;
> +
> +pub use self::dma_fence::{
> +    DriverFence,
> +    Fence,
> +    FenceCb,
> +    FenceCbRegistration,
> +    FenceCtx,
> +    FenceCtxOps, //
> +};
> diff --git a/rust/kernel/lib.rs b/rust/kernel/lib.rs
> index b72b2fbe046d..a05ccaa7598c 100644
> --- a/rust/kernel/lib.rs
> +++ b/rust/kernel/lib.rs
> @@ -63,6 +63,7 @@
>  pub mod device_id;
>  pub mod devres;
>  pub mod dma;
> +pub mod dma_buf;
>  pub mod driver;
>  #[cfg(CONFIG_DRM = "y")]
>  pub mod drm;
> -- 
> 2.54.0
> 

^ permalink raw reply	[flat|nested] 19+ messages in thread

* Re: [PATCH v2 5/6] rust: Add dma_fence abstractions
  2026-06-16 12:47   ` Onur Özkan
@ 2026-06-16 14:38     ` Philipp Stanner
  2026-06-16 14:51       ` Onur Özkan
  2026-06-16 15:37       ` Gary Guo
  0 siblings, 2 replies; 19+ messages in thread
From: Philipp Stanner @ 2026-06-16 14:38 UTC (permalink / raw)
  To: Onur Özkan, Philipp Stanner
  Cc: Miguel Ojeda, Boqun Feng, Gary Guo, Björn Roy Baron,
	Benno Lossin, Andreas Hindborg, Alice Ryhl, Trevor Gross,
	Danilo Krummrich, Sumit Semwal, Christian König,
	Paul E. McKenney, Frederic Weisbecker, Neeraj Upadhyay,
	Joel Fernandes, Josh Triplett, Uladzislau Rezki, Steven Rostedt,
	Mathieu Desnoyers, Lai Jiangshan, Zqiang, Daniel Almeida,
	Greg Kroah-Hartman, Igor Korotin, Lorenzo Stoakes,
	Alexandre Courbot, FUJITA Tomonori, Krishna Ketan Rai,
	Shankari Anand, manos, Boris Brezillon, linux-kernel,
	rust-for-linux, linux-media, dri-devel, rcu

On Tue, 2026-06-16 at 15:47 +0300, Onur Özkan wrote:
> On Tue, 16 Jun 2026 10:28:17 +0200
> > +
> > +        // DriverFenceData is repr(C) and a Fence is its first member.
> > +        let fence_data_ptr = fence_ptr as *mut DriverFenceData<T>;
> 
> Either the field ordering on the type or this code is wrong because the first
> member of DriverFenceData is `rcu_head`.

Yeah, that's a bug introduced in this revision. Thx for pointing it
out. Already fixed it downstream.

I'm just wondering why my test did not blow up.

> > 
> > +}
> 
> I am unsure whether it is safe to cast the pointer in Fence::from_raw without
> Fence being #[repr(transparent)] as the layout compatibility is not guaranteed
> explicitly.

Shouldn't #[repr(C)] do that trick, too? Do you need both?

Who can answer that?


P.

^ permalink raw reply	[flat|nested] 19+ messages in thread

* Re: [PATCH v2 5/6] rust: Add dma_fence abstractions
  2026-06-16 14:38     ` Philipp Stanner
@ 2026-06-16 14:51       ` Onur Özkan
  2026-06-16 15:28         ` Danilo Krummrich
  2026-06-16 15:37       ` Gary Guo
  1 sibling, 1 reply; 19+ messages in thread
From: Onur Özkan @ 2026-06-16 14:51 UTC (permalink / raw)
  To: Philipp Stanner
  Cc: Philipp Stanner, Miguel Ojeda, Boqun Feng, Gary Guo,
	Björn Roy Baron, Benno Lossin, Andreas Hindborg, Alice Ryhl,
	Trevor Gross, Danilo Krummrich, Sumit Semwal,
	Christian König, Paul E. McKenney, Frederic Weisbecker,
	Neeraj Upadhyay, Joel Fernandes, Josh Triplett, Uladzislau Rezki,
	Steven Rostedt, Mathieu Desnoyers, Lai Jiangshan, Zqiang,
	Daniel Almeida, Greg Kroah-Hartman, Igor Korotin, Lorenzo Stoakes,
	Alexandre Courbot, FUJITA Tomonori, Krishna Ketan Rai,
	Shankari Anand, manos, Boris Brezillon, linux-kernel,
	rust-for-linux, linux-media, dri-devel, rcu

On Tue, 16 Jun 2026 16:38:51 +0200
Philipp Stanner <phasta@mailbox.org> wrote:

> On Tue, 2026-06-16 at 15:47 +0300, Onur Özkan wrote:
> > On Tue, 16 Jun 2026 10:28:17 +0200
> > > +
> > > +        // DriverFenceData is repr(C) and a Fence is its first member.
> > > +        let fence_data_ptr = fence_ptr as *mut DriverFenceData<T>;
> > 
> > Either the field ordering on the type or this code is wrong because the first
> > member of DriverFenceData is `rcu_head`.
> 
> Yeah, that's a bug introduced in this revision. Thx for pointing it
> out. Already fixed it downstream.
> 
> I'm just wondering why my test did not blow up.
> 
> > > 
> > > +}
> > 
> > I am unsure whether it is safe to cast the pointer in Fence::from_raw without
> > Fence being #[repr(transparent)] as the layout compatibility is not guaranteed
> > explicitly.
> 
> Shouldn't #[repr(C)] do that trick, too? Do you need both?
> 
> Who can answer that?
> 
> 
> P.

You shouldn't use both and I think it wouldn't compile anyway. repr(C) only
makes sense when the type have multiple fields.

Thanks,
Onur

^ permalink raw reply	[flat|nested] 19+ messages in thread

* Re: [PATCH v2 5/6] rust: Add dma_fence abstractions
  2026-06-16 14:51       ` Onur Özkan
@ 2026-06-16 15:28         ` Danilo Krummrich
  2026-06-16 16:35           ` Onur Özkan
  0 siblings, 1 reply; 19+ messages in thread
From: Danilo Krummrich @ 2026-06-16 15:28 UTC (permalink / raw)
  To: Onur Özkan
  Cc: Philipp Stanner, Philipp Stanner, Miguel Ojeda, Boqun Feng,
	Gary Guo, Björn Roy Baron, Benno Lossin, Andreas Hindborg,
	Alice Ryhl, Trevor Gross, Sumit Semwal, Christian König,
	Paul E. McKenney, Frederic Weisbecker, Neeraj Upadhyay,
	Joel Fernandes, Josh Triplett, Uladzislau Rezki, Steven Rostedt,
	Mathieu Desnoyers, Lai Jiangshan, Zqiang, Daniel Almeida,
	Greg Kroah-Hartman, Igor Korotin, Lorenzo Stoakes,
	Alexandre Courbot, FUJITA Tomonori, Krishna Ketan Rai,
	Shankari Anand, manos, Boris Brezillon, linux-kernel,
	rust-for-linux, linux-media, dri-devel, rcu

On Tue Jun 16, 2026 at 4:51 PM CEST, Onur Özkan wrote:
> repr(C) only makes sense when the type have multiple fields.

I don't think that's universally true. repr(transparent) guarantees the layout
and ABI match the inner field. The wrapper is passed through calls exactly as
the field would be. repr(C) guarantees C-compatible layout and makes the type
follow the platform's C ABI for passing an aggregate, which isn't always how the
bare field is passed.

^ permalink raw reply	[flat|nested] 19+ messages in thread

* Re: [PATCH v2 5/6] rust: Add dma_fence abstractions
  2026-06-16 14:38     ` Philipp Stanner
  2026-06-16 14:51       ` Onur Özkan
@ 2026-06-16 15:37       ` Gary Guo
  1 sibling, 0 replies; 19+ messages in thread
From: Gary Guo @ 2026-06-16 15:37 UTC (permalink / raw)
  To: phasta, Onur Özkan
  Cc: Miguel Ojeda, Boqun Feng, Gary Guo, Björn Roy Baron,
	Benno Lossin, Andreas Hindborg, Alice Ryhl, Trevor Gross,
	Danilo Krummrich, Sumit Semwal, Christian König,
	Paul E. McKenney, Frederic Weisbecker, Neeraj Upadhyay,
	Joel Fernandes, Josh Triplett, Uladzislau Rezki, Steven Rostedt,
	Mathieu Desnoyers, Lai Jiangshan, Zqiang, Daniel Almeida,
	Greg Kroah-Hartman, Igor Korotin, Lorenzo Stoakes,
	Alexandre Courbot, FUJITA Tomonori, Krishna Ketan Rai,
	Shankari Anand, manos, Boris Brezillon, linux-kernel,
	rust-for-linux, linux-media, dri-devel, rcu

On Tue Jun 16, 2026 at 3:38 PM BST, Philipp Stanner wrote:
> On Tue, 2026-06-16 at 15:47 +0300, Onur Özkan wrote:
>> On Tue, 16 Jun 2026 10:28:17 +0200
>> > +
>> > +        // DriverFenceData is repr(C) and a Fence is its first member.
>> > +        let fence_data_ptr = fence_ptr as *mut DriverFenceData<T>;
>> 
>> Either the field ordering on the type or this code is wrong because the first
>> member of DriverFenceData is `rcu_head`.
>
> Yeah, that's a bug introduced in this revision. Thx for pointing it
> out. Already fixed it downstream.
>
> I'm just wondering why my test did not blow up.
>
>> > 
>> > +}
>> 
>> I am unsure whether it is safe to cast the pointer in Fence::from_raw without
>> Fence being #[repr(transparent)] as the layout compatibility is not guaranteed
>> explicitly.
>
> Shouldn't #[repr(C)] do that trick, too? Do you need both?
>
> Who can answer that?

You have `#[repr(C)]` for DriverFence, but not on `Fence`. `Fence::from_raw`
does require layout equivalence between `Fence` and `bindings::dma_fence` and
this is done by adding `#[repr(transparent)]` on `Fence`.

Best,
Gary

^ permalink raw reply	[flat|nested] 19+ messages in thread

* Re: [PATCH v2 5/6] rust: Add dma_fence abstractions
  2026-06-16 15:28         ` Danilo Krummrich
@ 2026-06-16 16:35           ` Onur Özkan
  0 siblings, 0 replies; 19+ messages in thread
From: Onur Özkan @ 2026-06-16 16:35 UTC (permalink / raw)
  To: Danilo Krummrich
  Cc: Philipp Stanner, Philipp Stanner, Miguel Ojeda, Boqun Feng,
	Gary Guo, Björn Roy Baron, Benno Lossin, Andreas Hindborg,
	Alice Ryhl, Trevor Gross, Sumit Semwal, Christian König,
	Paul E. McKenney, Frederic Weisbecker, Neeraj Upadhyay,
	Joel Fernandes, Josh Triplett, Uladzislau Rezki, Steven Rostedt,
	Mathieu Desnoyers, Lai Jiangshan, Zqiang, Daniel Almeida,
	Greg Kroah-Hartman, Igor Korotin, Lorenzo Stoakes,
	Alexandre Courbot, FUJITA Tomonori, Krishna Ketan Rai,
	Shankari Anand, manos, Boris Brezillon, linux-kernel,
	rust-for-linux, linux-media, dri-devel, rcu

On Tue, 16 Jun 2026 17:28:04 +0200
Danilo Krummrich <dakr@kernel.org> wrote:

> On Tue Jun 16, 2026 at 4:51 PM CEST, Onur Özkan wrote:
> > repr(C) only makes sense when the type have multiple fields.
> 
> I don't think that's universally true. repr(transparent) guarantees the layout
> and ABI match the inner field. The wrapper is passed through calls exactly as
> the field would be. repr(C) guarantees C-compatible layout and makes the type
> follow the platform's C ABI for passing an aggregate, which isn't always how the
> bare field is passed.

I was too focused on the field ordering perspective during the review, sorry for
that.

Onur

^ permalink raw reply	[flat|nested] 19+ messages in thread

end of thread, other threads:[~2026-06-16 16:35 UTC | newest]

Thread overview: 19+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2026-06-16  8:28 [PATCH v2 0/6] rust / dma_buf: Add abstractions for dma_fence Philipp Stanner
2026-06-16  8:28 ` [PATCH v2 1/6] rust: types: implement ForeignOwnable for ARef<T> Philipp Stanner
2026-06-16  8:38   ` sashiko-bot
2026-06-16  8:28 ` [PATCH v2 2/6] rust: sync: Add abstraction for rcu_barrier() Philipp Stanner
2026-06-16  8:35   ` sashiko-bot
2026-06-16  8:28 ` [PATCH v2 3/6] rust: sync: Add abstraction for synchronize_rcu() Philipp Stanner
2026-06-16  8:36   ` sashiko-bot
2026-06-16  8:28 ` [PATCH v2 4/6] rust: error: Add ECANCELED error code Philipp Stanner
2026-06-16  8:28 ` [PATCH v2 5/6] rust: Add dma_fence abstractions Philipp Stanner
2026-06-16  8:44   ` sashiko-bot
2026-06-16 12:47   ` Onur Özkan
2026-06-16 14:38     ` Philipp Stanner
2026-06-16 14:51       ` Onur Özkan
2026-06-16 15:28         ` Danilo Krummrich
2026-06-16 16:35           ` Onur Özkan
2026-06-16 15:37       ` Gary Guo
2026-06-16  8:28 ` [PATCH v2 6/6] MAINTAINERS: Add entry for Rust dma-buf Philipp Stanner
2026-06-16 11:02 ` [PATCH v2 0/6] rust / dma_buf: Add abstractions for dma_fence Danilo Krummrich
2026-06-16 11:28   ` Philipp Stanner

This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.