public inbox for rust-for-linux@vger.kernel.org
 help / color / mirror / Atom feed
* [PATCH 0/5] Rework index projection syntax
@ 2026-04-15 19:57 Gary Guo
  2026-04-15 19:57 ` [PATCH 1/5] rust: ptr: add panicking index projection variant Gary Guo
                   ` (4 more replies)
  0 siblings, 5 replies; 11+ messages in thread
From: Gary Guo @ 2026-04-15 19:57 UTC (permalink / raw)
  To: Danilo Krummrich, Abdiel Janulgue, Daniel Almeida, Robin Murphy,
	Andreas Hindborg, Miguel Ojeda, Boqun Feng, Björn Roy Baron,
	Benno Lossin, Alice Ryhl, Trevor Gross, Alexandre Courbot,
	David Airlie, Simona Vetter
  Cc: driver-core, rust-for-linux, linux-kernel, nouveau, dri-devel,
	Gary Guo

This series reworks the index projection syntax in the pointer
projection infrastructure to be use the keyworded syntax to be more
explicit.

Doing this allows adding a new panicking variant of index projection,
just like the normal index operator. This is useful for cases where
compiler cannot prove (or cannot reliably prove) access is inbounds.

The new syntax looks like this:
- `[try: index]`: Fallible indexing (replaces `[index]?`).
- `[build: index]`: Build-time checked indexing (replaces `[index]`).
- `[panic: index]`: Runtime panicking indexing (newly added).

DMA sample driver and nova-core have been updated.

---
Alexandre Courbot (1):
      gpu: nova-core: use pointer projection for command queue code

Gary Guo (4):
      rust: ptr: add panicking index projection variant
      rust: dma: update to keyworded index projection syntax
      gpu: nova-core: convert to keyworded projection syntax
      rust: ptr: remove implicit index projection syntax

 drivers/gpu/nova-core/gsp/cmdq.rs | 108 +++++++++++++++++++++-----------------
 rust/kernel/dma.rs                |  15 +++---
 rust/kernel/ptr/projection.rs     |  90 +++++++++++++++++++++++++------
 samples/rust/rust_dma.rs          |  12 ++---
 4 files changed, 145 insertions(+), 80 deletions(-)
---
base-commit: 1c7cc4904160c6fc6377564140062d68a3dc93a0
change-id: 20260415-projection-syntax-rework-b790a305bc52

Best regards,
--  
Gary Guo <gary@garyguo.net>


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

* [PATCH 1/5] rust: ptr: add panicking index projection variant
  2026-04-15 19:57 [PATCH 0/5] Rework index projection syntax Gary Guo
@ 2026-04-15 19:57 ` Gary Guo
  2026-04-16  7:07   ` Alice Ryhl
  2026-04-15 19:57 ` [PATCH 2/5] rust: dma: update to keyworded index projection syntax Gary Guo
                   ` (3 subsequent siblings)
  4 siblings, 1 reply; 11+ messages in thread
From: Gary Guo @ 2026-04-15 19:57 UTC (permalink / raw)
  To: Danilo Krummrich, Abdiel Janulgue, Daniel Almeida, Robin Murphy,
	Andreas Hindborg, Miguel Ojeda, Boqun Feng, Björn Roy Baron,
	Benno Lossin, Alice Ryhl, Trevor Gross, Alexandre Courbot,
	David Airlie, Simona Vetter
  Cc: driver-core, rust-for-linux, linux-kernel, nouveau, dri-devel,
	Gary Guo

There have been a few cases where the programmer knows that the indices are
in bounds but compiler cannot deduce that. This is also
compiler-version-dependent, so using build indexing here can be
problematic. On the other hand, it is also not ideal to use the fallible
variant, as it adds error handling path that is never hit.

Add a new panicking index projection for this scenario. Like all panicking
operations, this should be used carefully only in cases where the user
knows the index is going to be in bounds, and panicking would indicate
something is catastrophically wrong.

To signify this, require users to explicitly denote the type of index being
used. The existing two types of index projections also gain the keyworded
version, which will be the recommended way going forward.

The keyworded syntax also paves the way of perhaps adding more flavors in
the future, e.g. `unsafe` index projection. However, unless the code is
extremely performance sensitive and bounds checking cannot be tolerated,
panicking variant is safer and should be preferred, so it will be left to
future when demand arises.

Signed-off-by: Gary Guo <gary@garyguo.net>
---
 rust/kernel/dma.rs            |  3 ++
 rust/kernel/ptr/projection.rs | 98 +++++++++++++++++++++++++++++++++++--------
 2 files changed, 84 insertions(+), 17 deletions(-)

