Rust for Linux List
 help / color / mirror / Atom feed
* [PATCH] rust: workqueue: add cancel_work for Arc<T>
@ 2026-05-18  4:23 Moayad Salloum
  2026-05-18  5:45 ` Onur Özkan
                   ` (2 more replies)
  0 siblings, 3 replies; 5+ messages in thread
From: Moayad Salloum @ 2026-05-18  4:23 UTC (permalink / raw)
  To: rust-for-linux
  Cc: linux-kernel, ojeda, aliceryhl, lossin, a.hindborg,
	Moayad Salloum

Add a safe cancel_work() function for work items owned by Arc<T>.

In the existing API, enqueueing an Arc<T> calls Arc::into_raw() to leak
the Arc reference into the workqueue. WorkItemPointer::run() is the only
place that reclaims it via Arc::from_raw(). Without a cancel function,
drivers had no safe way to cancel pending work, and any attempt to do so
by calling bindings::cancel_work() directly would leak the Arc reference
since run() would never be called.

cancel_work() handles this by calling Arc::from_raw() when cancel
succeeds, reclaiming the leaked reference that __enqueue() left behind.

Signed-off-by: Moayad Salloum <salloummoayad4@gmail.com>
---
 rust/kernel/workqueue.rs | 29 +++++++++++++++++++++++++++++
 1 file changed, 29 insertions(+)

diff --git a/rust/kernel/workqueue.rs b/rust/kernel/workqueue.rs
index 6d665418b..daff9040a 100644
--- a/rust/kernel/workqueue.rs
+++ b/rust/kernel/workqueue.rs
@@ -871,6 +871,35 @@ unsafe fn __enqueue<F>(self, queue_work_on: F) -> Self::EnqueueOutput
     }
 }
 
+pub fn cancel_work<T, const ID: u64>(item: &Arc<T>) -> bool
+where
+    T: WorkItem<ID, Pointer = Arc<T>>,
+    T: HasWork<T, ID>,
+{
+    let ptr = Arc::as_ptr(item);
+    // SAFETY: `ptr` comes from `Arc::as_ptr` which is a valid non-dangling pointer to `T`.
+    let work_ptr = unsafe { T::raw_get_work(ptr.cast_mut()) };
+    // SAFETY: `raw_get_work` returns a pointer to a valid `Work<T, ID>` field.
+    let work_ptr_struct = unsafe { Work::raw_get(work_ptr) };
+    // SAFETY: The `Arc` keeps the allocation alive, so `work_ptr_struct` is valid for
+    // the duration of this call.
+    let cancel_res = unsafe { bindings::cancel_work(work_ptr_struct) };
+
+    if cancel_res {
+        // SAFETY: `cancel_work` returned true, meaning the work was pending and has been
+        // removed from the queue. The workqueue will not call `run`, so we are responsible
+        // for reclaiming the `Arc` reference that was leaked in `__enqueue` via
+        // `Arc::into_raw`. We use `work_container_of` to recover the original `*const T`
+        // pointer from the `Work<T, ID>` field pointer, then reconstruct the `Arc` with
+        // `Arc::from_raw` and drop it to decrement the ref count.
+        let item_ptr = unsafe { T::work_container_of(work_ptr) };
+        drop(unsafe { Arc::from_raw(item_ptr) });
+        true
+    } else {
+        false
+    }
+}
+
 // SAFETY: By the safety requirements of `HasDelayedWork`, the `work_struct` returned by methods in
 // `HasWork` provides a `work_struct` that is the `work` field of a `delayed_work`, and the rest of
 // the `delayed_work` has the same access rules as its `work` field.
-- 
2.43.0


^ permalink raw reply related	[flat|nested] 5+ messages in thread
* [PATCH 1/2] rust: workqueue: add SAFETY comments for Pin<KBox<T>> impls
@ 2026-05-18  4:11 Moayad Salloum
  2026-05-18  4:11 ` [PATCH] rust: workqueue: add cancel_work for Arc<T> Moayad Salloum
  0 siblings, 1 reply; 5+ messages in thread
From: Moayad Salloum @ 2026-05-18  4:11 UTC (permalink / raw)
  To: rust-for-linux
  Cc: linux-kernel, ojeda, aliceryhl, lossin, a.hindborg,
	Moayad Salloum

Fill in the two SAFETY: TODO placeholders for the WorkItemPointer<ID>
and RawWorkItem<ID> implementations of Pin<KBox<T>>.

For WorkItemPointer: __enqueue uses a work_struct initialized with this
trait's run function because Work::new stores T::Pointer::run via
init_work_with_key, and the ID const generic ensures the correct Work
field and WorkItemPointer impl are selected.

For RawWorkItem: the work_struct pointer remains valid for the closure's
duration because KBox::into_raw leaks the allocation without dropping it.
If queue_work_on returns true, the memory is only reclaimed in
WorkItemPointer::run via KBox::from_raw. queue_work_on cannot return
false because Pin<KBox<T>> provides exclusive ownership, so the item
cannot already be in a workqueue.

Signed-off-by: Moayad Salloum <salloummoayad4@gmail.com>
---
 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 7e253b6f2..6d665418b 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 ensures 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: The `work_struct` raw pointer is guaranteed to be valid for the duration of the call to
+// the closure because we call `KBox::into_raw` before the closure, which leaks the allocation
+// without dropping it, so the pointer remains live. If `queue_work_on` returns true, it is further
+// guaranteed to be valid until a call to the function pointer in the `work_struct` because the
+// leaked memory is only reclaimed in `WorkItemPointer::run` (via `KBox::from_raw`), which is what
+// the function pointer in the `work_struct` must be pointing to, according to the safety
+// requirements of `WorkItemPointer`. Note that `queue_work_on` can never return false here:
+// `Pin<KBox<T>>` provides exclusive ownership, so the work item cannot already be in a workqueue.
 unsafe impl<T, const ID: u64> RawWorkItem<ID> for Pin<KBox<T>>
 where
     T: WorkItem<ID, Pointer = Self>,
-- 
2.43.0


^ permalink raw reply related	[flat|nested] 5+ messages in thread

end of thread, other threads:[~2026-05-18 19:46 UTC | newest]

Thread overview: 5+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2026-05-18  4:23 [PATCH] rust: workqueue: add cancel_work for Arc<T> Moayad Salloum
2026-05-18  5:45 ` Onur Özkan
2026-05-18  8:10 ` kernel test robot
2026-05-18 19:46 ` kernel test robot
  -- strict thread matches above, loose matches on Subject: below --
2026-05-18  4:11 [PATCH 1/2] rust: workqueue: add SAFETY comments for Pin<KBox<T>> impls Moayad Salloum
2026-05-18  4:11 ` [PATCH] rust: workqueue: add cancel_work for Arc<T> Moayad Salloum

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox