Re: [PATCH v1 1/2] rust: Add support for calling a function exactly once

Boqun Feng
2025-11-12T02:23:13Z

On Tue, Nov 11, 2025 at 05:30:29PM -0800, Boqun Feng wrote:
> On Wed, Nov 12, 2025 at 06:43:49AM +0900, FUJITA Tomonori wrote:
> > On Mon, 10 Nov 2025 21:17:08 -0800
> > Boqun Feng <boqun.feng@gmail.com> wrote:
> >
> > > On Tue, Nov 11, 2025 at 12:09:49PM +0900, FUJITA Tomonori wrote:
> > >> On Mon, 10 Nov 2025 08:14:56 -0800
> > >> Boqun Feng <boqun.feng@gmail.com> wrote:
> > >>
> > >> > On Mon, Nov 10, 2025 at 06:21:50PM +0900, FUJITA Tomonori wrote:
> > >> >> On Fri, 7 Nov 2025 09:03:01 +0000
> > >> >> Alice Ryhl <aliceryhl@google.com> wrote:
> > >> >>
> > >> >> >> That's my point (and probably also Andreas' point), we already has the
> > >> >> >> type `SetOnce` to do this, no need for a `OnceLite` type if not
> > >> >> >> necessary, and the fact that it can be zero'd by debugfs doesn't change
> > >> >> >> it as I explained above.
> > >> >> >
> > >> >> > The SetOnce type doesn't do the same thing as OnceLite. SetOnce has
> > >> >> > three different states, but OnceLite only needs two. I don't think we
> > >> >> > should be reusing SetOnce here.
> > >> >
> > >> > I mean 3 states should cover 2 states, right? In this case we only need
> > >> > to support SetOnce<()>, so I think it's fine, see below:
> > >>
> > >> Yeah, that would remove access to the value member, but I think that
> > >> init member could still be accessed in an unintended way.
> > >>
> > >
> > > What an unintended way you mean? Do you have an example?
> >
> > From my understanding, the init member of SetOnce is designed to be
> > accessed by using atomic operations, only through its methods. If we
> > use SetOnce for OnceLite, however, the init member would be written
> > using non-atomic operations, which is what I referred to as the
> > "unintended way" since I don't believe Andreas intended it to be
> > modified in that manner; i.e., not through its methods or by
> > non-atomic operations.
> >
>
> Ok, the "non-atomic operations" seems to be a distraction for me to see
> the real problem ;-) Let's clear things out a bit.
>
> First of all, data races are not allowed in kernel, but kernel has
> special rules about data races, namely, a few operation should be
> treated as "per-byte atomics" so that they will have defined behaviors.
> This is kinda a gray area, but it's what it is.
>
> Now for Rust, we shouldn't do what C code did previously that so atomics
> have to be used in OnceLite, but when interacting with C, we can still
> use the special rule of data races in kernel for reasoning, so the
> "non-atomic operations" part in your argument shouldn't affect the
> validity of OnceLite or SetOnce.
>

Btw, the "non-atomic operations" can be simply "fixed" by the following
diff (only compile tested and I haven't done the "move to kernel::sync"
part).

Regards,
Boqun

--------------------->8
diff --git a/include/asm-generic/vmlinux.lds.h b/include/asm-generic/vmlinux.lds.h
index 8a9a2e732a65..61dc35e49a63 100644
--- a/include/asm-generic/vmlinux.lds.h
+++ b/include/asm-generic/vmlinux.lds.h
@@ -361,6 +361,9 @@ defined(CONFIG_AUTOFDO_CLANG) || defined(CONFIG_PROPELLER_CLANG)
__start_once = .; \
*(.data..once) \
__end_once = .; \
+ __rstart_once = .; \
+ *(.data..ronce) \
+ __rend_once = .; \
*(.data..do_once) \
STRUCT_ALIGN(); \
*(__tracepoints) \
diff --git a/rust/kernel/once_lite.rs b/rust/kernel/once_lite.rs
index 44ad5dbdc67e..b94834d2fd29 100644
--- a/rust/kernel/once_lite.rs
+++ b/rust/kernel/once_lite.rs
@@ -48,8 +48,32 @@ pub fn call_once<F>(&self, f: F) -> bool
f();
true
}
+
+ /// Reset so that `call_once()` can call the function again.
+ pub fn reset(&self) {
+ self.0.store(State::Incomplete, Relaxed);
+ }
}

+/// # Safety
+///
+/// This can be only called with the current `start` and `end` range for `pr_*_once!()`.
+#[no_mangle]
+pub unsafe extern "C" fn reset_rust_call_once(start: *mut ffi::c_void, end: *mut ffi::c_void)
+{
+ let start = start.cast::<OnceLite>();
+ let end = end.cast::<OnceLite>();
+ // SAFETY: Function safety requirement
+ let offset = unsafe { end.offset_from_unsigned(start) };
+
+ if offset != 0 {
+ // SAFETY: Function safety requirement
+ let onces = unsafe { core::slice::from_raw_parts(start, offset) };
+ for once in onces {
+ once.reset();
+ }
+ }
+}
/// Run the given function exactly once.
///
/// This is equivalent to the kernel's `DO_ONCE_LITE` macro.
@@ -64,7 +88,7 @@ pub fn call_once<F>(&self, f: F) -> bool
#[macro_export]
macro_rules! do_once_lite {
($e:expr) => {{
- #[link_section = ".data..once"]
+ #[link_section = ".data..ronce"]
static ONCE: $crate::once_lite::OnceLite = $crate::once_lite::OnceLite::new();
ONCE.call_once($e)
}};