Skip to content

Commit f523d11

Browse files
DarksonnYuryNorov
authored andcommitted
rust: id_pool: do not immediately acquire new ids
When Rust Binder assigns a new ID, it performs various fallible operations before it "commits" to actually using the new ID. To support this pattern, change acquire_next_id() so that it does not immediately call set_bit(), but instead returns an object that may be used to call set_bit() later. The UnusedId type holds a exclusive reference to the IdPool, so it's guaranteed that nobody else can call find_unused_id() while the UnusedId object is live. [Miguel: rust: id_pool: fix example] Reviewed-by: Burak Emir <bqe@google.com> Reviewed-by: Danilo Krummrich <dakr@kernel.org> Signed-off-by: Alice Ryhl <aliceryhl@google.com> Signed-off-by: Yury Norov (NVIDIA) <yury.norov@gmail.com>
1 parent 69ec6a1 commit f523d11

1 file changed

Lines changed: 61 additions & 16 deletions

File tree

rust/kernel/id_pool.rs

Lines changed: 61 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -23,22 +23,22 @@ use crate::bitmap::BitmapVec;
2323
/// Basic usage
2424
///
2525
/// ```
26-
/// use kernel::alloc::{AllocError, flags::GFP_KERNEL};
27-
/// use kernel::id_pool::IdPool;
26+
/// use kernel::alloc::AllocError;
27+
/// use kernel::id_pool::{IdPool, UnusedId};
2828
///
2929
/// let mut pool = IdPool::with_capacity(64, GFP_KERNEL)?;
3030
/// for i in 0..64 {
31-
/// assert_eq!(i, pool.acquire_next_id(i).ok_or(ENOSPC)?);
31+
/// assert_eq!(i, pool.find_unused_id(i).ok_or(ENOSPC)?.acquire());
3232
/// }
3333
///
3434
/// pool.release_id(23);
35-
/// assert_eq!(23, pool.acquire_next_id(0).ok_or(ENOSPC)?);
35+
/// assert_eq!(23, pool.find_unused_id(0).ok_or(ENOSPC)?.acquire());
3636
///
37-
/// assert_eq!(None, pool.acquire_next_id(0)); // time to realloc.
37+
/// assert!(pool.find_unused_id(0).is_none()); // time to realloc.
3838
/// let resizer = pool.grow_request().ok_or(ENOSPC)?.realloc(GFP_KERNEL)?;
3939
/// pool.grow(resizer);
4040
///
41-
/// assert_eq!(pool.acquire_next_id(0), Some(64));
41+
/// assert_eq!(pool.find_unused_id(0).ok_or(ENOSPC)?.acquire(), 64);
4242
/// # Ok::<(), Error>(())
4343
/// ```
4444
///
@@ -52,8 +52,8 @@ use crate::bitmap::BitmapVec;
5252
/// fn get_id_maybe_realloc(guarded_pool: &SpinLock<IdPool>) -> Result<usize, AllocError> {
5353
/// let mut pool = guarded_pool.lock();
5454
/// loop {
55-
/// match pool.acquire_next_id(0) {
56-
/// Some(index) => return Ok(index),
55+
/// match pool.find_unused_id(0) {
56+
/// Some(index) => return Ok(index.acquire()),
5757
/// None => {
5858
/// let alloc_request = pool.grow_request();
5959
/// drop(pool);
@@ -221,18 +221,18 @@ impl IdPool {
221221
self.map = resizer.new;
222222
}
223223

224-
/// Acquires a new ID by finding and setting the next zero bit in the
225-
/// bitmap.
224+
/// Finds an unused ID in the bitmap.
226225
///
227226
/// Upon success, returns its index. Otherwise, returns [`None`]
228227
/// to indicate that a [`Self::grow_request`] is needed.
229228
#[inline]
230-
pub fn acquire_next_id(&mut self, offset: usize) -> Option<usize> {
231-
let next_zero_bit = self.map.next_zero_bit(offset);
232-
if let Some(nr) = next_zero_bit {
233-
self.map.set_bit(nr);
234-
}
235-
next_zero_bit
229+
#[must_use]
230+
pub fn find_unused_id(&mut self, offset: usize) -> Option<UnusedId<'_>> {
231+
// INVARIANT: `next_zero_bit()` returns None or an integer less than `map.len()`
232+
Some(UnusedId {
233+
id: self.map.next_zero_bit(offset)?,
234+
pool: self,
235+
})
236236
}
237237

238238
/// Releases an ID.
@@ -242,6 +242,51 @@ impl IdPool {
242242
}
243243
}
244244

245+
/// Represents an unused id in an [`IdPool`].
246+
///
247+
/// # Invariants
248+
///
249+
/// The value of `id` is less than `pool.map.len()`.
250+
pub struct UnusedId<'pool> {
251+
id: usize,
252+
pool: &'pool mut IdPool,
253+
}
254+
255+
impl<'pool> UnusedId<'pool> {
256+
/// Get the unused id as an usize.
257+
///
258+
/// Be aware that the id has not yet been acquired in the pool. The
259+
/// [`acquire`] method must be called to prevent others from taking the id.
260+
///
261+
/// [`acquire`]: UnusedId::acquire()
262+
#[inline]
263+
#[must_use]
264+
pub fn as_usize(&self) -> usize {
265+
self.id
266+
}
267+
268+
/// Get the unused id as an u32.
269+
///
270+
/// Be aware that the id has not yet been acquired in the pool. The
271+
/// [`acquire`] method must be called to prevent others from taking the id.
272+
///
273+
/// [`acquire`]: UnusedId::acquire()
274+
#[inline]
275+
#[must_use]
276+
pub fn as_u32(&self) -> u32 {
277+
// CAST: By the type invariants:
278+
// `self.id < pool.map.len() <= BitmapVec::MAX_LEN = i32::MAX`.
279+
self.id as u32
280+
}
281+
282+
/// Acquire the unused id.
283+
#[inline]
284+
pub fn acquire(self) -> usize {
285+
self.pool.map.set_bit(self.id);
286+
self.id
287+
}
288+
}
289+
245290
impl Default for IdPool {
246291
#[inline]
247292
fn default() -> Self {

0 commit comments

Comments
 (0)