DAMON development mailing list
 help / color / mirror / Atom feed
From: sashiko-bot@kernel.org
To: "Enze Li" <lienze@kylinos.cn>
Cc: ojeda@kernel.org, gary@garyguo.net, damon@lists.linux.dev
Subject: Re: [RFC PATCH 2/4] rust: damon: add basic DAMON abstractions
Date: Sun, 27 Sep 2026 07:40:07 +0000	[thread overview]
Message-ID: <20260927074007.8C75C1F000FF@smtp.kernel.org> (raw)
In-Reply-To: <20260927072812.2393456-3-lienze@kylinos.cn>

Thank you for your contribution! Sashiko AI review found 4 potential issue(s) to consider:
- [High] damon: data race in damon_add_target() and damon_add_scheme() via shared reference
- [High] damon: PID refcount leak in damon_add_target() when cleanup_target is NULL
- [High] damon: memory leak of struct damos due to missing Drop
- [Low] damon: missing #[inline] on C binding wrappers like damon_initialized()
--

commit d3e2e841f98a461b836da0fb19644c82a53dc09d
Author: Enze Li <lienze@kylinos.cn>

rust: damon: add basic DAMON abstractions

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.

diff --git a/rust/kernel/damon.rs b/rust/kernel/damon.rs
new file mode 100644
index 0000000000000..80e6609132e75
--- /dev/null
+++ b/rust/kernel/damon.rs
@@ -0,0 +1,292 @@

[ ... ]

> +/// 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() }
> +}

[Severity: Low]
This isn't a bug, but should this C binding wrapper have an #[inline]
annotation? The Rust subsystem guidelines recommend that small abstraction
functions forwarding to C bindings use #[inline].

[ ... ]

> +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)

[Severity: High]
Does this code leak the allocated struct damos?

If a user calls Damos::new() but returns early (for example, via the ? operator
during error handling) before passing ownership to DamonCtx::damon_add_scheme(),
it appears the memory allocated by damon_new_scheme() is never freed because
the Damos struct does not implement the Drop trait.

> +    }
> +}

[ ... ]

> +    /// 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);

[Severity: High]
Can this data race with the kdamond thread iterating over the same lists
locklessly?

Because damon_add_target() takes a shared &self reference, safe Rust code
can call it concurrently, or after damon_start() spawns the background C kernel
thread. This does not enforce mutual exclusion.

[Severity: High]
Does this code leak the struct pid refcount when cleanup_target is NULL?

Target::damon_set_target_pid() takes a refcount on a struct pid. Here,
core::mem::forget(target) bypasses the Rust Drop handler. If a user drops the
context without successfully selecting operations (like OpsID::VADDR), the C
fallback damon_destroy_ctx() leaves cleanup_target as NULL and put_pid() is
never called, leaking the refcount.

> +    }
> +
> +    /// 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()) };

[Severity: High]
Can this data race with the background thread for the same reasons as
damon_add_target()?

This method also takes a shared &self reference, allowing concurrent mutation
of the context's scheme list while the kthread locklessly accesses it.

> +        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) };

[Severity: High]
Can this allow data races by only requiring a shared reference?

damon_start() spawns the kdamond background thread. Because it takes &self
instead of an exclusive reference or consuming the context, it fails to
statically prevent safe Rust code from calling mutating methods like
damon_add_target() or damon_add_scheme() concurrently.

> +        if ret < 0 {
> +            Err(Error::from_errno(ret))
> +        } else {
> +            Ok(())
> +        }
> +    }

-- 
Sashiko AI review · https://sashiko.dev/#/patchset/20260927072812.2393456-1-lienze@kylinos.cn?part=2

  reply	other threads:[~2026-09-27  7:40 UTC|newest]

Thread overview: 11+ 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:29   ` sashiko-bot
2026-09-27  7:28 ` [RFC PATCH 2/4] rust: damon: add basic DAMON abstractions Enze Li
2026-09-27  7:40   ` sashiko-bot [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:37   ` sashiko-bot
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=20260927074007.8C75C1F000FF@smtp.kernel.org \
    --to=sashiko-bot@kernel.org \
    --cc=damon@lists.linux.dev \
    --cc=gary@garyguo.net \
    --cc=lienze@kylinos.cn \
    --cc=ojeda@kernel.org \
    --cc=sashiko-reviews@lists.linux.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