allocator.rs 7.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194
  1. // SPDX-License-Identifier: GPL-2.0
  2. //! Allocator support.
  3. //!
  4. //! Documentation for the kernel's memory allocators can found in the "Memory Allocation Guide"
  5. //! linked below. For instance, this includes the concept of "get free page" (GFP) flags and the
  6. //! typical application of the different kernel allocators.
  7. //!
  8. //! Reference: <https://docs.kernel.org/core-api/memory-allocation.html>
  9. use super::Flags;
  10. use core::alloc::Layout;
  11. use core::ptr;
  12. use core::ptr::NonNull;
  13. use crate::alloc::{AllocError, Allocator};
  14. use crate::bindings;
  15. use crate::pr_warn;
  16. /// The contiguous kernel allocator.
  17. ///
  18. /// `Kmalloc` is typically used for physically contiguous allocations up to page size, but also
  19. /// supports larger allocations up to `bindings::KMALLOC_MAX_SIZE`, which is hardware specific.
  20. ///
  21. /// For more details see [self].
  22. pub struct Kmalloc;
  23. /// The virtually contiguous kernel allocator.
  24. ///
  25. /// `Vmalloc` allocates pages from the page level allocator and maps them into the contiguous kernel
  26. /// virtual space. It is typically used for large allocations. The memory allocated with this
  27. /// allocator is not physically contiguous.
  28. ///
  29. /// For more details see [self].
  30. pub struct Vmalloc;
  31. /// The kvmalloc kernel allocator.
  32. ///
  33. /// `KVmalloc` attempts to allocate memory with `Kmalloc` first, but falls back to `Vmalloc` upon
  34. /// failure. This allocator is typically used when the size for the requested allocation is not
  35. /// known and may exceed the capabilities of `Kmalloc`.
  36. ///
  37. /// For more details see [self].
  38. pub struct KVmalloc;
  39. /// # Invariants
  40. ///
  41. /// One of the following: `krealloc`, `vrealloc`, `kvrealloc`.
  42. struct ReallocFunc(
  43. unsafe extern "C" fn(*const crate::ffi::c_void, usize, u32) -> *mut crate::ffi::c_void,
  44. );
  45. impl ReallocFunc {
  46. // INVARIANT: `krealloc` satisfies the type invariants.
  47. const KREALLOC: Self = Self(bindings::krealloc);
  48. // INVARIANT: `vrealloc` satisfies the type invariants.
  49. const VREALLOC: Self = Self(bindings::vrealloc);
  50. // INVARIANT: `kvrealloc` satisfies the type invariants.
  51. const KVREALLOC: Self = Self(bindings::kvrealloc);
  52. /// # Safety
  53. ///
  54. /// This method has the same safety requirements as [`Allocator::realloc`].
  55. ///
  56. /// # Guarantees
  57. ///
  58. /// This method has the same guarantees as `Allocator::realloc`. Additionally
  59. /// - it accepts any pointer to a valid memory allocation allocated by this function.
  60. /// - memory allocated by this function remains valid until it is passed to this function.
  61. unsafe fn call(
  62. &self,
  63. ptr: Option<NonNull<u8>>,
  64. layout: Layout,
  65. old_layout: Layout,
  66. flags: Flags,
  67. ) -> Result<NonNull<[u8]>, AllocError> {
  68. let size = layout.size();
  69. let ptr = match ptr {
  70. Some(ptr) => {
  71. if old_layout.size() == 0 {
  72. ptr::null()
  73. } else {
  74. ptr.as_ptr()
  75. }
  76. }
  77. None => ptr::null(),
  78. };
  79. // SAFETY:
  80. // - `self.0` is one of `krealloc`, `vrealloc`, `kvrealloc` and thus only requires that
  81. // `ptr` is NULL or valid.
  82. // - `ptr` is either NULL or valid by the safety requirements of this function.
  83. //
  84. // GUARANTEE:
  85. // - `self.0` is one of `krealloc`, `vrealloc`, `kvrealloc`.
  86. // - Those functions provide the guarantees of this function.
  87. let raw_ptr = unsafe {
  88. // If `size == 0` and `ptr != NULL` the memory behind the pointer is freed.
  89. self.0(ptr.cast(), size, flags.0).cast()
  90. };
  91. let ptr = if size == 0 {
  92. crate::alloc::dangling_from_layout(layout)
  93. } else {
  94. NonNull::new(raw_ptr).ok_or(AllocError)?
  95. };
  96. Ok(NonNull::slice_from_raw_parts(ptr, size))
  97. }
  98. }
  99. impl Kmalloc {
  100. /// Returns a [`Layout`] that makes [`Kmalloc`] fulfill the requested size and alignment of
  101. /// `layout`.
  102. pub fn aligned_layout(layout: Layout) -> Layout {
  103. // Note that `layout.size()` (after padding) is guaranteed to be a multiple of
  104. // `layout.align()` which together with the slab guarantees means that `Kmalloc` will return
  105. // a properly aligned object (see comments in `kmalloc()` for more information).
  106. layout.pad_to_align()
  107. }
  108. }
  109. // SAFETY: `realloc` delegates to `ReallocFunc::call`, which guarantees that
  110. // - memory remains valid until it is explicitly freed,
  111. // - passing a pointer to a valid memory allocation is OK,
  112. // - `realloc` satisfies the guarantees, since `ReallocFunc::call` has the same.
  113. unsafe impl Allocator for Kmalloc {
  114. #[inline]
  115. unsafe fn realloc(
  116. ptr: Option<NonNull<u8>>,
  117. layout: Layout,
  118. old_layout: Layout,
  119. flags: Flags,
  120. ) -> Result<NonNull<[u8]>, AllocError> {
  121. let layout = Kmalloc::aligned_layout(layout);
  122. // SAFETY: `ReallocFunc::call` has the same safety requirements as `Allocator::realloc`.
  123. unsafe { ReallocFunc::KREALLOC.call(ptr, layout, old_layout, flags) }
  124. }
  125. }
  126. // SAFETY: `realloc` delegates to `ReallocFunc::call`, which guarantees that
  127. // - memory remains valid until it is explicitly freed,
  128. // - passing a pointer to a valid memory allocation is OK,
  129. // - `realloc` satisfies the guarantees, since `ReallocFunc::call` has the same.
  130. unsafe impl Allocator for Vmalloc {
  131. #[inline]
  132. unsafe fn realloc(
  133. ptr: Option<NonNull<u8>>,
  134. layout: Layout,
  135. old_layout: Layout,
  136. flags: Flags,
  137. ) -> Result<NonNull<[u8]>, AllocError> {
  138. // TODO: Support alignments larger than PAGE_SIZE.
  139. if layout.align() > bindings::PAGE_SIZE {
  140. pr_warn!("Vmalloc does not support alignments larger than PAGE_SIZE yet.\n");
  141. return Err(AllocError);
  142. }
  143. // SAFETY: If not `None`, `ptr` is guaranteed to point to valid memory, which was previously
  144. // allocated with this `Allocator`.
  145. unsafe { ReallocFunc::VREALLOC.call(ptr, layout, old_layout, flags) }
  146. }
  147. }
  148. // SAFETY: `realloc` delegates to `ReallocFunc::call`, which guarantees that
  149. // - memory remains valid until it is explicitly freed,
  150. // - passing a pointer to a valid memory allocation is OK,
  151. // - `realloc` satisfies the guarantees, since `ReallocFunc::call` has the same.
  152. unsafe impl Allocator for KVmalloc {
  153. #[inline]
  154. unsafe fn realloc(
  155. ptr: Option<NonNull<u8>>,
  156. layout: Layout,
  157. old_layout: Layout,
  158. flags: Flags,
  159. ) -> Result<NonNull<[u8]>, AllocError> {
  160. // `KVmalloc` may use the `Kmalloc` backend, hence we have to enforce a `Kmalloc`
  161. // compatible layout.
  162. let layout = Kmalloc::aligned_layout(layout);
  163. // TODO: Support alignments larger than PAGE_SIZE.
  164. if layout.align() > bindings::PAGE_SIZE {
  165. pr_warn!("KVmalloc does not support alignments larger than PAGE_SIZE yet.\n");
  166. return Err(AllocError);
  167. }
  168. // SAFETY: If not `None`, `ptr` is guaranteed to point to valid memory, which was previously
  169. // allocated with this `Allocator`.
  170. unsafe { ReallocFunc::KVREALLOC.call(ptr, layout, old_layout, flags) }
  171. }
  172. }