* [PATCH v5 0/4] rust: byte-wise atomic copies between `Page` and shared memory
@ 2026-11-10 14:44 Andreas Hindborg
2026-11-10 14:44 ` [PATCH v5 1/4] rust: io: require freedom from plain accesses for `SysMem` Andreas Hindborg
` (3 more replies)
0 siblings, 4 replies; 5+ messages in thread
From: Andreas Hindborg @ 2026-11-10 14:44 UTC (permalink / raw)
To: Alice Ryhl, Lorenzo Stoakes, Liam R. Howlett, Miguel Ojeda,
Boqun Feng, Gary Guo, Björn Roy Baron, Benno Lossin,
Trevor Gross, Danilo Krummrich, Will Deacon, Peter Zijlstra,
Mark Rutland, Daniel Almeida, Tamir Duberstein,
Alexandre Courbot, Onur Özkan, Lyude Paul
Cc: linux-mm, rust-for-linux, linux-kernel, driver-core, Andreas Hindborg
This series adds a way to copy data between a `Page` and memory that is
shared with actors outside the kernel, such as user space threads or
hardware, without introducing a data race.
The copy is built on `SysMem` from `kernel::io`, which already accesses
system memory with volatile or byte-wise atomic operations. The series
first makes the requirements of `SysMem` explicit by requiring freedom
from plain accesses for the lifetime of a view. It then adds
`atomic_per_byte_memcpy()`, a wrapper around the kernel's `memcpy()`
with explicit safety requirements, and routes the `SysMem` copies
through it. Finally, it adds `Page::{read,write}_bytewise_atomic`, which
take a `SysMem<'_, [u8]>` view of the shared buffer.
Signed-off-by: Andreas Hindborg <a.hindborg@kernel.org>
---
Changes in v5:
- Rebase on v7.4-rc2.
- Build on `SysMem` from `kernel::io` instead of adding raw pointer
based helpers to `Page` (Gary).
- Add a patch that requires freedom from plain accesses for `SysMem`
views (Gary).
- Route the `SysMemBackend` copies through `atomic_per_byte_memcpy()`,
so that the requirements of the `memcpy()` call are explicit.
- Let `Page::{read,write}_bytewise_atomic` take a `SysMem<'_, [u8]>`
instead of a raw pointer and a length.
- Drop the claim that `atomic_per_byte_memcpy()` is volatile.
- Add a non-overlap requirement to `atomic_per_byte_memcpy()`.
- Add examples to `Page::{read,write}_bytewise_atomic` (Alice).
- Link to v4: https://msgid.link/20260605-page-volatile-io-v4-0-0e217d9bf11a@kernel.org
Changes in v4:
- Split `atomic_per_byte_memcpy` out into its own patch (Boqun).
- Expand `atomic_per_byte_memcpy` documentation to explain the
concurrent-safe semantics and to note that the kernel's `memcpy()`
uses byte-wise atomic load/store instructions (Boqun).
- Clarify the src/dst asymmetry in the documentation of
`Page::{read,write}_bytewise_atomic` (Boqun).
- Use the "valid for atomic {reads,writes}" shorthand in the safety
lists of the new methods and of `atomic_per_byte_memcpy` (Benno).
- Drop the redundant data-race bullets that v3 added on
`Page::{read,write}_raw` (Miguel).
- Link to v3: https://msgid.link/20260213-page-volatile-io-v3-1-d60487b04d40@kernel.org
Changes in v3:
- Update documentation adn safety requirements for `Page::{read,write}_bytewise_atomic`.
- Update safety comments in `Page::{read,write}_bytewise_atomic`.
- Call the correct copy function in `Page::{read,write}_bytewise_atomic`.
- Link to v2: https://msgid.link/20260212-page-volatile-io-v2-1-a36cb97d15c2@kernel.org
Changes in v2:
- Rewrite patch with byte-wise atomic operations as foundation of operation.
- Update subject and commit message.
- Link to v1: https://lore.kernel.org/r/20260130-page-volatile-io-v1-1-19f3d3e8f265@kernel.org
---
Andreas Hindborg (4):
rust: io: require freedom from plain accesses for `SysMem`
rust: sync: atomic: add atomic_per_byte_memcpy
rust: io: copy `SysMem` with `atomic_per_byte_memcpy`
rust: page: add byte-wise atomic copy methods for `SysMem`
rust/kernel/io.rs | 72 +++++++++++++++++----------
rust/kernel/page.rs | 121 +++++++++++++++++++++++++++++++++++++++++++++
rust/kernel/sync/atomic.rs | 35 +++++++++++++
3 files changed, 203 insertions(+), 25 deletions(-)
---
base-commit: 7475ea8b58616b33c04afb976cf0fbaad9dd63fb
change-id: 20260130-page-volatile-io-05ff595507d3
Best regards,
--
Andreas Hindborg <a.hindborg@kernel.org>
^ permalink raw reply [flat|nested] 5+ messages in thread
* [PATCH v5 1/4] rust: io: require freedom from plain accesses for `SysMem`
2026-11-10 14:44 [PATCH v5 0/4] rust: byte-wise atomic copies between `Page` and shared memory Andreas Hindborg
@ 2026-11-10 14:44 ` Andreas Hindborg
2026-11-10 14:44 ` [PATCH v5 2/4] rust: sync: atomic: add atomic_per_byte_memcpy Andreas Hindborg
` (2 subsequent siblings)
3 siblings, 0 replies; 5+ messages in thread
From: Andreas Hindborg @ 2026-11-10 14:44 UTC (permalink / raw)
To: Alice Ryhl, Lorenzo Stoakes, Liam R. Howlett, Miguel Ojeda,
Boqun Feng, Gary Guo, Björn Roy Baron, Benno Lossin,
Trevor Gross, Danilo Krummrich, Will Deacon, Peter Zijlstra,
Mark Rutland, Daniel Almeida, Tamir Duberstein,
Alexandre Courbot, Onur Özkan, Lyude Paul
Cc: linux-mm, rust-for-linux, linux-kernel, driver-core, Andreas Hindborg
`SysMem` accesses memory with volatile loads and stores and with the
kernel's `memcpy()`. Both are well-defined when racing with hardware or
user space, and when racing with atomic or volatile accesses from other
kernel code. However, if they race with plain, non-atomic accesses from
kernel code, they are considered a data race, and therefore undefined
behavior.
The invariants of `SysMem` only require the region to be valid and
aligned kernel accessible memory. Nothing stops a caller from creating
a `SysMem` over memory that other kernel code accesses with plain loads
and stores at the same time.
Make the requirement explicit. Require that no plain access to the
region is performed for the lifetime of the view, define what a plain
access is, and update the safety comments on the backend implementation
to rely on the new invariant.
Signed-off-by: Andreas Hindborg <a.hindborg@kernel.org>
---
rust/kernel/io.rs | 46 ++++++++++++++++++++++++++++++----------------
1 file changed, 30 insertions(+), 16 deletions(-)
diff --git a/rust/kernel/io.rs b/rust/kernel/io.rs
index a877df11a03e..3bc4be35e355 100644
--- a/rust/kernel/io.rs
+++ b/rust/kernel/io.rs
@@ -1452,7 +1452,7 @@ unsafe fn project_view<'a, T: ?Sized + KnownSize, U: ?Sized + KnownSize>(
ptr: *mut U,
) -> Self::View<'a, U> {
// INVARIANT: Per safety requirement, `ptr` is projection from `view`, so it is also a valid
- // kernel accessible memory region.
+ // kernel accessible memory region that is free of plain accesses.
SysMem {
ptr,
phantom: PhantomData,
@@ -1470,8 +1470,9 @@ fn io_read(view: SysMem<'_, $ty>) -> $ty {
// SAFETY:
// - Per type invariant, `ptr` is valid 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.
+ // - Per type invariant, no plain access races with this read. Racing with atomic
+ // or volatile accesses from other CPUs is not sound per Rust rules, but this is
+ // allowed per LKMM.
// - The macro is only used on primitives so all bit patterns are valid.
unsafe { view.ptr.read_volatile() }
}
@@ -1481,8 +1482,9 @@ fn io_write(view: SysMem<'_, $ty>, value: $ty) {
// SAFETY:
// - Per type invariant, `ptr` is valid 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.
+ // - Per type invariant, no plain access races with this write. Racing with atomic
+ // or volatile accesses from other CPUs is not sound per Rust rules, but this is
+ // allowed per LKMM.
unsafe { view.ptr.write_volatile(value) }
}
}
@@ -1519,8 +1521,9 @@ fn copy_read<T: FromBytes>(view: Self::View<'_, T>) -> T {
// SAFETY:
// - Per type invariant, `ptr` is valid 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.
+ // - Per type invariant, no plain access races with this read. Racing with atomic or
+ // volatile accesses from other CPUs is not sound per Rust rules, but this is allowed
+ // per LKMM.
// - `T: FromBytes` so all bit patterns are valid.
unsafe { view.ptr.read_volatile() }
}
@@ -1530,22 +1533,31 @@ fn copy_write<T: IntoBytes>(view: Self::View<'_, T>, value: T) {
// SAFETY:
// - Per type invariant, `ptr` is valid 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.
+ // - Per type invariant, no plain access races with this write. Racing with atomic or
+ // volatile accesses from other CPUs is not sound per Rust rules, but this is allowed
+ // per LKMM.
unsafe { view.ptr.write_volatile(value) }
}
}
/// A view of a system memory region.
///
-/// Provides `Io` trait implementation for kernel virtual address ranges,
-/// using volatile read/write to safely access shared memory that may be
-/// concurrently accessed by external hardware.
+/// Provides `Io` trait implementation for kernel virtual address ranges, using volatile or
+/// byte-wise atomic accesses to safely access shared memory that may be concurrently accessed by
+/// external actors, such as hardware or user space threads, or by other kernel code using atomic or
+/// volatile accesses.
+///
+/// Accesses through a `SysMem` are never plain accesses. A plain access is a non-atomic,
+/// non-volatile access performed by kernel code, whether in C or in Rust. A plain access that
+/// races with any other access to the same memory is a data race, and therefore undefined
+/// behavior. Accesses performed by hardware or by user space are not plain accesses in this sense.
+/// Racing with them is well-defined, but the data observed may be arbitrary.
///
/// # Invariants
///
-/// `self.ptr.addr() .. self.ptr.addr() + KnownSize::size(self.ptr)` is valid and aligned kernel
-/// accessible memory region for the lifetime `'a`.
+/// - `self.ptr.addr() .. self.ptr.addr() + KnownSize::size(self.ptr)` is valid and aligned kernel
+/// accessible memory region for the lifetime `'a`.
+/// - No plain access to that memory region is performed for the lifetime `'a`.
pub struct SysMem<'a, T: ?Sized> {
ptr: *mut T,
phantom: PhantomData<&'a ()>,
@@ -1570,8 +1582,10 @@ impl<'a, T: ?Sized> SysMem<'a, T> {
///
/// # Safety
///
- /// `ptr.addr() .. ptr.addr() + KnownSize::size(ptr)` must be valid and aligned kernel
- /// accessible memory region for the lifetime `'a`.
+ /// - `ptr.addr() .. ptr.addr() + KnownSize::size(ptr)` must be valid and aligned kernel
+ /// accessible memory region for the lifetime `'a`.
+ /// - No plain access (see [`SysMem`]) to that memory region may be performed for the lifetime
+ /// `'a`.
#[inline]
pub unsafe fn new(ptr: *mut T) -> Self {
// INVARIANT: Per safety requirement.
--
2.54.0
^ permalink raw reply [flat|nested] 5+ messages in thread
* [PATCH v5 2/4] rust: sync: atomic: add atomic_per_byte_memcpy
2026-11-10 14:44 [PATCH v5 0/4] rust: byte-wise atomic copies between `Page` and shared memory Andreas Hindborg
2026-11-10 14:44 ` [PATCH v5 1/4] rust: io: require freedom from plain accesses for `SysMem` Andreas Hindborg
@ 2026-11-10 14:44 ` Andreas Hindborg
2026-11-10 14:44 ` [PATCH v5 3/4] rust: io: copy `SysMem` with `atomic_per_byte_memcpy` Andreas Hindborg
2026-11-10 14:44 ` [PATCH v5 4/4] rust: page: add byte-wise atomic copy methods for `SysMem` Andreas Hindborg
3 siblings, 0 replies; 5+ messages in thread
From: Andreas Hindborg @ 2026-11-10 14:44 UTC (permalink / raw)
To: Alice Ryhl, Lorenzo Stoakes, Liam R. Howlett, Miguel Ojeda,
Boqun Feng, Gary Guo, Björn Roy Baron, Benno Lossin,
Trevor Gross, Danilo Krummrich, Will Deacon, Peter Zijlstra,
Mark Rutland, Daniel Almeida, Tamir Duberstein,
Alexandre Courbot, Onur Özkan, Lyude Paul
Cc: linux-mm, rust-for-linux, linux-kernel, driver-core, Andreas Hindborg
Add a helper to copy `len` bytes from `src` to `dst` using byte-wise atomic
memory operations. This is the concurrent-safe counterpart of
`core::ptr::copy()` (the equivalent of standard C's `memcpy()`). Because of
the atomicity at byte level, when the copy races with another concurrent
atomic access (or when a normal read races with an atomic read), or with an
external access (from DMA or userspace), the behavior of this function is
defined: the memory is copied at (at least) byte granularity.
The helper is the building block for higher-level copy operations that need
to remain defined under racing accesses, such as the `SysMem` I/O backend,
which copies to and from memory that may be shared with hardware or user
space.
The implementation forwards to the kernel's `memcpy()`, which is
implemented in a way that byte-wise atomic memory load/store instructions
are used.
Signed-off-by: Andreas Hindborg <a.hindborg@kernel.org>
---
rust/kernel/sync/atomic.rs | 35 +++++++++++++++++++++++++++++++++++
1 file changed, 35 insertions(+)
diff --git a/rust/kernel/sync/atomic.rs b/rust/kernel/sync/atomic.rs
index 6d27898add42..cbcb081d8674 100644
--- a/rust/kernel/sync/atomic.rs
+++ b/rust/kernel/sync/atomic.rs
@@ -848,3 +848,38 @@ pub unsafe fn cmpxchg<T: AtomicType, Ordering: ordering::Ordering>(
// per LKMM.
unsafe { Atomic::from_ptr(ptr) }.cmpxchg(old, new, o)
}
+
+/// Copy `len` bytes from `src` to `dst` using byte-wise atomic operations.
+///
+/// This is the concurrent-safe counterpart of `core::ptr::copy()` (the equivalent of standard
+/// C's `memcpy()`). Because of the atomicity at byte level, when racing with another concurrent
+/// atomic access (or when a normal read races with an atomic read), or with an external access
+/// (from DMA or userspace), the behavior of this function is defined: the memory is copied at
+/// (at least) byte granularity.
+///
+/// Implementation note: this is currently implemented by the kernel's `memcpy()`, which is
+/// implemented in a way such that byte-wise atomic memory load/store instructions are used.
+///
+/// # Safety
+///
+/// Callers must ensure that:
+///
+/// - `src` is valid for atomic reads for `len` bytes for the duration of the call.
+/// - `dst` is valid for atomic writes for `len` bytes for the duration of the call.
+/// - The memory regions `src .. src + len` and `dst .. dst + len` do not overlap.
+pub unsafe fn atomic_per_byte_memcpy(src: *const u8, dst: *mut u8, len: usize) {
+ // SAFETY: By the safety requirements of this function, the following operation will not:
+ // - Trap.
+ // - Invalidate any reference invariants.
+ // - Race with any operation by the Rust AM, as `bindings::memcpy` is a byte-wise atomic
+ // operation and all operations by the Rust AM to the involved memory areas use byte-wise
+ // atomic semantics.
+ // - Copy between overlapping regions, which `memcpy()` does not support.
+ unsafe {
+ bindings::memcpy(
+ dst.cast::<kernel::ffi::c_void>(),
+ src.cast::<kernel::ffi::c_void>(),
+ len,
+ )
+ };
+}
--
2.54.0
^ permalink raw reply [flat|nested] 5+ messages in thread
* [PATCH v5 3/4] rust: io: copy `SysMem` with `atomic_per_byte_memcpy`
2026-11-10 14:44 [PATCH v5 0/4] rust: byte-wise atomic copies between `Page` and shared memory Andreas Hindborg
2026-11-10 14:44 ` [PATCH v5 1/4] rust: io: require freedom from plain accesses for `SysMem` Andreas Hindborg
2026-11-10 14:44 ` [PATCH v5 2/4] rust: sync: atomic: add atomic_per_byte_memcpy Andreas Hindborg
@ 2026-11-10 14:44 ` Andreas Hindborg
2026-11-10 14:44 ` [PATCH v5 4/4] rust: page: add byte-wise atomic copy methods for `SysMem` Andreas Hindborg
3 siblings, 0 replies; 5+ messages in thread
From: Andreas Hindborg @ 2026-11-10 14:44 UTC (permalink / raw)
To: Alice Ryhl, Lorenzo Stoakes, Liam R. Howlett, Miguel Ojeda,
Boqun Feng, Gary Guo, Björn Roy Baron, Benno Lossin,
Trevor Gross, Danilo Krummrich, Will Deacon, Peter Zijlstra,
Mark Rutland, Daniel Almeida, Tamir Duberstein,
Alexandre Courbot, Onur Özkan, Lyude Paul
Cc: linux-mm, rust-for-linux, linux-kernel, driver-core, Andreas Hindborg
The `IoCopyable` implementation for `SysMemBackend` calls
`bindings::memcpy()` directly. The comments say that this is done "for
volatile", but the requirements of the call are not stated anywhere. The
reason the call is sound is that the kernel's `memcpy()` is byte-wise
atomic, and that neither side of the copy is subject to plain accesses.
Call `atomic_per_byte_memcpy()` instead, so that the requirements are
spelled out at the call site and checked against the invariants of `SysMem`
and the safety requirements of `IoCopyable`.
Signed-off-by: Andreas Hindborg <a.hindborg@kernel.org>
---
rust/kernel/io.rs | 26 +++++++++++++++++---------
1 file changed, 17 insertions(+), 9 deletions(-)
diff --git a/rust/kernel/io.rs b/rust/kernel/io.rs
index 3bc4be35e355..b51cfc04e3ca 100644
--- a/rust/kernel/io.rs
+++ b/rust/kernel/io.rs
@@ -21,7 +21,8 @@
ptr::{
Alignment,
KnownSize, //
- }, //
+ },
+ sync::atomic::atomic_per_byte_memcpy, //
};
#[cfg(CONFIG_HAS_IOMEM)]
@@ -1500,20 +1501,27 @@ fn io_write(view: SysMem<'_, $ty>, value: $ty) {
impl IoCopyable for SysMemBackend {
#[inline]
unsafe fn copy_from_io(view: Self::View<'_, [u8]>, buffer: *mut u8) {
- // Use `bindings::memcpy` instead of `copy_nonoverlapping` for volatile.
// SAFETY:
- // - `view.ptr` is in CPU address space and valid for read.
- // - `buffer` is valid for write for `view.size()` bytes which is equal to `view.ptr.len()`.
- unsafe { bindings::memcpy(buffer.cast(), view.ptr.cast(), view.ptr.len()) };
+ // - Per type invariant, `view.ptr` is a valid kernel accessible memory region of
+ // `view.ptr.len()` bytes that is free of plain accesses, so it is valid for atomic reads.
+ // - Per the safety requirements of this function, `buffer` is valid for volatile writes for
+ // `view.size()` bytes, which is equal to `view.ptr.len()`, so it is valid for atomic
+ // writes.
+ // - Per the safety requirements of this function, `buffer` does not overlap with `view`.
+ unsafe { atomic_per_byte_memcpy(view.ptr.cast(), buffer, view.ptr.len()) };
}
#[inline]
unsafe fn copy_to_io(view: Self::View<'_, [u8]>, buffer: *const u8) {
- // Use `bindings::memcpy` instead of `copy_nonoverlapping` for volatile.
// SAFETY:
- // - `view.ptr` is in CPU address space and valid for write.
- // - `buffer` is valid for read for `view.size()` bytes which is equal to `view.ptr.len()`.
- unsafe { bindings::memcpy(view.ptr.cast(), buffer.cast(), view.ptr.len()) };
+ // - Per the safety requirements of this function, `buffer` is valid for volatile reads for
+ // `view.size()` bytes, which is equal to `view.ptr.len()`, so it is valid for atomic
+ // reads.
+ // - Per type invariant, `view.ptr` is a valid kernel accessible memory region of
+ // `view.ptr.len()` bytes that is free of plain accesses, so it is valid for atomic
+ // writes.
+ // - Per the safety requirements of this function, `buffer` does not overlap with `view`.
+ unsafe { atomic_per_byte_memcpy(buffer, view.ptr.cast(), view.ptr.len()) };
}
#[inline]
--
2.54.0
^ permalink raw reply [flat|nested] 5+ messages in thread
* [PATCH v5 4/4] rust: page: add byte-wise atomic copy methods for `SysMem`
2026-11-10 14:44 [PATCH v5 0/4] rust: byte-wise atomic copies between `Page` and shared memory Andreas Hindborg
` (2 preceding siblings ...)
2026-11-10 14:44 ` [PATCH v5 3/4] rust: io: copy `SysMem` with `atomic_per_byte_memcpy` Andreas Hindborg
@ 2026-11-10 14:44 ` Andreas Hindborg
3 siblings, 0 replies; 5+ messages in thread
From: Andreas Hindborg @ 2026-11-10 14:44 UTC (permalink / raw)
To: Alice Ryhl, Lorenzo Stoakes, Liam R. Howlett, Miguel Ojeda,
Boqun Feng, Gary Guo, Björn Roy Baron, Benno Lossin,
Trevor Gross, Danilo Krummrich, Will Deacon, Peter Zijlstra,
Mark Rutland, Daniel Almeida, Tamir Duberstein,
Alexandre Courbot, Onur Özkan, Lyude Paul
Cc: linux-mm, rust-for-linux, linux-kernel, driver-core, Andreas Hindborg
When copying data between a `Page` and a buffer that is mapped to user
space, or otherwise shared outside the kernel, it is impossible to
guarantee absence of concurrent memory operations on that buffer. Copying
with `Page::{read,write}_raw` would be a data race in that case.
Add `Page::{read,write}_bytewise_atomic`, which take a `SysMem` view of the
shared buffer. The accesses to the buffer go through `SysMemBackend`, which
uses byte-wise atomic operations, so they are well-defined when racing with
external actors or with atomic accesses from other kernel code. The page
side of the copy uses plain accesses, so the page itself must not be
accessed concurrently, as is the case for `Page::{read,write}_raw`.
Signed-off-by: Andreas Hindborg <a.hindborg@kernel.org>
---
rust/kernel/page.rs | 121 ++++++++++++++++++++++++++++++++++++++++++++++++++++
1 file changed, 121 insertions(+)
diff --git a/rust/kernel/page.rs b/rust/kernel/page.rs
index 1c0796ea229f..92486d312b0c 100644
--- a/rust/kernel/page.rs
+++ b/rust/kernel/page.rs
@@ -12,6 +12,12 @@
code::*,
Result, //
},
+ io::{
+ Io,
+ IoCopyable,
+ SysMem,
+ SysMemBackend, //
+ },
uaccess::UserSliceReader, //
};
use core::{
@@ -305,6 +311,58 @@ pub unsafe fn read_raw(&self, dst: *mut u8, offset: usize, len: usize) -> Result
})
}
+ /// Maps the page and reads from it into the given system memory region using byte-wise atomic
+ /// memory operations.
+ ///
+ /// This method will perform bounds checks on the page offset. If `offset .. offset+dst.len()`
+ /// goes outside of the page, then this call returns [`EINVAL`].
+ ///
+ /// The writes to `dst` are byte-wise atomic, so they are well-defined even if `dst` is
+ /// concurrently accessed by hardware, by user space, or by other kernel code using atomic or
+ /// volatile accesses. The reads from the page are plain accesses, so the page must not be
+ /// written to concurrently.
+ ///
+ /// # Safety
+ ///
+ /// Callers must ensure that:
+ ///
+ /// - This call does not race with a write to the source page that overlaps with this read.
+ /// - The memory region of `dst` does not overlap with the memory of the page.
+ ///
+ /// # Examples
+ ///
+ /// ```
+ /// use kernel::{
+ /// io::SysMem,
+ /// page::Page,
+ /// };
+ ///
+ /// let page = Page::alloc_page(GFP_KERNEL | __GFP_ZERO)?;
+ /// let mut buf = [0xffu8; 16];
+ ///
+ /// // SAFETY: `buf` is a local array that outlives `dst`, and it is not accessed in any other
+ /// // way while `dst` is alive.
+ /// let dst = unsafe { SysMem::new(&raw mut buf as *mut [u8]) };
+ ///
+ /// // SAFETY: `page` is not written to during this call, and `buf` is not part of the page.
+ /// unsafe { page.read_bytewise_atomic(dst, 0)? };
+ /// assert_eq!(buf, [0u8; 16]);
+ /// # Ok::<(), Error>(())
+ /// ```
+ pub unsafe fn read_bytewise_atomic(&self, dst: SysMem<'_, [u8]>, offset: usize) -> Result {
+ self.with_pointer_into_page(offset, dst.len(), move |src| {
+ // SAFETY:
+ // - If `with_pointer_into_page` calls into this closure, then it has performed a
+ // bounds check and guarantees that `src` is valid for `dst.len()` bytes, which is
+ // equal to `dst.size()`.
+ // - By the safety requirements of this function, the page is not written to during
+ // this call, so `src` is valid for volatile reads.
+ // - By the safety requirements of this function, `src` does not overlap with `dst`.
+ unsafe { SysMemBackend::copy_to_io(dst, src) };
+ Ok(())
+ })
+ }
+
/// Maps the page and writes into it from the given buffer.
///
/// This method will perform bounds checks on the page offset. If `offset .. offset+len` goes
@@ -326,6 +384,69 @@ pub unsafe fn write_raw(&self, src: *const u8, offset: usize, len: usize) -> Res
})
}
+ /// Maps the page and writes into it from the given system memory region using byte-wise atomic
+ /// memory operations.
+ ///
+ /// This method will perform bounds checks on the page offset. If `offset .. offset+src.len()`
+ /// goes outside of the page, then this call returns [`EINVAL`].
+ ///
+ /// The reads from `src` are byte-wise atomic, so they are well-defined even if `src` is
+ /// concurrently accessed by hardware, by user space, or by other kernel code using atomic or
+ /// volatile accesses. The writes to the page are plain accesses, so the page must not be read
+ /// from or written to concurrently.
+ ///
+ /// # Safety
+ ///
+ /// Callers must ensure that:
+ ///
+ /// - This call does not race with a read or write to the destination page that overlaps with
+ /// this write.
+ /// - The memory region of `src` does not overlap with the memory of the page.
+ ///
+ /// # Examples
+ ///
+ /// ```
+ /// use kernel::{
+ /// io::SysMem,
+ /// page::Page,
+ /// };
+ ///
+ /// let page = Page::alloc_page(GFP_KERNEL)?;
+ /// let mut data = [0xabu8; 16];
+ /// let mut buf = [0u8; 16];
+ ///
+ /// // SAFETY: `data` and `buf` are local arrays that outlive the views, and they are not
+ /// // accessed in any other way while the views are alive.
+ /// let (src, dst) = unsafe {
+ /// (
+ /// SysMem::new(&raw mut data as *mut [u8]),
+ /// SysMem::new(&raw mut buf as *mut [u8]),
+ /// )
+ /// };
+ ///
+ /// // SAFETY: `page` is not accessed in any other way during these calls, and neither `data`
+ /// // nor `buf` is part of the page.
+ /// unsafe {
+ /// page.write_bytewise_atomic(src, 0)?;
+ /// page.read_bytewise_atomic(dst, 0)?;
+ /// }
+ /// assert_eq!(buf, data);
+ /// # Ok::<(), Error>(())
+ /// ```
+ pub unsafe fn write_bytewise_atomic(&self, src: SysMem<'_, [u8]>, offset: usize) -> Result {
+ self.with_pointer_into_page(offset, src.len(), move |dst| {
+ // SAFETY:
+ // - If `with_pointer_into_page` calls into this closure, then it has performed a
+ // bounds check and guarantees that `dst` is valid for `src.len()` bytes, which is
+ // equal to `src.size()`.
+ // - By the safety requirements of this function, the page is not read from or written
+ // to during this call, so `dst` is valid for volatile writes.
+ // - By the safety requirements of this function, `dst` does not overlap with `src`.
+ unsafe { SysMemBackend::copy_from_io(src, dst) };
+ Ok(())
+ })
+ }
+
/// Maps the page and zeroes the given slice.
///
/// This method will perform bounds checks on the page offset. If `offset .. offset+len` goes
--
2.54.0
^ permalink raw reply [flat|nested] 5+ messages in thread
end of thread, other threads:[~2026-11-10 14:45 UTC | newest]
Thread overview: 5+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2026-11-10 14:44 [PATCH v5 0/4] rust: byte-wise atomic copies between `Page` and shared memory Andreas Hindborg
2026-11-10 14:44 ` [PATCH v5 1/4] rust: io: require freedom from plain accesses for `SysMem` Andreas Hindborg
2026-11-10 14:44 ` [PATCH v5 2/4] rust: sync: atomic: add atomic_per_byte_memcpy Andreas Hindborg
2026-11-10 14:44 ` [PATCH v5 3/4] rust: io: copy `SysMem` with `atomic_per_byte_memcpy` Andreas Hindborg
2026-11-10 14:44 ` [PATCH v5 4/4] rust: page: add byte-wise atomic copy methods for `SysMem` Andreas Hindborg
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®