Skip to contentSkip to conversation

Generic `Allocator` support for Rust

PATCH Superseded Danilo Krummrich 6 participants 100 messages Aug 16, 2024 – Sep 23, 2024

Subsystems (9)

v6 of 9 Superseded Latest is v9 →

lore
Share & tools
lore

Danilo Krummrich

author maintainer
#0 lore raw

v6 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
author maintainer
#1 lore raw

PATCH 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
index 1966bd407017..9932f21b0539 100644
@@ -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
#2 lore raw

Re: [PATCH v6 01/26] rust: alloc: add `Allocator` trait

Quoted 9 lines · Danilo Krummrich

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.

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

author maintainer
#3 lore raw

Re: [PATCH v6 01/26] rust: alloc: add `Allocator` trait

Quoted 8 lines · Benno Lossin
Quoted 7 lines · Danilo Krummrich

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.

Quoted 7 lines
Quoted 2 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_layout parameter, 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

#4 lore raw

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 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.

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_layout parameter, 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

author maintainer
#5 lore raw

Re: [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 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.

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_layout parameter, 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 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.)

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

#6 lore raw

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 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.

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_layout parameter, 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 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.)

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

author maintainer
#7 lore raw

Re: [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 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.

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_layout parameter, 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 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.)

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

#8 lore raw

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 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.

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_layout parameter, 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 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.)

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

#9 lore raw

Re: [PATCH v6 01/26] rust: alloc: add `Allocator` trait

In reply to Danilo Krummrich
Quoted 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

maintainer
#10 lore raw

Re: [PATCH v6 01/26] rust: alloc: add `Allocator` trait

In reply to Danilo Krummrich
Quoted 98 lines · On Fri, 16 Aug 2024 02:10:43 +0200 Danilo Krummrich <[email protected]> wrote:

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 +70 -0
index 1966bd407017..9932f21b0539 100644
@@ -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)) };
+    }
+}
author maintainer
#11 lore raw

PATCH v6 2/26 rust: alloc: separate `aligned_size` from `krealloc_aligned`

In reply to Danilo Krummrich

Separate 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
index e6ea601f38c6..c83b6dff896d 100644
@@ -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
maintainer Reviewed-by on v6 2/26
#12 lore raw

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_size from krealloc_aligned.

Subsequent patches implement Allocator derivates, such as Kmalloc, that require aligned_size and replace the original krealloc_aligned.

Quoted 41 lines
---
 rust/kernel/alloc/allocator.rs | 19 ++++++++++++-------
 1 file changed, 12 insertions(+), 7 deletions(-)
rust/kernel/alloc/allocator.rs +12 -7
index e6ea601f38c6..c83b6dff896d 100644
@@ -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
author maintainer
#13 lore raw

PATCH v6 3/26 rust: alloc: rename `KernelAllocator` to `Kmalloc`

In reply to Danilo Krummrich

Subsequent 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
index c83b6dff896d..e32182f91167 100644
@@ -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: F
     unsafe { bindings::krealloc(ptr as *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
author maintainer
#14 lore raw

PATCH v6 4/26 rust: alloc: implement `Allocator` for `Kmalloc`

In reply to Danilo Krummrich

Implement 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
index 9932f21b0539..477dbe3c5a2f 100644
@@ -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
#15 lore raw

Re: [PATCH v6 04/26] rust: alloc: implement `Allocator` for `Kmalloc`

Quoted 32 lines · Danilo Krummrich

Implement 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
index 9932f21b0539..477dbe3c5a2f 100644
@@ -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
index e32182f91167..78e7d5488843 100644

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 -0
 use 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: 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>>,

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

author maintainer
#16 lore raw

Re: [PATCH v6 04/26] rust: alloc: implement `Allocator` for `Kmalloc`

Quoted 96 lines · Benno Lossin
Quoted 32 lines · Danilo Krummrich

Implement 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
index 9932f21b0539..477dbe3c5a2f 100644
@@ -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
index e32182f91167..78e7d5488843 100644

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 -0
 use 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: 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

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,
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

#17 lore raw

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 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
index 9932f21b0539..477dbe3c5a2f 100644
@@ -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
index e32182f91167..78e7d5488843 100644

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 -0
 use 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: 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

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 Allocator trait 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

author maintainer
#18 lore raw

Re: [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 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
index 9932f21b0539..477dbe3c5a2f 100644
@@ -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
index e32182f91167..78e7d5488843 100644

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 -0
 use 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: 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

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:

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 Allocator trait 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 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.
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 ReallocFunc and the second one add the impl for Kmalloc? 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,
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

#19 lore raw

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: 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

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:

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 Allocator trait 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::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.

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, Kmalloc could implement its own Allocator::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::call to allocate and free memory.

Quoted 8 lines

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.
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 ReallocFunc and the second one add the impl for Kmalloc? 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

author maintainer
#20 lore raw

Re: [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: 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

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.
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 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.

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 Allocator trait 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 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::realloc memory allocated with Allocator::alloc would be fine too I guess.

Quoted 6 lines
Quoted 2 lines

But if e.g. Kmalloc wouldn't use the default Allocator::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?

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 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, Kmalloc could implement its own Allocator::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 39 lines

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 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.
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 ReallocFunc and the second one add the impl for Kmalloc? 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

#21 lore raw

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 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:

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 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.

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.0 is one of [...]
//
// SAFETY:
// - ptr is 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 ReallocFunc and the second one add the impl for Kmalloc? 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

author maintainer
#22 lore raw

PATCH v6 5/26 rust: alloc: add module `allocator_test`

In reply to Danilo Krummrich

Allocators, 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
index 477dbe3c5a2f..91a367a58f36 100644
@@ -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_test as allocator;
+
 /// Indicates an allocation error.
 #[derive(Copy, Clone, PartialEq, Eq, Debug)]
 pub struct AllocError;
rust/kernel/alloc/allocator_test.rs new file +19 -0
new file mode 100644
index 000000000000..4785efc474a7
@@ -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
maintainer Reviewed-by on v6 5/26
#23 lore raw

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 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.

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.rs
rust/kernel/alloc.rs +7 -2
index 477dbe3c5a2f..91a367a58f36 100644
@@ -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_test as allocator;
+
 /// Indicates an allocation error.
 #[derive(Copy, Clone, PartialEq, Eq, Debug)]
 pub struct AllocError;
rust/kernel/alloc/allocator_test.rs new file +19 -0
new file mode 100644
index 000000000000..4785efc474a7
@@ -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!();
+    }
+}
author maintainer
#24 lore raw

PATCH v6 6/26 rust: alloc: implement `Vmalloc` allocator

In reply to Danilo Krummrich

Implement 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
index 41df5b1a6de6..96f8b37a668b 100644
@@ -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
index 78e7d5488843..09a322e9168d 100644
@@ -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
index 4785efc474a7..e7bf2982f68f 100644
@@ -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
#25 lore raw

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
index 4785efc474a7..e7bf2982f68f 100644
@@ -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

author maintainer
#26 lore raw

PATCH v6 7/26 rust: alloc: implement `KVmalloc` allocator

In reply to Danilo Krummrich

Implement 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
index 96f8b37a668b..9a7604e3a749 100644
@@ -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
index 09a322e9168d..0183fbac1ada 100644
@@ -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
index e7bf2982f68f..1b2642c547ec 100644
@@ -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
author maintainer
#27 lore raw

PATCH v6 8/26 rust: alloc: add __GFP_NOWARN to `Flags`

In reply to Danilo Krummrich

Some 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
index b5793142904d..90493123bdf1 100644
@@ -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
index 91a367a58f36..53a93617a9f6 100644
@@ -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
author maintainer
#28 lore raw

PATCH v6 9/26 rust: alloc: implement kernel `Box`

In reply to Danilo Krummrich

Box provides the simplest way to allocate memory for a generic type with one of the kernel's allocators, e.g. Kmalloc, Vmalloc or KVmalloc.

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
index 53a93617a9f6..0c9bb60250af 100644
@@ -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_test as 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
index 4571daec0961..a9210634a8c3 100644
@@ -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
maintainer
#29 lore raw

Re: [PATCH v6 09/26] rust: alloc: implement kernel `Box`

Quoted 11 lines · Danilo Krummrich

Box provides the simplest way to allocate memory for a generic type with one of the kernel's allocators, e.g. Kmalloc, Vmalloc or KVmalloc.

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.

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> = &'a T;
+    type BorrowedMut<'a> = &'a mut T;
[..]
+    unsafe fn borrow_mut<'a>(ptr: *const core::ffi::c_void) -> &'a mut 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

[1]: https://lore.kernel.org/all/[email protected]/

author maintainer
#30 lore raw

Re: [PATCH v6 09/26] rust: alloc: implement kernel `Box`

Quoted 15 lines · Alice Ryhl
Quoted 11 lines · Danilo Krummrich

Box provides the simplest way to allocate memory for a generic type with one of the kernel's allocators, e.g. Kmalloc, Vmalloc or KVmalloc.

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.

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> = &'a T;
+    type BorrowedMut<'a> = &'a mut T;
[..]
+    unsafe fn borrow_mut<'a>(ptr: *const core::ffi::c_void) -> &'a mut 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

[1]: https://lore.kernel.org/all/[email protected]/

#31 lore raw

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

Box provides the simplest way to allocate memory for a generic type with one of the kernel's allocators, e.g. Kmalloc, Vmalloc or KVmalloc.

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.

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> = &'a T;
+    type BorrowedMut<'a> = &'a mut T;
[..]
+    unsafe fn borrow_mut<'a>(ptr: *const core::ffi::c_void) -> &'a mut 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

[1]: https://lore.kernel.org/all/[email protected]/

#32 lore raw

Re: [PATCH v6 09/26] rust: alloc: implement kernel `Box`

In reply to Danilo Krummrich
Quoted 16 lines · Danilo Krummrich
rust/kernel/alloc/kbox.rs new file +10 -0
new file mode 100644
index 000000000000..93b1ab9de6e8
@@ -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) -> &'a mut 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(raw as *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
index 4571daec0961..a9210634a8c3 100644
@@ -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

author maintainer
#33 lore raw

Re: [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
new file mode 100644
index 000000000000..93b1ab9de6e8
@@ -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 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 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 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]'".

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 Box references 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) -> &'a mut 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(raw as *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
index 4571daec0961..a9210634a8c3 100644
@@ -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

