Rust for Linux List
 help / color / mirror / Atom feed
From: Daniel Almeida <daniel.almeida@collabora.com>
To: Gary Guo <gary@garyguo.net>
Cc: "Alice Ryhl" <aliceryhl@google.com>,
	"Greg Kroah-Hartman" <gregkh@linuxfoundation.org>,
	"Rafael J. Wysocki" <rafael@kernel.org>,
	"Miguel Ojeda" <ojeda@kernel.org>,
	"Boqun Feng" <boqun@kernel.org>,
	"Björn Roy Baron" <bjorn3_gh@protonmail.com>,
	"Benno Lossin" <lossin@kernel.org>,
	"Andreas Hindborg" <a.hindborg@kernel.org>,
	"Trevor Gross" <tmgross@umich.edu>,
	"Tamir Duberstein" <tamird@kernel.org>,
	"Onur Özkan" <work@onurozkan.dev>,
	"Bjorn Helgaas" <bhelgaas@google.com>,
	"Krzysztof Wilczyński" <kwilczynski@kernel.org>,
	"Abdiel Janulgue" <abdiel.janulgue@gmail.com>,
	"Robin Murphy" <robin.murphy@arm.com>,
	"Alexandre Courbot" <acourbot@nvidia.com>,
	"David Airlie" <airlied@gmail.com>,
	"Simona Vetter" <simona@ffwll.ch>,
	"Michal Wilczynski" <m.wilczynski@samsung.com>,
	"Uwe Kleine-König" <ukleinek@kernel.org>,
	"Danilo Krummrich" <dakr@kernel.org>,
	driver-core@lists.linux.dev, rust-for-linux@vger.kernel.org,
	linux-kernel@vger.kernel.org, linux-pci@vger.kernel.org,
	nova-gpu@lists.linux.dev, dri-devel@lists.freedesktop.org,
	linux-pwm@vger.kernel.org
Subject: Re: [PATCH v6 09/20] rust: io: use view types instead of addresses for `Io`
Date: Wed, 8 Jul 2026 11:36:12 -0300	[thread overview]
Message-ID: <280FBE45-5653-499B-9BB0-A2C77030A826@collabora.com> (raw)
In-Reply-To: <20260706-io_projection-v6-9-72cd5d055d54@garyguo.net>



> On 6 Jul 2026, at 09:44, Gary Guo <gary@garyguo.net> wrote:
> 
> Currently, `io_read` and `io_write` methods require the exact type of `Io`
> plus an address. This means that they need to be monomorphized for each
> different `Io` instance. This also means that multiple I/O implementors for
> the same I/O kind needs to duplicate implementation (e.g. `Mmio` and
> `MmioOwned`).
> 
> Create a new `IoBackend` trait and define these operations on it instead.
> The operations are just going to receive a view type and operate on them.
> This has the additional advantage that the invariants can be moved from the
> trait (and guaranteed via `unsafe`) to type invariants on the canonical
> view types of the backends, so `io_read` and `io_write` can be safe.
> 
> Note that view type is needed; addresses are insufficient in this

nit: missing article: “a view type is needed” or “view types are needed”.

> design, as they do not carry sufficient information. For example,
> `ConfigSpace` needs `&pci::Device` in addition to the address.
> 
> `io_addr_assert` and `io_addr` are renamed to `io_view*` to reflect
> that they operate on views now, and make them standalone functions so
> they cannot be used by users to cast types outside io.rs.
> 
> Reviewed-by: Alexandre Courbot <acourbot@nvidia.com>
> Signed-off-by: Gary Guo <gary@garyguo.net>
> ---
> rust/kernel/io.rs     | 382 ++++++++++++++++++++++++++------------------------
> rust/kernel/pci/io.rs |  70 +++++----
> 2 files changed, 246 insertions(+), 206 deletions(-)
> 
> diff --git a/rust/kernel/io.rs b/rust/kernel/io.rs
> index 8110b49aa430..89039b3defd5 100644
> --- a/rust/kernel/io.rs
> +++ b/rust/kernel/io.rs
> @@ -243,6 +243,81 @@ const fn offset_valid<U>(base: usize, offset: usize, size: usize) -> bool {
>     }
> }
> 
> +/// Returns a view for a given `offset`, performing compile-time bound checks.
> +// Always inline to optimize out error path of `build_assert`.

You mean to avoid that cryptic “rust_build_error” when the assertion fails?

> +#[inline(always)]
> +fn io_view_assert<'a, IO: Io<'a>, U>(
> +    this: IO,
> +    offset: usize,
> +) -> <IO::Backend as IoBackend>::View<'a, U> {
> +    // We cannot check alignment with `offset_valid` using `ptr.addr()`. So set 0 for it and
> +    // ensure alignment by checking that the alignment of `U` is smaller or equal to the
> +    // alignment of `IO::Target`.
> +    const_assert!(Alignment::of::<U>().as_usize() <= IO::Target::MIN_ALIGN.as_usize());
> +    build_assert!(offset_valid::<U>(0, offset, IO::Target::MIN_SIZE));
> +
> +    let view = this.as_view();
> +    let ptr = IO::Backend::as_ptr(view);
> +    let projected_ptr = ptr.cast::<U>().wrapping_byte_add(offset);
> +    // SAFETY: `offset_valid` checks for size and alignment and therefore `projected_ptr` is a
> +    // valid projection.
> +    unsafe { IO::Backend::project_view(view, projected_ptr) }
> +}
> +
> +/// Returns a view for a given `offset`, performing runtime bound checks.
> +#[inline]
> +fn io_view<'a, IO: Io<'a>, U>(
> +    this: IO,
> +    offset: usize,
> +) -> Result<<IO::Backend as IoBackend>::View<'a, U>> {
> +    let view = this.as_view();
> +    let ptr = IO::Backend::as_ptr(view);
> +
> +    if !offset_valid::<U>(ptr.addr(), offset, KnownSize::size(ptr)) {
> +        return Err(EINVAL);
> +    }
> +
> +    let projected_ptr = ptr.cast::<U>().wrapping_byte_add(offset);
> +    // SAFETY: `offset_valid` checks for size and alignment and therefore `projected_ptr` is a
> +    // valid projection.
> +    Ok(unsafe { IO::Backend::project_view(view, projected_ptr) })
> +}
> +
> +/// I/O backends.
> +///
> +/// This is an abstract representation to be implemented by arbitrary I/O
> +/// backends (e.g. MMIO, PCI config space, etc.).
> +///
> +/// The base trait only defines the projection operations; which I/O methods are available depends
> +/// on which [`IoCapable<T>`] traits are implemented for the type. For example, for MMIO regions,
> +/// all widths (u8, u16, u32, and u64 on 64-bit systems) are typically supported. For PCI
> +/// configuration space, u8, u16, and u32 are supported but u64 is not.
> +///
> +/// This trait is separate from the `Io` trait as multiple different I/O types may share the same
> +/// operation.
> +pub trait IoBackend {
> +    /// View type for this I/O backend.
> +    type View<'a, T: ?Sized + KnownSize>: Io<'a, Backend = Self, Target = T>;
> +
> +    /// Convert a `view` to a raw pointer for projection.
> +    ///
> +    /// The returned pointer is private implementation detail of the backend; it is likely not
> +    /// valid. It should not be dereferenced.
> +    fn as_ptr<'a, T: ?Sized + KnownSize>(view: Self::View<'a, T>) -> *mut T;
> +
> +    /// Project `view` to its subregion indicated by `ptr`.
> +    ///
> +    /// If input `view` is valid, returned view must also be valid.
> +    ///
> +    /// # Safety
> +    ///
> +    /// `ptr` must be a projection of `Self::as_ptr(view)`.
> +    unsafe fn project_view<'a, T: ?Sized + KnownSize, U: ?Sized + KnownSize>(
> +        view: Self::View<'a, T>,
> +        ptr: *mut U,
> +    ) -> Self::View<'a, U>;
> +}
> +
> /// Trait indicating that an I/O backend supports operations of a certain type and providing an
> /// implementation for these operations.
> ///
> @@ -251,22 +326,12 @@ const fn offset_valid<U>(base: usize, offset: usize, size: usize) -> bool {
> /// For example, a PCI configuration space may implement `IoCapable<u8>`, `IoCapable<u16>`,
> /// and `IoCapable<u32>`, but not `IoCapable<u64>`, while an MMIO region on a 64-bit
> /// system might implement all four.
> -pub trait IoCapable<T> {
> -    /// Performs an I/O read of type `T` at `address` and returns the result.
> -    ///
> -    /// # Safety
> -    ///
> -    /// - The range `[address..address + size_of::<T>()]` must be within the bounds of `Self`.
> -    /// - `address` must be aligned.
> -    unsafe fn io_read(self, address: usize) -> T;
> +pub trait IoCapable<T>: IoBackend {
> +    /// Performs an I/O read of type `T` at `view` and returns the result.
> +    fn io_read<'a>(view: Self::View<'a, T>) -> T;
> 
> -    /// Performs an I/O write of `value` at `address`.
> -    ///
> -    /// # Safety
> -    ///
> -    /// - The range `[address..address + size_of::<T>()]` must be within the bounds of `Self`.
> -    /// - `address` must be aligned.
> -    unsafe fn io_write(self, value: T, address: usize);
> +    /// Performs an I/O write of `value` at `view`.
> +    fn io_write<'a>(view: Self::View<'a, T>, value: T);
> }
> 
> /// Describes a given I/O location: its offset, width, and type to convert the raw value from and
> @@ -318,66 +383,30 @@ fn offset(self) -> usize {
> /// Types implementing this trait (e.g. MMIO BARs or PCI config regions)
> /// can perform I/O operations on regions of memory.
> ///
> -/// This is an abstract representation to be implemented by arbitrary I/O
> -/// backends (e.g. MMIO, PCI config space, etc.).
> -///
> /// The [`Io`] trait provides:
> -/// - Base address and size information
> +/// - Method to convert into [`IoBackend::View`].
> /// - Helper methods for offset validation and address calculation
> /// - Fallible (runtime checked) accessors for different data widths
> ///
> -/// Which I/O methods are available depends on which [`IoCapable<T>`] traits
> -/// are implemented for the type.
> +/// Which I/O methods are available depends on the associated [`IoBackend`] implementation.
> ///
> /// This should be implemented on cheaply copyable handles, such as references or view types.
> -///
> -/// # Examples
> -///
> -/// For MMIO regions, all widths (u8, u16, u32, and u64 on 64-bit systems) are typically
> -/// supported. For PCI configuration space, u8, u16, and u32 are supported but u64 is not.
> -pub trait Io: Copy {
> +pub trait Io<'a>: Copy {
> +    /// Type that defines all I/O operations.
> +    type Backend: IoBackend;
> +
>     /// Type of this I/O region. For untyped regions, [`Region`] can be used.
>     type Target: ?Sized + KnownSize;
> 
> -    /// Returns the base address of this mapping.
> -    fn addr(self) -> usize;
> -
> -    /// Returns the maximum size of this mapping.
> -    fn maxsize(self) -> usize;
> -
> -    /// Returns the absolute I/O address for a given `offset`,
> -    /// performing compile-time bound checks.
> -    // Always inline to optimize out error path of `build_assert`.
> -    #[inline(always)]
> -    fn io_addr_assert<U>(self, offset: usize) -> usize {
> -        // We cannot check alignment with `offset_valid` using `self.addr()`. So set 0 for it and
> -        // ensure alignment by checking that the alignment of `U` is smaller or equal to the
> -        // alignment of `Self::Target`.
> -        const_assert!(Alignment::of::<U>().as_usize() <= Self::Target::MIN_ALIGN.as_usize());
> -        build_assert!(offset_valid::<U>(0, offset, Self::Target::MIN_SIZE));
> -
> -        self.addr() + offset
> -    }
> -
> -    /// Returns the absolute I/O address for a given `offset`,
> -    /// performing runtime bound checks.
> -    #[inline]
> -    fn io_addr<U>(self, offset: usize) -> Result<usize> {
> -        if !offset_valid::<U>(self.addr(), offset, self.maxsize()) {
> -            return Err(EINVAL);
> -        }
> -
> -        // Probably no need to check, since the safety requirements of `Self::new` guarantee that
> -        // this can't overflow.
> -        self.addr().checked_add(offset).ok_or(EINVAL)
> -    }
> +    /// Return a view that covers the full region.
> +    fn as_view(self) -> <Self::Backend as IoBackend>::View<'a, Self::Target>;
> 
>     /// Fallible 8-bit read with runtime bounds check.
>     #[inline(always)]
>     fn try_read8(self, offset: usize) -> Result<u8>
>     where
>         usize: IoLoc<Self::Target, u8, IoType = u8>,
> -        Self: IoCapable<u8>,
> +        Self::Backend: IoCapable<u8>,
>     {
>         self.try_read(offset)
>     }
> @@ -387,7 +416,7 @@ fn try_read8(self, offset: usize) -> Result<u8>
>     fn try_read16(self, offset: usize) -> Result<u16>
>     where
>         usize: IoLoc<Self::Target, u16, IoType = u16>,
> -        Self: IoCapable<u16>,
> +        Self::Backend: IoCapable<u16>,
>     {
>         self.try_read(offset)
>     }
> @@ -397,7 +426,7 @@ fn try_read16(self, offset: usize) -> Result<u16>
>     fn try_read32(self, offset: usize) -> Result<u32>
>     where
>         usize: IoLoc<Self::Target, u32, IoType = u32>,
> -        Self: IoCapable<u32>,
> +        Self::Backend: IoCapable<u32>,
>     {
>         self.try_read(offset)
>     }
> @@ -407,7 +436,7 @@ fn try_read32(self, offset: usize) -> Result<u32>
>     fn try_read64(self, offset: usize) -> Result<u64>
>     where
>         usize: IoLoc<Self::Target, u64, IoType = u64>,
> -        Self: IoCapable<u64>,
> +        Self::Backend: IoCapable<u64>,
>     {
>         self.try_read(offset)
>     }
> @@ -417,7 +446,7 @@ fn try_read64(self, offset: usize) -> Result<u64>
>     fn try_write8(self, value: u8, offset: usize) -> Result
>     where
>         usize: IoLoc<Self::Target, u8, IoType = u8>,
> -        Self: IoCapable<u8>,
> +        Self::Backend: IoCapable<u8>,
>     {
>         self.try_write(offset, value)
>     }
> @@ -427,7 +456,7 @@ fn try_write8(self, value: u8, offset: usize) -> Result
>     fn try_write16(self, value: u16, offset: usize) -> Result
>     where
>         usize: IoLoc<Self::Target, u16, IoType = u16>,
> -        Self: IoCapable<u16>,
> +        Self::Backend: IoCapable<u16>,
>     {
>         self.try_write(offset, value)
>     }
> @@ -437,7 +466,7 @@ fn try_write16(self, value: u16, offset: usize) -> Result
>     fn try_write32(self, value: u32, offset: usize) -> Result
>     where
>         usize: IoLoc<Self::Target, u32, IoType = u32>,
> -        Self: IoCapable<u32>,
> +        Self::Backend: IoCapable<u32>,
>     {
>         self.try_write(offset, value)
>     }
> @@ -447,7 +476,7 @@ fn try_write32(self, value: u32, offset: usize) -> Result
>     fn try_write64(self, value: u64, offset: usize) -> Result
>     where
>         usize: IoLoc<Self::Target, u64, IoType = u64>,
> -        Self: IoCapable<u64>,
> +        Self::Backend: IoCapable<u64>,
>     {
>         self.try_write(offset, value)
>     }
> @@ -457,7 +486,7 @@ fn try_write64(self, value: u64, offset: usize) -> Result
>     fn read8(self, offset: usize) -> u8
>     where
>         usize: IoLoc<Self::Target, u8, IoType = u8>,
> -        Self: IoCapable<u8>,
> +        Self::Backend: IoCapable<u8>,
>     {
>         self.read(offset)
>     }
> @@ -467,7 +496,7 @@ fn read8(self, offset: usize) -> u8
>     fn read16(self, offset: usize) -> u16
>     where
>         usize: IoLoc<Self::Target, u16, IoType = u16>,
> -        Self: IoCapable<u16>,
> +        Self::Backend: IoCapable<u16>,
>     {
>         self.read(offset)
>     }
> @@ -477,7 +506,7 @@ fn read16(self, offset: usize) -> u16
>     fn read32(self, offset: usize) -> u32
>     where
>         usize: IoLoc<Self::Target, u32, IoType = u32>,
> -        Self: IoCapable<u32>,
> +        Self::Backend: IoCapable<u32>,
>     {
>         self.read(offset)
>     }
> @@ -487,7 +516,7 @@ fn read32(self, offset: usize) -> u32
>     fn read64(self, offset: usize) -> u64
>     where
>         usize: IoLoc<Self::Target, u64, IoType = u64>,
> -        Self: IoCapable<u64>,
> +        Self::Backend: IoCapable<u64>,
>     {
>         self.read(offset)
>     }
> @@ -497,7 +526,7 @@ fn read64(self, offset: usize) -> u64
>     fn write8(self, value: u8, offset: usize)
>     where
>         usize: IoLoc<Self::Target, u8, IoType = u8>,
> -        Self: IoCapable<u8>,
> +        Self::Backend: IoCapable<u8>,
>     {
>         self.write(offset, value)
>     }
> @@ -507,7 +536,7 @@ fn write8(self, value: u8, offset: usize)
>     fn write16(self, value: u16, offset: usize)
>     where
>         usize: IoLoc<Self::Target, u16, IoType = u16>,
> -        Self: IoCapable<u16>,
> +        Self::Backend: IoCapable<u16>,
>     {
>         self.write(offset, value)
>     }
> @@ -517,7 +546,7 @@ fn write16(self, value: u16, offset: usize)
>     fn write32(self, value: u32, offset: usize)
>     where
>         usize: IoLoc<Self::Target, u32, IoType = u32>,
> -        Self: IoCapable<u32>,
> +        Self::Backend: IoCapable<u32>,
>     {
>         self.write(offset, value)
>     }
> @@ -527,7 +556,7 @@ fn write32(self, value: u32, offset: usize)
>     fn write64(self, value: u64, offset: usize)
>     where
>         usize: IoLoc<Self::Target, u64, IoType = u64>,
> -        Self: IoCapable<u64>,
> +        Self::Backend: IoCapable<u64>,
>     {
>         self.write(offset, value)
>     }
> @@ -559,12 +588,10 @@ fn write64(self, value: u64, offset: usize)
>     fn try_read<T, L>(self, location: L) -> Result<T>
>     where
>         L: IoLoc<Self::Target, T>,
> -        Self: IoCapable<L::IoType>,
> +        Self::Backend: IoCapable<L::IoType>,
>     {
> -        let address = self.io_addr::<L::IoType>(location.offset())?;
> -
> -        // SAFETY: `address` has been validated by `io_addr`.
> -        Ok(unsafe { self.io_read(address) }.into())
> +        let view = io_view::<Self, L::IoType>(self, location.offset())?;
> +        Ok(Self::Backend::io_read(view).into())
>     }
> 
>     /// Generic fallible write with runtime bounds check.
> @@ -594,14 +621,11 @@ fn try_read<T, L>(self, location: L) -> Result<T>
>     fn try_write<T, L>(self, location: L, value: T) -> Result
>     where
>         L: IoLoc<Self::Target, T>,
> -        Self: IoCapable<L::IoType>,
> +        Self::Backend: IoCapable<L::IoType>,
>     {
> -        let address = self.io_addr::<L::IoType>(location.offset())?;
> +        let view = io_view::<Self, L::IoType>(self, location.offset())?;
>         let io_value = value.into();
> -
> -        // SAFETY: `address` has been validated by `io_addr`.
> -        unsafe { self.io_write(io_value, address) }
> -
> +        Self::Backend::io_write(view, io_value);
>         Ok(())
>     }
> 
> @@ -642,7 +666,7 @@ fn try_write_reg<T, L, V>(self, value: V) -> Result
>     where
>         L: IoLoc<Self::Target, T>,
>         V: LocatedRegister<Self::Target, Location = L, Value = T>,
> -        Self: IoCapable<L::IoType>,
> +        Self::Backend: IoCapable<L::IoType>,
>     {
>         let (location, value) = value.into_io_op();
> 
> @@ -675,17 +699,14 @@ fn try_write_reg<T, L, V>(self, value: V) -> Result
>     fn try_update<T, L, F>(self, location: L, f: F) -> Result
>     where
>         L: IoLoc<Self::Target, T>,
> -        Self: IoCapable<L::IoType>,
> +        Self::Backend: IoCapable<L::IoType>,
>         F: FnOnce(T) -> T,
>     {
> -        let address = self.io_addr::<L::IoType>(location.offset())?;
> +        let view = io_view::<Self, L::IoType>(self, location.offset())?;
> 
> -        // SAFETY: `address` has been validated by `io_addr`.
> -        let value: T = unsafe { self.io_read(address) }.into();
> +        let value: T = Self::Backend::io_read(view).into();
>         let io_value = f(value).into();
> -
> -        // SAFETY: `address` has been validated by `io_addr`.
> -        unsafe { self.io_write(io_value, address) }
> +        Self::Backend::io_write(view, io_value);
> 
>         Ok(())
>     }
> @@ -715,12 +736,10 @@ fn try_update<T, L, F>(self, location: L, f: F) -> Result
>     fn read<T, L>(self, location: L) -> T
>     where
>         L: IoLoc<Self::Target, T>,
> -        Self: IoCapable<L::IoType>,
> +        Self::Backend: IoCapable<L::IoType>,
>     {
> -        let address = self.io_addr_assert::<L::IoType>(location.offset());
> -
> -        // SAFETY: `address` has been validated by `io_addr_assert`.
> -        unsafe { self.io_read(address) }.into()
> +        let view = io_view_assert::<Self, L::IoType>(self, location.offset());
> +        Self::Backend::io_read(view).into()
>     }
> 
>     /// Generic infallible write with compile-time bounds check.
> @@ -748,13 +767,11 @@ fn read<T, L>(self, location: L) -> T
>     fn write<T, L>(self, location: L, value: T)
>     where
>         L: IoLoc<Self::Target, T>,
> -        Self: IoCapable<L::IoType>,
> +        Self::Backend: IoCapable<L::IoType>,
>     {
> -        let address = self.io_addr_assert::<L::IoType>(location.offset());
> +        let view = io_view_assert::<Self, L::IoType>(self, location.offset());
>         let io_value = value.into();
> -
> -        // SAFETY: `address` has been validated by `io_addr_assert`.
> -        unsafe { self.io_write(io_value, address) }
> +        Self::Backend::io_write(view, io_value);
>     }
> 
>     /// Generic infallible write of a fully-located register value.
> @@ -793,7 +810,7 @@ fn write_reg<T, L, V>(self, value: V)
>     where
>         L: IoLoc<Self::Target, T>,
>         V: LocatedRegister<Self::Target, Location = L, Value = T>,
> -        Self: IoCapable<L::IoType>,
> +        Self::Backend: IoCapable<L::IoType>,
>     {
>         let (location, value) = value.into_io_op();
> 
> @@ -826,17 +843,13 @@ fn write_reg<T, L, V>(self, value: V)
>     fn update<T, L, F>(self, location: L, f: F)
>     where
>         L: IoLoc<Self::Target, T>,
> -        Self: IoCapable<L::IoType>,
> +        Self::Backend: IoCapable<L::IoType>,
>         F: FnOnce(T) -> T,
>     {
> -        let address = self.io_addr_assert::<L::IoType>(location.offset());
> -
> -        // SAFETY: `address` has been validated by `io_addr_assert`.
> -        let value: T = unsafe { self.io_read(address) }.into();
> +        let view = io_view_assert::<Self, L::IoType>(self, location.offset());
> +        let value: T = Self::Backend::io_read(view).into();
>         let io_value = f(value).into();
> -
> -        // SAFETY: `address` has been validated by `io_addr_assert`.
> -        unsafe { self.io_write(io_value, address) }
> +        Self::Backend::io_write(view, io_value);
>     }
> }
> 
> @@ -880,78 +893,78 @@ unsafe impl<T: ?Sized + Sync> Send for Mmio<'_, T> {}
> // SAFETY: `Mmio<'_, T>` is conceptually `&T` but in I/O memory.
> unsafe impl<T: ?Sized + Sync> Sync for Mmio<'_, T> {}
> 
> -impl<T: ?Sized + KnownSize> Io for Mmio<'_, T> {
> +impl<'a, T: ?Sized + KnownSize> Io<'a> for Mmio<'a, T> {
> +    type Backend = MmioBackend;
>     type Target = T;
> 
>     #[inline]
> -    fn addr(self) -> usize {
> -        self.ptr.addr()
> +    fn as_view(self) -> Mmio<'a, T> {
> +        self
>     }
> +}
> +
> +/// I/O Backend for memory-mapped I/O.
> +pub struct MmioBackend;
> +
> +impl IoBackend for MmioBackend {
> +    type View<'a, T: ?Sized + KnownSize> = Mmio<'a, T>;
> 
>     #[inline]
> -    fn maxsize(self) -> usize {
> -        KnownSize::size(self.ptr)
> +    fn as_ptr<'a, T: ?Sized + KnownSize>(view: Self::View<'a, T>) -> *mut T {
> +        view.ptr
> +    }
> +
> +    #[inline]
> +    unsafe fn project_view<'a, T: ?Sized + KnownSize, U: ?Sized + KnownSize>(
> +        _view: Self::View<'a, T>,
> +        ptr: *mut U,
> +    ) -> Self::View<'a, U> {
> +        // INVARIANT: Per safety requirement, `ptr` is projection from `view`, so it is also a valid
> +        // memory-mapped I/O region.
> +        Mmio {
> +            ptr,
> +            phantom: PhantomData,
> +        }
>     }
> }
> 
> -/// Implements [`IoCapable`] on `$mmio` for `$ty` using `$read_fn` and `$write_fn`.
> +/// Implements [`IoCapable`] on `$backend` for `$ty` using `$read_fn` and `$write_fn`.
> macro_rules! impl_mmio_io_capable {
> -    ($mmio:ident, $(#[$attr:meta])* $ty:ty, $read_fn:ident, $write_fn:ident) => {
> -        $(#[$attr])*
> -        impl<T: ?Sized> IoCapable<$ty> for $mmio<'_, T> {
> +    ($backend: ident, $ty:ty, $read_fn:ident, $write_fn:ident) => {
> +        impl IoCapable<$ty> for $backend {
>             #[inline]
> -            unsafe fn io_read(self, address: usize) -> $ty {
> -                // SAFETY: By the trait invariant `address` is a valid address for MMIO operations.
> -                unsafe { bindings::$read_fn(address as *const c_void) }
> +            fn io_read(view: <$backend as IoBackend>::View<'_, $ty>) -> $ty {
> +                // SAFETY: `$backend::as_ptr(view)` is a valid pointer for MMIO operations for both
> +                // `MmioBackend` and `RelaxedMmioBackend`.
> +                unsafe { bindings::$read_fn($backend::as_ptr(view).cast_const().cast()) }
>             }
> 
>             #[inline]
> -            unsafe fn io_write(self, value: $ty, address: usize) {
> -                // SAFETY: By the trait invariant `address` is a valid address for MMIO operations.
> -                unsafe { bindings::$write_fn(value, address as *mut c_void) }
> +            fn io_write(view: <$backend as IoBackend>::View<'_, $ty>, value: $ty) {
> +                // SAFETY: `$backend::as_ptr(view)` is a valid pointer for MMIO operations for both
> +                // `MmioBackend` and `RelaxedMmioBackend`.
> +                unsafe { bindings::$write_fn(value, $backend::as_ptr(view).cast()) }
>             }
>         }
>     };
> }
> 
> // MMIO regions support 8, 16, and 32-bit accesses.
> -impl_mmio_io_capable!(Mmio, u8, readb, writeb);
> -impl_mmio_io_capable!(Mmio, u16, readw, writew);
> -impl_mmio_io_capable!(Mmio, u32, readl, writel);
> +impl_mmio_io_capable!(MmioBackend, u8, readb, writeb);
> +impl_mmio_io_capable!(MmioBackend, u16, readw, writew);
> +impl_mmio_io_capable!(MmioBackend, u32, readl, writel);
> // MMIO regions on 64-bit systems also support 64-bit accesses.
> #[cfg(CONFIG_64BIT)]
> -impl_mmio_io_capable!(Mmio, u64, readq, writeq);
> +impl_mmio_io_capable!(MmioBackend, u64, readq, writeq);
> 
> -impl<'a, const SIZE: usize> Io for &'a MmioOwned<SIZE> {
> +impl<'a, const SIZE: usize> Io<'a> for &'a MmioOwned<SIZE> {
> +    type Backend = MmioBackend;
>     type Target = Region<SIZE>;
> 
> -    /// Returns the base address of this mapping.
>     #[inline]
> -    fn addr(self) -> usize {
> -        self.0.addr()
> -    }
> -
> -    /// Returns the maximum size of this mapping.
> -    #[inline]
> -    fn maxsize(self) -> usize {
> -        self.0.size()
> -    }
> -}
> -
> -impl<'a, const SIZE: usize, T> IoCapable<T> for &'a MmioOwned<SIZE>
> -where
> -    Mmio<'a, Region<SIZE>>: IoCapable<T>,
> -{
> -    #[inline]
> -    unsafe fn io_read(self, address: usize) -> T {
> -        // SAFETY: Per safety requirement.
> -        unsafe { self.as_view().io_read(address) }
> -    }
> -
> -    #[inline]
> -    unsafe fn io_write(self, value: T, address: usize) {
> -        // SAFETY: Per safety requirement.
> -        unsafe { self.as_view().io_write(value, address) }
> +    fn as_view(self) -> Mmio<'a, Self::Target> {
> +        // SAFETY: `Mmio` has same invariant as `MmioOwned`
> +        unsafe { Mmio::from_raw(self.0) }
>     }
> }
> 
> @@ -967,13 +980,6 @@ pub unsafe fn from_raw(raw: &MmioRaw<Region<SIZE>>) -> &Self {
>         // SAFETY: `MmioOwned` is a transparent wrapper around `MmioRaw`.
>         unsafe { &*core::ptr::from_ref(raw).cast() }
>     }
> -
> -    /// Return a view that covers the full region.
> -    #[inline]
> -    pub fn as_view(&self) -> Mmio<'_, Region<SIZE>> {
> -        // SAFETY: `Mmio` has same invariant as `MmioOwned`.
> -        unsafe { Mmio::from_raw(self.0) }
> -    }
> }
> 
> /// [`Mmio`] but using relaxed accessors.
> @@ -992,17 +998,34 @@ fn clone(&self) -> Self {
>     }
> }
> 
> -impl<T: ?Sized + KnownSize> Io for RelaxedMmio<'_, T> {
> -    type Target = T;
> +/// I/O Backend for memory-mapped I/O, with relaxed access semantics.
> +pub struct RelaxedMmioBackend;
> +
> +impl IoBackend for RelaxedMmioBackend {
> +    type View<'a, T: ?Sized + KnownSize> = RelaxedMmio<'a, T>;
> 
>     #[inline]
> -    fn addr(self) -> usize {
> -        self.0.addr()
> +    fn as_ptr<'a, T: ?Sized + KnownSize>(view: Self::View<'a, T>) -> *mut T {
> +        MmioBackend::as_ptr(view.0)
>     }
> 
>     #[inline]
> -    fn maxsize(self) -> usize {
> -        self.0.maxsize()
> +    unsafe fn project_view<'a, T: ?Sized + KnownSize, U: ?Sized + KnownSize>(
> +        view: Self::View<'a, T>,
> +        ptr: *mut U,
> +    ) -> Self::View<'a, U> {
> +        // SAFETY: Per safety requirement.
> +        RelaxedMmio(unsafe { MmioBackend::project_view(view.0, ptr) })
> +    }
> +}
> +
> +impl<'a, T: ?Sized + KnownSize> Io<'a> for RelaxedMmio<'a, T> {
> +    type Backend = RelaxedMmioBackend;
> +    type Target = T;
> +
> +    #[inline]
> +    fn as_view(self) -> RelaxedMmio<'a, T> {
> +        self
>     }
> }
> 
> @@ -1035,14 +1058,9 @@ pub fn relaxed(self) -> RelaxedMmio<'a, T> {
> }
> 
> // MMIO regions support 8, 16, and 32-bit accesses.
> -impl_mmio_io_capable!(RelaxedMmio, u8, readb_relaxed, writeb_relaxed);
> -impl_mmio_io_capable!(RelaxedMmio, u16, readw_relaxed, writew_relaxed);
> -impl_mmio_io_capable!(RelaxedMmio, u32, readl_relaxed, writel_relaxed);
> +impl_mmio_io_capable!(RelaxedMmioBackend, u8, readb_relaxed, writeb_relaxed);
> +impl_mmio_io_capable!(RelaxedMmioBackend, u16, readw_relaxed, writew_relaxed);
> +impl_mmio_io_capable!(RelaxedMmioBackend, u32, readl_relaxed, writel_relaxed);
> // MMIO regions on 64-bit systems also support 64-bit accesses.
> -impl_mmio_io_capable!(
> -    RelaxedMmio,
> -    #[cfg(CONFIG_64BIT)]
> -    u64,
> -    readq_relaxed,
> -    writeq_relaxed
> -);
> +#[cfg(CONFIG_64BIT)]
> +impl_mmio_io_capable!(RelaxedMmioBackend, u64, readq_relaxed, writeq_relaxed);
> diff --git a/rust/kernel/pci/io.rs b/rust/kernel/pci/io.rs
> index 89f4bb483a7f..e67c1e3694fb 100644
> --- a/rust/kernel/pci/io.rs
> +++ b/rust/kernel/pci/io.rs
> @@ -9,6 +9,7 @@
>     devres::Devres,
>     io::{
>         Io,
> +        IoBackend,
>         IoCapable,
>         MmioOwned,
>         MmioRaw,
> @@ -84,32 +85,57 @@ unsafe impl<T: ?Sized + Sync> Send for ConfigSpace<'_, T> {}
> // SAFETY: `ConfigSpace<'_, T>` is conceptually `&T` but in I/O memory.
> unsafe impl<T: ?Sized + Sync> Sync for ConfigSpace<'_, T> {}
> 
> +/// I/O Backend for PCI configuration space.
> +pub struct ConfigSpaceBackend;
> +
> +impl IoBackend for ConfigSpaceBackend {
> +    type View<'a, T: ?Sized + KnownSize> = ConfigSpace<'a, T>;
> +
> +    #[inline]
> +    fn as_ptr<'a, T: ?Sized + KnownSize>(view: ConfigSpace<'a, T>) -> *mut T {
> +        view.ptr
> +    }
> +
> +    #[inline]
> +    unsafe fn project_view<'a, T: ?Sized + KnownSize, U: ?Sized + KnownSize>(
> +        view: Self::View<'a, T>,
> +        ptr: *mut U,
> +    ) -> Self::View<'a, U> {
> +        // INVARIANT: Per safety requirement.
> +        ConfigSpace {
> +            pdev: view.pdev,
> +            ptr,
> +        }
> +    }
> +}
> +
> /// Implements [`IoCapable`] on [`ConfigSpace`] for `$ty` using `$read_fn` and `$write_fn`.
> macro_rules! impl_config_space_io_capable {
>     ($ty:ty, $read_fn:ident, $write_fn:ident) => {
> -        impl<'a, T: ?Sized> IoCapable<$ty> for ConfigSpace<'a, T> {
> -            unsafe fn io_read(self, address: usize) -> $ty {
> +        impl IoCapable<$ty> for ConfigSpaceBackend {
> +            fn io_read(view: ConfigSpace<'_, $ty>) -> $ty {
> +                // CAST: The offset is cast to `i32` because the C functions expect a 32-bit
> +                // signed offset parameter. PCI configuration space size is at most 4096 bytes,
> +                // so the value always fits within `i32` without truncation or sign change.
> +                let addr = view.ptr.addr() as i32;
> +
>                 let mut val: $ty = 0;
> 
>                 // Return value from C function is ignored in infallible accessors.
> -                let _ret =
> -                    // SAFETY: By the type invariant `self.pdev` is a valid address.
> -                    // CAST: The offset is cast to `i32` because the C functions expect a 32-bit
> -                    // signed offset parameter. PCI configuration space size is at most 4096 bytes,
> -                    // so the value always fits within `i32` without truncation or sign change.
> -                    unsafe { bindings::$read_fn(self.pdev.as_raw(), address as i32, &mut val) };
> -
> +                // SAFETY: By the type invariant `pdev` is a valid address.
> +                let _ = unsafe { bindings::$read_fn(view.pdev.as_raw(), addr, &mut val) };
>                 val
>             }
> 
> -            unsafe fn io_write(self, value: $ty, address: usize) {
> +            fn io_write(view: ConfigSpace<'_, $ty>, value: $ty) {
> +                // CAST: The offset is cast to `i32` because the C functions expect a 32-bit
> +                // signed offset parameter. PCI configuration space size is at most 4096 bytes,
> +                // so the value always fits within `i32` without truncation or sign change.
> +                let addr = view.ptr.addr() as i32;
> +
>                 // Return value from C function is ignored in infallible accessors.
> -                let _ret =
> -                    // SAFETY: By the type invariant `self.pdev` is a valid address.
> -                    // CAST: The offset is cast to `i32` because the C functions expect a 32-bit
> -                    // signed offset parameter. PCI configuration space size is at most 4096 bytes,
> -                    // so the value always fits within `i32` without truncation or sign change.
> -                    unsafe { bindings::$write_fn(self.pdev.as_raw(), address as i32, value) };
> +                // SAFETY: By the type invariant `pdev` is a valid address.
> +                let _ = unsafe { bindings::$write_fn(view.pdev.as_raw(), addr, value) };
>             }
>         }
>     };
> @@ -120,17 +146,13 @@ unsafe fn io_write(self, value: $ty, address: usize) {
> impl_config_space_io_capable!(u16, pci_read_config_word, pci_write_config_word);
> impl_config_space_io_capable!(u32, pci_read_config_dword, pci_write_config_dword);
> 
> -impl<'a, T: ?Sized + KnownSize> Io for ConfigSpace<'a, T> {
> +impl<'a, T: ?Sized + KnownSize> Io<'a> for ConfigSpace<'a, T> {
> +    type Backend = ConfigSpaceBackend;
>     type Target = T;
> 
>     #[inline]
> -    fn addr(self) -> usize {
> -        self.ptr.addr()
> -    }
> -
> -    #[inline]
> -    fn maxsize(self) -> usize {
> -        KnownSize::size(self.ptr)
> +    fn as_view(self) -> ConfigSpace<'a, T> {
> +        self
>     }
> }
> 
> 
> -- 
> 2.54.0
> 

Reviewed-by: Daniel Almeida <daniel.almeida@collabora.com>


  reply	other threads:[~2026-07-08 14:37 UTC|newest]

Thread overview: 45+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2026-07-06 12:44 [PATCH v6 00/20] rust: I/O type generalization and projection Gary Guo
2026-07-06 12:44 ` [PATCH v6 01/20] rust: io: add dynamically-sized `Region` type Gary Guo
2026-07-07 17:14   ` Daniel Almeida
2026-07-07 20:02     ` Gary Guo
2026-07-08 10:34       ` Miguel Ojeda
2026-07-06 12:44 ` [PATCH v6 02/20] rust: io: add missing safety requirement in `IoCapable` methods Gary Guo
2026-07-07 18:40   ` Daniel Almeida
2026-07-06 12:44 ` [PATCH v6 03/20] rust: io: restrict untyped IO access and `register!` to `Region` Gary Guo
2026-07-08  0:23   ` Daniel Almeida
2026-07-06 12:44 ` [PATCH v6 04/20] rust: io: implement `Io` on reference types instead Gary Guo
2026-07-08 11:58   ` Daniel Almeida
2026-07-08 12:05     ` Gary Guo
2026-07-06 12:44 ` [PATCH v6 05/20] rust: io: generalize `MmioRaw` to pointer to arbitrary type Gary Guo
2026-07-08 12:22   ` Daniel Almeida
2026-07-06 12:44 ` [PATCH v6 06/20] rust: io: rename `Mmio` to `MmioOwned` Gary Guo
2026-07-08 12:26   ` Daniel Almeida
2026-07-08 12:35   ` Daniel Almeida
2026-07-06 12:44 ` [PATCH v6 07/20] rust: io: implement `Mmio` as view type Gary Guo
2026-07-08 13:10   ` Daniel Almeida
2026-07-06 12:44 ` [PATCH v6 08/20] rust: pci: io: make `ConfigSpace` a view Gary Guo
2026-07-08 13:28   ` Daniel Almeida
2026-07-06 12:44 ` [PATCH v6 09/20] rust: io: use view types instead of addresses for `Io` Gary Guo
2026-07-08 14:36   ` Daniel Almeida [this message]
2026-07-08 15:25     ` Miguel Ojeda
2026-07-06 12:44 ` [PATCH v6 10/20] pwm: th1520: remove unnecessary `deref` Gary Guo
2026-07-08 14:37   ` Daniel Almeida
2026-07-06 12:44 ` [PATCH v6 11/20] rust: io: remove `MmioOwned` Gary Guo
2026-07-08 14:48   ` Daniel Almeida
2026-07-06 12:44 ` [PATCH v6 12/20] rust: io: move `Io` methods to extension trait Gary Guo
2026-07-08 15:15   ` Daniel Almeida
2026-07-06 12:44 ` [PATCH v6 13/20] rust: io: add projection macro and methods Gary Guo
2026-07-08 17:58   ` Daniel Almeida
2026-07-06 12:44 ` [PATCH v6 14/20] rust: io: add I/O backend for system memory with volatile access Gary Guo
2026-07-11  1:26   ` Daniel Almeida
2026-07-06 12:44 ` [PATCH v6 15/20] rust: io: implement a view type for `Coherent` Gary Guo
2026-07-11  1:32   ` Daniel Almeida
2026-07-06 12:44 ` [PATCH v6 16/20] rust: io: add `read_val` and `write_val` functions on `Io` Gary Guo
2026-07-11  1:56   ` Daniel Almeida
2026-07-06 12:44 ` [PATCH v6 17/20] gpu: nova-core: use I/O projection for cleaner encapsulation Gary Guo
2026-07-06 12:44 ` [PATCH v6 18/20] rust: dma: drop `dma_read!` and `dma_write!` API Gary Guo
2026-07-11  2:04   ` Daniel Almeida
2026-07-06 12:44 ` [PATCH v6 19/20] rust: io: add copying methods Gary Guo
2026-07-07 12:17   ` Alexandre Courbot
2026-07-06 12:44 ` [PATCH v6 20/20] rust: io: implement `IoSysMap` Gary Guo
2026-07-11 16:10 ` [PATCH v6 00/20] rust: I/O type generalization and projection Danilo Krummrich

Reply instructions:

You may reply publicly to this message via plain-text email
using any one of the following methods:

* Save the following mbox file, import it into your mail client,
  and reply-to-all from there: mbox

  Avoid top-posting and favor interleaved quoting:
  https://en.wikipedia.org/wiki/Posting_style#Interleaved_style

* Reply using the --to, --cc, and --in-reply-to
  switches of git-send-email(1):

  git send-email \
    --in-reply-to=280FBE45-5653-499B-9BB0-A2C77030A826@collabora.com \
    --to=daniel.almeida@collabora.com \
    --cc=a.hindborg@kernel.org \
    --cc=abdiel.janulgue@gmail.com \
    --cc=acourbot@nvidia.com \
    --cc=airlied@gmail.com \
    --cc=aliceryhl@google.com \
    --cc=bhelgaas@google.com \
    --cc=bjorn3_gh@protonmail.com \
    --cc=boqun@kernel.org \
    --cc=dakr@kernel.org \
    --cc=dri-devel@lists.freedesktop.org \
    --cc=driver-core@lists.linux.dev \
    --cc=gary@garyguo.net \
    --cc=gregkh@linuxfoundation.org \
    --cc=kwilczynski@kernel.org \
    --cc=linux-kernel@vger.kernel.org \
    --cc=linux-pci@vger.kernel.org \
    --cc=linux-pwm@vger.kernel.org \
    --cc=lossin@kernel.org \
    --cc=m.wilczynski@samsung.com \
    --cc=nova-gpu@lists.linux.dev \
    --cc=ojeda@kernel.org \
    --cc=rafael@kernel.org \
    --cc=robin.murphy@arm.com \
    --cc=rust-for-linux@vger.kernel.org \
    --cc=simona@ffwll.ch \
    --cc=tamird@kernel.org \
    --cc=tmgross@umich.edu \
    --cc=ukleinek@kernel.org \
    --cc=work@onurozkan.dev \
    /path/to/YOUR_REPLY

  https://kernel.org/pub/software/scm/git/docs/git-send-email.html

* If your mail client supports setting the In-Reply-To header
  via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line before the message body.
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox