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