From: "Onur Özkan" <work@onurozkan.dev>
To: Aakash Bollineni via B4 Relay
<devnull+aakash.bollineni.multicorewareinc.com@kernel.org>
Cc: aakash.bollineni@multicorewareinc.com,
rust-for-linux@vger.kernel.org, linux-kernel@vger.kernel.org
Subject: Re: [PATCH 3/3] rust: workqueue: add KUnit and sample stress tests
Date: Thu, 2 Apr 2026 14:34:12 +0300 [thread overview]
Message-ID: <20260402143412.2d7cc919@nimda> (raw)
In-Reply-To: <20260402-rust-next-v1-3-0940bb8f201c@multicorewareinc.com>
On Thu, 02 Apr 2026 08:53:48 +0530
Aakash Bollineni via B4 Relay
<devnull+aakash.bollineni.multicorewareinc.com@kernel.org> wrote:
> From: Aakash Bollineni <aakash.bollineni@multicorewareinc.com>
>
> To ensure the safety and correctness of the improved workqueue API,
> this patch adds comprehensive testing infrastructure:
> 1. KUnit Tests: Adds an internal 'rust_kernel_workqueue' test suite
> to rust/kernel/workqueue.rs. These tests verify basic and
> synchronous cancellation, refcount stability, and concurrency
> safety for both standard and delayed work.
> 2. Sample Module: Adds samples/rust/rust_workqueue_test.rs as a
> standalone module that performs a 1000-iteration stress test
> designed to verify race-free pointer handover during concurrent
> enqueue/cancel operations.
> The tests use distinct types for standard and delayed work items to
> ensure clear trait dispatch and prevent field offset conflicts.
>
This seems like the opposite what you did with the TestItem type? It
contains both the standard and delayed work inside.
> Signed-off-by: Aakash Bollineni
> <aakash.bollineni@multicorewareinc.com> ---
> samples/rust/Makefile | 2 +
> samples/rust/rust_workqueue_test.rs | 214
> ++++++++++++++++++++++++++++++++++++ 2 files changed, 216
> insertions(+)
>
> diff --git a/samples/rust/Makefile b/samples/rust/Makefile
> index 6c0aaa58cccc..0f304bd90997 100644
> --- a/samples/rust/Makefile
> +++ b/samples/rust/Makefile
> @@ -20,3 +20,5 @@ obj-$(CONFIG_SAMPLE_RUST_SOC)
> += rust_soc.o rust_print-y := rust_print_main.o
> rust_print_events.o
> subdir-$(CONFIG_SAMPLE_RUST_HOSTPROGS) += hostprogs
> +
> +obj-y += rust_workqueue_test.o
> diff --git a/samples/rust/rust_workqueue_test.rs
> b/samples/rust/rust_workqueue_test.rs new file mode 100644
> index 000000000000..c055ec1964c0
> --- /dev/null
> +++ b/samples/rust/rust_workqueue_test.rs
> @@ -0,0 +1,214 @@
> +// SPDX-License-Identifier: GPL-2.0
> +
> +//! Robust stress test for Rust workqueue API.
> +
> +use kernel::prelude::*;
> +use kernel::sync::Arc;
> +use kernel::time::msecs_to_jiffies;
> +use kernel::workqueue::{self, new_work, Work, WorkItem};
> +
> +#[pin_data]
> +struct TestItem {
> + #[pin]
> + work: Work<TestItem>,
> + value: i32,
> + #[pin]
> + delayed_work: workqueue::DelayedWork<TestItem>,
> +}
> +
> +kernel::impl_has_work! {
> + impl HasWork<Self> for TestItem { self.work }
> +}
> +
> +// SAFETY: The `delayed_work` field is at a fixed offset and is
> valid for the lifetime of +// `TestItem`.
> +unsafe impl workqueue::HasDelayedWork<Self> for TestItem {}
> +
> +impl WorkItem for TestItem {
> + type Pointer = Arc<TestItem>;
> +
> + fn run(this: Arc<TestItem>) {
> + pr_info!(
> + "Rust workqueue test: Work item running (value: {})\n",
> + this.value
> + );
> + }
> +}
> +
> +/// Helper to get Arc strong count for verification in tests.
> +/// This uses internal layout knowledge of Arc.
> +fn get_arc_count<T: Sized>(arc: &Arc<T>) -> i32 {
> + // SAFETY: ArcInner has refcount as its first field. Arc points
> to data at DATA_OFFSET.
> + unsafe {
> + let ptr = Arc::as_ptr(arc);
> + let inner_ptr = (ptr as *const
> u8).sub(Arc::<T>::DATA_OFFSET);
> + // The first field of ArcInner is Refcount, which is a
> transparent wrapper around
> + // refcount_t. In the kernel, refcount_t is an atomic_t
> (i32).
> + let refcount_ptr = inner_ptr as *const i32;
> + // We use a relaxed load to get the current value.
> + core::ptr::read_volatile(refcount_ptr)
> + }
> +}
> +
> +struct RustWorkqueueTest;
> +
> +impl kernel::Module for RustWorkqueueTest {
> + fn init(_module: &'static ThisModule) -> Result<Self> {
> + pr_info!("Rust workqueue test: starting robust
> verification\n"); +
> + // 1. Basic Lifecycle with Refcount Validation
> + {
> + let test_item = Arc::pin_init(
> + pin_init!(TestItem {
> + work <- new_work!("TestItem::work"),
> + value: 42,
> + delayed_work <-
> workqueue::new_delayed_work!("TestItem::delayed_work"),
> + }),
> + GFP_KERNEL,
> + )?;
> +
> + let initial_count = get_arc_count(&test_item);
> + pr_info!("Initial Arc strong count: {}\n",
> initial_count); +
> + // Enqueue
> + let enqueued_item = test_item.clone();
> +
> + if let Err(returned_item) =
> workqueue::system().enqueue(enqueued_item) {
> + pr_warn!("Work already pending, unexpected!\n");
> + let _ = returned_item;
> + } else {
> + pr_info!(
> + "Work enqueued successfully. Strong count: {}\n",
> + get_arc_count(&test_item)
> + );
> + }
> +
> + // Cancel immediately (best effort)
> + if let Some(reclaimed) = test_item.work.cancel() {
> + let count_after_cancel = get_arc_count(&test_item);
> + pr_info!(
> + "Success: Work cancelled and Arc reclaimed.
> Strong count: {}\n",
> + count_after_cancel
> + );
> +
> + // VALIDATION: Reclamation must restore the refcount
> (minus the clone we just
> + // reclaimed)
> + if count_after_cancel != initial_count + 1 {
> + pr_err!(
> + "ERROR: Refcount mismatch after cancel!
> Expected {}, got {}\n",
> + initial_count + 1,
> + count_after_cancel
> + );
> + return Err(ENXIO);
> + }
> + drop(reclaimed);
> + if get_arc_count(&test_item) != initial_count {
> + pr_err!(
> + "ERROR: Refcount mismatch after drop!
> Expected {}, got {}\n",
> + initial_count,
> + get_arc_count(&test_item)
> + );
> + return Err(ENXIO);
> + }
> + } else {
> + pr_info!("Work already running or finished, could
> not reclaim via cancel().\n");
> + }
> + }
> +
> + // 2. Stress Testing: Enqueue/Cancel Sync Loop
> + {
> + pr_info!("Starting stress test (1000 iterations)...\n");
> + let test_item = Arc::pin_init(
> + pin_init!(TestItem {
> + work <- new_work!("TestItem::work"),
> + value: 99,
> + delayed_work <-
> workqueue::new_delayed_work!("TestItem::delayed_work"),
> + }),
> + GFP_KERNEL,
> + )?;
> +
> + for i in 0..1000 {
> + let _ =
> workqueue::system().enqueue(test_item.clone());
> + // Use cancel_sync to ensure determinism for the
> next iteration
> + let _ = test_item.work.cancel_sync();
> +
> + if i % 250 == 0 {
> + pr_info!("Stress test progress: {}/1000\n", i);
> + }
> + }
> +
> + if get_arc_count(&test_item) != 1 {
> + pr_err!(
> + "ERROR: Refcount leak detected after stress
> test! count: {}\n",
> + get_arc_count(&test_item)
> + );
> + return Err(ENXIO);
> + } else {
> + pr_info!("Stress test completed successfully. No
> refcount leaks.\n");
> + }
> + }
> +
> + // 3. Delayed Work Cancellation Test
> + {
> + let test_item = Arc::pin_init(
> + pin_init!(TestItem {
> + work <- new_work!("TestItem::work"),
> + value: 7,
> + delayed_work <-
> workqueue::new_delayed_work!("TestItem::delayed_work"),
> + }),
> + GFP_KERNEL,
> + )?;
> +
> + let initial_count = get_arc_count(&test_item);
> +
> + // Schedule with a long delay
> + let to_enqueue = test_item.clone();
> + let res = workqueue::system()
> + .enqueue_delayed(to_enqueue, msecs_to_jiffies(5000));
> + if let Err(returned) = res {
> + pr_warn!("Delayed work already pending, returned
> item.\n");
> + drop(returned);
> + } else {
> + pr_info!("Delayed work enqueued. count: {}\n",
> get_arc_count(&test_item));
> + }
> +
> + if test_item.delayed_work.is_pending() {
> + pr_info!("Delayed work is pending as expected.\n");
> + }
> +
> + if let Some(reclaimed) = test_item.delayed_work.cancel()
> {
> + pr_info!("Success: Delayed work reclaimed. No
> leak.\n");
> + drop(reclaimed);
> + } else {
> + pr_warn!("Notice: Delayed work not reclaimed
> (running or never enqueued).\n");
> + }
> +
> + let final_count = get_arc_count(&test_item);
> + if final_count != initial_count {
> + pr_err!(
> + "ERROR: Refcount leak after delayed cancel!
> expected {}, got {}\n",
> + initial_count,
> + final_count
> + );
> + return Err(ENXIO);
> + }
> + }
> +
> + pr_info!("Rust workqueue test: all robust checks passed\n");
> + Ok(RustWorkqueueTest)
> + }
> +}
> +
> +impl Drop for RustWorkqueueTest {
> + fn drop(&mut self) {
> + pr_info!("Rust workqueue test: exit\n");
> + }
> +}
> +
> +module! {
> + type: RustWorkqueueTest,
> + name: "rust_workqueue_test",
> + authors: ["Aakash Bollineni"],
> + description: "Robust stress test for Rust workqueue API",
> + license: "GPL",
> +}
>
next prev parent reply other threads:[~2026-04-02 11:34 UTC|newest]
Thread overview: 7+ messages / expand[flat|nested] mbox.gz Atom feed top
2026-04-02 3:23 [PATCH 0/3] rust: workqueue: add safe cancellation and status methods Aakash Bollineni via B4 Relay
2026-04-02 3:23 ` [PATCH 1/3] rust: helpers: add workqueue helpers Aakash Bollineni via B4 Relay
2026-04-02 3:23 ` [PATCH 2/3] rust: workqueue: add safe cancellation and status methods Aakash Bollineni via B4 Relay
2026-04-02 3:23 ` [PATCH 3/3] rust: workqueue: add KUnit and sample stress tests Aakash Bollineni via B4 Relay
2026-04-02 11:23 ` Onur Özkan
2026-04-02 11:34 ` Onur Özkan [this message]
2026-04-02 11:19 ` [PATCH 0/3] rust: workqueue: add safe cancellation and status methods Onur Özkan
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=20260402143412.2d7cc919@nimda \
--to=work@onurozkan.dev \
--cc=aakash.bollineni@multicorewareinc.com \
--cc=devnull+aakash.bollineni.multicorewareinc.com@kernel.org \
--cc=linux-kernel@vger.kernel.org \
--cc=rust-for-linux@vger.kernel.org \
/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 a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox