Generic `Allocator` support for Rust
Subsystems (9)
Danilo Krummrich
maintainerv6 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 v6:
- rebase onto rust-dev
- keep compiler annotations for {k,v,kv}realloc()
- documentation changes suggested by Alice, Benno
- remove Box::into_pin
- fix typo in Send and Sync for Box and Vec
- kvec! changes suggested by Alice
- free src after copy in Cmalloc
- handle n == 0 in Vec::extend_with
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 | 15 + rust/kernel/alloc.rs | 142 ++++- rust/kernel/alloc/allocator.rs | 173 ++++-- rust/kernel/alloc/allocator_test.rs | 185 ++++++ rust/kernel/alloc/box_ext.rs | 80 --- rust/kernel/alloc/kbox.rs | 480 +++++++++++++++ rust/kernel/alloc/kvec.rs | 891 ++++++++++++++++++++++++++++ rust/kernel/alloc/vec_ext.rs | 185 ------ rust/kernel/error.rs | 6 +- rust/kernel/init.rs | 93 +-- rust/kernel/init/__internal.rs | 2 +- rust/kernel/lib.rs | 1 - rust/kernel/prelude.rs | 5 +- rust/kernel/rbtree.rs | 34 +- 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 | 30 +- 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 +- 32 files changed, 1973 insertions(+), 539 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: f005c686d6c1a2e66f2f9d21179d6b6bd45b20e2
Signature · 1 line
2.46.0
Danilo Krummrich
maintainerPATCH v6 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 | 102 +++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 102 insertions(+)
rust/kernel/alloc.rs +102 -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,104 @@ 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 +/// +/// A memory allocation returned from an allocator must remain valid until it is explicitly freed. +/// +/// Any pointer to a valid memory allocation must be valid to be passed to any other [`Allocator`] +/// function of the same type. +/// +/// Implementers must ensure that all trait functions abide by the guarantees documented in the +/// `# Guarantees` sections. +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`. +/// +/// # Guarantees +/// +/// When the return value is `Ok(ptr)`, then `ptr` is +/// - valid for reads and writes for `layout.size()` bytes, until it is passed to +/// [`Allocator::free`] or [`Allocator::realloc`], +/// - aligned to `layout.align()`, +/// +/// Additionally, `Flags` are honored as documented in +/// <https://docs.kernel.org/core-api/mm-api.html#mm-api-gfp-flags>. +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. +/// +/// # Guarantees +/// +/// This function has the same guarantees as [`Allocator::alloc`]. When `ptr == Some(p)`, then +/// it additionally guarantees that: +/// - the contents of the memory pointed to by `p` are preserved up to the lesser of the new +/// and old size, +/// and old size, i.e. +/// `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. +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.46.0
Re: [PATCH v6 01/26] rust: alloc: add `Allocator` trait
Quoted 9 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]>
We discussed this in our weekly meeting (I think ~one week ago?). If you give me a draft version of the comment that you plan to add regarding the old_layout parameter, I can see if I am happy with it. If I am, I would give you my RB.
---
Cheers,
Benno
Danilo Krummrich
maintainerRe: [PATCH v6 01/26] rust: alloc: add `Allocator` trait
Quoted 8 lines · Benno Lossin
Quoted 7 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.
Quoted 7 lines
Quoted 2 lines
Reviewed-by Alice Ryhl <[email protected]>Signed-off-by Danilo Krummrich <[email protected]>We discussed this in our weekly meeting (I think ~one week ago?). If you give me a draft version of the comment that you plan to add regarding the
old_layoutparameter, I can see if I am happy with it. If I am, I would give you my RB.
May I propose you let me know what you would like to see covered, rather than me trying to guess it. :-)
Quoted 5 lines
---
Cheers,
Benno
Re: [PATCH v6 01/26] rust: alloc: add `Allocator` trait
Quoted 12 lines · Danilo Krummrich
Quoted 11 lines · Benno Lossin
Quoted 9 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]>
Quoted 7 lines
Quoted 4 lines
We discussed this in our weekly meeting (I think ~one week ago?). If you give me a draft version of the comment that you plan to add regarding the
old_layoutparameter, I can see if I am happy with it. If I am, I would give you my RB.May I propose you let me know what you would like to see covered, rather than me trying to guess it. :-)
I was hoping that we put that in our meeting notes, but I failed to find them... I would put this in a normal comment, so it doesn't show up in the documentation. Preface it like implementation decision/detail:
- Why do Allocator::{realloc,free} not have an old_layout parameter like in the stdlib? (the reasons you had for that decision, like we don't need it etc.)
- Then something along the lines of "Note that no technical reason is listed above, so if you need/want to implement an allocator taking advantage of that, you can change it"
I don't think we need a lot here. Additionally it would be very useful to also put this in an issue to not lose track of it.
---
Cheers,
Benno
Danilo Krummrich
maintainerRe: [PATCH v6 01/26] rust: alloc: add `Allocator` trait
Quoted 21 lines · Benno Lossin
Quoted 12 lines · Danilo Krummrich
Quoted 11 lines · Benno Lossin
Quoted 9 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]>Quoted 7 lines
Quoted 4 lines
We discussed this in our weekly meeting (I think ~one week ago?). If you give me a draft version of the comment that you plan to add regarding the
old_layoutparameter, I can see if I am happy with it. If I am, I would give you my RB.May I propose you let me know what you would like to see covered, rather than me trying to guess it. :-)
Quoted 6 lines
I was hoping that we put that in our meeting notes, but I failed to find them... I would put this in a normal comment, so it doesn't show up in the documentation. Preface it like implementation decision/detail:
- Why doAllocator::{realloc,free}not have anold_layoutparameter like in the stdlib? (the reasons you had for that decision, like we don't need it etc.)
Ok.
Quoted 3 lines
- Then something along the lines of "Note that no technical reason is listed above, so if you need/want to implement an allocator taking advantage of that, you can change it"
I don't really want to set the conditions for this to change in the documentation. It really depends on whether it's actually needed or the advantage of having it is huge enough to leave the core kernel allocators with unused arguments.
This can really only be properly evaluated case by case in a discussion.
Quoted 8 lines
I don't think we need a lot here. Additionally it would be very useful to also put this in an issue to not lose track of it.
---
Cheers,
Benno
Re: [PATCH v6 01/26] rust: alloc: add `Allocator` trait
Quoted 35 lines · Danilo Krummrich
Quoted 21 lines · Benno Lossin
Quoted 12 lines · Danilo Krummrich
Quoted 11 lines · Benno Lossin
Quoted 9 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]>Quoted 7 lines
Quoted 4 lines
We discussed this in our weekly meeting (I think ~one week ago?). If you give me a draft version of the comment that you plan to add regarding the
old_layoutparameter, I can see if I am happy with it. If I am, I would give you my RB.May I propose you let me know what you would like to see covered, rather than me trying to guess it. :-)
Quoted 6 lines
I was hoping that we put that in our meeting notes, but I failed to find them... I would put this in a normal comment, so it doesn't show up in the documentation. Preface it like implementation decision/detail:
- Why doAllocator::{realloc,free}not have anold_layoutparameter like in the stdlib? (the reasons you had for that decision, like we don't need it etc.)Ok.
Quoted 3 lines
- Then something along the lines of "Note that no technical reason is listed above, so if you need/want to implement an allocator taking advantage of that, you can change it"
Quoted 6 lines
I don't really want to set the conditions for this to change in the documentation. It really depends on whether it's actually needed or the advantage of having it is huge enough to leave the core kernel allocators with unused arguments.
This can really only be properly evaluated case by case in a discussion.
Agreed, but I don't want people to think that we have a reason against doing it in the future. Do you have an idea how to convey this?
---
Cheers,
Benno
Danilo Krummrich
maintainerRe: [PATCH v6 01/26] rust: alloc: add `Allocator` trait
Quoted 41 lines · Benno Lossin
Quoted 40 lines · Danilo Krummrich
Quoted 21 lines · Benno Lossin
Quoted 12 lines · Danilo Krummrich
Quoted 11 lines · Benno Lossin
Quoted 9 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]>Quoted 7 lines
Quoted 4 lines
We discussed this in our weekly meeting (I think ~one week ago?). If you give me a draft version of the comment that you plan to add regarding the
old_layoutparameter, I can see if I am happy with it. If I am, I would give you my RB.May I propose you let me know what you would like to see covered, rather than me trying to guess it. :-)
Quoted 6 lines
I was hoping that we put that in our meeting notes, but I failed to find them... I would put this in a normal comment, so it doesn't show up in the documentation. Preface it like implementation decision/detail:
- Why doAllocator::{realloc,free}not have anold_layoutparameter like in the stdlib? (the reasons you had for that decision, like we don't need it etc.)Ok.
Quoted 3 lines
- Then something along the lines of "Note that no technical reason is listed above, so if you need/want to implement an allocator taking advantage of that, you can change it"
I don't really want to set the conditions for this to change in the documentation. It really depends on whether it's actually needed or the advantage of having it is huge enough to leave the core kernel allocators with unused arguments.
Quoted 4 lines
Quoted 1 line
This can really only be properly evaluated case by case in a discussion.
Agreed, but I don't want people to think that we have a reason against doing it in the future. Do you have an idea how to convey this?
I understand (and agree with) your intention. But I don't think it's necessary to document, because, ideally, this is already true for the whole kernel.
Generally, I think it's valid to assume that people are willing to change the code if the advantages outweigh the disadvantages.
So, we could write something like "This may be changed if the advantages outweigh the disadvantages.", but it'd be a bit random, since we could probably sprinkle this everywhere.
Quoted 5 lines
---
Cheers,
Benno
Re: [PATCH v6 01/26] rust: alloc: add `Allocator` trait
Quoted 44 lines · Danilo Krummrich
Quoted 43 lines · Benno Lossin
Quoted 41 lines · Danilo Krummrich
Quoted 21 lines · Benno Lossin
Quoted 12 lines · Danilo Krummrich
Quoted 11 lines · Benno Lossin
Quoted 9 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]>Quoted 7 lines
Quoted 4 lines
We discussed this in our weekly meeting (I think ~one week ago?). If you give me a draft version of the comment that you plan to add regarding the
old_layoutparameter, I can see if I am happy with it. If I am, I would give you my RB.May I propose you let me know what you would like to see covered, rather than me trying to guess it. :-)
Quoted 6 lines
I was hoping that we put that in our meeting notes, but I failed to find them... I would put this in a normal comment, so it doesn't show up in the documentation. Preface it like implementation decision/detail:
- Why doAllocator::{realloc,free}not have anold_layoutparameter like in the stdlib? (the reasons you had for that decision, like we don't need it etc.)Ok.
Quoted 3 lines
- Then something along the lines of "Note that no technical reason is listed above, so if you need/want to implement an allocator taking advantage of that, you can change it"
I don't really want to set the conditions for this to change in the documentation. It really depends on whether it's actually needed or the advantage of having it is huge enough to leave the core kernel allocators with unused arguments.
This can really only be properly evaluated case by case in a discussion.
Quoted 8 lines
Quoted 2 lines
Agreed, but I don't want people to think that we have a reason against doing it in the future. Do you have an idea how to convey this?
I understand (and agree with) your intention. But I don't think it's necessary to document, because, ideally, this is already true for the whole kernel.
Generally, I think it's valid to assume that people are willing to change the code if the advantages outweigh the disadvantages.
Sure, but for certain areas you might need a very, very good reason to do so.
In this case, I fear that people might believe that this is the case, even though it isn't (one of course still needs a good reason).
Quoted 3 lines
So, we could write something like "This may be changed if the advantages outweigh the disadvantages.", but it'd be a bit random, since we could probably sprinkle this everywhere.
Sure, that works for me. I don't think that we can sprinkle this everywhere (but of course a in lot of places).
---
Cheers,
Benno
Re: [PATCH v6 01/26] rust: alloc: add `Allocator` trait
In reply to Danilo KrummrichQuoted 11 lines · Danilo Krummrich
rust/kernel/alloc.rs +11
-0+/// 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.
rust/kernel/alloc.rs pub mod flags { · line 111
Quoted 10 lines
rust/kernel/alloc.rs +10
-0+/// +/// # Safety +/// +/// A memory allocation returned from an allocator must remain valid until it is explicitly freed. +/// +/// Any pointer to a valid memory allocation must be valid to be passed to any other [`Allocator`] +/// function of the same type. +/// +/// Implementers must ensure that all trait functions abide by the guarantees documented in the +/// `# Guarantees` sections.
Can you make a bullet point list out of these three paragraphs?
---
Cheers,
Benno
Gary Guo
maintainerRe: [PATCH v6 01/26] rust: alloc: add `Allocator` trait
In reply to Danilo KrummrichQuoted 98 lines · On Fri, 16 Aug 2024 02:10:43 +0200 Danilo Krummrich <[email protected]> wrote:
Add a kernel specific
Allocatortrait, that in contrast to the one in Rust's core library doesn't require unstable features and supports GFP flags.Subsequent patches add the following trait implementors:
Kmalloc,VmallocandKVmalloc.Reviewed-by Alice Ryhl <[email protected]>Signed-off-by Danilo Krummrich <[email protected]>--- rust/kernel/alloc.rs | 102 +++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 102 insertions(+)rust/kernel/alloc.rs +70
-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,104 @@ 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 +/// +/// A memory allocation returned from an allocator must remain valid until it is explicitly freed. +/// +/// Any pointer to a valid memory allocation must be valid to be passed to any other [`Allocator`] +/// function of the same type. +/// +/// Implementers must ensure that all trait functions abide by the guarantees documented in the +/// `# Guarantees` sections. +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`. +/// +/// # Guarantees +/// +/// When the return value is `Ok(ptr)`, then `ptr` is +/// - valid for reads and writes for `layout.size()` bytes, until it is passed to +/// [`Allocator::free`] or [`Allocator::realloc`], +/// - aligned to `layout.align()`, +/// +/// Additionally, `Flags` are honored as documented in +/// <https://docs.kernel.org/core-api/mm-api.html#mm-api-gfp-flags>. +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. +///
rust/kernel/alloc.rs pub mod flags { · line 168
Quoted 10 lines
rust/kernel/alloc.rs +10
-0+/// # Guarantees +/// +/// This function has the same guarantees as [`Allocator::alloc`]. When `ptr == Some(p)`, then +/// it additionally guarantees that: +/// - the contents of the memory pointed to by `p` are preserved up to the lesser of the new +/// and old size, +/// and old size, i.e. +/// `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. +
This line seems to be missing ///?
Quoted 22 lines
rust/kernel/alloc.rs +22
-0+/// - when the return value is `Err(AllocError)`, then `p` is still valid. +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 )) }; + } +}
Danilo Krummrich
maintainerPATCH v6 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.46.0
Re: [PATCH v6 02/26] rust: alloc: separate `aligned_size` from `krealloc_aligned`
Quoted 8 lines · On Fri, 16 Aug 2024 02:10:44 +0200 Danilo Krummrich <[email protected]> wrote:
Separate
aligned_sizefromkrealloc_aligned.Subsequent patches implement
Allocatorderivates, such asKmalloc, that requirealigned_sizeand replace the originalkrealloc_aligned.Reviewed-by Alice Ryhl <[email protected]>Reviewed-by Benno Lossin <[email protected]>Signed-off-by Danilo Krummrich <[email protected]>
Quoted 41 lines
--- 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
Danilo Krummrich
maintainerPATCH v6 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.46.0
Danilo Krummrich
maintainerPATCH v6 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/kernel/alloc.rs | 2 +- rust/kernel/alloc/allocator.rs | 72 +++++++++++++++++++++++++++++++++- 2 files changed, 72 insertions(+), 2 deletions(-)
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 +71 -1
89 lines not on this page · raw message
Signature · 1 line
2.46.0
Re: [PATCH v6 04/26] rust: alloc: implement `Allocator` for `Kmalloc`
Quoted 32 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/kernel/alloc.rs | 2 +- rust/kernel/alloc/allocator.rs | 72 +++++++++++++++++++++++++++++++++- 2 files changed, 72 insertions(+), 2 deletions(-)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 +0
-0
rust/kernel/alloc/allocator.rs line 9
Quoted 9 lines
rust/kernel/alloc/allocator.rs +6
-0@@ -1,12 +1,28 @@// SPDX-License-Identifier: GPL-2.0 //! Allocator support. +//! +//! Documentation for the kernel's memory allocators can found in the "Memory Allocation Guide" +//! linked below. For instance, this includes the concept of "get free page" (GFP) flags and the +//! typical application of the different kernel allocators. +//! +//! Reference: <https://docs.kernel.org/core-api/memory-allocation.html>
Thanks, this nice.
Quoted 4 lines
rust/kernel/alloc/allocator.rs +0
-0use super ::{flags::*,Flags };use core::alloc::{GlobalAlloc ,Layout };use core::ptr;
rust/kernel/alloc/allocator.rs line 22
Quoted 10 lines
rust/kernel/alloc/allocator.rs +8
-1+use core::ptr::NonNull ; -struct Kmalloc ; +use crate ::alloc::{AllocError ,Allocator }; +use crate ::bindings; + +/// The contiguous kernel allocator. +/// +/// `Kmalloc` is typically used for physically contiguous allocations up to page size, but also +/// supports larger allocations up to `bindings::KMALLOC_MAX_SIZE`, which is hardware specific.
Does putting a link here work? (I guess we don't yet export the bindings documentation, so it will probably fail... When we decide to enable it, we should create an issue to add missing links)
Quoted 27 lines
rust/kernel/alloc/allocator.rs +20
-0+/// +/// For more details see [self]. +pub struct Kmalloc ;/// Returns a proper size to alloc a new object aligned to `new_layout`'s alignment. fn aligned_size (new_layout:Layout ) ->usize {@@ -36,6 +52,60 @@ 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 } } +/// # Invariants +/// +/// One of the following `krealloc`, `vrealloc`, `kvrealloc`. +struct ReallocFunc ( +unsafe extern "C" fn (*const core::ffi::c_void ,usize ,u32 ) -> *mut core::ffi::c_void , +); + +impl ReallocFunc { +// INVARIANT: `krealloc` satisfies the type invariants. +const KREALLOC :Self =Self (bindings::krealloc); + +/// # Safety +/// +/// This method has the same safety requirements as [`Allocator::realloc`]. +unsafe fn call ( + &self, + ptr: Option<NonNull<u8>>,
rust/kernel/alloc/allocator.rs pub(crate) unsafe fn krealloc_aligned(ptr: *mut u8, new_layout: Layout, flags: F · line 81
Quoted 10 lines
rust/kernel/alloc/allocator.rs +10
-0+ 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.
You need some justification as to why calling the three allowed functions here.
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 {
rust/kernel/alloc/allocator.rs pub(crate) unsafe fn krealloc_aligned(ptr: *mut u8, new_layout: Layout, flags: F · line 97
Quoted 10 lines
rust/kernel/alloc/allocator.rs +10
-0+NonNull ::dangling () + }else { +NonNull ::new (raw_ptr).ok_or (AllocError )? + }; + +Ok (NonNull ::slice_from_raw_parts (ptr, size)) + } +} + +unsafe impl Allocator for Kmalloc {
Missing SAFETY comment.
---
Cheers,
Benno
Quoted 17 lines
rust/kernel/alloc/allocator.rs +11
-1+#[inline] +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.46.0
Danilo Krummrich
maintainerRe: [PATCH v6 04/26] rust: alloc: implement `Allocator` for `Kmalloc`
Quoted 96 lines · Benno Lossin
Quoted 32 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/kernel/alloc.rs | 2 +- rust/kernel/alloc/allocator.rs | 72 +++++++++++++++++++++++++++++++++- 2 files changed, 72 insertions(+), 2 deletions(-)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 +0
-0rust/kernel/alloc/allocator.rs line 9
Quoted 9 lines
rust/kernel/alloc/allocator.rs +6
-0@@ -1,12 +1,28 @@// SPDX-License-Identifier: GPL-2.0 //! Allocator support. +//! +//! Documentation for the kernel's memory allocators can found in the "Memory Allocation Guide" +//! linked below. For instance, this includes the concept of "get free page" (GFP) flags and the +//! typical application of the different kernel allocators. +//! +//! Reference: <https://docs.kernel.org/core-api/memory-allocation.html> Thanks, this nice.
Quoted 4 lines
rust/kernel/alloc/allocator.rs +0
-0use super ::{flags::*,Flags };use core::alloc::{GlobalAlloc ,Layout };use core::ptr;rust/kernel/alloc/allocator.rs line 22
Quoted 10 lines
rust/kernel/alloc/allocator.rs +8
-1+use core::ptr::NonNull ; -struct Kmalloc ; +use crate ::alloc::{AllocError ,Allocator }; +use crate ::bindings; + +/// The contiguous kernel allocator. +/// +/// `Kmalloc` is typically used for physically contiguous allocations up to page size, but also +/// supports larger allocations up to `bindings::KMALLOC_MAX_SIZE`, which is hardware specific. Does putting a link here work? (I guess we don't yet export the bindings documentation, so it will probably fail... When we decide to enable it, we should create an issue to add missing links)
Quoted 32 lines
rust/kernel/alloc/allocator.rs +25
-0+/// +/// For more details see [self]. +pub struct Kmalloc ;/// Returns a proper size to alloc a new object aligned to `new_layout`'s alignment. fn aligned_size (new_layout:Layout ) ->usize {@@ -36,6 +52,60 @@ 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 } } +/// # Invariants +/// +/// One of the following `krealloc`, `vrealloc`, `kvrealloc`. +struct ReallocFunc ( +unsafe extern "C" fn (*const core::ffi::c_void ,usize ,u32 ) -> *mut core::ffi::c_void , +); + +impl ReallocFunc { +// INVARIANT: `krealloc` satisfies the type invariants. +const KREALLOC :Self =Self (bindings::krealloc); + +/// # Safety +/// +/// This method has the same safety requirements as [`Allocator::realloc`]. +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 {
Quoted 8 lines
rust/kernel/alloc/allocator.rs pub(crate) unsafe fn krealloc_aligned(ptr: *mut u8, new_layout: Layout, flags: F · line 81
Quoted 5 lines
rust/kernel/alloc/allocator.rs +5
-0+ Some(ptr) => ptr.as_ptr(), + None => ptr::null(), + }; + +// SAFETY: `ptr` is either NULL or valid by the safety requirements of this function. You need some justification as to why calling the three allowed functions here.
What kind of justification do I need? Can you please share some more details on what you think is missing here?
Quoted 11 lines
Quoted 11 lines
rust/kernel/alloc/allocator.rs +10
-0+let raw_ptr =unsafe { +// If `size == 0` and `ptr != NULL` the memory behind the pointer is freed. + self.0 (ptr.cast (), size, flags.0 ).cast () + }; + +let ptr =if size ==0 { +NonNull ::dangling () + }else { +NonNull ::new (raw_ptr).ok_or (AllocError )? + };
Quoted 8 lines
rust/kernel/alloc/allocator.rs pub(crate) unsafe fn krealloc_aligned(ptr: *mut u8, new_layout: Layout, flags: F · line 97
Quoted 6 lines
rust/kernel/alloc/allocator.rs +6
-0+ +Ok (NonNull ::slice_from_raw_parts (ptr, size)) + } +} + +unsafe impl Allocator for Kmalloc {Missing SAFETY comment.
Yeah, I think we came across this in an earlier version of the series. I asked you about the content and usefulness of a comment here, since I'd just end up re-iterating what the Allocator trait documentation says.
IIRC, you replied that you want to think of something that'd make sense to add here.
What do you think should be written here?
Quoted 23 lines
---
Cheers,
BennoQuoted 17 lines
rust/kernel/alloc/allocator.rs +11
-1+#[inline] +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.46.0
Re: [PATCH v6 04/26] rust: alloc: implement `Allocator` for `Kmalloc`
Quoted 106 lines · Danilo Krummrich
Quoted 96 lines · Benno Lossin
Quoted 32 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/kernel/alloc.rs | 2 +- rust/kernel/alloc/allocator.rs | 72 +++++++++++++++++++++++++++++++++- 2 files changed, 72 insertions(+), 2 deletions(-)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 +0
-0rust/kernel/alloc/allocator.rs line 9
Quoted 9 lines
rust/kernel/alloc/allocator.rs +6
-0@@ -1,12 +1,28 @@// SPDX-License-Identifier: GPL-2.0 //! Allocator support. +//! +//! Documentation for the kernel's memory allocators can found in the "Memory Allocation Guide" +//! linked below. For instance, this includes the concept of "get free page" (GFP) flags and the +//! typical application of the different kernel allocators. +//! +//! Reference: <https://docs.kernel.org/core-api/memory-allocation.html> Thanks, this nice.
Quoted 4 lines
rust/kernel/alloc/allocator.rs +0
-0use super ::{flags::*,Flags };use core::alloc::{GlobalAlloc ,Layout };use core::ptr;rust/kernel/alloc/allocator.rs line 22
Quoted 10 lines
rust/kernel/alloc/allocator.rs +8
-1+use core::ptr::NonNull ; -struct Kmalloc ; +use crate ::alloc::{AllocError ,Allocator }; +use crate ::bindings; + +/// The contiguous kernel allocator. +/// +/// `Kmalloc` is typically used for physically contiguous allocations up to page size, but also +/// supports larger allocations up to `bindings::KMALLOC_MAX_SIZE`, which is hardware specific. Does putting a link here work? (I guess we don't yet export the bindings documentation, so it will probably fail... When we decide to enable it, we should create an issue to add missing links)
Quoted 32 lines
rust/kernel/alloc/allocator.rs +25
-0+/// +/// For more details see [self]. +pub struct Kmalloc ;/// Returns a proper size to alloc a new object aligned to `new_layout`'s alignment. fn aligned_size (new_layout:Layout ) ->usize {@@ -36,6 +52,60 @@ 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 } } +/// # Invariants +/// +/// One of the following `krealloc`, `vrealloc`, `kvrealloc`. +struct ReallocFunc ( +unsafe extern "C" fn (*const core::ffi::c_void ,usize ,u32 ) -> *mut core::ffi::c_void , +); + +impl ReallocFunc { +// INVARIANT: `krealloc` satisfies the type invariants. +const KREALLOC :Self =Self (bindings::krealloc); + +/// # Safety +/// +/// This method has the same safety requirements as [`Allocator::realloc`]. +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 {Quoted 8 lines
rust/kernel/alloc/allocator.rs pub(crate) unsafe fn krealloc_aligned(ptr: *mut u8, new_layout: Layout, flags: F · line 81
Quoted 5 lines
rust/kernel/alloc/allocator.rs +5
-0+ Some(ptr) => ptr.as_ptr(), + None => ptr::null(), + }; + +// SAFETY: `ptr` is either NULL or valid by the safety requirements of this function. You need some justification as to why calling the three allowed functions here.
Quoted 2 lines
What kind of justification do I need? Can you please share some more details on what you think is missing here?
So, you are calling a function pointer to an unsafe function. This means that through some invariant you have to know what the safety requirements are (otherwise how can you guarantee that this is OK?). You have the invariant that the pointer points at one of the three functions mentioned above. What are the safety requirements of those functions? I would assume that the only one is that ptr is valid. So you can use:
// SAFETY:
// - `self.0` is one of `krealloc`, `vrealloc`, `kvrealloc` and thus only requires that `ptr` is
// NULL or valid.
// - `ptr` is either NULL or valid by the safety requirements of this function.Quoted 19 lines
Quoted 10 lines
Quoted 10 lines
rust/kernel/alloc/allocator.rs +10
-0+let raw_ptr =unsafe { +// If `size == 0` and `ptr != NULL` the memory behind the pointer is freed. + self.0 (ptr.cast (), size, flags.0 ).cast () + }; + +let ptr =if size ==0 { +NonNull ::dangling () + }else { +NonNull ::new (raw_ptr).ok_or (AllocError )? + };Quoted 8 lines
rust/kernel/alloc/allocator.rs pub(crate) unsafe fn krealloc_aligned(ptr: *mut u8, new_layout: Layout, flags: F · line 97
Quoted 6 lines
rust/kernel/alloc/allocator.rs +6
-0+ +Ok (NonNull ::slice_from_raw_parts (ptr, size)) + } +} + +unsafe impl Allocator for Kmalloc {Missing SAFETY comment.
Quoted 6 lines
Yeah, I think we came across this in an earlier version of the series. I asked you about the content and usefulness of a comment here, since I'd just end up re-iterating what the
Allocatortrait documentation says.IIRC, you replied that you want to think of something that'd make sense to add here.
Oh yeah, sorry I forgot about that.
Quoted 1 line
What do you think should be written here?
I think the best way to do it, would be to push this question down into ReallocFunc::call. So we would put this on the trait:
// SAFETY: `realloc` delegates to `ReallocFunc::call`, which guarantees that
// - memory remains valid until it is explicitly freed,
// - passing a pointer to a vaild memory allocation is OK,
// - `realloc` satisfies the guarantees, since `ReallocFunc::call` has the same.We then need to put this on ReallocFunc::call:
/// # Guarantees
///
/// This method has the same guarantees as `Allocator::realloc`. Additionally
/// - it accepts any pointer to a valid memory allocation allocated by this function.
/// - memory allocated by this function remains valid until it is passed to this function.Finally, we need a GUARANTEE comment (just above the return [^1] value) that establishes these guarantees:
// GUARANTEE: Since we called `self.0` with `size` above and by the type invariants of `Self`,
// `self.0` is one of `krealloc`, `vrealloc`, `kvrealloc`. Those functions provide the guarantees of
// this function.I am not really happy with the last sentence, but I also don't think that there is value in listing out all the guarantees, only to then say "all of this is guaranteed by us calling one of these three functions.
[^1]: I am not sure that there is the right place. If you have any
suggestions, feel free to share them.
Quoted 10 lines
Quoted 10 lines
rust/kernel/alloc/allocator.rs pub(crate) unsafe fn krealloc_aligned(ptr: *mut u8, new_layout: Layout, flags: F · line 107
Quoted 10 lines
rust/kernel/alloc/allocator.rs +10
-0+#[inline] +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) } + } +}
Oh one more thing, I know that you already have a lot of patches in this series, but could you split this one into two? So the first one should introduce ReallocFunc and the second one add the impl for Kmalloc? I managed to confuse me twice because of that :)
---
Cheers,
Benno
Quoted 8 lines
Quoted 8 lines
Quoted 7 lines
rust/kernel/alloc/allocator.rs +1
-1+unsafe impl GlobalAlloc for Kmalloc {unsafe fn alloc (&self, layout:Layout ) -> *mut u8 {// SAFETY: `ptr::null_mut()` is null and `layout` has a non-zero size by the function safety --2.46.0
Danilo Krummrich
maintainerRe: [PATCH v6 04/26] rust: alloc: implement `Allocator` for `Kmalloc`
Quoted 117 lines · Benno Lossin
Quoted 106 lines · Danilo Krummrich
Quoted 96 lines · Benno Lossin
Quoted 32 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/kernel/alloc.rs | 2 +- rust/kernel/alloc/allocator.rs | 72 +++++++++++++++++++++++++++++++++- 2 files changed, 72 insertions(+), 2 deletions(-)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 +0
-0rust/kernel/alloc/allocator.rs line 9
Quoted 9 lines
rust/kernel/alloc/allocator.rs +6
-0@@ -1,12 +1,28 @@// SPDX-License-Identifier: GPL-2.0 //! Allocator support. +//! +//! Documentation for the kernel's memory allocators can found in the "Memory Allocation Guide" +//! linked below. For instance, this includes the concept of "get free page" (GFP) flags and the +//! typical application of the different kernel allocators. +//! +//! Reference: <https://docs.kernel.org/core-api/memory-allocation.html> Thanks, this nice.
Quoted 4 lines
rust/kernel/alloc/allocator.rs +0
-0use super ::{flags::*,Flags };use core::alloc::{GlobalAlloc ,Layout };use core::ptr;rust/kernel/alloc/allocator.rs line 22
Quoted 10 lines
rust/kernel/alloc/allocator.rs +8
-1+use core::ptr::NonNull ; -struct Kmalloc ; +use crate ::alloc::{AllocError ,Allocator }; +use crate ::bindings; + +/// The contiguous kernel allocator. +/// +/// `Kmalloc` is typically used for physically contiguous allocations up to page size, but also +/// supports larger allocations up to `bindings::KMALLOC_MAX_SIZE`, which is hardware specific. Does putting a link here work? (I guess we don't yet export the bindings documentation, so it will probably fail... When we decide to enable it, we should create an issue to add missing links)
Quoted 32 lines
rust/kernel/alloc/allocator.rs +25
-0+/// +/// For more details see [self]. +pub struct Kmalloc ;/// Returns a proper size to alloc a new object aligned to `new_layout`'s alignment. fn aligned_size (new_layout:Layout ) ->usize {@@ -36,6 +52,60 @@ 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 } } +/// # Invariants +/// +/// One of the following `krealloc`, `vrealloc`, `kvrealloc`. +struct ReallocFunc ( +unsafe extern "C" fn (*const core::ffi::c_void ,usize ,u32 ) -> *mut core::ffi::c_void , +); + +impl ReallocFunc { +// INVARIANT: `krealloc` satisfies the type invariants. +const KREALLOC :Self =Self (bindings::krealloc); + +/// # Safety +/// +/// This method has the same safety requirements as [`Allocator::realloc`]. +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 {Quoted 8 lines
rust/kernel/alloc/allocator.rs pub(crate) unsafe fn krealloc_aligned(ptr: *mut u8, new_layout: Layout, flags: F · line 81
Quoted 5 lines
rust/kernel/alloc/allocator.rs +5
-0+ Some(ptr) => ptr.as_ptr(), + None => ptr::null(), + }; + +// SAFETY: `ptr` is either NULL or valid by the safety requirements of this function. You need some justification as to why calling the three allowed functions here.
Quoted 2 lines
What kind of justification do I need? Can you please share some more details on what you think is missing here?
So, you are calling a function pointer to an
unsafefunction. This means that through some invariant you have to know what the safety requirements are (otherwise how can you guarantee that this is OK?). You have the invariant that the pointer points at one of the three functions mentioned above. What are the safety requirements of those functions? I would assume that the only one is thatptris valid. So you can use:
Quoted 3 lines
// SAFETY:
// - `self.0` is one of `krealloc`, `vrealloc`, `kvrealloc` and thus only requires that `ptr` is
// NULL or valid.I'm fine adding it, but I'd like to understand why you think it's required in the safety comment here? Isn't this implicit by being the type invariant?
Quoted 1 line
// - `ptr` is either NULL or valid by the safety requirements of this function.
This is the part I already have.
Quoted 31 lines
Quoted 20 lines
Quoted 11 lines
Quoted 11 lines
rust/kernel/alloc/allocator.rs +10
-0+let raw_ptr =unsafe { +// If `size == 0` and `ptr != NULL` the memory behind the pointer is freed. + self.0 (ptr.cast (), size, flags.0 ).cast () + }; + +let ptr =if size ==0 { +NonNull ::dangling () + }else { +NonNull ::new (raw_ptr).ok_or (AllocError )? + };Quoted 8 lines
rust/kernel/alloc/allocator.rs pub(crate) unsafe fn krealloc_aligned(ptr: *mut u8, new_layout: Layout, flags: F · line 97
Quoted 6 lines
rust/kernel/alloc/allocator.rs +6
-0+ +Ok (NonNull ::slice_from_raw_parts (ptr, size)) + } +} + +unsafe impl Allocator for Kmalloc {Missing SAFETY comment.
Quoted 6 lines
Yeah, I think we came across this in an earlier version of the series. I asked you about the content and usefulness of a comment here, since I'd just end up re-iterating what the
Allocatortrait documentation says.IIRC, you replied that you want to think of something that'd make sense to add here.
Oh yeah, sorry I forgot about that.
Quoted 1 line
What do you think should be written here?
Quoted 7 lines
I think the best way to do it, would be to push this question down into
ReallocFunc::call. So we would put this on the trait:// SAFETY: `realloc` delegates to `ReallocFunc::call`, which guarantees that // - memory remains valid until it is explicitly freed, // - passing a pointer to a vaild memory allocation is OK, // - `realloc` satisfies the guarantees, since `ReallocFunc::call` has the same.
So, we'd also need the same for:
- unsafe impl Allocator for Vmalloc
- unsafe impl Allocator for KVmalloc
Quoted 7 lines
We then need to put this on
ReallocFunc::call:/// # Guarantees /// /// This method has the same guarantees as `Allocator::realloc`. Additionally /// - it accepts any pointer to a valid memory allocation allocated by this function.
You propose this, since for Allocator::realloc memory allocated with Allocator::alloc would be fine too I guess.
But if e.g. Kmalloc wouldn't use the default Allocator::alloc, this would be valid too.
We could instead write something like:
"it accepts any pointer to a valid memory allocation allocated with the same kernel allocator."
Quoted 1 line
/// - memory allocated by this function remains valid until it is passed to this function.
Same here, Kmalloc could implement its own Allocator::free.
Maybe just "...until it is explicitly freed.".
Anyway, I'm fine with both, since non of the kernel allocators uses anything else than ReallocFunc::call to allocate and free memory.
Quoted 8 lines
Finally, we need a
GUARANTEEcomment (just above the return [^1] value) that establishes these guarantees:// GUARANTEE: Since we called `self.0` with `size` above and by the type invariants of `Self`, // `self.0` is one of `krealloc`, `vrealloc`, `kvrealloc`. Those functions provide the guarantees of // this function.
Quoted 7 lines
I am not really happy with the last sentence, but I also don't think that there is value in listing out all the guarantees, only to then say "all of this is guaranteed by us calling one of these three functions.
[^1]: I am not sure that there is the right place. If you have any
suggestions, feel free to share them.
Either way, I'm fine with this proposal.
Quoted 13 lines
Quoted 12 lines
Quoted 12 lines
rust/kernel/alloc/allocator.rs pub(crate) unsafe fn krealloc_aligned(ptr: *mut u8, new_layout: Layout, flags: F · line 107
Quoted 12 lines
rust/kernel/alloc/allocator.rs +10
-0+#[inline] +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) } + } +}
Quoted 4 lines
Oh one more thing, I know that you already have a lot of patches in this series, but could you split this one into two? So the first one should introduce
ReallocFuncand the second one add the impl forKmalloc? I managed to confuse me twice because of that :)
Generally, I'm fine with that, but I'm not sure if I can avoid an intermediate compiler warning about unused code doing that.
Quoted 14 lines
---
Cheers,
BennoQuoted 8 lines
Quoted 8 lines
Quoted 7 lines
rust/kernel/alloc/allocator.rs +1
-1+unsafe impl GlobalAlloc for Kmalloc {unsafe fn alloc (&self, layout:Layout ) -> *mut u8 {// SAFETY: `ptr::null_mut()` is null and `layout` has a non-zero size by the function safety --2.46.0
Re: [PATCH v6 04/26] rust: alloc: implement `Allocator` for `Kmalloc`
Quoted 55 lines · Danilo Krummrich
Quoted 54 lines · Benno Lossin
Quoted 43 lines · Danilo Krummrich
Quoted 33 lines · Benno Lossin
Quoted 32 lines · Danilo Krummrich
rust/kernel/alloc/allocator.rs +25
-0+/// +/// For more details see [self]. +pub struct Kmalloc ;/// Returns a proper size to alloc a new object aligned to `new_layout`'s alignment. fn aligned_size (new_layout:Layout ) ->usize {@@ -36,6 +52,60 @@ 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 } } +/// # Invariants +/// +/// One of the following `krealloc`, `vrealloc`, `kvrealloc`. +struct ReallocFunc ( +unsafe extern "C" fn (*const core::ffi::c_void ,usize ,u32 ) -> *mut core::ffi::c_void , +); + +impl ReallocFunc { +// INVARIANT: `krealloc` satisfies the type invariants. +const KREALLOC :Self =Self (bindings::krealloc); + +/// # Safety +/// +/// This method has the same safety requirements as [`Allocator::realloc`]. +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 {Quoted 8 lines
rust/kernel/alloc/allocator.rs pub(crate) unsafe fn krealloc_aligned(ptr: *mut u8, new_layout: Layout, flags: F · line 81
Quoted 5 lines
rust/kernel/alloc/allocator.rs +5
-0+ Some(ptr) => ptr.as_ptr(), + None => ptr::null(), + }; + +// SAFETY: `ptr` is either NULL or valid by the safety requirements of this function. You need some justification as to why calling the three allowed functions here.
Quoted 2 lines
What kind of justification do I need? Can you please share some more details on what you think is missing here?
So, you are calling a function pointer to an
unsafefunction. This means that through some invariant you have to know what the safety requirements are (otherwise how can you guarantee that this is OK?). You have the invariant that the pointer points at one of the three functions mentioned above. What are the safety requirements of those functions? I would assume that the only one is thatptris valid. So you can use:
Quoted 6 lines
Quoted 3 lines
// SAFETY: // - `self.0` is one of `krealloc`, `vrealloc`, `kvrealloc` and thus only requires that `ptr` is // NULL or valid.I'm fine adding it, but I'd like to understand why you think it's required in the safety comment here? Isn't this implicit by being the type invariant?
You are calling a function pointer to an unsafe function that takes a raw pointer. Without this comment it is not clear what the function pointer's safety requirements are for the raw pointer parameter.
Quoted 3 lines
Quoted 1 line
// - `ptr` is either NULL or valid by the safety requirements of this function.This is the part I already have.
I kept it to ensure that you also keep it.
Quoted 33 lines
Quoted 33 lines
Quoted 19 lines
Quoted 10 lines
Quoted 10 lines
rust/kernel/alloc/allocator.rs +10
-0+let raw_ptr =unsafe { +// If `size == 0` and `ptr != NULL` the memory behind the pointer is freed. + self.0 (ptr.cast (), size, flags.0 ).cast () + }; + +let ptr =if size ==0 { +NonNull ::dangling () + }else { +NonNull ::new (raw_ptr).ok_or (AllocError )? + };Quoted 8 lines
rust/kernel/alloc/allocator.rs pub(crate) unsafe fn krealloc_aligned(ptr: *mut u8, new_layout: Layout, flags: F · line 97
Quoted 6 lines
rust/kernel/alloc/allocator.rs +6
-0+ +Ok (NonNull ::slice_from_raw_parts (ptr, size)) + } +} + +unsafe impl Allocator for Kmalloc {Missing SAFETY comment.
Quoted 6 lines
Yeah, I think we came across this in an earlier version of the series. I asked you about the content and usefulness of a comment here, since I'd just end up re-iterating what the
Allocatortrait documentation says.IIRC, you replied that you want to think of something that'd make sense to add here.
Oh yeah, sorry I forgot about that.
Quoted 1 line
What do you think should be written here?
I think the best way to do it, would be to push this question down into
ReallocFunc::call. So we would put this on the trait:
Quoted 8 lines
Quoted 4 lines
// SAFETY: `realloc` delegates to `ReallocFunc::call`, which guarantees that // - memory remains valid until it is explicitly freed, // - passing a pointer to a vaild memory allocation is OK, // - `realloc` satisfies the guarantees, since `ReallocFunc::call` has the same.So, we'd also need the same for:
-unsafe impl Allocator for Vmalloc
-unsafe impl Allocator for KVmalloc
Yes.
Quoted 7 lines
Quoted 6 lines
We then need to put this on
ReallocFunc::call:/// # Guarantees /// /// This method has the same guarantees as `Allocator::realloc`. Additionally /// - it accepts any pointer to a valid memory allocation allocated by this function.
Quoted 5 lines
You propose this, since for
Allocator::reallocmemory allocated withAllocator::allocwould be fine too I guess.But if e.g.
Kmallocwouldn't use the defaultAllocator::alloc, this would be valid too.
So if Kmalloc were to implement alloc by not calling ReallocFun::call, then we couldn't use this comment. Do you think that such a change might be required at some point?
Quoted 4 lines
We could instead write something like:
"it accepts any pointer to a valid memory allocation allocated with the same kernel allocator."
It would be better, if we can keep it simpler (ie only realloc is implemented).
Quoted 5 lines
Quoted 1 line
/// - memory allocated by this function remains valid until it is passed to this function.Same here,
Kmalloccould implement its ownAllocator::free.Maybe just "...until it is explicitly freed.".
I don't really like that, since by that any other function could be meant. Do you need to override the free function? If not then it would be better.
Quoted 34 lines
Anyway, I'm fine with both, since non of the kernel allocators uses anything else than
ReallocFunc::callto allocate and free memory.Quoted 8 lines
Finally, we need a
GUARANTEEcomment (just above the return [^1] value) that establishes these guarantees:// GUARANTEE: Since we called `self.0` with `size` above and by the type invariants of `Self`, // `self.0` is one of `krealloc`, `vrealloc`, `kvrealloc`. Those functions provide the guarantees of // this function.Quoted 7 lines
I am not really happy with the last sentence, but I also don't think that there is value in listing out all the guarantees, only to then say "all of this is guaranteed by us calling one of these three functions.
[^1]: I am not sure that there is the right place. If you have any
suggestions, feel free to share them.Either way, I'm fine with this proposal.
Quoted 13 lines
Quoted 12 lines
Quoted 12 lines
Quoted 12 lines
rust/kernel/alloc/allocator.rs +10
-0+#[inline] +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) } + } +}
Quoted 7 lines
Quoted 4 lines
Oh one more thing, I know that you already have a lot of patches in this series, but could you split this one into two? So the first one should introduce
ReallocFuncand the second one add the impl forKmalloc? I managed to confuse me twice because of that :)Generally, I'm fine with that, but I'm not sure if I can avoid an intermediate compiler warning about unused code doing that.
You can just use #[expect(dead_code)] for that in the intermediate patches.
---
Cheers,
Benno
Danilo Krummrich
maintainerRe: [PATCH v6 04/26] rust: alloc: implement `Allocator` for `Kmalloc`
Quoted 60 lines · Benno Lossin
Quoted 59 lines · Danilo Krummrich
Quoted 57 lines · Benno Lossin
Quoted 43 lines · Danilo Krummrich
Quoted 33 lines · Benno Lossin
Quoted 32 lines · Danilo Krummrich
rust/kernel/alloc/allocator.rs +25
-0+/// +/// For more details see [self]. +pub struct Kmalloc ;/// Returns a proper size to alloc a new object aligned to `new_layout`'s alignment. fn aligned_size (new_layout:Layout ) ->usize {@@ -36,6 +52,60 @@ 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 } } +/// # Invariants +/// +/// One of the following `krealloc`, `vrealloc`, `kvrealloc`. +struct ReallocFunc ( +unsafe extern "C" fn (*const core::ffi::c_void ,usize ,u32 ) -> *mut core::ffi::c_void , +); + +impl ReallocFunc { +// INVARIANT: `krealloc` satisfies the type invariants. +const KREALLOC :Self =Self (bindings::krealloc); + +/// # Safety +/// +/// This method has the same safety requirements as [`Allocator::realloc`]. +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 {Quoted 8 lines
rust/kernel/alloc/allocator.rs pub(crate) unsafe fn krealloc_aligned(ptr: *mut u8, new_layout: Layout, flags: F · line 81
Quoted 5 lines
rust/kernel/alloc/allocator.rs +5
-0+ Some(ptr) => ptr.as_ptr(), + None => ptr::null(), + }; + +// SAFETY: `ptr` is either NULL or valid by the safety requirements of this function. You need some justification as to why calling the three allowed functions here.
Quoted 2 lines
What kind of justification do I need? Can you please share some more details on what you think is missing here?
So, you are calling a function pointer to an
unsafefunction. This means that through some invariant you have to know what the safety requirements are (otherwise how can you guarantee that this is OK?). You have the invariant that the pointer points at one of the three functions mentioned above. What are the safety requirements of those functions? I would assume that the only one is thatptris valid. So you can use:// SAFETY: // - `self.0` is one of `krealloc`, `vrealloc`, `kvrealloc` and thus only requires that `ptr` is // NULL or valid.
Quoted 6 lines
Quoted 2 lines
I'm fine adding it, but I'd like to understand why you think it's required in the safety comment here? Isn't this implicit by being the type invariant?
You are calling a function pointer to an
unsafefunction that takes a raw pointer. Without this comment it is not clear what the function pointer's safety requirements are for the raw pointer parameter.
That's my point, isn't this implicitly clear by the type invariant? If needed, shouldn't it be:
// INVARIANT:
// - self.0 is one of [...]
//
// SAFETY:
// - ptr is either NULL or [...]
Quoted 61 lines
Quoted 4 lines
Quoted 2 lines
// - `ptr` is either NULL or valid by the safety requirements of this function.This is the part I already have.
I kept it to ensure that you also keep it.
Quoted 33 lines
Quoted 33 lines
Quoted 19 lines
Quoted 10 lines
Quoted 10 lines
rust/kernel/alloc/allocator.rs +10
-0+let raw_ptr =unsafe { +// If `size == 0` and `ptr != NULL` the memory behind the pointer is freed. + self.0 (ptr.cast (), size, flags.0 ).cast () + }; + +let ptr =if size ==0 { +NonNull ::dangling () + }else { +NonNull ::new (raw_ptr).ok_or (AllocError )? + };Quoted 8 lines
rust/kernel/alloc/allocator.rs pub(crate) unsafe fn krealloc_aligned(ptr: *mut u8, new_layout: Layout, flags: F · line 97
Quoted 6 lines
rust/kernel/alloc/allocator.rs +6
-0+ +Ok (NonNull ::slice_from_raw_parts (ptr, size)) + } +} + +unsafe impl Allocator for Kmalloc {Missing SAFETY comment.
Quoted 6 lines
Yeah, I think we came across this in an earlier version of the series. I asked you about the content and usefulness of a comment here, since I'd just end up re-iterating what the
Allocatortrait documentation says.IIRC, you replied that you want to think of something that'd make sense to add here.
Oh yeah, sorry I forgot about that.
Quoted 1 line
What do you think should be written here?
I think the best way to do it, would be to push this question down into
ReallocFunc::call. So we would put this on the trait:Quoted 8 lines
Quoted 4 lines
// SAFETY: `realloc` delegates to `ReallocFunc::call`, which guarantees that // - memory remains valid until it is explicitly freed, // - passing a pointer to a vaild memory allocation is OK, // - `realloc` satisfies the guarantees, since `ReallocFunc::call` has the same.So, we'd also need the same for:
-unsafe impl Allocator for Vmalloc
-unsafe impl Allocator for KVmallocYes.
Quoted 10 lines
Quoted 6 lines
We then need to put this on
ReallocFunc::call:/// # Guarantees /// /// This method has the same guarantees as `Allocator::realloc`. Additionally /// - it accepts any pointer to a valid memory allocation allocated by this function.You propose this, since for
Allocator::reallocmemory allocated withAllocator::allocwould be fine too I guess.
Quoted 6 lines
Quoted 2 lines
But if e.g.
Kmallocwouldn't use the defaultAllocator::alloc, this would be valid too.So if
Kmallocwere to implementallocby not callingReallocFun::call, then we couldn't use this comment. Do you think that such a change might be required at some point?
I don't think so, this was purely hypothetical. Let's stick to your proposal.
Quoted 58 lines
Quoted 5 lines
We could instead write something like:
"it accepts any pointer to a valid memory allocation allocated with the same kernel allocator."
It would be better, if we can keep it simpler (ie only
reallocis implemented).Quoted 5 lines
Quoted 1 line
/// - memory allocated by this function remains valid until it is passed to this function.Same here,
Kmalloccould implement its ownAllocator::free.Maybe just "...until it is explicitly freed.".
I don't really like that, since by that any other function could be meant. Do you need to override the
freefunction? If not then it would be better.Quoted 39 lines
Anyway, I'm fine with both, since non of the kernel allocators uses anything else than
ReallocFunc::callto allocate and free memory.Quoted 8 lines
Finally, we need a
GUARANTEEcomment (just above the return [^1] value) that establishes these guarantees:// GUARANTEE: Since we called `self.0` with `size` above and by the type invariants of `Self`, // `self.0` is one of `krealloc`, `vrealloc`, `kvrealloc`. Those functions provide the guarantees of // this function.Quoted 7 lines
I am not really happy with the last sentence, but I also don't think that there is value in listing out all the guarantees, only to then say "all of this is guaranteed by us calling one of these three functions.
[^1]: I am not sure that there is the right place. If you have any
suggestions, feel free to share them.Either way, I'm fine with this proposal.
Quoted 17 lines
Quoted 12 lines
Quoted 12 lines
rust/kernel/alloc/allocator.rs pub(crate) unsafe fn krealloc_aligned(ptr: *mut u8, new_layout: Layout, flags: F · line 107
Quoted 12 lines
rust/kernel/alloc/allocator.rs +10
-0+#[inline] +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) } + } +}Oh one more thing, I know that you already have a lot of patches in this series, but could you split this one into two? So the first one should introduce
ReallocFuncand the second one add the impl forKmalloc? I managed to confuse me twice because of that :)
Quoted 5 lines
Quoted 2 lines
Generally, I'm fine with that, but I'm not sure if I can avoid an intermediate compiler warning about unused code doing that.
You can just use
#[expect(dead_code)]for that in the intermediate patches.
I usually try to avoid that, because it can be misleading when bisecting things.
If the temporarily unused code contains a bug, your bisection doesn't end up at this patch, but some other patch that starts using it.
Quoted 5 lines
---
Cheers,
Benno
Re: [PATCH v6 04/26] rust: alloc: implement `Allocator` for `Kmalloc`
Quoted 64 lines · Danilo Krummrich
Quoted 63 lines · Benno Lossin
Quoted 61 lines · Danilo Krummrich
Quoted 54 lines · Benno Lossin
Quoted 43 lines · Danilo Krummrich
Quoted 33 lines · Benno Lossin
Quoted 32 lines · Danilo Krummrich
(quoted diff) +25
-0+/// +/// For more details see [self]. +pub struct Kmalloc; /// Returns a proper size to alloc a new object aligned to `new_layout`'s alignment. fn aligned_size(new_layout: Layout) -> usize {@@ -36,6 +52,60 @@ pub(crate) unsafe fn krealloc_aligned(ptr: *mut u8, new_layout: Layout, flags: F unsafe { bindings::krealloc(ptr as *const core::ffi::c_void, size, flags.0) as *mut u8 } } +/// # Invariants +/// +/// One of the following `krealloc`, `vrealloc`, `kvrealloc`. +struct ReallocFunc( + unsafe extern "C" fn(*const core::ffi::c_void, usize, u32) -> *mut core::ffi::c_void, +); + +impl ReallocFunc { + // INVARIANT: `krealloc` satisfies the type invariants. + const KREALLOC: Self = Self(bindings::krealloc); + + /// # Safety + /// + /// This method has the same safety requirements as [`Allocator::realloc`]. + 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 {Quoted 8 lines
Review pub(crate) unsafe fn krealloc_aligned(ptr: *mut u8, new_layout: Layout, flags: F · line 81
Quoted 5 lines
(quoted diff) +5
-0+ Some(ptr) => ptr.as_ptr(), + None => ptr::null(), + }; + + // SAFETY: `ptr` is either NULL or valid by the safety requirements of this function.You need some justification as to why calling the three allowed functions here.
Quoted 2 lines
What kind of justification do I need? Can you please share some more details on what you think is missing here?
So, you are calling a function pointer to an
unsafefunction. This means that through some invariant you have to know what the safety requirements are (otherwise how can you guarantee that this is OK?). You have the invariant that the pointer points at one of the three functions mentioned above. What are the safety requirements of those functions? I would assume that the only one is thatptris valid. So you can use:Quoted 3 lines
// SAFETY: // - `self.0` is one of `krealloc`, `vrealloc`, `kvrealloc` and thus only requires that `ptr` is // NULL or valid.I'm fine adding it, but I'd like to understand why you think it's required in the safety comment here? Isn't this implicit by being the type invariant?
Quoted 6 lines
Quoted 3 lines
You are calling a function pointer to an
unsafefunction that takes a raw pointer. Without this comment it is not clear what the function pointer's safety requirements are for the raw pointer parameter.That's my point, isn't this implicitly clear by the type invariant? If needed, shouldn't it be:
I would argue that it is not implicitly clear, since to the reader of just that unsafe block it's totally unclear that self.0 has such an invariant. They would have to read the type definition.
Quoted 12 lines
// INVARIANT:
// -self.0is one of [...]
//
// SAFETY:
// -ptris either NULL or [...]Quoted 6 lines
Quoted 4 lines
Quoted 2 lines
// - `ptr` is either NULL or valid by the safety requirements of this function.This is the part I already have.
I kept it to ensure that you also keep it.
[...]
Quoted 19 lines
Quoted 19 lines
Quoted 18 lines
Quoted 11 lines
Quoted 10 lines
Quoted 10 lines
Review
Quoted 10 lines
(quoted diff) +10
-0+ #[inline] + 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) } + } +}Quoted 4 lines
Oh one more thing, I know that you already have a lot of patches in this series, but could you split this one into two? So the first one should introduce
ReallocFuncand the second one add the impl forKmalloc? I managed to confuse me twice because of that :)Generally, I'm fine with that, but I'm not sure if I can avoid an intermediate compiler warning about unused code doing that.
Quoted 7 lines
Quoted 2 lines
You can just use
#[expect(dead_code)]for that in the intermediate patches.I usually try to avoid that, because it can be misleading when bisecting things.
If the temporarily unused code contains a bug, your bisection doesn't end up at this patch, but some other patch that starts using it.
I don't think it's a problem in this case, since the two patches are directly next to each other and you're not changing existing code, just splitting up the addition of new code.
---
Cheers,
Benno
Danilo Krummrich
maintainerPATCH v6 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.46.0
Re: [PATCH v6 05/26] rust: alloc: add module `allocator_test`
Quoted 11 lines · On Fri, 16 Aug 2024 02:10:47 +0200 Danilo Krummrich <[email protected]> wrote:
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.Reviewed-by Alice Ryhl <[email protected]>Reviewed-by Benno Lossin <[email protected]>Signed-off-by Danilo Krummrich <[email protected]>
Quoted 55 lines
--- rust/kernel/alloc.rs | 9 +++++++-- rust/kernel/alloc/allocator_test.rs | 19 +++++++++++++++++++ 2 files changed, 26 insertions(+), 2 deletions(-) create mode 100644 rust/kernel/alloc/allocator_test.rsrust/kernel/alloc.rs +7
-2@@ -2,12 +2,17 @@//! Extensions to the [`alloc`] crate. -#[cfg(not(test))] -#[cfg(not(testlib))] +#[cfg(not(any(test, testlib)))] pub mod allocator;pub mod box_ext;pub mod vec_ext; +#[cfg(any(test, testlib))] +pub mod allocator_test; + +#[cfg(any(test, testlib))] +pub use self::allocator_testas allocator; +/// Indicates an allocation error. #[derive( Copy , Clone , PartialEq , Eq , Debug )] pub struct AllocError ;rust/kernel/alloc/allocator_test.rs new file +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! (); + } +}
Danilo Krummrich
maintainerPATCH v6 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 | 8 ++++++++ rust/kernel/alloc/allocator.rs | 32 +++++++++++++++++++++++++++++ rust/kernel/alloc/allocator_test.rs | 1 + 3 files changed, 41 insertions(+)
rust/helpers.c +8 -0
@@ -36,6 +36,7 @@#include <linux/security.h> #include <linux/slab.h> #include <linux/spinlock.h> +#include <linux/vmalloc.h> #include <linux/wait.h> #include <linux/workqueue.h>
@@ -248,6 +249,13 @@ rust_helper_krealloc(const void *objp, size_t new_size, gfp_t flags) }EXPORT_SYMBOL_GPL (rust_helper_krealloc); +void * __must_check __realloc_size(2) +rust_helper_vrealloc(const void *p, size_t size, gfp_t flags) +{ +return vrealloc (p, size, flags); +} +EXPORT_SYMBOL_GPL (rust_helper_vrealloc); +void rust_helper_rb_link_node (struct rb_node *node,struct rb_node *parent,struct rb_node **rb_link) {
rust/kernel/alloc/allocator.rs +32 -0
@@ -15,6 +15,7 @@use crate ::alloc::{AllocError ,Allocator };use crate ::bindings; +use crate ::pr_warn;/// The contiguous kernel allocator. ///
@@ -24,6 +25,15 @@/// For more details see [self]. pub struct Kmalloc ; +/// The virtually contiguous kernel allocator. +/// +/// `Vmalloc` allocates pages from the page level allocator and maps them into the contiguous kernel +/// virtual space. It is typically used for large allocations. The memory allocated with this +/// allocator is not physically contiguous. +/// +/// For more details see [self]. +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.
@@ -63,6 +73,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`].
@@ -141,6 +154,25 @@ unsafe fn alloc_zeroed(&self, layout: Layout) -> *mut u8 { } } +unsafe impl Allocator for Vmalloc { +#[inline] +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.46.0
Re: [PATCH v6 06/26] rust: alloc: implement `Vmalloc` allocator
Quoted 11 lines · Danilo Krummrich
rust/kernel/alloc/allocator.rs +3
-0@@ -63,6 +73,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 157
Quoted 4 lines
rust/kernel/alloc/allocator.rs +1
-0@@ -141,6 +154,25 @@ unsafe fn alloc_zeroed(&self, layout: Layout) -> *mut u8 { } } +unsafe impl Allocator for Vmalloc {
The same safety comment as with Kmalloc should fit here (and with KVmalloc too).
---
Cheers,
Benno
Quoted 36 lines
rust/kernel/alloc/allocator.rs +18
-0+#[inline] +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
-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.46.0
Danilo Krummrich
maintainerPATCH v6 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 | 7 +++++++ rust/kernel/alloc/allocator.rs | 31 +++++++++++++++++++++++++++++ rust/kernel/alloc/allocator_test.rs | 1 + 3 files changed, 39 insertions(+)
rust/helpers.c +7 -0
@@ -256,6 +256,13 @@ rust_helper_vrealloc(const void *p, size_t size, gfp_t flags) }EXPORT_SYMBOL_GPL (rust_helper_vrealloc); +void * __must_check __realloc_size(2) +rust_helper_kvrealloc(const void *p, size_t size, gfp_t flags) +{ +return kvrealloc (p, size, flags); +} +EXPORT_SYMBOL_GPL (rust_helper_kvrealloc); +void rust_helper_rb_link_node (struct rb_node *node,struct rb_node *parent,struct rb_node **rb_link) {
rust/kernel/alloc/allocator.rs +31 -0
@@ -34,6 +34,15 @@/// For more details see [self]. pub struct Vmalloc ; +/// The kvmalloc kernel allocator. +/// +/// `KVmalloc` attempts to allocate memory with `Kmalloc` first, but falls back to `Vmalloc` upon +/// failure. This allocator is typically used when the size for the requsted allocation is not +/// known and may exceed the capabilities of `Kmalloc`. +/// +/// For more details see [self]. +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.
@@ -76,6 +85,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`].
@@ -173,6 +185,25 @@ unsafe fn realloc( } } +unsafe impl Allocator for KVmalloc { +#[inline] +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.46.0
Danilo Krummrich
maintainerPATCH v6 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
@@ -37,4 +37,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.46.0
Danilo Krummrich
maintainerPATCH v6 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 | 480 ++++++++++++++++++++++++++++++++++++++ rust/kernel/prelude.rs | 2 +- 3 files changed, 487 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 +480 -0
480 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.46.0
Alice Ryhl
maintainerRe: [PATCH v6 09/26] rust: alloc: implement kernel `Box`
Quoted 11 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]>
Overall looks good to me, but I have a question:
rust/kernel/alloc/kbox.rs line 384
Quoted 12 lines
rust/kernel/alloc/kbox.rs new file +11
-0+impl <T : 'static,A >ForeignOwnable for Box <T ,A > +where +A :Allocator , +{ +type Borrowed <'a> = &'aT ; +type BorrowedMut <'a> = &'amut T ; +unsafe fn borrow_mut <'a>(ptr: *const core::ffi::c_void ) -> &'amut T { +// SAFETY: The safety requirements of this method ensure that the pointer is valid and that +// nothing else will access the value for the duration of 'a. +unsafe { &mut *ptr.cast_mut ().cast () } + }
Where does this come from? It looks like you've based the series on top of [1], but I dropped that patch a long time ago, and I don't see it in rust-dev anymore.
Alice
Danilo Krummrich
maintainerRe: [PATCH v6 09/26] rust: alloc: implement kernel `Box`
Quoted 15 lines · Alice Ryhl
Quoted 11 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]>Overall looks good to me, but I have a question:
Quoted 16 lines
rust/kernel/alloc/kbox.rs line 384
Quoted 12 lines
rust/kernel/alloc/kbox.rs new file +11
-0+impl <T : 'static,A >ForeignOwnable for Box <T ,A > +where +A :Allocator , +{ +type Borrowed <'a> = &'aT ; +type BorrowedMut <'a> = &'amut T ; +unsafe fn borrow_mut <'a>(ptr: *const core::ffi::c_void ) -> &'amut T { +// SAFETY: The safety requirements of this method ensure that the pointer is valid and that +// nothing else will access the value for the duration of 'a. +unsafe { &mut *ptr.cast_mut ().cast () } + }Where does this come from? It looks like you've based the series on top of [1], but I dropped that patch a long time ago, and I don't see it in rust-dev anymore.
I comes from me rebasing onto rust-dev. When Boqun asked me to resolve the merge conflicts a few days ago, this patch was in rust-dev. I think it disappeared two days ago or so.
@Bonqun: Need to me to rebase again?
- Danilo
Quoted 5 lines
Alice
Re: [PATCH v6 09/26] rust: alloc: implement kernel `Box`
Quoted 33 lines · Danilo Krummrich
Quoted 15 lines · Alice Ryhl
Quoted 11 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]>Overall looks good to me, but I have a question:
Quoted 16 lines
rust/kernel/alloc/kbox.rs line 384
Quoted 12 lines
rust/kernel/alloc/kbox.rs new file +11
-0+impl <T : 'static,A >ForeignOwnable for Box <T ,A > +where +A :Allocator , +{ +type Borrowed <'a> = &'aT ; +type BorrowedMut <'a> = &'amut T ; +unsafe fn borrow_mut <'a>(ptr: *const core::ffi::c_void ) -> &'amut T { +// SAFETY: The safety requirements of this method ensure that the pointer is valid and that +// nothing else will access the value for the duration of 'a. +unsafe { &mut *ptr.cast_mut ().cast () } + }Where does this come from? It looks like you've based the series on top of [1], but I dropped that patch a long time ago, and I don't see it in rust-dev anymore.
Quoted 6 lines
I comes from me rebasing onto rust-dev. When Boqun asked me to resolve the merge conflicts a few days ago, this patch was in rust-dev. I think it disappeared two days ago or so.
@Bonqun: Need to me to rebase again?
No need to rebase for this. I've already applied your series on rust-dev. The background of carrying borrow_mut is because it was simple to do, and so I carried it as the best effort, now with more ForeignOwnable ipmls, I think it's better if we just drop it, and re-introduce if needed.
Sorry for a bit out-of-sync on this one.
Regards,
Boqun
Quoted 7 lines
- Danilo
Quoted 5 lines
Alice
Re: [PATCH v6 09/26] rust: alloc: implement kernel `Box`
In reply to Danilo KrummrichQuoted 16 lines · Danilo Krummrich
rust/kernel/alloc/kbox.rs new file +10
-0@@ -0,0 +1,480 @@ +// SPDX-License-Identifier: GPL-2.0 + +//! Implementation of [`Box`]. + +#[allow(unused_imports)] // Used in doc comments. +use super ::allocator::{KVmalloc ,Kmalloc ,Vmalloc }; +use super ::{AllocError ,Allocator ,Flags }; +use core::fmt; +use core::marker::PhantomData ; +use core::mem::ManuallyDrop ;
rust/kernel/alloc/kbox.rs line 20
Quoted 10 lines
rust/kernel/alloc/kbox.rs new file +10
-0+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 ,InPlaceWrite ,Init ,PinInit }; +use crate ::types::ForeignOwnable ; + +/// The kernel's [`Box`] type - a heap allocation for a single value of type `T`.
A single - doesn't really render nicely in markdown, instead use a double or triple dash (-- or ---).
rust/kernel/alloc/kbox.rs line 26
Quoted 6 lines
rust/kernel/alloc/kbox.rs new file +6
-0+/// +/// This is the kernel's version of the Rust stdlib's `Box`. There are several of differences, +/// for example no `noalias` attribute is emitted and partially moving out of a `Box` is not +/// supported. There are also several API differences, e.g. `Box` always requires an [`Allocator`] +/// implementation to be passed as generic, page [`Flags`] when allocating memory and all functions +/// that may allocate memory are failable.
Do you mean fallible?
Quoted 12 lines
rust/kernel/alloc/kbox.rs new file +12
-0+/// +/// `Box` works with any of the kernel's allocators, e.g. [`Kmalloc`], [`Vmalloc`] or [`KVmalloc`]. +/// There are aliases for `Box` with these allocators ([`KBox`], [`VBox`], [`KVBox`]). +/// +/// 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);
rust/kernel/alloc/kbox.rs line 48
Quoted 10 lines
rust/kernel/alloc/kbox.rs new file +10
-0+/// # 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()); +/// ```
It would be nice if you could add something like "KBox can't handle big allocations:" above this example, so that people aren't confused why this example expects an error.
rust/kernel/alloc/kbox.rs line 56
Quoted 8 lines
rust/kernel/alloc/kbox.rs new file +8
-0+/// +/// ``` +/// # use kernel::bindings; +/// const SIZE: usize = bindings::KMALLOC_MAX_SIZE as usize + 1; +/// struct Huge([u8; SIZE]); +/// +/// assert!(KVBox::<Huge>::new_uninit(GFP_KERNEL).is_ok()); +/// ```
Similarly, you could then say above this one "Instead use either VBox or KVBox:"
rust/kernel/alloc/kbox.rs line 60
Quoted 4 lines
rust/kernel/alloc/kbox.rs new file +4
-0+/// +/// # Invariants +/// +/// The [`Box`]' pointer is always properly aligned and either points to memory allocated with `A`
Please use self.0 instead of "[Box]'".
rust/kernel/alloc/kbox.rs line 61
Quoted 1 line
rust/kernel/alloc/kbox.rs new file +1
-0+/// or, for zero-sized types, is a dangling pointer.
Probably "dangling, well aligned pointer.".
rust/kernel/alloc/kbox.rs line 65
Quoted 4 lines
rust/kernel/alloc/kbox.rs new file +4
-0+#[repr(transparent)] +pub struct Box <T : ?Sized ,A :Allocator >(NonNull <T >,PhantomData <A >); + +/// Type alias for `Box` with a [`Kmalloc`] allocator.
Can you make these Box references links?
Quoted 53 lines
rust/kernel/alloc/kbox.rs new file +53
-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. +/// +/// # Examples +/// +/// ``` +/// let b = VBox::new(24_u64, GFP_KERNEL)?; +/// +/// assert_eq!(*b, 24_u64); +/// # Ok::<(), Error>(()) +/// ``` +pub type VBox <T > =Box <T ,super ::allocator::Vmalloc >; + +/// Type alias for `Box` with a [`KVmalloc`] allocator. +/// +/// # Examples +/// +/// ``` +/// let b = KVBox::new(24_u64, GFP_KERNEL)?; +/// +/// assert_eq!(*b, 24_u64); +/// # Ok::<(), Error>(()) +/// ``` +pub type KVBox <T > =Box <T ,super ::allocator::KVmalloc >; + +// SAFETY: `Box` is `Send` if `T` is `Send` because the `Box` owns a `T`. +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 `Box` owns a `T`. +unsafe impl <T ,A >Sync for Box <T ,A > +where +T :Sync + ?Sized , +A :Allocator , +{ +} + +impl <T ,A >Box <T ,A > +where
rust/kernel/alloc/kbox.rs line 128
Quoted 10 lines
rust/kernel/alloc/kbox.rs new file +10
-0+ T: ?Sized, + A: Allocator, +{ +/// Creates a new `Box<T, A>` from a raw pointer. +/// +/// # Safety +/// +/// For non-ZSTs, `raw` must point at an allocation allocated with `A`that is sufficiently +/// aligned for and holds a valid `T`. The caller passes ownership of the allocation to the +/// `Box`.
You don't say what must happen for ZSTs.
Quoted 50 lines
rust/kernel/alloc/kbox.rs new file +50
-0+#[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); + + 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 { +// 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) } + } +} + +impl <T ,A >Box <MaybeUninit <T >,A > +where +A :Allocator , +{ +/// Converts a `Box<MaybeUninit<T>, A>` to a `Box<T, A>`. +/// +/// It is undefined behavior to call this function while the value inside of `b` is not yet +/// fully initialized. +///
rust/kernel/alloc/kbox.rs line 188
Quoted 10 lines
rust/kernel/alloc/kbox.rs new file +10
-0+/// # Safety +/// +/// Callers must ensure that the value inside of `b` is in an initialized state. +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 ) }
You should be able to use .cast() instead.
rust/kernel/alloc/kbox.rs line 194
Quoted 6 lines
rust/kernel/alloc/kbox.rs new file +6
-0+ } + +/// 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.
The variable is called b.
Quoted 3 lines
rust/kernel/alloc/kbox.rs new file +3
-0+unsafe {Self ::assume_init (b) } + } +}
[...]
rust/kernel/alloc/kbox.rs line 472
Quoted 9 lines
rust/kernel/alloc/kbox.rs new file +9
-0+impl <T ,A >Drop for Box <T ,A > +where +T : ?Sized , +A :Allocator , +{ +fn drop (&mut self) { +let size = core::mem::size_of_val ::<T >(self); + +// SAFETY: We need to drop `self.0` in place, before we free the backing memory.
This is the reason you are calling this function, not the justification why it is OK to do so. (the pointer is valid)
Quoted 1 line
+ unsafe { core::ptr::drop_in_place(self.0.as_ptr()) };
Instead of using the raw pointer directly, you can also just use deref_mut.
rust/kernel/alloc/kbox.rs line 476
Quoted 3 lines
rust/kernel/alloc/kbox.rs new file +3
-0+ +if size !=0 { +// SAFETY: `ptr` was previously allocated with `A`.
There is no variable ptr, this is guaranteed by the type invariant of Self.
---
Cheers,
Benno
Quoted 20 lines
rust/kernel/alloc/kbox.rs new file +4
-0+unsafe {A ::free (self.0 .cast ()) }; + } + } +}rust/kernel/prelude.rs +1
-2@@ -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 }; --2.46.0
Danilo Krummrich
maintainerRe: [PATCH v6 09/26] rust: alloc: implement kernel `Box`
Quoted 58 lines · Benno Lossin
Quoted 16 lines · Danilo Krummrich
rust/kernel/alloc/kbox.rs new file +10
-0@@ -0,0 +1,480 @@ +// SPDX-License-Identifier: GPL-2.0 + +//! Implementation of [`Box`]. + +#[allow(unused_imports)] // Used in doc comments. +use super ::allocator::{KVmalloc ,Kmalloc ,Vmalloc }; +use super ::{AllocError ,Allocator ,Flags }; +use core::fmt; +use core::marker::PhantomData ; +use core::mem::ManuallyDrop ;rust/kernel/alloc/kbox.rs line 20
Quoted 10 lines
rust/kernel/alloc/kbox.rs new file +10
-0+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 ,InPlaceWrite ,Init ,PinInit }; +use crate ::types::ForeignOwnable ; + +/// The kernel's [`Box`] type - a heap allocation for a single value of type `T`. A single
-doesn't really render nicely in markdown, instead use a double or triple dash (--or---).rust/kernel/alloc/kbox.rs line 26
Quoted 6 lines
rust/kernel/alloc/kbox.rs new file +6
-0+/// +/// This is the kernel's version of the Rust stdlib's `Box`. There are several of differences, +/// for example no `noalias` attribute is emitted and partially moving out of a `Box` is not +/// supported. There are also several API differences, e.g. `Box` always requires an [`Allocator`] +/// implementation to be passed as generic, page [`Flags`] when allocating memory and all functions +/// that may allocate memory are failable. Do you mean fallible?
Quoted 18 lines
rust/kernel/alloc/kbox.rs new file +18
-0+/// +/// `Box` works with any of the kernel's allocators, e.g. [`Kmalloc`], [`Vmalloc`] or [`KVmalloc`]. +/// There are aliases for `Box` with these allocators ([`KBox`], [`VBox`], [`KVBox`]). +/// +/// 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;
Quoted 8 lines
rust/kernel/alloc/kbox.rs line 48
Quoted 4 lines
rust/kernel/alloc/kbox.rs new file +4
-0+/// struct Huge([u8; SIZE]); +/// +/// assert!(KBox::<Huge>::new_uninit(GFP_KERNEL | __GFP_NOWARN).is_err()); +/// ``` It would be nice if you could add something like "KBox can't handle big allocations:" above this example, so that people aren't confused why this example expects an error.
I don't think that's needed, it's implied bySIZE == bindings::KMALLOC_MAX_SIZE + 1.
Surely, we could add it nevertheless, but it's not very precise to just say "big allocations". And I think this isn't the place for lengthy explanations of Kmalloc behavior.
Quoted 20 lines
rust/kernel/alloc/kbox.rs line 56
Quoted 9 lines
rust/kernel/alloc/kbox.rs new file +8
-0+/// +/// ``` +/// # use kernel::bindings; +/// const SIZE: usize = bindings::KMALLOC_MAX_SIZE as usize + 1; +/// struct Huge([u8; SIZE]); +/// +/// assert!(KVBox::<Huge>::new_uninit(GFP_KERNEL).is_ok()); +/// ``` Similarly, you could then say above this one "Instead use either
VBoxorKVBox:"rust/kernel/alloc/kbox.rs line 60
Quoted 4 lines
rust/kernel/alloc/kbox.rs new file +4
-0+/// +/// # Invariants +/// +/// The [`Box`]' pointer is always properly aligned and either points to memory allocated with `A` Please use
self.0instead of "[Box]'".
Quoted 3 lines
rust/kernel/alloc/kbox.rs line 61
Quoted 1 line
rust/kernel/alloc/kbox.rs new file +1
-0+/// or, for zero-sized types, is a dangling pointer. Probably "dangling, well aligned pointer.".
Does this add any value? For ZSTs everything is "well aligned", isn't it?
Quoted 65 lines
rust/kernel/alloc/kbox.rs line 65
Quoted 5 lines
rust/kernel/alloc/kbox.rs new file +4
-0+#[repr(transparent)] +pub struct Box <T : ?Sized ,A :Allocator >(NonNull <T >,PhantomData <A >); + +/// Type alias for `Box` with a [`Kmalloc`] allocator. Can you make these
Boxreferences links?Quoted 57 lines
rust/kernel/alloc/kbox.rs new file +57
-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. +/// +/// # Examples +/// +/// ``` +/// let b = VBox::new(24_u64, GFP_KERNEL)?; +/// +/// assert_eq!(*b, 24_u64); +/// # Ok::<(), Error>(()) +/// ``` +pub type VBox <T > =Box <T ,super ::allocator::Vmalloc >; + +/// Type alias for `Box` with a [`KVmalloc`] allocator. +/// +/// # Examples +/// +/// ``` +/// let b = KVBox::new(24_u64, GFP_KERNEL)?; +/// +/// assert_eq!(*b, 24_u64); +/// # Ok::<(), Error>(()) +/// ``` +pub type KVBox <T > =Box <T ,super ::allocator::KVmalloc >; + +// SAFETY: `Box` is `Send` if `T` is `Send` because the `Box` owns a `T`. +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 `Box` owns a `T`. +unsafe impl <T ,A >Sync for Box <T ,A > +where +T :Sync + ?Sized , +A :Allocator , +{ +} + +impl <T ,A >Box <T ,A > +where +T : ?Sized , +A :Allocator , +{ +/// Creates a new `Box<T, A>` from a raw pointer.
Quoted 8 lines
rust/kernel/alloc/kbox.rs line 128
Quoted 6 lines
rust/kernel/alloc/kbox.rs new file +6
-0+/// +/// # Safety +/// +/// For non-ZSTs, `raw` must point at an allocation allocated with `A`that is sufficiently +/// aligned for and holds a valid `T`. The caller passes ownership of the allocation to the +/// `Box`. You don't say what must happen for ZSTs.
Because we don't require anything for a ZST, do we?
Quoted 129 lines
Quoted 51 lines
rust/kernel/alloc/kbox.rs new file +50
-0+#[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); + + 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 { +// 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) } + } +} + +impl <T ,A >Box <MaybeUninit <T >,A > +where +A :Allocator , +{ +/// Converts a `Box<MaybeUninit<T>, A>` to a `Box<T, A>`. +/// +/// It is undefined behavior to call this function while the value inside of `b` is not yet +/// fully initialized. +/// rust/kernel/alloc/kbox.rs line 188
Quoted 10 lines
rust/kernel/alloc/kbox.rs new file +10
-0+/// # Safety +/// +/// Callers must ensure that the value inside of `b` is in an initialized state. +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 ) }You should be able to use
.cast()instead.rust/kernel/alloc/kbox.rs line 194
Quoted 6 lines
rust/kernel/alloc/kbox.rs new file +6
-0+ } + +/// 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. The variable is called
b.Quoted 3 lines
rust/kernel/alloc/kbox.rs new file +3
-0+unsafe {Self ::assume_init (b) } + } +}[...]
rust/kernel/alloc/kbox.rs line 472
Quoted 9 lines
rust/kernel/alloc/kbox.rs new file +9
-0+impl <T ,A >Drop for Box <T ,A > +where +T : ?Sized , +A :Allocator , +{ +fn drop (&mut self) { +let size = core::mem::size_of_val ::<T >(self); + +// SAFETY: We need to drop `self.0` in place, before we free the backing memory. This is the reason you are calling this function, not the justification why it is OK to do so. (the pointer is valid)
Quoted 1 line
+ unsafe { core::ptr::drop_in_place(self.0.as_ptr()) };
Instead of using the raw pointer directly, you can also just use
deref_mut.rust/kernel/alloc/kbox.rs line 476
Quoted 3 lines
rust/kernel/alloc/kbox.rs new file +3
-0+ +if size !=0 { +// SAFETY: `ptr` was previously allocated with `A`. There is no variable
ptr, this is guaranteed by the type invariant ofSelf.---
Cheers,
BennoQuoted 20 lines
rust/kernel/alloc/kbox.rs new file +4
-0+unsafe {A ::free (self.0 .cast ()) }; + } + } +}rust/kernel/prelude.rs +1
-2@@ -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 }; --2.46.0
Re: [PATCH v6 09/26] rust: alloc: implement kernel `Box`
Quoted 23 lines · Danilo Krummrich
Quoted 13 lines · Benno Lossin
Quoted 12 lines · Danilo Krummrich
rust/kernel/alloc/kbox.rs new file +12
-0+/// # 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; Quoted 8 lines
rust/kernel/alloc/kbox.rs line 48
Quoted 4 lines
rust/kernel/alloc/kbox.rs new file +4
-0+/// struct Huge([u8; SIZE]); +/// +/// assert!(KBox::<Huge>::new_uninit(GFP_KERNEL | __GFP_NOWARN).is_err()); +/// ``` It would be nice if you could add something like "KBox can't handle big allocations:" above this example, so that people aren't confused why this example expects an error.
Quoted 6 lines
I don't think that's needed, it's implied by
SIZE == bindings::KMALLOC_MAX_SIZE + 1.Surely, we could add it nevertheless, but it's not very precise to just say "big allocations". And I think this isn't the place for lengthy explanations of
Kmallocbehavior.
Fair point, nevertheless I find examples a bit more useful, when the intention behind them is not only given as code.
Quoted 19 lines
Quoted 19 lines
rust/kernel/alloc/kbox.rs line 56
Quoted 8 lines
rust/kernel/alloc/kbox.rs new file +8
-0+/// +/// ``` +/// # use kernel::bindings; +/// const SIZE: usize = bindings::KMALLOC_MAX_SIZE as usize + 1; +/// struct Huge([u8; SIZE]); +/// +/// assert!(KVBox::<Huge>::new_uninit(GFP_KERNEL).is_ok()); +/// ``` Similarly, you could then say above this one "Instead use either
VBoxorKVBox:"rust/kernel/alloc/kbox.rs line 60
Quoted 4 lines
rust/kernel/alloc/kbox.rs new file +4
-0+/// +/// # Invariants +/// +/// The [`Box`]' pointer is always properly aligned and either points to memory allocated with `A` Please use
self.0instead of "[Box]'".
Quoted 5 lines
Quoted 3 lines
Quoted 1 line
+/// or, for zero-sized types, is a dangling pointer.
Probably "dangling, well aligned pointer.".
Does this add any value? For ZSTs everything is "well aligned", isn't it?
ZSTs can have alignment and then unaligned pointers do exist for them (and dereferencing them is UB!):
#[repr(align(64))]
struct Token;
fn main() {
let t = 64 as *mut Token;
let t = unsafe { t.read() }; // this is fine.
let t = 4 as *mut Token;
let t = unsafe { t.read() }; // this is UB, see below for miri's output
}Miri complains:
error: Undefined Behavior: accessing memory based on pointer with alignment 4, but alignment 64 is required
--> src/main.rs:8:22
|
8 | let t = unsafe { t.read() }; // this is UB, see below for miri's output
| ^^^^^^^^ accessing memory based on pointer with alignment 4, but alignment 64 is required
|
= help: this indicates a bug in the program: it performed an invalid operation, and caused Undefined Behavior = help: see https://doc.rust-lang.org/nightly/reference/behavior-considered-undefined.html for further information = note: BACKTRACE:
= note: inside main at src/main.rs:8:22: 8:30
Quoted 19 lines
Quoted 4 lines
Quoted 2 lines
rust/kernel/alloc/kbox.rs new file +2
-0+#[repr(transparent)] +pub struct Box <T : ?Sized ,A :Allocator >(NonNull <T >,PhantomData <A >);Quoted 14 lines
rust/kernel/alloc/kbox.rs line 128
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 +/// +/// For non-ZSTs, `raw` must point at an allocation allocated with `A`that is sufficiently +/// aligned for and holds a valid `T`. The caller passes ownership of the allocation to the +/// `Box`. You don't say what must happen for ZSTs.
Quoted 1 line
Because we don't require anything for a ZST, do we?
We require a non-null, well aligned pointer (see above).
---
Cheers,
Benno
Quoted 6 lines
Quoted 6 lines
Quoted 6 lines
rust/kernel/alloc/kbox.rs new file +6
-0+#[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 >) + }
Danilo Krummrich
maintainerRe: [PATCH v6 09/26] rust: alloc: implement kernel `Box`
Quoted 53 lines · Benno Lossin
Quoted 23 lines · Danilo Krummrich
Quoted 13 lines · Benno Lossin
Quoted 12 lines · Danilo Krummrich
rust/kernel/alloc/kbox.rs new file +12
-0+/// # 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; Quoted 8 lines
rust/kernel/alloc/kbox.rs line 48
Quoted 4 lines
rust/kernel/alloc/kbox.rs new file +4
-0+/// struct Huge([u8; SIZE]); +/// +/// assert!(KBox::<Huge>::new_uninit(GFP_KERNEL | __GFP_NOWARN).is_err()); +/// ``` It would be nice if you could add something like "KBox can't handle big allocations:" above this example, so that people aren't confused why this example expects an error.
Quoted 6 lines
I don't think that's needed, it's implied by
SIZE == bindings::KMALLOC_MAX_SIZE + 1.Surely, we could add it nevertheless, but it's not very precise to just say "big allocations". And I think this isn't the place for lengthy explanations of
Kmallocbehavior.Fair point, nevertheless I find examples a bit more useful, when the intention behind them is not only given as code.
Quoted 19 lines
Quoted 19 lines
rust/kernel/alloc/kbox.rs line 56
Quoted 8 lines
rust/kernel/alloc/kbox.rs new file +8
-0+/// +/// ``` +/// # use kernel::bindings; +/// const SIZE: usize = bindings::KMALLOC_MAX_SIZE as usize + 1; +/// struct Huge([u8; SIZE]); +/// +/// assert!(KVBox::<Huge>::new_uninit(GFP_KERNEL).is_ok()); +/// ``` Similarly, you could then say above this one "Instead use either
VBoxorKVBox:"rust/kernel/alloc/kbox.rs line 60
Quoted 4 lines
rust/kernel/alloc/kbox.rs new file +4
-0+/// +/// # Invariants +/// +/// The [`Box`]' pointer is always properly aligned and either points to memory allocated with `A` Please use
self.0instead of "[Box]'".
Quoted 8 lines
Quoted 5 lines
Quoted 3 lines
Quoted 1 line
+/// or, for zero-sized types, is a dangling pointer.
Probably "dangling, well aligned pointer.".
Does this add any value? For ZSTs everything is "well aligned", isn't it?
ZSTs can have alignment and then unaligned pointers do exist for them (and dereferencing them is UB!):
Where is this documented? The documentation says:
"For operations of size zero, *every* pointer is valid, including the null pointer. The following points are only concerned with non-zero-sized accesses."
[1]
[1] https://doc.rust-lang.org/std/ptr/index.html
Quoted 13 lines
#[repr(align(64))] struct Token; fn main() { let t = 64 as *mut Token; let t = unsafe { t.read() }; // this is fine. let t = 4 as *mut Token; let t = unsafe { t.read() }; // this is UB, see below for miri's output }Miri complains:
Quoted 10 lines
error: Undefined Behavior: accessing memory based on pointer with alignment 4, but alignment 64 is required
-->src/main.rs:8:22
|
8 | let t = unsafe {t.read()}; // this is UB, see below for miri's output
| ^^^^^^^^ accessing memory based on pointer with alignment 4, but alignment 64 is required
|
= help: this indicates a bug in the program: it performed an invalid operation, and caused Undefined Behavior = help: see https://doc.rust-lang.org/nightly/reference/behavior-considered-undefined.html for further information = note: BACKTRACE:
= note: insidemainatsrc/main.rs:8:22: 8:30
read explicitly asks for non-null and properly aligned even if T has size zero.
Quoted 20 lines
Quoted 20 lines
Quoted 19 lines
Quoted 7 lines
rust/kernel/alloc/kbox.rs new file +4
-0+#[repr(transparent)] +pub struct Box <T : ?Sized ,A :Allocator >(NonNull <T >,PhantomData <A >); +impl <T ,A >Box <T ,A > +where rust/kernel/alloc/kbox.rs line 128
Quoted 10 lines
rust/kernel/alloc/kbox.rs new file +10
-0+ T: ?Sized, + A: Allocator, +{ +/// Creates a new `Box<T, A>` from a raw pointer. +/// +/// # Safety +/// +/// For non-ZSTs, `raw` must point at an allocation allocated with `A`that is sufficiently +/// aligned for and holds a valid `T`. The caller passes ownership of the allocation to the +/// `Box`. You don't say what must happen for ZSTs.
Quoted 3 lines
Quoted 1 line
Because we don't require anything for a ZST, do we?
We require a non-null, well aligned pointer (see above).
We indeed do, gonna add it.
Quoted 12 lines
---
Cheers,
BennoQuoted 6 lines
Quoted 6 lines
Quoted 6 lines
rust/kernel/alloc/kbox.rs new file +6
-0+#[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 >) + }
Re: [PATCH v6 09/26] rust: alloc: implement kernel `Box`
Quoted 60 lines · Danilo Krummrich
Quoted 59 lines · Benno Lossin
Quoted 23 lines · Danilo Krummrich
Quoted 13 lines · Benno Lossin
Quoted 12 lines · Danilo Krummrich
rust/kernel/alloc/kbox.rs new file +12
-0+/// # 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; Quoted 8 lines
rust/kernel/alloc/kbox.rs line 48
Quoted 4 lines
rust/kernel/alloc/kbox.rs new file +4
-0+/// struct Huge([u8; SIZE]); +/// +/// assert!(KBox::<Huge>::new_uninit(GFP_KERNEL | __GFP_NOWARN).is_err()); +/// ``` It would be nice if you could add something like "KBox can't handle big allocations:" above this example, so that people aren't confused why this example expects an error.
Quoted 6 lines
I don't think that's needed, it's implied by
SIZE == bindings::KMALLOC_MAX_SIZE + 1.Surely, we could add it nevertheless, but it's not very precise to just say "big allocations". And I think this isn't the place for lengthy explanations of
Kmallocbehavior.Fair point, nevertheless I find examples a bit more useful, when the intention behind them is not only given as code.
Quoted 24 lines
Quoted 19 lines
rust/kernel/alloc/kbox.rs line 56
Quoted 8 lines
rust/kernel/alloc/kbox.rs new file +8
-0+/// +/// ``` +/// # use kernel::bindings; +/// const SIZE: usize = bindings::KMALLOC_MAX_SIZE as usize + 1; +/// struct Huge([u8; SIZE]); +/// +/// assert!(KVBox::<Huge>::new_uninit(GFP_KERNEL).is_ok()); +/// ``` Similarly, you could then say above this one "Instead use either
VBoxorKVBox:"rust/kernel/alloc/kbox.rs line 60
Quoted 4 lines
rust/kernel/alloc/kbox.rs new file +4
-0+/// +/// # Invariants +/// +/// The [`Box`]' pointer is always properly aligned and either points to memory allocated with `A` Please use
self.0instead of "[Box]'".Quoted 3 lines
Quoted 1 line
+/// or, for zero-sized types, is a dangling pointer.
Probably "dangling, well aligned pointer.".
Does this add any value? For ZSTs everything is "well aligned", isn't it?
Quoted 8 lines
Quoted 2 lines
ZSTs can have alignment and then unaligned pointers do exist for them (and dereferencing them is UB!):
Where is this documented? The documentation says:
"For operations of size zero, *every* pointer is valid, including the null pointer. The following points are only concerned with non-zero-sized accesses."
[1]
That's a good point, the documentation looks a bit outdated. I found this page in the nomicon: https://doc.rust-lang.org/nomicon/vec/vec-zsts.html
The first iterator implementation has an alignment issue. (Nevertheless, that chapter of the nomicon is probably useful to you, since it goes over implementing Vec, but maybe you already saw it)
Quoted 1 line
Might be a good idea to improve/complain about this at the rust project.
Quoted 23 lines
Quoted 12 lines
#[repr(align(64))] struct Token; fn main() { let t = 64 as *mut Token; let t = unsafe { t.read() }; // this is fine. let t = 4 as *mut Token; let t = unsafe { t.read() }; // this is UB, see below for miri's output }Miri complains:
Quoted 10 lines
error: Undefined Behavior: accessing memory based on pointer with alignment 4, but alignment 64 is required
-->src/main.rs:8:22
|
8 | let t = unsafe {t.read()}; // this is UB, see below for miri's output
| ^^^^^^^^ accessing memory based on pointer with alignment 4, but alignment 64 is required
|
= help: this indicates a bug in the program: it performed an invalid operation, and caused Undefined Behavior = help: see https://doc.rust-lang.org/nightly/reference/behavior-considered-undefined.html for further information = note: BACKTRACE:
= note: insidemainatsrc/main.rs:8:22: 8:30
Quoted 2 lines
readexplicitly asks for non-null and properly aligned even ifThas size zero.
Dereferencing (ie *t) also requires that (I just didn't do it, since then the Token must implement Copy).
---
Cheers,
Benno
Danilo Krummrich
maintainerRe: [PATCH v6 09/26] rust: alloc: implement kernel `Box`
Quoted 76 lines · Benno Lossin
Quoted 60 lines · Danilo Krummrich
Quoted 59 lines · Benno Lossin
Quoted 23 lines · Danilo Krummrich
Quoted 13 lines · Benno Lossin
Quoted 12 lines · Danilo Krummrich
rust/kernel/alloc/kbox.rs new file +12
-0+/// # 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; Quoted 8 lines
rust/kernel/alloc/kbox.rs line 48
Quoted 4 lines
rust/kernel/alloc/kbox.rs new file +4
-0+/// struct Huge([u8; SIZE]); +/// +/// assert!(KBox::<Huge>::new_uninit(GFP_KERNEL | __GFP_NOWARN).is_err()); +/// ``` It would be nice if you could add something like "KBox can't handle big allocations:" above this example, so that people aren't confused why this example expects an error.
Quoted 6 lines
I don't think that's needed, it's implied by
SIZE == bindings::KMALLOC_MAX_SIZE + 1.Surely, we could add it nevertheless, but it's not very precise to just say "big allocations". And I think this isn't the place for lengthy explanations of
Kmallocbehavior.Fair point, nevertheless I find examples a bit more useful, when the intention behind them is not only given as code.
Quoted 24 lines
Quoted 19 lines
rust/kernel/alloc/kbox.rs line 56
Quoted 8 lines
rust/kernel/alloc/kbox.rs new file +8
-0+/// +/// ``` +/// # use kernel::bindings; +/// const SIZE: usize = bindings::KMALLOC_MAX_SIZE as usize + 1; +/// struct Huge([u8; SIZE]); +/// +/// assert!(KVBox::<Huge>::new_uninit(GFP_KERNEL).is_ok()); +/// ``` Similarly, you could then say above this one "Instead use either
VBoxorKVBox:"rust/kernel/alloc/kbox.rs line 60
Quoted 4 lines
rust/kernel/alloc/kbox.rs new file +4
-0+/// +/// # Invariants +/// +/// The [`Box`]' pointer is always properly aligned and either points to memory allocated with `A` Please use
self.0instead of "[Box]'".Quoted 3 lines
Quoted 1 line
+/// or, for zero-sized types, is a dangling pointer.
Probably "dangling, well aligned pointer.".
Does this add any value? For ZSTs everything is "well aligned", isn't it?
Quoted 8 lines
Quoted 2 lines
ZSTs can have alignment and then unaligned pointers do exist for them (and dereferencing them is UB!):
Where is this documented? The documentation says:
"For operations of size zero, *every* pointer is valid, including the null pointer. The following points are only concerned with non-zero-sized accesses."
[1]That's a good point, the documentation looks a bit outdated. I found this page in the nomicon: https://doc.rust-lang.org/nomicon/vec/vec-zsts.html
The first iterator implementation has an alignment issue. (Nevertheless, that chapter of the nomicon is probably useful to you, since it goes over implementingVec, but maybe you already saw it)
Quoted 3 lines
Quoted 1 line
Might be a good idea to improve/complain about this at the rust project.
Well, my point is how do we know? There's no language specification and the documentation is (at least) ambiguous.
Quoted 24 lines
Quoted 24 lines
Quoted 23 lines
#[repr(align(64))] struct Token; fn main() { let t = 64 as *mut Token; let t = unsafe { t.read() }; // this is fine. let t = 4 as *mut Token; let t = unsafe { t.read() }; // this is UB, see below for miri's output }Miri complains:
error: Undefined Behavior: accessing memory based on pointer with alignment 4, but alignment 64 is required
-->src/main.rs:8:22
|
8 | let t = unsafe {t.read()}; // this is UB, see below for miri's output
| ^^^^^^^^ accessing memory based on pointer with alignment 4, but alignment 64 is required
|
= help: this indicates a bug in the program: it performed an invalid operation, and caused Undefined Behavior = help: see https://doc.rust-lang.org/nightly/reference/behavior-considered-undefined.html for further information = note: BACKTRACE:
= note: insidemainatsrc/main.rs:8:22: 8:30
Quoted 2 lines
Quoted 2 lines
readexplicitly asks for non-null and properly aligned even ifThas size zero.
I mentioned this because for read it's explicitly documented.
However, the nomicon also says "This is possibly needless pedantry because ptr::read is a noop for a ZST, [...]".
Quoted 3 lines
Dereferencing (ie
*t) also requires that (I just didn't do it, since then theTokenmust implementCopy).
Again, how do you know? The documentation isn't clear about it.
Quoted 5 lines
---
Cheers,
Benno
Re: [PATCH v6 09/26] rust: alloc: implement kernel `Box`
Quoted 46 lines · Danilo Krummrich
Quoted 45 lines · Benno Lossin
Quoted 29 lines · Danilo Krummrich
Quoted 28 lines · Benno Lossin
Quoted 26 lines · Danilo Krummrich
Quoted 20 lines · Benno Lossin
Review
Quoted 8 lines · Danilo Krummrich
(quoted diff) +8
-0+/// +/// ``` +/// # use kernel::bindings; +/// const SIZE: usize = bindings::KMALLOC_MAX_SIZE as usize + 1; +/// struct Huge([u8; SIZE]); +/// +/// assert!(KVBox::<Huge>::new_uninit(GFP_KERNEL).is_ok()); +/// ```Similarly, you could then say above this one "Instead use either
VBoxorKVBox:"Review
Quoted 4 lines
(quoted diff) +4
-0+/// +/// # Invariants +/// +/// The [`Box`]' pointer is always properly aligned and either points to memory allocated with `A`Please use
self.0instead of "[Box]'".Quoted 3 lines
Quoted 1 line
+/// or, for zero-sized types, is a dangling pointer.
Probably "dangling, well aligned pointer.".
Does this add any value? For ZSTs everything is "well aligned", isn't it?
Quoted 8 lines
Quoted 2 lines
ZSTs can have alignment and then unaligned pointers do exist for them (and dereferencing them is UB!):
Where is this documented? The documentation says:
"For operations of size zero, *every* pointer is valid, including the null pointer. The following points are only concerned with non-zero-sized accesses."
[1]That's a good point, the documentation looks a bit outdated. I found this page in the nomicon: https://doc.rust-lang.org/nomicon/vec/vec-zsts.html
The first iterator implementation has an alignment issue. (Nevertheless, that chapter of the nomicon is probably useful to you, since it goes over implementingVec, but maybe you already saw it)
Quoted 6 lines
Quoted 3 lines
Quoted 1 line
Might be a good idea to improve/complain about this at the rust project.
Well, my point is how do we know? There's no language specification and the documentation is (at least) ambiguous.
So I went through the unsafe-coding-guidelines issues list and only found this one: https://github.com/rust-lang/unsafe-code-guidelines/issues/93
Maybe I missed something. You could also try to ask at the rust zulip in the t-opsem channel for further clarification.
I think we should just be on the safe side and assume that ZSTs require alignment. But if you get a convincing answer and if they say that they will document it, then I don't mind removing the alignment requirement.
---
Cheers,
Benno
Alice Ryhl
maintainerRe: [PATCH v6 09/26] rust: alloc: implement kernel `Box`
Quoted 55 lines · Benno Lossin
Quoted 46 lines · Danilo Krummrich
Quoted 45 lines · Benno Lossin
Quoted 29 lines · Danilo Krummrich
Quoted 28 lines · Benno Lossin
Quoted 26 lines · Danilo Krummrich
Quoted 20 lines · Benno Lossin
Review
Quoted 8 lines · Danilo Krummrich
(quoted diff) +8
-0+/// +/// ``` +/// # use kernel::bindings; +/// const SIZE: usize = bindings::KMALLOC_MAX_SIZE as usize + 1; +/// struct Huge([u8; SIZE]); +/// +/// assert!(KVBox::<Huge>::new_uninit(GFP_KERNEL).is_ok()); +/// ```Similarly, you could then say above this one "Instead use either
VBoxorKVBox:"Review
Quoted 4 lines
(quoted diff) +4
-0+/// +/// # Invariants +/// +/// The [`Box`]' pointer is always properly aligned and either points to memory allocated with `A`Please use
self.0instead of "[Box]'".Quoted 3 lines
Quoted 1 line
+/// or, for zero-sized types, is a dangling pointer.
Probably "dangling, well aligned pointer.".
Does this add any value? For ZSTs everything is "well aligned", isn't it?
Quoted 8 lines
Quoted 2 lines
ZSTs can have alignment and then unaligned pointers do exist for them (and dereferencing them is UB!):
Where is this documented? The documentation says:
"For operations of size zero, *every* pointer is valid, including the null pointer. The following points are only concerned with non-zero-sized accesses."
[1]That's a good point, the documentation looks a bit outdated. I found this page in the nomicon: https://doc.rust-lang.org/nomicon/vec/vec-zsts.html
The first iterator implementation has an alignment issue. (Nevertheless, that chapter of the nomicon is probably useful to you, since it goes over implementingVec, but maybe you already saw it)Quoted 6 lines
Quoted 3 lines
Quoted 1 line
Might be a good idea to improve/complain about this at the rust project.
Well, my point is how do we know? There's no language specification and the documentation is (at least) ambiguous.
Quoted 8 lines
So I went through the unsafe-coding-guidelines issues list and only found this one: https://github.com/rust-lang/unsafe-code-guidelines/issues/93
Maybe I missed something. You could also try to ask at the rust zulip in the t-opsem channel for further clarification.I think we should just be on the safe side and assume that ZSTs require alignment. But if you get a convincing answer and if they say that they will document it, then I don't mind removing the alignment requirement.
Please see the section on alignment in the same page. Just because a pointer is valid does not mean that it is properly aligned.
From the page:
Valid raw pointers as defined above are not necessarily properly aligned (where “proper” alignment is defined by the pointee type, i.e., *const T must be aligned to mem::align_of::<T>()). However, most functions require their arguments to be properly aligned, and will explicitly state this requirement in their documentation. Notable exceptions to this are read_unaligned and write_unaligned.
When a function requires proper alignment, it does so even if the access has size 0, i.e., even if memory is not actually touched. Consider using NonNull::dangling in such cases.
Alice
Danilo Krummrich
maintainerRe: [PATCH v6 09/26] rust: alloc: implement kernel `Box`
Quoted 56 lines · Alice Ryhl
Quoted 55 lines · Benno Lossin
Quoted 52 lines · Danilo Krummrich
Quoted 45 lines · Benno Lossin
Quoted 29 lines · Danilo Krummrich
Quoted 28 lines · Benno Lossin
Quoted 26 lines · Danilo Krummrich
Quoted 20 lines · Benno Lossin
Review
Quoted 8 lines · Danilo Krummrich
(quoted diff) +8
-0+/// +/// ``` +/// # use kernel::bindings; +/// const SIZE: usize = bindings::KMALLOC_MAX_SIZE as usize + 1; +/// struct Huge([u8; SIZE]); +/// +/// assert!(KVBox::<Huge>::new_uninit(GFP_KERNEL).is_ok()); +/// ```Similarly, you could then say above this one "Instead use either
VBoxorKVBox:"Review
Quoted 4 lines
(quoted diff) +4
-0+/// +/// # Invariants +/// +/// The [`Box`]' pointer is always properly aligned and either points to memory allocated with `A`Please use
self.0instead of "[Box]'".Quoted 3 lines
Quoted 1 line
+/// or, for zero-sized types, is a dangling pointer.
Probably "dangling, well aligned pointer.".
Does this add any value? For ZSTs everything is "well aligned", isn't it?
Quoted 8 lines
Quoted 2 lines
ZSTs can have alignment and then unaligned pointers do exist for them (and dereferencing them is UB!):
Where is this documented? The documentation says:
"For operations of size zero, *every* pointer is valid, including the null pointer. The following points are only concerned with non-zero-sized accesses."
[1]That's a good point, the documentation looks a bit outdated. I found this page in the nomicon: https://doc.rust-lang.org/nomicon/vec/vec-zsts.html
The first iterator implementation has an alignment issue. (Nevertheless, that chapter of the nomicon is probably useful to you, since it goes over implementingVec, but maybe you already saw it)Quoted 3 lines
Quoted 1 line
Might be a good idea to improve/complain about this at the rust project.
Well, my point is how do we know? There's no language specification and the documentation is (at least) ambiguous.
Quoted 8 lines
Quoted 8 lines
So I went through the unsafe-coding-guidelines issues list and only found this one: https://github.com/rust-lang/unsafe-code-guidelines/issues/93
Maybe I missed something. You could also try to ask at the rust zulip in the t-opsem channel for further clarification.I think we should just be on the safe side and assume that ZSTs require alignment. But if you get a convincing answer and if they say that they will document it, then I don't mind removing the alignment requirement.
I agree -- I also wrote this in a previous mail.
I was just wondering why you are so sure about it, since the documentation doesn't seem to be clear about it.
Quoted 13 lines
Please see the section on alignment in the same page. Just because a pointer is valid does not mean that it is properly aligned.
From the page:
Valid raw pointers as defined above are not necessarily properly aligned (where “proper” alignment is defined by the pointee type, i.e., *const T must be aligned to mem::align_of::<T>()). However, most functions require their arguments to be properly aligned, and will explicitly state this requirement in their documentation. Notable exceptions to this are
read_unalignedandwrite_unaligned.
Quoted 3 lines
When a function requires proper alignment, it does so even if the access has size 0, i.e., even if memory is not actually touched. Consider using
NonNull::danglingin such cases.
Good point.
It still sounds like it's only required for functions that explicitly state so.
And as cited from nomicon "This is possibly needless pedantry because ptr::read is a noop for a ZST, [...]". But, no question, of course we have to honor it anyways.
Quoted 3 lines
Alice
Re: [PATCH v6 09/26] rust: alloc: implement kernel `Box`
Quoted 61 lines · Danilo Krummrich
Quoted 60 lines · Alice Ryhl
Quoted 59 lines · Benno Lossin
Quoted 46 lines · Danilo Krummrich
Quoted 45 lines · Benno Lossin
Quoted 29 lines · Danilo Krummrich
Quoted 28 lines · Benno Lossin
Quoted 26 lines · Danilo Krummrich
Quoted 20 lines · Benno Lossin
Review
Quoted 8 lines · Danilo Krummrich
(quoted diff) +8
-0+/// +/// ``` +/// # use kernel::bindings; +/// const SIZE: usize = bindings::KMALLOC_MAX_SIZE as usize + 1; +/// struct Huge([u8; SIZE]); +/// +/// assert!(KVBox::<Huge>::new_uninit(GFP_KERNEL).is_ok()); +/// ```Similarly, you could then say above this one "Instead use either
VBoxorKVBox:"Review
Quoted 4 lines
(quoted diff) +4
-0+/// +/// # Invariants +/// +/// The [`Box`]' pointer is always properly aligned and either points to memory allocated with `A`Please use
self.0instead of "[Box]'".Quoted 3 lines
Quoted 1 line
+/// or, for zero-sized types, is a dangling pointer.
Probably "dangling, well aligned pointer.".
Does this add any value? For ZSTs everything is "well aligned", isn't it?
Quoted 8 lines
Quoted 2 lines
ZSTs can have alignment and then unaligned pointers do exist for them (and dereferencing them is UB!):
Where is this documented? The documentation says:
"For operations of size zero, *every* pointer is valid, including the null pointer. The following points are only concerned with non-zero-sized accesses."
[1]That's a good point, the documentation looks a bit outdated. I found this page in the nomicon: https://doc.rust-lang.org/nomicon/vec/vec-zsts.html
The first iterator implementation has an alignment issue. (Nevertheless, that chapter of the nomicon is probably useful to you, since it goes over implementingVec, but maybe you already saw it)Quoted 6 lines
Quoted 3 lines
Quoted 1 line
Might be a good idea to improve/complain about this at the rust project.
Well, my point is how do we know? There's no language specification and the documentation is (at least) ambiguous.
So I went through the unsafe-coding-guidelines issues list and only found this one: https://github.com/rust-lang/unsafe-code-guidelines/issues/93
Maybe I missed something. You could also try to ask at the rust zulip in the t-opsem channel for further clarification.
Quoted 8 lines
Quoted 3 lines
Quoted 3 lines
I think we should just be on the safe side and assume that ZSTs require alignment. But if you get a convincing answer and if they say that they will document it, then I don't mind removing the alignment requirement.
I agree -- I also wrote this in a previous mail.
I was just wondering why you are so sure about it, since the documentation doesn't seem to be clear about it.
As Alice found below, the documentation is actually clear about this. (I think I read it at some point, but forgot exactly where it was)
Maybe it could be better documented that dereferencing has the same requirements as read (or whatever they are).
Quoted 16 lines
Quoted 12 lines
Please see the section on alignment in the same page. Just because a pointer is valid does not mean that it is properly aligned.
From the page:
Valid raw pointers as defined above are not necessarily properly aligned (where “proper” alignment is defined by the pointee type, i.e., *const T must be aligned to mem::align_of::<T>()). However, most functions require their arguments to be properly aligned, and will explicitly state this requirement in their documentation. Notable exceptions to this are
read_unalignedandwrite_unaligned.Quoted 3 lines
When a function requires proper alignment, it does so even if the access has size 0, i.e., even if memory is not actually touched. Consider using
NonNull::danglingin such cases.
Quoted 7 lines
Good point.
It still sounds like it's only required for functions that explicitly state so.
And as cited from nomicon "This is possibly needless pedantry because
ptr::readis a noop for a ZST, [...]". But, no question, of course we have to honor it anyways.
This sounds to me like an implementation detail note, not something that a caller should consider. But that's my interpretation.
---
Cheers,
Benno
Danilo Krummrich
maintainerPATCH v6 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/rbtree.rs | 34 ++++++++++++--------- 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 ++-- 10 files changed, 73 insertions(+), 69 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/rbtree.rs +19 -15
@@ -7,7 +7,6 @@//! Reference: <https://www.kernel.org/doc/html/latest/core-api/rbtree.html> use crate ::{alloc::Flags , bindings, container_of, error::Result , prelude::*}; -use alloc::boxed::Box ;use core::{ cmp::{Ord ,Ordering }, marker::PhantomData ,
@@ -507,7 +506,7 @@ fn drop(&mut self) {// but it is not observable. The loop invariant is still maintained. // SAFETY: `this` is valid per the loop invariant. -unsafe {drop (Box ::from_raw (this.cast_mut ())) }; +unsafe {drop (KBox ::from_raw (this.cast_mut ())) }; } } }
@@ -771,7 +770,7 @@ pub fn remove_current(self) -> (Option<Self>, RBTreeNode<K, V>) {// point to the links field of `Node<K, V>` objects. let this =unsafe {container_of! (self.current.as_ptr (),Node <K ,V >, links) }.cast_mut ();// SAFETY: `this` is valid by the type invariants as described above. -let node =unsafe {Box ::from_raw (this) }; +let node =unsafe {KBox ::from_raw (this) };let node =RBTreeNode { node };// SAFETY: The reference to the tree used to create the cursor outlives the cursor, so // the tree cannot change. By the tree invariant, all nodes are valid.
@@ -819,7 +818,7 @@ fn remove_neighbor(&mut self, direction: Direction) -> Option<RBTreeNode<K, V>>// point to the links field of `Node<K, V>` objects. let this =unsafe {container_of! (neighbor,Node <K ,V >, links) }.cast_mut ();// SAFETY: `this` is valid by the type invariants as described above. -let node =unsafe {Box ::from_raw (this) }; +let node =unsafe {KBox ::from_raw (this) };return Some (RBTreeNode { node }); }None
@@ -1047,7 +1046,7 @@ fn next(&mut self) -> Option<Self::Item> {/// It contains the memory needed to hold a node that can be inserted into a red-black tree. One /// can be obtained by directly allocating it ([`RBTreeNodeReservation::new`]). pub struct RBTreeNodeReservation <K ,V > { - node:Box <MaybeUninit <Node <K ,V >>>, + node:KBox <MaybeUninit <Node <K ,V >>>, }impl <K ,V >RBTreeNodeReservation <K ,V > {
@@ -1055,7 +1054,7 @@ impl<K, V> RBTreeNodeReservation<K, V> {/// call to [`RBTree::insert`]. pub fn new (flags:Flags ) ->Result <RBTreeNodeReservation <K ,V >> {Ok (RBTreeNodeReservation { - node:Box ::new_uninit (flags)?, + node:KBox ::new_uninit (flags)?, }) } }
@@ -1072,7 +1071,7 @@ impl<K, V> RBTreeNodeReservation<K, V> {/// /// It then becomes an [`RBTreeNode`] that can be inserted into a tree. pub fn into_node (self, key:K , value:V ) ->RBTreeNode <K ,V > { - let node = Box::write( + let node = KBox::write( self.node, Node { key,
@@ -1089,7 +1088,7 @@ pub fn into_node(self, key: K, value: V) -> RBTreeNode<K, V> {/// The node is fully initialised (with key and value) and can be inserted into a tree without any /// extra allocations or failure paths. pub struct RBTreeNode <K ,V > { - node:Box <Node <K ,V >>, + node:KBox <Node <K ,V >>, }impl <K ,V >RBTreeNode <K ,V > {
@@ -1101,7 +1100,9 @@ pub fn new(key: K, value: V, flags: Flags) -> Result<RBTreeNode<K, V>> {/// Get the key and value from inside the node. pub fn to_key_value (self) -> (K ,V ) { - (self.node.key, self.node.value) +let node =KBox ::into_inner (self.node); + + (node.key, node.value) } }
@@ -1123,7 +1124,7 @@ impl<K, V> RBTreeNode<K, V> {/// may be freed (but only for the key/value; memory for the node itself is kept for reuse). pub fn into_reservation (self) ->RBTreeNodeReservation <K ,V > {RBTreeNodeReservation { - node:Box ::drop_contents (self.node), + node:KBox ::drop_contents (self.node), } } }
@@ -1174,7 +1175,7 @@ impl<'a, K, V> RawVacantEntry<'a, K, V> {/// The `node` must have a key such that inserting it here does not break the ordering of this /// [`RBTree`]. fn insert (self, node:RBTreeNode <K ,V >) -> &'amut V { -let node =Box ::into_raw (node.node); +let node =KBox ::into_raw (node.node);// SAFETY: `node` is valid at least until we call `Box::from_raw`, which only happens when // the node is removed or replaced.
@@ -1247,20 +1248,23 @@ pub fn remove_node(self) -> RBTreeNode<K, V> {RBTreeNode {// SAFETY: The node was a node in the tree, but we removed it, so we can convert it // back into a box. - node:unsafe {Box ::from_raw (self.node_ptr ()) }, + node:unsafe {KBox ::from_raw (self.node_ptr ()) }, } }/// Takes the value of the entry out of the map, and returns it. pub fn remove (self) ->V { - self.remove_node ().node.value +let rb_node = self.remove_node (); +let node =KBox ::into_inner (rb_node.node); + + node.value }/// Swap the current node for the provided node. /// /// The key of both nodes must be equal. fn replace (self, node:RBTreeNode <K ,V >) ->RBTreeNode <K ,V > { -let node =Box ::into_raw (node.node); +let node =KBox ::into_raw (node.node);// SAFETY: `node` is valid at least until we call `Box::from_raw`, which only happens when // the node is removed or replaced.
@@ -1275,7 +1279,7 @@ fn replace(self, node: RBTreeNode<K, V>) -> RBTreeNode<K, V> {// SAFETY: // - `self.node_ptr` produces a valid pointer to a node in the tree. // - Now that we removed this entry from the tree, we can convert the node to a box. -let old_node =unsafe {Box ::from_raw (self.node_ptr ()) }; +let old_node =unsafe {KBox ::from_raw (self.node_ptr ()) };RBTreeNode { node: old_node } }
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`.
@@ -431,8 +430,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 ())) }; } } }
@@ -677,7 +676,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) }),
@@ -687,8 +686,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.46.0
Re: [PATCH v6 10/26] rust: treewide: switch to our kernel `Box` type
Quoted 5 lines · Danilo Krummrich
Now that we got the kernel
Boxtype in place, convert all existingBoxusers to make use of it.Reviewed-by Alice Ryhl <[email protected]>Signed-off-by Danilo Krummrich <[email protected]>
---
Cheers,
Benno
Quoted 12 lines
--- drivers/block/rnull.rs | 4 +-- rust/kernel/init.rs | 51 ++++++++++++++++--------------- rust/kernel/init/__internal.rs | 2 +- rust/kernel/rbtree.rs | 34 ++++++++++++--------- 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 ++-- 10 files changed, 73 insertions(+), 69 deletions(-)
Danilo Krummrich
maintainerPATCH v6 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 | 80 ------------------------------------ rust/kernel/init.rs | 44 +------------------- rust/kernel/lib.rs | 1 - rust/kernel/prelude.rs | 4 +- rust/kernel/types.rs | 28 ------------- 6 files changed, 3 insertions(+), 155 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 -80
80 lines not on this page · raw message
rust/kernel/init.rs +1 -43
72 lines not on this page · raw message
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 -28
@@ -3,7 +3,6 @@//! Kernel types. use crate ::init::{self,PinInit }; -use alloc::boxed::Box ;use core::{ cell::UnsafeCell , marker::{PhantomData ,PhantomPinned },
@@ -105,33 +104,6 @@ unsafe fn try_from_foreign(ptr: *const core::ffi::c_void) -> Option<Self> {unsafe fn borrow_mut <'a>(ptr: *const core::ffi::c_void ) ->Self ::BorrowedMut <'a>; } -impl <T : 'static>ForeignOwnable for Box <T > { -type Borrowed <'a> = &'aT ; -type BorrowedMut <'a> = &'amut T ; - -fn into_foreign (self) -> *const core::ffi::c_void { -Box ::into_raw (self)as _ - } - -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 _ ) } - } - -unsafe fn borrow <'a>(ptr: *const core::ffi::c_void ) -> &'aT { -// SAFETY: The safety requirements of this method ensure that the object remains alive and -// immutable for the duration of 'a. -unsafe { &*ptr.cast () } - } - -unsafe fn borrow_mut <'a>(ptr: *const core::ffi::c_void ) -> &'amut T { -// SAFETY: The safety requirements of this method ensure that the pointer is valid and that -// nothing else will access the value for the duration of 'a. -unsafe { &mut *ptr.cast_mut ().cast () } - } -} -impl ForeignOwnable for () {type Borrowed <'a> = ();type BorrowedMut <'a> = ();
Signature · 1 line
2.46.0
Re: [PATCH v6 11/26] rust: alloc: remove `BoxExt` extension
Quoted 5 lines · Danilo Krummrich
Now that all existing
Boxusers were moved to the kernelBoxtype, remove theBoxExtextension and all other related extensions.Reviewed-by Alice Ryhl <[email protected]>Signed-off-by Danilo Krummrich <[email protected]>
This is just a minor nit, but could you please change the commit title to "rust: alloc: remove extension of std's Box"?
With that:
---
Cheers,
Benno
Quoted 9 lines
--- rust/kernel/alloc.rs | 1 - rust/kernel/alloc/box_ext.rs | 80 ------------------------------------ rust/kernel/init.rs | 44 +------------------- rust/kernel/lib.rs | 1 - rust/kernel/prelude.rs | 4 +- rust/kernel/types.rs | 28 ------------- 6 files changed, 3 insertions(+), 155 deletions(-) delete mode 100644 rust/kernel/alloc/box_ext.rs
Danilo Krummrich
maintainerPATCH v6 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.46.0
Danilo Krummrich
maintainerPATCH v6 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/kvec.rs | 629 ++++++++++++++++++++++++++++++++++++++ rust/kernel/prelude.rs | 2 +- 3 files changed, 636 insertions(+), 1 deletion(-) 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/kvec.rs new file +629 -0
629 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.46.0
Re: [PATCH v6 13/26] rust: alloc: implement kernel `Vec` type
Quoted 11 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]>---
[...]
Quoted 5 lines
rust/kernel/alloc/kvec.rs new file +5
-0+ +impl <T ,A >Vec <T ,A > +where +A :Allocator , +{
[...]
rust/kernel/alloc/kvec.rs line 195
Quoted 7 lines
rust/kernel/alloc/kvec.rs new file +7
-0+/// Forcefully sets `self.len` to `new_len`. +/// +/// # Safety +/// +/// - `new_len` must be less than or equal to [`Self::capacity`]. +/// - If `new_len` is greater than `self.len`, all elements within the interval +/// [`self.len`,`new_len`] must be initialized.
Maybe use "[self.len, new_len)" to indicate new_len side is open? Also self.len may confuse people whether it's the old length or new length, could you use old_len and add note saying "old_len is the length before set_len()?
rust/kernel/alloc/kvec.rs line 203
Quoted 8 lines
rust/kernel/alloc/kvec.rs new file +8
-0+#[inline] +pub unsafe fn set_len (&mut self, new_len:usize ) { + self.len = new_len; + } + +/// Returns a slice of the entire vector. +/// +/// Equivalent to `&s[..]`.
"s" is used here without pre-definition, it's a bit confusing. Either we use an example here, or we can just avoid mentioning it at all?
rust/kernel/alloc/kvec.rs line 211
Quoted 8 lines
rust/kernel/alloc/kvec.rs new file +8
-0+#[inline] +pub fn as_slice (&self) -> &[T ] { + self + } + +/// Returns a mutable slice of the entire vector. +/// +/// Equivalent to `&mut s[..]`.
Ditto.
rust/kernel/alloc/kvec.rs line 220
Quoted 9 lines
rust/kernel/alloc/kvec.rs new file +9
-0+#[inline] +pub fn as_mut_slice (&mut self) -> &mut [T ] { + self + } + +/// Returns a mutable raw pointer to the vector's backing buffer, or, if `T` is a ZST, a +/// dangling raw pointer. +#[inline] +pub fn as_mut_ptr (&self) -> *mut T {
I think this function needs to take a &mut self to mirror the as_mut_ptr of a slice.
Regards,
Boqun
Quoted 10 lines
rust/kernel/alloc/kvec.rs new file +10
-0+ self.ptr.as_ptr () + } + +/// Returns a raw pointer to the vector's backing buffer, or, if `T` is a ZST, a dangling raw +/// pointer. +#[inline] +pub fn as_ptr (&self) -> *const T { + self.as_mut_ptr () + } +
[...]
Danilo Krummrich
maintainerRe: [PATCH v6 13/26] rust: alloc: implement kernel `Vec` type
Quoted 27 lines · Boqun Feng
Quoted 11 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]>---
[...]
Quoted 5 lines
rust/kernel/alloc/kvec.rs new file +5
-0+ +impl <T ,A >Vec <T ,A > +where +A :Allocator , +{[...]
rust/kernel/alloc/kvec.rs line 195
Quoted 7 lines
rust/kernel/alloc/kvec.rs new file +7
-0+/// Forcefully sets `self.len` to `new_len`. +/// +/// # Safety +/// +/// - `new_len` must be less than or equal to [`Self::capacity`]. +/// - If `new_len` is greater than `self.len`, all elements within the interval +/// [`self.len`,`new_len`] must be initialized.
Quoted 1 line
Maybe use "[
self.len,new_len)" to indicatenew_lenside is open?
Agreed.
Quoted 3 lines
Also
self.lenmay confuse people whether it's the old length or new length, could you useold_lenand add note saying "old_lenis the length beforeset_len()?
What about:
/// - If new_len is greater than self.len (from before calling this function), all elements
/// within the interval [self.len,new_len] must be initialized.
Re: [PATCH v6 13/26] rust: alloc: implement kernel `Vec` type
Quoted 28 lines · Danilo Krummrich
Quoted 27 lines · Boqun Feng
Quoted 11 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]>---
[...]
Quoted 5 lines
rust/kernel/alloc/kvec.rs new file +5
-0+ +impl <T ,A >Vec <T ,A > +where +A :Allocator , +{[...]
Quoted 7 lines
rust/kernel/alloc/kvec.rs new file +7
-0+/// Forcefully sets `self.len` to `new_len`. +/// +/// # Safety +/// +/// - `new_len` must be less than or equal to [`Self::capacity`]. +/// - If `new_len` is greater than `self.len`, all elements within the interval +/// [`self.len`,`new_len`] must be initialized.
Quoted 3 lines
Quoted 1 line
Maybe use "[
self.len,new_len)" to indicatenew_lenside is open?Agreed.
Alternatively just use the rust range operator self.len..new_len, it is exclusive by default (for an inclusive range you can use ..=).
Quoted 3 lines
Quoted 3 lines
Also
self.lenmay confuse people whether it's the old length or new length, could you useold_lenand add note saying "old_lenis the length beforeset_len()?
I personally think this is not a big issue, since safety requirements are preconditions to calling a function (so the function couldn't have been called yet).
Quoted 4 lines
What about:
/// - If `new_len` is greater than `self.len` (from before calling this function), all elements /// within the interval [`self.len`,`new_len`] must be initialized.
You will still apply the range fix, right?
---
Cheers,
Benno
Re: [PATCH v6 13/26] rust: alloc: implement kernel `Vec` type
In reply to Danilo KrummrichQuoted 1 line · Danilo Krummrich
Vecprovides a contiguous growable array type (such asVec) with
"Vec [...] such as Vec"? What do you want to say? I think without the parenthesis it works better.
Quoted 4 lines
contents allocated with the kernel's allocators (e.g.
Kmalloc,VmallocorKVmalloc).In contrast to Rust's
Vectype, the kernelVectype considers the
I would add "stdlib" after "Rust's".
Quoted 71 lines
kernel's GFP flags for all appropriate functions, always reports allocation failures through
Result<_, AllocError>and remains independent from unstable features.Signed-off-by Danilo Krummrich <[email protected]>--- rust/kernel/alloc.rs | 6 + rust/kernel/alloc/kvec.rs | 629 ++++++++++++++++++++++++++++++++++++++ rust/kernel/prelude.rs | 2 +- 3 files changed, 636 insertions(+), 1 deletion(-) create mode 100644 rust/kernel/alloc/kvec.rsrust/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/kvec.rs new file +29
-0@@ -0,0 +1,629 @@ +// SPDX-License-Identifier: GPL-2.0 + +//! Implementation of [`Vec`]. + +use super ::{AllocError ,Allocator ,Box ,Flags }; +use core::{ + fmt, + marker::PhantomData , + mem::{ManuallyDrop ,MaybeUninit }, + ops::Deref , + ops::DerefMut , + ops::Index , + ops::IndexMut , + ptr::NonNull , + slice, + slice::SliceIndex , +}; + +/// Create a [`Vec`] containing the arguments. +/// +/// # Examples +/// +/// ``` +/// let mut v = kernel::kvec![]; +/// v.push(1, GFP_KERNEL)?; +/// assert_eq!(v, [1]); +/// +/// let mut v = kernel::kvec![1; 3]?; +/// v.push(4, GFP_KERNEL)?;
rust/kernel/alloc/kvec.rs line 39
Quoted 10 lines
rust/kernel/alloc/kvec.rs new file +10
-0+/// assert_eq!(v, [1, 1, 1, 4]); +/// +/// let mut v = kernel::kvec![1, 2, 3]?; +/// v.push(4, GFP_KERNEL)?; +/// assert_eq!(v, [1, 2, 3, 4]); +/// +/// # Ok::<(), Error>(()) +/// ``` +#[macro_export] +macro_rules! kvec {
Do we only want to support creating KVec via a macro?
rust/kernel/alloc/kvec.rs line 44
Quoted 5 lines
rust/kernel/alloc/kvec.rs new file +5
-0+ () => ( + { + $crate::alloc::KVec ::new () + } + );
Why do you have parenthesis and braces? IIRC just braces/parenthesis or brackets should be enough.
rust/kernel/alloc/kvec.rs line 47
Quoted 3 lines
rust/kernel/alloc/kvec.rs new file +3
-0+ ($elem:expr; $n:expr) => ( + { + $crate::alloc::KVec ::from_elem ($elem, $n,GFP_KERNEL )
Hmm, could it be that one might want to use kvec! without GFP_KERNEL?
Or add additional flags, eg __GFP_ZERO?
If you think that supporting this is not immediately necessary or if this is too much for this patchset, then we can also postpone it (maybe it could be a good-first-issue). Do you keep a list of future improvements for the new allocator API somewhere? If not, then I think we should create one (best place would be the issue tracker on GH).
Quoted 6 lines
rust/kernel/alloc/kvec.rs new file +6
-0+ } + ); + ($($x:expr),+ $(,)?) => ( + { +match $crate::alloc::KBox ::new_uninit (GFP_KERNEL ) { + Ok(b) => Ok($crate::alloc::KVec::from($crate::alloc::KBox::write(b, [$($x),+]))),
rust/kernel/alloc/kvec.rs line 63
Quoted 10 lines
rust/kernel/alloc/kvec.rs new file +10
-0+ Err(e) => Err(e), + } + } + ); +} + +/// The kernel's [`Vec`] type. +/// +/// A contiguous growable array type with contents allocated with the kernel's allocators (e.g. +/// `Kmalloc`, `Vmalloc` or `KVmalloc`), written `Vec<T, A>`.
New folks might get confused as to which allocator they should choose. Do we have a sensible default for Vec?
If yes, then we at least should document that here. We might also want to make it the default value for the generic parameter. This is also a good idea for Box.
Quoted 11 lines
rust/kernel/alloc/kvec.rs new file +11
-0+/// +/// For non-zero-sized values, a [`Vec`] will use the given allocator `A` for its allocation. For +/// the most common allocators the type aliases `KVec`, `VVec` and `KVVec` exist. +/// +/// For zero-sized types the [`Vec`]'s pointer must be `dangling_mut::<T>`; no memory is allocated. +/// +/// Generally, [`Vec`] consists of a pointer that represents the vector's backing buffer, the +/// capacity of the vector (the number of elements that currently fit into the vector), it's length +/// (the number of elements that are currently stored in the vector) and the `Allocator` type used +/// to allocate (and free) the backing buffer. +///
rust/kernel/alloc/kvec.rs line 84
Quoted 10 lines
rust/kernel/alloc/kvec.rs new file +10
-0+/// A [`Vec`] can be deconstructed into and (re-)constructed from it's previously named raw parts +/// and manually modified. +/// +/// [`Vec`]'s backing buffer gets, if required, automatically increased (re-allocated) when elements +/// are added to the vector. +/// +/// # Invariants +/// +/// The [`Vec`] backing buffer's pointer is always properly aligned and either points to memory +/// allocated with `A` or, for zero-sized types, is a dangling pointer.
Just use self.ptr instead of "backing buffer".
rust/kernel/alloc/kvec.rs line 86
Quoted 2 lines
rust/kernel/alloc/kvec.rs new file +2
-0+/// +/// The length of the vector always represents the exact number of elements stored in the vector.
Same here, self.len.
rust/kernel/alloc/kvec.rs line 88
Quoted 2 lines
rust/kernel/alloc/kvec.rs new file +2
-0+/// +/// The capacity of the vector always represents the absolute number of elements that can be stored
Ditto.
rust/kernel/alloc/kvec.rs line 93
Quoted 5 lines
rust/kernel/alloc/kvec.rs new file +5
-0+/// within the vector without re-allocation. However, it is legal for the backing buffer to be +/// larger than `size_of<T>` times the capacity. +/// +/// The `Allocator` type `A` of the vector is the exact same `Allocator` type the backing buffer was +/// allocated with (and must be freed with).
Please turn this into a bullet-point list.
Quoted 6 lines
rust/kernel/alloc/kvec.rs new file +6
-0+pub struct Vec <T ,A :Allocator > { + ptr:NonNull <T >, +/// Represents the actual buffer size as `cap` times `size_of::<T>` bytes. +/// +/// Note: This isn't quite the same as `Self::capacity`, which in contrast returns the number of +/// elements we can still store without reallocating.
rust/kernel/alloc/kvec.rs line 109
Quoted 10 lines
rust/kernel/alloc/kvec.rs new file +10
-0+/// +/// # Invariants +/// +/// `cap` must be in the `0..=isize::MAX` range. + cap: usize, + len: usize, + _p: PhantomData<A>, +} + +/// Type alias for `Vec` with a `Kmalloc` allocator.
Can you turn these into links?
Quoted 78 lines
rust/kernel/alloc/kvec.rs new file +78
-0+/// +/// # Examples +/// +/// ``` +/// let mut v = KVec::new(); +/// v.push(1, GFP_KERNEL)?; +/// assert_eq!(&v, &[1]); +/// +/// # Ok::<(), Error>(()) +/// ``` +pub type KVec <T > =Vec <T ,super ::allocator::Kmalloc >; + +/// Type alias for `Vec` with a `Vmalloc` allocator. +/// +/// # Examples +/// +/// ``` +/// let mut v = VVec::new(); +/// v.push(1, GFP_KERNEL)?; +/// assert_eq!(&v, &[1]); +/// +/// # Ok::<(), Error>(()) +/// ``` +pub type VVec <T > =Vec <T ,super ::allocator::Vmalloc >; + +/// Type alias for `Vec` with a `KVmalloc` allocator. +/// +/// # Examples +/// +/// ``` +/// let mut v = KVVec::new(); +/// v.push(1, GFP_KERNEL)?; +/// assert_eq!(&v, &[1]); +/// +/// # Ok::<(), Error>(()) +/// ``` +pub type KVVec <T > =Vec <T ,super ::allocator::KVmalloc >; + +// SAFETY: `Vec` is `Send` if `T` is `Send` because `Vec` owns its elements. +unsafe impl <T ,A >Send for Vec <T ,A > +where +T :Send , +A :Allocator , +{ +} + +// SAFETY: `Vec` is `Sync` if `T` is `Sync` because `Vec` owns its elements. +unsafe impl <T ,A >Sync for Vec <T ,A > +where +T :Sync , +A :Allocator , +{ +} + +impl <T ,A >Vec <T ,A > +where +A :Allocator , +{ +#[inline] +fn is_zst () ->bool { + core::mem::size_of ::<T >() ==0 + } + +/// Returns the number of elements that can be stored within the vector without allocating +/// additional memory. +pub fn capacity (&self) ->usize { +if Self ::is_zst () { + usize::MAX + }else { + self.cap + } + } + +/// Returns the number of elements stored within the vector. +#[inline] +pub fn len (&self) ->usize { + self.len + }
rust/kernel/alloc/kvec.rs line 197
Quoted 10 lines
rust/kernel/alloc/kvec.rs new file +10
-0+ +/// Forcefully sets `self.len` to `new_len`. +/// +/// # Safety +/// +/// - `new_len` must be less than or equal to [`Self::capacity`]. +/// - If `new_len` is greater than `self.len`, all elements within the interval +/// [`self.len`,`new_len`] must be initialized. +#[inline] +pub unsafe fn set_len (&mut self, new_len:usize ) {
Can you add a debug_assert!(new_len < self.cap)
Quoted 53 lines
rust/kernel/alloc/kvec.rs new file +53
-0+ self.len = new_len; + } + +/// Returns a slice of the entire vector. +/// +/// Equivalent to `&s[..]`. +#[inline] +pub fn as_slice (&self) -> &[T ] { + self + } + +/// Returns a mutable slice of the entire vector. +/// +/// Equivalent to `&mut s[..]`. +#[inline] +pub fn as_mut_slice (&mut self) -> &mut [T ] { + self + } + +/// Returns a mutable raw pointer to the vector's backing buffer, or, if `T` is a ZST, a +/// dangling raw pointer. +#[inline] +pub fn as_mut_ptr (&self) -> *mut T { + self.ptr.as_ptr () + } + +/// Returns a raw pointer to the vector's backing buffer, or, if `T` is a ZST, a dangling raw +/// pointer. +#[inline] +pub fn as_ptr (&self) -> *const T { + self.as_mut_ptr () + } + +/// Returns `true` if the vector contains no elements, `false` otherwise. +/// +/// # Examples +/// +/// ``` +/// let mut v = KVec::new(); +/// assert!(v.is_empty()); +/// +/// v.push(1, GFP_KERNEL); +/// assert!(!v.is_empty()); +/// ``` +#[inline] +pub fn is_empty (&self) ->bool { + self.len () ==0 + } + +/// Creates a new, empty Vec<T, A>. +/// +/// This method does not allocate by itself. +#[inline]
rust/kernel/alloc/kvec.rs line 260
Quoted 10 lines
rust/kernel/alloc/kvec.rs new file +10
-0+pub const fn new () ->Self { +Self { + ptr:NonNull ::dangling (), + cap:0 , + len:0 , + _p:PhantomData ::<A >, + } + } + +/// Returns a slice of `MaybeUninit<T>` for the remaining spare capacity of the vector.
Returns
rust/kernel/alloc/kvec.rs line 269
Quoted 9 lines
rust/kernel/alloc/kvec.rs new file +9
-0+pub fn spare_capacity_mut (&mut self) -> &mut [MaybeUninit <T >] { +// SAFETY: The memory between `self.len` and `self.capacity` is guaranteed to be allocated +// and valid, but uninitialized. +unsafe { + slice::from_raw_parts_mut ( + self.as_mut_ptr ().add (self.len)as *mut MaybeUninit <T >, + self.capacity () - self.len, + ) + }
There are two unsafe operations in this block, please split it into two blocks.
Quoted 9 lines
rust/kernel/alloc/kvec.rs new file +9
-0+ } + +/// Appends an element to the back of the [`Vec`] instance. +/// +/// # Examples +/// +/// ``` +/// let mut v = KVec::new(); +/// v.push(1, GFP_KERNEL)?;
rust/kernel/alloc/kvec.rs line 288
Quoted 10 lines
rust/kernel/alloc/kvec.rs new file +10
-0+/// assert_eq!(&v, &[1]); +/// +/// v.push(2, GFP_KERNEL)?; +/// assert_eq!(&v, &[1, 2]); +/// # Ok::<(), Error>(()) +/// ``` +pub fn push (&mut self, v:T , flags:Flags ) ->Result <(),AllocError > { +Vec ::reserve (self,1 , flags)?; +let s = self.spare_capacity_mut (); + s[0 ].write (v);
The slice access will check the size, I don't know if it will be optimized out, if not, I would use a raw pointer here instead.
Quoted 43 lines
rust/kernel/alloc/kvec.rs new file +43
-0+ +// SAFETY: We just initialised the first spare entry, so it is safe to increase the length +// by 1. We also know that the new length is <= capacity because of the previous call to +// `reserve` above. +unsafe { self.set_len (self.len () +1 ) }; +Ok (()) + } + +/// Creates a new [`Vec`] instance with at least the given capacity. +/// +/// # Examples +/// +/// ``` +/// let v = KVec::<u32>::with_capacity(20, GFP_KERNEL)?; +/// +/// assert!(v.capacity() >= 20); +/// # Ok::<(), Error>(()) +/// ``` +pub fn with_capacity (capacity:usize , flags:Flags ) ->Result <Self ,AllocError > { +let mut v =Vec ::new (); + +Self ::reserve (&mut v, capacity, flags)?; + +Ok (v) + } + +/// Pushes clones of the elements of slice into the [`Vec`] instance. +/// +/// # Examples +/// +/// ``` +/// let mut v = KVec::new(); +/// v.push(1, GFP_KERNEL)?; +/// +/// v.extend_from_slice(&[20, 30, 40], GFP_KERNEL)?; +/// assert_eq!(&v, &[1, 20, 30, 40]); +/// +/// v.extend_from_slice(&[50, 60], GFP_KERNEL)?; +/// assert_eq!(&v, &[1, 20, 30, 40, 50, 60]); +/// # Ok::<(), Error>(()) +/// ``` +pub fn extend_from_slice (&mut self, other: &[T ], flags:Flags ) ->Result <(),AllocError > +where
rust/kernel/alloc/kvec.rs line 341
Quoted 10 lines
rust/kernel/alloc/kvec.rs new file +10
-0+ T: Clone, + { + self.reserve(other.len(), flags)?; +for (slot, item)in core::iter::zip (self.spare_capacity_mut (), other) { + slot.write (item.clone ()); + } + +// SAFETY: We just initialised the `other.len()` spare entries, so it is safe to increase +// the length by the same amount. We also know that the new length is <= capacity because +// of the previous call to `reserve` above.
Please use a bullet point list and avoid natural language filler words.
rust/kernel/alloc/kvec.rs line 353
Quoted 12 lines
rust/kernel/alloc/kvec.rs new file +12
-0+unsafe { self.set_len (self.len () + other.len ()) }; +Ok (()) + } + +/// Creates a Vec<T, A> from a pointer, a length and a capacity using the allocator `A`. +/// +/// # Safety +/// +/// If `T` is a ZST: +/// +/// - `ptr` must be a dangling pointer. +/// - `capacity` must be zero.
When T is a ZST, into_raw_parts returns usize::MAX for capacity, so one can't just pipe its output to this function? That seems strange.
Also below you discard the value of capacity, when T is a ZST, so I think you can just remove this line.
rust/kernel/alloc/kvec.rs line 354
Quoted 1 line
rust/kernel/alloc/kvec.rs new file +1
-0+/// - `length` must be smaller than or equal to `usize::MAX`.
This is always true.
rust/kernel/alloc/kvec.rs line 361
Quoted 7 lines
rust/kernel/alloc/kvec.rs new file +7
-0+/// +/// Otherwise: +/// +/// - `ptr` must have been allocated with the allocator `A`. +/// - `ptr` must satisfy or exceed the alignment requirements of `T`. +/// - `ptr` must point to memory with a size of at least `size_of::<T>` times the `capacity` +/// bytes.
Just write "size_of::<T>() * capacity bytes".
rust/kernel/alloc/kvec.rs line 362
Quoted 1 line
rust/kernel/alloc/kvec.rs new file +1
-0+/// - The allocated size in bytes must not be larger than `isize::MAX`.
Should we make this a safety requirement of Allocator? I think this is generally the maximum size other allocated things can have anyways.
rust/kernel/alloc/kvec.rs line 367
Quoted 5 lines
rust/kernel/alloc/kvec.rs new file +5
-0+/// - `length` must be less than or equal to `capacity`. +/// - The first `length` elements must be initialized values of type `T`. +/// +/// It is also valid to create an empty `Vec` passing a dangling pointer for `ptr` and zero for +/// `cap` and `len`.
Can you make this last sentence part of the if chain that you have above (ie the one started with "If T is a ZST:").
Just to experiment with the suggestion from Kangrejos to use Rust as the language for safety documentation, here is what it could look like (we should discuss it more before we start using it):
/// # Safety
///
/// ```ignore
/// assert!(ptr.is_aligned_to(align_of::<T>()));
/// assert!(!ptr.is_null());
/// assert!(length <= capacity);
/// if capacity != 0 && size_of::<T>() != 0 {
/// assert!(A::did_allocate(ptr));
/// assert!(size_of::<T>() * capacity <= A::layout_of(ptr).len());
/// assert!(is_initialized(ptr::slice_from_raw_parts(ptr, length)));
/// }
/// ```I really like how this looks! We might want to add labels/names to each of the conditions and then one could use those in the justifications. A tool could then read those and match them to the requirements of the unsafe operation.
Quoted 12 lines
rust/kernel/alloc/kvec.rs new file +12
-0+/// +/// # Examples +/// +/// ``` +/// let mut v = kernel::kvec![1, 2, 3]?; +/// v.reserve(1, GFP_KERNEL)?; +/// +/// let (mut ptr, mut len, cap) = v.into_raw_parts(); +/// +/// // SAFETY: We've just reserved memory for another element. +/// unsafe { ptr.add(len).write(4) }; +/// len += 1;
rust/kernel/alloc/kvec.rs line 389
Quoted 10 lines
rust/kernel/alloc/kvec.rs new file +10
-0+/// +/// // SAFETY: We only wrote an additional element at the end of the `KVec`'s buffer and +/// // correspondingly increased the length of the `KVec` by one. Otherwise, we construct it +/// // from the exact same raw parts. +/// let v = unsafe { KVec::from_raw_parts(ptr, len, cap) }; +/// +/// assert_eq!(v, [1, 2, 3, 4]); +/// +/// # Ok::<(), Error>(()) +/// ```
Please reorder the sections to be # Examples followed by # Safety.
Quoted 45 lines
rust/kernel/alloc/kvec.rs new file +45
-0+pub unsafe fn from_raw_parts (ptr: *mut T , length:usize , capacity:usize ) ->Self { +let cap =if Self ::is_zst () {0 }else { capacity }; + +Self { +// SAFETY: By the safety requirements, `ptr` is either dangling or pointing to a valid +// memory allocation, allocated with `A`. + ptr:unsafe {NonNull ::new_unchecked (ptr) }, + cap, + len: length, + _p:PhantomData ::<A >, + } + } + +/// Consumes the `Vec<T, A>` and returns its raw components `pointer`, `length` and `capacity`. +/// +/// This will not run the destructor of the contained elements and for non-ZSTs the allocation +/// will stay alive indefinitely. Use [`Vec::from_raw_parts`] to recover the [`Vec`], drop the +/// elements and free the allocation, if any. +pub fn into_raw_parts (self) -> (*mut T ,usize ,usize ) { +let me =ManuallyDrop ::new (self); +let len = me.len (); +let capacity = me.capacity (); +let ptr = me.as_mut_ptr (); + (ptr, len, capacity) + } + +/// Ensures that the capacity exceeds the length by at least `additional` +/// elements. +/// +/// # Examples +/// +/// ``` +/// let mut v = KVec::new(); +/// v.push(1, GFP_KERNEL)?; +/// +/// v.reserve(10, GFP_KERNEL)?; +/// let cap = v.capacity(); +/// assert!(cap >= 10); +/// +/// v.reserve(10, GFP_KERNEL)?; +/// let new_cap = v.capacity(); +/// assert_eq!(new_cap, cap); +/// +/// # Ok::<(), Error>(()) +/// ```
rust/kernel/alloc/kvec.rs line 444
Quoted 10 lines
rust/kernel/alloc/kvec.rs new file +10
-0+pub fn reserve (&mut self, additional:usize , flags:Flags ) ->Result <(),AllocError > { +let len = self.len (); +let cap = self.capacity (); + +if cap - len >= additional { +return Ok (()); + } + +if Self ::is_zst () { +// The capacity is already `usize::MAX` for SZTs, we can't go higher.
Typo: SZTs -> ZSTs
rust/kernel/alloc/kvec.rs line 448
Quoted 4 lines
rust/kernel/alloc/kvec.rs new file +4
-0+return Err (AllocError ); + } + +// We know `cap` is <= `isize::MAX` because of it's type invariant. So the multiplication by
"it's type invariant" -> "type invariants of Self"
Quoted 19 lines
rust/kernel/alloc/kvec.rs new file +19
-0+// two won't overflow. +let new_cap = core::cmp::max (cap *2 , len.checked_add (additional).ok_or (AllocError )?); +let layout = core::alloc::Layout ::array ::<T >(new_cap).map_err (|_|AllocError )?; + +// We need to make sure that `ptr` is either NULL or comes from a previous call to +// `realloc_flags`. A `Vec<T, A>`'s `ptr` value is not guaranteed to be NULL and might be +// dangling after being created with `Vec::new`. Instead, we can rely on `Vec<T, A>`'s +// capacity to be zero if no memory has been allocated yet. +let ptr =if cap ==0 { +None + }else { +Some (self.ptr.cast ()) + }; + +// SAFETY: `ptr` is valid because it's either `None` or comes from a previous call to +// `A::realloc`. We also verified that the type is not a ZST. +let ptr =unsafe {A ::realloc (ptr, layout, flags)? }; + + self.ptr = ptr.cast ();
rust/kernel/alloc/kvec.rs line 477
Quoted 10 lines
rust/kernel/alloc/kvec.rs new file +10
-0+ +// INVARIANT: `Layout::array` fails if the resulting byte size is greater than `isize::MAX`. + self.cap = new_cap; + +Ok (()) + } +} + +impl <T :Clone ,A :Allocator >Vec <T ,A > { +/// Extend the vector by `n` clones of value.
value -> value
Quoted 8 lines
rust/kernel/alloc/kvec.rs new file +8
-0+pub fn extend_with (&mut self, n:usize , value:T , flags:Flags ) ->Result <(),AllocError > { +if n ==0 { +return Ok (()); + } + + self.reserve (n, flags)?; + +let spare = self.spare_capacity_mut ();
rust/kernel/alloc/kvec.rs line 495
Quoted 10 lines
rust/kernel/alloc/kvec.rs new file +10
-0+ +for itemin spare.iter_mut ().take (n -1 ) { + item.write (value.clone ()); + } + +// We can write the last element directly without cloning needlessly. + spare[n -1 ].write (value); + +// SAFETY: `self.reserve` not bailing out with an error guarantees that we're not +// exceeding the capacity of this `Vec`.
This safety comment needs to be improved, how about using bullet points:
- self.len() + n < capacity due to the call to reserve above,
- the loop and the line above initialized the next n elements.
Quoted 14 lines
rust/kernel/alloc/kvec.rs new file +14
-0+unsafe { self.set_len (self.len () + n) }; + +Ok (()) + } + +/// Create a new `Vec<T, A> and extend it by `n` clones of `value`. +pub fn from_elem (value:T , n:usize , flags:Flags ) ->Result <Self ,AllocError > { +let mut v =Self ::with_capacity (n, flags)?; + + v.extend_with (n, value, flags)?; + +Ok (v) + } +}
rust/kernel/alloc/kvec.rs line 519
Quoted 10 lines
rust/kernel/alloc/kvec.rs new file +10
-0+ +impl<T, A> Drop for Vec<T, A> +where + A: Allocator, +{ + fn drop(&mut self) { + // SAFETY: We need to drop the vector's elements in place, before we free the backing + // memory. + unsafe { + core::ptr::drop_in_place(core::ptr::slice_from_raw_parts_mut(
You are using core::ptr a bunch, consider using it, then you don't need to write the prefix core::.
Quoted 12 lines
rust/kernel/alloc/kvec.rs new file +12
-0+ self.as_mut_ptr(), + self.len, + )) + }; + +// If `cap == 0` we never allocated any memory in the first place. +if self.cap !=0 { +// SAFETY: `self.ptr` was previously allocated with `A`. +unsafe {A ::free (self.ptr.cast ()) }; + } + } +}
rust/kernel/alloc/kvec.rs line 541
Quoted 10 lines
rust/kernel/alloc/kvec.rs new file +10
-0+ +impl <T ,A ,const N :usize >From <Box <[T ;N ],A >>for Vec <T ,A > +where +A :Allocator , +{ +fn from (b:Box <[T ;N ],A >) ->Vec <T ,A > { +let len = b.len (); +let ptr =Box ::into_raw (b); + +unsafe {Vec ::from_raw_parts (ptras _ , len, len) }
Missing SAFETY comment.
Quoted 57 lines
rust/kernel/alloc/kvec.rs new file +57
-0+ } +} + +impl <T >Default for KVec <T > { +#[inline] +fn default () ->Self { +Self ::new () + } +} + +impl <T : fmt::Debug ,A :Allocator > fmt::Debug for Vec <T ,A > { +fn fmt (&self, f: &mut fmt::Formatter <'_>) -> fmt::Result { + fmt::Debug ::fmt (&**self, f) + } +} + +impl <T ,A >Deref for Vec <T ,A > +where +A :Allocator , +{ +type Target = [T ]; + +#[inline] +fn deref (&self) -> &[T ] { +// SAFETY: The memory behind `self.as_ptr()` is guaranteed to contain `self.len` +// initialized elements of type `T`. +unsafe { slice::from_raw_parts (self.as_ptr (), self.len) } + } +} + +impl <T ,A >DerefMut for Vec <T ,A > +where +A :Allocator , +{ +#[inline] +fn deref_mut (&mut self) -> &mut [T ] { +// SAFETY: The memory behind `self.as_ptr()` is guaranteed to contain `self.len` +// initialized elements of type `T`. +unsafe { slice::from_raw_parts_mut (self.as_mut_ptr (), self.len) } + } +} + +impl <T :Eq ,A >Eq for Vec <T ,A >where A :Allocator {} + +impl <T ,I :SliceIndex <[T ]>,A >Index <I >for Vec <T ,A > +where +A :Allocator , +{ +type Output =I ::Output ; + +#[inline] +fn index (&self, index:I ) -> &Self ::Output { +Index ::index (&**self, index) + } +} + +impl <T ,I :SliceIndex <[T ]>,A >IndexMut <I >for Vec <T ,A >
rust/kernel/alloc/kvec.rs line 608
Quoted 10 lines
rust/kernel/alloc/kvec.rs new file +10
-0+where + A: Allocator, +{ + #[inline] + fn index_mut(&mut self, index: I) -> &mut Self::Output { + IndexMut::index_mut(&mut **self, index) + } +} + +macro_rules! __impl_slice_eq {
I wouldn't begin this macro with __, it is already private.
rust/kernel/alloc/kvec.rs line 609
Quoted 1 line
rust/kernel/alloc/kvec.rs new file +1
-0+ ([$($vars:tt)*] $lhs:ty, $rhs:ty $(where $ty:ty: $bound:ident)?) => {
You don't use the optional where part in the code below, if you don't use it later, then remove it (if you do use it later, I would move it into that patch).
---
Cheers,
Benno
Quoted 36 lines
rust/kernel/alloc/kvec.rs new file +20
-0+ impl<T, U, $($vars)*> PartialEq<$rhs> for $lhs +where + T: PartialEq<U>, + $($ty: $bound)? + { + #[inline] + fn eq(&self, other: &$rhs) -> bool { self[..] == other[..] } + } + } +} + +__impl_slice_eq! { [A1 :Allocator ,A2 :Allocator ]Vec <T ,A1 >,Vec <U ,A2 > } +__impl_slice_eq! { [A :Allocator ]Vec <T ,A >, &[U ] } +__impl_slice_eq! { [A :Allocator ]Vec <T ,A >, &mut [U ] } +__impl_slice_eq! { [A :Allocator ] &[T ],Vec <U ,A > } +__impl_slice_eq! { [A :Allocator ] &mut [T ],Vec <U ,A > } +__impl_slice_eq! { [A :Allocator ]Vec <T ,A >, [U ] } +__impl_slice_eq! { [A :Allocator ] [T ],Vec <U ,A > } +__impl_slice_eq! { [A :Allocator ,const N :usize ]Vec <T ,A >, [U ;N ] } +__impl_slice_eq! { [A :Allocator ,const N :usize ]Vec <T ,A >, &[U ;N ] }rust/kernel/prelude.rs +1
-2@@ -14,7 +14,7 @@#[doc(no_inline)] pub use core::pin::Pin ; -pub use crate ::alloc::{flags::*, vec_ext::VecExt ,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 ; --2.46.0
Danilo Krummrich
maintainerRe: [PATCH v6 13/26] rust: alloc: implement kernel `Vec` type
Quoted 88 lines · Benno Lossin
Quoted 1 line · Danilo Krummrich
Vecprovides a contiguous growable array type (such asVec) with"
Vec[...] such asVec"? What do you want to say? I think without the parenthesis it works better.Quoted 4 lines
contents allocated with the kernel's allocators (e.g.
Kmalloc,VmallocorKVmalloc).In contrast to Rust's
Vectype, the kernelVectype considers theI would add "stdlib" after "Rust's".
Quoted 75 lines
kernel's GFP flags for all appropriate functions, always reports allocation failures through
Result<_, AllocError>and remains independent from unstable features.Signed-off-by Danilo Krummrich <[email protected]>--- rust/kernel/alloc.rs | 6 + rust/kernel/alloc/kvec.rs | 629 ++++++++++++++++++++++++++++++++++++++ rust/kernel/prelude.rs | 2 +- 3 files changed, 636 insertions(+), 1 deletion(-) create mode 100644 rust/kernel/alloc/kvec.rsrust/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/kvec.rs new file +33
-0@@ -0,0 +1,629 @@ +// SPDX-License-Identifier: GPL-2.0 + +//! Implementation of [`Vec`]. + +use super ::{AllocError ,Allocator ,Box ,Flags }; +use core::{ + fmt, + marker::PhantomData , + mem::{ManuallyDrop ,MaybeUninit }, + ops::Deref , + ops::DerefMut , + ops::Index , + ops::IndexMut , + ptr::NonNull , + slice, + slice::SliceIndex , +}; + +/// Create a [`Vec`] containing the arguments. +/// +/// # Examples +/// +/// ``` +/// let mut v = kernel::kvec![]; +/// v.push(1, GFP_KERNEL)?; +/// assert_eq!(v, [1]); +/// +/// let mut v = kernel::kvec![1; 3]?; +/// v.push(4, GFP_KERNEL)?; +/// assert_eq!(v, [1, 1, 1, 4]); +/// +/// let mut v = kernel::kvec![1, 2, 3]?; +/// v.push(4, GFP_KERNEL)?;
Quoted 8 lines
rust/kernel/alloc/kvec.rs line 39
Quoted 6 lines
rust/kernel/alloc/kvec.rs new file +6
-0+/// assert_eq!(v, [1, 2, 3, 4]); +/// +/// # Ok::<(), Error>(()) +/// ``` +#[macro_export] +macro_rules! kvec {Do we only want to support creating
KVecvia a macro?
Yes, needing such a macro for any other allocator is rather unlikely.
Quoted 10 lines
rust/kernel/alloc/kvec.rs line 44
Quoted 6 lines
rust/kernel/alloc/kvec.rs new file +5
-0+ () => ( + { + $crate::alloc::KVec ::new () + } + );Why do you have parenthesis and braces? IIRC just braces/parenthesis or brackets should be enough.
Quoted 7 lines
rust/kernel/alloc/kvec.rs line 47
Quoted 3 lines
rust/kernel/alloc/kvec.rs new file +3
-0+ ($elem:expr; $n:expr) => ( + { + $crate::alloc::KVec ::from_elem ($elem, $n,GFP_KERNEL )Hmm, could it be that one might want to use
kvec!withoutGFP_KERNEL?
Or add additional flags, eg__GFP_ZERO?
Pretty unlikely, I'd say. __GFP_ZERO in particular wouldn't make much sense, since the macro enforces initialization anyways.
Maybe something like GFP_ATOMIC, but I expect this to be uncommon enough to not consider this for this macro for now.
Quoted 6 lines
If you think that supporting this is not immediately necessary or if this is too much for this patchset, then we can also postpone it (maybe it could be a good-first-issue). Do you keep a list of future improvements for the new allocator API somewhere? If not, then I think we should create one (best place would be the issue tracker on GH).
I would only add it if it turns out to be a common need. As mentioned, I don't expect it to be.
I'd rather keep issues in the source tree. For instance, DRM has them in '/Documentation/gpu/todo.rst'.
Quoted 12 lines
Quoted 12 lines
rust/kernel/alloc/kvec.rs new file +11
-0+ } + ); + ($($x:expr),+ $(,)?) => ( + { +match $crate::alloc::KBox ::new_uninit (GFP_KERNEL ) { + Ok(b) => Ok($crate::alloc::KVec::from($crate::alloc::KBox::write(b, [$($x),+]))), +Err (e) =>Err (e), + } + } + ); +}
Quoted 8 lines
rust/kernel/alloc/kvec.rs line 63
Quoted 5 lines
rust/kernel/alloc/kvec.rs new file +5
-0+ +/// The kernel's [`Vec`] type. +/// +/// A contiguous growable array type with contents allocated with the kernel's allocators (e.g. +/// `Kmalloc`, `Vmalloc` or `KVmalloc`), written `Vec<T, A>`. New folks might get confused as to which allocator they should choose. Do we have a sensible default for
Vec?
Then they should read the documentation about the kernel's allocators. We already link them in rust/kernel/alloc/allocator.rs.
Quoted 3 lines
If yes, then we at least should document that here. We might also want to make it the default value for the generic parameter. This is also a good idea for
Box.
If we really want a default it should be Kmalloc, but I really think we should force people to make an explicit decision and think about it and don't just go with whatever default.
It makes it also easier to grep for things. :)
Quoted 35 lines
Quoted 12 lines
rust/kernel/alloc/kvec.rs new file +11
-0+/// +/// For non-zero-sized values, a [`Vec`] will use the given allocator `A` for its allocation. For +/// the most common allocators the type aliases `KVec`, `VVec` and `KVVec` exist. +/// +/// For zero-sized types the [`Vec`]'s pointer must be `dangling_mut::<T>`; no memory is allocated. +/// +/// Generally, [`Vec`] consists of a pointer that represents the vector's backing buffer, the +/// capacity of the vector (the number of elements that currently fit into the vector), it's length +/// (the number of elements that are currently stored in the vector) and the `Allocator` type used +/// to allocate (and free) the backing buffer. +/// rust/kernel/alloc/kvec.rs line 84
Quoted 10 lines
rust/kernel/alloc/kvec.rs new file +10
-0+/// A [`Vec`] can be deconstructed into and (re-)constructed from it's previously named raw parts +/// and manually modified. +/// +/// [`Vec`]'s backing buffer gets, if required, automatically increased (re-allocated) when elements +/// are added to the vector. +/// +/// # Invariants +/// +/// The [`Vec`] backing buffer's pointer is always properly aligned and either points to memory +/// allocated with `A` or, for zero-sized types, is a dangling pointer. Just use
self.ptrinstead of "backing buffer".rust/kernel/alloc/kvec.rs line 86
Quoted 2 lines
rust/kernel/alloc/kvec.rs new file +2
-0+/// +/// The length of the vector always represents the exact number of elements stored in the vector. Same here,
self.len.rust/kernel/alloc/kvec.rs line 88
Quoted 2 lines
rust/kernel/alloc/kvec.rs new file +2
-0+/// +/// The capacity of the vector always represents the absolute number of elements that can be stored Ditto.
Quoted 7 lines
rust/kernel/alloc/kvec.rs line 93
Quoted 5 lines
rust/kernel/alloc/kvec.rs new file +5
-0+/// within the vector without re-allocation. However, it is legal for the backing buffer to be +/// larger than `size_of<T>` times the capacity. +/// +/// The `Allocator` type `A` of the vector is the exact same `Allocator` type the backing buffer was +/// allocated with (and must be freed with). Please turn this into a bullet-point list.
Is this a rule? Do we want to make it one?
Quoted 168 lines
Quoted 7 lines
rust/kernel/alloc/kvec.rs new file +6
-0+pub struct Vec <T ,A :Allocator > { + ptr:NonNull <T >, +/// Represents the actual buffer size as `cap` times `size_of::<T>` bytes. +/// +/// Note: This isn't quite the same as `Self::capacity`, which in contrast returns the number of +/// elements we can still store without reallocating. rust/kernel/alloc/kvec.rs line 109
Quoted 10 lines
rust/kernel/alloc/kvec.rs new file +10
-0+/// +/// # Invariants +/// +/// `cap` must be in the `0..=isize::MAX` range. + cap: usize, + len: usize, + _p: PhantomData<A>, +} + +/// Type alias for `Vec` with a `Kmalloc` allocator. Can you turn these into links?
Quoted 78 lines
rust/kernel/alloc/kvec.rs new file +78
-0+/// +/// # Examples +/// +/// ``` +/// let mut v = KVec::new(); +/// v.push(1, GFP_KERNEL)?; +/// assert_eq!(&v, &[1]); +/// +/// # Ok::<(), Error>(()) +/// ``` +pub type KVec <T > =Vec <T ,super ::allocator::Kmalloc >; + +/// Type alias for `Vec` with a `Vmalloc` allocator. +/// +/// # Examples +/// +/// ``` +/// let mut v = VVec::new(); +/// v.push(1, GFP_KERNEL)?; +/// assert_eq!(&v, &[1]); +/// +/// # Ok::<(), Error>(()) +/// ``` +pub type VVec <T > =Vec <T ,super ::allocator::Vmalloc >; + +/// Type alias for `Vec` with a `KVmalloc` allocator. +/// +/// # Examples +/// +/// ``` +/// let mut v = KVVec::new(); +/// v.push(1, GFP_KERNEL)?; +/// assert_eq!(&v, &[1]); +/// +/// # Ok::<(), Error>(()) +/// ``` +pub type KVVec <T > =Vec <T ,super ::allocator::KVmalloc >; + +// SAFETY: `Vec` is `Send` if `T` is `Send` because `Vec` owns its elements. +unsafe impl <T ,A >Send for Vec <T ,A > +where +T :Send , +A :Allocator , +{ +} + +// SAFETY: `Vec` is `Sync` if `T` is `Sync` because `Vec` owns its elements. +unsafe impl <T ,A >Sync for Vec <T ,A > +where +T :Sync , +A :Allocator , +{ +} + +impl <T ,A >Vec <T ,A > +where +A :Allocator , +{ +#[inline] +fn is_zst () ->bool { + core::mem::size_of ::<T >() ==0 + } + +/// Returns the number of elements that can be stored within the vector without allocating +/// additional memory. +pub fn capacity (&self) ->usize { +if Self ::is_zst () { + usize::MAX + }else { + self.cap + } + } + +/// Returns the number of elements stored within the vector. +#[inline] +pub fn len (&self) ->usize { + self.len + }rust/kernel/alloc/kvec.rs line 197
Quoted 10 lines
rust/kernel/alloc/kvec.rs new file +10
-0+ +/// Forcefully sets `self.len` to `new_len`. +/// +/// # Safety +/// +/// - `new_len` must be less than or equal to [`Self::capacity`]. +/// - If `new_len` is greater than `self.len`, all elements within the interval +/// [`self.len`,`new_len`] must be initialized. +#[inline] +pub unsafe fn set_len (&mut self, new_len:usize ) {Can you add a
debug_assert!(new_len < self.cap)Quoted 57 lines
rust/kernel/alloc/kvec.rs new file +57
-0+ self.len = new_len; + } + +/// Returns a slice of the entire vector. +/// +/// Equivalent to `&s[..]`. +#[inline] +pub fn as_slice (&self) -> &[T ] { + self + } + +/// Returns a mutable slice of the entire vector. +/// +/// Equivalent to `&mut s[..]`. +#[inline] +pub fn as_mut_slice (&mut self) -> &mut [T ] { + self + } + +/// Returns a mutable raw pointer to the vector's backing buffer, or, if `T` is a ZST, a +/// dangling raw pointer. +#[inline] +pub fn as_mut_ptr (&self) -> *mut T { + self.ptr.as_ptr () + } + +/// Returns a raw pointer to the vector's backing buffer, or, if `T` is a ZST, a dangling raw +/// pointer. +#[inline] +pub fn as_ptr (&self) -> *const T { + self.as_mut_ptr () + } + +/// Returns `true` if the vector contains no elements, `false` otherwise. +/// +/// # Examples +/// +/// ``` +/// let mut v = KVec::new(); +/// assert!(v.is_empty()); +/// +/// v.push(1, GFP_KERNEL); +/// assert!(!v.is_empty()); +/// ``` +#[inline] +pub fn is_empty (&self) ->bool { + self.len () ==0 + } + +/// Creates a new, empty Vec<T, A>. +/// +/// This method does not allocate by itself. +#[inline] +pub const fn new () ->Self { +Self { + ptr:NonNull ::dangling (), + cap:0 ,
Quoted 8 lines
rust/kernel/alloc/kvec.rs line 260
Quoted 6 lines
rust/kernel/alloc/kvec.rs new file +6
-0+ len: 0, + _p: PhantomData::<A>, + } + } + +/// Returns a slice of `MaybeUninit<T>` for the remaining spare capacity of the vector. Returns
Hm?
Quoted 134 lines
rust/kernel/alloc/kvec.rs line 269
Quoted 10 lines
rust/kernel/alloc/kvec.rs new file +9
-0+pub fn spare_capacity_mut (&mut self) -> &mut [MaybeUninit <T >] { +// SAFETY: The memory between `self.len` and `self.capacity` is guaranteed to be allocated +// and valid, but uninitialized. +unsafe { + slice::from_raw_parts_mut ( + self.as_mut_ptr ().add (self.len)as *mut MaybeUninit <T >, + self.capacity () - self.len, + ) + }There are two
unsafeoperations in this block, please split it into two blocks.Quoted 9 lines
rust/kernel/alloc/kvec.rs new file +9
-0+ } + +/// Appends an element to the back of the [`Vec`] instance. +/// +/// # Examples +/// +/// ``` +/// let mut v = KVec::new(); +/// v.push(1, GFP_KERNEL)?; rust/kernel/alloc/kvec.rs line 288
Quoted 10 lines
rust/kernel/alloc/kvec.rs new file +10
-0+/// assert_eq!(&v, &[1]); +/// +/// v.push(2, GFP_KERNEL)?; +/// assert_eq!(&v, &[1, 2]); +/// # Ok::<(), Error>(()) +/// ``` +pub fn push (&mut self, v:T , flags:Flags ) ->Result <(),AllocError > { +Vec ::reserve (self,1 , flags)?; +let s = self.spare_capacity_mut (); + s[0 ].write (v);The slice access will check the size, I don't know if it will be optimized out, if not, I would use a raw pointer here instead.
Quoted 43 lines
rust/kernel/alloc/kvec.rs new file +43
-0+ +// SAFETY: We just initialised the first spare entry, so it is safe to increase the length +// by 1. We also know that the new length is <= capacity because of the previous call to +// `reserve` above. +unsafe { self.set_len (self.len () +1 ) }; +Ok (()) + } + +/// Creates a new [`Vec`] instance with at least the given capacity. +/// +/// # Examples +/// +/// ``` +/// let v = KVec::<u32>::with_capacity(20, GFP_KERNEL)?; +/// +/// assert!(v.capacity() >= 20); +/// # Ok::<(), Error>(()) +/// ``` +pub fn with_capacity (capacity:usize , flags:Flags ) ->Result <Self ,AllocError > { +let mut v =Vec ::new (); + +Self ::reserve (&mut v, capacity, flags)?; + +Ok (v) + } + +/// Pushes clones of the elements of slice into the [`Vec`] instance. +/// +/// # Examples +/// +/// ``` +/// let mut v = KVec::new(); +/// v.push(1, GFP_KERNEL)?; +/// +/// v.extend_from_slice(&[20, 30, 40], GFP_KERNEL)?; +/// assert_eq!(&v, &[1, 20, 30, 40]); +/// +/// v.extend_from_slice(&[50, 60], GFP_KERNEL)?; +/// assert_eq!(&v, &[1, 20, 30, 40, 50, 60]); +/// # Ok::<(), Error>(()) +/// ``` +pub fn extend_from_slice (&mut self, other: &[T ], flags:Flags ) ->Result <(),AllocError > +where rust/kernel/alloc/kvec.rs line 341
Quoted 10 lines
rust/kernel/alloc/kvec.rs new file +10
-0+ T: Clone, + { + self.reserve(other.len(), flags)?; +for (slot, item)in core::iter::zip (self.spare_capacity_mut (), other) { + slot.write (item.clone ()); + } + +// SAFETY: We just initialised the `other.len()` spare entries, so it is safe to increase +// the length by the same amount. We also know that the new length is <= capacity because +// of the previous call to `reserve` above. Please use a bullet point list and avoid natural language filler words.
rust/kernel/alloc/kvec.rs line 353
Quoted 12 lines
rust/kernel/alloc/kvec.rs new file +12
-0+unsafe { self.set_len (self.len () + other.len ()) }; +Ok (()) + } + +/// Creates a Vec<T, A> from a pointer, a length and a capacity using the allocator `A`. +/// +/// # Safety +/// +/// If `T` is a ZST: +/// +/// - `ptr` must be a dangling pointer. +/// - `capacity` must be zero. When
Tis a ZST,into_raw_partsreturnsusize::MAXforcapacity, so one can't just pipe its output to this function? That seems strange.Also below you discard the value of
capacity, whenTis a ZST, so I think you can just remove this line.rust/kernel/alloc/kvec.rs line 354
Quoted 1 line
rust/kernel/alloc/kvec.rs new file +1
-0+/// - `length` must be smaller than or equal to `usize::MAX`. This is always true.
rust/kernel/alloc/kvec.rs line 361
Quoted 7 lines
rust/kernel/alloc/kvec.rs new file +7
-0+/// +/// Otherwise: +/// +/// - `ptr` must have been allocated with the allocator `A`. +/// - `ptr` must satisfy or exceed the alignment requirements of `T`. +/// - `ptr` must point to memory with a size of at least `size_of::<T>` times the `capacity` +/// bytes. Just write "
size_of::<T>() * capacitybytes".rust/kernel/alloc/kvec.rs line 362
Quoted 1 line
rust/kernel/alloc/kvec.rs new file +1
-0+/// - The allocated size in bytes must not be larger than `isize::MAX`. Should we make this a safety requirement of
Allocator? I think this is generally the maximum size other allocated things can have anyways.
Quoted 8 lines
rust/kernel/alloc/kvec.rs line 367
Quoted 5 lines
rust/kernel/alloc/kvec.rs new file +5
-0+/// - `length` must be less than or equal to `capacity`. +/// - The first `length` elements must be initialized values of type `T`. +/// +/// It is also valid to create an empty `Vec` passing a dangling pointer for `ptr` and zero for +/// `cap` and `len`. Can you make this last sentence part of the
ifchain that you have above (ie the one started with "IfTis a ZST:").
But T isn't necessarily a ZST, but it may be.
Quoted 19 lines
Just to experiment with the suggestion from Kangrejos to use Rust as the language for safety documentation, here is what it could look like (we should discuss it more before we start using it):
/// # Safety /// /// ```ignore /// assert!(ptr.is_aligned_to(align_of::<T>())); /// assert!(!ptr.is_null()); /// assert!(length <= capacity); /// if capacity != 0 && size_of::<T>() != 0 { /// assert!(A::did_allocate(ptr)); /// assert!(size_of::<T>() * capacity <= A::layout_of(ptr).len()); /// assert!(is_initialized(ptr::slice_from_raw_parts(ptr, length))); /// } /// ```
Quoted 4 lines
I really like how this looks! We might want to add labels/names to each of the conditions and then one could use those in the justifications. A tool could then read those and match them to the requirements of the unsafe operation.
I need to think about this a bit more, but at a first glance I think I like it.
The tool would ideally be the compiler itself...
Quoted 316 lines
Quoted 13 lines
rust/kernel/alloc/kvec.rs new file +12
-0+/// +/// # Examples +/// +/// ``` +/// let mut v = kernel::kvec![1, 2, 3]?; +/// v.reserve(1, GFP_KERNEL)?; +/// +/// let (mut ptr, mut len, cap) = v.into_raw_parts(); +/// +/// // SAFETY: We've just reserved memory for another element. +/// unsafe { ptr.add(len).write(4) }; +/// len += 1; rust/kernel/alloc/kvec.rs line 389
Quoted 10 lines
rust/kernel/alloc/kvec.rs new file +10
-0+/// +/// // SAFETY: We only wrote an additional element at the end of the `KVec`'s buffer and +/// // correspondingly increased the length of the `KVec` by one. Otherwise, we construct it +/// // from the exact same raw parts. +/// let v = unsafe { KVec::from_raw_parts(ptr, len, cap) }; +/// +/// assert_eq!(v, [1, 2, 3, 4]); +/// +/// # Ok::<(), Error>(()) +/// ``` Please reorder the sections to be
# Examplesfollowed by# Safety.Quoted 45 lines
rust/kernel/alloc/kvec.rs new file +45
-0+pub unsafe fn from_raw_parts (ptr: *mut T , length:usize , capacity:usize ) ->Self { +let cap =if Self ::is_zst () {0 }else { capacity }; + +Self { +// SAFETY: By the safety requirements, `ptr` is either dangling or pointing to a valid +// memory allocation, allocated with `A`. + ptr:unsafe {NonNull ::new_unchecked (ptr) }, + cap, + len: length, + _p:PhantomData ::<A >, + } + } + +/// Consumes the `Vec<T, A>` and returns its raw components `pointer`, `length` and `capacity`. +/// +/// This will not run the destructor of the contained elements and for non-ZSTs the allocation +/// will stay alive indefinitely. Use [`Vec::from_raw_parts`] to recover the [`Vec`], drop the +/// elements and free the allocation, if any. +pub fn into_raw_parts (self) -> (*mut T ,usize ,usize ) { +let me =ManuallyDrop ::new (self); +let len = me.len (); +let capacity = me.capacity (); +let ptr = me.as_mut_ptr (); + (ptr, len, capacity) + } + +/// Ensures that the capacity exceeds the length by at least `additional` +/// elements. +/// +/// # Examples +/// +/// ``` +/// let mut v = KVec::new(); +/// v.push(1, GFP_KERNEL)?; +/// +/// v.reserve(10, GFP_KERNEL)?; +/// let cap = v.capacity(); +/// assert!(cap >= 10); +/// +/// v.reserve(10, GFP_KERNEL)?; +/// let new_cap = v.capacity(); +/// assert_eq!(new_cap, cap); +/// +/// # Ok::<(), Error>(()) +/// ``` rust/kernel/alloc/kvec.rs line 444
Quoted 10 lines
rust/kernel/alloc/kvec.rs new file +10
-0+pub fn reserve (&mut self, additional:usize , flags:Flags ) ->Result <(),AllocError > { +let len = self.len (); +let cap = self.capacity (); + +if cap - len >= additional { +return Ok (()); + } + +if Self ::is_zst () { +// The capacity is already `usize::MAX` for SZTs, we can't go higher. Typo: SZTs -> ZSTs
rust/kernel/alloc/kvec.rs line 448
Quoted 4 lines
rust/kernel/alloc/kvec.rs new file +4
-0+return Err (AllocError ); + } + +// We know `cap` is <= `isize::MAX` because of it's type invariant. So the multiplication by "it's type invariant" -> "type invariants of Self"
Quoted 19 lines
rust/kernel/alloc/kvec.rs new file +19
-0+// two won't overflow. +let new_cap = core::cmp::max (cap *2 , len.checked_add (additional).ok_or (AllocError )?); +let layout = core::alloc::Layout ::array ::<T >(new_cap).map_err (|_|AllocError )?; + +// We need to make sure that `ptr` is either NULL or comes from a previous call to +// `realloc_flags`. A `Vec<T, A>`'s `ptr` value is not guaranteed to be NULL and might be +// dangling after being created with `Vec::new`. Instead, we can rely on `Vec<T, A>`'s +// capacity to be zero if no memory has been allocated yet. +let ptr =if cap ==0 { +None + }else { +Some (self.ptr.cast ()) + }; + +// SAFETY: `ptr` is valid because it's either `None` or comes from a previous call to +// `A::realloc`. We also verified that the type is not a ZST. +let ptr =unsafe {A ::realloc (ptr, layout, flags)? }; + + self.ptr = ptr.cast ();rust/kernel/alloc/kvec.rs line 477
Quoted 10 lines
rust/kernel/alloc/kvec.rs new file +10
-0+ +// INVARIANT: `Layout::array` fails if the resulting byte size is greater than `isize::MAX`. + self.cap = new_cap; + +Ok (()) + } +} + +impl <T :Clone ,A :Allocator >Vec <T ,A > { +/// Extend the vector by `n` clones of value. value ->
valueQuoted 8 lines
rust/kernel/alloc/kvec.rs new file +8
-0+pub fn extend_with (&mut self, n:usize , value:T , flags:Flags ) ->Result <(),AllocError > { +if n ==0 { +return Ok (()); + } + + self.reserve (n, flags)?; + +let spare = self.spare_capacity_mut ();rust/kernel/alloc/kvec.rs line 495
Quoted 10 lines
rust/kernel/alloc/kvec.rs new file +10
-0+ +for itemin spare.iter_mut ().take (n -1 ) { + item.write (value.clone ()); + } + +// We can write the last element directly without cloning needlessly. + spare[n -1 ].write (value); + +// SAFETY: `self.reserve` not bailing out with an error guarantees that we're not +// exceeding the capacity of this `Vec`. This safety comment needs to be improved, how about using bullet points:
-self.len() + n < capacitydue to the call to reserve above,
- the loop and the line above initialized the nextnelements.Quoted 14 lines
rust/kernel/alloc/kvec.rs new file +14
-0+unsafe { self.set_len (self.len () + n) }; + +Ok (()) + } + +/// Create a new `Vec<T, A> and extend it by `n` clones of `value`. +pub fn from_elem (value:T , n:usize , flags:Flags ) ->Result <Self ,AllocError > { +let mut v =Self ::with_capacity (n, flags)?; + + v.extend_with (n, value, flags)?; + +Ok (v) + } +}rust/kernel/alloc/kvec.rs line 519
Quoted 10 lines
rust/kernel/alloc/kvec.rs new file +10
-0+ +impl<T, A> Drop for Vec<T, A> +where + A: Allocator, +{ + fn drop(&mut self) { + // SAFETY: We need to drop the vector's elements in place, before we free the backing + // memory. + unsafe { + core::ptr::drop_in_place(core::ptr::slice_from_raw_parts_mut(You are using
core::ptra bunch, consider using it, then you don't need to write the prefixcore::.Quoted 12 lines
rust/kernel/alloc/kvec.rs new file +12
-0+ self.as_mut_ptr(), + self.len, + )) + }; + +// If `cap == 0` we never allocated any memory in the first place. +if self.cap !=0 { +// SAFETY: `self.ptr` was previously allocated with `A`. +unsafe {A ::free (self.ptr.cast ()) }; + } + } +}rust/kernel/alloc/kvec.rs line 541
Quoted 10 lines
rust/kernel/alloc/kvec.rs new file +10
-0+ +impl <T ,A ,const N :usize >From <Box <[T ;N ],A >>for Vec <T ,A > +where +A :Allocator , +{ +fn from (b:Box <[T ;N ],A >) ->Vec <T ,A > { +let len = b.len (); +let ptr =Box ::into_raw (b); + +unsafe {Vec ::from_raw_parts (ptras _ , len, len) }Missing SAFETY comment.
Quoted 57 lines
rust/kernel/alloc/kvec.rs new file +57
-0+ } +} + +impl <T >Default for KVec <T > { +#[inline] +fn default () ->Self { +Self ::new () + } +} + +impl <T : fmt::Debug ,A :Allocator > fmt::Debug for Vec <T ,A > { +fn fmt (&self, f: &mut fmt::Formatter <'_>) -> fmt::Result { + fmt::Debug ::fmt (&**self, f) + } +} + +impl <T ,A >Deref for Vec <T ,A > +where +A :Allocator , +{ +type Target = [T ]; + +#[inline] +fn deref (&self) -> &[T ] { +// SAFETY: The memory behind `self.as_ptr()` is guaranteed to contain `self.len` +// initialized elements of type `T`. +unsafe { slice::from_raw_parts (self.as_ptr (), self.len) } + } +} + +impl <T ,A >DerefMut for Vec <T ,A > +where +A :Allocator , +{ +#[inline] +fn deref_mut (&mut self) -> &mut [T ] { +// SAFETY: The memory behind `self.as_ptr()` is guaranteed to contain `self.len` +// initialized elements of type `T`. +unsafe { slice::from_raw_parts_mut (self.as_mut_ptr (), self.len) } + } +} + +impl <T :Eq ,A >Eq for Vec <T ,A >where A :Allocator {} + +impl <T ,I :SliceIndex <[T ]>,A >Index <I >for Vec <T ,A > +where +A :Allocator , +{ +type Output =I ::Output ; + +#[inline] +fn index (&self, index:I ) -> &Self ::Output { +Index ::index (&**self, index) + } +} + +impl <T ,I :SliceIndex <[T ]>,A >IndexMut <I >for Vec <T ,A >rust/kernel/alloc/kvec.rs line 608
Quoted 10 lines
rust/kernel/alloc/kvec.rs new file +10
-0+where + A: Allocator, +{ + #[inline] + fn index_mut(&mut self, index: I) -> &mut Self::Output { + IndexMut::index_mut(&mut **self, index) + } +} + +macro_rules! __impl_slice_eq {I wouldn't begin this macro with
__, it is already private.rust/kernel/alloc/kvec.rs line 609
Quoted 1 line
rust/kernel/alloc/kvec.rs new file +1
-0+ ([$($vars:tt)*] $lhs:ty, $rhs:ty $(where $ty:ty: $bound:ident)?) => {You don't use the optional
wherepart in the code below, if you don't use it later, then remove it (if you do use it later, I would move it into that patch).---
Cheers,
BennoQuoted 36 lines
rust/kernel/alloc/kvec.rs new file +20
-0+ impl<T, U, $($vars)*> PartialEq<$rhs> for $lhs +where + T: PartialEq<U>, + $($ty: $bound)? + { + #[inline] + fn eq(&self, other: &$rhs) -> bool { self[..] == other[..] } + } + } +} + +__impl_slice_eq! { [A1 :Allocator ,A2 :Allocator ]Vec <T ,A1 >,Vec <U ,A2 > } +__impl_slice_eq! { [A :Allocator ]Vec <T ,A >, &[U ] } +__impl_slice_eq! { [A :Allocator ]Vec <T ,A >, &mut [U ] } +__impl_slice_eq! { [A :Allocator ] &[T ],Vec <U ,A > } +__impl_slice_eq! { [A :Allocator ] &mut [T ],Vec <U ,A > } +__impl_slice_eq! { [A :Allocator ]Vec <T ,A >, [U ] } +__impl_slice_eq! { [A :Allocator ] [T ],Vec <U ,A > } +__impl_slice_eq! { [A :Allocator ,const N :usize ]Vec <T ,A >, [U ;N ] } +__impl_slice_eq! { [A :Allocator ,const N :usize ]Vec <T ,A >, &[U ;N ] }rust/kernel/prelude.rs +1
-2@@ -14,7 +14,7 @@#[doc(no_inline)] pub use core::pin::Pin ; -pub use crate ::alloc::{flags::*, vec_ext::VecExt ,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 ; --2.46.0
Re: [PATCH v6 13/26] rust: alloc: implement kernel `Vec` type
Quoted 10 lines · Danilo Krummrich
Quoted 8 lines · Benno Lossin
rust/kernel/alloc/kvec.rs line 47
Quoted 3 lines · Danilo Krummrich
rust/kernel/alloc/kvec.rs new file +3
-0+ ($elem:expr; $n:expr) => ( + { + $crate::alloc::KVec ::from_elem ($elem, $n,GFP_KERNEL )Hmm, could it be that one might want to use
kvec!withoutGFP_KERNEL?
Or add additional flags, eg__GFP_ZERO?
Quoted 5 lines
Pretty unlikely, I'd say.
__GFP_ZEROin particular wouldn't make much sense, since the macro enforces initialization anyways.Maybe something like
GFP_ATOMIC, but I expect this to be uncommon enough to not consider this for this macro for now.
SGTM
Quoted 11 lines
Quoted 5 lines
If you think that supporting this is not immediately necessary or if this is too much for this patchset, then we can also postpone it (maybe it could be a good-first-issue). Do you keep a list of future improvements for the new allocator API somewhere? If not, then I think we should create one (best place would be the issue tracker on GH).
I would only add it if it turns out to be a common need. As mentioned, I don't expect it to be.
I'd rather keep issues in the source tree. For instance, DRM has them in '/Documentation/gpu/todo.rst'.
But then you need to submit patches to change them... That sounds like a hassle and we already have the precedent to use the github issue tracker. It is also much better for discoverability for people outside of the kernel.
Quoted 27 lines
Quoted 11 lines
Quoted 11 lines
rust/kernel/alloc/kvec.rs new file +11
-0+ } + ); + ($($x:expr),+ $(,)?) => ( + { +match $crate::alloc::KBox ::new_uninit (GFP_KERNEL ) { + Ok(b) => Ok($crate::alloc::KVec::from($crate::alloc::KBox::write(b, [$($x),+]))), +Err (e) =>Err (e), + } + } + ); +}Quoted 8 lines
rust/kernel/alloc/kvec.rs line 63
Quoted 5 lines
rust/kernel/alloc/kvec.rs new file +5
-0+ +/// The kernel's [`Vec`] type. +/// +/// A contiguous growable array type with contents allocated with the kernel's allocators (e.g. +/// `Kmalloc`, `Vmalloc` or `KVmalloc`), written `Vec<T, A>`. New folks might get confused as to which allocator they should choose. Do we have a sensible default for
Vec?Then they should read the documentation about the kernel's allocators. We already link them in
rust/kernel/alloc/allocator.rs.Quoted 3 lines
If yes, then we at least should document that here. We might also want to make it the default value for the generic parameter. This is also a good idea for
Box.
Quoted 5 lines
If we really want a default it should be
Kmalloc, but I really think we should force people to make an explicit decision and think about it and don't just go with whatever default.It makes it also easier to grep for things. :)
SGTM
Quoted 42 lines
Quoted 34 lines
Quoted 11 lines
rust/kernel/alloc/kvec.rs new file +11
-0+/// +/// For non-zero-sized values, a [`Vec`] will use the given allocator `A` for its allocation. For +/// the most common allocators the type aliases `KVec`, `VVec` and `KVVec` exist. +/// +/// For zero-sized types the [`Vec`]'s pointer must be `dangling_mut::<T>`; no memory is allocated. +/// +/// Generally, [`Vec`] consists of a pointer that represents the vector's backing buffer, the +/// capacity of the vector (the number of elements that currently fit into the vector), it's length +/// (the number of elements that are currently stored in the vector) and the `Allocator` type used +/// to allocate (and free) the backing buffer. +/// rust/kernel/alloc/kvec.rs line 84
Quoted 10 lines
rust/kernel/alloc/kvec.rs new file +10
-0+/// A [`Vec`] can be deconstructed into and (re-)constructed from it's previously named raw parts +/// and manually modified. +/// +/// [`Vec`]'s backing buffer gets, if required, automatically increased (re-allocated) when elements +/// are added to the vector. +/// +/// # Invariants +/// +/// The [`Vec`] backing buffer's pointer is always properly aligned and either points to memory +/// allocated with `A` or, for zero-sized types, is a dangling pointer. Just use
self.ptrinstead of "backing buffer".Review
Quoted 2 lines
(quoted diff) +2
-0+/// +/// The length of the vector always represents the exact number of elements stored in the vector.Same here,
self.len.Review
Quoted 2 lines
(quoted diff) +2
-0+/// +/// The capacity of the vector always represents the absolute number of elements that can be storedDitto.
Quoted 7 lines
rust/kernel/alloc/kvec.rs line 93
Quoted 5 lines
rust/kernel/alloc/kvec.rs new file +5
-0+/// within the vector without re-allocation. However, it is legal for the backing buffer to be +/// larger than `size_of<T>` times the capacity. +/// +/// The `Allocator` type `A` of the vector is the exact same `Allocator` type the backing buffer was +/// allocated with (and must be freed with). Please turn this into a bullet-point list.
Quoted 1 line
Is this a rule? Do we want to make it one?
I am trying to make it one with my safety standard.
Quoted 32 lines
Quoted 32 lines
Quoted 31 lines
rust/kernel/alloc/kvec.rs new file +28
-0+pub struct Vec <T ,A :Allocator > { + ptr:NonNull <T >, +/// Represents the actual buffer size as `cap` times `size_of::<T>` bytes. +/// +/// Note: This isn't quite the same as `Self::capacity`, which in contrast returns the number of +/// elements we can still store without reallocating. +/// +/// # Invariants +/// +/// `cap` must be in the `0..=isize::MAX` range. + cap:usize , + len:usize , + _p:PhantomData <A >, +} +/// Creates a new, empty Vec<T, A>. +/// +/// This method does not allocate by itself. +#[inline] +pub const fn new () ->Self { +Self { + ptr:NonNull ::dangling (), + cap:0 , + len:0 , + _p:PhantomData ::<A >, + } + } + +/// Returns a slice of `MaybeUninit<T>` for the remaining spare capacity of the vector.
Quoted 3 lines
Quoted 1 line
Returns
Hm?
Forgot to finish that sentence, since I couldn't really pinpoint what exactly I wanted to change. Just ignore it.
Quoted 36 lines
Quoted 36 lines
Quoted 9 lines
rust/kernel/alloc/kvec.rs new file +9
-0+pub fn spare_capacity_mut (&mut self) -> &mut [MaybeUninit <T >] { +// SAFETY: The memory between `self.len` and `self.capacity` is guaranteed to be allocated +// and valid, but uninitialized. +unsafe { + slice::from_raw_parts_mut ( + self.as_mut_ptr ().add (self.len)as *mut MaybeUninit <T >, + self.capacity () - self.len, + ) + }rust/kernel/alloc/kvec.rs
Quoted 10 lines
rust/kernel/alloc/kvec.rs new file +7
-0+/// +/// Otherwise: +/// +/// - `ptr` must have been allocated with the allocator `A`. +/// - `ptr` must satisfy or exceed the alignment requirements of `T`. +/// - `ptr` must point to memory with a size of at least `size_of::<T>` times the `capacity` +/// bytes. Just write "
size_of::<T>() * capacitybytes".Quoted 1 line
+ /// - The allocated size in bytes must not be larger than
isize::MAX.Should we make this a safety requirement of
Allocator? I think this is generally the maximum size other allocated things can have anyways.Quoted 5 lines
rust/kernel/alloc/kvec.rs new file +5
-0+/// - `length` must be less than or equal to `capacity`. +/// - The first `length` elements must be initialized values of type `T`. +/// +/// It is also valid to create an empty `Vec` passing a dangling pointer for `ptr` and zero for +/// `cap` and `len`.
Quoted 4 lines
Quoted 2 lines
Can you make this last sentence part of the
ifchain that you have above (ie the one started with "IfTis a ZST:").But
Tisn't necessarily a ZST, but it may be.
I originally meant adding an "else if" part that checks for empty capacity. But you could just add that to the if at the top ie "If capacity == 0 or T is a ZST".
Quoted 17 lines
Quoted 17 lines
Just to experiment with the suggestion from Kangrejos to use Rust as the language for safety documentation, here is what it could look like (we should discuss it more before we start using it):
/// # Safety /// /// ```ignore /// assert!(ptr.is_aligned_to(align_of::<T>())); /// assert!(!ptr.is_null()); /// assert!(length <= capacity); /// if capacity != 0 && size_of::<T>() != 0 { /// assert!(A::did_allocate(ptr)); /// assert!(size_of::<T>() * capacity <= A::layout_of(ptr).len()); /// assert!(is_initialized(ptr::slice_from_raw_parts(ptr, length))); /// } /// ```
Quoted 8 lines
Quoted 4 lines
I really like how this looks! We might want to add labels/names to each of the conditions and then one could use those in the justifications. A tool could then read those and match them to the requirements of the unsafe operation.
I need to think about this a bit more, but at a first glance I think I like it.
The tool would ideally be the compiler itself...
Yes! There is the contracts draft PR that might add support for
this: https://github.com/rust-lang/rust/pull/128045
---
Cheers,
Benno
Re: [PATCH v6 13/26] rust: alloc: implement kernel `Vec` type
In reply to Danilo KrummrichQuoted 9 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 7 lines
Signed-off-by Danilo Krummrich <[email protected]>--- rust/kernel/alloc.rs | 6 + rust/kernel/alloc/kvec.rs | 629 ++++++++++++++++++++++++++++++++++++++ rust/kernel/prelude.rs | 2 +- 3 files changed, 636 insertions(+), 1 deletion(-) create mode 100644 rust/kernel/alloc/kvec.rs
I noticed that you don't have a pop or remove function implemented, I think it would be weird to have all the other functions but not those.
---
Cheers,
Benno
Danilo Krummrich
maintainerRe: [PATCH v6 13/26] rust: alloc: implement kernel `Vec` type
Quoted 18 lines · Benno Lossin
Quoted 9 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 7 lines
Signed-off-by Danilo Krummrich <[email protected]>--- rust/kernel/alloc.rs | 6 + rust/kernel/alloc/kvec.rs | 629 ++++++++++++++++++++++++++++++++++++++ rust/kernel/prelude.rs | 2 +- 3 files changed, 636 insertions(+), 1 deletion(-) create mode 100644 rust/kernel/alloc/kvec.rs
Quoted 2 lines
I noticed that you don't have a
poporremovefunction implemented, I think it would be weird to have all the other functions but not those.
I'd rather not add them for this series anymore. We can add them in subsequent patches, or wait until they're actually needed.
I also already have some subsequent patches in the queue. For instance, in Nova we'll need Vec::resize and Vec::truncate.
Quoted 5 lines
---
Cheers,
Benno
Alice Ryhl
maintainerRe: [PATCH v6 13/26] rust: alloc: implement kernel `Vec` type
Quoted 20 lines · Danilo Krummrich
Quoted 18 lines · Benno Lossin
Quoted 16 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.rs | 6 + rust/kernel/alloc/kvec.rs | 629 ++++++++++++++++++++++++++++++++++++++ rust/kernel/prelude.rs | 2 +- 3 files changed, 636 insertions(+), 1 deletion(-) create mode 100644 rust/kernel/alloc/kvec.rs
Quoted 8 lines
Quoted 2 lines
I noticed that you don't have a
poporremovefunction implemented, I think it would be weird to have all the other functions but not those.I'd rather not add them for this series anymore. We can add them in subsequent patches, or wait until they're actually needed.
I also already have some subsequent patches in the queue. For instance, in Nova we'll need
Vec::resizeandVec::truncate.
When this lands in rust-next, we should open good-first-issues for these on the Rust-for-Linux bug tracker.
Alice
Danilo Krummrich
maintainerPATCH v6 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.46.0
Re: [PATCH v6 14/26] rust: alloc: implement `IntoIterator` for `Vec`
Quoted 9 lines · Danilo Krummrich
Implement
IntoIteratorforVec,Vec'sIntoItertype, as well asIteratorforIntoIter.
Vec::into_iterdisassembles theVecinto its raw parts; additionally,IntoIterkeeps track of a separate pointer, which is incremented correspondingsly as the iterator advances, while the length, or the count of elements, is decremented.
Quoted 5 lines
This also means that
IntoItertakes the ownership of the backing buffer and is responsible to drop the remaining elements and free the backing buffer, if it's dropped.Signed-off-by Danilo Krummrich <[email protected]>
This looks ok to me. One nit below, though. Either way:
Quoted 102 lines
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 +71
-0@@ -11,6 +11,7 @@ ops::DerefMut, ops::Index, ops::IndexMut, + ptr, ptr::NonNull, slice, slice::SliceIndex,@@ -627,3 +628,186 @@ fn eq(&self, other: &$rhs) -> bool { self[..] == other[..] }__impl_slice_eq! { [A :Allocator ] [T ],Vec <U ,A > }__impl_slice_eq! { [A :Allocator ,const N :usize ]Vec <T ,A >, [U ;N ] }__impl_slice_eq! { [A :Allocator ,const N :usize ]Vec <T ,A >, &[U ;N ] } + +impl <'a,T ,A >IntoIterator for &'aVec <T ,A > +where +A :Allocator , +{ +type Item = &'aT ; +type IntoIter = slice::Iter <'a,T >; + +fn into_iter (self) ->Self ::IntoIter { + self.iter () + } +} + +impl <'a,T ,A :Allocator >IntoIterator for &'amut Vec <T ,A > +where +A :Allocator , +{ +type Item = &'amut T ; +type IntoIter = slice::IterMut <'a,T >; + +fn into_iter (self) ->Self ::IntoIter { + self.iter_mut () + } +} + +/// An `Iterator` implementation for `Vec<T,A>` that moves elements out of a vector. +/// +/// This structure is created by the `Vec::into_iter` method on [`Vec`] (provided by the +/// [`IntoIterator`] trait). +/// +/// # Examples +/// +/// ``` +/// let v = kernel::kvec![0, 1, 2]?; +/// let iter = v.into_iter(); +/// +/// # Ok::<(), Error>(()) +/// ``` +pub struct IntoIter <T ,A :Allocator > { + ptr: *mut T , + buf:NonNull <T >, + len:usize , + cap:usize , + _p:PhantomData <A >, +} + +impl <T ,A >IntoIter <T ,A > +where +A :Allocator , +{ +fn as_raw_mut_slice (&mut self) -> *mut [T ] { + ptr::slice_from_raw_parts_mut (self.ptr, self.len) + } +} + +impl <T ,A >Iterator for IntoIter <T ,A > +where +A :Allocator , +{ +type Item =T ; + +/// # Examples +/// +/// ``` +/// let v = kernel::kvec![1, 2, 3]?; +/// let mut it = v.into_iter(); +/// +/// assert_eq!(it.next(), Some(1)); +/// assert_eq!(it.next(), Some(2)); +/// assert_eq!(it.next(), Some(3));
rust/kernel/alloc/kvec.rs fn eq(&self, other: &$rhs) -> bool { self[..] == other[..] } · line 710
Quoted 10 lines
rust/kernel/alloc/kvec.rs +10
-0+/// assert_eq!(it.next(), None); +/// +/// # Ok::<(), Error>(()) +/// ``` +fn next (&mut self) ->Option <T > { +if self.len ==0 { +return None ; + } + +let ptr = self.ptr;
Nit: It would probably be slightly clearer to rename this variable to current.
Quoted 106 lines
rust/kernel/alloc/kvec.rs +103
-1+if !Vec ::<T ,A >::is_zst () { +// SAFETY: We can't overflow; `end` is guaranteed to mark the end of the buffer. +unsafe { self.ptr = self.ptr.add (1 ) }; + }else { +// For ZST `ptr` has to stay where it is to remain aligned, so we just reduce `self.len` +// by 1. + } + self.len -=1 ; + +// SAFETY: `ptr` is guaranteed to point at a valid element within the buffer. +Some (unsafe { ptr.read () }) + } + +/// # Examples +/// +/// ``` +/// let v: KVec<u32> = kernel::kvec![1, 2, 3]?; +/// let mut iter = v.into_iter(); +/// let size = iter.size_hint().0; +/// +/// iter.next(); +/// assert_eq!(iter.size_hint().0, size - 1); +/// +/// iter.next(); +/// assert_eq!(iter.size_hint().0, size - 2); +/// +/// iter.next(); +/// assert_eq!(iter.size_hint().0, size - 3); +/// +/// # Ok::<(), Error>(()) +/// ``` +fn size_hint (&self) -> (usize ,Option <usize >) { + (self.len,Some (self.len)) + } +} + +impl <T ,A >Drop for IntoIter <T ,A > +where +A :Allocator , +{ +fn drop (&mut self) { +// SAFETY: Drop the remaining vector's elements in place, before we free the backing +// memory. +unsafe { ptr::drop_in_place (self.as_raw_mut_slice ()) }; + +// If `cap == 0` we never allocated any memory in the first place. +if self.cap !=0 { +// SAFETY: `self.buf` was previously allocated with `A`. +unsafe {A ::free (self.buf.cast ()) }; + } + } +} + +impl <T ,A >IntoIterator for Vec <T ,A > +where +A :Allocator , +{ +type Item =T ; +type IntoIter =IntoIter <T ,A >; + +/// Consumes the `Vec<T, A>` and creates an `Iterator`, which moves each value out of the +/// vector (from start to end). +/// +/// # Examples +/// +/// ``` +/// let v = kernel::kvec![1, 2]?; +/// let mut v_iter = v.into_iter(); +/// +/// let first_element: Option<u32> = v_iter.next(); +/// +/// assert_eq!(first_element, Some(1)); +/// assert_eq!(v_iter.next(), Some(2)); +/// assert_eq!(v_iter.next(), None); +/// +/// # Ok::<(), Error>(()) +/// ``` +/// +/// ``` +/// let v = kernel::kvec![]; +/// let mut v_iter = v.into_iter(); +/// +/// let first_element: Option<u32> = v_iter.next(); +/// +/// assert_eq!(first_element, None); +/// +/// # Ok::<(), Error>(()) +/// ``` +#[inline] +fn into_iter (self) ->Self ::IntoIter { +let (ptr, len, cap) = self.into_raw_parts (); + +IntoIter { + ptr, +// SAFETY: `ptr` is either a dangling pointer or a pointer to a valid memory +// allocation, allocated with `A`. + buf:unsafe {NonNull ::new_unchecked (ptr) }, + len, + cap, + _p:PhantomData ::<A >, + } + } +} --2.46.0
Re: [PATCH v6 14/26] rust: alloc: implement `IntoIterator` for `Vec`
In reply to Danilo KrummrichQuoted 62 lines · Danilo Krummrich
Implement
IntoIteratorforVec,Vec'sIntoItertype, as well asIteratorforIntoIter.
Vec::into_iterdisassembles theVecinto its raw parts; additionally,IntoIterkeeps 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
IntoItertakes the ownership of the backing buffer and is responsible to drop the remaining elements and free the backing buffer, if it's dropped.Signed-off-by Danilo Krummrich <[email protected]>--- 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 +17
-0@@ -11,6 +11,7 @@ ops::DerefMut, ops::Index, ops::IndexMut, + ptr, ptr::NonNull, slice, slice::SliceIndex,@@ -627,3 +628,186 @@ fn eq(&self, other: &$rhs) -> bool { self[..] == other[..] }__impl_slice_eq! { [A :Allocator ] [T ],Vec <U ,A > }__impl_slice_eq! { [A :Allocator ,const N :usize ]Vec <T ,A >, [U ;N ] }__impl_slice_eq! { [A :Allocator ,const N :usize ]Vec <T ,A >, &[U ;N ] } + +impl <'a,T ,A >IntoIterator for &'aVec <T ,A > +where +A :Allocator , +{ +type Item = &'aT ; +type IntoIter = slice::Iter <'a,T >; + +fn into_iter (self) ->Self ::IntoIter { + self.iter () + } +} + +impl <'a,T ,A :Allocator >IntoIterator for &'amut Vec <T ,A > +where +A :Allocator ,
rust/kernel/alloc/kvec.rs fn eq(&self, other: &$rhs) -> bool { self[..] == other[..] } · line 656
Quoted 10 lines
rust/kernel/alloc/kvec.rs +10
-0+{ +type Item = &'amut T ; +type IntoIter = slice::IterMut <'a,T >; + +fn into_iter (self) ->Self ::IntoIter { + self.iter_mut () + } +} + +/// An `Iterator` implementation for `Vec<T,A>` that moves elements out of a vector.
Please make both links.
rust/kernel/alloc/kvec.rs fn eq(&self, other: &$rhs) -> bool { self[..] == other[..] } · line 658
Quoted 2 lines
rust/kernel/alloc/kvec.rs +2
-0+/// +/// This structure is created by the `Vec::into_iter` method on [`Vec`] (provided by the
Ditto.
rust/kernel/alloc/kvec.rs fn eq(&self, other: &$rhs) -> bool { self[..] == other[..] } · line 671
Quoted 13 lines
rust/kernel/alloc/kvec.rs +13
-0+/// [`IntoIterator`] trait). +/// +/// # Examples +/// +/// ``` +/// let v = kernel::kvec![0, 1, 2]?; +/// let iter = v.into_iter(); +/// +/// # Ok::<(), Error>(()) +/// ``` +pub struct IntoIter <T ,A :Allocator > { + ptr: *mut T , + buf:NonNull <T >,
No invariants for these two fields?
Quoted 23 lines
rust/kernel/alloc/kvec.rs +23
-0+ len: usize, + cap: usize, + _p: PhantomData<A>, +} + +impl <T ,A >IntoIter <T ,A > +where +A :Allocator , +{ +fn as_raw_mut_slice (&mut self) -> *mut [T ] { + ptr::slice_from_raw_parts_mut (self.ptr, self.len) + } +} + +impl <T ,A >Iterator for IntoIter <T ,A > +where +A :Allocator , +{ +type Item =T ; + +/// # Examples +/// +/// ```
rust/kernel/alloc/kvec.rs fn eq(&self, other: &$rhs) -> bool { self[..] == other[..] } · line 704
Quoted 10 lines
rust/kernel/alloc/kvec.rs +10
-0+/// let v = kernel::kvec![1, 2, 3]?; +/// let mut it = v.into_iter(); +/// +/// assert_eq!(it.next(), Some(1)); +/// assert_eq!(it.next(), Some(2)); +/// assert_eq!(it.next(), Some(3)); +/// assert_eq!(it.next(), None); +/// +/// # Ok::<(), Error>(()) +/// ```
AFAIK documentation on functions in trait implementations won't show up in rustdoc (I just checked this). So I would remove it.
rust/kernel/alloc/kvec.rs fn eq(&self, other: &$rhs) -> bool { self[..] == other[..] } · line 716
Quoted 12 lines
rust/kernel/alloc/kvec.rs +12
-0+fn next (&mut self) ->Option <T > { +if self.len ==0 { +return None ; + } + +let ptr = self.ptr; +if !Vec ::<T ,A >::is_zst () { +// SAFETY: We can't overflow; `end` is guaranteed to mark the end of the buffer. +unsafe { self.ptr = self.ptr.add (1 ) }; + }else { +// For ZST `ptr` has to stay where it is to remain aligned, so we just reduce `self.len` +// by 1.
Note that <*mut T>::add advances the pointer by size_of::<T>() bytes. So in the case that T is a ZST, it won't be advanced. So you could remove this if.
Quoted 27 lines
rust/kernel/alloc/kvec.rs +27
-0+ } + self.len -=1 ; + +// SAFETY: `ptr` is guaranteed to point at a valid element within the buffer. +Some (unsafe { ptr.read () }) + } + +/// # Examples +/// +/// ``` +/// let v: KVec<u32> = kernel::kvec![1, 2, 3]?; +/// let mut iter = v.into_iter(); +/// let size = iter.size_hint().0; +/// +/// iter.next(); +/// assert_eq!(iter.size_hint().0, size - 1); +/// +/// iter.next(); +/// assert_eq!(iter.size_hint().0, size - 2); +/// +/// iter.next(); +/// assert_eq!(iter.size_hint().0, size - 3); +/// +/// # Ok::<(), Error>(()) +/// ``` +fn size_hint (&self) -> (usize ,Option <usize >) { + (self.len,Some (self.len))
rust/kernel/alloc/kvec.rs fn eq(&self, other: &$rhs) -> bool { self[..] == other[..] } · line 753
Quoted 10 lines
rust/kernel/alloc/kvec.rs +10
-0+ } +} + +impl <T ,A >Drop for IntoIter <T ,A > +where +A :Allocator , +{ +fn drop (&mut self) { +// SAFETY: Drop the remaining vector's elements in place, before we free the backing +// memory.
This comment explains why you are doing it, not why it's ok to do it.
Quoted 35 lines
rust/kernel/alloc/kvec.rs +35
-0+unsafe { ptr::drop_in_place (self.as_raw_mut_slice ()) }; + +// If `cap == 0` we never allocated any memory in the first place. +if self.cap !=0 { +// SAFETY: `self.buf` was previously allocated with `A`. +unsafe {A ::free (self.buf.cast ()) }; + } + } +} + +impl <T ,A >IntoIterator for Vec <T ,A > +where +A :Allocator , +{ +type Item =T ; +type IntoIter =IntoIter <T ,A >; + +/// Consumes the `Vec<T, A>` and creates an `Iterator`, which moves each value out of the +/// vector (from start to end). +/// +/// # Examples +/// +/// ``` +/// let v = kernel::kvec![1, 2]?; +/// let mut v_iter = v.into_iter(); +/// +/// let first_element: Option<u32> = v_iter.next(); +/// +/// assert_eq!(first_element, Some(1)); +/// assert_eq!(v_iter.next(), Some(2)); +/// assert_eq!(v_iter.next(), None); +/// +/// # Ok::<(), Error>(()) +/// ``` +///
rust/kernel/alloc/kvec.rs fn eq(&self, other: &$rhs) -> bool { self[..] == other[..] } · line 798
Quoted 10 lines
rust/kernel/alloc/kvec.rs +10
-0+/// ``` +/// let v = kernel::kvec![]; +/// let mut v_iter = v.into_iter(); +/// +/// let first_element: Option<u32> = v_iter.next(); +/// +/// assert_eq!(first_element, None); +/// +/// # Ok::<(), Error>(()) +/// ```
I feel a bit bad that you wrote all of this nice documentation for functions that receive their documentation from the trait...
---
Cheers,
Benno
Quoted 18 lines
rust/kernel/alloc/kvec.rs +15
-1+#[inline] +fn into_iter (self) ->Self ::IntoIter { +let (ptr, len, cap) = self.into_raw_parts (); + +IntoIter { + ptr, +// SAFETY: `ptr` is either a dangling pointer or a pointer to a valid memory +// allocation, allocated with `A`. + buf:unsafe {NonNull ::new_unchecked (ptr) }, + len, + cap, + _p:PhantomData ::<A >, + } + } +} --2.46.0
Danilo Krummrich
maintainerRe: [PATCH v6 14/26] rust: alloc: implement `IntoIterator` for `Vec`
Quoted 81 lines · Benno Lossin
Quoted 62 lines · Danilo Krummrich
Implement
IntoIteratorforVec,Vec'sIntoItertype, as well asIteratorforIntoIter.
Vec::into_iterdisassembles theVecinto its raw parts; additionally,IntoIterkeeps 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
IntoItertakes the ownership of the backing buffer and is responsible to drop the remaining elements and free the backing buffer, if it's dropped.Signed-off-by Danilo Krummrich <[email protected]>--- 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 +17
-0@@ -11,6 +11,7 @@ ops::DerefMut, ops::Index, ops::IndexMut, + ptr, ptr::NonNull, slice, slice::SliceIndex,@@ -627,3 +628,186 @@ fn eq(&self, other: &$rhs) -> bool { self[..] == other[..] }__impl_slice_eq! { [A :Allocator ] [T ],Vec <U ,A > }__impl_slice_eq! { [A :Allocator ,const N :usize ]Vec <T ,A >, [U ;N ] }__impl_slice_eq! { [A :Allocator ,const N :usize ]Vec <T ,A >, &[U ;N ] } + +impl <'a,T ,A >IntoIterator for &'aVec <T ,A > +where +A :Allocator , +{ +type Item = &'aT ; +type IntoIter = slice::Iter <'a,T >; + +fn into_iter (self) ->Self ::IntoIter { + self.iter () + } +} + +impl <'a,T ,A :Allocator >IntoIterator for &'amut Vec <T ,A > +where +A :Allocator ,rust/kernel/alloc/kvec.rs fn eq(&self, other: &$rhs) -> bool { self[..] == other[..] } · line 656
Quoted 10 lines
rust/kernel/alloc/kvec.rs +10
-0+{ +type Item = &'amut T ; +type IntoIter = slice::IterMut <'a,T >; + +fn into_iter (self) ->Self ::IntoIter { + self.iter_mut () + } +} + +/// An `Iterator` implementation for `Vec<T,A>` that moves elements out of a vector. Please make both links.
rust/kernel/alloc/kvec.rs fn eq(&self, other: &$rhs) -> bool { self[..] == other[..] } · line 658
Quoted 2 lines
rust/kernel/alloc/kvec.rs +2
-0+/// +/// This structure is created by the `Vec::into_iter` method on [`Vec`] (provided by the Ditto.
Quoted 15 lines
rust/kernel/alloc/kvec.rs fn eq(&self, other: &$rhs) -> bool { self[..] == other[..] } · line 671
Quoted 13 lines
rust/kernel/alloc/kvec.rs +13
-0+/// [`IntoIterator`] trait). +/// +/// # Examples +/// +/// ``` +/// let v = kernel::kvec![0, 1, 2]?; +/// let iter = v.into_iter(); +/// +/// # Ok::<(), Error>(()) +/// ``` +pub struct IntoIter <T ,A :Allocator > { + ptr: *mut T , + buf:NonNull <T >,No invariants for these two fields?
Suggestions?
Quoted 29 lines
Quoted 29 lines
rust/kernel/alloc/kvec.rs +28
-0+ len: usize, + cap: usize, + _p: PhantomData<A>, +} + +impl <T ,A >IntoIter <T ,A > +where +A :Allocator , +{ +fn as_raw_mut_slice (&mut self) -> *mut [T ] { + ptr::slice_from_raw_parts_mut (self.ptr, self.len) + } +} + +impl <T ,A >Iterator for IntoIter <T ,A > +where +A :Allocator , +{ +type Item =T ; + +/// # Examples +/// +/// ``` +/// let v = kernel::kvec![1, 2, 3]?; +/// let mut it = v.into_iter(); +/// +/// assert_eq!(it.next(), Some(1)); +/// assert_eq!(it.next(), Some(2));
Quoted 8 lines
rust/kernel/alloc/kvec.rs fn eq(&self, other: &$rhs) -> bool { self[..] == other[..] } · line 704
Quoted 5 lines
rust/kernel/alloc/kvec.rs +5
-0+/// assert_eq!(it.next(), Some(3)); +/// assert_eq!(it.next(), None); +/// +/// # Ok::<(), Error>(()) +/// ``` AFAIK documentation on functions in trait implementations won't show up in rustdoc (I just checked this). So I would remove it.
They don't, but the KUnit tests are still executed. :)
Quoted 98 lines
rust/kernel/alloc/kvec.rs fn eq(&self, other: &$rhs) -> bool { self[..] == other[..] } · line 716
Quoted 13 lines
rust/kernel/alloc/kvec.rs +12
-0+fn next (&mut self) ->Option <T > { +if self.len ==0 { +return None ; + } + +let ptr = self.ptr; +if !Vec ::<T ,A >::is_zst () { +// SAFETY: We can't overflow; `end` is guaranteed to mark the end of the buffer. +unsafe { self.ptr = self.ptr.add (1 ) }; + }else { +// For ZST `ptr` has to stay where it is to remain aligned, so we just reduce `self.len` +// by 1. Note that
<*mut T>::addadvances the pointer bysize_of::<T>()bytes. So in the case thatTis a ZST, it won't be advanced. So you could remove thisif.Quoted 27 lines
rust/kernel/alloc/kvec.rs +27
-0+ } + self.len -=1 ; + +// SAFETY: `ptr` is guaranteed to point at a valid element within the buffer. +Some (unsafe { ptr.read () }) + } + +/// # Examples +/// +/// ``` +/// let v: KVec<u32> = kernel::kvec![1, 2, 3]?; +/// let mut iter = v.into_iter(); +/// let size = iter.size_hint().0; +/// +/// iter.next(); +/// assert_eq!(iter.size_hint().0, size - 1); +/// +/// iter.next(); +/// assert_eq!(iter.size_hint().0, size - 2); +/// +/// iter.next(); +/// assert_eq!(iter.size_hint().0, size - 3); +/// +/// # Ok::<(), Error>(()) +/// ``` +fn size_hint (&self) -> (usize ,Option <usize >) { + (self.len,Some (self.len))rust/kernel/alloc/kvec.rs fn eq(&self, other: &$rhs) -> bool { self[..] == other[..] } · line 753
Quoted 10 lines
rust/kernel/alloc/kvec.rs +10
-0+ } +} + +impl <T ,A >Drop for IntoIter <T ,A > +where +A :Allocator , +{ +fn drop (&mut self) { +// SAFETY: Drop the remaining vector's elements in place, before we free the backing +// memory. This comment explains why you are doing it, not why it's ok to do it.
Quoted 40 lines
rust/kernel/alloc/kvec.rs +40
-0+unsafe { ptr::drop_in_place (self.as_raw_mut_slice ()) }; + +// If `cap == 0` we never allocated any memory in the first place. +if self.cap !=0 { +// SAFETY: `self.buf` was previously allocated with `A`. +unsafe {A ::free (self.buf.cast ()) }; + } + } +} + +impl <T ,A >IntoIterator for Vec <T ,A > +where +A :Allocator , +{ +type Item =T ; +type IntoIter =IntoIter <T ,A >; + +/// Consumes the `Vec<T, A>` and creates an `Iterator`, which moves each value out of the +/// vector (from start to end). +/// +/// # Examples +/// +/// ``` +/// let v = kernel::kvec![1, 2]?; +/// let mut v_iter = v.into_iter(); +/// +/// let first_element: Option<u32> = v_iter.next(); +/// +/// assert_eq!(first_element, Some(1)); +/// assert_eq!(v_iter.next(), Some(2)); +/// assert_eq!(v_iter.next(), None); +/// +/// # Ok::<(), Error>(()) +/// ``` +/// +/// ``` +/// let v = kernel::kvec![]; +/// let mut v_iter = v.into_iter(); +/// +/// let first_element: Option<u32> = v_iter.next();
Quoted 8 lines
rust/kernel/alloc/kvec.rs fn eq(&self, other: &$rhs) -> bool { self[..] == other[..] } · line 798
Quoted 5 lines
rust/kernel/alloc/kvec.rs +5
-0+/// +/// assert_eq!(first_element, None); +/// +/// # Ok::<(), Error>(()) +/// ``` I feel a bit bad that you wrote all of this nice documentation for functions that receive their documentation from the trait...
No worries, I really only added them for the KUnit tests.
Quoted 24 lines
---
Cheers,
BennoQuoted 18 lines
rust/kernel/alloc/kvec.rs +15
-1+#[inline] +fn into_iter (self) ->Self ::IntoIter { +let (ptr, len, cap) = self.into_raw_parts (); + +IntoIter { + ptr, +// SAFETY: `ptr` is either a dangling pointer or a pointer to a valid memory +// allocation, allocated with `A`. + buf:unsafe {NonNull ::new_unchecked (ptr) }, + len, + cap, + _p:PhantomData ::<A >, + } + } +} --2.46.0
Re: [PATCH v6 14/26] rust: alloc: implement `IntoIterator` for `Vec`
Quoted 18 lines · Danilo Krummrich
Quoted 16 lines · Benno Lossin
rust/kernel/alloc/kvec.rs fn eq(&self, other: &$rhs) -> bool { self[..] == other[..] } · line 671
Quoted 13 lines · Danilo Krummrich
rust/kernel/alloc/kvec.rs +13
-0+/// [`IntoIterator`] trait). +/// +/// # Examples +/// +/// ``` +/// let v = kernel::kvec![0, 1, 2]?; +/// let iter = v.into_iter(); +/// +/// # Ok::<(), Error>(()) +/// ``` +pub struct IntoIter <T ,A :Allocator > { + ptr: *mut T , + buf:NonNull <T >,No invariants for these two fields?
Quoted 1 line
Suggestions?
When determining the invariants, I look at the places where you would want to use them, ie the SAFETY comments that use these fields:
- for buf you only use it to free the backing allocation, so you only need that it has been allocated by A if cap != 0.
- for ptr you need that it is valid for reads for size_of::<T>() * length bytes.
So I would put those two things into invariants.
Quoted 37 lines
Quoted 28 lines
Quoted 28 lines
rust/kernel/alloc/kvec.rs +28
-0+ len: usize, + cap: usize, + _p: PhantomData<A>, +} + +impl <T ,A >IntoIter <T ,A > +where +A :Allocator , +{ +fn as_raw_mut_slice (&mut self) -> *mut [T ] { + ptr::slice_from_raw_parts_mut (self.ptr, self.len) + } +} + +impl <T ,A >Iterator for IntoIter <T ,A > +where +A :Allocator , +{ +type Item =T ; + +/// # Examples +/// +/// ``` +/// let v = kernel::kvec![1, 2, 3]?; +/// let mut it = v.into_iter(); +/// +/// assert_eq!(it.next(), Some(1)); +/// assert_eq!(it.next(), Some(2)); Quoted 8 lines
rust/kernel/alloc/kvec.rs fn eq(&self, other: &$rhs) -> bool { self[..] == other[..] } · line 704
Quoted 5 lines
rust/kernel/alloc/kvec.rs +5
-0+/// assert_eq!(it.next(), Some(3)); +/// assert_eq!(it.next(), None); +/// +/// # Ok::<(), Error>(()) +/// ``` AFAIK documentation on functions in trait implementations won't show up in rustdoc (I just checked this). So I would remove it.
Quoted 1 line
They don't, but the KUnit tests are still executed. :)
Oh I see, then may I suggest moving them to the module documentation or put them onto Vec, that way people can also read them :)
---
Cheers,
Benno
Danilo Krummrich
maintainerRe: [PATCH v6 14/26] rust: alloc: implement `IntoIterator` for `Vec`
Quoted 67 lines · Benno Lossin
Quoted 18 lines · Danilo Krummrich
Quoted 16 lines · Benno Lossin
rust/kernel/alloc/kvec.rs fn eq(&self, other: &$rhs) -> bool { self[..] == other[..] } · line 671
Quoted 13 lines · Danilo Krummrich
rust/kernel/alloc/kvec.rs +13
-0+/// [`IntoIterator`] trait). +/// +/// # Examples +/// +/// ``` +/// let v = kernel::kvec![0, 1, 2]?; +/// let iter = v.into_iter(); +/// +/// # Ok::<(), Error>(()) +/// ``` +pub struct IntoIter <T ,A :Allocator > { + ptr: *mut T , + buf:NonNull <T >,No invariants for these two fields?
Quoted 1 line
Suggestions?
When determining the invariants, I look at the places where you would want to use them, ie the
SAFETYcomments that use these fields:
- forbufyou only use it to free the backing allocation, so you only need that it has been allocated byAifcap != 0.
- forptryou need that it is valid for reads forsize_of::<T>() * lengthbytes.So I would put those two things into invariants.
Quoted 37 lines
Quoted 36 lines
Quoted 23 lines
rust/kernel/alloc/kvec.rs +23
-0+ len: usize, + cap: usize, + _p: PhantomData<A>, +} + +impl <T ,A >IntoIter <T ,A > +where +A :Allocator , +{ +fn as_raw_mut_slice (&mut self) -> *mut [T ] { + ptr::slice_from_raw_parts_mut (self.ptr, self.len) + } +} + +impl <T ,A >Iterator for IntoIter <T ,A > +where +A :Allocator , +{ +type Item =T ; + +/// # Examples +/// +/// ``` rust/kernel/alloc/kvec.rs fn eq(&self, other: &$rhs) -> bool { self[..] == other[..] } · line 704
Quoted 10 lines
rust/kernel/alloc/kvec.rs +10
-0+/// let v = kernel::kvec![1, 2, 3]?; +/// let mut it = v.into_iter(); +/// +/// assert_eq!(it.next(), Some(1)); +/// assert_eq!(it.next(), Some(2)); +/// assert_eq!(it.next(), Some(3)); +/// assert_eq!(it.next(), None); +/// +/// # Ok::<(), Error>(()) +/// ``` AFAIK documentation on functions in trait implementations won't show up in rustdoc (I just checked this). So I would remove it.
Quoted 4 lines
Quoted 1 line
They don't, but the KUnit tests are still executed. :)
Oh I see, then may I suggest moving them to the module documentation or put them onto
Vec, that way people can also read them :)
Hm, I'd rather keep them close on the functions they're testing. Those examples probably don't have a huge documentation purpose. As you've said, the trait is documented already and has examples.
Quoted 6 lines
---
Cheers,
Benno
Danilo Krummrich
maintainerPATCH v6 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
@@ -681,6 +681,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.46.0
Re: [PATCH v6 15/26] rust: alloc: implement `collect` for `IntoIter`
Quoted 47 lines · Danilo Krummrich
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::IntoIterequalsVec'sIntoItertype.
- We also can't useI::IntoIter's type ID either to work around this, sinceFromIteratordoesn't require this type to be'static.
-FromIterator::from_iterdoes returnSelfinstead ofResult<Self, AllocError>, hence we can't properly handle allocation failures.
- NeitherIterator::collectnorFromIterator::from_itercan handle additional allocation flags.Instead, provide
IntoIter::collect, such that we can at least convertIntoIterinto aVecagain.Reviewed-by Alice Ryhl <[email protected]>Signed-off-by Danilo Krummrich <[email protected]>--- rust/kernel/alloc/kvec.rs | 78 +++++++++++++++++++++++++++++++++++++++ 1 file changed, 78 insertions(+)rust/kernel/alloc/kvec.rs +16
-0@@ -681,6 +681,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
rust/kernel/alloc/kvec.rs impl<T, A> IntoIter<T, A> · line 709
Quoted 10 lines
rust/kernel/alloc/kvec.rs +10
-0+/// 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.
I think it's great that you include this in the code, but I don't think that it should be visible in the documentation, can you move it under the Examples section and turn it into normal comments?
Quoted 13 lines
rust/kernel/alloc/kvec.rs +13
-0+/// +/// 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);
rust/kernel/alloc/kvec.rs impl<T, A> IntoIter<T, A> · line 732
Quoted 10 lines
rust/kernel/alloc/kvec.rs +10
-0+/// 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.
This first sentence should not be part of the SAFETY comment.
rust/kernel/alloc/kvec.rs impl<T, A> IntoIter<T, A> · line 734
Quoted 2 lines
rust/kernel/alloc/kvec.rs +2
-0+// `ptr` is guaranteed to be between `buf` and `buf.add(cap)` and `ptr.add(len)` is +// guaranteed to be smaller than `buf.add(cap)`.
This doesn't justify all the requirements documented in [1].
Quoted 15 lines
rust/kernel/alloc/kvec.rs +15
-0+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) => {
rust/kernel/alloc/kvec.rs impl<T, A> IntoIter<T, A> · line 759
Quoted 10 lines
rust/kernel/alloc/kvec.rs +10
-0+ 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`.
Turn this into bullet points please.
---
Cheers,
Benno
Quoted 8 lines
rust/kernel/alloc/kvec.rs +2
-1+unsafe {Vec ::from_raw_parts (ptr, len, cap) } + } }impl <T ,A >Iterator for IntoIter <T ,A > --2.46.0
Danilo Krummrich
maintainerRe: [PATCH v6 15/26] rust: alloc: implement `collect` for `IntoIter`
Quoted 53 lines · Benno Lossin
Quoted 52 lines · Danilo Krummrich
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::IntoIterequalsVec'sIntoItertype.
- We also can't useI::IntoIter's type ID either to work around this, sinceFromIteratordoesn't require this type to be'static.
-FromIterator::from_iterdoes returnSelfinstead ofResult<Self, AllocError>, hence we can't properly handle allocation failures.
- NeitherIterator::collectnorFromIterator::from_itercan handle additional allocation flags.Instead, provide
IntoIter::collect, such that we can at least convertIntoIterinto aVecagain.Reviewed-by Alice Ryhl <[email protected]>Signed-off-by Danilo Krummrich <[email protected]>--- rust/kernel/alloc/kvec.rs | 78 +++++++++++++++++++++++++++++++++++++++ 1 file changed, 78 insertions(+)rust/kernel/alloc/kvec.rs +21
-0@@ -681,6 +681,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.
Quoted 8 lines
rust/kernel/alloc/kvec.rs impl<T, A> IntoIter<T, A> · line 709
Quoted 5 lines
rust/kernel/alloc/kvec.rs +5
-0+/// - 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. I think it's great that you include this in the code, but I don't think that it should be visible in the documentation,
Why not? I think this information is valuable for users of this API.
Quoted 77 lines
can you move it under
theExamplessection and turn it into normal comments?Quoted 13 lines
rust/kernel/alloc/kvec.rs +13
-0+/// +/// 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); rust/kernel/alloc/kvec.rs impl<T, A> IntoIter<T, A> · line 732
Quoted 10 lines
rust/kernel/alloc/kvec.rs +10
-0+/// 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. This first sentence should not be part of the SAFETY comment.
rust/kernel/alloc/kvec.rs impl<T, A> IntoIter<T, A> · line 734
Quoted 2 lines
rust/kernel/alloc/kvec.rs +2
-0+// `ptr` is guaranteed to be between `buf` and `buf.add(cap)` and `ptr.add(len)` is +// guaranteed to be smaller than `buf.add(cap)`. This doesn't justify all the requirements documented in [1].
Quoted 15 lines
rust/kernel/alloc/kvec.rs +15
-0+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) => {rust/kernel/alloc/kvec.rs impl<T, A> IntoIter<T, A> · line 759
Quoted 10 lines
rust/kernel/alloc/kvec.rs +10
-0+ 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`. Turn this into bullet points please.
---
Cheers,
BennoQuoted 8 lines
rust/kernel/alloc/kvec.rs +2
-1+unsafe {Vec ::from_raw_parts (ptr, len, cap) } + } }impl <T ,A >Iterator for IntoIter <T ,A > --2.46.0
Re: [PATCH v6 15/26] rust: alloc: implement `collect` for `IntoIter`
Quoted 19 lines · Danilo Krummrich
Quoted 18 lines · Benno Lossin
Quoted 16 lines · Danilo Krummrich
rust/kernel/alloc/kvec.rs +16
-0+/// 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.
Quoted 4 lines
Quoted 2 lines
I think it's great that you include this in the code, but I don't think that it should be visible in the documentation,
Why not? I think this information is valuable for users of this API.
If you want to keep it, then I don't mind, but I would still move it underneath Examples and add a section header # Implementation Details or similar.
---
Cheers,
Benno
Quoted 21 lines
Quoted 21 lines
can you move it under
theExamplessection and turn it into normal comments?Quoted 18 lines
rust/kernel/alloc/kvec.rs +18
-0+/// +/// 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 > {
Danilo Krummrich
maintainerRe: [PATCH v6 15/26] rust: alloc: implement `collect` for `IntoIter`
Quoted 20 lines · Benno Lossin
Quoted 19 lines · Danilo Krummrich
Quoted 18 lines · Benno Lossin
Quoted 16 lines · Danilo Krummrich
rust/kernel/alloc/kvec.rs +16
-0+/// 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.
Quoted 8 lines
Quoted 4 lines
Quoted 2 lines
I think it's great that you include this in the code, but I don't think that it should be visible in the documentation,
Why not? I think this information is valuable for users of this API.
If you want to keep it, then I don't mind, but I would still move it underneath
Examplesand add a section header# Implementation Detailsor similar.
Sure, we can do that.
Quoted 27 lines
---
Cheers,
BennoQuoted 21 lines
Quoted 21 lines
can you move it under
theExamplessection and turn it into normal comments?Quoted 18 lines
rust/kernel/alloc/kvec.rs +18
-0+/// +/// 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 > {
Danilo Krummrich
maintainerPATCH v6 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
@@ -172,7 +172,7 @@ unsafe fn borrow_mut<'a>(_: *const core::ffi::c_void) -> Self::BorrowedMut<'a> {/// # 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.46.0
Re: [PATCH v6 16/26] rust: treewide: switch to the kernel `Vec` type
Quoted 5 lines · Danilo Krummrich
Now that we got the kernel
Vecin place, convert all existingVecusers to make use of it.Reviewed-by Alice Ryhl <[email protected]>Signed-off-by Danilo Krummrich <[email protected]>
---
Cheers,
Benno
Quoted 8 lines
--- 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(-)
Danilo Krummrich
maintainerPATCH v6 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.46.0
Danilo Krummrich
maintainerPATCH v6 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.46.0
Danilo Krummrich
maintainerPATCH v6 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.46.0
Danilo Krummrich
maintainerPATCH v6 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.46.0
Re: [PATCH v6 20/26] rust: error: check for config `test` in `Error::name`
Quoted 6 lines · Danilo Krummrich
Additional to
testlibalso check fortestinError::name. This is required by a subsequent patch that (indirectly) usesErrorin test cases.Reviewed-by Alice Ryhl <[email protected]>Signed-off-by Danilo Krummrich <[email protected]>
---
Cheers,
Benno
Quoted 29 lines
--- rust/kernel/error.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-)rust/kernel/error.rs +2
-3@@ -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 } --2.46.0
Danilo Krummrich
maintainerPATCH v6 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.46.0
Re: [PATCH v6 21/26] rust: alloc: implement `contains` for `Flags`
Quoted 8 lines · Danilo Krummrich
Provide a simple helper function to check whether given flags do contain one or multiple other flags.
This is used by a subsequent patch implementing the Cmalloc
Allocatorto check for__GFP_ZERO.Reviewed-by Alice Ryhl <[email protected]>Signed-off-by Danilo Krummrich <[email protected]>
---
Cheers,
Benno
Quoted 32 lines
--- rust/kernel/alloc.rs | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-)rust/kernel/alloc.rs +6
-2@@ -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 { --2.46.0
Danilo Krummrich
maintainerPATCH v6 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.
--- rust/kernel/alloc/allocator_test.rs | 178 ++++++++++++++++++++++++++-- 1 file changed, 171 insertions(+), 7 deletions(-)
rust/kernel/alloc/allocator_test.rs +171 -7
@@ -2,20 +2,184 @@#![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), + ) + }; + +// SAFETY: `src` has been created by `Self::alloc_store_data`. +unsafe {Self ::free_read_data (src) }; + +Ok (dst) } }
Signature · 1 line
2.46.0
Re: [PATCH v6 22/26] rust: alloc: implement `Cmalloc` in module allocator_test
Quoted 19 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]>--- rust/kernel/alloc/allocator_test.rs | 178 ++++++++++++++++++++++++++-- 1 file changed, 171 insertions(+), 7 deletions(-)rust/kernel/alloc/allocator_test.rs +0
-0
rust/kernel/alloc/allocator_test.rs line 2
Quoted 1 line
rust/kernel/alloc/allocator_test.rs +0
-0@@ -2,20 +2,184 @@
Could add a short paragraph as the module description why this module exists? Would probably be enough to paste the commit message.
Quoted 31 lines
rust/kernel/alloc/allocator_test.rs +20
-3#![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 ,
rust/kernel/alloc/allocator_test.rs line 40
Quoted 10 lines
rust/kernel/alloc/allocator_test.rs +10
-0+} + +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.
This looks like you want it rendered as bulletpoints (but it won't).
rust/kernel/alloc/allocator_test.rs line 44
Quoted 4 lines
rust/kernel/alloc/allocator_test.rs +4
-0+/// +/// 0 16 32 96 +/// |----------------|----------------|------------------------------------------------| +/// empty CmallocData data
Can you put this inside of '```'? Then it will render nicely in markdown (don't forget to specify the type 'text')
Quoted 26 lines
rust/kernel/alloc/allocator_test.rs +26
-0+/// +/// 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 ;
rust/kernel/alloc/allocator_test.rs line 80
Quoted 10 lines
rust/kernel/alloc/allocator_test.rs +10
-0+ +// 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.
This SAFETY comment should address why it's OK to call add. You justify something different, namely why the allocation still satisfies the requirements of layout. That is something that this function should probably guarantee.
Quoted 11 lines
rust/kernel/alloc/allocator_test.rs +11
-0+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, + }; +
rust/kernel/alloc/allocator_test.rs line 101
Quoted 10 lines
rust/kernel/alloc/allocator_test.rs +10
-0+// 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`.
You additionally need that you have shared access to the pointee.
Quoted 6 lines
rust/kernel/alloc/allocator_test.rs +6
-0+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 (); +
rust/kernel/alloc/allocator_test.rs line 117
Quoted 10 lines
rust/kernel/alloc/allocator_test.rs +10
-0+// 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`.
You additionally need that you have exclusive access to the pointee.
Quoted 15 lines
rust/kernel/alloc/allocator_test.rs +15
-0+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 {
rust/kernel/alloc/allocator_test.rs line 142
Quoted 10 lines
rust/kernel/alloc/allocator_test.rs +10
-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 ()) }; + }
This makes me wonder, what other flags should we handle for this allocator?
Quoted 5 lines
rust/kernel/alloc/allocator_test.rs +4
-0+ +Ok (NonNull ::slice_from_raw_parts (ptr, layout.size ())) + } + unsafe fn realloc(
rust/kernel/alloc/allocator_test.rs line 153
Quoted 10 lines
rust/kernel/alloc/allocator_test.rs +5
-4- _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 ()
Why the cast?
rust/kernel/alloc/allocator_test.rs line 156
Quoted 3 lines
rust/kernel/alloc/allocator_test.rs +3
-0+ }else { +return Self ::alloc (layout, flags); + };
You should be able to write this instead:
let Some(src) = ptr else {
return Self::alloc(layout, flags);
};rust/kernel/alloc/allocator_test.rs line 159
Quoted 3 lines
rust/kernel/alloc/allocator_test.rs +3
-0+ +if layout.size () ==0 { +// SAFETY: `src` has been created by `Self::alloc_store_data`.
This is not true, consider:
let ptr = alloc(size = 0);
free(ptr)Alloc will return a dangling pointer due to the first if statement and then this function will pass it to free_read_data, even though it wasn't created by alloc_store_data.
This isn't forbidden by the Allocator trait function's safety requirements.
rust/kernel/alloc/allocator_test.rs line 168
Quoted 9 lines
rust/kernel/alloc/allocator_test.rs +9
-0+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) };
Same issue here, if the allocation passed in is zero size. I think you have no other choice than to allocate even for zero size requests... Otherwise how would you know that they are zero-sized.
---
Cheers,
Benno
Quoted 20 lines
rust/kernel/alloc/allocator_test.rs +15
-1+ +// SAFETY: `src` has previously been allocated with this `Allocator`; `dst` has just been +// newly allocated. 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), + ) + }; + +// SAFETY: `src` has been created by `Self::alloc_store_data`. +unsafe {Self ::free_read_data (src) }; + +Ok (dst) } } --2.46.0
Danilo Krummrich
maintainerRe: [PATCH v6 22/26] rust: alloc: implement `Cmalloc` in module allocator_test
Quoted 15 lines · Benno Lossin
Quoted 14 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]>--- rust/kernel/alloc/allocator_test.rs | 178 ++++++++++++++++++++++++++-- 1 file changed, 171 insertions(+), 7 deletions(-)
Quoted 9 lines
rust/kernel/alloc/allocator_test.rs line 2
Quoted 6 lines
rust/kernel/alloc/allocator_test.rs +0
-0@@ -2,20 +2,184 @@Could add a short paragraph as the module description why this module exists? Would probably be enough to paste the commit message.
Yes, sounds good.
Quoted 36 lines
Quoted 36 lines
rust/kernel/alloc/allocator_test.rs +24
-3#![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
Quoted 8 lines
rust/kernel/alloc/allocator_test.rs line 40
Quoted 6 lines
rust/kernel/alloc/allocator_test.rs +6
-0+/// 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. This looks like you want it rendered as bulletpoints (but it won't).
Actually, that wasn't my intention, but I'm fine changing that.
Quoted 8 lines
rust/kernel/alloc/allocator_test.rs line 44
Quoted 5 lines
rust/kernel/alloc/allocator_test.rs +4
-0+/// +/// 0 16 32 96 +/// |----------------|----------------|------------------------------------------------| +/// empty CmallocData data Can you put this inside of '```'? Then it will render nicely in markdown (don't forget to specify the type 'text')
Sure.
Quoted 34 lines
Quoted 34 lines
rust/kernel/alloc/allocator_test.rs +33
-0+/// +/// 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 )?; +
Quoted 8 lines
rust/kernel/alloc/allocator_test.rs line 80
Quoted 3 lines
rust/kernel/alloc/allocator_test.rs +3
-0+// 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. This SAFETY comment should address why it's OK to call
add. You justify something different, namely why the allocation still satisfies the requirements oflayout. That is something that this function should probably guarantee.
So, I guess you're arguing that instead I should say that, we're still within the bounds of the same allocated object and don't exceed isize?
Quoted 64 lines
Quoted 12 lines
rust/kernel/alloc/allocator_test.rs +11
-0+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, + }; +rust/kernel/alloc/allocator_test.rs line 101
Quoted 10 lines
rust/kernel/alloc/allocator_test.rs +10
-0+// 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`. You additionally need that you have shared access to the pointee.
Quoted 6 lines
rust/kernel/alloc/allocator_test.rs +6
-0+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 (); +rust/kernel/alloc/allocator_test.rs line 117
Quoted 10 lines
rust/kernel/alloc/allocator_test.rs +10
-0+// 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`. You additionally need that you have exclusive access to the pointee.
Quoted 20 lines
rust/kernel/alloc/allocator_test.rs +20
-0+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)?; +
Quoted 8 lines
rust/kernel/alloc/allocator_test.rs line 142
Quoted 5 lines
rust/kernel/alloc/allocator_test.rs +5
-0+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 ()) }; + }This makes me wonder, what other flags should we handle for this allocator?
I don't think there are any other flags that we can handle. The only other one that'd make sense is __GFP_NOFAIL, but we can't guarantee that.
If any specific gfp flags are needed, I think it's simply not a candidate for a userspace test.
If we really want to do something here, we could whitelist the flags we ignore, since they do not matter (such as __GFP_NOWARN) and panic() for everything else.
But I don't think that's really needed.
Quoted 10 lines
Quoted 10 lines
rust/kernel/alloc/allocator_test.rs +5
-3+ +Ok (NonNull ::slice_from_raw_parts (ptr, layout.size ())) + } +unsafe fn realloc ( - _ptr:Option <NonNull <u8 >>, - _layout:Layout , - _flags: Flags, + ptr: Option<NonNull<u8>>,
Quoted 8 lines
rust/kernel/alloc/allocator_test.rs line 153
Quoted 6 lines
rust/kernel/alloc/allocator_test.rs +4
-1+ layout: Layout, + flags: Flags, ) -> Result<NonNull<[u8]>, AllocError> { -panic! (); +let src:NonNull <u8 > =if let Some (src) = ptr { + src.cast ()Why the cast?
Probably a copy-paste mistake.
Quoted 10 lines
rust/kernel/alloc/allocator_test.rs line 156
Quoted 4 lines
rust/kernel/alloc/allocator_test.rs +3
-0+ }else { +return Self ::alloc (layout, flags); + };You should be able to write this instead:
let Some(src) = ptr else { return Self::alloc(layout, flags); };
Yes, indeed.
Quoted 16 lines
rust/kernel/alloc/allocator_test.rs line 159
Quoted 4 lines
rust/kernel/alloc/allocator_test.rs +3
-0+ +if layout.size () ==0 { +// SAFETY: `src` has been created by `Self::alloc_store_data`. This is not true, consider:
let ptr = alloc(size = 0); free(ptr)Alloc will return a dangling pointer due to the first if statement and then this function will pass it to
free_read_data, even though it wasn't created byalloc_store_data.
This isn't forbidden by theAllocatortrait function's safety requirements.
Quoted 13 lines
rust/kernel/alloc/allocator_test.rs line 168
Quoted 9 lines
rust/kernel/alloc/allocator_test.rs +9
-0+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) };Same issue here, if the allocation passed in is zero size. I think you have no other choice than to allocate even for zero size requests... Otherwise how would you know that they are zero-sized.
Good catch - gonna fix it.
Quoted 26 lines
---
Cheers,
BennoQuoted 20 lines
rust/kernel/alloc/allocator_test.rs +15
-1+ +// SAFETY: `src` has previously been allocated with this `Allocator`; `dst` has just been +// newly allocated. 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), + ) + }; + +// SAFETY: `src` has been created by `Self::alloc_store_data`. +unsafe {Self ::free_read_data (src) }; + +Ok (dst) } } --2.46.0
Re: [PATCH v6 22/26] rust: alloc: implement `Cmalloc` in module allocator_test
Quoted 11 lines · Danilo Krummrich
Quoted 10 lines · Benno Lossin
Quoted 8 lines · Danilo Krummrich
rust/kernel/alloc/allocator_test.rs +8
-0+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.
Quoted 3 lines
Quoted 1 line
This looks like you want it rendered as bulletpoints (but it won't).
Actually, that wasn't my intention, but I'm fine changing that.
I see, in that case not putting a newline there is also fine with me. But I think bulletpoints are probably easier to read.
Quoted 21 lines
Quoted 12 lines
Quoted 12 lines
rust/kernel/alloc/allocator_test.rs +12
-0+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 )?; +Quoted 8 lines
rust/kernel/alloc/allocator_test.rs line 80
Quoted 3 lines
rust/kernel/alloc/allocator_test.rs +3
-0+// 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. This SAFETY comment should address why it's OK to call
add. You justify something different, namely why the allocation still satisfies the requirements oflayout. That is something that this function should probably guarantee.
Quoted 2 lines
So, I guess you're arguing that instead I should say that, we're still within the bounds of the same allocated object and don't exceed
isize?
Yes.
Quoted 32 lines
Quoted 20 lines
Quoted 20 lines
rust/kernel/alloc/allocator_test.rs +20
-0+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)?; +Quoted 8 lines
rust/kernel/alloc/allocator_test.rs line 142
Quoted 5 lines
rust/kernel/alloc/allocator_test.rs +5
-0+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 ()) }; + }This makes me wonder, what other flags should we handle for this allocator?
I don't think there are any other flags that we can handle. The only other one that'd make sense is
__GFP_NOFAIL, but we can't guarantee that.
Quoted 7 lines
If any specific gfp flags are needed, I think it's simply not a candidate for a userspace test.
If we really want to do something here, we could whitelist the flags we ignore, since they do not matter (such as
__GFP_NOWARN) andpanic()for everything else.But I don't think that's really needed.
Makes sense, just wanted to check that this has been accounted for.
---
Cheers,
Benno
Danilo Krummrich
maintainerRe: [PATCH v6 22/26] rust: alloc: implement `Cmalloc` in module allocator_test
In reply to Danilo KrummrichQuoted 32 lines · Danilo Krummrich
Quoted 17 lines · Benno Lossin
rust/kernel/alloc/allocator_test.rs line 159
Quoted 3 lines · Danilo Krummrich
rust/kernel/alloc/allocator_test.rs +3
-0+ +if layout.size () ==0 { +// SAFETY: `src` has been created by `Self::alloc_store_data`. This is not true, consider:
let ptr = alloc(size = 0); free(ptr)Alloc will return a dangling pointer due to the first if statement and then this function will pass it to
free_read_data, even though it wasn't created byalloc_store_data.
This isn't forbidden by theAllocatortrait function's safety requirements.Quoted 13 lines
rust/kernel/alloc/allocator_test.rs line 168
Quoted 9 lines
rust/kernel/alloc/allocator_test.rs +9
-0+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) };Same issue here, if the allocation passed in is zero size. I think you have no other choice than to allocate even for zero size requests... Otherwise how would you know that they are zero-sized.
Quoted 1 line
Good catch - gonna fix it.
Almost got me. :) I think the code is fine, callers are not allowed to pass pointers to realloc and free, which haven't been allocated with the same corresponding allocator or are dangling.
Quoted 27 lines
Quoted 27 lines
---
Cheers,
BennoQuoted 20 lines
rust/kernel/alloc/allocator_test.rs +15
-1+ +// SAFETY: `src` has previously been allocated with this `Allocator`; `dst` has just been +// newly allocated. 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), + ) + }; + +// SAFETY: `src` has been created by `Self::alloc_store_data`. +unsafe {Self ::free_read_data (src) }; + +Ok (dst) } } --2.46.0
Re: [PATCH v6 22/26] rust: alloc: implement `Cmalloc` in module allocator_test
Quoted 32 lines · Danilo Krummrich
Quoted 31 lines · Danilo Krummrich
Quoted 29 lines · Benno Lossin
rust/kernel/alloc/allocator_test.rs line 159
Quoted 3 lines · Danilo Krummrich
rust/kernel/alloc/allocator_test.rs +3
-0+ +if layout.size () ==0 { +// SAFETY: `src` has been created by `Self::alloc_store_data`. This is not true, consider:
let ptr = alloc(size = 0); free(ptr)Alloc will return a dangling pointer due to the first if statement and then this function will pass it to
free_read_data, even though it wasn't created byalloc_store_data.
This isn't forbidden by theAllocatortrait function's safety requirements.rust/kernel/alloc/allocator_test.rs line 168
Quoted 9 lines
rust/kernel/alloc/allocator_test.rs +9
-0+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) };Same issue here, if the allocation passed in is zero size. I think you have no other choice than to allocate even for zero size requests... Otherwise how would you know that they are zero-sized.
Quoted 5 lines
Quoted 1 line
Good catch - gonna fix it.
Almost got me. :) I think the code is fine, callers are not allowed to pass pointers to
reallocandfree, which haven't been allocated with the same corresponding allocator or are dangling.
But what about the example above (ie the alloc(size = 0) and then free)? I guess this all depends on how one interprets the term "existing, valid memory allocation". To me that describes anything an Allocator returns via alloc and realloc, including zero-sized allocations.
But if you argue that those are not valid allocations from that allocator, then that is not properly documented in the safety requirements of Allocator.
---
Cheers,
Benno
Danilo Krummrich
maintainerRe: [PATCH v6 22/26] rust: alloc: implement `Cmalloc` in module allocator_test
Quoted 33 lines · Benno Lossin
Quoted 32 lines · Danilo Krummrich
Quoted 31 lines · Danilo Krummrich
Quoted 29 lines · Benno Lossin
rust/kernel/alloc/allocator_test.rs line 159
Quoted 3 lines · Danilo Krummrich
rust/kernel/alloc/allocator_test.rs +3
-0+ +if layout.size () ==0 { +// SAFETY: `src` has been created by `Self::alloc_store_data`. This is not true, consider:
let ptr = alloc(size = 0); free(ptr)Alloc will return a dangling pointer due to the first if statement and then this function will pass it to
free_read_data, even though it wasn't created byalloc_store_data.
This isn't forbidden by theAllocatortrait function's safety requirements.rust/kernel/alloc/allocator_test.rs line 168
Quoted 9 lines
rust/kernel/alloc/allocator_test.rs +9
-0+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) };Same issue here, if the allocation passed in is zero size. I think you have no other choice than to allocate even for zero size requests... Otherwise how would you know that they are zero-sized.
Quoted 8 lines
Quoted 5 lines
Quoted 1 line
Good catch - gonna fix it.
Almost got me. :) I think the code is fine, callers are not allowed to pass pointers to
reallocandfree, which haven't been allocated with the same corresponding allocator or are dangling.But what about the example above (ie the
alloc(size = 0)and thenfree)?
This never has been valid for the Allocator trait. Look at Kmalloc, Vmalloc and KVmalloc, they don't allow this either.
We've discussed this already in previous versions of this series, where for this purpose, you asked for old_layout for free. Such that free can check if the size was zero and therefore return without doing anything.
Quoted 4 lines
I guess this all depends on how one interprets the term "existing, valid memory allocation". To me that describes anything an
Allocatorreturns viaallocandrealloc, including zero-sized allocations.
I argue that the dangling pointer returned for size == 0 does not point to any allocation in the sense of those allocators. It's just a dangling [u8] pointer.
Quoted 3 lines
But if you argue that those are not valid allocations from that allocator, then that is not properly documented in the safety requirements of
Allocator.
The safety requirements of Allocator where proposed by you and I thought they consider this aspect?
realloc has:
"If ptr == Some(p), then p must point to an existing and valid memory allocation created by this allocator."
free has:
"ptr must point to an existing and valid memory allocation created by this Allocator and must not be a dangling pointer."
We can add the part about the dangling pointer to realloc if you want.
Quoted 5 lines
---
Cheers,
Benno
Re: [PATCH v6 22/26] rust: alloc: implement `Cmalloc` in module allocator_test
Quoted 40 lines · Danilo Krummrich
Quoted 39 lines · Benno Lossin
Quoted 37 lines · Danilo Krummrich
Quoted 31 lines · Danilo Krummrich
Quoted 16 lines · Benno Lossin
rust/kernel/alloc/allocator_test.rs line 159
Quoted 3 lines · Danilo Krummrich
rust/kernel/alloc/allocator_test.rs +3
-0+ +if layout.size () ==0 { +// SAFETY: `src` has been created by `Self::alloc_store_data`. This is not true, consider:
let ptr = alloc(size = 0); free(ptr)Alloc will return a dangling pointer due to the first if statement and then this function will pass it to
free_read_data, even though it wasn't created byalloc_store_data.
This isn't forbidden by theAllocatortrait function's safety requirements.Quoted 13 lines
rust/kernel/alloc/allocator_test.rs line 168
Quoted 9 lines
rust/kernel/alloc/allocator_test.rs +9
-0+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) };Same issue here, if the allocation passed in is zero size. I think you have no other choice than to allocate even for zero size requests... Otherwise how would you know that they are zero-sized.
Quoted 1 line
Good catch - gonna fix it.
Almost got me. :) I think the code is fine, callers are not allowed to pass pointers to
reallocandfree, which haven't been allocated with the same corresponding allocator or are dangling.
Quoted 5 lines
Quoted 2 lines
But what about the example above (ie the
alloc(size = 0)and thenfree)?This never has been valid for the
Allocatortrait. Look atKmalloc,VmallocandKVmalloc, they don't allow this either.
That is true.
Quoted 3 lines
We've discussed this already in previous versions of this series, where for this purpose, you asked for
old_layoutforfree. Such thatfreecan check if thesizewas zero and therefore return without doing anything.
Yes, but that was only about the old_layout parameter (at least that's what I thought).
Quoted 8 lines
Quoted 4 lines
I guess this all depends on how one interprets the term "existing, valid memory allocation". To me that describes anything an
Allocatorreturns viaallocandrealloc, including zero-sized allocations.I argue that the dangling pointer returned for
size == 0does not point to any allocation in the sense of those allocators. It's just a dangling[u8]pointer.
Sure, but to me the concept of zero-sized allocations does exist.
Quoted 6 lines
Quoted 3 lines
But if you argue that those are not valid allocations from that allocator, then that is not properly documented in the safety requirements of
Allocator.The safety requirements of
Allocatorwhere proposed by you and I thought they consider this aspect?
No, they did not consider this aspect. I was under the impression, that we would still allow zero-sized allocations (in retrospect, this is stupid, since dangling pointers shouldn't be passed to krealloc etc.).
Quoted 11 lines
reallochas:"If
ptr == Some(p), thenpmust point to an existing and valid memory allocation created by this allocator."
freehas:"
ptrmust point to an existing and valid memory allocation created by thisAllocatorand must not be a dangling pointer."We can add the part about the dangling pointer to
reallocif you want.
So I think we should do the following:
(1) Add a paragraph to the Allocator trait that explains that zero-sized allocations are not supported.
(2) Add a check to realloc for zero-sized allocations + null pointer (ie a new allocation request) that prints a warning and returns an error
(3) Instead of writing "existing and valid memory allocation created by this allocator", I think "valid non-zero-sized memory allocation created by this allocator" fits better.
---
Cheers,
Benno
Danilo Krummrich
maintainerPATCH v6 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.46.0
Re: [PATCH v6 23/26] rust: str: test: replace `alloc::format`
Quoted 11 lines · Danilo Krummrich
The current implementation of tests in
str.rsuseformat!to format strings for comparison, which, internally, creates a newString.In order to prepare for getting rid of Rust's alloc crate, we have to cut this dependency. Instead, implement
format!forCString.Note that for userspace tests,
Kmalloc, which is backingCString's memory, is just a type alias toCmalloc.Reviewed-by Alice Ryhl <[email protected]>Signed-off-by Danilo Krummrich <[email protected]>
---
Cheers,
Benno
Quoted 41 lines
--- rust/kernel/str.rs | 23 ++++++++++++++++++++++- 1 file changed, 22 insertions(+), 1 deletion(-)rust/kernel/str.rs +22
-2@@ -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\ --2.46.0
Danilo Krummrich
maintainerPATCH v6 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.46.0
Danilo Krummrich
maintainerPATCH v6 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.46.0
Re: [PATCH v6 25/26] kbuild: rust: remove the `alloc` crate and `GlobalAlloc`
rust/kernel/alloc/allocator.rs unsafe fn realloc( · line 157
Quoted 10 lines · Danilo Krummrich
rust/kernel/alloc/allocator.rs +0
-3@@ -204,9 +152,6 @@ unsafe fn realloc( } } -#[global_allocator] -static ALLOCATOR :Kmalloc =Kmalloc ; -// See <https://github.com/rust-lang/rust/pull/86844>. #[no_mangle] static __rust_no_alloc_shim_is_unstable:u8 =0 ;
I think Bjorn already mentioned this, but you can also remove this static, since it was also something that alloc needed.
---
Cheers,
Benno
Danilo Krummrich
maintainerPATCH v6 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.46.0
Gary Guo
maintainerRe: [PATCH v6 26/26] MAINTAINERS: add entry for the Rust `alloc` module
Quoted 17 lines · On Fri, 16 Aug 2024 02:11:08 +0200 Danilo Krummrich <[email protected]> wrote:
Add maintainers entry for the Rust
allocmodule.Currently, this includes the
AllocatorAPI itself,Allocatorimplementations, such asKmallocorVmalloc, as well as the kernel's implementation of the primary memory allocation data structures,BoxandVec.Signed-off-by Danilo Krummrich <[email protected]>--- MAINTAINERS | 7 +++++++ 1 file changed, 7 insertions(+)MAINTAINERS +0
-0
MAINTAINERS F: scripts/*rust* · line 19933
Quoted 9 lines
MAINTAINERS +6
-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/
It feels like we should use mod.rs.
Quoted 4 lines
MAINTAINERS +1
-0+ RXRPC SOCKETS (AF_RXRPC) M: David Howells <[email protected]> M: Marc Dionne <[email protected]>
Danilo Krummrich
maintainerRe: [PATCH v6 26/26] MAINTAINERS: add entry for the Rust `alloc` module
Quoted 23 lines · Gary Guo
Quoted 20 lines · On Fri, 16 Aug 2024 02:11:08 +0200 Danilo Krummrich <[email protected]> wrote:
Add maintainers entry for the Rust
allocmodule.Currently, this includes the
AllocatorAPI itself,Allocatorimplementations, such asKmallocorVmalloc, as well as the kernel's implementation of the primary memory allocation data structures,BoxandVec.Signed-off-by Danilo Krummrich <[email protected]>--- MAINTAINERS | 7 +++++++ 1 file changed, 7 insertions(+)MAINTAINERS +0
-0@@ -19925,6 +19925,13 @@ F: scripts/*rust* F: tools/testing/selftests/rust/ K: \b(?i:rust)\b
Quoted 8 lines
MAINTAINERS F: scripts/*rust* · line 19933
Quoted 6 lines
MAINTAINERS +6
-0+RUST [ALLOC] +M: Danilo Krummrich <[email protected]> +L: [email protected] +S: Maintained +F: rust/kernel/alloc.rs +F: rust/kernel/alloc/It feels like we should use
mod.rs.
The same would be true for:
- rust/kernel/sync.rs
- rust/kernel/net.rs
- rust/kernel/init.rs
- rust/kernel/fs.rs
- ...
Do you propose to change it for all of them?
Quoted 6 lines
Quoted 5 lines
MAINTAINERS +1
-0+ RXRPC SOCKETS (AF_RXRPC) M: David Howells <[email protected]> M: Marc Dionne <[email protected]>
Alice Ryhl
maintainerRe: [PATCH v6 26/26] MAINTAINERS: add entry for the Rust `alloc` module
Quoted 36 lines · Danilo Krummrich
Quoted 23 lines · Gary Guo
Quoted 20 lines · On Fri, 16 Aug 2024 02:11:08 +0200 Danilo Krummrich <[email protected]> wrote:
Add maintainers entry for the Rust
allocmodule.Currently, this includes the
AllocatorAPI itself,Allocatorimplementations, such asKmallocorVmalloc, as well as the kernel's implementation of the primary memory allocation data structures,BoxandVec.Signed-off-by Danilo Krummrich <[email protected]>--- MAINTAINERS | 7 +++++++ 1 file changed, 7 insertions(+)MAINTAINERS +0
-0@@ -19925,6 +19925,13 @@ F: scripts/*rust* F: tools/testing/selftests/rust/ K: \b(?i:rust)\bQuoted 8 lines
MAINTAINERS F: scripts/*rust* · line 19933
Quoted 6 lines
MAINTAINERS +6
-0+RUST [ALLOC] +M: Danilo Krummrich <[email protected]> +L: [email protected] +S: Maintained +F: rust/kernel/alloc.rs +F: rust/kernel/alloc/It feels like we should use
mod.rs.The same would be true for:
Quoted 7 lines
-
rust/kernel/sync.rs
-rust/kernel/net.rs
-rust/kernel/init.rs
-rust/kernel/fs.rs
- ...Do you propose to change it for all of them?
I do actually think mod.rs is superior in general, but it's probably not worth changing it right now.
Alice
Re: [PATCH v6 26/26] MAINTAINERS: add entry for the Rust `alloc` module
Quoted 43 lines · Alice Ryhl
Quoted 42 lines · Danilo Krummrich
Quoted 23 lines · Gary Guo
Quoted 20 lines · On Fri, 16 Aug 2024 02:11:08 +0200 Danilo Krummrich <[email protected]> wrote:
Add maintainers entry for the Rust
allocmodule.Currently, this includes the
AllocatorAPI itself,Allocatorimplementations, such asKmallocorVmalloc, as well as the kernel's implementation of the primary memory allocation data structures,BoxandVec.Signed-off-by Danilo Krummrich <[email protected]>--- MAINTAINERS | 7 +++++++ 1 file changed, 7 insertions(+)MAINTAINERS +0
-0@@ -19925,6 +19925,13 @@ F: scripts/*rust* F: tools/testing/selftests/rust/ K: \b(?i:rust)\bQuoted 8 lines
MAINTAINERS F: scripts/*rust* · line 19933
Quoted 6 lines
MAINTAINERS +6
-0+RUST [ALLOC] +M: Danilo Krummrich <[email protected]> +L: [email protected] +S: Maintained +F: rust/kernel/alloc.rs +F: rust/kernel/alloc/It feels like we should use
mod.rs.The same would be true for:
-
rust/kernel/sync.rs
-rust/kernel/net.rs
-rust/kernel/init.rs
-rust/kernel/fs.rs
- ...
Quoted 4 lines
Quoted 1 line
Do you propose to change it for all of them?
I do actually think
mod.rsis superior in general, but it's probably not worth changing it right now.
I forgot the reason for not using it, does anyone remember?
---
Cheers,
Benno
Re: [PATCH v6 26/26] MAINTAINERS: add entry for the Rust `alloc` module
Quoted 2 lines · Benno Lossin
I forgot the reason for not using it, does anyone remember?
One of the reasons argued was that mod.rs is the same name everywhere, and thus it is hard to notice the difference in some editors and may be harder to "jump into" in history/tabs/etc.
Cheers,
Miguel
Re: [PATCH v6 26/26] MAINTAINERS: add entry for the Rust `alloc` module
Quoted 7 lines · Miguel Ojeda
Quoted 2 lines · Benno Lossin
I forgot the reason for not using it, does anyone remember?
One of the reasons argued was that
mod.rsis the same name everywhere, and thus it is hard to notice the difference in some editors and may be harder to "jump into" in history/tabs/etc.
I don't usually open more than 2-4 files anyways, so it's not an issue for me. But of course people's workflow is different, does anyone have a problem with switching to mod.rs?
I have another reason that it might be nicer, when I want to edit eg init.rs and I decide to directly open it with the editor from my shell, not using my fuzzy finder, then I need to press tab to select between rust/kernel/init and rust/kernel/init.rs.
---
Cheers,
Benno
Danilo Krummrich
maintainerRe: [PATCH v6 26/26] MAINTAINERS: add entry for the Rust `alloc` module
Quoted 5 lines · Benno Lossin
Quoted 4 lines · Miguel Ojeda
Quoted 2 lines · Benno Lossin
I forgot the reason for not using it, does anyone remember?
Quoted 7 lines
Quoted 3 lines
One of the reasons argued was that
mod.rsis the same name everywhere, and thus it is hard to notice the difference in some editors and may be harder to "jump into" in history/tabs/etc.I don't usually open more than 2-4 files anyways, so it's not an issue for me. But of course people's workflow is different, does anyone have a problem with switching to
mod.rs?
I'm also not against it. I'd appreciate if a potential change doesn't interfere with this series too much though. :-)
Quoted 10 lines
I have another reason that it might be nicer, when I want to edit eg
init.rsand I decide to directly open it with the editor from my shell, not using my fuzzy finder, then I need to press tab to select betweenrust/kernel/initandrust/kernel/init.rs.---
Cheers,
Benno
Re: [PATCH v6 26/26] MAINTAINERS: add entry for the Rust `alloc` module
Quoted 10 lines · Danilo Krummrich
Quoted 9 lines · Benno Lossin
Quoted 7 lines · Miguel Ojeda
Quoted 2 lines · Benno Lossin
I forgot the reason for not using it, does anyone remember?
One of the reasons argued was that
mod.rsis the same name everywhere, and thus it is hard to notice the difference in some editors and may be harder to "jump into" in history/tabs/etc.
Quoted 6 lines
Quoted 3 lines
I don't usually open more than 2-4 files anyways, so it's not an issue for me. But of course people's workflow is different, does anyone have a problem with switching to
mod.rs?I'm also not against it. I'd appreciate if a potential change doesn't interfere with this series too much though. :-)
Sure, we can do it after the series.
---
Cheers,
Benno
Quoted 41 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.
Quoted 6 lines
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
Finally managed to apply this on rust-dev ;-) A few things I resolved, JFYI:
* Resolve the conflicts between patch 6 & 7 and the helper split,
note that the vmalloc() helper temporarily lives in a file called slab.c.
* Adjust patch 9 with the introduction of InPlaceInit::PinnedSelf,
also drop the ForeignOwnable::borrow_mut() implemenation.
* Resolve the conflict between patch 10 and the latest rbtree
patchset (v12).
* Adjust patch 11 with the introduction of
InPlaceInit::PinnedSelf.
* Resolve the conflicts between patch 25 and the helper split.
(very likely I could make mistakes during these conflict resolution, just let me know)
I've put this on the rust-dev based on today's rust-next (+ your mm changes) for testing, will continue to review the series, thanks!
Regards,
Boqun
Quoted 148 lines
Changes in v6:
- rebase onto rust-dev
- keep compiler annotations for {k,v,kv}realloc()
- documentation changes suggested by Alice, Benno
- removeBox::into_pin
- fix typo inSendandSyncforBoxandVec
-kvec!changes suggested by Alice
- freesrcafter copy inCmalloc
- handlen == 0inVec::extend_withChanges 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
- improve usage ofsize_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.pyChanges in v4:
- (safety) comment fixes suggested by Alice and Boqun
- removeBox::from_raw_allocandBox::into_raw_alloc, we don't need them
- inBox::dropcallsize_of_valbeforedrop_in_place
- implement ForeignOwnable for Pin<Box<T>> as suggested by Alice
- inVec::extend_with, iterate overninstead ofspare.len()
- forVmallocandKVmallocfail allocation for alignments larger thanPAGE_SIZEfor now (will add support for larger alignments in a separate series)
- implementCmallocinallocator_testand type alias all kernel allocator types to it, such that we can use the kernel'sBoxandVectypes in userspace tests (rusttest)
- this makes patch "rust: str: test: replacealloc::format" rather trivialChanges in v3:
- Box:
- minor documentation fixes
- removed unnecessary imports in doc tests
- dropeedselfargument from some remainingBoxmethods
- implementInPlaceInitfor Box<T, A> rather than specifically forKBox<T>
- Vec:
- minor documentation fixes
- removed uselessVec::allocatormethod
- inVec::extend_withuseVec::spare_capacity_mutinstead of raw pointer operations
- added a few missing safety comments
- pass GFP flags toVec::collect
- fixed a rustdoc warning inalloc.rs
- fixed theallocator_testmodule to implement theAllocatortrait correctly
- rebased to rust-nextChanges in v2:
- preserveimpl GlobalAlloc for Kmallocand remove it at the end (Benno)
- remove&selfparameter from allAllocatorfunctions (Benno)
- various documentation fixes forAllocator(Benno)
- useNonNull<u8>forAllocator::freeandOption<NonNull<u8>>forAllocator::realloc(Benno)
- fix leak ofIntoIterinVec::collect(Boqun)
- always realloc (try to shrink) inVec::collect, it's up the theAllocatorto provide a heuristic whether it makes sense to actually shrink
- renameKBox<T, A>->Box<T, A>andKVec<T, A>->Vec<T, A>and provide type aliasesKBox<T>,VBox<T>,KVBox<T>, etc.
- This allows for much cleaner code and, in combination with removing&selfparameters fromAllocators, gets us rid of the need forBox::newandBox::new_allocand 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: addAllocatortrait
rust: alloc: separatealigned_sizefromkrealloc_aligned
rust: alloc: renameKernelAllocatortoKmalloc
rust: alloc: implementAllocatorforKmalloc
rust: alloc: add moduleallocator_test
rust: alloc: implementVmallocallocator
rust: alloc: implementKVmallocallocator
rust: alloc: add__GFP_NOWARNtoFlags
rust: alloc: implement kernelBox
rust: treewide: switch to our kernelBoxtype
rust: alloc: removeBoxExtextension
rust: alloc: addBoxto prelude
rust: alloc: implement kernelVectype
rust: alloc: implementIntoIteratorforVec
rust: alloc: implementcollectforIntoIter
rust: treewide: switch to the kernelVectype
rust: alloc: removeVecExtextension
rust: alloc: addVecto prelude
rust: error: usecore::alloc::LayoutError
rust: error: check for configtestinError::name
rust: alloc: implementcontainsforFlags
rust: alloc: implementCmallocin moduleallocator_test
rust: str: test: replacealloc::format
rust: alloc: update module comment ofalloc.rs
kbuild: rust: remove thealloccrate andGlobalAlloc
MAINTAINERS: add entry for the RustallocmoduleMAINTAINERS | 7 + drivers/block/rnull.rs | 4 +- rust/Makefile | 44 +- rust/bindings/bindings_helper.h | 1 + rust/exports.c | 1 - rust/helpers.c | 15 + rust/kernel/alloc.rs | 142 ++++- rust/kernel/alloc/allocator.rs | 173 ++++-- rust/kernel/alloc/allocator_test.rs | 185 ++++++ rust/kernel/alloc/box_ext.rs | 80 --- rust/kernel/alloc/kbox.rs | 480 +++++++++++++++ rust/kernel/alloc/kvec.rs | 891 ++++++++++++++++++++++++++++ rust/kernel/alloc/vec_ext.rs | 185 ------ rust/kernel/error.rs | 6 +- rust/kernel/init.rs | 93 +-- rust/kernel/init/__internal.rs | 2 +- rust/kernel/lib.rs | 1 - rust/kernel/prelude.rs | 5 +- rust/kernel/rbtree.rs | 34 +- 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 | 30 +- 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 +- 32 files changed, 1973 insertions(+), 539 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.rsbase-commit:
f005c686d6c1a2e66f2f9d21179d6b6bd45b20e2Signature · 1 line
2.46.0