rust: id_pool: do not immediately acquire new ids

When Rust Binder assigns a new ID, it performs various fallible
operations before it "commits" to actually using the new ID. To support
this pattern, change acquire_next_id() so that it does not immediately
call set_bit(), but instead returns an object that may be used to call
set_bit() later.

The UnusedId type holds a exclusive reference to the IdPool, so it's
guaranteed that nobody else can call find_unused_id() while the UnusedId
object is live.

[Miguel: rust: id_pool: fix example]

Reviewed-by: Burak Emir <bqe@google.com>
Reviewed-by: Danilo Krummrich <dakr@kernel.org>
Signed-off-by: Alice Ryhl <aliceryhl@google.com>
Signed-off-by: Yury Norov (NVIDIA) <yury.norov@gmail.com>
This commit is contained in:
Alice Ryhl
2025-11-25 13:59:41 +00:00
committed by Yury Norov (NVIDIA)
parent 69ec6a1bed
commit f523d110a6

View File

@@ -23,22 +23,22 @@ use crate::bitmap::BitmapVec;
/// Basic usage /// Basic usage
/// ///
/// ``` /// ```
/// use kernel::alloc::{AllocError, flags::GFP_KERNEL}; /// use kernel::alloc::AllocError;
/// use kernel::id_pool::IdPool; /// use kernel::id_pool::{IdPool, UnusedId};
/// ///
/// let mut pool = IdPool::with_capacity(64, GFP_KERNEL)?; /// let mut pool = IdPool::with_capacity(64, GFP_KERNEL)?;
/// for i in 0..64 { /// for i in 0..64 {
/// assert_eq!(i, pool.acquire_next_id(i).ok_or(ENOSPC)?); /// assert_eq!(i, pool.find_unused_id(i).ok_or(ENOSPC)?.acquire());
/// } /// }
/// ///
/// pool.release_id(23); /// pool.release_id(23);
/// assert_eq!(23, pool.acquire_next_id(0).ok_or(ENOSPC)?); /// assert_eq!(23, pool.find_unused_id(0).ok_or(ENOSPC)?.acquire());
/// ///
/// assert_eq!(None, pool.acquire_next_id(0)); // time to realloc. /// assert!(pool.find_unused_id(0).is_none()); // time to realloc.
/// let resizer = pool.grow_request().ok_or(ENOSPC)?.realloc(GFP_KERNEL)?; /// let resizer = pool.grow_request().ok_or(ENOSPC)?.realloc(GFP_KERNEL)?;
/// pool.grow(resizer); /// pool.grow(resizer);
/// ///
/// assert_eq!(pool.acquire_next_id(0), Some(64)); /// assert_eq!(pool.find_unused_id(0).ok_or(ENOSPC)?.acquire(), 64);
/// # Ok::<(), Error>(()) /// # Ok::<(), Error>(())
/// ``` /// ```
/// ///
@@ -52,8 +52,8 @@ use crate::bitmap::BitmapVec;
/// fn get_id_maybe_realloc(guarded_pool: &SpinLock<IdPool>) -> Result<usize, AllocError> { /// fn get_id_maybe_realloc(guarded_pool: &SpinLock<IdPool>) -> Result<usize, AllocError> {
/// let mut pool = guarded_pool.lock(); /// let mut pool = guarded_pool.lock();
/// loop { /// loop {
/// match pool.acquire_next_id(0) { /// match pool.find_unused_id(0) {
/// Some(index) => return Ok(index), /// Some(index) => return Ok(index.acquire()),
/// None => { /// None => {
/// let alloc_request = pool.grow_request(); /// let alloc_request = pool.grow_request();
/// drop(pool); /// drop(pool);
@@ -221,18 +221,18 @@ impl IdPool {
self.map = resizer.new; self.map = resizer.new;
} }
/// Acquires a new ID by finding and setting the next zero bit in the /// Finds an unused ID in the bitmap.
/// bitmap.
/// ///
/// Upon success, returns its index. Otherwise, returns [`None`] /// Upon success, returns its index. Otherwise, returns [`None`]
/// to indicate that a [`Self::grow_request`] is needed. /// to indicate that a [`Self::grow_request`] is needed.
#[inline] #[inline]
pub fn acquire_next_id(&mut self, offset: usize) -> Option<usize> { #[must_use]
let next_zero_bit = self.map.next_zero_bit(offset); pub fn find_unused_id(&mut self, offset: usize) -> Option<UnusedId<'_>> {
if let Some(nr) = next_zero_bit { // INVARIANT: `next_zero_bit()` returns None or an integer less than `map.len()`
self.map.set_bit(nr); Some(UnusedId {
} id: self.map.next_zero_bit(offset)?,
next_zero_bit pool: self,
})
} }
/// Releases an ID. /// Releases an ID.
@@ -242,6 +242,51 @@ impl IdPool {
} }
} }
/// Represents an unused id in an [`IdPool`].
///
/// # Invariants
///
/// The value of `id` is less than `pool.map.len()`.
pub struct UnusedId<'pool> {
id: usize,
pool: &'pool mut IdPool,
}
impl<'pool> UnusedId<'pool> {
/// Get the unused id as an usize.
///
/// Be aware that the id has not yet been acquired in the pool. The
/// [`acquire`] method must be called to prevent others from taking the id.
///
/// [`acquire`]: UnusedId::acquire()
#[inline]
#[must_use]
pub fn as_usize(&self) -> usize {
self.id
}
/// Get the unused id as an u32.
///
/// Be aware that the id has not yet been acquired in the pool. The
/// [`acquire`] method must be called to prevent others from taking the id.
///
/// [`acquire`]: UnusedId::acquire()
#[inline]
#[must_use]
pub fn as_u32(&self) -> u32 {
// CAST: By the type invariants:
// `self.id < pool.map.len() <= BitmapVec::MAX_LEN = i32::MAX`.
self.id as u32
}
/// Acquire the unused id.
#[inline]
pub fn acquire(self) -> usize {
self.pool.map.set_bit(self.id);
self.id
}
}
impl Default for IdPool { impl Default for IdPool {
#[inline] #[inline]
fn default() -> Self { fn default() -> Self {