Messages in this thread Patch in this message |  | | From | Marco Crivellari <> | | Subject | [PATCH v5 2/2] rust: add system_percpu() and per-cpu enqueue functions | | Date | Wed, 4 Feb 2026 16:50:37 +0100 |
| |
The C code defines 2 new workqueues: system_percpu_wq and system_dfl_wq, respectively the futures replacement for system_wq and system_unbound_wq.
This change introduce system_percpu(), that use the new system_percpu_wq.
In order to enqueue on a specific CPU, two new functions have been added to the Queue implementation:
enqueue_cpu() - that receive a u32 CPU id as 2nd argument enqueue_delayed_cpu() - that receive a u32 CPU id as 3rd argument
system_wq (and so workqueue::system()) will be removed in a future release cycle and should not be used.
Suggested-by: Tejun Heo <tj@kernel.org> Signed-off-by: Marco Crivellari <marco.crivellari@suse.com> --- rust/kernel/sync/completion.rs | 2 +- rust/kernel/workqueue.rs | 96 +++++++++++++++++++++++++++++++--- 2 files changed, 91 insertions(+), 7 deletions(-)
diff --git a/rust/kernel/sync/completion.rs b/rust/kernel/sync/completion.rs index c50012a940a3..ee6aa8f18507 100644 --- a/rust/kernel/sync/completion.rs +++ b/rust/kernel/sync/completion.rs @@ -38,7 +38,7 @@ /// done <- Completion::new(), /// }), GFP_KERNEL)?; /// -/// let _ = workqueue::system().enqueue(this.clone()); +/// let _ = workqueue::system_percpu().enqueue(this.clone()); /// /// Ok(this) /// } diff --git a/rust/kernel/workqueue.rs b/rust/kernel/workqueue.rs index adbecdb2b3a9..fd28fc74d5b7 100644 --- a/rust/kernel/workqueue.rs +++ b/rust/kernel/workqueue.rs @@ -67,7 +67,7 @@ //! /// This method will enqueue the struct for execution on the system workqueue, where its value //! /// will be printed. //! fn print_later(val: Arc<MyStruct>) { -//! let _ = workqueue::system().enqueue(val); +//! let _ = workqueue::system_percpu().enqueue(val); //! } //! # print_later(MyStruct::new(42).unwrap()); //! ``` @@ -121,11 +121,11 @@ //! } //! //! fn print_1_later(val: Arc<MyStruct>) { -//! let _ = workqueue::system().enqueue::<Arc<MyStruct>, 1>(val); +//! let _ = workqueue::system_percpu().enqueue::<Arc<MyStruct>, 1>(val); //! } //! //! fn print_2_later(val: Arc<MyStruct>) { -//! let _ = workqueue::system().enqueue::<Arc<MyStruct>, 2>(val); +//! let _ = workqueue::system_percpu().enqueue::<Arc<MyStruct>, 2>(val); //! } //! # print_1_later(MyStruct::new(24, 25).unwrap()); //! # print_2_later(MyStruct::new(41, 42).unwrap()); @@ -171,13 +171,13 @@ //! /// This method will enqueue the struct for execution on the system workqueue, where its value //! /// will be printed 12 jiffies later. //! fn print_later(val: Arc<MyStruct>) { -//! let _ = workqueue::system().enqueue_delayed(val, 12); +//! let _ = workqueue::system_percpu().enqueue_delayed(val, 12); //! } //! //! /// It is also possible to use the ordinary `enqueue` method together with `DelayedWork`. This //! /// is equivalent to calling `enqueue_delayed` with a delay of zero. //! fn print_now(val: Arc<MyStruct>) { -//! let _ = workqueue::system().enqueue(val); +//! let _ = workqueue::system_percpu().enqueue(val); //! } //! # print_later(MyStruct::new(42).unwrap()); //! # print_now(MyStruct::new(42).unwrap()); @@ -292,6 +292,39 @@ pub fn enqueue<W, const ID: u64>(&self, w: W) -> W::EnqueueOutput } } + /// Enqueues a work item on a specific CPU. + /// + /// This may fail if the work item is already enqueued in a workqueue. + /// + /// The work item will be submitted on cpu_id. + pub fn enqueue_cpu<W, const ID: u64>(&self, w: W, cpu_id: u32) -> W::EnqueueOutput + where + W: RawWorkItem<ID> + Send + 'static, + { + let queue_ptr = self.0.get(); + + // SAFETY: We only return `false` if the `work_struct` is already in a workqueue. The other + // `__enqueue` requirements are not relevant since `W` is `Send` and static. + // + // The call to `bindings::queue_work_on` will dereference the provided raw pointer, which + // is ok because `__enqueue` guarantees that the pointer is valid for the duration of this + // closure. + // + // Furthermore, if the C workqueue code accesses the pointer after this call to + // `__enqueue`, then the work item was successfully enqueued, and `bindings::queue_work_on` + // will have returned true. In this case, `__enqueue` 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| { + bindings::queue_work_on( + cpu_id as ffi::c_int, + queue_ptr, + work_ptr, + ) + }) + } + } + /// Enqueues a delayed work item. /// /// This may fail if the work item is already enqueued in a workqueue. @@ -328,6 +361,42 @@ pub fn enqueue_delayed<W, const ID: u64>(&self, w: W, delay: Jiffies) -> W::Enqu } } + /// Enqueues a delayed work item on a specific CPU. + /// + /// This may fail if the work item is already enqueued in a workqueue. + /// + /// The work item will be submitted on cpu_id. + pub fn enqueue_delayed_cpu<W, const ID: u64>(&self, w: W, delay: Jiffies, cpu_id: u32) -> W::EnqueueOutput + where + W: RawDelayedWorkItem<ID> + Send + 'static, + { + let queue_ptr = self.0.get(); + + // SAFETY: We only return `false` if the `work_struct` is already in a workqueue. The other + // `__enqueue` requirements are not relevant since `W` is `Send` and static. + // + // The call to `bindings::queue_delayed_work_on` will dereference the provided raw pointer, + // which is ok because `__enqueue` guarantees that the pointer is valid for the duration of + // this closure, and the safety requirements of `RawDelayedWorkItem` expands this + // requirement to apply to the entire `delayed_work`. + // + // Furthermore, if the C workqueue code accesses the pointer after this call to + // `__enqueue`, then the work item was successfully enqueued, and + // `bindings::queue_delayed_work_on` will have returned true. In this case, `__enqueue` + // 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| { + bindings::queue_delayed_work_on( + cpu_id as ffi::c_int, + queue_ptr, + container_of!(work_ptr, bindings::delayed_work, work), + delay, + ) + }) + } + } + /// Tries to spawn the given function or closure as a work item. /// /// This method can fail because it allocates memory to store the work item. @@ -934,17 +1003,32 @@ unsafe impl<T, const ID: u64> RawDelayedWorkItem<ID> for Pin<KBox<T>> { } -/// Returns the system work queue (`system_wq`). +/// Returns the per-cpu system work queue (`system_wq`). /// /// It is the one used by `schedule[_delayed]_work[_on]()`. Multi-CPU multi-threaded. There are /// users which expect relatively short queue flush time. /// /// Callers shouldn't queue work items which can run for too long. +/// +/// Note: `system_wq` will be removed in a future release cycle. Use `system_percpu_wq` instead. pub fn system() -> &'static Queue { // SAFETY: `system_wq` is a C global, always available. unsafe { Queue::from_raw(bindings::system_wq) } } +/// Returns the per-cpu system work queue (`system_percpu_wq`). +/// +/// It is the one used by `schedule[_delayed]_work[_on]()`. Multi-CPU multi-threaded. There are +/// users which expect relatively short queue flush time. +/// +/// Callers shouldn't queue work items which can run for too long. +/// +/// Note: `system_percpu_wq` will replace `system_wq` in a future release cycle. +pub fn system_percpu() -> &'static Queue { + // SAFETY: `system_percpu_wq` is a C global, always available. + unsafe { Queue::from_raw(bindings::system_percpu_wq) } +} + /// Returns the system high-priority work queue (`system_highpri_wq`). /// /// It is similar to the one returned by [`system`] but for work items which require higher -- 2.52.0
|  |