From: Andreas Hindborg <a.hindborg@kernel.org>
To: "Alice Ryhl" <aliceryhl@google.com>,
"Lorenzo Stoakes" <ljs@kernel.org>,
"Liam R. Howlett" <liam@infradead.org>,
"Miguel Ojeda" <ojeda@kernel.org>,
"Boqun Feng" <boqun@kernel.org>, "Gary Guo" <gary@garyguo.net>,
"Björn Roy Baron" <bjorn3_gh@protonmail.com>,
"Benno Lossin" <lossin@kernel.org>,
"Trevor Gross" <tmgross@umich.edu>,
"Danilo Krummrich" <dakr@kernel.org>,
"Will Deacon" <will@kernel.org>,
"Peter Zijlstra" <peterz@infradead.org>,
"Mark Rutland" <mark.rutland@arm.com>,
"Daniel Almeida" <daniel.almeida@collabora.com>,
"Tamir Duberstein" <tamird@kernel.org>,
"Alexandre Courbot" <acourbot@nvidia.com>,
"Onur Özkan" <work@onurozkan.dev>,
"Lyude Paul" <lyude@redhat.com>,
"Abdiel Janulgue" <abdiel.janulgue@gmail.com>,
"Robin Murphy" <robin.murphy@arm.com>,
"David Airlie" <airlied@gmail.com>,
"Simona Vetter" <simona@ffwll.ch>
Cc: linux-mm@kvack.org, rust-for-linux@vger.kernel.org,
linux-kernel@vger.kernel.org, driver-core@lists.linux.dev,
dri-devel@lists.freedesktop.org,
Andreas Hindborg <a.hindborg@kernel.org>
Subject: [PATCH v6 1/4] rust: io: forbid plain accesses racing with `SysMem`
Date: Fri, 13 Nov 2026 14:15:08 +0100 [thread overview]
Message-ID: <20261113-page-volatile-io-v6-1-5603e527c919@kernel.org> (raw)
In-Reply-To: <20261113-page-volatile-io-v6-0-5603e527c919@kernel.org>
`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 races with an access performed through the view, define what a
plain access and a race is, and update the safety comments on the
backend implementation and on the existing callers of `SysMem::new` to
rely on the new invariant.
Signed-off-by: Andreas Hindborg <a.hindborg@kernel.org>
---
rust/kernel/dma.rs | 8 +++++++-
rust/kernel/drm/gem/shmem.rs | 2 ++
rust/kernel/io.rs | 48 +++++++++++++++++++++++++++++---------------
3 files changed, 41 insertions(+), 17 deletions(-)
diff --git a/rust/kernel/dma.rs b/rust/kernel/dma.rs
index 580e1c3427bc..445b5859c60b 100644
--- a/rust/kernel/dma.rs
+++ b/rust/kernel/dma.rs
@@ -1237,7 +1237,13 @@ impl<'a, T: ?Sized + KnownSize> IoBase<'a> for &'a Coherent<'_, T> {
#[inline]
fn as_view(self) -> CoherentView<'a, Self::Target> {
CoherentView {
- // SAFETY: `cpu_addr` is valid and aligned kernel accessible memory.
+ // SAFETY:
+ // - Per type invariant, `cpu_addr` is valid and aligned kernel accessible memory.
+ // - Plain accesses to the region are only possible through `unsafe` APIs, that is
+ // `Coherent::{as_ref,as_mut}`, `CoherentView::{as_ref,as_mut}` and dereferencing
+ // the raw pointers returned by `Coherent::{as_ptr,as_mut_ptr}`, all of which require
+ // the caller to avoid racing with other accesses to the region, including accesses
+ // through this view.
cpu_addr: unsafe { SysMem::new(self.cpu_addr.as_ptr()) },
dma_addr: self.dma_addr,
}
diff --git a/rust/kernel/drm/gem/shmem.rs b/rust/kernel/drm/gem/shmem.rs
index 24cb5343e0db..e55c3d16729d 100644
--- a/rust/kernel/drm/gem/shmem.rs
+++ b/rust/kernel/drm/gem/shmem.rs
@@ -471,6 +471,8 @@ fn as_view(self) -> SysMem<'a, Region<SIZE>> {
// - `addr` is page-aligned, which satisfies `Region`'s 4-byte alignment requirement.
// - The memory remains valid until this `VMap` is dropped; since `self` is `&'a VMap`,
// the borrow prevents the `VMap` from being dropped for the lifetime `'a`.
+ // - `VMap` does not expose the mapping in any other way, so all accesses to the region
+ // from Rust go through views created here, and those are never plain accesses.
unsafe { SysMem::new(ptr) }
}
}
diff --git a/rust/kernel/io.rs b/rust/kernel/io.rs
index a877df11a03e..00bab24d613c 100644
--- a/rust/kernel/io.rs
+++ b/rust/kernel/io.rs
@@ -1452,7 +1452,8 @@ 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. Accesses through the new view are accesses through
+ // `view`, which no plain access races with per the invariants of `view`.
SysMem {
ptr,
phantom: PhantomData,
@@ -1470,8 +1471,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 +1483,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 +1522,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 +1534,32 @@ 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. Two accesses to the same
+/// memory race if at least one of them is a write and they are not ordered by synchronization. A
+/// plain access that races with another access 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 races with an access performed through this view.
pub struct SysMem<'a, T: ?Sized> {
ptr: *mut T,
phantom: PhantomData<&'a ()>,
@@ -1570,8 +1584,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 race with an access performed
+ /// through the returned view.
#[inline]
pub unsafe fn new(ptr: *mut T) -> Self {
// INVARIANT: Per safety requirement.
--
2.54.0
next prev parent reply other threads:[~2026-11-13 13:15 UTC|newest]
Thread overview: 5+ messages / expand[flat|nested] mbox.gz Atom feed top
2026-11-13 13:15 [PATCH v6 0/4] rust: byte-wise atomic copies between `Page` and shared memory Andreas Hindborg
2026-11-13 13:15 ` Andreas Hindborg [this message]
2026-11-13 13:15 ` [PATCH v6 2/4] rust: sync: atomic: add memcpy Andreas Hindborg
2026-11-13 13:15 ` [PATCH v6 3/4] rust: io: copy `SysMem` with `atomic::memcpy` Andreas Hindborg
2026-11-13 13:15 ` [PATCH v6 4/4] rust: page: add byte-wise atomic copy methods for `SysMem` Andreas Hindborg
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=20261113-page-volatile-io-v6-1-5603e527c919@kernel.org \
--to=a.hindborg@kernel.org \
--cc=abdiel.janulgue@gmail.com \
--cc=acourbot@nvidia.com \
--cc=airlied@gmail.com \
--cc=aliceryhl@google.com \
--cc=bjorn3_gh@protonmail.com \
--cc=boqun@kernel.org \
--cc=dakr@kernel.org \
--cc=daniel.almeida@collabora.com \
--cc=dri-devel@lists.freedesktop.org \
--cc=driver-core@lists.linux.dev \
--cc=gary@garyguo.net \
--cc=liam@infradead.org \
--cc=linux-kernel@vger.kernel.org \
--cc=linux-mm@kvack.org \
--cc=ljs@kernel.org \
--cc=lossin@kernel.org \
--cc=lyude@redhat.com \
--cc=mark.rutland@arm.com \
--cc=ojeda@kernel.org \
--cc=peterz@infradead.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=will@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
all inboxes | Powered by JetHome®