Generic `Allocator` support for Rust
Subsystems (9)
Danilo Krummrich
maintainerv5 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 v5:
- (safety) comment / documentation fixes suggested by Alice, Benno and Gary
- remove Unique<T> and implement Send and Sync for Box and Vec
- use KMALLOC_MAX_SIZE for KVmalloc test and add a Kmalloc test that expects to fail for KMALLOC_MAX_SIZE
- create use constants KREALLOC, VREALLOC and KVREALLOC for ReallocFuncs
- drop Box::drop_contents for now, will add it again, once I actually rebase on the original patch that introduces it
- improve usage of size_of_val in Box
- move InPlaceInit and ForeignOwnable impls into kbox.rs
- fix missing Box conversions in rnull.rs
- reworked Cmalloc to keep track of the size of memory allocations itself
- remove GlobalAlloc together with the alloc crate to avoid a linker error
- remove alloc from scripts/generate_rust_analyzer.py
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 (26):
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: alloc: add __GFP_NOWARN to Flags
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: 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: 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 and GlobalAlloc
MAINTAINERS: add entry for the Rust alloc module
MAINTAINERS | 7 + drivers/block/rnull.rs | 4 +- rust/Makefile | 44 +- rust/bindings/bindings_helper.h | 1 + rust/exports.c | 1 - rust/helpers.c | 16 +- rust/kernel/alloc.rs | 121 +++- rust/kernel/alloc/allocator.rs | 156 +++-- rust/kernel/alloc/allocator_test.rs | 182 ++++++ rust/kernel/alloc/box_ext.rs | 56 -- rust/kernel/alloc/kbox.rs | 437 ++++++++++++++ rust/kernel/alloc/kvec.rs | 875 ++++++++++++++++++++++++++++ rust/kernel/alloc/vec_ext.rs | 185 ------ rust/kernel/error.rs | 6 +- rust/kernel/init.rs | 83 +-- 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 | 25 +- rust/kernel/uaccess.rs | 17 +- rust/kernel/workqueue.rs | 20 +- rust/macros/lib.rs | 12 +- samples/rust/rust_minimal.rs | 4 +- scripts/Makefile.build | 7 +- scripts/generate_rust_analyzer.py | 11 +- 31 files changed, 1853 insertions(+), 487 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 v5 1/26 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 | 81 ++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 81 insertions(+)
rust/kernel/alloc.rs +81 -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,83 @@ 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. +/// +/// If `realloc` is called with: +/// - a size of zero, the given memory allocation, if any, must be freed +/// - `None`, 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 `None`. +fn alloc (layout:Layout , flags:Flags ) ->Result <NonNull <[u8 ]>,AllocError > { +// SAFETY: Passing `None` 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 +/// +/// If `ptr = Some(p)`, then `p` must point to an existing and valid memory allocation created +/// by this allocator. 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` and +/// must not be a dangling pointer. +/// +/// The memory allocation at `ptr` must never again be read from or written to. +unsafe fn free (ptr:NonNull <u8 >) { +// 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. +let _ =unsafe {Self ::realloc (Some (ptr),Layout ::new ::<()>(),Flags (0 )) }; + } +}
Signature · 1 line
2.45.2
Re: [PATCH v5 01/26] rust: alloc: add `Allocator` trait
Quoted 36 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.Reviewed-by Alice Ryhl <[email protected]>Signed-off-by Danilo Krummrich <[email protected]>--- rust/kernel/alloc.rs | 81 ++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 81 insertions(+)rust/kernel/alloc.rs +8
-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,83 @@ 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
rust/kernel/alloc.rs pub mod flags { · line 106
Quoted 10 lines
rust/kernel/alloc.rs +10
-0+/// 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.
I wouldn't say that an allocator "returns memory", and in general I don't think the structure of the safety comment here is nice, how about the following: we put "Implementers must ensure that all trait functions abide by the guarantees documented in the # Guarantees sections."...
Quoted 4 lines
rust/kernel/alloc.rs +4
-0+/// +/// 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. +///
rust/kernel/alloc.rs pub mod flags { · line 120
Quoted 10 lines
rust/kernel/alloc.rs +10
-0+/// If `realloc` is called with: +/// - a size of zero, the given memory allocation, if any, must be freed +/// - `None`, 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 `None`.
... Then we can add this here:
/// # Guarantees
///
/// When the return value is `Ok(ptr)`, then `ptr` is
/// - valid for writes (and reads after the memory has been initialized) for `layout.size()` bytes,
/// until it is passed to [`Allocator::free`] or [`Allocator::realloc`],
/// - aligned to `layout.align()`,
/// - is valid for reads, if `flags.contains(flags::__GFP_ZERO)`,Do we need to handle other flags?
Also IIRC the memory given to us by C is considered initialized by Rust (though it has a non-deterministic value), so we might have an unconditional "valid for reads". Am I correct?
rust/kernel/alloc.rs pub mod flags { · line 128
Quoted 8 lines
rust/kernel/alloc.rs +8
-0+fn alloc (layout:Layout , flags:Flags ) ->Result <NonNull <[u8 ]>,AllocError > { +// SAFETY: Passing `None` 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`.
I don't think we want to include the second sentence in the short description of this function, please add an empty line in between.
Quoted 5 lines
rust/kernel/alloc.rs +5
-0+/// +/// 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 +/// +/// If `ptr = Some(p)`, then `p` must point to an existing and valid memory allocation created
I don't like the single = (I might have written it in haste myself), how about == or we use if-let syntax?
rust/kernel/alloc.rs pub mod flags { · line 148
Quoted 5 lines
rust/kernel/alloc.rs +5
-0+/// by this allocator. 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.
This Safety section does not talk about the case layout.size() == 0, but it should have the same requirement as free().
Also add a # Guarantees section here:
/// # Guarantees
///
/// This function has the same guarantees as [`Allocator::alloc`]. When `ptr == Some(p)`, then it
/// additionally has the following:
/// - when `Ok(ret_ptr)` is the return value, then
/// `ret_ptr[0..min(layout.size(), old_size)] == p[0..min(layout.size(), old_size)]`, where
/// `old_size` is the size of the allocation that `p` points at.
/// - when the return value is `Err(AllocError)`, then `p` is still valid.---
Cheers,
Benno
Quoted 25 lines
rust/kernel/alloc.rs +21
-1+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` and +/// must not be a dangling pointer. +/// +/// The memory allocation at `ptr` must never again be read from or written to. +unsafe fn free (ptr:NonNull <u8 >) { +// 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. +let _ =unsafe {Self ::realloc (Some (ptr),Layout ::new ::<()>(),Flags (0 )) }; + } +} --2.45.2
Danilo Krummrich
maintainerRe: [PATCH v5 01/26] rust: alloc: add `Allocator` trait
Quoted 44 lines · Benno Lossin
Quoted 43 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.Reviewed-by Alice Ryhl <[email protected]>Signed-off-by Danilo Krummrich <[email protected]>--- rust/kernel/alloc.rs | 81 ++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 81 insertions(+)rust/kernel/alloc.rs +15
-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,83 @@ 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
Quoted 8 lines
rust/kernel/alloc.rs pub mod flags { · line 106
Quoted 3 lines
rust/kernel/alloc.rs +3
-0+/// +/// Memory returned from an allocator must point to a valid memory buffer and remain valid until +/// it is explicitly freed. I wouldn't say that an allocator "returns memory", and in general I don't think the structure of the safety comment here is nice, how about the following: we put "Implementers must ensure that all trait functions abide by the guarantees documented in the
# Guaranteessections."...
Sounds reasonable to me. Additionally, I'd still keep the part below, that says that any pointer to a memory allocation must bbe valid to be passed to any other [Allocator] function of the same type.
Quoted 26 lines
Quoted 5 lines
rust/kernel/alloc.rs +4
-0+/// +/// 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. +/// rust/kernel/alloc.rs pub mod flags { · line 120
Quoted 10 lines
rust/kernel/alloc.rs +10
-0+/// If `realloc` is called with: +/// - a size of zero, the given memory allocation, if any, must be freed +/// - `None`, 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 `None`. ... Then we can add this here:
/// # Guarantees /// /// When the return value is `Ok(ptr)`, then `ptr` is /// - valid for writes (and reads after the memory has been initialized) for `layout.size()` bytes, /// until it is passed to [`Allocator::free`] or [`Allocator::realloc`], /// - aligned to `layout.align()`, /// - is valid for reads, if `flags.contains(flags::__GFP_ZERO)`,
Quoted 1 line
Do we need to handle other flags?
The whole flags thing is a bit difficult to represent here properly.
Theoretically, we'd need to add that it guarantees that the memory is zeroed for __GFP_ZERO, non-blocking for GFP_NOWAIT, etc. But, I think we shouldn't re-iterate all different behavior for the different flags.
Another inconvenience is that not all page flags are honored or make sense for all allocators. This is especially inconvenient for KVmalloc where we can't even say if we end up in vrealloc() or krealloc(). kvmalloc() even contains a couple of flag fixups for this reason [2].
I think we should just point to [1], which should document everything already.
[1] https://elixir.bootlin.com/linux/v6.10.4/source/include/linux/gfp_types.h
[2] https://elixir.bootlin.com/linux/v6.10.4/source/mm/util.c#L612
Quoted 3 lines
Also IIRC the memory given to us by C is considered initialized by Rust (though it has a non-deterministic value), so we might have an unconditional "valid for reads". Am I correct?
Yes, but as you say, unless allocated with __GFP_ZERO, it contains non-deterministic data. It may even contain old data from previous allocations.
Quoted 44 lines
rust/kernel/alloc.rs pub mod flags { · line 128
Quoted 10 lines
rust/kernel/alloc.rs +8
-0+fn alloc (layout:Layout , flags:Flags ) ->Result <NonNull <[u8 ]>,AllocError > { +// SAFETY: Passing `None` 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`. I don't think we want to include the second sentence in the short description of this function, please add an empty line in between.
Quoted 5 lines
rust/kernel/alloc.rs +5
-0+/// +/// 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 +/// +/// If `ptr = Some(p)`, then `p` must point to an existing and valid memory allocation created I don't like the single
=(I might have written it in haste myself), how about==or we use if-let syntax?rust/kernel/alloc.rs pub mod flags { · line 148
Quoted 5 lines
rust/kernel/alloc.rs +5
-0+/// by this allocator. 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. This Safety section does not talk about the case
layout.size() == 0, but it should have the same requirement asfree().Also add a
# Guaranteessection here:
Quoted 7 lines
/// # Guarantees
///
/// This function has the same guarantees as [`Allocator::alloc`]. When `ptr == Some(p)`, then it
/// additionally has the following:
/// - when `Ok(ret_ptr)` is the return value, then
/// `ret_ptr[0..min(layout.size(), old_size)] == p[0..min(layout.size(), old_size)]`, where
/// `old_size` is the size of the allocation that `p` points at.We could also say "The contents of the memory pointed to by p are preserved up to the lesser of the new and old size." But I'm fine with both.
Quoted 32 lines
/// - when the return value is `Err(AllocError)`, then `p` is still valid.---
Cheers,
BennoQuoted 25 lines
rust/kernel/alloc.rs +21
-1+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` and +/// must not be a dangling pointer. +/// +/// The memory allocation at `ptr` must never again be read from or written to. +unsafe fn free (ptr:NonNull <u8 >) { +// 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. +let _ =unsafe {Self ::realloc (Some (ptr),Layout ::new ::<()>(),Flags (0 )) }; + } +} --2.45.2
Re: [PATCH v5 01/26] rust: alloc: add `Allocator` trait
Quoted 12 lines · Danilo Krummrich
Quoted 10 lines · Benno Lossin
rust/kernel/alloc.rs pub mod flags { · line 106
Quoted 4 lines · Danilo Krummrich
rust/kernel/alloc.rs +4
-0+/// # Safety +/// +/// Memory returned from an allocator must point to a valid memory buffer and remain valid until +/// it is explicitly freed. I wouldn't say that an allocator "returns memory", and in general I don't think the structure of the safety comment here is nice, how about the following: we put "Implementers must ensure that all trait functions abide by the guarantees documented in the
# Guaranteessections."...
Quoted 3 lines
Sounds reasonable to me. Additionally, I'd still keep the part below, that says that any pointer to a memory allocation must bbe valid to be passed to any other [
Allocator] function of the same type.
Yes of course, that should be kept.
Quoted 7 lines
Quoted 7 lines
rust/kernel/alloc.rs pub mod flags { · line 113
Quoted 7 lines
rust/kernel/alloc.rs +7
-0+/// +/// 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. +/// +/// If `realloc` is called with: +/// - a size of zero, the given memory allocation, if any, must be freed +/// - `None`, a new memory allocation must be created
Only this list should be moved.
Quoted 18 lines
Quoted 18 lines
rust/kernel/alloc.rs pub mod flags { · line 120
Quoted 7 lines
rust/kernel/alloc.rs +7
-0+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 `None`. ... Then we can add this here:
/// # Guarantees /// /// When the return value is `Ok(ptr)`, then `ptr` is /// - valid for writes (and reads after the memory has been initialized) for `layout.size()` bytes, /// until it is passed to [`Allocator::free`] or [`Allocator::realloc`], /// - aligned to `layout.align()`, /// - is valid for reads, if `flags.contains(flags::__GFP_ZERO)`,
Quoted 7 lines
Quoted 1 line
Do we need to handle other flags?
The whole flags thing is a bit difficult to represent here properly.
Theoretically, we'd need to add that it guarantees that the memory is zeroed for
__GFP_ZERO, non-blocking forGFP_NOWAIT, etc. But, I think we shouldn't re-iterate all different behavior for the different flags.
If there are good docs, then link them.
Quoted 4 lines
Another inconvenience is that not all page flags are honored or make sense for all allocators. This is especially inconvenient for
KVmallocwhere we can't even say if we end up invrealloc()orkrealloc().kvmalloc()even contains a couple of flag fixups for this reason [2].
I am wondering if we want to encode this in the type system...
Quoted 11 lines
I think we should just point to [1], which should document everything already.
[1] https://elixir.bootlin.com/linux/v6.10.4/source/include/linux/gfp_types.h
[2] https://elixir.bootlin.com/linux/v6.10.4/source/mm/util.c#L612Quoted 3 lines
Also IIRC the memory given to us by C is considered initialized by Rust (though it has a non-deterministic value), so we might have an unconditional "valid for reads". Am I correct?
Yes, but as you say, unless allocated with
__GFP_ZERO, it contains non-deterministic data. It may even contain old data from previous allocations.
Yeah, but IIRC that is not a soundness issue. So memory coming from C is always considered initialized by Rust. Whereas MaybeUninit::uninit().assume_init() is insta-UB, reading such memory should be fine (just not useful).
Quoted 19 lines
Quoted 11 lines
rust/kernel/alloc.rs pub mod flags { · line 148
Quoted 5 lines
rust/kernel/alloc.rs +5
-0+/// by this allocator. 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. This Safety section does not talk about the case
layout.size() == 0, but it should have the same requirement asfree().Also add a
# Guaranteessection here:Quoted 7 lines
/// # Guarantees /// /// This function has the same guarantees as [`Allocator::alloc`]. When `ptr == Some(p)`, then it /// additionally has the following: /// - when `Ok(ret_ptr)` is the return value, then /// `ret_ptr[0..min(layout.size(), old_size)] == p[0..min(layout.size(), old_size)]`, where /// `old_size` is the size of the allocation that `p` points at.
Quoted 2 lines
We could also say "The contents of the memory pointed to by
pare preserved up to the lesser of the new and old size." But I'm fine with both.
I can read and write the math-ish syntax better, so I would prefer that over words. If others think we should use words, then we can discuss.
---
Cheers,
Benno
Danilo Krummrich
maintainerPATCH v5 2/26 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 | 19 ++++++++++++------- 1 file changed, 12 insertions(+), 7 deletions(-)
rust/kernel/alloc/allocator.rs +12 -7
@@ -8,6 +8,17 @@struct KernelAllocator ; +/// 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). + layout.size () +} +/// Calls `krealloc` with a proper size to alloc a new object aligned to `new_layout`'s alignment. /// /// # Safety
@@ -15,13 +26,7 @@/// - `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 { -// 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 (); +let size =aligned_size (new_layout);// SAFETY: // - `ptr` is either null or a pointer returned from a previous `k{re}alloc()` by the
Signature · 1 line
2.45.2
Danilo Krummrich
maintainerPATCH v5 3/26 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 {
@@ -36,7 +36,7 @@ pub(crate) unsafe fn krealloc_aligned(ptr: *mut u8, new_layout: Layout, flags: Funsafe { bindings::krealloc (ptras *const core::ffi::c_void , size, flags.0 )as *mut u8 } } -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.
@@ -72,7 +72,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
Danilo Krummrich
maintainerPATCH v5 4/26 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 | 63 +++++++++++++++++++++++++++++++++- 3 files changed, 64 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 +62 -1
76 lines not on this page · raw message
Signature · 1 line
2.45.2
Alice Ryhl
maintainerRe: [PATCH v5 04/26] rust: alloc: implement `Allocator` for `Kmalloc`
Quoted 21 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 | 63 +++++++++++++++++++++++++++++++++- 3 files changed, 64 insertions(+), 4 deletions(-)rust/helpers.c +0
-0
rust/helpers.c void rust_helper_init_work_with_key(struct work_struct *work, work_func_t func, · line 199
Quoted 9 lines
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); }
Why are the various annotations on this helper being removed? This deserves an explanation in the commit message.
Alice
Danilo Krummrich
maintainerRe: [PATCH v5 04/26] rust: alloc: implement `Allocator` for `Kmalloc`
Quoted 25 lines · Alice Ryhl
Quoted 24 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 | 63 +++++++++++++++++++++++++++++++++- 3 files changed, 64 insertions(+), 4 deletions(-)rust/helpers.c +0
-0@@ -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);
Quoted 8 lines
rust/helpers.c void rust_helper_init_work_with_key(struct work_struct *work, work_func_t func, · line 199
Quoted 6 lines
rust/helpers.c +1
-2-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); }Why are the various annotations on this helper being removed?
rust_helper_krealloc() is only called from Rust, hence neither __must_check nor __realloc_size() should have any effect.
I also do not apply them in subsequent commits for the vrealloc() and kvrealloc() helpers for this reason and removed them here for consistency.
Quoted 1 line
This deserves an explanation in the commit message.
I can also add a separate commit for that.
Quoted 3 lines
Alice
Alice Ryhl
maintainerRe: [PATCH v5 04/26] rust: alloc: implement `Allocator` for `Kmalloc`
Quoted 39 lines · Danilo Krummrich
Quoted 25 lines · Alice Ryhl
Quoted 24 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 | 63 +++++++++++++++++++++++++++++++++- 3 files changed, 64 insertions(+), 4 deletions(-)rust/helpers.c +0
-0@@ -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);Quoted 8 lines
rust/helpers.c void rust_helper_init_work_with_key(struct work_struct *work, work_func_t func, · line 199
Quoted 6 lines
rust/helpers.c +1
-2-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); }Why are the various annotations on this helper being removed?
rust_helper_krealloc()is only called from Rust, hence neither__must_checknor__realloc_size()should have any effect.
Quoted 6 lines
I also do not apply them in subsequent commits for the
vrealloc()andkvrealloc()helpers for this reason and removed them here for consistency.Quoted 1 line
This deserves an explanation in the commit message.
I can also add a separate commit for that.
I think your change would be more obviously correct if you keep them.
Alice
Danilo Krummrich
maintainerRe: [PATCH v5 04/26] rust: alloc: implement `Allocator` for `Kmalloc`
Quoted 40 lines · Alice Ryhl
Quoted 39 lines · Danilo Krummrich
Quoted 25 lines · Alice Ryhl
Quoted 24 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 | 63 +++++++++++++++++++++++++++++++++- 3 files changed, 64 insertions(+), 4 deletions(-)rust/helpers.c +0
-0@@ -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);Quoted 8 lines
rust/helpers.c void rust_helper_init_work_with_key(struct work_struct *work, work_func_t func, · line 199
Quoted 6 lines
rust/helpers.c +1
-2-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); }Why are the various annotations on this helper being removed?
rust_helper_krealloc()is only called from Rust, hence neither__must_checknor__realloc_size()should have any effect.
Quoted 8 lines
Quoted 6 lines
I also do not apply them in subsequent commits for the
vrealloc()andkvrealloc()helpers for this reason and removed them here for consistency.Quoted 1 line
This deserves an explanation in the commit message.
I can also add a separate commit for that.
I think your change would be more obviously correct if you keep them.
As in generally, or just for this patch?
Generally, I don't think we should indicate compiler checks that actually are never done.
For this patch, yes, it's probably better to separate it.
Quoted 3 lines
Alice
Alice Ryhl
maintainerRe: [PATCH v5 04/26] rust: alloc: implement `Allocator` for `Kmalloc`
Quoted 49 lines · Danilo Krummrich
Quoted 47 lines · Alice Ryhl
Quoted 45 lines · Danilo Krummrich
Quoted 25 lines · Alice Ryhl
Quoted 24 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 | 63 +++++++++++++++++++++++++++++++++- 3 files changed, 64 insertions(+), 4 deletions(-)rust/helpers.c +0
-0@@ -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);Quoted 8 lines
rust/helpers.c void rust_helper_init_work_with_key(struct work_struct *work, work_func_t func, · line 199
Quoted 6 lines
rust/helpers.c +1
-2-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); }Why are the various annotations on this helper being removed?
rust_helper_krealloc()is only called from Rust, hence neither__must_checknor__realloc_size()should have any effect.I also do not apply them in subsequent commits for the
vrealloc()andkvrealloc()helpers for this reason and removed them here for consistency.Quoted 1 line
This deserves an explanation in the commit message.
I can also add a separate commit for that.
Quoted 8 lines
Quoted 1 line
I think your change would be more obviously correct if you keep them.
As in generally, or just for this patch?
Generally, I don't think we should indicate compiler checks that actually are never done.
For this patch, yes, it's probably better to separate it.
In general. If you keep it, then I don't have to think about whether it affects bindgen's output. That's the main reason.
Alice
Danilo Krummrich
maintainerRe: [PATCH v5 04/26] rust: alloc: implement `Allocator` for `Kmalloc`
Quoted 54 lines · Alice Ryhl
Quoted 53 lines · Danilo Krummrich
Quoted 40 lines · Alice Ryhl
Quoted 39 lines · Danilo Krummrich
Quoted 25 lines · Alice Ryhl
Quoted 24 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 | 63 +++++++++++++++++++++++++++++++++- 3 files changed, 64 insertions(+), 4 deletions(-)rust/helpers.c +0
-0@@ -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);Quoted 8 lines
rust/helpers.c void rust_helper_init_work_with_key(struct work_struct *work, work_func_t func, · line 199
Quoted 6 lines
rust/helpers.c +1
-2-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); }Why are the various annotations on this helper being removed?
rust_helper_krealloc()is only called from Rust, hence neither__must_checknor__realloc_size()should have any effect.Quoted 8 lines
Quoted 6 lines
I also do not apply them in subsequent commits for the
vrealloc()andkvrealloc()helpers for this reason and removed them here for consistency.Quoted 1 line
This deserves an explanation in the commit message.
I can also add a separate commit for that.
I think your change would be more obviously correct if you keep them.
As in generally, or just for this patch?
Quoted 7 lines
Quoted 4 lines
Generally, I don't think we should indicate compiler checks that actually are never done.
For this patch, yes, it's probably better to separate it.
In general. If you keep it, then I don't have to think about whether it affects bindgen's output. That's the main reason.
Well, it doesn't.
If we keep them, we'd consequently also need to add them for vrealloc() and kvrealloc(). But again, they don't do anything for us, and hence are more misleading than helpful IMO.
Quoted 3 lines
Alice
Re: [PATCH v5 04/26] rust: alloc: implement `Allocator` for `Kmalloc`
Quoted 4 lines · Danilo Krummrich
If we keep them, we'd consequently also need to add them for
vrealloc()andkvrealloc(). But again, they don't do anything for us, and hence are more misleading than helpful IMO.
In general, they could do something (e.g. noreturn), perhaps in the future.
Apart from being potentially misleading, do we gain something by removing them? I guess simplicity in the file, but it is also simpler to keep them aligned to the C side (which I guess is Alice's point), and avoids having to keep track of what could have a present or future impact in bindgen.
Cheers,
Miguel
Danilo Krummrich
maintainerRe: [PATCH v5 04/26] rust: alloc: implement `Allocator` for `Kmalloc`
Quoted 7 lines · Miguel Ojeda
Quoted 4 lines · Danilo Krummrich
If we keep them, we'd consequently also need to add them for
vrealloc()andkvrealloc(). But again, they don't do anything for us, and hence are more misleading than helpful IMO.In general, they could do something (e.g.
noreturn), perhaps in the future.
Indeed, and I think once they're honored we should add them again.
It's just that I think as long as compiler attributes aren't honored, we should not have them in the first place to avoid confusion about whether they do or do not have any effect.
Quoted 3 lines
Apart from being potentially misleading, do we gain something by removing them?
It's not so much that I want to remove them for krealloc(), it's that I don't want to intentionally add them for the vrealloc() and kvrealloc() helpers, knowing that they don't do anything (yet).
And I think it's even more confusing if the krealloc() helper has those compiler attributes, but the vrealloc() and kvrealloc() helpers do not.
Quoted 8 lines
I guess simplicity in the file, but it is also simpler
to keep them aligned to the C side (which I guess is Alice's point), and avoids having to keep track of what could have a present or future impact inbindgen.Cheers,
Miguel
Re: [PATCH v5 04/26] rust: alloc: implement `Allocator` for `Kmalloc`
Quoted 10 lines · Danilo Krummrich
Quoted 7 lines · Miguel Ojeda
Quoted 4 lines · Danilo Krummrich
If we keep them, we'd consequently also need to add them for
vrealloc()andkvrealloc(). But again, they don't do anything for us, and hence are more misleading than helpful IMO.In general, they could do something (e.g.
noreturn), perhaps in the future.Indeed, and I think once they're honored we should add them again.
That sounds like it will be a lot of work, going through every function and checking if it has the given attribute. Especially when the attributes are enabled one by one. I think we should keep them (and of course introduce them on new functions).
---
Cheers,
Benno
Quoted 3 lines
It's just that I think as long as compiler attributes aren't honored, we should not have them in the first place to avoid confusion about whether they do or do not have any effect.
Danilo Krummrich
maintainerRe: [PATCH v5 04/26] rust: alloc: implement `Allocator` for `Kmalloc`
Quoted 8 lines · Benno Lossin
Quoted 7 lines · Danilo Krummrich
Quoted 6 lines · Miguel Ojeda
Quoted 4 lines · Danilo Krummrich
If we keep them, we'd consequently also need to add them for
vrealloc()andkvrealloc(). But again, they don't do anything for us, and hence are more misleading than helpful IMO.
Quoted 8 lines
Quoted 3 lines
Quoted 1 line
In general, they could do something (e.g.
noreturn), perhaps in the future.Indeed, and I think once they're honored we should add them again.
That sounds like it will be a lot of work, going through every function and checking if it has the given attribute. Especially when the attributes are enabled one by one. I think we should keep them (and of course introduce them on new functions).
I don't think it's gonna be a lot of work, if they're honored one day, which we don't know, do we?
Since it seems that everyone else prefers to have those attributes, I'll keep / add them accordingly.
However, I think we should at least keep a comment in rust/helpers.c that documents which attributes are honored by bindgen and which aren't. For now, the comment should probably say that non of them are honored?
Quoted 9 lines
---
Cheers,
BennoQuoted 3 lines
It's just that I think as long as compiler attributes aren't honored, we should not have them in the first place to avoid confusion about whether they do or do not have any effect.
Re: [PATCH v5 04/26] rust: alloc: implement `Allocator` for `Kmalloc`
In reply to Danilo KrummrichQuoted 6 lines · Danilo Krummrich
Indeed, and I think once they're honored we should add them again.
It's just that I think as long as compiler attributes aren't honored, we should not have them in the first place to avoid confusion about whether they do or do not have any effect.
In the C side, many attributes are not honored anyway, depending on the compiler (or tool like sparse), compiler version, etc. So it would be "OK" in that sense (even if here we may know there is no C caller).
Quoted 3 lines
It's not so much that I want to remove them for
krealloc(), it's that I don't want to intentionally add them for thevrealloc()andkvrealloc()helpers, knowing that they don't do anything (yet).
One can think of them as "documentation", e.g. seeing __must_check tells you something about the function, so I don't think it would be bad to add them, if they are not too much work to maintain.
I checked about __must_check, because it would be nice if it is used by bindgen, and it turns out it already does, but behind --enable-function-attribute-detection (apparently for performance reasons):
int f(void);
__attribute__((warn_unused_result)) int g(void);gives:
extern "C" {
pub fn f() -> ::std::os::raw::c_int;
}
extern "C" {
#[must_use]
pub fn g() -> ::std::os::raw::c_int;
}So that one should be kept at the very least, and it is a good example of potential improvements later on based on these attributes.
In general, I think it is not a bad idea to write this file like we would write other C files, even if it is not a regular C file. And adding more information to signatures is, after all, part of what we do with Rust overall!
Cheers,
Miguel
Re: [PATCH v5 04/26] rust: alloc: implement `Allocator` for `Kmalloc`
Quoted 5 lines · Miguel Ojeda
I checked about
__must_check, because it would be nice if it is used bybindgen, and it turns out it already does, but behind--enable-function-attribute-detection(apparently for performance reasons):
I just tried, and the flag seems to work, gives us a bunch of #[must_use]s which are nice, and apparently no other change (in usual x86_64 config at least).
I don't notice any significant performance difference in our case, so I will send a quick patch to see if others find an issue with it.
Cheers,
Miguel
Danilo Krummrich
maintainerRe: [PATCH v5 04/26] rust: alloc: implement `Allocator` for `Kmalloc`
Quoted 11 lines · Miguel Ojeda
Quoted 5 lines · Miguel Ojeda
I checked about
__must_check, because it would be nice if it is used bybindgen, and it turns out it already does, but behind--enable-function-attribute-detection(apparently for performance reasons):I just tried, and the flag seems to work, gives us a bunch of
#[must_use]s which are nice, and apparently no other change (in usualx86_64config at least).
Cool! That's even better then.
Quoted 7 lines
I don't notice any significant performance difference in our case, so I will send a quick patch to see if others find an issue with it.
Cheers,
Miguel
Re: [PATCH v5 04/26] rust: alloc: implement `Allocator` for `Kmalloc`
In reply to Danilo Krummrichrust/kernel/alloc/allocator.rs pub(crate) unsafe fn krealloc_aligned(ptr: *mut u8, new_layout: Layout, flags: F · line 60
Quoted 3 lines · Danilo Krummrich
rust/kernel/alloc/allocator.rs +3
-0+/// # Safety +/// +/// This method has the same safety requirements as `Allocator::realloc`.
Please make this a link.
Quoted 9 lines
rust/kernel/alloc/allocator.rs +9
-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 (),
rust/kernel/alloc/allocator.rs pub(crate) unsafe fn krealloc_aligned(ptr: *mut u8, new_layout: Layout, flags: F · line 79
Quoted 10 lines
rust/kernel/alloc/allocator.rs +10
-0+ None => ptr::null(), + }; + +// SAFETY: `ptr` is either NULL or valid by the safety requirements of this function. +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 {
Why do you do this check *after* calling self.0?
---
Cheers,
Benno
Quoted 8 lines
rust/kernel/alloc/allocator.rs +8
-0+NonNull ::dangling () + }else { +NonNull ::new (raw_ptr).ok_or (AllocError )? + }; + +Ok (NonNull ::slice_from_raw_parts (ptr, size)) + } +}
Danilo Krummrich
maintainerRe: [PATCH v5 04/26] rust: alloc: implement `Allocator` for `Kmalloc`
Quoted 6 lines · Benno Lossin
rust/kernel/alloc/allocator.rs pub(crate) unsafe fn krealloc_aligned(ptr: *mut u8, new_layout: Layout, flags: F · line 60
Quoted 3 lines · Danilo Krummrich
rust/kernel/alloc/allocator.rs +3
-0+/// # Safety +/// +/// This method has the same safety requirements as `Allocator::realloc`. Please make this a link.
Sure.
Quoted 14 lines
Quoted 14 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 either NULL or valid by the safety requirements of this function.
Quoted 8 lines
rust/kernel/alloc/allocator.rs pub(crate) unsafe fn krealloc_aligned(ptr: *mut u8, new_layout: Layout, flags: F · line 79
Quoted 6 lines
rust/kernel/alloc/allocator.rs +6
-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 {Why do you do this check *after* calling
self.0?
Because I need raw_ptr in the else case below.
Quoted 14 lines
---
Cheers,
BennoQuoted 8 lines
rust/kernel/alloc/allocator.rs +8
-0+NonNull ::dangling () + }else { +NonNull ::new (raw_ptr).ok_or (AllocError )? + }; + +Ok (NonNull ::slice_from_raw_parts (ptr, size)) + } +}
Re: [PATCH v5 04/26] rust: alloc: implement `Allocator` for `Kmalloc`
Quoted 24 lines · Danilo Krummrich
Quoted 14 lines · Benno Lossin
Quoted 13 lines · Danilo Krummrich
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 either NULL or valid by the safety requirements of this function. Quoted 8 lines
rust/kernel/alloc/allocator.rs pub(crate) unsafe fn krealloc_aligned(ptr: *mut u8, new_layout: Layout, flags: F · line 79
Quoted 6 lines
rust/kernel/alloc/allocator.rs +6
-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 {Why do you do this check *after* calling
self.0?
Quoted 1 line
Because I need
raw_ptrin the else case below.
But you can just return early above? I would prefer the check be done before self.0 is called.
---
Cheers,
Benno
Danilo Krummrich
maintainerRe: [PATCH v5 04/26] rust: alloc: implement `Allocator` for `Kmalloc`
Quoted 25 lines · Benno Lossin
Quoted 24 lines · Danilo Krummrich
Quoted 22 lines · Benno Lossin
Quoted 9 lines · Danilo Krummrich
rust/kernel/alloc/allocator.rs +9
-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 (),rust/kernel/alloc/allocator.rs pub(crate) unsafe fn krealloc_aligned(ptr: *mut u8, new_layout: Layout, flags: F · line 79
Quoted 10 lines
rust/kernel/alloc/allocator.rs +10
-0+ None => ptr::null(), + }; + +// SAFETY: `ptr` is either NULL or valid by the safety requirements of this function. +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 {Why do you do this check *after* calling
self.0?
Quoted 4 lines
Quoted 1 line
Because I need
raw_ptrin the else case below.But you can just return early above? I would prefer the check be done before
self.0is called.
No, we can't return early, we need self.0 to be called, because if size == 0 we free the given allocation, if any.
Quoted 5 lines
---
Cheers,
Benno
Re: [PATCH v5 04/26] rust: alloc: implement `Allocator` for `Kmalloc`
Quoted 26 lines · Danilo Krummrich
Quoted 25 lines · Benno Lossin
Quoted 24 lines · Danilo Krummrich
Quoted 22 lines · Benno Lossin
Quoted 9 lines · Danilo Krummrich
rust/kernel/alloc/allocator.rs +9
-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 (),rust/kernel/alloc/allocator.rs pub(crate) unsafe fn krealloc_aligned(ptr: *mut u8, new_layout: Layout, flags: F · line 79
Quoted 10 lines
rust/kernel/alloc/allocator.rs +10
-0+ None => ptr::null(), + }; + +// SAFETY: `ptr` is either NULL or valid by the safety requirements of this function. +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 {Why do you do this check *after* calling
self.0?
Quoted 7 lines
Quoted 4 lines
Quoted 1 line
Because I need
raw_ptrin the else case below.But you can just return early above? I would prefer the check be done before
self.0is called.No, we can't return early, we need
self.0to be called, because ifsize == 0we free the given allocation, if any.
Oh yeah of course... I don't know how I arrived at that comment...
---
Cheers,
Benno
Re: [PATCH v5 04/26] rust: alloc: implement `Allocator` for `Kmalloc`
In reply to Danilo KrummrichQuoted 1 line · Danilo Krummrich
+unsafe impl Allocator for Kmalloc {
There is a missing SAFETY comment here (and also for Vmalloc, probably also for VKmalloc then).
---
Cheers,
Benno
Quoted 16 lines
rust/kernel/alloc/allocator.rs +10
-1+unsafe fn realloc ( + ptr:Option <NonNull <u8 >>, + layout:Layout , + flags:Flags , + ) ->Result <NonNull <[u8 ]>,AllocError > { +// SAFETY: `ReallocFunc::call` has the same safety requirements as `Allocator::realloc`. +unsafe {ReallocFunc ::KREALLOC .call (ptr, layout, flags) } + } +} +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 v5 04/26] rust: alloc: implement `Allocator` for `Kmalloc`
Quoted 5 lines · Benno Lossin
Quoted 1 line · Danilo Krummrich
+unsafe impl Allocator for Kmalloc {
There is a missing SAFETY comment here (and also for Vmalloc, probably also for VKmalloc then).
Any suggestion on what to write here?
I'd probably come up with something like:
--
Memory returned from Kmalloc remains valid until explicitly freed.
It is valid to pass any pointer to an allocated memory buffer obtained with any function of Kmalloc to any other function of Kmalloc.
If Kmalloc::realloc is called with a size of zero, the given memory allocation, if any, is freed.
If Kmalloc::realloc is called with None it behaves like Kmalloc::alloc, i.e. a new memory allocation is created.
--
and repeat that for Vmalloc and KVmalloc.
I'm not sure how useful that is though.
Quoted 22 lines
---
Cheers,
BennoQuoted 16 lines
rust/kernel/alloc/allocator.rs +10
-1+unsafe fn realloc ( + ptr:Option <NonNull <u8 >>, + layout:Layout , + flags:Flags , + ) ->Result <NonNull <[u8 ]>,AllocError > { +// SAFETY: `ReallocFunc::call` has the same safety requirements as `Allocator::realloc`. +unsafe {ReallocFunc ::KREALLOC .call (ptr, layout, flags) } + } +} +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 v5 04/26] rust: alloc: implement `Allocator` for `Kmalloc`
Quoted 20 lines · Danilo Krummrich
Quoted 5 lines · Benno Lossin
Quoted 1 line · Danilo Krummrich
+unsafe impl Allocator for Kmalloc {
There is a missing SAFETY comment here (and also for Vmalloc, probably also for VKmalloc then).
Any suggestion on what to write here?
I'd probably come up with something like:
--
Memory returned fromKmallocremains valid until explicitly freed.It is valid to pass any pointer to an allocated memory buffer obtained with any function of
Kmallocto any other function ofKmalloc.If
Kmalloc::reallocis called with a size of zero, the given memory allocation, if any, is freed.
Quoted 7 lines
If
Kmalloc::reallocis called withNoneit behaves likeKmalloc::alloc, i.e. a new memory allocation is created.
--and repeat that for
VmallocandKVmalloc.I'm not sure how useful that is though.
Ideally you would write down how the guarantees are satisfied in addition to the invariants. But I guess this is only guaranteed by the ReallocFunc::call, since that handles the ZSTs... Maybe this is a bit excessive. If I come up with something better I will let you know.
---
Cheers,
Benno
Danilo Krummrich
maintainerPATCH v5 5/26 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 v5 05/26] rust: alloc: add module `allocator_test`
Quoted 8 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.
Quoted 7 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.rs
---
Cheers,
Benno
Danilo Krummrich
maintainerPATCH v5 6/26 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 | 28 ++++++++++++++++++++++++++++ rust/kernel/alloc/allocator_test.rs | 1 + 3 files changed, 36 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 +28 -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.
@@ -55,6 +62,9 @@ impl ReallocFunc {// INVARIANT: `krealloc` satisfies the type invariants. const KREALLOC :Self =Self (bindings::krealloc); +// INVARIANT: `vrealloc` satisfies the type invariants. +const VREALLOC :Self =Self (bindings::vrealloc); +/// # Safety /// /// This method has the same safety requirements as `Allocator::realloc`.
@@ -132,6 +142,24 @@ 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 > { +// 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 {ReallocFunc ::VREALLOC .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 v5 06/26] rust: alloc: implement `Vmalloc` allocator
Quoted 75 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 | 28 ++++++++++++++++++++++++++++ rust/kernel/alloc/allocator_test.rs | 1 + 3 files changed, 36 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 +10
-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. @@ -55,6 +62,9 @@ impl ReallocFunc {// INVARIANT: `krealloc` satisfies the type invariants. const KREALLOC :Self =Self (bindings::krealloc); +// INVARIANT: `vrealloc` satisfies the type invariants. +const VREALLOC :Self =Self (bindings::vrealloc); +/// # Safety /// /// This method has the same safety requirements as `Allocator::realloc`.
rust/kernel/alloc/allocator.rs unsafe fn alloc_zeroed(&self, layout: Layout) -> *mut u8 { · line 145
Quoted 4 lines
rust/kernel/alloc/allocator.rs +1
-0@@ -132,6 +142,24 @@ unsafe fn alloc_zeroed(&self, layout: Layout) -> *mut u8 { } } +unsafe impl Allocator for Vmalloc {
Missing SAFETY comment.
rust/kernel/alloc/allocator.rs unsafe fn alloc_zeroed(&self, layout: Layout) -> *mut u8 { · line 146
Quoted 1 line
rust/kernel/alloc/allocator.rs +1
-0+ unsafe fn realloc(
Does this need #[inline]?
rust/kernel/alloc/allocator.rs unsafe fn alloc_zeroed(&self, layout: Layout) -> *mut u8 { · line 154
Quoted 8 lines
rust/kernel/alloc/allocator.rs +8
-0+ ptr: Option<NonNull<u8>>, + layout: Layout, + flags: Flags, + ) -> Result<NonNull<[u8]>, AllocError> { +// 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 );
I think here we should first try to use build_error!, most often the alignment will be specified statically, so it should get optimized away.
How difficult will it be to support this? (it is a weird requirement, but I dislike just returning an error...)
---
Cheers,
Benno
Quoted 26 lines
rust/kernel/alloc/allocator.rs +8
-0+ } + +// SAFETY: If not `None`, `ptr` is guaranteed to point to valid memory, which was previously +// allocated with this `Allocator`. +unsafe {ReallocFunc ::VREALLOC .call (ptr, layout, flags) } + } +} +#[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 v5 06/26] rust: alloc: implement `Vmalloc` allocator
Quoted 79 lines · Benno Lossin
Quoted 78 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 | 28 ++++++++++++++++++++++++++++ rust/kernel/alloc/allocator_test.rs | 1 + 3 files changed, 36 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 +10
-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. @@ -55,6 +62,9 @@ impl ReallocFunc {// INVARIANT: `krealloc` satisfies the type invariants. const KREALLOC :Self =Self (bindings::krealloc); +// INVARIANT: `vrealloc` satisfies the type invariants. +const VREALLOC :Self =Self (bindings::vrealloc); +/// # Safety /// /// This method has the same safety requirements as `Allocator::realloc`. @@ -132,6 +142,24 @@ unsafe fn alloc_zeroed(&self, layout: Layout) -> *mut u8 { } }
Quoted 7 lines
rust/kernel/alloc/allocator.rs unsafe fn alloc_zeroed(&self, layout: Layout) -> *mut u8 { · line 145
Quoted 1 line
rust/kernel/alloc/allocator.rs +1
-0+unsafe impl Allocator for Vmalloc {Missing SAFETY comment.
rust/kernel/alloc/allocator.rs unsafe fn alloc_zeroed(&self, layout: Layout) -> *mut u8 { · line 146
Quoted 1 line
rust/kernel/alloc/allocator.rs +1
-0+ unsafe fn realloc(Does this need
#[inline]?
Given that we almost only call ReallocFunc::VREALLOC.call, inlining this seems reasonable.
Quoted 10 lines
rust/kernel/alloc/allocator.rs unsafe fn alloc_zeroed(&self, layout: Layout) -> *mut u8 { · line 154
Quoted 9 lines
rust/kernel/alloc/allocator.rs +8
-0+ ptr: Option<NonNull<u8>>, + layout: Layout, + flags: Flags, + ) -> Result<NonNull<[u8]>, AllocError> { +// 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 2 lines
I think here we should first try to use
build_error!, most often the alignment will be specified statically, so it should get optimized away.
Sure, we can try that first.
Quoted 3 lines
How difficult will it be to support this? (it is a weird requirement, but I dislike just returning an error...)
It's not difficult to support at all. But it requires a C API taking an alignment argument (same for KVmalloc).
Coming up with a vrealloc_aligned() is rather trivial. kvrealloc_aligned() would be a bit weird though, because the alignment argument could only be really honored if we run into the vrealloc() case. For the krealloc() case it'd still depend on the bucket size that is selected for the requested size.
Adding the C API, I'm also pretty sure someone's gonna ask what we need an alignment larger than PAGE_SIZE for and if we have a real use case for that. I'm not entirely sure we have a reasonable answer for that.
I got some hacked up patches for that, but I'd rather polish and send them once we actually need it.
Quoted 32 lines
---
Cheers,
BennoQuoted 26 lines
rust/kernel/alloc/allocator.rs +8
-0+ } + +// SAFETY: If not `None`, `ptr` is guaranteed to point to valid memory, which was previously +// allocated with this `Allocator`. +unsafe {ReallocFunc ::VREALLOC .call (ptr, layout, flags) } + } +} +#[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 v5 06/26] rust: alloc: implement `Vmalloc` allocator
Quoted 11 lines · Danilo Krummrich
Quoted 11 lines
Quoted 10 lines
rust/kernel/alloc/allocator.rs +8
-0+ ptr: Option<NonNull<u8>>, + layout: Layout, + flags: Flags, + ) -> Result<NonNull<[u8]>, AllocError> { +// 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 4 lines
Quoted 2 lines
I think here we should first try to use
build_error!, most often the alignment will be specified statically, so it should get optimized away.Sure, we can try that first.
I think I spoke too soon here. I don't think build_error! or build_assert! can work here, it would also fail the build when the compiler doesn't know the value of the alignment, wouldn't it? I remember that I wasn't overly happy about failing this on runtime either when I first thought about this case, but I also couldn't think of something better.
In the end it's rather unlikely to ever hit this case, and probably even more unlikely to hit it for a sane reason.
Quoted 19 lines
Quoted 4 lines
How difficult will it be to support this? (it is a weird requirement, but I dislike just returning an error...)
It's not difficult to support at all. But it requires a C API taking an alignment argument (same for
KVmalloc).Coming up with a
vrealloc_aligned()is rather trivial.kvrealloc_aligned()would be a bit weird though, because the alignment argument could only be really honored if we run into thevrealloc()case. For thekrealloc()case it'd still depend on the bucket size that is selected for the requested size.Adding the C API, I'm also pretty sure someone's gonna ask what we need an alignment larger than
PAGE_SIZEfor and if we have a real use case for that. I'm not entirely sure we have a reasonable answer for that.I got some hacked up patches for that, but I'd rather polish and send them once we actually need it.
Re: [PATCH v5 06/26] rust: alloc: implement `Vmalloc` allocator
Quoted 15 lines · Danilo Krummrich
Quoted 14 lines · Danilo Krummrich
Quoted 13 lines
rust/kernel/alloc/allocator.rs unsafe fn alloc_zeroed(&self, layout: Layout) -> *mut u8 { · line 154
Quoted 10 lines
rust/kernel/alloc/allocator.rs +8
-0+ ptr: Option<NonNull<u8>>, + layout: Layout, + flags: Flags, + ) -> Result<NonNull<[u8]>, AllocError> { +// 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 );I think here we should first try to use
build_error!, most often the alignment will be specified statically, so it should get optimized away.
Quoted 7 lines
Quoted 1 line
Sure, we can try that first.
I think I spoke too soon here. I don't think
build_error!orbuild_assert!can work here, it would also fail the build when the compiler doesn't know the value of the alignment, wouldn't it? I remember that I wasn't overly happy about failing this on runtime either when I first thought about this case, but I also couldn't think of something better.
Yes, it might fail even though the alignment at runtime will be fine. But that's why I suggested trying build_error!(or build_assert!) first, if nobody hits the case where the compiler cannot figure it out, then we can keep it. If there are instances, where it fails, but the alignment would be fine at runtime, then we can change it to the above. (I would add such a comment above the assert).
Quoted 2 lines
In the end it's rather unlikely to ever hit this case, and probably even more unlikely to hit it for a sane reason.
Yeah, but I still prefer the build to fail, rather than emitting a warn message that can be overlooked at runtime.
Quoted 5 lines
Quoted 5 lines
Quoted 2 lines
How difficult will it be to support this? (it is a weird requirement, but I dislike just returning an error...)
It's not difficult to support at all. But it requires a C API taking an alignment argument (same for
KVmalloc).
I see, that's good to know.
Quoted 4 lines
Quoted 4 lines
Coming up with a
vrealloc_aligned()is rather trivial.kvrealloc_aligned()would be a bit weird though, because the alignment argument could only be really honored if we run into thevrealloc()case. For thekrealloc()case it'd still depend on the bucket size that is selected for the requested size.
Yeah... Maybe some more logic on the Rust side can help with that.
Quoted 3 lines
Quoted 3 lines
Adding the C API, I'm also pretty sure someone's gonna ask what we need an alignment larger than
PAGE_SIZEfor and if we have a real use case for that. I'm not entirely sure we have a reasonable answer for that.
We could argue that we can remove an "ugly hack" (when we don't have the build assert, if we do have that, I don't mind not supporting it), but I agree that finding a user will be difficult.
Quoted 2 lines
Quoted 2 lines
I got some hacked up patches for that, but I'd rather polish and send them once we actually need it.
Sure, just wanted to check why you don't want to do it this series.
---
Cheers,
Benno
Danilo Krummrich
maintainerRe: [PATCH v5 06/26] rust: alloc: implement `Vmalloc` allocator
Quoted 24 lines · Benno Lossin
Quoted 15 lines · Danilo Krummrich
Quoted 14 lines · Danilo Krummrich
Quoted 13 lines
rust/kernel/alloc/allocator.rs unsafe fn alloc_zeroed(&self, layout: Layout) -> *mut u8 { · line 154
Quoted 10 lines
rust/kernel/alloc/allocator.rs +8
-0+ ptr: Option<NonNull<u8>>, + layout: Layout, + flags: Flags, + ) -> Result<NonNull<[u8]>, AllocError> { +// 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 );I think here we should first try to use
build_error!, most often the alignment will be specified statically, so it should get optimized away.Quoted 7 lines
Quoted 1 line
Sure, we can try that first.
I think I spoke too soon here. I don't think
build_error!orbuild_assert!can work here, it would also fail the build when the compiler doesn't know the value of the alignment, wouldn't it? I remember that I wasn't overly happy about failing this on runtime either when I first thought about this case, but I also couldn't think of something better.
Quoted 6 lines
Yes, it might fail even though the alignment at runtime will be fine. But that's why I suggested trying
build_error!(orbuild_assert!) first, if nobody hits the case where the compiler cannot figure it out, then we can keep it. If there are instances, where it fails, but the alignment would be fine at runtime, then we can change it to the above. (I would add such a comment above the assert).
Unfortunately, it already does fail with just the test cases.
Anyway, even if it would have been fine, I don't think it would have been nice for a future user to run into a build error even though the alignment is perfectlly within bounds.
Quoted 13 lines
Quoted 3 lines
In the end it's rather unlikely to ever hit this case, and probably even more unlikely to hit it for a sane reason.
Yeah, but I still prefer the build to fail, rather than emitting a warn message that can be overlooked at runtime.
Quoted 5 lines
Quoted 5 lines
Quoted 2 lines
How difficult will it be to support this? (it is a weird requirement, but I dislike just returning an error...)
It's not difficult to support at all. But it requires a C API taking an alignment argument (same for
KVmalloc).
Quoted 8 lines
I see, that's good to know.
Quoted 4 lines
Quoted 4 lines
Coming up with a
vrealloc_aligned()is rather trivial.kvrealloc_aligned()would be a bit weird though, because the alignment argument could only be really honored if we run into thevrealloc()case. For thekrealloc()case it'd still depend on the bucket size that is selected for the requested size.Yeah... Maybe some more logic on the Rust side can help with that.
Only if we reimplement KVmalloc in Rust, However, there are quite some special cases in __kvmalloc_node_noprof(), i.e. fixup page flags, sanity check the size on kmalloc failure, fail on certain page flags, etc.
I don't really want to duplicate this code, unless we absolutely have to.
Quoted 8 lines
Quoted 4 lines
Quoted 4 lines
Adding the C API, I'm also pretty sure someone's gonna ask what we need an alignment larger than
PAGE_SIZEfor and if we have a real use case for that. I'm not entirely sure we have a reasonable answer for that.We could argue that we can remove an "ugly hack" (when we don't have the build assert, if we do have that, I don't mind not supporting it), but I agree that finding a user will be difficult.
I'd argue it's not really a hack to fail on something that's not supported (yet). Allocations can (almost) always fail, this is just another case.
Quoted 10 lines
Quoted 3 lines
Quoted 3 lines
I got some hacked up patches for that, but I'd rather polish and send them once we actually need it.
Sure, just wanted to check why you don't want to do it this series.
---
Cheers,
Benno
Re: [PATCH v5 06/26] rust: alloc: implement `Vmalloc` allocator
Quoted 25 lines · Danilo Krummrich
Quoted 24 lines · Benno Lossin
Quoted 22 lines · Danilo Krummrich
Quoted 11 lines · Danilo Krummrich
Quoted 11 lines
Quoted 10 lines
rust/kernel/alloc/allocator.rs +8
-0+ ptr: Option<NonNull<u8>>, + layout: Layout, + flags: Flags, + ) -> Result<NonNull<[u8]>, AllocError> { +// 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 4 lines
Quoted 2 lines
I think here we should first try to use
build_error!, most often the alignment will be specified statically, so it should get optimized away.Sure, we can try that first.
I think I spoke too soon here. I don't think
build_error!orbuild_assert!can work here, it would also fail the build when the compiler doesn't know the value of the alignment, wouldn't it? I remember that I wasn't overly happy about failing this on runtime either when I first thought about this case, but I also couldn't think of something better.
Quoted 8 lines
Quoted 6 lines
Yes, it might fail even though the alignment at runtime will be fine. But that's why I suggested trying
build_error!(orbuild_assert!) first, if nobody hits the case where the compiler cannot figure it out, then we can keep it. If there are instances, where it fails, but the alignment would be fine at runtime, then we can change it to the above. (I would add such a comment above the assert).Unfortunately, it already does fail with just the test cases.
Aw that's sad.
Quoted 3 lines
Anyway, even if it would have been fine, I don't think it would have been nice for a future user to run into a build error even though the alignment is perfectlly within bounds.
I think it would have been better compared to failing with a warning at runtime.
Quoted 19 lines
Quoted 19 lines
Quoted 2 lines
In the end it's rather unlikely to ever hit this case, and probably even more unlikely to hit it for a sane reason.
Yeah, but I still prefer the build to fail, rather than emitting a warn message that can be overlooked at runtime.
Quoted 5 lines
Quoted 5 lines
Quoted 2 lines
How difficult will it be to support this? (it is a weird requirement, but I dislike just returning an error...)
It's not difficult to support at all. But it requires a C API taking an alignment argument (same for
KVmalloc).I see, that's good to know.
Quoted 4 lines
Quoted 4 lines
Coming up with a
vrealloc_aligned()is rather trivial.kvrealloc_aligned()would be a bit weird though, because the alignment argument could only be really honored if we run into thevrealloc()case. For thekrealloc()case it'd still depend on the bucket size that is selected for the requested size.
Quoted 7 lines
Quoted 1 line
Yeah... Maybe some more logic on the Rust side can help with that.
Only if we reimplement
KVmallocin Rust, However, there are quite some special cases in__kvmalloc_node_noprof(), i.e. fixup page flags, sanity check the size on kmalloc failure, fail on certain page flags, etc.I don't really want to duplicate this code, unless we absolutely have to.
I am under the (probably wrong) impression that kvmalloc has some size check and selects vmalloc or kmalloc depending on that. I think that we could check the size and if it is going to allocate via kmalloc, then we adjust the size for alignment as usual and if it is going to select vmalloc, then we can just pass the alignment (if the vmalloc alignment patch is done first).
Quoted 10 lines
Quoted 7 lines
Quoted 3 lines
Quoted 3 lines
Adding the C API, I'm also pretty sure someone's gonna ask what we need an alignment larger than
PAGE_SIZEfor and if we have a real use case for that. I'm not entirely sure we have a reasonable answer for that.We could argue that we can remove an "ugly hack" (when we don't have the build assert, if we do have that, I don't mind not supporting it), but I agree that finding a user will be difficult.
I'd argue it's not really a hack to fail on something that's not supported (yet). Allocations can (almost) always fail, this is just another case.
I guess since this is a deterministic failure, it's better than other failures. But I would still say this is hacky.
---
Cheers,
Benno
Danilo Krummrich
maintainerRe: [PATCH v5 06/26] rust: alloc: implement `Vmalloc` allocator
Quoted 35 lines · Benno Lossin
Quoted 25 lines · Danilo Krummrich
Quoted 24 lines · Benno Lossin
Quoted 22 lines · Danilo Krummrich
Quoted 11 lines · Danilo Krummrich
Quoted 11 lines
Quoted 10 lines
(quoted diff) +8
-0+ ptr: Option<NonNull<u8>>, + layout: Layout, + flags: Flags, + ) -> Result<NonNull<[u8]>, AllocError> { + // 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 4 lines
Quoted 2 lines
I think here we should first try to use
build_error!, most often the alignment will be specified statically, so it should get optimized away.Sure, we can try that first.
I think I spoke too soon here. I don't think
build_error!orbuild_assert!can work here, it would also fail the build when the compiler doesn't know the value of the alignment, wouldn't it? I remember that I wasn't overly happy about failing this on runtime either when I first thought about this case, but I also couldn't think of something better.Quoted 8 lines
Quoted 6 lines
Yes, it might fail even though the alignment at runtime will be fine. But that's why I suggested trying
build_error!(orbuild_assert!) first, if nobody hits the case where the compiler cannot figure it out, then we can keep it. If there are instances, where it fails, but the alignment would be fine at runtime, then we can change it to the above. (I would add such a comment above the assert).Unfortunately, it already does fail with just the test cases.
Quoted 8 lines
Aw that's sad.
Quoted 3 lines
Anyway, even if it would have been fine, I don't think it would have been nice for a future user to run into a build error even though the alignment is perfectlly within bounds.
I think it would have been better compared to failing with a warning at runtime.
Generally, yes. But I think it's not acceptable to make calls fail that should actually succeed.
Quoted 22 lines
Quoted 22 lines
Quoted 21 lines
Quoted 3 lines
In the end it's rather unlikely to ever hit this case, and probably even more unlikely to hit it for a sane reason.
Yeah, but I still prefer the build to fail, rather than emitting a warn message that can be overlooked at runtime.
Quoted 5 lines
Quoted 5 lines
Quoted 2 lines
How difficult will it be to support this? (it is a weird requirement, but I dislike just returning an error...)
It's not difficult to support at all. But it requires a C API taking an alignment argument (same for
KVmalloc).I see, that's good to know.
Quoted 4 lines
Quoted 4 lines
Coming up with a
vrealloc_aligned()is rather trivial.kvrealloc_aligned()would be a bit weird though, because the alignment argument could only be really honored if we run into thevrealloc()case. For thekrealloc()case it'd still depend on the bucket size that is selected for the requested size.Yeah... Maybe some more logic on the Rust side can help with that.
Quoted 8 lines
Quoted 5 lines
Only if we reimplement
KVmallocin Rust, However, there are quite some special cases in__kvmalloc_node_noprof(), i.e. fixup page flags, sanity check the size on kmalloc failure, fail on certain page flags, etc.I don't really want to duplicate this code, unless we absolutely have to.
I am under the (probably wrong) impression that kvmalloc has some size check and selects vmalloc or kmalloc depending on that.
Basically, yes. But as mentioned above, there are quite some corner cases [1].
Quoted 3 lines
I think that we
could check the size and if it is going to allocate via kmalloc, then we adjust the size for alignment as usual
We don't need this adjustment any longer, see commit ad59baa31695 ("slab, rust:
extend kmalloc() alignment guarantees to remove Rust padding").
Quoted 3 lines
and if it is going to select
vmalloc, then we can just pass the alignment (if the vmalloc alignment patch is done first).
Yeah, but as mentioned, I'd prefer to do this in C, such that we don't need to open code everything the C code already does.
[1] https://elixir.bootlin.com/linux/v6.11-rc3/source/mm/util.c#L628
Quoted 19 lines
Quoted 11 lines
Quoted 8 lines
Quoted 4 lines
Quoted 4 lines
Adding the C API, I'm also pretty sure someone's gonna ask what we need an alignment larger than
PAGE_SIZEfor and if we have a real use case for that. I'm not entirely sure we have a reasonable answer for that.We could argue that we can remove an "ugly hack" (when we don't have the build assert, if we do have that, I don't mind not supporting it), but I agree that finding a user will be difficult.
I'd argue it's not really a hack to fail on something that's not supported (yet). Allocations can (almost) always fail, this is just another case.
I guess since this is a deterministic failure, it's better than other failures. But I would still say this is hacky.
---
Cheers,
Benno
Re: [PATCH v5 06/26] rust: alloc: implement `Vmalloc` allocator
Quoted 38 lines · Danilo Krummrich
Quoted 19 lines · Benno Lossin
Quoted 18 lines · Danilo Krummrich
Quoted 16 lines · Benno Lossin
Quoted 6 lines · Danilo Krummrich
Quoted 5 lines · Danilo Krummrich
Quoted 2 lines
How difficult will it be to support this? (it is a weird requirement, but I dislike just returning an error...)
It's not difficult to support at all. But it requires a C API taking an alignment argument (same for
KVmalloc).I see, that's good to know.
Quoted 4 lines
Quoted 4 lines
Coming up with a
vrealloc_aligned()is rather trivial.kvrealloc_aligned()would be a bit weird though, because the alignment argument could only be really honored if we run into thevrealloc()case. For thekrealloc()case it'd still depend on the bucket size that is selected for the requested size.Yeah... Maybe some more logic on the Rust side can help with that.
Quoted 8 lines
Quoted 5 lines
Only if we reimplement
KVmallocin Rust, However, there are quite some special cases in__kvmalloc_node_noprof(), i.e. fixup page flags, sanity check the size on kmalloc failure, fail on certain page flags, etc.I don't really want to duplicate this code, unless we absolutely have to.
I am under the (probably wrong) impression that kvmalloc has some size check and selects vmalloc or kmalloc depending on that.
Basically, yes. But as mentioned above, there are quite some corner cases [1].
Quoted 3 lines
I think that we
could check the size and if it is going to allocate via kmalloc, then we adjust the size for alignment as usualWe don't need this adjustment any longer, see commit
ad59baa31695("slab, rust:
extendkmalloc()alignment guarantees to remove Rust padding").
Quoted 8 lines
Quoted 3 lines
and if it is going to select
vmalloc, then we can just pass the alignment (if the vmalloc alignment patch is done first).Yeah, but as mentioned, I'd prefer to do this in C, such that we don't need to open code everything the C code already does.
[1] https://elixir.bootlin.com/linux/v6.11-rc3/source/mm/util.c#L628
I see, then it's probably better to just add an align parameter variant on the C side. Instead of rebuilding it in Rust.
---
Cheers,
Benno
Danilo Krummrich
maintainerPATCH v5 7/26 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 | 27 +++++++++++++++++++++++++++ rust/kernel/alloc/allocator_test.rs | 1 + 3 files changed, 34 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 +27 -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.
@@ -65,6 +71,9 @@ impl ReallocFunc {// INVARIANT: `vrealloc` satisfies the type invariants. const VREALLOC :Self =Self (bindings::vrealloc); +// INVARIANT: `kvrealloc` satisfies the type invariants. +const KVREALLOC :Self =Self (bindings::kvrealloc); +/// # Safety /// /// This method has the same safety requirements as `Allocator::realloc`.
@@ -160,6 +169,24 @@ unsafe fn realloc( } } +unsafe impl Allocator for KVmalloc { +unsafe fn realloc ( + ptr:Option <NonNull <u8 >>, + layout:Layout , + flags:Flags , + ) ->Result <NonNull <[u8 ]>,AllocError > { +// 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 {ReallocFunc ::KVREALLOC .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 v5 8/26 rust: alloc: add __GFP_NOWARN to `Flags`
In reply to Danilo KrummrichSome test cases in subsequent patches provoke allocation failures. Add __GFP_NOWARN to enable test cases to silence unpleasant warnings.
--- rust/bindings/bindings_helper.h | 1 + rust/kernel/alloc.rs | 5 +++++ 2 files changed, 6 insertions(+)
rust/bindings/bindings_helper.h +1 -0
@@ -31,4 +31,5 @@ const gfp_t RUST_CONST_HELPER_GFP_KERNEL_ACCOUNT = GFP_KERNEL_ACCOUNT;const gfp_t RUST_CONST_HELPER_GFP_NOWAIT =GFP_NOWAIT ;const gfp_t RUST_CONST_HELPER___GFP_ZERO = __GFP_ZERO;const gfp_t RUST_CONST_HELPER___GFP_HIGHMEM = ___GFP_HIGHMEM; +const gfp_t RUST_CONST_HELPER___GFP_NOWARN = ___GFP_NOWARN;const blk_features_t RUST_CONST_HELPER_BLK_FEAT_ROTATIONAL =BLK_FEAT_ROTATIONAL ;
rust/kernel/alloc.rs +5 -0
@@ -91,6 +91,11 @@ pub mod flags {/// use any filesystem callback. It is very likely to fail to allocate memory, even for very /// small allocations. pub const GFP_NOWAIT :Flags =Flags (bindings::GFP_NOWAIT ); + +/// Suppresses allocation failure reports. +/// +/// This is normally or'd with other flags. +pub const __GFP_NOWARN:Flags =Flags (bindings::__GFP_NOWARN); }/// The kernel's [`Allocator`] trait.
Signature · 1 line
2.45.2
Re: [PATCH v5 08/26] rust: alloc: add __GFP_NOWARN to `Flags`
Quoted 5 lines · Danilo Krummrich
Some test cases in subsequent patches provoke allocation failures. Add
__GFP_NOWARNto enable test cases to silence unpleasant warnings.Signed-off-by Danilo Krummrich <[email protected]>
Quoted 33 lines
rust/bindings/bindings_helper.h | 1 + rust/kernel/alloc.rs | 5 +++++ 2 files changed, 6 insertions(+)rust/bindings/bindings_helper.h +1
-0@@ -31,4 +31,5 @@ const gfp_t RUST_CONST_HELPER_GFP_KERNEL_ACCOUNT = GFP_KERNEL_ACCOUNT;const gfp_t RUST_CONST_HELPER_GFP_NOWAIT =GFP_NOWAIT ;const gfp_t RUST_CONST_HELPER___GFP_ZERO = __GFP_ZERO;const gfp_t RUST_CONST_HELPER___GFP_HIGHMEM = ___GFP_HIGHMEM; +const gfp_t RUST_CONST_HELPER___GFP_NOWARN = ___GFP_NOWARN;const blk_features_t RUST_CONST_HELPER_BLK_FEAT_ROTATIONAL =BLK_FEAT_ROTATIONAL ;rust/kernel/alloc.rs +5
-1@@ -91,6 +91,11 @@ pub mod flags {/// use any filesystem callback. It is very likely to fail to allocate memory, even for very /// small allocations. pub const GFP_NOWAIT :Flags =Flags (bindings::GFP_NOWAIT ); + +/// Suppresses allocation failure reports. +/// +/// This is normally or'd with other flags. +pub const __GFP_NOWARN:Flags =Flags (bindings::__GFP_NOWARN); }/// The kernel's [`Allocator`] trait. --2.45.2
Re: [PATCH v5 08/26] rust: alloc: add __GFP_NOWARN to `Flags`
In reply to Danilo KrummrichQuoted 8 lines · Danilo Krummrich
Some test cases in subsequent patches provoke allocation failures. Add
__GFP_NOWARNto enable test cases to silence unpleasant warnings.Signed-off-by Danilo Krummrich <[email protected]>--- rust/bindings/bindings_helper.h | 1 + rust/kernel/alloc.rs | 5 +++++ 2 files changed, 6 insertions(+)
---
Cheers,
Benno
Danilo Krummrich
maintainerPATCH v5 9/26 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 | 423 ++++++++++++++++++++++++++++++++++++++ rust/kernel/prelude.rs | 2 +- 3 files changed, 430 insertions(+), 1 deletion(-) 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 +423 -0
423 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::{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 };
Signature · 1 line
2.45.2
Alice Ryhl
maintainerRe: [PATCH v5 09/26] rust: alloc: implement kernel `Box`
Quoted 75 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 | 423 ++++++++++++++++++++++++++++++++++++++ rust/kernel/prelude.rs | 2 +- 3 files changed, 430 insertions(+), 1 deletion(-) 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 +27
-0@@ -0,0 +1,423 @@ +// 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::ptr::NonNull ; +use core::result::Result ; + +use crate ::init::{InPlaceInit ,Init ,PinInit }; +use crate ::types::ForeignOwnable ; + +/// The kernel's [`Box`] type - a heap allocation for a single value of type `T`. +/// +/// This is the kernel's version of the Rust stdlib's `Box`. There are a couple of differences, +/// 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. [`super::allocator::Kmalloc`], +/// [`super::allocator::Vmalloc`] or [`super::allocator::KVmalloc`]. There are aliases for `Box` +/// with these allocators ([`KBox`], [`VBox`], [`KVBox`]). +///
rust/kernel/alloc/kbox.rs line 37
Quoted 10 lines
rust/kernel/alloc/kbox.rs new file +10
-0+/// When dropping a [`Box`], the value is also dropped and the heap memory is automatically freed. +/// +/// # Examples +/// +/// ``` +/// let b = KBox::<u64>::new(24_u64, GFP_KERNEL)?; +/// +/// assert_eq!(*b, 24_u64); +/// +/// # Ok::<(), Error>(())
This is a minor nit, but when hiding lines in examples you should avoid having the rendered docs have empty lines at the beginning/end. There are also several examples of this below with the kernel::bindings import.
Quoted 15 lines
rust/kernel/alloc/kbox.rs new file +15
-0+/// ``` +/// +/// ``` +/// # use kernel::bindings; +/// +/// const SIZE: usize = bindings::KMALLOC_MAX_SIZE as usize + 1; +/// struct Huge([u8; SIZE]); +/// +/// assert!(KBox::<Huge>::new_uninit(GFP_KERNEL | __GFP_NOWARN).is_err()); +/// ``` +/// +/// ``` +/// # use kernel::bindings; +/// +/// const SIZE: usize = bindings::KMALLOC_MAX_SIZE as usize + 1;
rust/kernel/alloc/kbox.rs line 62
Quoted 10 lines
rust/kernel/alloc/kbox.rs new file +10
-0+/// struct Huge([u8; SIZE]); +/// +/// assert!(KVBox::<Huge>::new_uninit(GFP_KERNEL).is_ok()); +/// ``` +/// +/// # Invariants +/// +/// The [`Box`]' pointer always properly aligned and either points to memory allocated with `A` or, +/// for zero-sized types, is a dangling pointer. +pub struct Box <T : ?Sized ,A :Allocator >(NonNull <T >,PhantomData <A >);
I was about to say this needs a PhantomData<T> too, but I guess it isn't necessary anymore.
https://doc.rust-lang.org/nomicon/phantom-data.html#generic-parameters-and-drop-checking
rust/kernel/alloc/kbox.rs line 103
Quoted 1 line
rust/kernel/alloc/kbox.rs new file +1
-0+// SAFETY: `Box` is `Send` if `T` is `Send` because the data referenced by `self.0` is unaliased.
Instead of "unaliased" I would probably just say "because the Box owns a T".
rust/kernel/alloc/kbox.rs line 115
Quoted 6 lines
rust/kernel/alloc/kbox.rs new file +6
-0+ +// SAFETY: `Box` is `Sync` if `T` is `Sync` because the data referenced by `self.0` is unaliased. +unsafe impl <T ,A >Sync for Box <T ,A > +where +T :Send + ?Sized , +A :Allocator ,
This needs to say T: Sync instead of T: Send. That matches the std Box.
rust/kernel/alloc/kbox.rs line 129
Quoted 12 lines
rust/kernel/alloc/kbox.rs new file +12
-0+ +impl <T ,A >Box <T ,A > +where +T : ?Sized , +A :Allocator , +{ +/// Creates a new `Box<T, A>` from a raw pointer. +/// +/// # Safety +/// +/// `raw` must point to valid memory, previously be allocated with `A`, and provide at least +/// the size of type `T`. For ZSTs `raw` must be a dangling pointer.
Hmm. I don't love this wording. How about this?
For non-ZSTs, raw must point at a live allocation allocated with A that is sufficiently aligned for and holds a valid T. The caller passes ownership of the allocation to the Box. For ZSTs, the pointer must be non-null and aligned.
rust/kernel/alloc/kbox.rs line 280
Quoted 11 lines
rust/kernel/alloc/kbox.rs new file +11
-0+impl <T ,A >From <Box <T ,A >>for Pin <Box <T ,A >> +where +T : ?Sized , +A :Allocator , +{ +/// Converts a `Box<T, A>` into a `Pin<Box<T, A>>`. If `T` does not implement [`Unpin`], then +/// `*b` will be pinned in memory and can't be moved. +/// +/// See [`Box::into_pin`] for more details. +fn from (b:Box <T ,A >) ->Self { +Box ::into_pin (b)
I still think it makes more sense to match std and only provide From and not an into_pin, but it's not a blocker.
Alice
Danilo Krummrich
maintainerRe: [PATCH v5 09/26] rust: alloc: implement kernel `Box`
Quoted 83 lines · Alice Ryhl
Quoted 82 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 | 423 ++++++++++++++++++++++++++++++++++++++ rust/kernel/prelude.rs | 2 +- 3 files changed, 430 insertions(+), 1 deletion(-) 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 +34
-0@@ -0,0 +1,423 @@ +// 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::ptr::NonNull ; +use core::result::Result ; + +use crate ::init::{InPlaceInit ,Init ,PinInit }; +use crate ::types::ForeignOwnable ; + +/// The kernel's [`Box`] type - a heap allocation for a single value of type `T`. +/// +/// This is the kernel's version of the Rust stdlib's `Box`. There are a couple of differences, +/// 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. [`super::allocator::Kmalloc`], +/// [`super::allocator::Vmalloc`] or [`super::allocator::KVmalloc`]. There are aliases for `Box` +/// with these allocators ([`KBox`], [`VBox`], [`KVBox`]). +/// +/// When dropping a [`Box`], the value is also dropped and the heap memory is automatically freed. +/// +/// # Examples +/// +/// ``` +/// let b = KBox::<u64>::new(24_u64, GFP_KERNEL)?; +///
Quoted 8 lines
rust/kernel/alloc/kbox.rs line 37
Quoted 3 lines
rust/kernel/alloc/kbox.rs new file +3
-0+/// assert_eq!(*b, 24_u64); +/// +/// # Ok::<(), Error>(()) This is a minor nit, but when hiding lines in examples you should avoid having the rendered docs have empty lines at the beginning/end. There are also several examples of this below with the
kernel::bindingsimport.
Makes sense, gonna fix it.
Quoted 31 lines
Quoted 16 lines
rust/kernel/alloc/kbox.rs new file +15
-0+/// ``` +/// +/// ``` +/// # use kernel::bindings; +/// +/// const SIZE: usize = bindings::KMALLOC_MAX_SIZE as usize + 1; +/// struct Huge([u8; SIZE]); +/// +/// assert!(KBox::<Huge>::new_uninit(GFP_KERNEL | __GFP_NOWARN).is_err()); +/// ``` +/// +/// ``` +/// # use kernel::bindings; +/// +/// const SIZE: usize = bindings::KMALLOC_MAX_SIZE as usize + 1; rust/kernel/alloc/kbox.rs line 62
Quoted 10 lines
rust/kernel/alloc/kbox.rs new file +10
-0+/// struct Huge([u8; SIZE]); +/// +/// assert!(KVBox::<Huge>::new_uninit(GFP_KERNEL).is_ok()); +/// ``` +/// +/// # Invariants +/// +/// The [`Box`]' pointer always properly aligned and either points to memory allocated with `A` or, +/// for zero-sized types, is a dangling pointer. +pub struct Box <T : ?Sized ,A :Allocator >(NonNull <T >,PhantomData <A >);I was about to say this needs a PhantomData<T> too, but I guess it isn't necessary anymore.
https://doc.rust-lang.org/nomicon/phantom-data.html#generic-parameters-and-drop-checking
Quoted 3 lines
rust/kernel/alloc/kbox.rs line 103
Quoted 1 line
rust/kernel/alloc/kbox.rs new file +1
-0+// SAFETY: `Box` is `Send` if `T` is `Send` because the data referenced by `self.0` is unaliased. Instead of "unaliased" I would probably just say "because the Box owns a T".
I'm fine with either one, if that's preferred, I'll change it.
Quoted 9 lines
rust/kernel/alloc/kbox.rs line 115
Quoted 7 lines
rust/kernel/alloc/kbox.rs new file +6
-0+ +// SAFETY: `Box` is `Sync` if `T` is `Sync` because the data referenced by `self.0` is unaliased. +unsafe impl <T ,A >Sync for Box <T ,A > +where +T :Send + ?Sized , +A :Allocator ,This needs to say
T: Syncinstead ofT: Send. That matches the std Box.
Good catch, pretty sure Vec has the same copy-paste mistake.
Quoted 16 lines
rust/kernel/alloc/kbox.rs line 129
Quoted 13 lines
rust/kernel/alloc/kbox.rs new file +12
-0+ +impl <T ,A >Box <T ,A > +where +T : ?Sized , +A :Allocator , +{ +/// Creates a new `Box<T, A>` from a raw pointer. +/// +/// # Safety +/// +/// `raw` must point to valid memory, previously be allocated with `A`, and provide at least +/// the size of type `T`. For ZSTs `raw` must be a dangling pointer. Hmm. I don't love this wording. How about this?
Quoted 3 lines
For non-ZSTs,
rawmust point at a live allocation allocated withAthat is sufficiently aligned for and holds a validT. The caller passes ownership of the allocation to theBox.
I'm fine taking that, I'm not so sure about "live allocation" though, we don't use this wording anywhere else. It's probably fine to just say "allocation", since once it's freed it technically isn't an allocation anymore anyways.
Quoted 1 line
For ZSTs, the pointer must be non-null and aligned.
Technically, for ZSTs any pointer is aligned and NULL should be valid as well. Maybe we just don't mentioned ZSTs at all, since we don't really need to enforce anything for ZSTs.
Quoted 15 lines
rust/kernel/alloc/kbox.rs line 280
Quoted 12 lines
rust/kernel/alloc/kbox.rs new file +11
-0+impl <T ,A >From <Box <T ,A >>for Pin <Box <T ,A >> +where +T : ?Sized , +A :Allocator , +{ +/// Converts a `Box<T, A>` into a `Pin<Box<T, A>>`. If `T` does not implement [`Unpin`], then +/// `*b` will be pinned in memory and can't be moved. +/// +/// See [`Box::into_pin`] for more details. +fn from (b:Box <T ,A >) ->Self { +Box ::into_pin (b)I still think it makes more sense to match std and only provide From and not an
into_pin, but it's not a blocker.
Yeah, I just kept it since I'm not (yet) entirely sure what to think of the From and Into stuff in some cases.
I don't really like that, depending on the context, it may hide relevant details.
In the kernel, no matter how well documented an API is, I think it's rather common to look at the code for some implementation details before using it.
Sometimes it might not be super trivial for the "occasional" reader to figure out what's the type of some variable. Calling into_pin vs. just into immediately tells the reader that things need to be pinned from there on.
However, I had no specific example in my mind and I'm also not overly concerned to remove into_pin, but I want to at least share the reason why I kept it in the first place.
Quoted 3 lines
Alice
Alice Ryhl
maintainerRe: [PATCH v5 09/26] rust: alloc: implement kernel `Box`
Quoted 27 lines · Danilo Krummrich
Quoted 15 lines · Alice Ryhl
rust/kernel/alloc/kbox.rs line 280
Quoted 11 lines · Danilo Krummrich
rust/kernel/alloc/kbox.rs new file +11
-0+impl <T ,A >From <Box <T ,A >>for Pin <Box <T ,A >> +where +T : ?Sized , +A :Allocator , +{ +/// Converts a `Box<T, A>` into a `Pin<Box<T, A>>`. If `T` does not implement [`Unpin`], then +/// `*b` will be pinned in memory and can't be moved. +/// +/// See [`Box::into_pin`] for more details. +fn from (b:Box <T ,A >) ->Self { +Box ::into_pin (b)I still think it makes more sense to match std and only provide From and not an
into_pin, but it's not a blocker.Yeah, I just kept it since I'm not (yet) entirely sure what to think of the
FromandIntostuff in some cases.I don't really like that, depending on the context, it may hide relevant details.
In the kernel, no matter how well documented an API is, I think it's rather common to look at the code for some implementation details before using it.
Quoted 7 lines
Sometimes it might not be super trivial for the "occasional" reader to figure out what's the type of some variable. Calling
into_pinvs. justintoimmediately tells the reader that things need to be pinned from there on.However, I had no specific example in my mind and I'm also not overly concerned to remove
into_pin, but I want to at least share the reason why I kept it in the first place.
You can write Pin::from to convert the box. I think that reads reasonably well.
But like I said, not a blocker for me.
Alice
Re: [PATCH v5 09/26] rust: alloc: implement kernel `Box`
In reply to Danilo Krummrichrust/kernel/alloc/kbox.rs line 22
Quoted 5 lines · Danilo Krummrich
rust/kernel/alloc/kbox.rs new file +5
-0+/// The kernel's [`Box`] type - a heap allocation for a single value of type `T`. +/// +/// This is the kernel's version of the Rust stdlib's `Box`. There are a couple of differences, +/// for example no `noalias` attribute is emitted and partially moving out of a `Box` is not +/// supported.
I would add "But otherwise it works the same." (I don't know if there is a comma needed after the "otherwise").
Also I remember that there was one more difference with a custom box compared to the stdlib, but I forgot what that was, does someone else remember? We should also put that here.
Quoted 28 lines
rust/kernel/alloc/kbox.rs new file +28
-0+/// +/// `Box` works with any of the kernel's allocators, e.g. [`super::allocator::Kmalloc`], +/// [`super::allocator::Vmalloc`] or [`super::allocator::KVmalloc`]. There are aliases for `Box` +/// with these allocators ([`KBox`], [`VBox`], [`KVBox`]). +/// +/// When dropping a [`Box`], the value is also dropped and the heap memory is automatically freed. +/// +/// # Examples +/// +/// ``` +/// let b = KBox::<u64>::new(24_u64, GFP_KERNEL)?; +/// +/// assert_eq!(*b, 24_u64); +/// +/// # Ok::<(), Error>(()) +/// ``` +/// +/// ``` +/// # use kernel::bindings; +/// +/// const SIZE: usize = bindings::KMALLOC_MAX_SIZE as usize + 1; +/// struct Huge([u8; SIZE]); +/// +/// assert!(KBox::<Huge>::new_uninit(GFP_KERNEL | __GFP_NOWARN).is_err()); +/// ``` +/// +/// ``` +/// # use kernel::bindings;
rust/kernel/alloc/kbox.rs line 60
Quoted 10 lines
rust/kernel/alloc/kbox.rs new file +10
-0+/// +/// const SIZE: usize = bindings::KMALLOC_MAX_SIZE as usize + 1; +/// struct Huge([u8; SIZE]); +/// +/// assert!(KVBox::<Huge>::new_uninit(GFP_KERNEL).is_ok()); +/// ``` +/// +/// # Invariants +/// +/// The [`Box`]' pointer always properly aligned and either points to memory allocated with `A` or,
"pointer always properly" -> "pointer is properly"
Quoted 1 line
+/// for zero-sized types, is a dangling pointer.
I think this section would look nicer, if it were formatted using bullet points (that way the bracketing of the "or" is also unambiguous).
Additionally, this is missing that the pointer is valid for reads and writes.
Quoted 1 line
+pub struct Box<T: ?Sized, A: Allocator>(NonNull<T>, PhantomData<A>);
Why no repr(transparent)?
Review
Quoted 2 lines
(quoted diff) +2
-0+ +/// Type alias for `Box` with a `Kmalloc` allocator.
I think we should add that this is only designed for small values.
rust/kernel/alloc/kbox.rs line 77
Quoted 13 lines
rust/kernel/alloc/kbox.rs new file +13
-0+/// +/// # 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.
Same here, add that this is supposed to be used for big values (or is this also a general-purpose allocator, just not guaranteeing that the memory is physically contiguous? in that case I would document it here and also on Vmalloc).
rust/kernel/alloc/kbox.rs line 90
Quoted 13 lines
rust/kernel/alloc/kbox.rs new file +13
-0+/// +/// # 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.
Ditto.
Quoted 67 lines
rust/kernel/alloc/kbox.rs new file +67
-0+/// +/// # 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 >; + +// SAFETY: `Box` is `Send` if `T` is `Send` because the data referenced by `self.0` is unaliased. +unsafe impl <T ,A >Send for Box <T ,A > +where +T :Send + ?Sized , +A :Allocator , +{ +} + +// SAFETY: `Box` is `Sync` if `T` is `Sync` because the data referenced by `self.0` is unaliased. +unsafe impl <T ,A >Sync for Box <T ,A > +where +T :Send + ?Sized , +A :Allocator , +{ +} + +impl <T ,A >Box <T ,A > +where +T : ?Sized , +A :Allocator , +{ +/// Creates a new `Box<T, A>` from a raw pointer. +/// +/// # Safety +/// +/// `raw` must point to valid memory, previously be allocated with `A`, and provide at least +/// the size of type `T`. For ZSTs `raw` must be a dangling pointer. +#[inline] +pub const unsafe fn from_raw (raw: *mut T ) ->Self { +// INVARIANT: Validity of `raw` is guaranteed by the safety preconditions of this function. +// SAFETY: By the safety preconditions of this function, `raw` is not a NULL pointer. +Self (unsafe {NonNull ::new_unchecked (raw) },PhantomData ::<A >) + } + +/// Consumes the `Box<T, A>` and returns a raw pointer. +/// +/// This will not run the destructor of `T` and for non-ZSTs the allocation will stay alive +/// indefinitely. Use [`Box::from_raw`] to recover the [`Box`], drop the value and free the +/// allocation, if any. +/// +/// # 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); +
rust/kernel/alloc/kbox.rs line 167
Quoted 10 lines
rust/kernel/alloc/kbox.rs new file +10
-0+ b.0 .as_ptr () + } + +/// Consumes and leaks the `Box<T, A>` and returns a mutable reference. +/// +/// See [Box::into_raw] for more details. +#[inline] +pub fn leak <'a>(b:Self ) -> &'amut T +where +T : 'a,
This bound shouldn't be needed, it is implicit, since T appears in the type &'a mut T.
rust/kernel/alloc/kbox.rs line 179
Quoted 12 lines
rust/kernel/alloc/kbox.rs new file +12
-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>>`. If `T` does not implement [`Unpin`], then +/// `*b` will be pinned in memory and can't be moved. +/// +/// This moves `b` into `Pin` without moving `*b` or allocating and copying any memory. +#[inline] +pub fn into_pin (b:Self ) ->Pin <Self > {
I would agree with Alice, that this is not really needed, since you can just as well write Pin::from(my_box).
Quoted 7 lines
rust/kernel/alloc/kbox.rs new file +7
-0+// SAFETY: The value wrapped inside a `Pin<Box<T, A>>` cannot be moved or replaced as long +// as `T` does not implement `Unpin`. +unsafe {Pin ::new_unchecked (b) } + } +} + +impl <T ,A >Box <MaybeUninit <T >,A >
rust/kernel/alloc/kbox.rs line 196
Quoted 10 lines
rust/kernel/alloc/kbox.rs new file +10
-0+where + A: Allocator, +{ +/// Converts a `Box<MaybeUninit<T>, A>` to a `Box<T, A>`. +/// +/// # Safety +/// +/// Callers must ensure that the value inside of `b` is in an initialized state. It is +/// undefined behavior to call this function while the value inside of `b` is not yet fully +/// initialized.
The second sentence is unnecessary safety documentation. It might still be useful as normal documentation though.
Quoted 18 lines
rust/kernel/alloc/kbox.rs new file +18
-0+pub unsafe fn assume_init (b:Self ) ->Box <T ,A > { +let raw =Self ::into_raw (b); + +// SAFETY: `raw` comes from a previous call to `Box::into_raw`. By the safety requirements +// of this function, the value inside the `Box` is in an initialized state. Hence, it is +// safe to reconstruct the `Box` as `Box<T, A>`. +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 >
rust/kernel/alloc/kbox.rs line 224
Quoted 10 lines
rust/kernel/alloc/kbox.rs new file +10
-0+where + A: Allocator, +{ + fn is_zst() -> bool { + core::mem::size_of ::<T >() ==0 + } + +/// Creates a new `Box<T, A>` and initializes its contents with `x`. +/// +/// New memory is allocated with `a`. The allocation may fail, in which case an error is
Wrong case on "a" (can this also be a link?).
rust/kernel/alloc/kbox.rs line 233
Quoted 9 lines
rust/kernel/alloc/kbox.rs new file +9
-0+/// returned. For ZSTs no memory is allocated. +pub fn new (x:T , flags:Flags ) ->Result <Self ,AllocError > { +let b =Self ::new_uninit (flags)?; +Ok (Box ::write (b, x)) + } + +/// Creates a new `Box<T, A>` with uninitialized contents. +/// +/// New memory is allocated with `a`. The allocation may fail, in which case an error is
Ditto.
Quoted 13 lines
rust/kernel/alloc/kbox.rs new file +13
-0+/// returned. For ZSTs no memory is allocated. +/// +/// # 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 > {
rust/kernel/alloc/kbox.rs line 256
Quoted 10 lines
rust/kernel/alloc/kbox.rs new file +10
-0+let ptr =if Self ::is_zst () { +NonNull ::dangling () + }else { +let layout = core::alloc::Layout ::new ::<MaybeUninit <T >>(); +let ptr =A ::alloc (layout, flags)?; + + ptr.cast () + }; + +Ok (Box (ptr,PhantomData ::<A >))
Missing INVARIANT comment.
---
Cheers,
Benno
Quoted 1 line
+ }
Danilo Krummrich
maintainerRe: [PATCH v5 09/26] rust: alloc: implement kernel `Box`
Quoted 9 lines · Benno Lossin
rust/kernel/alloc/kbox.rs line 22
Quoted 5 lines · Danilo Krummrich
rust/kernel/alloc/kbox.rs new file +5
-0+/// The kernel's [`Box`] type - a heap allocation for a single value of type `T`. +/// +/// This is the kernel's version of the Rust stdlib's `Box`. There are a couple of differences, +/// for example no `noalias` attribute is emitted and partially moving out of a `Box` is not +/// supported. I would add "But otherwise it works the same." (I don't know if there is a comma needed after the "otherwise").
There are more differences we don't list here, and probably don't need to. Hence, saying that it otherwise works the same isn't correct.
Quoted 3 lines
Also I remember that there was one more difference with a custom box compared to the stdlib, but I forgot what that was, does someone else remember? We should also put that here.
Obviously, there are also quite some API differences. For instance, Box generally requires two generics, value type and allocator, we take page flags and return a Result, where std just panics on failure.
Quoted 47 lines
Quoted 29 lines
rust/kernel/alloc/kbox.rs new file +28
-0+/// +/// `Box` works with any of the kernel's allocators, e.g. [`super::allocator::Kmalloc`], +/// [`super::allocator::Vmalloc`] or [`super::allocator::KVmalloc`]. There are aliases for `Box` +/// with these allocators ([`KBox`], [`VBox`], [`KVBox`]). +/// +/// When dropping a [`Box`], the value is also dropped and the heap memory is automatically freed. +/// +/// # Examples +/// +/// ``` +/// let b = KBox::<u64>::new(24_u64, GFP_KERNEL)?; +/// +/// assert_eq!(*b, 24_u64); +/// +/// # Ok::<(), Error>(()) +/// ``` +/// +/// ``` +/// # use kernel::bindings; +/// +/// const SIZE: usize = bindings::KMALLOC_MAX_SIZE as usize + 1; +/// struct Huge([u8; SIZE]); +/// +/// assert!(KBox::<Huge>::new_uninit(GFP_KERNEL | __GFP_NOWARN).is_err()); +/// ``` +/// +/// ``` +/// # use kernel::bindings; rust/kernel/alloc/kbox.rs line 60
Quoted 10 lines
rust/kernel/alloc/kbox.rs new file +10
-0+/// +/// const SIZE: usize = bindings::KMALLOC_MAX_SIZE as usize + 1; +/// struct Huge([u8; SIZE]); +/// +/// assert!(KVBox::<Huge>::new_uninit(GFP_KERNEL).is_ok()); +/// ``` +/// +/// # Invariants +/// +/// The [`Box`]' pointer always properly aligned and either points to memory allocated with `A` or, "pointer always properly" -> "pointer is properly"
Quoted 1 line
+/// for zero-sized types, is a dangling pointer.
I think this section would look nicer, if it were formatted using bullet points (that way the bracketing of the "or" is also unambiguous).
Quoted 6 lines
Additionally, this is missing that the pointer is valid for reads and writes.
Quoted 1 line
+pub struct Box<T: ?Sized, A: Allocator>(NonNull<T>, PhantomData<A>);
Why no
repr(transparent)?
I wasn't entirely sure whether that's OK with the additional PhantomData, but I think it is, gonna add it.
Quoted 5 lines
Review
Quoted 3 lines
(quoted diff) +2
-0+ +/// Type alias for `Box` with a `Kmalloc` allocator.I think we should add that this is only designed for small values.
I don't want duplicate the existing documentation around kmalloc and friends [1].
Maybe we can refer to the existing documentation somehow.
[1] https://www.kernel.org/doc/html/latest/core-api/memory-allocation.html
Quoted 11 lines
Quoted 11 lines
rust/kernel/alloc/kbox.rs new file +10
-0+/// +/// # Examples +/// +/// ``` +/// let b = KBox::new(24_u64, GFP_KERNEL)?; +/// +/// assert_eq!(*b, 24_u64); +/// +/// # Ok::<(), Error>(()) +/// ```
Quoted 8 lines
rust/kernel/alloc/kbox.rs line 77
Quoted 3 lines
rust/kernel/alloc/kbox.rs new file +3
-0+pub type KBox <T > =Box <T ,super ::allocator::Kmalloc >; + +/// Type alias for `Box` with a `Vmalloc` allocator. Same here, add that this is supposed to be used for big values (or is this also a general-purpose allocator, just not guaranteeing that the memory is physically contiguous? in that case I would document it here and also on
Vmalloc).
Same as above, I'd rather not duplicate that. But I'm happy to link things in, just not sure what's the best way doing it.
Re: [PATCH v5 09/26] rust: alloc: implement kernel `Box`
Quoted 7 lines · Danilo Krummrich
I don't want duplicate the existing documentation around kmalloc and friends [1].
Maybe we can refer to the existing documentation somehow.
[1] https://www.kernel.org/doc/html/latest/core-api/memory-allocation.html
Yeah, that is always a good idea. Sometimes we use the "Reference:
<https://...>" patterns, sometimes we linked to C functions/macros/etc. too (as if they were intra-doc links).
In the future, the plan is to have the C side docs connected as "external references" via a new rustdoc feature, so that we can just write intra-doc links that "magically" resolve to the right place to the C docs.
By the way, please use links to docs.kernel.org if possible, they are a bit nicer/shorter.
Cheers,
Miguel
Re: [PATCH v5 09/26] rust: alloc: implement kernel `Box`
In reply to Danilo KrummrichQuoted 14 lines · Danilo Krummrich
Quoted 9 lines · Benno Lossin
rust/kernel/alloc/kbox.rs line 22
Quoted 5 lines · Danilo Krummrich
rust/kernel/alloc/kbox.rs new file +5
-0+/// The kernel's [`Box`] type - a heap allocation for a single value of type `T`. +/// +/// This is the kernel's version of the Rust stdlib's `Box`. There are a couple of differences, +/// for example no `noalias` attribute is emitted and partially moving out of a `Box` is not +/// supported. I would add "But otherwise it works the same." (I don't know if there is a comma needed after the "otherwise").
There are more differences we don't list here, and probably don't need to. Hence, saying that it otherwise works the same isn't correct.
Quoted 7 lines
Quoted 3 lines
Also I remember that there was one more difference with a custom box compared to the stdlib, but I forgot what that was, does someone else remember? We should also put that here.
Obviously, there are also quite some API differences. For instance,
Boxgenerally requires two generics, value type and allocator, we take page flags and return aResult, where std just panics on failure.
Oh yeah that's true. The things listed above don't really refer to API stuff, so I didn't consider that. How about changing "couple differences" to "several differences"? Also adding that the APIs are different would not hurt.
Quoted 49 lines
Quoted 49 lines
Quoted 28 lines
rust/kernel/alloc/kbox.rs new file +28
-0+/// +/// `Box` works with any of the kernel's allocators, e.g. [`super::allocator::Kmalloc`], +/// [`super::allocator::Vmalloc`] or [`super::allocator::KVmalloc`]. There are aliases for `Box` +/// with these allocators ([`KBox`], [`VBox`], [`KVBox`]). +/// +/// When dropping a [`Box`], the value is also dropped and the heap memory is automatically freed. +/// +/// # Examples +/// +/// ``` +/// let b = KBox::<u64>::new(24_u64, GFP_KERNEL)?; +/// +/// assert_eq!(*b, 24_u64); +/// +/// # Ok::<(), Error>(()) +/// ``` +/// +/// ``` +/// # use kernel::bindings; +/// +/// const SIZE: usize = bindings::KMALLOC_MAX_SIZE as usize + 1; +/// struct Huge([u8; SIZE]); +/// +/// assert!(KBox::<Huge>::new_uninit(GFP_KERNEL | __GFP_NOWARN).is_err()); +/// ``` +/// +/// ``` +/// # use kernel::bindings; rust/kernel/alloc/kbox.rs line 60
Quoted 10 lines
rust/kernel/alloc/kbox.rs new file +10
-0+/// +/// const SIZE: usize = bindings::KMALLOC_MAX_SIZE as usize + 1; +/// struct Huge([u8; SIZE]); +/// +/// assert!(KVBox::<Huge>::new_uninit(GFP_KERNEL).is_ok()); +/// ``` +/// +/// # Invariants +/// +/// The [`Box`]' pointer always properly aligned and either points to memory allocated with `A` or, "pointer always properly" -> "pointer is properly"
Quoted 1 line
+/// for zero-sized types, is a dangling pointer.
I think this section would look nicer, if it were formatted using bullet points (that way the bracketing of the "or" is also unambiguous).
Additionally, this is missing that the pointer is valid for reads and writes.
Quoted 6 lines
Quoted 3 lines
Quoted 1 line
+pub struct Box<T: ?Sized, A: Allocator>(NonNull<T>, PhantomData<A>);
Why no
repr(transparent)?I wasn't entirely sure whether that's OK with the additional
PhantomData, but I think it is, gonna add it.
Yes it is fine, repr(transparent) requires that at most one field is non-ZST, but the type can have as many ZST fields as it wants. Otherwise the compiler will complain (there is no unsafe here, so just adding it is completely fine).
Quoted 11 lines
Quoted 4 lines
Review
Quoted 2 lines
(quoted diff) +2
-0+ +/// Type alias for `Box` with a `Kmalloc` allocator.I think we should add that this is only designed for small values.
I don't want duplicate the existing documentation around kmalloc and friends [1].
Maybe we can refer to the existing documentation somehow.
[1] https://www.kernel.org/doc/html/latest/core-api/memory-allocation.html
Oh great! With the C docs, I never know where to find them (is it in the code and do they exist?). Yeah let's just link it.
Quoted 19 lines
Quoted 10 lines
Quoted 10 lines
rust/kernel/alloc/kbox.rs new file +10
-0+/// +/// # Examples +/// +/// ``` +/// let b = KBox::new(24_u64, GFP_KERNEL)?; +/// +/// assert_eq!(*b, 24_u64); +/// +/// # Ok::<(), Error>(()) +/// ``` Quoted 8 lines
rust/kernel/alloc/kbox.rs line 77
Quoted 3 lines
rust/kernel/alloc/kbox.rs new file +3
-0+pub type KBox <T > =Box <T ,super ::allocator::Kmalloc >; + +/// Type alias for `Box` with a `Vmalloc` allocator. Same here, add that this is supposed to be used for big values (or is this also a general-purpose allocator, just not guaranteeing that the memory is physically contiguous? in that case I would document it here and also on
Vmalloc).
Quoted 2 lines
Same as above, I'd rather not duplicate that. But I'm happy to link things in, just not sure what's the best way doing it.
I took a look at the link and there is the "Selecting memory allocator" section, but there isn't really just a vmalloc or kmalloc section, it is rather stuff that we would put in the module documentation. What I would write on these types would be what to use these boxes for. eg large allocations, general purpose etc. I don't think that that is easily accessible from the docs that you linked above.
---
Cheers,
Benno
Danilo Krummrich
maintainerRe: [PATCH v5 09/26] rust: alloc: implement kernel `Box`
Quoted 19 lines · Benno Lossin
Quoted 18 lines · Danilo Krummrich
Quoted 9 lines · Benno Lossin
rust/kernel/alloc/kbox.rs line 22
Quoted 5 lines · Danilo Krummrich
rust/kernel/alloc/kbox.rs new file +5
-0+/// The kernel's [`Box`] type - a heap allocation for a single value of type `T`. +/// +/// This is the kernel's version of the Rust stdlib's `Box`. There are a couple of differences, +/// for example no `noalias` attribute is emitted and partially moving out of a `Box` is not +/// supported. I would add "But otherwise it works the same." (I don't know if there is a comma needed after the "otherwise").
There are more differences we don't list here, and probably don't need to. Hence, saying that it otherwise works the same isn't correct.
Quoted 3 lines
Also I remember that there was one more difference with a custom box compared to the stdlib, but I forgot what that was, does someone else remember? We should also put that here.
Quoted 9 lines
Quoted 3 lines
Obviously, there are also quite some API differences. For instance,
Boxgenerally requires two generics, value type and allocator, we take page flags and return aResult, where std just panics on failure.Oh yeah that's true. The things listed above don't really refer to API stuff, so I didn't consider that. How about changing "couple differences" to "several differences"? Also adding that the APIs are different would not hurt.
Sure.
Quoted 35 lines
Quoted 6 lines
Quoted 5 lines
Review
Quoted 3 lines
(quoted diff) +2
-0+ +/// Type alias for `Box` with a `Kmalloc` allocator.I think we should add that this is only designed for small values.
Quoted 6 lines
I don't want duplicate the existing documentation around kmalloc and friends [1].
Maybe we can refer to the existing documentation somehow.
[1] https://www.kernel.org/doc/html/latest/core-api/memory-allocation.html
Oh great! With the C docs, I never know where to find them (is it in the code and do they exist?). Yeah let's just link it.
Quoted 19 lines
Quoted 18 lines
rust/kernel/alloc/kbox.rs line 77
Quoted 13 lines
rust/kernel/alloc/kbox.rs new file +13
-0+/// +/// # 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. Same here, add that this is supposed to be used for big values (or is this also a general-purpose allocator, just not guaranteeing that the memory is physically contiguous? in that case I would document it here and also on
Vmalloc).
Quoted 6 lines
Quoted 2 lines
Same as above, I'd rather not duplicate that. But I'm happy to link things in, just not sure what's the best way doing it.
I took a look at the link and there is the "Selecting memory allocator" section, but there isn't really just a vmalloc or kmalloc section, it is rather stuff that we would put in the module documentation.
There are no dedicated sections, but...
Quoted 3 lines
What I would write on these types would be what to use these boxes for. eg large allocations, general purpose etc. I don't think that that is easily accessible from the docs that you linked above.
...this stuff should be covered by the document, e.g.:
"The maximal size of a chunk that can be allocated with kmalloc is limited. The actual limit depends on the hardware and the kernel configuration, but it is a good practice to use kmalloc for objects smaller than page size."
or
"For large allocations you can use vmalloc() and vzalloc(), or directly request pages from the page allocator. The memory allocated by vmalloc and related functions is not physically contiguous."
I'd probably reference [1] in the module documentation as you say and otherwise refer to the C APIs, just like RBTree does.
Quoted 5 lines
---
Cheers,
Benno
Re: [PATCH v5 09/26] rust: alloc: implement kernel `Box`
Quoted 42 lines · Danilo Krummrich
Quoted 22 lines · Benno Lossin
Quoted 21 lines · Danilo Krummrich
Quoted 19 lines · Benno Lossin
rust/kernel/alloc/kbox.rs line 77
Quoted 13 lines · Danilo Krummrich
rust/kernel/alloc/kbox.rs new file +13
-0+/// +/// # 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. Same here, add that this is supposed to be used for big values (or is this also a general-purpose allocator, just not guaranteeing that the memory is physically contiguous? in that case I would document it here and also on
Vmalloc).Quoted 6 lines
Quoted 2 lines
Same as above, I'd rather not duplicate that. But I'm happy to link things in, just not sure what's the best way doing it.
I took a look at the link and there is the "Selecting memory allocator" section, but there isn't really just a vmalloc or kmalloc section, it is rather stuff that we would put in the module documentation.
There are no dedicated sections, but...
Quoted 3 lines
What I would write on these types would be what to use these boxes for. eg large allocations, general purpose etc. I don't think that that is easily accessible from the docs that you linked above.
...this stuff should be covered by the document, e.g.:
"The maximal size of a chunk that can be allocated with kmalloc is limited. The actual limit depends on the hardware and the kernel configuration, but it is a good practice to use kmalloc for objects smaller than page size."
Quoted 5 lines
or
"For large allocations you can use
vmalloc()andvzalloc(), or directly request pages from the page allocator. The memory allocated by vmalloc and related functions is not physically contiguous."
Yeah, but for that you have to read big chunks of the document and item docs in Rust are often very short, since you only need to know what that one item does.
Would be a good idea to talk about how we can improve this at Kangrejos.
---
Cheers,
Benno
Danilo Krummrich
maintainerRe: [PATCH v5 09/26] rust: alloc: implement kernel `Box`
Quoted 45 lines · Benno Lossin
Quoted 44 lines · Danilo Krummrich
Quoted 22 lines · Benno Lossin
Quoted 21 lines · Danilo Krummrich
Quoted 19 lines · Benno Lossin
rust/kernel/alloc/kbox.rs line 77
Quoted 13 lines · Danilo Krummrich
rust/kernel/alloc/kbox.rs new file +13
-0+/// +/// # 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. Same here, add that this is supposed to be used for big values (or is this also a general-purpose allocator, just not guaranteeing that the memory is physically contiguous? in that case I would document it here and also on
Vmalloc).Quoted 6 lines
Quoted 2 lines
Same as above, I'd rather not duplicate that. But I'm happy to link things in, just not sure what's the best way doing it.
I took a look at the link and there is the "Selecting memory allocator" section, but there isn't really just a vmalloc or kmalloc section, it is rather stuff that we would put in the module documentation.
There are no dedicated sections, but...
Quoted 3 lines
What I would write on these types would be what to use these boxes for. eg large allocations, general purpose etc. I don't think that that is easily accessible from the docs that you linked above.
...this stuff should be covered by the document, e.g.:
"The maximal size of a chunk that can be allocated with kmalloc is limited. The actual limit depends on the hardware and the kernel configuration, but it is a good practice to use kmalloc for objects smaller than page size."
or
Quoted 7 lines
Quoted 3 lines
"For large allocations you can use
vmalloc()andvzalloc(), or directly request pages from the page allocator. The memory allocated by vmalloc and related functions is not physically contiguous."Yeah, but for that you have to read big chunks of the document and item docs in Rust are often very short, since you only need to know what that one item does.
In this specific case I'd argue that the contents of this document should be read and understood by everyone who attempts to write kernel code anyways, hence linking it should be fine.
Quoted 6 lines
Would be a good idea to talk about how we can improve this at Kangrejos.
---
Cheers,
Benno
Danilo Krummrich
maintainerPATCH v5 10/26 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.
--- drivers/block/rnull.rs | 4 +-- rust/kernel/init.rs | 51 ++++++++++++++++--------------- 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 ++++++------ rust/macros/lib.rs | 6 ++-- 9 files changed, 54 insertions(+), 54 deletions(-)
drivers/block/rnull.rs +2 -2
@@ -32,7 +32,7 @@ }struct NullBlkModule { - _disk:Pin <Box <Mutex <GenDisk <NullBlkDevice >>>>, + _disk:Pin <KBox <Mutex <GenDisk <NullBlkDevice >>>>, }impl kernel::Module for NullBlkModule {
@@ -47,7 +47,7 @@ fn init(_module: &'static ThisModule) -> Result<Self> { .rotational(false) .build (format_args! ("rnullb{}" ,0 ), tagset)?; -let disk =Box ::pin_init (new_mutex! (disk,"nullb:disk" ), flags::GFP_KERNEL )?; +let disk =KBox ::pin_init (new_mutex! (disk,"nullb:disk" ), flags::GFP_KERNEL )?;Ok (Self { _disk: disk }) }
rust/kernel/init.rs +26 -25
178 lines not on this page · raw message
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) };
rust/macros/lib.rs +3 -3
@@ -239,7 +239,7 @@ pub fn concat_idents(ts: TokenStream) -> TokenStream {/// struct DriverData { /// #[pin] /// queue: Mutex<Vec<Command>>, -/// buf: Box<[u8; 1024 * 1024]>, +/// buf: KBox<[u8; 1024 * 1024]>, /// } /// ``` ///
@@ -248,7 +248,7 @@ pub fn concat_idents(ts: TokenStream) -> TokenStream {/// struct DriverData { /// #[pin] /// queue: Mutex<Vec<Command>>, -/// buf: Box<[u8; 1024 * 1024]>, +/// buf: KBox<[u8; 1024 * 1024]>, /// raw_info: *mut Info, /// } ///
@@ -278,7 +278,7 @@ pub fn pin_data(inner: TokenStream, item: TokenStream) -> TokenStream {/// struct DriverData { /// #[pin] /// queue: Mutex<Vec<Command>>, -/// buf: Box<[u8; 1024 * 1024]>, +/// buf: KBox<[u8; 1024 * 1024]>, /// raw_info: *mut Info, /// } ///
Signature · 1 line
2.45.2
Danilo Krummrich
maintainerPATCH v5 11/26 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 and all other related extensions.
--- 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 ,Flags ,KBox }, + alloc::{AllocError ,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 >InPlaceInit <T >for UniqueArc <T > {#[inline] fn try_pin_init <E >(init:impl PinInit <T ,E >, flags:Flags ) ->Result <Pin <Self >,E >
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
@@ -3,7 +3,6 @@//! Kernel types. use crate ::init::{self,PinInit }; -use alloc::boxed::Box ;use core::{ cell::UnsafeCell , marker::{PhantomData ,PhantomPinned },
@@ -67,28 +66,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 ForeignOwnable for () {type Borrowed <'a> = ();
Signature · 1 line
2.45.2
Re: [PATCH v5 11/26] rust: alloc: remove `BoxExt` extension
Quoted 2 lines · Danilo Krummrich
Now that all existing
Boxusers were moved to the kernelBoxtype, remove theBoxExtextension and all other related extensions.
I just noticed that in the recent 'rust-dev' branch we have a change which *adds* something to BoxExt:
rust: kernel: add drop_contents to BoxExt
https://github.com/Rust-for-Linux/linux/commit/62c34da1da6c01a635ea2308cb42996d0571059e
I'm unclear how relevant that is. Just want to mention this in case it would make sense to include that directly in this patch series to avoid a future add-on patch ;)
Thanks,
Dirk
P.S.: It looks like anything like this at least makes the compiler happy:
rust/kernel/alloc/kbox.rs +23 -0
@@ -9,6 +9,7 @@use core::mem::MaybeUninit ;use core::ops::{Deref ,DerefMut };use core::pin::Pin ; +use core::ptr;use core::ptr::NonNull ;use core::result::Result ;
@@ -270,6 +271,28 @@ pub fn new_uninit(flags: Flags) -> Result<Box<MaybeUninit<T>, A>, AllocError> {Ok (Box (ptr,PhantomData ::<A >)) } +/// Drops the contents, but keeps the allocation. +/// +/// # Examples +/// +/// ``` +/// use kernel::alloc::{Flags, KBox}; +/// 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]); +/// 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 { ptr::drop_in_place (ptr) }; +// SAFETY: `ptr` is valid, because it came from `Box::into_raw`. +unsafe {Box ::from_raw (ptr.cast ()) } + } +/// Constructs a new `Pin<Box<T, A>>`. If `T` does not implement
[Unpin], then x will be
/// pinned in memory and can't be moved.
#[inline]Danilo Krummrich
maintainerRe: [PATCH v5 11/26] rust: alloc: remove `BoxExt` extension
Hi Dirk,
Quoted 9 lines · Dirk Behme
Quoted 2 lines · Danilo Krummrich
Now that all existing
Boxusers were moved to the kernelBoxtype, remove theBoxExtextension and all other related extensions.I just noticed that in the recent 'rust-dev' branch we have a change which *adds* something to BoxExt:
rust: kernel: add
drop_contentsto BoxExt
https://github.com/Rust-for-Linux/linux/commit/62c34da1da6c01a635ea2308cb42996d0571059eI'm unclear how relevant that is. Just want to mention this in case it would make sense to include that directly in this patch series to avoid a future add-on patch ;)
Thanks for reporting. I'm aware of this patch, we expect it to land before this series. I'll add drop_contents once I rebase onto it.
- Danilo
Quoted 48 lines
Thanks,
Dirk
P.S.: It looks like anything like this at least makes the compiler happy:rust/kernel/alloc/kbox.rs +23
-0@@ -9,6 +9,7 @@ use core::mem::MaybeUninit; use core::ops::{Deref, DerefMut}; use core::pin::Pin; +use core::ptr; use core::ptr::NonNull; use core::result::Result;@@ -270,6 +271,28 @@ pub fn new_uninit(flags: Flags) -> Result<Box<MaybeUninit<T>, A>, AllocError> { Ok(Box(ptr, PhantomData::<A>)) } + /// Drops the contents, but keeps the allocation. + /// + /// # Examples + /// + /// ``` + /// use kernel::alloc::{Flags, KBox}; + /// 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]); + /// 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 { ptr::drop_in_place(ptr) }; + // SAFETY: `ptr` is valid, because it came from `Box::into_raw`. + unsafe { Box::from_raw(ptr.cast()) } + } + /// Constructs a new `Pin<Box<T, A>>`. If `T` does not implement [`Unpin`], then `x` will be /// pinned in memory and can't be moved. #[inline]
Danilo Krummrich
maintainerPATCH v5 12/26 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
Danilo Krummrich
maintainerPATCH v5 13/26 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 | 613 ++++++++++++++++++++++++++++++++++++++ rust/kernel/prelude.rs | 2 +- 4 files changed, 635 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 ;
@@ -183,6 +183,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 +613 -0
613 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
Alice Ryhl
maintainerRe: [PATCH v5 13/26] rust: alloc: implement kernel `Vec` type
Quoted 12 lines · Danilo Krummrich
Vecprovides a contiguous growable array type (such asVec) with contents allocated with the kernel's allocators (e.g.Kmalloc,VmallocorKVmalloc).In contrast to Rust's
Vectype, the kernelVectype 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/kbox.rs pub fn into_pin(b: Self) -> Pin<Self> { · line 191
Quoted 6 lines
rust/kernel/alloc/kbox.rs +6
-0+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 > {
Nit: I would probably make this a From impl.
Quoted 7 lines
rust/kernel/alloc/kvec.rs new file +7
-0+#[macro_export] +macro_rules! kvec { + () => ( + { + $crate::alloc::KVec ::new () + } + );
rust/kernel/alloc/kvec.rs line 54
Quoted 10 lines
rust/kernel/alloc/kvec.rs new file +10
-0+ ($elem:expr; $n:expr) => ( + { + $crate::alloc::KVec ::from_elem ($elem, $n,GFP_KERNEL ) + } + ); + ($($x:expr),+ $(,)?) => ( + { + match $crate::alloc::KBox::new([$($x),+], GFP_KERNEL) { +Ok (b) =>Ok ($crate::alloc::KBox ::into_vec (b)), +Err (e) =>Err (e),
Hmm. This currently generates code that:
1. Creates the array.
2. Allocates the memory.
3. Moves the array into the box.
Whereas the stdlib macro swaps step 1 and 2. You can do the same by utilizing new_uninit. A sketch:
match KBox::<[_; _]>::new_uninit(GFP_KERNEL) {
Ok(b) => Ok(KVec::from(KBox::write(b, [$($x),+]))),
Err(e) => Err(e),
}rust/kernel/alloc/kvec.rs line 160
Quoted 13 lines
rust/kernel/alloc/kvec.rs new file +13
-0+// SAFETY: `Vec` is `Send` if `T` is `Send` because the data referenced by `self.ptr` is unaliased. +unsafe impl <T ,A >Send for Vec <T ,A > +where +T :Send , +A :Allocator , +{ +} + +// SAFETY: `Vec` is `Sync` if `T` is `Sync` because the data referenced by `self.ptr` is unaliased. +unsafe impl <T ,A >Sync for Vec <T ,A > +where +T :Send , +A :Allocator ,
Same comment as Box. Ditto about "unaliased". Needs T: Sync instead.
rust/kernel/alloc/kvec.rs line 483
Quoted 8 lines
rust/kernel/alloc/kvec.rs new file +8
-0+impl <T :Clone ,A :Allocator >Vec <T ,A > { +/// Extend the vector by `n` clones of value. +pub fn extend_with (&mut self, n:usize , value:T , flags:Flags ) ->Result <(),AllocError > { + self.reserve (n, flags)?; + +let spare = self.spare_capacity_mut (); + +for itemin spare.iter_mut ().take (n -1 ) {
You need to handle n == 0 here.
Alice
Danilo Krummrich
maintainerRe: [PATCH v5 13/26] rust: alloc: implement kernel `Vec` type
Quoted 11 lines · Alice Ryhl
Quoted 10 lines · Danilo Krummrich
Vecprovides a contiguous growable array type (such asVec) with contents allocated with the kernel's allocators (e.g.Kmalloc,VmallocorKVmalloc).In contrast to Rust's
Vectype, the kernelVectype considers the kernel's GFP flags for all appropriate functions, always reports allocation failures throughResult<_, AllocError>and remains independent from unstable features.
Quoted 10 lines
rust/kernel/alloc/kbox.rs pub fn into_pin(b: Self) -> Pin<Self> { · line 191
Quoted 8 lines
Signed-off-by Danilo Krummrich <[email protected]>[...]rust/kernel/alloc/kbox.rs +6
-0+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 > {Nit: I would probably make this a From impl.
Please see my reply to patch 9 of this series.
Quoted 25 lines
Quoted 8 lines
rust/kernel/alloc/kvec.rs new file +7
-0+#[macro_export] +macro_rules! kvec { + () => ( + { + $crate::alloc::KVec ::new () + } + );rust/kernel/alloc/kvec.rs line 54
Quoted 10 lines
rust/kernel/alloc/kvec.rs new file +10
-0+ ($elem:expr; $n:expr) => ( + { + $crate::alloc::KVec ::from_elem ($elem, $n,GFP_KERNEL ) + } + ); + ($($x:expr),+ $(,)?) => ( + { + match $crate::alloc::KBox::new([$($x),+], GFP_KERNEL) { +Ok (b) =>Ok ($crate::alloc::KBox ::into_vec (b)), +Err (e) =>Err (e),Hmm. This currently generates code that:
1. Creates the array.
2. Allocates the memory.
3. Moves the array into the box.
Quoted 7 lines
Whereas the stdlib macro swaps step 1 and 2. You can do the same by utilizing
new_uninit. A sketch:match KBox::<[_; _]>::new_uninit(GFP_KERNEL) { Ok(b) => Ok(KVec::from(KBox::write(b, [$($x),+]))), Err(e) => Err(e), }
Generally, I'm fine changing that, but what's the reason for the suggestion? It shouldn't make a difference, does it?
Quoted 17 lines
Quoted 4 lines
rust/kernel/alloc/kvec.rs new file +3
-0+// SAFETY: `Vec` is `Send` if `T` is `Send` because the data referenced by `self.ptr` is unaliased. +unsafe impl <T ,A >Send for Vec <T ,A > +where rust/kernel/alloc/kvec.rs line 160
Quoted 10 lines
rust/kernel/alloc/kvec.rs new file +10
-0+ T: Send, + A: Allocator, +{ +} + +// SAFETY: `Vec` is `Sync` if `T` is `Sync` because the data referenced by `self.ptr` is unaliased. +unsafe impl<T, A> Sync for Vec<T, A> +where +T :Send , +A :Allocator ,Same comment as Box. Ditto about "unaliased". Needs
T: Syncinstead.
Quoted 10 lines
rust/kernel/alloc/kvec.rs line 483
Quoted 8 lines
rust/kernel/alloc/kvec.rs new file +8
-0+impl <T :Clone ,A :Allocator >Vec <T ,A > { +/// Extend the vector by `n` clones of value. +pub fn extend_with (&mut self, n:usize , value:T , flags:Flags ) ->Result <(),AllocError > { + self.reserve (n, flags)?; + +let spare = self.spare_capacity_mut (); + +for itemin spare.iter_mut ().take (n -1 ) {You need to handle
n == 0here.
Gonna fix.
Quoted 3 lines
Alice
Alice Ryhl
maintainerRe: [PATCH v5 13/26] rust: alloc: implement kernel `Vec` type
Quoted 30 lines · Danilo Krummrich
Quoted 28 lines · Alice Ryhl
Quoted 7 lines · Danilo Krummrich
rust/kernel/alloc/kvec.rs new file +7
-0+#[macro_export] +macro_rules! kvec { + () => ( + { + $crate::alloc::KVec ::new () + } + );rust/kernel/alloc/kvec.rs line 54
Quoted 10 lines
rust/kernel/alloc/kvec.rs new file +10
-0+ ($elem:expr; $n:expr) => ( + { + $crate::alloc::KVec ::from_elem ($elem, $n,GFP_KERNEL ) + } + ); + ($($x:expr),+ $(,)?) => ( + { + match $crate::alloc::KBox::new([$($x),+], GFP_KERNEL) { +Ok (b) =>Ok ($crate::alloc::KBox ::into_vec (b)), +Err (e) =>Err (e),Hmm. This currently generates code that:
1. Creates the array.
2. Allocates the memory.
3. Moves the array into the box.Whereas the stdlib macro swaps step 1 and 2. You can do the same by utilizing
new_uninit. A sketch:
Quoted 7 lines
Quoted 4 lines
match KBox::<[_; _]>::new_uninit(GFP_KERNEL) { Ok(b) => Ok(KVec::from(KBox::write(b, [$($x),+]))), Err(e) => Err(e), }Generally, I'm fine changing that, but what's the reason for the suggestion? It shouldn't make a difference, does it?
The compiler is much more likely to not put the array on the stack before it is copied to the heap.
In the case where $x constructs new values, it also avoids create-then-destroy on allocator failure.
Alice
Re: [PATCH v5 13/26] rust: alloc: implement kernel `Vec` type
Quoted 36 lines · Alice Ryhl
Quoted 35 lines · Danilo Krummrich
Quoted 32 lines · Alice Ryhl
Quoted 7 lines · Danilo Krummrich
rust/kernel/alloc/kvec.rs new file +7
-0+#[macro_export] +macro_rules! kvec { + () => ( + { + $crate::alloc::KVec ::new () + } + );rust/kernel/alloc/kvec.rs line 54
Quoted 10 lines
rust/kernel/alloc/kvec.rs new file +10
-0+ ($elem:expr; $n:expr) => ( + { + $crate::alloc::KVec ::from_elem ($elem, $n,GFP_KERNEL ) + } + ); + ($($x:expr),+ $(,)?) => ( + { + match $crate::alloc::KBox::new([$($x),+], GFP_KERNEL) { +Ok (b) =>Ok ($crate::alloc::KBox ::into_vec (b)), +Err (e) =>Err (e),Hmm. This currently generates code that:
1. Creates the array.
2. Allocates the memory.
3. Moves the array into the box.Whereas the stdlib macro swaps step 1 and 2. You can do the same by utilizing
new_uninit. A sketch:match KBox::<[_; _]>::new_uninit(GFP_KERNEL) { Ok(b) => Ok(KVec::from(KBox::write(b, [$($x),+]))), Err(e) => Err(e), }
Quoted 8 lines
Quoted 2 lines
Generally, I'm fine changing that, but what's the reason for the suggestion? It shouldn't make a difference, does it?
The compiler is much more likely to not put the array on the stack before it is copied to the heap.
In the case where $x constructs new values, it also avoids create-then-destroy on allocator failure.
If we are this worried about not putting the array on the stack, then we could also do this using init!, but the macro still would need to count the number of elements in the array.
@Danilo, from the other thread:
Quoted 2 lines
How do we get the size here?
#![feature(generic_arg_infer)]seems to be unstable.
You can use a macro to count the number of elements:
macro_rules! count_exprs {
() => { 0 };
($e:expr $(,$rest:expr)* $(,)?) => {
1 + count_exprs!($($rest,)*)
};
}And then do
match KBox::<[_; count_exprs!($($x),+)]>::new_uninit(GFP_KERNEL) {
Ok(b) => Ok(KVec::from(KBox::write(b, [$($x),+]))),
Err(e) => Err(e),
}---
Cheers,
Benno
Danilo Krummrich
maintainerRe: [PATCH v5 13/26] rust: alloc: implement kernel `Vec` type
In reply to Alice RyhlQuoted 20 lines · Alice Ryhl
Quoted 7 lines
rust/kernel/alloc/kvec.rs new file +7
-0+#[macro_export] +macro_rules! kvec { + () => ( + { + $crate::alloc::KVec ::new () + } + );rust/kernel/alloc/kvec.rs line 54
Quoted 10 lines
rust/kernel/alloc/kvec.rs new file +10
-0+ ($elem:expr; $n:expr) => ( + { + $crate::alloc::KVec ::from_elem ($elem, $n,GFP_KERNEL ) + } + ); + ($($x:expr),+ $(,)?) => ( + { + match $crate::alloc::KBox::new([$($x),+], GFP_KERNEL) { +Ok (b) =>Ok ($crate::alloc::KBox ::into_vec (b)), +Err (e) =>Err (e),Hmm. This currently generates code that:
Quoted 5 lines
1. Creates the array.
2. Allocates the memory.
3. Moves the array into the box.Whereas the stdlib macro swaps step 1 and 2.
Isn't stdlib [1] doing the same thing I do?
[1] https://doc.rust-lang.org/1.80.1/src/alloc/macros.rs.html#49
Quoted 3 lines
You can do the same by utilizing
new_uninit. A sketch:match KBox::<[_; _]>::new_uninit(GFP_KERNEL) {
How do we get the size here? #![feature(generic_arg_infer)] seems to be unstable.
Quoted 3 lines
Ok(b) => Ok(KVec::from(KBox::write(b, [$($x),+]))),
Err(e) => Err(e),
}Alice Ryhl
maintainerRe: [PATCH v5 13/26] rust: alloc: implement kernel `Vec` type
Quoted 26 lines · Danilo Krummrich
Quoted 24 lines · Alice Ryhl
Quoted 7 lines
rust/kernel/alloc/kvec.rs new file +7
-0+#[macro_export] +macro_rules! kvec { + () => ( + { + $crate::alloc::KVec ::new () + } + );rust/kernel/alloc/kvec.rs line 54
Quoted 10 lines
rust/kernel/alloc/kvec.rs new file +10
-0+ ($elem:expr; $n:expr) => ( + { + $crate::alloc::KVec ::from_elem ($elem, $n,GFP_KERNEL ) + } + ); + ($($x:expr),+ $(,)?) => ( + { + match $crate::alloc::KBox::new([$($x),+], GFP_KERNEL) { +Ok (b) =>Ok ($crate::alloc::KBox ::into_vec (b)), +Err (e) =>Err (e),Hmm. This currently generates code that:
1. Creates the array.
2. Allocates the memory.
3. Moves the array into the box.
Quoted 5 lines
Quoted 1 line
Whereas the stdlib macro swaps step 1 and 2.
Isn't stdlib [1] doing the same thing I do?
[1] https://doc.rust-lang.org/1.80.1/src/alloc/macros.rs.html#49
Stdlib is using something called #[rustc_box] which has the effect I described.
Quoted 6 lines
Quoted 3 lines
You can do the same by utilizing
new_uninit. A sketch:match KBox::<[_; _]>::new_uninit(GFP_KERNEL) {
How do we get the size here?
#![feature(generic_arg_infer)]seems to be unstable.
It probably works if you don't specify the type at all:KBox::new_uninit. But you should double check.
Quoted 3 lines
Quoted 3 lines
Ok(b) => Ok(KVec::from(KBox::write(b, [$($x),+]))), Err(e) => Err(e), }
Danilo Krummrich
maintainerRe: [PATCH v5 13/26] rust: alloc: implement kernel `Vec` type
Quoted 37 lines · Alice Ryhl
Quoted 26 lines · Danilo Krummrich
Quoted 24 lines · Alice Ryhl
Quoted 7 lines
rust/kernel/alloc/kvec.rs new file +7
-0+#[macro_export] +macro_rules! kvec { + () => ( + { + $crate::alloc::KVec ::new () + } + );rust/kernel/alloc/kvec.rs line 54
Quoted 10 lines
rust/kernel/alloc/kvec.rs new file +10
-0+ ($elem:expr; $n:expr) => ( + { + $crate::alloc::KVec ::from_elem ($elem, $n,GFP_KERNEL ) + } + ); + ($($x:expr),+ $(,)?) => ( + { + match $crate::alloc::KBox::new([$($x),+], GFP_KERNEL) { +Ok (b) =>Ok ($crate::alloc::KBox ::into_vec (b)), +Err (e) =>Err (e),Hmm. This currently generates code that:
1. Creates the array.
2. Allocates the memory.
3. Moves the array into the box.Quoted 5 lines
Quoted 1 line
Whereas the stdlib macro swaps step 1 and 2.
Isn't stdlib [1] doing the same thing I do?
[1] https://doc.rust-lang.org/1.80.1/src/alloc/macros.rs.html#49
Stdlib is using something called #[rustc_box] which has the effect I described.
Quoted 2 lines
Quoted 2 lines
You can do the same by utilizing
new_uninit. A sketch:
Quoted 7 lines
Quoted 4 lines
Quoted 1 line
match KBox::<[_; _]>::new_uninit(GFP_KERNEL) {
How do we get the size here?
#![feature(generic_arg_infer)]seems to be unstable.It probably works if you don't specify the type at all:
KBox::new_uninit. But you should double check.
That works, thanks.
Quoted 5 lines
Quoted 4 lines
Quoted 4 lines
Ok(b) => Ok(KVec::from(KBox::write(b, [$($x),+]))), Err(e) => Err(e), }
Danilo Krummrich
maintainerPATCH v5 14/26 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 | 184 ++++++++++++++++++++++++++++++++++++++ 2 files changed, 185 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 +184 -0
193 lines not on this page · raw message
Signature · 1 line
2.45.2
Danilo Krummrich
maintainerPATCH v5 15/26 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
@@ -665,6 +665,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 v5 16/26 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 | 17 +++++++---------- rust/macros/lib.rs | 6 +++--- samples/rust/rust_minimal.rs | 4 ++-- 6 files changed, 19 insertions(+), 24 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 )?; + buf.extend_from_slice (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
@@ -132,7 +132,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 +7 -10
@@ -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,21 +75,21 @@/// 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); /// } /// /// let read = UserSlice::new(uptr, len).reader(); /// -/// let mut buf = Vec::new(); +/// let mut buf = KVec::new(); /// read.read_all(&mut buf, GFP_KERNEL)?; /// /// // THIS IS A BUG! The bytes could have changed since we checked them.
@@ -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)?; + buf.reserve (len, flags)?;// The call to `try_reserve` was successful, so the spare capacity is at least `len` bytes // long.
rust/macros/lib.rs +3 -3
@@ -238,7 +238,7 @@ pub fn concat_idents(ts: TokenStream) -> TokenStream {/// #[pin_data] /// struct DriverData { /// #[pin] -/// queue: Mutex<Vec<Command>>, +/// queue: Mutex<KVec<Command>>, /// buf: KBox<[u8; 1024 * 1024]>, /// } /// ```
@@ -247,7 +247,7 @@ pub fn concat_idents(ts: TokenStream) -> TokenStream {/// #[pin_data(PinnedDrop)] /// struct DriverData { /// #[pin] -/// queue: Mutex<Vec<Command>>, +/// queue: Mutex<KVec<Command>>, /// buf: KBox<[u8; 1024 * 1024]>, /// raw_info: *mut Info, /// }
@@ -277,7 +277,7 @@ pub fn pin_data(inner: TokenStream, item: TokenStream) -> TokenStream {/// #[pin_data(PinnedDrop)] /// struct DriverData { /// #[pin] -/// queue: Mutex<Vec<Command>>, +/// queue: Mutex<KVec<Command>>, /// buf: KBox<[u8; 1024 * 1024]>, /// raw_info: *mut Info, /// }
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
Danilo Krummrich
maintainerPATCH v5 17/26 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
Danilo Krummrich
maintainerPATCH v5 18/26 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
Danilo Krummrich
maintainerPATCH v5 19/26 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
Danilo Krummrich
maintainerPATCH v5 20/26 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 Error 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
Danilo Krummrich
maintainerPATCH v5 21/26 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
Danilo Krummrich
maintainerPATCH v5 22/26 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.
It's on the test infrastructure to find a way to deal with it, even if it's more complicated.
--- rust/kernel/alloc/allocator_test.rs | 175 ++++++++++++++++++++++++++-- 1 file changed, 168 insertions(+), 7 deletions(-)
rust/kernel/alloc/allocator_test.rs +168 -7
@@ -2,20 +2,181 @@#![allow(missing_docs)] -use super ::{AllocError ,Allocator ,Flags }; +use super ::{flags::*,AllocError ,Allocator ,Flags };use core::alloc::Layout ; +use core::cmp; +use core::mem; +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 ); +} + +struct CmallocData { +// The actual size as requested through `Cmalloc::alloc` or `Cmalloc::realloc`. + size:usize , +// The offset from the pointer returned to the caller of `Cmalloc::alloc` or `Cmalloc::realloc` +// to the actual base address of the allocation. + offset:usize , +} + +impl Cmalloc { +/// Adjust the size and alignment such that we can additionally store `CmallocData` right +/// before the actual data described by `layout`. +/// +/// Example: +/// +/// For `CmallocData` assume an alignment of 8 and a size of 16. +/// For `layout` assume and alignment of 16 and a size of 64. +/// +/// 0 16 32 96 +/// |----------------|----------------|------------------------------------------------| +/// empty CmallocData data +/// +/// For this example the returned `Layout` has an alignment of 32 and a size of 96. +fn layout_adjust (layout:Layout ) ->Result <Layout ,AllocError > { +let layout = layout.pad_to_align (); + +// Ensure that `CmallocData` fits into half the alignment. Additionally, this guarantees +// that advancing a pointer aligned to `align` by `align / 2` we still satisfy or exceed +// the alignment requested through `layout`. +let align = cmp::max ( + layout.align (), + mem::size_of ::<CmallocData >().next_power_of_two (), + ) *2 ; + +// Add the additional space required for `CmallocData`. +let size = layout.size () + mem::size_of ::<CmallocData >(); + +Ok (Layout ::from_size_align (size, align) + .map_err (|_|AllocError )? + .pad_to_align ()) + } + +fn alloc_store_data (layout:Layout ) ->Result <NonNull <u8 >,AllocError > { +let requested_size = layout.size (); + +let layout =Self ::layout_adjust (layout)?; +let min_align = layout.align () /2 ; + +// 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 ; + +let priv_ptr =NonNull ::new (raw_ptr).ok_or (AllocError )?; + +// SAFETY: Advance the pointer by `min_align`. The adjustments from `Self::layout_adjust` +// ensure that after this operation the original size and alignment requirements are still +// satisfied or exceeded. +let ptr =unsafe { priv_ptr.as_ptr ().add (min_align) }; + +// SAFETY: `min_align` is greater than or equal to the size of `CmallocData`, hence we +// don't exceed the allocation boundaries. +let data_ptr: *mut CmallocData =unsafe { ptr.sub (mem::size_of ::<CmallocData >()) }.cast (); + +let data =CmallocData { + size: requested_size, + offset: min_align, + }; + +// SAFETY: `data_ptr` is properly aligned and within the allocation boundaries reserved for +// `CmallocData`. +unsafe { data_ptr.write (data) }; + +NonNull ::new (ptr).ok_or (AllocError ) + } + +/// # Safety +/// +/// `ptr` must have been previously allocated with `Self::alloc_store_data`. +unsafe fn data <'a>(ptr:NonNull <u8 >) -> &'aCmallocData { +// SAFETY: `Self::alloc_store_data` stores the `CmallocData` right before the address +// returned to callers of `Self::alloc_store_data`. +let data_ptr: *mut CmallocData = +unsafe { ptr.as_ptr ().sub (mem::size_of ::<CmallocData >()) }.cast (); + +// SAFETY: The `CmallocData` has been previously stored at this offset with +// `Self::alloc_store_data`. +unsafe { &*data_ptr } + } + +/// # Safety +/// +/// This function must not be called more than once for the same allocation. +/// +/// `ptr` must have been previously allocated with `Self::alloc_store_data`. +unsafe fn free_read_data (ptr:NonNull <u8 >) { +// SAFETY: `ptr` has been created by `Self::alloc_store_data`. +let data =unsafe {Self ::data (ptr) }; + +// SAFETY: `ptr` has been created by `Self::alloc_store_data`. +let priv_ptr =unsafe { ptr.as_ptr ().sub (data.offset) }; + +// SAFETY: `priv_ptr` has previously been allocatored with this `Allocator`. +unsafe {libc_free (priv_ptr.cast ()) }; + } +} + +unsafe impl Allocator for Cmalloc { +fn alloc (layout:Layout , flags:Flags ) ->Result <NonNull <[u8 ]>,AllocError > { +if layout.size () ==0 { +return Ok (NonNull ::slice_from_raw_parts (NonNull ::dangling (),0 )); + } + +let ptr =Self ::alloc_store_data (layout)?; + +if flags.contains (__GFP_ZERO) { +// SAFETY: `Self::alloc_store_data` guarantees that `ptr` points to memory of at least +// `layout.size()` bytes. +unsafe { ptr.as_ptr ().write_bytes (0 , layout.size ()) }; + } + +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 src:NonNull <u8 > =if let Some (src) = ptr { + src.cast () + }else { +return Self ::alloc (layout, flags); + }; + +if layout.size () ==0 { +// SAFETY: `src` has been created by `Self::alloc_store_data`. +unsafe {Self ::free_read_data (src) }; + +return Ok (NonNull ::slice_from_raw_parts (NonNull ::dangling (),0 )); + } + +let dst =Self ::alloc (layout, flags)?; + +// SAFETY: `src` has been created by `Self::alloc_store_data`. +let data =unsafe {Self ::data (src) }; + +// SAFETY: `src` has previously been allocated with this `Allocator`; `dst` has just been +// newly allocated. Copy up to the smaller of both sizes. +unsafe { + ptr::copy_nonoverlapping ( + src.as_ptr (), + dst.as_ptr ().cast (), + cmp::min (layout.size (), data.size), + ) + }; + +Ok (dst) } }
Signature · 1 line
2.45.2
Re: [PATCH v5 22/26] rust: alloc: implement `Cmalloc` in module allocator_test
Quoted 202 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.
It's on the test infrastructure to find a way to deal with it, even if it's more complicated.
--- rust/kernel/alloc/allocator_test.rs | 175 ++++++++++++++++++++++++++-- 1 file changed, 168 insertions(+), 7 deletions(-)rust/kernel/alloc/allocator_test.rs +157
-7@@ -2,20 +2,181 @@#![allow(missing_docs)] -use super ::{AllocError ,Allocator ,Flags }; +use super ::{flags::*,AllocError ,Allocator ,Flags };use core::alloc::Layout ; +use core::cmp; +use core::mem; +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 ); +} + +struct CmallocData { +// The actual size as requested through `Cmalloc::alloc` or `Cmalloc::realloc`. + size:usize , +// The offset from the pointer returned to the caller of `Cmalloc::alloc` or `Cmalloc::realloc` +// to the actual base address of the allocation. + offset:usize , +} + +impl Cmalloc { +/// Adjust the size and alignment such that we can additionally store `CmallocData` right +/// before the actual data described by `layout`. +/// +/// Example: +/// +/// For `CmallocData` assume an alignment of 8 and a size of 16. +/// For `layout` assume and alignment of 16 and a size of 64. +/// +/// 0 16 32 96 +/// |----------------|----------------|------------------------------------------------| +/// empty CmallocData data +/// +/// For this example the returned `Layout` has an alignment of 32 and a size of 96. +fn layout_adjust (layout:Layout ) ->Result <Layout ,AllocError > { +let layout = layout.pad_to_align (); + +// Ensure that `CmallocData` fits into half the alignment. Additionally, this guarantees +// that advancing a pointer aligned to `align` by `align / 2` we still satisfy or exceed +// the alignment requested through `layout`. +let align = cmp::max ( + layout.align (), + mem::size_of ::<CmallocData >().next_power_of_two (), + ) *2 ; + +// Add the additional space required for `CmallocData`. +let size = layout.size () + mem::size_of ::<CmallocData >(); + +Ok (Layout ::from_size_align (size, align) + .map_err (|_|AllocError )? + .pad_to_align ()) + } + +fn alloc_store_data (layout:Layout ) ->Result <NonNull <u8 >,AllocError > { +let requested_size = layout.size (); + +let layout =Self ::layout_adjust (layout)?; +let min_align = layout.align () /2 ; + +// 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 ; + +let priv_ptr =NonNull ::new (raw_ptr).ok_or (AllocError )?; + +// SAFETY: Advance the pointer by `min_align`. The adjustments from `Self::layout_adjust` +// ensure that after this operation the original size and alignment requirements are still +// satisfied or exceeded. +let ptr =unsafe { priv_ptr.as_ptr ().add (min_align) }; + +// SAFETY: `min_align` is greater than or equal to the size of `CmallocData`, hence we +// don't exceed the allocation boundaries. +let data_ptr: *mut CmallocData =unsafe { ptr.sub (mem::size_of ::<CmallocData >()) }.cast (); + +let data =CmallocData { + size: requested_size, + offset: min_align, + }; + +// SAFETY: `data_ptr` is properly aligned and within the allocation boundaries reserved for +// `CmallocData`. +unsafe { data_ptr.write (data) }; + +NonNull ::new (ptr).ok_or (AllocError ) + } + +/// # Safety +/// +/// `ptr` must have been previously allocated with `Self::alloc_store_data`. +unsafe fn data <'a>(ptr:NonNull <u8 >) -> &'aCmallocData { +// SAFETY: `Self::alloc_store_data` stores the `CmallocData` right before the address +// returned to callers of `Self::alloc_store_data`. +let data_ptr: *mut CmallocData = +unsafe { ptr.as_ptr ().sub (mem::size_of ::<CmallocData >()) }.cast (); + +// SAFETY: The `CmallocData` has been previously stored at this offset with +// `Self::alloc_store_data`. +unsafe { &*data_ptr } + } + +/// # Safety +/// +/// This function must not be called more than once for the same allocation. +/// +/// `ptr` must have been previously allocated with `Self::alloc_store_data`. +unsafe fn free_read_data (ptr:NonNull <u8 >) { +// SAFETY: `ptr` has been created by `Self::alloc_store_data`. +let data =unsafe {Self ::data (ptr) }; + +// SAFETY: `ptr` has been created by `Self::alloc_store_data`. +let priv_ptr =unsafe { ptr.as_ptr ().sub (data.offset) }; + +// SAFETY: `priv_ptr` has previously been allocatored with this `Allocator`. +unsafe {libc_free (priv_ptr.cast ()) }; + } +} + +unsafe impl Allocator for Cmalloc { +fn alloc (layout:Layout , flags:Flags ) ->Result <NonNull <[u8 ]>,AllocError > { +if layout.size () ==0 { +return Ok (NonNull ::slice_from_raw_parts (NonNull ::dangling (),0 )); + } + +let ptr =Self ::alloc_store_data (layout)?; + +if flags.contains (__GFP_ZERO) { +// SAFETY: `Self::alloc_store_data` guarantees that `ptr` points to memory of at least +// `layout.size()` bytes. +unsafe { ptr.as_ptr ().write_bytes (0 , layout.size ()) }; + } + +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 src:NonNull <u8 > =if let Some (src) = ptr { + src.cast () + }else { +return Self ::alloc (layout, flags); + }; + +if layout.size () ==0 { +// SAFETY: `src` has been created by `Self::alloc_store_data`. +unsafe {Self ::free_read_data (src) }; + +return Ok (NonNull ::slice_from_raw_parts (NonNull ::dangling (),0 )); + } + +let dst =Self ::alloc (layout, flags)?; + +// SAFETY: `src` has been created by `Self::alloc_store_data`. +let data =unsafe {Self ::data (src) }; +
rust/kernel/alloc/allocator_test.rs line 179
Quoted 10 lines
rust/kernel/alloc/allocator_test.rs +10
-0+// SAFETY: `src` has previously been allocated with this `Allocator`; `dst` has just been +// newly allocated. Copy up to the smaller of both sizes. +unsafe { + ptr::copy_nonoverlapping ( + src.as_ptr (), + dst.as_ptr ().cast (), + cmp::min (layout.size (), data.size), + ) + }; +
At this point should we free the src? Or is the original pointer expected to remain valid?
Quoted 5 lines
rust/kernel/alloc/allocator_test.rs +1
-1+Ok (dst) } } --2.45.2
Danilo Krummrich
maintainerRe: [PATCH v5 22/26] rust: alloc: implement `Cmalloc` in module allocator_test
Quoted 206 lines · Heghedus Razvan
Quoted 205 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.
It's on the test infrastructure to find a way to deal with it, even if it's more complicated.
--- rust/kernel/alloc/allocator_test.rs | 175 ++++++++++++++++++++++++++-- 1 file changed, 168 insertions(+), 7 deletions(-)rust/kernel/alloc/allocator_test.rs +160
-7@@ -2,20 +2,181 @@#![allow(missing_docs)] -use super ::{AllocError ,Allocator ,Flags }; +use super ::{flags::*,AllocError ,Allocator ,Flags };use core::alloc::Layout ; +use core::cmp; +use core::mem; +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 ); +} + +struct CmallocData { +// The actual size as requested through `Cmalloc::alloc` or `Cmalloc::realloc`. + size:usize , +// The offset from the pointer returned to the caller of `Cmalloc::alloc` or `Cmalloc::realloc` +// to the actual base address of the allocation. + offset:usize , +} + +impl Cmalloc { +/// Adjust the size and alignment such that we can additionally store `CmallocData` right +/// before the actual data described by `layout`. +/// +/// Example: +/// +/// For `CmallocData` assume an alignment of 8 and a size of 16. +/// For `layout` assume and alignment of 16 and a size of 64. +/// +/// 0 16 32 96 +/// |----------------|----------------|------------------------------------------------| +/// empty CmallocData data +/// +/// For this example the returned `Layout` has an alignment of 32 and a size of 96. +fn layout_adjust (layout:Layout ) ->Result <Layout ,AllocError > { +let layout = layout.pad_to_align (); + +// Ensure that `CmallocData` fits into half the alignment. Additionally, this guarantees +// that advancing a pointer aligned to `align` by `align / 2` we still satisfy or exceed +// the alignment requested through `layout`. +let align = cmp::max ( + layout.align (), + mem::size_of ::<CmallocData >().next_power_of_two (), + ) *2 ; + +// Add the additional space required for `CmallocData`. +let size = layout.size () + mem::size_of ::<CmallocData >(); + +Ok (Layout ::from_size_align (size, align) + .map_err (|_|AllocError )? + .pad_to_align ()) + } + +fn alloc_store_data (layout:Layout ) ->Result <NonNull <u8 >,AllocError > { +let requested_size = layout.size (); + +let layout =Self ::layout_adjust (layout)?; +let min_align = layout.align () /2 ; + +// 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 ; + +let priv_ptr =NonNull ::new (raw_ptr).ok_or (AllocError )?; + +// SAFETY: Advance the pointer by `min_align`. The adjustments from `Self::layout_adjust` +// ensure that after this operation the original size and alignment requirements are still +// satisfied or exceeded. +let ptr =unsafe { priv_ptr.as_ptr ().add (min_align) }; + +// SAFETY: `min_align` is greater than or equal to the size of `CmallocData`, hence we +// don't exceed the allocation boundaries. +let data_ptr: *mut CmallocData =unsafe { ptr.sub (mem::size_of ::<CmallocData >()) }.cast (); + +let data =CmallocData { + size: requested_size, + offset: min_align, + }; + +// SAFETY: `data_ptr` is properly aligned and within the allocation boundaries reserved for +// `CmallocData`. +unsafe { data_ptr.write (data) }; + +NonNull ::new (ptr).ok_or (AllocError ) + } + +/// # Safety +/// +/// `ptr` must have been previously allocated with `Self::alloc_store_data`. +unsafe fn data <'a>(ptr:NonNull <u8 >) -> &'aCmallocData { +// SAFETY: `Self::alloc_store_data` stores the `CmallocData` right before the address +// returned to callers of `Self::alloc_store_data`. +let data_ptr: *mut CmallocData = +unsafe { ptr.as_ptr ().sub (mem::size_of ::<CmallocData >()) }.cast (); + +// SAFETY: The `CmallocData` has been previously stored at this offset with +// `Self::alloc_store_data`. +unsafe { &*data_ptr } + } + +/// # Safety +/// +/// This function must not be called more than once for the same allocation. +/// +/// `ptr` must have been previously allocated with `Self::alloc_store_data`. +unsafe fn free_read_data (ptr:NonNull <u8 >) { +// SAFETY: `ptr` has been created by `Self::alloc_store_data`. +let data =unsafe {Self ::data (ptr) }; + +// SAFETY: `ptr` has been created by `Self::alloc_store_data`. +let priv_ptr =unsafe { ptr.as_ptr ().sub (data.offset) }; + +// SAFETY: `priv_ptr` has previously been allocatored with this `Allocator`. +unsafe {libc_free (priv_ptr.cast ()) }; + } +} + +unsafe impl Allocator for Cmalloc { +fn alloc (layout:Layout , flags:Flags ) ->Result <NonNull <[u8 ]>,AllocError > { +if layout.size () ==0 { +return Ok (NonNull ::slice_from_raw_parts (NonNull ::dangling (),0 )); + } + +let ptr =Self ::alloc_store_data (layout)?; + +if flags.contains (__GFP_ZERO) { +// SAFETY: `Self::alloc_store_data` guarantees that `ptr` points to memory of at least +// `layout.size()` bytes. +unsafe { ptr.as_ptr ().write_bytes (0 , layout.size ()) }; + } + +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 src:NonNull <u8 > =if let Some (src) = ptr { + src.cast () + }else { +return Self ::alloc (layout, flags); + }; + +if layout.size () ==0 { +// SAFETY: `src` has been created by `Self::alloc_store_data`. +unsafe {Self ::free_read_data (src) }; + +return Ok (NonNull ::slice_from_raw_parts (NonNull ::dangling (),0 )); + } + +let dst =Self ::alloc (layout, flags)?; + +// SAFETY: `src` has been created by `Self::alloc_store_data`. +let data =unsafe {Self ::data (src) }; + +// SAFETY: `src` has previously been allocated with this `Allocator`; `dst` has just been +// newly allocated. Copy up to the smaller of both sizes. +unsafe {
Quoted 8 lines
rust/kernel/alloc/allocator_test.rs line 179
Quoted 7 lines
rust/kernel/alloc/allocator_test.rs +7
-0+ ptr::copy_nonoverlapping ( + src.as_ptr (), + dst.as_ptr ().cast (), + cmp::min (layout.size (), data.size), + ) + }; +At this point should we free the src? Or is the original pointer expected to remain valid?
Yes, we're indeed missing Self::free_read_data(src) here.
Quoted 8 lines
Quoted 6 lines
rust/kernel/alloc/allocator_test.rs +1
-1+Ok (dst) } } --2.45.2
Danilo Krummrich
maintainerPATCH v5 23/26 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
Danilo Krummrich
maintainerPATCH v5 24/26 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
Danilo Krummrich
maintainerPATCH v5 25/26 kbuild: rust: remove the `alloc` crate and `GlobalAlloc`
In reply to Danilo KrummrichNow that we have our own Allocator, Box and Vec types we can remove Rust's alloc crate and the new_uninit unstable feature.
Also remove Kmalloc's GlobalAlloc implementation -- we can't remove this in a separate patch, since the alloc crate requires a #[global_allocator] to set, that implements GlobalAlloc.
--- rust/Makefile | 44 ++++++----------------- rust/exports.c | 1 - rust/kernel/alloc/allocator.rs | 59 ++----------------------------- scripts/Makefile.build | 7 +--- scripts/generate_rust_analyzer.py | 11 ++---- 5 files changed, 15 insertions(+), 107 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"
rust/kernel/alloc/allocator.rs +2 -57
83 lines not on this page · raw message
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 \
scripts/generate_rust_analyzer.py +2 -9
@@ -64,13 +64,6 @@ def generate_crates(srctree, objtree, sysroot_src, external_src, cfgs): [], ) - append_crate( - "alloc", - sysroot_src / "alloc" / "src" / "lib.rs", - ["core", "compiler_builtins"], - cfg=crates_cfgs.get("alloc", []), - ) - append_crate( "macros", srctree / "rust" / "macros" / "lib.rs",
@@ -96,7 +89,7 @@ def generate_crates(srctree, objtree, sysroot_src, external_src, cfgs): append_crate( "kernel", srctree / "rust" / "kernel" / "lib.rs", - ["core", "alloc", "macros", "build_error", "bindings"], + ["core", "macros", "build_error", "bindings"], cfg=cfg, ) crates[-1]["source"] = {
@@ -133,7 +126,7 @@ def generate_crates(srctree, objtree, sysroot_src, external_src, cfgs): append_crate( name, path, - ["core", "alloc", "kernel"], + ["core", "kernel"], cfg=cfg, )
Signature · 1 line
2.45.2
Danilo Krummrich
maintainerPATCH v5 26/26 MAINTAINERS: add entry for the Rust `alloc` module
In reply to Danilo KrummrichAdd maintainers entry for the Rust alloc module.
Currently, this includes the Allocator API itself, Allocator implementations, such as Kmalloc or Vmalloc, as well as the kernel's implementation of the primary memory allocation data structures, Box and Vec.
--- MAINTAINERS | 7 +++++++ 1 file changed, 7 insertions(+)
MAINTAINERS +7 -0
@@ -19925,6 +19925,13 @@ F: scripts/*rust* F: tools/testing/selftests/rust/ K: \b(?i:rust)\b +RUST [ALLOC] +M: Danilo Krummrich <[email protected]> +L: [email protected] +S: Maintained +F: rust/kernel/alloc.rs +F: rust/kernel/alloc/ + RXRPC SOCKETS (AF_RXRPC) M: David Howells <[email protected]> M: Marc Dionne <[email protected]>
Signature · 1 line
2.45.2
Hi Danilo,
Quoted 47 lines · Danilo Krummrich
Hi,
This patch series adds generic kernel allocator support for Rust, which so far is limited to
kmallocallocations.In order to abstain from (re-)adding unstable Rust features to the kernel, this patch series does not extend the
Allocatortrait from Rust'salloccrate, nor does it extend theBoxExtandVecExtextensions.Instead, this series introduces a kernel specific
Allocatortrait, which is implemented by theKmalloc,VmallocandKVmallocallocators, also implemented in the context of this series.As a consequence we need our own kernel
Box<T, A>andVec<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
BoxandVectypes throughout the tree and remove the now obolete extensionsBoxExtandVecExt.For a final cleanup, this series removes the last minor dependencies to Rust's
alloccrate and removes it from the entire kernel build.The series ensures not to break the
rusttestmake target by implementing theallocator_testmodule providing a stub implementation for all kernelAllocators.This patch series passes all KUnit tests, including the ones added by this series. Additionally, the tests were run with
kmemleakandKASANenabled, 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
Quoted 9 lines
Changes in v5:
- (safety) comment / documentation fixes suggested by Alice, Benno and Gary
- removeUnique<T>and implementSendandSyncforBoxandVec
- useKMALLOC_MAX_SIZEforKVmalloctest and add aKmalloctest that expects to fail forKMALLOC_MAX_SIZE
- create use constantsKREALLOC,VREALLOCandKVREALLOCforReallocFuncs
- dropBox::drop_contentsfor now, will add it again, once I actually rebase on the original patch that introduces it
I'm trying to put your series on rust-dev, but I hit a few conflicts due to the conflict with Box::drop_contents, which has been in rust-dev for a while. And the conflict is not that trivial for me to resolve. So just a head-up, that's a requirement for me to put it on rust-dev for more tests from my end ;-)
Regards,
Boqun
Quoted 7 lines
- improve usage of
size_of_valinBox
- moveInPlaceInitandForeignOwnableimpls intokbox.rs
- fix missingBoxconversions inrnull.rs
- reworkedCmallocto keep track of the size of memory allocations itself
- removeGlobalAlloctogether with thealloccrate to avoid a linker error
- removeallocfromscripts/generate_rust_analyzer.py
[...]
Danilo Krummrich
maintainerQuoted 60 lines · Boqun Feng
Hi Danilo,
Quoted 47 lines · Danilo Krummrich
Hi,
This patch series adds generic kernel allocator support for Rust, which so far is limited to
kmallocallocations.In order to abstain from (re-)adding unstable Rust features to the kernel, this patch series does not extend the
Allocatortrait from Rust'salloccrate, nor does it extend theBoxExtandVecExtextensions.Instead, this series introduces a kernel specific
Allocatortrait, which is implemented by theKmalloc,VmallocandKVmallocallocators, also implemented in the context of this series.As a consequence we need our own kernel
Box<T, A>andVec<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
BoxandVectypes throughout the tree and remove the now obolete extensionsBoxExtandVecExt.For a final cleanup, this series removes the last minor dependencies to Rust's
alloccrate and removes it from the entire kernel build.The series ensures not to break the
rusttestmake target by implementing theallocator_testmodule providing a stub implementation for all kernelAllocators.This patch series passes all KUnit tests, including the ones added by this series. Additionally, the tests were run with
kmemleakandKASANenabled, 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/mmQuoted 9 lines
Changes in v5:
- (safety) comment / documentation fixes suggested by Alice, Benno and Gary
- removeUnique<T>and implementSendandSyncforBoxandVec
- useKMALLOC_MAX_SIZEforKVmalloctest and add aKmalloctest that expects to fail forKMALLOC_MAX_SIZE
- create use constantsKREALLOC,VREALLOCandKVREALLOCforReallocFuncs
- dropBox::drop_contentsfor now, will add it again, once I actually rebase on the original patch that introduces it
Quoted 5 lines
I'm trying to put your series on rust-dev, but I hit a few conflicts due to the conflict with
Box::drop_contents, which has been in rust-dev for a while. And the conflict is not that trivial for me to resolve. So just a head-up, that's a requirement for me to put it on rust-dev for more tests from my end ;-)
I'll rebase later on and send you a branch.
Quoted 13 lines
Regards,
BoqunQuoted 7 lines
- improve usage of
size_of_valinBox
- moveInPlaceInitandForeignOwnableimpls intokbox.rs
- fix missingBoxconversions inrnull.rs
- reworkedCmallocto keep track of the size of memory allocations itself
- removeGlobalAlloctogether with thealloccrate to avoid a linker error
- removeallocfromscripts/generate_rust_analyzer.py[...]
Danilo Krummrich
maintainerQuoted 7 lines · Boqun Feng
Hi Danilo,
I'm trying to put your series on rust-dev, but I hit a few conflicts due to the conflict with
Box::drop_contents, which has been in rust-dev for a while. And the conflict is not that trivial for me to resolve. So just a head-up, that's a requirement for me to put it on rust-dev for more tests from my end ;-)
I rebased everything and you can fetch them from [1].
I resolved the following conflicts:
- for Box, implement
- drop_contents
- manually_drop_contents [2]
- ``move_out` [2]
- BorrowedMut for ForeignOwnable for Box<T, A> and Pin<Box<T, A>>
- InPlaceWrite and updated InPlaceInit
- for RBTreeNode, make use of Box::move_out to replace the original implementation partially moving out of Box
@Alice: Please have a look at the changes for RBTreeNode. Maybe it's also worth having them in a separate patch.
- Danilo
[1] https://git.kernel.org/pub/scm/linux/kernel/git/dakr/linux.git/log/?h=rust-dev/mm
[2] https://git.kernel.org/pub/scm/linux/kernel/git/dakr/linux.git/commit/?h=rust-dev/mm&id=ef80ccca2ccebf3c7bcafdc13d1bfe81341cbe63
[3] https://git.kernel.org/pub/scm/linux/kernel/git/dakr/linux.git/diff/rust/kernel/rbtree.rs?h=rust-dev/mm&id=c361d66df7fb7760064fbca6bf9d72171c352a73
Quoted 3 lines
Regards,
Boqun
Alice Ryhl
maintainerQuoted 10 lines · Danilo Krummrich
Quoted 7 lines · Boqun Feng
Hi Danilo,
I'm trying to put your series on rust-dev, but I hit a few conflicts due to the conflict with
Box::drop_contents, which has been in rust-dev for a while. And the conflict is not that trivial for me to resolve. So just a head-up, that's a requirement for me to put it on rust-dev for more tests from my end ;-)
Quoted 7 lines
I rebased everything and you can fetch them from [1].
I resolved the following conflicts:
- for
Box, implement
-drop_contents
-manually_drop_contents[2]
Not sure I like this name. It sounds like something that runs the destructor, but it does the exact opposite.
Quoted 8 lines
- ``move_out` [2]
-BorrowedMutforForeignOwnableforBox<T, A>andPin<Box<T, A>>
-InPlaceWriteand updatedInPlaceInit
- forRBTreeNode, make use ofBox::move_outto replace the original implementation partially moving out ofBox@Alice: Please have a look at the changes for
RBTreeNode. Maybe it's also worth having them in a separate patch.
RBTree changes LGTM.
Alice
Danilo Krummrich
maintainerQuoted 13 lines · Alice Ryhl
Quoted 12 lines · Danilo Krummrich
Quoted 7 lines · Boqun Feng
Hi Danilo,
I'm trying to put your series on rust-dev, but I hit a few conflicts due to the conflict with
Box::drop_contents, which has been in rust-dev for a while. And the conflict is not that trivial for me to resolve. So just a head-up, that's a requirement for me to put it on rust-dev for more tests from my end ;-)I rebased everything and you can fetch them from [1].
Quoted 8 lines
Quoted 5 lines
I resolved the following conflicts:
- for
Box, implement
-drop_contents
-manually_drop_contents[2]Not sure I like this name. It sounds like something that runs the destructor, but it does the exact opposite.
I thought it kinda makes sense, since it's analogous to ManuallyDrop::new.
What about Box::forget_contents instead?
Quoted 14 lines
Quoted 9 lines
- ``move_out` [2]
-BorrowedMutforForeignOwnableforBox<T, A>andPin<Box<T, A>>
-InPlaceWriteand updatedInPlaceInit
- forRBTreeNode, make use ofBox::move_outto replace the original implementation partially moving out ofBox@Alice: Please have a look at the changes for
RBTreeNode. Maybe it's also worth having them in a separate patch.RBTree changes LGTM.
Alice
Alice Ryhl
maintainerQuoted 21 lines · Danilo Krummrich
Quoted 19 lines · Alice Ryhl
Quoted 17 lines · Danilo Krummrich
Quoted 7 lines · Boqun Feng
Hi Danilo,
I'm trying to put your series on rust-dev, but I hit a few conflicts due to the conflict with
Box::drop_contents, which has been in rust-dev for a while. And the conflict is not that trivial for me to resolve. So just a head-up, that's a requirement for me to put it on rust-dev for more tests from my end ;-)I rebased everything and you can fetch them from [1].
I resolved the following conflicts:
- for
Box, implement
-drop_contents
-manually_drop_contents[2]
Quoted 6 lines
Quoted 2 lines
Not sure I like this name. It sounds like something that runs the destructor, but it does the exact opposite.
I thought it kinda makes sense, since it's analogous to
ManuallyDrop::new.What about
Box::forget_contentsinstead?
One option is into_manually_drop. This uses the convention of using the into_* prefix for conversions that take ownership of the original value.
Alice
Danilo Krummrich
maintainerQuoted 25 lines · Alice Ryhl
Quoted 24 lines · Danilo Krummrich
Quoted 21 lines · Alice Ryhl
Quoted 10 lines · Danilo Krummrich
Quoted 7 lines · Boqun Feng
Hi Danilo,
I'm trying to put your series on rust-dev, but I hit a few conflicts due to the conflict with
Box::drop_contents, which has been in rust-dev for a while. And the conflict is not that trivial for me to resolve. So just a head-up, that's a requirement for me to put it on rust-dev for more tests from my end ;-)Quoted 7 lines
I rebased everything and you can fetch them from [1].
I resolved the following conflicts:
- for
Box, implement
-drop_contents
-manually_drop_contents[2]Not sure I like this name. It sounds like something that runs the destructor, but it does the exact opposite.
Quoted 7 lines
Quoted 3 lines
I thought it kinda makes sense, since it's analogous to
ManuallyDrop::new.What about
Box::forget_contentsinstead?One option is
into_manually_drop. This uses the convention of using theinto_*prefix for conversions that take ownership of the original value.
The signature of the current Box::manually_drop_contents is the same as for Box::drop_contents, namelyfn manually_drop_contents(this: Self) -> Box<MaybeUninit<T>, A>.
into_manually_drop seems misleading for for returning a Box<MaybeUninit<T>, A>.
I still think forget_contents hits it quite well. Just as drop_contents drops the value, forget_contents makes the Box forget the value.
Quoted 3 lines
Alice
Quoted 38 lines · Danilo Krummrich
Quoted 25 lines · Alice Ryhl
Quoted 24 lines · Danilo Krummrich
Quoted 21 lines · Alice Ryhl
Quoted 10 lines · Danilo Krummrich
Quoted 7 lines · Boqun Feng
Hi Danilo,
I'm trying to put your series on rust-dev, but I hit a few conflicts due to the conflict with
Box::drop_contents, which has been in rust-dev for a while. And the conflict is not that trivial for me to resolve. So just a head-up, that's a requirement for me to put it on rust-dev for more tests from my end ;-)Quoted 7 lines
I rebased everything and you can fetch them from [1].
I resolved the following conflicts:
- for
Box, implement
-drop_contents
-manually_drop_contents[2]Not sure I like this name. It sounds like something that runs the destructor, but it does the exact opposite.
Quoted 7 lines
Quoted 3 lines
I thought it kinda makes sense, since it's analogous to
ManuallyDrop::new.What about
Box::forget_contentsinstead?One option is
into_manually_drop. This uses the convention of using theinto_*prefix for conversions that take ownership of the original value.The signature of the current
Box::manually_drop_contentsis the same as forBox::drop_contents, namelyfn manually_drop_contents(this: Self) -> Box<MaybeUninit<T>, A>.
Quoted 5 lines
into_manually_dropseems misleading for for returning aBox<MaybeUninit<T>, A>.I still think
forget_contentshits it quite well. Just asdrop_contentsdrops the value,forget_contentsmakes theBoxforget the value.
I think forget_contents sounds good. Can you please add some more docs to that function though? Like an example and change "Manually drops the contents, but keeps the allocation" to "Forgets the contents (does not run the destructor), but keeps the allocation.".
Another thing that I spotted while looking at the patch, move_out doesn't need the transmute_copy, you should be able to just call read on the pointer.
---
Cheers,
Benno
Danilo Krummrich
maintainerQuoted 42 lines · Benno Lossin
Quoted 41 lines · Danilo Krummrich
Quoted 25 lines · Alice Ryhl
Quoted 24 lines · Danilo Krummrich
Quoted 21 lines · Alice Ryhl
Quoted 10 lines · Danilo Krummrich
Quoted 7 lines · Boqun Feng
Hi Danilo,
I'm trying to put your series on rust-dev, but I hit a few conflicts due to the conflict with
Box::drop_contents, which has been in rust-dev for a while. And the conflict is not that trivial for me to resolve. So just a head-up, that's a requirement for me to put it on rust-dev for more tests from my end ;-)Quoted 7 lines
I rebased everything and you can fetch them from [1].
I resolved the following conflicts:
- for
Box, implement
-drop_contents
-manually_drop_contents[2]Not sure I like this name. It sounds like something that runs the destructor, but it does the exact opposite.
Quoted 7 lines
Quoted 3 lines
I thought it kinda makes sense, since it's analogous to
ManuallyDrop::new.What about
Box::forget_contentsinstead?One option is
into_manually_drop. This uses the convention of using theinto_*prefix for conversions that take ownership of the original value.The signature of the current
Box::manually_drop_contentsis the same as forBox::drop_contents, namelyfn manually_drop_contents(this: Self) -> Box<MaybeUninit<T>, A>.
into_manually_dropseems misleading for for returning aBox<MaybeUninit<T>, A>.
Quoted 7 lines
Quoted 2 lines
I still think
forget_contentshits it quite well. Just asdrop_contentsdrops the value,forget_contentsmakes theBoxforget the value.I think
forget_contentssounds good. Can you please add some more docs to that function though? Like an example and change "Manually drops the contents, but keeps the allocation" to "Forgets the contents (does not run the destructor), but keeps the allocation.".
I can't really think of a good real world example other than moving out of a Box, can you? Otherwise, maybe it just shouldn't be public?
Quoted 4 lines
Another thing that I spotted while looking at the patch,
move_outdoesn't need thetransmute_copy, you should be able to just callreadon the pointer.
While technically it's the same I thought transmute_copy is considered better, since it has less stict safety requirements?
For transmute_copy we only need to say, that dst has the same type as src, whereas for read the pointer must be valid for reads, properly aligned and point to an initialized value.
Quoted 5 lines
---
Cheers,
Benno
Quoted 46 lines · Danilo Krummrich
Quoted 45 lines · Benno Lossin
Quoted 43 lines · Danilo Krummrich
Quoted 25 lines · Alice Ryhl
Quoted 24 lines · Danilo Krummrich
Quoted 21 lines · Alice Ryhl
Quoted 10 lines · Danilo Krummrich
Quoted 7 lines · Boqun Feng
Hi Danilo,
I'm trying to put your series on rust-dev, but I hit a few conflicts due to the conflict with
Box::drop_contents, which has been in rust-dev for a while. And the conflict is not that trivial for me to resolve. So just a head-up, that's a requirement for me to put it on rust-dev for more tests from my end ;-)Quoted 7 lines
I rebased everything and you can fetch them from [1].
I resolved the following conflicts:
- for
Box, implement
-drop_contents
-manually_drop_contents[2]Not sure I like this name. It sounds like something that runs the destructor, but it does the exact opposite.
Quoted 7 lines
Quoted 3 lines
I thought it kinda makes sense, since it's analogous to
ManuallyDrop::new.What about
Box::forget_contentsinstead?One option is
into_manually_drop. This uses the convention of using theinto_*prefix for conversions that take ownership of the original value.The signature of the current
Box::manually_drop_contentsis the same as forBox::drop_contents, namelyfn manually_drop_contents(this: Self) -> Box<MaybeUninit<T>, A>.
into_manually_dropseems misleading for for returning aBox<MaybeUninit<T>, A>.I still think
forget_contentshits it quite well. Just asdrop_contentsdrops the value,forget_contentsmakes theBoxforget the value.
Quoted 7 lines
Quoted 4 lines
I think
forget_contentssounds good. Can you please add some more docs to that function though? Like an example and change "Manually drops the contents, but keeps the allocation" to "Forgets the contents (does not run the destructor), but keeps the allocation.".I can't really think of a good real world example other than moving out of a
Box, can you? Otherwise, maybe it just shouldn't be public?
Oh I thought you had a user for that function. In that case making it private makes a lot of sense.
Also, drop_contents can be implemented with forget_contents, but that might be a good follow up as a good-first-issue.
Quoted 6 lines
Quoted 3 lines
Another thing that I spotted while looking at the patch,
move_outdoesn't need thetransmute_copy, you should be able to just callreadon the pointer.While technically it's the same I thought
transmute_copyis considered better, since it has less stict safety requirements?
I would avoid transmute_copy as much as possible, transmuting signifies to me that you somehow need to change the type and transmute_copy means that the compiler isn't even able to figure out that the two types have the same size (they are allowed to have different size [only Src larger than Dst though], but I most often have used this with generics where it was guaranteed to be the same type, but the compiler failed to notice it.).
Quoted 3 lines
For
transmute_copywe only need to say, that dst has the same type as src, whereas forreadthe pointer must be valid for reads, properly aligned and point to an initialized value.
You can input a reference, so you get pointer validity for free, though you still need to put that in the SAFETY comment.
---
Cheers,
Benno
Quoted 11 lines · Danilo Krummrich
Quoted 7 lines · Boqun Feng
Hi Danilo,
I'm trying to put your series on rust-dev, but I hit a few conflicts due to the conflict with
Box::drop_contents, which has been in rust-dev for a while. And the conflict is not that trivial for me to resolve. So just a head-up, that's a requirement for me to put it on rust-dev for more tests from my end ;-)I rebased everything and you can fetch them from [1].
Thanks! I will take a look later today or tomorrow.
Quoted 6 lines
I resolved the following conflicts:
- for
Box, implement
-drop_contents
-manually_drop_contents[2]
- ``move_out` [2]
Have you considered naming this into_inner which is aligned to std Box?
Regards,
Boqun
Quoted 18 lines
-
BorrowedMutforForeignOwnableforBox<T, A>andPin<Box<T, A>>
-InPlaceWriteand updatedInPlaceInit
- forRBTreeNode, make use ofBox::move_outto replace the original implementation partially moving out ofBox@Alice: Please have a look at the changes for
RBTreeNode. Maybe it's also worth having them in a separate patch.- Danilo
[1] https://git.kernel.org/pub/scm/linux/kernel/git/dakr/linux.git/log/?h=rust-dev/mm
[2] https://git.kernel.org/pub/scm/linux/kernel/git/dakr/linux.git/commit/?h=rust-dev/mm&id=ef80ccca2ccebf3c7bcafdc13d1bfe81341cbe63
[3] https://git.kernel.org/pub/scm/linux/kernel/git/dakr/linux.git/diff/rust/kernel/rbtree.rs?h=rust-dev/mm&id=c361d66df7fb7760064fbca6bf9d72171c352a73Quoted 3 lines
Regards,
Boqun
Danilo Krummrich
maintainerQuoted 10 lines · Boqun Feng
Quoted 9 lines · Danilo Krummrich
Quoted 7 lines · Boqun Feng
Hi Danilo,
I'm trying to put your series on rust-dev, but I hit a few conflicts due to the conflict with
Box::drop_contents, which has been in rust-dev for a while. And the conflict is not that trivial for me to resolve. So just a head-up, that's a requirement for me to put it on rust-dev for more tests from my end ;-)
Quoted 4 lines
Quoted 2 lines
I rebased everything and you can fetch them from [1].
Thanks! I will take a look later today or tomorrow.
I'll also send a v6 soon, which will also be based on rust-dev, so can also take this one then.
Quoted 10 lines
Quoted 7 lines
I resolved the following conflicts:
- for
Box, implement
-drop_contents
-manually_drop_contents[2]
- ``move_out` [2]Have you considered naming this
into_innerwhich is aligned to stdBox?
Seems viable too. I can rename it.
Quoted 23 lines
Regards,
BoqunQuoted 18 lines
-
BorrowedMutforForeignOwnableforBox<T, A>andPin<Box<T, A>>
-InPlaceWriteand updatedInPlaceInit
- forRBTreeNode, make use ofBox::move_outto replace the original implementation partially moving out ofBox@Alice: Please have a look at the changes for
RBTreeNode. Maybe it's also worth having them in a separate patch.- Danilo
[1] https://git.kernel.org/pub/scm/linux/kernel/git/dakr/linux.git/log/?h=rust-dev/mm
[2] https://git.kernel.org/pub/scm/linux/kernel/git/dakr/linux.git/commit/?h=rust-dev/mm&id=ef80ccca2ccebf3c7bcafdc13d1bfe81341cbe63
[3] https://git.kernel.org/pub/scm/linux/kernel/git/dakr/linux.git/diff/rust/kernel/rbtree.rs?h=rust-dev/mm&id=c361d66df7fb7760064fbca6bf9d72171c352a73Quoted 3 lines
Regards,
Boqun