295 lines · 9.1 KB · Rust
RawDownload
// SPDX-License-Identifier: GPL-2.0 // Copyright (C) 2025 Google LLC. //! Rust API for an ID pool backed by a [`BitmapVec`]. use crate::alloc::{AllocError, Flags};use crate::bitmap::BitmapVec; /// Represents a dynamic ID pool backed by a [`BitmapVec`].////// Clients acquire and release IDs from unset bits in a bitmap.////// The capacity of the ID pool may be adjusted by users as/// needed. The API supports the scenario where users need precise control/// over the time of allocation of a new backing bitmap, which may require/// release of spinlock./// Due to concurrent updates, all operations are re-verified to determine/// if the grow or shrink is sill valid.////// # Examples////// Basic usage////// ```/// use kernel::alloc::AllocError;/// use kernel::id_pool::{IdPool, UnusedId};////// let mut pool = IdPool::with_capacity(64, GFP_KERNEL)?;/// for i in 0..64 {///     assert_eq!(i, pool.find_unused_id(i).ok_or(ENOSPC)?.acquire());/// }////// pool.release_id(23);/// assert_eq!(23, pool.find_unused_id(0).ok_or(ENOSPC)?.acquire());////// assert!(pool.find_unused_id(0).is_none());  // time to realloc./// let resizer = pool.grow_request().ok_or(ENOSPC)?.realloc(GFP_KERNEL)?;/// pool.grow(resizer);////// assert_eq!(pool.find_unused_id(0).ok_or(ENOSPC)?.acquire(), 64);/// # Ok::<(), Error>(())/// ```////// Releasing spinlock to grow the pool////// ```no_run/// use kernel::alloc::{AllocError, flags::GFP_KERNEL};/// use kernel::sync::{new_spinlock, SpinLock};/// use kernel::id_pool::IdPool;////// fn get_id_maybe_realloc(guarded_pool: &SpinLock<IdPool>) -> Result<usize, AllocError> {///     let mut pool = guarded_pool.lock();///     loop {///         match pool.find_unused_id(0) {///             Some(index) => return Ok(index.acquire()),///             None => {///                 let alloc_request = pool.grow_request();///                 drop(pool);///                 let resizer = alloc_request.ok_or(AllocError)?.realloc(GFP_KERNEL)?;///                 pool = guarded_pool.lock();///                 pool.grow(resizer)///             }///         }///     }/// }/// ```pub struct IdPool {    map: BitmapVec,} /// Indicates that an [`IdPool`] should change to a new target size.pub struct ReallocRequest {    num_ids: usize,} /// Contains a [`BitmapVec`] of a size suitable for reallocating [`IdPool`].pub struct PoolResizer {    new: BitmapVec,} impl ReallocRequest {    /// Allocates a new backing [`BitmapVec`] for [`IdPool`].    ///    /// This method only prepares reallocation and does not complete it.    /// Reallocation will complete after passing the [`PoolResizer`] to the    /// [`IdPool::grow`] or [`IdPool::shrink`] operation, which will check    /// that reallocation still makes sense.    pub fn realloc(&self, flags: Flags) -> Result<PoolResizer, AllocError> {        let new = BitmapVec::new(self.num_ids, flags)?;        Ok(PoolResizer { new })    }} impl IdPool {    /// Constructs a new [`IdPool`].    ///    /// The pool will have a capacity of [`MAX_INLINE_LEN`].    ///    /// [`MAX_INLINE_LEN`]: BitmapVec::MAX_INLINE_LEN    #[inline]    pub fn new() -> Self {        Self {            map: BitmapVec::new_inline(),        }    }     /// Constructs a new [`IdPool`] with space for a specific number of bits.    ///    /// A capacity below [`MAX_INLINE_LEN`] is adjusted to [`MAX_INLINE_LEN`].    ///    /// [`MAX_INLINE_LEN`]: BitmapVec::MAX_INLINE_LEN    #[inline]    pub fn with_capacity(num_ids: usize, flags: Flags) -> Result<Self, AllocError> {        let num_ids = usize::max(num_ids, BitmapVec::MAX_INLINE_LEN);        let map = BitmapVec::new(num_ids, flags)?;        Ok(Self { map })    }     /// Returns how many IDs this pool can currently have.    #[inline]    pub fn capacity(&self) -> usize {        self.map.len()    }     /// Returns a [`ReallocRequest`] if the [`IdPool`] can be shrunk, [`None`] otherwise.    ///    /// The capacity of an [`IdPool`] cannot be shrunk below [`MAX_INLINE_LEN`].    ///    /// [`MAX_INLINE_LEN`]: BitmapVec::MAX_INLINE_LEN    ///    /// # Examples    ///    /// ```    /// use kernel::{    ///     alloc::AllocError,    ///     bitmap::BitmapVec,    ///     id_pool::{    ///         IdPool,    ///         ReallocRequest,    ///     },    /// };    ///    /// let mut pool = IdPool::with_capacity(1024, GFP_KERNEL)?;    /// let alloc_request = pool.shrink_request().ok_or(AllocError)?;    /// let resizer = alloc_request.realloc(GFP_KERNEL)?;    /// pool.shrink(resizer);    /// assert_eq!(pool.capacity(), BitmapVec::MAX_INLINE_LEN);    /// # Ok::<(), AllocError>(())    /// ```    #[inline]    pub fn shrink_request(&self) -> Option<ReallocRequest> {        let cap = self.capacity();        // Shrinking below `MAX_INLINE_LEN` is never possible.        if cap <= BitmapVec::MAX_INLINE_LEN {            return None;        }        // Determine if the bitmap can shrink based on the position of        // its last set bit. If the bit is within the first quarter of        // the bitmap then shrinking is possible. In this case, the        // bitmap should shrink to half its current size.        let Some(bit) = self.map.last_bit() else {            return Some(ReallocRequest {                num_ids: BitmapVec::MAX_INLINE_LEN,            });        };        if bit >= (cap / 4) {            return None;        }        let num_ids = usize::max(BitmapVec::MAX_INLINE_LEN, cap / 2);        Some(ReallocRequest { num_ids })    }     /// Shrinks pool by using a new [`BitmapVec`], if still possible.    #[inline]    pub fn shrink(&mut self, mut resizer: PoolResizer) {        // Between request to shrink that led to allocation of `resizer` and now,        // bits may have changed.        // Verify that shrinking is still possible. In case shrinking to        // the size of `resizer` is no longer possible, do nothing,        // drop `resizer` and move on.        let Some(updated) = self.shrink_request() else {            return;        };        if updated.num_ids > resizer.new.len() {            return;        }         resizer.new.copy_and_extend(&self.map);        self.map = resizer.new;    }     /// Returns a [`ReallocRequest`] for growing this [`IdPool`], if possible.    ///    /// The capacity of an [`IdPool`] cannot be grown above [`MAX_LEN`].    ///    /// [`MAX_LEN`]: BitmapVec::MAX_LEN    #[inline]    pub fn grow_request(&self) -> Option<ReallocRequest> {        let num_ids = self.capacity() * 2;        if num_ids > BitmapVec::MAX_LEN {            return None;        }        Some(ReallocRequest { num_ids })    }     /// Grows pool by using a new [`BitmapVec`], if still necessary.    ///    /// The `resizer` arguments has to be obtained by calling [`Self::grow_request`]    /// on this object and performing a [`ReallocRequest::realloc`].    #[inline]    pub fn grow(&mut self, mut resizer: PoolResizer) {        // Between request to grow that led to allocation of `resizer` and now,        // another thread may have already grown the capacity.        // In this case, do nothing, drop `resizer` and move on.        if resizer.new.len() <= self.capacity() {            return;        }         resizer.new.copy_and_extend(&self.map);        self.map = resizer.new;    }     /// Finds an unused ID in the bitmap.    ///    /// Upon success, returns its index. Otherwise, returns [`None`]    /// to indicate that a [`Self::grow_request`] is needed.    #[inline]    #[must_use]    pub fn find_unused_id(&mut self, offset: usize) -> Option<UnusedId<'_>> {        // INVARIANT: `next_zero_bit()` returns None or an integer less than `map.len()`        Some(UnusedId {            id: self.map.next_zero_bit(offset)?,            pool: self,        })    }     /// Releases an ID.    #[inline]    pub fn release_id(&mut self, id: usize) {        self.map.clear_bit(id);    }} /// 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 {    #[inline]    fn default() -> Self {        Self::new()    }}