From 962cdb95b6753c9ef19f2163809091e8baa9085f Mon Sep 17 00:00:00 2001 From: Marko Turk Date: Wed, 10 Dec 2025 12:25:35 +0100 Subject: rust: pci: document Bar's endianness conversion Document that the Bar's MMIO backend always assumes little-endian devices and that its operations automatically convert to CPU endianness. Signed-off-by: Marko Turk Link: https://lore.kernel.org/lkml/DE7F6RR1NAKW.3DJYO44O73941@kernel.org/ Link: https://patch.msgid.link/20251210112503.62925-1-mt@markoturk.info [ Drop unrelated spelling fix. - Danilo ] Signed-off-by: Danilo Krummrich --- rust/kernel/pci/io.rs | 3 +++ 1 file changed, 3 insertions(+) (limited to 'rust/kernel') diff --git a/rust/kernel/pci/io.rs b/rust/kernel/pci/io.rs index 0d55c3139b6f..c250b7c29d2d 100644 --- a/rust/kernel/pci/io.rs +++ b/rust/kernel/pci/io.rs @@ -18,6 +18,9 @@ use core::ops::Deref; /// A PCI BAR to perform I/O-Operations on. /// +/// I/O backend assumes that the device is little-endian and will automatically +/// convert from little-endian to CPU endianness. +/// /// # Invariants /// /// `Bar` always holds an `IoRaw` inststance that holds a valid pointer to the start of the I/O -- cgit v1.2.3 From a625a898ea8f74ca0bb55c6e0c2fcff80edb1068 Mon Sep 17 00:00:00 2001 From: Shankari Anand Date: Sun, 23 Nov 2025 14:54:32 +0530 Subject: rust: drm: Update AlwaysRefCounted imports to use sync::aref Update call sites to import `AlwaysRefCounted` from `sync::aref` instead of `types`. This aligns with the ongoing effort to move `ARef` and `AlwaysRefCounted` to sync. Suggested-by: Benno Lossin Link: https://github.com/Rust-for-Linux/linux/issues/1173 Signed-off-by: Shankari Anand Link: https://patch.msgid.link/20251123092438.182251-5-shankari.ak0208@gmail.com Signed-off-by: Alice Ryhl --- rust/kernel/drm/gem/mod.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'rust/kernel') diff --git a/rust/kernel/drm/gem/mod.rs b/rust/kernel/drm/gem/mod.rs index a7f682e95c01..76e6c40d525e 100644 --- a/rust/kernel/drm/gem/mod.rs +++ b/rust/kernel/drm/gem/mod.rs @@ -253,7 +253,7 @@ impl Object { } // SAFETY: Instances of `Object` are always reference-counted. -unsafe impl crate::types::AlwaysRefCounted for Object { +unsafe impl crate::sync::aref::AlwaysRefCounted for Object { fn inc_ref(&self) { // SAFETY: The existence of a shared reference guarantees that the refcount is non-zero. unsafe { bindings::drm_gem_object_get(self.as_raw()) }; -- cgit v1.2.3 From 97cf6bc0abd381fd84e5d8e978322a62a58fb00e Mon Sep 17 00:00:00 2001 From: Atharv Dubey Date: Mon, 1 Dec 2025 20:57:58 +0530 Subject: rust: drm: use `pin_init::zeroed()` for file operations initialization Replace the manual `unsafe { core::mem::zeroed() }` initialization of `bindings::file_operations` with `pin_init::zeroed()`. This removes the explicit unsafe Signed-off-by: Atharv Dubey Link: https://patch.msgid.link/20251201152759.16429-1-atharvd440@gmail.com Signed-off-by: Alice Ryhl --- rust/kernel/drm/gem/mod.rs | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) (limited to 'rust/kernel') diff --git a/rust/kernel/drm/gem/mod.rs b/rust/kernel/drm/gem/mod.rs index 76e6c40d525e..bdaac839dacc 100644 --- a/rust/kernel/drm/gem/mod.rs +++ b/rust/kernel/drm/gem/mod.rs @@ -293,9 +293,7 @@ impl AllocImpl for Object { } pub(super) const fn create_fops() -> bindings::file_operations { - // SAFETY: As by the type invariant, it is safe to initialize `bindings::file_operations` - // zeroed. - let mut fops: bindings::file_operations = unsafe { core::mem::zeroed() }; + let mut fops: bindings::file_operations = pin_init::zeroed(); fops.owner = core::ptr::null_mut(); fops.open = Some(bindings::drm_open); -- cgit v1.2.3 From 2e2b4135d1cb32fc310f21e395ee7313a3681bee Mon Sep 17 00:00:00 2001 From: Shankari Anand Date: Sun, 23 Nov 2025 14:54:31 +0530 Subject: rust: device: Update ARef and AlwaysRefCounted imports from sync::aref Update call sites to import `ARef` and `AlwaysRefCounted` from `sync::aref` instead of `types`. This aligns with the ongoing effort to move `ARef` and `AlwaysRefCounted` to sync. Suggested-by: Benno Lossin Link: https://github.com/Rust-for-Linux/linux/issues/1173 Signed-off-by: Shankari Anand Link: https://patch.msgid.link/20251123092438.182251-4-shankari.ak0208@gmail.com Signed-off-by: Danilo Krummrich --- rust/kernel/device.rs | 4 ++-- rust/kernel/device/property.rs | 5 +++-- 2 files changed, 5 insertions(+), 4 deletions(-) (limited to 'rust/kernel') diff --git a/rust/kernel/device.rs b/rust/kernel/device.rs index c79be2e2bfe3..21bde8d95185 100644 --- a/rust/kernel/device.rs +++ b/rust/kernel/device.rs @@ -158,7 +158,7 @@ static_assert!(core::mem::size_of::() >= core::mem::size_ /// `bindings::device::release` is valid to be called from any thread, hence `ARef` can be /// dropped from any thread. /// -/// [`AlwaysRefCounted`]: kernel::types::AlwaysRefCounted +/// [`AlwaysRefCounted`]: kernel::sync::aref::AlwaysRefCounted /// [`impl_device_context_deref`]: kernel::impl_device_context_deref /// [`pci::Device`]: kernel::pci::Device /// [`platform::Device`]: kernel::platform::Device @@ -540,7 +540,7 @@ pub trait DeviceContext: private::Sealed {} /// [`Device`]. It is the only [`DeviceContext`] for which it is valid to implement /// [`AlwaysRefCounted`] for. /// -/// [`AlwaysRefCounted`]: kernel::types::AlwaysRefCounted +/// [`AlwaysRefCounted`]: kernel::sync::aref::AlwaysRefCounted pub struct Normal; /// The [`Core`] context is the context of a bus specific device when it appears as argument of diff --git a/rust/kernel/device/property.rs b/rust/kernel/device/property.rs index 3a332a8c53a9..413221817ef1 100644 --- a/rust/kernel/device/property.rs +++ b/rust/kernel/device/property.rs @@ -14,7 +14,8 @@ use crate::{ fmt, prelude::*, str::{CStr, CString}, - types::{ARef, Opaque}, + sync::aref::ARef, + types::Opaque, }; /// A reference-counted fwnode_handle. @@ -359,7 +360,7 @@ impl fmt::Debug for FwNodeReferenceArgs { } // SAFETY: Instances of `FwNode` are always reference-counted. -unsafe impl crate::types::AlwaysRefCounted for FwNode { +unsafe impl crate::sync::aref::AlwaysRefCounted for FwNode { fn inc_ref(&self) { // SAFETY: The existence of a shared reference guarantees that the // refcount is non-zero. -- cgit v1.2.3 From 2da67beda68776842fd0a26f2374e42a5e9b12c8 Mon Sep 17 00:00:00 2001 From: Shankari Anand Date: Sun, 23 Nov 2025 14:54:35 +0530 Subject: rust: scatterlist: Update ARef imports to use sync::aref Update call sites in `scatterlist.rs` to import `ARef` from `sync::aref` instead of `types`. This aligns with the ongoing effort to move `ARef` and `AlwaysRefCounted` to sync. Suggested-by: Benno Lossin Link: https://github.com/Rust-for-Linux/linux/issues/1173 Signed-off-by: Shankari Anand Link: https://patch.msgid.link/20251123092438.182251-8-shankari.ak0208@gmail.com [ Change subject prefix to from 'kernel' to 'scatterlist'. - Danilo ] Signed-off-by: Danilo Krummrich --- rust/kernel/scatterlist.rs | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) (limited to 'rust/kernel') diff --git a/rust/kernel/scatterlist.rs b/rust/kernel/scatterlist.rs index 196fdb9a75e7..b83c468b5c63 100644 --- a/rust/kernel/scatterlist.rs +++ b/rust/kernel/scatterlist.rs @@ -38,7 +38,8 @@ use crate::{ io::ResourceSize, page, prelude::*, - types::{ARef, Opaque}, + sync::aref::ARef, + types::Opaque, }; use core::{ops::Deref, ptr::NonNull}; -- cgit v1.2.3 From b0655377aa5a410df02d89170c20141a1a5bbc28 Mon Sep 17 00:00:00 2001 From: Tamir Duberstein Date: Mon, 22 Dec 2025 13:15:58 +0100 Subject: rust: regulator: replace `kernel::c_str!` with C-Strings C-String literals were added in Rust 1.77. Replace instances of `kernel::c_str!` with C-String literals where possible. Signed-off-by: Tamir Duberstein Reviewed-by: Daniel Almeida Link: https://patch.msgid.link/20251222-cstr-regulator-v1-1-430e3d517025@gmail.com Signed-off-by: Mark Brown --- rust/kernel/regulator.rs | 9 +++------ 1 file changed, 3 insertions(+), 6 deletions(-) (limited to 'rust/kernel') diff --git a/rust/kernel/regulator.rs b/rust/kernel/regulator.rs index 2c44827ad0b7..4f7837c7e53a 100644 --- a/rust/kernel/regulator.rs +++ b/rust/kernel/regulator.rs @@ -122,12 +122,11 @@ pub fn devm_enable_optional(dev: &Device, name: &CStr) -> Result { /// /// ``` /// # use kernel::prelude::*; -/// # use kernel::c_str; /// # use kernel::device::Device; /// # use kernel::regulator::{Voltage, Regulator, Disabled, Enabled}; /// fn enable(dev: &Device, min_voltage: Voltage, max_voltage: Voltage) -> Result { /// // Obtain a reference to a (fictitious) regulator. -/// let regulator: Regulator = Regulator::::get(dev, c_str!("vcc"))?; +/// let regulator: Regulator = Regulator::::get(dev, c"vcc")?; /// /// // The voltage can be set before enabling the regulator if needed, e.g.: /// regulator.set_voltage(min_voltage, max_voltage)?; @@ -166,12 +165,11 @@ pub fn devm_enable_optional(dev: &Device, name: &CStr) -> Result { /// /// ``` /// # use kernel::prelude::*; -/// # use kernel::c_str; /// # use kernel::device::Device; /// # use kernel::regulator::{Voltage, Regulator, Enabled}; /// fn enable(dev: &Device) -> Result { /// // Obtain a reference to a (fictitious) regulator and enable it. -/// let regulator: Regulator = Regulator::::get(dev, c_str!("vcc"))?; +/// let regulator: Regulator = Regulator::::get(dev, c"vcc")?; /// /// // Dropping an enabled regulator will disable it. The refcount will be /// // decremented. @@ -193,13 +191,12 @@ pub fn devm_enable_optional(dev: &Device, name: &CStr) -> Result { /// /// ``` /// # use kernel::prelude::*; -/// # use kernel::c_str; /// # use kernel::device::{Bound, Device}; /// # use kernel::regulator; /// fn enable(dev: &Device) -> Result { /// // Obtain a reference to a (fictitious) regulator and enable it. This /// // call only returns whether the operation succeeded. -/// regulator::devm_enable(dev, c_str!("vcc"))?; +/// regulator::devm_enable(dev, c"vcc")?; /// /// // The regulator will be disabled and put when `dev` is unbound. /// Ok(()) -- cgit v1.2.3 From 9202cef05d6b61a03475b744c7f0622cd8be8e90 Mon Sep 17 00:00:00 2001 From: Danilo Krummrich Date: Thu, 18 Dec 2025 17:56:11 +0100 Subject: rust: debugfs: use "kernel vertical" style for imports Convert all imports in the debugfs Rust module to use "kernel vertical" style. With this subsequent patches neither introduce unrelated changes nor leave an inconsistent import pattern. While at it, drop unnecessary imports covered by prelude::*. Link: https://docs.kernel.org/rust/coding-guidelines.html#imports Acked-by: Greg Kroah-Hartman Link: https://patch.msgid.link/20251218165626.450264-1-dakr@kernel.org [ Apply the same change to the debugfs sample code. - Danilo ] Signed-off-by: Danilo Krummrich --- rust/kernel/debugfs.rs | 46 ++++++++++++++++++++++++-------- rust/kernel/debugfs/callback_adapters.rs | 21 ++++++++++----- rust/kernel/debugfs/entry.rs | 14 +++++++--- rust/kernel/debugfs/file_ops.rs | 25 +++++++++++------ rust/kernel/debugfs/traits.rs | 43 +++++++++++++++++++++-------- 5 files changed, 109 insertions(+), 40 deletions(-) (limited to 'rust/kernel') diff --git a/rust/kernel/debugfs.rs b/rust/kernel/debugfs.rs index facad81e8290..536a320334bf 100644 --- a/rust/kernel/debugfs.rs +++ b/rust/kernel/debugfs.rs @@ -8,28 +8,52 @@ // When DebugFS is disabled, many parameters are dead. Linting for this isn't helpful. #![cfg_attr(not(CONFIG_DEBUG_FS), allow(unused_variables))] -use crate::fmt; -use crate::prelude::*; -use crate::str::CStr; #[cfg(CONFIG_DEBUG_FS)] use crate::sync::Arc; -use crate::uaccess::UserSliceReader; -use core::marker::PhantomData; -use core::marker::PhantomPinned; +use crate::{ + fmt, + prelude::*, + str::CStr, + uaccess::UserSliceReader, // +}; + #[cfg(CONFIG_DEBUG_FS)] use core::mem::ManuallyDrop; -use core::ops::Deref; +use core::{ + marker::{ + PhantomData, + PhantomPinned, // + }, + ops::Deref, +}; mod traits; -pub use traits::{BinaryReader, BinaryReaderMut, BinaryWriter, Reader, Writer}; +pub use traits::{ + BinaryReader, + BinaryReaderMut, + BinaryWriter, + Reader, + Writer, // +}; mod callback_adapters; -use callback_adapters::{FormatAdapter, NoWriter, WritableAdapter}; +use callback_adapters::{ + FormatAdapter, + NoWriter, + WritableAdapter, // +}; + mod file_ops; use file_ops::{ - BinaryReadFile, BinaryReadWriteFile, BinaryWriteFile, FileOps, ReadFile, ReadWriteFile, - WriteFile, + BinaryReadFile, + BinaryReadWriteFile, + BinaryWriteFile, + FileOps, + ReadFile, + ReadWriteFile, + WriteFile, // }; + #[cfg(CONFIG_DEBUG_FS)] mod entry; #[cfg(CONFIG_DEBUG_FS)] diff --git a/rust/kernel/debugfs/callback_adapters.rs b/rust/kernel/debugfs/callback_adapters.rs index a260d8dee051..dee7d021e18c 100644 --- a/rust/kernel/debugfs/callback_adapters.rs +++ b/rust/kernel/debugfs/callback_adapters.rs @@ -4,12 +4,21 @@ //! Adapters which allow the user to supply a write or read implementation as a value rather //! than a trait implementation. If provided, it will override the trait implementation. -use super::{Reader, Writer}; -use crate::fmt; -use crate::prelude::*; -use crate::uaccess::UserSliceReader; -use core::marker::PhantomData; -use core::ops::Deref; +use super::{ + Reader, + Writer, // +}; + +use crate::{ + fmt, + prelude::*, + uaccess::UserSliceReader, // +}; + +use core::{ + marker::PhantomData, + ops::Deref, // +}; /// # Safety /// diff --git a/rust/kernel/debugfs/entry.rs b/rust/kernel/debugfs/entry.rs index 706cb7f73d6c..5ed1303f2fe6 100644 --- a/rust/kernel/debugfs/entry.rs +++ b/rust/kernel/debugfs/entry.rs @@ -1,10 +1,16 @@ // SPDX-License-Identifier: GPL-2.0 // Copyright (C) 2025 Google LLC. -use crate::debugfs::file_ops::FileOps; -use crate::ffi::c_void; -use crate::str::{CStr, CStrExt as _}; -use crate::sync::Arc; +use crate::{ + debugfs::file_ops::FileOps, + prelude::*, + str::{ + CStr, + CStrExt as _, // + }, + sync::Arc, +}; + use core::marker::PhantomData; /// Owning handle to a DebugFS entry. diff --git a/rust/kernel/debugfs/file_ops.rs b/rust/kernel/debugfs/file_ops.rs index 8a0442d6dd7a..ad19360540ba 100644 --- a/rust/kernel/debugfs/file_ops.rs +++ b/rust/kernel/debugfs/file_ops.rs @@ -1,14 +1,23 @@ // SPDX-License-Identifier: GPL-2.0 // Copyright (C) 2025 Google LLC. -use super::{BinaryReader, BinaryWriter, Reader, Writer}; -use crate::debugfs::callback_adapters::Adapter; -use crate::fmt; -use crate::fs::file; -use crate::prelude::*; -use crate::seq_file::SeqFile; -use crate::seq_print; -use crate::uaccess::UserSlice; +use super::{ + BinaryReader, + BinaryWriter, + Reader, + Writer, // +}; + +use crate::{ + debugfs::callback_adapters::Adapter, + fmt, + fs::file, + prelude::*, + seq_file::SeqFile, + seq_print, + uaccess::UserSlice, // +}; + use core::marker::PhantomData; #[cfg(CONFIG_DEBUG_FS)] diff --git a/rust/kernel/debugfs/traits.rs b/rust/kernel/debugfs/traits.rs index 3eee60463fd5..8c39524b6a99 100644 --- a/rust/kernel/debugfs/traits.rs +++ b/rust/kernel/debugfs/traits.rs @@ -3,17 +3,38 @@ //! Traits for rendering or updating values exported to DebugFS. -use crate::alloc::Allocator; -use crate::fmt; -use crate::fs::file; -use crate::prelude::*; -use crate::sync::atomic::{Atomic, AtomicBasicOps, AtomicType, Relaxed}; -use crate::sync::Arc; -use crate::sync::Mutex; -use crate::transmute::{AsBytes, FromBytes}; -use crate::uaccess::{UserSliceReader, UserSliceWriter}; -use core::ops::{Deref, DerefMut}; -use core::str::FromStr; +use crate::{ + alloc::Allocator, + fmt, + fs::file, + prelude::*, + sync::{ + atomic::{ + Atomic, + AtomicBasicOps, + AtomicType, + Relaxed, // + }, + Arc, + Mutex, // + }, + transmute::{ + AsBytes, + FromBytes, // + }, + uaccess::{ + UserSliceReader, + UserSliceWriter, // + }, +}; + +use core::{ + ops::{ + Deref, + DerefMut, // + }, + str::FromStr, +}; /// A trait for types that can be written into a string. /// -- cgit v1.2.3 From f0c6ea853bd7f48aeec231e9378fc17cf36b9109 Mon Sep 17 00:00:00 2001 From: Tamir Duberstein Date: Mon, 22 Dec 2025 13:35:28 +0100 Subject: rust: device: replace `kernel::c_str!` with C-Strings C-String literals were added in Rust 1.77. Replace instances of `kernel::c_str!` with C-String literals where possible. Acked-by: Greg Kroah-Hartman Reviewed-by: Alice Ryhl Reviewed-by: Benno Lossin Signed-off-by: Tamir Duberstein Reviewed-by: Daniel Almeida Link: https://patch.msgid.link/20251222-cstr-driver-core-v1-2-1142a177d0fd@gmail.com Signed-off-by: Danilo Krummrich --- rust/kernel/device.rs | 4 +--- rust/kernel/device/property.rs | 6 +++--- 2 files changed, 4 insertions(+), 6 deletions(-) (limited to 'rust/kernel') diff --git a/rust/kernel/device.rs b/rust/kernel/device.rs index 21bde8d95185..a13f6ee24b09 100644 --- a/rust/kernel/device.rs +++ b/rust/kernel/device.rs @@ -12,8 +12,6 @@ use crate::{ }; use core::{any::TypeId, marker::PhantomData, ptr}; -#[cfg(CONFIG_PRINTK)] -use crate::c_str; use crate::str::CStrExt as _; pub mod property; @@ -463,7 +461,7 @@ impl Device { bindings::_dev_printk( klevel.as_ptr().cast::(), self.as_raw(), - c_str!("%pA").as_char_ptr(), + c"%pA".as_char_ptr(), core::ptr::from_ref(&msg).cast::(), ) }; diff --git a/rust/kernel/device/property.rs b/rust/kernel/device/property.rs index 413221817ef1..5aead835fbbc 100644 --- a/rust/kernel/device/property.rs +++ b/rust/kernel/device/property.rs @@ -179,11 +179,11 @@ impl FwNode { /// # Examples /// /// ``` - /// # use kernel::{c_str, device::{Device, property::FwNode}, str::CString}; + /// # use kernel::{device::{Device, property::FwNode}, str::CString}; /// fn examples(dev: &Device) -> Result { /// let fwnode = dev.fwnode().ok_or(ENOENT)?; - /// let b: u32 = fwnode.property_read(c_str!("some-number")).required_by(dev)?; - /// if let Some(s) = fwnode.property_read::(c_str!("some-str")).optional() { + /// let b: u32 = fwnode.property_read(c"some-number").required_by(dev)?; + /// if let Some(s) = fwnode.property_read::(c"some-str").optional() { /// // ... /// } /// Ok(()) -- cgit v1.2.3 From 1114c87e49642165a224c28ceaa333e8504ff122 Mon Sep 17 00:00:00 2001 From: Tamir Duberstein Date: Mon, 22 Dec 2025 13:35:29 +0100 Subject: rust: platform: replace `kernel::c_str!` with C-Strings C-String literals were added in Rust 1.77. Replace instances of `kernel::c_str!` with C-String literals where possible. Acked-by: Greg Kroah-Hartman Reviewed-by: Alice Ryhl Reviewed-by: Benno Lossin Signed-off-by: Tamir Duberstein Reviewed-by: Daniel Almeida Link: https://patch.msgid.link/20251222-cstr-driver-core-v1-3-1142a177d0fd@gmail.com [ Use kernel vertical import style; discard unrelated faux changes. - Danilo ] Signed-off-by: Danilo Krummrich --- rust/kernel/platform.rs | 13 +++++++++---- 1 file changed, 9 insertions(+), 4 deletions(-) (limited to 'rust/kernel') diff --git a/rust/kernel/platform.rs b/rust/kernel/platform.rs index ed889f079cab..bddb593cee7b 100644 --- a/rust/kernel/platform.rs +++ b/rust/kernel/platform.rs @@ -137,8 +137,13 @@ macro_rules! module_platform_driver { /// # Examples /// ///``` -/// # use kernel::{acpi, bindings, c_str, device::Core, of, platform}; -/// +/// # use kernel::{ +/// # acpi, +/// # bindings, +/// # device::Core, +/// # of, +/// # platform, +/// # }; /// struct MyDriver; /// /// kernel::of_device_table!( @@ -146,7 +151,7 @@ macro_rules! module_platform_driver { /// MODULE_OF_TABLE, /// ::IdInfo, /// [ -/// (of::DeviceId::new(c_str!("test,device")), ()) +/// (of::DeviceId::new(c"test,device"), ()) /// ] /// ); /// @@ -155,7 +160,7 @@ macro_rules! module_platform_driver { /// MODULE_ACPI_TABLE, /// ::IdInfo, /// [ -/// (acpi::DeviceId::new(c_str!("LNUXBEEF")), ()) +/// (acpi::DeviceId::new(c"LNUXBEEF"), ()) /// ] /// ); /// -- cgit v1.2.3 From 0250ea325cda689525139ae5f069974e7ed6d886 Mon Sep 17 00:00:00 2001 From: Tamir Duberstein Date: Mon, 22 Dec 2025 13:35:30 +0100 Subject: rust: io: replace `kernel::c_str!` with C-Strings C-String literals were added in Rust 1.77. Replace instances of `kernel::c_str!` with C-String literals where possible. Signed-off-by: Tamir Duberstein Reviewed-by: Daniel Almeida Link: https://patch.msgid.link/20251222-cstr-driver-core-v1-4-1142a177d0fd@gmail.com [ Use kernel vertical import style. - Danilo ] Signed-off-by: Danilo Krummrich --- rust/kernel/io/mem.rs | 17 +++++++++++++---- 1 file changed, 13 insertions(+), 4 deletions(-) (limited to 'rust/kernel') diff --git a/rust/kernel/io/mem.rs b/rust/kernel/io/mem.rs index b03b82cd531b..e4878c131c6d 100644 --- a/rust/kernel/io/mem.rs +++ b/rust/kernel/io/mem.rs @@ -5,7 +5,6 @@ use core::ops::Deref; use crate::{ - c_str, device::{ Bound, Device, // @@ -52,7 +51,12 @@ impl<'a> IoRequest<'a> { /// illustration purposes. /// /// ```no_run - /// use kernel::{bindings, c_str, platform, of, device::Core}; + /// use kernel::{ + /// bindings, + /// device::Core, + /// of, + /// platform, + /// }; /// struct SampleDriver; /// /// impl platform::Driver for SampleDriver { @@ -110,7 +114,12 @@ impl<'a> IoRequest<'a> { /// illustration purposes. /// /// ```no_run - /// use kernel::{bindings, c_str, platform, of, device::Core}; + /// use kernel::{ + /// bindings, + /// device::Core, + /// of, + /// platform, + /// }; /// struct SampleDriver; /// /// impl platform::Driver for SampleDriver { @@ -172,7 +181,7 @@ impl ExclusiveIoMem { fn ioremap(resource: &Resource) -> Result { let start = resource.start(); let size = resource.size(); - let name = resource.name().unwrap_or(c_str!("")); + let name = resource.name().unwrap_or_default(); let region = resource .request_region( -- cgit v1.2.3 From 644672e93a1aa6bfc3ebc102cbf9b8efad16e786 Mon Sep 17 00:00:00 2001 From: Tamir Duberstein Date: Mon, 22 Dec 2025 13:35:31 +0100 Subject: rust: irq: replace `kernel::c_str!` with C-Strings C-String literals were added in Rust 1.77. Replace instances of `kernel::c_str!` with C-String literals where possible. Signed-off-by: Tamir Duberstein Reviewed-by: Daniel Almeida Link: https://patch.msgid.link/20251222-cstr-driver-core-v1-5-1142a177d0fd@gmail.com Signed-off-by: Danilo Krummrich --- rust/kernel/irq/request.rs | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) (limited to 'rust/kernel') diff --git a/rust/kernel/irq/request.rs b/rust/kernel/irq/request.rs index b150563fdef8..67769800117c 100644 --- a/rust/kernel/irq/request.rs +++ b/rust/kernel/irq/request.rs @@ -139,7 +139,6 @@ impl<'a> IrqRequest<'a> { /// [`Completion::wait_for_completion()`]: kernel::sync::Completion::wait_for_completion /// /// ``` -/// use kernel::c_str; /// use kernel::device::{Bound, Device}; /// use kernel::irq::{self, Flags, IrqRequest, IrqReturn, Registration}; /// use kernel::prelude::*; @@ -167,7 +166,7 @@ impl<'a> IrqRequest<'a> { /// handler: impl PinInit, /// request: IrqRequest<'_>, /// ) -> Result>> { -/// let registration = Registration::new(request, Flags::SHARED, c_str!("my_device"), handler); +/// let registration = Registration::new(request, Flags::SHARED, c"my_device", handler); /// /// let registration = Arc::pin_init(registration, GFP_KERNEL)?; /// @@ -340,7 +339,6 @@ impl ThreadedHandler for Box { /// [`Mutex`](kernel::sync::Mutex) to provide interior mutability. /// /// ``` -/// use kernel::c_str; /// use kernel::device::{Bound, Device}; /// use kernel::irq::{ /// self, Flags, IrqRequest, IrqReturn, ThreadedHandler, ThreadedIrqReturn, @@ -381,7 +379,7 @@ impl ThreadedHandler for Box { /// request: IrqRequest<'_>, /// ) -> Result>> { /// let registration = -/// ThreadedRegistration::new(request, Flags::SHARED, c_str!("my_device"), handler); +/// ThreadedRegistration::new(request, Flags::SHARED, c"my_device", handler); /// /// let registration = Arc::pin_init(registration, GFP_KERNEL)?; /// -- cgit v1.2.3 From f47a8f595a5ed5a9602d71c31671e121da00c0e6 Mon Sep 17 00:00:00 2001 From: Tamir Duberstein Date: Mon, 22 Dec 2025 13:35:32 +0100 Subject: rust: debugfs: replace `kernel::c_str!` with C-Strings C-String literals were added in Rust 1.77. Replace instances of `kernel::c_str!` with C-String literals where possible. Signed-off-by: Tamir Duberstein Reviewed-by: Daniel Almeida Link: https://patch.msgid.link/20251222-cstr-driver-core-v1-6-1142a177d0fd@gmail.com Signed-off-by: Danilo Krummrich --- rust/kernel/debugfs.rs | 23 +++++++++-------------- 1 file changed, 9 insertions(+), 14 deletions(-) (limited to 'rust/kernel') diff --git a/rust/kernel/debugfs.rs b/rust/kernel/debugfs.rs index 536a320334bf..513cc5750e63 100644 --- a/rust/kernel/debugfs.rs +++ b/rust/kernel/debugfs.rs @@ -126,9 +126,8 @@ impl Dir { /// # Examples /// /// ``` - /// # use kernel::c_str; /// # use kernel::debugfs::Dir; - /// let debugfs = Dir::new(c_str!("parent")); + /// let debugfs = Dir::new(c"parent"); /// ``` pub fn new(name: &CStr) -> Self { Dir::create(name, None) @@ -139,10 +138,9 @@ impl Dir { /// # Examples /// /// ``` - /// # use kernel::c_str; /// # use kernel::debugfs::Dir; - /// let parent = Dir::new(c_str!("parent")); - /// let child = parent.subdir(c_str!("child")); + /// let parent = Dir::new(c"parent"); + /// let child = parent.subdir(c"child"); /// ``` pub fn subdir(&self, name: &CStr) -> Self { Dir::create(name, Some(self)) @@ -156,11 +154,10 @@ impl Dir { /// # Examples /// /// ``` - /// # use kernel::c_str; /// # use kernel::debugfs::Dir; /// # use kernel::prelude::*; - /// # let dir = Dir::new(c_str!("my_debugfs_dir")); - /// let file = KBox::pin_init(dir.read_only_file(c_str!("foo"), 200), GFP_KERNEL)?; + /// # let dir = Dir::new(c"my_debugfs_dir"); + /// let file = KBox::pin_init(dir.read_only_file(c"foo", 200), GFP_KERNEL)?; /// // "my_debugfs_dir/foo" now contains the number 200. /// // The file is removed when `file` is dropped. /// # Ok::<(), Error>(()) @@ -185,11 +182,10 @@ impl Dir { /// # Examples /// /// ``` - /// # use kernel::c_str; /// # use kernel::debugfs::Dir; /// # use kernel::prelude::*; - /// # let dir = Dir::new(c_str!("my_debugfs_dir")); - /// let file = KBox::pin_init(dir.read_binary_file(c_str!("foo"), [0x1, 0x2]), GFP_KERNEL)?; + /// # let dir = Dir::new(c"my_debugfs_dir"); + /// let file = KBox::pin_init(dir.read_binary_file(c"foo", [0x1, 0x2]), GFP_KERNEL)?; /// # Ok::<(), Error>(()) /// ``` pub fn read_binary_file<'a, T, E: 'a>( @@ -212,12 +208,11 @@ impl Dir { /// /// ``` /// # use core::sync::atomic::{AtomicU32, Ordering}; - /// # use kernel::c_str; /// # use kernel::debugfs::Dir; /// # use kernel::prelude::*; - /// # let dir = Dir::new(c_str!("foo")); + /// # let dir = Dir::new(c"foo"); /// let file = KBox::pin_init( - /// dir.read_callback_file(c_str!("bar"), + /// dir.read_callback_file(c"bar", /// AtomicU32::new(3), /// &|val, f| { /// let out = val.load(Ordering::Relaxed); -- cgit v1.2.3 From 6558749ef3405c143711cbdc67ec88cbc1582d91 Mon Sep 17 00:00:00 2001 From: Alice Ryhl Date: Wed, 17 Dec 2025 13:10:37 +0000 Subject: rust: maple_tree: rcu_read_lock() in destructor to silence lockdep MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit When running the Rust maple tree kunit tests with lockdep, you may trigger a warning that looks like this: lib/maple_tree.c:780 suspicious rcu_dereference_check() usage! other info that might help us debug this: rcu_scheduler_active = 2, debug_locks = 1 no locks held by kunit_try_catch/344. stack backtrace: CPU: 3 UID: 0 PID: 344 Comm: kunit_try_catch Tainted: G N 6.19.0-rc1+ #2 NONE Tainted: [N]=TEST Hardware name: QEMU Standard PC (Q35 + ICH9, 2009), BIOS rel-1.17.0-0-gb52ca86e094d-prebuilt.qemu.org 04/01/2014 Call Trace: dump_stack_lvl+0x71/0x90 lockdep_rcu_suspicious+0x150/0x190 mas_start+0x104/0x150 mas_find+0x179/0x240 _RINvNtCs5QSdWC790r4_4core3ptr13drop_in_placeINtNtCs1cdwasc6FUb_6kernel10maple_tree9MapleTreeINtNtNtBL_5alloc4kbox3BoxlNtNtB1x_9allocator7KmallocEEECsgxAQYCfdR72_25doctests_kernel_generated+0xaf/0x130 rust_doctest_kernel_maple_tree_rs_0+0x600/0x6b0 ? lock_release+0xeb/0x2a0 ? kunit_try_catch_run+0x210/0x210 kunit_try_run_case+0x74/0x160 ? kunit_try_catch_run+0x210/0x210 kunit_generic_run_threadfn_adapter+0x12/0x30 kthread+0x21c/0x230 ? __do_trace_sched_kthread_stop_ret+0x40/0x40 ret_from_fork+0x16c/0x270 ? __do_trace_sched_kthread_stop_ret+0x40/0x40 ret_from_fork_asm+0x11/0x20 This is because the destructor of maple tree calls mas_find() without taking rcu_read_lock() or the spinlock. Doing that is actually ok in this case since the destructor has exclusive access to the entire maple tree, but it triggers a lockdep warning. To fix that, take the rcu read lock. In the future, it's possible that memory reclaim could gain a feature where it reallocates entries in maple trees even if no user-code is touching it. If that feature is added, then this use of rcu read lock would become load-bearing, so I did not make it conditional on lockdep. We have to repeatedly take and release rcu because the destructor of T might perform operations that sleep. Link: https://lkml.kernel.org/r/20251217-maple-drop-rcu-v1-1-702af063573f@google.com Fixes: da939ef4c494 ("rust: maple_tree: add MapleTree") Signed-off-by: Alice Ryhl Reported-by: Andreas Hindborg Closes: https://rust-for-linux.zulipchat.com/#narrow/channel/x/topic/x/near/564215108 Reviewed-by: Gary Guo Reviewed-by: Daniel Almeida Cc: Andrew Ballance Cc: Björn Roy Baron Cc: Boqun Feng Cc: Danilo Krummrich Cc: Liam Howlett Cc: Matthew Wilcox (Oracle) Cc: Miguel Ojeda Cc: Trevor Gross Cc: Signed-off-by: Andrew Morton --- rust/kernel/maple_tree.rs | 11 ++++++++++- 1 file changed, 10 insertions(+), 1 deletion(-) (limited to 'rust/kernel') diff --git a/rust/kernel/maple_tree.rs b/rust/kernel/maple_tree.rs index e72eec56bf57..265d6396a78a 100644 --- a/rust/kernel/maple_tree.rs +++ b/rust/kernel/maple_tree.rs @@ -265,7 +265,16 @@ impl MapleTree { loop { // This uses the raw accessor because we're destroying pointers without removing them // from the maple tree, which is only valid because this is the destructor. - let ptr = ma_state.mas_find_raw(usize::MAX); + // + // Take the rcu lock because mas_find_raw() requires that you hold either the spinlock + // or the rcu read lock. This is only really required if memory reclaim might + // reallocate entries in the tree, as we otherwise have exclusive access. That feature + // doesn't exist yet, so for now, taking the rcu lock only serves the purpose of + // silencing lockdep. + let ptr = { + let _rcu = kernel::sync::rcu::Guard::new(); + ma_state.mas_find_raw(usize::MAX) + }; if ptr.is_null() { break; } -- cgit v1.2.3 From 057d44b057755f31a38a3cb040960e8727b93610 Mon Sep 17 00:00:00 2001 From: Matthew Maurer Date: Fri, 26 Dec 2025 20:17:07 +0000 Subject: rust: Add soc_device support Allow SoC drivers in Rust to present metadata about their devices to userspace through /sys/devices/socX and other drivers to identify their properties through `soc_device_match`. Signed-off-by: Matthew Maurer Link: https://patch.msgid.link/20251226-soc-bindings-v4-1-2c2fac08f820@google.com Signed-off-by: Danilo Krummrich --- rust/kernel/lib.rs | 2 + rust/kernel/soc.rs | 135 +++++++++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 137 insertions(+) create mode 100644 rust/kernel/soc.rs (limited to 'rust/kernel') diff --git a/rust/kernel/lib.rs b/rust/kernel/lib.rs index f812cf120042..6d637e2fed1b 100644 --- a/rust/kernel/lib.rs +++ b/rust/kernel/lib.rs @@ -138,6 +138,8 @@ pub mod security; pub mod seq_file; pub mod sizes; pub mod slice; +#[cfg(CONFIG_SOC_BUS)] +pub mod soc; mod static_assert; #[doc(hidden)] pub mod std_vendor; diff --git a/rust/kernel/soc.rs b/rust/kernel/soc.rs new file mode 100644 index 000000000000..0d6a36c83cb6 --- /dev/null +++ b/rust/kernel/soc.rs @@ -0,0 +1,135 @@ +// SPDX-License-Identifier: GPL-2.0 + +// Copyright (C) 2025 Google LLC. + +//! SoC Driver Abstraction. +//! +//! C header: [`include/linux/sys_soc.h`](srctree/include/linux/sys_soc.h) + +use crate::{ + bindings, + error, + prelude::*, + str::CString, + types::Opaque, // +}; +use core::ptr::NonNull; + +/// Attributes for a SoC device. +/// +/// These are both exported to userspace under /sys/devices/socX and provided to other drivers to +/// match against via `soc_device_match` (not yet available in Rust) to enable quirks or +/// device-specific support where necessary. +/// +/// All fields are freeform - they have no specific formatting, just defined meanings. +/// For example, the [`machine`](`Attributes::machine`) field could be "DB8500" or +/// "Qualcomm Technologies, Inc. SM8560 HDK", but regardless it should identify a board or product. +pub struct Attributes { + /// Should generally be a board ID or product ID. Examples + /// include DB8500 (ST-Ericsson) or "Qualcomm Technologies, inc. SM8560 HDK". + /// + /// If this field is not populated, the SoC infrastructure will try to populate it from + /// `/model` in the device tree. + pub machine: Option, + /// The broader class this SoC belongs to. Examples include ux500 + /// (for DB8500) or Snapdragon (for SM8650). + /// + /// On chips with ARM firmware supporting SMCCC v1.2+, this may be a JEDEC JEP106 manufacturer + /// identification. + pub family: Option, + /// The manufacturing revision of the part. Frequently this is MAJOR.MINOR, but not always. + pub revision: Option, + /// Serial Number - uniquely identifies a specific SoC. If present, should be unique (buying a + /// replacement part should change it if present). This field cannot be matched on and is + /// solely present to export through /sys. + pub serial_number: Option, + /// SoC ID - identifies a specific SoC kind in question, sometimes more specifically than + /// `machine` if the same SoC is used in multiple products. Some devices use this to specify a + /// SoC name, e.g. "I.MX??", and others just print an ID number (e.g. Tegra and Qualcomm). + /// + /// On chips with ARM firmware supporting SMCCC v1.2+, this may be a JEDEC JEP106 manufacturer + /// identification (the family value) followed by a colon and then a 4-digit ID value. + pub soc_id: Option, +} + +struct BuiltAttributes { + // While `inner` has pointers to `_backing`, it is to the interior of the `CStrings`, not + // `backing` itself, so it does not need to be pinned. + _backing: Attributes, + // `Opaque` makes us `!Unpin`, as the registration holds a pointer to `inner` when used. + inner: Opaque, +} + +fn cstring_to_c(mcs: &Option) -> *const kernel::ffi::c_char { + mcs.as_ref() + .map(|cs| cs.as_char_ptr()) + .unwrap_or(core::ptr::null()) +} + +impl BuiltAttributes { + fn as_mut_ptr(&self) -> *mut bindings::soc_device_attribute { + self.inner.get() + } +} + +impl Attributes { + fn build(self) -> BuiltAttributes { + BuiltAttributes { + inner: Opaque::new(bindings::soc_device_attribute { + machine: cstring_to_c(&self.machine), + family: cstring_to_c(&self.family), + revision: cstring_to_c(&self.revision), + serial_number: cstring_to_c(&self.serial_number), + soc_id: cstring_to_c(&self.soc_id), + data: core::ptr::null(), + custom_attr_group: core::ptr::null(), + }), + _backing: self, + } + } +} + +#[pin_data(PinnedDrop)] +/// Registration handle for your soc_dev. If you let it go out of scope, your soc_dev will be +/// unregistered. +pub struct Registration { + #[pin] + attr: BuiltAttributes, + soc_dev: NonNull, +} + +// SAFETY: We provide no operations through `&Registration`. +unsafe impl Sync for Registration {} + +// SAFETY: All pointers are normal allocations, not thread-specific. +unsafe impl Send for Registration {} + +#[pinned_drop] +impl PinnedDrop for Registration { + fn drop(self: Pin<&mut Self>) { + // SAFETY: Device always contains a live pointer to a soc_device that can be unregistered + unsafe { bindings::soc_device_unregister(self.soc_dev.as_ptr()) } + } +} + +impl Registration { + /// Register a new SoC device + pub fn new(attr: Attributes) -> impl PinInit { + try_pin_init!(Self { + attr: attr.build(), + soc_dev: { + // SAFETY: + // * The struct provided through attr is backed by pinned data next to it, + // so as long as attr lives, the strings pointed to by the struct will too. + // * `attr` is pinned, so the pinned data won't move. + // * If it returns a device, and so others may try to read this data, by + // caller invariant, `attr` won't be released until the device is. + let raw_soc = error::from_err_ptr(unsafe { + bindings::soc_device_register(attr.as_mut_ptr()) + })?; + + NonNull::new(raw_soc).ok_or(EINVAL)? + }, + }? Error) + } +} -- cgit v1.2.3 From 953deba747911225bcb936b3fa1cd02014910b17 Mon Sep 17 00:00:00 2001 From: Atharv Dubey Date: Sat, 29 Nov 2025 17:45:13 +0530 Subject: rust: miscdevice: use `pin_init::zeroed()` for C type initialization Replace manual zero-initialization using `MaybeUninit::zeroed().assume_init()` with `pin_init::zeroed()`. The `pin_init` helper provides a safer and clearer API for zero-initializing C structs without requiring an `unsafe` block. Link: https://github.com/Rust-for-Linux/linux/issues/1189 Signed-off-by: Atharv Dubey Reviewed-by: Alexandre Courbot Link: https://patch.msgid.link/20251129121513.20738-1-atharvd440@gmail.com Signed-off-by: Greg Kroah-Hartman --- rust/kernel/miscdevice.rs | 8 +++----- 1 file changed, 3 insertions(+), 5 deletions(-) (limited to 'rust/kernel') diff --git a/rust/kernel/miscdevice.rs b/rust/kernel/miscdevice.rs index d698cddcb4a5..ba64c8a858f0 100644 --- a/rust/kernel/miscdevice.rs +++ b/rust/kernel/miscdevice.rs @@ -20,7 +20,7 @@ use crate::{ seq_file::SeqFile, types::{ForeignOwnable, Opaque}, }; -use core::{marker::PhantomData, mem::MaybeUninit, pin::Pin}; +use core::{marker::PhantomData, pin::Pin}; /// Options for creating a misc device. #[derive(Copy, Clone)] @@ -32,8 +32,7 @@ pub struct MiscDeviceOptions { impl MiscDeviceOptions { /// Create a raw `struct miscdev` ready for registration. pub const fn into_raw(self) -> bindings::miscdevice { - // SAFETY: All zeros is valid for this C type. - let mut result: bindings::miscdevice = unsafe { MaybeUninit::zeroed().assume_init() }; + let mut result: bindings::miscdevice = pin_init::zeroed(); result.minor = bindings::MISC_DYNAMIC_MINOR as ffi::c_int; result.name = crate::str::as_char_ptr_in_const_context(self.name); result.fops = MiscdeviceVTable::::build(); @@ -420,8 +419,7 @@ impl MiscdeviceVTable { } else { None }, - // SAFETY: All zeros is a valid value for `bindings::file_operations`. - ..unsafe { MaybeUninit::zeroed().assume_init() } + ..pin_init::zeroed() }; const fn build() -> &'static bindings::file_operations { -- cgit v1.2.3 From c1093b85890693fa2d372468f279854c3624d2b1 Mon Sep 17 00:00:00 2001 From: Alice Ryhl Date: Wed, 3 Dec 2025 14:48:08 +0000 Subject: rust: sync: add Arc::DATA_OFFSET This constant will be used to expose some offset constants from the Rust Binder driver to tracepoints which are implemented in C. The constant is usually equal to sizeof(refcount_t), but may be larger if T has a large alignment. Signed-off-by: Alice Ryhl Reviewed-by: Daniel Almeida Link: https://patch.msgid.link/20251203-binder-trace1-v1-1-22d3ffddb44e@google.com Signed-off-by: Greg Kroah-Hartman --- rust/kernel/sync/arc.rs | 3 +++ 1 file changed, 3 insertions(+) (limited to 'rust/kernel') diff --git a/rust/kernel/sync/arc.rs b/rust/kernel/sync/arc.rs index 289f77abf415..921e19333b89 100644 --- a/rust/kernel/sync/arc.rs +++ b/rust/kernel/sync/arc.rs @@ -240,6 +240,9 @@ impl Arc { // `Arc` object. Ok(unsafe { Self::from_inner(inner) }) } + + /// The offset that the value is stored at. + pub const DATA_OFFSET: usize = core::mem::offset_of!(ArcInner, data); } impl Arc { -- cgit v1.2.3 From 68ece1e2ce3a1cb0aac9f5af685cf18fa2fa31aa Mon Sep 17 00:00:00 2001 From: Yilin Chen <1479826151@qq.com> Date: Mon, 29 Dec 2025 00:52:34 +0800 Subject: rust: dma: remove incorrect safety documentation Removes a safety requirement that incorrectly states callers must ensure the device does not access memory while the returned slice is live, as this method doesn't return a slice. Fixes: d37a39f607c4 ("rust: dma: add as_slice/write functions for CoherentAllocation") Signed-off-by: Yilin Chen <1479826151@qq.com> Link: https://patch.msgid.link/tencent_5195C0324923A2B67DEF8AE4B8E139BCB105@qq.com Signed-off-by: Danilo Krummrich --- rust/kernel/dma.rs | 2 -- 1 file changed, 2 deletions(-) (limited to 'rust/kernel') diff --git a/rust/kernel/dma.rs b/rust/kernel/dma.rs index 84d3c67269e8..2ac107d8f7b7 100644 --- a/rust/kernel/dma.rs +++ b/rust/kernel/dma.rs @@ -532,8 +532,6 @@ impl CoherentAllocation { /// /// # Safety /// - /// * Callers must ensure that the device does not read/write to/from memory while the returned - /// slice is live. /// * Callers must ensure that this call does not race with a read or write to the same region /// that overlaps with this write. /// -- cgit v1.2.3 From 3691fd19ccad4c1c0e3fc4888ef36edaa6e571be Mon Sep 17 00:00:00 2001 From: Yilin Chen <1479826151@qq.com> Date: Mon, 29 Dec 2025 00:53:44 +0800 Subject: rust: device_id: replace incorrect word in safety documentation The safety documentation incorrectly refers to `RawDeviceId` when transmuting to `RawType`. This fixes the documentation to correctly indicate that implementers must ensure layout compatibility with `RawType`, not `RawDeviceId`. Fixes: 9b90864bb42b ("rust: implement `IdArray`, `IdTable` and `RawDeviceId`") Signed-off-by: Yilin Chen <1479826151@qq.com> Link: https://patch.msgid.link/tencent_C18DD5047749311142ED455779C7CCCF3A08@qq.com Signed-off-by: Danilo Krummrich --- rust/kernel/device_id.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'rust/kernel') diff --git a/rust/kernel/device_id.rs b/rust/kernel/device_id.rs index 62c42da12e9d..8e9721446014 100644 --- a/rust/kernel/device_id.rs +++ b/rust/kernel/device_id.rs @@ -15,7 +15,7 @@ use core::mem::MaybeUninit; /// # Safety /// /// Implementers must ensure that `Self` is layout-compatible with [`RawDeviceId::RawType`]; -/// i.e. it's safe to transmute to `RawDeviceId`. +/// i.e. it's safe to transmute to `RawType`. /// /// This requirement is needed so `IdArray::new` can convert `Self` to `RawType` when building /// the ID table. -- cgit v1.2.3 From f91ffed95c06e94c835cd7deaea666d69948cde9 Mon Sep 17 00:00:00 2001 From: Brendan Shephard Date: Tue, 23 Dec 2025 15:56:47 +1000 Subject: rust: Return Option from page_align and ensure no usize overflow Change `page_align()` to return `Option` to allow validation of the provided `addr` value. This ensures that any value that is within one `PAGE_SIZE` of `usize::MAX` will not panic, and instead returns `None` to indicate overflow. Signed-off-by: Brendan Shephard Reviewed-by: Alice Ryhl Reviewed-by: Alexandre Courbot Reviewed-by: Daniel Almeida Link: https://patch.msgid.link/20251223055647.9761-1-bshephar@bne-home.net [ Use kernel vertical style for imports; use markdown in comments. - Danilo ] Signed-off-by: Danilo Krummrich --- rust/kernel/page.rs | 36 +++++++++++++++++++++++++++++------- 1 file changed, 29 insertions(+), 7 deletions(-) (limited to 'rust/kernel') diff --git a/rust/kernel/page.rs b/rust/kernel/page.rs index 432fc0297d4a..adecb200c654 100644 --- a/rust/kernel/page.rs +++ b/rust/kernel/page.rs @@ -25,14 +25,36 @@ pub const PAGE_SIZE: usize = bindings::PAGE_SIZE; /// A bitmask that gives the page containing a given address. pub const PAGE_MASK: usize = !(PAGE_SIZE - 1); -/// Round up the given number to the next multiple of [`PAGE_SIZE`]. +/// Rounds up to the next multiple of [`PAGE_SIZE`]. /// -/// It is incorrect to pass an address where the next multiple of [`PAGE_SIZE`] doesn't fit in a -/// [`usize`]. -pub const fn page_align(addr: usize) -> usize { - // Parentheses around `PAGE_SIZE - 1` to avoid triggering overflow sanitizers in the wrong - // cases. - (addr + (PAGE_SIZE - 1)) & PAGE_MASK +/// Returns [`None`] on integer overflow. +/// +/// # Examples +/// +/// ``` +/// use kernel::page::{ +/// page_align, +/// PAGE_SIZE, +/// }; +/// +/// // Requested address is already aligned. +/// assert_eq!(page_align(0x0), Some(0x0)); +/// assert_eq!(page_align(PAGE_SIZE), Some(PAGE_SIZE)); +/// +/// // Requested address needs alignment up. +/// assert_eq!(page_align(0x1), Some(PAGE_SIZE)); +/// assert_eq!(page_align(PAGE_SIZE + 1), Some(2 * PAGE_SIZE)); +/// +/// // Requested address causes overflow (returns `None`). +/// let overflow_addr = usize::MAX - (PAGE_SIZE / 2); +/// assert_eq!(page_align(overflow_addr), None); +/// ``` +#[inline(always)] +pub const fn page_align(addr: usize) -> Option { + let Some(sum) = addr.checked_add(PAGE_SIZE - 1) else { + return None; + }; + Some(sum & PAGE_MASK) } /// Representation of a non-owning reference to a [`Page`]. -- cgit v1.2.3 From 4c9f6a782f6078dc94450fcb22e65d520bfa0775 Mon Sep 17 00:00:00 2001 From: Alice Ryhl Date: Sat, 27 Dec 2025 15:47:21 +0000 Subject: rust: driver: fix broken intra-doc links to example driver types The `auxiliary` and `pci` modules are conditional on `CONFIG_AUXILIARY_BUS` and `CONFIG_PCI` respectively. When these are disabled, the intra-doc links to `auxiliary::Driver` and `pci::Driver` break, causing rustdoc warnings (or errors with `-D warnings`). error: unresolved link to `kernel::auxiliary::Driver` --> rust/kernel/driver.rs:82:28 | 82 | //! [`auxiliary::Driver`]: kernel::auxiliary::Driver | ^^^^^^^^^^^^^^^^^^^^^^^^^ no item named `auxiliary` in module `kernel` Fix this by making the documentation for these examples conditional on the corresponding configuration options. Fixes: 970a7c68788e ("driver: rust: expand documentation for driver infrastructure") Signed-off-by: Alice Ryhl Reported-by: FUJITA Tomonori Closes: https://lore.kernel.org/rust-for-linux/20251209.151817.744108529426448097.fujita.tomonori@gmail.com/ Link: https://patch.msgid.link/20251227-driver-types-v1-1-1916154fbe5e@google.com Signed-off-by: Danilo Krummrich --- rust/kernel/driver.rs | 12 ++++++++---- 1 file changed, 8 insertions(+), 4 deletions(-) (limited to 'rust/kernel') diff --git a/rust/kernel/driver.rs b/rust/kernel/driver.rs index 9beae2e3d57e..649d06468f41 100644 --- a/rust/kernel/driver.rs +++ b/rust/kernel/driver.rs @@ -33,7 +33,14 @@ //! } //! ``` //! -//! For specific examples see [`auxiliary::Driver`], [`pci::Driver`] and [`platform::Driver`]. +//! For specific examples see: +//! +//! * [`platform::Driver`](kernel::platform::Driver) +#![cfg_attr( + CONFIG_AUXILIARY_BUS, + doc = "* [`auxiliary::Driver`](kernel::auxiliary::Driver)" +)] +#![cfg_attr(CONFIG_PCI, doc = "* [`pci::Driver`](kernel::pci::Driver)")] //! //! The `probe()` callback should return a `impl PinInit`, i.e. the driver's private //! data. The bus abstraction should store the pointer in the corresponding bus device. The generic @@ -79,7 +86,6 @@ //! //! For this purpose the generic infrastructure in [`device_id`] should be used. //! -//! [`auxiliary::Driver`]: kernel::auxiliary::Driver //! [`Core`]: device::Core //! [`Device`]: device::Device //! [`Device`]: device::Device @@ -87,8 +93,6 @@ //! [`DeviceContext`]: device::DeviceContext //! [`device_id`]: kernel::device_id //! [`module_driver`]: kernel::module_driver -//! [`pci::Driver`]: kernel::pci::Driver -//! [`platform::Driver`]: kernel::platform::Driver use crate::error::{Error, Result}; use crate::{acpi, device, of, str::CStr, try_pin_init, types::Opaque, ThisModule}; -- cgit v1.2.3 From 32cb3840386fd3684fbe8294cfc0a6684417139e Mon Sep 17 00:00:00 2001 From: FUJITA Tomonori Date: Wed, 31 Dec 2025 13:57:27 +0900 Subject: rust: dma: fix broken intra-doc links The `pci` module is conditional on CONFIG_PCI. When it's disabled, the intra-doc link to `pci::Device` causes rustdoc warnings: warning: unresolved link to `::kernel::pci::Device` --> rust/kernel/dma.rs:30:70 | 30 | /// where the underlying bus is DMA capable, such as [`pci::Device`](::kernel::pci::Device) or | ^^^^^^^^^^^^^^^^^^^^^ no item named `pci` in module `kernel` Fix this by making the documentation conditional on CONFIG_PCI. Fixes: d06d5f66f549 ("rust: dma: implement `dma::Device` trait") Signed-off-by: FUJITA Tomonori Reviewed-by: Dirk Behme Link: https://patch.msgid.link/20251231045728.1912024-1-fujita.tomonori@gmail.com [ Keep the "such as" part indicating a list of examples; fix typos in commit message. - Danilo ] Signed-off-by: Danilo Krummrich --- rust/kernel/dma.rs | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) (limited to 'rust/kernel') diff --git a/rust/kernel/dma.rs b/rust/kernel/dma.rs index 2ac107d8f7b7..acc65b1e0f24 100644 --- a/rust/kernel/dma.rs +++ b/rust/kernel/dma.rs @@ -27,8 +27,9 @@ pub type DmaAddress = bindings::dma_addr_t; /// Trait to be implemented by DMA capable bus devices. /// /// The [`dma::Device`](Device) trait should be implemented by bus specific device representations, -/// where the underlying bus is DMA capable, such as [`pci::Device`](::kernel::pci::Device) or -/// [`platform::Device`](::kernel::platform::Device). +/// where the underlying bus is DMA capable, such as: +#[cfg_attr(CONFIG_PCI, doc = "* [`pci::Device`](kernel::pci::Device)")] +/// * [`platform::Device`](::kernel::platform::Device) pub trait Device: AsRef> { /// Set up the device's DMA streaming addressing capabilities. /// -- cgit v1.2.3 From a9a42f0754b6c69525612d678b73da790e28b9fd Mon Sep 17 00:00:00 2001 From: FUJITA Tomonori Date: Wed, 31 Dec 2025 13:57:28 +0900 Subject: rust: device: fix broken intra-doc links The `pci` module is conditional on CONFIG_PCI. When it's disabled, the intra-doc link to `pci::Device` causes rustdoc warnings: warning: unresolved link to `kernel::pci::Device` --> rust/kernel/device.rs:163:22 | 163 | /// [`pci::Device`]: kernel::pci::Device | ^^^^^^^^^^^^^^^^^^^ no item named `pci` in module `kernel` | = note: `#[warn(rustdoc::broken_intra_doc_links)]` on by default Fix this by making the documentation conditional on CONFIG_PCI. Fixes: d6e26c1ae4a6 ("device: rust: expand documentation for Device") Signed-off-by: FUJITA Tomonori Reviewed-by: Dirk Behme Link: https://patch.msgid.link/20251231045728.1912024-2-fujita.tomonori@gmail.com [ Keep the "such as" part indicating a list of examples; fix typos in commit message. - Danilo ] Signed-off-by: Danilo Krummrich --- rust/kernel/device.rs | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) (limited to 'rust/kernel') diff --git a/rust/kernel/device.rs b/rust/kernel/device.rs index c79be2e2bfe3..5c2e1e0369e9 100644 --- a/rust/kernel/device.rs +++ b/rust/kernel/device.rs @@ -67,8 +67,9 @@ static_assert!(core::mem::size_of::() >= core::mem::size_ /// /// # Implementing Bus Devices /// -/// This section provides a guideline to implement bus specific devices, such as [`pci::Device`] or -/// [`platform::Device`]. +/// This section provides a guideline to implement bus specific devices, such as: +#[cfg_attr(CONFIG_PCI, doc = "* [`pci::Device`](kernel::pci::Device)")] +/// * [`platform::Device`] /// /// A bus specific device should be defined as follows. /// @@ -160,7 +161,6 @@ static_assert!(core::mem::size_of::() >= core::mem::size_ /// /// [`AlwaysRefCounted`]: kernel::types::AlwaysRefCounted /// [`impl_device_context_deref`]: kernel::impl_device_context_deref -/// [`pci::Device`]: kernel::pci::Device /// [`platform::Device`]: kernel::platform::Device #[repr(transparent)] pub struct Device(Opaque, PhantomData); -- cgit v1.2.3 From 600559b9817f6eaa927035eebb12534fadb35ee8 Mon Sep 17 00:00:00 2001 From: Atharv Dubey Date: Mon, 1 Dec 2025 22:26:01 +0530 Subject: rust: rbtree: fix minor typo in comment Fix a typo in a comment to improve clarity and readability. Suggested-by: Miguel Ojeda Link: https://github.com/Rust-for-Linux/linux/issues/1206 Signed-off-by: Atharv Dubey Reviewed-by: Alice Ryhl Link: https://patch.msgid.link/20251201165601.31484-1-atharvd440@gmail.com [ Removed one of the cases that is gone now. Reworded accordingly (and to avoid mentioning 'documentation', since it is just a comment). - Miguel ] Signed-off-by: Miguel Ojeda --- rust/kernel/rbtree.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'rust/kernel') diff --git a/rust/kernel/rbtree.rs b/rust/kernel/rbtree.rs index 4729eb56827a..6c3dbb2e6f0d 100644 --- a/rust/kernel/rbtree.rs +++ b/rust/kernel/rbtree.rs @@ -1130,7 +1130,7 @@ pub struct IterMut<'a, K, V> { } // SAFETY: The [`IterMut`] has exclusive access to both `K` and `V`, so it is sufficient to require them to be `Send`. -// The iterator only gives out immutable references to the keys, but since the iterator has excusive access to those same +// The iterator only gives out immutable references to the keys, but since the iterator has exclusive access to those same // keys, `Send` is sufficient. `Sync` would be okay, but it is more restrictive to the user. unsafe impl<'a, K: Send, V: Send> Send for IterMut<'a, K, V> {} -- cgit v1.2.3 From 45f6aed8a835ee2bdd0a5d5ee626a91fe285014f Mon Sep 17 00:00:00 2001 From: Hang Shu Date: Fri, 7 Nov 2025 09:39:17 +0000 Subject: rust: rbtree: fix documentation typo in CursorMut peek_next method The peek_next method's doc comment incorrectly stated it accesses the "previous" node when it actually accesses the next node. Fix the documentation to accurately reflect the method's behavior. Fixes: 98c14e40e07a ("rust: rbtree: add cursor") Reviewed-by: Alice Ryhl Signed-off-by: Hang Shu Reported-by: Miguel Ojeda Closes: https://github.com/Rust-for-Linux/linux/issues/1205 Cc: stable@vger.kernel.org Reviewed-by: Gary Guo Link: https://patch.msgid.link/20251107093921.3379954-1-m18080292938@163.com [ Reworded slightly. - Miguel ] Signed-off-by: Miguel Ojeda --- rust/kernel/rbtree.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'rust/kernel') diff --git a/rust/kernel/rbtree.rs b/rust/kernel/rbtree.rs index 6c3dbb2e6f0d..312cecab72e7 100644 --- a/rust/kernel/rbtree.rs +++ b/rust/kernel/rbtree.rs @@ -985,7 +985,7 @@ impl<'a, K, V> CursorMut<'a, K, V> { self.peek(Direction::Prev) } - /// Access the previous node without moving the cursor. + /// Access the next node without moving the cursor. pub fn peek_next(&self) -> Option<(&K, &V)> { self.peek(Direction::Next) } -- cgit v1.2.3 From 1e4e2a847f3c0bb3f34f3b20229be5c0214b80fa Mon Sep 17 00:00:00 2001 From: Dirk Behme Date: Fri, 2 Jan 2026 09:48:21 +0100 Subject: rust: fmt: Fix grammar in Adapter description Add a missing `and` in the description of the `Adapter`. Fixes: c5cf01ba8dfe ("rust: support formatting of foreign types") Signed-off-by: Dirk Behme Acked-by: Tamir Duberstein Link: https://patch.msgid.link/20260102084821.1077864-1-dirk.behme@de.bosch.com [ Reworded for typo. - Miguel ] Signed-off-by: Miguel Ojeda --- rust/kernel/fmt.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'rust/kernel') diff --git a/rust/kernel/fmt.rs b/rust/kernel/fmt.rs index 84d634201d90..1e8725eb44ed 100644 --- a/rust/kernel/fmt.rs +++ b/rust/kernel/fmt.rs @@ -6,7 +6,7 @@ pub use core::fmt::{Arguments, Debug, Error, Formatter, Result, Write}; -/// Internal adapter used to route allow implementations of formatting traits for foreign types. +/// Internal adapter used to route and allow implementations of formatting traits for foreign types. /// /// It is inserted automatically by the [`fmt!`] macro and is not meant to be used directly. /// -- cgit v1.2.3 From f6b8d4b7e54ffa1492db476c299c7058603108cb Mon Sep 17 00:00:00 2001 From: Nakamura Shuta Date: Thu, 4 Dec 2025 11:43:36 +0900 Subject: rust: num: fix typos in Bounded documentation Fix several typos and grammatical errors in the Bounded type documentation: - "less significant bits" -> "least significant bits" - "with in" -> "within" - "withheld" -> "upheld" - "// This" -> "// This" (double space) - "doesn't fits" -> "doesn't fit" (2 occurrences) Reported-by: Miguel Ojeda Closes: https://github.com/Rust-for-Linux/linux/issues/1210 Signed-off-by: Nakamura Shuta Acked-by: Alexandre Courbot Acked-by: Yury Norov (NVIDIA) Fixes: 01e345e82ec3 ("rust: num: add Bounded integer wrapping type") Link: https://patch.msgid.link/20251204024336.246587-1-nakamura.shuta@gmail.com [ Removed Link tag due to duplicated URL. - Miguel ] Signed-off-by: Miguel Ojeda --- rust/kernel/num/bounded.rs | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) (limited to 'rust/kernel') diff --git a/rust/kernel/num/bounded.rs b/rust/kernel/num/bounded.rs index f870080af8ac..c3ee79ba9746 100644 --- a/rust/kernel/num/bounded.rs +++ b/rust/kernel/num/bounded.rs @@ -40,11 +40,11 @@ fn fits_within(value: T, num_bits: u32) -> bool { fits_within!(value, T, num_bits) } -/// An integer value that requires only the `N` less significant bits of the wrapped type to be +/// An integer value that requires only the `N` least significant bits of the wrapped type to be /// encoded. /// /// This limits the number of usable bits in the wrapped integer type, and thus the stored value to -/// a narrower range, which provides guarantees that can be useful when working with in e.g. +/// a narrower range, which provides guarantees that can be useful when working within e.g. /// bitfields. /// /// # Invariants @@ -56,7 +56,7 @@ fn fits_within(value: T, num_bits: u32) -> bool { /// # Examples /// /// The preferred way to create values is through constants and the [`Bounded::new`] family of -/// constructors, as they trigger a build error if the type invariants cannot be withheld. +/// constructors, as they trigger a build error if the type invariants cannot be upheld. /// /// ``` /// use kernel::num::Bounded; @@ -82,7 +82,7 @@ fn fits_within(value: T, num_bits: u32) -> bool { /// ``` /// use kernel::num::Bounded; /// -/// // This succeeds because `15` can be represented with 4 unsigned bits. +/// // This succeeds because `15` can be represented with 4 unsigned bits. /// assert!(Bounded::::try_new(15).is_some()); /// /// // This fails because `16` cannot be represented with 4 unsigned bits. @@ -221,7 +221,7 @@ fn fits_within(value: T, num_bits: u32) -> bool { /// let v: Option> = 128u32.try_into_bounded(); /// assert_eq!(v.as_deref().copied(), Some(128)); /// -/// // Fails because `128` doesn't fits into 6 bits. +/// // Fails because `128` doesn't fit into 6 bits. /// let v: Option> = 128u32.try_into_bounded(); /// assert_eq!(v, None); /// ``` @@ -501,7 +501,7 @@ where /// let v: Option> = 128u32.try_into_bounded(); /// assert_eq!(v.as_deref().copied(), Some(128)); /// -/// // Fails because `128` doesn't fits into 6 bits. +/// // Fails because `128` doesn't fit into 6 bits. /// let v: Option> = 128u32.try_into_bounded(); /// assert_eq!(v, None); /// ``` -- cgit v1.2.3 From 7a8461a2a8dad031050ec372d756826e9a40c050 Mon Sep 17 00:00:00 2001 From: Tamir Duberstein Date: Sat, 3 Jan 2026 21:24:27 -0500 Subject: rust: net: replace `kernel::c_str!` with C-Strings C-String literals were added in Rust 1.77. Replace instances of `kernel::c_str!` with C-String literals where possible. Acked-by: Greg Kroah-Hartman Reviewed-by: Alice Ryhl Reviewed-by: Benno Lossin Reviewed-by: FUJITA Tomonori Reviewed-by: Daniel Almeida Signed-off-by: Tamir Duberstein Link: https://patch.msgid.link/20260103-cstr-net-v2-1-8688f504b85d@gmail.com Signed-off-by: Jakub Kicinski --- rust/kernel/net/phy.rs | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) (limited to 'rust/kernel') diff --git a/rust/kernel/net/phy.rs b/rust/kernel/net/phy.rs index bf6272d87a7b..3ca99db5cccf 100644 --- a/rust/kernel/net/phy.rs +++ b/rust/kernel/net/phy.rs @@ -777,7 +777,6 @@ impl DeviceMask { /// /// ``` /// # mod module_phy_driver_sample { -/// use kernel::c_str; /// use kernel::net::phy::{self, DeviceId}; /// use kernel::prelude::*; /// @@ -796,7 +795,7 @@ impl DeviceMask { /// /// #[vtable] /// impl phy::Driver for PhySample { -/// const NAME: &'static CStr = c_str!("PhySample"); +/// const NAME: &'static CStr = c"PhySample"; /// const PHY_DEVICE_ID: phy::DeviceId = phy::DeviceId::new_with_exact_mask(0x00000001); /// } /// # } @@ -805,7 +804,6 @@ impl DeviceMask { /// This expands to the following code: /// /// ```ignore -/// use kernel::c_str; /// use kernel::net::phy::{self, DeviceId}; /// use kernel::prelude::*; /// @@ -825,7 +823,7 @@ impl DeviceMask { /// /// #[vtable] /// impl phy::Driver for PhySample { -/// const NAME: &'static CStr = c_str!("PhySample"); +/// const NAME: &'static CStr = c"PhySample"; /// const PHY_DEVICE_ID: phy::DeviceId = phy::DeviceId::new_with_exact_mask(0x00000001); /// } /// -- cgit v1.2.3 From 9f92d7d1cb9cccc6c703ca53d4f1d1acca79b598 Mon Sep 17 00:00:00 2001 From: Marko Turk Date: Mon, 5 Jan 2026 22:37:57 +0100 Subject: rust: pci: fix typos in Bar struct's comments Fix a typo in the doc-comment of the Bar structure: 'inststance -> instance'. Add also 'is' to the comment inside Bar's `new()` function (suggested by Dirk): // `pdev` is valid by the invariants of `Device`. Fixes: bf9651f84b4e ("rust: pci: implement I/O mappable `pci::Bar`") Suggested-by: Dirk Behme Signed-off-by: Marko Turk Reviewed-by: Dirk Behme Link: https://patch.msgid.link/20260105213726.73000-2-mt@markoturk.info Signed-off-by: Danilo Krummrich --- rust/kernel/pci/io.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'rust/kernel') diff --git a/rust/kernel/pci/io.rs b/rust/kernel/pci/io.rs index 0d55c3139b6f..82a4f1eba2f5 100644 --- a/rust/kernel/pci/io.rs +++ b/rust/kernel/pci/io.rs @@ -20,7 +20,7 @@ use core::ops::Deref; /// /// # Invariants /// -/// `Bar` always holds an `IoRaw` inststance that holds a valid pointer to the start of the I/O +/// `Bar` always holds an `IoRaw` instance that holds a valid pointer to the start of the I/O /// memory mapped PCI BAR and its size. pub struct Bar { pdev: ARef, @@ -54,7 +54,7 @@ impl Bar { let ioptr: usize = unsafe { bindings::pci_iomap(pdev.as_raw(), num, 0) } as usize; if ioptr == 0 { // SAFETY: - // `pdev` valid by the invariants of `Device`. + // `pdev` is valid by the invariants of `Device`. // `num` is checked for validity by a previous call to `Device::resource_len`. unsafe { bindings::pci_release_region(pdev.as_raw(), num) }; return Err(ENOMEM); -- cgit v1.2.3 From 3a1ec424dd9c9491138a5ebadb24ce9f33e6a822 Mon Sep 17 00:00:00 2001 From: Hsiu Che Yu Date: Thu, 4 Dec 2025 11:38:48 +0800 Subject: rust: num: bounded: mark __new as unsafe The `Bounded::__new()` constructor relies on the caller to ensure the value can be represented within N bits. Failing to uphold this requirement breaks the type invariant. Mark it as unsafe and document this requirement in a Safety section to make the contract explicit. Update all call sites to use unsafe blocks and change their comments from `INVARIANT:` to `SAFETY:`, as they are now justifying unsafe operations rather than establishing type invariants. Fixes: 01e345e82ec3a ("rust: num: add Bounded integer wrapping type") Link: https://lore.kernel.org/all/aS1qC_ol2XEpZ44b@google.com/ Reported-by: Miguel Ojeda Closes: https://github.com/Rust-for-Linux/linux/issues/1211 Signed-off-by: Hsiu Che Yu Acked-by: Alexandre Courbot Link: https://patch.msgid.link/20251204033849.23480-1-yu.whisper.personal@gmail.com Signed-off-by: Miguel Ojeda --- rust/kernel/num/bounded.rs | 34 +++++++++++++++++++--------------- 1 file changed, 19 insertions(+), 15 deletions(-) (limited to 'rust/kernel') diff --git a/rust/kernel/num/bounded.rs b/rust/kernel/num/bounded.rs index c3ee79ba9746..c9a44e12c19b 100644 --- a/rust/kernel/num/bounded.rs +++ b/rust/kernel/num/bounded.rs @@ -259,9 +259,9 @@ macro_rules! impl_const_new { assert!(fits_within!(VALUE, $type, N)); } - // INVARIANT: `fits_within` confirmed that `VALUE` can be represented within + // SAFETY: `fits_within` confirmed that `VALUE` can be represented within // `N` bits. - Self::__new(VALUE) + unsafe { Self::__new(VALUE) } } } )* @@ -284,7 +284,11 @@ where /// /// The caller remains responsible for checking, either statically or dynamically, that `value` /// can be represented as a `T` using at most `N` bits. - const fn __new(value: T) -> Self { + /// + /// # Safety + /// + /// The caller must ensure that `value` can be represented within `N` bits. + const unsafe fn __new(value: T) -> Self { // Enforce the type invariants. const { // `N` cannot be zero. @@ -328,8 +332,8 @@ where /// ``` pub fn try_new(value: T) -> Option { fits_within(value, N).then(|| { - // INVARIANT: `fits_within` confirmed that `value` can be represented within `N` bits. - Self::__new(value) + // SAFETY: `fits_within` confirmed that `value` can be represented within `N` bits. + unsafe { Self::__new(value) } }) } @@ -370,8 +374,8 @@ where "Requested value larger than maximal representable value." ); - // INVARIANT: `fits_within` confirmed that `expr` can be represented within `N` bits. - Self::__new(expr) + // SAFETY: `fits_within` confirmed that `expr` can be represented within `N` bits. + unsafe { Self::__new(expr) } } /// Returns the wrapped value as the backing type. @@ -410,9 +414,9 @@ where ); } - // INVARIANT: The value did fit within `N` bits, so it will all the more fit within + // SAFETY: The value did fit within `N` bits, so it will all the more fit within // the larger `M` bits. - Bounded::__new(self.0) + unsafe { Bounded::__new(self.0) } } /// Attempts to shrink the number of bits usable for `self`. @@ -466,9 +470,9 @@ where // `U` and `T` have the same sign, hence this conversion cannot fail. let value = unsafe { U::try_from(self.get()).unwrap_unchecked() }; - // INVARIANT: Although the backing type has changed, the value is still represented within + // SAFETY: Although the backing type has changed, the value is still represented within // `N` bits, and with the same signedness. - Bounded::__new(value) + unsafe { Bounded::__new(value) } } } @@ -944,9 +948,9 @@ macro_rules! impl_from_primitive { Self: AtLeastXBits<{ <$type as Integer>::BITS as usize }>, { fn from(value: $type) -> Self { - // INVARIANT: The trait bound on `Self` guarantees that `N` bits is + // SAFETY: The trait bound on `Self` guarantees that `N` bits is // enough to hold any value of the source type. - Self::__new(T::from(value)) + unsafe { Self::__new(T::from(value)) } } } )* @@ -1051,8 +1055,8 @@ where T: Integer + From, { fn from(value: bool) -> Self { - // INVARIANT: A boolean can be represented using a single bit, and thus fits within any + // SAFETY: A boolean can be represented using a single bit, and thus fits within any // integer type for any `N` > 0. - Self::__new(T::from(value)) + unsafe { Self::__new(T::from(value)) } } } -- cgit v1.2.3 From 8510ef5e3cfbd7d59a16845f85cd0194a8689761 Mon Sep 17 00:00:00 2001 From: FUJITA Tomonori Date: Tue, 6 Jan 2026 09:03:20 +0900 Subject: rust: device: Remove explicit import of CStrExt Remove the explicit import of CStrExt. When CONFIG_PRINTK is disabled this import causes a build error: error: unused import: `crate::str::CStrExt` --> rust/kernel/device.rs:17:5 | 17 | use crate::str::CStrExt as _; | ^^^^^^^^^^^^^^^^^^^ | = note: `-D unused-imports` implied by `-D warnings` = help: to override `-D warnings` add `#[allow(unused_imports)]` error: aborting due to 1 previous error CStrExt is covered by prelude::* so the explicit import is redundant. Signed-off-by: FUJITA Tomonori Fixes: 3b83f5d5e78a ("rust: replace `CStr` with `core::ffi::CStr`") Link: https://patch.msgid.link/20260106000320.2593800-1-fujita.tomonori@gmail.com Signed-off-by: Danilo Krummrich --- rust/kernel/device.rs | 1 - 1 file changed, 1 deletion(-) (limited to 'rust/kernel') diff --git a/rust/kernel/device.rs b/rust/kernel/device.rs index 5c2e1e0369e9..71b200df0f40 100644 --- a/rust/kernel/device.rs +++ b/rust/kernel/device.rs @@ -14,7 +14,6 @@ use core::{any::TypeId, marker::PhantomData, ptr}; #[cfg(CONFIG_PRINTK)] use crate::c_str; -use crate::str::CStrExt as _; pub mod property; -- cgit v1.2.3 From 31bc0aade4e03a056a6b568571e59d3783c97ffc Mon Sep 17 00:00:00 2001 From: Marko Turk Date: Mon, 5 Jan 2026 22:37:42 +0100 Subject: rust: io: remove square brackets from pci::Bar reference Remove square brackets since this section is not a part of doc-comment so the reference will not be converted to a link in the generated docs. Suggested-by: Danilo Krummrich Signed-off-by: Marko Turk Link: https://patch.msgid.link/20260105213726.73000-1-mt@markoturk.info Signed-off-by: Danilo Krummrich --- rust/kernel/io.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'rust/kernel') diff --git a/rust/kernel/io.rs b/rust/kernel/io.rs index 98e8b84e68d1..a97eb44a9a87 100644 --- a/rust/kernel/io.rs +++ b/rust/kernel/io.rs @@ -87,7 +87,7 @@ impl IoRaw { /// }; /// use core::ops::Deref; /// -/// // See also [`pci::Bar`] for a real example. +/// // See also `pci::Bar` for a real example. /// struct IoMem(IoRaw); /// /// impl IoMem { -- cgit v1.2.3 From 4348796233e736147e2e79c58784d0a9fb48867d Mon Sep 17 00:00:00 2001 From: Ewan Chorynski Date: Sun, 28 Dec 2025 17:15:23 +0100 Subject: rust: drm: Improve safety comment when using `Pin::into_inner_unchecked` The safety requirements for `Pin::into_inner_unchecked` state that the returned pointer must be treated as pinned until it is dropped. Such a guarantee is provided by the `ARef` type. This patch improves the safety comment to better reflect this. Signed-off-by: Ewan Chorynski Link: https://patch.msgid.link/20251228-drm-gem-safety-comment-v2-1-99bb861c3371@ik.me Signed-off-by: Danilo Krummrich --- rust/kernel/drm/gem/mod.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'rust/kernel') diff --git a/rust/kernel/drm/gem/mod.rs b/rust/kernel/drm/gem/mod.rs index bdaac839dacc..d49a9ba02635 100644 --- a/rust/kernel/drm/gem/mod.rs +++ b/rust/kernel/drm/gem/mod.rs @@ -210,7 +210,7 @@ impl Object { // SAFETY: The arguments are all valid per the type invariants. to_result(unsafe { bindings::drm_gem_object_init(dev.as_raw(), obj.obj.get(), size) })?; - // SAFETY: We never move out of `Self`. + // SAFETY: We will never move out of `Self` as `ARef` is always treated as pinned. let ptr = KBox::into_raw(unsafe { Pin::into_inner_unchecked(obj) }); // SAFETY: `ptr` comes from `KBox::into_raw` and hence can't be NULL. -- cgit v1.2.3 From 13f2bd893ae3638c96e7c54a6e477dbceec6e529 Mon Sep 17 00:00:00 2001 From: Danilo Krummrich Date: Mon, 5 Jan 2026 15:19:45 +0100 Subject: rust: usb: use "kernel vertical" style for imports Convert all imports to use "kernel vertical" style. With this, subsequent patches neither introduce unrelated changes nor leave an inconsistent import pattern. While at it, drop unnecessary imports covered by prelude::*. Link: https://docs.kernel.org/rust/coding-guidelines.html#imports Signed-off-by: Danilo Krummrich Reviewed-by: Daniel Almeida Link: https://patch.msgid.link/20260105142123.95030-4-dakr@kernel.org Signed-off-by: Greg Kroah-Hartman --- rust/kernel/usb.rs | 21 +++++++++++++++------ 1 file changed, 15 insertions(+), 6 deletions(-) (limited to 'rust/kernel') diff --git a/rust/kernel/usb.rs b/rust/kernel/usb.rs index d10b65e9fb6a..99b07b5ac491 100644 --- a/rust/kernel/usb.rs +++ b/rust/kernel/usb.rs @@ -6,14 +6,23 @@ //! C header: [`include/linux/usb.h`](srctree/include/linux/usb.h) use crate::{ - bindings, device, - device_id::{RawDeviceId, RawDeviceIdIndex}, + bindings, + device, + device_id::{ + RawDeviceId, + RawDeviceIdIndex, // + }, driver, - error::{from_result, to_result, Result}, + error::{ + from_result, + to_result, // + }, prelude::*, - str::CStr, - types::{AlwaysRefCounted, Opaque}, - ThisModule, + types::{ + AlwaysRefCounted, + Opaque, // + }, + ThisModule, // }; use core::{ marker::PhantomData, -- cgit v1.2.3 From 6506b44e88da265688f786d379987e91b4826bf4 Mon Sep 17 00:00:00 2001 From: Danilo Krummrich Date: Mon, 5 Jan 2026 15:19:42 +0100 Subject: rust: auxiliary: use "kernel vertical" style for imports Convert all imports to use "kernel vertical" style. With this, subsequent patches neither introduce unrelated changes nor leave an inconsistent import pattern. While at it, drop unnecessary imports covered by prelude::*. Link: https://docs.kernel.org/rust/coding-guidelines.html#imports Reviewed-by: Greg Kroah-Hartman Link: https://patch.msgid.link/20260105142123.95030-1-dakr@kernel.org Signed-off-by: Danilo Krummrich --- rust/kernel/auxiliary.rs | 21 ++++++++++++++++----- 1 file changed, 16 insertions(+), 5 deletions(-) (limited to 'rust/kernel') diff --git a/rust/kernel/auxiliary.rs b/rust/kernel/auxiliary.rs index 56f3c180e8f6..f8273cf165dc 100644 --- a/rust/kernel/auxiliary.rs +++ b/rust/kernel/auxiliary.rs @@ -5,19 +5,30 @@ //! C header: [`include/linux/auxiliary_bus.h`](srctree/include/linux/auxiliary_bus.h) use crate::{ - bindings, container_of, device, - device_id::{RawDeviceId, RawDeviceIdIndex}, + bindings, + container_of, + device, + device_id::{ + RawDeviceId, + RawDeviceIdIndex, // + }, devres::Devres, driver, - error::{from_result, to_result, Result}, + error::{ + from_result, + to_result, // + }, prelude::*, types::Opaque, - ThisModule, + ThisModule, // }; use core::{ marker::PhantomData, mem::offset_of, - ptr::{addr_of_mut, NonNull}, + ptr::{ + addr_of_mut, + NonNull, // + }, }; /// An adapter for the registration of auxiliary drivers. -- cgit v1.2.3 From da74aee2ad0dcae8a5544deace7465869890e8f0 Mon Sep 17 00:00:00 2001 From: Danilo Krummrich Date: Mon, 5 Jan 2026 15:19:43 +0100 Subject: rust: platform: use "kernel vertical" style for imports Convert all imports to use "kernel vertical" style. With this, subsequent patches neither introduce unrelated changes nor leave an inconsistent import pattern. While at it, drop unnecessary imports covered by prelude::*. Link: https://docs.kernel.org/rust/coding-guidelines.html#imports Reviewed-by: Greg Kroah-Hartman Link: https://patch.msgid.link/20260105142123.95030-2-dakr@kernel.org Signed-off-by: Danilo Krummrich --- rust/kernel/platform.rs | 31 ++++++++++++++++++++++++------- 1 file changed, 24 insertions(+), 7 deletions(-) (limited to 'rust/kernel') diff --git a/rust/kernel/platform.rs b/rust/kernel/platform.rs index bddb593cee7b..8836ac46b066 100644 --- a/rust/kernel/platform.rs +++ b/rust/kernel/platform.rs @@ -5,22 +5,39 @@ //! C header: [`include/linux/platform_device.h`](srctree/include/linux/platform_device.h) use crate::{ - acpi, bindings, container_of, - device::{self, Bound}, + acpi, + bindings, + container_of, + device::{ + self, + Bound, // + }, driver, - error::{from_result, to_result, Result}, - io::{mem::IoRequest, Resource}, - irq::{self, IrqRequest}, + error::{ + from_result, + to_result, // + }, + io::{ + mem::IoRequest, + Resource, // + }, + irq::{ + self, + IrqRequest, // + }, of, prelude::*, types::Opaque, - ThisModule, + ThisModule, // }; use core::{ marker::PhantomData, mem::offset_of, - ptr::{addr_of_mut, NonNull}, + ptr::{ + addr_of_mut, + NonNull, // + }, }; /// An adapter for the registration of platform drivers. -- cgit v1.2.3 From 52563c665b0b0b39f319bee40ecc5e8f25b9050a Mon Sep 17 00:00:00 2001 From: Danilo Krummrich Date: Mon, 5 Jan 2026 15:19:44 +0100 Subject: rust: driver-core: use "kernel vertical" style for imports Convert all imports to use "kernel vertical" style. With this, subsequent patches neither introduce unrelated changes nor leave an inconsistent import pattern. While at it, drop unnecessary imports covered by prelude::*. Link: https://docs.kernel.org/rust/coding-guidelines.html#imports Reviewed-by: Greg Kroah-Hartman Link: https://patch.msgid.link/20260105142123.95030-3-dakr@kernel.org Signed-off-by: Danilo Krummrich --- rust/kernel/device.rs | 14 +++++++++++--- rust/kernel/devres.rs | 25 +++++++++++++++++++------ rust/kernel/driver.rs | 12 ++++++++---- 3 files changed, 38 insertions(+), 13 deletions(-) (limited to 'rust/kernel') diff --git a/rust/kernel/device.rs b/rust/kernel/device.rs index a13f6ee24b09..ec9b0945b74c 100644 --- a/rust/kernel/device.rs +++ b/rust/kernel/device.rs @@ -5,12 +5,20 @@ //! C header: [`include/linux/device.h`](srctree/include/linux/device.h) use crate::{ - bindings, fmt, + bindings, + fmt, prelude::*, sync::aref::ARef, - types::{ForeignOwnable, Opaque}, + types::{ + ForeignOwnable, + Opaque, // + }, // +}; +use core::{ + any::TypeId, + marker::PhantomData, + ptr, // }; -use core::{any::TypeId, marker::PhantomData, ptr}; use crate::str::CStrExt as _; diff --git a/rust/kernel/devres.rs b/rust/kernel/devres.rs index 835d9c11948e..db02f8b1788d 100644 --- a/rust/kernel/devres.rs +++ b/rust/kernel/devres.rs @@ -8,13 +8,26 @@ use crate::{ alloc::Flags, bindings, - device::{Bound, Device}, - error::{to_result, Error, Result}, - ffi::c_void, + device::{ + Bound, + Device, // + }, + error::to_result, prelude::*, - revocable::{Revocable, RevocableGuard}, - sync::{aref::ARef, rcu, Completion}, - types::{ForeignOwnable, Opaque, ScopeGuard}, + revocable::{ + Revocable, + RevocableGuard, // + }, + sync::{ + aref::ARef, + rcu, + Completion, // + }, + types::{ + ForeignOwnable, + Opaque, + ScopeGuard, // + }, }; use pin_init::Wrapper; diff --git a/rust/kernel/driver.rs b/rust/kernel/driver.rs index 9beae2e3d57e..866d5d76ca7e 100644 --- a/rust/kernel/driver.rs +++ b/rust/kernel/driver.rs @@ -90,10 +90,14 @@ //! [`pci::Driver`]: kernel::pci::Driver //! [`platform::Driver`]: kernel::platform::Driver -use crate::error::{Error, Result}; -use crate::{acpi, device, of, str::CStr, try_pin_init, types::Opaque, ThisModule}; -use core::pin::Pin; -use pin_init::{pin_data, pinned_drop, PinInit}; +use crate::{ + acpi, + device, + of, + prelude::*, + types::Opaque, + ThisModule, // +}; /// The [`RegistrationOps`] trait serves as generic interface for subsystems (e.g., PCI, Platform, /// Amba, etc.) to provide the corresponding subsystem specific implementation to register / -- cgit v1.2.3 From d88f27d7f4a1861fe6d548d9a9c203e59489880a Mon Sep 17 00:00:00 2001 From: Danilo Krummrich Date: Mon, 5 Jan 2026 15:19:46 +0100 Subject: rust: faux: use "kernel vertical" style for imports Convert all imports to use "kernel vertical" style. With this, subsequent patches neither introduce unrelated changes nor leave an inconsistent import pattern. While at it, drop unnecessary imports covered by prelude::*. Link: https://docs.kernel.org/rust/coding-guidelines.html#imports Reviewed-by: Greg Kroah-Hartman Link: https://patch.msgid.link/20260105142123.95030-5-dakr@kernel.org Signed-off-by: Danilo Krummrich --- rust/kernel/faux.rs | 13 +++++++++++-- 1 file changed, 11 insertions(+), 2 deletions(-) (limited to 'rust/kernel') diff --git a/rust/kernel/faux.rs b/rust/kernel/faux.rs index 7fe2dd197e37..43b4974f48cd 100644 --- a/rust/kernel/faux.rs +++ b/rust/kernel/faux.rs @@ -6,8 +6,17 @@ //! //! C header: [`include/linux/device/faux.h`](srctree/include/linux/device/faux.h) -use crate::{bindings, device, error::code::*, prelude::*}; -use core::ptr::{addr_of_mut, null, null_mut, NonNull}; +use crate::{ + bindings, + device, + prelude::*, // +}; +use core::ptr::{ + addr_of_mut, + null, + null_mut, + NonNull, // +}; /// The registration of a faux device. /// -- cgit v1.2.3 From 86f4a271dc1962e389ea512d07a77626dbd8c1d8 Mon Sep 17 00:00:00 2001 From: Alice Ryhl Date: Mon, 11 Aug 2025 12:14:41 +0000 Subject: rust: sync: Refactor static_lock_class!() macro By introducing a new_static() constructor, the macro does not need to go through MaybeUninit::uninit().assume_init(), which is a pattern that is best avoided when possible. The safety comment not only requires that the value is leaked, but also that it is stored in the right portion of memory. This is so that the lockdep static_obj() check will succeed when using this constructor. One could argue that lockdep detects this scenario, so that safety requirement isn't needed. However, it simplifies matters to require that static_obj() will succeed and it's not a burdensome requirement on the caller. Suggested-by: Benno Lossin Reviewed-by: Daniel Almeida Reviewed-by: Benno Lossin Signed-off-by: Alice Ryhl Signed-off-by: Boqun Feng Link: https://patch.msgid.link/20250811-lock-class-key-cleanup-v3-1-b12967ee1ca2@google.com --- rust/kernel/sync.rs | 24 ++++++++++++++++++------ 1 file changed, 18 insertions(+), 6 deletions(-) (limited to 'rust/kernel') diff --git a/rust/kernel/sync.rs b/rust/kernel/sync.rs index 5df87e2bd212..1dfbee8e9d00 100644 --- a/rust/kernel/sync.rs +++ b/rust/kernel/sync.rs @@ -45,6 +45,21 @@ pub struct LockClassKey { unsafe impl Sync for LockClassKey {} impl LockClassKey { + /// Initializes a statically allocated lock class key. + /// + /// This is usually used indirectly through the [`static_lock_class!`] macro. + /// + /// # Safety + /// + /// * Before using the returned value, it must be pinned in a static memory location. + /// * The destructor must never run on the returned `LockClassKey`. + #[doc(hidden)] + pub const unsafe fn new_static() -> Self { + LockClassKey { + inner: Opaque::uninit(), + } + } + /// Initializes a dynamically allocated lock class key. In the common case of using a /// statically allocated lock class key, the static_lock_class! macro should be used instead. /// @@ -101,12 +116,9 @@ impl PinnedDrop for LockClassKey { macro_rules! static_lock_class { () => {{ static CLASS: $crate::sync::LockClassKey = - // Lockdep expects uninitialized memory when it's handed a statically allocated `struct - // lock_class_key`. - // - // SAFETY: `LockClassKey` transparently wraps `Opaque` which permits uninitialized - // memory. - unsafe { ::core::mem::MaybeUninit::uninit().assume_init() }; + // SAFETY: The returned `LockClassKey` is stored in static memory and we pin it. Drop + // never runs on a static global. + unsafe { $crate::sync::LockClassKey::new_static() }; $crate::prelude::Pin::static_ref(&CLASS) }}; } -- cgit v1.2.3 From 106ab474e5a711ea08e0908a42cfa89d691e57ad Mon Sep 17 00:00:00 2001 From: Alice Ryhl Date: Mon, 11 Aug 2025 12:14:42 +0000 Subject: rust: sync: Clean up LockClassKey and its docs Several aspects of the code and documentation for this type is incomplete. Also several things are hidden from the docs. Thus, clean it up and make it easier to read the rendered html docs. Reviewed-by: Daniel Almeida Signed-off-by: Alice Ryhl Reviewed-by: Benno Lossin Signed-off-by: Boqun Feng Link: https://patch.msgid.link/20250811-lock-class-key-cleanup-v3-2-b12967ee1ca2@google.com --- rust/kernel/sync.rs | 54 +++++++++++++++++++++++++++++++++++++++-------------- 1 file changed, 40 insertions(+), 14 deletions(-) (limited to 'rust/kernel') diff --git a/rust/kernel/sync.rs b/rust/kernel/sync.rs index 1dfbee8e9d00..b10e576221ff 100644 --- a/rust/kernel/sync.rs +++ b/rust/kernel/sync.rs @@ -32,7 +32,9 @@ pub use locked_by::LockedBy; pub use refcount::Refcount; pub use set_once::SetOnce; -/// Represents a lockdep class. It's a wrapper around C's `lock_class_key`. +/// Represents a lockdep class. +/// +/// Wraps the kernel's `struct lock_class_key`. #[repr(transparent)] #[pin_data(PinnedDrop)] pub struct LockClassKey { @@ -40,6 +42,10 @@ pub struct LockClassKey { inner: Opaque, } +// SAFETY: Unregistering a lock class key from a different thread than where it was registered is +// allowed. +unsafe impl Send for LockClassKey {} + // SAFETY: `bindings::lock_class_key` is designed to be used concurrently from multiple threads and // provides its own synchronization. unsafe impl Sync for LockClassKey {} @@ -47,28 +53,31 @@ unsafe impl Sync for LockClassKey {} impl LockClassKey { /// Initializes a statically allocated lock class key. /// - /// This is usually used indirectly through the [`static_lock_class!`] macro. + /// This is usually used indirectly through the [`static_lock_class!`] macro. See its + /// documentation for more information. /// /// # Safety /// /// * Before using the returned value, it must be pinned in a static memory location. /// * The destructor must never run on the returned `LockClassKey`. - #[doc(hidden)] pub const unsafe fn new_static() -> Self { LockClassKey { inner: Opaque::uninit(), } } - /// Initializes a dynamically allocated lock class key. In the common case of using a - /// statically allocated lock class key, the static_lock_class! macro should be used instead. + /// Initializes a dynamically allocated lock class key. + /// + /// In the common case of using a statically allocated lock class key, the + /// [`static_lock_class!`] macro should be used instead. /// /// # Examples + /// /// ``` - /// # use kernel::alloc::KBox; - /// # use kernel::types::ForeignOwnable; - /// # use kernel::sync::{LockClassKey, SpinLock}; - /// # use pin_init::stack_pin_init; + /// use kernel::alloc::KBox; + /// use kernel::types::ForeignOwnable; + /// use kernel::sync::{LockClassKey, SpinLock}; + /// use pin_init::stack_pin_init; /// /// let key = KBox::pin_init(LockClassKey::new_dynamic(), GFP_KERNEL)?; /// let key_ptr = key.into_foreign(); @@ -86,7 +95,6 @@ impl LockClassKey { /// // SAFETY: We dropped `num`, the only use of the key, so the result of the previous /// // `borrow` has also been dropped. Thus, it's safe to use from_foreign. /// unsafe { drop(> as ForeignOwnable>::from_foreign(key_ptr)) }; - /// /// # Ok::<(), Error>(()) /// ``` pub fn new_dynamic() -> impl PinInit { @@ -96,7 +104,10 @@ impl LockClassKey { }) } - pub(crate) fn as_ptr(&self) -> *mut bindings::lock_class_key { + /// Returns a raw pointer to the inner C struct. + /// + /// It is up to the caller to use the raw pointer correctly. + pub fn as_ptr(&self) -> *mut bindings::lock_class_key { self.inner.get() } } @@ -104,14 +115,28 @@ impl LockClassKey { #[pinned_drop] impl PinnedDrop for LockClassKey { fn drop(self: Pin<&mut Self>) { - // SAFETY: self.as_ptr was registered with lockdep and self is pinned, so the address - // hasn't changed. Thus, it's safe to pass to unregister. + // SAFETY: `self.as_ptr()` was registered with lockdep and `self` is pinned, so the address + // hasn't changed. Thus, it's safe to pass it to unregister. unsafe { bindings::lockdep_unregister_key(self.as_ptr()) } } } /// Defines a new static lock class and returns a pointer to it. -#[doc(hidden)] +/// +/// # Examples +/// +/// ``` +/// use kernel::c_str; +/// use kernel::sync::{static_lock_class, Arc, SpinLock}; +/// +/// fn new_locked_int() -> Result>> { +/// Arc::pin_init(SpinLock::new( +/// 42, +/// c_str!("new_locked_int"), +/// static_lock_class!(), +/// ), GFP_KERNEL) +/// } +/// ``` #[macro_export] macro_rules! static_lock_class { () => {{ @@ -122,6 +147,7 @@ macro_rules! static_lock_class { $crate::prelude::Pin::static_ref(&CLASS) }}; } +pub use static_lock_class; /// Returns the given string, if one is provided, otherwise generates one based on the source code /// location. -- cgit v1.2.3 From 8a581130b1cbc17c702298b8325e3df98c792760 Mon Sep 17 00:00:00 2001 From: FUJITA Tomonori Date: Tue, 16 Dec 2025 09:09:01 +0900 Subject: rust: sync: set_once: Implement Send and Sync Implement Send and Sync for SetOnce to allow it to be used across thread boundaries. Send: SetOnce can be transferred across threads when T: Send, as the contained value is also transferred and will be dropped on the destination thread. Sync: SetOnce can be shared across threads when T: Sync, as as_ref() provides shared references &T and atomic operations ensure proper synchronization. Since the inner T may be dropped on any thread, we also require T: Send. Signed-off-by: FUJITA Tomonori Reviewed-by: Andreas Hindborg Reviewed-by: Gary Guo Signed-off-by: Boqun Feng Link: https://patch.msgid.link/20251216000901.221375-1-fujita.tomonori@gmail.com --- rust/kernel/sync/set_once.rs | 8 ++++++++ 1 file changed, 8 insertions(+) (limited to 'rust/kernel') diff --git a/rust/kernel/sync/set_once.rs b/rust/kernel/sync/set_once.rs index bdba601807d8..139cef05e935 100644 --- a/rust/kernel/sync/set_once.rs +++ b/rust/kernel/sync/set_once.rs @@ -123,3 +123,11 @@ impl Drop for SetOnce { } } } + +// SAFETY: `SetOnce` can be transferred across thread boundaries iff the data it contains can. +unsafe impl Send for SetOnce {} + +// SAFETY: `SetOnce` synchronises access to the inner value via atomic operations, +// so shared references are safe when `T: Sync`. Since the inner `T` may be dropped +// on any thread, we also require `T: Send`. +unsafe impl Sync for SetOnce {} -- cgit v1.2.3 From 09248ed8cdb6345afc883c02aecd79dfbd9c2a9c Mon Sep 17 00:00:00 2001 From: Alice Ryhl Date: Thu, 18 Dec 2025 08:25:13 +0000 Subject: rust: sync: Implement Unpin for ARef The default implementation of Unpin for ARef is conditional on T being Unpin due to its PhantomData field. However, this is overly strict as pointers to T are legal to move even if T itself cannot move. Since commit 66f1ea83d9f8 ("rust: lock: Add a Pin<&mut T> accessor") this causes build failures when combined with a Mutex that contains an field ARef, because almost any type that ARef is used with is !Unpin. Reviewed-by: Daniel Almeida Signed-off-by: Alice Ryhl Reviewed-by: Alexandre Courbot Reviewed-by: Benno Lossin Signed-off-by: Boqun Feng Link: https://patch.msgid.link/20251218-unpin-for-aref-v2-1-30d77129cbc6@google.com --- rust/kernel/sync/aref.rs | 3 +++ 1 file changed, 3 insertions(+) (limited to 'rust/kernel') diff --git a/rust/kernel/sync/aref.rs b/rust/kernel/sync/aref.rs index 0d24a0432015..0616c0353c2b 100644 --- a/rust/kernel/sync/aref.rs +++ b/rust/kernel/sync/aref.rs @@ -83,6 +83,9 @@ unsafe impl Send for ARef {} // example, when the reference count reaches zero and `T` is dropped. unsafe impl Sync for ARef {} +// Even if `T` is pinned, pointers to `T` can still move. +impl Unpin for ARef {} + impl ARef { /// Creates a new instance of [`ARef`]. /// -- cgit v1.2.3 From 2bb8c41e61b29ccdf7b6d716c3a8fe8488aa202a Mon Sep 17 00:00:00 2001 From: FUJITA Tomonori Date: Sun, 28 Dec 2025 21:05:44 +0900 Subject: rust: sync: atomic: Prepare AtomicOps macros for i8/i16 support Rework the internal AtomicOps macro plumbing to generate per-type implementations from a mapping list. Capture the trait definition once and reuse it for both declaration and per-type impl expansion to reduce duplication and keep future extensions simple. This is a preparatory refactor for enabling i8/i16 atomics cleanly. Signed-off-by: FUJITA Tomonori Signed-off-by: Boqun Feng Link: https://patch.msgid.link/20251228120546.1602275-2-fujita.tomonori@gmail.com --- rust/kernel/sync/atomic/internal.rs | 85 ++++++++++++++++++++++++++++--------- 1 file changed, 66 insertions(+), 19 deletions(-) (limited to 'rust/kernel') diff --git a/rust/kernel/sync/atomic/internal.rs b/rust/kernel/sync/atomic/internal.rs index 6fdd8e59f45b..41b4ce2935e3 100644 --- a/rust/kernel/sync/atomic/internal.rs +++ b/rust/kernel/sync/atomic/internal.rs @@ -156,16 +156,17 @@ macro_rules! impl_atomic_method { } } -// Delcares $ops trait with methods and implements the trait for `i32` and `i64`. -macro_rules! declare_and_impl_atomic_methods { - ($(#[$attr:meta])* $pub:vis trait $ops:ident { - $( - $(#[doc=$doc:expr])* - fn $func:ident [$($variant:ident),*]($($arg_sig:tt)*) $( -> $ret:ty)? { - $unsafe:tt { bindings::#call($($arg:tt)*) } - } - )* - }) => { +macro_rules! declare_atomic_ops_trait { + ( + $(#[$attr:meta])* $pub:vis trait $ops:ident { + $( + $(#[doc=$doc:expr])* + fn $func:ident [$($variant:ident),*]($($arg_sig:tt)*) $( -> $ret:ty)? { + $unsafe:tt { bindings::#call($($arg:tt)*) } + } + )* + } + ) => { $(#[$attr])* $pub trait $ops: AtomicImpl { $( @@ -175,21 +176,25 @@ macro_rules! declare_and_impl_atomic_methods { ); )* } + } +} - impl $ops for i32 { +macro_rules! impl_atomic_ops_for_one { + ( + $ty:ty => $ctype:ident, + $(#[$attr:meta])* $pub:vis trait $ops:ident { $( - impl_atomic_method!( - (atomic) $func[$($variant)*]($($arg_sig)*) $(-> $ret)? { - $unsafe { call($($arg)*) } - } - ); + $(#[doc=$doc:expr])* + fn $func:ident [$($variant:ident),*]($($arg_sig:tt)*) $( -> $ret:ty)? { + $unsafe:tt { bindings::#call($($arg:tt)*) } + } )* } - - impl $ops for i64 { + ) => { + impl $ops for $ty { $( impl_atomic_method!( - (atomic64) $func[$($variant)*]($($arg_sig)*) $(-> $ret)? { + ($ctype) $func[$($variant)*]($($arg_sig)*) $(-> $ret)? { $unsafe { call($($arg)*) } } ); @@ -198,7 +203,47 @@ macro_rules! declare_and_impl_atomic_methods { } } +// Declares $ops trait with methods and implements the trait. +macro_rules! declare_and_impl_atomic_methods { + ( + [ $($map:tt)* ] + $(#[$attr:meta])* $pub:vis trait $ops:ident { $($body:tt)* } + ) => { + declare_and_impl_atomic_methods!( + @with_ops_def + [ $($map)* ] + ( $(#[$attr])* $pub trait $ops { $($body)* } ) + ); + }; + + (@with_ops_def [ $($map:tt)* ] ( $($ops_def:tt)* )) => { + declare_atomic_ops_trait!( $($ops_def)* ); + + declare_and_impl_atomic_methods!( + @munch + [ $($map)* ] + ( $($ops_def)* ) + ); + }; + + (@munch [] ( $($ops_def:tt)* )) => {}; + + (@munch [ $ty:ty => $ctype:ident $(, $($rest:tt)*)? ] ( $($ops_def:tt)* )) => { + impl_atomic_ops_for_one!( + $ty => $ctype, + $($ops_def)* + ); + + declare_and_impl_atomic_methods!( + @munch + [ $($($rest)*)? ] + ( $($ops_def)* ) + ); + }; +} + declare_and_impl_atomic_methods!( + [ i32 => atomic, i64 => atomic64 ] /// Basic atomic operations pub trait AtomicBasicOps { /// Atomic read (load). @@ -216,6 +261,7 @@ declare_and_impl_atomic_methods!( ); declare_and_impl_atomic_methods!( + [ i32 => atomic, i64 => atomic64 ] /// Exchange and compare-and-exchange atomic operations pub trait AtomicExchangeOps { /// Atomic exchange. @@ -243,6 +289,7 @@ declare_and_impl_atomic_methods!( ); declare_and_impl_atomic_methods!( + [ i32 => atomic, i64 => atomic64 ] /// Atomic arithmetic operations pub trait AtomicArithmeticOps { /// Atomic add (wrapping). -- cgit v1.2.3 From b33796d554f270e19141c0c1fa0a90705a511d2b Mon Sep 17 00:00:00 2001 From: FUJITA Tomonori Date: Thu, 11 Dec 2025 20:38:25 +0900 Subject: rust: sync: atomic: Add i8/i16 load and store support Add atomic operation support for i8 and i16 types using volatile read/write and smp_load_acquire/smp_store_release helpers. [boqun: Adjust [1] to avoid introduction of impl_atomic_only_load_and_store_ops!() in the middle] Signed-off-by: FUJITA Tomonori Reviewed-by: Gary Guo Reviewed-by: Joel Fernandes Link: https://lore.kernel.org/all/20251228120546.1602275-1-fujita.tomonori@gmail.com/ [1] Signed-off-by: Boqun Feng Link: https://patch.msgid.link/20251211113826.1299077-4-fujita.tomonori@gmail.com --- rust/kernel/sync/atomic/internal.rs | 25 +++++++++++++++++++------ rust/kernel/sync/atomic/predefine.rs | 14 +++++++++++++- 2 files changed, 32 insertions(+), 7 deletions(-) (limited to 'rust/kernel') diff --git a/rust/kernel/sync/atomic/internal.rs b/rust/kernel/sync/atomic/internal.rs index 41b4ce2935e3..1b2a7933bc14 100644 --- a/rust/kernel/sync/atomic/internal.rs +++ b/rust/kernel/sync/atomic/internal.rs @@ -13,17 +13,22 @@ mod private { pub trait Sealed {} } -// `i32` and `i64` are only supported atomic implementations. +// The C side supports atomic primitives only for `i32` and `i64` (`atomic_t` and `atomic64_t`), +// while the Rust side also layers provides atomic support for `i8` and `i16` +// on top of lower-level C primitives. +impl private::Sealed for i8 {} +impl private::Sealed for i16 {} impl private::Sealed for i32 {} impl private::Sealed for i64 {} /// A marker trait for types that implement atomic operations with C side primitives. /// -/// This trait is sealed, and only types that have directly mapping to the C side atomics should -/// impl this: +/// This trait is sealed, and only types that map directly to the C side atomics +/// or can be implemented with lower-level C primitives are allowed to implement this: /// -/// - `i32` maps to `atomic_t`. -/// - `i64` maps to `atomic64_t`. +/// - `i8` and `i16` are implemented with lower-level C primitives. +/// - `i32` map to `atomic_t` +/// - `i64` map to `atomic64_t` pub trait AtomicImpl: Sized + Send + Copy + private::Sealed { /// The type of the delta in arithmetic or logical operations. /// @@ -32,6 +37,14 @@ pub trait AtomicImpl: Sized + Send + Copy + private::Sealed { type Delta; } +impl AtomicImpl for i8 { + type Delta = Self; +} + +impl AtomicImpl for i16 { + type Delta = Self; +} + // `atomic_t` implements atomic operations on `i32`. impl AtomicImpl for i32 { type Delta = Self; @@ -243,7 +256,7 @@ macro_rules! declare_and_impl_atomic_methods { } declare_and_impl_atomic_methods!( - [ i32 => atomic, i64 => atomic64 ] + [ i8 => atomic_i8, i16 => atomic_i16, i32 => atomic, i64 => atomic64 ] /// Basic atomic operations pub trait AtomicBasicOps { /// Atomic read (load). diff --git a/rust/kernel/sync/atomic/predefine.rs b/rust/kernel/sync/atomic/predefine.rs index 45a17985cda4..09b357be59b8 100644 --- a/rust/kernel/sync/atomic/predefine.rs +++ b/rust/kernel/sync/atomic/predefine.rs @@ -5,6 +5,18 @@ use crate::static_assert; use core::mem::{align_of, size_of}; +// SAFETY: `i8` has the same size and alignment with itself, and is round-trip transmutable to +// itself. +unsafe impl super::AtomicType for i8 { + type Repr = i8; +} + +// SAFETY: `i16` has the same size and alignment with itself, and is round-trip transmutable to +// itself. +unsafe impl super::AtomicType for i16 { + type Repr = i16; +} + // SAFETY: `i32` has the same size and alignment with itself, and is round-trip transmutable to // itself. unsafe impl super::AtomicType for i32 { @@ -118,7 +130,7 @@ mod tests { #[test] fn atomic_basic_tests() { - for_each_type!(42 in [i32, i64, u32, u64, isize, usize] |v| { + for_each_type!(42 in [i8, i16, i32, i64, u32, u64, isize, usize] |v| { let x = Atomic::new(v); assert_eq!(v, x.load(Relaxed)); -- cgit v1.2.3 From 7b001c97d9bdaea50e1e1834040c58f7ef9f4e89 Mon Sep 17 00:00:00 2001 From: FUJITA Tomonori Date: Thu, 11 Dec 2025 20:38:26 +0900 Subject: rust: sync: atomic: Add store_release/load_acquire tests Add minimum store_release/load_acquire tests. Signed-off-by: FUJITA Tomonori Reviewed-by: Gary Guo Reviewed-by: Joel Fernandes Signed-off-by: Boqun Feng Link: https://patch.msgid.link/20251211113826.1299077-5-fujita.tomonori@gmail.com --- rust/kernel/sync/atomic/predefine.rs | 10 ++++++++++ 1 file changed, 10 insertions(+) (limited to 'rust/kernel') diff --git a/rust/kernel/sync/atomic/predefine.rs b/rust/kernel/sync/atomic/predefine.rs index 09b357be59b8..51e9df0cf56e 100644 --- a/rust/kernel/sync/atomic/predefine.rs +++ b/rust/kernel/sync/atomic/predefine.rs @@ -137,6 +137,16 @@ mod tests { }); } + #[test] + fn atomic_acquire_release_tests() { + for_each_type!(42 in [i8, i16, i32, i64, u32, u64, isize, usize] |v| { + let x = Atomic::new(0); + + x.store(v, Release); + assert_eq!(v, x.load(Acquire)); + }); + } + #[test] fn atomic_xchg_tests() { for_each_type!(42 in [i32, i64, u32, u64, isize, usize] |v| { -- cgit v1.2.3 From 584f286f822afecc1a6521a27b3caf3e2f515d41 Mon Sep 17 00:00:00 2001 From: FUJITA Tomonori Date: Sun, 28 Dec 2025 21:05:46 +0900 Subject: rust: sync: atomic: Add i8/i16 xchg and cmpxchg support Add atomic xchg and cmpxchg operation support for i8 and i16 types with tests. Note that since the current implementation of Atomic::<{i8,i16}>::{load,store}() is READ_ONCE()/WRITE_ONCE()-based. The atomicity between load/store and xchg/cmpxchg is only guaranteed if the architecture has native RmW support, hence i8/i16 is currently AtomicImpl only when CONFIG_ARCH_SUPPORTS_ATOMIC_RWM=y. [boqun: Make i8/i16 AtomicImpl only when CONFIG_ARCH_SUPPORTS_ATOMIC_RWM=y] Signed-off-by: FUJITA Tomonori Signed-off-by: Boqun Feng Link: https://patch.msgid.link/20251228120546.1602275-4-fujita.tomonori@gmail.com --- rust/kernel/sync/atomic/internal.rs | 8 +++++++- rust/kernel/sync/atomic/predefine.rs | 4 ++-- 2 files changed, 9 insertions(+), 3 deletions(-) (limited to 'rust/kernel') diff --git a/rust/kernel/sync/atomic/internal.rs b/rust/kernel/sync/atomic/internal.rs index 1b2a7933bc14..0dac58bca2b3 100644 --- a/rust/kernel/sync/atomic/internal.rs +++ b/rust/kernel/sync/atomic/internal.rs @@ -37,10 +37,16 @@ pub trait AtomicImpl: Sized + Send + Copy + private::Sealed { type Delta; } +// The current helpers of load/store uses `{WRITE,READ}_ONCE()` hence the atomicity is only +// guaranteed against read-modify-write operations if the architecture supports native atomic RmW. +#[cfg(CONFIG_ARCH_SUPPORTS_ATOMIC_RMW)] impl AtomicImpl for i8 { type Delta = Self; } +// The current helpers of load/store uses `{WRITE,READ}_ONCE()` hence the atomicity is only +// guaranteed against read-modify-write operations if the architecture supports native atomic RmW. +#[cfg(CONFIG_ARCH_SUPPORTS_ATOMIC_RMW)] impl AtomicImpl for i16 { type Delta = Self; } @@ -274,7 +280,7 @@ declare_and_impl_atomic_methods!( ); declare_and_impl_atomic_methods!( - [ i32 => atomic, i64 => atomic64 ] + [ i8 => atomic_i8, i16 => atomic_i16, i32 => atomic, i64 => atomic64 ] /// Exchange and compare-and-exchange atomic operations pub trait AtomicExchangeOps { /// Atomic exchange. diff --git a/rust/kernel/sync/atomic/predefine.rs b/rust/kernel/sync/atomic/predefine.rs index 51e9df0cf56e..248d26555ccf 100644 --- a/rust/kernel/sync/atomic/predefine.rs +++ b/rust/kernel/sync/atomic/predefine.rs @@ -149,7 +149,7 @@ mod tests { #[test] fn atomic_xchg_tests() { - for_each_type!(42 in [i32, i64, u32, u64, isize, usize] |v| { + for_each_type!(42 in [i8, i16, i32, i64, u32, u64, isize, usize] |v| { let x = Atomic::new(v); let old = v; @@ -162,7 +162,7 @@ mod tests { #[test] fn atomic_cmpxchg_tests() { - for_each_type!(42 in [i32, i64, u32, u64, isize, usize] |v| { + for_each_type!(42 in [i8, i16, i32, i64, u32, u64, isize, usize] |v| { let x = Atomic::new(v); let old = v; -- cgit v1.2.3 From 06bd0e52bfd78eae1c7dd5db163ce64161b495e7 Mon Sep 17 00:00:00 2001 From: FUJITA Tomonori Date: Thu, 1 Jan 2026 12:49:21 +0900 Subject: rust: sync: atomic: Add atomic bool support via i8 representation Add `bool` support, `Atomic` by using `i8` as its underlying representation. Rust specifies that `bool` has size 1 and alignment 1 [1], so it matches `i8` on layout; keep `static_assert!()` checks to enforce this assumption at build time. [boqun: Remove the unnecessary impl AtomicImpl for bool] Link: https://doc.rust-lang.org/reference/types/boolean.html [1] Signed-off-by: FUJITA Tomonori Reviewed-by: Gary Guo Signed-off-by: Boqun Feng Link: https://patch.msgid.link/20260101034922.2020334-2-fujita.tomonori@gmail.com --- rust/kernel/sync/atomic/predefine.rs | 11 +++++++++++ 1 file changed, 11 insertions(+) (limited to 'rust/kernel') diff --git a/rust/kernel/sync/atomic/predefine.rs b/rust/kernel/sync/atomic/predefine.rs index 248d26555ccf..3fc99174b086 100644 --- a/rust/kernel/sync/atomic/predefine.rs +++ b/rust/kernel/sync/atomic/predefine.rs @@ -5,6 +5,17 @@ use crate::static_assert; use core::mem::{align_of, size_of}; +// Ensure size and alignment requirements are checked. +static_assert!(size_of::() == size_of::()); +static_assert!(align_of::() == align_of::()); + +// SAFETY: `bool` has the same size and alignment as `i8`, and Rust guarantees that `bool` has +// only two valid bit patterns: 0 (false) and 1 (true). Those are valid `i8` values, so `bool` is +// round-trip transmutable to `i8`. +unsafe impl super::AtomicType for bool { + type Repr = i8; +} + // SAFETY: `i8` has the same size and alignment with itself, and is round-trip transmutable to // itself. unsafe impl super::AtomicType for i8 { -- cgit v1.2.3 From 4bac28727a2b3f33e6375aeafdf31df67deff5d0 Mon Sep 17 00:00:00 2001 From: FUJITA Tomonori Date: Thu, 1 Jan 2026 12:49:22 +0900 Subject: rust: sync: atomic: Add atomic bool tests Add tests for Atomic operations. Atomic does not fit into the existing u8/16/32/64 tests so introduce a dedicated test for it. Signed-off-by: FUJITA Tomonori Reviewed-by: Gary Guo Signed-off-by: Boqun Feng Link: https://patch.msgid.link/20260101034922.2020334-3-fujita.tomonori@gmail.com --- rust/kernel/sync/atomic/predefine.rs | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) (limited to 'rust/kernel') diff --git a/rust/kernel/sync/atomic/predefine.rs b/rust/kernel/sync/atomic/predefine.rs index 3fc99174b086..42067c6a266c 100644 --- a/rust/kernel/sync/atomic/predefine.rs +++ b/rust/kernel/sync/atomic/predefine.rs @@ -199,4 +199,20 @@ mod tests { assert_eq!(v + 25, x.load(Relaxed)); }); } + + #[test] + fn atomic_bool_tests() { + let x = Atomic::new(false); + + assert_eq!(false, x.load(Relaxed)); + x.store(true, Relaxed); + assert_eq!(true, x.load(Relaxed)); + + assert_eq!(true, x.xchg(false, Relaxed)); + assert_eq!(false, x.load(Relaxed)); + + assert_eq!(Err(false), x.cmpxchg(true, true, Relaxed)); + assert_eq!(false, x.load(Relaxed)); + assert_eq!(Ok(false), x.cmpxchg(false, true, Full)); + } } -- cgit v1.2.3 From 323e4bfcbe2dc6c6cac6e007dded0ba4f89a6458 Mon Sep 17 00:00:00 2001 From: FUJITA Tomonori Date: Tue, 30 Dec 2025 18:37:17 +0900 Subject: rust: list: Switch to kernel::sync atomic primitives Convert uses of `AtomicBool` to `Atomic`. Note that the compare_exchange migration simplifies to `try_cmpxchg()`, since `try_cmpxchg()` provides relaxed ordering on failure, making the explicit failure ordering unnecessary. Signed-off-by: FUJITA Tomonori Reviewed-by: Alice Ryhl Signed-off-by: Boqun Feng Link: https://patch.msgid.link/20251230093718.1852322-3-fujita.tomonori@gmail.com --- rust/kernel/list/arc.rs | 14 ++++++-------- 1 file changed, 6 insertions(+), 8 deletions(-) (limited to 'rust/kernel') diff --git a/rust/kernel/list/arc.rs b/rust/kernel/list/arc.rs index d92bcf665c89..2282f33913ee 100644 --- a/rust/kernel/list/arc.rs +++ b/rust/kernel/list/arc.rs @@ -6,11 +6,11 @@ use crate::alloc::{AllocError, Flags}; use crate::prelude::*; +use crate::sync::atomic::{ordering, Atomic}; use crate::sync::{Arc, ArcBorrow, UniqueArc}; use core::marker::PhantomPinned; use core::ops::Deref; use core::pin::Pin; -use core::sync::atomic::{AtomicBool, Ordering}; /// Declares that this type has some way to ensure that there is exactly one `ListArc` instance for /// this id. @@ -469,7 +469,7 @@ where /// If the boolean is `false`, then there is no [`ListArc`] for this value. #[repr(transparent)] pub struct AtomicTracker { - inner: AtomicBool, + inner: Atomic, // This value needs to be pinned to justify the INVARIANT: comment in `AtomicTracker::new`. _pin: PhantomPinned, } @@ -480,12 +480,12 @@ impl AtomicTracker { // INVARIANT: Pin-init initializers can't be used on an existing `Arc`, so this value will // not be constructed in an `Arc` that already has a `ListArc`. Self { - inner: AtomicBool::new(false), + inner: Atomic::new(false), _pin: PhantomPinned, } } - fn project_inner(self: Pin<&mut Self>) -> &mut AtomicBool { + fn project_inner(self: Pin<&mut Self>) -> &mut Atomic { // SAFETY: The `inner` field is not structurally pinned, so we may obtain a mutable // reference to it even if we only have a pinned reference to `self`. unsafe { &mut Pin::into_inner_unchecked(self).inner } @@ -500,7 +500,7 @@ impl ListArcSafe for AtomicTracker { unsafe fn on_drop_list_arc(&self) { // INVARIANT: We just dropped a ListArc, so the boolean should be false. - self.inner.store(false, Ordering::Release); + self.inner.store(false, ordering::Release); } } @@ -514,8 +514,6 @@ unsafe impl TryNewListArc for AtomicTracker { fn try_new_list_arc(&self) -> bool { // INVARIANT: If this method returns true, then the boolean used to be false, and is no // longer false, so it is okay for the caller to create a new [`ListArc`]. - self.inner - .compare_exchange(false, true, Ordering::Acquire, Ordering::Relaxed) - .is_ok() + self.inner.cmpxchg(false, true, ordering::Acquire).is_ok() } } -- cgit v1.2.3 From ccf9e070116a81d29aae30db501d562c8efd1ed8 Mon Sep 17 00:00:00 2001 From: Alice Ryhl Date: Thu, 18 Dec 2025 12:10:23 +0000 Subject: rust: sync: Inline various lock related methods While debugging a different issue [1], the following relocation was noticed in the rust_binder.ko file: R_AARCH64_CALL26 _RNvXNtNtNtCsdfZWD8DztAw_6kernel4sync4lock8spinlockNtB2_15SpinLockBackendNtB4_7Backend6unlock This relocation (and a similar one for lock) occurred many times throughout the module. That is not really useful because all this function does is call spin_unlock(), so what we actually want here is that a call to spin_unlock() dirctly is generated in favor of this wrapper method. Thus, mark these methods inline. [boqun: Reword the commit message a bit] Link: https://lore.kernel.org/p/20251111-binder-fix-list-remove-v1-0-8ed14a0da63d@google.com Signed-off-by: Alice Ryhl Reviewed-by: Gary Guo Reviewed-by: Daniel Almeida Signed-off-by: Boqun Feng Link: https://patch.msgid.link/20251218-inline-lock-unlock-v2-1-fbadac8bd61b@google.com --- rust/kernel/sync/lock.rs | 7 +++++++ rust/kernel/sync/lock/global.rs | 2 ++ rust/kernel/sync/lock/mutex.rs | 5 +++++ rust/kernel/sync/lock/spinlock.rs | 5 +++++ 4 files changed, 19 insertions(+) (limited to 'rust/kernel') diff --git a/rust/kernel/sync/lock.rs b/rust/kernel/sync/lock.rs index 46a57d1fc309..10b6b5e9b024 100644 --- a/rust/kernel/sync/lock.rs +++ b/rust/kernel/sync/lock.rs @@ -156,6 +156,7 @@ impl Lock<(), B> { /// the whole lifetime of `'a`. /// /// [`State`]: Backend::State + #[inline] pub unsafe fn from_raw<'a>(ptr: *mut B::State) -> &'a Self { // SAFETY: // - By the safety contract `ptr` must point to a valid initialised instance of `B::State` @@ -169,6 +170,7 @@ impl Lock<(), B> { impl Lock { /// Acquires the lock and gives the caller access to the data protected by it. + #[inline] pub fn lock(&self) -> Guard<'_, T, B> { // SAFETY: The constructor of the type calls `init`, so the existence of the object proves // that `init` was called. @@ -182,6 +184,7 @@ impl Lock { /// Returns a guard that can be used to access the data protected by the lock if successful. // `Option` is not `#[must_use]` even if `T` is, thus the attribute is needed here. #[must_use = "if unused, the lock will be immediately unlocked"] + #[inline] pub fn try_lock(&self) -> Option> { // SAFETY: The constructor of the type calls `init`, so the existence of the object proves // that `init` was called. @@ -275,6 +278,7 @@ impl<'a, T: ?Sized, B: Backend> Guard<'a, T, B> { impl core::ops::Deref for Guard<'_, T, B> { type Target = T; + #[inline] fn deref(&self) -> &Self::Target { // SAFETY: The caller owns the lock, so it is safe to deref the protected data. unsafe { &*self.lock.data.get() } @@ -285,6 +289,7 @@ impl core::ops::DerefMut for Guard<'_, T, B> where T: Unpin, { + #[inline] fn deref_mut(&mut self) -> &mut Self::Target { // SAFETY: The caller owns the lock, so it is safe to deref the protected data. unsafe { &mut *self.lock.data.get() } @@ -292,6 +297,7 @@ where } impl Drop for Guard<'_, T, B> { + #[inline] fn drop(&mut self) { // SAFETY: The caller owns the lock, so it is safe to unlock it. unsafe { B::unlock(self.lock.state.get(), &self.state) }; @@ -304,6 +310,7 @@ impl<'a, T: ?Sized, B: Backend> Guard<'a, T, B> { /// # Safety /// /// The caller must ensure that it owns the lock. + #[inline] pub unsafe fn new(lock: &'a Lock, state: B::GuardState) -> Self { // SAFETY: The caller can only hold the lock if `Backend::init` has already been called. unsafe { B::assert_is_held(lock.state.get()) }; diff --git a/rust/kernel/sync/lock/global.rs b/rust/kernel/sync/lock/global.rs index eab48108a4ae..aecbdc34738f 100644 --- a/rust/kernel/sync/lock/global.rs +++ b/rust/kernel/sync/lock/global.rs @@ -77,6 +77,7 @@ impl GlobalLock { } /// Lock this global lock. + #[inline] pub fn lock(&'static self) -> GlobalGuard { GlobalGuard { inner: self.inner.lock(), @@ -84,6 +85,7 @@ impl GlobalLock { } /// Try to lock this global lock. + #[inline] pub fn try_lock(&'static self) -> Option> { Some(GlobalGuard { inner: self.inner.try_lock()?, diff --git a/rust/kernel/sync/lock/mutex.rs b/rust/kernel/sync/lock/mutex.rs index 581cee7ab842..cda0203efefb 100644 --- a/rust/kernel/sync/lock/mutex.rs +++ b/rust/kernel/sync/lock/mutex.rs @@ -102,6 +102,7 @@ unsafe impl super::Backend for MutexBackend { type State = bindings::mutex; type GuardState = (); + #[inline] unsafe fn init( ptr: *mut Self::State, name: *const crate::ffi::c_char, @@ -112,18 +113,21 @@ unsafe impl super::Backend for MutexBackend { unsafe { bindings::__mutex_init(ptr, name, key) } } + #[inline] unsafe fn lock(ptr: *mut Self::State) -> Self::GuardState { // SAFETY: The safety requirements of this function ensure that `ptr` points to valid // memory, and that it has been initialised before. unsafe { bindings::mutex_lock(ptr) }; } + #[inline] unsafe fn unlock(ptr: *mut Self::State, _guard_state: &Self::GuardState) { // SAFETY: The safety requirements of this function ensure that `ptr` is valid and that the // caller is the owner of the mutex. unsafe { bindings::mutex_unlock(ptr) }; } + #[inline] unsafe fn try_lock(ptr: *mut Self::State) -> Option { // SAFETY: The `ptr` pointer is guaranteed to be valid and initialized before use. let result = unsafe { bindings::mutex_trylock(ptr) }; @@ -135,6 +139,7 @@ unsafe impl super::Backend for MutexBackend { } } + #[inline] unsafe fn assert_is_held(ptr: *mut Self::State) { // SAFETY: The `ptr` pointer is guaranteed to be valid and initialized before use. unsafe { bindings::mutex_assert_is_held(ptr) } diff --git a/rust/kernel/sync/lock/spinlock.rs b/rust/kernel/sync/lock/spinlock.rs index d7be38ccbdc7..ef76fa07ca3a 100644 --- a/rust/kernel/sync/lock/spinlock.rs +++ b/rust/kernel/sync/lock/spinlock.rs @@ -101,6 +101,7 @@ unsafe impl super::Backend for SpinLockBackend { type State = bindings::spinlock_t; type GuardState = (); + #[inline] unsafe fn init( ptr: *mut Self::State, name: *const crate::ffi::c_char, @@ -111,18 +112,21 @@ unsafe impl super::Backend for SpinLockBackend { unsafe { bindings::__spin_lock_init(ptr, name, key) } } + #[inline] unsafe fn lock(ptr: *mut Self::State) -> Self::GuardState { // SAFETY: The safety requirements of this function ensure that `ptr` points to valid // memory, and that it has been initialised before. unsafe { bindings::spin_lock(ptr) } } + #[inline] unsafe fn unlock(ptr: *mut Self::State, _guard_state: &Self::GuardState) { // SAFETY: The safety requirements of this function ensure that `ptr` is valid and that the // caller is the owner of the spinlock. unsafe { bindings::spin_unlock(ptr) } } + #[inline] unsafe fn try_lock(ptr: *mut Self::State) -> Option { // SAFETY: The `ptr` pointer is guaranteed to be valid and initialized before use. let result = unsafe { bindings::spin_trylock(ptr) }; @@ -134,6 +138,7 @@ unsafe impl super::Backend for SpinLockBackend { } } + #[inline] unsafe fn assert_is_held(ptr: *mut Self::State) { // SAFETY: The `ptr` pointer is guaranteed to be valid and initialized before use. unsafe { bindings::spin_assert_is_held(ptr) } -- cgit v1.2.3 From 601cd264a31d198d5710846c9b53dfa92f1c4268 Mon Sep 17 00:00:00 2001 From: Alok Tiwari Date: Sat, 10 Jan 2026 03:48:13 -0800 Subject: rust: auxiliary: fix remove_callback invariant comment Correct copy-paste errors where remove_callback safety invariants incorrectly referenced probe_callback(). Signed-off-by: Alok Tiwari Reviewed-by: Alice Ryhl Link: https://patch.msgid.link/20260110114817.2312828-1-alok.a.tiwari@oracle.com Signed-off-by: Danilo Krummrich --- rust/kernel/auxiliary.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'rust/kernel') diff --git a/rust/kernel/auxiliary.rs b/rust/kernel/auxiliary.rs index f8273cf165dc..d2890a2c543a 100644 --- a/rust/kernel/auxiliary.rs +++ b/rust/kernel/auxiliary.rs @@ -92,7 +92,7 @@ impl Adapter { // SAFETY: The auxiliary bus only ever calls the probe callback with a valid pointer to a // `struct auxiliary_device`. // - // INVARIANT: `adev` is valid for the duration of `probe_callback()`. + // INVARIANT: `adev` is valid for the duration of `remove_callback()`. let adev = unsafe { &*adev.cast::>() }; // SAFETY: `remove_callback` is only ever called after a successful call to -- cgit v1.2.3 From 585e8a26abfd0c453f01ef78bd637cb7600e9b04 Mon Sep 17 00:00:00 2001 From: Alok Tiwari Date: Sat, 10 Jan 2026 03:51:56 -0800 Subject: rust: platform: fix remove_callback invariant comment Correct copy-paste errors where remove_callback safety invariants incorrectly referenced probe_callback(). Signed-off-by: Alok Tiwari Reviewed-by: Alice Ryhl Link: https://patch.msgid.link/20260110115159.2313116-1-alok.a.tiwari@oracle.com Signed-off-by: Danilo Krummrich --- rust/kernel/platform.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'rust/kernel') diff --git a/rust/kernel/platform.rs b/rust/kernel/platform.rs index 8836ac46b066..bdf89c8293fc 100644 --- a/rust/kernel/platform.rs +++ b/rust/kernel/platform.rs @@ -103,7 +103,7 @@ impl Adapter { // SAFETY: The platform bus only ever calls the remove callback with a valid pointer to a // `struct platform_device`. // - // INVARIANT: `pdev` is valid for the duration of `probe_callback()`. + // INVARIANT: `pdev` is valid for the duration of `remove_callback()`. let pdev = unsafe { &*pdev.cast::>() }; // SAFETY: `remove_callback` is only ever called after a successful call to -- cgit v1.2.3 From 33d19f621641de1b6ec6fe1bb2ac68a7d2c61f6a Mon Sep 17 00:00:00 2001 From: Alexandre Courbot Date: Mon, 8 Dec 2025 11:47:00 +0900 Subject: rust: io: always inline functions using build_assert with arguments `build_assert` relies on the compiler to optimize out its error path. Functions using it with its arguments must thus always be inlined, otherwise the error path of `build_assert` might not be optimized out, triggering a build error. Cc: stable@vger.kernel.org Fixes: ce30d94e6855 ("rust: add `io::{Io, IoRaw}` base types") Reviewed-by: Daniel Almeida Signed-off-by: Alexandre Courbot Tested-by: Timur Tabi Link: https://patch.msgid.link/20251208-io-build-assert-v3-2-98aded02c1ea@nvidia.com Signed-off-by: Danilo Krummrich --- rust/kernel/io.rs | 9 ++++++--- rust/kernel/io/resource.rs | 2 ++ 2 files changed, 8 insertions(+), 3 deletions(-) (limited to 'rust/kernel') diff --git a/rust/kernel/io.rs b/rust/kernel/io.rs index 98e8b84e68d1..b64b11f75a35 100644 --- a/rust/kernel/io.rs +++ b/rust/kernel/io.rs @@ -142,7 +142,8 @@ macro_rules! define_read { /// Bound checks are performed on compile time, hence if the offset is not known at compile /// time, the build will fail. $(#[$attr])* - #[inline] + // Always inline to optimize out error path of `io_addr_assert`. + #[inline(always)] pub fn $name(&self, offset: usize) -> $type_name { let addr = self.io_addr_assert::<$type_name>(offset); @@ -171,7 +172,8 @@ macro_rules! define_write { /// Bound checks are performed on compile time, hence if the offset is not known at compile /// time, the build will fail. $(#[$attr])* - #[inline] + // Always inline to optimize out error path of `io_addr_assert`. + #[inline(always)] pub fn $name(&self, value: $type_name, offset: usize) { let addr = self.io_addr_assert::<$type_name>(offset); @@ -239,7 +241,8 @@ impl Io { self.addr().checked_add(offset).ok_or(EINVAL) } - #[inline] + // Always inline to optimize out error path of `build_assert`. + #[inline(always)] fn io_addr_assert(&self, offset: usize) -> usize { build_assert!(Self::offset_valid::(offset, SIZE)); diff --git a/rust/kernel/io/resource.rs b/rust/kernel/io/resource.rs index 56cfde97ce87..b7ac9faf141d 100644 --- a/rust/kernel/io/resource.rs +++ b/rust/kernel/io/resource.rs @@ -226,6 +226,8 @@ impl Flags { /// Resource represents a memory region that must be ioremaped using `ioremap_np`. pub const IORESOURCE_MEM_NONPOSTED: Flags = Flags::new(bindings::IORESOURCE_MEM_NONPOSTED); + // Always inline to optimize out error path of `build_assert`. + #[inline(always)] const fn new(value: u32) -> Self { crate::build_assert!(value as u64 <= c_ulong::MAX as u64); Flags(value as c_ulong) -- cgit v1.2.3 From 5d9c4c272ba06055d19e05c2a02e16e58acc8943 Mon Sep 17 00:00:00 2001 From: Alexandre Courbot Date: Mon, 8 Dec 2025 11:47:04 +0900 Subject: rust: irq: always inline functions using build_assert with arguments `build_assert` relies on the compiler to optimize out its error path. Functions using it with its arguments must thus always be inlined, otherwise the error path of `build_assert` might not be optimized out, triggering a build error. Cc: stable@vger.kernel.org Fixes: 746680ec6696 ("rust: irq: add flags module") Reviewed-by: Daniel Almeida Signed-off-by: Alexandre Courbot Link: https://patch.msgid.link/20251208-io-build-assert-v3-6-98aded02c1ea@nvidia.com Signed-off-by: Danilo Krummrich --- rust/kernel/irq/flags.rs | 2 ++ 1 file changed, 2 insertions(+) (limited to 'rust/kernel') diff --git a/rust/kernel/irq/flags.rs b/rust/kernel/irq/flags.rs index adfde96ec47c..d26e25af06ee 100644 --- a/rust/kernel/irq/flags.rs +++ b/rust/kernel/irq/flags.rs @@ -96,6 +96,8 @@ impl Flags { self.0 } + // Always inline to optimize out error path of `build_assert`. + #[inline(always)] const fn new(value: u32) -> Self { build_assert!(value as u64 <= c_ulong::MAX as u64); Self(value as c_ulong) -- cgit v1.2.3 From 4181aceb4af414bd6d2ce5eb9a22637bbb4f5f8c Mon Sep 17 00:00:00 2001 From: Danilo Krummrich Date: Wed, 7 Jan 2026 11:35:00 +0100 Subject: rust: i2c: do not drop device private data on shutdown() We must not drop the device private data on shutdown(); none of the registrations attached to devres that might access the device private data are released before shutdown() is called. Hence, freeing the device private data on shutdown() can cause UAF bugs. Fixes: 57c5bd9aee94 ("rust: i2c: add basic I2C device and driver abstractions") Acked-by: Alice Ryhl Acked-by: Igor Korotin Link: https://patch.msgid.link/20260107103511.570525-2-dakr@kernel.org Signed-off-by: Danilo Krummrich --- rust/kernel/i2c.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'rust/kernel') diff --git a/rust/kernel/i2c.rs b/rust/kernel/i2c.rs index 491e6cc25cf4..35b678b78d91 100644 --- a/rust/kernel/i2c.rs +++ b/rust/kernel/i2c.rs @@ -181,9 +181,9 @@ impl Adapter { // SAFETY: `shutdown_callback` is only ever called after a successful call to // `probe_callback`, hence it's guaranteed that `Device::set_drvdata()` has been called // and stored a `Pin>`. - let data = unsafe { idev.as_ref().drvdata_obtain::() }; + let data = unsafe { idev.as_ref().drvdata_borrow::() }; - T::shutdown(idev, data.as_ref()); + T::shutdown(idev, data); } /// The [`i2c::IdTable`] of the corresponding driver. -- cgit v1.2.3 From 5f4476e98387618ce22bb93fb5c11142827458ec Mon Sep 17 00:00:00 2001 From: Danilo Krummrich Date: Wed, 7 Jan 2026 11:35:01 +0100 Subject: rust: auxiliary: add Driver::unbind() callback Add missing unbind() callback to auxiliary::Driver, since it will be needed by drivers eventually (e.g. the Nova DRM driver). Acked-by: Alice Ryhl Link: https://patch.msgid.link/20260107103511.570525-3-dakr@kernel.org Signed-off-by: Danilo Krummrich --- rust/kernel/auxiliary.rs | 18 +++++++++++++++++- 1 file changed, 17 insertions(+), 1 deletion(-) (limited to 'rust/kernel') diff --git a/rust/kernel/auxiliary.rs b/rust/kernel/auxiliary.rs index 56f3c180e8f6..6931f8a4267f 100644 --- a/rust/kernel/auxiliary.rs +++ b/rust/kernel/auxiliary.rs @@ -87,7 +87,9 @@ impl Adapter { // SAFETY: `remove_callback` is only ever called after a successful call to // `probe_callback`, hence it's guaranteed that `Device::set_drvdata()` has been called // and stored a `Pin>`. - drop(unsafe { adev.as_ref().drvdata_obtain::() }); + let data = unsafe { adev.as_ref().drvdata_obtain::() }; + + T::unbind(adev, data.as_ref()); } } @@ -187,6 +189,20 @@ pub trait Driver { /// /// Called when an auxiliary device is matches a corresponding driver. fn probe(dev: &Device, id_info: &Self::IdInfo) -> impl PinInit; + + /// Auxiliary driver unbind. + /// + /// Called when a [`Device`] is unbound from its bound [`Driver`]. Implementing this callback + /// is optional. + /// + /// This callback serves as a place for drivers to perform teardown operations that require a + /// `&Device` or `&Device` reference. For instance, drivers may try to perform I/O + /// operations to gracefully tear down the device. + /// + /// Otherwise, release operations for driver resources should be performed in `Self::drop`. + fn unbind(dev: &Device, this: Pin<&Self>) { + let _ = (dev, this); + } } /// The auxiliary device representation. -- cgit v1.2.3 From 1d40cb05e077bb294f128c6a52630b93e452a8ed Mon Sep 17 00:00:00 2001 From: Tamir Duberstein Date: Mon, 22 Dec 2025 13:12:20 +0100 Subject: rust: configfs: replace `kernel::c_str!` with C-Strings C-String literals were added in Rust 1.77. Replace instances of `kernel::c_str!` with C-String literals where possible. Acked-by: Greg Kroah-Hartman Reviewed-by: Alice Ryhl Reviewed-by: Benno Lossin Signed-off-by: Tamir Duberstein Acked-by: Andreas Hindborg Link: https://lore.kernel.org/r/20251222-cstr-configfs-v1-1-cc1665c51c43@gmail.com Signed-off-by: Andreas Hindborg --- rust/kernel/configfs.rs | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) (limited to 'rust/kernel') diff --git a/rust/kernel/configfs.rs b/rust/kernel/configfs.rs index 466fb7f40762..2339c6467325 100644 --- a/rust/kernel/configfs.rs +++ b/rust/kernel/configfs.rs @@ -21,7 +21,6 @@ //! //! ```ignore //! use kernel::alloc::flags; -//! use kernel::c_str; //! use kernel::configfs_attrs; //! use kernel::configfs; //! use kernel::new_mutex; @@ -50,7 +49,7 @@ //! //! try_pin_init!(Self { //! config <- configfs::Subsystem::new( -//! c_str!("rust_configfs"), item_type, Configuration::new() +//! c"rust_configfs", item_type, Configuration::new() //! ), //! }) //! } @@ -66,7 +65,7 @@ //! impl Configuration { //! fn new() -> impl PinInit { //! try_pin_init!(Self { -//! message: c_str!("Hello World\n"), +//! message: c"Hello World\n", //! bar <- new_mutex!((KBox::new([0; PAGE_SIZE], flags::GFP_KERNEL)?, 0)), //! }) //! } @@ -1000,7 +999,9 @@ macro_rules! configfs_attrs { static [< $data:upper _ $name:upper _ATTR >]: $crate::configfs::Attribute<$attr, $data, $data> = unsafe { - $crate::configfs::Attribute::new(c_str!(::core::stringify!($name))) + $crate::configfs::Attribute::new( + $crate::c_str!(::core::stringify!($name)), + ) }; )* -- cgit v1.2.3 From 0af1a9e4629a85964a7eebe58ebd2ca37c8c21fc Mon Sep 17 00:00:00 2001 From: Danilo Krummrich Date: Wed, 7 Jan 2026 11:35:02 +0100 Subject: rust: driver: introduce a DriverLayout trait The DriverLayout trait describes the layout of a specific driver structure, such as `struct pci_driver` or `struct platform_driver`. In a first step, this replaces the associated type RegType of the RegistrationOps with the DriverLayout::DriverType associated type. Acked-by: Alice Ryhl Acked-by: Igor Korotin Link: https://patch.msgid.link/20260107103511.570525-4-dakr@kernel.org [ Rename driver::Driver to driver::DriverLayout, as it represents the layout of a driver structure rather than the driver structure itself. - Danilo ] Signed-off-by: Danilo Krummrich --- rust/kernel/auxiliary.rs | 18 +++++++++++------- rust/kernel/driver.rs | 40 +++++++++++++++++++++++++--------------- rust/kernel/i2c.rs | 18 +++++++++++------- rust/kernel/pci.rs | 18 +++++++++++------- rust/kernel/platform.rs | 18 +++++++++++------- rust/kernel/usb.rs | 18 +++++++++++------- 6 files changed, 80 insertions(+), 50 deletions(-) (limited to 'rust/kernel') diff --git a/rust/kernel/auxiliary.rs b/rust/kernel/auxiliary.rs index 6931f8a4267f..9922b9158d16 100644 --- a/rust/kernel/auxiliary.rs +++ b/rust/kernel/auxiliary.rs @@ -23,13 +23,17 @@ use core::{ /// An adapter for the registration of auxiliary drivers. pub struct Adapter(T); -// SAFETY: A call to `unregister` for a given instance of `RegType` is guaranteed to be valid if +// SAFETY: +// - `bindings::auxiliary_driver` is a C type declared as `repr(C)`. +unsafe impl driver::DriverLayout for Adapter { + type DriverType = bindings::auxiliary_driver; +} + +// SAFETY: A call to `unregister` for a given instance of `DriverType` is guaranteed to be valid if // a preceding call to `register` has been successful. unsafe impl driver::RegistrationOps for Adapter { - type RegType = bindings::auxiliary_driver; - unsafe fn register( - adrv: &Opaque, + adrv: &Opaque, name: &'static CStr, module: &'static ThisModule, ) -> Result { @@ -41,14 +45,14 @@ unsafe impl driver::RegistrationOps for Adapter { (*adrv.get()).id_table = T::ID_TABLE.as_ptr(); } - // SAFETY: `adrv` is guaranteed to be a valid `RegType`. + // SAFETY: `adrv` is guaranteed to be a valid `DriverType`. to_result(unsafe { bindings::__auxiliary_driver_register(adrv.get(), module.0, name.as_char_ptr()) }) } - unsafe fn unregister(adrv: &Opaque) { - // SAFETY: `adrv` is guaranteed to be a valid `RegType`. + unsafe fn unregister(adrv: &Opaque) { + // SAFETY: `adrv` is guaranteed to be a valid `DriverType`. unsafe { bindings::auxiliary_driver_unregister(adrv.get()) } } } diff --git a/rust/kernel/driver.rs b/rust/kernel/driver.rs index 649d06468f41..73968b13d7dc 100644 --- a/rust/kernel/driver.rs +++ b/rust/kernel/driver.rs @@ -99,23 +99,33 @@ use crate::{acpi, device, of, str::CStr, try_pin_init, types::Opaque, ThisModule use core::pin::Pin; use pin_init::{pin_data, pinned_drop, PinInit}; +/// Trait describing the layout of a specific device driver. +/// +/// This trait describes the layout of a specific driver structure, such as `struct pci_driver` or +/// `struct platform_driver`. +/// +/// # Safety +/// +/// Implementors must guarantee that: +/// - `DriverType` is `repr(C)`. +pub unsafe trait DriverLayout { + /// The specific driver type embedding a `struct device_driver`. + type DriverType: Default; +} + /// The [`RegistrationOps`] trait serves as generic interface for subsystems (e.g., PCI, Platform, /// Amba, etc.) to provide the corresponding subsystem specific implementation to register / -/// unregister a driver of the particular type (`RegType`). +/// unregister a driver of the particular type (`DriverType`). /// -/// For instance, the PCI subsystem would set `RegType` to `bindings::pci_driver` and call +/// For instance, the PCI subsystem would set `DriverType` to `bindings::pci_driver` and call /// `bindings::__pci_register_driver` from `RegistrationOps::register` and /// `bindings::pci_unregister_driver` from `RegistrationOps::unregister`. /// /// # Safety /// -/// A call to [`RegistrationOps::unregister`] for a given instance of `RegType` is only valid if a -/// preceding call to [`RegistrationOps::register`] has been successful. -pub unsafe trait RegistrationOps { - /// The type that holds information about the registration. This is typically a struct defined - /// by the C portion of the kernel. - type RegType: Default; - +/// A call to [`RegistrationOps::unregister`] for a given instance of `DriverType` is only valid if +/// a preceding call to [`RegistrationOps::register`] has been successful. +pub unsafe trait RegistrationOps: DriverLayout { /// Registers a driver. /// /// # Safety @@ -123,7 +133,7 @@ pub unsafe trait RegistrationOps { /// On success, `reg` must remain pinned and valid until the matching call to /// [`RegistrationOps::unregister`]. unsafe fn register( - reg: &Opaque, + reg: &Opaque, name: &'static CStr, module: &'static ThisModule, ) -> Result; @@ -134,7 +144,7 @@ pub unsafe trait RegistrationOps { /// /// Must only be called after a preceding successful call to [`RegistrationOps::register`] for /// the same `reg`. - unsafe fn unregister(reg: &Opaque); + unsafe fn unregister(reg: &Opaque); } /// A [`Registration`] is a generic type that represents the registration of some driver type (e.g. @@ -146,7 +156,7 @@ pub unsafe trait RegistrationOps { #[pin_data(PinnedDrop)] pub struct Registration { #[pin] - reg: Opaque, + reg: Opaque, } // SAFETY: `Registration` has no fields or methods accessible via `&Registration`, so it is safe to @@ -161,13 +171,13 @@ impl Registration { /// Creates a new instance of the registration object. pub fn new(name: &'static CStr, module: &'static ThisModule) -> impl PinInit { try_pin_init!(Self { - reg <- Opaque::try_ffi_init(|ptr: *mut T::RegType| { + reg <- Opaque::try_ffi_init(|ptr: *mut T::DriverType| { // SAFETY: `try_ffi_init` guarantees that `ptr` is valid for write. - unsafe { ptr.write(T::RegType::default()) }; + unsafe { ptr.write(T::DriverType::default()) }; // SAFETY: `try_ffi_init` guarantees that `ptr` is valid for write, and it has // just been initialised above, so it's also valid for read. - let drv = unsafe { &*(ptr as *const Opaque) }; + let drv = unsafe { &*(ptr as *const Opaque) }; // SAFETY: `drv` is guaranteed to be pinned until `T::unregister`. unsafe { T::register(drv, name, module) } diff --git a/rust/kernel/i2c.rs b/rust/kernel/i2c.rs index 35b678b78d91..8e80d8572e1a 100644 --- a/rust/kernel/i2c.rs +++ b/rust/kernel/i2c.rs @@ -92,13 +92,17 @@ macro_rules! i2c_device_table { /// An adapter for the registration of I2C drivers. pub struct Adapter(T); -// SAFETY: A call to `unregister` for a given instance of `RegType` is guaranteed to be valid if +// SAFETY: +// - `bindings::i2c_driver` is a C type declared as `repr(C)`. +unsafe impl driver::DriverLayout for Adapter { + type DriverType = bindings::i2c_driver; +} + +// SAFETY: A call to `unregister` for a given instance of `DriverType` is guaranteed to be valid if // a preceding call to `register` has been successful. unsafe impl driver::RegistrationOps for Adapter { - type RegType = bindings::i2c_driver; - unsafe fn register( - idrv: &Opaque, + idrv: &Opaque, name: &'static CStr, module: &'static ThisModule, ) -> Result { @@ -133,12 +137,12 @@ unsafe impl driver::RegistrationOps for Adapter { (*idrv.get()).driver.acpi_match_table = acpi_table; } - // SAFETY: `idrv` is guaranteed to be a valid `RegType`. + // SAFETY: `idrv` is guaranteed to be a valid `DriverType`. to_result(unsafe { bindings::i2c_register_driver(module.0, idrv.get()) }) } - unsafe fn unregister(idrv: &Opaque) { - // SAFETY: `idrv` is guaranteed to be a valid `RegType`. + unsafe fn unregister(idrv: &Opaque) { + // SAFETY: `idrv` is guaranteed to be a valid `DriverType`. unsafe { bindings::i2c_del_driver(idrv.get()) } } } diff --git a/rust/kernel/pci.rs b/rust/kernel/pci.rs index 82e128431f08..703ce5709f0c 100644 --- a/rust/kernel/pci.rs +++ b/rust/kernel/pci.rs @@ -50,13 +50,17 @@ pub use self::irq::{ /// An adapter for the registration of PCI drivers. pub struct Adapter(T); -// SAFETY: A call to `unregister` for a given instance of `RegType` is guaranteed to be valid if +// SAFETY: +// - `bindings::pci_driver` is a C type declared as `repr(C)`. +unsafe impl driver::DriverLayout for Adapter { + type DriverType = bindings::pci_driver; +} + +// SAFETY: A call to `unregister` for a given instance of `DriverType` is guaranteed to be valid if // a preceding call to `register` has been successful. unsafe impl driver::RegistrationOps for Adapter { - type RegType = bindings::pci_driver; - unsafe fn register( - pdrv: &Opaque, + pdrv: &Opaque, name: &'static CStr, module: &'static ThisModule, ) -> Result { @@ -68,14 +72,14 @@ unsafe impl driver::RegistrationOps for Adapter { (*pdrv.get()).id_table = T::ID_TABLE.as_ptr(); } - // SAFETY: `pdrv` is guaranteed to be a valid `RegType`. + // SAFETY: `pdrv` is guaranteed to be a valid `DriverType`. to_result(unsafe { bindings::__pci_register_driver(pdrv.get(), module.0, name.as_char_ptr()) }) } - unsafe fn unregister(pdrv: &Opaque) { - // SAFETY: `pdrv` is guaranteed to be a valid `RegType`. + unsafe fn unregister(pdrv: &Opaque) { + // SAFETY: `pdrv` is guaranteed to be a valid `DriverType`. unsafe { bindings::pci_unregister_driver(pdrv.get()) } } } diff --git a/rust/kernel/platform.rs b/rust/kernel/platform.rs index ed889f079cab..93a64cf86b76 100644 --- a/rust/kernel/platform.rs +++ b/rust/kernel/platform.rs @@ -26,13 +26,17 @@ use core::{ /// An adapter for the registration of platform drivers. pub struct Adapter(T); -// SAFETY: A call to `unregister` for a given instance of `RegType` is guaranteed to be valid if +// SAFETY: +// - `bindings::platform_driver` is a C type declared as `repr(C)`. +unsafe impl driver::DriverLayout for Adapter { + type DriverType = bindings::platform_driver; +} + +// SAFETY: A call to `unregister` for a given instance of `DriverType` is guaranteed to be valid if // a preceding call to `register` has been successful. unsafe impl driver::RegistrationOps for Adapter { - type RegType = bindings::platform_driver; - unsafe fn register( - pdrv: &Opaque, + pdrv: &Opaque, name: &'static CStr, module: &'static ThisModule, ) -> Result { @@ -55,12 +59,12 @@ unsafe impl driver::RegistrationOps for Adapter { (*pdrv.get()).driver.acpi_match_table = acpi_table; } - // SAFETY: `pdrv` is guaranteed to be a valid `RegType`. + // SAFETY: `pdrv` is guaranteed to be a valid `DriverType`. to_result(unsafe { bindings::__platform_driver_register(pdrv.get(), module.0) }) } - unsafe fn unregister(pdrv: &Opaque) { - // SAFETY: `pdrv` is guaranteed to be a valid `RegType`. + unsafe fn unregister(pdrv: &Opaque) { + // SAFETY: `pdrv` is guaranteed to be a valid `DriverType`. unsafe { bindings::platform_driver_unregister(pdrv.get()) }; } } diff --git a/rust/kernel/usb.rs b/rust/kernel/usb.rs index d10b65e9fb6a..60b761c06fbd 100644 --- a/rust/kernel/usb.rs +++ b/rust/kernel/usb.rs @@ -27,13 +27,17 @@ use core::{ /// An adapter for the registration of USB drivers. pub struct Adapter(T); -// SAFETY: A call to `unregister` for a given instance of `RegType` is guaranteed to be valid if +// SAFETY: +// - `bindings::usb_driver` is a C type declared as `repr(C)`. +unsafe impl driver::DriverLayout for Adapter { + type DriverType = bindings::usb_driver; +} + +// SAFETY: A call to `unregister` for a given instance of `DriverType` is guaranteed to be valid if // a preceding call to `register` has been successful. unsafe impl driver::RegistrationOps for Adapter { - type RegType = bindings::usb_driver; - unsafe fn register( - udrv: &Opaque, + udrv: &Opaque, name: &'static CStr, module: &'static ThisModule, ) -> Result { @@ -45,14 +49,14 @@ unsafe impl driver::RegistrationOps for Adapter { (*udrv.get()).id_table = T::ID_TABLE.as_ptr(); } - // SAFETY: `udrv` is guaranteed to be a valid `RegType`. + // SAFETY: `udrv` is guaranteed to be a valid `DriverType`. to_result(unsafe { bindings::usb_register_driver(udrv.get(), module.0, name.as_char_ptr()) }) } - unsafe fn unregister(udrv: &Opaque) { - // SAFETY: `udrv` is guaranteed to be a valid `RegType`. + unsafe fn unregister(udrv: &Opaque) { + // SAFETY: `udrv` is guaranteed to be a valid `DriverType`. unsafe { bindings::usb_deregister(udrv.get()) }; } } -- cgit v1.2.3 From c1d4519e1c36ffa01973e23af4502e69dcd84f39 Mon Sep 17 00:00:00 2001 From: Danilo Krummrich Date: Wed, 7 Jan 2026 11:35:03 +0100 Subject: rust: driver: add DEVICE_DRIVER_OFFSET to the DriverLayout trait Add an associated const DEVICE_DRIVER_OFFSET to the DriverLayout trait indicating the offset of the embedded struct device_driver within Self::DriverType, i.e. the specific driver structs, such as struct pci_driver or struct platform_driver. Acked-by: Alice Ryhl Acked-by: Igor Korotin Link: https://patch.msgid.link/20260107103511.570525-5-dakr@kernel.org Signed-off-by: Danilo Krummrich --- rust/kernel/auxiliary.rs | 3 +++ rust/kernel/driver.rs | 8 +++++++- rust/kernel/i2c.rs | 3 +++ rust/kernel/pci.rs | 3 +++ rust/kernel/platform.rs | 3 +++ rust/kernel/usb.rs | 3 +++ 6 files changed, 22 insertions(+), 1 deletion(-) (limited to 'rust/kernel') diff --git a/rust/kernel/auxiliary.rs b/rust/kernel/auxiliary.rs index 9922b9158d16..9b25af331ad5 100644 --- a/rust/kernel/auxiliary.rs +++ b/rust/kernel/auxiliary.rs @@ -25,8 +25,11 @@ pub struct Adapter(T); // SAFETY: // - `bindings::auxiliary_driver` is a C type declared as `repr(C)`. +// - `struct auxiliary_driver` embeds a `struct device_driver`. +// - `DEVICE_DRIVER_OFFSET` is the correct byte offset to the embedded `struct device_driver`. unsafe impl driver::DriverLayout for Adapter { type DriverType = bindings::auxiliary_driver; + const DEVICE_DRIVER_OFFSET: usize = core::mem::offset_of!(Self::DriverType, driver); } // SAFETY: A call to `unregister` for a given instance of `DriverType` is guaranteed to be valid if diff --git a/rust/kernel/driver.rs b/rust/kernel/driver.rs index 73968b13d7dc..4a96a07905d1 100644 --- a/rust/kernel/driver.rs +++ b/rust/kernel/driver.rs @@ -107,10 +107,16 @@ use pin_init::{pin_data, pinned_drop, PinInit}; /// # Safety /// /// Implementors must guarantee that: -/// - `DriverType` is `repr(C)`. +/// - `DriverType` is `repr(C)`, +/// - `DriverType` embeds a valid `struct device_driver` at byte offset `DEVICE_DRIVER_OFFSET`. pub unsafe trait DriverLayout { /// The specific driver type embedding a `struct device_driver`. type DriverType: Default; + + /// Byte offset of the embedded `struct device_driver` within `DriverType`. + /// + /// This must correspond exactly to the location of the embedded `struct device_driver` field. + const DEVICE_DRIVER_OFFSET: usize; } /// The [`RegistrationOps`] trait serves as generic interface for subsystems (e.g., PCI, Platform, diff --git a/rust/kernel/i2c.rs b/rust/kernel/i2c.rs index 8e80d8572e1a..d97e73282003 100644 --- a/rust/kernel/i2c.rs +++ b/rust/kernel/i2c.rs @@ -94,8 +94,11 @@ pub struct Adapter(T); // SAFETY: // - `bindings::i2c_driver` is a C type declared as `repr(C)`. +// - `struct i2c_driver` embeds a `struct device_driver`. +// - `DEVICE_DRIVER_OFFSET` is the correct byte offset to the embedded `struct device_driver`. unsafe impl driver::DriverLayout for Adapter { type DriverType = bindings::i2c_driver; + const DEVICE_DRIVER_OFFSET: usize = core::mem::offset_of!(Self::DriverType, driver); } // SAFETY: A call to `unregister` for a given instance of `DriverType` is guaranteed to be valid if diff --git a/rust/kernel/pci.rs b/rust/kernel/pci.rs index 703ce5709f0c..fe6f508b0cac 100644 --- a/rust/kernel/pci.rs +++ b/rust/kernel/pci.rs @@ -52,8 +52,11 @@ pub struct Adapter(T); // SAFETY: // - `bindings::pci_driver` is a C type declared as `repr(C)`. +// - `struct pci_driver` embeds a `struct device_driver`. +// - `DEVICE_DRIVER_OFFSET` is the correct byte offset to the embedded `struct device_driver`. unsafe impl driver::DriverLayout for Adapter { type DriverType = bindings::pci_driver; + const DEVICE_DRIVER_OFFSET: usize = core::mem::offset_of!(Self::DriverType, driver); } // SAFETY: A call to `unregister` for a given instance of `DriverType` is guaranteed to be valid if diff --git a/rust/kernel/platform.rs b/rust/kernel/platform.rs index 93a64cf86b76..716c9cc25aea 100644 --- a/rust/kernel/platform.rs +++ b/rust/kernel/platform.rs @@ -28,8 +28,11 @@ pub struct Adapter(T); // SAFETY: // - `bindings::platform_driver` is a C type declared as `repr(C)`. +// - `struct platform_driver` embeds a `struct device_driver`. +// - `DEVICE_DRIVER_OFFSET` is the correct byte offset to the embedded `struct device_driver`. unsafe impl driver::DriverLayout for Adapter { type DriverType = bindings::platform_driver; + const DEVICE_DRIVER_OFFSET: usize = core::mem::offset_of!(Self::DriverType, driver); } // SAFETY: A call to `unregister` for a given instance of `DriverType` is guaranteed to be valid if diff --git a/rust/kernel/usb.rs b/rust/kernel/usb.rs index 60b761c06fbd..eb1c9b9ef228 100644 --- a/rust/kernel/usb.rs +++ b/rust/kernel/usb.rs @@ -29,8 +29,11 @@ pub struct Adapter(T); // SAFETY: // - `bindings::usb_driver` is a C type declared as `repr(C)`. +// - `struct usb_driver` embeds a `struct device_driver`. +// - `DEVICE_DRIVER_OFFSET` is the correct byte offset to the embedded `struct device_driver`. unsafe impl driver::DriverLayout for Adapter { type DriverType = bindings::usb_driver; + const DEVICE_DRIVER_OFFSET: usize = core::mem::offset_of!(Self::DriverType, driver); } // SAFETY: A call to `unregister` for a given instance of `DriverType` is guaranteed to be valid if -- cgit v1.2.3 From 2ad0f490c224283eb5b38f81e247000ce3c714d3 Mon Sep 17 00:00:00 2001 From: Danilo Krummrich Date: Wed, 7 Jan 2026 11:35:04 +0100 Subject: rust: driver: add DriverData type to the DriverLayout trait Add an associated type DriverData to the DriverLayout trait indicating the type of the driver's device private data. Acked-by: Alice Ryhl Acked-by: Igor Korotin Link: https://patch.msgid.link/20260107103511.570525-6-dakr@kernel.org Signed-off-by: Danilo Krummrich --- rust/kernel/auxiliary.rs | 2 ++ rust/kernel/driver.rs | 4 ++++ rust/kernel/i2c.rs | 2 ++ rust/kernel/pci.rs | 2 ++ rust/kernel/platform.rs | 2 ++ rust/kernel/usb.rs | 2 ++ 6 files changed, 14 insertions(+) (limited to 'rust/kernel') diff --git a/rust/kernel/auxiliary.rs b/rust/kernel/auxiliary.rs index 9b25af331ad5..17574aa5066f 100644 --- a/rust/kernel/auxiliary.rs +++ b/rust/kernel/auxiliary.rs @@ -25,10 +25,12 @@ pub struct Adapter(T); // SAFETY: // - `bindings::auxiliary_driver` is a C type declared as `repr(C)`. +// - `T` is the type of the driver's device private data. // - `struct auxiliary_driver` embeds a `struct device_driver`. // - `DEVICE_DRIVER_OFFSET` is the correct byte offset to the embedded `struct device_driver`. unsafe impl driver::DriverLayout for Adapter { type DriverType = bindings::auxiliary_driver; + type DriverData = T; const DEVICE_DRIVER_OFFSET: usize = core::mem::offset_of!(Self::DriverType, driver); } diff --git a/rust/kernel/driver.rs b/rust/kernel/driver.rs index 4a96a07905d1..ba1ca1f7a7e2 100644 --- a/rust/kernel/driver.rs +++ b/rust/kernel/driver.rs @@ -108,11 +108,15 @@ use pin_init::{pin_data, pinned_drop, PinInit}; /// /// Implementors must guarantee that: /// - `DriverType` is `repr(C)`, +/// - `DriverData` is the type of the driver's device private data. /// - `DriverType` embeds a valid `struct device_driver` at byte offset `DEVICE_DRIVER_OFFSET`. pub unsafe trait DriverLayout { /// The specific driver type embedding a `struct device_driver`. type DriverType: Default; + /// The type of the driver's device private data. + type DriverData; + /// Byte offset of the embedded `struct device_driver` within `DriverType`. /// /// This must correspond exactly to the location of the embedded `struct device_driver` field. diff --git a/rust/kernel/i2c.rs b/rust/kernel/i2c.rs index d97e73282003..e86242227081 100644 --- a/rust/kernel/i2c.rs +++ b/rust/kernel/i2c.rs @@ -94,10 +94,12 @@ pub struct Adapter(T); // SAFETY: // - `bindings::i2c_driver` is a C type declared as `repr(C)`. +// - `T` is the type of the driver's device private data. // - `struct i2c_driver` embeds a `struct device_driver`. // - `DEVICE_DRIVER_OFFSET` is the correct byte offset to the embedded `struct device_driver`. unsafe impl driver::DriverLayout for Adapter { type DriverType = bindings::i2c_driver; + type DriverData = T; const DEVICE_DRIVER_OFFSET: usize = core::mem::offset_of!(Self::DriverType, driver); } diff --git a/rust/kernel/pci.rs b/rust/kernel/pci.rs index fe6f508b0cac..590723dcb5ae 100644 --- a/rust/kernel/pci.rs +++ b/rust/kernel/pci.rs @@ -52,10 +52,12 @@ pub struct Adapter(T); // SAFETY: // - `bindings::pci_driver` is a C type declared as `repr(C)`. +// - `T` is the type of the driver's device private data. // - `struct pci_driver` embeds a `struct device_driver`. // - `DEVICE_DRIVER_OFFSET` is the correct byte offset to the embedded `struct device_driver`. unsafe impl driver::DriverLayout for Adapter { type DriverType = bindings::pci_driver; + type DriverData = T; const DEVICE_DRIVER_OFFSET: usize = core::mem::offset_of!(Self::DriverType, driver); } diff --git a/rust/kernel/platform.rs b/rust/kernel/platform.rs index 716c9cc25aea..b8a681df9ddc 100644 --- a/rust/kernel/platform.rs +++ b/rust/kernel/platform.rs @@ -28,10 +28,12 @@ pub struct Adapter(T); // SAFETY: // - `bindings::platform_driver` is a C type declared as `repr(C)`. +// - `T` is the type of the driver's device private data. // - `struct platform_driver` embeds a `struct device_driver`. // - `DEVICE_DRIVER_OFFSET` is the correct byte offset to the embedded `struct device_driver`. unsafe impl driver::DriverLayout for Adapter { type DriverType = bindings::platform_driver; + type DriverData = T; const DEVICE_DRIVER_OFFSET: usize = core::mem::offset_of!(Self::DriverType, driver); } diff --git a/rust/kernel/usb.rs b/rust/kernel/usb.rs index eb1c9b9ef228..4cf4bb1705b5 100644 --- a/rust/kernel/usb.rs +++ b/rust/kernel/usb.rs @@ -29,10 +29,12 @@ pub struct Adapter(T); // SAFETY: // - `bindings::usb_driver` is a C type declared as `repr(C)`. +// - `T` is the type of the driver's device private data. // - `struct usb_driver` embeds a `struct device_driver`. // - `DEVICE_DRIVER_OFFSET` is the correct byte offset to the embedded `struct device_driver`. unsafe impl driver::DriverLayout for Adapter { type DriverType = bindings::usb_driver; + type DriverData = T; const DEVICE_DRIVER_OFFSET: usize = core::mem::offset_of!(Self::DriverType, driver); } -- cgit v1.2.3 From a995fe1a3aa78b7d06cc1cc7b6b8436c5e93b07f Mon Sep 17 00:00:00 2001 From: Danilo Krummrich Date: Wed, 7 Jan 2026 11:35:05 +0100 Subject: rust: driver: drop device private data post unbind Currently, the driver's device private data is allocated and initialized from driver core code called from bus abstractions after the driver's probe() callback returned the corresponding initializer. Similarly, the driver's device private data is dropped within the remove() callback of bus abstractions after calling the remove() callback of the corresponding driver. However, commit 6f61a2637abe ("rust: device: introduce Device::drvdata()") introduced an accessor for the driver's device private data for a Device, i.e. a device that is currently bound to a driver. Obviously, this is in conflict with dropping the driver's device private data in remove(), since a device can not be considered to be fully unbound after remove() has finished: We also have to consider registrations guarded by devres - such as IRQ or class device registrations - which are torn down after remove() in devres_release_all(). Thus, it can happen that, for instance, a class device or IRQ callback still calls Device::drvdata(), which then runs concurrently to remove() (which sets dev->driver_data to NULL and drops the driver's device private data), before devres_release_all() started to tear down the corresponding registration. This is because devres guarded registrations can, as expected, access the corresponding Device that defines their scope. In C it simply is the driver's responsibility to ensure that its device private data is freed after e.g. an IRQ registration is unregistered. Typically, C drivers achieve this by allocating their device private data with e.g. devm_kzalloc() before doing anything else, i.e. before e.g. registering an IRQ with devm_request_threaded_irq(), relying on the reverse order cleanup of devres. Technically, we could do something similar in Rust. However, the resulting code would be pretty messy: In Rust we have to differentiate between allocated but uninitialized memory and initialized memory in the type system. Thus, we would need to somehow keep track of whether the driver's device private data object has been initialized (i.e. probe() was successful and returned a valid initializer for this memory) and conditionally call the destructor of the corresponding object when it is freed. This is because we'd need to allocate and register the memory of the driver's device private data *before* it is initialized by the initializer returned by the driver's probe() callback, because the driver could already register devres guarded registrations within probe() outside of the driver's device private data initializer. Luckily there is a much simpler solution: Instead of dropping the driver's device private data at the end of remove(), we just drop it after the device has been fully unbound, i.e. after all devres callbacks have been processed. For this, we introduce a new post_unbind() callback private to the driver-core, i.e. the callback is neither exposed to drivers, nor to bus abstractions. This way, the driver-core code can simply continue to conditionally allocate the memory for the driver's device private data when the driver's initializer is returned from probe() - no change needed - and drop it when the driver-core code receives the post_unbind() callback. Closes: https://lore.kernel.org/all/DEZMS6Y4A7XE.XE7EUBT5SJFJ@kernel.org/ Fixes: 6f61a2637abe ("rust: device: introduce Device::drvdata()") Acked-by: Alice Ryhl Acked-by: Greg Kroah-Hartman Acked-by: Igor Korotin Link: https://patch.msgid.link/20260107103511.570525-7-dakr@kernel.org [ Remove #ifdef CONFIG_RUST, rename post_unbind() to post_unbind_rust(). - Danilo] Signed-off-by: Danilo Krummrich --- rust/kernel/auxiliary.rs | 4 ++-- rust/kernel/device.rs | 20 +++++++++++--------- rust/kernel/driver.rs | 36 +++++++++++++++++++++++++++++++++++- rust/kernel/i2c.rs | 4 ++-- rust/kernel/pci.rs | 4 ++-- rust/kernel/platform.rs | 4 ++-- rust/kernel/usb.rs | 4 ++-- 7 files changed, 56 insertions(+), 20 deletions(-) (limited to 'rust/kernel') diff --git a/rust/kernel/auxiliary.rs b/rust/kernel/auxiliary.rs index 17574aa5066f..be76f11aecb7 100644 --- a/rust/kernel/auxiliary.rs +++ b/rust/kernel/auxiliary.rs @@ -96,9 +96,9 @@ impl Adapter { // SAFETY: `remove_callback` is only ever called after a successful call to // `probe_callback`, hence it's guaranteed that `Device::set_drvdata()` has been called // and stored a `Pin>`. - let data = unsafe { adev.as_ref().drvdata_obtain::() }; + let data = unsafe { adev.as_ref().drvdata_borrow::() }; - T::unbind(adev, data.as_ref()); + T::unbind(adev, data); } } diff --git a/rust/kernel/device.rs b/rust/kernel/device.rs index 71b200df0f40..031720bf5d8c 100644 --- a/rust/kernel/device.rs +++ b/rust/kernel/device.rs @@ -232,30 +232,32 @@ impl Device { /// /// # Safety /// - /// - Must only be called once after a preceding call to [`Device::set_drvdata`]. /// - The type `T` must match the type of the `ForeignOwnable` previously stored by /// [`Device::set_drvdata`]. - pub unsafe fn drvdata_obtain(&self) -> Pin> { + pub(crate) unsafe fn drvdata_obtain(&self) -> Option>> { // SAFETY: By the type invariants, `self.as_raw()` is a valid pointer to a `struct device`. let ptr = unsafe { bindings::dev_get_drvdata(self.as_raw()) }; // SAFETY: By the type invariants, `self.as_raw()` is a valid pointer to a `struct device`. unsafe { bindings::dev_set_drvdata(self.as_raw(), core::ptr::null_mut()) }; + if ptr.is_null() { + return None; + } + // SAFETY: - // - By the safety requirements of this function, `ptr` comes from a previous call to - // `into_foreign()`. + // - If `ptr` is not NULL, it comes from a previous call to `into_foreign()`. // - `dev_get_drvdata()` guarantees to return the same pointer given to `dev_set_drvdata()` // in `into_foreign()`. - unsafe { Pin::>::from_foreign(ptr.cast()) } + Some(unsafe { Pin::>::from_foreign(ptr.cast()) }) } /// Borrow the driver's private data bound to this [`Device`]. /// /// # Safety /// - /// - Must only be called after a preceding call to [`Device::set_drvdata`] and before - /// [`Device::drvdata_obtain`]. + /// - Must only be called after a preceding call to [`Device::set_drvdata`] and before the + /// device is fully unbound. /// - The type `T` must match the type of the `ForeignOwnable` previously stored by /// [`Device::set_drvdata`]. pub unsafe fn drvdata_borrow(&self) -> Pin<&T> { @@ -271,7 +273,7 @@ impl Device { /// # Safety /// /// - Must only be called after a preceding call to [`Device::set_drvdata`] and before - /// [`Device::drvdata_obtain`]. + /// the device is fully unbound. /// - The type `T` must match the type of the `ForeignOwnable` previously stored by /// [`Device::set_drvdata`]. unsafe fn drvdata_unchecked(&self) -> Pin<&T> { @@ -320,7 +322,7 @@ impl Device { // SAFETY: // - The above check of `dev_get_drvdata()` guarantees that we are called after - // `set_drvdata()` and before `drvdata_obtain()`. + // `set_drvdata()`. // - We've just checked that the type of the driver's private data is in fact `T`. Ok(unsafe { self.drvdata_unchecked() }) } diff --git a/rust/kernel/driver.rs b/rust/kernel/driver.rs index ba1ca1f7a7e2..bee3ae21a27b 100644 --- a/rust/kernel/driver.rs +++ b/rust/kernel/driver.rs @@ -177,7 +177,39 @@ unsafe impl Sync for Registration {} // any thread, so `Registration` is `Send`. unsafe impl Send for Registration {} -impl Registration { +impl Registration { + extern "C" fn post_unbind_callback(dev: *mut bindings::device) { + // SAFETY: The driver core only ever calls the post unbind callback with a valid pointer to + // a `struct device`. + // + // INVARIANT: `dev` is valid for the duration of the `post_unbind_callback()`. + let dev = unsafe { &*dev.cast::>() }; + + // `remove()` and all devres callbacks have been completed at this point, hence drop the + // driver's device private data. + // + // SAFETY: By the safety requirements of the `Driver` trait, `T::DriverData` is the + // driver's device private data type. + drop(unsafe { dev.drvdata_obtain::() }); + } + + /// Attach generic `struct device_driver` callbacks. + fn callbacks_attach(drv: &Opaque) { + let ptr = drv.get().cast::(); + + // SAFETY: + // - `drv.get()` yields a valid pointer to `Self::DriverType`. + // - Adding `DEVICE_DRIVER_OFFSET` yields the address of the embedded `struct device_driver` + // as guaranteed by the safety requirements of the `Driver` trait. + let base = unsafe { ptr.add(T::DEVICE_DRIVER_OFFSET) }; + + // CAST: `base` points to the offset of the embedded `struct device_driver`. + let base = base.cast::(); + + // SAFETY: It is safe to set the fields of `struct device_driver` on initialization. + unsafe { (*base).p_cb.post_unbind_rust = Some(Self::post_unbind_callback) }; + } + /// Creates a new instance of the registration object. pub fn new(name: &'static CStr, module: &'static ThisModule) -> impl PinInit { try_pin_init!(Self { @@ -189,6 +221,8 @@ impl Registration { // just been initialised above, so it's also valid for read. let drv = unsafe { &*(ptr as *const Opaque) }; + Self::callbacks_attach(drv); + // SAFETY: `drv` is guaranteed to be pinned until `T::unregister`. unsafe { T::register(drv, name, module) } }), diff --git a/rust/kernel/i2c.rs b/rust/kernel/i2c.rs index e86242227081..39b0a9a207fd 100644 --- a/rust/kernel/i2c.rs +++ b/rust/kernel/i2c.rs @@ -178,9 +178,9 @@ impl Adapter { // SAFETY: `remove_callback` is only ever called after a successful call to // `probe_callback`, hence it's guaranteed that `I2cClient::set_drvdata()` has been called // and stored a `Pin>`. - let data = unsafe { idev.as_ref().drvdata_obtain::() }; + let data = unsafe { idev.as_ref().drvdata_borrow::() }; - T::unbind(idev, data.as_ref()); + T::unbind(idev, data); } extern "C" fn shutdown_callback(idev: *mut bindings::i2c_client) { diff --git a/rust/kernel/pci.rs b/rust/kernel/pci.rs index 590723dcb5ae..bea76ca9c3da 100644 --- a/rust/kernel/pci.rs +++ b/rust/kernel/pci.rs @@ -123,9 +123,9 @@ impl Adapter { // SAFETY: `remove_callback` is only ever called after a successful call to // `probe_callback`, hence it's guaranteed that `Device::set_drvdata()` has been called // and stored a `Pin>`. - let data = unsafe { pdev.as_ref().drvdata_obtain::() }; + let data = unsafe { pdev.as_ref().drvdata_borrow::() }; - T::unbind(pdev, data.as_ref()); + T::unbind(pdev, data); } } diff --git a/rust/kernel/platform.rs b/rust/kernel/platform.rs index b8a681df9ddc..35a5813ffb33 100644 --- a/rust/kernel/platform.rs +++ b/rust/kernel/platform.rs @@ -101,9 +101,9 @@ impl Adapter { // SAFETY: `remove_callback` is only ever called after a successful call to // `probe_callback`, hence it's guaranteed that `Device::set_drvdata()` has been called // and stored a `Pin>`. - let data = unsafe { pdev.as_ref().drvdata_obtain::() }; + let data = unsafe { pdev.as_ref().drvdata_borrow::() }; - T::unbind(pdev, data.as_ref()); + T::unbind(pdev, data); } } diff --git a/rust/kernel/usb.rs b/rust/kernel/usb.rs index 4cf4bb1705b5..67ce5c85c619 100644 --- a/rust/kernel/usb.rs +++ b/rust/kernel/usb.rs @@ -103,9 +103,9 @@ impl Adapter { // SAFETY: `disconnect_callback` is only ever called after a successful call to // `probe_callback`, hence it's guaranteed that `Device::set_drvdata()` has been called // and stored a `Pin>`. - let data = unsafe { dev.drvdata_obtain::() }; + let data = unsafe { dev.drvdata_borrow::() }; - T::disconnect(intf, data.as_ref()); + T::disconnect(intf, data); } } -- cgit v1.2.3 From 68aabb29a5469e4b7358e70e64a7fac433e27f06 Mon Sep 17 00:00:00 2001 From: Alice Ryhl Date: Mon, 5 Jan 2026 14:25:03 +0000 Subject: rust: redefine `bindings::compat_ptr_ioctl` in Rust There is currently an inconsistency between C and Rust, which is that when Rust requires cfg(CONFIG_COMPAT) on compat_ioctl when using the compat_ptr_ioctl symbol because '#define compat_ptr_ioctl NULL' does not get translated to anything by bindgen. But it's not *just* a matter of translating the '#define' into Rust when CONFIG_COMPAT=n. This is because when CONFIG_COMPAT=y, the type of compat_ptr_ioctl is a non-nullable function pointer, and to seamlessly use it regardless of the config, we need a nullable function pointer. I think it's important to do something about this; I've seen the mistake of accidentally forgetting '#[cfg(CONFIG_COMPAT)]' when compat_ptr_ioctl is used multiple times now. This explicitly declares 'bindings::compat_ptr_ioctl' as an Option that is always defined but might be None. This matches C, but isn't ideal: it modifies the bindings crate. But I'm not sure if there's a better way to do it. If we just redefine in kernel/, then people may still use the one in bindings::, since that is where you would normally find it. I am open to suggestions. Signed-off-by: Alice Ryhl Reviewed-by: Gary Guo Link: https://patch.msgid.link/20260105-redefine-compat_ptr_ioctl-v1-1-25edb3d91acc@google.com Signed-off-by: Greg Kroah-Hartman --- rust/kernel/miscdevice.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'rust/kernel') diff --git a/rust/kernel/miscdevice.rs b/rust/kernel/miscdevice.rs index ba64c8a858f0..c3c2052c9206 100644 --- a/rust/kernel/miscdevice.rs +++ b/rust/kernel/miscdevice.rs @@ -410,7 +410,7 @@ impl MiscdeviceVTable { compat_ioctl: if T::HAS_COMPAT_IOCTL { Some(Self::compat_ioctl) } else if T::HAS_IOCTL { - Some(bindings::compat_ptr_ioctl) + bindings::compat_ptr_ioctl } else { None }, -- cgit v1.2.3 From 61d62ab08f0e62f89929f3920c0b6521d849fd57 Mon Sep 17 00:00:00 2001 From: Benno Lossin Date: Fri, 16 Jan 2026 11:54:16 +0100 Subject: rust: pin-init: remove `try_` versions of the initializer macros The `try_[pin_]init!` versions of the initializer macros are superfluous. Instead of forcing the user to always write an error in `try_[pin_]init!` and not allowing one in `[pin_]init!`, combine them into `[pin_]init!` that defaults the error to `core::convert::Infallible`, but also allows to specify a custom one. Projects using pin-init still can provide their own defaulting initializers using the `try_` prefix by using the `#[default_error]` attribute added in a future patch. [ Adjust the definition of the kernel's version of the `try_` initializer macros - Benno] Reviewed-by: Gary Guo Tested-by: Andreas Hindborg Signed-off-by: Benno Lossin --- rust/kernel/init.rs | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) (limited to 'rust/kernel') diff --git a/rust/kernel/init.rs b/rust/kernel/init.rs index 899b9a962762..917f7ef001fd 100644 --- a/rust/kernel/init.rs +++ b/rust/kernel/init.rs @@ -222,14 +222,14 @@ macro_rules! try_init { ($(&$this:ident in)? $t:ident $(::<$($generics:ty),* $(,)?>)? { $($fields:tt)* }) => { - ::pin_init::try_init!($(&$this in)? $t $(::<$($generics),*>)? { + ::pin_init::init!($(&$this in)? $t $(::<$($generics),*>)? { $($fields)* }? $crate::error::Error) }; ($(&$this:ident in)? $t:ident $(::<$($generics:ty),* $(,)?>)? { $($fields:tt)* }? $err:ty) => { - ::pin_init::try_init!($(&$this in)? $t $(::<$($generics),*>)? { + ::pin_init::init!($(&$this in)? $t $(::<$($generics),*>)? { $($fields)* }? $err) }; @@ -282,14 +282,14 @@ macro_rules! try_pin_init { ($(&$this:ident in)? $t:ident $(::<$($generics:ty),* $(,)?>)? { $($fields:tt)* }) => { - ::pin_init::try_pin_init!($(&$this in)? $t $(::<$($generics),*>)? { + ::pin_init::pin_init!($(&$this in)? $t $(::<$($generics),*>)? { $($fields)* }? $crate::error::Error) }; ($(&$this:ident in)? $t:ident $(::<$($generics:ty),* $(,)?>)? { $($fields:tt)* }? $err:ty) => { - ::pin_init::try_pin_init!($(&$this in)? $t $(::<$($generics),*>)? { + ::pin_init::pin_init!($(&$this in)? $t $(::<$($generics),*>)? { $($fields)* }? $err) }; -- cgit v1.2.3 From d083a6214ca6d486ac58f84f8964c72028469342 Mon Sep 17 00:00:00 2001 From: Benno Lossin Date: Fri, 16 Jan 2026 11:54:26 +0100 Subject: rust: init: use `#[default_error(err)]` for the initializer macros Initializer macros should use this attribute instead of manually parsing the macro's input. This is because the syntax is now parsed using `syn`, which permits more complex constructs to be parsed. In addition, this ensures that the kernel's initializer marcos will have the exact same syntax as the ones from pin-init. Reviewed-by: Gary Guo Signed-off-by: Benno Lossin --- rust/kernel/init.rs | 40 ++++++++++++---------------------------- 1 file changed, 12 insertions(+), 28 deletions(-) (limited to 'rust/kernel') diff --git a/rust/kernel/init.rs b/rust/kernel/init.rs index 917f7ef001fd..7a0d4559d7b5 100644 --- a/rust/kernel/init.rs +++ b/rust/kernel/init.rs @@ -219,20 +219,12 @@ pub trait InPlaceInit: Sized { /// [`Error`]: crate::error::Error #[macro_export] macro_rules! try_init { - ($(&$this:ident in)? $t:ident $(::<$($generics:ty),* $(,)?>)? { - $($fields:tt)* - }) => { - ::pin_init::init!($(&$this in)? $t $(::<$($generics),*>)? { - $($fields)* - }? $crate::error::Error) - }; - ($(&$this:ident in)? $t:ident $(::<$($generics:ty),* $(,)?>)? { - $($fields:tt)* - }? $err:ty) => { - ::pin_init::init!($(&$this in)? $t $(::<$($generics),*>)? { - $($fields)* - }? $err) - }; + ($($args:tt)*) => { + ::pin_init::init!( + #[default_error($crate::error::Error)] + $($args)* + ) + } } /// Construct an in-place, fallible pinned initializer for `struct`s. @@ -279,18 +271,10 @@ macro_rules! try_init { /// [`Error`]: crate::error::Error #[macro_export] macro_rules! try_pin_init { - ($(&$this:ident in)? $t:ident $(::<$($generics:ty),* $(,)?>)? { - $($fields:tt)* - }) => { - ::pin_init::pin_init!($(&$this in)? $t $(::<$($generics),*>)? { - $($fields)* - }? $crate::error::Error) - }; - ($(&$this:ident in)? $t:ident $(::<$($generics:ty),* $(,)?>)? { - $($fields:tt)* - }? $err:ty) => { - ::pin_init::pin_init!($(&$this in)? $t $(::<$($generics),*>)? { - $($fields)* - }? $err) - }; + ($($args:tt)*) => { + ::pin_init::pin_init!( + #[default_error($crate::error::Error)] + $($args)* + ) + } } -- cgit v1.2.3 From 09c3c9112d71c44146419c87c55c710e68335741 Mon Sep 17 00:00:00 2001 From: Alexandre Courbot Date: Mon, 8 Dec 2025 11:47:02 +0900 Subject: rust: bits: always inline functions using build_assert with arguments `build_assert` relies on the compiler to optimize out its error path. Functions using it with its arguments must thus always be inlined, otherwise the error path of `build_assert` might not be optimized out, triggering a build error. Cc: stable@vger.kernel.org Fixes: cc84ef3b88f4 ("rust: bits: add support for bits/genmask macros") Reviewed-by: Daniel Almeida Signed-off-by: Alexandre Courbot Link: https://patch.msgid.link/20251208-io-build-assert-v3-4-98aded02c1ea@nvidia.com Signed-off-by: Miguel Ojeda --- rust/kernel/bits.rs | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) (limited to 'rust/kernel') diff --git a/rust/kernel/bits.rs b/rust/kernel/bits.rs index 553d50265883..2daead125626 100644 --- a/rust/kernel/bits.rs +++ b/rust/kernel/bits.rs @@ -27,7 +27,8 @@ macro_rules! impl_bit_fn { /// /// This version is the default and should be used if `n` is known at /// compile time. - #[inline] + // Always inline to optimize out error path of `build_assert`. + #[inline(always)] pub const fn [](n: u32) -> $ty { build_assert!(n < <$ty>::BITS); (1 as $ty) << n @@ -75,7 +76,8 @@ macro_rules! impl_genmask_fn { /// This version is the default and should be used if the range is known /// at compile time. $(#[$genmask_ex])* - #[inline] + // Always inline to optimize out error path of `build_assert`. + #[inline(always)] pub const fn [](range: RangeInclusive) -> $ty { let start = *range.start(); let end = *range.end(); -- cgit v1.2.3 From d6ff6e870077ae0f01a6f860ca1e4a5a825dc032 Mon Sep 17 00:00:00 2001 From: Alexandre Courbot Date: Mon, 8 Dec 2025 11:47:03 +0900 Subject: rust: sync: refcount: always inline functions using build_assert with arguments `build_assert` relies on the compiler to optimize out its error path. Functions using it with its arguments must thus always be inlined, otherwise the error path of `build_assert` might not be optimized out, triggering a build error. Cc: stable@vger.kernel.org Fixes: bb38f35b35f9 ("rust: implement `kernel::sync::Refcount`") Reviewed-by: Daniel Almeida Signed-off-by: Alexandre Courbot Acked-by: Boqun Feng Link: https://patch.msgid.link/20251208-io-build-assert-v3-5-98aded02c1ea@nvidia.com Signed-off-by: Miguel Ojeda --- rust/kernel/sync/refcount.rs | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) (limited to 'rust/kernel') diff --git a/rust/kernel/sync/refcount.rs b/rust/kernel/sync/refcount.rs index 19236a5bccde..6c7ae8b05a0b 100644 --- a/rust/kernel/sync/refcount.rs +++ b/rust/kernel/sync/refcount.rs @@ -23,7 +23,8 @@ impl Refcount { /// Construct a new [`Refcount`] from an initial value. /// /// The initial value should be non-saturated. - #[inline] + // Always inline to optimize out error path of `build_assert`. + #[inline(always)] pub fn new(value: i32) -> Self { build_assert!(value >= 0, "initial value saturated"); // SAFETY: There are no safety requirements for this FFI call. -- cgit v1.2.3 From 2af6ad09fc7dfe9b3610100983cccf16998bf34d Mon Sep 17 00:00:00 2001 From: Alexandre Courbot Date: Mon, 8 Dec 2025 11:47:05 +0900 Subject: rust: num: bounded: add missing comment for always inlined function This code is always inlined to avoid a build error if the error path of `build_assert` cannot be optimized out. Add a comment justifying the `#[inline(always)]` property to avoid it being taken away by mistake. Reviewed-by: Daniel Almeida Signed-off-by: Alexandre Courbot Link: https://patch.msgid.link/20251208-io-build-assert-v3-7-98aded02c1ea@nvidia.com Signed-off-by: Miguel Ojeda --- rust/kernel/num/bounded.rs | 1 + 1 file changed, 1 insertion(+) (limited to 'rust/kernel') diff --git a/rust/kernel/num/bounded.rs b/rust/kernel/num/bounded.rs index c9a44e12c19b..5ef8361cf5d5 100644 --- a/rust/kernel/num/bounded.rs +++ b/rust/kernel/num/bounded.rs @@ -367,6 +367,7 @@ where /// assert_eq!(Bounded::::from_expr(1).get(), 1); /// assert_eq!(Bounded::::from_expr(0xff).get(), 0xff); /// ``` + // Always inline to optimize out error path of `build_assert`. #[inline(always)] pub fn from_expr(expr: T) -> Self { crate::build_assert!( -- cgit v1.2.3 From 1b18b37a2c30f6e6698205a06de393f7e626f5d2 Mon Sep 17 00:00:00 2001 From: Alexandre Courbot Date: Mon, 8 Dec 2025 11:46:59 +0900 Subject: rust: build_assert: add instructions for use with function arguments `build_assert` relies on the compiler to optimize out its error path, lest build fails with the dreaded error: ERROR: modpost: "rust_build_error" [path/to/module.ko] undefined! It has been observed that very trivial code performing I/O accesses (sometimes even using an immediate value) would seemingly randomly fail with this error whenever `CLIPPY=1` was set. The same behavior was also observed until different, very similar conditions [1][2]. The cause appears to be that the failing function is eventually using `build_assert` with its argument, but is only annotated with `#[inline]`. This gives the compiler freedom to not inline the function, which it notably did when Clippy was active, triggering the error. The fix is to annotate functions passing their argument to `build_assert` with `#[inline(always)]`, telling the compiler to be as aggressive as possible with their inlining. This is also the correct behavior as inlining is mandatory for correct behavior in these cases. Add a paragraph instructing to annotate such functions with `#[inline(always)]` in `build_assert`'s documentation, and split its example to illustrate. Reviewed-by: Daniel Almeida Signed-off-by: Alexandre Courbot Link: https://patch.msgid.link/20251208-io-build-assert-v3-1-98aded02c1ea@nvidia.com Signed-off-by: Miguel Ojeda --- rust/kernel/build_assert.rs | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) (limited to 'rust/kernel') diff --git a/rust/kernel/build_assert.rs b/rust/kernel/build_assert.rs index 6331b15d7c4d..f8124dbc663f 100644 --- a/rust/kernel/build_assert.rs +++ b/rust/kernel/build_assert.rs @@ -61,8 +61,13 @@ macro_rules! build_error { /// build_assert!(N > 1); // Build-time check /// assert!(N > 1); // Run-time check /// } +/// ``` /// -/// #[inline] +/// When a condition depends on a function argument, the function must be annotated with +/// `#[inline(always)]`. Without this attribute, the compiler may choose to not inline the +/// function, preventing it from optimizing out the error path. +/// ``` +/// #[inline(always)] /// fn bar(n: usize) { /// // `static_assert!(n > 1);` is not allowed /// build_assert!(n > 1); // Build-time check -- cgit v1.2.3 From 84b1b49eccb79ec2e4aaa45116fffb2ac61b876c Mon Sep 17 00:00:00 2001 From: Alexandre Courbot Date: Tue, 16 Dec 2025 17:24:49 +0900 Subject: rust: ptr: replace unneeded use of `build_assert` Since `ALIGN` is a const parameter, this assertion can be done in const context using the `assert!` macro. Suggested-by: Alice Ryhl Signed-off-by: Alexandre Courbot Reviewed-by: Alice Ryhl Link: https://patch.msgid.link/20251216-ptr_assert-v1-1-d8b2d5c5741d@nvidia.com Signed-off-by: Miguel Ojeda --- rust/kernel/ptr.rs | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) (limited to 'rust/kernel') diff --git a/rust/kernel/ptr.rs b/rust/kernel/ptr.rs index e3893ed04049..5b6a382637fe 100644 --- a/rust/kernel/ptr.rs +++ b/rust/kernel/ptr.rs @@ -5,8 +5,6 @@ use core::mem::align_of; use core::num::NonZero; -use crate::build_assert; - /// Type representing an alignment, which is always a power of two. /// /// It is used to validate that a given value is a valid alignment, and to perform masking and @@ -40,10 +38,12 @@ impl Alignment { /// ``` #[inline(always)] pub const fn new() -> Self { - build_assert!( - ALIGN.is_power_of_two(), - "Provided alignment is not a power of two." - ); + const { + assert!( + ALIGN.is_power_of_two(), + "Provided alignment is not a power of two." + ); + } // INVARIANT: `align` is a power of two. // SAFETY: `align` is a power of two, and thus non-zero. -- cgit v1.2.3 From eeaad2f021def7efdaa441b104550615ca328a48 Mon Sep 17 00:00:00 2001 From: Tamir Duberstein Date: Mon, 22 Dec 2025 13:14:32 +0100 Subject: rust: i2c: replace `kernel::c_str!` with C-Strings C-String literals were added in Rust 1.77. Replace instances of `kernel::c_str!` with C-String literals where possible. Signed-off-by: Tamir Duberstein Acked-by: Igor Korotin Reviewed-by: Daniel Almeida Link: https://patch.msgid.link/20251222-cstr-i2c-v1-1-df1c258d4615@gmail.com Signed-off-by: Miguel Ojeda --- rust/kernel/i2c.rs | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) (limited to 'rust/kernel') diff --git a/rust/kernel/i2c.rs b/rust/kernel/i2c.rs index 491e6cc25cf4..792a71b15463 100644 --- a/rust/kernel/i2c.rs +++ b/rust/kernel/i2c.rs @@ -253,7 +253,7 @@ macro_rules! module_i2c_driver { /// # Example /// ///``` -/// # use kernel::{acpi, bindings, c_str, device::Core, i2c, of}; +/// # use kernel::{acpi, bindings, device::Core, i2c, of}; /// /// struct MyDriver; /// @@ -262,7 +262,7 @@ macro_rules! module_i2c_driver { /// MODULE_ACPI_TABLE, /// ::IdInfo, /// [ -/// (acpi::DeviceId::new(c_str!("LNUXBEEF")), ()) +/// (acpi::DeviceId::new(c"LNUXBEEF"), ()) /// ] /// ); /// @@ -271,7 +271,7 @@ macro_rules! module_i2c_driver { /// MODULE_I2C_TABLE, /// ::IdInfo, /// [ -/// (i2c::DeviceId::new(c_str!("rust_driver_i2c")), ()) +/// (i2c::DeviceId::new(c"rust_driver_i2c"), ()) /// ] /// ); /// @@ -280,7 +280,7 @@ macro_rules! module_i2c_driver { /// MODULE_OF_TABLE, /// ::IdInfo, /// [ -/// (of::DeviceId::new(c_str!("test,device")), ()) +/// (of::DeviceId::new(c"test,device"), ()) /// ] /// ); /// -- cgit v1.2.3 From 6c37b6841a92714eba4a7b7f823aea801da4e09f Mon Sep 17 00:00:00 2001 From: Tamir Duberstein Date: Mon, 22 Dec 2025 13:28:27 +0100 Subject: rust: kunit: replace `kernel::c_str!` with C-Strings C-String literals were added in Rust 1.77. Replace instances of `kernel::c_str!` with C-String literals where possible. Acked-by: Greg Kroah-Hartman Reviewed-by: Alice Ryhl Reviewed-by: Benno Lossin Signed-off-by: Tamir Duberstein Reviewed-by: David Gow Link: https://patch.msgid.link/20251222-cstr-kunit-v1-1-39d999672f35@gmail.com Signed-off-by: Miguel Ojeda --- rust/kernel/kunit.rs | 11 ++++------- 1 file changed, 4 insertions(+), 7 deletions(-) (limited to 'rust/kernel') diff --git a/rust/kernel/kunit.rs b/rust/kernel/kunit.rs index 79436509dd73..21aef6c97325 100644 --- a/rust/kernel/kunit.rs +++ b/rust/kernel/kunit.rs @@ -9,9 +9,6 @@ use crate::fmt; use crate::prelude::*; -#[cfg(CONFIG_PRINTK)] -use crate::c_str; - /// Prints a KUnit error-level message. /// /// Public but hidden since it should only be used from KUnit generated code. @@ -22,7 +19,7 @@ pub fn err(args: fmt::Arguments<'_>) { #[cfg(CONFIG_PRINTK)] unsafe { bindings::_printk( - c_str!("\x013%pA").as_char_ptr(), + c"\x013%pA".as_char_ptr(), core::ptr::from_ref(&args).cast::(), ); } @@ -38,7 +35,7 @@ pub fn info(args: fmt::Arguments<'_>) { #[cfg(CONFIG_PRINTK)] unsafe { bindings::_printk( - c_str!("\x016%pA").as_char_ptr(), + c"\x016%pA".as_char_ptr(), core::ptr::from_ref(&args).cast::(), ); } @@ -60,7 +57,7 @@ macro_rules! kunit_assert { break 'out; } - static FILE: &'static $crate::str::CStr = $crate::c_str!($file); + static FILE: &'static $crate::str::CStr = $file; static LINE: i32 = ::core::line!() as i32 - $diff; static CONDITION: &'static $crate::str::CStr = $crate::c_str!(stringify!($condition)); @@ -253,7 +250,7 @@ pub const fn kunit_case_null() -> kernel::bindings::kunit_case { /// } /// /// static mut KUNIT_TEST_CASES: [kernel::bindings::kunit_case; 2] = [ -/// kernel::kunit::kunit_case(kernel::c_str!("name"), test_fn), +/// kernel::kunit::kunit_case(c"name", test_fn), /// kernel::kunit::kunit_case_null(), /// ]; /// kernel::kunit_unsafe_test_suite!(suite_name, KUNIT_TEST_CASES); -- cgit v1.2.3 From 2ad6c5cdc89acfefb01b84afa5e55262c40d6fec Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Onur=20=C3=96zkan?= Date: Thu, 13 Nov 2025 17:45:47 +0300 Subject: rust: rbtree: reduce unsafe blocks on pointer derefs MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Refactors parts of the get() and find_best_match() traversal logic to minimize the scope of unsafe blocks and avoid duplicating same safety comments. One of the removed comments was also misleading: // SAFETY: `node` is a non-null node... Ordering::Equal => return Some(unsafe { &(*this).value }), as `node` should have been `this`. No functional changes intended; this is purely a safety improvement that reduces the amount of unsafe blocks while keeping all invariants intact. [ Alice writes: "One consequence of creating a &_ to the bindings::rb_node struct means that we assert immutability for the entire struct and not just the rb_left/rb_right fields, but I have verified that this is ok." - Miguel ] Signed-off-by: Onur Özkan Reviewed-by: Charalampos Mitrodimas Reviewed-by: Alice Ryhl Link: https://patch.msgid.link/20251113144547.502-1-work@onurozkan.dev [ Reworded title and replaced `cursor_lower_bound()` with `find_best_match()` in message. - Miguel ] Signed-off-by: Miguel Ojeda --- rust/kernel/rbtree.rs | 27 +++++++++++++++------------ 1 file changed, 15 insertions(+), 12 deletions(-) (limited to 'rust/kernel') diff --git a/rust/kernel/rbtree.rs b/rust/kernel/rbtree.rs index 4729eb56827a..ed3582d88e4e 100644 --- a/rust/kernel/rbtree.rs +++ b/rust/kernel/rbtree.rs @@ -414,14 +414,17 @@ where // SAFETY: By the type invariant of `Self`, all non-null `rb_node` pointers stored in `self` // point to the links field of `Node` objects. let this = unsafe { container_of!(node, Node, links) }; + // SAFETY: `this` is a non-null node so it is valid by the type invariants. - node = match key.cmp(unsafe { &(*this).key }) { - // SAFETY: `node` is a non-null node so it is valid by the type invariants. - Ordering::Less => unsafe { (*node).rb_left }, - // SAFETY: `node` is a non-null node so it is valid by the type invariants. - Ordering::Greater => unsafe { (*node).rb_right }, - // SAFETY: `node` is a non-null node so it is valid by the type invariants. - Ordering::Equal => return Some(unsafe { &(*this).value }), + let this_ref = unsafe { &*this }; + + // SAFETY: `node` is a non-null node so it is valid by the type invariants. + let node_ref = unsafe { &*node }; + + node = match key.cmp(&this_ref.key) { + Ordering::Less => node_ref.rb_left, + Ordering::Greater => node_ref.rb_right, + Ordering::Equal => return Some(&this_ref.value), } } None @@ -498,10 +501,10 @@ where let this = unsafe { container_of!(node, Node, links) }; // SAFETY: `this` is a non-null node so it is valid by the type invariants. let this_key = unsafe { &(*this).key }; + // SAFETY: `node` is a non-null node so it is valid by the type invariants. - let left_child = unsafe { (*node).rb_left }; - // SAFETY: `node` is a non-null node so it is valid by the type invariants. - let right_child = unsafe { (*node).rb_right }; + let node_ref = unsafe { &*node }; + match key.cmp(this_key) { Ordering::Equal => { // SAFETY: `this` is a non-null node so it is valid by the type invariants. @@ -509,7 +512,7 @@ where break; } Ordering::Greater => { - node = right_child; + node = node_ref.rb_right; } Ordering::Less => { let is_better_match = match best_key { @@ -521,7 +524,7 @@ where // SAFETY: `this` is a non-null node so it is valid by the type invariants. best_links = Some(unsafe { NonNull::new_unchecked(&mut (*this).links) }); } - node = left_child; + node = node_ref.rb_left; } }; } -- cgit v1.2.3 From 2e2f6b0ef8551bf3bd8255729d27e3ad9451e562 Mon Sep 17 00:00:00 2001 From: Asahi Lina Date: Sun, 18 Jan 2026 10:08:08 +0000 Subject: rust: iommu: add io_pgtable abstraction This will be used by the Tyr driver to create and modify the page table of each address space on the GPU. Each time a mapping gets created or removed by userspace, Tyr will call into GPUVM, which will figure out which calls to map_pages and unmap_pages are required to map the data in question in the page table so that the GPU may access those pages when using that address space. The Rust type wraps the struct using a raw pointer rather than the usual Opaque+ARef approach because Opaque+ARef requires the target type to be refcounted. Signed-off-by: Asahi Lina Acked-by: Boris Brezillon Reviewed-by: Daniel Almeida Tested-by: Deborah Brouwer Co-developed-by: Alice Ryhl Signed-off-by: Alice Ryhl Reviewed-by: Gary Guo Reviewed-by: Danilo Krummrich [joro: Fixed up Rust import style] Signed-off-by: Joerg Roedel --- rust/kernel/iommu/mod.rs | 5 + rust/kernel/iommu/pgtable.rs | 279 +++++++++++++++++++++++++++++++++++++++++++ rust/kernel/lib.rs | 1 + 3 files changed, 285 insertions(+) create mode 100644 rust/kernel/iommu/mod.rs create mode 100644 rust/kernel/iommu/pgtable.rs (limited to 'rust/kernel') diff --git a/rust/kernel/iommu/mod.rs b/rust/kernel/iommu/mod.rs new file mode 100644 index 000000000000..1423d7b19b57 --- /dev/null +++ b/rust/kernel/iommu/mod.rs @@ -0,0 +1,5 @@ +// SPDX-License-Identifier: GPL-2.0 + +//! Rust support related to IOMMU. + +pub mod pgtable; diff --git a/rust/kernel/iommu/pgtable.rs b/rust/kernel/iommu/pgtable.rs new file mode 100644 index 000000000000..6135ba141e48 --- /dev/null +++ b/rust/kernel/iommu/pgtable.rs @@ -0,0 +1,279 @@ +// SPDX-License-Identifier: GPL-2.0 + +//! IOMMU page table management. +//! +//! C header: [`include/io-pgtable.h`](srctree/include/io-pgtable.h) + +use core::{ + marker::PhantomData, + ptr::NonNull, // +}; + +use crate::{ + alloc, + bindings, + device::{ + Bound, + Device // + }, + devres::Devres, + error::to_result, + io::PhysAddr, + prelude::*, // +}; + +use bindings::io_pgtable_fmt; + +/// Protection flags used with IOMMU mappings. +pub mod prot { + /// Read access. + pub const READ: u32 = bindings::IOMMU_READ; + /// Write access. + pub const WRITE: u32 = bindings::IOMMU_WRITE; + /// Request cache coherency. + pub const CACHE: u32 = bindings::IOMMU_CACHE; + /// Request no-execute permission. + pub const NOEXEC: u32 = bindings::IOMMU_NOEXEC; + /// MMIO peripheral mapping. + pub const MMIO: u32 = bindings::IOMMU_MMIO; + /// Privileged mapping. + pub const PRIVILEGED: u32 = bindings::IOMMU_PRIV; +} + +/// Represents a requested `io_pgtable` configuration. +pub struct Config { + /// Quirk bitmask (type-specific). + pub quirks: usize, + /// Valid page sizes, as a bitmask of powers of two. + pub pgsize_bitmap: usize, + /// Input address space size in bits. + pub ias: u32, + /// Output address space size in bits. + pub oas: u32, + /// IOMMU uses coherent accesses for page table walks. + pub coherent_walk: bool, +} + +/// An io page table using a specific format. +/// +/// # Invariants +/// +/// The pointer references a valid io page table. +pub struct IoPageTable { + ptr: NonNull, + _marker: PhantomData, +} + +// SAFETY: `struct io_pgtable_ops` is not restricted to a single thread. +unsafe impl Send for IoPageTable {} +// SAFETY: `struct io_pgtable_ops` may be accessed concurrently. +unsafe impl Sync for IoPageTable {} + +/// The format used by this page table. +pub trait IoPageTableFmt: 'static { + /// The value representing this format. + const FORMAT: io_pgtable_fmt; +} + +impl IoPageTable { + /// Create a new `IoPageTable` as a device resource. + #[inline] + pub fn new( + dev: &Device, + config: Config, + ) -> impl PinInit>, Error> + '_ { + // SAFETY: Devres ensures that the value is dropped during device unbind. + Devres::new(dev, unsafe { Self::new_raw(dev, config) }) + } + + /// Create a new `IoPageTable`. + /// + /// # Safety + /// + /// If successful, then the returned `IoPageTable` must be dropped before the device is + /// unbound. + #[inline] + pub unsafe fn new_raw(dev: &Device, config: Config) -> Result> { + let mut raw_cfg = bindings::io_pgtable_cfg { + quirks: config.quirks, + pgsize_bitmap: config.pgsize_bitmap, + ias: config.ias, + oas: config.oas, + coherent_walk: config.coherent_walk, + tlb: &raw const NOOP_FLUSH_OPS, + iommu_dev: dev.as_raw(), + // SAFETY: All zeroes is a valid value for `struct io_pgtable_cfg`. + ..unsafe { core::mem::zeroed() } + }; + + // SAFETY: + // * The raw_cfg pointer is valid for the duration of this call. + // * The provided `FLUSH_OPS` contains valid function pointers that accept a null pointer + // as cookie. + // * The caller ensures that the io pgtable does not outlive the device. + let ops = unsafe { + bindings::alloc_io_pgtable_ops(F::FORMAT, &mut raw_cfg, core::ptr::null_mut()) + }; + + // INVARIANT: We successfully created a valid page table. + Ok(IoPageTable { + ptr: NonNull::new(ops).ok_or(ENOMEM)?, + _marker: PhantomData, + }) + } + + /// Obtain a raw pointer to the underlying `struct io_pgtable_ops`. + #[inline] + pub fn raw_ops(&self) -> *mut bindings::io_pgtable_ops { + self.ptr.as_ptr() + } + + /// Obtain a raw pointer to the underlying `struct io_pgtable`. + #[inline] + pub fn raw_pgtable(&self) -> *mut bindings::io_pgtable { + // SAFETY: The io_pgtable_ops of an io-pgtable is always the ops field of a io_pgtable. + unsafe { kernel::container_of!(self.raw_ops(), bindings::io_pgtable, ops) } + } + + /// Obtain a raw pointer to the underlying `struct io_pgtable_cfg`. + #[inline] + pub fn raw_cfg(&self) -> *mut bindings::io_pgtable_cfg { + // SAFETY: The `raw_pgtable()` method returns a valid pointer. + unsafe { &raw mut (*self.raw_pgtable()).cfg } + } + + /// Map a physically contiguous range of pages of the same size. + /// + /// Even if successful, this operation may not map the entire range. In that case, only a + /// prefix of the range is mapped, and the returned integer indicates its length in bytes. In + /// this case, the caller will usually call `map_pages` again for the remaining range. + /// + /// The returned [`Result`] indicates whether an error was encountered while mapping pages. + /// Note that this may return a non-zero length even if an error was encountered. The caller + /// will usually [unmap the relevant pages](Self::unmap_pages) on error. + /// + /// The caller must flush the TLB before using the pgtable to access the newly created mapping. + /// + /// # Safety + /// + /// * No other io-pgtable operation may access the range `iova .. iova+pgsize*pgcount` while + /// this `map_pages` operation executes. + /// * This page table must not contain any mapping that overlaps with the mapping created by + /// this call. + /// * If this page table is live, then the caller must ensure that it's okay to access the + /// physical address being mapped for the duration in which it is mapped. + #[inline] + pub unsafe fn map_pages( + &self, + iova: usize, + paddr: PhysAddr, + pgsize: usize, + pgcount: usize, + prot: u32, + flags: alloc::Flags, + ) -> (usize, Result) { + let mut mapped: usize = 0; + + // SAFETY: The `map_pages` function in `io_pgtable_ops` is never null. + let map_pages = unsafe { (*self.raw_ops()).map_pages.unwrap_unchecked() }; + + // SAFETY: The safety requirements of this method are sufficient to call `map_pages`. + let ret = to_result(unsafe { + (map_pages)( + self.raw_ops(), + iova, + paddr, + pgsize, + pgcount, + prot as i32, + flags.as_raw(), + &mut mapped, + ) + }); + + (mapped, ret) + } + + /// Unmap a range of virtually contiguous pages of the same size. + /// + /// This may not unmap the entire range, and returns the length of the unmapped prefix in + /// bytes. + /// + /// # Safety + /// + /// * No other io-pgtable operation may access the range `iova .. iova+pgsize*pgcount` while + /// this `unmap_pages` operation executes. + /// * This page table must contain one or more consecutive mappings starting at `iova` whose + /// total size is `pgcount * pgsize`. + #[inline] + #[must_use] + pub unsafe fn unmap_pages(&self, iova: usize, pgsize: usize, pgcount: usize) -> usize { + // SAFETY: The `unmap_pages` function in `io_pgtable_ops` is never null. + let unmap_pages = unsafe { (*self.raw_ops()).unmap_pages.unwrap_unchecked() }; + + // SAFETY: The safety requirements of this method are sufficient to call `unmap_pages`. + unsafe { (unmap_pages)(self.raw_ops(), iova, pgsize, pgcount, core::ptr::null_mut()) } + } +} + +// For the initial users of these rust bindings, the GPU FW is managing the IOTLB and performs all +// required invalidations using a range. There is no need for it get ARM style invalidation +// instructions from the page table code. +// +// Support for flushing the TLB with ARM style invalidation instructions may be added in the +// future. +static NOOP_FLUSH_OPS: bindings::iommu_flush_ops = bindings::iommu_flush_ops { + tlb_flush_all: Some(rust_tlb_flush_all_noop), + tlb_flush_walk: Some(rust_tlb_flush_walk_noop), + tlb_add_page: None, +}; + +#[no_mangle] +extern "C" fn rust_tlb_flush_all_noop(_cookie: *mut core::ffi::c_void) {} + +#[no_mangle] +extern "C" fn rust_tlb_flush_walk_noop( + _iova: usize, + _size: usize, + _granule: usize, + _cookie: *mut core::ffi::c_void, +) { +} + +impl Drop for IoPageTable { + fn drop(&mut self) { + // SAFETY: The caller of `Self::ttbr()` promised that the page table is not live when this + // destructor runs. + unsafe { bindings::free_io_pgtable_ops(self.raw_ops()) }; + } +} + +/// The `ARM_64_LPAE_S1` page table format. +pub enum ARM64LPAES1 {} + +impl IoPageTableFmt for ARM64LPAES1 { + const FORMAT: io_pgtable_fmt = bindings::io_pgtable_fmt_ARM_64_LPAE_S1 as io_pgtable_fmt; +} + +impl IoPageTable { + /// Access the `ttbr` field of the configuration. + /// + /// This is the physical address of the page table, which may be passed to the device that + /// needs to use it. + /// + /// # Safety + /// + /// The caller must ensure that the device stops using the page table before dropping it. + #[inline] + pub unsafe fn ttbr(&self) -> u64 { + // SAFETY: `arm_lpae_s1_cfg` is the right cfg type for `ARM64LPAES1`. + unsafe { (*self.raw_cfg()).__bindgen_anon_1.arm_lpae_s1_cfg.ttbr } + } + + /// Access the `mair` field of the configuration. + #[inline] + pub fn mair(&self) -> u64 { + // SAFETY: `arm_lpae_s1_cfg` is the right cfg type for `ARM64LPAES1`. + unsafe { (*self.raw_cfg()).__bindgen_anon_1.arm_lpae_s1_cfg.mair } + } +} diff --git a/rust/kernel/lib.rs b/rust/kernel/lib.rs index f812cf120042..e7fba6fa0f81 100644 --- a/rust/kernel/lib.rs +++ b/rust/kernel/lib.rs @@ -103,6 +103,7 @@ pub mod id_pool; pub mod init; pub mod io; pub mod ioctl; +pub mod iommu; pub mod iov; pub mod irq; pub mod jump_label; -- cgit v1.2.3 From 744905705113a6ab4c38127f18cc0d71594cfaec Mon Sep 17 00:00:00 2001 From: FUJITA Tomonori Date: Wed, 3 Dec 2025 09:04:11 +0900 Subject: rust: debugfs: Use kernel Atomic type in docs example Switch the read_callback_file() documentation example from core::sync::atomic::AtomicU32 to the kernel's Atomic because Rust native atomics are not allowed to use in kernel. Signed-off-by: FUJITA Tomonori Reviewed-by: Boqun Feng Link: https://patch.msgid.link/20251203000411.30434-1-fujita.tomonori@gmail.com [ Use kernel vertical import style. - Danilo ] Signed-off-by: Danilo Krummrich --- rust/kernel/debugfs.rs | 17 +++++++++++------ 1 file changed, 11 insertions(+), 6 deletions(-) (limited to 'rust/kernel') diff --git a/rust/kernel/debugfs.rs b/rust/kernel/debugfs.rs index 513cc5750e63..d7b8014a6474 100644 --- a/rust/kernel/debugfs.rs +++ b/rust/kernel/debugfs.rs @@ -207,20 +207,25 @@ impl Dir { /// # Examples /// /// ``` - /// # use core::sync::atomic::{AtomicU32, Ordering}; - /// # use kernel::debugfs::Dir; - /// # use kernel::prelude::*; + /// # use kernel::{ + /// # debugfs::Dir, + /// # prelude::*, + /// # sync::atomic::{ + /// # Atomic, + /// # Relaxed, + /// # }, + /// # }; /// # let dir = Dir::new(c"foo"); /// let file = KBox::pin_init( /// dir.read_callback_file(c"bar", - /// AtomicU32::new(3), + /// Atomic::::new(3), /// &|val, f| { - /// let out = val.load(Ordering::Relaxed); + /// let out = val.load(Relaxed); /// writeln!(f, "{out:#010x}") /// }), /// GFP_KERNEL)?; /// // Reading "foo/bar" will show "0x00000003". - /// file.store(10, Ordering::Relaxed); + /// file.store(10, Relaxed); /// // Reading "foo/bar" will now show "0x0000000a". /// # Ok::<(), Error>(()) /// ``` -- cgit v1.2.3 From 5025569cb63060255834c95ab3779905aecf67b0 Mon Sep 17 00:00:00 2001 From: Shankari Anand Date: Sun, 23 Nov 2025 14:54:34 +0530 Subject: rust: pwm: Update ARef and AlwaysRefCounted imports to use sync::aref MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Update call sites in `pwm.rs` to import `ARef` and `AlwaysRefCounted` from `sync::aref` instead of `types`. This aligns with the ongoing effort to move `ARef` and `AlwaysRefCounted` to sync. Suggested-by: Benno Lossin Link: https://github.com/Rust-for-Linux/linux/issues/1173 Signed-off-by: Shankari Anand Acked-by: Michal Wilczynski Link: https://patch.msgid.link/20251123092438.182251-7-shankari.ak0208@gmail.com Signed-off-by: Uwe Kleine-König --- rust/kernel/pwm.rs | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) (limited to 'rust/kernel') diff --git a/rust/kernel/pwm.rs b/rust/kernel/pwm.rs index cb00f8a8765c..1605d13d5d64 100644 --- a/rust/kernel/pwm.rs +++ b/rust/kernel/pwm.rs @@ -13,7 +13,8 @@ use crate::{ devres, error::{self, to_result}, prelude::*, - types::{ARef, AlwaysRefCounted, Opaque}, // + sync::aref::{ARef, AlwaysRefCounted}, + types::Opaque, // }; use core::{marker::PhantomData, ptr::NonNull}; -- cgit v1.2.3 From 85a5ffbd56b236e96a7a22a631f9febf36d7ead5 Mon Sep 17 00:00:00 2001 From: Markus Probst Date: Tue, 2 Dec 2025 18:17:52 +0000 Subject: rust: pwm: Add UnregisteredChip wrapper around Chip MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The `pwm::Registration::register` function provides no guarantee that the function isn't called twice with the same pwm chip, which is considered unsafe. Add `pwm::UnregisteredChip` as wrapper around `pwm::Chip`. Implement `pwm::UnregisteredChip::register` for the registration. This function takes ownership of `pwm::UnregisteredChip` and therefore guarantees that the registration can't be called twice on the same pwm chip. Signed-off-by: Markus Probst Tested-by: Michal Wilczynski Acked-by: Michal Wilczynski Link: https://patch.msgid.link/20251202-pwm_safe_register-v2-1-7a2e0d1e287f@posteo.de [ukleinek: fixes a typo that Michal pointed out during review] Signed-off-by: Uwe Kleine-König --- rust/kernel/pwm.rs | 68 +++++++++++++++++++++++++++++++++++------------------- 1 file changed, 44 insertions(+), 24 deletions(-) (limited to 'rust/kernel') diff --git a/rust/kernel/pwm.rs b/rust/kernel/pwm.rs index 1605d13d5d64..2dd72a39acb5 100644 --- a/rust/kernel/pwm.rs +++ b/rust/kernel/pwm.rs @@ -16,7 +16,11 @@ use crate::{ sync::aref::{ARef, AlwaysRefCounted}, types::Opaque, // }; -use core::{marker::PhantomData, ptr::NonNull}; +use core::{ + marker::PhantomData, + ops::Deref, + ptr::NonNull, // +}; /// Represents a PWM waveform configuration. /// Mirrors struct [`struct pwm_waveform`](srctree/include/linux/pwm.h). @@ -174,7 +178,7 @@ pub struct RoundedWaveform { } /// Trait defining the operations for a PWM driver. -pub trait PwmOps: 'static + Sized { +pub trait PwmOps: 'static + Send + Sync + Sized { /// The driver-specific hardware representation of a waveform. /// /// This type must be [`Copy`], [`Default`], and fit within `PWM_WFHWSIZE`. @@ -585,11 +589,12 @@ impl Chip { /// /// Returns an [`ARef`] managing the chip's lifetime via refcounting /// on its embedded `struct device`. - pub fn new( - parent_dev: &device::Device, + #[allow(clippy::new_ret_no_self)] + pub fn new<'a>( + parent_dev: &'a device::Device, num_channels: u32, data: impl pin_init::PinInit, - ) -> Result> { + ) -> Result> { let sizeof_priv = core::mem::size_of::(); // SAFETY: `pwmchip_alloc` allocates memory for the C struct and our private data. let c_chip_ptr_raw = @@ -624,7 +629,9 @@ impl Chip { // SAFETY: `chip_ptr_as_self` points to a valid `Chip` (layout-compatible with // `bindings::pwm_chip`) whose embedded device has refcount 1. // `ARef::from_raw` takes this pointer and manages it via `AlwaysRefCounted`. - Ok(unsafe { ARef::from_raw(NonNull::new_unchecked(chip_ptr_as_self)) }) + let chip = unsafe { ARef::from_raw(NonNull::new_unchecked(chip_ptr_as_self)) }; + + Ok(UnregisteredChip { chip, parent_dev }) } } @@ -655,37 +662,29 @@ unsafe impl AlwaysRefCounted for Chip { // structure's state is managed and synchronized by the kernel's device model // and PWM core locking mechanisms. Therefore, it is safe to move the `Chip` // wrapper (and the pointer it contains) across threads. -unsafe impl Send for Chip {} +unsafe impl Send for Chip {} // SAFETY: It is safe for multiple threads to have shared access (`&Chip`) because // the `Chip` data is immutable from the Rust side without holding the appropriate // kernel locks, which the C core is responsible for. Any interior mutability is // handled and synchronized by the C kernel code. -unsafe impl Sync for Chip {} +unsafe impl Sync for Chip {} -/// A resource guard that ensures `pwmchip_remove` is called on drop. -/// -/// This struct is intended to be managed by the `devres` framework by transferring its ownership -/// via [`devres::register`]. This ties the lifetime of the PWM chip registration -/// to the lifetime of the underlying device. -pub struct Registration { +/// A wrapper around `ARef>` that ensures that `register` can only be called once. +pub struct UnregisteredChip<'a, T: PwmOps> { chip: ARef>, + parent_dev: &'a device::Device, } -impl Registration { +impl UnregisteredChip<'_, T> { /// Registers a PWM chip with the PWM subsystem. /// /// Transfers its ownership to the `devres` framework, which ties its lifetime /// to the parent device. /// On unbind of the parent device, the `devres` entry will be dropped, automatically /// calling `pwmchip_remove`. This function should be called from the driver's `probe`. - pub fn register(dev: &device::Device, chip: ARef>) -> Result { - let chip_parent = chip.device().parent().ok_or(EINVAL)?; - if dev.as_raw() != chip_parent.as_raw() { - return Err(EINVAL); - } - - let c_chip_ptr = chip.as_raw(); + pub fn register(self) -> Result>> { + let c_chip_ptr = self.chip.as_raw(); // SAFETY: `c_chip_ptr` points to a valid chip with its ops initialized. // `__pwmchip_add` is the C function to register the chip with the PWM core. @@ -693,12 +692,33 @@ impl Registration { to_result(bindings::__pwmchip_add(c_chip_ptr, core::ptr::null_mut()))?; } - let registration = Registration { chip }; + let registration = Registration { + chip: ARef::clone(&self.chip), + }; + + devres::register(self.parent_dev, registration, GFP_KERNEL)?; - devres::register(dev, registration, GFP_KERNEL) + Ok(self.chip) } } +impl Deref for UnregisteredChip<'_, T> { + type Target = Chip; + + fn deref(&self) -> &Self::Target { + &self.chip + } +} + +/// A resource guard that ensures `pwmchip_remove` is called on drop. +/// +/// This struct is intended to be managed by the `devres` framework by transferring its ownership +/// via [`devres::register`]. This ties the lifetime of the PWM chip registration +/// to the lifetime of the underlying device. +struct Registration { + chip: ARef>, +} + impl Drop for Registration { fn drop(&mut self) { let chip_raw = self.chip.as_raw(); -- cgit v1.2.3 From a2633dc243c35754a0c2270131d8a199c987c9bf Mon Sep 17 00:00:00 2001 From: Kari Argillander Date: Fri, 2 Jan 2026 09:51:41 +0200 Subject: rust: pwm: Fix potential memory leak on init error MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit When initializing a PWM chip using pwmchip_alloc(), the allocated device owns an initial reference that must be released on all error paths. If __pinned_init() were to fail, the allocated pwm_chip would currently leak because the error path returns without calling pwmchip_put(). Fixes: 7b3dce814a15 ("rust: pwm: Add Kconfig and basic data structures") Signed-off-by: Kari Argillander Acked-by: Michal Wilczynski Link: https://patch.msgid.link/20260102-pwm-rust-v2-1-2702ce57d571@gmail.com Signed-off-by: Uwe Kleine-König --- rust/kernel/pwm.rs | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) (limited to 'rust/kernel') diff --git a/rust/kernel/pwm.rs b/rust/kernel/pwm.rs index 2dd72a39acb5..4f683158fc08 100644 --- a/rust/kernel/pwm.rs +++ b/rust/kernel/pwm.rs @@ -607,7 +607,11 @@ impl Chip { let drvdata_ptr = unsafe { bindings::pwmchip_get_drvdata(c_chip_ptr) }; // SAFETY: We construct the `T` object in-place in the allocated private memory. - unsafe { data.__pinned_init(drvdata_ptr.cast())? }; + unsafe { data.__pinned_init(drvdata_ptr.cast()) }.inspect_err(|_| { + // SAFETY: It is safe to call `pwmchip_put()` with a valid pointer obtained + // from `pwmchip_alloc()`. We will not use pointer after this. + unsafe { bindings::pwmchip_put(c_chip_ptr) } + })?; // SAFETY: `c_chip_ptr` points to a valid chip. unsafe { -- cgit v1.2.3 From fc1e4eae19eef739fea732902354ec93ab8234c6 Mon Sep 17 00:00:00 2001 From: Kari Argillander Date: Fri, 2 Jan 2026 09:51:42 +0200 Subject: rust: pwm: Simplify to_result call sites and unsafe blocks MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Remove unnecessary temporary variables around to_result() calls and move trailing semicolons outside unsafe blocks to improve readability and produce cleaner rustfmt output. No functional change intended. Signed-off-by: Kari Argillander Acked-by: Michal Wilczynski Link: https://patch.msgid.link/20260102-pwm-rust-v2-2-2702ce57d571@gmail.com Signed-off-by: Uwe Kleine-König --- rust/kernel/pwm.rs | 47 ++++++++++++++--------------------------------- 1 file changed, 14 insertions(+), 33 deletions(-) (limited to 'rust/kernel') diff --git a/rust/kernel/pwm.rs b/rust/kernel/pwm.rs index 4f683158fc08..6c9d667009ef 100644 --- a/rust/kernel/pwm.rs +++ b/rust/kernel/pwm.rs @@ -129,8 +129,7 @@ impl Device { // SAFETY: `self.as_raw()` provides a valid `*mut pwm_device` pointer. // `&c_wf` is a valid pointer to a `pwm_waveform` struct. The C function // handles all necessary internal locking. - let ret = unsafe { bindings::pwm_set_waveform_might_sleep(self.as_raw(), &c_wf, exact) }; - to_result(ret) + to_result(unsafe { bindings::pwm_set_waveform_might_sleep(self.as_raw(), &c_wf, exact) }) } /// Queries the hardware for the configuration it would apply for a given @@ -160,9 +159,7 @@ impl Device { // SAFETY: `self.as_raw()` is a valid pointer. We provide a valid pointer // to a stack-allocated `pwm_waveform` struct for the kernel to fill. - let ret = unsafe { bindings::pwm_get_waveform_might_sleep(self.as_raw(), &mut c_wf) }; - - to_result(ret)?; + to_result(unsafe { bindings::pwm_get_waveform_might_sleep(self.as_raw(), &mut c_wf) })?; Ok(Waveform::from(c_wf)) } @@ -263,8 +260,8 @@ impl Adapter { core::ptr::from_ref::(wfhw).cast::(), wfhw_ptr.cast::(), size, - ); - } + ) + }; Ok(()) } @@ -284,8 +281,8 @@ impl Adapter { wfhw_ptr.cast::(), core::ptr::from_mut::(&mut wfhw).cast::(), size, - ); - } + ) + }; Ok(wfhw) } @@ -311,9 +308,7 @@ impl Adapter { // Now, call the original release function to free the `pwm_chip` itself. // SAFETY: `dev` is the valid pointer passed into this callback, which is // the expected argument for `pwmchip_release`. - unsafe { - bindings::pwmchip_release(dev); - } + unsafe { bindings::pwmchip_release(dev) }; } /// # Safety @@ -413,9 +408,7 @@ impl Adapter { match T::round_waveform_fromhw(chip, pwm, &wfhw, &mut rust_wf) { Ok(()) => { // SAFETY: `wf_ptr` is guaranteed valid by the C caller. - unsafe { - *wf_ptr = rust_wf.into(); - }; + unsafe { *wf_ptr = rust_wf.into() }; 0 } Err(e) => e.to_errno(), @@ -614,16 +607,12 @@ impl Chip { })?; // SAFETY: `c_chip_ptr` points to a valid chip. - unsafe { - (*c_chip_ptr).dev.release = Some(Adapter::::release_callback); - } + unsafe { (*c_chip_ptr).dev.release = Some(Adapter::::release_callback) }; // SAFETY: `c_chip_ptr` points to a valid chip. // The `Adapter`'s `VTABLE` has a 'static lifetime, so the pointer // returned by `as_raw()` is always valid. - unsafe { - (*c_chip_ptr).ops = Adapter::::VTABLE.as_raw(); - } + unsafe { (*c_chip_ptr).ops = Adapter::::VTABLE.as_raw() }; // Cast the `*mut bindings::pwm_chip` to `*mut Chip`. This is valid because // `Chip` is `repr(transparent)` over `Opaque`, and @@ -645,9 +634,7 @@ unsafe impl AlwaysRefCounted for Chip { fn inc_ref(&self) { // SAFETY: `self.0.get()` points to a valid `pwm_chip` because `self` exists. // The embedded `dev` is valid. `get_device` increments its refcount. - unsafe { - bindings::get_device(&raw mut (*self.0.get()).dev); - } + unsafe { bindings::get_device(&raw mut (*self.0.get()).dev) }; } #[inline] @@ -656,9 +643,7 @@ unsafe impl AlwaysRefCounted for Chip { // SAFETY: `obj` is a valid pointer to a `Chip` (and thus `bindings::pwm_chip`) // with a non-zero refcount. `put_device` handles decrement and final release. - unsafe { - bindings::put_device(&raw mut (*c_chip_ptr).dev); - } + unsafe { bindings::put_device(&raw mut (*c_chip_ptr).dev) }; } } @@ -692,9 +677,7 @@ impl UnregisteredChip<'_, T> { // SAFETY: `c_chip_ptr` points to a valid chip with its ops initialized. // `__pwmchip_add` is the C function to register the chip with the PWM core. - unsafe { - to_result(bindings::__pwmchip_add(c_chip_ptr, core::ptr::null_mut()))?; - } + to_result(unsafe { bindings::__pwmchip_add(c_chip_ptr, core::ptr::null_mut()) })?; let registration = Registration { chip: ARef::clone(&self.chip), @@ -730,9 +713,7 @@ impl Drop for Registration { // SAFETY: `chip_raw` points to a chip that was successfully registered. // `bindings::pwmchip_remove` is the correct C function to unregister it. // This `drop` implementation is called automatically by `devres` on driver unbind. - unsafe { - bindings::pwmchip_remove(chip_raw); - } + unsafe { bindings::pwmchip_remove(chip_raw) }; } } -- cgit v1.2.3 From d8f87aa5fa0a4276491fa8ef436cd22605a3f9ba Mon Sep 17 00:00:00 2001 From: Ethan Nelson-Moore Date: Sun, 18 Jan 2026 18:24:04 -0800 Subject: net: remove HIPPI support and RoadRunner HIPPI driver HIPPI has not been relevant for over two decades. It was rapidly eclipsed by Fibre Channel, and even when it was new, it was confined to very high-end hardware. The HIPPI code has only received tree-wide changes and fixes by inspection in the entire Git history. Remove HIPPI support and the rrunner HIPPI driver, and move the former maintainer to the CREDITS file. Keep the include/uapi/linux/if_hippi.h header because it is used by the TUN code, and to avoid breaking userspace, however unlikely that may be. Signed-off-by: Ethan Nelson-Moore Link: https://patch.msgid.link/20260119022451.22344-1-enelsonmoore@gmail.com Signed-off-by: Jakub Kicinski --- rust/kernel/pci/id.rs | 1 - 1 file changed, 1 deletion(-) (limited to 'rust/kernel') diff --git a/rust/kernel/pci/id.rs b/rust/kernel/pci/id.rs index c09125946d9e..42ffb1bbf87c 100644 --- a/rust/kernel/pci/id.rs +++ b/rust/kernel/pci/id.rs @@ -416,7 +416,6 @@ define_all_pci_vendors! { MICROSEMI = bindings::PCI_VENDOR_ID_MICROSEMI, // 0x11f8 RP = bindings::PCI_VENDOR_ID_RP, // 0x11fe CYCLADES = bindings::PCI_VENDOR_ID_CYCLADES, // 0x120e - ESSENTIAL = bindings::PCI_VENDOR_ID_ESSENTIAL, // 0x120f O2 = bindings::PCI_VENDOR_ID_O2, // 0x1217 THREEDX = bindings::PCI_VENDOR_ID_3DFX, // 0x121a AVM = bindings::PCI_VENDOR_ID_AVM, // 0x1244 -- cgit v1.2.3 From d7a4693a250ee2f185ce5c878e74252e533ac4b9 Mon Sep 17 00:00:00 2001 From: Ke Sun Date: Tue, 20 Jan 2026 16:38:18 +0800 Subject: rust: block: mq: use pin_init::zeroed() for queue_limits Replace unsafe core::mem::zeroed() with pin_init::zeroed() for queue_limits initialization. Signed-off-by: Ke Sun Acked-by: Andreas Hindborg Reviewed-by: Gary Guo Link: https://lore.kernel.org/r/20260120083824.477339-3-sunke@kylinos.cn Signed-off-by: Jens Axboe --- rust/kernel/block/mq/gen_disk.rs | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) (limited to 'rust/kernel') diff --git a/rust/kernel/block/mq/gen_disk.rs b/rust/kernel/block/mq/gen_disk.rs index 1ce815c8cdab..c8b0ecb17082 100644 --- a/rust/kernel/block/mq/gen_disk.rs +++ b/rust/kernel/block/mq/gen_disk.rs @@ -107,8 +107,7 @@ impl GenDiskBuilder { drop(unsafe { T::QueueData::from_foreign(data) }); }); - // SAFETY: `bindings::queue_limits` contain only fields that are valid when zeroed. - let mut lim: bindings::queue_limits = unsafe { core::mem::zeroed() }; + let mut lim: bindings::queue_limits = pin_init::zeroed(); lim.logical_block_size = self.logical_block_size; lim.physical_block_size = self.physical_block_size; -- cgit v1.2.3 From 880528eaa67fc6446a0b5c16757f0d6a2639ccda Mon Sep 17 00:00:00 2001 From: Ke Sun Date: Tue, 20 Jan 2026 16:38:19 +0800 Subject: rust: block: mq: use pin_init::zeroed() for tag_set Replace unsafe core::mem::zeroed() with pin_init::zeroed() for blk_mq_tag_set initialization. Signed-off-by: Ke Sun Acked-by: Andreas Hindborg Reviewed-by: Gary Guo Link: https://lore.kernel.org/r/20260120083824.477339-4-sunke@kylinos.cn Signed-off-by: Jens Axboe --- rust/kernel/block/mq/tag_set.rs | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) (limited to 'rust/kernel') diff --git a/rust/kernel/block/mq/tag_set.rs b/rust/kernel/block/mq/tag_set.rs index c3cf56d52bee..dae9df408a86 100644 --- a/rust/kernel/block/mq/tag_set.rs +++ b/rust/kernel/block/mq/tag_set.rs @@ -38,9 +38,7 @@ impl TagSet { num_tags: u32, num_maps: u32, ) -> impl PinInit { - // SAFETY: `blk_mq_tag_set` only contains integers and pointers, which - // all are allowed to be 0. - let tag_set: bindings::blk_mq_tag_set = unsafe { core::mem::zeroed() }; + let tag_set: bindings::blk_mq_tag_set = pin_init::zeroed(); let tag_set: Result<_> = core::mem::size_of::() .try_into() .map(|cmd_size| { -- cgit v1.2.3 From 7222dd071b221dde38fea6d0798520572877ee2a Mon Sep 17 00:00:00 2001 From: Miguel Ojeda Date: Wed, 21 Jan 2026 19:43:38 +0100 Subject: rust: iommu: fix Rust formatting The Rust kernel code should be kept `rustfmt`-clean [1]. Thus run the `rustfmt` target to fix the formatting issue. Link: https://rust-for-linux.com/contributing#submit-checklist-addendum [1] Fixes: 2e2f6b0ef855 ("rust: iommu: add io_pgtable abstraction") Signed-off-by: Miguel Ojeda Signed-off-by: Joerg Roedel --- rust/kernel/iommu/pgtable.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'rust/kernel') diff --git a/rust/kernel/iommu/pgtable.rs b/rust/kernel/iommu/pgtable.rs index 6135ba141e48..916e1f509e62 100644 --- a/rust/kernel/iommu/pgtable.rs +++ b/rust/kernel/iommu/pgtable.rs @@ -14,7 +14,7 @@ use crate::{ bindings, device::{ Bound, - Device // + Device, // }, devres::Devres, error::to_result, -- cgit v1.2.3 From 12248a3862d50ef1a889153bb222d10d43e78c9d Mon Sep 17 00:00:00 2001 From: Miguel Ojeda Date: Wed, 21 Jan 2026 19:43:39 +0100 Subject: rust: iommu: fix `srctree` link warning The Rust kernel code should be kept `rustdoc`-clean [1]. Our custom `srctree` link checker in the `rustdoc` target reports: warning: srctree/ link to include/io-pgtable.h does not exist Thus fix it. Link: https://rust-for-linux.com/contributing#submit-checklist-addendum [1] Fixes: 2e2f6b0ef855 ("rust: iommu: add io_pgtable abstraction") Signed-off-by: Miguel Ojeda Signed-off-by: Joerg Roedel --- rust/kernel/iommu/pgtable.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'rust/kernel') diff --git a/rust/kernel/iommu/pgtable.rs b/rust/kernel/iommu/pgtable.rs index 916e1f509e62..c88e38fd938a 100644 --- a/rust/kernel/iommu/pgtable.rs +++ b/rust/kernel/iommu/pgtable.rs @@ -2,7 +2,7 @@ //! IOMMU page table management. //! -//! C header: [`include/io-pgtable.h`](srctree/include/io-pgtable.h) +//! C header: [`include/linux/io-pgtable.h`](srctree/include/linux/io-pgtable.h) use core::{ marker::PhantomData, -- cgit v1.2.3 From 7043698aee6b6f61415ebb49b7e95fdfb9373a77 Mon Sep 17 00:00:00 2001 From: Zhi Wang Date: Wed, 21 Jan 2026 22:22:07 +0200 Subject: rust: devres: style for imports Convert all imports in the devres to use "kernel vertical" style. Cc: Gary Guo Cc: Miguel Ojeda Reviewed-by: Gary Guo Signed-off-by: Zhi Wang Reviewed-by: Alexandre Courbot Link: https://patch.msgid.link/20260121202212.4438-2-zhiw@nvidia.com Signed-off-by: Danilo Krummrich --- rust/kernel/devres.rs | 16 +++++++++++++--- 1 file changed, 13 insertions(+), 3 deletions(-) (limited to 'rust/kernel') diff --git a/rust/kernel/devres.rs b/rust/kernel/devres.rs index db02f8b1788d..43089511bf76 100644 --- a/rust/kernel/devres.rs +++ b/rust/kernel/devres.rs @@ -254,8 +254,12 @@ impl Devres { /// # Examples /// /// ```no_run - /// # #![cfg(CONFIG_PCI)] - /// # use kernel::{device::Core, devres::Devres, pci}; + /// #![cfg(CONFIG_PCI)] + /// use kernel::{ + /// device::Core, + /// devres::Devres, + /// pci, // + /// }; /// /// fn from_core(dev: &pci::Device, devres: Devres>) -> Result { /// let bar = devres.access(dev.as_ref())?; @@ -358,7 +362,13 @@ where /// # Examples /// /// ```no_run -/// use kernel::{device::{Bound, Device}, devres}; +/// use kernel::{ +/// device::{ +/// Bound, +/// Device, // +/// }, +/// devres, // +/// }; /// /// /// Registration of e.g. a class device, IRQ, etc. /// struct Registration; -- cgit v1.2.3 From 638eeda8abaa3e6afe6bd5758ef8045a7f33b9a0 Mon Sep 17 00:00:00 2001 From: Lyude Paul Date: Thu, 22 Jan 2026 17:10:37 -0500 Subject: rust/drm: Fix Registration::{new,new_foreign_owned}() docs Looks like we've actually had a malformed rustdoc reference in the rustdocs for Registration::new_foreign_owned() for a while that, when fixed, still couldn't resolve properly because it refers to a private item. This is probably leftover from when Registration::new() was public, so drop the documentation from that function and fixup the documentation for Registration::new_foreign_owned(). Signed-off-by: Lyude Paul Acked-by: Danilo Krummrich Fixes: 0600032c54b7 ("rust: drm: add DRM driver registration") Cc: # v6.16+ Link: https://patch.msgid.link/20260122221037.3462081-1-lyude@redhat.com --- rust/kernel/drm/driver.rs | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) (limited to 'rust/kernel') diff --git a/rust/kernel/drm/driver.rs b/rust/kernel/drm/driver.rs index f30ee4c6245c..e09f977b5b51 100644 --- a/rust/kernel/drm/driver.rs +++ b/rust/kernel/drm/driver.rs @@ -121,7 +121,6 @@ pub trait Driver { pub struct Registration(ARef>); impl Registration { - /// Creates a new [`Registration`] and registers it. fn new(drm: &drm::Device, flags: usize) -> Result { // SAFETY: `drm.as_raw()` is valid by the invariants of `drm::Device`. to_result(unsafe { bindings::drm_dev_register(drm.as_raw(), flags) })?; @@ -129,8 +128,9 @@ impl Registration { Ok(Self(drm.into())) } - /// Same as [`Registration::new`}, but transfers ownership of the [`Registration`] to - /// [`devres::register`]. + /// Registers a new [`Device`](drm::Device) with userspace. + /// + /// Ownership of the [`Registration`] object is passed to [`devres::register`]. pub fn new_foreign_owned( drm: &drm::Device, dev: &device::Device, -- cgit v1.2.3 From 121d87b28e1d9061d3aaa156c43a627d3cb5e620 Mon Sep 17 00:00:00 2001 From: Zhi Wang Date: Wed, 21 Jan 2026 22:22:08 +0200 Subject: rust: io: separate generic I/O helpers from MMIO implementation The previous Io type combined both the generic I/O access helpers and MMIO implementation details in a single struct. This coupling prevented reusing the I/O helpers for other backends, such as PCI configuration space. Establish a clean separation between the I/O interface and concrete backends by separating generic I/O helpers from MMIO implementation. Introduce a new trait hierarchy to handle different access capabilities: - IoCapable: A marker trait indicating that a backend supports I/O operations of a certain type (u8, u16, u32, or u64). - Io trait: Defines fallible (try_read8, try_write8, etc.) and infallibile (read8, write8, etc.) I/O methods with runtime bounds checking and compile-time bounds checking. - IoKnownSize trait: The marker trait for types support infallible I/O methods. Move the MMIO-specific logic into a dedicated Mmio type that implements the Io traits. Rename IoRaw to MmioRaw and update consumers to use the new types. Cc: Alexandre Courbot Cc: Alice Ryhl Cc: Bjorn Helgaas Cc: Gary Guo Cc: Danilo Krummrich Cc: John Hubbard Signed-off-by: Zhi Wang Reviewed-by: Alice Ryhl Reviewed-by: Alexandre Courbot Reviewed-by: Gary Guo Link: https://patch.msgid.link/20260121202212.4438-3-zhiw@nvidia.com [ Add #[expect(unused)] to define_{read,write}!(). - Danilo ] Signed-off-by: Danilo Krummrich --- rust/kernel/devres.rs | 19 ++- rust/kernel/io.rs | 400 ++++++++++++++++++++++++++++++++++++++++--------- rust/kernel/io/mem.rs | 16 +- rust/kernel/io/poll.rs | 16 +- rust/kernel/pci/io.rs | 12 +- 5 files changed, 370 insertions(+), 93 deletions(-) (limited to 'rust/kernel') diff --git a/rust/kernel/devres.rs b/rust/kernel/devres.rs index 43089511bf76..cdc49677022a 100644 --- a/rust/kernel/devres.rs +++ b/rust/kernel/devres.rs @@ -74,14 +74,17 @@ struct Inner { /// devres::Devres, /// io::{ /// Io, -/// IoRaw, -/// PhysAddr, +/// IoKnownSize, +/// Mmio, +/// MmioRaw, +/// PhysAddr, // /// }, +/// prelude::*, /// }; /// use core::ops::Deref; /// /// // See also [`pci::Bar`] for a real example. -/// struct IoMem(IoRaw); +/// struct IoMem(MmioRaw); /// /// impl IoMem { /// /// # Safety @@ -96,7 +99,7 @@ struct Inner { /// return Err(ENOMEM); /// } /// -/// Ok(IoMem(IoRaw::new(addr as usize, SIZE)?)) +/// Ok(IoMem(MmioRaw::new(addr as usize, SIZE)?)) /// } /// } /// @@ -108,11 +111,11 @@ struct Inner { /// } /// /// impl Deref for IoMem { -/// type Target = Io; +/// type Target = Mmio; /// /// fn deref(&self) -> &Self::Target { /// // SAFETY: The memory range stored in `self` has been properly mapped in `Self::new`. -/// unsafe { Io::from_raw(&self.0) } +/// unsafe { Mmio::from_raw(&self.0) } /// } /// } /// # fn no_run(dev: &Device) -> Result<(), Error> { @@ -258,6 +261,10 @@ impl Devres { /// use kernel::{ /// device::Core, /// devres::Devres, + /// io::{ + /// Io, + /// IoKnownSize, // + /// }, /// pci, // /// }; /// diff --git a/rust/kernel/io.rs b/rust/kernel/io.rs index a97eb44a9a87..53179eb839b1 100644 --- a/rust/kernel/io.rs +++ b/rust/kernel/io.rs @@ -32,16 +32,16 @@ pub type ResourceSize = bindings::resource_size_t; /// By itself, the existence of an instance of this structure does not provide any guarantees that /// the represented MMIO region does exist or is properly mapped. /// -/// Instead, the bus specific MMIO implementation must convert this raw representation into an `Io` -/// instance providing the actual memory accessors. Only by the conversion into an `Io` structure -/// any guarantees are given. -pub struct IoRaw { +/// Instead, the bus specific MMIO implementation must convert this raw representation into an +/// `Mmio` instance providing the actual memory accessors. Only by the conversion into an `Mmio` +/// structure any guarantees are given. +pub struct MmioRaw { addr: usize, maxsize: usize, } -impl IoRaw { - /// Returns a new `IoRaw` instance on success, an error otherwise. +impl MmioRaw { + /// Returns a new `MmioRaw` instance on success, an error otherwise. pub fn new(addr: usize, maxsize: usize) -> Result { if maxsize < SIZE { return Err(EINVAL); @@ -81,14 +81,16 @@ impl IoRaw { /// ffi::c_void, /// io::{ /// Io, -/// IoRaw, +/// IoKnownSize, +/// Mmio, +/// MmioRaw, /// PhysAddr, /// }, /// }; /// use core::ops::Deref; /// /// // See also `pci::Bar` for a real example. -/// struct IoMem(IoRaw); +/// struct IoMem(MmioRaw); /// /// impl IoMem { /// /// # Safety @@ -103,7 +105,7 @@ impl IoRaw { /// return Err(ENOMEM); /// } /// -/// Ok(IoMem(IoRaw::new(addr as usize, SIZE)?)) +/// Ok(IoMem(MmioRaw::new(addr as usize, SIZE)?)) /// } /// } /// @@ -115,11 +117,11 @@ impl IoRaw { /// } /// /// impl Deref for IoMem { -/// type Target = Io; +/// type Target = Mmio; /// /// fn deref(&self) -> &Self::Target { /// // SAFETY: The memory range stored in `self` has been properly mapped in `Self::new`. -/// unsafe { Io::from_raw(&self.0) } +/// unsafe { Mmio::from_raw(&self.0) } /// } /// } /// @@ -133,29 +135,31 @@ impl IoRaw { /// # } /// ``` #[repr(transparent)] -pub struct Io(IoRaw); +pub struct Mmio(MmioRaw); macro_rules! define_read { - ($(#[$attr:meta])* $name:ident, $try_name:ident, $c_fn:ident -> $type_name:ty) => { + (infallible, $(#[$attr:meta])* $vis:vis $name:ident, $c_fn:ident -> $type_name:ty) => { /// Read IO data from a given offset known at compile time. /// /// Bound checks are performed on compile time, hence if the offset is not known at compile /// time, the build will fail. $(#[$attr])* #[inline] - pub fn $name(&self, offset: usize) -> $type_name { + $vis fn $name(&self, offset: usize) -> $type_name { let addr = self.io_addr_assert::<$type_name>(offset); // SAFETY: By the type invariant `addr` is a valid address for MMIO operations. unsafe { bindings::$c_fn(addr as *const c_void) } } + }; + (fallible, $(#[$attr:meta])* $vis:vis $try_name:ident, $c_fn:ident -> $type_name:ty) => { /// Read IO data from a given offset. /// /// Bound checks are performed on runtime, it fails if the offset (plus the type size) is /// out of bounds. $(#[$attr])* - pub fn $try_name(&self, offset: usize) -> Result<$type_name> { + $vis fn $try_name(&self, offset: usize) -> Result<$type_name> { let addr = self.io_addr::<$type_name>(offset)?; // SAFETY: By the type invariant `addr` is a valid address for MMIO operations. @@ -163,74 +167,97 @@ macro_rules! define_read { } }; } +#[expect(unused)] +pub(crate) use define_read; macro_rules! define_write { - ($(#[$attr:meta])* $name:ident, $try_name:ident, $c_fn:ident <- $type_name:ty) => { + (infallible, $(#[$attr:meta])* $vis:vis $name:ident, $c_fn:ident <- $type_name:ty) => { /// Write IO data from a given offset known at compile time. /// /// Bound checks are performed on compile time, hence if the offset is not known at compile /// time, the build will fail. $(#[$attr])* #[inline] - pub fn $name(&self, value: $type_name, offset: usize) { + $vis fn $name(&self, value: $type_name, offset: usize) { let addr = self.io_addr_assert::<$type_name>(offset); // SAFETY: By the type invariant `addr` is a valid address for MMIO operations. unsafe { bindings::$c_fn(value, addr as *mut c_void) } } + }; + (fallible, $(#[$attr:meta])* $vis:vis $try_name:ident, $c_fn:ident <- $type_name:ty) => { /// Write IO data from a given offset. /// /// Bound checks are performed on runtime, it fails if the offset (plus the type size) is /// out of bounds. $(#[$attr])* - pub fn $try_name(&self, value: $type_name, offset: usize) -> Result { + $vis fn $try_name(&self, value: $type_name, offset: usize) -> Result { let addr = self.io_addr::<$type_name>(offset)?; // SAFETY: By the type invariant `addr` is a valid address for MMIO operations. - unsafe { bindings::$c_fn(value, addr as *mut c_void) } + unsafe { bindings::$c_fn(value, addr as *mut c_void) }; Ok(()) } }; } - -impl Io { - /// Converts an `IoRaw` into an `Io` instance, providing the accessors to the MMIO mapping. - /// - /// # Safety - /// - /// Callers must ensure that `addr` is the start of a valid I/O mapped memory region of size - /// `maxsize`. - pub unsafe fn from_raw(raw: &IoRaw) -> &Self { - // SAFETY: `Io` is a transparent wrapper around `IoRaw`. - unsafe { &*core::ptr::from_ref(raw).cast() } +#[expect(unused)] +pub(crate) use define_write; + +/// Checks whether an access of type `U` at the given `offset` +/// is valid within this region. +#[inline] +const fn offset_valid(offset: usize, size: usize) -> bool { + let type_size = core::mem::size_of::(); + if let Some(end) = offset.checked_add(type_size) { + end <= size && offset % type_size == 0 + } else { + false } +} + +/// Marker trait indicating that an I/O backend supports operations of a certain type. +/// +/// Different I/O backends can implement this trait to expose only the operations they support. +/// +/// For example, a PCI configuration space may implement `IoCapable`, `IoCapable`, +/// and `IoCapable`, but not `IoCapable`, while an MMIO region on a 64-bit +/// system might implement all four. +pub trait IoCapable {} + +/// 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 +/// - 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`] traits +/// are implemented for the type. +/// +/// # 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 { + /// Minimum usable size of this region. + const MIN_SIZE: usize; /// Returns the base address of this mapping. - #[inline] - pub fn addr(&self) -> usize { - self.0.addr() - } + fn addr(&self) -> usize; /// Returns the maximum size of this mapping. - #[inline] - pub fn maxsize(&self) -> usize { - self.0.maxsize() - } - - #[inline] - const fn offset_valid(offset: usize, size: usize) -> bool { - let type_size = core::mem::size_of::(); - if let Some(end) = offset.checked_add(type_size) { - end <= size && offset % type_size == 0 - } else { - false - } - } + fn maxsize(&self) -> usize; + /// Returns the absolute I/O address for a given `offset`, + /// performing runtime bound checks. #[inline] fn io_addr(&self, offset: usize) -> Result { - if !Self::offset_valid::(offset, self.maxsize()) { + if !offset_valid::(offset, self.maxsize()) { return Err(EINVAL); } @@ -239,50 +266,285 @@ impl Io { self.addr().checked_add(offset).ok_or(EINVAL) } + /// Returns the absolute I/O address for a given `offset`, + /// performing compile-time bound checks. #[inline] fn io_addr_assert(&self, offset: usize) -> usize { - build_assert!(Self::offset_valid::(offset, SIZE)); + build_assert!(offset_valid::(offset, Self::MIN_SIZE)); self.addr() + offset } - define_read!(read8, try_read8, readb -> u8); - define_read!(read16, try_read16, readw -> u16); - define_read!(read32, try_read32, readl -> u32); + /// Fallible 8-bit read with runtime bounds check. + #[inline(always)] + fn try_read8(&self, _offset: usize) -> Result + where + Self: IoCapable, + { + build_error!("Backend does not support fallible 8-bit read") + } + + /// Fallible 16-bit read with runtime bounds check. + #[inline(always)] + fn try_read16(&self, _offset: usize) -> Result + where + Self: IoCapable, + { + build_error!("Backend does not support fallible 16-bit read") + } + + /// Fallible 32-bit read with runtime bounds check. + #[inline(always)] + fn try_read32(&self, _offset: usize) -> Result + where + Self: IoCapable, + { + build_error!("Backend does not support fallible 32-bit read") + } + + /// Fallible 64-bit read with runtime bounds check. + #[inline(always)] + fn try_read64(&self, _offset: usize) -> Result + where + Self: IoCapable, + { + build_error!("Backend does not support fallible 64-bit read") + } + + /// Fallible 8-bit write with runtime bounds check. + #[inline(always)] + fn try_write8(&self, _value: u8, _offset: usize) -> Result + where + Self: IoCapable, + { + build_error!("Backend does not support fallible 8-bit write") + } + + /// Fallible 16-bit write with runtime bounds check. + #[inline(always)] + fn try_write16(&self, _value: u16, _offset: usize) -> Result + where + Self: IoCapable, + { + build_error!("Backend does not support fallible 16-bit write") + } + + /// Fallible 32-bit write with runtime bounds check. + #[inline(always)] + fn try_write32(&self, _value: u32, _offset: usize) -> Result + where + Self: IoCapable, + { + build_error!("Backend does not support fallible 32-bit write") + } + + /// Fallible 64-bit write with runtime bounds check. + #[inline(always)] + fn try_write64(&self, _value: u64, _offset: usize) -> Result + where + Self: IoCapable, + { + build_error!("Backend does not support fallible 64-bit write") + } + + /// Infallible 8-bit read with compile-time bounds check. + #[inline(always)] + fn read8(&self, _offset: usize) -> u8 + where + Self: IoKnownSize + IoCapable, + { + build_error!("Backend does not support infallible 8-bit read") + } + + /// Infallible 16-bit read with compile-time bounds check. + #[inline(always)] + fn read16(&self, _offset: usize) -> u16 + where + Self: IoKnownSize + IoCapable, + { + build_error!("Backend does not support infallible 16-bit read") + } + + /// Infallible 32-bit read with compile-time bounds check. + #[inline(always)] + fn read32(&self, _offset: usize) -> u32 + where + Self: IoKnownSize + IoCapable, + { + build_error!("Backend does not support infallible 32-bit read") + } + + /// Infallible 64-bit read with compile-time bounds check. + #[inline(always)] + fn read64(&self, _offset: usize) -> u64 + where + Self: IoKnownSize + IoCapable, + { + build_error!("Backend does not support infallible 64-bit read") + } + + /// Infallible 8-bit write with compile-time bounds check. + #[inline(always)] + fn write8(&self, _value: u8, _offset: usize) + where + Self: IoKnownSize + IoCapable, + { + build_error!("Backend does not support infallible 8-bit write") + } + + /// Infallible 16-bit write with compile-time bounds check. + #[inline(always)] + fn write16(&self, _value: u16, _offset: usize) + where + Self: IoKnownSize + IoCapable, + { + build_error!("Backend does not support infallible 16-bit write") + } + + /// Infallible 32-bit write with compile-time bounds check. + #[inline(always)] + fn write32(&self, _value: u32, _offset: usize) + where + Self: IoKnownSize + IoCapable, + { + build_error!("Backend does not support infallible 32-bit write") + } + + /// Infallible 64-bit write with compile-time bounds check. + #[inline(always)] + fn write64(&self, _value: u64, _offset: usize) + where + Self: IoKnownSize + IoCapable, + { + build_error!("Backend does not support infallible 64-bit write") + } +} + +/// Marker trait for types with a known size at compile time. +/// +/// This trait is implemented by I/O backends that have a compile-time known size, +/// enabling the use of infallible I/O accessors with compile-time bounds checking. +/// +/// Types implementing this trait can use the infallible methods in [`Io`] trait +/// (e.g., `read8`, `write32`), which require `Self: IoKnownSize` bound. +pub trait IoKnownSize: Io {} + +// MMIO regions support 8, 16, and 32-bit accesses. +impl IoCapable for Mmio {} +impl IoCapable for Mmio {} +impl IoCapable for Mmio {} + +// MMIO regions on 64-bit systems also support 64-bit accesses. +#[cfg(CONFIG_64BIT)] +impl IoCapable for Mmio {} + +impl Io for Mmio { + const MIN_SIZE: usize = 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.maxsize() + } + + define_read!(fallible, try_read8, readb -> u8); + define_read!(fallible, try_read16, readw -> u16); + define_read!(fallible, try_read32, readl -> u32); define_read!( + fallible, #[cfg(CONFIG_64BIT)] - read64, try_read64, readq -> u64 ); - define_read!(read8_relaxed, try_read8_relaxed, readb_relaxed -> u8); - define_read!(read16_relaxed, try_read16_relaxed, readw_relaxed -> u16); - define_read!(read32_relaxed, try_read32_relaxed, readl_relaxed -> u32); + define_write!(fallible, try_write8, writeb <- u8); + define_write!(fallible, try_write16, writew <- u16); + define_write!(fallible, try_write32, writel <- u32); + define_write!( + fallible, + #[cfg(CONFIG_64BIT)] + try_write64, + writeq <- u64 + ); + + define_read!(infallible, read8, readb -> u8); + define_read!(infallible, read16, readw -> u16); + define_read!(infallible, read32, readl -> u32); define_read!( + infallible, #[cfg(CONFIG_64BIT)] - read64_relaxed, - try_read64_relaxed, - readq_relaxed -> u64 + read64, + readq -> u64 ); - define_write!(write8, try_write8, writeb <- u8); - define_write!(write16, try_write16, writew <- u16); - define_write!(write32, try_write32, writel <- u32); + define_write!(infallible, write8, writeb <- u8); + define_write!(infallible, write16, writew <- u16); + define_write!(infallible, write32, writel <- u32); define_write!( + infallible, #[cfg(CONFIG_64BIT)] write64, - try_write64, writeq <- u64 ); +} + +impl IoKnownSize for Mmio {} + +impl Mmio { + /// Converts an `MmioRaw` into an `Mmio` instance, providing the accessors to the MMIO mapping. + /// + /// # Safety + /// + /// Callers must ensure that `addr` is the start of a valid I/O mapped memory region of size + /// `maxsize`. + pub unsafe fn from_raw(raw: &MmioRaw) -> &Self { + // SAFETY: `Mmio` is a transparent wrapper around `MmioRaw`. + unsafe { &*core::ptr::from_ref(raw).cast() } + } + + define_read!(infallible, pub read8_relaxed, readb_relaxed -> u8); + define_read!(infallible, pub read16_relaxed, readw_relaxed -> u16); + define_read!(infallible, pub read32_relaxed, readl_relaxed -> u32); + define_read!( + infallible, + #[cfg(CONFIG_64BIT)] + pub read64_relaxed, + readq_relaxed -> u64 + ); + + define_read!(fallible, pub try_read8_relaxed, readb_relaxed -> u8); + define_read!(fallible, pub try_read16_relaxed, readw_relaxed -> u16); + define_read!(fallible, pub try_read32_relaxed, readl_relaxed -> u32); + define_read!( + fallible, + #[cfg(CONFIG_64BIT)] + pub try_read64_relaxed, + readq_relaxed -> u64 + ); + + define_write!(infallible, pub write8_relaxed, writeb_relaxed <- u8); + define_write!(infallible, pub write16_relaxed, writew_relaxed <- u16); + define_write!(infallible, pub write32_relaxed, writel_relaxed <- u32); + define_write!( + infallible, + #[cfg(CONFIG_64BIT)] + pub write64_relaxed, + writeq_relaxed <- u64 + ); - define_write!(write8_relaxed, try_write8_relaxed, writeb_relaxed <- u8); - define_write!(write16_relaxed, try_write16_relaxed, writew_relaxed <- u16); - define_write!(write32_relaxed, try_write32_relaxed, writel_relaxed <- u32); + define_write!(fallible, pub try_write8_relaxed, writeb_relaxed <- u8); + define_write!(fallible, pub try_write16_relaxed, writew_relaxed <- u16); + define_write!(fallible, pub try_write32_relaxed, writel_relaxed <- u32); define_write!( + fallible, #[cfg(CONFIG_64BIT)] - write64_relaxed, - try_write64_relaxed, + pub try_write64_relaxed, writeq_relaxed <- u64 ); } diff --git a/rust/kernel/io/mem.rs b/rust/kernel/io/mem.rs index e4878c131c6d..620022cff401 100644 --- a/rust/kernel/io/mem.rs +++ b/rust/kernel/io/mem.rs @@ -16,8 +16,8 @@ use crate::{ Region, Resource, // }, - Io, - IoRaw, // + Mmio, + MmioRaw, // }, prelude::*, }; @@ -212,7 +212,7 @@ impl ExclusiveIoMem { } impl Deref for ExclusiveIoMem { - type Target = Io; + type Target = Mmio; fn deref(&self) -> &Self::Target { &self.iomem @@ -226,10 +226,10 @@ impl Deref for ExclusiveIoMem { /// /// # Invariants /// -/// [`IoMem`] always holds an [`IoRaw`] instance that holds a valid pointer to the +/// [`IoMem`] always holds an [`MmioRaw`] instance that holds a valid pointer to the /// start of the I/O memory mapped region. pub struct IoMem { - io: IoRaw, + io: MmioRaw, } impl IoMem { @@ -264,7 +264,7 @@ impl IoMem { return Err(ENOMEM); } - let io = IoRaw::new(addr as usize, size)?; + let io = MmioRaw::new(addr as usize, size)?; let io = IoMem { io }; Ok(io) @@ -287,10 +287,10 @@ impl Drop for IoMem { } impl Deref for IoMem { - type Target = Io; + type Target = Mmio; fn deref(&self) -> &Self::Target { // SAFETY: Safe as by the invariant of `IoMem`. - unsafe { Io::from_raw(&self.io) } + unsafe { Mmio::from_raw(&self.io) } } } diff --git a/rust/kernel/io/poll.rs b/rust/kernel/io/poll.rs index b1a2570364f4..75d1b3e8596c 100644 --- a/rust/kernel/io/poll.rs +++ b/rust/kernel/io/poll.rs @@ -45,12 +45,16 @@ use crate::{ /// # Examples /// /// ```no_run -/// use kernel::io::{Io, poll::read_poll_timeout}; +/// use kernel::io::{ +/// Io, +/// Mmio, +/// poll::read_poll_timeout, // +/// }; /// use kernel::time::Delta; /// /// const HW_READY: u16 = 0x01; /// -/// fn wait_for_hardware(io: &Io) -> Result { +/// fn wait_for_hardware(io: &Mmio) -> Result { /// read_poll_timeout( /// // The `op` closure reads the value of a specific status register. /// || io.try_read16(0x1000), @@ -128,12 +132,16 @@ where /// # Examples /// /// ```no_run -/// use kernel::io::{poll::read_poll_timeout_atomic, Io}; +/// use kernel::io::{ +/// Io, +/// Mmio, +/// poll::read_poll_timeout_atomic, // +/// }; /// use kernel::time::Delta; /// /// const HW_READY: u16 = 0x01; /// -/// fn wait_for_hardware(io: &Io) -> Result { +/// fn wait_for_hardware(io: &Mmio) -> Result { /// read_poll_timeout_atomic( /// // The `op` closure reads the value of a specific status register. /// || io.try_read16(0x1000), diff --git a/rust/kernel/pci/io.rs b/rust/kernel/pci/io.rs index 70e3854e7d8d..e3377397666e 100644 --- a/rust/kernel/pci/io.rs +++ b/rust/kernel/pci/io.rs @@ -8,8 +8,8 @@ use crate::{ device, devres::Devres, io::{ - Io, - IoRaw, // + Mmio, + MmioRaw, // }, prelude::*, sync::aref::ARef, // @@ -27,7 +27,7 @@ use core::ops::Deref; /// memory mapped PCI BAR and its size. pub struct Bar { pdev: ARef, - io: IoRaw, + io: MmioRaw, num: i32, } @@ -63,7 +63,7 @@ impl Bar { return Err(ENOMEM); } - let io = match IoRaw::new(ioptr, len as usize) { + let io = match MmioRaw::new(ioptr, len as usize) { Ok(io) => io, Err(err) => { // SAFETY: @@ -117,11 +117,11 @@ impl Drop for Bar { } impl Deref for Bar { - type Target = Io; + type Target = Mmio; fn deref(&self) -> &Self::Target { // SAFETY: By the type invariant of `Self`, the MMIO range in `self.io` is properly mapped. - unsafe { Io::from_raw(&self.io) } + unsafe { Mmio::from_raw(&self.io) } } } -- cgit v1.2.3 From 5981d03c27a14df1c03e10570eeb1ab26e9709f7 Mon Sep 17 00:00:00 2001 From: Zhi Wang Date: Wed, 21 Jan 2026 22:22:09 +0200 Subject: rust: io: factor out MMIO read/write macros Refactor the existing MMIO accessors to use common call macros instead of inlining the bindings calls in each `define_{read,write}!` expansion. This factoring separates the common offset/bounds checks from the low-level call pattern, making it easier to add additional I/O accessor families. No functional change intended. Cc: Alexandre Courbot Signed-off-by: Zhi Wang Reviewed-by: Alexandre Courbot Link: https://patch.msgid.link/20260121202212.4438-4-zhiw@nvidia.com Signed-off-by: Danilo Krummrich --- rust/kernel/io.rs | 147 +++++++++++++++++++++++++++++++++++++----------------- 1 file changed, 102 insertions(+), 45 deletions(-) (limited to 'rust/kernel') diff --git a/rust/kernel/io.rs b/rust/kernel/io.rs index 53179eb839b1..87719e58548a 100644 --- a/rust/kernel/io.rs +++ b/rust/kernel/io.rs @@ -137,8 +137,65 @@ impl MmioRaw { #[repr(transparent)] pub struct Mmio(MmioRaw); +/// Internal helper macros used to invoke C MMIO read functions. +/// +/// This macro is intended to be used by higher-level MMIO access macros (define_read) and provides +/// a unified expansion for infallible vs. fallible read semantics. It emits a direct call into the +/// corresponding C helper and performs the required cast to the Rust return type. +/// +/// # Parameters +/// +/// * `$c_fn` – The C function performing the MMIO read. +/// * `$self` – The I/O backend object. +/// * `$ty` – The type of the value to be read. +/// * `$addr` – The MMIO address to read. +/// +/// This macro does not perform any validation; all invariants must be upheld by the higher-level +/// abstraction invoking it. +macro_rules! call_mmio_read { + (infallible, $c_fn:ident, $self:ident, $type:ty, $addr:expr) => { + // SAFETY: By the type invariant `addr` is a valid address for MMIO operations. + unsafe { bindings::$c_fn($addr as *const c_void) as $type } + }; + + (fallible, $c_fn:ident, $self:ident, $type:ty, $addr:expr) => {{ + // SAFETY: By the type invariant `addr` is a valid address for MMIO operations. + Ok(unsafe { bindings::$c_fn($addr as *const c_void) as $type }) + }}; +} + +/// Internal helper macros used to invoke C MMIO write functions. +/// +/// This macro is intended to be used by higher-level MMIO access macros (define_write) and provides +/// a unified expansion for infallible vs. fallible write semantics. It emits a direct call into the +/// corresponding C helper and performs the required cast to the Rust return type. +/// +/// # Parameters +/// +/// * `$c_fn` – The C function performing the MMIO write. +/// * `$self` – The I/O backend object. +/// * `$ty` – The type of the written value. +/// * `$addr` – The MMIO address to write. +/// * `$value` – The value to write. +/// +/// This macro does not perform any validation; all invariants must be upheld by the higher-level +/// abstraction invoking it. +macro_rules! call_mmio_write { + (infallible, $c_fn:ident, $self:ident, $ty:ty, $addr:expr, $value:expr) => { + // SAFETY: By the type invariant `addr` is a valid address for MMIO operations. + unsafe { bindings::$c_fn($value, $addr as *mut c_void) } + }; + + (fallible, $c_fn:ident, $self:ident, $ty:ty, $addr:expr, $value:expr) => {{ + // SAFETY: By the type invariant `addr` is a valid address for MMIO operations. + unsafe { bindings::$c_fn($value, $addr as *mut c_void) }; + Ok(()) + }}; +} + macro_rules! define_read { - (infallible, $(#[$attr:meta])* $vis:vis $name:ident, $c_fn:ident -> $type_name:ty) => { + (infallible, $(#[$attr:meta])* $vis:vis $name:ident, $call_macro:ident($c_fn:ident) -> + $type_name:ty) => { /// Read IO data from a given offset known at compile time. /// /// Bound checks are performed on compile time, hence if the offset is not known at compile @@ -148,12 +205,13 @@ macro_rules! define_read { $vis fn $name(&self, offset: usize) -> $type_name { let addr = self.io_addr_assert::<$type_name>(offset); - // SAFETY: By the type invariant `addr` is a valid address for MMIO operations. - unsafe { bindings::$c_fn(addr as *const c_void) } + // SAFETY: By the type invariant `addr` is a valid address for IO operations. + $call_macro!(infallible, $c_fn, self, $type_name, addr) } }; - (fallible, $(#[$attr:meta])* $vis:vis $try_name:ident, $c_fn:ident -> $type_name:ty) => { + (fallible, $(#[$attr:meta])* $vis:vis $try_name:ident, $call_macro:ident($c_fn:ident) -> + $type_name:ty) => { /// Read IO data from a given offset. /// /// Bound checks are performed on runtime, it fails if the offset (plus the type size) is @@ -162,8 +220,8 @@ macro_rules! define_read { $vis fn $try_name(&self, offset: usize) -> Result<$type_name> { let addr = self.io_addr::<$type_name>(offset)?; - // SAFETY: By the type invariant `addr` is a valid address for MMIO operations. - Ok(unsafe { bindings::$c_fn(addr as *const c_void) }) + // SAFETY: By the type invariant `addr` is a valid address for IO operations. + $call_macro!(fallible, $c_fn, self, $type_name, addr) } }; } @@ -171,7 +229,8 @@ macro_rules! define_read { pub(crate) use define_read; macro_rules! define_write { - (infallible, $(#[$attr:meta])* $vis:vis $name:ident, $c_fn:ident <- $type_name:ty) => { + (infallible, $(#[$attr:meta])* $vis:vis $name:ident, $call_macro:ident($c_fn:ident) <- + $type_name:ty) => { /// Write IO data from a given offset known at compile time. /// /// Bound checks are performed on compile time, hence if the offset is not known at compile @@ -181,12 +240,12 @@ macro_rules! define_write { $vis fn $name(&self, value: $type_name, offset: usize) { let addr = self.io_addr_assert::<$type_name>(offset); - // SAFETY: By the type invariant `addr` is a valid address for MMIO operations. - unsafe { bindings::$c_fn(value, addr as *mut c_void) } + $call_macro!(infallible, $c_fn, self, $type_name, addr, value); } }; - (fallible, $(#[$attr:meta])* $vis:vis $try_name:ident, $c_fn:ident <- $type_name:ty) => { + (fallible, $(#[$attr:meta])* $vis:vis $try_name:ident, $call_macro:ident($c_fn:ident) <- + $type_name:ty) => { /// Write IO data from a given offset. /// /// Bound checks are performed on runtime, it fails if the offset (plus the type size) is @@ -195,9 +254,7 @@ macro_rules! define_write { $vis fn $try_name(&self, value: $type_name, offset: usize) -> Result { let addr = self.io_addr::<$type_name>(offset)?; - // SAFETY: By the type invariant `addr` is a valid address for MMIO operations. - unsafe { bindings::$c_fn(value, addr as *mut c_void) }; - Ok(()) + $call_macro!(fallible, $c_fn, self, $type_name, addr, value) } }; } @@ -453,44 +510,44 @@ impl Io for Mmio { self.0.maxsize() } - define_read!(fallible, try_read8, readb -> u8); - define_read!(fallible, try_read16, readw -> u16); - define_read!(fallible, try_read32, readl -> u32); + define_read!(fallible, try_read8, call_mmio_read(readb) -> u8); + define_read!(fallible, try_read16, call_mmio_read(readw) -> u16); + define_read!(fallible, try_read32, call_mmio_read(readl) -> u32); define_read!( fallible, #[cfg(CONFIG_64BIT)] try_read64, - readq -> u64 + call_mmio_read(readq) -> u64 ); - define_write!(fallible, try_write8, writeb <- u8); - define_write!(fallible, try_write16, writew <- u16); - define_write!(fallible, try_write32, writel <- u32); + define_write!(fallible, try_write8, call_mmio_write(writeb) <- u8); + define_write!(fallible, try_write16, call_mmio_write(writew) <- u16); + define_write!(fallible, try_write32, call_mmio_write(writel) <- u32); define_write!( fallible, #[cfg(CONFIG_64BIT)] try_write64, - writeq <- u64 + call_mmio_write(writeq) <- u64 ); - define_read!(infallible, read8, readb -> u8); - define_read!(infallible, read16, readw -> u16); - define_read!(infallible, read32, readl -> u32); + define_read!(infallible, read8, call_mmio_read(readb) -> u8); + define_read!(infallible, read16, call_mmio_read(readw) -> u16); + define_read!(infallible, read32, call_mmio_read(readl) -> u32); define_read!( infallible, #[cfg(CONFIG_64BIT)] read64, - readq -> u64 + call_mmio_read(readq) -> u64 ); - define_write!(infallible, write8, writeb <- u8); - define_write!(infallible, write16, writew <- u16); - define_write!(infallible, write32, writel <- u32); + define_write!(infallible, write8, call_mmio_write(writeb) <- u8); + define_write!(infallible, write16, call_mmio_write(writew) <- u16); + define_write!(infallible, write32, call_mmio_write(writel) <- u32); define_write!( infallible, #[cfg(CONFIG_64BIT)] write64, - writeq <- u64 + call_mmio_write(writeq) <- u64 ); } @@ -508,43 +565,43 @@ impl Mmio { unsafe { &*core::ptr::from_ref(raw).cast() } } - define_read!(infallible, pub read8_relaxed, readb_relaxed -> u8); - define_read!(infallible, pub read16_relaxed, readw_relaxed -> u16); - define_read!(infallible, pub read32_relaxed, readl_relaxed -> u32); + define_read!(infallible, pub read8_relaxed, call_mmio_read(readb_relaxed) -> u8); + define_read!(infallible, pub read16_relaxed, call_mmio_read(readw_relaxed) -> u16); + define_read!(infallible, pub read32_relaxed, call_mmio_read(readl_relaxed) -> u32); define_read!( infallible, #[cfg(CONFIG_64BIT)] pub read64_relaxed, - readq_relaxed -> u64 + call_mmio_read(readq_relaxed) -> u64 ); - define_read!(fallible, pub try_read8_relaxed, readb_relaxed -> u8); - define_read!(fallible, pub try_read16_relaxed, readw_relaxed -> u16); - define_read!(fallible, pub try_read32_relaxed, readl_relaxed -> u32); + define_read!(fallible, pub try_read8_relaxed, call_mmio_read(readb_relaxed) -> u8); + define_read!(fallible, pub try_read16_relaxed, call_mmio_read(readw_relaxed) -> u16); + define_read!(fallible, pub try_read32_relaxed, call_mmio_read(readl_relaxed) -> u32); define_read!( fallible, #[cfg(CONFIG_64BIT)] pub try_read64_relaxed, - readq_relaxed -> u64 + call_mmio_read(readq_relaxed) -> u64 ); - define_write!(infallible, pub write8_relaxed, writeb_relaxed <- u8); - define_write!(infallible, pub write16_relaxed, writew_relaxed <- u16); - define_write!(infallible, pub write32_relaxed, writel_relaxed <- u32); + define_write!(infallible, pub write8_relaxed, call_mmio_write(writeb_relaxed) <- u8); + define_write!(infallible, pub write16_relaxed, call_mmio_write(writew_relaxed) <- u16); + define_write!(infallible, pub write32_relaxed, call_mmio_write(writel_relaxed) <- u32); define_write!( infallible, #[cfg(CONFIG_64BIT)] pub write64_relaxed, - writeq_relaxed <- u64 + call_mmio_write(writeq_relaxed) <- u64 ); - define_write!(fallible, pub try_write8_relaxed, writeb_relaxed <- u8); - define_write!(fallible, pub try_write16_relaxed, writew_relaxed <- u16); - define_write!(fallible, pub try_write32_relaxed, writel_relaxed <- u32); + define_write!(fallible, pub try_write8_relaxed, call_mmio_write(writeb_relaxed) <- u8); + define_write!(fallible, pub try_write16_relaxed, call_mmio_write(writew_relaxed) <- u16); + define_write!(fallible, pub try_write32_relaxed, call_mmio_write(writel_relaxed) <- u32); define_write!( fallible, #[cfg(CONFIG_64BIT)] pub try_write64_relaxed, - writeq_relaxed <- u64 + call_mmio_write(writeq_relaxed) <- u64 ); } -- cgit v1.2.3 From 4dc0bacb1d3c4722cbd002c4aab6bd458d30d869 Mon Sep 17 00:00:00 2001 From: Zhi Wang Date: Wed, 21 Jan 2026 22:22:10 +0200 Subject: rust: pci: add config space read/write support Drivers might need to access PCI config space for querying capability structures and access the registers inside the structures. For Rust drivers need to access PCI config space, the Rust PCI abstraction needs to support it in a way that upholds Rust's safety principles. Introduce a `ConfigSpace` wrapper in Rust PCI abstraction to provide safe accessors for PCI config space. The new type implements the `Io` trait and `IoCapable` for u8, u16, and u32 to share offset validation and bound-checking logic with other I/O backends. The `ConfigSpace` type uses marker types (`Normal` and `Extended`) to represent configuration space sizes at the type level. Cc: Alexandre Courbot Cc: Danilo Krummrich Cc: Gary Guo Cc: Joel Fernandes Signed-off-by: Zhi Wang Reviewed-by: Gary Guo Link: https://lore.kernel.org/all/DFV4IJDQC2J6.1Q91JOAL6CJSG@kernel.org/ [1] Link: https://patch.msgid.link/20260121202212.4438-5-zhiw@nvidia.com [ Applied the diff from [1], considering subsequent comment; remove #[expect(unused)] from define_{read,write}!(). - Danilo ] Signed-off-by: Danilo Krummrich --- rust/kernel/io.rs | 2 - rust/kernel/pci.rs | 8 +- rust/kernel/pci/io.rs | 198 +++++++++++++++++++++++++++++++++++++++++++++++++- 3 files changed, 204 insertions(+), 4 deletions(-) (limited to 'rust/kernel') diff --git a/rust/kernel/io.rs b/rust/kernel/io.rs index 87719e58548a..fcd1b156a14a 100644 --- a/rust/kernel/io.rs +++ b/rust/kernel/io.rs @@ -225,7 +225,6 @@ macro_rules! define_read { } }; } -#[expect(unused)] pub(crate) use define_read; macro_rules! define_write { @@ -258,7 +257,6 @@ macro_rules! define_write { } }; } -#[expect(unused)] pub(crate) use define_write; /// Checks whether an access of type `U` at the given `offset` diff --git a/rust/kernel/pci.rs b/rust/kernel/pci.rs index 82e128431f08..1d1a253e5d5d 100644 --- a/rust/kernel/pci.rs +++ b/rust/kernel/pci.rs @@ -40,7 +40,13 @@ pub use self::id::{ ClassMask, Vendor, // }; -pub use self::io::Bar; +pub use self::io::{ + Bar, + ConfigSpaceKind, + ConfigSpaceSize, + Extended, + Normal, // +}; pub use self::irq::{ IrqType, IrqTypes, diff --git a/rust/kernel/pci/io.rs b/rust/kernel/pci/io.rs index e3377397666e..026e7a3b69bd 100644 --- a/rust/kernel/pci/io.rs +++ b/rust/kernel/pci/io.rs @@ -8,13 +8,174 @@ use crate::{ device, devres::Devres, io::{ + define_read, + define_write, + Io, + IoCapable, + IoKnownSize, Mmio, MmioRaw, // }, prelude::*, sync::aref::ARef, // }; -use core::ops::Deref; +use core::{ + marker::PhantomData, + ops::Deref, // +}; + +/// Represents the size of a PCI configuration space. +/// +/// PCI devices can have either a *normal* (legacy) configuration space of 256 bytes, +/// or an *extended* configuration space of 4096 bytes as defined in the PCI Express +/// specification. +#[repr(usize)] +#[derive(Eq, PartialEq)] +pub enum ConfigSpaceSize { + /// 256-byte legacy PCI configuration space. + Normal = 256, + + /// 4096-byte PCIe extended configuration space. + Extended = 4096, +} + +impl ConfigSpaceSize { + /// Get the raw value of this enum. + #[inline(always)] + pub const fn into_raw(self) -> usize { + // CAST: PCI configuration space size is at most 4096 bytes, so the value always fits + // within `usize` without truncation or sign change. + self as usize + } +} + +/// Marker type for normal (256-byte) PCI configuration space. +pub struct Normal; + +/// Marker type for extended (4096-byte) PCIe configuration space. +pub struct Extended; + +/// Trait for PCI configuration space size markers. +/// +/// This trait is implemented by [`Normal`] and [`Extended`] to provide +/// compile-time knowledge of the configuration space size. +pub trait ConfigSpaceKind { + /// The size of this configuration space in bytes. + const SIZE: usize; +} + +impl ConfigSpaceKind for Normal { + const SIZE: usize = 256; +} + +impl ConfigSpaceKind for Extended { + const SIZE: usize = 4096; +} + +/// The PCI configuration space of a device. +/// +/// Provides typed read and write accessors for configuration registers +/// using the standard `pci_read_config_*` and `pci_write_config_*` helpers. +/// +/// The generic parameter `S` indicates the maximum size of the configuration space. +/// Use [`Normal`] for 256-byte legacy configuration space or [`Extended`] for +/// 4096-byte PCIe extended configuration space (default). +pub struct ConfigSpace<'a, S: ConfigSpaceKind = Extended> { + pub(crate) pdev: &'a Device, + _marker: PhantomData, +} + +/// Internal helper macros used to invoke C PCI configuration space read functions. +/// +/// This macro is intended to be used by higher-level PCI configuration space access macros +/// (define_read) and provides a unified expansion for infallible vs. fallible read semantics. It +/// emits a direct call into the corresponding C helper and performs the required cast to the Rust +/// return type. +/// +/// # Parameters +/// +/// * `$c_fn` – The C function performing the PCI configuration space write. +/// * `$self` – The I/O backend object. +/// * `$ty` – The type of the value to read. +/// * `$addr` – The PCI configuration space offset to read. +/// +/// This macro does not perform any validation; all invariants must be upheld by the higher-level +/// abstraction invoking it. +macro_rules! call_config_read { + (infallible, $c_fn:ident, $self:ident, $ty:ty, $addr:expr) => {{ + let mut val: $ty = 0; + // 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. + // Return value from C function is ignored in infallible accessors. + let _ret = unsafe { bindings::$c_fn($self.pdev.as_raw(), $addr as i32, &mut val) }; + val + }}; +} + +/// Internal helper macros used to invoke C PCI configuration space write functions. +/// +/// This macro is intended to be used by higher-level PCI configuration space access macros +/// (define_write) and provides a unified expansion for infallible vs. fallible read semantics. It +/// emits a direct call into the corresponding C helper and performs the required cast to the Rust +/// return type. +/// +/// # Parameters +/// +/// * `$c_fn` – The C function performing the PCI configuration space write. +/// * `$self` – The I/O backend object. +/// * `$ty` – The type of the written value. +/// * `$addr` – The configuration space offset to write. +/// * `$value` – The value to write. +/// +/// This macro does not perform any validation; all invariants must be upheld by the higher-level +/// abstraction invoking it. +macro_rules! call_config_write { + (infallible, $c_fn:ident, $self:ident, $ty:ty, $addr:expr, $value:expr) => { + // 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. + // Return value from C function is ignored in infallible accessors. + let _ret = unsafe { bindings::$c_fn($self.pdev.as_raw(), $addr as i32, $value) }; + }; +} + +// PCI configuration space supports 8, 16, and 32-bit accesses. +impl<'a, S: ConfigSpaceKind> IoCapable for ConfigSpace<'a, S> {} +impl<'a, S: ConfigSpaceKind> IoCapable for ConfigSpace<'a, S> {} +impl<'a, S: ConfigSpaceKind> IoCapable for ConfigSpace<'a, S> {} + +impl<'a, S: ConfigSpaceKind> Io for ConfigSpace<'a, S> { + const MIN_SIZE: usize = S::SIZE; + + /// Returns the base address of the I/O region. It is always 0 for configuration space. + #[inline] + fn addr(&self) -> usize { + 0 + } + + /// Returns the maximum size of the configuration space. + #[inline] + fn maxsize(&self) -> usize { + self.pdev.cfg_size().into_raw() + } + + // PCI configuration space does not support fallible operations. + // The default implementations from the Io trait are not used. + + define_read!(infallible, read8, call_config_read(pci_read_config_byte) -> u8); + define_read!(infallible, read16, call_config_read(pci_read_config_word) -> u16); + define_read!(infallible, read32, call_config_read(pci_read_config_dword) -> u32); + + define_write!(infallible, write8, call_config_write(pci_write_config_byte) <- u8); + define_write!(infallible, write16, call_config_write(pci_write_config_word) <- u16); + define_write!(infallible, write32, call_config_write(pci_write_config_dword) <- u32); +} + +/// Marker trait indicating ConfigSpace has a known size at compile time. +impl<'a, S: ConfigSpaceKind> IoKnownSize for ConfigSpace<'a, S> {} /// A PCI BAR to perform I/O-Operations on. /// @@ -144,4 +305,39 @@ impl Device { ) -> impl PinInit, Error> + 'a { self.iomap_region_sized::<0>(bar, name) } + + /// Returns the size of configuration space. + pub fn cfg_size(&self) -> ConfigSpaceSize { + // SAFETY: `self.as_raw` is a valid pointer to a `struct pci_dev`. + let size = unsafe { (*self.as_raw()).cfg_size }; + match size { + 256 => ConfigSpaceSize::Normal, + 4096 => ConfigSpaceSize::Extended, + _ => { + // PANIC: The PCI subsystem only ever reports the configuration space size as either + // `ConfigSpaceSize::Normal` or `ConfigSpaceSize::Extended`. + unreachable!(); + } + } + } + + /// Return an initialized normal (256-byte) config space object. + pub fn config_space<'a>(&'a self) -> ConfigSpace<'a, Normal> { + ConfigSpace { + pdev: self, + _marker: PhantomData, + } + } + + /// Return an initialized extended (4096-byte) config space object. + pub fn config_space_extended<'a>(&'a self) -> Result> { + if self.cfg_size() != ConfigSpaceSize::Extended { + return Err(EINVAL); + } + + Ok(ConfigSpace { + pdev: self, + _marker: PhantomData, + }) + } } -- cgit v1.2.3 From a38cd1fea98990e20021823cea251e6cb088eeab Mon Sep 17 00:00:00 2001 From: Gary Guo Date: Fri, 23 Jan 2026 17:58:38 +0000 Subject: rust: device: support `dev_printk` on all devices Currently, `dev_*` only works on the core `Device`, but not on any other bus or class device objects. This causes a pattern of `dev_info!(pdev.as_ref())` which is not ideal. This adds support of using these devices directly with `dev_*` macros, by adding `AsRef` call inside the macro. To make sure we can still use just `kernel::device::Device`, as `AsRef` implementation is added for it; this is typical for types that is designed to use with `AsRef` anyway, for example, `str` implements `AsRef` and `Path` implements `AsRef`. Signed-off-by: Gary Guo Link: https://patch.msgid.link/20260123175854.176735-1-gary@kernel.org Signed-off-by: Danilo Krummrich --- rust/kernel/device.rs | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) (limited to 'rust/kernel') diff --git a/rust/kernel/device.rs b/rust/kernel/device.rs index cb2348934676..a9cbed2e204c 100644 --- a/rust/kernel/device.rs +++ b/rust/kernel/device.rs @@ -599,6 +599,13 @@ impl DeviceContext for Core {} impl DeviceContext for CoreInternal {} impl DeviceContext for Normal {} +impl AsRef> for Device { + #[inline] + fn as_ref(&self) -> &Device { + self + } +} + /// Convert device references to bus device references. /// /// Bus devices can implement this trait to allow abstractions to provide the bus device in @@ -718,7 +725,7 @@ macro_rules! impl_device_context_into_aref { macro_rules! dev_printk { ($method:ident, $dev:expr, $($f:tt)*) => { { - ($dev).$method($crate::prelude::fmt!($($f)*)); + $crate::device::Device::$method($dev.as_ref(), $crate::prelude::fmt!($($f)*)) } } } -- cgit v1.2.3 From 600de1c008b2302b56d69ff27d12a9d8d14892ac Mon Sep 17 00:00:00 2001 From: Gary Guo Date: Fri, 23 Jan 2026 17:58:39 +0000 Subject: rust: pci: remove redundant `.as_ref()` for `dev_*` print This is now handled by the macro itself. Signed-off-by: Gary Guo Link: https://patch.msgid.link/20260123175854.176735-2-gary@kernel.org Signed-off-by: Danilo Krummrich --- rust/kernel/pci.rs | 2 +- rust/kernel/pci/id.rs | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) (limited to 'rust/kernel') diff --git a/rust/kernel/pci.rs b/rust/kernel/pci.rs index 1d1a253e5d5d..cd46ac12812c 100644 --- a/rust/kernel/pci.rs +++ b/rust/kernel/pci.rs @@ -348,7 +348,7 @@ impl Device { /// // Get an instance of `Vendor`. /// let vendor = pdev.vendor_id(); /// dev_info!( - /// pdev.as_ref(), + /// pdev, /// "Device: Vendor={}, Device=0x{:x}\n", /// vendor, /// pdev.device_id() diff --git a/rust/kernel/pci/id.rs b/rust/kernel/pci/id.rs index c09125946d9e..e2d9e8804347 100644 --- a/rust/kernel/pci/id.rs +++ b/rust/kernel/pci/id.rs @@ -22,7 +22,7 @@ use crate::{ /// fn probe_device(pdev: &pci::Device) -> Result { /// let pci_class = pdev.pci_class(); /// dev_info!( -/// pdev.as_ref(), +/// pdev, /// "Detected PCI class: {}\n", /// pci_class /// ); -- cgit v1.2.3 From ae3bf7612220ac8a8020a285142e0d918543a408 Mon Sep 17 00:00:00 2001 From: Ke Sun Date: Tue, 20 Jan 2026 16:38:20 +0800 Subject: rust: debugfs: use pin_init::zeroed() for file_operations Replace unsafe core::mem::zeroed() with pin_init::zeroed() for file_operations initialization in all debugfs file operation implementations. Suggested-by: Benno Lossin Signed-off-by: Ke Sun Link: https://github.com/Rust-for-Linux/linux/issues/1189 Link: https://patch.msgid.link/20260120083824.477339-5-sunke@kylinos.cn Signed-off-by: Danilo Krummrich --- rust/kernel/debugfs/file_ops.rs | 18 ++++++------------ 1 file changed, 6 insertions(+), 12 deletions(-) (limited to 'rust/kernel') diff --git a/rust/kernel/debugfs/file_ops.rs b/rust/kernel/debugfs/file_ops.rs index ad19360540ba..f15908f71c4a 100644 --- a/rust/kernel/debugfs/file_ops.rs +++ b/rust/kernel/debugfs/file_ops.rs @@ -135,8 +135,7 @@ impl ReadFile for T { llseek: Some(bindings::seq_lseek), release: Some(bindings::single_release), open: Some(writer_open::), - // SAFETY: `file_operations` supports zeroes in all fields. - ..unsafe { core::mem::zeroed() } + ..pin_init::zeroed() }; // SAFETY: `operations` is all stock `seq_file` implementations except for `writer_open`. // `open`'s only requirement beyond what is provided to all open functions is that the @@ -188,8 +187,7 @@ impl ReadWriteFile for T { write: Some(write::), llseek: Some(bindings::seq_lseek), release: Some(bindings::single_release), - // SAFETY: `file_operations` supports zeroes in all fields. - ..unsafe { core::mem::zeroed() } + ..pin_init::zeroed() }; // SAFETY: `operations` is all stock `seq_file` implementations except for `writer_open` // and `write`. @@ -244,8 +242,7 @@ impl WriteFile for T { open: Some(write_only_open), write: Some(write_only_write::), llseek: Some(bindings::noop_llseek), - // SAFETY: `file_operations` supports zeroes in all fields. - ..unsafe { core::mem::zeroed() } + ..pin_init::zeroed() }; // SAFETY: // * `write_only_open` populates the file private data with the inode private data @@ -297,8 +294,7 @@ impl BinaryReadFile for T { read: Some(blob_read::), llseek: Some(bindings::default_llseek), open: Some(bindings::simple_open), - // SAFETY: `file_operations` supports zeroes in all fields. - ..unsafe { core::mem::zeroed() } + ..pin_init::zeroed() }; // SAFETY: @@ -352,8 +348,7 @@ impl BinaryWriteFile for T { write: Some(blob_write::), llseek: Some(bindings::default_llseek), open: Some(bindings::simple_open), - // SAFETY: `file_operations` supports zeroes in all fields. - ..unsafe { core::mem::zeroed() } + ..pin_init::zeroed() }; // SAFETY: @@ -378,8 +373,7 @@ impl BinaryReadWriteFile for T { write: Some(blob_write::), llseek: Some(bindings::default_llseek), open: Some(bindings::simple_open), - // SAFETY: `file_operations` supports zeroes in all fields. - ..unsafe { core::mem::zeroed() } + ..pin_init::zeroed() }; // SAFETY: -- cgit v1.2.3 From 1cab0874875a1c37f71edf1e1e3029b1cf31d81e Mon Sep 17 00:00:00 2001 From: Atharv Dubey Date: Sat, 29 Nov 2025 18:17:06 +0530 Subject: rust: auxiliary: use `pin_init::zeroed()` for device ID Replace the previous `unsafe { core::mem::zeroed() }` initialization for `bindings::auxillary_device_id` with `pin_init::zeroed()`. This removes the explicit unsafe block and uses the safer pinned zero-initialization helper. Suggested-by: Benno Lossin Signed-off-by: Atharv Dubey Link: https://github.com/Rust-for-Linux/linux/issues/1189 Link: https://patch.msgid.link/20251129124706.26263-1-atharvd440@gmail.com Signed-off-by: Danilo Krummrich --- rust/kernel/auxiliary.rs | 7 +------ 1 file changed, 1 insertion(+), 6 deletions(-) (limited to 'rust/kernel') diff --git a/rust/kernel/auxiliary.rs b/rust/kernel/auxiliary.rs index d2890a2c543a..c0d34f53a399 100644 --- a/rust/kernel/auxiliary.rs +++ b/rust/kernel/auxiliary.rs @@ -121,12 +121,7 @@ impl DeviceId { let name = name.to_bytes_with_nul(); let modname = modname.to_bytes_with_nul(); - // TODO: Replace with `bindings::auxiliary_device_id::default()` once stabilized for - // `const`. - // - // SAFETY: FFI type is valid to be zero-initialized. - let mut id: bindings::auxiliary_device_id = unsafe { core::mem::zeroed() }; - + let mut id: bindings::auxiliary_device_id = pin_init::zeroed(); let mut i = 0; while i < modname.len() { id.name[i] = modname[i]; -- cgit v1.2.3 From bd36f6e2abf7f85644f7ea8deb1de4040b03bbc1 Mon Sep 17 00:00:00 2001 From: Miguel Ojeda Date: Sat, 24 Jan 2026 00:34:32 +0100 Subject: rust: sync: atomic: Provide stub for `rusttest` 32-bit hosts MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit For arm32, on a x86_64 builder, running the `rusttest` target yields: error[E0080]: evaluation of constant value failed --> rust/kernel/static_assert.rs:37:23 | 37 | const _: () = ::core::assert!($condition $(,$arg)?); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ the evaluated program panicked at 'assertion failed: size_of::() == size_of::()', rust/kernel/sync/atomic/predefine.rs:68:1 | ::: rust/kernel/sync/atomic/predefine.rs:68:1 | 68 | static_assert!(size_of::() == size_of::()); | -------------------------------------------------------------------- in this macro invocation | = note: this error originates in the macro `::core::assert` which comes from the expansion of the macro `static_assert` (in Nightly builds, run with -Z macro-backtrace for more info) The reason is that `rusttest` runs on the host, so for e.g. a x86_64 builder `isize` is 64 bits but it is not a `CONFIG_64BIT` build. Fix it by providing a stub for `rusttest` as usual. Fixes: 84c6d36bcaf9 ("rust: sync: atomic: Add Atomic<{usize,isize}>") Cc: stable@vger.kernel.org Reviewed-by: Onur Özkan Acked-by: Boqun Feng Link: https://patch.msgid.link/20260123233432.22703-1-ojeda@kernel.org Signed-off-by: Miguel Ojeda --- rust/kernel/sync/atomic/predefine.rs | 11 +++++++++++ 1 file changed, 11 insertions(+) (limited to 'rust/kernel') diff --git a/rust/kernel/sync/atomic/predefine.rs b/rust/kernel/sync/atomic/predefine.rs index 45a17985cda4..0fca1ba3c2db 100644 --- a/rust/kernel/sync/atomic/predefine.rs +++ b/rust/kernel/sync/atomic/predefine.rs @@ -35,12 +35,23 @@ unsafe impl super::AtomicAdd for i64 { // as `isize` and `usize`, and `isize` and `usize` are always bi-directional transmutable to // `isize_atomic_repr`, which also always implements `AtomicImpl`. #[allow(non_camel_case_types)] +#[cfg(not(testlib))] #[cfg(not(CONFIG_64BIT))] type isize_atomic_repr = i32; #[allow(non_camel_case_types)] +#[cfg(not(testlib))] #[cfg(CONFIG_64BIT)] type isize_atomic_repr = i64; +#[allow(non_camel_case_types)] +#[cfg(testlib)] +#[cfg(target_pointer_width = "32")] +type isize_atomic_repr = i32; +#[allow(non_camel_case_types)] +#[cfg(testlib)] +#[cfg(target_pointer_width = "64")] +type isize_atomic_repr = i64; + // Ensure size and alignment requirements are checked. static_assert!(size_of::() == size_of::()); static_assert!(align_of::() == align_of::()); -- cgit v1.2.3 From 5016cae970d7d59d62aa4f6f11455a9e9630dd1c Mon Sep 17 00:00:00 2001 From: Shivam Kalra Date: Fri, 23 Jan 2026 18:51:13 +0530 Subject: rust: num: bounded: clean __new documentation and comments Following commit 3a1ec424dd9c ("rust: num: bounded: mark __new as unsafe"), remove the redundant paragraph in the documentation of __new now that the Safety section explicitly covers the requirement. Additionally, add an INVARIANT comment inside the function body where the Bounded instance is actually constructed to document that the type invariant is upheld. Suggested-by: Miguel Ojeda Link: https://lore.kernel.org/rust-for-linux/CANiq72mUCUh72BWP4eD1PTDpwdb1ML+Xgfom-Ys6thJooqQPwQ@mail.gmail.com/ Signed-off-by: Shivam Kalra Acked-by: Alexandre Courbot Link: https://patch.msgid.link/20260123132132.53854-1-shivamklr@cock.li [ Reworded slightly. - Miguel ] Signed-off-by: Miguel Ojeda --- rust/kernel/num/bounded.rs | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) (limited to 'rust/kernel') diff --git a/rust/kernel/num/bounded.rs b/rust/kernel/num/bounded.rs index 5ef8361cf5d5..fa81acbdc8c2 100644 --- a/rust/kernel/num/bounded.rs +++ b/rust/kernel/num/bounded.rs @@ -282,9 +282,6 @@ where /// All instances of [`Bounded`] must be created through this method as it enforces most of the /// type invariants. /// - /// The caller remains responsible for checking, either statically or dynamically, that `value` - /// can be represented as a `T` using at most `N` bits. - /// /// # Safety /// /// The caller must ensure that `value` can be represented within `N` bits. @@ -297,6 +294,7 @@ where assert!(N <= T::BITS); } + // INVARIANT: The caller ensures `value` fits within `N` bits. Self(value) } -- cgit v1.2.3 From 7f87c7a003125d5af5ec7abbbc0ac21b4a4661ae Mon Sep 17 00:00:00 2001 From: Peter Novak Date: Sun, 30 Nov 2025 22:12:33 +0100 Subject: rust: use consistent backtick formatting for NULL in docs Some doc comments use `NULL` while others use plain NULL. Make it consistent by adding backticks everywhere, matching the majority of existing usage. Signed-off-by: Peter Novak Acked-by: Stephen Boyd Acked-by: David Gow Reviewed-by: Alexandre Courbot Acked-by: Danilo Krummrich Link: https://patch.msgid.link/20251130211233.367946-1-seimun018r@gmail.com [ Reworded slightly. - Miguel ] Signed-off-by: Miguel Ojeda --- rust/kernel/clk.rs | 2 +- rust/kernel/debugfs/entry.rs | 2 +- rust/kernel/kunit.rs | 8 ++++---- 3 files changed, 6 insertions(+), 6 deletions(-) (limited to 'rust/kernel') diff --git a/rust/kernel/clk.rs b/rust/kernel/clk.rs index c1cfaeaa36a2..db3464e2df32 100644 --- a/rust/kernel/clk.rs +++ b/rust/kernel/clk.rs @@ -94,7 +94,7 @@ mod common_clk { /// # Invariants /// /// A [`Clk`] instance holds either a pointer to a valid [`struct clk`] created by the C - /// portion of the kernel or a NULL pointer. + /// portion of the kernel or a `NULL` pointer. /// /// Instances of this type are reference-counted. Calling [`Clk::get`] ensures that the /// allocation remains valid for the lifetime of the [`Clk`]. diff --git a/rust/kernel/debugfs/entry.rs b/rust/kernel/debugfs/entry.rs index 706cb7f73d6c..a30bf8f29679 100644 --- a/rust/kernel/debugfs/entry.rs +++ b/rust/kernel/debugfs/entry.rs @@ -148,7 +148,7 @@ impl Entry<'_> { /// # Guarantees /// /// Due to the type invariant, the value returned from this function will always be an error - /// code, NULL, or a live DebugFS directory. If it is live, it will remain live at least as + /// code, `NULL`, or a live DebugFS directory. If it is live, it will remain live at least as /// long as this entry lives. pub(crate) fn as_ptr(&self) -> *mut bindings::dentry { self.entry diff --git a/rust/kernel/kunit.rs b/rust/kernel/kunit.rs index 21aef6c97325..4ccc8fc4a800 100644 --- a/rust/kernel/kunit.rs +++ b/rust/kernel/kunit.rs @@ -190,7 +190,7 @@ pub fn is_test_result_ok(t: impl TestResult) -> bool { /// Represents an individual test case. /// -/// The [`kunit_unsafe_test_suite!`] macro expects a NULL-terminated list of valid test cases. +/// The [`kunit_unsafe_test_suite!`] macro expects a `NULL`-terminated list of valid test cases. /// Use [`kunit_case_null`] to generate such a delimiter. #[doc(hidden)] pub const fn kunit_case( @@ -212,9 +212,9 @@ pub const fn kunit_case( } } -/// Represents the NULL test case delimiter. +/// Represents the `NULL` test case delimiter. /// -/// The [`kunit_unsafe_test_suite!`] macro expects a NULL-terminated list of test cases. This +/// The [`kunit_unsafe_test_suite!`] macro expects a `NULL`-terminated list of test cases. This /// function returns such a delimiter. #[doc(hidden)] pub const fn kunit_case_null() -> kernel::bindings::kunit_case { @@ -237,7 +237,7 @@ pub const fn kunit_case_null() -> kernel::bindings::kunit_case { /// /// # Safety /// -/// `test_cases` must be a NULL terminated array of valid test cases, +/// `test_cases` must be a `NULL` terminated array of valid test cases, /// whose lifetime is at least that of the test suite (i.e., static). /// /// # Examples -- cgit v1.2.3 From 209c70953aa3630eb25e93e95464306a41b16d12 Mon Sep 17 00:00:00 2001 From: Alexandre Courbot Date: Mon, 15 Dec 2025 14:49:09 +0900 Subject: rust: transmute: implement FromBytes and AsBytes for inhabited ZSTs This is useful when using types that may or may not be empty in generic code relying on these traits. It is also safe because technically a no-op. Reviewed-by: Alistair Popple Reviewed-by: Gary Guo Signed-off-by: Alexandre Courbot Reviewed-by: Benno Lossin Link: https://patch.msgid.link/20251215-transmute_unit-v4-1-477d71ec7c23@nvidia.com Signed-off-by: Miguel Ojeda --- rust/kernel/transmute.rs | 8 ++++++++ 1 file changed, 8 insertions(+) (limited to 'rust/kernel') diff --git a/rust/kernel/transmute.rs b/rust/kernel/transmute.rs index be5dbf3829e2..5711580c9f9b 100644 --- a/rust/kernel/transmute.rs +++ b/rust/kernel/transmute.rs @@ -170,6 +170,10 @@ macro_rules! impl_frombytes { } impl_frombytes! { + // SAFETY: Inhabited ZSTs only have one possible bit pattern, and these two have no invariant. + (), + {} core::marker::PhantomData, + // SAFETY: All bit patterns are acceptable values of the types below. u8, u16, u32, u64, usize, i8, i16, i32, i64, isize, @@ -230,6 +234,10 @@ macro_rules! impl_asbytes { } impl_asbytes! { + // SAFETY: Inhabited ZSTs only have one possible bit pattern, and these two have no invariant. + (), + {} core::marker::PhantomData, + // SAFETY: Instances of the following types have no uninitialized portions. u8, u16, u32, u64, usize, i8, i16, i32, i64, isize, -- cgit v1.2.3 From 105ddfb2d2b3acec7a7d9695463df48733d91e6c Mon Sep 17 00:00:00 2001 From: Alice Ryhl Date: Wed, 7 Jan 2026 08:28:46 +0000 Subject: rust: task: restrict Task::group_leader() to current MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The Task::group_leader() method currently allows you to access the group_leader() of any task, for example one you hold a refcount to. But this is not safe in general since the group leader could change when a task exits. See for example commit a15f37a40145c ("kernel/sys.c: fix the racy usage of task_lock(tsk->group_leader) in sys_prlimit64() paths"). All existing users of Task::group_leader() call this method on current, which is guaranteed running, so there's not an actual issue in Rust code today. But to prevent code in the future from making this mistake, restrict Task::group_leader() so that it can only be called on current. There are some other cases where accessing task->group_leader is okay. For example it can be safe if you hold tasklist_lock or rcu_read_lock(). However, only supporting current->group_leader is sufficient for all in-tree Rust users of group_leader right now. Safe Rust functionality for accessing it under rcu or while holding tasklist_lock may be added in the future if required by any future Rust module. This patch is a bugfix in that it prevents users of this API from writing incorrect code. It doesn't change behavior of correct code. Link: https://lkml.kernel.org/r/20260107-task-group-leader-v2-1-8fbf816f2a2f@google.com Signed-off-by: Alice Ryhl Fixes: 313c4281bc9d ("rust: add basic `Task`") Reported-by: Oleg Nesterov Closes: https://lore.kernel.org/all/aTLnV-5jlgfk1aRK@redhat.com/ Reviewed-by: Boqun Feng Reviewed-by: Gary Guo Cc: Andreas Hindborg Cc: Benno Lossin Cc: "Björn Roy Baron" Cc: Björn Roy Baron Cc: Christian Brauner Cc: Danilo Krummrich Cc: FUJITA Tomonori Cc: Miguel Ojeda Cc: Panagiotis Foliadis Cc: Shankari Anand Cc: Trevor Gross Signed-off-by: Andrew Morton --- rust/kernel/task.rs | 24 ++++++++++++------------ 1 file changed, 12 insertions(+), 12 deletions(-) (limited to 'rust/kernel') diff --git a/rust/kernel/task.rs b/rust/kernel/task.rs index 49fad6de0674..cc907fb531bc 100644 --- a/rust/kernel/task.rs +++ b/rust/kernel/task.rs @@ -204,18 +204,6 @@ impl Task { self.0.get() } - /// Returns the group leader of the given task. - pub fn group_leader(&self) -> &Task { - // SAFETY: The group leader of a task never changes after initialization, so reading this - // field is not a data race. - let ptr = unsafe { *ptr::addr_of!((*self.as_ptr()).group_leader) }; - - // SAFETY: The lifetime of the returned task reference is tied to the lifetime of `self`, - // and given that a task has a reference to its group leader, we know it must be valid for - // the lifetime of the returned task reference. - unsafe { &*ptr.cast() } - } - /// Returns the PID of the given task. pub fn pid(&self) -> Pid { // SAFETY: The pid of a task never changes after initialization, so reading this field is @@ -345,6 +333,18 @@ impl CurrentTask { // `release_task()` call. Some(unsafe { PidNamespace::from_ptr(active_ns) }) } + + /// Returns the group leader of the current task. + pub fn group_leader(&self) -> &Task { + // SAFETY: The group leader of a task never changes while the task is running, and `self` + // is the current task, which is guaranteed running. + let ptr = unsafe { (*self.as_ptr()).group_leader }; + + // SAFETY: `current->group_leader` stays valid for at least the duration in which `current` + // is running, and the signature of this function ensures that the returned `&Task` can + // only be used while `current` is still valid, thus still running. + unsafe { &*ptr.cast() } + } } // SAFETY: The type invariants guarantee that `Task` is always refcounted. -- cgit v1.2.3 From 8c8b12a55614ea05953e8d695e700e6e1322a05d Mon Sep 17 00:00:00 2001 From: Alexandre Courbot Date: Fri, 28 Nov 2025 11:11:39 +0900 Subject: rust: cpufreq: always inline functions using build_assert with arguments `build_assert` relies on the compiler to optimize out its error path. Functions using it with its arguments must thus always be inlined, otherwise the error path of `build_assert` might not be optimized out, triggering a build error. Signed-off-by: Alexandre Courbot Reviewed-by: Daniel Almeida Signed-off-by: Viresh Kumar --- rust/kernel/cpufreq.rs | 2 ++ 1 file changed, 2 insertions(+) (limited to 'rust/kernel') diff --git a/rust/kernel/cpufreq.rs b/rust/kernel/cpufreq.rs index f968fbd22890..0879a79485f8 100644 --- a/rust/kernel/cpufreq.rs +++ b/rust/kernel/cpufreq.rs @@ -1015,6 +1015,8 @@ impl Registration { ..pin_init::zeroed() }; + // Always inline to optimize out error path of `build_assert`. + #[inline(always)] const fn copy_name(name: &'static CStr) -> [c_char; CPUFREQ_NAME_LEN] { let src = name.to_bytes_with_nul(); let mut dst = [0; CPUFREQ_NAME_LEN]; -- cgit v1.2.3 From e05d9e5c8b754cc7d72acd896f5f7caf6b78a973 Mon Sep 17 00:00:00 2001 From: Tamir Duberstein Date: Mon, 22 Dec 2025 13:29:32 +0100 Subject: rust: cpufreq: replace `kernel::c_str!` with C-Strings C-String literals were added in Rust 1.77. Replace instances of `kernel::c_str!` with C-String literals where possible. Acked-by: Greg Kroah-Hartman Reviewed-by: Alice Ryhl Reviewed-by: Benno Lossin Signed-off-by: Tamir Duberstein Reviewed-by: Daniel Almeida Acked-by: Danilo Krummrich Signed-off-by: Viresh Kumar --- rust/kernel/cpufreq.rs | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) (limited to 'rust/kernel') diff --git a/rust/kernel/cpufreq.rs b/rust/kernel/cpufreq.rs index 0879a79485f8..76faa1ac8501 100644 --- a/rust/kernel/cpufreq.rs +++ b/rust/kernel/cpufreq.rs @@ -840,7 +840,6 @@ pub trait Driver { /// ``` /// use kernel::{ /// cpufreq, -/// c_str, /// device::{Core, Device}, /// macros::vtable, /// of, platform, @@ -853,7 +852,7 @@ pub trait Driver { /// /// #[vtable] /// impl cpufreq::Driver for SampleDriver { -/// const NAME: &'static CStr = c_str!("cpufreq-sample"); +/// const NAME: &'static CStr = c"cpufreq-sample"; /// const FLAGS: u16 = cpufreq::flags::NEED_INITIAL_FREQ_CHECK | cpufreq::flags::IS_COOLING_DEV; /// const BOOST_ENABLED: bool = true; /// -- cgit v1.2.3 From 11af6e102d31433e3084d6d6cdb2b2fe6c23d1a9 Mon Sep 17 00:00:00 2001 From: Yilin Chen <1479826151@qq.com> Date: Mon, 12 Jan 2026 16:00:47 +0800 Subject: rust: cpumask: rename methods of Cpumask for clarity and consistency Rename `as_ref` and `as_mut_ref` to `from_raw` and `from_raw_mut` to align with the established naming convention for constructing types from raw pointers in the kernel's Rust codebase. Signed-off-by: Yilin Chen <1479826151@qq.com> Reviewed-by: Gary Guo Reviewed-by: Alice Ryhl Signed-off-by: Viresh Kumar --- rust/kernel/cpumask.rs | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) (limited to 'rust/kernel') diff --git a/rust/kernel/cpumask.rs b/rust/kernel/cpumask.rs index c1d17826ae7b..44bb36636ee3 100644 --- a/rust/kernel/cpumask.rs +++ b/rust/kernel/cpumask.rs @@ -39,7 +39,7 @@ use core::ops::{Deref, DerefMut}; /// fn set_clear_cpu(ptr: *mut bindings::cpumask, set_cpu: CpuId, clear_cpu: CpuId) { /// // SAFETY: The `ptr` is valid for writing and remains valid for the lifetime of the /// // returned reference. -/// let mask = unsafe { Cpumask::as_mut_ref(ptr) }; +/// let mask = unsafe { Cpumask::from_raw_mut(ptr) }; /// /// mask.set(set_cpu); /// mask.clear(clear_cpu); @@ -49,13 +49,13 @@ use core::ops::{Deref, DerefMut}; pub struct Cpumask(Opaque); impl Cpumask { - /// Creates a mutable reference to an existing `struct cpumask` pointer. + /// Creates a mutable reference from an existing `struct cpumask` pointer. /// /// # Safety /// /// The caller must ensure that `ptr` is valid for writing and remains valid for the lifetime /// of the returned reference. - pub unsafe fn as_mut_ref<'a>(ptr: *mut bindings::cpumask) -> &'a mut Self { + pub unsafe fn from_raw_mut<'a>(ptr: *mut bindings::cpumask) -> &'a mut Self { // SAFETY: Guaranteed by the safety requirements of the function. // // INVARIANT: The caller ensures that `ptr` is valid for writing and remains valid for the @@ -63,13 +63,13 @@ impl Cpumask { unsafe { &mut *ptr.cast() } } - /// Creates a reference to an existing `struct cpumask` pointer. + /// Creates a reference from an existing `struct cpumask` pointer. /// /// # Safety /// /// The caller must ensure that `ptr` is valid for reading and remains valid for the lifetime /// of the returned reference. - pub unsafe fn as_ref<'a>(ptr: *const bindings::cpumask) -> &'a Self { + pub unsafe fn from_raw<'a>(ptr: *const bindings::cpumask) -> &'a Self { // SAFETY: Guaranteed by the safety requirements of the function. // // INVARIANT: The caller ensures that `ptr` is valid for reading and remains valid for the -- cgit v1.2.3 From e6de07249ef381b674f0d65adf9defcdab76b768 Mon Sep 17 00:00:00 2001 From: Tamir Duberstein Date: Thu, 22 Jan 2026 21:46:24 -0800 Subject: rust: sync: Replace `kernel::c_str!` with C-Strings C-String literals were added in Rust 1.77. Replace instances of `kernel::c_str!` with C-String literals where possible. Signed-off-by: Tamir Duberstein Signed-off-by: Boqun Feng Signed-off-by: Peter Zijlstra (Intel) Reviewed-by: Gary Guo Link: https://patch.msgid.link/20260120-cstr-sync-again-v1-1-2a775a2a36fd@kernel.org Link: https://patch.msgid.link/20260123054624.8226-2-boqun.feng@gmail.com --- rust/kernel/sync.rs | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) (limited to 'rust/kernel') diff --git a/rust/kernel/sync.rs b/rust/kernel/sync.rs index b10e576221ff..993dbf2caa0e 100644 --- a/rust/kernel/sync.rs +++ b/rust/kernel/sync.rs @@ -126,13 +126,12 @@ impl PinnedDrop for LockClassKey { /// # Examples /// /// ``` -/// use kernel::c_str; /// use kernel::sync::{static_lock_class, Arc, SpinLock}; /// /// fn new_locked_int() -> Result>> { /// Arc::pin_init(SpinLock::new( /// 42, -/// c_str!("new_locked_int"), +/// c"new_locked_int", /// static_lock_class!(), /// ), GFP_KERNEL) /// } -- cgit v1.2.3 From be97f3c82021239476ce32cddde32948c597753e Mon Sep 17 00:00:00 2001 From: Gary Guo Date: Mon, 12 Jan 2026 17:07:22 +0000 Subject: rust: kunit: use `pin_init::zeroed` instead of custom null value The last null element can be created (constly) using `pin_init::zeroed`, so prefer to use it instead of adding a custom way of building it. Reviewed-by: Tamir Duberstein Reviewed-by: Benno Lossin Signed-off-by: Gary Guo Reviewed-by: David Gow Link: https://patch.msgid.link/20260112170919.1888584-12-gary@kernel.org Signed-off-by: Miguel Ojeda --- rust/kernel/kunit.rs | 26 +------------------------- 1 file changed, 1 insertion(+), 25 deletions(-) (limited to 'rust/kernel') diff --git a/rust/kernel/kunit.rs b/rust/kernel/kunit.rs index 4ccc8fc4a800..f93f24a60bdd 100644 --- a/rust/kernel/kunit.rs +++ b/rust/kernel/kunit.rs @@ -189,9 +189,6 @@ pub fn is_test_result_ok(t: impl TestResult) -> bool { } /// Represents an individual test case. -/// -/// The [`kunit_unsafe_test_suite!`] macro expects a `NULL`-terminated list of valid test cases. -/// Use [`kunit_case_null`] to generate such a delimiter. #[doc(hidden)] pub const fn kunit_case( name: &'static kernel::str::CStr, @@ -212,27 +209,6 @@ pub const fn kunit_case( } } -/// Represents the `NULL` test case delimiter. -/// -/// The [`kunit_unsafe_test_suite!`] macro expects a `NULL`-terminated list of test cases. This -/// function returns such a delimiter. -#[doc(hidden)] -pub const fn kunit_case_null() -> kernel::bindings::kunit_case { - kernel::bindings::kunit_case { - run_case: None, - name: core::ptr::null_mut(), - generate_params: None, - attr: kernel::bindings::kunit_attributes { - speed: kernel::bindings::kunit_speed_KUNIT_SPEED_NORMAL, - }, - status: kernel::bindings::kunit_status_KUNIT_SUCCESS, - module_name: core::ptr::null_mut(), - log: core::ptr::null_mut(), - param_init: None, - param_exit: None, - } -} - /// Registers a KUnit test suite. /// /// # Safety @@ -251,7 +227,7 @@ pub const fn kunit_case_null() -> kernel::bindings::kunit_case { /// /// static mut KUNIT_TEST_CASES: [kernel::bindings::kunit_case; 2] = [ /// kernel::kunit::kunit_case(c"name", test_fn), -/// kernel::kunit::kunit_case_null(), +/// pin_init::zeroed(), /// ]; /// kernel::kunit_unsafe_test_suite!(suite_name, KUNIT_TEST_CASES); /// ``` -- cgit v1.2.3 From c71257394bc9c59ea727803f6e55e83fe63db74e Mon Sep 17 00:00:00 2001 From: Beata Michalska Date: Wed, 28 Jan 2026 14:53:20 +0100 Subject: rust: dma: allow drivers to tune max segment size Make dma_set_max_seg_size() available to Rust so drivers can perform standard DMA setup steps. Signed-off-by: Beata Michalska Acked-by: Robin Murphy Reviewed-by: Alice Ryhl Link: https://patch.msgid.link/20260128135320.689046-1-beata.michalska@arm.com Signed-off-by: Danilo Krummrich --- rust/kernel/dma.rs | 17 +++++++++++++++++ 1 file changed, 17 insertions(+) (limited to 'rust/kernel') diff --git a/rust/kernel/dma.rs b/rust/kernel/dma.rs index acc65b1e0f24..909d56fd5118 100644 --- a/rust/kernel/dma.rs +++ b/rust/kernel/dma.rs @@ -85,6 +85,23 @@ pub trait Device: AsRef> { bindings::dma_set_mask_and_coherent(self.as_ref().as_raw(), mask.value()) }) } + + /// Set the maximum size of a single DMA segment the device may request. + /// + /// This method is usually called once from `probe()` as soon as the device capabilities are + /// known. + /// + /// # Safety + /// + /// This method must not be called concurrently with any DMA allocation or mapping primitives, + /// such as [`CoherentAllocation::alloc_attrs`]. + unsafe fn dma_set_max_seg_size(&self, size: u32) { + // SAFETY: + // - By the type invariant of `device::Device`, `self.as_ref().as_raw()` is valid. + // - The safety requirement of this function guarantees that there are no concurrent calls + // to DMA allocation and mapping primitives using this parameter. + unsafe { bindings::dma_set_max_seg_size(self.as_ref().as_raw(), size) } + } } /// A DMA mask that holds a bitmask with the lowest `n` bits set. -- cgit v1.2.3 From 40210c2b11a873ff64a812c2d2600f529f01a83e Mon Sep 17 00:00:00 2001 From: Tamir Duberstein Date: Mon, 22 Dec 2025 13:18:57 +0100 Subject: rust: seq_file: replace `kernel::c_str!` with C-Strings C-String literals were added in Rust 1.77. Replace instances of `kernel::c_str!` with C-String literals where possible. Acked-by: Greg Kroah-Hartman Reviewed-by: Alice Ryhl Reviewed-by: Benno Lossin Signed-off-by: Tamir Duberstein Link: https://patch.msgid.link/20251222-cstr-vfs-v1-1-18e3d327cbd7@gmail.com Acked-by: Danilo Krummrich Reviewed-by: Daniel Almeida Signed-off-by: Christian Brauner --- rust/kernel/seq_file.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'rust/kernel') diff --git a/rust/kernel/seq_file.rs b/rust/kernel/seq_file.rs index 855e533813a6..518265558d66 100644 --- a/rust/kernel/seq_file.rs +++ b/rust/kernel/seq_file.rs @@ -4,7 +4,7 @@ //! //! C header: [`include/linux/seq_file.h`](srctree/include/linux/seq_file.h) -use crate::{bindings, c_str, fmt, str::CStrExt as _, types::NotThreadSafe, types::Opaque}; +use crate::{bindings, fmt, str::CStrExt as _, types::NotThreadSafe, types::Opaque}; /// A utility for generating the contents of a seq file. #[repr(transparent)] @@ -36,7 +36,7 @@ impl SeqFile { unsafe { bindings::seq_printf( self.inner.get(), - c_str!("%pA").as_char_ptr(), + c"%pA".as_char_ptr(), core::ptr::from_ref(&args).cast::(), ); } -- cgit v1.2.3 From 844590b439870c921fa0e11d412c300564391a18 Mon Sep 17 00:00:00 2001 From: Tamir Duberstein Date: Mon, 22 Dec 2025 13:16:59 +0100 Subject: rust: clk: replace `kernel::c_str!` with C-Strings C-String literals were added in Rust 1.77. Replace instances of `kernel::c_str!` with C-String literals where possible. Acked-by: Greg Kroah-Hartman Reviewed-by: Alice Ryhl Reviewed-by: Benno Lossin Acked-by: Stephen Boyd Acked-by: Viresh Kumar Signed-off-by: Tamir Duberstein Reviewed-by: Daniel Almeida Link: https://patch.msgid.link/20251222-cstr-clk-v1-1-ef0687717aa1@gmail.com Signed-off-by: Miguel Ojeda --- rust/kernel/clk.rs | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) (limited to 'rust/kernel') diff --git a/rust/kernel/clk.rs b/rust/kernel/clk.rs index db3464e2df32..4059aff34d09 100644 --- a/rust/kernel/clk.rs +++ b/rust/kernel/clk.rs @@ -104,13 +104,12 @@ mod common_clk { /// The following example demonstrates how to obtain and configure a clock for a device. /// /// ``` - /// use kernel::c_str; /// use kernel::clk::{Clk, Hertz}; /// use kernel::device::Device; /// use kernel::error::Result; /// /// fn configure_clk(dev: &Device) -> Result { - /// let clk = Clk::get(dev, Some(c_str!("apb_clk")))?; + /// let clk = Clk::get(dev, Some(c"apb_clk"))?; /// /// clk.prepare_enable()?; /// @@ -272,13 +271,12 @@ mod common_clk { /// device. The code functions correctly whether or not the clock is available. /// /// ``` - /// use kernel::c_str; /// use kernel::clk::{OptionalClk, Hertz}; /// use kernel::device::Device; /// use kernel::error::Result; /// /// fn configure_clk(dev: &Device) -> Result { - /// let clk = OptionalClk::get(dev, Some(c_str!("apb_clk")))?; + /// let clk = OptionalClk::get(dev, Some(c"apb_clk"))?; /// /// clk.prepare_enable()?; /// -- cgit v1.2.3 From 966f79ce6f6b3d138b69cacc3cdcbb5001141b4d Mon Sep 17 00:00:00 2001 From: FUJITA Tomonori Date: Thu, 8 Jan 2026 10:33:50 +0900 Subject: rust: bug: Support DEBUG_BUGVERBOSE_DETAILED option Make warn_on() support DEBUG_BUGVERBOSE_DETAILED option, which was introduced by the commit aec58b48517c ("bugs/core: Extend __WARN_FLAGS() with the 'cond_str' parameter"). When the option is enabled, WARN splats now show the evaluated warn_on() condition alongside the file path, e.g.: ------------[ cut here ]------------ WARNING: [val == 1] linux/samples/rust/rust_minimal.rs:27 at _RNvXCsk7t4azzUqHP_12rust_minimalNtB2_11RustMinimalNtCs8pcx3n4 Modules linked in: rust_minimal(+) Signed-off-by: FUJITA Tomonori Reviewed-by: Gary Guo Link: https://patch.msgid.link/20260108013350.2880613-1-fujita.tomonori@gmail.com Signed-off-by: Miguel Ojeda --- rust/kernel/bug.rs | 20 +++++++++++++------- 1 file changed, 13 insertions(+), 7 deletions(-) (limited to 'rust/kernel') diff --git a/rust/kernel/bug.rs b/rust/kernel/bug.rs index 36aef43e5ebe..ed943960f851 100644 --- a/rust/kernel/bug.rs +++ b/rust/kernel/bug.rs @@ -11,9 +11,9 @@ #[cfg(all(CONFIG_BUG, not(CONFIG_UML), not(CONFIG_LOONGARCH), not(CONFIG_ARM)))] #[cfg(CONFIG_DEBUG_BUGVERBOSE)] macro_rules! warn_flags { - ($flags:expr) => { + ($file:expr, $flags:expr) => { const FLAGS: u32 = $crate::bindings::BUGFLAG_WARNING | $flags; - const _FILE: &[u8] = file!().as_bytes(); + const _FILE: &[u8] = $file.as_bytes(); // Plus one for null-terminator. static FILE: [u8; _FILE.len() + 1] = { let mut bytes = [0; _FILE.len() + 1]; @@ -50,7 +50,7 @@ macro_rules! warn_flags { #[cfg(all(CONFIG_BUG, not(CONFIG_UML), not(CONFIG_LOONGARCH), not(CONFIG_ARM)))] #[cfg(not(CONFIG_DEBUG_BUGVERBOSE))] macro_rules! warn_flags { - ($flags:expr) => { + ($file:expr, $flags:expr) => { const FLAGS: u32 = $crate::bindings::BUGFLAG_WARNING | $flags; // SAFETY: @@ -75,7 +75,7 @@ macro_rules! warn_flags { #[doc(hidden)] #[cfg(all(CONFIG_BUG, CONFIG_UML))] macro_rules! warn_flags { - ($flags:expr) => { + ($file:expr, $flags:expr) => { // SAFETY: It is always safe to call `warn_slowpath_fmt()` // with a valid null-terminated string. unsafe { @@ -93,7 +93,7 @@ macro_rules! warn_flags { #[doc(hidden)] #[cfg(all(CONFIG_BUG, any(CONFIG_LOONGARCH, CONFIG_ARM)))] macro_rules! warn_flags { - ($flags:expr) => { + ($file:expr, $flags:expr) => { // SAFETY: It is always safe to call `WARN_ON()`. unsafe { $crate::bindings::WARN_ON(true) } }; @@ -103,7 +103,7 @@ macro_rules! warn_flags { #[doc(hidden)] #[cfg(not(CONFIG_BUG))] macro_rules! warn_flags { - ($flags:expr) => {}; + ($file:expr, $flags:expr) => {}; } #[doc(hidden)] @@ -116,10 +116,16 @@ pub const fn bugflag_taint(value: u32) -> u32 { macro_rules! warn_on { ($cond:expr) => {{ let cond = $cond; + + #[cfg(CONFIG_DEBUG_BUGVERBOSE_DETAILED)] + const _COND_STR: &str = concat!("[", stringify!($cond), "] ", file!()); + #[cfg(not(CONFIG_DEBUG_BUGVERBOSE_DETAILED))] + const _COND_STR: &str = file!(); + if cond { const WARN_ON_FLAGS: u32 = $crate::bug::bugflag_taint($crate::bindings::TAINT_WARN); - $crate::warn_flags!(WARN_ON_FLAGS); + $crate::warn_flags!(_COND_STR, WARN_ON_FLAGS); } cond }}; -- cgit v1.2.3 From e600425708148e952adce496ac9e9def79264c8a Mon Sep 17 00:00:00 2001 From: FUJITA Tomonori Date: Mon, 17 Nov 2025 09:24:51 +0900 Subject: rust: print: Add support for calling a function exactly once Add the Rust equivalent of the kernel's `DO_ONCE_LITE` macro. While it would be possible to implement the feature entirely as a Rust macro, the functionality that can be implemented as regular functions has been extracted and implemented as the `OnceLite` struct for better code maintainability. Signed-off-by: FUJITA Tomonori Reviewed-by: Alice Ryhl Reviewed-by: Gary Guo Link: https://patch.msgid.link/20251117002452.4068692-2-fujita.tomonori@gmail.com [ Added prefix to title. - Miguel ] Signed-off-by: Miguel Ojeda --- rust/kernel/print.rs | 83 ++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 83 insertions(+) (limited to 'rust/kernel') diff --git a/rust/kernel/print.rs b/rust/kernel/print.rs index 2d743d78d220..af32054be5a2 100644 --- a/rust/kernel/print.rs +++ b/rust/kernel/print.rs @@ -11,6 +11,11 @@ use crate::{ fmt, prelude::*, str::RawFormatter, + sync::atomic::{ + Atomic, + AtomicType, + Relaxed, // + }, }; // Called from `vsprintf` with format specifier `%pA`. @@ -423,3 +428,81 @@ macro_rules! pr_cont ( $crate::print_macro!($crate::print::format_strings::CONT, true, $($arg)*) ) ); + +/// A lightweight `call_once` primitive. +/// +/// This structure provides the Rust equivalent of the kernel's `DO_ONCE_LITE` macro. +/// While it would be possible to implement the feature entirely as a Rust macro, +/// the functionality that can be implemented as regular functions has been +/// extracted and implemented as the `OnceLite` struct for better code maintainability. +pub struct OnceLite(Atomic); + +#[derive(Clone, Copy, PartialEq, Eq)] +#[repr(i32)] +enum State { + Incomplete = 0, + Complete = 1, +} + +// SAFETY: `State` and `i32` has the same size and alignment, and it's round-trip +// transmutable to `i32`. +unsafe impl AtomicType for State { + type Repr = i32; +} + +impl OnceLite { + /// Creates a new [`OnceLite`] in the incomplete state. + #[inline(always)] + #[allow(clippy::new_without_default)] + pub const fn new() -> Self { + OnceLite(Atomic::new(State::Incomplete)) + } + + /// Calls the provided function exactly once. + /// + /// There is no other synchronization between two `call_once()`s + /// except that only one will execute `f`, in other words, callers + /// should not use a failed `call_once()` as a proof that another + /// `call_once()` has already finished and the effect is observable + /// to this thread. + pub fn call_once(&self, f: F) -> bool + where + F: FnOnce(), + { + // Avoid expensive cmpxchg if already completed. + // ORDERING: `Relaxed` is used here since no synchronization is required. + let old = self.0.load(Relaxed); + if old == State::Complete { + return false; + } + + // ORDERING: `Relaxed` is used here since no synchronization is required. + let old = self.0.xchg(State::Complete, Relaxed); + if old == State::Complete { + return false; + } + + f(); + true + } +} + +/// Run the given function exactly once. +/// +/// This is equivalent to the kernel's `DO_ONCE_LITE` macro. +/// +/// # Examples +/// +/// ``` +/// kernel::do_once_lite! { +/// kernel::pr_info!("This will be printed only once\n"); +/// }; +/// ``` +#[macro_export] +macro_rules! do_once_lite { + { $($e:tt)* } => {{ + #[link_section = ".data..once"] + static ONCE: $crate::print::OnceLite = $crate::print::OnceLite::new(); + ONCE.call_once(|| { $($e)* }); + }}; +} -- cgit v1.2.3 From 46c40f938f5f15e0a3ecfdd0feba485f8feaff92 Mon Sep 17 00:00:00 2001 From: FUJITA Tomonori Date: Mon, 17 Nov 2025 09:24:52 +0900 Subject: rust: print: Add pr_*_once macros Add Rust version of pr_[emerg|alert|crit|err|warn|notice|info]_once macros, which print a message only once. Reviewed-by: Alice Ryhl Signed-off-by: FUJITA Tomonori Link: https://patch.msgid.link/20251117002452.4068692-3-fujita.tomonori@gmail.com [ Added prefix to title. Fixed typo. - Miguel ] Signed-off-by: Miguel Ojeda --- rust/kernel/print.rs | 70 ++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 70 insertions(+) (limited to 'rust/kernel') diff --git a/rust/kernel/print.rs b/rust/kernel/print.rs index af32054be5a2..6fd84389a858 100644 --- a/rust/kernel/print.rs +++ b/rust/kernel/print.rs @@ -506,3 +506,73 @@ macro_rules! do_once_lite { ONCE.call_once(|| { $($e)* }); }}; } + +/// Prints an emergency-level message (level 0) only once. +/// +/// Equivalent to the kernel's `pr_emerg_once` macro. +#[macro_export] +macro_rules! pr_emerg_once ( + ($($arg:tt)*) => ( + $crate::do_once_lite! { $crate::pr_emerg!($($arg)*) } + ) +); + +/// Prints an alert-level message (level 1) only once. +/// +/// Equivalent to the kernel's `pr_alert_once` macro. +#[macro_export] +macro_rules! pr_alert_once ( + ($($arg:tt)*) => ( + $crate::do_once_lite! { $crate::pr_alert!($($arg)*) } + ) +); + +/// Prints a critical-level message (level 2) only once. +/// +/// Equivalent to the kernel's `pr_crit_once` macro. +#[macro_export] +macro_rules! pr_crit_once ( + ($($arg:tt)*) => ( + $crate::do_once_lite! { $crate::pr_crit!($($arg)*) } + ) +); + +/// Prints an error-level message (level 3) only once. +/// +/// Equivalent to the kernel's `pr_err_once` macro. +#[macro_export] +macro_rules! pr_err_once ( + ($($arg:tt)*) => ( + $crate::do_once_lite! { $crate::pr_err!($($arg)*) } + ) +); + +/// Prints a warning-level message (level 4) only once. +/// +/// Equivalent to the kernel's `pr_warn_once` macro. +#[macro_export] +macro_rules! pr_warn_once ( + ($($arg:tt)*) => ( + $crate::do_once_lite! { $crate::pr_warn!($($arg)*) } + ) +); + +/// Prints a notice-level message (level 5) only once. +/// +/// Equivalent to the kernel's `pr_notice_once` macro. +#[macro_export] +macro_rules! pr_notice_once ( + ($($arg:tt)*) => ( + $crate::do_once_lite! { $crate::pr_notice!($($arg)*) } + ) +); + +/// Prints an info-level message (level 6) only once. +/// +/// Equivalent to the kernel's `pr_info_once` macro. +#[macro_export] +macro_rules! pr_info_once ( + ($($arg:tt)*) => ( + $crate::do_once_lite! { $crate::pr_info!($($arg)*) } + ) +); -- cgit v1.2.3 From 72bfbe50ffbf36937c77b39e19143aabdb69b080 Mon Sep 17 00:00:00 2001 From: Zijing Zhang Date: Sat, 31 Jan 2026 16:42:17 +0000 Subject: rust: pci: re-export ConfigSpace Re-export ConfigSpace, such that users can refer to the type as kernel::pci::ConfigSpace, rather than kernel::pci::io::ConfigSpace. Fixes: 4dc0bacb1d3c ("rust: pci: add config space read/write support") Reported-by: Gary Guo Closes: https://lore.kernel.org/rust-for-linux/DG2D5ONS18FE.TC7K3O8V8SU1@garyguo.net/ Reviewed-by: Alexandre Courbot Signed-off-by: Zijing Zhang Link: https://patch.msgid.link/995384df9224283fab185b5e06f519506fff1873.1769877524.git.zijing.zhang@ry.rs [ Slightly reworded commit message. - Danilo ] Signed-off-by: Danilo Krummrich --- rust/kernel/pci.rs | 1 + 1 file changed, 1 insertion(+) (limited to 'rust/kernel') diff --git a/rust/kernel/pci.rs b/rust/kernel/pci.rs index f347c2f7c3a6..af74ddff6114 100644 --- a/rust/kernel/pci.rs +++ b/rust/kernel/pci.rs @@ -42,6 +42,7 @@ pub use self::id::{ }; pub use self::io::{ Bar, + ConfigSpace, ConfigSpaceKind, ConfigSpaceSize, Extended, -- cgit v1.2.3 From 726c262060252e13d5805f9acc382fb9f081ba07 Mon Sep 17 00:00:00 2001 From: Alexandre Courbot Date: Fri, 30 Jan 2026 22:32:46 +0900 Subject: rust: io: move MIN_SIZE and io_addr_assert to IoKnownSize `MIN_SIZE` and `io_addr_assert` are only ever used for IO types which implement `IoKnownSize` and do not make sense for types that don't. It looks like they should have been there since the beginning, so move them while the code is still fresh. Also update `IoKnownSize`'s documentation since it is not just a marker trait anymore. Fixes: 121d87b28e1d ("rust: io: separate generic I/O helpers from MMIO implementation") Signed-off-by: Alexandre Courbot Link: https://patch.msgid.link/20260130-io-min-size-v1-1-65a546e3104d@nvidia.com [ Fix typo in commit message. - Danilo ] Signed-off-by: Danilo Krummrich --- rust/kernel/io.rs | 36 ++++++++++++++++++------------------ rust/kernel/pci/io.rs | 7 +++---- 2 files changed, 21 insertions(+), 22 deletions(-) (limited to 'rust/kernel') diff --git a/rust/kernel/io.rs b/rust/kernel/io.rs index 056a3ec71647..c1cca7b438c3 100644 --- a/rust/kernel/io.rs +++ b/rust/kernel/io.rs @@ -301,9 +301,6 @@ pub trait IoCapable {} /// 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 { - /// Minimum usable size of this region. - const MIN_SIZE: usize; - /// Returns the base address of this mapping. fn addr(&self) -> usize; @@ -323,16 +320,6 @@ pub trait Io { self.addr().checked_add(offset).ok_or(EINVAL) } - /// 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(&self, offset: usize) -> usize { - build_assert!(offset_valid::(offset, Self::MIN_SIZE)); - - self.addr() + offset - } - /// Fallible 8-bit read with runtime bounds check. #[inline(always)] fn try_read8(&self, _offset: usize) -> Result @@ -478,14 +465,27 @@ pub trait Io { } } -/// Marker trait for types with a known size at compile time. +/// Trait for types with a known size at compile time. /// /// This trait is implemented by I/O backends that have a compile-time known size, /// enabling the use of infallible I/O accessors with compile-time bounds checking. /// /// Types implementing this trait can use the infallible methods in [`Io`] trait /// (e.g., `read8`, `write32`), which require `Self: IoKnownSize` bound. -pub trait IoKnownSize: Io {} +pub trait IoKnownSize: Io { + /// Minimum usable size of this region. + const MIN_SIZE: 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(&self, offset: usize) -> usize { + build_assert!(offset_valid::(offset, Self::MIN_SIZE)); + + self.addr() + offset + } +} // MMIO regions support 8, 16, and 32-bit accesses. impl IoCapable for Mmio {} @@ -497,8 +497,6 @@ impl IoCapable for Mmio {} impl IoCapable for Mmio {} impl Io for Mmio { - const MIN_SIZE: usize = SIZE; - /// Returns the base address of this mapping. #[inline] fn addr(&self) -> usize { @@ -552,7 +550,9 @@ impl Io for Mmio { ); } -impl IoKnownSize for Mmio {} +impl IoKnownSize for Mmio { + const MIN_SIZE: usize = SIZE; +} impl Mmio { /// Converts an `MmioRaw` into an `Mmio` instance, providing the accessors to the MMIO mapping. diff --git a/rust/kernel/pci/io.rs b/rust/kernel/pci/io.rs index 026e7a3b69bd..6ca4cf75594c 100644 --- a/rust/kernel/pci/io.rs +++ b/rust/kernel/pci/io.rs @@ -148,8 +148,6 @@ impl<'a, S: ConfigSpaceKind> IoCapable for ConfigSpace<'a, S> {} impl<'a, S: ConfigSpaceKind> IoCapable for ConfigSpace<'a, S> {} impl<'a, S: ConfigSpaceKind> Io for ConfigSpace<'a, S> { - const MIN_SIZE: usize = S::SIZE; - /// Returns the base address of the I/O region. It is always 0 for configuration space. #[inline] fn addr(&self) -> usize { @@ -174,8 +172,9 @@ impl<'a, S: ConfigSpaceKind> Io for ConfigSpace<'a, S> { define_write!(infallible, write32, call_config_write(pci_write_config_dword) <- u32); } -/// Marker trait indicating ConfigSpace has a known size at compile time. -impl<'a, S: ConfigSpaceKind> IoKnownSize for ConfigSpace<'a, S> {} +impl<'a, S: ConfigSpaceKind> IoKnownSize for ConfigSpace<'a, S> { + const MIN_SIZE: usize = S::SIZE; +} /// A PCI BAR to perform I/O-Operations on. /// -- cgit v1.2.3 From 0e62e4f3e56cf6c44926db2ee82ff29b4a28ac03 Mon Sep 17 00:00:00 2001 From: Filipe Xavier Date: Sat, 17 Jan 2026 07:41:25 -0300 Subject: rust: add `impl_flags!` macro for defining common bitflag operations We have seen a proliferation of `mod_whatever::foo::Flags` being defined with essentially the same implementation for `BitAnd`, `BitOr`, `.contains()` etc. This macro aims to bring a solution for this, allowing to generate these methods for user-defined structs. With some use cases in KMS and upcoming GPU drivers. Link: https://rust-for-linux.zulipchat.com/#narrow/channel/288089-General/topic/We.20really.20need.20a.20common.20.60Flags.60.20type Suggested-by: Daniel Almeida Suggested-by: Lyude Paul Reviewed-by: Daniel Almeida Reviewed-by: Lyude Paul Tested-by: Andreas Hindborg Reviewed-by: Andreas Hindborg Signed-off-by: Filipe Xavier Link: https://patch.msgid.link/20260117-feat-add-bitmask-macro-v9-1-45ea1f00f846@gmail.com [ Implemented missing `BitXorAssign<$flag> for $flags`. Sorted `impl`s. Removed prelude addition for now -- I asked the team and they also felt it wasn't needed. We can always add it later on if needed. Fixed intra-doc link (by removing the sentence since it was superfluous anyway). Simplified `empty()` title. Reworded commit slightly. Added docs to enum variants in example to avoid 'missing_docs' lint when used in actual code. - Miguel ] Signed-off-by: Miguel Ojeda --- rust/kernel/impl_flags.rs | 272 ++++++++++++++++++++++++++++++++++++++++++++++ rust/kernel/lib.rs | 2 + 2 files changed, 274 insertions(+) create mode 100644 rust/kernel/impl_flags.rs (limited to 'rust/kernel') diff --git a/rust/kernel/impl_flags.rs b/rust/kernel/impl_flags.rs new file mode 100644 index 000000000000..e2bd7639da12 --- /dev/null +++ b/rust/kernel/impl_flags.rs @@ -0,0 +1,272 @@ +// SPDX-License-Identifier: GPL-2.0 + +//! Bitflag type generator. + +/// Common helper for declaring bitflag and bitmask types. +/// +/// This macro takes as input: +/// - A struct declaration representing a bitmask type +/// (e.g., `pub struct Permissions(u32)`). +/// - An enumeration declaration representing individual bit flags +/// (e.g., `pub enum Permission { ... }`). +/// +/// And generates: +/// - The struct and enum types with appropriate `#[repr]` attributes. +/// - Implementations of common bitflag operators +/// ([`::core::ops::BitOr`], [`::core::ops::BitAnd`], etc.). +/// - Utility methods such as `.contains()` to check flags. +/// +/// # Examples +/// +/// ``` +/// use kernel::impl_flags; +/// +/// impl_flags!( +/// /// Represents multiple permissions. +/// #[derive(Debug, Clone, Default, Copy, PartialEq, Eq)] +/// pub struct Permissions(u32); +/// +/// /// Represents a single permission. +/// #[derive(Debug, Clone, Copy, PartialEq, Eq)] +/// pub enum Permission { +/// /// Read permission. +/// Read = 1 << 0, +/// +/// /// Write permission. +/// Write = 1 << 1, +/// +/// /// Execute permission. +/// Execute = 1 << 2, +/// } +/// ); +/// +/// // Combine multiple permissions using the bitwise OR (`|`) operator. +/// let mut read_write: Permissions = Permission::Read | Permission::Write; +/// assert!(read_write.contains(Permission::Read)); +/// assert!(read_write.contains(Permission::Write)); +/// assert!(!read_write.contains(Permission::Execute)); +/// assert!(read_write.contains_any(Permission::Read | Permission::Execute)); +/// assert!(read_write.contains_all(Permission::Read | Permission::Write)); +/// +/// // Using the bitwise OR assignment (`|=`) operator. +/// read_write |= Permission::Execute; +/// assert!(read_write.contains(Permission::Execute)); +/// +/// // Masking a permission with the bitwise AND (`&`) operator. +/// let read_only: Permissions = read_write & Permission::Read; +/// assert!(read_only.contains(Permission::Read)); +/// assert!(!read_only.contains(Permission::Write)); +/// +/// // Toggling permissions with the bitwise XOR (`^`) operator. +/// let toggled: Permissions = read_only ^ Permission::Read; +/// assert!(!toggled.contains(Permission::Read)); +/// +/// // Inverting permissions with the bitwise NOT (`!`) operator. +/// let negated = !read_only; +/// assert!(negated.contains(Permission::Write)); +/// assert!(!negated.contains(Permission::Read)); +/// ``` +#[macro_export] +macro_rules! impl_flags { + ( + $(#[$outer_flags:meta])* + $vis_flags:vis struct $flags:ident($ty:ty); + + $(#[$outer_flag:meta])* + $vis_flag:vis enum $flag:ident { + $( + $(#[$inner_flag:meta])* + $name:ident = $value:expr + ),+ $( , )? + } + ) => { + $(#[$outer_flags])* + #[repr(transparent)] + $vis_flags struct $flags($ty); + + $(#[$outer_flag])* + #[repr($ty)] + $vis_flag enum $flag { + $( + $(#[$inner_flag])* + $name = $value + ),+ + } + + impl ::core::convert::From<$flag> for $flags { + #[inline] + fn from(value: $flag) -> Self { + Self(value as $ty) + } + } + + impl ::core::convert::From<$flags> for $ty { + #[inline] + fn from(value: $flags) -> Self { + value.0 + } + } + + impl ::core::ops::BitOr for $flags { + type Output = Self; + #[inline] + fn bitor(self, rhs: Self) -> Self::Output { + Self(self.0 | rhs.0) + } + } + + impl ::core::ops::BitOrAssign for $flags { + #[inline] + fn bitor_assign(&mut self, rhs: Self) { + *self = *self | rhs; + } + } + + impl ::core::ops::BitOr<$flag> for $flags { + type Output = Self; + #[inline] + fn bitor(self, rhs: $flag) -> Self::Output { + self | Self::from(rhs) + } + } + + impl ::core::ops::BitOrAssign<$flag> for $flags { + #[inline] + fn bitor_assign(&mut self, rhs: $flag) { + *self = *self | rhs; + } + } + + impl ::core::ops::BitAnd for $flags { + type Output = Self; + #[inline] + fn bitand(self, rhs: Self) -> Self::Output { + Self(self.0 & rhs.0) + } + } + + impl ::core::ops::BitAndAssign for $flags { + #[inline] + fn bitand_assign(&mut self, rhs: Self) { + *self = *self & rhs; + } + } + + impl ::core::ops::BitAnd<$flag> for $flags { + type Output = Self; + #[inline] + fn bitand(self, rhs: $flag) -> Self::Output { + self & Self::from(rhs) + } + } + + impl ::core::ops::BitAndAssign<$flag> for $flags { + #[inline] + fn bitand_assign(&mut self, rhs: $flag) { + *self = *self & rhs; + } + } + + impl ::core::ops::BitXor for $flags { + type Output = Self; + #[inline] + fn bitxor(self, rhs: Self) -> Self::Output { + Self((self.0 ^ rhs.0) & Self::all_bits()) + } + } + + impl ::core::ops::BitXorAssign for $flags { + #[inline] + fn bitxor_assign(&mut self, rhs: Self) { + *self = *self ^ rhs; + } + } + + impl ::core::ops::BitXor<$flag> for $flags { + type Output = Self; + #[inline] + fn bitxor(self, rhs: $flag) -> Self::Output { + self ^ Self::from(rhs) + } + } + + impl ::core::ops::BitXorAssign<$flag> for $flags { + #[inline] + fn bitxor_assign(&mut self, rhs: $flag) { + *self = *self ^ rhs; + } + } + + impl ::core::ops::Not for $flags { + type Output = Self; + #[inline] + fn not(self) -> Self::Output { + Self((!self.0) & Self::all_bits()) + } + } + + impl ::core::ops::BitOr for $flag { + type Output = $flags; + #[inline] + fn bitor(self, rhs: Self) -> Self::Output { + $flags(self as $ty | rhs as $ty) + } + } + + impl ::core::ops::BitAnd for $flag { + type Output = $flags; + #[inline] + fn bitand(self, rhs: Self) -> Self::Output { + $flags(self as $ty & rhs as $ty) + } + } + + impl ::core::ops::BitXor for $flag { + type Output = $flags; + #[inline] + fn bitxor(self, rhs: Self) -> Self::Output { + $flags((self as $ty ^ rhs as $ty) & $flags::all_bits()) + } + } + + impl ::core::ops::Not for $flag { + type Output = $flags; + #[inline] + fn not(self) -> Self::Output { + $flags((!(self as $ty)) & $flags::all_bits()) + } + } + + impl $flags { + /// Returns an empty instance where no flags are set. + #[inline] + pub const fn empty() -> Self { + Self(0) + } + + /// Returns a mask containing all valid flag bits. + #[inline] + pub const fn all_bits() -> $ty { + 0 $( | $value )+ + } + + /// Checks if a specific flag is set. + #[inline] + pub fn contains(self, flag: $flag) -> bool { + (self.0 & flag as $ty) == flag as $ty + } + + /// Checks if at least one of the provided flags is set. + #[inline] + pub fn contains_any(self, flags: $flags) -> bool { + (self.0 & flags.0) != 0 + } + + /// Checks if all of the provided flags are set. + #[inline] + pub fn contains_all(self, flags: $flags) -> bool { + (self.0 & flags.0) == flags.0 + } + } + }; +} diff --git a/rust/kernel/lib.rs b/rust/kernel/lib.rs index f812cf120042..996affce2c9e 100644 --- a/rust/kernel/lib.rs +++ b/rust/kernel/lib.rs @@ -100,6 +100,8 @@ pub mod fs; #[cfg(CONFIG_I2C = "y")] pub mod i2c; pub mod id_pool; +#[doc(hidden)] +pub mod impl_flags; pub mod init; pub mod io; pub mod ioctl; -- cgit v1.2.3 From b8d687c7eeb52d0353ac27c4f71594a2e6aa365f Mon Sep 17 00:00:00 2001 From: Ritvik Gupta Date: Wed, 8 Oct 2025 03:20:28 +0530 Subject: rust: safety: introduce `unsafe_precondition_assert!` macro Introduce a new `safety` module containing `unsafe_precondition_assert!` macro. It is a wrapper around `debug_assert!`, intended for validating preconditions of unsafe function. When `CONFIG_RUST_DEBUG_ASSERTIONS` flag is enabled, this macro performs runtime checks to ensure that the preconditions for unsafe function hold. Otherwise, the macro is a no-op. Suggested-by: Miguel Ojeda Link: https://github.com/Rust-for-Linux/linux/issues/1162 Link: https://rust-for-linux.zulipchat.com/#narrow/channel/291566-Library/topic/.60unsafe_precondition_assert.60.20macro/with/528457452 Signed-off-by: Ritvik Gupta Reviewed-by: Benno Lossin Link: https://patch.msgid.link/20251007215034.213779-1-ritvikfoss@gmail.com [ Added trailing periods, intra-doc link, "a" in "is a no-op" and `()` to function reference. Removed plural in assertion message and title of macro. Reworded slightly. - Miguel ] Signed-off-by: Miguel Ojeda --- rust/kernel/lib.rs | 1 + rust/kernel/safety.rs | 53 +++++++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 54 insertions(+) create mode 100644 rust/kernel/safety.rs (limited to 'rust/kernel') diff --git a/rust/kernel/lib.rs b/rust/kernel/lib.rs index 996affce2c9e..696f62f85eb5 100644 --- a/rust/kernel/lib.rs +++ b/rust/kernel/lib.rs @@ -135,6 +135,7 @@ pub mod pwm; pub mod rbtree; pub mod regulator; pub mod revocable; +pub mod safety; pub mod scatterlist; pub mod security; pub mod seq_file; diff --git a/rust/kernel/safety.rs b/rust/kernel/safety.rs new file mode 100644 index 000000000000..c1c6bd0fa2cc --- /dev/null +++ b/rust/kernel/safety.rs @@ -0,0 +1,53 @@ +// SPDX-License-Identifier: GPL-2.0 + +//! Safety related APIs. + +/// Checks that a precondition of an unsafe function is followed. +/// +/// The check is enabled at runtime if debug assertions (`CONFIG_RUST_DEBUG_ASSERTIONS`) +/// are enabled. Otherwise, this macro is a no-op. +/// +/// # Examples +/// +/// ```no_run +/// use kernel::unsafe_precondition_assert; +/// +/// struct RawBuffer { +/// data: [T; N], +/// } +/// +/// impl RawBuffer { +/// /// # Safety +/// /// +/// /// The caller must ensure that `index` is less than `N`. +/// unsafe fn set_unchecked(&mut self, index: usize, value: T) { +/// unsafe_precondition_assert!( +/// index < N, +/// "RawBuffer::set_unchecked() requires index ({index}) < N ({N})" +/// ); +/// +/// // SAFETY: By the safety requirements of this function, `index` is valid. +/// unsafe { +/// *self.data.get_unchecked_mut(index) = value; +/// } +/// } +/// } +/// ``` +/// +/// # Panics +/// +/// Panics if the expression is evaluated to [`false`] at runtime. +#[macro_export] +macro_rules! unsafe_precondition_assert { + ($cond:expr $(,)?) => { + $crate::unsafe_precondition_assert!(@inner $cond, ::core::stringify!($cond)) + }; + + ($cond:expr, $($arg:tt)+) => { + $crate::unsafe_precondition_assert!(@inner $cond, $crate::prelude::fmt!($($arg)+)) + }; + + (@inner $cond:expr, $msg:expr) => { + ::core::debug_assert!($cond, "unsafe precondition violated: {}", $msg) + }; +} -- cgit v1.2.3 From ba268514ea14b44570030e8ed2aef92a38679e85 Mon Sep 17 00:00:00 2001 From: Danilo Krummrich Date: Thu, 5 Feb 2026 23:25:15 +0100 Subject: rust: devres: fix race condition due to nesting Commit f5d3ef25d238 ("rust: devres: get rid of Devres' inner Arc") did attempt to optimize away the internal reference count of Devres. However, without an internal reference count, we can't support cases where Devres is indirectly nested, resulting into a deadlock. Such indirect nesting easily happens in the following way: A registration object (which is guarded by devres) hold a reference count of an object that holds a device resource guarded by devres itself. For instance a drm::Registration holds a reference of a drm::Device. The drm::Device itself holds a device resource in its private data. When the drm::Registration is dropped by devres, and it happens that it did hold the last reference count of the drm::Device, it also drops the device resource, which is guarded by devres itself. Thus, resulting into a deadlock in the Devres destructor of the device resource, as in the following backtrace. sysrq: Show Blocked State task:rmmod state:D stack:0 pid:1331 tgid:1331 ppid:1330 task_flags:0x400100 flags:0x00000010 Call trace: __switch_to+0x190/0x294 (T) __schedule+0x878/0xf10 schedule+0x4c/0xcc schedule_timeout+0x44/0x118 wait_for_common+0xc0/0x18c wait_for_completion+0x18/0x24 _RINvNtCs4gKlGRWyJ5S_4core3ptr13drop_in_placeINtNtNtCsgzhNYVB7wSz_6kernel4sync3arc3ArcINtNtBN_6devres6DevresmEEECsRdyc7Hyps3_15rust_driver_pci+0x68/0xe8 [rust_driver_pci] _RINvNvNtCsgzhNYVB7wSz_6kernel6devres16register_foreign8callbackINtNtCs4gKlGRWyJ5S_4core3pin3PinINtNtNtB6_5alloc4kbox3BoxINtNtNtB6_4sync3arc3ArcINtB4_6DevresmEENtNtB1A_9allocator7KmallocEEECsRdyc7Hyps3_15rust_driver_pci+0x34/0xc8 [rust_driver_pci] devm_action_release+0x14/0x20 devres_release_all+0xb8/0x118 device_release_driver_internal+0x1c4/0x28c driver_detach+0x94/0xd4 bus_remove_driver+0xdc/0x11c driver_unregister+0x34/0x58 pci_unregister_driver+0x20/0x80 __arm64_sys_delete_module+0x1d8/0x254 invoke_syscall+0x40/0xcc el0_svc_common+0x8c/0xd8 do_el0_svc+0x1c/0x28 el0_svc+0x54/0x1d4 el0t_64_sync_handler+0x84/0x12c el0t_64_sync+0x198/0x19c In order to fix this, re-introduce the internal reference count. Reported-by: Boris Brezillon Closes: https://rust-for-linux.zulipchat.com/#narrow/channel/288089-General/topic/.E2.9C.94.20Deadlock.20caused.20by.20nested.20Devres/with/571242651 Reported-by: Markus Probst Closes: https://rust-for-linux.zulipchat.com/#narrow/channel/288089-General/topic/.E2.9C.94.20Devres.20inside.20Devres.20stuck.20on.20cleanup/with/571239721 Reported-by: Alice Ryhl Closes: https://gitlab.freedesktop.org/panfrost/linux/-/merge_requests/56#note_3282757 Fixes: f5d3ef25d238 ("rust: devres: get rid of Devres' inner Arc") Reviewed-by: Greg Kroah-Hartman Reviewed-by: Alice Ryhl Tested-by: Boris Brezillon Link: https://patch.msgid.link/20260205222529.91465-1-dakr@kernel.org [ Call clone() prior to devm_add_action(). - Danilo ] Signed-off-by: Danilo Krummrich --- rust/kernel/devres.rs | 149 ++++++++++++++------------------------------------ 1 file changed, 40 insertions(+), 109 deletions(-) (limited to 'rust/kernel') diff --git a/rust/kernel/devres.rs b/rust/kernel/devres.rs index cdc49677022a..6afe196be42c 100644 --- a/rust/kernel/devres.rs +++ b/rust/kernel/devres.rs @@ -21,30 +21,11 @@ use crate::{ sync::{ aref::ARef, rcu, - Completion, // - }, - types::{ - ForeignOwnable, - Opaque, - ScopeGuard, // + Arc, // }, + types::ForeignOwnable, }; -use pin_init::Wrapper; - -/// [`Devres`] inner data accessed from [`Devres::callback`]. -#[pin_data] -struct Inner { - #[pin] - data: Revocable, - /// Tracks whether [`Devres::callback`] has been completed. - #[pin] - devm: Completion, - /// Tracks whether revoking [`Self::data`] has been completed. - #[pin] - revoke: Completion, -} - /// This abstraction is meant to be used by subsystems to containerize [`Device`] bound resources to /// manage their lifetime. /// @@ -121,18 +102,13 @@ struct Inner { /// # fn no_run(dev: &Device) -> Result<(), Error> { /// // SAFETY: Invalid usage for example purposes. /// let iomem = unsafe { IoMem::<{ core::mem::size_of::() }>::new(0xBAAAAAAD)? }; -/// let devres = KBox::pin_init(Devres::new(dev, iomem), GFP_KERNEL)?; +/// let devres = Devres::new(dev, iomem)?; /// /// let res = devres.try_access().ok_or(ENXIO)?; /// res.write8(0x42, 0x0); /// # Ok(()) /// # } /// ``` -/// -/// # Invariants -/// -/// `Self::inner` is guaranteed to be initialized and is always accessed read-only. -#[pin_data(PinnedDrop)] pub struct Devres { dev: ARef, /// Pointer to [`Self::devres_callback`]. @@ -140,14 +116,7 @@ pub struct Devres { /// Has to be stored, since Rust does not guarantee to always return the same address for a /// function. However, the C API uses the address as a key. callback: unsafe extern "C" fn(*mut c_void), - /// Contains all the fields shared with [`Self::callback`]. - // TODO: Replace with `UnsafePinned`, once available. - // - // Subsequently, the `drop_in_place()` in `Devres::drop` and `Devres::new` as well as the - // explicit `Send` and `Sync' impls can be removed. - #[pin] - inner: Opaque>, - _add_action: (), + data: Arc>, } impl Devres { @@ -155,74 +124,48 @@ impl Devres { /// /// The `data` encapsulated within the returned `Devres` instance' `data` will be /// (revoked)[`Revocable`] once the device is detached. - pub fn new<'a, E>( - dev: &'a Device, - data: impl PinInit + 'a, - ) -> impl PinInit + 'a + pub fn new(dev: &Device, data: impl PinInit) -> Result where - T: 'a, Error: From, { - try_pin_init!(&this in Self { - dev: dev.into(), - callback: Self::devres_callback, - // INVARIANT: `inner` is properly initialized. - inner <- Opaque::pin_init(try_pin_init!(Inner { - devm <- Completion::new(), - revoke <- Completion::new(), - data <- Revocable::new(data), - })), - // TODO: Replace with "initializer code blocks" [1] once available. - // - // [1] https://github.com/Rust-for-Linux/pin-init/pull/69 - _add_action: { - // SAFETY: `this` is a valid pointer to uninitialized memory. - let inner = unsafe { &raw mut (*this.as_ptr()).inner }; + let callback = Self::devres_callback; + let data = Arc::pin_init(Revocable::new(data), GFP_KERNEL)?; + let devres_data = data.clone(); - // SAFETY: - // - `dev.as_raw()` is a pointer to a valid bound device. - // - `inner` is guaranteed to be a valid for the duration of the lifetime of `Self`. - // - `devm_add_action()` is guaranteed not to call `callback` until `this` has been - // properly initialized, because we require `dev` (i.e. the *bound* device) to - // live at least as long as the returned `impl PinInit`. - to_result(unsafe { - bindings::devm_add_action(dev.as_raw(), Some(*callback), inner.cast()) - }).inspect_err(|_| { - let inner = Opaque::cast_into(inner); + // SAFETY: + // - `dev.as_raw()` is a pointer to a valid bound device. + // - `data` is guaranteed to be a valid for the duration of the lifetime of `Self`. + // - `devm_add_action()` is guaranteed not to call `callback` for the entire lifetime of + // `dev`. + to_result(unsafe { + bindings::devm_add_action( + dev.as_raw(), + Some(callback), + Arc::as_ptr(&data).cast_mut().cast(), + ) + })?; - // SAFETY: `inner` is a valid pointer to an `Inner` and valid for both reads - // and writes. - unsafe { core::ptr::drop_in_place(inner) }; - })?; - }, - }) - } + // `devm_add_action()` was successful and has consumed the reference count. + core::mem::forget(devres_data); - fn inner(&self) -> &Inner { - // SAFETY: By the type invairants of `Self`, `inner` is properly initialized and always - // accessed read-only. - unsafe { &*self.inner.get() } + Ok(Self { + dev: dev.into(), + callback, + data, + }) } fn data(&self) -> &Revocable { - &self.inner().data + &self.data } #[allow(clippy::missing_safety_doc)] unsafe extern "C" fn devres_callback(ptr: *mut kernel::ffi::c_void) { - // SAFETY: In `Self::new` we've passed a valid pointer to `Inner` to `devm_add_action()`, - // hence `ptr` must be a valid pointer to `Inner`. - let inner = unsafe { &*ptr.cast::>() }; + // SAFETY: In `Self::new` we've passed a valid pointer of `Revocable` to + // `devm_add_action()`, hence `ptr` must be a valid pointer to `Revocable`. + let data = unsafe { Arc::from_raw(ptr.cast::>()) }; - // Ensure that `inner` can't be used anymore after we signal completion of this callback. - let inner = ScopeGuard::new_with_data(inner, |inner| inner.devm.complete_all()); - - if !inner.data.revoke() { - // If `revoke()` returns false, it means that `Devres::drop` already started revoking - // `data` for us. Hence we have to wait until `Devres::drop` signals that it - // completed revoking `data`. - inner.revoke.wait_for_completion(); - } + data.revoke(); } fn remove_action(&self) -> bool { @@ -234,7 +177,7 @@ impl Devres { bindings::devm_remove_action_nowarn( self.dev.as_raw(), Some(self.callback), - core::ptr::from_ref(self.inner()).cast_mut().cast(), + core::ptr::from_ref(self.data()).cast_mut().cast(), ) } == 0) } @@ -313,31 +256,19 @@ unsafe impl Send for Devres {} // SAFETY: `Devres` can be shared with any task, if `T: Sync`. unsafe impl Sync for Devres {} -#[pinned_drop] -impl PinnedDrop for Devres { - fn drop(self: Pin<&mut Self>) { +impl Drop for Devres { + fn drop(&mut self) { // SAFETY: When `drop` runs, it is guaranteed that nobody is accessing the revocable data // anymore, hence it is safe not to wait for the grace period to finish. if unsafe { self.data().revoke_nosync() } { // We revoked `self.data` before the devres action did, hence try to remove it. - if !self.remove_action() { - // We could not remove the devres action, which means that it now runs concurrently, - // hence signal that `self.data` has been revoked by us successfully. - self.inner().revoke.complete_all(); - - // Wait for `Self::devres_callback` to be done using this object. - self.inner().devm.wait_for_completion(); + if self.remove_action() { + // SAFETY: In `Self::new` we have taken an additional reference count of `self.data` + // for `devm_add_action()`. Since `remove_action()` was successful, we have to drop + // this additional reference count. + drop(unsafe { Arc::from_raw(Arc::as_ptr(&self.data)) }); } - } else { - // `Self::devres_callback` revokes `self.data` for us, hence wait for it to be done - // using this object. - self.inner().devm.wait_for_completion(); } - - // INVARIANT: At this point it is guaranteed that `inner` can't be accessed any more. - // - // SAFETY: `inner` is valid for dropping. - unsafe { core::ptr::drop_in_place(self.inner.get()) }; } } -- cgit v1.2.3 From 621609f1e5ca43a75edd497dd1c28bd84aa66433 Mon Sep 17 00:00:00 2001 From: Benno Lossin Date: Sat, 14 Feb 2026 10:27:40 +0100 Subject: rust: irq: add `'static` bounds to irq callbacks These callback functions take a generic `T` that is used in the body as the generic argument in `Registration` and `ThreadedRegistration`. Those types require `T: 'static`, but due to a compiler bug this requirement isn't propagated to the function. Thus add the bound. This was caught in the upstream Rust CI [1]. [ The three errors looked similar and will start appearing with Rust 1.95.0 (expected 2026-04-16). The first one was: error[E0310]: the parameter type `T` may not live long enough Error: --> rust/kernel/irq/request.rs:266:43 | 266 | let registration = unsafe { &*(ptr as *const Registration) }; | ^^^^^^^^^^^^^^^^^^^^^^ | | | the parameter type `T` must be valid for the static lifetime... | ...so that the type `T` will meet its required lifetime bounds | help: consider adding an explicit lifetime bound | 264 | unsafe extern "C" fn handle_irq_callback(_irq: i32, ptr: *mut c_void) -> c_uint { | +++++++++ - Miguel ] Link: https://github.com/rust-lang/rust/pull/149389 [1] Signed-off-by: Benno Lossin Cc: stable@vger.kernel.org Fixes: 29e16fcd67ee ("rust: irq: add &Device argument to irq callbacks") Reviewed-by: Gary Guo Reviewed-by: Daniel Almeida Acked-by: Danilo Krummrich Link: https://lore.kernel.org/rust-for-linux/20260217222425.8755-1-cole@unwrap.rs/ Link: https://patch.msgid.link/20260214092740.3201946-1-lossin@kernel.org Signed-off-by: Miguel Ojeda --- rust/kernel/irq/request.rs | 12 +++++++++--- 1 file changed, 9 insertions(+), 3 deletions(-) (limited to 'rust/kernel') diff --git a/rust/kernel/irq/request.rs b/rust/kernel/irq/request.rs index b150563fdef8..2ceeaeb0543a 100644 --- a/rust/kernel/irq/request.rs +++ b/rust/kernel/irq/request.rs @@ -261,7 +261,10 @@ impl Registration { /// # Safety /// /// This function should be only used as the callback in `request_irq`. -unsafe extern "C" fn handle_irq_callback(_irq: i32, ptr: *mut c_void) -> c_uint { +unsafe extern "C" fn handle_irq_callback( + _irq: i32, + ptr: *mut c_void, +) -> c_uint { // SAFETY: `ptr` is a pointer to `Registration` set in `Registration::new` let registration = unsafe { &*(ptr as *const Registration) }; // SAFETY: The irq callback is removed before the device is unbound, so the fact that the irq @@ -480,7 +483,7 @@ impl ThreadedRegistration { /// # Safety /// /// This function should be only used as the callback in `request_threaded_irq`. -unsafe extern "C" fn handle_threaded_irq_callback( +unsafe extern "C" fn handle_threaded_irq_callback( _irq: i32, ptr: *mut c_void, ) -> c_uint { @@ -496,7 +499,10 @@ unsafe extern "C" fn handle_threaded_irq_callback( /// # Safety /// /// This function should be only used as the callback in `request_threaded_irq`. -unsafe extern "C" fn thread_fn_callback(_irq: i32, ptr: *mut c_void) -> c_uint { +unsafe extern "C" fn thread_fn_callback( + _irq: i32, + ptr: *mut c_void, +) -> c_uint { // SAFETY: `ptr` is a pointer to `ThreadedRegistration` set in `ThreadedRegistration::new` let registration = unsafe { &*(ptr as *const ThreadedRegistration) }; // SAFETY: The irq callback is removed before the device is unbound, so the fact that the irq -- cgit v1.2.3 From 97b281d7edb2ae662365be2809cd728470119720 Mon Sep 17 00:00:00 2001 From: Philipp Stanner Date: Mon, 16 Feb 2026 14:16:15 +0100 Subject: rust: list: Add unsafe blocks for container_of and safety comments impl_list_item_mod.rs calls container_of! without unsafe blocks at a couple of places. Since container_of! is unsafe, the blocks are strictly necessary. The problem was so far not visible because the "unsafe-op-in-unsafe-fn" check is a lint rather than a hard compiler error, and Rust suppresses lints triggered inside of a macro from another crate. Thus, the error becomes only visible once someone from within the kernel crate tries to use linked lists: error[E0133]: call to unsafe function `core::ptr::mut_ptr::::byte_sub` is unsafe and requires unsafe block --> rust/kernel/lib.rs:252:29 | 252 | let container_ptr = field_ptr.byte_sub(offset).cast::<$Container>(); | ^^^^^^^^^^^^^^^^^^^^^^^^^^ call to unsafe function | ::: rust/kernel/drm/jq.rs:98:1 | 98 | / impl_list_item! { 99 | | impl ListItem<0> for BasicItem { using ListLinks { self.links }; } 100 | | } | |_- in this macro invocation | note: an unsafe function restricts its caller, but its body is safe by default --> rust/kernel/list/impl_list_item_mod.rs:216:13 | 216 | unsafe fn view_value(me: *mut $crate::list::ListLinks<$num>) -> *const Self { | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ | ::: rust/kernel/drm/jq.rs:98:1 | 98 | / impl_list_item! { 99 | | impl ListItem<0> for BasicItem { using ListLinks { self.links }; } 100 | | } | |_- in this macro invocation = note: requested on the command line with `-D unsafe-op-in-unsafe-fn` = note: this error originates in the macro `$crate::container_of` which comes from the expansion of the macro `impl_list_item` Therefore, add unsafe blocks to container_of! calls to fix the issue. [ As discussed, let's fix the build for those that want to use the macro within the `kernel` crate now and we can discuss the proper safety comments afterwards. Thus I removed the ones from the patch. However, we cannot just avoid the comments with `CLIPPY=1`, so I provided placeholders for now, like we did in the past. They were also needed for an `unsafe impl`. While I am not happy about it, it isn't worse than the current status (the comments were meant to be there), and at least this shows what is missing -- our pre-existing "good first issue" [1] may motivate new contributors to complete them properly. Finally, I moved one of the existing safety comments one line down so that Clippy could locate it. Link: https://github.com/Rust-for-Linux/linux/issues/351 [1] - Miguel ] Cc: stable@vger.kernel.org Fixes: c77f85b347dd ("rust: list: remove OFFSET constants") Suggested-by: Alice Ryhl Signed-off-by: Philipp Stanner Reviewed-by: Gary Guo Reviewed-by: Alice Ryhl Link: https://patch.msgid.link/20260216131613.45344-3-phasta@kernel.org [ Fixed formatting. Reworded to fix the lint suppression explanation. Indent build error. - Miguel ] Signed-off-by: Miguel Ojeda --- rust/kernel/list/impl_list_item_mod.rs | 25 ++++++++++++++++--------- 1 file changed, 16 insertions(+), 9 deletions(-) (limited to 'rust/kernel') diff --git a/rust/kernel/list/impl_list_item_mod.rs b/rust/kernel/list/impl_list_item_mod.rs index 202bc6f97c13..ee53d0387e63 100644 --- a/rust/kernel/list/impl_list_item_mod.rs +++ b/rust/kernel/list/impl_list_item_mod.rs @@ -84,11 +84,12 @@ macro_rules! impl_has_list_links_self_ptr { // right type. unsafe impl$(<$($generics)*>)? $crate::list::HasSelfPtr<$item_type $(, $id)?> for $self {} + // SAFETY: TODO. unsafe impl$(<$($generics)*>)? $crate::list::HasListLinks$(<$id>)? for $self { #[inline] unsafe fn raw_get_list_links(ptr: *mut Self) -> *mut $crate::list::ListLinks$(<$id>)? { - // SAFETY: The caller promises that the pointer is not dangling. let ptr: *mut $crate::list::ListLinksSelfPtr<$item_type $(, $id)?> = + // SAFETY: The caller promises that the pointer is not dangling. unsafe { ::core::ptr::addr_of_mut!((*ptr)$(.$field)*) }; ptr.cast() } @@ -217,7 +218,7 @@ macro_rules! impl_list_item { // SAFETY: `me` originates from the most recent call to `prepare_to_insert`, so it // points at the field `$field` in a value of type `Self`. Thus, reversing that // operation is still in-bounds of the allocation. - $crate::container_of!(me, Self, $($field).*) + unsafe { $crate::container_of!(me, Self, $($field).*) } } // GUARANTEES: @@ -242,7 +243,7 @@ macro_rules! impl_list_item { // SAFETY: `me` originates from the most recent call to `prepare_to_insert`, so it // points at the field `$field` in a value of type `Self`. Thus, reversing that // operation is still in-bounds of the allocation. - $crate::container_of!(me, Self, $($field).*) + unsafe { $crate::container_of!(me, Self, $($field).*) } } } )*}; @@ -270,9 +271,12 @@ macro_rules! impl_list_item { // SAFETY: The caller promises that `me` points at a valid value of type `Self`. let links_field = unsafe { >::view_links(me) }; - let container = $crate::container_of!( - links_field, $crate::list::ListLinksSelfPtr, inner - ); + // SAFETY: TODO. + let container = unsafe { + $crate::container_of!( + links_field, $crate::list::ListLinksSelfPtr, inner + ) + }; // SAFETY: By the same reasoning above, `links_field` is a valid pointer. let self_ptr = unsafe { @@ -319,9 +323,12 @@ macro_rules! impl_list_item { // `ListArc` containing `Self` until the next call to `post_remove`. The value cannot // be destroyed while a `ListArc` reference exists. unsafe fn view_value(links_field: *mut $crate::list::ListLinks<$num>) -> *const Self { - let container = $crate::container_of!( - links_field, $crate::list::ListLinksSelfPtr, inner - ); + // SAFETY: TODO. + let container = unsafe { + $crate::container_of!( + links_field, $crate::list::ListLinksSelfPtr, inner + ) + }; // SAFETY: By the same reasoning above, `links_field` is a valid pointer. let self_ptr = unsafe { -- cgit v1.2.3 From bfcaf4e8ae9b4f52b97fb04ce03be3a01d41cdd0 Mon Sep 17 00:00:00 2001 From: Danilo Krummrich Date: Mon, 16 Feb 2026 14:14:33 +0100 Subject: rust: io: macro_export io_define_read!() and io_define_write!() Currently, the define_read!() and define_write!() I/O macros are crate public. The only user outside of the I/O module is PCI (for the configurations space I/O backend). Consequently, when CONFIG_PCI=n this causes a compile time warning [1]. In order to fix this, rename the macros to io_define_read!() and io_define_write!() and use #[macro_export] to export them. This is better than making the crate public visibility conditional, as eventually subsystems will have their own crate. Also, I/O backends are valid to be implemented by drivers as well. For instance, there are devices (such as GPUs) that run firmware which allows to program other devices only accessible through the primary device through indirect I/O. Since the macros are now public, also add the corresponding documentation. Fixes: 121d87b28e1d ("rust: io: separate generic I/O helpers from MMIO implementation") Reported-by: Miguel Ojeda Closes: https://lore.kernel.org/driver-core/CANiq72khOYkt6t5zwMvSiyZvWWHMZuNCMERXu=7K=_5tT-8Pgg@mail.gmail.com/ [1] Reviewed-by: Alice Ryhl Reviewed-by: Daniel Almeida Link: https://patch.msgid.link/20260216131534.65008-1-dakr@kernel.org Signed-off-by: Danilo Krummrich --- rust/kernel/io.rs | 131 ++++++++++++++++++++++++++++++++++---------------- rust/kernel/pci/io.rs | 24 ++++----- 2 files changed, 101 insertions(+), 54 deletions(-) (limited to 'rust/kernel') diff --git a/rust/kernel/io.rs b/rust/kernel/io.rs index c1cca7b438c3..e5fba6bf6db0 100644 --- a/rust/kernel/io.rs +++ b/rust/kernel/io.rs @@ -139,9 +139,9 @@ pub struct Mmio(MmioRaw); /// Internal helper macros used to invoke C MMIO read functions. /// -/// This macro is intended to be used by higher-level MMIO access macros (define_read) and provides -/// a unified expansion for infallible vs. fallible read semantics. It emits a direct call into the -/// corresponding C helper and performs the required cast to the Rust return type. +/// This macro is intended to be used by higher-level MMIO access macros (io_define_read) and +/// provides a unified expansion for infallible vs. fallible read semantics. It emits a direct call +/// into the corresponding C helper and performs the required cast to the Rust return type. /// /// # Parameters /// @@ -166,9 +166,9 @@ macro_rules! call_mmio_read { /// Internal helper macros used to invoke C MMIO write functions. /// -/// This macro is intended to be used by higher-level MMIO access macros (define_write) and provides -/// a unified expansion for infallible vs. fallible write semantics. It emits a direct call into the -/// corresponding C helper and performs the required cast to the Rust return type. +/// This macro is intended to be used by higher-level MMIO access macros (io_define_write) and +/// provides a unified expansion for infallible vs. fallible write semantics. It emits a direct call +/// into the corresponding C helper and performs the required cast to the Rust return type. /// /// # Parameters /// @@ -193,7 +193,30 @@ macro_rules! call_mmio_write { }}; } -macro_rules! define_read { +/// Generates an accessor method for reading from an I/O backend. +/// +/// This macro reduces boilerplate by automatically generating either compile-time bounds-checked +/// (infallible) or runtime bounds-checked (fallible) read methods. It abstracts the address +/// calculation and bounds checking, and delegates the actual I/O read operation to a specified +/// helper macro, making it generic over different I/O backends. +/// +/// # Parameters +/// +/// * `infallible` / `fallible` - Determines the bounds-checking strategy. `infallible` relies on +/// `IoKnownSize` for compile-time checks and returns the value directly. `fallible` performs +/// runtime checks against `maxsize()` and returns a `Result`. +/// * `$(#[$attr:meta])*` - Optional attributes to apply to the generated method (e.g., +/// `#[cfg(CONFIG_64BIT)]` or inline directives). +/// * `$vis:vis` - The visibility of the generated method (e.g., `pub`). +/// * `$name:ident` / `$try_name:ident` - The name of the generated method (e.g., `read32`, +/// `try_read8`). +/// * `$call_macro:ident` - The backend-specific helper macro used to emit the actual I/O call +/// (e.g., `call_mmio_read`). +/// * `$c_fn:ident` - The backend-specific C function or identifier to be passed into the +/// `$call_macro`. +/// * `$type_name:ty` - The Rust type of the value being read (e.g., `u8`, `u32`). +#[macro_export] +macro_rules! io_define_read { (infallible, $(#[$attr:meta])* $vis:vis $name:ident, $call_macro:ident($c_fn:ident) -> $type_name:ty) => { /// Read IO data from a given offset known at compile time. @@ -226,9 +249,33 @@ macro_rules! define_read { } }; } -pub(crate) use define_read; +pub use io_define_read; -macro_rules! define_write { +/// Generates an accessor method for writing to an I/O backend. +/// +/// This macro reduces boilerplate by automatically generating either compile-time bounds-checked +/// (infallible) or runtime bounds-checked (fallible) write methods. It abstracts the address +/// calculation and bounds checking, and delegates the actual I/O write operation to a specified +/// helper macro, making it generic over different I/O backends. +/// +/// # Parameters +/// +/// * `infallible` / `fallible` - Determines the bounds-checking strategy. `infallible` relies on +/// `IoKnownSize` for compile-time checks and returns `()`. `fallible` performs runtime checks +/// against `maxsize()` and returns a `Result`. +/// * `$(#[$attr:meta])*` - Optional attributes to apply to the generated method (e.g., +/// `#[cfg(CONFIG_64BIT)]` or inline directives). +/// * `$vis:vis` - The visibility of the generated method (e.g., `pub`). +/// * `$name:ident` / `$try_name:ident` - The name of the generated method (e.g., `write32`, +/// `try_write8`). +/// * `$call_macro:ident` - The backend-specific helper macro used to emit the actual I/O call +/// (e.g., `call_mmio_write`). +/// * `$c_fn:ident` - The backend-specific C function or identifier to be passed into the +/// `$call_macro`. +/// * `$type_name:ty` - The Rust type of the value being written (e.g., `u8`, `u32`). Note the use +/// of `<-` before the type to denote a write operation. +#[macro_export] +macro_rules! io_define_write { (infallible, $(#[$attr:meta])* $vis:vis $name:ident, $call_macro:ident($c_fn:ident) <- $type_name:ty) => { /// Write IO data from a given offset known at compile time. @@ -259,7 +306,7 @@ macro_rules! define_write { } }; } -pub(crate) use define_write; +pub use io_define_write; /// Checks whether an access of type `U` at the given `offset` /// is valid within this region. @@ -509,40 +556,40 @@ impl Io for Mmio { self.0.maxsize() } - define_read!(fallible, try_read8, call_mmio_read(readb) -> u8); - define_read!(fallible, try_read16, call_mmio_read(readw) -> u16); - define_read!(fallible, try_read32, call_mmio_read(readl) -> u32); - define_read!( + io_define_read!(fallible, try_read8, call_mmio_read(readb) -> u8); + io_define_read!(fallible, try_read16, call_mmio_read(readw) -> u16); + io_define_read!(fallible, try_read32, call_mmio_read(readl) -> u32); + io_define_read!( fallible, #[cfg(CONFIG_64BIT)] try_read64, call_mmio_read(readq) -> u64 ); - define_write!(fallible, try_write8, call_mmio_write(writeb) <- u8); - define_write!(fallible, try_write16, call_mmio_write(writew) <- u16); - define_write!(fallible, try_write32, call_mmio_write(writel) <- u32); - define_write!( + io_define_write!(fallible, try_write8, call_mmio_write(writeb) <- u8); + io_define_write!(fallible, try_write16, call_mmio_write(writew) <- u16); + io_define_write!(fallible, try_write32, call_mmio_write(writel) <- u32); + io_define_write!( fallible, #[cfg(CONFIG_64BIT)] try_write64, call_mmio_write(writeq) <- u64 ); - define_read!(infallible, read8, call_mmio_read(readb) -> u8); - define_read!(infallible, read16, call_mmio_read(readw) -> u16); - define_read!(infallible, read32, call_mmio_read(readl) -> u32); - define_read!( + io_define_read!(infallible, read8, call_mmio_read(readb) -> u8); + io_define_read!(infallible, read16, call_mmio_read(readw) -> u16); + io_define_read!(infallible, read32, call_mmio_read(readl) -> u32); + io_define_read!( infallible, #[cfg(CONFIG_64BIT)] read64, call_mmio_read(readq) -> u64 ); - define_write!(infallible, write8, call_mmio_write(writeb) <- u8); - define_write!(infallible, write16, call_mmio_write(writew) <- u16); - define_write!(infallible, write32, call_mmio_write(writel) <- u32); - define_write!( + io_define_write!(infallible, write8, call_mmio_write(writeb) <- u8); + io_define_write!(infallible, write16, call_mmio_write(writew) <- u16); + io_define_write!(infallible, write32, call_mmio_write(writel) <- u32); + io_define_write!( infallible, #[cfg(CONFIG_64BIT)] write64, @@ -566,40 +613,40 @@ impl Mmio { unsafe { &*core::ptr::from_ref(raw).cast() } } - define_read!(infallible, pub read8_relaxed, call_mmio_read(readb_relaxed) -> u8); - define_read!(infallible, pub read16_relaxed, call_mmio_read(readw_relaxed) -> u16); - define_read!(infallible, pub read32_relaxed, call_mmio_read(readl_relaxed) -> u32); - define_read!( + io_define_read!(infallible, pub read8_relaxed, call_mmio_read(readb_relaxed) -> u8); + io_define_read!(infallible, pub read16_relaxed, call_mmio_read(readw_relaxed) -> u16); + io_define_read!(infallible, pub read32_relaxed, call_mmio_read(readl_relaxed) -> u32); + io_define_read!( infallible, #[cfg(CONFIG_64BIT)] pub read64_relaxed, call_mmio_read(readq_relaxed) -> u64 ); - define_read!(fallible, pub try_read8_relaxed, call_mmio_read(readb_relaxed) -> u8); - define_read!(fallible, pub try_read16_relaxed, call_mmio_read(readw_relaxed) -> u16); - define_read!(fallible, pub try_read32_relaxed, call_mmio_read(readl_relaxed) -> u32); - define_read!( + io_define_read!(fallible, pub try_read8_relaxed, call_mmio_read(readb_relaxed) -> u8); + io_define_read!(fallible, pub try_read16_relaxed, call_mmio_read(readw_relaxed) -> u16); + io_define_read!(fallible, pub try_read32_relaxed, call_mmio_read(readl_relaxed) -> u32); + io_define_read!( fallible, #[cfg(CONFIG_64BIT)] pub try_read64_relaxed, call_mmio_read(readq_relaxed) -> u64 ); - define_write!(infallible, pub write8_relaxed, call_mmio_write(writeb_relaxed) <- u8); - define_write!(infallible, pub write16_relaxed, call_mmio_write(writew_relaxed) <- u16); - define_write!(infallible, pub write32_relaxed, call_mmio_write(writel_relaxed) <- u32); - define_write!( + io_define_write!(infallible, pub write8_relaxed, call_mmio_write(writeb_relaxed) <- u8); + io_define_write!(infallible, pub write16_relaxed, call_mmio_write(writew_relaxed) <- u16); + io_define_write!(infallible, pub write32_relaxed, call_mmio_write(writel_relaxed) <- u32); + io_define_write!( infallible, #[cfg(CONFIG_64BIT)] pub write64_relaxed, call_mmio_write(writeq_relaxed) <- u64 ); - define_write!(fallible, pub try_write8_relaxed, call_mmio_write(writeb_relaxed) <- u8); - define_write!(fallible, pub try_write16_relaxed, call_mmio_write(writew_relaxed) <- u16); - define_write!(fallible, pub try_write32_relaxed, call_mmio_write(writel_relaxed) <- u32); - define_write!( + io_define_write!(fallible, pub try_write8_relaxed, call_mmio_write(writeb_relaxed) <- u8); + io_define_write!(fallible, pub try_write16_relaxed, call_mmio_write(writew_relaxed) <- u16); + io_define_write!(fallible, pub try_write32_relaxed, call_mmio_write(writel_relaxed) <- u32); + io_define_write!( fallible, #[cfg(CONFIG_64BIT)] pub try_write64_relaxed, diff --git a/rust/kernel/pci/io.rs b/rust/kernel/pci/io.rs index 6ca4cf75594c..fb6edab2aea7 100644 --- a/rust/kernel/pci/io.rs +++ b/rust/kernel/pci/io.rs @@ -8,8 +8,8 @@ use crate::{ device, devres::Devres, io::{ - define_read, - define_write, + io_define_read, + io_define_write, Io, IoCapable, IoKnownSize, @@ -88,7 +88,7 @@ pub struct ConfigSpace<'a, S: ConfigSpaceKind = Extended> { /// Internal helper macros used to invoke C PCI configuration space read functions. /// /// This macro is intended to be used by higher-level PCI configuration space access macros -/// (define_read) and provides a unified expansion for infallible vs. fallible read semantics. It +/// (io_define_read) and provides a unified expansion for infallible vs. fallible read semantics. It /// emits a direct call into the corresponding C helper and performs the required cast to the Rust /// return type. /// @@ -117,9 +117,9 @@ macro_rules! call_config_read { /// Internal helper macros used to invoke C PCI configuration space write functions. /// /// This macro is intended to be used by higher-level PCI configuration space access macros -/// (define_write) and provides a unified expansion for infallible vs. fallible read semantics. It -/// emits a direct call into the corresponding C helper and performs the required cast to the Rust -/// return type. +/// (io_define_write) and provides a unified expansion for infallible vs. fallible read semantics. +/// It emits a direct call into the corresponding C helper and performs the required cast to the +/// Rust return type. /// /// # Parameters /// @@ -163,13 +163,13 @@ impl<'a, S: ConfigSpaceKind> Io for ConfigSpace<'a, S> { // PCI configuration space does not support fallible operations. // The default implementations from the Io trait are not used. - define_read!(infallible, read8, call_config_read(pci_read_config_byte) -> u8); - define_read!(infallible, read16, call_config_read(pci_read_config_word) -> u16); - define_read!(infallible, read32, call_config_read(pci_read_config_dword) -> u32); + io_define_read!(infallible, read8, call_config_read(pci_read_config_byte) -> u8); + io_define_read!(infallible, read16, call_config_read(pci_read_config_word) -> u16); + io_define_read!(infallible, read32, call_config_read(pci_read_config_dword) -> u32); - define_write!(infallible, write8, call_config_write(pci_write_config_byte) <- u8); - define_write!(infallible, write16, call_config_write(pci_write_config_word) <- u16); - define_write!(infallible, write32, call_config_write(pci_write_config_dword) <- u32); + io_define_write!(infallible, write8, call_config_write(pci_write_config_byte) <- u8); + io_define_write!(infallible, write16, call_config_write(pci_write_config_word) <- u16); + io_define_write!(infallible, write32, call_config_write(pci_write_config_dword) <- u32); } impl<'a, S: ConfigSpaceKind> IoKnownSize for ConfigSpace<'a, S> { -- cgit v1.2.3