rust/kernel/workqueue.rs | 239 ++++++++++++++++++++++++++++++++++++++- 1 file changed, 233 insertions(+), 6 deletions(-)
This feature is required for our Hyper-V VMBus reimplementation effort.
As it provides the ability to modify the running state of work items at
runtime.
The design revolves around having
Queue::enqueue()/Queue::enqueue_delayed() return implementations of
WorkHandle which can then be used to enable/disable/cancel
the relevant work item.
Please see the example introduced at the top of the file for example
usage of work handles.
Signed-off-by: Hamza Mahfooz <hamzamahfooz@linux.microsoft.com>
---
rust/kernel/workqueue.rs | 239 ++++++++++++++++++++++++++++++++++++++-
1 file changed, 233 insertions(+), 6 deletions(-)
diff --git a/rust/kernel/workqueue.rs b/rust/kernel/workqueue.rs
index 706e833e9702..cbbd487ce8cb 100644
--- a/rust/kernel/workqueue.rs
+++ b/rust/kernel/workqueue.rs
@@ -135,7 +135,7 @@
//!
//! ```
//! use kernel::sync::Arc;
-//! use kernel::workqueue::{self, impl_has_delayed_work, new_delayed_work, DelayedWork, WorkItem};
+//! use kernel::workqueue::{self, impl_has_delayed_work, new_delayed_work, DelayedWork, WorkItem, WorkHandle};
//!
//! #[pin_data]
//! struct MyStruct {
@@ -179,8 +179,22 @@
//! fn print_now(val: Arc<MyStruct>) {
//! let _ = workqueue::system().enqueue(val);
//! }
+//!
+//! /// The returned WorkHandle can be used to cancel the work item before it gets executed.
+//! fn print_never(val: Arc<MyStruct>) {
+//! let result = workqueue::system_bh().enqueue_delayed(val, 1000);
+//!
+//! match result {
+//! Ok(handle) => {
+//! handle.cancel(true);
+//! },
+//! Err(_) => (),
+//! }
+//! }
+//!
//! # print_later(MyStruct::new(42).unwrap());
//! # print_now(MyStruct::new(42).unwrap());
+//! # print_never(MyStruct::new(42).unwrap());
//! ```
//!
//! C header: [`include/linux/workqueue.h`](srctree/include/linux/workqueue.h)
@@ -297,7 +311,7 @@ pub fn enqueue<W, const ID: u64>(&self, w: W) -> W::EnqueueOutput
/// This may fail if the work item is already enqueued in a workqueue.
///
/// The work item will be submitted using `WORK_CPU_UNBOUND`.
- pub fn enqueue_delayed<W, const ID: u64>(&self, w: W, delay: Jiffies) -> W::EnqueueOutput
+ pub fn enqueue_delayed<W, const ID: u64>(&self, w: W, delay: Jiffies) -> W::EnqueueDelayedOutput
where
W: RawDelayedWorkItem<ID> + Send + 'static,
{
@@ -317,7 +331,7 @@ pub fn enqueue_delayed<W, const ID: u64>(&self, w: W, delay: Jiffies) -> W::Enqu
// promises that the raw pointer will stay valid until we call the function pointer in the
// `work_struct`, so the access is ok.
unsafe {
- w.__enqueue(move |work_ptr| {
+ w.__enqueue_delayed(move |work_ptr| {
bindings::queue_delayed_work_on(
bindings::wq_misc_consts_WORK_CPU_UNBOUND as ffi::c_int,
queue_ptr,
@@ -421,7 +435,15 @@ unsafe fn __enqueue<F>(self, queue_work_on: F) -> Self::EnqueueOutput
/// provided pointer must point at the `work` field of a valid `delayed_work`, and the guarantees
/// that `__enqueue` provides about accessing the `work_struct` must also apply to the rest of the
/// `delayed_work` struct.
-pub unsafe trait RawDelayedWorkItem<const ID: u64>: RawWorkItem<ID> {}
+pub unsafe trait RawDelayedWorkItem<const ID: u64>: RawWorkItem<ID> {
+ /// The return type of [`Queue::enqueue_delayed`].
+ type EnqueueDelayedOutput;
+
+ /// See [`RawWorkItem::__enqueue`], this method only differs in return type.
+ unsafe fn __enqueue_delayed<F>(self, queue_work_on: F) -> Self::EnqueueDelayedOutput
+ where
+ F: FnOnce(*mut bindings::work_struct) -> bool;
+}
/// Defines the method that should be called directly when a work item is executed.
///
@@ -530,6 +552,66 @@ 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)) }
}
+
+ /// Enable the current work item
+ #[inline]
+ pub unsafe fn raw_enable(this: *const Self) -> bool {
+ unsafe { bindings::enable_work(Self::raw_get(this)) }
+ }
+
+ /// Disable and cancel the current work item
+ #[inline]
+ pub unsafe fn raw_disable(this: *const Self) -> bool {
+ unsafe { bindings::disable_work(Self::raw_get(this)) }
+ }
+
+ #[inline]
+ unsafe fn raw_get_delayed(this: *const Self) -> *mut bindings::delayed_work {
+ let work_ptr = unsafe { Self::raw_get(this) };
+ unsafe { container_of!(work_ptr, bindings::delayed_work, work) }
+ }
+
+ /// Disable and cancel the current delayed work item
+ #[inline]
+ pub unsafe fn raw_disable_delayed(this: *const Self) -> bool {
+ unsafe { bindings::disable_delayed_work(Self::raw_get_delayed(this)) }
+ }
+
+ /// Disable and cancel the current work item and block until it's done
+ #[inline]
+ pub unsafe fn raw_disable_sync(this: *const Self) -> bool {
+ unsafe { bindings::disable_work_sync(Self::raw_get(this)) }
+ }
+
+ /// Disable and cancel the current delayed work item and block until it's done
+ #[inline]
+ pub unsafe fn raw_disable_delayed_sync(this: *const Self) -> bool {
+ unsafe { bindings::disable_delayed_work_sync(Self::raw_get_delayed(this)) }
+ }
+
+ /// Kill off the current work item
+ #[inline]
+ pub unsafe fn raw_cancel(this: *const Self) -> bool {
+ unsafe { bindings::cancel_work(Self::raw_get(this)) }
+ }
+
+ /// Kill off the current delayed work item
+ #[inline]
+ pub unsafe fn raw_cancel_delayed(this: *const Self) -> bool {
+ unsafe { bindings::cancel_delayed_work(Self::raw_get_delayed(this)) }
+ }
+
+ /// Kill off the current work item and block until it's done
+ #[inline]
+ pub unsafe fn raw_cancel_sync(this: *const Self) -> bool {
+ unsafe { bindings::cancel_work_sync(Self::raw_get(this)) }
+ }
+
+ /// Kill off the current delayed work item and block until it's done
+ #[inline]
+ pub unsafe fn raw_cancel_delayed_sync(this: *const Self) -> bool {
+ unsafe { bindings::cancel_delayed_work_sync(Self::raw_get_delayed(this)) }
+ }
}
/// Declares that a type contains a [`Work<T, ID>`].
@@ -578,6 +660,122 @@ pub unsafe trait HasWork<T, const ID: u64 = 0> {
unsafe fn work_container_of(ptr: *mut Work<T, ID>) -> *mut Self;
}
+/// A handle representing a potentially running work item.
+pub unsafe trait WorkHandle {
+ /// Enable a work item
+ ///
+ /// SAFETY: can be called from any context.
+ ///
+ /// Returns `true` only if the disable count reached 0.
+ fn enable(self) -> bool;
+
+ /// Disable and cancel a work item
+ ///
+ /// SAFETY: Can be called from any context if `sync` is set to `false`.
+ ///
+ /// If `sync` is set to true this method will block until the work item
+ /// has been cancelled.
+ ///
+ /// Returns `true` if the work item was pending.
+ fn disable(self, sync: bool) -> bool;
+
+ /// Cancel a work item
+ ///
+ /// SAFETY: Can be called from any context if `sync` is set to `false`.
+ ///
+ /// If `sync` is set to true this method will block until the work item
+ /// has been cancelled.
+ ///
+ /// Returns `true` if the work item was pending.
+ fn cancel(self, sync: bool) -> bool;
+}
+
+/// A handle for an `Arc<HasWork<T>>` returned by a call to
+/// [`RawWorkItem::__enqueue`]
+pub struct ArcWorkHandle<T, const ID: u64 = 0>
+where
+ T: HasWork<T, ID>,
+{
+ inner: Arc<T>,
+}
+
+unsafe impl<T, const ID: u64> WorkHandle for ArcWorkHandle<T, ID>
+where
+ T: HasWork<T, ID>,
+{
+ fn enable(self) -> bool {
+ let self_ptr = Arc::as_ptr(&self.inner) as *mut T;
+ let work_ptr = unsafe { <T as HasWork<T, ID>>::raw_get_work(self_ptr) };
+
+ unsafe { Work::<T, ID>::raw_enable(work_ptr) }
+ }
+
+ fn disable(self, sync: bool) -> bool {
+ let self_ptr = Arc::as_ptr(&self.inner) as *mut T;
+ let work_ptr = unsafe { <T as HasWork<T, ID>>::raw_get_work(self_ptr) };
+
+ if sync {
+ unsafe { Work::<T, ID>::raw_disable_sync(work_ptr) }
+ } else {
+ unsafe { Work::<T, ID>::raw_disable(work_ptr) }
+ }
+ }
+
+ fn cancel(self, sync: bool) -> bool {
+ let self_ptr = Arc::as_ptr(&self.inner) as *mut T;
+ let work_ptr = unsafe { <T as HasWork<T, ID>>::raw_get_work(self_ptr) };
+
+ if sync {
+ unsafe { Work::<T, ID>::raw_cancel_sync(work_ptr) }
+ } else {
+ unsafe { Work::<T, ID>::raw_cancel(work_ptr) }
+ }
+ }
+}
+
+/// A handle for an `Arc<HasDelayedWork<T>>` returned by a call to
+/// [`RawWorkItem::__enqueue_delayed`]
+pub struct ArcDelayedWorkHandle<T, const ID: u64 = 0>
+where
+ T: HasDelayedWork<T, ID>,
+{
+ inner: Arc<T>,
+}
+
+unsafe impl<T, const ID: u64> WorkHandle for ArcDelayedWorkHandle<T, ID>
+where
+ T: HasDelayedWork<T, ID>,
+{
+ fn enable(self) -> bool {
+ let self_ptr = Arc::as_ptr(&self.inner) as *mut T;
+ let work_ptr = unsafe { <T as HasWork<T, ID>>::raw_get_work(self_ptr) };
+
+ unsafe { Work::<T, ID>::raw_enable(work_ptr) }
+ }
+
+ fn disable(self, sync: bool) -> bool {
+ let self_ptr = Arc::as_ptr(&self.inner) as *mut T;
+ let work_ptr = unsafe { <T as HasWork<T, ID>>::raw_get_work(self_ptr) };
+
+ if sync {
+ unsafe { Work::<T, ID>::raw_disable_delayed_sync(work_ptr) }
+ } else {
+ unsafe { Work::<T, ID>::raw_disable_delayed(work_ptr) }
+ }
+ }
+
+ fn cancel(self, sync: bool) -> bool {
+ let self_ptr = Arc::as_ptr(&self.inner) as *mut T;
+ let work_ptr = unsafe { <T as HasWork<T, ID>>::raw_get_work(self_ptr) };
+
+ if sync {
+ unsafe { Work::<T, ID>::raw_cancel_delayed_sync(work_ptr) }
+ } else {
+ unsafe { Work::<T, ID>::raw_cancel_delayed(work_ptr) }
+ }
+ }
+}
+
/// Used to safely implement the [`HasWork<T, ID>`] trait.
///
/// # Examples
@@ -841,7 +1039,7 @@ unsafe impl<T, const ID: u64> RawWorkItem<ID> for Arc<T>
T: WorkItem<ID, Pointer = Self>,
T: HasWork<T, ID>,
{
- type EnqueueOutput = Result<(), Self>;
+ type EnqueueOutput = Result<ArcWorkHandle<T, ID>, Self>;
unsafe fn __enqueue<F>(self, queue_work_on: F) -> Self::EnqueueOutput
where
@@ -856,7 +1054,7 @@ unsafe fn __enqueue<F>(self, queue_work_on: F) -> Self::EnqueueOutput
let work_ptr = unsafe { Work::raw_get(work_ptr) };
if queue_work_on(work_ptr) {
- Ok(())
+ Ok(ArcWorkHandle { inner: unsafe { Arc::from_raw(ptr) } })
} else {
// SAFETY: The work queue has not taken ownership of the pointer.
Err(unsafe { Arc::from_raw(ptr) })
@@ -872,6 +1070,27 @@ unsafe impl<T, const ID: u64> RawDelayedWorkItem<ID> for Arc<T>
T: WorkItem<ID, Pointer = Self>,
T: HasDelayedWork<T, ID>,
{
+ type EnqueueDelayedOutput = Result<ArcDelayedWorkHandle<T, ID>, Self>;
+
+ unsafe fn __enqueue_delayed<F>(self, queue_work_on: F) -> Self::EnqueueDelayedOutput
+ where
+ F: FnOnce(*mut bindings::work_struct) -> bool,
+ {
+ // Casting between const and mut is not a problem as long as the pointer is a raw pointer.
+ let ptr = Arc::into_raw(self).cast_mut();
+
+ // SAFETY: Pointers into an `Arc` point at a valid value.
+ let work_ptr = unsafe { T::raw_get_work(ptr) };
+ // SAFETY: `raw_get_work` returns a pointer to a valid value.
+ let work_ptr = unsafe { Work::raw_get(work_ptr) };
+
+ if queue_work_on(work_ptr) {
+ Ok(ArcDelayedWorkHandle { inner: unsafe { Arc::from_raw(ptr) } })
+ } else {
+ // SAFETY: The work queue has not taken ownership of the pointer.
+ Err(unsafe { Arc::from_raw(ptr) })
+ }
+ }
}
// SAFETY: TODO.
@@ -932,6 +1151,14 @@ unsafe impl<T, const ID: u64> RawDelayedWorkItem<ID> for Pin<KBox<T>>
T: WorkItem<ID, Pointer = Self>,
T: HasDelayedWork<T, ID>,
{
+ type EnqueueDelayedOutput = ();
+
+ unsafe fn __enqueue_delayed<F>(self, queue_work_on: F) -> Self::EnqueueDelayedOutput
+ where
+ F: FnOnce(*mut bindings::work_struct) -> bool,
+ {
+ unsafe { self.__enqueue(queue_work_on) }
+ }
}
/// Returns the system work queue (`system_wq`).
--
2.52.0
On Tue, Feb 03, 2026 at 01:55:55PM -0800, Hamza Mahfooz wrote: > This feature is required for our Hyper-V VMBus reimplementation effort. > As it provides the ability to modify the running state of work items at > runtime. This is great, BUT we need to see the user of this new code at the same time before adding it to the kernel tree. Why not just submit this as part of your patch series for the rust hyperv stuff? thanks, greg k-h
On Wed, Feb 04, 2026 at 06:24:18AM +0100, Greg KH wrote: > On Tue, Feb 03, 2026 at 01:55:55PM -0800, Hamza Mahfooz wrote: > > This feature is required for our Hyper-V VMBus reimplementation effort. > > As it provides the ability to modify the running state of work items at > > runtime. > > This is great, BUT we need to see the user of this new code at the same > time before adding it to the kernel tree. Why not just submit this as > part of your patch series for the rust hyperv stuff? I don't have the rest fully fleshed out quite yet. So, mostly just working on getting the prerequistes in a good state. I can mark it as an RFC if that is preferable. > > thanks, > > greg k-h
On Fri, Feb 06, 2026 at 01:07:07PM -0800, Hamza Mahfooz wrote: > On Wed, Feb 04, 2026 at 06:24:18AM +0100, Greg KH wrote: > > On Tue, Feb 03, 2026 at 01:55:55PM -0800, Hamza Mahfooz wrote: > > > This feature is required for our Hyper-V VMBus reimplementation effort. > > > As it provides the ability to modify the running state of work items at > > > runtime. > > > > This is great, BUT we need to see the user of this new code at the same > > time before adding it to the kernel tree. Why not just submit this as > > part of your patch series for the rust hyperv stuff? > > I don't have the rest fully fleshed out quite yet. So, mostly just > working on getting the prerequistes in a good state. I can mark it as an > RFC if that is preferable. Just send it all when you feel it is ready, no need to ask us to review an api when you don't even have working code that uses it for you to determine if it works or not :) thanks, greg k-h
Hi Hamza,
kernel test robot noticed the following build warnings:
[auto build test WARNING on rust/rust-next]
[also build test WARNING on next-20260203]
[cannot apply to linus/master v6.16-rc1]
[If your patch is applied to the wrong git tree, kindly drop us a note.
And when submitting patch, we suggest to use '--base' as documented in
https://git-scm.com/docs/git-format-patch#_base_tree_information]
url: https://github.com/intel-lab-lkp/linux/commits/Hamza-Mahfooz/workqueue-rust-add-work-item-runtime-modification-support/20260204-060728
base: https://github.com/Rust-for-Linux/linux rust-next
patch link: https://lore.kernel.org/r/20260203215555.832171-1-hamzamahfooz%40linux.microsoft.com
patch subject: [PATCH] workqueue: rust: add work item runtime modification support
config: x86_64-rhel-9.4-rust (https://download.01.org/0day-ci/archive/20260204/202602040543.4ixOmzHo-lkp@intel.com/config)
compiler: clang version 20.1.8 (https://github.com/llvm/llvm-project 87f0227cb60147a26a1eeb4fb06e3b505e9c7261)
rustc: rustc 1.88.0 (6b00bc388 2025-06-23)
reproduce (this is a W=1 build): (https://download.01.org/0day-ci/archive/20260204/202602040543.4ixOmzHo-lkp@intel.com/reproduce)
If you fix the issue in a separate patch/commit (i.e. not just a new version of
the same patch/commit), kindly add following tags
| Reported-by: kernel test robot <lkp@intel.com>
| Closes: https://lore.kernel.org/oe-kbuild-all/202602040543.4ixOmzHo-lkp@intel.com/
All warnings (new ones prefixed by >>):
>> warning: unsafe function's docs are missing a `# Safety` section
--> rust/kernel/workqueue.rs:443:5
|
443 | / unsafe fn __enqueue_delayed<F>(self, queue_work_on: F) -> Self::EnqueueDelayedOutput
444 | | where
445 | | F: FnOnce(*mut bindings::work_struct) -> bool;
| |______________________________________________________^
|
= help: for further information visit https://rust-lang.github.io/rust-clippy/master/index.html#missing_safety_doc
= note: `-W clippy::missing-safety-doc` implied by `-W clippy::all`
= help: to override `-W clippy::all` add `#[allow(clippy::missing_safety_doc)]`
--
>> warning: unsafe function's docs are missing a `# Safety` section
--> rust/kernel/workqueue.rs:558:5
|
558 | pub unsafe fn raw_enable(this: *const Self) -> bool {
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
= help: for further information visit https://rust-lang.github.io/rust-clippy/master/index.html#missing_safety_doc
--
>> warning: unsafe function's docs are missing a `# Safety` section
--> rust/kernel/workqueue.rs:582:5
|
582 | pub unsafe fn raw_disable_sync(this: *const Self) -> bool {
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
= help: for further information visit https://rust-lang.github.io/rust-clippy/master/index.html#missing_safety_doc
--
>> warning: unsafe function's docs are missing a `# Safety` section
--> rust/kernel/workqueue.rs:588:5
|
588 | pub unsafe fn raw_disable_delayed_sync(this: *const Self) -> bool {
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
= help: for further information visit https://rust-lang.github.io/rust-clippy/master/index.html#missing_safety_doc
--
>> warning: unsafe function's docs are missing a `# Safety` section
--> rust/kernel/workqueue.rs:594:5
|
594 | pub unsafe fn raw_cancel(this: *const Self) -> bool {
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
= help: for further information visit https://rust-lang.github.io/rust-clippy/master/index.html#missing_safety_doc
--
>> warning: unsafe function's docs are missing a `# Safety` section
--> rust/kernel/workqueue.rs:600:5
|
600 | pub unsafe fn raw_cancel_delayed(this: *const Self) -> bool {
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
= help: for further information visit https://rust-lang.github.io/rust-clippy/master/index.html#missing_safety_doc
--
>> warning: unsafe function's docs are missing a `# Safety` section
--> rust/kernel/workqueue.rs:606:5
|
606 | pub unsafe fn raw_cancel_sync(this: *const Self) -> bool {
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
= help: for further information visit https://rust-lang.github.io/rust-clippy/master/index.html#missing_safety_doc
--
>> warning: unsafe function's docs are missing a `# Safety` section
--> rust/kernel/workqueue.rs:612:5
|
612 | pub unsafe fn raw_cancel_delayed_sync(this: *const Self) -> bool {
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
= help: for further information visit https://rust-lang.github.io/rust-clippy/master/index.html#missing_safety_doc
--
>> warning: docs for unsafe trait missing `# Safety` section
--> rust/kernel/workqueue.rs:664:1
|
664 | pub unsafe trait WorkHandle {
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
= help: for further information visit https://rust-lang.github.io/rust-clippy/master/index.html#missing_safety_doc
--
>> warning: unsafe impl missing a safety comment
--> rust/kernel/workqueue.rs:702:1
|
702 | unsafe impl<T, const ID: u64> WorkHandle for ArcWorkHandle<T, ID>
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
= help: consider adding a safety comment on the preceding line
= help: for further information visit https://rust-lang.github.io/rust-clippy/master/index.html#undocumented_unsafe_blocks
--
>> warning: unsafe function's docs are missing a `# Safety` section
--> rust/kernel/workqueue.rs:564:5
|
564 | pub unsafe fn raw_disable(this: *const Self) -> bool {
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
= help: for further information visit https://rust-lang.github.io/rust-clippy/master/index.html#missing_safety_doc
..
--
0-DAY CI Kernel Test Service
https://github.com/intel/lkp-tests/wiki
© 2016 - 2026 Red Hat, Inc.