mirror of https://lore.kernel.org/lkml/
 help / color / mirror / Atom feed
* [PATCH v3] rust: io: convert ResourceSize into a transparent newtype
@ 2026-09-20 12:13 Lorenzo Delgado
  2026-09-24 17:21 ` Danilo Krummrich
  0 siblings, 1 reply; 2+ messages in thread
From: Lorenzo Delgado @ 2026-09-20 12:13 UTC (permalink / raw)
  To: Daniel Almeida, Alice Ryhl, Danilo Krummrich, David Airlie,
	Simona Vetter, Alexandre Courbot, Miguel Ojeda, Boqun Feng,
	Gary Guo, Björn Roy Baron, Benno Lossin, Andreas Hindborg,
	Trevor Gross, Tamir Duberstein, Onur Özkan, Abdiel Janulgue,
	Robin Murphy
  Cc: dri-devel, linux-kernel, nova-gpu, driver-core, rust-for-linux

`ResourceSize` is a bare type alias for `resource_size_t`, so it
inherits every integer operation and `as` cast. That allows operations
that make no sense for the size of a hardware resource, such as mixing
it with unrelated integers or truncating it with a cast.

Wrap it in a `#[repr(transparent)]` newtype so each conversion at a
boundary is explicit. The representation is unchanged, so this is
ABI-identical; only the spelling at the FFI boundary changes. Provide
`from_raw`/`into_raw`, `From` in both directions, and a fallible
`TryFrom<ResourceSize> for usize`. On 64-bit, where `resource_size_t`
is a `u64`, also implement `FromSafeCastArch<ResourceSize> for usize`,
so code that is specific to 64-bit can keep the conversion infallible.

Update the producers, `Resource::size` and `SGEntry::dma_len`, and the
`Region` and `request_region` consumers. nova-core only builds on
64-bit, so its page count in gsp.rs uses `from_safe_cast_arch()`. tyr
widens `dma_len()` to `u64`; go through `into_raw()` so that still
builds on 32-bit ARM, where `resource_size_t` is a `u32`.

Suggested-by: Miguel Ojeda <ojeda@kernel.org>
Link: https://github.com/Rust-for-Linux/linux/issues/1203
Signed-off-by: Lorenzo Delgado <lnsdev@proton.me>
---
Changes in v3:
- Rebase on rust-next (v7.3-rc4).
- Implement FromSafeCastArch<ResourceSize> for usize in the kernel
  crate, next to the type, now that the lossless casts live in
  kernel::num::casts. Drop the impl from nova-core's num.rs, and switch
  gsp.rs to usize::from_safe_cast_arch(), the same line the nova-core
  conversion to kernel::num uses.
- Convert the dma_len() user in tyr, added since v2. Without it, tyr
  fails to build on 32-bit ARM without LPAE.
- Link to v2: https://lore.kernel.org/r/20260719200945.687904-1-lnsdev@proton.me
- Link to v1: https://lore.kernel.org/r/20260712113602.389060-1-lnsdev@proton.me
---
 drivers/gpu/drm/tyr/vm.rs             |  2 +-
 drivers/gpu/nova-core/firmware/gsp.rs |  3 +-
 rust/kernel/io.rs                     | 83 ++++++++++++++++++++++++++++++++---
 rust/kernel/io/resource.rs            |  6 +--
 rust/kernel/scatterlist.rs            |  2 +-
 5 files changed, 85 insertions(+), 11 deletions(-)

diff --git a/drivers/gpu/drm/tyr/vm.rs b/drivers/gpu/drm/tyr/vm.rs
index c5e307b1e241..c32d0259af5b 100644
--- a/drivers/gpu/drm/tyr/vm.rs
+++ b/drivers/gpu/drm/tyr/vm.rs
@@ -645,7 +645,7 @@ fn sm_step_map<'op>(
             #[allow(clippy::useless_conversion)]
             let mut paddr = u64::from(sgt_entry.dma_address());
             #[allow(clippy::useless_conversion)]
-            let mut sgt_entry_length = u64::from(sgt_entry.dma_len());
+            let mut sgt_entry_length = u64::from(sgt_entry.dma_len().into_raw());
 
             if bytes_left_to_map == 0 {
                 break;
diff --git a/drivers/gpu/nova-core/firmware/gsp.rs b/drivers/gpu/nova-core/firmware/gsp.rs
index e8f9491e84cc..956f76611cb0 100644
--- a/drivers/gpu/nova-core/firmware/gsp.rs
+++ b/drivers/gpu/nova-core/firmware/gsp.rs
@@ -9,6 +9,7 @@
         DmaAddress, //
     },
     firmware,
+    num::casts::arch::FromSafeCastArch,
     prelude::*,
     scatterlist::{
         Owned,
@@ -154,7 +155,7 @@ pub(crate) fn radix3_dma_address(&self) -> DmaAddress {
 fn map_into_lvl(sg_table: &SGTable<Owned<VVec<u8>>>, mut dst: VVec<u8>) -> Result<VVec<u8>> {
     for sg_entry in sg_table.iter() {
         // Number of pages we need to map.
-        let num_pages = usize::from_safe_cast(sg_entry.dma_len()).div_ceil(GSP_PAGE_SIZE);
+        let num_pages = usize::from_safe_cast_arch(sg_entry.dma_len()).div_ceil(GSP_PAGE_SIZE);
 
         for i in 0..num_pages {
             let entry = sg_entry.dma_address()
diff --git a/rust/kernel/io.rs b/rust/kernel/io.rs
index 5ce9fd129068..3cc64983be82 100644
--- a/rust/kernel/io.rs
+++ b/rust/kernel/io.rs
@@ -6,11 +6,13 @@
 
 use core::{
     marker::PhantomData,
-    mem::MaybeUninit, //
+    mem::MaybeUninit,
+    num::TryFromIntError, //
 };
 
 use crate::{
     bindings,
+    fmt,
     prelude::*,
     ptr::{
         Alignment,
@@ -35,11 +37,82 @@
 /// `CONFIG_PHYS_ADDR_T_64BIT`, and it can be a u64 even on 32-bit architectures.
 pub type PhysAddr = bindings::phys_addr_t;
 
-/// Resource Size type.
+/// Resource size type.
 ///
-/// This is a type alias to either `u32` or `u64` depending on the config option
-/// `CONFIG_PHYS_ADDR_T_64BIT`, and it can be a u64 even on 32-bit architectures.
-pub type ResourceSize = bindings::resource_size_t;
+/// This wraps either `u32` or `u64` depending on the config option
+/// `CONFIG_PHYS_ADDR_T_64BIT`, and it can be a `u64` even on 32-bit architectures.
+///
+/// # Examples
+///
+/// ```
+/// use kernel::io::ResourceSize;
+///
+/// let size = ResourceSize::from_raw(0x1000);
+/// assert_eq!(size.into_raw(), 0x1000);
+///
+/// // Round-trips through the raw C type.
+/// let raw: kernel::bindings::resource_size_t = size.into();
+/// assert_eq!(ResourceSize::from(raw), size);
+///
+/// // Fallible conversion to `usize` (can truncate on 32-bit).
+/// assert_eq!(usize::try_from(size)?, 0x1000);
+/// # Ok::<(), core::num::TryFromIntError>(())
+/// ```
+#[repr(transparent)]
+#[derive(Clone, Copy, PartialEq, Eq, PartialOrd, Ord)]
+pub struct ResourceSize(bindings::resource_size_t);
+
+impl ResourceSize {
+    /// Creates a resource size from the raw C type.
+    #[inline]
+    pub const fn from_raw(value: bindings::resource_size_t) -> Self {
+        Self(value)
+    }
+
+    /// Turns this resource size into the raw C type.
+    #[inline]
+    pub const fn into_raw(self) -> bindings::resource_size_t {
+        self.0
+    }
+}
+
+impl fmt::Debug for ResourceSize {
+    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
+        write!(f, "{:#x}", self.0)
+    }
+}
+
+impl From<bindings::resource_size_t> for ResourceSize {
+    #[inline]
+    fn from(value: bindings::resource_size_t) -> Self {
+        Self::from_raw(value)
+    }
+}
+
+impl From<ResourceSize> for bindings::resource_size_t {
+    #[inline]
+    fn from(value: ResourceSize) -> Self {
+        value.into_raw()
+    }
+}
+
+impl TryFrom<ResourceSize> for usize {
+    type Error = TryFromIntError;
+
+    #[inline]
+    fn try_from(value: ResourceSize) -> Result<Self, Self::Error> {
+        Self::try_from(value.into_raw())
+    }
+}
+
+// A `resource_size_t` is a `u64` on 64-bit platforms, so it fits into a `usize` there.
+#[cfg(CONFIG_64BIT)]
+impl crate::num::casts::arch::FromSafeCastArch<ResourceSize> for usize {
+    #[inline]
+    fn from_safe_cast_arch(value: ResourceSize) -> Self {
+        crate::num::casts::arch::u64_as_usize(value.into_raw())
+    }
+}
 
 /// Untyped I/O region.
 ///
diff --git a/rust/kernel/io/resource.rs b/rust/kernel/io/resource.rs
index 17b0c174cfc5..a33a416b289a 100644
--- a/rust/kernel/io/resource.rs
+++ b/rust/kernel/io/resource.rs
@@ -58,7 +58,7 @@ fn drop(&mut self) {
         };
 
         // SAFETY: Safe as per the invariant of `Region`.
-        unsafe { release_fn(start, size) };
+        unsafe { release_fn(start, size.into_raw()) };
     }
 }
 
@@ -114,7 +114,7 @@ pub fn request_region(
             bindings::__request_region(
                 self.0.get(),
                 start,
-                size,
+                size.into_raw(),
                 name.as_char_ptr(),
                 flags.0 as c_int,
             )
@@ -130,7 +130,7 @@ pub fn request_region(
     pub fn size(&self) -> ResourceSize {
         let inner = self.0.get();
         // SAFETY: Safe as per the invariants of `Resource`.
-        unsafe { bindings::resource_size(inner) }
+        ResourceSize::from_raw(unsafe { bindings::resource_size(inner) })
     }
 
     /// Returns the start address of the resource.
diff --git a/rust/kernel/scatterlist.rs b/rust/kernel/scatterlist.rs
index b83c468b5c63..5d67242befd8 100644
--- a/rust/kernel/scatterlist.rs
+++ b/rust/kernel/scatterlist.rs
@@ -93,7 +93,7 @@ pub fn dma_address(&self) -> dma::DmaAddress {
     pub fn dma_len(&self) -> ResourceSize {
         #[allow(clippy::useless_conversion)]
         // SAFETY: `self.as_raw()` is a valid pointer to a `struct scatterlist`.
-        unsafe { bindings::sg_dma_len(self.as_raw()) }.into()
+        ResourceSize::from_raw(unsafe { bindings::sg_dma_len(self.as_raw()) }.into())
     }
 }
 

---
base-commit: 40288c9206c17eb66a603262e06a58d300d0f279
change-id: 20260920-resource-size-newtype-63a82d4f2cdb

Best regards,
--  
Lorenzo Delgado <lnsdev@proton.me>



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

* Re: [PATCH v3] rust: io: convert ResourceSize into a transparent newtype
  2026-09-20 12:13 [PATCH v3] rust: io: convert ResourceSize into a transparent newtype Lorenzo Delgado
@ 2026-09-24 17:21 ` Danilo Krummrich
  0 siblings, 0 replies; 2+ messages in thread
From: Danilo Krummrich @ 2026-09-24 17:21 UTC (permalink / raw)
  To: Lorenzo Delgado
  Cc: Daniel Almeida, Alice Ryhl, David Airlie, Simona Vetter,
	Alexandre Courbot, Miguel Ojeda, Boqun Feng, Gary Guo,
	Björn Roy Baron, Benno Lossin, Andreas Hindborg,
	Trevor Gross, Tamir Duberstein, Onur Özkan, Abdiel Janulgue,
	Robin Murphy, dri-devel, linux-kernel, nova-gpu, driver-core,
	rust-for-linux

On Sun Sep 20, 2026 at 2:13 PM CEST, Lorenzo Delgado wrote:
> `ResourceSize` is a bare type alias for `resource_size_t`, so it
> inherits every integer operation and `as` cast. That allows operations
> that make no sense for the size of a hardware resource, such as mixing
> it with unrelated integers or truncating it with a cast.
>
> Wrap it in a `#[repr(transparent)]` newtype so each conversion at a
> boundary is explicit. The representation is unchanged, so this is
> ABI-identical; only the spelling at the FFI boundary changes. Provide
> `from_raw`/`into_raw`, `From` in both directions, and a fallible
> `TryFrom<ResourceSize> for usize`. On 64-bit, where `resource_size_t`
> is a `u64`, also implement `FromSafeCastArch<ResourceSize> for usize`,
> so code that is specific to 64-bit can keep the conversion infallible.

This looks good now, but it makes me notice that dma_len() shouldn't return
ResourceSize in the first place.

The length of a single SG segment is bounded by max_segment_size, which is also
unsigned int.

So, I think we should change dma_len() to just return u32 (I'm also fine with a
new type, but it might be slighly overkill).

I'd usually pick this patch regardless and leave that for a follow-up, as it is
a separate issue. But if we change dma_len() first, we only have to update tyr
once, whereas if change it after we end up touching nova and tyr twice.

Thanks,
Danilo

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

end of thread, other threads:[~2026-09-24 17:21 UTC | newest]

Thread overview: 2+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2026-09-20 12:13 [PATCH v3] rust: io: convert ResourceSize into a transparent newtype Lorenzo Delgado
2026-09-24 17:21 ` Danilo Krummrich

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

all inboxes | Powered by JetHome®