All of lore.kernel.org
 help / color / mirror / Atom feed
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",
> +}
> 


  parent reply	other threads:[~2026-04-02 11:34 UTC|newest]

Thread overview: 13+ 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
2026-04-02  3:23 ` Aakash Bollineni via B4 Relay
2026-04-02  3:23 ` [PATCH 1/3] rust: helpers: add workqueue helpers Aakash Bollineni
2026-04-02  3:23   ` Aakash Bollineni via B4 Relay
2026-04-02  3:23 ` [PATCH 2/3] rust: workqueue: add safe cancellation and status methods Aakash Bollineni
2026-04-02  3:23   ` Aakash Bollineni via B4 Relay
2026-04-02  3:23 ` [PATCH 3/3] rust: workqueue: add KUnit and sample stress tests Aakash Bollineni
2026-04-02  3:23   ` 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
  -- strict thread matches above, loose matches on Subject: below --
2026-04-07 11:06 Aakash Bollineni
2026-04-07 11:06 ` [PATCH 3/3] rust: workqueue: add KUnit and sample stress tests Aakash Bollineni
2026-04-07 11:06   ` Aakash Bollineni via B4 Relay

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 an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.