public inbox for llvm@lists.linux.dev
 help / color / mirror / Atom feed
* [PATCH 6/8] rust: io: add `read_val` and `write_val` function on I/O view
       [not found] <20260323153807.1360705-1-gary@kernel.org>
@ 2026-03-23 15:37 ` Gary Guo
  2026-03-27  8:21   ` Andreas Hindborg
  0 siblings, 1 reply; 3+ messages in thread
From: Gary Guo @ 2026-03-23 15:37 UTC (permalink / raw)
  To: Miguel Ojeda, Boqun Feng, Gary Guo, Björn Roy Baron,
	Benno Lossin, Andreas Hindborg, Alice Ryhl, Trevor Gross,
	Danilo Krummrich, Abdiel Janulgue, Daniel Almeida, Robin Murphy,
	Nathan Chancellor, Nick Desaulniers, Bill Wendling, Justin Stitt
  Cc: rust-for-linux, driver-core, linux-kernel, llvm

From: Gary Guo <gary@garyguo.net>

When a view is narrowed to just a primitive, these functions provide a way
to access them using the `IoCapable` trait. This is used to provide
`io_read!` and `io_write!` macros, which are generalized version of current
`dma_read!` and `dma_write!` macro (that works on `Coherent` only, but not
subview of them, or other I/O backends).

For DMA coherent objects, `IoCapable` is only implemented for atomically
accessible primitives; this is because `read_volatile`/`write_volatile` can
behave undesirably for aggregates; LLVM may turn them to multiple
instructions to access parts and assemble, or could combine them to a
single instruction.

The ability to read/write aggregates (when atomicity is of no concern),
could be implemented with copying primitives (e.g. memcpy_{from,to}io) in
the future.

Signed-off-by: Gary Guo <gary@garyguo.net>
---
 rust/kernel/dma.rs | 40 +++++++++++++++++++
 rust/kernel/io.rs  | 95 ++++++++++++++++++++++++++++++++++++++++++++++
 2 files changed, 135 insertions(+)

diff --git a/rust/kernel/dma.rs b/rust/kernel/dma.rs
index ae2939abc166..fcdf85f5ed50 100644
--- a/rust/kernel/dma.rs
+++ b/rust/kernel/dma.rs
@@ -877,6 +877,46 @@ fn as_ptr(&self) -> *mut Self::Type {
     }
 }
 
+/// Implements [`IoCapable`] on `Coherent` for `$ty` using `read_volatile` and `write_volatile`.
+macro_rules! impl_coherent_io_capable {
+    ($(#[$attr:meta])* $ty:ty) => {
+        $(#[$attr])*
+        impl<T: ?Sized + KnownSize> IoCapable<$ty> for Coherent<T> {
+            #[inline]
+            unsafe fn io_read(&self, address: *mut $ty) -> $ty {
+                // SAFETY:
+                // - By the safety precondition, the address is within bounds of the allocation and
+                //   aligned.
+                // - Using read_volatile() here so that race with hardware is well-defined.
+                // - Using read_volatile() here is not sound if it races with other CPU per Rust
+                //   rules, but this is allowed per LKMM.
+                unsafe { address.read_volatile() }
+            }
+
+            #[inline]
+            unsafe fn io_write(&self, value: $ty, address: *mut $ty) {
+                // SAFETY:
+                // - By the safety precondition, the address is within bounds of the allocation and
+                //   aligned.
+                // - Using write_volatile() here so that race with hardware is well-defined.
+                // - Using write_volatile() here is not sound if it races with other CPU per Rust
+                //   rules, but this is allowed per LKMM.
+                unsafe { address.write_volatile(value) }
+            }
+        }
+    };
+}
+
+// DMA regions support atomic 8, 16, and 32-bit accesses.
+impl_coherent_io_capable!(u8);
+impl_coherent_io_capable!(u16);
+impl_coherent_io_capable!(u32);
+// DMA regions on 64-bit systems also support atomic 64-bit accesses.
+impl_coherent_io_capable!(
+    #[cfg(CONFIG_64BIT)]
+    u64
+);
+
 impl<'a, B: ?Sized + KnownSize, T: ?Sized> crate::io::View<'a, Coherent<B>, T> {
     /// Returns a DMA handle which may be given to the device as the DMA address base of
     /// the region.
diff --git a/rust/kernel/io.rs b/rust/kernel/io.rs
index 8166e47f1381..cf2074d066d2 100644
--- a/rust/kernel/io.rs
+++ b/rust/kernel/io.rs
@@ -992,6 +992,26 @@ pub fn try_cast<U>(self) -> Result<View<'a, IO, U>>
     }
 }
 
+impl<T, IO: ?Sized + Io + IoCapable<T>> View<'_, IO, T> {
+    /// Read from I/O memory.
+    ///
+    /// This is only supported on types that is directly supported by `IO` with [`IoCapable`].
+    #[inline]
+    pub fn read_val(&self) -> T {
+        // SAFETY: Per type invariant.
+        unsafe { self.io.io_read(self.ptr) }
+    }
+
+    /// Write to I/O memory.
+    ///
+    /// This is only supported on types that is directly supported by `IO` with [`IoCapable`].
+    #[inline]
+    pub fn write_val(&self, value: T) {
+        // SAFETY: Per type invariant.
+        unsafe { self.io.io_write(value, self.ptr) }
+    }
+}
+
 /// Project an I/O type to a subview of it.
 ///
 /// The syntax is of form `io_project!(io, proj)` where `io` is an expression to a type that
