dri-devel Archive on lore.kernel.org
 help / color / mirror / Atom feed
* [PATCH v7 0/5] rust / dma_buf: Add abstractions for dma_fence
@ 2026-07-29  9:45 Philipp Stanner
  2026-07-29  9:45 ` [PATCH v7 1/5] rust: error: add remaining error codes Philipp Stanner
                   ` (4 more replies)
  0 siblings, 5 replies; 11+ messages in thread
From: Philipp Stanner @ 2026-07-29  9:45 UTC (permalink / raw)
  To: Miguel Ojeda, Boqun Feng, Gary Guo, Björn Roy Baron,
	Benno Lossin, Andreas Hindborg, Alice Ryhl, Trevor Gross,
	Danilo Krummrich, Daniel Almeida, Tamir Duberstein,
	Alexandre Courbot, Onur Özkan, Sumit Semwal,
	Christian König, Philipp Stanner, Lyude Paul,
	Paul E. McKenney, Frederic Weisbecker, Neeraj Upadhyay,
	Joel Fernandes, Josh Triplett, Uladzislau Rezki, Steven Rostedt,
	Mathieu Desnoyers, Lai Jiangshan, Zqiang, Greg Kroah-Hartman,
	Asahi Lina, Burak Emir, Lorenzo Stoakes, FUJITA Tomonori,
	Eliot Courtney, Mirko Adzic, Timur Tabi, Daniel del Castillo,
	Boris Brezillon
  Cc: linux-kernel, rust-for-linux, linux-media, dri-devel,
	linaro-mm-sig, rcu


I had chats with the XArray maintainers and concluded that it's not the
right data structure for us. So the most notable change is that I for
now abstain from using XArray for the fctx's fence list (the one we need
to guard against UAF against core::mem::forget). Reasons are detailed in
the code comments, especially at DriverFence::remove_from_fctx_list().

Changes since v6:
  - Add Timur's error code patch (just so that the series builds. Merge
    however you prefer). (Timur)
  - Replace XArray with list, remove XArray patch.
  - Detail the issue with core::mem::forget() in the commit message.
  - Some code formatting etc.

Changes since v5:
  - Because Rust can "forget" DriverFences, it could bypass the lifetime
    ensuring that DriverFences don't outlive a FenceContext. Mitigate
    this by having the FenceContext keep track of unsignaled fences.
    (Danilo)
  - To support the point above, add support for memory reservations to
    XArray.
  - To support the point above, have FenceContext::alloc_fence()
    increment the sequence number. This is necessary for the xarray
    index. It might become critical with fence seqno holes.. please
    review.
  - Remove acronyms in declarations. (Daniel)
  - Allow for providing initial seqno in FenceContext::new(). (Daniel)
  - Flesh out documentation about DriverFence <-> Fence. (Daniel)
  - Flesh out documentation about weirdness in C backend. (Daniel)
  - Add new, purely internal locking helper / guard for dma_fence.
  - Use the kernel atomics (Gary).
  - Curse angrily at my computer.

Changes since v4:
  - Fix an uninitialized memory bug for FenceCbRegistration with
    ManuallyDrop.
  - Return FenceCtx as impl PinInit
  - Make FenceCtx return an impl PinInit<T, Error> (Danilo)
  - Reformat some comments

Changes since v3:
  - Add a FIXME for an encountered Rust compiler bug. (Gary)
  - Add new Rust files also to DRM drivers & common infrastructure
    MAINTAINERS file. (Danilo)
  - Reposition ECANCELED error code. (Miguel)
  - Replace refcounted FenceCtx in DriverFenceData with a reference plus
    life time. (Boris)
  - Re-add rcu_barrier() patch, since we now can use it for dropping the
    fence context. (Danilo)
  - Add forgotten R-b from Alice, and Acks for MAINTAINERS from
    Christian and Sumit.

Changes since v2:
  - Don't drop DriverFenceData as a whole, but only the members we
    really want to drop. Gives more robustness. (Gary).
  - Break apart large pin_init_from_closure(). (Danilo, Onur)
  - Remove rcu_barrier() and synchronize_rcu() from FenceCtx::drop().
    FenceCtx might drop in atomic context, where you must not perform
    those operations. With the current way C dma_fence is designed, the
    driver must wait for a grace period manually until it unloads.
  - Repair the DriverFenceBorrow implementation, properly injecting a
    life time into it. (Danilo)
  - Fix memory layout bug for rcu_head. (Onur)
  - Drop RCU patches, since this series doesn't need them anymore.

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 (3):
  rust: sync: Add abstraction for rcu_barrier()
  rust: Add dma_fence abstractions
  MAINTAINERS: Add entry for Rust dma-buf

Timur Tabi (1):
  rust: error: add remaining error codes

 MAINTAINERS                      |    5 +
 rust/bindings/bindings_helper.h  |    1 +
 rust/helpers/dma_fence.c         |   48 ++
 rust/helpers/helpers.c           |    1 +
 rust/kernel/dma_buf/dma_fence.rs | 1111 ++++++++++++++++++++++++++++++
 rust/kernel/dma_buf/mod.rs       |   14 +
 rust/kernel/error.rs             |  105 +++
 rust/kernel/lib.rs               |    1 +
 rust/kernel/sync/aref.rs         |   40 ++
 rust/kernel/sync/rcu.rs          |   20 +
 10 files changed, 1346 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


base-commit: 71d4e7233f235871b13553e504e591ace6b54373
-- 
2.55.0


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

* [PATCH v7 1/5] rust: error: add remaining error codes
  2026-07-29  9:45 [PATCH v7 0/5] rust / dma_buf: Add abstractions for dma_fence Philipp Stanner
