diff options
Diffstat (limited to 'rust/kernel/workqueue.rs')
-rw-r--r-- | rust/kernel/workqueue.rs | 502 |
1 files changed, 502 insertions, 0 deletions
diff --git a/rust/kernel/workqueue.rs b/rust/kernel/workqueue.rs new file mode 100644 index 000000000000..c76046f65e5d --- /dev/null +++ b/rust/kernel/workqueue.rs @@ -0,0 +1,502 @@ +// SPDX-License-Identifier: GPL-2.0 + +//! Work queues. +//! +//! C header: [`include/linux/workqueue.h`](../../../../include/linux/workqueue.h) + +use crate::{ + bindings, c_str, + error::code::*, + sync::{Arc, LockClassKey, UniqueArc}, + Opaque, Result, +}; +use core::{fmt, ops::Deref, ptr::NonNull}; + +/// Spawns a new work item to run in the work queue. +/// +/// It also automatically defines a new lockdep lock class for the work item. +#[macro_export] +macro_rules! spawn_work_item { + ($queue:expr, $func:expr) => {{ + static CLASS: $crate::sync::LockClassKey = $crate::sync::LockClassKey::new(); + $crate::workqueue::Queue::try_spawn($queue, &CLASS, $func) + }}; +} + +/// Implements the [`WorkAdapter`] trait for a type where its [`Work`] instance is a field. +/// +/// # Examples +/// +/// ``` +/// # use kernel::workqueue::Work; +/// +/// struct Example { +/// work: Work, +/// } +/// +/// kernel::impl_self_work_adapter!(Example, work, |_| {}); +/// ``` +#[macro_export] +macro_rules! impl_self_work_adapter { + ($work_type:ty, $field:ident, $closure:expr) => { + $crate::impl_work_adapter!($work_type, $work_type, $field, $closure); + }; +} + +/// Implements the [`WorkAdapter`] trait for an adapter type. +/// +/// # Examples +/// +/// ``` +/// # use kernel::workqueue::Work; +/// +/// struct Example { +/// work: Work, +/// } +/// +/// struct Adapter; +/// +/// kernel::impl_work_adapter!(Adapter, Example, work, |_| {}); +/// ``` +#[macro_export] +macro_rules! impl_work_adapter { + ($adapter:ty, $work_type:ty, $field:ident, $closure:expr) => { + // SAFETY: We use `offset_of` to ensure that the field is within the given type, and we + // also check its type is `Work`. + unsafe impl $crate::workqueue::WorkAdapter for $adapter { + type Target = $work_type; + const FIELD_OFFSET: isize = $crate::offset_of!(Self::Target, $field); + fn run(w: $crate::sync::Arc<Self::Target>) { + let closure: fn($crate::sync::Arc<Self::Target>) = $closure; + closure(w); + return; + + // Checks that the type of the field is actually `Work`. + let tmp = core::mem::MaybeUninit::<$work_type>::uninit(); + // SAFETY: The pointer is valid and aligned, just not initialised; `addr_of` + // ensures that we don't actually read from it (which would be UB) nor create an + // intermediate reference. + let _x: *const $crate::workqueue::Work = + unsafe { core::ptr::addr_of!((*tmp.as_ptr()).$field) }; + } + } + }; +} + +/// Initialises a work item. +/// +/// It automatically defines a new lockdep lock class for the work item. +#[macro_export] +macro_rules! init_work_item { + ($work_container:expr) => {{ + static CLASS: $crate::sync::LockClassKey = $crate::sync::LockClassKey::new(); + $crate::workqueue::Work::init($work_container, &CLASS) + }}; +} + +/// Initialises a work item with the given adapter. +/// +/// It automatically defines a new lockdep lock class for the work item. +#[macro_export] +macro_rules! init_work_item_adapter { + ($adapter:ty, $work_container:expr) => {{ + static CLASS: $crate::sync::LockClassKey = $crate::sync::LockClassKey::new(); + $crate::workqueue::Work::init_with_adapter::<$adapter>($work_container, &CLASS) + }}; +} + +/// A kernel work queue. +/// +/// Wraps the kernel's C `struct workqueue_struct`. +/// +/// It allows work items to be queued to run on thread pools managed by the kernel. Several are +/// always available, for example, the ones returned by [`system`], [`system_highpri`], +/// [`system_long`], etc. +/// +/// # Examples +/// +/// The following example is the simplest way to launch a work item: +/// +/// ``` +/// # use kernel::{spawn_work_item, workqueue}; +/// spawn_work_item!(workqueue::system(), || pr_info!("Hello from a work item\n"))?; +/// +/// # Ok::<(), Error>(()) +/// ``` +/// +/// The following example is used to create a work item and enqueue it several times. We note that +/// enqueuing while the work item is already queued is a no-op, so we enqueue it when it is not +/// enqueued yet. +/// +/// ``` +/// # use kernel::workqueue::{self, Work}; +/// use core::sync::atomic::{AtomicU32, Ordering}; +/// use kernel::sync::UniqueArc; +/// +/// struct Example { +/// count: AtomicU32, +/// work: Work, +/// } +/// +/// kernel::impl_self_work_adapter!(Example, work, |w| { +/// let count = w.count.fetch_add(1, Ordering::Relaxed); +/// pr_info!("Called with count={}\n", count); +/// +/// // Queue again if the count is less than 10. +/// if count < 10 { +/// workqueue::system().enqueue(w); +/// } +/// }); +/// +/// let e = UniqueArc::try_new(Example { +/// count: AtomicU32::new(0), +/// // SAFETY: `work` is initialised below. +/// work: unsafe { Work::new() }, +/// })?; +/// +/// kernel::init_work_item!(&e); +/// +/// // Queue the first time. +/// workqueue::system().enqueue(e.into()); +/// +/// # Ok::<(), Error>(()) +/// ``` +/// +/// The following example has two different work items in the same struct, which allows it to be +/// queued twice. +/// +/// ``` +/// # use kernel::workqueue::{self, Work, WorkAdapter}; +/// use core::sync::atomic::{AtomicU32, Ordering}; +/// use kernel::sync::{Arc, UniqueArc}; +/// +/// struct Example { +/// work1: Work, +/// work2: Work, +/// } +/// +/// kernel::impl_self_work_adapter!(Example, work1, |_| pr_info!("First work\n")); +/// +/// struct SecondAdapter; +/// kernel::impl_work_adapter!(SecondAdapter, Example, work2, |_| pr_info!("Second work\n")); +/// +/// let e = UniqueArc::try_new(Example { +/// // SAFETY: `work1` is initialised below. +/// work1: unsafe { Work::new() }, +/// // SAFETY: `work2` is initialised below. +/// work2: unsafe { Work::new() }, +/// })?; +/// +/// kernel::init_work_item!(&e); +/// kernel::init_work_item_adapter!(SecondAdapter, &e); +/// +/// let e = Arc::from(e); +/// +/// // Enqueue the two different work items. +/// workqueue::system().enqueue(e.clone()); +/// workqueue::system().enqueue_adapter::<SecondAdapter>(e); +/// +/// # Ok::<(), Error>(()) +/// ``` +#[repr(transparent)] +pub struct Queue(Opaque<bindings::workqueue_struct>); + +// SAFETY: Kernel workqueues are usable from any thread. +unsafe impl Sync for Queue {} + +impl Queue { + /// Tries to allocate a new work queue. + /// + /// Callers should first consider using one of the existing ones (e.g. [`system`]) before + /// deciding to create a new one. + pub fn try_new(name: fmt::Arguments<'_>) -> Result<BoxedQueue> { + // SAFETY: We use a format string that requires an `fmt::Arguments` pointer as the first + // and only argument. + let ptr = unsafe { + bindings::alloc_workqueue( + c_str!("%pA").as_char_ptr(), + 0, + 0, + &name as *const _ as *const core::ffi::c_void, + ) + }; + if ptr.is_null() { + return Err(ENOMEM); + } + + // SAFETY: `ptr` was just allocated and checked above, so it non-null and valid. Plus, it + // isn't touched after the call below, so ownership is transferred. + Ok(unsafe { BoxedQueue::new(ptr) }) + } + + /// Enqueues a work item. + /// + /// Returns `true` if the work item was successfully enqueue; returns `false` if it had already + /// been (and continued to be) enqueued. + pub fn enqueue<T: WorkAdapter<Target = T>>(&self, w: Arc<T>) -> bool { + self.enqueue_adapter::<T>(w) + } + + /// Enqueues a work item with an explicit adapter. + /// + /// 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<A: WorkAdapter + ?Sized>(&self, w: Arc<A::Target>) -> bool { + let ptr = Arc::into_raw(w); + 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. + 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) }; + } + + ret + } + + /// Tries to spawn the given function or closure as a work item. + /// + /// Users are encouraged to use [`spawn_work_item`] as it automatically defines the lock class + /// key to be used. + pub fn try_spawn<T: 'static + Send + Fn()>( + &self, + key: &'static LockClassKey, + func: T, + ) -> Result { + let w = UniqueArc::<ClosureAdapter<T>>::try_new(ClosureAdapter { + // SAFETY: `work` is initialised below. + work: unsafe { Work::new() }, + func, + })?; + Work::init(&w, key); + self.enqueue(w.into()); + Ok(()) + } +} + +struct ClosureAdapter<T: Fn() + Send> { + work: Work, + func: T, +} + +// SAFETY: `ClosureAdapter::work` is of type `Work`. +unsafe impl<T: Fn() + Send> WorkAdapter for ClosureAdapter<T> { + type Target = Self; + const FIELD_OFFSET: isize = crate::offset_of!(Self, work); + + fn run(w: Arc<Self::Target>) { + (w.func)(); + } +} + +/// An adapter for work items. +/// +/// For the most usual case where a type has a [`Work`] in it and is itself the adapter, it is +/// recommended that they use the [`impl_self_work_adapter`] or [`impl_work_adapter`] macros +/// instead of implementing the [`WorkAdapter`] manually. The great advantage is that they don't +/// require any unsafe blocks. +/// +/// # Safety +/// +/// Implementers must ensure that there is a [`Work`] instance `FIELD_OFFSET` bytes from the +/// beginning of a valid `Target` type. It is normally safe to use the [`crate::offset_of`] macro +/// for this. +pub unsafe trait WorkAdapter { + /// The type that this work adapter is meant to use. + type Target; + + /// The offset, in bytes, from the beginning of [`Self::Target`] to the instance of [`Work`]. + const FIELD_OFFSET: isize; + + /// Runs when the work item is picked up for execution after it has been enqueued to some work + /// queue. + fn run(w: Arc<Self::Target>); +} + +/// A work item. +/// +/// Wraps the kernel's C `struct work_struct`. +/// +/// Users must add a field of this type to a structure, then implement [`WorkAdapter`] so that it +/// can be queued for execution in a thread pool. Examples of it being used are available in the +/// documentation for [`Queue`]. +#[repr(transparent)] +pub struct Work(Opaque<bindings::work_struct>); + +impl Work { + /// Creates a new instance of [`Work`]. + /// + /// # Safety + /// + /// Callers must call either [`Work::init`] or [`Work::init_with_adapter`] before the work item + /// can be used. + pub unsafe fn new() -> Self { + Self(Opaque::uninit()) + } + + /// Initialises the work item. + /// + /// Users should prefer the [`init_work_item`] macro because it automatically defines a new + /// lock class key. + pub fn init<T: WorkAdapter<Target = T>>(obj: &UniqueArc<T>, key: &'static LockClassKey) { + Self::init_with_adapter::<T>(obj, key) + } + + /// Initialises the work item with the given adapter. + /// + /// Users should prefer the [`init_work_item_adapter`] macro because it automatically defines a + /// new lock class key. + pub fn init_with_adapter<A: WorkAdapter>( + obj: &UniqueArc<A::Target>, + key: &'static LockClassKey, + ) { + let ptr = &**obj as *const _ as *const u8; + let field_ptr = ptr.wrapping_offset(A::FIELD_OFFSET) as *mut bindings::work_struct; + + // SAFETY: `work` is valid for writes -- the `UniqueArc` instance guarantees that it has + // been allocated and there is only one pointer to it. Additionally, `work_func` is a valid + // callback for the work item. + unsafe { + bindings::__INIT_WORK_WITH_KEY(field_ptr, Some(Self::work_func::<A>), false, key.get()) + }; + } + + /// Cancels the work item. + /// + /// It is ok for this to be called when the work is not queued. + pub fn cancel(&self) { + // SAFETY: The work is valid (we have a reference to it), and the function can be called + // whether the work is queued or not. + if unsafe { bindings::cancel_work_sync(self.0.get()) } { + // SAFETY: When the work was queued, a call to `into_raw` was made. We just canceled + // the work without it having the chance to run, so we need to explicitly destroy this + // reference (which would have happened in `work_func` if it did run). + unsafe { Arc::from_raw(&*self) }; + } + } + + unsafe extern "C" fn work_func<A: WorkAdapter>(work: *mut bindings::work_struct) { + let field_ptr = work as *const _ as *const u8; + let ptr = field_ptr.wrapping_offset(-A::FIELD_OFFSET) as *const A::Target; + + // SAFETY: This callback is only ever used by the `init_with_adapter` method, so it is + // always the case that the work item is embedded in a `Work` (Self) struct. + let w = unsafe { Arc::from_raw(ptr) }; + A::run(w); + } +} + +/// A boxed owned workqueue. +/// +/// # Invariants +/// +/// `ptr` is owned by this instance of [`BoxedQueue`], so it's always valid. +pub struct BoxedQueue { + ptr: NonNull<Queue>, +} + +impl BoxedQueue { + /// Creates a new instance of [`BoxedQueue`]. + /// + /// # Safety + /// + /// `ptr` must be non-null and valid. Additionally, ownership must be handed over to new + /// instance of [`BoxedQueue`]. + unsafe fn new(ptr: *mut bindings::workqueue_struct) -> Self { + Self { + // SAFETY: We checked above that `ptr` is non-null. + ptr: unsafe { NonNull::new_unchecked(ptr.cast()) }, + } + } +} + +impl Deref for BoxedQueue { + type Target = Queue; + + fn deref(&self) -> &Queue { + // SAFETY: The type invariants guarantee that `ptr` is always valid. + unsafe { self.ptr.as_ref() } + } +} + +impl Drop for BoxedQueue { + fn drop(&mut self) { + // SAFETY: The type invariants guarantee that `ptr` is always valid. + unsafe { bindings::destroy_workqueue(self.ptr.as_ref().0.get()) }; + } +} + +/// Returns the system work queue (`system_wq`). +/// +/// It is the one used by schedule\[_delayed\]_work\[_on\](). Multi-CPU multi-threaded. There are +/// users which expect relatively short queue flush time. +/// +/// Callers shouldn't queue work items which can run for too long. +pub fn system() -> &'static Queue { + // SAFETY: `system_wq` is a C global, always available. + unsafe { &*bindings::system_wq.cast() } +} + +/// Returns the system high-priority work queue (`system_highpri_wq`). +/// +/// It is similar to the one returned by [`system`] but for work items which require higher +/// scheduling priority. +pub fn system_highpri() -> &'static Queue { + // SAFETY: `system_highpri_wq` is a C global, always available. + unsafe { &*bindings::system_highpri_wq.cast() } +} + +/// Returns the system work queue for potentially long-running work items (`system_long_wq`). +/// +/// It is similar to the one returned by [`system`] but may host long running work items. Queue +/// flushing might take relatively long. +pub fn system_long() -> &'static Queue { + // SAFETY: `system_long_wq` is a C global, always available. + unsafe { &*bindings::system_long_wq.cast() } +} + +/// Returns the system unbound work queue (`system_unbound_wq`). +/// +/// Workers are not bound to any specific CPU, not concurrency managed, and all queued work items +/// are executed immediately as long as `max_active` limit is not reached and resources are +/// available. +pub fn system_unbound() -> &'static Queue { + // SAFETY: `system_unbound_wq` is a C global, always available. + unsafe { &*bindings::system_unbound_wq.cast() } +} + +/// Returns the system freezable work queue (`system_freezable_wq`). +/// +/// It is equivalent to the one returned by [`system`] except that it's freezable. +pub fn system_freezable() -> &'static Queue { + // SAFETY: `system_freezable_wq` is a C global, always available. + unsafe { &*bindings::system_freezable_wq.cast() } +} + +/// Returns the system power-efficient work queue (`system_power_efficient_wq`). +/// +/// It is inclined towards saving power and is converted to "unbound" variants if the +/// `workqueue.power_efficient` kernel parameter is specified; otherwise, it is similar to the one +/// returned by [`system`]. +pub fn system_power_efficient() -> &'static Queue { + // SAFETY: `system_power_efficient_wq` is a C global, always available. + unsafe { &*bindings::system_power_efficient_wq.cast() } +} + +/// Returns the system freezable power-efficient work queue (`system_freezable_power_efficient_wq`). +/// +/// It is similar to the one returned by [`system_power_efficient`] except that is freezable. +pub fn system_freezable_power_efficient() -> &'static Queue { + // SAFETY: `system_freezable_power_efficient_wq` is a C global, always available. + unsafe { &*bindings::system_freezable_power_efficient_wq.cast() } +} |