[PATCH 4/4] rust: workqueue: document safety of Pin<KBox<T>> work item impls
Cian McGuire <[email protected]> Fri, 24 Jul 2026 22:49:40 +0100
| Newsgroups | org.kernel.vger.rust-for-linux,org.kernel.vger.linux-kernel |
|---|---|
| Message-ID | <[email protected]> |
The `WorkItemPointer` and `RawWorkItem` impls for `Pin<KBox<T>>` had their SAFETY comments left as "TODO.". Both traits are already implemented for `Arc<T>` earlier in this file with real justifications; these two impls follow the same shape, adapted for `KBox`. `WorkItemPointer`'s contract requires that `__enqueue` use a `work_struct` initialized with this impl's `run` as the function pointer. This holds regardless of which smart pointer type is involved: `Work::new` is generic over any `T: WorkItem<ID>` and always bakes in `T::Pointer::run`, so for a `T` bound by `WorkItem<ID, Pointer = Self>` it bakes in exactly this impl's `run`. The argument is identical to the existing `Arc<T>` comment and does not depend on anything Arc-specific. `RawWorkItem`'s contract requires that pointers passed to the `queue_work_on` closure remain valid for the duration `__enqueue` guarantees. This one is not a direct copy of the `Arc<T>` case, because the two `__enqueue` implementations differ: `__enqueue` here leaks the box via `KBox::into_raw` before calling the closure, so the pointer is valid for the call. If the closure returns true, the pointer remains valid until reclaimed in `WorkItemPointer::run` via a matching `KBox::from_raw`. If the closure returns false, `__enqueue`'s own safety requirement only permits that when the `work_struct` is already enqueued elsewhere -- but `self` is an exclusively-owned `Pin<KBox<T>>`, and ownership is only ever given up by enqueuing it, so it cannot already be enqueued while this call still holds it. That branch is therefore genuinely unreachable, matching the `unreachable_unchecked` call. Suggested-by: Miguel Ojeda <[email protected]> Link: https://github.com/Rust-for-Linux/linux/issues/351 Signed-off-by: Cian McGuire <[email protected]> --- rust/kernel/workqueue.rs | 19 +++++++++++++++++-- 1 file changed, 17 insertions(+), 2 deletions(-) diff --git a/rust/kernel/workqueue.rs b/rust/kernel/workqueue.rs index 7e253b6f299c..0a525746028b 100644 --- a/rust/kernel/workqueue.rs +++ b/rust/kernel/workqueue.rs @@ -881,7 +881,15 @@ unsafe impl<T, const ID: u64> RawDelayedWorkItem<ID> for Arc<T> { } -// SAFETY: TODO. +// SAFETY: The `__enqueue` implementation in `RawWorkItem` uses a `work_struct` initialized +// with the `run` method of this trait as the function pointer because: +// - `__enqueue` gets the `work_struct` from the `Work` field, using `T::raw_get_work`. +// - The only safe way to create a `Work` object is through `Work::new`. +// - `Work::new` makes sure that `T::Pointer::run` is passed to `init_work_with_key`. +// - Finally `Work` and `RawWorkItem` guarantee that the correct `Work` field will be used +// because of the ID const generic bound. This makes sure that `T::raw_get_work` uses the +// correct offset for the `Work` field, and `Work::new` picks the correct implementation +// of `WorkItemPointer` for `Pin<KBox<T>>`. unsafe impl<T, const ID: u64> WorkItemPointer<ID> for Pin<KBox<T>> where T: WorkItem<ID, Pointer = Self>, @@ -901,7 +909,14 @@ unsafe impl<T, const ID: u64> WorkItemPointer<ID> for Pin<KBox<T>> } } -// SAFETY: TODO. +// SAFETY: `__enqueue` leaks the box via `KBox::into_raw` before calling `queue_work_on`, so +// the pointer is valid for the duration of that call. If the closure returns true, the +// pointer remains valid until reclaimed in `WorkItemPointer::run`, which reconstructs the box +// with a matching `KBox::from_raw`. If the closure returns false, `__enqueue`'s own safety +// requirement only permits that when the `work_struct` is already in a workqueue -- but +// `self` is an exclusively-owned `Pin<KBox<T>>`, and ownership is only ever given up by +// enqueuing it, so it cannot already be enqueued elsewhere while we still hold it here. That +// branch is therefore unreachable, matching the `unreachable_unchecked` call. unsafe impl<T, const ID: u64> RawWorkItem<ID> for Pin<KBox<T>> where T: WorkItem<ID, Pointer = Self>, -- 2.55.0