@ 2026-07-29  9:45 ` Philipp Stanner
  2026-07-29  9:45 ` [PATCH v7 2/5] rust: types: implement ForeignOwnable for ARef<T> Philipp Stanner
                   ` (3 subsequent siblings)
  4 siblings, 0 replies; 11+ messages in thread
From: Philipp Stanner @ 2026-07-29  9:45 UTC (permalink / raw)
  To: Miguel Ojeda, Boqun Feng, Gary Guo, Björn Roy Baron,
	Benno Lossin, Andreas Hindborg, Alice Ryhl, Trevor Gross,
	Danilo Krummrich, Daniel Almeida, Tamir Duberstein,
	Alexandre Courbot, Onur Özkan, Sumit Semwal,
	Christian König, Philipp Stanner, Lyude Paul,
	Paul E. McKenney, Frederic Weisbecker, Neeraj Upadhyay,
	Joel Fernandes, Josh Triplett, Uladzislau Rezki, Steven Rostedt,
	Mathieu Desnoyers, Lai Jiangshan, Zqiang, Greg Kroah-Hartman,
	Asahi Lina, Burak Emir, Lorenzo Stoakes, FUJITA Tomonori,
	Eliot Courtney, Mirko Adzic, Timur Tabi, Daniel del Castillo,
	Boris Brezillon
  Cc: linux-kernel, rust-for-linux, linux-media, dri-devel,
	linaro-mm-sig, rcu, Fiona Behrens

From: Timur Tabi <ttabi@nvidia.com>

Add all of the remaining error codes from include/uapi/asm-generic/errno.h.

Previous updates to error.rs have been piecemeal -- adding single error
codes as needed.  Instead, we can avoid future problems by adding all
the remaining error code in one swoop.

EDEADLOCK and EWOULDBLOCK are intentionally left out: they are just
deprecated compatibility aliases of EDEADLK and EAGAIN, kept around for
non-Linux/POSIX code, and have no use in new kernel code.

Signed-off-by: Timur Tabi <ttabi@nvidia.com>
Reviewed-by: Fiona Behrens <me@kloenk.dev>
Acked-by: Danilo Krummrich <dakr@kernel.org>
Reviewed-by: Gary Guo <gary@garyguo.net>
Reviewed-by: Alexandre Courbot <acourbot@nvidia.com>
---
 rust/kernel/error.rs | 105 +++++++++++++++++++++++++++++++++++++++++++
 1 file changed, 105 insertions(+)

diff --git a/rust/kernel/error.rs b/rust/kernel/error.rs
index a56ba6309594..f3096d7c73ac 100644
--- a/rust/kernel/error.rs
+++ b/rust/kernel/error.rs
@@ -30,6 +30,8 @@ macro_rules! declare_err {
         };
     }
 
+    // From include/uapi/asm-generic/errno-base.h
+
     declare_err!(EPERM, "Operation not permitted.");
     declare_err!(ENOENT, "No such file or directory.");
     declare_err!(ESRCH, "No such process.");
@@ -64,9 +66,112 @@ macro_rules! declare_err {
     declare_err!(EPIPE, "Broken pipe.");
     declare_err!(EDOM, "Math argument out of domain of func.");
     declare_err!(ERANGE, "Math result not representable.");
+
+    // From include/uapi/asm-generic/errno.h
+
+    declare_err!(EDEADLK, "Resource deadlock would occur.");
+    declare_err!(ENAMETOOLONG, "File name too long.");
+    declare_err!(ENOLCK, "No record locks available.");
+    declare_err!(ENOSYS, "Invalid system call number.");
+    declare_err!(ENOTEMPTY, "Directory not empty.");
+    declare_err!(ELOOP, "Too many symbolic links encountered.");
+    declare_err!(ENOMSG, "No message of desired type.");
+    declare_err!(EIDRM, "Identifier removed.");
+    declare_err!(ECHRNG, "Channel number out of range.");
+    declare_err!(EL2NSYNC, "Level 2 not synchronized.");
+    declare_err!(EL3HLT, "Level 3 halted.");
+    declare_err!(EL3RST, "Level 3 reset.");
+    declare_err!(ELNRNG, "Link number out of range.");
+    declare_err!(EUNATCH, "Protocol driver not attached.");
+    declare_err!(ENOCSI, "No CSI structure available.");
+    declare_err!(EL2HLT, "Level 2 halted.");
+    declare_err!(EBADE, "Invalid exchange.");
+    declare_err!(EBADR, "Invalid request descriptor.");
+    declare_err!(EXFULL, "Exchange full.");
+    declare_err!(ENOANO, "No anode.");
+    declare_err!(EBADRQC, "Invalid request code.");
+    declare_err!(EBADSLT, "Invalid slot.");
+    declare_err!(EBFONT, "Bad font file format.");
+    declare_err!(ENOSTR, "Device not a stream.");
+    declare_err!(ENODATA, "No data available.");
+    declare_err!(ETIME, "Timer expired.");
+    declare_err!(ENOSR, "Out of streams resources.");
+    declare_err!(ENONET, "Machine is not on the network.");
+    declare_err!(ENOPKG, "Package not installed.");
+    declare_err!(EREMOTE, "Object is remote.");
+    declare_err!(ENOLINK, "Link has been severed.");
+    declare_err!(EADV, "Advertise error.");
+    declare_err!(ESRMNT, "Srmount error.");
+    declare_err!(ECOMM, "Communication error on send.");
+    declare_err!(EPROTO, "Protocol error.");
+    declare_err!(EMULTIHOP, "Multihop attempted.");
+    declare_err!(EDOTDOT, "RFS specific error.");
+    declare_err!(EBADMSG, "Not a data message.");
+    declare_err!(EFSBADCRC, "Bad CRC detected.");
     declare_err!(EOVERFLOW, "Value too large for defined data type.");
+    declare_err!(ENOTUNIQ, "Name not unique on network.");
+    declare_err!(EBADFD, "File descriptor in bad state.");
+    declare_err!(EREMCHG, "Remote address changed.");
+    declare_err!(ELIBACC, "Can not access a needed shared library.");
+    declare_err!(ELIBBAD, "Accessing a corrupted shared library.");
+    declare_err!(ELIBSCN, ".lib section in a.out corrupted.");
+    declare_err!(ELIBMAX, "Attempting to link in too many shared libraries.");
+    declare_err!(ELIBEXEC, "Cannot exec a shared library directly.");
+    declare_err!(EILSEQ, "Illegal byte sequence.");
+    declare_err!(ERESTART, "Interrupted system call should be restarted.");
+    declare_err!(ESTRPIPE, "Streams pipe error.");
+    declare_err!(EUSERS, "Too many users.");
+    declare_err!(ENOTSOCK, "Socket operation on non-socket.");
+    declare_err!(EDESTADDRREQ, "Destination address required.");
     declare_err!(EMSGSIZE, "Message too long.");
+    declare_err!(EPROTOTYPE, "Protocol wrong type for socket.");
+    declare_err!(ENOPROTOOPT, "Protocol not available.");
+    declare_err!(EPROTONOSUPPORT, "Protocol not supported.");
+    declare_err!(ESOCKTNOSUPPORT, "Socket type not supported.");
+    declare_err!(EOPNOTSUPP, "Operation not supported on transport endpoint.");
+    declare_err!(EPFNOSUPPORT, "Protocol family not supported.");
+    declare_err!(EAFNOSUPPORT, "Address family not supported by protocol.");
+    declare_err!(EADDRINUSE, "Address already in use.");
+    declare_err!(EADDRNOTAVAIL, "Cannot assign requested address.");
+    declare_err!(ENETDOWN, "Network is down.");
+    declare_err!(ENETUNREACH, "Network is unreachable.");
+    declare_err!(ENETRESET, "Network dropped connection because of reset.");
+    declare_err!(ECONNABORTED, "Software caused connection abort.");
+    declare_err!(ECONNRESET, "Connection reset by peer.");
+    declare_err!(ENOBUFS, "No buffer space available.");
+    declare_err!(EISCONN, "Transport endpoint is already connected.");
+    declare_err!(ENOTCONN, "Transport endpoint is not connected.");
+    declare_err!(ESHUTDOWN, "Cannot send after transport endpoint shutdown.");
+    declare_err!(ETOOMANYREFS, "Too many references: cannot splice.");
     declare_err!(ETIMEDOUT, "Connection timed out.");
+    declare_err!(ECONNREFUSED, "Connection refused.");
+    declare_err!(EHOSTDOWN, "Host is down.");
+    declare_err!(EHOSTUNREACH, "No route to host.");
+    declare_err!(EALREADY, "Operation already in progress.");
+    declare_err!(EINPROGRESS, "Operation now in progress.");
+    declare_err!(ESTALE, "Stale file handle.");
+    declare_err!(EUCLEAN, "Structure needs cleaning.");
+    declare_err!(EFSCORRUPTED, "Filesystem is corrupted.");
+    declare_err!(ENOTNAM, "Not a XENIX named type file.");
+    declare_err!(ENAVAIL, "No XENIX semaphores available.");
+    declare_err!(EISNAM, "Is a named type file.");
+    declare_err!(EREMOTEIO, "Remote I/O error.");
+    declare_err!(EDQUOT, "Quota exceeded.");
+    declare_err!(ENOMEDIUM, "No medium found.");
+    declare_err!(EMEDIUMTYPE, "Wrong medium type.");
+    declare_err!(ECANCELED, "Operation Canceled.");
+    declare_err!(ENOKEY, "Required key not available.");
+    declare_err!(EKEYEXPIRED, "Key has expired.");
+    declare_err!(EKEYREVOKED, "Key has been revoked.");
+    declare_err!(EKEYREJECTED, "Key was rejected by service.");
+    declare_err!(EOWNERDEAD, "Owner died.");
+    declare_err!(ENOTRECOVERABLE, "State not recoverable.");
+    declare_err!(ERFKILL, "Operation not possible due to RF-kill.");
+    declare_err!(EHWPOISON, "Memory page has hardware error.");
+    declare_err!(EFTYPE, "Wrong file type for the intended operation.");
+
+    // From include/linux/errno.h
+
     declare_err!(ERESTARTSYS, "Restart the system call.");
     declare_err!(ERESTARTNOINTR, "System call was interrupted by a signal and will be restarted.");
     declare_err!(ERESTARTNOHAND, "Restart if no handler.");
-- 
2.55.0


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

* [PATCH v7 2/5] rust: types: implement ForeignOwnable for ARef<T>
  2026-07-29  9:45 [PATCH v7 0/5] rust / dma_buf: Add abstractions for dma_fence Philipp Stanner
  2026-07-29  9:45 ` [PATCH v7 1/5] rust: error: add remaining error codes Philipp Stanner
@ 2026-07-29  9:45 ` Philipp Stanner
  2026-07-29  9:56   ` sashiko-bot
  2026-07-29  9:45 ` [PATCH v7 3/5] rust: sync: Add abstraction for rcu_barrier() Philipp Stanner
                   ` (2 subsequent siblings)
  4 siblings, 1 reply; 11+ messages in thread
From: Philipp Stanner @ 2026-07-29  9:45 UTC (permalink / raw)
  To: Miguel Ojeda, Boqun Feng, Gary Guo, Björn Roy Baron,
	Benno Lossin, Andreas Hindborg, Alice Ryhl, Trevor Gross,
	Danilo Krummrich, Daniel Almeida, Tamir Duberstein,
	Alexandre Courbot, Onur Özkan, Sumit Semwal,
	Christian König, Philipp Stanner, Lyude Paul,
	Paul E. McKenney, Frederic Weisbecker, Neeraj Upadhyay,
	Joel Fernandes, Josh Triplett, Uladzislau Rezki, Steven Rostedt,
	Mathieu Desnoyers, Lai Jiangshan, Zqiang, Greg Kroah-Hartman,
	Asahi Lina, Burak Emir, Lorenzo Stoakes, FUJITA Tomonori,
	Eliot Courtney, Mirko Adzic, Timur Tabi, Daniel del Castillo,
	Boris Brezillon
  Cc: linux-kernel, rust-for-linux, linux-media, dri-devel,
	linaro-mm-sig, 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>
Reviewed-by: Alice Ryhl <aliceryhl@google.com>
Tested-by: Daniel Almeida <daniel.almeida@collabora.com>
---
 rust/kernel/sync/aref.rs | 40 ++++++++++++++++++++++++++++++++++++++++
 1 file changed, 40 insertions(+)

diff --git a/rust/kernel/sync/aref.rs b/rust/kernel/sync/aref.rs
index b721b2e00b98..540766613659 100644
--- a/rust/kernel/sync/aref.rs
+++ b/rust/kernel/sync/aref.rs
@@ -24,6 +24,11 @@
     ptr::NonNull, //
 };
 
+use crate::{
+    prelude::*,
+    types::ForeignOwnable, //
+};
+
 /// Types that are _always_ reference counted.
 ///
 /// It allows such types to define their own custom ref increment and decrement functions.
@@ -188,6 +193,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.55.0


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

* [PATCH v7 3/5] rust: sync: Add abstraction for rcu_barrier()
  2026-07-29  9:45 [PATCH v7 0/5] rust / dma_buf: Add abstractions for dma_fence Philipp Stanner
  2026-07-29  9:45 ` [PATCH v7 1/5] rust: error: add remaining error codes Philipp Stanner
  2026-07-29  9:45 ` [PATCH v7 2/5] rust: types: implement ForeignOwnable for ARef<T> Philipp Stanner
@ 2026-07-29  9:45 ` Philipp Stanner
  2026-07-29 17:18   ` Paul E. McKenney
  2026-07-29  9:45 ` [PATCH v7 4/5] rust: Add dma_fence abstractions Philipp Stanner
  2026-07-29  9:45 ` [PATCH v7 5/5] MAINTAINERS: Add entry for Rust dma-buf Philipp Stanner
  4 siblings, 1 reply; 11+ messages in thread
From: Philipp Stanner @ 2026-07-29  9:45 UTC (permalink / raw)
  To: Miguel Ojeda, Boqun Feng, Gary Guo, Björn Roy Baron,
	Benno Lossin, Andreas Hindborg, Alice Ryhl, Trevor Gross,
	Danilo Krummrich, Daniel Almeida, Tamir Duberstein,
	Alexandre Courbot, Onur Özkan, Sumit Semwal,
	Christian König, Philipp Stanner, Lyude Paul,
	Paul E. McKenney, Frederic Weisbecker, Neeraj Upadhyay,
	Joel Fernandes, Josh Triplett, Uladzislau Rezki, Steven Rostedt,
	Mathieu Desnoyers, Lai Jiangshan, Zqiang, Greg Kroah-Hartman,
	Asahi Lina, Burak Emir, Lorenzo Stoakes, FUJITA Tomonori,
	Eliot Courtney, Mirko Adzic, Timur Tabi, Daniel del Castillo,
	Boris Brezillon
  Cc: linux-kernel, rust-for-linux, linux-media, dri-devel,
	linaro-mm-sig, 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>
Tested-by: Daniel Almeida <daniel.almeida@collabora.com>
---
 rust/kernel/sync/rcu.rs | 20 ++++++++++++++++++++
 1 file changed, 20 insertions(+)

