Skip to content

Commit 9def0d0

Browse files
DarksonnDanilo Krummrich
authored andcommitted
rust: alloc: add Vec::push_within_capacity
This introduces a new method called `push_within_capacity` for appending to a vector without attempting to allocate if the capacity is full. Rust Binder will use this in various places to safely push to a vector while holding a spinlock. The implementation is moved to a push_within_capacity_unchecked method. This is preferred over having push() call push_within_capacity() followed by an unwrap_unchecked() for simpler unsafe. Panics in the kernel are best avoided when possible, so an error is returned if the vector does not have sufficient capacity. An error type is used rather than just returning Result<(),T> to make it more convenient for callers (i.e. they can use ? or unwrap). 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-3-06d20ad9366f@google.com [ Remove public visibility from `Vec::push_within_capacity_unchecked()`. - Danilo ] Signed-off-by: Danilo Krummrich <dakr@kernel.org>
1 parent f2b4dd7 commit 9def0d0

File tree

2 files changed

+65
-4
lines changed

2 files changed

+65
-4
lines changed

rust/kernel/alloc/kvec.rs

Lines changed: 42 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,9 @@ use core::{
2121
slice::SliceIndex,
2222
};
2323

24+
mod errors;
25+
pub use self::errors::PushError;
26+
2427
/// Create a [`KVec`] containing the arguments.
2528
///
2629
/// New memory is allocated with `GFP_KERNEL`.
@@ -307,17 +310,52 @@ where
307310
/// ```
308311
pub fn push(&mut self, v: T, flags: Flags) -> Result<(), AllocError> {
309312
self.reserve(1, flags)?;
313+
// SAFETY: The call to `reserve` was successful, so the capacity is at least one greater
314+
// than the length.
315+
unsafe { self.push_within_capacity_unchecked(v) };
316+
Ok(())
317+
}
310318

319+
/// Appends an element to the back of the [`Vec`] instance without reallocating.
320+
///
321+
/// Fails if the vector does not have capacity for the new element.
322+
///
323+
/// # Examples
324+
///
325+
/// ```
326+
/// let mut v = KVec::with_capacity(10, GFP_KERNEL)?;
327+
/// for i in 0..10 {
328+
/// v.push_within_capacity(i)?;
329+
/// }
330+
///
331+
/// assert!(v.push_within_capacity(10).is_err());
332+
/// # Ok::<(), Error>(())
333+
/// ```
334+
pub fn push_within_capacity(&mut self, v: T) -> Result<(), PushError<T>> {
335+
if self.len() < self.capacity() {
336+
// SAFETY: The length is less than the capacity.
337+
unsafe { self.push_within_capacity_unchecked(v) };
338+
Ok(())
339+
} else {
340+
Err(PushError(v))
341+
}
342+
}
343+
344+
/// Appends an element to the back of the [`Vec`] instance without reallocating.
345+
///
346+
/// # Safety
347+
///
348+
/// The length must be less than the capacity.
349+
unsafe fn push_within_capacity_unchecked(&mut self, v: T) {
311350
let spare = self.spare_capacity_mut();
312351

313-
// SAFETY: The call to `reserve` was successful so the spare capacity is at least 1.
352+
// SAFETY: By the safety requirements, `spare` is non-empty.
314353
unsafe { spare.get_unchecked_mut(0) }.write(v);
315354

316355
// SAFETY: We just initialised the first spare entry, so it is safe to increase the length
317-
// by 1. We also know that the new length is <= capacity because of the previous call to
318-
// `reserve` above.
356+
// by 1. We also know that the new length is <= capacity because the caller guarantees that
357+
// the length is less than the capacity at the beginning of this function.
319358
unsafe { self.inc_len(1) };
320-
Ok(())
321359
}
322360

323361
/// Removes the last element from a vector and returns it, or `None` if it is empty.

rust/kernel/alloc/kvec/errors.rs

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
// SPDX-License-Identifier: GPL-2.0
2+
3+
//! Errors for the [`Vec`] type.
4+
5+
use core::fmt::{self, Debug, Formatter};
6+
use kernel::prelude::*;
7+
8+
/// Error type for [`Vec::push_within_capacity`].
9+
pub struct PushError<T>(pub T);
10+
11+
impl<T> Debug for PushError<T> {
12+
fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
13+
write!(f, "Not enough capacity")
14+
}
15+
}
16+
17+
impl<T> From<PushError<T>> for Error {
18+
fn from(_: PushError<T>) -> Error {
19+
// Returning ENOMEM isn't appropriate because the system is not out of memory. The vector
20+
// is just full and we are refusing to resize it.
21+
EINVAL
22+
}
23+
}

0 commit comments

Comments
 (0)