rust: io: move Io methods to extension trait

`Io` trait now has a single required method with many more provided
methods. Provided methods may want to rely on their implementations to not
be arbitrarily overridden by implementers for correctness or soundness.
A good example is the `size` method, it may be relied by unsafe code and
thus must be consistent with the metadata obtained from `as_ptr`.

Thus, create a new trait to host `size` method, extract existing provided
methods to the new trait, and provide a blanket implementation. This
pattern is used extensively in userspace Rust libraries e.g. `tokio` where
`AsyncRead` has minimum methods and `AsyncReadExt` is what users mostly
interact with.

To avoid changing all user imports, the base trait is renamed to `IoBase`
and the newly added trait takes the existing `Io` name.

Reviewed-by: Alexandre Courbot <acourbot@nvidia.com>
Suggested-by: Danilo Krummrich <dakr@kernel.org>
Signed-off-by: Gary Guo <gary@garyguo.net>
Reviewed-by: Daniel Almeida <daniel.almeida@collabora.com>
Link: https://patch.msgid.link/20260706-io_projection-v6-12-72cd5d055d54@garyguo.net
[ Add comment explaining the purpose of the Io blanket implementation.
  - Danilo ]
Signed-off-by: Danilo Krummrich <dakr@kernel.org>
This commit is contained in:
Gary Guo 2026-07-06 13:44:25 +01:00 committed by Danilo Krummrich
parent bed01ca9e9
commit 9b36c13cbd
4 changed files with 34 additions and 17 deletions

View File

@ -68,6 +68,7 @@ struct Inner<T> {
/// devres::Devres,
/// io::{
/// Io,
/// IoBase,
/// Mmio,
/// MmioRaw,
/// MmioBackend,
@ -105,7 +106,7 @@ struct Inner<T> {
/// }
/// }
///
/// impl<'a, const SIZE: usize> Io<'a> for &'a IoMem<SIZE> {
/// impl<'a, const SIZE: usize> IoBase<'a> for &'a IoMem<SIZE> {
/// type Backend = MmioBackend;
/// type Target = Region<SIZE>;
///

View File

@ -224,7 +224,7 @@ fn io_view<'a, IO: Io<'a>, U>(
/// operation.
pub trait IoBackend {
/// View type for this I/O backend.
type View<'a, T: ?Sized + KnownSize>: Io<'a, Backend = Self, Target = T>;
type View<'a, T: ?Sized + KnownSize>: IoBase<'a, Backend = Self, Target = T>;
/// Convert a `view` to a raw pointer for projection.
///
@ -310,15 +310,12 @@ 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.
///
/// The [`Io`] trait provides:
/// - 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 the associated [`IoBackend`] implementation.
/// This trait defines which backend shall be used for I/O operations and provides a method to
/// convert into [`IoBackend::View`]. Users should use the [`Io`] trait which provides the actual
/// methods to perform I/O operations.
///
/// This should be implemented on cheaply copyable handles, such as references or view types.
pub trait Io<'a>: Copy {
pub trait IoBase<'a>: Copy {
/// Type that defines all I/O operations.
type Backend: IoBackend;
@ -327,6 +324,21 @@ pub trait Io<'a>: Copy {
/// Return a view that covers the full region.
fn as_view(self) -> <Self::Backend as IoBackend>::View<'a, Self::Target>;
}
/// Extension trait to provide I/O operation methods to types that implement [`IoBase`].
///
/// This trait provides:
/// - Helper methods for offset validation and address calculation
/// - Fallible (runtime checked) accessors for different data widths
///
/// Which I/O methods are available depends on the associated [`IoBackend`] implementation.
pub trait Io<'a>: IoBase<'a> {
/// Returns the size of this I/O region.
#[inline]
fn size(self) -> usize {
KnownSize::size(Self::Backend::as_ptr(self.as_view()))
}
/// Fallible 8-bit read with runtime bounds check.
#[inline(always)]
@ -780,6 +792,10 @@ fn update<T, L, F>(self, location: L, f: F)
}
}
// Blanket implementation ensures that provided methods cannot be arbitrarily overridden by
// implementers, which is relied upon for correctness and soundness.
impl<'a, T: IoBase<'a>> Io<'a> for T {}
/// A view of memory-mapped I/O region.
///
/// # Invariant
@ -820,7 +836,7 @@ 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<'a, T: ?Sized + KnownSize> Io<'a> for Mmio<'a, T> {
impl<'a, T: ?Sized + KnownSize> IoBase<'a> for Mmio<'a, T> {
type Backend = MmioBackend;
type Target = T;
@ -921,7 +937,7 @@ unsafe fn project_view<'a, T: ?Sized + KnownSize, U: ?Sized + KnownSize>(
}
}
impl<'a, T: ?Sized + KnownSize> Io<'a> for RelaxedMmio<'a, T> {
impl<'a, T: ?Sized + KnownSize> IoBase<'a> for RelaxedMmio<'a, T> {
type Backend = RelaxedMmioBackend;
type Target = T;

View File

@ -14,7 +14,7 @@
Region,
Resource, //
},
Io,
IoBase,
Mmio,
MmioBackend,
MmioRaw, //
@ -210,7 +210,7 @@ pub fn into_devres(self) -> Result<Devres<ExclusiveIoMem<'static, SIZE>>> {
}
}
impl<'a, const SIZE: usize> Io<'a> for &'a ExclusiveIoMem<'_, SIZE> {
impl<'a, const SIZE: usize> IoBase<'a> for &'a ExclusiveIoMem<'_, SIZE> {
type Backend = MmioBackend;
type Target = super::Region<SIZE>;
@ -292,7 +292,7 @@ fn drop(&mut self) {
}
}
impl<'a, const SIZE: usize> Io<'a> for &'a IoMem<'_, SIZE> {
impl<'a, const SIZE: usize> IoBase<'a> for &'a IoMem<'_, SIZE> {
type Backend = MmioBackend;
type Target = super::Region<SIZE>;

View File

@ -8,8 +8,8 @@
device,
devres::Devres,
io::{
Io,
IoBackend,
IoBase,
IoCapable,
Mmio,
MmioBackend,
@ -144,7 +144,7 @@ fn io_write(view: ConfigSpace<'_, $ty>, value: $ty) {
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<'a> for ConfigSpace<'a, T> {
impl<'a, T: ?Sized + KnownSize> IoBase<'a> for ConfigSpace<'a, T> {
type Backend = ConfigSpaceBackend;
type Target = T;
@ -267,7 +267,7 @@ fn drop(&mut self) {
}
}
impl<'a, const SIZE: usize> Io<'a> for &'a Bar<'_, SIZE> {
impl<'a, const SIZE: usize> IoBase<'a> for &'a Bar<'_, SIZE> {
type Backend = MmioBackend;
type Target = crate::io::Region<SIZE>;