From nobody Mon Jun 8 09:48:00 2026 Received: from smtp.kernel.org (aws-us-west-2-korg-mail-alma10-1.taild15c8.ts.net [100.103.45.18]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by smtp.subspace.kernel.org (Postfix) with ESMTPS id D42B124EA90; Sat, 30 May 2026 14:36:24 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=100.103.45.18 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1780151785; cv=none; b=JquFIaaIfex2x1uBwev4KVhzdmmPD+zXs94q+tRhc/euc5gkQ4ZwprIZOXN8aSDZz/SASCNjvtmIWAiG0XrZD0mWyOEUC0KmknlIpFJ8eFHwME7TWLVufziZb8ijYketHotc+RM/P2+xdGMRw+lPT9nBX3rdDPTo3SIMtI2GsTM= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1780151785; c=relaxed/simple; bh=iusRewpZ34TnQ5G2HLBSE7H0Lnu251CvtBRxV7HZGC0=; h=From:To:Cc:Subject:Date:Message-ID:In-Reply-To:References: MIME-Version; b=UPx8GK3juxeBvBWp+awHW+fqaXCXdQ6BJgWFbksbA1l3M9MEJzZMGE90FrjBM59mh5QS1PyRaXCrrzvgrL/wgMYkdCAE7SdcfFqJ+O5ULxm/5rHqCt2GAYLaJaunTMxoZ+9TNcp16B5zLIAjlom352y/c1eGT7V8HqukchTR3qk= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=kernel.org header.i=@kernel.org header.b=CVR7LIu9; arc=none smtp.client-ip=100.103.45.18 Authentication-Results: smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=kernel.org header.i=@kernel.org header.b="CVR7LIu9" Received: by smtp.kernel.org (Postfix) with ESMTPSA id 3121E1F00898; Sat, 30 May 2026 14:36:15 +0000 (UTC) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=kernel.org; s=k20260515; t=1780151784; bh=pDJyMWyNek1/CTCATK0JZj8GklloT+oL+OiV9mbnCoA=; h=From:To:Cc:Subject:Date:In-Reply-To:References; b=CVR7LIu9D96HTbHsL8aBjWenUaBybuuukmD4fMNjxcMZnl63zeJhQ8xZBs0if29ua RutT4/vJgD70WtBIhs7aHz7+KoobG2F2itlJcCPiHANBtwgd0ZPgqdeaqEB/j5EIQF z2R8gfk0d9wJFpdGs/FXn/ikx6lpcn3R1Lh4jmhp+4/rPSzI+L3QzULgEsFRPmIjyV bqHR8sEKi4+XG+mT/+2MEIq+M8uoZ6dzlEWhQpEFJ6RgFo8wRJW53uXppvZ3NHqEar agGq7cBi/W+qCHpg+h4mbcNQ2Nn2DkAZtfYLJ7LK6e6eoPT1oQcVFI+hHFE4uMZ7Lz hoQc9Z5K74OmQ== From: Philipp Stanner To: Miguel Ojeda , Boqun Feng , Gary Guo , =?UTF-8?q?Bj=C3=B6rn=20Roy=20Baron?= , Benno Lossin , Andreas Hindborg , Alice Ryhl , Trevor Gross , Danilo Krummrich , Sumit Semwal , =?UTF-8?q?Christian=20K=C3=B6nig?= , Philipp Stanner , "Paul E. McKenney" , Frederic Weisbecker , Neeraj Upadhyay , Joel Fernandes , Josh Triplett , Uladzislau Rezki , Steven Rostedt , Mathieu Desnoyers , Lai Jiangshan , Zqiang , Daniel Almeida , Greg Kroah-Hartman , Igor Korotin , Lorenzo Stoakes , Alexandre Courbot , FUJITA Tomonori , Krishna Ketan Rai , Shankari Anand , manos@pitsidianak.is, Boris Brezillon Cc: linux-kernel@vger.kernel.org, rust-for-linux@vger.kernel.org, linux-media@vger.kernel.org, dri-devel@lists.freedesktop.org, linaro-mm-sig@lists.linaro.org, rcu@vger.kernel.org Subject: [PATCH 1/4] rust: types: implement ForeignOwnable for ARef Date: Sat, 30 May 2026 16:35:09 +0200 Message-ID: <20260530143541.229628-3-phasta@kernel.org> X-Mailer: git-send-email 2.54.0 In-Reply-To: <20260530143541.229628-2-phasta@kernel.org> References: <20260530143541.229628-2-phasta@kernel.org> Precedence: bulk X-Mailing-List: linux-kernel@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 Content-Transfer-Encoding: quoted-printable Content-Type: text/plain; charset="utf-8" From: Danilo Krummrich Implement ForeignOwnable for ARef, making it possible for C code to own an ARef. 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 Reviewed-by: Alice Ryhl Tested-by: Daniel Almeida --- rust/kernel/sync/aref.rs | 39 +++++++++++++++++++++++++++++++++++++++ 1 file changed, 39 insertions(+) diff --git a/rust/kernel/sync/aref.rs b/rust/kernel/sync/aref.rs index 9989f56d0605..82907383c44b 100644 --- a/rust/kernel/sync/aref.rs +++ b/rust/kernel/sync/aref.rs @@ -17,6 +17,10 @@ //! [`Arc`]: crate::sync::Arc //! [`Arc`]: crate::sync::Arc =20 +use crate::{ + prelude::*, + types::ForeignOwnable, // +}; use core::{marker::PhantomData, mem::ManuallyDrop, ops::Deref, ptr::NonNul= l}; =20 /// Types that are _always_ reference counted. @@ -183,6 +187,41 @@ fn eq(&self, other: &ARef) -> bool { } impl Eq for ARef {} =20 +// 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 a= ligned to `T`. +unsafe impl ForeignOwnable for ARef { + const FOREIGN_ALIGN: usize =3D core::mem::align_of::(); + + type Borrowed<'a> =3D &'a T; + type BorrowedMut<'a> =3D &'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 `p= tr` comes from a previous + // call to `Self::into_foreign`. + let ptr =3D 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 { ::borrow(ptr) } + } +} + impl PartialEq<&'_ U> for ARef where T: AlwaysRefCounted + PartialEq, --=20 2.54.0 From nobody Mon Jun 8 09:48:00 2026 Received: from smtp.kernel.org (aws-us-west-2-korg-mail-alma10-1.taild15c8.ts.net [100.103.45.18]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by smtp.subspace.kernel.org (Postfix) with ESMTPS id 0078124EA90; Sat, 30 May 2026 14:36:37 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=100.103.45.18 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1780151799; cv=none; b=QsEtfacLuGR6DWMe6EBzJcoFEA5FUWLT3/6KlpQKTv+YvESzd6ThDazjrU9IFpYG3Y0jzIAiP8YnqW5Ks4pnDtYR4Wui1Fha++MPulp838V9VGmpoXxMg0PozlJhCdyCiHI66voKghvNDmhu8sHL/cIZMVLJr66+7tVZ/hyv/Vg= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1780151799; c=relaxed/simple; bh=j13bHhU0pkqLtqhmThdRI5vZsTfYYx3E5DZ+AN72eto=; h=From:To:Cc:Subject:Date:Message-ID:In-Reply-To:References: MIME-Version; b=IRLP7Sq+6JYoOqk/yS10BzXx+VVf3fWf+1BycK5xFVcsg0og4+5RBkdXGcPwgip09aFCIdXfrGuGX57n6xvJY67DX6OhEs515KSqPG3gBuA58F8aKUcqVeUh/oe+urG1XbD2D13q0MvMVJtSLCooh9qeXBmjtdLCCZYFydsw1yA= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=kernel.org header.i=@kernel.org header.b=k3TVWN3V; arc=none smtp.client-ip=100.103.45.18 Authentication-Results: smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=kernel.org header.i=@kernel.org header.b="k3TVWN3V" Received: by smtp.kernel.org (Postfix) with ESMTPSA id 7D1761F00893; Sat, 30 May 2026 14:36:29 +0000 (UTC) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=kernel.org; s=k20260515; t=1780151797; bh=dNbTGAbme9kt3YqMUKN2FspzxRjZvUcj7K7J7y2iZ3U=; h=From:To:Cc:Subject:Date:In-Reply-To:References; b=k3TVWN3Vqh1wLDUNxAj0ciKt0MCWTe82+l/lyquU2HNALCAGyRjrd2Xz6kK7zpwJV ySoA8kHlSDWUeJxlZGDcXicjcv6GuBeLeV9QkJiyDySp4xSl3rrQ5I8t/01AZcESwU tWBGeHGmS7URJ+u1p5877i19c+WZP+jTULHzT2KvFTH9cE264BgBxNT6AEVtApJ7rV tZdmicfQSQPD7FuBVjkF0sPW2PmjOFlFSCQCzmbZtWM8q1kWRoHJyAoHRbNQIeX6oA eIPgChh/13ZCbSIaad9svInJCGiTgwfoosmrkG2ZnToQjpbJOevEFlDnBbszSy2U+D 4K0iTTcEsxptg== From: Philipp Stanner To: Miguel Ojeda , Boqun Feng , Gary Guo , =?UTF-8?q?Bj=C3=B6rn=20Roy=20Baron?= , Benno Lossin , Andreas Hindborg , Alice Ryhl , Trevor Gross , Danilo Krummrich , Sumit Semwal , =?UTF-8?q?Christian=20K=C3=B6nig?= , Philipp Stanner , "Paul E. McKenney" , Frederic Weisbecker , Neeraj Upadhyay , Joel Fernandes , Josh Triplett , Uladzislau Rezki , Steven Rostedt , Mathieu Desnoyers , Lai Jiangshan , Zqiang , Daniel Almeida , Greg Kroah-Hartman , Igor Korotin , Lorenzo Stoakes , Alexandre Courbot , FUJITA Tomonori , Krishna Ketan Rai , Shankari Anand , manos@pitsidianak.is, Boris Brezillon Cc: linux-kernel@vger.kernel.org, rust-for-linux@vger.kernel.org, linux-media@vger.kernel.org, dri-devel@lists.freedesktop.org, linaro-mm-sig@lists.linaro.org, rcu@vger.kernel.org Subject: [PATCH 2/4] rust: rcu: add RcuBox type Date: Sat, 30 May 2026 16:35:10 +0200 Message-ID: <20260530143541.229628-4-phasta@kernel.org> X-Mailer: git-send-email 2.54.0 In-Reply-To: <20260530143541.229628-2-phasta@kernel.org> References: <20260530143541.229628-2-phasta@kernel.org> Precedence: bulk X-Mailing-List: linux-kernel@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 Content-Transfer-Encoding: quoted-printable Content-Type: text/plain; charset="utf-8" From: Alice Ryhl This adds an RcuBox container, which is like KBox except that the value is freed with kfree_rcu. To allow containers to rely on the rcu properties of RcuBox, an extension of ForeignOwnable is added. Signed-off-by: Alice Ryhl Tested-by: Daniel Almeida --- rust/bindings/bindings_helper.h | 1 + rust/kernel/sync/rcu.rs | 31 ++++++- rust/kernel/sync/rcu/rcu_box.rs | 145 ++++++++++++++++++++++++++++++++ 3 files changed, 176 insertions(+), 1 deletion(-) create mode 100644 rust/kernel/sync/rcu/rcu_box.rs diff --git a/rust/bindings/bindings_helper.h b/rust/bindings/bindings_helpe= r.h index 446dbeaf0866..2011645c7cfb 100644 --- a/rust/bindings/bindings_helper.h +++ b/rust/bindings/bindings_helper.h @@ -80,6 +80,7 @@ #include #include #include +#include #include #include #include diff --git a/rust/kernel/sync/rcu.rs b/rust/kernel/sync/rcu.rs index a32bef6e490b..7234fe3e79ee 100644 --- a/rust/kernel/sync/rcu.rs +++ b/rust/kernel/sync/rcu.rs @@ -4,7 +4,16 @@ //! //! C header: [`include/linux/rcupdate.h`](srctree/include/linux/rcupdate.= h) =20 -use crate::{bindings, types::NotThreadSafe}; +use crate::{ + bindings, + types::{ + ForeignOwnable, + NotThreadSafe, // + }, // +}; + +mod rcu_box; +pub use self::rcu_box::RcuBox; =20 /// Evidence that the RCU read side lock is held on the current thread/CPU. /// @@ -50,3 +59,23 @@ fn drop(&mut self) { pub fn read_lock() -> Guard { Guard::new() } + +/// Declares that a pointer type is rcu safe. +pub trait ForeignOwnableRcu: ForeignOwnable { + /// Type used to immutably borrow an rcu-safe value that is currently = foreign-owned. + type RcuBorrowed<'a>; + + /// Borrows a foreign-owned object immutably for an rcu grace period. + /// + /// This method provides a way to access a foreign-owned rcu-safe valu= e from Rust immutably. + /// + /// # Safety + /// + /// * The provided pointer must have been returned by a previous call = to [`into_foreign`]. + /// * If [`from_foreign`] is called, then `'a` must not end after the = call to `from_foreign` + /// plus one rcu grace period. + /// + /// [`into_foreign`]: ForeignOwnable::into_foreign + /// [`from_foreign`]: ForeignOwnable::from_foreign + unsafe fn rcu_borrow<'a>(ptr: *mut ffi::c_void) -> Self::RcuBorrowed<'= a>; +} diff --git a/rust/kernel/sync/rcu/rcu_box.rs b/rust/kernel/sync/rcu/rcu_box= .rs new file mode 100644 index 000000000000..2508fdb609ec --- /dev/null +++ b/rust/kernel/sync/rcu/rcu_box.rs @@ -0,0 +1,145 @@ +// SPDX-License-Identifier: GPL-2.0 + +// Copyright (C) 2026 Google LLC. + +//! Provides the `RcuBox` type for Rust allocations that live for a grace = period. + +use core::{ops::Deref, ptr::NonNull}; + +use kernel::{ + alloc::{self, AllocError}, + bindings, + ffi::c_void, + prelude::*, + sync::rcu::{ForeignOwnableRcu, Guard}, + types::ForeignOwnable, +}; + +/// A box that is freed with rcu. +/// +/// The value must be `Send`, as rcu may drop it on another thread. +/// +/// # Invariants +/// +/// * The pointer is valid and references a pinned `RcuBoxInner` alloca= ted with `kmalloc`. +/// * This `RcuBox` holds exclusive permissions to rcu free the allocation. +pub struct RcuBox(NonNull>); + +struct RcuBoxInner { + value: T, + rcu_head: bindings::callback_head, +} + +// Note that `T: Sync` is required since when moving an `RcuBox`, the p= revious owner may still +// access `&T` for one grace period. +// +// SAFETY: Ownership of the `RcuBox` allows for `&T` and dropping the `= T`, so `T: Send + Sync` +// implies `RcuBox: Send`. +unsafe impl Send for RcuBox {} + +// SAFETY: `&RcuBox` allows for no operations other than those permitte= d by `&T`, so `T: Sync` +// implies `RcuBox: Sync`. +unsafe impl Sync for RcuBox {} + +impl RcuBox { + /// Create a new `RcuBox`. + pub fn new(x: T, flags: alloc::Flags) -> Result { + let b =3D KBox::new( + RcuBoxInner { + value: x, + rcu_head: Default::default(), + }, + flags, + )?; + + // INVARIANT: + // * The pointer contains a valid `RcuBoxInner` allocated with `km= alloc`. + // * We just allocated it, so we own free permissions. + Ok(RcuBox(NonNull::from(KBox::leak(b)))) + } + + /// Access the value for a grace period. + pub fn with_rcu<'rcu>(&self, _read_guard: &'rcu Guard) -> &'rcu T { + // SAFETY: The `RcuBox` has not been dropped yet, so the value is = valid for at least one + // grace period. + unsafe { &(*self.0.as_ptr()).value } + } +} + +impl Deref for RcuBox { + type Target =3D T; + fn deref(&self) -> &T { + // SAFETY: While the `RcuBox` exists, the value remains valid. + unsafe { &(*self.0.as_ptr()).value } + } +} + +// SAFETY: +// * The `RcuBoxInner` was allocated with `kmalloc`. +// * `NonNull::as_ptr` returns a non-null pointer. +unsafe impl ForeignOwnable for RcuBox { + const FOREIGN_ALIGN: usize =3D > as ForeignOwnable= >::FOREIGN_ALIGN; + + type Borrowed<'a> =3D &'a T; + type BorrowedMut<'a> =3D &'a T; + + fn into_foreign(self) -> *mut c_void { + self.0.as_ptr().cast() + } + + unsafe fn from_foreign(ptr: *mut c_void) -> Self { + // INVARIANT: Pointer returned by `into_foreign` carries same inva= riants as `RcuBox`. + // SAFETY: `into_foreign` never returns a null pointer. + Self(unsafe { NonNull::new_unchecked(ptr.cast()) }) + } + + unsafe fn borrow<'a>(ptr: *mut c_void) -> &'a T { + // SAFETY: Caller ensures that `'a` is short enough. + unsafe { &(*ptr.cast::>()).value } + } + + unsafe fn borrow_mut<'a>(ptr: *mut c_void) -> &'a T { + // SAFETY: `borrow_mut` has strictly stronger preconditions than `= borrow`. + unsafe { Self::borrow(ptr) } + } +} + +impl ForeignOwnableRcu for RcuBox { + type RcuBorrowed<'a> =3D &'a T; + + unsafe fn rcu_borrow<'a>(ptr: *mut c_void) -> &'a T { + // SAFETY: `RcuBox::drop` can only run after `from_foreign` is cal= led, and the value is + // valid until `RcuBox::drop` plus one grace period. + unsafe { &(*ptr.cast::>()).value } + } +} + +impl Drop for RcuBox { + fn drop(&mut self) { + // SAFETY: The `rcu_head` field is in-bounds of a valid allocation. + let rcu_head =3D unsafe { &raw mut (*self.0.as_ptr()).rcu_head }; + if core::mem::needs_drop::() { + // SAFETY: `rcu_head` is the `rcu_head` field of `RcuBoxInner<= T>`. All users will be + // gone in an rcu grace period. This is the destructor, so we = may pass ownership of the + // allocation. + unsafe { bindings::call_rcu(rcu_head, Some(drop_rcu_box::))= }; + } else { + // SAFETY: All users will be gone in an rcu grace period. + unsafe { bindings::kvfree_call_rcu(rcu_head, self.0.as_ptr().c= ast()) }; + } + } +} + +/// Free this `RcuBoxInner`. +/// +/// # Safety +/// +/// `head` references the `rcu_head` field of an `RcuBoxInner` that has= no references to it. +/// Ownership of the `KBox>` must be passed. +unsafe extern "C" fn drop_rcu_box(head: *mut bindings::callback_head) { + // SAFETY: Caller provides a pointer to the `rcu_head` field of a `Rcu= BoxInner`. + let box_inner =3D unsafe { crate::container_of!(head, RcuBoxInner, = rcu_head) }; + + // SAFETY: Caller ensures exclusive access and passed ownership. + drop(unsafe { KBox::from_raw(box_inner) }); +} --=20 2.54.0 From nobody Mon Jun 8 09:48:00 2026 Received: from smtp.kernel.org (aws-us-west-2-korg-mail-alma10-1.taild15c8.ts.net [100.103.45.18]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by smtp.subspace.kernel.org (Postfix) with ESMTPS id 8859F1940B0; Sat, 30 May 2026 14:36:51 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=100.103.45.18 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1780151813; cv=none; b=K7pExYC8AtCOnGJJrT9PdfkhD6jpmVvbXtsCbL3yoFb2nwvbG44gM6jmYi/2AqOVNmwgDQ2IUNy8IEdLCjigHuAwX7Yuidk4etMyr3Zoe1ZxlnnTxeXXbtv271zHG9U9IIjgINfVrswj/jo7Mpy/wLRUqfka+1154UrLdCNAXzg= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1780151813; c=relaxed/simple; bh=fDY8f3wuXZdFOUbnMN9sKg1SD+kkBURaOd5YEtl1liU=; h=From:To:Cc:Subject:Date:Message-ID:In-Reply-To:References: MIME-Version:Content-Type; b=AnMTRgZp1BjBUWm5TjU25EQcWA5Vm92728LejU3rTKW37ht+EWSqzsY13kN46zw5CvxwT6qg3Q1jiz8Okd/+YcvJ1wRXB2VJfSJsCbVeRp6NIG7OAQHEkTk9WQhSAxWC8/ZKl1Rv3n7XMJUBjrKOowb1hdj1Q+Ql9vCdlFVxj2k= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=kernel.org header.i=@kernel.org header.b=WQRAnw2H; arc=none smtp.client-ip=100.103.45.18 Authentication-Results: smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=kernel.org header.i=@kernel.org header.b="WQRAnw2H" Received: by smtp.kernel.org (Postfix) with ESMTPSA id C9A6A1F00893; Sat, 30 May 2026 14:36:42 +0000 (UTC) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=kernel.org; s=k20260515; t=1780151811; bh=qkuj8OhFHXyqgQPKbUxCfeYfitjkYWllEG/4ZUUkFOw=; h=From:To:Cc:Subject:Date:In-Reply-To:References; b=WQRAnw2HQgIsqSKDXzcLZipXjK0V/obgzPs8erOmYqRkteeJMMIMVLevcaiekpCv3 JYcZiBX1bKv+H6/WBk/DAa1JzsM6cooRBBFIOukcAEdhLiSodttKIivVh6zObyN7h0 Di73YjxOmnxdTWuS6I2qhvk6BHNzD/O1HBtEyMWdDYmthznlLGCliIbJ8GQqpluycE /tEBC7frXxcf3yNJ8krBoAzRRNdo689R0Df5D1ANvgBvodffkW0gvuTRg3b0EYkO0/ eVrTk5IAQiSviR/Y5RUu6i+3cNzz2NQZXI1QDo8/3jZkOdaP+SuM3GpkevlsPXoKUd eGF4oNtXyvsHg== From: Philipp Stanner To: Miguel Ojeda , Boqun Feng , Gary Guo , =?UTF-8?q?Bj=C3=B6rn=20Roy=20Baron?= , Benno Lossin , Andreas Hindborg , Alice Ryhl , Trevor Gross , Danilo Krummrich , Sumit Semwal , =?UTF-8?q?Christian=20K=C3=B6nig?= , Philipp Stanner , "Paul E. McKenney" , Frederic Weisbecker , Neeraj Upadhyay , Joel Fernandes , Josh Triplett , Uladzislau Rezki , Steven Rostedt , Mathieu Desnoyers , Lai Jiangshan , Zqiang , Daniel Almeida , Greg Kroah-Hartman , Igor Korotin , Lorenzo Stoakes , Alexandre Courbot , FUJITA Tomonori , Krishna Ketan Rai , Shankari Anand , manos@pitsidianak.is, Boris Brezillon Cc: linux-kernel@vger.kernel.org, rust-for-linux@vger.kernel.org, linux-media@vger.kernel.org, dri-devel@lists.freedesktop.org, linaro-mm-sig@lists.linaro.org, rcu@vger.kernel.org Subject: [PATCH 3/4] rust: Add dma_fence abstractions Date: Sat, 30 May 2026 16:35:11 +0200 Message-ID: <20260530143541.229628-5-phasta@kernel.org> X-Mailer: git-send-email 2.54.0 In-Reply-To: <20260530143541.229628-2-phasta@kernel.org> References: <20260530143541.229628-2-phasta@kernel.org> Precedence: bulk X-Mailing-List: linux-kernel@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 Content-Type: text/plain; charset="utf-8" Content-Transfer-Encoding: quoted-printable 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 through 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 Tested-by: Daniel Almeida --- rust/bindings/bindings_helper.h | 1 + rust/helpers/dma_fence.c | 48 ++ rust/helpers/helpers.c | 1 + rust/kernel/dma_buf/dma_fence.rs | 821 +++++++++++++++++++++++++++++++ rust/kernel/dma_buf/mod.rs | 13 + rust/kernel/lib.rs | 1 + 6 files changed, 885 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_helpe= r.h index 2011645c7cfb..69daeb790f77 100644 --- a/rust/bindings/bindings_helper.h +++ b/rust/bindings/bindings_helper.h @@ -52,6 +52,7 @@ #include #include #include +#include #include #include #include diff --git a/rust/helpers/dma_fence.c b/rust/helpers/dma_fence.c new file mode 100644 index 000000000000..6244a5a61038 --- /dev/null +++ b/rust/helpers/dma_fence.c @@ -0,0 +1,48 @@ +// SPDX-License-Identifier: GPL-2.0 + +#include + +__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_is_signaled_locked(struct dma_fen= ce *f) +{ + return dma_fence_is_signaled_locked(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_fenc= e *f, unsigned long *flags) +{ + dma_fence_unlock_irqrestore(f, *flags); +} + +__rust_helper void rust_helper_dma_fence_set_error(struct dma_fence *f, in= t error) +{ + dma_fence_set_error(f, error); +} diff --git a/rust/helpers/helpers.c b/rust/helpers/helpers.c index 625921e27dfb..d9114d0b3c8f 100644 --- a/rust/helpers/helpers.c +++ b/rust/helpers/helpers.c @@ -57,6 +57,7 @@ #include "cred.c" #include "device.c" #include "dma.c" +#include "dma_fence.c" #include "dma-resv.c" #include "drm.c" #include "err.c" diff --git a/rust/kernel/dma_buf/dma_fence.rs b/rust/kernel/dma_buf/dma_fen= ce.rs new file mode 100644 index 000000000000..7dc1f5c16b02 --- /dev/null +++ b/rust/kernel/dma_buf/dma_fence.rs @@ -0,0 +1,821 @@ +// SPDX-License-Identifier: GPL-2.0 +// +// Copyright (C) 2025, 2026 Red Hat Inc.: +// - Philipp Stanner + +//! DriverFence support. +//! +//! Reference: +//! +//! C header: [`include/linux/dma-fence.h`](srctree/include/linux/dma-fenc= e.h) + +use crate::{ + alloc::AllocError, + bindings, + container_of, + error::to_result, + prelude::*, + sync::rcu::RcuBox, + types::ForeignOwnable, + types::Opaque, + warn_on, // +}; + +use pin_init::pin_init_from_closure; + +use core::{ + marker::PhantomData, // + ops::Deref, + ptr, + ptr::{ + drop_in_place, + NonNull, // + }, + sync::atomic::{ + AtomicU64, + Ordering, // + }, +}; + +use bindings::ECANCELED; + +use kernel::str::CString; +use kernel::sync::{ + aref::{ + ARef, + AlwaysRefCounted, // + }, + Arc, + ArcBorrow, // +}; + +/// VTable for dma_fence backend_ops callbacks. +// +// Mandatory dma_fence backend_ops are implemented implicitly through +// [`FenceCtx`]. Additional ones shall get implemented on this trait, whic= h then +// shall be demanded for the fence context data. +pub trait FenceCtxOps {} + +/// 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 { + /// The fence context number. + nr: u64, + /// The sequence number for the next fence created. + seqno: AtomicU64, + // The name parameters live in RcuBox because they can be accessed by = the + // dma_fence backend_ops. Those accesses are guarded by the rcu_read_l= ock(), + // so dropping them must be delayed by a grace period. + /// The name of the driver this FenceCtx's fences belong to. + driver_name: CString, + /// The name of the timeline this FenceCtx's fences belong to. + timeline_name: CString, + #[pin] + data: C, + fence_type: PhantomData, +} + +#[allow(unused_unsafe)] +impl FenceCtx { + // This can later be extended as a vtable in case other parties need s= upport + // for the more "exotic" callbacks. + const OPS: bindings::dma_fence_ops =3D bindings::dma_fence_ops { + get_driver_name: Some(Self::get_driver_name), + get_timeline_name: Some(Self::get_timeline_name), + enable_signaling: None, + signaled: None, + wait: None, + release: None, + set_deadline: None, + }; + + /// Create a new `FenceCtx`. + pub fn new( + driver_name: CString, + timeline_name: CString, + data: impl PinInit, + ) -> Result> { + let ctx =3D pin_init!(Self { + // SAFETY: `dma_fence_context_alloc()` merely works on a globa= l atomic. Parameter `1` + // is the number of contexts we want to allocate. + nr: unsafe { bindings::dma_fence_context_alloc(1) }, + seqno: AtomicU64::new(0), + driver_name, + timeline_name, + data <- data, + fence_type: PhantomData, + }); + + Arc::pin_init(ctx, GFP_KERNEL) + } + + fn get_next_fence_seqno(&self) -> u64 { + self.seqno.fetch_add(1, Ordering::Relaxed) + } + + /// Allocate the memory for a [`DriverFence`] and already store `data`= inside. + /// + /// This is needed because many times, creation of a [`DriverFence`] m= ust not + /// fail, and allocating might deadlock in some situations. + /// + /// The `data` you pass here must not perform any operations that are = illegal + /// in atomic context in its [`Drop`] implementation. + pub fn new_fence_allocation( + self: ArcBorrow<'_, Self>, + data: F, + ) -> Result> { + let fctx =3D Arc::::from(self); + + DriverFenceAllocation::new(fctx, data) + } + + /// Create a new fence, consuming `data`. + /// + /// The fence will increment the refcount of the fence context associa= ted with this + /// [`FenceCtx`]. + pub fn new_fence(&self, memory: DriverFenceAllocation) -> Driver= Fence { + let seqno: u64 =3D self.get_next_fence_seqno(); + + // We feed the C dma_fence backend a NULL for the spinlock so that= it + // uses per-fence locks automatically. + let null_ptr: *mut bindings::spinlock =3D ptr::null_mut(); + let fence_ptr =3D memory.as_raw(); + // SAFETY: `fence_ptr` has been created directly above. It will li= ve + // at least as long as `Self`. The same applies to `&Self::OPS`. + unsafe { bindings::dma_fence_init(fence_ptr, &Self::OPS, null_ptr,= self.nr, seqno) }; + + // A `DriverFenceAllocation`'s purpose is to carry allocated memor= y, 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 reach= es 0. + let ptr =3D KBox::into_raw(memory.data); + // SAFETY: `ptr` was just created validly directly above. + let ptr =3D unsafe { NonNull::new_unchecked(ptr) }; + + DriverFence { data: ptr } + } + + extern "C" fn get_driver_name(ptr: *mut bindings::dma_fence) -> *const= c_char { + // SAFETY: The C backend only invokes this callback with `ptr` poi= nting + // to a valid, unsignaled `bindings::dma_fence`. All fences created + // in this module always reside within `Fence` which always reside= s in + // a `DriverFenceData`, thus satisfying the function's safety requ= irements. + let fctx =3D unsafe { Self::from_raw_fence(ptr) }; + + fctx.driver_name.as_char_ptr() + } + + extern "C" fn get_timeline_name(ptr: *mut bindings::dma_fence) -> *con= st c_char { + // SAFETY: The C backend only invokes this callback with `ptr` poi= nting + // to a valid, unsignaled `bindings::dma_fence`. All fences created + // in this module always reside within `Fence` which always reside= s in + // a `DriverFenceData`, thus satisfying the function's safety requ= irements. + let fctx =3D unsafe { Self::from_raw_fence(ptr) }; + + fctx.timeline_name.as_char_ptr() + } + + /// Create a [`FenceCtx`] from an associated [`bindings::dma_fence`]. + /// + /// # Safety + /// + /// `ptr` must be a valid pointer to a dma_fence which resides within = a [`Fence`], + /// which in turn resides in a [`DriverFenceData`]. + unsafe fn from_raw_fence<'a>(ptr: *mut bindings::dma_fence) -> &'a Sel= f { + let opaque_fence =3D Opaque::cast_from(ptr); + + // SAFETY: Safe due to the function's overall safety requirements. + let fence_ptr =3D unsafe { container_of!(opaque_fence, Fence, inne= r) }; + + // DriverFenceData is repr(C) and a Fence is its first member. + let fence_data_ptr =3D fence_ptr as *mut DriverFenceData; + + // SAFETY: Safe because of the safety comment directly above. + let fence_data =3D unsafe { &*fence_data_ptr }; + + &fence_data.fctx + } +} + +// FenceCtx'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 PinnedDrop for FenceCtx { + fn drop(self: Pin<&mut Self>) { + // SAFETY: `rcu_barrier()` is always safe to be called. + unsafe { bindings::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 { + /// The fence was already signaled. The callback is returned so the ca= ller + /// can extract owned resources without losing them. + AlreadySignaled(T), + /// Some other error occurred during registration. + Other(Error), +} + +impl From> for Error { + fn from(err: CallbackError) -> Self { + match err { + CallbackError::AlreadySignaled(_) =3D> ENOENT, + CallbackError::Other(e) =3D> e, + } + } +} + +impl From for CallbackError { + fn from(e: AllocError) -> Self { + CallbackError::Other(Error::from(e)) + } +} + +/// Trait for callbacks that can be registered on fences. +/// +/// When the fence signals, the callback will be invoked. +/// +/// # Example +/// +/// ```rust +/// use kernel::dma_buf::FenceCb; +/// +/// struct MyCallback { +/// // Your callback state here +/// } +/// +/// impl FenceCb for MyCallback { +/// fn called(&mut self) { +/// pr_info!("Fence signaled!"); +/// // Handle fence completion +/// } +/// } +/// ``` +pub trait FenceCb: Send + 'static { + /// Called when the fence is signaled. + /// + /// This is called from the fence signaling path, which may be in inte= rrupt + /// 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 w= ork item) + /// within this callback that the associated [`FenceCbRegistration`] c= an now be + /// dropped. + fn called(&mut self); +} + +/// A callback registration on a fence. +/// +/// When this object is dropped, the callback is automatically removed if = it +/// hasn't been called yet. +/// +/// # Invariants +/// +/// If `callback` is `Some`, then `cb` is registered with the fence and the +/// callback hasn't been invoked yet. If `None`, the callback has been inv= oked +/// or the fence was already signaled when we tried to register. +#[pin_data(PinnedDrop)] +pub struct FenceCbRegistration { + #[pin] + cb: Opaque, + callback: T, + fence: ARef, +} + +impl FenceCbRegistration { + /// Register a callback on a fence. + /// + /// On success the callback is pinned in place and will fire when the = fence + /// signals. On `AlreadySignaled` the callback is returned to the call= er so + /// that owned resources can be reclaimed. + pub fn new<'a>(fence: &'a Fence, callback: T) -> impl PinInit> + 'a + where + T: 'a, + { + // Uses `pin_init_from_closure` instead of `try_pin_init!` so that= on + // `-ENOENT` (already signaled) the callback can be read back from= the + // partially-initialized slot and returned through the error. + // + // SAFETY: `pin_init_from_closure` requires: + // - On `Ok(())`: the slot is fully initialized and valid for `Dro= p`. + // - On `Err(_)`: the slot is clean, i.e.: no partially-initialize= d fields + // remain, and the slot can be deallocated without dropping. + // + // We uphold this as follows: + // - On success: all three fields are initialized. Ok(()) is retur= ned. + // - On ENOENT (already signaled): `callback` and `fence` are read= back + // from the slot via `ptr::read`, leaving the slot clean. `cb` w= as + // initialized by `dma_fence_add_callback` (it calls + // `INIT_LIST_HEAD(&cb->node)` even on error), but `cb` is + // `Opaque` which has no `Drop`, so not dropping i= t is + // fine. The callback is returned through `AlreadySignaled(T)`. + // - On other errors: same cleanup as ENOENT, error returned as + // `Other(e)`. + unsafe { + pin_init_from_closure(move |slot: *mut Self| { + let slot_callback =3D &raw mut (*slot).callback; + let slot_fence =3D &raw mut (*slot).fence; + let slot_cb =3D &raw mut (*slot).cb; + + // Write callback and fence first =E2=80=94 must be visibl= e before + // dma_fence_add_callback makes the registration live. + core::ptr::write(slot_callback, callback); + core::ptr::write(slot_fence, ARef::from(fence)); + + let ret =3D to_result(bindings::dma_fence_add_callback( + fence.inner.get(), + Opaque::cast_into(slot_cb), + Some(Self::dma_fence_callback), + )); + + match ret { + Ok(()) =3D> Ok(()), + Err(e) =3D> { + // Read back what we wrote to leave the slot clean. + let cb_back =3D core::ptr::read(slot_callback); + let _fence_back =3D core::ptr::read(slot_fence); + + if e.to_errno() =3D=3D ENOENT.to_errno() { + Err(CallbackError::AlreadySignaled(cb_back)) + } else { + Err(CallbackError::Other(e)) + } + } + } + }) + } + } + + /// Raw dma fence callback that is called by the C code. + /// + /// # Safety + /// + /// This is only called by the dma_fence subsystem with valid pointers. + unsafe extern "C" fn dma_fence_callback( + _fence: *mut bindings::dma_fence, + cb: *mut bindings::dma_fence_cb, + ) { + let ptr =3D Opaque::cast_from(cb).cast_mut(); + + // SAFETY: All `cb` we can receive here have been created in such = a way + // that they are embedded into a `FenceCbRegistration`. The backend + // ensures synchronisation so whoever holds the registration object + // cannot drop it while this code is running. See `FenceCbRegistra= tion::drop`. + unsafe { + let reg: *mut Self =3D container_of!(ptr, Self, cb); + + (*reg).callback.called(); + } + } + + /// Returns a reference to the fence this callback is registered on. + pub fn fence(self: Pin<&Self>) -> &Fence { + &self.get_ref().fence + } +} + +#[pinned_drop] +impl PinnedDrop for FenceCbRegistration { + fn drop(self: Pin<&mut Self>) { + // Always call dma_fence_remove_callback, even if `callback` has a= lready + // been taken by `dma_fence_callback`. This is necessary for + // synchronization: `dma_fence_remove_callback` acquires `fence->l= ock`, + // which ensures that any in-flight `dma_fence_signal` (which call= s our + // callback while holding the same lock) has completed before we f= ree + // the struct. + // + // Without this, Drop can race with a concurrent signal: + // CPU0 (signal, lock held): take() -> signaled(fence_ref) (in p= rogress) + // CPU1 (drop): sees is_some()=3D=3Dfalse -> skips lock -> frees= struct + // CPU0: accesses fence_ref -> use-after-free + // + // When the callback has already fired, the signal path detached t= he + // list node via INIT_LIST_HEAD, so dma_fence_remove_callback just= sees + // an empty node and returns false =E2=80=94 the lock acquisition = is the only + // thing that matters. + // + // SAFETY: The fence pointer is valid and the cb was initialized by + // dma_fence_add_callback during construction. + unsafe { + bindings::dma_fence_remove_callback(self.fence.as_raw(), self.= cb.get()); + } + } +} + +// SAFETY: FenceCbRegistration can be sent between threads +unsafe impl Send for FenceCbRegistration {} + +// SAFETY: &FenceCbRegistration can be shared between threads if &T can. +unsafe impl Sync for FenceCbRegistration where T: Sync {} + +/// The receiving counterpart of a [`DriverFence`], designed to register c= allbacks +/// on, check the signalled state etc. A [`Fence`] cannot be signalled. +/// A [`Fence`] is always refcounted. +pub struct Fence { + /// The actual dma_fence passed to C. + inner: Opaque, +} + +// SAFETY: Fences are literally designed to be shared between threads. +unsafe impl Send for Fence {} +// SAFETY: Fences are literally designed to be shared between threads. +unsafe impl Sync for Fence {} + +impl Fence { + /// Check whether the fence was signalled at the moment of the functio= n call. + pub fn is_signaled(&self) -> bool { + // SAFETY: self is by definition still valid. The backend ensures = proper + // locking. + unsafe { bindings::dma_fence_is_signaled(self.as_raw()) } + } + + 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 pr= oven to work correctly. +unsafe impl AlwaysRefCounted for Fence { + fn inc_ref(&self) { + // SAFETY: `self.as_raw()` is a pointer to a valid `struct dma_fen= ce`. + unsafe { bindings::dma_fence_get(self.as_raw()) } + } + + /// # Safety + /// + /// `ptr`must be a valid pointer to a [`DriverFence`]. + unsafe fn dec_ref(ptr: NonNull) { + // SAFETY: `ptr` is never a NULL pointer; and when `dec_ref()` is = called + // the fence is by definition still valid. + let fence =3D unsafe { (*ptr.as_ptr()).inner.get() }; + + // SAFETY: Valid because `fence` was created validly above. + unsafe { bindings::dma_fence_put(fence) } + } +} + +#[repr(C)] // Necessary to guarantee that `inner` always comes first so th= at we can cast. +#[pin_data] +struct DriverFenceData { + #[pin] + /// The inner fence. + inner: Fence, + /// Pointer to access the FenceCtx. Useful for obtaining name paramete= rs. + // The FenceCtx lives as long as at least all its fences, hence this i= s safe. + fctx: Arc>, + /// The API user's data. As required by [`DriverFenceAllowedData`], th= is either + /// does not need drop, or must live in a [`rcu::RcuBox`]. It is essen= tial + /// that the data only performs operations legal in atomic context in = its + /// [`Drop`] implementation. + #[pin] + data: F, +} + +/// A trait to enforce that all data in a [`DriverFence`] either does not = need +/// drop, or lives in a [`RcuBox`]. +pub trait DriverFenceAllowedData: private::Sealed {} + +mod private { + pub trait Sealed {} +} + +impl DriverFenceAllowedData for F {} +impl DriverFenceAllowedData for RcuBox {} + +impl private::Sealed for F {} +impl private::Sealed for RcuBox {} + +/// A synchronization primitive mainly for GPU drivers. +/// +/// Fences are always reference counted. The typical use case is that one = side registers +/// callbacks on the fence which will perform a certain action (such as qu= eueing work) once the +/// other side signals the fence. +/// +/// # Examples +/// +/// ``` +/// use kernel::dma_buf::{DriverFence, FenceCtx, FenceCb, FenceCbRegistrat= ion}; +/// use kernel::str::CString; +/// use kernel::sync::{ +/// aref::ARef, +/// rcu::RcuBox, // +/// }; +/// use core::ops::Deref; +/// use core::fmt::Display; +/// +/// struct CallbackData { } +/// +/// impl FenceCb for CallbackData { +/// fn called(&mut self) { +/// pr_info!("DmaFence callback executed.\n"); +/// } +/// } +/// +/// let driver_name =3D CString::try_from_fmt(fmt!("dummy_driver"))?; +/// let timeline_name =3D CString::try_from_fmt(fmt!("dummy_timeline"))?; +/// +/// let fctx =3D FenceCtx::new(driver_name, timeline_name, ())?; +/// +/// let fence_data =3D CString::try_from_fmt(fmt!("dummy_data"))?; +/// // DriverFence::data must either not need drop, or live in an RcuBox. +/// let fence_data =3D RcuBox::new(fence_data, GFP_KERNEL)?; +/// +/// let fence_alloc =3D fctx.as_arc_borrow().new_fence_allocation(fence_da= ta)?; +/// let mut fence =3D fctx.new_fence(fence_alloc); +/// +/// let cb_data =3D CallbackData { }; +/// let waiting_fence =3D ARef::from(fence.as_fence()); +/// let cb_reg =3D FenceCbRegistration::new(&waiting_fence, cb_data); +/// let cb_reg =3D KBox::pin_init(cb_reg, GFP_KERNEL)?; +/// +/// // DriverFence implements Deref. +/// // FIXME: unit test claims that CString does not implement Display. Wh= y? +/// // pr_info!("Fence's inner data is: {}", fence.deref().deref()); +/// +/// // TODO begin_signalling +/// fence.signal(Ok(())); +/// assert_eq!(waiting_fence.is_signaled(), true); +/// +/// Ok::<(), Error>(()) +/// ``` +pub struct DriverFence { + /// The actual content of the fence. Lives in a raw pointer so that its + /// memory can be managed independently. Valid until both the [`Driver= Fence`] + /// and all associated [`Fence`]s have disappeared. + data: NonNull>, +} + +/// A pre-prepared DMA fence, carrying the user's data and the memory it a= nd 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 { + /// The memory for the actual content of the fence. + /// Handed over to a [`DriverFence`], or deallocated once the + /// [`DriverFenceAllocation`] drops. + data: KBox>, +} + +impl DriverFenceA= llocation { + /// Create a new allocation slot that can later be used to create a fu= lly + /// initialized [`DriverFence`] without the need to allocate. + pub fn new(fctx: Arc>, data: F) -> Result { + let fence_data =3D DriverFenceData { + // `inner` remains uninitialized until a [`DriverFence`] takes= over. + inner: Fence { + inner: Opaque::uninit(), + }, + fctx, + data, + }; + + // In order to support the C dma_fence callbacks, it is necessary = for + // a `Fence` and a `DriverFence` to live in the same allocation, + // because the C backend passes a dma_fence, from which the driver= most + // likely wants to be able to access its `data` in `DriverFence`. + // + // Hence, we need the manage the memory manually. It will be freed= by the + // C backend automatically once the refcount within `Fence` drops = to 0. + let data =3D KBox::new(fence_data, GFP_KERNEL | __GFP_ZERO)?; + + Ok(Self { data }) + } + + fn as_raw(&self) -> *mut bindings::dma_fence { + self.data.inner.inner.get() + } +} + +impl DriverFence { + fn as_raw(&self) -> *mut bindings::dma_fence { + // SAFETY: Valid because `self` is valid. + let fence_data =3D unsafe { &mut *self.data.as_ptr() }; + + fence_data.inner.inner.get() + } + + /// Create a [`DriverFence`] from a raw pointer to a [`bindings::dma_f= ence`]. + /// + /// # Safety + /// + /// `ptr` must be a valid pointer to a `dma_fence` that was obtained t= hrough + /// a [`DriverFence`] with matching generic data for both fence and as= sociated + /// [`FenceCtx`]. + unsafe fn from_raw(ptr: *mut bindings::dma_fence) -> Self { + let opaque_fence =3D Opaque::cast_from(ptr); + + // SAFETY: Safe due to the function's overall safety requirements. + let fence_ptr =3D unsafe { container_of!(opaque_fence, Fence, inne= r) }; + + // DriverFenceData is repr(C) and a Fence is its first member. + let fence_data_ptr =3D fence_ptr as *mut DriverFenceData; + + // SAFETY: `fence_data_ptr` was created validly above. + let data =3D unsafe { NonNull::new_unchecked(fence_data_ptr) }; + + Self { data } + } + + /// Return the underlying [`Fence`]. + pub fn as_fence(&self) -> &Fence { + // SAFETY: `self` is by definition still valid, and it cannot drop= until + // this new reference is gone. + unsafe { Fence::from_raw(self.as_raw()) } + } + + /// Signal the fence. This will invoke all registered callbacks. + pub fn signal(self, res: Result) { + let fence =3D self.as_raw(); + let mut fence_flags: usize =3D 0; + let flag_ptr =3D &raw mut fence_flags; + + // 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 the `DriverFence` has been= dropped. + unsafe { + bindings::dma_fence_lock_irqsave(fence, flag_ptr); + if !bindings::dma_fence_is_signaled_locked(fence) { + if let Err(err) =3D res { + bindings::dma_fence_set_error(fence, err.to_errno()); + } + bindings::dma_fence_signal_locked(fence); + } + bindings::dma_fence_unlock_irqrestore(fence, flag_ptr); + } + } +} + +// SAFETY: Fences are literally designed to be shared between threads. +unsafe impl Send for DriverFence {} + +impl Deref for DriverFence { + type Target =3D F; + + fn deref(&self) -> &Self::Target { + // SAFETY: Thanks to refcounting, `data` is always valid as long a= s `self` is. + let data =3D unsafe { &*self.data.as_ptr() }; + + &data.data + } +} + +/// A borrowed [`DriverFence`]. All you can do with it is access your user= data +/// and obtain a [`Fence`]. +pub struct DriverFenceBorrow { + /// The actual content of the fence. Lives in a raw pointer so that its + /// memory can be managed independently. Valid until both the [`Driver= Fence`] + /// and all associated [`Fence`]s have disappeared. + data: NonNull>, +} + +impl Deref for DriverFenceBorrow { + type Target =3D F; + + fn deref(&self) -> &Self::Target { + // SAFETY: Thanks to refcounting, `data` is always valid as long a= s `self` is. + let data =3D unsafe { &*self.data.as_ptr() }; + + &data.data + } +} + +impl DriverFenceBorrow { + fn as_raw(&self) -> *mut bindings::dma_fence { + // SAFETY: Valid because `self` is valid. + let fence_data =3D unsafe { &mut *self.data.as_ptr() }; + + fence_data.inner.inner.get() + } + + /// Return the underlying [`Fence`]. + pub fn as_fence(&self) -> &Fence { + // SAFETY: `self` is by definition still valid, and it cannot drop= until + // this new reference is gone. + unsafe { Fence::from_raw(self.as_raw()) } + } + + /// Get a [`DriverFenceBorrow`] from a raw pointer. + /// + /// # Safety + /// + /// `ptr` must point to a raw dma_fence within a [`Fence`] within a [`= DriverFenceData`]. + unsafe fn from_raw(ptr: *mut bindings::dma_fence) -> Self { + let opaque_fence =3D Opaque::cast_from(ptr); + + // SAFETY: Safe due to the function's overall safety requirements. + let fence_ptr =3D unsafe { container_of!(opaque_fence, Fence, inne= r) }; + + // DriverFenceData is repr(C) and a Fence is its first member. + let fence_data_ptr =3D fence_ptr as *mut DriverFenceData; + + // SAFETY: `fence_data_ptr` was created validly above. + let data =3D unsafe { NonNull::new_unchecked(fence_data_ptr) }; + + Self { data } + } +} + +// SAFETY: The Rust dma_fence abstractions are already designed around the= inner +// C `dma_fence`, which can serve safely as the identification point when = being +// owned by C. Moreover, safety is ensured by not dropping `DriverFence` a= nd by +// only allowing operations without side effects on the Borrowed type. +unsafe impl ForeignOwn= able + for DriverFence +{ + // `DriverFence` is merely a wrapper around a raw pointer. Thus, we ca= n just + // use it directly. + type Borrowed<'a> =3D DriverFenceBorrow; + type BorrowedMut<'a> =3D DriverFenceBorrow; + + const FOREIGN_ALIGN: usize =3D core::mem::align_of::(); + + fn into_foreign(self) -> *mut c_void { + let fence =3D self; + + let ptr =3D fence.as_raw(); + + // DriverFence must not drop. + core::mem::forget(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 gene= ric data. + unsafe { Self::from_raw(ptr.cast()) } + } + + unsafe fn borrow<'a>(ptr: *mut c_void) -> Self::Borrowed<'a> { + // SAFETY: The trait implementation ensures that `ptr` always resi= des + // within a [`Fence`] within a [`DriverFenceData`]. + unsafe { DriverFenceBorrow::from_raw(ptr.cast()) } + } + + unsafe fn borrow_mut<'a>(ptr: *mut c_void) -> Self::BorrowedMut<'a> { + // SAFETY: The trait implementation ensures that `ptr` always resi= des + // within a [`Fence`] within a [`DriverFenceData`]. + unsafe { DriverFenceBorrow::from_raw(ptr.cast()) } + } +} + +impl Drop for DriverFence { + fn drop(&mut self) { + let fence =3D self.as_raw(); + let mut fence_flags: usize =3D 0; + let flag_ptr =3D &raw mut fence_flags; + + // 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 the `DriverFence` has been= dropped. + unsafe { + bindings::dma_fence_lock_irqsave(fence, flag_ptr); + #[allow(unused_unsafe)] + if warn_on!(!bindings::dma_fence_is_signaled_locked(fence)) { + bindings::dma_fence_set_error(fence, ECANCELED as i32); + bindings::dma_fence_signal_locked(fence); + } + bindings::dma_fence_unlock_irqrestore(fence, flag_ptr); + } + + // SAFETY: `self.data` is owned by the DriverFence, but could be a= ccessed + // through some dma_fence callbacks right now. Access is being rev= oked + // above by signalling the fence. The DriverFenceAllowedData trait + // ensures that the data either does not need drop, or if it does = it + // lives in a RcuBox which will delay dropping by one grace period= , hence + // ensuring that all readers have disappeared. + unsafe { drop_in_place(self.data.as_ptr()) }; + + // 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 the `DriverFence` has been= dropped. + unsafe { + bindings::dma_fence_put(fence); + } + + // The actual memory the data associated with a `DriverFence` live= s 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..d9da3dc57fce --- /dev/null +++ b/rust/kernel/dma_buf/mod.rs @@ -0,0 +1,13 @@ +// 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, // +}; diff --git a/rust/kernel/lib.rs b/rust/kernel/lib.rs index b72b2fbe046d..a05ccaa7598c 100644 --- a/rust/kernel/lib.rs +++ b/rust/kernel/lib.rs @@ -63,6 +63,7 @@ pub mod device_id; pub mod devres; pub mod dma; +pub mod dma_buf; pub mod driver; #[cfg(CONFIG_DRM =3D "y")] pub mod drm; --=20 2.54.0 From nobody Mon Jun 8 09:48:00 2026 Received: from smtp.kernel.org (aws-us-west-2-korg-mail-alma10-1.taild15c8.ts.net [100.103.45.18]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by smtp.subspace.kernel.org (Postfix) with ESMTPS id E68682BE033; Sat, 30 May 2026 14:37:38 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=100.103.45.18 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1780151859; cv=none; b=QwP8Nh6DwHwcEI0P0Ymwo1gBvKMfI14GYEF7sa4sLJ3JkMYqX6XvjGnPm02SIOCi3WoO237zVLsl/MsmswsQZeOjJMLFEvcXMXKPYJsZdg8VjwMCwkTUdofLKPYlETipcRmfZ6MIW8urxxU6sxCzjqQfzpFkYS1A0VRnH1UQSfo= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1780151859; c=relaxed/simple; bh=4euHBbsz9eB8Goft/Un98U/vCSep1wxIpkjJFv0sH48=; h=From:To:Cc:Subject:Date:Message-ID:In-Reply-To:References: MIME-Version:Content-Type; b=Lhv9dnokl0bp6GbmFpvaUI2b68U4rv9+4uXLxP/vUHHb9jFdnLZ6TsghcBJRjWunYB8oAUuwrwC3kSET2wYTiO152ReZOWLBLa4jSC4L7eGMynYMa0ZhilBvGatWzTnUMPvsI7tfVxNhjCJo9ornJajruVh/qK/UX54S/EUSXfk= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=kernel.org header.i=@kernel.org header.b=P695W+rQ; arc=none smtp.client-ip=100.103.45.18 Authentication-Results: smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=kernel.org header.i=@kernel.org header.b="P695W+rQ" Received: by smtp.kernel.org (Postfix) with ESMTPSA id 850C31F00893; Sat, 30 May 2026 14:37:30 +0000 (UTC) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=kernel.org; s=k20260515; t=1780151858; bh=Oh702E20bwx/p0BX78CbvBoV0ZkW0mSKteIxLUaDpYc=; h=From:To:Cc:Subject:Date:In-Reply-To:References; b=P695W+rQUAycRVtKTMK8ZUb4SRqjKHX3MUQPaWLoXKoi1Fh4ArSVZjLAgERV3InZ6 azFgBj9tuRdq+8LLH7SCkgIYl6jT59xVULdW8b5zOFVNr1ql+RMvRTRCm50Z6KChcN LpL/sHozZlMg1O7Ds+qQ+9Io9DBC+ahhBanec3c+RZgUauwgBNQFkO3fo4Cagx41Rm Uk8vp2pTDBqdgelT1YnZDLBWC7xVa99KDyu8Uj82J234Gn0mFp/R7hQynU1DK3bPlX PXfqWdFRo6ef4p0Qv3tXENq/y6iZrogTLXOBuAgOW2py0oh9wchFqhFHCMsEErcqhl nuAKWfPFkUkkw== From: Philipp Stanner To: Miguel Ojeda , Boqun Feng , Gary Guo , =?UTF-8?q?Bj=C3=B6rn=20Roy=20Baron?= , Benno Lossin , Andreas Hindborg , Alice Ryhl , Trevor Gross , Danilo Krummrich , Sumit Semwal , =?UTF-8?q?Christian=20K=C3=B6nig?= , Philipp Stanner , "Paul E. McKenney" , Frederic Weisbecker , Neeraj Upadhyay , Joel Fernandes , Josh Triplett , Uladzislau Rezki , Steven Rostedt , Mathieu Desnoyers , Lai Jiangshan , Zqiang , Daniel Almeida , Greg Kroah-Hartman , Igor Korotin , Lorenzo Stoakes , Alexandre Courbot , FUJITA Tomonori , Krishna Ketan Rai , Shankari Anand , manos@pitsidianak.is, Boris Brezillon Cc: linux-kernel@vger.kernel.org, rust-for-linux@vger.kernel.org, linux-media@vger.kernel.org, dri-devel@lists.freedesktop.org, linaro-mm-sig@lists.linaro.org, rcu@vger.kernel.org Subject: [PATCH 4/4] MAINTAINERS: Add entry for Rust dma-buf Date: Sat, 30 May 2026 16:35:13 +0200 Message-ID: <20260530143541.229628-7-phasta@kernel.org> X-Mailer: git-send-email 2.54.0 In-Reply-To: <20260530143541.229628-2-phasta@kernel.org> References: <20260530143541.229628-2-phasta@kernel.org> Precedence: bulk X-Mailing-List: linux-kernel@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 Content-Type: text/plain; charset="utf-8" Content-Transfer-Encoding: quoted-printable 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 Tested-by: Daniel Almeida --- Just as a suggestion, I don't want to force myself in here. Would also be perfectly happy with other approaches; there are certainly a few people who could maintain or co-maintain it. --- MAINTAINERS | 2 ++ 1 file changed, 2 insertions(+) diff --git a/MAINTAINERS b/MAINTAINERS index 2e8d160babc2..31fc595d5c6b 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -7521,6 +7521,7 @@ F: fs/dlm/ DMA BUFFER SHARING FRAMEWORK M: Sumit Semwal M: Christian K=C3=B6nig +M: Philipp Stanner L: linux-media@vger.kernel.org L: dri-devel@lists.freedesktop.org L: linaro-mm-sig@lists.linaro.org (moderated for non-subscribers) @@ -7529,6 +7530,7 @@ T: git https://gitlab.freedesktop.org/drm/misc/kernel= .git F: Documentation/driver-api/dma-buf.rst F: Documentation/userspace-api/dma-buf-alloc-exchange.rst F: drivers/dma-buf/ +F: rust/kernel/dma_buf/ F: include/linux/*fence.h F: include/linux/dma-buf.h F: include/linux/dma-buf/ --=20 2.54.0