From: David Rheinsberg <david@readahead.eu>
To: rust-for-linux@vger.kernel.org
Cc: teg@jklm.no, Miguel Ojeda <ojeda@kernel.org>,
David Rheinsberg <david@readahead.eu>
Subject: [RFC 02/16] rust/sync: add Arc::drop_unless_unique()
Date: Tue, 31 Mar 2026 21:02:54 +0200 [thread overview]
Message-ID: <20260331190308.141622-3-david@readahead.eu> (raw)
In-Reply-To: <20260331190308.141622-1-david@readahead.eu>
Introduce `Arc::drop_unless_unique()`, a rust version of
`refcount_dec_not_one()`.
It is very common to store an object with `struct kref` (or
`refcount_t`) in a locked lookup-tree, but without the tree actually
owning a reference. Instead, whenever the object is destroyed, it
should be removed from the lookup tree.
The problem is, this requires taking the lock whenever releasing a
reference. Otherwise, a racing lookup might see a ref-count of 0. But
this is needlessly expensive.
One solution provided by `struct kref` is `kref_put_mutex()`. This drops
a reference, unless it is the last reference. In that case it takes a
mutex and tries again. If it no longer is the last reference, it simply
unlocks and returns. But if it dropped the last reference, it calls the
destructor with the lock held.
This entire technique is built around `refcount_dec_not_one()`. Expose
this exact feature in Rust, so it can be used to get the same effect as
`kref_put_mutex()`.
Signed-off-by: David Rheinsberg <david@readahead.eu>
---
rust/kernel/sync/arc.rs | 21 +++++++++++++++++++++
rust/kernel/sync/refcount.rs | 16 ++++++++++++++++
2 files changed, 37 insertions(+)
diff --git a/rust/kernel/sync/arc.rs b/rust/kernel/sync/arc.rs
index 921e19333b89..ead021a0dd5d 100644
--- a/rust/kernel/sync/arc.rs
+++ b/rust/kernel/sync/arc.rs
@@ -368,6 +368,27 @@ pub fn into_unique_or_drop(this: Self) -> Option<Pin<UniqueArc<T>>> {
None
}
}
+
+ /// Drop this [`Arc`] unless it is the last reference.
+ ///
+ /// When this is the last reference to the `Arc`, it is returned as `Some`
+ /// unmodified. Otherwise, the `Arc` is dropped and [`None`] is returned.
+ ///
+ /// This function will never release the last reference to the object, and
+ /// as such never call its destructor.
+ pub fn drop_unless_unique(this: Self) -> Option<Self> {
+ let this = ManuallyDrop::new(this);
+
+ // SAFETY: We own a refcount, so the pointer is still valid.
+ if unsafe { this.ptr.as_ref() }.refcount.dec_not_one() {
+ // A single ref-count was dropped, but it was not the last. We
+ // manually drop `this` without invoking `Drop`.
+ None
+ } else {
+ // This was a no-op, return the reference to the caller.
+ Some(ManuallyDrop::into_inner(this))
+ }
+ }
}
// SAFETY: The pointer returned by `into_foreign` was originally allocated as an
diff --git a/rust/kernel/sync/refcount.rs b/rust/kernel/sync/refcount.rs
index 6c7ae8b05a0b..2a65b3e3f961 100644
--- a/rust/kernel/sync/refcount.rs
+++ b/rust/kernel/sync/refcount.rs
@@ -105,6 +105,22 @@ pub fn dec_and_test(&self) -> bool {
// SAFETY: `self.as_ptr()` is valid.
unsafe { bindings::refcount_dec_and_test(self.as_ptr()) }
}
+
+ /// Decrement a refcount if it is not 1.
+ ///
+ /// It will `WARN` on underflow and fail to decrement when saturated.
+ ///
+ /// Provides release memory ordering when succeeding in decrementing the
+ /// refcount.
+ ///
+ /// Returns true if the decrement operation succeeded, false if the
+ /// decrement operation was skipped as the refcount is 1.
+ #[inline]
+ #[must_use = "refcount release is conditional and must be checked"]
+ pub fn dec_not_one(&self) -> bool {
+ // SAFETY: `self.as_ptr()` is valid.
+ unsafe { bindings::refcount_dec_not_one(self.as_ptr()) }
+ }
}
// SAFETY: `refcount_t` is thread-safe.
--
2.53.0
next prev parent reply other threads:[~2026-03-31 19:05 UTC|newest]
Thread overview: 33+ messages / expand[flat|nested] mbox.gz Atom feed top
2026-03-31 19:02 [RFC 00/16] bus1: Capability-based IPC for Linux David Rheinsberg
2026-03-31 19:02 ` [RFC 01/16] rust/sync: add LockedBy::access_mut_unchecked() David Rheinsberg
2026-03-31 19:29 ` Miguel Ojeda
2026-03-31 19:02 ` David Rheinsberg [this message]
2026-03-31 19:02 ` [RFC 03/16] rust/alloc: add Vec::into_boxed_slice() David Rheinsberg
2026-03-31 19:28 ` Miguel Ojeda
2026-03-31 21:10 ` Gary Guo
2026-03-31 22:07 ` Danilo Krummrich
2026-04-01 9:28 ` David Rheinsberg
2026-03-31 19:02 ` [RFC 04/16] rust/error: add EXFULL, EBADRQC, EDQUOT, ENOTRECOVERABLE David Rheinsberg
2026-03-31 19:02 ` [RFC 05/16] bus1: add module scaffolding David Rheinsberg
2026-03-31 19:02 ` [RFC 06/16] bus1: add the user-space API David Rheinsberg
2026-03-31 19:02 ` [RFC 07/16] bus1: add man-page David Rheinsberg
2026-04-01 16:30 ` Jonathan Corbet
2026-04-01 18:01 ` David Rheinsberg
2026-04-01 18:06 ` David Rheinsberg
2026-04-04 15:30 ` Thomas Meyer
2026-03-31 19:03 ` [RFC 08/16] bus1/util: add basic utilities David Rheinsberg
2026-03-31 19:35 ` Miguel Ojeda
2026-04-01 11:05 ` David Rheinsberg
2026-04-01 11:25 ` Miguel Ojeda
2026-03-31 19:03 ` [RFC 09/16] bus1/util: add field projections David Rheinsberg
2026-03-31 19:38 ` Miguel Ojeda
2026-03-31 19:03 ` [RFC 10/16] bus1/util: add IntoDeref/FromDeref David Rheinsberg
2026-03-31 19:44 ` Miguel Ojeda
2026-03-31 19:03 ` [RFC 11/16] bus1/util: add intrusive data-type helpers David Rheinsberg
2026-03-31 19:03 ` [RFC 12/16] bus1/util: add intrusive single linked lists David Rheinsberg
2026-03-31 19:03 ` [RFC 13/16] bus1/util: add intrusive rb-tree David Rheinsberg
2026-03-31 19:43 ` Miguel Ojeda
2026-03-31 19:03 ` [RFC 14/16] bus1/acct: add resouce accounting David Rheinsberg
2026-03-31 19:03 ` [RFC 15/16] bus1: introduce peers, handles, and nodes David Rheinsberg
2026-03-31 19:03 ` [RFC 16/16] bus1: implement the uapi David Rheinsberg
2026-03-31 19:46 ` [RFC 00/16] bus1: Capability-based IPC for Linux Miguel Ojeda
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=20260331190308.141622-3-david@readahead.eu \
--to=david@readahead.eu \
--cc=ojeda@kernel.org \
--cc=rust-for-linux@vger.kernel.org \
--cc=teg@jklm.no \
/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