* [PATCH v7 00/26] Generic `Allocator` support for Rust
@ 2024-09-11 22:52 Danilo Krummrich
2024-09-11 22:52 ` [PATCH v7 01/26] rust: alloc: add `Allocator` trait Danilo Krummrich
` (25 more replies)
0 siblings, 26 replies; 77+ messages in thread
From: Danilo Krummrich @ 2024-09-11 22:52 UTC (permalink / raw)
To: ojeda, alex.gaynor, wedsonaf, boqun.feng, gary, bjorn3_gh,
benno.lossin, a.hindborg, aliceryhl, akpm
Cc: daniel.almeida, faith.ekstrand, boris.brezillon, lina, mcanal,
zhiw, cjia, jhubbard, airlied, ajanulgu, lyude, linux-kernel,
rust-for-linux, linux-mm, 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
`Allocator`s.
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-stable, 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 v7:
- rebase onto rust-next resolving recent conflicts (93dc3be19450447a3a7090bd1dfb9f3daac3e8d2)
- documentation / safety comment changes suggested by Benno and Boqun
- rename `ptr` to `current` in `IntoIter::next` (Alice)
- remove unnecessary braces in `kvec!` (Benno)
- add `debug_assert!` in `Vec::set_len` (Benno)
- remove unused args in `impl_slice_eq!` (Benno)
- simplify `if` statement in `Cmalloc::realloc` (Benno)
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 `Allocator`s, 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 extension of std's `Box`
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 | 43 +-
rust/bindings/bindings_helper.h | 1 +
rust/exports.c | 1 -
rust/helpers/helpers.c | 1 +
rust/helpers/slab.c | 6 +
rust/helpers/vmalloc.c | 9 +
rust/kernel/alloc.rs | 152 ++++-
rust/kernel/alloc/allocator.rs | 198 ++++--
rust/kernel/alloc/allocator_test.rs | 200 ++++++
rust/kernel/alloc/box_ext.rs | 87 ---
rust/kernel/alloc/kbox.rs | 465 ++++++++++++++
rust/kernel/alloc/kvec.rs | 905 ++++++++++++++++++++++++++++
rust/kernel/alloc/vec_ext.rs | 185 ------
rust/kernel/error.rs | 6 +-
rust/kernel/init.rs | 95 +--
rust/kernel/init/__internal.rs | 2 +-
rust/kernel/lib.rs | 1 -
rust/kernel/prelude.rs | 5 +-
rust/kernel/rbtree.rs | 49 +-
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 | 52 +-
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 +-
34 files changed, 2029 insertions(+), 578 deletions(-)
create mode 100644 rust/helpers/vmalloc.c
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: db8c7e4c95acc68f1e1cd51e0a7f1532c69a23c9
--
2.46.0
^ permalink raw reply [flat|nested] 77+ messages in thread
* [PATCH v7 01/26] rust: alloc: add `Allocator` trait
2024-09-11 22:52 [PATCH v7 00/26] Generic `Allocator` support for Rust Danilo Krummrich
@ 2024-09-11 22:52 ` Danilo Krummrich
2024-09-15 15:28 ` Gary Guo
2024-09-21 15:32 ` [RFC PATCH] rust: alloc: pass `old_layout` to `Allocator` Danilo Krummrich
2024-09-11 22:52 ` [PATCH v7 02/26] rust: alloc: separate `aligned_size` from `krealloc_aligned` Danilo Krummrich
` (24 subsequent siblings)
25 siblings, 2 replies; 77+ messages in thread
From: Danilo Krummrich @ 2024-09-11 22:52 UTC (permalink / raw)
To: ojeda, alex.gaynor, wedsonaf, boqun.feng, gary, bjorn3_gh,
benno.lossin, a.hindborg, aliceryhl, akpm
Cc: daniel.almeida, faith.ekstrand, boris.brezillon, lina, mcanal,
zhiw, cjia, jhubbard, airlied, ajanulgu, lyude, linux-kernel,
rust-for-linux, linux-mm, 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`.
Reviewed-by: Alice Ryhl <aliceryhl@google.com>
Signed-off-by: Danilo Krummrich <dakr@kernel.org>
---
rust/kernel/alloc.rs | 112 +++++++++++++++++++++++++++++++++++++++++++
1 file changed, 112 insertions(+)
diff --git a/rust/kernel/alloc.rs b/rust/kernel/alloc.rs
index 1966bd407017..6c21bd2edad9 100644
--- a/rust/kernel/alloc.rs
+++ b/rust/kernel/alloc.rs
@@ -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,114 @@ 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 buffers 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.
+//
+// Note that `Allocator::{realloc,free}` don't have an `old_layout` argument (like stdlib's
+// corresponding `Allocator` trait functions have), since the implemented (kernel) allocators
+// neither need nor honor such an argument. Thus, it would be misleading to make this API require it
+// anyways.
+//
+// More generally, this trait isn't intended for implementers to encode a lot of semantics, but
+// rather provide a thin generalization layer for the kernel's allocators.
+//
+// Depending on future requirements, the requirements for this trait may change as well and
+// implementing allocators that need to encode more semantics may become desirable.
+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)) };
+ }
+}
--
2.46.0
^ permalink raw reply related [flat|nested] 77+ messages in thread
* [PATCH v7 02/26] rust: alloc: separate `aligned_size` from `krealloc_aligned`
2024-09-11 22:52 [PATCH v7 00/26] Generic `Allocator` support for Rust Danilo Krummrich
2024-09-11 22:52 ` [PATCH v7 01/26] rust: alloc: add `Allocator` trait Danilo Krummrich
@ 2024-09-11 22:52 ` Danilo Krummrich
2024-09-11 22:52 ` [PATCH v7 03/26] rust: alloc: rename `KernelAllocator` to `Kmalloc` Danilo Krummrich
` (23 subsequent siblings)
25 siblings, 0 replies; 77+ messages in thread
From: Danilo Krummrich @ 2024-09-11 22:52 UTC (permalink / raw)
To: ojeda, alex.gaynor, wedsonaf, boqun.feng, gary, bjorn3_gh,
benno.lossin, a.hindborg, aliceryhl, akpm
Cc: daniel.almeida, faith.ekstrand, boris.brezillon, lina, mcanal,
zhiw, cjia, jhubbard, airlied, ajanulgu, lyude, linux-kernel,
rust-for-linux, linux-mm, 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`.
Reviewed-by: Alice Ryhl <aliceryhl@google.com>
Reviewed-by: Benno Lossin <benno.lossin@proton.me>
Reviewed-by: Gary Guo <gary@garyguo.net>
Signed-off-by: Danilo Krummrich <dakr@kernel.org>
---
rust/kernel/alloc/allocator.rs | 19 ++++++++++++-------
1 file changed, 12 insertions(+), 7 deletions(-)
diff --git a/rust/kernel/alloc/allocator.rs b/rust/kernel/alloc/allocator.rs
index e6ea601f38c6..c83b6dff896d 100644
--- a/rust/kernel/alloc/allocator.rs
+++ b/rust/kernel/alloc/allocator.rs
@@ -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
--
2.46.0
^ permalink raw reply related [flat|nested] 77+ messages in thread
* [PATCH v7 03/26] rust: alloc: rename `KernelAllocator` to `Kmalloc`
2024-09-11 22:52 [PATCH v7 00/26] Generic `Allocator` support for Rust Danilo Krummrich
2024-09-11 22:52 ` [PATCH v7 01/26] rust: alloc: add `Allocator` trait Danilo Krummrich
2024-09-11 22:52 ` [PATCH v7 02/26] rust: alloc: separate `aligned_size` from `krealloc_aligned` Danilo Krummrich
@ 2024-09-11 22:52 ` Danilo Krummrich
2024-09-11 22:52 ` [PATCH v7 04/26] rust: alloc: implement `Allocator` for `Kmalloc` Danilo Krummrich
` (22 subsequent siblings)
25 siblings, 0 replies; 77+ messages in thread
From: Danilo Krummrich @ 2024-09-11 22:52 UTC (permalink / raw)
To: ojeda, alex.gaynor, wedsonaf, boqun.feng, gary, bjorn3_gh,
benno.lossin, a.hindborg, aliceryhl, akpm
Cc: daniel.almeida, faith.ekstrand, boris.brezillon, lina, mcanal,
zhiw, cjia, jhubbard, airlied, ajanulgu, lyude, linux-kernel,
rust-for-linux, linux-mm, Danilo Krummrich
Subsequent patches implement `Vmalloc` and `KVmalloc` allocators, hence
align `KernelAllocator` to this naming scheme.
Reviewed-by: Alice Ryhl <aliceryhl@google.com>
Reviewed-by: Benno Lossin <benno.lossin@proton.me>
Reviewed-by: Gary Guo <gary@garyguo.net>
Signed-off-by: Danilo Krummrich <dakr@kernel.org>
---
rust/kernel/alloc/allocator.rs | 6 +++---
1 file changed, 3 insertions(+), 3 deletions(-)
diff --git a/rust/kernel/alloc/allocator.rs b/rust/kernel/alloc/allocator.rs
index c83b6dff896d..e32182f91167 100644
--- a/rust/kernel/alloc/allocator.rs
+++ b/rust/kernel/alloc/allocator.rs
@@ -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]
--
2.46.0
^ permalink raw reply related [flat|nested] 77+ messages in thread
* [PATCH v7 04/26] rust: alloc: implement `Allocator` for `Kmalloc`
2024-09-11 22:52 [PATCH v7 00/26] Generic `Allocator` support for Rust Danilo Krummrich
` (2 preceding siblings ...)
2024-09-11 22:52 ` [PATCH v7 03/26] rust: alloc: rename `KernelAllocator` to `Kmalloc` Danilo Krummrich
@ 2024-09-11 22:52 ` Danilo Krummrich
2024-09-26 13:00 ` Benno Lossin
2024-09-11 22:52 ` [PATCH v7 05/26] rust: alloc: add module `allocator_test` Danilo Krummrich
` (21 subsequent siblings)
25 siblings, 1 reply; 77+ messages in thread
From: Danilo Krummrich @ 2024-09-11 22:52 UTC (permalink / raw)
To: ojeda, alex.gaynor, wedsonaf, boqun.feng, gary, bjorn3_gh,
benno.lossin, a.hindborg, aliceryhl, akpm
Cc: daniel.almeida, faith.ekstrand, boris.brezillon, lina, mcanal,
zhiw, cjia, jhubbard, airlied, ajanulgu, lyude, linux-kernel,
rust-for-linux, linux-mm, 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`.
Signed-off-by: Danilo Krummrich <dakr@kernel.org>
---
rust/kernel/alloc.rs | 2 +-
rust/kernel/alloc/allocator.rs | 89 +++++++++++++++++++++++++++++++++-
2 files changed, 89 insertions(+), 2 deletions(-)
diff --git a/rust/kernel/alloc.rs b/rust/kernel/alloc.rs
index 6c21bd2edad9..535ed1527034 100644
--- a/rust/kernel/alloc.rs
+++ b/rust/kernel/alloc.rs
@@ -4,7 +4,7 @@
#[cfg(not(test))]
#[cfg(not(testlib))]
-mod allocator;
+pub mod allocator;
pub mod box_ext;
pub mod vec_ext;
diff --git a/rust/kernel/alloc/allocator.rs b/rust/kernel/alloc/allocator.rs
index e32182f91167..7a93442187a1 100644
--- a/rust/kernel/alloc/allocator.rs
+++ b/rust/kernel/alloc/allocator.rs
@@ -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>
use super::{flags::*, Flags};
use core::alloc::{GlobalAlloc, Layout};
use core::ptr;
+use core::ptr::NonNull;
-struct Kmalloc;
+use crate::alloc::{AllocError, Allocator};
+use crate::bindings;
+
+/// The contiguous kernel allocator.
+///
+/// `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.
+///
+/// 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,77 @@ 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`].
+ ///
+ /// # 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.
+ unsafe fn call(
+ &self,
+ ptr: Option<NonNull<u8>>,
+ layout: Layout,
+ flags: Flags,
+ ) -> Result<NonNull<[u8]>, AllocError> {
+ let size = aligned_size(layout);
+ let ptr = match ptr {
+ Some(ptr) => ptr.as_ptr(),
+ None => ptr::null(),
+ };
+
+ // SAFETY:
+ // - `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.
+ //
+ // GUARANTEE:
+ // - `self.0` is one of `krealloc`, `vrealloc`, `kvrealloc`.
+ // - Those functions provide the guarantees of this function.
+ let raw_ptr = unsafe {
+ // If `size == 0` and `ptr != NULL` the memory behind the pointer is freed.
+ self.0(ptr.cast(), size, flags.0).cast()
+ };
+
+ let ptr = if size == 0 {
+ NonNull::dangling()
+ } else {
+ NonNull::new(raw_ptr).ok_or(AllocError)?
+ };
+
+ Ok(NonNull::slice_from_raw_parts(ptr, size))
+ }
+}
+
+// SAFETY: `realloc` delegates to `ReallocFunc::call`, which guarantees that
+// - memory remains valid until it is explicitly freed,
+// - passing a pointer to a valid memory allocation is OK,
+// - `realloc` satisfies the guarantees, since `ReallocFunc::call` has the same.
+unsafe impl Allocator for Kmalloc {
+ #[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
^ permalink raw reply related [flat|nested] 77+ messages in thread
* [PATCH v7 05/26] rust: alloc: add module `allocator_test`
2024-09-11 22:52 [PATCH v7 00/26] Generic `Allocator` support for Rust Danilo Krummrich
` (3 preceding siblings ...)
2024-09-11 22:52 ` [PATCH v7 04/26] rust: alloc: implement `Allocator` for `Kmalloc` Danilo Krummrich
@ 2024-09-11 22:52 ` Danilo Krummrich
2024-09-11 22:52 ` [PATCH v7 06/26] rust: alloc: implement `Vmalloc` allocator Danilo Krummrich
` (20 subsequent siblings)
25 siblings, 0 replies; 77+ messages in thread
From: Danilo Krummrich @ 2024-09-11 22:52 UTC (permalink / raw)
To: ojeda, alex.gaynor, wedsonaf, boqun.feng, gary, bjorn3_gh,
benno.lossin, a.hindborg, aliceryhl, akpm
Cc: daniel.almeida, faith.ekstrand, boris.brezillon, lina, mcanal,
zhiw, cjia, jhubbard, airlied, ajanulgu, lyude, linux-kernel,
rust-for-linux, linux-mm, Danilo Krummrich
`Allocator`s, 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 `Allocator`s in the kernel, such that we don't break the
`rusttest` make target in subsequent patches.
Reviewed-by: Alice Ryhl <aliceryhl@google.com>
Reviewed-by: Benno Lossin <benno.lossin@proton.me>
Reviewed-by: Gary Guo <gary@garyguo.net>
Signed-off-by: Danilo Krummrich <dakr@kernel.org>
---
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
diff --git a/rust/kernel/alloc.rs b/rust/kernel/alloc.rs
index 535ed1527034..da025faf07f8 100644
--- a/rust/kernel/alloc.rs
+++ b/rust/kernel/alloc.rs
@@ -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;
diff --git a/rust/kernel/alloc/allocator_test.rs b/rust/kernel/alloc/allocator_test.rs
new file mode 100644
index 000000000000..4785efc474a7
--- /dev/null
+++ b/rust/kernel/alloc/allocator_test.rs
@@ -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!();
+ }
+}
--
2.46.0
^ permalink raw reply related [flat|nested] 77+ messages in thread
* [PATCH v7 06/26] rust: alloc: implement `Vmalloc` allocator
2024-09-11 22:52 [PATCH v7 00/26] Generic `Allocator` support for Rust Danilo Krummrich
` (4 preceding siblings ...)
2024-09-11 22:52 ` [PATCH v7 05/26] rust: alloc: add module `allocator_test` Danilo Krummrich
@ 2024-09-11 22:52 ` Danilo Krummrich
2024-09-26 13:06 ` Benno Lossin
2024-09-11 22:52 ` [PATCH v7 07/26] rust: alloc: implement `KVmalloc` allocator Danilo Krummrich
` (19 subsequent siblings)
25 siblings, 1 reply; 77+ messages in thread
From: Danilo Krummrich @ 2024-09-11 22:52 UTC (permalink / raw)
To: ojeda, alex.gaynor, wedsonaf, boqun.feng, gary, bjorn3_gh,
benno.lossin, a.hindborg, aliceryhl, akpm
Cc: daniel.almeida, faith.ekstrand, boris.brezillon, lina, mcanal,
zhiw, cjia, jhubbard, airlied, ajanulgu, lyude, linux-kernel,
rust-for-linux, linux-mm, 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()`.
Reviewed-by: Alice Ryhl <aliceryhl@google.com>
Signed-off-by: Danilo Krummrich <dakr@kernel.org>
---
rust/helpers/helpers.c | 1 +
rust/helpers/vmalloc.c | 9 ++++++++
rust/kernel/alloc/allocator.rs | 36 +++++++++++++++++++++++++++++
rust/kernel/alloc/allocator_test.rs | 1 +
4 files changed, 47 insertions(+)
create mode 100644 rust/helpers/vmalloc.c
diff --git a/rust/helpers/helpers.c b/rust/helpers/helpers.c
index 30f40149f3a9..20a0c69d5cc7 100644
--- a/rust/helpers/helpers.c
+++ b/rust/helpers/helpers.c
@@ -22,5 +22,6 @@
#include "spinlock.c"
#include "task.c"
#include "uaccess.c"
+#include "vmalloc.c"
#include "wait.c"
#include "workqueue.c"
diff --git a/rust/helpers/vmalloc.c b/rust/helpers/vmalloc.c
new file mode 100644
index 000000000000..80d34501bbc0
--- /dev/null
+++ b/rust/helpers/vmalloc.c
@@ -0,0 +1,9 @@
+// SPDX-License-Identifier: GPL-2.0
+
+#include <linux/vmalloc.h>
+
+void * __must_check __realloc_size(2)
+rust_helper_vrealloc(const void *p, size_t size, gfp_t flags)
+{
+ return vrealloc(p, size, flags);
+}
diff --git a/rust/kernel/alloc/allocator.rs b/rust/kernel/alloc/allocator.rs
index 7a93442187a1..0f2bc702e8e4 100644
--- a/rust/kernel/alloc/allocator.rs
+++ b/rust/kernel/alloc/allocator.rs
@@ -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`].
@@ -158,6 +171,29 @@ unsafe fn alloc_zeroed(&self, layout: Layout) -> *mut u8 {
}
}
+// SAFETY: `realloc` delegates to `ReallocFunc::call`, which guarantees that
+// - memory remains valid until it is explicitly freed,
+// - passing a pointer to a valid memory allocation is OK,
+// - `realloc` satisfies the guarantees, since `ReallocFunc::call` has the same.
+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;
diff --git a/rust/kernel/alloc/allocator_test.rs b/rust/kernel/alloc/allocator_test.rs
index 4785efc474a7..e7bf2982f68f 100644
--- a/rust/kernel/alloc/allocator_test.rs
+++ b/rust/kernel/alloc/allocator_test.rs
@@ -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
^ permalink raw reply related [flat|nested] 77+ messages in thread
* [PATCH v7 07/26] rust: alloc: implement `KVmalloc` allocator
2024-09-11 22:52 [PATCH v7 00/26] Generic `Allocator` support for Rust Danilo Krummrich
` (5 preceding siblings ...)
2024-09-11 22:52 ` [PATCH v7 06/26] rust: alloc: implement `Vmalloc` allocator Danilo Krummrich
@ 2024-09-11 22:52 ` Danilo Krummrich
2024-09-26 13:07 ` Benno Lossin
2024-09-11 22:52 ` [PATCH v7 08/26] rust: alloc: add __GFP_NOWARN to `Flags` Danilo Krummrich
` (18 subsequent siblings)
25 siblings, 1 reply; 77+ messages in thread
From: Danilo Krummrich @ 2024-09-11 22:52 UTC (permalink / raw)
To: ojeda, alex.gaynor, wedsonaf, boqun.feng, gary, bjorn3_gh,
benno.lossin, a.hindborg, aliceryhl, akpm
Cc: daniel.almeida, faith.ekstrand, boris.brezillon, lina, mcanal,
zhiw, cjia, jhubbard, airlied, ajanulgu, lyude, linux-kernel,
rust-for-linux, linux-mm, 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()`.
Reviewed-by: Alice Ryhl <aliceryhl@google.com>
Signed-off-by: Danilo Krummrich <dakr@kernel.org>
---
rust/helpers/slab.c | 6 +++++
rust/kernel/alloc/allocator.rs | 35 +++++++++++++++++++++++++++++
rust/kernel/alloc/allocator_test.rs | 1 +
3 files changed, 42 insertions(+)
diff --git a/rust/helpers/slab.c b/rust/helpers/slab.c
index f043e087f9d6..a842bfbddcba 100644
--- a/rust/helpers/slab.c
+++ b/rust/helpers/slab.c
@@ -7,3 +7,9 @@ rust_helper_krealloc(const void *objp, size_t new_size, gfp_t flags)
{
return krealloc(objp, new_size, flags);
}
+
+void * __must_check __realloc_size(2)
+rust_helper_kvrealloc(const void *p, size_t size, gfp_t flags)
+{
+ return kvrealloc(p, size, flags);
+}
diff --git a/rust/kernel/alloc/allocator.rs b/rust/kernel/alloc/allocator.rs
index 0f2bc702e8e4..a5d7e66a68db 100644
--- a/rust/kernel/alloc/allocator.rs
+++ b/rust/kernel/alloc/allocator.rs
@@ -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 requested 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`].
@@ -194,6 +206,29 @@ unsafe fn realloc(
}
}
+// SAFETY: `realloc` delegates to `ReallocFunc::call`, which guarantees that
+// - memory remains valid until it is explicitly freed,
+// - passing a pointer to a valid memory allocation is OK,
+// - `realloc` satisfies the guarantees, since `ReallocFunc::call` has the same.
+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;
diff --git a/rust/kernel/alloc/allocator_test.rs b/rust/kernel/alloc/allocator_test.rs
index e7bf2982f68f..1b2642c547ec 100644
--- a/rust/kernel/alloc/allocator_test.rs
+++ b/rust/kernel/alloc/allocator_test.rs
@@ -8,6 +8,7 @@
pub struct Kmalloc;
pub type Vmalloc = Kmalloc;
+pub type KVmalloc = Kmalloc;
unsafe impl Allocator for Kmalloc {
unsafe fn realloc(
--
2.46.0
^ permalink raw reply related [flat|nested] 77+ messages in thread
* [PATCH v7 08/26] rust: alloc: add __GFP_NOWARN to `Flags`
2024-09-11 22:52 [PATCH v7 00/26] Generic `Allocator` support for Rust Danilo Krummrich
` (6 preceding siblings ...)
2024-09-11 22:52 ` [PATCH v7 07/26] rust: alloc: implement `KVmalloc` allocator Danilo Krummrich
@ 2024-09-11 22:52 ` Danilo Krummrich
2024-09-28 18:55 ` Gary Guo
2024-09-11 22:52 ` [PATCH v7 09/26] rust: alloc: implement kernel `Box` Danilo Krummrich
` (17 subsequent siblings)
25 siblings, 1 reply; 77+ messages in thread
From: Danilo Krummrich @ 2024-09-11 22:52 UTC (permalink / raw)
To: ojeda, alex.gaynor, wedsonaf, boqun.feng, gary, bjorn3_gh,
benno.lossin, a.hindborg, aliceryhl, akpm
Cc: daniel.almeida, faith.ekstrand, boris.brezillon, lina, mcanal,
zhiw, cjia, jhubbard, airlied, ajanulgu, lyude, linux-kernel,
rust-for-linux, linux-mm, Danilo Krummrich
Some test cases in subsequent patches provoke allocation failures. Add
`__GFP_NOWARN` to enable test cases to silence unpleasant warnings.
Reviewed-by: Alice Ryhl <aliceryhl@google.com>
Reviewed-by: Benno Lossin <benno.lossin@proton.me>
Signed-off-by: Danilo Krummrich <dakr@kernel.org>
---
rust/bindings/bindings_helper.h | 1 +
rust/kernel/alloc.rs | 5 +++++
2 files changed, 6 insertions(+)
diff --git a/rust/bindings/bindings_helper.h b/rust/bindings/bindings_helper.h
index ae82e9c941af..a80783fcbe04 100644
--- a/rust/bindings/bindings_helper.h
+++ b/rust/bindings/bindings_helper.h
@@ -31,4 +31,5 @@ const gfp_t RUST_CONST_HELPER_GFP_KERNEL_ACCOUNT = GFP_KERNEL_ACCOUNT;
const gfp_t RUST_CONST_HELPER_GFP_NOWAIT = GFP_NOWAIT;
const gfp_t RUST_CONST_HELPER___GFP_ZERO = __GFP_ZERO;
const gfp_t RUST_CONST_HELPER___GFP_HIGHMEM = ___GFP_HIGHMEM;
+const gfp_t RUST_CONST_HELPER___GFP_NOWARN = ___GFP_NOWARN;
const blk_features_t RUST_CONST_HELPER_BLK_FEAT_ROTATIONAL = BLK_FEAT_ROTATIONAL;
diff --git a/rust/kernel/alloc.rs b/rust/kernel/alloc.rs
index da025faf07f8..7a405d6f3034 100644
--- a/rust/kernel/alloc.rs
+++ b/rust/kernel/alloc.rs
@@ -91,6 +91,11 @@ pub mod flags {
/// use any filesystem callback. It is very likely to fail to allocate memory, even for very
/// small allocations.
pub const GFP_NOWAIT: Flags = Flags(bindings::GFP_NOWAIT);
+
+ /// Suppresses allocation failure reports.
+ ///
+ /// This is normally or'd with other flags.
+ pub const __GFP_NOWARN: Flags = Flags(bindings::__GFP_NOWARN);
}
/// The kernel's [`Allocator`] trait.
--
2.46.0
^ permalink raw reply related [flat|nested] 77+ messages in thread
* [PATCH v7 09/26] rust: alloc: implement kernel `Box`
2024-09-11 22:52 [PATCH v7 00/26] Generic `Allocator` support for Rust Danilo Krummrich
` (7 preceding siblings ...)
2024-09-11 22:52 ` [PATCH v7 08/26] rust: alloc: add __GFP_NOWARN to `Flags` Danilo Krummrich
@ 2024-09-11 22:52 ` Danilo Krummrich
2024-09-26 13:23 ` Benno Lossin
2024-09-28 18:54 ` Gary Guo
2024-09-11 22:52 ` [PATCH v7 10/26] rust: treewide: switch to our kernel `Box` type Danilo Krummrich
` (16 subsequent siblings)
25 siblings, 2 replies; 77+ messages in thread
From: Danilo Krummrich @ 2024-09-11 22:52 UTC (permalink / raw)
To: ojeda, alex.gaynor, wedsonaf, boqun.feng, gary, bjorn3_gh,
benno.lossin, a.hindborg, aliceryhl, akpm
Cc: daniel.almeida, faith.ekstrand, boris.brezillon, lina, mcanal,
zhiw, cjia, jhubbard, airlied, ajanulgu, lyude, linux-kernel,
rust-for-linux, linux-mm, 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.
Signed-off-by: Danilo Krummrich <dakr@kernel.org>
---
rust/kernel/alloc.rs | 6 +
rust/kernel/alloc/kbox.rs | 465 ++++++++++++++++++++++++++++++++++++++
rust/kernel/prelude.rs | 2 +-
3 files changed, 472 insertions(+), 1 deletion(-)
create mode 100644 rust/kernel/alloc/kbox.rs
diff --git a/rust/kernel/alloc.rs b/rust/kernel/alloc.rs
index 7a405d6f3034..b1f28334b27a 100644
--- a/rust/kernel/alloc.rs
+++ b/rust/kernel/alloc.rs
@@ -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;
diff --git a/rust/kernel/alloc/kbox.rs b/rust/kernel/alloc/kbox.rs
new file mode 100644
index 000000000000..6188494f040d
--- /dev/null
+++ b/rust/kernel/alloc/kbox.rs
@@ -0,0 +1,465 @@
+// 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;
+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`.
+///
+/// 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 fallible.
+///
+/// `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;
+/// struct Huge([u8; SIZE]);
+///
+/// assert!(KBox::<Huge>::new_uninit(GFP_KERNEL | __GFP_NOWARN).is_err());
+/// ```
+///
+/// ```
+/// # use kernel::bindings;
+/// const SIZE: usize = bindings::KMALLOC_MAX_SIZE as usize + 1;
+/// struct Huge([u8; SIZE]);
+///
+/// assert!(KVBox::<Huge>::new_uninit(GFP_KERNEL).is_ok());
+/// ```
+///
+/// # Invariants
+///
+/// `self.0` is always properly aligned and either points to memory allocated with `A` or, for
+/// zero-sized types, is a dangling, well aligned pointer.
+#[repr(transparent)]
+pub struct Box<T: ?Sized, A: Allocator>(NonNull<T>, PhantomData<A>);
+
+/// Type alias for [`Box`] with a [`Kmalloc`] allocator.
+///
+/// # Examples
+///
+/// ```
+/// let b = KBox::new(24_u64, GFP_KERNEL)?;
+///
+/// assert_eq!(*b, 24_u64);
+/// # Ok::<(), Error>(())
+/// ```
+pub type KBox<T> = Box<T, super::allocator::Kmalloc>;
+
+/// Type alias for [`Box`] with a [`Vmalloc`] allocator.
+///
+/// # Examples
+///
+/// ```
+/// let b = VBox::new(24_u64, GFP_KERNEL)?;
+///
+/// assert_eq!(*b, 24_u64);
+/// # Ok::<(), Error>(())
+/// ```
+pub type VBox<T> = Box<T, super::allocator::Vmalloc>;
+
+/// Type alias for [`Box`] with a [`KVmalloc`] allocator.
+///
+/// # Examples
+///
+/// ```
+/// let b = KVBox::new(24_u64, GFP_KERNEL)?;
+///
+/// assert_eq!(*b, 24_u64);
+/// # Ok::<(), Error>(())
+/// ```
+pub type KVBox<T> = Box<T, super::allocator::KVmalloc>;
+
+// 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.
+ ///
+ /// # 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`.
+ ///
+ /// For ZSTs, `raw` must be a dangling, well aligned pointer.
+ #[inline]
+ pub const unsafe fn from_raw(raw: *mut T) -> Self {
+ // INVARIANT: Validity of `raw` is guaranteed by the safety preconditions of this function.
+ // SAFETY: By the safety preconditions of this function, `raw` is not a NULL pointer.
+ Self(unsafe { NonNull::new_unchecked(raw) }, PhantomData::<A>)
+ }
+
+ /// Consumes the `Box<T, A>` and returns a raw pointer.
+ ///
+ /// This will not run the destructor of `T` and for non-ZSTs the allocation will stay alive
+ /// indefinitely. Use [`Box::from_raw`] to recover the [`Box`], drop the value and free the
+ /// allocation, if any.
+ ///
+ /// # Examples
+ ///
+ /// ```
+ /// let x = KBox::new(24, GFP_KERNEL)?;
+ /// let ptr = KBox::into_raw(x);
+ /// let x = unsafe { KBox::from_raw(ptr) };
+ ///
+ /// assert_eq!(*x, 24);
+ /// # Ok::<(), Error>(())
+ /// ```
+ #[inline]
+ pub fn into_raw(b: Self) -> *mut T {
+ let b = ManuallyDrop::new(b);
+
+ 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.
+ ///
+ /// # 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.cast()) }
+ }
+
+ /// 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 `b`'s value.
+ unsafe { Self::assume_init(b) }
+ }
+}
+
+impl<T, A> Box<T, A>
+where
+ A: Allocator,
+{
+ fn is_zst() -> bool {
+ core::mem::size_of::<T>() == 0
+ }
+
+ /// Creates a new `Box<T, A>` and initializes its contents with `x`.
+ ///
+ /// New memory is allocated with `A`. The allocation may fail, in which case an error is
+ /// returned. For ZSTs no memory is allocated.
+ pub fn new(x: T, flags: Flags) -> Result<Self, AllocError> {
+ let b = Self::new_uninit(flags)?;
+ Ok(Box::write(b, x))
+ }
+
+ /// Creates a new `Box<T, A>` with uninitialized contents.
+ ///
+ /// New memory is allocated with `A`. The allocation may fail, in which case an error is
+ /// returned. For ZSTs no memory is allocated.
+ ///
+ /// # Examples
+ ///
+ /// ```
+ /// let b = KBox::<u64>::new_uninit(GFP_KERNEL)?;
+ /// let b = KBox::write(b, 24);
+ ///
+ /// assert_eq!(*b, 24_u64);
+ /// # Ok::<(), Error>(())
+ /// ```
+ pub fn new_uninit(flags: Flags) -> Result<Box<MaybeUninit<T>, A>, AllocError> {
+ let ptr = if Self::is_zst() {
+ NonNull::dangling()
+ } else {
+ let layout = core::alloc::Layout::new::<MaybeUninit<T>>();
+ let ptr = A::alloc(layout, flags)?;
+
+ ptr.cast()
+ };
+
+ // INVARIANT: `ptr` is either a dangling pointer or points to memory allocated with `A`,
+ // which is sufficient in size and alignment for storing a `T`.
+ Ok(Box(ptr, PhantomData::<A>))
+ }
+
+ /// Constructs a new `Pin<Box<T, A>>`. If `T` does not implement [`Unpin`], then `x` will be
+ /// pinned in memory and can't be moved.
+ #[inline]
+ pub fn pin(x: T, flags: Flags) -> Result<Pin<Box<T, A>>, AllocError>
+ where
+ A: 'static,
+ {
+ Ok(Self::new(x, flags)?.into())
+ }
+
+ /// Forgets the contents (does not run the destructor), but keeps the allocation.
+ fn forget_contents(this: Self) -> Box<MaybeUninit<T>, A> {
+ let ptr = Self::into_raw(this);
+
+ // SAFETY: `ptr` is valid, because it came from `Box::into_raw`.
+ unsafe { Box::from_raw(ptr.cast()) }
+ }
+
+ /// Drops the contents, but keeps the allocation.
+ ///
+ /// # Examples
+ ///
+ /// ```
+ /// let value = KBox::new([0; 32], GFP_KERNEL)?;
+ /// assert_eq!(*value, [0; 32]);
+ /// let value = KBox::drop_contents(value);
+ /// // Now we can re-use `value`:
+ /// let value = KBox::write(value, [1; 32]);
+ /// assert_eq!(*value, [1; 32]);
+ /// # Ok::<(), Error>(())
+ /// ```
+ pub fn drop_contents(this: Self) -> Box<MaybeUninit<T>, A> {
+ let ptr = this.0.as_ptr();
+
+ // SAFETY: `ptr` is valid, because it came from `this`. After this call we never access the
+ // value stored in `this` again.
+ unsafe { core::ptr::drop_in_place(ptr) };
+
+ Self::forget_contents(this)
+ }
+
+ /// Moves the `Box`' value out of the `Box` and consumes the `Box`.
+ pub fn into_inner(b: Self) -> T {
+ // SAFETY: By the type invariant `&*b` is valid for `read`.
+ let value = unsafe { core::ptr::read(&*b) };
+ let _ = Self::forget_contents(b);
+ value
+ }
+}
+
+impl<T, A> From<Box<T, A>> for Pin<Box<T, A>>
+where
+ T: ?Sized,
+ A: Allocator,
+{
+ /// Converts a `Box<T, A>` into a `Pin<Box<T, A>>`. If `T` does not implement [`Unpin`], then
+ /// `*b` will be pinned in memory and can't be moved.
+ ///
+ /// This moves `b` into `Pin` without moving `*b` or allocating and copying any memory.
+ fn from(b: Box<T, A>) -> Self {
+ // SAFETY: The value wrapped inside a `Pin<Box<T, A>>` cannot be moved or replaced as long
+ // as `T` does not implement `Unpin`.
+ unsafe { Pin::new_unchecked(b) }
+ }
+}
+
+impl<T, A> InPlaceWrite<T> for Box<MaybeUninit<T>, A>
+where
+ A: Allocator + 'static,
+{
+ type Initialized = Box<T, A>;
+
+ fn write_init<E>(mut self, init: impl Init<T, E>) -> Result<Self::Initialized, E> {
+ let slot = self.as_mut_ptr();
+ // SAFETY: When init errors/panics, slot will get deallocated but not dropped,
+ // slot is valid.
+ unsafe { init.__init(slot)? };
+ // SAFETY: All fields have been initialized.
+ Ok(unsafe { Box::assume_init(self) })
+ }
+
+ fn write_pin_init<E>(mut self, init: impl PinInit<T, E>) -> Result<Pin<Self::Initialized>, E> {
+ let slot = self.as_mut_ptr();
+ // SAFETY: When init errors/panics, slot will get deallocated but not dropped,
+ // slot is valid and will not be moved, because we pin it later.
+ unsafe { init.__pinned_init(slot)? };
+ // SAFETY: All fields have been initialized.
+ Ok(unsafe { Box::assume_init(self) }.into())
+ }
+}
+
+impl<T, A> InPlaceInit<T> for Box<T, A>
+where
+ A: Allocator + 'static,
+{
+ type PinnedSelf = Pin<Self>;
+
+ #[inline]
+ fn try_pin_init<E>(init: impl PinInit<T, E>, flags: Flags) -> Result<Pin<Self>, E>
+ where
+ E: From<AllocError>,
+ {
+ Box::<_, A>::new_uninit(flags)?.write_pin_init(init)
+ }
+
+ #[inline]
+ fn try_init<E>(init: impl Init<T, E>, flags: Flags) -> Result<Self, E>
+ where
+ E: From<AllocError>,
+ {
+ Box::<_, A>::new_uninit(flags)?.write_init(init)
+ }
+}
+
+impl<T: 'static, A> ForeignOwnable for Box<T, A>
+where
+ A: Allocator,
+{
+ type Borrowed<'a> = &'a 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() }
+ }
+}
+
+impl<T: 'static, A> ForeignOwnable for Pin<Box<T, A>>
+where
+ A: Allocator,
+{
+ type Borrowed<'a> = Pin<&'a T>;
+
+ fn into_foreign(self) -> *const core::ffi::c_void {
+ // SAFETY: We are still treating the box as pinned.
+ Box::into_raw(unsafe { Pin::into_inner_unchecked(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 { Pin::new_unchecked(Box::from_raw(ptr as _)) }
+ }
+
+ unsafe fn borrow<'a>(ptr: *const core::ffi::c_void) -> Pin<&'a T> {
+ // SAFETY: The safety requirements for this function ensure that the object is still alive,
+ // so it is safe to dereference the raw pointer.
+ // The safety requirements of `from_foreign` also ensure that the object remains alive for
+ // the lifetime of the returned value.
+ let r = unsafe { &*ptr.cast() };
+
+ // SAFETY: This pointer originates from a `Pin<Box<T>>`.
+ unsafe { Pin::new_unchecked(r) }
+ }
+}
+
+impl<T, A> Deref for Box<T, A>
+where
+ T: ?Sized,
+ A: Allocator,
+{
+ type Target = T;
+
+ fn deref(&self) -> &T {
+ // SAFETY: `self.0` is always properly aligned, dereferenceable and points to an initialized
+ // instance of `T`.
+ unsafe { self.0.as_ref() }
+ }
+}
+
+impl<T, A> DerefMut for Box<T, A>
+where
+ T: ?Sized,
+ A: Allocator,
+{
+ fn deref_mut(&mut self) -> &mut T {
+ // SAFETY: `self.0` is always properly aligned, dereferenceable and points to an initialized
+ // instance of `T`.
+ unsafe { self.0.as_mut() }
+ }
+}
+
+impl<T, A> fmt::Debug for Box<T, A>
+where
+ T: ?Sized + fmt::Debug,
+ A: Allocator,
+{
+ fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
+ fmt::Debug::fmt(&**self, f)
+ }
+}
+
+impl<T, A> Drop for Box<T, A>
+where
+ T: ?Sized,
+ A: Allocator,
+{
+ fn drop(&mut self) {
+ let size = core::mem::size_of_val::<T>(self);
+
+ // SAFETY: The pointer in `self.0` is guaranteed to be valid by the type invariant.
+ unsafe { core::ptr::drop_in_place::<T>(self.deref_mut()) };
+
+ if size != 0 {
+ // SAFETY: As `size` is not zero, `self.0` was previously allocated with `A`.
+ unsafe { A::free(self.0.cast()) };
+ }
+ }
+}
diff --git a/rust/kernel/prelude.rs b/rust/kernel/prelude.rs
index 4571daec0961..a9210634a8c3 100644
--- a/rust/kernel/prelude.rs
+++ b/rust/kernel/prelude.rs
@@ -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
^ permalink raw reply related [flat|nested] 77+ messages in thread
* [PATCH v7 10/26] rust: treewide: switch to our kernel `Box` type
2024-09-11 22:52 [PATCH v7 00/26] Generic `Allocator` support for Rust Danilo Krummrich
` (8 preceding siblings ...)
2024-09-11 22:52 ` [PATCH v7 09/26] rust: alloc: implement kernel `Box` Danilo Krummrich
@ 2024-09-11 22:52 ` Danilo Krummrich
2024-09-28 18:59 ` Gary Guo
2024-09-11 22:52 ` [PATCH v7 11/26] rust: alloc: remove extension of std's `Box` Danilo Krummrich
` (15 subsequent siblings)
25 siblings, 1 reply; 77+ messages in thread
From: Danilo Krummrich @ 2024-09-11 22:52 UTC (permalink / raw)
To: ojeda, alex.gaynor, wedsonaf, boqun.feng, gary, bjorn3_gh,
benno.lossin, a.hindborg, aliceryhl, akpm
Cc: daniel.almeida, faith.ekstrand, boris.brezillon, lina, mcanal,
zhiw, cjia, jhubbard, airlied, ajanulgu, lyude, linux-kernel,
rust-for-linux, linux-mm, Danilo Krummrich
Now that we got the kernel `Box` type in place, convert all existing
`Box` users to make use of it.
Reviewed-by: Alice Ryhl <aliceryhl@google.com>
Reviewed-by: Benno Lossin <benno.lossin@proton.me>
Signed-off-by: Danilo Krummrich <dakr@kernel.org>
---
drivers/block/rnull.rs | 4 +--
rust/kernel/init.rs | 51 ++++++++++++++++---------------
rust/kernel/init/__internal.rs | 2 +-
rust/kernel/rbtree.rs | 49 ++++++++++++++++-------------
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, 81 insertions(+), 76 deletions(-)
diff --git a/drivers/block/rnull.rs b/drivers/block/rnull.rs
index b0227cf9ddd3..5de7223beb4d 100644
--- a/drivers/block/rnull.rs
+++ b/drivers/block/rnull.rs
@@ -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 })
}
diff --git a/rust/kernel/init.rs b/rust/kernel/init.rs
index a17ac8762d8f..e057b374f255 100644
--- a/rust/kernel/init.rs
+++ b/rust/kernel/init.rs
@@ -13,7 +13,7 @@
//! To initialize a `struct` with an in-place constructor you will need two things:
//! - an in-place constructor,
//! - a memory location that can hold your `struct` (this can be the [stack], an [`Arc<T>`],
-//! [`UniqueArc<T>`], [`Box<T>`] or any other smart pointer that implements [`InPlaceInit`]).
+//! [`UniqueArc<T>`], [`KBox<T>`] or any other smart pointer that implements [`InPlaceInit`]).
//!
//! To get an in-place constructor there are generally three options:
//! - directly creating an in-place constructor using the [`pin_init!`] macro,
@@ -68,7 +68,7 @@
//! # a <- new_mutex!(42, "Foo::a"),
//! # b: 24,
//! # });
-//! let foo: Result<Pin<Box<Foo>>> = Box::pin_init(foo, GFP_KERNEL);
+//! let foo: Result<Pin<KBox<Foo>>> = KBox::pin_init(foo, GFP_KERNEL);
//! ```
//!
//! For more information see the [`pin_init!`] macro.
@@ -93,14 +93,14 @@
//! struct DriverData {
//! #[pin]
//! status: Mutex<i32>,
-//! buffer: Box<[u8; 1_000_000]>,
+//! buffer: KBox<[u8; 1_000_000]>,
//! }
//!
//! impl DriverData {
//! fn new() -> impl PinInit<Self, Error> {
//! try_pin_init!(Self {
//! status <- new_mutex!(0, "DriverData::status"),
-//! buffer: Box::init(kernel::init::zeroed(), GFP_KERNEL)?,
+//! buffer: KBox::init(kernel::init::zeroed(), GFP_KERNEL)?,
//! })
//! }
//! }
@@ -211,7 +211,7 @@
//! [`pin_init!`]: crate::pin_init!
use crate::{
- alloc::{box_ext::BoxExt, AllocError, Flags},
+ alloc::{box_ext::BoxExt, AllocError, Flags, KBox},
error::{self, Error},
sync::Arc,
sync::UniqueArc,
@@ -298,7 +298,7 @@ macro_rules! stack_pin_init {
/// struct Foo {
/// #[pin]
/// a: Mutex<usize>,
-/// b: Box<Bar>,
+/// b: KBox<Bar>,
/// }
///
/// struct Bar {
@@ -307,7 +307,7 @@ macro_rules! stack_pin_init {
///
/// stack_try_pin_init!(let foo: Result<Pin<&mut Foo>, AllocError> = pin_init!(Foo {
/// a <- new_mutex!(42),
-/// b: Box::new(Bar {
+/// b: KBox::new(Bar {
/// x: 64,
/// }, GFP_KERNEL)?,
/// }));
@@ -324,7 +324,7 @@ macro_rules! stack_pin_init {
/// struct Foo {
/// #[pin]
/// a: Mutex<usize>,
-/// b: Box<Bar>,
+/// b: KBox<Bar>,
/// }
///
/// struct Bar {
@@ -333,7 +333,7 @@ macro_rules! stack_pin_init {
///
/// stack_try_pin_init!(let foo: Pin<&mut Foo> =? pin_init!(Foo {
/// a <- new_mutex!(42),
-/// b: Box::new(Bar {
+/// b: KBox::new(Bar {
/// x: 64,
/// }, GFP_KERNEL)?,
/// }));
@@ -392,7 +392,7 @@ macro_rules! stack_try_pin_init {
/// },
/// });
/// # initializer }
-/// # Box::pin_init(demo(), GFP_KERNEL).unwrap();
+/// # KBox::pin_init(demo(), GFP_KERNEL).unwrap();
/// ```
///
/// Arbitrary Rust expressions can be used to set the value of a variable.
@@ -462,7 +462,7 @@ macro_rules! stack_try_pin_init {
/// # })
/// # }
/// # }
-/// let foo = Box::pin_init(Foo::new(), GFP_KERNEL);
+/// let foo = KBox::pin_init(Foo::new(), GFP_KERNEL);
/// ```
///
/// They can also easily embed it into their own `struct`s:
@@ -594,7 +594,7 @@ macro_rules! pin_init {
/// use kernel::{init::{self, PinInit}, error::Error};
/// #[pin_data]
/// struct BigBuf {
-/// big: Box<[u8; 1024 * 1024 * 1024]>,
+/// big: KBox<[u8; 1024 * 1024 * 1024]>,
/// small: [u8; 1024 * 1024],
/// ptr: *mut u8,
/// }
@@ -602,7 +602,7 @@ macro_rules! pin_init {
/// impl BigBuf {
/// fn new() -> impl PinInit<Self, Error> {
/// try_pin_init!(Self {
-/// big: Box::init(init::zeroed(), GFP_KERNEL)?,
+/// big: KBox::init(init::zeroed(), GFP_KERNEL)?,
/// small: [0; 1024 * 1024],
/// ptr: core::ptr::null_mut(),
/// }? Error)
@@ -694,16 +694,16 @@ macro_rules! init {
/// # Examples
///
/// ```rust
-/// use kernel::{init::{PinInit, zeroed}, error::Error};
+/// use kernel::{alloc::KBox, init::{PinInit, zeroed}, error::Error};
/// struct BigBuf {
-/// big: Box<[u8; 1024 * 1024 * 1024]>,
+/// big: KBox<[u8; 1024 * 1024 * 1024]>,
/// small: [u8; 1024 * 1024],
/// }
///
/// impl BigBuf {
/// fn new() -> impl Init<Self, Error> {
/// try_init!(Self {
-/// big: Box::init(zeroed(), GFP_KERNEL)?,
+/// big: KBox::init(zeroed(), GFP_KERNEL)?,
/// small: [0; 1024 * 1024],
/// }? Error)
/// }
@@ -814,8 +814,8 @@ macro_rules! assert_pinned {
/// A pin-initializer for the type `T`.
///
/// To use this initializer, you will need a suitable memory location that can hold a `T`. This can
-/// be [`Box<T>`], [`Arc<T>`], [`UniqueArc<T>`] or even the stack (see [`stack_pin_init!`]). Use the
-/// [`InPlaceInit::pin_init`] function of a smart pointer like [`Arc<T>`] on this.
+/// be [`KBox<T>`], [`Arc<T>`], [`UniqueArc<T>`] or even the stack (see [`stack_pin_init!`]). Use
+/// the [`InPlaceInit::pin_init`] function of a smart pointer like [`Arc<T>`] on this.
///
/// Also see the [module description](self).
///
@@ -894,7 +894,7 @@ fn pin_chain<F>(self, f: F) -> ChainPinInit<Self, F, T, E>
}
/// An initializer returned by [`PinInit::pin_chain`].
-pub struct ChainPinInit<I, F, T: ?Sized, E>(I, F, __internal::Invariant<(E, Box<T>)>);
+pub struct ChainPinInit<I, F, T: ?Sized, E>(I, F, __internal::Invariant<(E, KBox<T>)>);
// SAFETY: The `__pinned_init` function is implemented such that it
// - returns `Ok(())` on successful initialization,
@@ -920,8 +920,8 @@ unsafe fn __pinned_init(self, slot: *mut T) -> Result<(), E> {
/// An initializer for `T`.
///
/// To use this initializer, you will need a suitable memory location that can hold a `T`. This can
-/// be [`Box<T>`], [`Arc<T>`], [`UniqueArc<T>`] or even the stack (see [`stack_pin_init!`]). Use the
-/// [`InPlaceInit::init`] function of a smart pointer like [`Arc<T>`] on this. Because
+/// be [`KBox<T>`], [`Arc<T>`], [`UniqueArc<T>`] or even the stack (see [`stack_pin_init!`]). Use
+/// the [`InPlaceInit::init`] function of a smart pointer like [`Arc<T>`] on this. Because
/// [`PinInit<T, E>`] is a super trait, you can use every function that takes it as well.
///
/// Also see the [module description](self).
@@ -993,7 +993,7 @@ fn chain<F>(self, f: F) -> ChainInit<Self, F, T, E>
}
/// An initializer returned by [`Init::chain`].
-pub struct ChainInit<I, F, T: ?Sized, E>(I, F, __internal::Invariant<(E, Box<T>)>);
+pub struct ChainInit<I, F, T: ?Sized, E>(I, F, __internal::Invariant<(E, KBox<T>)>);
// SAFETY: The `__init` function is implemented such that it
// - returns `Ok(())` on successful initialization,
@@ -1077,8 +1077,9 @@ pub fn uninit<T, E>() -> impl Init<MaybeUninit<T>, E> {
/// # Examples
///
/// ```rust
-/// use kernel::{error::Error, init::init_array_from_fn};
-/// let array: Box<[usize; 1_000]> = Box::init::<Error>(init_array_from_fn(|i| i), GFP_KERNEL).unwrap();
+/// use kernel::{alloc::KBox, error::Error, init::init_array_from_fn};
+/// let array: KBox<[usize; 1_000]> =
+/// KBox::init::<Error>(init_array_from_fn(|i| i), GFP_KERNEL).unwrap();
/// assert_eq!(array.len(), 1_000);
/// ```
pub fn init_array_from_fn<I, const N: usize, T, E>(
@@ -1451,7 +1452,7 @@ macro_rules! impl_zeroable {
//
// In this case we are allowed to use `T: ?Sized`, since all zeros is the `None` variant.
{<T: ?Sized>} Option<NonNull<T>>,
- {<T: ?Sized>} Option<Box<T>>,
+ {<T: ?Sized>} Option<KBox<T>>,
// SAFETY: `null` pointer is valid.
//
diff --git a/rust/kernel/init/__internal.rs b/rust/kernel/init/__internal.rs
index 13cefd37512f..b31f498f4f8e 100644
--- a/rust/kernel/init/__internal.rs
+++ b/rust/kernel/init/__internal.rs
@@ -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 {
diff --git a/rust/kernel/rbtree.rs b/rust/kernel/rbtree.rs
index 25eb36fd1cdc..a63d4b0fe485 100644
--- a/rust/kernel/rbtree.rs
+++ b/rust/kernel/rbtree.rs
@@ -7,7 +7,6 @@
//! Reference: <https://docs.kernel.org/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,
@@ -497,7 +496,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())) };
}
}
}
@@ -764,7 +763,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.
@@ -809,7 +808,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
@@ -1035,7 +1034,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> {
@@ -1043,7 +1042,7 @@ impl<K, V> RBTreeNodeReservation<K, V> {
/// call to [`RBTree::insert`].
pub fn new(flags: Flags) -> Result<RBTreeNodeReservation<K, V>> {
Ok(RBTreeNodeReservation {
- node: <Box<_> as BoxExt<_>>::new_uninit(flags)?,
+ node: KBox::new_uninit(flags)?,
})
}
}
@@ -1059,14 +1058,15 @@ impl<K, V> RBTreeNodeReservation<K, V> {
/// Initialises a node reservation.
///
/// It then becomes an [`RBTreeNode`] that can be inserted into a tree.
- pub fn into_node(mut self, key: K, value: V) -> RBTreeNode<K, V> {
- self.node.write(Node {
- key,
- value,
- links: bindings::rb_node::default(),
- });
- // SAFETY: We just wrote to it.
- let node = unsafe { self.node.assume_init() };
+ pub fn into_node(self, key: K, value: V) -> RBTreeNode<K, V> {
+ let node = KBox::write(
+ self.node,
+ Node {
+ key,
+ value,
+ links: bindings::rb_node::default(),
+ },
+ );
RBTreeNode { node }
}
}
@@ -1076,7 +1076,7 @@ pub fn into_node(mut 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> {
@@ -1088,7 +1088,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)
}
}
@@ -1110,7 +1112,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),
}
}
}
@@ -1161,7 +1163,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.
@@ -1235,21 +1237,24 @@ pub fn remove_node(self) -> RBTreeNode<K, V> {
// 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(container_of!(self.node_links, Node<K, V>, links).cast_mut())
+ KBox::from_raw(container_of!(self.node_links, Node<K, V>, links).cast_mut())
},
}
}
/// 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.
@@ -1265,7 +1270,7 @@ fn replace(self, node: RBTreeNode<K, V>) -> RBTreeNode<K, V> {
// - `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(container_of!(self.node_links, Node<K, V>, links).cast_mut()) };
+ unsafe { KBox::from_raw(container_of!(self.node_links, Node<K, V>, links).cast_mut()) };
RBTreeNode { node: old_node }
}
diff --git a/rust/kernel/sync/arc.rs b/rust/kernel/sync/arc.rs
index 3021f30fd822..d7adbad7d3b4 100644
--- a/rust/kernel/sync/arc.rs
+++ b/rust/kernel/sync/arc.rs
@@ -17,13 +17,12 @@
//! [`Arc`]: https://doc.rust-lang.org/std/sync/struct.Arc.html
use crate::{
- alloc::{box_ext::BoxExt, AllocError, Flags},
+ alloc::{AllocError, Flags, KBox},
bindings,
init::{self, InPlaceInit, Init, PinInit},
try_init,
types::{ForeignOwnable, Opaque},
};
-use alloc::boxed::Box;
use core::{
alloc::Layout,
fmt,
@@ -204,11 +203,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()) })
}
}
@@ -401,8 +400,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())) };
}
}
}
@@ -647,7 +646,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) }),
@@ -657,8 +656,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()) },
})
}
}
diff --git a/rust/kernel/sync/condvar.rs b/rust/kernel/sync/condvar.rs
index 2b306afbe56d..2081932bb4b9 100644
--- a/rust/kernel/sync/condvar.rs
+++ b/rust/kernel/sync/condvar.rs
@@ -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)
diff --git a/rust/kernel/sync/lock/mutex.rs b/rust/kernel/sync/lock/mutex.rs
index 30632070ee67..f8f6d530db7d 100644
--- a/rust/kernel/sync/lock/mutex.rs
+++ b/rust/kernel/sync/lock/mutex.rs
@@ -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);
diff --git a/rust/kernel/sync/lock/spinlock.rs b/rust/kernel/sync/lock/spinlock.rs
index ea5c5bc1ce12..a9096a4dc42a 100644
--- a/rust/kernel/sync/lock/spinlock.rs
+++ b/rust/kernel/sync/lock/spinlock.rs
@@ -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);
diff --git a/rust/kernel/workqueue.rs b/rust/kernel/workqueue.rs
index 553a5cba2adc..94318472507f 100644
--- a/rust/kernel/workqueue.rs
+++ b/rust/kernel/workqueue.rs
@@ -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) };
diff --git a/rust/macros/lib.rs b/rust/macros/lib.rs
index a626b1145e5c..ab93111a048c 100644
--- a/rust/macros/lib.rs
+++ b/rust/macros/lib.rs
@@ -243,7 +243,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]>,
/// }
/// ```
///
@@ -252,7 +252,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,
/// }
///
@@ -282,7 +282,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,
/// }
///
--
2.46.0
^ permalink raw reply related [flat|nested] 77+ messages in thread
* [PATCH v7 11/26] rust: alloc: remove extension of std's `Box`
2024-09-11 22:52 [PATCH v7 00/26] Generic `Allocator` support for Rust Danilo Krummrich
` (9 preceding siblings ...)
2024-09-11 22:52 ` [PATCH v7 10/26] rust: treewide: switch to our kernel `Box` type Danilo Krummrich
@ 2024-09-11 22:52 ` Danilo Krummrich
2024-09-28 19:00 ` Gary Guo
2024-09-11 22:52 ` [PATCH v7 12/26] rust: alloc: add `Box` to prelude Danilo Krummrich
` (14 subsequent siblings)
25 siblings, 1 reply; 77+ messages in thread
From: Danilo Krummrich @ 2024-09-11 22:52 UTC (permalink / raw)
To: ojeda, alex.gaynor, wedsonaf, boqun.feng, gary, bjorn3_gh,
benno.lossin, a.hindborg, aliceryhl, akpm
Cc: daniel.almeida, faith.ekstrand, boris.brezillon, lina, mcanal,
zhiw, cjia, jhubbard, airlied, ajanulgu, lyude, linux-kernel,
rust-for-linux, linux-mm, Danilo Krummrich
Now that all existing `Box` users were moved to the kernel `Box` type,
remove the `BoxExt` extension and all other related extensions.
Reviewed-by: Alice Ryhl <aliceryhl@google.com>
Reviewed-by: Benno Lossin <benno.lossin@proton.me>
Signed-off-by: Danilo Krummrich <dakr@kernel.org>
---
rust/kernel/alloc.rs | 1 -
rust/kernel/alloc/box_ext.rs | 87 ------------------------------------
rust/kernel/init.rs | 46 +------------------
rust/kernel/lib.rs | 1 -
rust/kernel/prelude.rs | 4 +-
rust/kernel/types.rs | 50 ---------------------
6 files changed, 3 insertions(+), 186 deletions(-)
delete mode 100644 rust/kernel/alloc/box_ext.rs
diff --git a/rust/kernel/alloc.rs b/rust/kernel/alloc.rs
index b1f28334b27a..1d0cb6f12af9 100644
--- a/rust/kernel/alloc.rs
+++ b/rust/kernel/alloc.rs
@@ -4,7 +4,6 @@
#[cfg(not(any(test, testlib)))]
pub mod allocator;
-pub mod box_ext;
pub mod kbox;
pub mod vec_ext;
diff --git a/rust/kernel/alloc/box_ext.rs b/rust/kernel/alloc/box_ext.rs
deleted file mode 100644
index 5b1550d620fd..000000000000
--- a/rust/kernel/alloc/box_ext.rs
+++ /dev/null
@@ -1,87 +0,0 @@
-// SPDX-License-Identifier: GPL-2.0
-
-//! Extensions to [`Box`] for fallible allocations.
-
-use super::{AllocError, Flags};
-use alloc::boxed::Box;
-use core::{mem::MaybeUninit, ptr, result::Result};
-
-/// Extensions to [`Box`].
-pub trait BoxExt<T>: Sized {
- /// Allocates a new box.
- ///
- /// The allocation may fail, in which case an error is returned.
- fn new(x: T, flags: Flags) -> Result<Self, AllocError>;
-
- /// Allocates a new uninitialised box.
- ///
- /// The allocation may fail, in which case an error is returned.
- fn new_uninit(flags: Flags) -> Result<Box<MaybeUninit<T>>, AllocError>;
-
- /// Drops the contents, but keeps the allocation.
- ///
- /// # Examples
- ///
- /// ```
- /// use kernel::alloc::{flags, box_ext::BoxExt};
- /// let value = Box::new([0; 32], flags::GFP_KERNEL)?;
- /// assert_eq!(*value, [0; 32]);
- /// let mut value = Box::drop_contents(value);
- /// // Now we can re-use `value`:
- /// value.write([1; 32]);
- /// // SAFETY: We just wrote to it.
- /// let value = unsafe { value.assume_init() };
- /// assert_eq!(*value, [1; 32]);
- /// # Ok::<(), Error>(())
- /// ```
- fn drop_contents(this: Self) -> Box<MaybeUninit<T>>;
-}
-
-impl<T> BoxExt<T> for Box<T> {
- fn new(x: T, flags: Flags) -> Result<Self, AllocError> {
- let b = <Self as BoxExt<_>>::new_uninit(flags)?;
- Ok(Box::write(b, x))
- }
-
- #[cfg(any(test, testlib))]
- fn new_uninit(_flags: Flags) -> Result<Box<MaybeUninit<T>>, AllocError> {
- Ok(Box::new_uninit())
- }
-
- #[cfg(not(any(test, testlib)))]
- fn new_uninit(flags: Flags) -> Result<Box<MaybeUninit<T>>, AllocError> {
- let ptr = if core::mem::size_of::<MaybeUninit<T>>() == 0 {
- core::ptr::NonNull::<_>::dangling().as_ptr()
- } else {
- let layout = core::alloc::Layout::new::<MaybeUninit<T>>();
-
- // SAFETY: Memory is being allocated (first arg is null). The only other source of
- // safety issues is sleeping on atomic context, which is addressed by klint. Lastly,
- // the type is not a SZT (checked above).
- let ptr =
- unsafe { super::allocator::krealloc_aligned(core::ptr::null_mut(), layout, flags) };
- if ptr.is_null() {
- return Err(AllocError);
- }
-
- ptr.cast::<MaybeUninit<T>>()
- };
-
- // SAFETY: For non-zero-sized types, we allocate above using the global allocator. For
- // zero-sized types, we use `NonNull::dangling`.
- Ok(unsafe { Box::from_raw(ptr) })
- }
-
- fn drop_contents(this: Self) -> Box<MaybeUninit<T>> {
- let ptr = Box::into_raw(this);
- // SAFETY: `ptr` is valid, because it came from `Box::into_raw`.
- unsafe { ptr::drop_in_place(ptr) };
-
- // CAST: `MaybeUninit<T>` is a transparent wrapper of `T`.
- let ptr = ptr.cast::<MaybeUninit<T>>();
-
- // SAFETY: `ptr` is valid for writes, because it came from `Box::into_raw` and it is valid for
- // reads, since the pointer came from `Box::into_raw` and the type is `MaybeUninit<T>`.
- unsafe { Box::from_raw(ptr) }
- }
-}
diff --git a/rust/kernel/init.rs b/rust/kernel/init.rs
index e057b374f255..2f562642e9a4 100644
--- a/rust/kernel/init.rs
+++ b/rust/kernel/init.rs
@@ -211,13 +211,12 @@
//! [`pin_init!`]: crate::pin_init!
use crate::{
- alloc::{box_ext::BoxExt, AllocError, Flags, KBox},
+ alloc::{AllocError, Flags, KBox},
error::{self, Error},
sync::Arc,
sync::UniqueArc,
types::{Opaque, ScopeGuard},
};
-use alloc::boxed::Box;
use core::{
cell::UnsafeCell,
convert::Infallible,
@@ -590,7 +589,6 @@ macro_rules! pin_init {
/// # Examples
///
/// ```rust
-/// # #![feature(new_uninit)]
/// use kernel::{init::{self, PinInit}, error::Error};
/// #[pin_data]
/// struct BigBuf {
@@ -1244,26 +1242,6 @@ fn try_init<E>(init: impl Init<T, E>, flags: Flags) -> Result<Self, E>
}
}
-impl<T> InPlaceInit<T> for Box<T> {
- type PinnedSelf = Pin<Self>;
-
- #[inline]
- fn try_pin_init<E>(init: impl PinInit<T, E>, flags: Flags) -> Result<Self::PinnedSelf, E>
- where
- E: From<AllocError>,
- {
- <Box<_> as BoxExt<_>>::new_uninit(flags)?.write_pin_init(init)
- }
-
- #[inline]
- fn try_init<E>(init: impl Init<T, E>, flags: Flags) -> Result<Self, E>
- where
- E: From<AllocError>,
- {
- <Box<_> as BoxExt<_>>::new_uninit(flags)?.write_init(init)
- }
-}
-
impl<T> InPlaceInit<T> for UniqueArc<T> {
type PinnedSelf = Pin<Self>;
@@ -1300,28 +1278,6 @@ pub trait InPlaceWrite<T> {
fn write_pin_init<E>(self, init: impl PinInit<T, E>) -> Result<Pin<Self::Initialized>, E>;
}
-impl<T> InPlaceWrite<T> for Box<MaybeUninit<T>> {
- type Initialized = Box<T>;
-
- fn write_init<E>(mut self, init: impl Init<T, E>) -> Result<Self::Initialized, E> {
- let slot = self.as_mut_ptr();
- // SAFETY: When init errors/panics, slot will get deallocated but not dropped,
- // slot is valid.
- unsafe { init.__init(slot)? };
- // SAFETY: All fields have been initialized.
- Ok(unsafe { self.assume_init() })
- }
-
- fn write_pin_init<E>(mut self, init: impl PinInit<T, E>) -> Result<Pin<Self::Initialized>, E> {
- let slot = self.as_mut_ptr();
- // SAFETY: When init errors/panics, slot will get deallocated but not dropped,
- // slot is valid and will not be moved, because we pin it later.
- unsafe { init.__pinned_init(slot)? };
- // SAFETY: All fields have been initialized.
- Ok(unsafe { self.assume_init() }.into())
- }
-}
-
impl<T> InPlaceWrite<T> for UniqueArc<MaybeUninit<T>> {
type Initialized = UniqueArc<T>;
diff --git a/rust/kernel/lib.rs b/rust/kernel/lib.rs
index f10b06a78b9d..eb5c593acfc0 100644
--- a/rust/kernel/lib.rs
+++ b/rust/kernel/lib.rs
@@ -14,7 +14,6 @@
#![no_std]
#![feature(coerce_unsized)]
#![feature(dispatch_from_dyn)]
-#![feature(new_uninit)]
#![feature(receiver_trait)]
#![feature(unsize)]
diff --git a/rust/kernel/prelude.rs b/rust/kernel/prelude.rs
index a9210634a8c3..c1f8e5c832e2 100644
--- a/rust/kernel/prelude.rs
+++ b/rust/kernel/prelude.rs
@@ -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};
diff --git a/rust/kernel/types.rs b/rust/kernel/types.rs
index 9e7ca066355c..53d3ddc0b98c 100644
--- a/rust/kernel/types.rs
+++ b/rust/kernel/types.rs
@@ -3,13 +3,11 @@
//! Kernel types.
use crate::init::{self, PinInit};
-use alloc::boxed::Box;
use core::{
cell::UnsafeCell,
marker::{PhantomData, PhantomPinned},
mem::{ManuallyDrop, MaybeUninit},
ops::{Deref, DerefMut},
- pin::Pin,
ptr::NonNull,
};
@@ -71,54 +69,6 @@ unsafe fn try_from_foreign(ptr: *const core::ffi::c_void) -> Option<Self> {
}
}
-impl<T: 'static> ForeignOwnable for Box<T> {
- type Borrowed<'a> = &'a T;
-
- fn into_foreign(self) -> *const core::ffi::c_void {
- Box::into_raw(self) as _
- }
-
- unsafe fn borrow<'a>(ptr: *const core::ffi::c_void) -> &'a T {
- // SAFETY: The safety requirements for this function ensure that the object is still alive,
- // so it is safe to dereference the raw pointer.
- // The safety requirements of `from_foreign` also ensure that the object remains alive for
- // the lifetime of the returned value.
- unsafe { &*ptr.cast() }
- }
-
- unsafe fn from_foreign(ptr: *const core::ffi::c_void) -> Self {
- // SAFETY: The safety requirements of this function ensure that `ptr` comes from a previous
- // call to `Self::into_foreign`.
- unsafe { Box::from_raw(ptr as _) }
- }
-}
-
-impl<T: 'static> ForeignOwnable for Pin<Box<T>> {
- type Borrowed<'a> = Pin<&'a T>;
-
- fn into_foreign(self) -> *const core::ffi::c_void {
- // SAFETY: We are still treating the box as pinned.
- Box::into_raw(unsafe { Pin::into_inner_unchecked(self) }) as _
- }
-
- unsafe fn borrow<'a>(ptr: *const core::ffi::c_void) -> Pin<&'a T> {
- // SAFETY: The safety requirements for this function ensure that the object is still alive,
- // so it is safe to dereference the raw pointer.
- // The safety requirements of `from_foreign` also ensure that the object remains alive for
- // the lifetime of the returned value.
- let r = unsafe { &*ptr.cast() };
-
- // SAFETY: This pointer originates from a `Pin<Box<T>>`.
- unsafe { Pin::new_unchecked(r) }
- }
-
- unsafe fn from_foreign(ptr: *const core::ffi::c_void) -> Self {
- // SAFETY: The safety requirements of this function ensure that `ptr` comes from a previous
- // call to `Self::into_foreign`.
- unsafe { Pin::new_unchecked(Box::from_raw(ptr as _)) }
- }
-}
-
impl ForeignOwnable for () {
type Borrowed<'a> = ();
--
2.46.0
^ permalink raw reply related [flat|nested] 77+ messages in thread
* [PATCH v7 12/26] rust: alloc: add `Box` to prelude
2024-09-11 22:52 [PATCH v7 00/26] Generic `Allocator` support for Rust Danilo Krummrich
` (10 preceding siblings ...)
2024-09-11 22:52 ` [PATCH v7 11/26] rust: alloc: remove extension of std's `Box` Danilo Krummrich
@ 2024-09-11 22:52 ` Danilo Krummrich
2024-09-28 19:00 ` Gary Guo
2024-09-11 22:52 ` [PATCH v7 13/26] rust: alloc: implement kernel `Vec` type Danilo Krummrich
` (13 subsequent siblings)
25 siblings, 1 reply; 77+ messages in thread
From: Danilo Krummrich @ 2024-09-11 22:52 UTC (permalink / raw)
To: ojeda, alex.gaynor, wedsonaf, boqun.feng, gary, bjorn3_gh,
benno.lossin, a.hindborg, aliceryhl, akpm
Cc: daniel.almeida, faith.ekstrand, boris.brezillon, lina, mcanal,
zhiw, cjia, jhubbard, airlied, ajanulgu, lyude, linux-kernel,
rust-for-linux, linux-mm, Danilo Krummrich
Now that we removed `BoxExt` and the corresponding includes in
prelude.rs, add the new kernel `Box` type instead.
Reviewed-by: Alice Ryhl <aliceryhl@google.com>
Reviewed-by: Benno Lossin <benno.lossin@proton.me>
Signed-off-by: Danilo Krummrich <dakr@kernel.org>
---
rust/kernel/prelude.rs | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/rust/kernel/prelude.rs b/rust/kernel/prelude.rs
index c1f8e5c832e2..d5f2fe42d093 100644
--- a/rust/kernel/prelude.rs
+++ b/rust/kernel/prelude.rs
@@ -14,7 +14,7 @@
#[doc(no_inline)]
pub use core::pin::Pin;
-pub use crate::alloc::{flags::*, vec_ext::VecExt, KBox, KVBox, VBox};
+pub use crate::alloc::{flags::*, vec_ext::VecExt, Box, KBox, KVBox, VBox};
#[doc(no_inline)]
pub use alloc::vec::Vec;
--
2.46.0
^ permalink raw reply related [flat|nested] 77+ messages in thread
* [PATCH v7 13/26] rust: alloc: implement kernel `Vec` type
2024-09-11 22:52 [PATCH v7 00/26] Generic `Allocator` support for Rust Danilo Krummrich
` (11 preceding siblings ...)
2024-09-11 22:52 ` [PATCH v7 12/26] rust: alloc: add `Box` to prelude Danilo Krummrich
@ 2024-09-11 22:52 ` Danilo Krummrich
2024-09-26 13:47 ` Benno Lossin
2024-09-28 19:14 ` Gary Guo
2024-09-11 22:52 ` [PATCH v7 14/26] rust: alloc: implement `IntoIterator` for `Vec` Danilo Krummrich
` (12 subsequent siblings)
25 siblings, 2 replies; 77+ messages in thread
From: Danilo Krummrich @ 2024-09-11 22:52 UTC (permalink / raw)
To: ojeda, alex.gaynor, wedsonaf, boqun.feng, gary, bjorn3_gh,
benno.lossin, a.hindborg, aliceryhl, akpm
Cc: daniel.almeida, faith.ekstrand, boris.brezillon, lina, mcanal,
zhiw, cjia, jhubbard, airlied, ajanulgu, lyude, linux-kernel,
rust-for-linux, linux-mm, Danilo Krummrich
`Vec` provides a contiguous growable array type with contents allocated
with the kernel's allocators (e.g. `Kmalloc`, `Vmalloc` or `KVmalloc`).
In contrast to Rust's stdlib `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.
Signed-off-by: Danilo Krummrich <dakr@kernel.org>
---
rust/kernel/alloc.rs | 6 +
rust/kernel/alloc/kvec.rs | 638 ++++++++++++++++++++++++++++++++++++++
rust/kernel/prelude.rs | 2 +-
3 files changed, 645 insertions(+), 1 deletion(-)
create mode 100644 rust/kernel/alloc/kvec.rs
diff --git a/rust/kernel/alloc.rs b/rust/kernel/alloc.rs
index 1d0cb6f12af9..4fb983b63d46 100644
--- a/rust/kernel/alloc.rs
+++ b/rust/kernel/alloc.rs
@@ -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;
diff --git a/rust/kernel/alloc/kvec.rs b/rust/kernel/alloc/kvec.rs
new file mode 100644
index 000000000000..631a44e19f35
--- /dev/null
+++ b/rust/kernel/alloc/kvec.rs
@@ -0,0 +1,638 @@
+// SPDX-License-Identifier: GPL-2.0
+
+//! Implementation of [`Vec`].
+
+use super::{
+ allocator::{KVmalloc, Kmalloc, Vmalloc},
+ 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)?;
+/// assert_eq!(v, [1, 2, 3, 4]);
+///
+/// # Ok::<(), Error>(())
+/// ```
+#[macro_export]
+macro_rules! kvec {
+ () => (
+ $crate::alloc::KVec::new()
+ );
+ ($elem:expr; $n:expr) => (
+ $crate::alloc::KVec::from_elem($elem, $n, GFP_KERNEL)
+ );
+ ($($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),
+ }
+ );
+}
+
+/// 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>`.
+///
+/// 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.
+///
+/// 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
+///
+/// - `self.ptr` is always properly aligned and either points to memory allocated with `A` or, for
+/// zero-sized types, is a dangling, well aligned pointer.
+///
+/// - `self.len` always represents the exact number of elements stored in the vector.
+///
+/// - `self.cap` represents the absolute number of elements that can be stored 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).
+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>,
+}
+
+/// Type alias for [`Vec`] with a [`Kmalloc`] allocator.
+///
+/// # Examples
+///
+/// ```
+/// let mut v = KVec::new();
+/// v.push(1, GFP_KERNEL)?;
+/// assert_eq!(&v, &[1]);
+///
+/// # Ok::<(), Error>(())
+/// ```
+pub type KVec<T> = Vec<T, 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, 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, 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
+ }
+
+ /// 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) {
+ debug_assert!(new_len <= self.capacity());
+ self.len = new_len;
+ }
+
+ /// Returns a slice of the entire vector.
+ #[inline]
+ pub fn as_slice(&self) -> &[T] {
+ self
+ }
+
+ /// Returns a mutable slice of the entire vector.
+ #[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(&mut 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.ptr.as_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,
+ len: 0,
+ _p: PhantomData::<A>,
+ }
+ }
+
+ /// Returns a slice of `MaybeUninit<T>` for the remaining spare capacity of the vector.
+ pub fn spare_capacity_mut(&mut self) -> &mut [MaybeUninit<T>] {
+ // SAFETY:
+ // - `self.len` is smaller than `self.capacity` and hence, the resulting pointer is
+ // guaranteed to be part of the same allocated object.
+ // - `self.len` can not overflow `isize`.
+ let ptr = unsafe { self.as_mut_ptr().add(self.len) } as *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(ptr, self.capacity() - self.len) }
+ }
+
+ /// Appends an element to the back of the [`Vec`] instance.
+ ///
+ /// # Examples
+ ///
+ /// ```
+ /// let mut v = KVec::new();
+ /// v.push(1, GFP_KERNEL)?;
+ /// 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)?;
+
+ // SAFETY:
+ // - `self.len` is smaller than `self.capacity` and hence, the resulting pointer is
+ // guaranteed to be part of the same allocated object.
+ // - `self.len` can not overflow `isize`.
+ let ptr = unsafe { self.as_mut_ptr().add(self.len) };
+
+ // SAFETY:
+ // - `ptr` is properly aligned and valid for writes.
+ unsafe { core::ptr::write(ptr, v) };
+
+ // 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
+ T: Clone,
+ {
+ self.reserve(other.len(), flags)?;
+ for (slot, item) in core::iter::zip(self.spare_capacity_mut(), other) {
+ slot.write(item.clone());
+ }
+
+ // SAFETY:
+ // - `other.len()` spare entries have just been initialized, so it is safe to increase
+ // the length by the same number.
+ // - `self.len() + other.len() <= self.capacity()` is guaranteed by the preceding `reserve`
+ // call.
+ 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`.
+ ///
+ /// # 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;
+ ///
+ /// // 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>(())
+ /// ```
+ ///
+ /// # Safety
+ ///
+ /// If `T` is a ZST:
+ ///
+ /// - `ptr` must be a dangling, well aligned pointer.
+ ///
+ /// 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>() * capacity`.
+ /// bytes.
+ /// - The allocated size in bytes must not be larger than `isize::MAX`.
+ /// - `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`.
+ 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 mut 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>(())
+ /// ```
+ 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 ZSTs, we can't go higher.
+ return Err(AllocError);
+ }
+
+ // We know `cap` is <= `isize::MAX` because of the type invariants of `Self`. So the
+ // multiplication by 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();
+
+ // 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`.
+ 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();
+
+ 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.len() + n < self.capacity()` due to the call to reserve above,
+ // - the loop and the line above initialized the next `n` elements.
+ 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)
+ }
+}
+
+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(
+ 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()) };
+ }
+ }
+}
+
+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);
+
+ // SAFETY:
+ // - `b` has been allocated with `A`,
+ // - `ptr` fulfills the alignment requirements for `T`,
+ // - `ptr` points to memory with at least a size of `size_of::<T>() * len`,
+ // - all elements within `b` are initialized values of `T`,
+ // - `len` does not exceed `isize::MAX`.
+ unsafe { Vec::from_raw_parts(ptr as _, len, len) }
+ }
+}
+
+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>
+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 {
+ ([$($vars:tt)*] $lhs:ty, $rhs:ty) => {
+ impl<T, U, $($vars)*> PartialEq<$rhs> for $lhs
+ where
+ T: PartialEq<U>,
+ {
+ #[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] }
diff --git a/rust/kernel/prelude.rs b/rust/kernel/prelude.rs
index d5f2fe42d093..80223cdaa485 100644
--- a/rust/kernel/prelude.rs
+++ b/rust/kernel/prelude.rs
@@ -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
^ permalink raw reply related [flat|nested] 77+ messages in thread
* [PATCH v7 14/26] rust: alloc: implement `IntoIterator` for `Vec`
2024-09-11 22:52 [PATCH v7 00/26] Generic `Allocator` support for Rust Danilo Krummrich
` (12 preceding siblings ...)
2024-09-11 22:52 ` [PATCH v7 13/26] rust: alloc: implement kernel `Vec` type Danilo Krummrich
@ 2024-09-11 22:52 ` Danilo Krummrich
2024-09-26 13:53 ` Benno Lossin
2024-09-28 19:20 ` Gary Guo
2024-09-11 22:52 ` [PATCH v7 15/26] rust: alloc: implement `collect` for `IntoIter` Danilo Krummrich
` (11 subsequent siblings)
25 siblings, 2 replies; 77+ messages in thread
From: Danilo Krummrich @ 2024-09-11 22:52 UTC (permalink / raw)
To: ojeda, alex.gaynor, wedsonaf, boqun.feng, gary, bjorn3_gh,
benno.lossin, a.hindborg, aliceryhl, akpm
Cc: daniel.almeida, faith.ekstrand, boris.brezillon, lina, mcanal,
zhiw, cjia, jhubbard, airlied, ajanulgu, lyude, linux-kernel,
rust-for-linux, linux-mm, 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.
Reviewed-by: Alice Ryhl <aliceryhl@google.com>
Signed-off-by: Danilo Krummrich <dakr@kernel.org>
---
rust/kernel/alloc.rs | 1 +
rust/kernel/alloc/kvec.rs | 181 ++++++++++++++++++++++++++++++++++++++
2 files changed, 182 insertions(+)
diff --git a/rust/kernel/alloc.rs b/rust/kernel/alloc.rs
index 4fb983b63d46..e8fbae2adadb 100644
--- a/rust/kernel/alloc.rs
+++ b/rust/kernel/alloc.rs
@@ -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;
diff --git a/rust/kernel/alloc/kvec.rs b/rust/kernel/alloc/kvec.rs
index 631a44e19f35..e91761c5c52d 100644
--- a/rust/kernel/alloc/kvec.rs
+++ b/rust/kernel/alloc/kvec.rs
@@ -14,6 +14,7 @@
ops::DerefMut,
ops::Index,
ops::IndexMut,
+ ptr,
ptr::NonNull,
slice,
slice::SliceIndex,
@@ -636,3 +637,183 @@ 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`] 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));
+ /// assert_eq!(it.next(), None);
+ ///
+ /// # Ok::<(), Error>(())
+ /// ```
+ fn next(&mut self) -> Option<T> {
+ if self.len == 0 {
+ return None;
+ }
+
+ let current = self.ptr;
+
+ // SAFETY: We can't overflow; decreasing `self.len` by one every time we advance `self.ptr`
+ // by one guarantees that.
+ unsafe { self.ptr = self.ptr.add(1) };
+
+ self.len -= 1;
+
+ // SAFETY: `current` is guaranteed to point at a valid element within the buffer.
+ Some(unsafe { current.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: The pointer in `self.0` is guaranteed to be valid by the type invariant.
+ 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
^ permalink raw reply related [flat|nested] 77+ messages in thread
* [PATCH v7 15/26] rust: alloc: implement `collect` for `IntoIter`
2024-09-11 22:52 [PATCH v7 00/26] Generic `Allocator` support for Rust Danilo Krummrich
` (13 preceding siblings ...)
2024-09-11 22:52 ` [PATCH v7 14/26] rust: alloc: implement `IntoIterator` for `Vec` Danilo Krummrich
@ 2024-09-11 22:52 ` Danilo Krummrich
2024-09-26 13:57 ` Benno Lossin
2024-09-28 19:27 ` Gary Guo
2024-09-11 22:52 ` [PATCH v7 16/26] rust: treewide: switch to the kernel `Vec` type Danilo Krummrich
` (10 subsequent siblings)
25 siblings, 2 replies; 77+ messages in thread
From: Danilo Krummrich @ 2024-09-11 22:52 UTC (permalink / raw)
To: ojeda, alex.gaynor, wedsonaf, boqun.feng, gary, bjorn3_gh,
benno.lossin, a.hindborg, aliceryhl, akpm
Cc: daniel.almeida, faith.ekstrand, boris.brezillon, lina, mcanal,
zhiw, cjia, jhubbard, airlied, ajanulgu, lyude, linux-kernel,
rust-for-linux, linux-mm, 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.
Reviewed-by: Alice Ryhl <aliceryhl@google.com>
Signed-off-by: Danilo Krummrich <dakr@kernel.org>
---
rust/kernel/alloc/kvec.rs | 86 +++++++++++++++++++++++++++++++++++++++
1 file changed, 86 insertions(+)
diff --git a/rust/kernel/alloc/kvec.rs b/rust/kernel/alloc/kvec.rs
index e91761c5c52d..686e969463f8 100644
--- a/rust/kernel/alloc/kvec.rs
+++ b/rust/kernel/alloc/kvec.rs
@@ -690,6 +690,92 @@ 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`.
+ ///
+ /// # 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>(())
+ /// ```
+ /// # Implementation Details
+ ///
+ /// 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.
+ 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 {
+ // Copy the contents we have advanced to at the beginning of the buffer.
+ //
+ // SAFETY:
+ // - `ptr` is valid for reads of `len * size_of::<T>()` bytes,
+ // - `buf.as_ptr()` is valid for writes of `len * size_of::<T>()` bytes,
+ // - `ptr` and `buf.as_ptr()` are not be subject to aliasing restrictions relative to
+ // each other,
+ // - both `ptr` and `buf.ptr()` are properly aligned.
+ 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>
--
2.46.0
^ permalink raw reply related [flat|nested] 77+ messages in thread
* [PATCH v7 16/26] rust: treewide: switch to the kernel `Vec` type
2024-09-11 22:52 [PATCH v7 00/26] Generic `Allocator` support for Rust Danilo Krummrich
` (14 preceding siblings ...)
2024-09-11 22:52 ` [PATCH v7 15/26] rust: alloc: implement `collect` for `IntoIter` Danilo Krummrich
@ 2024-09-11 22:52 ` Danilo Krummrich
2024-09-28 19:28 ` Gary Guo
2024-09-11 22:52 ` [PATCH v7 17/26] rust: alloc: remove `VecExt` extension Danilo Krummrich
` (9 subsequent siblings)
25 siblings, 1 reply; 77+ messages in thread
From: Danilo Krummrich @ 2024-09-11 22:52 UTC (permalink / raw)
To: ojeda, alex.gaynor, wedsonaf, boqun.feng, gary, bjorn3_gh,
benno.lossin, a.hindborg, aliceryhl, akpm
Cc: daniel.almeida, faith.ekstrand, boris.brezillon, lina, mcanal,
zhiw, cjia, jhubbard, airlied, ajanulgu, lyude, linux-kernel,
rust-for-linux, linux-mm, Danilo Krummrich
Now that we got the kernel `Vec` in place, convert all existing `Vec`
users to make use of it.
Reviewed-by: Alice Ryhl <aliceryhl@google.com>
Reviewed-by: Benno Lossin <benno.lossin@proton.me>
Signed-off-by: Danilo Krummrich <dakr@kernel.org>
---
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(-)
diff --git a/rust/kernel/str.rs b/rust/kernel/str.rs
index bb8d4f41475b..209ad1bb1a49 100644
--- a/rust/kernel/str.rs
+++ b/rust/kernel/str.rs
@@ -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.
diff --git a/rust/kernel/sync/locked_by.rs b/rust/kernel/sync/locked_by.rs
index babc731bd5f6..b94517231fcc 100644
--- a/rust/kernel/sync/locked_by.rs
+++ b/rust/kernel/sync/locked_by.rs
@@ -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 {
diff --git a/rust/kernel/types.rs b/rust/kernel/types.rs
index 53d3ddc0b98c..bf1a96d5e1f8 100644
--- a/rust/kernel/types.rs
+++ b/rust/kernel/types.rs
@@ -135,7 +135,7 @@ unsafe fn from_foreign(_: *const core::ffi::c_void) -> Self {}
/// # use kernel::types::ScopeGuard;
/// fn example3(arg: bool) -> Result {
/// let mut vec =
-/// ScopeGuard::new_with_data(Vec::new(), |v| pr_info!("vec had {} elements\n", v.len()));
+/// ScopeGuard::new_with_data(KVec::new(), |v| pr_info!("vec had {} elements\n", v.len()));
///
/// vec.push(10u8, GFP_KERNEL)?;
/// if arg {
diff --git a/rust/kernel/uaccess.rs b/rust/kernel/uaccess.rs
index e9347cff99ab..bc011061de45 100644
--- a/rust/kernel/uaccess.rs
+++ b/rust/kernel/uaccess.rs
@@ -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.
diff --git a/rust/macros/lib.rs b/rust/macros/lib.rs
index ab93111a048c..8d4ac914b48b 100644
--- a/rust/macros/lib.rs
+++ b/rust/macros/lib.rs
@@ -242,7 +242,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]>,
/// }
/// ```
@@ -251,7 +251,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,
/// }
@@ -281,7 +281,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,
/// }
diff --git a/samples/rust/rust_minimal.rs b/samples/rust/rust_minimal.rs
index 2a9eaab62d1c..4aaf117bf8e3 100644
--- a/samples/rust/rust_minimal.rs
+++ b/samples/rust/rust_minimal.rs
@@ -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)?;
--
2.46.0
^ permalink raw reply related [flat|nested] 77+ messages in thread
* [PATCH v7 17/26] rust: alloc: remove `VecExt` extension
2024-09-11 22:52 [PATCH v7 00/26] Generic `Allocator` support for Rust Danilo Krummrich
` (15 preceding siblings ...)
2024-09-11 22:52 ` [PATCH v7 16/26] rust: treewide: switch to the kernel `Vec` type Danilo Krummrich
@ 2024-09-11 22:52 ` Danilo Krummrich
2024-09-28 19:29 ` Gary Guo
2024-09-11 22:52 ` [PATCH v7 18/26] rust: alloc: add `Vec` to prelude Danilo Krummrich
` (8 subsequent siblings)
25 siblings, 1 reply; 77+ messages in thread
From: Danilo Krummrich @ 2024-09-11 22:52 UTC (permalink / raw)
To: ojeda, alex.gaynor, wedsonaf, boqun.feng, gary, bjorn3_gh,
benno.lossin, a.hindborg, aliceryhl, akpm
Cc: daniel.almeida, faith.ekstrand, boris.brezillon, lina, mcanal,
zhiw, cjia, jhubbard, airlied, ajanulgu, lyude, linux-kernel,
rust-for-linux, linux-mm, Danilo Krummrich
Now that all existing `Vec` users were moved to the kernel `Vec` type,
remove the `VecExt` extension.
Reviewed-by: Alice Ryhl <aliceryhl@google.com>
Reviewed-by: Benno Lossin <benno.lossin@proton.me>
Signed-off-by: Danilo Krummrich <dakr@kernel.org>
---
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
diff --git a/rust/kernel/alloc.rs b/rust/kernel/alloc.rs
index e8fbae2adadb..aabdf80e4f7b 100644
--- a/rust/kernel/alloc.rs
+++ b/rust/kernel/alloc.rs
@@ -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;
diff --git a/rust/kernel/alloc/vec_ext.rs b/rust/kernel/alloc/vec_ext.rs
deleted file mode 100644
index 1297a4be32e8..000000000000
--- a/rust/kernel/alloc/vec_ext.rs
+++ /dev/null
@@ -1,185 +0,0 @@
-// SPDX-License-Identifier: GPL-2.0
-
-//! Extensions to [`Vec`] for fallible allocations.
-
-use super::{AllocError, Flags};
-use alloc::vec::Vec;
-
-/// Extensions to [`Vec`].
-pub trait VecExt<T>: Sized {
- /// Creates a new [`Vec`] instance with at least the given capacity.
- ///
- /// # Examples
- ///
- /// ```
- /// let v = Vec::<u32>::with_capacity(20, GFP_KERNEL)?;
- ///
- /// assert!(v.capacity() >= 20);
- /// # Ok::<(), Error>(())
- /// ```
- fn with_capacity(capacity: usize, flags: Flags) -> Result<Self, AllocError>;
-
- /// Appends an element to the back of the [`Vec`] instance.
- ///
- /// # Examples
- ///
- /// ```
- /// let mut v = Vec::new();
- /// v.push(1, GFP_KERNEL)?;
- /// assert_eq!(&v, &[1]);
- ///
- /// v.push(2, GFP_KERNEL)?;
- /// assert_eq!(&v, &[1, 2]);
- /// # Ok::<(), Error>(())
- /// ```
- fn push(&mut self, v: T, flags: Flags) -> Result<(), AllocError>;
-
- /// Pushes clones of the elements of slice into the [`Vec`] instance.
- ///
- /// # Examples
- ///
- /// ```
- /// let mut v = Vec::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>(())
- /// ```
- fn extend_from_slice(&mut self, other: &[T], flags: Flags) -> Result<(), AllocError>
- where
- T: Clone;
-
- /// Ensures that the capacity exceeds the length by at least `additional` elements.
- ///
- /// # Examples
- ///
- /// ```
- /// let mut v = Vec::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>(())
- /// ```
- fn reserve(&mut self, additional: usize, flags: Flags) -> Result<(), AllocError>;
-}
-
-impl<T> VecExt<T> for Vec<T> {
- fn with_capacity(capacity: usize, flags: Flags) -> Result<Self, AllocError> {
- let mut v = Vec::new();
- <Self as VecExt<_>>::reserve(&mut v, capacity, flags)?;
- Ok(v)
- }
-
- fn push(&mut self, v: T, flags: Flags) -> Result<(), AllocError> {
- <Self as VecExt<_>>::reserve(self, 1, flags)?;
- let s = self.spare_capacity_mut();
- s[0].write(v);
-
- // 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(())
- }
-
- fn extend_from_slice(&mut self, other: &[T], flags: Flags) -> Result<(), AllocError>
- where
- T: Clone,
- {
- <Self as VecExt<_>>::reserve(self, 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.
- unsafe { self.set_len(self.len() + other.len()) };
- Ok(())
- }
-
- #[cfg(any(test, testlib))]
- fn reserve(&mut self, additional: usize, _flags: Flags) -> Result<(), AllocError> {
- Vec::reserve(self, additional);
- Ok(())
- }
-
- #[cfg(not(any(test, testlib)))]
- 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 core::mem::size_of::<T>() == 0 {
- // The capacity is already `usize::MAX` for SZTs, we can't go higher.
- return Err(AllocError);
- }
-
- // We know cap is <= `isize::MAX` because `Layout::array` fails if the resulting byte size
- // is greater than `isize::MAX`. So the multiplication by 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)?;
-
- let (old_ptr, len, cap) = destructure(self);
-
- // We need to make sure that `ptr` is either NULL or comes from a previous call to
- // `krealloc_aligned`. A `Vec<T>`'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>`'s capacity
- // to be zero if no memory has been allocated yet.
- let ptr = if cap == 0 {
- core::ptr::null_mut()
- } else {
- old_ptr
- };
-
- // SAFETY: `ptr` is valid because it's either NULL or comes from a previous call to
- // `krealloc_aligned`. We also verified that the type is not a ZST.
- let new_ptr = unsafe { super::allocator::krealloc_aligned(ptr.cast(), layout, flags) };
- if new_ptr.is_null() {
- // SAFETY: We are just rebuilding the existing `Vec` with no changes.
- unsafe { rebuild(self, old_ptr, len, cap) };
- Err(AllocError)
- } else {
- // SAFETY: `ptr` has been reallocated with the layout for `new_cap` elements. New cap
- // is greater than `cap`, so it continues to be >= `len`.
- unsafe { rebuild(self, new_ptr.cast::<T>(), len, new_cap) };
- Ok(())
- }
- }
-}
-
-#[cfg(not(any(test, testlib)))]
-fn destructure<T>(v: &mut Vec<T>) -> (*mut T, usize, usize) {
- let mut tmp = Vec::new();
- core::mem::swap(&mut tmp, v);
- let mut tmp = core::mem::ManuallyDrop::new(tmp);
- let len = tmp.len();
- let cap = tmp.capacity();
- (tmp.as_mut_ptr(), len, cap)
-}
-
-/// Rebuilds a `Vec` from a pointer, length, and capacity.
-///
-/// # Safety
-///
-/// The same as [`Vec::from_raw_parts`].
-#[cfg(not(any(test, testlib)))]
-unsafe fn rebuild<T>(v: &mut Vec<T>, ptr: *mut T, len: usize, cap: usize) {
- // SAFETY: The safety requirements from this function satisfy those of `from_raw_parts`.
- let mut tmp = unsafe { Vec::from_raw_parts(ptr, len, cap) };
- core::mem::swap(&mut tmp, v);
-}
diff --git a/rust/kernel/prelude.rs b/rust/kernel/prelude.rs
index 80223cdaa485..07daccf6ca8e 100644
--- a/rust/kernel/prelude.rs
+++ b/rust/kernel/prelude.rs
@@ -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};
--
2.46.0
^ permalink raw reply related [flat|nested] 77+ messages in thread
* [PATCH v7 18/26] rust: alloc: add `Vec` to prelude
2024-09-11 22:52 [PATCH v7 00/26] Generic `Allocator` support for Rust Danilo Krummrich
` (16 preceding siblings ...)
2024-09-11 22:52 ` [PATCH v7 17/26] rust: alloc: remove `VecExt` extension Danilo Krummrich
@ 2024-09-11 22:52 ` Danilo Krummrich
2024-09-28 19:29 ` Gary Guo
2024-09-11 22:52 ` [PATCH v7 19/26] rust: error: use `core::alloc::LayoutError` Danilo Krummrich
` (7 subsequent siblings)
25 siblings, 1 reply; 77+ messages in thread
From: Danilo Krummrich @ 2024-09-11 22:52 UTC (permalink / raw)
To: ojeda, alex.gaynor, wedsonaf, boqun.feng, gary, bjorn3_gh,
benno.lossin, a.hindborg, aliceryhl, akpm
Cc: daniel.almeida, faith.ekstrand, boris.brezillon, lina, mcanal,
zhiw, cjia, jhubbard, airlied, ajanulgu, lyude, linux-kernel,
rust-for-linux, linux-mm, Danilo Krummrich
Now that we removed `VecExt` and the corresponding includes in
prelude.rs, add the new kernel `Vec` type instead.
Reviewed-by: Alice Ryhl <aliceryhl@google.com>
Reviewed-by: Benno Lossin <benno.lossin@proton.me>
Signed-off-by: Danilo Krummrich <dakr@kernel.org>
---
rust/kernel/prelude.rs | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/rust/kernel/prelude.rs b/rust/kernel/prelude.rs
index 07daccf6ca8e..8bdab9aa0d16 100644
--- a/rust/kernel/prelude.rs
+++ b/rust/kernel/prelude.rs
@@ -14,7 +14,7 @@
#[doc(no_inline)]
pub use core::pin::Pin;
-pub use crate::alloc::{flags::*, Box, KBox, KVBox, KVVec, KVec, VBox, VVec};
+pub use crate::alloc::{flags::*, Box, KBox, KVBox, KVVec, KVec, VBox, VVec, Vec};
#[doc(no_inline)]
pub use macros::{module, pin_data, pinned_drop, vtable, Zeroable};
--
2.46.0
^ permalink raw reply related [flat|nested] 77+ messages in thread
* [PATCH v7 19/26] rust: error: use `core::alloc::LayoutError`
2024-09-11 22:52 [PATCH v7 00/26] Generic `Allocator` support for Rust Danilo Krummrich
` (17 preceding siblings ...)
2024-09-11 22:52 ` [PATCH v7 18/26] rust: alloc: add `Vec` to prelude Danilo Krummrich
@ 2024-09-11 22:52 ` Danilo Krummrich
2024-09-28 19:30 ` Gary Guo
2024-09-11 22:52 ` [PATCH v7 20/26] rust: error: check for config `test` in `Error::name` Danilo Krummrich
` (6 subsequent siblings)
25 siblings, 1 reply; 77+ messages in thread
From: Danilo Krummrich @ 2024-09-11 22:52 UTC (permalink / raw)
To: ojeda, alex.gaynor, wedsonaf, boqun.feng, gary, bjorn3_gh,
benno.lossin, a.hindborg, aliceryhl, akpm
Cc: daniel.almeida, faith.ekstrand, boris.brezillon, lina, mcanal,
zhiw, cjia, jhubbard, airlied, ajanulgu, lyude, linux-kernel,
rust-for-linux, linux-mm, Danilo Krummrich
Use `core::alloc::LayoutError` instead of `alloc::alloc::LayoutError` in
preparation to get rid of Rust's alloc crate.
Reviewed-by: Alice Ryhl <aliceryhl@google.com>
Reviewed-by: Benno Lossin <benno.lossin@proton.me>
Signed-off-by: Danilo Krummrich <dakr@kernel.org>
---
rust/kernel/error.rs | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/rust/kernel/error.rs b/rust/kernel/error.rs
index 6f1587a2524e..81b4fc5cf21e 100644
--- a/rust/kernel/error.rs
+++ b/rust/kernel/error.rs
@@ -6,7 +6,7 @@
use crate::{alloc::AllocError, str::CStr};
-use alloc::alloc::LayoutError;
+use core::alloc::LayoutError;
use core::fmt;
use core::num::TryFromIntError;
--
2.46.0
^ permalink raw reply related [flat|nested] 77+ messages in thread
* [PATCH v7 20/26] rust: error: check for config `test` in `Error::name`
2024-09-11 22:52 [PATCH v7 00/26] Generic `Allocator` support for Rust Danilo Krummrich
` (18 preceding siblings ...)
2024-09-11 22:52 ` [PATCH v7 19/26] rust: error: use `core::alloc::LayoutError` Danilo Krummrich
@ 2024-09-11 22:52 ` Danilo Krummrich
2024-09-28 19:30 ` Gary Guo
2024-09-11 22:52 ` [PATCH v7 21/26] rust: alloc: implement `contains` for `Flags` Danilo Krummrich
` (5 subsequent siblings)
25 siblings, 1 reply; 77+ messages in thread
From: Danilo Krummrich @ 2024-09-11 22:52 UTC (permalink / raw)
To: ojeda, alex.gaynor, wedsonaf, boqun.feng, gary, bjorn3_gh,
benno.lossin, a.hindborg, aliceryhl, akpm
Cc: daniel.almeida, faith.ekstrand, boris.brezillon, lina, mcanal,
zhiw, cjia, jhubbard, airlied, ajanulgu, lyude, linux-kernel,
rust-for-linux, linux-mm, 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.
Reviewed-by: Alice Ryhl <aliceryhl@google.com>
Reviewed-by: Benno Lossin <benno.lossin@proton.me>
Signed-off-by: Danilo Krummrich <dakr@kernel.org>
---
rust/kernel/error.rs | 4 ++--
1 file changed, 2 insertions(+), 2 deletions(-)
diff --git a/rust/kernel/error.rs b/rust/kernel/error.rs
index 81b4fc5cf21e..b71fdd0f54c2 100644
--- a/rust/kernel/error.rs
+++ b/rust/kernel/error.rs
@@ -143,7 +143,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) };
@@ -160,7 +160,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
^ permalink raw reply related [flat|nested] 77+ messages in thread
* [PATCH v7 21/26] rust: alloc: implement `contains` for `Flags`
2024-09-11 22:52 [PATCH v7 00/26] Generic `Allocator` support for Rust Danilo Krummrich
` (19 preceding siblings ...)
2024-09-11 22:52 ` [PATCH v7 20/26] rust: error: check for config `test` in `Error::name` Danilo Krummrich
@ 2024-09-11 22:52 ` Danilo Krummrich
2024-09-28 19:31 ` Gary Guo
2024-09-11 22:52 ` [PATCH v7 22/26] rust: alloc: implement `Cmalloc` in module allocator_test Danilo Krummrich
` (4 subsequent siblings)
25 siblings, 1 reply; 77+ messages in thread
From: Danilo Krummrich @ 2024-09-11 22:52 UTC (permalink / raw)
To: ojeda, alex.gaynor, wedsonaf, boqun.feng, gary, bjorn3_gh,
benno.lossin, a.hindborg, aliceryhl, akpm
Cc: daniel.almeida, faith.ekstrand, boris.brezillon, lina, mcanal,
zhiw, cjia, jhubbard, airlied, ajanulgu, lyude, linux-kernel,
rust-for-linux, linux-mm, 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.
Reviewed-by: Alice Ryhl <aliceryhl@google.com>
Reviewed-by: Benno Lossin <benno.lossin@proton.me>
Signed-off-by: Danilo Krummrich <dakr@kernel.org>
---
rust/kernel/alloc.rs | 7 ++++++-
1 file changed, 6 insertions(+), 1 deletion(-)
diff --git a/rust/kernel/alloc.rs b/rust/kernel/alloc.rs
index aabdf80e4f7b..caa0b9dfac87 100644
--- a/rust/kernel/alloc.rs
+++ b/rust/kernel/alloc.rs
@@ -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
^ permalink raw reply related [flat|nested] 77+ messages in thread
* [PATCH v7 22/26] rust: alloc: implement `Cmalloc` in module allocator_test
2024-09-11 22:52 [PATCH v7 00/26] Generic `Allocator` support for Rust Danilo Krummrich
` (20 preceding siblings ...)
2024-09-11 22:52 ` [PATCH v7 21/26] rust: alloc: implement `contains` for `Flags` Danilo Krummrich
@ 2024-09-11 22:52 ` Danilo Krummrich
2024-09-28 19:35 ` Gary Guo
2024-09-11 22:52 ` [PATCH v7 23/26] rust: str: test: replace `alloc::format` Danilo Krummrich
` (3 subsequent siblings)
25 siblings, 1 reply; 77+ messages in thread
From: Danilo Krummrich @ 2024-09-11 22:52 UTC (permalink / raw)
To: ojeda, alex.gaynor, wedsonaf, boqun.feng, gary, bjorn3_gh,
benno.lossin, a.hindborg, aliceryhl, akpm
Cc: daniel.almeida, faith.ekstrand, boris.brezillon, lina, mcanal,
zhiw, cjia, jhubbard, airlied, ajanulgu, lyude, linux-kernel,
rust-for-linux, linux-mm, 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.
Signed-off-by: Danilo Krummrich <dakr@kernel.org>
---
rust/kernel/alloc/allocator_test.rs | 193 +++++++++++++++++++++++++++-
1 file changed, 186 insertions(+), 7 deletions(-)
diff --git a/rust/kernel/alloc/allocator_test.rs b/rust/kernel/alloc/allocator_test.rs
index 1b2642c547ec..b5b30513c398 100644
--- a/rust/kernel/alloc/allocator_test.rs
+++ b/rust/kernel/alloc/allocator_test.rs
@@ -1,21 +1,200 @@
// SPDX-License-Identifier: GPL-2.0
+//! 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.
+
#![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;
+/// The userspace allocator based on libc.
+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.
+ ///
+ ///```text
+ /// 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:
+ // - By adding `min_align` the pointer remains within the allocated object and `min_align`
+ // can not exceed `isize`.
+ //
+ // GUARANTEE:
+ // - 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(ptr: NonNull<u8>) -> 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();
+
+ // `CmallocData` has been previously stored at this offset with `Self::alloc_store_data`.
+ //
+ // SAFETY:
+ // - `data_ptr` points to a properly aligned and initialized value of `CmallocData`.
+ unsafe { core::ptr::read(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 Some(src) = ptr 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)
}
}
--
2.46.0
^ permalink raw reply related [flat|nested] 77+ messages in thread
* [PATCH v7 23/26] rust: str: test: replace `alloc::format`
2024-09-11 22:52 [PATCH v7 00/26] Generic `Allocator` support for Rust Danilo Krummrich
` (21 preceding siblings ...)
2024-09-11 22:52 ` [PATCH v7 22/26] rust: alloc: implement `Cmalloc` in module allocator_test Danilo Krummrich
@ 2024-09-11 22:52 ` Danilo Krummrich
2024-09-28 19:37 ` Gary Guo
2024-09-11 22:53 ` [PATCH v7 24/26] rust: alloc: update module comment of alloc.rs Danilo Krummrich
` (2 subsequent siblings)
25 siblings, 1 reply; 77+ messages in thread
From: Danilo Krummrich @ 2024-09-11 22:52 UTC (permalink / raw)
To: ojeda, alex.gaynor, wedsonaf, boqun.feng, gary, bjorn3_gh,
benno.lossin, a.hindborg, aliceryhl, akpm
Cc: daniel.almeida, faith.ekstrand, boris.brezillon, lina, mcanal,
zhiw, cjia, jhubbard, airlied, ajanulgu, lyude, linux-kernel,
rust-for-linux, linux-mm, 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`.
Reviewed-by: Alice Ryhl <aliceryhl@google.com>
Reviewed-by: Benno Lossin <benno.lossin@proton.me>
Signed-off-by: Danilo Krummrich <dakr@kernel.org>
---
rust/kernel/str.rs | 23 ++++++++++++++++++++++-
1 file changed, 22 insertions(+), 1 deletion(-)
diff --git a/rust/kernel/str.rs b/rust/kernel/str.rs
index 209ad1bb1a49..726329530338 100644
--- a/rust/kernel/str.rs
+++ b/rust/kernel/str.rs
@@ -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
^ permalink raw reply related [flat|nested] 77+ messages in thread
* [PATCH v7 24/26] rust: alloc: update module comment of alloc.rs
2024-09-11 22:52 [PATCH v7 00/26] Generic `Allocator` support for Rust Danilo Krummrich
` (22 preceding siblings ...)
2024-09-11 22:52 ` [PATCH v7 23/26] rust: str: test: replace `alloc::format` Danilo Krummrich
@ 2024-09-11 22:53 ` Danilo Krummrich
2024-09-28 19:38 ` Gary Guo
2024-09-11 22:53 ` [PATCH v7 25/26] kbuild: rust: remove the `alloc` crate and `GlobalAlloc` Danilo Krummrich
2024-09-11 22:53 ` [PATCH v7 26/26] MAINTAINERS: add entry for the Rust `alloc` module Danilo Krummrich
25 siblings, 1 reply; 77+ messages in thread
From: Danilo Krummrich @ 2024-09-11 22:53 UTC (permalink / raw)
To: ojeda, alex.gaynor, wedsonaf, boqun.feng, gary, bjorn3_gh,
benno.lossin, a.hindborg, aliceryhl, akpm
Cc: daniel.almeida, faith.ekstrand, boris.brezillon, lina, mcanal,
zhiw, cjia, jhubbard, airlied, ajanulgu, lyude, linux-kernel,
rust-for-linux, linux-mm, 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.
Reviewed-by: Alice Ryhl <aliceryhl@google.com>
Reviewed-by: Benno Lossin <benno.lossin@proton.me>
Signed-off-by: Danilo Krummrich <dakr@kernel.org>
---
rust/kernel/alloc.rs | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/rust/kernel/alloc.rs b/rust/kernel/alloc.rs
index caa0b9dfac87..2170b53acd0c 100644
--- a/rust/kernel/alloc.rs
+++ b/rust/kernel/alloc.rs
@@ -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;
--
2.46.0
^ permalink raw reply related [flat|nested] 77+ messages in thread
* [PATCH v7 25/26] kbuild: rust: remove the `alloc` crate and `GlobalAlloc`
2024-09-11 22:52 [PATCH v7 00/26] Generic `Allocator` support for Rust Danilo Krummrich
` (23 preceding siblings ...)
2024-09-11 22:53 ` [PATCH v7 24/26] rust: alloc: update module comment of alloc.rs Danilo Krummrich
@ 2024-09-11 22:53 ` Danilo Krummrich
2024-09-28 19:43 ` Gary Guo
2024-09-11 22:53 ` [PATCH v7 26/26] MAINTAINERS: add entry for the Rust `alloc` module Danilo Krummrich
25 siblings, 1 reply; 77+ messages in thread
From: Danilo Krummrich @ 2024-09-11 22:53 UTC (permalink / raw)
To: ojeda, alex.gaynor, wedsonaf, boqun.feng, gary, bjorn3_gh,
benno.lossin, a.hindborg, aliceryhl, akpm
Cc: daniel.almeida, faith.ekstrand, boris.brezillon, lina, mcanal,
zhiw, cjia, jhubbard, airlied, ajanulgu, lyude, linux-kernel,
rust-for-linux, linux-mm, 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`.
Signed-off-by: Danilo Krummrich <dakr@kernel.org>
---
rust/Makefile | 43 +++++----------------
rust/exports.c | 1 -
rust/kernel/alloc/allocator.rs | 63 +------------------------------
scripts/Makefile.build | 7 +---
scripts/generate_rust_analyzer.py | 11 +-----
5 files changed, 15 insertions(+), 110 deletions(-)
diff --git a/rust/Makefile b/rust/Makefile
index 4eae318f36ff..a3ac60b48ae1 100644
--- a/rust/Makefile
+++ b/rust/Makefile
@@ -15,8 +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_helpers_generated.h \
+obj-$(CONFIG_RUST) += bindings.o kernel.o
+always-$(CONFIG_RUST) += exports_helpers_generated.h \
exports_bindings_generated.h exports_kernel_generated.h
always-$(CONFIG_RUST) += uapi/uapi_generated.rs
@@ -53,11 +53,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)) \
@@ -81,7 +76,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 \
@@ -105,20 +100,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)
@@ -162,7 +148,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 \
@@ -198,7 +184,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 \
@@ -311,9 +297,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)
-
# Even though Rust kernel modules should never use the bindings directly,
# symbols from the `bindings` crate and the C helpers need to be exported
# because Rust generics and inlined functions may not get their code generated
@@ -360,7 +343,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)) \
$(rustc_sysroot) $(RUST_LIB_SRC) $(KBUILD_EXTMOD) > \
$(if $(KBUILD_EXTMOD),$(extmod_prefix),$(objtree))/rust-project.json
@@ -398,12 +381,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_rule,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_rule,rustc_library)
-
$(obj)/build_error.o: $(src)/build_error.rs $(obj)/compiler_builtins.o FORCE
+$(call if_changed_rule,rustc_library)
@@ -418,9 +395,9 @@ $(obj)/uapi.o: $(src)/uapi/lib.rs \
$(obj)/uapi/uapi_generated.rs FORCE
+$(call if_changed_rule,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_rule,rustc_library)
diff --git a/rust/exports.c b/rust/exports.c
index e5695f3b45b7..82a037381798 100644
--- a/rust/exports.c
+++ b/rust/exports.c
@@ -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_helpers_generated.h"
#include "exports_bindings_generated.h"
#include "exports_kernel_generated.h"
diff --git a/rust/kernel/alloc/allocator.rs b/rust/kernel/alloc/allocator.rs
index a5d7e66a68db..0b586c0361f4 100644
--- a/rust/kernel/alloc/allocator.rs
+++ b/rust/kernel/alloc/allocator.rs
@@ -8,8 +8,8 @@
//!
//! Reference: <https://docs.kernel.org/core-api/memory-allocation.html>
-use super::{flags::*, Flags};
-use core::alloc::{GlobalAlloc, Layout};
+use super::Flags;
+use core::alloc::Layout;
use core::ptr;
use core::ptr::NonNull;
@@ -54,23 +54,6 @@ fn aligned_size(new_layout: Layout) -> usize {
layout.size()
}
-/// Calls `krealloc` with a proper size to alloc a new object aligned to `new_layout`'s alignment.
-///
-/// # Safety
-///
-/// - `ptr` can be either null or a pointer which has been allocated by this allocator.
-/// - `new_layout` must have a non-zero size.
-pub(crate) unsafe fn krealloc_aligned(ptr: *mut u8, new_layout: Layout, flags: Flags) -> *mut u8 {
- let size = aligned_size(new_layout);
-
- // SAFETY:
- // - `ptr` is either null or a pointer returned from a previous `k{re}alloc()` by the
- // function safety requirement.
- // - `size` is greater than 0 since it's from `layout.size()` (which cannot be zero according
- // to the function safety requirement)
- unsafe { bindings::krealloc(ptr as *const core::ffi::c_void, size, flags.0) as *mut u8 }
-}
-
/// # Invariants
///
/// One of the following `krealloc`, `vrealloc`, `kvrealloc`.
@@ -148,41 +131,6 @@ unsafe fn realloc(
}
}
-unsafe impl GlobalAlloc for Kmalloc {
- unsafe fn alloc(&self, layout: Layout) -> *mut u8 {
- // SAFETY: `ptr::null_mut()` is null and `layout` has a non-zero size by the function safety
- // requirement.
- unsafe { krealloc_aligned(ptr::null_mut(), layout, GFP_KERNEL) }
- }
-
- unsafe fn dealloc(&self, ptr: *mut u8, _layout: Layout) {
- unsafe {
- bindings::kfree(ptr as *const core::ffi::c_void);
- }
- }
-
- unsafe fn realloc(&self, ptr: *mut u8, layout: Layout, new_size: usize) -> *mut u8 {
- // SAFETY:
- // - `new_size`, when rounded up to the nearest multiple of `layout.align()`, will not
- // overflow `isize` by the function safety requirement.
- // - `layout.align()` is a proper alignment (i.e. not zero and must be a power of two).
- let layout = unsafe { Layout::from_size_align_unchecked(new_size, layout.align()) };
-
- // SAFETY:
- // - `ptr` is either null or a pointer allocated by this allocator by the function safety
- // requirement.
- // - the size of `layout` is not zero because `new_size` is not zero by the function safety
- // requirement.
- unsafe { krealloc_aligned(ptr, layout, GFP_KERNEL) }
- }
-
- unsafe fn alloc_zeroed(&self, layout: Layout) -> *mut u8 {
- // SAFETY: `ptr::null_mut()` is null and `layout` has a non-zero size by the function safety
- // requirement.
- unsafe { krealloc_aligned(ptr::null_mut(), layout, GFP_KERNEL | __GFP_ZERO) }
- }
-}
-
// SAFETY: `realloc` delegates to `ReallocFunc::call`, which guarantees that
// - memory remains valid until it is explicitly freed,
// - passing a pointer to a valid memory allocation is OK,
@@ -228,10 +176,3 @@ unsafe fn realloc(
unsafe { ReallocFunc::KVREALLOC.call(ptr, layout, flags) }
}
}
-
-#[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;
diff --git a/scripts/Makefile.build b/scripts/Makefile.build
index 72b1232b1f7d..529ec5972e55 100644
--- a/scripts/Makefile.build
+++ b/scripts/Makefile.build
@@ -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 \
diff --git a/scripts/generate_rust_analyzer.py b/scripts/generate_rust_analyzer.py
index d2bc63cde8c6..09e1d166d8d2 100755
--- a/scripts/generate_rust_analyzer.py
+++ b/scripts/generate_rust_analyzer.py
@@ -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,
)
--
2.46.0
^ permalink raw reply related [flat|nested] 77+ messages in thread
* [PATCH v7 26/26] MAINTAINERS: add entry for the Rust `alloc` module
2024-09-11 22:52 [PATCH v7 00/26] Generic `Allocator` support for Rust Danilo Krummrich
` (24 preceding siblings ...)
2024-09-11 22:53 ` [PATCH v7 25/26] kbuild: rust: remove the `alloc` crate and `GlobalAlloc` Danilo Krummrich
@ 2024-09-11 22:53 ` Danilo Krummrich
25 siblings, 0 replies; 77+ messages in thread
From: Danilo Krummrich @ 2024-09-11 22:53 UTC (permalink / raw)
To: ojeda, alex.gaynor, wedsonaf, boqun.feng, gary, bjorn3_gh,
benno.lossin, a.hindborg, aliceryhl, akpm
Cc: daniel.almeida, faith.ekstrand, boris.brezillon, lina, mcanal,
zhiw, cjia, jhubbard, airlied, ajanulgu, lyude, linux-kernel,
rust-for-linux, linux-mm, 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`.
Signed-off-by: Danilo Krummrich <dakr@kernel.org>
---
MAINTAINERS | 7 +++++++
1 file changed, 7 insertions(+)
diff --git a/MAINTAINERS b/MAINTAINERS
index 77b395476a80..f57e3b5bc410 100644
--- a/MAINTAINERS
+++ b/MAINTAINERS
@@ -19929,6 +19929,13 @@ F: scripts/*rust*
F: tools/testing/selftests/rust/
K: \b(?i:rust)\b
+RUST [ALLOC]
+M: Danilo Krummrich <dakr@kernel.org>
+L: rust-for-linux@vger.kernel.org
+S: Maintained
+F: rust/kernel/alloc.rs
+F: rust/kernel/alloc/
+
RXRPC SOCKETS (AF_RXRPC)
M: David Howells <dhowells@redhat.com>
M: Marc Dionne <marc.dionne@auristor.com>
--
2.46.0
^ permalink raw reply related [flat|nested] 77+ messages in thread
* Re: [PATCH v7 01/26] rust: alloc: add `Allocator` trait
2024-09-11 22:52 ` [PATCH v7 01/26] rust: alloc: add `Allocator` trait Danilo Krummrich
@ 2024-09-15 15:28 ` Gary Guo
2024-09-15 17:02 ` Danilo Krummrich
2024-09-21 15:32 ` [RFC PATCH] rust: alloc: pass `old_layout` to `Allocator` Danilo Krummrich
1 sibling, 1 reply; 77+ messages in thread
From: Gary Guo @ 2024-09-15 15:28 UTC (permalink / raw)
To: Danilo Krummrich
Cc: ojeda, alex.gaynor, wedsonaf, boqun.feng, bjorn3_gh, benno.lossin,
a.hindborg, aliceryhl, akpm, daniel.almeida, faith.ekstrand,
boris.brezillon, lina, mcanal, zhiw, cjia, jhubbard, airlied,
ajanulgu, lyude, linux-kernel, rust-for-linux, linux-mm
On Thu, 12 Sep 2024 00:52:37 +0200
Danilo Krummrich <dakr@kernel.org> 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`.
Hi Danilo,
I think the current design is unsound regarding ZST.
Let's say that `Allocator::alloc` gets called with a ZST type with
alignment of 4096. Your implementation will call into `krelloc` with
new_size of 0, which gets turned into of `kfree` of null pointer, which
is no-op. Everything is fine so far. Krealloc returns `ZERO_SIZE_PTR`,
and then implementation of `<Kmalloc as Allocator>::realloc` throws it
away and returns `NonNull::dangling`.
Since `NonNull::dangling` is called with T=u8, this means the pointer
returns is 1, and it's invalid for ZSTs with larger alignments.
And this is unfixable even if the realloc implementation is changed.
Let's say the realloc now returns a dangling pointer that is suitable
aligned. Now let's see what happens when the `Allocator::free` is
called. `kfree` would be trying to free a Rust-side ZST pointer, but it
has no way to know that it's ZST!
I can see 3 ways of fixing this:
1. Reject ZSTs that have larger alignment than 16 and fix the realloc
implementation to return suitable aligned ZST pointer. I don't
particularly like the idea of allocating ZST can fail though.
2. Say ZST must be handled by the caller, and make alloc function
unsafe. This means that we essentially revert to the `GlobalAlloc`
design of Rust, and all callers have to check for ZST.
3. Accept the `old_layout` and use it to check whether the allocation
is ZST allocation.
My personal preference is 3.
Best,
Gary
>
> Reviewed-by: Alice Ryhl <aliceryhl@google.com>
> Signed-off-by: Danilo Krummrich <dakr@kernel.org>
> ---
> rust/kernel/alloc.rs | 112 +++++++++++++++++++++++++++++++++++++++++++
> 1 file changed, 112 insertions(+)
>
> diff --git a/rust/kernel/alloc.rs b/rust/kernel/alloc.rs
> index 1966bd407017..6c21bd2edad9 100644
> --- a/rust/kernel/alloc.rs
> +++ b/rust/kernel/alloc.rs
> @@ -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,114 @@ 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 buffers described
> +/// via [`Layout`].
> +///
> +/// [`Allocator`] is designed to be implemented as a ZST; [`Allocator`] functions do not operate on
> +/// an object instance.
I think whether the Allocator is ZST or not doesn't matter anymore
since we say that functions do not operate on an 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.
nit: this is the reason for `Allocator` to not have instances, so
should be merged together with the preceding sentence into one
paragraph.
> +///
> +/// # 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.
> +//
> +// Note that `Allocator::{realloc,free}` don't have an `old_layout` argument (like stdlib's
> +// corresponding `Allocator` trait functions have), since the implemented (kernel) allocators
> +// neither need nor honor such an argument. Thus, it would be misleading to make this API require it
> +// anyways.
I would drop the "honor" part, and drop the sentence saying it's
misleading to require it. The documentation should say why we don't
have the `old_layout` argument (because we don't need it for now), and
shouldn't be trying to dissuade it from being added if it ends up being
useful in the future.
> +//
> +// More generally, this trait isn't intended for implementers to encode a lot of semantics, but
> +// rather provide a thin generalization layer for the kernel's allocators.
> +//
> +// Depending on future requirements, the requirements for this trait may change as well and
> +// implementing allocators that need to encode more semantics may become desirable.
Not sure what's the purpose of these two paragraphs. They sound
contradictory to each other.
> +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
nit: maybe
If `ptr` is `Some(p)`?
because `ptr` carries the provenance not only addresses.
> + /// 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)) };
> + }
> +}
^ permalink raw reply [flat|nested] 77+ messages in thread
* Re: [PATCH v7 01/26] rust: alloc: add `Allocator` trait
2024-09-15 15:28 ` Gary Guo
@ 2024-09-15 17:02 ` Danilo Krummrich
2024-09-15 19:22 ` Gary Guo
0 siblings, 1 reply; 77+ messages in thread
From: Danilo Krummrich @ 2024-09-15 17:02 UTC (permalink / raw)
To: Gary Guo
Cc: ojeda, alex.gaynor, wedsonaf, boqun.feng, bjorn3_gh, benno.lossin,
a.hindborg, aliceryhl, akpm, daniel.almeida, faith.ekstrand,
boris.brezillon, lina, mcanal, zhiw, cjia, jhubbard, airlied,
ajanulgu, lyude, linux-kernel, rust-for-linux, linux-mm
Hi Gary,
thanks for taking a look.
On Sun, Sep 15, 2024 at 04:28:13PM +0100, Gary Guo wrote:
> On Thu, 12 Sep 2024 00:52:37 +0200
> Danilo Krummrich <dakr@kernel.org> 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`.
>
> Hi Danilo,
>
> I think the current design is unsound regarding ZST.
>
> Let's say that `Allocator::alloc` gets called with a ZST type with
> alignment of 4096. Your implementation will call into `krelloc` with
> new_size of 0, which gets turned into of `kfree` of null pointer, which
> is no-op. Everything is fine so far. Krealloc returns `ZERO_SIZE_PTR`,
> and then implementation of `<Kmalloc as Allocator>::realloc` throws it
> away and returns `NonNull::dangling`.
>
> Since `NonNull::dangling` is called with T=u8, this means the pointer
> returns is 1, and it's invalid for ZSTs with larger alignments.
Right, this interface is not meant to handle "allocations" for ZSTs.
But you're right, since `alloc` is a safe function, we should return a properly
aligned pointer.
>
> And this is unfixable even if the realloc implementation is changed.
> Let's say the realloc now returns a dangling pointer that is suitable
> aligned. Now let's see what happens when the `Allocator::free` is
> called. `kfree` would be trying to free a Rust-side ZST pointer, but it
> has no way to know that it's ZST!
Right, that's why it's not valid to call `free` with dangling pointers.
From the safety comment of `free`:
"`ptr` must point to an existing and valid memory allocation created by this
`Allocator` and must not be a dangling pointer."
We still need the same in `realloc` though.
>
> I can see 3 ways of fixing this:
> 1. Reject ZSTs that have larger alignment than 16 and fix the realloc
> implementation to return suitable aligned ZST pointer. I don't
> particularly like the idea of allocating ZST can fail though.
> 2. Say ZST must be handled by the caller, and make alloc function
> unsafe. This means that we essentially revert to the `GlobalAlloc`
> design of Rust, and all callers have to check for ZST.
> 3. Accept the `old_layout` and use it to check whether the allocation
> is ZST allocation.
>
> My personal preference is 3.
There is also 4.
Let `alloc` and `realloc` return a properly aligned dangling pointer for
`size == 0` and don't accept dangling pointers in `realloc` and `free`.
And 5.
Reject the combination of `None` and `size == 0` entirely, as earlier proposed
by Benno.
I'm fine with both, 4. and 5. with a slight preference for 4.
I'd also go along with 1., as a mix of 4. and 5.
I really don't like making `alloc` unsafe, and I really don't want to have
`old_layout` in `free`. Please let's not discuss this again. :-)
>
> Best,
> Gary
>
> >
> > Reviewed-by: Alice Ryhl <aliceryhl@google.com>
> > Signed-off-by: Danilo Krummrich <dakr@kernel.org>
> > ---
> > rust/kernel/alloc.rs | 112 +++++++++++++++++++++++++++++++++++++++++++
> > 1 file changed, 112 insertions(+)
> >
> > diff --git a/rust/kernel/alloc.rs b/rust/kernel/alloc.rs
> > index 1966bd407017..6c21bd2edad9 100644
> > --- a/rust/kernel/alloc.rs
> > +++ b/rust/kernel/alloc.rs
> > @@ -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,114 @@ 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 buffers described
> > +/// via [`Layout`].
> > +///
> > +/// [`Allocator`] is designed to be implemented as a ZST; [`Allocator`] functions do not operate on
> > +/// an object instance.
>
> I think whether the Allocator is ZST or not doesn't matter anymore
> since we say that functions do not operate on an instance.
IMO, It's still valid to say that it's designed to be implemented as ZST,
especially since it's never instantiated.
>
> > +///
> > +/// 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.
>
>
> nit: this is the reason for `Allocator` to not have instances, so
> should be merged together with the preceding sentence into one
> paragraph.
>
> > +///
> > +/// # 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.
> > +//
> > +// Note that `Allocator::{realloc,free}` don't have an `old_layout` argument (like stdlib's
> > +// corresponding `Allocator` trait functions have), since the implemented (kernel) allocators
> > +// neither need nor honor such an argument. Thus, it would be misleading to make this API require it
> > +// anyways.
>
> I would drop the "honor" part, and drop the sentence saying it's
> misleading to require it. The documentation should say why we don't
> have the `old_layout` argument (because we don't need it for now), and
> shouldn't be trying to dissuade it from being added if it ends up being
> useful in the future.
But the honor part is the crucial one for me. Just because it's not needed it
could still be optional (and honored). But that's not the case. And because of
that it would indeed be misleading to have the argument.
That's really just my reason, I'm not trying to dissuade anyone from adding it
if it's actually needed.
If there's a good reason to add it, and it's honored by (other) implementations,
it wouldn't be generally misleading anymore.
>
> > +//
> > +// More generally, this trait isn't intended for implementers to encode a lot of semantics, but
> > +// rather provide a thin generalization layer for the kernel's allocators.
> > +//
> > +// Depending on future requirements, the requirements for this trait may change as well and
> > +// implementing allocators that need to encode more semantics may become desirable.
>
> Not sure what's the purpose of these two paragraphs. They sound
> contradictory to each other.
I tried to articulate what it is currently intended for, but, depending on
future requirements, this may change.
I was asked by Benno to somehow add that we're open for changes...
>
> > +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
>
> nit: maybe
>
> If `ptr` is `Some(p)`?
>
> because `ptr` carries the provenance not only addresses.
I don't mind either versions, this one was proposed by Benno.
>
> > + /// 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)) };
> > + }
> > +}
>
^ permalink raw reply [flat|nested] 77+ messages in thread
* Re: [PATCH v7 01/26] rust: alloc: add `Allocator` trait
2024-09-15 17:02 ` Danilo Krummrich
@ 2024-09-15 19:22 ` Gary Guo
2024-09-15 20:08 ` Gary Guo
2024-09-15 21:37 ` Danilo Krummrich
0 siblings, 2 replies; 77+ messages in thread
From: Gary Guo @ 2024-09-15 19:22 UTC (permalink / raw)
To: Danilo Krummrich
Cc: ojeda, alex.gaynor, wedsonaf, boqun.feng, bjorn3_gh, benno.lossin,
a.hindborg, aliceryhl, akpm, daniel.almeida, faith.ekstrand,
boris.brezillon, lina, mcanal, zhiw, cjia, jhubbard, airlied,
ajanulgu, lyude, linux-kernel, rust-for-linux, linux-mm
On Sun, 15 Sep 2024 19:02:40 +0200
Danilo Krummrich <dakr@kernel.org> wrote:
> Hi Gary,
>
> thanks for taking a look.
>
> On Sun, Sep 15, 2024 at 04:28:13PM +0100, Gary Guo wrote:
> > On Thu, 12 Sep 2024 00:52:37 +0200
> > Danilo Krummrich <dakr@kernel.org> 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`.
> >
> > Hi Danilo,
> >
> > I think the current design is unsound regarding ZST.
> >
> > Let's say that `Allocator::alloc` gets called with a ZST type with
> > alignment of 4096. Your implementation will call into `krelloc` with
> > new_size of 0, which gets turned into of `kfree` of null pointer, which
> > is no-op. Everything is fine so far. Krealloc returns `ZERO_SIZE_PTR`,
> > and then implementation of `<Kmalloc as Allocator>::realloc` throws it
> > away and returns `NonNull::dangling`.
> >
> > Since `NonNull::dangling` is called with T=u8, this means the pointer
> > returns is 1, and it's invalid for ZSTs with larger alignments.
>
> Right, this interface is not meant to handle "allocations" for ZSTs.
>
> But you're right, since `alloc` is a safe function, we should return a properly
> aligned pointer.
>
> >
> > And this is unfixable even if the realloc implementation is changed.
> > Let's say the realloc now returns a dangling pointer that is suitable
> > aligned. Now let's see what happens when the `Allocator::free` is
> > called. `kfree` would be trying to free a Rust-side ZST pointer, but it
> > has no way to know that it's ZST!
>
> Right, that's why it's not valid to call `free` with dangling pointers.
>
> From the safety comment of `free`:
>
> "`ptr` must point to an existing and valid memory allocation created by this
> `Allocator` and must not be a dangling pointer."
>
> We still need the same in `realloc` though.
I don't agree with this reading. If you allocate something with `alloc`
and it doesn't return an error then you should be able to feed it to
`free`. Whether the allocator does actual allocation when size is zero
or return a dangling pointer shouldn't matter to the caller.
The fact you `Kmalloc` returns a dangling pointer for ZST is an
implementation detail and the caller shouldn't care (and it also
couldn't check whether it's a dangling pointer). Nothing in your
`alloc` doc mention about dangling pointer return for zero-sized alloc
at all.
>
> >
> > I can see 3 ways of fixing this:
> > 1. Reject ZSTs that have larger alignment than 16 and fix the realloc
> > implementation to return suitable aligned ZST pointer. I don't
> > particularly like the idea of allocating ZST can fail though.
> > 2. Say ZST must be handled by the caller, and make alloc function
> > unsafe. This means that we essentially revert to the `GlobalAlloc`
> > design of Rust, and all callers have to check for ZST.
> > 3. Accept the `old_layout` and use it to check whether the allocation
> > is ZST allocation.
> >
> > My personal preference is 3.
>
> There is also 4.
>
> Let `alloc` and `realloc` return a properly aligned dangling pointer for
> `size == 0` and don't accept dangling pointers in `realloc` and `free`.
I'll consider the API design to be bad if I can't pass allocated pointer to
free. If caller needs to handle ZST specially then we might as well
just ban it completely.
> And 5.
>
> Reject the combination of `None` and `size == 0` entirely, as earlier proposed
> by Benno.
>
> I'm fine with both, 4. and 5. with a slight preference for 4.
>
> I'd also go along with 1., as a mix of 4. and 5.
>
> I really don't like making `alloc` unsafe, and I really don't want to have
> `old_layout` in `free`. Please let's not discuss this again. :-)
I don't buy it.
Your argument for having `old_layout` is so that the caller doesn't
need to care about the size. But as demonstrated the caller *does* need
to care about whether the size is zero.
Our previous discussion doesn't cover the particular case of ZST and
you said that it reason arise that we need this extra parameter, then
it could be added. It feels to me that sane behaviour when it comes
to ZST allocation is a very good reason.
>
> >
> > Best,
> > Gary
> >
> > >
> > > Reviewed-by: Alice Ryhl <aliceryhl@google.com>
> > > Signed-off-by: Danilo Krummrich <dakr@kernel.org>
> > > ---
> > > rust/kernel/alloc.rs | 112 +++++++++++++++++++++++++++++++++++++++++++
> > > 1 file changed, 112 insertions(+)
^ permalink raw reply [flat|nested] 77+ messages in thread
* Re: [PATCH v7 01/26] rust: alloc: add `Allocator` trait
2024-09-15 19:22 ` Gary Guo
@ 2024-09-15 20:08 ` Gary Guo
2024-09-15 21:39 ` Danilo Krummrich
2024-09-15 21:37 ` Danilo Krummrich
1 sibling, 1 reply; 77+ messages in thread
From: Gary Guo @ 2024-09-15 20:08 UTC (permalink / raw)
To: Danilo Krummrich
Cc: ojeda, alex.gaynor, wedsonaf, boqun.feng, bjorn3_gh, benno.lossin,
a.hindborg, aliceryhl, akpm, daniel.almeida, faith.ekstrand,
boris.brezillon, lina, mcanal, zhiw, cjia, jhubbard, airlied,
ajanulgu, lyude, linux-kernel, rust-for-linux, linux-mm
On Sun, 15 Sep 2024 20:22:42 +0100
Gary Guo <gary@garyguo.net> wrote:
> On Sun, 15 Sep 2024 19:02:40 +0200
> Danilo Krummrich <dakr@kernel.org> wrote:
>
> > Hi Gary,
> >
> > thanks for taking a look.
> >
> > On Sun, Sep 15, 2024 at 04:28:13PM +0100, Gary Guo wrote:
> > > On Thu, 12 Sep 2024 00:52:37 +0200
> > > Danilo Krummrich <dakr@kernel.org> 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`.
> > >
> > > Hi Danilo,
> > >
> > > I think the current design is unsound regarding ZST.
> > >
> > > Let's say that `Allocator::alloc` gets called with a ZST type with
> > > alignment of 4096. Your implementation will call into `krelloc` with
> > > new_size of 0, which gets turned into of `kfree` of null pointer, which
> > > is no-op. Everything is fine so far. Krealloc returns `ZERO_SIZE_PTR`,
> > > and then implementation of `<Kmalloc as Allocator>::realloc` throws it
> > > away and returns `NonNull::dangling`.
> > >
> > > Since `NonNull::dangling` is called with T=u8, this means the pointer
> > > returns is 1, and it's invalid for ZSTs with larger alignments.
> >
> > Right, this interface is not meant to handle "allocations" for ZSTs.
> >
> > But you're right, since `alloc` is a safe function, we should return a properly
> > aligned pointer.
> >
> > >
> > > And this is unfixable even if the realloc implementation is changed.
> > > Let's say the realloc now returns a dangling pointer that is suitable
> > > aligned. Now let's see what happens when the `Allocator::free` is
> > > called. `kfree` would be trying to free a Rust-side ZST pointer, but it
> > > has no way to know that it's ZST!
> >
> > Right, that's why it's not valid to call `free` with dangling pointers.
> >
> > From the safety comment of `free`:
> >
> > "`ptr` must point to an existing and valid memory allocation created by this
> > `Allocator` and must not be a dangling pointer."
> >
> > We still need the same in `realloc` though.
>
> I don't agree with this reading. If you allocate something with `alloc`
> and it doesn't return an error then you should be able to feed it to
> `free`. Whether the allocator does actual allocation when size is zero
> or return a dangling pointer shouldn't matter to the caller.
>
> The fact you `Kmalloc` returns a dangling pointer for ZST is an
> implementation detail and the caller shouldn't care (and it also
> couldn't check whether it's a dangling pointer). Nothing in your
> `alloc` doc mention about dangling pointer return for zero-sized alloc
> at all.
>
> >
> > >
> > > I can see 3 ways of fixing this:
> > > 1. Reject ZSTs that have larger alignment than 16 and fix the realloc
> > > implementation to return suitable aligned ZST pointer. I don't
> > > particularly like the idea of allocating ZST can fail though.
> > > 2. Say ZST must be handled by the caller, and make alloc function
> > > unsafe. This means that we essentially revert to the `GlobalAlloc`
> > > design of Rust, and all callers have to check for ZST.
> > > 3. Accept the `old_layout` and use it to check whether the allocation
> > > is ZST allocation.
> > >
> > > My personal preference is 3.
> >
> > There is also 4.
> >
> > Let `alloc` and `realloc` return a properly aligned dangling pointer for
> > `size == 0` and don't accept dangling pointers in `realloc` and `free`.
>
> I'll consider the API design to be bad if I can't pass allocated pointer to
> free. If caller needs to handle ZST specially then we might as well
> just ban it completely.
>
> > And 5.
> >
> > Reject the combination of `None` and `size == 0` entirely, as earlier proposed
> > by Benno.
> >
> > I'm fine with both, 4. and 5. with a slight preference for 4.
> >
> > I'd also go along with 1., as a mix of 4. and 5.
> >
> > I really don't like making `alloc` unsafe, and I really don't want to have
> > `old_layout` in `free`. Please let's not discuss this again. :-)
>
> I don't buy it.
>
> Your argument for having `old_layout` is so that the caller doesn't
> need to care about the size. But as demonstrated the caller *does* need
> to care about whether the size is zero.
>
> Our previous discussion doesn't cover the particular case of ZST and
> you said that it reason arise that we need this extra parameter, then
> it could be added. It feels to me that sane behaviour when it comes
> to ZST allocation is a very good reason.
Just to add that if you *really* want to avoid the old layout param,
another approach accceptable to me is to have a `NonZeroLayout` and have
`alloc` only accept that.
Either `Allocator` trait handles ZST well or it refuses to handle them
at all. No "it works for alloc but not for free" please.
Best,
Gary
> > >
> > > >
> > > > Reviewed-by: Alice Ryhl <aliceryhl@google.com>
> > > > Signed-off-by: Danilo Krummrich <dakr@kernel.org>
> > > > ---
> > > > rust/kernel/alloc.rs | 112 +++++++++++++++++++++++++++++++++++++++++++
> > > > 1 file changed, 112 insertions(+)
>
^ permalink raw reply [flat|nested] 77+ messages in thread
* Re: [PATCH v7 01/26] rust: alloc: add `Allocator` trait
2024-09-15 19:22 ` Gary Guo
2024-09-15 20:08 ` Gary Guo
@ 2024-09-15 21:37 ` Danilo Krummrich
1 sibling, 0 replies; 77+ messages in thread
From: Danilo Krummrich @ 2024-09-15 21:37 UTC (permalink / raw)
To: Gary Guo
Cc: ojeda, alex.gaynor, wedsonaf, boqun.feng, bjorn3_gh, benno.lossin,
a.hindborg, aliceryhl, akpm, daniel.almeida, faith.ekstrand,
boris.brezillon, lina, mcanal, zhiw, cjia, jhubbard, airlied,
ajanulgu, lyude, linux-kernel, rust-for-linux, linux-mm
On Sun, Sep 15, 2024 at 08:22:42PM +0100, Gary Guo wrote:
> On Sun, 15 Sep 2024 19:02:40 +0200
> Danilo Krummrich <dakr@kernel.org> wrote:
> > There is also 4.
> >
> > Let `alloc` and `realloc` return a properly aligned dangling pointer for
> > `size == 0` and don't accept dangling pointers in `realloc` and `free`.
>
> I'll consider the API design to be bad if I can't pass allocated pointer to
> free. If caller needs to handle ZST specially then we might as well
> just ban it completely.
Fine for me -- I don't see a need to support ZSTs with `Allocator`.
I think its main purpose is to give us an interface to actually allocate memory.
We probably don't need it to be a "dangling pointer generator".
>
> > And 5.
> >
> > Reject the combination of `None` and `size == 0` entirely, as earlier proposed
> > by Benno.
> >
> > I'm fine with both, 4. and 5. with a slight preference for 4.
> >
> > I'd also go along with 1., as a mix of 4. and 5.
> >
> > I really don't like making `alloc` unsafe, and I really don't want to have
> > `old_layout` in `free`. Please let's not discuss this again. :-)
>
> I don't buy it.
>
> Your argument for having `old_layout` is so that the caller doesn't
> need to care about the size. But as demonstrated the caller *does* need
> to care about whether the size is zero.
>
> Our previous discussion doesn't cover the particular case of ZST and
> you said that it reason arise that we need this extra parameter, then
> it could be added. It feels to me that sane behaviour when it comes
> to ZST allocation is a very good reason.
I don't see why we should "optimize" the API for creating dangling pointers and
be able to pass them to `free` (which does not serve any practical purpose).
I don't want to add arguments that are meaningless for the actual backing
allocators (such as Kmalloc, Vmalloc, etc.), only to be able to generate and
"free" pointers for ZSTs with arbitrary alignment.
Do we even have use cases for ZSTs with other alignments?
>
> >
> > >
> > > Best,
> > > Gary
> > >
> > > >
> > > > Reviewed-by: Alice Ryhl <aliceryhl@google.com>
> > > > Signed-off-by: Danilo Krummrich <dakr@kernel.org>
> > > > ---
> > > > rust/kernel/alloc.rs | 112 +++++++++++++++++++++++++++++++++++++++++++
> > > > 1 file changed, 112 insertions(+)
>
^ permalink raw reply [flat|nested] 77+ messages in thread
* Re: [PATCH v7 01/26] rust: alloc: add `Allocator` trait
2024-09-15 20:08 ` Gary Guo
@ 2024-09-15 21:39 ` Danilo Krummrich
0 siblings, 0 replies; 77+ messages in thread
From: Danilo Krummrich @ 2024-09-15 21:39 UTC (permalink / raw)
To: Gary Guo
Cc: ojeda, alex.gaynor, wedsonaf, boqun.feng, bjorn3_gh, benno.lossin,
a.hindborg, aliceryhl, akpm, daniel.almeida, faith.ekstrand,
boris.brezillon, lina, mcanal, zhiw, cjia, jhubbard, airlied,
ajanulgu, lyude, linux-kernel, rust-for-linux, linux-mm
On Sun, Sep 15, 2024 at 09:08:27PM +0100, Gary Guo wrote:
> Just to add that if you *really* want to avoid the old layout param,
> another approach accceptable to me is to have a `NonZeroLayout` and have
> `alloc` only accept that.
That is a good idea, I like that.
>
> Either `Allocator` trait handles ZST well or it refuses to handle them
> at all. No "it works for alloc but not for free" please.
I agree with that, then we shouldn't handle them at all.
>
> Best,
> Gary
>
> > > >
> > > > >
> > > > > Reviewed-by: Alice Ryhl <aliceryhl@google.com>
> > > > > Signed-off-by: Danilo Krummrich <dakr@kernel.org>
> > > > > ---
> > > > > rust/kernel/alloc.rs | 112 +++++++++++++++++++++++++++++++++++++++++++
> > > > > 1 file changed, 112 insertions(+)
> >
>
^ permalink raw reply [flat|nested] 77+ messages in thread
* [RFC PATCH] rust: alloc: pass `old_layout` to `Allocator`
2024-09-11 22:52 ` [PATCH v7 01/26] rust: alloc: add `Allocator` trait Danilo Krummrich
2024-09-15 15:28 ` Gary Guo
@ 2024-09-21 15:32 ` Danilo Krummrich
2024-09-23 13:56 ` Alice Ryhl
1 sibling, 1 reply; 77+ messages in thread
From: Danilo Krummrich @ 2024-09-21 15:32 UTC (permalink / raw)
To: ojeda, alex.gaynor, wedsonaf, boqun.feng, gary, bjorn3_gh,
benno.lossin, a.hindborg, aliceryhl, akpm
Cc: daniel.almeida, faith.ekstrand, boris.brezillon, lina, mcanal,
zhiw, cjia, jhubbard, airlied, ajanulgu, lyude, linux-kernel,
rust-for-linux, linux-mm, Danilo Krummrich
Since this came up a few times, this patch shows how the implementation
looks like with an `old_layout` argument.
Signed-off-by: Danilo Krummrich <dakr@kernel.org>
---
rust/kernel/alloc.rs | 32 ++++++++++++--------------
rust/kernel/alloc/allocator.rs | 28 +++++++++++++++++++----
rust/kernel/alloc/kbox.rs | 13 ++++++-----
rust/kernel/alloc/kvec.rs | 42 +++++++++++++++++++++++++++-------
4 files changed, 78 insertions(+), 37 deletions(-)
diff --git a/rust/kernel/alloc.rs b/rust/kernel/alloc.rs
index 2170b53acd0c..78564eeb987d 100644
--- a/rust/kernel/alloc.rs
+++ b/rust/kernel/alloc.rs
@@ -166,7 +166,7 @@ pub unsafe trait Allocator {
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) }
+ unsafe { Self::realloc(None, layout, Layout::new::<()>(), flags) }
}
/// Re-allocate an existing memory allocation to satisfy the requested `layout`.
@@ -186,26 +186,23 @@ fn alloc(layout: Layout, flags: Flags) -> Result<NonNull<[u8]>, AllocError> {
///
/// # 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.
+ /// - If `ptr == Some(p)`, then `p` must point to an existing and valid memory allocation
+ /// created by this allocator.
+ /// - `ptr` is allowed to be `None`; in this case a new memory allocation is created.
+ /// - `old_layout` must match the `Layout` the allocation has been created with.
///
/// # 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.
+ /// and old size, i.e. `ret_ptr[0..min(layout.size(), old_layout.size())] ==
+ /// p[0..min(layout.size(), old_layout.size())]`.
+ /// - when the return value is `Err(AllocError)`, then `ptr` is still valid.
unsafe fn realloc(
ptr: Option<NonNull<u8>>,
layout: Layout,
+ old_layout: Layout,
flags: Flags,
) -> Result<NonNull<[u8]>, AllocError>;
@@ -213,14 +210,13 @@ unsafe fn realloc(
///
/// # 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>) {
+ /// - `ptr` must point to an existing and valid memory allocation created by this `Allocator`.
+ /// - `layout` must match the `Layout` the allocation has been created with.
+ /// - The memory allocation at `ptr` must never again be read from or written to.
+ unsafe fn free(ptr: NonNull<u8>, layout: Layout) {
// 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)) };
+ let _ = unsafe { Self::realloc(Some(ptr), Layout::new::<()>(), layout, Flags(0)) };
}
}
diff --git a/rust/kernel/alloc/allocator.rs b/rust/kernel/alloc/allocator.rs
index 0b586c0361f4..07820c8c4e17 100644
--- a/rust/kernel/alloc/allocator.rs
+++ b/rust/kernel/alloc/allocator.rs
@@ -54,6 +54,14 @@ fn aligned_size(new_layout: Layout) -> usize {
layout.size()
}
+/// Returns a properly aligned dangling pointer from the given `layout`.
+fn zst_realloc(layout: Layout) -> NonNull<u8> {
+ let ptr = layout.align() as *mut u8;
+
+ // SAFETY: `layout.align()` (and hence `ptr`) is guaranteed to be non-zero.
+ unsafe { NonNull::new_unchecked(ptr) }
+}
+
/// # Invariants
///
/// One of the following `krealloc`, `vrealloc`, `kvrealloc`.
@@ -84,11 +92,18 @@ unsafe fn call(
&self,
ptr: Option<NonNull<u8>>,
layout: Layout,
+ old_layout: Layout,
flags: Flags,
) -> Result<NonNull<[u8]>, AllocError> {
let size = aligned_size(layout);
let ptr = match ptr {
- Some(ptr) => ptr.as_ptr(),
+ Some(ptr) => {
+ if old_layout.size() == 0 {
+ ptr::null()
+ } else {
+ ptr.as_ptr()
+ }
+ }
None => ptr::null(),
};
@@ -106,7 +121,7 @@ unsafe fn call(
};
let ptr = if size == 0 {
- NonNull::dangling()
+ zst_realloc(layout)
} else {
NonNull::new(raw_ptr).ok_or(AllocError)?
};
@@ -124,10 +139,11 @@ unsafe impl Allocator for Kmalloc {
unsafe fn realloc(
ptr: Option<NonNull<u8>>,
layout: Layout,
+ old_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 { ReallocFunc::KREALLOC.call(ptr, layout, old_layout, flags) }
}
}
@@ -140,6 +156,7 @@ unsafe impl Allocator for Vmalloc {
unsafe fn realloc(
ptr: Option<NonNull<u8>>,
layout: Layout,
+ old_layout: Layout,
flags: Flags,
) -> Result<NonNull<[u8]>, AllocError> {
// TODO: Support alignments larger than PAGE_SIZE.
@@ -150,7 +167,7 @@ unsafe fn realloc(
// 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) }
+ unsafe { ReallocFunc::VREALLOC.call(ptr, layout, old_layout, flags) }
}
}
@@ -163,6 +180,7 @@ unsafe impl Allocator for KVmalloc {
unsafe fn realloc(
ptr: Option<NonNull<u8>>,
layout: Layout,
+ old_layout: Layout,
flags: Flags,
) -> Result<NonNull<[u8]>, AllocError> {
// TODO: Support alignments larger than PAGE_SIZE.
@@ -173,6 +191,6 @@ unsafe fn realloc(
// 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) }
+ unsafe { ReallocFunc::KVREALLOC.call(ptr, layout, old_layout, flags) }
}
}
diff --git a/rust/kernel/alloc/kbox.rs b/rust/kernel/alloc/kbox.rs
index 6188494f040d..e9e2e94430ef 100644
--- a/rust/kernel/alloc/kbox.rs
+++ b/rust/kernel/alloc/kbox.rs
@@ -5,6 +5,7 @@
#[allow(unused_imports)] // Used in doc comments.
use super::allocator::{KVmalloc, Kmalloc, Vmalloc};
use super::{AllocError, Allocator, Flags};
+use core::alloc::Layout;
use core::fmt;
use core::marker::PhantomData;
use core::mem::ManuallyDrop;
@@ -233,7 +234,7 @@ pub fn new_uninit(flags: Flags) -> Result<Box<MaybeUninit<T>, A>, AllocError> {
let ptr = if Self::is_zst() {
NonNull::dangling()
} else {
- let layout = core::alloc::Layout::new::<MaybeUninit<T>>();
+ let layout = Layout::new::<MaybeUninit<T>>();
let ptr = A::alloc(layout, flags)?;
ptr.cast()
@@ -452,14 +453,14 @@ impl<T, A> Drop for Box<T, A>
A: Allocator,
{
fn drop(&mut self) {
- let size = core::mem::size_of_val::<T>(self);
+ let layout = Layout::for_value::<T>(self);
// SAFETY: The pointer in `self.0` is guaranteed to be valid by the type invariant.
unsafe { core::ptr::drop_in_place::<T>(self.deref_mut()) };
- if size != 0 {
- // SAFETY: As `size` is not zero, `self.0` was previously allocated with `A`.
- unsafe { A::free(self.0.cast()) };
- }
+ // SAFETY:
+ // - `self.0` was previously allocated with `A`.
+ // - `layout` is equal to the `Layout´ `self.0` was allocated with.
+ unsafe { A::free(self.0.cast(), layout) };
}
}
diff --git a/rust/kernel/alloc/kvec.rs b/rust/kernel/alloc/kvec.rs
index 686e969463f8..fb979013562c 100644
--- a/rust/kernel/alloc/kvec.rs
+++ b/rust/kernel/alloc/kvec.rs
@@ -7,6 +7,7 @@
AllocError, Allocator, Box, Flags,
};
use core::{
+ alloc::Layout,
fmt,
marker::PhantomData,
mem::{ManuallyDrop, MaybeUninit},
@@ -452,21 +453,28 @@ pub fn reserve(&mut self, additional: usize, flags: Flags) -> Result<(), AllocEr
// We know `cap` is <= `isize::MAX` because of the type invariants of `Self`. So the
// multiplication by 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)?;
+ let layout = Layout::array::<T>(new_cap).map_err(|_| AllocError)?;
+
+ let old_layout = Layout::array::<T>(self.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.
+ //
+ // Still required? In `Vec::new` we use `NonNull::dangling`, which effectively would be
+ // valid to pass to `A::realloc`, but was never created by one the `Allocator`'s functions.
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)? };
+ // SAFETY:
+ // - `ptr` is valid because it's either `None` or comes from a previous call to
+ // `A::realloc`.
+ // - `old_layout` matches the `Layout` of the preceeding allocation, if any.
+ let ptr = unsafe { A::realloc(ptr, layout, old_layout, flags)? };
self.ptr = ptr.cast();
@@ -528,9 +536,16 @@ fn drop(&mut self) {
};
// If `cap == 0` we never allocated any memory in the first place.
+ //
+ // Same here, theoretically, we know that `NonNull::dangling` from `Vec::new` is valid for
+ // `A::free`, but it was never created by any of the `Allocator` functions.
if self.cap != 0 {
+ // This can never fail; since this `Layout` is equivalent to the one of the original
+ // allocation.
+ let layout = Layout::array::<T>(self.cap).unwrap();
+
// SAFETY: `self.ptr` was previously allocated with `A`.
- unsafe { A::free(self.ptr.cast()) };
+ unsafe { A::free(self.ptr.cast(), layout) };
}
}
}
@@ -751,13 +766,17 @@ pub fn collect(self, flags: Flags) -> Vec<T, A> {
ptr = buf.as_ptr();
}
+ // This can never fail; since this `Layout` is equivalent to the one of the original
+ // allocation.
+ let old_layout = Layout::array::<T>(cap).unwrap();
+
// This can never fail, `len` is guaranteed to be smaller than `cap`.
- let layout = core::alloc::Layout::array::<T>(len).unwrap();
+ let layout = 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) } {
+ ptr = match unsafe { A::realloc(Some(buf.cast()), layout, old_layout, flags) } {
// If we fail to shrink, which likely can't even happen, continue with the existing
// buffer.
Err(_) => ptr,
@@ -846,9 +865,16 @@ fn drop(&mut self) {
unsafe { ptr::drop_in_place(self.as_raw_mut_slice()) };
// If `cap == 0` we never allocated any memory in the first place.
+ //
+ // Same here, theoretically, we know that `NonNull::dangling` from `Vec::new` is valid for
+ // `A::free`, but it was never created by any of the `Allocator` functions.
if self.cap != 0 {
+ // This can never fail; since this `Layout` is equivalent to the one of the original
+ // allocation.
+ let layout = Layout::array::<T>(self.cap).unwrap();
+
// SAFETY: `self.buf` was previously allocated with `A`.
- unsafe { A::free(self.buf.cast()) };
+ unsafe { A::free(self.buf.cast(), layout) };
}
}
}
--
2.46.1
^ permalink raw reply related [flat|nested] 77+ messages in thread
* Re: [RFC PATCH] rust: alloc: pass `old_layout` to `Allocator`
2024-09-21 15:32 ` [RFC PATCH] rust: alloc: pass `old_layout` to `Allocator` Danilo Krummrich
@ 2024-09-23 13:56 ` Alice Ryhl
2024-09-23 15:20 ` Benno Lossin
2024-09-23 16:13 ` Gary Guo
0 siblings, 2 replies; 77+ messages in thread
From: Alice Ryhl @ 2024-09-23 13:56 UTC (permalink / raw)
To: Danilo Krummrich
Cc: ojeda, alex.gaynor, wedsonaf, boqun.feng, gary, bjorn3_gh,
benno.lossin, a.hindborg, akpm, daniel.almeida, faith.ekstrand,
boris.brezillon, lina, mcanal, zhiw, cjia, jhubbard, airlied,
ajanulgu, lyude, linux-kernel, rust-for-linux, linux-mm
On Sat, Sep 21, 2024 at 5:33 PM Danilo Krummrich <dakr@kernel.org> wrote:
> @@ -84,11 +92,18 @@ unsafe fn call(
> &self,
> ptr: Option<NonNull<u8>>,
> layout: Layout,
> + old_layout: Layout,
> flags: Flags,
> ) -> Result<NonNull<[u8]>, AllocError> {
> let size = aligned_size(layout);
> let ptr = match ptr {
> - Some(ptr) => ptr.as_ptr(),
> + Some(ptr) => {
> + if old_layout.size() == 0 {
> + ptr::null()
> + } else {
> + ptr.as_ptr()
> + }
> + }
This is making Allocator work with zero-sized types, which deviates
from std. We should not do that without a reason. What is the reason?
Alice
^ permalink raw reply [flat|nested] 77+ messages in thread
* Re: [RFC PATCH] rust: alloc: pass `old_layout` to `Allocator`
2024-09-23 13:56 ` Alice Ryhl
@ 2024-09-23 15:20 ` Benno Lossin
2024-09-23 16:13 ` Gary Guo
1 sibling, 0 replies; 77+ messages in thread
From: Benno Lossin @ 2024-09-23 15:20 UTC (permalink / raw)
To: Alice Ryhl, Danilo Krummrich
Cc: ojeda, alex.gaynor, wedsonaf, boqun.feng, gary, bjorn3_gh,
a.hindborg, akpm, daniel.almeida, faith.ekstrand, boris.brezillon,
lina, mcanal, zhiw, cjia, jhubbard, airlied, ajanulgu, lyude,
linux-kernel, rust-for-linux, linux-mm
On 23.09.24 15:56, Alice Ryhl wrote:
> On Sat, Sep 21, 2024 at 5:33 PM Danilo Krummrich <dakr@kernel.org> wrote:
>> @@ -84,11 +92,18 @@ unsafe fn call(
>> &self,
>> ptr: Option<NonNull<u8>>,
>> layout: Layout,
>> + old_layout: Layout,
>> flags: Flags,
>> ) -> Result<NonNull<[u8]>, AllocError> {
>> let size = aligned_size(layout);
>> let ptr = match ptr {
>> - Some(ptr) => ptr.as_ptr(),
>> + Some(ptr) => {
>> + if old_layout.size() == 0 {
>> + ptr::null()
>> + } else {
>> + ptr.as_ptr()
>> + }
>> + }
>
> This is making Allocator work with zero-sized types, which deviates
> from std. We should not do that without a reason. What is the reason?
The global allocator doesn't support it, but the `Allocator` trait from
std handles zero-sized allocations. For example, this code runs as
expected:
#![feature(allocator_api)]
use std::alloc::{self, Allocator, Layout};
fn main() {
let alloc: &dyn Allocator = &alloc::Global;
let ptr = alloc.allocate(Layout::new::<()>()).unwrap().cast::<u8>();
unsafe { alloc.deallocate(ptr, Layout::new::<()>()) };
}
https://play.rust-lang.org/?version=nightly&mode=debug&edition=2021&gist=0a2d12ee6dabf16f2ebd67cc6faa864e
---
Cheers,
Benno
^ permalink raw reply [flat|nested] 77+ messages in thread
* Re: [RFC PATCH] rust: alloc: pass `old_layout` to `Allocator`
2024-09-23 13:56 ` Alice Ryhl
2024-09-23 15:20 ` Benno Lossin
@ 2024-09-23 16:13 ` Gary Guo
2024-09-24 13:31 ` Danilo Krummrich
1 sibling, 1 reply; 77+ messages in thread
From: Gary Guo @ 2024-09-23 16:13 UTC (permalink / raw)
To: Alice Ryhl
Cc: Danilo Krummrich, ojeda, alex.gaynor, wedsonaf, boqun.feng,
bjorn3_gh, benno.lossin, a.hindborg, akpm, daniel.almeida,
faith.ekstrand, boris.brezillon, lina, mcanal, zhiw, cjia,
jhubbard, airlied, ajanulgu, lyude, linux-kernel, rust-for-linux,
linux-mm
On Mon, 23 Sep 2024 15:56:28 +0200
Alice Ryhl <aliceryhl@google.com> wrote:
> On Sat, Sep 21, 2024 at 5:33 PM Danilo Krummrich <dakr@kernel.org> wrote:
> > @@ -84,11 +92,18 @@ unsafe fn call(
> > &self,
> > ptr: Option<NonNull<u8>>,
> > layout: Layout,
> > + old_layout: Layout,
> > flags: Flags,
> > ) -> Result<NonNull<[u8]>, AllocError> {
> > let size = aligned_size(layout);
> > let ptr = match ptr {
> > - Some(ptr) => ptr.as_ptr(),
> > + Some(ptr) => {
> > + if old_layout.size() == 0 {
> > + ptr::null()
> > + } else {
> > + ptr.as_ptr()
> > + }
> > + }
>
> This is making Allocator work with zero-sized types, which deviates
> from std. We should not do that without a reason. What is the reason?
>
> Alice
As Benno said, this makes the API closer to Rust `allocator_api`
Allocator trait as opposed to deviation.
There's one benefit of doing this (discussed with Danilo off-list),
which is it removes ZST special casing from caller. This RFC patch
simplifies `Box` handling, and if we add this line to the safety doc
`ptr` does not need to be a pointer returned by this
allocator if the layout is zero-sized.
then the `Vec` can also be simplified, removing all logics handling ZST
specially, except for `Vec::new()` which it forges a well-aligned
dangling pointer from nowhere.
Best,
Gary
^ permalink raw reply [flat|nested] 77+ messages in thread
* Re: [RFC PATCH] rust: alloc: pass `old_layout` to `Allocator`
2024-09-23 16:13 ` Gary Guo
@ 2024-09-24 13:31 ` Danilo Krummrich
2024-09-24 13:34 ` Danilo Krummrich
2024-09-24 19:58 ` Gary Guo
0 siblings, 2 replies; 77+ messages in thread
From: Danilo Krummrich @ 2024-09-24 13:31 UTC (permalink / raw)
To: Gary Guo
Cc: Alice Ryhl, ojeda, alex.gaynor, wedsonaf, boqun.feng, bjorn3_gh,
benno.lossin, a.hindborg, akpm, daniel.almeida, faith.ekstrand,
boris.brezillon, lina, mcanal, zhiw, cjia, jhubbard, airlied,
ajanulgu, lyude, linux-kernel, rust-for-linux, linux-mm
On Mon, Sep 23, 2024 at 05:13:15PM +0100, Gary Guo wrote:
> On Mon, 23 Sep 2024 15:56:28 +0200
> Alice Ryhl <aliceryhl@google.com> wrote:
>
> > On Sat, Sep 21, 2024 at 5:33 PM Danilo Krummrich <dakr@kernel.org> wrote:
> > > @@ -84,11 +92,18 @@ unsafe fn call(
> > > &self,
> > > ptr: Option<NonNull<u8>>,
> > > layout: Layout,
> > > + old_layout: Layout,
> > > flags: Flags,
> > > ) -> Result<NonNull<[u8]>, AllocError> {
> > > let size = aligned_size(layout);
> > > let ptr = match ptr {
> > > - Some(ptr) => ptr.as_ptr(),
> > > + Some(ptr) => {
> > > + if old_layout.size() == 0 {
> > > + ptr::null()
> > > + } else {
> > > + ptr.as_ptr()
> > > + }
> > > + }
> >
> > This is making Allocator work with zero-sized types, which deviates
> > from std. We should not do that without a reason. What is the reason?
> >
> > Alice
>
> As Benno said, this makes the API closer to Rust `allocator_api`
> Allocator trait as opposed to deviation.
>
> There's one benefit of doing this (discussed with Danilo off-list),
> which is it removes ZST special casing from caller. This RFC patch
> simplifies `Box` handling, and if we add this line to the safety doc
>
> `ptr` does not need to be a pointer returned by this
> allocator if the layout is zero-sized.
>
> then the `Vec` can also be simplified, removing all logics handling ZST
> specially, except for `Vec::new()` which it forges a well-aligned
> dangling pointer from nowhere.
Partially, we still need the additional `Layout` for `Allocator::free`, which
in `Vec::drop` and `IntoIter::drop` looks like this:
`let layout = Layout::array::<T>(self.cap).unwrap();`
I really dislike that this can potentially transform into `BUG()`, but that's
probably unrelated to this patch series.
>
> Best,
> Gary
>
^ permalink raw reply [flat|nested] 77+ messages in thread
* Re: [RFC PATCH] rust: alloc: pass `old_layout` to `Allocator`
2024-09-24 13:31 ` Danilo Krummrich
@ 2024-09-24 13:34 ` Danilo Krummrich
2024-09-24 19:58 ` Gary Guo
1 sibling, 0 replies; 77+ messages in thread
From: Danilo Krummrich @ 2024-09-24 13:34 UTC (permalink / raw)
To: Gary Guo
Cc: Alice Ryhl, ojeda, alex.gaynor, wedsonaf, boqun.feng, bjorn3_gh,
benno.lossin, a.hindborg, akpm, daniel.almeida, faith.ekstrand,
boris.brezillon, lina, mcanal, zhiw, cjia, jhubbard, airlied,
ajanulgu, lyude, linux-kernel, rust-for-linux, linux-mm
On Tue, Sep 24, 2024 at 03:31:55PM +0200, Danilo Krummrich wrote:
> On Mon, Sep 23, 2024 at 05:13:15PM +0100, Gary Guo wrote:
> > On Mon, 23 Sep 2024 15:56:28 +0200
> > Alice Ryhl <aliceryhl@google.com> wrote:
> >
> > > On Sat, Sep 21, 2024 at 5:33 PM Danilo Krummrich <dakr@kernel.org> wrote:
> > > > @@ -84,11 +92,18 @@ unsafe fn call(
> > > > &self,
> > > > ptr: Option<NonNull<u8>>,
> > > > layout: Layout,
> > > > + old_layout: Layout,
> > > > flags: Flags,
> > > > ) -> Result<NonNull<[u8]>, AllocError> {
> > > > let size = aligned_size(layout);
> > > > let ptr = match ptr {
> > > > - Some(ptr) => ptr.as_ptr(),
> > > > + Some(ptr) => {
> > > > + if old_layout.size() == 0 {
> > > > + ptr::null()
> > > > + } else {
> > > > + ptr.as_ptr()
> > > > + }
> > > > + }
> > >
> > > This is making Allocator work with zero-sized types, which deviates
> > > from std. We should not do that without a reason. What is the reason?
> > >
> > > Alice
> >
> > As Benno said, this makes the API closer to Rust `allocator_api`
> > Allocator trait as opposed to deviation.
> >
> > There's one benefit of doing this (discussed with Danilo off-list),
> > which is it removes ZST special casing from caller. This RFC patch
> > simplifies `Box` handling, and if we add this line to the safety doc
> >
> > `ptr` does not need to be a pointer returned by this
> > allocator if the layout is zero-sized.
> >
> > then the `Vec` can also be simplified, removing all logics handling ZST
> > specially, except for `Vec::new()` which it forges a well-aligned
> > dangling pointer from nowhere.
>
> Partially, we still need the additional `Layout` for `Allocator::free`, which
> in `Vec::drop` and `IntoIter::drop` looks like this:
>
> `let layout = Layout::array::<T>(self.cap).unwrap();`
Adding in the diff:
- // 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()) };
- }
+ // This can never fail; since this `Layout` is equivalent to the one of the original
+ // allocation.
+ let layout = Layout::array::<T>(self.cap).unwrap();
+
+ // SAFETY: `self.ptr` was previously allocated with `A`.
+ unsafe { A::free(self.ptr.cast(), layout) };
>
> I really dislike that this can potentially transform into `BUG()`, but that's
> probably unrelated to this patch series.
>
> >
> > Best,
> > Gary
> >
^ permalink raw reply [flat|nested] 77+ messages in thread
* Re: [RFC PATCH] rust: alloc: pass `old_layout` to `Allocator`
2024-09-24 13:31 ` Danilo Krummrich
2024-09-24 13:34 ` Danilo Krummrich
@ 2024-09-24 19:58 ` Gary Guo
1 sibling, 0 replies; 77+ messages in thread
From: Gary Guo @ 2024-09-24 19:58 UTC (permalink / raw)
To: Danilo Krummrich
Cc: Alice Ryhl, ojeda, alex.gaynor, wedsonaf, boqun.feng, bjorn3_gh,
benno.lossin, a.hindborg, akpm, daniel.almeida, faith.ekstrand,
boris.brezillon, lina, mcanal, zhiw, cjia, jhubbard, airlied,
ajanulgu, lyude, linux-kernel, rust-for-linux, linux-mm
On Tue, 24 Sep 2024 15:31:47 +0200
Danilo Krummrich <dakr@kernel.org> wrote:
> On Mon, Sep 23, 2024 at 05:13:15PM +0100, Gary Guo wrote:
> > On Mon, 23 Sep 2024 15:56:28 +0200
> > Alice Ryhl <aliceryhl@google.com> wrote:
> >
> > > On Sat, Sep 21, 2024 at 5:33 PM Danilo Krummrich <dakr@kernel.org> wrote:
> > > > @@ -84,11 +92,18 @@ unsafe fn call(
> > > > &self,
> > > > ptr: Option<NonNull<u8>>,
> > > > layout: Layout,
> > > > + old_layout: Layout,
> > > > flags: Flags,
> > > > ) -> Result<NonNull<[u8]>, AllocError> {
> > > > let size = aligned_size(layout);
> > > > let ptr = match ptr {
> > > > - Some(ptr) => ptr.as_ptr(),
> > > > + Some(ptr) => {
> > > > + if old_layout.size() == 0 {
> > > > + ptr::null()
> > > > + } else {
> > > > + ptr.as_ptr()
> > > > + }
> > > > + }
> > >
> > > This is making Allocator work with zero-sized types, which deviates
> > > from std. We should not do that without a reason. What is the reason?
> > >
> > > Alice
> >
> > As Benno said, this makes the API closer to Rust `allocator_api`
> > Allocator trait as opposed to deviation.
> >
> > There's one benefit of doing this (discussed with Danilo off-list),
> > which is it removes ZST special casing from caller. This RFC patch
> > simplifies `Box` handling, and if we add this line to the safety doc
> >
> > `ptr` does not need to be a pointer returned by this
> > allocator if the layout is zero-sized.
> >
> > then the `Vec` can also be simplified, removing all logics handling ZST
> > specially, except for `Vec::new()` which it forges a well-aligned
> > dangling pointer from nowhere.
>
> Partially, we still need the additional `Layout` for `Allocator::free`, which
> in `Vec::drop` and `IntoIter::drop` looks like this:
>
> `let layout = Layout::array::<T>(self.cap).unwrap();`
>
You can add an invariant to `Vec` that the size in bytes does not
exceed `isize::MAX` (which is already true, just not documented as
invariant), and this can be changed to `unwrap_unchecked`.
> I really dislike that this can potentially transform into `BUG()`, but that's
> probably unrelated to this patch series.
^ permalink raw reply [flat|nested] 77+ messages in thread
* Re: [PATCH v7 04/26] rust: alloc: implement `Allocator` for `Kmalloc`
2024-09-11 22:52 ` [PATCH v7 04/26] rust: alloc: implement `Allocator` for `Kmalloc` Danilo Krummrich
@ 2024-09-26 13:00 ` Benno Lossin
2024-09-26 13:24 ` Danilo Krummrich
0 siblings, 1 reply; 77+ messages in thread
From: Benno Lossin @ 2024-09-26 13:00 UTC (permalink / raw)
To: Danilo Krummrich, ojeda, alex.gaynor, wedsonaf, boqun.feng, gary,
bjorn3_gh, a.hindborg, aliceryhl, akpm
Cc: daniel.almeida, faith.ekstrand, boris.brezillon, lina, mcanal,
zhiw, cjia, jhubbard, airlied, ajanulgu, lyude, linux-kernel,
rust-for-linux, linux-mm
On 12.09.24 00:52, Danilo Krummrich wrote:
> +/// # 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`].
> + ///
> + /// # 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.
> + unsafe fn call(
> + &self,
> + ptr: Option<NonNull<u8>>,
> + layout: Layout,
> + flags: Flags,
> + ) -> Result<NonNull<[u8]>, AllocError> {
> + let size = aligned_size(layout);
> + let ptr = match ptr {
> + Some(ptr) => ptr.as_ptr(),
> + None => ptr::null(),
> + };
> +
> + // SAFETY:
> + // - `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.
> + //
> + // GUARANTEE:
> + // - `self.0` is one of `krealloc`, `vrealloc`, `kvrealloc`.
> + // - Those functions provide the guarantees of this function.
> + let raw_ptr = unsafe {
> + // If `size == 0` and `ptr != NULL` the memory behind the pointer is freed.
> + self.0(ptr.cast(), size, flags.0).cast()
> + };
> +
> + let ptr = if size == 0 {
> + NonNull::dangling()
> + } else {
> + NonNull::new(raw_ptr).ok_or(AllocError)?
> + };
> +
> + Ok(NonNull::slice_from_raw_parts(ptr, size))
> + }
> +}
I remember asking you to split this into a different commit. I think you
argued that it would be better to keep it in the same commit when
bisecting. I don't think that applies in this case, are there any other
disadvantages?
---
Cheers,
Benno
> +
> +// SAFETY: `realloc` delegates to `ReallocFunc::call`, which guarantees that
> +// - memory remains valid until it is explicitly freed,
> +// - passing a pointer to a valid memory allocation is OK,
> +// - `realloc` satisfies the guarantees, since `ReallocFunc::call` has the same.
> +unsafe impl Allocator for Kmalloc {
> + #[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
>
^ permalink raw reply [flat|nested] 77+ messages in thread
* Re: [PATCH v7 06/26] rust: alloc: implement `Vmalloc` allocator
2024-09-11 22:52 ` [PATCH v7 06/26] rust: alloc: implement `Vmalloc` allocator Danilo Krummrich
@ 2024-09-26 13:06 ` Benno Lossin
0 siblings, 0 replies; 77+ messages in thread
From: Benno Lossin @ 2024-09-26 13:06 UTC (permalink / raw)
To: Danilo Krummrich, ojeda, alex.gaynor, wedsonaf, boqun.feng, gary,
bjorn3_gh, a.hindborg, aliceryhl, akpm
Cc: daniel.almeida, faith.ekstrand, boris.brezillon, lina, mcanal,
zhiw, cjia, jhubbard, airlied, ajanulgu, lyude, linux-kernel,
rust-for-linux, linux-mm
On 12.09.24 00:52, Danilo Krummrich wrote:
> 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()`.
>
> Reviewed-by: Alice Ryhl <aliceryhl@google.com>
> Signed-off-by: Danilo Krummrich <dakr@kernel.org>
> ---
> rust/helpers/helpers.c | 1 +
> rust/helpers/vmalloc.c | 9 ++++++++
> rust/kernel/alloc/allocator.rs | 36 +++++++++++++++++++++++++++++
> rust/kernel/alloc/allocator_test.rs | 1 +
> 4 files changed, 47 insertions(+)
> create mode 100644 rust/helpers/vmalloc.c
Reviewed-by: Benno Lossin <benno.lossin@proton.me>
---
Cheers,
Benno
^ permalink raw reply [flat|nested] 77+ messages in thread
* Re: [PATCH v7 07/26] rust: alloc: implement `KVmalloc` allocator
2024-09-11 22:52 ` [PATCH v7 07/26] rust: alloc: implement `KVmalloc` allocator Danilo Krummrich
@ 2024-09-26 13:07 ` Benno Lossin
0 siblings, 0 replies; 77+ messages in thread
From: Benno Lossin @ 2024-09-26 13:07 UTC (permalink / raw)
To: Danilo Krummrich, ojeda, alex.gaynor, wedsonaf, boqun.feng, gary,
bjorn3_gh, a.hindborg, aliceryhl, akpm
Cc: daniel.almeida, faith.ekstrand, boris.brezillon, lina, mcanal,
zhiw, cjia, jhubbard, airlied, ajanulgu, lyude, linux-kernel,
rust-for-linux, linux-mm
On 12.09.24 00:52, Danilo Krummrich wrote:
> 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()`.
>
> Reviewed-by: Alice Ryhl <aliceryhl@google.com>
> Signed-off-by: Danilo Krummrich <dakr@kernel.org>
> ---
> rust/helpers/slab.c | 6 +++++
> rust/kernel/alloc/allocator.rs | 35 +++++++++++++++++++++++++++++
> rust/kernel/alloc/allocator_test.rs | 1 +
> 3 files changed, 42 insertions(+)
Reviewed-by: Benno Lossin <benno.lossin@proton.me>
---
Cheers,
Benno
^ permalink raw reply [flat|nested] 77+ messages in thread
* Re: [PATCH v7 09/26] rust: alloc: implement kernel `Box`
2024-09-11 22:52 ` [PATCH v7 09/26] rust: alloc: implement kernel `Box` Danilo Krummrich
@ 2024-09-26 13:23 ` Benno Lossin
2024-09-28 18:54 ` Gary Guo
1 sibling, 0 replies; 77+ messages in thread
From: Benno Lossin @ 2024-09-26 13:23 UTC (permalink / raw)
To: Danilo Krummrich, ojeda, alex.gaynor, wedsonaf, boqun.feng, gary,
bjorn3_gh, a.hindborg, aliceryhl, akpm
Cc: daniel.almeida, faith.ekstrand, boris.brezillon, lina, mcanal,
zhiw, cjia, jhubbard, airlied, ajanulgu, lyude, linux-kernel,
rust-for-linux, linux-mm
On 12.09.24 00:52, Danilo Krummrich wrote:
> `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.
>
> Signed-off-by: Danilo Krummrich <dakr@kernel.org>
> ---
> rust/kernel/alloc.rs | 6 +
> rust/kernel/alloc/kbox.rs | 465 ++++++++++++++++++++++++++++++++++++++
> rust/kernel/prelude.rs | 2 +-
> 3 files changed, 472 insertions(+), 1 deletion(-)
> create mode 100644 rust/kernel/alloc/kbox.rs
I have two nits below. I think the safety documentation can be improved
later, when we have decided on a better approach (I have to to spend a
significant amount of time coming up with a suggestion and even then I
am not satisfied with it). So
Reviewed-by: Benno Lossin <benno.lossin@proton.me>
---
Cheers,
Benno
> +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
Missing space after "with `A`".
> + /// aligned for and holds a valid `T`. The caller passes ownership of the allocation to the
> + /// `Box`.
> + ///
> + /// For ZSTs, `raw` must be a dangling, well aligned pointer.
> + #[inline]
> + pub const unsafe fn from_raw(raw: *mut T) -> Self {
> + // INVARIANT: Validity of `raw` is guaranteed by the safety preconditions of this function.
> + // SAFETY: By the safety preconditions of this function, `raw` is not a NULL pointer.
> + Self(unsafe { NonNull::new_unchecked(raw) }, PhantomData::<A>)
> + }
> +
> + /// Consumes the `Box<T, A>` and returns a raw pointer.
> + ///
> + /// This will not run the destructor of `T` and for non-ZSTs the allocation will stay alive
> + /// indefinitely. Use [`Box::from_raw`] to recover the [`Box`], drop the value and free the
> + /// allocation, if any.
> + ///
> + /// # Examples
> + ///
> + /// ```
> + /// let x = KBox::new(24, GFP_KERNEL)?;
> + /// let ptr = KBox::into_raw(x);
> + /// let x = unsafe { KBox::from_raw(ptr) };
> + ///
> + /// assert_eq!(*x, 24);
> + /// # Ok::<(), Error>(())
> + /// ```
> + #[inline]
> + pub fn into_raw(b: Self) -> *mut T {
> + let b = ManuallyDrop::new(b);
> +
> + b.0.as_ptr()
Why not a single line `ManuallyDrop::new().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) }
> + }
> +}
^ permalink raw reply [flat|nested] 77+ messages in thread
* Re: [PATCH v7 04/26] rust: alloc: implement `Allocator` for `Kmalloc`
2024-09-26 13:00 ` Benno Lossin
@ 2024-09-26 13:24 ` Danilo Krummrich
2024-09-26 14:00 ` Benno Lossin
0 siblings, 1 reply; 77+ messages in thread
From: Danilo Krummrich @ 2024-09-26 13:24 UTC (permalink / raw)
To: Benno Lossin
Cc: ojeda, alex.gaynor, wedsonaf, boqun.feng, gary, bjorn3_gh,
a.hindborg, aliceryhl, akpm, daniel.almeida, faith.ekstrand,
boris.brezillon, lina, mcanal, zhiw, cjia, jhubbard, airlied,
ajanulgu, lyude, linux-kernel, rust-for-linux, linux-mm
On Thu, Sep 26, 2024 at 01:00:58PM +0000, Benno Lossin wrote:
> On 12.09.24 00:52, Danilo Krummrich wrote:
> > +/// # 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`].
> > + ///
> > + /// # 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.
> > + unsafe fn call(
> > + &self,
> > + ptr: Option<NonNull<u8>>,
> > + layout: Layout,
> > + flags: Flags,
> > + ) -> Result<NonNull<[u8]>, AllocError> {
> > + let size = aligned_size(layout);
> > + let ptr = match ptr {
> > + Some(ptr) => ptr.as_ptr(),
> > + None => ptr::null(),
> > + };
> > +
> > + // SAFETY:
> > + // - `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.
> > + //
> > + // GUARANTEE:
> > + // - `self.0` is one of `krealloc`, `vrealloc`, `kvrealloc`.
> > + // - Those functions provide the guarantees of this function.
> > + let raw_ptr = unsafe {
> > + // If `size == 0` and `ptr != NULL` the memory behind the pointer is freed.
> > + self.0(ptr.cast(), size, flags.0).cast()
> > + };
> > +
> > + let ptr = if size == 0 {
> > + NonNull::dangling()
> > + } else {
> > + NonNull::new(raw_ptr).ok_or(AllocError)?
> > + };
> > +
> > + Ok(NonNull::slice_from_raw_parts(ptr, size))
> > + }
> > +}
>
> I remember asking you to split this into a different commit. I think you
> argued that it would be better to keep it in the same commit when
> bisecting. I don't think that applies in this case, are there any other
> disadvantages?
I don't really like the intermediate `#[expect(dead_code)]`, plus it's
additional work you didn't really give me a motivation for, i.e. you did not
mention what would be the advantage.
But sure, I will change it for the next version.
>
> ---
> Cheers,
> Benno
>
> > +
> > +// SAFETY: `realloc` delegates to `ReallocFunc::call`, which guarantees that
> > +// - memory remains valid until it is explicitly freed,
> > +// - passing a pointer to a valid memory allocation is OK,
> > +// - `realloc` satisfies the guarantees, since `ReallocFunc::call` has the same.
> > +unsafe impl Allocator for Kmalloc {
> > + #[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
> >
>
^ permalink raw reply [flat|nested] 77+ messages in thread
* Re: [PATCH v7 13/26] rust: alloc: implement kernel `Vec` type
2024-09-11 22:52 ` [PATCH v7 13/26] rust: alloc: implement kernel `Vec` type Danilo Krummrich
@ 2024-09-26 13:47 ` Benno Lossin
2024-09-28 12:43 ` Danilo Krummrich
2024-09-28 19:14 ` Gary Guo
1 sibling, 1 reply; 77+ messages in thread
From: Benno Lossin @ 2024-09-26 13:47 UTC (permalink / raw)
To: Danilo Krummrich, ojeda, alex.gaynor, wedsonaf, boqun.feng, gary,
bjorn3_gh, a.hindborg, aliceryhl, akpm
Cc: daniel.almeida, faith.ekstrand, boris.brezillon, lina, mcanal,
zhiw, cjia, jhubbard, airlied, ajanulgu, lyude, linux-kernel,
rust-for-linux, linux-mm
On 12.09.24 00:52, Danilo Krummrich wrote:
> diff --git a/rust/kernel/alloc/kvec.rs b/rust/kernel/alloc/kvec.rs
> new file mode 100644
> index 000000000000..631a44e19f35
> --- /dev/null
> +++ b/rust/kernel/alloc/kvec.rs
> @@ -0,0 +1,638 @@
> +// SPDX-License-Identifier: GPL-2.0
> +
> +//! Implementation of [`Vec`].
> +
> +use super::{
> + allocator::{KVmalloc, Kmalloc, Vmalloc},
> + 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.
I would change this to [`KVec`].
> +///
> +/// # 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)?;
> +/// assert_eq!(v, [1, 2, 3, 4]);
> +///
> +/// # Ok::<(), Error>(())
> +/// ```
> +#[macro_export]
> +macro_rules! kvec {
> + () => (
> + $crate::alloc::KVec::new()
> + );
> + ($elem:expr; $n:expr) => (
> + $crate::alloc::KVec::from_elem($elem, $n, GFP_KERNEL)
> + );
> + ($($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),
> + }
> + );
> +}
> +
> +/// 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>`.
Can you turn these into links?
> +///
> +/// 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.
Ditto.
> +///
> +/// 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.
> +///
> +/// 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
> +///
> +/// - `self.ptr` is always properly aligned and either points to memory allocated with `A` or, for
> +/// zero-sized types, is a dangling, well aligned pointer.
> +///
> +/// - `self.len` always represents the exact number of elements stored in the vector.
> +///
> +/// - `self.cap` represents the absolute number of elements that can be stored 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).
> +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>,
> +}
[...]
> + /// Appends an element to the back of the [`Vec`] instance.
> + ///
> + /// # Examples
> + ///
> + /// ```
> + /// let mut v = KVec::new();
> + /// v.push(1, GFP_KERNEL)?;
> + /// 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)?;
> +
> + // SAFETY:
> + // - `self.len` is smaller than `self.capacity` and hence, the resulting pointer is
> + // guaranteed to be part of the same allocated object.
> + // - `self.len` can not overflow `isize`.
> + let ptr = unsafe { self.as_mut_ptr().add(self.len) };
> +
> + // SAFETY:
> + // - `ptr` is properly aligned and valid for writes.
> + unsafe { core::ptr::write(ptr, v) };
Why not use `self.spare_capacity_mut()[0].write(v);`?
If you want to avoid the bounds check, you can do
let first = self.spare_capacity_mut().first();
// SAFETY: the call to `Vec::reserve` above ensures that `spare_capacity_mut()` is non-empty.
unsafe { first.unwrap_unchecked() }.write(v);
> +
> + // 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
> + T: Clone,
This method can be moved into the other impl block below, it already has
the `T: Clone` bound.
> + {
> + self.reserve(other.len(), flags)?;
> + for (slot, item) in core::iter::zip(self.spare_capacity_mut(), other) {
> + slot.write(item.clone());
> + }
> +
> + // SAFETY:
> + // - `other.len()` spare entries have just been initialized, so it is safe to increase
> + // the length by the same number.
> + // - `self.len() + other.len() <= self.capacity()` is guaranteed by the preceding `reserve`
> + // call.
> + 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`.
> + ///
> + /// # 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;
> + ///
> + /// // 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>(())
> + /// ```
> + ///
> + /// # Safety
> + ///
> + /// If `T` is a ZST:
> + ///
> + /// - `ptr` must be a dangling, well aligned pointer.
> + ///
> + /// 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>() * capacity`.
> + /// bytes.
> + /// - The allocated size in bytes must not be larger than `isize::MAX`.
> + /// - `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`.
> + 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>,
> + }
Would be nice to have `debug_assert!(length <= capacity)` here. But feel
free to make that a good-first-issue instead of including it in the next
version. (there are probably more asserts elsewhere)
> + }
> +
> + /// 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 mut me = ManuallyDrop::new(self);
> + let len = me.len();
> + let capacity = me.capacity();
> + let ptr = me.as_mut_ptr();
> + (ptr, len, capacity)
> + }
[...]
> +macro_rules! impl_slice_eq {
> + ([$($vars:tt)*] $lhs:ty, $rhs:ty) => {
You could wrap the entire pattern in "$()*", same for the entire body
and then...
> + impl<T, U, $($vars)*> PartialEq<$rhs> for $lhs
> + where
> + T: PartialEq<U>,
> + {
> + #[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] }
...we could have a single `impl_slice_eq` invocation here:
impl_slice_eq! {
[A1: Allocator, A2: Allocator] Vec<T, A1>, Vec<U, A2>
[A: Allocator] Vec<T, A>, &[U]
[A: Allocator] Vec<T, A>, &mut [U]
[A: Allocator] &[T], Vec<U, A>
[A: Allocator] &mut [T], Vec<U, A>
[A: Allocator] Vec<T, A>, [U]
[A: Allocator] [T], Vec<U, A>
[A: Allocator, const N: usize] Vec<T, A>, [U; N]
[A: Allocator, const N: usize] Vec<T, A>, &[U; N]
}
Not a huge improvement, but I think it makes it a bit nicer to read.
---
Cheers,
Benno
> diff --git a/rust/kernel/prelude.rs b/rust/kernel/prelude.rs
> index d5f2fe42d093..80223cdaa485 100644
> --- a/rust/kernel/prelude.rs
> +++ b/rust/kernel/prelude.rs
> @@ -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
>
^ permalink raw reply [flat|nested] 77+ messages in thread
* Re: [PATCH v7 14/26] rust: alloc: implement `IntoIterator` for `Vec`
2024-09-11 22:52 ` [PATCH v7 14/26] rust: alloc: implement `IntoIterator` for `Vec` Danilo Krummrich
@ 2024-09-26 13:53 ` Benno Lossin
2024-09-28 19:20 ` Gary Guo
1 sibling, 0 replies; 77+ messages in thread
From: Benno Lossin @ 2024-09-26 13:53 UTC (permalink / raw)
To: Danilo Krummrich, ojeda, alex.gaynor, wedsonaf, boqun.feng, gary,
bjorn3_gh, a.hindborg, aliceryhl, akpm
Cc: daniel.almeida, faith.ekstrand, boris.brezillon, lina, mcanal,
zhiw, cjia, jhubbard, airlied, ajanulgu, lyude, linux-kernel,
rust-for-linux, linux-mm
On 12.09.24 00:52, Danilo Krummrich wrote:
> 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.
>
> Reviewed-by: Alice Ryhl <aliceryhl@google.com>
> Signed-off-by: Danilo Krummrich <dakr@kernel.org>
> ---
> rust/kernel/alloc.rs | 1 +
> rust/kernel/alloc/kvec.rs | 181 ++++++++++++++++++++++++++++++++++++++
> 2 files changed, 182 insertions(+)
One comment below, with that fixed:
Reviewed-by: Benno Lossin <benno.lossin@proton.me>
---
Cheers,
Benno
> +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) },
Instead of this `unsafe` call, you can do
let buf = self.ptr;
Before the call to `into_raw_parts`.
> + len,
> + cap,
> + _p: PhantomData::<A>,
> + }
> + }
> +}
> --
> 2.46.0
>
^ permalink raw reply [flat|nested] 77+ messages in thread
* Re: [PATCH v7 15/26] rust: alloc: implement `collect` for `IntoIter`
2024-09-11 22:52 ` [PATCH v7 15/26] rust: alloc: implement `collect` for `IntoIter` Danilo Krummrich
@ 2024-09-26 13:57 ` Benno Lossin
2024-09-28 19:27 ` Gary Guo
1 sibling, 0 replies; 77+ messages in thread
From: Benno Lossin @ 2024-09-26 13:57 UTC (permalink / raw)
To: Danilo Krummrich, ojeda, alex.gaynor, wedsonaf, boqun.feng, gary,
bjorn3_gh, a.hindborg, aliceryhl, akpm
Cc: daniel.almeida, faith.ekstrand, boris.brezillon, lina, mcanal,
zhiw, cjia, jhubbard, airlied, ajanulgu, lyude, linux-kernel,
rust-for-linux, linux-mm
On 12.09.24 00:52, Danilo Krummrich wrote:
> 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.
>
> Reviewed-by: Alice Ryhl <aliceryhl@google.com>
> Signed-off-by: Danilo Krummrich <dakr@kernel.org>
> ---
> rust/kernel/alloc/kvec.rs | 86 +++++++++++++++++++++++++++++++++++++++
> 1 file changed, 86 insertions(+)
One comment below, but feel free to keep it as-is.
Reviewed-by: Benno Lossin <benno.lossin@proton.me>
> + 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 {
> + // Copy the contents we have advanced to at the beginning of the buffer.
> + //
> + // SAFETY:
> + // - `ptr` is valid for reads of `len * size_of::<T>()` bytes,
> + // - `buf.as_ptr()` is valid for writes of `len * size_of::<T>()` bytes,
> + // - `ptr` and `buf.as_ptr()` are not be subject to aliasing restrictions relative to
> + // each other,
> + // - both `ptr` and `buf.ptr()` are properly aligned.
> + 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()
> + }
> + };
Would it make sense to only do the resize if the iterator has advanced?
If it hasn't, doing `into_iter().collect()` would be a no-op, which
would make sense IMO.
---
Cheers,
Benno
> +
> + // 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>
> --
> 2.46.0
>
^ permalink raw reply [flat|nested] 77+ messages in thread
* Re: [PATCH v7 04/26] rust: alloc: implement `Allocator` for `Kmalloc`
2024-09-26 13:24 ` Danilo Krummrich
@ 2024-09-26 14:00 ` Benno Lossin
0 siblings, 0 replies; 77+ messages in thread
From: Benno Lossin @ 2024-09-26 14:00 UTC (permalink / raw)
To: Danilo Krummrich
Cc: ojeda, alex.gaynor, wedsonaf, boqun.feng, gary, bjorn3_gh,
a.hindborg, aliceryhl, akpm, daniel.almeida, faith.ekstrand,
boris.brezillon, lina, mcanal, zhiw, cjia, jhubbard, airlied,
ajanulgu, lyude, linux-kernel, rust-for-linux, linux-mm
On 26.09.24 15:24, Danilo Krummrich wrote:
> On Thu, Sep 26, 2024 at 01:00:58PM +0000, Benno Lossin wrote:
>> On 12.09.24 00:52, Danilo Krummrich wrote:
>>> +/// # 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`].
>>> + ///
>>> + /// # 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.
>>> + unsafe fn call(
>>> + &self,
>>> + ptr: Option<NonNull<u8>>,
>>> + layout: Layout,
>>> + flags: Flags,
>>> + ) -> Result<NonNull<[u8]>, AllocError> {
>>> + let size = aligned_size(layout);
>>> + let ptr = match ptr {
>>> + Some(ptr) => ptr.as_ptr(),
>>> + None => ptr::null(),
>>> + };
>>> +
>>> + // SAFETY:
>>> + // - `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.
>>> + //
>>> + // GUARANTEE:
>>> + // - `self.0` is one of `krealloc`, `vrealloc`, `kvrealloc`.
>>> + // - Those functions provide the guarantees of this function.
>>> + let raw_ptr = unsafe {
>>> + // If `size == 0` and `ptr != NULL` the memory behind the pointer is freed.
>>> + self.0(ptr.cast(), size, flags.0).cast()
>>> + };
>>> +
>>> + let ptr = if size == 0 {
>>> + NonNull::dangling()
>>> + } else {
>>> + NonNull::new(raw_ptr).ok_or(AllocError)?
>>> + };
>>> +
>>> + Ok(NonNull::slice_from_raw_parts(ptr, size))
>>> + }
>>> +}
>>
>> I remember asking you to split this into a different commit. I think you
>> argued that it would be better to keep it in the same commit when
>> bisecting. I don't think that applies in this case, are there any other
>> disadvantages?
>
> I don't really like the intermediate `#[expect(dead_code)]`, plus it's
> additional work you didn't really give me a motivation for, i.e. you did not
> mention what would be the advantage.
The advantage would be that it's easier to review (granted it probably
is a bit late for that). I got confused a couple of times (but that's
probably on me).
> But sure, I will change it for the next version.
Thanks
---
Cheers,
Benno
^ permalink raw reply [flat|nested] 77+ messages in thread
* Re: [PATCH v7 13/26] rust: alloc: implement kernel `Vec` type
2024-09-26 13:47 ` Benno Lossin
@ 2024-09-28 12:43 ` Danilo Krummrich
2024-09-28 13:20 ` Benno Lossin
0 siblings, 1 reply; 77+ messages in thread
From: Danilo Krummrich @ 2024-09-28 12:43 UTC (permalink / raw)
To: Benno Lossin
Cc: ojeda, alex.gaynor, wedsonaf, boqun.feng, gary, bjorn3_gh,
a.hindborg, aliceryhl, akpm, daniel.almeida, faith.ekstrand,
boris.brezillon, lina, mcanal, zhiw, cjia, jhubbard, airlied,
ajanulgu, lyude, linux-kernel, rust-for-linux, linux-mm
On Thu, Sep 26, 2024 at 01:47:04PM +0000, Benno Lossin wrote:
> On 12.09.24 00:52, Danilo Krummrich wrote:
> > + /// Appends an element to the back of the [`Vec`] instance.
> > + ///
> > + /// # Examples
> > + ///
> > + /// ```
> > + /// let mut v = KVec::new();
> > + /// v.push(1, GFP_KERNEL)?;
> > + /// 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)?;
> > +
> > + // SAFETY:
> > + // - `self.len` is smaller than `self.capacity` and hence, the resulting pointer is
> > + // guaranteed to be part of the same allocated object.
> > + // - `self.len` can not overflow `isize`.
> > + let ptr = unsafe { self.as_mut_ptr().add(self.len) };
> > +
> > + // SAFETY:
> > + // - `ptr` is properly aligned and valid for writes.
> > + unsafe { core::ptr::write(ptr, v) };
>
> Why not use `self.spare_capacity_mut()[0].write(v);`?
Before v7 I did exactly that, but in v6 you suggested to use the raw pointer
instead to avoid the bounds check.
>
> If you want to avoid the bounds check, you can do
>
> let first = self.spare_capacity_mut().first();
> // SAFETY: the call to `Vec::reserve` above ensures that `spare_capacity_mut()` is non-empty.
> unsafe { first.unwrap_unchecked() }.write(v);
`first` does a similar check to create the `Option<&T>`, right?. I'd rather keep
the raw pointer access as suggested in v6.
^ permalink raw reply [flat|nested] 77+ messages in thread
* Re: [PATCH v7 13/26] rust: alloc: implement kernel `Vec` type
2024-09-28 12:43 ` Danilo Krummrich
@ 2024-09-28 13:20 ` Benno Lossin
0 siblings, 0 replies; 77+ messages in thread
From: Benno Lossin @ 2024-09-28 13:20 UTC (permalink / raw)
To: Danilo Krummrich
Cc: ojeda, alex.gaynor, wedsonaf, boqun.feng, gary, bjorn3_gh,
a.hindborg, aliceryhl, akpm, daniel.almeida, faith.ekstrand,
boris.brezillon, lina, mcanal, zhiw, cjia, jhubbard, airlied,
ajanulgu, lyude, linux-kernel, rust-for-linux, linux-mm
On 28.09.24 14:43, Danilo Krummrich wrote:
> On Thu, Sep 26, 2024 at 01:47:04PM +0000, Benno Lossin wrote:
>> On 12.09.24 00:52, Danilo Krummrich wrote:
>>> + /// Appends an element to the back of the [`Vec`] instance.
>>> + ///
>>> + /// # Examples
>>> + ///
>>> + /// ```
>>> + /// let mut v = KVec::new();
>>> + /// v.push(1, GFP_KERNEL)?;
>>> + /// 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)?;
>>> +
>>> + // SAFETY:
>>> + // - `self.len` is smaller than `self.capacity` and hence, the resulting pointer is
>>> + // guaranteed to be part of the same allocated object.
>>> + // - `self.len` can not overflow `isize`.
>>> + let ptr = unsafe { self.as_mut_ptr().add(self.len) };
>>> +
>>> + // SAFETY:
>>> + // - `ptr` is properly aligned and valid for writes.
>>> + unsafe { core::ptr::write(ptr, v) };
>>
>> Why not use `self.spare_capacity_mut()[0].write(v);`?
>
> Before v7 I did exactly that, but in v6 you suggested to use the raw pointer
> instead to avoid the bounds check.
Ah I see... Would be pretty useful for me to have my previous comments
easily accessible, I don't usually look at the previous thread. Is
anyone aware of some tools for that?
>> If you want to avoid the bounds check, you can do
>>
>> let first = self.spare_capacity_mut().first();
>> // SAFETY: the call to `Vec::reserve` above ensures that `spare_capacity_mut()` is non-empty.
>> unsafe { first.unwrap_unchecked() }.write(v);
>
> `first` does a similar check to create the `Option<&T>`, right?. I'd rather keep
> the raw pointer access as suggested in v6.
It does a check, but the optimizer will get rid of it if you use
`unwrap_unchecked` [1]. But feel free to leave it as-is.
[1]: https://godbolt.org/z/zYbMTo86M
---
Cheers,
Benno
^ permalink raw reply [flat|nested] 77+ messages in thread
* Re: [PATCH v7 09/26] rust: alloc: implement kernel `Box`
2024-09-11 22:52 ` [PATCH v7 09/26] rust: alloc: implement kernel `Box` Danilo Krummrich
2024-09-26 13:23 ` Benno Lossin
@ 2024-09-28 18:54 ` Gary Guo
1 sibling, 0 replies; 77+ messages in thread
From: Gary Guo @ 2024-09-28 18:54 UTC (permalink / raw)
To: Danilo Krummrich
Cc: ojeda, alex.gaynor, wedsonaf, boqun.feng, bjorn3_gh, benno.lossin,
a.hindborg, aliceryhl, akpm, daniel.almeida, faith.ekstrand,
boris.brezillon, lina, mcanal, zhiw, cjia, jhubbard, airlied,
ajanulgu, lyude, linux-kernel, rust-for-linux, linux-mm
On Thu, 12 Sep 2024 00:52:45 +0200
Danilo Krummrich <dakr@kernel.org> wrote:
> `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.
>
> Signed-off-by: Danilo Krummrich <dakr@kernel.org>
> ---
> rust/kernel/alloc.rs | 6 +
> rust/kernel/alloc/kbox.rs | 465 ++++++++++++++++++++++++++++++++++++++
> rust/kernel/prelude.rs | 2 +-
> 3 files changed, 472 insertions(+), 1 deletion(-)
> create mode 100644 rust/kernel/alloc/kbox.rs
>
> diff --git a/rust/kernel/alloc.rs b/rust/kernel/alloc.rs
> index 7a405d6f3034..b1f28334b27a 100644
> --- a/rust/kernel/alloc.rs
> +++ b/rust/kernel/alloc.rs
> @@ -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;
> diff --git a/rust/kernel/alloc/kbox.rs b/rust/kernel/alloc/kbox.rs
> new file mode 100644
> index 000000000000..6188494f040d
> --- /dev/null
> +++ b/rust/kernel/alloc/kbox.rs
> @@ -0,0 +1,465 @@
> +// 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;
> +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`.
> +///
> +/// 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 fallible.
> +///
> +/// `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;
> +/// struct Huge([u8; SIZE]);
> +///
> +/// assert!(KBox::<Huge>::new_uninit(GFP_KERNEL | __GFP_NOWARN).is_err());
> +/// ```
> +///
> +/// ```
> +/// # use kernel::bindings;
> +/// const SIZE: usize = bindings::KMALLOC_MAX_SIZE as usize + 1;
> +/// struct Huge([u8; SIZE]);
> +///
> +/// assert!(KVBox::<Huge>::new_uninit(GFP_KERNEL).is_ok());
> +/// ```
> +///
> +/// # Invariants
> +///
> +/// `self.0` is always properly aligned and either points to memory allocated with `A` or, for
> +/// zero-sized types, is a dangling, well aligned pointer.
> +#[repr(transparent)]
> +pub struct Box<T: ?Sized, A: Allocator>(NonNull<T>, PhantomData<A>);
> +
> +/// Type alias for [`Box`] with a [`Kmalloc`] allocator.
> +///
> +/// # Examples
> +///
> +/// ```
> +/// let b = KBox::new(24_u64, GFP_KERNEL)?;
> +///
> +/// assert_eq!(*b, 24_u64);
> +/// # Ok::<(), Error>(())
> +/// ```
> +pub type KBox<T> = Box<T, super::allocator::Kmalloc>;
> +
> +/// Type alias for [`Box`] with a [`Vmalloc`] allocator.
> +///
> +/// # Examples
> +///
> +/// ```
> +/// let b = VBox::new(24_u64, GFP_KERNEL)?;
> +///
> +/// assert_eq!(*b, 24_u64);
> +/// # Ok::<(), Error>(())
> +/// ```
> +pub type VBox<T> = Box<T, super::allocator::Vmalloc>;
> +
> +/// Type alias for [`Box`] with a [`KVmalloc`] allocator.
> +///
> +/// # Examples
> +///
> +/// ```
> +/// let b = KVBox::new(24_u64, GFP_KERNEL)?;
> +///
> +/// assert_eq!(*b, 24_u64);
> +/// # Ok::<(), Error>(())
> +/// ```
> +pub type KVBox<T> = Box<T, super::allocator::KVmalloc>;
> +
> +// 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.
> + ///
> + /// # 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`.
> + ///
> + /// For ZSTs, `raw` must be a dangling, well aligned pointer.
> + #[inline]
> + pub const unsafe fn from_raw(raw: *mut T) -> Self {
> + // INVARIANT: Validity of `raw` is guaranteed by the safety preconditions of this function.
> + // SAFETY: By the safety preconditions of this function, `raw` is not a NULL pointer.
> + Self(unsafe { NonNull::new_unchecked(raw) }, PhantomData::<A>)
nit: the turbofish here is not necessary.
Self(unsafe { NonNull::new_unchecked(raw) }, PhantomData)
> + }
> +
> + /// 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.
> + ///
> + /// # 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> {
I think this can take `self` and shadow the underlying
`MaybeUninit::assume_init` -- the rationale being that the
`MaybeUninit::assume_init` takes `self` so it cannot actually be used
for `Box<MaybeUninit<T>>`. Rust standard library also takes the
approach (with the unstable box assume_init takes `self`).
Note the argument of `Box` not having `self` methods don't apply here,
as it doesn't deref to an arbitrary type, but rather always
`MaybeUninit`.
> + 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.cast()) }
> + }
> +
> + /// 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 `b`'s value.
> + unsafe { Self::assume_init(b) }
> + }
same here.
> +}
> +
> +impl<T, A> Box<T, A>
> +where
> + A: Allocator,
> +{
> + fn is_zst() -> bool {
> + core::mem::size_of::<T>() == 0
> + }
> +
> + /// Creates a new `Box<T, A>` and initializes its contents with `x`.
> + ///
> + /// New memory is allocated with `A`. The allocation may fail, in which case an error is
> + /// returned. For ZSTs no memory is allocated.
> + pub fn new(x: T, flags: Flags) -> Result<Self, AllocError> {
> + let b = Self::new_uninit(flags)?;
> + Ok(Box::write(b, x))
> + }
^ permalink raw reply [flat|nested] 77+ messages in thread
* Re: [PATCH v7 08/26] rust: alloc: add __GFP_NOWARN to `Flags`
2024-09-11 22:52 ` [PATCH v7 08/26] rust: alloc: add __GFP_NOWARN to `Flags` Danilo Krummrich
@ 2024-09-28 18:55 ` Gary Guo
0 siblings, 0 replies; 77+ messages in thread
From: Gary Guo @ 2024-09-28 18:55 UTC (permalink / raw)
To: Danilo Krummrich
Cc: ojeda, alex.gaynor, wedsonaf, boqun.feng, bjorn3_gh, benno.lossin,
a.hindborg, aliceryhl, akpm, daniel.almeida, faith.ekstrand,
boris.brezillon, lina, mcanal, zhiw, cjia, jhubbard, airlied,
ajanulgu, lyude, linux-kernel, rust-for-linux, linux-mm
On Thu, 12 Sep 2024 00:52:44 +0200
Danilo Krummrich <dakr@kernel.org> wrote:
> Some test cases in subsequent patches provoke allocation failures. Add
> `__GFP_NOWARN` to enable test cases to silence unpleasant warnings.
>
> Reviewed-by: Alice Ryhl <aliceryhl@google.com>
> Reviewed-by: Benno Lossin <benno.lossin@proton.me>
> Signed-off-by: Danilo Krummrich <dakr@kernel.org>
Reviewed-by: Gary Guo <gary@garyguo.net>
> ---
> rust/bindings/bindings_helper.h | 1 +
> rust/kernel/alloc.rs | 5 +++++
> 2 files changed, 6 insertions(+)
>
> diff --git a/rust/bindings/bindings_helper.h b/rust/bindings/bindings_helper.h
> index ae82e9c941af..a80783fcbe04 100644
> --- a/rust/bindings/bindings_helper.h
> +++ b/rust/bindings/bindings_helper.h
> @@ -31,4 +31,5 @@ const gfp_t RUST_CONST_HELPER_GFP_KERNEL_ACCOUNT = GFP_KERNEL_ACCOUNT;
> const gfp_t RUST_CONST_HELPER_GFP_NOWAIT = GFP_NOWAIT;
> const gfp_t RUST_CONST_HELPER___GFP_ZERO = __GFP_ZERO;
> const gfp_t RUST_CONST_HELPER___GFP_HIGHMEM = ___GFP_HIGHMEM;
> +const gfp_t RUST_CONST_HELPER___GFP_NOWARN = ___GFP_NOWARN;
> const blk_features_t RUST_CONST_HELPER_BLK_FEAT_ROTATIONAL = BLK_FEAT_ROTATIONAL;
> diff --git a/rust/kernel/alloc.rs b/rust/kernel/alloc.rs
> index da025faf07f8..7a405d6f3034 100644
> --- a/rust/kernel/alloc.rs
> +++ b/rust/kernel/alloc.rs
> @@ -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.
^ permalink raw reply [flat|nested] 77+ messages in thread
* Re: [PATCH v7 10/26] rust: treewide: switch to our kernel `Box` type
2024-09-11 22:52 ` [PATCH v7 10/26] rust: treewide: switch to our kernel `Box` type Danilo Krummrich
@ 2024-09-28 18:59 ` Gary Guo
2024-09-29 14:52 ` Danilo Krummrich
0 siblings, 1 reply; 77+ messages in thread
From: Gary Guo @ 2024-09-28 18:59 UTC (permalink / raw)
To: Danilo Krummrich
Cc: ojeda, alex.gaynor, wedsonaf, boqun.feng, bjorn3_gh, benno.lossin,
a.hindborg, aliceryhl, akpm, daniel.almeida, faith.ekstrand,
boris.brezillon, lina, mcanal, zhiw, cjia, jhubbard, airlied,
ajanulgu, lyude, linux-kernel, rust-for-linux, linux-mm
On Thu, 12 Sep 2024 00:52:46 +0200
Danilo Krummrich <dakr@kernel.org> wrote:
> Now that we got the kernel `Box` type in place, convert all existing
> `Box` users to make use of it.
>
> Reviewed-by: Alice Ryhl <aliceryhl@google.com>
> Reviewed-by: Benno Lossin <benno.lossin@proton.me>
> Signed-off-by: Danilo Krummrich <dakr@kernel.org>
Reviewed-by: Gary Guo <gary@garyguo.net>
Have you considered defaulting the `A` in `Box` to `Kmalloc`? This
would avoid doing a lot of tree-wide changes.
If you have a reason against it (I'm pretty sure you have), then
probably you should put it in the commit message.
Best,
Gary
> ---
> drivers/block/rnull.rs | 4 +--
> rust/kernel/init.rs | 51 ++++++++++++++++---------------
> rust/kernel/init/__internal.rs | 2 +-
> rust/kernel/rbtree.rs | 49 ++++++++++++++++-------------
> 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, 81 insertions(+), 76 deletions(-)
^ permalink raw reply [flat|nested] 77+ messages in thread
* Re: [PATCH v7 12/26] rust: alloc: add `Box` to prelude
2024-09-11 22:52 ` [PATCH v7 12/26] rust: alloc: add `Box` to prelude Danilo Krummrich
@ 2024-09-28 19:00 ` Gary Guo
0 siblings, 0 replies; 77+ messages in thread
From: Gary Guo @ 2024-09-28 19:00 UTC (permalink / raw)
To: Danilo Krummrich
Cc: ojeda, alex.gaynor, wedsonaf, boqun.feng, bjorn3_gh, benno.lossin,
a.hindborg, aliceryhl, akpm, daniel.almeida, faith.ekstrand,
boris.brezillon, lina, mcanal, zhiw, cjia, jhubbard, airlied,
ajanulgu, lyude, linux-kernel, rust-for-linux, linux-mm
On Thu, 12 Sep 2024 00:52:48 +0200
Danilo Krummrich <dakr@kernel.org> wrote:
> Now that we removed `BoxExt` and the corresponding includes in
> prelude.rs, add the new kernel `Box` type instead.
>
> Reviewed-by: Alice Ryhl <aliceryhl@google.com>
> Reviewed-by: Benno Lossin <benno.lossin@proton.me>
> Signed-off-by: Danilo Krummrich <dakr@kernel.org>
Reviewed-by: Gary Guo <gary@garyguo.net>
> ---
> rust/kernel/prelude.rs | 2 +-
> 1 file changed, 1 insertion(+), 1 deletion(-)
>
> diff --git a/rust/kernel/prelude.rs b/rust/kernel/prelude.rs
> index c1f8e5c832e2..d5f2fe42d093 100644
> --- a/rust/kernel/prelude.rs
> +++ b/rust/kernel/prelude.rs
> @@ -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;
^ permalink raw reply [flat|nested] 77+ messages in thread
* Re: [PATCH v7 11/26] rust: alloc: remove extension of std's `Box`
2024-09-11 22:52 ` [PATCH v7 11/26] rust: alloc: remove extension of std's `Box` Danilo Krummrich
@ 2024-09-28 19:00 ` Gary Guo
0 siblings, 0 replies; 77+ messages in thread
From: Gary Guo @ 2024-09-28 19:00 UTC (permalink / raw)
To: Danilo Krummrich
Cc: ojeda, alex.gaynor, wedsonaf, boqun.feng, bjorn3_gh, benno.lossin,
a.hindborg, aliceryhl, akpm, daniel.almeida, faith.ekstrand,
boris.brezillon, lina, mcanal, zhiw, cjia, jhubbard, airlied,
ajanulgu, lyude, linux-kernel, rust-for-linux, linux-mm
On Thu, 12 Sep 2024 00:52:47 +0200
Danilo Krummrich <dakr@kernel.org> wrote:
> Now that all existing `Box` users were moved to the kernel `Box` type,
> remove the `BoxExt` extension and all other related extensions.
>
> Reviewed-by: Alice Ryhl <aliceryhl@google.com>
> Reviewed-by: Benno Lossin <benno.lossin@proton.me>
> Signed-off-by: Danilo Krummrich <dakr@kernel.org>
Reviewed-by: Gary Guo <gary@garyguo.net>
> ---
> rust/kernel/alloc.rs | 1 -
> rust/kernel/alloc/box_ext.rs | 87 ------------------------------------
> rust/kernel/init.rs | 46 +------------------
> rust/kernel/lib.rs | 1 -
> rust/kernel/prelude.rs | 4 +-
> rust/kernel/types.rs | 50 ---------------------
> 6 files changed, 3 insertions(+), 186 deletions(-)
> delete mode 100644 rust/kernel/alloc/box_ext.rs
^ permalink raw reply [flat|nested] 77+ messages in thread
* Re: [PATCH v7 13/26] rust: alloc: implement kernel `Vec` type
2024-09-11 22:52 ` [PATCH v7 13/26] rust: alloc: implement kernel `Vec` type Danilo Krummrich
2024-09-26 13:47 ` Benno Lossin
@ 2024-09-28 19:14 ` Gary Guo
1 sibling, 0 replies; 77+ messages in thread
From: Gary Guo @ 2024-09-28 19:14 UTC (permalink / raw)
To: Danilo Krummrich
Cc: ojeda, alex.gaynor, wedsonaf, boqun.feng, bjorn3_gh, benno.lossin,
a.hindborg, aliceryhl, akpm, daniel.almeida, faith.ekstrand,
boris.brezillon, lina, mcanal, zhiw, cjia, jhubbard, airlied,
ajanulgu, lyude, linux-kernel, rust-for-linux, linux-mm
On Thu, 12 Sep 2024 00:52:49 +0200
Danilo Krummrich <dakr@kernel.org> wrote:
> `Vec` provides a contiguous growable array type with contents allocated
> with the kernel's allocators (e.g. `Kmalloc`, `Vmalloc` or `KVmalloc`).
>
> In contrast to Rust's stdlib `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.
>
> Signed-off-by: Danilo Krummrich <dakr@kernel.org>
> ---
> rust/kernel/alloc.rs | 6 +
> rust/kernel/alloc/kvec.rs | 638 ++++++++++++++++++++++++++++++++++++++
> rust/kernel/prelude.rs | 2 +-
> 3 files changed, 645 insertions(+), 1 deletion(-)
> create mode 100644 rust/kernel/alloc/kvec.rs
>
> diff --git a/rust/kernel/alloc.rs b/rust/kernel/alloc.rs
> index 1d0cb6f12af9..4fb983b63d46 100644
> --- a/rust/kernel/alloc.rs
> +++ b/rust/kernel/alloc.rs
> @@ -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;
> diff --git a/rust/kernel/alloc/kvec.rs b/rust/kernel/alloc/kvec.rs
> new file mode 100644
> index 000000000000..631a44e19f35
> --- /dev/null
> +++ b/rust/kernel/alloc/kvec.rs
> @@ -0,0 +1,638 @@
> +// SPDX-License-Identifier: GPL-2.0
> +
> +//! Implementation of [`Vec`].
> +
> +use super::{
> + allocator::{KVmalloc, Kmalloc, Vmalloc},
> + 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)?;
> +/// assert_eq!(v, [1, 2, 3, 4]);
> +///
> +/// # Ok::<(), Error>(())
> +/// ```
> +#[macro_export]
> +macro_rules! kvec {
> + () => (
> + $crate::alloc::KVec::new()
> + );
> + ($elem:expr; $n:expr) => (
> + $crate::alloc::KVec::from_elem($elem, $n, GFP_KERNEL)
> + );
> + ($($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),
> + }
> + );
> +}
> +
> +/// 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>`.
> +///
> +/// 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.
> +///
> +/// 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
> +///
> +/// - `self.ptr` is always properly aligned and either points to memory allocated with `A` or, for
> +/// zero-sized types, is a dangling, well aligned pointer.
> +///
> +/// - `self.len` always represents the exact number of elements stored in the vector.
> +///
> +/// - `self.cap` represents the absolute number of elements that can be stored 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).
> +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>,
> +}
> +
> +/// Type alias for [`Vec`] with a [`Kmalloc`] allocator.
> +///
> +/// # Examples
> +///
> +/// ```
> +/// let mut v = KVec::new();
> +/// v.push(1, GFP_KERNEL)?;
> +/// assert_eq!(&v, &[1]);
> +///
> +/// # Ok::<(), Error>(())
> +/// ```
> +pub type KVec<T> = Vec<T, 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, 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, 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() {
Better to ensure everything ZST related is const to avoid putting load
on optimizer.
The Rust standard library defines a trait `SizedTypeProperties` and an
associative const, but you can also just do:
if const { Self::is_zst() } {
(and change is_zst to const).
You might need to add `feature(inline_const)` since it's stable in 1.79
and we have MSRV of 1.76 (the trait method doesn't need new feature gate).
> + usize::MAX
> + } else {
> + self.cap
> + }
> + }
> +
> + /// Returns the number of elements stored within the vector.
> + #[inline]
> + pub fn len(&self) -> usize {
> + self.len
> + }
> +
> + /// 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) {
> + debug_assert!(new_len <= self.capacity());
> + self.len = new_len;
> + }
> +
> + /// Returns a slice of the entire vector.
> + #[inline]
> + pub fn as_slice(&self) -> &[T] {
> + self
> + }
> +
> + /// Returns a mutable slice of the entire vector.
> + #[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(&mut 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.ptr.as_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,
> + len: 0,
> + _p: PhantomData::<A>,
> + }
> + }
> +
> + /// Returns a slice of `MaybeUninit<T>` for the remaining spare capacity of the vector.
> + pub fn spare_capacity_mut(&mut self) -> &mut [MaybeUninit<T>] {
> + // SAFETY:
> + // - `self.len` is smaller than `self.capacity` and hence, the resulting pointer is
> + // guaranteed to be part of the same allocated object.
> + // - `self.len` can not overflow `isize`.
> + let ptr = unsafe { self.as_mut_ptr().add(self.len) } as *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(ptr, self.capacity() - self.len) }
> + }
> +
> + /// Appends an element to the back of the [`Vec`] instance.
> + ///
> + /// # Examples
> + ///
> + /// ```
> + /// let mut v = KVec::new();
> + /// v.push(1, GFP_KERNEL)?;
> + /// 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)?;
Not sure why this isn't `self.reserve(1, flags)?`?
> +
> + // SAFETY:
> + // - `self.len` is smaller than `self.capacity` and hence, the resulting pointer is
> + // guaranteed to be part of the same allocated object.
> + // - `self.len` can not overflow `isize`.
> + let ptr = unsafe { self.as_mut_ptr().add(self.len) };
> +
> + // SAFETY:
> + // - `ptr` is properly aligned and valid for writes.
> + unsafe { core::ptr::write(ptr, v) };
> +
> + // 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)?;
same here.
> +
> + 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
> + T: Clone,
> + {
> + self.reserve(other.len(), flags)?;
> + for (slot, item) in core::iter::zip(self.spare_capacity_mut(), other) {
> + slot.write(item.clone());
> + }
> +
> + // SAFETY:
> + // - `other.len()` spare entries have just been initialized, so it is safe to increase
> + // the length by the same number.
> + // - `self.len() + other.len() <= self.capacity()` is guaranteed by the preceding `reserve`
> + // call.
> + 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`.
> + ///
> + /// # 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;
> + ///
> + /// // 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>(())
> + /// ```
> + ///
> + /// # Safety
> + ///
> + /// If `T` is a ZST:
> + ///
> + /// - `ptr` must be a dangling, well aligned pointer.
> + ///
> + /// 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>() * capacity`.
> + /// bytes.
> + /// - The allocated size in bytes must not be larger than `isize::MAX`.
> + /// - `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`.
> + 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 mut 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>(())
> + /// ```
> + 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 ZSTs, we can't go higher.
> + return Err(AllocError);
> + }
> +
> + // We know `cap` is <= `isize::MAX` because of the type invariants of `Self`. So the
> + // multiplication by 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();
> +
> + // 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`.
> + 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();
> +
> + 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.len() + n < self.capacity()` due to the call to reserve above,
> + // - the loop and the line above initialized the next `n` elements.
> + 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)
> + }
> +}
> +
> +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(
> + self.as_mut_ptr(),
> + self.len,
> + ))
> + };
Comment: this can be `core::ptr::drop_in_place(&mut *self)`. The
standard library plays safe with raw pointers because it has
`#[may_dangle]` on T. However I think we should keep the code as is.
> +
> + // 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()) };
> + }
> + }
> +}
> +
> +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);
> +
> + // SAFETY:
> + // - `b` has been allocated with `A`,
> + // - `ptr` fulfills the alignment requirements for `T`,
> + // - `ptr` points to memory with at least a size of `size_of::<T>() * len`,
> + // - all elements within `b` are initialized values of `T`,
> + // - `len` does not exceed `isize::MAX`.
> + unsafe { Vec::from_raw_parts(ptr as _, len, len) }
> + }
> +}
> +
> +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>
> +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 {
> + ([$($vars:tt)*] $lhs:ty, $rhs:ty) => {
> + impl<T, U, $($vars)*> PartialEq<$rhs> for $lhs
> + where
> + T: PartialEq<U>,
> + {
> + #[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] }
> diff --git a/rust/kernel/prelude.rs b/rust/kernel/prelude.rs
> index d5f2fe42d093..80223cdaa485 100644
> --- a/rust/kernel/prelude.rs
> +++ b/rust/kernel/prelude.rs
> @@ -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;
^ permalink raw reply [flat|nested] 77+ messages in thread
* Re: [PATCH v7 14/26] rust: alloc: implement `IntoIterator` for `Vec`
2024-09-11 22:52 ` [PATCH v7 14/26] rust: alloc: implement `IntoIterator` for `Vec` Danilo Krummrich
2024-09-26 13:53 ` Benno Lossin
@ 2024-09-28 19:20 ` Gary Guo
1 sibling, 0 replies; 77+ messages in thread
From: Gary Guo @ 2024-09-28 19:20 UTC (permalink / raw)
To: Danilo Krummrich
Cc: ojeda, alex.gaynor, wedsonaf, boqun.feng, bjorn3_gh, benno.lossin,
a.hindborg, aliceryhl, akpm, daniel.almeida, faith.ekstrand,
boris.brezillon, lina, mcanal, zhiw, cjia, jhubbard, airlied,
ajanulgu, lyude, linux-kernel, rust-for-linux, linux-mm
On Thu, 12 Sep 2024 00:52:50 +0200
Danilo Krummrich <dakr@kernel.org> wrote:
> 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.
>
> Reviewed-by: Alice Ryhl <aliceryhl@google.com>
> Signed-off-by: Danilo Krummrich <dakr@kernel.org>
A small nit below, with it fixed:
Reviewed-by: Gary Guo <gary@garyguo.net>
> ---
> rust/kernel/alloc.rs | 1 +
> rust/kernel/alloc/kvec.rs | 181 ++++++++++++++++++++++++++++++++++++++
> 2 files changed, 182 insertions(+)
>
> diff --git a/rust/kernel/alloc.rs b/rust/kernel/alloc.rs
> index 4fb983b63d46..e8fbae2adadb 100644
> --- a/rust/kernel/alloc.rs
> +++ b/rust/kernel/alloc.rs
> @@ -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;
> diff --git a/rust/kernel/alloc/kvec.rs b/rust/kernel/alloc/kvec.rs
> index 631a44e19f35..e91761c5c52d 100644
> --- a/rust/kernel/alloc/kvec.rs
> +++ b/rust/kernel/alloc/kvec.rs
> @@ -14,6 +14,7 @@
> ops::DerefMut,
> ops::Index,
> ops::IndexMut,
> + ptr,
> ptr::NonNull,
> slice,
> slice::SliceIndex,
> @@ -636,3 +637,183 @@ 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`] 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));
> + /// assert_eq!(it.next(), None);
> + ///
> + /// # Ok::<(), Error>(())
> + /// ```
> + fn next(&mut self) -> Option<T> {
> + if self.len == 0 {
> + return None;
> + }
> +
> + let current = self.ptr;
> +
> + // SAFETY: We can't overflow; decreasing `self.len` by one every time we advance `self.ptr`
> + // by one guarantees that.
> + unsafe { self.ptr = self.ptr.add(1) };
> +
> + self.len -= 1;
> +
> + // SAFETY: `current` is guaranteed to point at a valid element within the buffer.
> + Some(unsafe { current.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: The pointer in `self.0` is guaranteed to be valid by the type invariant.
> + unsafe { ptr::drop_in_place(self.as_raw_mut_slice()) };
`as_raw_mut_slice` is only used once? It's better to construct the raw
pointer directly so there's no need to refer to some other place when
reviewing unsafe code.
> +
> + // 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>,
> + }
> + }
> +}
^ permalink raw reply [flat|nested] 77+ messages in thread
* Re: [PATCH v7 15/26] rust: alloc: implement `collect` for `IntoIter`
2024-09-11 22:52 ` [PATCH v7 15/26] rust: alloc: implement `collect` for `IntoIter` Danilo Krummrich
2024-09-26 13:57 ` Benno Lossin
@ 2024-09-28 19:27 ` Gary Guo
2024-09-29 15:12 ` Danilo Krummrich
1 sibling, 1 reply; 77+ messages in thread
From: Gary Guo @ 2024-09-28 19:27 UTC (permalink / raw)
To: Danilo Krummrich
Cc: ojeda, alex.gaynor, wedsonaf, boqun.feng, bjorn3_gh, benno.lossin,
a.hindborg, aliceryhl, akpm, daniel.almeida, faith.ekstrand,
boris.brezillon, lina, mcanal, zhiw, cjia, jhubbard, airlied,
ajanulgu, lyude, linux-kernel, rust-for-linux, linux-mm
On Thu, 12 Sep 2024 00:52:51 +0200
Danilo Krummrich <dakr@kernel.org> wrote:
> 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.
>
> Reviewed-by: Alice Ryhl <aliceryhl@google.com>
> Signed-off-by: Danilo Krummrich <dakr@kernel.org>
A question is how useful is this? The way this can be used seems
fairly limited: you `into_iter`, consume a few elements, and the
`collect()`?
It feels whatever user this serves, it would make more sense for them
to use a `VecDeque` and just `pop_front()`.
Also, inline comments below.
> ---
> rust/kernel/alloc/kvec.rs | 86 +++++++++++++++++++++++++++++++++++++++
> 1 file changed, 86 insertions(+)
>
> diff --git a/rust/kernel/alloc/kvec.rs b/rust/kernel/alloc/kvec.rs
> index e91761c5c52d..686e969463f8 100644
> --- a/rust/kernel/alloc/kvec.rs
> +++ b/rust/kernel/alloc/kvec.rs
> @@ -690,6 +690,92 @@ 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`.
> + ///
> + /// # 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>(())
> + /// ```
> + /// # Implementation Details
> + ///
> + /// 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.
> + 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 {
> + // Copy the contents we have advanced to at the beginning of the buffer.
> + //
> + // SAFETY:
> + // - `ptr` is valid for reads of `len * size_of::<T>()` bytes,
> + // - `buf.as_ptr()` is valid for writes of `len * size_of::<T>()` bytes,
> + // - `ptr` and `buf.as_ptr()` are not be subject to aliasing restrictions relative to
> + // each other,
> + // - both `ptr` and `buf.ptr()` are properly aligned.
> + 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();
nit: could be `unwrap_unchecked()`. Although feel free to leave it to
avoid an additional unsafe.
> +
> + // 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()
> + }
> + };
This should be moved to `Vec::shrink_to_fit`. And then this function
can just `Vec::from_raw_parts(...)` and then `vec.shrink_to_fit`.
But my question would be why this function needs to shrink in the first
place.
> +
> + // 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>
^ permalink raw reply [flat|nested] 77+ messages in thread
* Re: [PATCH v7 16/26] rust: treewide: switch to the kernel `Vec` type
2024-09-11 22:52 ` [PATCH v7 16/26] rust: treewide: switch to the kernel `Vec` type Danilo Krummrich
@ 2024-09-28 19:28 ` Gary Guo
0 siblings, 0 replies; 77+ messages in thread
From: Gary Guo @ 2024-09-28 19:28 UTC (permalink / raw)
To: Danilo Krummrich
Cc: ojeda, alex.gaynor, wedsonaf, boqun.feng, bjorn3_gh, benno.lossin,
a.hindborg, aliceryhl, akpm, daniel.almeida, faith.ekstrand,
boris.brezillon, lina, mcanal, zhiw, cjia, jhubbard, airlied,
ajanulgu, lyude, linux-kernel, rust-for-linux, linux-mm
On Thu, 12 Sep 2024 00:52:52 +0200
Danilo Krummrich <dakr@kernel.org> wrote:
> Now that we got the kernel `Vec` in place, convert all existing `Vec`
> users to make use of it.
>
> Reviewed-by: Alice Ryhl <aliceryhl@google.com>
> Reviewed-by: Benno Lossin <benno.lossin@proton.me>
> Signed-off-by: Danilo Krummrich <dakr@kernel.org>
Similar to `KBox`, please add in the commit message about why `Vec`
doesn't have a default allocator type.
Reviewed-by: Gary Guo <gary@garyguo.net>
> ---
> 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(-)
>
> diff --git a/rust/kernel/str.rs b/rust/kernel/str.rs
> index bb8d4f41475b..209ad1bb1a49 100644
> --- a/rust/kernel/str.rs
> +++ b/rust/kernel/str.rs
> @@ -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.
> diff --git a/rust/kernel/sync/locked_by.rs b/rust/kernel/sync/locked_by.rs
> index babc731bd5f6..b94517231fcc 100644
> --- a/rust/kernel/sync/locked_by.rs
> +++ b/rust/kernel/sync/locked_by.rs
> @@ -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 {
> diff --git a/rust/kernel/types.rs b/rust/kernel/types.rs
> index 53d3ddc0b98c..bf1a96d5e1f8 100644
> --- a/rust/kernel/types.rs
> +++ b/rust/kernel/types.rs
> @@ -135,7 +135,7 @@ unsafe fn from_foreign(_: *const core::ffi::c_void) -> Self {}
> /// # use kernel::types::ScopeGuard;
> /// fn example3(arg: bool) -> Result {
> /// let mut vec =
> -/// ScopeGuard::new_with_data(Vec::new(), |v| pr_info!("vec had {} elements\n", v.len()));
> +/// ScopeGuard::new_with_data(KVec::new(), |v| pr_info!("vec had {} elements\n", v.len()));
> ///
> /// vec.push(10u8, GFP_KERNEL)?;
> /// if arg {
> diff --git a/rust/kernel/uaccess.rs b/rust/kernel/uaccess.rs
> index e9347cff99ab..bc011061de45 100644
> --- a/rust/kernel/uaccess.rs
> +++ b/rust/kernel/uaccess.rs
> @@ -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.
> diff --git a/rust/macros/lib.rs b/rust/macros/lib.rs
> index ab93111a048c..8d4ac914b48b 100644
> --- a/rust/macros/lib.rs
> +++ b/rust/macros/lib.rs
> @@ -242,7 +242,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]>,
> /// }
> /// ```
> @@ -251,7 +251,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,
> /// }
> @@ -281,7 +281,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,
> /// }
> diff --git a/samples/rust/rust_minimal.rs b/samples/rust/rust_minimal.rs
> index 2a9eaab62d1c..4aaf117bf8e3 100644
> --- a/samples/rust/rust_minimal.rs
> +++ b/samples/rust/rust_minimal.rs
> @@ -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)?;
^ permalink raw reply [flat|nested] 77+ messages in thread
* Re: [PATCH v7 17/26] rust: alloc: remove `VecExt` extension
2024-09-11 22:52 ` [PATCH v7 17/26] rust: alloc: remove `VecExt` extension Danilo Krummrich
@ 2024-09-28 19:29 ` Gary Guo
0 siblings, 0 replies; 77+ messages in thread
From: Gary Guo @ 2024-09-28 19:29 UTC (permalink / raw)
To: Danilo Krummrich
Cc: ojeda, alex.gaynor, wedsonaf, boqun.feng, bjorn3_gh, benno.lossin,
a.hindborg, aliceryhl, akpm, daniel.almeida, faith.ekstrand,
boris.brezillon, lina, mcanal, zhiw, cjia, jhubbard, airlied,
ajanulgu, lyude, linux-kernel, rust-for-linux, linux-mm
On Thu, 12 Sep 2024 00:52:53 +0200
Danilo Krummrich <dakr@kernel.org> wrote:
> Now that all existing `Vec` users were moved to the kernel `Vec` type,
> remove the `VecExt` extension.
>
> Reviewed-by: Alice Ryhl <aliceryhl@google.com>
> Reviewed-by: Benno Lossin <benno.lossin@proton.me>
> Signed-off-by: Danilo Krummrich <dakr@kernel.org>
Reviewed-by: Gary Guo <gary@garyguo.net>
> ---
> 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
>
> diff --git a/rust/kernel/alloc.rs b/rust/kernel/alloc.rs
> index e8fbae2adadb..aabdf80e4f7b 100644
> --- a/rust/kernel/alloc.rs
> +++ b/rust/kernel/alloc.rs
> @@ -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;
> diff --git a/rust/kernel/alloc/vec_ext.rs b/rust/kernel/alloc/vec_ext.rs
> deleted file mode 100644
> index 1297a4be32e8..000000000000
> --- a/rust/kernel/alloc/vec_ext.rs
> +++ /dev/null
> @@ -1,185 +0,0 @@
> -// SPDX-License-Identifier: GPL-2.0
> -
> -//! Extensions to [`Vec`] for fallible allocations.
> -
> -use super::{AllocError, Flags};
> -use alloc::vec::Vec;
> -
> -/// Extensions to [`Vec`].
> -pub trait VecExt<T>: Sized {
> - /// Creates a new [`Vec`] instance with at least the given capacity.
> - ///
> - /// # Examples
> - ///
> - /// ```
> - /// let v = Vec::<u32>::with_capacity(20, GFP_KERNEL)?;
> - ///
> - /// assert!(v.capacity() >= 20);
> - /// # Ok::<(), Error>(())
> - /// ```
> - fn with_capacity(capacity: usize, flags: Flags) -> Result<Self, AllocError>;
> -
> - /// Appends an element to the back of the [`Vec`] instance.
> - ///
> - /// # Examples
> - ///
> - /// ```
> - /// let mut v = Vec::new();
> - /// v.push(1, GFP_KERNEL)?;
> - /// assert_eq!(&v, &[1]);
> - ///
> - /// v.push(2, GFP_KERNEL)?;
> - /// assert_eq!(&v, &[1, 2]);
> - /// # Ok::<(), Error>(())
> - /// ```
> - fn push(&mut self, v: T, flags: Flags) -> Result<(), AllocError>;
> -
> - /// Pushes clones of the elements of slice into the [`Vec`] instance.
> - ///
> - /// # Examples
> - ///
> - /// ```
> - /// let mut v = Vec::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>(())
> - /// ```
> - fn extend_from_slice(&mut self, other: &[T], flags: Flags) -> Result<(), AllocError>
> - where
> - T: Clone;
> -
> - /// Ensures that the capacity exceeds the length by at least `additional` elements.
> - ///
> - /// # Examples
> - ///
> - /// ```
> - /// let mut v = Vec::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>(())
> - /// ```
> - fn reserve(&mut self, additional: usize, flags: Flags) -> Result<(), AllocError>;
> -}
> -
> -impl<T> VecExt<T> for Vec<T> {
> - fn with_capacity(capacity: usize, flags: Flags) -> Result<Self, AllocError> {
> - let mut v = Vec::new();
> - <Self as VecExt<_>>::reserve(&mut v, capacity, flags)?;
> - Ok(v)
> - }
> -
> - fn push(&mut self, v: T, flags: Flags) -> Result<(), AllocError> {
> - <Self as VecExt<_>>::reserve(self, 1, flags)?;
> - let s = self.spare_capacity_mut();
> - s[0].write(v);
> -
> - // 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(())
> - }
> -
> - fn extend_from_slice(&mut self, other: &[T], flags: Flags) -> Result<(), AllocError>
> - where
> - T: Clone,
> - {
> - <Self as VecExt<_>>::reserve(self, 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.
> - unsafe { self.set_len(self.len() + other.len()) };
> - Ok(())
> - }
> -
> - #[cfg(any(test, testlib))]
> - fn reserve(&mut self, additional: usize, _flags: Flags) -> Result<(), AllocError> {
> - Vec::reserve(self, additional);
> - Ok(())
> - }
> -
> - #[cfg(not(any(test, testlib)))]
> - 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 core::mem::size_of::<T>() == 0 {
> - // The capacity is already `usize::MAX` for SZTs, we can't go higher.
> - return Err(AllocError);
> - }
> -
> - // We know cap is <= `isize::MAX` because `Layout::array` fails if the resulting byte size
> - // is greater than `isize::MAX`. So the multiplication by 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)?;
> -
> - let (old_ptr, len, cap) = destructure(self);
> -
> - // We need to make sure that `ptr` is either NULL or comes from a previous call to
> - // `krealloc_aligned`. A `Vec<T>`'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>`'s capacity
> - // to be zero if no memory has been allocated yet.
> - let ptr = if cap == 0 {
> - core::ptr::null_mut()
> - } else {
> - old_ptr
> - };
> -
> - // SAFETY: `ptr` is valid because it's either NULL or comes from a previous call to
> - // `krealloc_aligned`. We also verified that the type is not a ZST.
> - let new_ptr = unsafe { super::allocator::krealloc_aligned(ptr.cast(), layout, flags) };
> - if new_ptr.is_null() {
> - // SAFETY: We are just rebuilding the existing `Vec` with no changes.
> - unsafe { rebuild(self, old_ptr, len, cap) };
> - Err(AllocError)
> - } else {
> - // SAFETY: `ptr` has been reallocated with the layout for `new_cap` elements. New cap
> - // is greater than `cap`, so it continues to be >= `len`.
> - unsafe { rebuild(self, new_ptr.cast::<T>(), len, new_cap) };
> - Ok(())
> - }
> - }
> -}
> -
> -#[cfg(not(any(test, testlib)))]
> -fn destructure<T>(v: &mut Vec<T>) -> (*mut T, usize, usize) {
> - let mut tmp = Vec::new();
> - core::mem::swap(&mut tmp, v);
> - let mut tmp = core::mem::ManuallyDrop::new(tmp);
> - let len = tmp.len();
> - let cap = tmp.capacity();
> - (tmp.as_mut_ptr(), len, cap)
> -}
> -
> -/// Rebuilds a `Vec` from a pointer, length, and capacity.
> -///
> -/// # Safety
> -///
> -/// The same as [`Vec::from_raw_parts`].
> -#[cfg(not(any(test, testlib)))]
> -unsafe fn rebuild<T>(v: &mut Vec<T>, ptr: *mut T, len: usize, cap: usize) {
> - // SAFETY: The safety requirements from this function satisfy those of `from_raw_parts`.
> - let mut tmp = unsafe { Vec::from_raw_parts(ptr, len, cap) };
> - core::mem::swap(&mut tmp, v);
> -}
> diff --git a/rust/kernel/prelude.rs b/rust/kernel/prelude.rs
> index 80223cdaa485..07daccf6ca8e 100644
> --- a/rust/kernel/prelude.rs
> +++ b/rust/kernel/prelude.rs
> @@ -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};
^ permalink raw reply [flat|nested] 77+ messages in thread
* Re: [PATCH v7 18/26] rust: alloc: add `Vec` to prelude
2024-09-11 22:52 ` [PATCH v7 18/26] rust: alloc: add `Vec` to prelude Danilo Krummrich
@ 2024-09-28 19:29 ` Gary Guo
0 siblings, 0 replies; 77+ messages in thread
From: Gary Guo @ 2024-09-28 19:29 UTC (permalink / raw)
To: Danilo Krummrich
Cc: ojeda, alex.gaynor, wedsonaf, boqun.feng, bjorn3_gh, benno.lossin,
a.hindborg, aliceryhl, akpm, daniel.almeida, faith.ekstrand,
boris.brezillon, lina, mcanal, zhiw, cjia, jhubbard, airlied,
ajanulgu, lyude, linux-kernel, rust-for-linux, linux-mm
On Thu, 12 Sep 2024 00:52:54 +0200
Danilo Krummrich <dakr@kernel.org> wrote:
> Now that we removed `VecExt` and the corresponding includes in
> prelude.rs, add the new kernel `Vec` type instead.
>
> Reviewed-by: Alice Ryhl <aliceryhl@google.com>
> Reviewed-by: Benno Lossin <benno.lossin@proton.me>
> Signed-off-by: Danilo Krummrich <dakr@kernel.org>
Reviewed-by: Gary Guo <gary@garyguo.net>
> ---
> rust/kernel/prelude.rs | 2 +-
> 1 file changed, 1 insertion(+), 1 deletion(-)
>
> diff --git a/rust/kernel/prelude.rs b/rust/kernel/prelude.rs
> index 07daccf6ca8e..8bdab9aa0d16 100644
> --- a/rust/kernel/prelude.rs
> +++ b/rust/kernel/prelude.rs
> @@ -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};
^ permalink raw reply [flat|nested] 77+ messages in thread
* Re: [PATCH v7 19/26] rust: error: use `core::alloc::LayoutError`
2024-09-11 22:52 ` [PATCH v7 19/26] rust: error: use `core::alloc::LayoutError` Danilo Krummrich
@ 2024-09-28 19:30 ` Gary Guo
0 siblings, 0 replies; 77+ messages in thread
From: Gary Guo @ 2024-09-28 19:30 UTC (permalink / raw)
To: Danilo Krummrich
Cc: ojeda, alex.gaynor, wedsonaf, boqun.feng, bjorn3_gh, benno.lossin,
a.hindborg, aliceryhl, akpm, daniel.almeida, faith.ekstrand,
boris.brezillon, lina, mcanal, zhiw, cjia, jhubbard, airlied,
ajanulgu, lyude, linux-kernel, rust-for-linux, linux-mm
On Thu, 12 Sep 2024 00:52:55 +0200
Danilo Krummrich <dakr@kernel.org> wrote:
> Use `core::alloc::LayoutError` instead of `alloc::alloc::LayoutError` in
> preparation to get rid of Rust's alloc crate.
>
> Reviewed-by: Alice Ryhl <aliceryhl@google.com>
> Reviewed-by: Benno Lossin <benno.lossin@proton.me>
> Signed-off-by: Danilo Krummrich <dakr@kernel.org>
Reviewed-by: Gary Guo <gary@garyguo.net>
> ---
> rust/kernel/error.rs | 2 +-
> 1 file changed, 1 insertion(+), 1 deletion(-)
>
> diff --git a/rust/kernel/error.rs b/rust/kernel/error.rs
> index 6f1587a2524e..81b4fc5cf21e 100644
> --- a/rust/kernel/error.rs
> +++ b/rust/kernel/error.rs
> @@ -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;
^ permalink raw reply [flat|nested] 77+ messages in thread
* Re: [PATCH v7 20/26] rust: error: check for config `test` in `Error::name`
2024-09-11 22:52 ` [PATCH v7 20/26] rust: error: check for config `test` in `Error::name` Danilo Krummrich
@ 2024-09-28 19:30 ` Gary Guo
0 siblings, 0 replies; 77+ messages in thread
From: Gary Guo @ 2024-09-28 19:30 UTC (permalink / raw)
To: Danilo Krummrich
Cc: ojeda, alex.gaynor, wedsonaf, boqun.feng, bjorn3_gh, benno.lossin,
a.hindborg, aliceryhl, akpm, daniel.almeida, faith.ekstrand,
boris.brezillon, lina, mcanal, zhiw, cjia, jhubbard, airlied,
ajanulgu, lyude, linux-kernel, rust-for-linux, linux-mm
On Thu, 12 Sep 2024 00:52:56 +0200
Danilo Krummrich <dakr@kernel.org> wrote:
> Additional to `testlib` also check for `test` in `Error::name`. This is
> required by a subsequent patch that (indirectly) uses `Error` in test
> cases.
>
> Reviewed-by: Alice Ryhl <aliceryhl@google.com>
> Reviewed-by: Benno Lossin <benno.lossin@proton.me>
> Signed-off-by: Danilo Krummrich <dakr@kernel.org>
Reviewed-by: Gary Guo <gary@garyguo.net>
> ---
> rust/kernel/error.rs | 4 ++--
> 1 file changed, 2 insertions(+), 2 deletions(-)
>
> diff --git a/rust/kernel/error.rs b/rust/kernel/error.rs
> index 81b4fc5cf21e..b71fdd0f54c2 100644
> --- a/rust/kernel/error.rs
> +++ b/rust/kernel/error.rs
> @@ -143,7 +143,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) };
> @@ -160,7 +160,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
> }
^ permalink raw reply [flat|nested] 77+ messages in thread
* Re: [PATCH v7 21/26] rust: alloc: implement `contains` for `Flags`
2024-09-11 22:52 ` [PATCH v7 21/26] rust: alloc: implement `contains` for `Flags` Danilo Krummrich
@ 2024-09-28 19:31 ` Gary Guo
0 siblings, 0 replies; 77+ messages in thread
From: Gary Guo @ 2024-09-28 19:31 UTC (permalink / raw)
To: Danilo Krummrich
Cc: ojeda, alex.gaynor, wedsonaf, boqun.feng, bjorn3_gh, benno.lossin,
a.hindborg, aliceryhl, akpm, daniel.almeida, faith.ekstrand,
boris.brezillon, lina, mcanal, zhiw, cjia, jhubbard, airlied,
ajanulgu, lyude, linux-kernel, rust-for-linux, linux-mm
On Thu, 12 Sep 2024 00:52:57 +0200
Danilo Krummrich <dakr@kernel.org> wrote:
> 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.
>
> Reviewed-by: Alice Ryhl <aliceryhl@google.com>
> Reviewed-by: Benno Lossin <benno.lossin@proton.me>
> Signed-off-by: Danilo Krummrich <dakr@kernel.org>
Reviewed-by: Gary Guo <gary@garyguo.net>
> ---
> rust/kernel/alloc.rs | 7 ++++++-
> 1 file changed, 6 insertions(+), 1 deletion(-)
>
> diff --git a/rust/kernel/alloc.rs b/rust/kernel/alloc.rs
> index aabdf80e4f7b..caa0b9dfac87 100644
> --- a/rust/kernel/alloc.rs
> +++ b/rust/kernel/alloc.rs
> @@ -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 {
^ permalink raw reply [flat|nested] 77+ messages in thread
* Re: [PATCH v7 22/26] rust: alloc: implement `Cmalloc` in module allocator_test
2024-09-11 22:52 ` [PATCH v7 22/26] rust: alloc: implement `Cmalloc` in module allocator_test Danilo Krummrich
@ 2024-09-28 19:35 ` Gary Guo
2024-09-29 15:14 ` Danilo Krummrich
0 siblings, 1 reply; 77+ messages in thread
From: Gary Guo @ 2024-09-28 19:35 UTC (permalink / raw)
To: Danilo Krummrich
Cc: ojeda, alex.gaynor, wedsonaf, boqun.feng, bjorn3_gh, benno.lossin,
a.hindborg, aliceryhl, akpm, daniel.almeida, faith.ekstrand,
boris.brezillon, lina, mcanal, zhiw, cjia, jhubbard, airlied,
ajanulgu, lyude, linux-kernel, rust-for-linux, linux-mm
On Thu, 12 Sep 2024 00:52:58 +0200
Danilo Krummrich <dakr@kernel.org> wrote:
> 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.
I think this needs an explaination of why it needs a complex
implementation instead of just using aligned_alloc and free.
>
> Signed-off-by: Danilo Krummrich <dakr@kernel.org>
> ---
> rust/kernel/alloc/allocator_test.rs | 193 +++++++++++++++++++++++++++-
> 1 file changed, 186 insertions(+), 7 deletions(-)
^ permalink raw reply [flat|nested] 77+ messages in thread
* Re: [PATCH v7 23/26] rust: str: test: replace `alloc::format`
2024-09-11 22:52 ` [PATCH v7 23/26] rust: str: test: replace `alloc::format` Danilo Krummrich
@ 2024-09-28 19:37 ` Gary Guo
0 siblings, 0 replies; 77+ messages in thread
From: Gary Guo @ 2024-09-28 19:37 UTC (permalink / raw)
To: Danilo Krummrich
Cc: ojeda, alex.gaynor, wedsonaf, boqun.feng, bjorn3_gh, benno.lossin,
a.hindborg, aliceryhl, akpm, daniel.almeida, faith.ekstrand,
boris.brezillon, lina, mcanal, zhiw, cjia, jhubbard, airlied,
ajanulgu, lyude, linux-kernel, rust-for-linux, linux-mm
On Thu, 12 Sep 2024 00:52:59 +0200
Danilo Krummrich <dakr@kernel.org> wrote:
> 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`.
>
> Reviewed-by: Alice Ryhl <aliceryhl@google.com>
> Reviewed-by: Benno Lossin <benno.lossin@proton.me>
> Signed-off-by: Danilo Krummrich <dakr@kernel.org>
Reviewed-by: Gary Guo <gary@garyguo.net>
> ---
> rust/kernel/str.rs | 23 ++++++++++++++++++++++-
> 1 file changed, 22 insertions(+), 1 deletion(-)
>
> diff --git a/rust/kernel/str.rs b/rust/kernel/str.rs
> index 209ad1bb1a49..726329530338 100644
> --- a/rust/kernel/str.rs
> +++ b/rust/kernel/str.rs
> @@ -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\
^ permalink raw reply [flat|nested] 77+ messages in thread
* Re: [PATCH v7 24/26] rust: alloc: update module comment of alloc.rs
2024-09-11 22:53 ` [PATCH v7 24/26] rust: alloc: update module comment of alloc.rs Danilo Krummrich
@ 2024-09-28 19:38 ` Gary Guo
0 siblings, 0 replies; 77+ messages in thread
From: Gary Guo @ 2024-09-28 19:38 UTC (permalink / raw)
To: Danilo Krummrich
Cc: ojeda, alex.gaynor, wedsonaf, boqun.feng, bjorn3_gh, benno.lossin,
a.hindborg, aliceryhl, akpm, daniel.almeida, faith.ekstrand,
boris.brezillon, lina, mcanal, zhiw, cjia, jhubbard, airlied,
ajanulgu, lyude, linux-kernel, rust-for-linux, linux-mm
On Thu, 12 Sep 2024 00:53:00 +0200
Danilo Krummrich <dakr@kernel.org> wrote:
> 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.
>
> Reviewed-by: Alice Ryhl <aliceryhl@google.com>
> Reviewed-by: Benno Lossin <benno.lossin@proton.me>
> Signed-off-by: Danilo Krummrich <dakr@kernel.org>
Reviewed-by: Gary Guo <gary@garyguo.net>
> ---
> rust/kernel/alloc.rs | 2 +-
> 1 file changed, 1 insertion(+), 1 deletion(-)
>
> diff --git a/rust/kernel/alloc.rs b/rust/kernel/alloc.rs
> index caa0b9dfac87..2170b53acd0c 100644
> --- a/rust/kernel/alloc.rs
> +++ b/rust/kernel/alloc.rs
> @@ -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;
^ permalink raw reply [flat|nested] 77+ messages in thread
* Re: [PATCH v7 25/26] kbuild: rust: remove the `alloc` crate and `GlobalAlloc`
2024-09-11 22:53 ` [PATCH v7 25/26] kbuild: rust: remove the `alloc` crate and `GlobalAlloc` Danilo Krummrich
@ 2024-09-28 19:43 ` Gary Guo
2024-09-29 15:17 ` Danilo Krummrich
2024-10-03 21:41 ` Miguel Ojeda
0 siblings, 2 replies; 77+ messages in thread
From: Gary Guo @ 2024-09-28 19:43 UTC (permalink / raw)
To: Danilo Krummrich
Cc: ojeda, alex.gaynor, wedsonaf, boqun.feng, bjorn3_gh, benno.lossin,
a.hindborg, aliceryhl, akpm, daniel.almeida, faith.ekstrand,
boris.brezillon, lina, mcanal, zhiw, cjia, jhubbard, airlied,
ajanulgu, lyude, linux-kernel, rust-for-linux, linux-mm
On Thu, 12 Sep 2024 00:53:01 +0200
Danilo Krummrich <dakr@kernel.org> wrote:
> 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`.
>
> Signed-off-by: Danilo Krummrich <dakr@kernel.org>
> ---
> rust/Makefile | 43 +++++----------------
> rust/exports.c | 1 -
> rust/kernel/alloc/allocator.rs | 63 +------------------------------
> scripts/Makefile.build | 7 +---
> scripts/generate_rust_analyzer.py | 11 +-----
> 5 files changed, 15 insertions(+), 110 deletions(-)
>
> diff --git a/scripts/Makefile.build b/scripts/Makefile.build
> index 72b1232b1f7d..529ec5972e55 100644
> --- a/scripts/Makefile.build
> +++ b/scripts/Makefile.build
> @@ -262,18 +262,13 @@ $(obj)/%.lst: $(obj)/%.c FORCE
>
> # Compile Rust sources (.rs)
> # ---------------------------------------------------------------------------
> -
> -rust_allowed_features := new_uninit
> -
Would it make sense to throw in a soon-to-be-stable (or
already-stable-but-beyond-min-rust-version) feature here to not have to
remove the build system support for unstable feature directly?
For example, I know that const_refs_to_static might be added here
soon-ish.
> # `--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) \
I think this actually allow all features instead of allowing none.
> -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 \
> diff --git a/scripts/generate_rust_analyzer.py b/scripts/generate_rust_analyzer.py
> index d2bc63cde8c6..09e1d166d8d2 100755
> --- a/scripts/generate_rust_analyzer.py
> +++ b/scripts/generate_rust_analyzer.py
> @@ -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,
> )
>
^ permalink raw reply [flat|nested] 77+ messages in thread
* Re: [PATCH v7 10/26] rust: treewide: switch to our kernel `Box` type
2024-09-28 18:59 ` Gary Guo
@ 2024-09-29 14:52 ` Danilo Krummrich
0 siblings, 0 replies; 77+ messages in thread
From: Danilo Krummrich @ 2024-09-29 14:52 UTC (permalink / raw)
To: Gary Guo
Cc: ojeda, alex.gaynor, wedsonaf, boqun.feng, bjorn3_gh, benno.lossin,
a.hindborg, aliceryhl, akpm, daniel.almeida, faith.ekstrand,
boris.brezillon, lina, mcanal, zhiw, cjia, jhubbard, airlied,
ajanulgu, lyude, linux-kernel, rust-for-linux, linux-mm
On Sat, Sep 28, 2024 at 07:59:13PM +0100, Gary Guo wrote:
> On Thu, 12 Sep 2024 00:52:46 +0200
> Danilo Krummrich <dakr@kernel.org> wrote:
>
> > Now that we got the kernel `Box` type in place, convert all existing
> > `Box` users to make use of it.
> >
> > Reviewed-by: Alice Ryhl <aliceryhl@google.com>
> > Reviewed-by: Benno Lossin <benno.lossin@proton.me>
> > Signed-off-by: Danilo Krummrich <dakr@kernel.org>
>
> Reviewed-by: Gary Guo <gary@garyguo.net>
>
> Have you considered defaulting the `A` in `Box` to `Kmalloc`? This
> would avoid doing a lot of tree-wide changes.
>
> If you have a reason against it (I'm pretty sure you have), then
> probably you should put it in the commit message.
Yes, I want people to be forced to make an explicit decision about the allocator
backend they choose, because this has relevant implications.
For instance, it's likely to be unexpected to people coming from userspace Rust,
that (with the default of `Kmalloc`) `Box` allocations larger than
`KMALLOC_MAX_SIZE` would just fail.
>
> Best,
> Gary
>
> > ---
> > drivers/block/rnull.rs | 4 +--
> > rust/kernel/init.rs | 51 ++++++++++++++++---------------
> > rust/kernel/init/__internal.rs | 2 +-
> > rust/kernel/rbtree.rs | 49 ++++++++++++++++-------------
> > 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, 81 insertions(+), 76 deletions(-)
>
^ permalink raw reply [flat|nested] 77+ messages in thread
* Re: [PATCH v7 15/26] rust: alloc: implement `collect` for `IntoIter`
2024-09-28 19:27 ` Gary Guo
@ 2024-09-29 15:12 ` Danilo Krummrich
0 siblings, 0 replies; 77+ messages in thread
From: Danilo Krummrich @ 2024-09-29 15:12 UTC (permalink / raw)
To: Gary Guo
Cc: ojeda, alex.gaynor, wedsonaf, boqun.feng, bjorn3_gh, benno.lossin,
a.hindborg, aliceryhl, akpm, daniel.almeida, faith.ekstrand,
boris.brezillon, lina, mcanal, zhiw, cjia, jhubbard, airlied,
ajanulgu, lyude, linux-kernel, rust-for-linux, linux-mm
On Sat, Sep 28, 2024 at 08:27:34PM +0100, Gary Guo wrote:
> On Thu, 12 Sep 2024 00:52:51 +0200
> Danilo Krummrich <dakr@kernel.org> wrote:
>
> > 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.
> >
> > Reviewed-by: Alice Ryhl <aliceryhl@google.com>
> > Signed-off-by: Danilo Krummrich <dakr@kernel.org>
>
> A question is how useful is this? The way this can be used seems
> fairly limited: you `into_iter`, consume a few elements, and the
> `collect()`?
Well, it allows us to convert back to a `Vec`, which we otherwise can't until we
implement our own `FromIterator` trait.
Also note that we want to have this specialization of `collect` for performance
reasons anyways. The Rust stdlib uses specialization traits (which aren't yet
stable) for this optimization.
>
> It feels whatever user this serves, it would make more sense for them
> to use a `VecDeque` and just `pop_front()`.
We don't have `VecDeque` (yet).
>
> Also, inline comments below.
>
> > ---
> > rust/kernel/alloc/kvec.rs | 86 +++++++++++++++++++++++++++++++++++++++
> > 1 file changed, 86 insertions(+)
> >
> > diff --git a/rust/kernel/alloc/kvec.rs b/rust/kernel/alloc/kvec.rs
> > index e91761c5c52d..686e969463f8 100644
> > --- a/rust/kernel/alloc/kvec.rs
> > +++ b/rust/kernel/alloc/kvec.rs
> > @@ -690,6 +690,92 @@ 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`.
> > + ///
> > + /// # 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>(())
> > + /// ```
> > + /// # Implementation Details
> > + ///
> > + /// 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.
> > + 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 {
> > + // Copy the contents we have advanced to at the beginning of the buffer.
> > + //
> > + // SAFETY:
> > + // - `ptr` is valid for reads of `len * size_of::<T>()` bytes,
> > + // - `buf.as_ptr()` is valid for writes of `len * size_of::<T>()` bytes,
> > + // - `ptr` and `buf.as_ptr()` are not be subject to aliasing restrictions relative to
> > + // each other,
> > + // - both `ptr` and `buf.ptr()` are properly aligned.
> > + 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();
>
> nit: could be `unwrap_unchecked()`. Although feel free to leave it to
> avoid an additional unsafe.
This will be replaced with `ArrayLayout` anyways.
>
> > +
> > + // 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()
> > + }
> > + };
>
> This should be moved to `Vec::shrink_to_fit`. And then this function
> can just `Vec::from_raw_parts(...)` and then `vec.shrink_to_fit`.
I'll put it on my list for a follow up patch, I really think we should focus on
landing the series now.
>
> But my question would be why this function needs to shrink in the first
> place.
It's meant as an optimization for `Iterator::collect` in the mid / long term. A
user expects that the resulting allocation isn't larger than actually needed for
`Iterator::collect`.
>
> > +
> > + // 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>
>
^ permalink raw reply [flat|nested] 77+ messages in thread
* Re: [PATCH v7 22/26] rust: alloc: implement `Cmalloc` in module allocator_test
2024-09-28 19:35 ` Gary Guo
@ 2024-09-29 15:14 ` Danilo Krummrich
0 siblings, 0 replies; 77+ messages in thread
From: Danilo Krummrich @ 2024-09-29 15:14 UTC (permalink / raw)
To: Gary Guo
Cc: ojeda, alex.gaynor, wedsonaf, boqun.feng, bjorn3_gh, benno.lossin,
a.hindborg, aliceryhl, akpm, daniel.almeida, faith.ekstrand,
boris.brezillon, lina, mcanal, zhiw, cjia, jhubbard, airlied,
ajanulgu, lyude, linux-kernel, rust-for-linux, linux-mm
On Sat, Sep 28, 2024 at 08:35:52PM +0100, Gary Guo wrote:
> On Thu, 12 Sep 2024 00:52:58 +0200
> Danilo Krummrich <dakr@kernel.org> wrote:
>
> > 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.
>
> I think this needs an explaination of why it needs a complex
> implementation instead of just using aligned_alloc and free.
The reason basically is that there's no `aligned_realloc` in libc. Anyways, it's
gonna simplified by the introduction of `old_layout`.
>
> >
> > Signed-off-by: Danilo Krummrich <dakr@kernel.org>
> > ---
> > rust/kernel/alloc/allocator_test.rs | 193 +++++++++++++++++++++++++++-
> > 1 file changed, 186 insertions(+), 7 deletions(-)
>
^ permalink raw reply [flat|nested] 77+ messages in thread
* Re: [PATCH v7 25/26] kbuild: rust: remove the `alloc` crate and `GlobalAlloc`
2024-09-28 19:43 ` Gary Guo
@ 2024-09-29 15:17 ` Danilo Krummrich
2024-10-01 13:27 ` Danilo Krummrich
2024-10-03 21:41 ` Miguel Ojeda
1 sibling, 1 reply; 77+ messages in thread
From: Danilo Krummrich @ 2024-09-29 15:17 UTC (permalink / raw)
To: Gary Guo
Cc: ojeda, alex.gaynor, wedsonaf, boqun.feng, bjorn3_gh, benno.lossin,
a.hindborg, aliceryhl, akpm, daniel.almeida, faith.ekstrand,
boris.brezillon, lina, mcanal, zhiw, cjia, jhubbard, airlied,
ajanulgu, lyude, linux-kernel, rust-for-linux, linux-mm
On Sat, Sep 28, 2024 at 08:43:57PM +0100, Gary Guo wrote:
> On Thu, 12 Sep 2024 00:53:01 +0200
> Danilo Krummrich <dakr@kernel.org> wrote:
>
> > 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`.
> >
> > Signed-off-by: Danilo Krummrich <dakr@kernel.org>
> > ---
> > rust/Makefile | 43 +++++----------------
> > rust/exports.c | 1 -
> > rust/kernel/alloc/allocator.rs | 63 +------------------------------
> > scripts/Makefile.build | 7 +---
> > scripts/generate_rust_analyzer.py | 11 +-----
> > 5 files changed, 15 insertions(+), 110 deletions(-)
> >
> > diff --git a/scripts/Makefile.build b/scripts/Makefile.build
> > index 72b1232b1f7d..529ec5972e55 100644
> > --- a/scripts/Makefile.build
> > +++ b/scripts/Makefile.build
> > @@ -262,18 +262,13 @@ $(obj)/%.lst: $(obj)/%.c FORCE
> >
> > # Compile Rust sources (.rs)
> > # ---------------------------------------------------------------------------
> > -
> > -rust_allowed_features := new_uninit
> > -
>
> Would it make sense to throw in a soon-to-be-stable (or
> already-stable-but-beyond-min-rust-version) feature here to not have to
> remove the build system support for unstable feature directly?
It's only about those two lines, right? I can only remove the `new_uninit` and
leave `rust_allowed_features :=` and leave...
>
> For example, I know that const_refs_to_static might be added here
> soon-ish.
>
> > # `--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) \
...this line, I guess?
>
> I think this actually allow all features instead of allowing none.
>
> > -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 \
> > diff --git a/scripts/generate_rust_analyzer.py b/scripts/generate_rust_analyzer.py
> > index d2bc63cde8c6..09e1d166d8d2 100755
> > --- a/scripts/generate_rust_analyzer.py
> > +++ b/scripts/generate_rust_analyzer.py
> > @@ -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,
> > )
> >
>
^ permalink raw reply [flat|nested] 77+ messages in thread
* Re: [PATCH v7 25/26] kbuild: rust: remove the `alloc` crate and `GlobalAlloc`
2024-09-29 15:17 ` Danilo Krummrich
@ 2024-10-01 13:27 ` Danilo Krummrich
0 siblings, 0 replies; 77+ messages in thread
From: Danilo Krummrich @ 2024-10-01 13:27 UTC (permalink / raw)
To: Gary Guo
Cc: ojeda, alex.gaynor, wedsonaf, boqun.feng, bjorn3_gh, benno.lossin,
a.hindborg, aliceryhl, akpm, daniel.almeida, faith.ekstrand,
boris.brezillon, lina, mcanal, zhiw, cjia, jhubbard, airlied,
ajanulgu, lyude, linux-kernel, rust-for-linux, linux-mm
On 9/29/24 5:17 PM, Danilo Krummrich wrote:
> On Sat, Sep 28, 2024 at 08:43:57PM +0100, Gary Guo wrote:
>> On Thu, 12 Sep 2024 00:53:01 +0200
>> Danilo Krummrich <dakr@kernel.org> wrote:
>>
>>> 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`.
>>>
>>> Signed-off-by: Danilo Krummrich <dakr@kernel.org>
>>> ---
>>> rust/Makefile | 43 +++++----------------
>>> rust/exports.c | 1 -
>>> rust/kernel/alloc/allocator.rs | 63 +------------------------------
>>> scripts/Makefile.build | 7 +---
>>> scripts/generate_rust_analyzer.py | 11 +-----
>>> 5 files changed, 15 insertions(+), 110 deletions(-)
>>>
>>> diff --git a/scripts/Makefile.build b/scripts/Makefile.build
>>> index 72b1232b1f7d..529ec5972e55 100644
>>> --- a/scripts/Makefile.build
>>> +++ b/scripts/Makefile.build
>>> @@ -262,18 +262,13 @@ $(obj)/%.lst: $(obj)/%.c FORCE
>>>
>>> # Compile Rust sources (.rs)
>>> # ---------------------------------------------------------------------------
>>> -
>>> -rust_allowed_features := new_uninit
>>> -
>>
>> Would it make sense to throw in a soon-to-be-stable (or
>> already-stable-but-beyond-min-rust-version) feature here to not have to
>> remove the build system support for unstable feature directly?
>
> It's only about those two lines, right? I can only remove the `new_uninit` and
> leave `rust_allowed_features :=` and leave...
>
>>
>> For example, I know that const_refs_to_static might be added here
>> soon-ish.
>>
>>> # `--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) \
>
> ...this line, I guess?
Seems like I can't, it blows up with an empty feature list. I'll keep it as it
is for now. Shouldn't be a big deal to add it again, once needed.
>
>>
>> I think this actually allow all features instead of allowing none.
>>
>>> -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 \
>>> diff --git a/scripts/generate_rust_analyzer.py b/scripts/generate_rust_analyzer.py
>>> index d2bc63cde8c6..09e1d166d8d2 100755
>>> --- a/scripts/generate_rust_analyzer.py
>>> +++ b/scripts/generate_rust_analyzer.py
>>> @@ -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,
>>> )
>>>
>>
^ permalink raw reply [flat|nested] 77+ messages in thread
* Re: [PATCH v7 25/26] kbuild: rust: remove the `alloc` crate and `GlobalAlloc`
2024-09-28 19:43 ` Gary Guo
2024-09-29 15:17 ` Danilo Krummrich
@ 2024-10-03 21:41 ` Miguel Ojeda
2024-10-03 21:53 ` Danilo Krummrich
1 sibling, 1 reply; 77+ messages in thread
From: Miguel Ojeda @ 2024-10-03 21:41 UTC (permalink / raw)
To: Gary Guo
Cc: Danilo Krummrich, ojeda, alex.gaynor, wedsonaf, boqun.feng,
bjorn3_gh, benno.lossin, a.hindborg, aliceryhl, akpm,
daniel.almeida, faith.ekstrand, boris.brezillon, lina, mcanal,
zhiw, cjia, jhubbard, airlied, ajanulgu, lyude, linux-kernel,
rust-for-linux, linux-mm
On Mon, Sep 30, 2024 at 9:11 PM Gary Guo <gary@garyguo.net> wrote:
>
> Would it make sense to throw in a soon-to-be-stable (or
> already-stable-but-beyond-min-rust-version) feature here to not have to
> remove the build system support for unstable feature directly?
Yeah, agreed -- we are going to have `lint_reasons` and
`arbitrary_self_types` before this series, so it should be fine.
Cheers,
Miguel
^ permalink raw reply [flat|nested] 77+ messages in thread
* Re: [PATCH v7 25/26] kbuild: rust: remove the `alloc` crate and `GlobalAlloc`
2024-10-03 21:41 ` Miguel Ojeda
@ 2024-10-03 21:53 ` Danilo Krummrich
2024-10-03 22:49 ` Miguel Ojeda
0 siblings, 1 reply; 77+ messages in thread
From: Danilo Krummrich @ 2024-10-03 21:53 UTC (permalink / raw)
To: Miguel Ojeda
Cc: Gary Guo, ojeda, alex.gaynor, wedsonaf, boqun.feng, bjorn3_gh,
benno.lossin, a.hindborg, aliceryhl, akpm, daniel.almeida,
faith.ekstrand, boris.brezillon, lina, mcanal, zhiw, cjia,
jhubbard, airlied, ajanulgu, lyude, linux-kernel, rust-for-linux,
linux-mm
On 10/3/24 11:41 PM, Miguel Ojeda wrote:
> On Mon, Sep 30, 2024 at 9:11 PM Gary Guo <gary@garyguo.net> wrote:
>>
>> Would it make sense to throw in a soon-to-be-stable (or
>> already-stable-but-beyond-min-rust-version) feature here to not have to
>> remove the build system support for unstable feature directly?
>
> Yeah, agreed -- we are going to have `lint_reasons` and
> `arbitrary_self_types` before this series, so it should be fine.
So, I guess you'll just fix it up when applying the series?
>
> Cheers,
> Miguel
>
^ permalink raw reply [flat|nested] 77+ messages in thread
* Re: [PATCH v7 25/26] kbuild: rust: remove the `alloc` crate and `GlobalAlloc`
2024-10-03 21:53 ` Danilo Krummrich
@ 2024-10-03 22:49 ` Miguel Ojeda
0 siblings, 0 replies; 77+ messages in thread
From: Miguel Ojeda @ 2024-10-03 22:49 UTC (permalink / raw)
To: Danilo Krummrich
Cc: Gary Guo, ojeda, alex.gaynor, wedsonaf, boqun.feng, bjorn3_gh,
benno.lossin, a.hindborg, aliceryhl, akpm, daniel.almeida,
faith.ekstrand, boris.brezillon, lina, mcanal, zhiw, cjia,
jhubbard, airlied, ajanulgu, lyude, linux-kernel, rust-for-linux,
linux-mm
On Thu, Oct 3, 2024 at 11:54 PM Danilo Krummrich <dakr@kernel.org> wrote:
>
> So, I guess you'll just fix it up when applying the series?
Sure, if there is no need for a new version, then no worries.
Cheers,
Miguel
^ permalink raw reply [flat|nested] 77+ messages in thread
end of thread, other threads:[~2024-10-03 22:50 UTC | newest]
Thread overview: 77+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2024-09-11 22:52 [PATCH v7 00/26] Generic `Allocator` support for Rust Danilo Krummrich
2024-09-11 22:52 ` [PATCH v7 01/26] rust: alloc: add `Allocator` trait Danilo Krummrich
2024-09-15 15:28 ` Gary Guo
2024-09-15 17:02 ` Danilo Krummrich
2024-09-15 19:22 ` Gary Guo
2024-09-15 20:08 ` Gary Guo
2024-09-15 21:39 ` Danilo Krummrich
2024-09-15 21:37 ` Danilo Krummrich
2024-09-21 15:32 ` [RFC PATCH] rust: alloc: pass `old_layout` to `Allocator` Danilo Krummrich
2024-09-23 13:56 ` Alice Ryhl
2024-09-23 15:20 ` Benno Lossin
2024-09-23 16:13 ` Gary Guo
2024-09-24 13:31 ` Danilo Krummrich
2024-09-24 13:34 ` Danilo Krummrich
2024-09-24 19:58 ` Gary Guo
2024-09-11 22:52 ` [PATCH v7 02/26] rust: alloc: separate `aligned_size` from `krealloc_aligned` Danilo Krummrich
2024-09-11 22:52 ` [PATCH v7 03/26] rust: alloc: rename `KernelAllocator` to `Kmalloc` Danilo Krummrich
2024-09-11 22:52 ` [PATCH v7 04/26] rust: alloc: implement `Allocator` for `Kmalloc` Danilo Krummrich
2024-09-26 13:00 ` Benno Lossin
2024-09-26 13:24 ` Danilo Krummrich
2024-09-26 14:00 ` Benno Lossin
2024-09-11 22:52 ` [PATCH v7 05/26] rust: alloc: add module `allocator_test` Danilo Krummrich
2024-09-11 22:52 ` [PATCH v7 06/26] rust: alloc: implement `Vmalloc` allocator Danilo Krummrich
2024-09-26 13:06 ` Benno Lossin
2024-09-11 22:52 ` [PATCH v7 07/26] rust: alloc: implement `KVmalloc` allocator Danilo Krummrich
2024-09-26 13:07 ` Benno Lossin
2024-09-11 22:52 ` [PATCH v7 08/26] rust: alloc: add __GFP_NOWARN to `Flags` Danilo Krummrich
2024-09-28 18:55 ` Gary Guo
2024-09-11 22:52 ` [PATCH v7 09/26] rust: alloc: implement kernel `Box` Danilo Krummrich
2024-09-26 13:23 ` Benno Lossin
2024-09-28 18:54 ` Gary Guo
2024-09-11 22:52 ` [PATCH v7 10/26] rust: treewide: switch to our kernel `Box` type Danilo Krummrich
2024-09-28 18:59 ` Gary Guo
2024-09-29 14:52 ` Danilo Krummrich
2024-09-11 22:52 ` [PATCH v7 11/26] rust: alloc: remove extension of std's `Box` Danilo Krummrich
2024-09-28 19:00 ` Gary Guo
2024-09-11 22:52 ` [PATCH v7 12/26] rust: alloc: add `Box` to prelude Danilo Krummrich
2024-09-28 19:00 ` Gary Guo
2024-09-11 22:52 ` [PATCH v7 13/26] rust: alloc: implement kernel `Vec` type Danilo Krummrich
2024-09-26 13:47 ` Benno Lossin
2024-09-28 12:43 ` Danilo Krummrich
2024-09-28 13:20 ` Benno Lossin
2024-09-28 19:14 ` Gary Guo
2024-09-11 22:52 ` [PATCH v7 14/26] rust: alloc: implement `IntoIterator` for `Vec` Danilo Krummrich
2024-09-26 13:53 ` Benno Lossin
2024-09-28 19:20 ` Gary Guo
2024-09-11 22:52 ` [PATCH v7 15/26] rust: alloc: implement `collect` for `IntoIter` Danilo Krummrich
2024-09-26 13:57 ` Benno Lossin
2024-09-28 19:27 ` Gary Guo
2024-09-29 15:12 ` Danilo Krummrich
2024-09-11 22:52 ` [PATCH v7 16/26] rust: treewide: switch to the kernel `Vec` type Danilo Krummrich
2024-09-28 19:28 ` Gary Guo
2024-09-11 22:52 ` [PATCH v7 17/26] rust: alloc: remove `VecExt` extension Danilo Krummrich
2024-09-28 19:29 ` Gary Guo
2024-09-11 22:52 ` [PATCH v7 18/26] rust: alloc: add `Vec` to prelude Danilo Krummrich
2024-09-28 19:29 ` Gary Guo
2024-09-11 22:52 ` [PATCH v7 19/26] rust: error: use `core::alloc::LayoutError` Danilo Krummrich
2024-09-28 19:30 ` Gary Guo
2024-09-11 22:52 ` [PATCH v7 20/26] rust: error: check for config `test` in `Error::name` Danilo Krummrich
2024-09-28 19:30 ` Gary Guo
2024-09-11 22:52 ` [PATCH v7 21/26] rust: alloc: implement `contains` for `Flags` Danilo Krummrich
2024-09-28 19:31 ` Gary Guo
2024-09-11 22:52 ` [PATCH v7 22/26] rust: alloc: implement `Cmalloc` in module allocator_test Danilo Krummrich
2024-09-28 19:35 ` Gary Guo
2024-09-29 15:14 ` Danilo Krummrich
2024-09-11 22:52 ` [PATCH v7 23/26] rust: str: test: replace `alloc::format` Danilo Krummrich
2024-09-28 19:37 ` Gary Guo
2024-09-11 22:53 ` [PATCH v7 24/26] rust: alloc: update module comment of alloc.rs Danilo Krummrich
2024-09-28 19:38 ` Gary Guo
2024-09-11 22:53 ` [PATCH v7 25/26] kbuild: rust: remove the `alloc` crate and `GlobalAlloc` Danilo Krummrich
2024-09-28 19:43 ` Gary Guo
2024-09-29 15:17 ` Danilo Krummrich
2024-10-01 13:27 ` Danilo Krummrich
2024-10-03 21:41 ` Miguel Ojeda
2024-10-03 21:53 ` Danilo Krummrich
2024-10-03 22:49 ` Miguel Ojeda
2024-09-11 22:53 ` [PATCH v7 26/26] MAINTAINERS: add entry for the Rust `alloc` module Danilo Krummrich
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for NNTP newsgroup(s).