kernel/id_pool.rs
1// SPDX-License-Identifier: GPL-2.0
2
3// Copyright (C) 2025 Google LLC.
4
5//! Rust API for an ID pool backed by a [`BitmapVec`].
6
7use crate::alloc::{AllocError, Flags};
8use crate::bitmap::BitmapVec;
9
10/// Represents a dynamic ID pool backed by a [`BitmapVec`].
11///
12/// Clients acquire and release IDs from unset bits in a bitmap.
13///
14/// The capacity of the ID pool may be adjusted by users as
15/// needed. The API supports the scenario where users need precise control
16/// over the time of allocation of a new backing bitmap, which may require
17/// release of spinlock.
18/// Due to concurrent updates, all operations are re-verified to determine
19/// if the grow or shrink is sill valid.
20///
21/// # Examples
22///
23/// Basic usage
24///
25/// ```
26/// use kernel::alloc::AllocError;
27/// use kernel::id_pool::{IdPool, UnusedId};
28///
29/// let mut pool = IdPool::with_capacity(64, GFP_KERNEL)?;
30/// for i in 0..64 {
31/// assert_eq!(i, pool.find_unused_id(i).ok_or(ENOSPC)?.acquire());
32/// }
33///
34/// pool.release_id(23);
35/// assert_eq!(23, pool.find_unused_id(0).ok_or(ENOSPC)?.acquire());
36///
37/// assert!(pool.find_unused_id(0).is_none()); // time to realloc.
38/// let resizer = pool.grow_request().ok_or(ENOSPC)?.realloc(GFP_KERNEL)?;
39/// pool.grow(resizer);
40///
41/// assert_eq!(pool.find_unused_id(0).ok_or(ENOSPC)?.acquire(), 64);
42/// # Ok::<(), Error>(())
43/// ```
44///
45/// Releasing spinlock to grow the pool
46///
47/// ```no_run
48/// use kernel::alloc::{AllocError, flags::GFP_KERNEL};
49/// use kernel::sync::{new_spinlock, SpinLock};
50/// use kernel::id_pool::IdPool;
51///
52/// fn get_id_maybe_realloc(guarded_pool: &SpinLock<IdPool>) -> Result<usize, AllocError> {
53/// let mut pool = guarded_pool.lock();
54/// loop {
55/// match pool.find_unused_id(0) {
56/// Some(index) => return Ok(index.acquire()),
57/// None => {
58/// let alloc_request = pool.grow_request();
59/// drop(pool);
60/// let resizer = alloc_request.ok_or(AllocError)?.realloc(GFP_KERNEL)?;
61/// pool = guarded_pool.lock();
62/// pool.grow(resizer)
63/// }
64/// }
65/// }
66/// }
67/// ```
68pub struct IdPool {
69 map: BitmapVec,
70}
71
72/// Indicates that an [`IdPool`] should change to a new target size.
73pub struct ReallocRequest {
74 num_ids: usize,
75}
76
77/// Contains a [`BitmapVec`] of a size suitable for reallocating [`IdPool`].
78pub struct PoolResizer {
79 new: BitmapVec,
80}
81
82impl ReallocRequest {
83 /// Allocates a new backing [`BitmapVec`] for [`IdPool`].
84 ///
85 /// This method only prepares reallocation and does not complete it.
86 /// Reallocation will complete after passing the [`PoolResizer`] to the
87 /// [`IdPool::grow`] or [`IdPool::shrink`] operation, which will check
88 /// that reallocation still makes sense.
89 pub fn realloc(&self, flags: Flags) -> Result<PoolResizer, AllocError> {
90 let new = BitmapVec::new(self.num_ids, flags)?;
91 Ok(PoolResizer { new })
92 }
93}
94
95impl IdPool {
96 /// Constructs a new [`IdPool`].
97 ///
98 /// The pool will have a capacity of [`MAX_INLINE_LEN`].
99 ///
100 /// [`MAX_INLINE_LEN`]: BitmapVec::MAX_INLINE_LEN
101 #[inline]
102 pub fn new() -> Self {
103 Self {
104 map: BitmapVec::new_inline(),
105 }
106 }
107
108 /// Constructs a new [`IdPool`] with space for a specific number of bits.
109 ///
110 /// A capacity below [`MAX_INLINE_LEN`] is adjusted to [`MAX_INLINE_LEN`].
111 ///
112 /// [`MAX_INLINE_LEN`]: BitmapVec::MAX_INLINE_LEN
113 #[inline]
114 pub fn with_capacity(num_ids: usize, flags: Flags) -> Result<Self, AllocError> {
115 let num_ids = usize::max(num_ids, BitmapVec::MAX_INLINE_LEN);
116 let map = BitmapVec::new(num_ids, flags)?;
117 Ok(Self { map })
118 }
119
120 /// Returns how many IDs this pool can currently have.
121 #[inline]
122 pub fn capacity(&self) -> usize {
123 self.map.len()
124 }
125
126 /// Returns a [`ReallocRequest`] if the [`IdPool`] can be shrunk, [`None`] otherwise.
127 ///
128 /// The capacity of an [`IdPool`] cannot be shrunk below [`MAX_INLINE_LEN`].
129 ///
130 /// [`MAX_INLINE_LEN`]: BitmapVec::MAX_INLINE_LEN
131 ///
132 /// # Examples
133 ///
134 /// ```
135 /// use kernel::{
136 /// alloc::AllocError,
137 /// bitmap::BitmapVec,
138 /// id_pool::{
139 /// IdPool,
140 /// ReallocRequest,
141 /// },
142 /// };
143 ///
144 /// let mut pool = IdPool::with_capacity(1024, GFP_KERNEL)?;
145 /// let alloc_request = pool.shrink_request().ok_or(AllocError)?;
146 /// let resizer = alloc_request.realloc(GFP_KERNEL)?;
147 /// pool.shrink(resizer);
148 /// assert_eq!(pool.capacity(), BitmapVec::MAX_INLINE_LEN);
149 /// # Ok::<(), AllocError>(())
150 /// ```
151 #[inline]
152 pub fn shrink_request(&self) -> Option<ReallocRequest> {
153 let cap = self.capacity();
154 // Shrinking below `MAX_INLINE_LEN` is never possible.
155 if cap <= BitmapVec::MAX_INLINE_LEN {
156 return None;
157 }
158 // Determine if the bitmap can shrink based on the position of
159 // its last set bit. If the bit is within the first quarter of
160 // the bitmap then shrinking is possible. In this case, the
161 // bitmap should shrink to half its current size.
162 let Some(bit) = self.map.last_bit() else {
163 return Some(ReallocRequest {
164 num_ids: BitmapVec::MAX_INLINE_LEN,
165 });
166 };
167 if bit >= (cap / 4) {
168 return None;
169 }
170 let num_ids = usize::max(BitmapVec::MAX_INLINE_LEN, cap / 2);
171 Some(ReallocRequest { num_ids })
172 }
173
174 /// Shrinks pool by using a new [`BitmapVec`], if still possible.
175 #[inline]
176 pub fn shrink(&mut self, mut resizer: PoolResizer) {
177 // Between request to shrink that led to allocation of `resizer` and now,
178 // bits may have changed.
179 // Verify that shrinking is still possible. In case shrinking to
180 // the size of `resizer` is no longer possible, do nothing,
181 // drop `resizer` and move on.
182 let Some(updated) = self.shrink_request() else {
183 return;
184 };
185 if updated.num_ids > resizer.new.len() {
186 return;
187 }
188
189 resizer.new.copy_and_extend(&self.map);
190 self.map = resizer.new;
191 }
192
193 /// Returns a [`ReallocRequest`] for growing this [`IdPool`], if possible.
194 ///
195 /// The capacity of an [`IdPool`] cannot be grown above [`MAX_LEN`].
196 ///
197 /// [`MAX_LEN`]: BitmapVec::MAX_LEN
198 #[inline]
199 pub fn grow_request(&self) -> Option<ReallocRequest> {
200 let num_ids = self.capacity() * 2;
201 if num_ids > BitmapVec::MAX_LEN {
202 return None;
203 }
204 Some(ReallocRequest { num_ids })
205 }
206
207 /// Grows pool by using a new [`BitmapVec`], if still necessary.
208 ///
209 /// The `resizer` arguments has to be obtained by calling [`Self::grow_request`]
210 /// on this object and performing a [`ReallocRequest::realloc`].
211 #[inline]
212 pub fn grow(&mut self, mut resizer: PoolResizer) {
213 // Between request to grow that led to allocation of `resizer` and now,
214 // another thread may have already grown the capacity.
215 // In this case, do nothing, drop `resizer` and move on.
216 if resizer.new.len() <= self.capacity() {
217 return;
218 }
219
220 resizer.new.copy_and_extend(&self.map);
221 self.map = resizer.new;
222 }
223
224 /// Finds an unused ID in the bitmap.
225 ///
226 /// Upon success, returns its index. Otherwise, returns [`None`]
227 /// to indicate that a [`Self::grow_request`] is needed. `offset` must be
228 /// within bounds.
229 ///
230 /// # Panics
231 ///
232 /// Panics if `CONFIG_RUST_BITMAP_HARDENED` is enabled and `offset` is out
233 /// of bounds.
234 #[inline]
235 #[must_use]
236 pub fn find_unused_id(&mut self, offset: usize) -> Option<UnusedId<'_>> {
237 // INVARIANT: `next_zero_bit()` returns None or an integer less than `map.len()`
238 Some(UnusedId {
239 id: self.map.next_zero_bit(offset)?,
240 pool: self,
241 })
242 }
243
244 /// Releases an ID.
245 ///
246 /// The `id` must be within bounds.
247 ///
248 /// # Panics
249 ///
250 /// Panics if `CONFIG_RUST_BITMAP_HARDENED` is enabled and `id` is out of
251 /// bounds.
252 #[inline]
253 pub fn release_id(&mut self, id: usize) {
254 self.map.clear_bit(id);
255 }
256}
257
258/// Represents an unused id in an [`IdPool`].
259///
260/// # Invariants
261///
262/// The value of `id` is less than `pool.map.len()`.
263pub struct UnusedId<'pool> {
264 id: usize,
265 pool: &'pool mut IdPool,
266}
267
268impl<'pool> UnusedId<'pool> {
269 /// Get the unused id as an usize.
270 ///
271 /// Be aware that the id has not yet been acquired in the pool. The
272 /// [`acquire`] method must be called to prevent others from taking the id.
273 ///
274 /// [`acquire`]: UnusedId::acquire()
275 #[inline]
276 #[must_use]
277 pub fn as_usize(&self) -> usize {
278 self.id
279 }
280
281 /// Get the unused id as an u32.
282 ///
283 /// Be aware that the id has not yet been acquired in the pool. The
284 /// [`acquire`] method must be called to prevent others from taking the id.
285 ///
286 /// [`acquire`]: UnusedId::acquire()
287 #[inline]
288 #[must_use]
289 pub fn as_u32(&self) -> u32 {
290 // CAST: By the type invariants:
291 // `self.id < pool.map.len() <= BitmapVec::MAX_LEN = i32::MAX`.
292 self.id as u32
293 }
294
295 /// Acquire the unused id.
296 #[inline]
297 pub fn acquire(self) -> usize {
298 self.pool.map.set_bit(self.id);
299 self.id
300 }
301}
302
303impl Default for IdPool {
304 #[inline]
305 fn default() -> Self {
306 Self::new()
307 }
308}