From: "Onur Özkan" <work@onurozkan.dev>
To: rust-for-linux@vger.kernel.org, linux-kernel@vger.kernel.org
Cc: ojeda@kernel.org, boqun@kernel.org, gary@garyguo.net,
bjorn3_gh@protonmail.com, lossin@kernel.org,
a.hindborg@kernel.org, aliceryhl@google.com, tmgross@umich.edu,
dakr@kernel.org, tamird@kernel.org, daniel.almeida@collabora.com,
"Onur Özkan" <work@onurozkan.dev>
Subject: [PATCH v2 1/1] rust: workqueue: add cancel_sync support
Date: Fri, 12 Jun 2026 22:45:42 +0300 [thread overview]
Message-ID: <20260612194633.350011-2-work@onurozkan.dev> (raw)
In-Reply-To: <20260612194633.350011-1-work@onurozkan.dev>
Drivers can use this during teardown to cancel pending work and wait for
running work to finish before dropping related resources.
This is not implemented for Pin<KBox<T>> because queuing a boxed work
item transfers ownership of the box to the workqueue. There is therefore
no separate safe owner that can cancel the boxed work while it is pending.
Signed-off-by: Onur Özkan <work@onurozkan.dev>
---
rust/kernel/workqueue.rs | 116 +++++++++++++++++++++++++++++++++++++++
1 file changed, 116 insertions(+)
diff --git a/rust/kernel/workqueue.rs b/rust/kernel/workqueue.rs
index 7e253b6f299c..4d61d7a10fae 100644
--- a/rust/kernel/workqueue.rs
+++ b/rust/kernel/workqueue.rs
@@ -471,6 +471,23 @@ pub trait WorkItem<const ID: u64 = 0> {
fn run(this: Self::Pointer);
}
+/// Work item pointers that support cancellation.
+///
+/// # Safety
+///
+/// Implementers must ensure that `from_raw_work` rebuilds the exact ownership transferred
+/// by a successful [`RawWorkItem::__enqueue`] call.
+pub unsafe trait SupportsCancelling<const ID: u64>: WorkItemPointer<ID> + Sized {
+ /// Rebuild this work item's pointer from its embedded `work_struct`.
+ ///
+ /// # Safety
+ ///
+ /// The provided `work_struct` pointer must originate from a previous call to
+ /// [`RawWorkItem::__enqueue`] where the `queue_work_on` closure returned true
+ /// and the pointer must still be valid.
+ unsafe fn from_raw_work(ptr: *mut bindings::work_struct) -> Self;
+}
+
/// Links for a work item.
///
/// This struct contains a function pointer to the [`run`] function from the [`WorkItemPointer`]
@@ -537,6 +554,32 @@ pub unsafe fn raw_get(ptr: *const Self) -> *mut bindings::work_struct {
// the compiler does not complain that the `work` field is unused.
unsafe { Opaque::cast_into(core::ptr::addr_of!((*ptr).work)) }
}
+
+ /// Cancels this work item if it is pending and waits for any running execution to finish.
+ ///
+ /// On return, the work item is guaranteed to not be pending or executing as long as there are
+ /// no racing re-enqueues.
+ ///
+ /// # Note
+ ///
+ /// Should be called from a sleepable context if the work was last queued on a non-BH
+ /// workqueue.
+ #[inline]
+ pub fn cancel_sync(&self) -> Option<T::Pointer>
+ where
+ T: WorkItem<ID>,
+ T::Pointer: SupportsCancelling<ID>,
+ {
+ let ptr = self.work.get();
+ // SAFETY: `ptr` is a valid embedded `work_struct`.
+ if unsafe { bindings::cancel_work_sync(ptr) } {
+ // SAFETY: A `true` return means the work was pending and got canceled, so the queued
+ // ownership transfer performed by `__enqueue` is reclaimed here.
+ Some(unsafe { T::Pointer::from_raw_work(ptr) })
+ } else {
+ None
+ }
+ }
}
/// Declares that a type contains a [`Work<T, ID>`].
@@ -725,6 +768,34 @@ pub unsafe fn raw_as_work(ptr: *const Self) -> *mut Work<T, ID> {
// CAST: Work and work_struct have compatible layouts.
wrk.cast()
}
+
+ /// Cancels this delayed work item if it is pending and waits for any running execution to
+ /// finish.
+ ///
+ /// On return, the work item is guaranteed to not be pending or executing as long as there are
+ /// no racing re-enqueues.
+ ///
+ /// # Note
+ ///
+ /// Should be called from a sleepable context if the work was last queued on a non-BH
+ /// workqueue.
+ #[inline]
+ pub fn cancel_sync(&self) -> Option<T::Pointer>
+ where
+ T: WorkItem<ID>,
+ T::Pointer: SupportsCancelling<ID>,
+ {
+ let ptr = self.dwork.get();
+
+ // SAFETY: `ptr` is a valid embedded `delayed_work`.
+ if unsafe { bindings::cancel_delayed_work_sync(ptr) } {
+ // SAFETY: A `true` return means the work was pending and got canceled, so the queued
+ // ownership transfer performed by `__enqueue` is reclaimed here.
+ Some(unsafe { T::Pointer::from_raw_work(core::ptr::addr_of_mut!((*ptr).work)) })
+ } else {
+ None
+ }
+ }
}
/// Declares that a type contains a [`DelayedWork<T, ID>`].
@@ -871,6 +942,24 @@ unsafe fn __enqueue<F>(self, queue_work_on: F) -> Self::EnqueueOutput
}
}
+// SAFETY: `from_raw_work()` reconstructs exactly the `Arc<T>` ownership previously transferred by
+// `Arc::into_raw()` in `RawWorkItem::__enqueue`, using the same `Work<T, ID>` field selected by
+// `HasWork<T, ID>`.
+unsafe impl<T, const ID: u64> SupportsCancelling<ID> for Arc<T>
+where
+ T: WorkItem<ID, Pointer = Self>,
+ T: HasWork<T, ID>,
+{
+ unsafe fn from_raw_work(ptr: *mut bindings::work_struct) -> Self {
+ // The `__enqueue` method always uses a `work_struct` stored in a `Work<T, ID>`.
+ let ptr = ptr.cast::<Work<T, ID>>();
+ // SAFETY: This computes the pointer that `__enqueue` got from `Arc::into_raw`.
+ let ptr = unsafe { T::work_container_of(ptr) };
+ // SAFETY: This pointer comes from `Arc::into_raw` and we've been given back ownership.
+ unsafe { Arc::from_raw(ptr) }
+ }
+}
+
// 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.
@@ -1013,6 +1102,33 @@ unsafe fn __enqueue<F>(self, queue_work_on: F) -> Self::EnqueueOutput
}
}
+// SAFETY: `from_raw_work()` reconstructs exactly the `ARef<T>` ownership previously transferred by
+// `ARef::into_raw()` in `RawWorkItem::__enqueue`, using the same `Work<T, ID>` field selected by
+// `HasWork<T, ID>`.
+unsafe impl<T, const ID: u64> SupportsCancelling<ID> for ARef<T>
+where
+ T: AlwaysRefCounted,
+ T: WorkItem<ID, Pointer = Self>,
+ T: HasWork<T, ID>,
+{
+ unsafe fn from_raw_work(ptr: *mut bindings::work_struct) -> Self {
+ // The `__enqueue` method always uses a `work_struct` stored in a `Work<T, ID>`.
+ let ptr = ptr.cast::<Work<T, ID>>();
+
+ // SAFETY: This computes the pointer that `__enqueue` got from
+ // `ARef::into_raw`.
+ let ptr = unsafe { T::work_container_of(ptr) };
+
+ // SAFETY: The safety contract of `work_container_of` ensures that it
+ // returns a valid non-null pointer.
+ let ptr = unsafe { NonNull::new_unchecked(ptr) };
+
+ // SAFETY: This pointer comes from `ARef::into_raw` and we've been given
+ // back ownership.
+ unsafe { ARef::from_raw(ptr) }
+ }
+}
+
// 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.51.2
next prev parent reply other threads:[~2026-06-12 19:46 UTC|newest]
Thread overview: 3+ messages / expand[flat|nested] mbox.gz Atom feed top
2026-06-12 19:45 [PATCH v2 0/1] rust: workqueue: add cancel_sync support Onur Özkan
2026-06-12 19:45 ` Onur Özkan [this message]
2026-06-15 7:08 ` [PATCH v2 1/1] " Alice Ryhl
Reply instructions:
You may reply publicly to this message via plain-text email
using any one of the following methods:
* Save the following mbox file, import it into your mail client,
and reply-to-all from there: mbox
Avoid top-posting and favor interleaved quoting:
https://en.wikipedia.org/wiki/Posting_style#Interleaved_style
* Reply using the --to, --cc, and --in-reply-to
switches of git-send-email(1):
git send-email \
--in-reply-to=20260612194633.350011-2-work@onurozkan.dev \
--to=work@onurozkan.dev \
--cc=a.hindborg@kernel.org \
--cc=aliceryhl@google.com \
--cc=bjorn3_gh@protonmail.com \
--cc=boqun@kernel.org \
--cc=dakr@kernel.org \
--cc=daniel.almeida@collabora.com \
--cc=gary@garyguo.net \
--cc=linux-kernel@vger.kernel.org \
--cc=lossin@kernel.org \
--cc=ojeda@kernel.org \
--cc=rust-for-linux@vger.kernel.org \
--cc=tamird@kernel.org \
--cc=tmgross@umich.edu \
/path/to/YOUR_REPLY
https://kernel.org/pub/software/scm/git/docs/git-send-email.html
* If your mail client supports setting the In-Reply-To header
via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line
before the message body.
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox