mirror of
https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
synced 2026-05-16 06:41:39 -04:00
rust: kvec: implement shrink_to for KVVec
Implement shrink_to method specifically for `KVVec` (i.e., `Vec<T, KVmalloc>`). `shrink_to` reduces the vector's capacity to a specified minimum. For kmalloc-backed allocations, the method delegates to realloc(), letting the allocator decide whether shrinking is worthwhile. For vmalloc-backed allocations (detected via is_vmalloc_addr), shrinking only occurs if at least one page of memory can be freed, using an explicit alloc+copy+free since vrealloc does not yet support in-place shrinking. A TODO note marks this for future replacement with a generic shrink_to for all allocators that uses A::realloc() once the underlying allocators properly support shrinking via realloc. Suggested-by: Alice Ryhl <aliceryhl@google.com> Suggested-by: Danilo Krummrich <dakr@kernel.org> Reviewed-by: Alice Ryhl <aliceryhl@google.com> Acked-by: Danilo Krummrich <dakr@kernel.org> Signed-off-by: Shivam Kalra <shivamkalra98@zohomail.in> Link: https://patch.msgid.link/20260216-binder-shrink-vec-v3-v6-1-ece8e8593e53@zohomail.in Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
This commit is contained in:
committed by
Greg Kroah-Hartman
parent
d31ed22a06
commit
47ac2a4b5c
@@ -9,7 +9,10 @@
|
||||
};
|
||||
use crate::{
|
||||
fmt,
|
||||
page::AsPageIter, //
|
||||
page::{
|
||||
AsPageIter,
|
||||
PAGE_SIZE, //
|
||||
},
|
||||
};
|
||||
use core::{
|
||||
borrow::{Borrow, BorrowMut},
|
||||
@@ -734,6 +737,115 @@ pub fn retain(&mut self, mut f: impl FnMut(&mut T) -> bool) {
|
||||
self.truncate(num_kept);
|
||||
}
|
||||
}
|
||||
// TODO: This is a temporary KVVec-specific implementation. It should be replaced with a generic
|
||||
// `shrink_to()` for `impl<T, A: Allocator> Vec<T, A>` that uses `A::realloc()` once the
|
||||
// underlying allocators properly support shrinking via realloc.
|
||||
impl<T> Vec<T, KVmalloc> {
|
||||
/// Shrinks the capacity of the vector with a lower bound.
|
||||
///
|
||||
/// The capacity will remain at least as large as both the length and the supplied value.
|
||||
/// If the current capacity is less than the lower limit, this is a no-op.
|
||||
///
|
||||
/// For `kmalloc` allocations, this delegates to `realloc()`, which decides whether
|
||||
/// shrinking is worthwhile. For `vmalloc` allocations, shrinking only occurs if the
|
||||
/// operation would free at least one page of memory, and performs a deep copy since
|
||||
/// `vrealloc` does not yet support in-place shrinking.
|
||||
///
|
||||
/// # Examples
|
||||
///
|
||||
/// ```
|
||||
/// // Allocate enough capacity to span multiple pages.
|
||||
/// let elements_per_page = kernel::page::PAGE_SIZE / core::mem::size_of::<u32>();
|
||||
/// let mut v = KVVec::with_capacity(elements_per_page * 4, GFP_KERNEL)?;
|
||||
/// v.push(1, GFP_KERNEL)?;
|
||||
/// v.push(2, GFP_KERNEL)?;
|
||||
///
|
||||
/// v.shrink_to(0, GFP_KERNEL)?;
|
||||
/// # Ok::<(), Error>(())
|
||||
/// ```
|
||||
pub fn shrink_to(&mut self, min_capacity: usize, flags: Flags) -> Result<(), AllocError> {
|
||||
let target_cap = core::cmp::max(self.len(), min_capacity);
|
||||
|
||||
if self.capacity() <= target_cap {
|
||||
return Ok(());
|
||||
}
|
||||
|
||||
if Self::is_zst() {
|
||||
return Ok(());
|
||||
}
|
||||
|
||||
// For kmalloc allocations, delegate to realloc() and let the allocator decide
|
||||
// whether shrinking is worthwhile.
|
||||
//
|
||||
// SAFETY: `self.ptr` points to a valid `KVmalloc` allocation.
|
||||
if !unsafe { bindings::is_vmalloc_addr(self.ptr.as_ptr().cast()) } {
|
||||
let new_layout = ArrayLayout::<T>::new(target_cap).map_err(|_| AllocError)?;
|
||||
|
||||
// SAFETY:
|
||||
// - `self.ptr` is valid and was previously allocated with `KVmalloc`.
|
||||
// - `self.layout` matches the `ArrayLayout` of the preceding allocation.
|
||||
let ptr = unsafe {
|
||||
KVmalloc::realloc(
|
||||
Some(self.ptr.cast()),
|
||||
new_layout.into(),
|
||||
self.layout.into(),
|
||||
flags,
|
||||
NumaNode::NO_NODE,
|
||||
)?
|
||||
};
|
||||
|
||||
self.ptr = ptr.cast();
|
||||
self.layout = new_layout;
|
||||
return Ok(());
|
||||
}
|
||||
|
||||
// Only shrink if we would free at least one page.
|
||||
let current_size = self.capacity() * core::mem::size_of::<T>();
|
||||
let target_size = target_cap * core::mem::size_of::<T>();
|
||||
let current_pages = current_size.div_ceil(PAGE_SIZE);
|
||||
let target_pages = target_size.div_ceil(PAGE_SIZE);
|
||||
|
||||
if current_pages <= target_pages {
|
||||
return Ok(());
|
||||
}
|
||||
|
||||
if target_cap == 0 {
|
||||
if !self.layout.is_empty() {
|
||||
// SAFETY:
|
||||
// - `self.ptr` was previously allocated with `KVmalloc`.
|
||||
// - `self.layout` matches the `ArrayLayout` of the preceding allocation.
|
||||
unsafe { KVmalloc::free(self.ptr.cast(), self.layout.into()) };
|
||||
}
|
||||
self.ptr = NonNull::dangling();
|
||||
self.layout = ArrayLayout::empty();
|
||||
return Ok(());
|
||||
}
|
||||
|
||||
// SAFETY: `target_cap <= self.capacity()` and original capacity was valid.
|
||||
let new_layout = unsafe { ArrayLayout::<T>::new_unchecked(target_cap) };
|
||||
|
||||
let new_ptr = KVmalloc::alloc(new_layout.into(), flags, NumaNode::NO_NODE)?;
|
||||
|
||||
// SAFETY:
|
||||
// - `self.as_ptr()` is valid for reads of `self.len()` elements of `T`.
|
||||
// - `new_ptr` is valid for writes of at least `target_cap >= self.len()` elements.
|
||||
// - The two allocations do not overlap since `new_ptr` is freshly allocated.
|
||||
// - Both pointers are properly aligned for `T`.
|
||||
unsafe {
|
||||
ptr::copy_nonoverlapping(self.as_ptr(), new_ptr.as_ptr().cast::<T>(), self.len())
|
||||
};
|
||||
|
||||
// SAFETY:
|
||||
// - `self.ptr` was previously allocated with `KVmalloc`.
|
||||
// - `self.layout` matches the `ArrayLayout` of the preceding allocation.
|
||||
unsafe { KVmalloc::free(self.ptr.cast(), self.layout.into()) };
|
||||
|
||||
self.ptr = new_ptr.cast::<T>();
|
||||
self.layout = new_layout;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: Clone, A: Allocator> Vec<T, A> {
|
||||
/// Extend the vector by `n` clones of `value`.
|
||||
|
||||
Reference in New Issue
Block a user