Skip to content

Commit 37d3679

Browse files
hoshinolinajannau
authored andcommitted
rust: sync: arc: Add lockdep integration
Signed-off-by: Asahi Lina <lina@asahilina.net>
1 parent d735fd0 commit 37d3679

2 files changed

Lines changed: 80 additions & 3 deletions

File tree

lib/Kconfig.debug

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3072,6 +3072,14 @@ config RUST_KERNEL_DOCTESTS
30723072

30733073
If unsure, say N.
30743074

3075+
config RUST_EXTRA_LOCKDEP
3076+
bool "Extra lockdep checking"
3077+
depends on RUST && PROVE_LOCKING
3078+
help
3079+
Enabled additional lockdep integration with certain Rust types.
3080+
3081+
If unsure, say N.
3082+
30753083
endmenu # "Rust"
30763084

30773085
endmenu # Kernel hacking

rust/kernel/sync/arc.rs

Lines changed: 72 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -34,6 +34,9 @@ use core::{
3434
};
3535
use macros::pin_data;
3636

37+
#[cfg(CONFIG_RUST_EXTRA_LOCKDEP)]
38+
use crate::sync::lockdep::LockdepMap;
39+
3740
mod std_vendor;
3841

3942
/// A reference-counted pointer to an instance of `T`.
@@ -130,6 +133,17 @@ pub struct Arc<T: ?Sized> {
130133
_p: PhantomData<ArcInner<T>>,
131134
}
132135

136+
#[cfg(CONFIG_RUST_EXTRA_LOCKDEP)]
137+
#[pin_data]
138+
#[repr(C)]
139+
struct ArcInner<T: ?Sized> {
140+
refcount: Opaque<bindings::refcount_t>,
141+
lockdep_map: LockdepMap,
142+
data: T,
143+
}
144+
145+
// FIXME: pin_data does not work well with cfg attributes within the struct definition.
146+
#[cfg(not(CONFIG_RUST_EXTRA_LOCKDEP))]
133147
#[pin_data]
134148
#[repr(C)]
135149
struct ArcInner<T: ?Sized> {
@@ -192,11 +206,14 @@ unsafe impl<T: ?Sized + Sync + Send> Sync for Arc<T> {}
192206

193207
impl<T> Arc<T> {
194208
/// Constructs a new reference counted instance of `T`.
209+
#[track_caller]
195210
pub fn new(contents: T, flags: Flags) -> Result<Self, AllocError> {
196211
// INVARIANT: The refcount is initialised to a non-zero value.
197212
let value = ArcInner {
198213
// SAFETY: There are no safety requirements for this FFI call.
199214
refcount: Opaque::new(unsafe { bindings::REFCOUNT_INIT(1) }),
215+
#[cfg(CONFIG_RUST_EXTRA_LOCKDEP)]
216+
lockdep_map: LockdepMap::new(),
200217
data: contents,
201218
};
202219

@@ -390,15 +407,50 @@ impl<T: ?Sized> Drop for Arc<T> {
390407
// freed/invalid memory as long as it is never dereferenced.
391408
let refcount = unsafe { self.ptr.as_ref() }.refcount.get();
392409

410+
#[cfg(CONFIG_RUST_EXTRA_LOCKDEP)]
411+
// SAFETY: By the type invariant, there is necessarily a reference to the object.
412+
// We cannot hold the map lock across the reference decrement, as we might race
413+
// another thread. Therefore, we lock and immediately drop the guard here. This
414+
// only serves to inform lockdep of the dependency up the call stack.
415+
unsafe { self.ptr.as_ref() }.lockdep_map.lock();
416+
393417
// INVARIANT: If the refcount reaches zero, there are no other instances of `Arc`, and
394418
// this instance is being dropped, so the broken invariant is not observable.
395419
// SAFETY: Also by the type invariant, we are allowed to decrement the refcount.
396420
let is_zero = unsafe { bindings::refcount_dec_and_test(refcount) };
421+
397422
if is_zero {
398423
// The count reached zero, we must free the memory.
399-
//
400-
// SAFETY: The pointer was initialised from the result of `KBox::leak`.
401-
unsafe { drop(KBox::from_raw(self.ptr.as_ptr())) };
424+
425+
#[cfg(CONFIG_RUST_EXTRA_LOCKDEP)]
426+
// SAFETY: If we get this far, we had the last reference to the object.
427+
// That means we are responsible for freeing it, so we can safely lock
428+
// the fake lock again. This wraps dropping the inner object, which
429+
// informs lockdep of the dependencies down the call stack.
430+
let guard = unsafe { self.ptr.as_ref() }.lockdep_map.lock();
431+
432+
// SAFETY: The pointer was initialised from the result of `Box::leak`,
433+
// and the value is valid.
434+
unsafe { core::ptr::drop_in_place(&mut self.ptr.as_mut().data) };
435+
436+
// We need to drop the lock guard before freeing the LockdepMap itself
437+
#[cfg(CONFIG_RUST_EXTRA_LOCKDEP)]
438+
core::mem::drop(guard);
439+
440+
#[cfg(CONFIG_RUST_EXTRA_LOCKDEP)]
441+
// SAFETY: The pointer was initialised from the result of `Box::leak`,
442+
// and the lockdep map is valid.
443+
unsafe {
444+
core::ptr::drop_in_place(&mut self.ptr.as_mut().lockdep_map)
445+
};
446+
447+
// SAFETY: The pointer was initialised from the result of `Box::leak`, and
448+
// a ManuallyDrop<T> is compatible. We already dropped the contents above.
449+
unsafe {
450+
drop(KBox::from_raw(
451+
self.ptr.as_ptr() as *mut ManuallyDrop<ArcInner<T>>
452+
))
453+
};
402454
}
403455
}
404456
}
@@ -630,6 +682,7 @@ pub struct UniqueArc<T: ?Sized> {
630682

631683
impl<T> UniqueArc<T> {
632684
/// Tries to allocate a new [`UniqueArc`] instance.
685+
#[track_caller]
633686
pub fn new(value: T, flags: Flags) -> Result<Self, AllocError> {
634687
Ok(Self {
635688
// INVARIANT: The newly-created object has a refcount of 1.
@@ -638,8 +691,24 @@ impl<T> UniqueArc<T> {
638691
}
639692

640693
/// Tries to allocate a new [`UniqueArc`] instance whose contents are not initialised yet.
694+
#[track_caller]
641695
pub fn new_uninit(flags: Flags) -> Result<UniqueArc<MaybeUninit<T>>, AllocError> {
642696
// INVARIANT: The refcount is initialised to a non-zero value.
697+
#[cfg(CONFIG_RUST_EXTRA_LOCKDEP)]
698+
let inner = {
699+
let map = LockdepMap::new();
700+
KBox::try_init::<AllocError>(
701+
try_init!(ArcInner {
702+
// SAFETY: There are no safety requirements for this FFI call.
703+
refcount: Opaque::new(unsafe { bindings::REFCOUNT_INIT(1) }),
704+
lockdep_map: map,
705+
data <- init::uninit::<T, AllocError>(),
706+
}? AllocError),
707+
flags,
708+
)?
709+
};
710+
// FIXME: try_init!() does not work with cfg attributes.
711+
#[cfg(not(CONFIG_RUST_EXTRA_LOCKDEP))]
643712
let inner = KBox::try_init::<AllocError>(
644713
try_init!(ArcInner {
645714
// SAFETY: There are no safety requirements for this FFI call.

0 commit comments

Comments
 (0)