From nobody Thu Apr 9 10:42:49 2026 Received: from smtp.kernel.org (aws-us-west-2-korg-mail-1.web.codeaurora.org [10.30.226.201]) (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 EEBE138F255; Mon, 9 Mar 2026 16:35:18 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=10.30.226.201 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1773074119; cv=none; b=iXrekU/BkYo76paR/xntra21/TjNsHoOa4eWjS1vtlzAt8mkSVaGWrZ3WgChNqXBQ1j6h2eNtk4MVb4J2VAub5JQb0MCbkNrTF52M95O3b1eI7b+UfmRknwU+2UPLo6SrSZDSqdlBsS2S3AotShEMTa9EhdJzN/Esci/MZwic9s= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1773074119; c=relaxed/simple; bh=gvPoHfp4kILjun0x+PiFZTGPIwNfY5nP6rl8dO323WQ=; h=From:Date:Subject:MIME-Version:Content-Type:Message-Id:References: In-Reply-To:To:Cc; b=B0r2KSo/8H2bUWzwX2CJQTvUclYFRCR7MetNW0HwqguMy1WtL6mjNNYmXprUTK9/HhVjBpGKmZ8IHDiLPNfxZ8Knanu+2vqGT1aeGatVMTiaQ1WIRZl4wmCTmaTaxk0ZIwKYAySPjJ7KvQIKiJqnjPXIcmRdSayPK1o4F94oPMI= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=kernel.org header.i=@kernel.org header.b=UyLYSwNI; arc=none smtp.client-ip=10.30.226.201 Authentication-Results: smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=kernel.org header.i=@kernel.org header.b="UyLYSwNI" Received: by smtp.kernel.org (Postfix) with ESMTPS id 9D450C2BCAF; Mon, 9 Mar 2026 16:35:18 +0000 (UTC) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=kernel.org; s=k20201202; t=1773074118; bh=gvPoHfp4kILjun0x+PiFZTGPIwNfY5nP6rl8dO323WQ=; h=From:Date:Subject:References:In-Reply-To:To:Cc:Reply-To:From; b=UyLYSwNI19v9fIU/UbWQcc5LMvSlHh2m1+TmiGF9bUNaACjOvtlccovTSWh9MZjOu FRjSnf/V5MzFcFzJlOXhxossZXZYcvi2ULR2U0DcU7BvOSd5AQpdYQPftTVApp3LeC DdaoRbnIqtjX4kzODRi7OISkxtYKvCjqe9aamimi8IykwcmXBq2K5mA2e11fF6XfOn EEeJuwI3Ay0TEzU5vsUR9rWGgBJg7hT6fDYWpRsSC+GxxkGgxEvGNnAp+yZhvtLemF NYfDCOQgK3qCxuv5q0owT3xbZkd9kfsFebgC6HOpEZN3r68PQgLWV5vsPmBJr3pMkD rNvIeysjpvkKw== Received: from aws-us-west-2-korg-lkml-1.web.codeaurora.org (localhost.localdomain [127.0.0.1]) by smtp.lore.kernel.org (Postfix) with ESMTP id 7F83BF41810; Mon, 9 Mar 2026 16:35:18 +0000 (UTC) From: Tim Kovalenko via B4 Relay Date: Mon, 09 Mar 2026 12:34:18 -0400 Subject: [PATCH v4 1/4] rust: ptr: add `KnownSize` trait to support DST size info extraction 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 Message-Id: <20260309-drm-rust-next-v4-1-4ef485b19a4c@proton.me> References: <20260309-drm-rust-next-v4-0-4ef485b19a4c@proton.me> In-Reply-To: <20260309-drm-rust-next-v4-0-4ef485b19a4c@proton.me> To: Alexandre Courbot , Danilo Krummrich , Alice Ryhl , David Airlie , Simona Vetter , Miguel Ojeda , Gary Guo , =?utf-8?q?Bj=C3=B6rn_Roy_Baron?= , Benno Lossin , Andreas Hindborg , Trevor Gross , Boqun Feng , Nathan Chancellor , Nicolas Schier , Abdiel Janulgue , Daniel Almeida , Robin Murphy , Boqun Feng Cc: nouveau@lists.freedesktop.org, dri-devel@lists.freedesktop.org, linux-kernel@vger.kernel.org, rust-for-linux@vger.kernel.org, linux-kbuild@vger.kernel.org, driver-core@lists.linux.dev X-Mailer: b4 0.14.2 X-Developer-Signature: v=1; a=ed25519-sha256; t=1773074117; l=2158; i=tim.kovalenko@proton.me; s=20260212; h=from:subject:message-id; bh=FheLpAl87yI4UaHfqa3k20aJCUg9ixwJ8DWS/cDgRg4=; b=45N81LFvbnZxj7EBp15VjpGQhHombjwrqodP4FxEVsCd0JDjLKei5FWNCjy3jFam1BlfUrrI7 tx8XLpQNe94AjeTcEcFGAGgbnHF9RBdYUogAKI4RUMXskNkIfccBCVr X-Developer-Key: i=tim.kovalenko@proton.me; a=ed25519; pk=/+OiulEpgeZifgP4mDE4e5YlV6nMeY+frze/lY/xiHI= X-Endpoint-Received: by B4 Relay for tim.kovalenko@proton.me/20260212 with auth_id=635 X-Original-From: Tim Kovalenko Reply-To: tim.kovalenko@proton.me From: Gary Guo Add a `KnownSize` trait which is used obtain a size from a raw pointer's metadata. This makes it possible to obtain size information on a raw slice pointer. This is similar to Rust `core::mem::size_of_val_raw` which is not yet stable. Signed-off-by: Gary Guo Reviewed-by: Benno Lossin Acked-by: Miguel Ojeda --- rust/kernel/lib.rs | 1 + rust/kernel/ptr.rs | 27 ++++++++++++++++++++++++++- 2 files changed, 27 insertions(+), 1 deletion(-) diff --git a/rust/kernel/lib.rs b/rust/kernel/lib.rs index 3da92f18f4eed16335c3c251e6bff68dcf7e781e..510cc7fe496113f85c34f420b1c= 4be95596297ad 100644 --- a/rust/kernel/lib.rs +++ b/rust/kernel/lib.rs @@ -20,6 +20,7 @@ #![feature(generic_nonzero)] #![feature(inline_const)] #![feature(pointer_is_aligned)] +#![feature(slice_ptr_len)] // // Stable since Rust 1.80.0. #![feature(slice_flatten)] diff --git a/rust/kernel/ptr.rs b/rust/kernel/ptr.rs index 5b6a382637fef11e9c0ae9122906422a970d7fcd..cf980a103acf19ee3bd17bb1dfd= bcadfe30467ae 100644 --- a/rust/kernel/ptr.rs +++ b/rust/kernel/ptr.rs @@ -2,7 +2,10 @@ =20 //! Types and functions to work with pointers and addresses. =20 -use core::mem::align_of; +use core::mem::{ + align_of, + size_of, // +}; use core::num::NonZero; =20 /// Type representing an alignment, which is always a power of two. @@ -225,3 +228,25 @@ fn align_up(self, alignment: Alignment) -> Option { } =20 impl_alignable_uint!(u8, u16, u32, u64, usize); + +/// Trait to represent compile-time known size information. +/// +/// This is a generalization of what [`size_of`] which works for dynamical= ly sized types. +pub trait KnownSize { + /// Get the size of an object of this type in bytes, with the metadata= of the given pointer. + fn size(p: *const Self) -> usize; +} + +impl KnownSize for T { + #[inline(always)] + fn size(_: *const Self) -> usize { + size_of::() + } +} + +impl KnownSize for [T] { + #[inline(always)] + fn size(p: *const Self) -> usize { + p.len() * size_of::() + } +} --=20 2.53.0 From nobody Thu Apr 9 10:42:49 2026 Received: from smtp.kernel.org (aws-us-west-2-korg-mail-1.web.codeaurora.org [10.30.226.201]) (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 F3B473A7F4E; Mon, 9 Mar 2026 16:35:18 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=10.30.226.201 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1773074119; cv=none; b=U68F0AYo9uUuL9rW6YUsCz7rrzJbFB4eAog9SgBdjpJyhTCw4GrXpRrh5H8o/GtlKzYSO/AFQ4rfqTA6KlsRbGSi1Z2FHqyiJ6NbJxYmQ9YFO6hqsDEk7sbQEWpqLNK/Zel98yg97nJIjxhto/2T1Gpf2EZYUULC7PgRRB8ofo0= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1773074119; c=relaxed/simple; bh=M2wvf00S2/NepjcwOTg0nKGdQToaxfiaijo9qaj8rYw=; h=From:Date:Subject:MIME-Version:Content-Type:Message-Id:References: In-Reply-To:To:Cc; b=FzEsL3V3r3CqASfk+MlBWDKhkCzcRuiMY7S9rsBqzYD/TTGsB57TA5Of5ghSfuoX5LjPrukjNr7168YsJVbqSBhXJIsoYzfW5HBSG9Q7j1imrMf8Cs+tFDm8vxxo9wDTDfRAvHKSv09EVzlHSBaBLtHhSyCZp0A6qSvOoR5S5Kg= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=kernel.org header.i=@kernel.org header.b=mDwXvrAR; arc=none smtp.client-ip=10.30.226.201 Authentication-Results: smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=kernel.org header.i=@kernel.org header.b="mDwXvrAR" Received: by smtp.kernel.org (Postfix) with ESMTPS id A38D9C2BCB2; Mon, 9 Mar 2026 16:35:18 +0000 (UTC) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=kernel.org; s=k20201202; t=1773074118; bh=M2wvf00S2/NepjcwOTg0nKGdQToaxfiaijo9qaj8rYw=; h=From:Date:Subject:References:In-Reply-To:To:Cc:Reply-To:From; b=mDwXvrARmG0Rt9IVtcr+6GfxEHOP+AG2Dcvw1DlyWu+9Nf90YTVknJtFd7YJVY4gQ HM7NyomUO5SdjBno//mRbEXLqXwgUYB5I3SIHW3CQp0kcXYG828Qwm34fkZnUvkzfL 4XjS9j0Jc36Ykuo5j3X7wE9nTQMkifqQmXGdLuczsgaM1ga/ghfY4C2XYGrLJ+ESs3 M1KVNKsgUZEcTcWkTQkvjHEEwgluUhL9lpvEEXE3x+2OytCLGXXU+1enYfguwS2R2j QJebrYRANlMVxHb+ZAwhspAtlJT3PR/s4ix8To4fQIW5dN5EK2Ull8vnugvwnneKg2 3/qaXz2Q/tqMA== Received: from aws-us-west-2-korg-lkml-1.web.codeaurora.org (localhost.localdomain [127.0.0.1]) by smtp.lore.kernel.org (Postfix) with ESMTP id 8FFE7F4180E; Mon, 9 Mar 2026 16:35:18 +0000 (UTC) From: Tim Kovalenko via B4 Relay Date: Mon, 09 Mar 2026 12:34:19 -0400 Subject: [PATCH v4 2/4] rust: ptr: add projection infrastructure 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 Message-Id: <20260309-drm-rust-next-v4-2-4ef485b19a4c@proton.me> References: <20260309-drm-rust-next-v4-0-4ef485b19a4c@proton.me> In-Reply-To: <20260309-drm-rust-next-v4-0-4ef485b19a4c@proton.me> To: Alexandre Courbot , Danilo Krummrich , Alice Ryhl , David Airlie , Simona Vetter , Miguel Ojeda , Gary Guo , =?utf-8?q?Bj=C3=B6rn_Roy_Baron?= , Benno Lossin , Andreas Hindborg , Trevor Gross , Boqun Feng , Nathan Chancellor , Nicolas Schier , Abdiel Janulgue , Daniel Almeida , Robin Murphy , Boqun Feng Cc: nouveau@lists.freedesktop.org, dri-devel@lists.freedesktop.org, linux-kernel@vger.kernel.org, rust-for-linux@vger.kernel.org, linux-kbuild@vger.kernel.org, driver-core@lists.linux.dev X-Mailer: b4 0.14.2 X-Developer-Signature: v=1; a=ed25519-sha256; t=1773074117; l=15953; i=tim.kovalenko@proton.me; s=20260212; h=from:subject:message-id; bh=miLP0yl3FjGCpj96QFx68fa6u7hqlgNIww43hMgjNq8=; b=eA0m7RkR/mSGXK0WMRmnkmdjAaQ+cSBVISnJrKWvXCW5yra/kqFDo2rbTPpWQrbv/rw/l7HK0 3DN0iiv4XALCtKc8uHMdcxNRIIWlTKx758rQtfIlPT7rczFVgSv3NWM X-Developer-Key: i=tim.kovalenko@proton.me; a=ed25519; pk=/+OiulEpgeZifgP4mDE4e5YlV6nMeY+frze/lY/xiHI= X-Endpoint-Received: by B4 Relay for tim.kovalenko@proton.me/20260212 with auth_id=635 X-Original-From: Tim Kovalenko Reply-To: tim.kovalenko@proton.me From: Gary Guo Add a generic infrastructure for performing field and index projections on raw pointers. This will form the basis of performing I/O projections. Pointers manipulations are intentionally using the safe wrapping variants instead of the unsafe variants, as the latter requires pointers to be inside an allocation which is not necessarily true for I/O pointers. This projection macro protects against rogue `Deref` implementation, which can causes the projected pointer to be outside the bounds of starting pointer. This is extremely unlikely and Rust has a lint to catch this, but is unsoundness regardless. The protection works by inducing type inference ambiguity when `Deref` is implemented. This projection macro also stops projecting into unaligned fields (i.e. fields of `#[repr(packed)]` structs), as misaligned pointers require special handling. This is implemented by attempting to create reference to projected field inside a `if false` block. Despite being unreachable, Rust still checks that they're not unaligned fields. The projection macro supports both fallible and infallible index projections. These are described in detail inside the documentation. Signed-off-by: Gary Guo Reviewed-by: Benno Lossin Acked-by: Miguel Ojeda --- rust/kernel/lib.rs | 3 + rust/kernel/ptr.rs | 3 + rust/kernel/ptr/projection.rs | 294 ++++++++++++++++++++++++++++++++++++++= ++++ scripts/Makefile.build | 4 +- 4 files changed, 303 insertions(+), 1 deletion(-) diff --git a/rust/kernel/lib.rs b/rust/kernel/lib.rs index 510cc7fe496113f85c34f420b1c4be95596297ad..d93292d47420f1f298a452ade5f= eefedce5ade86 100644 --- a/rust/kernel/lib.rs +++ b/rust/kernel/lib.rs @@ -38,6 +38,9 @@ #![feature(const_ptr_write)] #![feature(const_refs_to_cell)] // +// Stable since Rust 1.84.0. +#![feature(strict_provenance)] +// // Expected to become stable. #![feature(arbitrary_self_types)] // diff --git a/rust/kernel/ptr.rs b/rust/kernel/ptr.rs index cf980a103acf19ee3bd17bb1dfdbcadfe30467ae..930f523a4659b2cd5974e681ebf= 4c15c892a5e5a 100644 --- a/rust/kernel/ptr.rs +++ b/rust/kernel/ptr.rs @@ -2,6 +2,9 @@ =20 //! Types and functions to work with pointers and addresses. =20 +pub mod projection; +pub use crate::project_pointer as project; + use core::mem::{ align_of, size_of, // diff --git a/rust/kernel/ptr/projection.rs b/rust/kernel/ptr/projection.rs new file mode 100644 index 0000000000000000000000000000000000000000..7686d8502095108b90e863e90cb= c3a626c65031e --- /dev/null +++ b/rust/kernel/ptr/projection.rs @@ -0,0 +1,294 @@ +// SPDX-License-Identifier: GPL-2.0 + +//! Infrastructure for handling projections. + +use core::{ + mem::MaybeUninit, + ops::Deref, // +}; + +use crate::prelude::*; + +/// Error raised when a projection is attempted on array or slices out of = bounds. +pub struct OutOfBound; + +impl From for Error { + #[inline(always)] + fn from(_: OutOfBound) -> Self { + ERANGE + } +} + +/// A helper trait to perform index projection. +/// +/// This is similar to `core::slice::SliceIndex`, but operate on raw point= ers safely and fallibly. +/// +/// # Safety +/// +/// The implementation of `index` and `get` (if `Some` is returned) must e= nsure that, if provided +/// input pointer `slice` and returned pointer `output`, then: +/// - `output` has the same provenance as `slice`; +/// - `output.byte_offset_from(slice)` is between 0 to +/// `KnownSize::size(slice) - KnownSize::size(output)`. +/// +/// This means that if the input pointer is valid, then pointer returned b= y `get` or `index` is +/// also valid. +#[diagnostic::on_unimplemented(message =3D "`{Self}` cannot be used to ind= ex `{T}`")] +#[doc(hidden)] +pub unsafe trait ProjectIndex: Sized { + type Output: ?Sized; + + /// Returns an index-projected pointer, if in bounds. + fn get(self, slice: *mut T) -> Option<*mut Self::Output>; + + /// Returns an index-projected pointer; fail the build if it cannot be= proved to be in bounds. + #[inline(always)] + fn index(self, slice: *mut T) -> *mut Self::Output { + Self::get(self, slice).unwrap_or_else(|| build_error!()) + } +} + +// Forward array impl to slice impl. +// SAFETY: safety requirement guaranteed by the forwarded impl. +unsafe impl ProjectIndex<[T; N]> for I +where + I: ProjectIndex<[T]>, +{ + type Output =3D >::Output; + + #[inline(always)] + fn get(self, slice: *mut [T; N]) -> Option<*mut Self::Output> { + >::get(self, slice) + } + + #[inline(always)] + fn index(self, slice: *mut [T; N]) -> *mut Self::Output { + >::index(self, slice) + } +} + +// SAFETY: `get` returned pointers has same provenance as `slice` and the = offset is checked to not +// exceed the required bound. +unsafe impl ProjectIndex<[T]> for usize { + type Output =3D T; + + #[inline(always)] + fn get(self, slice: *mut [T]) -> Option<*mut T> { + if self >=3D slice.len() { + None + } else { + Some(slice.cast::().wrapping_add(self)) + } + } +} + +// SAFETY: `get` returned pointers has same provenance as `slice` and the = offset is checked to not +// exceed the required bound. +unsafe impl ProjectIndex<[T]> for core::ops::Range { + type Output =3D [T]; + + #[inline(always)] + fn get(self, slice: *mut [T]) -> Option<*mut [T]> { + let new_len =3D self.end.checked_sub(self.start)?; + if self.end > slice.len() { + return None; + } + Some(core::ptr::slice_from_raw_parts_mut( + slice.cast::().wrapping_add(self.start), + new_len, + )) + } +} + +// SAFETY: safety requirement guaranteed by the forwarded impl. +unsafe impl ProjectIndex<[T]> for core::ops::RangeTo { + type Output =3D [T]; + + #[inline(always)] + fn get(self, slice: *mut [T]) -> Option<*mut [T]> { + (0..self.end).get(slice) + } +} + +// SAFETY: safety requirement guaranteed by the forwarded impl. +unsafe impl ProjectIndex<[T]> for core::ops::RangeFrom { + type Output =3D [T]; + + #[inline(always)] + fn get(self, slice: *mut [T]) -> Option<*mut [T]> { + (self.start..slice.len()).get(slice) + } +} + +// SAFETY: `get` returned the pointer as is, so it always have the same pr= ovenance and offset of 0. +unsafe impl ProjectIndex<[T]> for core::ops::RangeFull { + type Output =3D [T]; + + #[inline(always)] + fn get(self, slice: *mut [T]) -> Option<*mut [T]> { + Some(slice) + } +} + +/// A helper trait to perform field projection. +/// +/// This trait has a `DEREF` generic parameter so it can be implemented tw= ice for types that +/// implement `Deref`. This will cause an ambiguity error and thus block `= Deref` types being used +/// as base of projection, as they can inject unsoundness. Users therefore= must not specify `DEREF` +/// and should always leave it to be inferred. +/// +/// # Safety +/// +/// `proj` may only invoke `f` with a valid allocation, as documentation d= escribed. +#[doc(hidden)] +pub unsafe trait ProjectField { + /// Project a pointer to a type to a pointer of a field. + /// + /// `f` may only be invoked with a valid allocation so it can safely o= btain raw pointers to + /// fields using `&raw mut`. + /// + /// This is needed because `base` might not point to a valid allocatio= n, while `&raw mut` + /// requires pointers to be in bounds of a valid allocation. + /// + /// # Safety + /// + /// `f` must return a pointer in bounds of the provided pointer. + unsafe fn proj(base: *mut Self, f: impl FnOnce(*mut Self) -> *mut F= ) -> *mut F; +} + +// NOTE: in theory, this API should work for `T: ?Sized` and `F: ?Sized`, = too. However we cannot +// currently support that as we need to obtain a valid allocation that `&r= aw const` can operate on. +// SAFETY: `proj` invokes `f` with valid allocation. +unsafe impl ProjectField for T { + #[inline(always)] + unsafe fn proj(base: *mut Self, f: impl FnOnce(*mut Self) -> *mut F= ) -> *mut F { + // Create a valid allocation to start projection, as `base` is not= necessarily so. The + // memory is never actually used so it will be optimized out, so i= t should work even for + // very large `T` (`memoffset` crate also relies on this). To be e= xtra certain, we also + // annotate `f` closure with `#[inline(always)]` in the macro. + let mut place =3D MaybeUninit::uninit(); + let place_base =3D place.as_mut_ptr(); + let field =3D f(place_base); + // SAFETY: `field` is in bounds from `base` per safety requirement. + let offset =3D unsafe { field.byte_offset_from(place_base) }; + // Use `wrapping_byte_offset` as `base` does not need to be of val= id allocation. + base.wrapping_byte_offset(offset).cast() + } +} + +// SAFETY: vacuously satisfied. +unsafe impl ProjectField for T { + #[inline(always)] + unsafe fn proj(_: *mut Self, _: impl FnOnce(*mut Self) -> *mut F) -= > *mut F { + build_error!("this function is a guard against `Deref` impl and is= never invoked"); + } +} + +/// Create a projection from a raw pointer. +/// +/// The projected pointer is within the memory region marked by the input = pointer. There is no +/// requirement that the input raw pointer needs to be valid, so this macr= o may be used for +/// projecting pointers outside normal address space, e.g. I/O pointers. H= owever, if the input +/// pointer is valid, the projected pointer is also valid. +/// +/// Supported projections include field projections and index projections. +/// It is not allowed to project into types that implement custom `Deref` = or `Index`. +/// +/// The macro has basic syntax of `kernel::ptr::project!(ptr, projection)`= , where `ptr` is an +/// expression that evaluates to a raw pointer which serves as the base of= projection. `projection` +/// can be a projection expression of form `.field` (normally identifer, o= r numeral in case of +/// tuple structs) or of form `[index]`. +/// +/// If mutable pointer is needed, the macro input can be prefixed with `mu= t` keyword, i.e. +/// `kernel::ptr::project!(mut ptr, projection)`. By default, a const poin= ter is created. +/// +/// `ptr::project!` macro can perform both fallible indexing and build-tim= e checked indexing. +/// `[index]` form performs build-time bounds checking; if compiler fails = to prove `[index]` is in +/// bounds, compilation will fail. `[index]?` can be used to perform runti= me bounds checking; +/// `OutOfBound` error is raised via `?` if the index is out of bounds. +/// +/// # Examples +/// +/// Field projections are performed with `.field_name`: +/// ``` +/// struct MyStruct { field: u32, } +/// let ptr: *const MyStruct =3D core::ptr::dangling(); +/// let field_ptr: *const u32 =3D kernel::ptr::project!(ptr, .field); +/// +/// struct MyTupleStruct(u32, u32); +/// +/// fn proj(ptr: *const MyTupleStruct) { +/// let field_ptr: *const u32 =3D kernel::ptr::project!(ptr, .1); +/// } +/// ``` +/// +/// Index projections are performed with `[index]`: +/// ``` +/// fn proj(ptr: *const [u8; 32]) -> Result { +/// let field_ptr: *const u8 =3D kernel::ptr::project!(ptr, [1]); +/// // This will fail the build. +/// // kernel::ptr::project!(ptr, [128]); +/// // This will raise an `OutOfBound` error (which is convertable to = `ERANGE`). +/// kernel::ptr::project!(ptr, [128]?); +/// Ok(()) +/// } +/// ``` +/// +/// If you need to match on the error instead of propagate, put the invoca= tion inside a closure: +/// ``` +/// let ptr: *const [u8; 32] =3D core::ptr::dangling(); +/// let field_ptr: Result<*const u8> =3D (|| -> Result<_> { +/// Ok(kernel::ptr::project!(ptr, [128]?)) +/// })(); +/// assert!(field_ptr.is_err()); +/// ``` +/// +/// For mutable pointers, put `mut` as the first token in macro invocation. +/// ``` +/// let ptr: *mut [(u8, u16); 32] =3D core::ptr::dangling_mut(); +/// let field_ptr: *mut u16 =3D kernel::ptr::project!(mut ptr, [1].1); +/// ``` +#[macro_export] +macro_rules! project_pointer { + (@gen $ptr:ident, ) =3D> {}; + // Field projection. `$field` needs to be `tt` to support tuple index = like `.0`. + (@gen $ptr:ident, .$field:tt $($rest:tt)*) =3D> { + // SAFETY: the provided closure always return in bounds pointer. + let $ptr =3D unsafe { + $crate::ptr::projection::ProjectField::proj($ptr, #[inline(alw= ays)] |ptr| { + // Check unaligned field. Not all users (e.g. DMA) can han= dle unaligned + // projections. + if false { + let _ =3D &(*ptr).$field; + } + // SAFETY: `$field` is in bounds, and no implicit `Deref` = is possible (if the + // type implements `Deref`, Rust cannot infer the generic = parameter `DEREF`). + &raw mut (*ptr).$field + }) + }; + $crate::ptr::project!(@gen $ptr, $($rest)*) + }; + // Fallible index projection. + (@gen $ptr:ident, [$index:expr]? $($rest:tt)*) =3D> { + let $ptr =3D $crate::ptr::projection::ProjectIndex::get($index, $p= tr) + .ok_or($crate::ptr::projection::OutOfBound)?; + $crate::ptr::project!(@gen $ptr, $($rest)*) + }; + // Build-time checked index projection. + (@gen $ptr:ident, [$index:expr] $($rest:tt)*) =3D> { + let $ptr =3D $crate::ptr::projection::ProjectIndex::index($index, = $ptr); + $crate::ptr::project!(@gen $ptr, $($rest)*) + }; + (mut $ptr:expr, $($proj:tt)*) =3D> {{ + let ptr: *mut _ =3D $ptr; + $crate::ptr::project!(@gen ptr, $($proj)*); + ptr + }}; + ($ptr:expr, $($proj:tt)*) =3D> {{ + let ptr =3D <*const _>::cast_mut($ptr); + // We currently always project using mutable pointer, as it is not= decided whether `&raw + // const` allows the resulting pointer to be mutated (see document= ation of `addr_of!`). + $crate::ptr::project!(@gen ptr, $($proj)*); + ptr.cast_const() + }}; +} diff --git a/scripts/Makefile.build b/scripts/Makefile.build index 32e209bc7985cbf26ad8af2b986fbd1920aa3bd4..3652b85be54594e3616be5a92c1= 967035e9d919d 100644 --- a/scripts/Makefile.build +++ b/scripts/Makefile.build @@ -310,16 +310,18 @@ $(obj)/%.lst: $(obj)/%.c FORCE =20 # The features in this list are the ones allowed for non-`rust/` code. # +# - Stable since Rust 1.79.0: `feature(slice_ptr_len)`. # - Stable since Rust 1.81.0: `feature(lint_reasons)`. # - Stable since Rust 1.82.0: `feature(asm_const)`, # `feature(offset_of_nested)`, `feature(raw_ref_op)`. +# - Stable since Rust 1.84.0: `feature(strict_provenance)`. # - Stable since Rust 1.87.0: `feature(asm_goto)`. # - Expected to become stable: `feature(arbitrary_self_types)`. # - To be determined: `feature(used_with_arg)`. # # Please see https://github.com/Rust-for-Linux/linux/issues/2 for details = on # the unstable features in use. -rust_allowed_features :=3D asm_const,asm_goto,arbitrary_self_types,lint_re= asons,offset_of_nested,raw_ref_op,used_with_arg +rust_allowed_features :=3D asm_const,asm_goto,arbitrary_self_types,lint_re= asons,offset_of_nested,raw_ref_op,slice_ptr_len,strict_provenance,used_with= _arg =20 # `--out-dir` is required to avoid temporaries being created by `rustc` in= the # current working directory, which may be not accessible in the out-of-tree --=20 2.53.0 From nobody Thu Apr 9 10:42:49 2026 Received: from smtp.kernel.org (aws-us-west-2-korg-mail-1.web.codeaurora.org [10.30.226.201]) (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 134B03A9014; Mon, 9 Mar 2026 16:35:19 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=10.30.226.201 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1773074119; cv=none; b=rnW1F8al/r6s2iOHOnEQqSuGFtH+8MbcSWWixHAejWYz96R+Dg6g8oUL2A/EMM/r2l4n+DKjpnfOzWlGpXT7xKObT/CZS6vjShAFFAsXNnc17NAVL/yoxgE6wUkf1i6TZsqrlvqg4etDoAI7U4R+VMrKeZQKj4jy84tKi3Y+JSM= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1773074119; c=relaxed/simple; bh=fotoDCgC7OeaYG7UTBkWK10x0k8SydZQX9tf3l+mJfs=; h=From:Date:Subject:MIME-Version:Content-Type:Message-Id:References: In-Reply-To:To:Cc; b=W4mdhA/IxfECsLiM7QQks7lbPvpdDD/Rr/ac9QPq/F8hbmpMeKNEjp9oxmHx2n814U1OpVHjhbuIqwcdW5fi4zCKSLcfdPIP4VcDD1OT71UtUufaB59DiG+/XSffHF7TCn9bUpl4lKF7nrGohQwKvOdfgSYvWQyF+Pqs7kgCU7U= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=kernel.org header.i=@kernel.org header.b=r6Em/fif; arc=none smtp.client-ip=10.30.226.201 Authentication-Results: smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=kernel.org header.i=@kernel.org header.b="r6Em/fif" Received: by smtp.kernel.org (Postfix) with ESMTPS id B880CC4AF0F; Mon, 9 Mar 2026 16:35:18 +0000 (UTC) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=kernel.org; s=k20201202; t=1773074118; bh=fotoDCgC7OeaYG7UTBkWK10x0k8SydZQX9tf3l+mJfs=; h=From:Date:Subject:References:In-Reply-To:To:Cc:Reply-To:From; b=r6Em/fifFf+JL09ITOtlAAdEYPnmRbGzs14wcOublz99WdP5nPGm/ONCxAnr5AZMK M7x7hCxyfaN2Yeal05eKiRHFNZvKqzOD+moycVYeGsVJwfeXXf61q9Leo9Q6tyrTEg 2efYDUvqHvW6fSoL5G6AKajWU6Q/Sp9QhB4qJHQfelRMIHz38XKOqq9As96KWUug4l xLfFnf7TGv2feP/JkRMcuGnF/SQGshKGlpLFRDeLlLcZo2gD48+6lPdfNUMC1DyNfm W6uskOpcMjOBLDpWk62LDjS3Tm1t5wCmJjsneo1fxXnjZdyGo9EXtobLT4A7eaa66P 8C7MfiBmUL9KQ== Received: from aws-us-west-2-korg-lkml-1.web.codeaurora.org (localhost.localdomain [127.0.0.1]) by smtp.lore.kernel.org (Postfix) with ESMTP id A1AFAF41811; Mon, 9 Mar 2026 16:35:18 +0000 (UTC) From: Tim Kovalenko via B4 Relay Date: Mon, 09 Mar 2026 12:34:20 -0400 Subject: [PATCH v4 3/4] rust: dma: use pointer projection infra for `dma_{read,write}` macro 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 Message-Id: <20260309-drm-rust-next-v4-3-4ef485b19a4c@proton.me> References: <20260309-drm-rust-next-v4-0-4ef485b19a4c@proton.me> In-Reply-To: <20260309-drm-rust-next-v4-0-4ef485b19a4c@proton.me> To: Alexandre Courbot , Danilo Krummrich , Alice Ryhl , David Airlie , Simona Vetter , Miguel Ojeda , Gary Guo , =?utf-8?q?Bj=C3=B6rn_Roy_Baron?= , Benno Lossin , Andreas Hindborg , Trevor Gross , Boqun Feng , Nathan Chancellor , Nicolas Schier , Abdiel Janulgue , Daniel Almeida , Robin Murphy , Boqun Feng Cc: nouveau@lists.freedesktop.org, dri-devel@lists.freedesktop.org, linux-kernel@vger.kernel.org, rust-for-linux@vger.kernel.org, linux-kbuild@vger.kernel.org, driver-core@lists.linux.dev X-Mailer: b4 0.14.2 X-Developer-Signature: v=1; a=ed25519-sha256; t=1773074117; l=15716; i=tim.kovalenko@proton.me; s=20260212; h=from:subject:message-id; bh=Upnq6G3yp4f/mbP79I/+fne9vntggLcTIt7uIQ8neSk=; b=sqTDthGSfjKed8A4BAAZQvoZUy1p5qKwmQT0xo73JLs8JyW6Hd/J2qHL7Xtc3Rx6qUw2ZkrsA Sn+jSi1eNKNCpyLy+Pl9evdmfHN3bTKaEDhtCjOHp02MZ9X+3VqvF56 X-Developer-Key: i=tim.kovalenko@proton.me; a=ed25519; pk=/+OiulEpgeZifgP4mDE4e5YlV6nMeY+frze/lY/xiHI= X-Endpoint-Received: by B4 Relay for tim.kovalenko@proton.me/20260212 with auth_id=635 X-Original-From: Tim Kovalenko Reply-To: tim.kovalenko@proton.me From: Gary Guo Current `dma_read!`, `dma_write!` macros also use a custom `addr_of!()`-based implementation for projecting pointers, which has soundness issue as it relies on absence of `Deref` implementation on types. It also has a soundness issue where it does not protect against unaligned fields (when `#[repr(packed)]` is used) so it can generate misaligned accesses. This commit migrates them to use the general pointer projection infrastructure, which handles these cases correctly. As part of migration, the macro is updated to have an improved surface syntax. The current macro have dma_read!(a.b.c[d].e.f) to mean `a.b.c` is a DMA coherent allocation and it should project into it with `[d].e.f` and do a read, which is confusing as it makes the indexing operator integral to the macro (so it will break if you have an array of `CoherentAllocation`, for example). This also is problematic as we would like to generalize `CoherentAllocation` from just slices to arbitrary types. Make the macro expects `dma_read!(path.to.dma, .path.inside.dma)` as the canonical syntax. The index operator is no longer special and is just one type of projection (in additional to field projection). Similarly, make `dma_write!(path.to.dma, .path.inside.dma, value)` become the canonical syntax for writing. Another issue of the current macro is that it is always fallible. This makes sense with existing design of `CoherentAllocation`, but once we support fixed size arrays with `CoherentAllocation`, it is desirable to have the ability to perform infallible indexing as well, e.g. doing a `[0]` index of `[Foo; 2]` is okay and can be checked at build-time, so forcing falliblity is non-ideal. To capture this, the macro is changed to use `[idx]` as infallible projection and `[idx]?` as fallible index projection (those syntax are part of the general projection infra). A benefit of this is that while individual indexing operation may fail, the overall read/write operation is not fallible. Fixes: ad2907b4e308 ("rust: add dma coherent allocator abstraction") Reviewed-by: Benno Lossin Signed-off-by: Gary Guo Acked-by: Miguel Ojeda --- drivers/gpu/nova-core/gsp.rs | 14 ++--- drivers/gpu/nova-core/gsp/boot.rs | 2 +- drivers/gpu/nova-core/gsp/cmdq.rs | 10 +++- rust/kernel/dma.rs | 114 +++++++++++++++++-----------------= ---- samples/rust/rust_dma.rs | 30 +++++----- 5 files changed, 81 insertions(+), 89 deletions(-) diff --git a/drivers/gpu/nova-core/gsp.rs b/drivers/gpu/nova-core/gsp.rs index 174feaca0a6b9269cf35286dec3acc4d60918904..25cd48514c777cb405a2af0acf5= 7196b2e2e7837 100644 --- a/drivers/gpu/nova-core/gsp.rs +++ b/drivers/gpu/nova-core/gsp.rs @@ -143,14 +143,14 @@ pub(crate) fn new(pdev: &pci::Device) = -> impl PinInit::alloc_coherent(dev, 1, GFP= _KERNEL | __GFP_ZERO)?; - dma_write!(wpr_meta[0] =3D GspFwWprMeta::new(&gsp_fw, &fb_layout))= ?; + dma_write!(wpr_meta, [0]?, GspFwWprMeta::new(&gsp_fw, &fb_layout)); =20 self.cmdq .send_command(bar, commands::SetSystemInfo::new(pdev))?; diff --git a/drivers/gpu/nova-core/gsp/cmdq.rs b/drivers/gpu/nova-core/gsp/= cmdq.rs index 87dbbd6d1be9d86e7fb45a84f9647265bd63f84e..0056bfbf0a44cfbc5a0ca08d069= f881b877e1edc 100644 --- a/drivers/gpu/nova-core/gsp/cmdq.rs +++ b/drivers/gpu/nova-core/gsp/cmdq.rs @@ -202,9 +202,13 @@ fn new(dev: &device::Device) -> Result<= Self> { =20 let gsp_mem =3D CoherentAllocation::::alloc_coherent(dev, 1, GFP_KERNE= L | __GFP_ZERO)?; - dma_write!(gsp_mem[0].ptes =3D PteArray::new(gsp_mem.dma_handle())= ?)?; - dma_write!(gsp_mem[0].cpuq.tx =3D MsgqTxHeader::new(MSGQ_SIZE, RX_= HDR_OFF, MSGQ_NUM_PAGES))?; - dma_write!(gsp_mem[0].cpuq.rx =3D MsgqRxHeader::new())?; + dma_write!(gsp_mem, [0]?.ptes, PteArray::new(gsp_mem.dma_handle())= ?); + dma_write!( + gsp_mem, + [0]?.cpuq.tx, + MsgqTxHeader::new(MSGQ_SIZE, RX_HDR_OFF, MSGQ_NUM_PAGES) + ); + dma_write!(gsp_mem, [0]?.cpuq.rx, MsgqRxHeader::new()); =20 Ok(Self(gsp_mem)) } diff --git a/rust/kernel/dma.rs b/rust/kernel/dma.rs index 909d56fd5118ee1db3585a8c10a99fe1d091dd00..cd2957b5f260b04c89e0762edba= 0820f11b064a4 100644 --- a/rust/kernel/dma.rs +++ b/rust/kernel/dma.rs @@ -461,6 +461,19 @@ pub fn size(&self) -> usize { self.count * core::mem::size_of::() } =20 + /// Returns the raw pointer to the allocated region in the CPU's virtu= al address space. + #[inline] + pub fn as_ptr(&self) -> *const [T] { + core::ptr::slice_from_raw_parts(self.cpu_addr.as_ptr(), self.count) + } + + /// Returns the raw pointer to the allocated region in the CPU's virtu= al address space as + /// a mutable pointer. + #[inline] + pub fn as_mut_ptr(&self) -> *mut [T] { + core::ptr::slice_from_raw_parts_mut(self.cpu_addr.as_ptr(), self.c= ount) + } + /// Returns the base address to the allocated region in the CPU's virt= ual address space. pub fn start_ptr(&self) -> *const T { self.cpu_addr.as_ptr() @@ -581,23 +594,6 @@ pub unsafe fn write(&mut self, src: &[T], offset: usiz= e) -> Result { Ok(()) } =20 - /// Returns a pointer to an element from the region with bounds checki= ng. `offset` is in - /// units of `T`, not the number of bytes. - /// - /// Public but hidden since it should only be used from [`dma_read`] a= nd [`dma_write`] macros. - #[doc(hidden)] - pub fn item_from_index(&self, offset: usize) -> Result<*mut T> { - if offset >=3D self.count { - return Err(EINVAL); - } - // SAFETY: - // - The pointer is valid due to type invariant on `CoherentAlloca= tion` - // and we've just checked that the range and index is within bound= s. - // - `offset` can't overflow since it is smaller than `self.count`= and we've checked - // that `self.count` won't overflow early in the constructor. - Ok(unsafe { self.cpu_addr.as_ptr().add(offset) }) - } - /// Reads the value of `field` and ensures that its type is [`FromByte= s`]. /// /// # Safety @@ -670,6 +666,9 @@ unsafe impl Send for Coh= erentAllocation {} =20 /// Reads a field of an item from an allocated region of structs. /// +/// The syntax is of form `kernel::dma_read!(dma, proj)` where `dma` is an= expression to an +/// [`CoherentAllocation`] and `proj` is a [projection specification](kern= el::ptr::project!). +/// /// # Examples /// /// ``` @@ -684,36 +683,29 @@ unsafe impl Send for C= oherentAllocation {} /// unsafe impl kernel::transmute::AsBytes for MyStruct{}; /// /// # fn test(alloc: &kernel::dma::CoherentAllocation) -> Result= { -/// let whole =3D kernel::dma_read!(alloc[2]); -/// let field =3D kernel::dma_read!(alloc[1].field); +/// let whole =3D kernel::dma_read!(alloc, [2]?); +/// let field =3D kernel::dma_read!(alloc, [1]?.field); /// # Ok::<(), Error>(()) } /// ``` #[macro_export] macro_rules! dma_read { - ($dma:expr, $idx: expr, $($field:tt)*) =3D> {{ - (|| -> ::core::result::Result<_, $crate::error::Error> { - let item =3D $crate::dma::CoherentAllocation::item_from_index(= &$dma, $idx)?; - // SAFETY: `item_from_index` ensures that `item` is always a v= alid pointer and can be - // dereferenced. The compiler also further validates the expre= ssion on whether `field` - // is a member of `item` when expanded by the macro. - unsafe { - let ptr_field =3D ::core::ptr::addr_of!((*item) $($field)*= ); - ::core::result::Result::Ok( - $crate::dma::CoherentAllocation::field_read(&$dma, ptr= _field) - ) - } - })() + ($dma:expr, $($proj:tt)*) =3D> {{ + let dma =3D &$dma; + let ptr =3D $crate::ptr::project!( + $crate::dma::CoherentAllocation::as_ptr(dma), $($proj)* + ); + // SAFETY: pointer created by projection is within DMA region. + unsafe { $crate::dma::CoherentAllocation::field_read(dma, ptr) } }}; - ($dma:ident [ $idx:expr ] $($field:tt)* ) =3D> { - $crate::dma_read!($dma, $idx, $($field)*) - }; - ($($dma:ident).* [ $idx:expr ] $($field:tt)* ) =3D> { - $crate::dma_read!($($dma).*, $idx, $($field)*) - }; } =20 /// Writes to a field of an item from an allocated region of structs. /// +/// The syntax is of form `kernel::dma_write!(dma, proj, val)` where `dma`= is an expression to an +/// [`CoherentAllocation`] and `proj` is a [projection specification](kern= el::ptr::project!), +/// and `val` is the value to be written to the projected location. +/// +/// /// # Examples /// /// ``` @@ -728,37 +720,31 @@ macro_rules! dma_read { /// unsafe impl kernel::transmute::AsBytes for MyStruct{}; /// /// # fn test(alloc: &kernel::dma::CoherentAllocation) -> Result= { -/// kernel::dma_write!(alloc[2].member =3D 0xf); -/// kernel::dma_write!(alloc[1] =3D MyStruct { member: 0xf }); +/// kernel::dma_write!(alloc, [2]?.member, 0xf); +/// kernel::dma_write!(alloc, [1]?, MyStruct { member: 0xf }); /// # Ok::<(), Error>(()) } /// ``` #[macro_export] macro_rules! dma_write { - ($dma:ident [ $idx:expr ] $($field:tt)*) =3D> {{ - $crate::dma_write!($dma, $idx, $($field)*) - }}; - ($($dma:ident).* [ $idx:expr ] $($field:tt)* ) =3D> {{ - $crate::dma_write!($($dma).*, $idx, $($field)*) + (@parse [$dma:expr] [$($proj:tt)*] [, $val:expr]) =3D> {{ + let dma =3D &$dma; + let ptr =3D $crate::ptr::project!( + mut $crate::dma::CoherentAllocation::as_mut_ptr(dma), $($proj)* + ); + let val =3D $val; + // SAFETY: pointer created by projection is within DMA region. + unsafe { $crate::dma::CoherentAllocation::field_write(dma, ptr, va= l) } }}; - ($dma:expr, $idx: expr, =3D $val:expr) =3D> { - (|| -> ::core::result::Result<_, $crate::error::Error> { - let item =3D $crate::dma::CoherentAllocation::item_from_index(= &$dma, $idx)?; - // SAFETY: `item_from_index` ensures that `item` is always a v= alid item. - unsafe { $crate::dma::CoherentAllocation::field_write(&$dma, i= tem, $val) } - ::core::result::Result::Ok(()) - })() + (@parse [$dma:expr] [$($proj:tt)*] [.$field:tt $($rest:tt)*]) =3D> { + $crate::dma_write!(@parse [$dma] [$($proj)* .$field] [$($rest)*]) + }; + (@parse [$dma:expr] [$($proj:tt)*] [[$index:expr]? $($rest:tt)*]) =3D>= { + $crate::dma_write!(@parse [$dma] [$($proj)* [$index]?] [$($rest)*]) + }; + (@parse [$dma:expr] [$($proj:tt)*] [[$index:expr] $($rest:tt)*]) =3D> { + $crate::dma_write!(@parse [$dma] [$($proj)* [$index]] [$($rest)*]) }; - ($dma:expr, $idx: expr, $(.$field:ident)* =3D $val:expr) =3D> { - (|| -> ::core::result::Result<_, $crate::error::Error> { - let item =3D $crate::dma::CoherentAllocation::item_from_index(= &$dma, $idx)?; - // SAFETY: `item_from_index` ensures that `item` is always a v= alid pointer and can be - // dereferenced. The compiler also further validates the expre= ssion on whether `field` - // is a member of `item` when expanded by the macro. - unsafe { - let ptr_field =3D ::core::ptr::addr_of_mut!((*item) $(.$fi= eld)*); - $crate::dma::CoherentAllocation::field_write(&$dma, ptr_fi= eld, $val) - } - ::core::result::Result::Ok(()) - })() + ($dma:expr, $($rest:tt)*) =3D> { + $crate::dma_write!(@parse [$dma] [] [$($rest)*]) }; } diff --git a/samples/rust/rust_dma.rs b/samples/rust/rust_dma.rs index 9c45851c876ef33414eb0071c42a2fb4ac3f1e78..ce39b55450978e69f40b20bb2a0= 479973f2843ad 100644 --- a/samples/rust/rust_dma.rs +++ b/samples/rust/rust_dma.rs @@ -68,7 +68,7 @@ fn probe(pdev: &pci::Device, _info: &Self::IdInfo) = -> impl PinInit, _info: &Self::IdInfo= ) -> impl PinInit Result { + for (i, value) in TEST_VALUES.into_iter().enumerate() { + let val0 =3D kernel::dma_read!(self.ca, [i]?.h); + let val1 =3D kernel::dma_read!(self.ca, [i]?.b); + + assert_eq!(val0, value.0); + assert_eq!(val1, value.1); + } + + Ok(()) + } +} + #[pinned_drop] impl PinnedDrop for DmaSampleDriver { fn drop(self: Pin<&mut Self>) { dev_info!(self.pdev, "Unload DMA test driver.\n"); =20 - for (i, value) in TEST_VALUES.into_iter().enumerate() { - let val0 =3D kernel::dma_read!(self.ca[i].h); - let val1 =3D kernel::dma_read!(self.ca[i].b); - assert!(val0.is_ok()); - assert!(val1.is_ok()); - - if let Ok(val0) =3D val0 { - assert_eq!(val0, value.0); - } - if let Ok(val1) =3D val1 { - assert_eq!(val1, value.1); - } - } + assert!(self.check_dma().is_ok()); =20 for (i, entry) in self.sgt.iter().enumerate() { dev_info!( --=20 2.53.0 From nobody Thu Apr 9 10:42:49 2026 Received: from smtp.kernel.org (aws-us-west-2-korg-mail-1.web.codeaurora.org [10.30.226.201]) (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 1354B3A9D8A; Mon, 9 Mar 2026 16:35:19 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=10.30.226.201 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1773074119; cv=none; b=FpW3ftCdADHUfVRi1txs5H3AK134YjI/m8rumOZGFcYAjPqKufkkaFlV1ZP+QFVvchKdZNJHNQzcxwMYbi++09d+0Jc1UQHLM5svao4epYV5CYI4cXPEvKLWWvw6W7XVwlS88DMGk5UtFBQWJzXuUEmK0DoCQuvV1ZlEfbi7R3E= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1773074119; c=relaxed/simple; bh=fssZhaE39h8/ifGb5lyKDHlx9SyrHqGfVID8VjmY0ME=; h=From:Date:Subject:MIME-Version:Content-Type:Message-Id:References: In-Reply-To:To:Cc; b=AaVgx4Wp8NeWhrHizVG7oGkejqq7M2iawmhG7jQj6PwZRwFp6ImLy+4buVtI6oJCsNjNb/f24TlbS95M/MUN8263SU/wGsq0MGix9uMBHR0GY4P7lQeWQk8DtioD7bv6H1IrySEFt45Z/lfv6OD0XsiU4wXn3/pt8e9uZC43OOs= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=kernel.org header.i=@kernel.org header.b=nE9CPFES; arc=none smtp.client-ip=10.30.226.201 Authentication-Results: smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=kernel.org header.i=@kernel.org header.b="nE9CPFES" Received: by smtp.kernel.org (Postfix) with ESMTPS id C224BC2BCB7; Mon, 9 Mar 2026 16:35:18 +0000 (UTC) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=kernel.org; s=k20201202; t=1773074118; bh=fssZhaE39h8/ifGb5lyKDHlx9SyrHqGfVID8VjmY0ME=; h=From:Date:Subject:References:In-Reply-To:To:Cc:Reply-To:From; b=nE9CPFESeIElYpdFRVFYi/eDY9RoazP12jcTKXLiS3P7dRa9zphKtGh1pPAm0qMUf Si1w3fT84m6mtNoKkiABeBnJPXnSSW1pcCw5+SieCJkAQsC0XZRTZbvXQhkysdgEm9 B56/DvoqOv6oxIeGkLvbO/i/fL6vA8SyOZbjKIf6Ub6oMWmKpiG3trmrmtJGoc+9ut oISHSJI+QPG7Oi1wFCM0b/vDyzjppoceO75Z3Y3wZExWRhgsBxLQdEUmL3sEVDuhK9 Xmb/yGfDEC15+YoOym7ExlwwmTmLi825LE4F0VxIHZet28u3jXNL+ZEPYjOhdEi7rC oLdNDkjd3nh2g== Received: from aws-us-west-2-korg-lkml-1.web.codeaurora.org (localhost.localdomain [127.0.0.1]) by smtp.lore.kernel.org (Postfix) with ESMTP id B1174F41812; Mon, 9 Mar 2026 16:35:18 +0000 (UTC) From: Tim Kovalenko via B4 Relay Date: Mon, 09 Mar 2026 12:34:21 -0400 Subject: [PATCH v4 4/4] gpu: nova-core: fix stack overflow in GSP memory allocation 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 Message-Id: <20260309-drm-rust-next-v4-4-4ef485b19a4c@proton.me> References: <20260309-drm-rust-next-v4-0-4ef485b19a4c@proton.me> In-Reply-To: <20260309-drm-rust-next-v4-0-4ef485b19a4c@proton.me> To: Alexandre Courbot , Danilo Krummrich , Alice Ryhl , David Airlie , Simona Vetter , Miguel Ojeda , Gary Guo , =?utf-8?q?Bj=C3=B6rn_Roy_Baron?= , Benno Lossin , Andreas Hindborg , Trevor Gross , Boqun Feng , Nathan Chancellor , Nicolas Schier , Abdiel Janulgue , Daniel Almeida , Robin Murphy , Boqun Feng Cc: nouveau@lists.freedesktop.org, dri-devel@lists.freedesktop.org, linux-kernel@vger.kernel.org, rust-for-linux@vger.kernel.org, linux-kbuild@vger.kernel.org, driver-core@lists.linux.dev, Tim Kovalenko X-Mailer: b4 0.14.2 X-Developer-Signature: v=1; a=ed25519-sha256; t=1773074117; l=3986; i=tim.kovalenko@proton.me; s=20260212; h=from:subject:message-id; bh=02lIiBKVUWPZgiHflont6ppBx+2QAZbO4PzXiivsIkE=; b=pE0ZKAo+0q+3X2PamEznSFPbbQiSdIw7Xy/EgQG+Zfy/iiwn0Rb6Om2UKZsCeEXyeudQjTtkF +jL/j8+uX6ICxdDQJsEfm++qZt+CwLBc7cGDTtVkdNtnXo/EFKH2KKd X-Developer-Key: i=tim.kovalenko@proton.me; a=ed25519; pk=/+OiulEpgeZifgP4mDE4e5YlV6nMeY+frze/lY/xiHI= X-Endpoint-Received: by B4 Relay for tim.kovalenko@proton.me/20260212 with auth_id=635 X-Original-From: Tim Kovalenko Reply-To: tim.kovalenko@proton.me From: Tim Kovalenko The `Cmdq::new` function was allocating a `PteArray` struct on the stack and was causing a stack overflow with 8216 bytes. Modify the `PteArray` to calculate and write the Page Table Entries directly into the coherent DMA buffer one-by-one. This reduces the stack usage quite a lot. Signed-off-by: Tim Kovalenko Acked-by: Alexandre Courbot Reported-by: Gary Guo --- drivers/gpu/nova-core/gsp.rs | 34 +++++++++++++++++++--------------- drivers/gpu/nova-core/gsp/cmdq.rs | 15 ++++++++++++++- 2 files changed, 33 insertions(+), 16 deletions(-) diff --git a/drivers/gpu/nova-core/gsp.rs b/drivers/gpu/nova-core/gsp.rs index 25cd48514c777cb405a2af0acf57196b2e2e7837..20170e483e04c476efce8997b39= 16b0ad829ed38 100644 --- a/drivers/gpu/nova-core/gsp.rs +++ b/drivers/gpu/nova-core/gsp.rs @@ -47,16 +47,11 @@ unsafe impl AsBytes for PteArray {} =20 impl PteArray { - /// Creates a new page table array mapping `NUM_PAGES` GSP pages start= ing at address `start`. - fn new(start: DmaAddress) -> Result { - let mut ptes =3D [0u64; NUM_PAGES]; - for (i, pte) in ptes.iter_mut().enumerate() { - *pte =3D start - .checked_add(num::usize_as_u64(i) << GSP_PAGE_SHIFT) - .ok_or(EOVERFLOW)?; - } - - Ok(Self(ptes)) + /// Returns the page table entry for `index`, for a mapping starting a= t `start` DmaAddress. + fn entry(start: DmaAddress, index: usize) -> Result { + start + .checked_add(num::usize_as_u64(index) << GSP_PAGE_SHIFT) + .ok_or(EOVERFLOW) } } =20 @@ -86,16 +81,25 @@ fn new(dev: &device::Device) -> Result { NUM_PAGES * GSP_PAGE_SIZE, GFP_KERNEL | __GFP_ZERO, )?); - let ptes =3D PteArray::::new(obj.0.dma_handle())?; + + let start_addr =3D obj.0.dma_handle(); =20 // SAFETY: `obj` has just been created and we are its sole user. - unsafe { - // Copy the self-mapping PTE at the expected location. + let pte_region =3D unsafe { obj.0 - .as_slice_mut(size_of::(), size_of_val(&ptes))? - .copy_from_slice(ptes.as_bytes()) + .as_slice_mut(size_of::(), NUM_PAGES * size_of::= ())? }; =20 + // This is a one by one GSP Page write to the memory + // to avoid stack overflow when allocating the whole array at once. + for (i, chunk) in pte_region.chunks_exact_mut(size_of::()).en= umerate() { + let pte_value =3D start_addr + .checked_add(num::usize_as_u64(i) << GSP_PAGE_SHIFT) + .ok_or(EOVERFLOW)?; + + chunk.copy_from_slice(&pte_value.to_ne_bytes()); + } + Ok(obj) } } diff --git a/drivers/gpu/nova-core/gsp/cmdq.rs b/drivers/gpu/nova-core/gsp/= cmdq.rs index 0056bfbf0a44cfbc5a0ca08d069f881b877e1edc..c8327d3098f73f9b880eee99038= ad10a16e1e32d 100644 --- a/drivers/gpu/nova-core/gsp/cmdq.rs +++ b/drivers/gpu/nova-core/gsp/cmdq.rs @@ -202,7 +202,20 @@ fn new(dev: &device::Device) -> Result<= Self> { =20 let gsp_mem =3D CoherentAllocation::::alloc_coherent(dev, 1, GFP_KERNE= L | __GFP_ZERO)?; - dma_write!(gsp_mem, [0]?.ptes, PteArray::new(gsp_mem.dma_handle())= ?); + + const NUM_PTES: usize =3D GSP_PAGE_SIZE / size_of::(); + + let start =3D gsp_mem.dma_handle(); + // One by one GSP Page write to the memory to avoid stack overflow= when allocating + // the whole array at once. + for i in 0..NUM_PTES { + dma_write!( + gsp_mem, + [0]?.ptes.0[i], + PteArray::::entry(start, i)? + ); + } + dma_write!( gsp_mem, [0]?.cpuq.tx, --=20 2.53.0