diff --git a/.gitignore b/.gitignore index cd171497905a9d..afbe5dbcad7b55 100644 --- a/.gitignore +++ b/.gitignore @@ -25,6 +25,7 @@ *.gcno *.gz *.i +*.klint *.ko *.lex.c *.ll diff --git a/rust/Makefile b/rust/Makefile index 8f598a904f3820..419e4c508f038c 100644 --- a/rust/Makefile +++ b/rust/Makefile @@ -382,6 +382,7 @@ quiet_cmd_rustc_library = $(if $(skip_clippy),RUSTC,$(RUSTC_OR_CLIPPY_QUIET)) L $(if $(skip_clippy),$(RUSTC),$(RUSTC_OR_CLIPPY)) \ $(filter-out $(skip_flags),$(rust_flags) $(rustc_target_flags)) \ --emit=dep-info,obj,metadata --crate-type rlib \ + -Zcrate-attr='feature(register_tool)' -Zcrate-attr='register_tool(klint)' \ --out-dir $(objtree)/$(obj) -L$(objtree)/$(obj) \ --crate-name $(patsubst %.o,%,$(notdir $@)) $<; \ mv $(objtree)/$(obj)/$(patsubst %.o,%,$(notdir $@)).d $(depfile); \ diff --git a/rust/kernel/device.rs b/rust/kernel/device.rs index 9b00b239aefdff..df70aa49ac2601 100644 --- a/rust/kernel/device.rs +++ b/rust/kernel/device.rs @@ -11,7 +11,7 @@ use crate::{ bindings, revocable::{Revocable, RevocableGuard}, str::CStr, - sync::{LockClassKey, NeedsLockClass, RevocableMutex, RevocableMutexGuard, UniqueArc}, + sync::{rcu, LockClassKey, NeedsLockClass, RevocableMutex, RevocableMutexGuard, UniqueArc}, Result, }; use core::{ @@ -294,7 +294,7 @@ impl Data { } /// Returns the resources if they're still available. - pub fn resources(&self) -> Option> { + pub fn resources(&self) -> Result, rcu::Guard> { self.resources.try_access() } diff --git a/rust/kernel/error.rs b/rust/kernel/error.rs index 17ec6f132e936e..20e58dfd33de15 100644 --- a/rust/kernel/error.rs +++ b/rust/kernel/error.rs @@ -448,7 +448,7 @@ impl From for Error { /// Note that even if a function does not return anything when it succeeds, /// it should still be modeled as returning a `Result` rather than /// just an [`Error`]. -pub type Result = core::result::Result; +pub type Result = core::result::Result; impl From for Error { fn from(_: AllocError) -> Error { diff --git a/rust/kernel/kasync/executor.rs b/rust/kernel/kasync/executor.rs index c8dc1cb26bf5ee..785bbd6d384e83 100644 --- a/rust/kernel/kasync/executor.rs +++ b/rust/kernel/kasync/executor.rs @@ -65,9 +65,11 @@ pub trait Executor: Sync + Send { /// Types that implement this trait can get a [`Waker`] by calling [`ref_waker`]. pub trait ArcWake: Send + Sync { /// Wakes a task up. + #[klint::preempt_count(expect = 0..)] fn wake_by_ref(self: ArcBorrow<'_, Self>); /// Wakes a task up and consumes a reference. + #[klint::preempt_count(expect = 0..)] fn wake(self: Arc) { self.as_arc_borrow().wake_by_ref(); } @@ -83,18 +85,21 @@ pub fn ref_waker(w: Arc) -> Waker { ) } + #[klint::preempt_count(expect = 0..)] // Required as `Waker::clone` must not sleep. unsafe fn clone(ptr: *const ()) -> RawWaker { // SAFETY: The data stored in the raw waker is the result of a call to `into_pointer`. let w = unsafe { Arc::::borrow(ptr.cast()) }; raw_waker(w.into()) } + #[klint::preempt_count(expect = 0..)] // Required as `Waker::wake` must not sleep. unsafe fn wake(ptr: *const ()) { // SAFETY: The data stored in the raw waker is the result of a call to `into_pointer`. let w = unsafe { Arc::::from_pointer(ptr.cast()) }; w.wake(); } + #[klint::preempt_count(expect = 0..)] // Required as `Waker::wake_by_ref` must not sleep. unsafe fn wake_by_ref(ptr: *const ()) { // SAFETY: The data stored in the raw waker is the result of a call to `into_pointer`. let w = unsafe { Arc::::borrow(ptr.cast()) }; diff --git a/rust/kernel/kasync/executor/workqueue.rs b/rust/kernel/kasync/executor/workqueue.rs index dcde56115b2ebe..9c23a53130eb1c 100644 --- a/rust/kernel/kasync/executor/workqueue.rs +++ b/rust/kernel/kasync/executor/workqueue.rs @@ -143,7 +143,12 @@ impl RevocableTask for Task { } impl ArcWake for Task { + #[klint::preempt_count(expect = 0.., unchecked)] // FIXME: This is a bug. fn wake(self: Arc) { + Self::wake_by_ref(self.as_arc_borrow()); + } + + fn wake_by_ref(self: ArcBorrow<'_, Self>) { if self.future.is_revoked() { return; } @@ -152,11 +157,7 @@ impl ArcWake for Task { Left(q) => &**q, Right(q) => *q, } - .enqueue(self.clone()); - } - - fn wake_by_ref(self: ArcBorrow<'_, Self>) { - Arc::from(self).wake(); + .enqueue(self); } } diff --git a/rust/kernel/kasync/net.rs b/rust/kernel/kasync/net.rs index b4bbeffad94a1b..d463cf757ab82f 100644 --- a/rust/kernel/kasync/net.rs +++ b/rust/kernel/kasync/net.rs @@ -185,6 +185,7 @@ impl<'a, Out, F: FnMut() -> Result + Send + 'a> SocketFuture<'a, Out, F> { /// /// If the state matches the one we're waiting on, we wake up the task so that the future can be /// polled again. + #[klint::preempt_count(expect = 0..)] // This function can be called from `wake_up` which must noy sleep. unsafe extern "C" fn wake_callback( wq_entry: *mut bindings::wait_queue_entry, _mode: core::ffi::c_uint, diff --git a/rust/kernel/revocable.rs b/rust/kernel/revocable.rs index 1093c4d26026dd..86bc4a95741abc 100644 --- a/rust/kernel/revocable.rs +++ b/rust/kernel/revocable.rs @@ -94,14 +94,14 @@ impl Revocable { /// Returns a guard that gives access to the object otherwise; the object is guaranteed to /// remain accessible while the guard is alive. In such cases, callers are not allowed to sleep /// because another CPU may be waiting to complete the revocation of this object. - pub fn try_access(&self) -> Option> { + pub fn try_access(&self) -> Result, rcu::Guard> { let guard = rcu::read_lock(); if self.is_available.load(Ordering::Relaxed) { // SAFETY: Since `self.is_available` is true, data is initialised and has to remain // valid because the RCU read side lock prevents it from being dropped. - Some(unsafe { RevocableGuard::new(self.data.assume_init_ref().get(), guard) }) + Ok(unsafe { RevocableGuard::new(self.data.assume_init_ref().get(), guard) }) } else { - None + Err(guard) } } diff --git a/rust/kernel/workqueue.rs b/rust/kernel/workqueue.rs index d87dfe41562148..0c45275331aa01 100644 --- a/rust/kernel/workqueue.rs +++ b/rust/kernel/workqueue.rs @@ -7,7 +7,7 @@ use crate::{ bindings, c_str, error::code::*, - sync::{Arc, LockClassKey, UniqueArc}, + sync::{Arc, ArcBorrow, LockClassKey, UniqueArc}, Opaque, Result, }; use core::{fmt, ops::Deref, ptr::NonNull}; @@ -233,7 +233,7 @@ impl Queue { /// /// Returns `true` if the work item was successfully enqueue; returns `false` if it had already /// been (and continued to be) enqueued. - pub fn enqueue>(&self, w: Arc) -> bool { + pub fn enqueue>(&self, w: ArcBorrow<'_, T>) -> bool { self.enqueue_adapter::(w) } @@ -241,24 +241,21 @@ impl Queue { /// /// Returns `true` if the work item was successfully enqueue; returns `false` if it had already /// been (and continued to be) enqueued. - pub fn enqueue_adapter(&self, w: Arc) -> bool { - let ptr = Arc::into_raw(w); + pub fn enqueue_adapter(&self, w: ArcBorrow<'_, A::Target>) -> bool { + let ptr = &*w as *const A::Target; let field_ptr = (ptr as *const u8).wrapping_offset(A::FIELD_OFFSET) as *mut bindings::work_struct; // SAFETY: Having a shared reference to work queue guarantees that it remains valid, while - // the work item remains valid because we called `into_raw` and only call `from_raw` again - // if the object was already queued (so a previous call already guarantees it remains - // alive), when the work item runs, or when the work item is canceled. + // the work item remains valid because we will increment the reference count later if this function + // returns true. let ret = unsafe { bindings::queue_work_on(bindings::WORK_CPU_UNBOUND as _, self.0.get(), field_ptr) }; - if !ret { - // SAFETY: `ptr` comes from a previous call to `into_raw`. Additionally, given that - // `queue_work_on` returned `false`, we know that no-one is going to use the result of - // `into_raw`, so we must drop it here to avoid a reference leak. - unsafe { Arc::from_raw(ptr) }; + if ret { + // Increase the reference count because the work item has a copy. + core::mem::forget(Arc::from(w)); } ret @@ -279,7 +276,7 @@ impl Queue { func, })?; Work::init(&w, key); - self.enqueue(w.into()); + self.enqueue(Arc::from(w).as_arc_borrow()); Ok(()) } } diff --git a/scripts/Makefile.build b/scripts/Makefile.build index b7e13ff13e4f6e..cfb2d27757a7b5 100644 --- a/scripts/Makefile.build +++ b/scripts/Makefile.build @@ -279,9 +279,11 @@ rust_allowed_features := allocator_api,const_refs_to_cell rust_common_cmd = \ RUST_MODFILE=$(modfile) $(RUSTC_OR_CLIPPY) $(rust_flags) \ - -Zallow-features=$(rust_allowed_features) \ + -Zallow-features=$(rust_allowed_features),register_tool \ -Zcrate-attr=no_std \ -Zcrate-attr='feature($(rust_allowed_features))' \ + -Zcrate-attr='feature(register_tool)' \ + -Zcrate-attr='register_tool(klint)' \ --extern alloc --extern kernel \ --crate-type rlib --out-dir $(obj) -L $(objtree)/rust/ \ --crate-name $(basename $(notdir $@))