Add a Rust module that exercises remote dataflow collection: work a task queues to a kworker, bracketed with kcov_df_remote_start(handle) / kcov_df_remote_stop(), is attributed back to the buffer user space published for that handle with KCOV_DF_REMOTE_ENABLE.
A CompositeStore of three RBTrees (standing in for separate lookup tables) is driven through three phases on system_wq: populate: fill all three trees update: insert new entries, read existing ones, overwrite some drain: remove every entry via a cursor The work item calls kcov_df_remote_start(1) on entry and kcov_df_remote_stop() before completing; kselftest script publishe handle 1 (KCOV_SUBSYSTEM_COMMON, instance 1) with KCOV_DF_REMOTE_ENABLE, triggers the kworker through a debugfs file, waits for completion, and checks that records from all three CompositeStore phases were captured from the kworker rather than the triggering task. Needs CONFIG_RUST. Assisted-by: Claude:claude-opus-4-6 [kiro-chat] Signed-off-by: Yunseong Kim <[email protected]> --- .../kcov_dataflow/rust_kworker_remote/Makefile | 3 + .../kcov_dataflow/rust_kworker_remote/README.rst | 13 ++ .../rust_kworker_remote/rust_kworker_remote.rs | 207 +++++++++++++++++++++ 3 files changed, 223 insertions(+) diff --git a/tools/testing/selftests/kcov_dataflow/rust_kworker_remote/Makefile b/tools/testing/selftests/kcov_dataflow/rust_kworker_remote/Makefile new file mode 100644 index 0000000000000..cb7392a50b1a9 --- /dev/null +++ b/tools/testing/selftests/kcov_dataflow/rust_kworker_remote/Makefile @@ -0,0 +1,3 @@ +# SPDX-License-Identifier: GPL-2.0 +obj-m := rust_kworker_remote.o +KCOV_DATAFLOW_rust_kworker_remote.o := y diff --git a/tools/testing/selftests/kcov_dataflow/rust_kworker_remote/README.rst b/tools/testing/selftests/kcov_dataflow/rust_kworker_remote/README.rst new file mode 100644 index 0000000000000..aff597ab67aea --- /dev/null +++ b/tools/testing/selftests/kcov_dataflow/rust_kworker_remote/README.rst @@ -0,0 +1,13 @@ +.. SPDX-License-Identifier: GPL-2.0 + +KCOV-Dataflow Selftests: rust_kworker_remote +============================================ + +Rust module testing kcov_df_remote_start()/kcov_df_remote_stop() from +kworker context: the trigger queues a work item on system_wq whose three +phases (populate/update/drain of a CompositeStore of RBTrees) run with +remote capture on handle 1, which the runner publishes with +KCOV_DF_REMOTE_ENABLE. Built only with CONFIG_RUST=y:: + + ./test_modules.py -t rust_kworker_remote + ./trigger-view.py rust_kworker_remote --remote diff --git a/tools/testing/selftests/kcov_dataflow/rust_kworker_remote/rust_kworker_remote.rs b/tools/testing/selftests/kcov_dataflow/rust_kworker_remote/rust_kworker_remote.rs new file mode 100644 index 0000000000000..65c5722c383cc --- /dev/null +++ b/tools/testing/selftests/kcov_dataflow/rust_kworker_remote/rust_kworker_remote.rs @@ -0,0 +1,207 @@ +// SPDX-License-Identifier: GPL-2.0 +//! Test kcov_df_remote_start/stop from kworker context. +//! +//! A composite struct holds three RBTrees (simulating RBTree/XArray/maple_tree +//! workloads). Three work phases run on system_wq: +//! Phase 1 (populate): fill all three trees +//! Phase 2 (update): insert new values, read existing, overwrite +//! Phase 3 (drain): remove all entries +//! +//! User space publishes a buffer with KCOV_DF_REMOTE_ENABLE, writes to +//! /sys/kernel/debug/kcov_dataflow_test/trigger_kworker_remote, then reads +//! the captured records. + +#![allow(missing_docs)] + +use kernel::prelude::*; +use kernel::sync::{Arc, Completion}; +use kernel::workqueue::{self, impl_has_work, new_work, Work, WorkItem}; +use kernel::rbtree::RBTree; +use kernel::c_str; + +module! { + type: RustKworkerRemote, + name: "rust_kworker_remote", + authors: ["kcov-dataflow"], + description: "Test kcov_df_remote capturing from kworker (RBTree composite)", + license: "GPL", +} + +// Extern bindings for kcov_dataflow remote API (kernel/kcov_dataflow.c) +unsafe extern "C" { + fn kcov_df_remote_start(handle: u64); + fn kcov_df_remote_stop(); +} + +/// Composite data structure: three trees with different key ranges. +/// Simulates a real driver managing multiple lookup tables. +struct CompositeStore { + /// Primary index (keys 0..N) + primary: RBTree<u64, u64>, + /// Secondary/auxiliary index (keys 100..N) + aux: RBTree<u64, u64>, + /// Scratch/temp space (keys 200..N) + scratch: RBTree<u64, u64>, +} + +impl CompositeStore { + fn new() -> Self { + Self { + primary: RBTree::new(), + aux: RBTree::new(), + scratch: RBTree::new(), + } + } + + /// Phase 1: populate all three trees with initial data. + #[inline(never)] + fn populate(&mut self) -> Result { + for i in 0u64..8 { + self.primary.try_create_and_insert(i, i * 0x1111, GFP_KERNEL)?; + } + for i in 100u64..108 { + self.aux.try_create_and_insert(i, i * 0x2222, GFP_KERNEL)?; + } + for i in 200u64..208 { + self.scratch.try_create_and_insert(i, i * 0x3333, GFP_KERNEL)?; + } + Ok(()) + } + + /// Phase 2: insert more, read existing, overwrite some. + #[inline(never)] + fn update(&mut self) -> Result { + // Insert new entries into primary + for i in 8u64..12 { + self.primary.try_create_and_insert(i, i * 0x4444, GFP_KERNEL)?; + } + // Read from aux (get passes &K which is a struct arg) + for i in 100u64..108 { + let _ = self.aux.get(&i); + } + // Overwrite scratch entries + for i in 200u64..204 { + self.scratch.remove(&i); + self.scratch.try_create_and_insert(i, i * 0x5555, GFP_KERNEL)?; + } + Ok(()) + } + + /// Phase 3: drain all trees. + #[inline(never)] + fn drain(&mut self) { + while let Some(c) = self.primary.cursor_front_mut() { + c.remove_current(); + } + while let Some(c) = self.aux.cursor_front_mut() { + c.remove_current(); + } + while let Some(c) = self.scratch.cursor_front_mut() { + c.remove_current(); + } + } +} + +/// Work item that runs three phases in kworker context with remote capture. +#[pin_data] +struct RemoteWork { + #[pin] + work: Work<RemoteWork>, + #[pin] + done: Completion, +} + +impl_has_work! { + impl HasWork<Self> for RemoteWork { self.work } +} + +impl RemoteWork { + fn new() -> Result<Arc<Self>> { + Arc::pin_init(pin_init!(RemoteWork { + work <- new_work!("RemoteWork::work"), + done <- Completion::new(), + }), GFP_KERNEL) + } +} + +impl WorkItem for RemoteWork { + type Pointer = Arc<RemoteWork>; + + fn run(this: Arc<RemoteWork>) { + // Enable remote kcov_dataflow capture for this kworker task. + // SAFETY: FFI call to exported kernel symbol; no-op if no buffer published. + // Handle 1 matches what trigger-view.py passes via KCOV_DF_REMOTE_ENABLE. + unsafe { kcov_df_remote_start(1) }; + + let mut store = CompositeStore::new(); + let _ = store.populate(); + let _ = store.update(); + store.drain(); + + // SAFETY: FFI call to exported kernel symbol; disables capture. + unsafe { kcov_df_remote_stop() }; + + this.done.complete_all(); + } +} + +// --- Debugfs trigger (same raw pattern as eight_struct_args_rust) --- + +unsafe extern "C" fn write_handler( + _file: *mut kernel::bindings::file, + _buf: *const core::ffi::c_char, + count: usize, + _ppos: *mut kernel::bindings::loff_t, +) -> kernel::ffi::c_long { + let work = match RemoteWork::new() { + Ok(w) => w, + Err(_) => return -(kernel::bindings::ENOMEM as kernel::ffi::c_long), + }; + let waiter = work.clone(); + let _ = workqueue::system().enqueue(work); + waiter.done.wait_for_completion(); + count as kernel::ffi::c_long +} + +#[repr(transparent)] +struct SyncFops(kernel::bindings::file_operations); +unsafe impl Sync for SyncFops {} + +static FOPS: SyncFops = SyncFops(kernel::bindings::file_operations { + write: Some(unsafe { core::mem::transmute(write_handler as *const ()) }), + ..unsafe { core::mem::zeroed() } +}); + +struct RustKworkerRemote { + dir: *mut kernel::bindings::dentry, +} + +impl kernel::Module for RustKworkerRemote { + fn init(_module: &'static ThisModule) -> Result<Self> { + let dir = unsafe { + kernel::bindings::debugfs_create_dir( + c_str!("kcov_dataflow_test").as_char_ptr(), + core::ptr::null_mut(), + ) + }; + unsafe { + kernel::bindings::debugfs_create_file_unsafe( + c_str!("trigger_kworker_remote").as_char_ptr(), + 0o222, + dir, + core::ptr::null_mut(), + &FOPS.0, + ) + }; + Ok(Self { dir }) + } +} + +impl Drop for RustKworkerRemote { + fn drop(&mut self) { + unsafe { kernel::bindings::debugfs_remove(self.dir) }; + } +} + +unsafe impl Send for RustKworkerRemote {} +unsafe impl Sync for RustKworkerRemote {} -- 2.47.3

