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 300cc2bfe012..81a68d5062b7 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
Hi Marco,
kernel test robot noticed the following build errors:
[auto build test ERROR on rust/rust-next]
[also build test ERROR on linus/master v6.19-rc8 next-20260203]
[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/Marco-Crivellari/rust-add-system_dfl-around-the-new-system_dfl_wq/20260203-233415
base: https://github.com/Rust-for-Linux/linux rust-next
patch link: https://lore.kernel.org/r/20260203152818.317806-3-marco.crivellari%40suse.com
patch subject: [PATCH v4 2/2] rust: add system_percpu() and per-cpu enqueue functions
config: x86_64-rhel-9.4-rust (https://download.01.org/0day-ci/archive/20260204/202602040208.hNcu9QEl-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/202602040208.hNcu9QEl-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/202602040208.hNcu9QEl-lkp@intel.com/
All error/warnings (new ones prefixed by >>):
PATH=/opt/cross/clang-20/bin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin
INFO PATH=/opt/cross/rustc-1.88.0-bindgen-0.72.1/cargo/bin:/opt/cross/clang-20/bin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin
/usr/bin/timeout -k 100 12h /usr/bin/make KCFLAGS= -fno-crash-diagnostics -Wno-error=return-type -Wreturn-type -funsigned-char -Wundef -falign-functions=64 W=1 --keep-going LLVM=1 -j32 -C source O=/kbuild/obj/consumer/x86_64-rhel-9.4-rust ARCH=x86_64 SHELL=/bin/bash rustfmtcheck
make: Entering directory '/kbuild/src/consumer'
make[1]: Entering directory '/kbuild/obj/consumer/x86_64-rhel-9.4-rust'
>> Diff in rust/kernel/workqueue.rs:316:
// 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,
- )
+ bindings::queue_work_on(cpu_id as ffi::c_int, queue_ptr, work_ptr)
})
}
}
Diff in rust/kernel/workqueue.rs:366:
/// 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
+ 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,
{
>> Diff in rust/kernel/workqueue.rs:316:
// 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,
- )
+ bindings::queue_work_on(cpu_id as ffi::c_int, queue_ptr, work_ptr)
})
}
}
Diff in rust/kernel/workqueue.rs:366:
/// 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
+ 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,
{
make[2]: *** [Makefile:1871: rustfmt] Error 123
make[2]: Target 'rustfmtcheck' not remade because of errors.
make[1]: Leaving directory '/kbuild/obj/consumer/x86_64-rhel-9.4-rust'
make[1]: *** [Makefile:248: __sub-make] Error 2
make[1]: Target 'rustfmtcheck' not remade because of errors.
make: *** [Makefile:248: __sub-make] Error 2
make: Target 'rustfmtcheck' not remade because of errors.
make: Leaving directory '/kbuild/src/consumer'
--
>> warning: unresolved link to `system_percpu_wq`
--> rust/kernel/workqueue.rs:1013:72
|
1013 | /// Note: `system_wq` will be removed in a future release cycle. Use [`system_percpu_wq`] instead.
| ^^^^^^^^^^^^^^^^ no item named `system_percpu_wq` in scope
|
= help: to escape `[` and `]` characters, add '\' before them like `\[` or `\]`
= note: `#[warn(rustdoc::broken_intra_doc_links)]` on by default
--
0-DAY CI Kernel Test Service
https://github.com/intel/lkp-tests/wiki
On Tue, Feb 03, 2026 at 04:28:17PM +0100, Marco Crivellari wrote: > 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> Reviewed-by: Alice Ryhl <aliceryhl@google.com>
© 2016 - 2026 Red Hat, Inc.