@@ -1040,3 +1060,78 @@ macro_rules! io_project {
 
 #[doc(inline)]
 pub use crate::io_project;
+
+/// Read from I/O memory.
+///
+/// The syntax is of form `io_read!(io, proj)` where `io` is an expression to a type that
+/// implements [`Io`] and `proj` is a [projection specification](kernel::ptr::project!).
+///
+/// # Examples
+///
+/// ```
+/// # use kernel::io::View;
+/// struct MyStruct { field: u32, }
+///
+/// // SAFETY: All bit patterns are acceptable values for `MyStruct`.
+/// unsafe impl kernel::transmute::FromBytes for MyStruct{};
+/// // SAFETY: Instances of `MyStruct` have no uninitialized portions.
+/// unsafe impl kernel::transmute::AsBytes for MyStruct{};
+///
+/// # fn test(mmio: &kernel::io::Mmio<[MyStruct]>) -> Result {
+/// // let mmio: Mmio<[MyStruct]>;
+/// let field: u32 = kernel::io::io_read!(mmio, [2]?.field);
+/// # Ok::<(), Error>(()) }
+#[macro_export]
+#[doc(hidden)]
+macro_rules! io_read {
+    ($io:expr, $($proj:tt)*) => {
+        $crate::io_project!($io, $($proj)*).read_val()
+    };
+}
+
+#[doc(inline)]
+pub use crate::io_read;
+
+/// Writes to I/O memory.
+///
+/// The syntax is of form `io_write!(io, proj, val)` where `io` is an expression to a type that
+/// implements [`Io`] and `proj` is a [projection specification](kernel::ptr::project!),
+/// and `val` is the value to be written to the projected location.
+///
+/// # Examples
+///
+/// ```
+/// # use kernel::io::View;
+/// struct MyStruct { field: u32, }
+///
+/// // SAFETY: All bit patterns are acceptable values for `MyStruct`.
+/// unsafe impl kernel::transmute::FromBytes for MyStruct{};
+/// // SAFETY: Instances of `MyStruct` have no uninitialized portions.
+/// unsafe impl kernel::transmute::AsBytes for MyStruct{};
+///
+/// # fn test(mmio: &kernel::io::Mmio<[MyStruct]>) -> Result {
+/// // let mmio: Mmio<[MyStruct]>;
+/// kernel::io::io_write!(mmio, [2]?.field, 10);
+/// # Ok::<(), Error>(()) }
+#[macro_export]
+#[doc(hidden)]
+macro_rules! io_write {
+    (@parse [$io:expr] [$($proj:tt)*] [, $val:expr]) => {
+        $crate::io_project!($io, $($proj)*).write_val($val)
+    };
+    (@parse [$io:expr] [$($proj:tt)*] [.$field:tt $($rest:tt)*]) => {
+        $crate::io_write!(@parse [$io] [$($proj)* .$field] [$($rest)*])
+    };
+    (@parse [$io:expr] [$($proj:tt)*] [[$index:expr]? $($rest:tt)*]) => {
+        $crate::io_write!(@parse [$io] [$($proj)* [$index]?] [$($rest)*])
+    };
+    (@parse [$io:expr] [$($proj:tt)*] [[$index:expr] $($rest:tt)*]) => {
+        $crate::io_write!(@parse [$io] [$($proj)* [$index]] [$($rest)*])
+    };
+    ($io:expr, $($rest:tt)*) => {
+        $crate::io_write!(@parse [$io] [] [$($rest)*])
+    };
+}
+
+#[doc(inline)]
+pub use crate::io_write;
-- 
2.51.2


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

* Re: [PATCH 6/8] rust: io: add `read_val` and `write_val` function on I/O view
  2026-03-23 15:37 ` [PATCH 6/8] rust: io: add `read_val` and `write_val` function on I/O view Gary Guo
@ 2026-03-27  8:21   ` Andreas Hindborg
  2026-03-27 12:19     ` Gary Guo
  0 siblings, 1 reply; 3+ messages in thread
From: Andreas Hindborg @ 2026-03-27  8:21 UTC (permalink / raw)
  To: Gary Guo, Miguel Ojeda, Boqun Feng, Gary Guo,
	Björn Roy Baron, Benno Lossin, Alice Ryhl, Trevor Gross,
	Danilo Krummrich, Abdiel Janulgue, Daniel Almeida, Robin Murphy,
	Nathan Chancellor, Nick Desaulniers, Bill Wendling, Justin Stitt
  Cc: rust-for-linux, driver-core, linux-kernel, llvm

"Gary Guo" <gary@kernel.org> writes:

> From: Gary Guo <gary@garyguo.net>
>
> When a view is narrowed to just a primitive, these functions provide a way
> to access them using the `IoCapable` trait. This is used to provide
> `io_read!` and `io_write!` macros, which are generalized version of current
> `dma_read!` and `dma_write!` macro (that works on `Coherent` only, but not
> subview of them, or other I/O backends).
>
> For DMA coherent objects, `IoCapable` is only implemented for atomically
> accessible primitives; this is because `read_volatile`/`write_volatile` can
> behave undesirably for aggregates; LLVM may turn them to multiple
> instructions to access parts and assemble, or could combine them to a
> single instruction.
>
> The ability to read/write aggregates (when atomicity is of no concern),
> could be implemented with copying primitives (e.g. memcpy_{from,to}io) in
> the future.
>
> Signed-off-by: Gary Guo <gary@garyguo.net>
> ---
>  rust/kernel/dma.rs | 40 +++++++++++++++++++
>  rust/kernel/io.rs  | 95 ++++++++++++++++++++++++++++++++++++++++++++++
>  2 files changed, 135 insertions(+)
>
> diff --git a/rust/kernel/dma.rs b/rust/kernel/dma.rs
> index ae2939abc166..fcdf85f5ed50 100644
> --- a/rust/kernel/dma.rs
> +++ b/rust/kernel/dma.rs
> @@ -877,6 +877,46 @@ fn as_ptr(&self) -> *mut Self::Type {
>      }
>  }
>
> +/// Implements [`IoCapable`] on `Coherent` for `$ty` using `read_volatile` and `write_volatile`.
> +macro_rules! impl_coherent_io_capable {
> +    ($(#[$attr:meta])* $ty:ty) => {
> +        $(#[$attr])*
> +        impl<T: ?Sized + KnownSize> IoCapable<$ty> for Coherent<T> {
> +            #[inline]
> +            unsafe fn io_read(&self, address: *mut $ty) -> $ty {
> +                // SAFETY:
> +                // - By the safety precondition, the address is within bounds of the allocation and
> +                //   aligned.
> +                // - Using read_volatile() here so that race with hardware is well-defined.
> +                // - Using read_volatile() here is not sound if it races with other CPU per Rust
> +                //   rules, but this is allowed per LKMM.

You are not covering how we satisfy "Reading from src must produce a
properly initialized value of type T." I assume you need a bound on `T: FromBytes`?


Best regards,
Andreas Hindborg



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

* Re: [PATCH 6/8] rust: io: add `read_val` and `write_val` function on I/O view
  2026-03-27  8:21   ` Andreas Hindborg
@ 2026-03-27 12:19     ` Gary Guo
  0 siblings, 0 replies; 3+ messages in thread
From: Gary Guo @ 2026-03-27 12:19 UTC (permalink / raw)
  To: Andreas Hindborg, Gary Guo, Miguel Ojeda, Boqun Feng, Gary Guo,
	Björn Roy Baron, Benno Lossin, Alice Ryhl, Trevor Gross,
	Danilo Krummrich, Abdiel Janulgue, Daniel Almeida, Robin Murphy,
	Nathan Chancellor, Nick Desaulniers, Bill Wendling, Justin Stitt
  Cc: rust-for-linux, driver-core, linux-kernel, llvm

On Fri Mar 27, 2026 at 8:21 AM GMT, Andreas Hindborg wrote:
> "Gary Guo" <gary@kernel.org> writes:
>
>> From: Gary Guo <gary@garyguo.net>
>>
>> When a view is narrowed to just a primitive, these functions provide a way
>> to access them using the `IoCapable` trait. This is used to provide
>> `io_read!` and `io_write!` macros, which are generalized version of current
>> `dma_read!` and `dma_write!` macro (that works on `Coherent` only, but not
>> subview of them, or other I/O backends).
>>
>> For DMA coherent objects, `IoCapable` is only implemented for atomically
>> accessible primitives; this is because `read_volatile`/`write_volatile` can
>> behave undesirably for aggregates; LLVM may turn them to multiple
>> instructions to access parts and assemble, or could combine them to a
>> single instruction.
>>
>> The ability to read/write aggregates (when atomicity is of no concern),
>> could be implemented with copying primitives (e.g. memcpy_{from,to}io) in
>> the future.
>>
>> Signed-off-by: Gary Guo <gary@garyguo.net>
>> ---
>>  rust/kernel/dma.rs | 40 +++++++++++++++++++
>>  rust/kernel/io.rs  | 95 ++++++++++++++++++++++++++++++++++++++++++++++
>>  2 files changed, 135 insertions(+)
>>
>> diff --git a/rust/kernel/dma.rs b/rust/kernel/dma.rs
>> index ae2939abc166..fcdf85f5ed50 100644
>> --- a/rust/kernel/dma.rs
>> +++ b/rust/kernel/dma.rs
>> @@ -877,6 +877,46 @@ fn as_ptr(&self) -> *mut Self::Type {
>>      }
>>  }
>>
>> +/// Implements [`IoCapable`] on `Coherent` for `$ty` using `read_volatile` and `write_volatile`.
>> +macro_rules! impl_coherent_io_capable {
>> +    ($(#[$attr:meta])* $ty:ty) => {
>> +        $(#[$attr])*
>> +        impl<T: ?Sized + KnownSize> IoCapable<$ty> for Coherent<T> {
>> +            #[inline]
>> +            unsafe fn io_read(&self, address: *mut $ty) -> $ty {
>> +                // SAFETY:
>> +                // - By the safety precondition, the address is within bounds of the allocation and
>> +                //   aligned.
>> +                // - Using read_volatile() here so that race with hardware is well-defined.
>> +                // - Using read_volatile() here is not sound if it races with other CPU per Rust
>> +                //   rules, but this is allowed per LKMM.
>
> You are not covering how we satisfy "Reading from src must produce a
> properly initialized value of type T." I assume you need a bound on `T: FromBytes`?

The macro is only used for primitives. I'll add a line explaining that next version.

Best,
Gary

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

end of thread, other threads:[~2026-03-27 12:19 UTC | newest]

Thread overview: 3+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
     [not found] <20260323153807.1360705-1-gary@kernel.org>
2026-03-23 15:37 ` [PATCH 6/8] rust: io: add `read_val` and `write_val` function on I/O view Gary Guo
2026-03-27  8:21   ` Andreas Hindborg
2026-03-27 12:19     ` Gary Guo

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