Rust for Linux List
 help / color / mirror / Atom feed
* [PATCH 0/5] Rate limited printing for Rust
@ 2026-06-23 15:38 Alice Ryhl
  2026-06-23 15:38 ` [PATCH 1/5] rust: sync: move lockdep types to rust/kernel/sync/lockdep.rs Alice Ryhl
                   ` (4 more replies)
  0 siblings, 5 replies; 12+ messages in thread
From: Alice Ryhl @ 2026-06-23 15:38 UTC (permalink / raw)
  To: Greg Kroah-Hartman, Carlos Llamas, Boqun Feng, Gary Guo
  Cc: Onur Özkan, Andreas Hindborg, Benno Lossin,
	Björn Roy Baron, Daniel Almeida, Danilo Krummrich,
	Ingo Molnar, Lyude Paul, Miguel Ojeda, Peter Zijlstra,
	Trevor Gross, Waiman Long, Will Deacon, linux-kernel,
	rust-for-linux, Alice Ryhl

To avoid DoS on the kernel log, the Rust Binder driver is being switched
to rate limited printing. But before we can do that, we must first
implement rate limited printing for Rust. Thus, do that.

However, it turns out that before we can implement rate limited
printing, we must first implement the ability declare a raw_spinlock_t
in a global variable. Thus, do that too.

The core Rust part of the series applies on top of 7.1-rc6, but the
Binder patches require some other Binder changes. I'll be resending a
more useful and easier to apply series after the merge window.

Signed-off-by: Alice Ryhl <aliceryhl@google.com>
---
Alice Ryhl (5):
      rust: sync: move lockdep types to rust/kernel/sync/lockdep.rs
      rust: sync: add const constructor for raw_spinlock_t
      rust: add pr_*_ratelimit! macros for printing
      rust_binder: consolidate transaction failure prints
      rust_binder: use pr_*_ratelimited! for printing

 drivers/android/binder/allocation.rs  |   4 +-
 drivers/android/binder/context.rs     |   6 +-
 drivers/android/binder/error.rs       |   4 -
 drivers/android/binder/freeze.rs      |  22 ++--
 drivers/android/binder/node.rs        |   8 +-
 drivers/android/binder/page_range.rs  |  12 +-
 drivers/android/binder/process.rs     |  36 +++---
 drivers/android/binder/thread.rs      | 133 ++++++++++++----------
 drivers/android/binder/transaction.rs |  25 +----
 include/linux/spinlock_types_raw.h    |   4 +
 rust/bindings/lib.rs                  |  24 ++++
 rust/helpers/helpers.c                |   1 +
 rust/helpers/ratelimit.c              |  14 +++
 rust/kernel/error.rs                  |   2 +-
 rust/kernel/lib.rs                    |   1 +
 rust/kernel/prelude.rs                |   8 ++
 rust/kernel/ratelimit.rs              | 202 ++++++++++++++++++++++++++++++++++
 rust/kernel/sync.rs                   | 135 +----------------------
 rust/kernel/sync/lock/spinlock.rs     |  29 +++++
 rust/kernel/sync/lockdep.rs           | 161 +++++++++++++++++++++++++++
 20 files changed, 574 insertions(+), 257 deletions(-)
---
base-commit: 242689558c681ab2df283c54ed7b9dee8db0904d
change-id: 20260619-pr-ratelimited-6e20fa89bd5b

Best regards,
-- 
Alice Ryhl <aliceryhl@google.com>


^ permalink raw reply	[flat|nested] 12+ messages in thread

* [PATCH 1/5] rust: sync: move lockdep types to rust/kernel/sync/lockdep.rs
  2026-06-23 15:38 [PATCH 0/5] Rate limited printing for Rust Alice Ryhl
@ 2026-06-23 15:38 ` Alice Ryhl
  2026-06-23 15:38 ` [PATCH 2/5] rust: sync: add const constructor for raw_spinlock_t Alice Ryhl
                   ` (3 subsequent siblings)
  4 siblings, 0 replies; 12+ messages in thread
From: Alice Ryhl @ 2026-06-23 15:38 UTC (permalink / raw)
  To: Greg Kroah-Hartman, Carlos Llamas, Boqun Feng, Gary Guo
  Cc: Onur Özkan, Andreas Hindborg, Benno Lossin,
	Björn Roy Baron, Daniel Almeida, Danilo Krummrich,
	Ingo Molnar, Lyude Paul, Miguel Ojeda, Peter Zijlstra,
	Trevor Gross, Waiman Long, Will Deacon, linux-kernel,
	rust-for-linux, Alice Ryhl

The lockdep types are currently stored directly in rust/kernel/sync.rs,
but there are starting to be too many of them to keep them in that file.
Thus, move them to a submodule.

For commonly used lockdep logic it's useful to keep re-exports in
kernel::sync, and this also avoids the need to update any users.

Signed-off-by: Alice Ryhl <aliceryhl@google.com>
---
 rust/kernel/sync.rs         | 135 +-----------------------------------------
 rust/kernel/sync/lockdep.rs | 139 ++++++++++++++++++++++++++++++++++++++++++++
 2 files changed, 141 insertions(+), 133 deletions(-)

diff --git a/rust/kernel/sync.rs b/rust/kernel/sync.rs
index 993dbf2caa0e..e87a7e339994 100644
--- a/rust/kernel/sync.rs
+++ b/rust/kernel/sync.rs
@@ -5,10 +5,6 @@
 //! This module contains the kernel APIs related to synchronisation that have been ported or
 //! wrapped for usage by Rust code in the kernel.
 
-use crate::prelude::*;
-use crate::types::Opaque;
-use pin_init;
-
 mod arc;
 pub mod aref;
 pub mod atomic;
@@ -16,6 +12,7 @@
 pub mod completion;
 mod condvar;
 pub mod lock;
+pub mod lockdep;
 mod locked_by;
 pub mod poll;
 pub mod rcu;
@@ -28,135 +25,7 @@
 pub use lock::global::{global_lock, GlobalGuard, GlobalLock, GlobalLockBackend, GlobalLockedBy};
 pub use lock::mutex::{new_mutex, Mutex, MutexGuard};
 pub use lock::spinlock::{new_spinlock, SpinLock, SpinLockGuard};
+pub use lockdep::{static_lock_class, LockClassKey};
 pub use locked_by::LockedBy;
 pub use refcount::Refcount;
 pub use set_once::SetOnce;
-
-/// Represents a lockdep class.
-///
-/// Wraps the kernel's `struct lock_class_key`.
-#[repr(transparent)]
-#[pin_data(PinnedDrop)]
-pub struct LockClassKey {
-    #[pin]
-    inner: Opaque<bindings::lock_class_key>,
-}
-
-// 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 {}
-
-impl LockClassKey {
-    /// Initializes a statically allocated lock class key.
-    ///
-    /// 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`.
-    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.
-    ///
-    /// # Examples
-    ///
-    /// ```
-    /// 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();
-    ///
-    /// {
-    ///     stack_pin_init!(let num: SpinLock<u32> = SpinLock::new(
-    ///         0,
-    ///         c"my_spinlock",
-    ///         // SAFETY: `key_ptr` is returned by the above `into_foreign()`, whose
-    ///         // `from_foreign()` has not yet been called.
-    ///         unsafe { <Pin<KBox<LockClassKey>> as ForeignOwnable>::borrow(key_ptr) }
-    ///     ));
-    /// }
-    ///
-    /// // 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(<Pin<KBox<LockClassKey>> as ForeignOwnable>::from_foreign(key_ptr)) };
-    /// # Ok::<(), Error>(())
-    /// ```
-    pub fn new_dynamic() -> impl PinInit<Self> {
-        pin_init!(Self {
-            // SAFETY: lockdep_register_key expects an uninitialized block of memory
-            inner <- Opaque::ffi_init(|slot| unsafe { bindings::lockdep_register_key(slot) })
-        })
-    }
-
-    /// 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()
-    }
-}
-
-#[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 it to unregister.
-        unsafe { bindings::lockdep_unregister_key(self.as_ptr()) }
-    }
-}
-
-/// Defines a new static lock class and returns a pointer to it.
-///
-/// # Examples
-///
-/// ```
-/// use kernel::sync::{static_lock_class, Arc, SpinLock};
-///
-/// fn new_locked_int() -> Result<Arc<SpinLock<u32>>> {
-///     Arc::pin_init(SpinLock::new(
-///         42,
-///         c"new_locked_int",
-///         static_lock_class!(),
-///     ), GFP_KERNEL)
-/// }
-/// ```
-#[macro_export]
-macro_rules! static_lock_class {
-    () => {{
-        static CLASS: $crate::sync::LockClassKey =
-            // 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)
-    }};
-}
-pub use static_lock_class;
-
-/// Returns the given string, if one is provided, otherwise generates one based on the source code
-/// location.
-#[doc(hidden)]
-#[macro_export]
-macro_rules! optional_name {
-    () => {
-        $crate::c_str!(::core::concat!(::core::file!(), ":", ::core::line!()))
-    };
-    ($name:literal) => {
-        $crate::c_str!($name)
-    };
-}
diff --git a/rust/kernel/sync/lockdep.rs b/rust/kernel/sync/lockdep.rs
new file mode 100644
index 000000000000..784821cc2a39
--- /dev/null
+++ b/rust/kernel/sync/lockdep.rs
@@ -0,0 +1,139 @@
+// SPDX-License-Identifier: GPL-2.0
+
+//! Utilities related to lockdep.
+//!
+//! C headers: [`include/linux/lockdep.h`](srctree/include/linux/lockdep.h)
+
+use crate::{
+    prelude::*,
+    types::Opaque, //
+};
+
+/// Represents a lockdep class.
+///
+/// Wraps the kernel's `struct lock_class_key`.
+#[repr(transparent)]
+#[pin_data(PinnedDrop)]
+pub struct LockClassKey {
+    #[pin]
+    inner: Opaque<bindings::lock_class_key>,
+}
+
+// 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 {}
+
+impl LockClassKey {
+    /// Initializes a statically allocated lock class key.
+    ///
+    /// 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`.
+    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.
+    ///
+    /// # Examples
+    ///
+    /// ```
+    /// 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();
+    ///
+    /// {
+    ///     stack_pin_init!(let num: SpinLock<u32> = SpinLock::new(
+    ///         0,
+    ///         c"my_spinlock",
+    ///         // SAFETY: `key_ptr` is returned by the above `into_foreign()`, whose
+    ///         // `from_foreign()` has not yet been called.
+    ///         unsafe { <Pin<KBox<LockClassKey>> as ForeignOwnable>::borrow(key_ptr) }
+    ///     ));
+    /// }
+    ///
+    /// // 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(<Pin<KBox<LockClassKey>> as ForeignOwnable>::from_foreign(key_ptr)) };
+    /// # Ok::<(), Error>(())
+    /// ```
+    pub fn new_dynamic() -> impl PinInit<Self> {
+        pin_init!(Self {
+            // SAFETY: lockdep_register_key expects an uninitialized block of memory
+            inner <- Opaque::ffi_init(|slot| unsafe { bindings::lockdep_register_key(slot) })
+        })
+    }
+
+    /// 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()
+    }
+}
+
+#[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 it to unregister.
+        unsafe { bindings::lockdep_unregister_key(self.as_ptr()) }
+    }
+}
+
+/// Defines a new static lock class and returns a pointer to it.
+///
+/// # Examples
+///
+/// ```
+/// use kernel::sync::{static_lock_class, Arc, SpinLock};
+///
+/// fn new_locked_int() -> Result<Arc<SpinLock<u32>>> {
+///     Arc::pin_init(SpinLock::new(
+///         42,
+///         c"new_locked_int",
+///         static_lock_class!(),
+///     ), GFP_KERNEL)
+/// }
+/// ```
+#[macro_export]
+macro_rules! static_lock_class {
+    () => {{
+        static CLASS: $crate::sync::LockClassKey =
+            // 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)
+    }};
+}
+pub use static_lock_class;
+
+/// Returns the given string, if one is provided, otherwise generates one based on the source code
+/// location.
+#[doc(hidden)]
+#[macro_export]
+macro_rules! optional_name {
+    () => {
+        $crate::c_str!(::core::concat!(::core::file!(), ":", ::core::line!()))
+    };
+    ($name:literal) => {
+        $crate::c_str!($name)
+    };
+}

-- 
2.55.0.rc0.799.gd6f94ed593-goog


^ permalink raw reply related	[flat|nested] 12+ messages in thread

* [PATCH 2/5] rust: sync: add const constructor for raw_spinlock_t
  2026-06-23 15:38 [PATCH 0/5] Rate limited printing for Rust Alice Ryhl
  2026-06-23 15:38 ` [PATCH 1/5] rust: sync: move lockdep types to rust/kernel/sync/lockdep.rs Alice Ryhl
@ 2026-06-23 15:38 ` Alice Ryhl
  2026-06-23 15:38 ` [PATCH 3/5] rust: add pr_*_ratelimit! macros for printing Alice Ryhl
                   ` (2 subsequent siblings)
  4 siblings, 0 replies; 12+ messages in thread
From: Alice Ryhl @ 2026-06-23 15:38 UTC (permalink / raw)
  To: Greg Kroah-Hartman, Carlos Llamas, Boqun Feng, Gary Guo
  Cc: Onur Özkan, Andreas Hindborg, Benno Lossin,
	Björn Roy Baron, Daniel Almeida, Danilo Krummrich,
	Ingo Molnar, Lyude Paul, Miguel Ojeda, Peter Zijlstra,
	Trevor Gross, Waiman Long, Will Deacon, linux-kernel,
	rust-for-linux, Alice Ryhl

The abstractions for pr_*_ratelimited! need to construct a global
`struct ratelimit_state`, which contains a `raw_spinlock_t` field. Thus,
add a const constructor for the `raw_spinlock_t` type.

The SPINLOCK_OWNER_INIT constant isn't mirrored via a const helper
because bindgen generates a 'static mut' instead of a constant from the
pointer constant.,

The __ARCH_SPIN_LOCK_UNLOCKED constant cannot be translated by bindgen
because it's a define for a struct without type annotations, so it's
explicitly declared in Rust.

Signed-off-by: Alice Ryhl <aliceryhl@google.com>
---
 include/linux/spinlock_types_raw.h |  4 ++++
 rust/bindings/lib.rs               | 24 ++++++++++++++++++++++++
 rust/kernel/sync/lock/spinlock.rs  | 29 +++++++++++++++++++++++++++++
 rust/kernel/sync/lockdep.rs        | 22 ++++++++++++++++++++++
 4 files changed, 79 insertions(+)

diff --git a/include/linux/spinlock_types_raw.h b/include/linux/spinlock_types_raw.h
index e5644ab2161f..942c229c90bb 100644
--- a/include/linux/spinlock_types_raw.h
+++ b/include/linux/spinlock_types_raw.h
@@ -11,6 +11,10 @@
 
 #include <linux/lockdep_types.h>
 
+/*
+ * Keep in sync with rust/kernel/sync/lock/spinlock.rs
+ */
+
 context_lock_struct(raw_spinlock) {
 	arch_spinlock_t raw_lock;
 #ifdef CONFIG_DEBUG_SPINLOCK
diff --git a/rust/bindings/lib.rs b/rust/bindings/lib.rs
index 854e7c471434..adde41e41edc 100644
--- a/rust/bindings/lib.rs
+++ b/rust/bindings/lib.rs
@@ -77,3 +77,27 @@ mod bindings_helper {
         None
     }
 };
+
+// Explicitly list architectures where this logic is checked correct.
+#[cfg(any(
+    CONFIG_ARM,
+    CONFIG_ARM64,
+    CONFIG_LOONGARCH,
+    CONFIG_PPC,
+    CONFIG_RISCV,
+    CONFIG_S390,
+    CONFIG_X86,
+))]
+pub const __ARCH_SPIN_LOCK_UNLOCKED: arch_spinlock_t = {
+    // SAFETY: The `arch_spinlock_t` type can be zeroed.
+    #[allow(unused_mut)]
+    let mut lock: arch_spinlock_t = unsafe { core::mem::zeroed() };
+
+    #[cfg(not(CONFIG_SMP))]
+    #[cfg(CONFIG_DEBUG_SPINLOCK)]
+    {
+        lock.slock = 1;
+    }
+
+    lock
+};
diff --git a/rust/kernel/sync/lock/spinlock.rs b/rust/kernel/sync/lock/spinlock.rs
index ef76fa07ca3a..8babfb79098f 100644
--- a/rust/kernel/sync/lock/spinlock.rs
+++ b/rust/kernel/sync/lock/spinlock.rs
@@ -4,6 +4,8 @@
 //!
 //! This module allows Rust code to use the kernel's `spinlock_t`.
 
+use kernel::prelude::*;
+
 /// Creates a [`SpinLock`] initialiser with the given name and a newly-created lock class.
 ///
 /// It uses the name if one is given, otherwise it generates one based on the file name and line
@@ -144,3 +146,30 @@ unsafe fn assert_is_held(ptr: *mut Self::State) {
         unsafe { bindings::spin_assert_is_held(ptr) }
     }
 }
+
+/// Helper for creating a raw unlocked `bindings::raw_spinlock_t`.
+///
+/// For use in statics containing raw spinlocks.
+pub const fn raw_spin_lock_unlocked(name: &'static CStr) -> bindings::raw_spinlock_t {
+    // Silence unused variable warnings.
+    #[cfg(not(CONFIG_DEBUG_LOCK_ALLOC))]
+    let _ = name;
+
+    bindings::raw_spinlock_t {
+        raw_lock: bindings::__ARCH_SPIN_LOCK_UNLOCKED,
+
+        #[cfg(CONFIG_DEBUG_SPINLOCK)]
+        magic: bindings::SPINLOCK_MAGIC,
+        #[cfg(CONFIG_DEBUG_SPINLOCK)]
+        owner_cpu: u32::MAX,
+        #[cfg(CONFIG_DEBUG_SPINLOCK)]
+        owner: usize::MAX as *mut c_void,
+
+        #[cfg(CONFIG_DEBUG_LOCK_ALLOC)]
+        dep_map: kernel::sync::lockdep::raw_lockdep_map(
+            name,
+            kernel::sync::lockdep::LD_WAIT_SPIN,
+            kernel::sync::lockdep::LD_WAIT_INV,
+        ),
+    }
+}
diff --git a/rust/kernel/sync/lockdep.rs b/rust/kernel/sync/lockdep.rs
index 784821cc2a39..c0f8b196c084 100644
--- a/rust/kernel/sync/lockdep.rs
+++ b/rust/kernel/sync/lockdep.rs
@@ -137,3 +137,25 @@ macro_rules! optional_name {
         $crate::c_str!($name)
     };
 }
+
+/// Not checked, catch all.
+pub const LD_WAIT_INV: u8 = bindings::lockdep_wait_type_LD_WAIT_INV as u8;
+/// Spin loops, `raw_spinlock_t` etc
+pub const LD_WAIT_SPIN: u8 = bindings::lockdep_wait_type_LD_WAIT_SPIN as u8;
+
+/// Helper for declaring a raw `struct lockdep_map` for locks in statics.
+///
+/// It's up to the caller to use the returned `struct lockdep_map` correctly.
+#[cfg(CONFIG_LOCKDEP)]
+pub const fn raw_lockdep_map(
+    name: &'static CStr,
+    wait_type_inner: u8,
+    wait_type_outer: u8,
+) -> bindings::lockdep_map {
+    // SAFETY: All zeros is valid for this type.
+    let mut map: bindings::lockdep_map = unsafe { core::mem::zeroed() };
+    map.name = kernel::str::as_char_ptr_in_const_context(name);
+    map.wait_type_inner = wait_type_inner;
+    map.wait_type_outer = wait_type_outer;
+    map
+}

-- 
2.55.0.rc0.799.gd6f94ed593-goog


^ permalink raw reply related	[flat|nested] 12+ messages in thread

* [PATCH 3/5] rust: add pr_*_ratelimit! macros for printing
  2026-06-23 15:38 [PATCH 0/5] Rate limited printing for Rust Alice Ryhl
  2026-06-23 15:38 ` [PATCH 1/5] rust: sync: move lockdep types to rust/kernel/sync/lockdep.rs Alice Ryhl
  2026-06-23 15:38 ` [PATCH 2/5] rust: sync: add const constructor for raw_spinlock_t Alice Ryhl
@ 2026-06-23 15:38 ` Alice Ryhl
  2026-06-23 15:55   ` Gary Guo
  2026-06-23 19:31   ` Miguel Ojeda
  2026-06-23 15:38 ` [PATCH 4/5] rust_binder: consolidate transaction failure prints Alice Ryhl
  2026-06-23 15:38 ` [PATCH 5/5] rust_binder: use pr_*_ratelimited! for printing Alice Ryhl
  4 siblings, 2 replies; 12+ messages in thread
From: Alice Ryhl @ 2026-06-23 15:38 UTC (permalink / raw)
  To: Greg Kroah-Hartman, Carlos Llamas, Boqun Feng, Gary Guo
  Cc: Onur Özkan, Andreas Hindborg, Benno Lossin,
	Björn Roy Baron, Daniel Almeida, Danilo Krummrich,
	Ingo Molnar, Lyude Paul, Miguel Ojeda, Peter Zijlstra,
	Trevor Gross, Waiman Long, Will Deacon, linux-kernel,
	rust-for-linux, Alice Ryhl

Printing can be very expensive if it occurs often, so printing that can
be triggered by userspace should be rate limited. For this purpose, add
a Rust wrapper around `struct ratelimit_state` and use it in the new
macros.

Signed-off-by: Alice Ryhl <aliceryhl@google.com>
---
 rust/helpers/helpers.c   |   1 +
 rust/helpers/ratelimit.c |  14 ++++
 rust/kernel/lib.rs       |   1 +
 rust/kernel/prelude.rs   |   8 ++
 rust/kernel/ratelimit.rs | 202 +++++++++++++++++++++++++++++++++++++++++++++++
 5 files changed, 226 insertions(+)

diff --git a/rust/helpers/helpers.c b/rust/helpers/helpers.c
index d17eaec76450..2184b11c927f 100644
--- a/rust/helpers/helpers.c
+++ b/rust/helpers/helpers.c
@@ -80,6 +80,7 @@
 #include "processor.c"
 #include "property.c"
 #include "pwm.c"
+#include "ratelimit.c"
 #include "rbtree.c"
 #include "rcu.c"
 #include "refcount.c"
diff --git a/rust/helpers/ratelimit.c b/rust/helpers/ratelimit.c
new file mode 100644
index 000000000000..e5052f568b81
--- /dev/null
+++ b/rust/helpers/ratelimit.c
@@ -0,0 +1,14 @@
+// SPDX-License-Identifier: GPL-2.0
+
+#include <linux/ratelimit.h>
+
+__rust_helper void rust_helper_ratelimit_state_init(struct ratelimit_state *rs,
+						    int interval, int burst)
+{
+	ratelimit_state_init(rs, interval, burst);
+}
+
+__rust_helper void rust_helper_ratelimit_state_exit(struct ratelimit_state *rs)
+{
+	ratelimit_state_exit(rs);
+}
diff --git a/rust/kernel/lib.rs b/rust/kernel/lib.rs
index b72b2fbe046d..ba65ab4f0b8c 100644
--- a/rust/kernel/lib.rs
+++ b/rust/kernel/lib.rs
@@ -111,6 +111,7 @@
 pub mod ptr;
 #[cfg(CONFIG_RUST_PWM_ABSTRACTIONS)]
 pub mod pwm;
+pub mod ratelimit;
 pub mod rbtree;
 pub mod regulator;
 pub mod revocable;
diff --git a/rust/kernel/prelude.rs b/rust/kernel/prelude.rs
index 44edf72a4a24..5a66028dd973 100644
--- a/rust/kernel/prelude.rs
+++ b/rust/kernel/prelude.rs
@@ -89,13 +89,21 @@
     },
     init::InPlaceInit,
     pr_alert,
+    pr_alert_ratelimited,
     pr_crit,
+    pr_crit_ratelimited,
     pr_debug,
+    pr_debug_ratelimited,
     pr_emerg,
+    pr_emerg_ratelimited,
     pr_err,
+    pr_err_ratelimited,
     pr_info,
+    pr_info_ratelimited,
     pr_notice,
+    pr_notice_ratelimited,
     pr_warn,
+    pr_warn_ratelimited,
     static_assert,
     str::CStrExt as _,
     try_init,
diff --git a/rust/kernel/ratelimit.rs b/rust/kernel/ratelimit.rs
new file mode 100644
index 000000000000..da0a49412023
--- /dev/null
+++ b/rust/kernel/ratelimit.rs
@@ -0,0 +1,202 @@
+// SPDX-License-Identifier: GPL-2.0
+
+//! Rate limiting support.
+//!
+//! C header: [`include/linux/ratelimit.h`](srctree/include/linux/ratelimit.h)
+
+use crate::{
+    bindings,
+    prelude::*,
+    types::Opaque, //
+};
+
+/// Defines a `static` containing a [`Ratelimit`].
+#[macro_export]
+macro_rules! ratelimit_state_init {
+    ($name:ident, $interval:expr, $burst:expr $(,)?) => {
+        static $name: $crate::ratelimit::Ratelimit = {
+            let init: $crate::bindings::ratelimit_state = $crate::bindings::ratelimit_state {
+                lock: $crate::sync::lock::spinlock::raw_spin_lock_unlocked($crate::c_str!(
+                    ::core::stringify!($name)
+                )),
+                interval: $interval,
+                burst: $burst,
+                // SAFETY: This type can be zeroed.
+                ..unsafe { ::core::mem::zeroed() }
+            };
+            // SAFETY: This is a repr(transparent) wrapper, and the invariants are satisfied.
+            unsafe { ::core::mem::transmute(init) }
+        };
+    };
+}
+pub use ratelimit_state_init;
+
+/// Rate limiter state.
+///
+/// # Invariants
+///
+/// The `inner` field contains an initialized `struct ratelimit_state`.
+#[pin_data(PinnedDrop)]
+#[repr(transparent)]
+pub struct Ratelimit {
+    #[pin]
+    inner: Opaque<bindings::ratelimit_state>,
+}
+
+// SAFETY: `Ratelimit` is safe to be sent to any task.
+unsafe impl Send for Ratelimit {}
+
+// SAFETY: `Ratelimit` is safe to be accessed concurrently as it is protected by an internal
+// spinlock.
+unsafe impl Sync for Ratelimit {}
+
+impl Ratelimit {
+    /// Constructs a [`Ratelimit`] with the specified configuration.
+    ///
+    /// If `interval` is zero, then no rate limit is applied.
+    #[inline]
+    pub fn new(interval: i32, burst: i32) -> impl PinInit<Self> {
+        // INVARIANT: This creates a `Ratelimit` containing an initialized `struct ratelimit_state`
+        pin_init!(Self {
+            inner <- Opaque::ffi_init(|slot: *mut bindings::ratelimit_state| {
+                // SAFETY: `slot` is a valid pointer to an uninitialized `struct ratelimit_state`.
+                // The memory is pinned so it remains valid until `ratelimit_state_exit` is called.
+                unsafe { bindings::ratelimit_state_init(slot, interval, burst) };
+            }),
+        })
+    }
+
+    /// Constructs a [`Ratelimit`] with the default configuration.
+    #[inline]
+    pub fn new_default() -> impl PinInit<Self> {
+        Ratelimit::new(Ratelimit::DEFAULT_INTERVAL, Ratelimit::DEFAULT_BURST)
+    }
+
+    /// The default interval used for rate limiting.
+    pub const DEFAULT_INTERVAL: i32 = bindings::DEFAULT_RATELIMIT_INTERVAL as i32;
+
+    /// The default burst size.
+    pub const DEFAULT_BURST: i32 = bindings::DEFAULT_RATELIMIT_BURST as i32;
+
+    /// Check if an action should be rate-limited.
+    ///
+    /// Returns `true` if the action is allowed, and `false` if it should be suppressed.
+    #[inline]
+    pub fn ratelimit(&self) -> bool {
+        // We don't set `RATELIMIT_MSG_ON_RELEASE`, so the function name parameter is not used.
+        //
+        // SAFETY: `self.inner.get()` is a valid pointer to a `struct ratelimit_state`.
+        // The lifetime of `func` ensures the pointer remains valid for the duration of the call.
+        // The C function `___ratelimit` handles its own internal locking, so it is safe to call
+        // concurrently.
+        unsafe { bindings::___ratelimit(self.inner.get(), c"Rust".as_char_ptr()) != 0 }
+    }
+}
+
+#[pinned_drop]
+impl PinnedDrop for Ratelimit {
+    #[inline]
+    fn drop(self: Pin<&mut Self>) {
+        // SAFETY: By the type invariants, this struct contains an initialized `struct
+        // ratelimit_state`.
+        unsafe { bindings::ratelimit_state_exit(self.inner.get()) };
+    }
+}
+
+/// Helper macro to implement ratelimited printing.
+#[macro_export]
+#[doc(hidden)]
+macro_rules! print_ratelimited {
+    ($print_macro:ident, $($arg:tt)*) => {{
+        $crate::ratelimit::ratelimit_state_init!(
+            _rs,
+            $crate::ratelimit::Ratelimit::DEFAULT_INTERVAL,
+            $crate::ratelimit::Ratelimit::DEFAULT_BURST,
+        );
+        if $crate::ratelimit::Ratelimit::ratelimit(&_rs) {
+            $crate::$print_macro!($($arg)*);
+        }
+    }};
+}
+
+/// Prints an emergency-level message (level 0) if allowed by a rate limiter.
+///
+/// [`Ratelimit`]: $crate::ratelimit::Ratelimit
+#[macro_export]
+macro_rules! pr_emerg_ratelimited (
+    ($($arg:tt)*) => (
+        $crate::print_ratelimited!(pr_emerg, $($arg)*)
+    )
+);
+
+/// Prints an alert-level message (level 1) if allowed by a rate limiter.
+///
+/// [`Ratelimit`]: $crate::ratelimit::Ratelimit
+#[macro_export]
+macro_rules! pr_alert_ratelimited (
+    ($($arg:tt)*) => (
+        $crate::print_ratelimited!(pr_alert, $($arg)*)
+    )
+);
+
+/// Prints a critical-level message (level 2) if allowed by a rate limiter.
+///
+/// [`Ratelimit`]: $crate::ratelimit::Ratelimit
+#[macro_export]
+macro_rules! pr_crit_ratelimited (
+    ($($arg:tt)*) => (
+        $crate::print_ratelimited!(pr_crit, $($arg)*)
+    )
+);
+
+/// Prints an error-level message (level 3) if allowed by a rate limiter.
+///
+/// [`Ratelimit`]: $crate::ratelimit::Ratelimit
+#[macro_export]
+macro_rules! pr_err_ratelimited (
+    ($($arg:tt)*) => (
+        $crate::print_ratelimited!(pr_err, $($arg)*)
+    )
+);
+
+/// Prints a warning-level message (level 4) if allowed by a rate limiter.
+///
+/// [`Ratelimit`]: $crate::ratelimit::Ratelimit
+#[macro_export]
+macro_rules! pr_warn_ratelimited (
+    ($($arg:tt)*) => (
+        $crate::print_ratelimited!(pr_warn, $($arg)*)
+    )
+);
+
+/// Prints a notice-level message (level 5) if allowed by a rate limiter.
+///
+/// [`Ratelimit`]: $crate::ratelimit::Ratelimit
+#[macro_export]
+macro_rules! pr_notice_ratelimited (
+    ($($arg:tt)*) => (
+        $crate::print_ratelimited!(pr_notice, $($arg)*)
+    )
+);
+
+/// Prints an info-level message (level 6) if allowed by a rate limiter.
+///
+/// [`Ratelimit`]: $crate::ratelimit::Ratelimit
+#[macro_export]
+macro_rules! pr_info_ratelimited (
+    ($($arg:tt)*) => (
+        $crate::print_ratelimited!(pr_info, $($arg)*)
+    )
+);
+
+/// Prints a debug-level message (level 7) if allowed by a rate limiter.
+///
+/// [`Ratelimit`]: $crate::ratelimit::Ratelimit
+#[macro_export]
+macro_rules! pr_debug_ratelimited (
+    ($($arg:tt)*) => (
+        if cfg!(debug_assertions) {
+            $crate::print_ratelimited!(pr_debug, $($arg)*)
+        }
+    )
+);

-- 
2.55.0.rc0.799.gd6f94ed593-goog


^ permalink raw reply related	[flat|nested] 12+ messages in thread

* [PATCH 4/5] rust_binder: consolidate transaction failure prints
  2026-06-23 15:38 [PATCH 0/5] Rate limited printing for Rust Alice Ryhl
                   ` (2 preceding siblings ...)
  2026-06-23 15:38 ` [PATCH 3/5] rust: add pr_*_ratelimit! macros for printing Alice Ryhl
@ 2026-06-23 15:38 ` Alice Ryhl
  2026-06-23 15:38 ` [PATCH 5/5] rust_binder: use pr_*_ratelimited! for printing Alice Ryhl
  4 siblings, 0 replies; 12+ messages in thread
From: Alice Ryhl @ 2026-06-23 15:38 UTC (permalink / raw)
  To: Greg Kroah-Hartman, Carlos Llamas, Boqun Feng, Gary Guo
  Cc: Onur Özkan, Andreas Hindborg, Benno Lossin,
	Björn Roy Baron, Daniel Almeida, Danilo Krummrich,
	Ingo Molnar, Lyude Paul, Miguel Ojeda, Peter Zijlstra,
	Trevor Gross, Waiman Long, Will Deacon, linux-kernel,
	rust-for-linux, Alice Ryhl

When a transaction fails, it currently hits multiple pr_warn!
invocations meaning that a single failure can result in several lines in
the kernel log. This is unnecessary, so consolidate them into one print
used for all transaction failures.

For ENOSPC errors we want to know the transaction size, so a field is
added to TransactionInfo that bubbles up this information to the new
print location.

Signed-off-by: Alice Ryhl <aliceryhl@google.com>
---
 drivers/android/binder/error.rs       |  4 ---
 drivers/android/binder/thread.rs      | 59 ++++++++++++++++++++---------------
 drivers/android/binder/transaction.rs | 21 +++----------
 rust/kernel/error.rs                  |  2 +-
 4 files changed, 39 insertions(+), 47 deletions(-)

diff --git a/drivers/android/binder/error.rs b/drivers/android/binder/error.rs
index 1296072c35d9..aed1c747640b 100644
--- a/drivers/android/binder/error.rs
+++ b/drivers/android/binder/error.rs
@@ -37,10 +37,6 @@ pub(crate) fn new_frozen_oneway() -> Self {
             source: None,
         }
     }
-
-    pub(crate) fn is_dead(&self) -> bool {
-        self.reply == BR_DEAD_REPLY
-    }
 }
 
 /// Convert an errno into a `BinderError` and store the errno used to construct it. The errno
diff --git a/drivers/android/binder/thread.rs b/drivers/android/binder/thread.rs
index def00edce9c4..a5b038877747 100644
--- a/drivers/android/binder/thread.rs
+++ b/drivers/android/binder/thread.rs
@@ -25,7 +25,7 @@
 use crate::{
     allocation::{Allocation, AllocationView, BinderObject, BinderObjectRef, NewAllocation},
     defs::*,
-    error::BinderResult,
+    error::{BinderError, BinderResult},
     process::{GetWorkOrRegister, Process},
     ptr_align,
     stats::GLOBAL_STATS,
@@ -1018,18 +1018,9 @@ pub(crate) fn copy_transaction_data(
                 .ok_or(ENOMEM)?,
             size_of::<u64>(),
         );
+        info.debug_total_size = len;
         let secctx_off = aligned_data_size + offsets_size + buffers_size;
-        let mut alloc = match to_process.buffer_alloc(debug_id, len, info) {
-            Ok(alloc) => alloc,
-            Err(err) => {
-                pr_warn!(
-                    "Failed to allocate buffer. len:{}, is_oneway:{}",
-                    len,
-                    info.is_oneway(),
-                );
-                return Err(err);
-            }
-        };
+        let mut alloc = to_process.buffer_alloc(debug_id, len, info)?;
 
         let mut buffer_reader = UserSlice::new(info.data_ptr, data_size).reader();
         let mut end_of_previous_object = 0;
@@ -1259,6 +1250,15 @@ fn read_transaction_info(
 
         info.debug_id = super::next_debug_id();
 
+        // We don't yet know the real message size because it depends on the secctx size, so for
+        // now write an estimate. When the real value is computed, this estimate is replaced.
+        // Writing an estimate here ensures that the approx message size can still be printed if
+        // the transaction fails before it is computed exactly.
+        info.debug_total_size = info
+            .data_size
+            .saturating_add(info.offsets_size)
+            .saturating_add(info.buffers_size);
+
         Ok(())
     }
 
@@ -1277,6 +1277,9 @@ fn transaction(self: &Arc<Self>, cmd: u32, reader: &mut UserSliceReader) -> Resu
             self.transaction_inner(&mut info)
         };
 
+        // This runs when return work is passed to the caller. This is not
+        // always the same as the transaction failing, as reply errors are
+        // delivered to the remote process.
         if let Err(err) = ret {
             self.push_return_work(err.reply);
             if err.reply != BR_TRANSACTION_COMPLETE {
@@ -1290,13 +1293,6 @@ fn transaction(self: &Arc<Self>, cmd: u32, reader: &mut UserSliceReader) -> Resu
                             ExtendedError::new(info.debug_id as u32, err.reply, source.to_errno());
                     }
                 }
-
-                pr_warn!(
-                    "{}:{} transaction to {} failed: {err:?}",
-                    info.from_pid,
-                    info.from_tid,
-                    info.to_pid
-                );
             }
         }
 
@@ -1305,8 +1301,27 @@ fn transaction(self: &Arc<Self>, cmd: u32, reader: &mut UserSliceReader) -> Resu
             // useful in case the transaction failed with BR_TRANSACTION_PENDING_FROZEN.
             info.report_netlink(BR_ONEWAY_SPAM_SUSPECT, &self.process.ctx);
         }
+        // This runs when the transaction failed.
         if info.reply != 0 {
             info.report_netlink(info.reply, &self.process.ctx);
+            let err = BinderError {
+                reply: info.reply,
+                source: Error::try_from_errno(info.errno),
+            };
+            pr_warn!(
+                "{}:{} {} to {} failed: {err:?}, {} bytes\n",
+                info.from_pid,
+                info.from_tid,
+                if info.is_reply {
+                    "reply"
+                } else if info.is_oneway() {
+                    "oneway"
+                } else {
+                    "transaction"
+                },
+                info.to_pid,
+                info.debug_total_size
+            );
         }
 
         Ok(())
@@ -1374,12 +1389,6 @@ fn reply_inner(self: &Arc<Self>, info: &mut TransactionInfo) -> BinderResult {
             // At this point we only return `BR_TRANSACTION_COMPLETE` to the caller, and we must let
             // the sender know that the transaction has completed (with an error in this case).
 
-            pr_warn!(
-                "{}:{} reply to {} failed: {err:?}",
-                info.from_pid,
-                info.from_tid,
-                info.to_pid
-            );
             let param = err.source.as_ref().map_or(0, |e| e.to_errno());
             let ee = ExtendedError::new(info.debug_id as u32, err.reply, param);
             orig.from
diff --git a/drivers/android/binder/transaction.rs b/drivers/android/binder/transaction.rs
index 9aefa01599fb..316627e5f9ac 100644
--- a/drivers/android/binder/transaction.rs
+++ b/drivers/android/binder/transaction.rs
@@ -39,6 +39,7 @@ pub(crate) struct TransactionInfo {
     pub(crate) offsets_ptr: UserPtr,
     pub(crate) offsets_size: usize,
     pub(crate) buffers_size: usize,
+    pub(crate) debug_total_size: usize,
     pub(crate) target_handle: u32,
     pub(crate) errno: i32,
     pub(crate) reply: u32,
@@ -138,21 +139,13 @@ pub(crate) fn new(
         let txn_security_ctx = node_ref.node.flags & FLAT_BINDER_FLAG_TXN_SECURITY_CTX != 0;
         let mut txn_security_ctx_off = if txn_security_ctx { Some(0) } else { None };
         let to = node_ref.node.owner.clone();
-        let mut alloc = match from.copy_transaction_data(
+        let mut alloc = from.copy_transaction_data(
             to.clone(),
             info,
             info.debug_id,
             allow_fds,
             txn_security_ctx_off.as_mut(),
-        ) {
-            Ok(alloc) => alloc,
-            Err(err) => {
-                if !err.is_dead() {
-                    pr_warn!("Failure in copy_transaction_data: {:?}", err);
-                }
-                return Err(err);
-            }
-        };
+        )?;
         if info.is_oneway() {
             if from_parent.is_some() {
                 pr_warn!("Oneway transaction should not be in a transaction stack.");
@@ -193,13 +186,7 @@ pub(crate) fn new_reply(
         allow_fds: bool,
     ) -> BinderResult<DLArc<Self>> {
         let mut alloc =
-            match from.copy_transaction_data(to.clone(), info, info.debug_id, allow_fds, None) {
-                Ok(alloc) => alloc,
-                Err(err) => {
-                    pr_warn!("Failure in copy_transaction_data: {:?}", err);
-                    return Err(err);
-                }
-            };
+            from.copy_transaction_data(to.clone(), info, info.debug_id, allow_fds, None)?;
         if info.flags & TF_CLEAR_BUF != 0 {
             alloc.set_info_clear_on_drop();
         }
diff --git a/rust/kernel/error.rs b/rust/kernel/error.rs
index 05cf869ac090..89c247f150b3 100644
--- a/rust/kernel/error.rs
+++ b/rust/kernel/error.rs
@@ -137,7 +137,7 @@ pub fn from_errno(errno: crate::ffi::c_int) -> Error {
     /// Creates an [`Error`] from a kernel error code.
     ///
     /// Returns [`None`] if `errno` is out-of-range.
-    const fn try_from_errno(errno: crate::ffi::c_int) -> Option<Error> {
+    pub const fn try_from_errno(errno: crate::ffi::c_int) -> Option<Error> {
         if errno < -(bindings::MAX_ERRNO as i32) || errno >= 0 {
             return None;
         }

-- 
2.55.0.rc0.799.gd6f94ed593-goog


^ permalink raw reply related	[flat|nested] 12+ messages in thread

* [PATCH 5/5] rust_binder: use pr_*_ratelimited! for printing
  2026-06-23 15:38 [PATCH 0/5] Rate limited printing for Rust Alice Ryhl
                   ` (3 preceding siblings ...)
  2026-06-23 15:38 ` [PATCH 4/5] rust_binder: consolidate transaction failure prints Alice Ryhl
@ 2026-06-23 15:38 ` Alice Ryhl
  4 siblings, 0 replies; 12+ messages in thread
From: Alice Ryhl @ 2026-06-23 15:38 UTC (permalink / raw)
  To: Greg Kroah-Hartman, Carlos Llamas, Boqun Feng, Gary Guo
  Cc: Onur Özkan, Andreas Hindborg, Benno Lossin,
	Björn Roy Baron, Daniel Almeida, Danilo Krummrich,
	Ingo Molnar, Lyude Paul, Miguel Ojeda, Peter Zijlstra,
	Trevor Gross, Waiman Long, Will Deacon, linux-kernel,
	rust-for-linux, Alice Ryhl

To avoid DoS from printing too much, make printing in Binder rate
limited. While we're at it, fix the missing newlines at the end of
several println statements.

Signed-off-by: Alice Ryhl <aliceryhl@google.com>
---
 drivers/android/binder/allocation.rs  |  4 +-
 drivers/android/binder/context.rs     |  6 +--
 drivers/android/binder/freeze.rs      | 22 +++++-----
 drivers/android/binder/node.rs        |  8 ++--
 drivers/android/binder/page_range.rs  | 12 +++---
 drivers/android/binder/process.rs     | 36 +++++++++--------
 drivers/android/binder/thread.rs      | 76 +++++++++++++++++++----------------
 drivers/android/binder/transaction.rs |  4 +-
 8 files changed, 90 insertions(+), 78 deletions(-)

diff --git a/drivers/android/binder/allocation.rs b/drivers/android/binder/allocation.rs
index b7b05e72970a..ed5f6c253c92 100644
--- a/drivers/android/binder/allocation.rs
+++ b/drivers/android/binder/allocation.rs
@@ -261,7 +261,7 @@ fn drop(&mut self) {
                 let view = AllocationView::new(self, offsets.start);
                 for i in offsets.step_by(size_of::<usize>()) {
                     if view.cleanup_object(i).is_err() {
-                        pr_warn!("Error cleaning up object at offset {}\n", i)
+                        pr_warn_ratelimited!("Error cleaning up object at offset {}\n", i)
                     }
                 }
             }
@@ -286,7 +286,7 @@ fn drop(&mut self) {
 
             if info.clear_on_free {
                 if let Err(e) = self.fill_zero() {
-                    pr_warn!("Failed to clear data on free: {:?}", e);
+                    pr_warn_ratelimited!("Failed to clear data on free: {:?}\n", e);
                 }
             }
         }
diff --git a/drivers/android/binder/context.rs b/drivers/android/binder/context.rs
index ddddb66b3557..431d6007a9b1 100644
--- a/drivers/android/binder/context.rs
+++ b/drivers/android/binder/context.rs
@@ -80,7 +80,7 @@ pub(crate) fn deregister(self: &Arc<Self>) {
 
     pub(crate) fn register_process(self: &Arc<Self>, proc: Arc<Process>) -> Result {
         if !Arc::ptr_eq(self, &proc.ctx) {
-            pr_err!("Context::register_process called on the wrong context.");
+            pr_err_ratelimited!("Context::register_process called on the wrong context.\n");
             return Err(EINVAL);
         }
         self.manager.lock().all_procs.push(proc, GFP_KERNEL)?;
@@ -89,7 +89,7 @@ pub(crate) fn register_process(self: &Arc<Self>, proc: Arc<Process>) -> Result {
 
     pub(crate) fn deregister_process(self: &Arc<Self>, proc: &Arc<Process>) {
         if !Arc::ptr_eq(self, &proc.ctx) {
-            pr_err!("Context::deregister_process called on the wrong context.");
+            pr_err_ratelimited!("Context::deregister_process called on the wrong context.\n");
             return;
         }
         let mut manager = self.manager.lock();
@@ -110,7 +110,7 @@ pub(crate) fn deregister_process(self: &Arc<Self>, proc: &Arc<Process>) {
     pub(crate) fn set_manager_node(&self, node_ref: NodeRef) -> Result {
         let mut manager = self.manager.lock();
         if manager.node.is_some() {
-            pr_warn!("BINDER_SET_CONTEXT_MGR already set");
+            pr_warn_ratelimited!("BINDER_SET_CONTEXT_MGR already set\n");
             return Err(EBUSY);
         }
         security::binder_set_context_mgr(&node_ref.node.owner.cred)?;
diff --git a/drivers/android/binder/freeze.rs b/drivers/android/binder/freeze.rs
index 20041689e98d..d721a58453b5 100644
--- a/drivers/android/binder/freeze.rs
+++ b/drivers/android/binder/freeze.rs
@@ -183,12 +183,12 @@ pub(crate) fn request_freeze_notif(
             info = match node_refs.by_handle.get_mut(&handle) {
                 Some(info) => info,
                 None => {
-                    pr_warn!("BC_REQUEST_FREEZE_NOTIFICATION invalid ref {}\n", handle);
+                    pr_warn_ratelimited!("BC_REQUEST_FREEZE_NOTIFICATION invalid ref {}\n", handle);
                     return Err(EINVAL);
                 }
             };
             if info.freeze().is_some() {
-                pr_warn!("BC_REQUEST_FREEZE_NOTIFICATION already set\n");
+                pr_warn_ratelimited!("BC_REQUEST_FREEZE_NOTIFICATION already set\n");
                 return Err(EINVAL);
             }
             let node_ref = info.node_ref();
@@ -197,7 +197,7 @@ pub(crate) fn request_freeze_notif(
 
             if let rbtree::Entry::Occupied(ref dupe) = freeze_entry {
                 if !dupe.get().allow_duplicate(&node_ref.node) {
-                    pr_warn!("BC_REQUEST_FREEZE_NOTIFICATION duplicate cookie\n");
+                    pr_warn_ratelimited!("BC_REQUEST_FREEZE_NOTIFICATION duplicate cookie\n");
                     return Err(EINVAL);
                 }
             }
@@ -262,7 +262,7 @@ pub(crate) fn freeze_notif_done(self: &Arc<Self>, reader: &mut UserSliceReader)
         let mut node_refs_guard = self.node_refs.lock();
         let node_refs = &mut *node_refs_guard;
         let Some(freeze) = node_refs.freeze_listeners.get_mut(&cookie) else {
-            pr_warn!("BC_FREEZE_NOTIFICATION_DONE {:016x} not found\n", cookie.0);
+            pr_warn_ratelimited!("BC_FREEZE_NOTIFICATION_DONE {:016x} not found\n", cookie.0);
             return Err(EINVAL);
         };
         let mut clear_msg = None;
@@ -272,7 +272,7 @@ pub(crate) fn freeze_notif_done(self: &Arc<Self>, reader: &mut UserSliceReader)
             freeze.num_cleared_duplicates += 1;
         } else {
             if !freeze.is_pending {
-                pr_warn!(
+                pr_warn_ratelimited!(
                     "BC_FREEZE_NOTIFICATION_DONE {:016x} not pending\n",
                     cookie.0
                 );
@@ -302,19 +302,21 @@ pub(crate) fn clear_freeze_notif(self: &Arc<Self>, reader: &mut UserSliceReader)
         let mut node_refs_guard = self.node_refs.lock();
         let node_refs = &mut *node_refs_guard;
         let Some(info) = node_refs.by_handle.get_mut(&handle) else {
-            pr_warn!("BC_CLEAR_FREEZE_NOTIFICATION invalid ref {}\n", handle);
+            pr_warn_ratelimited!("BC_CLEAR_FREEZE_NOTIFICATION invalid ref {}\n", handle);
             return Err(EINVAL);
         };
         let Some(info_cookie) = info.freeze() else {
-            pr_warn!("BC_CLEAR_FREEZE_NOTIFICATION freeze notification not active\n");
+            pr_warn_ratelimited!("BC_CLEAR_FREEZE_NOTIFICATION freeze notification not active\n");
             return Err(EINVAL);
         };
         if *info_cookie != cookie {
-            pr_warn!("BC_CLEAR_FREEZE_NOTIFICATION freeze notification cookie mismatch\n");
+            pr_warn_ratelimited!(
+                "BC_CLEAR_FREEZE_NOTIFICATION freeze notification cookie mismatch\n"
+            );
             return Err(EINVAL);
         }
         let Some(listener) = node_refs.freeze_listeners.get_mut(&cookie) else {
-            pr_warn!("BC_CLEAR_FREEZE_NOTIFICATION invalid cookie {}\n", handle);
+            pr_warn_ratelimited!("BC_CLEAR_FREEZE_NOTIFICATION invalid cookie {}\n", handle);
             return Err(EINVAL);
         };
         listener.is_clearing = true;
@@ -379,7 +381,7 @@ fn find_freeze_recipients(&self) -> Result<KVVec<(DArc<Node>, Arc<Process>)>, Al
                 recipients
                     .push_within_capacity(node_proc_pair)
                     .map_err(|_| {
-                        pr_err!(
+                        pr_err_ratelimited!(
                             "push_within_capacity failed even though we checked the capacity\n"
                         );
                         AllocError
diff --git a/drivers/android/binder/node.rs b/drivers/android/binder/node.rs
index 8041677908fd..350b6a6a7c83 100644
--- a/drivers/android/binder/node.rs
+++ b/drivers/android/binder/node.rs
@@ -343,7 +343,7 @@ pub(crate) fn inc_ref_done_locked(
     ) -> Option<DLArc<Node>> {
         let inner = self.inner.access_mut(owner_inner);
         if inner.active_inc_refs == 0 {
-            pr_err!("inc_ref_done called when no active inc_refs");
+            pr_err_ratelimited!("inc_ref_done called when no active inc_refs\n");
             return None;
         }
 
@@ -399,7 +399,7 @@ pub(crate) fn update_refcount_locked(
             !is_dead && !state.has_count
         } else {
             if state.count < count {
-                pr_err!("Failure: refcount underflow!");
+                pr_err_ratelimited!("Failure: refcount underflow!\n");
                 return None;
             }
             state.count -= count;
@@ -693,7 +693,7 @@ pub(crate) fn remove_freeze_listener(&self, p: &Arc<Process>) -> KVVec<Arc<Proce
         let len = inner.freeze_list.len();
         inner.freeze_list.retain(|proc| !Arc::ptr_eq(proc, p));
         if len == inner.freeze_list.len() {
-            pr_warn!(
+            pr_warn_ratelimited!(
                 "Could not remove freeze listener for {}\n",
                 p.pid_in_current_ns()
             );
@@ -865,7 +865,7 @@ pub(crate) fn update(&mut self, inc: bool, strong: bool) -> bool {
             *count += 1;
         } else {
             if *count == 0 {
-                pr_warn!(
+                pr_warn_ratelimited!(
                     "pid {} performed invalid decrement on ref\n",
                     kernel::current!().pid()
                 );
diff --git a/drivers/android/binder/page_range.rs b/drivers/android/binder/page_range.rs
index e82a5523804f..18ff80d05479 100644
--- a/drivers/android/binder/page_range.rs
+++ b/drivers/android/binder/page_range.rs
@@ -212,7 +212,7 @@ unsafe fn set_page(me: *mut PageInfo, page: Page) {
 
         // SAFETY: The pointer is valid for writing, so also valid for reading.
         if unsafe { (*ptr).is_some() } {
-            pr_err!("set_page called when there is already a page");
+            pr_err_ratelimited!("set_page called when there is already a page\n");
             // SAFETY: We will initialize the page again below.
             unsafe { ptr::drop_in_place(ptr) };
         }
@@ -300,11 +300,11 @@ pub(crate) fn register_with_vma(&self, vma: &virt::VmaNew) -> Result<usize> {
         let num_pages = num_bytes >> PAGE_SHIFT;
 
         if !ptr::eq::<Mm>(&*self.mm, &**vma.mm()) {
-            pr_debug!("Failed to register with vma: invalid vma->vm_mm");
+            pr_debug_ratelimited!("Failed to register with vma: invalid vma->vm_mm\n");
             return Err(EINVAL);
         }
         if num_pages == 0 {
-            pr_debug!("Failed to register with vma: size zero");
+            pr_debug_ratelimited!("Failed to register with vma: size zero\n");
             return Err(EINVAL);
         }
 
@@ -325,7 +325,7 @@ pub(crate) fn register_with_vma(&self, vma: &virt::VmaNew) -> Result<usize> {
 
         let mut inner = self.lock.lock();
         if inner.size > 0 {
-            pr_debug!("Failed to register with vma: already registered");
+            pr_debug_ratelimited!("Failed to register with vma: already registered\n");
             drop(inner);
             return Err(EBUSY);
         }
@@ -380,7 +380,7 @@ pub(crate) fn use_range(&self, start: usize, end: usize) -> Result<()> {
                 match unsafe { self.use_page_slow(i) } {
                     Ok(()) => {}
                     Err(err) => {
-                        pr_warn!("Error in use_page_slow: {:?}", err);
+                        pr_warn_ratelimited!("Error in use_page_slow: {:?}\n", err);
                         return Err(err);
                     }
                 }
@@ -529,7 +529,7 @@ unsafe fn iterate<T>(&self, mut offset: usize, mut size: usize, mut cb: T) -> Re
             // duration of this call to `iterate`, so nobody will change the page.
             let page = unsafe { PageInfo::get_page(page_info) };
             if page.is_none() {
-                pr_warn!("Page is null!");
+                pr_warn_ratelimited!("Page is null!\n");
             }
             let page = page.ok_or(EFAULT)?;
             cb(page, offset, available)?;
diff --git a/drivers/android/binder/process.rs b/drivers/android/binder/process.rs
index fd09e177f7e8..aeec16284956 100644
--- a/drivers/android/binder/process.rs
+++ b/drivers/android/binder/process.rs
@@ -314,7 +314,7 @@ pub(crate) fn death_delivered(&mut self, death: DArc<NodeDeath>) {
         if let Some(death) = ListArc::try_from_arc_or_drop(death) {
             self.delivered_deaths.push_back(death);
         } else {
-            pr_warn!("Notification added to `delivered_deaths` twice.");
+            pr_warn_ratelimited!("Notification added to `delivered_deaths` twice.\n");
         }
     }
 
@@ -691,7 +691,7 @@ fn get_current_thread(self: ArcBorrow<'_, Self>) -> Result<Arc<Thread>> {
         let id = {
             let current = kernel::current!();
             if self.task != current.group_leader() {
-                pr_err!("get_current_thread was called from the wrong process.");
+                pr_err_ratelimited!("get_current_thread was called from the wrong process.\n");
                 return Err(EINVAL);
             }
             current.pid()
@@ -715,7 +715,7 @@ fn get_current_thread(self: ArcBorrow<'_, Self>) -> Result<Arc<Thread>> {
                 Ok(ta)
             }
             rbtree::Entry::Occupied(_entry) => {
-                pr_err!("Cannot create two threads with the same id.");
+                pr_err_ratelimited!("Cannot create two threads with the same id.\n");
                 Err(EINVAL)
             }
         }
@@ -851,7 +851,9 @@ pub(crate) fn insert_or_update_handle(
                 match refs.by_handle.entry(res.as_u32()) {
                     rbtree::Entry::Vacant(entry) => break (res, entry),
                     rbtree::Entry::Occupied(_) => {
-                        pr_err!("Detected mismatch between handle_is_present and by_handle");
+                        pr_err_ratelimited!(
+                            "Detected mismatch between handle_is_present and by_handle\n"
+                        );
                         res.acquire();
                         kernel::warn_on!(true);
                         return Err(EINVAL);
@@ -991,7 +993,7 @@ pub(crate) fn update_ref(
         } else {
             // All refs are cleared in process exit, so this warning is expected in that case.
             if !self.inner.lock().is_dead {
-                pr_warn!("{}: no such ref {handle}\n", self.pid_in_current_ns());
+                pr_warn_ratelimited!("{}: no such ref {handle}\n", self.pid_in_current_ns());
             }
         }
         Ok(())
@@ -1071,7 +1073,7 @@ pub(crate) fn buffer_alloc(
         ) {
             Ok(()) => {}
             Err(err) => {
-                pr_warn!("use_range failure {:?}", err);
+                pr_warn_ratelimited!("use_range failure {:?}\n", err);
                 return Err(err.into());
             }
         }
@@ -1102,7 +1104,7 @@ pub(crate) fn buffer_raw_free(&self, ptr: usize) {
             let freed_range = match mapping.alloc.reservation_abort(offset) {
                 Ok(freed_range) => freed_range,
                 Err(_) => {
-                    pr_warn!(
+                    pr_warn_ratelimited!(
                         "Pointer {:x} failed to free, base = {:x}\n",
                         ptr,
                         mapping.address
@@ -1124,7 +1126,7 @@ pub(crate) fn buffer_make_freeable(&self, offset: usize, mut data: Option<Alloca
         let mut inner = self.inner.lock();
         if let Some(ref mut mapping) = &mut inner.mapping {
             if mapping.alloc.reservation_commit(offset, &mut data).is_err() {
-                pr_warn!("Offset {} failed to be marked freeable\n", offset);
+                pr_warn_ratelimited!("Offset {} failed to be marked freeable\n", offset);
             }
         }
     }
@@ -1244,13 +1246,13 @@ pub(crate) fn request_death(
         })?;
         let mut refs = self.node_refs.lock();
         let Some(info) = refs.by_handle.get_mut(&handle) else {
-            pr_warn!("BC_REQUEST_DEATH_NOTIFICATION invalid ref {handle}\n");
+            pr_warn_ratelimited!("BC_REQUEST_DEATH_NOTIFICATION invalid ref {handle}\n");
             return Ok(());
         };
 
         // Nothing to do if there is already a death notification request for this handle.
         if info.death().is_some() {
-            pr_warn!("BC_REQUEST_DEATH_NOTIFICATION death notification already set\n");
+            pr_warn_ratelimited!("BC_REQUEST_DEATH_NOTIFICATION death notification already set\n");
             return Ok(());
         }
 
@@ -1287,17 +1289,19 @@ pub(crate) fn clear_death(&self, reader: &mut UserSliceReader, thread: &Thread)
 
         let mut refs = self.node_refs.lock();
         let Some(info) = refs.by_handle.get_mut(&handle) else {
-            pr_warn!("BC_CLEAR_DEATH_NOTIFICATION invalid ref {handle}\n");
+            pr_warn_ratelimited!("BC_CLEAR_DEATH_NOTIFICATION invalid ref {handle}\n");
             return Ok(());
         };
 
         let Some(death) = info.death().take() else {
-            pr_warn!("BC_CLEAR_DEATH_NOTIFICATION death notification not active\n");
+            pr_warn_ratelimited!("BC_CLEAR_DEATH_NOTIFICATION death notification not active\n");
             return Ok(());
         };
         if death.cookie != cookie {
             *info.death() = Some(death);
-            pr_warn!("BC_CLEAR_DEATH_NOTIFICATION death notification cookie mismatch\n");
+            pr_warn_ratelimited!(
+                "BC_CLEAR_DEATH_NOTIFICATION death notification cookie mismatch\n"
+            );
             return Ok(());
         }
 
@@ -1454,7 +1458,7 @@ pub(crate) fn drop_outstanding_txn(&self) {
         let wake = {
             let mut inner = self.inner.lock();
             if inner.outstanding_txns == 0 {
-                pr_err!("outstanding_txns underflow");
+                pr_err_ratelimited!("outstanding_txns underflow\n");
                 return;
             }
             inner.outstanding_txns -= 1;
@@ -1768,7 +1772,7 @@ fn new(thread: &'a Arc<Thread>, guard: &mut Guard<'_, ProcessInner, SpinLockBack
             // It is an error to hit this branch, and it should not be reachable. We try to do
             // something reasonable when the failure path happens. Most likely, the thread in
             // question will sleep forever.
-            pr_err!("Same thread registered with `ready_threads` twice.");
+            pr_err_ratelimited!("Same thread registered with `ready_threads` twice.\n");
         }
         Self { thread }
     }
@@ -1793,7 +1797,7 @@ impl Drop for WithNodes<'_> {
     fn drop(&mut self) {
         core::mem::swap(&mut self.nodes, &mut self.inner.nodes);
         if self.nodes.iter().next().is_some() {
-            pr_err!("nodes array was modified while using lock_with_nodes\n");
+            pr_err_ratelimited!("nodes array was modified while using lock_with_nodes\n");
         }
     }
 }
diff --git a/drivers/android/binder/thread.rs b/drivers/android/binder/thread.rs
index a5b038877747..34950d86854c 100644
--- a/drivers/android/binder/thread.rs
+++ b/drivers/android/binder/thread.rs
@@ -159,8 +159,8 @@ fn validate_parent_fixup(
         let sg_entry = match self.sg_entries.get(sg_idx) {
             Some(sg_entry) => sg_entry,
             None => {
-                pr_err!(
-                    "self.ancestors[{}] is {}, but self.sg_entries.len() is {}",
+                pr_err_ratelimited!(
+                    "self.ancestors[{}] is {}, but self.sg_entries.len() is {}\n",
                     ancestors_i,
                     sg_idx,
                     self.sg_entries.len()
@@ -169,8 +169,8 @@ fn validate_parent_fixup(
             }
         };
         if sg_entry.fixup_min_offset > parent_offset {
-            pr_warn!(
-                "validate_parent_fixup: fixup_min_offset={}, parent_offset={}",
+            pr_warn_ratelimited!(
+                "validate_parent_fixup: fixup_min_offset={}, parent_offset={}\n",
                 sg_entry.fixup_min_offset,
                 parent_offset
             );
@@ -178,8 +178,8 @@ fn validate_parent_fixup(
         }
         let new_min_offset = parent_offset.checked_add(length).ok_or(EINVAL)?;
         if new_min_offset > sg_entry.length {
-            pr_warn!(
-                "validate_parent_fixup: new_min_offset={}, sg_entry.length={}",
+            pr_warn_ratelimited!(
+                "validate_parent_fixup: new_min_offset={}, sg_entry.length={}\n",
                 new_min_offset,
                 sg_entry.length
             );
@@ -328,7 +328,7 @@ fn push_reply_work(&mut self, code: u32) -> PushWorkRes {
             work.set_error_code(code);
             self.push_work(work)
         } else {
-            pr_warn!("Thread reply work is already in use.");
+            pr_warn_ratelimited!("Thread reply work is already in use.\n");
             PushWorkRes::Ok
         }
     }
@@ -339,7 +339,7 @@ fn push_return_work(&mut self, reply: u32) {
             // Not notifying: Reply to current thread.
             let _ = self.push_work(work);
         } else {
-            pr_warn!("Thread return work is already in use.");
+            pr_warn_ratelimited!("Thread return work is already in use.\n");
         }
     }
 
@@ -743,8 +743,8 @@ fn translate_object(
                 let alloc_offset = match sg_state.unused_buffer_space.claim_next(obj_length) {
                     Ok(alloc_offset) => alloc_offset,
                     Err(err) => {
-                        pr_warn!(
-                            "Failed to claim space for a BINDER_TYPE_PTR. (offset: {}, limit: {}, size: {})",
+                        pr_warn_ratelimited!(
+                            "Failed to claim space for a BINDER_TYPE_PTR. (offset: {}, limit: {}, size: {})\n",
                             sg_state.unused_buffer_space.offset,
                             sg_state.unused_buffer_space.limit,
                             obj_length,
@@ -790,8 +790,8 @@ fn translate_object(
                     let parent_entry = match sg_state.sg_entries.get_mut(info.parent_sg_index) {
                         Some(parent_entry) => parent_entry,
                         None => {
-                            pr_err!(
-                                "validate_parent_fixup returned index out of bounds for sg.entries"
+                            pr_err_ratelimited!(
+                                "validate_parent_fixup returned index out of bounds for sg.entries\n"
                             );
                             return Err(EINVAL.into());
                         }
@@ -836,8 +836,8 @@ fn translate_object(
                 let parent_entry = match sg_state.sg_entries.get_mut(info.parent_sg_index) {
                     Some(parent_entry) => parent_entry,
                     None => {
-                        pr_err!(
-                            "validate_parent_fixup returned index out of bounds for sg.entries"
+                        pr_err_ratelimited!(
+                            "validate_parent_fixup returned index out of bounds for sg.entries\n"
                         );
                         return Err(EINVAL.into());
                     }
@@ -869,7 +869,9 @@ fn translate_object(
                     .read_all(&mut fda_bytes, GFP_KERNEL)?;
 
                 if fds_len != fda_bytes.len() {
-                    pr_err!("UserSlice::read_all returned wrong length in BINDER_TYPE_FDA");
+                    pr_err_ratelimited!(
+                        "UserSlice::read_all returned wrong length in BINDER_TYPE_FDA\n"
+                    );
                     return Err(EINVAL.into());
                 }
 
@@ -927,8 +929,8 @@ fn apply_sg(&self, alloc: &mut Allocation, sg_state: &mut ScatterGatherState) ->
 
                 let target_offset_end = fixup_offset.checked_add(fixup_len).ok_or(EINVAL)?;
                 if fixup_offset < end_of_previous_fixup || offset_end < target_offset_end {
-                    pr_warn!(
-                        "Fixups oob {} {} {} {}",
+                    pr_warn_ratelimited!(
+                        "Fixups oob {} {} {} {}\n",
                         fixup_offset,
                         end_of_previous_fixup,
                         offset_end,
@@ -940,18 +942,18 @@ fn apply_sg(&self, alloc: &mut Allocation, sg_state: &mut ScatterGatherState) ->
                 let copy_off = end_of_previous_fixup;
                 let copy_len = fixup_offset - end_of_previous_fixup;
                 if let Err(err) = alloc.copy_into(&mut reader, copy_off, copy_len) {
-                    pr_warn!("Failed copying into alloc: {:?}", err);
+                    pr_warn_ratelimited!("Failed copying into alloc: {:?}\n", err);
                     return Err(err.into());
                 }
                 if let PointerFixupEntry::Fixup { pointer_value, .. } = fixup {
                     let res = alloc.write::<u64>(fixup_offset, pointer_value);
                     if let Err(err) = res {
-                        pr_warn!("Failed copying ptr into alloc: {:?}", err);
+                        pr_warn_ratelimited!("Failed copying ptr into alloc: {:?}\n", err);
                         return Err(err.into());
                     }
                 }
                 if let Err(err) = reader.skip(fixup_len) {
-                    pr_warn!("Failed skipping {} from reader: {:?}", fixup_len, err);
+                    pr_warn_ratelimited!("Failed skipping {} from reader: {:?}\n", fixup_len, err);
                     return Err(err.into());
                 }
                 end_of_previous_fixup = target_offset_end;
@@ -959,7 +961,7 @@ fn apply_sg(&self, alloc: &mut Allocation, sg_state: &mut ScatterGatherState) ->
             let copy_off = end_of_previous_fixup;
             let copy_len = offset_end - end_of_previous_fixup;
             if let Err(err) = alloc.copy_into(&mut reader, copy_off, copy_len) {
-                pr_warn!("Failed copying remainder into alloc: {:?}", err);
+                pr_warn_ratelimited!("Failed copying remainder into alloc: {:?}\n", err);
                 return Err(err.into());
             }
         }
@@ -984,7 +986,11 @@ pub(crate) fn copy_transaction_data(
             let ctx = match security::SecurityCtx::from_secid(secid) {
                 Ok(ctx) => ctx,
                 Err(err) => {
-                    pr_warn!("Failed to get security ctx for id {}: {:?}", secid, err);
+                    pr_warn_ratelimited!(
+                        "Failed to get security ctx for id {}: {:?}\n",
+                        secid,
+                        err
+                    );
                     return Err(err.into());
                 }
             };
@@ -1054,7 +1060,7 @@ pub(crate) fn copy_transaction_data(
                 let offset: usize = offset.try_into().map_err(|_| EINVAL)?;
 
                 if offset < end_of_previous_object || !is_aligned(offset, size_of::<u32>()) {
-                    pr_warn!("Got transaction with invalid offset.");
+                    pr_warn_ratelimited!("Got transaction with invalid offset.\n");
                     return Err(EINVAL.into());
                 }
 
@@ -1079,7 +1085,7 @@ pub(crate) fn copy_transaction_data(
                 ) {
                     Ok(()) => end_of_previous_object = offset + object.size(),
                     Err(err) => {
-                        pr_warn!("Error while translating object.");
+                        pr_warn_ratelimited!("Error while translating object.\n");
                         return Err(err);
                     }
                 }
@@ -1100,14 +1106,14 @@ pub(crate) fn copy_transaction_data(
 
         if let Some(sg_state) = sg_state.as_mut() {
             if let Err(err) = self.apply_sg(&mut alloc, sg_state) {
-                pr_warn!("Failure in apply_sg: {:?}", err);
+                pr_warn_ratelimited!("Failure in apply_sg: {:?}\n", err);
                 return Err(err);
             }
         }
 
         if let Some((off_out, secctx)) = secctx.as_mut() {
             if let Err(err) = alloc.write(secctx_off, secctx.as_bytes()) {
-                pr_warn!("Failed to write security context: {:?}", err);
+                pr_warn_ratelimited!("Failed to write security context: {:?}\n", err);
                 return Err(err.into());
             }
             **off_out = secctx_off;
@@ -1206,7 +1212,7 @@ fn top_of_transaction_stack(&self) -> Result<Option<DArc<Transaction>>> {
         let inner = self.inner.lock();
         if let Some(cur) = &inner.current_transaction {
             if core::ptr::eq(self, cur.from.as_ref()) {
-                pr_warn!("got new transaction with bad transaction stack");
+                pr_warn_ratelimited!("got new transaction with bad transaction stack\n");
                 return Err(EINVAL);
             }
             Ok(Some(cur.clone()))
@@ -1308,7 +1314,7 @@ fn transaction(self: &Arc<Self>, cmd: u32, reader: &mut UserSliceReader) -> Resu
                 reply: info.reply,
                 source: Error::try_from_errno(info.errno),
             };
-            pr_warn!(
+            pr_warn_ratelimited!(
                 "{}:{} {} to {} failed: {err:?}, {} bytes\n",
                 info.from_pid,
                 info.from_tid,
@@ -1343,7 +1349,7 @@ fn transaction_inner(self: &Arc<Self>, info: &mut TransactionInfo) -> BinderResu
         {
             let mut inner = self.inner.lock();
             if !transaction.is_stacked_on(&inner.current_transaction) {
-                pr_warn!("Transaction stack changed during transaction!");
+                pr_warn_ratelimited!("Transaction stack changed during transaction!\n");
                 return Err(EINVAL.into());
             }
             inner.current_transaction = Some(transaction.clone_arc());
@@ -1525,7 +1531,7 @@ fn read(self: &Arc<Self>, req: &mut BinderWriteRead, wait: bool) -> Result {
         let mut has_noop_placeholder = false;
         if req.read_consumed == 0 {
             if let Err(err) = writer.write_code(BR_NOOP) {
-                pr_warn!("Failure when writing BR_NOOP at beginning of buffer.");
+                pr_warn_ratelimited!("Failure when writing BR_NOOP at beginning of buffer.\n");
                 return Err(err);
             }
             has_noop_placeholder = true;
@@ -1548,7 +1554,7 @@ fn read(self: &Arc<Self>, req: &mut BinderWriteRead, wait: bool) -> Result {
                 Err(err) => {
                     // Propagate the error if we haven't written anything else.
                     if err != EINTR && err != EAGAIN {
-                        pr_warn!("Failure in work getter: {:?}", err);
+                        pr_warn_ratelimited!("Failure in work getter: {:?}\n", err);
                     }
                     if initial_len == writer.len() {
                         return Err(err);
@@ -1581,8 +1587,8 @@ pub(crate) fn write_read(self: &Arc<Self>, data: UserSlice, wait: bool) -> Resul
             ret = self.write(&mut req);
             crate::trace::trace_write_done(ret);
             if let Err(err) = ret {
-                pr_warn!(
-                    "Write failure {:?} in pid:{}",
+                pr_warn_ratelimited!(
+                    "Write failure {:?} in pid:{}\n",
                     err,
                     self.process.pid_in_current_ns()
                 );
@@ -1598,8 +1604,8 @@ pub(crate) fn write_read(self: &Arc<Self>, data: UserSlice, wait: bool) -> Resul
             ret = self.read(&mut req, wait);
             crate::trace::trace_read_done(ret);
             if ret.is_err() && ret != Err(EINTR) {
-                pr_warn!(
-                    "Read failure {:?} in pid:{}",
+                pr_warn_ratelimited!(
+                    "Read failure {:?} in pid:{}\n",
                     ret,
                     self.process.pid_in_current_ns()
                 );
diff --git a/drivers/android/binder/transaction.rs b/drivers/android/binder/transaction.rs
index 316627e5f9ac..bfbd02671b37 100644
--- a/drivers/android/binder/transaction.rs
+++ b/drivers/android/binder/transaction.rs
@@ -148,7 +148,7 @@ pub(crate) fn new(
         )?;
         if info.is_oneway() {
             if from_parent.is_some() {
-                pr_warn!("Oneway transaction should not be in a transaction stack.");
+                pr_warn_ratelimited!("Oneway transaction should not be in a transaction stack.\n");
                 return Err(EINVAL.into());
             }
             alloc.set_info_oneway_node(node_ref.node.clone());
@@ -343,7 +343,7 @@ pub(crate) fn submit(self: DLArc<Self>, info: &mut TransactionInfo) -> BinderRes
                     return Ok(());
                 }
             } else {
-                pr_err!("Failed to submit oneway transaction to node.");
+                pr_err_ratelimited!("Failed to submit oneway transaction to node.\n");
             }
         }
 

-- 
2.55.0.rc0.799.gd6f94ed593-goog


^ permalink raw reply related	[flat|nested] 12+ messages in thread

* Re: [PATCH 3/5] rust: add pr_*_ratelimit! macros for printing
  2026-06-23 15:38 ` [PATCH 3/5] rust: add pr_*_ratelimit! macros for printing Alice Ryhl
@ 2026-06-23 15:55   ` Gary Guo
  2026-06-23 19:11     ` Alice Ryhl
  2026-06-23 19:53     ` Miguel Ojeda
  2026-06-23 19:31   ` Miguel Ojeda
  1 sibling, 2 replies; 12+ messages in thread
From: Gary Guo @ 2026-06-23 15:55 UTC (permalink / raw)
  To: Alice Ryhl, Greg Kroah-Hartman, Carlos Llamas, Boqun Feng,
	Gary Guo
  Cc: Onur Özkan, Andreas Hindborg, Benno Lossin,
	Björn Roy Baron, Daniel Almeida, Danilo Krummrich,
	Ingo Molnar, Lyude Paul, Miguel Ojeda, Peter Zijlstra,
	Trevor Gross, Waiman Long, Will Deacon, linux-kernel,
	rust-for-linux

On Tue Jun 23, 2026 at 4:38 PM BST, Alice Ryhl wrote:
> Printing can be very expensive if it occurs often, so printing that can
> be triggered by userspace should be rate limited. For this purpose, add
> a Rust wrapper around `struct ratelimit_state` and use it in the new
> macros.
>
> Signed-off-by: Alice Ryhl <aliceryhl@google.com>
> ---
>  rust/helpers/helpers.c   |   1 +
>  rust/helpers/ratelimit.c |  14 ++++
>  rust/kernel/lib.rs       |   1 +
>  rust/kernel/prelude.rs   |   8 ++
>  rust/kernel/ratelimit.rs | 202 +++++++++++++++++++++++++++++++++++++++++++++++
>  5 files changed, 226 insertions(+)
>
> diff --git a/rust/helpers/helpers.c b/rust/helpers/helpers.c
> index d17eaec76450..2184b11c927f 100644
> --- a/rust/helpers/helpers.c
> +++ b/rust/helpers/helpers.c
> @@ -80,6 +80,7 @@
>  #include "processor.c"
>  #include "property.c"
>  #include "pwm.c"
> +#include "ratelimit.c"
>  #include "rbtree.c"
>  #include "rcu.c"
>  #include "refcount.c"
> diff --git a/rust/helpers/ratelimit.c b/rust/helpers/ratelimit.c
> new file mode 100644
> index 000000000000..e5052f568b81
> --- /dev/null
> +++ b/rust/helpers/ratelimit.c
> @@ -0,0 +1,14 @@
> +// SPDX-License-Identifier: GPL-2.0
> +
> +#include <linux/ratelimit.h>
> +
> +__rust_helper void rust_helper_ratelimit_state_init(struct ratelimit_state *rs,
> +						    int interval, int burst)
> +{
> +	ratelimit_state_init(rs, interval, burst);
> +}
> +
> +__rust_helper void rust_helper_ratelimit_state_exit(struct ratelimit_state *rs)
> +{
> +	ratelimit_state_exit(rs);
> +}
> diff --git a/rust/kernel/lib.rs b/rust/kernel/lib.rs
> index b72b2fbe046d..ba65ab4f0b8c 100644
> --- a/rust/kernel/lib.rs
> +++ b/rust/kernel/lib.rs
> @@ -111,6 +111,7 @@
>  pub mod ptr;
>  #[cfg(CONFIG_RUST_PWM_ABSTRACTIONS)]
>  pub mod pwm;
> +pub mod ratelimit;
>  pub mod rbtree;
>  pub mod regulator;
>  pub mod revocable;
> diff --git a/rust/kernel/prelude.rs b/rust/kernel/prelude.rs
> index 44edf72a4a24..5a66028dd973 100644
> --- a/rust/kernel/prelude.rs
> +++ b/rust/kernel/prelude.rs
> @@ -89,13 +89,21 @@
>      },
>      init::InPlaceInit,
>      pr_alert,
> +    pr_alert_ratelimited,
>      pr_crit,
> +    pr_crit_ratelimited,
>      pr_debug,
> +    pr_debug_ratelimited,
>      pr_emerg,
> +    pr_emerg_ratelimited,
>      pr_err,
> +    pr_err_ratelimited,
>      pr_info,
> +    pr_info_ratelimited,
>      pr_notice,
> +    pr_notice_ratelimited,
>      pr_warn,
> +    pr_warn_ratelimited,
>      static_assert,
>      str::CStrExt as _,

I really want this to be just a variant of pr_, not a entire new family of
macros.

Last time I raise this in
https://lore.kernel.org/rust-for-linux/20260108125523.5c7810ae.gary@garyguo.net/
, I was suggesting unifying pr_ and dev_ too, which might be a step too much. 

But I think just making ratelimited a keyword in `pr_` macro should be alright?

pr_info!("foo"); // pr_info("foo\n");
pr_info!(once, "foo"); // pr_info_once("foo\n");
pr_info!(ratelimited, "foo"); // pr_info_ratelimited("foo\n");
dev_info!(dev, "foo"); // dev_info(dev, "foo\n");
dev_info!(dev, once, "foo"); // dev_info_once(dev, "foo\n");
dev_info!(dev, ratelimited, "foo"); // dev_info_ratelimited(dev, "foo\n");

Best,
Gary

^ permalink raw reply	[flat|nested] 12+ messages in thread

* Re: [PATCH 3/5] rust: add pr_*_ratelimit! macros for printing
  2026-06-23 15:55   ` Gary Guo
@ 2026-06-23 19:11     ` Alice Ryhl
  2026-06-23 19:53     ` Miguel Ojeda
  1 sibling, 0 replies; 12+ messages in thread
From: Alice Ryhl @ 2026-06-23 19:11 UTC (permalink / raw)
  To: Gary Guo
  Cc: Greg Kroah-Hartman, Carlos Llamas, Boqun Feng, Onur Özkan,
	Andreas Hindborg, Benno Lossin, Björn Roy Baron,
	Daniel Almeida, Danilo Krummrich, Ingo Molnar, Lyude Paul,
	Miguel Ojeda, Peter Zijlstra, Trevor Gross, Waiman Long,
	Will Deacon, linux-kernel, rust-for-linux

On Tue, Jun 23, 2026 at 04:55:31PM +0100, Gary Guo wrote:
> On Tue Jun 23, 2026 at 4:38 PM BST, Alice Ryhl wrote:
> > Printing can be very expensive if it occurs often, so printing that can
> > be triggered by userspace should be rate limited. For this purpose, add
> > a Rust wrapper around `struct ratelimit_state` and use it in the new
> > macros.
> >
> > Signed-off-by: Alice Ryhl <aliceryhl@google.com>
> > ---
> >  rust/helpers/helpers.c   |   1 +
> >  rust/helpers/ratelimit.c |  14 ++++
> >  rust/kernel/lib.rs       |   1 +
> >  rust/kernel/prelude.rs   |   8 ++
> >  rust/kernel/ratelimit.rs | 202 +++++++++++++++++++++++++++++++++++++++++++++++
> >  5 files changed, 226 insertions(+)
> >
> > diff --git a/rust/helpers/helpers.c b/rust/helpers/helpers.c
> > index d17eaec76450..2184b11c927f 100644
> > --- a/rust/helpers/helpers.c
> > +++ b/rust/helpers/helpers.c
> > @@ -80,6 +80,7 @@
> >  #include "processor.c"
> >  #include "property.c"
> >  #include "pwm.c"
> > +#include "ratelimit.c"
> >  #include "rbtree.c"
> >  #include "rcu.c"
> >  #include "refcount.c"
> > diff --git a/rust/helpers/ratelimit.c b/rust/helpers/ratelimit.c
> > new file mode 100644
> > index 000000000000..e5052f568b81
> > --- /dev/null
> > +++ b/rust/helpers/ratelimit.c
> > @@ -0,0 +1,14 @@
> > +// SPDX-License-Identifier: GPL-2.0
> > +
> > +#include <linux/ratelimit.h>
> > +
> > +__rust_helper void rust_helper_ratelimit_state_init(struct ratelimit_state *rs,
> > +						    int interval, int burst)
> > +{
> > +	ratelimit_state_init(rs, interval, burst);
> > +}
> > +
> > +__rust_helper void rust_helper_ratelimit_state_exit(struct ratelimit_state *rs)
> > +{
> > +	ratelimit_state_exit(rs);
> > +}
> > diff --git a/rust/kernel/lib.rs b/rust/kernel/lib.rs
> > index b72b2fbe046d..ba65ab4f0b8c 100644
> > --- a/rust/kernel/lib.rs
> > +++ b/rust/kernel/lib.rs
> > @@ -111,6 +111,7 @@
> >  pub mod ptr;
> >  #[cfg(CONFIG_RUST_PWM_ABSTRACTIONS)]
> >  pub mod pwm;
> > +pub mod ratelimit;
> >  pub mod rbtree;
> >  pub mod regulator;
> >  pub mod revocable;
> > diff --git a/rust/kernel/prelude.rs b/rust/kernel/prelude.rs
> > index 44edf72a4a24..5a66028dd973 100644
> > --- a/rust/kernel/prelude.rs
> > +++ b/rust/kernel/prelude.rs
> > @@ -89,13 +89,21 @@
> >      },
> >      init::InPlaceInit,
> >      pr_alert,
> > +    pr_alert_ratelimited,
> >      pr_crit,
> > +    pr_crit_ratelimited,
> >      pr_debug,
> > +    pr_debug_ratelimited,
> >      pr_emerg,
> > +    pr_emerg_ratelimited,
> >      pr_err,
> > +    pr_err_ratelimited,
> >      pr_info,
> > +    pr_info_ratelimited,
> >      pr_notice,
> > +    pr_notice_ratelimited,
> >      pr_warn,
> > +    pr_warn_ratelimited,
> >      static_assert,
> >      str::CStrExt as _,
> 
> I really want this to be just a variant of pr_, not a entire new family of
> macros.
> 
> Last time I raise this in
> https://lore.kernel.org/rust-for-linux/20260108125523.5c7810ae.gary@garyguo.net/
> , I was suggesting unifying pr_ and dev_ too, which might be a step too much. 
> 
> But I think just making ratelimited a keyword in `pr_` macro should be alright?
> 
> pr_info!("foo"); // pr_info("foo\n");
> pr_info!(once, "foo"); // pr_info_once("foo\n");
> pr_info!(ratelimited, "foo"); // pr_info_ratelimited("foo\n");
> dev_info!(dev, "foo"); // dev_info(dev, "foo\n");
> dev_info!(dev, once, "foo"); // dev_info_once(dev, "foo\n");
> dev_info!(dev, ratelimited, "foo"); // dev_info_ratelimited(dev, "foo\n");

I did it this way for consistency with the existing pr_*_once!.

Alice

^ permalink raw reply	[flat|nested] 12+ messages in thread

* Re: [PATCH 3/5] rust: add pr_*_ratelimit! macros for printing
  2026-06-23 15:38 ` [PATCH 3/5] rust: add pr_*_ratelimit! macros for printing Alice Ryhl
  2026-06-23 15:55   ` Gary Guo
@ 2026-06-23 19:31   ` Miguel Ojeda
  2026-06-23 20:05     ` Alice Ryhl
  1 sibling, 1 reply; 12+ messages in thread
From: Miguel Ojeda @ 2026-06-23 19:31 UTC (permalink / raw)
  To: Alice Ryhl
  Cc: Greg Kroah-Hartman, Carlos Llamas, Boqun Feng, Gary Guo,
	Onur Özkan, Andreas Hindborg, Benno Lossin,
	Björn Roy Baron, Daniel Almeida, Danilo Krummrich,
	Ingo Molnar, Lyude Paul, Miguel Ojeda, Peter Zijlstra,
	Trevor Gross, Waiman Long, Will Deacon, linux-kernel,
	rust-for-linux

On Tue, Jun 23, 2026 at 5:38 PM Alice Ryhl <aliceryhl@google.com> wrote:
>
> Printing can be very expensive if it occurs often, so printing that can
> be triggered by userspace should be rate limited. For this purpose, add
> a Rust wrapper around `struct ratelimit_state` and use it in the new
> macros.
>
> Signed-off-by: Alice Ryhl <aliceryhl@google.com>

Link: https://github.com/Rust-for-Linux/linux/issues/122

(I back-linked to this thread from there too.)

> +            // SAFETY: This is a repr(transparent) wrapper, and the invariants are satisfied.

`repr(transparent)`

> +    /// Returns `true` if the action is allowed, and `false` if it should be suppressed.

[`true`]
[`false`]

I can change all these on apply.

Cheers,
Miguel

^ permalink raw reply	[flat|nested] 12+ messages in thread

* Re: [PATCH 3/5] rust: add pr_*_ratelimit! macros for printing
  2026-06-23 15:55   ` Gary Guo
  2026-06-23 19:11     ` Alice Ryhl
@ 2026-06-23 19:53     ` Miguel Ojeda
  2026-06-23 20:06       ` Gary Guo
  1 sibling, 1 reply; 12+ messages in thread
From: Miguel Ojeda @ 2026-06-23 19:53 UTC (permalink / raw)
  To: Gary Guo
  Cc: Alice Ryhl, Greg Kroah-Hartman, Carlos Llamas, Boqun Feng,
	Onur Özkan, Andreas Hindborg, Benno Lossin,
	Björn Roy Baron, Daniel Almeida, Danilo Krummrich,
	Ingo Molnar, Lyude Paul, Miguel Ojeda, Peter Zijlstra,
	Trevor Gross, Waiman Long, Will Deacon, linux-kernel,
	rust-for-linux

On Tue, Jun 23, 2026 at 5:55 PM Gary Guo <gary@garyguo.net> wrote:
>
> I really want this to be just a variant of pr_, not a entire new family of
> macros.

I think both approaches are fine. Personally I would like a `pr!`
macro, but I am not convinced the advantages are big enough to drop
the similitude/familiarity with C.

It may be the kind of proposal that is best discussed in Kangrejos,
LPC or similar in order to convince enough people and move it
forward... :)

Cheers,
Miguel

^ permalink raw reply	[flat|nested] 12+ messages in thread

* Re: [PATCH 3/5] rust: add pr_*_ratelimit! macros for printing
  2026-06-23 19:31   ` Miguel Ojeda
@ 2026-06-23 20:05     ` Alice Ryhl
  0 siblings, 0 replies; 12+ messages in thread
From: Alice Ryhl @ 2026-06-23 20:05 UTC (permalink / raw)
  To: Miguel Ojeda
  Cc: Greg Kroah-Hartman, Carlos Llamas, Boqun Feng, Gary Guo,
	Onur Özkan, Andreas Hindborg, Benno Lossin,
	Björn Roy Baron, Daniel Almeida, Danilo Krummrich,
	Ingo Molnar, Lyude Paul, Miguel Ojeda, Peter Zijlstra,
	Trevor Gross, Waiman Long, Will Deacon, linux-kernel,
	rust-for-linux

On Tue, Jun 23, 2026 at 9:32 PM Miguel Ojeda
<miguel.ojeda.sandonis@gmail.com> wrote:
>
> On Tue, Jun 23, 2026 at 5:38 PM Alice Ryhl <aliceryhl@google.com> wrote:
> >
> > Printing can be very expensive if it occurs often, so printing that can
> > be triggered by userspace should be rate limited. For this purpose, add
> > a Rust wrapper around `struct ratelimit_state` and use it in the new
> > macros.
> >
> > Signed-off-by: Alice Ryhl <aliceryhl@google.com>
>
> Link: https://github.com/Rust-for-Linux/linux/issues/122
>
> (I back-linked to this thread from there too.)
>
> > +            // SAFETY: This is a repr(transparent) wrapper, and the invariants are satisfied.
>
> `repr(transparent)`
>
> > +    /// Returns `true` if the action is allowed, and `false` if it should be suppressed.
>
> [`true`]
> [`false`]

Thanks!

> I can change all these on apply.

This series has conflicts with other Rust Binder series, so it may be
easier if Greg takes it.

Alice

^ permalink raw reply	[flat|nested] 12+ messages in thread

* Re: [PATCH 3/5] rust: add pr_*_ratelimit! macros for printing
  2026-06-23 19:53     ` Miguel Ojeda
@ 2026-06-23 20:06       ` Gary Guo
  0 siblings, 0 replies; 12+ messages in thread
From: Gary Guo @ 2026-06-23 20:06 UTC (permalink / raw)
  To: Miguel Ojeda, Gary Guo
  Cc: Alice Ryhl, Greg Kroah-Hartman, Carlos Llamas, Boqun Feng,
	Onur Özkan, Andreas Hindborg, Benno Lossin,
	Björn Roy Baron, Daniel Almeida, Danilo Krummrich,
	Ingo Molnar, Lyude Paul, Miguel Ojeda, Peter Zijlstra,
	Trevor Gross, Waiman Long, Will Deacon, linux-kernel,
	rust-for-linux

On Tue Jun 23, 2026 at 8:53 PM BST, Miguel Ojeda wrote:
> On Tue, Jun 23, 2026 at 5:55 PM Gary Guo <gary@garyguo.net> wrote:
>>
>> I really want this to be just a variant of pr_, not a entire new family of
>> macros.
>
> I think both approaches are fine. Personally I would like a `pr!`
> macro, but I am not convinced the advantages are big enough to drop
> the similitude/familiarity with C.

The previous proposal that I have on merging pr_ and dev_ macros (and add
implicit newline) is arguably not similar, but I think

    pr_info_ratelimited(...)

is similar enough to both

    pr_info_ratelimited!(...)

and

    pr_info!(ratelimited, ...)

In C the second one is not doable due to expressivity limitation, but I think
our Rust macros shouldn't be restricted to be as expressive as C :)

(Also, even if we don't merge pr_ and dev_ print macros, we could still try to
unify the implementation so they share the same plumbing but different macro
entrypoint)

Best,
Gary

>
> It may be the kind of proposal that is best discussed in Kangrejos,
> LPC or similar in order to convince enough people and move it
> forward... :)
>
> Cheers,
> Miguel



^ permalink raw reply	[flat|nested] 12+ messages in thread

end of thread, other threads:[~2026-06-23 20:06 UTC | newest]

Thread overview: 12+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2026-06-23 15:38 [PATCH 0/5] Rate limited printing for Rust Alice Ryhl
2026-06-23 15:38 ` [PATCH 1/5] rust: sync: move lockdep types to rust/kernel/sync/lockdep.rs Alice Ryhl
2026-06-23 15:38 ` [PATCH 2/5] rust: sync: add const constructor for raw_spinlock_t Alice Ryhl
2026-06-23 15:38 ` [PATCH 3/5] rust: add pr_*_ratelimit! macros for printing Alice Ryhl
2026-06-23 15:55   ` Gary Guo
2026-06-23 19:11     ` Alice Ryhl
2026-06-23 19:53     ` Miguel Ojeda
2026-06-23 20:06       ` Gary Guo
2026-06-23 19:31   ` Miguel Ojeda
2026-06-23 20:05     ` Alice Ryhl
2026-06-23 15:38 ` [PATCH 4/5] rust_binder: consolidate transaction failure prints Alice Ryhl
2026-06-23 15:38 ` [PATCH 5/5] rust_binder: use pr_*_ratelimited! for printing Alice Ryhl

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox