mirror of
https://github.com/torvalds/linux.git
synced 2025-11-03 01:59:51 +02:00
Introduce the VmallocPageIter type; an instance of VmallocPageIter may be exposed by owners of vmalloc allocations to provide borrowed access to its backing pages. For instance, this is useful to access and borrow the backing pages of allocation primitives, such as Box and Vec, backing a scatterlist. Reviewed-by: Daniel Almeida <daniel.almeida@collabora.com> Reviewed-by: Alexandre Courbot <acourbot@nvidia.com> Tested-by: Alexandre Courbot <acourbot@nvidia.com> Reviewed-by: Alice Ryhl <aliceryhl@google.com> Suggested-by: Alice Ryhl <aliceryhl@google.com> Link: https://lore.kernel.org/r/20250820145434.94745-4-dakr@kernel.org [ Drop VmallocPageIter::base_address(), move to allocator/iter.rs and stub VmallocPageIter for allocator_test.rs. - Danilo ] Signed-off-by: Danilo Krummrich <dakr@kernel.org>
142 lines
4.8 KiB
Rust
142 lines
4.8 KiB
Rust
// SPDX-License-Identifier: GPL-2.0
|
|
|
|
//! So far the kernel's `Box` and `Vec` types can't be used by userspace test cases, since all users
|
|
//! of those types (e.g. `CString`) use kernel allocators for instantiation.
|
|
//!
|
|
//! In order to allow userspace test cases to make use of such types as well, implement the
|
|
//! `Cmalloc` allocator within the `allocator_test` module and type alias all kernel allocators to
|
|
//! `Cmalloc`. The `Cmalloc` allocator uses libc's `realloc()` function as allocator backend.
|
|
|
|
#![allow(missing_docs)]
|
|
|
|
use super::{flags::*, AllocError, Allocator, Flags};
|
|
use core::alloc::Layout;
|
|
use core::cmp;
|
|
use core::marker::PhantomData;
|
|
use core::ptr;
|
|
use core::ptr::NonNull;
|
|
use kernel::page;
|
|
|
|
/// The userspace allocator based on libc.
|
|
pub struct Cmalloc;
|
|
|
|
pub type Kmalloc = Cmalloc;
|
|
pub type Vmalloc = Kmalloc;
|
|
pub type KVmalloc = Kmalloc;
|
|
|
|
pub struct VmallocPageIter<'a> {
|
|
_p: PhantomData<page::BorrowedPage<'a>>,
|
|
}
|
|
|
|
impl<'a> Iterator for VmallocPageIter<'a> {
|
|
type Item = page::BorrowedPage<'a>;
|
|
|
|
fn next(&mut self) -> Option<Self::Item> {
|
|
None
|
|
}
|
|
}
|
|
|
|
impl<'a> VmallocPageIter<'a> {
|
|
#[allow(clippy::missing_safety_doc)]
|
|
pub unsafe fn new(_buf: NonNull<u8>, _size: usize) -> Self {
|
|
Self { _p: PhantomData }
|
|
}
|
|
|
|
pub fn size(&self) -> usize {
|
|
0
|
|
}
|
|
|
|
pub fn page_count(&self) -> usize {
|
|
0
|
|
}
|
|
}
|
|
|
|
extern "C" {
|
|
#[link_name = "aligned_alloc"]
|
|
fn libc_aligned_alloc(align: usize, size: usize) -> *mut crate::ffi::c_void;
|
|
|
|
#[link_name = "free"]
|
|
fn libc_free(ptr: *mut crate::ffi::c_void);
|
|
}
|
|
|
|
// SAFETY:
|
|
// - memory remains valid until it is explicitly freed,
|
|
// - passing a pointer to a valid memory allocation created by this `Allocator` is always OK,
|
|
// - `realloc` provides the guarantees as provided in the `# Guarantees` section.
|
|
unsafe impl Allocator for Cmalloc {
|
|
unsafe fn realloc(
|
|
ptr: Option<NonNull<u8>>,
|
|
layout: Layout,
|
|
old_layout: Layout,
|
|
flags: Flags,
|
|
) -> Result<NonNull<[u8]>, AllocError> {
|
|
let src = match ptr {
|
|
Some(src) => {
|
|
if old_layout.size() == 0 {
|
|
ptr::null_mut()
|
|
} else {
|
|
src.as_ptr()
|
|
}
|
|
}
|
|
None => ptr::null_mut(),
|
|
};
|
|
|
|
if layout.size() == 0 {
|
|
// SAFETY: `src` is either NULL or was previously allocated with this `Allocator`
|
|
unsafe { libc_free(src.cast()) };
|
|
|
|
return Ok(NonNull::slice_from_raw_parts(
|
|
crate::alloc::dangling_from_layout(layout),
|
|
0,
|
|
));
|
|
}
|
|
|
|
// ISO C (ISO/IEC 9899:2011) defines `aligned_alloc`:
|
|
//
|
|
// > The value of alignment shall be a valid alignment supported by the implementation
|
|
// [...].
|
|
//
|
|
// As an example of the "supported by the implementation" requirement, POSIX.1-2001 (IEEE
|
|
// 1003.1-2001) defines `posix_memalign`:
|
|
//
|
|
// > The value of alignment shall be a power of two multiple of sizeof (void *).
|
|
//
|
|
// and POSIX-based implementations of `aligned_alloc` inherit this requirement. At the time
|
|
// of writing, this is known to be the case on macOS (but not in glibc).
|
|
//
|
|
// Satisfy the stricter requirement to avoid spurious test failures on some platforms.
|
|
let min_align = core::mem::size_of::<*const crate::ffi::c_void>();
|
|
let layout = layout.align_to(min_align).map_err(|_| AllocError)?;
|
|
let layout = layout.pad_to_align();
|
|
|
|
// SAFETY: Returns either NULL or a pointer to a memory allocation that satisfies or
|
|
// exceeds the given size and alignment requirements.
|
|
let dst = unsafe { libc_aligned_alloc(layout.align(), layout.size()) }.cast::<u8>();
|
|
let dst = NonNull::new(dst).ok_or(AllocError)?;
|
|
|
|
if flags.contains(__GFP_ZERO) {
|
|
// SAFETY: The preceding calls to `libc_aligned_alloc` and `NonNull::new`
|
|
// guarantee that `dst` points to memory of at least `layout.size()` bytes.
|
|
unsafe { dst.as_ptr().write_bytes(0, layout.size()) };
|
|
}
|
|
|
|
if !src.is_null() {
|
|
// SAFETY:
|
|
// - `src` has previously been allocated with this `Allocator`; `dst` has just been
|
|
// newly allocated, hence the memory regions do not overlap.
|
|
// - both` src` and `dst` are properly aligned and valid for reads and writes
|
|
unsafe {
|
|
ptr::copy_nonoverlapping(
|
|
src,
|
|
dst.as_ptr(),
|
|
cmp::min(layout.size(), old_layout.size()),
|
|
)
|
|
};
|
|
}
|
|
|
|
// SAFETY: `src` is either NULL or was previously allocated with this `Allocator`
|
|
unsafe { libc_free(src.cast()) };
|
|
|
|
Ok(NonNull::slice_from_raw_parts(dst, layout.size()))
|
|
}
|
|
}
|