diff --git a/rust/kernel/sync/rcu.rs b/rust/kernel/sync/rcu.rs
index a32bef6e490b..7031ca5d2473 100644
--- a/rust/kernel/sync/rcu.rs
+++ b/rust/kernel/sync/rcu.rs
@@ -50,3 +50,23 @@ fn drop(&mut self) {
 pub fn read_lock() -> Guard {
     Guard::new()
 }
+
+/// Wait until all in-flight call_rcu() callbacks complete.
+///
+/// Note that this primitive does not necessarily wait for an RCU grace period
+/// to complete.  For example, if there are no RCU callbacks queued anywhere
+/// in the system, then rcu_barrier() is within its rights to return
+/// immediately, without waiting for anything, much less an RCU grace period.
+/// In fact, rcu_barrier() will normally not result in any RCU grace periods
+/// beyond those that were already destined to be executed.
+///
+/// In kernels built with CONFIG_RCU_LAZY=y, this function also hurries all
+/// pending lazy RCU callbacks.
+///
+/// Note that this is one of the RCU primitives which must not be called in
+/// atomic context.
+#[inline]
+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.55.0


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

* [PATCH v7 4/5] rust: Add dma_fence abstractions
  2026-07-29  9:45 [PATCH v7 0/5] rust / dma_buf: Add abstractions for dma_fence Philipp Stanner
                   ` (2 preceding siblings ...)
  2026-07-29  9:45 ` [PATCH v7 3/5] rust: sync: Add abstraction for rcu_barrier() Philipp Stanner
@ 2026-07-29  9:45 ` Philipp Stanner
  2026-07-29 10:14   ` sashiko-bot
  2026-07-29  9:45 ` [PATCH v7 5/5] MAINTAINERS: Add entry for Rust dma-buf Philipp Stanner
  4 siblings, 1 reply; 11+ messages in thread
From: Philipp Stanner @ 2026-07-29  9:45 UTC (permalink / raw)
  To: Miguel Ojeda, Boqun Feng, Gary Guo, Björn Roy Baron,
	Benno Lossin, Andreas Hindborg, Alice Ryhl, Trevor Gross,
	Danilo Krummrich, Daniel Almeida, Tamir Duberstein,
	Alexandre Courbot, Onur Özkan, Sumit Semwal,
	Christian König, Philipp Stanner, Lyude Paul,
	Paul E. McKenney, Frederic Weisbecker, Neeraj Upadhyay,
	Joel Fernandes, Josh Triplett, Uladzislau Rezki, Steven Rostedt,
	Mathieu Desnoyers, Lai Jiangshan, Zqiang, Greg Kroah-Hartman,
	Asahi Lina, Burak Emir, Lorenzo Stoakes, FUJITA Tomonori,
	Eliot Courtney, Mirko Adzic, Timur Tabi, Daniel del Castillo,
	Boris Brezillon
  Cc: linux-kernel, rust-for-linux, linux-media, dri-devel,
	linaro-mm-sig, 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 signaled once
  - all fences must be signaled at some point
  - fence error codes must only be set before signaling
  - 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 signaled 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 signaled.

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 signaled by
the creator with an error code. In case a DriverFence drops without
being signaled beforehand, it is signaled 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
'signaled' 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 signaling
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 DmaFenceContext 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 signaling) 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.

It is expected that other callbacks, added in the future, also mostly
operate on the generic data in the FenceContext. To make this safe, the
implementation ensures through a lifetime that a DriverFence cannot
outlive its FenceContext. Since this can, currently, not be fully
guaranteed (core::mem::forget() could make the compiler forget about the
memory) in Rust, an additional safety measure is implemented: the
FenceContext keeps track of all unsignaled fences, and should it ever
drop with such a fence present, it will signal it, ensuring full
decoupling.

Synchronization for dma_fence_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. Since they can also operate on the
FenceContext's data, its drop implementation also performs the necessary
delay with rcu_barrier().

An additional issue discovered during the review process of this code is
that there is (currently) no mechanism in Rust from circumventing the
DriverFence's lifetime on the FenceContext by "forgetting" the fence,
e.g. with core::mem::forget(). Since this apparently can also happen
with recfounting cycles, it's quite likely that it would enable UAF bugs
on the FenceContext. To make this absolutely water proof, the
FenceContext keeps track of unsignaled fences and signals those should
the context drop before some fences.

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 | 1111 ++++++++++++++++++++++++++++++
 rust/kernel/dma_buf/mod.rs       |   14 +
 rust/kernel/lib.rs               |    1 +
 6 files changed, 1176 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 1124785e210b..54b62d952e01 100644
--- a/rust/bindings/bindings_helper.h
+++ b/rust/bindings/bindings_helper.h
@@ -53,6 +53,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 998e31052e66..4ab8aa9da7e7 100644
--- a/rust/helpers/helpers.c
+++ b/rust/helpers/helpers.c
@@ -58,6 +58,7 @@
 #include "cred.c"
 #include "device.c"
 #include "dma.c"
+#include "dma_fence.c"
 #include "dma-resv.c"
 #include "drm.c"
 #include "drm_gpuvm.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..c33cc8d40e94
--- /dev/null
+++ b/rust/kernel/dma_buf/dma_fence.rs
@@ -0,0 +1,1111 @@
+// SPDX-License-Identifier: GPL-2.0
+
+/*
+ * Copyright (C) 2025, 2026 Red Hat Inc.:
+ *   Author: Philipp Stanner <pstanner@redhat.com>
+ */
+
+//! DriverFence support.
+//!
+//! Reference: <https://docs.kernel.org/driver-api/dma-buf.html#c.dma_fence>
+//!
+//! 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 core::{
+    marker::PhantomData,
+    mem::ManuallyDrop,
+    ops::Deref,
+    ptr,
+    ptr::{
+        drop_in_place,
+        NonNull, //
+    }, //
+};
+
+use kernel::{
+    list::*,
+    str::CString,
+    sync::{
+        aref::{
+            ARef,
+            AlwaysRefCounted, //
+        },
+        atomic::{
+            Atomic,
+            Relaxed, //
+        },
+        new_spinlock,
+        rcu::rcu_barrier,
+        Arc,
+        SpinLock, //
+    }, //
+};
+
+/// Helper for enlisting fences into a [`FenceContext`] so that the fence context
+/// can signal fences as a last resort.
+#[pin_data]
+struct FenceLastResortSignaler {
+    fence: Option<ARef<Fence>>,
+    #[pin]
+    links: ListLinks,
+}
+
+impl FenceLastResortSignaler {
+    fn alloc() -> Result<ListArc<Self>> {
+        ListArc::pin_init(
+            try_pin_init!(Self {
+                fence: None,
+                links <- ListLinks::new(),
+            }),
+            GFP_KERNEL,
+        )
+    }
+}
+
+impl_list_arc_safe! {
+    impl ListArcSafe<0> for FenceLastResortSignaler { untracked; }
+}
+
+impl_list_item! {
+    impl ListItem<0> for FenceLastResortSignaler { using ListLinks { self.links }; }
+}
+
+/// VTable for dma_fence backend_ops callbacks.
+//
+// Mandatory dma_fence backend_ops are implemented implicitly through
+// [`FenceContext`]. Additional ones shall get implemented on this trait, which
+// then shall be demanded for the fence context data.
+pub trait FenceContextOps {
+    /// 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(PinnedDrop)]
+pub struct FenceContext<T: FenceContextOps + Send + Sync> {
+    /// The fence context number.
+    nr: u64,
+    /// The sequence number for the next fence created.
+    seqno: Atomic<u64>,
+    // The name parameters can be accessed by the dma_fence backend_ops. UAF
+    // errors are prevented by the `call_rcu()` in `drop_driver_fence_data()`.
+    /// The name of the driver this FenceContext's fences belong to.
+    driver_name: CString,
+    /// The name of the timeline this FenceContext's fences belong to.
+    timeline_name: CString,
+    /// A list of all unsignaled fences of this context.
+    // As an additional safety-measure, the fence context will signal all fences
+    // that were not yet signaled when it drops.
+    //
+    // The life-time on `DriverFence`s should typically prevent this from
+    // happening.
+    //
+    // However, we cannot fully guarantee in Rust that `DriverFence`s will not
+    // be forgotten, e.g., through refcounting. This could circumvent the
+    // lifetime which intends to enforce that all fences disappear before their
+    // context.
+    //
+    // Safety can still be ensured, though, by keeping track of unsignaled fences
+    // and signalling those manually when the context drops.
+    #[pin]
+    fences: SpinLock<List<FenceLastResortSignaler>>,
+    /// The user's data.
+    #[pin]
+    data: T,
+}
+
+impl<'a, T: Send + Sync + FenceContextOps> FenceContext<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 `FenceContext`.
+    pub fn new<E>(
+        initial_seqno: u64,
+        driver_name: CString,
+        timeline_name: CString,
+        data: impl PinInit<T, E>,
+    ) -> impl PinInit<Self, Error>
+    where
+        Error: From<E>,
+    {
+        try_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: Atomic::new(initial_seqno),
+            driver_name,
+            timeline_name,
+            fences <- new_spinlock!(List::<FenceLastResortSignaler>::new()),
+            data <- data,
+        })
+    }
+
+    fn get_next_seqno(&self) -> u64 {
+        self.seqno.fetch_add(1, 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.
+    // Mutable so that the unlikely issue of seqno misordering gets impossible.
+    pub fn fence_alloc(&self, data: T::FenceDataType) -> Result<DriverFenceAllocation<'_, T>> {
+        let fence_data = DriverFenceData {
+            rcu_head: Default::default(),
+            // `inner` remains uninitialized until a [`DriverFence`] takes over.
+            inner: Fence {
+                inner: Opaque::uninit(),
+            },
+            fctx: self,
+            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)?;
+        let signaler = FenceLastResortSignaler::alloc()?;
+
+        Ok(DriverFenceAllocation {
+            data,
+            signaler,
+            ops: &Self::OPS,
+        })
+    }
+
+    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 [`FenceContext`] from an associated [`bindings::dma_fence`].
+    ///
+    /// # Safety
+    ///
+    /// `ptr` must be a valid pointer to a [`bindings::dma_fence`] which resides
+    /// within a [`Fence`], which in turn resides in a [`DriverFenceData`].
+    unsafe fn from_raw_fence(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) };
+
+        // CAST: `DriverFenceData` is repr(C) and a `Fence` is its first member.
+        let fence_data_ptr = fence_ptr as *mut DriverFenceData<'a, T>;
+
+        // SAFETY: Safe because of the comments directly above.
+        let fence_data = unsafe { &*fence_data_ptr };
+
+        fence_data.fctx
+    }
+}
+
+// FenceContext's drop() ensures that the driver cannot unload while there are
+// still dma_fence callbacks running. This also prevents UAF problems with
+// `fctx.driver_name` and `fctx.timeline_name`.
+//
+// DriverFence data gets dropped through call_rcu() in DriverFence::drop.
+// This `rcu_barrier()` also serves to wait for their completion.
+#[pinned_drop]
+impl<T: FenceContextOps + Send + Sync> PinnedDrop for FenceContext<T> {
+    fn drop(self: Pin<&mut Self>) {
+        let mut guard = self.fences.lock();
+        let mut cursor = guard.cursor_front();
+
+        while let Some(pos) = cursor.peek_next() {
+            let list_arc = pos.remove();
+            let mut signaler =
+                Arc::<FenceLastResortSignaler>::into_unique_or_drop(list_arc.into_arc()).unwrap();
+
+            // All fences in this list are always set. Unwrap.
+            let fence = signaler.as_mut().project().fence.take().unwrap();
+            let fence = fence.lock();
+
+            // SAFETY: We still hold a reference, thus the pointer is valid.
+            let signaled: bool = unsafe { bindings::dma_fence_test_signaled_flag(*fence.deref()) };
+            if !signaled {
+                // SAFETY: We still hold a reference, thus the pointer is
+                // valid. The fence must not have been signaled yet, which we
+                // check directly above. The lock must be held, which we
+                // acquired above.
+                unsafe { bindings::dma_fence_set_error(*fence.deref(), ECANCELED.to_errno()) };
+                // SAFETY: We still hold a reference, thus the pointer is
+                // valid. The lock must be held, which we acquired above.
+                unsafe { bindings::dma_fence_signal_locked(*fence.deref()) };
+            } else {
+                pr_warn!("Signaled Fences in FenceContext.");
+            }
+        }
+
+        drop(guard);
+        rcu_barrier();
+    }
+}
+
+/// 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::FenceCallback;
+///
+/// struct MyCallback {
+///     // Your callback state here
+/// }
+///
+/// impl FenceCallback for MyCallback {
+///     fn called(&mut self) {
+///         pr_info!("Fence signaled!");
+///         // Handle fence completion
+///     }
+/// }
+/// ```
+pub trait FenceCallback: 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 [`FenceCallbackRegistration`]
+    /// 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.
+#[pin_data(PinnedDrop)]
+pub struct FenceCallbackRegistration<T: FenceCallback + 'static> {
+    #[pin]
+    callback_foreign: Opaque<bindings::dma_fence_cb>,
+    callback: ManuallyDrop<T>,
+    fence: ARef<Fence>,
+}
+
+impl<T: FenceCallback> FenceCallbackRegistration<T> {
+    /// Create a [`PinInit`] closure for registering a callback on a fence.
+    ///
+    /// The actual attempt at registering the callback will take place once you
+    /// call an allocator's `pin_init()` function.
+    ///
+    /// 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,
+    {
+        try_pin_init!(Self {
+            // We need to fully initialize the fence because after
+            // `dma_fence_add_callback()` ran, the callback might immediately
+            // get invoked.
+            callback: ManuallyDrop::new(callback),
+            fence: ARef::from(fence),
+            callback_foreign <- 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) => {
+                        // SAFETY: We could not register the callback. Thus,
+                        // C will not use it. So we can just take it back
+                        // and pass it to the user again.
+                        let cb_back = unsafe { ManuallyDrop::take(callback) };
+                        if e == ENOENT {
+                            Err(CallbackError::AlreadySignaled(cb_back))
+                        } else {
+                            Err(CallbackError::Other(e))
+                        }
+                    },
+                }
+            }),
+        }? CallbackError<T>)
+    }
+
+    /// 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,
+        callback_foreign: *mut bindings::dma_fence_cb,
+    ) {
+        let ptr = Opaque::cast_from(callback_foreign).cast_mut();
+
+        // SAFETY: All `cb` we can receive here have been created in such a way
+        // that they are embedded into a `FenceCallbackRegistration`. The
+        // backend ensures synchronisation so whoever holds the registration
+        // object cannot drop it while this code is running. See
+        // `FenceCallbackRegistration::drop`.
+        unsafe {
+            let reg: *mut Self = container_of!(ptr, Self, callback_foreign);
+
+            (*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: FenceCallback> PinnedDrop for FenceCallbackRegistration<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.callback_foreign.get());
+        }
+
+        // SAFETY: This is literally the drop implementation, so no one has
+        // dropped this so far; so we can do it now.
+        unsafe { ManuallyDrop::<T>::drop(self.project().callback) };
+    }
+}
+
+// SAFETY: FenceCallbackRegistration can be sent between threads.
+unsafe impl<T: FenceCallback> Send for FenceCallbackRegistration<T> {}
+
+// SAFETY: &FenceCallbackRegistration can be shared between threads if &T can.
+unsafe impl<T: FenceCallback> Sync for FenceCallbackRegistration<T> where T: Sync {}
+
+/// The receiving counterpart of a [`DriverFence`].
+///
+/// The Rust DMA fence implementation has a dualistic design: [`DriverFence`]s
+/// are the producer-side, intended to be always owned by only one party. That
+/// party has the monopoly on signalling the fence.
+///
+/// A [`Fence`] is the counterpart for consumers. Thus, [`Fence`]s are always
+/// refcounted and can shared with an arbitrary number of parties, including
+/// userspace. Hereby, a [`Fence`] can only be used for actions such as checking
+/// the fence's status or for registering callbacks on it.
+///
+/// Once the associated [`DriverFence`] signals, all
+/// [`FenceCallbackRegistration`]s registered on the [`Fence`] will be executed.
+///
+/// A [`Fence`] can arbitrarily outlive its [`DriverFence`] and the
+/// [`FenceContext`]. Signalling a [`DriverFence`] decouples it from its
+/// [`Fence`]s.
+#[repr(transparent)]
+pub struct Fence {
+    /// The actual dma_fence passed to C.
+    inner: Opaque<bindings::dma_fence>,
+}
+
+/// Guard helper for locking within this module.
+struct FenceGuard {
+    inner: *mut bindings::dma_fence,
+    flags: usize,
+}
+
+impl Deref for FenceGuard {
+    type Target = *mut bindings::dma_fence;
+
+    fn deref(&self) -> &Self::Target {
+        &self.inner
+    }
+}
+
+impl Drop for FenceGuard {
+    fn drop(&mut self) {
+        // SAFETY: `fence` is valid because `self` is valid. `flag_ptr` is
+        // merely a pointer to an integer, which lives as long as this function.
+        // When a `FenceGuard` exists, the lock has been taken by definition.
+        unsafe { bindings::dma_fence_unlock_irqrestore(self.inner, &raw mut self.flags) };
+    }
+}
+
+// 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 signaled 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 {
+        // We should 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: `fence` stems from `self`, which is valid by definition.
+        let ret = unsafe { bindings::dma_fence_is_signaled(self.as_raw()) };
+
+        // To be as robust as possible for the future we guarantee that an API
+        // caller can 100% rely on the signalling being completed (i.e., all
+        // fence callbacks ran), so 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.
+        //
+        // The fundamental reason is that the C backend currently does guard
+        // setting of the fence's signaled-bit with the fence's spinlock, but
+        // reading is done locklessly.
+        //
+        // See commit c8a5d5ea3ba6a.
+
+        let _ = self.lock();
+
+        ret
+    }
+
+    fn lock(&self) -> FenceGuard {
+        let mut guard = FenceGuard {
+            inner: self.as_raw(),
+            flags: 0,
+        };
+
+        // 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(guard.inner, &raw mut guard.flags) };
+
+        guard
+    }
+
+    /// Get the fence's sequence number.
+    pub fn get_seqno(&self) -> u64 {
+        // SAFETY: Valid because `self` is valid.
+        unsafe { (*self.as_raw()).seqno }
+    }
+
+    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) }
+    }
+}
+
+// Necessary to guarantee that `inner` always comes first and can be freed by C.
+// Also useful for using casts instead of container_of().
+#[repr(C)]
+#[pin_data]
+struct DriverFenceData<'a, T: Send + Sync + FenceContextOps> {
+    #[pin]
+    /// The inner fence.
+    // Must always be the first member so that unsafe casting works; but also
+    // necessary so that the C backend can free the allocation (coming from our
+    // Rust code) with kfree_rcu().
+    inner: Fence,
+    /// Callback head for dropping this in a deferred manner through RCU.
+    rcu_head: bindings::callback_head,
+    /// Reference to access the FenceContext. Useful for obtaining name parameters.
+    fctx: &'a FenceContext<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.
+///
+/// The Rust DMA fence implementation has a dualistic design: [`DriverFence`]s
+/// are the producer-side, intended to be always owned by only one party. That
+/// party has the monopoly on signalling the fence.
+///
+/// A [`Fence`] is the counterpart for consumers. Thus, [`Fence`]s are always
+/// refcounted and can shared with an arbitrary number of parties, including
+/// userspace. Hereby, a [`Fence`] can only be used for actions such as checking
+/// the fence's status or for registering callbacks on it.
+///
+/// Once the associated [`DriverFence`] signals, all
+/// [`FenceCallbackRegistration`]s registered on a [`Fence`] will be executed.
+///
+/// A [`Fence`] can arbitrarily outlive its [`DriverFence`] and the
+/// [`FenceContext`]. Signalling a [`DriverFence`] decouples it from its
+/// [`Fence`]s.
+///
+/// It is crucial that a [`DriverFence`] always correctly represents the state
+/// of the associated job on the hardware. Especially, it is strictly necessary
+/// that the owner ensures that all [`DriverFence`]s get eventually signaled.
+/// As a last ressort, a [`DriverFence`] will signal itself if it drops
+/// unsignaled.
+///
+/// This design intends to implement the [`bindings::dma_fence_ops`] in such a
+/// way that the driver-data necessary to implement the callback's functionality
+/// resides in the [`FenceContext`]. Thus, a [`DriverFence`] contains a
+/// reference to the context, which can be accessed in the callbacks. The
+/// implementation, therefore, ensures that a [`DriverFence`] cannot outlive its
+/// [`FenceContext`]. Unfortunately, this can be circumvented under certain
+/// circumstances in Rust. To mitigate this, the [`FenceContext`] will signal
+/// all unsignaled [`DriverFence`]s in the unlikely case of it dropping before
+/// the last fence.
+///
+/// # Examples
+///
+/// ```
+/// use kernel::dma_buf::{
+///     DriverFence,
+///     FenceContext,
+///     FenceContextOps,
+///     FenceCallback,
+///     FenceCallbackRegistration, //
+/// };
+/// use kernel::str::CString;
+/// use kernel::sync::aref::ARef;
+/// use core::fmt::Display;
+///
+/// struct CallbackData { }
+///
+/// impl FenceCallback for CallbackData {
+///     fn called(&mut self) {
+///         pr_info!("DmaFence callback executed.\n");
+///     }
+/// }
+///
+/// #[pin_data]
+/// struct FenceContextData {}
+///
+/// impl FenceContextData {
+///     fn new() -> impl PinInit<Self> {
+///         pin_init!(Self {})
+///     }
+/// }
+///
+/// impl FenceContextOps for FenceContextData {
+///     type FenceDataType = FenceData;
+/// }
+///
+/// let fctx_data = FenceContextData::new();
+///
+/// let driver_name = CString::try_from_fmt(fmt!("dummy_driver"))?;
+/// let timeline_name = CString::try_from_fmt(fmt!("dummy_timeline"))?;
+///
+/// let mut fctx = KBox::pin_init(FenceContext::new(0, driver_name, timeline_name, fctx_data), GFP_KERNEL)?;
+///
+/// struct FenceData {
+///     data: CString,
+/// }
+///
+/// let data = CString::try_from_fmt(fmt!("dummy_data"))?;
+/// let fence_data = FenceData { data };
+///
+/// let fence_alloc = fctx.fence_alloc(fence_data)?;
+/// let mut fence = fence_alloc.new_fence();
+///
+/// let cb_data = CallbackData { };
+/// let waiting_fence = ARef::from(fence.as_fence());
+/// let cb_reg = FenceCallbackRegistration::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<'a, T: Send + Sync + FenceContextOps> {
+    /// The actual content of the fence. Lives in a [`NonNull`] so that its
+    /// memory can be managed independently. Valid until both the [`DriverFence`]
+    /// and all associated [`Fence`]s have disappeared.
+    data: NonNull<DriverFenceData<'a, 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<'a, T: Send + Sync + FenceContextOps> {
+    /// The memory for the actual content of the fence.
+    /// Handed over to a [`DriverFence`], or deallocated once the
+    /// [`DriverFenceAllocation`] drops.
+    data: KBox<DriverFenceData<'a, T>>,
+    /// Pointer for the ops for the associated [`FenceContext`]
+    ops: *const bindings::dma_fence_ops,
+    /// Pre-allocated last ressort signaler to absolutely prevent unsignaled fences.
+    signaler: ListArc<FenceLastResortSignaler>,
+}
+
+impl<'a, T: Send + Sync + FenceContextOps> DriverFenceAllocation<'a, T> {
+    /// Create a new fence, consuming `data`.
+    ///
+    /// This increments the sequence number in the associated [`FenceContext`].
+    pub fn new_fence(self) -> DriverFence<'a, T> {
+        // 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 seqno = self.data.fctx.get_next_seqno();
+        let fence_ptr = self.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.data.fctx.nr, seqno)
+        };
+
+        let fence = ARef::from(&self.data.inner);
+
+        // A list element's are always refcounted, which forces us to establish
+        // that no one else is holding a reference. This element is in no list
+        // yet, so we can unwrap.
+        let mut signaler =
+            Arc::<FenceLastResortSignaler>::into_unique_or_drop(self.signaler.into_arc()).unwrap();
+        *signaler.as_mut().project().fence = Some(fence);
+
+        let signaler = ListArc::from(signaler);
+        self.data.fctx.fences.lock().push_back(signaler);
+
+        // 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(self.data);
+        // SAFETY: `ptr` was just created validly directly above.
+        let ptr = unsafe { NonNull::new_unchecked(ptr) };
+
+        DriverFence { data: ptr }
+    }
+
+    fn as_raw(&self) -> *mut bindings::dma_fence {
+        self.data.inner.inner.get()
+    }
+}
+
+impl<'a, T: Send + Sync + FenceContextOps> DriverFence<'a, 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
+    /// [`FenceContext`].
+    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<'a, 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()) }
+    }
+
+    // TODO: Base this on a more efficient data structure.
+    //
+    // The fctx needs to keep track of unsignaled fences to prevent UAF bugs if
+    // a `DriverFence` is forgotten. To do so, the fctx needs a data structure
+    // to keep track of all these fences.
+    //
+    // Originally, an XArray was supposed to do that. The current XArray
+    // implementation, however, is not suitable because:
+    //   a) this implementation needs to support 32-bit architectures. The fence
+    //      sequence number is a u64, and XArray tracks indices with a `usize`,
+    //      which can be u32 and therefore be incompatible.
+    //   b) XArray is in general not a good data structure for huge indices (i.e.,
+    //      those approaching U32_MAX), since it then ends up doing a lot of
+    //      pointer indirections.
+    //
+    // Our choice of data structures is limited by the fact that the entries to
+    // be stored need to be pre-allocated, and by what Rust4Linux currently
+    // provides. We cannot have the `ListArc` refcount exposed, so the only way
+    // to remove from the list for now is by searching for the sequence number.
+    //
+    // While in theory this means O(n) for signaling a fence, in practice it
+    // should usually be O(1) because fences on the same fence context are
+    // created, submitted and signaled in order. So this solution is good for
+    // now, and, as stated above, the only alternative (XArray) would also
+    // become very inefficient once the sequence number gets high.
+    //
+    // Anyways, the best data structure for this task is probably a hash table,
+    // which allows for getting the fence with O(1) and iterating over all
+    // fences once the context drops.
+    //
+    // Alternatively, someone could explore how the `ListArc` could be stored in
+    // the `DriverFenceData`. It would then have to be moved, however, and the
+    // cast into a `UniqueArc` would have to be considered. Having a data
+    // structure like a list has the advantage of no fence sequence numbers
+    // having to be preallocated.
+    fn remove_from_fctx_list(&self) {
+        // SAFETY: `self.data` is valid because `self` was validly initialized.
+        let mut guard = unsafe { self.data.as_ref().fctx.fences.lock() };
+        let mut cursor = guard.cursor_front();
+
+        while let Some(pos) = cursor.peek_next() {
+            // Fences in this list are always initialized, so `unwrap` is OK.
+            if self.as_fence().get_seqno() == pos.fence.as_ref().unwrap().get_seqno() {
+                let _ = pos.remove();
+            }
+        }
+    }
+
+    /// Signal the fence. This will invoke all registered callbacks.
+    pub fn signal(self, res: Result) {
+        // We signal this fence now. Remove it from the list of unsignaled fences.
+        self.remove_from_fctx_list();
+
+        let fence = self.as_fence().lock();
+
+        // 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.deref()) } {
+            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.deref(), 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.deref()) };
+        }
+    }
+}
+
+// SAFETY: Fences are literally designed to be shared between threads.
+unsafe impl<'a, T: Send + Sync + FenceContextOps> Send for DriverFence<'a, T> {}
+// SAFETY: Fences are literally designed to be shared between threads.
+unsafe impl<'a, T: Send + Sync + FenceContextOps> Sync for DriverFence<'a, T> {}
+
+impl<'a, T: Send + Sync + FenceContextOps> Deref for DriverFence<'a, 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 borrow wrapper for [`DriverFence`]. Implements [`Deref`].
+pub struct DriverFenceBorrow<'a, T: Send + Sync + FenceContextOps> {
+    driver_fence: ManuallyDrop<DriverFence<'a, T>>,
+    _lifetime: PhantomData<&'a T>,
+}
+
+impl<'a, T: Send + Sync + FenceContextOps> Deref for DriverFenceBorrow<'a, T> {
+    type Target = DriverFence<'a, T>;
+
+    fn deref(&self) -> &Self::Target {
+        self.driver_fence.deref()
+    }
+}
+
+// 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 + FenceContextOps + 'static> ForeignOwnable for DriverFence<'_, T> {
+    type Borrowed<'a>
+        = DriverFenceBorrow<'a, T>
+    where
+        Self: 'a;
+    type BorrowedMut<'a>
+        = DriverFenceBorrow<'a, T>
+    where
+        Self: 'a;
+
+    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>
+    where
+        Self: 'a,
+    {
+        // SAFETY: The trait implementation ensures that `ptr` always resides
+        // within a [`Fence`] within a [`DriverFenceData`].
+        let driver_fence = unsafe { Self::from_raw(ptr.cast()) };
+
+        let driver_fence = ManuallyDrop::new(driver_fence);
+
+        DriverFenceBorrow {
+            driver_fence,
+            _lifetime: PhantomData,
+        }
+    }
+
+    unsafe fn borrow_mut<'a>(ptr: *mut c_void) -> Self::BorrowedMut<'a>
+    // FIXME: The bound below and the one above in `borrow` should actually be
+    // unnecessary since the compiler should be able to completely derive all
+    // necessary information automatically. There is currently a compiler bug
+    // preventing that, though:
+    //
+    // https://github.com/rust-lang/rust/issues/155430.
+    //
+    // (Help to) fix the compiler bug and remove the bounds afterwards.
+    where
+        Self: 'a,
+    {
+        // SAFETY: The trait implementation ensures that `ptr` always resides
+        // within a [`Fence`] within a [`DriverFenceData`].
+        let driver_fence = unsafe { Self::from_raw(ptr.cast()) };
+
+        let driver_fence = ManuallyDrop::new(driver_fence);
+
+        DriverFenceBorrow {
+            driver_fence,
+            _lifetime: PhantomData,
+        }
+    }
+}
+
+impl<'a, T: Send + Sync + FenceContextOps> Drop for DriverFence<'a, T> {
+    fn drop(&mut self) {
+        let guard = self.as_fence().lock();
+
+        // 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: `guard` is valid until the `call_rcu()` below.
+        let signaled: bool = unsafe { bindings::dma_fence_test_signaled_flag(*guard.deref()) };
+        if warn_on!(!signaled) {
+            // SAFETY: `guard` 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(*guard.deref(), ECANCELED.to_errno()) };
+            // SAFETY: `guard` is valid until the `call_rcu()` below. The lock
+            // must be held, which we acquired above.
+            unsafe { bindings::dma_fence_signal_locked(*guard.deref()) };
+        }
+        drop(guard);
+
+        // 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 + FenceContextOps>(
+    head: *mut bindings::callback_head,
+) {
+    // SAFETY: Caller provides a pointer to the `rcu_head` field of a `DriverFenceData<C>`.
+    let fence_data = unsafe { container_of!(head, DriverFenceData<'_, T>, rcu_head) };
+
+    // SAFETY: `fence_data` was created validly above. All the fence's data will
+    // only drop below, but the raw pointer to the raw C `dma_fence` remains
+    // valid because the reference count is only decremented at the end of the
+    // function.
+    let fence = unsafe { (*fence_data).inner.inner.get() };
+
+    // SAFETY: `fence_data` was created validly above. A grace period has passed.
+    // All callbacks which might have had access to the `fctx` are gone now.
+    unsafe { drop_in_place(&raw mut (*fence_data).fctx) };
+
+    // SAFETY: `fence_data` was created validly above. The user has already
+    // dropped the only conventional accessor to the user data, the `DriverFence`,
+    // one grace period ago. All accessors are gone now.
+    unsafe { drop_in_place(&raw mut (*fence_data).data) };
+
+    // The inner `Fence` explicitly does not get dropped because there may be
+    // many more users / consumers, each holding their own reference.
+
+    // SAFETY: Once a `DriverFence` is initialized, the inner `fence` is
+    // valid and initialized. It is valid until the refcount drops
+    // to 0, which can earliest happen once we drop the `DriverFence`'s reference
+    // here.
+    unsafe { bindings::dma_fence_put(fence) };
+
+    // The actual memory the data associated with a `DriverFence` lives in
+    // gets freed by the C dma_fence backend once the fence's refcount reaches 0.
+}
diff --git a/rust/kernel/dma_buf/mod.rs b/rust/kernel/dma_buf/mod.rs
new file mode 100644
index 000000000000..4764a828642e
--- /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,
+    FenceCallback,
+    FenceCallbackRegistration,
+    FenceContext,
+    FenceContextOps, //
+};
diff --git a/rust/kernel/lib.rs b/rust/kernel/lib.rs
index 68f4d9a3425d..6221ebfe71df 100644
--- a/rust/kernel/lib.rs
+++ b/rust/kernel/lib.rs
@@ -67,6 +67,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.55.0


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

* [PATCH v7 5/5] MAINTAINERS: Add entry for Rust dma-buf
  2026-07-29  9:45 [PATCH v7 0/5] rust / dma_buf: Add abstractions for dma_fence Philipp Stanner
                   ` (3 preceding siblings ...)
  2026-07-29  9:45 ` [PATCH v7 4/5] rust: Add dma_fence abstractions Philipp Stanner
@ 2026-07-29  9:45 ` Philipp Stanner
  4 siblings, 0 replies; 11+ messages in thread
From: Philipp Stanner @ 2026-07-29  9:45 UTC (permalink / raw)
  To: Miguel Ojeda, Boqun Feng, Gary Guo, Björn Roy Baron,
	Benno Lossin, Andreas Hindborg, Alice Ryhl, Trevor Gross,
	Danilo Krummrich, Daniel Almeida, Tamir Duberstein,
	Alexandre Courbot, Onur Özkan, Sumit Semwal,
	Christian König, Philipp Stanner, Lyude Paul,
	Paul E. McKenney, Frederic Weisbecker, Neeraj Upadhyay,
	Joel Fernandes, Josh Triplett, Uladzislau Rezki, Steven Rostedt,
	Mathieu Desnoyers, Lai Jiangshan, Zqiang, Greg Kroah-Hartman,
	Asahi Lina, Burak Emir, Lorenzo Stoakes, FUJITA Tomonori,
	Eliot Courtney, Mirko Adzic, Timur Tabi, Daniel del Castillo,
	Boris Brezillon
  Cc: linux-kernel, rust-for-linux, linux-media, dri-devel,
	linaro-mm-sig, 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>
Acked-by: Christian König <christian.koenig@amd.com>
Acked-by: Sumit Semwal <sumit.semwal@linaro.org>
---
 MAINTAINERS | 5 +++++
 1 file changed, 5 insertions(+)

diff --git a/MAINTAINERS b/MAINTAINERS
index 15011f5752a9..5e5a23a21cd8 100644
--- a/MAINTAINERS
+++ b/MAINTAINERS
@@ -7622,6 +7622,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)
@@ -7635,6 +7636,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
@@ -8621,7 +8624,9 @@ T:	git https://gitlab.freedesktop.org/drm/rust/kernel.git
 F:	drivers/gpu/drm/nova/
 F:	drivers/gpu/drm/tyr/
 F:	drivers/gpu/nova-core/
+F:	rust/helpers/dma_fence.c
 F:	rust/helpers/gpu.c
+F:	rust/kernel/dma_buf/
 F:	rust/kernel/drm/
 F:	rust/kernel/gpu.rs
 F:	rust/kernel/gpu/
-- 
2.55.0


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

* Re: [PATCH v7 2/5] rust: types: implement ForeignOwnable for ARef<T>
  2026-07-29  9:45 ` [PATCH v7 2/5] rust: types: implement ForeignOwnable for ARef<T> Philipp Stanner
@ 2026-07-29  9:56   ` sashiko-bot
  0 siblings, 0 replies; 11+ messages in thread
From: sashiko-bot @ 2026-07-29  9:56 UTC (permalink / raw)
  To: Philipp Stanner; +Cc: dri-devel, ojeda, media-ci

Thank you for your contribution! Sashiko AI review found 1 potential issue(s) to consider:
- [Low] Missing `#[inline]` annotations on small functions in an abstraction. The `into_foreign`, `from_foreign`, `borrow`, and `borrow_mut` methods in the `ForeignOwnable` trait implementation for `ARef<T>` are small, forwarding-style wrappers but lack the required `#[inline]` attribute.
--

--- Patch [2]: [PATCH v7 2/5] rust: types: implement ForeignOwnable for ARef<T> ---
commit 8f27d3e39cb5663e64788b9670c36ec543c46826
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>. Since ARef represents shared ownership, BorrowedMut yields
a shared reference.

> diff --git a/rust/kernel/sync/aref.rs b/rust/kernel/sync/aref.rs
> index b721b2e00b986..5407666136595 100644
> --- a/rust/kernel/sync/aref.rs
> +++ b/rust/kernel/sync/aref.rs
[ ... ]
> +// 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 {

[Severity: Low]
This isn't a bug, but should the small methods implemented here
(into_foreign, from_foreign, borrow, and borrow_mut) include #[inline]
annotations?

The Rust subsystem guidelines note that small functions or forwarding methods
in abstractions need to be annotated with #[inline] to allow optimal inlining
across crate boundaries.

> +        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) }
> +    }
> +}

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

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

