Generic `Allocator` support for Rust
Subsystems (7)
Danilo Krummrich
maintainerv4 cover Generic `Allocator` support for Rust
Hi,
This patch series adds generic kernel allocator support for Rust, which so far is limited to kmalloc allocations.
In order to abstain from (re-)adding unstable Rust features to the kernel, this patch series does not extend the Allocator trait from Rust's alloc crate, nor does it extend the BoxExt and VecExt extensions.
Instead, this series introduces a kernel specific Allocator trait, which is implemented by the Kmalloc, Vmalloc and KVmalloc allocators, also implemented in the context of this series.
As a consequence we need our own kernel Box<T, A> and Vec<T, A> types. Additionally, this series adds the following type aliases:
``` pub type KBox<T> = Box<T, Kmalloc>; pub type VBox<T> = Box<T, Vmalloc>; pub type KVBox<T> = Box<T, KVmalloc>; pub type KVec<T> = Vec<T, Kmalloc>; pub type VVec<T> = Vec<T, Vmalloc>; pub type KVVec<T> = Vec<T, KVmalloc>; ```
With that, we can start using the kernel Box and Vec types throughout the tree and remove the now obolete extensions BoxExt and VecExt.
For a final cleanup, this series removes the last minor dependencies to Rust's alloc crate and removes it from the entire kernel build.
The series ensures not to break the rusttest make target by implementing the allocator_test module providing a stub implementation for all kernel Allocators.
This patch series passes all KUnit tests, including the ones added by this series. Additionally, the tests were run with kmemleak and KASAN enabled, without any issues.
This series is based on [1], which hit -mm/mm-unstable, and is also available in [2].
[1] https://git.kernel.org/pub/scm/linux/kernel/git/dakr/linux.git/log/?h=mm/krealloc
[2] https://git.kernel.org/pub/scm/linux/kernel/git/dakr/linux.git/log/?h=rust/mm
Changes in v4:
- (safety) comment fixes suggested by Alice and Boqun
- remove Box::from_raw_alloc and Box::into_raw_alloc, we don't need them
- in Box::drop call size_of_val before drop_in_place
- implement ForeignOwnable for Pin<Box<T>> as suggested by Alice
- in Vec::extend_with, iterate over n instead of spare.len()
- for Vmalloc and KVmalloc fail allocation for alignments larger than PAGE_SIZE for now (will add support for larger alignments in a separate series)
- implement Cmalloc in allocator_test and type alias all kernel allocator types to it, such that we can use the kernel's Box and Vec types in userspace tests (rusttest)
- this makes patch "rust: str: test: replace alloc::format" rather trivial
Changes in v3:
- Box:
- minor documentation fixes
- removed unnecessary imports in doc tests
- dropeed self argument from some remaining Box methods
- implement InPlaceInit for Box<T, A> rather than specifically for KBox<T>
- Vec:
- minor documentation fixes
- removed useless Vec::allocator method
- in Vec::extend_with use Vec::spare_capacity_mut instead of raw pointer operations
- added a few missing safety comments
- pass GFP flags to Vec::collect
- fixed a rustdoc warning in alloc.rs
- fixed the allocator_test module to implement the Allocator trait correctly
- rebased to rust-next
Changes in v2:
- preserve impl GlobalAlloc for Kmalloc and remove it at the end (Benno)
- remove &self parameter from all Allocator functions (Benno)
- various documentation fixes for Allocator (Benno)
- use NonNull<u8> for Allocator::free and Option<NonNull<u8>> for Allocator::realloc (Benno)
- fix leak of IntoIter in Vec::collect (Boqun)
- always realloc (try to shrink) in Vec::collect, it's up the the Allocator to provide a heuristic whether it makes sense to actually shrink
- rename KBox<T, A> -> Box<T, A> and KVec<T, A> -> Vec<T, A> and provide type aliases KBox<T>, VBox<T>, KVBox<T>, etc.
- This allows for much cleaner code and, in combination with removing &self parameters from Allocators, gets us rid of the need for Box::new and Box::new_alloc and all other "_alloc" postfixed functions.
- Before: KBox::new_alloc(foo, Vmalloc)?
- After: VBox::new(foo)?, which resolves to
`Box::<Foo, Vmalloc>::new(foo)?;
Danilo Krummrich (28):
rust: alloc: add Allocator trait
rust: alloc: separate aligned_size from krealloc_aligned
rust: alloc: rename KernelAllocator to Kmalloc
rust: alloc: implement Allocator for Kmalloc
rust: alloc: add module allocator_test
rust: alloc: implement Vmalloc allocator
rust: alloc: implement KVmalloc allocator
rust: types: implement Unique<T>
rust: alloc: implement kernel Box
rust: treewide: switch to our kernel Box type
rust: alloc: remove BoxExt extension
rust: alloc: add Box to prelude
rust: alloc: import kernel Box type in types.rs
rust: alloc: import kernel Box type in init.rs
rust: alloc: implement kernel Vec type
rust: alloc: implement IntoIterator for Vec
rust: alloc: implement collect for IntoIter
rust: treewide: switch to the kernel Vec type
rust: alloc: remove VecExt extension
rust: alloc: add Vec to prelude
rust: alloc: remove GlobalAlloc and krealloc_aligned
rust: error: use core::alloc::LayoutError
rust: error: check for config test in Error::name
rust: alloc: implement contains for Flags
rust: alloc: implement Cmalloc in module allocator_test
rust: str: test: replace alloc::format
rust: alloc: update module comment of alloc.rs
kbuild: rust: remove the alloc crate
rust/Makefile | 44 +- rust/exports.c | 1 - rust/helpers.c | 16 +- rust/kernel/alloc.rs | 114 +++- rust/kernel/alloc/allocator.rs | 167 ++++-- rust/kernel/alloc/allocator_test.rs | 98 ++++ rust/kernel/alloc/box_ext.rs | 56 -- rust/kernel/alloc/kbox.rs | 344 +++++++++++ rust/kernel/alloc/kvec.rs | 855 ++++++++++++++++++++++++++++ rust/kernel/alloc/vec_ext.rs | 185 ------ rust/kernel/error.rs | 6 +- rust/kernel/init.rs | 56 +- rust/kernel/init/__internal.rs | 2 +- rust/kernel/lib.rs | 1 - rust/kernel/prelude.rs | 5 +- rust/kernel/str.rs | 35 +- rust/kernel/sync/arc.rs | 17 +- rust/kernel/sync/condvar.rs | 4 +- rust/kernel/sync/lock/mutex.rs | 2 +- rust/kernel/sync/lock/spinlock.rs | 2 +- rust/kernel/sync/locked_by.rs | 2 +- rust/kernel/types.rs | 222 +++++++- rust/kernel/uaccess.rs | 15 +- rust/kernel/workqueue.rs | 20 +- samples/rust/rust_minimal.rs | 4 +- scripts/Makefile.build | 7 +- 26 files changed, 1863 insertions(+), 417 deletions(-) create mode 100644 rust/kernel/alloc/allocator_test.rs delete mode 100644 rust/kernel/alloc/box_ext.rs create mode 100644 rust/kernel/alloc/kbox.rs create mode 100644 rust/kernel/alloc/kvec.rs delete mode 100644 rust/kernel/alloc/vec_ext.rs
base-commit: c0d669a6bbe161a3afa26dea3d8491863e352e59
Signature · 1 line
2.45.2
Danilo Krummrich
maintainerPATCH v4 1/28 rust: alloc: add `Allocator` trait
Add a kernel specific Allocator trait, that in contrast to the one in Rust's core library doesn't require unstable features and supports GFP flags.
Subsequent patches add the following trait implementors: Kmalloc, Vmalloc and KVmalloc.
--- rust/kernel/alloc.rs | 79 ++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 79 insertions(+)
rust/kernel/alloc.rs +79 -0
@@ -11,6 +11,7 @@/// Indicates an allocation error. #[derive( Copy , Clone , PartialEq , Eq , Debug )] pub struct AllocError ; +use core::{alloc::Layout , ptr::NonNull };/// Flags to be used when allocating memory. ///
@@ -86,3 +87,81 @@ pub mod flags {/// small allocations. pub const GFP_NOWAIT :Flags =Flags (bindings::GFP_NOWAIT ); } + +/// The kernel's [`Allocator`] trait. +/// +/// An implementation of [`Allocator`] can allocate, re-allocate and free memory buffer described +/// via [`Layout`]. +/// +/// [`Allocator`] is designed to be implemented as a ZST; [`Allocator`] functions do not operate on +/// an object instance. +/// +/// In order to be able to support `#[derive(SmartPointer)]` later on, we need to avoid a design +/// that requires an `Allocator` to be instantiated, hence its functions must not contain any kind +/// of `self` parameter. +/// +/// # Safety +/// +/// Memory returned from an allocator must point to a valid memory buffer and remain valid until +/// it is explicitly freed. +/// +/// Any pointer to a memory buffer which is currently allocated must be valid to be passed to any +/// other [`Allocator`] function of the same type. The same applies for a NULL pointer. +/// +/// If `realloc` is called with: +/// - a size of zero, the given memory allocation, if any, must be freed +/// - a NULL pointer, a new memory allocation must be created +pub unsafe trait Allocator { +/// Allocate memory based on `layout` and `flags`. +/// +/// On success, returns a buffer represented as `NonNull<[u8]>` that satisfies the layout +/// constraints (i.e. minimum size and alignment as specified by `layout`). +/// +/// This function is equivalent to `realloc` when called with a NULL pointer. +fn alloc (layout:Layout , flags:Flags ) ->Result <NonNull <[u8 ]>,AllocError > { +// SAFETY: Passing a NULL pointer to `realloc` is valid by it's safety requirements and asks +// for a new memory allocation. +unsafe {Self ::realloc (None , layout, flags) } + } + +/// Re-allocate an existing memory allocation to satisfy the requested `layout`. If the +/// requested size is zero, `realloc` behaves equivalent to `free`. +/// +/// If the requested size is larger than the size of the existing allocation, a successful call +/// to `realloc` guarantees that the new or grown buffer has at least `Layout::size` bytes, but +/// may also be larger. +/// +/// If the requested size is smaller than the size of the existing allocation, `realloc` may or +/// may not shrink the buffer; this is implementation specific to the allocator. +/// +/// On allocation failure, the existing buffer, if any, remains valid. +/// +/// The buffer is represented as `NonNull<[u8]>`. +/// +/// # Safety +/// +/// `Some(ptr)` must point to an existing and valid memory allocation created by this allocator +/// instance. The alignment encoded in `layout` must be smaller than or equal to the alignment +/// requested in the previous `alloc` or `realloc` call of the same allocation. +/// +/// Additionally, `ptr` is allowed to be `None`; in this case a new memory allocation is +/// created. +/// +unsafe fn realloc ( + ptr:Option <NonNull <u8 >>, + layout:Layout , + flags:Flags , + ) ->Result <NonNull <[u8 ]>,AllocError >; + +/// Free an existing memory allocation. +/// +/// # Safety +/// +/// `ptr` must point to an existing and valid memory allocation created by this `Allocator` +/// instance. +unsafe fn free (ptr:NonNull <u8 >) { +// SAFETY: `ptr` is guaranteed to be previously allocated with this `Allocator` or NULL. +// Calling `realloc` with a buffer size of zero, frees the buffer `ptr` points to. +let _ =unsafe {Self ::realloc (Some (ptr),Layout ::new ::<()>(),Flags (0 )) }; + } +}
Signature · 1 line
2.45.2
Re: [PATCH v4 01/28] rust: alloc: add `Allocator` trait
Quoted 94 lines · Danilo Krummrich
Add a kernel specific
Allocatortrait, that in contrast to the one in Rust's core library doesn't require unstable features and supports GFP flags.Subsequent patches add the following trait implementors:
Kmalloc,VmallocandKVmalloc.Signed-off-by Danilo Krummrich <[email protected]>--- rust/kernel/alloc.rs | 79 ++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 79 insertions(+)rust/kernel/alloc.rs +66
-0@@ -11,6 +11,7 @@/// Indicates an allocation error. #[derive( Copy , Clone , PartialEq , Eq , Debug )] pub struct AllocError ; +use core::{alloc::Layout , ptr::NonNull };/// Flags to be used when allocating memory. /// @@ -86,3 +87,81 @@ pub mod flags {/// small allocations. pub const GFP_NOWAIT :Flags =Flags (bindings::GFP_NOWAIT ); } + +/// The kernel's [`Allocator`] trait. +/// +/// An implementation of [`Allocator`] can allocate, re-allocate and free memory buffer described +/// via [`Layout`]. +/// +/// [`Allocator`] is designed to be implemented as a ZST; [`Allocator`] functions do not operate on +/// an object instance. +/// +/// In order to be able to support `#[derive(SmartPointer)]` later on, we need to avoid a design +/// that requires an `Allocator` to be instantiated, hence its functions must not contain any kind +/// of `self` parameter. +/// +/// # Safety +/// +/// Memory returned from an allocator must point to a valid memory buffer and remain valid until +/// it is explicitly freed. +/// +/// Any pointer to a memory buffer which is currently allocated must be valid to be passed to any +/// other [`Allocator`] function of the same type. The same applies for a NULL pointer. +/// +/// If `realloc` is called with: +/// - a size of zero, the given memory allocation, if any, must be freed +/// - a NULL pointer, a new memory allocation must be created +pub unsafe trait Allocator { +/// Allocate memory based on `layout` and `flags`. +/// +/// On success, returns a buffer represented as `NonNull<[u8]>` that satisfies the layout +/// constraints (i.e. minimum size and alignment as specified by `layout`). +/// +/// This function is equivalent to `realloc` when called with a NULL pointer. +fn alloc (layout:Layout , flags:Flags ) ->Result <NonNull <[u8 ]>,AllocError > { +// SAFETY: Passing a NULL pointer to `realloc` is valid by it's safety requirements and asks +// for a new memory allocation. +unsafe {Self ::realloc (None , layout, flags) } + } + +/// Re-allocate an existing memory allocation to satisfy the requested `layout`. If the +/// requested size is zero, `realloc` behaves equivalent to `free`. +/// +/// If the requested size is larger than the size of the existing allocation, a successful call +/// to `realloc` guarantees that the new or grown buffer has at least `Layout::size` bytes, but +/// may also be larger. +/// +/// If the requested size is smaller than the size of the existing allocation, `realloc` may or +/// may not shrink the buffer; this is implementation specific to the allocator. +/// +/// On allocation failure, the existing buffer, if any, remains valid. +/// +/// The buffer is represented as `NonNull<[u8]>`. +/// +/// # Safety +/// +/// `Some(ptr)` must point to an existing and valid memory allocation created by this allocator +/// instance. The alignment encoded in `layout` must be smaller than or equal to the alignment +/// requested in the previous `alloc` or `realloc` call of the same allocation. +/// +/// Additionally, `ptr` is allowed to be `None`; in this case a new memory allocation is +/// created. +/// +unsafe fn realloc ( + ptr:Option <NonNull <u8 >>, + layout:Layout , + flags:Flags , + ) ->Result <NonNull <[u8 ]>,AllocError >;
rust/kernel/alloc.rs pub mod flags { · line 164
Quoted 10 lines
rust/kernel/alloc.rs +10
-0+ +/// Free an existing memory allocation. +/// +/// # Safety +/// +/// `ptr` must point to an existing and valid memory allocation created by this `Allocator` +/// instance. +unsafe fn free (ptr:NonNull <u8 >) { +// SAFETY: `ptr` is guaranteed to be previously allocated with this `Allocator` or NULL. +// Calling `realloc` with a buffer size of zero, frees the buffer `ptr` points to.
It can't actually be NULL since it's a NonNull. I would reword to:
// SAFETY: The caller guarantees that ptr points at a valid allocation created by this allocator. We are passing a Layout with the smallest possible alignment, so it is smaller than or equal to the alignment previously used with this allocation.
Anyway, otherwise this LGTM.
Alice
Re: [PATCH v4 01/28] rust: alloc: add `Allocator` trait
In reply to Danilo KrummrichQuoted 26 lines · Danilo Krummrich
Add a kernel specific
Allocatortrait, that in contrast to the one in Rust's core library doesn't require unstable features and supports GFP flags.Subsequent patches add the following trait implementors:
Kmalloc,VmallocandKVmalloc.Signed-off-by Danilo Krummrich <[email protected]>--- rust/kernel/alloc.rs | 79 ++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 79 insertions(+)rust/kernel/alloc.rs +1
-0@@ -11,6 +11,7 @@/// Indicates an allocation error. #[derive( Copy , Clone , PartialEq , Eq , Debug )] pub struct AllocError ; +use core::{alloc::Layout , ptr::NonNull };/// Flags to be used when allocating memory. /// @@ -86,3 +87,81 @@ pub mod flags {/// small allocations.
rust/kernel/alloc.rs pub mod flags { · line 97
Quoted 10 lines
rust/kernel/alloc.rs +8
-0pub const GFP_NOWAIT :Flags =Flags (bindings::GFP_NOWAIT ); } + +/// The kernel's [`Allocator`] trait. +/// +/// An implementation of [`Allocator`] can allocate, re-allocate and free memory buffer described +/// via [`Layout`]. +/// +/// [`Allocator`] is designed to be implemented as a ZST; [`Allocator`] functions do not operate on +/// an object instance.
This will prevent us from implementing arena-type allocators [^1]. Do we want/need those?
I have heard that some people use them in embedded systems, but I can't say for sure. But this is a rather big design decision, so we should discuss it now.
[^1]: For those who don't know what I mean by that here is a quick sketch (without handling flags and optimizations):
pub struct ArenaAlloc<const SIZE: usize> {
memory: Opaque<[u8; SIZE]>,
head: Cell<usize>,
}
impl<const SIZE: usize> ArenaAlloc<SIZE> {
pub fn new() -> Self {
Self {
memory: Opaque::uninit(),
head: 0,
}
}
}
impl<const SIZE: usize> Allocator for ArenaAlloc<SIZE> {
fn alloc(&self, layout: Layout, _flags: Flags) -> Result<NonNull<u8>, AllocError> {
let head = self.head.get();
if head + layout.size() >= SIZE {
return Err(AllocError);
}
let ptr = self.memory.get();
let ptr = ptr.cast::<u8>();
let ptr = unsafe { ptr.add(head) };
self.head.set(head + layout.size());
unsafe { NonNull::new_unchecked(ptr) }
}
unsafe fn realloc(
&self,
ptr: Option<NonNull<u8>>,
old_layout: Layout, // Note that we also need `old_layout`!
layout: Layout,
flags: Flags
) -> Result<NonNull<u8>, AllocError> {
let new = self.alloc(layout, flags)?;
let Some(ptr) = ptr else { return Ok(new); };
unsafe { core::ptr::copy_nonoverlapping(ptr.as_ptr(), new.as_ptr(), old_layout.size()) };
self.free(ptr);
Ok(new)
}
fn free(&self, ptr: NonNull<u8>) { /* noop */ }
}rust/kernel/alloc.rs pub mod flags { · line 101
Quoted 4 lines
rust/kernel/alloc.rs +4
-0+/// +/// In order to be able to support `#[derive(SmartPointer)]` later on, we need to avoid a design +/// that requires an `Allocator` to be instantiated, hence its functions must not contain any kind +/// of `self` parameter.
Ah I see, so since #[derive(SmartPointer)] needs Box to only consist of one non ZST field... I skimmed the RFC discussion and it seems like a problem that *might* be solved in the future, but probably not in the (very) near future. I guess this is just a bullet that we have to bite. We can always have an ArenaBox that can deal with that (although without DispatchFromDyn).
We should revisit this when #[derive(SmartPointer)] becomes advanced enough.
Quoted 32 lines
rust/kernel/alloc.rs +32
-0+/// +/// # Safety +/// +/// Memory returned from an allocator must point to a valid memory buffer and remain valid until +/// it is explicitly freed. +/// +/// Any pointer to a memory buffer which is currently allocated must be valid to be passed to any +/// other [`Allocator`] function of the same type. The same applies for a NULL pointer. +/// +/// If `realloc` is called with: +/// - a size of zero, the given memory allocation, if any, must be freed +/// - a NULL pointer, a new memory allocation must be created +pub unsafe trait Allocator { +/// Allocate memory based on `layout` and `flags`. +/// +/// On success, returns a buffer represented as `NonNull<[u8]>` that satisfies the layout +/// constraints (i.e. minimum size and alignment as specified by `layout`). +/// +/// This function is equivalent to `realloc` when called with a NULL pointer. +fn alloc (layout:Layout , flags:Flags ) ->Result <NonNull <[u8 ]>,AllocError > { +// SAFETY: Passing a NULL pointer to `realloc` is valid by it's safety requirements and asks +// for a new memory allocation. +unsafe {Self ::realloc (None , layout, flags) } + } + +/// Re-allocate an existing memory allocation to satisfy the requested `layout`. If the +/// requested size is zero, `realloc` behaves equivalent to `free`. +/// +/// If the requested size is larger than the size of the existing allocation, a successful call +/// to `realloc` guarantees that the new or grown buffer has at least `Layout::size` bytes, but +/// may also be larger. +///
rust/kernel/alloc.rs pub mod flags { · line 143
Quoted 10 lines
rust/kernel/alloc.rs +10
-0+/// If the requested size is smaller than the size of the existing allocation, `realloc` may or +/// may not shrink the buffer; this is implementation specific to the allocator. +/// +/// On allocation failure, the existing buffer, if any, remains valid. +/// +/// The buffer is represented as `NonNull<[u8]>`. +/// +/// # Safety +/// +/// `Some(ptr)` must point to an existing and valid memory allocation created by this allocator
This is the wrong way around, ptr: Option<NonNull<u8>>, so Some(ptr): Option<Option<NonNull<u8>>>. Instead I would write "If ptr = Some(p), then p must point to...".
Quoted 8 lines
rust/kernel/alloc.rs +8
-0+/// instance. The alignment encoded in `layout` must be smaller than or equal to the alignment +/// requested in the previous `alloc` or `realloc` call of the same allocation. +/// +/// Additionally, `ptr` is allowed to be `None`; in this case a new memory allocation is +/// created. +/// +unsafe fn realloc ( + ptr: Option<NonNull<u8>>,
rust/kernel/alloc.rs pub mod flags { · line 161
Quoted 10 lines
rust/kernel/alloc.rs +10
-0+ layout: Layout, + flags: Flags, + ) -> Result<NonNull<[u8]>, AllocError>; + +/// Free an existing memory allocation. +/// +/// # Safety +/// +/// `ptr` must point to an existing and valid memory allocation created by this `Allocator` +/// instance.
Additionally, you need "The memory allocation at ptr must never again be read from or written to.".
---
Cheers,
Benno
Quoted 9 lines
rust/kernel/alloc.rs +6
-1+unsafe fn free (ptr:NonNull <u8 >) { +// SAFETY: `ptr` is guaranteed to be previously allocated with this `Allocator` or NULL. +// Calling `realloc` with a buffer size of zero, frees the buffer `ptr` points to. +let _ =unsafe {Self ::realloc (Some (ptr),Layout ::new ::<()>(),Flags (0 )) }; + } +} --2.45.2
Danilo Krummrich
maintainerRe: [PATCH v4 01/28] rust: alloc: add `Allocator` trait
Quoted 32 lines · Benno Lossin
Quoted 31 lines · Danilo Krummrich
Add a kernel specific
Allocatortrait, that in contrast to the one in Rust's core library doesn't require unstable features and supports GFP flags.Subsequent patches add the following trait implementors:
Kmalloc,VmallocandKVmalloc.Signed-off-by Danilo Krummrich <[email protected]>--- rust/kernel/alloc.rs | 79 ++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 79 insertions(+)rust/kernel/alloc.rs +4
-0@@ -11,6 +11,7 @@/// Indicates an allocation error. #[derive( Copy , Clone , PartialEq , Eq , Debug )] pub struct AllocError ; +use core::{alloc::Layout , ptr::NonNull };/// Flags to be used when allocating memory. /// @@ -86,3 +87,81 @@ pub mod flags {/// small allocations. pub const GFP_NOWAIT :Flags =Flags (bindings::GFP_NOWAIT ); } + +/// The kernel's [`Allocator`] trait. +///
Quoted 8 lines
rust/kernel/alloc.rs pub mod flags { · line 97
Quoted 5 lines
rust/kernel/alloc.rs +5
-0+/// An implementation of [`Allocator`] can allocate, re-allocate and free memory buffer described +/// via [`Layout`]. +/// +/// [`Allocator`] is designed to be implemented as a ZST; [`Allocator`] functions do not operate on +/// an object instance. This will prevent us from implementing arena-type allocators [^1]. Do we want/need those?
I'm not aware of any code in the kernel that does exactly this, but kmem_cache is rather close to that.
Quoted 51 lines
I have heard that some people use them in embedded systems, but I can't say for sure. But this is a rather big design decision, so we should discuss it now.
[^1]: For those who don't know what I mean by that here is a quick sketch (without handling flags and optimizations):
pub struct ArenaAlloc<const SIZE: usize> { memory: Opaque<[u8; SIZE]>, head: Cell<usize>, } impl<const SIZE: usize> ArenaAlloc<SIZE> { pub fn new() -> Self { Self { memory: Opaque::uninit(), head: 0, } } } impl<const SIZE: usize> Allocator for ArenaAlloc<SIZE> { fn alloc(&self, layout: Layout, _flags: Flags) -> Result<NonNull<u8>, AllocError> { let head = self.head.get(); if head + layout.size() >= SIZE { return Err(AllocError); } let ptr = self.memory.get(); let ptr = ptr.cast::<u8>(); let ptr = unsafe { ptr.add(head) }; self.head.set(head + layout.size()); unsafe { NonNull::new_unchecked(ptr) } } unsafe fn realloc( &self, ptr: Option<NonNull<u8>>, old_layout: Layout, // Note that we also need `old_layout`! layout: Layout, flags: Flags ) -> Result<NonNull<u8>, AllocError> { let new = self.alloc(layout, flags)?; let Some(ptr) = ptr else { return Ok(new); }; unsafe { core::ptr::copy_nonoverlapping(ptr.as_ptr(), new.as_ptr(), old_layout.size()) }; self.free(ptr); Ok(new) } fn free(&self, ptr: NonNull<u8>) { /* noop */ } }
Quoted 13 lines
rust/kernel/alloc.rs pub mod flags { · line 101
Quoted 4 lines
rust/kernel/alloc.rs +4
-0+/// +/// In order to be able to support `#[derive(SmartPointer)]` later on, we need to avoid a design +/// that requires an `Allocator` to be instantiated, hence its functions must not contain any kind +/// of `self` parameter. Ah I see, so since
#[derive(SmartPointer)]needsBoxto only consist of one non ZST field... I skimmed the RFC discussion and it seems like a problem that *might* be solved in the future, but probably not in the (very) near future. I guess this is just a bullet that we have to bite. We can always have anArenaBoxthat can deal with that (although withoutDispatchFromDyn).
We should revisit this when#[derive(SmartPointer)]becomes advanced enough.
Agreed.
Quoted 39 lines
Quoted 39 lines
rust/kernel/alloc.rs +38
-0+/// +/// # Safety +/// +/// Memory returned from an allocator must point to a valid memory buffer and remain valid until +/// it is explicitly freed. +/// +/// Any pointer to a memory buffer which is currently allocated must be valid to be passed to any +/// other [`Allocator`] function of the same type. The same applies for a NULL pointer. +/// +/// If `realloc` is called with: +/// - a size of zero, the given memory allocation, if any, must be freed +/// - a NULL pointer, a new memory allocation must be created +pub unsafe trait Allocator { +/// Allocate memory based on `layout` and `flags`. +/// +/// On success, returns a buffer represented as `NonNull<[u8]>` that satisfies the layout +/// constraints (i.e. minimum size and alignment as specified by `layout`). +/// +/// This function is equivalent to `realloc` when called with a NULL pointer. +fn alloc (layout:Layout , flags:Flags ) ->Result <NonNull <[u8 ]>,AllocError > { +// SAFETY: Passing a NULL pointer to `realloc` is valid by it's safety requirements and asks +// for a new memory allocation. +unsafe {Self ::realloc (None , layout, flags) } + } + +/// Re-allocate an existing memory allocation to satisfy the requested `layout`. If the +/// requested size is zero, `realloc` behaves equivalent to `free`. +/// +/// If the requested size is larger than the size of the existing allocation, a successful call +/// to `realloc` guarantees that the new or grown buffer has at least `Layout::size` bytes, but +/// may also be larger. +/// +/// If the requested size is smaller than the size of the existing allocation, `realloc` may or +/// may not shrink the buffer; this is implementation specific to the allocator. +/// +/// On allocation failure, the existing buffer, if any, remains valid. +/// +/// The buffer is represented as `NonNull<[u8]>`.
Quoted 8 lines
rust/kernel/alloc.rs pub mod flags { · line 143
Quoted 4 lines
rust/kernel/alloc.rs +4
-0+/// +/// # Safety +/// +/// `Some(ptr)` must point to an existing and valid memory allocation created by this allocator This is the wrong way around,
ptr: Option<NonNull<u8>>, soSome(ptr): Option<Option<NonNull<u8>>>. Instead I would write "Ifptr = Some(p), thenpmust point to...".
Yes, makes sense.
Quoted 20 lines
Quoted 9 lines
rust/kernel/alloc.rs +8
-0+/// instance. The alignment encoded in `layout` must be smaller than or equal to the alignment +/// requested in the previous `alloc` or `realloc` call of the same allocation. +/// +/// Additionally, `ptr` is allowed to be `None`; in this case a new memory allocation is +/// created. +/// +unsafe fn realloc ( + ptr: Option<NonNull<u8>>,rust/kernel/alloc.rs pub mod flags { · line 161
Quoted 10 lines
rust/kernel/alloc.rs +10
-0+ layout: Layout, + flags: Flags, + ) -> Result<NonNull<[u8]>, AllocError>; + +/// Free an existing memory allocation. +/// +/// # Safety +/// +/// `ptr` must point to an existing and valid memory allocation created by this `Allocator` +/// instance.
Quoted 2 lines
Additionally, you need "The memory allocation at
ptrmust never again be read from or written to.".
I'm fine adding it, but I wonder if technically this is really required? The condition whether the pointer is ever accessed again in any way is not relevant in terms of being a precondition for free not causing UB, right?
Quoted 15 lines
---
Cheers,
BennoQuoted 9 lines
rust/kernel/alloc.rs +6
-1+unsafe fn free (ptr:NonNull <u8 >) { +// SAFETY: `ptr` is guaranteed to be previously allocated with this `Allocator` or NULL. +// Calling `realloc` with a buffer size of zero, frees the buffer `ptr` points to. +let _ =unsafe {Self ::realloc (Some (ptr),Layout ::new ::<()>(),Flags (0 )) }; + } +} --2.45.2
Re: [PATCH v4 01/28] rust: alloc: add `Allocator` trait
Quoted 110 lines · Danilo Krummrich
Quoted 32 lines · Benno Lossin
Quoted 31 lines · Danilo Krummrich
Add a kernel specific
Allocatortrait, that in contrast to the one in Rust's core library doesn't require unstable features and supports GFP flags.Subsequent patches add the following trait implementors:
Kmalloc,VmallocandKVmalloc.Signed-off-by Danilo Krummrich <[email protected]>--- rust/kernel/alloc.rs | 79 ++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 79 insertions(+)rust/kernel/alloc.rs +4
-0@@ -11,6 +11,7 @@/// Indicates an allocation error. #[derive( Copy , Clone , PartialEq , Eq , Debug )] pub struct AllocError ; +use core::{alloc::Layout , ptr::NonNull };/// Flags to be used when allocating memory. /// @@ -86,3 +87,81 @@ pub mod flags {/// small allocations. pub const GFP_NOWAIT :Flags =Flags (bindings::GFP_NOWAIT ); } + +/// The kernel's [`Allocator`] trait. +/// Quoted 8 lines
rust/kernel/alloc.rs pub mod flags { · line 97
Quoted 5 lines
rust/kernel/alloc.rs +5
-0+/// An implementation of [`Allocator`] can allocate, re-allocate and free memory buffer described +/// via [`Layout`]. +/// +/// [`Allocator`] is designed to be implemented as a ZST; [`Allocator`] functions do not operate on +/// an object instance. This will prevent us from implementing arena-type allocators [^1]. Do we want/need those?
I'm not aware of any code in the kernel that does exactly this, but
kmem_cacheis rather close to that.Quoted 51 lines
I have heard that some people use them in embedded systems, but I can't say for sure. But this is a rather big design decision, so we should discuss it now.
[^1]: For those who don't know what I mean by that here is a quick sketch (without handling flags and optimizations):
pub struct ArenaAlloc<const SIZE: usize> { memory: Opaque<[u8; SIZE]>, head: Cell<usize>, } impl<const SIZE: usize> ArenaAlloc<SIZE> { pub fn new() -> Self { Self { memory: Opaque::uninit(), head: 0, } } } impl<const SIZE: usize> Allocator for ArenaAlloc<SIZE> { fn alloc(&self, layout: Layout, _flags: Flags) -> Result<NonNull<u8>, AllocError> { let head = self.head.get(); if head + layout.size() >= SIZE { return Err(AllocError); } let ptr = self.memory.get(); let ptr = ptr.cast::<u8>(); let ptr = unsafe { ptr.add(head) }; self.head.set(head + layout.size()); unsafe { NonNull::new_unchecked(ptr) } } unsafe fn realloc( &self, ptr: Option<NonNull<u8>>, old_layout: Layout, // Note that we also need `old_layout`! layout: Layout, flags: Flags ) -> Result<NonNull<u8>, AllocError> { let new = self.alloc(layout, flags)?; let Some(ptr) = ptr else { return Ok(new); }; unsafe { core::ptr::copy_nonoverlapping(ptr.as_ptr(), new.as_ptr(), old_layout.size()) }; self.free(ptr); Ok(new) } fn free(&self, ptr: NonNull<u8>) { /* noop */ } }Quoted 13 lines
rust/kernel/alloc.rs pub mod flags { · line 101
Quoted 4 lines
rust/kernel/alloc.rs +4
-0+/// +/// In order to be able to support `#[derive(SmartPointer)]` later on, we need to avoid a design +/// that requires an `Allocator` to be instantiated, hence its functions must not contain any kind +/// of `self` parameter. Ah I see, so since
#[derive(SmartPointer)]needsBoxto only consist of one non ZST field... I skimmed the RFC discussion and it seems like a problem that *might* be solved in the future, but probably not in the (very) near future. I guess this is just a bullet that we have to bite. We can always have anArenaBoxthat can deal with that (although withoutDispatchFromDyn).
We should revisit this when#[derive(SmartPointer)]becomes advanced enough.
Quoted 1 line
Agreed.
I opened https://github.com/Rust-for-Linux/linux/issues/1095 to track this.
Quoted 69 lines
Quoted 38 lines
Quoted 38 lines
rust/kernel/alloc.rs +38
-0+/// +/// # Safety +/// +/// Memory returned from an allocator must point to a valid memory buffer and remain valid until +/// it is explicitly freed. +/// +/// Any pointer to a memory buffer which is currently allocated must be valid to be passed to any +/// other [`Allocator`] function of the same type. The same applies for a NULL pointer. +/// +/// If `realloc` is called with: +/// - a size of zero, the given memory allocation, if any, must be freed +/// - a NULL pointer, a new memory allocation must be created +pub unsafe trait Allocator { +/// Allocate memory based on `layout` and `flags`. +/// +/// On success, returns a buffer represented as `NonNull<[u8]>` that satisfies the layout +/// constraints (i.e. minimum size and alignment as specified by `layout`). +/// +/// This function is equivalent to `realloc` when called with a NULL pointer. +fn alloc (layout:Layout , flags:Flags ) ->Result <NonNull <[u8 ]>,AllocError > { +// SAFETY: Passing a NULL pointer to `realloc` is valid by it's safety requirements and asks +// for a new memory allocation. +unsafe {Self ::realloc (None , layout, flags) } + } + +/// Re-allocate an existing memory allocation to satisfy the requested `layout`. If the +/// requested size is zero, `realloc` behaves equivalent to `free`. +/// +/// If the requested size is larger than the size of the existing allocation, a successful call +/// to `realloc` guarantees that the new or grown buffer has at least `Layout::size` bytes, but +/// may also be larger. +/// +/// If the requested size is smaller than the size of the existing allocation, `realloc` may or +/// may not shrink the buffer; this is implementation specific to the allocator. +/// +/// On allocation failure, the existing buffer, if any, remains valid. +/// +/// The buffer is represented as `NonNull<[u8]>`. Quoted 8 lines
rust/kernel/alloc.rs pub mod flags { · line 143
Quoted 4 lines
rust/kernel/alloc.rs +4
-0+/// +/// # Safety +/// +/// `Some(ptr)` must point to an existing and valid memory allocation created by this allocator This is the wrong way around,
ptr: Option<NonNull<u8>>, soSome(ptr): Option<Option<NonNull<u8>>>. Instead I would write "Ifptr = Some(p), thenpmust point to...".Yes, makes sense.
Quoted 20 lines
Quoted 19 lines
rust/kernel/alloc.rs +18
-0+/// instance. The alignment encoded in `layout` must be smaller than or equal to the alignment +/// requested in the previous `alloc` or `realloc` call of the same allocation. +/// +/// Additionally, `ptr` is allowed to be `None`; in this case a new memory allocation is +/// created. +/// +unsafe fn realloc ( + ptr:Option <NonNull <u8 >>, + layout:Layout , + flags:Flags , + ) ->Result <NonNull <[u8 ]>,AllocError >; + +/// Free an existing memory allocation. +/// +/// # Safety +/// +/// `ptr` must point to an existing and valid memory allocation created by this `Allocator` +/// instance.
Quoted 6 lines
Quoted 2 lines
Additionally, you need "The memory allocation at
ptrmust never again be read from or written to.".I'm fine adding it, but I wonder if technically this is really required? The condition whether the pointer is ever accessed again in any way is not relevant in terms of being a precondition for
freenot causing UB, right?
I don't see how else we would find the mistake in the following code:
let ptr = Box::into_raw(Box::<i32, Kmalloc>::new(42));
// SAFETY: `ptr` came from `Box::into_raw` and thus is pointing to a
// valid and existing memory allocation allocated by `Kmalloc`.
unsafe { Kmalloc::free(ptr) };
// SAFETY: `ptr` came from `Box::into_raw` and thus is pointing at a
// valid `i32`.
let v = unsafe { ptr.read() };Also see the from_raw for our Arc:
/// Recreates an [`Arc`] instance previously deconstructed via [`Arc::into_raw`].
///
/// # Safety
///
/// `ptr` must have been returned by a previous call to [`Arc::into_raw`]. Additionally, it
/// must not be called more than once for each previous call to [`Arc::into_raw`].
pub unsafe fn from_raw(ptr: *const T) -> Self {That also requires that the function must not be called more than once. This reminds me, I forgot to say that about Box::from_raw.
---
Cheers,
Benno
Danilo Krummrich
maintainerRe: [PATCH v4 01/28] rust: alloc: add `Allocator` trait
Quoted 28 lines · Benno Lossin
Quoted 19 lines
Quoted 19 lines
Quoted 18 lines
rust/kernel/alloc.rs +18
-0+/// instance. The alignment encoded in `layout` must be smaller than or equal to the alignment +/// requested in the previous `alloc` or `realloc` call of the same allocation. +/// +/// Additionally, `ptr` is allowed to be `None`; in this case a new memory allocation is +/// created. +/// +unsafe fn realloc ( + ptr:Option <NonNull <u8 >>, + layout:Layout , + flags:Flags , + ) ->Result <NonNull <[u8 ]>,AllocError >; + +/// Free an existing memory allocation. +/// +/// # Safety +/// +/// `ptr` must point to an existing and valid memory allocation created by this `Allocator` +/// instance. Quoted 6 lines
Quoted 2 lines
Additionally, you need "The memory allocation at
ptrmust never again be read from or written to.".I'm fine adding it, but I wonder if technically this is really required? The condition whether the pointer is ever accessed again in any way is not relevant in terms of being a precondition for
freenot causing UB, right?I don't see how else we would find the mistake in the following code:
Quoted 7 lines
let ptr = Box::into_raw(Box::<i32, Kmalloc>::new(42));
// SAFETY: `ptr` came from `Box::into_raw` and thus is pointing to a
// valid and existing memory allocation allocated by `Kmalloc`.
unsafe { Kmalloc::free(ptr) };
// SAFETY: `ptr` came from `Box::into_raw` and thus is pointing at a
// valid `i32`.
let v = unsafe { ptr.read() };Sure, but what I mean is that my understanding is that the "Safety" section in a comment describes the requirements of the function it documents. I.e. free itself doesn't care whether the pointer is read or writted ever again.
Or in other words, what are the rules where this belongs to? E.g. why not document this exact aspect in the safety section of Allocator?
Quoted 11 lines
Also see the
from_rawfor ourArc:/// Recreates an [`Arc`] instance previously deconstructed via [`Arc::into_raw`]. /// /// # Safety /// /// `ptr` must have been returned by a previous call to [`Arc::into_raw`]. Additionally, it /// must not be called more than once for each previous call to [`Arc::into_raw`]. pub unsafe fn from_raw(ptr: *const T) -> Self {
Quoted 2 lines
That also requires that the function must not be called more than once. This reminds me, I forgot to say that about
Box::from_raw.
Indeed, I also wonder if we ever have cases where C code gives us ownership of a memory allocation of a certain type that fulfills the requirements we have for a Box, such that Rust code is tempted to pass it to Box::from_raw.
It sounds a bit scary design wise, but in theory it's possible.
Quoted 5 lines
---
Cheers,
Benno
Re: [PATCH v4 01/28] rust: alloc: add `Allocator` trait
Quoted 37 lines · Danilo Krummrich
Quoted 28 lines · Benno Lossin
Quoted 19 lines
Quoted 19 lines
Quoted 18 lines
rust/kernel/alloc.rs +18
-0+/// instance. The alignment encoded in `layout` must be smaller than or equal to the alignment +/// requested in the previous `alloc` or `realloc` call of the same allocation. +/// +/// Additionally, `ptr` is allowed to be `None`; in this case a new memory allocation is +/// created. +/// +unsafe fn realloc ( + ptr:Option <NonNull <u8 >>, + layout:Layout , + flags:Flags , + ) ->Result <NonNull <[u8 ]>,AllocError >; + +/// Free an existing memory allocation. +/// +/// # Safety +/// +/// `ptr` must point to an existing and valid memory allocation created by this `Allocator` +/// instance. Quoted 6 lines
Quoted 2 lines
Additionally, you need "The memory allocation at
ptrmust never again be read from or written to.".I'm fine adding it, but I wonder if technically this is really required? The condition whether the pointer is ever accessed again in any way is not relevant in terms of being a precondition for
freenot causing UB, right?I don't see how else we would find the mistake in the following code:
Quoted 7 lines
let ptr = Box::into_raw(Box::<i32, Kmalloc>::new(42)); // SAFETY: `ptr` came from `Box::into_raw` and thus is pointing to a // valid and existing memory allocation allocated by `Kmalloc`. unsafe { Kmalloc::free(ptr) }; // SAFETY: `ptr` came from `Box::into_raw` and thus is pointing at a // valid `i32`. let v = unsafe { ptr.read() };
Quoted 3 lines
Sure, but what I mean is that my understanding is that the "Safety" section in a comment describes the requirements of the function it documents. I.e.
freeitself doesn't care whether the pointer is read or writted ever again.
So if you have an unsafe function, the safety requirements are not limited to ensuring that that function does not exhibit UB. But in general any correct usage of that function must not exhibit UB in combination with any other correct usage of any other unsafe functions.
Quoted 2 lines
Or in other words, what are the rules where this belongs to? E.g. why not document this exact aspect in the safety section of
Allocator?
That doesn't work, since the Safety section of Allocator is meant for safety requirements for implementing Allocator. They should not be relevant for using it.
Quoted 10 lines
Quoted 10 lines
Also see the
from_rawfor ourArc:/// Recreates an [`Arc`] instance previously deconstructed via [`Arc::into_raw`]. /// /// # Safety /// /// `ptr` must have been returned by a previous call to [`Arc::into_raw`]. Additionally, it /// must not be called more than once for each previous call to [`Arc::into_raw`]. pub unsafe fn from_raw(ptr: *const T) -> Self {
Quoted 8 lines
Quoted 2 lines
That also requires that the function must not be called more than once. This reminds me, I forgot to say that about
Box::from_raw.Indeed, I also wonder if we ever have cases where C code gives us ownership of a memory allocation of a certain type that fulfills the requirements we have for a
Box, such that Rust code is tempted to pass it toBox::from_raw.It sounds a bit scary design wise, but in theory it's possible.
I don't think it's scary, in fact we are already doing it and have a trait for that: ForeignOwnable. But that means that the pointer originates from Rust, so it might not exactly be what you meant ie C allocates the memory. But that case also doesn't seem too scary for me. C just needs to respect alignment and then it should be equivalent to Rust calling the allocator.
---
Cheers,
Benno
Gary Guo
maintainerRe: [PATCH v4 01/28] rust: alloc: add `Allocator` trait
In reply to Danilo KrummrichQuoted 8 lines · On Mon, 5 Aug 2024 17:19:20 +0200 Danilo Krummrich <[email protected]> wrote:
Add a kernel specific
Allocatortrait, that in contrast to the one in Rust's core library doesn't require unstable features and supports GFP flags.Subsequent patches add the following trait implementors:
Kmalloc,VmallocandKVmalloc.Signed-off-by Danilo Krummrich <[email protected]>
I agree with Alice's comments that mentioning of NULL is confusing and should be changed to refer to None.
Some extra nits below.
Best,
Gary
Quoted 65 lines
--- rust/kernel/alloc.rs | 79 ++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 79 insertions(+)rust/kernel/alloc.rs +46
-0@@ -11,6 +11,7 @@/// Indicates an allocation error. #[derive( Copy , Clone , PartialEq , Eq , Debug )] pub struct AllocError ; +use core::{alloc::Layout , ptr::NonNull };/// Flags to be used when allocating memory. /// @@ -86,3 +87,81 @@ pub mod flags {/// small allocations. pub const GFP_NOWAIT :Flags =Flags (bindings::GFP_NOWAIT ); } + +/// The kernel's [`Allocator`] trait. +/// +/// An implementation of [`Allocator`] can allocate, re-allocate and free memory buffer described +/// via [`Layout`]. +/// +/// [`Allocator`] is designed to be implemented as a ZST; [`Allocator`] functions do not operate on +/// an object instance. +/// +/// In order to be able to support `#[derive(SmartPointer)]` later on, we need to avoid a design +/// that requires an `Allocator` to be instantiated, hence its functions must not contain any kind +/// of `self` parameter. +/// +/// # Safety +/// +/// Memory returned from an allocator must point to a valid memory buffer and remain valid until +/// it is explicitly freed. +/// +/// Any pointer to a memory buffer which is currently allocated must be valid to be passed to any +/// other [`Allocator`] function of the same type. The same applies for a NULL pointer. +/// +/// If `realloc` is called with: +/// - a size of zero, the given memory allocation, if any, must be freed +/// - a NULL pointer, a new memory allocation must be created +pub unsafe trait Allocator { +/// Allocate memory based on `layout` and `flags`. +/// +/// On success, returns a buffer represented as `NonNull<[u8]>` that satisfies the layout +/// constraints (i.e. minimum size and alignment as specified by `layout`). +/// +/// This function is equivalent to `realloc` when called with a NULL pointer. +fn alloc (layout:Layout , flags:Flags ) ->Result <NonNull <[u8 ]>,AllocError > { +// SAFETY: Passing a NULL pointer to `realloc` is valid by it's safety requirements and asks +// for a new memory allocation. +unsafe {Self ::realloc (None , layout, flags) } + } + +/// Re-allocate an existing memory allocation to satisfy the requested `layout`. If the +/// requested size is zero, `realloc` behaves equivalent to `free`. +/// +/// If the requested size is larger than the size of the existing allocation, a successful call +/// to `realloc` guarantees that the new or grown buffer has at least `Layout::size` bytes, but +/// may also be larger. +/// +/// If the requested size is smaller than the size of the existing allocation, `realloc` may or
rust/kernel/alloc.rs pub mod flags { · line 144
Quoted 10 lines
rust/kernel/alloc.rs +10
-0+/// may not shrink the buffer; this is implementation specific to the allocator. +/// +/// On allocation failure, the existing buffer, if any, remains valid. +/// +/// The buffer is represented as `NonNull<[u8]>`. +/// +/// # Safety +/// +/// `Some(ptr)` must point to an existing and valid memory allocation created by this allocator +/// instance. The alignment encoded in `layout` must be smaller than or equal to the alignment
nit: remove instance since the methods now have no self parameter.
Quoted 7 lines
rust/kernel/alloc.rs +7
-0+/// requested in the previous `alloc` or `realloc` call of the same allocation. +/// +/// Additionally, `ptr` is allowed to be `None`; in this case a new memory allocation is +/// created. +/// +unsafe fn realloc ( + ptr: Option<NonNull<u8>>,
rust/kernel/alloc.rs pub mod flags { · line 161
Quoted 10 lines
rust/kernel/alloc.rs +10
-0+ layout: Layout, + flags: Flags, + ) -> Result<NonNull<[u8]>, AllocError>; + +/// Free an existing memory allocation. +/// +/// # Safety +/// +/// `ptr` must point to an existing and valid memory allocation created by this `Allocator` +/// instance.
same here.
Quoted 6 lines
rust/kernel/alloc.rs +6
-0+unsafe fn free (ptr:NonNull <u8 >) { +// SAFETY: `ptr` is guaranteed to be previously allocated with this `Allocator` or NULL. +// Calling `realloc` with a buffer size of zero, frees the buffer `ptr` points to. +let _ =unsafe {Self ::realloc (Some (ptr),Layout ::new ::<()>(),Flags (0 )) }; + } +}
Danilo Krummrich
maintainerPATCH v4 2/28 rust: alloc: separate `aligned_size` from `krealloc_aligned`
In reply to Danilo KrummrichSeparate aligned_size from krealloc_aligned.
Subsequent patches implement Allocator derivates, such as Kmalloc, that require aligned_size and replace the original krealloc_aligned.
--- rust/kernel/alloc/allocator.rs | 27 ++++++++++++++++++--------- 1 file changed, 18 insertions(+), 9 deletions(-)
rust/kernel/alloc/allocator.rs +18 -9
@@ -8,27 +8,36 @@struct KernelAllocator ; -/// Calls `krealloc` with a proper size to alloc a new object aligned to `new_layout`'s alignment. -/// -/// # Safety -/// -/// - `ptr` can be either null or a pointer which has been allocated by this allocator. -/// - `new_layout` must have a non-zero size. -pub (crate )unsafe fn krealloc_aligned (ptr: *mut u8 , new_layout:Layout , flags:Flags ) -> *mut u8 { +/// Returns a proper size to alloc a new object aligned to `new_layout`'s alignment. +fn aligned_size (new_layout:Layout ) ->usize {// Customized layouts from `Layout::from_size_align()` can have size < align, so pad first. let layout = new_layout.pad_to_align ();// Note that `layout.size()` (after padding) is guaranteed to be a multiple of `layout.align()` // which together with the slab guarantees means the `krealloc` will return a properly aligned // object (see comments in `kmalloc()` for more information). -let size = layout.size (); + layout.size () +} +/// Calls `krealloc` with a proper size to alloc a new object aligned to `new_layout`'s alignment. +/// +/// # Safety +/// +/// - `ptr` can be either null or a pointer which has been allocated by this allocator. +/// - `new_layout` must have a non-zero size. +pub (crate )unsafe fn krealloc_aligned (ptr: *mut u8 , new_layout:Layout , flags:Flags ) -> *mut u8 {// SAFETY: // - `ptr` is either null or a pointer returned from a previous `k{re}alloc()` by the // function safety requirement. // - `size` is greater than 0 since it's from `layout.size()` (which cannot be zero according // to the function safety requirement) -unsafe { bindings::krealloc (ptras *const core::ffi::c_void , size, flags.0 )as *mut u8 } +unsafe { + bindings::krealloc ( + ptras *const core::ffi::c_void , +aligned_size (new_layout), + flags.0 , + )as *mut u8 + } }unsafe impl GlobalAlloc for KernelAllocator {
Signature · 1 line
2.45.2
Re: [PATCH v4 02/28] rust: alloc: separate `aligned_size` from `krealloc_aligned`
Quoted 45 lines · Danilo Krummrich
Separate
aligned_sizefromkrealloc_aligned.Subsequent patches implement
Allocatorderivates, such asKmalloc, that requirealigned_sizeand replace the originalkrealloc_aligned.Reviewed-by Alice Ryhl <[email protected]>Signed-off-by Danilo Krummrich <[email protected]>--- rust/kernel/alloc/allocator.rs | 27 ++++++++++++++++++--------- 1 file changed, 18 insertions(+), 9 deletions(-)rust/kernel/alloc/allocator.rs +11
-8@@ -8,27 +8,36 @@struct KernelAllocator ; -/// Calls `krealloc` with a proper size to alloc a new object aligned to `new_layout`'s alignment. -/// -/// # Safety -/// -/// - `ptr` can be either null or a pointer which has been allocated by this allocator. -/// - `new_layout` must have a non-zero size. -pub (crate )unsafe fn krealloc_aligned (ptr: *mut u8 , new_layout:Layout , flags:Flags ) -> *mut u8 { +/// Returns a proper size to alloc a new object aligned to `new_layout`'s alignment. +fn aligned_size (new_layout:Layout ) ->usize {// Customized layouts from `Layout::from_size_align()` can have size < align, so pad first. let layout = new_layout.pad_to_align ();// Note that `layout.size()` (after padding) is guaranteed to be a multiple of `layout.align()` // which together with the slab guarantees means the `krealloc` will return a properly aligned // object (see comments in `kmalloc()` for more information). -let size = layout.size (); + layout.size () +} +/// Calls `krealloc` with a proper size to alloc a new object aligned to `new_layout`'s alignment. +/// +/// # Safety +/// +/// - `ptr` can be either null or a pointer which has been allocated by this allocator. +/// - `new_layout` must have a non-zero size. +pub (crate )unsafe fn krealloc_aligned (ptr: *mut u8 , new_layout:Layout , flags:Flags ) -> *mut u8 {
rust/kernel/alloc/allocator.rs line 37
Quoted 10 lines
rust/kernel/alloc/allocator.rs +4
-1// SAFETY: // - `ptr` is either null or a pointer returned from a previous `k{re}alloc()` by the // function safety requirement. // - `size` is greater than 0 since it's from `layout.size()` (which cannot be zero according // to the function safety requirement) -unsafe { bindings::krealloc (ptras *const core::ffi::c_void , size, flags.0 )as *mut u8 } +unsafe { + bindings::krealloc ( + ptras *const core::ffi::c_void , + aligned_size(new_layout),
Can you move the safe operation outside of the unsafe block?
With that changed,
---
Cheers,
Benno
Quoted 9 lines
rust/kernel/alloc/allocator.rs +3
-1+ flags.0, + ) as *mut u8 + } }unsafe impl GlobalAlloc for KernelAllocator { --2.45.2
Danilo Krummrich
maintainerPATCH v4 3/28 rust: alloc: rename `KernelAllocator` to `Kmalloc`
In reply to Danilo KrummrichSubsequent patches implement Vmalloc and KVmalloc allocators, hence align KernelAllocator to this naming scheme.
--- rust/kernel/alloc/allocator.rs | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-)
rust/kernel/alloc/allocator.rs +3 -3
@@ -6,7 +6,7 @@use core::alloc::{GlobalAlloc ,Layout };use core::ptr; -struct KernelAllocator ; +struct Kmalloc ;/// Returns a proper size to alloc a new object aligned to `new_layout`'s alignment. fn aligned_size (new_layout:Layout ) ->usize {
@@ -40,7 +40,7 @@ pub(crate) unsafe fn krealloc_aligned(ptr: *mut u8, new_layout: Layout, flags: F } } -unsafe impl GlobalAlloc for KernelAllocator { +unsafe impl GlobalAlloc for Kmalloc {unsafe fn alloc (&self, layout:Layout ) -> *mut u8 {// SAFETY: `ptr::null_mut()` is null and `layout` has a non-zero size by the function safety // requirement.
@@ -76,7 +76,7 @@ unsafe fn alloc_zeroed(&self, layout: Layout) -> *mut u8 { }#[global_allocator] -static ALLOCATOR :KernelAllocator =KernelAllocator ; +static ALLOCATOR :Kmalloc =Kmalloc ;// See <https://github.com/rust-lang/rust/pull/86844>. #[no_mangle]
Signature · 1 line
2.45.2
Re: [PATCH v4 03/28] rust: alloc: rename `KernelAllocator` to `Kmalloc`
Quoted 8 lines · Danilo Krummrich
Subsequent patches implement
VmallocandKVmallocallocators, hence alignKernelAllocatorto this naming scheme.Reviewed-by Alice Ryhl <[email protected]>Signed-off-by Danilo Krummrich <[email protected]>--- rust/kernel/alloc/allocator.rs | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-)
---
Cheers,
Benno
Re: [PATCH v4 03/28] rust: alloc: rename `KernelAllocator` to `Kmalloc`
In reply to Danilo KrummrichQuoted 5 lines · On Mon, 5 Aug 2024 17:19:22 +0200 Danilo Krummrich <[email protected]> wrote:
Subsequent patches implement
VmallocandKVmallocallocators, hence alignKernelAllocatorto this naming scheme.Reviewed-by Alice Ryhl <[email protected]>Signed-off-by Danilo Krummrich <[email protected]>
Quoted 35 lines
--- rust/kernel/alloc/allocator.rs | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-)rust/kernel/alloc/allocator.rs +3
-3@@ -6,7 +6,7 @@use core::alloc::{GlobalAlloc ,Layout };use core::ptr; -struct KernelAllocator ; +struct Kmalloc ;/// Returns a proper size to alloc a new object aligned to `new_layout`'s alignment. fn aligned_size (new_layout:Layout ) ->usize {@@ -40,7 +40,7 @@ pub(crate) unsafe fn krealloc_aligned(ptr: *mut u8, new_layout: Layout, flags: F } } -unsafe impl GlobalAlloc for KernelAllocator { +unsafe impl GlobalAlloc for Kmalloc {unsafe fn alloc (&self, layout:Layout ) -> *mut u8 {// SAFETY: `ptr::null_mut()` is null and `layout` has a non-zero size by the function safety // requirement. @@ -76,7 +76,7 @@ unsafe fn alloc_zeroed(&self, layout: Layout) -> *mut u8 { }#[global_allocator] -static ALLOCATOR :KernelAllocator =KernelAllocator ; +static ALLOCATOR :Kmalloc =Kmalloc ;// See <https://github.com/rust-lang/rust/pull/86844>. #[no_mangle]
Danilo Krummrich
maintainerPATCH v4 4/28 rust: alloc: implement `Allocator` for `Kmalloc`
In reply to Danilo KrummrichImplement Allocator for Kmalloc, the kernel's default allocator, typically used for objects smaller than page size.
All memory allocations made with Kmalloc end up in krealloc().
It serves as allocator for the subsequently introduced types KBox and KVec.
--- rust/helpers.c | 3 +- rust/kernel/alloc.rs | 2 +- rust/kernel/alloc/allocator.rs | 68 +++++++++++++++++++++++++++++++++- 3 files changed, 69 insertions(+), 4 deletions(-)
rust/helpers.c +1 -2
@@ -193,8 +193,7 @@ void rust_helper_init_work_with_key(struct work_struct *work, work_func_t func, }EXPORT_SYMBOL_GPL (rust_helper_init_work_with_key); -void * __must_check __realloc_size(2) -rust_helper_krealloc(const void *objp, size_t new_size, gfp_t flags) +void *rust_helper_krealloc (const void *objp,size_t new_size,gfp_t flags) {return krealloc (objp, new_size, flags); }
rust/kernel/alloc.rs +1 -1
@@ -4,7 +4,7 @@#[cfg(not(test))] #[cfg(not(testlib))] -mod allocator; +pub mod allocator;pub mod box_ext;pub mod vec_ext;
rust/kernel/alloc/allocator.rs +67 -1
81 lines not on this page · raw message
Signature · 1 line
2.45.2
Re: [PATCH v4 04/28] rust: alloc: implement `Allocator` for `Kmalloc`
Quoted 67 lines · Danilo Krummrich
Implement
AllocatorforKmalloc, the kernel's default allocator, typically used for objects smaller than page size.All memory allocations made with
Kmallocend up inkrealloc().It serves as allocator for the subsequently introduced types
KBoxandKVec.Signed-off-by Danilo Krummrich <[email protected]>--- rust/helpers.c | 3 +- rust/kernel/alloc.rs | 2 +- rust/kernel/alloc/allocator.rs | 68 +++++++++++++++++++++++++++++++++- 3 files changed, 69 insertions(+), 4 deletions(-)rust/helpers.c +1
-2@@ -193,8 +193,7 @@ void rust_helper_init_work_with_key(struct work_struct *work, work_func_t func, }EXPORT_SYMBOL_GPL (rust_helper_init_work_with_key); -void * __must_check __realloc_size(2) -rust_helper_krealloc(const void *objp, size_t new_size, gfp_t flags) +void *rust_helper_krealloc (const void *objp,size_t new_size,gfp_t flags) {return krealloc (objp, new_size, flags); }rust/kernel/alloc.rs +1
-1@@ -4,7 +4,7 @@#[cfg(not(test))] #[cfg(not(testlib))] -mod allocator; +pub mod allocator;pub mod box_ext;pub mod vec_ext;rust/kernel/alloc/allocator.rs +9
-1@@ -5,8 +5,16 @@use super ::{flags::*,Flags };use core::alloc::{GlobalAlloc ,Layout };use core::ptr; +use core::ptr::NonNull ; -struct Kmalloc ; +use crate ::alloc::{AllocError ,Allocator }; +use crate ::bindings; + +/// The contiguous kernel allocator. +/// +/// The contiguous kernel allocator only ever allocates physically contiguous memory through +/// `bindings::krealloc`. +pub struct Kmalloc ;/// Returns a proper size to alloc a new object aligned to `new_layout`'s alignment. fn aligned_size (new_layout:Layout ) ->usize {@@ -40,6 +48,64 @@ pub(crate) unsafe fn krealloc_aligned(ptr: *mut u8, new_layout: Layout, flags: F } }
rust/kernel/alloc/allocator.rs pub(crate) unsafe fn krealloc_aligned(ptr: *mut u8, new_layout: Layout, flags: F · line 59
Quoted 10 lines
rust/kernel/alloc/allocator.rs +9
-0+/// # Invariants +/// +/// One of the following `krealloc`, `vrealloc`, `kvrealloc`. +struct ReallocFunc ( +unsafe extern "C" fn (*const core::ffi::c_void ,usize ,u32 ) -> *mut core::ffi::c_void , +); + +impl ReallocFunc { +// INVARIANT: `krealloc` satisfies the type invariants.
This INVARIANT comment should be moved one line downwards.
rust/kernel/alloc/allocator.rs pub(crate) unsafe fn krealloc_aligned(ptr: *mut u8, new_layout: Layout, flags: F · line 66
Quoted 7 lines
rust/kernel/alloc/allocator.rs +7
-0+fn krealloc () ->Self { +Self (bindings::krealloc) + } + +/// # Safety +/// +/// This method has the exact same safety requirements as `Allocator::realloc`.
I would remove "exact", I don't think we want to mean "almost the same" when we write just "same".
rust/kernel/alloc/allocator.rs pub(crate) unsafe fn krealloc_aligned(ptr: *mut u8, new_layout: Layout, flags: F · line 79
Quoted 13 lines
rust/kernel/alloc/allocator.rs +13
-0+unsafe fn call ( + &self, + ptr:Option <NonNull <u8 >>, + layout:Layout , + flags:Flags , + ) ->Result <NonNull <[u8 ]>,AllocError > { +let size =aligned_size (layout); +let ptr =match ptr { +Some (ptr) => ptr.as_ptr (), +None => ptr::null (), + }; + +// SAFETY: `ptr` is valid by the safety requirements of this function.
"ptr is either NULL or valid by the safety requirements of this function."
rust/kernel/alloc/allocator.rs pub(crate) unsafe fn krealloc_aligned(ptr: *mut u8, new_layout: Layout, flags: F · line 86
Quoted 7 lines
rust/kernel/alloc/allocator.rs +7
-0+let raw_ptr =unsafe { +// If `size == 0` and `ptr != NULL` the memory behind the pointer is freed. + self.0 (ptr.cast (), size, flags.0 ).cast () + }; + +let ptr =if size ==0 { +NonNull ::dangling ()
If we call realloc(Some(ptr), <layout with size = 0>, ...), then this leaks the pointer returned by the call to self.0 above. I don't know what the return value of the different functions are that can appear in self.0, do they return NULL?
What about the following sequence:
let ptr = realloc(None, <layout with size = 0>, ...);
let ptr = realloc(Some(ptr), <layout with size = 0>, ...);Then the above call to self.0 is done with a dangling pointer, can the functions that appear in self.0 handle that?
Quoted 8 lines
rust/kernel/alloc/allocator.rs +8
-0+ }else { +NonNull ::new (raw_ptr).ok_or (AllocError )? + }; + +Ok (NonNull ::slice_from_raw_parts (ptr, size)) + } +} +
rust/kernel/alloc/allocator.rs pub(crate) unsafe fn krealloc_aligned(ptr: *mut u8, new_layout: Layout, flags: F · line 104
Quoted 10 lines
rust/kernel/alloc/allocator.rs +10
-0+unsafe impl Allocator for Kmalloc { +unsafe fn realloc ( + ptr:Option <NonNull <u8 >>, + layout:Layout , + flags:Flags , + ) ->Result <NonNull <[u8 ]>,AllocError > { +let realloc =ReallocFunc ::krealloc (); + +// SAFETY: If not `None`, `ptr` is guaranteed to point to valid memory, which was previously +// allocated with this `Allocator`.
What about the other requirements? (they should be satisfied, since they are also requirements for calling this function)
rust/kernel/alloc/allocator.rs pub(crate) unsafe fn krealloc_aligned(ptr: *mut u8, new_layout: Layout, flags: F · line 105
Quoted 1 line
rust/kernel/alloc/allocator.rs +1
-0+unsafe { realloc.call (ptr, layout, flags) }
If you make ReallocFunc::krealloc() into a constant ReallocFunc::KREALLOC, then we could avoid the let binding above.
---
Cheers,
Benno
Quoted 9 lines
rust/kernel/alloc/allocator.rs +3
-1+ } +} +unsafe impl GlobalAlloc for Kmalloc {unsafe fn alloc (&self, layout:Layout ) -> *mut u8 {// SAFETY: `ptr::null_mut()` is null and `layout` has a non-zero size by the function safety --2.45.2
Danilo Krummrich
maintainerRe: [PATCH v4 04/28] rust: alloc: implement `Allocator` for `Kmalloc`
Quoted 106 lines · Benno Lossin
Quoted 67 lines · Danilo Krummrich
Implement
AllocatorforKmalloc, the kernel's default allocator, typically used for objects smaller than page size.All memory allocations made with
Kmallocend up inkrealloc().It serves as allocator for the subsequently introduced types
KBoxandKVec.Signed-off-by Danilo Krummrich <[email protected]>--- rust/helpers.c | 3 +- rust/kernel/alloc.rs | 2 +- rust/kernel/alloc/allocator.rs | 68 +++++++++++++++++++++++++++++++++- 3 files changed, 69 insertions(+), 4 deletions(-)rust/helpers.c +1
-2@@ -193,8 +193,7 @@ void rust_helper_init_work_with_key(struct work_struct *work, work_func_t func, }EXPORT_SYMBOL_GPL (rust_helper_init_work_with_key); -void * __must_check __realloc_size(2) -rust_helper_krealloc(const void *objp, size_t new_size, gfp_t flags) +void *rust_helper_krealloc (const void *objp,size_t new_size,gfp_t flags) {return krealloc (objp, new_size, flags); }rust/kernel/alloc.rs +1
-1@@ -4,7 +4,7 @@#[cfg(not(test))] #[cfg(not(testlib))] -mod allocator; +pub mod allocator;pub mod box_ext;pub mod vec_ext;rust/kernel/alloc/allocator.rs +9
-1@@ -5,8 +5,16 @@use super ::{flags::*,Flags };use core::alloc::{GlobalAlloc ,Layout };use core::ptr; +use core::ptr::NonNull ; -struct Kmalloc ; +use crate ::alloc::{AllocError ,Allocator }; +use crate ::bindings; + +/// The contiguous kernel allocator. +/// +/// The contiguous kernel allocator only ever allocates physically contiguous memory through +/// `bindings::krealloc`. +pub struct Kmalloc ;/// Returns a proper size to alloc a new object aligned to `new_layout`'s alignment. fn aligned_size (new_layout:Layout ) ->usize {@@ -40,6 +48,64 @@ pub(crate) unsafe fn krealloc_aligned(ptr: *mut u8, new_layout: Layout, flags: F } }rust/kernel/alloc/allocator.rs pub(crate) unsafe fn krealloc_aligned(ptr: *mut u8, new_layout: Layout, flags: F · line 59
Quoted 10 lines
rust/kernel/alloc/allocator.rs +9
-0+/// # Invariants +/// +/// One of the following `krealloc`, `vrealloc`, `kvrealloc`. +struct ReallocFunc ( +unsafe extern "C" fn (*const core::ffi::c_void ,usize ,u32 ) -> *mut core::ffi::c_void , +); + +impl ReallocFunc { +// INVARIANT: `krealloc` satisfies the type invariants. This INVARIANT comment should be moved one line downwards.
rust/kernel/alloc/allocator.rs pub(crate) unsafe fn krealloc_aligned(ptr: *mut u8, new_layout: Layout, flags: F · line 66
Quoted 7 lines
rust/kernel/alloc/allocator.rs +7
-0+fn krealloc () ->Self { +Self (bindings::krealloc) + } + +/// # Safety +/// +/// This method has the exact same safety requirements as `Allocator::realloc`. I would remove "exact", I don't think we want to mean "almost the same" when we write just "same".
rust/kernel/alloc/allocator.rs pub(crate) unsafe fn krealloc_aligned(ptr: *mut u8, new_layout: Layout, flags: F · line 79
Quoted 13 lines
rust/kernel/alloc/allocator.rs +13
-0+unsafe fn call ( + &self, + ptr:Option <NonNull <u8 >>, + layout:Layout , + flags:Flags , + ) ->Result <NonNull <[u8 ]>,AllocError > { +let size =aligned_size (layout); +let ptr =match ptr { +Some (ptr) => ptr.as_ptr (), +None => ptr::null (), + }; + +// SAFETY: `ptr` is valid by the safety requirements of this function.
Quoted 2 lines
"
ptris either NULL or valid by the safety requirements of this function."
Agreed, for this one and the above ones.
Quoted 9 lines
rust/kernel/alloc/allocator.rs pub(crate) unsafe fn krealloc_aligned(ptr: *mut u8, new_layout: Layout, flags: F · line 86
Quoted 8 lines
rust/kernel/alloc/allocator.rs +7
-0+let raw_ptr =unsafe { +// If `size == 0` and `ptr != NULL` the memory behind the pointer is freed. + self.0 (ptr.cast (), size, flags.0 ).cast () + }; + +let ptr =if size ==0 { +NonNull ::dangling ()
Quoted 4 lines
If we call
realloc(Some(ptr), <layout with size = 0>, ...), then this leaks the pointer returned by the call toself.0above. I don't know what the return value of the different functions are that can appear inself.0, do they return NULL?
That is fine, we don't care about the return value. All ReallocFunc free the memory behind ptr if called with a size of zero. But to answer the question, they return either NULL or ZERO_SIZE_PTR.
Quoted 8 lines
What about the following sequence:
let ptr = realloc(None, <layout with size = 0>, ...); let ptr = realloc(Some(ptr), <layout with size = 0>, ...);Then the above call to
self.0is done with a dangling pointer, can the functions that appear inself.0handle that?
This would be incorrect.
Calling realloc(Some(ptr), <layout with size = 0>, ...) frees the memory behind ptr. This is guranteed behavior for all ReallocFuncs, i.e. krealloc(), vrealloc(), kvrealloc().
Quoted 14 lines
Quoted 14 lines
rust/kernel/alloc/allocator.rs +13
-0+ }else { +NonNull ::new (raw_ptr).ok_or (AllocError )? + }; + +Ok (NonNull ::slice_from_raw_parts (ptr, size)) + } +} + +unsafe impl Allocator for Kmalloc { +unsafe fn realloc ( + ptr:Option <NonNull <u8 >>, + layout:Layout , + flags: Flags,
Quoted 8 lines
rust/kernel/alloc/allocator.rs pub(crate) unsafe fn krealloc_aligned(ptr: *mut u8, new_layout: Layout, flags: F · line 104
Quoted 5 lines
rust/kernel/alloc/allocator.rs +5
-0+ ) -> Result<NonNull<[u8]>, AllocError> { +let realloc =ReallocFunc ::krealloc (); + +// SAFETY: If not `None`, `ptr` is guaranteed to point to valid memory, which was previously +// allocated with this `Allocator`. What about the other requirements? (they should be satisfied, since they are also requirements for calling this function)
Indeed, I think we should just write that by definition Realloc::call has the same safety requirements as Allocator::realloc.
Quoted 5 lines
rust/kernel/alloc/allocator.rs pub(crate) unsafe fn krealloc_aligned(ptr: *mut u8, new_layout: Layout, flags: F · line 105
Quoted 2 lines
rust/kernel/alloc/allocator.rs +1
-0+unsafe { realloc.call (ptr, layout, flags) }If you make
ReallocFunc::krealloc()into a constantReallocFunc::KREALLOC, then we could avoid the let binding above.
Agreed, sounds good.
Quoted 15 lines
---
Cheers,
BennoQuoted 9 lines
rust/kernel/alloc/allocator.rs +3
-1+ } +} +unsafe impl GlobalAlloc for Kmalloc {unsafe fn alloc (&self, layout:Layout ) -> *mut u8 {// SAFETY: `ptr::null_mut()` is null and `layout` has a non-zero size by the function safety --2.45.2
Re: [PATCH v4 04/28] rust: alloc: implement `Allocator` for `Kmalloc`
Quoted 10 lines · Danilo Krummrich
Quoted 9 lines · Benno Lossin
Quoted 7 lines · Danilo Krummrich
rust/kernel/alloc/allocator.rs +7
-0+let raw_ptr =unsafe { +// If `size == 0` and `ptr != NULL` the memory behind the pointer is freed. + self.0 (ptr.cast (), size, flags.0 ).cast () + }; + +let ptr =if size ==0 { +NonNull ::dangling ()
Quoted 8 lines
Quoted 4 lines
If we call
realloc(Some(ptr), <layout with size = 0>, ...), then this leaks the pointer returned by the call toself.0above. I don't know what the return value of the different functions are that can appear inself.0, do they return NULL?That is fine, we don't care about the return value. All
ReallocFuncfree the memory behindptrif called with a size of zero. But to answer the question, they return either NULL orZERO_SIZE_PTR.
I see, then it's fine. I think it would help if we know the exact behavior of kmalloc & friends (either add a link to C docs or write it down on ReallocFunc).
Quoted 5 lines
Quoted 5 lines
What about the following sequence:
let ptr = realloc(None, <layout with size = 0>, ...); let ptr = realloc(Some(ptr), <layout with size = 0>, ...);
Quoted 8 lines
Quoted 2 lines
Then the above call to
self.0is done with a dangling pointer, can the functions that appear inself.0handle that?This would be incorrect.
Calling
realloc(Some(ptr), <layout with size = 0>, ...)frees the memory behindptr. This is guranteed behavior for allReallocFuncs, i.e.krealloc(),vrealloc(),kvrealloc().
Note that I don't use ptr afterwards, the code snippet above is equivalent to this:
let ptr = Kmalloc::alloc(<layout with size = 0>, ...);
unsafe { Kmalloc::free(ptr) };internally exactly the realloc calls that I put above should be called.
---
Cheers,
Benno
Danilo Krummrich
maintainerRe: [PATCH v4 04/28] rust: alloc: implement `Allocator` for `Kmalloc`
Quoted 38 lines · Benno Lossin
Quoted 10 lines · Danilo Krummrich
Quoted 9 lines · Benno Lossin
Quoted 7 lines · Danilo Krummrich
rust/kernel/alloc/allocator.rs +7
-0+let raw_ptr =unsafe { +// If `size == 0` and `ptr != NULL` the memory behind the pointer is freed. + self.0 (ptr.cast (), size, flags.0 ).cast () + }; + +let ptr =if size ==0 { +NonNull ::dangling ()Quoted 8 lines
Quoted 4 lines
If we call
realloc(Some(ptr), <layout with size = 0>, ...), then this leaks the pointer returned by the call toself.0above. I don't know what the return value of the different functions are that can appear inself.0, do they return NULL?That is fine, we don't care about the return value. All
ReallocFuncfree the memory behindptrif called with a size of zero. But to answer the question, they return either NULL orZERO_SIZE_PTR.I see, then it's fine. I think it would help if we know the exact behavior of
kmalloc& friends (either add a link to C docs or write it down onReallocFunc).Quoted 5 lines
Quoted 5 lines
What about the following sequence:
let ptr = realloc(None, <layout with size = 0>, ...); let ptr = realloc(Some(ptr), <layout with size = 0>, ...);Quoted 8 lines
Quoted 2 lines
Then the above call to
self.0is done with a dangling pointer, can the functions that appear inself.0handle that?This would be incorrect.
Calling
realloc(Some(ptr), <layout with size = 0>, ...)frees the memory behindptr. This is guranteed behavior for allReallocFuncs, i.e.krealloc(),vrealloc(),kvrealloc().
Quoted 7 lines
Note that I don't use
ptrafterwards, the code snippet above is equivalent to this:let ptr = Kmalloc::alloc(<layout with size = 0>, ...); unsafe { Kmalloc::free(ptr) };internally exactly the realloc calls that I put above should be called.
I think I misunderstood what you mean here.
So, that's not permitted. free can't be called with a dangling pointer. The kernel free functions (*1) can't handle it, and I can't detect it, since a dangling pointer does not have a descrete value.
We can decide for a specific dangling pointer to be allowed, i.e. the dangling pointer returned by alloc for a zero sized allocation is always dangling<u8>, so we can assert that free is only allowed to be called with what was previously returned by alloc or free and therefore disallow dangling pointers with a different alignment.
Surely, we could also let the caller pass the old alignment, but this all sounds complicated for something that is very trivial for the caller to take care of, i.e. just don't try to free something that was never actually allocated.
It can also lead to subtle bugs, e.g. what if someone calls Box::from_raw for a ZST with some other random pointer? Currently, that doesn't hurt us, which for robustness, seems to be a good thing.
I think it's better to just let Box and Vec figure out if calling free is the right thing to do. The code for that is simple and obvious, i.e. check if T is a ZST.
*1: kfree() can handle dangling pointers up to 16 bytes aligned, see ZERO_OR_NULL_PTR(x).
Quoted 5 lines
---
Cheers,
Benno
Re: [PATCH v4 04/28] rust: alloc: implement `Allocator` for `Kmalloc`
Quoted 45 lines · Danilo Krummrich
Quoted 44 lines · Benno Lossin
Quoted 10 lines · Danilo Krummrich
Quoted 9 lines · Benno Lossin
Quoted 7 lines · Danilo Krummrich
rust/kernel/alloc/allocator.rs +7
-0+let raw_ptr =unsafe { +// If `size == 0` and `ptr != NULL` the memory behind the pointer is freed. + self.0 (ptr.cast (), size, flags.0 ).cast () + }; + +let ptr =if size ==0 { +NonNull ::dangling ()Quoted 8 lines
Quoted 4 lines
If we call
realloc(Some(ptr), <layout with size = 0>, ...), then this leaks the pointer returned by the call toself.0above. I don't know what the return value of the different functions are that can appear inself.0, do they return NULL?That is fine, we don't care about the return value. All
ReallocFuncfree the memory behindptrif called with a size of zero. But to answer the question, they return either NULL orZERO_SIZE_PTR.I see, then it's fine. I think it would help if we know the exact behavior of
kmalloc& friends (either add a link to C docs or write it down onReallocFunc).Quoted 5 lines
Quoted 5 lines
What about the following sequence:
let ptr = realloc(None, <layout with size = 0>, ...); let ptr = realloc(Some(ptr), <layout with size = 0>, ...);Quoted 8 lines
Quoted 2 lines
Then the above call to
self.0is done with a dangling pointer, can the functions that appear inself.0handle that?This would be incorrect.
Calling
realloc(Some(ptr), <layout with size = 0>, ...)frees the memory behindptr. This is guranteed behavior for allReallocFuncs, i.e.krealloc(),vrealloc(),kvrealloc().Note that I don't use
ptrafterwards, the code snippet above is equivalent to this:let ptr = Kmalloc::alloc(<layout with size = 0>, ...); unsafe { Kmalloc::free(ptr) };
Quoted 7 lines
Quoted 1 line
internally exactly the realloc calls that I put above should be called.
I think I misunderstood what you mean here.
So, that's not permitted.
freecan't be called with a dangling pointer. The kernel free functions (*1) can't handle it, and I can't detect it, since a dangling pointer does not have a descrete value.
That is true, but only if we do not have access to the old layout of the allocation. If we add old_layout as a parameter, then we can handle dangling pointers.
Quoted 5 lines
We can decide for a specific dangling pointer to be allowed, i.e. the dangling pointer returned by
allocfor a zero sized allocation is alwaysdangling<u8>, so we can assert thatfreeis only allowed to be called with what was previously returned byallocorfreeand therefore disallow dangling pointers with a different alignment.
I don't like that.
Quoted 7 lines
Surely, we could also let the caller pass the old alignment, but this all sounds complicated for something that is very trivial for the caller to take care of, i.e. just don't try to free something that was never actually allocated.
It can also lead to subtle bugs, e.g. what if someone calls
Box::from_rawfor a ZST with some other random pointer? Currently, that doesn't hurt us, which for robustness, seems to be a good thing.
I think we should forbid that. To me it's just plain wrong to take a random integer literal and cast it to a ZST. IIRC it even is UB if that points to a previously allocated object that has been freed (but I don't remember where I read it...).
Also if we use the size of the old layout instead of comparing alignment with the address of the pointer, then we avoid this issue.
Quoted 3 lines
I think it's better to just let
BoxandVecfigure out if callingfreeis the right thing to do. The code for that is simple and obvious, i.e. check ifTis a ZST.
I don't think that it is as simple as you make it out to be. To me this is functionality that we can move into one place and never have to think about again.
Also if we at some point decide to add some sort of debugging allocator (am I right in assuming that such a thing already exists for C?) then we might want to tag on extra data in the allocation, in that case it would be very useful if the allocator also saw zero-sized allocations, since we should check all allocations.
Quoted 2 lines
*1:
kfree()can handle dangling pointers up to 16 bytes aligned, seeZERO_OR_NULL_PTR(x).
I agree with you that we shouldn't rely on that.
---
Cheers,
Benno
Danilo Krummrich
maintainerRe: [PATCH v4 04/28] rust: alloc: implement `Allocator` for `Kmalloc`
Quoted 7 lines · Benno Lossin
Quoted 3 lines
So, that's not permitted.
freecan't be called with a dangling pointer. The kernel free functions (*1) can't handle it, and I can't detect it, since a dangling pointer does not have a descrete value.That is true, but only if we do not have access to the old layout of the allocation. If we add
old_layoutas a parameter, then we can handle dangling pointers.
Then we'd need free to be fn free(ptr: NonNull<u8>, layout: Layout) just to compare whether ptr.as_ptr() == layout.align() as _. Same for realloc, but that's less weird.
It's also not that we safe code with that. Box, Vec, any other user, still would have to create the Layout before they call A::free.
Quoted 4 lines
Quoted 4 lines
Surely, we could also let the caller pass the old alignment, but this all sounds complicated for something that is very trivial for the caller to take care of, i.e. just don't try to free something that was never actually allocated.
Quoted 8 lines
Quoted 3 lines
It can also lead to subtle bugs, e.g. what if someone calls
Box::from_rawfor a ZST with some other random pointer? Currently, that doesn't hurt us, which for robustness, seems to be a good thing.I think we should forbid that. To me it's just plain wrong to take a random integer literal and cast it to a ZST. IIRC it even is UB if that points to a previously allocated object that has been freed (but I don't remember where I read it...).
I think my argument about robustness still holds even if we forbid it.
The documentation says "For operations of size zero, every pointer is valid, including the null pointer." [1]
[1] https://doc.rust-lang.org/std/ptr/index.html
Quoted 3 lines
Also if we use the size of the old layout instead of comparing alignment with the address of the pointer, then we avoid this issue.
That is just another problem when passing the old Layout (or maybe just the old size as usize). Neither do we need the old size, nor do we honor it with any kernel allocator. This has the following implications:
(1) We never see any error if the old size that is passed is garbage (unless it's non-zero, when it should be zero and vice versa), which is bad.
(2) We'd need free to be fn free(ptr: NonNull<u8>, size: usize), which is confusing, because it implies that an actual free relies on this size for freeing the memory.
If we want to avoid (1) and (2), we'd need to make itfn free(ptr: NonNull<u8>, zero: bool) instead, but then also the caller can just check this boolean and conditionally call free.
I don't really see why it's better to let free do the if !zero check.
Re: [PATCH v4 04/28] rust: alloc: implement `Allocator` for `Kmalloc`
Quoted 5 lines · Danilo Krummrich
Quoted 4 lines · Benno Lossin
Quoted 3 lines
So, that's not permitted.
freecan't be called with a dangling pointer. The kernel free functions (*1) can't handle it, and I can't detect it, since a dangling pointer does not have a descrete value.
Quoted 7 lines
Quoted 3 lines
That is true, but only if we do not have access to the old layout of the allocation. If we add
old_layoutas a parameter, then we can handle dangling pointers.Then we'd need
freeto befn free(ptr: NonNull<u8>, layout: Layout)just to compare whetherptr.as_ptr() == layout.align() as _. Same forrealloc, but that's less weird.
I don't think that's a problem (though we would check against the size and not compare the address!). Allocator from stdlib also has the extra argument.
Quoted 2 lines
It's also not that we safe code with that.
Box,Vec, any other user, still would have to create theLayoutbefore they callA::free.
But for Box it would just be Layout::<T>::new() and Vec needs Layout::<T>::new().repeat(self.cap).
Though for repeat we need the alloc_layout_extra feature, which is an argument against doing this.
Quoted 13 lines
Quoted 4 lines
Quoted 4 lines
Surely, we could also let the caller pass the old alignment, but this all sounds complicated for something that is very trivial for the caller to take care of, i.e. just don't try to free something that was never actually allocated.
Quoted 8 lines
Quoted 3 lines
It can also lead to subtle bugs, e.g. what if someone calls
Box::from_rawfor a ZST with some other random pointer? Currently, that doesn't hurt us, which for robustness, seems to be a good thing.I think we should forbid that. To me it's just plain wrong to take a random integer literal and cast it to a ZST. IIRC it even is UB if that points to a previously allocated object that has been freed (but I don't remember where I read it...).
Quoted 4 lines
I think my argument about robustness still holds even if we forbid it.
The documentation says "For operations of size zero, every pointer is valid, including the null pointer." [1]
How does this increase robustness? I am not allowed to call free with a random pointer, only pointers returned by that allocator. The same should also be true for Box::from_raw. That way the ZST dangling pointer stuff leaks into that API.
Quoted 6 lines
[1] https://doc.rust-lang.org/std/ptr/index.html
Quoted 3 lines
Also if we use the size of the old layout instead of comparing alignment with the address of the pointer, then we avoid this issue.
Quoted 6 lines
That is just another problem when passing the old
Layout(or maybe just the old size as usize). Neither do we need the old size, nor do we honor it with any kernel allocator. This has the following implications:(1) We never see any error if the old size that is passed is garbage (unless it's non-zero, when it should be zero and vice versa), which is bad.
We could add debug support for that though?
Quoted 3 lines
(2) We'd need
freeto befn free(ptr: NonNull<u8>, size: usize), which is confusing, because it implies that an actual free relies on this size for freeing the memory.
I don't think that it is confusing to ask for the old layout.
Quoted 3 lines
If we want to avoid (1) and (2), we'd need to make it
fn free(ptr: NonNull<u8>, zero: bool)instead, but then also the caller can just check this boolean and conditionally callfree.
Yeah having free with a zero: bool sounds like a bad idea.
Quoted 1 line
I don't really see why it's better to let
freedo theif !zerocheck.
You did not reply to my last argument:
Quoted 4 lines
Quoted 3 lines
I think it's better to just let
BoxandVecfigure out if callingfreeis the right thing to do. The code for that is simple and obvious, i.e. check ifTis a ZST.
Quoted 8 lines
I don't think that it is as simple as you make it out to be. To me this is functionality that we can move into one place and never have to think about again.
Also if we at some point decide to add some sort of debugging allocator (am I right in assuming that such a thing already exists for C?) then we might want to tag on extra data in the allocation, in that case it would be very useful if the allocator also saw zero-sized allocations, since we should check all allocations.
---
Cheers,
Benno
Re: [PATCH v4 04/28] rust: alloc: implement `Allocator` for `Kmalloc`
Quoted 9 lines · Benno Lossin
Quoted 2 lines · Danilo Krummrich
It's also not that we safe code with that.
Box,Vec, any other user, still would have to create theLayoutbefore they callA::free.But for
Boxit would just beLayout::<T>::new()andVecneedsLayout::<T>::new().repeat(self.cap).Though for
repeatwe need thealloc_layout_extrafeature, which is an argument against doing this.
Oops, I overlooked the Layout::array function, so this is not a problem. Instead it just is Layout::<T>::array(self.cap).unwrap().
---
Cheers,
Benno
Danilo Krummrich
maintainerRe: [PATCH v4 04/28] rust: alloc: implement `Allocator` for `Kmalloc`
In reply to Benno LossinQuoted 10 lines · Benno Lossin
Quoted 9 lines · Danilo Krummrich
Quoted 7 lines · Benno Lossin
Quoted 3 lines
So, that's not permitted.
freecan't be called with a dangling pointer. The kernel free functions (*1) can't handle it, and I can't detect it, since a dangling pointer does not have a descrete value.That is true, but only if we do not have access to the old layout of the allocation. If we add
old_layoutas a parameter, then we can handle dangling pointers.
Quoted 7 lines
Quoted 3 lines
Then we'd need
freeto befn free(ptr: NonNull<u8>, layout: Layout)just to compare whetherptr.as_ptr() == layout.align() as _. Same forrealloc, but that's less weird.I don't think that's a problem (though we would check against the size and not compare the address!).
Allocatorfrom stdlib also has the extra argument.
Because Allocator from stdlib actually needs it, e.g. they have to open code realloc, since userspace allocators don't give proper alignment guarantees.
Again, the kernel allocators neither need, nor take and honor this extra information.
Quoted 6 lines
Quoted 3 lines
It's also not that we safe code with that.
Box,Vec, any other user, still would have to create theLayoutbefore they callA::free.But for
Boxit would just beLayout::<T>::new()andVecneedsLayout::<T>::new().repeat(self.cap).
Let's take Vec for instance, there it's either
current code
------------
```
if cap != 0 {
unsafe { A::free(self.ptr.as_non_null().cast()) };
}
```your proposal
-------------
```
let layout = Layout::<T>::array(self.cap).unwrap();
unsafe { A::free(self.ptr.as_non_null().cast(), layout) };
```I really don't see how that's an improvement.
Quoted 19 lines
Though for
repeatwe need thealloc_layout_extrafeature, which is an argument against doing this.Quoted 15 lines
Quoted 4 lines
Quoted 4 lines
Surely, we could also let the caller pass the old alignment, but this all sounds complicated for something that is very trivial for the caller to take care of, i.e. just don't try to free something that was never actually allocated.
Quoted 8 lines
Quoted 3 lines
It can also lead to subtle bugs, e.g. what if someone calls
Box::from_rawfor a ZST with some other random pointer? Currently, that doesn't hurt us, which for robustness, seems to be a good thing.I think we should forbid that. To me it's just plain wrong to take a random integer literal and cast it to a ZST. IIRC it even is UB if that points to a previously allocated object that has been freed (but I don't remember where I read it...).
I think my argument about robustness still holds even if we forbid it.
Quoted 7 lines
Quoted 2 lines
The documentation says "For operations of size zero, every pointer is valid, including the null pointer." [1]
How does this increase robustness? I am not allowed to call
freewith a random pointer, only pointers returned by that allocator. The same should also be true forBox::from_raw. That way the ZST dangling pointer stuff leaks into that API.
This point was about your first proposal to use the alignment. With cosidering only the alignment we can't handle the case where Box::from_raw is called with a random pointer for a ZST, which *technically* isn't illegal. We can define it as illegal, but I'd consider it to be more robust, if we don't oops in case.
But as you say checking the size instead of the alignment does not have this problem.
Quoted 7 lines
Quoted 7 lines
[1] https://doc.rust-lang.org/std/ptr/index.html
Quoted 3 lines
Also if we use the size of the old layout instead of comparing alignment with the address of the pointer, then we avoid this issue.
Quoted 8 lines
Quoted 6 lines
That is just another problem when passing the old
Layout(or maybe just the old size as usize). Neither do we need the old size, nor do we honor it with any kernel allocator. This has the following implications:(1) We never see any error if the old size that is passed is garbage (unless it's non-zero, when it should be zero and vice versa), which is bad.
We could add debug support for that though?
And add even more complexity just to avoid a simple if !zero check before calling free? I don't like that.
Just to clarify, I'm not against passing the old layout in general. But I really don't want to pass something in that is not required *and* not honored by a single kernel allocator.
IMO, the only other valid reason to accept unneeded arguments would be if we could use the Allocator interface from stdlib.
Quoted 6 lines
Quoted 4 lines
(2) We'd need
freeto befn free(ptr: NonNull<u8>, size: usize), which is confusing, because it implies that an actual free relies on this size for freeing the memory.I don't think that it is confusing to ask for the old layout.
It is always confusing if a function asks for information that it doesn't need and doesn't consider, because asking for it in the first place creates the impression that it is indeed needed and considered.
Quoted 5 lines
Quoted 4 lines
If we want to avoid (1) and (2), we'd need to make it
fn free(ptr: NonNull<u8>, zero: bool)instead, but then also the caller can just check this boolean and conditionally callfree.
Quoted 15 lines
Yeah having
freewith azero: boolsounds like a bad idea.Quoted 1 line
I don't really see why it's better to let
freedo theif !zerocheck.You did not reply to my last argument:
Quoted 9 lines
Quoted 3 lines
I think it's better to just let
BoxandVecfigure out if callingfreeis the right thing to do. The code for that is simple and obvious, i.e. check ifTis a ZST.I don't think that it is as simple as you make it out to be. To me this is functionality that we can move into one place and never have to think about again.
Also if we at some point decide to add some sort of debugging allocator (am I right in assuming that such a thing already exists for C?) then we
The C side allocators have quite a lot of debugging capabilities, but there is no separate one.
Quoted 3 lines
Quoted 3 lines
might want to tag on extra data in the allocation, in that case it would be very useful if the allocator also saw zero-sized allocations, since we should check all allocations.
What would such a debugging allocator do on the Rust side?
The allocators we have are just simple wrappers around the real kernel allocators from the C side.
I don't really see the need for some sort of debugging allocator.
Danilo Krummrich
maintainerPATCH v4 5/28 rust: alloc: add module `allocator_test`
In reply to Danilo KrummrichAllocators, such as Kmalloc, will be used by e.g. Box and Vec in subsequent patches, and hence this dependency propagates throughout the whole kernel.
Add the allocator_test module that provides an empty implementation for all Allocators in the kernel, such that we don't break the rusttest make target in subsequent patches.
--- rust/kernel/alloc.rs | 9 +++++++-- rust/kernel/alloc/allocator_test.rs | 19 +++++++++++++++++++ 2 files changed, 26 insertions(+), 2 deletions(-) create mode 100644 rust/kernel/alloc/allocator_test.rs
rust/kernel/alloc.rs +7 -2
@@ -2,12 +2,17 @@//! Extensions to the [`alloc`] crate. -#[cfg(not(test))] -#[cfg(not(testlib))] +#[cfg(not(any(test, testlib)))] pub mod allocator;pub mod box_ext;pub mod vec_ext; +#[cfg(any(test, testlib))] +pub mod allocator_test; + +#[cfg(any(test, testlib))] +pub use self::allocator_testas allocator; +/// Indicates an allocation error. #[derive( Copy , Clone , PartialEq , Eq , Debug )] pub struct AllocError ;
rust/kernel/alloc/allocator_test.rs new file +19 -0
@@ -0,0 +1,19 @@ +// SPDX-License-Identifier: GPL-2.0 + +#![allow(missing_docs)] + +use super ::{AllocError ,Allocator ,Flags }; +use core::alloc::Layout ; +use core::ptr::NonNull ; + +pub struct Kmalloc ; + +unsafe impl Allocator for Kmalloc { +unsafe fn realloc ( + _ptr:Option <NonNull <u8 >>, + _layout:Layout , + _flags:Flags , + ) ->Result <NonNull <[u8 ]>,AllocError > { +panic! (); + } +}
Signature · 1 line
2.45.2
Re: [PATCH v4 05/28] rust: alloc: add module `allocator_test`
Quoted 7 lines · Danilo Krummrich
Allocators, such asKmalloc, will be used by e.g.BoxandVecin subsequent patches, and hence this dependency propagates throughout the whole kernel.Add the
allocator_testmodule that provides an empty implementation for allAllocators in the kernel, such that we don't break therusttestmake target in subsequent patches.
This is confusing, since you are talking about both our new Allocator trait, allocators and the alloc crate Allocator.
Quoted 45 lines
Reviewed-by Alice Ryhl <[email protected]>Signed-off-by Danilo Krummrich <[email protected]>--- rust/kernel/alloc.rs | 9 +++++++-- rust/kernel/alloc/allocator_test.rs | 19 +++++++++++++++++++ 2 files changed, 26 insertions(+), 2 deletions(-) create mode 100644 rust/kernel/alloc/allocator_test.rsrust/kernel/alloc.rs +7
-2@@ -2,12 +2,17 @@//! Extensions to the [`alloc`] crate. -#[cfg(not(test))] -#[cfg(not(testlib))] +#[cfg(not(any(test, testlib)))] pub mod allocator;pub mod box_ext;pub mod vec_ext; +#[cfg(any(test, testlib))] +pub mod allocator_test; + +#[cfg(any(test, testlib))] +pub use self::allocator_testas allocator; +/// Indicates an allocation error. #[derive( Copy , Clone , PartialEq , Eq , Debug )] pub struct AllocError ;rust/kernel/alloc/allocator_test.rs new file +7
-0@@ -0,0 +1,19 @@ +// SPDX-License-Identifier: GPL-2.0 + +#![allow(missing_docs)] + +use super ::{AllocError ,Allocator ,Flags }; +use core::alloc::Layout ; +use core::ptr::NonNull ;
rust/kernel/alloc/allocator_test.rs line 17
Quoted 10 lines
rust/kernel/alloc/allocator_test.rs new file +10
-0+ +pub struct Kmalloc ; + +unsafe impl Allocator for Kmalloc { +unsafe fn realloc ( + _ptr:Option <NonNull <u8 >>, + _layout:Layout , + _flags:Flags , + ) ->Result <NonNull <[u8 ]>,AllocError > { +panic! ();
Does build_error!() also work? If yes, then I would prefer that.
---
Cheers,
Benno
Quoted 5 lines
rust/kernel/alloc/allocator_test.rs new file +2
-1+ } +} --2.45.2
Danilo Krummrich
maintainerRe: [PATCH v4 05/28] rust: alloc: add module `allocator_test`
Quoted 11 lines · Benno Lossin
Quoted 7 lines · Danilo Krummrich
Allocators, such asKmalloc, will be used by e.g.BoxandVecin subsequent patches, and hence this dependency propagates throughout the whole kernel.Add the
allocator_testmodule that provides an empty implementation for allAllocators in the kernel, such that we don't break therusttestmake target in subsequent patches.This is confusing, since you are talking about both our new
Allocatortrait, allocators and thealloccrateAllocator.
I never mention the alloc crate Allocator here.
Quoted 50 lines
Quoted 50 lines
Reviewed-by Alice Ryhl <[email protected]>Signed-off-by Danilo Krummrich <[email protected]>--- rust/kernel/alloc.rs | 9 +++++++-- rust/kernel/alloc/allocator_test.rs | 19 +++++++++++++++++++ 2 files changed, 26 insertions(+), 2 deletions(-) create mode 100644 rust/kernel/alloc/allocator_test.rsrust/kernel/alloc.rs +7
-2@@ -2,12 +2,17 @@//! Extensions to the [`alloc`] crate. -#[cfg(not(test))] -#[cfg(not(testlib))] +#[cfg(not(any(test, testlib)))] pub mod allocator;pub mod box_ext;pub mod vec_ext; +#[cfg(any(test, testlib))] +pub mod allocator_test; + +#[cfg(any(test, testlib))] +pub use self::allocator_testas allocator; +/// Indicates an allocation error. #[derive( Copy , Clone , PartialEq , Eq , Debug )] pub struct AllocError ;rust/kernel/alloc/allocator_test.rs new file +11
-0@@ -0,0 +1,19 @@ +// SPDX-License-Identifier: GPL-2.0 + +#![allow(missing_docs)] + +use super ::{AllocError ,Allocator ,Flags }; +use core::alloc::Layout ; +use core::ptr::NonNull ; + +pub struct Kmalloc ; + +unsafe impl Allocator for Kmalloc {
Quoted 8 lines
rust/kernel/alloc/allocator_test.rs line 17
Quoted 6 lines
rust/kernel/alloc/allocator_test.rs new file +6
-0+unsafe fn realloc ( + _ptr:Option <NonNull <u8 >>, + _layout:Layout , + _flags:Flags , + ) ->Result <NonNull <[u8 ]>,AllocError > { +panic! ();Does
build_error!()also work? If yes, then I would prefer that.
Probably, but it also probably doesn't matter too much. A later patch in the series replaces this with a Cmalloc implementation and type aliases all kernel allocators (e.g. Kmalloc) to it.
This allows us to use our kernel Box and Vec types in userspace tests (rusttest target).
Quoted 11 lines
---
Cheers,
BennoQuoted 5 lines
rust/kernel/alloc/allocator_test.rs new file +2
-1+ } +} --2.45.2
Re: [PATCH v4 05/28] rust: alloc: add module `allocator_test`
Quoted 6 lines · Danilo Krummrich
Quoted 5 lines · Benno Lossin
Quoted 4 lines · Danilo Krummrich
Allocators, such asKmalloc, will be used by e.g.BoxandVecin subsequent patches, and hence this dependency propagates throughout the whole kernel.
Quoted 8 lines
Quoted 6 lines
Quoted 3 lines
Add the
allocator_testmodule that provides an empty implementation for allAllocators in the kernel, such that we don't break therusttestmake target in subsequent patches.This is confusing, since you are talking about both our new
Allocatortrait, allocators and thealloccrateAllocator.I never mention the
alloccrateAllocatorhere.
Seems like I confused myself...
Quoted 58 lines
Quoted 49 lines
Quoted 49 lines
Reviewed-by Alice Ryhl <[email protected]>Signed-off-by Danilo Krummrich <[email protected]>--- rust/kernel/alloc.rs | 9 +++++++-- rust/kernel/alloc/allocator_test.rs | 19 +++++++++++++++++++ 2 files changed, 26 insertions(+), 2 deletions(-) create mode 100644 rust/kernel/alloc/allocator_test.rsrust/kernel/alloc.rs +7
-2@@ -2,12 +2,17 @@//! Extensions to the [`alloc`] crate. -#[cfg(not(test))] -#[cfg(not(testlib))] +#[cfg(not(any(test, testlib)))] pub mod allocator;pub mod box_ext;pub mod vec_ext; +#[cfg(any(test, testlib))] +pub mod allocator_test; + +#[cfg(any(test, testlib))] +pub use self::allocator_testas allocator; +/// Indicates an allocation error. #[derive( Copy , Clone , PartialEq , Eq , Debug )] pub struct AllocError ;rust/kernel/alloc/allocator_test.rs new file +11
-0@@ -0,0 +1,19 @@ +// SPDX-License-Identifier: GPL-2.0 + +#![allow(missing_docs)] + +use super ::{AllocError ,Allocator ,Flags }; +use core::alloc::Layout ; +use core::ptr::NonNull ; + +pub struct Kmalloc ; + +unsafe impl Allocator for Kmalloc {Quoted 8 lines
rust/kernel/alloc/allocator_test.rs line 17
Quoted 6 lines
rust/kernel/alloc/allocator_test.rs new file +6
-0+unsafe fn realloc ( + _ptr:Option <NonNull <u8 >>, + _layout:Layout , + _flags:Flags , + ) ->Result <NonNull <[u8 ]>,AllocError > { +panic! ();Does
build_error!()also work? If yes, then I would prefer that.
Quoted 3 lines
Probably, but it also probably doesn't matter too much. A later patch in the series replaces this with a
Cmallocimplementation and type aliases all kernel allocators (e.g.Kmalloc) to it.
What prevents you from doing the Cmalloc patch here? build_error! probably doesn't work, since we probably allocate in rusttest, right?
---
Cheers,
Benno
Danilo Krummrich
maintainerRe: [PATCH v4 05/28] rust: alloc: add module `allocator_test`
Quoted 76 lines · Benno Lossin
Quoted 6 lines · Danilo Krummrich
Quoted 5 lines · Benno Lossin
Quoted 4 lines · Danilo Krummrich
Allocators, such asKmalloc, will be used by e.g.BoxandVecin subsequent patches, and hence this dependency propagates throughout the whole kernel.Quoted 8 lines
Quoted 6 lines
Quoted 3 lines
Add the
allocator_testmodule that provides an empty implementation for allAllocators in the kernel, such that we don't break therusttestmake target in subsequent patches.This is confusing, since you are talking about both our new
Allocatortrait, allocators and thealloccrateAllocator.I never mention the
alloccrateAllocatorhere.Seems like I confused myself...
Quoted 58 lines
Quoted 57 lines
Quoted 45 lines
Reviewed-by Alice Ryhl <[email protected]>Signed-off-by Danilo Krummrich <[email protected]>--- rust/kernel/alloc.rs | 9 +++++++-- rust/kernel/alloc/allocator_test.rs | 19 +++++++++++++++++++ 2 files changed, 26 insertions(+), 2 deletions(-) create mode 100644 rust/kernel/alloc/allocator_test.rsrust/kernel/alloc.rs +7
-2@@ -2,12 +2,17 @@//! Extensions to the [`alloc`] crate. -#[cfg(not(test))] -#[cfg(not(testlib))] +#[cfg(not(any(test, testlib)))] pub mod allocator;pub mod box_ext;pub mod vec_ext; +#[cfg(any(test, testlib))] +pub mod allocator_test; + +#[cfg(any(test, testlib))] +pub use self::allocator_testas allocator; +/// Indicates an allocation error. #[derive( Copy , Clone , PartialEq , Eq , Debug )] pub struct AllocError ;rust/kernel/alloc/allocator_test.rs new file +7
-0@@ -0,0 +1,19 @@ +// SPDX-License-Identifier: GPL-2.0 + +#![allow(missing_docs)] + +use super ::{AllocError ,Allocator ,Flags }; +use core::alloc::Layout ; +use core::ptr::NonNull ;rust/kernel/alloc/allocator_test.rs line 17
Quoted 10 lines
rust/kernel/alloc/allocator_test.rs new file +10
-0+ +pub struct Kmalloc ; + +unsafe impl Allocator for Kmalloc { +unsafe fn realloc ( + _ptr:Option <NonNull <u8 >>, + _layout:Layout , + _flags:Flags , + ) ->Result <NonNull <[u8 ]>,AllocError > { +panic! ();Does
build_error!()also work? If yes, then I would prefer that.
Quoted 6 lines
Quoted 3 lines
Probably, but it also probably doesn't matter too much. A later patch in the series replaces this with a
Cmallocimplementation and type aliases all kernel allocators (e.g.Kmalloc) to it.What prevents you from doing the
Cmallocpatch here?build_error!probably doesn't work, since we probably allocate in rusttest, right?
I think it's better to have the creation of the allocator_test module and the implementation of Cmalloc in separate patches. Cmalloc comes later because I don't need it yet. I need it to get rid of building Rust's alloc crate and I think it's good to have those patches close to each other.
Quoted 5 lines
---
Cheers,
Benno
Danilo Krummrich
maintainerPATCH v4 6/28 rust: alloc: implement `Vmalloc` allocator
In reply to Danilo KrummrichImplement Allocator for Vmalloc, the kernel's virtually contiguous allocator, typically used for larger objects, (much) larger than page size.
All memory allocations made with Vmalloc end up in vrealloc().
--- rust/helpers.c | 7 +++++++ rust/kernel/alloc/allocator.rs | 32 +++++++++++++++++++++++++++++ rust/kernel/alloc/allocator_test.rs | 1 + 3 files changed, 40 insertions(+)
rust/helpers.c +7 -0
@@ -33,6 +33,7 @@#include <linux/sched/signal.h> #include <linux/slab.h> #include <linux/spinlock.h> +#include <linux/vmalloc.h> #include <linux/wait.h> #include <linux/workqueue.h>
@@ -199,6 +200,12 @@ void *rust_helper_krealloc(const void *objp, size_t new_size, gfp_t flags) }EXPORT_SYMBOL_GPL (rust_helper_krealloc); +void *rust_helper_vrealloc (const void *p,size_t size,gfp_t flags) +{ +return vrealloc (p, size, flags); +} +EXPORT_SYMBOL_GPL (rust_helper_vrealloc); +/* * `bindgen` binds the C `size_t` type as the Rust `usize` type, so we can * use it in contexts where Rust expects a `usize` like slice (array) indices.
rust/kernel/alloc/allocator.rs +32 -0
@@ -9,6 +9,7 @@use crate ::alloc::{AllocError ,Allocator };use crate ::bindings; +use crate ::pr_warn;/// The contiguous kernel allocator. ///
@@ -16,6 +17,12 @@/// `bindings::krealloc`. pub struct Kmalloc ; +/// The virtually contiguous kernel allocator. +/// +/// The vmalloc allocator allocates pages from the page level allocator and maps them into the +/// contiguous kernel virtual space. +pub struct Vmalloc ; +/// Returns a proper size to alloc a new object aligned to `new_layout`'s alignment. fn aligned_size (new_layout:Layout ) ->usize {// Customized layouts from `Layout::from_size_align()` can have size < align, so pad first.
@@ -61,6 +68,11 @@ fn krealloc() -> Self {Self (bindings::krealloc) } +// INVARIANT: `vrealloc` satisfies the type invariants. +fn vrealloc () ->Self { +Self (bindings::vrealloc) + } +/// # Safety /// /// This method has the exact same safety requirements as `Allocator::realloc`.
@@ -141,6 +153,26 @@ unsafe fn alloc_zeroed(&self, layout: Layout) -> *mut u8 { } } +unsafe impl Allocator for Vmalloc { +unsafe fn realloc ( + ptr:Option <NonNull <u8 >>, + layout:Layout , + flags:Flags , + ) ->Result <NonNull <[u8 ]>,AllocError > { +let realloc =ReallocFunc ::vrealloc (); + +// TODO: Support alignments larger than PAGE_SIZE. +if layout.align () > bindings::PAGE_SIZE { +pr_warn! ("Vmalloc does not support alignments larger than PAGE_SIZE yet.\n" ); +return Err (AllocError ); + } + +// SAFETY: If not `None`, `ptr` is guaranteed to point to valid memory, which was previously +// allocated with this `Allocator`. +unsafe { realloc.call (ptr, layout, flags) } + } +} +#[global_allocator] static ALLOCATOR :Kmalloc =Kmalloc ;
rust/kernel/alloc/allocator_test.rs +1 -0
@@ -7,6 +7,7 @@use core::ptr::NonNull ;pub struct Kmalloc ; +pub type Vmalloc =Kmalloc ;unsafe impl Allocator for Kmalloc { unsafe fn realloc(
Signature · 1 line
2.45.2
Re: [PATCH v4 06/28] rust: alloc: implement `Vmalloc` allocator
Quoted 52 lines · Danilo Krummrich
Implement
AllocatorforVmalloc, the kernel's virtually contiguous allocator, typically used for larger objects, (much) larger than page size.All memory allocations made with
Vmallocend up invrealloc().Reviewed-by Alice Ryhl <[email protected]>Signed-off-by Danilo Krummrich <[email protected]>--- rust/helpers.c | 7 +++++++ rust/kernel/alloc/allocator.rs | 32 +++++++++++++++++++++++++++++ rust/kernel/alloc/allocator_test.rs | 1 + 3 files changed, 40 insertions(+)rust/helpers.c +7
-0@@ -33,6 +33,7 @@#include <linux/sched/signal.h> #include <linux/slab.h> #include <linux/spinlock.h> +#include <linux/vmalloc.h> #include <linux/wait.h> #include <linux/workqueue.h> @@ -199,6 +200,12 @@ void *rust_helper_krealloc(const void *objp, size_t new_size, gfp_t flags) }EXPORT_SYMBOL_GPL (rust_helper_krealloc); +void *rust_helper_vrealloc (const void *p,size_t size,gfp_t flags) +{ +return vrealloc (p, size, flags); +} +EXPORT_SYMBOL_GPL (rust_helper_vrealloc); +/* * `bindgen` binds the C `size_t` type as the Rust `usize` type, so we can * use it in contexts where Rust expects a `usize` like slice (array) indices. rust/kernel/alloc/allocator.rs +1
-0@@ -9,6 +9,7 @@use crate ::alloc::{AllocError ,Allocator };use crate ::bindings; +use crate ::pr_warn;/// The contiguous kernel allocator. ///
rust/kernel/alloc/allocator.rs line 24
Quoted 8 lines
rust/kernel/alloc/allocator.rs +5
-0@@ -16,6 +17,12 @@/// `bindings::krealloc`. pub struct Kmalloc ; +/// The virtually contiguous kernel allocator. +/// +/// The vmalloc allocator allocates pages from the page level allocator and maps them into the +/// contiguous kernel virtual space. +pub struct Vmalloc ;
One thing that I should also have mentioned for Kmalloc, do we want these types to also have values? I don't think that we need them to be, so we could declare them as pub enum Vmalloc {}.
rust/kernel/alloc/allocator.rs fn krealloc() -> Self { · line 71
Quoted 9 lines
rust/kernel/alloc/allocator.rs +2
-0+/// Returns a proper size to alloc a new object aligned to `new_layout`'s alignment. fn aligned_size (new_layout:Layout ) ->usize {// Customized layouts from `Layout::from_size_align()` can have size < align, so pad first. @@ -61,6 +68,11 @@ fn krealloc() -> Self {Self (bindings::krealloc) } +// INVARIANT: `vrealloc` satisfies the type invariants.
This should be moved down a line.
---
Cheers,
Benno
Quoted 18 lines
rust/kernel/alloc/allocator.rs +11
-0+fn vrealloc () ->Self { +Self (bindings::vrealloc) + } +/// # Safety /// /// This method has the exact same safety requirements as `Allocator::realloc`. @@ -141,6 +153,26 @@ unsafe fn alloc_zeroed(&self, layout: Layout) -> *mut u8 { } } +unsafe impl Allocator for Vmalloc { +unsafe fn realloc ( + ptr:Option <NonNull <u8 >>, + layout:Layout , + flags:Flags , + ) ->Result <NonNull <[u8 ]>,AllocError > { +let realloc =ReallocFunc ::vrealloc ();
rust/kernel/alloc/allocator.rs unsafe fn alloc_zeroed(&self, layout: Layout) -> *mut u8 { · line 172
Quoted 10 lines
rust/kernel/alloc/allocator.rs +10
-0+ +// TODO: Support alignments larger than PAGE_SIZE. +if layout.align () > bindings::PAGE_SIZE { +pr_warn! ("Vmalloc does not support alignments larger than PAGE_SIZE yet.\n" ); +return Err (AllocError ); + } + +// SAFETY: If not `None`, `ptr` is guaranteed to point to valid memory, which was previously +// allocated with this `Allocator`. +unsafe { realloc.call (ptr, layout, flags) }
I am a bit confused, for Kmalloc, you manually returned NonNull::dangling when allocating a zero-sized allocation, but here you don't?
---
Cheers,
Benno
Quoted 21 lines
rust/kernel/alloc/allocator.rs +3
-0+ } +} +#[global_allocator] static ALLOCATOR :Kmalloc =Kmalloc ;rust/kernel/alloc/allocator_test.rs +1
-1@@ -7,6 +7,7 @@use core::ptr::NonNull ;pub struct Kmalloc ; +pub type Vmalloc =Kmalloc ;unsafe impl Allocator for Kmalloc { unsafe fn realloc( --2.45.2
Danilo Krummrich
maintainerRe: [PATCH v4 06/28] rust: alloc: implement `Vmalloc` allocator
Quoted 56 lines · Benno Lossin
Quoted 55 lines · Danilo Krummrich
Implement
AllocatorforVmalloc, the kernel's virtually contiguous allocator, typically used for larger objects, (much) larger than page size.All memory allocations made with
Vmallocend up invrealloc().Reviewed-by Alice Ryhl <[email protected]>Signed-off-by Danilo Krummrich <[email protected]>--- rust/helpers.c | 7 +++++++ rust/kernel/alloc/allocator.rs | 32 +++++++++++++++++++++++++++++ rust/kernel/alloc/allocator_test.rs | 1 + 3 files changed, 40 insertions(+)rust/helpers.c +7
-0@@ -33,6 +33,7 @@#include <linux/sched/signal.h> #include <linux/slab.h> #include <linux/spinlock.h> +#include <linux/vmalloc.h> #include <linux/wait.h> #include <linux/workqueue.h> @@ -199,6 +200,12 @@ void *rust_helper_krealloc(const void *objp, size_t new_size, gfp_t flags) }EXPORT_SYMBOL_GPL (rust_helper_krealloc); +void *rust_helper_vrealloc (const void *p,size_t size,gfp_t flags) +{ +return vrealloc (p, size, flags); +} +EXPORT_SYMBOL_GPL (rust_helper_vrealloc); +/* * `bindgen` binds the C `size_t` type as the Rust `usize` type, so we can * use it in contexts where Rust expects a `usize` like slice (array) indices. rust/kernel/alloc/allocator.rs +1
-0@@ -9,6 +9,7 @@use crate ::alloc::{AllocError ,Allocator };use crate ::bindings; +use crate ::pr_warn;/// The contiguous kernel allocator. /// @@ -16,6 +17,12 @@/// `bindings::krealloc`. pub struct Kmalloc ;
Quoted 9 lines
rust/kernel/alloc/allocator.rs line 24
Quoted 5 lines
rust/kernel/alloc/allocator.rs +5
-0+/// The virtually contiguous kernel allocator. +/// +/// The vmalloc allocator allocates pages from the page level allocator and maps them into the +/// contiguous kernel virtual space. +pub struct Vmalloc ;One thing that I should also have mentioned for
Kmalloc, do we want these types to also have values? I don't think that we need them to be, so we could declare them aspub enum Vmalloc {}.
What the difference? Would pub enum Vmalloc {} be better for some reason?
Quoted 41 lines
rust/kernel/alloc/allocator.rs fn krealloc() -> Self { · line 71
Quoted 10 lines
rust/kernel/alloc/allocator.rs +2
-0+/// Returns a proper size to alloc a new object aligned to `new_layout`'s alignment. fn aligned_size (new_layout:Layout ) ->usize {// Customized layouts from `Layout::from_size_align()` can have size < align, so pad first. @@ -61,6 +68,11 @@ fn krealloc() -> Self {Self (bindings::krealloc) } +// INVARIANT: `vrealloc` satisfies the type invariants. This should be moved down a line.
---
Cheers,
BennoQuoted 24 lines
rust/kernel/alloc/allocator.rs +17
-0+fn vrealloc () ->Self { +Self (bindings::vrealloc) + } +/// # Safety /// /// This method has the exact same safety requirements as `Allocator::realloc`. @@ -141,6 +153,26 @@ unsafe fn alloc_zeroed(&self, layout: Layout) -> *mut u8 { } } +unsafe impl Allocator for Vmalloc { +unsafe fn realloc ( + ptr:Option <NonNull <u8 >>, + layout:Layout , + flags:Flags , + ) ->Result <NonNull <[u8 ]>,AllocError > { +let realloc =ReallocFunc ::vrealloc (); + +// TODO: Support alignments larger than PAGE_SIZE. +if layout.align () > bindings::PAGE_SIZE { +pr_warn! ("Vmalloc does not support alignments larger than PAGE_SIZE yet.\n" ); +return Err (AllocError ); + }
Quoted 9 lines
rust/kernel/alloc/allocator.rs unsafe fn alloc_zeroed(&self, layout: Layout) -> *mut u8 { · line 172
Quoted 4 lines
rust/kernel/alloc/allocator.rs +4
-0+ +// SAFETY: If not `None`, `ptr` is guaranteed to point to valid memory, which was previously +// allocated with this `Allocator`. +unsafe { realloc.call (ptr, layout, flags) }I am a bit confused, for
Kmalloc, you manually returnedNonNull::danglingwhen allocating a zero-sized allocation, but here you don't?
I do, it's the exact same implementation for krealloc(), vrealloc() and kvrealloc(). That why I added the ReallocFunc abstraction.
Quoted 26 lines
---
Cheers,
BennoQuoted 21 lines
rust/kernel/alloc/allocator.rs +3
-0+ } +} +#[global_allocator] static ALLOCATOR :Kmalloc =Kmalloc ;rust/kernel/alloc/allocator_test.rs +1
-1@@ -7,6 +7,7 @@use core::ptr::NonNull ;pub struct Kmalloc ; +pub type Vmalloc =Kmalloc ;unsafe impl Allocator for Kmalloc { unsafe fn realloc( --2.45.2
Re: [PATCH v4 06/28] rust: alloc: implement `Vmalloc` allocator
Quoted 67 lines · Danilo Krummrich
Quoted 56 lines · Benno Lossin
Quoted 55 lines · Danilo Krummrich
Implement
AllocatorforVmalloc, the kernel's virtually contiguous allocator, typically used for larger objects, (much) larger than page size.All memory allocations made with
Vmallocend up invrealloc().Reviewed-by Alice Ryhl <[email protected]>Signed-off-by Danilo Krummrich <[email protected]>--- rust/helpers.c | 7 +++++++ rust/kernel/alloc/allocator.rs | 32 +++++++++++++++++++++++++++++ rust/kernel/alloc/allocator_test.rs | 1 + 3 files changed, 40 insertions(+)rust/helpers.c +7
-0@@ -33,6 +33,7 @@#include <linux/sched/signal.h> #include <linux/slab.h> #include <linux/spinlock.h> +#include <linux/vmalloc.h> #include <linux/wait.h> #include <linux/workqueue.h> @@ -199,6 +200,12 @@ void *rust_helper_krealloc(const void *objp, size_t new_size, gfp_t flags) }EXPORT_SYMBOL_GPL (rust_helper_krealloc); +void *rust_helper_vrealloc (const void *p,size_t size,gfp_t flags) +{ +return vrealloc (p, size, flags); +} +EXPORT_SYMBOL_GPL (rust_helper_vrealloc); +/* * `bindgen` binds the C `size_t` type as the Rust `usize` type, so we can * use it in contexts where Rust expects a `usize` like slice (array) indices. rust/kernel/alloc/allocator.rs +1
-0@@ -9,6 +9,7 @@use crate ::alloc::{AllocError ,Allocator };use crate ::bindings; +use crate ::pr_warn;/// The contiguous kernel allocator. /// @@ -16,6 +17,12 @@/// `bindings::krealloc`. pub struct Kmalloc ;Quoted 9 lines
rust/kernel/alloc/allocator.rs line 24
Quoted 5 lines
rust/kernel/alloc/allocator.rs +5
-0+/// The virtually contiguous kernel allocator. +/// +/// The vmalloc allocator allocates pages from the page level allocator and maps them into the +/// contiguous kernel virtual space. +pub struct Vmalloc ;One thing that I should also have mentioned for
Kmalloc, do we want these types to also have values? I don't think that we need them to be, so we could declare them aspub enum Vmalloc {}.
Quoted 1 line
What the difference? Would
pub enum Vmalloc {}be better for some reason?
It doesn't make a huge difference, it doesn't allow you to create a value of type Vmalloc (as there are no values of that type). So you can't accidentally use the type where it shouldn't be used. If we use pub struct Vmalloc;, then you can do this:
let a = Vmalloc;
you can't really do anything with it (as there are no methods on that type), but it might be confusing for people.
Quoted 27 lines
Quoted 17 lines
Quoted 17 lines
rust/kernel/alloc/allocator.rs +13
-0@@ -141,6 +153,26 @@ unsafe fn alloc_zeroed(&self, layout: Layout) -> *mut u8 { } } +unsafe impl Allocator for Vmalloc { +unsafe fn realloc ( + ptr:Option <NonNull <u8 >>, + layout:Layout , + flags:Flags , + ) ->Result <NonNull <[u8 ]>,AllocError > { +let realloc =ReallocFunc ::vrealloc (); + +// TODO: Support alignments larger than PAGE_SIZE. +if layout.align () > bindings::PAGE_SIZE { +pr_warn! ("Vmalloc does not support alignments larger than PAGE_SIZE yet.\n" ); +return Err (AllocError ); + }Quoted 9 lines
rust/kernel/alloc/allocator.rs unsafe fn alloc_zeroed(&self, layout: Layout) -> *mut u8 { · line 172
Quoted 4 lines
rust/kernel/alloc/allocator.rs +4
-0+ +// SAFETY: If not `None`, `ptr` is guaranteed to point to valid memory, which was previously +// allocated with this `Allocator`. +unsafe { realloc.call (ptr, layout, flags) }I am a bit confused, for
Kmalloc, you manually returnedNonNull::danglingwhen allocating a zero-sized allocation, but here you don't?
Quoted 2 lines
I do, it's the exact same implementation for
krealloc(),vrealloc()andkvrealloc(). That why I added theReallocFuncabstraction.
Oh yeah, my bad.
---
Cheers
Benno
Danilo Krummrich
maintainerPATCH v4 7/28 rust: alloc: implement `KVmalloc` allocator
In reply to Danilo KrummrichImplement Allocator for KVmalloc, an Allocator that tries to allocate memory wth kmalloc first and, on failure, falls back to vmalloc.
All memory allocations made with KVmalloc end up in kvrealloc_noprof(); all frees in kvfree().
--- rust/helpers.c | 6 ++++++ rust/kernel/alloc/allocator.rs | 31 +++++++++++++++++++++++++++++ rust/kernel/alloc/allocator_test.rs | 1 + 3 files changed, 38 insertions(+)
rust/helpers.c +6 -0
@@ -206,6 +206,12 @@ void *rust_helper_vrealloc(const void *p, size_t size, gfp_t flags) }EXPORT_SYMBOL_GPL (rust_helper_vrealloc); +void *rust_helper_kvrealloc (const void *p,size_t size,gfp_t flags) +{ +return kvrealloc (p, size, flags); +} +EXPORT_SYMBOL_GPL (rust_helper_kvrealloc); +/* * `bindgen` binds the C `size_t` type as the Rust `usize` type, so we can * use it in contexts where Rust expects a `usize` like slice (array) indices.
rust/kernel/alloc/allocator.rs +31 -0
@@ -23,6 +23,12 @@/// contiguous kernel virtual space. pub struct Vmalloc ; +/// The kvmalloc kernel allocator. +/// +/// Attempt to allocate physically contiguous memory, but upon failure, fall back to non-contiguous +/// (vmalloc) allocation. +pub struct KVmalloc ; +/// Returns a proper size to alloc a new object aligned to `new_layout`'s alignment. fn aligned_size (new_layout:Layout ) ->usize {// Customized layouts from `Layout::from_size_align()` can have size < align, so pad first.
@@ -73,6 +79,11 @@ fn vrealloc() -> Self {Self (bindings::vrealloc) } +// INVARIANT: `vrealloc` satisfies the type invariants. +fn kvrealloc () ->Self { +Self (bindings::kvrealloc) + } +/// # Safety /// /// This method has the exact same safety requirements as `Allocator::realloc`.
@@ -173,6 +184,26 @@ unsafe fn realloc( } } +unsafe impl Allocator for KVmalloc { +unsafe fn realloc ( + ptr:Option <NonNull <u8 >>, + layout:Layout , + flags:Flags , + ) ->Result <NonNull <[u8 ]>,AllocError > { +let realloc =ReallocFunc ::kvrealloc (); + +// TODO: Support alignments larger than PAGE_SIZE. +if layout.align () > bindings::PAGE_SIZE { +pr_warn! ("KVmalloc does not support alignments larger than PAGE_SIZE yet.\n" ); +return Err (AllocError ); + } + +// SAFETY: If not `None`, `ptr` is guaranteed to point to valid memory, which was previously +// allocated with this `Allocator`. +unsafe { realloc.call (ptr, layout, flags) } + } +} +#[global_allocator] static ALLOCATOR :Kmalloc =Kmalloc ;
rust/kernel/alloc/allocator_test.rs +1 -0
@@ -8,6 +8,7 @@pub struct Kmalloc ;pub type Vmalloc =Kmalloc ; +pub type KVmalloc =Kmalloc ;unsafe impl Allocator for Kmalloc { unsafe fn realloc(
Signature · 1 line
2.45.2
Danilo Krummrich
maintainerPATCH v4 8/28 rust: types: implement `Unique<T>`
In reply to Danilo KrummrichImplement the Unique type as a prerequisite for Box and Vec introduced in subsequent patches.
Unique serves as wrapper around a NonNull, but indicates that the possessor of this wrapper owns the referent.
This type already exists in Rust's core library, but, unfortunately, is exposed as unstable API and hence shouldn't be used in the kernel.
This implementation of Unique is almost identical, but mostly stripped down to the functionality we need for Box and Vec. Additionally, all unstable features are removed and / or replaced by stable ones.
--- rust/kernel/types.rs | 183 +++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 183 insertions(+)
rust/kernel/types.rs +183 -0
@@ -473,3 +473,186 @@ unsafe impl AsBytes for str {}// does not have any uninitialized portions either. unsafe impl <T :AsBytes >AsBytes for [T ] {}unsafe impl <T :AsBytes ,const N :usize >AsBytes for [T ;N ] {} + +/// A wrapper around a raw non-null `*mut T` that indicates that the possessor +/// of this wrapper owns the referent. Useful for building abstractions like +/// `Box<T>`, `Vec<T>`, `String`, and `HashMap<K, V>`. +/// +/// Unlike `*mut T`, `Unique<T>` behaves "as if" it were an instance of `T`. +/// It implements `Send`/`Sync` if `T` is `Send`/`Sync`. It also implies +/// the kind of strong aliasing guarantees an instance of `T` can expect: +/// the referent of the pointer should not be modified without a unique path to +/// its owning Unique. +/// +/// If you're uncertain of whether it's correct to use `Unique` for your purposes, +/// consider using `NonNull`, which has weaker semantics. +/// +/// Unlike `*mut T`, the pointer must always be non-null, even if the pointer +/// is never dereferenced. This is so that enums may use this forbidden value +/// as a discriminant -- `Option<Unique<T>>` has the same size as `Unique<T>`. +/// However the pointer may still dangle if it isn't dereferenced. +/// +/// Unlike `*mut T`, `Unique<T>` is covariant over `T`. This should always be correct +/// for any type which upholds Unique's aliasing requirements. +#[repr(transparent)] +pub struct Unique <T : ?Sized > { + pointer:NonNull <T >, +// NOTE: this marker has no consequences for variance, but is necessary +// for dropck to understand that we logically own a `T`. +// +// For details, see: +// https://github.com/rust-lang/rfcs/blob/master/text/0769-sound-generic-drop.md#phantom-data + _marker:PhantomData <T >, +} + +/// `Unique` pointers are `Send` if `T` is `Send` because the data they +/// reference is unaliased. Note that this aliasing invariant is +/// unenforced by the type system; the abstraction using the +/// `Unique` must enforce it. +unsafe impl <T :Send + ?Sized >Send for Unique <T > {} + +/// `Unique` pointers are `Sync` if `T` is `Sync` because the data they +/// reference is unaliased. Note that this aliasing invariant is +/// unenforced by the type system; the abstraction using the +/// `Unique` must enforce it. +unsafe impl <T :Sync + ?Sized >Sync for Unique <T > {} + +impl <T :Sized >Unique <T > { +/// Creates a new `Unique` that is dangling, but well-aligned. +/// +/// This is useful for initializing types which lazily allocate, like +/// `Vec::new` does. +/// +/// Note that the pointer value may potentially represent a valid pointer to +/// a `T`, which means this must not be used as a "not yet initialized" +/// sentinel value. Types that lazily allocate must track initialization by +/// some other means. +#[must_use] +#[inline] +pub const fn dangling () ->Self { +Unique { + pointer:NonNull ::dangling (), + _marker:PhantomData , + } + } +} + +impl <T : ?Sized >Unique <T > { +/// Creates a new `Unique`. +/// +/// # Safety +/// +/// `ptr` must be non-null. +#[inline] +pub const unsafe fn new_unchecked (ptr: *mut T ) ->Self { +// SAFETY: the caller must guarantee that `ptr` is non-null. +unsafe { +Unique { + pointer:NonNull ::new_unchecked (ptr), + _marker:PhantomData , + } + } + } + +/// Creates a new `Unique` if `ptr` is non-null. +#[allow(clippy::manual_map)] +#[inline] +pub fn new (ptr: *mut T ) ->Option <Self > { +if let Some (pointer) =NonNull ::new (ptr) { +Some (Unique { + pointer, + _marker:PhantomData , + }) + }else { +None + } + } + +/// Acquires the underlying `*mut` pointer. +#[must_use = "`self` will be dropped if the result is not used" ] +#[inline] +pub const fn as_ptr (self) -> *mut T { + self.pointer.as_ptr () + } + +/// Dereferences the content. +/// +/// The resulting lifetime is bound to self so this behaves "as if" +/// it were actually an instance of T that is getting borrowed. If a longer +/// (unbound) lifetime is needed, use `&*my_ptr.as_ptr()`. +/// +/// # Safety +/// +/// Safety requirements for this function are inherited from [NonNull::as_ref]. +/// +#[must_use] +#[inline] +pub const unsafe fn as_ref (&self) -> &T { +// SAFETY: the caller must guarantee that `self` meets all the +// requirements for a reference. +unsafe { self.pointer.as_ref () } + } + +/// Mutably dereferences the content. +/// +/// The resulting lifetime is bound to self so this behaves "as if" +/// it were actually an instance of T that is getting borrowed. If a longer +/// (unbound) lifetime is needed, use `&mut *my_ptr.as_ptr()`. +/// +/// # Safety +/// +/// Safety requirements for this function are inherited from [NonNull::as_mut]. +#[must_use] +#[inline] +pub unsafe fn as_mut (&mut self) -> &mut T { +// SAFETY: the caller must guarantee that `self` meets all the +// requirements for a mutable reference. +unsafe { self.pointer.as_mut () } + } + +/// Casts to a pointer of another type. +#[must_use = "`self` will be dropped if the result is not used" ] +#[inline] +pub fn cast <U >(self) ->Unique <U > { +Unique ::from (self.pointer.cast ()) + } + +/// Acquires the underlying `*mut` pointer. +#[must_use = "`self` will be dropped if the result is not used" ] +#[inline] +pub const fn as_non_null (self) ->NonNull <T > { + self.pointer + } +} + +impl <T : ?Sized >Clone for Unique <T > { +#[inline] +fn clone (&self) ->Self { + *self + } +} + +impl <T : ?Sized >Copy for Unique <T > {} + +impl <T : ?Sized >From <&mut T >for Unique <T > { +/// Converts a `&mut T` to a `Unique<T>`. +/// +/// This conversion is infallible since references cannot be null. +#[inline] +fn from (reference: &mut T ) ->Self { +Self ::from (NonNull ::from (reference)) + } +} + +impl <T : ?Sized >From <NonNull <T >>for Unique <T > { +/// Converts a `NonNull<T>` to a `Unique<T>`. +/// +/// This conversion is infallible since `NonNull` cannot be null. +#[inline] +fn from (pointer:NonNull <T >) ->Self { +Unique { + pointer, + _marker:PhantomData , + } + } +}
Signature · 1 line
2.45.2
Re: [PATCH v4 08/28] rust: types: implement `Unique<T>`
Quoted 79 lines · Danilo Krummrich
Implement the
Uniquetype as a prerequisite forBoxandVecintroduced in subsequent patches.
Uniqueserves as wrapper around aNonNull, but indicates that the possessor of this wrapper owns the referent.This type already exists in Rust's core library, but, unfortunately, is exposed as unstable API and hence shouldn't be used in the kernel.
This implementation of
Uniqueis almost identical, but mostly stripped down to the functionality we need forBoxandVec. Additionally, all unstable features are removed and / or replaced by stable ones.Reviewed-by Alice Ryhl <[email protected]>Signed-off-by Danilo Krummrich <[email protected]>--- rust/kernel/types.rs | 183 +++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 183 insertions(+)rust/kernel/types.rs +52
-0@@ -473,3 +473,186 @@ unsafe impl AsBytes for str {}// does not have any uninitialized portions either. unsafe impl <T :AsBytes >AsBytes for [T ] {}unsafe impl <T :AsBytes ,const N :usize >AsBytes for [T ;N ] {} + +/// A wrapper around a raw non-null `*mut T` that indicates that the possessor +/// of this wrapper owns the referent. Useful for building abstractions like +/// `Box<T>`, `Vec<T>`, `String`, and `HashMap<K, V>`. +/// +/// Unlike `*mut T`, `Unique<T>` behaves "as if" it were an instance of `T`. +/// It implements `Send`/`Sync` if `T` is `Send`/`Sync`. It also implies +/// the kind of strong aliasing guarantees an instance of `T` can expect: +/// the referent of the pointer should not be modified without a unique path to +/// its owning Unique. +/// +/// If you're uncertain of whether it's correct to use `Unique` for your purposes, +/// consider using `NonNull`, which has weaker semantics. +/// +/// Unlike `*mut T`, the pointer must always be non-null, even if the pointer +/// is never dereferenced. This is so that enums may use this forbidden value +/// as a discriminant -- `Option<Unique<T>>` has the same size as `Unique<T>`. +/// However the pointer may still dangle if it isn't dereferenced. +/// +/// Unlike `*mut T`, `Unique<T>` is covariant over `T`. This should always be correct +/// for any type which upholds Unique's aliasing requirements. +#[repr(transparent)] +pub struct Unique <T : ?Sized > { + pointer:NonNull <T >, +// NOTE: this marker has no consequences for variance, but is necessary +// for dropck to understand that we logically own a `T`. +// +// For details, see: +// https://github.com/rust-lang/rfcs/blob/master/text/0769-sound-generic-drop.md#phantom-data + _marker:PhantomData <T >, +} + +/// `Unique` pointers are `Send` if `T` is `Send` because the data they +/// reference is unaliased. Note that this aliasing invariant is +/// unenforced by the type system; the abstraction using the +/// `Unique` must enforce it. +unsafe impl <T :Send + ?Sized >Send for Unique <T > {} + +/// `Unique` pointers are `Sync` if `T` is `Sync` because the data they +/// reference is unaliased. Note that this aliasing invariant is +/// unenforced by the type system; the abstraction using the +/// `Unique` must enforce it. +unsafe impl <T :Sync + ?Sized >Sync for Unique <T > {} + +impl <T :Sized >Unique <T > { +/// Creates a new `Unique` that is dangling, but well-aligned. +/// +/// This is useful for initializing types which lazily allocate, like +/// `Vec::new` does. +/// +/// Note that the pointer value may potentially represent a valid pointer to +/// a `T`, which means this must not be used as a "not yet initialized"
rust/kernel/types.rs unsafe impl AsBytes for str {} · line 537
Quoted 10 lines
rust/kernel/types.rs +10
-0+/// sentinel value. Types that lazily allocate must track initialization by +/// some other means. +#[must_use] +#[inline] +pub const fn dangling () ->Self { +Unique { + pointer:NonNull ::dangling (), + _marker:PhantomData , + } + }
I think I already asked this, but the code until this point is copied from the rust stdlib and nowhere cited, does that work with the licensing?
I also think that the code above could use some improvements:
- add an # Invariants section with appropriate invariants (what are they supposed to be?)
- Do we really want this type to be public and exported from the kernel crate? I think it would be better if it were crate-private.
- What do we gain from having this type? As I learned recently, the Unique type from core doesn't actually put the noalias onto Box and Vec. The functions are mostly delegations to NonNull, so if the only advantages are that Send and Sync are already implemented, then I think we should drop this.
rust/kernel/types.rs unsafe impl AsBytes for str {} · line 549
Quoted 12 lines
rust/kernel/types.rs +12
-0+} + +impl <T : ?Sized >Unique <T > { +/// Creates a new `Unique`. +/// +/// # Safety +/// +/// `ptr` must be non-null. +#[inline] +pub const unsafe fn new_unchecked (ptr: *mut T ) ->Self { +// SAFETY: the caller must guarantee that `ptr` is non-null. +unsafe {
The only unsafe operation in the body is new_unchecked only that one should be wrapped in unsafe {}.
Quoted 9 lines
rust/kernel/types.rs +9
-0+Unique { + pointer:NonNull ::new_unchecked (ptr), + _marker:PhantomData , + } + } + } + +/// Creates a new `Unique` if `ptr` is non-null. +#[allow(clippy::manual_map)]
rust/kernel/types.rs unsafe impl AsBytes for str {} · line 568
Quoted 10 lines
rust/kernel/types.rs +10
-0+#[inline] +pub fn new (ptr: *mut T ) ->Option <Self > { +if let Some (pointer) =NonNull ::new (ptr) { +Some (Unique { + pointer, + _marker:PhantomData , + }) + }else { +None + }
Why is this so verbose? You even needed to disable the clippy lint! Can't this just be?:
Some(Unique {
pointer: NonNull::new(ptr)?,
_marker: PhantomData,
})or maybe even
NonNull::new(ptr).map(Unique::from)
rust/kernel/types.rs unsafe impl AsBytes for str {} · line 572
Quoted 4 lines
rust/kernel/types.rs +4
-0+ } + +/// Acquires the underlying `*mut` pointer. +#[must_use = "`self` will be dropped if the result is not used" ]
This seems like an odd thing, there is no Drop impl that drops the pointee...
---
Cheers,
Benno
Re: [PATCH v4 08/28] rust: types: implement `Unique<T>`
Quoted 4 lines · Benno Lossin
I think I already asked this, but the code until this point is copied from the rust stdlib and nowhere cited, does that work with the licensing?
No, it doesn't. They should be put into its own file with the right license and a paragraph about it, e.g. the other std_vendor.rs files we have (if it is just about Unique, it could go in the existing one at the kernel level).
Cheers,
Miguel
Danilo Krummrich
maintainerRe: [PATCH v4 08/28] rust: types: implement `Unique<T>`
Quoted 10 lines · Miguel Ojeda
Quoted 4 lines · Benno Lossin
I think I already asked this, but the code until this point is copied from the rust stdlib and nowhere cited, does that work with the licensing?
No, it doesn't. They should be put into its own file with the right license and a paragraph about it, e.g. the other
std_vendor.rsfiles we have (if it is just aboutUnique, it could go in the existing one at thekernellevel).
Thanks for catching this, I once thought about it and then forgot about it entirely.
I didn't know about std_vendor.rs, that's pretty convenient. I will move it over in case we don't drop Unique entirely.
Quoted 4 lines
Cheers,
Miguel
Danilo Krummrich
maintainerRe: [PATCH v4 08/28] rust: types: implement `Unique<T>`
In reply to Benno LossinQuoted 95 lines · Benno Lossin
Quoted 79 lines · Danilo Krummrich
Implement the
Uniquetype as a prerequisite forBoxandVecintroduced in subsequent patches.
Uniqueserves as wrapper around aNonNull, but indicates that the possessor of this wrapper owns the referent.This type already exists in Rust's core library, but, unfortunately, is exposed as unstable API and hence shouldn't be used in the kernel.
This implementation of
Uniqueis almost identical, but mostly stripped down to the functionality we need forBoxandVec. Additionally, all unstable features are removed and / or replaced by stable ones.Reviewed-by Alice Ryhl <[email protected]>Signed-off-by Danilo Krummrich <[email protected]>--- rust/kernel/types.rs | 183 +++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 183 insertions(+)rust/kernel/types.rs +52
-0@@ -473,3 +473,186 @@ unsafe impl AsBytes for str {}// does not have any uninitialized portions either. unsafe impl <T :AsBytes >AsBytes for [T ] {}unsafe impl <T :AsBytes ,const N :usize >AsBytes for [T ;N ] {} + +/// A wrapper around a raw non-null `*mut T` that indicates that the possessor +/// of this wrapper owns the referent. Useful for building abstractions like +/// `Box<T>`, `Vec<T>`, `String`, and `HashMap<K, V>`. +/// +/// Unlike `*mut T`, `Unique<T>` behaves "as if" it were an instance of `T`. +/// It implements `Send`/`Sync` if `T` is `Send`/`Sync`. It also implies +/// the kind of strong aliasing guarantees an instance of `T` can expect: +/// the referent of the pointer should not be modified without a unique path to +/// its owning Unique. +/// +/// If you're uncertain of whether it's correct to use `Unique` for your purposes, +/// consider using `NonNull`, which has weaker semantics. +/// +/// Unlike `*mut T`, the pointer must always be non-null, even if the pointer +/// is never dereferenced. This is so that enums may use this forbidden value +/// as a discriminant -- `Option<Unique<T>>` has the same size as `Unique<T>`. +/// However the pointer may still dangle if it isn't dereferenced. +/// +/// Unlike `*mut T`, `Unique<T>` is covariant over `T`. This should always be correct +/// for any type which upholds Unique's aliasing requirements. +#[repr(transparent)] +pub struct Unique <T : ?Sized > { + pointer:NonNull <T >, +// NOTE: this marker has no consequences for variance, but is necessary +// for dropck to understand that we logically own a `T`. +// +// For details, see: +// https://github.com/rust-lang/rfcs/blob/master/text/0769-sound-generic-drop.md#phantom-data + _marker:PhantomData <T >, +} + +/// `Unique` pointers are `Send` if `T` is `Send` because the data they +/// reference is unaliased. Note that this aliasing invariant is +/// unenforced by the type system; the abstraction using the +/// `Unique` must enforce it. +unsafe impl <T :Send + ?Sized >Send for Unique <T > {} + +/// `Unique` pointers are `Sync` if `T` is `Sync` because the data they +/// reference is unaliased. Note that this aliasing invariant is +/// unenforced by the type system; the abstraction using the +/// `Unique` must enforce it. +unsafe impl <T :Sync + ?Sized >Sync for Unique <T > {} + +impl <T :Sized >Unique <T > { +/// Creates a new `Unique` that is dangling, but well-aligned. +/// +/// This is useful for initializing types which lazily allocate, like +/// `Vec::new` does. +/// +/// Note that the pointer value may potentially represent a valid pointer to +/// a `T`, which means this must not be used as a "not yet initialized" rust/kernel/types.rs unsafe impl AsBytes for str {} · line 537
Quoted 10 lines
rust/kernel/types.rs +10
-0+/// sentinel value. Types that lazily allocate must track initialization by +/// some other means. +#[must_use] +#[inline] +pub const fn dangling () ->Self { +Unique { + pointer:NonNull ::dangling (), + _marker:PhantomData , + } + }I think I already asked this, but the code until this point is copied from the rust stdlib and nowhere cited, does that work with the licensing?
Quoted 10 lines
I also think that the code above could use some improvements:
- add an# Invariantssection with appropriate invariants (what are they supposed to be?)
- Do we really want this type to be public and exported from the kernel crate? I think it would be better if it were crate-private.
- What do we gain from having this type? As I learned recently, theUniquetype fromcoredoesn't actually put thenoaliasontoBoxandVec. The functions are mostly delegations toNonNull, so if the only advantages are thatSendandSyncare already implemented, then I think we should drop this.
I originally introduced it for the reasons described in [1], but mainly to make clear that the owner of this thing also owns the memory behind the pointer and the Send and Sync stuff you already mentioned.
If no one else has objections we can also just drop it. Personally, I'm fine either way.
[1] https://docs.rs/rust-libcore/latest/core/ptr/struct.Unique.html
Quoted 62 lines
rust/kernel/types.rs unsafe impl AsBytes for str {} · line 549
Quoted 13 lines
rust/kernel/types.rs +12
-0+} + +impl <T : ?Sized >Unique <T > { +/// Creates a new `Unique`. +/// +/// # Safety +/// +/// `ptr` must be non-null. +#[inline] +pub const unsafe fn new_unchecked (ptr: *mut T ) ->Self { +// SAFETY: the caller must guarantee that `ptr` is non-null. +unsafe {The only unsafe operation in the body is
new_uncheckedonly that one should be wrapped inunsafe {}.Quoted 9 lines
rust/kernel/types.rs +9
-0+Unique { + pointer:NonNull ::new_unchecked (ptr), + _marker:PhantomData , + } + } + } + +/// Creates a new `Unique` if `ptr` is non-null. +#[allow(clippy::manual_map)] rust/kernel/types.rs unsafe impl AsBytes for str {} · line 568
Quoted 10 lines
rust/kernel/types.rs +10
-0+#[inline] +pub fn new (ptr: *mut T ) ->Option <Self > { +if let Some (pointer) =NonNull ::new (ptr) { +Some (Unique { + pointer, + _marker:PhantomData , + }) + }else { +None + }Why is this so verbose? You even needed to disable the clippy lint! Can't this just be?:
Some(Unique { pointer: NonNull::new(ptr)?, _marker: PhantomData, })or maybe even
NonNull::new(ptr).map(Unique::from)rust/kernel/types.rs unsafe impl AsBytes for str {} · line 572
Quoted 4 lines
rust/kernel/types.rs +4
-0+ } + +/// Acquires the underlying `*mut` pointer. +#[must_use = "`self` will be dropped if the result is not used" ] This seems like an odd thing, there is no
Dropimpl that drops the pointee...---
Cheers,
Benno
Re: [PATCH v4 08/28] rust: types: implement `Unique<T>`
Quoted 36 lines · Danilo Krummrich
Quoted 24 lines · Benno Lossin
Quoted 8 lines · Danilo Krummrich
rust/kernel/types.rs +8
-0+impl <T :Sized >Unique <T > { +/// Creates a new `Unique` that is dangling, but well-aligned. +/// +/// This is useful for initializing types which lazily allocate, like +/// `Vec::new` does. +/// +/// Note that the pointer value may potentially represent a valid pointer to +/// a `T`, which means this must not be used as a "not yet initialized" rust/kernel/types.rs unsafe impl AsBytes for str {} · line 537
Quoted 10 lines
rust/kernel/types.rs +10
-0+/// sentinel value. Types that lazily allocate must track initialization by +/// some other means. +#[must_use] +#[inline] +pub const fn dangling () ->Self { +Unique { + pointer:NonNull ::dangling (), + _marker:PhantomData , + } + }I think I already asked this, but the code until this point is copied from the rust stdlib and nowhere cited, does that work with the licensing?
Quoted 10 lines
I also think that the code above could use some improvements:
- add an# Invariantssection with appropriate invariants (what are they supposed to be?)
- Do we really want this type to be public and exported from the kernel crate? I think it would be better if it were crate-private.
- What do we gain from having this type? As I learned recently, theUniquetype fromcoredoesn't actually put thenoaliasontoBoxandVec. The functions are mostly delegations toNonNull, so if the only advantages are thatSendandSyncare already implemented, then I think we should drop this.
Quoted 3 lines
I originally introduced it for the reasons described in [1], but mainly to make clear that the owner of this thing also owns the memory behind the pointer and the
SendandSyncstuff you already mentioned.
I would prefer if we make that explicit, since it is rather error-prone when creating new pointer types (and one should have to think about thread safety).
---
Cheers,
Benno
Quoted 4 lines
If no one else has objections we can also just drop it. Personally, I'm fine either way.
[1] https://docs.rs/rust-libcore/latest/core/ptr/struct.Unique.html
Danilo Krummrich
maintainerRe: [PATCH v4 08/28] rust: types: implement `Unique<T>`
Quoted 37 lines · Benno Lossin
Quoted 36 lines · Danilo Krummrich
Quoted 34 lines · Benno Lossin
Quoted 8 lines · Danilo Krummrich
rust/kernel/types.rs +8
-0+impl <T :Sized >Unique <T > { +/// Creates a new `Unique` that is dangling, but well-aligned. +/// +/// This is useful for initializing types which lazily allocate, like +/// `Vec::new` does. +/// +/// Note that the pointer value may potentially represent a valid pointer to +/// a `T`, which means this must not be used as a "not yet initialized" rust/kernel/types.rs unsafe impl AsBytes for str {} · line 537
Quoted 10 lines
rust/kernel/types.rs +10
-0+/// sentinel value. Types that lazily allocate must track initialization by +/// some other means. +#[must_use] +#[inline] +pub const fn dangling () ->Self { +Unique { + pointer:NonNull ::dangling (), + _marker:PhantomData , + } + }I think I already asked this, but the code until this point is copied from the rust stdlib and nowhere cited, does that work with the licensing?
I also think that the code above could use some improvements:
- add an# Invariantssection with appropriate invariants (what are they supposed to be?)
- Do we really want this type to be public and exported from the kernel crate? I think it would be better if it were crate-private.
- What do we gain from having this type? As I learned recently, theUniquetype fromcoredoesn't actually put thenoaliasontoBoxandVec. The functions are mostly delegations toNonNull, so if the only advantages are thatSendandSyncare already implemented, then I think we should drop this.
Quoted 7 lines
Quoted 3 lines
I originally introduced it for the reasons described in [1], but mainly to make clear that the owner of this thing also owns the memory behind the pointer and the
SendandSyncstuff you already mentioned.I would prefer if we make that explicit, since it is rather error-prone when creating new pointer types (and one should have to think about thread safety).
Again, fine for me. If no one else has objections I'll just drop Unique.
Quoted 10 lines
---
Cheers,
BennoQuoted 4 lines
If no one else has objections we can also just drop it. Personally, I'm fine either way.
[1] https://docs.rs/rust-libcore/latest/core/ptr/struct.Unique.html
Danilo Krummrich
maintainerPATCH v4 9/28 rust: alloc: implement kernel `Box`
In reply to Danilo KrummrichBox provides the simplest way to allocate memory for a generic type with one of the kernel's allocators, e.g. Kmalloc, Vmalloc or KVmalloc.
In contrast to Rust's Box type, the kernel Box type considers the kernel's GFP flags for all appropriate functions, always reports allocation failures through Result<_, AllocError> and remains independent from unstable features.
--- rust/kernel/alloc.rs | 6 + rust/kernel/alloc/kbox.rs | 330 ++++++++++++++++++++++++++++++++++++++ rust/kernel/init.rs | 35 +++- rust/kernel/prelude.rs | 2 +- rust/kernel/types.rs | 56 +++++++ 5 files changed, 427 insertions(+), 2 deletions(-) create mode 100644 rust/kernel/alloc/kbox.rs
rust/kernel/alloc.rs +6 -0
@@ -5,6 +5,7 @@#[cfg(not(any(test, testlib)))] pub mod allocator;pub mod box_ext; +pub mod kbox;pub mod vec_ext;#[cfg(any(test, testlib))]
@@ -13,6 +14,11 @@#[cfg(any(test, testlib))] pub use self::allocator_testas allocator; +pub use self::kbox::Box ; +pub use self::kbox::KBox ; +pub use self::kbox::KVBox ; +pub use self::kbox::VBox ; +/// Indicates an allocation error. #[derive( Copy , Clone , PartialEq , Eq , Debug )] pub struct AllocError ;
rust/kernel/alloc/kbox.rs new file +330 -0
330 lines not on this page · raw message
rust/kernel/init.rs +34 -1
@@ -211,7 +211,7 @@//! [`pin_init!`]: crate::pin_init! use crate ::{ - alloc::{box_ext::BoxExt ,AllocError ,Flags }, + alloc::{box_ext::BoxExt ,AllocError ,Allocator ,Flags }, error::{self,Error }, sync::UniqueArc , types::{Opaque ,ScopeGuard },
@@ -1178,6 +1178,39 @@ fn try_init<E>(init: impl Init<T, E>, flags: Flags) -> Result<Self, E> } } +impl <T ,A >InPlaceInit <T >for crate ::alloc::Box <T ,A > +where +A :Allocator + 'static, +{ +#[inline] +fn try_pin_init <E >(init:impl PinInit <T ,E >, flags:Flags ) ->Result <Pin <Self >,E > +where +E :From <AllocError >, + { +let mut this =crate ::alloc::Box ::<_ ,A >::new_uninit (flags)?; +let slot = this.as_mut_ptr (); +// SAFETY: When init errors/panics, slot will get deallocated but not dropped, +// slot is valid and will not be moved, because we pin it later. +unsafe { init.__pinned_init (slot)? }; +// SAFETY: All fields have been initialized. +Ok (unsafe {crate ::alloc::Box ::assume_init (this) }.into ()) + } + +#[inline] +fn try_init <E >(init:impl Init <T ,E >, flags:Flags ) ->Result <Self ,E > +where +E :From <AllocError >, + { +let mut this =crate ::alloc::Box ::<_ ,A >::new_uninit (flags)?; +let slot = this.as_mut_ptr (); +// SAFETY: When init errors/panics, slot will get deallocated but not dropped, +// slot is valid. +unsafe { init.__init (slot)? }; +// SAFETY: All fields have been initialized. +Ok (unsafe {crate ::alloc::Box ::assume_init (this) }) + } +} +impl <T >InPlaceInit <T >for UniqueArc <T > {#[inline] fn try_pin_init <E >(init:impl PinInit <T ,E >, flags:Flags ) ->Result <Pin <Self >,E >
rust/kernel/prelude.rs +1 -1
@@ -14,7 +14,7 @@#[doc(no_inline)] pub use core::pin::Pin ; -pub use crate ::alloc::{box_ext::BoxExt , flags::*, vec_ext::VecExt }; +pub use crate ::alloc::{box_ext::BoxExt , flags::*, vec_ext::VecExt ,KBox ,KVBox ,VBox };#[doc(no_inline)] pub use alloc::{boxed::Box , vec::Vec };
rust/kernel/types.rs +56 -0
74 lines not on this page · raw message
Signature · 1 line
2.45.2
Re: [PATCH v4 09/28] rust: alloc: implement kernel `Box`
Quoted 66 lines · Danilo Krummrich
Boxprovides the simplest way to allocate memory for a generic type with one of the kernel's allocators, e.g.Kmalloc,VmallocorKVmalloc.In contrast to Rust's
Boxtype, the kernelBoxtype considers the kernel's GFP flags for all appropriate functions, always reports allocation failures throughResult<_, AllocError>and remains independent from unstable features.Signed-off-by Danilo Krummrich <[email protected]>--- rust/kernel/alloc.rs | 6 + rust/kernel/alloc/kbox.rs | 330 ++++++++++++++++++++++++++++++++++++++ rust/kernel/init.rs | 35 +++- rust/kernel/prelude.rs | 2 +- rust/kernel/types.rs | 56 +++++++ 5 files changed, 427 insertions(+), 2 deletions(-) create mode 100644 rust/kernel/alloc/kbox.rsrust/kernel/alloc.rs +6
-0@@ -5,6 +5,7 @@#[cfg(not(any(test, testlib)))] pub mod allocator;pub mod box_ext; +pub mod kbox;pub mod vec_ext;#[cfg(any(test, testlib))] @@ -13,6 +14,11 @@#[cfg(any(test, testlib))] pub use self::allocator_testas allocator; +pub use self::kbox::Box ; +pub use self::kbox::KBox ; +pub use self::kbox::KVBox ; +pub use self::kbox::VBox ; +/// Indicates an allocation error. #[derive( Copy , Clone , PartialEq , Eq , Debug )] pub struct AllocError ;rust/kernel/alloc/kbox.rs new file +17
-0@@ -0,0 +1,330 @@ +// SPDX-License-Identifier: GPL-2.0 + +//! Implementation of [`Box`]. + +use super ::{AllocError ,Allocator ,Flags }; +use core::fmt; +use core::marker::PhantomData ; +use core::mem::ManuallyDrop ; +use core::mem::MaybeUninit ; +use core::ops::{Deref ,DerefMut }; +use core::pin::Pin ; +use core::result::Result ; + +use crate ::types::Unique ; + +/// The kernel's [`Box`] type. +///
rust/kernel/alloc/kbox.rs line 27
Quoted 10 lines
rust/kernel/alloc/kbox.rs new file +10
-0+/// `Box` provides the simplest way to allocate memory for a generic type with one of the kernel's +/// allocators, e.g. `Kmalloc`, `Vmalloc` or `KVmalloc`. +/// +/// For non-zero-sized values, a [`Box`] will use the given allocator `A` for its allocation. For +/// the most common allocators the type aliases `KBox`, `VBox` and `KVBox` exist. +/// +/// It is valid to convert both ways between a [`Box`] and a raw pointer allocated with any +/// `Allocator`, given that the `Layout` used with the allocator is correct for the type. +/// +/// For zero-sized values the [`Box`]' pointer must be `dangling_mut::<T>`; no memory is allocated.
Why do we need this to be in the docs?
rust/kernel/alloc/kbox.rs line 30
Quoted 3 lines
rust/kernel/alloc/kbox.rs new file +3
-0+/// +/// So long as `T: Sized`, a `Box<T>` is guaranteed to be represented as a single pointer and is +/// also ABI-compatible with C pointers (i.e. the C type `T*`).
You did not make Box repr(transparent), so this is not true. Additionally, Box<T> (from stdlib) is not FFI-safe [1], this might be surprising, given that it is ABI-compatible (and the documentation seems to suggest that one *can* just use it across FFI). I think we should generally avoid using Box in glue code between Rust and C. I would remove this paragraph.
[1]: https://github.com/rust-lang/unsafe-code-guidelines/issues/334
I think there are also other improvements, how about the following (feel free to adapt it):
/// A heap allocation for a single value of type `T`.
///
/// When dropping a [`Box`], the value is also dropped and the heap memory is automatically freed.
///
/// This is the kernel's version of the Rust stdlib's `Box`. There are a couple, for example no
/// `noalias` attribute is emitted and partially moving out of a `Box` is not supported.
///
/// `Box` works with any of the kernel's allocators, e.g. [`Kmalloc`], [`Vmalloc`] or [`KVMalloc`].
/// There are aliases for `Box` with these allocators ([`KBox`], [`VBox`], [`KVBox`]).rust/kernel/alloc/kbox.rs line 32
Quoted 2 lines
rust/kernel/alloc/kbox.rs new file +2
-0+/// +/// # Invariants
Please move this section below the examples section.
rust/kernel/alloc/kbox.rs line 34
Quoted 2 lines
rust/kernel/alloc/kbox.rs new file +2
-0+/// +/// The [`Box`]' pointer always properly aligned and either points to memory allocated with `A` or,
"The [Box]' pointer" -> "self.0 is"
rust/kernel/alloc/kbox.rs line 45
Quoted 11 lines
rust/kernel/alloc/kbox.rs new file +11
-0+/// for zero-sized types, is a dangling pointer. +/// +/// # Examples +/// +/// ``` +/// let b = KBox::<u64>::new(24_u64, GFP_KERNEL)?; +/// +/// assert_eq!(*b, 24_u64); +/// +/// # Ok::<(), Error>(()) +/// ```
Do you think it would be a good idea to have an example that fails (ie allocate with Kmalloc more than PAGE_SIZE)?
rust/kernel/alloc/kbox.rs line 48
Quoted 3 lines
rust/kernel/alloc/kbox.rs new file +3
-0+/// +/// ``` +/// struct Huge([u8; 1 << 24]);
I know that this is ~16MB, but are there test-vms with less memory (I have no idea how much you normally run these at, I usually give my vms plenty of ram, but when testing, people might not [my intuition is telling me that 16MB should be fine, but I am not sure]).
Quoted 45 lines
rust/kernel/alloc/kbox.rs new file +45
-0+/// +/// assert!(KVBox::<Huge>::new_uninit(GFP_KERNEL).is_ok()); +/// ``` +pub struct Box <T : ?Sized ,A :Allocator >(Unique <T >,PhantomData <A >); + +/// Type alias for `Box` with a `Kmalloc` allocator. +/// +/// # Examples +/// +/// ``` +/// let b = KBox::new(24_u64, GFP_KERNEL)?; +/// +/// assert_eq!(*b, 24_u64); +/// +/// # Ok::<(), Error>(()) +/// ``` +pub type KBox <T > =Box <T ,super ::allocator::Kmalloc >; + +/// Type alias for `Box` with a `Vmalloc` allocator. +/// +/// # Examples +/// +/// ``` +/// let b = VBox::new(24_u64, GFP_KERNEL)?; +/// +/// assert_eq!(*b, 24_u64); +/// +/// # Ok::<(), Error>(()) +/// ``` +pub type VBox <T > =Box <T ,super ::allocator::Vmalloc >; + +/// Type alias for `Box` with a `KVmalloc` allocator. +/// +/// # Examples +/// +/// ``` +/// let b = KVBox::new(24_u64, GFP_KERNEL)?; +/// +/// assert_eq!(*b, 24_u64); +/// +/// # Ok::<(), Error>(()) +/// ``` +pub type KVBox <T > =Box <T ,super ::allocator::KVmalloc >; + +impl <T ,A >Box <T ,A >
rust/kernel/alloc/kbox.rs line 103
Quoted 10 lines
rust/kernel/alloc/kbox.rs new file +10
-0+where + T: ?Sized, + A: Allocator, +{ +/// Constructs a `Box<T, A>` from a raw pointer. +/// +/// # Safety +/// +/// `raw` must point to valid memory, previously allocated with `A`, and at least the size of +/// type `T`.
With this requirement and the invariant on Box, I am lead to believe that you can't use this for ZSTs, since they are not allocated with A. One solution would be to adjust this requirement. But I would rather use a different solution: we move the dangling pointer stuff into the allocator and also call it when T is a ZST (ie don't special case them in Box but in the impls of Allocator). That way this can stay as-is and the part about ZSTs in the invariant can be removed.
rust/kernel/alloc/kbox.rs line 106
Quoted 3 lines
rust/kernel/alloc/kbox.rs new file +3
-0+#[inline] +pub const unsafe fn from_raw (raw: *mut T ) ->Self { +// SAFETY: Validity of `raw` is guaranteed by the safety preconditions of this function.
This is not a safety requirement of Unique::new_unchecked. Instead it is a type invariant of Box, so it should be an INVARIANT comment. You still need to justify new_unchecked though (which is requires that raw is not NULL.
rust/kernel/alloc/kbox.rs line 107
Quoted 1 line
rust/kernel/alloc/kbox.rs new file +1
-0+Self (unsafe {Unique ::new_unchecked (raw) },PhantomData ::<A >)
You don't need the ::<A>.
rust/kernel/alloc/kbox.rs line 111
Quoted 4 lines
rust/kernel/alloc/kbox.rs new file +4
-0+ } + +/// Consumes the `Box<T>`, returning a wrapped raw pointer. +///
Please add a new paragraph: "This will not run the destructor of T and the allocation will stay alive indefinitely. Use [Box::from_raw] to recover the [Box], drop the value and free the allocation.".
Quoted 9 lines
rust/kernel/alloc/kbox.rs new file +9
-0+/// # Examples +/// +/// ``` +/// let x = KBox::new(24, GFP_KERNEL)?; +/// let ptr = KBox::into_raw(x); +/// let x = unsafe { KBox::from_raw(ptr) }; +/// +/// assert_eq!(*x, 24); +///
rust/kernel/alloc/kbox.rs line 130
Quoted 10 lines
rust/kernel/alloc/kbox.rs new file +10
-0+/// # Ok::<(), Error>(()) +/// ``` +#[inline] +pub fn into_raw (b:Self ) -> *mut T { +let b =ManuallyDrop ::new (b); + + b.0 .as_ptr () + } + +/// Consumes and leaks the `Box<T>`, returning a mutable reference, &'a mut T.
The last part seems a bit weird, it should definitely be enclosed in '`', but it also seems unnecessary. Instead I would stress that this will never drop the value and also never free the allocation.
Quoted 5 lines
rust/kernel/alloc/kbox.rs new file +5
-0+#[inline] +pub fn leak <'a>(b:Self ) -> &'amut T +where +T : 'a, + {
rust/kernel/alloc/kbox.rs line 145
Quoted 10 lines
rust/kernel/alloc/kbox.rs new file +10
-0+// SAFETY: `Box::into_raw` always returns a properly aligned and dereferenceable pointer +// which points to an initialized instance of `T`. +unsafe { &mut *Box ::into_raw (b) } + } + +/// Converts a `Box<T, A>` into a `Pin<Box<T, A>>`. +#[inline] +pub fn into_pin (b:Self ) ->Pin <Self > +where +A : 'static,
Why do we require this? Our Box doesn't store an allocator.
Quoted 8 lines
rust/kernel/alloc/kbox.rs new file +8
-0+ { +// SAFETY: It's not possible to move or replace the insides of a `Pin<Box<T, A>>` when +// `T: !Unpin`, so it's safe to pin it directly without any additional requirements. +unsafe {Pin ::new_unchecked (b) } + } +} + +impl <T ,A >Box <MaybeUninit <T >,A >
rust/kernel/alloc/kbox.rs line 163
Quoted 10 lines
rust/kernel/alloc/kbox.rs new file +10
-0+where + A: Allocator, +{ +/// Converts to `Box<T, A>`. +/// +/// # Safety +/// +/// As with MaybeUninit::assume_init, it is up to the caller to guarantee that the value really +/// is in an initialized state. Calling this when the content is not yet fully initialized +/// causes immediate undefined behavior.
This also looks like it was copied from the Rust stdlib, please see Miguel's response as to what to do about that.
Additionally, this Safety section is not up to par with the rest of the kernel, for me this sounds better:
/// The pointee must be a valid value of type `T`.
Quoted 52 lines
rust/kernel/alloc/kbox.rs new file +52
-0+pub unsafe fn assume_init (b:Self ) ->Box <T ,A > { +let raw =Self ::into_raw (b); +// SAFETY: Reconstruct the `Box<MaybeUninit<T>, A>` as Box<T, A> now that has been +// initialized. `raw` and `alloc` are safe by the invariants of `Box`. +unsafe {Box ::from_raw (rawas *mut T ) } + } + +/// Writes the value and converts to `Box<T, A>`. +pub fn write (mut b:Self , value:T ) ->Box <T ,A > { + (*b).write (value); +// SAFETY: We've just initialized `boxed`'s value. +unsafe {Self ::assume_init (b) } + } +} + +impl <T ,A >Box <T ,A > +where +A :Allocator , +{ +fn is_zst () ->bool { + core::mem::size_of ::<T >() ==0 + } + +/// Allocates memory with the allocator `A` and then places `x` into it. +/// +/// This doesn’t actually allocate if T is zero-sized. +pub fn new (x:T , flags:Flags ) ->Result <Self ,AllocError > { +let b =Self ::new_uninit (flags)?; +Ok (Box ::write (b, x)) + } + +/// Constructs a new `Box<T, A>` with uninitialized contents. +/// +/// # Examples +/// +/// ``` +/// let b = KBox::<u64>::new_uninit(GFP_KERNEL)?; +/// let b = KBox::write(b, 24); +/// +/// assert_eq!(*b, 24_u64); +/// +/// # Ok::<(), Error>(()) +/// ``` +pub fn new_uninit (flags:Flags ) ->Result <Box <MaybeUninit <T >,A >,AllocError > { +let ptr =if Self ::is_zst () { +Unique ::dangling () + }else { +let layout = core::alloc::Layout ::new ::<MaybeUninit <T >>(); +let ptr =A ::alloc (layout, flags)?; + + ptr.cast ().into () + };
rust/kernel/alloc/kbox.rs line 225
Quoted 10 lines
rust/kernel/alloc/kbox.rs new file +10
-0+ +Ok (Box (ptr,PhantomData ::<A >)) + } + +/// Constructs a new `Pin<Box<T, A>>`. If `T` does not implement [`Unpin`], then `x` will be +/// pinned in memory and unable to be moved. +#[inline] +pub fn pin (x:T , flags:Flags ) ->Result <Pin <Box <T ,A >>,AllocError > +where +A : 'static,
Again, we don't need this.
Quoted 14 lines
rust/kernel/alloc/kbox.rs new file +14
-0+ { +Ok (Self ::new (x, flags)?.into ()) + } + +/// Drops the contents, but keeps the allocation. +/// +/// # Examples +/// +/// ``` +/// let value = KBox::new([0; 32], GFP_KERNEL)?; +/// assert_eq!(*value, [0; 32]); +/// let value = KBox::drop_contents(value); +/// // Now we can re-use `value`: +/// let value = KBox::write(value, [1; 32]);
rust/kernel/alloc/kbox.rs line 249
Quoted 10 lines
rust/kernel/alloc/kbox.rs new file +10
-0+/// assert_eq!(*value, [1; 32]); +/// # Ok::<(), Error>(()) +/// ``` +pub fn drop_contents (this:Self ) ->Box <MaybeUninit <T >,A > { +let ptr =Box ::into_raw (this); +// SAFETY: `ptr` is valid, because it came from `Box::into_raw`. +unsafe { core::ptr::drop_in_place (ptr) }; +// SAFETY: `ptr` is valid, because it came from `Box::into_raw`. +unsafe {Box ::from_raw (ptr.cast ()) } + }
I don't particularly care in this instance, but you just took my patch and folded it into your own without asking me or specifying it in the commit message. In general I would have assumed that you just put the entire patch into the series (with correct From:... etc).
Quoted 9 lines
rust/kernel/alloc/kbox.rs new file +9
-0+} + +impl <T ,A >From <Box <T ,A >>for Pin <Box <T ,A >> +where +T : ?Sized , +A :Allocator , +A : 'static, +{ +/// Converts a `Box<T>` into a `Pin<Box<T>>`. If `T` does not implement [`Unpin`], then
rust/kernel/alloc/kbox.rs line 268
Quoted 10 lines
rust/kernel/alloc/kbox.rs new file +10
-0+/// `*boxed` will be pinned in memory and unable to be moved. +/// +/// This conversion does not allocate on the heap and happens in place. +/// +/// This is also available via [`Box::into_pin`]. +/// +/// Constructing and pinning a `Box` with <code><Pin<Box\<T>>>::from([Box::new]\(x))</code> +/// can also be written more concisely using <code>[Box::pin]\(x)</code>. +/// This `From` implementation is useful if you already have a `Box<T>`, or you are +/// constructing a (pinned) `Box` in a different way than with [`Box::new`].
This also looks very much like something from the stdlib...
Quoted 5 lines
rust/kernel/alloc/kbox.rs new file +5
-0+fn from (b:Box <T ,A >) ->Self { +Box ::into_pin (b) + } +} +
rust/kernel/alloc/kbox.rs line 283
Quoted 10 lines
rust/kernel/alloc/kbox.rs new file +10
-0+impl <T ,A >Deref for Box <T ,A > +where +T : ?Sized , +A :Allocator , +{ +type Target =T ; + +fn deref (&self) -> &T { +// SAFETY: `self.0` is always properly aligned, dereferenceable and points to an initialized +// instance of `T`.
If T is a ZST, then it is not dereferenceable.
Quoted 27 lines
rust/kernel/alloc/kbox.rs new file +27
-0+unsafe { self.0 .as_ref () } + } +} + +impl <T ,A >DerefMut for Box <T ,A > +where +T : ?Sized , +A :Allocator , +{ +fn deref_mut (&mut self) -> &mut T { +// SAFETY: `self.0` is always properly aligned, dereferenceable and points to an initialized +// instance of `T`. +unsafe { self.0 .as_mut () } + } +} + +impl <T ,A > fmt::Debug for Box <T ,A > +where +T : ?Sized + fmt::Debug , +A :Allocator , +{ +fn fmt (&self, f: &mut fmt::Formatter <'_>) -> fmt::Result { + fmt::Debug ::fmt (&**self, f) + } +} + +impl <T ,A >Drop for Box <T ,A >
rust/kernel/alloc/kbox.rs line 320
Quoted 10 lines
rust/kernel/alloc/kbox.rs new file +10
-0+where + T: ?Sized, + A: Allocator, +{ +fn drop (&mut self) { +let ptr = self.0 .as_ptr (); + +// SAFETY: `ptr` is always properly aligned, dereferenceable and points to an initialized +// instance of `T`. +let size =unsafe { core::mem::size_of_val (&*ptr) };
1. size_of_val is not unsafe.
2. why not use &*self instead of using the raw pointer? (then move the let binding below this line)
rust/kernel/alloc/kbox.rs line 325
Quoted 5 lines
rust/kernel/alloc/kbox.rs new file +5
-0+ +// SAFETY: We need to drop `self.0` in place, before we free the backing memory. +unsafe { core::ptr::drop_in_place (ptr) }; + +if size !=0 {
Making zero-sized allocations possible with Allocators would also simplify this.
Quoted 44 lines
rust/kernel/alloc/kbox.rs new file +5
-0+// SAFETY: `ptr` was previously allocated with `A`. +unsafe {A ::free (self.0 .as_non_null ().cast ()) }; + } + } +}rust/kernel/init.rs +23
-1@@ -211,7 +211,7 @@//! [`pin_init!`]: crate::pin_init! use crate ::{ - alloc::{box_ext::BoxExt ,AllocError ,Flags }, + alloc::{box_ext::BoxExt ,AllocError ,Allocator ,Flags }, error::{self,Error }, sync::UniqueArc , types::{Opaque ,ScopeGuard },@@ -1178,6 +1178,39 @@ fn try_init<E>(init: impl Init<T, E>, flags: Flags) -> Result<Self, E> } } +impl <T ,A >InPlaceInit <T >for crate ::alloc::Box <T ,A > +where +A :Allocator + 'static, +{ +#[inline] +fn try_pin_init <E >(init:impl PinInit <T ,E >, flags:Flags ) ->Result <Pin <Self >,E > +where +E :From <AllocError >, + { +let mut this =crate ::alloc::Box ::<_ ,A >::new_uninit (flags)?; +let slot = this.as_mut_ptr (); +// SAFETY: When init errors/panics, slot will get deallocated but not dropped, +// slot is valid and will not be moved, because we pin it later. +unsafe { init.__pinned_init (slot)? }; +// SAFETY: All fields have been initialized. +Ok (unsafe {crate ::alloc::Box ::assume_init (this) }.into ()) + } + +#[inline] +fn try_init <E >(init:impl Init <T ,E >, flags:Flags ) ->Result <Self ,E > +where +E :From <AllocError >,
rust/kernel/init.rs fn try_init<E>(init: impl Init<T, E>, flags: Flags) -> Result<Self, E> · line 1212
Quoted 10 lines
rust/kernel/init.rs +10
-0+ { +let mut this =crate ::alloc::Box ::<_ ,A >::new_uninit (flags)?; +let slot = this.as_mut_ptr (); +// SAFETY: When init errors/panics, slot will get deallocated but not dropped, +// slot is valid. +unsafe { init.__init (slot)? }; +// SAFETY: All fields have been initialized. +Ok (unsafe {crate ::alloc::Box ::assume_init (this) }) + } +}
Please move this impl into kbox.rs, for the stdlib Box, this was here, since we did not own that Box.
Quoted 84 lines
rust/kernel/init.rs +1
-0+impl <T >InPlaceInit <T >for UniqueArc <T > {#[inline] fn try_pin_init <E >(init:impl PinInit <T ,E >, flags:Flags ) ->Result <Pin <Self >,E >rust/kernel/prelude.rs +1
-1@@ -14,7 +14,7 @@#[doc(no_inline)] pub use core::pin::Pin ; -pub use crate ::alloc::{box_ext::BoxExt , flags::*, vec_ext::VecExt }; +pub use crate ::alloc::{box_ext::BoxExt , flags::*, vec_ext::VecExt ,KBox ,KVBox ,VBox };#[doc(no_inline)] pub use alloc::{boxed::Box , vec::Vec };rust/kernel/types.rs +45
-0@@ -2,6 +2,7 @@//! Kernel types. +use crate ::alloc::Allocator ;use crate ::init::{self,PinInit };use alloc::boxed::Box ;use core::{@@ -9,6 +10,7 @@ marker::{PhantomData, PhantomPinned}, mem::MaybeUninit, ops::{Deref, DerefMut}, + pin::Pin, ptr::NonNull, };@@ -89,6 +91,60 @@ unsafe fn from_foreign(ptr: *const core::ffi::c_void) -> Self { } } +impl <T : 'static,A >ForeignOwnable for crate ::alloc::Box <T ,A > +where +A :Allocator , +{ +type Borrowed <'a> = &'aT ; + +fn into_foreign (self) -> *const core::ffi::c_void { +crate ::alloc::Box ::into_raw (self)as _ + } + +unsafe fn borrow <'a>(ptr: *const core::ffi::c_void ) -> &'aT { +// SAFETY: The safety requirements for this function ensure that the object is still alive, +// so it is safe to dereference the raw pointer. +// The safety requirements of `from_foreign` also ensure that the object remains alive for +// the lifetime of the returned value. +unsafe { &*ptr.cast () } + } + +unsafe fn from_foreign (ptr: *const core::ffi::c_void ) ->Self { +// SAFETY: The safety requirements of this function ensure that `ptr` comes from a previous +// call to `Self::into_foreign`. +unsafe {crate ::alloc::Box ::from_raw (ptras _ ) } + } +} + +impl <T : 'static,A >ForeignOwnable for Pin <crate ::alloc::Box <T ,A >> +where +A :Allocator , +{ +type Borrowed <'a> =Pin <&'aT >; + +fn into_foreign (self) -> *const core::ffi::c_void { +// SAFETY: We are still treating the box as pinned. +crate ::alloc::Box ::into_raw (unsafe {Pin ::into_inner_unchecked (self) })as _ + } + +unsafe fn borrow <'a>(ptr: *const core::ffi::c_void ) ->Pin <&'aT > { +// SAFETY: The safety requirements for this function ensure that the object is still alive, +// so it is safe to dereference the raw pointer. +// The safety requirements of `from_foreign` also ensure that the object remains alive for +// the lifetime of the returned value. +let r =unsafe { &*ptr.cast () }; +
rust/kernel/types.rs unsafe fn from_foreign(ptr: *const core::ffi::c_void) -> Self { · line 146
Quoted 10 lines
rust/kernel/types.rs +10
-0+// SAFETY: This pointer originates from a `Pin<Box<T>>`. +unsafe {Pin ::new_unchecked (r) } + } + +unsafe fn from_foreign (ptr: *const core::ffi::c_void ) ->Self { +// SAFETY: The safety requirements of this function ensure that `ptr` comes from a previous +// call to `Self::into_foreign`. +unsafe {Pin ::new_unchecked (crate ::alloc::Box ::from_raw (ptras _ )) } + } +}
Ditto for these two.
---
Cheers,
Benno
Quoted 7 lines
rust/kernel/types.rs +1
-1+impl ForeignOwnable for () {type Borrowed <'a> = (); --2.45.2
Danilo Krummrich
maintainerRe: [PATCH v4 09/28] rust: alloc: implement kernel `Box`
Quoted 71 lines · Benno Lossin
Quoted 70 lines · Danilo Krummrich
Boxprovides the simplest way to allocate memory for a generic type with one of the kernel's allocators, e.g.Kmalloc,VmallocorKVmalloc.In contrast to Rust's
Boxtype, the kernelBoxtype considers the kernel's GFP flags for all appropriate functions, always reports allocation failures throughResult<_, AllocError>and remains independent from unstable features.Signed-off-by Danilo Krummrich <[email protected]>--- rust/kernel/alloc.rs | 6 + rust/kernel/alloc/kbox.rs | 330 ++++++++++++++++++++++++++++++++++++++ rust/kernel/init.rs | 35 +++- rust/kernel/prelude.rs | 2 +- rust/kernel/types.rs | 56 +++++++ 5 files changed, 427 insertions(+), 2 deletions(-) create mode 100644 rust/kernel/alloc/kbox.rsrust/kernel/alloc.rs +6
-0@@ -5,6 +5,7 @@#[cfg(not(any(test, testlib)))] pub mod allocator;pub mod box_ext; +pub mod kbox;pub mod vec_ext;#[cfg(any(test, testlib))] @@ -13,6 +14,11 @@#[cfg(any(test, testlib))] pub use self::allocator_testas allocator; +pub use self::kbox::Box ; +pub use self::kbox::KBox ; +pub use self::kbox::KVBox ; +pub use self::kbox::VBox ; +/// Indicates an allocation error. #[derive( Copy , Clone , PartialEq , Eq , Debug )] pub struct AllocError ;rust/kernel/alloc/kbox.rs new file +21
-0@@ -0,0 +1,330 @@ +// SPDX-License-Identifier: GPL-2.0 + +//! Implementation of [`Box`]. + +use super ::{AllocError ,Allocator ,Flags }; +use core::fmt; +use core::marker::PhantomData ; +use core::mem::ManuallyDrop ; +use core::mem::MaybeUninit ; +use core::ops::{Deref ,DerefMut }; +use core::pin::Pin ; +use core::result::Result ; + +use crate ::types::Unique ; + +/// The kernel's [`Box`] type. +/// +/// `Box` provides the simplest way to allocate memory for a generic type with one of the kernel's +/// allocators, e.g. `Kmalloc`, `Vmalloc` or `KVmalloc`. +/// +/// For non-zero-sized values, a [`Box`] will use the given allocator `A` for its allocation. For
Quoted 8 lines
rust/kernel/alloc/kbox.rs line 27
Quoted 6 lines
rust/kernel/alloc/kbox.rs new file +6
-0+/// the most common allocators the type aliases `KBox`, `VBox` and `KVBox` exist. +/// +/// It is valid to convert both ways between a [`Box`] and a raw pointer allocated with any +/// `Allocator`, given that the `Layout` used with the allocator is correct for the type. +/// +/// For zero-sized values the [`Box`]' pointer must be `dangling_mut::<T>`; no memory is allocated. Why do we need this to be in the docs?
Probably not - do you suggest to remove it entirely? Otherwise, where do you think we should move it?
Quoted 11 lines
rust/kernel/alloc/kbox.rs line 30
Quoted 4 lines
rust/kernel/alloc/kbox.rs new file +3
-0+/// +/// So long as `T: Sized`, a `Box<T>` is guaranteed to be represented as a single pointer and is +/// also ABI-compatible with C pointers (i.e. the C type `T*`). You did not make
Boxrepr(transparent), so this is not true. Additionally,Box<T>(from stdlib) is not FFI-safe [1], this might be surprising, given that it is ABI-compatible (and the documentation seems to suggest that one *can* just use it across FFI). I think we should generally avoid usingBoxin glue code between Rust and C. I would remove this paragraph.
Agreed.
Quoted 7 lines
[1]: https://github.com/rust-lang/unsafe-code-guidelines/issues/334
I think there are also other improvements, how about the following (feel free to adapt it):
Quoted 9 lines
/// A heap allocation for a single value of type `T`.
///
/// When dropping a [`Box`], the value is also dropped and the heap memory is automatically freed.
///
/// This is the kernel's version of the Rust stdlib's `Box`. There are a couple, for example no
/// `noalias` attribute is emitted and partially moving out of a `Box` is not supported.
///
/// `Box` works with any of the kernel's allocators, e.g. [`Kmalloc`], [`Vmalloc`] or [`KVMalloc`].
/// There are aliases for `Box` with these allocators ([`KBox`], [`VBox`], [`KVBox`]).That sounds good, I will take this with a few minor adjustments, thanks.
Quoted 11 lines
rust/kernel/alloc/kbox.rs line 32
Quoted 3 lines
rust/kernel/alloc/kbox.rs new file +2
-0+/// +/// # Invariants Please move this section below the examples section.
rust/kernel/alloc/kbox.rs line 34
Quoted 2 lines
rust/kernel/alloc/kbox.rs new file +2
-0+/// +/// The [`Box`]' pointer always properly aligned and either points to memory allocated with `A` or, "The [
Box]' pointer" -> "self.0is"
Quoted 14 lines
rust/kernel/alloc/kbox.rs line 45
Quoted 11 lines
rust/kernel/alloc/kbox.rs new file +11
-0+/// for zero-sized types, is a dangling pointer. +/// +/// # Examples +/// +/// ``` +/// let b = KBox::<u64>::new(24_u64, GFP_KERNEL)?; +/// +/// assert_eq!(*b, 24_u64); +/// +/// # Ok::<(), Error>(()) +/// ``` Do you think it would be a good idea to have an example that fails (ie allocate with Kmalloc more than
PAGE_SIZE)?
I think that's a good idea, I'll add one.
Please note that kmalloc() can allocate larger buffers than PAGE_SIZE. We can request something larger than KMALLOC_MAX_SIZE though.
Quoted 9 lines
rust/kernel/alloc/kbox.rs line 48
Quoted 4 lines
rust/kernel/alloc/kbox.rs new file +3
-0+/// +/// ``` +/// struct Huge([u8; 1 << 24]); I know that this is ~16MB, but are there test-vms with less memory (I have no idea how much you normally run these at, I usually give my vms plenty of ram, but when testing, people might not [my intuition is telling me that 16MB should be fine, but I am not sure]).
I think it's pretty reasonable to ask for 16MiB for a test case to succeed.
Quoted 57 lines
Quoted 46 lines
rust/kernel/alloc/kbox.rs new file +45
-0+/// +/// assert!(KVBox::<Huge>::new_uninit(GFP_KERNEL).is_ok()); +/// ``` +pub struct Box <T : ?Sized ,A :Allocator >(Unique <T >,PhantomData <A >); + +/// Type alias for `Box` with a `Kmalloc` allocator. +/// +/// # Examples +/// +/// ``` +/// let b = KBox::new(24_u64, GFP_KERNEL)?; +/// +/// assert_eq!(*b, 24_u64); +/// +/// # Ok::<(), Error>(()) +/// ``` +pub type KBox <T > =Box <T ,super ::allocator::Kmalloc >; + +/// Type alias for `Box` with a `Vmalloc` allocator. +/// +/// # Examples +/// +/// ``` +/// let b = VBox::new(24_u64, GFP_KERNEL)?; +/// +/// assert_eq!(*b, 24_u64); +/// +/// # Ok::<(), Error>(()) +/// ``` +pub type VBox <T > =Box <T ,super ::allocator::Vmalloc >; + +/// Type alias for `Box` with a `KVmalloc` allocator. +/// +/// # Examples +/// +/// ``` +/// let b = KVBox::new(24_u64, GFP_KERNEL)?; +/// +/// assert_eq!(*b, 24_u64); +/// +/// # Ok::<(), Error>(()) +/// ``` +pub type KVBox <T > =Box <T ,super ::allocator::KVmalloc >; + +impl <T ,A >Box <T ,A >rust/kernel/alloc/kbox.rs line 103
Quoted 10 lines
rust/kernel/alloc/kbox.rs new file +10
-0+where + T: ?Sized, + A: Allocator, +{ +/// Constructs a `Box<T, A>` from a raw pointer. +/// +/// # Safety +/// +/// `raw` must point to valid memory, previously allocated with `A`, and at least the size of +/// type `T`.
Quoted 7 lines
With this requirement and the invariant on
Box, I am lead to believe that you can't use this for ZSTs, since they are not allocated withA. One solution would be to adjust this requirement. But I would rather use a different solution: we move the dangling pointer stuff into the allocator and also call it whenTis a ZST (ie don't special case them inBoxbut in the impls ofAllocator). That way this can stay as-is and the part about ZSTs in the invariant can be removed.
Actually, we already got that. Every zero sized allocation will return a dangling pointer. However, we can't call Allocator::free with (any) dangling pointer though.
Quoted 9 lines
rust/kernel/alloc/kbox.rs line 106
Quoted 4 lines
rust/kernel/alloc/kbox.rs new file +3
-0+#[inline] +pub const unsafe fn from_raw (raw: *mut T ) ->Self { +// SAFETY: Validity of `raw` is guaranteed by the safety preconditions of this function. This is not a safety requirement of
Unique::new_unchecked. Instead it is a type invariant ofBox, so it should be an INVARIANT comment. You still need to justifynew_uncheckedthough (which is requires thatrawis not NULL.
Agreed.
Quoted 29 lines
rust/kernel/alloc/kbox.rs line 107
Quoted 2 lines
rust/kernel/alloc/kbox.rs new file +1
-0+Self (unsafe {Unique ::new_unchecked (raw) },PhantomData ::<A >)You don't need the
::<A>.rust/kernel/alloc/kbox.rs line 111
Quoted 4 lines
rust/kernel/alloc/kbox.rs new file +4
-0+ } + +/// Consumes the `Box<T>`, returning a wrapped raw pointer. +/// Please add a new paragraph: "This will not run the destructor of
Tand the allocation will stay alive indefinitely. Use [Box::from_raw] to recover the [Box], drop the value and free the allocation.".Quoted 15 lines
rust/kernel/alloc/kbox.rs new file +15
-0+/// # Examples +/// +/// ``` +/// let x = KBox::new(24, GFP_KERNEL)?; +/// let ptr = KBox::into_raw(x); +/// let x = unsafe { KBox::from_raw(ptr) }; +/// +/// assert_eq!(*x, 24); +/// +/// # Ok::<(), Error>(()) +/// ``` +#[inline] +pub fn into_raw (b:Self ) -> *mut T { +let b =ManuallyDrop ::new (b); +
Quoted 8 lines
rust/kernel/alloc/kbox.rs line 130
Quoted 4 lines
rust/kernel/alloc/kbox.rs new file +4
-0+ b.0 .as_ptr () + } + +/// Consumes and leaks the `Box<T>`, returning a mutable reference, &'a mut T. The last part seems a bit weird, it should definitely be enclosed in '`', but it also seems unnecessary. Instead I would stress that this will never drop the value and also never free the allocation.
Agreed, for this and the above.
Quoted 17 lines
Quoted 6 lines
rust/kernel/alloc/kbox.rs new file +5
-0+#[inline] +pub fn leak <'a>(b:Self ) -> &'amut T +where +T : 'a, + {rust/kernel/alloc/kbox.rs line 145
Quoted 10 lines
rust/kernel/alloc/kbox.rs new file +10
-0+// SAFETY: `Box::into_raw` always returns a properly aligned and dereferenceable pointer +// which points to an initialized instance of `T`. +unsafe { &mut *Box ::into_raw (b) } + } + +/// Converts a `Box<T, A>` into a `Pin<Box<T, A>>`. +#[inline] +pub fn into_pin (b:Self ) ->Pin <Self > +where +A : 'static,
Quoted 1 line
Why do we require this? Our
Boxdoesn't store an allocator.
I just forgot to remove it.
Quoted 14 lines
Quoted 14 lines
rust/kernel/alloc/kbox.rs new file +13
-0+ { +// SAFETY: It's not possible to move or replace the insides of a `Pin<Box<T, A>>` when +// `T: !Unpin`, so it's safe to pin it directly without any additional requirements. +unsafe {Pin ::new_unchecked (b) } + } +} + +impl <T ,A >Box <MaybeUninit <T >,A > +where +A :Allocator , +{ +/// Converts to `Box<T, A>`. +///
Quoted 8 lines
rust/kernel/alloc/kbox.rs line 163
Quoted 5 lines
rust/kernel/alloc/kbox.rs new file +5
-0+/// # Safety +/// +/// As with MaybeUninit::assume_init, it is up to the caller to guarantee that the value really +/// is in an initialized state. Calling this when the content is not yet fully initialized +/// causes immediate undefined behavior. This also looks like it was copied from the Rust stdlib, please see Miguel's response as to what to do about that.
Yeah, I think there are a few places more places where I forgot about that, will fix all of them.
Quoted 96 lines
Additionally, this Safety section is not up to par with the rest of the kernel, for me this sounds better:
/// The pointee must be a valid value of type `T`.Quoted 52 lines
rust/kernel/alloc/kbox.rs new file +52
-0+pub unsafe fn assume_init (b:Self ) ->Box <T ,A > { +let raw =Self ::into_raw (b); +// SAFETY: Reconstruct the `Box<MaybeUninit<T>, A>` as Box<T, A> now that has been +// initialized. `raw` and `alloc` are safe by the invariants of `Box`. +unsafe {Box ::from_raw (rawas *mut T ) } + } + +/// Writes the value and converts to `Box<T, A>`. +pub fn write (mut b:Self , value:T ) ->Box <T ,A > { + (*b).write (value); +// SAFETY: We've just initialized `boxed`'s value. +unsafe {Self ::assume_init (b) } + } +} + +impl <T ,A >Box <T ,A > +where +A :Allocator , +{ +fn is_zst () ->bool { + core::mem::size_of ::<T >() ==0 + } + +/// Allocates memory with the allocator `A` and then places `x` into it. +/// +/// This doesn’t actually allocate if T is zero-sized. +pub fn new (x:T , flags:Flags ) ->Result <Self ,AllocError > { +let b =Self ::new_uninit (flags)?; +Ok (Box ::write (b, x)) + } + +/// Constructs a new `Box<T, A>` with uninitialized contents. +/// +/// # Examples +/// +/// ``` +/// let b = KBox::<u64>::new_uninit(GFP_KERNEL)?; +/// let b = KBox::write(b, 24); +/// +/// assert_eq!(*b, 24_u64); +/// +/// # Ok::<(), Error>(()) +/// ``` +pub fn new_uninit (flags:Flags ) ->Result <Box <MaybeUninit <T >,A >,AllocError > { +let ptr =if Self ::is_zst () { +Unique ::dangling () + }else { +let layout = core::alloc::Layout ::new ::<MaybeUninit <T >>(); +let ptr =A ::alloc (layout, flags)?; + + ptr.cast ().into () + };rust/kernel/alloc/kbox.rs line 225
Quoted 10 lines
rust/kernel/alloc/kbox.rs new file +10
-0+ +Ok (Box (ptr,PhantomData ::<A >)) + } + +/// Constructs a new `Pin<Box<T, A>>`. If `T` does not implement [`Unpin`], then `x` will be +/// pinned in memory and unable to be moved. +#[inline] +pub fn pin (x:T , flags:Flags ) ->Result <Pin <Box <T ,A >>,AllocError > +where +A : 'static,Again, we don't need this.
Quoted 14 lines
rust/kernel/alloc/kbox.rs new file +14
-0+ { +Ok (Self ::new (x, flags)?.into ()) + } + +/// Drops the contents, but keeps the allocation. +/// +/// # Examples +/// +/// ``` +/// let value = KBox::new([0; 32], GFP_KERNEL)?; +/// assert_eq!(*value, [0; 32]); +/// let value = KBox::drop_contents(value); +/// // Now we can re-use `value`: +/// let value = KBox::write(value, [1; 32]); rust/kernel/alloc/kbox.rs line 249
Quoted 10 lines
rust/kernel/alloc/kbox.rs new file +10
-0+/// assert_eq!(*value, [1; 32]); +/// # Ok::<(), Error>(()) +/// ``` +pub fn drop_contents (this:Self ) ->Box <MaybeUninit <T >,A > { +let ptr =Box ::into_raw (this); +// SAFETY: `ptr` is valid, because it came from `Box::into_raw`. +unsafe { core::ptr::drop_in_place (ptr) }; +// SAFETY: `ptr` is valid, because it came from `Box::into_raw`. +unsafe {Box ::from_raw (ptr.cast ()) } + }
Quoted 4 lines
I don't particularly care in this instance, but you just took my patch and folded it into your own without asking me or specifying it in the commit message. In general I would have assumed that you just put the entire patch into the series (with correct From:... etc).
When I asked about this in [1] my understanding was that we expect [1] to land prior to this series. So, I'm just anticipating a future rebase where I move this code from box_ext.rs to kbox.rs, just like Alice suggested for her "ForeignOwnable for Pin<crate::alloc::Box<T, A>>" implementation.
I also understand your later reply, where you said: "[...] then you can just include it when you remove the BoxExit trait." as confirmation.
Probably that's a misunderstanding though. Sorry if that's the case.
[1] https://lore.kernel.org/lkml/[email protected]/
Quoted 14 lines
Quoted 14 lines
rust/kernel/alloc/kbox.rs new file +13
-0+} + +impl <T ,A >From <Box <T ,A >>for Pin <Box <T ,A >> +where +T : ?Sized , +A :Allocator , +A : 'static, +{ +/// Converts a `Box<T>` into a `Pin<Box<T>>`. If `T` does not implement [`Unpin`], then +/// `*boxed` will be pinned in memory and unable to be moved. +/// +/// This conversion does not allocate on the heap and happens in place. +///
Quoted 8 lines
rust/kernel/alloc/kbox.rs line 268
Quoted 6 lines
rust/kernel/alloc/kbox.rs new file +6
-0+/// This is also available via [`Box::into_pin`]. +/// +/// Constructing and pinning a `Box` with <code><Pin<Box\<T>>>::from([Box::new]\(x))</code> +/// can also be written more concisely using <code>[Box::pin]\(x)</code>. +/// This `From` implementation is useful if you already have a `Box<T>`, or you are +/// constructing a (pinned) `Box` in a different way than with [`Box::new`]. This also looks very much like something from the stdlib...
Yeah, I'll replace that.
Quoted 10 lines
Quoted 10 lines
rust/kernel/alloc/kbox.rs new file +9
-0+fn from (b:Box <T ,A >) ->Self { +Box ::into_pin (b) + } +} + +impl <T ,A >Deref for Box <T ,A > +where +T : ?Sized , +A :Allocator ,
Quoted 8 lines
rust/kernel/alloc/kbox.rs line 283
Quoted 6 lines
rust/kernel/alloc/kbox.rs new file +6
-0+{ +type Target =T ; + +fn deref (&self) -> &T { +// SAFETY: `self.0` is always properly aligned, dereferenceable and points to an initialized +// instance of `T`. If
Tis a ZST, then it is not dereferenceable.
Why not? If T is a ZST self.0 is Unique::<T>::dangling(). So, in the end this is the same as NonNull::<T>::dangling().as_ref().
Quoted 32 lines
Quoted 32 lines
rust/kernel/alloc/kbox.rs new file +31
-0+unsafe { self.0 .as_ref () } + } +} + +impl <T ,A >DerefMut for Box <T ,A > +where +T : ?Sized , +A :Allocator , +{ +fn deref_mut (&mut self) -> &mut T { +// SAFETY: `self.0` is always properly aligned, dereferenceable and points to an initialized +// instance of `T`. +unsafe { self.0 .as_mut () } + } +} + +impl <T ,A > fmt::Debug for Box <T ,A > +where +T : ?Sized + fmt::Debug , +A :Allocator , +{ +fn fmt (&self, f: &mut fmt::Formatter <'_>) -> fmt::Result { + fmt::Debug ::fmt (&**self, f) + } +} + +impl <T ,A >Drop for Box <T ,A > +where +T : ?Sized , +A :Allocator , +{
Quoted 8 lines
rust/kernel/alloc/kbox.rs line 320
Quoted 6 lines
rust/kernel/alloc/kbox.rs new file +6
-0+fn drop (&mut self) { +let ptr = self.0 .as_ptr (); + +// SAFETY: `ptr` is always properly aligned, dereferenceable and points to an initialized +// instance of `T`. +let size =unsafe { core::mem::size_of_val (&*ptr) };1.
size_of_valis notunsafe.
Right, but dereferencing the ptr is unsafe.
Quoted 2 lines
2. why not use
&*selfinstead of using the raw pointer? (then move the let binding below this line)
If we ever support non-ZST Allocators using self would not always evaluate to the correct size. I think evaluating the size of T rather than Box<T> is the correct thing to do.
Quoted 9 lines
rust/kernel/alloc/kbox.rs line 325
Quoted 6 lines
rust/kernel/alloc/kbox.rs new file +5
-0+ +// SAFETY: We need to drop `self.0` in place, before we free the backing memory. +unsafe { core::ptr::drop_in_place (ptr) }; + +if size !=0 {Making zero-sized allocations possible with Allocators would also simplify this.
As mentioned, it's possible already. But we still can't pass any dangling pointer to free.
Quoted 147 lines
Quoted 45 lines
rust/kernel/alloc/kbox.rs new file +5
-0+// SAFETY: `ptr` was previously allocated with `A`. +unsafe {A ::free (self.0 .as_non_null ().cast ()) }; + } + } +}rust/kernel/init.rs +23
-1@@ -211,7 +211,7 @@//! [`pin_init!`]: crate::pin_init! use crate ::{ - alloc::{box_ext::BoxExt ,AllocError ,Flags }, + alloc::{box_ext::BoxExt ,AllocError ,Allocator ,Flags }, error::{self,Error }, sync::UniqueArc , types::{Opaque ,ScopeGuard },@@ -1178,6 +1178,39 @@ fn try_init<E>(init: impl Init<T, E>, flags: Flags) -> Result<Self, E> } } +impl <T ,A >InPlaceInit <T >for crate ::alloc::Box <T ,A > +where +A :Allocator + 'static, +{ +#[inline] +fn try_pin_init <E >(init:impl PinInit <T ,E >, flags:Flags ) ->Result <Pin <Self >,E > +where +E :From <AllocError >, + { +let mut this =crate ::alloc::Box ::<_ ,A >::new_uninit (flags)?; +let slot = this.as_mut_ptr (); +// SAFETY: When init errors/panics, slot will get deallocated but not dropped, +// slot is valid and will not be moved, because we pin it later. +unsafe { init.__pinned_init (slot)? }; +// SAFETY: All fields have been initialized. +Ok (unsafe {crate ::alloc::Box ::assume_init (this) }.into ()) + } + +#[inline] +fn try_init <E >(init:impl Init <T ,E >, flags:Flags ) ->Result <Self ,E > +where +E :From <AllocError >,rust/kernel/init.rs fn try_init<E>(init: impl Init<T, E>, flags: Flags) -> Result<Self, E> · line 1212
Quoted 10 lines
rust/kernel/init.rs +10
-0+ { +let mut this =crate ::alloc::Box ::<_ ,A >::new_uninit (flags)?; +let slot = this.as_mut_ptr (); +// SAFETY: When init errors/panics, slot will get deallocated but not dropped, +// slot is valid. +unsafe { init.__init (slot)? }; +// SAFETY: All fields have been initialized. +Ok (unsafe {crate ::alloc::Box ::assume_init (this) }) + } +}Please move this impl into
kbox.rs, for the stdlibBox, this was here, since we did not own thatBox.Quoted 88 lines
rust/kernel/init.rs +1
-0+impl <T >InPlaceInit <T >for UniqueArc <T > {#[inline] fn try_pin_init <E >(init:impl PinInit <T ,E >, flags:Flags ) ->Result <Pin <Self >,E >rust/kernel/prelude.rs +1
-1@@ -14,7 +14,7 @@#[doc(no_inline)] pub use core::pin::Pin ; -pub use crate ::alloc::{box_ext::BoxExt , flags::*, vec_ext::VecExt }; +pub use crate ::alloc::{box_ext::BoxExt , flags::*, vec_ext::VecExt ,KBox ,KVBox ,VBox };#[doc(no_inline)] pub use alloc::{boxed::Box , vec::Vec };rust/kernel/types.rs +49
-0@@ -2,6 +2,7 @@//! Kernel types. +use crate ::alloc::Allocator ;use crate ::init::{self,PinInit };use alloc::boxed::Box ;use core::{@@ -9,6 +10,7 @@ marker::{PhantomData, PhantomPinned}, mem::MaybeUninit, ops::{Deref, DerefMut}, + pin::Pin, ptr::NonNull, };@@ -89,6 +91,60 @@ unsafe fn from_foreign(ptr: *const core::ffi::c_void) -> Self { } } +impl <T : 'static,A >ForeignOwnable for crate ::alloc::Box <T ,A > +where +A :Allocator , +{ +type Borrowed <'a> = &'aT ; + +fn into_foreign (self) -> *const core::ffi::c_void { +crate ::alloc::Box ::into_raw (self)as _ + } + +unsafe fn borrow <'a>(ptr: *const core::ffi::c_void ) -> &'aT { +// SAFETY: The safety requirements for this function ensure that the object is still alive, +// so it is safe to dereference the raw pointer. +// The safety requirements of `from_foreign` also ensure that the object remains alive for +// the lifetime of the returned value. +unsafe { &*ptr.cast () } + } + +unsafe fn from_foreign (ptr: *const core::ffi::c_void ) ->Self { +// SAFETY: The safety requirements of this function ensure that `ptr` comes from a previous +// call to `Self::into_foreign`. +unsafe {crate ::alloc::Box ::from_raw (ptras _ ) } + } +} + +impl <T : 'static,A >ForeignOwnable for Pin <crate ::alloc::Box <T ,A >> +where +A :Allocator , +{ +type Borrowed <'a> =Pin <&'aT >; + +fn into_foreign (self) -> *const core::ffi::c_void { +// SAFETY: We are still treating the box as pinned. +crate ::alloc::Box ::into_raw (unsafe {Pin ::into_inner_unchecked (self) })as _ + } + +unsafe fn borrow <'a>(ptr: *const core::ffi::c_void ) ->Pin <&'aT > { +// SAFETY: The safety requirements for this function ensure that the object is still alive, +// so it is safe to dereference the raw pointer. +// The safety requirements of `from_foreign` also ensure that the object remains alive for +// the lifetime of the returned value. +let r =unsafe { &*ptr.cast () }; + +// SAFETY: This pointer originates from a `Pin<Box<T>>`. +unsafe {Pin ::new_unchecked (r) } + } +
Quoted 8 lines
rust/kernel/types.rs unsafe fn from_foreign(ptr: *const core::ffi::c_void) -> Self { · line 146
Quoted 6 lines
rust/kernel/types.rs +6
-0+unsafe fn from_foreign (ptr: *const core::ffi::c_void ) ->Self { +// SAFETY: The safety requirements of this function ensure that `ptr` comes from a previous +// call to `Self::into_foreign`. +unsafe {Pin ::new_unchecked (crate ::alloc::Box ::from_raw (ptras _ )) } + } +}Ditto for these two.
Agreed, will do.
Quoted 13 lines
---
Cheers,
BennoQuoted 7 lines
rust/kernel/types.rs +1
-1+impl ForeignOwnable for () {type Borrowed <'a> = (); --2.45.2
Re: [PATCH v4 09/28] rust: alloc: implement kernel `Box`
Quoted 81 lines · Danilo Krummrich
Quoted 71 lines · Benno Lossin
Quoted 70 lines · Danilo Krummrich
Boxprovides the simplest way to allocate memory for a generic type with one of the kernel's allocators, e.g.Kmalloc,VmallocorKVmalloc.In contrast to Rust's
Boxtype, the kernelBoxtype considers the kernel's GFP flags for all appropriate functions, always reports allocation failures throughResult<_, AllocError>and remains independent from unstable features.Signed-off-by Danilo Krummrich <[email protected]>--- rust/kernel/alloc.rs | 6 + rust/kernel/alloc/kbox.rs | 330 ++++++++++++++++++++++++++++++++++++++ rust/kernel/init.rs | 35 +++- rust/kernel/prelude.rs | 2 +- rust/kernel/types.rs | 56 +++++++ 5 files changed, 427 insertions(+), 2 deletions(-) create mode 100644 rust/kernel/alloc/kbox.rsrust/kernel/alloc.rs +6
-0@@ -5,6 +5,7 @@#[cfg(not(any(test, testlib)))] pub mod allocator;pub mod box_ext; +pub mod kbox;pub mod vec_ext;#[cfg(any(test, testlib))] @@ -13,6 +14,11 @@#[cfg(any(test, testlib))] pub use self::allocator_testas allocator; +pub use self::kbox::Box ; +pub use self::kbox::KBox ; +pub use self::kbox::KVBox ; +pub use self::kbox::VBox ; +/// Indicates an allocation error. #[derive( Copy , Clone , PartialEq , Eq , Debug )] pub struct AllocError ;rust/kernel/alloc/kbox.rs new file +21
-0@@ -0,0 +1,330 @@ +// SPDX-License-Identifier: GPL-2.0 + +//! Implementation of [`Box`]. + +use super ::{AllocError ,Allocator ,Flags }; +use core::fmt; +use core::marker::PhantomData ; +use core::mem::ManuallyDrop ; +use core::mem::MaybeUninit ; +use core::ops::{Deref ,DerefMut }; +use core::pin::Pin ; +use core::result::Result ; + +use crate ::types::Unique ; + +/// The kernel's [`Box`] type. +/// +/// `Box` provides the simplest way to allocate memory for a generic type with one of the kernel's +/// allocators, e.g. `Kmalloc`, `Vmalloc` or `KVmalloc`. +/// +/// For non-zero-sized values, a [`Box`] will use the given allocator `A` for its allocation. For Quoted 8 lines
rust/kernel/alloc/kbox.rs line 27
Quoted 6 lines
rust/kernel/alloc/kbox.rs new file +6
-0+/// the most common allocators the type aliases `KBox`, `VBox` and `KVBox` exist. +/// +/// It is valid to convert both ways between a [`Box`] and a raw pointer allocated with any +/// `Allocator`, given that the `Layout` used with the allocator is correct for the type. +/// +/// For zero-sized values the [`Box`]' pointer must be `dangling_mut::<T>`; no memory is allocated. Why do we need this to be in the docs?
Quoted 2 lines
Probably not - do you suggest to remove it entirely? Otherwise, where do you think we should move it?
I would remove it, since it's just implementation detail and allocator-dependent.
Quoted 20 lines
Quoted 12 lines
rust/kernel/alloc/kbox.rs line 103
Quoted 11 lines
rust/kernel/alloc/kbox.rs new file +11
-0+impl <T ,A >Box <T ,A > +where +T : ?Sized , +A :Allocator , +{ +/// Constructs a `Box<T, A>` from a raw pointer. +/// +/// # Safety +/// +/// `raw` must point to valid memory, previously allocated with `A`, and at least the size of +/// type `T`. Quoted 7 lines
With this requirement and the invariant on
Box, I am lead to believe that you can't use this for ZSTs, since they are not allocated withA. One solution would be to adjust this requirement. But I would rather use a different solution: we move the dangling pointer stuff into the allocator and also call it whenTis a ZST (ie don't special case them inBoxbut in the impls ofAllocator). That way this can stay as-is and the part about ZSTs in the invariant can be removed.
Quoted 3 lines
Actually, we already got that. Every zero sized allocation will return a dangling pointer. However, we can't call
Allocator::freewith (any) dangling pointer though.
The last part is rather problematic in my opinion, since the safety requirements of the functions in Allocator don't ensure that you're not allowed to do it. We should make it possible to free dangling pointers that were previously "allocated" by the allocator (ie returned by realloc).
Maybe we do need an old_layout parameter for that (that way we can also debug_assert_eq!(old_layout.align(), new_layout.align())).
Quoted 35 lines
Quoted 25 lines
Quoted 14 lines
rust/kernel/alloc/kbox.rs new file +14
-0+ { +Ok (Self ::new (x, flags)?.into ()) + } + +/// Drops the contents, but keeps the allocation. +/// +/// # Examples +/// +/// ``` +/// let value = KBox::new([0; 32], GFP_KERNEL)?; +/// assert_eq!(*value, [0; 32]); +/// let value = KBox::drop_contents(value); +/// // Now we can re-use `value`: +/// let value = KBox::write(value, [1; 32]); rust/kernel/alloc/kbox.rs line 249
Quoted 10 lines
rust/kernel/alloc/kbox.rs new file +10
-0+/// assert_eq!(*value, [1; 32]); +/// # Ok::<(), Error>(()) +/// ``` +pub fn drop_contents (this:Self ) ->Box <MaybeUninit <T >,A > { +let ptr =Box ::into_raw (this); +// SAFETY: `ptr` is valid, because it came from `Box::into_raw`. +unsafe { core::ptr::drop_in_place (ptr) }; +// SAFETY: `ptr` is valid, because it came from `Box::into_raw`. +unsafe {Box ::from_raw (ptr.cast ()) } + }Quoted 4 lines
I don't particularly care in this instance, but you just took my patch and folded it into your own without asking me or specifying it in the commit message. In general I would have assumed that you just put the entire patch into the series (with correct From:... etc).
When I asked about this in [1] my understanding was that we expect [1] to land prior to this series. So, I'm just anticipating a future rebase where I move this code from
box_ext.rstokbox.rs, just like Alice suggested for her "ForeignOwnable for Pin<crate::alloc::Box<T, A>>" implementation.
Quoted 4 lines
I also understand your later reply, where you said: "[...] then you can just include it when you remove the
BoxExittrait." as confirmation.Probably that's a misunderstanding though. Sorry if that's the case.
Yeah what I meant by that was you base it on top and then move it from the BoxExt trait over to Box in a correctly attributed patch. As I said above, I don't really mind in this case, since it's trivial, so no worries. Just a heads-up for occasions where it is non-trivial.
Quoted 46 lines
[1] https://lore.kernel.org/lkml/[email protected]/
Quoted 14 lines
Quoted 14 lines
rust/kernel/alloc/kbox.rs new file +13
-0+} + +impl <T ,A >From <Box <T ,A >>for Pin <Box <T ,A >> +where +T : ?Sized , +A :Allocator , +A : 'static, +{ +/// Converts a `Box<T>` into a `Pin<Box<T>>`. If `T` does not implement [`Unpin`], then +/// `*boxed` will be pinned in memory and unable to be moved. +/// +/// This conversion does not allocate on the heap and happens in place. +/// Quoted 8 lines
rust/kernel/alloc/kbox.rs line 268
Quoted 6 lines
rust/kernel/alloc/kbox.rs new file +6
-0+/// This is also available via [`Box::into_pin`]. +/// +/// Constructing and pinning a `Box` with <code><Pin<Box\<T>>>::from([Box::new]\(x))</code> +/// can also be written more concisely using <code>[Box::pin]\(x)</code>. +/// This `From` implementation is useful if you already have a `Box<T>`, or you are +/// constructing a (pinned) `Box` in a different way than with [`Box::new`]. This also looks very much like something from the stdlib...
Yeah, I'll replace that.
Quoted 10 lines
Quoted 10 lines
rust/kernel/alloc/kbox.rs new file +9
-0+fn from (b:Box <T ,A >) ->Self { +Box ::into_pin (b) + } +} + +impl <T ,A >Deref for Box <T ,A > +where +T : ?Sized , +A :Allocator ,Quoted 8 lines
rust/kernel/alloc/kbox.rs line 283
Quoted 6 lines
rust/kernel/alloc/kbox.rs new file +6
-0+{ +type Target =T ; + +fn deref (&self) -> &T { +// SAFETY: `self.0` is always properly aligned, dereferenceable and points to an initialized +// instance of `T`. If
Tis a ZST, then it is not dereferenceable.
Quoted 2 lines
Why not? If
Tis a ZSTself.0isUnique::<T>::dangling(). So, in the end this is the same asNonNull::<T>::dangling().as_ref().
You are right, I just looked at [1] again and they define dereferenceable as "the memory range of the given size starting at the pointer must all be within the bounds of a single allocated object", for a zero-sized allocation, this holds vacuously.
[1]: https://doc.rust-lang.org/core/ptr/index.html#safety
Quoted 40 lines
Quoted 31 lines
Quoted 31 lines
rust/kernel/alloc/kbox.rs new file +31
-0+unsafe { self.0 .as_ref () } + } +} + +impl <T ,A >DerefMut for Box <T ,A > +where +T : ?Sized , +A :Allocator , +{ +fn deref_mut (&mut self) -> &mut T { +// SAFETY: `self.0` is always properly aligned, dereferenceable and points to an initialized +// instance of `T`. +unsafe { self.0 .as_mut () } + } +} + +impl <T ,A > fmt::Debug for Box <T ,A > +where +T : ?Sized + fmt::Debug , +A :Allocator , +{ +fn fmt (&self, f: &mut fmt::Formatter <'_>) -> fmt::Result { + fmt::Debug ::fmt (&**self, f) + } +} + +impl <T ,A >Drop for Box <T ,A > +where +T : ?Sized , +A :Allocator , +{Quoted 8 lines
rust/kernel/alloc/kbox.rs line 320
Quoted 6 lines
rust/kernel/alloc/kbox.rs new file +6
-0+fn drop (&mut self) { +let ptr = self.0 .as_ptr (); + +// SAFETY: `ptr` is always properly aligned, dereferenceable and points to an initialized +// instance of `T`. +let size =unsafe { core::mem::size_of_val (&*ptr) };1.
size_of_valis notunsafe.
Quoted 8 lines
Right, but dereferencing the
ptris unsafe.Quoted 2 lines
2. why not use
&*selfinstead of using the raw pointer? (then move the let binding below this line)If we ever support non-ZST
Allocators usingselfwould not always evaluate to the correct size. I think evaluating the size ofTrather thanBox<T>is the correct thing to do.
I mean use Box::deref (that's what &*self should do), you don't need to repeat the same SAFETY comment when it already is wrapped by a safe function.
---
Cheers,
Benno
Alice Ryhl
maintainerRe: [PATCH v4 09/28] rust: alloc: implement kernel `Box`
Quoted 113 lines · Benno Lossin
Quoted 81 lines · Danilo Krummrich
Quoted 71 lines · Benno Lossin
Quoted 70 lines · Danilo Krummrich
Boxprovides the simplest way to allocate memory for a generic type with one of the kernel's allocators, e.g.Kmalloc,VmallocorKVmalloc.In contrast to Rust's
Boxtype, the kernelBoxtype considers the kernel's GFP flags for all appropriate functions, always reports allocation failures throughResult<_, AllocError>and remains independent from unstable features.Signed-off-by Danilo Krummrich <[email protected]>--- rust/kernel/alloc.rs | 6 + rust/kernel/alloc/kbox.rs | 330 ++++++++++++++++++++++++++++++++++++++ rust/kernel/init.rs | 35 +++- rust/kernel/prelude.rs | 2 +- rust/kernel/types.rs | 56 +++++++ 5 files changed, 427 insertions(+), 2 deletions(-) create mode 100644 rust/kernel/alloc/kbox.rsrust/kernel/alloc.rs +6
-0@@ -5,6 +5,7 @@#[cfg(not(any(test, testlib)))] pub mod allocator;pub mod box_ext; +pub mod kbox;pub mod vec_ext;#[cfg(any(test, testlib))] @@ -13,6 +14,11 @@#[cfg(any(test, testlib))] pub use self::allocator_testas allocator; +pub use self::kbox::Box ; +pub use self::kbox::KBox ; +pub use self::kbox::KVBox ; +pub use self::kbox::VBox ; +/// Indicates an allocation error. #[derive( Copy , Clone , PartialEq , Eq , Debug )] pub struct AllocError ;rust/kernel/alloc/kbox.rs new file +21
-0@@ -0,0 +1,330 @@ +// SPDX-License-Identifier: GPL-2.0 + +//! Implementation of [`Box`]. + +use super ::{AllocError ,Allocator ,Flags }; +use core::fmt; +use core::marker::PhantomData ; +use core::mem::ManuallyDrop ; +use core::mem::MaybeUninit ; +use core::ops::{Deref ,DerefMut }; +use core::pin::Pin ; +use core::result::Result ; + +use crate ::types::Unique ; + +/// The kernel's [`Box`] type. +/// +/// `Box` provides the simplest way to allocate memory for a generic type with one of the kernel's +/// allocators, e.g. `Kmalloc`, `Vmalloc` or `KVmalloc`. +/// +/// For non-zero-sized values, a [`Box`] will use the given allocator `A` for its allocation. For Quoted 8 lines
rust/kernel/alloc/kbox.rs line 27
Quoted 6 lines
rust/kernel/alloc/kbox.rs new file +6
-0+/// the most common allocators the type aliases `KBox`, `VBox` and `KVBox` exist. +/// +/// It is valid to convert both ways between a [`Box`] and a raw pointer allocated with any +/// `Allocator`, given that the `Layout` used with the allocator is correct for the type. +/// +/// For zero-sized values the [`Box`]' pointer must be `dangling_mut::<T>`; no memory is allocated. Why do we need this to be in the docs?
Quoted 2 lines
Probably not - do you suggest to remove it entirely? Otherwise, where do you think we should move it?
I would remove it, since it's just implementation detail and allocator-dependent.
Quoted 20 lines
Quoted 12 lines
rust/kernel/alloc/kbox.rs line 103
Quoted 11 lines
rust/kernel/alloc/kbox.rs new file +11
-0+impl <T ,A >Box <T ,A > +where +T : ?Sized , +A :Allocator , +{ +/// Constructs a `Box<T, A>` from a raw pointer. +/// +/// # Safety +/// +/// `raw` must point to valid memory, previously allocated with `A`, and at least the size of +/// type `T`. Quoted 7 lines
With this requirement and the invariant on
Box, I am lead to believe that you can't use this for ZSTs, since they are not allocated withA. One solution would be to adjust this requirement. But I would rather use a different solution: we move the dangling pointer stuff into the allocator and also call it whenTis a ZST (ie don't special case them inBoxbut in the impls ofAllocator). That way this can stay as-is and the part about ZSTs in the invariant can be removed.Quoted 3 lines
Actually, we already got that. Every zero sized allocation will return a dangling pointer. However, we can't call
Allocator::freewith (any) dangling pointer though.
Quoted 7 lines
The last part is rather problematic in my opinion, since the safety requirements of the functions in
Allocatordon't ensure that you're not allowed to do it. We should make it possible to free dangling pointers that were previously "allocated" by the allocator (ie returned byrealloc).
Maybe we do need anold_layoutparameter for that (that way we can alsodebug_assert_eq!(old_layout.align(), new_layout.align())).
The std allocators generally prohibit zero sized allocations, so it seems sensible for us to do the same?
Alice
Re: [PATCH v4 09/28] rust: alloc: implement kernel `Box`
Quoted 36 lines · Alice Ryhl
Quoted 27 lines · Benno Lossin
Quoted 22 lines · Danilo Krummrich
Quoted 13 lines · Benno Lossin
rust/kernel/alloc/kbox.rs line 103
Quoted 11 lines · Danilo Krummrich
rust/kernel/alloc/kbox.rs new file +11
-0+impl <T ,A >Box <T ,A > +where +T : ?Sized , +A :Allocator , +{ +/// Constructs a `Box<T, A>` from a raw pointer. +/// +/// # Safety +/// +/// `raw` must point to valid memory, previously allocated with `A`, and at least the size of +/// type `T`. Quoted 7 lines
With this requirement and the invariant on
Box, I am lead to believe that you can't use this for ZSTs, since they are not allocated withA. One solution would be to adjust this requirement. But I would rather use a different solution: we move the dangling pointer stuff into the allocator and also call it whenTis a ZST (ie don't special case them inBoxbut in the impls ofAllocator). That way this can stay as-is and the part about ZSTs in the invariant can be removed.Quoted 3 lines
Actually, we already got that. Every zero sized allocation will return a dangling pointer. However, we can't call
Allocator::freewith (any) dangling pointer though.Quoted 7 lines
The last part is rather problematic in my opinion, since the safety requirements of the functions in
Allocatordon't ensure that you're not allowed to do it. We should make it possible to free dangling pointers that were previously "allocated" by the allocator (ie returned byrealloc).
Maybe we do need anold_layoutparameter for that (that way we can alsodebug_assert_eq!(old_layout.align(), new_layout.align())).
Quoted 2 lines
The std allocators generally prohibit zero sized allocations, so it seems sensible for us to do the same?
I never understood why they do that, the stdlib Allocator trait has all the information it needs to detect zero-sized allocations, so it could just return dangling pointers. I don't see the point of duplicating the zero-sized logic in Box and Vec...
---
Cheers,
Benno
Danilo Krummrich
maintainerRe: [PATCH v4 09/28] rust: alloc: implement kernel `Box`
Quoted 37 lines · Benno Lossin
Quoted 36 lines · Alice Ryhl
Quoted 34 lines · Benno Lossin
Quoted 22 lines · Danilo Krummrich
Quoted 13 lines · Benno Lossin
rust/kernel/alloc/kbox.rs line 103
Quoted 11 lines · Danilo Krummrich
rust/kernel/alloc/kbox.rs new file +11
-0+impl <T ,A >Box <T ,A > +where +T : ?Sized , +A :Allocator , +{ +/// Constructs a `Box<T, A>` from a raw pointer. +/// +/// # Safety +/// +/// `raw` must point to valid memory, previously allocated with `A`, and at least the size of +/// type `T`. Quoted 7 lines
With this requirement and the invariant on
Box, I am lead to believe that you can't use this for ZSTs, since they are not allocated withA. One solution would be to adjust this requirement. But I would rather use a different solution: we move the dangling pointer stuff into the allocator and also call it whenTis a ZST (ie don't special case them inBoxbut in the impls ofAllocator). That way this can stay as-is and the part about ZSTs in the invariant can be removed.Quoted 3 lines
Actually, we already got that. Every zero sized allocation will return a dangling pointer. However, we can't call
Allocator::freewith (any) dangling pointer though.The last part is rather problematic in my opinion, since the safety requirements of the functions in
Allocatordon't ensure that you're not allowed to do it. We should make it possible to free dangling pointers that were previously "allocated" by the allocator (ie returned byrealloc).
Maybe we do need anold_layoutparameter for that (that way we can alsodebug_assert_eq!(old_layout.align(), new_layout.align())).
Quoted 7 lines
Quoted 2 lines
The std allocators generally prohibit zero sized allocations, so it seems sensible for us to do the same?
I never understood why they do that, the stdlib
Allocatortrait has all the information it needs to detect zero-sized allocations, so it could just return dangling pointers. I don't see the point of duplicating the zero-sized logic inBoxandVec...
I think it's simpler and less error prone.
Besides that, I think the stdlib Box allows to call from_raw with any random pointer for ZST, which the allocator API can't catch.
Quoted 5 lines
---
Cheers,
Benno
Danilo Krummrich
maintainerRe: [PATCH v4 09/28] rust: alloc: implement kernel `Box`
In reply to Benno LossinQuoted 8 lines · Benno Lossin
Quoted 8 lines
Quoted 7 lines
With this requirement and the invariant on
Box, I am lead to believe that you can't use this for ZSTs, since they are not allocated withA. One solution would be to adjust this requirement. But I would rather use a different solution: we move the dangling pointer stuff into the allocator and also call it whenTis a ZST (ie don't special case them inBoxbut in the impls ofAllocator). That way this can stay as-is and the part about ZSTs in the invariant can be removed.
Quoted 7 lines
Quoted 3 lines
Actually, we already got that. Every zero sized allocation will return a dangling pointer. However, we can't call
Allocator::freewith (any) dangling pointer though.The last part is rather problematic in my opinion, since the safety requirements of the functions in
Allocatordon't ensure that you're not allowed to do it.
Yes, I think it needs to be added.
Quoted 5 lines
We should make it possible to free dangling
pointers that were previously "allocated" by the allocator (ie returned byrealloc).
Maybe we do need anold_layoutparameter for that (that way we can alsodebug_assert_eq!(old_layout.align(), new_layout.align())).
Please see my reply in [1] - let's continue the discussion there.
[1] https://lore.kernel.org/rust-for-linux/ZrNIaAcGkGU0d8I3@pollux/
Quoted 36 lines
Quoted 36 lines
Quoted 26 lines
Quoted 15 lines
rust/kernel/alloc/kbox.rs new file +14
-0+ { +Ok (Self ::new (x, flags)?.into ()) + } + +/// Drops the contents, but keeps the allocation. +/// +/// # Examples +/// +/// ``` +/// let value = KBox::new([0; 32], GFP_KERNEL)?; +/// assert_eq!(*value, [0; 32]); +/// let value = KBox::drop_contents(value); +/// // Now we can re-use `value`: +/// let value = KBox::write(value, [1; 32]); rust/kernel/alloc/kbox.rs line 249
Quoted 10 lines
rust/kernel/alloc/kbox.rs new file +10
-0+/// assert_eq!(*value, [1; 32]); +/// # Ok::<(), Error>(()) +/// ``` +pub fn drop_contents (this:Self ) ->Box <MaybeUninit <T >,A > { +let ptr =Box ::into_raw (this); +// SAFETY: `ptr` is valid, because it came from `Box::into_raw`. +unsafe { core::ptr::drop_in_place (ptr) }; +// SAFETY: `ptr` is valid, because it came from `Box::into_raw`. +unsafe {Box ::from_raw (ptr.cast ()) } + }Quoted 4 lines
I don't particularly care in this instance, but you just took my patch and folded it into your own without asking me or specifying it in the commit message. In general I would have assumed that you just put the entire patch into the series (with correct From:... etc).
When I asked about this in [1] my understanding was that we expect [1] to land prior to this series. So, I'm just anticipating a future rebase where I move this code from
box_ext.rstokbox.rs, just like Alice suggested for her "ForeignOwnable for Pin<crate::alloc::Box<T, A>>" implementation.
Quoted 7 lines
Quoted 4 lines
I also understand your later reply, where you said: "[...] then you can just include it when you remove the
BoxExittrait." as confirmation.Probably that's a misunderstanding though. Sorry if that's the case.
Yeah what I meant by that was you base it on top and then move it from the
BoxExttrait over toBoxin a correctly attributed patch.
I don't understand this. What do you mean with "correctly attributed patch" in this case?
There are various existing implementations around Box and BoxExt, are you saying that I should create separate patches for moving / adapting all of them, e.g. this patch adapts parts from BoxExt, the implementation of ForeignOwnable for Box<T>, the implementation of InPlaceInit<T> for Box<T>.
I don't think this is necessary.
I probably shouldn't anticipate a future rebase though, it just leads to confusion. I'll drop it for now and re-add it once your patch lands in rust-next.
Quoted 105 lines
As I
said above, I don't really mind in this case, since it's trivial, so no worries. Just a heads-up for occasions where it is non-trivial.Quoted 46 lines
[1] https://lore.kernel.org/lkml/[email protected]/
Quoted 14 lines
Quoted 14 lines
rust/kernel/alloc/kbox.rs new file +13
-0+} + +impl <T ,A >From <Box <T ,A >>for Pin <Box <T ,A >> +where +T : ?Sized , +A :Allocator , +A : 'static, +{ +/// Converts a `Box<T>` into a `Pin<Box<T>>`. If `T` does not implement [`Unpin`], then +/// `*boxed` will be pinned in memory and unable to be moved. +/// +/// This conversion does not allocate on the heap and happens in place. +/// Quoted 8 lines
rust/kernel/alloc/kbox.rs line 268
Quoted 6 lines
rust/kernel/alloc/kbox.rs new file +6
-0+/// This is also available via [`Box::into_pin`]. +/// +/// Constructing and pinning a `Box` with <code><Pin<Box\<T>>>::from([Box::new]\(x))</code> +/// can also be written more concisely using <code>[Box::pin]\(x)</code>. +/// This `From` implementation is useful if you already have a `Box<T>`, or you are +/// constructing a (pinned) `Box` in a different way than with [`Box::new`]. This also looks very much like something from the stdlib...
Yeah, I'll replace that.
Quoted 10 lines
Quoted 10 lines
rust/kernel/alloc/kbox.rs new file +9
-0+fn from (b:Box <T ,A >) ->Self { +Box ::into_pin (b) + } +} + +impl <T ,A >Deref for Box <T ,A > +where +T : ?Sized , +A :Allocator ,Quoted 8 lines
rust/kernel/alloc/kbox.rs line 283
Quoted 6 lines
rust/kernel/alloc/kbox.rs new file +6
-0+{ +type Target =T ; + +fn deref (&self) -> &T { +// SAFETY: `self.0` is always properly aligned, dereferenceable and points to an initialized +// instance of `T`. If
Tis a ZST, then it is not dereferenceable.Quoted 2 lines
Why not? If
Tis a ZSTself.0isUnique::<T>::dangling(). So, in the end this is the same asNonNull::<T>::dangling().as_ref().You are right, I just looked at [1] again and they define dereferenceable as "the memory range of the given size starting at the pointer must all be within the bounds of a single allocated object", for a zero-sized allocation, this holds vacuously.
[1]: https://doc.rust-lang.org/core/ptr/index.html#safety
Quoted 45 lines
Quoted 31 lines
Quoted 31 lines
rust/kernel/alloc/kbox.rs new file +31
-0+unsafe { self.0 .as_ref () } + } +} + +impl <T ,A >DerefMut for Box <T ,A > +where +T : ?Sized , +A :Allocator , +{ +fn deref_mut (&mut self) -> &mut T { +// SAFETY: `self.0` is always properly aligned, dereferenceable and points to an initialized +// instance of `T`. +unsafe { self.0 .as_mut () } + } +} + +impl <T ,A > fmt::Debug for Box <T ,A > +where +T : ?Sized + fmt::Debug , +A :Allocator , +{ +fn fmt (&self, f: &mut fmt::Formatter <'_>) -> fmt::Result { + fmt::Debug ::fmt (&**self, f) + } +} + +impl <T ,A >Drop for Box <T ,A > +where +T : ?Sized , +A :Allocator , +{Quoted 8 lines
rust/kernel/alloc/kbox.rs line 320
Quoted 6 lines
rust/kernel/alloc/kbox.rs new file +6
-0+fn drop (&mut self) { +let ptr = self.0 .as_ptr (); + +// SAFETY: `ptr` is always properly aligned, dereferenceable and points to an initialized +// instance of `T`. +let size =unsafe { core::mem::size_of_val (&*ptr) };1.
size_of_valis notunsafe.Right, but dereferencing the
ptris unsafe.Quoted 2 lines
2. why not use
&*selfinstead of using the raw pointer? (then move the let binding below this line)
Quoted 7 lines
Quoted 3 lines
If we ever support non-ZST
Allocators usingselfwould not always evaluate to the correct size. I think evaluating the size ofTrather thanBox<T>is the correct thing to do.I mean use
Box::deref(that's what&*selfshould do), you don't need to repeat the same SAFETY comment when it already is wrapped by a safe function.
Oh, yes, that's indeed a good suggestion.
Quoted 5 lines
---
Cheers,
Benno
Re: [PATCH v4 09/28] rust: alloc: implement kernel `Box`
Quoted 39 lines · Danilo Krummrich
Quoted 38 lines · Benno Lossin
Quoted 38 lines
Quoted 25 lines
Quoted 14 lines
rust/kernel/alloc/kbox.rs new file +14
-0+ { +Ok (Self ::new (x, flags)?.into ()) + } + +/// Drops the contents, but keeps the allocation. +/// +/// # Examples +/// +/// ``` +/// let value = KBox::new([0; 32], GFP_KERNEL)?; +/// assert_eq!(*value, [0; 32]); +/// let value = KBox::drop_contents(value); +/// // Now we can re-use `value`: +/// let value = KBox::write(value, [1; 32]); rust/kernel/alloc/kbox.rs line 249
Quoted 10 lines
rust/kernel/alloc/kbox.rs new file +10
-0+/// assert_eq!(*value, [1; 32]); +/// # Ok::<(), Error>(()) +/// ``` +pub fn drop_contents (this:Self ) ->Box <MaybeUninit <T >,A > { +let ptr =Box ::into_raw (this); +// SAFETY: `ptr` is valid, because it came from `Box::into_raw`. +unsafe { core::ptr::drop_in_place (ptr) }; +// SAFETY: `ptr` is valid, because it came from `Box::into_raw`. +unsafe {Box ::from_raw (ptr.cast ()) } + }Quoted 4 lines
I don't particularly care in this instance, but you just took my patch and folded it into your own without asking me or specifying it in the commit message. In general I would have assumed that you just put the entire patch into the series (with correct From:... etc).
When I asked about this in [1] my understanding was that we expect [1] to land prior to this series. So, I'm just anticipating a future rebase where I move this code from
box_ext.rstokbox.rs, just like Alice suggested for her "ForeignOwnable for Pin<crate::alloc::Box<T, A>>" implementation.I also understand your later reply, where you said: "[...] then you can just include it when you remove the
BoxExittrait." as confirmation.
Quoted 7 lines
Quoted 4 lines
Quoted 1 line
Probably that's a misunderstanding though. Sorry if that's the case.
Yeah what I meant by that was you base it on top and then move it from the
BoxExttrait over toBoxin a correctly attributed patch.I don't understand this. What do you mean with "correctly attributed patch" in this case?
So, seems like I mixed the two approaches I thought of. Here are they separated:
1. base the series on top of my patches and then copy the implementation from the in-tree file this patch.
2. create a new patch that adds drop_contents to your Box (ie after this patch) that has Signed-off-by: you and Co-Developed-by: me. With this approach I would also ask privately (including the patch) if that would be ok.
---
Cheers,
Benno
Quoted 16 lines
There are various existing implementations around
BoxandBoxExt, are you saying that I should create separate patches for moving / adapting all of them, e.g. this patch adapts parts fromBoxExt, the implementation ofForeignOwnableforBox<T>, the implementation ofInPlaceInit<T>forBox<T>.I don't think this is necessary.
I probably shouldn't anticipate a future rebase though, it just leads to confusion. I'll drop it for now and re-add it once your patch lands in rust-next.
Quoted 5 lines
As I
said above, I don't really mind in this case, since it's trivial, so no worries. Just a heads-up for occasions where it is non-trivial.Quoted 1 line
Danilo Krummrich
maintainerRe: [PATCH v4 09/28] rust: alloc: implement kernel `Box`
In reply to Benno LossinQuoted 16 lines · Benno Lossin
Quoted 16 lines
Quoted 5 lines
Quoted 5 lines
rust/kernel/alloc/kbox.rs new file +5
-0+impl <T ,A >Drop for Box <T ,A > +where +T : ?Sized , +A :Allocator , +{Quoted 8 lines
rust/kernel/alloc/kbox.rs line 320
Quoted 6 lines
rust/kernel/alloc/kbox.rs new file +6
-0+fn drop (&mut self) { +let ptr = self.0 .as_ptr (); + +// SAFETY: `ptr` is always properly aligned, dereferenceable and points to an initialized +// instance of `T`. +let size =unsafe { core::mem::size_of_val (&*ptr) };1.
size_of_valis notunsafe.Right, but dereferencing the
ptris unsafe.
Quoted 8 lines
Quoted 6 lines
Quoted 2 lines
2. why not use
&*selfinstead of using the raw pointer? (then move the let binding below this line)If we ever support non-ZST
Allocators usingselfwould not always evaluate to the correct size. I think evaluating the size ofTrather thanBox<T>is the correct thing to do.I mean use
Box::deref(that's what&*selfshould do), you don't need
Actually, this must either be size_of_val(&**self) or `size_of_val::<T>(self).
size_of_val(&*self) should indeed resolve to &Box<T, A>, right?
Quoted 7 lines
to repeat the same SAFETY comment when it already is wrapped by a safe function.
---
Cheers,
Benno
Re: [PATCH v4 09/28] rust: alloc: implement kernel `Box`
Quoted 24 lines · Danilo Krummrich
Quoted 23 lines · Benno Lossin
Quoted 22 lines
Quoted 5 lines
Quoted 5 lines
rust/kernel/alloc/kbox.rs new file +5
-0+impl <T ,A >Drop for Box <T ,A > +where +T : ?Sized , +A :Allocator , +{Quoted 8 lines
rust/kernel/alloc/kbox.rs line 320
Quoted 6 lines
rust/kernel/alloc/kbox.rs new file +6
-0+fn drop (&mut self) { +let ptr = self.0 .as_ptr (); + +// SAFETY: `ptr` is always properly aligned, dereferenceable and points to an initialized +// instance of `T`. +let size =unsafe { core::mem::size_of_val (&*ptr) };1.
size_of_valis notunsafe.Right, but dereferencing the
ptris unsafe.Quoted 2 lines
2. why not use
&*selfinstead of using the raw pointer? (then move the let binding below this line)If we ever support non-ZST
Allocators usingselfwould not always evaluate to the correct size. I think evaluating the size ofTrather thanBox<T>is the correct thing to do.
Quoted 5 lines
Quoted 1 line
I mean use
Box::deref(that's what&*selfshould do), you don't needActually, this must either be
size_of_val(&**self)or `size_of_val::<T>(self).
size_of_val(&*self)should indeed resolve to&Box<T, A>, right?
Oh yeah that is true, good catch! Here is a playground example [1].
---
Cheers,
Benno
Quoted 8 lines
Quoted 8 lines
to repeat the same SAFETY comment when it already is wrapped by a safe function.
---
Cheers,
Benno
Danilo Krummrich
maintainerPATCH v4 10/28 rust: treewide: switch to our kernel `Box` type
In reply to Danilo KrummrichNow that we got the kernel Box type in place, convert all existing Box users to make use of it.
--- rust/kernel/init.rs | 41 ++++++++++++++++--------------- rust/kernel/init/__internal.rs | 2 +- rust/kernel/sync/arc.rs | 17 ++++++------- rust/kernel/sync/condvar.rs | 4 +-- rust/kernel/sync/lock/mutex.rs | 2 +- rust/kernel/sync/lock/spinlock.rs | 2 +- rust/kernel/workqueue.rs | 20 +++++++-------- 7 files changed, 44 insertions(+), 44 deletions(-)
rust/kernel/init.rs +21 -20
@@ -68,7 +68,7 @@//! # a <- new_mutex!(42, "Foo::a"), //! # b: 24, //! # }); -//! let foo: Result<Pin<Box<Foo>>> = Box::pin_init(foo, GFP_KERNEL); +//! let foo: Result<Pin<KBox<Foo>>> = KBox::pin_init(foo, GFP_KERNEL); //! ``` //! //! For more information see the [`pin_init!`] macro.
@@ -93,14 +93,14 @@//! struct DriverData { //! #[pin] //! status: Mutex<i32>, -//! buffer: Box<[u8; 1_000_000]>, +//! buffer: KBox<[u8; 1_000_000]>, //! } //! //! impl DriverData { //! fn new() -> impl PinInit<Self, Error> { //! try_pin_init!(Self { //! status <- new_mutex!(0, "DriverData::status"), -//! buffer: Box::init(kernel::init::zeroed(), GFP_KERNEL)?, +//! buffer: KBox::init(kernel::init::zeroed(), GFP_KERNEL)?, //! }) //! } //! }
@@ -211,7 +211,7 @@//! [`pin_init!`]: crate::pin_init! use crate ::{ - alloc::{box_ext::BoxExt ,AllocError ,Allocator ,Flags }, + alloc::{box_ext::BoxExt ,AllocError ,Allocator ,Flags ,KBox }, error::{self,Error }, sync::UniqueArc , types::{Opaque ,ScopeGuard },
@@ -297,7 +297,7 @@ macro_rules! stack_pin_init {/// struct Foo { /// #[pin] /// a: Mutex<usize>, -/// b: Box<Bar>, +/// b: KBox<Bar>, /// } /// /// struct Bar {
@@ -306,7 +306,7 @@ macro_rules! stack_pin_init {/// /// stack_try_pin_init!(let foo: Result<Pin<&mut Foo>, AllocError> = pin_init!(Foo { /// a <- new_mutex!(42), -/// b: Box::new(Bar { +/// b: KBox::new(Bar { /// x: 64, /// }, GFP_KERNEL)?, /// }));
@@ -323,7 +323,7 @@ macro_rules! stack_pin_init {/// struct Foo { /// #[pin] /// a: Mutex<usize>, -/// b: Box<Bar>, +/// b: KBox<Bar>, /// } /// /// struct Bar {
@@ -332,7 +332,7 @@ macro_rules! stack_pin_init {/// /// stack_try_pin_init!(let foo: Pin<&mut Foo> =? pin_init!(Foo { /// a <- new_mutex!(42), -/// b: Box::new(Bar { +/// b: KBox::new(Bar { /// x: 64, /// }, GFP_KERNEL)?, /// }));
@@ -391,7 +391,7 @@ macro_rules! stack_try_pin_init {/// }, /// }); /// # initializer } -/// # Box::pin_init(demo(), GFP_KERNEL).unwrap(); +/// # KBox::pin_init(demo(), GFP_KERNEL).unwrap(); /// ``` /// /// Arbitrary Rust expressions can be used to set the value of a variable.
@@ -461,7 +461,7 @@ macro_rules! stack_try_pin_init {/// # }) /// # } /// # } -/// let foo = Box::pin_init(Foo::new(), GFP_KERNEL); +/// let foo = KBox::pin_init(Foo::new(), GFP_KERNEL); /// ``` /// /// They can also easily embed it into their own `struct`s:
@@ -593,7 +593,7 @@ macro_rules! pin_init {/// use kernel::{init::{self, PinInit}, error::Error}; /// #[pin_data] /// struct BigBuf { -/// big: Box<[u8; 1024 * 1024 * 1024]>, +/// big: KBox<[u8; 1024 * 1024 * 1024]>, /// small: [u8; 1024 * 1024], /// ptr: *mut u8, /// }
@@ -601,7 +601,7 @@ macro_rules! pin_init {/// impl BigBuf { /// fn new() -> impl PinInit<Self, Error> { /// try_pin_init!(Self { -/// big: Box::init(init::zeroed(), GFP_KERNEL)?, +/// big: KBox::init(init::zeroed(), GFP_KERNEL)?, /// small: [0; 1024 * 1024], /// ptr: core::ptr::null_mut(), /// }? Error)
@@ -693,16 +693,16 @@ macro_rules! init {/// # Examples /// /// ```rust -/// use kernel::{init::{PinInit, zeroed}, error::Error}; +/// use kernel::{alloc::KBox, init::{PinInit, zeroed}, error::Error}; /// struct BigBuf { -/// big: Box<[u8; 1024 * 1024 * 1024]>, +/// big: KBox<[u8; 1024 * 1024 * 1024]>, /// small: [u8; 1024 * 1024], /// } /// /// impl BigBuf { /// fn new() -> impl Init<Self, Error> { /// try_init!(Self { -/// big: Box::init(zeroed(), GFP_KERNEL)?, +/// big: KBox::init(zeroed(), GFP_KERNEL)?, /// small: [0; 1024 * 1024], /// }? Error) /// }
@@ -825,7 +825,7 @@ fn pin_chain<F>(self, f: F) -> ChainPinInit<Self, F, T, E> }/// An initializer returned by [`PinInit::pin_chain`]. -pub struct ChainPinInit <I ,F ,T : ?Sized ,E >(I ,F , __internal::Invariant <(E ,Box <T >)>); +pub struct ChainPinInit <I ,F ,T : ?Sized ,E >(I ,F , __internal::Invariant <(E ,KBox <T >)>);// SAFETY: The `__pinned_init` function is implemented such that it // - returns `Ok(())` on successful initialization,
@@ -924,7 +924,7 @@ fn chain<F>(self, f: F) -> ChainInit<Self, F, T, E> }/// An initializer returned by [`Init::chain`]. -pub struct ChainInit <I ,F ,T : ?Sized ,E >(I ,F , __internal::Invariant <(E ,Box <T >)>); +pub struct ChainInit <I ,F ,T : ?Sized ,E >(I ,F , __internal::Invariant <(E ,KBox <T >)>);// SAFETY: The `__init` function is implemented such that it // - returns `Ok(())` on successful initialization,
@@ -1008,8 +1008,9 @@ pub fn uninit<T, E>() -> impl Init<MaybeUninit<T>, E> {/// # Examples /// /// ```rust -/// use kernel::{error::Error, init::init_array_from_fn}; -/// let array: Box<[usize; 1_000]> = Box::init::<Error>(init_array_from_fn(|i| i), GFP_KERNEL).unwrap(); +/// use kernel::{alloc::KBox, error::Error, init::init_array_from_fn}; +/// let array: KBox<[usize; 1_000]> = +/// KBox::init::<Error>(init_array_from_fn(|i| i), GFP_KERNEL).unwrap(); /// assert_eq!(array.len(), 1_000); /// ``` pub fn init_array_from_fn<I, const N: usize, T, E>(
@@ -1349,7 +1350,7 @@ macro_rules! impl_zeroable { // // In this case we are allowed to use `T: ?Sized`, since all zeros is the `None` variant. {<T: ?Sized>} Option<NonNull<T>>, - {<T: ?Sized>} Option<Box<T>>, + {<T: ?Sized>} Option<KBox<T>>, // SAFETY: `null` pointer is valid. //
rust/kernel/init/__internal.rs +1 -1
@@ -102,7 +102,7 @@ fn make_closure<F, O, E>(self, f: F) -> F } } -pub struct AllData <T : ?Sized >(PhantomData <fn (Box <T >) ->Box <T >>); +pub struct AllData <T : ?Sized >(PhantomData <fn (KBox <T >) ->KBox <T >>);impl <T : ?Sized >Clone for AllData <T > {fn clone (&self) ->Self {
rust/kernel/sync/arc.rs +8 -9
@@ -16,13 +16,12 @@//! [`Arc`]: https://doc.rust-lang.org/std/sync/struct.Arc.html use crate ::{ - alloc::{box_ext::BoxExt ,AllocError ,Flags }, + alloc::{AllocError ,Flags ,KBox }, error::{self,Error }, init::{self,InPlaceInit ,Init ,PinInit }, try_init, types::{ForeignOwnable ,Opaque }, }; -use alloc::boxed::Box ;use core::{ alloc::Layout , fmt,
@@ -203,11 +202,11 @@ pub fn new(contents: T, flags: Flags) -> Result<Self, AllocError> { data: contents, }; -let inner = <Box <_ >as BoxExt <_ >>::new (value, flags)?; +let inner =KBox ::new (value, flags)?;// SAFETY: We just created `inner` with a reference count of 1, which is owned by the new // `Arc` object. -Ok (unsafe {Self ::from_inner (Box ::leak (inner).into ()) }) +Ok (unsafe {Self ::from_inner (KBox ::leak (inner).into ()) }) }/// Use the given initializer to in-place initialize a `T`.
@@ -422,8 +421,8 @@ fn drop(&mut self) {if is_zero {// The count reached zero, we must free the memory. // -// SAFETY: The pointer was initialised from the result of `Box::leak`. -unsafe {drop (Box ::from_raw (self.ptr.as_ptr ())) }; +// SAFETY: The pointer was initialised from the result of `KBox::leak`. +unsafe {drop (KBox ::from_raw (self.ptr.as_ptr ())) }; } } }
@@ -668,7 +667,7 @@ pub fn new(value: T, flags: Flags) -> Result<Self, AllocError> {/// Tries to allocate a new [`UniqueArc`] instance whose contents are not initialised yet. pub fn new_uninit (flags:Flags ) ->Result <UniqueArc <MaybeUninit <T >>,AllocError > {// INVARIANT: The refcount is initialised to a non-zero value. - let inner = Box::try_init::<AllocError>( + let inner = KBox::try_init::<AllocError>( try_init!(ArcInner { // SAFETY: There are no safety requirements for this FFI call. refcount: Opaque::new(unsafe { bindings::REFCOUNT_INIT(1) }),
@@ -678,8 +677,8 @@ pub fn new_uninit(flags: Flags) -> Result<UniqueArc<MaybeUninit<T>>, AllocError> )?;Ok (UniqueArc {// INVARIANT: The newly-created object has a refcount of 1. -// SAFETY: The pointer from the `Box` is valid. - inner:unsafe {Arc ::from_inner (Box ::leak (inner).into ()) }, +// SAFETY: The pointer from the `KBox` is valid. + inner:unsafe {Arc ::from_inner (KBox ::leak (inner).into ()) }, }) } }
rust/kernel/sync/condvar.rs +2 -2
@@ -70,8 +70,8 @@ macro_rules! new_condvar {/// } /// /// /// Allocates a new boxed `Example`. -/// fn new_example() -> Result<Pin<Box<Example>>> { -/// Box::pin_init(pin_init!(Example { +/// fn new_example() -> Result<Pin<KBox<Example>>> { +/// KBox::pin_init(pin_init!(Example { /// value <- new_mutex!(0), /// value_changed <- new_condvar!(), /// }), GFP_KERNEL)
rust/kernel/sync/lock/mutex.rs +1 -1
@@ -58,7 +58,7 @@ macro_rules! new_mutex {/// } /// /// // Allocate a boxed `Example`. -/// let e = Box::pin_init(Example::new(), GFP_KERNEL)?; +/// let e = KBox::pin_init(Example::new(), GFP_KERNEL)?; /// assert_eq!(e.c, 10); /// assert_eq!(e.d.lock().a, 20); /// assert_eq!(e.d.lock().b, 30);
rust/kernel/sync/lock/spinlock.rs +1 -1
@@ -56,7 +56,7 @@ macro_rules! new_spinlock {/// } /// /// // Allocate a boxed `Example`. -/// let e = Box::pin_init(Example::new(), GFP_KERNEL)?; +/// let e = KBox::pin_init(Example::new(), GFP_KERNEL)?; /// assert_eq!(e.c, 10); /// assert_eq!(e.d.lock().a, 20); /// assert_eq!(e.d.lock().b, 30);
rust/kernel/workqueue.rs +10 -10
@@ -216,7 +216,7 @@ pub fn try_spawn<T: 'static + Send + FnOnce()>( func: Some(func), }); - self.enqueue (Box ::pin_init (init, flags).map_err (|_|AllocError )?); + self.enqueue (KBox ::pin_init (init, flags).map_err (|_|AllocError )?);Ok (()) } }
@@ -239,9 +239,9 @@ fn project(self: Pin<&mut Self>) -> &mut Option<T> { }impl <T :FnOnce ()>WorkItem for ClosureWork <T > { -type Pointer =Pin <Box <Self >>; +type Pointer =Pin <KBox <Self >>; -fn run (mut this:Pin <Box <Self >>) { +fn run (mut this:Pin <KBox <Self >>) {if let Some (func) = this.as_mut ().project ().take () { (func)() }
@@ -297,7 +297,7 @@ unsafe fn __enqueue<F>(self, queue_work_on: F) -> Self::EnqueueOutput/// Defines the method that should be called directly when a work item is executed. /// -/// This trait is implemented by `Pin<Box<T>>` and [`Arc<T>`], and is mainly intended to be +/// This trait is implemented by `Pin<KBox<T>>` and [`Arc<T>`], and is mainly intended to be /// implemented for smart pointer types. For your own structs, you would implement [`WorkItem`] /// instead. The [`run`] method on this trait will usually just perform the appropriate /// `container_of` translation and then call into the [`run`][WorkItem::run] method from the
@@ -329,7 +329,7 @@ pub unsafe trait WorkItemPointer<const ID: u64>: RawWorkItem<ID> {/// This trait is used when the `work_struct` field is defined using the [`Work`] helper. pub trait WorkItem <const ID :u64 =0 > {/// The pointer type that this struct is wrapped in. This will typically be `Arc<Self>` or -/// `Pin<Box<Self>>`. +/// `Pin<KBox<Self>>`. type Pointer :WorkItemPointer <ID >;/// The method that should be called when this work item is executed.
@@ -565,7 +565,7 @@ unsafe fn __enqueue<F>(self, queue_work_on: F) -> Self::EnqueueOutput } } -unsafe impl <T ,const ID :u64 >WorkItemPointer <ID >for Pin <Box <T >> +unsafe impl <T ,const ID :u64 >WorkItemPointer <ID >for Pin <KBox <T >>where T :WorkItem <ID ,Pointer =Self >,T :HasWork <T ,ID >,
@@ -576,7 +576,7 @@ unsafe impl<T, const ID: u64> WorkItemPointer<ID> for Pin<Box<T>>// SAFETY: This computes the pointer that `__enqueue` got from `Arc::into_raw`. let ptr =unsafe {T ::work_container_of (ptr) };// SAFETY: This pointer comes from `Arc::into_raw` and we've been given back ownership. -let boxed =unsafe {Box ::from_raw (ptr) }; +let boxed =unsafe {KBox ::from_raw (ptr) };// SAFETY: The box was already pinned when it was enqueued. let pinned =unsafe {Pin ::new_unchecked (boxed) };
@@ -584,7 +584,7 @@ unsafe impl<T, const ID: u64> WorkItemPointer<ID> for Pin<Box<T>> } } -unsafe impl <T ,const ID :u64 >RawWorkItem <ID >for Pin <Box <T >> +unsafe impl <T ,const ID :u64 >RawWorkItem <ID >for Pin <KBox <T >>where T :WorkItem <ID ,Pointer =Self >,T :HasWork <T ,ID >,
@@ -598,9 +598,9 @@ unsafe fn __enqueue<F>(self, queue_work_on: F) -> Self::EnqueueOutput// SAFETY: We're not going to move `self` or any of its fields, so its okay to temporarily // remove the `Pin` wrapper. let boxed =unsafe {Pin ::into_inner_unchecked (self) }; -let ptr =Box ::into_raw (boxed); +let ptr =KBox ::into_raw (boxed); -// SAFETY: Pointers into a `Box` point at a valid value. +// SAFETY: Pointers into a `KBox` point at a valid value. let work_ptr =unsafe {T ::raw_get_work (ptr) };// SAFETY: `raw_get_work` returns a pointer to a valid value. let work_ptr =unsafe {Work ::raw_get (work_ptr) };
Signature · 1 line
2.45.2
Re: [PATCH v4 10/28] rust: treewide: switch to our kernel `Box` type
Quoted 5 lines · Danilo Krummrich
Now that we got the kernel
Boxtype in place, convert all existingBoxusers to make use of it.Signed-off-by Danilo Krummrich <[email protected]>
Re: [PATCH v4 10/28] rust: treewide: switch to our kernel `Box` type
In reply to Danilo KrummrichQuoted 2 lines · Danilo Krummrich
Now that we got the kernel
Boxtype in place, convert all existingBoxusers to make use of it.
You missed a couple usages of Box:
- rust/macros/lib.rs:{242,251,281}
- drivers/block/rnull.rs:{35,50}
or is that intentional? (for me rnull doesn't compile after this patch)
---
Cheers,
Benno
Quoted 10 lines
Signed-off-by Danilo Krummrich <[email protected]>--- rust/kernel/init.rs | 41 ++++++++++++++++--------------- rust/kernel/init/__internal.rs | 2 +- rust/kernel/sync/arc.rs | 17 ++++++------- rust/kernel/sync/condvar.rs | 4 +-- rust/kernel/sync/lock/mutex.rs | 2 +- rust/kernel/sync/lock/spinlock.rs | 2 +- rust/kernel/workqueue.rs | 20 +++++++-------- 7 files changed, 44 insertions(+), 44 deletions(-)
Re: [PATCH v4 10/28] rust: treewide: switch to our kernel `Box` type
Quoted 7 lines · Benno Lossin
Quoted 2 lines · Danilo Krummrich
Now that we got the kernel
Boxtype in place, convert all existingBoxusers to make use of it.You missed a couple usages of
Box:
-rust/macros/lib.rs:{242,251,281}
-drivers/block/rnull.rs:{35,50}
I also missed some rustdoc links: rust/kernel/init.rs:{16,746,852} (though these should probably link to Box)
---
Cheers,
Benno
Quoted 18 lines
or is that intentional? (for me rnull doesn't compile after this patch)
---
Cheers,
BennoQuoted 10 lines
Signed-off-by Danilo Krummrich <[email protected]>--- rust/kernel/init.rs | 41 ++++++++++++++++--------------- rust/kernel/init/__internal.rs | 2 +- rust/kernel/sync/arc.rs | 17 ++++++------- rust/kernel/sync/condvar.rs | 4 +-- rust/kernel/sync/lock/mutex.rs | 2 +- rust/kernel/sync/lock/spinlock.rs | 2 +- rust/kernel/workqueue.rs | 20 +++++++-------- 7 files changed, 44 insertions(+), 44 deletions(-)
Danilo Krummrich
maintainerRe: [PATCH v4 10/28] rust: treewide: switch to our kernel `Box` type
In reply to Benno LossinQuoted 9 lines · Benno Lossin
Quoted 2 lines · Danilo Krummrich
Now that we got the kernel
Boxtype in place, convert all existingBoxusers to make use of it.You missed a couple usages of
Box:
-rust/macros/lib.rs:{242,251,281}
-drivers/block/rnull.rs:{35,50}or is that intentional? (for me rnull doesn't compile after this patch)
No, I missed them. I probably messed up my .config. I really thought I had everything relevent enabled for compilation.
Gonna fix those.
Quoted 16 lines
---
Cheers,
BennoQuoted 10 lines
Signed-off-by Danilo Krummrich <[email protected]>--- rust/kernel/init.rs | 41 ++++++++++++++++--------------- rust/kernel/init/__internal.rs | 2 +- rust/kernel/sync/arc.rs | 17 ++++++------- rust/kernel/sync/condvar.rs | 4 +-- rust/kernel/sync/lock/mutex.rs | 2 +- rust/kernel/sync/lock/spinlock.rs | 2 +- rust/kernel/workqueue.rs | 20 +++++++-------- 7 files changed, 44 insertions(+), 44 deletions(-)
Danilo Krummrich
maintainerPATCH v4 11/28 rust: alloc: remove `BoxExt` extension
In reply to Danilo KrummrichNow that all existing Box users were moved to the kernel Box type, remove the BoxExt extension.
--- rust/kernel/alloc.rs | 1 - rust/kernel/alloc/box_ext.rs | 56 ------------------------------------ rust/kernel/init.rs | 34 +--------------------- rust/kernel/lib.rs | 1 - rust/kernel/prelude.rs | 4 +-- rust/kernel/types.rs | 23 --------------- 6 files changed, 3 insertions(+), 116 deletions(-) delete mode 100644 rust/kernel/alloc/box_ext.rs
rust/kernel/alloc.rs +0 -1
@@ -4,7 +4,6 @@#[cfg(not(any(test, testlib)))] pub mod allocator; -pub mod box_ext;pub mod kbox;pub mod vec_ext;
rust/kernel/alloc/box_ext.rs deleted +0 -56
56 lines not on this page · raw message
rust/kernel/init.rs +1 -33
@@ -211,12 +211,11 @@//! [`pin_init!`]: crate::pin_init! use crate ::{ - alloc::{box_ext::BoxExt ,AllocError ,Allocator ,Flags ,KBox }, + alloc::{AllocError ,Allocator ,Flags ,KBox }, error::{self,Error }, sync::UniqueArc , types::{Opaque ,ScopeGuard }, }; -use alloc::boxed::Box ;use core::{ cell::UnsafeCell , convert::Infallible ,
@@ -589,7 +588,6 @@ macro_rules! pin_init {/// # Examples /// /// ```rust -/// # #![feature(new_uninit)] /// use kernel::{init::{self, PinInit}, error::Error}; /// #[pin_data] /// struct BigBuf {
@@ -1149,36 +1147,6 @@ fn init<E>(init: impl Init<T, E>, flags: Flags) -> error::Result<Self> } } -impl <T >InPlaceInit <T >for Box <T > { -#[inline] -fn try_pin_init <E >(init:impl PinInit <T ,E >, flags:Flags ) ->Result <Pin <Self >,E > -where -E :From <AllocError >, - { -let mut this = <Box <_ >as BoxExt <_ >>::new_uninit (flags)?; -let slot = this.as_mut_ptr (); -// SAFETY: When init errors/panics, slot will get deallocated but not dropped, -// slot is valid and will not be moved, because we pin it later. -unsafe { init.__pinned_init (slot)? }; -// SAFETY: All fields have been initialized. -Ok (unsafe { this.assume_init () }.into ()) - } - -#[inline] -fn try_init <E >(init:impl Init <T ,E >, flags:Flags ) ->Result <Self ,E > -where -E :From <AllocError >, - { -let mut this = <Box <_ >as BoxExt <_ >>::new_uninit (flags)?; -let slot = this.as_mut_ptr (); -// SAFETY: When init errors/panics, slot will get deallocated but not dropped, -// slot is valid. -unsafe { init.__init (slot)? }; -// SAFETY: All fields have been initialized. -Ok (unsafe { this.assume_init () }) - } -} -impl <T ,A >InPlaceInit <T >for crate ::alloc::Box <T ,A >where A :Allocator + 'static,
rust/kernel/lib.rs +0 -1
@@ -14,7 +14,6 @@#![no_std] #![feature(coerce_unsized)] #![feature(dispatch_from_dyn)] -#![feature(new_uninit)] #![feature(receiver_trait)] #![feature(unsize)]
rust/kernel/prelude.rs +2 -2
@@ -14,10 +14,10 @@#[doc(no_inline)] pub use core::pin::Pin ; -pub use crate ::alloc::{box_ext::BoxExt , flags::*, vec_ext::VecExt ,KBox ,KVBox ,VBox }; +pub use crate ::alloc::{flags::*, vec_ext::VecExt ,KBox ,KVBox ,VBox };#[doc(no_inline)] -pub use alloc::{boxed::Box , vec::Vec }; +pub use alloc::vec::Vec ;#[doc(no_inline)] pub use macros::{module, pin_data, pinned_drop, vtable,Zeroable };
rust/kernel/types.rs +0 -23
@@ -4,7 +4,6 @@use crate ::alloc::Allocator ;use crate ::init::{self,PinInit }; -use alloc::boxed::Box ;use core::{ cell::UnsafeCell , marker::{PhantomData ,PhantomPinned },
@@ -69,28 +68,6 @@ unsafe fn try_from_foreign(ptr: *const core::ffi::c_void) -> Option<Self> { } } -impl <T : 'static>ForeignOwnable for Box <T > { -type Borrowed <'a> = &'aT ; - -fn into_foreign (self) -> *const core::ffi::c_void { -Box ::into_raw (self)as _ - } - -unsafe fn borrow <'a>(ptr: *const core::ffi::c_void ) -> &'aT { -// SAFETY: The safety requirements for this function ensure that the object is still alive, -// so it is safe to dereference the raw pointer. -// The safety requirements of `from_foreign` also ensure that the object remains alive for -// the lifetime of the returned value. -unsafe { &*ptr.cast () } - } - -unsafe fn from_foreign (ptr: *const core::ffi::c_void ) ->Self { -// SAFETY: The safety requirements of this function ensure that `ptr` comes from a previous -// call to `Self::into_foreign`. -unsafe {Box ::from_raw (ptras _ ) } - } -} -impl <T : 'static,A >ForeignOwnable for crate ::alloc::Box <T ,A >where A :Allocator ,
Signature · 1 line
2.45.2
Re: [PATCH v4 11/28] rust: alloc: remove `BoxExt` extension
Quoted 2 lines · Danilo Krummrich
Now that all existing
Boxusers were moved to the kernelBoxtype, remove theBoxExtextension.
In the changes below you also remove the new_uninit feature, the impl InPlaceInit for Box and the impl ForeignOwnable for Box but don't mention it here.
---
Cheers,
Benno
Quoted 11 lines
Reviewed-by Alice Ryhl <[email protected]>Signed-off-by Danilo Krummrich <[email protected]>--- rust/kernel/alloc.rs | 1 - rust/kernel/alloc/box_ext.rs | 56 ------------------------------------ rust/kernel/init.rs | 34 +--------------------- rust/kernel/lib.rs | 1 - rust/kernel/prelude.rs | 4 +-- rust/kernel/types.rs | 23 --------------- 6 files changed, 3 insertions(+), 116 deletions(-) delete mode 100644 rust/kernel/alloc/box_ext.rs
Danilo Krummrich
maintainerPATCH v4 12/28 rust: alloc: add `Box` to prelude
In reply to Danilo KrummrichNow that we removed BoxExt and the corresponding includes in prelude.rs, add the new kernel Box type instead.
--- rust/kernel/prelude.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-)
rust/kernel/prelude.rs +1 -1
@@ -14,7 +14,7 @@#[doc(no_inline)] pub use core::pin::Pin ; -pub use crate ::alloc::{flags::*, vec_ext::VecExt ,KBox ,KVBox ,VBox }; +pub use crate ::alloc::{flags::*, vec_ext::VecExt ,Box ,KBox ,KVBox ,VBox };#[doc(no_inline)] pub use alloc::vec::Vec ;
Signature · 1 line
2.45.2
Re: [PATCH v4 12/28] rust: alloc: add `Box` to prelude
Quoted 5 lines · Danilo Krummrich
Now that we removed
BoxExtand the corresponding includes inprelude.rs, add the new kernelBoxtype instead.Reviewed-by Alice Ryhl <[email protected]>Signed-off-by Danilo Krummrich <[email protected]>
---
Cheers,
Benno
Quoted 20 lines
--- rust/kernel/prelude.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-)rust/kernel/prelude.rs +1
-2@@ -14,7 +14,7 @@#[doc(no_inline)] pub use core::pin::Pin ; -pub use crate ::alloc::{flags::*, vec_ext::VecExt ,KBox ,KVBox ,VBox }; +pub use crate ::alloc::{flags::*, vec_ext::VecExt ,Box ,KBox ,KVBox ,VBox };#[doc(no_inline)] pub use alloc::vec::Vec ; --2.45.2
Danilo Krummrich
maintainerPATCH v4 13/28 rust: alloc: import kernel `Box` type in types.rs
In reply to Danilo KrummrichNow that we removed BoxExt and the corresponding includes in types.rs, add the new kernel Box type instead.
--- rust/kernel/types.rs | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-)
rust/kernel/types.rs +7 -7
@@ -2,7 +2,7 @@//! Kernel types. -use crate ::alloc::Allocator ; +use crate ::alloc::{Allocator ,Box };use crate ::init::{self,PinInit };use core::{ cell::UnsafeCell ,
@@ -68,14 +68,14 @@ unsafe fn try_from_foreign(ptr: *const core::ffi::c_void) -> Option<Self> { } } -impl <T : 'static,A >ForeignOwnable for crate ::alloc::Box <T ,A > +impl <T : 'static,A >ForeignOwnable for Box <T ,A >where A :Allocator , {type Borrowed <'a> = &'aT ;fn into_foreign (self) -> *const core::ffi::c_void { -crate ::alloc::Box ::into_raw (self)as _ +Box ::into_raw (self)as _ }unsafe fn borrow <'a>(ptr: *const core::ffi::c_void ) -> &'aT {
@@ -89,11 +89,11 @@ unsafe fn borrow<'a>(ptr: *const core::ffi::c_void) -> &'a T {unsafe fn from_foreign (ptr: *const core::ffi::c_void ) ->Self {// SAFETY: The safety requirements of this function ensure that `ptr` comes from a previous // call to `Self::into_foreign`. -unsafe {crate ::alloc::Box ::from_raw (ptras _ ) } +unsafe {Box ::from_raw (ptras _ ) } } } -impl <T : 'static,A >ForeignOwnable for Pin <crate ::alloc::Box <T ,A >> +impl <T : 'static,A >ForeignOwnable for Pin <Box <T ,A >>where A :Allocator , {
@@ -101,7 +101,7 @@ impl<T: 'static, A> ForeignOwnable for Pin<crate::alloc::Box<T, A>>fn into_foreign (self) -> *const core::ffi::c_void {// SAFETY: We are still treating the box as pinned. -crate ::alloc::Box ::into_raw (unsafe {Pin ::into_inner_unchecked (self) })as _ +Box ::into_raw (unsafe {Pin ::into_inner_unchecked (self) })as _ }unsafe fn borrow <'a>(ptr: *const core::ffi::c_void ) ->Pin <&'aT > {
@@ -118,7 +118,7 @@ unsafe fn borrow<'a>(ptr: *const core::ffi::c_void) -> Pin<&'a T> {unsafe fn from_foreign (ptr: *const core::ffi::c_void ) ->Self {// SAFETY: The safety requirements of this function ensure that `ptr` comes from a previous // call to `Self::into_foreign`. -unsafe {Pin ::new_unchecked (crate ::alloc::Box ::from_raw (ptras _ )) } +unsafe {Pin ::new_unchecked (Box ::from_raw (ptras _ )) } } }
Signature · 1 line
2.45.2
Danilo Krummrich
maintainerPATCH v4 14/28 rust: alloc: import kernel `Box` type in init.rs
In reply to Danilo KrummrichNow that we removed BoxExt and the corresponding includes in init.rs, add the new kernel Box type instead.
--- rust/kernel/init.rs | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-)
rust/kernel/init.rs +6 -6
@@ -211,7 +211,7 @@//! [`pin_init!`]: crate::pin_init! use crate ::{ - alloc::{AllocError ,Allocator ,Flags ,KBox }, + alloc::{AllocError ,Allocator ,Box ,Flags ,KBox }, error::{self,Error }, sync::UniqueArc , types::{Opaque ,ScopeGuard },
@@ -1147,7 +1147,7 @@ fn init<E>(init: impl Init<T, E>, flags: Flags) -> error::Result<Self> } } -impl <T ,A >InPlaceInit <T >for crate ::alloc::Box <T ,A > +impl <T ,A >InPlaceInit <T >for Box <T ,A >where A :Allocator + 'static, {
@@ -1156,13 +1156,13 @@ fn try_pin_init<E>(init: impl PinInit<T, E>, flags: Flags) -> Result<Pin<Self>, where E: From<AllocError>, { -let mut this =crate ::alloc::Box ::<_ ,A >::new_uninit (flags)?; +let mut this =Box ::<_ ,A >::new_uninit (flags)?;let slot = this.as_mut_ptr ();// SAFETY: When init errors/panics, slot will get deallocated but not dropped, // slot is valid and will not be moved, because we pin it later. unsafe { init.__pinned_init (slot)? };// SAFETY: All fields have been initialized. -Ok (unsafe {crate ::alloc::Box ::assume_init (this) }.into ()) +Ok (unsafe {Box ::assume_init (this) }.into ()) } #[inline]
@@ -1170,13 +1170,13 @@ fn try_init<E>(init: impl Init<T, E>, flags: Flags) -> Result<Self, E> where E: From<AllocError>, { -let mut this =crate ::alloc::Box ::<_ ,A >::new_uninit (flags)?; +let mut this =Box ::<_ ,A >::new_uninit (flags)?;let slot = this.as_mut_ptr ();// SAFETY: When init errors/panics, slot will get deallocated but not dropped, // slot is valid. unsafe { init.__init (slot)? };// SAFETY: All fields have been initialized. -Ok (unsafe {crate ::alloc::Box ::assume_init (this) }) +Ok (unsafe {Box ::assume_init (this) }) } }
Signature · 1 line
2.45.2
Danilo Krummrich
maintainerPATCH v4 15/28 rust: alloc: implement kernel `Vec` type
In reply to Danilo KrummrichVec provides a contiguous growable array type (such as Vec) with contents allocated with the kernel's allocators (e.g. Kmalloc, Vmalloc or KVmalloc).
In contrast to Rust's Vec type, the kernel Vec type considers the kernel's GFP flags for all appropriate functions, always reports allocation failures through Result<_, AllocError> and remains independent from unstable features.
--- rust/kernel/alloc.rs | 6 + rust/kernel/alloc/kbox.rs | 16 +- rust/kernel/alloc/kvec.rs | 591 ++++++++++++++++++++++++++++++++++++++ rust/kernel/prelude.rs | 2 +- 4 files changed, 613 insertions(+), 2 deletions(-) create mode 100644 rust/kernel/alloc/kvec.rs
rust/kernel/alloc.rs +6 -0
@@ -5,6 +5,7 @@#[cfg(not(any(test, testlib)))] pub mod allocator;pub mod kbox; +pub mod kvec;pub mod vec_ext;#[cfg(any(test, testlib))]
@@ -18,6 +19,11 @@pub use self::kbox::KVBox ;pub use self::kbox::VBox ; +pub use self::kvec::KVVec ; +pub use self::kvec::KVec ; +pub use self::kvec::VVec ; +pub use self::kvec::Vec ; +/// Indicates an allocation error. #[derive( Copy , Clone , PartialEq , Eq , Debug )] pub struct AllocError ;
rust/kernel/alloc/kbox.rs +15 -1
@@ -2,7 +2,7 @@//! Implementation of [`Box`]. -use super ::{AllocError ,Allocator ,Flags }; +use super ::{AllocError ,Allocator ,Flags ,Vec };use core::fmt;use core::marker::PhantomData ;use core::mem::ManuallyDrop ;
@@ -150,6 +150,20 @@ pub fn into_pin(b: Self) -> Pin<Self> } } +impl <T ,A ,const N :usize >Box <[T ;N ],A > +where +A :Allocator , +{ +/// Convert a `Box<[T, N], A>` to a `Vec<T, A>`. +pub fn into_vec (b:Self ) ->Vec <T ,A > { +let len = b.len (); +unsafe { +let ptr =Self ::into_raw (b); +Vec ::from_raw_parts (ptras _ , len, len) + } + } +} +impl <T ,A >Box <MaybeUninit <T >,A >where A :Allocator ,
rust/kernel/alloc/kvec.rs new file +591 -0
591 lines not on this page · raw message
rust/kernel/prelude.rs +1 -1
@@ -14,7 +14,7 @@#[doc(no_inline)] pub use core::pin::Pin ; -pub use crate ::alloc::{flags::*, vec_ext::VecExt ,Box ,KBox ,KVBox ,VBox }; +pub use crate ::alloc::{flags::*, vec_ext::VecExt ,Box ,KBox ,KVBox ,KVVec ,KVec ,VBox ,VVec };#[doc(no_inline)] pub use alloc::vec::Vec ;
Signature · 1 line
2.45.2
Danilo Krummrich
maintainerPATCH v4 16/28 rust: alloc: implement `IntoIterator` for `Vec`
In reply to Danilo KrummrichImplement IntoIterator for Vec, Vec's IntoIter type, as well as Iterator for IntoIter.
Vec::into_iter disassembles the Vec into its raw parts; additionally, IntoIter keeps track of a separate pointer, which is incremented correspondingsly as the iterator advances, while the length, or the count of elements, is decremented.
This also means that IntoIter takes the ownership of the backing buffer and is responsible to drop the remaining elements and free the backing buffer, if it's dropped.
--- rust/kernel/alloc.rs | 1 + rust/kernel/alloc/kvec.rs | 186 ++++++++++++++++++++++++++++++++++++++ 2 files changed, 187 insertions(+)
rust/kernel/alloc.rs +1 -0
@@ -19,6 +19,7 @@pub use self::kbox::KVBox ;pub use self::kbox::VBox ; +pub use self::kvec::IntoIter ;pub use self::kvec::KVVec ;pub use self::kvec::KVec ;pub use self::kvec::VVec ;
rust/kernel/alloc/kvec.rs +186 -0
195 lines not on this page · raw message
Signature · 1 line
2.45.2
Danilo Krummrich
maintainerPATCH v4 17/28 rust: alloc: implement `collect` for `IntoIter`
In reply to Danilo KrummrichCurrently, we can't implement FromIterator. There are a couple of issues with this trait in the kernel, namely:
- Rust's specialization feature is unstable. This prevents us to optimze for the special case where I::IntoIter equals Vec's IntoIter type.
- We also can't use I::IntoIter's type ID either to work around this, since FromIterator doesn't require this type to be 'static.
- FromIterator::from_iter does return Self instead of Result<Self, AllocError>, hence we can't properly handle allocation failures.
- Neither Iterator::collect nor FromIterator::from_iter can handle additional allocation flags.
Instead, provide IntoIter::collect, such that we can at least convert IntoIter into a Vec again.
--- rust/kernel/alloc/kvec.rs | 78 +++++++++++++++++++++++++++++++++++++++ 1 file changed, 78 insertions(+)
rust/kernel/alloc/kvec.rs +78 -0
@@ -644,6 +644,84 @@ impl<T, A> IntoIter<T, A>fn as_raw_mut_slice (&mut self) -> *mut [T ] { ptr::slice_from_raw_parts_mut (self.ptr, self.len) } + +fn into_raw_parts (self) -> (*mut T ,NonNull <T >,usize ,usize ) { +let me =ManuallyDrop ::new (self); +let ptr = me.ptr; +let buf = me.buf; +let len = me.len; +let cap = me.cap; + (ptr, buf, len, cap) + } + +/// Same as `Iterator::collect` but specialized for `Vec`'s `IntoIter`. +/// +/// Currently, we can't implement `FromIterator`. There are a couple of issues with this trait +/// in the kernel, namely: +/// +/// - Rust's specialization feature is unstable. This prevents us to optimze for the special +/// case where `I::IntoIter` equals `Vec`'s `IntoIter` type. +/// - We also can't use `I::IntoIter`'s type ID either to work around this, since `FromIterator` +/// doesn't require this type to be `'static`. +/// - `FromIterator::from_iter` does return `Self` instead of `Result<Self, AllocError>`, hence +/// we can't properly handle allocation failures. +/// - Neither `Iterator::collect` nor `FromIterator::from_iter` can handle additional allocation +/// flags. +/// +/// Instead, provide `IntoIter::collect`, such that we can at least convert a `IntoIter` into a +/// `Vec` again. +/// +/// Note that `IntoIter::collect` doesn't require `Flags`, since it re-uses the existing backing +/// buffer. However, this backing buffer may be shrunk to the actual count of elements. +/// +/// # Examples +/// +/// ``` +/// let v = kernel::kvec![1, 2, 3]?; +/// let mut it = v.into_iter(); +/// +/// assert_eq!(it.next(), Some(1)); +/// +/// let v = it.collect(GFP_KERNEL); +/// assert_eq!(v, [2, 3]); +/// +/// # Ok::<(), Error>(()) +/// ``` +pub fn collect (self, flags:Flags ) ->Vec <T ,A > { +let (mut ptr, buf, len,mut cap) = self.into_raw_parts (); +let has_advanced = ptr != buf.as_ptr (); + +if has_advanced { +// SAFETY: Copy the contents we have advanced to at the beginning of the buffer. +// `ptr` is guaranteed to be between `buf` and `buf.add(cap)` and `ptr.add(len)` is +// guaranteed to be smaller than `buf.add(cap)`. +unsafe { ptr::copy (ptr, buf.as_ptr (), len) }; + ptr = buf.as_ptr (); + } + +// This can never fail, `len` is guaranteed to be smaller than `cap`. +let layout = core::alloc::Layout ::array ::<T >(len).unwrap (); + +// SAFETY: `buf` points to the start of the backing buffer and `len` is guaranteed to be +// smaller than `cap`. Depending on `alloc` this operation may shrink the buffer or leaves +// it as it is. + ptr =match unsafe {A ::realloc (Some (buf.cast ()), layout, flags) } { +// If we fail to shrink, which likely can't even happen, continue with the existing +// buffer. +Err (_) => ptr, +Ok (ptr) => { + cap = len; + ptr.as_ptr ().cast () + } + }; + +// SAFETY: If the iterator has been advanced, the advanced elements have been copied to +// the beginning of the buffer and `len` has been adjusted accordingly. `ptr` is guaranteed +// to point to the start of the backing buffer. `cap` is either the original capacity or, +// after shrinking the buffer, equal to `len`. `alloc` is guaranteed to be unchanged since +// `into_iter` has been called on the original `Vec`. +unsafe {Vec ::from_raw_parts (ptr, len, cap) } + } }impl <T ,A >Iterator for IntoIter <T ,A >
Signature · 1 line
2.45.2
Danilo Krummrich
maintainerPATCH v4 18/28 rust: treewide: switch to the kernel `Vec` type
In reply to Danilo KrummrichNow that we got the kernel Vec in place, convert all existing Vec users to make use of it.
--- rust/kernel/str.rs | 12 +++++------- rust/kernel/sync/locked_by.rs | 2 +- rust/kernel/types.rs | 2 +- rust/kernel/uaccess.rs | 15 ++++++--------- samples/rust/rust_minimal.rs | 4 ++-- 5 files changed, 15 insertions(+), 20 deletions(-)
rust/kernel/str.rs +5 -7
@@ -2,8 +2,7 @@//! String representations. -use crate ::alloc::{flags::*, vec_ext::VecExt ,AllocError }; -use alloc::vec::Vec ; +use crate ::alloc::{flags::*,AllocError ,KVec };use core::fmt::{self,Write };use core::ops::{self,Deref ,DerefMut ,Index };
@@ -790,7 +789,7 @@ fn write_str(&mut self, s: &str) -> fmt::Result {/// assert_eq!(s.is_ok(), false); /// ``` pub struct CString { - buf:Vec <u8 >, + buf:KVec <u8 >, }impl CString {
@@ -803,7 +802,7 @@ pub fn try_from_fmt(args: fmt::Arguments<'_>) -> Result<Self, Error> {let size = f.bytes_written ();// Allocate a vector with the required number of bytes, and write to it. -let mut buf = <Vec <_ >as VecExt <_ >>::with_capacity (size,GFP_KERNEL )?; +let mut buf =KVec ::with_capacity (size,GFP_KERNEL )?;// SAFETY: The buffer stored in `buf` is at least of size `size` and is valid for writes. let mut f =unsafe {Formatter ::from_buffer (buf.as_mut_ptr (), size) }; f.write_fmt (args)?;
@@ -850,10 +849,9 @@ impl<'a> TryFrom<&'a CStr> for CString {type Error =AllocError ;fn try_from (cstr: &'aCStr ) ->Result <CString ,AllocError > { -let mut buf =Vec ::new (); +let mut buf =KVec ::new (); - <Vec <_ >as VecExt <_ >>::extend_from_slice (&mut buf, cstr.as_bytes_with_nul (),GFP_KERNEL ) - .map_err (|_|AllocError )?; +KVec ::extend_from_slice (&mut buf, cstr.as_bytes_with_nul (),GFP_KERNEL )?;// INVARIANT: The `CStr` and `CString` types have the same invariants for // the string data, and we copied it over without changes.
rust/kernel/sync/locked_by.rs +1 -1
@@ -43,7 +43,7 @@/// struct InnerDirectory { /// /// The sum of the bytes used by all files. /// bytes_used: u64, -/// _files: Vec<File>, +/// _files: KVec<File>, /// } /// /// struct Directory {
rust/kernel/types.rs +1 -1
@@ -188,7 +188,7 @@ unsafe fn from_foreign(_: *const core::ffi::c_void) -> Self {}/// # use kernel::types::ScopeGuard; /// fn example3(arg: bool) -> Result { /// let mut vec = -/// ScopeGuard::new_with_data(Vec::new(), |v| pr_info!("vec had {} elements\n", v.len())); +/// ScopeGuard::new_with_data(KVec::new(), |v| pr_info!("vec had {} elements\n", v.len())); /// /// vec.push(10u8, GFP_KERNEL)?; /// if arg {
rust/kernel/uaccess.rs +6 -9
@@ -11,7 +11,6 @@ prelude::*, types::{AsBytes, FromBytes}, }; -use alloc::vec::Vec ;use core::ffi::{c_ulong, c_void};use core::mem::{size_of,MaybeUninit };
@@ -46,7 +45,6 @@/// every byte in the region. /// /// ```no_run -/// use alloc::vec::Vec; /// use core::ffi::c_void; /// use kernel::error::Result; /// use kernel::uaccess::{UserPtr, UserSlice};
@@ -54,7 +52,7 @@/// fn bytes_add_one(uptr: UserPtr, len: usize) -> Result<()> { /// let (read, mut write) = UserSlice::new(uptr, len).reader_writer(); /// -/// let mut buf = Vec::new(); +/// let mut buf = KVec::new(); /// read.read_all(&mut buf, GFP_KERNEL)?; /// /// for b in &mut buf {
@@ -69,7 +67,6 @@/// Example illustrating a TOCTOU (time-of-check to time-of-use) bug. /// /// ```no_run -/// use alloc::vec::Vec; /// use core::ffi::c_void; /// use kernel::error::{code::EINVAL, Result}; /// use kernel::uaccess::{UserPtr, UserSlice};
@@ -78,14 +75,14 @@/// fn is_valid(uptr: UserPtr, len: usize) -> Result<bool> { /// let read = UserSlice::new(uptr, len).reader(); /// -/// let mut buf = Vec::new(); +/// let mut buf = KVec::new(); /// read.read_all(&mut buf, GFP_KERNEL)?; /// /// todo!() /// } /// /// /// Returns the bytes behind this user pointer if they are valid. -/// fn get_bytes_if_valid(uptr: UserPtr, len: usize) -> Result<Vec<u8>> { +/// fn get_bytes_if_valid(uptr: UserPtr, len: usize) -> Result<KVec<u8>> { /// if !is_valid(uptr, len)? { /// return Err(EINVAL); /// }
@@ -130,7 +127,7 @@ pub fn new(ptr: UserPtr, length: usize) -> Self {/// Reads the entirety of the user slice, appending it to the end of the provided buffer. /// /// Fails with [`EFAULT`] if the read happens on a bad address. -pub fn read_all (self, buf: &mut Vec <u8 >, flags:Flags ) ->Result { +pub fn read_all (self, buf: &mut KVec <u8 >, flags:Flags ) ->Result { self.reader ().read_all (buf, flags) }
@@ -291,9 +288,9 @@ pub fn read<T: FromBytes>(&mut self) -> Result<T> {/// Reads the entirety of the user slice, appending it to the end of the provided buffer. /// /// Fails with [`EFAULT`] if the read happens on a bad address. -pub fn read_all (mut self, buf: &mut Vec <u8 >, flags:Flags ) ->Result { +pub fn read_all (mut self, buf: &mut KVec <u8 >, flags:Flags ) ->Result {let len = self.length; -VecExt ::<u8 >::reserve (buf, len, flags)?; +KVec ::<u8 >::reserve (buf, len, flags)?;// The call to `try_reserve` was successful, so the spare capacity is at least `len` bytes // long.
samples/rust/rust_minimal.rs +2 -2
@@ -13,7 +13,7 @@ }struct RustMinimal { - numbers:Vec <i32 >, + numbers:KVec <i32 >, }impl kernel::Module for RustMinimal {
@@ -21,7 +21,7 @@ fn init(_module: &'static ThisModule) -> Result<Self> {pr_info! ("Rust minimal sample (init)\n" );pr_info! ("Am I built-in? {}\n" , !cfg! (MODULE )); -let mut numbers =Vec ::new (); +let mut numbers =KVec ::new (); numbers.push (72 ,GFP_KERNEL )?; numbers.push (108 ,GFP_KERNEL )?; numbers.push (200 ,GFP_KERNEL )?;
Signature · 1 line
2.45.2
Re: [PATCH v4 18/28] rust: treewide: switch to the kernel `Vec` type
Quoted 5 lines · Danilo Krummrich
Now that we got the kernel
Vecin place, convert all existingVecusers to make use of it.Signed-off-by Danilo Krummrich <[email protected]>
I have two nits. With them fixed, you can add:
rust/kernel/str.rs impl<'a> TryFrom<&'a CStr> for CString { · line 854
Quoted 3 lines
rust/kernel/str.rs +1
-2- <Vec <_ >as VecExt <_ >>::extend_from_slice (&mut buf, cstr.as_bytes_with_nul (),GFP_KERNEL ) - .map_err (|_|AllocError )?; +KVec ::extend_from_slice (&mut buf, cstr.as_bytes_with_nul (),GFP_KERNEL )?;
This would be more idiomatic as buf.extend_from_slice(cstr.as_bytes_with_nul(), GFP_KERNEL)?;
rust/kernel/uaccess.rs pub fn read<T: FromBytes>(&mut self) -> Result<T> { · line 293
Quoted 9 lines
rust/kernel/uaccess.rs +2
-2@@ -291,9 +288,9 @@ pub fn read<T: FromBytes>(&mut self) -> Result<T> {/// Reads the entirety of the user slice, appending it to the end of the provided buffer. /// /// Fails with [`EFAULT`] if the read happens on a bad address. -pub fn read_all (mut self, buf: &mut Vec <u8 >, flags:Flags ) ->Result { +pub fn read_all (mut self, buf: &mut KVec <u8 >, flags:Flags ) ->Result {let len = self.length; -VecExt ::<u8 >::reserve (buf, len, flags)?; +KVec ::<u8 >::reserve (buf, len, flags)?;
Ditto here. Could be:buf.reserve(len, flags)?;
Alice
Re: [PATCH v4 18/28] rust: treewide: switch to the kernel `Vec` type
In reply to Danilo KrummrichQuoted 2 lines · Danilo Krummrich
Now that we got the kernel
Vecin place, convert all existingVecusers to make use of it.
You missed one in rust/kernel/uaccess.rs:92, but this doesn't prevent it from compiling since the allocator is correctly inferred from the context.
---
Cheers,
Benno
Quoted 8 lines
Signed-off-by Danilo Krummrich <[email protected]>--- rust/kernel/str.rs | 12 +++++------- rust/kernel/sync/locked_by.rs | 2 +- rust/kernel/types.rs | 2 +- rust/kernel/uaccess.rs | 15 ++++++--------- samples/rust/rust_minimal.rs | 4 ++-- 5 files changed, 15 insertions(+), 20 deletions(-)
Danilo Krummrich
maintainerPATCH v4 19/28 rust: alloc: remove `VecExt` extension
In reply to Danilo KrummrichNow that all existing Vec users were moved to the kernel Vec type, remove the VecExt extension.
--- rust/kernel/alloc.rs | 1 - rust/kernel/alloc/vec_ext.rs | 185 ----------------------------------- rust/kernel/prelude.rs | 5 +- 3 files changed, 1 insertion(+), 190 deletions(-) delete mode 100644 rust/kernel/alloc/vec_ext.rs
rust/kernel/alloc.rs +0 -1
@@ -6,7 +6,6 @@pub mod allocator;pub mod kbox;pub mod kvec; -pub mod vec_ext;#[cfg(any(test, testlib))] pub mod allocator_test;
rust/kernel/alloc/vec_ext.rs deleted +0 -185
185 lines not on this page · raw message
rust/kernel/prelude.rs +1 -4
@@ -14,10 +14,7 @@#[doc(no_inline)] pub use core::pin::Pin ; -pub use crate ::alloc::{flags::*, vec_ext::VecExt ,Box ,KBox ,KVBox ,KVVec ,KVec ,VBox ,VVec }; - -#[doc(no_inline)] -pub use alloc::vec::Vec ; +pub use crate ::alloc::{flags::*,Box ,KBox ,KVBox ,KVVec ,KVec ,VBox ,VVec };#[doc(no_inline)] pub use macros::{module, pin_data, pinned_drop, vtable,Zeroable };
Signature · 1 line
2.45.2
Re: [PATCH v4 19/28] rust: alloc: remove `VecExt` extension
Quoted 5 lines · Danilo Krummrich
Now that all existing
Vecusers were moved to the kernelVectype, remove theVecExtextension.Signed-off-by Danilo Krummrich <[email protected]>
Re: [PATCH v4 19/28] rust: alloc: remove `VecExt` extension
In reply to Danilo KrummrichQuoted 10 lines · Danilo Krummrich
Now that all existing
Vecusers were moved to the kernelVectype, remove theVecExtextension.Signed-off-by Danilo Krummrich <[email protected]>--- rust/kernel/alloc.rs | 1 - rust/kernel/alloc/vec_ext.rs | 185 ----------------------------------- rust/kernel/prelude.rs | 5 +- 3 files changed, 1 insertion(+), 190 deletions(-) delete mode 100644 rust/kernel/alloc/vec_ext.rs
---
Cheers,
Benno
Danilo Krummrich
maintainerPATCH v4 20/28 rust: alloc: add `Vec` to prelude
In reply to Danilo KrummrichNow that we removed VecExt and the corresponding includes in prelude.rs, add the new kernel Vec type instead.
--- rust/kernel/prelude.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-)
rust/kernel/prelude.rs +1 -1
@@ -14,7 +14,7 @@#[doc(no_inline)] pub use core::pin::Pin ; -pub use crate ::alloc::{flags::*,Box ,KBox ,KVBox ,KVVec ,KVec ,VBox ,VVec }; +pub use crate ::alloc::{flags::*,Box ,KBox ,KVBox ,KVVec ,KVec ,VBox ,VVec ,Vec };#[doc(no_inline)] pub use macros::{module, pin_data, pinned_drop, vtable,Zeroable };
Signature · 1 line
2.45.2
Re: [PATCH v4 20/28] rust: alloc: add `Vec` to prelude
Quoted 5 lines · Danilo Krummrich
Now that we removed
VecExtand the corresponding includes inprelude.rs, add the new kernelVectype instead.Signed-off-by Danilo Krummrich <[email protected]>
Re: [PATCH v4 20/28] rust: alloc: add `Vec` to prelude
In reply to Danilo KrummrichQuoted 7 lines · Danilo Krummrich
Now that we removed
VecExtand the corresponding includes inprelude.rs, add the new kernelVectype instead.Signed-off-by Danilo Krummrich <[email protected]>--- rust/kernel/prelude.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-)
---
Cheers,
Benno
Quoted 16 lines
rust/kernel/prelude.rs +1
-2@@ -14,7 +14,7 @@#[doc(no_inline)] pub use core::pin::Pin ; -pub use crate ::alloc::{flags::*,Box ,KBox ,KVBox ,KVVec ,KVec ,VBox ,VVec }; +pub use crate ::alloc::{flags::*,Box ,KBox ,KVBox ,KVVec ,KVec ,VBox ,VVec ,Vec };#[doc(no_inline)] pub use macros::{module, pin_data, pinned_drop, vtable,Zeroable }; --2.45.2
Danilo Krummrich
maintainerPATCH v4 21/28 rust: alloc: remove `GlobalAlloc` and `krealloc_aligned`
In reply to Danilo KrummrichNow that we have all the Allocators and the kernel Box and Vec implementations in place, remove GlobalAlloc and krealloc_aligned.
--- rust/kernel/alloc/allocator.rs | 63 ++-------------------------------- 1 file changed, 2 insertions(+), 61 deletions(-)
rust/kernel/alloc/allocator.rs +2 -61
@@ -2,8 +2,8 @@//! Allocator support. -use super ::{flags::*,Flags }; -use core::alloc::{GlobalAlloc ,Layout }; +use super ::Flags ; +use core::alloc::Layout ;use core::ptr;use core::ptr::NonNull ;
@@ -40,27 +40,6 @@ fn aligned_size(new_layout: Layout) -> usize { layout.size () } -/// Calls `krealloc` with a proper size to alloc a new object aligned to `new_layout`'s alignment. -/// -/// # Safety -/// -/// - `ptr` can be either null or a pointer which has been allocated by this allocator. -/// - `new_layout` must have a non-zero size. -pub (crate )unsafe fn krealloc_aligned (ptr: *mut u8 , new_layout:Layout , flags:Flags ) -> *mut u8 { -// SAFETY: -// - `ptr` is either null or a pointer returned from a previous `k{re}alloc()` by the -// function safety requirement. -// - `size` is greater than 0 since it's from `layout.size()` (which cannot be zero according -// to the function safety requirement) -unsafe { - bindings::krealloc ( - ptras *const core::ffi::c_void , -aligned_size (new_layout), - flags.0 , - )as *mut u8 - } -} -/// # Invariants /// /// One of the following `krealloc`, `vrealloc`, `kvrealloc`.
@@ -129,41 +108,6 @@ unsafe fn realloc( } } -unsafe impl GlobalAlloc for Kmalloc { -unsafe fn alloc (&self, layout:Layout ) -> *mut u8 { -// SAFETY: `ptr::null_mut()` is null and `layout` has a non-zero size by the function safety -// requirement. -unsafe {krealloc_aligned (ptr::null_mut (), layout,GFP_KERNEL ) } - } - -unsafe fn dealloc (&self, ptr: *mut u8 , _layout:Layout ) { -unsafe { - bindings::kfree (ptras *const core::ffi::c_void ); - } - } - -unsafe fn realloc (&self, ptr: *mut u8 , layout:Layout , new_size:usize ) -> *mut u8 { -// SAFETY: -// - `new_size`, when rounded up to the nearest multiple of `layout.align()`, will not -// overflow `isize` by the function safety requirement. -// - `layout.align()` is a proper alignment (i.e. not zero and must be a power of two). -let layout =unsafe {Layout ::from_size_align_unchecked (new_size, layout.align ()) }; - -// SAFETY: -// - `ptr` is either null or a pointer allocated by this allocator by the function safety -// requirement. -// - the size of `layout` is not zero because `new_size` is not zero by the function safety -// requirement. -unsafe {krealloc_aligned (ptr, layout,GFP_KERNEL ) } - } - -unsafe fn alloc_zeroed (&self, layout:Layout ) -> *mut u8 { -// SAFETY: `ptr::null_mut()` is null and `layout` has a non-zero size by the function safety -// requirement. -unsafe {krealloc_aligned (ptr::null_mut (), layout,GFP_KERNEL | __GFP_ZERO) } - } -} -unsafe impl Allocator for Vmalloc {unsafe fn realloc ( ptr: Option<NonNull<u8>>,
@@ -204,9 +148,6 @@ unsafe fn realloc( } } -#[global_allocator] -static ALLOCATOR :Kmalloc =Kmalloc ; -// See <https://github.com/rust-lang/rust/pull/86844>. #[no_mangle] static __rust_no_alloc_shim_is_unstable:u8 =0 ;
Signature · 1 line
2.45.2
Björn Roy Baron
reviewerRe: [PATCH v4 21/28] rust: alloc: remove `GlobalAlloc` and `krealloc_aligned`
Quoted 94 lines · Danilo Krummrich
Now that we have all the
Allocators and the kernelBoxandVecimplementations in place, removeGlobalAllocandkrealloc_aligned.Signed-off-by Danilo Krummrich <[email protected]>--- rust/kernel/alloc/allocator.rs | 63 ++-------------------------------- 1 file changed, 2 insertions(+), 61 deletions(-)rust/kernel/alloc/allocator.rs +2
-58@@ -2,8 +2,8 @@//! Allocator support. -use super ::{flags::*,Flags }; -use core::alloc::{GlobalAlloc ,Layout }; +use super ::Flags ; +use core::alloc::Layout ;use core::ptr;use core::ptr::NonNull ;@@ -40,27 +40,6 @@ fn aligned_size(new_layout: Layout) -> usize { layout.size () } -/// Calls `krealloc` with a proper size to alloc a new object aligned to `new_layout`'s alignment. -/// -/// # Safety -/// -/// - `ptr` can be either null or a pointer which has been allocated by this allocator. -/// - `new_layout` must have a non-zero size. -pub (crate )unsafe fn krealloc_aligned (ptr: *mut u8 , new_layout:Layout , flags:Flags ) -> *mut u8 { -// SAFETY: -// - `ptr` is either null or a pointer returned from a previous `k{re}alloc()` by the -// function safety requirement. -// - `size` is greater than 0 since it's from `layout.size()` (which cannot be zero according -// to the function safety requirement) -unsafe { - bindings::krealloc ( - ptras *const core::ffi::c_void , -aligned_size (new_layout), - flags.0 , - )as *mut u8 - } -} -/// # Invariants /// /// One of the following `krealloc`, `vrealloc`, `kvrealloc`. @@ -129,41 +108,6 @@ unsafe fn realloc( } } -unsafe impl GlobalAlloc for Kmalloc { -unsafe fn alloc (&self, layout:Layout ) -> *mut u8 { -// SAFETY: `ptr::null_mut()` is null and `layout` has a non-zero size by the function safety -// requirement. -unsafe {krealloc_aligned (ptr::null_mut (), layout,GFP_KERNEL ) } - } - -unsafe fn dealloc (&self, ptr: *mut u8 , _layout:Layout ) { -unsafe { - bindings::kfree (ptras *const core::ffi::c_void ); - } - } - -unsafe fn realloc (&self, ptr: *mut u8 , layout:Layout , new_size:usize ) -> *mut u8 { -// SAFETY: -// - `new_size`, when rounded up to the nearest multiple of `layout.align()`, will not -// overflow `isize` by the function safety requirement. -// - `layout.align()` is a proper alignment (i.e. not zero and must be a power of two). -let layout =unsafe {Layout ::from_size_align_unchecked (new_size, layout.align ()) }; - -// SAFETY: -// - `ptr` is either null or a pointer allocated by this allocator by the function safety -// requirement. -// - the size of `layout` is not zero because `new_size` is not zero by the function safety -// requirement. -unsafe {krealloc_aligned (ptr, layout,GFP_KERNEL ) } - } - -unsafe fn alloc_zeroed (&self, layout:Layout ) -> *mut u8 { -// SAFETY: `ptr::null_mut()` is null and `layout` has a non-zero size by the function safety -// requirement. -unsafe {krealloc_aligned (ptr::null_mut (), layout,GFP_KERNEL | __GFP_ZERO) } - } -} -unsafe impl Allocator for Vmalloc {unsafe fn realloc ( ptr: Option<NonNull<u8>>,
rust/kernel/alloc/allocator.rs unsafe fn realloc( · line 153
Quoted 9 lines
rust/kernel/alloc/allocator.rs +0
-3@@ -204,9 +148,6 @@ unsafe fn realloc( } } -#[global_allocator] -static ALLOCATOR :Kmalloc =Kmalloc ; -// See <https://github.com/rust-lang/rust/pull/86844>. #[no_mangle] static __rust_no_alloc_shim_is_unstable:u8 =0 ;
This can be removed too. liballoc needed it for us to acknowledge that we were linking liballoc in a way that isn't guaranteed to be stable, but now that we are removing liballoc entirely, this static is no longer necessary.
Quoted 2 lines
--
2.45.2
Re: [PATCH v4 21/28] rust: alloc: remove `GlobalAlloc` and `krealloc_aligned`
Quoted 4 lines · Björn Roy Baron
This can be removed too. liballoc needed it for us to acknowledge that we were linking liballoc in a way that isn't guaranteed to be stable, but now that we are removing liballoc entirely, this static is no longer necessary.
Thanks Björn, noted in issue #2 (and linked to the new tracking issue).
Cheers,
Miguel
Re: [PATCH v4 21/28] rust: alloc: remove `GlobalAlloc` and `krealloc_aligned`
In reply to Danilo KrummrichQuoted 2 lines · Danilo Krummrich
Now that we have all the
Allocators and the kernelBoxandVecimplementations in place, removeGlobalAllocandkrealloc_aligned.
When I try to compile this patch, then I get this error:
UPD include/generated/utsversion.h
CC init/version-timestamp.o
KSYMS .tmp_vmlinux0.kallsyms.S
AS .tmp_vmlinux0.kallsyms.o
LD .tmp_vmlinux1 ld.lld: error: undefined symbol: __rust_realloc >>> referenced by usercopy_64.c
>>> vmlinux.o:(alloc::raw_vec::finish_grow::<alloc::alloc::Global>)
ld.lld: error: undefined symbol: __rust_alloc
>>> referenced by usercopy_64.c
>>> vmlinux.o:(alloc::raw_vec::finish_grow::<alloc::alloc::Global>)
make[2]: *** [scripts/Makefile.vmlinux:34: vmlinux] Error 1
So maybe the #[global_allocator] needs to stay until we remove the alloc dependency?
---
Cheers,
Benno
Quoted 4 lines
Signed-off-by Danilo Krummrich <[email protected]>--- rust/kernel/alloc/allocator.rs | 63 ++-------------------------------- 1 file changed, 2 insertions(+), 61 deletions(-)
Danilo Krummrich
maintainerRe: [PATCH v4 21/28] rust: alloc: remove `GlobalAlloc` and `krealloc_aligned`
Quoted 15 lines · Benno Lossin
Quoted 2 lines · Danilo Krummrich
Now that we have all the
Allocators and the kernelBoxandVecimplementations in place, removeGlobalAllocandkrealloc_aligned.When I try to compile this patch, then I get this error:
UPD include/generated/utsversion.h CC init/version-timestamp.o KSYMS .tmp_vmlinux0.kallsyms.S AS .tmp_vmlinux0.kallsyms.o LD .tmp_vmlinux1ld.lld: error: undefined symbol:
__rust_realloc>>> referenced byusercopy_64.c
>>> vmlinux.o:(alloc::raw_vec::finish_grow::<alloc::alloc::Global>)
Quoted 7 lines
ld.lld: error: undefined symbol:
__rust_alloc
>>> referenced byusercopy_64.c
>>> vmlinux.o:(alloc::raw_vec::finish_grow::<alloc::alloc::Global>)
make[2]: *** [scripts/Makefile.vmlinux:34: vmlinux] Error 1So maybe the
#[global_allocator]needs to stay until we remove theallocdependency?
Yes, I think so. I compile checked the single patches with M=rust and M=drivers, which only compiles, but doesn't link things, so it didn't catch it.
Quoted 10 lines
---
Cheers,
BennoQuoted 4 lines
Signed-off-by Danilo Krummrich <[email protected]>--- rust/kernel/alloc/allocator.rs | 63 ++-------------------------------- 1 file changed, 2 insertions(+), 61 deletions(-)
Danilo Krummrich
maintainerPATCH v4 22/28 rust: error: use `core::alloc::LayoutError`
In reply to Danilo KrummrichUse core::alloc::LayoutError instead of alloc::alloc::LayoutError in preparation to get rid of Rust's alloc crate.
--- rust/kernel/error.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-)
rust/kernel/error.rs +1 -1
@@ -6,7 +6,7 @@use crate ::{alloc::AllocError , str::CStr }; -use alloc::alloc::LayoutError ; +use core::alloc::LayoutError ;use core::fmt;use core::num::TryFromIntError ;
Signature · 1 line
2.45.2
Re: [PATCH v4 22/28] rust: error: use `core::alloc::LayoutError`
Quoted 5 lines · Danilo Krummrich
Use
core::alloc::LayoutErrorinstead ofalloc::alloc::LayoutErrorin preparation to get rid of Rust's alloc crate.Signed-off-by Danilo Krummrich <[email protected]>
It could make sense to squash some of these super small changes that prepare for deleting alloc. E.g., patch 27.
Re: [PATCH v4 22/28] rust: error: use `core::alloc::LayoutError`
In reply to Danilo KrummrichQuoted 7 lines · Danilo Krummrich
Use
core::alloc::LayoutErrorinstead ofalloc::alloc::LayoutErrorin preparation to get rid of Rust's alloc crate.Signed-off-by Danilo Krummrich <[email protected]>--- rust/kernel/error.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-)
---
Cheers,
Benno
Quoted 16 lines
rust/kernel/error.rs +1
-2@@ -6,7 +6,7 @@use crate ::{alloc::AllocError , str::CStr }; -use alloc::alloc::LayoutError ; +use core::alloc::LayoutError ;use core::fmt;use core::num::TryFromIntError ; --2.45.2
Danilo Krummrich
maintainerPATCH v4 23/28 rust: error: check for config `test` in `Error::name`
In reply to Danilo KrummrichAdditional to testlib also check for test in Error::name. This is required by a subsequent patch that (indirectly) uses Èrror in test cases.
--- rust/kernel/error.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-)
rust/kernel/error.rs +2 -2
@@ -140,7 +140,7 @@ pub(crate) fn to_ptr<T>(self) -> *mut T { }/// Returns a string representing the error, if one exists. -#[cfg(not(testlib))] +#[cfg(not(any(test, testlib)))] pub fn name (&self) ->Option <&'staticCStr > {// SAFETY: Just an FFI call, there are no extra safety requirements. let ptr =unsafe { bindings::errname (-self.0 ) };
@@ -157,7 +157,7 @@ pub fn name(&self) -> Option<&'static CStr> {/// When `testlib` is configured, this always returns `None` to avoid the dependency on a /// kernel function so that tests that use this (e.g., by calling [`Result::unwrap`]) can still /// run in userspace. -#[cfg(testlib)] +#[cfg(any(test, testlib))] pub fn name (&self) ->Option <&'staticCStr > {None }
Signature · 1 line
2.45.2
Re: [PATCH v4 23/28] rust: error: check for config `test` in `Error::name`
Quoted 3 lines · Danilo Krummrich
Additional to
testlibalso check fortestinError::name. This is required by a subsequent patch that (indirectly) usesÈrrorin test
That's a weird E.
Quoted 3 lines
cases.
Signed-off-by Danilo Krummrich <[email protected]>
Danilo Krummrich
maintainerPATCH v4 24/28 rust: alloc: implement `contains` for `Flags`
In reply to Danilo KrummrichProvide a simple helper function to check whether given flags do contain one or multiple other flags.
This is used by a subsequent patch implementing the Cmalloc Allocator to check for __GFP_ZERO.
--- rust/kernel/alloc.rs | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-)
rust/kernel/alloc.rs +6 -1
@@ -34,7 +34,7 @@/// They can be combined with the operators `|`, `&`, and `!`. /// /// Values can be used from the [`flags`] module. -#[derive( Clone , Copy )] +#[derive( Clone , Copy , PartialEq )] pub struct Flags (u32 );impl Flags {
@@ -42,6 +42,11 @@ impl Flags {pub (crate )fn as_raw (self) ->u32 { self.0 } + +/// Check whether `flags` is contained in `self`. +pub fn contains (&self, flags:Flags ) ->bool { + (*self & flags) == flags + } }impl core::ops::BitOr for Flags {
Signature · 1 line
2.45.2
Re: [PATCH v4 24/28] rust: alloc: implement `contains` for `Flags`
Quoted 8 lines · Danilo Krummrich
Provide a simple helper function to check whether given flags do contain one or multiple other flags.
This is used by a subsequent patch implementing the Cmalloc
Allocatorto check for__GFP_ZERO.Signed-off-by Danilo Krummrich <[email protected]>
One nit, but:
Quoted 17 lines
rust/kernel/alloc.rs | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-)rust/kernel/alloc.rs +1
-1@@ -34,7 +34,7 @@/// They can be combined with the operators `|`, `&`, and `!`. /// /// Values can be used from the [`flags`] module. -#[derive( Clone , Copy )] +#[derive( Clone , Copy , PartialEq )] pub struct Flags (u32 );impl Flags {
rust/kernel/alloc.rs impl Flags { · line 49
Quoted 8 lines
rust/kernel/alloc.rs +5
-0@@ -42,6 +42,11 @@ impl Flags {pub (crate )fn as_raw (self) ->u32 { self.0 } + +/// Check whether `flags` is contained in `self`. +pub fn contains (&self, flags:Flags ) ->bool { + (*self & flags) == flags + }
Since this type is Copy, you can do this:
pub fn contains(self, flags: Flags) -> bool {
(self & flags) == flags }
and avoid the asterisk.
Alice
Danilo Krummrich
maintainerPATCH v4 25/28 rust: alloc: implement `Cmalloc` in module allocator_test
In reply to Danilo KrummrichSo 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.
---
I know, having an old_size parameter would indeed help implementing Cmalloc.
However, I really don't want test infrastructure to influence the design of kernel internal APIs.
Besides that, adding the old_size parameter would have the consequence that we'd either need to honor it for kernel allocators too (which adds another source of potential failure) or ignore it for all kernel allocators (which potentially tricks people into taking wrong assumptions on how the API works).
--- rust/kernel/alloc/allocator_test.rs | 91 ++++++++++++++++++++++++++--- 1 file changed, 84 insertions(+), 7 deletions(-)
rust/kernel/alloc/allocator_test.rs +84 -7
@@ -2,20 +2,97 @@#![allow(missing_docs)] -use super ::{AllocError ,Allocator ,Flags }; +use super ::{flags::*,AllocError ,Allocator ,Flags };use core::alloc::Layout ; +use core::ptr;use core::ptr::NonNull ; -pub struct Kmalloc ; +pub struct Cmalloc ; +pub type Kmalloc =Cmalloc ;pub type Vmalloc =Kmalloc ;pub type KVmalloc =Kmalloc ; -unsafe impl Allocator for Kmalloc { +extern "C" { +#[link_name = "aligned_alloc" ] +fn libc_aligned_alloc (align:usize , size:usize ) -> *mut core::ffi::c_void ; + +#[link_name = "free" ] +fn libc_free (ptr: *mut core::ffi::c_void ); + +// Do not use this function for production code! For test cases only it's +// probably fine if used with care. +#[link_name = "malloc_usable_size" ] +fn libc_malloc_usable_size (ptr: *mut core::ffi::c_void ) ->usize ; +} + +unsafe impl Allocator for Cmalloc { +fn alloc (layout:Layout , flags:Flags ) ->Result <NonNull <[u8 ]>,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 raw_ptr =unsafe {libc_aligned_alloc (layout.align (), layout.size ()) }as *mut u8 ; + +if flags.contains (__GFP_ZERO) && !raw_ptr.is_null () { +// SAFETY: `raw_ptr` points to memory successfully allocated with `libc_aligned_alloc`. +let size =unsafe {libc_malloc_usable_size (raw_ptr.cast ()) }; + +// SAFETY: `raw_ptr` points to memory successfully allocated with `libc_aligned_alloc` +// of at least `size` bytes. +unsafe { core::ptr::write_bytes (raw_ptr,0 , size) }; + } + +let ptr =if layout.size () ==0 { +NonNull ::dangling () + }else { +NonNull ::new (raw_ptr).ok_or (AllocError )? + }; + +Ok (NonNull ::slice_from_raw_parts (ptr, layout.size ())) + } +unsafe fn realloc ( - _ptr:Option <NonNull <u8 >>, - _layout:Layout , - _flags:Flags , + ptr:Option <NonNull <u8 >>, + layout:Layout , + flags:Flags , ) ->Result <NonNull <[u8 ]>,AllocError > { -panic! (); +let layout = layout.pad_to_align (); +let src: *mut u8 =if let Some (src) = ptr { + src.as_ptr ().cast () + }else { + ptr::null_mut () + }; + +if layout.size () ==0 { +// SAFETY: `src` is either NULL or has previously been allocatored with this +// `Allocator`. +unsafe {libc_free (src.cast ()) }; + +return Ok (NonNull ::slice_from_raw_parts (NonNull ::dangling (),0 )); + } + +let dst =Self ::alloc (layout, flags)?; + +if src.is_null () { +return Ok (dst); + } + +// SAFETY: `src` is either NULL or has previously been allocatored with this `Allocator`. +let old_size =unsafe {libc_malloc_usable_size (src.cast ()) }; + +// SAFETY: `src` has previously been allocated with this `Allocator`; `dst` has just been +// newly allocated. Taking the minimum of their sizes guarantees that we do not exceed +// either bounds. +unsafe { +// Always copy -- do not rely on potential spare memory reported by +// malloc_usable_size() which technically may still be sufficient. + ptr::copy_nonoverlapping ( + src, + dst.as_ptr ().cast (), + core::cmp::min (layout.size (), old_size), + ) + }; + +Ok (dst) } }
Signature · 1 line
2.45.2
Re: [PATCH v4 25/28] rust: alloc: implement `Cmalloc` in module allocator_test
Quoted 67 lines · Danilo Krummrich
So far the kernel's
BoxandVectypes 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
Cmallocallocator within theallocator_testmodule and type alias all kernel allocators toCmalloc. TheCmallocallocator uses libc'srealloc()function as allocator backend.Signed-off-by Danilo Krummrich <[email protected]>---
I know, having anold_sizeparameter would indeed help implementingCmalloc.However, I really don't want test infrastructure to influence the design of kernel internal APIs.
Besides that, adding the
old_sizeparameter would have the consequence that we'd either need to honor it for kernel allocators too (which adds another source of potential failure) or ignore it for all kernel allocators (which potentially tricks people into taking wrong assumptions on how the API works).--- rust/kernel/alloc/allocator_test.rs | 91 ++++++++++++++++++++++++++--- 1 file changed, 84 insertions(+), 7 deletions(-)rust/kernel/alloc/allocator_test.rs +26
-3@@ -2,20 +2,97 @@#![allow(missing_docs)] -use super ::{AllocError ,Allocator ,Flags }; +use super ::{flags::*,AllocError ,Allocator ,Flags };use core::alloc::Layout ; +use core::ptr;use core::ptr::NonNull ; -pub struct Kmalloc ; +pub struct Cmalloc ; +pub type Kmalloc =Cmalloc ;pub type Vmalloc =Kmalloc ;pub type KVmalloc =Kmalloc ; -unsafe impl Allocator for Kmalloc { +extern "C" { +#[link_name = "aligned_alloc" ] +fn libc_aligned_alloc (align:usize , size:usize ) -> *mut core::ffi::c_void ; + +#[link_name = "free" ] +fn libc_free (ptr: *mut core::ffi::c_void ); + +// Do not use this function for production code! For test cases only it's +// probably fine if used with care. +#[link_name = "malloc_usable_size" ] +fn libc_malloc_usable_size (ptr: *mut core::ffi::c_void ) ->usize ; +} + +unsafe impl Allocator for Cmalloc { +fn alloc (layout:Layout , flags:Flags ) ->Result <NonNull <[u8 ]>,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 raw_ptr =unsafe {libc_aligned_alloc (layout.align (), layout.size ()) }as *mut u8 ; + +if flags.contains (__GFP_ZERO) && !raw_ptr.is_null () {
rust/kernel/alloc/allocator_test.rs line 46
Quoted 10 lines
rust/kernel/alloc/allocator_test.rs +10
-0+// SAFETY: `raw_ptr` points to memory successfully allocated with `libc_aligned_alloc`. +let size =unsafe {libc_malloc_usable_size (raw_ptr.cast ()) }; + +// SAFETY: `raw_ptr` points to memory successfully allocated with `libc_aligned_alloc` +// of at least `size` bytes. +unsafe { core::ptr::write_bytes (raw_ptr,0 , size) }; + } + +let ptr =if layout.size () ==0 { +NonNull ::dangling ()
Why do you call libc_aligned_alloc when you return dangling() anyways when size is zero? I would move this check upwards.
rust/kernel/alloc/allocator_test.rs line 48
Quoted 2 lines
rust/kernel/alloc/allocator_test.rs +2
-0+ }else { +NonNull ::new (raw_ptr).ok_or (AllocError )?
Would also make sense to do this above the null check.
Quoted 27 lines
rust/kernel/alloc/allocator_test.rs +21
-4+ }; + +Ok (NonNull ::slice_from_raw_parts (ptr, layout.size ())) + } +unsafe fn realloc ( - _ptr:Option <NonNull <u8 >>, - _layout:Layout , - _flags:Flags , + ptr:Option <NonNull <u8 >>, + layout:Layout , + flags:Flags , ) ->Result <NonNull <[u8 ]>,AllocError > { -panic! (); +let layout = layout.pad_to_align (); +let src: *mut u8 =if let Some (src) = ptr { + src.as_ptr ().cast () + }else { + ptr::null_mut () + }; + +if layout.size () ==0 { +// SAFETY: `src` is either NULL or has previously been allocatored with this +// `Allocator`. +unsafe {libc_free (src.cast ()) }; + +return Ok (NonNull ::slice_from_raw_parts (NonNull ::dangling (),0 ));
rust/kernel/alloc/allocator_test.rs line 81
Quoted 10 lines
rust/kernel/alloc/allocator_test.rs +10
-0+ } + +let dst =Self ::alloc (layout, flags)?; + +if src.is_null () { +return Ok (dst); + } + +// SAFETY: `src` is either NULL or has previously been allocatored with this `Allocator`. +let old_size =unsafe {libc_malloc_usable_size (src.cast ()) };
Citing man malloc_usable_size(3):
CAVEATS
The value returned by malloc_usable_size() may be greater than the requested size of the allocation because of various internal implementation details, none of which the programmer should rely on. This function is intended to only be used for diagnostics and statistics; writing to the excess memory without first calling realloc(3) to resize the allocation is not supported. The returned value is only valid at the time of the call.
While you don't write, you might read below, which might not be OK?
---
Cheers,
Benno
Quoted 20 lines
rust/kernel/alloc/allocator_test.rs +15
-1+ +// SAFETY: `src` has previously been allocated with this `Allocator`; `dst` has just been +// newly allocated. Taking the minimum of their sizes guarantees that we do not exceed +// either bounds. +unsafe { +// Always copy -- do not rely on potential spare memory reported by +// malloc_usable_size() which technically may still be sufficient. + ptr::copy_nonoverlapping ( + src, + dst.as_ptr ().cast (), + core::cmp::min (layout.size (), old_size), + ) + }; + +Ok (dst) } } --2.45.2
Danilo Krummrich
maintainerRe: [PATCH v4 25/28] rust: alloc: implement `Cmalloc` in module allocator_test
Quoted 136 lines · Benno Lossin
Quoted 67 lines · Danilo Krummrich
So far the kernel's
BoxandVectypes 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
Cmallocallocator within theallocator_testmodule and type alias all kernel allocators toCmalloc. TheCmallocallocator uses libc'srealloc()function as allocator backend.Signed-off-by Danilo Krummrich <[email protected]>---
I know, having anold_sizeparameter would indeed help implementingCmalloc.However, I really don't want test infrastructure to influence the design of kernel internal APIs.
Besides that, adding the
old_sizeparameter would have the consequence that we'd either need to honor it for kernel allocators too (which adds another source of potential failure) or ignore it for all kernel allocators (which potentially tricks people into taking wrong assumptions on how the API works).--- rust/kernel/alloc/allocator_test.rs | 91 ++++++++++++++++++++++++++--- 1 file changed, 84 insertions(+), 7 deletions(-)rust/kernel/alloc/allocator_test.rs +26
-3@@ -2,20 +2,97 @@#![allow(missing_docs)] -use super ::{AllocError ,Allocator ,Flags }; +use super ::{flags::*,AllocError ,Allocator ,Flags };use core::alloc::Layout ; +use core::ptr;use core::ptr::NonNull ; -pub struct Kmalloc ; +pub struct Cmalloc ; +pub type Kmalloc =Cmalloc ;pub type Vmalloc =Kmalloc ;pub type KVmalloc =Kmalloc ; -unsafe impl Allocator for Kmalloc { +extern "C" { +#[link_name = "aligned_alloc" ] +fn libc_aligned_alloc (align:usize , size:usize ) -> *mut core::ffi::c_void ; + +#[link_name = "free" ] +fn libc_free (ptr: *mut core::ffi::c_void ); + +// Do not use this function for production code! For test cases only it's +// probably fine if used with care. +#[link_name = "malloc_usable_size" ] +fn libc_malloc_usable_size (ptr: *mut core::ffi::c_void ) ->usize ; +} + +unsafe impl Allocator for Cmalloc { +fn alloc (layout:Layout , flags:Flags ) ->Result <NonNull <[u8 ]>,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 raw_ptr =unsafe {libc_aligned_alloc (layout.align (), layout.size ()) }as *mut u8 ; + +if flags.contains (__GFP_ZERO) && !raw_ptr.is_null () {rust/kernel/alloc/allocator_test.rs line 46
Quoted 10 lines
rust/kernel/alloc/allocator_test.rs +10
-0+// SAFETY: `raw_ptr` points to memory successfully allocated with `libc_aligned_alloc`. +let size =unsafe {libc_malloc_usable_size (raw_ptr.cast ()) }; + +// SAFETY: `raw_ptr` points to memory successfully allocated with `libc_aligned_alloc` +// of at least `size` bytes. +unsafe { core::ptr::write_bytes (raw_ptr,0 , size) }; + } + +let ptr =if layout.size () ==0 { +NonNull ::dangling ()Why do you call
libc_aligned_allocwhen you returndangling()anyways when size is zero? I would move this check upwards.rust/kernel/alloc/allocator_test.rs line 48
Quoted 2 lines
rust/kernel/alloc/allocator_test.rs +2
-0+ }else { +NonNull ::new (raw_ptr).ok_or (AllocError )?Would also make sense to do this above the null check.
Quoted 27 lines
rust/kernel/alloc/allocator_test.rs +21
-4+ }; + +Ok (NonNull ::slice_from_raw_parts (ptr, layout.size ())) + } +unsafe fn realloc ( - _ptr:Option <NonNull <u8 >>, - _layout:Layout , - _flags:Flags , + ptr:Option <NonNull <u8 >>, + layout:Layout , + flags:Flags , ) ->Result <NonNull <[u8 ]>,AllocError > { -panic! (); +let layout = layout.pad_to_align (); +let src: *mut u8 =if let Some (src) = ptr { + src.as_ptr ().cast () + }else { + ptr::null_mut () + }; + +if layout.size () ==0 { +// SAFETY: `src` is either NULL or has previously been allocatored with this +// `Allocator`. +unsafe {libc_free (src.cast ()) }; + +return Ok (NonNull ::slice_from_raw_parts (NonNull ::dangling (),0 ));rust/kernel/alloc/allocator_test.rs line 81
Quoted 10 lines
rust/kernel/alloc/allocator_test.rs +10
-0+ } + +let dst =Self ::alloc (layout, flags)?; + +if src.is_null () { +return Ok (dst); + } + +// SAFETY: `src` is either NULL or has previously been allocatored with this `Allocator`. +let old_size =unsafe {libc_malloc_usable_size (src.cast ()) };Citing man
malloc_usable_size(3):CAVEATS
The value returned bymalloc_usable_size()may be greater than the requested size of the allocation because of various internal implementation details, none of which the programmer should rely on. This function is intended to only be used for diagnostics and statistics; writing to the excess memory without first calling realloc(3) to resize the allocation is not supported. The returned value is only valid at the time of the call.
Quoted 1 line
While you don't write, you might read below, which might not be OK?
That is very interesting, the man page entry I looked at said:
The value returned by malloc_usable_size() may be greater than the requested size of the allocation because of alignment and minimum size constraints. Although the excess bytes can be overwritten by the application without ill effects, this is not good programming
practice: the number of excess bytes in an allocation depends on the underlying implementation.
This was changed in [1] and it looks like "can be overwritten by the application without ill effects" just isn't true any more, too bad.
I have to think about it a bit, what we could do instead. I also don't really want to have an old size parameter for realloc only because it's needed by test infrastructure.
Quoted 26 lines
---
Cheers,
BennoQuoted 20 lines
rust/kernel/alloc/allocator_test.rs +15
-1+ +// SAFETY: `src` has previously been allocated with this `Allocator`; `dst` has just been +// newly allocated. Taking the minimum of their sizes guarantees that we do not exceed +// either bounds. +unsafe { +// Always copy -- do not rely on potential spare memory reported by +// malloc_usable_size() which technically may still be sufficient. + ptr::copy_nonoverlapping ( + src, + dst.as_ptr ().cast (), + core::cmp::min (layout.size (), old_size), + ) + }; + +Ok (dst) } } --2.45.2
Danilo Krummrich
maintainerPATCH v4 26/28 rust: str: test: replace `alloc::format`
In reply to Danilo KrummrichThe current implementation of tests in str.rs use format! to format strings for comparison, which, internally, creates a new String.
In order to prepare for getting rid of Rust's alloc crate, we have to cut this dependency. Instead, implement format! for CString.
Note that for userspace tests, Kmalloc, which is backing CString's memory, is just a type alias to Cmalloc.
--- rust/kernel/str.rs | 23 ++++++++++++++++++++++- 1 file changed, 22 insertions(+), 1 deletion(-)
rust/kernel/str.rs +22 -1
@@ -523,7 +523,28 @@ macro_rules! c_str {#[cfg(test)] mod tests {use super ::*; -use alloc::format; + +struct String (CString ); + +impl String { +fn from_fmt (args: fmt::Arguments <'_>) ->Self { +String (CString ::try_from_fmt (args).unwrap ()) + } + } + +impl Deref for String { +type Target =str ; + +fn deref (&self) -> &str { + self.0 .to_str ().unwrap () + } + } + +macro_rules! format { + ($($f:tt)*) => ({ + &*String ::from_fmt (kernel::fmt!($($f)*)) + }) + }const ALL_ASCII_CHARS : &'staticstr ="\\x01\\x02\\x03\\x04\\x05\\x06\\x07\\x08\\x09\\x0a\\x0b\\x0c\\x0d\\x0e\\x0f\
Signature · 1 line
2.45.2
Re: [PATCH v4 26/28] rust: str: test: replace `alloc::format`
Quoted 11 lines · Danilo Krummrich
The current implementation of tests in
str.rsuseformat!to format strings for comparison, which, internally, creates a newString.In order to prepare for getting rid of Rust's alloc crate, we have to cut this dependency. Instead, implement
format!forCString.Note that for userspace tests,
Kmalloc, which is backingCString's memory, is just a type alias toCmalloc.Signed-off-by Danilo Krummrich <[email protected]>
Re: [PATCH v4 26/28] rust: str: test: replace `alloc::format`
In reply to Danilo KrummrichQuoted 29 lines · Danilo Krummrich
The current implementation of tests in
str.rsuseformat!to format strings for comparison, which, internally, creates a newString.In order to prepare for getting rid of Rust's alloc crate, we have to cut this dependency. Instead, implement
format!forCString.Note that for userspace tests,
Kmalloc, which is backingCString's memory, is just a type alias toCmalloc.Signed-off-by Danilo Krummrich <[email protected]>--- rust/kernel/str.rs | 23 ++++++++++++++++++++++- 1 file changed, 22 insertions(+), 1 deletion(-)rust/kernel/str.rs +6
-1@@ -523,7 +523,28 @@ macro_rules! c_str {#[cfg(test)] mod tests {use super ::*; -use alloc::format; + +struct String (CString ); + +impl String { +fn from_fmt (args: fmt::Arguments <'_>) ->Self { +String (CString ::try_from_fmt (args).unwrap ())
rust/kernel/str.rs macro_rules! c_str { · line 541
Quoted 10 lines
rust/kernel/str.rs +10
-0+ } + } + +impl Deref for String { +type Target =str ; + +fn deref (&self) -> &str { + self.0 .to_str ().unwrap () + } + }
Don't actually think we need this newtype.
rust/kernel/str.rs macro_rules! c_str { · line 545
Quoted 4 lines
rust/kernel/str.rs +4
-0+ +macro_rules! format { + ($($f:tt)*) => ({ + &*String ::from_fmt (kernel::fmt!($($f)*))
We could just do this:
CString::try_from_fmt(kernel::fmt!($(f)*)).unwrap().to_str().unwrap()
---
Cheers,
Benno
Quoted 8 lines
rust/kernel/str.rs +2
-1+ }) + }const ALL_ASCII_CHARS : &'staticstr ="\\x01\\x02\\x03\\x04\\x05\\x06\\x07\\x08\\x09\\x0a\\x0b\\x0c\\x0d\\x0e\\x0f\ --2.45.2
Danilo Krummrich
maintainerRe: [PATCH v4 26/28] rust: str: test: replace `alloc::format`
Quoted 34 lines · Benno Lossin
Quoted 33 lines · Danilo Krummrich
The current implementation of tests in
str.rsuseformat!to format strings for comparison, which, internally, creates a newString.In order to prepare for getting rid of Rust's alloc crate, we have to cut this dependency. Instead, implement
format!forCString.Note that for userspace tests,
Kmalloc, which is backingCString's memory, is just a type alias toCmalloc.Signed-off-by Danilo Krummrich <[email protected]>--- rust/kernel/str.rs | 23 ++++++++++++++++++++++- 1 file changed, 22 insertions(+), 1 deletion(-)rust/kernel/str.rs +10
-1@@ -523,7 +523,28 @@ macro_rules! c_str {#[cfg(test)] mod tests {use super ::*; -use alloc::format; + +struct String (CString ); + +impl String { +fn from_fmt (args: fmt::Arguments <'_>) ->Self { +String (CString ::try_from_fmt (args).unwrap ()) + } + } + +impl Deref for String {
Quoted 8 lines
rust/kernel/str.rs macro_rules! c_str { · line 541
Quoted 6 lines
rust/kernel/str.rs +6
-0+type Target =str ; + +fn deref (&self) -> &str { + self.0 .to_str ().unwrap () + } + }Don't actually think we need this newtype.
Right, but I thought it's a bit more extensible and cleaner than...
Quoted 9 lines
rust/kernel/str.rs macro_rules! c_str { · line 545
Quoted 5 lines
rust/kernel/str.rs +4
-0+ +macro_rules! format { + ($($f:tt)*) => ({ + &*String ::from_fmt (kernel::fmt!($($f)*))We could just do this:
CString::try_from_fmt(kernel::fmt!($(f)*)).unwrap().to_str().unwrap()
...doing it inline.
I'm fine either way, but unless you really want me to change it, I'd rather keep it as it is.
Quoted 14 lines
---
Cheers,
BennoQuoted 8 lines
rust/kernel/str.rs +2
-1+ }) + }const ALL_ASCII_CHARS : &'staticstr ="\\x01\\x02\\x03\\x04\\x05\\x06\\x07\\x08\\x09\\x0a\\x0b\\x0c\\x0d\\x0e\\x0f\ --2.45.2
Danilo Krummrich
maintainerPATCH v4 27/28 rust: alloc: update module comment of alloc.rs
In reply to Danilo KrummrichBefore we remove Rust's alloc crate, rewrite the module comment in alloc.rs to avoid a rustdoc warning.
Besides that, the module comment in alloc.rs isn't correct anymore, we're no longer extending Rust's alloc crate.
--- rust/kernel/alloc.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-)
rust/kernel/alloc.rs +1 -1
@@ -1,6 +1,6 @@// SPDX-License-Identifier: GPL-2.0 -//! Extensions to the [`alloc`] crate. +//! Implementation of the kernel's memory allocation infrastructure. #[cfg(not(any(test, testlib)))] pub mod allocator;
Signature · 1 line
2.45.2
Re: [PATCH v4 27/28] rust: alloc: update module comment of alloc.rs
Quoted 8 lines · Danilo Krummrich
Before we remove Rust's alloc crate, rewrite the module comment in
alloc.rsto avoid a rustdoc warning.Besides that, the module comment in
alloc.rsisn't correct anymore, we're no longer extending Rust's alloc crate.Signed-off-by Danilo Krummrich <[email protected]>
Re: [PATCH v4 27/28] rust: alloc: update module comment of alloc.rs
In reply to Danilo KrummrichQuoted 10 lines · Danilo Krummrich
Before we remove Rust's alloc crate, rewrite the module comment in
alloc.rsto avoid a rustdoc warning.Besides that, the module comment in
alloc.rsisn't correct anymore, we're no longer extending Rust's alloc crate.Signed-off-by Danilo Krummrich <[email protected]>--- rust/kernel/alloc.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-)
---
Cheers,
Benno
Danilo Krummrich
maintainerPATCH v4 28/28 kbuild: rust: remove the `alloc` crate
In reply to Danilo KrummrichNow that we have our own Allocator, Box and Vec types we can remove Rust's alloc crate and the corresponding unstable features.
--- rust/Makefile | 44 ++++++++++-------------------------------- rust/exports.c | 1 - scripts/Makefile.build | 7 +------ 3 files changed, 11 insertions(+), 41 deletions(-)
rust/Makefile +10 -34
@@ -15,9 +15,8 @@ always-$(CONFIG_RUST) += libmacros.so no-clean-files += libmacros.so always-$(CONFIG_RUST) += bindings/bindings_generated.rs bindings/bindings_helpers_generated.rs -obj-$(CONFIG_RUST) += alloc.o bindings.o kernel.o -always-$(CONFIG_RUST) += exports_alloc_generated.h exports_bindings_generated.h \ - exports_kernel_generated.h +obj-$(CONFIG_RUST) += bindings.o kernel.o +always-$(CONFIG_RUST) += exports_bindings_generated.h exports_kernel_generated.h always-$(CONFIG_RUST) += uapi/uapi_generated.rs obj-$(CONFIG_RUST) += uapi.o
@@ -53,11 +52,6 @@ endif core-cfgs = \ --cfg no_fp_fmt_parse -alloc-cfgs = \ - --cfg no_global_oom_handling \ - --cfg no_rc \ - --cfg no_sync - quiet_cmd_rustdoc = RUSTDOC $(if $(rustdoc_host),H, ) $< cmd_rustdoc = \ OBJTREE=$(abspath $(objtree)) \
@@ -80,7 +74,7 @@ quiet_cmd_rustdoc = RUSTDOC $(if $(rustdoc_host),H, ) $< # command-like flags to solve the issue. Meanwhile, we use the non-custom case # and then retouch the generated files. rustdoc: rustdoc-core rustdoc-macros rustdoc-compiler_builtins \ - rustdoc-alloc rustdoc-kernel + rustdoc-kernel $(Q)cp $(srctree)/Documentation/images/logo.svg $(rustdoc_output)/static.files/ $(Q)cp $(srctree)/Documentation/images/COPYING-logo $(rustdoc_output)/static.files/ $(Q)find $(rustdoc_output) -name '*.html' -type f -print0 | xargs -0 sed -Ei \
@@ -104,20 +98,11 @@ rustdoc-core: $(RUST_LIB_SRC)/core/src/lib.rs FORCE rustdoc-compiler_builtins: $(src)/compiler_builtins.rs rustdoc-core FORCE +$(call if_changed,rustdoc) -# We need to allow `rustdoc::broken_intra_doc_links` because some -# `no_global_oom_handling` functions refer to non-`no_global_oom_handling` -# functions. Ideally `rustdoc` would have a way to distinguish broken links -# due to things that are "configured out" vs. entirely non-existing ones. -rustdoc-alloc: private rustc_target_flags = $(alloc-cfgs) \ - -Arustdoc::broken_intra_doc_links -rustdoc-alloc: $(RUST_LIB_SRC)/alloc/src/lib.rs rustdoc-core rustdoc-compiler_builtins FORCE - +$(call if_changed,rustdoc) - -rustdoc-kernel: private rustc_target_flags = --extern alloc \ +rustdoc-kernel: private rustc_target_flags = \ --extern build_error --extern macros=$(objtree)/$(obj)/libmacros.so \ --extern bindings --extern uapi rustdoc-kernel: $(src)/kernel/lib.rs rustdoc-core rustdoc-macros \ - rustdoc-compiler_builtins rustdoc-alloc $(obj)/libmacros.so \ + rustdoc-compiler_builtins $(obj)/libmacros.so \ $(obj)/bindings.o FORCE +$(call if_changed,rustdoc)
@@ -161,7 +146,7 @@ quiet_cmd_rustdoc_test_kernel = RUSTDOC TK $< mkdir -p $(objtree)/$(obj)/test/doctests/kernel; \ OBJTREE=$(abspath $(objtree)) \ $(RUSTDOC) --test $(rust_flags) \ - -L$(objtree)/$(obj) --extern alloc --extern kernel \ + -L$(objtree)/$(obj) --extern kernel \ --extern build_error --extern macros \ --extern bindings --extern uapi \ --no-run --crate-name kernel -Zunstable-options \
@@ -197,7 +182,7 @@ rusttest-macros: $(src)/macros/lib.rs FORCE +$(call if_changed,rustc_test) +$(call if_changed,rustdoc_test) -rusttest-kernel: private rustc_target_flags = --extern alloc \ +rusttest-kernel: private rustc_target_flags = \ --extern build_error --extern macros --extern bindings --extern uapi rusttest-kernel: $(src)/kernel/lib.rs \ rusttestlib-build_error rusttestlib-macros rusttestlib-bindings \
@@ -310,9 +295,6 @@ quiet_cmd_exports = EXPORTS $@ $(obj)/exports_core_generated.h: $(obj)/core.o FORCE $(call if_changed,exports) -$(obj)/exports_alloc_generated.h: $(obj)/alloc.o FORCE - $(call if_changed,exports) - $(obj)/exports_bindings_generated.h: $(obj)/bindings.o FORCE $(call if_changed,exports)
@@ -348,7 +330,7 @@ quiet_cmd_rustc_library = $(if $(skip_clippy),RUSTC,$(RUSTC_OR_CLIPPY_QUIET)) L rust-analyzer: $(Q)$(srctree)/scripts/generate_rust_analyzer.py \ - --cfgs='core=$(core-cfgs)' --cfgs='alloc=$(alloc-cfgs)' \ + --cfgs='core=$(core-cfgs)' \ $(realpath $(srctree)) $(realpath $(objtree)) \ $(RUST_LIB_SRC) $(KBUILD_EXTMOD) > \ $(if $(KBUILD_EXTMOD),$(extmod_prefix),$(objtree))/rust-project.json
@@ -380,12 +362,6 @@ $(obj)/compiler_builtins.o: private rustc_objcopy = -w -W '__*' $(obj)/compiler_builtins.o: $(src)/compiler_builtins.rs $(obj)/core.o FORCE +$(call if_changed_dep,rustc_library) -$(obj)/alloc.o: private skip_clippy = 1 -$(obj)/alloc.o: private skip_flags = -Wunreachable_pub -$(obj)/alloc.o: private rustc_target_flags = $(alloc-cfgs) -$(obj)/alloc.o: $(RUST_LIB_SRC)/alloc/src/lib.rs $(obj)/compiler_builtins.o FORCE - +$(call if_changed_dep,rustc_library) - $(obj)/build_error.o: $(src)/build_error.rs $(obj)/compiler_builtins.o FORCE +$(call if_changed_dep,rustc_library)
@@ -400,9 +376,9 @@ $(obj)/uapi.o: $(src)/uapi/lib.rs \ $(obj)/uapi/uapi_generated.rs FORCE +$(call if_changed_dep,rustc_library) -$(obj)/kernel.o: private rustc_target_flags = --extern alloc \ +$(obj)/kernel.o: private rustc_target_flags = \ --extern build_error --extern macros --extern bindings --extern uapi -$(obj)/kernel.o: $(src)/kernel/lib.rs $(obj)/alloc.o $(obj)/build_error.o \ +$(obj)/kernel.o: $(src)/kernel/lib.rs $(obj)/build_error.o \ $(obj)/libmacros.so $(obj)/bindings.o $(obj)/uapi.o FORCE +$(call if_changed_dep,rustc_library)
rust/exports.c +0 -1
@@ -16,7 +16,6 @@#define EXPORT_SYMBOL_RUST_GPL (sym) extern int sym; EXPORT_SYMBOL_GPL(sym)#include "exports_core_generated.h" -#include "exports_alloc_generated.h" #include "exports_bindings_generated.h" #include "exports_kernel_generated.h"
scripts/Makefile.build +1 -6
@@ -262,18 +262,13 @@ $(obj)/%.lst: $(obj)/%.c FORCE # Compile Rust sources (.rs) # --------------------------------------------------------------------------- - -rust_allowed_features := new_uninit - # `--out-dir` is required to avoid temporaries being created by `rustc` in the # current working directory, which may be not accessible in the out-of-tree # modules case. rust_common_cmd = \ RUST_MODFILE=$(modfile) $(RUSTC_OR_CLIPPY) $(rust_flags) \ - -Zallow-features=$(rust_allowed_features) \ -Zcrate-attr=no_std \ - -Zcrate-attr='feature($(rust_allowed_features))' \ - -Zunstable-options --extern force:alloc --extern kernel \ + -Zunstable-options --extern kernel \ --crate-type rlib -L $(objtree)/rust/ \ --crate-name $(basename $(notdir $@)) \ --sysroot=/dev/null \
Signature · 1 line
2.45.2
Re: [PATCH v4 28/28] kbuild: rust: remove the `alloc` crate
Quoted 2 lines · Danilo Krummrich
Now that we have our own
Allocator,BoxandVectypes we can remove Rust'salloccrate and the corresponding unstable features.
Would be great if you could include the list of unstable features that are now no longer needed here.
---
Cheers,
Benno
Quoted 6 lines
Signed-off-by Danilo Krummrich <[email protected]>--- rust/Makefile | 44 ++++++++++-------------------------------- rust/exports.c | 1 - scripts/Makefile.build | 7 +------ 3 files changed, 11 insertions(+), 41 deletions(-)
Re: [PATCH v4 28/28] kbuild: rust: remove the `alloc` crate
In reply to Danilo KrummrichQuoted 2 lines · Danilo Krummrich
Now that we have our own
Allocator,BoxandVectypes we can remove Rust'salloccrate and the corresponding unstable features.
Should this patch also remove alloc from scripts/generate_rust_analyzer?
Or do we need that when editing our macros crate (since it depends on std)?
---
Cheers,
Benno
Quoted 7 lines
Signed-off-by Danilo Krummrich <[email protected]>--- rust/Makefile | 44 ++++++++++-------------------------------- rust/exports.c | 1 - scripts/Makefile.build | 7 +------ 3 files changed, 11 insertions(+), 41 deletions(-)