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


Reply via email to