use super::Flags;
use core::alloc::Layout;
use core::ptr;
use core::ptr::NonNull;
use crate::alloc::{AllocError, Allocator, NumaNode};
use crate::bindings;
use crate::page;
const ARCH_KMALLOC_MINALIGN: usize = bindings::ARCH_KMALLOC_MINALIGN;
mod iter;
pub use self::iter::VmallocPageIter;
pub struct Kmalloc;
pub struct Vmalloc;
pub struct KVmalloc;
struct ReallocFunc(
unsafe extern "C" fn(
*const crate::ffi::c_void,
usize,
crate::ffi::c_ulong,
u32,
crate::ffi::c_int,
) -> *mut crate::ffi::c_void,
);
impl ReallocFunc {
const KREALLOC: Self = Self(bindings::krealloc_node_align);
const VREALLOC: Self = Self(bindings::vrealloc_node_align);
const KVREALLOC: Self = Self(bindings::kvrealloc_node_align);
#[inline]
unsafe fn call(
&self,
ptr: Option<NonNull<u8>>,
layout: Layout,
old_layout: Layout,
flags: Flags,
nid: NumaNode,
) -> Result<NonNull<[u8]>, AllocError> {
let size = layout.size();
let ptr = match ptr {
Some(ptr) => {
if old_layout.size() == 0 {
ptr::null()
} else {
ptr.as_ptr()
}
}
None => ptr::null(),
};
let raw_ptr = unsafe {
self.0(ptr.cast(), size, layout.align(), flags.0, nid.0).cast()
};
let ptr = if size == 0 {
crate::alloc::dangling_from_layout(layout)
} else {
NonNull::new(raw_ptr).ok_or(AllocError)?
};
Ok(NonNull::slice_from_raw_parts(ptr, size))
}
}
impl Kmalloc {
pub fn aligned_layout(layout: Layout) -> Layout {
layout.pad_to_align()
}
}
unsafe impl Allocator for Kmalloc {
const MIN_ALIGN: usize = ARCH_KMALLOC_MINALIGN;
#[inline]
unsafe fn realloc(
ptr: Option<NonNull<u8>>,
layout: Layout,
old_layout: Layout,
flags: Flags,
nid: NumaNode,
) -> Result<NonNull<[u8]>, AllocError> {
let layout = Kmalloc::aligned_layout(layout);
unsafe { ReallocFunc::KREALLOC.call(ptr, layout, old_layout, flags, nid) }
}
}
impl Vmalloc {
pub unsafe fn to_page<'a>(ptr: NonNull<u8>) -> page::BorrowedPage<'a> {
let page = unsafe { bindings::vmalloc_to_page(ptr.as_ptr().cast()) };
let page = unsafe { NonNull::new_unchecked(page) };
unsafe { page::BorrowedPage::from_raw(page) }
}
}
unsafe impl Allocator for Vmalloc {
const MIN_ALIGN: usize = kernel::page::PAGE_SIZE;
#[inline]
unsafe fn realloc(
ptr: Option<NonNull<u8>>,
layout: Layout,
old_layout: Layout,
flags: Flags,
nid: NumaNode,
) -> Result<NonNull<[u8]>, AllocError> {
unsafe { ReallocFunc::VREALLOC.call(ptr, layout, old_layout, flags, nid) }
}
}
unsafe impl Allocator for KVmalloc {
const MIN_ALIGN: usize = ARCH_KMALLOC_MINALIGN;
#[inline]
unsafe fn realloc(
ptr: Option<NonNull<u8>>,
layout: Layout,
old_layout: Layout,
flags: Flags,
nid: NumaNode,
) -> Result<NonNull<[u8]>, AllocError> {
let layout = Kmalloc::aligned_layout(layout);
unsafe { ReallocFunc::KVREALLOC.call(ptr, layout, old_layout, flags, nid) }
}
}
#[macros::kunit_tests(rust_allocator)]
mod tests {
use super::*;
use core::mem::MaybeUninit;
use kernel::prelude::*;
#[test]
fn test_alignment() -> Result {
const TEST_SIZE: usize = 1024;
const TEST_LARGE_ALIGN_SIZE: usize = kernel::page::PAGE_SIZE * 4;
#[expect(dead_code)]
#[repr(align(128))]
struct Blob([u8; TEST_SIZE]);
#[expect(dead_code)]
#[repr(align(8192))]
struct LargeAlignBlob([u8; TEST_LARGE_ALIGN_SIZE]);
struct TestAlign<T, A: Allocator>(Box<MaybeUninit<T>, A>);
impl<T, A: Allocator> TestAlign<T, A> {
fn new() -> Result<Self> {
Ok(Self(Box::<_, A>::new_uninit(GFP_KERNEL)?))
}
fn is_aligned_to(&self, align: usize) -> bool {
assert!(align.is_power_of_two());
let addr = self.0.as_ptr() as usize;
addr & (align - 1) == 0
}
}
let ta = TestAlign::<Blob, Kmalloc>::new()?;
assert!(ta.is_aligned_to(128));
let ta = TestAlign::<LargeAlignBlob, Kmalloc>::new()?;
assert!(ta.is_aligned_to(8192));
let ta = TestAlign::<Blob, Vmalloc>::new()?;
assert!(ta.is_aligned_to(128));
let ta = TestAlign::<LargeAlignBlob, Vmalloc>::new()?;
assert!(ta.is_aligned_to(8192));
let ta = TestAlign::<Blob, KVmalloc>::new()?;
assert!(ta.is_aligned_to(128));
let ta = TestAlign::<LargeAlignBlob, KVmalloc>::new()?;
assert!(ta.is_aligned_to(8192));
Ok(())
}
}