From: Enze Li <lienze@kylinos.cn>
To: ojeda@kernel.org, boqun@kernel.org, gary@garyguo.net,
bjorn3_gh@protonmail.com, lossin@kernel.org,
a.hindborg@kernel.org, aliceryhl@google.com, tmgross@umich.edu,
dakr@kernel.org, daniel.almeida@collabora.com, tamird@kernel.org,
acourbot@nvidia.com, work@onurozkan.dev, sj@kernel.org
Cc: linux-kernel@vger.kernel.org, rust-for-linux@vger.kernel.org,
damon@lists.linux.dev, linux-mm@kvack.org, enze.li@gmx.com,
lienze@kylinos.cn
Subject: [RFC PATCH 3/4] samples/damon: add Rust sample for DAMON access-aware proactive reclamation
Date: Sun, 27 Sep 2026 15:28:11 +0800 [thread overview]
Message-ID: <20260927072812.2393456-4-lienze@kylinos.cn> (raw)
In-Reply-To: <20260927072812.2393456-1-lienze@kylinos.cn>
Add a Rust implementation of the DAMON-based proactive reclamation sample
module, porting the logic from prcl.c to demonstrate using Rust bindings
for DAMON C APIs.
The module monitors the virtual address space of a target process via DAMON
vaddr operations and proactively reclaims cold memory regions using the
DAMOS_PAGEOUT action with an access pattern filter.
Unlike the C version (prcl.c), which is configured through module
parameters, the Rust version provides debugfs-based runtime control under
/sys/kernel/debug/rust_prcl/. Monitoring can be enabled or disabled by
writing to the enabled file, while the target process to monitor is
selected by writing its PID to target_pid.
Signed-off-by: Enze Li <lienze@kylinos.cn>
---
samples/Makefile | 1 +
samples/damon/Kconfig | 12 +++
samples/damon/Makefile | 1 +
samples/damon/rust_prcl.rs | 158 +++++++++++++++++++++++++++++++++++++
4 files changed, 172 insertions(+)
create mode 100644 samples/damon/rust_prcl.rs
diff --git a/samples/Makefile b/samples/Makefile
index 07641e177bd8..df726c0653cd 100644
--- a/samples/Makefile
+++ b/samples/Makefile
@@ -43,5 +43,6 @@ obj-$(CONFIG_SAMPLES_RUST) += rust/
obj-$(CONFIG_SAMPLE_DAMON_WSSE) += damon/
obj-$(CONFIG_SAMPLE_DAMON_PRCL) += damon/
obj-$(CONFIG_SAMPLE_DAMON_MTIER) += damon/
+obj-$(CONFIG_SAMPLE_DAMON_RUST_PRCL) += damon/
obj-$(CONFIG_SAMPLE_HUNG_TASK) += hung_task/
obj-$(CONFIG_SAMPLE_TSM_MR) += tsm-mr/
diff --git a/samples/damon/Kconfig b/samples/damon/Kconfig
index 00be3e6bdd65..654b62e186c9 100644
--- a/samples/damon/Kconfig
+++ b/samples/damon/Kconfig
@@ -40,4 +40,16 @@ config SAMPLE_DAMON_MTIER
If unsure, say N.
+config SAMPLE_DAMON_RUST_PRCL
+ bool "Rust sample module for DAMON-based access-aware proactive reclamation"
+ depends on DAMON && DAMON_VADDR && RUST && DEBUG_FS
+ help
+ This option builds the Rust implementation of DAMON-based prcl sample.
+
+ The module exposes two debugfs interfaces under
+ /sys/kernel/debug/rust_prcl/ for starting/stopping monitoring
+ and setting the target PID.
+
+ If unsure, say N.
+
endmenu
diff --git a/samples/damon/Makefile b/samples/damon/Makefile
index 72f68cbf422a..2e3ed8d9dd5d 100644
--- a/samples/damon/Makefile
+++ b/samples/damon/Makefile
@@ -3,3 +3,4 @@
obj-$(CONFIG_SAMPLE_DAMON_WSSE) += wsse.o
obj-$(CONFIG_SAMPLE_DAMON_PRCL) += prcl.o
obj-$(CONFIG_SAMPLE_DAMON_MTIER) += mtier.o
+obj-$(CONFIG_SAMPLE_DAMON_RUST_PRCL) += rust_prcl.o
diff --git a/samples/damon/rust_prcl.rs b/samples/damon/rust_prcl.rs
new file mode 100644
index 000000000000..1e6c820311e2
--- /dev/null
+++ b/samples/damon/rust_prcl.rs
@@ -0,0 +1,158 @@
+// SPDX-License-Identifier: GPL-2.0
+
+//! Rust prcl sample.
+
+use core::ptr::null_mut;
+use kernel::bindings;
+use kernel::damon;
+use kernel::debugfs::Scope;
+use kernel::prelude::*;
+use kernel::sync::atomic::{Atomic, Relaxed};
+use kernel::uaccess::UserSliceReader;
+
+module! {
+ type: RustPrcl,
+ name: "rust_prcl",
+ authors: ["Enze Li <lienze@kylinos.cn>"],
+ description: "DAMON-based proactive reclamation module for rust edition",
+ license: "GPL",
+}
+
+struct RustPrcl {
+ _data: Pin<KBox<Scope<ModuleData>>>,
+}
+
+struct ModuleData {
+ enabled: Atomic<bool>,
+ target_pid: Atomic<i32>,
+}
+
+impl Drop for ModuleData {
+ fn drop(&mut self) {
+ if self.enabled.load(Relaxed) {
+ damon_sample_rust_prcl_stop();
+ }
+ }
+}
+
+static CTX: Atomic<*mut bindings::damon_ctx> = Atomic::new(null_mut());
+
+fn damon_sample_rust_prcl_start(target_pid: i32) -> Result {
+ let ctx = damon::DamonCtx::damon_new_ctx()?;
+ ctx.damon_select_ops(damon::OpsID::VADDR)?;
+
+ let mut target = damon::Target::damon_new_target()?;
+ target.damon_set_target_pid(target_pid)?;
+ ctx.damon_add_target(target);
+
+ let mut pattern =
+ damon::DamosAccessPattern::new(bindings::PAGE_SIZE, usize::MAX, 0, 0, 50, u32::MAX);
+
+ let mut quota = damon::DamosQuota::zeroed();
+ let mut watermarks = damon::DamosWatermarks::zeroed();
+ let scheme = damon::Damos::new(
+ &mut pattern,
+ damon::DamosAction::PAGEOUT,
+ 0,
+ &mut quota,
+ &mut watermarks,
+ bindings::NUMA_NO_NODE,
+ )?;
+
+ ctx.damon_add_scheme(scheme);
+
+ let ret = ctx.damon_start();
+ match ret {
+ Ok(_) => {
+ CTX.store(ctx.into_raw(), Relaxed);
+ }
+ Err(e) => {
+ pr_err!("damon_start failed, error: {:?}\n", e);
+ return Err(e);
+ }
+ }
+ Ok(())
+}
+
+fn damon_sample_rust_prcl_stop() {
+ pr_info!("stop\n");
+ let raw = CTX.xchg(null_mut(), Relaxed);
+ if !raw.is_null() {
+ // SAFETY: raw came from into_raw() at damon_start(), and xchg()
+ // makes its sole remaining owner, so moving it back once is safe.
+ let _ctx = unsafe { damon::DamonCtx::from_raw(raw) };
+ // _ctx drops at the end of this scope, it runs damon_stop(1) and
+ // damon_destroy_ctx().
+ }
+}
+
+fn damon_sample_rust_parse_bool(s: &str) -> Result<bool> {
+ let s = s.trim();
+ if s.eq_ignore_ascii_case("y") {
+ Ok(true)
+ } else if s.eq_ignore_ascii_case("n") {
+ Ok(false)
+ } else {
+ Err(EINVAL)
+ }
+}
+
+fn enabled_read(data: &ModuleData, f: &mut kernel::fmt::Formatter<'_>) -> kernel::fmt::Result {
+ writeln!(f, "{}", if data.enabled.load(Relaxed) { 'Y' } else { 'N' })
+}
+
+fn enabled_write(data: &ModuleData, reader: &mut UserSliceReader) -> Result {
+ let mut buf = [0u8; 32];
+ if reader.len() > buf.len() {
+ return Err(EINVAL);
+ }
+ let n = reader.len();
+ reader.read_slice(&mut buf[..n])?;
+
+ let s = core::str::from_utf8(&buf[..n]).map_err(|_| EINVAL)?;
+ let enabled = damon_sample_rust_parse_bool(s)?;
+
+ let is_enabled = data.enabled.load(Relaxed);
+ if enabled == is_enabled {
+ return Ok(());
+ }
+
+ if !damon::damon_initialized() {
+ return Ok(());
+ }
+
+ data.enabled.store(enabled, Relaxed);
+
+ if enabled {
+ let target_pid = data.target_pid.load(Relaxed);
+ if let Err(e) = damon_sample_rust_prcl_start(target_pid) {
+ data.enabled.store(false, Relaxed);
+ return Err(e);
+ }
+ } else {
+ damon_sample_rust_prcl_stop();
+ }
+
+ Ok(())
+}
+
+impl kernel::Module for RustPrcl {
+ fn init(_module: &'static kernel::ThisModule) -> Result<Self> {
+ let data = KBox::pin_init(
+ Scope::dir(
+ ModuleData {
+ enabled: Atomic::new(false),
+ target_pid: Atomic::new(0),
+ },
+ c"rust_prcl",
+ |data, dir| {
+ dir.read_write_callback_file(c"enabled", data, &enabled_read, &enabled_write);
+ dir.read_write_file(c"target_pid", &data.target_pid);
+ },
+ ),
+ GFP_KERNEL,
+ )?;
+
+ Ok(Self { _data: data })
+ }
+}
--
2.43.0
next prev parent reply other threads:[~2026-09-27 7:28 UTC|newest]
Thread overview: 8+ messages / expand[flat|nested] mbox.gz Atom feed top
2026-09-27 7:28 [RFC PATCH 0/4] rust: damon: a first small step, plus a Rust prcl sample Enze Li
2026-09-27 7:28 ` [RFC PATCH 1/4] rust: add bindings for linux/damon.h Enze Li
2026-09-27 7:28 ` [RFC PATCH 2/4] rust: damon: add basic DAMON abstractions Enze Li
2026-09-27 7:28 ` Enze Li [this message]
2026-09-27 7:28 ` [RFC PATCH 4/4] MAINTAINERS: add entry for the DAMON Rust abstractions Enze Li
2026-09-27 10:07 ` [RFC PATCH 0/4] rust: damon: a first small step, plus a Rust prcl sample SJ Park
2026-09-28 12:59 ` Enze Li
2026-09-28 16:56 ` SJ Park
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=20260927072812.2393456-4-lienze@kylinos.cn \
--to=lienze@kylinos.cn \
--cc=a.hindborg@kernel.org \
--cc=acourbot@nvidia.com \
--cc=aliceryhl@google.com \
--cc=bjorn3_gh@protonmail.com \
--cc=boqun@kernel.org \
--cc=dakr@kernel.org \
--cc=damon@lists.linux.dev \
--cc=daniel.almeida@collabora.com \
--cc=enze.li@gmx.com \
--cc=gary@garyguo.net \
--cc=linux-kernel@vger.kernel.org \
--cc=linux-mm@kvack.org \
--cc=lossin@kernel.org \
--cc=ojeda@kernel.org \
--cc=rust-for-linux@vger.kernel.org \
--cc=sj@kernel.org \
--cc=tamird@kernel.org \
--cc=tmgross@umich.edu \
--cc=work@onurozkan.dev \
/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