diff --git a/rust/kernel/dma.rs b/rust/kernel/dma.rs
index 4995ee5dc689..3e4d44749aaf 100644
--- a/rust/kernel/dma.rs
+++ b/rust/kernel/dma.rs
@@ -1207,6 +1207,9 @@ macro_rules! dma_write {
     (@parse [$dma:expr] [$($proj:tt)*] [.$field:tt $($rest:tt)*]) => {
         $crate::dma_write!(@parse [$dma] [$($proj)* .$field] [$($rest)*])
     };
+    (@parse [$dma:expr] [$($proj:tt)*] [[$flavor:ident: $index:expr] $($rest:tt)*]) => {
+        $crate::dma_write!(@parse [$dma] [$($proj)* [$flavor: $index]] [$($rest)*])
+    };
     (@parse [$dma:expr] [$($proj:tt)*] [[$index:expr]? $($rest:tt)*]) => {
         $crate::dma_write!(@parse [$dma] [$($proj)* [$index]?] [$($rest)*])
     };
diff --git a/rust/kernel/ptr/projection.rs b/rust/kernel/ptr/projection.rs
index 140ea8e21617..845811795393 100644
--- a/rust/kernel/ptr/projection.rs
+++ b/rust/kernel/ptr/projection.rs
@@ -26,14 +26,14 @@ fn from(_: OutOfBound) -> Self {
 ///
 /// # Safety
 ///
-/// The implementation of `index` and `get` (if [`Some`] is returned) must ensure that, if provided
-/// input pointer `slice` and returned pointer `output`, then:
+/// The implementation of `index`, `build_index` and `get` (if [`Some`] is returned) must ensure
+/// 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 by `get` or `index` is
-/// also valid.
+/// This means that if the input pointer is valid, then pointer returned by `get`, `index` or
+/// `build_index` is also valid.
 #[diagnostic::on_unimplemented(message = "`{Self}` cannot be used to index `{T}`")]
 #[doc(hidden)]
 pub unsafe trait ProjectIndex<T: ?Sized>: Sized {
@@ -42,9 +42,12 @@ pub unsafe trait ProjectIndex<T: ?Sized>: Sized {
     /// Returns an index-projected pointer, if in bounds.
     fn get(self, slice: *mut T) -> Option<*mut Self::Output>;
 
+    /// Returns an index-projected pointer; panic if out of bounds.
+    fn index(self, slice: *mut T) -> *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 {
+    fn build_index(self, slice: *mut T) -> *mut Self::Output {
         Self::get(self, slice).unwrap_or_else(|| build_error!())
     }
 }
@@ -67,6 +70,11 @@ fn index(self, slice: *mut T) -> *mut Self::Output {
     fn index(self, slice: *mut [T; N]) -> *mut Self::Output {
         <I as ProjectIndex<[T]>>::index(self, slice)
     }
+
+    #[inline(always)]
+    fn build_index(self, slice: *mut [T; N]) -> *mut Self::Output {
+        <I as ProjectIndex<[T]>>::build_index(self, slice)
+    }
 }
 
 // SAFETY: `get`-returned pointer has the same provenance as `slice` and the offset is checked to
@@ -82,6 +90,14 @@ fn get(self, slice: *mut [T]) -> Option<*mut T> {
             Some(slice.cast::<T>().wrapping_add(self))
         }
     }
+
+    #[inline(always)]
+    fn index(self, slice: *mut [T]) -> *mut T {
+        // Leverage Rust built-in operators for bounds checking.
+        // SAFETY: All non-null and aligned pointers are valid for ZST read.
+        unsafe { core::slice::from_raw_parts::<()>(core::ptr::dangling(), slice.len())[self] };
+        slice.cast::<T>().wrapping_add(self)
+    }
 }
 
 // SAFETY: `get`-returned pointer has the same provenance as `slice` and the offset is checked to
@@ -100,6 +116,18 @@ fn get(self, slice: *mut [T]) -> Option<*mut [T]> {
             new_len,
         ))
     }
+
+    #[inline(always)]
+    fn index(self, slice: *mut [T]) -> *mut [T] {
+        // Leverage Rust built-in operators for bounds checking.
+        // SAFETY: All non-null and aligned pointers are valid for ZST read.
+        unsafe {
+            _ = core::slice::from_raw_parts::<()>(core::ptr::dangling(), slice.len())[self.clone()];
+        };
+
+        // SAFETY: bounds checked.
+        unsafe { self.get(slice).unwrap_unchecked() }
+    }
 }
 
 // SAFETY: Safety requirement guaranteed by the forwarded impl.
@@ -110,6 +138,11 @@ unsafe impl<T> ProjectIndex<[T]> for core::ops::RangeTo<usize> {
     fn get(self, slice: *mut [T]) -> Option<*mut [T]> {
         (0..self.end).get(slice)
     }
+
+    #[inline(always)]
+    fn index(self, slice: *mut [T]) -> *mut [T] {
+        (0..self.end).index(slice)
+    }
 }
 
 // SAFETY: Safety requirement guaranteed by the forwarded impl.
@@ -120,6 +153,11 @@ unsafe impl<T> ProjectIndex<[T]> for core::ops::RangeFrom<usize> {
     fn get(self, slice: *mut [T]) -> Option<*mut [T]> {
         (self.start..slice.len()).get(slice)
     }
+
+    #[inline(always)]
+    fn index(self, slice: *mut [T]) -> *mut [T] {
+        (self.start..slice.len()).index(slice)
+    }
 }
 
 // SAFETY: `get` returned the pointer as is, so it always has the same provenance and offset of 0.
@@ -130,6 +168,11 @@ unsafe impl<T> ProjectIndex<[T]> for core::ops::RangeFull {
     fn get(self, slice: *mut [T]) -> Option<*mut [T]> {
         Some(slice)
     }
+
+    #[inline(always)]
+    fn index(self, slice: *mut [T]) -> *mut [T] {
+        slice
+    }
 }
 
 /// A helper trait to perform field projection.
@@ -208,9 +251,12 @@ unsafe fn proj<F>(_: *mut Self, _: impl FnOnce(*mut Self) -> *mut F) -> *mut F {
 /// `kernel::ptr::project!(mut ptr, projection)`. By default, a const pointer is created.
 ///
 /// `ptr::project!` macro can perform both fallible indexing and build-time 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 runtime bounds checking;
-/// `OutOfBound` error is raised via `?` if the index is out of bounds.
+/// The syntax is of form `[<flavor> index]` where `flavor` indicates the way of handling index
+/// out-of-bound errors.
+/// - `try` will raise an `OutOfBound` error (which is convertible to `ERANGE`).
+/// - `build` will use [`build_assert!`](kernel::build_assert::build_assert) mechanism to have
+///   compiler validate the index is in bounds.
+/// - `panic` will cause a Rust [`panic!`] if index goes out of bound.
 ///
 /// # Examples
 ///
@@ -228,17 +274,21 @@ unsafe fn proj<F>(_: *mut Self, _: impl FnOnce(*mut Self) -> *mut F) -> *mut F {
 /// }
 /// ```
 ///
-/// Index projections are performed with `[index]`:
+/// Index projections are performed with `[<flavor>: index]`, where `flavor` is `try`, `build` or
+/// `panic`:
 ///
 /// ```
 /// fn proj(ptr: *const [u8; 32]) -> Result {
-///     let field_ptr: *const u8 = kernel::ptr::project!(ptr, [1]);
+///     let field_ptr: *const u8 = kernel::ptr::project!(ptr, [build: 1]);
 ///     // The following invocation, if uncommented, would fail the build.
 ///     //
-///     // kernel::ptr::project!(ptr, [128]);
+///     // kernel::ptr::project!(ptr, [build: 128]);
 ///
 ///     // This will raise an `OutOfBound` error (which is convertible to `ERANGE`).
-///     kernel::ptr::project!(ptr, [128]?);
+///     kernel::ptr::project!(ptr, [try: 128]);
+///
+///     // This will panic in runtime if executed.
+///     kernel::ptr::project!(ptr, [panic: 128]);
 ///     Ok(())
 /// }
 /// ```
@@ -248,7 +298,7 @@ unsafe fn proj<F>(_: *mut Self, _: impl FnOnce(*mut Self) -> *mut F) -> *mut F {
 /// ```
 /// let ptr: *const [u8; 32] = core::ptr::dangling();
 /// let field_ptr: Result<*const u8> = (|| -> Result<_> {
-///     Ok(kernel::ptr::project!(ptr, [128]?))
+///     Ok(kernel::ptr::project!(ptr, [try: 128]))
 /// })();
 /// assert!(field_ptr.is_err());
 /// ```
@@ -257,7 +307,7 @@ unsafe fn proj<F>(_: *mut Self, _: impl FnOnce(*mut Self) -> *mut F) -> *mut F {
 ///
 /// ```
 /// let ptr: *mut [(u8, u16); 32] = core::ptr::dangling_mut();
-/// let field_ptr: *mut u16 = kernel::ptr::project!(mut ptr, [1].1);
+/// let field_ptr: *mut u16 = kernel::ptr::project!(mut ptr, [build: 1].1);
 /// ```
 #[macro_export]
 macro_rules! project_pointer {
@@ -280,16 +330,30 @@ macro_rules! project_pointer {
         $crate::ptr::project!(@gen $ptr, $($rest)*)
     };
     // Fallible index projection.
-    (@gen $ptr:ident, [$index:expr]? $($rest:tt)*) => {
+    (@gen $ptr:ident, [try: $index:expr] $($rest:tt)*) => {
         let $ptr = $crate::ptr::projection::ProjectIndex::get($index, $ptr)
             .ok_or($crate::ptr::projection::OutOfBound)?;
         $crate::ptr::project!(@gen $ptr, $($rest)*)
     };
-    // Build-time checked index projection.
-    (@gen $ptr:ident, [$index:expr] $($rest:tt)*) => {
+    // Panicking index projection.
+    (@gen $ptr:ident, [panic: $index:expr] $($rest:tt)*) => {
         let $ptr = $crate::ptr::projection::ProjectIndex::index($index, $ptr);
         $crate::ptr::project!(@gen $ptr, $($rest)*)
     };
+    // Build-time checked index projection.
+    (@gen $ptr:ident, [build: $index:expr] $($rest:tt)*) => {
+        let $ptr = $crate::ptr::projection::ProjectIndex::build_index($index, $ptr);
+        $crate::ptr::project!(@gen $ptr, $($rest)*)
+    };
+
+    // For compatibility
+    (@gen $ptr:ident, [$index:expr]? $($rest:tt)*) => {
+        $crate::ptr::project!(@gen $ptr, [try: $index] $($rest)*)
+    };
+    (@gen $ptr:ident, [$index:expr] $($rest:tt)*) => {
+        $crate::ptr::project!(@gen $ptr, [build: $index] $($rest)*)
+    };
+
     (mut $ptr:expr, $($proj:tt)*) => {{
         let ptr: *mut _ = $ptr;
         $crate::ptr::project!(@gen ptr, $($proj)*);

-- 
2.51.2


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

* [PATCH 2/5] rust: dma: update to keyworded index projection syntax
  2026-04-15 19:57 [PATCH 0/5] Rework index projection syntax Gary Guo
  2026-04-15 19:57 ` [PATCH 1/5] rust: ptr: add panicking index projection variant Gary Guo
@ 2026-04-15 19:57 ` Gary Guo
  2026-04-16  7:07   ` Alice Ryhl
  2026-04-15 19:57 ` [PATCH 3/5] gpu: nova-core: convert to keyworded " Gary Guo
                   ` (2 subsequent siblings)
  4 siblings, 1 reply; 11+ messages in thread
From: Gary Guo @ 2026-04-15 19:57 UTC (permalink / raw)
  To: Danilo Krummrich, Abdiel Janulgue, Daniel Almeida, Robin Murphy,
	Andreas Hindborg, Miguel Ojeda, Boqun Feng, Björn Roy Baron,
	Benno Lossin, Alice Ryhl, Trevor Gross, Alexandre Courbot,
	David Airlie, Simona Vetter
  Cc: driver-core, rust-for-linux, linux-kernel, nouveau, dri-devel,
	Gary Guo

Demonstrate the preferred syntax of index projection in DMA documentation
and examples. A few `[i]?` cases are converted to demonstrate the new
variant.

Signed-off-by: Gary Guo <gary@garyguo.net>
---
 rust/kernel/dma.rs       |  8 ++++----
 samples/rust/rust_dma.rs | 12 +++++-------
 2 files changed, 9 insertions(+), 11 deletions(-)

diff --git a/rust/kernel/dma.rs b/rust/kernel/dma.rs
index 3e4d44749aaf..d6382904a90d 100644
--- a/rust/kernel/dma.rs
+++ b/rust/kernel/dma.rs
@@ -1152,8 +1152,8 @@ unsafe impl Sync for CoherentHandle {}
 /// unsafe impl kernel::transmute::AsBytes for MyStruct{};
 ///
 /// # fn test(alloc: &kernel::dma::Coherent<[MyStruct]>) -> Result {
-/// let whole = kernel::dma_read!(alloc, [2]?);
-/// let field = kernel::dma_read!(alloc, [1]?.field);
+/// let whole = kernel::dma_read!(alloc, [try: 2]);
+/// let field = kernel::dma_read!(alloc, [panic: 1].field);
 /// # Ok::<(), Error>(()) }
 /// ```
 #[macro_export]
@@ -1189,8 +1189,8 @@ macro_rules! dma_read {
 /// unsafe impl kernel::transmute::AsBytes for MyStruct{};
 ///
 /// # fn test(alloc: &kernel::dma::Coherent<[MyStruct]>) -> Result {
-/// kernel::dma_write!(alloc, [2]?.member, 0xf);
-/// kernel::dma_write!(alloc, [1]?, MyStruct { member: 0xf });
+/// kernel::dma_write!(alloc, [try: 2].member, 0xf);
+/// kernel::dma_write!(alloc, [panic: 1], MyStruct { member: 0xf });
 /// # Ok::<(), Error>(()) }
 /// ```
 #[macro_export]
diff --git a/samples/rust/rust_dma.rs b/samples/rust/rust_dma.rs
index 129bb4b39c04..a2c34bb74273 100644
--- a/samples/rust/rust_dma.rs
+++ b/samples/rust/rust_dma.rs
@@ -73,7 +73,7 @@ fn probe(pdev: &pci::Device<Core>, _info: &Self::IdInfo) -> impl PinInit<Self, E
                 Coherent::zeroed_slice(pdev.as_ref(), TEST_VALUES.len(), GFP_KERNEL)?;
 
             for (i, value) in TEST_VALUES.into_iter().enumerate() {
-                kernel::dma_write!(ca, [i]?, MyStruct::new(value.0, value.1));
+                kernel::dma_write!(ca, [try: i], MyStruct::new(value.0, value.1));
             }
 
             let size = 4 * page::PAGE_SIZE;
@@ -91,16 +91,14 @@ fn probe(pdev: &pci::Device<Core>, _info: &Self::IdInfo) -> impl PinInit<Self, E
 }
 
 impl DmaSampleDriver {
-    fn check_dma(&self) -> Result {
+    fn check_dma(&self) {
         for (i, value) in TEST_VALUES.into_iter().enumerate() {
-            let val0 = kernel::dma_read!(self.ca, [i]?.h);
-            let val1 = kernel::dma_read!(self.ca, [i]?.b);
+            let val0 = kernel::dma_read!(self.ca, [panic: i].h);
+            let val1 = kernel::dma_read!(self.ca, [panic: i].b);
 
             assert_eq!(val0, value.0);
             assert_eq!(val1, value.1);
         }
-
-        Ok(())
     }
 }
 
@@ -109,7 +107,7 @@ impl PinnedDrop for DmaSampleDriver {
     fn drop(self: Pin<&mut Self>) {
         dev_info!(self.pdev, "Unload DMA test driver.\n");
 
-        assert!(self.check_dma().is_ok());
+        self.check_dma();
 
         for (i, entry) in self.sgt.iter().enumerate() {
             dev_info!(

-- 
2.51.2


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

* [PATCH 3/5] gpu: nova-core: convert to keyworded projection syntax
  2026-04-15 19:57 [PATCH 0/5] Rework index projection syntax Gary Guo
  2026-04-15 19:57 ` [PATCH 1/5] rust: ptr: add panicking index projection variant Gary Guo
  2026-04-15 19:57 ` [PATCH 2/5] rust: dma: update to keyworded index projection syntax Gary Guo
@ 2026-04-15 19:57 ` Gary Guo
  2026-04-16  7:08   ` Alice Ryhl
  2026-04-15 19:57 ` [PATCH 4/5] gpu: nova-core: use pointer projection for command queue code Gary Guo
  2026-04-15 19:57 ` [PATCH 5/5] rust: ptr: remove implicit index projection syntax Gary Guo
  4 siblings, 1 reply; 11+ messages in thread
From: Gary Guo @ 2026-04-15 19:57 UTC (permalink / raw)
  To: Danilo Krummrich, Abdiel Janulgue, Daniel Almeida, Robin Murphy,
	Andreas Hindborg, Miguel Ojeda, Boqun Feng, Björn Roy Baron,
	Benno Lossin, Alice Ryhl, Trevor Gross, Alexandre Courbot,
	David Airlie, Simona Vetter
  Cc: driver-core, rust-for-linux, linux-kernel, nouveau, dri-devel,
	Gary Guo

Use "build" to denote that the index bounds checking here is performed at
build time.

Signed-off-by: Gary Guo <gary@garyguo.net>
---
 drivers/gpu/nova-core/gsp/cmdq.rs | 6 +++---
 1 file changed, 3 insertions(+), 3 deletions(-)

diff --git a/drivers/gpu/nova-core/gsp/cmdq.rs b/drivers/gpu/nova-core/gsp/cmdq.rs
index 275da9b1ee0e..1c9b2085f5e4 100644
--- a/drivers/gpu/nova-core/gsp/cmdq.rs
+++ b/drivers/gpu/nova-core/gsp/cmdq.rs
@@ -237,7 +237,7 @@ fn new(dev: &device::Device<device::Bound>) -> Result<Self> {
         let start = gsp_mem.dma_handle();
         // Write values one by one to avoid an on-stack instance of `PteArray`.
         for i in 0..GspMem::PTE_ARRAY_SIZE {
-            dma_write!(gsp_mem, .ptes.0[i], PteArray::<0>::entry(start, i)?);
+            dma_write!(gsp_mem, .ptes.0[build: i], PteArray::<0>::entry(start, i)?);
         }
 
         dma_write!(
@@ -260,7 +260,7 @@ fn new(dev: &device::Device<device::Bound>) -> Result<Self> {
         let rx = self.gsp_read_ptr();
 
         // Pointer to the first entry of the CPU message queue.
-        let data = ptr::project!(mut self.0.as_mut_ptr(), .cpuq.msgq.data[0]);
+        let data = ptr::project!(mut self.0.as_mut_ptr(), .cpuq.msgq.data[build: 0]);
 
         let (tail_end, wrap_end) = if rx == 0 {
             // The write area is non-wrapping, and stops at the second-to-last entry of the command
@@ -322,7 +322,7 @@ fn driver_write_area_size(&self) -> usize {
         let rx = self.cpu_read_ptr();
 
         // Pointer to the first entry of the GSP message queue.
-        let data = ptr::project!(self.0.as_ptr(), .gspq.msgq.data[0]);
+        let data = ptr::project!(self.0.as_ptr(), .gspq.msgq.data[build: 0]);
 
         let (tail_end, wrap_end) = if rx <= tx {
             // Read area is non-wrapping and stops right before `tx`.

-- 
2.51.2


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

* [PATCH 4/5] gpu: nova-core: use pointer projection for command queue code
  2026-04-15 19:57 [PATCH 0/5] Rework index projection syntax Gary Guo
                   ` (2 preceding siblings ...)
  2026-04-15 19:57 ` [PATCH 3/5] gpu: nova-core: convert to keyworded " Gary Guo
@ 2026-04-15 19:57 ` Gary Guo
  2026-04-16  7:14   ` Alice Ryhl
  2026-04-15 19:57 ` [PATCH 5/5] rust: ptr: remove implicit index projection syntax Gary Guo
  4 siblings, 1 reply; 11+ messages in thread
From: Gary Guo @ 2026-04-15 19:57 UTC (permalink / raw)
  To: Danilo Krummrich, Abdiel Janulgue, Daniel Almeida, Robin Murphy,
	Andreas Hindborg, Miguel Ojeda, Boqun Feng, Björn Roy Baron,
	Benno Lossin, Alice Ryhl, Trevor Gross, Alexandre Courbot,
	David Airlie, Simona Vetter
  Cc: driver-core, rust-for-linux, linux-kernel, nouveau, dri-devel,
	Gary Guo

From: Alexandre Courbot <acourbot@nvidia.com>

This has been previously attempted but missing compiler optimisations for
older LLVM versions cause build errors. With the panicking index projection
variant implemented, replace the manual pointer arithmetic with projection.

Signed-off-by: Alexandre Courbot <acourbot@nvidia.com>
Co-developed-by: Gary Guo <gary@garyguo.net>
Signed-off-by: Gary Guo <gary@garyguo.net>
---
 drivers/gpu/nova-core/gsp/cmdq.rs | 106 +++++++++++++++++++++-----------------
 1 file changed, 60 insertions(+), 46 deletions(-)

diff --git a/drivers/gpu/nova-core/gsp/cmdq.rs b/drivers/gpu/nova-core/gsp/cmdq.rs
index 1c9b2085f5e4..0b51e10e2cfc 100644
--- a/drivers/gpu/nova-core/gsp/cmdq.rs
+++ b/drivers/gpu/nova-core/gsp/cmdq.rs
@@ -256,47 +256,60 @@ fn new(dev: &device::Device<device::Bound>) -> Result<Self> {
     /// As the message queue is a circular buffer, the region may be discontiguous in memory. In
     /// that case the second slice will have a non-zero length.
     fn driver_write_area(&mut self) -> (&mut [[u8; GSP_PAGE_SIZE]], &mut [[u8; GSP_PAGE_SIZE]]) {
-        let tx = self.cpu_write_ptr();
-        let rx = self.gsp_read_ptr();
+        let tx = num::u32_as_usize(self.cpu_write_ptr());
+        let rx = num::u32_as_usize(self.gsp_read_ptr());
 
         // Pointer to the first entry of the CPU message queue.
-        let data = ptr::project!(mut self.0.as_mut_ptr(), .cpuq.msgq.data[build: 0]);
+        let data = ptr::project!(mut self.0.as_mut_ptr(), .cpuq.msgq.data);
 
-        let (tail_end, wrap_end) = if rx == 0 {
+        let (tail_slice, wrap_slice) = if rx == 0 {
             // The write area is non-wrapping, and stops at the second-to-last entry of the command
             // queue (to leave the last one empty).
-            (MSGQ_NUM_PAGES - 1, 0)
-        } else if rx <= tx {
-            // The write area wraps and continues until `rx - 1`.
-            (MSGQ_NUM_PAGES, rx - 1)
+            // PANIC: per the invariant of `cpu_write_ptr`, `tx` is `< MSGQ_NUM_PAGES`.
+            (
+                ptr::project!(mut data, [panic: tx..num::u32_as_usize(MSGQ_NUM_PAGES) - 1]),
+                ptr::project!(mut data, [build: ..0]),
+            )
         } else {
-            // The write area doesn't wrap and stops at `rx - 1`.
-            (rx - 1, 0)
+            // Leave an empty slot before `rx`.
+            let end = rx - 1;
+
+            if rx <= tx {
+                // The area is discontiguous and we leave an empty slot before `rx`.
+                // The write area wraps and continues until `end`.
+                // PANIC:
+                // - per the invariant of `cpu_write_ptr`, `tx < MSGQ_NUM_PAGES`
+                // - The index `end` is non-negative because `rx != 0` in this branch.
+                (
+                    ptr::project!(mut data, [panic: tx..]),
+                    ptr::project!(mut data, [panic: ..end]),
+                )
+            } else {
+                // The write area doesn't wrap and stops at `end`.
+                // PANIC:
+                // - per the invariant of `cpu_write_ptr`, `tx < MSGQ_NUM_PAGES`
+                // - `end >= tx` because `rx > tx` in this branch.
+                (
+                    ptr::project!(mut data, [panic: tx..end]),
+                    ptr::project!(mut data, [build: ..0]),
+                )
+            }
         };
 
         // SAFETY:
-        // - `data` was created from a valid pointer, and `rx` and `tx` are in the
-        //   `0..MSGQ_NUM_PAGES` range per the invariants of `cpu_write_ptr` and `gsp_read_ptr`,
-        //   thus the created slices are valid.
+        // - Since `data` was created from a valid pointer, both `tail_slice` and `wrap_slice` are
+        //   pointers to valid arrays.
         // - The area starting at `tx` and ending at `rx - 2` modulo `MSGQ_NUM_PAGES`,
         //   inclusive, belongs to the driver for writing and is not accessed concurrently by
         //   the GSP.
         // - The caller holds a reference to `self` for as long as the returned slices are live,
         //   meaning the CPU write pointer cannot be advanced and thus that the returned area
         //   remains exclusive to the CPU for the duration of the slices.
-        // - The created slices point to non-overlapping sub-ranges of `data` in all
-        //   branches (in the `rx <= tx` case, the second slice ends at `rx - 1` which is strictly
-        //   less than `tx` where the first slice starts; in the other cases the second slice is
-        //   empty), so creating two `&mut` references from them does not violate aliasing rules.
-        unsafe {
-            (
-                core::slice::from_raw_parts_mut(
-                    data.add(num::u32_as_usize(tx)),
-                    num::u32_as_usize(tail_end - tx),
-                ),
-                core::slice::from_raw_parts_mut(data, num::u32_as_usize(wrap_end)),
-            )
-        }
+        // - `tail_slice` and `wrap_slice` point to non-overlapping sub-ranges of `data` in all
+        //   branches (in the `rx <= tx` case, `wrap_slice` ends at `rx - 1` which is strictly less
+        //   than `tx` where `tail_slice` starts; in the other cases `wrap_slice` is empty), so
+        //   creating two `&mut` references from them does not violate aliasing rules.
+        (unsafe { &mut *tail_slice }, unsafe { &mut *wrap_slice })
     }
 
     /// Returns the size of the region of the CPU message queue that the driver is currently allowed
@@ -318,39 +331,40 @@ fn driver_write_area_size(&self) -> usize {
     /// As the message queue is a circular buffer, the region may be discontiguous in memory. In
     /// that case the second slice will have a non-zero length.
     fn driver_read_area(&self) -> (&[[u8; GSP_PAGE_SIZE]], &[[u8; GSP_PAGE_SIZE]]) {
-        let tx = self.gsp_write_ptr();
-        let rx = self.cpu_read_ptr();
+        let tx = num::u32_as_usize(self.gsp_write_ptr());
+        let rx = num::u32_as_usize(self.cpu_read_ptr());
 
         // Pointer to the first entry of the GSP message queue.
-        let data = ptr::project!(self.0.as_ptr(), .gspq.msgq.data[build: 0]);
+        let data = ptr::project!(self.0.as_ptr(), .gspq.msgq.data);
 
-        let (tail_end, wrap_end) = if rx <= tx {
-            // Read area is non-wrapping and stops right before `tx`.
-            (tx, 0)
+        let (tail_slice, wrap_slice) = if rx <= tx {
+            // PANIC:
+            // - per branch condition, `rx <= tx`
+            // - per the invariant of `gsp_write_ptr`, `tx < MSGQ_NUM_PAGES`
+            (
+                ptr::project!(data, [panic: rx..tx]),
+                ptr::project!(data, [build: ..0]),
+            )
         } else {
-            // Read area is wrapping and stops right before `tx`.
-            (MSGQ_NUM_PAGES, tx)
+            // PANIC:
+            // - per the invariant of `cpu_read_ptr`, `rx < MSGQ_NUM_PAGES`
+            // - per the invariant of `gsp_write_ptr`, `tx < MSGQ_NUM_PAGES`
+            (
+                ptr::project!(data, [panic: rx..]),
+                ptr::project!(data, [panic: ..tx]),
+            )
         };
 
         // SAFETY:
-        // - `data` was created from a valid pointer, and `rx` and `tx` are in the
-        //   `0..MSGQ_NUM_PAGES` range per the invariants of `gsp_write_ptr` and `cpu_read_ptr`,
-        //   thus the created slices are valid.
+        // - Since `data` was created from a valid pointer, both `tail_slice` and `wrap_slice` are
+        //   pointers to valid arrays.
         // - The area starting at `rx` and ending at `tx - 1` modulo `MSGQ_NUM_PAGES`,
         //   inclusive, belongs to the driver for reading and is not accessed concurrently by
         //   the GSP.
         // - The caller holds a reference to `self` for as long as the returned slices are live,
         //   meaning the CPU read pointer cannot be advanced and thus that the returned area
         //   remains exclusive to the CPU for the duration of the slices.
-        unsafe {
-            (
-                core::slice::from_raw_parts(
-                    data.add(num::u32_as_usize(rx)),
-                    num::u32_as_usize(tail_end - rx),
-                ),
-                core::slice::from_raw_parts(data, num::u32_as_usize(wrap_end)),
-            )
-        }
+        (unsafe { &*tail_slice }, unsafe { &*wrap_slice })
     }
 
     /// Allocates a region on the command queue that is large enough to send a command of `size`

-- 
2.51.2


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

* [PATCH 5/5] rust: ptr: remove implicit index projection syntax
  2026-04-15 19:57 [PATCH 0/5] Rework index projection syntax Gary Guo
                   ` (3 preceding siblings ...)
  2026-04-15 19:57 ` [PATCH 4/5] gpu: nova-core: use pointer projection for command queue code Gary Guo
@ 2026-04-15 19:57 ` Gary Guo
  2026-04-16  7:14   ` Alice Ryhl
  4 siblings, 1 reply; 11+ messages in thread
From: Gary Guo @ 2026-04-15 19:57 UTC (permalink / raw)
  To: Danilo Krummrich, Abdiel Janulgue, Daniel Almeida, Robin Murphy,
	Andreas Hindborg, Miguel Ojeda, Boqun Feng, Björn Roy Baron,
	Benno Lossin, Alice Ryhl, Trevor Gross, Alexandre Courbot,
	David Airlie, Simona Vetter
  Cc: driver-core, rust-for-linux, linux-kernel, nouveau, dri-devel,
	Gary Guo

All users have been converted to use keyworded index projection syntax to
explicitly state their intention when doing index projection.

Signed-off-by: Gary Guo <gary@garyguo.net>
---
 rust/kernel/dma.rs            | 6 ------
 rust/kernel/ptr/projection.rs | 8 --------
 2 files changed, 14 deletions(-)

diff --git a/rust/kernel/dma.rs b/rust/kernel/dma.rs
index d6382904a90d..642ccff465c8 100644
--- a/rust/kernel/dma.rs
+++ b/rust/kernel/dma.rs
@@ -1210,12 +1210,6 @@ macro_rules! dma_write {
     (@parse [$dma:expr] [$($proj:tt)*] [[$flavor:ident: $index:expr] $($rest:tt)*]) => {
         $crate::dma_write!(@parse [$dma] [$($proj)* [$flavor: $index]] [$($rest)*])
     };
-    (@parse [$dma:expr] [$($proj:tt)*] [[$index:expr]? $($rest:tt)*]) => {
-        $crate::dma_write!(@parse [$dma] [$($proj)* [$index]?] [$($rest)*])
-    };
-    (@parse [$dma:expr] [$($proj:tt)*] [[$index:expr] $($rest:tt)*]) => {
-        $crate::dma_write!(@parse [$dma] [$($proj)* [$index]] [$($rest)*])
-    };
     ($dma:expr, $($rest:tt)*) => {
         $crate::dma_write!(@parse [$dma] [] [$($rest)*])
     };
diff --git a/rust/kernel/ptr/projection.rs b/rust/kernel/ptr/projection.rs
index 845811795393..fde565c836fc 100644
--- a/rust/kernel/ptr/projection.rs
+++ b/rust/kernel/ptr/projection.rs
@@ -346,14 +346,6 @@ macro_rules! project_pointer {
         $crate::ptr::project!(@gen $ptr, $($rest)*)
     };
 
-    // For compatibility
-    (@gen $ptr:ident, [$index:expr]? $($rest:tt)*) => {
-        $crate::ptr::project!(@gen $ptr, [try: $index] $($rest)*)
-    };
-    (@gen $ptr:ident, [$index:expr] $($rest:tt)*) => {
-        $crate::ptr::project!(@gen $ptr, [build: $index] $($rest)*)
-    };
-
     (mut $ptr:expr, $($proj:tt)*) => {{
         let ptr: *mut _ = $ptr;
         $crate::ptr::project!(@gen ptr, $($proj)*);

-- 
2.51.2


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

* Re: [PATCH 1/5] rust: ptr: add panicking index projection variant
  2026-04-15 19:57 ` [PATCH 1/5] rust: ptr: add panicking index projection variant Gary Guo
@ 2026-04-16  7:07   ` Alice Ryhl
  0 siblings, 0 replies; 11+ messages in thread
From: Alice Ryhl @ 2026-04-16  7:07 UTC (permalink / raw)
  To: Gary Guo
  Cc: Danilo Krummrich, Abdiel Janulgue, Daniel Almeida, Robin Murphy,
	Andreas Hindborg, Miguel Ojeda, Boqun Feng, Björn Roy Baron,
	Benno Lossin, Trevor Gross, Alexandre Courbot, David Airlie,
	Simona Vetter, driver-core, rust-for-linux, linux-kernel, nouveau,
	dri-devel

On Wed, Apr 15, 2026 at 08:57:12PM +0100, Gary Guo wrote:
> There have been a few cases where the programmer knows that the indices are
> in bounds but compiler cannot deduce that. This is also
> compiler-version-dependent, so using build indexing here can be
> problematic. On the other hand, it is also not ideal to use the fallible
> variant, as it adds error handling path that is never hit.
> 
> Add a new panicking index projection for this scenario. Like all panicking
> operations, this should be used carefully only in cases where the user
> knows the index is going to be in bounds, and panicking would indicate
> something is catastrophically wrong.
> 
> To signify this, require users to explicitly denote the type of index being
> used. The existing two types of index projections also gain the keyworded
> version, which will be the recommended way going forward.
> 
> The keyworded syntax also paves the way of perhaps adding more flavors in
> the future, e.g. `unsafe` index projection. However, unless the code is
> extremely performance sensitive and bounds checking cannot be tolerated,
> panicking variant is safer and should be preferred, so it will be left to
> future when demand arises.
> 
> Signed-off-by: Gary Guo <gary@garyguo.net>

>      /// 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 {
> +    fn build_index(self, slice: *mut T) -> *mut Self::Output {
>          Self::get(self, slice).unwrap_or_else(|| build_error!())
>      }

This is pre-existing issue but IMO this should use match instead of
unwrap_or_else() to avoid potential inlining issues.

> @@ -208,9 +251,12 @@ unsafe fn proj<F>(_: *mut Self, _: impl FnOnce(*mut Self) -> *mut F) -> *mut F {
>  /// `kernel::ptr::project!(mut ptr, projection)`. By default, a const pointer is created.
>  ///
>  /// `ptr::project!` macro can perform both fallible indexing and build-time 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 runtime bounds checking;
> -/// `OutOfBound` error is raised via `?` if the index is out of bounds.
> +/// The syntax is of form `[<flavor> index]` where `flavor` indicates the way of handling index
> +/// out-of-bound errors.

Missing colon.

Alice

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

* Re: [PATCH 2/5] rust: dma: update to keyworded index projection syntax
  2026-04-15 19:57 ` [PATCH 2/5] rust: dma: update to keyworded index projection syntax Gary Guo
@ 2026-04-16  7:07   ` Alice Ryhl
  0 siblings, 0 replies; 11+ messages in thread
From: Alice Ryhl @ 2026-04-16  7:07 UTC (permalink / raw)
  To: Gary Guo
  Cc: Danilo Krummrich, Abdiel Janulgue, Daniel Almeida, Robin Murphy,
	Andreas Hindborg, Miguel Ojeda, Boqun Feng, Björn Roy Baron,
	Benno Lossin, Trevor Gross, Alexandre Courbot, David Airlie,
	Simona Vetter, driver-core, rust-for-linux, linux-kernel, nouveau,
	dri-devel

On Wed, Apr 15, 2026 at 08:57:13PM +0100, Gary Guo wrote:
> Demonstrate the preferred syntax of index projection in DMA documentation
> and examples. A few `[i]?` cases are converted to demonstrate the new
> variant.
> 
> Signed-off-by: Gary Guo <gary@garyguo.net>

Reviewed-by: Alice Ryhl <aliceryhl@google.com>


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

* Re: [PATCH 3/5] gpu: nova-core: convert to keyworded projection syntax
  2026-04-15 19:57 ` [PATCH 3/5] gpu: nova-core: convert to keyworded " Gary Guo
@ 2026-04-16  7:08   ` Alice Ryhl
  0 siblings, 0 replies; 11+ messages in thread
From: Alice Ryhl @ 2026-04-16  7:08 UTC (permalink / raw)
  To: Gary Guo
  Cc: Danilo Krummrich, Abdiel Janulgue, Daniel Almeida, Robin Murphy,
	Andreas Hindborg, Miguel Ojeda, Boqun Feng, Björn Roy Baron,
	Benno Lossin, Trevor Gross, Alexandre Courbot, David Airlie,
	Simona Vetter, driver-core, rust-for-linux, linux-kernel, nouveau,
	dri-devel

On Wed, Apr 15, 2026 at 08:57:14PM +0100, Gary Guo wrote:
> Use "build" to denote that the index bounds checking here is performed at
> build time.
> 
> Signed-off-by: Gary Guo <gary@garyguo.net>

Reviewed-by: Alice Ryhl <aliceryhl@google.com>


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

* Re: [PATCH 4/5] gpu: nova-core: use pointer projection for command queue code
  2026-04-15 19:57 ` [PATCH 4/5] gpu: nova-core: use pointer projection for command queue code Gary Guo
@ 2026-04-16  7:14   ` Alice Ryhl
  0 siblings, 0 replies; 11+ messages in thread
From: Alice Ryhl @ 2026-04-16  7:14 UTC (permalink / raw)
  To: Gary Guo
  Cc: Danilo Krummrich, Abdiel Janulgue, Daniel Almeida, Robin Murphy,
	Andreas Hindborg, Miguel Ojeda, Boqun Feng, Björn Roy Baron,
	Benno Lossin, Trevor Gross, Alexandre Courbot, David Airlie,
	Simona Vetter, driver-core, rust-for-linux, linux-kernel, nouveau,
	dri-devel

On Wed, Apr 15, 2026 at 08:57:15PM +0100, Gary Guo wrote:
> From: Alexandre Courbot <acourbot@nvidia.com>
> 
> This has been previously attempted but missing compiler optimisations for
> older LLVM versions cause build errors. With the panicking index projection
> variant implemented, replace the manual pointer arithmetic with projection.
> 
> Signed-off-by: Alexandre Courbot <acourbot@nvidia.com>
> Co-developed-by: Gary Guo <gary@garyguo.net>
> Signed-off-by: Gary Guo <gary@garyguo.net>

Reviewed-by: Alice Ryhl <aliceryhl@google.com>


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

* Re: [PATCH 5/5] rust: ptr: remove implicit index projection syntax
  2026-04-15 19:57 ` [PATCH 5/5] rust: ptr: remove implicit index projection syntax Gary Guo
@ 2026-04-16  7:14   ` Alice Ryhl
  0 siblings, 0 replies; 11+ messages in thread
From: Alice Ryhl @ 2026-04-16  7:14 UTC (permalink / raw)
  To: Gary Guo
  Cc: Danilo Krummrich, Abdiel Janulgue, Daniel Almeida, Robin Murphy,
	Andreas Hindborg, Miguel Ojeda, Boqun Feng, Björn Roy Baron,
	Benno Lossin, Trevor Gross, Alexandre Courbot, David Airlie,
	Simona Vetter, driver-core, rust-for-linux, linux-kernel, nouveau,
	dri-devel

On Wed, Apr 15, 2026 at 08:57:16PM +0100, Gary Guo wrote:
> All users have been converted to use keyworded index projection syntax to
> explicitly state their intention when doing index projection.
> 
> Signed-off-by: Gary Guo <gary@garyguo.net>

Reviewed-by: Alice Ryhl <aliceryhl@google.com>

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

end of thread, other threads:[~2026-04-16  7:14 UTC | newest]

Thread overview: 11+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2026-04-15 19:57 [PATCH 0/5] Rework index projection syntax Gary Guo
2026-04-15 19:57 ` [PATCH 1/5] rust: ptr: add panicking index projection variant Gary Guo
2026-04-16  7:07   ` Alice Ryhl
2026-04-15 19:57 ` [PATCH 2/5] rust: dma: update to keyworded index projection syntax Gary Guo
2026-04-16  7:07   ` Alice Ryhl
2026-04-15 19:57 ` [PATCH 3/5] gpu: nova-core: convert to keyworded " Gary Guo
2026-04-16  7:08   ` Alice Ryhl
2026-04-15 19:57 ` [PATCH 4/5] gpu: nova-core: use pointer projection for command queue code Gary Guo
2026-04-16  7:14   ` Alice Ryhl
2026-04-15 19:57 ` [PATCH 5/5] rust: ptr: remove implicit index projection syntax Gary Guo
2026-04-16  7:14   ` Alice Ryhl

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