[PATCH RFC v3 7/8] selftests/kcov_dataflow: add rust_kworker_remote selftest module

1 view
Skip to first unread message

Yunseong Kim

unread,
Sep 7, 2026, 3:56:06 AM (3 days ago) Sep 7
to linux-...@vger.kernel.org, kasa...@googlegroups.com, linu...@kvack.org, linux-...@vger.kernel.org, rust-fo...@vger.kernel.org, ll...@lists.linux.dev, work...@vger.kernel.org, linu...@vger.kernel.org, linux-k...@vger.kernel.org, Ingo Molnar, Peter Zijlstra, Juri Lelli, Vincent Guittot, Dietmar Eggemann, Steven Rostedt, Ben Segall, Mel Gorman, Valentin Schneider, K Prateek Nayak, Andrew Morton, David Hildenbrand, Lorenzo Stoakes, Liam R. Howlett, Vlastimil Babka, Mike Rapoport, Suren Baghdasaryan, Michal Hocko, Kees Cook, Nathan Chancellor, Nicolas Schier, Josh Poimboeuf, Miguel Ojeda, Boqun Feng, Gary Guo, Björn Roy Baron, Benno Lossin, Andreas Hindborg, Alice Ryhl, Trevor Gross, Danilo Krummrich, Daniel Almeida, Tamir Duberstein, Alexandre Courbot, Onur Özkan, Nick Desaulniers, Bill Wendling, Marco Elver, Nikita Popov, Matt Arsenault, Justin Stitt, Jonathan Corbet, Shuah Khan, Randy Dunlap, Shuah Khan, Yeoreum Yun, Yunseong Kim, Yunseong Kim
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 <yunseo...@est.tech>
---
.../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

Reply all
Reply to author
Forward
0 new messages