// SPDX-License-Identifier: GPL-2.012// Copyright (C) 2025 Google LLC.34//! Rust API for an ID pool backed by a [`BitmapVec`].56use crate::alloc::{AllocError, Flags};7use crate::bitmap::BitmapVec;89/// Represents a dynamic ID pool backed by a [`BitmapVec`].10///11/// Clients acquire and release IDs from unset bits in a bitmap.12///13/// The capacity of the ID pool may be adjusted by users as14/// needed. The API supports the scenario where users need precise control15/// over the time of allocation of a new backing bitmap, which may require16/// release of spinlock.17/// Due to concurrent updates, all operations are re-verified to determine18/// if the grow or shrink is sill valid.19///20/// # Examples21///22/// Basic usage23///24/// ```25/// use kernel::alloc::AllocError;26/// use kernel::id_pool::{IdPool, UnusedId};27///28/// let mut pool = IdPool::with_capacity(64, GFP_KERNEL)?;29/// for i in 0..64 {30/// assert_eq!(i, pool.find_unused_id(i).ok_or(ENOSPC)?.acquire());31/// }32///33/// pool.release_id(23);34/// assert_eq!(23, pool.find_unused_id(0).ok_or(ENOSPC)?.acquire());35///36/// assert!(pool.find_unused_id(0).is_none()); // time to realloc.37/// let resizer = pool.grow_request().ok_or(ENOSPC)?.realloc(GFP_KERNEL)?;38/// pool.grow(resizer);39///40/// assert_eq!(pool.find_unused_id(0).ok_or(ENOSPC)?.acquire(), 64);41/// # Ok::<(), Error>(())42/// ```43///44/// Releasing spinlock to grow the pool45///46/// ```no_run47/// use kernel::alloc::{AllocError, flags::GFP_KERNEL};48/// use kernel::sync::{new_spinlock, SpinLock};49/// use kernel::id_pool::IdPool;50///51/// fn get_id_maybe_realloc(guarded_pool: &SpinLock<IdPool>) -> Result<usize, AllocError> {52/// let mut pool = guarded_pool.lock();53/// loop {54/// match pool.find_unused_id(0) {55/// Some(index) => return Ok(index.acquire()),56/// None => {57/// let alloc_request = pool.grow_request();58/// drop(pool);59/// let resizer = alloc_request.ok_or(AllocError)?.realloc(GFP_KERNEL)?;60/// pool = guarded_pool.lock();61/// pool.grow(resizer)62/// }63/// }64/// }65/// }66/// ```67pub struct IdPool {68map: BitmapVec,69}7071/// Indicates that an [`IdPool`] should change to a new target size.72pub struct ReallocRequest {73num_ids: usize,74}7576/// Contains a [`BitmapVec`] of a size suitable for reallocating [`IdPool`].77pub struct PoolResizer {78new: BitmapVec,79}8081impl ReallocRequest {82/// Allocates a new backing [`BitmapVec`] for [`IdPool`].83///84/// This method only prepares reallocation and does not complete it.85/// Reallocation will complete after passing the [`PoolResizer`] to the86/// [`IdPool::grow`] or [`IdPool::shrink`] operation, which will check87/// that reallocation still makes sense.88pub fn realloc(&self, flags: Flags) -> Result<PoolResizer, AllocError> {89let new = BitmapVec::new(self.num_ids, flags)?;90Ok(PoolResizer { new })91}92}9394impl IdPool {95/// Constructs a new [`IdPool`].96///97/// The pool will have a capacity of [`MAX_INLINE_LEN`].98///99/// [`MAX_INLINE_LEN`]: BitmapVec::MAX_INLINE_LEN100#[inline]101pub fn new() -> Self {102Self {103map: BitmapVec::new_inline(),104}105}106107/// Constructs a new [`IdPool`] with space for a specific number of bits.108///109/// A capacity below [`MAX_INLINE_LEN`] is adjusted to [`MAX_INLINE_LEN`].110///111/// [`MAX_INLINE_LEN`]: BitmapVec::MAX_INLINE_LEN112#[inline]113pub fn with_capacity(num_ids: usize, flags: Flags) -> Result<Self, AllocError> {114let num_ids = usize::max(num_ids, BitmapVec::MAX_INLINE_LEN);115let map = BitmapVec::new(num_ids, flags)?;116Ok(Self { map })117}118119/// Returns how many IDs this pool can currently have.120#[inline]121pub fn capacity(&self) -> usize {122self.map.len()123}124125/// Returns a [`ReallocRequest`] if the [`IdPool`] can be shrunk, [`None`] otherwise.126///127/// The capacity of an [`IdPool`] cannot be shrunk below [`MAX_INLINE_LEN`].128///129/// [`MAX_INLINE_LEN`]: BitmapVec::MAX_INLINE_LEN130///131/// # Examples132///133/// ```134/// use kernel::{135/// alloc::AllocError,136/// bitmap::BitmapVec,137/// id_pool::{138/// IdPool,139/// ReallocRequest,140/// },141/// };142///143/// let mut pool = IdPool::with_capacity(1024, GFP_KERNEL)?;144/// let alloc_request = pool.shrink_request().ok_or(AllocError)?;145/// let resizer = alloc_request.realloc(GFP_KERNEL)?;146/// pool.shrink(resizer);147/// assert_eq!(pool.capacity(), BitmapVec::MAX_INLINE_LEN);148/// # Ok::<(), AllocError>(())149/// ```150#[inline]151pub fn shrink_request(&self) -> Option<ReallocRequest> {152let cap = self.capacity();153// Shrinking below `MAX_INLINE_LEN` is never possible.154if cap <= BitmapVec::MAX_INLINE_LEN {155return None;156}157// Determine if the bitmap can shrink based on the position of158// its last set bit. If the bit is within the first quarter of159// the bitmap then shrinking is possible. In this case, the160// bitmap should shrink to half its current size.161let Some(bit) = self.map.last_bit() else {162return Some(ReallocRequest {163num_ids: BitmapVec::MAX_INLINE_LEN,164});165};166if bit >= (cap / 4) {167return None;168}169let num_ids = usize::max(BitmapVec::MAX_INLINE_LEN, cap / 2);170Some(ReallocRequest { num_ids })171}172173/// Shrinks pool by using a new [`BitmapVec`], if still possible.174#[inline]175pub fn shrink(&mut self, mut resizer: PoolResizer) {176// Between request to shrink that led to allocation of `resizer` and now,177// bits may have changed.178// Verify that shrinking is still possible. In case shrinking to179// the size of `resizer` is no longer possible, do nothing,180// drop `resizer` and move on.181let Some(updated) = self.shrink_request() else {182return;183};184if updated.num_ids > resizer.new.len() {185return;186}187188resizer.new.copy_and_extend(&self.map);189self.map = resizer.new;190}191192/// Returns a [`ReallocRequest`] for growing this [`IdPool`], if possible.193///194/// The capacity of an [`IdPool`] cannot be grown above [`MAX_LEN`].195///196/// [`MAX_LEN`]: BitmapVec::MAX_LEN197#[inline]198pub fn grow_request(&self) -> Option<ReallocRequest> {199let num_ids = self.capacity() * 2;200if num_ids > BitmapVec::MAX_LEN {201return None;202}203Some(ReallocRequest { num_ids })204}205206/// Grows pool by using a new [`BitmapVec`], if still necessary.207///208/// The `resizer` arguments has to be obtained by calling [`Self::grow_request`]209/// on this object and performing a [`ReallocRequest::realloc`].210#[inline]211pub fn grow(&mut self, mut resizer: PoolResizer) {212// Between request to grow that led to allocation of `resizer` and now,213// another thread may have already grown the capacity.214// In this case, do nothing, drop `resizer` and move on.215if resizer.new.len() <= self.capacity() {216return;217}218219resizer.new.copy_and_extend(&self.map);220self.map = resizer.new;221}222223/// Finds an unused ID in the bitmap.224///225/// Upon success, returns its index. Otherwise, returns [`None`]226/// to indicate that a [`Self::grow_request`] is needed.227#[inline]228#[must_use]229pub fn find_unused_id(&mut self, offset: usize) -> Option<UnusedId<'_>> {230// INVARIANT: `next_zero_bit()` returns None or an integer less than `map.len()`231Some(UnusedId {232id: self.map.next_zero_bit(offset)?,233pool: self,234})235}236237/// Releases an ID.238#[inline]239pub fn release_id(&mut self, id: usize) {240self.map.clear_bit(id);241}242}243244/// Represents an unused id in an [`IdPool`].245///246/// # Invariants247///248/// The value of `id` is less than `pool.map.len()`.249pub struct UnusedId<'pool> {250id: usize,251pool: &'pool mut IdPool,252}253254impl<'pool> UnusedId<'pool> {255/// Get the unused id as an usize.256///257/// Be aware that the id has not yet been acquired in the pool. The258/// [`acquire`] method must be called to prevent others from taking the id.259///260/// [`acquire`]: UnusedId::acquire()261#[inline]262#[must_use]263pub fn as_usize(&self) -> usize {264self.id265}266267/// Get the unused id as an u32.268///269/// Be aware that the id has not yet been acquired in the pool. The270/// [`acquire`] method must be called to prevent others from taking the id.271///272/// [`acquire`]: UnusedId::acquire()273#[inline]274#[must_use]275pub fn as_u32(&self) -> u32 {276// CAST: By the type invariants:277// `self.id < pool.map.len() <= BitmapVec::MAX_LEN = i32::MAX`.278self.id as u32279}280281/// Acquire the unused id.282#[inline]283pub fn acquire(self) -> usize {284self.pool.map.set_bit(self.id);285self.id286}287}288289impl Default for IdPool {290#[inline]291fn default() -> Self {292Self::new()293}294}295296297