#34 lore raw

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 Kmalloc behavior.

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 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]'".

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>)
+    }
author maintainer
#35 lore raw

Re: [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 Kmalloc behavior.

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 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]'".

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: inside main at src/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,
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>)
+    }
#36 lore raw

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 Kmalloc behavior.

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 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]'".

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

[1] https://doc.rust-lang.org/std/ptr/index.html

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: inside main at src/main.rs:8:22: 8:30

Quoted 2 lines

read explicitly asks for non-null and properly aligned even if T has size zero.

Dereferencing (ie *t) also requires that (I just didn't do it, since then the Token must implement Copy).

---
Cheers,
Benno

author maintainer
#37 lore raw

Re: [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 Kmalloc behavior.

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 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]'".

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 3 lines
Quoted 1 line

[1] https://doc.rust-lang.org/std/ptr/index.html

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: inside main at src/main.rs:8:22: 8:30

Quoted 2 lines
Quoted 2 lines

read explicitly asks for non-null and properly aligned even if T has 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 the Token must implement Copy).

Again, how do you know? The documentation isn't clear about it.

Quoted 5 lines

---
Cheers,
Benno

#38 lore raw

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 VBox or KVBox:"

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.0 instead 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 6 lines
Quoted 3 lines
Quoted 1 line

[1] https://doc.rust-lang.org/std/ptr/index.html

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

maintainer
#39 lore raw

Re: [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 VBox or KVBox:"

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.0 instead 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 6 lines
Quoted 3 lines
Quoted 1 line

[1] https://doc.rust-lang.org/std/ptr/index.html

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

author maintainer
#40 lore raw

Re: [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 VBox or KVBox:"

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.0 instead 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 3 lines
Quoted 1 line

[1] https://doc.rust-lang.org/std/ptr/index.html

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_unaligned and write_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::dangling in 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

#41 lore raw

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 VBox or KVBox:"

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.0 instead 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 6 lines
Quoted 3 lines
Quoted 1 line

[1] https://doc.rust-lang.org/std/ptr/index.html

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_unaligned and write_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::dangling in 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::read is 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

author maintainer
#42 lore raw

PATCH v6 10/26 rust: treewide: switch to our kernel `Box` type

In reply to Danilo Krummrich

Now 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
index b0227cf9ddd3..5de7223beb4d 100644
@@ -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
index db3372619ecd..dfb2204918c7 100644
@@ -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
index 428f8be8f3a2..e42fe222826c 100644
@@ -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>) -> &'a mut 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
index 9023479281f0..5a868ce013f7 100644
@@ -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
index 2b306afbe56d..2081932bb4b9 100644
@@ -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
index 30632070ee67..f8f6d530db7d 100644
@@ -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
index ea5c5bc1ce12..a9096a4dc42a 100644
@@ -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
index 553a5cba2adc..94318472507f 100644
@@ -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
index 159e75292970..c327fe0617c8 100644
@@ -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
Reviewed-by on v6 10/26
#43 lore raw

Re: [PATCH v6 10/26] rust: treewide: switch to our kernel `Box` type

Quoted 5 lines · Danilo Krummrich

Now that we got the kernel Box type in place, convert all existing Box users to make use of it.

---
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(-)
author maintainer
#44 lore raw

PATCH v6 11/26 rust: alloc: remove `BoxExt` extension

In reply to Danilo Krummrich

Now 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
index 0c9bb60250af..d248390345ec 100644
@@ -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
index a10d0e140ab1..4599bd06b1e6 100644
@@ -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
index a9210634a8c3..c1f8e5c832e2 100644
@@ -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
index 9b9e9b913d1c..234b615662a2 100644
@@ -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> = &'a T;
-    type BorrowedMut<'a> = &'a mut 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(ptr as _) }
-    }
-
-    unsafe fn borrow<'a>(ptr: *const core::ffi::c_void) -> &'a T {
-        // 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) -> &'a mut 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
Reviewed-by on v6 11/26
#45 lore raw

Re: [PATCH v6 11/26] rust: alloc: remove `BoxExt` extension

Quoted 5 lines · Danilo Krummrich

Now that all existing Box users were moved to the kernel Box type, remove the BoxExt extension and all other related extensions.

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
author maintainer
#46 lore raw

PATCH v6 12/26 rust: alloc: add `Box` to prelude

In reply to Danilo Krummrich

Now 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
index c1f8e5c832e2..d5f2fe42d093 100644
@@ -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
author maintainer
#47 lore raw

PATCH v6 13/26 rust: alloc: implement kernel `Vec` type

In reply to Danilo Krummrich

Vec 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
index d248390345ec..e88c7e10ee9b 100644
@@ -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
index d5f2fe42d093..80223cdaa485 100644
@@ -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
#48 lore raw

Re: [PATCH v6 13/26] rust: alloc: implement kernel `Vec` type

Quoted 11 lines · Danilo Krummrich

Vec 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.

---

[...]

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()
+    }
+

[...]

author maintainer
#49 lore raw

Re: [PATCH v6 13/26] rust: alloc: implement kernel `Vec` type

Quoted 27 lines · Boqun Feng
Quoted 11 lines · Danilo Krummrich

Vec 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.

---

[...]

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 indicate new_len side is open?

Agreed.

Quoted 3 lines

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()?

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.

#50 lore raw

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

Vec 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.

---

[...]

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 indicate new_len side 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.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()?

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

#51 lore raw

Re: [PATCH v6 13/26] rust: alloc: implement kernel `Vec` type

In reply to Danilo Krummrich
Quoted 1 line · Danilo Krummrich

Vec provides a contiguous growable array type (such as Vec) 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, Vmalloc or KVmalloc).

In contrast to Rust's Vec type, the kernel Vec type 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.

---
 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
index d248390345ec..e88c7e10ee9b 100644
@@ -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
new file mode 100644
index 000000000000..89afc0f25bd4
@@ -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 item in 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(ptr as _, 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
index d5f2fe42d093..80223cdaa485 100644
@@ -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

author maintainer
#52 lore raw

Re: [PATCH v6 13/26] rust: alloc: implement kernel `Vec` type

Quoted 88 lines · Benno Lossin
Quoted 1 line · Danilo Krummrich

Vec provides a contiguous growable array type (such as Vec) 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, Vmalloc or KVmalloc).

In contrast to Rust's Vec type, the kernel Vec type considers the

I 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.

---
 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
index d248390345ec..e88c7e10ee9b 100644
@@ -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
new file mode 100644
index 000000000000..89afc0f25bd4
@@ -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 KVec via 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! without GFP_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.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.

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 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.

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 if chain that you have above (ie the one started with "If T is 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 # 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 item in 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(ptr as _, 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
index d5f2fe42d093..80223cdaa485 100644
@@ -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

#53 lore raw

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! without GFP_KERNEL?
Or add additional flags, eg __GFP_ZERO?

Quoted 5 lines

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.

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.ptr instead 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 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.

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>() * capacity bytes".

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 if chain that you have above (ie the one started with "If T is a ZST:").

But T isn'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

#54 lore raw

Re: [PATCH v6 13/26] rust: alloc: implement kernel `Vec` type

In reply to Danilo Krummrich
Quoted 9 lines · Danilo Krummrich

Vec 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.

Quoted 7 lines
---
 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

author maintainer
#55 lore raw

Re: [PATCH v6 13/26] rust: alloc: implement kernel `Vec` type

Quoted 18 lines · Benno Lossin
Quoted 9 lines · Danilo Krummrich

Vec 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.

Quoted 7 lines
---
 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 pop or remove function 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

maintainer
#56 lore raw

Re: [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

Vec 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
Quoted 8 lines
Quoted 2 lines

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.

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.

When this lands in rust-next, we should open good-first-issues for these on the Rust-for-Linux bug tracker.

Alice

author maintainer
#57 lore raw

PATCH v6 14/26 rust: alloc: implement `IntoIterator` for `Vec`

In reply to Danilo Krummrich

Implement 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
index e88c7e10ee9b..4ff4df4597a3 100644
@@ -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
maintainer Reviewed-by on v6 14/26
#58 lore raw

Re: [PATCH v6 14/26] rust: alloc: implement `IntoIterator` for `Vec`

Quoted 9 lines · Danilo Krummrich

Implement 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.

Quoted 5 lines

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.

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
index e88c7e10ee9b..4ff4df4597a3 100644
@@ -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
index 89afc0f25bd4..3b79f977b65e 100644
@@ -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 &'a Vec<T, A>
+where
+    A: Allocator,
+{
+    type Item = &'a T;
+    type IntoIter = slice::Iter<'a, T>;
+
+    fn into_iter(self) -> Self::IntoIter {
+        self.iter()
+    }
+}
+
+impl<'a, T, A: Allocator> IntoIterator for &'a mut Vec<T, A>
+where
+    A: Allocator,
+{
+    type Item = &'a mut 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

#59 lore raw

Re: [PATCH v6 14/26] rust: alloc: implement `IntoIterator` for `Vec`

In reply to Danilo Krummrich
Quoted 62 lines · Danilo Krummrich

Implement 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
index e88c7e10ee9b..4ff4df4597a3 100644
@@ -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
index 89afc0f25bd4..3b79f977b65e 100644
@@ -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 &'a Vec<T, A>
+where
+    A: Allocator,
+{
+    type Item = &'a T;
+    type IntoIter = slice::Iter<'a, T>;
+
+    fn into_iter(self) -> Self::IntoIter {
+        self.iter()
+    }
+}
+
+impl<'a, T, A: Allocator> IntoIterator for &'a mut 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 = &'a mut 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

author maintainer
#60 lore raw

Re: [PATCH v6 14/26] rust: alloc: implement `IntoIterator` for `Vec`

Quoted 81 lines · Benno Lossin
Quoted 62 lines · Danilo Krummrich

Implement 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
index e88c7e10ee9b..4ff4df4597a3 100644
@@ -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
index 89afc0f25bd4..3b79f977b65e 100644
@@ -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 &'a Vec<T, A>
+where
+    A: Allocator,
+{
+    type Item = &'a T;
+    type IntoIter = slice::Iter<'a, T>;
+
+    fn into_iter(self) -> Self::IntoIter {
+        self.iter()
+    }
+}
+
+impl<'a, T, A: Allocator> IntoIterator for &'a mut 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 = &'a mut 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>::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 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,
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

#61 lore raw

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

author maintainer
#62 lore raw

Re: [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 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 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

author maintainer
#63 lore raw

PATCH v6 15/26 rust: alloc: implement `collect` for `IntoIter`

In reply to 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::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
index 3b79f977b65e..ad96f4c3af9e 100644
@@ -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
#64 lore raw

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::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 +16 -0
index 3b79f977b65e..ad96f4c3af9e 100644
@@ -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].

[1]: https://doc.rust-lang.org/core/ptr/fn.copy.html#safety

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

author maintainer
#65 lore raw

Re: [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::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 +21 -0
index 3b79f977b65e..ad96f4c3af9e 100644
@@ -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
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].

[1]: https://doc.rust-lang.org/core/ptr/fn.copy.html#safety

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

#66 lore raw

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
the Examples section 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> {
author maintainer
#67 lore raw

Re: [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 Examples and add a section header # Implementation Details or similar.

Sure, we can do that.

Quoted 27 lines

---
Cheers,
Benno

Quoted 21 lines
Quoted 21 lines

can you move it under
the Examples section 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> {
author maintainer
#68 lore raw

PATCH v6 16/26 rust: treewide: switch to the kernel `Vec` type

In reply to Danilo Krummrich

Now 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
index bb8d4f41475b..209ad1bb1a49 100644
@@ -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: &'a CStr) -> 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
index babc731bd5f6..b94517231fcc 100644
@@ -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
index 234b615662a2..43f6efbe31c6 100644
@@ -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
index e9347cff99ab..bc011061de45 100644
@@ -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
index c327fe0617c8..a733168d40f9 100644
@@ -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
index 2a9eaab62d1c..4aaf117bf8e3 100644
@@ -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
Reviewed-by on v6 16/26
#69 lore raw

Re: [PATCH v6 16/26] rust: treewide: switch to the kernel `Vec` type

Quoted 5 lines · Danilo Krummrich

Now that we got the kernel Vec in place, convert all existing Vec users to make use of it.

---
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(-)
author maintainer
#70 lore raw

PATCH v6 17/26 rust: alloc: remove `VecExt` extension

In reply to Danilo Krummrich

Now 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
index 4ff4df4597a3..1feabc817d00 100644
@@ -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
index 80223cdaa485..07daccf6ca8e 100644
@@ -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
author maintainer
#71 lore raw

PATCH v6 18/26 rust: alloc: add `Vec` to prelude

In reply to Danilo Krummrich

Now 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
index 07daccf6ca8e..8bdab9aa0d16 100644
@@ -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
author maintainer
#72 lore raw

PATCH v6 19/26 rust: error: use `core::alloc::LayoutError`

In reply to Danilo Krummrich

Use 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
index 145f5c397009..2d012cc3881a 100644
@@ -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
author maintainer
#73 lore raw

PATCH v6 20/26 rust: error: check for config `test` in `Error::name`

In reply to Danilo Krummrich

Additional 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
index 2d012cc3881a..7adf889b9526 100644
@@ -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<&'static CStr> {
         // 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<&'static CStr> {
         None
     }
Signature · 1 line
2.46.0
Reviewed-by on v6 20/26
#74 lore raw

Re: [PATCH v6 20/26] rust: error: check for config `test` in `Error::name`

Quoted 6 lines · Danilo Krummrich

Additional to testlib also check for test in Error::name. This is required by a subsequent patch that (indirectly) uses Error in test cases.

---
Cheers,
Benno

Quoted 29 lines
---
 rust/kernel/error.rs | 4 ++--
 1 file changed, 2 insertions(+), 2 deletions(-)
rust/kernel/error.rs +2 -3
index 2d012cc3881a..7adf889b9526 100644
@@ -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<&'static CStr> {
         // 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<&'static CStr> {
         None
     }
--

2.46.0

author maintainer
#75 lore raw

PATCH v6 21/26 rust: alloc: implement `contains` for `Flags`

In reply to 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 Allocator to check for __GFP_ZERO.

---
 rust/kernel/alloc.rs | 7 ++++++-
 1 file changed, 6 insertions(+), 1 deletion(-)
rust/kernel/alloc.rs +6 -1
index 1feabc817d00..5c66229a7542 100644
@@ -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
Reviewed-by on v6 21/26
#76 lore raw

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 Allocator to check for __GFP_ZERO.

---
Cheers,
Benno

Quoted 32 lines
---
 rust/kernel/alloc.rs | 7 ++++++-
 1 file changed, 6 insertions(+), 1 deletion(-)
rust/kernel/alloc.rs +6 -2
index 1feabc817d00..5c66229a7542 100644
@@ -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

author maintainer
#77 lore raw

PATCH v6 22/26 rust: alloc: implement `Cmalloc` in module allocator_test

In reply to Danilo Krummrich

So 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
index 1b2642c547ec..7fff308d02dc 100644
@@ -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>) -> &'a CmallocData {
+        // 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
#78 lore raw

Re: [PATCH v6 22/26] rust: alloc: implement `Cmalloc` in module allocator_test

Quoted 19 lines · Danilo Krummrich

So 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 +0 -0
index 1b2642c547ec..7fff308d02dc 100644

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>) -> &'a CmallocData {
+        // 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

author maintainer
#79 lore raw

Re: [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 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(-)
Quoted 9 lines

rust/kernel/alloc/allocator_test.rs line 2

Quoted 6 lines
rust/kernel/alloc/allocator_test.rs +0 -0
index 1b2642c547ec..7fff308d02dc 100644
@@ -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 of layout. 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>) -> &'a CmallocData {
+        // 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 by alloc_store_data.
This isn't forbidden by the Allocator trait 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,
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

#80 lore raw

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 of layout. 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) and panic() 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

author maintainer
#81 lore raw

Re: [PATCH v6 22/26] rust: alloc: implement `Cmalloc` in module allocator_test

In reply to Danilo Krummrich
Quoted 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 by alloc_store_data.
This isn't forbidden by the Allocator trait 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,
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

#82 lore raw

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 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.

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 realloc and free, 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

author maintainer
#83 lore raw

Re: [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 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.

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 realloc and free, 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)?

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 Allocator returns via alloc and realloc, 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

#84 lore raw

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 by alloc_store_data.
This isn't forbidden by the Allocator trait 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 5 lines
Quoted 2 lines

But what about the example above (ie the alloc(size = 0) and then free)?

This never has been valid for the Allocator trait. Look at Kmalloc, Vmalloc and KVmalloc, 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_layout for free. Such that free can check if the size was 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 Allocator returns via alloc and realloc, 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.

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 Allocator where 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

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.

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

author maintainer
#85 lore raw

PATCH v6 23/26 rust: str: test: replace `alloc::format`

In reply to Danilo Krummrich

The 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
index 209ad1bb1a49..726329530338 100644
@@ -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: &'static str =
         "\\x01\\x02\\x03\\x04\\x05\\x06\\x07\\x08\\x09\\x0a\\x0b\\x0c\\x0d\\x0e\\x0f\
Signature · 1 line
2.46.0
Reviewed-by on v6 23/26
#86 lore raw

Re: [PATCH v6 23/26] rust: str: test: replace `alloc::format`

Quoted 11 lines · Danilo Krummrich

The 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.

---
Cheers,
Benno

Quoted 41 lines
---
 rust/kernel/str.rs | 23 ++++++++++++++++++++++-
 1 file changed, 22 insertions(+), 1 deletion(-)
rust/kernel/str.rs +22 -2
index 209ad1bb1a49..726329530338 100644
@@ -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: &'static str =
         "\\x01\\x02\\x03\\x04\\x05\\x06\\x07\\x08\\x09\\x0a\\x0b\\x0c\\x0d\\x0e\\x0f\
--

2.46.0

author maintainer
#87 lore raw

PATCH v6 24/26 rust: alloc: update module comment of alloc.rs

In reply to Danilo Krummrich

Before 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
index 5c66229a7542..282afd184957 100644
@@ -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
author maintainer
#88 lore raw

PATCH v6 25/26 kbuild: rust: remove the `alloc` crate and `GlobalAlloc`

In reply to Danilo Krummrich

Now 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
index 77836388377d..8963f53c5d6c 100644
@@ -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
index 3803c21d1403..1b870e8e83ea 100644
@@ -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
index efacca63c897..7e7b6b3d5bb9 100644
@@ -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
index f270c7b0cf34..3421a9ea3604 100755
@@ -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
#89 lore raw

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

author maintainer
#90 lore raw

PATCH v6 26/26 MAINTAINERS: add entry for the Rust `alloc` module

In reply to Danilo Krummrich

Add 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
index 42decde38320..560516b3aaf4 100644
@@ -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
maintainer
#91 lore raw

Re: [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 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 +0 -0
index 42decde38320..560516b3aaf4 100644

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]>
author maintainer
#92 lore raw

Re: [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 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 +0 -0
index 42decde38320..560516b3aaf4 100644
@@ -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]>
maintainer
#93 lore raw

Re: [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 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 +0 -0
index 42decde38320..560516b3aaf4 100644
@@ -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:

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

#94 lore raw

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 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 +0 -0
index 42decde38320..560516b3aaf4 100644
@@ -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
- ...

Quoted 4 lines
Quoted 1 line

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.

I forgot the reason for not using it, does anyone remember?

---
Cheers,
Benno

#95 lore raw

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

#96 lore raw

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.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.

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

author maintainer
#97 lore raw

Re: [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.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.

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.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

#98 lore raw

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.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.

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

#99 lore raw
In reply to Danilo Krummrich
Quoted 41 lines · Danilo Krummrich

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.

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
- 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