From nobody Sat Feb 7 23:22:51 2026 Received: from linux.microsoft.com (linux.microsoft.com [13.77.154.182]) by smtp.subspace.kernel.org (Postfix) with ESMTP id 788AC28DEE9; Tue, 3 Feb 2026 21:55:57 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=13.77.154.182 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1770155759; cv=none; b=FEaSXYqkaLhnZ+iFk3I6dt/skRvgsU6HBHSu2mYIYWyYOMMqDmgI0S/3UYKRTMOLMGXY1daK5pcg6arftrcVEU8T9IsQ8z1UjYFWCfZPv+FJ5Mub+X8DYkZpg76n8nawlPPgnub1gHCK/ZjRzALJr1ckYPfhrxGLNm0uOJYzXyI= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1770155759; c=relaxed/simple; bh=9fL2eXgKIaB4OA+z4Vgc+97K18HEnvlkGb7UrjUvBTU=; h=From:To:Cc:Subject:Date:Message-ID:MIME-Version; b=GRxu9z3kdB2zoD8vy+UAL4fzmYQ0FgmheYl+2YwrYYzSSf/SGshVjc8uCgB3reNLm2ujoLI0FVMvpg7KiUUEBWvXtCfacwrFq0Rc4BPqjskYOzz37xBH51KRCWE45Lgo6zUo5Ys/KJDMcGXLwhwp3Nz1rnwrbR4AUC61dUtQyTQ= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=linux.microsoft.com; spf=pass smtp.mailfrom=linux.microsoft.com; dkim=pass (1024-bit key) header.d=linux.microsoft.com header.i=@linux.microsoft.com header.b=UsbYNRFn; arc=none smtp.client-ip=13.77.154.182 Authentication-Results: smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=linux.microsoft.com Authentication-Results: smtp.subspace.kernel.org; spf=pass smtp.mailfrom=linux.microsoft.com Authentication-Results: smtp.subspace.kernel.org; dkim=pass (1024-bit key) header.d=linux.microsoft.com header.i=@linux.microsoft.com header.b="UsbYNRFn" Received: by linux.microsoft.com (Postfix, from userid 1216) id 777B120B7167; Tue, 3 Feb 2026 13:55:57 -0800 (PST) DKIM-Filter: OpenDKIM Filter v2.11.0 linux.microsoft.com 777B120B7167 DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=linux.microsoft.com; s=default; t=1770155757; bh=pC6lHroD7JyCDC87pwnE68DPXivF823eGIXzd8kq0uc=; h=From:To:Cc:Subject:Date:From; b=UsbYNRFnJPFYnZsIoRYW235o+kaT8JzznOr0X00RilsHKHwlQVQxAZ1Wi+oRq+3Mn gQeF5D46gCR2jIOElMioxZZNchBV2An5zFsq5It5MRTRhkhCXiFplModBayn3U3AKJ vUTAJTP7V8Ar6NC8BoiuuSxnm6F5Lzlm7hj1GpJY= From: Hamza Mahfooz To: rust-for-linux@vger.kernel.org Cc: Miguel Ojeda , Boqun Feng , Gary Guo , =?UTF-8?q?Bj=C3=B6rn=20Roy=20Baron?= , Benno Lossin , Andreas Hindborg , Alice Ryhl , Trevor Gross , Danilo Krummrich , Tejun Heo , Tamir Duberstein , Alban Kurti , linux-kernel@vger.kernel.org, Hamza Mahfooz Subject: [PATCH] workqueue: rust: add work item runtime modification support Date: Tue, 3 Feb 2026 13:55:55 -0800 Message-ID: <20260203215555.832171-1-hamzamahfooz@linux.microsoft.com> X-Mailer: git-send-email 2.43.7 Precedence: bulk X-Mailing-List: linux-kernel@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 Content-Transfer-Encoding: quoted-printable Content-Type: text/plain; charset="utf-8" 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 --- 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) { //! let _ =3D workqueue::system().enqueue(val); //! } +//! +//! /// The returned WorkHandle can be used to cancel the work item before= it gets executed. +//! fn print_never(val: Arc) { +//! let result =3D workqueue::system_bh().enqueue_delayed(val, 1000); +//! +//! match result { +//! Ok(handle) =3D> { +//! handle.cancel(true); +//! }, +//! Err(_) =3D> (), +//! } +//! } +//! //! # 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/workqueu= e.h) @@ -297,7 +311,7 @@ pub fn enqueue(&self, w: W) -> W::Enq= ueueOutput /// 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(&self, w: W, delay: Jiffies) = -> W::EnqueueOutput + pub fn enqueue_delayed(&self, w: W, delay: Jiffies) = -> W::EnqueueDelayedOutput where W: RawDelayedWorkItem + Send + 'static, { @@ -317,7 +331,7 @@ pub fn enqueue_delayed(&self, w: W, d= elay: 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_in= t, queue_ptr, @@ -421,7 +435,15 @@ unsafe fn __enqueue(self, queue_work_on: F) -> Self= ::EnqueueOutput /// provided pointer must point at the `work` field of a valid `delayed_wo= rk`, 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: RawWorkItem {} +pub unsafe trait RawDelayedWorkItem: RawWorkItem { + /// The return type of [`Queue::enqueue_delayed`]. + type EnqueueDelayedOutput; + + /// See [`RawWorkItem::__enqueue`], this method only differs in return= type. + unsafe fn __enqueue_delayed(self, queue_work_on: F) -> Self::Enqueu= eDelayedOutput + where + F: FnOnce(*mut bindings::work_struct) -> bool; +} =20 /// 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 bindin= gs::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 =3D 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 i= t'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)) } + } } =20 /// Declares that a type contains a [`Work`]. @@ -578,6 +660,122 @@ pub unsafe trait HasWork { unsafe fn work_container_of(ptr: *mut Work) -> *mut Self; } =20 +/// 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>` returned by a call to +/// [`RawWorkItem::__enqueue`] +pub struct ArcWorkHandle +where + T: HasWork, +{ + inner: Arc, +} + +unsafe impl WorkHandle for ArcWorkHandle +where + T: HasWork, +{ + fn enable(self) -> bool { + let self_ptr =3D Arc::as_ptr(&self.inner) as *mut T; + let work_ptr =3D unsafe { >::raw_get_work(self= _ptr) }; + + unsafe { Work::::raw_enable(work_ptr) } + } + + fn disable(self, sync: bool) -> bool { + let self_ptr =3D Arc::as_ptr(&self.inner) as *mut T; + let work_ptr =3D unsafe { >::raw_get_work(self= _ptr) }; + + if sync { + unsafe { Work::::raw_disable_sync(work_ptr) } + } else { + unsafe { Work::::raw_disable(work_ptr) } + } + } + + fn cancel(self, sync: bool) -> bool { + let self_ptr =3D Arc::as_ptr(&self.inner) as *mut T; + let work_ptr =3D unsafe { >::raw_get_work(self= _ptr) }; + + if sync { + unsafe { Work::::raw_cancel_sync(work_ptr) } + } else { + unsafe { Work::::raw_cancel(work_ptr) } + } + } +} + +/// A handle for an `Arc>` returned by a call to +/// [`RawWorkItem::__enqueue_delayed`] +pub struct ArcDelayedWorkHandle +where + T: HasDelayedWork, +{ + inner: Arc, +} + +unsafe impl WorkHandle for ArcDelayedWorkHandle +where + T: HasDelayedWork, +{ + fn enable(self) -> bool { + let self_ptr =3D Arc::as_ptr(&self.inner) as *mut T; + let work_ptr =3D unsafe { >::raw_get_work(self= _ptr) }; + + unsafe { Work::::raw_enable(work_ptr) } + } + + fn disable(self, sync: bool) -> bool { + let self_ptr =3D Arc::as_ptr(&self.inner) as *mut T; + let work_ptr =3D unsafe { >::raw_get_work(self= _ptr) }; + + if sync { + unsafe { Work::::raw_disable_delayed_sync(work_ptr) } + } else { + unsafe { Work::::raw_disable_delayed(work_ptr) } + } + } + + fn cancel(self, sync: bool) -> bool { + let self_ptr =3D Arc::as_ptr(&self.inner) as *mut T; + let work_ptr =3D unsafe { >::raw_get_work(self= _ptr) }; + + if sync { + unsafe { Work::::raw_cancel_delayed_sync(work_ptr) } + } else { + unsafe { Work::::raw_cancel_delayed(work_ptr) } + } + } +} + /// Used to safely implement the [`HasWork`] trait. /// /// # Examples @@ -841,7 +1039,7 @@ unsafe impl RawWorkItem for Arc<= T> T: WorkItem, T: HasWork, { - type EnqueueOutput =3D Result<(), Self>; + type EnqueueOutput =3D Result, Self>; =20 unsafe fn __enqueue(self, queue_work_on: F) -> Self::EnqueueOutput where @@ -856,7 +1054,7 @@ unsafe fn __enqueue(self, queue_work_on: F) -> Self= ::EnqueueOutput let work_ptr =3D unsafe { Work::raw_get(work_ptr) }; =20 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 point= er. Err(unsafe { Arc::from_raw(ptr) }) @@ -872,6 +1070,27 @@ unsafe impl RawDelayedWorkItem = for Arc T: WorkItem, T: HasDelayedWork, { + type EnqueueDelayedOutput =3D Result, Self= >; + + unsafe fn __enqueue_delayed(self, queue_work_on: F) -> Self::Enqueu= eDelayedOutput + where + F: FnOnce(*mut bindings::work_struct) -> bool, + { + // Casting between const and mut is not a problem as long as the p= ointer is a raw pointer. + let ptr =3D Arc::into_raw(self).cast_mut(); + + // SAFETY: Pointers into an `Arc` point at a valid value. + let work_ptr =3D unsafe { T::raw_get_work(ptr) }; + // SAFETY: `raw_get_work` returns a pointer to a valid value. + let work_ptr =3D 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 point= er. + Err(unsafe { Arc::from_raw(ptr) }) + } + } } =20 // SAFETY: TODO. @@ -932,6 +1151,14 @@ unsafe impl RawDelayedWorkItem = for Pin> T: WorkItem, T: HasDelayedWork, { + type EnqueueDelayedOutput =3D (); + + unsafe fn __enqueue_delayed(self, queue_work_on: F) -> Self::Enqueu= eDelayedOutput + where + F: FnOnce(*mut bindings::work_struct) -> bool, + { + unsafe { self.__enqueue(queue_work_on) } + } } =20 /// Returns the system work queue (`system_wq`). --=20 2.52.0