Rust for Linux List
 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 2/4] rust: damon: add basic DAMON abstractions
Date: Sun, 27 Sep 2026 15:28:10 +0800	[thread overview]
Message-ID: <20260927072812.2393456-3-lienze@kylinos.cn> (raw)
In-Reply-To: <20260927072812.2393456-1-lienze@kylinos.cn>

Wrap the basic DAMON C API in a new rust/kernel/damon module, exposing
contexts, targets, schemes, and the types that configure them, so Rust
modules can drive DAMON without calling the raw C functions directly.

This initial support will then be used by a subsequent sample module.

Signed-off-by: Enze Li <lienze@kylinos.cn>
---
 rust/kernel/damon.rs | 292 +++++++++++++++++++++++++++++++++++++++++++
 rust/kernel/lib.rs   |   2 +
 2 files changed, 294 insertions(+)
 create mode 100644 rust/kernel/damon.rs

diff --git a/rust/kernel/damon.rs b/rust/kernel/damon.rs
new file mode 100644
index 000000000000..80e6609132e7
--- /dev/null
+++ b/rust/kernel/damon.rs
@@ -0,0 +1,292 @@
+// SPDX-License-Identifier: GPL-2.0
+
+// Copyright (C) 2026 KylinSoft Corporation.
+// Author: Enze Li <lienze@kylinos.cn>
+
+//! DAMON (Data Access MONitor) abstractions.
+//!
+//! C header: [`include/linux/damon.h`](srctree/include/linux/damon.h)
+
+use core::ptr::NonNull;
+
+use kernel::bindings;
+use kernel::error::code::*;
+use kernel::error::Error;
+use kernel::error::Result;
+
+/// Return if DAMON is ready to be used.
+pub fn damon_initialized() -> bool {
+    // SAFETY: It is a simple pure query function.
+    unsafe { bindings::damon_initialized() }
+}
+
+/// DAMON operations.
+pub struct OpsID {
+    ops_id: bindings::damon_ops_id,
+}
+
+impl OpsID {
+    /// Monitoring operations for virtual address spaces.
+    pub const VADDR: Self = Self {
+        ops_id: bindings::damon_ops_id_DAMON_OPS_VADDR,
+    };
+}
+
+/// Represents a monitoring target.
+pub struct Target {
+    target: NonNull<bindings::damon_target>,
+}
+
+impl Target {
+    /// Construct a damon_target struct.
+    pub fn damon_new_target() -> Result<Self> {
+        // SAFETY: damon_new_target returns a valid pointer to new allocated
+        // damon_target or NULL if allocation failure, which will be checked
+        // by the subsequent NonNull::new.
+        let raw = unsafe { bindings::damon_new_target() };
+        let t = NonNull::new(raw).ok_or(ENOMEM)?;
+        Ok(Self { target: t })
+    }
+
+    /// Set the PID of monitoring target.
+    pub fn damon_set_target_pid(&mut self, pid: i32) -> Result {
+        // SAFETY: slef.target is a valid, non-null 'damon_target *' by the
+        // Target invariant, and the '&mut self' borrow keeps it alive for
+        // the duration of the call.  The returned errno is checked below.
+        let ret = unsafe { bindings::damon_set_target_pid(self.target.as_ptr(), pid) };
+        if ret < 0 {
+            Err(Error::from_errno(ret))
+        } else {
+            Ok(())
+        }
+    }
+}
+
+impl Drop for Target {
+    fn drop(&mut self) {
+        let t = self.target.as_ptr();
+
+        // SAFETY: t is a valid, non-null 'damon_target *' by the Target
+        // invariant, and release the pid refcount taken by find_get_pid
+        // with a matching put_pid, then free the target once in Drop.
+        unsafe {
+            if !(*t).pid.is_null() {
+                bindings::put_pid((*t).pid);
+            }
+            bindings::damon_free_target(t)
+        };
+    }
+}
+
+/// Target access pattern of the given scheme.
+pub struct DamosAccessPattern {
+    damos_access_pattern: bindings::damos_access_pattern,
+}
+
+impl DamosAccessPattern {
+    /// Create a new damos_access_pattern.
+    pub const fn new(
+        min_sz_region: usize,
+        max_sz_region: usize,
+        min_nr_accesses: u32,
+        max_nr_accesses: u32,
+        min_age_region: u32,
+        max_age_region: u32,
+    ) -> Self {
+        Self {
+            damos_access_pattern: bindings::damos_access_pattern {
+                min_sz_region,
+                max_sz_region,
+                min_nr_accesses,
+                max_nr_accesses,
+                min_age_region,
+                max_age_region,
+            },
+        }
+    }
+}
+
+/// Controls the aggressiveness of the given scheme.
+pub struct DamosQuota {
+    damos_quota: bindings::damos_quota,
+}
+
+impl DamosQuota {
+    /// Create a zero-initialized damos_quota sturct.
+    pub fn zeroed() -> Self {
+        Self {
+            // SAFETY: All-zero is a valid bit pattern for damos_quota.
+            damos_quota: unsafe { core::mem::zeroed() },
+        }
+    }
+}
+
+/// Controls when a given scheme should be activated.
+pub struct DamosWatermarks {
+    damos_watermarks: bindings::damos_watermarks,
+}
+
+impl DamosWatermarks {
+    /// Create a zero-initialized damos_watermarks sturct.
+    pub fn zeroed() -> Self {
+        Self {
+            // SAFETY: All-zero is a valid bit pattern for damos_watermarks.
+            damos_watermarks: unsafe { core::mem::zeroed() },
+        }
+    }
+}
+
+/// Represents an action of a Data Access Monitoring-based Operation Scheme.
+pub struct DamosAction {
+    /// Reclaim the region.
+    damos_action: bindings::damos_action,
+}
+
+impl DamosAction {
+    /// PAGEOUT action.
+    pub const PAGEOUT: Self = Self {
+        damos_action: bindings::damos_action_DAMOS_PAGEOUT,
+    };
+}
+
+/// Represents a Data Access Monitoring-based Operation Scheme.
+pub struct Damos {
+    damos: NonNull<bindings::damos>,
+}
+
+impl Damos {
+    /// Create new Damos.
+    pub fn new(
+        pattern: &mut DamosAccessPattern,
+        action: DamosAction,
+        apply_interval_us: usize,
+        quota: &mut DamosQuota,
+        wmarks: &mut DamosWatermarks,
+        target_nid: i32,
+    ) -> Result<Self> {
+        // SAFETY: All pointer arguments come from local variables that
+        // stay alive during the call; the rest are just numbers.  The
+        // return value is checked for NULL right after.
+        let ptr = unsafe {
+            bindings::damon_new_scheme(
+                &mut pattern.damos_access_pattern,
+                action.damos_action,
+                apply_interval_us,
+                &mut quota.damos_quota,
+                &mut wmarks.damos_watermarks,
+                target_nid,
+            )
+        };
+        NonNull::new(ptr).map(|p| Self { damos: p }).ok_or(ENOMEM)
+    }
+}
+
+/// DAMON monitoring context.
+pub struct DamonCtx {
+    ctx: NonNull<bindings::damon_ctx>,
+}
+
+impl DamonCtx {
+    /// Create a new DAMON monitoring context.
+    pub fn damon_new_ctx() -> Result<Self> {
+        // SAFETY: damon_new_ctx returns a valid pointer to new allocated
+        // damon_ctx or NULL if allocation failure, which will be checked
+        // by the subsequent NonNull::new.
+        let raw = unsafe { bindings::damon_new_ctx() };
+        let c = NonNull::new(raw).ok_or(ENOMEM)?;
+        Ok(Self { ctx: c })
+    }
+
+    /// Select a monitoring operations to use with the context.
+    pub fn damon_select_ops(&self, id: OpsID) -> Result {
+        // SAFETY: self.ctx was created by a successful damon_new_ctx()
+        // call, so it points to a valid damon_ctx. id.ops_id comes from a
+        // known-good constant, so it is a value the C side understands.
+        let ret = unsafe { bindings::damon_select_ops(self.ctx.as_ptr(), id.ops_id) };
+        if ret < 0 {
+            Err(Error::from_errno(ret))
+        } else {
+            Ok(())
+        }
+    }
+
+    /// Add a new DAMON monitoring target.
+    pub fn damon_add_target(&self, target: Target) {
+        // SAFETY: Both arguments are valid: self.ctx is non-null by
+        // construction, and target.target points to an unique owned
+        // damon_target.  The C function takes ownership of the target
+        // that is why rust slide should forget it.
+        unsafe { bindings::damon_add_target(self.ctx.as_ptr(), target.target.as_ptr()) };
+        core::mem::forget(target);
+    }
+
+    /// Add a scheme to context.
+    pub fn damon_add_scheme(&self, scheme: Damos) {
+        // SAFETY: self.ctx is a valid 'damon_ctx *' and scheme.damos is a
+        // valid 'damos *', both by the type invariants of Ctx and Damos.
+        // damon_add_scheme takes ownership of the scheme, so the rust
+        // side should forget it to avoid a double free.
+        unsafe { bindings::damon_add_scheme(self.ctx.as_ptr(), scheme.damos.as_ptr()) };
+        core::mem::forget(scheme);
+    }
+
+    /// Starts the monitorings for a given group of contexts.
+    pub fn damon_start(&self) -> Result {
+        let mut ctxs = [self.ctx.as_ptr()];
+        // SAFETY: ctxs is a stack-allocated array of length 1 whose only
+        // element is a valid 'damon_ctx *'.  The pointer remains valid
+        // for the duration of the call.  The length argument 1 matches the
+        // array size, and true is a valid bool value for the exclusive
+        // parameter.
+        let ret = unsafe { bindings::damon_start(ctxs.as_mut_ptr(), 1, true) };
+        if ret < 0 {
+            Err(Error::from_errno(ret))
+        } else {
+            Ok(())
+        }
+    }
+
+    /// Transfers ownership of the underlying damon_ctx to the caller.
+    /// After this call, the DamonCtx is consumed and will not call
+    /// damon_destroy_ctx on drop.  The caller becomes responsible for
+    /// eventually calling damon_destroy_ctx.
+    pub fn into_raw(self) -> *mut bindings::damon_ctx {
+        let ptr = self.ctx.as_ptr();
+        core::mem::forget(self);
+        ptr
+    }
+
+    /// Reconstructs a DamonCtx from a raw pointer returned by into_raw.
+    /// Note that the ptr must be the non-null pointer obtained from
+    /// into_raw(), and returned once -- the caller must not use ptr
+    /// afterwards, and no other owner may remain.
+    pub unsafe fn from_raw(ptr: *mut bindings::damon_ctx) -> Self {
+        // SAFETY: ptr is a valid, non-null 'damon_ctx *'.
+        Self {
+            ctx: unsafe { NonNull::new_unchecked(ptr) },
+        }
+    }
+
+    /// Stops the monitorings for a given group of contexts.
+    fn damon_stop(&self, nr_ctxs: i32) {
+        let mut ctxs = [self.ctx.as_ptr()];
+        // SAFETY: ctxs is a stack buffer whose element is a valid
+        // 'damon_ctx *' by the DamonCtx invariant.
+        unsafe { bindings::damon_stop(ctxs.as_mut_ptr(), nr_ctxs) };
+    }
+
+    /// Destroys the contexts and frees all associated resources.
+    fn damon_destroy_ctx(&self) {
+        // SAFETY: self.ctx is a valid, non-null 'damon_ctx *' by the
+        // DamonCtx invariant, and it is only destroyed once from drop()
+        // that execute after damon_stop().
+        unsafe { bindings::damon_destroy_ctx(self.ctx.as_ptr()) };
+    }
+}
+
+impl Drop for DamonCtx {
+    fn drop(&mut self) {
+        self.damon_stop(1);
+        self.damon_destroy_ctx();
+    }
+}
diff --git a/rust/kernel/lib.rs b/rust/kernel/lib.rs
index 4d5c96ddc49c..7e9e748cd0b9 100644
--- a/rust/kernel/lib.rs
+++ b/rust/kernel/lib.rs
@@ -62,6 +62,8 @@
 pub mod cpufreq;
 pub mod cpumask;
 pub mod cred;
+#[cfg(CONFIG_DAMON)]
+pub mod damon;
 pub mod debugfs;
 pub mod device;
 pub mod device_id;
-- 
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 ` Enze Li [this message]
2026-09-27  7:28 ` [RFC PATCH 3/4] samples/damon: add Rust sample for DAMON access-aware proactive reclamation Enze Li
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-3-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