Skip to content

Commit 322ca11

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

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
@@ -3253,6 +3253,14 @@ config RUST_KERNEL_DOCTESTS
32533253

32543254
If unsure, say N.
32553255

3256+
config RUST_EXTRA_LOCKDEP
3257+
bool "Extra lockdep checking"
3258+
depends on RUST && PROVE_LOCKING
3259+
help
3260+
Enabled additional lockdep integration with certain Rust types.
3261+
3262+
If unsure, say N.
3263+
32563264
endmenu # "Rust"
32573265

32583266
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`.
@@ -140,6 +143,17 @@ pub struct Arc<T: ?Sized> {
140143
_p: PhantomData<ArcInner<T>>,
141144
}
142145

146+
#[cfg(CONFIG_RUST_EXTRA_LOCKDEP)]
147+
#[pin_data]
148+
#[repr(C)]
149+
struct ArcInner<T: ?Sized> {
150+
refcount: Opaque<bindings::refcount_t>,
151+
lockdep_map: LockdepMap,
152+
data: T,
153+
}
154+
155+
// FIXME: pin_data does not work well with cfg attributes within the struct definition.
156+
#[cfg(not(CONFIG_RUST_EXTRA_LOCKDEP))]
143157
#[pin_data]
144158
#[repr(C)]
145159
struct ArcInner<T: ?Sized> {
@@ -204,11 +218,14 @@ unsafe impl<T: ?Sized + Sync + Send> Sync for Arc<T> {}
204218

205219
impl<T> Arc<T> {
206220
/// Constructs a new reference counted instance of `T`.
221+
#[track_caller]
207222
pub fn new(contents: T, flags: Flags) -> Result<Self, AllocError> {
208223
// INVARIANT: The refcount is initialised to a non-zero value.
209224
let value = ArcInner {
210225
// SAFETY: There are no safety requirements for this FFI call.
211226
refcount: Opaque::new(unsafe { bindings::REFCOUNT_INIT(1) }),
227+
#[cfg(CONFIG_RUST_EXTRA_LOCKDEP)]
228+
lockdep_map: LockdepMap::new(),
212229
data: contents,
213230
};
214231

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

438+
#[cfg(CONFIG_RUST_EXTRA_LOCKDEP)]
439+
// SAFETY: By the type invariant, there is necessarily a reference to the object.
440+
// We cannot hold the map lock across the reference decrement, as we might race
441+
// another thread. Therefore, we lock and immediately drop the guard here. This
442+
// only serves to inform lockdep of the dependency up the call stack.
443+
unsafe { self.ptr.as_ref() }.lockdep_map.lock();
444+
421445
// INVARIANT: If the refcount reaches zero, there are no other instances of `Arc`, and
422446
// this instance is being dropped, so the broken invariant is not observable.
423447
// SAFETY: Also by the type invariant, we are allowed to decrement the refcount.
424448
let is_zero = unsafe { bindings::refcount_dec_and_test(refcount) };
449+
425450
if is_zero {
426451
// The count reached zero, we must free the memory.
427-
//
428-
// SAFETY: The pointer was initialised from the result of `KBox::leak`.
429-
unsafe { drop(KBox::from_raw(self.ptr.as_ptr())) };
452+
453+
#[cfg(CONFIG_RUST_EXTRA_LOCKDEP)]
454+
// SAFETY: If we get this far, we had the last reference to the object.
455+
// That means we are responsible for freeing it, so we can safely lock
456+
// the fake lock again. This wraps dropping the inner object, which
457+
// informs lockdep of the dependencies down the call stack.
458+
let guard = unsafe { self.ptr.as_ref() }.lockdep_map.lock();
459+
460+
// SAFETY: The pointer was initialised from the result of `Box::leak`,
461+
// and the value is valid.
462+
unsafe { core::ptr::drop_in_place(&mut self.ptr.as_mut().data) };
463+
464+
// We need to drop the lock guard before freeing the LockdepMap itself
465+
#[cfg(CONFIG_RUST_EXTRA_LOCKDEP)]
466+
core::mem::drop(guard);
467+
468+
#[cfg(CONFIG_RUST_EXTRA_LOCKDEP)]
469+
// SAFETY: The pointer was initialised from the result of `Box::leak`,
470+
// and the lockdep map is valid.
471+
unsafe {
472+
core::ptr::drop_in_place(&mut self.ptr.as_mut().lockdep_map)
473+
};
474+
475+
// SAFETY: The pointer was initialised from the result of `Box::leak`, and
476+
// a ManuallyDrop<T> is compatible. We already dropped the contents above.
477+
unsafe {
478+
drop(KBox::from_raw(
479+
self.ptr.as_ptr() as *mut ManuallyDrop<ArcInner<T>>
480+
))
481+
};
430482
}
431483
}
432484
}
@@ -661,6 +713,7 @@ pub struct UniqueArc<T: ?Sized> {
661713

662714
impl<T> UniqueArc<T> {
663715
/// Tries to allocate a new [`UniqueArc`] instance.
716+
#[track_caller]
664717
pub fn new(value: T, flags: Flags) -> Result<Self, AllocError> {
665718
Ok(Self {
666719
// INVARIANT: The newly-created object has a refcount of 1.
@@ -669,8 +722,24 @@ impl<T> UniqueArc<T> {
669722
}
670723

671724
/// Tries to allocate a new [`UniqueArc`] instance whose contents are not initialised yet.
725+
#[track_caller]
672726
pub fn new_uninit(flags: Flags) -> Result<UniqueArc<MaybeUninit<T>>, AllocError> {
673727
// INVARIANT: The refcount is initialised to a non-zero value.
728+
#[cfg(CONFIG_RUST_EXTRA_LOCKDEP)]
729+
let inner = {
730+
let map = LockdepMap::new();
731+
KBox::try_init::<AllocError>(
732+
try_init!(ArcInner {
733+
// SAFETY: There are no safety requirements for this FFI call.
734+
refcount: Opaque::new(unsafe { bindings::REFCOUNT_INIT(1) }),
735+
lockdep_map: map,
736+
data <- init::uninit::<T, AllocError>(),
737+
}? AllocError),
738+
flags,
739+
)?
740+
};
741+
// FIXME: try_init!() does not work with cfg attributes.
742+
#[cfg(not(CONFIG_RUST_EXTRA_LOCKDEP))]
674743
let inner = KBox::try_init::<AllocError>(
675744
try_init!(ArcInner {
676745
// SAFETY: There are no safety requirements for this FFI call.

0 commit comments

Comments
 (0)