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 an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.