Skip to content

Commit 08afcc3

Browse files
committed
Merge patch series "Add support for print exactly once"
Tomonori writes: "This adds the Rust equivalent of the C's DO_ONCE_LITE and pr_*_once macros. A proposal for this feature was made in the past [1], but it didn't reach consensus on the implementation and wasn't merged. Unlike the previous proposal, this implements the C's DO_ONCE_LITE mechanism using a single atomic variable. 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 readability. To make it clear that this feature is intended solely for print-related functionality (just like in C), the implementation is placed in print.rs. If an equivalent of std::sync::Once is needed in the future, it should be based on SetOnce rather than OnceLite. Unlike std::sync::Once and SetOnce, OnceLite only manages two states: incomplete and complete. The do_once_lite macro places the OnceLite object in the .data..once section, which may be zeroed by memset at any time. While this means tear reads might happen, OnceLite only manages two states (zero and non-zero), so no actual problem occurs in practice. OnceLite currently uses Atomic<i32>, but may be changed to use Atomic<i8> [2] when it becomes available." Link: https://lore.kernel.org/rust-for-linux/20241126-pr_once_macros-v4-0-410b8ca9643e@tuta.io/ [1] Link: https://lore.kernel.org/rust-for-linux/20251115050305.3872412-1-fujita.tomonori@gmail.com/ [2] Link: https://patch.msgid.link/20251117002452.4068692-1-fujita.tomonori@gmail.com Signed-off-by: Miguel Ojeda <ojeda@kernel.org>
2 parents 966f79c + 46c40f9 commit 08afcc3

1 file changed

Lines changed: 153 additions & 0 deletions

File tree

rust/kernel/print.rs

Lines changed: 153 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,11 @@ use crate::{
1111
fmt,
1212
prelude::*,
1313
str::RawFormatter,
14+
sync::atomic::{
15+
Atomic,
16+
AtomicType,
17+
Relaxed, //
18+
},
1419
};
1520

1621
// Called from `vsprintf` with format specifier `%pA`.
@@ -423,3 +428,151 @@ macro_rules! pr_cont (
423428
$crate::print_macro!($crate::print::format_strings::CONT, true, $($arg)*)
424429
)
425430
);
431+
432+
/// A lightweight `call_once` primitive.
433+
///
434+
/// This structure provides the Rust equivalent of the kernel's `DO_ONCE_LITE` macro.
435+
/// While it would be possible to implement the feature entirely as a Rust macro,
436+
/// the functionality that can be implemented as regular functions has been
437+
/// extracted and implemented as the `OnceLite` struct for better code maintainability.
438+
pub struct OnceLite(Atomic<State>);
439+
440+
#[derive(Clone, Copy, PartialEq, Eq)]
441+
#[repr(i32)]
442+
enum State {
443+
Incomplete = 0,
444+
Complete = 1,
445+
}
446+
447+
// SAFETY: `State` and `i32` has the same size and alignment, and it's round-trip
448+
// transmutable to `i32`.
449+
unsafe impl AtomicType for State {
450+
type Repr = i32;
451+
}
452+
453+
impl OnceLite {
454+
/// Creates a new [`OnceLite`] in the incomplete state.
455+
#[inline(always)]
456+
#[allow(clippy::new_without_default)]
457+
pub const fn new() -> Self {
458+
OnceLite(Atomic::new(State::Incomplete))
459+
}
460+
461+
/// Calls the provided function exactly once.
462+
///
463+
/// There is no other synchronization between two `call_once()`s
464+
/// except that only one will execute `f`, in other words, callers
465+
/// should not use a failed `call_once()` as a proof that another
466+
/// `call_once()` has already finished and the effect is observable
467+
/// to this thread.
468+
pub fn call_once<F>(&self, f: F) -> bool
469+
where
470+
F: FnOnce(),
471+
{
472+
// Avoid expensive cmpxchg if already completed.
473+
// ORDERING: `Relaxed` is used here since no synchronization is required.
474+
let old = self.0.load(Relaxed);
475+
if old == State::Complete {
476+
return false;
477+
}
478+
479+
// ORDERING: `Relaxed` is used here since no synchronization is required.
480+
let old = self.0.xchg(State::Complete, Relaxed);
481+
if old == State::Complete {
482+
return false;
483+
}
484+
485+
f();
486+
true
487+
}
488+
}
489+
490+
/// Run the given function exactly once.
491+
///
492+
/// This is equivalent to the kernel's `DO_ONCE_LITE` macro.
493+
///
494+
/// # Examples
495+
///
496+
/// ```
497+
/// kernel::do_once_lite! {
498+
/// kernel::pr_info!("This will be printed only once\n");
499+
/// };
500+
/// ```
501+
#[macro_export]
502+
macro_rules! do_once_lite {
503+
{ $($e:tt)* } => {{
504+
#[link_section = ".data..once"]
505+
static ONCE: $crate::print::OnceLite = $crate::print::OnceLite::new();
506+
ONCE.call_once(|| { $($e)* });
507+
}};
508+
}
509+
510+
/// Prints an emergency-level message (level 0) only once.
511+
///
512+
/// Equivalent to the kernel's `pr_emerg_once` macro.
513+
#[macro_export]
514+
macro_rules! pr_emerg_once (
515+
($($arg:tt)*) => (
516+
$crate::do_once_lite! { $crate::pr_emerg!($($arg)*) }
517+
)
518+
);
519+
520+
/// Prints an alert-level message (level 1) only once.
521+
///
522+
/// Equivalent to the kernel's `pr_alert_once` macro.
523+
#[macro_export]
524+
macro_rules! pr_alert_once (
525+
($($arg:tt)*) => (
526+
$crate::do_once_lite! { $crate::pr_alert!($($arg)*) }
527+
)
528+
);
529+
530+
/// Prints a critical-level message (level 2) only once.
531+
///
532+
/// Equivalent to the kernel's `pr_crit_once` macro.
533+
#[macro_export]
534+
macro_rules! pr_crit_once (
535+
($($arg:tt)*) => (
536+
$crate::do_once_lite! { $crate::pr_crit!($($arg)*) }
537+
)
538+
);
539+
540+
/// Prints an error-level message (level 3) only once.
541+
///
542+
/// Equivalent to the kernel's `pr_err_once` macro.
543+
#[macro_export]
544+
macro_rules! pr_err_once (
545+
($($arg:tt)*) => (
546+
$crate::do_once_lite! { $crate::pr_err!($($arg)*) }
547+
)
548+
);
549+
550+
/// Prints a warning-level message (level 4) only once.
551+
///
552+
/// Equivalent to the kernel's `pr_warn_once` macro.
553+
#[macro_export]
554+
macro_rules! pr_warn_once (
555+
($($arg:tt)*) => (
556+
$crate::do_once_lite! { $crate::pr_warn!($($arg)*) }
557+
)
558+
);
559+
560+
/// Prints a notice-level message (level 5) only once.
561+
///
562+
/// Equivalent to the kernel's `pr_notice_once` macro.
563+
#[macro_export]
564+
macro_rules! pr_notice_once (
565+
($($arg:tt)*) => (
566+
$crate::do_once_lite! { $crate::pr_notice!($($arg)*) }
567+
)
568+
);
569+
570+
/// Prints an info-level message (level 6) only once.
571+
///
572+
/// Equivalent to the kernel's `pr_info_once` macro.
573+
#[macro_export]
574+
macro_rules! pr_info_once (
575+
($($arg:tt)*) => (
576+
$crate::do_once_lite! { $crate::pr_info!($($arg)*) }
577+
)
578+
);

0 commit comments

Comments
 (0)