// 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;89const BITS_PER_LONG: usize = bindings::BITS_PER_LONG as usize;1011/// Represents a dynamic ID pool backed by a [`BitmapVec`].12///13/// Clients acquire and release IDs from unset bits in a bitmap.14///15/// The capacity of the ID pool may be adjusted by users as16/// needed. The API supports the scenario where users need precise control17/// over the time of allocation of a new backing bitmap, which may require18/// release of spinlock.19/// Due to concurrent updates, all operations are re-verified to determine20/// if the grow or shrink is sill valid.21///22/// # Examples23///24/// Basic usage25///26/// ```27/// use kernel::alloc::{AllocError, flags::GFP_KERNEL};28/// use kernel::id_pool::IdPool;29///30/// let mut pool = IdPool::new(64, GFP_KERNEL)?;31/// for i in 0..64 {32/// assert_eq!(i, pool.acquire_next_id(i).ok_or(ENOSPC)?);33/// }34///35/// pool.release_id(23);36/// assert_eq!(23, pool.acquire_next_id(0).ok_or(ENOSPC)?);37///38/// assert_eq!(None, pool.acquire_next_id(0)); // time to realloc.39/// let resizer = pool.grow_request().ok_or(ENOSPC)?.realloc(GFP_KERNEL)?;40/// pool.grow(resizer);41///42/// assert_eq!(pool.acquire_next_id(0), Some(64));43/// # Ok::<(), Error>(())44/// ```45///46/// Releasing spinlock to grow the pool47///48/// ```no_run49/// use kernel::alloc::{AllocError, flags::GFP_KERNEL};50/// use kernel::sync::{new_spinlock, SpinLock};51/// use kernel::id_pool::IdPool;52///53/// fn get_id_maybe_realloc(guarded_pool: &SpinLock<IdPool>) -> Result<usize, AllocError> {54/// let mut pool = guarded_pool.lock();55/// loop {56/// match pool.acquire_next_id(0) {57/// Some(index) => return Ok(index),58/// None => {59/// let alloc_request = pool.grow_request();60/// drop(pool);61/// let resizer = alloc_request.ok_or(AllocError)?.realloc(GFP_KERNEL)?;62/// pool = guarded_pool.lock();63/// pool.grow(resizer)64/// }65/// }66/// }67/// }68/// ```69pub struct IdPool {70map: BitmapVec,71}7273/// Indicates that an [`IdPool`] should change to a new target size.74pub struct ReallocRequest {75num_ids: usize,76}7778/// Contains a [`BitmapVec`] of a size suitable for reallocating [`IdPool`].79pub struct PoolResizer {80new: BitmapVec,81}8283impl ReallocRequest {84/// Allocates a new backing [`BitmapVec`] for [`IdPool`].85///86/// This method only prepares reallocation and does not complete it.87/// Reallocation will complete after passing the [`PoolResizer`] to the88/// [`IdPool::grow`] or [`IdPool::shrink`] operation, which will check89/// that reallocation still makes sense.90pub fn realloc(&self, flags: Flags) -> Result<PoolResizer, AllocError> {91let new = BitmapVec::new(self.num_ids, flags)?;92Ok(PoolResizer { new })93}94}9596impl IdPool {97/// Constructs a new [`IdPool`].98///99/// A capacity below [`BITS_PER_LONG`] is adjusted to100/// [`BITS_PER_LONG`].101///102/// [`BITS_PER_LONG`]: srctree/include/asm-generic/bitsperlong.h103#[inline]104pub fn new(num_ids: usize, flags: Flags) -> Result<Self, AllocError> {105let num_ids = core::cmp::max(num_ids, BITS_PER_LONG);106let map = BitmapVec::new(num_ids, flags)?;107Ok(Self { map })108}109110/// Returns how many IDs this pool can currently have.111#[inline]112pub fn capacity(&self) -> usize {113self.map.len()114}115116/// Returns a [`ReallocRequest`] if the [`IdPool`] can be shrunk, [`None`] otherwise.117///118/// The capacity of an [`IdPool`] cannot be shrunk below [`BITS_PER_LONG`].119///120/// [`BITS_PER_LONG`]: srctree/include/asm-generic/bitsperlong.h121///122/// # Examples123///124/// ```125/// use kernel::alloc::{AllocError, flags::GFP_KERNEL};126/// use kernel::id_pool::{ReallocRequest, IdPool};127///128/// let mut pool = IdPool::new(1024, GFP_KERNEL)?;129/// let alloc_request = pool.shrink_request().ok_or(AllocError)?;130/// let resizer = alloc_request.realloc(GFP_KERNEL)?;131/// pool.shrink(resizer);132/// assert_eq!(pool.capacity(), kernel::bindings::BITS_PER_LONG as usize);133/// # Ok::<(), AllocError>(())134/// ```135#[inline]136pub fn shrink_request(&self) -> Option<ReallocRequest> {137let cap = self.capacity();138// Shrinking below [`BITS_PER_LONG`] is never possible.139if cap <= BITS_PER_LONG {140return None;141}142// Determine if the bitmap can shrink based on the position of143// its last set bit. If the bit is within the first quarter of144// the bitmap then shrinking is possible. In this case, the145// bitmap should shrink to half its current size.146let Some(bit) = self.map.last_bit() else {147return Some(ReallocRequest {148num_ids: BITS_PER_LONG,149});150};151if bit >= (cap / 4) {152return None;153}154let num_ids = usize::max(BITS_PER_LONG, cap / 2);155Some(ReallocRequest { num_ids })156}157158/// Shrinks pool by using a new [`BitmapVec`], if still possible.159#[inline]160pub fn shrink(&mut self, mut resizer: PoolResizer) {161// Between request to shrink that led to allocation of `resizer` and now,162// bits may have changed.163// Verify that shrinking is still possible. In case shrinking to164// the size of `resizer` is no longer possible, do nothing,165// drop `resizer` and move on.166let Some(updated) = self.shrink_request() else {167return;168};169if updated.num_ids > resizer.new.len() {170return;171}172173resizer.new.copy_and_extend(&self.map);174self.map = resizer.new;175}176177/// Returns a [`ReallocRequest`] for growing this [`IdPool`], if possible.178///179/// The capacity of an [`IdPool`] cannot be grown above [`i32::MAX`].180#[inline]181pub fn grow_request(&self) -> Option<ReallocRequest> {182let num_ids = self.capacity() * 2;183if num_ids > i32::MAX.try_into().unwrap() {184return None;185}186Some(ReallocRequest { num_ids })187}188189/// Grows pool by using a new [`BitmapVec`], if still necessary.190///191/// The `resizer` arguments has to be obtained by calling [`Self::grow_request`]192/// on this object and performing a [`ReallocRequest::realloc`].193#[inline]194pub fn grow(&mut self, mut resizer: PoolResizer) {195// Between request to grow that led to allocation of `resizer` and now,196// another thread may have already grown the capacity.197// In this case, do nothing, drop `resizer` and move on.198if resizer.new.len() <= self.capacity() {199return;200}201202resizer.new.copy_and_extend(&self.map);203self.map = resizer.new;204}205206/// Acquires a new ID by finding and setting the next zero bit in the207/// bitmap.208///209/// Upon success, returns its index. Otherwise, returns [`None`]210/// to indicate that a [`Self::grow_request`] is needed.211#[inline]212pub fn acquire_next_id(&mut self, offset: usize) -> Option<usize> {213let next_zero_bit = self.map.next_zero_bit(offset);214if let Some(nr) = next_zero_bit {215self.map.set_bit(nr);216}217next_zero_bit218}219220/// Releases an ID.221#[inline]222pub fn release_id(&mut self, id: usize) {223self.map.clear_bit(id);224}225}226227228