Linux-mm Archive on lore.kernel.org
 help / color / mirror / Atom feed
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



  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