xref: /linux/rust/kernel/id_pool.rs (revision 2cdae413cd3ee6aad782cf4bce8c10fdb0f0657c)
1*2cdae413SBurak Emir // SPDX-License-Identifier: GPL-2.0
2*2cdae413SBurak Emir 
3*2cdae413SBurak Emir // Copyright (C) 2025 Google LLC.
4*2cdae413SBurak Emir 
5*2cdae413SBurak Emir //! Rust API for an ID pool backed by a [`BitmapVec`].
6*2cdae413SBurak Emir 
7*2cdae413SBurak Emir use crate::alloc::{AllocError, Flags};
8*2cdae413SBurak Emir use crate::bitmap::BitmapVec;
9*2cdae413SBurak Emir 
10*2cdae413SBurak Emir const BITS_PER_LONG: usize = bindings::BITS_PER_LONG as usize;
11*2cdae413SBurak Emir 
12*2cdae413SBurak Emir /// Represents a dynamic ID pool backed by a [`BitmapVec`].
13*2cdae413SBurak Emir ///
14*2cdae413SBurak Emir /// Clients acquire and release IDs from unset bits in a bitmap.
15*2cdae413SBurak Emir ///
16*2cdae413SBurak Emir /// The capacity of the ID pool may be adjusted by users as
17*2cdae413SBurak Emir /// needed. The API supports the scenario where users need precise control
18*2cdae413SBurak Emir /// over the time of allocation of a new backing bitmap, which may require
19*2cdae413SBurak Emir /// release of spinlock.
20*2cdae413SBurak Emir /// Due to concurrent updates, all operations are re-verified to determine
21*2cdae413SBurak Emir /// if the grow or shrink is sill valid.
22*2cdae413SBurak Emir ///
23*2cdae413SBurak Emir /// # Examples
24*2cdae413SBurak Emir ///
25*2cdae413SBurak Emir /// Basic usage
26*2cdae413SBurak Emir ///
27*2cdae413SBurak Emir /// ```
28*2cdae413SBurak Emir /// use kernel::alloc::{AllocError, flags::GFP_KERNEL};
29*2cdae413SBurak Emir /// use kernel::id_pool::IdPool;
30*2cdae413SBurak Emir ///
31*2cdae413SBurak Emir /// let mut pool = IdPool::new(64, GFP_KERNEL)?;
32*2cdae413SBurak Emir /// for i in 0..64 {
33*2cdae413SBurak Emir ///     assert_eq!(i, pool.acquire_next_id(i).ok_or(ENOSPC)?);
34*2cdae413SBurak Emir /// }
35*2cdae413SBurak Emir ///
36*2cdae413SBurak Emir /// pool.release_id(23);
37*2cdae413SBurak Emir /// assert_eq!(23, pool.acquire_next_id(0).ok_or(ENOSPC)?);
38*2cdae413SBurak Emir ///
39*2cdae413SBurak Emir /// assert_eq!(None, pool.acquire_next_id(0));  // time to realloc.
40*2cdae413SBurak Emir /// let resizer = pool.grow_request().ok_or(ENOSPC)?.realloc(GFP_KERNEL)?;
41*2cdae413SBurak Emir /// pool.grow(resizer);
42*2cdae413SBurak Emir ///
43*2cdae413SBurak Emir /// assert_eq!(pool.acquire_next_id(0), Some(64));
44*2cdae413SBurak Emir /// # Ok::<(), Error>(())
45*2cdae413SBurak Emir /// ```
46*2cdae413SBurak Emir ///
47*2cdae413SBurak Emir /// Releasing spinlock to grow the pool
48*2cdae413SBurak Emir ///
49*2cdae413SBurak Emir /// ```no_run
50*2cdae413SBurak Emir /// use kernel::alloc::{AllocError, flags::GFP_KERNEL};
51*2cdae413SBurak Emir /// use kernel::sync::{new_spinlock, SpinLock};
52*2cdae413SBurak Emir /// use kernel::id_pool::IdPool;
53*2cdae413SBurak Emir ///
54*2cdae413SBurak Emir /// fn get_id_maybe_realloc(guarded_pool: &SpinLock<IdPool>) -> Result<usize, AllocError> {
55*2cdae413SBurak Emir ///     let mut pool = guarded_pool.lock();
56*2cdae413SBurak Emir ///     loop {
57*2cdae413SBurak Emir ///         match pool.acquire_next_id(0) {
58*2cdae413SBurak Emir ///             Some(index) => return Ok(index),
59*2cdae413SBurak Emir ///             None => {
60*2cdae413SBurak Emir ///                 let alloc_request = pool.grow_request();
61*2cdae413SBurak Emir ///                 drop(pool);
62*2cdae413SBurak Emir ///                 let resizer = alloc_request.ok_or(AllocError)?.realloc(GFP_KERNEL)?;
63*2cdae413SBurak Emir ///                 pool = guarded_pool.lock();
64*2cdae413SBurak Emir ///                 pool.grow(resizer)
65*2cdae413SBurak Emir ///             }
66*2cdae413SBurak Emir ///         }
67*2cdae413SBurak Emir ///     }
68*2cdae413SBurak Emir /// }
69*2cdae413SBurak Emir /// ```
70*2cdae413SBurak Emir pub struct IdPool {
71*2cdae413SBurak Emir     map: BitmapVec,
72*2cdae413SBurak Emir }
73*2cdae413SBurak Emir 
74*2cdae413SBurak Emir /// Indicates that an [`IdPool`] should change to a new target size.
75*2cdae413SBurak Emir pub struct ReallocRequest {
76*2cdae413SBurak Emir     num_ids: usize,
77*2cdae413SBurak Emir }
78*2cdae413SBurak Emir 
79*2cdae413SBurak Emir /// Contains a [`BitmapVec`] of a size suitable for reallocating [`IdPool`].
80*2cdae413SBurak Emir pub struct PoolResizer {
81*2cdae413SBurak Emir     new: BitmapVec,
82*2cdae413SBurak Emir }
83*2cdae413SBurak Emir 
84*2cdae413SBurak Emir impl ReallocRequest {
85*2cdae413SBurak Emir     /// Allocates a new backing [`BitmapVec`] for [`IdPool`].
86*2cdae413SBurak Emir     ///
87*2cdae413SBurak Emir     /// This method only prepares reallocation and does not complete it.
88*2cdae413SBurak Emir     /// Reallocation will complete after passing the [`PoolResizer`] to the
89*2cdae413SBurak Emir     /// [`IdPool::grow`] or [`IdPool::shrink`] operation, which will check
90*2cdae413SBurak Emir     /// that reallocation still makes sense.
91*2cdae413SBurak Emir     pub fn realloc(&self, flags: Flags) -> Result<PoolResizer, AllocError> {
92*2cdae413SBurak Emir         let new = BitmapVec::new(self.num_ids, flags)?;
93*2cdae413SBurak Emir         Ok(PoolResizer { new })
94*2cdae413SBurak Emir     }
95*2cdae413SBurak Emir }
96*2cdae413SBurak Emir 
97*2cdae413SBurak Emir impl IdPool {
98*2cdae413SBurak Emir     /// Constructs a new [`IdPool`].
99*2cdae413SBurak Emir     ///
100*2cdae413SBurak Emir     /// A capacity below [`BITS_PER_LONG`] is adjusted to
101*2cdae413SBurak Emir     /// [`BITS_PER_LONG`].
102*2cdae413SBurak Emir     ///
103*2cdae413SBurak Emir     /// [`BITS_PER_LONG`]: srctree/include/asm-generic/bitsperlong.h
104*2cdae413SBurak Emir     #[inline]
105*2cdae413SBurak Emir     pub fn new(num_ids: usize, flags: Flags) -> Result<Self, AllocError> {
106*2cdae413SBurak Emir         let num_ids = core::cmp::max(num_ids, BITS_PER_LONG);
107*2cdae413SBurak Emir         let map = BitmapVec::new(num_ids, flags)?;
108*2cdae413SBurak Emir         Ok(Self { map })
109*2cdae413SBurak Emir     }
110*2cdae413SBurak Emir 
111*2cdae413SBurak Emir     /// Returns how many IDs this pool can currently have.
112*2cdae413SBurak Emir     #[inline]
113*2cdae413SBurak Emir     pub fn capacity(&self) -> usize {
114*2cdae413SBurak Emir         self.map.len()
115*2cdae413SBurak Emir     }
116*2cdae413SBurak Emir 
117*2cdae413SBurak Emir     /// Returns a [`ReallocRequest`] if the [`IdPool`] can be shrunk, [`None`] otherwise.
118*2cdae413SBurak Emir     ///
119*2cdae413SBurak Emir     /// The capacity of an [`IdPool`] cannot be shrunk below [`BITS_PER_LONG`].
120*2cdae413SBurak Emir     ///
121*2cdae413SBurak Emir     /// [`BITS_PER_LONG`]: srctree/include/asm-generic/bitsperlong.h
122*2cdae413SBurak Emir     ///
123*2cdae413SBurak Emir     /// # Examples
124*2cdae413SBurak Emir     ///
125*2cdae413SBurak Emir     /// ```
126*2cdae413SBurak Emir     /// use kernel::alloc::{AllocError, flags::GFP_KERNEL};
127*2cdae413SBurak Emir     /// use kernel::id_pool::{ReallocRequest, IdPool};
128*2cdae413SBurak Emir     ///
129*2cdae413SBurak Emir     /// let mut pool = IdPool::new(1024, GFP_KERNEL)?;
130*2cdae413SBurak Emir     /// let alloc_request = pool.shrink_request().ok_or(AllocError)?;
131*2cdae413SBurak Emir     /// let resizer = alloc_request.realloc(GFP_KERNEL)?;
132*2cdae413SBurak Emir     /// pool.shrink(resizer);
133*2cdae413SBurak Emir     /// assert_eq!(pool.capacity(), kernel::bindings::BITS_PER_LONG as usize);
134*2cdae413SBurak Emir     /// # Ok::<(), AllocError>(())
135*2cdae413SBurak Emir     /// ```
136*2cdae413SBurak Emir     #[inline]
137*2cdae413SBurak Emir     pub fn shrink_request(&self) -> Option<ReallocRequest> {
138*2cdae413SBurak Emir         let cap = self.capacity();
139*2cdae413SBurak Emir         // Shrinking below [`BITS_PER_LONG`] is never possible.
140*2cdae413SBurak Emir         if cap <= BITS_PER_LONG {
141*2cdae413SBurak Emir             return None;
142*2cdae413SBurak Emir         }
143*2cdae413SBurak Emir         // Determine if the bitmap can shrink based on the position of
144*2cdae413SBurak Emir         // its last set bit. If the bit is within the first quarter of
145*2cdae413SBurak Emir         // the bitmap then shrinking is possible. In this case, the
146*2cdae413SBurak Emir         // bitmap should shrink to half its current size.
147*2cdae413SBurak Emir         let Some(bit) = self.map.last_bit() else {
148*2cdae413SBurak Emir             return Some(ReallocRequest {
149*2cdae413SBurak Emir                 num_ids: BITS_PER_LONG,
150*2cdae413SBurak Emir             });
151*2cdae413SBurak Emir         };
152*2cdae413SBurak Emir         if bit >= (cap / 4) {
153*2cdae413SBurak Emir             return None;
154*2cdae413SBurak Emir         }
155*2cdae413SBurak Emir         let num_ids = usize::max(BITS_PER_LONG, cap / 2);
156*2cdae413SBurak Emir         Some(ReallocRequest { num_ids })
157*2cdae413SBurak Emir     }
158*2cdae413SBurak Emir 
159*2cdae413SBurak Emir     /// Shrinks pool by using a new [`BitmapVec`], if still possible.
160*2cdae413SBurak Emir     #[inline]
161*2cdae413SBurak Emir     pub fn shrink(&mut self, mut resizer: PoolResizer) {
162*2cdae413SBurak Emir         // Between request to shrink that led to allocation of `resizer` and now,
163*2cdae413SBurak Emir         // bits may have changed.
164*2cdae413SBurak Emir         // Verify that shrinking is still possible. In case shrinking to
165*2cdae413SBurak Emir         // the size of `resizer` is no longer possible, do nothing,
166*2cdae413SBurak Emir         // drop `resizer` and move on.
167*2cdae413SBurak Emir         let Some(updated) = self.shrink_request() else {
168*2cdae413SBurak Emir             return;
169*2cdae413SBurak Emir         };
170*2cdae413SBurak Emir         if updated.num_ids > resizer.new.len() {
171*2cdae413SBurak Emir             return;
172*2cdae413SBurak Emir         }
173*2cdae413SBurak Emir 
174*2cdae413SBurak Emir         resizer.new.copy_and_extend(&self.map);
175*2cdae413SBurak Emir         self.map = resizer.new;
176*2cdae413SBurak Emir     }
177*2cdae413SBurak Emir 
178*2cdae413SBurak Emir     /// Returns a [`ReallocRequest`] for growing this [`IdPool`], if possible.
179*2cdae413SBurak Emir     ///
180*2cdae413SBurak Emir     /// The capacity of an [`IdPool`] cannot be grown above [`i32::MAX`].
181*2cdae413SBurak Emir     #[inline]
182*2cdae413SBurak Emir     pub fn grow_request(&self) -> Option<ReallocRequest> {
183*2cdae413SBurak Emir         let num_ids = self.capacity() * 2;
184*2cdae413SBurak Emir         if num_ids > i32::MAX.try_into().unwrap() {
185*2cdae413SBurak Emir             return None;
186*2cdae413SBurak Emir         }
187*2cdae413SBurak Emir         Some(ReallocRequest { num_ids })
188*2cdae413SBurak Emir     }
189*2cdae413SBurak Emir 
190*2cdae413SBurak Emir     /// Grows pool by using a new [`BitmapVec`], if still necessary.
191*2cdae413SBurak Emir     ///
192*2cdae413SBurak Emir     /// The `resizer` arguments has to be obtained by calling [`Self::grow_request`]
193*2cdae413SBurak Emir     /// on this object and performing a [`ReallocRequest::realloc`].
194*2cdae413SBurak Emir     #[inline]
195*2cdae413SBurak Emir     pub fn grow(&mut self, mut resizer: PoolResizer) {
196*2cdae413SBurak Emir         // Between request to grow that led to allocation of `resizer` and now,
197*2cdae413SBurak Emir         // another thread may have already grown the capacity.
198*2cdae413SBurak Emir         // In this case, do nothing, drop `resizer` and move on.
199*2cdae413SBurak Emir         if resizer.new.len() <= self.capacity() {
200*2cdae413SBurak Emir             return;
201*2cdae413SBurak Emir         }
202*2cdae413SBurak Emir 
203*2cdae413SBurak Emir         resizer.new.copy_and_extend(&self.map);
204*2cdae413SBurak Emir         self.map = resizer.new;
205*2cdae413SBurak Emir     }
206*2cdae413SBurak Emir 
207*2cdae413SBurak Emir     /// Acquires a new ID by finding and setting the next zero bit in the
208*2cdae413SBurak Emir     /// bitmap.
209*2cdae413SBurak Emir     ///
210*2cdae413SBurak Emir     /// Upon success, returns its index. Otherwise, returns [`None`]
211*2cdae413SBurak Emir     /// to indicate that a [`Self::grow_request`] is needed.
212*2cdae413SBurak Emir     #[inline]
213*2cdae413SBurak Emir     pub fn acquire_next_id(&mut self, offset: usize) -> Option<usize> {
214*2cdae413SBurak Emir         let next_zero_bit = self.map.next_zero_bit(offset);
215*2cdae413SBurak Emir         if let Some(nr) = next_zero_bit {
216*2cdae413SBurak Emir             self.map.set_bit(nr);
217*2cdae413SBurak Emir         }
218*2cdae413SBurak Emir         next_zero_bit
219*2cdae413SBurak Emir     }
220*2cdae413SBurak Emir 
221*2cdae413SBurak Emir     /// Releases an ID.
222*2cdae413SBurak Emir     #[inline]
223*2cdae413SBurak Emir     pub fn release_id(&mut self, id: usize) {
224*2cdae413SBurak Emir         self.map.clear_bit(id);
225*2cdae413SBurak Emir     }
226*2cdae413SBurak Emir }
227