* [PATCH 1/2] rust: kernel: add cfg_select! backport for config-based selection
2026-06-29 3:51 [PATCH 0/2] rust: kernel: add cfg_select! and use it for config-based selection Nika Krasnova
@ 2026-06-29 3:51 ` Nika Krasnova
2026-06-29 3:51 ` [PATCH 2/2] rust: kernel: migrate to cfg_select! " Nika Krasnova
2026-06-29 9:38 ` [PATCH 0/2] rust: kernel: add cfg_select! and use it " Miguel Ojeda
2 siblings, 0 replies; 8+ messages in thread
From: Nika Krasnova @ 2026-06-29 3:51 UTC (permalink / raw)
To: Miguel Ojeda, Boqun Feng, Gary Guo, Björn Roy Baron,
Benno Lossin, Andreas Hindborg, Alice Ryhl, Trevor Gross,
Danilo Krummrich
Cc: Daniel Almeida, Tamir Duberstein, Alexandre Courbot,
Onur Özkan, Viresh Kumar, Yury Norov, Lorenzo Stoakes,
Brendan Higgins, David Gow, Rae Moar, rust-for-linux,
linux-kernel, kunit-dev, linux-kselftest, Nika Krasnova
Conditional compilation in the kernel's Rust code is currently expressed
with paired #[cfg(CONFIG_FOO)] / #[cfg(not(CONFIG_FOO))] attributes,
which is verbose and easy to get out of sync when selecting between two
or more implementations of an item.
The standard library gained core::cfg_select! for exactly this (stable
since Rust 1.95.0): a single macro that emits the first arm whose cfg
predicate holds. The kernel's minimum supported Rust version is 1.85.0,
so that macro is not yet available. Add a macro_rules! backport under
the same name and arm syntax so call sites can be written today and
migrate to the standard library macro unchanged once the minimum version
reaches 1.95.0.
The upstream macro is a compiler built-in, which lets it be used in
expression position with a single pair of braces and accept bare
"PREDICATE => EXPR," arms. A macro_rules! macro cannot place #[cfg] on a
bare expression without the unstable stmt_expr_attributes feature, so
this backport requires brace-delimited arms and an extra pair of braces
in expression position (cfg_select! {{ ... }}). Item and statement
position are syntactically identical to core::cfg_select!, which covers
the common case of selecting an item based on a CONFIG_* symbol.
Link: https://github.com/Rust-for-Linux/linux/issues/1183
Assisted-by: Claude:claude-opus-4-8
Suggested-by: Benno Lossin <lossin@kernel.org>
Signed-off-by: Nika Krasnova <nika@nikableh.moe>
---
rust/kernel/cfg.rs | 297 +++++++++++++++++++++++++++++++++++++++++++++++++
rust/kernel/lib.rs | 1 +
rust/kernel/prelude.rs | 1 +
3 files changed, 299 insertions(+)
diff --git a/rust/kernel/cfg.rs b/rust/kernel/cfg.rs
new file mode 100644
index 000000000000..20463701482e
--- /dev/null
+++ b/rust/kernel/cfg.rs
@@ -0,0 +1,297 @@
+// SPDX-License-Identifier: GPL-2.0
+
+//! Compile-time configuration selection.
+//!
+//! This module provides [`cfg_select!`](macro@crate::cfg_select), a backport of the
+//! standard library's [`core::cfg_select`] macro (stable since Rust 1.95.0). It selects
+//! between several blocks of code based on `cfg` predicates, which is most useful for
+//! choosing an implementation depending on a kernel configuration option, i.e. a `CONFIG_*`
+//! symbol.
+//!
+//! Once the kernel's minimum supported Rust version reaches 1.95.0, this module should be
+//! deleted and `cfg_select!` taken from the prelude / [`core`] instead. In item and statement
+//! position the syntax is identical, so those uses migrate unchanged; see
+//! [`cfg_select!`](macro@crate::cfg_select) for the (small) differences that apply in expression
+//! position.
+//!
+//! [`core::cfg_select`]: https://doc.rust-lang.org/core/macro.cfg_select.html
+
+#[doc(inline)]
+pub use crate::cfg_select;
+
+/// Selects code at compile time based on `cfg` predicates.
+///
+/// This is a backport of the standard library's [`core::cfg_select`] macro (stable since Rust
+/// 1.95.0). Each arm has the form `PREDICATE => { CODE }`, where `PREDICATE` is any [`cfg`]
+/// predicate (for example a `CONFIG_*` symbol). The arms are evaluated top to bottom and the `CODE`
+/// of the first arm whose predicate holds is emitted; all other arms are discarded. An optional
+/// final `_ => { CODE }` arm is used as a fallback when no predicate matches.
+///
+/// # Differences from `core::cfg_select`
+///
+/// The standard library macro is a compiler built-in and can therefore be used in expression
+/// position with a single pair of braces. This backport is a `macro_rules!` macro, so two
+/// restrictions apply:
+///
+/// - Arms must be brace-delimited (`PREDICATE => { ... }`); the bare `PREDICATE => EXPR,` form
+/// accepted by [`core::cfg_select`] is not supported (a `macro_rules!` macro cannot place
+/// `#[cfg]` on a bare expression without the unstable [`stmt_expr_attributes`] feature).
+/// - In expression position the whole invocation must be wrapped in an extra pair of braces, i.e.
+/// `cfg_select! {{ ... }}`.
+///
+/// In item and statement position the syntax is identical to [`core::cfg_select`], so those uses
+/// will migrate unchanged once the minimum Rust version reaches 1.95.0.
+///
+/// # Examples
+///
+/// Item position -- select an implementation based on a `CONFIG` option:
+///
+/// ```
+/// use kernel::cfg_select;
+///
+/// cfg_select! {
+/// CONFIG_64BIT => {
+/// fn ptr_bytes() -> usize { 8 }
+/// }
+/// _ => {
+/// fn ptr_bytes() -> usize { 4 }
+/// }
+/// }
+/// # assert!(ptr_bytes() == 8 || ptr_bytes() == 4);
+/// ```
+///
+/// Expression position (note the extra braces):
+///
+/// ```
+/// use kernel::cfg_select;
+///
+/// let bits = cfg_select! {{
+/// CONFIG_64BIT => { 64 }
+/// _ => { 32 }
+/// }};
+/// # assert!(bits == 64 || bits == 32);
+/// ```
+///
+/// [`core::cfg_select`]: https://doc.rust-lang.org/core/macro.cfg_select.html
+/// [`cfg`]: https://doc.rust-lang.org/reference/conditional-compilation.html
+/// [`stmt_expr_attributes`]: https://doc.rust-lang.org/beta/unstable-book/language-features/stmt-expr-attributes.html
+#[macro_export]
+#[doc(hidden)]
+macro_rules! cfg_select {
+ ({ $($tt:tt)* }) => {{
+ $crate::cfg_select! { $($tt)* }
+ }};
+ (_ => { $($output:tt)* }) => {
+ $($output)*
+ };
+ (
+ $cfg:meta => $output:tt
+ $($( $rest:tt )+)?
+ ) => {
+ #[cfg($cfg)]
+ $crate::cfg_select! { _ => $output }
+ $(
+ #[cfg(not($cfg))]
+ $crate::cfg_select! { $($rest)+ }
+ )?
+ };
+}
+
+#[macros::kunit_tests(rust_kernel_cfg)]
+mod tests {
+ use super::*;
+
+ #[test]
+ fn expr_true_arm_selected() {
+ // Expression position (double braces): the first arm is true, so its value wins.
+ let v = cfg_select! {{
+ all() => { 64 }
+ _ => { 32 }
+ }};
+ assert_eq!(v, 64);
+ }
+
+ #[test]
+ fn expr_fallback_when_false() {
+ // The only predicate is false, so the `_` fallback value is selected.
+ let v = cfg_select! {{
+ any() => { 64 }
+ _ => { 32 }
+ }};
+ assert_eq!(v, 32);
+ }
+
+ #[test]
+ fn expr_first_match_wins() {
+ // Two true arms: the first one wins, the second one is discarded (top-to-bottom, first
+ // match).
+ let v = cfg_select! {{
+ all() => { 1 }
+ all() => { 2 }
+ _ => { 3 }
+ }};
+ assert_eq!(v, 1);
+ }
+
+ #[test]
+ fn expr_fallthrough_false_to_true() {
+ // The false first arm is skipped and selection falls through to the later true arm, not to
+ // the `_` fallback.
+ let v = cfg_select! {{
+ any() => { 1 }
+ all() => { 2 }
+ _ => { 3 }
+ }};
+ assert_eq!(v, 2);
+ }
+
+ #[test]
+ fn expr_no_fallback_true_arm() {
+ // No `_` arm: the chain may terminate on a true arm and still emit it.
+ let v = cfg_select! {{
+ any() => { 1 }
+ all() => { 2 }
+ }};
+ assert_eq!(v, 2);
+ }
+
+ #[test]
+ fn expr_not_predicate() {
+ // `not()` in both polarities: `not(all())` is false (skipped), `not(any())` is true
+ // (selected).
+ let v = cfg_select! {{
+ not(all()) => { 1 }
+ not(any()) => { 2 }
+ _ => { 3 }
+ }};
+ assert_eq!(v, 2);
+ }
+
+ #[test]
+ fn expr_all_predicate() {
+ // `all(a, b)`: one false operand makes it false (skipped); all true makes it true
+ // (selected).
+ let v = cfg_select! {{
+ all(all(), any()) => { 1 }
+ all(all(), all()) => { 2 }
+ _ => { 3 }
+ }};
+ assert_eq!(v, 2);
+ }
+
+ #[test]
+ fn expr_any_predicate() {
+ // `any(a, b)`: all-false operands make it false (skipped); one true operand makes it true
+ // (selected).
+ let v = cfg_select! {{
+ any(any(), any()) => { 1 }
+ any(any(), all()) => { 2 }
+ _ => { 3 }
+ }};
+ assert_eq!(v, 2);
+ }
+
+ #[test]
+ fn expr_nested_select() {
+ // A `cfg_select!` nested inside an output block: the outer picks its true arm, the inner
+ // falls back (`any()` is false) to 2.
+ let v = cfg_select! {{
+ all() => {
+ cfg_select! {{
+ any() => { 1 }
+ _ => { 2 }
+ }}
+ }
+ _ => { 3 }
+ }};
+ assert_eq!(v, 2);
+ }
+
+ #[test]
+ fn item_true_arm_wins_over_fallback() {
+ // Item position: the true arm's `fn` is emitted, the `_` fallback stripped.
+ cfg_select! {
+ all() => {
+ fn pick() -> u32 {
+ 1
+ }
+ }
+ _ => {
+ fn pick() -> u32 {
+ 2
+ }
+ }
+ }
+ assert_eq!(pick(), 1);
+ }
+
+ #[test]
+ fn item_fallback_when_false() {
+ // Item position: the only predicate is false, so the `_` fallback supplies the item.
+ cfg_select! {
+ any() => {
+ fn pick() -> u32 {
+ 1
+ }
+ }
+ _ => {
+ fn pick() -> u32 {
+ 2
+ }
+ }
+ }
+ assert_eq!(pick(), 2);
+ }
+
+ #[test]
+ fn item_no_fallback_all_false_emits_nothing() {
+ // No `_` fallback and the sole predicate is false: the invocation must expand to nothing.
+ // If the false arm were wrongly emitted, the duplicate definition would fail to compile.
+ // Compiling at all shows the arm was dropped, and the assert confirms the surviving `pick`
+ // is this outer one.
+ fn pick() -> u32 {
+ 5
+ }
+ cfg_select! {
+ any() => {
+ fn pick() -> u32 {
+ 1
+ }
+ }
+ }
+ assert_eq!(pick(), 5);
+ }
+
+ #[test]
+ fn stmt_true_arm_selected() {
+ // Statement position: the `v = 10` assignment is a `#[cfg(all())]`-gated statement, proving
+ // the true arm's statements run. `let v;` defers initialization so the single surviving
+ // assignment leaves no dead store.
+ let v;
+ cfg_select! {
+ all() => {
+ v = 10;
+ }
+ _ => {
+ v = 20;
+ }
+ }
+ assert_eq!(v, 10);
+ }
+
+ #[test]
+ fn stmt_fallback_when_false() {
+ // Statement position with the predicate false: the `_` fallback statement is the one
+ // emitted.
+ let v;
+ cfg_select! {
+ any() => {
+ v = 1;
+ }
+ _ => {
+ v = 2;
+ }
+ }
+ assert_eq!(v, 2);
+ }
+}
diff --git a/rust/kernel/lib.rs b/rust/kernel/lib.rs
index 9512af7156df..cdcbb34047c0 100644
--- a/rust/kernel/lib.rs
+++ b/rust/kernel/lib.rs
@@ -51,6 +51,7 @@
pub mod block;
pub mod bug;
pub mod build_assert;
+pub mod cfg;
pub mod clk;
#[cfg(CONFIG_CONFIGFS_FS)]
pub mod configfs;
diff --git a/rust/kernel/prelude.rs b/rust/kernel/prelude.rs
index ca396f1f78a6..c7d5439da28e 100644
--- a/rust/kernel/prelude.rs
+++ b/rust/kernel/prelude.rs
@@ -91,6 +91,7 @@
const_assert,
static_assert, //
},
+ cfg_select,
current,
dev_alert,
dev_crit,
--
2.54.0
^ permalink raw reply related [flat|nested] 8+ messages in thread* [PATCH 2/2] rust: kernel: migrate to cfg_select! for config-based selection
2026-06-29 3:51 [PATCH 0/2] rust: kernel: add cfg_select! and use it for config-based selection Nika Krasnova
2026-06-29 3:51 ` [PATCH 1/2] rust: kernel: add cfg_select! backport " Nika Krasnova
@ 2026-06-29 3:51 ` Nika Krasnova
2026-06-29 9:38 ` [PATCH 0/2] rust: kernel: add cfg_select! and use it " Miguel Ojeda
2 siblings, 0 replies; 8+ messages in thread
From: Nika Krasnova @ 2026-06-29 3:51 UTC (permalink / raw)
To: Miguel Ojeda, Boqun Feng, Gary Guo, Björn Roy Baron,
Benno Lossin, Andreas Hindborg, Alice Ryhl, Trevor Gross,
Danilo Krummrich
Cc: Daniel Almeida, Tamir Duberstein, Alexandre Courbot,
Onur Özkan, Viresh Kumar, Yury Norov, Lorenzo Stoakes,
Brendan Higgins, David Gow, Rae Moar, rust-for-linux,
linux-kernel, kunit-dev, linux-kselftest, Nika Krasnova
Convert the paired #[cfg(CONFIG_FOO)] / #[cfg(not(CONFIG_FOO))]
selections in the kernel crate to cfg_select!. These sites each choose
one of several mutually-exclusive implementations, which is exactly what
cfg_select! expresses as a single construct.
One case is deliberately left as #[cfg]: a #[macro_export] macro_rules!
that is referred to by an absolute path ($crate::/crate::) cannot be
wrapped in cfg_select!, because a macro produced by macro expansion may
not be named that way (see rust-lang/rust#52234). print_macro in
print.rs hits this and is kept as paired #[cfg]; macros invoked only by
their bare name, such as the asm! wrapper in lib.rs, are unaffected and
are converted.
Link: https://github.com/Rust-for-Linux/linux/issues/1183
Assisted-by: Claude:claude-opus-4-8
Signed-off-by: Nika Krasnova <nika@nikableh.moe>
---
rust/kernel/cpu.rs | 21 ++---
rust/kernel/cpumask.rs | 42 +++++-----
rust/kernel/debugfs.rs | 145 ++++++++++++++++++-----------------
rust/kernel/driver.rs | 44 ++++++-----
rust/kernel/drm/device.rs | 53 +++++++------
rust/kernel/error.rs | 47 ++++++------
rust/kernel/kunit.rs | 54 +++++++------
rust/kernel/lib.rs | 77 ++++++++++---------
rust/kernel/mm.rs | 36 +++++----
rust/kernel/sync/atomic.rs | 54 +++++++------
rust/kernel/sync/atomic/predefine.rs | 35 +++++----
rust/kernel/time.rs | 82 ++++++++++----------
12 files changed, 366 insertions(+), 324 deletions(-)
diff --git a/rust/kernel/cpu.rs b/rust/kernel/cpu.rs
index cb6c0338ef5a..b6730331fda8 100644
--- a/rust/kernel/cpu.rs
+++ b/rust/kernel/cpu.rs
@@ -4,20 +4,21 @@
//!
//! C header: [`include/linux/cpu.h`](srctree/include/linux/cpu.h)
-use crate::{bindings, device::Device, error::Result, prelude::ENODEV};
+use crate::{bindings, cfg_select, device::Device, error::Result, prelude::ENODEV};
/// Returns the maximum number of possible CPUs in the current system configuration.
#[inline]
pub fn nr_cpu_ids() -> u32 {
- #[cfg(any(NR_CPUS_1, CONFIG_FORCE_NR_CPUS))]
- {
- bindings::NR_CPUS
- }
-
- #[cfg(not(any(NR_CPUS_1, CONFIG_FORCE_NR_CPUS)))]
- // SAFETY: `nr_cpu_ids` is a valid global provided by the kernel.
- unsafe {
- bindings::nr_cpu_ids
+ cfg_select! {
+ any(NR_CPUS_1, CONFIG_FORCE_NR_CPUS) => {
+ bindings::NR_CPUS
+ }
+ _ => {
+ // SAFETY: `nr_cpu_ids` is a valid global provided by the kernel.
+ unsafe {
+ bindings::nr_cpu_ids
+ }
+ }
}
}
diff --git a/rust/kernel/cpumask.rs b/rust/kernel/cpumask.rs
index 44bb36636ee3..f86ec96acfe2 100644
--- a/rust/kernel/cpumask.rs
+++ b/rust/kernel/cpumask.rs
@@ -307,28 +307,34 @@ pub fn try_clone(cpumask: &Cpumask) -> Result<Self> {
impl Deref for CpumaskVar {
type Target = Cpumask;
- #[cfg(CONFIG_CPUMASK_OFFSTACK)]
- fn deref(&self) -> &Self::Target {
- // SAFETY: The caller owns CpumaskVar, so it is safe to deref the cpumask.
- unsafe { &*self.ptr.as_ptr() }
- }
-
- #[cfg(not(CONFIG_CPUMASK_OFFSTACK))]
- fn deref(&self) -> &Self::Target {
- &self.mask
+ cfg_select! {
+ CONFIG_CPUMASK_OFFSTACK => {
+ fn deref(&self) -> &Self::Target {
+ // SAFETY: The caller owns CpumaskVar, so it is safe to deref the cpumask.
+ unsafe { &*self.ptr.as_ptr() }
+ }
+ }
+ _ => {
+ fn deref(&self) -> &Self::Target {
+ &self.mask
+ }
+ }
}
}
impl DerefMut for CpumaskVar {
- #[cfg(CONFIG_CPUMASK_OFFSTACK)]
- fn deref_mut(&mut self) -> &mut Cpumask {
- // SAFETY: The caller owns CpumaskVar, so it is safe to deref the cpumask.
- unsafe { self.ptr.as_mut() }
- }
-
- #[cfg(not(CONFIG_CPUMASK_OFFSTACK))]
- fn deref_mut(&mut self) -> &mut Cpumask {
- &mut self.mask
+ cfg_select! {
+ CONFIG_CPUMASK_OFFSTACK => {
+ fn deref_mut(&mut self) -> &mut Cpumask {
+ // SAFETY: The caller owns CpumaskVar, so it is safe to deref the cpumask.
+ unsafe { self.ptr.as_mut() }
+ }
+ }
+ _ => {
+ fn deref_mut(&mut self) -> &mut Cpumask {
+ &mut self.mask
+ }
+ }
}
}
diff --git a/rust/kernel/debugfs.rs b/rust/kernel/debugfs.rs
index d7b8014a6474..39657bfd1714 100644
--- a/rust/kernel/debugfs.rs
+++ b/rust/kernel/debugfs.rs
@@ -75,22 +75,22 @@
impl Dir {
/// Create a new directory in DebugFS. If `parent` is [`None`], it will be created at the root.
fn create(name: &CStr, parent: Option<&Dir>) -> Self {
- #[cfg(CONFIG_DEBUG_FS)]
- {
- let parent_entry = match parent {
- // If the parent couldn't be allocated, just early-return
- Some(Dir(None)) => return Self(None),
- Some(Dir(Some(entry))) => Some(entry.clone()),
- None => None,
- };
- Self(
- // If Arc creation fails, the `Entry` will be dropped, so the directory will be
- // cleaned up.
- Arc::new(Entry::dynamic_dir(name, parent_entry), GFP_KERNEL).ok(),
- )
+ cfg_select! {
+ CONFIG_DEBUG_FS => {
+ let parent_entry = match parent {
+ // If the parent couldn't be allocated, just early-return
+ Some(Dir(None)) => return Self(None),
+ Some(Dir(Some(entry))) => Some(entry.clone()),
+ None => None,
+ };
+ Self(
+ // If Arc creation fails, the `Entry` will be dropped, so the directory will be
+ // cleaned up.
+ Arc::new(Entry::dynamic_dir(name, parent_entry), GFP_KERNEL).ok(),
+ )
+ }
+ _ => { Self() }
}
- #[cfg(not(CONFIG_DEBUG_FS))]
- Self()
}
/// Creates a DebugFS file which will own the data produced by the initializer provided in
@@ -360,19 +360,19 @@ pub fn write_callback_file<'a, T, E: 'a, W>(
// Unless you also extract the `entry` later and schedule it for `Drop` at the appropriate
// time, a `ScopedDir` with a `Dir` parent will never be deleted.
fn scoped_dir<'data>(&self, name: &CStr) -> ScopedDir<'data, 'static> {
- #[cfg(CONFIG_DEBUG_FS)]
- {
- let parent_entry = match &self.0 {
- None => return ScopedDir::empty(),
- Some(entry) => entry.clone(),
- };
- ScopedDir {
- entry: ManuallyDrop::new(Entry::dynamic_dir(name, Some(parent_entry))),
- _phantom: PhantomData,
+ cfg_select! {
+ CONFIG_DEBUG_FS => {
+ let parent_entry = match &self.0 {
+ None => return ScopedDir::empty(),
+ Some(entry) => entry.clone(),
+ };
+ ScopedDir {
+ entry: ManuallyDrop::new(Entry::dynamic_dir(name, Some(parent_entry))),
+ _phantom: PhantomData,
+ }
}
+ _ => { ScopedDir::empty() }
}
- #[cfg(not(CONFIG_DEBUG_FS))]
- ScopedDir::empty()
}
/// Creates a new scope, which is a directory associated with some data `T`.
@@ -430,47 +430,50 @@ pub struct File<T> {
scope: Scope<T>,
}
-#[cfg(not(CONFIG_DEBUG_FS))]
-impl<'b, T: 'b> Scope<T> {
- fn new<E: 'b, F>(data: impl PinInit<T, E> + 'b, init: F) -> impl PinInit<Self, E> + 'b
- where
- F: for<'a> FnOnce(&'a T) + 'b,
- {
- try_pin_init! {
- Self {
- data <- data,
- _pin: PhantomPinned
- } ? E
- }
- .pin_chain(|scope| {
- init(&scope.data);
- Ok(())
- })
- }
-}
+cfg_select! {
+ CONFIG_DEBUG_FS => {
+ impl<'b, T: 'b> Scope<T> {
+ fn entry_mut(self: Pin<&mut Self>) -> &mut Entry<'static> {
+ // SAFETY: _entry is not structurally pinned.
+ unsafe { &mut Pin::into_inner_unchecked(self)._entry }
+ }
-#[cfg(CONFIG_DEBUG_FS)]
-impl<'b, T: 'b> Scope<T> {
- fn entry_mut(self: Pin<&mut Self>) -> &mut Entry<'static> {
- // SAFETY: _entry is not structurally pinned.
- unsafe { &mut Pin::into_inner_unchecked(self)._entry }
+ fn new<E: 'b, F>(data: impl PinInit<T, E> + 'b, init: F) -> impl PinInit<Self, E> + 'b
+ where
+ F: for<'a> FnOnce(&'a T) -> Entry<'static> + 'b,
+ {
+ try_pin_init! {
+ Self {
+ _entry: Entry::empty(),
+ data <- data,
+ _pin: PhantomPinned
+ } ? E
+ }
+ .pin_chain(|scope| {
+ *scope.entry_mut() = init(&scope.data);
+ Ok(())
+ })
+ }
+ }
}
-
- fn new<E: 'b, F>(data: impl PinInit<T, E> + 'b, init: F) -> impl PinInit<Self, E> + 'b
- where
- F: for<'a> FnOnce(&'a T) -> Entry<'static> + 'b,
- {
- try_pin_init! {
- Self {
- _entry: Entry::empty(),
- data <- data,
- _pin: PhantomPinned
- } ? E
+ _ => {
+ impl<'b, T: 'b> Scope<T> {
+ fn new<E: 'b, F>(data: impl PinInit<T, E> + 'b, init: F) -> impl PinInit<Self, E> + 'b
+ where
+ F: for<'a> FnOnce(&'a T) + 'b,
+ {
+ try_pin_init! {
+ Self {
+ data <- data,
+ _pin: PhantomPinned
+ } ? E
+ }
+ .pin_chain(|scope| {
+ init(&scope.data);
+ Ok(())
+ })
+ }
}
- .pin_chain(|scope| {
- *scope.entry_mut() = init(&scope.data);
- Ok(())
- })
}
}
@@ -702,12 +705,16 @@ fn empty() -> Self {
_phantom: PhantomData,
}
}
- #[cfg(CONFIG_DEBUG_FS)]
- fn into_entry(self) -> Entry<'dir> {
- ManuallyDrop::into_inner(self.entry)
+ cfg_select! {
+ CONFIG_DEBUG_FS => {
+ fn into_entry(self) -> Entry<'dir> {
+ ManuallyDrop::into_inner(self.entry)
+ }
+ }
+ _ => {
+ fn into_entry(self) {}
+ }
}
- #[cfg(not(CONFIG_DEBUG_FS))]
- fn into_entry(self) {}
}
impl<'data> ScopedDir<'data, 'static> {
diff --git a/rust/kernel/driver.rs b/rust/kernel/driver.rs
index bf5ba0d27553..871ea9e04804 100644
--- a/rust/kernel/driver.rs
+++ b/rust/kernel/driver.rs
@@ -328,29 +328,31 @@ pub trait Adapter {
///
/// If this returns `None`, it means there is no match with an entry in the [`acpi::IdTable`].
fn acpi_id_info(dev: &device::Device) -> Option<&'static Self::IdInfo> {
- #[cfg(not(CONFIG_ACPI))]
- {
- let _ = dev;
- None
- }
+ cfg_select! {
+ CONFIG_ACPI => {
+ let table = Self::acpi_id_table()?;
- #[cfg(CONFIG_ACPI)]
- {
- let table = Self::acpi_id_table()?;
-
- // SAFETY:
- // - `table` has static lifetime, hence it's valid for read,
- // - `dev` is guaranteed to be valid while it's alive, and so is `dev.as_raw()`.
- let raw_id = unsafe { bindings::acpi_match_device(table.as_ptr(), dev.as_raw()) };
-
- if raw_id.is_null() {
+ // SAFETY:
+ // - `table` has static lifetime, hence it's valid for read,
+ // - `dev` is guaranteed to be valid while it's alive, and so is `dev.as_raw()`.
+ let raw_id = unsafe { bindings::acpi_match_device(table.as_ptr(), dev.as_raw()) };
+
+ if raw_id.is_null() {
+ None
+ } else {
+ // SAFETY: `DeviceId` is a `#[repr(transparent)]` wrapper of
+ // `struct acpi_device_id` and does not add additional invariants, so
+ // it's safe to transmute.
+ let id = unsafe { &*raw_id.cast::<acpi::DeviceId>() };
+
+ Some(table.info(
+ <acpi::DeviceId as crate::device_id::RawDeviceIdIndex>::index(id),
+ ))
+ }
+ }
+ _ => {
+ let _ = dev;
None
- } else {
- // SAFETY: `DeviceId` is a `#[repr(transparent)]` wrapper of `struct acpi_device_id`
- // and does not add additional invariants, so it's safe to transmute.
- let id = unsafe { &*raw_id.cast::<acpi::DeviceId>() };
-
- Some(table.info(<acpi::DeviceId as crate::device_id::RawDeviceIdIndex>::index(id)))
}
}
}
diff --git a/rust/kernel/drm/device.rs b/rust/kernel/drm/device.rs
index 477cf771fb10..61a184241d55 100644
--- a/rust/kernel/drm/device.rs
+++ b/rust/kernel/drm/device.rs
@@ -41,33 +41,36 @@
},
};
-#[cfg(CONFIG_DRM_LEGACY)]
-macro_rules! drm_legacy_fields {
- ( $($field:ident: $val:expr),* $(,)? ) => {
- bindings::drm_driver {
- $( $field: $val ),*,
- firstopen: None,
- preclose: None,
- dma_ioctl: None,
- dma_quiescent: None,
- context_dtor: None,
- irq_handler: None,
- irq_preinstall: None,
- irq_postinstall: None,
- irq_uninstall: None,
- get_vblank_counter: None,
- enable_vblank: None,
- disable_vblank: None,
- dev_priv_size: 0,
+cfg_select! {
+ CONFIG_DRM_LEGACY => {
+ macro_rules! drm_legacy_fields {
+ ( $($field:ident: $val:expr),* $(,)? ) => {
+ bindings::drm_driver {
+ $( $field: $val ),*,
+ firstopen: None,
+ preclose: None,
+ dma_ioctl: None,
+ dma_quiescent: None,
+ context_dtor: None,
+ irq_handler: None,
+ irq_preinstall: None,
+ irq_postinstall: None,
+ irq_uninstall: None,
+ get_vblank_counter: None,
+ enable_vblank: None,
+ disable_vblank: None,
+ dev_priv_size: 0,
+ }
+ }
}
}
-}
-
-#[cfg(not(CONFIG_DRM_LEGACY))]
-macro_rules! drm_legacy_fields {
- ( $($field:ident: $val:expr),* $(,)? ) => {
- bindings::drm_driver {
- $( $field: $val ),*
+ _ => {
+ macro_rules! drm_legacy_fields {
+ ( $($field:ident: $val:expr),* $(,)? ) => {
+ bindings::drm_driver {
+ $( $field: $val ),*
+ }
+ }
}
}
}
diff --git a/rust/kernel/error.rs b/rust/kernel/error.rs
index a56ba6309594..733c2d08c5e6 100644
--- a/rust/kernel/error.rs
+++ b/rust/kernel/error.rs
@@ -8,7 +8,7 @@
use crate::{
alloc::{layout::LayoutError, AllocError},
- fmt,
+ cfg_select, fmt,
str::CStr,
};
@@ -173,29 +173,32 @@ pub fn to_ptr<T>(self) -> *mut T {
unsafe { bindings::ERR_PTR(self.0.get() as crate::ffi::c_long).cast() }
}
- /// Returns a string representing the error, if one exists.
- #[cfg(not(testlib))]
- pub fn name(&self) -> Option<&'static CStr> {
- // SAFETY: Just an FFI call, there are no extra safety requirements.
- let ptr = unsafe { bindings::errname(-self.0.get()) };
- if ptr.is_null() {
- None
- } else {
- use crate::str::CStrExt as _;
-
- // SAFETY: The string returned by `errname` is static and `NUL`-terminated.
- Some(unsafe { CStr::from_char_ptr(ptr) })
+ cfg_select! {
+ testlib => {
+ /// Returns a string representing the error, if one exists.
+ ///
+ /// When `testlib` is configured, this always returns `None` to avoid
+ /// the dependency on a kernel function so that tests that use this
+ /// (e.g., by calling [`Result::unwrap`]) can still run in userspace.
+ pub fn name(&self) -> Option<&'static CStr> {
+ None
+ }
}
- }
+ _ => {
+ /// Returns a string representing the error, if one exists.
+ pub fn name(&self) -> Option<&'static CStr> {
+ // SAFETY: Just an FFI call, there are no extra safety requirements.
+ let ptr = unsafe { bindings::errname(-self.0.get()) };
+ if ptr.is_null() {
+ None
+ } else {
+ use crate::str::CStrExt as _;
- /// Returns a string representing the error, if one exists.
- ///
- /// When `testlib` is configured, this always returns `None` to avoid the dependency on a
- /// kernel function so that tests that use this (e.g., by calling [`Result::unwrap`]) can still
- /// run in userspace.
- #[cfg(testlib)]
- pub fn name(&self) -> Option<&'static CStr> {
- None
+ // SAFETY: The string returned by `errname` is static and `NUL`-terminated.
+ Some(unsafe { CStr::from_char_ptr(ptr) })
+ }
+ }
+ }
}
}
diff --git a/rust/kernel/kunit.rs b/rust/kernel/kunit.rs
index cdee5f27bd7f..5ec15bfbfee6 100644
--- a/rust/kernel/kunit.rs
+++ b/rust/kernel/kunit.rs
@@ -14,18 +14,21 @@
/// Public but hidden since it should only be used from KUnit generated code.
#[doc(hidden)]
pub fn err(args: fmt::Arguments<'_>) {
- // `args` is unused if `CONFIG_PRINTK` is not set - this avoids a build-time warning.
- #[cfg(not(CONFIG_PRINTK))]
- let _ = args;
-
- // SAFETY: The format string is null-terminated and the `%pA` specifier matches the argument we
- // are passing.
- #[cfg(CONFIG_PRINTK)]
- unsafe {
- bindings::_printk(
- c"\x013%pA".as_char_ptr(),
- core::ptr::from_ref(&args).cast::<c_void>(),
- );
+ cfg_select! {
+ CONFIG_PRINTK => {
+ // SAFETY: The format string is null-terminated and the `%pA` specifier matches the
+ // argument we are passing.
+ unsafe {
+ bindings::_printk(
+ c"\x013%pA".as_char_ptr(),
+ core::ptr::from_ref(&args).cast::<c_void>(),
+ );
+ }
+ }
+ _ => {
+ // `args` is unused if `CONFIG_PRINTK` is not set - this avoids a build-time warning.
+ let _ = args;
+ }
}
}
@@ -34,18 +37,21 @@ pub fn err(args: fmt::Arguments<'_>) {
/// Public but hidden since it should only be used from KUnit generated code.
#[doc(hidden)]
pub fn info(args: fmt::Arguments<'_>) {
- // `args` is unused if `CONFIG_PRINTK` is not set - this avoids a build-time warning.
- #[cfg(not(CONFIG_PRINTK))]
- let _ = args;
-
- // SAFETY: The format string is null-terminated and the `%pA` specifier matches the argument we
- // are passing.
- #[cfg(CONFIG_PRINTK)]
- unsafe {
- bindings::_printk(
- c"\x016%pA".as_char_ptr(),
- core::ptr::from_ref(&args).cast::<c_void>(),
- );
+ cfg_select! {
+ CONFIG_PRINTK => {
+ // SAFETY: The format string is null-terminated and the `%pA` specifier matches the
+ // argument we are passing.
+ unsafe {
+ bindings::_printk(
+ c"\x016%pA".as_char_ptr(),
+ core::ptr::from_ref(&args).cast::<c_void>(),
+ );
+ }
+ }
+ _ => {
+ // `args` is unused if `CONFIG_PRINTK` is not set - this avoids a build-time warning.
+ let _ = args;
+ }
}
}
diff --git a/rust/kernel/lib.rs b/rust/kernel/lib.rs
index cdcbb34047c0..ba233a037fe6 100644
--- a/rust/kernel/lib.rs
+++ b/rust/kernel/lib.rs
@@ -276,30 +276,34 @@ macro_rules! concat_literals {
};
}
-/// Wrapper around `asm!` configured for use in the kernel.
-///
-/// Uses a semicolon to avoid parsing ambiguities, even though this does not match native `asm!`
-/// syntax.
-// For x86, `asm!` uses intel syntax by default, but we want to use at&t syntax in the kernel.
-#[cfg(any(target_arch = "x86", target_arch = "x86_64"))]
-#[macro_export]
-macro_rules! asm {
- ($($asm:expr),* ; $($rest:tt)*) => {
- ::core::arch::asm!( $($asm)*, options(att_syntax), $($rest)* )
- };
-}
-
-/// Wrapper around `asm!` configured for use in the kernel.
-///
-/// Uses a semicolon to avoid parsing ambiguities, even though this does not match native `asm!`
-/// syntax.
-// For non-x86 arches we just pass through to `asm!`.
-#[cfg(not(any(target_arch = "x86", target_arch = "x86_64")))]
-#[macro_export]
-macro_rules! asm {
- ($($asm:expr),* ; $($rest:tt)*) => {
- ::core::arch::asm!( $($asm)*, $($rest)* )
- };
+cfg_select! {
+ any(target_arch = "x86", target_arch = "x86_64") => {
+ /// Wrapper around `asm!` configured for use in the kernel.
+ ///
+ /// Uses a semicolon to avoid parsing ambiguities, even though this does
+ /// not match native `asm!` syntax.
+ // For x86, `asm!` uses intel syntax by default, but we want to use at&t
+ // syntax in the kernel.
+ #[macro_export]
+ macro_rules! asm {
+ ($($asm:expr),* ; $($rest:tt)*) => {
+ ::core::arch::asm!( $($asm)*, options(att_syntax), $($rest)* )
+ };
+ }
+ }
+ _ => {
+ /// Wrapper around `asm!` configured for use in the kernel.
+ ///
+ /// Uses a semicolon to avoid parsing ambiguities, even though this does
+ /// not match native `asm!` syntax.
+ // For non-x86 arches we just pass through to `asm!`.
+ #[macro_export]
+ macro_rules! asm {
+ ($($asm:expr),* ; $($rest:tt)*) => {
+ ::core::arch::asm!( $($asm)*, $($rest)* )
+ };
+ }
+ }
}
/// Gets the C string file name of a [`Location`].
@@ -334,19 +338,16 @@ macro_rules! asm {
/// ```
#[inline]
pub fn file_from_location<'a>(loc: &'a core::panic::Location<'a>) -> &'a core::ffi::CStr {
- #[cfg(CONFIG_RUSTC_HAS_FILE_AS_C_STR)]
- {
- loc.file_as_c_str()
- }
-
- #[cfg(all(CONFIG_RUSTC_HAS_FILE_WITH_NUL, not(CONFIG_RUSTC_HAS_FILE_AS_C_STR)))]
- {
- loc.file_with_nul()
- }
-
- #[cfg(not(CONFIG_RUSTC_HAS_FILE_WITH_NUL))]
- {
- let _ = loc;
- c"<Location::file_as_c_str() not supported>"
+ cfg_select! {
+ CONFIG_RUSTC_HAS_FILE_AS_C_STR => {
+ loc.file_as_c_str()
+ }
+ all(CONFIG_RUSTC_HAS_FILE_WITH_NUL, not(CONFIG_RUSTC_HAS_FILE_AS_C_STR)) => {
+ loc.file_with_nul()
+ }
+ not(CONFIG_RUSTC_HAS_FILE_WITH_NUL) => {
+ let _ = loc;
+ c"<Location::file_as_c_str() not supported>"
+ }
}
}
diff --git a/rust/kernel/mm.rs b/rust/kernel/mm.rs
index 4764d7b68f2a..b3937d37b3a2 100644
--- a/rust/kernel/mm.rs
+++ b/rust/kernel/mm.rs
@@ -12,7 +12,7 @@
//! C header: [`include/linux/mm.h`](srctree/include/linux/mm.h)
use crate::{
- bindings,
+ bindings, cfg_select,
sync::aref::{ARef, AlwaysRefCounted},
types::{NotThreadSafe, Opaque},
};
@@ -174,25 +174,27 @@ pub unsafe fn from_raw<'a>(ptr: *const bindings::mm_struct) -> &'a MmWithUser {
/// When per-vma locks are disabled, this always returns `None`.
#[inline]
pub fn lock_vma_under_rcu(&self, vma_addr: usize) -> Option<VmaReadGuard<'_>> {
- #[cfg(CONFIG_PER_VMA_LOCK)]
- {
- // SAFETY: Calling `bindings::lock_vma_under_rcu` is always okay given an mm where
- // `mm_users` is non-zero.
- let vma = unsafe { bindings::lock_vma_under_rcu(self.as_raw(), vma_addr) };
- if !vma.is_null() {
- return Some(VmaReadGuard {
- // SAFETY: If `lock_vma_under_rcu` returns a non-null ptr, then it points at a
- // valid vma. The vma is stable for as long as the vma read lock is held.
- vma: unsafe { VmaRef::from_raw(vma) },
- _nts: NotThreadSafe,
- });
+ cfg_select! {
+ CONFIG_PER_VMA_LOCK => {
+ // SAFETY: Calling `bindings::lock_vma_under_rcu` is always okay given an mm where
+ // `mm_users` is non-zero.
+ let vma = unsafe { bindings::lock_vma_under_rcu(self.as_raw(), vma_addr) };
+ if !vma.is_null() {
+ return Some(VmaReadGuard {
+ // SAFETY: If `lock_vma_under_rcu` returns a non-null ptr, then it
+ // points at a valid vma. The vma is stable for as long as the vma
+ // read lock is held.
+ vma: unsafe { VmaRef::from_raw(vma) },
+ _nts: NotThreadSafe,
+ });
+ }
+ }
+ _ => {
+ // Silence warnings about unused variables.
+ let _ = vma_addr;
}
}
- // Silence warnings about unused variables.
- #[cfg(not(CONFIG_PER_VMA_LOCK))]
- let _ = vma_addr;
-
None
}
diff --git a/rust/kernel/sync/atomic.rs b/rust/kernel/sync/atomic.rs
index 9cd009d57e35..d67dc44e8b2e 100644
--- a/rust/kernel/sync/atomic.rs
+++ b/rust/kernel/sync/atomic.rs
@@ -26,6 +26,7 @@
pub(crate) use internal::{AtomicArithmeticOps, AtomicBasicOps, AtomicExchangeOps};
use crate::build_error;
+use crate::cfg_select;
use internal::AtomicRepr;
use ordering::OrderingType;
@@ -620,25 +621,28 @@ pub fn fetch_sub<Rhs, Ordering: ordering::Ordering>(&self, v: Rhs, _: Ordering)
}
}
-#[cfg(any(CONFIG_X86_64, CONFIG_UML, CONFIG_ARM, CONFIG_ARM64))]
-#[repr(C)]
-#[derive(Clone, Copy)]
-struct Flag {
- bool_field: bool,
-}
-
-/// # Invariants
-///
-/// `padding` must be all zeroes.
-#[cfg(not(any(CONFIG_X86_64, CONFIG_UML, CONFIG_ARM, CONFIG_ARM64)))]
-#[repr(C, align(4))]
-#[derive(Clone, Copy)]
-struct Flag {
- #[cfg(target_endian = "big")]
- padding: [u8; 3],
- bool_field: bool,
- #[cfg(target_endian = "little")]
- padding: [u8; 3],
+cfg_select! {
+ any(CONFIG_X86_64, CONFIG_UML, CONFIG_ARM, CONFIG_ARM64) => {
+ #[repr(C)]
+ #[derive(Clone, Copy)]
+ struct Flag {
+ bool_field: bool,
+ }
+ }
+ _ => {
+ /// # Invariants
+ ///
+ /// `padding` must be all zeroes.
+ #[repr(C, align(4))]
+ #[derive(Clone, Copy)]
+ struct Flag {
+ #[cfg(target_endian = "big")]
+ padding: [u8; 3],
+ bool_field: bool,
+ #[cfg(target_endian = "little")]
+ padding: [u8; 3],
+ }
+ }
}
impl Flag {
@@ -656,10 +660,14 @@ const fn new(b: bool) -> Self {
// SAFETY: `Flag` and `Repr` have the same size and alignment, and `Flag` is round-trip
// transmutable to the selected representation (`i8` or `i32`).
unsafe impl AtomicType for Flag {
- #[cfg(any(CONFIG_X86_64, CONFIG_UML, CONFIG_ARM, CONFIG_ARM64))]
- type Repr = i8;
- #[cfg(not(any(CONFIG_X86_64, CONFIG_UML, CONFIG_ARM, CONFIG_ARM64)))]
- type Repr = i32;
+ cfg_select! {
+ any(CONFIG_X86_64, CONFIG_UML, CONFIG_ARM, CONFIG_ARM64) => {
+ type Repr = i8;
+ }
+ _ => {
+ type Repr = i32;
+ }
+ }
}
/// An atomic flag type intended to be backed by performance-optimal integer type.
diff --git a/rust/kernel/sync/atomic/predefine.rs b/rust/kernel/sync/atomic/predefine.rs
index 3d63f40791fa..7156e9b67c8c 100644
--- a/rust/kernel/sync/atomic/predefine.rs
+++ b/rust/kernel/sync/atomic/predefine.rs
@@ -76,23 +76,24 @@ fn rhs_into_delta(rhs: i64) -> i64 {
// Defines an internal type that always maps to the integer type which has the same size alignment
// 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;
+cfg_select! {
+ all(not(testlib), not(CONFIG_64BIT)) => {
+ #[allow(non_camel_case_types)]
+ type isize_atomic_repr = i32;
+ }
+ all(not(testlib), CONFIG_64BIT) => {
+ #[allow(non_camel_case_types)]
+ type isize_atomic_repr = i64;
+ }
+ all(testlib, target_pointer_width = "32") => {
+ #[allow(non_camel_case_types)]
+ type isize_atomic_repr = i32;
+ }
+ all(testlib, target_pointer_width = "64") => {
+ #[allow(non_camel_case_types)]
+ type isize_atomic_repr = i64;
+ }
+}
// Ensure size and alignment requirements are checked.
static_assert!(size_of::<isize>() == size_of::<isize_atomic_repr>());
diff --git a/rust/kernel/time.rs b/rust/kernel/time.rs
index 363e93cbb139..5cca5b9d111e 100644
--- a/rust/kernel/time.rs
+++ b/rust/kernel/time.rs
@@ -27,6 +27,8 @@
use core::marker::PhantomData;
use core::ops;
+use crate::cfg_select;
+
pub mod delay;
pub mod hrtimer;
@@ -360,15 +362,14 @@ impl ops::Div for Delta {
#[inline]
fn div(self, rhs: Self) -> Self::Output {
- #[cfg(CONFIG_64BIT)]
- {
- self.nanos / rhs.nanos
- }
-
- #[cfg(not(CONFIG_64BIT))]
- {
- // SAFETY: This function is always safe to call regardless of the input values
- unsafe { bindings::div64_s64(self.nanos, rhs.nanos) }
+ cfg_select! {
+ CONFIG_64BIT => {
+ self.nanos / rhs.nanos
+ }
+ _ => {
+ // SAFETY: This function is always safe to call regardless of the input values
+ unsafe { bindings::div64_s64(self.nanos, rhs.nanos) }
+ }
}
}
}
@@ -441,30 +442,32 @@ pub const fn as_nanos(self) -> i64 {
/// to the value in the [`Delta`].
#[inline]
pub fn as_micros_ceil(self) -> i64 {
- #[cfg(CONFIG_64BIT)]
- {
- self.as_nanos().saturating_add(NSEC_PER_USEC - 1) / NSEC_PER_USEC
- }
-
- #[cfg(not(CONFIG_64BIT))]
- // SAFETY: It is always safe to call `ktime_to_us()` with any value.
- unsafe {
- bindings::ktime_to_us(self.as_nanos().saturating_add(NSEC_PER_USEC - 1))
+ cfg_select! {
+ CONFIG_64BIT => {
+ self.as_nanos().saturating_add(NSEC_PER_USEC - 1) / NSEC_PER_USEC
+ }
+ _ => {
+ // SAFETY: It is always safe to call `ktime_to_us()` with any value.
+ unsafe {
+ bindings::ktime_to_us(self.as_nanos().saturating_add(NSEC_PER_USEC - 1))
+ }
+ }
}
}
/// Return the number of milliseconds in the [`Delta`].
#[inline]
pub fn as_millis(self) -> i64 {
- #[cfg(CONFIG_64BIT)]
- {
- self.as_nanos() / NSEC_PER_MSEC
- }
-
- #[cfg(not(CONFIG_64BIT))]
- // SAFETY: It is always safe to call `ktime_to_ms()` with any value.
- unsafe {
- bindings::ktime_to_ms(self.as_nanos())
+ cfg_select! {
+ CONFIG_64BIT => {
+ self.as_nanos() / NSEC_PER_MSEC
+ }
+ _ => {
+ // SAFETY: It is always safe to call `ktime_to_ms()` with any value.
+ unsafe {
+ bindings::ktime_to_ms(self.as_nanos())
+ }
+ }
}
}
@@ -474,22 +477,21 @@ pub fn as_millis(self) -> i64 {
/// limited to 32 bit dividends.
#[inline]
pub fn rem_nanos(self, dividend: i32) -> Self {
- #[cfg(CONFIG_64BIT)]
- {
- Self {
- nanos: self.as_nanos() % i64::from(dividend),
+ cfg_select! {
+ CONFIG_64BIT => {
+ Self {
+ nanos: self.as_nanos() % i64::from(dividend),
+ }
}
- }
-
- #[cfg(not(CONFIG_64BIT))]
- {
- let mut rem = 0;
+ _ => {
+ let mut rem = 0;
- // SAFETY: `rem` is in the stack, so we can always provide a valid pointer to it.
- unsafe { bindings::div_s64_rem(self.as_nanos(), dividend, &mut rem) };
+ // SAFETY: `rem` is in the stack, so we can always provide a valid pointer to it.
+ unsafe { bindings::div_s64_rem(self.as_nanos(), dividend, &mut rem) };
- Self {
- nanos: i64::from(rem),
+ Self {
+ nanos: i64::from(rem),
+ }
}
}
}
--
2.54.0
^ permalink raw reply related [flat|nested] 8+ messages in thread