On Fri, 03 Jul 2026 09:31:40 +0200 Philipp Stanner <[email protected]> wrote:
> C's dma_fence's are synchronisation primitives that will be needed by all > Rust GPU drivers. > > The dma_fence framework sets a number of rules, notably: > - fences must only be signalled once > - all fences must be signalled at some point > - fence error codes must only be set before signalling > - every pointer to a fence must be backed by a reference > > All those rules are being addressed by these abstractions. > > To cleanly decouple fence issuers and consumers, two types are provided: > - DriverFence: the only fence type that can be signalled and that > carries driver-specific data. > - Fence: the fence type to be shared with other drivers and / or > userspace. The only type callbacks can be registered on. > Cannot be signalled. > > Hereby, a Fence lives in the same chunk of memory as a DriverFence. Both > share the refcount of the underlying C dma_fence. Since this > implementation does not provide a custom dma_fence_backend_ops.release() > function, the memory is freed by the dma_fence backend once the refcount > drops to 0. > > To create a DriverFence, the user must first allocate a > DriverFenceAllocation, so that the creation of the DriverFence later on > can always succeed. Otherwise, deadlocks could occur if fences need to > be created in a GPU job submission path. > > Synchronization is ensured by the dma_fence backend. > > All DriverFence's created through this abstraction must be signalled by > the creator with an error code. In case a DriverFence drops without > being signalled beforehand, it is signalled with -ECANCELLED as its > error and a warning is printed. This allows the Rust abstraction to very > cleanly decouple fence issuer and consumer by relying on the decoupling > mechanisms in the C backend, which ensures through RCU and the > 'signalled' fence-flag that dma_fence_backend_ops functions cannot > access the potentially unloaded driver code anymore. > > Signalling fences on drop thus grants many advantages. Not signalling > fences on drop would risk deadlock and does not grant real advantages: > By definition only the drivers can ensure that a fence always represents > the hardware's state correctly. > > This implementation models a DmaFenceCtx (fence context) object on which > fences are to be created, thereby ensuring correct sequence numbering > according to the timeline. > > dma_fence supports a variety of callbacks. The mandatory callbacks > (get_timeline_name() and get_driver_name()) are implemented in this > patch. For convenience, they store those name parameters in the fence > context, saving the driver from implementing these two callbacks. > > Support for other callbacks (like for hardware signalling) is prepared > for through the fact that both DriverFence and Fence live in the same > allocation, allowing for usage of container_of from the callback to > access the driver-specific data. > > Synchronization for backend_ops callbacks is ensured by only running the > Rust deconstructor delayed with call_rcu(), which prevents UAF-bugs > should a DriverFence drop while a Fence callback is currently operating > on the associated driver data. > > Add abstractions for dma_fence in Rust. > > Signed-off-by: Philipp Stanner <[email protected]> > --- > rust/bindings/bindings_helper.h | 1 + > rust/helpers/dma_fence.c | 48 ++ > rust/helpers/helpers.c | 1 + > rust/kernel/dma_buf/dma_fence.rs | 894 +++++++++++++++++++++++++++++++ > rust/kernel/dma_buf/mod.rs | 14 + > rust/kernel/lib.rs | 1 + > 6 files changed, 959 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..cbe8f447a603 > --- /dev/null > +++ b/rust/kernel/dma_buf/dma_fence.rs > @@ -0,0 +1,894 @@ > +// SPDX-License-Identifier: GPL-2.0 > +// > +// Copyright (T) 2025, 2026 Red Hat Inc.: > +// - Philipp Stanner <[email protected]> > + > +//! DriverFence support. > +//! > +//! Reference: <https://docs.kernel.org/driver-api/dma-buf.html#c.dma_fence> > +//! > +//! T header: > [`include/linux/dma-fence.h`](srctree/include/linux/dma-fence.h) > + > +use crate::{ > + alloc::AllocError, > + bindings, > + container_of, > + error::to_result, > + prelude::*, > + types::ForeignOwnable, > + types::Opaque, > + warn_on, // > +}; > + > +use core::{ > + marker::PhantomData, > + mem::ManuallyDrop, > + ops::Deref, > + ptr, > + ptr::{ > + drop_in_place, > + NonNull, // > + }, > + sync::atomic::{ > + AtomicU64, > + Ordering, // > + }, // > +}; > + > +use kernel::{ > + str::CString, > + sync::{ > + aref::{ > + ARef, > + AlwaysRefCounted, // > + }, > + rcu::rcu_barrier, // > + }, // > +}; > + > +/// VTable for dma_fence backend_ops callbacks. > +// > +// Mandatory dma_fence backend_ops are implemented implicitly through > +// [`FenceCtx`]. Additional ones shall get implemented on this trait, which > then > +// shall be demanded for the fence context data. > +pub trait FenceCtxOps { > + /// The generic payload data for [`DriverFence`]s created on this fctx. > + type FenceDataType: Send + Sync; > +} > + > +/// A dma-fence context. A fence context takes care of associating related > fences > +/// with each other, providing each with raising sequence numbers and a > common > +/// identifier. > +#[pin_data(PinnedDrop)] > +pub struct FenceCtx<T: Send + Sync> { > + /// The fence context number. > + nr: u64, > + /// The sequence number for the next fence created. > + seqno: AtomicU64, > + // The name parameters can be accessed by the dma_fence backend_ops. UAF > + // errors are prevented by the `call_rcu()` in > `drop_driver_fence_data()`. > + /// The name of the driver this FenceCtx's fences belong to. > + driver_name: CString, > + /// The name of the timeline this FenceCtx's fences belong to. > + timeline_name: CString, > + /// The user's data. > + #[pin] > + data: T, > +} > + > +#[allow(unused_unsafe)] Why is this needed? > +impl<'a, T: Send + Sync + FenceCtxOps> FenceCtx<T> { > + // This can later be extended as a vtable in case other parties need > support > + // for the more "exotic" callbacks. > + const OPS: bindings::dma_fence_ops = bindings::dma_fence_ops { > + get_driver_name: Some(Self::get_driver_name), > + get_timeline_name: Some(Self::get_timeline_name), > + enable_signaling: None, > + signaled: None, > + wait: None, > + release: None, > + set_deadline: None, > + }; > + > + /// Create a new `FenceCtx`. > + pub fn new<E>( > + driver_name: CString, > + timeline_name: CString, > + data: impl PinInit<T, E>, > + ) -> impl PinInit<Self, Error> > + where [...] > + unsafe { bindings::dma_fence_put(fence) }; > + > + // The actual memory the data associated with a `DriverFence` lives in > + // gets freed by the C dma_fence backend once the fence's refcount > reaches 0. > +} > diff --git a/rust/kernel/dma_buf/mod.rs b/rust/kernel/dma_buf/mod.rs > new file mode 100644 > index 000000000000..fb353ce042ce > --- /dev/null > +++ b/rust/kernel/dma_buf/mod.rs > @@ -0,0 +1,14 @@ > +// SPDX-License-Identifier: GPL-2.0 OR MIT > + > +//! DMA-buf subsystem abstractions. > + > +pub mod dma_fence; > + > +pub use self::dma_fence::{ > + DriverFence, > + Fence, > + FenceCb, > + FenceCbRegistration, > + FenceCtx, > + FenceCtxOps, // > +}; > diff --git a/rust/kernel/lib.rs b/rust/kernel/lib.rs > index 9512af7156df..dc54d8af471f 100644 > --- a/rust/kernel/lib.rs > +++ b/rust/kernel/lib.rs > @@ -64,6 +64,7 @@ > pub mod device_id; > pub mod devres; > pub mod dma; > +pub mod dma_buf; > pub mod driver; > #[cfg(CONFIG_DRM = "y")] > pub mod drm; > -- > 2.54.0 >
