Skip to content

Commit 771c5a7

Browse files
DarksonnDanilo Krummrich
authored andcommitted
rust: alloc: add Vec::insert_within_capacity
This adds a variant of Vec::insert that does not allocate memory. This makes it safe to use this function while holding a spinlock. Rust Binder uses it for the range allocator fast path. Signed-off-by: Alice Ryhl <aliceryhl@google.com> Reviewed-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org> Reviewed-by: Benno Lossin <lossin@kernel.org> Link: https://lore.kernel.org/r/20250502-vec-methods-v5-7-06d20ad9366f@google.com Signed-off-by: Danilo Krummrich <dakr@kernel.org>
1 parent 294a7ec commit 771c5a7

File tree

2 files changed

+73
-1
lines changed

2 files changed

+73
-1
lines changed

rust/kernel/alloc/kvec.rs

Lines changed: 50 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -22,7 +22,7 @@ use core::{
2222
};
2323

2424
mod errors;
25-
pub use self::errors::{PushError, RemoveError};
25+
pub use self::errors::{InsertError, PushError, RemoveError};
2626

2727
/// Create a [`KVec`] containing the arguments.
2828
///
@@ -358,6 +358,55 @@ where
358358
unsafe { self.inc_len(1) };
359359
}
360360

361+
/// Inserts an element at the given index in the [`Vec`] instance.
362+
///
363+
/// Fails if the vector does not have capacity for the new element. Panics if the index is out
364+
/// of bounds.
365+
///
366+
/// # Examples
367+
///
368+
/// ```
369+
/// use kernel::alloc::kvec::InsertError;
370+
///
371+
/// let mut v = KVec::with_capacity(5, GFP_KERNEL)?;
372+
/// for i in 0..5 {
373+
/// v.insert_within_capacity(0, i)?;
374+
/// }
375+
///
376+
/// assert!(matches!(v.insert_within_capacity(0, 5), Err(InsertError::OutOfCapacity(_))));
377+
/// assert!(matches!(v.insert_within_capacity(1000, 5), Err(InsertError::IndexOutOfBounds(_))));
378+
/// assert_eq!(v, [4, 3, 2, 1, 0]);
379+
/// # Ok::<(), Error>(())
380+
/// ```
381+
pub fn insert_within_capacity(
382+
&mut self,
383+
index: usize,
384+
element: T,
385+
) -> Result<(), InsertError<T>> {
386+
let len = self.len();
387+
if index > len {
388+
return Err(InsertError::IndexOutOfBounds(element));
389+
}
390+
391+
if len >= self.capacity() {
392+
return Err(InsertError::OutOfCapacity(element));
393+
}
394+
395+
// SAFETY: This is in bounds since `index <= len < capacity`.
396+
let p = unsafe { self.as_mut_ptr().add(index) };
397+
// INVARIANT: This breaks the Vec invariants by making `index` contain an invalid element,
398+
// but we restore the invariants below.
399+
// SAFETY: Both the src and dst ranges end no later than one element after the length.
400+
// Since the length is less than the capacity, both ranges are in bounds of the allocation.
401+
unsafe { ptr::copy(p, p.add(1), len - index) };
402+
// INVARIANT: This restores the Vec invariants.
403+
// SAFETY: The pointer is in-bounds of the allocation.
404+
unsafe { ptr::write(p, element) };
405+
// SAFETY: Index `len` contains a valid element due to the above copy and write.
406+
unsafe { self.inc_len(1) };
407+
Ok(())
408+
}
409+
361410
/// Removes the last element from a vector and returns it, or `None` if it is empty.
362411
///
363412
/// # Examples

rust/kernel/alloc/kvec/errors.rs

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -36,3 +36,26 @@ impl From<RemoveError> for Error {
3636
EINVAL
3737
}
3838
}
39+
40+
/// Error type for [`Vec::insert_within_capacity`].
41+
pub enum InsertError<T> {
42+
/// The value could not be inserted because the index is out of bounds.
43+
IndexOutOfBounds(T),
44+
/// The value could not be inserted because the vector is out of capacity.
45+
OutOfCapacity(T),
46+
}
47+
48+
impl<T> Debug for InsertError<T> {
49+
fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
50+
match self {
51+
InsertError::IndexOutOfBounds(_) => write!(f, "Index out of bounds"),
52+
InsertError::OutOfCapacity(_) => write!(f, "Not enough capacity"),
53+
}
54+
}
55+
}
56+
57+
impl<T> From<InsertError<T>> for Error {
58+
fn from(_: InsertError<T>) -> Error {
59+
EINVAL
60+
}
61+
}

0 commit comments

Comments
 (0)