rust-for-linux.vger.kernel.org archive mirror
 help / color / mirror / Atom feed
* [PATCH v4 00/28] Generic `Allocator` support for Rust
@ 2024-08-05 15:19 Danilo Krummrich
  2024-08-05 15:19 ` [PATCH v4 01/28] rust: alloc: add `Allocator` trait Danilo Krummrich
                   ` (27 more replies)
  0 siblings, 28 replies; 100+ messages in thread
From: Danilo Krummrich @ 2024-08-05 15:19 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, acurrid, 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-unstable, and is also available
in [2].

[1] https://git.kernel.org/pub/scm/linux/kernel/git/dakr/linux.git/log/?h=mm/krealloc
[2] https://git.kernel.org/pub/scm/linux/kernel/git/dakr/linux.git/log/?h=rust/mm

Changes in v4:
 - (safety) comment fixes suggested by Alice and Boqun
 - remove `Box::from_raw_alloc` and `Box::into_raw_alloc`, we don't need them
 - in `Box::drop` call `size_of_val` before `drop_in_place`
 - implement ForeignOwnable for Pin<Box<T>> as suggested by Alice
 - in `Vec::extend_with`, iterate over `n` instead of `spare.len()`
 - for `Vmalloc` and `KVmalloc` fail allocation for alignments larger than
   PAGE_SIZE for now (will add support for larger alignments in a separate
   series)
 - implement `Cmalloc` in `allocator_test` and type alias all kernel allocator
   types to it, such that we can use the kernel's `Box` and `Vec` types in
   userspace tests (rusttest)
   - this makes patch "rust: str: test: replace `alloc::format`" rather trivial

Changes in v3:
 - Box:
   - minor documentation fixes
   - removed unnecessary imports in doc tests
   - dropeed `self` argument from some remaining `Box` methods 
   - implement `InPlaceInit` for Box<T, A> rather than specifically for `KBox<T>`
 - Vec:
   - minor documentation fixes
   - removed useless `Vec::allocator` method
   - in `Vec::extend_with` use `Vec::spare_capacity_mut` instead of raw pointer operations
   - added a few missing safety comments
   - pass GFP flags to `Vec::collect`
 - fixed a rustdoc warning in alloc.rs
 - fixed the allocator_test module to implement the `Allocator` trait correctly
 - rebased to rust-next

Changes in v2:
  - preserve `impl GlobalAlloc for Kmalloc` and remove it at the end (Benno)
  - remove `&self` parameter from all `Allocator` functions (Benno)
  - various documentation fixes for `Allocator` (Benno)
  - use `NonNull<u8>` for `Allocator::free` and `Option<NonNull<u8>>` for
    `Allocator::realloc` (Benno)
  - fix leak of `IntoIter` in `Vec::collect` (Boqun)
  - always realloc (try to shrink) in `Vec::collect`, it's up the the
    `Allocator` to provide a heuristic whether it makes sense to actually shrink
  - rename `KBox<T, A>` -> `Box<T, A>` and `KVec<T, A>` -> `Vec<T, A>` and
    provide type aliases `KBox<T>`, `VBox<T>`, `KVBox<T>`, etc.
    - This allows for much cleaner code and, in combination with removing
      `&self` parameters from `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 (28):
  rust: alloc: add `Allocator` trait
  rust: alloc: separate `aligned_size` from `krealloc_aligned`
  rust: alloc: rename `KernelAllocator` to `Kmalloc`
  rust: alloc: implement `Allocator` for `Kmalloc`
  rust: alloc: add module `allocator_test`
  rust: alloc: implement `Vmalloc` allocator
  rust: alloc: implement `KVmalloc` allocator
  rust: types: implement `Unique<T>`
  rust: alloc: implement kernel `Box`
  rust: treewide: switch to our kernel `Box` type
  rust: alloc: remove `BoxExt` extension
  rust: alloc: add `Box` to prelude
  rust: alloc: import kernel `Box` type in types.rs
  rust: alloc: import kernel `Box` type in init.rs
  rust: alloc: implement kernel `Vec` type
  rust: alloc: implement `IntoIterator` for `Vec`
  rust: alloc: implement `collect` for `IntoIter`
  rust: treewide: switch to the kernel `Vec` type
  rust: alloc: remove `VecExt` extension
  rust: alloc: add `Vec` to prelude
  rust: alloc: remove `GlobalAlloc` and `krealloc_aligned`
  rust: error: use `core::alloc::LayoutError`
  rust: error: check for config `test` in `Error::name`
  rust: alloc: implement `contains` for `Flags`
  rust: alloc: implement `Cmalloc` in module allocator_test
  rust: str: test: replace `alloc::format`
  rust: alloc: update module comment of alloc.rs
  kbuild: rust: remove the `alloc` crate

 rust/Makefile                       |  44 +-
 rust/exports.c                      |   1 -
 rust/helpers.c                      |  16 +-
 rust/kernel/alloc.rs                | 114 +++-
 rust/kernel/alloc/allocator.rs      | 167 ++++--
 rust/kernel/alloc/allocator_test.rs |  98 ++++
 rust/kernel/alloc/box_ext.rs        |  56 --
 rust/kernel/alloc/kbox.rs           | 344 +++++++++++
 rust/kernel/alloc/kvec.rs           | 855 ++++++++++++++++++++++++++++
 rust/kernel/alloc/vec_ext.rs        | 185 ------
 rust/kernel/error.rs                |   6 +-
 rust/kernel/init.rs                 |  56 +-
 rust/kernel/init/__internal.rs      |   2 +-
 rust/kernel/lib.rs                  |   1 -
 rust/kernel/prelude.rs              |   5 +-
 rust/kernel/str.rs                  |  35 +-
 rust/kernel/sync/arc.rs             |  17 +-
 rust/kernel/sync/condvar.rs         |   4 +-
 rust/kernel/sync/lock/mutex.rs      |   2 +-
 rust/kernel/sync/lock/spinlock.rs   |   2 +-
 rust/kernel/sync/locked_by.rs       |   2 +-
 rust/kernel/types.rs                | 222 +++++++-
 rust/kernel/uaccess.rs              |  15 +-
 rust/kernel/workqueue.rs            |  20 +-
 samples/rust/rust_minimal.rs        |   4 +-
 scripts/Makefile.build              |   7 +-
 26 files changed, 1863 insertions(+), 417 deletions(-)
 create mode 100644 rust/kernel/alloc/allocator_test.rs
 delete mode 100644 rust/kernel/alloc/box_ext.rs
 create mode 100644 rust/kernel/alloc/kbox.rs
 create mode 100644 rust/kernel/alloc/kvec.rs
 delete mode 100644 rust/kernel/alloc/vec_ext.rs


base-commit: c0d669a6bbe161a3afa26dea3d8491863e352e59
-- 
2.45.2


^ permalink raw reply	[flat|nested] 100+ messages in thread

* [PATCH v4 01/28] rust: alloc: add `Allocator` trait
  2024-08-05 15:19 [PATCH v4 00/28] Generic `Allocator` support for Rust Danilo Krummrich
@ 2024-08-05 15:19 ` Danilo Krummrich
  2024-08-06 15:00   ` Alice Ryhl
                     ` (2 more replies)
  2024-08-05 15:19 ` [PATCH v4 02/28] rust: alloc: separate `aligned_size` from `krealloc_aligned` Danilo Krummrich
                   ` (26 subsequent siblings)
  27 siblings, 3 replies; 100+ messages in thread
From: Danilo Krummrich @ 2024-08-05 15:19 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, acurrid, 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`.

Signed-off-by: Danilo Krummrich <dakr@kernel.org>
---
 rust/kernel/alloc.rs | 79 ++++++++++++++++++++++++++++++++++++++++++++
 1 file changed, 79 insertions(+)

diff --git a/rust/kernel/alloc.rs b/rust/kernel/alloc.rs
index 1966bd407017..8a71a589469d 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,81 @@ pub mod flags {
     /// small allocations.
     pub const GFP_NOWAIT: Flags = Flags(bindings::GFP_NOWAIT);
 }
+
+/// The kernel's [`Allocator`] trait.
+///
+/// An implementation of [`Allocator`] can allocate, re-allocate and free memory buffer described
+/// via [`Layout`].
+///
+/// [`Allocator`] is designed to be implemented as a ZST; [`Allocator`] functions do not operate on
+/// an object instance.
+///
+/// In order to be able to support `#[derive(SmartPointer)]` later on, we need to avoid a design
+/// that requires an `Allocator` to be instantiated, hence its functions must not contain any kind
+/// of `self` parameter.
+///
+/// # Safety
+///
+/// Memory returned from an allocator must point to a valid memory buffer and remain valid until
+/// it is explicitly freed.
+///
+/// Any pointer to a memory buffer which is currently allocated must be valid to be passed to any
+/// other [`Allocator`] function of the same type. The same applies for a NULL pointer.
+///
+/// If `realloc` is called with:
+///   - a size of zero, the given memory allocation, if any, must be freed
+///   - a NULL pointer, a new memory allocation must be created
+pub unsafe trait Allocator {
+    /// Allocate memory based on `layout` and `flags`.
+    ///
+    /// On success, returns a buffer represented as `NonNull<[u8]>` that satisfies the layout
+    /// constraints (i.e. minimum size and alignment as specified by `layout`).
+    ///
+    /// This function is equivalent to `realloc` when called with a NULL pointer.
+    fn alloc(layout: Layout, flags: Flags) -> Result<NonNull<[u8]>, AllocError> {
+        // SAFETY: Passing a NULL pointer to `realloc` is valid by it's safety requirements and asks
+        // for a new memory allocation.
+        unsafe { Self::realloc(None, layout, flags) }
+    }
+
+    /// Re-allocate an existing memory allocation to satisfy the requested `layout`. If the
+    /// requested size is zero, `realloc` behaves equivalent to `free`.
+    ///
+    /// If the requested size is larger than the size of the existing allocation, a successful call
+    /// to `realloc` guarantees that the new or grown buffer has at least `Layout::size` bytes, but
+    /// may also be larger.
+    ///
+    /// If the requested size is smaller than the size of the existing allocation, `realloc` may or
+    /// may not shrink the buffer; this is implementation specific to the allocator.
+    ///
+    /// On allocation failure, the existing buffer, if any, remains valid.
+    ///
+    /// The buffer is represented as `NonNull<[u8]>`.
+    ///
+    /// # Safety
+    ///
+    /// `Some(ptr)` must point to an existing and valid memory allocation created by this allocator
+    /// instance. The alignment encoded in `layout` must be smaller than or equal to the alignment
+    /// requested in the previous `alloc` or `realloc` call of the same allocation.
+    ///
+    /// Additionally, `ptr` is allowed to be `None`; in this case a new memory allocation is
+    /// created.
+    ///
+    unsafe fn realloc(
+        ptr: Option<NonNull<u8>>,
+        layout: Layout,
+        flags: Flags,
+    ) -> Result<NonNull<[u8]>, AllocError>;
+
+    /// Free an existing memory allocation.
+    ///
+    /// # Safety
+    ///
+    /// `ptr` must point to an existing and valid memory allocation created by this `Allocator`
+    /// instance.
+    unsafe fn free(ptr: NonNull<u8>) {
+        // SAFETY: `ptr` is guaranteed to be previously allocated with this `Allocator` or NULL.
+        // Calling `realloc` with a buffer size of zero, frees the buffer `ptr` points to.
+        let _ = unsafe { Self::realloc(Some(ptr), Layout::new::<()>(), Flags(0)) };
+    }
+}
-- 
2.45.2


^ permalink raw reply related	[flat|nested] 100+ messages in thread

* [PATCH v4 02/28] rust: alloc: separate `aligned_size` from `krealloc_aligned`
  2024-08-05 15:19 [PATCH v4 00/28] Generic `Allocator` support for Rust Danilo Krummrich
  2024-08-05 15:19 ` [PATCH v4 01/28] rust: alloc: add `Allocator` trait Danilo Krummrich
@ 2024-08-05 15:19 ` Danilo Krummrich
  2024-08-06 16:06   ` Benno Lossin
  2024-08-05 15:19 ` [PATCH v4 03/28] rust: alloc: rename `KernelAllocator` to `Kmalloc` Danilo Krummrich
                   ` (25 subsequent siblings)
  27 siblings, 1 reply; 100+ messages in thread
From: Danilo Krummrich @ 2024-08-05 15:19 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, acurrid, 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>
Signed-off-by: Danilo Krummrich <dakr@kernel.org>
---
 rust/kernel/alloc/allocator.rs | 27 ++++++++++++++++++---------
 1 file changed, 18 insertions(+), 9 deletions(-)

diff --git a/rust/kernel/alloc/allocator.rs b/rust/kernel/alloc/allocator.rs
index e6ea601f38c6..e7b7eba84acb 100644
--- a/rust/kernel/alloc/allocator.rs
+++ b/rust/kernel/alloc/allocator.rs
@@ -8,27 +8,36 @@
 
 struct KernelAllocator;
 
-/// Calls `krealloc` with a proper size to alloc a new object aligned to `new_layout`'s alignment.
-///
-/// # Safety
-///
-/// - `ptr` can be either null or a pointer which has been allocated by this allocator.
-/// - `new_layout` must have a non-zero size.
-pub(crate) unsafe fn krealloc_aligned(ptr: *mut u8, new_layout: Layout, flags: Flags) -> *mut u8 {
+/// Returns a proper size to alloc a new object aligned to `new_layout`'s alignment.
+fn aligned_size(new_layout: Layout) -> usize {
     // Customized layouts from `Layout::from_size_align()` can have size < align, so pad first.
     let layout = new_layout.pad_to_align();
 
     // Note that `layout.size()` (after padding) is guaranteed to be a multiple of `layout.align()`
     // which together with the slab guarantees means the `krealloc` will return a properly aligned
     // object (see comments in `kmalloc()` for more information).
-    let size = layout.size();
+    layout.size()
+}
 
+/// Calls `krealloc` with a proper size to alloc a new object aligned to `new_layout`'s alignment.
+///
+/// # Safety
+///
+/// - `ptr` can be either null or a pointer which has been allocated by this allocator.
+/// - `new_layout` must have a non-zero size.
+pub(crate) unsafe fn krealloc_aligned(ptr: *mut u8, new_layout: Layout, flags: Flags) -> *mut u8 {
     // SAFETY:
     // - `ptr` is either null or a pointer returned from a previous `k{re}alloc()` by the
     //   function safety requirement.
     // - `size` is greater than 0 since it's from `layout.size()` (which cannot be zero according
     //   to the function safety requirement)
-    unsafe { bindings::krealloc(ptr as *const core::ffi::c_void, size, flags.0) as *mut u8 }
+    unsafe {
+        bindings::krealloc(
+            ptr as *const core::ffi::c_void,
+            aligned_size(new_layout),
+            flags.0,
+        ) as *mut u8
+    }
 }
 
 unsafe impl GlobalAlloc for KernelAllocator {
-- 
2.45.2


^ permalink raw reply related	[flat|nested] 100+ messages in thread

* [PATCH v4 03/28] rust: alloc: rename `KernelAllocator` to `Kmalloc`
  2024-08-05 15:19 [PATCH v4 00/28] Generic `Allocator` support for Rust Danilo Krummrich
  2024-08-05 15:19 ` [PATCH v4 01/28] rust: alloc: add `Allocator` trait Danilo Krummrich
  2024-08-05 15:19 ` [PATCH v4 02/28] rust: alloc: separate `aligned_size` from `krealloc_aligned` Danilo Krummrich
@ 2024-08-05 15:19 ` Danilo Krummrich
  2024-08-06 16:07   ` Benno Lossin
  2024-08-07 18:22   ` Gary Guo
  2024-08-05 15:19 ` [PATCH v4 04/28] rust: alloc: implement `Allocator` for `Kmalloc` Danilo Krummrich
                   ` (24 subsequent siblings)
  27 siblings, 2 replies; 100+ messages in thread
From: Danilo Krummrich @ 2024-08-05 15:19 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, acurrid, 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>
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 e7b7eba84acb..2c1eae25da84 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 {
@@ -40,7 +40,7 @@ pub(crate) unsafe fn krealloc_aligned(ptr: *mut u8, new_layout: Layout, flags: F
     }
 }
 
-unsafe impl GlobalAlloc for KernelAllocator {
+unsafe impl GlobalAlloc for Kmalloc {
     unsafe fn alloc(&self, layout: Layout) -> *mut u8 {
         // SAFETY: `ptr::null_mut()` is null and `layout` has a non-zero size by the function safety
         // requirement.
@@ -76,7 +76,7 @@ unsafe fn alloc_zeroed(&self, layout: Layout) -> *mut u8 {
 }
 
 #[global_allocator]
-static ALLOCATOR: KernelAllocator = KernelAllocator;
+static ALLOCATOR: Kmalloc = Kmalloc;
 
 // See <https://github.com/rust-lang/rust/pull/86844>.
 #[no_mangle]
-- 
2.45.2


^ permalink raw reply related	[flat|nested] 100+ messages in thread

* [PATCH v4 04/28] rust: alloc: implement `Allocator` for `Kmalloc`
  2024-08-05 15:19 [PATCH v4 00/28] Generic `Allocator` support for Rust Danilo Krummrich
                   ` (2 preceding siblings ...)
  2024-08-05 15:19 ` [PATCH v4 03/28] rust: alloc: rename `KernelAllocator` to `Kmalloc` Danilo Krummrich
@ 2024-08-05 15:19 ` Danilo Krummrich
  2024-08-06 16:51   ` Benno Lossin
  2024-08-05 15:19 ` [PATCH v4 05/28] rust: alloc: add module `allocator_test` Danilo Krummrich
                   ` (23 subsequent siblings)
  27 siblings, 1 reply; 100+ messages in thread
From: Danilo Krummrich @ 2024-08-05 15:19 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, acurrid, 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/helpers.c                 |  3 +-
 rust/kernel/alloc.rs           |  2 +-
 rust/kernel/alloc/allocator.rs | 68 +++++++++++++++++++++++++++++++++-
 3 files changed, 69 insertions(+), 4 deletions(-)

diff --git a/rust/helpers.c b/rust/helpers.c
index 92d3c03ae1bd..9f7275493365 100644
--- a/rust/helpers.c
+++ b/rust/helpers.c
@@ -193,8 +193,7 @@ void rust_helper_init_work_with_key(struct work_struct *work, work_func_t func,
 }
 EXPORT_SYMBOL_GPL(rust_helper_init_work_with_key);
 
-void * __must_check __realloc_size(2)
-rust_helper_krealloc(const void *objp, size_t new_size, gfp_t flags)
+void *rust_helper_krealloc(const void *objp, size_t new_size, gfp_t flags)
 {
 	return krealloc(objp, new_size, flags);
 }
diff --git a/rust/kernel/alloc.rs b/rust/kernel/alloc.rs
index 8a71a589469d..bc01a17df5e0 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 2c1eae25da84..c6ad1dd59dd0 100644
--- a/rust/kernel/alloc/allocator.rs
+++ b/rust/kernel/alloc/allocator.rs
@@ -5,8 +5,16 @@
 use super::{flags::*, Flags};
 use core::alloc::{GlobalAlloc, Layout};
 use core::ptr;
+use core::ptr::NonNull;
 
-struct Kmalloc;
+use crate::alloc::{AllocError, Allocator};
+use crate::bindings;
+
+/// The contiguous kernel allocator.
+///
+/// The contiguous kernel allocator only ever allocates physically contiguous memory through
+/// `bindings::krealloc`.
+pub struct Kmalloc;
 
 /// Returns a proper size to alloc a new object aligned to `new_layout`'s alignment.
 fn aligned_size(new_layout: Layout) -> usize {
@@ -40,6 +48,64 @@ pub(crate) unsafe fn krealloc_aligned(ptr: *mut u8, new_layout: Layout, flags: F
     }
 }
 
+/// # 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.
+    fn krealloc() -> Self {
+        Self(bindings::krealloc)
+    }
+
+    /// # Safety
+    ///
+    /// This method has the exact same safety requirements as `Allocator::realloc`.
+    unsafe fn call(
+        &self,
+        ptr: Option<NonNull<u8>>,
+        layout: Layout,
+        flags: Flags,
+    ) -> Result<NonNull<[u8]>, AllocError> {
+        let size = aligned_size(layout);
+        let ptr = match ptr {
+            Some(ptr) => ptr.as_ptr(),
+            None => ptr::null(),
+        };
+
+        // SAFETY: `ptr` is valid by the safety requirements of this function.
+        let raw_ptr = unsafe {
+            // If `size == 0` and `ptr != NULL` the memory behind the pointer is freed.
+            self.0(ptr.cast(), size, flags.0).cast()
+        };
+
+        let ptr = if size == 0 {
+            NonNull::dangling()
+        } else {
+            NonNull::new(raw_ptr).ok_or(AllocError)?
+        };
+
+        Ok(NonNull::slice_from_raw_parts(ptr, size))
+    }
+}
+
+unsafe impl Allocator for Kmalloc {
+    unsafe fn realloc(
+        ptr: Option<NonNull<u8>>,
+        layout: Layout,
+        flags: Flags,
+    ) -> Result<NonNull<[u8]>, AllocError> {
+        let realloc = ReallocFunc::krealloc();
+
+        // SAFETY: If not `None`, `ptr` is guaranteed to point to valid memory, which was previously
+        // allocated with this `Allocator`.
+        unsafe { realloc.call(ptr, layout, flags) }
+    }
+}
+
 unsafe impl GlobalAlloc for Kmalloc {
     unsafe fn alloc(&self, layout: Layout) -> *mut u8 {
         // SAFETY: `ptr::null_mut()` is null and `layout` has a non-zero size by the function safety
-- 
2.45.2


^ permalink raw reply related	[flat|nested] 100+ messages in thread

* [PATCH v4 05/28] rust: alloc: add module `allocator_test`
  2024-08-05 15:19 [PATCH v4 00/28] Generic `Allocator` support for Rust Danilo Krummrich
                   ` (3 preceding siblings ...)
  2024-08-05 15:19 ` [PATCH v4 04/28] rust: alloc: implement `Allocator` for `Kmalloc` Danilo Krummrich
@ 2024-08-05 15:19 ` Danilo Krummrich
  2024-08-06 16:54   ` Benno Lossin
  2024-08-05 15:19 ` [PATCH v4 06/28] rust: alloc: implement `Vmalloc` allocator Danilo Krummrich
                   ` (22 subsequent siblings)
  27 siblings, 1 reply; 100+ messages in thread
From: Danilo Krummrich @ 2024-08-05 15:19 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, acurrid, 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>
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 bc01a17df5e0..942e2755f217 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.45.2


^ permalink raw reply related	[flat|nested] 100+ messages in thread

* [PATCH v4 06/28] rust: alloc: implement `Vmalloc` allocator
  2024-08-05 15:19 [PATCH v4 00/28] Generic `Allocator` support for Rust Danilo Krummrich
                   ` (4 preceding siblings ...)
  2024-08-05 15:19 ` [PATCH v4 05/28] rust: alloc: add module `allocator_test` Danilo Krummrich
@ 2024-08-05 15:19 ` Danilo Krummrich
  2024-08-06 17:00   ` Benno Lossin
  2024-08-05 15:19 ` [PATCH v4 07/28] rust: alloc: implement `KVmalloc` allocator Danilo Krummrich
                   ` (21 subsequent siblings)
  27 siblings, 1 reply; 100+ messages in thread
From: Danilo Krummrich @ 2024-08-05 15:19 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, acurrid, 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.c                      |  7 +++++++
 rust/kernel/alloc/allocator.rs      | 32 +++++++++++++++++++++++++++++
 rust/kernel/alloc/allocator_test.rs |  1 +
 3 files changed, 40 insertions(+)

diff --git a/rust/helpers.c b/rust/helpers.c
index 9f7275493365..7406943f887d 100644
--- a/rust/helpers.c
+++ b/rust/helpers.c
@@ -33,6 +33,7 @@
 #include <linux/sched/signal.h>
 #include <linux/slab.h>
 #include <linux/spinlock.h>
+#include <linux/vmalloc.h>
 #include <linux/wait.h>
 #include <linux/workqueue.h>
 
@@ -199,6 +200,12 @@ void *rust_helper_krealloc(const void *objp, size_t new_size, gfp_t flags)
 }
 EXPORT_SYMBOL_GPL(rust_helper_krealloc);
 
+void *rust_helper_vrealloc(const void *p, size_t size, gfp_t flags)
+{
+	return vrealloc(p, size, flags);
+}
+EXPORT_SYMBOL_GPL(rust_helper_vrealloc);
+
 /*
  * `bindgen` binds the C `size_t` type as the Rust `usize` type, so we can
  * use it in contexts where Rust expects a `usize` like slice (array) indices.
diff --git a/rust/kernel/alloc/allocator.rs b/rust/kernel/alloc/allocator.rs
index c6ad1dd59dd0..bb55895cbd03 100644
--- a/rust/kernel/alloc/allocator.rs
+++ b/rust/kernel/alloc/allocator.rs
@@ -9,6 +9,7 @@
 
 use crate::alloc::{AllocError, Allocator};
 use crate::bindings;
+use crate::pr_warn;
 
 /// The contiguous kernel allocator.
 ///
@@ -16,6 +17,12 @@
 /// `bindings::krealloc`.
 pub struct Kmalloc;
 
+/// The virtually contiguous kernel allocator.
+///
+/// The vmalloc allocator allocates pages from the page level allocator and maps them into the
+/// contiguous kernel virtual space.
+pub struct Vmalloc;
+
 /// Returns a proper size to alloc a new object aligned to `new_layout`'s alignment.
 fn aligned_size(new_layout: Layout) -> usize {
     // Customized layouts from `Layout::from_size_align()` can have size < align, so pad first.
@@ -61,6 +68,11 @@ fn krealloc() -> Self {
         Self(bindings::krealloc)
     }
 
+    // INVARIANT: `vrealloc` satisfies the type invariants.
+    fn vrealloc() -> Self {
+        Self(bindings::vrealloc)
+    }
+
     /// # Safety
     ///
     /// This method has the exact same safety requirements as `Allocator::realloc`.
@@ -141,6 +153,26 @@ unsafe fn alloc_zeroed(&self, layout: Layout) -> *mut u8 {
     }
 }
 
+unsafe impl Allocator for Vmalloc {
+    unsafe fn realloc(
+        ptr: Option<NonNull<u8>>,
+        layout: Layout,
+        flags: Flags,
+    ) -> Result<NonNull<[u8]>, AllocError> {
+        let realloc = ReallocFunc::vrealloc();
+
+        // TODO: Support alignments larger than PAGE_SIZE.
+        if layout.align() > bindings::PAGE_SIZE {
+            pr_warn!("Vmalloc does not support alignments larger than PAGE_SIZE yet.\n");
+            return Err(AllocError);
+        }
+
+        // SAFETY: If not `None`, `ptr` is guaranteed to point to valid memory, which was previously
+        // allocated with this `Allocator`.
+        unsafe { realloc.call(ptr, layout, flags) }
+    }
+}
+
 #[global_allocator]
 static ALLOCATOR: Kmalloc = Kmalloc;
 
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.45.2


^ permalink raw reply related	[flat|nested] 100+ messages in thread

* [PATCH v4 07/28] rust: alloc: implement `KVmalloc` allocator
  2024-08-05 15:19 [PATCH v4 00/28] Generic `Allocator` support for Rust Danilo Krummrich
                   ` (5 preceding siblings ...)
  2024-08-05 15:19 ` [PATCH v4 06/28] rust: alloc: implement `Vmalloc` allocator Danilo Krummrich
@ 2024-08-05 15:19 ` Danilo Krummrich
  2024-08-05 15:19 ` [PATCH v4 08/28] rust: types: implement `Unique<T>` Danilo Krummrich
                   ` (20 subsequent siblings)
  27 siblings, 0 replies; 100+ messages in thread
From: Danilo Krummrich @ 2024-08-05 15:19 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, acurrid, 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.c                      |  6 ++++++
 rust/kernel/alloc/allocator.rs      | 31 +++++++++++++++++++++++++++++
 rust/kernel/alloc/allocator_test.rs |  1 +
 3 files changed, 38 insertions(+)

diff --git a/rust/helpers.c b/rust/helpers.c
index 7406943f887d..3285a85765ff 100644
--- a/rust/helpers.c
+++ b/rust/helpers.c
@@ -206,6 +206,12 @@ void *rust_helper_vrealloc(const void *p, size_t size, gfp_t flags)
 }
 EXPORT_SYMBOL_GPL(rust_helper_vrealloc);
 
+void *rust_helper_kvrealloc(const void *p, size_t size, gfp_t flags)
+{
+	return kvrealloc(p, size, flags);
+}
+EXPORT_SYMBOL_GPL(rust_helper_kvrealloc);
+
 /*
  * `bindgen` binds the C `size_t` type as the Rust `usize` type, so we can
  * use it in contexts where Rust expects a `usize` like slice (array) indices.
diff --git a/rust/kernel/alloc/allocator.rs b/rust/kernel/alloc/allocator.rs
index bb55895cbd03..91ff21372a8f 100644
--- a/rust/kernel/alloc/allocator.rs
+++ b/rust/kernel/alloc/allocator.rs
@@ -23,6 +23,12 @@
 /// contiguous kernel virtual space.
 pub struct Vmalloc;
 
+/// The kvmalloc kernel allocator.
+///
+/// Attempt to allocate physically contiguous memory, but upon failure, fall back to non-contiguous
+/// (vmalloc) allocation.
+pub struct KVmalloc;
+
 /// Returns a proper size to alloc a new object aligned to `new_layout`'s alignment.
 fn aligned_size(new_layout: Layout) -> usize {
     // Customized layouts from `Layout::from_size_align()` can have size < align, so pad first.
@@ -73,6 +79,11 @@ fn vrealloc() -> Self {
         Self(bindings::vrealloc)
     }
 
+    // INVARIANT: `vrealloc` satisfies the type invariants.
+    fn kvrealloc() -> Self {
+        Self(bindings::kvrealloc)
+    }
+
     /// # Safety
     ///
     /// This method has the exact same safety requirements as `Allocator::realloc`.
@@ -173,6 +184,26 @@ unsafe fn realloc(
     }
 }
 
+unsafe impl Allocator for KVmalloc {
+    unsafe fn realloc(
+        ptr: Option<NonNull<u8>>,
+        layout: Layout,
+        flags: Flags,
+    ) -> Result<NonNull<[u8]>, AllocError> {
+        let realloc = ReallocFunc::kvrealloc();
+
+        // TODO: Support alignments larger than PAGE_SIZE.
+        if layout.align() > bindings::PAGE_SIZE {
+            pr_warn!("KVmalloc does not support alignments larger than PAGE_SIZE yet.\n");
+            return Err(AllocError);
+        }
+
+        // SAFETY: If not `None`, `ptr` is guaranteed to point to valid memory, which was previously
+        // allocated with this `Allocator`.
+        unsafe { realloc.call(ptr, layout, flags) }
+    }
+}
+
 #[global_allocator]
 static ALLOCATOR: Kmalloc = Kmalloc;
 
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.45.2


^ permalink raw reply related	[flat|nested] 100+ messages in thread

* [PATCH v4 08/28] rust: types: implement `Unique<T>`
  2024-08-05 15:19 [PATCH v4 00/28] Generic `Allocator` support for Rust Danilo Krummrich
                   ` (6 preceding siblings ...)
  2024-08-05 15:19 ` [PATCH v4 07/28] rust: alloc: implement `KVmalloc` allocator Danilo Krummrich
@ 2024-08-05 15:19 ` Danilo Krummrich
  2024-08-06 17:22   ` Benno Lossin
  2024-08-05 15:19 ` [PATCH v4 09/28] rust: alloc: implement kernel `Box` Danilo Krummrich
                   ` (19 subsequent siblings)
  27 siblings, 1 reply; 100+ messages in thread
From: Danilo Krummrich @ 2024-08-05 15:19 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, acurrid, cjia, jhubbard, airlied, ajanulgu, lyude,
	linux-kernel, rust-for-linux, linux-mm, Danilo Krummrich

Implement the `Unique` type as a prerequisite for `Box` and `Vec`
introduced in subsequent patches.

`Unique` serves as wrapper around a `NonNull`, but indicates that the
possessor of this wrapper owns the referent.

This type already exists in Rust's core library, but, unfortunately, is
exposed as unstable API and hence shouldn't be used in the kernel.

This implementation of `Unique` is almost identical, but mostly stripped
down to the functionality we need for `Box` and `Vec`. Additionally, all
unstable features are removed and / or replaced by stable ones.

Reviewed-by: Alice Ryhl <aliceryhl@google.com>
Signed-off-by: Danilo Krummrich <dakr@kernel.org>
---
 rust/kernel/types.rs | 183 +++++++++++++++++++++++++++++++++++++++++++
 1 file changed, 183 insertions(+)

diff --git a/rust/kernel/types.rs b/rust/kernel/types.rs
index bd189d646adb..7cf89067b5fc 100644
--- a/rust/kernel/types.rs
+++ b/rust/kernel/types.rs
@@ -473,3 +473,186 @@ unsafe impl AsBytes for str {}
 // does not have any uninitialized portions either.
 unsafe impl<T: AsBytes> AsBytes for [T] {}
 unsafe impl<T: AsBytes, const N: usize> AsBytes for [T; N] {}
+
+/// A wrapper around a raw non-null `*mut T` that indicates that the possessor
+/// of this wrapper owns the referent. Useful for building abstractions like
+/// `Box<T>`, `Vec<T>`, `String`, and `HashMap<K, V>`.
+///
+/// Unlike `*mut T`, `Unique<T>` behaves "as if" it were an instance of `T`.
+/// It implements `Send`/`Sync` if `T` is `Send`/`Sync`. It also implies
+/// the kind of strong aliasing guarantees an instance of `T` can expect:
+/// the referent of the pointer should not be modified without a unique path to
+/// its owning Unique.
+///
+/// If you're uncertain of whether it's correct to use `Unique` for your purposes,
+/// consider using `NonNull`, which has weaker semantics.
+///
+/// Unlike `*mut T`, the pointer must always be non-null, even if the pointer
+/// is never dereferenced. This is so that enums may use this forbidden value
+/// as a discriminant -- `Option<Unique<T>>` has the same size as `Unique<T>`.
+/// However the pointer may still dangle if it isn't dereferenced.
+///
+/// Unlike `*mut T`, `Unique<T>` is covariant over `T`. This should always be correct
+/// for any type which upholds Unique's aliasing requirements.
+#[repr(transparent)]
+pub struct Unique<T: ?Sized> {
+    pointer: NonNull<T>,
+    // NOTE: this marker has no consequences for variance, but is necessary
+    // for dropck to understand that we logically own a `T`.
+    //
+    // For details, see:
+    // https://github.com/rust-lang/rfcs/blob/master/text/0769-sound-generic-drop.md#phantom-data
+    _marker: PhantomData<T>,
+}
+
+/// `Unique` pointers are `Send` if `T` is `Send` because the data they
+/// reference is unaliased. Note that this aliasing invariant is
+/// unenforced by the type system; the abstraction using the
+/// `Unique` must enforce it.
+unsafe impl<T: Send + ?Sized> Send for Unique<T> {}
+
+/// `Unique` pointers are `Sync` if `T` is `Sync` because the data they
+/// reference is unaliased. Note that this aliasing invariant is
+/// unenforced by the type system; the abstraction using the
+/// `Unique` must enforce it.
+unsafe impl<T: Sync + ?Sized> Sync for Unique<T> {}
+
+impl<T: Sized> Unique<T> {
+    /// Creates a new `Unique` that is dangling, but well-aligned.
+    ///
+    /// This is useful for initializing types which lazily allocate, like
+    /// `Vec::new` does.
+    ///
+    /// Note that the pointer value may potentially represent a valid pointer to
+    /// a `T`, which means this must not be used as a "not yet initialized"
+    /// sentinel value. Types that lazily allocate must track initialization by
+    /// some other means.
+    #[must_use]
+    #[inline]
+    pub const fn dangling() -> Self {
+        Unique {
+            pointer: NonNull::dangling(),
+            _marker: PhantomData,
+        }
+    }
+}
+
+impl<T: ?Sized> Unique<T> {
+    /// Creates a new `Unique`.
+    ///
+    /// # Safety
+    ///
+    /// `ptr` must be non-null.
+    #[inline]
+    pub const unsafe fn new_unchecked(ptr: *mut T) -> Self {
+        // SAFETY: the caller must guarantee that `ptr` is non-null.
+        unsafe {
+            Unique {
+                pointer: NonNull::new_unchecked(ptr),
+                _marker: PhantomData,
+            }
+        }
+    }
+
+    /// Creates a new `Unique` if `ptr` is non-null.
+    #[allow(clippy::manual_map)]
+    #[inline]
+    pub fn new(ptr: *mut T) -> Option<Self> {
+        if let Some(pointer) = NonNull::new(ptr) {
+            Some(Unique {
+                pointer,
+                _marker: PhantomData,
+            })
+        } else {
+            None
+        }
+    }
+
+    /// Acquires the underlying `*mut` pointer.
+    #[must_use = "`self` will be dropped if the result is not used"]
+    #[inline]
+    pub const fn as_ptr(self) -> *mut T {
+        self.pointer.as_ptr()
+    }
+
+    /// Dereferences the content.
+    ///
+    /// The resulting lifetime is bound to self so this behaves "as if"
+    /// it were actually an instance of T that is getting borrowed. If a longer
+    /// (unbound) lifetime is needed, use `&*my_ptr.as_ptr()`.
+    ///
+    /// # Safety
+    ///
+    /// Safety requirements for this function are inherited from [NonNull::as_ref].
+    ///
+    #[must_use]
+    #[inline]
+    pub const unsafe fn as_ref(&self) -> &T {
+        // SAFETY: the caller must guarantee that `self` meets all the
+        // requirements for a reference.
+        unsafe { self.pointer.as_ref() }
+    }
+
+    /// Mutably dereferences the content.
+    ///
+    /// The resulting lifetime is bound to self so this behaves "as if"
+    /// it were actually an instance of T that is getting borrowed. If a longer
+    /// (unbound) lifetime is needed, use `&mut *my_ptr.as_ptr()`.
+    ///
+    /// # Safety
+    ///
+    /// Safety requirements for this function are inherited from [NonNull::as_mut].
+    #[must_use]
+    #[inline]
+    pub unsafe fn as_mut(&mut self) -> &mut T {
+        // SAFETY: the caller must guarantee that `self` meets all the
+        // requirements for a mutable reference.
+        unsafe { self.pointer.as_mut() }
+    }
+
+    /// Casts to a pointer of another type.
+    #[must_use = "`self` will be dropped if the result is not used"]
+    #[inline]
+    pub fn cast<U>(self) -> Unique<U> {
+        Unique::from(self.pointer.cast())
+    }
+
+    /// Acquires the underlying `*mut` pointer.
+    #[must_use = "`self` will be dropped if the result is not used"]
+    #[inline]
+    pub const fn as_non_null(self) -> NonNull<T> {
+        self.pointer
+    }
+}
+
+impl<T: ?Sized> Clone for Unique<T> {
+    #[inline]
+    fn clone(&self) -> Self {
+        *self
+    }
+}
+
+impl<T: ?Sized> Copy for Unique<T> {}
+
+impl<T: ?Sized> From<&mut T> for Unique<T> {
+    /// Converts a `&mut T` to a `Unique<T>`.
+    ///
+    /// This conversion is infallible since references cannot be null.
+    #[inline]
+    fn from(reference: &mut T) -> Self {
+        Self::from(NonNull::from(reference))
+    }
+}
+
+impl<T: ?Sized> From<NonNull<T>> for Unique<T> {
+    /// Converts a `NonNull<T>` to a `Unique<T>`.
+    ///
+    /// This conversion is infallible since `NonNull` cannot be null.
+    #[inline]
+    fn from(pointer: NonNull<T>) -> Self {
+        Unique {
+            pointer,
+            _marker: PhantomData,
+        }
+    }
+}
-- 
2.45.2


^ permalink raw reply related	[flat|nested] 100+ messages in thread

* [PATCH v4 09/28] rust: alloc: implement kernel `Box`
  2024-08-05 15:19 [PATCH v4 00/28] Generic `Allocator` support for Rust Danilo Krummrich
                   ` (7 preceding siblings ...)
  2024-08-05 15:19 ` [PATCH v4 08/28] rust: types: implement `Unique<T>` Danilo Krummrich
@ 2024-08-05 15:19 ` Danilo Krummrich
  2024-08-06 19:47   ` Benno Lossin
  2024-08-05 15:19 ` [PATCH v4 10/28] rust: treewide: switch to our kernel `Box` type Danilo Krummrich
                   ` (18 subsequent siblings)
  27 siblings, 1 reply; 100+ messages in thread
From: Danilo Krummrich @ 2024-08-05 15:19 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, acurrid, 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 | 330 ++++++++++++++++++++++++++++++++++++++
 rust/kernel/init.rs       |  35 +++-
 rust/kernel/prelude.rs    |   2 +-
 rust/kernel/types.rs      |  56 +++++++
 5 files changed, 427 insertions(+), 2 deletions(-)
 create mode 100644 rust/kernel/alloc/kbox.rs

diff --git a/rust/kernel/alloc.rs b/rust/kernel/alloc.rs
index 942e2755f217..d7beaf0372af 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..4a4379980745
--- /dev/null
+++ b/rust/kernel/alloc/kbox.rs
@@ -0,0 +1,330 @@
+// SPDX-License-Identifier: GPL-2.0
+
+//! Implementation of [`Box`].
+
+use super::{AllocError, Allocator, Flags};
+use core::fmt;
+use core::marker::PhantomData;
+use core::mem::ManuallyDrop;
+use core::mem::MaybeUninit;
+use core::ops::{Deref, DerefMut};
+use core::pin::Pin;
+use core::result::Result;
+
+use crate::types::Unique;
+
+/// The kernel's [`Box`] type.
+///
+/// `Box` provides the simplest way to allocate memory for a generic type with one of the kernel's
+/// allocators, e.g. `Kmalloc`, `Vmalloc` or `KVmalloc`.
+///
+/// For non-zero-sized values, a [`Box`] will use the given allocator `A` for its allocation. For
+/// the most common allocators the type aliases `KBox`, `VBox` and `KVBox` exist.
+///
+/// It is valid to convert both ways between a [`Box`] and a raw pointer allocated with any
+/// `Allocator`, given that the `Layout` used with the allocator is correct for the type.
+///
+/// For zero-sized values the [`Box`]' pointer must be `dangling_mut::<T>`; no memory is allocated.
+///
+/// So long as `T: Sized`, a `Box<T>` is guaranteed to be represented as a single pointer and is
+/// also ABI-compatible with C pointers (i.e. the C type `T*`).
+///
+/// # Invariants
+///
+/// The [`Box`]' pointer always properly aligned and either points to memory allocated with `A` or,
+/// for zero-sized types, is a dangling pointer.
+///
+/// # Examples
+///
+/// ```
+/// let b = KBox::<u64>::new(24_u64, GFP_KERNEL)?;
+///
+/// assert_eq!(*b, 24_u64);
+///
+/// # Ok::<(), Error>(())
+/// ```
+///
+/// ```
+/// struct Huge([u8; 1 << 24]);
+///
+/// assert!(KVBox::<Huge>::new_uninit(GFP_KERNEL).is_ok());
+/// ```
+pub struct Box<T: ?Sized, A: Allocator>(Unique<T>, PhantomData<A>);
+
+/// Type alias for `Box` with a `Kmalloc` allocator.
+///
+/// # Examples
+///
+/// ```
+/// let b = KBox::new(24_u64, GFP_KERNEL)?;
+///
+/// assert_eq!(*b, 24_u64);
+///
+/// # Ok::<(), Error>(())
+/// ```
+pub type KBox<T> = Box<T, super::allocator::Kmalloc>;
+
+/// Type alias for `Box` with a `Vmalloc` allocator.
+///
+/// # Examples
+///
+/// ```
+/// let b = VBox::new(24_u64, GFP_KERNEL)?;
+///
+/// assert_eq!(*b, 24_u64);
+///
+/// # Ok::<(), Error>(())
+/// ```
+pub type VBox<T> = Box<T, super::allocator::Vmalloc>;
+
+/// Type alias for `Box` with a `KVmalloc` allocator.
+///
+/// # Examples
+///
+/// ```
+/// let b = KVBox::new(24_u64, GFP_KERNEL)?;
+///
+/// assert_eq!(*b, 24_u64);
+///
+/// # Ok::<(), Error>(())
+/// ```
+pub type KVBox<T> = Box<T, super::allocator::KVmalloc>;
+
+impl<T, A> Box<T, A>
+where
+    T: ?Sized,
+    A: Allocator,
+{
+    /// Constructs a `Box<T, A>` from a raw pointer.
+    ///
+    /// # Safety
+    ///
+    /// `raw` must point to valid memory, previously allocated with `A`, and at least the size of
+    /// type `T`.
+    #[inline]
+    pub const unsafe fn from_raw(raw: *mut T) -> Self {
+        // SAFETY: Validity of `raw` is guaranteed by the safety preconditions of this function.
+        Self(unsafe { Unique::new_unchecked(raw) }, PhantomData::<A>)
+    }
+
+    /// Consumes the `Box<T>`, returning a wrapped raw pointer.
+    ///
+    /// # 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>`, returning a mutable reference, &'a mut T.
+    #[inline]
+    pub fn leak<'a>(b: Self) -> &'a mut T
+    where
+        T: 'a,
+    {
+        // SAFETY: `Box::into_raw` always returns a properly aligned and dereferenceable pointer
+        // which points to an initialized instance of `T`.
+        unsafe { &mut *Box::into_raw(b) }
+    }
+
+    /// Converts a `Box<T, A>` into a `Pin<Box<T, A>>`.
+    #[inline]
+    pub fn into_pin(b: Self) -> Pin<Self>
+    where
+        A: 'static,
+    {
+        // SAFETY: It's not possible to move or replace the insides of a `Pin<Box<T, A>>` when
+        // `T: !Unpin`, so it's safe to pin it directly without any additional requirements.
+        unsafe { Pin::new_unchecked(b) }
+    }
+}
+
+impl<T, A> Box<MaybeUninit<T>, A>
+where
+    A: Allocator,
+{
+    /// Converts to `Box<T, A>`.
+    ///
+    /// # Safety
+    ///
+    /// As with MaybeUninit::assume_init, it is up to the caller to guarantee that the value really
+    /// is in an initialized state. Calling this when the content is not yet fully initialized
+    /// causes immediate undefined behavior.
+    pub unsafe fn assume_init(b: Self) -> Box<T, A> {
+        let raw = Self::into_raw(b);
+        // SAFETY: Reconstruct the `Box<MaybeUninit<T>, A>` as Box<T, A> now that has been
+        // initialized. `raw` and `alloc` are safe by the invariants of `Box`.
+        unsafe { Box::from_raw(raw as *mut T) }
+    }
+
+    /// Writes the value and converts to `Box<T, A>`.
+    pub fn write(mut b: Self, value: T) -> Box<T, A> {
+        (*b).write(value);
+        // SAFETY: We've just initialized `boxed`'s value.
+        unsafe { Self::assume_init(b) }
+    }
+}
+
+impl<T, A> Box<T, A>
+where
+    A: Allocator,
+{
+    fn is_zst() -> bool {
+        core::mem::size_of::<T>() == 0
+    }
+
+    /// Allocates memory with the allocator `A` and then places `x` into it.
+    ///
+    /// This doesn’t actually allocate if T is zero-sized.
+    pub fn new(x: T, flags: Flags) -> Result<Self, AllocError> {
+        let b = Self::new_uninit(flags)?;
+        Ok(Box::write(b, x))
+    }
+
+    /// Constructs a new `Box<T, A>` with uninitialized contents.
+    ///
+    /// # Examples
+    ///
+    /// ```
+    /// let b = KBox::<u64>::new_uninit(GFP_KERNEL)?;
+    /// let b = KBox::write(b, 24);
+    ///
+    /// assert_eq!(*b, 24_u64);
+    ///
+    /// # Ok::<(), Error>(())
+    /// ```
+    pub fn new_uninit(flags: Flags) -> Result<Box<MaybeUninit<T>, A>, AllocError> {
+        let ptr = if Self::is_zst() {
+            Unique::dangling()
+        } else {
+            let layout = core::alloc::Layout::new::<MaybeUninit<T>>();
+            let ptr = A::alloc(layout, flags)?;
+
+            ptr.cast().into()
+        };
+
+        Ok(Box(ptr, PhantomData::<A>))
+    }
+
+    /// Constructs a new `Pin<Box<T, A>>`. If `T` does not implement [`Unpin`], then `x` will be
+    /// pinned in memory and unable to be moved.
+    #[inline]
+    pub fn pin(x: T, flags: Flags) -> Result<Pin<Box<T, A>>, AllocError>
+    where
+        A: 'static,
+    {
+        Ok(Self::new(x, flags)?.into())
+    }
+
+    /// Drops the contents, but keeps the allocation.
+    ///
+    /// # Examples
+    ///
+    /// ```
+    /// let value = KBox::new([0; 32], GFP_KERNEL)?;
+    /// assert_eq!(*value, [0; 32]);
+    /// let value = KBox::drop_contents(value);
+    /// // Now we can re-use `value`:
+    /// let value = KBox::write(value, [1; 32]);
+    /// assert_eq!(*value, [1; 32]);
+    /// # Ok::<(), Error>(())
+    /// ```
+    pub fn drop_contents(this: Self) -> Box<MaybeUninit<T>, A> {
+        let ptr = Box::into_raw(this);
+        // SAFETY: `ptr` is valid, because it came from `Box::into_raw`.
+        unsafe { core::ptr::drop_in_place(ptr) };
+        // SAFETY: `ptr` is valid, because it came from `Box::into_raw`.
+        unsafe { Box::from_raw(ptr.cast()) }
+    }
+}
+
+impl<T, A> From<Box<T, A>> for Pin<Box<T, A>>
+where
+    T: ?Sized,
+    A: Allocator,
+    A: 'static,
+{
+    /// Converts a `Box<T>` into a `Pin<Box<T>>`. If `T` does not implement [`Unpin`], then
+    /// `*boxed` will be pinned in memory and unable to be moved.
+    ///
+    /// This conversion does not allocate on the heap and happens in place.
+    ///
+    /// This is also available via [`Box::into_pin`].
+    ///
+    /// Constructing and pinning a `Box` with <code><Pin<Box\<T>>>::from([Box::new]\(x))</code>
+    /// can also be written more concisely using <code>[Box::pin]\(x)</code>.
+    /// This `From` implementation is useful if you already have a `Box<T>`, or you are
+    /// constructing a (pinned) `Box` in a different way than with [`Box::new`].
+    fn from(b: Box<T, A>) -> Self {
+        Box::into_pin(b)
+    }
+}
+
+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 ptr = self.0.as_ptr();
+
+        // SAFETY: `ptr` is always properly aligned, dereferenceable and points to an initialized
+        // instance of `T`.
+        let size = unsafe { core::mem::size_of_val(&*ptr) };
+
+        // SAFETY: We need to drop `self.0` in place, before we free the backing memory.
+        unsafe { core::ptr::drop_in_place(ptr) };
+
+        if size != 0 {
+            // SAFETY: `ptr` was previously allocated with `A`.
+            unsafe { A::free(self.0.as_non_null().cast()) };
+        }
+    }
+}
diff --git a/rust/kernel/init.rs b/rust/kernel/init.rs
index 495c09ebe3a3..5fd7a0ffabd2 100644
--- a/rust/kernel/init.rs
+++ b/rust/kernel/init.rs
@@ -211,7 +211,7 @@
 //! [`pin_init!`]: crate::pin_init!
 
 use crate::{
-    alloc::{box_ext::BoxExt, AllocError, Flags},
+    alloc::{box_ext::BoxExt, AllocError, Allocator, Flags},
     error::{self, Error},
     sync::UniqueArc,
     types::{Opaque, ScopeGuard},
@@ -1178,6 +1178,39 @@ fn try_init<E>(init: impl Init<T, E>, flags: Flags) -> Result<Self, E>
     }
 }
 
+impl<T, A> InPlaceInit<T> for crate::alloc::Box<T, A>
+where
+    A: Allocator + 'static,
+{
+    #[inline]
+    fn try_pin_init<E>(init: impl PinInit<T, E>, flags: Flags) -> Result<Pin<Self>, E>
+    where
+        E: From<AllocError>,
+    {
+        let mut this = crate::alloc::Box::<_, A>::new_uninit(flags)?;
+        let slot = this.as_mut_ptr();
+        // SAFETY: When init errors/panics, slot will get deallocated but not dropped,
+        // slot is valid and will not be moved, because we pin it later.
+        unsafe { init.__pinned_init(slot)? };
+        // SAFETY: All fields have been initialized.
+        Ok(unsafe { crate::alloc::Box::assume_init(this) }.into())
+    }
+
+    #[inline]
+    fn try_init<E>(init: impl Init<T, E>, flags: Flags) -> Result<Self, E>
+    where
+        E: From<AllocError>,
+    {
+        let mut this = crate::alloc::Box::<_, A>::new_uninit(flags)?;
+        let slot = this.as_mut_ptr();
+        // SAFETY: When init errors/panics, slot will get deallocated but not dropped,
+        // slot is valid.
+        unsafe { init.__init(slot)? };
+        // SAFETY: All fields have been initialized.
+        Ok(unsafe { crate::alloc::Box::assume_init(this) })
+    }
+}
+
 impl<T> InPlaceInit<T> for UniqueArc<T> {
     #[inline]
     fn try_pin_init<E>(init: impl PinInit<T, E>, flags: Flags) -> Result<Pin<Self>, E>
diff --git a/rust/kernel/prelude.rs b/rust/kernel/prelude.rs
index b37a0b3180fb..39f9331a48e2 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};
diff --git a/rust/kernel/types.rs b/rust/kernel/types.rs
index 7cf89067b5fc..9fe87528d129 100644
--- a/rust/kernel/types.rs
+++ b/rust/kernel/types.rs
@@ -2,6 +2,7 @@
 
 //! Kernel types.
 
+use crate::alloc::Allocator;
 use crate::init::{self, PinInit};
 use alloc::boxed::Box;
 use core::{
@@ -9,6 +10,7 @@
     marker::{PhantomData, PhantomPinned},
     mem::MaybeUninit,
     ops::{Deref, DerefMut},
+    pin::Pin,
     ptr::NonNull,
 };
 
@@ -89,6 +91,60 @@ unsafe fn from_foreign(ptr: *const core::ffi::c_void) -> Self {
     }
 }
 
+impl<T: 'static, A> ForeignOwnable for crate::alloc::Box<T, A>
+where
+    A: Allocator,
+{
+    type Borrowed<'a> = &'a T;
+
+    fn into_foreign(self) -> *const core::ffi::c_void {
+        crate::alloc::Box::into_raw(self) as _
+    }
+
+    unsafe fn borrow<'a>(ptr: *const core::ffi::c_void) -> &'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 { crate::alloc::Box::from_raw(ptr as _) }
+    }
+}
+
+impl<T: 'static, A> ForeignOwnable for Pin<crate::alloc::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.
+        crate::alloc::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(crate::alloc::Box::from_raw(ptr as _)) }
+    }
+}
+
 impl ForeignOwnable for () {
     type Borrowed<'a> = ();
 
-- 
2.45.2


^ permalink raw reply related	[flat|nested] 100+ messages in thread

* [PATCH v4 10/28] rust: treewide: switch to our kernel `Box` type
  2024-08-05 15:19 [PATCH v4 00/28] Generic `Allocator` support for Rust Danilo Krummrich
                   ` (8 preceding siblings ...)
  2024-08-05 15:19 ` [PATCH v4 09/28] rust: alloc: implement kernel `Box` Danilo Krummrich
@ 2024-08-05 15:19 ` Danilo Krummrich
  2024-08-07 12:42   ` Alice Ryhl
  2024-08-07 20:57   ` Benno Lossin
  2024-08-05 15:19 ` [PATCH v4 11/28] rust: alloc: remove `BoxExt` extension Danilo Krummrich
                   ` (17 subsequent siblings)
  27 siblings, 2 replies; 100+ messages in thread
From: Danilo Krummrich @ 2024-08-05 15:19 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, acurrid, 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.

Signed-off-by: Danilo Krummrich <dakr@kernel.org>
---
 rust/kernel/init.rs               | 41 ++++++++++++++++---------------
 rust/kernel/init/__internal.rs    |  2 +-
 rust/kernel/sync/arc.rs           | 17 ++++++-------
 rust/kernel/sync/condvar.rs       |  4 +--
 rust/kernel/sync/lock/mutex.rs    |  2 +-
 rust/kernel/sync/lock/spinlock.rs |  2 +-
 rust/kernel/workqueue.rs          | 20 +++++++--------
 7 files changed, 44 insertions(+), 44 deletions(-)

diff --git a/rust/kernel/init.rs b/rust/kernel/init.rs
index 5fd7a0ffabd2..b3159eb1eb02 100644
--- a/rust/kernel/init.rs
+++ b/rust/kernel/init.rs
@@ -68,7 +68,7 @@
 //! #     a <- new_mutex!(42, "Foo::a"),
 //! #     b: 24,
 //! # });
-//! let foo: Result<Pin<Box<Foo>>> = Box::pin_init(foo, GFP_KERNEL);
+//! let foo: Result<Pin<KBox<Foo>>> = KBox::pin_init(foo, GFP_KERNEL);
 //! ```
 //!
 //! For more information see the [`pin_init!`] macro.
@@ -93,14 +93,14 @@
 //! struct DriverData {
 //!     #[pin]
 //!     status: Mutex<i32>,
-//!     buffer: Box<[u8; 1_000_000]>,
+//!     buffer: KBox<[u8; 1_000_000]>,
 //! }
 //!
 //! impl DriverData {
 //!     fn new() -> impl PinInit<Self, Error> {
 //!         try_pin_init!(Self {
 //!             status <- new_mutex!(0, "DriverData::status"),
-//!             buffer: Box::init(kernel::init::zeroed(), GFP_KERNEL)?,
+//!             buffer: KBox::init(kernel::init::zeroed(), GFP_KERNEL)?,
 //!         })
 //!     }
 //! }
@@ -211,7 +211,7 @@
 //! [`pin_init!`]: crate::pin_init!
 
 use crate::{
-    alloc::{box_ext::BoxExt, AllocError, Allocator, Flags},
+    alloc::{box_ext::BoxExt, AllocError, Allocator, Flags, KBox},
     error::{self, Error},
     sync::UniqueArc,
     types::{Opaque, ScopeGuard},
@@ -297,7 +297,7 @@ macro_rules! stack_pin_init {
 /// struct Foo {
 ///     #[pin]
 ///     a: Mutex<usize>,
-///     b: Box<Bar>,
+///     b: KBox<Bar>,
 /// }
 ///
 /// struct Bar {
@@ -306,7 +306,7 @@ macro_rules! stack_pin_init {
 ///
 /// stack_try_pin_init!(let foo: Result<Pin<&mut Foo>, AllocError> = pin_init!(Foo {
 ///     a <- new_mutex!(42),
-///     b: Box::new(Bar {
+///     b: KBox::new(Bar {
 ///         x: 64,
 ///     }, GFP_KERNEL)?,
 /// }));
@@ -323,7 +323,7 @@ macro_rules! stack_pin_init {
 /// struct Foo {
 ///     #[pin]
 ///     a: Mutex<usize>,
-///     b: Box<Bar>,
+///     b: KBox<Bar>,
 /// }
 ///
 /// struct Bar {
@@ -332,7 +332,7 @@ macro_rules! stack_pin_init {
 ///
 /// stack_try_pin_init!(let foo: Pin<&mut Foo> =? pin_init!(Foo {
 ///     a <- new_mutex!(42),
-///     b: Box::new(Bar {
+///     b: KBox::new(Bar {
 ///         x: 64,
 ///     }, GFP_KERNEL)?,
 /// }));
@@ -391,7 +391,7 @@ macro_rules! stack_try_pin_init {
 ///     },
 /// });
 /// # initializer }
-/// # Box::pin_init(demo(), GFP_KERNEL).unwrap();
+/// # KBox::pin_init(demo(), GFP_KERNEL).unwrap();
 /// ```
 ///
 /// Arbitrary Rust expressions can be used to set the value of a variable.
@@ -461,7 +461,7 @@ macro_rules! stack_try_pin_init {
 /// #         })
 /// #     }
 /// # }
-/// let foo = Box::pin_init(Foo::new(), GFP_KERNEL);
+/// let foo = KBox::pin_init(Foo::new(), GFP_KERNEL);
 /// ```
 ///
 /// They can also easily embed it into their own `struct`s:
@@ -593,7 +593,7 @@ macro_rules! pin_init {
 /// use kernel::{init::{self, PinInit}, error::Error};
 /// #[pin_data]
 /// struct BigBuf {
-///     big: Box<[u8; 1024 * 1024 * 1024]>,
+///     big: KBox<[u8; 1024 * 1024 * 1024]>,
 ///     small: [u8; 1024 * 1024],
 ///     ptr: *mut u8,
 /// }
@@ -601,7 +601,7 @@ macro_rules! pin_init {
 /// impl BigBuf {
 ///     fn new() -> impl PinInit<Self, Error> {
 ///         try_pin_init!(Self {
-///             big: Box::init(init::zeroed(), GFP_KERNEL)?,
+///             big: KBox::init(init::zeroed(), GFP_KERNEL)?,
 ///             small: [0; 1024 * 1024],
 ///             ptr: core::ptr::null_mut(),
 ///         }? Error)
@@ -693,16 +693,16 @@ macro_rules! init {
 /// # Examples
 ///
 /// ```rust
-/// use kernel::{init::{PinInit, zeroed}, error::Error};
+/// use kernel::{alloc::KBox, init::{PinInit, zeroed}, error::Error};
 /// struct BigBuf {
-///     big: Box<[u8; 1024 * 1024 * 1024]>,
+///     big: KBox<[u8; 1024 * 1024 * 1024]>,
 ///     small: [u8; 1024 * 1024],
 /// }
 ///
 /// impl BigBuf {
 ///     fn new() -> impl Init<Self, Error> {
 ///         try_init!(Self {
-///             big: Box::init(zeroed(), GFP_KERNEL)?,
+///             big: KBox::init(zeroed(), GFP_KERNEL)?,
 ///             small: [0; 1024 * 1024],
 ///         }? Error)
 ///     }
@@ -825,7 +825,7 @@ fn pin_chain<F>(self, f: F) -> ChainPinInit<Self, F, T, E>
 }
 
 /// An initializer returned by [`PinInit::pin_chain`].
-pub struct ChainPinInit<I, F, T: ?Sized, E>(I, F, __internal::Invariant<(E, Box<T>)>);
+pub struct ChainPinInit<I, F, T: ?Sized, E>(I, F, __internal::Invariant<(E, KBox<T>)>);
 
 // SAFETY: The `__pinned_init` function is implemented such that it
 // - returns `Ok(())` on successful initialization,
@@ -924,7 +924,7 @@ fn chain<F>(self, f: F) -> ChainInit<Self, F, T, E>
 }
 
 /// An initializer returned by [`Init::chain`].
-pub struct ChainInit<I, F, T: ?Sized, E>(I, F, __internal::Invariant<(E, Box<T>)>);
+pub struct ChainInit<I, F, T: ?Sized, E>(I, F, __internal::Invariant<(E, KBox<T>)>);
 
 // SAFETY: The `__init` function is implemented such that it
 // - returns `Ok(())` on successful initialization,
@@ -1008,8 +1008,9 @@ pub fn uninit<T, E>() -> impl Init<MaybeUninit<T>, E> {
 /// # Examples
 ///
 /// ```rust
-/// use kernel::{error::Error, init::init_array_from_fn};
-/// let array: Box<[usize; 1_000]> = Box::init::<Error>(init_array_from_fn(|i| i), GFP_KERNEL).unwrap();
+/// use kernel::{alloc::KBox, error::Error, init::init_array_from_fn};
+/// let array: KBox<[usize; 1_000]> =
+///     KBox::init::<Error>(init_array_from_fn(|i| i), GFP_KERNEL).unwrap();
 /// assert_eq!(array.len(), 1_000);
 /// ```
 pub fn init_array_from_fn<I, const N: usize, T, E>(
@@ -1349,7 +1350,7 @@ macro_rules! impl_zeroable {
     //
     // In this case we are allowed to use `T: ?Sized`, since all zeros is the `None` variant.
     {<T: ?Sized>} Option<NonNull<T>>,
-    {<T: ?Sized>} Option<Box<T>>,
+    {<T: ?Sized>} Option<KBox<T>>,
 
     // SAFETY: `null` pointer is valid.
     //
diff --git a/rust/kernel/init/__internal.rs b/rust/kernel/init/__internal.rs
index db3372619ecd..dfb2204918c7 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/sync/arc.rs b/rust/kernel/sync/arc.rs
index 3673496c2363..b5c84995d7d2 100644
--- a/rust/kernel/sync/arc.rs
+++ b/rust/kernel/sync/arc.rs
@@ -16,13 +16,12 @@
 //! [`Arc`]: https://doc.rust-lang.org/std/sync/struct.Arc.html
 
 use crate::{
-    alloc::{box_ext::BoxExt, AllocError, Flags},
+    alloc::{AllocError, Flags, KBox},
     error::{self, Error},
     init::{self, InPlaceInit, Init, PinInit},
     try_init,
     types::{ForeignOwnable, Opaque},
 };
-use alloc::boxed::Box;
 use core::{
     alloc::Layout,
     fmt,
@@ -203,11 +202,11 @@ pub fn new(contents: T, flags: Flags) -> Result<Self, AllocError> {
             data: contents,
         };
 
-        let inner = <Box<_> as BoxExt<_>>::new(value, flags)?;
+        let inner = KBox::new(value, flags)?;
 
         // SAFETY: We just created `inner` with a reference count of 1, which is owned by the new
         // `Arc` object.
-        Ok(unsafe { Self::from_inner(Box::leak(inner).into()) })
+        Ok(unsafe { Self::from_inner(KBox::leak(inner).into()) })
     }
 
     /// Use the given initializer to in-place initialize a `T`.
@@ -422,8 +421,8 @@ fn drop(&mut self) {
         if is_zero {
             // The count reached zero, we must free the memory.
             //
-            // SAFETY: The pointer was initialised from the result of `Box::leak`.
-            unsafe { drop(Box::from_raw(self.ptr.as_ptr())) };
+            // SAFETY: The pointer was initialised from the result of `KBox::leak`.
+            unsafe { drop(KBox::from_raw(self.ptr.as_ptr())) };
         }
     }
 }
@@ -668,7 +667,7 @@ pub fn new(value: T, flags: Flags) -> Result<Self, AllocError> {
     /// Tries to allocate a new [`UniqueArc`] instance whose contents are not initialised yet.
     pub fn new_uninit(flags: Flags) -> Result<UniqueArc<MaybeUninit<T>>, AllocError> {
         // INVARIANT: The refcount is initialised to a non-zero value.
-        let inner = Box::try_init::<AllocError>(
+        let inner = KBox::try_init::<AllocError>(
             try_init!(ArcInner {
                 // SAFETY: There are no safety requirements for this FFI call.
                 refcount: Opaque::new(unsafe { bindings::REFCOUNT_INIT(1) }),
@@ -678,8 +677,8 @@ pub fn new_uninit(flags: Flags) -> Result<UniqueArc<MaybeUninit<T>>, AllocError>
         )?;
         Ok(UniqueArc {
             // INVARIANT: The newly-created object has a refcount of 1.
-            // SAFETY: The pointer from the `Box` is valid.
-            inner: unsafe { Arc::from_inner(Box::leak(inner).into()) },
+            // SAFETY: The pointer from the `KBox` is valid.
+            inner: unsafe { Arc::from_inner(KBox::leak(inner).into()) },
         })
     }
 }
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) };
-- 
2.45.2


^ permalink raw reply related	[flat|nested] 100+ messages in thread

* [PATCH v4 11/28] rust: alloc: remove `BoxExt` extension
  2024-08-05 15:19 [PATCH v4 00/28] Generic `Allocator` support for Rust Danilo Krummrich
                   ` (9 preceding siblings ...)
  2024-08-05 15:19 ` [PATCH v4 10/28] rust: treewide: switch to our kernel `Box` type Danilo Krummrich
@ 2024-08-05 15:19 ` Danilo Krummrich
  2024-08-08  6:48   ` Benno Lossin
  2024-08-05 15:19 ` [PATCH v4 12/28] rust: alloc: add `Box` to prelude Danilo Krummrich
                   ` (16 subsequent siblings)
  27 siblings, 1 reply; 100+ messages in thread
From: Danilo Krummrich @ 2024-08-05 15:19 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, acurrid, 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.

Reviewed-by: Alice Ryhl <aliceryhl@google.com>
Signed-off-by: Danilo Krummrich <dakr@kernel.org>
---
 rust/kernel/alloc.rs         |  1 -
 rust/kernel/alloc/box_ext.rs | 56 ------------------------------------
 rust/kernel/init.rs          | 34 +---------------------
 rust/kernel/lib.rs           |  1 -
 rust/kernel/prelude.rs       |  4 +--
 rust/kernel/types.rs         | 23 ---------------
 6 files changed, 3 insertions(+), 116 deletions(-)
 delete mode 100644 rust/kernel/alloc/box_ext.rs

diff --git a/rust/kernel/alloc.rs b/rust/kernel/alloc.rs
index d7beaf0372af..f578175ff010 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 829cb1c1cf9e..000000000000
--- a/rust/kernel/alloc/box_ext.rs
+++ /dev/null
@@ -1,56 +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;
-
-/// 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>;
-}
-
-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) })
-    }
-}
diff --git a/rust/kernel/init.rs b/rust/kernel/init.rs
index b3159eb1eb02..ae533305d40e 100644
--- a/rust/kernel/init.rs
+++ b/rust/kernel/init.rs
@@ -211,12 +211,11 @@
 //! [`pin_init!`]: crate::pin_init!
 
 use crate::{
-    alloc::{box_ext::BoxExt, AllocError, Allocator, Flags, KBox},
+    alloc::{AllocError, Allocator, Flags, KBox},
     error::{self, Error},
     sync::UniqueArc,
     types::{Opaque, ScopeGuard},
 };
-use alloc::boxed::Box;
 use core::{
     cell::UnsafeCell,
     convert::Infallible,
@@ -589,7 +588,6 @@ macro_rules! pin_init {
 /// # Examples
 ///
 /// ```rust
-/// # #![feature(new_uninit)]
 /// use kernel::{init::{self, PinInit}, error::Error};
 /// #[pin_data]
 /// struct BigBuf {
@@ -1149,36 +1147,6 @@ fn init<E>(init: impl Init<T, E>, flags: Flags) -> error::Result<Self>
     }
 }
 
-impl<T> InPlaceInit<T> for Box<T> {
-    #[inline]
-    fn try_pin_init<E>(init: impl PinInit<T, E>, flags: Flags) -> Result<Pin<Self>, E>
-    where
-        E: From<AllocError>,
-    {
-        let mut this = <Box<_> as BoxExt<_>>::new_uninit(flags)?;
-        let slot = this.as_mut_ptr();
-        // SAFETY: When init errors/panics, slot will get deallocated but not dropped,
-        // slot is valid and will not be moved, because we pin it later.
-        unsafe { init.__pinned_init(slot)? };
-        // SAFETY: All fields have been initialized.
-        Ok(unsafe { this.assume_init() }.into())
-    }
-
-    #[inline]
-    fn try_init<E>(init: impl Init<T, E>, flags: Flags) -> Result<Self, E>
-    where
-        E: From<AllocError>,
-    {
-        let mut this = <Box<_> as BoxExt<_>>::new_uninit(flags)?;
-        let slot = this.as_mut_ptr();
-        // SAFETY: When init errors/panics, slot will get deallocated but not dropped,
-        // slot is valid.
-        unsafe { init.__init(slot)? };
-        // SAFETY: All fields have been initialized.
-        Ok(unsafe { this.assume_init() })
-    }
-}
-
 impl<T, A> InPlaceInit<T> for crate::alloc::Box<T, A>
 where
     A: Allocator + 'static,
diff --git a/rust/kernel/lib.rs b/rust/kernel/lib.rs
index 274bdc1b0a82..042f05c45214 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 39f9331a48e2..a8018ef2e691 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 9fe87528d129..b80a3b2b62ac 100644
--- a/rust/kernel/types.rs
+++ b/rust/kernel/types.rs
@@ -4,7 +4,6 @@
 
 use crate::alloc::Allocator;
 use crate::init::{self, PinInit};
-use alloc::boxed::Box;
 use core::{
     cell::UnsafeCell,
     marker::{PhantomData, PhantomPinned},
@@ -69,28 +68,6 @@ unsafe fn try_from_foreign(ptr: *const core::ffi::c_void) -> Option<Self> {
     }
 }
 
-impl<T: 'static> ForeignOwnable for Box<T> {
-    type Borrowed<'a> = &'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, A> ForeignOwnable for crate::alloc::Box<T, A>
 where
     A: Allocator,
-- 
2.45.2


^ permalink raw reply related	[flat|nested] 100+ messages in thread

* [PATCH v4 12/28] rust: alloc: add `Box` to prelude
  2024-08-05 15:19 [PATCH v4 00/28] Generic `Allocator` support for Rust Danilo Krummrich
                   ` (10 preceding siblings ...)
  2024-08-05 15:19 ` [PATCH v4 11/28] rust: alloc: remove `BoxExt` extension Danilo Krummrich
@ 2024-08-05 15:19 ` Danilo Krummrich
  2024-08-08  6:49   ` Benno Lossin
  2024-08-05 15:19 ` [PATCH v4 13/28] rust: alloc: import kernel `Box` type in types.rs Danilo Krummrich
                   ` (15 subsequent siblings)
  27 siblings, 1 reply; 100+ messages in thread
From: Danilo Krummrich @ 2024-08-05 15:19 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, acurrid, 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>
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 a8018ef2e691..6bf77577eae7 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.45.2


^ permalink raw reply related	[flat|nested] 100+ messages in thread

* [PATCH v4 13/28] rust: alloc: import kernel `Box` type in types.rs
  2024-08-05 15:19 [PATCH v4 00/28] Generic `Allocator` support for Rust Danilo Krummrich
                   ` (11 preceding siblings ...)
  2024-08-05 15:19 ` [PATCH v4 12/28] rust: alloc: add `Box` to prelude Danilo Krummrich
@ 2024-08-05 15:19 ` Danilo Krummrich
  2024-08-05 15:19 ` [PATCH v4 14/28] rust: alloc: import kernel `Box` type in init.rs Danilo Krummrich
                   ` (14 subsequent siblings)
  27 siblings, 0 replies; 100+ messages in thread
From: Danilo Krummrich @ 2024-08-05 15:19 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, acurrid, cjia, jhubbard, airlied, ajanulgu, lyude,
	linux-kernel, rust-for-linux, linux-mm, Danilo Krummrich

Now that we removed `BoxExt` and the corresponding includes in
types.rs, add the new kernel `Box` type instead.

Reviewed-by: Alice Ryhl <aliceryhl@google.com>
Signed-off-by: Danilo Krummrich <dakr@kernel.org>
---
 rust/kernel/types.rs | 14 +++++++-------
 1 file changed, 7 insertions(+), 7 deletions(-)

diff --git a/rust/kernel/types.rs b/rust/kernel/types.rs
index b80a3b2b62ac..25761da5a816 100644
--- a/rust/kernel/types.rs
+++ b/rust/kernel/types.rs
@@ -2,7 +2,7 @@
 
 //! Kernel types.
 
-use crate::alloc::Allocator;
+use crate::alloc::{Allocator, Box};
 use crate::init::{self, PinInit};
 use core::{
     cell::UnsafeCell,
@@ -68,14 +68,14 @@ unsafe fn try_from_foreign(ptr: *const core::ffi::c_void) -> Option<Self> {
     }
 }
 
-impl<T: 'static, A> ForeignOwnable for crate::alloc::Box<T, A>
+impl<T: 'static, A> ForeignOwnable for Box<T, A>
 where
     A: Allocator,
 {
     type Borrowed<'a> = &'a T;
 
     fn into_foreign(self) -> *const core::ffi::c_void {
-        crate::alloc::Box::into_raw(self) as _
+        Box::into_raw(self) as _
     }
 
     unsafe fn borrow<'a>(ptr: *const core::ffi::c_void) -> &'a T {
@@ -89,11 +89,11 @@ unsafe fn borrow<'a>(ptr: *const core::ffi::c_void) -> &'a T {
     unsafe fn from_foreign(ptr: *const core::ffi::c_void) -> Self {
         // SAFETY: The safety requirements of this function ensure that `ptr` comes from a previous
         // call to `Self::into_foreign`.
-        unsafe { crate::alloc::Box::from_raw(ptr as _) }
+        unsafe { Box::from_raw(ptr as _) }
     }
 }
 
-impl<T: 'static, A> ForeignOwnable for Pin<crate::alloc::Box<T, A>>
+impl<T: 'static, A> ForeignOwnable for Pin<Box<T, A>>
 where
     A: Allocator,
 {
@@ -101,7 +101,7 @@ impl<T: 'static, A> ForeignOwnable for Pin<crate::alloc::Box<T, A>>
 
     fn into_foreign(self) -> *const core::ffi::c_void {
         // SAFETY: We are still treating the box as pinned.
-        crate::alloc::Box::into_raw(unsafe { Pin::into_inner_unchecked(self) }) as _
+        Box::into_raw(unsafe { Pin::into_inner_unchecked(self) }) as _
     }
 
     unsafe fn borrow<'a>(ptr: *const core::ffi::c_void) -> Pin<&'a T> {
@@ -118,7 +118,7 @@ unsafe fn borrow<'a>(ptr: *const core::ffi::c_void) -> Pin<&'a T> {
     unsafe fn from_foreign(ptr: *const core::ffi::c_void) -> Self {
         // SAFETY: The safety requirements of this function ensure that `ptr` comes from a previous
         // call to `Self::into_foreign`.
-        unsafe { Pin::new_unchecked(crate::alloc::Box::from_raw(ptr as _)) }
+        unsafe { Pin::new_unchecked(Box::from_raw(ptr as _)) }
     }
 }
 
-- 
2.45.2


^ permalink raw reply related	[flat|nested] 100+ messages in thread

* [PATCH v4 14/28] rust: alloc: import kernel `Box` type in init.rs
  2024-08-05 15:19 [PATCH v4 00/28] Generic `Allocator` support for Rust Danilo Krummrich
                   ` (12 preceding siblings ...)
  2024-08-05 15:19 ` [PATCH v4 13/28] rust: alloc: import kernel `Box` type in types.rs Danilo Krummrich
@ 2024-08-05 15:19 ` Danilo Krummrich
  2024-08-05 15:19 ` [PATCH v4 15/28] rust: alloc: implement kernel `Vec` type Danilo Krummrich
                   ` (13 subsequent siblings)
  27 siblings, 0 replies; 100+ messages in thread
From: Danilo Krummrich @ 2024-08-05 15:19 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, acurrid, cjia, jhubbard, airlied, ajanulgu, lyude,
	linux-kernel, rust-for-linux, linux-mm, Danilo Krummrich

Now that we removed `BoxExt` and the corresponding includes in
init.rs, add the new kernel `Box` type instead.

Reviewed-by: Alice Ryhl <aliceryhl@google.com>
Signed-off-by: Danilo Krummrich <dakr@kernel.org>
---
 rust/kernel/init.rs | 12 ++++++------
 1 file changed, 6 insertions(+), 6 deletions(-)

diff --git a/rust/kernel/init.rs b/rust/kernel/init.rs
index ae533305d40e..350582662964 100644
--- a/rust/kernel/init.rs
+++ b/rust/kernel/init.rs
@@ -211,7 +211,7 @@
 //! [`pin_init!`]: crate::pin_init!
 
 use crate::{
-    alloc::{AllocError, Allocator, Flags, KBox},
+    alloc::{AllocError, Allocator, Box, Flags, KBox},
     error::{self, Error},
     sync::UniqueArc,
     types::{Opaque, ScopeGuard},
@@ -1147,7 +1147,7 @@ fn init<E>(init: impl Init<T, E>, flags: Flags) -> error::Result<Self>
     }
 }
 
-impl<T, A> InPlaceInit<T> for crate::alloc::Box<T, A>
+impl<T, A> InPlaceInit<T> for Box<T, A>
 where
     A: Allocator + 'static,
 {
@@ -1156,13 +1156,13 @@ fn try_pin_init<E>(init: impl PinInit<T, E>, flags: Flags) -> Result<Pin<Self>,
     where
         E: From<AllocError>,
     {
-        let mut this = crate::alloc::Box::<_, A>::new_uninit(flags)?;
+        let mut this = Box::<_, A>::new_uninit(flags)?;
         let slot = this.as_mut_ptr();
         // SAFETY: When init errors/panics, slot will get deallocated but not dropped,
         // slot is valid and will not be moved, because we pin it later.
         unsafe { init.__pinned_init(slot)? };
         // SAFETY: All fields have been initialized.
-        Ok(unsafe { crate::alloc::Box::assume_init(this) }.into())
+        Ok(unsafe { Box::assume_init(this) }.into())
     }
 
     #[inline]
@@ -1170,13 +1170,13 @@ fn try_init<E>(init: impl Init<T, E>, flags: Flags) -> Result<Self, E>
     where
         E: From<AllocError>,
     {
-        let mut this = crate::alloc::Box::<_, A>::new_uninit(flags)?;
+        let mut this = Box::<_, A>::new_uninit(flags)?;
         let slot = this.as_mut_ptr();
         // SAFETY: When init errors/panics, slot will get deallocated but not dropped,
         // slot is valid.
         unsafe { init.__init(slot)? };
         // SAFETY: All fields have been initialized.
-        Ok(unsafe { crate::alloc::Box::assume_init(this) })
+        Ok(unsafe { Box::assume_init(this) })
     }
 }
 
-- 
2.45.2


^ permalink raw reply related	[flat|nested] 100+ messages in thread

* [PATCH v4 15/28] rust: alloc: implement kernel `Vec` type
  2024-08-05 15:19 [PATCH v4 00/28] Generic `Allocator` support for Rust Danilo Krummrich
                   ` (13 preceding siblings ...)
  2024-08-05 15:19 ` [PATCH v4 14/28] rust: alloc: import kernel `Box` type in init.rs Danilo Krummrich
@ 2024-08-05 15:19 ` Danilo Krummrich
  2024-08-05 15:19 ` [PATCH v4 16/28] rust: alloc: implement `IntoIterator` for `Vec` Danilo Krummrich
                   ` (12 subsequent siblings)
  27 siblings, 0 replies; 100+ messages in thread
From: Danilo Krummrich @ 2024-08-05 15:19 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, acurrid, cjia, jhubbard, airlied, ajanulgu, lyude,
	linux-kernel, rust-for-linux, linux-mm, Danilo Krummrich

`Vec` provides a contiguous growable array type (such as `Vec`) with
contents allocated with the kernel's allocators (e.g. `Kmalloc`,
`Vmalloc` or `KVmalloc`).

In contrast to Rust's `Vec` type, the kernel `Vec` type considers the
kernel's GFP flags for all appropriate functions, always reports
allocation failures through `Result<_, AllocError>` and remains
independent from unstable features.

Signed-off-by: Danilo Krummrich <dakr@kernel.org>
---
 rust/kernel/alloc.rs      |   6 +
 rust/kernel/alloc/kbox.rs |  16 +-
 rust/kernel/alloc/kvec.rs | 591 ++++++++++++++++++++++++++++++++++++++
 rust/kernel/prelude.rs    |   2 +-
 4 files changed, 613 insertions(+), 2 deletions(-)
 create mode 100644 rust/kernel/alloc/kvec.rs

diff --git a/rust/kernel/alloc.rs b/rust/kernel/alloc.rs
index f578175ff010..28c186906e1b 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/kbox.rs b/rust/kernel/alloc/kbox.rs
index 4a4379980745..02aae24edda8 100644
--- a/rust/kernel/alloc/kbox.rs
+++ b/rust/kernel/alloc/kbox.rs
@@ -2,7 +2,7 @@
 
 //! Implementation of [`Box`].
 
-use super::{AllocError, Allocator, Flags};
+use super::{AllocError, Allocator, Flags, Vec};
 use core::fmt;
 use core::marker::PhantomData;
 use core::mem::ManuallyDrop;
@@ -150,6 +150,20 @@ pub fn into_pin(b: Self) -> Pin<Self>
     }
 }
 
+impl<T, A, const N: usize> Box<[T; N], A>
+where
+    A: Allocator,
+{
+    /// Convert a `Box<[T, N], A>` to a `Vec<T, A>`.
+    pub fn into_vec(b: Self) -> Vec<T, A> {
+        let len = b.len();
+        unsafe {
+            let ptr = Self::into_raw(b);
+            Vec::from_raw_parts(ptr as _, len, len)
+        }
+    }
+}
+
 impl<T, A> Box<MaybeUninit<T>, A>
 where
     A: Allocator,
diff --git a/rust/kernel/alloc/kvec.rs b/rust/kernel/alloc/kvec.rs
new file mode 100644
index 000000000000..6cf62e7dd36f
--- /dev/null
+++ b/rust/kernel/alloc/kvec.rs
@@ -0,0 +1,591 @@
+// SPDX-License-Identifier: GPL-2.0
+
+//! Implementation of [`Vec`].
+
+use super::{AllocError, Allocator, Flags};
+use crate::types::Unique;
+use core::{
+    fmt,
+    marker::PhantomData,
+    mem::{ManuallyDrop, MaybeUninit},
+    ops::Deref,
+    ops::DerefMut,
+    ops::Index,
+    ops::IndexMut,
+    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([$($x),+], GFP_KERNEL) {
+                Ok(b) => Ok($crate::alloc::KBox::into_vec(b)),
+                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` 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
+///
+/// The [`Vec`] backing buffer's pointer always properly aligned and either points to memory
+/// allocated with `A` or, for zero-sized types, is a dangling pointer.
+///
+/// The length of the vector always represents the exact number of elements stored in the vector.
+///
+/// The capacity of the vector always 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` of the vector is the exact allocator the backing buffer was allocated with (and
+/// must be freed with).
+pub struct Vec<T, A: Allocator> {
+    ptr: Unique<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, super::allocator::Kmalloc>;
+
+/// Type alias for `Vec` with a `Vmalloc` allocator.
+///
+/// # Examples
+///
+/// ```
+/// let mut v = VVec::new();
+/// v.push(1, GFP_KERNEL)?;
+/// assert_eq!(&v, &[1]);
+///
+/// # Ok::<(), Error>(())
+/// ```
+pub type VVec<T> = Vec<T, super::allocator::Vmalloc>;
+
+/// Type alias for `Vec` with a `KVmalloc` allocator.
+///
+/// # Examples
+///
+/// ```
+/// let mut v = KVVec::new();
+/// v.push(1, GFP_KERNEL)?;
+/// assert_eq!(&v, &[1]);
+///
+/// # Ok::<(), Error>(())
+/// ```
+pub type KVVec<T> = Vec<T, super::allocator::KVmalloc>;
+
+impl<T, A> Vec<T, A>
+where
+    A: Allocator,
+{
+    #[inline]
+    fn is_zst() -> bool {
+        core::mem::size_of::<T>() == 0
+    }
+
+    /// Returns the total number of elements the vector can hold without
+    /// reallocating.
+    pub fn capacity(&self) -> usize {
+        if Self::is_zst() {
+            usize::MAX
+        } else {
+            self.cap
+        }
+    }
+
+    /// Returns the number of elements in the vector, also referred to
+    /// as its 'length'.
+    #[inline]
+    pub fn len(&self) -> usize {
+        self.len
+    }
+
+    /// Forces the length of the vector to new_len.
+    ///
+    /// # Safety
+    ///
+    /// - `new_len` must be less than or equal to [`Self::capacity()`].
+    /// - The elements at `old_len..new_len` must be initialized.
+    #[inline]
+    pub unsafe fn set_len(&mut self, new_len: usize) {
+        self.len = new_len;
+    }
+
+    /// Extracts a slice containing the entire vector.
+    ///
+    /// Equivalent to `&s[..]`.
+    #[inline]
+    pub fn as_slice(&self) -> &[T] {
+        self
+    }
+
+    /// Extracts a mutable slice of the entire vector.
+    ///
+    /// Equivalent to `&mut s[..]`.
+    #[inline]
+    pub fn as_mut_slice(&mut self) -> &mut [T] {
+        self
+    }
+
+    /// Returns an unsafe mutable pointer to the vector's buffer, or a dangling
+    /// raw pointer valid for zero sized reads if the vector didn't allocate.
+    #[inline]
+    pub fn as_mut_ptr(&self) -> *mut T {
+        self.ptr.as_ptr()
+    }
+
+    /// Returns a raw pointer to the slice's buffer.
+    #[inline]
+    pub fn as_ptr(&self) -> *const T {
+        self.as_mut_ptr()
+    }
+
+    /// Returns `true` if the vector contains no elements.
+    ///
+    /// # 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
+    }
+
+    /// Constructs a new, empty Vec<T, A>.
+    ///
+    /// This method does not allocate by itself.
+    #[inline]
+    pub const fn new() -> Self {
+        Self {
+            ptr: Unique::dangling(),
+            cap: 0,
+            len: 0,
+            _p: PhantomData::<A>,
+        }
+    }
+
+    /// Returns the remaining spare capacity of the vector as a slice of
+    /// `MaybeUninit<T>`.
+    pub fn spare_capacity_mut(&mut self) -> &mut [MaybeUninit<T>] {
+        // SAFETY: The memory between `self.len` and `self.capacity` is guaranteed to be allocated
+        // and valid, but uninitialized.
+        unsafe {
+            slice::from_raw_parts_mut(
+                self.as_mut_ptr().add(self.len) as *mut MaybeUninit<T>,
+                self.capacity() - self.len,
+            )
+        }
+    }
+
+    /// 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)?;
+        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(())
+    }
+
+    /// 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: 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(())
+    }
+
+    /// Creates a Vec<T, A> directly from a pointer, a length, a capacity, and an allocator.
+    ///
+    /// # Safety
+    ///
+    /// This is highly unsafe, due to the number of invariants that aren’t checked:
+    ///
+    /// - `ptr` must be currently allocated via the given allocator `A`.
+    /// - `T` needs to have the same alignment as what `ptr` was allocated with. (`T` having a less
+    ///   strict alignment is not sufficient, the alignment really needs to be equal to satisfy the
+    ///   `dealloc` requirement that memory must be allocated and deallocated with the same layout.)
+    /// - The size of `T` times the `capacity` (i.e. the allocated size in bytes) needs to be
+    ///   smaller or equal the size the pointer was allocated with.
+    /// - `length` needs to be less than or equal to `capacity`.
+    /// - The first `length` values must be properly initialized values of type `T`.
+    /// - The allocated size in bytes must be no larger than `isize::MAX`. See the safety
+    ///   documentation of `pointer::offset`.
+    ///
+    /// It is also valid to create an empty `Vec` passing a dangling pointer for `ptr` and zero for
+    /// `cap` and `len`.
+    ///
+    /// # 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>(())
+    /// ```
+    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 { Unique::new_unchecked(ptr) },
+            cap,
+            len: length,
+            _p: PhantomData::<A>,
+        }
+    }
+
+    /// Decomposes a `Vec<T, A>` into its raw components: (`pointer`, `length`,
+    /// `capacity`).
+    pub fn into_raw_parts(self) -> (*mut T, usize, usize) {
+        let me = ManuallyDrop::new(self);
+        let len = me.len();
+        let capacity = me.capacity();
+        let ptr = me.as_mut_ptr();
+        (ptr, len, capacity)
+    }
+
+    /// Ensures that the capacity exceeds the length by at least `additional`
+    /// elements.
+    ///
+    /// # Examples
+    ///
+    /// ```
+    /// let mut v = KVec::new();
+    /// v.push(1, GFP_KERNEL)?;
+    ///
+    /// v.reserve(10, GFP_KERNEL)?;
+    /// let cap = v.capacity();
+    /// assert!(cap >= 10);
+    ///
+    /// v.reserve(10, GFP_KERNEL)?;
+    /// let new_cap = v.capacity();
+    /// assert_eq!(new_cap, cap);
+    ///
+    /// # Ok::<(), Error>(())
+    /// ```
+    pub fn reserve(&mut self, additional: usize, flags: Flags) -> Result<(), AllocError> {
+        let len = self.len();
+        let cap = self.capacity();
+
+        if cap - len >= additional {
+            return Ok(());
+        }
+
+        if Self::is_zst() {
+            // The capacity is already `usize::MAX` for SZTs, we can't go higher.
+            return Err(AllocError);
+        }
+
+        // We know `cap` is <= `isize::MAX` because of it's type invariant. 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.as_non_null().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().into();
+
+        // 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> {
+        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.reserve` not bailing out with an error guarantees that we're not
+        // exceeding the capacity of this `Vec`.
+        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.as_non_null().cast()) };
+        }
+    }
+}
+
+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 $(where $ty:ty: $bound:ident)?) => {
+        impl<T, U, $($vars)*> PartialEq<$rhs> for $lhs
+        where
+            T: PartialEq<U>,
+            $($ty: $bound)?
+        {
+            #[inline]
+            fn eq(&self, other: &$rhs) -> bool { self[..] == other[..] }
+        }
+    }
+}
+
+__impl_slice_eq! { [A1: Allocator, A2: Allocator] Vec<T, A1>, Vec<U, A2> }
+__impl_slice_eq! { [A: Allocator] Vec<T, A>, &[U] }
+__impl_slice_eq! { [A: Allocator] Vec<T, A>, &mut [U] }
+__impl_slice_eq! { [A: Allocator] &[T], Vec<U, A> }
+__impl_slice_eq! { [A: Allocator] &mut [T], Vec<U, A> }
+__impl_slice_eq! { [A: Allocator] Vec<T, A>, [U] }
+__impl_slice_eq! { [A: Allocator] [T], Vec<U, A> }
+__impl_slice_eq! { [A: Allocator, const N: usize] Vec<T, A>, [U; N] }
+__impl_slice_eq! { [A: Allocator, const N: usize] Vec<T, A>, &[U; N] }
diff --git a/rust/kernel/prelude.rs b/rust/kernel/prelude.rs
index 6bf77577eae7..bb80a43d20fb 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.45.2


^ permalink raw reply related	[flat|nested] 100+ messages in thread

* [PATCH v4 16/28] rust: alloc: implement `IntoIterator` for `Vec`
  2024-08-05 15:19 [PATCH v4 00/28] Generic `Allocator` support for Rust Danilo Krummrich
                   ` (14 preceding siblings ...)
  2024-08-05 15:19 ` [PATCH v4 15/28] rust: alloc: implement kernel `Vec` type Danilo Krummrich
@ 2024-08-05 15:19 ` Danilo Krummrich
  2024-08-05 15:19 ` [PATCH v4 17/28] rust: alloc: implement `collect` for `IntoIter` Danilo Krummrich
                   ` (11 subsequent siblings)
  27 siblings, 0 replies; 100+ messages in thread
From: Danilo Krummrich @ 2024-08-05 15:19 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, acurrid, 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.

Signed-off-by: Danilo Krummrich <dakr@kernel.org>
---
 rust/kernel/alloc.rs      |   1 +
 rust/kernel/alloc/kvec.rs | 186 ++++++++++++++++++++++++++++++++++++++
 2 files changed, 187 insertions(+)

diff --git a/rust/kernel/alloc.rs b/rust/kernel/alloc.rs
index 28c186906e1b..86648037b3dc 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 6cf62e7dd36f..51426e0b28e0 100644
--- a/rust/kernel/alloc/kvec.rs
+++ b/rust/kernel/alloc/kvec.rs
@@ -12,6 +12,8 @@
     ops::DerefMut,
     ops::Index,
     ops::IndexMut,
+    ptr,
+    ptr::NonNull,
     slice,
     slice::SliceIndex,
 };
@@ -589,3 +591,187 @@ 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 that moves out of a vector.
+///
+/// This `struct` is created by the `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 ptr = self.ptr;
+        if !Vec::<T, A>::is_zst() {
+            // SAFETY: We can't overflow; `end` is guaranteed to mark the end of the buffer.
+            unsafe { self.ptr = self.ptr.add(1) };
+        } else {
+            // For ZST `ptr` has to stay where it is to remain aligned, so we just reduce `self.len`
+            // by 1.
+        }
+        self.len -= 1;
+
+        // SAFETY: `ptr` is guaranteed to point at a valid element within the buffer.
+        Some(unsafe { ptr.read() })
+    }
+
+    /// # Examples
+    ///
+    /// ```
+    /// let v: KVec<u32> = kernel::kvec![1, 2, 3]?;
+    /// let mut iter = v.into_iter();
+    /// let size = iter.size_hint().0;
+    ///
+    /// iter.next();
+    /// assert_eq!(iter.size_hint().0, size - 1);
+    ///
+    /// iter.next();
+    /// assert_eq!(iter.size_hint().0, size - 2);
+    ///
+    /// iter.next();
+    /// assert_eq!(iter.size_hint().0, size - 3);
+    ///
+    /// # Ok::<(), Error>(())
+    /// ```
+    fn size_hint(&self) -> (usize, Option<usize>) {
+        (self.len, Some(self.len))
+    }
+}
+
+impl<T, A> Drop for IntoIter<T, A>
+where
+    A: Allocator,
+{
+    fn drop(&mut self) {
+        // SAFETY: Drop the remaining vector's elements in place, before we free the backing
+        // memory.
+        unsafe { ptr::drop_in_place(self.as_raw_mut_slice()) };
+
+        // If `cap == 0` we never allocated any memory in the first place.
+        if self.cap != 0 {
+            // SAFETY: `self.buf` was previously allocated with `A`.
+            unsafe { A::free(self.buf.cast()) };
+        }
+    }
+}
+
+impl<T, A> IntoIterator for Vec<T, A>
+where
+    A: Allocator,
+{
+    type Item = T;
+    type IntoIter = IntoIter<T, A>;
+
+    /// Creates a consuming iterator, that is, one that moves each value out of
+    /// the vector (from start to end). The vector cannot be used after calling
+    /// this.
+    ///
+    /// # 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.45.2


^ permalink raw reply related	[flat|nested] 100+ messages in thread

* [PATCH v4 17/28] rust: alloc: implement `collect` for `IntoIter`
  2024-08-05 15:19 [PATCH v4 00/28] Generic `Allocator` support for Rust Danilo Krummrich
                   ` (15 preceding siblings ...)
  2024-08-05 15:19 ` [PATCH v4 16/28] rust: alloc: implement `IntoIterator` for `Vec` Danilo Krummrich
@ 2024-08-05 15:19 ` Danilo Krummrich
  2024-08-05 15:19 ` [PATCH v4 18/28] rust: treewide: switch to the kernel `Vec` type Danilo Krummrich
                   ` (10 subsequent siblings)
  27 siblings, 0 replies; 100+ messages in thread
From: Danilo Krummrich @ 2024-08-05 15:19 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, acurrid, 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 | 78 +++++++++++++++++++++++++++++++++++++++
 1 file changed, 78 insertions(+)

diff --git a/rust/kernel/alloc/kvec.rs b/rust/kernel/alloc/kvec.rs
index 51426e0b28e0..160c6b3868fe 100644
--- a/rust/kernel/alloc/kvec.rs
+++ b/rust/kernel/alloc/kvec.rs
@@ -644,6 +644,84 @@ impl<T, A> IntoIter<T, A>
     fn as_raw_mut_slice(&mut self) -> *mut [T] {
         ptr::slice_from_raw_parts_mut(self.ptr, self.len)
     }
+
+    fn into_raw_parts(self) -> (*mut T, NonNull<T>, usize, usize) {
+        let me = ManuallyDrop::new(self);
+        let ptr = me.ptr;
+        let buf = me.buf;
+        let len = me.len;
+        let cap = me.cap;
+        (ptr, buf, len, cap)
+    }
+
+    /// Same as `Iterator::collect` but specialized for `Vec`'s `IntoIter`.
+    ///
+    /// Currently, we can't implement `FromIterator`. There are a couple of issues with this trait
+    /// in the kernel, namely:
+    ///
+    /// - Rust's specialization feature is unstable. This prevents us to optimze for the special
+    ///   case where `I::IntoIter` equals `Vec`'s `IntoIter` type.
+    /// - We also can't use `I::IntoIter`'s type ID either to work around this, since `FromIterator`
+    ///   doesn't require this type to be `'static`.
+    /// - `FromIterator::from_iter` does return `Self` instead of `Result<Self, AllocError>`, hence
+    ///   we can't properly handle allocation failures.
+    /// - Neither `Iterator::collect` nor `FromIterator::from_iter` can handle additional allocation
+    ///   flags.
+    ///
+    /// Instead, provide `IntoIter::collect`, such that we can at least convert a `IntoIter` into a
+    /// `Vec` again.
+    ///
+    /// Note that `IntoIter::collect` doesn't require `Flags`, since it re-uses the existing backing
+    /// buffer. However, this backing buffer may be shrunk to the actual count of elements.
+    ///
+    /// # Examples
+    ///
+    /// ```
+    /// let v = kernel::kvec![1, 2, 3]?;
+    /// let mut it = v.into_iter();
+    ///
+    /// assert_eq!(it.next(), Some(1));
+    ///
+    /// let v = it.collect(GFP_KERNEL);
+    /// assert_eq!(v, [2, 3]);
+    ///
+    /// # Ok::<(), Error>(())
+    /// ```
+    pub fn collect(self, flags: Flags) -> Vec<T, A> {
+        let (mut ptr, buf, len, mut cap) = self.into_raw_parts();
+        let has_advanced = ptr != buf.as_ptr();
+
+        if has_advanced {
+            // SAFETY: Copy the contents we have advanced to at the beginning of the buffer.
+            // `ptr` is guaranteed to be between `buf` and `buf.add(cap)` and `ptr.add(len)` is
+            // guaranteed to be smaller than `buf.add(cap)`.
+            unsafe { ptr::copy(ptr, buf.as_ptr(), len) };
+            ptr = buf.as_ptr();
+        }
+
+        // This can never fail, `len` is guaranteed to be smaller than `cap`.
+        let layout = core::alloc::Layout::array::<T>(len).unwrap();
+
+        // SAFETY: `buf` points to the start of the backing buffer and `len` is guaranteed to be
+        // smaller than `cap`. Depending on `alloc` this operation may shrink the buffer or leaves
+        // it as it is.
+        ptr = match unsafe { A::realloc(Some(buf.cast()), layout, flags) } {
+            // If we fail to shrink, which likely can't even happen, continue with the existing
+            // buffer.
+            Err(_) => ptr,
+            Ok(ptr) => {
+                cap = len;
+                ptr.as_ptr().cast()
+            }
+        };
+
+        // SAFETY: If the iterator has been advanced, the advanced elements have been copied to
+        // the beginning of the buffer and `len` has been adjusted accordingly. `ptr` is guaranteed
+        // to point to the start of the backing buffer. `cap` is either the original capacity or,
+        // after shrinking the buffer, equal to `len`. `alloc` is guaranteed to be unchanged since
+        // `into_iter` has been called on the original `Vec`.
+        unsafe { Vec::from_raw_parts(ptr, len, cap) }
+    }
 }
 
 impl<T, A> Iterator for IntoIter<T, A>
-- 
2.45.2


^ permalink raw reply related	[flat|nested] 100+ messages in thread

* [PATCH v4 18/28] rust: treewide: switch to the kernel `Vec` type
  2024-08-05 15:19 [PATCH v4 00/28] Generic `Allocator` support for Rust Danilo Krummrich
                   ` (16 preceding siblings ...)
  2024-08-05 15:19 ` [PATCH v4 17/28] rust: alloc: implement `collect` for `IntoIter` Danilo Krummrich
@ 2024-08-05 15:19 ` Danilo Krummrich
  2024-08-07 13:47   ` Alice Ryhl
  2024-08-08  9:08   ` Benno Lossin
  2024-08-05 15:19 ` [PATCH v4 19/28] rust: alloc: remove `VecExt` extension Danilo Krummrich
                   ` (9 subsequent siblings)
  27 siblings, 2 replies; 100+ messages in thread
From: Danilo Krummrich @ 2024-08-05 15:19 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, acurrid, 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.

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        | 15 ++++++---------
 samples/rust/rust_minimal.rs  |  4 ++--
 5 files changed, 15 insertions(+), 20 deletions(-)

diff --git a/rust/kernel/str.rs b/rust/kernel/str.rs
index bb8d4f41475b..0b6ffbade521 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)?;
+        KVec::extend_from_slice(&mut buf, cstr.as_bytes_with_nul(), GFP_KERNEL)?;
 
         // INVARIANT: The `CStr` and `CString` types have the same invariants for
         // the string data, and we copied it over without changes.
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 25761da5a816..25f290428c8d 100644
--- a/rust/kernel/types.rs
+++ b/rust/kernel/types.rs
@@ -188,7 +188,7 @@ unsafe fn from_foreign(_: *const core::ffi::c_void) -> Self {}
 /// # use kernel::types::ScopeGuard;
 /// fn example3(arg: bool) -> Result {
 ///     let mut vec =
-///         ScopeGuard::new_with_data(Vec::new(), |v| pr_info!("vec had {} elements\n", v.len()));
+///         ScopeGuard::new_with_data(KVec::new(), |v| pr_info!("vec had {} elements\n", v.len()));
 ///
 ///     vec.push(10u8, GFP_KERNEL)?;
 ///     if arg {
diff --git a/rust/kernel/uaccess.rs b/rust/kernel/uaccess.rs
index e9347cff99ab..d19002308636 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,14 +75,14 @@
 /// fn is_valid(uptr: UserPtr, len: usize) -> Result<bool> {
 ///     let read = UserSlice::new(uptr, len).reader();
 ///
-///     let mut buf = Vec::new();
+///     let mut buf = KVec::new();
 ///     read.read_all(&mut buf, GFP_KERNEL)?;
 ///
 ///     todo!()
 /// }
 ///
 /// /// Returns the bytes behind this user pointer if they are valid.
-/// fn get_bytes_if_valid(uptr: UserPtr, len: usize) -> Result<Vec<u8>> {
+/// fn get_bytes_if_valid(uptr: UserPtr, len: usize) -> Result<KVec<u8>> {
 ///     if !is_valid(uptr, len)? {
 ///         return Err(EINVAL);
 ///     }
@@ -130,7 +127,7 @@ pub fn new(ptr: UserPtr, length: usize) -> Self {
     /// Reads the entirety of the user slice, appending it to the end of the provided buffer.
     ///
     /// Fails with [`EFAULT`] if the read happens on a bad address.
-    pub fn read_all(self, buf: &mut Vec<u8>, flags: Flags) -> Result {
+    pub fn read_all(self, buf: &mut KVec<u8>, flags: Flags) -> Result {
         self.reader().read_all(buf, flags)
     }
 
@@ -291,9 +288,9 @@ pub fn read<T: FromBytes>(&mut self) -> Result<T> {
     /// Reads the entirety of the user slice, appending it to the end of the provided buffer.
     ///
     /// Fails with [`EFAULT`] if the read happens on a bad address.
-    pub fn read_all(mut self, buf: &mut Vec<u8>, flags: Flags) -> Result {
+    pub fn read_all(mut self, buf: &mut KVec<u8>, flags: Flags) -> Result {
         let len = self.length;
-        VecExt::<u8>::reserve(buf, len, flags)?;
+        KVec::<u8>::reserve(buf, len, flags)?;
 
         // The call to `try_reserve` was successful, so the spare capacity is at least `len` bytes
         // long.
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.45.2


^ permalink raw reply related	[flat|nested] 100+ messages in thread

* [PATCH v4 19/28] rust: alloc: remove `VecExt` extension
  2024-08-05 15:19 [PATCH v4 00/28] Generic `Allocator` support for Rust Danilo Krummrich
                   ` (17 preceding siblings ...)
  2024-08-05 15:19 ` [PATCH v4 18/28] rust: treewide: switch to the kernel `Vec` type Danilo Krummrich
@ 2024-08-05 15:19 ` Danilo Krummrich
  2024-08-07 12:42   ` Alice Ryhl
  2024-08-08  9:14   ` Benno Lossin
  2024-08-05 15:19 ` [PATCH v4 20/28] rust: alloc: add `Vec` to prelude Danilo Krummrich
                   ` (8 subsequent siblings)
  27 siblings, 2 replies; 100+ messages in thread
From: Danilo Krummrich @ 2024-08-05 15:19 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, acurrid, 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.

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 86648037b3dc..8a1cecc20d09 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 bb80a43d20fb..fcc8656fdb51 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.45.2


^ permalink raw reply related	[flat|nested] 100+ messages in thread

* [PATCH v4 20/28] rust: alloc: add `Vec` to prelude
  2024-08-05 15:19 [PATCH v4 00/28] Generic `Allocator` support for Rust Danilo Krummrich
                   ` (18 preceding siblings ...)
  2024-08-05 15:19 ` [PATCH v4 19/28] rust: alloc: remove `VecExt` extension Danilo Krummrich
@ 2024-08-05 15:19 ` Danilo Krummrich
  2024-08-07 13:55   ` Alice Ryhl
  2024-08-08  8:40   ` Benno Lossin
  2024-08-05 15:19 ` [PATCH v4 21/28] rust: alloc: remove `GlobalAlloc` and `krealloc_aligned` Danilo Krummrich
                   ` (7 subsequent siblings)
  27 siblings, 2 replies; 100+ messages in thread
From: Danilo Krummrich @ 2024-08-05 15:19 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, acurrid, 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.

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 fcc8656fdb51..b049ab96202e 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.45.2


^ permalink raw reply related	[flat|nested] 100+ messages in thread

* [PATCH v4 21/28] rust: alloc: remove `GlobalAlloc` and `krealloc_aligned`
  2024-08-05 15:19 [PATCH v4 00/28] Generic `Allocator` support for Rust Danilo Krummrich
                   ` (19 preceding siblings ...)
  2024-08-05 15:19 ` [PATCH v4 20/28] rust: alloc: add `Vec` to prelude Danilo Krummrich
@ 2024-08-05 15:19 ` Danilo Krummrich
  2024-08-06 19:07   ` Björn Roy Baron
  2024-08-07 21:23   ` Benno Lossin
  2024-08-05 15:19 ` [PATCH v4 22/28] rust: error: use `core::alloc::LayoutError` Danilo Krummrich
                   ` (6 subsequent siblings)
  27 siblings, 2 replies; 100+ messages in thread
From: Danilo Krummrich @ 2024-08-05 15:19 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, acurrid, cjia, jhubbard, airlied, ajanulgu, lyude,
	linux-kernel, rust-for-linux, linux-mm, Danilo Krummrich

Now that we have all the `Allocator`s and the kernel `Box` and `Vec`
implementations in place, remove `GlobalAlloc` and `krealloc_aligned`.

Signed-off-by: Danilo Krummrich <dakr@kernel.org>
---
 rust/kernel/alloc/allocator.rs | 63 ++--------------------------------
 1 file changed, 2 insertions(+), 61 deletions(-)

diff --git a/rust/kernel/alloc/allocator.rs b/rust/kernel/alloc/allocator.rs
index 91ff21372a8f..6abd55694422 100644
--- a/rust/kernel/alloc/allocator.rs
+++ b/rust/kernel/alloc/allocator.rs
@@ -2,8 +2,8 @@
 
 //! Allocator support.
 
-use super::{flags::*, Flags};
-use core::alloc::{GlobalAlloc, Layout};
+use super::Flags;
+use core::alloc::Layout;
 use core::ptr;
 use core::ptr::NonNull;
 
@@ -40,27 +40,6 @@ fn aligned_size(new_layout: Layout) -> usize {
     layout.size()
 }
 
-/// Calls `krealloc` with a proper size to alloc a new object aligned to `new_layout`'s alignment.
-///
-/// # Safety
-///
-/// - `ptr` can be either null or a pointer which has been allocated by this allocator.
-/// - `new_layout` must have a non-zero size.
-pub(crate) unsafe fn krealloc_aligned(ptr: *mut u8, new_layout: Layout, flags: Flags) -> *mut u8 {
-    // SAFETY:
-    // - `ptr` is either null or a pointer returned from a previous `k{re}alloc()` by the
-    //   function safety requirement.
-    // - `size` is greater than 0 since it's from `layout.size()` (which cannot be zero according
-    //   to the function safety requirement)
-    unsafe {
-        bindings::krealloc(
-            ptr as *const core::ffi::c_void,
-            aligned_size(new_layout),
-            flags.0,
-        ) as *mut u8
-    }
-}
-
 /// # Invariants
 ///
 /// One of the following `krealloc`, `vrealloc`, `kvrealloc`.
@@ -129,41 +108,6 @@ unsafe fn realloc(
     }
 }
 
-unsafe impl GlobalAlloc for Kmalloc {
-    unsafe fn alloc(&self, layout: Layout) -> *mut u8 {
-        // SAFETY: `ptr::null_mut()` is null and `layout` has a non-zero size by the function safety
-        // requirement.
-        unsafe { krealloc_aligned(ptr::null_mut(), layout, GFP_KERNEL) }
-    }
-
-    unsafe fn dealloc(&self, ptr: *mut u8, _layout: Layout) {
-        unsafe {
-            bindings::kfree(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) }
-    }
-}
-
 unsafe impl Allocator for Vmalloc {
     unsafe fn realloc(
         ptr: Option<NonNull<u8>>,
@@ -204,9 +148,6 @@ unsafe fn realloc(
     }
 }
 
-#[global_allocator]
-static ALLOCATOR: Kmalloc = Kmalloc;
-
 // See <https://github.com/rust-lang/rust/pull/86844>.
 #[no_mangle]
 static __rust_no_alloc_shim_is_unstable: u8 = 0;
-- 
2.45.2


^ permalink raw reply related	[flat|nested] 100+ messages in thread

* [PATCH v4 22/28] rust: error: use `core::alloc::LayoutError`
  2024-08-05 15:19 [PATCH v4 00/28] Generic `Allocator` support for Rust Danilo Krummrich
                   ` (20 preceding siblings ...)
  2024-08-05 15:19 ` [PATCH v4 21/28] rust: alloc: remove `GlobalAlloc` and `krealloc_aligned` Danilo Krummrich
@ 2024-08-05 15:19 ` Danilo Krummrich
  2024-08-07 13:55   ` Alice Ryhl
  2024-08-08  7:42   ` Benno Lossin
  2024-08-05 15:19 ` [PATCH v4 23/28] rust: error: check for config `test` in `Error::name` Danilo Krummrich
                   ` (5 subsequent siblings)
  27 siblings, 2 replies; 100+ messages in thread
From: Danilo Krummrich @ 2024-08-05 15:19 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, acurrid, 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.

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 145f5c397009..2d012cc3881a 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.45.2


^ permalink raw reply related	[flat|nested] 100+ messages in thread

* [PATCH v4 23/28] rust: error: check for config `test` in `Error::name`
  2024-08-05 15:19 [PATCH v4 00/28] Generic `Allocator` support for Rust Danilo Krummrich
                   ` (21 preceding siblings ...)
  2024-08-05 15:19 ` [PATCH v4 22/28] rust: error: use `core::alloc::LayoutError` Danilo Krummrich
@ 2024-08-05 15:19 ` Danilo Krummrich
  2024-08-07 13:54   ` Alice Ryhl
  2024-08-05 15:19 ` [PATCH v4 24/28] rust: alloc: implement `contains` for `Flags` Danilo Krummrich
                   ` (4 subsequent siblings)
  27 siblings, 1 reply; 100+ messages in thread
From: Danilo Krummrich @ 2024-08-05 15:19 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, acurrid, 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 `Èrror` in test
cases.

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 2d012cc3881a..7adf889b9526 100644
--- a/rust/kernel/error.rs
+++ b/rust/kernel/error.rs
@@ -140,7 +140,7 @@ pub(crate) fn to_ptr<T>(self) -> *mut T {
     }
 
     /// Returns a string representing the error, if one exists.
-    #[cfg(not(testlib))]
+    #[cfg(not(any(test, testlib)))]
     pub fn name(&self) -> Option<&'static CStr> {
         // SAFETY: Just an FFI call, there are no extra safety requirements.
         let ptr = unsafe { bindings::errname(-self.0) };
@@ -157,7 +157,7 @@ pub fn name(&self) -> Option<&'static CStr> {
     /// When `testlib` is configured, this always returns `None` to avoid the dependency on a
     /// kernel function so that tests that use this (e.g., by calling [`Result::unwrap`]) can still
     /// run in userspace.
-    #[cfg(testlib)]
+    #[cfg(any(test, testlib))]
     pub fn name(&self) -> Option<&'static CStr> {
         None
     }
-- 
2.45.2


^ permalink raw reply related	[flat|nested] 100+ messages in thread

* [PATCH v4 24/28] rust: alloc: implement `contains` for `Flags`
  2024-08-05 15:19 [PATCH v4 00/28] Generic `Allocator` support for Rust Danilo Krummrich
                   ` (22 preceding siblings ...)
  2024-08-05 15:19 ` [PATCH v4 23/28] rust: error: check for config `test` in `Error::name` Danilo Krummrich
@ 2024-08-05 15:19 ` Danilo Krummrich
  2024-08-07 13:53   ` Alice Ryhl
  2024-08-05 15:19 ` [PATCH v4 25/28] rust: alloc: implement `Cmalloc` in module allocator_test Danilo Krummrich
                   ` (3 subsequent siblings)
  27 siblings, 1 reply; 100+ messages in thread
From: Danilo Krummrich @ 2024-08-05 15:19 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, acurrid, 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.

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 8a1cecc20d09..615afe69d097 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.45.2


^ permalink raw reply related	[flat|nested] 100+ messages in thread

* [PATCH v4 25/28] rust: alloc: implement `Cmalloc` in module allocator_test
  2024-08-05 15:19 [PATCH v4 00/28] Generic `Allocator` support for Rust Danilo Krummrich
                   ` (23 preceding siblings ...)
  2024-08-05 15:19 ` [PATCH v4 24/28] rust: alloc: implement `contains` for `Flags` Danilo Krummrich
@ 2024-08-05 15:19 ` Danilo Krummrich
  2024-08-08  9:35   ` Benno Lossin
  2024-08-05 15:19 ` [PATCH v4 26/28] rust: str: test: replace `alloc::format` Danilo Krummrich
                   ` (2 subsequent siblings)
  27 siblings, 1 reply; 100+ messages in thread
From: Danilo Krummrich @ 2024-08-05 15:19 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, acurrid, 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>
---
I know, having an `old_size` parameter would indeed help implementing `Cmalloc`.

However, I really don't want test infrastructure to influence the design of
kernel internal APIs.

Besides that, adding the `old_size` parameter would have the consequence that
we'd either need to honor it for kernel allocators too (which adds another
source of potential failure) or ignore it for all kernel allocators (which
potentially tricks people into taking wrong assumptions on how the API works).
---
 rust/kernel/alloc/allocator_test.rs | 91 ++++++++++++++++++++++++++---
 1 file changed, 84 insertions(+), 7 deletions(-)

diff --git a/rust/kernel/alloc/allocator_test.rs b/rust/kernel/alloc/allocator_test.rs
index 1b2642c547ec..05fd75b89238 100644
--- a/rust/kernel/alloc/allocator_test.rs
+++ b/rust/kernel/alloc/allocator_test.rs
@@ -2,20 +2,97 @@
 
 #![allow(missing_docs)]
 
-use super::{AllocError, Allocator, Flags};
+use super::{flags::*, AllocError, Allocator, Flags};
 use core::alloc::Layout;
+use core::ptr;
 use core::ptr::NonNull;
 
-pub struct Kmalloc;
+pub struct Cmalloc;
+pub type Kmalloc = Cmalloc;
 pub type Vmalloc = Kmalloc;
 pub type KVmalloc = Kmalloc;
 
-unsafe impl Allocator for Kmalloc {
+extern "C" {
+    #[link_name = "aligned_alloc"]
+    fn libc_aligned_alloc(align: usize, size: usize) -> *mut core::ffi::c_void;
+
+    #[link_name = "free"]
+    fn libc_free(ptr: *mut core::ffi::c_void);
+
+    // Do not use this function for production code! For test cases only it's
+    // probably fine if used with care.
+    #[link_name = "malloc_usable_size"]
+    fn libc_malloc_usable_size(ptr: *mut core::ffi::c_void) -> usize;
+}
+
+unsafe impl Allocator for Cmalloc {
+    fn alloc(layout: Layout, flags: Flags) -> Result<NonNull<[u8]>, AllocError> {
+        let layout = layout.pad_to_align();
+
+        // SAFETY: Returns either NULL or a pointer to a memory allocation that satisfies or
+        // exceeds the given size and alignment requirements.
+        let raw_ptr = unsafe { libc_aligned_alloc(layout.align(), layout.size()) } as *mut u8;
+
+        if flags.contains(__GFP_ZERO) && !raw_ptr.is_null() {
+            // SAFETY: `raw_ptr` points to memory successfully allocated with `libc_aligned_alloc`.
+            let size = unsafe { libc_malloc_usable_size(raw_ptr.cast()) };
+
+            // SAFETY: `raw_ptr` points to memory successfully allocated with `libc_aligned_alloc`
+            // of at least `size` bytes.
+            unsafe { core::ptr::write_bytes(raw_ptr, 0, size) };
+        }
+
+        let ptr = if layout.size() == 0 {
+            NonNull::dangling()
+        } else {
+            NonNull::new(raw_ptr).ok_or(AllocError)?
+        };
+
+        Ok(NonNull::slice_from_raw_parts(ptr, layout.size()))
+    }
+
     unsafe fn realloc(
-        _ptr: Option<NonNull<u8>>,
-        _layout: Layout,
-        _flags: Flags,
+        ptr: Option<NonNull<u8>>,
+        layout: Layout,
+        flags: Flags,
     ) -> Result<NonNull<[u8]>, AllocError> {
-        panic!();
+        let layout = layout.pad_to_align();
+        let src: *mut u8 = if let Some(src) = ptr {
+            src.as_ptr().cast()
+        } else {
+            ptr::null_mut()
+        };
+
+        if layout.size() == 0 {
+            // SAFETY: `src` is either NULL or has previously been allocatored with this
+            // `Allocator`.
+            unsafe { libc_free(src.cast()) };
+
+            return Ok(NonNull::slice_from_raw_parts(NonNull::dangling(), 0));
+        }
+
+        let dst = Self::alloc(layout, flags)?;
+
+        if src.is_null() {
+            return Ok(dst);
+        }
+
+        // SAFETY: `src` is either NULL or has previously been allocatored with this `Allocator`.
+        let old_size = unsafe { libc_malloc_usable_size(src.cast()) };
+
+        // SAFETY: `src` has previously been allocated with this `Allocator`; `dst` has just been
+        // newly allocated. Taking the minimum of their sizes guarantees that we do not exceed
+        // either bounds.
+        unsafe {
+            // Always copy -- do not rely on potential spare memory reported by
+            // malloc_usable_size() which technically may still be sufficient.
+            ptr::copy_nonoverlapping(
+                src,
+                dst.as_ptr().cast(),
+                core::cmp::min(layout.size(), old_size),
+            )
+        };
+
+        Ok(dst)
     }
 }
-- 
2.45.2


^ permalink raw reply related	[flat|nested] 100+ messages in thread

* [PATCH v4 26/28] rust: str: test: replace `alloc::format`
  2024-08-05 15:19 [PATCH v4 00/28] Generic `Allocator` support for Rust Danilo Krummrich
                   ` (24 preceding siblings ...)
  2024-08-05 15:19 ` [PATCH v4 25/28] rust: alloc: implement `Cmalloc` in module allocator_test Danilo Krummrich
@ 2024-08-05 15:19 ` Danilo Krummrich
  2024-08-07 13:51   ` Alice Ryhl
  2024-08-08  7:22   ` Benno Lossin
  2024-08-05 15:19 ` [PATCH v4 27/28] rust: alloc: update module comment of alloc.rs Danilo Krummrich
  2024-08-05 15:19 ` [PATCH v4 28/28] kbuild: rust: remove the `alloc` crate Danilo Krummrich
  27 siblings, 2 replies; 100+ messages in thread
From: Danilo Krummrich @ 2024-08-05 15:19 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, acurrid, 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`.

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 0b6ffbade521..8f234224cbf5 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.45.2


^ permalink raw reply related	[flat|nested] 100+ messages in thread

* [PATCH v4 27/28] rust: alloc: update module comment of alloc.rs
  2024-08-05 15:19 [PATCH v4 00/28] Generic `Allocator` support for Rust Danilo Krummrich
                   ` (25 preceding siblings ...)
  2024-08-05 15:19 ` [PATCH v4 26/28] rust: str: test: replace `alloc::format` Danilo Krummrich
@ 2024-08-05 15:19 ` Danilo Krummrich
  2024-08-07 13:50   ` Alice Ryhl
  2024-08-08  6:59   ` Benno Lossin
  2024-08-05 15:19 ` [PATCH v4 28/28] kbuild: rust: remove the `alloc` crate Danilo Krummrich
  27 siblings, 2 replies; 100+ messages in thread
From: Danilo Krummrich @ 2024-08-05 15:19 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, acurrid, 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.

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 615afe69d097..ccb9f5ddc4bb 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.45.2


^ permalink raw reply related	[flat|nested] 100+ messages in thread

* [PATCH v4 28/28] kbuild: rust: remove the `alloc` crate
  2024-08-05 15:19 [PATCH v4 00/28] Generic `Allocator` support for Rust Danilo Krummrich
                   ` (26 preceding siblings ...)
  2024-08-05 15:19 ` [PATCH v4 27/28] rust: alloc: update module comment of alloc.rs Danilo Krummrich
@ 2024-08-05 15:19 ` Danilo Krummrich
  2024-08-08  6:58   ` Benno Lossin
  2024-08-08  9:45   ` Benno Lossin
  27 siblings, 2 replies; 100+ messages in thread
From: Danilo Krummrich @ 2024-08-05 15:19 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, acurrid, 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 corresponding unstable features.

Signed-off-by: Danilo Krummrich <dakr@kernel.org>
---
 rust/Makefile          | 44 ++++++++++--------------------------------
 rust/exports.c         |  1 -
 scripts/Makefile.build |  7 +------
 3 files changed, 11 insertions(+), 41 deletions(-)

diff --git a/rust/Makefile b/rust/Makefile
index 1f10f92737f2..8900c3d06573 100644
--- a/rust/Makefile
+++ b/rust/Makefile
@@ -15,9 +15,8 @@ always-$(CONFIG_RUST) += libmacros.so
 no-clean-files += libmacros.so
 
 always-$(CONFIG_RUST) += bindings/bindings_generated.rs bindings/bindings_helpers_generated.rs
-obj-$(CONFIG_RUST) += alloc.o bindings.o kernel.o
-always-$(CONFIG_RUST) += exports_alloc_generated.h exports_bindings_generated.h \
-    exports_kernel_generated.h
+obj-$(CONFIG_RUST) += bindings.o kernel.o
+always-$(CONFIG_RUST) += exports_bindings_generated.h exports_kernel_generated.h
 
 always-$(CONFIG_RUST) += uapi/uapi_generated.rs
 obj-$(CONFIG_RUST) += uapi.o
@@ -53,11 +52,6 @@ endif
 core-cfgs = \
     --cfg no_fp_fmt_parse
 
-alloc-cfgs = \
-    --cfg no_global_oom_handling \
-    --cfg no_rc \
-    --cfg no_sync
-
 quiet_cmd_rustdoc = RUSTDOC $(if $(rustdoc_host),H, ) $<
       cmd_rustdoc = \
 	OBJTREE=$(abspath $(objtree)) \
@@ -80,7 +74,7 @@ quiet_cmd_rustdoc = RUSTDOC $(if $(rustdoc_host),H, ) $<
 # command-like flags to solve the issue. Meanwhile, we use the non-custom case
 # and then retouch the generated files.
 rustdoc: rustdoc-core rustdoc-macros rustdoc-compiler_builtins \
-    rustdoc-alloc rustdoc-kernel
+    rustdoc-kernel
 	$(Q)cp $(srctree)/Documentation/images/logo.svg $(rustdoc_output)/static.files/
 	$(Q)cp $(srctree)/Documentation/images/COPYING-logo $(rustdoc_output)/static.files/
 	$(Q)find $(rustdoc_output) -name '*.html' -type f -print0 | xargs -0 sed -Ei \
@@ -104,20 +98,11 @@ rustdoc-core: $(RUST_LIB_SRC)/core/src/lib.rs FORCE
 rustdoc-compiler_builtins: $(src)/compiler_builtins.rs rustdoc-core FORCE
 	+$(call if_changed,rustdoc)
 
-# We need to allow `rustdoc::broken_intra_doc_links` because some
-# `no_global_oom_handling` functions refer to non-`no_global_oom_handling`
-# functions. Ideally `rustdoc` would have a way to distinguish broken links
-# due to things that are "configured out" vs. entirely non-existing ones.
-rustdoc-alloc: private rustc_target_flags = $(alloc-cfgs) \
-    -Arustdoc::broken_intra_doc_links
-rustdoc-alloc: $(RUST_LIB_SRC)/alloc/src/lib.rs rustdoc-core rustdoc-compiler_builtins FORCE
-	+$(call if_changed,rustdoc)
-
-rustdoc-kernel: private rustc_target_flags = --extern alloc \
+rustdoc-kernel: private rustc_target_flags = \
     --extern build_error --extern macros=$(objtree)/$(obj)/libmacros.so \
     --extern bindings --extern uapi
 rustdoc-kernel: $(src)/kernel/lib.rs rustdoc-core rustdoc-macros \
-    rustdoc-compiler_builtins rustdoc-alloc $(obj)/libmacros.so \
+    rustdoc-compiler_builtins $(obj)/libmacros.so \
     $(obj)/bindings.o FORCE
 	+$(call if_changed,rustdoc)
 
@@ -161,7 +146,7 @@ quiet_cmd_rustdoc_test_kernel = RUSTDOC TK $<
 	mkdir -p $(objtree)/$(obj)/test/doctests/kernel; \
 	OBJTREE=$(abspath $(objtree)) \
 	$(RUSTDOC) --test $(rust_flags) \
-		-L$(objtree)/$(obj) --extern alloc --extern kernel \
+		-L$(objtree)/$(obj) --extern kernel \
 		--extern build_error --extern macros \
 		--extern bindings --extern uapi \
 		--no-run --crate-name kernel -Zunstable-options \
@@ -197,7 +182,7 @@ rusttest-macros: $(src)/macros/lib.rs FORCE
 	+$(call if_changed,rustc_test)
 	+$(call if_changed,rustdoc_test)
 
-rusttest-kernel: private rustc_target_flags = --extern alloc \
+rusttest-kernel: private rustc_target_flags = \
     --extern build_error --extern macros --extern bindings --extern uapi
 rusttest-kernel: $(src)/kernel/lib.rs \
     rusttestlib-build_error rusttestlib-macros rusttestlib-bindings \
@@ -310,9 +295,6 @@ quiet_cmd_exports = EXPORTS $@
 $(obj)/exports_core_generated.h: $(obj)/core.o FORCE
 	$(call if_changed,exports)
 
-$(obj)/exports_alloc_generated.h: $(obj)/alloc.o FORCE
-	$(call if_changed,exports)
-
 $(obj)/exports_bindings_generated.h: $(obj)/bindings.o FORCE
 	$(call if_changed,exports)
 
@@ -348,7 +330,7 @@ quiet_cmd_rustc_library = $(if $(skip_clippy),RUSTC,$(RUSTC_OR_CLIPPY_QUIET)) L
 
 rust-analyzer:
 	$(Q)$(srctree)/scripts/generate_rust_analyzer.py \
-		--cfgs='core=$(core-cfgs)' --cfgs='alloc=$(alloc-cfgs)' \
+		--cfgs='core=$(core-cfgs)' \
 		$(realpath $(srctree)) $(realpath $(objtree)) \
 		$(RUST_LIB_SRC) $(KBUILD_EXTMOD) > \
 		$(if $(KBUILD_EXTMOD),$(extmod_prefix),$(objtree))/rust-project.json
@@ -380,12 +362,6 @@ $(obj)/compiler_builtins.o: private rustc_objcopy = -w -W '__*'
 $(obj)/compiler_builtins.o: $(src)/compiler_builtins.rs $(obj)/core.o FORCE
 	+$(call if_changed_dep,rustc_library)
 
-$(obj)/alloc.o: private skip_clippy = 1
-$(obj)/alloc.o: private skip_flags = -Wunreachable_pub
-$(obj)/alloc.o: private rustc_target_flags = $(alloc-cfgs)
-$(obj)/alloc.o: $(RUST_LIB_SRC)/alloc/src/lib.rs $(obj)/compiler_builtins.o FORCE
-	+$(call if_changed_dep,rustc_library)
-
 $(obj)/build_error.o: $(src)/build_error.rs $(obj)/compiler_builtins.o FORCE
 	+$(call if_changed_dep,rustc_library)
 
@@ -400,9 +376,9 @@ $(obj)/uapi.o: $(src)/uapi/lib.rs \
     $(obj)/uapi/uapi_generated.rs FORCE
 	+$(call if_changed_dep,rustc_library)
 
-$(obj)/kernel.o: private rustc_target_flags = --extern alloc \
+$(obj)/kernel.o: private rustc_target_flags = \
     --extern build_error --extern macros --extern bindings --extern uapi
-$(obj)/kernel.o: $(src)/kernel/lib.rs $(obj)/alloc.o $(obj)/build_error.o \
+$(obj)/kernel.o: $(src)/kernel/lib.rs $(obj)/build_error.o \
     $(obj)/libmacros.so $(obj)/bindings.o $(obj)/uapi.o FORCE
 	+$(call if_changed_dep,rustc_library)
 
diff --git a/rust/exports.c b/rust/exports.c
index 3803c21d1403..1b870e8e83ea 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_bindings_generated.h"
 #include "exports_kernel_generated.h"
 
diff --git a/scripts/Makefile.build b/scripts/Makefile.build
index efacca63c897..7e7b6b3d5bb9 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 \
-- 
2.45.2


^ permalink raw reply related	[flat|nested] 100+ messages in thread

* Re: [PATCH v4 01/28] rust: alloc: add `Allocator` trait
  2024-08-05 15:19 ` [PATCH v4 01/28] rust: alloc: add `Allocator` trait Danilo Krummrich
@ 2024-08-06 15:00   ` Alice Ryhl
  2024-08-06 16:03   ` Benno Lossin
  2024-08-07 18:19   ` Gary Guo
  2 siblings, 0 replies; 100+ messages in thread
From: Alice Ryhl @ 2024-08-06 15:00 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, acurrid, cjia, jhubbard,
	airlied, ajanulgu, lyude, linux-kernel, rust-for-linux, linux-mm

On Mon, Aug 5, 2024 at 5:20 PM 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`.
>
> Signed-off-by: Danilo Krummrich <dakr@kernel.org>
> ---
>  rust/kernel/alloc.rs | 79 ++++++++++++++++++++++++++++++++++++++++++++
>  1 file changed, 79 insertions(+)
>
> diff --git a/rust/kernel/alloc.rs b/rust/kernel/alloc.rs
> index 1966bd407017..8a71a589469d 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,81 @@ pub mod flags {
>      /// small allocations.
>      pub const GFP_NOWAIT: Flags = Flags(bindings::GFP_NOWAIT);
>  }
> +
> +/// The kernel's [`Allocator`] trait.
> +///
> +/// An implementation of [`Allocator`] can allocate, re-allocate and free memory buffer described
> +/// via [`Layout`].
> +///
> +/// [`Allocator`] is designed to be implemented as a ZST; [`Allocator`] functions do not operate on
> +/// an object instance.
> +///
> +/// In order to be able to support `#[derive(SmartPointer)]` later on, we need to avoid a design
> +/// that requires an `Allocator` to be instantiated, hence its functions must not contain any kind
> +/// of `self` parameter.
> +///
> +/// # Safety
> +///
> +/// Memory returned from an allocator must point to a valid memory buffer and remain valid until
> +/// it is explicitly freed.
> +///
> +/// Any pointer to a memory buffer which is currently allocated must be valid to be passed to any
> +/// other [`Allocator`] function of the same type. The same applies for a NULL pointer.
> +///
> +/// If `realloc` is called with:
> +///   - a size of zero, the given memory allocation, if any, must be freed
> +///   - a NULL pointer, a new memory allocation must be created
> +pub unsafe trait Allocator {
> +    /// Allocate memory based on `layout` and `flags`.
> +    ///
> +    /// On success, returns a buffer represented as `NonNull<[u8]>` that satisfies the layout
> +    /// constraints (i.e. minimum size and alignment as specified by `layout`).
> +    ///
> +    /// This function is equivalent to `realloc` when called with a NULL pointer.
> +    fn alloc(layout: Layout, flags: Flags) -> Result<NonNull<[u8]>, AllocError> {
> +        // SAFETY: Passing a NULL pointer to `realloc` is valid by it's safety requirements and asks
> +        // for a new memory allocation.
> +        unsafe { Self::realloc(None, layout, flags) }
> +    }
> +
> +    /// Re-allocate an existing memory allocation to satisfy the requested `layout`. If the
> +    /// requested size is zero, `realloc` behaves equivalent to `free`.
> +    ///
> +    /// If the requested size is larger than the size of the existing allocation, a successful call
> +    /// to `realloc` guarantees that the new or grown buffer has at least `Layout::size` bytes, but
> +    /// may also be larger.
> +    ///
> +    /// If the requested size is smaller than the size of the existing allocation, `realloc` may or
> +    /// may not shrink the buffer; this is implementation specific to the allocator.
> +    ///
> +    /// On allocation failure, the existing buffer, if any, remains valid.
> +    ///
> +    /// The buffer is represented as `NonNull<[u8]>`.
> +    ///
> +    /// # Safety
> +    ///
> +    /// `Some(ptr)` must point to an existing and valid memory allocation created by this allocator
> +    /// instance. The alignment encoded in `layout` must be smaller than or equal to the alignment
> +    /// requested in the previous `alloc` or `realloc` call of the same allocation.
> +    ///
> +    /// Additionally, `ptr` is allowed to be `None`; in this case a new memory allocation is
> +    /// created.
> +    ///
> +    unsafe fn realloc(
> +        ptr: Option<NonNull<u8>>,
> +        layout: Layout,
> +        flags: Flags,
> +    ) -> Result<NonNull<[u8]>, AllocError>;
> +
> +    /// Free an existing memory allocation.
> +    ///
> +    /// # Safety
> +    ///
> +    /// `ptr` must point to an existing and valid memory allocation created by this `Allocator`
> +    /// instance.
> +    unsafe fn free(ptr: NonNull<u8>) {
> +        // SAFETY: `ptr` is guaranteed to be previously allocated with this `Allocator` or NULL.
> +        // Calling `realloc` with a buffer size of zero, frees the buffer `ptr` points to.

It can't actually be NULL since it's a NonNull. I would reword to:

// SAFETY: The caller guarantees that `ptr` points at a valid
allocation created by this allocator. We are passing a Layout with the
smallest possible alignment, so it is smaller than or equal to the
alignment previously used with this allocation.

Anyway, otherwise this LGTM.
Reviewed-by: Alice Ryhl <aliceryhl@google.com>

Alice

^ permalink raw reply	[flat|nested] 100+ messages in thread

* Re: [PATCH v4 01/28] rust: alloc: add `Allocator` trait
  2024-08-05 15:19 ` [PATCH v4 01/28] rust: alloc: add `Allocator` trait Danilo Krummrich
  2024-08-06 15:00   ` Alice Ryhl
@ 2024-08-06 16:03   ` Benno Lossin
  2024-08-06 18:30     ` Danilo Krummrich
  2024-08-07 18:19   ` Gary Guo
  2 siblings, 1 reply; 100+ messages in thread
From: Benno Lossin @ 2024-08-06 16:03 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, acurrid, cjia, jhubbard, airlied, ajanulgu, lyude,
	linux-kernel, rust-for-linux, linux-mm

On 05.08.24 17:19, Danilo Krummrich 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`.
> 
> Signed-off-by: Danilo Krummrich <dakr@kernel.org>
> ---
>  rust/kernel/alloc.rs | 79 ++++++++++++++++++++++++++++++++++++++++++++
>  1 file changed, 79 insertions(+)
> 
> diff --git a/rust/kernel/alloc.rs b/rust/kernel/alloc.rs
> index 1966bd407017..8a71a589469d 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,81 @@ pub mod flags {
>      /// small allocations.
>      pub const GFP_NOWAIT: Flags = Flags(bindings::GFP_NOWAIT);
>  }
> +
> +/// The kernel's [`Allocator`] trait.
> +///
> +/// An implementation of [`Allocator`] can allocate, re-allocate and free memory buffer described
> +/// via [`Layout`].
> +///
> +/// [`Allocator`] is designed to be implemented as a ZST; [`Allocator`] functions do not operate on
> +/// an object instance.

This will prevent us from implementing arena-type allocators [^1]. Do we
want/need those?
I have heard that some people use them in embedded systems, but I can't 
say for sure. But this is a rather big design decision, so we should
discuss it now.

[^1]: For those who don't know what I mean by that here is a quick
sketch (without handling flags and optimizations):

    pub struct ArenaAlloc<const SIZE: usize> {
        memory: Opaque<[u8; SIZE]>,
        head: Cell<usize>,
    }

    impl<const SIZE: usize> ArenaAlloc<SIZE> {
        pub fn new() -> Self {
            Self {
                memory: Opaque::uninit(),
                head: 0,
            }
        }
    }

    impl<const SIZE: usize> Allocator for ArenaAlloc<SIZE> {
        fn alloc(&self, layout: Layout, _flags: Flags) -> Result<NonNull<u8>, AllocError> {
            let head = self.head.get();
            if head + layout.size() >= SIZE {
                return Err(AllocError);
            }
            let ptr = self.memory.get();
            let ptr = ptr.cast::<u8>();
            let ptr = unsafe { ptr.add(head) };
            self.head.set(head + layout.size());
            unsafe { NonNull::new_unchecked(ptr) }
        }

        unsafe fn realloc(
            &self,
            ptr: Option<NonNull<u8>>,
            old_layout: Layout, // Note that we also need `old_layout`!
            layout: Layout,
            flags: Flags
        ) -> Result<NonNull<u8>, AllocError> {
            let new = self.alloc(layout, flags)?;
            let Some(ptr) = ptr else { return Ok(new); };
            unsafe { core::ptr::copy_nonoverlapping(ptr.as_ptr(), new.as_ptr(), old_layout.size()) };
            self.free(ptr);
            Ok(new)
        }

        fn free(&self, ptr: NonNull<u8>) { /* noop */ }
    }

> +///
> +/// In order to be able to support `#[derive(SmartPointer)]` later on, we need to avoid a design
> +/// that requires an `Allocator` to be instantiated, hence its functions must not contain any kind
> +/// of `self` parameter.

Ah I see, so since `#[derive(SmartPointer)]` needs `Box` to only consist
of one non ZST field... I skimmed the RFC discussion and it seems like a
problem that *might* be solved in the future, but probably not in the
(very) near future. I guess this is just a bullet that we have to bite.
We can always have an `ArenaBox` that can deal with that (although
without `DispatchFromDyn`).
We should revisit this when `#[derive(SmartPointer)]` becomes advanced
enough.

> +///
> +/// # Safety
> +///
> +/// Memory returned from an allocator must point to a valid memory buffer and remain valid until
> +/// it is explicitly freed.
> +///
> +/// Any pointer to a memory buffer which is currently allocated must be valid to be passed to any
> +/// other [`Allocator`] function of the same type. The same applies for a NULL pointer.
> +///
> +/// If `realloc` is called with:
> +///   - a size of zero, the given memory allocation, if any, must be freed
> +///   - a NULL pointer, a new memory allocation must be created
> +pub unsafe trait Allocator {
> +    /// Allocate memory based on `layout` and `flags`.
> +    ///
> +    /// On success, returns a buffer represented as `NonNull<[u8]>` that satisfies the layout
> +    /// constraints (i.e. minimum size and alignment as specified by `layout`).
> +    ///
> +    /// This function is equivalent to `realloc` when called with a NULL pointer.
> +    fn alloc(layout: Layout, flags: Flags) -> Result<NonNull<[u8]>, AllocError> {
> +        // SAFETY: Passing a NULL pointer to `realloc` is valid by it's safety requirements and asks
> +        // for a new memory allocation.
> +        unsafe { Self::realloc(None, layout, flags) }
> +    }
> +
> +    /// Re-allocate an existing memory allocation to satisfy the requested `layout`. If the
> +    /// requested size is zero, `realloc` behaves equivalent to `free`.
> +    ///
> +    /// If the requested size is larger than the size of the existing allocation, a successful call
> +    /// to `realloc` guarantees that the new or grown buffer has at least `Layout::size` bytes, but
> +    /// may also be larger.
> +    ///
> +    /// If the requested size is smaller than the size of the existing allocation, `realloc` may or
> +    /// may not shrink the buffer; this is implementation specific to the allocator.
> +    ///
> +    /// On allocation failure, the existing buffer, if any, remains valid.
> +    ///
> +    /// The buffer is represented as `NonNull<[u8]>`.
> +    ///
> +    /// # Safety
> +    ///
> +    /// `Some(ptr)` must point to an existing and valid memory allocation created by this allocator

This is the wrong way around, `ptr: Option<NonNull<u8>>`, so
`Some(ptr): Option<Option<NonNull<u8>>>`. Instead I would write
"If `ptr = Some(p)`, then `p` must point to...".

> +    /// instance. The alignment encoded in `layout` must be smaller than or equal to the alignment
> +    /// requested in the previous `alloc` or `realloc` call of the same allocation.
> +    ///
> +    /// Additionally, `ptr` is allowed to be `None`; in this case a new memory allocation is
> +    /// created.
> +    ///
> +    unsafe fn realloc(
> +        ptr: Option<NonNull<u8>>,
> +        layout: Layout,
> +        flags: Flags,
> +    ) -> Result<NonNull<[u8]>, AllocError>;
> +
> +    /// Free an existing memory allocation.
> +    ///
> +    /// # Safety
> +    ///
> +    /// `ptr` must point to an existing and valid memory allocation created by this `Allocator`
> +    /// instance.

Additionally, you need "The memory allocation at `ptr` must never again
be read from or written to.".

---
Cheers,
Benno

> +    unsafe fn free(ptr: NonNull<u8>) {
> +        // SAFETY: `ptr` is guaranteed to be previously allocated with this `Allocator` or NULL.
> +        // Calling `realloc` with a buffer size of zero, frees the buffer `ptr` points to.
> +        let _ = unsafe { Self::realloc(Some(ptr), Layout::new::<()>(), Flags(0)) };
> +    }
> +}
> --
> 2.45.2
> 


^ permalink raw reply	[flat|nested] 100+ messages in thread

* Re: [PATCH v4 02/28] rust: alloc: separate `aligned_size` from `krealloc_aligned`
  2024-08-05 15:19 ` [PATCH v4 02/28] rust: alloc: separate `aligned_size` from `krealloc_aligned` Danilo Krummrich
@ 2024-08-06 16:06   ` Benno Lossin
  0 siblings, 0 replies; 100+ messages in thread
From: Benno Lossin @ 2024-08-06 16: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, acurrid, cjia, jhubbard, airlied, ajanulgu, lyude,
	linux-kernel, rust-for-linux, linux-mm

On 05.08.24 17:19, Danilo Krummrich wrote:
> Separate `aligned_size` from `krealloc_aligned`.
> 
> Subsequent patches implement `Allocator` derivates, such as `Kmalloc`,
> that require `aligned_size` and replace the original `krealloc_aligned`.
> 
> Reviewed-by: Alice Ryhl <aliceryhl@google.com>
> Signed-off-by: Danilo Krummrich <dakr@kernel.org>
> ---
>  rust/kernel/alloc/allocator.rs | 27 ++++++++++++++++++---------
>  1 file changed, 18 insertions(+), 9 deletions(-)
> 
> diff --git a/rust/kernel/alloc/allocator.rs b/rust/kernel/alloc/allocator.rs
> index e6ea601f38c6..e7b7eba84acb 100644
> --- a/rust/kernel/alloc/allocator.rs
> +++ b/rust/kernel/alloc/allocator.rs
> @@ -8,27 +8,36 @@
> 
>  struct KernelAllocator;
> 
> -/// Calls `krealloc` with a proper size to alloc a new object aligned to `new_layout`'s alignment.
> -///
> -/// # Safety
> -///
> -/// - `ptr` can be either null or a pointer which has been allocated by this allocator.
> -/// - `new_layout` must have a non-zero size.
> -pub(crate) unsafe fn krealloc_aligned(ptr: *mut u8, new_layout: Layout, flags: Flags) -> *mut u8 {
> +/// Returns a proper size to alloc a new object aligned to `new_layout`'s alignment.
> +fn aligned_size(new_layout: Layout) -> usize {
>      // Customized layouts from `Layout::from_size_align()` can have size < align, so pad first.
>      let layout = new_layout.pad_to_align();
> 
>      // Note that `layout.size()` (after padding) is guaranteed to be a multiple of `layout.align()`
>      // which together with the slab guarantees means the `krealloc` will return a properly aligned
>      // object (see comments in `kmalloc()` for more information).
> -    let size = layout.size();
> +    layout.size()
> +}
> 
> +/// Calls `krealloc` with a proper size to alloc a new object aligned to `new_layout`'s alignment.
> +///
> +/// # Safety
> +///
> +/// - `ptr` can be either null or a pointer which has been allocated by this allocator.
> +/// - `new_layout` must have a non-zero size.
> +pub(crate) unsafe fn krealloc_aligned(ptr: *mut u8, new_layout: Layout, flags: Flags) -> *mut u8 {
>      // SAFETY:
>      // - `ptr` is either null or a pointer returned from a previous `k{re}alloc()` by the
>      //   function safety requirement.
>      // - `size` is greater than 0 since it's from `layout.size()` (which cannot be zero according
>      //   to the function safety requirement)
> -    unsafe { bindings::krealloc(ptr as *const core::ffi::c_void, size, flags.0) as *mut u8 }
> +    unsafe {
> +        bindings::krealloc(
> +            ptr as *const core::ffi::c_void,
> +            aligned_size(new_layout),

Can you move the safe operation outside of the `unsafe` block?

With that changed,

Reviewed-by: Benno Lossin <benno.lossin@proton.me>

---
Cheers,
Benno

> +            flags.0,
> +        ) as *mut u8
> +    }
>  }
> 
>  unsafe impl GlobalAlloc for KernelAllocator {
> --
> 2.45.2
> 


^ permalink raw reply	[flat|nested] 100+ messages in thread

* Re: [PATCH v4 03/28] rust: alloc: rename `KernelAllocator` to `Kmalloc`
  2024-08-05 15:19 ` [PATCH v4 03/28] rust: alloc: rename `KernelAllocator` to `Kmalloc` Danilo Krummrich
@ 2024-08-06 16:07   ` Benno Lossin
  2024-08-07 18:22   ` Gary Guo
  1 sibling, 0 replies; 100+ messages in thread
From: Benno Lossin @ 2024-08-06 16: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, acurrid, cjia, jhubbard, airlied, ajanulgu, lyude,
	linux-kernel, rust-for-linux, linux-mm

On 05.08.24 17:19, Danilo Krummrich wrote:
> Subsequent patches implement `Vmalloc` and `KVmalloc` allocators, hence
> align `KernelAllocator` to this naming scheme.
> 
> Reviewed-by: Alice Ryhl <aliceryhl@google.com>
> Signed-off-by: Danilo Krummrich <dakr@kernel.org>
> ---
>  rust/kernel/alloc/allocator.rs | 6 +++---
>  1 file changed, 3 insertions(+), 3 deletions(-)

Reviewed-by: Benno Lossin <benno.lossin@proton.me>

---
Cheers,
Benno


^ permalink raw reply	[flat|nested] 100+ messages in thread

* Re: [PATCH v4 04/28] rust: alloc: implement `Allocator` for `Kmalloc`
  2024-08-05 15:19 ` [PATCH v4 04/28] rust: alloc: implement `Allocator` for `Kmalloc` Danilo Krummrich
@ 2024-08-06 16:51   ` Benno Lossin
  2024-08-06 18:55     ` Danilo Krummrich
  0 siblings, 1 reply; 100+ messages in thread
From: Benno Lossin @ 2024-08-06 16:51 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, acurrid, cjia, jhubbard, airlied, ajanulgu, lyude,
	linux-kernel, rust-for-linux, linux-mm

On 05.08.24 17:19, Danilo Krummrich wrote:
> 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/helpers.c                 |  3 +-
>  rust/kernel/alloc.rs           |  2 +-
>  rust/kernel/alloc/allocator.rs | 68 +++++++++++++++++++++++++++++++++-
>  3 files changed, 69 insertions(+), 4 deletions(-)
> 
> diff --git a/rust/helpers.c b/rust/helpers.c
> index 92d3c03ae1bd..9f7275493365 100644
> --- a/rust/helpers.c
> +++ b/rust/helpers.c
> @@ -193,8 +193,7 @@ void rust_helper_init_work_with_key(struct work_struct *work, work_func_t func,
>  }
>  EXPORT_SYMBOL_GPL(rust_helper_init_work_with_key);
> 
> -void * __must_check __realloc_size(2)
> -rust_helper_krealloc(const void *objp, size_t new_size, gfp_t flags)
> +void *rust_helper_krealloc(const void *objp, size_t new_size, gfp_t flags)
>  {
>  	return krealloc(objp, new_size, flags);
>  }
> diff --git a/rust/kernel/alloc.rs b/rust/kernel/alloc.rs
> index 8a71a589469d..bc01a17df5e0 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 2c1eae25da84..c6ad1dd59dd0 100644
> --- a/rust/kernel/alloc/allocator.rs
> +++ b/rust/kernel/alloc/allocator.rs
> @@ -5,8 +5,16 @@
>  use super::{flags::*, Flags};
>  use core::alloc::{GlobalAlloc, Layout};
>  use core::ptr;
> +use core::ptr::NonNull;
> 
> -struct Kmalloc;
> +use crate::alloc::{AllocError, Allocator};
> +use crate::bindings;
> +
> +/// The contiguous kernel allocator.
> +///
> +/// The contiguous kernel allocator only ever allocates physically contiguous memory through
> +/// `bindings::krealloc`.
> +pub struct Kmalloc;
> 
>  /// Returns a proper size to alloc a new object aligned to `new_layout`'s alignment.
>  fn aligned_size(new_layout: Layout) -> usize {
> @@ -40,6 +48,64 @@ pub(crate) unsafe fn krealloc_aligned(ptr: *mut u8, new_layout: Layout, flags: F
>      }
>  }
> 
> +/// # Invariants
> +///
> +/// One of the following `krealloc`, `vrealloc`, `kvrealloc`.
> +struct ReallocFunc(
> +    unsafe extern "C" fn(*const core::ffi::c_void, usize, u32) -> *mut core::ffi::c_void,
> +);
> +
> +impl ReallocFunc {
> +    // INVARIANT: `krealloc` satisfies the type invariants.

This INVARIANT comment should be moved one line downwards.

> +    fn krealloc() -> Self {
> +        Self(bindings::krealloc)
> +    }
> +
> +    /// # Safety
> +    ///
> +    /// This method has the exact same safety requirements as `Allocator::realloc`.

I would remove "exact", I don't think we want to mean "almost the same"
when we write just "same".

> +    unsafe fn call(
> +        &self,
> +        ptr: Option<NonNull<u8>>,
> +        layout: Layout,
> +        flags: Flags,
> +    ) -> Result<NonNull<[u8]>, AllocError> {
> +        let size = aligned_size(layout);
> +        let ptr = match ptr {
> +            Some(ptr) => ptr.as_ptr(),
> +            None => ptr::null(),
> +        };
> +
> +        // SAFETY: `ptr` is valid by the safety requirements of this function.

"`ptr` is either NULL or valid by the safety requirements of this
function."

> +        let raw_ptr = unsafe {
> +            // If `size == 0` and `ptr != NULL` the memory behind the pointer is freed.
> +            self.0(ptr.cast(), size, flags.0).cast()
> +        };
> +
> +        let ptr = if size == 0 {
> +            NonNull::dangling()

If we call `realloc(Some(ptr), <layout with size = 0>, ...)`, then this
leaks the pointer returned by the call to `self.0` above. I don't know
what the return value of the different functions are that can appear in
`self.0`, do they return NULL?

What about the following sequence:

    let ptr = realloc(None, <layout with size = 0>, ...);
    let ptr = realloc(Some(ptr), <layout with size = 0>, ...);

Then the above call to `self.0` is done with a dangling pointer, can the
functions that appear in `self.0` handle that?

> +        } else {
> +            NonNull::new(raw_ptr).ok_or(AllocError)?
> +        };
> +
> +        Ok(NonNull::slice_from_raw_parts(ptr, size))
> +    }
> +}
> +
> +unsafe impl Allocator for Kmalloc {
> +    unsafe fn realloc(
> +        ptr: Option<NonNull<u8>>,
> +        layout: Layout,
> +        flags: Flags,
> +    ) -> Result<NonNull<[u8]>, AllocError> {
> +        let realloc = ReallocFunc::krealloc();
> +
> +        // SAFETY: If not `None`, `ptr` is guaranteed to point to valid memory, which was previously
> +        // allocated with this `Allocator`.

What about the other requirements? (they should be satisfied, since they
are also requirements for calling this function)

> +        unsafe { realloc.call(ptr, layout, flags) }

If you make `ReallocFunc::krealloc()` into a constant
`ReallocFunc::KREALLOC`, then we could avoid the let binding above.

---
Cheers,
Benno

> +    }
> +}
> +
>  unsafe impl GlobalAlloc for Kmalloc {
>      unsafe fn alloc(&self, layout: Layout) -> *mut u8 {
>          // SAFETY: `ptr::null_mut()` is null and `layout` has a non-zero size by the function safety
> --
> 2.45.2
> 


^ permalink raw reply	[flat|nested] 100+ messages in thread

* Re: [PATCH v4 05/28] rust: alloc: add module `allocator_test`
  2024-08-05 15:19 ` [PATCH v4 05/28] rust: alloc: add module `allocator_test` Danilo Krummrich
@ 2024-08-06 16:54   ` Benno Lossin
  2024-08-06 18:58     ` Danilo Krummrich
  0 siblings, 1 reply; 100+ messages in thread
From: Benno Lossin @ 2024-08-06 16:54 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, acurrid, cjia, jhubbard, airlied, ajanulgu, lyude,
	linux-kernel, rust-for-linux, linux-mm

On 05.08.24 17:19, Danilo Krummrich wrote:
> `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.

This is confusing, since you are talking about both our new `Allocator`
trait, allocators and the `alloc` crate `Allocator`.

> Reviewed-by: Alice Ryhl <aliceryhl@google.com>
> 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 bc01a17df5e0..942e2755f217 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!();

Does `build_error!()` also work? If yes, then I would prefer that.

---
Cheers,
Benno

> +    }
> +}
> --
> 2.45.2
> 


^ permalink raw reply	[flat|nested] 100+ messages in thread

* Re: [PATCH v4 06/28] rust: alloc: implement `Vmalloc` allocator
  2024-08-05 15:19 ` [PATCH v4 06/28] rust: alloc: implement `Vmalloc` allocator Danilo Krummrich
@ 2024-08-06 17:00   ` Benno Lossin
  2024-08-06 19:01     ` Danilo Krummrich
  0 siblings, 1 reply; 100+ messages in thread
From: Benno Lossin @ 2024-08-06 17: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, acurrid, cjia, jhubbard, airlied, ajanulgu, lyude,
	linux-kernel, rust-for-linux, linux-mm

On 05.08.24 17:19, 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.c                      |  7 +++++++
>  rust/kernel/alloc/allocator.rs      | 32 +++++++++++++++++++++++++++++
>  rust/kernel/alloc/allocator_test.rs |  1 +
>  3 files changed, 40 insertions(+)
> 
> diff --git a/rust/helpers.c b/rust/helpers.c
> index 9f7275493365..7406943f887d 100644
> --- a/rust/helpers.c
> +++ b/rust/helpers.c
> @@ -33,6 +33,7 @@
>  #include <linux/sched/signal.h>
>  #include <linux/slab.h>
>  #include <linux/spinlock.h>
> +#include <linux/vmalloc.h>
>  #include <linux/wait.h>
>  #include <linux/workqueue.h>
> 
> @@ -199,6 +200,12 @@ void *rust_helper_krealloc(const void *objp, size_t new_size, gfp_t flags)
>  }
>  EXPORT_SYMBOL_GPL(rust_helper_krealloc);
> 
> +void *rust_helper_vrealloc(const void *p, size_t size, gfp_t flags)
> +{
> +	return vrealloc(p, size, flags);
> +}
> +EXPORT_SYMBOL_GPL(rust_helper_vrealloc);
> +
>  /*
>   * `bindgen` binds the C `size_t` type as the Rust `usize` type, so we can
>   * use it in contexts where Rust expects a `usize` like slice (array) indices.
> diff --git a/rust/kernel/alloc/allocator.rs b/rust/kernel/alloc/allocator.rs
> index c6ad1dd59dd0..bb55895cbd03 100644
> --- a/rust/kernel/alloc/allocator.rs
> +++ b/rust/kernel/alloc/allocator.rs
> @@ -9,6 +9,7 @@
> 
>  use crate::alloc::{AllocError, Allocator};
>  use crate::bindings;
> +use crate::pr_warn;
> 
>  /// The contiguous kernel allocator.
>  ///
> @@ -16,6 +17,12 @@
>  /// `bindings::krealloc`.
>  pub struct Kmalloc;
> 
> +/// The virtually contiguous kernel allocator.
> +///
> +/// The vmalloc allocator allocates pages from the page level allocator and maps them into the
> +/// contiguous kernel virtual space.
> +pub struct Vmalloc;

One thing that I should also have mentioned for `Kmalloc`, do we want
these types to also have values? I don't think that we need them to be,
so we could declare them as `pub enum Vmalloc {}`.

> +
>  /// Returns a proper size to alloc a new object aligned to `new_layout`'s alignment.
>  fn aligned_size(new_layout: Layout) -> usize {
>      // Customized layouts from `Layout::from_size_align()` can have size < align, so pad first.
> @@ -61,6 +68,11 @@ fn krealloc() -> Self {
>          Self(bindings::krealloc)
>      }
> 
> +    // INVARIANT: `vrealloc` satisfies the type invariants.

This should be moved down a line.

---
Cheers,
Benno

> +    fn vrealloc() -> Self {
> +        Self(bindings::vrealloc)
> +    }
> +
>      /// # Safety
>      ///
>      /// This method has the exact same safety requirements as `Allocator::realloc`.
> @@ -141,6 +153,26 @@ unsafe fn alloc_zeroed(&self, layout: Layout) -> *mut u8 {
>      }
>  }
> 
> +unsafe impl Allocator for Vmalloc {
> +    unsafe fn realloc(
> +        ptr: Option<NonNull<u8>>,
> +        layout: Layout,
> +        flags: Flags,
> +    ) -> Result<NonNull<[u8]>, AllocError> {
> +        let realloc = ReallocFunc::vrealloc();
> +
> +        // TODO: Support alignments larger than PAGE_SIZE.
> +        if layout.align() > bindings::PAGE_SIZE {
> +            pr_warn!("Vmalloc does not support alignments larger than PAGE_SIZE yet.\n");
> +            return Err(AllocError);
> +        }
> +
> +        // SAFETY: If not `None`, `ptr` is guaranteed to point to valid memory, which was previously
> +        // allocated with this `Allocator`.
> +        unsafe { realloc.call(ptr, layout, flags) }

I am a bit confused, for `Kmalloc`, you manually returned
`NonNull::dangling` when allocating a zero-sized allocation, but here
you don't?

---
Cheers,
Benno

> +    }
> +}
> +
>  #[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.45.2
> 


^ permalink raw reply	[flat|nested] 100+ messages in thread

* Re: [PATCH v4 08/28] rust: types: implement `Unique<T>`
  2024-08-05 15:19 ` [PATCH v4 08/28] rust: types: implement `Unique<T>` Danilo Krummrich
@ 2024-08-06 17:22   ` Benno Lossin
  2024-08-06 17:28     ` Miguel Ojeda
  2024-08-06 23:12     ` Danilo Krummrich
  0 siblings, 2 replies; 100+ messages in thread
From: Benno Lossin @ 2024-08-06 17:22 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, acurrid, cjia, jhubbard, airlied, ajanulgu, lyude,
	linux-kernel, rust-for-linux, linux-mm

On 05.08.24 17:19, Danilo Krummrich wrote:
> Implement the `Unique` type as a prerequisite for `Box` and `Vec`
> introduced in subsequent patches.
> 
> `Unique` serves as wrapper around a `NonNull`, but indicates that the
> possessor of this wrapper owns the referent.
> 
> This type already exists in Rust's core library, but, unfortunately, is
> exposed as unstable API and hence shouldn't be used in the kernel.
> 
> This implementation of `Unique` is almost identical, but mostly stripped
> down to the functionality we need for `Box` and `Vec`. Additionally, all
> unstable features are removed and / or replaced by stable ones.
> 
> Reviewed-by: Alice Ryhl <aliceryhl@google.com>
> Signed-off-by: Danilo Krummrich <dakr@kernel.org>
> ---
>  rust/kernel/types.rs | 183 +++++++++++++++++++++++++++++++++++++++++++
>  1 file changed, 183 insertions(+)
> 
> diff --git a/rust/kernel/types.rs b/rust/kernel/types.rs
> index bd189d646adb..7cf89067b5fc 100644
> --- a/rust/kernel/types.rs
> +++ b/rust/kernel/types.rs
> @@ -473,3 +473,186 @@ unsafe impl AsBytes for str {}
>  // does not have any uninitialized portions either.
>  unsafe impl<T: AsBytes> AsBytes for [T] {}
>  unsafe impl<T: AsBytes, const N: usize> AsBytes for [T; N] {}
> +
> +/// A wrapper around a raw non-null `*mut T` that indicates that the possessor
> +/// of this wrapper owns the referent. Useful for building abstractions like
> +/// `Box<T>`, `Vec<T>`, `String`, and `HashMap<K, V>`.
> +///
> +/// Unlike `*mut T`, `Unique<T>` behaves "as if" it were an instance of `T`.
> +/// It implements `Send`/`Sync` if `T` is `Send`/`Sync`. It also implies
> +/// the kind of strong aliasing guarantees an instance of `T` can expect:
> +/// the referent of the pointer should not be modified without a unique path to
> +/// its owning Unique.
> +///
> +/// If you're uncertain of whether it's correct to use `Unique` for your purposes,
> +/// consider using `NonNull`, which has weaker semantics.
> +///
> +/// Unlike `*mut T`, the pointer must always be non-null, even if the pointer
> +/// is never dereferenced. This is so that enums may use this forbidden value
> +/// as a discriminant -- `Option<Unique<T>>` has the same size as `Unique<T>`.
> +/// However the pointer may still dangle if it isn't dereferenced.
> +///
> +/// Unlike `*mut T`, `Unique<T>` is covariant over `T`. This should always be correct
> +/// for any type which upholds Unique's aliasing requirements.
> +#[repr(transparent)]
> +pub struct Unique<T: ?Sized> {
> +    pointer: NonNull<T>,
> +    // NOTE: this marker has no consequences for variance, but is necessary
> +    // for dropck to understand that we logically own a `T`.
> +    //
> +    // For details, see:
> +    // https://github.com/rust-lang/rfcs/blob/master/text/0769-sound-generic-drop.md#phantom-data
> +    _marker: PhantomData<T>,
> +}
> +
> +/// `Unique` pointers are `Send` if `T` is `Send` because the data they
> +/// reference is unaliased. Note that this aliasing invariant is
> +/// unenforced by the type system; the abstraction using the
> +/// `Unique` must enforce it.
> +unsafe impl<T: Send + ?Sized> Send for Unique<T> {}
> +
> +/// `Unique` pointers are `Sync` if `T` is `Sync` because the data they
> +/// reference is unaliased. Note that this aliasing invariant is
> +/// unenforced by the type system; the abstraction using the
> +/// `Unique` must enforce it.
> +unsafe impl<T: Sync + ?Sized> Sync for Unique<T> {}
> +
> +impl<T: Sized> Unique<T> {
> +    /// Creates a new `Unique` that is dangling, but well-aligned.
> +    ///
> +    /// This is useful for initializing types which lazily allocate, like
> +    /// `Vec::new` does.
> +    ///
> +    /// Note that the pointer value may potentially represent a valid pointer to
> +    /// a `T`, which means this must not be used as a "not yet initialized"
> +    /// sentinel value. Types that lazily allocate must track initialization by
> +    /// some other means.
> +    #[must_use]
> +    #[inline]
> +    pub const fn dangling() -> Self {
> +        Unique {
> +            pointer: NonNull::dangling(),
> +            _marker: PhantomData,
> +        }
> +    }

I think I already asked this, but the code until this point is copied
from the rust stdlib and nowhere cited, does that work with the
licensing?

I also think that the code above could use some improvements:
- add an `# Invariants` section with appropriate invariants (what are
  they supposed to be?)
- Do we really want this type to be public and exported from the kernel
  crate? I think it would be better if it were crate-private.
- What do we gain from having this type? As I learned recently, the
  `Unique` type from `core` doesn't actually put the `noalias` onto
  `Box` and `Vec`. The functions are mostly delegations to `NonNull`, so
  if the only advantages are that `Send` and `Sync` are already
  implemented, then I think we should drop this.

> +}
> +
> +impl<T: ?Sized> Unique<T> {
> +    /// Creates a new `Unique`.
> +    ///
> +    /// # Safety
> +    ///
> +    /// `ptr` must be non-null.
> +    #[inline]
> +    pub const unsafe fn new_unchecked(ptr: *mut T) -> Self {
> +        // SAFETY: the caller must guarantee that `ptr` is non-null.
> +        unsafe {

The only unsafe operation in the body is `new_unchecked` only that one
should be wrapped in `unsafe {}`.

> +            Unique {
> +                pointer: NonNull::new_unchecked(ptr),
> +                _marker: PhantomData,
> +            }
> +        }
> +    }
> +
> +    /// Creates a new `Unique` if `ptr` is non-null.
> +    #[allow(clippy::manual_map)]
> +    #[inline]
> +    pub fn new(ptr: *mut T) -> Option<Self> {
> +        if let Some(pointer) = NonNull::new(ptr) {
> +            Some(Unique {
> +                pointer,
> +                _marker: PhantomData,
> +            })
> +        } else {
> +            None
> +        }

Why is this so verbose? You even needed to disable the clippy lint!
Can't this just be?:

    Some(Unique {
        pointer: NonNull::new(ptr)?,
        _marker: PhantomData,
    })

or maybe even

    NonNull::new(ptr).map(Unique::from)


> +    }
> +
> +    /// Acquires the underlying `*mut` pointer.
> +    #[must_use = "`self` will be dropped if the result is not used"]

This seems like an odd thing, there is no `Drop` impl that drops the
pointee...

---
Cheers,
Benno


^ permalink raw reply	[flat|nested] 100+ messages in thread

* Re: [PATCH v4 08/28] rust: types: implement `Unique<T>`
  2024-08-06 17:22   ` Benno Lossin
@ 2024-08-06 17:28     ` Miguel Ojeda
  2024-08-06 23:16       ` Danilo Krummrich
  2024-08-06 23:12     ` Danilo Krummrich
  1 sibling, 1 reply; 100+ messages in thread
From: Miguel Ojeda @ 2024-08-06 17:28 UTC (permalink / raw)
  To: Benno Lossin
  Cc: Danilo Krummrich, ojeda, alex.gaynor, wedsonaf, boqun.feng, gary,
	bjorn3_gh, a.hindborg, aliceryhl, akpm, daniel.almeida,
	faith.ekstrand, boris.brezillon, lina, mcanal, zhiw, acurrid,
	cjia, jhubbard, airlied, ajanulgu, lyude, linux-kernel,
	rust-for-linux, linux-mm

On Tue, Aug 6, 2024 at 7:22 PM Benno Lossin <benno.lossin@proton.me> wrote:
>
> I think I already asked this, but the code until this point is copied
> from the rust stdlib and nowhere cited, does that work with the
> licensing?

No, it doesn't. They should be put into its own file with the right
license and a paragraph about it, e.g. the other `std_vendor.rs` files
we have (if it is just about `Unique`, it could go in the existing one
at the `kernel` level).

Cheers,
Miguel

^ permalink raw reply	[flat|nested] 100+ messages in thread

* Re: [PATCH v4 01/28] rust: alloc: add `Allocator` trait
  2024-08-06 16:03   ` Benno Lossin
@ 2024-08-06 18:30     ` Danilo Krummrich
  2024-08-06 20:04       ` Benno Lossin
  0 siblings, 1 reply; 100+ messages in thread
From: Danilo Krummrich @ 2024-08-06 18:30 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, acurrid, cjia, jhubbard,
	airlied, ajanulgu, lyude, linux-kernel, rust-for-linux, linux-mm

On Tue, Aug 06, 2024 at 04:03:49PM +0000, Benno Lossin wrote:
> On 05.08.24 17:19, Danilo Krummrich 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`.
> > 
> > Signed-off-by: Danilo Krummrich <dakr@kernel.org>
> > ---
> >  rust/kernel/alloc.rs | 79 ++++++++++++++++++++++++++++++++++++++++++++
> >  1 file changed, 79 insertions(+)
> > 
> > diff --git a/rust/kernel/alloc.rs b/rust/kernel/alloc.rs
> > index 1966bd407017..8a71a589469d 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,81 @@ pub mod flags {
> >      /// small allocations.
> >      pub const GFP_NOWAIT: Flags = Flags(bindings::GFP_NOWAIT);
> >  }
> > +
> > +/// The kernel's [`Allocator`] trait.
> > +///
> > +/// An implementation of [`Allocator`] can allocate, re-allocate and free memory buffer described
> > +/// via [`Layout`].
> > +///
> > +/// [`Allocator`] is designed to be implemented as a ZST; [`Allocator`] functions do not operate on
> > +/// an object instance.
> 
> This will prevent us from implementing arena-type allocators [^1]. Do we
> want/need those?

I'm not aware of any code in the kernel that does exactly this, but kmem_cache
is rather close to that.

> I have heard that some people use them in embedded systems, but I can't 
> say for sure. But this is a rather big design decision, so we should
> discuss it now.
> 
> [^1]: For those who don't know what I mean by that here is a quick
> sketch (without handling flags and optimizations):
> 
>     pub struct ArenaAlloc<const SIZE: usize> {
>         memory: Opaque<[u8; SIZE]>,
>         head: Cell<usize>,
>     }
> 
>     impl<const SIZE: usize> ArenaAlloc<SIZE> {
>         pub fn new() -> Self {
>             Self {
>                 memory: Opaque::uninit(),
>                 head: 0,
>             }
>         }
>     }
> 
>     impl<const SIZE: usize> Allocator for ArenaAlloc<SIZE> {
>         fn alloc(&self, layout: Layout, _flags: Flags) -> Result<NonNull<u8>, AllocError> {
>             let head = self.head.get();
>             if head + layout.size() >= SIZE {
>                 return Err(AllocError);
>             }
>             let ptr = self.memory.get();
>             let ptr = ptr.cast::<u8>();
>             let ptr = unsafe { ptr.add(head) };
>             self.head.set(head + layout.size());
>             unsafe { NonNull::new_unchecked(ptr) }
>         }
> 
>         unsafe fn realloc(
>             &self,
>             ptr: Option<NonNull<u8>>,
>             old_layout: Layout, // Note that we also need `old_layout`!
>             layout: Layout,
>             flags: Flags
>         ) -> Result<NonNull<u8>, AllocError> {
>             let new = self.alloc(layout, flags)?;
>             let Some(ptr) = ptr else { return Ok(new); };
>             unsafe { core::ptr::copy_nonoverlapping(ptr.as_ptr(), new.as_ptr(), old_layout.size()) };
>             self.free(ptr);
>             Ok(new)
>         }
> 
>         fn free(&self, ptr: NonNull<u8>) { /* noop */ }
>     }
> 
> > +///
> > +/// In order to be able to support `#[derive(SmartPointer)]` later on, we need to avoid a design
> > +/// that requires an `Allocator` to be instantiated, hence its functions must not contain any kind
> > +/// of `self` parameter.
> 
> Ah I see, so since `#[derive(SmartPointer)]` needs `Box` to only consist
> of one non ZST field... I skimmed the RFC discussion and it seems like a
> problem that *might* be solved in the future, but probably not in the
> (very) near future. I guess this is just a bullet that we have to bite.
> We can always have an `ArenaBox` that can deal with that (although
> without `DispatchFromDyn`).
> We should revisit this when `#[derive(SmartPointer)]` becomes advanced
> enough.

Agreed.

> 
> > +///
> > +/// # Safety
> > +///
> > +/// Memory returned from an allocator must point to a valid memory buffer and remain valid until
> > +/// it is explicitly freed.
> > +///
> > +/// Any pointer to a memory buffer which is currently allocated must be valid to be passed to any
> > +/// other [`Allocator`] function of the same type. The same applies for a NULL pointer.
> > +///
> > +/// If `realloc` is called with:
> > +///   - a size of zero, the given memory allocation, if any, must be freed
> > +///   - a NULL pointer, a new memory allocation must be created
> > +pub unsafe trait Allocator {
> > +    /// Allocate memory based on `layout` and `flags`.
> > +    ///
> > +    /// On success, returns a buffer represented as `NonNull<[u8]>` that satisfies the layout
> > +    /// constraints (i.e. minimum size and alignment as specified by `layout`).
> > +    ///
> > +    /// This function is equivalent to `realloc` when called with a NULL pointer.
> > +    fn alloc(layout: Layout, flags: Flags) -> Result<NonNull<[u8]>, AllocError> {
> > +        // SAFETY: Passing a NULL pointer to `realloc` is valid by it's safety requirements and asks
> > +        // for a new memory allocation.
> > +        unsafe { Self::realloc(None, layout, flags) }
> > +    }
> > +
> > +    /// Re-allocate an existing memory allocation to satisfy the requested `layout`. If the
> > +    /// requested size is zero, `realloc` behaves equivalent to `free`.
> > +    ///
> > +    /// If the requested size is larger than the size of the existing allocation, a successful call
> > +    /// to `realloc` guarantees that the new or grown buffer has at least `Layout::size` bytes, but
> > +    /// may also be larger.
> > +    ///
> > +    /// If the requested size is smaller than the size of the existing allocation, `realloc` may or
> > +    /// may not shrink the buffer; this is implementation specific to the allocator.
> > +    ///
> > +    /// On allocation failure, the existing buffer, if any, remains valid.
> > +    ///
> > +    /// The buffer is represented as `NonNull<[u8]>`.
> > +    ///
> > +    /// # Safety
> > +    ///
> > +    /// `Some(ptr)` must point to an existing and valid memory allocation created by this allocator
> 
> This is the wrong way around, `ptr: Option<NonNull<u8>>`, so
> `Some(ptr): Option<Option<NonNull<u8>>>`. Instead I would write
> "If `ptr = Some(p)`, then `p` must point to...".

Yes, makes sense.

> 
> > +    /// instance. The alignment encoded in `layout` must be smaller than or equal to the alignment
> > +    /// requested in the previous `alloc` or `realloc` call of the same allocation.
> > +    ///
> > +    /// Additionally, `ptr` is allowed to be `None`; in this case a new memory allocation is
> > +    /// created.
> > +    ///
> > +    unsafe fn realloc(
> > +        ptr: Option<NonNull<u8>>,
> > +        layout: Layout,
> > +        flags: Flags,
> > +    ) -> Result<NonNull<[u8]>, AllocError>;
> > +
> > +    /// Free an existing memory allocation.
> > +    ///
> > +    /// # Safety
> > +    ///
> > +    /// `ptr` must point to an existing and valid memory allocation created by this `Allocator`
> > +    /// instance.
> 
> Additionally, you need "The memory allocation at `ptr` must never again
> be read from or written to.".

I'm fine adding it, but I wonder if technically this is really required? The
condition whether the pointer is ever accessed again in any way is not relevant
in terms of being a precondition for `free` not causing UB, right?

> 
> ---
> Cheers,
> Benno
> 
> > +    unsafe fn free(ptr: NonNull<u8>) {
> > +        // SAFETY: `ptr` is guaranteed to be previously allocated with this `Allocator` or NULL.
> > +        // Calling `realloc` with a buffer size of zero, frees the buffer `ptr` points to.
> > +        let _ = unsafe { Self::realloc(Some(ptr), Layout::new::<()>(), Flags(0)) };
> > +    }
> > +}
> > --
> > 2.45.2
> > 
> 

^ permalink raw reply	[flat|nested] 100+ messages in thread

* Re: [PATCH v4 04/28] rust: alloc: implement `Allocator` for `Kmalloc`
  2024-08-06 16:51   ` Benno Lossin
@ 2024-08-06 18:55     ` Danilo Krummrich
  2024-08-07  7:14       ` Benno Lossin
  0 siblings, 1 reply; 100+ messages in thread
From: Danilo Krummrich @ 2024-08-06 18:55 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, acurrid, cjia, jhubbard,
	airlied, ajanulgu, lyude, linux-kernel, rust-for-linux, linux-mm

On Tue, Aug 06, 2024 at 04:51:28PM +0000, Benno Lossin wrote:
> On 05.08.24 17:19, Danilo Krummrich wrote:
> > 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/helpers.c                 |  3 +-
> >  rust/kernel/alloc.rs           |  2 +-
> >  rust/kernel/alloc/allocator.rs | 68 +++++++++++++++++++++++++++++++++-
> >  3 files changed, 69 insertions(+), 4 deletions(-)
> > 
> > diff --git a/rust/helpers.c b/rust/helpers.c
> > index 92d3c03ae1bd..9f7275493365 100644
> > --- a/rust/helpers.c
> > +++ b/rust/helpers.c
> > @@ -193,8 +193,7 @@ void rust_helper_init_work_with_key(struct work_struct *work, work_func_t func,
> >  }
> >  EXPORT_SYMBOL_GPL(rust_helper_init_work_with_key);
> > 
> > -void * __must_check __realloc_size(2)
> > -rust_helper_krealloc(const void *objp, size_t new_size, gfp_t flags)
> > +void *rust_helper_krealloc(const void *objp, size_t new_size, gfp_t flags)
> >  {
> >  	return krealloc(objp, new_size, flags);
> >  }
> > diff --git a/rust/kernel/alloc.rs b/rust/kernel/alloc.rs
> > index 8a71a589469d..bc01a17df5e0 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 2c1eae25da84..c6ad1dd59dd0 100644
> > --- a/rust/kernel/alloc/allocator.rs
> > +++ b/rust/kernel/alloc/allocator.rs
> > @@ -5,8 +5,16 @@
> >  use super::{flags::*, Flags};
> >  use core::alloc::{GlobalAlloc, Layout};
> >  use core::ptr;
> > +use core::ptr::NonNull;
> > 
> > -struct Kmalloc;
> > +use crate::alloc::{AllocError, Allocator};
> > +use crate::bindings;
> > +
> > +/// The contiguous kernel allocator.
> > +///
> > +/// The contiguous kernel allocator only ever allocates physically contiguous memory through
> > +/// `bindings::krealloc`.
> > +pub struct Kmalloc;
> > 
> >  /// Returns a proper size to alloc a new object aligned to `new_layout`'s alignment.
> >  fn aligned_size(new_layout: Layout) -> usize {
> > @@ -40,6 +48,64 @@ pub(crate) unsafe fn krealloc_aligned(ptr: *mut u8, new_layout: Layout, flags: F
> >      }
> >  }
> > 
> > +/// # Invariants
> > +///
> > +/// One of the following `krealloc`, `vrealloc`, `kvrealloc`.
> > +struct ReallocFunc(
> > +    unsafe extern "C" fn(*const core::ffi::c_void, usize, u32) -> *mut core::ffi::c_void,
> > +);
> > +
> > +impl ReallocFunc {
> > +    // INVARIANT: `krealloc` satisfies the type invariants.
> 
> This INVARIANT comment should be moved one line downwards.
> 
> > +    fn krealloc() -> Self {
> > +        Self(bindings::krealloc)
> > +    }
> > +
> > +    /// # Safety
> > +    ///
> > +    /// This method has the exact same safety requirements as `Allocator::realloc`.
> 
> I would remove "exact", I don't think we want to mean "almost the same"
> when we write just "same".
> 
> > +    unsafe fn call(
> > +        &self,
> > +        ptr: Option<NonNull<u8>>,
> > +        layout: Layout,
> > +        flags: Flags,
> > +    ) -> Result<NonNull<[u8]>, AllocError> {
> > +        let size = aligned_size(layout);
> > +        let ptr = match ptr {
> > +            Some(ptr) => ptr.as_ptr(),
> > +            None => ptr::null(),
> > +        };
> > +
> > +        // SAFETY: `ptr` is valid by the safety requirements of this function.
> 
> "`ptr` is either NULL or valid by the safety requirements of this
> function."

Agreed, for this one and the above ones.

> 
> > +        let raw_ptr = unsafe {
> > +            // If `size == 0` and `ptr != NULL` the memory behind the pointer is freed.
> > +            self.0(ptr.cast(), size, flags.0).cast()
> > +        };
> > +
> > +        let ptr = if size == 0 {
> > +            NonNull::dangling()
> 
> If we call `realloc(Some(ptr), <layout with size = 0>, ...)`, then this
> leaks the pointer returned by the call to `self.0` above. I don't know
> what the return value of the different functions are that can appear in
> `self.0`, do they return NULL?

That is fine, we don't care about the return value. All `ReallocFunc` free the
memory behind `ptr` if called with a size of zero. But to answer the question,
they return either NULL or ZERO_SIZE_PTR.

> 
> What about the following sequence:
> 
>     let ptr = realloc(None, <layout with size = 0>, ...);
>     let ptr = realloc(Some(ptr), <layout with size = 0>, ...);
> 
> Then the above call to `self.0` is done with a dangling pointer, can the
> functions that appear in `self.0` handle that?

This would be incorrect.

Calling `realloc(Some(ptr), <layout with size = 0>, ...)` frees the memory
behind `ptr`. This is guranteed behavior for all `ReallocFunc`s, i.e.
krealloc(), vrealloc(), kvrealloc().

> 
> > +        } else {
> > +            NonNull::new(raw_ptr).ok_or(AllocError)?
> > +        };
> > +
> > +        Ok(NonNull::slice_from_raw_parts(ptr, size))
> > +    }
> > +}
> > +
> > +unsafe impl Allocator for Kmalloc {
> > +    unsafe fn realloc(
> > +        ptr: Option<NonNull<u8>>,
> > +        layout: Layout,
> > +        flags: Flags,
> > +    ) -> Result<NonNull<[u8]>, AllocError> {
> > +        let realloc = ReallocFunc::krealloc();
> > +
> > +        // SAFETY: If not `None`, `ptr` is guaranteed to point to valid memory, which was previously
> > +        // allocated with this `Allocator`.
> 
> What about the other requirements? (they should be satisfied, since they
> are also requirements for calling this function)

Indeed, I think we should just write that by definition `Realloc::call` has the
same safety requirements as `Allocator::realloc`.

> 
> > +        unsafe { realloc.call(ptr, layout, flags) }
> 
> If you make `ReallocFunc::krealloc()` into a constant
> `ReallocFunc::KREALLOC`, then we could avoid the let binding above.

Agreed, sounds good.

> 
> ---
> Cheers,
> Benno
> 
> > +    }
> > +}
> > +
> >  unsafe impl GlobalAlloc for Kmalloc {
> >      unsafe fn alloc(&self, layout: Layout) -> *mut u8 {
> >          // SAFETY: `ptr::null_mut()` is null and `layout` has a non-zero size by the function safety
> > --
> > 2.45.2
> > 
> 

^ permalink raw reply	[flat|nested] 100+ messages in thread

* Re: [PATCH v4 05/28] rust: alloc: add module `allocator_test`
  2024-08-06 16:54   ` Benno Lossin
@ 2024-08-06 18:58     ` Danilo Krummrich
  2024-08-07  7:20       ` Benno Lossin
  0 siblings, 1 reply; 100+ messages in thread
From: Danilo Krummrich @ 2024-08-06 18:58 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, acurrid, cjia, jhubbard,
	airlied, ajanulgu, lyude, linux-kernel, rust-for-linux, linux-mm

On Tue, Aug 06, 2024 at 04:54:10PM +0000, Benno Lossin wrote:
> On 05.08.24 17:19, Danilo Krummrich wrote:
> > `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.
> 
> This is confusing, since you are talking about both our new `Allocator`
> trait, allocators and the `alloc` crate `Allocator`.

I never mention the `alloc` crate `Allocator` here.

> 
> > Reviewed-by: Alice Ryhl <aliceryhl@google.com>
> > 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 bc01a17df5e0..942e2755f217 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!();
> 
> Does `build_error!()` also work? If yes, then I would prefer that.

Probably, but it also probably doesn't matter too much. A later patch in the
series replaces this with a `Cmalloc` implementation and type aliases all kernel
allocators (e.g. `Kmalloc`) to it.

This allows us to use our kernel `Box` and `Vec` types in userspace tests
(rusttest target).

> 
> ---
> Cheers,
> Benno
> 
> > +    }
> > +}
> > --
> > 2.45.2
> > 
> 

^ permalink raw reply	[flat|nested] 100+ messages in thread

* Re: [PATCH v4 06/28] rust: alloc: implement `Vmalloc` allocator
  2024-08-06 17:00   ` Benno Lossin
@ 2024-08-06 19:01     ` Danilo Krummrich
  2024-08-07  7:23       ` Benno Lossin
  0 siblings, 1 reply; 100+ messages in thread
From: Danilo Krummrich @ 2024-08-06 19:01 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, acurrid, cjia, jhubbard,
	airlied, ajanulgu, lyude, linux-kernel, rust-for-linux, linux-mm

On Tue, Aug 06, 2024 at 05:00:24PM +0000, Benno Lossin wrote:
> On 05.08.24 17:19, 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.c                      |  7 +++++++
> >  rust/kernel/alloc/allocator.rs      | 32 +++++++++++++++++++++++++++++
> >  rust/kernel/alloc/allocator_test.rs |  1 +
> >  3 files changed, 40 insertions(+)
> > 
> > diff --git a/rust/helpers.c b/rust/helpers.c
> > index 9f7275493365..7406943f887d 100644
> > --- a/rust/helpers.c
> > +++ b/rust/helpers.c
> > @@ -33,6 +33,7 @@
> >  #include <linux/sched/signal.h>
> >  #include <linux/slab.h>
> >  #include <linux/spinlock.h>
> > +#include <linux/vmalloc.h>
> >  #include <linux/wait.h>
> >  #include <linux/workqueue.h>
> > 
> > @@ -199,6 +200,12 @@ void *rust_helper_krealloc(const void *objp, size_t new_size, gfp_t flags)
> >  }
> >  EXPORT_SYMBOL_GPL(rust_helper_krealloc);
> > 
> > +void *rust_helper_vrealloc(const void *p, size_t size, gfp_t flags)
> > +{
> > +	return vrealloc(p, size, flags);
> > +}
> > +EXPORT_SYMBOL_GPL(rust_helper_vrealloc);
> > +
> >  /*
> >   * `bindgen` binds the C `size_t` type as the Rust `usize` type, so we can
> >   * use it in contexts where Rust expects a `usize` like slice (array) indices.
> > diff --git a/rust/kernel/alloc/allocator.rs b/rust/kernel/alloc/allocator.rs
> > index c6ad1dd59dd0..bb55895cbd03 100644
> > --- a/rust/kernel/alloc/allocator.rs
> > +++ b/rust/kernel/alloc/allocator.rs
> > @@ -9,6 +9,7 @@
> > 
> >  use crate::alloc::{AllocError, Allocator};
> >  use crate::bindings;
> > +use crate::pr_warn;
> > 
> >  /// The contiguous kernel allocator.
> >  ///
> > @@ -16,6 +17,12 @@
> >  /// `bindings::krealloc`.
> >  pub struct Kmalloc;
> > 
> > +/// The virtually contiguous kernel allocator.
> > +///
> > +/// The vmalloc allocator allocates pages from the page level allocator and maps them into the
> > +/// contiguous kernel virtual space.
> > +pub struct Vmalloc;
> 
> One thing that I should also have mentioned for `Kmalloc`, do we want
> these types to also have values? I don't think that we need them to be,
> so we could declare them as `pub enum Vmalloc {}`.

What the difference? Would `pub enum Vmalloc {}` be better for some reason?

> 
> > +
> >  /// Returns a proper size to alloc a new object aligned to `new_layout`'s alignment.
> >  fn aligned_size(new_layout: Layout) -> usize {
> >      // Customized layouts from `Layout::from_size_align()` can have size < align, so pad first.
> > @@ -61,6 +68,11 @@ fn krealloc() -> Self {
> >          Self(bindings::krealloc)
> >      }
> > 
> > +    // INVARIANT: `vrealloc` satisfies the type invariants.
> 
> This should be moved down a line.
> 
> ---
> Cheers,
> Benno
> 
> > +    fn vrealloc() -> Self {
> > +        Self(bindings::vrealloc)
> > +    }
> > +
> >      /// # Safety
> >      ///
> >      /// This method has the exact same safety requirements as `Allocator::realloc`.
> > @@ -141,6 +153,26 @@ unsafe fn alloc_zeroed(&self, layout: Layout) -> *mut u8 {
> >      }
> >  }
> > 
> > +unsafe impl Allocator for Vmalloc {
> > +    unsafe fn realloc(
> > +        ptr: Option<NonNull<u8>>,
> > +        layout: Layout,
> > +        flags: Flags,
> > +    ) -> Result<NonNull<[u8]>, AllocError> {
> > +        let realloc = ReallocFunc::vrealloc();
> > +
> > +        // TODO: Support alignments larger than PAGE_SIZE.
> > +        if layout.align() > bindings::PAGE_SIZE {
> > +            pr_warn!("Vmalloc does not support alignments larger than PAGE_SIZE yet.\n");
> > +            return Err(AllocError);
> > +        }
> > +
> > +        // SAFETY: If not `None`, `ptr` is guaranteed to point to valid memory, which was previously
> > +        // allocated with this `Allocator`.
> > +        unsafe { realloc.call(ptr, layout, flags) }
> 
> I am a bit confused, for `Kmalloc`, you manually returned
> `NonNull::dangling` when allocating a zero-sized allocation, but here
> you don't?
> 

I do, it's the exact same implementation for krealloc(), vrealloc() and
kvrealloc(). That why I added the `ReallocFunc` abstraction.

> ---
> Cheers,
> Benno
> 
> > +    }
> > +}
> > +
> >  #[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.45.2
> > 
> 

^ permalink raw reply	[flat|nested] 100+ messages in thread

* Re: [PATCH v4 21/28] rust: alloc: remove `GlobalAlloc` and `krealloc_aligned`
  2024-08-05 15:19 ` [PATCH v4 21/28] rust: alloc: remove `GlobalAlloc` and `krealloc_aligned` Danilo Krummrich
@ 2024-08-06 19:07   ` Björn Roy Baron
  2024-08-06 21:14     ` Miguel Ojeda
  2024-08-07 21:23   ` Benno Lossin
  1 sibling, 1 reply; 100+ messages in thread
From: Björn Roy Baron @ 2024-08-06 19:07 UTC (permalink / raw)
  To: Danilo Krummrich
  Cc: ojeda, alex.gaynor, wedsonaf, boqun.feng, gary, benno.lossin,
	a.hindborg, aliceryhl, akpm, daniel.almeida, faith.ekstrand,
	boris.brezillon, lina, mcanal, zhiw, acurrid, cjia, jhubbard,
	airlied, ajanulgu, lyude, linux-kernel, rust-for-linux, linux-mm

On Monday, August 5th, 2024 at 17:19, Danilo Krummrich <dakr@kernel.org> wrote:

> Now that we have all the `Allocator`s and the kernel `Box` and `Vec`
> implementations in place, remove `GlobalAlloc` and `krealloc_aligned`.
> 
> Signed-off-by: Danilo Krummrich <dakr@kernel.org>
> ---
>  rust/kernel/alloc/allocator.rs | 63 ++--------------------------------
>  1 file changed, 2 insertions(+), 61 deletions(-)
> 
> diff --git a/rust/kernel/alloc/allocator.rs b/rust/kernel/alloc/allocator.rs
> index 91ff21372a8f..6abd55694422 100644
> --- a/rust/kernel/alloc/allocator.rs
> +++ b/rust/kernel/alloc/allocator.rs
> @@ -2,8 +2,8 @@
> 
>  //! Allocator support.
> 
> -use super::{flags::*, Flags};
> -use core::alloc::{GlobalAlloc, Layout};
> +use super::Flags;
> +use core::alloc::Layout;
>  use core::ptr;
>  use core::ptr::NonNull;
> 
> @@ -40,27 +40,6 @@ fn aligned_size(new_layout: Layout) -> usize {
>      layout.size()
>  }
> 
> -/// Calls `krealloc` with a proper size to alloc a new object aligned to `new_layout`'s alignment.
> -///
> -/// # Safety
> -///
> -/// - `ptr` can be either null or a pointer which has been allocated by this allocator.
> -/// - `new_layout` must have a non-zero size.
> -pub(crate) unsafe fn krealloc_aligned(ptr: *mut u8, new_layout: Layout, flags: Flags) -> *mut u8 {
> -    // SAFETY:
> -    // - `ptr` is either null or a pointer returned from a previous `k{re}alloc()` by the
> -    //   function safety requirement.
> -    // - `size` is greater than 0 since it's from `layout.size()` (which cannot be zero according
> -    //   to the function safety requirement)
> -    unsafe {
> -        bindings::krealloc(
> -            ptr as *const core::ffi::c_void,
> -            aligned_size(new_layout),
> -            flags.0,
> -        ) as *mut u8
> -    }
> -}
> -
>  /// # Invariants
>  ///
>  /// One of the following `krealloc`, `vrealloc`, `kvrealloc`.
> @@ -129,41 +108,6 @@ unsafe fn realloc(
>      }
>  }
> 
> -unsafe impl GlobalAlloc for Kmalloc {
> -    unsafe fn alloc(&self, layout: Layout) -> *mut u8 {
> -        // SAFETY: `ptr::null_mut()` is null and `layout` has a non-zero size by the function safety
> -        // requirement.
> -        unsafe { krealloc_aligned(ptr::null_mut(), layout, GFP_KERNEL) }
> -    }
> -
> -    unsafe fn dealloc(&self, ptr: *mut u8, _layout: Layout) {
> -        unsafe {
> -            bindings::kfree(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) }
> -    }
> -}
> -
>  unsafe impl Allocator for Vmalloc {
>      unsafe fn realloc(
>          ptr: Option<NonNull<u8>>,
> @@ -204,9 +148,6 @@ unsafe fn realloc(
>      }
>  }
> 
> -#[global_allocator]
> -static ALLOCATOR: Kmalloc = Kmalloc;
> -
>  // See <https://github.com/rust-lang/rust/pull/86844>.
>  #[no_mangle]
>  static __rust_no_alloc_shim_is_unstable: u8 = 0;

This can be removed too. liballoc needed it for us to acknowledge that we
were linking liballoc in a way that isn't guaranteed to be stable, but now
that we are removing liballoc entirely, this static is no longer necessary.

> --
> 2.45.2

^ permalink raw reply	[flat|nested] 100+ messages in thread

* Re: [PATCH v4 09/28] rust: alloc: implement kernel `Box`
  2024-08-05 15:19 ` [PATCH v4 09/28] rust: alloc: implement kernel `Box` Danilo Krummrich
@ 2024-08-06 19:47   ` Benno Lossin
  2024-08-06 23:01     ` Danilo Krummrich
  0 siblings, 1 reply; 100+ messages in thread
From: Benno Lossin @ 2024-08-06 19: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, acurrid, cjia, jhubbard, airlied, ajanulgu, lyude,
	linux-kernel, rust-for-linux, linux-mm

On 05.08.24 17:19, 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 | 330 ++++++++++++++++++++++++++++++++++++++
>  rust/kernel/init.rs       |  35 +++-
>  rust/kernel/prelude.rs    |   2 +-
>  rust/kernel/types.rs      |  56 +++++++
>  5 files changed, 427 insertions(+), 2 deletions(-)
>  create mode 100644 rust/kernel/alloc/kbox.rs
> 
> diff --git a/rust/kernel/alloc.rs b/rust/kernel/alloc.rs
> index 942e2755f217..d7beaf0372af 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..4a4379980745
> --- /dev/null
> +++ b/rust/kernel/alloc/kbox.rs
> @@ -0,0 +1,330 @@
> +// SPDX-License-Identifier: GPL-2.0
> +
> +//! Implementation of [`Box`].
> +
> +use super::{AllocError, Allocator, Flags};
> +use core::fmt;
> +use core::marker::PhantomData;
> +use core::mem::ManuallyDrop;
> +use core::mem::MaybeUninit;
> +use core::ops::{Deref, DerefMut};
> +use core::pin::Pin;
> +use core::result::Result;
> +
> +use crate::types::Unique;
> +
> +/// The kernel's [`Box`] type.
> +///
> +/// `Box` provides the simplest way to allocate memory for a generic type with one of the kernel's
> +/// allocators, e.g. `Kmalloc`, `Vmalloc` or `KVmalloc`.
> +///
> +/// For non-zero-sized values, a [`Box`] will use the given allocator `A` for its allocation. For
> +/// the most common allocators the type aliases `KBox`, `VBox` and `KVBox` exist.
> +///
> +/// It is valid to convert both ways between a [`Box`] and a raw pointer allocated with any
> +/// `Allocator`, given that the `Layout` used with the allocator is correct for the type.
> +///
> +/// For zero-sized values the [`Box`]' pointer must be `dangling_mut::<T>`; no memory is allocated.

Why do we need this to be in the docs?

> +///
> +/// So long as `T: Sized`, a `Box<T>` is guaranteed to be represented as a single pointer and is
> +/// also ABI-compatible with C pointers (i.e. the C type `T*`).

You did not make `Box` `repr(transparent)`, so this is not true.
Additionally, `Box<T>` (from stdlib) is not FFI-safe [1], this might be
surprising, given that it is ABI-compatible (and the documentation seems
to suggest that one *can* just use it across FFI). I think we should
generally avoid using `Box` in glue code between Rust and C. I would
remove this paragraph.

[1]: https://github.com/rust-lang/unsafe-code-guidelines/issues/334


I think there are also other improvements, how about the following (feel
free to adapt it):

    /// A heap allocation for a single value of type `T`.
    ///
    /// When dropping a [`Box`], the value is also dropped and the heap memory is automatically freed.
    ///
    /// This is the kernel's version of the Rust stdlib's `Box`. There are a couple, for example no
    /// `noalias` attribute is emitted and partially moving out of a `Box` is not supported.
    ///
    /// `Box` works with any of the kernel's allocators, e.g. [`Kmalloc`], [`Vmalloc`] or [`KVMalloc`].
    /// There are aliases for `Box` with these allocators ([`KBox`], [`VBox`], [`KVBox`]).

> +///
> +/// # Invariants

Please move this section below the examples section.

> +///
> +/// The [`Box`]' pointer always properly aligned and either points to memory allocated with `A` or,

"The [`Box`]' pointer" -> "`self.0` is"

> +/// for zero-sized types, is a dangling pointer.
> +///
> +/// # Examples
> +///
> +/// ```
> +/// let b = KBox::<u64>::new(24_u64, GFP_KERNEL)?;
> +///
> +/// assert_eq!(*b, 24_u64);
> +///
> +/// # Ok::<(), Error>(())
> +/// ```

Do you think it would be a good idea to have an example that fails (ie
allocate with Kmalloc more than PAGE_SIZE)?

> +///
> +/// ```
> +/// struct Huge([u8; 1 << 24]);

I know that this is ~16MB, but are there test-vms with less memory (I
have no idea how much you normally run these at, I usually give my vms
plenty of ram, but when testing, people might not [my intuition is
telling me that 16MB should be fine, but I am not sure]).

> +///
> +/// assert!(KVBox::<Huge>::new_uninit(GFP_KERNEL).is_ok());
> +/// ```
> +pub struct Box<T: ?Sized, A: Allocator>(Unique<T>, PhantomData<A>);
> +
> +/// Type alias for `Box` with a `Kmalloc` allocator.
> +///
> +/// # Examples
> +///
> +/// ```
> +/// let b = KBox::new(24_u64, GFP_KERNEL)?;
> +///
> +/// assert_eq!(*b, 24_u64);
> +///
> +/// # Ok::<(), Error>(())
> +/// ```
> +pub type KBox<T> = Box<T, super::allocator::Kmalloc>;
> +
> +/// Type alias for `Box` with a `Vmalloc` allocator.
> +///
> +/// # Examples
> +///
> +/// ```
> +/// let b = VBox::new(24_u64, GFP_KERNEL)?;
> +///
> +/// assert_eq!(*b, 24_u64);
> +///
> +/// # Ok::<(), Error>(())
> +/// ```
> +pub type VBox<T> = Box<T, super::allocator::Vmalloc>;
> +
> +/// Type alias for `Box` with a `KVmalloc` allocator.
> +///
> +/// # Examples
> +///
> +/// ```
> +/// let b = KVBox::new(24_u64, GFP_KERNEL)?;
> +///
> +/// assert_eq!(*b, 24_u64);
> +///
> +/// # Ok::<(), Error>(())
> +/// ```
> +pub type KVBox<T> = Box<T, super::allocator::KVmalloc>;
> +
> +impl<T, A> Box<T, A>
> +where
> +    T: ?Sized,
> +    A: Allocator,
> +{
> +    /// Constructs a `Box<T, A>` from a raw pointer.
> +    ///
> +    /// # Safety
> +    ///
> +    /// `raw` must point to valid memory, previously allocated with `A`, and at least the size of
> +    /// type `T`.

With this requirement and the invariant on `Box`, I am lead to believe
that you can't use this for ZSTs, since they are not allocated with `A`.
One solution would be to adjust this requirement. But I would rather use
a different solution: we move the dangling pointer stuff into the
allocator and also call it when `T` is a ZST (ie don't special case them
in `Box` but in the impls of `Allocator`). That way this can stay as-is
and the part about ZSTs in the invariant can be removed.

> +    #[inline]
> +    pub const unsafe fn from_raw(raw: *mut T) -> Self {
> +        // SAFETY: Validity of `raw` is guaranteed by the safety preconditions of this function.

This is not a safety requirement of `Unique::new_unchecked`. Instead it
is a type invariant of `Box`, so it should be an INVARIANT comment.
You still need to justify `new_unchecked` though (which is requires that
`raw` is not NULL.

> +        Self(unsafe { Unique::new_unchecked(raw) }, PhantomData::<A>)

You don't need the `::<A>`.

> +    }
> +
> +    /// Consumes the `Box<T>`, returning a wrapped raw pointer.
> +    ///

Please add a new paragraph: "This will not run the destructor of `T` and
the allocation will stay alive indefinitely. Use [`Box::from_raw`] to
recover the [`Box`], drop the value and free the allocation.".

> +    /// # 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>`, returning a mutable reference, &'a mut T.

The last part seems a bit weird, it should definitely be enclosed in
'`', but it also seems unnecessary. Instead I would stress that this
will never drop the value and also never free the allocation.

> +    #[inline]
> +    pub fn leak<'a>(b: Self) -> &'a mut T
> +    where
> +        T: 'a,
> +    {
> +        // SAFETY: `Box::into_raw` always returns a properly aligned and dereferenceable pointer
> +        // which points to an initialized instance of `T`.
> +        unsafe { &mut *Box::into_raw(b) }
> +    }
> +
> +    /// Converts a `Box<T, A>` into a `Pin<Box<T, A>>`.
> +    #[inline]
> +    pub fn into_pin(b: Self) -> Pin<Self>
> +    where
> +        A: 'static,

Why do we require this? Our `Box` doesn't store an allocator.

> +    {
> +        // SAFETY: It's not possible to move or replace the insides of a `Pin<Box<T, A>>` when
> +        // `T: !Unpin`, so it's safe to pin it directly without any additional requirements.
> +        unsafe { Pin::new_unchecked(b) }
> +    }
> +}
> +
> +impl<T, A> Box<MaybeUninit<T>, A>
> +where
> +    A: Allocator,
> +{
> +    /// Converts to `Box<T, A>`.
> +    ///
> +    /// # Safety
> +    ///
> +    /// As with MaybeUninit::assume_init, it is up to the caller to guarantee that the value really
> +    /// is in an initialized state. Calling this when the content is not yet fully initialized
> +    /// causes immediate undefined behavior.

This also looks like it was copied from the Rust stdlib, please see
Miguel's response as to what to do about that.

Additionally, this Safety section is not up to par with the rest of the
kernel, for me this sounds better:

    /// The pointee must be a valid value of type `T`.

> +    pub unsafe fn assume_init(b: Self) -> Box<T, A> {
> +        let raw = Self::into_raw(b);
> +        // SAFETY: Reconstruct the `Box<MaybeUninit<T>, A>` as Box<T, A> now that has been
> +        // initialized. `raw` and `alloc` are safe by the invariants of `Box`.
> +        unsafe { Box::from_raw(raw as *mut T) }
> +    }
> +
> +    /// Writes the value and converts to `Box<T, A>`.
> +    pub fn write(mut b: Self, value: T) -> Box<T, A> {
> +        (*b).write(value);
> +        // SAFETY: We've just initialized `boxed`'s value.
> +        unsafe { Self::assume_init(b) }
> +    }
> +}
> +
> +impl<T, A> Box<T, A>
> +where
> +    A: Allocator,
> +{
> +    fn is_zst() -> bool {
> +        core::mem::size_of::<T>() == 0
> +    }
> +
> +    /// Allocates memory with the allocator `A` and then places `x` into it.
> +    ///
> +    /// This doesn’t actually allocate if T is zero-sized.
> +    pub fn new(x: T, flags: Flags) -> Result<Self, AllocError> {
> +        let b = Self::new_uninit(flags)?;
> +        Ok(Box::write(b, x))
> +    }
> +
> +    /// Constructs a new `Box<T, A>` with uninitialized contents.
> +    ///
> +    /// # Examples
> +    ///
> +    /// ```
> +    /// let b = KBox::<u64>::new_uninit(GFP_KERNEL)?;
> +    /// let b = KBox::write(b, 24);
> +    ///
> +    /// assert_eq!(*b, 24_u64);
> +    ///
> +    /// # Ok::<(), Error>(())
> +    /// ```
> +    pub fn new_uninit(flags: Flags) -> Result<Box<MaybeUninit<T>, A>, AllocError> {
> +        let ptr = if Self::is_zst() {
> +            Unique::dangling()
> +        } else {
> +            let layout = core::alloc::Layout::new::<MaybeUninit<T>>();
> +            let ptr = A::alloc(layout, flags)?;
> +
> +            ptr.cast().into()
> +        };
> +
> +        Ok(Box(ptr, PhantomData::<A>))
> +    }
> +
> +    /// Constructs a new `Pin<Box<T, A>>`. If `T` does not implement [`Unpin`], then `x` will be
> +    /// pinned in memory and unable to be moved.
> +    #[inline]
> +    pub fn pin(x: T, flags: Flags) -> Result<Pin<Box<T, A>>, AllocError>
> +    where
> +        A: 'static,

Again, we don't need this.

> +    {
> +        Ok(Self::new(x, flags)?.into())
> +    }
> +
> +    /// Drops the contents, but keeps the allocation.
> +    ///
> +    /// # Examples
> +    ///
> +    /// ```
> +    /// let value = KBox::new([0; 32], GFP_KERNEL)?;
> +    /// assert_eq!(*value, [0; 32]);
> +    /// let value = KBox::drop_contents(value);
> +    /// // Now we can re-use `value`:
> +    /// let value = KBox::write(value, [1; 32]);
> +    /// assert_eq!(*value, [1; 32]);
> +    /// # Ok::<(), Error>(())
> +    /// ```
> +    pub fn drop_contents(this: Self) -> Box<MaybeUninit<T>, A> {
> +        let ptr = Box::into_raw(this);
> +        // SAFETY: `ptr` is valid, because it came from `Box::into_raw`.
> +        unsafe { core::ptr::drop_in_place(ptr) };
> +        // SAFETY: `ptr` is valid, because it came from `Box::into_raw`.
> +        unsafe { Box::from_raw(ptr.cast()) }
> +    }

I don't particularly care in this instance, but you just took my patch
and folded it into your own without asking me or specifying it in the
commit message. In general I would have assumed that you just put the
entire patch into the series (with correct From:... etc).

> +}
> +
> +impl<T, A> From<Box<T, A>> for Pin<Box<T, A>>
> +where
> +    T: ?Sized,
> +    A: Allocator,
> +    A: 'static,
> +{
> +    /// Converts a `Box<T>` into a `Pin<Box<T>>`. If `T` does not implement [`Unpin`], then
> +    /// `*boxed` will be pinned in memory and unable to be moved.
> +    ///
> +    /// This conversion does not allocate on the heap and happens in place.
> +    ///
> +    /// This is also available via [`Box::into_pin`].
> +    ///
> +    /// Constructing and pinning a `Box` with <code><Pin<Box\<T>>>::from([Box::new]\(x))</code>
> +    /// can also be written more concisely using <code>[Box::pin]\(x)</code>.
> +    /// This `From` implementation is useful if you already have a `Box<T>`, or you are
> +    /// constructing a (pinned) `Box` in a different way than with [`Box::new`].

This also looks very much like something from the stdlib...

> +    fn from(b: Box<T, A>) -> Self {
> +        Box::into_pin(b)
> +    }
> +}
> +
> +impl<T, A> Deref for Box<T, A>
> +where
> +    T: ?Sized,
> +    A: Allocator,
> +{
> +    type Target = T;
> +
> +    fn deref(&self) -> &T {
> +        // SAFETY: `self.0` is always properly aligned, dereferenceable and points to an initialized
> +        // instance of `T`.

If `T` is a ZST, then it is not dereferenceable.

> +        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 ptr = self.0.as_ptr();
> +
> +        // SAFETY: `ptr` is always properly aligned, dereferenceable and points to an initialized
> +        // instance of `T`.
> +        let size = unsafe { core::mem::size_of_val(&*ptr) };

1. `size_of_val` is not `unsafe`.
2. why not use `&*self` instead of using the raw pointer? (then move the
   let binding below this line)

> +
> +        // SAFETY: We need to drop `self.0` in place, before we free the backing memory.
> +        unsafe { core::ptr::drop_in_place(ptr) };
> +
> +        if size != 0 {

Making zero-sized allocations possible with Allocators would also
simplify this.

> +            // SAFETY: `ptr` was previously allocated with `A`.
> +            unsafe { A::free(self.0.as_non_null().cast()) };
> +        }
> +    }
> +}
> diff --git a/rust/kernel/init.rs b/rust/kernel/init.rs
> index 495c09ebe3a3..5fd7a0ffabd2 100644
> --- a/rust/kernel/init.rs
> +++ b/rust/kernel/init.rs
> @@ -211,7 +211,7 @@
>  //! [`pin_init!`]: crate::pin_init!
> 
>  use crate::{
> -    alloc::{box_ext::BoxExt, AllocError, Flags},
> +    alloc::{box_ext::BoxExt, AllocError, Allocator, Flags},
>      error::{self, Error},
>      sync::UniqueArc,
>      types::{Opaque, ScopeGuard},
> @@ -1178,6 +1178,39 @@ fn try_init<E>(init: impl Init<T, E>, flags: Flags) -> Result<Self, E>
>      }
>  }
> 
> +impl<T, A> InPlaceInit<T> for crate::alloc::Box<T, A>
> +where
> +    A: Allocator + 'static,
> +{
> +    #[inline]
> +    fn try_pin_init<E>(init: impl PinInit<T, E>, flags: Flags) -> Result<Pin<Self>, E>
> +    where
> +        E: From<AllocError>,
> +    {
> +        let mut this = crate::alloc::Box::<_, A>::new_uninit(flags)?;
> +        let slot = this.as_mut_ptr();
> +        // SAFETY: When init errors/panics, slot will get deallocated but not dropped,
> +        // slot is valid and will not be moved, because we pin it later.
> +        unsafe { init.__pinned_init(slot)? };
> +        // SAFETY: All fields have been initialized.
> +        Ok(unsafe { crate::alloc::Box::assume_init(this) }.into())
> +    }
> +
> +    #[inline]
> +    fn try_init<E>(init: impl Init<T, E>, flags: Flags) -> Result<Self, E>
> +    where
> +        E: From<AllocError>,
> +    {
> +        let mut this = crate::alloc::Box::<_, A>::new_uninit(flags)?;
> +        let slot = this.as_mut_ptr();
> +        // SAFETY: When init errors/panics, slot will get deallocated but not dropped,
> +        // slot is valid.
> +        unsafe { init.__init(slot)? };
> +        // SAFETY: All fields have been initialized.
> +        Ok(unsafe { crate::alloc::Box::assume_init(this) })
> +    }
> +}

Please move this impl into kbox.rs, for the stdlib `Box`, this was here,
since we did not own that `Box`.

> +
>  impl<T> InPlaceInit<T> for UniqueArc<T> {
>      #[inline]
>      fn try_pin_init<E>(init: impl PinInit<T, E>, flags: Flags) -> Result<Pin<Self>, E>
> diff --git a/rust/kernel/prelude.rs b/rust/kernel/prelude.rs
> index b37a0b3180fb..39f9331a48e2 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};
> diff --git a/rust/kernel/types.rs b/rust/kernel/types.rs
> index 7cf89067b5fc..9fe87528d129 100644
> --- a/rust/kernel/types.rs
> +++ b/rust/kernel/types.rs
> @@ -2,6 +2,7 @@
> 
>  //! Kernel types.
> 
> +use crate::alloc::Allocator;
>  use crate::init::{self, PinInit};
>  use alloc::boxed::Box;
>  use core::{
> @@ -9,6 +10,7 @@
>      marker::{PhantomData, PhantomPinned},
>      mem::MaybeUninit,
>      ops::{Deref, DerefMut},
> +    pin::Pin,
>      ptr::NonNull,
>  };
> 
> @@ -89,6 +91,60 @@ unsafe fn from_foreign(ptr: *const core::ffi::c_void) -> Self {
>      }
>  }
> 
> +impl<T: 'static, A> ForeignOwnable for crate::alloc::Box<T, A>
> +where
> +    A: Allocator,
> +{
> +    type Borrowed<'a> = &'a T;
> +
> +    fn into_foreign(self) -> *const core::ffi::c_void {
> +        crate::alloc::Box::into_raw(self) as _
> +    }
> +
> +    unsafe fn borrow<'a>(ptr: *const core::ffi::c_void) -> &'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 { crate::alloc::Box::from_raw(ptr as _) }
> +    }
> +}
> +
> +impl<T: 'static, A> ForeignOwnable for Pin<crate::alloc::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.
> +        crate::alloc::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(crate::alloc::Box::from_raw(ptr as _)) }
> +    }
> +}

Ditto for these two.

---
Cheers,
Benno

> +
>  impl ForeignOwnable for () {
>      type Borrowed<'a> = ();
> 
> --
> 2.45.2
> 


^ permalink raw reply	[flat|nested] 100+ messages in thread

* Re: [PATCH v4 01/28] rust: alloc: add `Allocator` trait
  2024-08-06 18:30     ` Danilo Krummrich
@ 2024-08-06 20:04       ` Benno Lossin
  2024-08-07  9:36         ` Danilo Krummrich
  0 siblings, 1 reply; 100+ messages in thread
From: Benno Lossin @ 2024-08-06 20:04 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, acurrid, cjia, jhubbard,
	airlied, ajanulgu, lyude, linux-kernel, rust-for-linux, linux-mm

On 06.08.24 20:30, Danilo Krummrich wrote:
> On Tue, Aug 06, 2024 at 04:03:49PM +0000, Benno Lossin wrote:
>> On 05.08.24 17:19, Danilo Krummrich 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`.
>>>
>>> Signed-off-by: Danilo Krummrich <dakr@kernel.org>
>>> ---
>>>  rust/kernel/alloc.rs | 79 ++++++++++++++++++++++++++++++++++++++++++++
>>>  1 file changed, 79 insertions(+)
>>>
>>> diff --git a/rust/kernel/alloc.rs b/rust/kernel/alloc.rs
>>> index 1966bd407017..8a71a589469d 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,81 @@ pub mod flags {
>>>      /// small allocations.
>>>      pub const GFP_NOWAIT: Flags = Flags(bindings::GFP_NOWAIT);
>>>  }
>>> +
>>> +/// The kernel's [`Allocator`] trait.
>>> +///
>>> +/// An implementation of [`Allocator`] can allocate, re-allocate and free memory buffer described
>>> +/// via [`Layout`].
>>> +///
>>> +/// [`Allocator`] is designed to be implemented as a ZST; [`Allocator`] functions do not operate on
>>> +/// an object instance.
>>
>> This will prevent us from implementing arena-type allocators [^1]. Do we
>> want/need those?
> 
> I'm not aware of any code in the kernel that does exactly this, but kmem_cache
> is rather close to that.
> 
>> I have heard that some people use them in embedded systems, but I can't
>> say for sure. But this is a rather big design decision, so we should
>> discuss it now.
>>
>> [^1]: For those who don't know what I mean by that here is a quick
>> sketch (without handling flags and optimizations):
>>
>>     pub struct ArenaAlloc<const SIZE: usize> {
>>         memory: Opaque<[u8; SIZE]>,
>>         head: Cell<usize>,
>>     }
>>
>>     impl<const SIZE: usize> ArenaAlloc<SIZE> {
>>         pub fn new() -> Self {
>>             Self {
>>                 memory: Opaque::uninit(),
>>                 head: 0,
>>             }
>>         }
>>     }
>>
>>     impl<const SIZE: usize> Allocator for ArenaAlloc<SIZE> {
>>         fn alloc(&self, layout: Layout, _flags: Flags) -> Result<NonNull<u8>, AllocError> {
>>             let head = self.head.get();
>>             if head + layout.size() >= SIZE {
>>                 return Err(AllocError);
>>             }
>>             let ptr = self.memory.get();
>>             let ptr = ptr.cast::<u8>();
>>             let ptr = unsafe { ptr.add(head) };
>>             self.head.set(head + layout.size());
>>             unsafe { NonNull::new_unchecked(ptr) }
>>         }
>>
>>         unsafe fn realloc(
>>             &self,
>>             ptr: Option<NonNull<u8>>,
>>             old_layout: Layout, // Note that we also need `old_layout`!
>>             layout: Layout,
>>             flags: Flags
>>         ) -> Result<NonNull<u8>, AllocError> {
>>             let new = self.alloc(layout, flags)?;
>>             let Some(ptr) = ptr else { return Ok(new); };
>>             unsafe { core::ptr::copy_nonoverlapping(ptr.as_ptr(), new.as_ptr(), old_layout.size()) };
>>             self.free(ptr);
>>             Ok(new)
>>         }
>>
>>         fn free(&self, ptr: NonNull<u8>) { /* noop */ }
>>     }
>>
>>> +///
>>> +/// In order to be able to support `#[derive(SmartPointer)]` later on, we need to avoid a design
>>> +/// that requires an `Allocator` to be instantiated, hence its functions must not contain any kind
>>> +/// of `self` parameter.
>>
>> Ah I see, so since `#[derive(SmartPointer)]` needs `Box` to only consist
>> of one non ZST field... I skimmed the RFC discussion and it seems like a
>> problem that *might* be solved in the future, but probably not in the
>> (very) near future. I guess this is just a bullet that we have to bite.
>> We can always have an `ArenaBox` that can deal with that (although
>> without `DispatchFromDyn`).
>> We should revisit this when `#[derive(SmartPointer)]` becomes advanced
>> enough.
> 
> Agreed.

I opened https://github.com/Rust-for-Linux/linux/issues/1095 to track
this.

>>> +///
>>> +/// # Safety
>>> +///
>>> +/// Memory returned from an allocator must point to a valid memory buffer and remain valid until
>>> +/// it is explicitly freed.
>>> +///
>>> +/// Any pointer to a memory buffer which is currently allocated must be valid to be passed to any
>>> +/// other [`Allocator`] function of the same type. The same applies for a NULL pointer.
>>> +///
>>> +/// If `realloc` is called with:
>>> +///   - a size of zero, the given memory allocation, if any, must be freed
>>> +///   - a NULL pointer, a new memory allocation must be created
>>> +pub unsafe trait Allocator {
>>> +    /// Allocate memory based on `layout` and `flags`.
>>> +    ///
>>> +    /// On success, returns a buffer represented as `NonNull<[u8]>` that satisfies the layout
>>> +    /// constraints (i.e. minimum size and alignment as specified by `layout`).
>>> +    ///
>>> +    /// This function is equivalent to `realloc` when called with a NULL pointer.
>>> +    fn alloc(layout: Layout, flags: Flags) -> Result<NonNull<[u8]>, AllocError> {
>>> +        // SAFETY: Passing a NULL pointer to `realloc` is valid by it's safety requirements and asks
>>> +        // for a new memory allocation.
>>> +        unsafe { Self::realloc(None, layout, flags) }
>>> +    }
>>> +
>>> +    /// Re-allocate an existing memory allocation to satisfy the requested `layout`. If the
>>> +    /// requested size is zero, `realloc` behaves equivalent to `free`.
>>> +    ///
>>> +    /// If the requested size is larger than the size of the existing allocation, a successful call
>>> +    /// to `realloc` guarantees that the new or grown buffer has at least `Layout::size` bytes, but
>>> +    /// may also be larger.
>>> +    ///
>>> +    /// If the requested size is smaller than the size of the existing allocation, `realloc` may or
>>> +    /// may not shrink the buffer; this is implementation specific to the allocator.
>>> +    ///
>>> +    /// On allocation failure, the existing buffer, if any, remains valid.
>>> +    ///
>>> +    /// The buffer is represented as `NonNull<[u8]>`.
>>> +    ///
>>> +    /// # Safety
>>> +    ///
>>> +    /// `Some(ptr)` must point to an existing and valid memory allocation created by this allocator
>>
>> This is the wrong way around, `ptr: Option<NonNull<u8>>`, so
>> `Some(ptr): Option<Option<NonNull<u8>>>`. Instead I would write
>> "If `ptr = Some(p)`, then `p` must point to...".
> 
> Yes, makes sense.
> 
>>
>>> +    /// instance. The alignment encoded in `layout` must be smaller than or equal to the alignment
>>> +    /// requested in the previous `alloc` or `realloc` call of the same allocation.
>>> +    ///
>>> +    /// Additionally, `ptr` is allowed to be `None`; in this case a new memory allocation is
>>> +    /// created.
>>> +    ///
>>> +    unsafe fn realloc(
>>> +        ptr: Option<NonNull<u8>>,
>>> +        layout: Layout,
>>> +        flags: Flags,
>>> +    ) -> Result<NonNull<[u8]>, AllocError>;
>>> +
>>> +    /// Free an existing memory allocation.
>>> +    ///
>>> +    /// # Safety
>>> +    ///
>>> +    /// `ptr` must point to an existing and valid memory allocation created by this `Allocator`
>>> +    /// instance.
>>
>> Additionally, you need "The memory allocation at `ptr` must never again
>> be read from or written to.".
> 
> I'm fine adding it, but I wonder if technically this is really required? The
> condition whether the pointer is ever accessed again in any way is not relevant
> in terms of being a precondition for `free` not causing UB, right?

I don't see how else we would find the mistake in the following code:

    let ptr = Box::into_raw(Box::<i32, Kmalloc>::new(42));
    // SAFETY: `ptr` came from `Box::into_raw` and thus is pointing to a
    // valid and existing memory allocation allocated by `Kmalloc`.
    unsafe { Kmalloc::free(ptr) };
    // SAFETY: `ptr` came from `Box::into_raw` and thus is pointing at a
    // valid `i32`.
    let v = unsafe { ptr.read() };

Also see the `from_raw` for our `Arc`:

    /// Recreates an [`Arc`] instance previously deconstructed via [`Arc::into_raw`].
    ///
    /// # Safety
    ///
    /// `ptr` must have been returned by a previous call to [`Arc::into_raw`]. Additionally, it
    /// must not be called more than once for each previous call to [`Arc::into_raw`].
    pub unsafe fn from_raw(ptr: *const T) -> Self {

That also requires that the function must not be called more than once.
This reminds me, I forgot to say that about `Box::from_raw`.

---
Cheers,
Benno


^ permalink raw reply	[flat|nested] 100+ messages in thread

* Re: [PATCH v4 21/28] rust: alloc: remove `GlobalAlloc` and `krealloc_aligned`
  2024-08-06 19:07   ` Björn Roy Baron
@ 2024-08-06 21:14     ` Miguel Ojeda
  0 siblings, 0 replies; 100+ messages in thread
From: Miguel Ojeda @ 2024-08-06 21:14 UTC (permalink / raw)
  To: Björn Roy Baron
  Cc: Danilo Krummrich, ojeda, alex.gaynor, wedsonaf, boqun.feng, gary,
	benno.lossin, a.hindborg, aliceryhl, akpm, daniel.almeida,
	faith.ekstrand, boris.brezillon, lina, mcanal, zhiw, acurrid,
	cjia, jhubbard, airlied, ajanulgu, lyude, linux-kernel,
	rust-for-linux, linux-mm

On Tue, Aug 6, 2024 at 9:07 PM Björn Roy Baron <bjorn3_gh@protonmail.com> wrote:
>
> This can be removed too. liballoc needed it for us to acknowledge that we
> were linking liballoc in a way that isn't guaranteed to be stable, but now
> that we are removing liballoc entirely, this static is no longer necessary.

Thanks Björn, noted in issue #2 (and linked to the new tracking issue).

Cheers,
Miguel

^ permalink raw reply	[flat|nested] 100+ messages in thread

* Re: [PATCH v4 09/28] rust: alloc: implement kernel `Box`
  2024-08-06 19:47   ` Benno Lossin
@ 2024-08-06 23:01     ` Danilo Krummrich
  2024-08-07  7:49       ` Benno Lossin
  0 siblings, 1 reply; 100+ messages in thread
From: Danilo Krummrich @ 2024-08-06 23:01 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, acurrid, cjia, jhubbard,
	airlied, ajanulgu, lyude, linux-kernel, rust-for-linux, linux-mm

On Tue, Aug 06, 2024 at 07:47:17PM +0000, Benno Lossin wrote:
> On 05.08.24 17:19, 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 | 330 ++++++++++++++++++++++++++++++++++++++
> >  rust/kernel/init.rs       |  35 +++-
> >  rust/kernel/prelude.rs    |   2 +-
> >  rust/kernel/types.rs      |  56 +++++++
> >  5 files changed, 427 insertions(+), 2 deletions(-)
> >  create mode 100644 rust/kernel/alloc/kbox.rs
> > 
> > diff --git a/rust/kernel/alloc.rs b/rust/kernel/alloc.rs
> > index 942e2755f217..d7beaf0372af 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..4a4379980745
> > --- /dev/null
> > +++ b/rust/kernel/alloc/kbox.rs
> > @@ -0,0 +1,330 @@
> > +// SPDX-License-Identifier: GPL-2.0
> > +
> > +//! Implementation of [`Box`].
> > +
> > +use super::{AllocError, Allocator, Flags};
> > +use core::fmt;
> > +use core::marker::PhantomData;
> > +use core::mem::ManuallyDrop;
> > +use core::mem::MaybeUninit;
> > +use core::ops::{Deref, DerefMut};
> > +use core::pin::Pin;
> > +use core::result::Result;
> > +
> > +use crate::types::Unique;
> > +
> > +/// The kernel's [`Box`] type.
> > +///
> > +/// `Box` provides the simplest way to allocate memory for a generic type with one of the kernel's
> > +/// allocators, e.g. `Kmalloc`, `Vmalloc` or `KVmalloc`.
> > +///
> > +/// For non-zero-sized values, a [`Box`] will use the given allocator `A` for its allocation. For
> > +/// the most common allocators the type aliases `KBox`, `VBox` and `KVBox` exist.
> > +///
> > +/// It is valid to convert both ways between a [`Box`] and a raw pointer allocated with any
> > +/// `Allocator`, given that the `Layout` used with the allocator is correct for the type.
> > +///
> > +/// For zero-sized values the [`Box`]' pointer must be `dangling_mut::<T>`; no memory is allocated.
> 
> Why do we need this to be in the docs?

Probably not - do you suggest to remove it entirely? Otherwise, where do you
think we should move it?

> 
> > +///
> > +/// So long as `T: Sized`, a `Box<T>` is guaranteed to be represented as a single pointer and is
> > +/// also ABI-compatible with C pointers (i.e. the C type `T*`).
> 
> You did not make `Box` `repr(transparent)`, so this is not true.
> Additionally, `Box<T>` (from stdlib) is not FFI-safe [1], this might be
> surprising, given that it is ABI-compatible (and the documentation seems
> to suggest that one *can* just use it across FFI). I think we should
> generally avoid using `Box` in glue code between Rust and C. I would
> remove this paragraph.

Agreed.

> 
> [1]: https://github.com/rust-lang/unsafe-code-guidelines/issues/334
> 
> 
> I think there are also other improvements, how about the following (feel
> free to adapt it):
> 
>     /// A heap allocation for a single value of type `T`.
>     ///
>     /// When dropping a [`Box`], the value is also dropped and the heap memory is automatically freed.
>     ///
>     /// This is the kernel's version of the Rust stdlib's `Box`. There are a couple, for example no
>     /// `noalias` attribute is emitted and partially moving out of a `Box` is not supported.
>     ///
>     /// `Box` works with any of the kernel's allocators, e.g. [`Kmalloc`], [`Vmalloc`] or [`KVMalloc`].
>     /// There are aliases for `Box` with these allocators ([`KBox`], [`VBox`], [`KVBox`]).

That sounds good, I will take this with a few minor adjustments, thanks.

> 
> > +///
> > +/// # Invariants
> 
> Please move this section below the examples section.
> 
> > +///
> > +/// The [`Box`]' pointer always properly aligned and either points to memory allocated with `A` or,
> 
> "The [`Box`]' pointer" -> "`self.0` is"
> 
> > +/// for zero-sized types, is a dangling pointer.
> > +///
> > +/// # Examples
> > +///
> > +/// ```
> > +/// let b = KBox::<u64>::new(24_u64, GFP_KERNEL)?;
> > +///
> > +/// assert_eq!(*b, 24_u64);
> > +///
> > +/// # Ok::<(), Error>(())
> > +/// ```
> 
> Do you think it would be a good idea to have an example that fails (ie
> allocate with Kmalloc more than PAGE_SIZE)?

I think that's a good idea, I'll add one.

Please note that kmalloc() can allocate larger buffers than PAGE_SIZE. We can
request something larger than KMALLOC_MAX_SIZE though.

> 
> > +///
> > +/// ```
> > +/// struct Huge([u8; 1 << 24]);
> 
> I know that this is ~16MB, but are there test-vms with less memory (I
> have no idea how much you normally run these at, I usually give my vms
> plenty of ram, but when testing, people might not [my intuition is
> telling me that 16MB should be fine, but I am not sure]).

I think it's pretty reasonable to ask for 16MiB for a test case to succeed.

> 
> > +///
> > +/// assert!(KVBox::<Huge>::new_uninit(GFP_KERNEL).is_ok());
> > +/// ```
> > +pub struct Box<T: ?Sized, A: Allocator>(Unique<T>, PhantomData<A>);
> > +
> > +/// Type alias for `Box` with a `Kmalloc` allocator.
> > +///
> > +/// # Examples
> > +///
> > +/// ```
> > +/// let b = KBox::new(24_u64, GFP_KERNEL)?;
> > +///
> > +/// assert_eq!(*b, 24_u64);
> > +///
> > +/// # Ok::<(), Error>(())
> > +/// ```
> > +pub type KBox<T> = Box<T, super::allocator::Kmalloc>;
> > +
> > +/// Type alias for `Box` with a `Vmalloc` allocator.
> > +///
> > +/// # Examples
> > +///
> > +/// ```
> > +/// let b = VBox::new(24_u64, GFP_KERNEL)?;
> > +///
> > +/// assert_eq!(*b, 24_u64);
> > +///
> > +/// # Ok::<(), Error>(())
> > +/// ```
> > +pub type VBox<T> = Box<T, super::allocator::Vmalloc>;
> > +
> > +/// Type alias for `Box` with a `KVmalloc` allocator.
> > +///
> > +/// # Examples
> > +///
> > +/// ```
> > +/// let b = KVBox::new(24_u64, GFP_KERNEL)?;
> > +///
> > +/// assert_eq!(*b, 24_u64);
> > +///
> > +/// # Ok::<(), Error>(())
> > +/// ```
> > +pub type KVBox<T> = Box<T, super::allocator::KVmalloc>;
> > +
> > +impl<T, A> Box<T, A>
> > +where
> > +    T: ?Sized,
> > +    A: Allocator,
> > +{
> > +    /// Constructs a `Box<T, A>` from a raw pointer.
> > +    ///
> > +    /// # Safety
> > +    ///
> > +    /// `raw` must point to valid memory, previously allocated with `A`, and at least the size of
> > +    /// type `T`.
> 
> With this requirement and the invariant on `Box`, I am lead to believe
> that you can't use this for ZSTs, since they are not allocated with `A`.
> One solution would be to adjust this requirement. But I would rather use
> a different solution: we move the dangling pointer stuff into the
> allocator and also call it when `T` is a ZST (ie don't special case them
> in `Box` but in the impls of `Allocator`). That way this can stay as-is
> and the part about ZSTs in the invariant can be removed.

Actually, we already got that. Every zero sized allocation will return a
dangling pointer. However, we can't call `Allocator::free` with (any) dangling
pointer though.

> 
> > +    #[inline]
> > +    pub const unsafe fn from_raw(raw: *mut T) -> Self {
> > +        // SAFETY: Validity of `raw` is guaranteed by the safety preconditions of this function.
> 
> This is not a safety requirement of `Unique::new_unchecked`. Instead it
> is a type invariant of `Box`, so it should be an INVARIANT comment.
> You still need to justify `new_unchecked` though (which is requires that
> `raw` is not NULL.

Agreed.

> 
> > +        Self(unsafe { Unique::new_unchecked(raw) }, PhantomData::<A>)
> 
> You don't need the `::<A>`.
> 
> > +    }
> > +
> > +    /// Consumes the `Box<T>`, returning a wrapped raw pointer.
> > +    ///
> 
> Please add a new paragraph: "This will not run the destructor of `T` and
> the allocation will stay alive indefinitely. Use [`Box::from_raw`] to
> recover the [`Box`], drop the value and free the allocation.".
> 
> > +    /// # 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>`, returning a mutable reference, &'a mut T.
> 
> The last part seems a bit weird, it should definitely be enclosed in
> '`', but it also seems unnecessary. Instead I would stress that this
> will never drop the value and also never free the allocation.

Agreed, for this and the above.

> 
> > +    #[inline]
> > +    pub fn leak<'a>(b: Self) -> &'a mut T
> > +    where
> > +        T: 'a,
> > +    {
> > +        // SAFETY: `Box::into_raw` always returns a properly aligned and dereferenceable pointer
> > +        // which points to an initialized instance of `T`.
> > +        unsafe { &mut *Box::into_raw(b) }
> > +    }
> > +
> > +    /// Converts a `Box<T, A>` into a `Pin<Box<T, A>>`.
> > +    #[inline]
> > +    pub fn into_pin(b: Self) -> Pin<Self>
> > +    where
> > +        A: 'static,
> 
> Why do we require this? Our `Box` doesn't store an allocator.

I just forgot to remove it.

> 
> > +    {
> > +        // SAFETY: It's not possible to move or replace the insides of a `Pin<Box<T, A>>` when
> > +        // `T: !Unpin`, so it's safe to pin it directly without any additional requirements.
> > +        unsafe { Pin::new_unchecked(b) }
> > +    }
> > +}
> > +
> > +impl<T, A> Box<MaybeUninit<T>, A>
> > +where
> > +    A: Allocator,
> > +{
> > +    /// Converts to `Box<T, A>`.
> > +    ///
> > +    /// # Safety
> > +    ///
> > +    /// As with MaybeUninit::assume_init, it is up to the caller to guarantee that the value really
> > +    /// is in an initialized state. Calling this when the content is not yet fully initialized
> > +    /// causes immediate undefined behavior.
> 
> This also looks like it was copied from the Rust stdlib, please see
> Miguel's response as to what to do about that.

Yeah, I think there are a few places more places where I forgot about that, will
fix all of them.

> 
> Additionally, this Safety section is not up to par with the rest of the
> kernel, for me this sounds better:
> 
>     /// The pointee must be a valid value of type `T`.
> 
> > +    pub unsafe fn assume_init(b: Self) -> Box<T, A> {
> > +        let raw = Self::into_raw(b);
> > +        // SAFETY: Reconstruct the `Box<MaybeUninit<T>, A>` as Box<T, A> now that has been
> > +        // initialized. `raw` and `alloc` are safe by the invariants of `Box`.
> > +        unsafe { Box::from_raw(raw as *mut T) }
> > +    }
> > +
> > +    /// Writes the value and converts to `Box<T, A>`.
> > +    pub fn write(mut b: Self, value: T) -> Box<T, A> {
> > +        (*b).write(value);
> > +        // SAFETY: We've just initialized `boxed`'s value.
> > +        unsafe { Self::assume_init(b) }
> > +    }
> > +}
> > +
> > +impl<T, A> Box<T, A>
> > +where
> > +    A: Allocator,
> > +{
> > +    fn is_zst() -> bool {
> > +        core::mem::size_of::<T>() == 0
> > +    }
> > +
> > +    /// Allocates memory with the allocator `A` and then places `x` into it.
> > +    ///
> > +    /// This doesn’t actually allocate if T is zero-sized.
> > +    pub fn new(x: T, flags: Flags) -> Result<Self, AllocError> {
> > +        let b = Self::new_uninit(flags)?;
> > +        Ok(Box::write(b, x))
> > +    }
> > +
> > +    /// Constructs a new `Box<T, A>` with uninitialized contents.
> > +    ///
> > +    /// # Examples
> > +    ///
> > +    /// ```
> > +    /// let b = KBox::<u64>::new_uninit(GFP_KERNEL)?;
> > +    /// let b = KBox::write(b, 24);
> > +    ///
> > +    /// assert_eq!(*b, 24_u64);
> > +    ///
> > +    /// # Ok::<(), Error>(())
> > +    /// ```
> > +    pub fn new_uninit(flags: Flags) -> Result<Box<MaybeUninit<T>, A>, AllocError> {
> > +        let ptr = if Self::is_zst() {
> > +            Unique::dangling()
> > +        } else {
> > +            let layout = core::alloc::Layout::new::<MaybeUninit<T>>();
> > +            let ptr = A::alloc(layout, flags)?;
> > +
> > +            ptr.cast().into()
> > +        };
> > +
> > +        Ok(Box(ptr, PhantomData::<A>))
> > +    }
> > +
> > +    /// Constructs a new `Pin<Box<T, A>>`. If `T` does not implement [`Unpin`], then `x` will be
> > +    /// pinned in memory and unable to be moved.
> > +    #[inline]
> > +    pub fn pin(x: T, flags: Flags) -> Result<Pin<Box<T, A>>, AllocError>
> > +    where
> > +        A: 'static,
> 
> Again, we don't need this.
> 
> > +    {
> > +        Ok(Self::new(x, flags)?.into())
> > +    }
> > +
> > +    /// Drops the contents, but keeps the allocation.
> > +    ///
> > +    /// # Examples
> > +    ///
> > +    /// ```
> > +    /// let value = KBox::new([0; 32], GFP_KERNEL)?;
> > +    /// assert_eq!(*value, [0; 32]);
> > +    /// let value = KBox::drop_contents(value);
> > +    /// // Now we can re-use `value`:
> > +    /// let value = KBox::write(value, [1; 32]);
> > +    /// assert_eq!(*value, [1; 32]);
> > +    /// # Ok::<(), Error>(())
> > +    /// ```
> > +    pub fn drop_contents(this: Self) -> Box<MaybeUninit<T>, A> {
> > +        let ptr = Box::into_raw(this);
> > +        // SAFETY: `ptr` is valid, because it came from `Box::into_raw`.
> > +        unsafe { core::ptr::drop_in_place(ptr) };
> > +        // SAFETY: `ptr` is valid, because it came from `Box::into_raw`.
> > +        unsafe { Box::from_raw(ptr.cast()) }
> > +    }
> 
> I don't particularly care in this instance, but you just took my patch
> and folded it into your own without asking me or specifying it in the
> commit message. In general I would have assumed that you just put the
> entire patch into the series (with correct From:... etc).

When I asked about this in [1] my understanding was that we expect [1] to land
prior to this series. So, I'm just anticipating a future rebase where I move
this code from box_ext.rs to kbox.rs, just like Alice suggested for her
"ForeignOwnable for Pin<crate::alloc::Box<T, A>>" implementation.

I also understand your later reply, where you said: "[...] then you can just
include it when you remove the `BoxExit` trait." as confirmation.

Probably that's a misunderstanding though. Sorry if that's the case.

[1] https://lore.kernel.org/lkml/24a8d381-dd13-4d19-a736-689b8880dbe1@proton.me/

> 
> > +}
> > +
> > +impl<T, A> From<Box<T, A>> for Pin<Box<T, A>>
> > +where
> > +    T: ?Sized,
> > +    A: Allocator,
> > +    A: 'static,
> > +{
> > +    /// Converts a `Box<T>` into a `Pin<Box<T>>`. If `T` does not implement [`Unpin`], then
> > +    /// `*boxed` will be pinned in memory and unable to be moved.
> > +    ///
> > +    /// This conversion does not allocate on the heap and happens in place.
> > +    ///
> > +    /// This is also available via [`Box::into_pin`].
> > +    ///
> > +    /// Constructing and pinning a `Box` with <code><Pin<Box\<T>>>::from([Box::new]\(x))</code>
> > +    /// can also be written more concisely using <code>[Box::pin]\(x)</code>.
> > +    /// This `From` implementation is useful if you already have a `Box<T>`, or you are
> > +    /// constructing a (pinned) `Box` in a different way than with [`Box::new`].
> 
> This also looks very much like something from the stdlib...

Yeah, I'll replace that.

> 
> > +    fn from(b: Box<T, A>) -> Self {
> > +        Box::into_pin(b)
> > +    }
> > +}
> > +
> > +impl<T, A> Deref for Box<T, A>
> > +where
> > +    T: ?Sized,
> > +    A: Allocator,
> > +{
> > +    type Target = T;
> > +
> > +    fn deref(&self) -> &T {
> > +        // SAFETY: `self.0` is always properly aligned, dereferenceable and points to an initialized
> > +        // instance of `T`.
> 
> If `T` is a ZST, then it is not dereferenceable.

Why not? If `T` is a ZST `self.0` is `Unique::<T>::dangling()`. So, in the end
this is the same as `NonNull::<T>::dangling().as_ref()`.

> 
> > +        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 ptr = self.0.as_ptr();
> > +
> > +        // SAFETY: `ptr` is always properly aligned, dereferenceable and points to an initialized
> > +        // instance of `T`.
> > +        let size = unsafe { core::mem::size_of_val(&*ptr) };
> 
> 1. `size_of_val` is not `unsafe`.

Right, but dereferencing the `ptr` is unsafe.

> 2. why not use `&*self` instead of using the raw pointer? (then move the
>    let binding below this line)

If we ever support non-ZST `Allocator`s using `self` would not always evaluate
to the correct size. I think evaluating the size of `T` rather than `Box<T>` is
the correct thing to do.

> 
> > +
> > +        // SAFETY: We need to drop `self.0` in place, before we free the backing memory.
> > +        unsafe { core::ptr::drop_in_place(ptr) };
> > +
> > +        if size != 0 {
> 
> Making zero-sized allocations possible with Allocators would also
> simplify this.

As mentioned, it's possible already. But we still can't pass any dangling
pointer to `free`.

> 
> > +            // SAFETY: `ptr` was previously allocated with `A`.
> > +            unsafe { A::free(self.0.as_non_null().cast()) };
> > +        }
> > +    }
> > +}
> > diff --git a/rust/kernel/init.rs b/rust/kernel/init.rs
> > index 495c09ebe3a3..5fd7a0ffabd2 100644
> > --- a/rust/kernel/init.rs
> > +++ b/rust/kernel/init.rs
> > @@ -211,7 +211,7 @@
> >  //! [`pin_init!`]: crate::pin_init!
> > 
> >  use crate::{
> > -    alloc::{box_ext::BoxExt, AllocError, Flags},
> > +    alloc::{box_ext::BoxExt, AllocError, Allocator, Flags},
> >      error::{self, Error},
> >      sync::UniqueArc,
> >      types::{Opaque, ScopeGuard},
> > @@ -1178,6 +1178,39 @@ fn try_init<E>(init: impl Init<T, E>, flags: Flags) -> Result<Self, E>
> >      }
> >  }
> > 
> > +impl<T, A> InPlaceInit<T> for crate::alloc::Box<T, A>
> > +where
> > +    A: Allocator + 'static,
> > +{
> > +    #[inline]
> > +    fn try_pin_init<E>(init: impl PinInit<T, E>, flags: Flags) -> Result<Pin<Self>, E>
> > +    where
> > +        E: From<AllocError>,
> > +    {
> > +        let mut this = crate::alloc::Box::<_, A>::new_uninit(flags)?;
> > +        let slot = this.as_mut_ptr();
> > +        // SAFETY: When init errors/panics, slot will get deallocated but not dropped,
> > +        // slot is valid and will not be moved, because we pin it later.
> > +        unsafe { init.__pinned_init(slot)? };
> > +        // SAFETY: All fields have been initialized.
> > +        Ok(unsafe { crate::alloc::Box::assume_init(this) }.into())
> > +    }
> > +
> > +    #[inline]
> > +    fn try_init<E>(init: impl Init<T, E>, flags: Flags) -> Result<Self, E>
> > +    where
> > +        E: From<AllocError>,
> > +    {
> > +        let mut this = crate::alloc::Box::<_, A>::new_uninit(flags)?;
> > +        let slot = this.as_mut_ptr();
> > +        // SAFETY: When init errors/panics, slot will get deallocated but not dropped,
> > +        // slot is valid.
> > +        unsafe { init.__init(slot)? };
> > +        // SAFETY: All fields have been initialized.
> > +        Ok(unsafe { crate::alloc::Box::assume_init(this) })
> > +    }
> > +}
> 
> Please move this impl into kbox.rs, for the stdlib `Box`, this was here,
> since we did not own that `Box`.
> 
> > +
> >  impl<T> InPlaceInit<T> for UniqueArc<T> {
> >      #[inline]
> >      fn try_pin_init<E>(init: impl PinInit<T, E>, flags: Flags) -> Result<Pin<Self>, E>
> > diff --git a/rust/kernel/prelude.rs b/rust/kernel/prelude.rs
> > index b37a0b3180fb..39f9331a48e2 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};
> > diff --git a/rust/kernel/types.rs b/rust/kernel/types.rs
> > index 7cf89067b5fc..9fe87528d129 100644
> > --- a/rust/kernel/types.rs
> > +++ b/rust/kernel/types.rs
> > @@ -2,6 +2,7 @@
> > 
> >  //! Kernel types.
> > 
> > +use crate::alloc::Allocator;
> >  use crate::init::{self, PinInit};
> >  use alloc::boxed::Box;
> >  use core::{
> > @@ -9,6 +10,7 @@
> >      marker::{PhantomData, PhantomPinned},
> >      mem::MaybeUninit,
> >      ops::{Deref, DerefMut},
> > +    pin::Pin,
> >      ptr::NonNull,
> >  };
> > 
> > @@ -89,6 +91,60 @@ unsafe fn from_foreign(ptr: *const core::ffi::c_void) -> Self {
> >      }
> >  }
> > 
> > +impl<T: 'static, A> ForeignOwnable for crate::alloc::Box<T, A>
> > +where
> > +    A: Allocator,
> > +{
> > +    type Borrowed<'a> = &'a T;
> > +
> > +    fn into_foreign(self) -> *const core::ffi::c_void {
> > +        crate::alloc::Box::into_raw(self) as _
> > +    }
> > +
> > +    unsafe fn borrow<'a>(ptr: *const core::ffi::c_void) -> &'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 { crate::alloc::Box::from_raw(ptr as _) }
> > +    }
> > +}
> > +
> > +impl<T: 'static, A> ForeignOwnable for Pin<crate::alloc::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.
> > +        crate::alloc::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(crate::alloc::Box::from_raw(ptr as _)) }
> > +    }
> > +}
> 
> Ditto for these two.

Agreed, will do.

> 
> ---
> Cheers,
> Benno
> 
> > +
> >  impl ForeignOwnable for () {
> >      type Borrowed<'a> = ();
> > 
> > --
> > 2.45.2
> > 
> 

^ permalink raw reply	[flat|nested] 100+ messages in thread

* Re: [PATCH v4 08/28] rust: types: implement `Unique<T>`
  2024-08-06 17:22   ` Benno Lossin
  2024-08-06 17:28     ` Miguel Ojeda
@ 2024-08-06 23:12     ` Danilo Krummrich
  2024-08-07  7:27       ` Benno Lossin
  1 sibling, 1 reply; 100+ messages in thread
From: Danilo Krummrich @ 2024-08-06 23:12 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, acurrid, cjia, jhubbard,
	airlied, ajanulgu, lyude, linux-kernel, rust-for-linux, linux-mm

On Tue, Aug 06, 2024 at 05:22:21PM +0000, Benno Lossin wrote:
> On 05.08.24 17:19, Danilo Krummrich wrote:
> > Implement the `Unique` type as a prerequisite for `Box` and `Vec`
> > introduced in subsequent patches.
> > 
> > `Unique` serves as wrapper around a `NonNull`, but indicates that the
> > possessor of this wrapper owns the referent.
> > 
> > This type already exists in Rust's core library, but, unfortunately, is
> > exposed as unstable API and hence shouldn't be used in the kernel.
> > 
> > This implementation of `Unique` is almost identical, but mostly stripped
> > down to the functionality we need for `Box` and `Vec`. Additionally, all
> > unstable features are removed and / or replaced by stable ones.
> > 
> > Reviewed-by: Alice Ryhl <aliceryhl@google.com>
> > Signed-off-by: Danilo Krummrich <dakr@kernel.org>
> > ---
> >  rust/kernel/types.rs | 183 +++++++++++++++++++++++++++++++++++++++++++
> >  1 file changed, 183 insertions(+)
> > 
> > diff --git a/rust/kernel/types.rs b/rust/kernel/types.rs
> > index bd189d646adb..7cf89067b5fc 100644
> > --- a/rust/kernel/types.rs
> > +++ b/rust/kernel/types.rs
> > @@ -473,3 +473,186 @@ unsafe impl AsBytes for str {}
> >  // does not have any uninitialized portions either.
> >  unsafe impl<T: AsBytes> AsBytes for [T] {}
> >  unsafe impl<T: AsBytes, const N: usize> AsBytes for [T; N] {}
> > +
> > +/// A wrapper around a raw non-null `*mut T` that indicates that the possessor
> > +/// of this wrapper owns the referent. Useful for building abstractions like
> > +/// `Box<T>`, `Vec<T>`, `String`, and `HashMap<K, V>`.
> > +///
> > +/// Unlike `*mut T`, `Unique<T>` behaves "as if" it were an instance of `T`.
> > +/// It implements `Send`/`Sync` if `T` is `Send`/`Sync`. It also implies
> > +/// the kind of strong aliasing guarantees an instance of `T` can expect:
> > +/// the referent of the pointer should not be modified without a unique path to
> > +/// its owning Unique.
> > +///
> > +/// If you're uncertain of whether it's correct to use `Unique` for your purposes,
> > +/// consider using `NonNull`, which has weaker semantics.
> > +///
> > +/// Unlike `*mut T`, the pointer must always be non-null, even if the pointer
> > +/// is never dereferenced. This is so that enums may use this forbidden value
> > +/// as a discriminant -- `Option<Unique<T>>` has the same size as `Unique<T>`.
> > +/// However the pointer may still dangle if it isn't dereferenced.
> > +///
> > +/// Unlike `*mut T`, `Unique<T>` is covariant over `T`. This should always be correct
> > +/// for any type which upholds Unique's aliasing requirements.
> > +#[repr(transparent)]
> > +pub struct Unique<T: ?Sized> {
> > +    pointer: NonNull<T>,
> > +    // NOTE: this marker has no consequences for variance, but is necessary
> > +    // for dropck to understand that we logically own a `T`.
> > +    //
> > +    // For details, see:
> > +    // https://github.com/rust-lang/rfcs/blob/master/text/0769-sound-generic-drop.md#phantom-data
> > +    _marker: PhantomData<T>,
> > +}
> > +
> > +/// `Unique` pointers are `Send` if `T` is `Send` because the data they
> > +/// reference is unaliased. Note that this aliasing invariant is
> > +/// unenforced by the type system; the abstraction using the
> > +/// `Unique` must enforce it.
> > +unsafe impl<T: Send + ?Sized> Send for Unique<T> {}
> > +
> > +/// `Unique` pointers are `Sync` if `T` is `Sync` because the data they
> > +/// reference is unaliased. Note that this aliasing invariant is
> > +/// unenforced by the type system; the abstraction using the
> > +/// `Unique` must enforce it.
> > +unsafe impl<T: Sync + ?Sized> Sync for Unique<T> {}
> > +
> > +impl<T: Sized> Unique<T> {
> > +    /// Creates a new `Unique` that is dangling, but well-aligned.
> > +    ///
> > +    /// This is useful for initializing types which lazily allocate, like
> > +    /// `Vec::new` does.
> > +    ///
> > +    /// Note that the pointer value may potentially represent a valid pointer to
> > +    /// a `T`, which means this must not be used as a "not yet initialized"
> > +    /// sentinel value. Types that lazily allocate must track initialization by
> > +    /// some other means.
> > +    #[must_use]
> > +    #[inline]
> > +    pub const fn dangling() -> Self {
> > +        Unique {
> > +            pointer: NonNull::dangling(),
> > +            _marker: PhantomData,
> > +        }
> > +    }
> 
> I think I already asked this, but the code until this point is copied
> from the rust stdlib and nowhere cited, does that work with the
> licensing?
> 
> I also think that the code above could use some improvements:
> - add an `# Invariants` section with appropriate invariants (what are
>   they supposed to be?)
> - Do we really want this type to be public and exported from the kernel
>   crate? I think it would be better if it were crate-private.
> - What do we gain from having this type? As I learned recently, the
>   `Unique` type from `core` doesn't actually put the `noalias` onto
>   `Box` and `Vec`. The functions are mostly delegations to `NonNull`, so
>   if the only advantages are that `Send` and `Sync` are already
>   implemented, then I think we should drop this.

I originally introduced it for the reasons described in [1], but mainly to make
clear that the owner of this thing also owns the memory behind the pointer and
the `Send` and `Sync` stuff you already mentioned.

If no one else has objections we can also just drop it. Personally, I'm fine
either way.

[1] https://docs.rs/rust-libcore/latest/core/ptr/struct.Unique.html

> 
> > +}
> > +
> > +impl<T: ?Sized> Unique<T> {
> > +    /// Creates a new `Unique`.
> > +    ///
> > +    /// # Safety
> > +    ///
> > +    /// `ptr` must be non-null.
> > +    #[inline]
> > +    pub const unsafe fn new_unchecked(ptr: *mut T) -> Self {
> > +        // SAFETY: the caller must guarantee that `ptr` is non-null.
> > +        unsafe {
> 
> The only unsafe operation in the body is `new_unchecked` only that one
> should be wrapped in `unsafe {}`.
> 
> > +            Unique {
> > +                pointer: NonNull::new_unchecked(ptr),
> > +                _marker: PhantomData,
> > +            }
> > +        }
> > +    }
> > +
> > +    /// Creates a new `Unique` if `ptr` is non-null.
> > +    #[allow(clippy::manual_map)]
> > +    #[inline]
> > +    pub fn new(ptr: *mut T) -> Option<Self> {
> > +        if let Some(pointer) = NonNull::new(ptr) {
> > +            Some(Unique {
> > +                pointer,
> > +                _marker: PhantomData,
> > +            })
> > +        } else {
> > +            None
> > +        }
> 
> Why is this so verbose? You even needed to disable the clippy lint!
> Can't this just be?:
> 
>     Some(Unique {
>         pointer: NonNull::new(ptr)?,
>         _marker: PhantomData,
>     })
> 
> or maybe even
> 
>     NonNull::new(ptr).map(Unique::from)
> 
> 
> > +    }
> > +
> > +    /// Acquires the underlying `*mut` pointer.
> > +    #[must_use = "`self` will be dropped if the result is not used"]
> 
> This seems like an odd thing, there is no `Drop` impl that drops the
> pointee...
> 
> ---
> Cheers,
> Benno
> 

^ permalink raw reply	[flat|nested] 100+ messages in thread

* Re: [PATCH v4 08/28] rust: types: implement `Unique<T>`
  2024-08-06 17:28     ` Miguel Ojeda
@ 2024-08-06 23:16       ` Danilo Krummrich
  0 siblings, 0 replies; 100+ messages in thread
From: Danilo Krummrich @ 2024-08-06 23:16 UTC (permalink / raw)
  To: Miguel Ojeda
  Cc: Benno Lossin, ojeda, alex.gaynor, wedsonaf, boqun.feng, gary,
	bjorn3_gh, a.hindborg, aliceryhl, akpm, daniel.almeida,
	faith.ekstrand, boris.brezillon, lina, mcanal, zhiw, acurrid,
	cjia, jhubbard, airlied, ajanulgu, lyude, linux-kernel,
	rust-for-linux, linux-mm

On Tue, Aug 06, 2024 at 07:28:45PM +0200, Miguel Ojeda wrote:
> On Tue, Aug 6, 2024 at 7:22 PM Benno Lossin <benno.lossin@proton.me> wrote:
> >
> > I think I already asked this, but the code until this point is copied
> > from the rust stdlib and nowhere cited, does that work with the
> > licensing?
> 
> No, it doesn't. They should be put into its own file with the right
> license and a paragraph about it, e.g. the other `std_vendor.rs` files
> we have (if it is just about `Unique`, it could go in the existing one
> at the `kernel` level).

Thanks for catching this, I once thought about it and then forgot about it
entirely.

I didn't know about std_vendor.rs, that's pretty convenient. I will move it over
in case we don't drop `Unique` entirely.

> 
> Cheers,
> Miguel
> 

^ permalink raw reply	[flat|nested] 100+ messages in thread

* Re: [PATCH v4 04/28] rust: alloc: implement `Allocator` for `Kmalloc`
  2024-08-06 18:55     ` Danilo Krummrich
@ 2024-08-07  7:14       ` Benno Lossin
  2024-08-07 10:11         ` Danilo Krummrich
  0 siblings, 1 reply; 100+ messages in thread
From: Benno Lossin @ 2024-08-07  7:14 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, acurrid, cjia, jhubbard,
	airlied, ajanulgu, lyude, linux-kernel, rust-for-linux, linux-mm

On 06.08.24 20:55, Danilo Krummrich wrote:
> On Tue, Aug 06, 2024 at 04:51:28PM +0000, Benno Lossin wrote:
>> On 05.08.24 17:19, Danilo Krummrich wrote:
>>> +        let raw_ptr = unsafe {
>>> +            // If `size == 0` and `ptr != NULL` the memory behind the pointer is freed.
>>> +            self.0(ptr.cast(), size, flags.0).cast()
>>> +        };
>>> +
>>> +        let ptr = if size == 0 {
>>> +            NonNull::dangling()
>>
>> If we call `realloc(Some(ptr), <layout with size = 0>, ...)`, then this
>> leaks the pointer returned by the call to `self.0` above. I don't know
>> what the return value of the different functions are that can appear in
>> `self.0`, do they return NULL?
> 
> That is fine, we don't care about the return value. All `ReallocFunc` free the
> memory behind `ptr` if called with a size of zero. But to answer the question,
> they return either NULL or ZERO_SIZE_PTR.

I see, then it's fine. I think it would help if we know the exact
behavior of `kmalloc` & friends (either add a link to C docs or write it
down on `ReallocFunc`).

>> What about the following sequence:
>>
>>     let ptr = realloc(None, <layout with size = 0>, ...);
>>     let ptr = realloc(Some(ptr), <layout with size = 0>, ...);
>>
>> Then the above call to `self.0` is done with a dangling pointer, can the
>> functions that appear in `self.0` handle that?
> 
> This would be incorrect.
> 
> Calling `realloc(Some(ptr), <layout with size = 0>, ...)` frees the memory
> behind `ptr`. This is guranteed behavior for all `ReallocFunc`s, i.e.
> krealloc(), vrealloc(), kvrealloc().

Note that I don't use `ptr` afterwards, the code snippet above is
equivalent to this:

    let ptr = Kmalloc::alloc(<layout with size = 0>, ...);
    unsafe { Kmalloc::free(ptr) };

internally exactly the realloc calls that I put above should be called.

---
Cheers,
Benno


^ permalink raw reply	[flat|nested] 100+ messages in thread

* Re: [PATCH v4 05/28] rust: alloc: add module `allocator_test`
  2024-08-06 18:58     ` Danilo Krummrich
@ 2024-08-07  7:20       ` Benno Lossin
  2024-08-07 10:16         ` Danilo Krummrich
  0 siblings, 1 reply; 100+ messages in thread
From: Benno Lossin @ 2024-08-07  7: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, acurrid, cjia, jhubbard,
	airlied, ajanulgu, lyude, linux-kernel, rust-for-linux, linux-mm

On 06.08.24 20:58, Danilo Krummrich wrote:
> On Tue, Aug 06, 2024 at 04:54:10PM +0000, Benno Lossin wrote:
>> On 05.08.24 17:19, Danilo Krummrich wrote:
>>> `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.
>>
>> This is confusing, since you are talking about both our new `Allocator`
>> trait, allocators and the `alloc` crate `Allocator`.
> 
> I never mention the `alloc` crate `Allocator` here.

Seems like I confused myself...

>>> Reviewed-by: Alice Ryhl <aliceryhl@google.com>
>>> 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 bc01a17df5e0..942e2755f217 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!();
>>
>> Does `build_error!()` also work? If yes, then I would prefer that.
> 
> Probably, but it also probably doesn't matter too much. A later patch in the
> series replaces this with a `Cmalloc` implementation and type aliases all kernel
> allocators (e.g. `Kmalloc`) to it.

What prevents you from doing the `Cmalloc` patch here? `build_error!`
probably doesn't work, since we probably allocate in rusttest, right?

---
Cheers,
Benno


^ permalink raw reply	[flat|nested] 100+ messages in thread

* Re: [PATCH v4 06/28] rust: alloc: implement `Vmalloc` allocator
  2024-08-06 19:01     ` Danilo Krummrich
@ 2024-08-07  7:23       ` Benno Lossin
  0 siblings, 0 replies; 100+ messages in thread
From: Benno Lossin @ 2024-08-07  7:23 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, acurrid, cjia, jhubbard,
	airlied, ajanulgu, lyude, linux-kernel, rust-for-linux, linux-mm

On 06.08.24 21:01, Danilo Krummrich wrote:
> On Tue, Aug 06, 2024 at 05:00:24PM +0000, Benno Lossin wrote:
>> On 05.08.24 17:19, 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.c                      |  7 +++++++
>>>  rust/kernel/alloc/allocator.rs      | 32 +++++++++++++++++++++++++++++
>>>  rust/kernel/alloc/allocator_test.rs |  1 +
>>>  3 files changed, 40 insertions(+)
>>>
>>> diff --git a/rust/helpers.c b/rust/helpers.c
>>> index 9f7275493365..7406943f887d 100644
>>> --- a/rust/helpers.c
>>> +++ b/rust/helpers.c
>>> @@ -33,6 +33,7 @@
>>>  #include <linux/sched/signal.h>
>>>  #include <linux/slab.h>
>>>  #include <linux/spinlock.h>
>>> +#include <linux/vmalloc.h>
>>>  #include <linux/wait.h>
>>>  #include <linux/workqueue.h>
>>>
>>> @@ -199,6 +200,12 @@ void *rust_helper_krealloc(const void *objp, size_t new_size, gfp_t flags)
>>>  }
>>>  EXPORT_SYMBOL_GPL(rust_helper_krealloc);
>>>
>>> +void *rust_helper_vrealloc(const void *p, size_t size, gfp_t flags)
>>> +{
>>> +	return vrealloc(p, size, flags);
>>> +}
>>> +EXPORT_SYMBOL_GPL(rust_helper_vrealloc);
>>> +
>>>  /*
>>>   * `bindgen` binds the C `size_t` type as the Rust `usize` type, so we can
>>>   * use it in contexts where Rust expects a `usize` like slice (array) indices.
>>> diff --git a/rust/kernel/alloc/allocator.rs b/rust/kernel/alloc/allocator.rs
>>> index c6ad1dd59dd0..bb55895cbd03 100644
>>> --- a/rust/kernel/alloc/allocator.rs
>>> +++ b/rust/kernel/alloc/allocator.rs
>>> @@ -9,6 +9,7 @@
>>>
>>>  use crate::alloc::{AllocError, Allocator};
>>>  use crate::bindings;
>>> +use crate::pr_warn;
>>>
>>>  /// The contiguous kernel allocator.
>>>  ///
>>> @@ -16,6 +17,12 @@
>>>  /// `bindings::krealloc`.
>>>  pub struct Kmalloc;
>>>
>>> +/// The virtually contiguous kernel allocator.
>>> +///
>>> +/// The vmalloc allocator allocates pages from the page level allocator and maps them into the
>>> +/// contiguous kernel virtual space.
>>> +pub struct Vmalloc;
>>
>> One thing that I should also have mentioned for `Kmalloc`, do we want
>> these types to also have values? I don't think that we need them to be,
>> so we could declare them as `pub enum Vmalloc {}`.
> 
> What the difference? Would `pub enum Vmalloc {}` be better for some reason?

It doesn't make a huge difference, it doesn't allow you to create a
value of type `Vmalloc` (as there are no values of that type). So
you can't accidentally use the type where it shouldn't be used.
If we use `pub struct Vmalloc;`, then you can do this:

    let a = Vmalloc;

you can't really do anything with it (as there are no methods on that
type), but it might be confusing for people.

>>> @@ -141,6 +153,26 @@ unsafe fn alloc_zeroed(&self, layout: Layout) -> *mut u8 {
>>>      }
>>>  }
>>>
>>> +unsafe impl Allocator for Vmalloc {
>>> +    unsafe fn realloc(
>>> +        ptr: Option<NonNull<u8>>,
>>> +        layout: Layout,
>>> +        flags: Flags,
>>> +    ) -> Result<NonNull<[u8]>, AllocError> {
>>> +        let realloc = ReallocFunc::vrealloc();
>>> +
>>> +        // TODO: Support alignments larger than PAGE_SIZE.
>>> +        if layout.align() > bindings::PAGE_SIZE {
>>> +            pr_warn!("Vmalloc does not support alignments larger than PAGE_SIZE yet.\n");
>>> +            return Err(AllocError);
>>> +        }
>>> +
>>> +        // SAFETY: If not `None`, `ptr` is guaranteed to point to valid memory, which was previously
>>> +        // allocated with this `Allocator`.
>>> +        unsafe { realloc.call(ptr, layout, flags) }
>>
>> I am a bit confused, for `Kmalloc`, you manually returned
>> `NonNull::dangling` when allocating a zero-sized allocation, but here
>> you don't?
>>
> 
> I do, it's the exact same implementation for krealloc(), vrealloc() and
> kvrealloc(). That why I added the `ReallocFunc` abstraction.

Oh yeah, my bad.

---
Cheers
Benno


^ permalink raw reply	[flat|nested] 100+ messages in thread

* Re: [PATCH v4 08/28] rust: types: implement `Unique<T>`
  2024-08-06 23:12     ` Danilo Krummrich
@ 2024-08-07  7:27       ` Benno Lossin
  2024-08-07 10:13         ` Danilo Krummrich
  0 siblings, 1 reply; 100+ messages in thread
From: Benno Lossin @ 2024-08-07  7:27 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, acurrid, cjia, jhubbard,
	airlied, ajanulgu, lyude, linux-kernel, rust-for-linux, linux-mm

On 07.08.24 01:12, Danilo Krummrich wrote:
> On Tue, Aug 06, 2024 at 05:22:21PM +0000, Benno Lossin wrote:
>> On 05.08.24 17:19, Danilo Krummrich wrote:
>>> +impl<T: Sized> Unique<T> {
>>> +    /// Creates a new `Unique` that is dangling, but well-aligned.
>>> +    ///
>>> +    /// This is useful for initializing types which lazily allocate, like
>>> +    /// `Vec::new` does.
>>> +    ///
>>> +    /// Note that the pointer value may potentially represent a valid pointer to
>>> +    /// a `T`, which means this must not be used as a "not yet initialized"
>>> +    /// sentinel value. Types that lazily allocate must track initialization by
>>> +    /// some other means.
>>> +    #[must_use]
>>> +    #[inline]
>>> +    pub const fn dangling() -> Self {
>>> +        Unique {
>>> +            pointer: NonNull::dangling(),
>>> +            _marker: PhantomData,
>>> +        }
>>> +    }
>>
>> I think I already asked this, but the code until this point is copied
>> from the rust stdlib and nowhere cited, does that work with the
>> licensing?
>>
>> I also think that the code above could use some improvements:
>> - add an `# Invariants` section with appropriate invariants (what are
>>   they supposed to be?)
>> - Do we really want this type to be public and exported from the kernel
>>   crate? I think it would be better if it were crate-private.
>> - What do we gain from having this type? As I learned recently, the
>>   `Unique` type from `core` doesn't actually put the `noalias` onto
>>   `Box` and `Vec`. The functions are mostly delegations to `NonNull`, so
>>   if the only advantages are that `Send` and `Sync` are already
>>   implemented, then I think we should drop this.
> 
> I originally introduced it for the reasons described in [1], but mainly to make
> clear that the owner of this thing also owns the memory behind the pointer and
> the `Send` and `Sync` stuff you already mentioned.

I would prefer if we make that explicit, since it is rather error-prone
when creating new pointer types (and one should have to think about
thread safety).

---
Cheers,
Benno

> If no one else has objections we can also just drop it. Personally, I'm fine
> either way.
> 
> [1] https://docs.rs/rust-libcore/latest/core/ptr/struct.Unique.html


^ permalink raw reply	[flat|nested] 100+ messages in thread

* Re: [PATCH v4 09/28] rust: alloc: implement kernel `Box`
  2024-08-06 23:01     ` Danilo Krummrich
@ 2024-08-07  7:49       ` Benno Lossin
  2024-08-07  7:51         ` Alice Ryhl
                           ` (2 more replies)
  0 siblings, 3 replies; 100+ messages in thread
From: Benno Lossin @ 2024-08-07  7:49 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, acurrid, cjia, jhubbard,
	airlied, ajanulgu, lyude, linux-kernel, rust-for-linux, linux-mm

On 07.08.24 01:01, Danilo Krummrich wrote:
> On Tue, Aug 06, 2024 at 07:47:17PM +0000, Benno Lossin wrote:
>> On 05.08.24 17:19, 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 | 330 ++++++++++++++++++++++++++++++++++++++
>>>  rust/kernel/init.rs       |  35 +++-
>>>  rust/kernel/prelude.rs    |   2 +-
>>>  rust/kernel/types.rs      |  56 +++++++
>>>  5 files changed, 427 insertions(+), 2 deletions(-)
>>>  create mode 100644 rust/kernel/alloc/kbox.rs
>>>
>>> diff --git a/rust/kernel/alloc.rs b/rust/kernel/alloc.rs
>>> index 942e2755f217..d7beaf0372af 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..4a4379980745
>>> --- /dev/null
>>> +++ b/rust/kernel/alloc/kbox.rs
>>> @@ -0,0 +1,330 @@
>>> +// SPDX-License-Identifier: GPL-2.0
>>> +
>>> +//! Implementation of [`Box`].
>>> +
>>> +use super::{AllocError, Allocator, Flags};
>>> +use core::fmt;
>>> +use core::marker::PhantomData;
>>> +use core::mem::ManuallyDrop;
>>> +use core::mem::MaybeUninit;
>>> +use core::ops::{Deref, DerefMut};
>>> +use core::pin::Pin;
>>> +use core::result::Result;
>>> +
>>> +use crate::types::Unique;
>>> +
>>> +/// The kernel's [`Box`] type.
>>> +///
>>> +/// `Box` provides the simplest way to allocate memory for a generic type with one of the kernel's
>>> +/// allocators, e.g. `Kmalloc`, `Vmalloc` or `KVmalloc`.
>>> +///
>>> +/// For non-zero-sized values, a [`Box`] will use the given allocator `A` for its allocation. For
>>> +/// the most common allocators the type aliases `KBox`, `VBox` and `KVBox` exist.
>>> +///
>>> +/// It is valid to convert both ways between a [`Box`] and a raw pointer allocated with any
>>> +/// `Allocator`, given that the `Layout` used with the allocator is correct for the type.
>>> +///
>>> +/// For zero-sized values the [`Box`]' pointer must be `dangling_mut::<T>`; no memory is allocated.
>>
>> Why do we need this to be in the docs?
> 
> Probably not - do you suggest to remove it entirely? Otherwise, where do you
> think we should move it?

I would remove it, since it's just implementation detail and
allocator-dependent.

>>> +impl<T, A> Box<T, A>
>>> +where
>>> +    T: ?Sized,
>>> +    A: Allocator,
>>> +{
>>> +    /// Constructs a `Box<T, A>` from a raw pointer.
>>> +    ///
>>> +    /// # Safety
>>> +    ///
>>> +    /// `raw` must point to valid memory, previously allocated with `A`, and at least the size of
>>> +    /// type `T`.
>>
>> With this requirement and the invariant on `Box`, I am lead to believe
>> that you can't use this for ZSTs, since they are not allocated with `A`.
>> One solution would be to adjust this requirement. But I would rather use
>> a different solution: we move the dangling pointer stuff into the
>> allocator and also call it when `T` is a ZST (ie don't special case them
>> in `Box` but in the impls of `Allocator`). That way this can stay as-is
>> and the part about ZSTs in the invariant can be removed.
> 
> Actually, we already got that. Every zero sized allocation will return a
> dangling pointer. However, we can't call `Allocator::free` with (any) dangling
> pointer though.

The last part is rather problematic in my opinion, since the safety
requirements of the functions in `Allocator` don't ensure that you're
not allowed to do it. We should make it possible to free dangling
pointers that were previously "allocated" by the allocator (ie returned
by `realloc`).
Maybe we do need an `old_layout` parameter for that (that way we can
also `debug_assert_eq!(old_layout.align(), new_layout.align())`).

>>> +    {
>>> +        Ok(Self::new(x, flags)?.into())
>>> +    }
>>> +
>>> +    /// Drops the contents, but keeps the allocation.
>>> +    ///
>>> +    /// # Examples
>>> +    ///
>>> +    /// ```
>>> +    /// let value = KBox::new([0; 32], GFP_KERNEL)?;
>>> +    /// assert_eq!(*value, [0; 32]);
>>> +    /// let value = KBox::drop_contents(value);
>>> +    /// // Now we can re-use `value`:
>>> +    /// let value = KBox::write(value, [1; 32]);
>>> +    /// assert_eq!(*value, [1; 32]);
>>> +    /// # Ok::<(), Error>(())
>>> +    /// ```
>>> +    pub fn drop_contents(this: Self) -> Box<MaybeUninit<T>, A> {
>>> +        let ptr = Box::into_raw(this);
>>> +        // SAFETY: `ptr` is valid, because it came from `Box::into_raw`.
>>> +        unsafe { core::ptr::drop_in_place(ptr) };
>>> +        // SAFETY: `ptr` is valid, because it came from `Box::into_raw`.
>>> +        unsafe { Box::from_raw(ptr.cast()) }
>>> +    }
>>
>> I don't particularly care in this instance, but you just took my patch
>> and folded it into your own without asking me or specifying it in the
>> commit message. In general I would have assumed that you just put the
>> entire patch into the series (with correct From:... etc).
> 
> When I asked about this in [1] my understanding was that we expect [1] to land
> prior to this series. So, I'm just anticipating a future rebase where I move
> this code from box_ext.rs to kbox.rs, just like Alice suggested for her
> "ForeignOwnable for Pin<crate::alloc::Box<T, A>>" implementation.
> 
> I also understand your later reply, where you said: "[...] then you can just
> include it when you remove the `BoxExit` trait." as confirmation.
> 
> Probably that's a misunderstanding though. Sorry if that's the case.

Yeah what I meant by that was you base it on top and then move it from
the `BoxExt` trait over to `Box` in a correctly attributed patch. As I
said above, I don't really mind in this case, since it's trivial, so no
worries. Just a heads-up for occasions where it is non-trivial.

> [1] https://lore.kernel.org/lkml/24a8d381-dd13-4d19-a736-689b8880dbe1@proton.me/
> 
>>
>>> +}
>>> +
>>> +impl<T, A> From<Box<T, A>> for Pin<Box<T, A>>
>>> +where
>>> +    T: ?Sized,
>>> +    A: Allocator,
>>> +    A: 'static,
>>> +{
>>> +    /// Converts a `Box<T>` into a `Pin<Box<T>>`. If `T` does not implement [`Unpin`], then
>>> +    /// `*boxed` will be pinned in memory and unable to be moved.
>>> +    ///
>>> +    /// This conversion does not allocate on the heap and happens in place.
>>> +    ///
>>> +    /// This is also available via [`Box::into_pin`].
>>> +    ///
>>> +    /// Constructing and pinning a `Box` with <code><Pin<Box\<T>>>::from([Box::new]\(x))</code>
>>> +    /// can also be written more concisely using <code>[Box::pin]\(x)</code>.
>>> +    /// This `From` implementation is useful if you already have a `Box<T>`, or you are
>>> +    /// constructing a (pinned) `Box` in a different way than with [`Box::new`].
>>
>> This also looks very much like something from the stdlib...
> 
> Yeah, I'll replace that.
> 
>>
>>> +    fn from(b: Box<T, A>) -> Self {
>>> +        Box::into_pin(b)
>>> +    }
>>> +}
>>> +
>>> +impl<T, A> Deref for Box<T, A>
>>> +where
>>> +    T: ?Sized,
>>> +    A: Allocator,
>>> +{
>>> +    type Target = T;
>>> +
>>> +    fn deref(&self) -> &T {
>>> +        // SAFETY: `self.0` is always properly aligned, dereferenceable and points to an initialized
>>> +        // instance of `T`.
>>
>> If `T` is a ZST, then it is not dereferenceable.
> 
> Why not? If `T` is a ZST `self.0` is `Unique::<T>::dangling()`. So, in the end
> this is the same as `NonNull::<T>::dangling().as_ref()`.

You are right, I just looked at [1] again and they define
dereferenceable as "the memory range of the given size starting at the
pointer must all be within the bounds of a single allocated object", for
a zero-sized allocation, this holds vacuously.

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

>>> +        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 ptr = self.0.as_ptr();
>>> +
>>> +        // SAFETY: `ptr` is always properly aligned, dereferenceable and points to an initialized
>>> +        // instance of `T`.
>>> +        let size = unsafe { core::mem::size_of_val(&*ptr) };
>>
>> 1. `size_of_val` is not `unsafe`.
> 
> Right, but dereferencing the `ptr` is unsafe.
> 
>> 2. why not use `&*self` instead of using the raw pointer? (then move the
>>    let binding below this line)
> 
> If we ever support non-ZST `Allocator`s using `self` would not always evaluate
> to the correct size. I think evaluating the size of `T` rather than `Box<T>` is
> the correct thing to do.

I mean use `Box::deref` (that's what `&*self` should do), you don't need
to repeat the same SAFETY comment when it already is wrapped by a safe
function.

---
Cheers,
Benno


^ permalink raw reply	[flat|nested] 100+ messages in thread

* Re: [PATCH v4 09/28] rust: alloc: implement kernel `Box`
  2024-08-07  7:49       ` Benno Lossin
@ 2024-08-07  7:51         ` Alice Ryhl
  2024-08-07  8:01           ` Benno Lossin
  2024-08-07 10:38         ` Danilo Krummrich
  2024-08-08 17:44         ` Danilo Krummrich
  2 siblings, 1 reply; 100+ messages in thread
From: Alice Ryhl @ 2024-08-07  7:51 UTC (permalink / raw)
  To: Benno Lossin
  Cc: Danilo Krummrich, ojeda, alex.gaynor, wedsonaf, boqun.feng, gary,
	bjorn3_gh, a.hindborg, akpm, daniel.almeida, faith.ekstrand,
	boris.brezillon, lina, mcanal, zhiw, acurrid, cjia, jhubbard,
	airlied, ajanulgu, lyude, linux-kernel, rust-for-linux, linux-mm

On Wed, Aug 7, 2024 at 9:49 AM Benno Lossin <benno.lossin@proton.me> wrote:
>
> On 07.08.24 01:01, Danilo Krummrich wrote:
> > On Tue, Aug 06, 2024 at 07:47:17PM +0000, Benno Lossin wrote:
> >> On 05.08.24 17:19, 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 | 330 ++++++++++++++++++++++++++++++++++++++
> >>>  rust/kernel/init.rs       |  35 +++-
> >>>  rust/kernel/prelude.rs    |   2 +-
> >>>  rust/kernel/types.rs      |  56 +++++++
> >>>  5 files changed, 427 insertions(+), 2 deletions(-)
> >>>  create mode 100644 rust/kernel/alloc/kbox.rs
> >>>
> >>> diff --git a/rust/kernel/alloc.rs b/rust/kernel/alloc.rs
> >>> index 942e2755f217..d7beaf0372af 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..4a4379980745
> >>> --- /dev/null
> >>> +++ b/rust/kernel/alloc/kbox.rs
> >>> @@ -0,0 +1,330 @@
> >>> +// SPDX-License-Identifier: GPL-2.0
> >>> +
> >>> +//! Implementation of [`Box`].
> >>> +
> >>> +use super::{AllocError, Allocator, Flags};
> >>> +use core::fmt;
> >>> +use core::marker::PhantomData;
> >>> +use core::mem::ManuallyDrop;
> >>> +use core::mem::MaybeUninit;
> >>> +use core::ops::{Deref, DerefMut};
> >>> +use core::pin::Pin;
> >>> +use core::result::Result;
> >>> +
> >>> +use crate::types::Unique;
> >>> +
> >>> +/// The kernel's [`Box`] type.
> >>> +///
> >>> +/// `Box` provides the simplest way to allocate memory for a generic type with one of the kernel's
> >>> +/// allocators, e.g. `Kmalloc`, `Vmalloc` or `KVmalloc`.
> >>> +///
> >>> +/// For non-zero-sized values, a [`Box`] will use the given allocator `A` for its allocation. For
> >>> +/// the most common allocators the type aliases `KBox`, `VBox` and `KVBox` exist.
> >>> +///
> >>> +/// It is valid to convert both ways between a [`Box`] and a raw pointer allocated with any
> >>> +/// `Allocator`, given that the `Layout` used with the allocator is correct for the type.
> >>> +///
> >>> +/// For zero-sized values the [`Box`]' pointer must be `dangling_mut::<T>`; no memory is allocated.
> >>
> >> Why do we need this to be in the docs?
> >
> > Probably not - do you suggest to remove it entirely? Otherwise, where do you
> > think we should move it?
>
> I would remove it, since it's just implementation detail and
> allocator-dependent.
>
> >>> +impl<T, A> Box<T, A>
> >>> +where
> >>> +    T: ?Sized,
> >>> +    A: Allocator,
> >>> +{
> >>> +    /// Constructs a `Box<T, A>` from a raw pointer.
> >>> +    ///
> >>> +    /// # Safety
> >>> +    ///
> >>> +    /// `raw` must point to valid memory, previously allocated with `A`, and at least the size of
> >>> +    /// type `T`.
> >>
> >> With this requirement and the invariant on `Box`, I am lead to believe
> >> that you can't use this for ZSTs, since they are not allocated with `A`.
> >> One solution would be to adjust this requirement. But I would rather use
> >> a different solution: we move the dangling pointer stuff into the
> >> allocator and also call it when `T` is a ZST (ie don't special case them
> >> in `Box` but in the impls of `Allocator`). That way this can stay as-is
> >> and the part about ZSTs in the invariant can be removed.
> >
> > Actually, we already got that. Every zero sized allocation will return a
> > dangling pointer. However, we can't call `Allocator::free` with (any) dangling
> > pointer though.
>
> The last part is rather problematic in my opinion, since the safety
> requirements of the functions in `Allocator` don't ensure that you're
> not allowed to do it. We should make it possible to free dangling
> pointers that were previously "allocated" by the allocator (ie returned
> by `realloc`).
> Maybe we do need an `old_layout` parameter for that (that way we can
> also `debug_assert_eq!(old_layout.align(), new_layout.align())`).

The std allocators generally prohibit zero sized allocations, so it
seems sensible for us to do the same?

Alice

^ permalink raw reply	[flat|nested] 100+ messages in thread

* Re: [PATCH v4 09/28] rust: alloc: implement kernel `Box`
  2024-08-07  7:51         ` Alice Ryhl
@ 2024-08-07  8:01           ` Benno Lossin
  2024-08-07 10:44             ` Danilo Krummrich
  0 siblings, 1 reply; 100+ messages in thread
From: Benno Lossin @ 2024-08-07  8:01 UTC (permalink / raw)
  To: Alice Ryhl
  Cc: Danilo Krummrich, ojeda, alex.gaynor, wedsonaf, boqun.feng, gary,
	bjorn3_gh, a.hindborg, akpm, daniel.almeida, faith.ekstrand,
	boris.brezillon, lina, mcanal, zhiw, acurrid, cjia, jhubbard,
	airlied, ajanulgu, lyude, linux-kernel, rust-for-linux, linux-mm

On 07.08.24 09:51, Alice Ryhl wrote:
> On Wed, Aug 7, 2024 at 9:49 AM Benno Lossin <benno.lossin@proton.me> wrote:
>> On 07.08.24 01:01, Danilo Krummrich wrote:
>>> On Tue, Aug 06, 2024 at 07:47:17PM +0000, Benno Lossin wrote:
>>>> On 05.08.24 17:19, Danilo Krummrich wrote:
>>>>> +impl<T, A> Box<T, A>
>>>>> +where
>>>>> +    T: ?Sized,
>>>>> +    A: Allocator,
>>>>> +{
>>>>> +    /// Constructs a `Box<T, A>` from a raw pointer.
>>>>> +    ///
>>>>> +    /// # Safety
>>>>> +    ///
>>>>> +    /// `raw` must point to valid memory, previously allocated with `A`, and at least the size of
>>>>> +    /// type `T`.
>>>>
>>>> With this requirement and the invariant on `Box`, I am lead to believe
>>>> that you can't use this for ZSTs, since they are not allocated with `A`.
>>>> One solution would be to adjust this requirement. But I would rather use
>>>> a different solution: we move the dangling pointer stuff into the
>>>> allocator and also call it when `T` is a ZST (ie don't special case them
>>>> in `Box` but in the impls of `Allocator`). That way this can stay as-is
>>>> and the part about ZSTs in the invariant can be removed.
>>>
>>> Actually, we already got that. Every zero sized allocation will return a
>>> dangling pointer. However, we can't call `Allocator::free` with (any) dangling
>>> pointer though.
>>
>> The last part is rather problematic in my opinion, since the safety
>> requirements of the functions in `Allocator` don't ensure that you're
>> not allowed to do it. We should make it possible to free dangling
>> pointers that were previously "allocated" by the allocator (ie returned
>> by `realloc`).
>> Maybe we do need an `old_layout` parameter for that (that way we can
>> also `debug_assert_eq!(old_layout.align(), new_layout.align())`).
> 
> The std allocators generally prohibit zero sized allocations, so it
> seems sensible for us to do the same?

I never understood why they do that, the stdlib `Allocator` trait has
all the information it needs to detect zero-sized allocations, so it
could just return dangling pointers. I don't see the point of
duplicating the zero-sized logic in `Box` and `Vec`...

---
Cheers,
Benno


^ permalink raw reply	[flat|nested] 100+ messages in thread

* Re: [PATCH v4 01/28] rust: alloc: add `Allocator` trait
  2024-08-06 20:04       ` Benno Lossin
@ 2024-08-07  9:36         ` Danilo Krummrich
  2024-08-07 20:00           ` Benno Lossin
  0 siblings, 1 reply; 100+ messages in thread
From: Danilo Krummrich @ 2024-08-07  9:36 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, acurrid, cjia, jhubbard,
	airlied, ajanulgu, lyude, linux-kernel, rust-for-linux, linux-mm

On Tue, Aug 06, 2024 at 08:04:30PM +0000, Benno Lossin wrote:
> >>> +    /// instance. The alignment encoded in `layout` must be smaller than or equal to the alignment
> >>> +    /// requested in the previous `alloc` or `realloc` call of the same allocation.
> >>> +    ///
> >>> +    /// Additionally, `ptr` is allowed to be `None`; in this case a new memory allocation is
> >>> +    /// created.
> >>> +    ///
> >>> +    unsafe fn realloc(
> >>> +        ptr: Option<NonNull<u8>>,
> >>> +        layout: Layout,
> >>> +        flags: Flags,
> >>> +    ) -> Result<NonNull<[u8]>, AllocError>;
> >>> +
> >>> +    /// Free an existing memory allocation.
> >>> +    ///
> >>> +    /// # Safety
> >>> +    ///
> >>> +    /// `ptr` must point to an existing and valid memory allocation created by this `Allocator`
> >>> +    /// instance.
> >>
> >> Additionally, you need "The memory allocation at `ptr` must never again
> >> be read from or written to.".
> > 
> > I'm fine adding it, but I wonder if technically this is really required? The
> > condition whether the pointer is ever accessed again in any way is not relevant
> > in terms of being a precondition for `free` not causing UB, right?
> 
> I don't see how else we would find the mistake in the following code:
> 
>     let ptr = Box::into_raw(Box::<i32, Kmalloc>::new(42));
>     // SAFETY: `ptr` came from `Box::into_raw` and thus is pointing to a
>     // valid and existing memory allocation allocated by `Kmalloc`.
>     unsafe { Kmalloc::free(ptr) };
>     // SAFETY: `ptr` came from `Box::into_raw` and thus is pointing at a
>     // valid `i32`.
>     let v = unsafe { ptr.read() };

Sure, but what I mean is that my understanding is that the "Safety" section in a
comment describes the requirements of the function it documents. I.e. `free`
itself doesn't care whether the pointer is read or writted ever again.

Or in other words, what are the rules where this belongs to? E.g. why not
document this exact aspect in the safety section of `Allocator`?

> 
> Also see the `from_raw` for our `Arc`:
> 
>     /// Recreates an [`Arc`] instance previously deconstructed via [`Arc::into_raw`].
>     ///
>     /// # Safety
>     ///
>     /// `ptr` must have been returned by a previous call to [`Arc::into_raw`]. Additionally, it
>     /// must not be called more than once for each previous call to [`Arc::into_raw`].
>     pub unsafe fn from_raw(ptr: *const T) -> Self {
> 
> That also requires that the function must not be called more than once.
> This reminds me, I forgot to say that about `Box::from_raw`.

Indeed, I also wonder if we ever have cases where C code gives us ownership of a
memory allocation of a certain type that fulfills the requirements we have for
a `Box`, such that Rust code is tempted to pass it to `Box::from_raw`.

It sounds a bit scary design wise, but in theory it's possible.

> 
> ---
> Cheers,
> Benno
> 

^ permalink raw reply	[flat|nested] 100+ messages in thread

* Re: [PATCH v4 04/28] rust: alloc: implement `Allocator` for `Kmalloc`
  2024-08-07  7:14       ` Benno Lossin
@ 2024-08-07 10:11         ` Danilo Krummrich
  2024-08-07 20:15           ` Benno Lossin
  0 siblings, 1 reply; 100+ messages in thread
From: Danilo Krummrich @ 2024-08-07 10:11 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, acurrid, cjia, jhubbard,
	airlied, ajanulgu, lyude, linux-kernel, rust-for-linux, linux-mm

On Wed, Aug 07, 2024 at 07:14:13AM +0000, Benno Lossin wrote:
> On 06.08.24 20:55, Danilo Krummrich wrote:
> > On Tue, Aug 06, 2024 at 04:51:28PM +0000, Benno Lossin wrote:
> >> On 05.08.24 17:19, Danilo Krummrich wrote:
> >>> +        let raw_ptr = unsafe {
> >>> +            // If `size == 0` and `ptr != NULL` the memory behind the pointer is freed.
> >>> +            self.0(ptr.cast(), size, flags.0).cast()
> >>> +        };
> >>> +
> >>> +        let ptr = if size == 0 {
> >>> +            NonNull::dangling()
> >>
> >> If we call `realloc(Some(ptr), <layout with size = 0>, ...)`, then this
> >> leaks the pointer returned by the call to `self.0` above. I don't know
> >> what the return value of the different functions are that can appear in
> >> `self.0`, do they return NULL?
> > 
> > That is fine, we don't care about the return value. All `ReallocFunc` free the
> > memory behind `ptr` if called with a size of zero. But to answer the question,
> > they return either NULL or ZERO_SIZE_PTR.
> 
> I see, then it's fine. I think it would help if we know the exact
> behavior of `kmalloc` & friends (either add a link to C docs or write it
> down on `ReallocFunc`).
> 
> >> What about the following sequence:
> >>
> >>     let ptr = realloc(None, <layout with size = 0>, ...);
> >>     let ptr = realloc(Some(ptr), <layout with size = 0>, ...);
> >>
> >> Then the above call to `self.0` is done with a dangling pointer, can the
> >> functions that appear in `self.0` handle that?
> > 
> > This would be incorrect.
> > 
> > Calling `realloc(Some(ptr), <layout with size = 0>, ...)` frees the memory
> > behind `ptr`. This is guranteed behavior for all `ReallocFunc`s, i.e.
> > krealloc(), vrealloc(), kvrealloc().
> 
> Note that I don't use `ptr` afterwards, the code snippet above is
> equivalent to this:
> 
>     let ptr = Kmalloc::alloc(<layout with size = 0>, ...);
>     unsafe { Kmalloc::free(ptr) };
> 
> internally exactly the realloc calls that I put above should be called.

I think I misunderstood what you mean here.

So, that's not permitted. `free` can't be called with a dangling pointer. The
kernel free functions (*1) can't handle it, and I can't detect it, since a
dangling pointer does not have a descrete value.

We can decide for a specific dangling pointer to be allowed, i.e. the dangling
pointer returned by `alloc` for a zero sized allocation is always
`dangling<u8>`, so we can assert that `free` is only allowed to be called with
what was previously returned by `alloc` or `free` and therefore disallow
dangling pointers with a different alignment.

Surely, we could also let the caller pass the old alignment, but this all sounds
complicated for something that is very trivial for the caller to take care of,
i.e. just don't try to free something that was never actually allocated.

It can also lead to subtle bugs, e.g. what if someone calls `Box::from_raw` for
a ZST with some other random pointer? Currently, that doesn't hurt us, which for
robustness, seems to be a good thing.

I think it's better to just let `Box` and `Vec` figure out if calling `free` is
the right thing to do. The code for that is simple and obvious, i.e. check if
`T` is a ZST.

*1: kfree() can handle dangling pointers up to 16 bytes aligned, see
ZERO_OR_NULL_PTR(x).

> 
> ---
> Cheers,
> Benno
> 

^ permalink raw reply	[flat|nested] 100+ messages in thread

* Re: [PATCH v4 08/28] rust: types: implement `Unique<T>`
  2024-08-07  7:27       ` Benno Lossin
@ 2024-08-07 10:13         ` Danilo Krummrich
  0 siblings, 0 replies; 100+ messages in thread
From: Danilo Krummrich @ 2024-08-07 10:13 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, acurrid, cjia, jhubbard,
	airlied, ajanulgu, lyude, linux-kernel, rust-for-linux, linux-mm

On Wed, Aug 07, 2024 at 07:27:43AM +0000, Benno Lossin wrote:
> On 07.08.24 01:12, Danilo Krummrich wrote:
> > On Tue, Aug 06, 2024 at 05:22:21PM +0000, Benno Lossin wrote:
> >> On 05.08.24 17:19, Danilo Krummrich wrote:
> >>> +impl<T: Sized> Unique<T> {
> >>> +    /// Creates a new `Unique` that is dangling, but well-aligned.
> >>> +    ///
> >>> +    /// This is useful for initializing types which lazily allocate, like
> >>> +    /// `Vec::new` does.
> >>> +    ///
> >>> +    /// Note that the pointer value may potentially represent a valid pointer to
> >>> +    /// a `T`, which means this must not be used as a "not yet initialized"
> >>> +    /// sentinel value. Types that lazily allocate must track initialization by
> >>> +    /// some other means.
> >>> +    #[must_use]
> >>> +    #[inline]
> >>> +    pub const fn dangling() -> Self {
> >>> +        Unique {
> >>> +            pointer: NonNull::dangling(),
> >>> +            _marker: PhantomData,
> >>> +        }
> >>> +    }
> >>
> >> I think I already asked this, but the code until this point is copied
> >> from the rust stdlib and nowhere cited, does that work with the
> >> licensing?
> >>
> >> I also think that the code above could use some improvements:
> >> - add an `# Invariants` section with appropriate invariants (what are
> >>   they supposed to be?)
> >> - Do we really want this type to be public and exported from the kernel
> >>   crate? I think it would be better if it were crate-private.
> >> - What do we gain from having this type? As I learned recently, the
> >>   `Unique` type from `core` doesn't actually put the `noalias` onto
> >>   `Box` and `Vec`. The functions are mostly delegations to `NonNull`, so
> >>   if the only advantages are that `Send` and `Sync` are already
> >>   implemented, then I think we should drop this.
> > 
> > I originally introduced it for the reasons described in [1], but mainly to make
> > clear that the owner of this thing also owns the memory behind the pointer and
> > the `Send` and `Sync` stuff you already mentioned.
> 
> I would prefer if we make that explicit, since it is rather error-prone
> when creating new pointer types (and one should have to think about
> thread safety).

Again, fine for me. If no one else has objections I'll just drop `Unique`.

> 
> ---
> Cheers,
> Benno
> 
> > If no one else has objections we can also just drop it. Personally, I'm fine
> > either way.
> > 
> > [1] https://docs.rs/rust-libcore/latest/core/ptr/struct.Unique.html
> 

^ permalink raw reply	[flat|nested] 100+ messages in thread

* Re: [PATCH v4 05/28] rust: alloc: add module `allocator_test`
  2024-08-07  7:20       ` Benno Lossin
@ 2024-08-07 10:16         ` Danilo Krummrich
  0 siblings, 0 replies; 100+ messages in thread
From: Danilo Krummrich @ 2024-08-07 10:16 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, acurrid, cjia, jhubbard,
	airlied, ajanulgu, lyude, linux-kernel, rust-for-linux, linux-mm

On Wed, Aug 07, 2024 at 07:20:03AM +0000, Benno Lossin wrote:
> On 06.08.24 20:58, Danilo Krummrich wrote:
> > On Tue, Aug 06, 2024 at 04:54:10PM +0000, Benno Lossin wrote:
> >> On 05.08.24 17:19, Danilo Krummrich wrote:
> >>> `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.
> >>
> >> This is confusing, since you are talking about both our new `Allocator`
> >> trait, allocators and the `alloc` crate `Allocator`.
> > 
> > I never mention the `alloc` crate `Allocator` here.
> 
> Seems like I confused myself...
> 
> >>> Reviewed-by: Alice Ryhl <aliceryhl@google.com>
> >>> 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 bc01a17df5e0..942e2755f217 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!();
> >>
> >> Does `build_error!()` also work? If yes, then I would prefer that.
> > 
> > Probably, but it also probably doesn't matter too much. A later patch in the
> > series replaces this with a `Cmalloc` implementation and type aliases all kernel
> > allocators (e.g. `Kmalloc`) to it.
> 
> What prevents you from doing the `Cmalloc` patch here? `build_error!`
> probably doesn't work, since we probably allocate in rusttest, right?

I think it's better to have the creation of the allocator_test module and the
implementation of `Cmalloc` in separate patches. `Cmalloc` comes later because I
don't need it yet. I need it to get rid of building Rust's `alloc` crate and I
think it's good to have those patches close to each other.

> 
> ---
> Cheers,
> Benno
> 

^ permalink raw reply	[flat|nested] 100+ messages in thread

* Re: [PATCH v4 09/28] rust: alloc: implement kernel `Box`
  2024-08-07  7:49       ` Benno Lossin
  2024-08-07  7:51         ` Alice Ryhl
@ 2024-08-07 10:38         ` Danilo Krummrich
  2024-08-07 19:45           ` Benno Lossin
  2024-08-08 17:44         ` Danilo Krummrich
  2 siblings, 1 reply; 100+ messages in thread
From: Danilo Krummrich @ 2024-08-07 10:38 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, acurrid, cjia, jhubbard,
	airlied, ajanulgu, lyude, linux-kernel, rust-for-linux, linux-mm

On Wed, Aug 07, 2024 at 07:49:31AM +0000, Benno Lossin wrote:
> >> With this requirement and the invariant on `Box`, I am lead to believe
> >> that you can't use this for ZSTs, since they are not allocated with `A`.
> >> One solution would be to adjust this requirement. But I would rather use
> >> a different solution: we move the dangling pointer stuff into the
> >> allocator and also call it when `T` is a ZST (ie don't special case them
> >> in `Box` but in the impls of `Allocator`). That way this can stay as-is
> >> and the part about ZSTs in the invariant can be removed.
> > 
> > Actually, we already got that. Every zero sized allocation will return a
> > dangling pointer. However, we can't call `Allocator::free` with (any) dangling
> > pointer though.
> 
> The last part is rather problematic in my opinion, since the safety
> requirements of the functions in `Allocator` don't ensure that you're
> not allowed to do it.

Yes, I think it needs to be added.

> We should make it possible to free dangling
> pointers that were previously "allocated" by the allocator (ie returned
> by `realloc`).
> Maybe we do need an `old_layout` parameter for that (that way we can
> also `debug_assert_eq!(old_layout.align(), new_layout.align())`).

Please see my reply in [1] - let's continue the discussion there.

[1] https://lore.kernel.org/rust-for-linux/ZrNIaAcGkGU0d8I3@pollux/

> 
> >>> +    {
> >>> +        Ok(Self::new(x, flags)?.into())
> >>> +    }
> >>> +
> >>> +    /// Drops the contents, but keeps the allocation.
> >>> +    ///
> >>> +    /// # Examples
> >>> +    ///
> >>> +    /// ```
> >>> +    /// let value = KBox::new([0; 32], GFP_KERNEL)?;
> >>> +    /// assert_eq!(*value, [0; 32]);
> >>> +    /// let value = KBox::drop_contents(value);
> >>> +    /// // Now we can re-use `value`:
> >>> +    /// let value = KBox::write(value, [1; 32]);
> >>> +    /// assert_eq!(*value, [1; 32]);
> >>> +    /// # Ok::<(), Error>(())
> >>> +    /// ```
> >>> +    pub fn drop_contents(this: Self) -> Box<MaybeUninit<T>, A> {
> >>> +        let ptr = Box::into_raw(this);
> >>> +        // SAFETY: `ptr` is valid, because it came from `Box::into_raw`.
> >>> +        unsafe { core::ptr::drop_in_place(ptr) };
> >>> +        // SAFETY: `ptr` is valid, because it came from `Box::into_raw`.
> >>> +        unsafe { Box::from_raw(ptr.cast()) }
> >>> +    }
> >>
> >> I don't particularly care in this instance, but you just took my patch
> >> and folded it into your own without asking me or specifying it in the
> >> commit message. In general I would have assumed that you just put the
> >> entire patch into the series (with correct From:... etc).
> > 
> > When I asked about this in [1] my understanding was that we expect [1] to land
> > prior to this series. So, I'm just anticipating a future rebase where I move
> > this code from box_ext.rs to kbox.rs, just like Alice suggested for her
> > "ForeignOwnable for Pin<crate::alloc::Box<T, A>>" implementation.
> > 
> > I also understand your later reply, where you said: "[...] then you can just
> > include it when you remove the `BoxExit` trait." as confirmation.
> > 
> > Probably that's a misunderstanding though. Sorry if that's the case.
> 
> Yeah what I meant by that was you base it on top and then move it from
> the `BoxExt` trait over to `Box` in a correctly attributed patch.

I don't understand this. What do you mean with "correctly attributed patch" in
this case?

There are various existing implementations around `Box` and `BoxExt`, are you
saying that I should create separate patches for moving / adapting all of them,
e.g. this patch adapts parts from `BoxExt`, the implementation of
`ForeignOwnable` for `Box<T>`, the implementation of `InPlaceInit<T>` for
`Box<T>`.

I don't think this is necessary.

I probably shouldn't anticipate a future rebase though, it just leads to
confusion. I'll drop it for now and re-add it once your patch lands in rust-next.

> As I
> said above, I don't really mind in this case, since it's trivial, so no
> worries. Just a heads-up for occasions where it is non-trivial.
> 
> > [1] https://lore.kernel.org/lkml/24a8d381-dd13-4d19-a736-689b8880dbe1@proton.me/
> > 
> >>
> >>> +}
> >>> +
> >>> +impl<T, A> From<Box<T, A>> for Pin<Box<T, A>>
> >>> +where
> >>> +    T: ?Sized,
> >>> +    A: Allocator,
> >>> +    A: 'static,
> >>> +{
> >>> +    /// Converts a `Box<T>` into a `Pin<Box<T>>`. If `T` does not implement [`Unpin`], then
> >>> +    /// `*boxed` will be pinned in memory and unable to be moved.
> >>> +    ///
> >>> +    /// This conversion does not allocate on the heap and happens in place.
> >>> +    ///
> >>> +    /// This is also available via [`Box::into_pin`].
> >>> +    ///
> >>> +    /// Constructing and pinning a `Box` with <code><Pin<Box\<T>>>::from([Box::new]\(x))</code>
> >>> +    /// can also be written more concisely using <code>[Box::pin]\(x)</code>.
> >>> +    /// This `From` implementation is useful if you already have a `Box<T>`, or you are
> >>> +    /// constructing a (pinned) `Box` in a different way than with [`Box::new`].
> >>
> >> This also looks very much like something from the stdlib...
> > 
> > Yeah, I'll replace that.
> > 
> >>
> >>> +    fn from(b: Box<T, A>) -> Self {
> >>> +        Box::into_pin(b)
> >>> +    }
> >>> +}
> >>> +
> >>> +impl<T, A> Deref for Box<T, A>
> >>> +where
> >>> +    T: ?Sized,
> >>> +    A: Allocator,
> >>> +{
> >>> +    type Target = T;
> >>> +
> >>> +    fn deref(&self) -> &T {
> >>> +        // SAFETY: `self.0` is always properly aligned, dereferenceable and points to an initialized
> >>> +        // instance of `T`.
> >>
> >> If `T` is a ZST, then it is not dereferenceable.
> > 
> > Why not? If `T` is a ZST `self.0` is `Unique::<T>::dangling()`. So, in the end
> > this is the same as `NonNull::<T>::dangling().as_ref()`.
> 
> You are right, I just looked at [1] again and they define
> dereferenceable as "the memory range of the given size starting at the
> pointer must all be within the bounds of a single allocated object", for
> a zero-sized allocation, this holds vacuously.
> 
> [1]: https://doc.rust-lang.org/core/ptr/index.html#safety
> 
> >>> +        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 ptr = self.0.as_ptr();
> >>> +
> >>> +        // SAFETY: `ptr` is always properly aligned, dereferenceable and points to an initialized
> >>> +        // instance of `T`.
> >>> +        let size = unsafe { core::mem::size_of_val(&*ptr) };
> >>
> >> 1. `size_of_val` is not `unsafe`.
> > 
> > Right, but dereferencing the `ptr` is unsafe.
> > 
> >> 2. why not use `&*self` instead of using the raw pointer? (then move the
> >>    let binding below this line)
> > 
> > If we ever support non-ZST `Allocator`s using `self` would not always evaluate
> > to the correct size. I think evaluating the size of `T` rather than `Box<T>` is
> > the correct thing to do.
> 
> I mean use `Box::deref` (that's what `&*self` should do), you don't need
> to repeat the same SAFETY comment when it already is wrapped by a safe
> function.

Oh, yes, that's indeed a good suggestion.

> 
> ---
> Cheers,
> Benno
> 

^ permalink raw reply	[flat|nested] 100+ messages in thread

* Re: [PATCH v4 09/28] rust: alloc: implement kernel `Box`
  2024-08-07  8:01           ` Benno Lossin
@ 2024-08-07 10:44             ` Danilo Krummrich
  0 siblings, 0 replies; 100+ messages in thread
From: Danilo Krummrich @ 2024-08-07 10:44 UTC (permalink / raw)
  To: Benno Lossin
  Cc: Alice Ryhl, ojeda, alex.gaynor, wedsonaf, boqun.feng, gary,
	bjorn3_gh, a.hindborg, akpm, daniel.almeida, faith.ekstrand,
	boris.brezillon, lina, mcanal, zhiw, acurrid, cjia, jhubbard,
	airlied, ajanulgu, lyude, linux-kernel, rust-for-linux, linux-mm

On Wed, Aug 07, 2024 at 08:01:00AM +0000, Benno Lossin wrote:
> On 07.08.24 09:51, Alice Ryhl wrote:
> > On Wed, Aug 7, 2024 at 9:49 AM Benno Lossin <benno.lossin@proton.me> wrote:
> >> On 07.08.24 01:01, Danilo Krummrich wrote:
> >>> On Tue, Aug 06, 2024 at 07:47:17PM +0000, Benno Lossin wrote:
> >>>> On 05.08.24 17:19, Danilo Krummrich wrote:
> >>>>> +impl<T, A> Box<T, A>
> >>>>> +where
> >>>>> +    T: ?Sized,
> >>>>> +    A: Allocator,
> >>>>> +{
> >>>>> +    /// Constructs a `Box<T, A>` from a raw pointer.
> >>>>> +    ///
> >>>>> +    /// # Safety
> >>>>> +    ///
> >>>>> +    /// `raw` must point to valid memory, previously allocated with `A`, and at least the size of
> >>>>> +    /// type `T`.
> >>>>
> >>>> With this requirement and the invariant on `Box`, I am lead to believe
> >>>> that you can't use this for ZSTs, since they are not allocated with `A`.
> >>>> One solution would be to adjust this requirement. But I would rather use
> >>>> a different solution: we move the dangling pointer stuff into the
> >>>> allocator and also call it when `T` is a ZST (ie don't special case them
> >>>> in `Box` but in the impls of `Allocator`). That way this can stay as-is
> >>>> and the part about ZSTs in the invariant can be removed.
> >>>
> >>> Actually, we already got that. Every zero sized allocation will return a
> >>> dangling pointer. However, we can't call `Allocator::free` with (any) dangling
> >>> pointer though.
> >>
> >> The last part is rather problematic in my opinion, since the safety
> >> requirements of the functions in `Allocator` don't ensure that you're
> >> not allowed to do it. We should make it possible to free dangling
> >> pointers that were previously "allocated" by the allocator (ie returned
> >> by `realloc`).
> >> Maybe we do need an `old_layout` parameter for that (that way we can
> >> also `debug_assert_eq!(old_layout.align(), new_layout.align())`).
> > 
> > The std allocators generally prohibit zero sized allocations, so it
> > seems sensible for us to do the same?
> 
> I never understood why they do that, the stdlib `Allocator` trait has
> all the information it needs to detect zero-sized allocations, so it
> could just return dangling pointers. I don't see the point of
> duplicating the zero-sized logic in `Box` and `Vec`...

I think it's simpler and less error prone.

Besides that, I think the stdlib `Box` allows to call `from_raw` with any random
pointer for ZST, which the allocator API can't catch.

> 
> ---
> Cheers,
> Benno
> 

^ permalink raw reply	[flat|nested] 100+ messages in thread

* Re: [PATCH v4 10/28] rust: treewide: switch to our kernel `Box` type
  2024-08-05 15:19 ` [PATCH v4 10/28] rust: treewide: switch to our kernel `Box` type Danilo Krummrich
@ 2024-08-07 12:42   ` Alice Ryhl
  2024-08-07 20:57   ` Benno Lossin
  1 sibling, 0 replies; 100+ messages in thread
From: Alice Ryhl @ 2024-08-07 12:42 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, acurrid, cjia, jhubbard,
	airlied, ajanulgu, lyude, linux-kernel, rust-for-linux, linux-mm

On Mon, Aug 5, 2024 at 5:21 PM 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.
>
> Signed-off-by: Danilo Krummrich <dakr@kernel.org>

Reviewed-by: Alice Ryhl <aliceryhl@google.com>

^ permalink raw reply	[flat|nested] 100+ messages in thread

* Re: [PATCH v4 19/28] rust: alloc: remove `VecExt` extension
  2024-08-05 15:19 ` [PATCH v4 19/28] rust: alloc: remove `VecExt` extension Danilo Krummrich
@ 2024-08-07 12:42   ` Alice Ryhl
  2024-08-08  9:14   ` Benno Lossin
  1 sibling, 0 replies; 100+ messages in thread
From: Alice Ryhl @ 2024-08-07 12:42 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, acurrid, cjia, jhubbard,
	airlied, ajanulgu, lyude, linux-kernel, rust-for-linux, linux-mm

On Mon, Aug 5, 2024 at 5:22 PM Danilo Krummrich <dakr@kernel.org> wrote:
>
> Now that all existing `Vec` users were moved to the kernel `Vec` type,
> remove the `VecExt` extension.
>
> Signed-off-by: Danilo Krummrich <dakr@kernel.org>

Reviewed-by: Alice Ryhl <aliceryhl@google.com>

^ permalink raw reply	[flat|nested] 100+ messages in thread

* Re: [PATCH v4 18/28] rust: treewide: switch to the kernel `Vec` type
  2024-08-05 15:19 ` [PATCH v4 18/28] rust: treewide: switch to the kernel `Vec` type Danilo Krummrich
@ 2024-08-07 13:47   ` Alice Ryhl
  2024-08-08  9:08   ` Benno Lossin
  1 sibling, 0 replies; 100+ messages in thread
From: Alice Ryhl @ 2024-08-07 13:47 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, acurrid, cjia, jhubbard,
	airlied, ajanulgu, lyude, linux-kernel, rust-for-linux, linux-mm

On Mon, Aug 5, 2024 at 5:21 PM 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.
>
> Signed-off-by: Danilo Krummrich <dakr@kernel.org>

I have two nits. With them fixed, you can add:

Reviewed-by: Alice Ryhl <aliceryhl@google.com>

> -        <Vec<_> as VecExt<_>>::extend_from_slice(&mut buf, cstr.as_bytes_with_nul(), GFP_KERNEL)
> -            .map_err(|_| AllocError)?;
> +        KVec::extend_from_slice(&mut buf, cstr.as_bytes_with_nul(), GFP_KERNEL)?;

This would be more idiomatic as
buf.extend_from_slice(cstr.as_bytes_with_nul(), GFP_KERNEL)?;

> @@ -291,9 +288,9 @@ pub fn read<T: FromBytes>(&mut self) -> Result<T> {
>      /// Reads the entirety of the user slice, appending it to the end of the provided buffer.
>      ///
>      /// Fails with [`EFAULT`] if the read happens on a bad address.
> -    pub fn read_all(mut self, buf: &mut Vec<u8>, flags: Flags) -> Result {
> +    pub fn read_all(mut self, buf: &mut KVec<u8>, flags: Flags) -> Result {
>          let len = self.length;
> -        VecExt::<u8>::reserve(buf, len, flags)?;
> +        KVec::<u8>::reserve(buf, len, flags)?;

Ditto here. Could be:
buf.reserve(len, flags)?;

Alice

^ permalink raw reply	[flat|nested] 100+ messages in thread

* Re: [PATCH v4 27/28] rust: alloc: update module comment of alloc.rs
  2024-08-05 15:19 ` [PATCH v4 27/28] rust: alloc: update module comment of alloc.rs Danilo Krummrich
@ 2024-08-07 13:50   ` Alice Ryhl
  2024-08-08  6:59   ` Benno Lossin
  1 sibling, 0 replies; 100+ messages in thread
From: Alice Ryhl @ 2024-08-07 13:50 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, acurrid, cjia, jhubbard,
	airlied, ajanulgu, lyude, linux-kernel, rust-for-linux, linux-mm

On Mon, Aug 5, 2024 at 5:22 PM 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.
>
> Signed-off-by: Danilo Krummrich <dakr@kernel.org>

Reviewed-by: Alice Ryhl <aliceryhl@google.com>

^ permalink raw reply	[flat|nested] 100+ messages in thread

* Re: [PATCH v4 26/28] rust: str: test: replace `alloc::format`
  2024-08-05 15:19 ` [PATCH v4 26/28] rust: str: test: replace `alloc::format` Danilo Krummrich
@ 2024-08-07 13:51   ` Alice Ryhl
  2024-08-08  7:22   ` Benno Lossin
  1 sibling, 0 replies; 100+ messages in thread
From: Alice Ryhl @ 2024-08-07 13:51 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, acurrid, cjia, jhubbard,
	airlied, ajanulgu, lyude, linux-kernel, rust-for-linux, linux-mm

On Mon, Aug 5, 2024 at 5:22 PM 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`.
>
> Signed-off-by: Danilo Krummrich <dakr@kernel.org>

Reviewed-by: Alice Ryhl <aliceryhl@google.com>

^ permalink raw reply	[flat|nested] 100+ messages in thread

* Re: [PATCH v4 24/28] rust: alloc: implement `contains` for `Flags`
  2024-08-05 15:19 ` [PATCH v4 24/28] rust: alloc: implement `contains` for `Flags` Danilo Krummrich
@ 2024-08-07 13:53   ` Alice Ryhl
  0 siblings, 0 replies; 100+ messages in thread
From: Alice Ryhl @ 2024-08-07 13:53 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, acurrid, cjia, jhubbard,
	airlied, ajanulgu, lyude, linux-kernel, rust-for-linux, linux-mm

On Mon, Aug 5, 2024 at 5:22 PM 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.
>
> Signed-off-by: Danilo Krummrich <dakr@kernel.org>

One nit, but:

Reviewed-by: Alice Ryhl <aliceryhl@google.com>

>  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 8a1cecc20d09..615afe69d097 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
> +    }

Since this type is Copy, you can do this:

pub fn contains(self, flags: Flags) -> bool {
    (self & flags) == flags
}

and avoid the asterisk.

Alice

^ permalink raw reply	[flat|nested] 100+ messages in thread

* Re: [PATCH v4 23/28] rust: error: check for config `test` in `Error::name`
  2024-08-05 15:19 ` [PATCH v4 23/28] rust: error: check for config `test` in `Error::name` Danilo Krummrich
@ 2024-08-07 13:54   ` Alice Ryhl
  0 siblings, 0 replies; 100+ messages in thread
From: Alice Ryhl @ 2024-08-07 13:54 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, acurrid, cjia, jhubbard,
	airlied, ajanulgu, lyude, linux-kernel, rust-for-linux, linux-mm

On Mon, Aug 5, 2024 at 5:22 PM 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 `Èrror` in test

That's a weird E.

> cases.
>
> Signed-off-by: Danilo Krummrich <dakr@kernel.org>

Reviewed-by: Alice Ryhl <aliceryhl@google.com>

^ permalink raw reply	[flat|nested] 100+ messages in thread

* Re: [PATCH v4 22/28] rust: error: use `core::alloc::LayoutError`
  2024-08-05 15:19 ` [PATCH v4 22/28] rust: error: use `core::alloc::LayoutError` Danilo Krummrich
@ 2024-08-07 13:55   ` Alice Ryhl
  2024-08-08  7:42   ` Benno Lossin
  1 sibling, 0 replies; 100+ messages in thread
From: Alice Ryhl @ 2024-08-07 13:55 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, acurrid, cjia, jhubbard,
	airlied, ajanulgu, lyude, linux-kernel, rust-for-linux, linux-mm

On Mon, Aug 5, 2024 at 5:22 PM 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.
>
> Signed-off-by: Danilo Krummrich <dakr@kernel.org>

It could make sense to squash some of these super small changes that
prepare for deleting alloc. E.g., patch 27.

Reviewed-by: Alice Ryhl <aliceryhl@google.com>

^ permalink raw reply	[flat|nested] 100+ messages in thread

* Re: [PATCH v4 20/28] rust: alloc: add `Vec` to prelude
  2024-08-05 15:19 ` [PATCH v4 20/28] rust: alloc: add `Vec` to prelude Danilo Krummrich
@ 2024-08-07 13:55   ` Alice Ryhl
  2024-08-08  8:40   ` Benno Lossin
  1 sibling, 0 replies; 100+ messages in thread
From: Alice Ryhl @ 2024-08-07 13:55 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, acurrid, cjia, jhubbard,
	airlied, ajanulgu, lyude, linux-kernel, rust-for-linux, linux-mm

On Mon, Aug 5, 2024 at 5:22 PM 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.
>
> Signed-off-by: Danilo Krummrich <dakr@kernel.org>

Reviewed-by: Alice Ryhl <aliceryhl@google.com>

^ permalink raw reply	[flat|nested] 100+ messages in thread

* Re: [PATCH v4 01/28] rust: alloc: add `Allocator` trait
  2024-08-05 15:19 ` [PATCH v4 01/28] rust: alloc: add `Allocator` trait Danilo Krummrich
  2024-08-06 15:00   ` Alice Ryhl
  2024-08-06 16:03   ` Benno Lossin
@ 2024-08-07 18:19   ` Gary Guo
  2 siblings, 0 replies; 100+ messages in thread
From: Gary Guo @ 2024-08-07 18:19 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, acurrid, cjia, jhubbard,
	airlied, ajanulgu, lyude, linux-kernel, rust-for-linux, linux-mm

On Mon,  5 Aug 2024 17:19:20 +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`.
> 
> Signed-off-by: Danilo Krummrich <dakr@kernel.org>

I agree with Alice's comments that mentioning of NULL is confusing and
should be changed to refer to None.

Some extra nits below.

Best,
Gary

> ---
>  rust/kernel/alloc.rs | 79 ++++++++++++++++++++++++++++++++++++++++++++
>  1 file changed, 79 insertions(+)
> 
> diff --git a/rust/kernel/alloc.rs b/rust/kernel/alloc.rs
> index 1966bd407017..8a71a589469d 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,81 @@ pub mod flags {
>      /// small allocations.
>      pub const GFP_NOWAIT: Flags = Flags(bindings::GFP_NOWAIT);
>  }
> +
> +/// The kernel's [`Allocator`] trait.
> +///
> +/// An implementation of [`Allocator`] can allocate, re-allocate and free memory buffer described
> +/// via [`Layout`].
> +///
> +/// [`Allocator`] is designed to be implemented as a ZST; [`Allocator`] functions do not operate on
> +/// an object instance.
> +///
> +/// In order to be able to support `#[derive(SmartPointer)]` later on, we need to avoid a design
> +/// that requires an `Allocator` to be instantiated, hence its functions must not contain any kind
> +/// of `self` parameter.
> +///
> +/// # Safety
> +///
> +/// Memory returned from an allocator must point to a valid memory buffer and remain valid until
> +/// it is explicitly freed.
> +///
> +/// Any pointer to a memory buffer which is currently allocated must be valid to be passed to any
> +/// other [`Allocator`] function of the same type. The same applies for a NULL pointer.
> +///
> +/// If `realloc` is called with:
> +///   - a size of zero, the given memory allocation, if any, must be freed
> +///   - a NULL pointer, a new memory allocation must be created
> +pub unsafe trait Allocator {
> +    /// Allocate memory based on `layout` and `flags`.
> +    ///
> +    /// On success, returns a buffer represented as `NonNull<[u8]>` that satisfies the layout
> +    /// constraints (i.e. minimum size and alignment as specified by `layout`).
> +    ///
> +    /// This function is equivalent to `realloc` when called with a NULL pointer.
> +    fn alloc(layout: Layout, flags: Flags) -> Result<NonNull<[u8]>, AllocError> {
> +        // SAFETY: Passing a NULL pointer to `realloc` is valid by it's safety requirements and asks
> +        // for a new memory allocation.
> +        unsafe { Self::realloc(None, layout, flags) }
> +    }
> +
> +    /// Re-allocate an existing memory allocation to satisfy the requested `layout`. If the
> +    /// requested size is zero, `realloc` behaves equivalent to `free`.
> +    ///
> +    /// If the requested size is larger than the size of the existing allocation, a successful call
> +    /// to `realloc` guarantees that the new or grown buffer has at least `Layout::size` bytes, but
> +    /// may also be larger.
> +    ///
> +    /// If the requested size is smaller than the size of the existing allocation, `realloc` may or
> +    /// may not shrink the buffer; this is implementation specific to the allocator.
> +    ///
> +    /// On allocation failure, the existing buffer, if any, remains valid.
> +    ///
> +    /// The buffer is represented as `NonNull<[u8]>`.
> +    ///
> +    /// # Safety
> +    ///
> +    /// `Some(ptr)` must point to an existing and valid memory allocation created by this allocator
> +    /// instance. The alignment encoded in `layout` must be smaller than or equal to the alignment

nit: remove `instance` since the methods now have no self parameter.

> +    /// requested in the previous `alloc` or `realloc` call of the same allocation.
> +    ///
> +    /// Additionally, `ptr` is allowed to be `None`; in this case a new memory allocation is
> +    /// created.
> +    ///
> +    unsafe fn realloc(
> +        ptr: Option<NonNull<u8>>,
> +        layout: Layout,
> +        flags: Flags,
> +    ) -> Result<NonNull<[u8]>, AllocError>;
> +
> +    /// Free an existing memory allocation.
> +    ///
> +    /// # Safety
> +    ///
> +    /// `ptr` must point to an existing and valid memory allocation created by this `Allocator`
> +    /// instance.

same here.

> +    unsafe fn free(ptr: NonNull<u8>) {
> +        // SAFETY: `ptr` is guaranteed to be previously allocated with this `Allocator` or NULL.
> +        // Calling `realloc` with a buffer size of zero, frees the buffer `ptr` points to.
> +        let _ = unsafe { Self::realloc(Some(ptr), Layout::new::<()>(), Flags(0)) };
> +    }
> +}


^ permalink raw reply	[flat|nested] 100+ messages in thread

* Re: [PATCH v4 03/28] rust: alloc: rename `KernelAllocator` to `Kmalloc`
  2024-08-05 15:19 ` [PATCH v4 03/28] rust: alloc: rename `KernelAllocator` to `Kmalloc` Danilo Krummrich
  2024-08-06 16:07   ` Benno Lossin
@ 2024-08-07 18:22   ` Gary Guo
  1 sibling, 0 replies; 100+ messages in thread
From: Gary Guo @ 2024-08-07 18: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, acurrid, cjia, jhubbard,
	airlied, ajanulgu, lyude, linux-kernel, rust-for-linux, linux-mm

On Mon,  5 Aug 2024 17:19:22 +0200
Danilo Krummrich <dakr@kernel.org> wrote:

> Subsequent patches implement `Vmalloc` and `KVmalloc` allocators, hence
> align `KernelAllocator` to this naming scheme.
> 
> Reviewed-by: Alice Ryhl <aliceryhl@google.com>
> Signed-off-by: Danilo Krummrich <dakr@kernel.org>

Reviewed-by: Gary Guo <gary@garyguo.net>

> ---
>  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 e7b7eba84acb..2c1eae25da84 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 {
> @@ -40,7 +40,7 @@ pub(crate) unsafe fn krealloc_aligned(ptr: *mut u8, new_layout: Layout, flags: F
>      }
>  }
>  
> -unsafe impl GlobalAlloc for KernelAllocator {
> +unsafe impl GlobalAlloc for Kmalloc {
>      unsafe fn alloc(&self, layout: Layout) -> *mut u8 {
>          // SAFETY: `ptr::null_mut()` is null and `layout` has a non-zero size by the function safety
>          // requirement.
> @@ -76,7 +76,7 @@ unsafe fn alloc_zeroed(&self, layout: Layout) -> *mut u8 {
>  }
>  
>  #[global_allocator]
> -static ALLOCATOR: KernelAllocator = KernelAllocator;
> +static ALLOCATOR: Kmalloc = Kmalloc;
>  
>  // See <https://github.com/rust-lang/rust/pull/86844>.
>  #[no_mangle]


^ permalink raw reply	[flat|nested] 100+ messages in thread

* Re: [PATCH v4 09/28] rust: alloc: implement kernel `Box`
  2024-08-07 10:38         ` Danilo Krummrich
@ 2024-08-07 19:45           ` Benno Lossin
  0 siblings, 0 replies; 100+ messages in thread
From: Benno Lossin @ 2024-08-07 19:45 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, acurrid, cjia, jhubbard,
	airlied, ajanulgu, lyude, linux-kernel, rust-for-linux, linux-mm

On 07.08.24 12:38, Danilo Krummrich wrote:
> On Wed, Aug 07, 2024 at 07:49:31AM +0000, Benno Lossin wrote:
>>>>> +    {
>>>>> +        Ok(Self::new(x, flags)?.into())
>>>>> +    }
>>>>> +
>>>>> +    /// Drops the contents, but keeps the allocation.
>>>>> +    ///
>>>>> +    /// # Examples
>>>>> +    ///
>>>>> +    /// ```
>>>>> +    /// let value = KBox::new([0; 32], GFP_KERNEL)?;
>>>>> +    /// assert_eq!(*value, [0; 32]);
>>>>> +    /// let value = KBox::drop_contents(value);
>>>>> +    /// // Now we can re-use `value`:
>>>>> +    /// let value = KBox::write(value, [1; 32]);
>>>>> +    /// assert_eq!(*value, [1; 32]);
>>>>> +    /// # Ok::<(), Error>(())
>>>>> +    /// ```
>>>>> +    pub fn drop_contents(this: Self) -> Box<MaybeUninit<T>, A> {
>>>>> +        let ptr = Box::into_raw(this);
>>>>> +        // SAFETY: `ptr` is valid, because it came from `Box::into_raw`.
>>>>> +        unsafe { core::ptr::drop_in_place(ptr) };
>>>>> +        // SAFETY: `ptr` is valid, because it came from `Box::into_raw`.
>>>>> +        unsafe { Box::from_raw(ptr.cast()) }
>>>>> +    }
>>>>
>>>> I don't particularly care in this instance, but you just took my patch
>>>> and folded it into your own without asking me or specifying it in the
>>>> commit message. In general I would have assumed that you just put the
>>>> entire patch into the series (with correct From:... etc).
>>>
>>> When I asked about this in [1] my understanding was that we expect [1] to land
>>> prior to this series. So, I'm just anticipating a future rebase where I move
>>> this code from box_ext.rs to kbox.rs, just like Alice suggested for her
>>> "ForeignOwnable for Pin<crate::alloc::Box<T, A>>" implementation.
>>>
>>> I also understand your later reply, where you said: "[...] then you can just
>>> include it when you remove the `BoxExit` trait." as confirmation.
>>>
>>> Probably that's a misunderstanding though. Sorry if that's the case.
>>
>> Yeah what I meant by that was you base it on top and then move it from
>> the `BoxExt` trait over to `Box` in a correctly attributed patch.
> 
> I don't understand this. What do you mean with "correctly attributed patch" in
> this case?

So, seems like I mixed the two approaches I thought of. Here are they
separated:
1. base the series on top of my patches and then copy the implementation
   from the in-tree file this patch.
2. create a new patch that adds `drop_contents` to your Box (ie after
   this patch) that has `Signed-off-by: you` and `Co-Developed-by: me`.
   With this approach I would also ask privately (including the patch)
   if that would be ok.

---
Cheers,
Benno

> There are various existing implementations around `Box` and `BoxExt`, are you
> saying that I should create separate patches for moving / adapting all of them,
> e.g. this patch adapts parts from `BoxExt`, the implementation of
> `ForeignOwnable` for `Box<T>`, the implementation of `InPlaceInit<T>` for
> `Box<T>`.
> 
> I don't think this is necessary.
> 
> I probably shouldn't anticipate a future rebase though, it just leads to
> confusion. I'll drop it for now and re-add it once your patch lands in rust-next.
> 
>> As I
>> said above, I don't really mind in this case, since it's trivial, so no
>> worries. Just a heads-up for occasions where it is non-trivial.
>>
>>> [1] https://lore.kernel.org/lkml/24a8d381-dd13-4d19-a736-689b8880dbe1@proton.me/


^ permalink raw reply	[flat|nested] 100+ messages in thread

* Re: [PATCH v4 01/28] rust: alloc: add `Allocator` trait
  2024-08-07  9:36         ` Danilo Krummrich
@ 2024-08-07 20:00           ` Benno Lossin
  0 siblings, 0 replies; 100+ messages in thread
From: Benno Lossin @ 2024-08-07 20: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, acurrid, cjia, jhubbard,
	airlied, ajanulgu, lyude, linux-kernel, rust-for-linux, linux-mm

On 07.08.24 11:36, Danilo Krummrich wrote:
> On Tue, Aug 06, 2024 at 08:04:30PM +0000, Benno Lossin wrote:
>>>>> +    /// instance. The alignment encoded in `layout` must be smaller than or equal to the alignment
>>>>> +    /// requested in the previous `alloc` or `realloc` call of the same allocation.
>>>>> +    ///
>>>>> +    /// Additionally, `ptr` is allowed to be `None`; in this case a new memory allocation is
>>>>> +    /// created.
>>>>> +    ///
>>>>> +    unsafe fn realloc(
>>>>> +        ptr: Option<NonNull<u8>>,
>>>>> +        layout: Layout,
>>>>> +        flags: Flags,
>>>>> +    ) -> Result<NonNull<[u8]>, AllocError>;
>>>>> +
>>>>> +    /// Free an existing memory allocation.
>>>>> +    ///
>>>>> +    /// # Safety
>>>>> +    ///
>>>>> +    /// `ptr` must point to an existing and valid memory allocation created by this `Allocator`
>>>>> +    /// instance.
>>>>
>>>> Additionally, you need "The memory allocation at `ptr` must never again
>>>> be read from or written to.".
>>>
>>> I'm fine adding it, but I wonder if technically this is really required? The
>>> condition whether the pointer is ever accessed again in any way is not relevant
>>> in terms of being a precondition for `free` not causing UB, right?
>>
>> I don't see how else we would find the mistake in the following code:
>>
>>     let ptr = Box::into_raw(Box::<i32, Kmalloc>::new(42));
>>     // SAFETY: `ptr` came from `Box::into_raw` and thus is pointing to a
>>     // valid and existing memory allocation allocated by `Kmalloc`.
>>     unsafe { Kmalloc::free(ptr) };
>>     // SAFETY: `ptr` came from `Box::into_raw` and thus is pointing at a
>>     // valid `i32`.
>>     let v = unsafe { ptr.read() };
> 
> Sure, but what I mean is that my understanding is that the "Safety" section in a
> comment describes the requirements of the function it documents. I.e. `free`
> itself doesn't care whether the pointer is read or writted ever again.

So if you have an `unsafe` function, the safety requirements are not
limited to ensuring that that function does not exhibit UB. But in
general any correct usage of that function must not exhibit UB in
combination with any other correct usage of any other `unsafe`
functions.

> Or in other words, what are the rules where this belongs to? E.g. why not
> document this exact aspect in the safety section of `Allocator`?

That doesn't work, since the Safety section of `Allocator` is meant for
safety requirements for implementing `Allocator`. They should not be
relevant for using it.

>> Also see the `from_raw` for our `Arc`:
>>
>>     /// Recreates an [`Arc`] instance previously deconstructed via [`Arc::into_raw`].
>>     ///
>>     /// # Safety
>>     ///
>>     /// `ptr` must have been returned by a previous call to [`Arc::into_raw`]. Additionally, it
>>     /// must not be called more than once for each previous call to [`Arc::into_raw`].
>>     pub unsafe fn from_raw(ptr: *const T) -> Self {
>>
>> That also requires that the function must not be called more than once.
>> This reminds me, I forgot to say that about `Box::from_raw`.
> 
> Indeed, I also wonder if we ever have cases where C code gives us ownership of a
> memory allocation of a certain type that fulfills the requirements we have for
> a `Box`, such that Rust code is tempted to pass it to `Box::from_raw`.
> 
> It sounds a bit scary design wise, but in theory it's possible.

I don't think it's scary, in fact we are already doing it and have a
trait for that: `ForeignOwnable`. But that means that the pointer
originates from Rust, so it might not exactly be what you meant ie C
allocates the memory. But that case also doesn't seem too scary for me.
C just needs to respect alignment and then it should be equivalent to
Rust calling the allocator.

---
Cheers,
Benno


^ permalink raw reply	[flat|nested] 100+ messages in thread

* Re: [PATCH v4 04/28] rust: alloc: implement `Allocator` for `Kmalloc`
  2024-08-07 10:11         ` Danilo Krummrich
@ 2024-08-07 20:15           ` Benno Lossin
  2024-08-07 23:05             ` Danilo Krummrich
  0 siblings, 1 reply; 100+ messages in thread
From: Benno Lossin @ 2024-08-07 20:15 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, acurrid, cjia, jhubbard,
	airlied, ajanulgu, lyude, linux-kernel, rust-for-linux, linux-mm

On 07.08.24 12:11, Danilo Krummrich wrote:
> On Wed, Aug 07, 2024 at 07:14:13AM +0000, Benno Lossin wrote:
>> On 06.08.24 20:55, Danilo Krummrich wrote:
>>> On Tue, Aug 06, 2024 at 04:51:28PM +0000, Benno Lossin wrote:
>>>> On 05.08.24 17:19, Danilo Krummrich wrote:
>>>>> +        let raw_ptr = unsafe {
>>>>> +            // If `size == 0` and `ptr != NULL` the memory behind the pointer is freed.
>>>>> +            self.0(ptr.cast(), size, flags.0).cast()
>>>>> +        };
>>>>> +
>>>>> +        let ptr = if size == 0 {
>>>>> +            NonNull::dangling()
>>>>
>>>> If we call `realloc(Some(ptr), <layout with size = 0>, ...)`, then this
>>>> leaks the pointer returned by the call to `self.0` above. I don't know
>>>> what the return value of the different functions are that can appear in
>>>> `self.0`, do they return NULL?
>>>
>>> That is fine, we don't care about the return value. All `ReallocFunc` free the
>>> memory behind `ptr` if called with a size of zero. But to answer the question,
>>> they return either NULL or ZERO_SIZE_PTR.
>>
>> I see, then it's fine. I think it would help if we know the exact
>> behavior of `kmalloc` & friends (either add a link to C docs or write it
>> down on `ReallocFunc`).
>>
>>>> What about the following sequence:
>>>>
>>>>     let ptr = realloc(None, <layout with size = 0>, ...);
>>>>     let ptr = realloc(Some(ptr), <layout with size = 0>, ...);
>>>>
>>>> Then the above call to `self.0` is done with a dangling pointer, can the
>>>> functions that appear in `self.0` handle that?
>>>
>>> This would be incorrect.
>>>
>>> Calling `realloc(Some(ptr), <layout with size = 0>, ...)` frees the memory
>>> behind `ptr`. This is guranteed behavior for all `ReallocFunc`s, i.e.
>>> krealloc(), vrealloc(), kvrealloc().
>>
>> Note that I don't use `ptr` afterwards, the code snippet above is
>> equivalent to this:
>>
>>     let ptr = Kmalloc::alloc(<layout with size = 0>, ...);
>>     unsafe { Kmalloc::free(ptr) };
>>
>> internally exactly the realloc calls that I put above should be called.
> 
> I think I misunderstood what you mean here.
> 
> So, that's not permitted. `free` can't be called with a dangling pointer. The
> kernel free functions (*1) can't handle it, and I can't detect it, since a
> dangling pointer does not have a descrete value.

That is true, but only if we do not have access to the old layout of the
allocation. If we add `old_layout` as a parameter, then we can handle
dangling pointers.

> We can decide for a specific dangling pointer to be allowed, i.e. the dangling
> pointer returned by `alloc` for a zero sized allocation is always
> `dangling<u8>`, so we can assert that `free` is only allowed to be called with
> what was previously returned by `alloc` or `free` and therefore disallow
> dangling pointers with a different alignment.

I don't like that.

> Surely, we could also let the caller pass the old alignment, but this all sounds
> complicated for something that is very trivial for the caller to take care of,
> i.e. just don't try to free something that was never actually allocated.
> 
> It can also lead to subtle bugs, e.g. what if someone calls `Box::from_raw` for
> a ZST with some other random pointer? Currently, that doesn't hurt us, which for
> robustness, seems to be a good thing.

I think we should forbid that. To me it's just plain wrong to take a
random integer literal and cast it to a ZST. IIRC it even is UB if that
points to a previously allocated object that has been freed (but I don't
remember where I read it...).

Also if we use the size of the old layout instead of comparing alignment
with the address of the pointer, then we avoid this issue.

> I think it's better to just let `Box` and `Vec` figure out if calling `free` is
> the right thing to do. The code for that is simple and obvious, i.e. check if
> `T` is a ZST.

I don't think that it is as simple as you make it out to be. To me this
is functionality that we can move into one place and never have to think
about again.
Also if we at some point decide to add some sort of debugging allocator
(am I right in assuming that such a thing already exists for C?) then we
might want to tag on extra data in the allocation, in that case it would
be very useful if the allocator also saw zero-sized allocations, since
we should check all allocations.

> *1: kfree() can handle dangling pointers up to 16 bytes aligned, see
> ZERO_OR_NULL_PTR(x).

I agree with you that we shouldn't rely on that.

---
Cheers,
Benno


^ permalink raw reply	[flat|nested] 100+ messages in thread

* Re: [PATCH v4 10/28] rust: treewide: switch to our kernel `Box` type
  2024-08-05 15:19 ` [PATCH v4 10/28] rust: treewide: switch to our kernel `Box` type Danilo Krummrich
  2024-08-07 12:42   ` Alice Ryhl
@ 2024-08-07 20:57   ` Benno Lossin
  2024-08-07 21:16     ` Benno Lossin
  2024-08-07 23:08     ` Danilo Krummrich
  1 sibling, 2 replies; 100+ messages in thread
From: Benno Lossin @ 2024-08-07 20: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, acurrid, cjia, jhubbard, airlied, ajanulgu, lyude,
	linux-kernel, rust-for-linux, linux-mm

On 05.08.24 17:19, Danilo Krummrich wrote:
> Now that we got the kernel `Box` type in place, convert all existing
> `Box` users to make use of it.

You missed a couple usages of `Box`:
- `rust/macros/lib.rs:{242,251,281}`
- `drivers/block/rnull.rs:{35,50}`

or is that intentional? (for me rnull doesn't compile after this patch)

---
Cheers,
Benno

> Signed-off-by: Danilo Krummrich <dakr@kernel.org>
> ---
>  rust/kernel/init.rs               | 41 ++++++++++++++++---------------
>  rust/kernel/init/__internal.rs    |  2 +-
>  rust/kernel/sync/arc.rs           | 17 ++++++-------
>  rust/kernel/sync/condvar.rs       |  4 +--
>  rust/kernel/sync/lock/mutex.rs    |  2 +-
>  rust/kernel/sync/lock/spinlock.rs |  2 +-
>  rust/kernel/workqueue.rs          | 20 +++++++--------
>  7 files changed, 44 insertions(+), 44 deletions(-)


^ permalink raw reply	[flat|nested] 100+ messages in thread

* Re: [PATCH v4 10/28] rust: treewide: switch to our kernel `Box` type
  2024-08-07 20:57   ` Benno Lossin
@ 2024-08-07 21:16     ` Benno Lossin
  2024-08-07 23:08     ` Danilo Krummrich
  1 sibling, 0 replies; 100+ messages in thread
From: Benno Lossin @ 2024-08-07 21:16 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, acurrid, cjia, jhubbard, airlied, ajanulgu, lyude,
	linux-kernel, rust-for-linux, linux-mm

On 07.08.24 22:57, Benno Lossin wrote:
> On 05.08.24 17:19, Danilo Krummrich wrote:
>> Now that we got the kernel `Box` type in place, convert all existing
>> `Box` users to make use of it.
> 
> You missed a couple usages of `Box`:
> - `rust/macros/lib.rs:{242,251,281}`
> - `drivers/block/rnull.rs:{35,50}`

I also missed some rustdoc links: `rust/kernel/init.rs:{16,746,852}`
(though these should probably link to `Box`)

---
Cheers,
Benno

> or is that intentional? (for me rnull doesn't compile after this patch)
> 
> ---
> Cheers,
> Benno
> 
>> Signed-off-by: Danilo Krummrich <dakr@kernel.org>
>> ---
>>  rust/kernel/init.rs               | 41 ++++++++++++++++---------------
>>  rust/kernel/init/__internal.rs    |  2 +-
>>  rust/kernel/sync/arc.rs           | 17 ++++++-------
>>  rust/kernel/sync/condvar.rs       |  4 +--
>>  rust/kernel/sync/lock/mutex.rs    |  2 +-
>>  rust/kernel/sync/lock/spinlock.rs |  2 +-
>>  rust/kernel/workqueue.rs          | 20 +++++++--------
>>  7 files changed, 44 insertions(+), 44 deletions(-)
> 
> 


^ permalink raw reply	[flat|nested] 100+ messages in thread

* Re: [PATCH v4 21/28] rust: alloc: remove `GlobalAlloc` and `krealloc_aligned`
  2024-08-05 15:19 ` [PATCH v4 21/28] rust: alloc: remove `GlobalAlloc` and `krealloc_aligned` Danilo Krummrich
  2024-08-06 19:07   ` Björn Roy Baron
@ 2024-08-07 21:23   ` Benno Lossin
  2024-08-07 23:16     ` Danilo Krummrich
  1 sibling, 1 reply; 100+ messages in thread
From: Benno Lossin @ 2024-08-07 21: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, acurrid, cjia, jhubbard, airlied, ajanulgu, lyude,
	linux-kernel, rust-for-linux, linux-mm

On 05.08.24 17:19, Danilo Krummrich wrote:
> Now that we have all the `Allocator`s and the kernel `Box` and `Vec`
> implementations in place, remove `GlobalAlloc` and `krealloc_aligned`.

When I try to compile this patch, then I get this error:

      UPD     include/generated/utsversion.h
      CC      init/version-timestamp.o
      KSYMS   .tmp_vmlinux0.kallsyms.S
      AS      .tmp_vmlinux0.kallsyms.o
      LD      .tmp_vmlinux1
    ld.lld: error: undefined symbol: __rust_realloc
    >>> referenced by usercopy_64.c
    >>>               vmlinux.o:(alloc::raw_vec::finish_grow::<alloc::alloc::Global>)
    
    ld.lld: error: undefined symbol: __rust_alloc
    >>> referenced by usercopy_64.c
    >>>               vmlinux.o:(alloc::raw_vec::finish_grow::<alloc::alloc::Global>)
    make[2]: *** [scripts/Makefile.vmlinux:34: vmlinux] Error 1

So maybe the `#[global_allocator]` needs to stay until we remove the
`alloc` dependency?

---
Cheers,
Benno

> Signed-off-by: Danilo Krummrich <dakr@kernel.org>
> ---
>  rust/kernel/alloc/allocator.rs | 63 ++--------------------------------
>  1 file changed, 2 insertions(+), 61 deletions(-)


^ permalink raw reply	[flat|nested] 100+ messages in thread

* Re: [PATCH v4 04/28] rust: alloc: implement `Allocator` for `Kmalloc`
  2024-08-07 20:15           ` Benno Lossin
@ 2024-08-07 23:05             ` Danilo Krummrich
  2024-08-08  8:55               ` Benno Lossin
  0 siblings, 1 reply; 100+ messages in thread
From: Danilo Krummrich @ 2024-08-07 23:05 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, acurrid, cjia, jhubbard,
	airlied, ajanulgu, lyude, linux-kernel, rust-for-linux, linux-mm

On Wed, Aug 07, 2024 at 08:15:41PM +0000, Benno Lossin wrote:
> > So, that's not permitted. `free` can't be called with a dangling pointer. The
> > kernel free functions (*1) can't handle it, and I can't detect it, since a
> > dangling pointer does not have a descrete value.
> 
> That is true, but only if we do not have access to the old layout of the
> allocation. If we add `old_layout` as a parameter, then we can handle
> dangling pointers.

Then we'd need `free` to be `fn free(ptr: NonNull<u8>, layout: Layout)` just to
compare whether `ptr.as_ptr() == layout.align() as _`. Same for `realloc`, but
that's less weird.

It's also not that we safe code with that. `Box`, `Vec`, any other user, still
would have to create the `Layout` before they call `A::free`.

> > Surely, we could also let the caller pass the old alignment, but this all sounds
> > complicated for something that is very trivial for the caller to take care of,
> > i.e. just don't try to free something that was never actually allocated.
> > 
> > It can also lead to subtle bugs, e.g. what if someone calls `Box::from_raw` for
> > a ZST with some other random pointer? Currently, that doesn't hurt us, which for
> > robustness, seems to be a good thing.
> 
> I think we should forbid that. To me it's just plain wrong to take a
> random integer literal and cast it to a ZST. IIRC it even is UB if that
> points to a previously allocated object that has been freed (but I don't
> remember where I read it...).

I think my argument about robustness still holds even if we forbid it.

The documentation says "For operations of size zero, every pointer is valid,
including the null pointer." [1]

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

> 
> Also if we use the size of the old layout instead of comparing alignment
> with the address of the pointer, then we avoid this issue.

That is just another problem when passing the old `Layout` (or maybe just the
old size as usize). Neither do we need the old size, nor do we honor it with any
kernel allocator. This has the following implications:

(1) We never see any error if the old size that is passed is garbage (unless
    it's non-zero, when it should be zero and vice versa), which is bad.

(2) We'd need `free` to be `fn free(ptr: NonNull<u8>, size: usize)`, which is
    confusing, because it implies that an actual free relies on this size for
    freeing the memory.

If we want to avoid (1) and (2), we'd need to make it
`fn free(ptr: NonNull<u8>, zero: bool)` instead, but then also the caller can
just check this boolean and conditionally call `free`.

I don't really see why it's better to let `free` do the `if !zero` check. 

^ permalink raw reply	[flat|nested] 100+ messages in thread

* Re: [PATCH v4 10/28] rust: treewide: switch to our kernel `Box` type
  2024-08-07 20:57   ` Benno Lossin
  2024-08-07 21:16     ` Benno Lossin
@ 2024-08-07 23:08     ` Danilo Krummrich
  1 sibling, 0 replies; 100+ messages in thread
From: Danilo Krummrich @ 2024-08-07 23:08 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, acurrid, cjia, jhubbard,
	airlied, ajanulgu, lyude, linux-kernel, rust-for-linux, linux-mm

On Wed, Aug 07, 2024 at 08:57:22PM +0000, Benno Lossin wrote:
> On 05.08.24 17:19, Danilo Krummrich wrote:
> > Now that we got the kernel `Box` type in place, convert all existing
> > `Box` users to make use of it.
> 
> You missed a couple usages of `Box`:
> - `rust/macros/lib.rs:{242,251,281}`
> - `drivers/block/rnull.rs:{35,50}`
> 
> or is that intentional? (for me rnull doesn't compile after this patch)

No, I missed them. I probably messed up my .config. I really thought I had
everything relevent enabled for compilation.

Gonna fix those.

> 
> ---
> Cheers,
> Benno
> 
> > Signed-off-by: Danilo Krummrich <dakr@kernel.org>
> > ---
> >  rust/kernel/init.rs               | 41 ++++++++++++++++---------------
> >  rust/kernel/init/__internal.rs    |  2 +-
> >  rust/kernel/sync/arc.rs           | 17 ++++++-------
> >  rust/kernel/sync/condvar.rs       |  4 +--
> >  rust/kernel/sync/lock/mutex.rs    |  2 +-
> >  rust/kernel/sync/lock/spinlock.rs |  2 +-
> >  rust/kernel/workqueue.rs          | 20 +++++++--------
> >  7 files changed, 44 insertions(+), 44 deletions(-)
> 

^ permalink raw reply	[flat|nested] 100+ messages in thread

* Re: [PATCH v4 21/28] rust: alloc: remove `GlobalAlloc` and `krealloc_aligned`
  2024-08-07 21:23   ` Benno Lossin
@ 2024-08-07 23:16     ` Danilo Krummrich
  0 siblings, 0 replies; 100+ messages in thread
From: Danilo Krummrich @ 2024-08-07 23:16 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, acurrid, cjia, jhubbard,
	airlied, ajanulgu, lyude, linux-kernel, rust-for-linux, linux-mm

On Wed, Aug 07, 2024 at 09:23:04PM +0000, Benno Lossin wrote:
> On 05.08.24 17:19, Danilo Krummrich wrote:
> > Now that we have all the `Allocator`s and the kernel `Box` and `Vec`
> > implementations in place, remove `GlobalAlloc` and `krealloc_aligned`.
> 
> When I try to compile this patch, then I get this error:
> 
>       UPD     include/generated/utsversion.h
>       CC      init/version-timestamp.o
>       KSYMS   .tmp_vmlinux0.kallsyms.S
>       AS      .tmp_vmlinux0.kallsyms.o
>       LD      .tmp_vmlinux1
>     ld.lld: error: undefined symbol: __rust_realloc
>     >>> referenced by usercopy_64.c
>     >>>               vmlinux.o:(alloc::raw_vec::finish_grow::<alloc::alloc::Global>)
>     
>     ld.lld: error: undefined symbol: __rust_alloc
>     >>> referenced by usercopy_64.c
>     >>>               vmlinux.o:(alloc::raw_vec::finish_grow::<alloc::alloc::Global>)
>     make[2]: *** [scripts/Makefile.vmlinux:34: vmlinux] Error 1
> 
> So maybe the `#[global_allocator]` needs to stay until we remove the
> `alloc` dependency?

Yes, I think so. I compile checked the single patches with `M=rust` and
`M=drivers`, which only compiles, but doesn't link things, so it didn't catch
it.

> 
> ---
> Cheers,
> Benno
> 
> > Signed-off-by: Danilo Krummrich <dakr@kernel.org>
> > ---
> >  rust/kernel/alloc/allocator.rs | 63 ++--------------------------------
> >  1 file changed, 2 insertions(+), 61 deletions(-)
> 

^ permalink raw reply	[flat|nested] 100+ messages in thread

* Re: [PATCH v4 11/28] rust: alloc: remove `BoxExt` extension
  2024-08-05 15:19 ` [PATCH v4 11/28] rust: alloc: remove `BoxExt` extension Danilo Krummrich
@ 2024-08-08  6:48   ` Benno Lossin
  0 siblings, 0 replies; 100+ messages in thread
From: Benno Lossin @ 2024-08-08  6:48 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, acurrid, cjia, jhubbard, airlied, ajanulgu, lyude,
	linux-kernel, rust-for-linux, linux-mm

On 05.08.24 17:19, Danilo Krummrich wrote:
> Now that all existing `Box` users were moved to the kernel `Box` type,
> remove the `BoxExt` extension.

In the changes below you also remove the `new_uninit` feature, the
`impl InPlaceInit for Box` and the `impl ForeignOwnable for Box` but
don't mention it here.

---
Cheers,
Benno

> Reviewed-by: Alice Ryhl <aliceryhl@google.com>
> Signed-off-by: Danilo Krummrich <dakr@kernel.org>
> ---
>  rust/kernel/alloc.rs         |  1 -
>  rust/kernel/alloc/box_ext.rs | 56 ------------------------------------
>  rust/kernel/init.rs          | 34 +---------------------
>  rust/kernel/lib.rs           |  1 -
>  rust/kernel/prelude.rs       |  4 +--
>  rust/kernel/types.rs         | 23 ---------------
>  6 files changed, 3 insertions(+), 116 deletions(-)
>  delete mode 100644 rust/kernel/alloc/box_ext.rs


^ permalink raw reply	[flat|nested] 100+ messages in thread

* Re: [PATCH v4 12/28] rust: alloc: add `Box` to prelude
  2024-08-05 15:19 ` [PATCH v4 12/28] rust: alloc: add `Box` to prelude Danilo Krummrich
@ 2024-08-08  6:49   ` Benno Lossin
  0 siblings, 0 replies; 100+ messages in thread
From: Benno Lossin @ 2024-08-08  6:49 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, acurrid, cjia, jhubbard, airlied, ajanulgu, lyude,
	linux-kernel, rust-for-linux, linux-mm

On 05.08.24 17:19, Danilo Krummrich 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>
> Signed-off-by: Danilo Krummrich <dakr@kernel.org>

Reviewed-by: Benno Lossin <benno.lossin@proton.me>

---
Cheers,
Benno

> ---
>  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 a8018ef2e691..6bf77577eae7 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.45.2
> 


^ permalink raw reply	[flat|nested] 100+ messages in thread

* Re: [PATCH v4 28/28] kbuild: rust: remove the `alloc` crate
  2024-08-05 15:19 ` [PATCH v4 28/28] kbuild: rust: remove the `alloc` crate Danilo Krummrich
@ 2024-08-08  6:58   ` Benno Lossin
  2024-08-08  9:45   ` Benno Lossin
  1 sibling, 0 replies; 100+ messages in thread
From: Benno Lossin @ 2024-08-08  6:58 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, acurrid, cjia, jhubbard, airlied, ajanulgu, lyude,
	linux-kernel, rust-for-linux, linux-mm

On 05.08.24 17:19, Danilo Krummrich wrote:
> Now that we have our own `Allocator`, `Box` and `Vec` types we can remove
> Rust's `alloc` crate and the corresponding unstable features.

Would be great if you could include the list of unstable features that
are now no longer needed here.

---
Cheers,
Benno

> Signed-off-by: Danilo Krummrich <dakr@kernel.org>
> ---
>  rust/Makefile          | 44 ++++++++++--------------------------------
>  rust/exports.c         |  1 -
>  scripts/Makefile.build |  7 +------
>  3 files changed, 11 insertions(+), 41 deletions(-)


^ permalink raw reply	[flat|nested] 100+ messages in thread

* Re: [PATCH v4 27/28] rust: alloc: update module comment of alloc.rs
  2024-08-05 15:19 ` [PATCH v4 27/28] rust: alloc: update module comment of alloc.rs Danilo Krummrich
  2024-08-07 13:50   ` Alice Ryhl
@ 2024-08-08  6:59   ` Benno Lossin
  1 sibling, 0 replies; 100+ messages in thread
From: Benno Lossin @ 2024-08-08  6:59 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, acurrid, cjia, jhubbard, airlied, ajanulgu, lyude,
	linux-kernel, rust-for-linux, linux-mm

On 05.08.24 17:19, Danilo Krummrich 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.
> 
> Signed-off-by: Danilo Krummrich <dakr@kernel.org>
> ---
>  rust/kernel/alloc.rs | 2 +-
>  1 file changed, 1 insertion(+), 1 deletion(-)

Reviewed-by: Benno Lossin <benno.lossin@proton.me>

---
Cheers,
Benno


^ permalink raw reply	[flat|nested] 100+ messages in thread

* Re: [PATCH v4 26/28] rust: str: test: replace `alloc::format`
  2024-08-05 15:19 ` [PATCH v4 26/28] rust: str: test: replace `alloc::format` Danilo Krummrich
  2024-08-07 13:51   ` Alice Ryhl
@ 2024-08-08  7:22   ` Benno Lossin
  2024-08-12 13:07     ` Danilo Krummrich
  1 sibling, 1 reply; 100+ messages in thread
From: Benno Lossin @ 2024-08-08  7:22 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, acurrid, cjia, jhubbard, airlied, ajanulgu, lyude,
	linux-kernel, rust-for-linux, linux-mm

On 05.08.24 17:19, Danilo Krummrich 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`.
> 
> 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 0b6ffbade521..8f234224cbf5 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()
> +        }
> +    }

Don't actually think we need this newtype.

> +
> +    macro_rules! format {
> +        ($($f:tt)*) => ({
> +            &*String::from_fmt(kernel::fmt!($($f)*))

We could just do this:

    CString::try_from_fmt(kernel::fmt!($(f)*)).unwrap().to_str().unwrap()

---
Cheers,
Benno

> +        })
> +    }
> 
>      const ALL_ASCII_CHARS: &'static str =
>          "\\x01\\x02\\x03\\x04\\x05\\x06\\x07\\x08\\x09\\x0a\\x0b\\x0c\\x0d\\x0e\\x0f\
> --
> 2.45.2
> 


^ permalink raw reply	[flat|nested] 100+ messages in thread

* Re: [PATCH v4 22/28] rust: error: use `core::alloc::LayoutError`
  2024-08-05 15:19 ` [PATCH v4 22/28] rust: error: use `core::alloc::LayoutError` Danilo Krummrich
  2024-08-07 13:55   ` Alice Ryhl
@ 2024-08-08  7:42   ` Benno Lossin
  1 sibling, 0 replies; 100+ messages in thread
From: Benno Lossin @ 2024-08-08  7:42 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, acurrid, cjia, jhubbard, airlied, ajanulgu, lyude,
	linux-kernel, rust-for-linux, linux-mm

On 05.08.24 17:19, Danilo Krummrich wrote:
> Use `core::alloc::LayoutError` instead of `alloc::alloc::LayoutError` in
> preparation to get rid of Rust's alloc crate.
> 
> Signed-off-by: Danilo Krummrich <dakr@kernel.org>
> ---
>  rust/kernel/error.rs | 2 +-
>  1 file changed, 1 insertion(+), 1 deletion(-)

Reviewed-by: Benno Lossin <benno.lossin@proton.me>

---
Cheers,
Benno

> diff --git a/rust/kernel/error.rs b/rust/kernel/error.rs
> index 145f5c397009..2d012cc3881a 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.45.2
> 


^ permalink raw reply	[flat|nested] 100+ messages in thread

* Re: [PATCH v4 20/28] rust: alloc: add `Vec` to prelude
  2024-08-05 15:19 ` [PATCH v4 20/28] rust: alloc: add `Vec` to prelude Danilo Krummrich
  2024-08-07 13:55   ` Alice Ryhl
@ 2024-08-08  8:40   ` Benno Lossin
  1 sibling, 0 replies; 100+ messages in thread
From: Benno Lossin @ 2024-08-08  8:40 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, acurrid, cjia, jhubbard, airlied, ajanulgu, lyude,
	linux-kernel, rust-for-linux, linux-mm

On 05.08.24 17:19, Danilo Krummrich wrote:
> Now that we removed `VecExt` and the corresponding includes in
> prelude.rs, add the new kernel `Vec` type instead.
> 
> Signed-off-by: Danilo Krummrich <dakr@kernel.org>
> ---
>  rust/kernel/prelude.rs | 2 +-
>  1 file changed, 1 insertion(+), 1 deletion(-)

Reviewed-by: Benno Lossin <benno.lossin@proton.me>

---
Cheers,
Benno

> diff --git a/rust/kernel/prelude.rs b/rust/kernel/prelude.rs
> index fcc8656fdb51..b049ab96202e 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.45.2
> 


^ permalink raw reply	[flat|nested] 100+ messages in thread

* Re: [PATCH v4 04/28] rust: alloc: implement `Allocator` for `Kmalloc`
  2024-08-07 23:05             ` Danilo Krummrich
@ 2024-08-08  8:55               ` Benno Lossin
  2024-08-08  9:02                 ` Benno Lossin
  2024-08-08  9:42                 ` Danilo Krummrich
  0 siblings, 2 replies; 100+ messages in thread
From: Benno Lossin @ 2024-08-08  8:55 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, acurrid, cjia, jhubbard,
	airlied, ajanulgu, lyude, linux-kernel, rust-for-linux, linux-mm

On 08.08.24 01:05, Danilo Krummrich wrote:
> On Wed, Aug 07, 2024 at 08:15:41PM +0000, Benno Lossin wrote:
>>> So, that's not permitted. `free` can't be called with a dangling pointer. The
>>> kernel free functions (*1) can't handle it, and I can't detect it, since a
>>> dangling pointer does not have a descrete value.
>>
>> That is true, but only if we do not have access to the old layout of the
>> allocation. If we add `old_layout` as a parameter, then we can handle
>> dangling pointers.
> 
> Then we'd need `free` to be `fn free(ptr: NonNull<u8>, layout: Layout)` just to
> compare whether `ptr.as_ptr() == layout.align() as _`. Same for `realloc`, but
> that's less weird.

I don't think that's a problem (though we would check against the size
and not compare the address!). `Allocator` from stdlib also has the
extra argument.

> It's also not that we safe code with that. `Box`, `Vec`, any other user, still
> would have to create the `Layout` before they call `A::free`.

But for `Box` it would just be `Layout::<T>::new()` and `Vec` needs
`Layout::<T>::new().repeat(self.cap)`.

Though for `repeat` we need the `alloc_layout_extra` feature, which is
an argument against doing this.

>>> Surely, we could also let the caller pass the old alignment, but this all sounds
>>> complicated for something that is very trivial for the caller to take care of,
>>> i.e. just don't try to free something that was never actually allocated.
>>>
>>> It can also lead to subtle bugs, e.g. what if someone calls `Box::from_raw` for
>>> a ZST with some other random pointer? Currently, that doesn't hurt us, which for
>>> robustness, seems to be a good thing.
>>
>> I think we should forbid that. To me it's just plain wrong to take a
>> random integer literal and cast it to a ZST. IIRC it even is UB if that
>> points to a previously allocated object that has been freed (but I don't
>> remember where I read it...).
> 
> I think my argument about robustness still holds even if we forbid it.
> 
> The documentation says "For operations of size zero, every pointer is valid,
> including the null pointer." [1]

How does this increase robustness? I am not allowed to call `free` with
a random pointer, only pointers returned by that allocator. The same
should also be true for `Box::from_raw`. That way the ZST dangling
pointer stuff leaks into that API.

> [1] https://doc.rust-lang.org/std/ptr/index.html
> 
>>
>> Also if we use the size of the old layout instead of comparing alignment
>> with the address of the pointer, then we avoid this issue.
> 
> That is just another problem when passing the old `Layout` (or maybe just the
> old size as usize). Neither do we need the old size, nor do we honor it with any
> kernel allocator. This has the following implications:
> 
> (1) We never see any error if the old size that is passed is garbage (unless
>     it's non-zero, when it should be zero and vice versa), which is bad.

We could add debug support for that though?

> (2) We'd need `free` to be `fn free(ptr: NonNull<u8>, size: usize)`, which is
>     confusing, because it implies that an actual free relies on this size for
>     freeing the memory.

I don't think that it is confusing to ask for the old layout.

> If we want to avoid (1) and (2), we'd need to make it
> `fn free(ptr: NonNull<u8>, zero: bool)` instead, but then also the caller can
> just check this boolean and conditionally call `free`.

Yeah having `free` with a `zero: bool` sounds like a bad idea.

> I don't really see why it's better to let `free` do the `if !zero` check.

You did not reply to my last argument:

>> I think it's better to just let `Box` and `Vec` figure out if calling `free` is
>> the right thing to do. The code for that is simple and obvious, i.e. check if
>> `T` is a ZST.
>
> I don't think that it is as simple as you make it out to be. To me this
> is functionality that we can move into one place and never have to think
> about again.
> Also if we at some point decide to add some sort of debugging allocator
> (am I right in assuming that such a thing already exists for C?) then we
> might want to tag on extra data in the allocation, in that case it would
> be very useful if the allocator also saw zero-sized allocations, since
> we should check all allocations.

---
Cheers,
Benno


^ permalink raw reply	[flat|nested] 100+ messages in thread

* Re: [PATCH v4 04/28] rust: alloc: implement `Allocator` for `Kmalloc`
  2024-08-08  8:55               ` Benno Lossin
@ 2024-08-08  9:02                 ` Benno Lossin
  2024-08-08  9:42                 ` Danilo Krummrich
  1 sibling, 0 replies; 100+ messages in thread
From: Benno Lossin @ 2024-08-08  9:02 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, acurrid, cjia, jhubbard,
	airlied, ajanulgu, lyude, linux-kernel, rust-for-linux, linux-mm

On 08.08.24 10:55, Benno Lossin wrote:
> On 08.08.24 01:05, Danilo Krummrich wrote:
>> It's also not that we safe code with that. `Box`, `Vec`, any other user, still
>> would have to create the `Layout` before they call `A::free`.
> 
> But for `Box` it would just be `Layout::<T>::new()` and `Vec` needs
> `Layout::<T>::new().repeat(self.cap)`.
> 
> Though for `repeat` we need the `alloc_layout_extra` feature, which is
> an argument against doing this.

Oops, I overlooked the `Layout::array` function, so this is not a
problem. Instead it just is `Layout::<T>::array(self.cap).unwrap()`.

---
Cheers,
Benno


^ permalink raw reply	[flat|nested] 100+ messages in thread

* Re: [PATCH v4 18/28] rust: treewide: switch to the kernel `Vec` type
  2024-08-05 15:19 ` [PATCH v4 18/28] rust: treewide: switch to the kernel `Vec` type Danilo Krummrich
  2024-08-07 13:47   ` Alice Ryhl
@ 2024-08-08  9:08   ` Benno Lossin
  1 sibling, 0 replies; 100+ messages in thread
From: Benno Lossin @ 2024-08-08  9:08 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, acurrid, cjia, jhubbard, airlied, ajanulgu, lyude,
	linux-kernel, rust-for-linux, linux-mm

On 05.08.24 17:19, Danilo Krummrich wrote:
> Now that we got the kernel `Vec` in place, convert all existing `Vec`
> users to make use of it.

You missed one in `rust/kernel/uaccess.rs:92`, but this doesn't
prevent it from compiling since the allocator is correctly inferred from
the context.

---
Cheers,
Benno

> 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        | 15 ++++++---------
>  samples/rust/rust_minimal.rs  |  4 ++--
>  5 files changed, 15 insertions(+), 20 deletions(-)


^ permalink raw reply	[flat|nested] 100+ messages in thread

* Re: [PATCH v4 19/28] rust: alloc: remove `VecExt` extension
  2024-08-05 15:19 ` [PATCH v4 19/28] rust: alloc: remove `VecExt` extension Danilo Krummrich
  2024-08-07 12:42   ` Alice Ryhl
@ 2024-08-08  9:14   ` Benno Lossin
  1 sibling, 0 replies; 100+ messages in thread
From: Benno Lossin @ 2024-08-08  9:14 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, acurrid, cjia, jhubbard, airlied, ajanulgu, lyude,
	linux-kernel, rust-for-linux, linux-mm

On 05.08.24 17:19, Danilo Krummrich wrote:
> Now that all existing `Vec` users were moved to the kernel `Vec` type,
> remove the `VecExt` extension.
> 
> 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

Reviewed-by: Benno Lossin <benno.lossin@proton.me>

---
Cheers,
Benno


^ permalink raw reply	[flat|nested] 100+ messages in thread

* Re: [PATCH v4 25/28] rust: alloc: implement `Cmalloc` in module allocator_test
  2024-08-05 15:19 ` [PATCH v4 25/28] rust: alloc: implement `Cmalloc` in module allocator_test Danilo Krummrich
@ 2024-08-08  9:35   ` Benno Lossin
  2024-08-08 10:07     ` Danilo Krummrich
  0 siblings, 1 reply; 100+ messages in thread
From: Benno Lossin @ 2024-08-08  9:35 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, acurrid, cjia, jhubbard, airlied, ajanulgu, lyude,
	linux-kernel, rust-for-linux, linux-mm

On 05.08.24 17:19, Danilo Krummrich 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.
> 
> Signed-off-by: Danilo Krummrich <dakr@kernel.org>
> ---
> I know, having an `old_size` parameter would indeed help implementing `Cmalloc`.
> 
> However, I really don't want test infrastructure to influence the design of
> kernel internal APIs.
> 
> Besides that, adding the `old_size` parameter would have the consequence that
> we'd either need to honor it for kernel allocators too (which adds another
> source of potential failure) or ignore it for all kernel allocators (which
> potentially tricks people into taking wrong assumptions on how the API works).
> ---
>  rust/kernel/alloc/allocator_test.rs | 91 ++++++++++++++++++++++++++---
>  1 file changed, 84 insertions(+), 7 deletions(-)
> 
> diff --git a/rust/kernel/alloc/allocator_test.rs b/rust/kernel/alloc/allocator_test.rs
> index 1b2642c547ec..05fd75b89238 100644
> --- a/rust/kernel/alloc/allocator_test.rs
> +++ b/rust/kernel/alloc/allocator_test.rs
> @@ -2,20 +2,97 @@
> 
>  #![allow(missing_docs)]
> 
> -use super::{AllocError, Allocator, Flags};
> +use super::{flags::*, AllocError, Allocator, Flags};
>  use core::alloc::Layout;
> +use core::ptr;
>  use core::ptr::NonNull;
> 
> -pub struct Kmalloc;
> +pub struct Cmalloc;
> +pub type Kmalloc = Cmalloc;
>  pub type Vmalloc = Kmalloc;
>  pub type KVmalloc = Kmalloc;
> 
> -unsafe impl Allocator for Kmalloc {
> +extern "C" {
> +    #[link_name = "aligned_alloc"]
> +    fn libc_aligned_alloc(align: usize, size: usize) -> *mut core::ffi::c_void;
> +
> +    #[link_name = "free"]
> +    fn libc_free(ptr: *mut core::ffi::c_void);
> +
> +    // Do not use this function for production code! For test cases only it's
> +    // probably fine if used with care.
> +    #[link_name = "malloc_usable_size"]
> +    fn libc_malloc_usable_size(ptr: *mut core::ffi::c_void) -> usize;
> +}
> +
> +unsafe impl Allocator for Cmalloc {
> +    fn alloc(layout: Layout, flags: Flags) -> Result<NonNull<[u8]>, AllocError> {
> +        let layout = layout.pad_to_align();
> +
> +        // SAFETY: Returns either NULL or a pointer to a memory allocation that satisfies or
> +        // exceeds the given size and alignment requirements.
> +        let raw_ptr = unsafe { libc_aligned_alloc(layout.align(), layout.size()) } as *mut u8;
> +
> +        if flags.contains(__GFP_ZERO) && !raw_ptr.is_null() {
> +            // SAFETY: `raw_ptr` points to memory successfully allocated with `libc_aligned_alloc`.
> +            let size = unsafe { libc_malloc_usable_size(raw_ptr.cast()) };
> +
> +            // SAFETY: `raw_ptr` points to memory successfully allocated with `libc_aligned_alloc`
> +            // of at least `size` bytes.
> +            unsafe { core::ptr::write_bytes(raw_ptr, 0, size) };
> +        }
> +
> +        let ptr = if layout.size() == 0 {
> +            NonNull::dangling()

Why do you call `libc_aligned_alloc` when you return `dangling()`
anyways when size is zero? I would move this check upwards.

> +        } else {
> +            NonNull::new(raw_ptr).ok_or(AllocError)?

Would also make sense to do this above the null check.

> +        };
> +
> +        Ok(NonNull::slice_from_raw_parts(ptr, layout.size()))
> +    }
> +
>      unsafe fn realloc(
> -        _ptr: Option<NonNull<u8>>,
> -        _layout: Layout,
> -        _flags: Flags,
> +        ptr: Option<NonNull<u8>>,
> +        layout: Layout,
> +        flags: Flags,
>      ) -> Result<NonNull<[u8]>, AllocError> {
> -        panic!();
> +        let layout = layout.pad_to_align();
> +        let src: *mut u8 = if let Some(src) = ptr {
> +            src.as_ptr().cast()
> +        } else {
> +            ptr::null_mut()
> +        };
> +
> +        if layout.size() == 0 {
> +            // SAFETY: `src` is either NULL or has previously been allocatored with this
> +            // `Allocator`.
> +            unsafe { libc_free(src.cast()) };
> +
> +            return Ok(NonNull::slice_from_raw_parts(NonNull::dangling(), 0));
> +        }
> +
> +        let dst = Self::alloc(layout, flags)?;
> +
> +        if src.is_null() {
> +            return Ok(dst);
> +        }
> +
> +        // SAFETY: `src` is either NULL or has previously been allocatored with this `Allocator`.
> +        let old_size = unsafe { libc_malloc_usable_size(src.cast()) };

Citing man malloc_usable_size(3):

    CAVEATS
        The value returned by malloc_usable_size() may be greater than
        the requested size of the allocation because of various internal
        implementation details, none of which the programmer should rely
        on.  This function is intended to only be used for diagnostics
        and statistics; writing to the excess memory without first
        calling realloc(3) to resize the allocation is not supported.
        The returned value is only valid at the time of the call.

While you don't write, you might read below, which might not be OK?

---
Cheers,
Benno

> +
> +        // SAFETY: `src` has previously been allocated with this `Allocator`; `dst` has just been
> +        // newly allocated. Taking the minimum of their sizes guarantees that we do not exceed
> +        // either bounds.
> +        unsafe {
> +            // Always copy -- do not rely on potential spare memory reported by
> +            // malloc_usable_size() which technically may still be sufficient.
> +            ptr::copy_nonoverlapping(
> +                src,
> +                dst.as_ptr().cast(),
> +                core::cmp::min(layout.size(), old_size),
> +            )
> +        };
> +
> +        Ok(dst)
>      }
>  }
> --
> 2.45.2
> 


^ permalink raw reply	[flat|nested] 100+ messages in thread

* Re: [PATCH v4 04/28] rust: alloc: implement `Allocator` for `Kmalloc`
  2024-08-08  8:55               ` Benno Lossin
  2024-08-08  9:02                 ` Benno Lossin
@ 2024-08-08  9:42                 ` Danilo Krummrich
  1 sibling, 0 replies; 100+ messages in thread
From: Danilo Krummrich @ 2024-08-08  9:42 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, acurrid, cjia, jhubbard,
	airlied, ajanulgu, lyude, linux-kernel, rust-for-linux, linux-mm

On Thu, Aug 08, 2024 at 08:55:29AM +0000, Benno Lossin wrote:
> On 08.08.24 01:05, Danilo Krummrich wrote:
> > On Wed, Aug 07, 2024 at 08:15:41PM +0000, Benno Lossin wrote:
> >>> So, that's not permitted. `free` can't be called with a dangling pointer. The
> >>> kernel free functions (*1) can't handle it, and I can't detect it, since a
> >>> dangling pointer does not have a descrete value.
> >>
> >> That is true, but only if we do not have access to the old layout of the
> >> allocation. If we add `old_layout` as a parameter, then we can handle
> >> dangling pointers.
> > 
> > Then we'd need `free` to be `fn free(ptr: NonNull<u8>, layout: Layout)` just to
> > compare whether `ptr.as_ptr() == layout.align() as _`. Same for `realloc`, but
> > that's less weird.
> 
> I don't think that's a problem (though we would check against the size
> and not compare the address!). `Allocator` from stdlib also has the
> extra argument.

Because `Allocator` from stdlib actually needs it, e.g. they have to open code
`realloc`, since userspace allocators don't give proper alignment guarantees.

Again, the kernel allocators neither need, nor take and honor this extra
information.

> 
> > It's also not that we safe code with that. `Box`, `Vec`, any other user, still
> > would have to create the `Layout` before they call `A::free`.
> 
> But for `Box` it would just be `Layout::<T>::new()` and `Vec` needs
> `Layout::<T>::new().repeat(self.cap)`.

Let's take `Vec` for instance, there it's either

current code
------------
```
	if cap != 0 {
		unsafe { A::free(self.ptr.as_non_null().cast()) };
	}
```

your proposal
-------------
```
	let layout = Layout::<T>::array(self.cap).unwrap();
	unsafe { A::free(self.ptr.as_non_null().cast(), layout) };
```

I really don't see how that's an improvement.

> 
> Though for `repeat` we need the `alloc_layout_extra` feature, which is
> an argument against doing this.
> 
> >>> Surely, we could also let the caller pass the old alignment, but this all sounds
> >>> complicated for something that is very trivial for the caller to take care of,
> >>> i.e. just don't try to free something that was never actually allocated.
> >>>
> >>> It can also lead to subtle bugs, e.g. what if someone calls `Box::from_raw` for
> >>> a ZST with some other random pointer? Currently, that doesn't hurt us, which for
> >>> robustness, seems to be a good thing.
> >>
> >> I think we should forbid that. To me it's just plain wrong to take a
> >> random integer literal and cast it to a ZST. IIRC it even is UB if that
> >> points to a previously allocated object that has been freed (but I don't
> >> remember where I read it...).
> > 
> > I think my argument about robustness still holds even if we forbid it.
> > 
> > The documentation says "For operations of size zero, every pointer is valid,
> > including the null pointer." [1]
> 
> How does this increase robustness? I am not allowed to call `free` with
> a random pointer, only pointers returned by that allocator. The same
> should also be true for `Box::from_raw`. That way the ZST dangling
> pointer stuff leaks into that API.

This point was about your first proposal to use the alignment. With cosidering
only the alignment we can't handle the case where `Box::from_raw` is called with
a random pointer for a ZST, which *technically* isn't illegal. We can define it
as illegal, but I'd consider it to be more robust, if we don't oops in case.

But as you say checking the size instead of the alignment does not have this
problem.

> 
> > [1] https://doc.rust-lang.org/std/ptr/index.html
> > 
> >>
> >> Also if we use the size of the old layout instead of comparing alignment
> >> with the address of the pointer, then we avoid this issue.
> > 
> > That is just another problem when passing the old `Layout` (or maybe just the
> > old size as usize). Neither do we need the old size, nor do we honor it with any
> > kernel allocator. This has the following implications:
> > 
> > (1) We never see any error if the old size that is passed is garbage (unless
> >     it's non-zero, when it should be zero and vice versa), which is bad.
> 
> We could add debug support for that though?

And add even more complexity just to avoid a simple `if !zero` check before
calling `free`? I don't like that.

Just to clarify, I'm not against passing the old layout in general. But I really
don't want to pass something in that is not required *and* not honored by a
single kernel allocator.

IMO, the only other valid reason to accept unneeded arguments would be if we
could use the `Allocator` interface from stdlib.

> 
> > (2) We'd need `free` to be `fn free(ptr: NonNull<u8>, size: usize)`, which is
> >     confusing, because it implies that an actual free relies on this size for
> >     freeing the memory.
> 
> I don't think that it is confusing to ask for the old layout.

It is always confusing if a function asks for information that it doesn't need
and doesn't consider, because asking for it in the first place creates the
impression that it is indeed needed and considered.

> 
> > If we want to avoid (1) and (2), we'd need to make it
> > `fn free(ptr: NonNull<u8>, zero: bool)` instead, but then also the caller can
> > just check this boolean and conditionally call `free`.
> 
> Yeah having `free` with a `zero: bool` sounds like a bad idea.
> 
> > I don't really see why it's better to let `free` do the `if !zero` check.
> 
> You did not reply to my last argument:
> 
> >> I think it's better to just let `Box` and `Vec` figure out if calling `free` is
> >> the right thing to do. The code for that is simple and obvious, i.e. check if
> >> `T` is a ZST.
> >
> > I don't think that it is as simple as you make it out to be. To me this
> > is functionality that we can move into one place and never have to think
> > about again.
> > Also if we at some point decide to add some sort of debugging allocator
> > (am I right in assuming that such a thing already exists for C?) then we

The C side allocators have quite a lot of debugging capabilities, but there is
no separate one.

> > might want to tag on extra data in the allocation, in that case it would
> > be very useful if the allocator also saw zero-sized allocations, since
> > we should check all allocations.

What would such a debugging allocator do on the Rust side?

The allocators we have are just simple wrappers around the real kernel
allocators from the C side.

I don't really see the need for some sort of debugging allocator.

^ permalink raw reply	[flat|nested] 100+ messages in thread

* Re: [PATCH v4 28/28] kbuild: rust: remove the `alloc` crate
  2024-08-05 15:19 ` [PATCH v4 28/28] kbuild: rust: remove the `alloc` crate Danilo Krummrich
  2024-08-08  6:58   ` Benno Lossin
@ 2024-08-08  9:45   ` Benno Lossin
  1 sibling, 0 replies; 100+ messages in thread
From: Benno Lossin @ 2024-08-08  9:45 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, acurrid, cjia, jhubbard, airlied, ajanulgu, lyude,
	linux-kernel, rust-for-linux, linux-mm

On 05.08.24 17:19, Danilo Krummrich wrote:
> Now that we have our own `Allocator`, `Box` and `Vec` types we can remove
> Rust's `alloc` crate and the corresponding unstable features.

Should this patch also remove `alloc` from
`scripts/generate_rust_analyzer`?
Or do we need that when editing our `macros` crate (since it depends on
std)?

---
Cheers,
Benno

> 
> Signed-off-by: Danilo Krummrich <dakr@kernel.org>
> ---
>  rust/Makefile          | 44 ++++++++++--------------------------------
>  rust/exports.c         |  1 -
>  scripts/Makefile.build |  7 +------
>  3 files changed, 11 insertions(+), 41 deletions(-)


^ permalink raw reply	[flat|nested] 100+ messages in thread

* Re: [PATCH v4 25/28] rust: alloc: implement `Cmalloc` in module allocator_test
  2024-08-08  9:35   ` Benno Lossin
@ 2024-08-08 10:07     ` Danilo Krummrich
  0 siblings, 0 replies; 100+ messages in thread
From: Danilo Krummrich @ 2024-08-08 10:07 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, acurrid, cjia, jhubbard,
	airlied, ajanulgu, lyude, linux-kernel, rust-for-linux, linux-mm

On Thu, Aug 08, 2024 at 09:35:47AM +0000, Benno Lossin wrote:
> On 05.08.24 17:19, Danilo Krummrich 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.
> > 
> > Signed-off-by: Danilo Krummrich <dakr@kernel.org>
> > ---
> > I know, having an `old_size` parameter would indeed help implementing `Cmalloc`.
> > 
> > However, I really don't want test infrastructure to influence the design of
> > kernel internal APIs.
> > 
> > Besides that, adding the `old_size` parameter would have the consequence that
> > we'd either need to honor it for kernel allocators too (which adds another
> > source of potential failure) or ignore it for all kernel allocators (which
> > potentially tricks people into taking wrong assumptions on how the API works).
> > ---
> >  rust/kernel/alloc/allocator_test.rs | 91 ++++++++++++++++++++++++++---
> >  1 file changed, 84 insertions(+), 7 deletions(-)
> > 
> > diff --git a/rust/kernel/alloc/allocator_test.rs b/rust/kernel/alloc/allocator_test.rs
> > index 1b2642c547ec..05fd75b89238 100644
> > --- a/rust/kernel/alloc/allocator_test.rs
> > +++ b/rust/kernel/alloc/allocator_test.rs
> > @@ -2,20 +2,97 @@
> > 
> >  #![allow(missing_docs)]
> > 
> > -use super::{AllocError, Allocator, Flags};
> > +use super::{flags::*, AllocError, Allocator, Flags};
> >  use core::alloc::Layout;
> > +use core::ptr;
> >  use core::ptr::NonNull;
> > 
> > -pub struct Kmalloc;
> > +pub struct Cmalloc;
> > +pub type Kmalloc = Cmalloc;
> >  pub type Vmalloc = Kmalloc;
> >  pub type KVmalloc = Kmalloc;
> > 
> > -unsafe impl Allocator for Kmalloc {
> > +extern "C" {
> > +    #[link_name = "aligned_alloc"]
> > +    fn libc_aligned_alloc(align: usize, size: usize) -> *mut core::ffi::c_void;
> > +
> > +    #[link_name = "free"]
> > +    fn libc_free(ptr: *mut core::ffi::c_void);
> > +
> > +    // Do not use this function for production code! For test cases only it's
> > +    // probably fine if used with care.
> > +    #[link_name = "malloc_usable_size"]
> > +    fn libc_malloc_usable_size(ptr: *mut core::ffi::c_void) -> usize;
> > +}
> > +
> > +unsafe impl Allocator for Cmalloc {
> > +    fn alloc(layout: Layout, flags: Flags) -> Result<NonNull<[u8]>, AllocError> {
> > +        let layout = layout.pad_to_align();
> > +
> > +        // SAFETY: Returns either NULL or a pointer to a memory allocation that satisfies or
> > +        // exceeds the given size and alignment requirements.
> > +        let raw_ptr = unsafe { libc_aligned_alloc(layout.align(), layout.size()) } as *mut u8;
> > +
> > +        if flags.contains(__GFP_ZERO) && !raw_ptr.is_null() {
> > +            // SAFETY: `raw_ptr` points to memory successfully allocated with `libc_aligned_alloc`.
> > +            let size = unsafe { libc_malloc_usable_size(raw_ptr.cast()) };
> > +
> > +            // SAFETY: `raw_ptr` points to memory successfully allocated with `libc_aligned_alloc`
> > +            // of at least `size` bytes.
> > +            unsafe { core::ptr::write_bytes(raw_ptr, 0, size) };
> > +        }
> > +
> > +        let ptr = if layout.size() == 0 {
> > +            NonNull::dangling()
> 
> Why do you call `libc_aligned_alloc` when you return `dangling()`
> anyways when size is zero? I would move this check upwards.
> 
> > +        } else {
> > +            NonNull::new(raw_ptr).ok_or(AllocError)?
> 
> Would also make sense to do this above the null check.
> 
> > +        };
> > +
> > +        Ok(NonNull::slice_from_raw_parts(ptr, layout.size()))
> > +    }
> > +
> >      unsafe fn realloc(
> > -        _ptr: Option<NonNull<u8>>,
> > -        _layout: Layout,
> > -        _flags: Flags,
> > +        ptr: Option<NonNull<u8>>,
> > +        layout: Layout,
> > +        flags: Flags,
> >      ) -> Result<NonNull<[u8]>, AllocError> {
> > -        panic!();
> > +        let layout = layout.pad_to_align();
> > +        let src: *mut u8 = if let Some(src) = ptr {
> > +            src.as_ptr().cast()
> > +        } else {
> > +            ptr::null_mut()
> > +        };
> > +
> > +        if layout.size() == 0 {
> > +            // SAFETY: `src` is either NULL or has previously been allocatored with this
> > +            // `Allocator`.
> > +            unsafe { libc_free(src.cast()) };
> > +
> > +            return Ok(NonNull::slice_from_raw_parts(NonNull::dangling(), 0));
> > +        }
> > +
> > +        let dst = Self::alloc(layout, flags)?;
> > +
> > +        if src.is_null() {
> > +            return Ok(dst);
> > +        }
> > +
> > +        // SAFETY: `src` is either NULL or has previously been allocatored with this `Allocator`.
> > +        let old_size = unsafe { libc_malloc_usable_size(src.cast()) };
> 
> Citing man malloc_usable_size(3):
> 
>     CAVEATS
>         The value returned by malloc_usable_size() may be greater than
>         the requested size of the allocation because of various internal
>         implementation details, none of which the programmer should rely
>         on.  This function is intended to only be used for diagnostics
>         and statistics; writing to the excess memory without first
>         calling realloc(3) to resize the allocation is not supported.
>         The returned value is only valid at the time of the call.
> 
> While you don't write, you might read below, which might not be OK?

That is very interesting, the man page entry I looked at said:

       The  value  returned by malloc_usable_size() may be greater than the requested size of the
       allocation because of alignment and minimum size constraints.  Although the  excess  bytes
       can  be  overwritten  by the application without ill effects, this is not good programming
       practice: the  number  of  excess  bytes  in  an  allocation  depends  on  the  underlying
       implementation.

This was changed in [1] and it looks like "can be overwritten by the application
without ill effects" just isn't true any more, too bad.

I have to think about it a bit, what we could do instead. I also don't really
want to have an old size parameter for `realloc` only because it's needed by
test infrastructure.

[1] https://git.kernel.org/pub/scm/docs/man-pages/man-pages.git/commit/?id=015464751006a964ff401f1eb5945ca28c4448a7

> 
> ---
> Cheers,
> Benno
> 
> > +
> > +        // SAFETY: `src` has previously been allocated with this `Allocator`; `dst` has just been
> > +        // newly allocated. Taking the minimum of their sizes guarantees that we do not exceed
> > +        // either bounds.
> > +        unsafe {
> > +            // Always copy -- do not rely on potential spare memory reported by
> > +            // malloc_usable_size() which technically may still be sufficient.
> > +            ptr::copy_nonoverlapping(
> > +                src,
> > +                dst.as_ptr().cast(),
> > +                core::cmp::min(layout.size(), old_size),
> > +            )
> > +        };
> > +
> > +        Ok(dst)
> >      }
> >  }
> > --
> > 2.45.2
> > 
> 

^ permalink raw reply	[flat|nested] 100+ messages in thread

* Re: [PATCH v4 09/28] rust: alloc: implement kernel `Box`
  2024-08-07  7:49       ` Benno Lossin
  2024-08-07  7:51         ` Alice Ryhl
  2024-08-07 10:38         ` Danilo Krummrich
@ 2024-08-08 17:44         ` Danilo Krummrich
  2024-08-08 19:44           ` Benno Lossin
  2 siblings, 1 reply; 100+ messages in thread
From: Danilo Krummrich @ 2024-08-08 17:44 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, acurrid, cjia, jhubbard,
	airlied, ajanulgu, lyude, linux-kernel, rust-for-linux, linux-mm

On Wed, Aug 07, 2024 at 07:49:31AM +0000, Benno Lossin wrote:
> >>> +impl<T, A> Drop for Box<T, A>
> >>> +where
> >>> +    T: ?Sized,
> >>> +    A: Allocator,
> >>> +{
> >>> +    fn drop(&mut self) {
> >>> +        let ptr = self.0.as_ptr();
> >>> +
> >>> +        // SAFETY: `ptr` is always properly aligned, dereferenceable and points to an initialized
> >>> +        // instance of `T`.
> >>> +        let size = unsafe { core::mem::size_of_val(&*ptr) };
> >>
> >> 1. `size_of_val` is not `unsafe`.
> > 
> > Right, but dereferencing the `ptr` is unsafe.
> > 
> >> 2. why not use `&*self` instead of using the raw pointer? (then move the
> >>    let binding below this line)
> > 
> > If we ever support non-ZST `Allocator`s using `self` would not always evaluate
> > to the correct size. I think evaluating the size of `T` rather than `Box<T>` is
> > the correct thing to do.
> 
> I mean use `Box::deref` (that's what `&*self` should do), you don't need

Actually, this must either be `size_of_val(&**self)` or `size_of_val::<T>(self).

`size_of_val(&*self)` should indeed resolve to `&Box<T, A>`, right?

> to repeat the same SAFETY comment when it already is wrapped by a safe
> function.
> 
> ---
> Cheers,
> Benno
> 

^ permalink raw reply	[flat|nested] 100+ messages in thread

* Re: [PATCH v4 09/28] rust: alloc: implement kernel `Box`
  2024-08-08 17:44         ` Danilo Krummrich
@ 2024-08-08 19:44           ` Benno Lossin
  0 siblings, 0 replies; 100+ messages in thread
From: Benno Lossin @ 2024-08-08 19:44 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, acurrid, cjia, jhubbard,
	airlied, ajanulgu, lyude, linux-kernel, rust-for-linux, linux-mm

On 08.08.24 19:44, Danilo Krummrich wrote:
> On Wed, Aug 07, 2024 at 07:49:31AM +0000, Benno Lossin wrote:
>>>>> +impl<T, A> Drop for Box<T, A>
>>>>> +where
>>>>> +    T: ?Sized,
>>>>> +    A: Allocator,
>>>>> +{
>>>>> +    fn drop(&mut self) {
>>>>> +        let ptr = self.0.as_ptr();
>>>>> +
>>>>> +        // SAFETY: `ptr` is always properly aligned, dereferenceable and points to an initialized
>>>>> +        // instance of `T`.
>>>>> +        let size = unsafe { core::mem::size_of_val(&*ptr) };
>>>>
>>>> 1. `size_of_val` is not `unsafe`.
>>>
>>> Right, but dereferencing the `ptr` is unsafe.
>>>
>>>> 2. why not use `&*self` instead of using the raw pointer? (then move the
>>>>    let binding below this line)
>>>
>>> If we ever support non-ZST `Allocator`s using `self` would not always evaluate
>>> to the correct size. I think evaluating the size of `T` rather than `Box<T>` is
>>> the correct thing to do.
>>
>> I mean use `Box::deref` (that's what `&*self` should do), you don't need
> 
> Actually, this must either be `size_of_val(&**self)` or `size_of_val::<T>(self).
> 
> `size_of_val(&*self)` should indeed resolve to `&Box<T, A>`, right?

Oh yeah that is true, good catch! Here is a playground example [1].

[1]: https://play.rust-lang.org/?version=stable&mode=debug&edition=2021&gist=c439494a0582bb287232c7a05e21bd23

---
Cheers,
Benno

> 
>> to repeat the same SAFETY comment when it already is wrapped by a safe
>> function.
>>
>> ---
>> Cheers,
>> Benno
>>


^ permalink raw reply	[flat|nested] 100+ messages in thread

* Re: [PATCH v4 26/28] rust: str: test: replace `alloc::format`
  2024-08-08  7:22   ` Benno Lossin
@ 2024-08-12 13:07     ` Danilo Krummrich
  0 siblings, 0 replies; 100+ messages in thread
From: Danilo Krummrich @ 2024-08-12 13:07 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, acurrid, cjia, jhubbard,
	airlied, ajanulgu, lyude, linux-kernel, rust-for-linux, linux-mm

On 8/8/24 9:22 AM, Benno Lossin wrote:
> On 05.08.24 17:19, Danilo Krummrich 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`.
>>
>> 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 0b6ffbade521..8f234224cbf5 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()
>> +        }
>> +    }
> 
> Don't actually think we need this newtype.

Right, but I thought it's a bit more extensible and cleaner than...

> 
>> +
>> +    macro_rules! format {
>> +        ($($f:tt)*) => ({
>> +            &*String::from_fmt(kernel::fmt!($($f)*))
> 
> We could just do this:
> 
>      CString::try_from_fmt(kernel::fmt!($(f)*)).unwrap().to_str().unwrap()

...doing it inline.

I'm fine either way, but unless you really want me to change it, I'd
rather keep it as it is.

> 
> ---
> Cheers,
> Benno
> 
>> +        })
>> +    }
>>
>>       const ALL_ASCII_CHARS: &'static str =
>>           "\\x01\\x02\\x03\\x04\\x05\\x06\\x07\\x08\\x09\\x0a\\x0b\\x0c\\x0d\\x0e\\x0f\
>> --
>> 2.45.2
>>
> 

^ permalink raw reply	[flat|nested] 100+ messages in thread

end of thread, other threads:[~2024-08-12 13:08 UTC | newest]

Thread overview: 100+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2024-08-05 15:19 [PATCH v4 00/28] Generic `Allocator` support for Rust Danilo Krummrich
2024-08-05 15:19 ` [PATCH v4 01/28] rust: alloc: add `Allocator` trait Danilo Krummrich
2024-08-06 15:00   ` Alice Ryhl
2024-08-06 16:03   ` Benno Lossin
2024-08-06 18:30     ` Danilo Krummrich
2024-08-06 20:04       ` Benno Lossin
2024-08-07  9:36         ` Danilo Krummrich
2024-08-07 20:00           ` Benno Lossin
2024-08-07 18:19   ` Gary Guo
2024-08-05 15:19 ` [PATCH v4 02/28] rust: alloc: separate `aligned_size` from `krealloc_aligned` Danilo Krummrich
2024-08-06 16:06   ` Benno Lossin
2024-08-05 15:19 ` [PATCH v4 03/28] rust: alloc: rename `KernelAllocator` to `Kmalloc` Danilo Krummrich
2024-08-06 16:07   ` Benno Lossin
2024-08-07 18:22   ` Gary Guo
2024-08-05 15:19 ` [PATCH v4 04/28] rust: alloc: implement `Allocator` for `Kmalloc` Danilo Krummrich
2024-08-06 16:51   ` Benno Lossin
2024-08-06 18:55     ` Danilo Krummrich
2024-08-07  7:14       ` Benno Lossin
2024-08-07 10:11         ` Danilo Krummrich
2024-08-07 20:15           ` Benno Lossin
2024-08-07 23:05             ` Danilo Krummrich
2024-08-08  8:55               ` Benno Lossin
2024-08-08  9:02                 ` Benno Lossin
2024-08-08  9:42                 ` Danilo Krummrich
2024-08-05 15:19 ` [PATCH v4 05/28] rust: alloc: add module `allocator_test` Danilo Krummrich
2024-08-06 16:54   ` Benno Lossin
2024-08-06 18:58     ` Danilo Krummrich
2024-08-07  7:20       ` Benno Lossin
2024-08-07 10:16         ` Danilo Krummrich
2024-08-05 15:19 ` [PATCH v4 06/28] rust: alloc: implement `Vmalloc` allocator Danilo Krummrich
2024-08-06 17:00   ` Benno Lossin
2024-08-06 19:01     ` Danilo Krummrich
2024-08-07  7:23       ` Benno Lossin
2024-08-05 15:19 ` [PATCH v4 07/28] rust: alloc: implement `KVmalloc` allocator Danilo Krummrich
2024-08-05 15:19 ` [PATCH v4 08/28] rust: types: implement `Unique<T>` Danilo Krummrich
2024-08-06 17:22   ` Benno Lossin
2024-08-06 17:28     ` Miguel Ojeda
2024-08-06 23:16       ` Danilo Krummrich
2024-08-06 23:12     ` Danilo Krummrich
2024-08-07  7:27       ` Benno Lossin
2024-08-07 10:13         ` Danilo Krummrich
2024-08-05 15:19 ` [PATCH v4 09/28] rust: alloc: implement kernel `Box` Danilo Krummrich
2024-08-06 19:47   ` Benno Lossin
2024-08-06 23:01     ` Danilo Krummrich
2024-08-07  7:49       ` Benno Lossin
2024-08-07  7:51         ` Alice Ryhl
2024-08-07  8:01           ` Benno Lossin
2024-08-07 10:44             ` Danilo Krummrich
2024-08-07 10:38         ` Danilo Krummrich
2024-08-07 19:45           ` Benno Lossin
2024-08-08 17:44         ` Danilo Krummrich
2024-08-08 19:44           ` Benno Lossin
2024-08-05 15:19 ` [PATCH v4 10/28] rust: treewide: switch to our kernel `Box` type Danilo Krummrich
2024-08-07 12:42   ` Alice Ryhl
2024-08-07 20:57   ` Benno Lossin
2024-08-07 21:16     ` Benno Lossin
2024-08-07 23:08     ` Danilo Krummrich
2024-08-05 15:19 ` [PATCH v4 11/28] rust: alloc: remove `BoxExt` extension Danilo Krummrich
2024-08-08  6:48   ` Benno Lossin
2024-08-05 15:19 ` [PATCH v4 12/28] rust: alloc: add `Box` to prelude Danilo Krummrich
2024-08-08  6:49   ` Benno Lossin
2024-08-05 15:19 ` [PATCH v4 13/28] rust: alloc: import kernel `Box` type in types.rs Danilo Krummrich
2024-08-05 15:19 ` [PATCH v4 14/28] rust: alloc: import kernel `Box` type in init.rs Danilo Krummrich
2024-08-05 15:19 ` [PATCH v4 15/28] rust: alloc: implement kernel `Vec` type Danilo Krummrich
2024-08-05 15:19 ` [PATCH v4 16/28] rust: alloc: implement `IntoIterator` for `Vec` Danilo Krummrich
2024-08-05 15:19 ` [PATCH v4 17/28] rust: alloc: implement `collect` for `IntoIter` Danilo Krummrich
2024-08-05 15:19 ` [PATCH v4 18/28] rust: treewide: switch to the kernel `Vec` type Danilo Krummrich
2024-08-07 13:47   ` Alice Ryhl
2024-08-08  9:08   ` Benno Lossin
2024-08-05 15:19 ` [PATCH v4 19/28] rust: alloc: remove `VecExt` extension Danilo Krummrich
2024-08-07 12:42   ` Alice Ryhl
2024-08-08  9:14   ` Benno Lossin
2024-08-05 15:19 ` [PATCH v4 20/28] rust: alloc: add `Vec` to prelude Danilo Krummrich
2024-08-07 13:55   ` Alice Ryhl
2024-08-08  8:40   ` Benno Lossin
2024-08-05 15:19 ` [PATCH v4 21/28] rust: alloc: remove `GlobalAlloc` and `krealloc_aligned` Danilo Krummrich
2024-08-06 19:07   ` Björn Roy Baron
2024-08-06 21:14     ` Miguel Ojeda
2024-08-07 21:23   ` Benno Lossin
2024-08-07 23:16     ` Danilo Krummrich
2024-08-05 15:19 ` [PATCH v4 22/28] rust: error: use `core::alloc::LayoutError` Danilo Krummrich
2024-08-07 13:55   ` Alice Ryhl
2024-08-08  7:42   ` Benno Lossin
2024-08-05 15:19 ` [PATCH v4 23/28] rust: error: check for config `test` in `Error::name` Danilo Krummrich
2024-08-07 13:54   ` Alice Ryhl
2024-08-05 15:19 ` [PATCH v4 24/28] rust: alloc: implement `contains` for `Flags` Danilo Krummrich
2024-08-07 13:53   ` Alice Ryhl
2024-08-05 15:19 ` [PATCH v4 25/28] rust: alloc: implement `Cmalloc` in module allocator_test Danilo Krummrich
2024-08-08  9:35   ` Benno Lossin
2024-08-08 10:07     ` Danilo Krummrich
2024-08-05 15:19 ` [PATCH v4 26/28] rust: str: test: replace `alloc::format` Danilo Krummrich
2024-08-07 13:51   ` Alice Ryhl
2024-08-08  7:22   ` Benno Lossin
2024-08-12 13:07     ` Danilo Krummrich
2024-08-05 15:19 ` [PATCH v4 27/28] rust: alloc: update module comment of alloc.rs Danilo Krummrich
2024-08-07 13:50   ` Alice Ryhl
2024-08-08  6:59   ` Benno Lossin
2024-08-05 15:19 ` [PATCH v4 28/28] kbuild: rust: remove the `alloc` crate Danilo Krummrich
2024-08-08  6:58   ` Benno Lossin
2024-08-08  9:45   ` Benno Lossin

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