* Re: [PATCH v7 4/5] rust: Add dma_fence abstractions
  2026-07-29  9:45 ` [PATCH v7 4/5] rust: Add dma_fence abstractions Philipp Stanner
@ 2026-07-29 10:14   ` sashiko-bot
  0 siblings, 0 replies; 11+ messages in thread
From: sashiko-bot @ 2026-07-29 10:14 UTC (permalink / raw)
  To: Philipp Stanner; +Cc: dri-devel, ojeda, media-ci

Thank you for your contribution! Sashiko AI review found 5 potential issue(s) to consider:
- [Critical] Infinite loop under spinlock in `remove_from_fctx_list`.
- [Critical] IRQ-context Deadlock due to using non-IRQ-safe `SpinLock` for `fctx.fences`.
- [High] Use-After-Free in dma_fence_ops callbacks (get_driver_name / get_timeline_name).
- [High] Memory leak of `DriverFenceData` and list nodes when implicitly dropped.
- [Low] Compilation Error (use-after-move) in `FenceCallbackRegistration::new`.
--

--- Patch [4]: [PATCH v7 4/5] rust: Add dma_fence abstractions ---
commit 6943f462ac2321e63a53d88290790522a943cbc4
Author: Philipp Stanner <phasta@kernel.org>

rust: Add dma_fence abstractions

This commit introduces Rust abstractions for the C dma_fence framework.
It provides DriverFence and Fence types to decouple fence issuers and
consumers, along with a FenceContext for synchronization.

> diff --git a/rust/kernel/dma_buf/dma_fence.rs b/rust/kernel/dma_buf/dma_fence.rs
> new file mode 100644
> --- /dev/null
> +++ b/rust/kernel/dma_buf/dma_fence.rs
[ ... ]
> +pub struct FenceContext<T: FenceContextOps + Send + Sync> {
> +    /// The fence context number.
> +    nr: u64,
[ ... ]
> +    #[pin]
> +    fences: SpinLock<List<FenceLastResortSignaler>>,

[Severity: Critical]
Can this lead to an IRQ-context deadlock?

If a process-context thread acquires the lock during DriverFence creation,
and a hardware interrupt fires on the same CPU before the lock is released:

Process context:
fctx.fence_alloc()
    new_fence()
        fctx.fences.lock()

IRQ context on same CPU:
GPU job completes
    DriverFence::signal()
        remove_from_fctx_list()
            fctx.fences.lock()

It seems the interrupt handler will attempt to acquire the already-held
lock. Since SpinLock resolves to spin_lock() which leaves interrupts
enabled, would this cause the CPU to deadlock?

[ ... ]
> +    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()
> +    }

[Severity: High]
Could this result in a use-after-free?

If the device is removed and the FenceContext is dropped, userspace could
still hold a reference to the fence via a sync_file file descriptor. If
userspace then calls the SYNC_IOC_FILE_INFO ioctl, the C backend would
unconditionally invoke get_driver_name.

It appears fctx would point to freed memory, causing a use-after-free when
accessing driver_name.

[ ... ]
> +    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()
> +    }

[Severity: High]
Does this have the same use-after-free issue as get_driver_name above?

[ ... ]
> +    pub fn new<'a>(fence: &'a Fence, callback: T) -> impl PinInit<Self, CallbackError<T>> + 'a
> +    where
> +        T: 'a,
> +    {
> +        try_pin_init!(Self {
> +            // We need to fully initialize the fence because after
> +            // `dma_fence_add_callback()` ran, the callback might immediately
> +            // get invoked.
> +            callback: ManuallyDrop::new(callback),
> +            fence: ARef::from(fence),
> +            callback_foreign <- Opaque::try_ffi_init(|ptr| {
[ ... ]
> +                match ret {
> +                    Ok(()) => Ok(()),
> +                    Err(e) => {
> +                        // SAFETY: We could not register the callback. Thus,
> +                        // C will not use it. So we can just take it back
> +                        // and pass it to the user again.
> +                        let cb_back = unsafe { ManuallyDrop::take(callback) };

[Severity: Low]
Does this compile successfully?

It looks like the callback parameter is moved into ManuallyDrop::new(), and
then later accessed inside the try_ffi_init closure. This appears to
violate ownership rules with a use-after-move. Additionally, wouldn't
ManuallyDrop::take() expect a mutable reference rather than the underlying
type?

[ ... ]
> +    fn remove_from_fctx_list(&self) {
> +        // SAFETY: `self.data` is valid because `self` was validly initialized.
> +        let mut guard = unsafe { self.data.as_ref().fctx.fences.lock() };
> +        let mut cursor = guard.cursor_front();
> +
> +        while let Some(pos) = cursor.peek_next() {
> +            // Fences in this list are always initialized, so `unwrap` is OK.
> +            if self.as_fence().get_seqno() == pos.fence.as_ref().unwrap().get_seqno() {
> +                let _ = pos.remove();
> +            }
> +        }
> +    }

[Severity: Critical]
Can this cause an infinite loop?

If the current element's seqno does not match the signaled fence, the code
takes no action and fails to advance the cursor. The next iteration would
peek at the exact same element indefinitely, leading to a lockup under the
spinlock.

Should there be a cursor.move_next() call when the sequence number does not
match?

[ ... ]
> +impl<'a, T: Send + Sync + FenceContextOps> Drop for DriverFence<'a, T> {
> +    fn drop(&mut self) {
> +        let guard = self.as_fence().lock();
> +
> +        // 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: `guard` is valid until the `call_rcu()` below.
> +        let signaled: bool = unsafe { bindings::dma_fence_test_signaled_flag(*guard.deref()) };
> +        if warn_on!(!signaled) {
> +            // SAFETY: `guard` 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(*guard.deref(), ECANCELED.to_errno()) };
> +            // SAFETY: `guard` is valid until the `call_rcu()` below. The lock
> +            // must be held, which we acquired above.
> +            unsafe { bindings::dma_fence_signal_locked(*guard.deref()) };
> +        }
> +        drop(guard);
> +
> +        // SAFETY: Valid because `self` is valid.
> +        let rcu_head_ptr = unsafe { &raw mut (*self.data.as_ptr()).rcu_head };
[ ... ]
> +        unsafe { bindings::call_rcu(rcu_head_ptr, Some(drop_driver_fence_data::<T>)) };
> +    }
> +}

[Severity: High]
Does implicitly dropping a DriverFence leak the tracking nodes?

The drop implementation orchestrates the destruction and signals the fence,
but it seems it does not call self.remove_from_fctx_list().

If the driver implicitly drops the DriverFence on an error path without
explicitly calling signal(), the tracking node would remain in the
fctx.fences list indefinitely. This holds an ARef<Fence> that keeps the C
dma_fence refcount elevated, preventing the memory from being freed.

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

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

* Re: [PATCH v7 3/5] rust: sync: Add abstraction for rcu_barrier()
  2026-07-29  9:45 ` [PATCH v7 3/5] rust: sync: Add abstraction for rcu_barrier() Philipp Stanner
@ 2026-07-29 17:18   ` Paul E. McKenney
  2026-07-29 18:47     ` Gary Guo
  0 siblings, 1 reply; 11+ messages in thread
From: Paul E. McKenney @ 2026-07-29 17:18 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, Daniel Almeida, Tamir Duberstein,
	Alexandre Courbot, Onur Özkan, Sumit Semwal,
	Christian König, Lyude Paul, Frederic Weisbecker,
	Neeraj Upadhyay, Joel Fernandes, Josh Triplett, Uladzislau Rezki,
	Steven Rostedt, Mathieu Desnoyers, Lai Jiangshan, Zqiang,
	Greg Kroah-Hartman, Asahi Lina, Burak Emir, Lorenzo Stoakes,
	FUJITA Tomonori, Eliot Courtney, Mirko Adzic, Timur Tabi,
	Daniel del Castillo, Boris Brezillon, linux-kernel,
	rust-for-linux, linux-media, dri-devel, linaro-mm-sig, rcu

On Wed, Jul 29, 2026 at 11:45:40AM +0200, Philipp Stanner wrote:
> rcu_barrier() is a frequently used C function which is always safe to be
> called.

Just checking...  Here "always safe" means only from task level, with BH,
preemption, and interrupts all enabled, correct?

In contrast, if you do this:

	preempt_disable();
	rcu_barrier();
	preempt_enable();

the results won't be safe.

							Thanx, Paul

> Add a safe abstraction for rcu_barrier().
> 
> Signed-off-by: Philipp Stanner <phasta@kernel.org>
> Tested-by: Daniel Almeida <daniel.almeida@collabora.com>
> ---
>  rust/kernel/sync/rcu.rs | 20 ++++++++++++++++++++
>  1 file changed, 20 insertions(+)
> 
> diff --git a/rust/kernel/sync/rcu.rs b/rust/kernel/sync/rcu.rs
> index a32bef6e490b..7031ca5d2473 100644
> --- a/rust/kernel/sync/rcu.rs
> +++ b/rust/kernel/sync/rcu.rs
> @@ -50,3 +50,23 @@ fn drop(&mut self) {
>  pub fn read_lock() -> Guard {
>      Guard::new()
>  }
> +
> +/// Wait until all in-flight call_rcu() callbacks complete.
> +///
> +/// Note that this primitive does not necessarily wait for an RCU grace period
> +/// to complete.  For example, if there are no RCU callbacks queued anywhere
> +/// in the system, then rcu_barrier() is within its rights to return
> +/// immediately, without waiting for anything, much less an RCU grace period.
> +/// In fact, rcu_barrier() will normally not result in any RCU grace periods
> +/// beyond those that were already destined to be executed.
> +///
> +/// In kernels built with CONFIG_RCU_LAZY=y, this function also hurries all
> +/// pending lazy RCU callbacks.
> +///
> +/// Note that this is one of the RCU primitives which must not be called in
> +/// atomic context.
> +#[inline]
> +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.55.0
> 

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

* Re: [PATCH v7 3/5] rust: sync: Add abstraction for rcu_barrier()
  2026-07-29 17:18   ` Paul E. McKenney
@ 2026-07-29 18:47     ` Gary Guo
  2026-07-29 18:58       ` Paul E. McKenney
  0 siblings, 1 reply; 11+ messages in thread
From: Gary Guo @ 2026-07-29 18:47 UTC (permalink / raw)
  To: paulmck, Philipp Stanner
  Cc: Miguel Ojeda, Boqun Feng, Gary Guo, Björn Roy Baron,
	Benno Lossin, Andreas Hindborg, Alice Ryhl, Trevor Gross,
	Danilo Krummrich, Daniel Almeida, Tamir Duberstein,
	Alexandre Courbot, Onur Özkan, Sumit Semwal,
	Christian König, Lyude Paul, Frederic Weisbecker,
	Neeraj Upadhyay, Joel Fernandes, Josh Triplett, Uladzislau Rezki,
	Steven Rostedt, Mathieu Desnoyers, Lai Jiangshan, Zqiang,
	Greg Kroah-Hartman, Asahi Lina, Burak Emir, Lorenzo Stoakes,
	FUJITA Tomonori, Eliot Courtney, Mirko Adzic, Timur Tabi,
	Daniel del Castillo, Boris Brezillon, linux-kernel,
	rust-for-linux, linux-media, dri-devel, linaro-mm-sig, rcu

On Wed Jul 29, 2026 at 6:18 PM BST, Paul E. McKenney wrote:
> On Wed, Jul 29, 2026 at 11:45:40AM +0200, Philipp Stanner wrote:
>> rcu_barrier() is a frequently used C function which is always safe to be
>> called.
>
> Just checking...  Here "always safe" means only from task level, with BH,
> preemption, and interrupts all enabled, correct?
>
> In contrast, if you do this:
>
> 	preempt_disable();
> 	rcu_barrier();
> 	preempt_enable();
>
> the results won't be safe.

This is same for all sleepable functions. In order to avoid having to mark all
sleeping function unsafe, we've decided that sleeping from non-preemptable
context is "safe", but is a bug regardless.

Best,
Gary


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

* Re: [PATCH v7 3/5] rust: sync: Add abstraction for rcu_barrier()
  2026-07-29 18:47     ` Gary Guo
@ 2026-07-29 18:58       ` Paul E. McKenney
  0 siblings, 0 replies; 11+ messages in thread
From: Paul E. McKenney @ 2026-07-29 18:58 UTC (permalink / raw)
  To: Gary Guo
  Cc: Philipp Stanner, Miguel Ojeda, Boqun Feng, Björn Roy Baron,
	Benno Lossin, Andreas Hindborg, Alice Ryhl, Trevor Gross,
	Danilo Krummrich, Daniel Almeida, Tamir Duberstein,
	Alexandre Courbot, Onur Özkan, Sumit Semwal,
	Christian König, Lyude Paul, Frederic Weisbecker,
	Neeraj Upadhyay, Joel Fernandes, Josh Triplett, Uladzislau Rezki,
	Steven Rostedt, Mathieu Desnoyers, Lai Jiangshan, Zqiang,
	Greg Kroah-Hartman, Asahi Lina, Burak Emir, Lorenzo Stoakes,
	FUJITA Tomonori, Eliot Courtney, Mirko Adzic, Timur Tabi,
	Daniel del Castillo, Boris Brezillon, linux-kernel,
	rust-for-linux, linux-media, dri-devel, linaro-mm-sig, rcu,
	Julia Lawall

On Wed, Jul 29, 2026 at 07:47:46PM +0100, Gary Guo wrote:
> On Wed Jul 29, 2026 at 6:18 PM BST, Paul E. McKenney wrote:
> > On Wed, Jul 29, 2026 at 11:45:40AM +0200, Philipp Stanner wrote:
> >> rcu_barrier() is a frequently used C function which is always safe to be
> >> called.
> >
> > Just checking...  Here "always safe" means only from task level, with BH,
> > preemption, and interrupts all enabled, correct?
> >
> > In contrast, if you do this:
> >
> > 	preempt_disable();
> > 	rcu_barrier();
> > 	preempt_enable();
> >
> > the results won't be safe.
> 
> This is same for all sleepable functions. In order to avoid having to mark all
> sleeping function unsafe, we've decided that sleeping from non-preemptable
> context is "safe", but is a bug regardless.

Got it, thank you!

I tried to resist parameterizing "safe" over things like execution
contexts, but obviously failed to do so.  Besides, there is a name for
that sort of thing, and that name is "precondition".  ;-)

Adding Julia Lawall on CC based on her recent work with preconditions,
which might be an act of kindness or of vandalism.  You guys get to
decide.  ;-)

							Thanx, Paul

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

end of thread, other threads:[~2026-07-29 18:58 UTC | newest]

Thread overview: 11+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2026-07-29  9:45 [PATCH v7 0/5] rust / dma_buf: Add abstractions for dma_fence Philipp Stanner
2026-07-29  9:45 ` [PATCH v7 1/5] rust: error: add remaining error codes Philipp Stanner
2026-07-29  9:45 ` [PATCH v7 2/5] rust: types: implement ForeignOwnable for ARef<T> Philipp Stanner
2026-07-29  9:56   ` sashiko-bot
2026-07-29  9:45 ` [PATCH v7 3/5] rust: sync: Add abstraction for rcu_barrier() Philipp Stanner
2026-07-29 17:18   ` Paul E. McKenney
2026-07-29 18:47     ` Gary Guo
2026-07-29 18:58       ` Paul E. McKenney
2026-07-29  9:45 ` [PATCH v7 4/5] rust: Add dma_fence abstractions Philipp Stanner
2026-07-29 10:14   ` sashiko-bot
2026-07-29  9:45 ` [PATCH v7 5/5] MAINTAINERS: Add entry for Rust dma-buf Philipp Stanner

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox