* [PATCH RFC 1/2] rust: sync: introduce a way to create lock class from caller
2026-07-03 13:47 [PATCH RFC 0/2] rust: sync: create lock class using `#[track_caller]` Gary Guo
@ 2026-07-03 13:47 ` Gary Guo
2026-07-03 13:47 ` [PATCH RFC 2/2] lockdep: delegate Rust lock class printing to Rust code Gary Guo
2026-07-03 14:01 ` [PATCH RFC 0/2] rust: sync: create lock class using `#[track_caller]` Peter Zijlstra
2 siblings, 0 replies; 9+ messages in thread
From: Gary Guo @ 2026-07-03 13:47 UTC (permalink / raw)
To: Boqun Feng, Alice Ryhl, Lyude Paul, Daniel Almeida,
Onur Özkan, Miguel Ojeda, Björn Roy Baron, Benno Lossin,
Andreas Hindborg, Trevor Gross, Danilo Krummrich,
Tamir Duberstein, Alexandre Courbot, Peter Zijlstra, Ingo Molnar,
Will Deacon, Waiman Long
Cc: linux-kernel, rust-for-linux, Gary Guo
Rust provides a `#[track_caller]` mechanism that can be used to identify
callers from callees. This also works across multiple level of calls.
As lockdep only needs the address for static key classes, the address of
these `&'static Location` can be used as unique lock class keys.
This allows a more implicit way of creating lock classes without having to
use macros.
Create `LockClassKey::from_caller`, and use it for mutex and spinlocks.
Signed-off-by: Gary Guo <gary@garyguo.net>
---
rust/kernel/sync.rs | 37 +++++++++++++++++++++++++++++++++----
rust/kernel/sync/lock.rs | 13 +++++++++++--
rust/kernel/sync/lock/mutex.rs | 3 +--
rust/kernel/sync/lock/spinlock.rs | 3 +--
4 files changed, 46 insertions(+), 10 deletions(-)
diff --git a/rust/kernel/sync.rs b/rust/kernel/sync.rs
index 993dbf2caa0e..cf76fb37c460 100644
--- a/rust/kernel/sync.rs
+++ b/rust/kernel/sync.rs
@@ -5,8 +5,12 @@
//! 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 core::panic::Location;
+
+use crate::{
+ prelude::*,
+ types::Opaque, //
+};
use pin_init;
mod arc;
@@ -66,6 +70,31 @@ impl LockClassKey {
}
}
+ /// Obtain a statically allocated lock class key identified by the caller's location.
+ ///
+ /// Different caller locations will be guaranteed to have different lock class keys; however for
+ /// the same caller location, this may return different keys, e.g. if the caller is instantiated
+ /// in different object files.
+ #[inline]
+ #[track_caller]
+ pub const fn from_caller() -> Pin<&'static Self> {
+ static_assert!(size_of::<Location<'_>>() >= size_of::<LockClassKey>());
+
+ #[cfg(CONFIG_LOCKDEP)]
+ {
+ let caller = Location::caller();
+ // SAFETY: For static objects, lockdep does not touch the underlying memory, and only
+ // the address matters. `Location::caller()` is in rodata so it meets the requirement.
+ // The size check above makes sure that it does not overlap with other lock class keys.
+ unsafe { Pin::new_unchecked(&*core::ptr::from_ref(caller).cast::<Self>()) }
+ }
+
+ // A separate version if lockdep is disabled to avoid unnecessary `Location` being
+ // generated.
+ #[cfg(not(CONFIG_LOCKDEP))]
+ static_lock_class!()
+ }
+
/// Initializes a dynamically allocated lock class key.
///
/// In the common case of using a statically allocated lock class key, the
@@ -83,7 +112,7 @@ impl LockClassKey {
/// let key_ptr = key.into_foreign();
///
/// {
- /// stack_pin_init!(let num: SpinLock<u32> = SpinLock::new(
+ /// stack_pin_init!(let num: SpinLock<u32> = SpinLock::new_with_lock_class(
/// 0,
/// c"my_spinlock",
/// // SAFETY: `key_ptr` is returned by the above `into_foreign()`, whose
@@ -129,7 +158,7 @@ fn drop(self: Pin<&mut Self>) {
/// use kernel::sync::{static_lock_class, Arc, SpinLock};
///
/// fn new_locked_int() -> Result<Arc<SpinLock<u32>>> {
-/// Arc::pin_init(SpinLock::new(
+/// Arc::pin_init(SpinLock::new_with_lock_class(
/// 42,
/// c"new_locked_int",
/// static_lock_class!(),
diff --git a/rust/kernel/sync/lock.rs b/rust/kernel/sync/lock.rs
index 10b6b5e9b024..447fec291cdf 100644
--- a/rust/kernel/sync/lock.rs
+++ b/rust/kernel/sync/lock.rs
@@ -127,8 +127,17 @@ unsafe impl<T: ?Sized + Send, B: Backend> Send for Lock<T, B> {}
unsafe impl<T: ?Sized + Send, B: Backend> Sync for Lock<T, B> {}
impl<T, B: Backend> Lock<T, B> {
- /// Constructs a new lock initialiser.
- pub fn new(
+ /// Constructs a new lock initialiser with a custom name.
+ #[inline]
+ #[track_caller]
+ pub fn new_with_name(t: impl PinInit<T>, name: &'static CStr) -> impl PinInit<Self> {
+ let key = LockClassKey::from_caller();
+ Self::new_with_lock_class(t, name, key)
+ }
+
+ /// Constructs a new lock initialiser with a custom name and lock class key.
+ #[inline]
+ pub fn new_with_lock_class(
t: impl PinInit<T>,
name: &'static CStr,
key: Pin<&'static LockClassKey>,
diff --git a/rust/kernel/sync/lock/mutex.rs b/rust/kernel/sync/lock/mutex.rs
index cda0203efefb..3675ce244e08 100644
--- a/rust/kernel/sync/lock/mutex.rs
+++ b/rust/kernel/sync/lock/mutex.rs
@@ -11,8 +11,7 @@
#[macro_export]
macro_rules! new_mutex {
($inner:expr $(, $name:literal)? $(,)?) => {
- $crate::sync::Mutex::new(
- $inner, $crate::optional_name!($($name)?), $crate::static_lock_class!())
+ $crate::sync::Mutex::new_with_name($inner, $crate::optional_name!($($name)?))
};
}
pub use new_mutex;
diff --git a/rust/kernel/sync/lock/spinlock.rs b/rust/kernel/sync/lock/spinlock.rs
index ef76fa07ca3a..091167ceda66 100644
--- a/rust/kernel/sync/lock/spinlock.rs
+++ b/rust/kernel/sync/lock/spinlock.rs
@@ -11,8 +11,7 @@
#[macro_export]
macro_rules! new_spinlock {
($inner:expr $(, $name:literal)? $(,)?) => {
- $crate::sync::SpinLock::new(
- $inner, $crate::optional_name!($($name)?), $crate::static_lock_class!())
+ $crate::sync::SpinLock::new_with_name($inner, $crate::optional_name!($($name)?))
};
}
pub use new_spinlock;
--
2.54.0
^ permalink raw reply related [flat|nested] 9+ messages in thread* [PATCH RFC 2/2] lockdep: delegate Rust lock class printing to Rust code
2026-07-03 13:47 [PATCH RFC 0/2] rust: sync: create lock class using `#[track_caller]` Gary Guo
2026-07-03 13:47 ` [PATCH RFC 1/2] rust: sync: introduce a way to create lock class from caller Gary Guo
@ 2026-07-03 13:47 ` Gary Guo
2026-07-03 14:01 ` [PATCH RFC 0/2] rust: sync: create lock class using `#[track_caller]` Peter Zijlstra
2 siblings, 0 replies; 9+ messages in thread
From: Gary Guo @ 2026-07-03 13:47 UTC (permalink / raw)
To: Boqun Feng, Alice Ryhl, Lyude Paul, Daniel Almeida,
Onur Özkan, Miguel Ojeda, Björn Roy Baron, Benno Lossin,
Andreas Hindborg, Trevor Gross, Danilo Krummrich,
Tamir Duberstein, Alexandre Courbot, Peter Zijlstra, Ingo Molnar,
Will Deacon, Waiman Long
Cc: linux-kernel, rust-for-linux, Gary Guo
Add a special "(rust)" name which means that the lock class comes from Rust
`#[track_caller]` and thus should be delegated to Rust code to print the
name.
This allows locks to be created with usual constructor syntax and does not
need macros anymore.
Signed-off-by: Gary Guo <gary@garyguo.net>
---
kernel/locking/lockdep.c | 7 ++++++-
kernel/locking/lockdep_internals.h | 3 +++
rust/kernel/sync.rs | 24 ++++++++++++++++++++++++
rust/kernel/sync/lock.rs | 8 ++++++++
rust/kernel/sync/lock/mutex.rs | 8 ++++----
rust/kernel/sync/lock/spinlock.rs | 8 ++++----
6 files changed, 49 insertions(+), 9 deletions(-)
diff --git a/kernel/locking/lockdep.c b/kernel/locking/lockdep.c
index 2d4c5bab5af8..1e05d51e710f 100644
--- a/kernel/locking/lockdep.c
+++ b/kernel/locking/lockdep.c
@@ -724,7 +724,12 @@ static void __print_lock_name(struct held_lock *hlock, struct lock_class *class)
name = __get_key_name(class->key, str);
printk(KERN_CONT "%s", name);
} else {
- printk(KERN_CONT "%s", name);
+ if (CONFIG_RUST && class->name == lockdep_rust_name)
+ lockdep_print_rust_name((struct lock_class_key *)(
+ class->key - class->subclass
+ ));
+ else
+ printk(KERN_CONT "%s", name);
if (class->name_version > 1)
printk(KERN_CONT "#%d", class->name_version);
if (class->subclass)
diff --git a/kernel/locking/lockdep_internals.h b/kernel/locking/lockdep_internals.h
index 0e5e6ffe91a3..d18d50f7fd99 100644
--- a/kernel/locking/lockdep_internals.h
+++ b/kernel/locking/lockdep_internals.h
@@ -134,6 +134,9 @@ extern void get_usage_chars(struct lock_class *class,
extern const char *__get_key_name(const struct lockdep_subclass_key *key,
char *str);
+extern const char lockdep_rust_name[];
+extern void lockdep_print_rust_name(struct lock_class_key *key);
+
struct lock_class *lock_chain_get_class(struct lock_chain *chain, int i);
extern unsigned long nr_lock_classes;
diff --git a/rust/kernel/sync.rs b/rust/kernel/sync.rs
index cf76fb37c460..cf35858469d1 100644
--- a/rust/kernel/sync.rs
+++ b/rust/kernel/sync.rs
@@ -8,6 +8,7 @@
use core::panic::Location;
use crate::{
+ pr_cont,
prelude::*,
types::Opaque, //
};
@@ -150,6 +151,29 @@ fn drop(self: Pin<&mut Self>) {
}
}
+// We want this to be completely unique so lockdep can identify when lock classes are generated with
+// `new_static` mechanism, hence making this `static` and attach a `link_section` so it cannot be
+// merged with other constants.
+#[export_name = "lockdep_rust_name"]
+#[link_section = ".rodata"]
+static LOCKDEP_RUST_NAME_BYTES: [u8; 7] = *b"(rust)\0";
+
+// This should only be used in conjunction of `LockClassKey::from_caller`.
+const LOCKDEP_RUST_NAME: &CStr = match CStr::from_bytes_with_nul(&LOCKDEP_RUST_NAME_BYTES) {
+ Ok(v) => v,
+ Err(_) => unreachable!(),
+};
+
+#[cfg(CONFIG_LOCKDEP)]
+#[expect(clippy::missing_safety_doc)]
+#[no_mangle]
+unsafe extern "C" fn lockdep_print_rust_name(key: *mut bindings::lock_class_key) {
+ // SAFETY: `rust_print_lockdep_name` is called when the lock name is a reserved value indicating
+ // that the lock_class_key is static and backed by a `Location`.
+ let location = unsafe { &*key.cast::<Location<'_>>() };
+ pr_cont!("{}:{}", location.file(), location.line());
+}
+
/// Defines a new static lock class and returns a pointer to it.
///
/// # Examples
diff --git a/rust/kernel/sync/lock.rs b/rust/kernel/sync/lock.rs
index 447fec291cdf..66d51c860483 100644
--- a/rust/kernel/sync/lock.rs
+++ b/rust/kernel/sync/lock.rs
@@ -127,6 +127,14 @@ unsafe impl<T: ?Sized + Send, B: Backend> Send for Lock<T, B> {}
unsafe impl<T: ?Sized + Send, B: Backend> Sync for Lock<T, B> {}
impl<T, B: Backend> Lock<T, B> {
+ /// Constructs a new lock initialiser with a custom name.
+ #[inline]
+ #[track_caller]
+ pub fn new(t: impl PinInit<T>) -> impl PinInit<Self> {
+ let key = LockClassKey::from_caller();
+ Self::new_with_lock_class(t, super::LOCKDEP_RUST_NAME, key)
+ }
+
/// Constructs a new lock initialiser with a custom name.
#[inline]
#[track_caller]
diff --git a/rust/kernel/sync/lock/mutex.rs b/rust/kernel/sync/lock/mutex.rs
index 3675ce244e08..c730196b9d2d 100644
--- a/rust/kernel/sync/lock/mutex.rs
+++ b/rust/kernel/sync/lock/mutex.rs
@@ -24,8 +24,8 @@ macro_rules! new_mutex {
///
/// Since it may block, [`Mutex`] needs to be used with care in atomic contexts.
///
-/// Instances of [`Mutex`] need a lock class and to be pinned. The recommended way to create such
-/// instances is with the [`pin_init`](pin_init::pin_init) and [`new_mutex`] macros.
+/// Instances of [`Mutex`] need to be pinned. You can create such instances with the
+/// [`pin_init`](pin_init::pin_init).
///
/// # Examples
///
@@ -33,7 +33,7 @@ macro_rules! new_mutex {
/// contains an inner struct (`Inner`) that is protected by a mutex.
///
/// ```
-/// use kernel::sync::{new_mutex, Mutex};
+/// use kernel::sync::Mutex;
///
/// struct Inner {
/// a: u32,
@@ -51,7 +51,7 @@ macro_rules! new_mutex {
/// fn new() -> impl PinInit<Self> {
/// pin_init!(Self {
/// c: 10,
-/// d <- new_mutex!(Inner { a: 20, b: 30 }),
+/// d <- Mutex::new(Inner { a: 20, b: 30 }),
/// })
/// }
/// }
diff --git a/rust/kernel/sync/lock/spinlock.rs b/rust/kernel/sync/lock/spinlock.rs
index 091167ceda66..f8bec26f1cee 100644
--- a/rust/kernel/sync/lock/spinlock.rs
+++ b/rust/kernel/sync/lock/spinlock.rs
@@ -22,8 +22,8 @@ macro_rules! new_spinlock {
/// one at a time is allowed to progress, the others will block (spinning) until the spinlock is
/// unlocked, at which point another CPU will be allowed to make progress.
///
-/// Instances of [`SpinLock`] need a lock class and to be pinned. The recommended way to create such
-/// instances is with the [`pin_init`](pin_init::pin_init) and [`new_spinlock`] macros.
+/// Instances of [`SpinLock`] need to be pinned. You can create such instances with the
+/// [`pin_init`](pin_init::pin_init).
///
/// # Examples
///
@@ -31,7 +31,7 @@ macro_rules! new_spinlock {
/// contains an inner struct (`Inner`) that is protected by a spinlock.
///
/// ```
-/// use kernel::sync::{new_spinlock, SpinLock};
+/// use kernel::sync::SpinLock;
///
/// struct Inner {
/// a: u32,
@@ -49,7 +49,7 @@ macro_rules! new_spinlock {
/// fn new() -> impl PinInit<Self> {
/// pin_init!(Self {
/// c: 10,
-/// d <- new_spinlock!(Inner { a: 20, b: 30 }),
+/// d <- SpinLock::new(Inner { a: 20, b: 30 }),
/// })
/// }
/// }
--
2.54.0
^ permalink raw reply related [flat|nested] 9+ messages in thread* Re: [PATCH RFC 0/2] rust: sync: create lock class using `#[track_caller]`
2026-07-03 13:47 [PATCH RFC 0/2] rust: sync: create lock class using `#[track_caller]` Gary Guo
2026-07-03 13:47 ` [PATCH RFC 1/2] rust: sync: introduce a way to create lock class from caller Gary Guo
2026-07-03 13:47 ` [PATCH RFC 2/2] lockdep: delegate Rust lock class printing to Rust code Gary Guo
@ 2026-07-03 14:01 ` Peter Zijlstra
2026-07-03 14:24 ` Gary Guo
2 siblings, 1 reply; 9+ messages in thread
From: Peter Zijlstra @ 2026-07-03 14:01 UTC (permalink / raw)
To: Gary Guo
Cc: Boqun Feng, Alice Ryhl, Lyude Paul, Daniel Almeida,
Onur Özkan, Miguel Ojeda, Björn Roy Baron, Benno Lossin,
Andreas Hindborg, Trevor Gross, Danilo Krummrich,
Tamir Duberstein, Alexandre Courbot, Ingo Molnar, Will Deacon,
Waiman Long, linux-kernel, rust-for-linux
On Fri, Jul 03, 2026 at 02:47:25PM +0100, Gary Guo wrote:
> Currently we're adding more and more macros so that lock classes can be
> created. The only purpose served by these macros are to create names and
> static lock classes so they can be tracked by lockdep.
>
> I've come up with an approach that uses `Location::caller` and Rust's
> `#[track_caller]` feature for this purpose instead. `Location::caller()`
> will return a `&'static Location<'static>` that lives in `.rodata`. As
> only addresses matter for static objects, the address of the `Location`
> themselves could be used for lock classes. The `Location` is of 2 * usize +
> 8 bytes, so they could host 2 lock class keys, so `DelayedWork` which needs
> two lock classes can also use this mechanism.
>
> The `Location::caller` could also be used to provide the name; currently,
> `optional_name` just uses `file_name:line_number` as the name, and this
> information is available in `Location`. However, `Location` encodes them
> using file name as a C string plus two `u32`'s for line and column number,
> so it cannot provide a single name. I came up with an alternative method,
> which is to use a special string, which lockdep can recognize and delegate
> back to Rust to print it.
Perhaps add some sample output so us simple folk that still think rust
looks like line noise can have an idea of what it'll look like. Because
I just cannot make much sense of the above.
^ permalink raw reply [flat|nested] 9+ messages in thread
* Re: [PATCH RFC 0/2] rust: sync: create lock class using `#[track_caller]`
2026-07-03 14:01 ` [PATCH RFC 0/2] rust: sync: create lock class using `#[track_caller]` Peter Zijlstra
@ 2026-07-03 14:24 ` Gary Guo
2026-07-03 22:32 ` Peter Zijlstra
0 siblings, 1 reply; 9+ messages in thread
From: Gary Guo @ 2026-07-03 14:24 UTC (permalink / raw)
To: Peter Zijlstra, Gary Guo
Cc: Boqun Feng, Alice Ryhl, Lyude Paul, Daniel Almeida,
Onur Özkan, Miguel Ojeda, Björn Roy Baron, Benno Lossin,
Andreas Hindborg, Trevor Gross, Danilo Krummrich,
Tamir Duberstein, Alexandre Courbot, Ingo Molnar, Will Deacon,
Waiman Long, linux-kernel, rust-for-linux
On Fri Jul 3, 2026 at 3:01 PM BST, Peter Zijlstra wrote:
> On Fri, Jul 03, 2026 at 02:47:25PM +0100, Gary Guo wrote:
>> Currently we're adding more and more macros so that lock classes can be
>> created. The only purpose served by these macros are to create names and
>> static lock classes so they can be tracked by lockdep.
>>
>> I've come up with an approach that uses `Location::caller` and Rust's
>> `#[track_caller]` feature for this purpose instead. `Location::caller()`
>> will return a `&'static Location<'static>` that lives in `.rodata`. As
>> only addresses matter for static objects, the address of the `Location`
>> themselves could be used for lock classes. The `Location` is of 2 * usize +
>> 8 bytes, so they could host 2 lock class keys, so `DelayedWork` which needs
>> two lock classes can also use this mechanism.
>>
>> The `Location::caller` could also be used to provide the name; currently,
>> `optional_name` just uses `file_name:line_number` as the name, and this
>> information is available in `Location`. However, `Location` encodes them
>> using file name as a C string plus two `u32`'s for line and column number,
>> so it cannot provide a single name. I came up with an alternative method,
>> which is to use a special string, which lockdep can recognize and delegate
>> back to Rust to print it.
>
> Perhaps add some sample output so us simple folk that still think rust
> looks like line noise can have an idea of what it'll look like. Because
> I just cannot make much sense of the above.
The above is mostly about the Rust implementation detail. On the lockdep side,
essentially the name will be "(rust)", while the actual name can be retrieved
from the lock class key.
So for
let foo = KBox::pin_init(Mutex::new(42));
in, say, foo.c:123
it will eventually call
struct rust_location *loc = /* static generated by the Rust compiler */;
mutex_init_lockdep(foo, "(rust)", (struct lock_class_key *)loc)
And when "(rust)" is encountered as lock class name, instead of printing the
string as is, lockdep will call
lockdep_print_rust_name(loc)
which will be doing essentially
pr_cont("foo.c:123");
Best,
Gary
^ permalink raw reply [flat|nested] 9+ messages in thread* Re: [PATCH RFC 0/2] rust: sync: create lock class using `#[track_caller]`
2026-07-03 14:24 ` Gary Guo
@ 2026-07-03 22:32 ` Peter Zijlstra
2026-07-04 16:52 ` Gary Guo
0 siblings, 1 reply; 9+ messages in thread
From: Peter Zijlstra @ 2026-07-03 22:32 UTC (permalink / raw)
To: Gary Guo
Cc: Boqun Feng, Alice Ryhl, Lyude Paul, Daniel Almeida,
Onur Özkan, Miguel Ojeda, Björn Roy Baron, Benno Lossin,
Andreas Hindborg, Trevor Gross, Danilo Krummrich,
Tamir Duberstein, Alexandre Courbot, Ingo Molnar, Will Deacon,
Waiman Long, linux-kernel, rust-for-linux
On Fri, Jul 03, 2026 at 03:24:29PM +0100, Gary Guo wrote:
> On Fri Jul 3, 2026 at 3:01 PM BST, Peter Zijlstra wrote:
> > On Fri, Jul 03, 2026 at 02:47:25PM +0100, Gary Guo wrote:
> >> Currently we're adding more and more macros so that lock classes can be
> >> created. The only purpose served by these macros are to create names and
> >> static lock classes so they can be tracked by lockdep.
> >>
> >> I've come up with an approach that uses `Location::caller` and Rust's
> >> `#[track_caller]` feature for this purpose instead. `Location::caller()`
> >> will return a `&'static Location<'static>` that lives in `.rodata`. As
> >> only addresses matter for static objects, the address of the `Location`
> >> themselves could be used for lock classes. The `Location` is of 2 * usize +
> >> 8 bytes, so they could host 2 lock class keys, so `DelayedWork` which needs
> >> two lock classes can also use this mechanism.
> >>
> >> The `Location::caller` could also be used to provide the name; currently,
> >> `optional_name` just uses `file_name:line_number` as the name, and this
> >> information is available in `Location`. However, `Location` encodes them
> >> using file name as a C string plus two `u32`'s for line and column number,
> >> so it cannot provide a single name. I came up with an alternative method,
> >> which is to use a special string, which lockdep can recognize and delegate
> >> back to Rust to print it.
> >
> > Perhaps add some sample output so us simple folk that still think rust
> > looks like line noise can have an idea of what it'll look like. Because
> > I just cannot make much sense of the above.
>
> The above is mostly about the Rust implementation detail. On the lockdep side,
> essentially the name will be "(rust)", while the actual name can be retrieved
> from the lock class key.
>
> So for
>
> let foo = KBox::pin_init(Mutex::new(42));
>
> in, say, foo.c:123
>
> it will eventually call
>
> struct rust_location *loc = /* static generated by the Rust compiler */;
> mutex_init_lockdep(foo, "(rust)", (struct lock_class_key *)loc)
>
> And when "(rust)" is encountered as lock class name, instead of printing the
> string as is, lockdep will call
>
> lockdep_print_rust_name(loc)
>
> which will be doing essentially
>
> pr_cont("foo.c:123");
That seems quite terrible. The C names are based on the expression used
to initialize the class and are thus somewhat descriptive. But file:line
combos are horrid identifiers for locks.
Why would you want to do this?
^ permalink raw reply [flat|nested] 9+ messages in thread* Re: [PATCH RFC 0/2] rust: sync: create lock class using `#[track_caller]`
2026-07-03 22:32 ` Peter Zijlstra
@ 2026-07-04 16:52 ` Gary Guo
2026-07-04 17:10 ` Peter Zijlstra
0 siblings, 1 reply; 9+ messages in thread
From: Gary Guo @ 2026-07-04 16:52 UTC (permalink / raw)
To: Peter Zijlstra, Gary Guo
Cc: Boqun Feng, Alice Ryhl, Lyude Paul, Daniel Almeida,
Onur Özkan, Miguel Ojeda, Björn Roy Baron, Benno Lossin,
Andreas Hindborg, Trevor Gross, Danilo Krummrich,
Tamir Duberstein, Alexandre Courbot, Ingo Molnar, Will Deacon,
Waiman Long, linux-kernel, rust-for-linux
On Fri Jul 3, 2026 at 11:32 PM BST, Peter Zijlstra wrote:
> On Fri, Jul 03, 2026 at 03:24:29PM +0100, Gary Guo wrote:
>> [snip] On the lockdep side,
>> essentially the name will be "(rust)", while the actual name can be retrieved
>> from the lock class key.
>>
>> So for
>>
>> let foo = KBox::pin_init(Mutex::new(42));
>>
>> in, say, foo.c:123
>>
>> it will eventually call
>>
>> struct rust_location *loc = /* static generated by the Rust compiler */;
>> mutex_init_lockdep(foo, "(rust)", (struct lock_class_key *)loc)
>>
>> And when "(rust)" is encountered as lock class name, instead of printing the
>> string as is, lockdep will call
>>
>> lockdep_print_rust_name(loc)
>>
>> which will be doing essentially
>>
>> pr_cont("foo.c:123");
>
> That seems quite terrible. The C names are based on the expression used
> to initialize the class and are thus somewhat descriptive. But file:line
> combos are horrid identifiers for locks.
>
> Why would you want to do this?
I do think this is not ideal, however the current code is already doing this.
This RFC series isn't changing the name at all, just represent this in a
different way.
The reason that file:line is used for names is due to the fundamental difference
between initialization in C and Rust. In C you create something uninitialized
first, and then initialize it, and it'll be UB if the value is used before
initialization or (for some types) initialize a value twice. In Rust we use
pin_init to ensure that a value cannot be used uninitialized.
The way the syntax is write out currently is something like
pin_init!(MyStruct {
my_mutex <- new_mutex!(initial_data),
})
[
which this series is turning it to
pin_init!(MyStruct {
my_mutex <- Mutex::new(initial_data),
})
]
as you can see the `new_mutex!` or `Mutx::new` does not know where it is going
to be initialized to, because we prevent people from being able to name a
yet-to-be-initialized place.
It is possible to use the C approach (which early days of Rust-for-Linux does
use), but doing so require a lot of unsafe keywords because you are relying on
the programmer to not mess things up, instead of having the compiler check.
So the best alternative that we can use for the name under this constraint is
the file:line combo. It is less descriptive, but at least it does tell you where
the lock class is used, so you can still trace it back. Given lockdep is a
debugging feature, I think the less descriptive name, albeit inconvenient, is
not a dealbreaker.
The trade off here is the convenience of creating a lock (safely) vs the
descriptiveness of the name, and I think the former is more important. It is
still possible to explicit give a name if it helps (Binder is actually doing
this already), but this currently cannot be implicitly created.
Best,
Gary
^ permalink raw reply [flat|nested] 9+ messages in thread* Re: [PATCH RFC 0/2] rust: sync: create lock class using `#[track_caller]`
2026-07-04 16:52 ` Gary Guo
@ 2026-07-04 17:10 ` Peter Zijlstra
2026-07-04 17:46 ` Gary Guo
0 siblings, 1 reply; 9+ messages in thread
From: Peter Zijlstra @ 2026-07-04 17:10 UTC (permalink / raw)
To: Gary Guo
Cc: Boqun Feng, Alice Ryhl, Lyude Paul, Daniel Almeida,
Onur Özkan, Miguel Ojeda, Björn Roy Baron, Benno Lossin,
Andreas Hindborg, Trevor Gross, Danilo Krummrich,
Tamir Duberstein, Alexandre Courbot, Ingo Molnar, Will Deacon,
Waiman Long, linux-kernel, rust-for-linux
On Sat, Jul 04, 2026 at 05:52:35PM +0100, Gary Guo wrote:
> On Fri Jul 3, 2026 at 11:32 PM BST, Peter Zijlstra wrote:
> > On Fri, Jul 03, 2026 at 03:24:29PM +0100, Gary Guo wrote:
> >> [snip] On the lockdep side,
> >> essentially the name will be "(rust)", while the actual name can be retrieved
> >> from the lock class key.
> >>
> >> So for
> >>
> >> let foo = KBox::pin_init(Mutex::new(42));
> >>
> >> in, say, foo.c:123
> >>
> >> it will eventually call
> >>
> >> struct rust_location *loc = /* static generated by the Rust compiler */;
> >> mutex_init_lockdep(foo, "(rust)", (struct lock_class_key *)loc)
> >>
> >> And when "(rust)" is encountered as lock class name, instead of printing the
> >> string as is, lockdep will call
> >>
> >> lockdep_print_rust_name(loc)
> >>
> >> which will be doing essentially
> >>
> >> pr_cont("foo.c:123");
> >
> > That seems quite terrible. The C names are based on the expression used
> > to initialize the class and are thus somewhat descriptive. But file:line
> > combos are horrid identifiers for locks.
> >
> > Why would you want to do this?
>
> I do think this is not ideal, however the current code is already doing this.
> This RFC series isn't changing the name at all, just represent this in a
> different way.
>
> The reason that file:line is used for names is due to the fundamental difference
> between initialization in C and Rust. In C you create something uninitialized
> first, and then initialize it, and it'll be UB if the value is used before
> initialization or (for some types) initialize a value twice. In Rust we use
> pin_init to ensure that a value cannot be used uninitialized.
>
> The way the syntax is write out currently is something like
>
> pin_init!(MyStruct {
> my_mutex <- new_mutex!(initial_data),
> })
>
> [
> which this series is turning it to
>
> pin_init!(MyStruct {
> my_mutex <- Mutex::new(initial_data),
> })
> ]
>
> as you can see the `new_mutex!` or `Mutx::new` does not know where it is going
To be frank, no I don't see. I have absolutely no frigging clue what any
of that means :-(
> to be initialized to, because we prevent people from being able to name a
> yet-to-be-initialized place.
>
> It is possible to use the C approach (which early days of Rust-for-Linux does
> use), but doing so require a lot of unsafe keywords because you are relying on
> the programmer to not mess things up, instead of having the compiler check.
>
> So the best alternative that we can use for the name under this constraint is
> the file:line combo. It is less descriptive, but at least it does tell you where
> the lock class is used, so you can still trace it back. Given lockdep is a
> debugging feature, I think the less descriptive name, albeit inconvenient, is
> not a dealbreaker.
People already struggle to make sense of lockdep reports, this isn't
going to make it better :/
> The trade off here is the convenience of creating a lock (safely) vs the
> descriptiveness of the name, and I think the former is more important. It is
> still possible to explicit give a name if it helps (Binder is actually doing
> this already), but this currently cannot be implicitly created.
Like I said, Rust is still line noise to me, and I really don't
understand anything you've just tried to tell me.
Now, I do speak (some) C++, and the new thing you mention got me
thinking you're talking about a constructor. Are you trying to say that
since the invocation of the constructor is somewhat implicit, there is
no good way to stringize the structure member name and store it?
That said, in C++ you can mandate a string be handed to the constructor;
this would mean everybody would need to explicitly provide a descriptive
name, surely Rust can do this same? Every !debug build could then
instantly ignore that string, but at least it'd be there.
^ permalink raw reply [flat|nested] 9+ messages in thread* Re: [PATCH RFC 0/2] rust: sync: create lock class using `#[track_caller]`
2026-07-04 17:10 ` Peter Zijlstra
@ 2026-07-04 17:46 ` Gary Guo
0 siblings, 0 replies; 9+ messages in thread
From: Gary Guo @ 2026-07-04 17:46 UTC (permalink / raw)
To: Peter Zijlstra, Gary Guo
Cc: Boqun Feng, Alice Ryhl, Lyude Paul, Daniel Almeida,
Onur Özkan, Miguel Ojeda, Björn Roy Baron, Benno Lossin,
Andreas Hindborg, Trevor Gross, Danilo Krummrich,
Tamir Duberstein, Alexandre Courbot, Ingo Molnar, Will Deacon,
Waiman Long, linux-kernel, rust-for-linux
On Sat Jul 4, 2026 at 6:10 PM BST, Peter Zijlstra wrote:
> On Sat, Jul 04, 2026 at 05:52:35PM +0100, Gary Guo wrote:
>> On Fri Jul 3, 2026 at 11:32 PM BST, Peter Zijlstra wrote:
>> > On Fri, Jul 03, 2026 at 03:24:29PM +0100, Gary Guo wrote:
>> >> [snip] On the lockdep side,
>> >> essentially the name will be "(rust)", while the actual name can be retrieved
>> >> from the lock class key.
>> >>
>> >> So for
>> >>
>> >> let foo = KBox::pin_init(Mutex::new(42));
>> >>
>> >> in, say, foo.c:123
>> >>
>> >> it will eventually call
>> >>
>> >> struct rust_location *loc = /* static generated by the Rust compiler */;
>> >> mutex_init_lockdep(foo, "(rust)", (struct lock_class_key *)loc)
>> >>
>> >> And when "(rust)" is encountered as lock class name, instead of printing the
>> >> string as is, lockdep will call
>> >>
>> >> lockdep_print_rust_name(loc)
>> >>
>> >> which will be doing essentially
>> >>
>> >> pr_cont("foo.c:123");
>> >
>> > That seems quite terrible. The C names are based on the expression used
>> > to initialize the class and are thus somewhat descriptive. But file:line
>> > combos are horrid identifiers for locks.
>> >
>> > Why would you want to do this?
>>
>> I do think this is not ideal, however the current code is already doing this.
>> This RFC series isn't changing the name at all, just represent this in a
>> different way.
>>
>> The reason that file:line is used for names is due to the fundamental difference
>> between initialization in C and Rust. In C you create something uninitialized
>> first, and then initialize it, and it'll be UB if the value is used before
>> initialization or (for some types) initialize a value twice. In Rust we use
>> pin_init to ensure that a value cannot be used uninitialized.
>>
>> The way the syntax is write out currently is something like
>>
>> pin_init!(MyStruct {
>> my_mutex <- new_mutex!(initial_data),
>> })
>>
>> [
>> which this series is turning it to
>>
>> pin_init!(MyStruct {
>> my_mutex <- Mutex::new(initial_data),
>> })
>> ]
>>
>> as you can see the `new_mutex!` or `Mutx::new` does not know where it is going
>
> To be frank, no I don't see. I have absolutely no frigging clue what any
> of that means :-(
>
>> to be initialized to, because we prevent people from being able to name a
>> yet-to-be-initialized place.
>>
>> It is possible to use the C approach (which early days of Rust-for-Linux does
>> use), but doing so require a lot of unsafe keywords because you are relying on
>> the programmer to not mess things up, instead of having the compiler check.
>>
>> So the best alternative that we can use for the name under this constraint is
>> the file:line combo. It is less descriptive, but at least it does tell you where
>> the lock class is used, so you can still trace it back. Given lockdep is a
>> debugging feature, I think the less descriptive name, albeit inconvenient, is
>> not a dealbreaker.
>
> People already struggle to make sense of lockdep reports, this isn't
> going to make it better :/
>
>> The trade off here is the convenience of creating a lock (safely) vs the
>> descriptiveness of the name, and I think the former is more important. It is
>> still possible to explicit give a name if it helps (Binder is actually doing
>> this already), but this currently cannot be implicitly created.
>
> Like I said, Rust is still line noise to me, and I really don't
> understand anything you've just tried to tell me.
>
> Now, I do speak (some) C++, and the new thing you mention got me
> thinking you're talking about a constructor. Are you trying to say that
> since the invocation of the constructor is somewhat implicit, there is
> no good way to stringize the structure member name and store it?
It's indeed somewhat similar to a C++ constructor (the actual mechanism is
different, but conceptually they're similar). You do get explicit invocation
(there's no implicit invocation of constructors like C++, you can see this as
similar to C++'s constructor with "explicit" keyword).
But the gist here is correct, there's no good way to stringify the structure
member name.
> That said, in C++ you can mandate a string be handed to the constructor;
> this would mean everybody would need to explicitly provide a descriptive
> name, surely Rust can do this same? Every !debug build could then
> instantly ignore that string, but at least it'd be there.
It's certainly possible to give explicit names (this is what patch 1/2's
"new_with_name" function is for). To mandate names being provided we just need
to remove the constructor function without the name. That said, I am not sure
that we want everyone to explicitly give names. Especially for types like
delayed_work where it needs two lock classes and names, it's somewhat
leaky abstraction if the constructor asks to user to provide a name for its
work completion and a name for its timer (as it's not possible to concat strings
without allocation without using macros).
Best,
Gary
^ permalink raw reply [flat|nested] 9+ messages in thread