* [PATCH v5 01/26] rust: alloc: add `Allocator` trait
2024-08-12 18:22 [PATCH v5 00/26] Generic `Allocator` support for Rust Danilo Krummrich
@ 2024-08-12 18:22 ` Danilo Krummrich
2024-08-14 16:13 ` Benno Lossin
2024-08-12 18:22 ` [PATCH v5 02/26] rust: alloc: separate `aligned_size` from `krealloc_aligned` Danilo Krummrich
` (25 subsequent siblings)
26 siblings, 1 reply; 95+ messages in thread
From: Danilo Krummrich @ 2024-08-12 18:22 UTC (permalink / raw)
To: ojeda, alex.gaynor, wedsonaf, boqun.feng, gary, bjorn3_gh,
benno.lossin, a.hindborg, aliceryhl, akpm
Cc: daniel.almeida, faith.ekstrand, boris.brezillon, lina, mcanal,
zhiw, cjia, jhubbard, airlied, ajanulgu, lyude, linux-kernel,
rust-for-linux, linux-mm, Danilo Krummrich
Add a kernel specific `Allocator` trait, that in contrast to the one in
Rust's core library doesn't require unstable features and supports GFP
flags.
Subsequent patches add the following trait implementors: `Kmalloc`,
`Vmalloc` and `KVmalloc`.
Reviewed-by: Alice Ryhl <aliceryhl@google.com>
Signed-off-by: Danilo Krummrich <dakr@kernel.org>
---
rust/kernel/alloc.rs | 81 ++++++++++++++++++++++++++++++++++++++++++++
1 file changed, 81 insertions(+)
diff --git a/rust/kernel/alloc.rs b/rust/kernel/alloc.rs
index 1966bd407017..194745498a75 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,83 @@ pub mod flags {
/// small allocations.
pub const GFP_NOWAIT: Flags = Flags(bindings::GFP_NOWAIT);
}
+
+/// The kernel's [`Allocator`] trait.
+///
+/// An implementation of [`Allocator`] can allocate, re-allocate and free memory buffer described
+/// via [`Layout`].
+///
+/// [`Allocator`] is designed to be implemented as a ZST; [`Allocator`] functions do not operate on
+/// an object instance.
+///
+/// In order to be able to support `#[derive(SmartPointer)]` later on, we need to avoid a design
+/// that requires an `Allocator` to be instantiated, hence its functions must not contain any kind
+/// of `self` parameter.
+///
+/// # Safety
+///
+/// Memory returned from an allocator must point to a valid memory buffer and remain valid until
+/// it is explicitly freed.
+///
+/// Any pointer to a memory buffer which is currently allocated must be valid to be passed to any
+/// other [`Allocator`] function of the same type.
+///
+/// If `realloc` is called with:
+/// - a size of zero, the given memory allocation, if any, must be freed
+/// - `None`, a new memory allocation must be created
+pub unsafe trait Allocator {
+ /// Allocate memory based on `layout` and `flags`.
+ ///
+ /// On success, returns a buffer represented as `NonNull<[u8]>` that satisfies the layout
+ /// constraints (i.e. minimum size and alignment as specified by `layout`).
+ ///
+ /// This function is equivalent to `realloc` when called with `None`.
+ fn alloc(layout: Layout, flags: Flags) -> Result<NonNull<[u8]>, AllocError> {
+ // SAFETY: Passing `None` to `realloc` is valid by it's safety requirements and asks for a
+ // new memory allocation.
+ unsafe { Self::realloc(None, layout, flags) }
+ }
+
+ /// Re-allocate an existing memory allocation to satisfy the requested `layout`. If the
+ /// requested size is zero, `realloc` behaves equivalent to `free`.
+ ///
+ /// If the requested size is larger than the size of the existing allocation, a successful call
+ /// to `realloc` guarantees that the new or grown buffer has at least `Layout::size` bytes, but
+ /// may also be larger.
+ ///
+ /// If the requested size is smaller than the size of the existing allocation, `realloc` may or
+ /// may not shrink the buffer; this is implementation specific to the allocator.
+ ///
+ /// On allocation failure, the existing buffer, if any, remains valid.
+ ///
+ /// The buffer is represented as `NonNull<[u8]>`.
+ ///
+ /// # Safety
+ ///
+ /// If `ptr = Some(p)`, then `p` must point to an existing and valid memory allocation created
+ /// by this allocator. The alignment encoded in `layout` must be smaller than or equal to the
+ /// alignment requested in the previous `alloc` or `realloc` call of the same allocation.
+ ///
+ /// Additionally, `ptr` is allowed to be `None`; in this case a new memory allocation is
+ /// created.
+ unsafe fn realloc(
+ ptr: Option<NonNull<u8>>,
+ layout: Layout,
+ flags: Flags,
+ ) -> Result<NonNull<[u8]>, AllocError>;
+
+ /// Free an existing memory allocation.
+ ///
+ /// # Safety
+ ///
+ /// `ptr` must point to an existing and valid memory allocation created by this `Allocator` and
+ /// must not be a dangling pointer.
+ ///
+ /// The memory allocation at `ptr` must never again be read from or written to.
+ unsafe fn free(ptr: NonNull<u8>) {
+ // SAFETY: The caller guarantees that `ptr` points at a valid allocation created by this
+ // allocator. We are passing a `Layout` with the smallest possible alignment, so it is
+ // smaller than or equal to the alignment previously used with this allocation.
+ let _ = unsafe { Self::realloc(Some(ptr), Layout::new::<()>(), Flags(0)) };
+ }
+}
--
2.45.2
^ permalink raw reply related [flat|nested] 95+ messages in thread* Re: [PATCH v5 01/26] rust: alloc: add `Allocator` trait
2024-08-12 18:22 ` [PATCH v5 01/26] rust: alloc: add `Allocator` trait Danilo Krummrich
@ 2024-08-14 16:13 ` Benno Lossin
2024-08-15 0:16 ` Danilo Krummrich
0 siblings, 1 reply; 95+ messages in thread
From: Benno Lossin @ 2024-08-14 16:13 UTC (permalink / raw)
To: Danilo Krummrich, ojeda, alex.gaynor, wedsonaf, boqun.feng, gary,
bjorn3_gh, a.hindborg, aliceryhl, akpm
Cc: daniel.almeida, faith.ekstrand, boris.brezillon, lina, mcanal,
zhiw, cjia, jhubbard, airlied, ajanulgu, lyude, linux-kernel,
rust-for-linux, linux-mm
On 12.08.24 20:22, 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`.
>
> Reviewed-by: Alice Ryhl <aliceryhl@google.com>
> Signed-off-by: Danilo Krummrich <dakr@kernel.org>
> ---
> rust/kernel/alloc.rs | 81 ++++++++++++++++++++++++++++++++++++++++++++
> 1 file changed, 81 insertions(+)
>
> diff --git a/rust/kernel/alloc.rs b/rust/kernel/alloc.rs
> index 1966bd407017..194745498a75 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,83 @@ pub mod flags {
> /// small allocations.
> pub const GFP_NOWAIT: Flags = Flags(bindings::GFP_NOWAIT);
> }
> +
> +/// The kernel's [`Allocator`] trait.
> +///
> +/// An implementation of [`Allocator`] can allocate, re-allocate and free memory buffer described
> +/// via [`Layout`].
> +///
> +/// [`Allocator`] is designed to be implemented as a ZST; [`Allocator`] functions do not operate on
> +/// an object instance.
> +///
> +/// In order to be able to support `#[derive(SmartPointer)]` later on, we need to avoid a design
> +/// that requires an `Allocator` to be instantiated, hence its functions must not contain any kind
> +/// of `self` parameter.
> +///
> +/// # Safety
> +///
> +/// Memory returned from an allocator must point to a valid memory buffer and remain valid until
> +/// it is explicitly freed.
I wouldn't say that an allocator "returns memory", and in general I
don't think the structure of the safety comment here is nice, how about
the following: we put "Implementers must ensure that all trait functions
abide by the guarantees documented in the `# Guarantees` sections."...
> +///
> +/// Any pointer to a memory buffer which is currently allocated must be valid to be passed to any
> +/// other [`Allocator`] function of the same type.
> +///
> +/// If `realloc` is called with:
> +/// - a size of zero, the given memory allocation, if any, must be freed
> +/// - `None`, a new memory allocation must be created
> +pub unsafe trait Allocator {
> + /// Allocate memory based on `layout` and `flags`.
> + ///
> + /// On success, returns a buffer represented as `NonNull<[u8]>` that satisfies the layout
> + /// constraints (i.e. minimum size and alignment as specified by `layout`).
> + ///
> + /// This function is equivalent to `realloc` when called with `None`.
... Then we can add this here:
/// # Guarantees
///
/// When the return value is `Ok(ptr)`, then `ptr` is
/// - valid for writes (and reads after the memory has been initialized) for `layout.size()` bytes,
/// until it is passed to [`Allocator::free`] or [`Allocator::realloc`],
/// - aligned to `layout.align()`,
/// - is valid for reads, if `flags.contains(flags::__GFP_ZERO)`,
Do we need to handle other flags?
Also IIRC the memory given to us by C is considered initialized by Rust
(though it has a non-deterministic value), so we might have an
unconditional "valid for reads". Am I correct?
> + fn alloc(layout: Layout, flags: Flags) -> Result<NonNull<[u8]>, AllocError> {
> + // SAFETY: Passing `None` to `realloc` is valid by it's safety requirements and asks for a
> + // new memory allocation.
> + unsafe { Self::realloc(None, layout, flags) }
> + }
> +
> + /// Re-allocate an existing memory allocation to satisfy the requested `layout`. If the
> + /// requested size is zero, `realloc` behaves equivalent to `free`.
I don't think we want to include the second sentence in the short
description of this function, please add an empty line in between.
> + ///
> + /// If the requested size is larger than the size of the existing allocation, a successful call
> + /// to `realloc` guarantees that the new or grown buffer has at least `Layout::size` bytes, but
> + /// may also be larger.
> + ///
> + /// If the requested size is smaller than the size of the existing allocation, `realloc` may or
> + /// may not shrink the buffer; this is implementation specific to the allocator.
> + ///
> + /// On allocation failure, the existing buffer, if any, remains valid.
> + ///
> + /// The buffer is represented as `NonNull<[u8]>`.
> + ///
> + /// # Safety
> + ///
> + /// If `ptr = Some(p)`, then `p` must point to an existing and valid memory allocation created
I don't like the single `=` (I might have written it in haste myself),
how about `==` or we use if-let syntax?
> + /// by this allocator. The alignment encoded in `layout` must be smaller than or equal to the
> + /// alignment requested in the previous `alloc` or `realloc` call of the same allocation.
> + ///
> + /// Additionally, `ptr` is allowed to be `None`; in this case a new memory allocation is
> + /// created.
This Safety section does not talk about the case `layout.size() == 0`,
but it should have the same requirement as `free()`.
Also add a `# Guarantees` section here:
/// # Guarantees
///
/// This function has the same guarantees as [`Allocator::alloc`]. When `ptr == Some(p)`, then it
/// additionally has the following:
/// - when `Ok(ret_ptr)` is the return value, then
/// `ret_ptr[0..min(layout.size(), old_size)] == p[0..min(layout.size(), old_size)]`, where
/// `old_size` is the size of the allocation that `p` points at.
/// - when the return value is `Err(AllocError)`, then `p` is still valid.
---
Cheers,
Benno
> + unsafe fn realloc(
> + ptr: Option<NonNull<u8>>,
> + layout: Layout,
> + flags: Flags,
> + ) -> Result<NonNull<[u8]>, AllocError>;
> +
> + /// Free an existing memory allocation.
> + ///
> + /// # Safety
> + ///
> + /// `ptr` must point to an existing and valid memory allocation created by this `Allocator` and
> + /// must not be a dangling pointer.
> + ///
> + /// The memory allocation at `ptr` must never again be read from or written to.
> + unsafe fn free(ptr: NonNull<u8>) {
> + // SAFETY: The caller guarantees that `ptr` points at a valid allocation created by this
> + // allocator. We are passing a `Layout` with the smallest possible alignment, so it is
> + // smaller than or equal to the alignment previously used with this allocation.
> + let _ = unsafe { Self::realloc(Some(ptr), Layout::new::<()>(), Flags(0)) };
> + }
> +}
> --
> 2.45.2
>
^ permalink raw reply [flat|nested] 95+ messages in thread* Re: [PATCH v5 01/26] rust: alloc: add `Allocator` trait
2024-08-14 16:13 ` Benno Lossin
@ 2024-08-15 0:16 ` Danilo Krummrich
2024-08-15 13:49 ` Benno Lossin
0 siblings, 1 reply; 95+ messages in thread
From: Danilo Krummrich @ 2024-08-15 0: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, cjia, jhubbard, airlied,
ajanulgu, lyude, linux-kernel, rust-for-linux, linux-mm
On Wed, Aug 14, 2024 at 04:13:06PM +0000, Benno Lossin wrote:
> On 12.08.24 20:22, 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`.
> >
> > Reviewed-by: Alice Ryhl <aliceryhl@google.com>
> > Signed-off-by: Danilo Krummrich <dakr@kernel.org>
> > ---
> > rust/kernel/alloc.rs | 81 ++++++++++++++++++++++++++++++++++++++++++++
> > 1 file changed, 81 insertions(+)
> >
> > diff --git a/rust/kernel/alloc.rs b/rust/kernel/alloc.rs
> > index 1966bd407017..194745498a75 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,83 @@ pub mod flags {
> > /// small allocations.
> > pub const GFP_NOWAIT: Flags = Flags(bindings::GFP_NOWAIT);
> > }
> > +
> > +/// The kernel's [`Allocator`] trait.
> > +///
> > +/// An implementation of [`Allocator`] can allocate, re-allocate and free memory buffer described
> > +/// via [`Layout`].
> > +///
> > +/// [`Allocator`] is designed to be implemented as a ZST; [`Allocator`] functions do not operate on
> > +/// an object instance.
> > +///
> > +/// In order to be able to support `#[derive(SmartPointer)]` later on, we need to avoid a design
> > +/// that requires an `Allocator` to be instantiated, hence its functions must not contain any kind
> > +/// of `self` parameter.
> > +///
> > +/// # Safety
> > +///
> > +/// Memory returned from an allocator must point to a valid memory buffer and remain valid until
> > +/// it is explicitly freed.
>
> I wouldn't say that an allocator "returns memory", and in general I
> don't think the structure of the safety comment here is nice, how about
> the following: we put "Implementers must ensure that all trait functions
> abide by the guarantees documented in the `# Guarantees` sections."...
Sounds reasonable to me. Additionally, I'd still keep the part below, that says
that any pointer to a memory allocation must bbe valid to be passed to any other [`Allocator`]
function of the same type.
>
> > +///
> > +/// Any pointer to a memory buffer which is currently allocated must be valid to be passed to any
> > +/// other [`Allocator`] function of the same type.
> > +///
> > +/// If `realloc` is called with:
> > +/// - a size of zero, the given memory allocation, if any, must be freed
> > +/// - `None`, a new memory allocation must be created
> > +pub unsafe trait Allocator {
> > + /// Allocate memory based on `layout` and `flags`.
> > + ///
> > + /// On success, returns a buffer represented as `NonNull<[u8]>` that satisfies the layout
> > + /// constraints (i.e. minimum size and alignment as specified by `layout`).
> > + ///
> > + /// This function is equivalent to `realloc` when called with `None`.
>
> ... Then we can add this here:
>
> /// # Guarantees
> ///
> /// When the return value is `Ok(ptr)`, then `ptr` is
> /// - valid for writes (and reads after the memory has been initialized) for `layout.size()` bytes,
> /// until it is passed to [`Allocator::free`] or [`Allocator::realloc`],
> /// - aligned to `layout.align()`,
> /// - is valid for reads, if `flags.contains(flags::__GFP_ZERO)`,
>
> Do we need to handle other flags?
The whole flags thing is a bit difficult to represent here properly.
Theoretically, we'd need to add that it guarantees that the memory is zeroed for
__GFP_ZERO, non-blocking for GFP_NOWAIT, etc. But, I think we shouldn't
re-iterate all different behavior for the different flags.
Another inconvenience is that not all page flags are honored or make sense for
all allocators. This is especially inconvenient for `KVmalloc` where we can't
even say if we end up in vrealloc() or krealloc(). kvmalloc() even contains a
couple of flag fixups for this reason [2].
I think we should just point to [1], which should document everything already.
[1] https://elixir.bootlin.com/linux/v6.10.4/source/include/linux/gfp_types.h
[2] https://elixir.bootlin.com/linux/v6.10.4/source/mm/util.c#L612
> Also IIRC the memory given to us by C is considered initialized by Rust
> (though it has a non-deterministic value), so we might have an
> unconditional "valid for reads". Am I correct?
Yes, but as you say, unless allocated with __GFP_ZERO, it contains non-deterministic data. It may
even contain old data from previous allocations.
>
>
> > + fn alloc(layout: Layout, flags: Flags) -> Result<NonNull<[u8]>, AllocError> {
> > + // SAFETY: Passing `None` to `realloc` is valid by it's safety requirements and asks for a
> > + // new memory allocation.
> > + unsafe { Self::realloc(None, layout, flags) }
> > + }
> > +
> > + /// Re-allocate an existing memory allocation to satisfy the requested `layout`. If the
> > + /// requested size is zero, `realloc` behaves equivalent to `free`.
>
> I don't think we want to include the second sentence in the short
> description of this function, please add an empty line in between.
>
> > + ///
> > + /// If the requested size is larger than the size of the existing allocation, a successful call
> > + /// to `realloc` guarantees that the new or grown buffer has at least `Layout::size` bytes, but
> > + /// may also be larger.
> > + ///
> > + /// If the requested size is smaller than the size of the existing allocation, `realloc` may or
> > + /// may not shrink the buffer; this is implementation specific to the allocator.
> > + ///
> > + /// On allocation failure, the existing buffer, if any, remains valid.
> > + ///
> > + /// The buffer is represented as `NonNull<[u8]>`.
> > + ///
> > + /// # Safety
> > + ///
> > + /// If `ptr = Some(p)`, then `p` must point to an existing and valid memory allocation created
>
> I don't like the single `=` (I might have written it in haste myself),
> how about `==` or we use if-let syntax?
>
> > + /// by this allocator. The alignment encoded in `layout` must be smaller than or equal to the
> > + /// alignment requested in the previous `alloc` or `realloc` call of the same allocation.
> > + ///
> > + /// Additionally, `ptr` is allowed to be `None`; in this case a new memory allocation is
> > + /// created.
>
> This Safety section does not talk about the case `layout.size() == 0`,
> but it should have the same requirement as `free()`.
>
> Also add a `# Guarantees` section here:
>
> /// # Guarantees
> ///
> /// This function has the same guarantees as [`Allocator::alloc`]. When `ptr == Some(p)`, then it
> /// additionally has the following:
> /// - when `Ok(ret_ptr)` is the return value, then
> /// `ret_ptr[0..min(layout.size(), old_size)] == p[0..min(layout.size(), old_size)]`, where
> /// `old_size` is the size of the allocation that `p` points at.
We could also say "The contents of the memory pointed to by `p` are preserved
up to the lesser of the new and old size." But I'm fine with both.
> /// - when the return value is `Err(AllocError)`, then `p` is still valid.
>
> ---
> Cheers,
> Benno
>
> > + unsafe fn realloc(
> > + ptr: Option<NonNull<u8>>,
> > + layout: Layout,
> > + flags: Flags,
> > + ) -> Result<NonNull<[u8]>, AllocError>;
>
> > +
> > + /// Free an existing memory allocation.
> > + ///
> > + /// # Safety
> > + ///
> > + /// `ptr` must point to an existing and valid memory allocation created by this `Allocator` and
> > + /// must not be a dangling pointer.
> > + ///
> > + /// The memory allocation at `ptr` must never again be read from or written to.
> > + unsafe fn free(ptr: NonNull<u8>) {
> > + // SAFETY: The caller guarantees that `ptr` points at a valid allocation created by this
> > + // allocator. We are passing a `Layout` with the smallest possible alignment, so it is
> > + // smaller than or equal to the alignment previously used with this allocation.
> > + let _ = unsafe { Self::realloc(Some(ptr), Layout::new::<()>(), Flags(0)) };
> > + }
> > +}
> > --
> > 2.45.2
> >
>
^ permalink raw reply [flat|nested] 95+ messages in thread* Re: [PATCH v5 01/26] rust: alloc: add `Allocator` trait
2024-08-15 0:16 ` Danilo Krummrich
@ 2024-08-15 13:49 ` Benno Lossin
0 siblings, 0 replies; 95+ messages in thread
From: Benno Lossin @ 2024-08-15 13: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, cjia, jhubbard, airlied,
ajanulgu, lyude, linux-kernel, rust-for-linux, linux-mm
On 15.08.24 02:16, Danilo Krummrich wrote:
> On Wed, Aug 14, 2024 at 04:13:06PM +0000, Benno Lossin wrote:
>> On 12.08.24 20:22, Danilo Krummrich wrote:
>>> +/// # Safety
>>> +///
>>> +/// Memory returned from an allocator must point to a valid memory buffer and remain valid until
>>> +/// it is explicitly freed.
>>
>> I wouldn't say that an allocator "returns memory", and in general I
>> don't think the structure of the safety comment here is nice, how about
>> the following: we put "Implementers must ensure that all trait functions
>> abide by the guarantees documented in the `# Guarantees` sections."...
>
> Sounds reasonable to me. Additionally, I'd still keep the part below, that says
> that any pointer to a memory allocation must bbe valid to be passed to any other [`Allocator`]
> function of the same type.
Yes of course, that should be kept.
>>> +///
>>> +/// Any pointer to a memory buffer which is currently allocated must be valid to be passed to any
>>> +/// other [`Allocator`] function of the same type.
>>> +///
>>> +/// If `realloc` is called with:
>>> +/// - a size of zero, the given memory allocation, if any, must be freed
>>> +/// - `None`, a new memory allocation must be created
Only this list should be moved.
>>> +pub unsafe trait Allocator {
>>> + /// Allocate memory based on `layout` and `flags`.
>>> + ///
>>> + /// On success, returns a buffer represented as `NonNull<[u8]>` that satisfies the layout
>>> + /// constraints (i.e. minimum size and alignment as specified by `layout`).
>>> + ///
>>> + /// This function is equivalent to `realloc` when called with `None`.
>>
>> ... Then we can add this here:
>>
>> /// # Guarantees
>> ///
>> /// When the return value is `Ok(ptr)`, then `ptr` is
>> /// - valid for writes (and reads after the memory has been initialized) for `layout.size()` bytes,
>> /// until it is passed to [`Allocator::free`] or [`Allocator::realloc`],
>> /// - aligned to `layout.align()`,
>> /// - is valid for reads, if `flags.contains(flags::__GFP_ZERO)`,
>>
>> Do we need to handle other flags?
>
> The whole flags thing is a bit difficult to represent here properly.
>
> Theoretically, we'd need to add that it guarantees that the memory is zeroed for
> __GFP_ZERO, non-blocking for GFP_NOWAIT, etc. But, I think we shouldn't
> re-iterate all different behavior for the different flags.
If there are good docs, then link them.
> Another inconvenience is that not all page flags are honored or make sense for
> all allocators. This is especially inconvenient for `KVmalloc` where we can't
> even say if we end up in vrealloc() or krealloc(). kvmalloc() even contains a
> couple of flag fixups for this reason [2].
I am wondering if we want to encode this in the type system...
> I think we should just point to [1], which should document everything already.
>
> [1] https://elixir.bootlin.com/linux/v6.10.4/source/include/linux/gfp_types.h
> [2] https://elixir.bootlin.com/linux/v6.10.4/source/mm/util.c#L612
>
>> Also IIRC the memory given to us by C is considered initialized by Rust
>> (though it has a non-deterministic value), so we might have an
>> unconditional "valid for reads". Am I correct?
>
> Yes, but as you say, unless allocated with __GFP_ZERO, it contains non-deterministic data. It may
> even contain old data from previous allocations.
Yeah, but IIRC that is not a soundness issue. So memory coming from C is
always considered initialized by Rust. Whereas
`MaybeUninit::uninit().assume_init()` is insta-UB, reading such memory
should be fine (just not useful).
>>> + /// by this allocator. The alignment encoded in `layout` must be smaller than or equal to the
>>> + /// alignment requested in the previous `alloc` or `realloc` call of the same allocation.
>>> + ///
>>> + /// Additionally, `ptr` is allowed to be `None`; in this case a new memory allocation is
>>> + /// created.
>>
>> This Safety section does not talk about the case `layout.size() == 0`,
>> but it should have the same requirement as `free()`.
>>
>> Also add a `# Guarantees` section here:
>>
>> /// # Guarantees
>> ///
>> /// This function has the same guarantees as [`Allocator::alloc`]. When `ptr == Some(p)`, then it
>> /// additionally has the following:
>> /// - when `Ok(ret_ptr)` is the return value, then
>> /// `ret_ptr[0..min(layout.size(), old_size)] == p[0..min(layout.size(), old_size)]`, where
>> /// `old_size` is the size of the allocation that `p` points at.
>
> We could also say "The contents of the memory pointed to by `p` are preserved
> up to the lesser of the new and old size." But I'm fine with both.
I can read and write the math-ish syntax better, so I would prefer that
over words. If others think we should use words, then we can discuss.
---
Cheers,
Benno
^ permalink raw reply [flat|nested] 95+ messages in thread
* [PATCH v5 02/26] rust: alloc: separate `aligned_size` from `krealloc_aligned`
2024-08-12 18:22 [PATCH v5 00/26] Generic `Allocator` support for Rust Danilo Krummrich
2024-08-12 18:22 ` [PATCH v5 01/26] rust: alloc: add `Allocator` trait Danilo Krummrich
@ 2024-08-12 18:22 ` Danilo Krummrich
2024-08-12 18:22 ` [PATCH v5 03/26] rust: alloc: rename `KernelAllocator` to `Kmalloc` Danilo Krummrich
` (24 subsequent siblings)
26 siblings, 0 replies; 95+ messages in thread
From: Danilo Krummrich @ 2024-08-12 18:22 UTC (permalink / raw)
To: ojeda, alex.gaynor, wedsonaf, boqun.feng, gary, bjorn3_gh,
benno.lossin, a.hindborg, aliceryhl, akpm
Cc: daniel.almeida, faith.ekstrand, boris.brezillon, lina, mcanal,
zhiw, cjia, jhubbard, airlied, ajanulgu, lyude, linux-kernel,
rust-for-linux, linux-mm, Danilo Krummrich
Separate `aligned_size` from `krealloc_aligned`.
Subsequent patches implement `Allocator` derivates, such as `Kmalloc`,
that require `aligned_size` and replace the original `krealloc_aligned`.
Reviewed-by: Alice Ryhl <aliceryhl@google.com>
Reviewed-by: Benno Lossin <benno.lossin@proton.me>
Signed-off-by: Danilo Krummrich <dakr@kernel.org>
---
rust/kernel/alloc/allocator.rs | 19 ++++++++++++-------
1 file changed, 12 insertions(+), 7 deletions(-)
diff --git a/rust/kernel/alloc/allocator.rs b/rust/kernel/alloc/allocator.rs
index e6ea601f38c6..c83b6dff896d 100644
--- a/rust/kernel/alloc/allocator.rs
+++ b/rust/kernel/alloc/allocator.rs
@@ -8,6 +8,17 @@
struct KernelAllocator;
+/// Returns a proper size to alloc a new object aligned to `new_layout`'s alignment.
+fn aligned_size(new_layout: Layout) -> usize {
+ // Customized layouts from `Layout::from_size_align()` can have size < align, so pad first.
+ let layout = new_layout.pad_to_align();
+
+ // Note that `layout.size()` (after padding) is guaranteed to be a multiple of `layout.align()`
+ // which together with the slab guarantees means the `krealloc` will return a properly aligned
+ // object (see comments in `kmalloc()` for more information).
+ layout.size()
+}
+
/// Calls `krealloc` with a proper size to alloc a new object aligned to `new_layout`'s alignment.
///
/// # Safety
@@ -15,13 +26,7 @@
/// - `ptr` can be either null or a pointer which has been allocated by this allocator.
/// - `new_layout` must have a non-zero size.
pub(crate) unsafe fn krealloc_aligned(ptr: *mut u8, new_layout: Layout, flags: Flags) -> *mut u8 {
- // Customized layouts from `Layout::from_size_align()` can have size < align, so pad first.
- let layout = new_layout.pad_to_align();
-
- // Note that `layout.size()` (after padding) is guaranteed to be a multiple of `layout.align()`
- // which together with the slab guarantees means the `krealloc` will return a properly aligned
- // object (see comments in `kmalloc()` for more information).
- let size = layout.size();
+ let size = aligned_size(new_layout);
// SAFETY:
// - `ptr` is either null or a pointer returned from a previous `k{re}alloc()` by the
--
2.45.2
^ permalink raw reply related [flat|nested] 95+ messages in thread* [PATCH v5 03/26] rust: alloc: rename `KernelAllocator` to `Kmalloc`
2024-08-12 18:22 [PATCH v5 00/26] Generic `Allocator` support for Rust Danilo Krummrich
2024-08-12 18:22 ` [PATCH v5 01/26] rust: alloc: add `Allocator` trait Danilo Krummrich
2024-08-12 18:22 ` [PATCH v5 02/26] rust: alloc: separate `aligned_size` from `krealloc_aligned` Danilo Krummrich
@ 2024-08-12 18:22 ` Danilo Krummrich
2024-08-12 18:22 ` [PATCH v5 04/26] rust: alloc: implement `Allocator` for `Kmalloc` Danilo Krummrich
` (23 subsequent siblings)
26 siblings, 0 replies; 95+ messages in thread
From: Danilo Krummrich @ 2024-08-12 18:22 UTC (permalink / raw)
To: ojeda, alex.gaynor, wedsonaf, boqun.feng, gary, bjorn3_gh,
benno.lossin, a.hindborg, aliceryhl, akpm
Cc: daniel.almeida, faith.ekstrand, boris.brezillon, lina, mcanal,
zhiw, cjia, jhubbard, airlied, ajanulgu, lyude, linux-kernel,
rust-for-linux, linux-mm, Danilo Krummrich
Subsequent patches implement `Vmalloc` and `KVmalloc` allocators, hence
align `KernelAllocator` to this naming scheme.
Reviewed-by: Alice Ryhl <aliceryhl@google.com>
Reviewed-by: Benno Lossin <benno.lossin@proton.me>
Reviewed-by: Gary Guo <gary@garyguo.net>
Signed-off-by: Danilo Krummrich <dakr@kernel.org>
---
rust/kernel/alloc/allocator.rs | 6 +++---
1 file changed, 3 insertions(+), 3 deletions(-)
diff --git a/rust/kernel/alloc/allocator.rs b/rust/kernel/alloc/allocator.rs
index c83b6dff896d..e32182f91167 100644
--- a/rust/kernel/alloc/allocator.rs
+++ b/rust/kernel/alloc/allocator.rs
@@ -6,7 +6,7 @@
use core::alloc::{GlobalAlloc, Layout};
use core::ptr;
-struct KernelAllocator;
+struct Kmalloc;
/// Returns a proper size to alloc a new object aligned to `new_layout`'s alignment.
fn aligned_size(new_layout: Layout) -> usize {
@@ -36,7 +36,7 @@ pub(crate) unsafe fn krealloc_aligned(ptr: *mut u8, new_layout: Layout, flags: F
unsafe { bindings::krealloc(ptr as *const core::ffi::c_void, size, flags.0) as *mut u8 }
}
-unsafe impl GlobalAlloc for KernelAllocator {
+unsafe impl GlobalAlloc for Kmalloc {
unsafe fn alloc(&self, layout: Layout) -> *mut u8 {
// SAFETY: `ptr::null_mut()` is null and `layout` has a non-zero size by the function safety
// requirement.
@@ -72,7 +72,7 @@ unsafe fn alloc_zeroed(&self, layout: Layout) -> *mut u8 {
}
#[global_allocator]
-static ALLOCATOR: KernelAllocator = KernelAllocator;
+static ALLOCATOR: Kmalloc = Kmalloc;
// See <https://github.com/rust-lang/rust/pull/86844>.
#[no_mangle]
--
2.45.2
^ permalink raw reply related [flat|nested] 95+ messages in thread* [PATCH v5 04/26] rust: alloc: implement `Allocator` for `Kmalloc`
2024-08-12 18:22 [PATCH v5 00/26] Generic `Allocator` support for Rust Danilo Krummrich
` (2 preceding siblings ...)
2024-08-12 18:22 ` [PATCH v5 03/26] rust: alloc: rename `KernelAllocator` to `Kmalloc` Danilo Krummrich
@ 2024-08-12 18:22 ` Danilo Krummrich
2024-08-14 7:51 ` Alice Ryhl
` (2 more replies)
2024-08-12 18:22 ` [PATCH v5 05/26] rust: alloc: add module `allocator_test` Danilo Krummrich
` (22 subsequent siblings)
26 siblings, 3 replies; 95+ messages in thread
From: Danilo Krummrich @ 2024-08-12 18:22 UTC (permalink / raw)
To: ojeda, alex.gaynor, wedsonaf, boqun.feng, gary, bjorn3_gh,
benno.lossin, a.hindborg, aliceryhl, akpm
Cc: daniel.almeida, faith.ekstrand, boris.brezillon, lina, mcanal,
zhiw, cjia, jhubbard, airlied, ajanulgu, lyude, linux-kernel,
rust-for-linux, linux-mm, Danilo Krummrich
Implement `Allocator` for `Kmalloc`, the kernel's default allocator,
typically used for objects smaller than page size.
All memory allocations made with `Kmalloc` end up in `krealloc()`.
It serves as allocator for the subsequently introduced types `KBox` and
`KVec`.
Signed-off-by: Danilo Krummrich <dakr@kernel.org>
---
rust/helpers.c | 3 +-
rust/kernel/alloc.rs | 2 +-
rust/kernel/alloc/allocator.rs | 63 +++++++++++++++++++++++++++++++++-
3 files changed, 64 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 194745498a75..b732720cfb95 100644
--- a/rust/kernel/alloc.rs
+++ b/rust/kernel/alloc.rs
@@ -4,7 +4,7 @@
#[cfg(not(test))]
#[cfg(not(testlib))]
-mod allocator;
+pub mod allocator;
pub mod box_ext;
pub mod vec_ext;
diff --git a/rust/kernel/alloc/allocator.rs b/rust/kernel/alloc/allocator.rs
index e32182f91167..b46883d87715 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 {
@@ -36,6 +44,59 @@ pub(crate) unsafe fn krealloc_aligned(ptr: *mut u8, new_layout: Layout, flags: F
unsafe { bindings::krealloc(ptr as *const core::ffi::c_void, size, flags.0) as *mut u8 }
}
+/// # Invariants
+///
+/// One of the following `krealloc`, `vrealloc`, `kvrealloc`.
+struct ReallocFunc(
+ unsafe extern "C" fn(*const core::ffi::c_void, usize, u32) -> *mut core::ffi::c_void,
+);
+
+impl ReallocFunc {
+ // INVARIANT: `krealloc` satisfies the type invariants.
+ const KREALLOC: Self = Self(bindings::krealloc);
+
+ /// # Safety
+ ///
+ /// This method has the same safety requirements as `Allocator::realloc`.
+ unsafe fn call(
+ &self,
+ ptr: Option<NonNull<u8>>,
+ layout: Layout,
+ flags: Flags,
+ ) -> Result<NonNull<[u8]>, AllocError> {
+ let size = aligned_size(layout);
+ let ptr = match ptr {
+ Some(ptr) => ptr.as_ptr(),
+ None => ptr::null(),
+ };
+
+ // SAFETY: `ptr` is either NULL or valid by the safety requirements of this function.
+ let raw_ptr = unsafe {
+ // If `size == 0` and `ptr != NULL` the memory behind the pointer is freed.
+ self.0(ptr.cast(), size, flags.0).cast()
+ };
+
+ let ptr = if size == 0 {
+ 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> {
+ // SAFETY: `ReallocFunc::call` has the same safety requirements as `Allocator::realloc`.
+ unsafe { ReallocFunc::KREALLOC.call(ptr, layout, flags) }
+ }
+}
+
unsafe impl GlobalAlloc for Kmalloc {
unsafe fn alloc(&self, layout: Layout) -> *mut u8 {
// SAFETY: `ptr::null_mut()` is null and `layout` has a non-zero size by the function safety
--
2.45.2
^ permalink raw reply related [flat|nested] 95+ messages in thread* Re: [PATCH v5 04/26] rust: alloc: implement `Allocator` for `Kmalloc`
2024-08-12 18:22 ` [PATCH v5 04/26] rust: alloc: implement `Allocator` for `Kmalloc` Danilo Krummrich
@ 2024-08-14 7:51 ` Alice Ryhl
2024-08-14 13:36 ` Danilo Krummrich
2024-08-14 16:21 ` Benno Lossin
2024-08-14 16:28 ` Benno Lossin
2 siblings, 1 reply; 95+ messages in thread
From: Alice Ryhl @ 2024-08-14 7: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, cjia, jhubbard, airlied,
ajanulgu, lyude, linux-kernel, rust-for-linux, linux-mm
On Mon, Aug 12, 2024 at 8:24 PM Danilo Krummrich <dakr@kernel.org> 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 | 63 +++++++++++++++++++++++++++++++++-
> 3 files changed, 64 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);
> }
Why are the various annotations on this helper being removed? This
deserves an explanation in the commit message.
Alice
^ permalink raw reply [flat|nested] 95+ messages in thread* Re: [PATCH v5 04/26] rust: alloc: implement `Allocator` for `Kmalloc`
2024-08-14 7:51 ` Alice Ryhl
@ 2024-08-14 13:36 ` Danilo Krummrich
2024-08-14 13:44 ` Alice Ryhl
0 siblings, 1 reply; 95+ messages in thread
From: Danilo Krummrich @ 2024-08-14 13:36 UTC (permalink / raw)
To: Alice Ryhl
Cc: ojeda, alex.gaynor, wedsonaf, boqun.feng, gary, bjorn3_gh,
benno.lossin, a.hindborg, akpm, daniel.almeida, faith.ekstrand,
boris.brezillon, lina, mcanal, zhiw, cjia, jhubbard, airlied,
ajanulgu, lyude, linux-kernel, rust-for-linux, linux-mm
On Wed, Aug 14, 2024 at 09:51:34AM +0200, Alice Ryhl wrote:
> On Mon, Aug 12, 2024 at 8:24 PM Danilo Krummrich <dakr@kernel.org> 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 | 63 +++++++++++++++++++++++++++++++++-
> > 3 files changed, 64 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);
> > }
>
> Why are the various annotations on this helper being removed?
rust_helper_krealloc() is only called from Rust, hence neither __must_check nor
__realloc_size() should have any effect.
I also do not apply them in subsequent commits for the vrealloc() and
kvrealloc() helpers for this reason and removed them here for consistency.
> This deserves an explanation in the commit message.
I can also add a separate commit for that.
>
> Alice
>
^ permalink raw reply [flat|nested] 95+ messages in thread* Re: [PATCH v5 04/26] rust: alloc: implement `Allocator` for `Kmalloc`
2024-08-14 13:36 ` Danilo Krummrich
@ 2024-08-14 13:44 ` Alice Ryhl
2024-08-14 13:48 ` Danilo Krummrich
0 siblings, 1 reply; 95+ messages in thread
From: Alice Ryhl @ 2024-08-14 13:44 UTC (permalink / raw)
To: Danilo Krummrich
Cc: ojeda, alex.gaynor, wedsonaf, boqun.feng, gary, bjorn3_gh,
benno.lossin, a.hindborg, akpm, daniel.almeida, faith.ekstrand,
boris.brezillon, lina, mcanal, zhiw, cjia, jhubbard, airlied,
ajanulgu, lyude, linux-kernel, rust-for-linux, linux-mm
On Wed, Aug 14, 2024 at 3:36 PM Danilo Krummrich <dakr@kernel.org> wrote:
>
> On Wed, Aug 14, 2024 at 09:51:34AM +0200, Alice Ryhl wrote:
> > On Mon, Aug 12, 2024 at 8:24 PM Danilo Krummrich <dakr@kernel.org> 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 | 63 +++++++++++++++++++++++++++++++++-
> > > 3 files changed, 64 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);
> > > }
> >
> > Why are the various annotations on this helper being removed?
>
> rust_helper_krealloc() is only called from Rust, hence neither __must_check nor
> __realloc_size() should have any effect.
>
> I also do not apply them in subsequent commits for the vrealloc() and
> kvrealloc() helpers for this reason and removed them here for consistency.
>
> > This deserves an explanation in the commit message.
>
> I can also add a separate commit for that.
I think your change would be more obviously correct if you keep them.
Alice
^ permalink raw reply [flat|nested] 95+ messages in thread* Re: [PATCH v5 04/26] rust: alloc: implement `Allocator` for `Kmalloc`
2024-08-14 13:44 ` Alice Ryhl
@ 2024-08-14 13:48 ` Danilo Krummrich
2024-08-14 13:50 ` Alice Ryhl
0 siblings, 1 reply; 95+ messages in thread
From: Danilo Krummrich @ 2024-08-14 13:48 UTC (permalink / raw)
To: Alice Ryhl
Cc: ojeda, alex.gaynor, wedsonaf, boqun.feng, gary, bjorn3_gh,
benno.lossin, a.hindborg, akpm, daniel.almeida, faith.ekstrand,
boris.brezillon, lina, mcanal, zhiw, cjia, jhubbard, airlied,
ajanulgu, lyude, linux-kernel, rust-for-linux, linux-mm
On Wed, Aug 14, 2024 at 03:44:56PM +0200, Alice Ryhl wrote:
> On Wed, Aug 14, 2024 at 3:36 PM Danilo Krummrich <dakr@kernel.org> wrote:
> >
> > On Wed, Aug 14, 2024 at 09:51:34AM +0200, Alice Ryhl wrote:
> > > On Mon, Aug 12, 2024 at 8:24 PM Danilo Krummrich <dakr@kernel.org> 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 | 63 +++++++++++++++++++++++++++++++++-
> > > > 3 files changed, 64 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);
> > > > }
> > >
> > > Why are the various annotations on this helper being removed?
> >
> > rust_helper_krealloc() is only called from Rust, hence neither __must_check nor
> > __realloc_size() should have any effect.
> >
> > I also do not apply them in subsequent commits for the vrealloc() and
> > kvrealloc() helpers for this reason and removed them here for consistency.
> >
> > > This deserves an explanation in the commit message.
> >
> > I can also add a separate commit for that.
>
> I think your change would be more obviously correct if you keep them.
As in generally, or just for this patch?
Generally, I don't think we should indicate compiler checks that actually are
never done.
For this patch, yes, it's probably better to separate it.
>
> Alice
>
^ permalink raw reply [flat|nested] 95+ messages in thread* Re: [PATCH v5 04/26] rust: alloc: implement `Allocator` for `Kmalloc`
2024-08-14 13:48 ` Danilo Krummrich
@ 2024-08-14 13:50 ` Alice Ryhl
2024-08-14 14:00 ` Danilo Krummrich
0 siblings, 1 reply; 95+ messages in thread
From: Alice Ryhl @ 2024-08-14 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, cjia, jhubbard, airlied,
ajanulgu, lyude, linux-kernel, rust-for-linux, linux-mm
On Wed, Aug 14, 2024 at 3:48 PM Danilo Krummrich <dakr@kernel.org> wrote:
>
> On Wed, Aug 14, 2024 at 03:44:56PM +0200, Alice Ryhl wrote:
> > On Wed, Aug 14, 2024 at 3:36 PM Danilo Krummrich <dakr@kernel.org> wrote:
> > >
> > > On Wed, Aug 14, 2024 at 09:51:34AM +0200, Alice Ryhl wrote:
> > > > On Mon, Aug 12, 2024 at 8:24 PM Danilo Krummrich <dakr@kernel.org> 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 | 63 +++++++++++++++++++++++++++++++++-
> > > > > 3 files changed, 64 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);
> > > > > }
> > > >
> > > > Why are the various annotations on this helper being removed?
> > >
> > > rust_helper_krealloc() is only called from Rust, hence neither __must_check nor
> > > __realloc_size() should have any effect.
> > >
> > > I also do not apply them in subsequent commits for the vrealloc() and
> > > kvrealloc() helpers for this reason and removed them here for consistency.
> > >
> > > > This deserves an explanation in the commit message.
> > >
> > > I can also add a separate commit for that.
> >
> > I think your change would be more obviously correct if you keep them.
>
> As in generally, or just for this patch?
>
> Generally, I don't think we should indicate compiler checks that actually are
> never done.
>
> For this patch, yes, it's probably better to separate it.
In general. If you keep it, then I don't have to think about whether
it affects bindgen's output. That's the main reason.
Alice
^ permalink raw reply [flat|nested] 95+ messages in thread* Re: [PATCH v5 04/26] rust: alloc: implement `Allocator` for `Kmalloc`
2024-08-14 13:50 ` Alice Ryhl
@ 2024-08-14 14:00 ` Danilo Krummrich
2024-08-14 15:03 ` Miguel Ojeda
0 siblings, 1 reply; 95+ messages in thread
From: Danilo Krummrich @ 2024-08-14 14:00 UTC (permalink / raw)
To: Alice Ryhl
Cc: ojeda, alex.gaynor, wedsonaf, boqun.feng, gary, bjorn3_gh,
benno.lossin, a.hindborg, akpm, daniel.almeida, faith.ekstrand,
boris.brezillon, lina, mcanal, zhiw, cjia, jhubbard, airlied,
ajanulgu, lyude, linux-kernel, rust-for-linux, linux-mm
On Wed, Aug 14, 2024 at 03:50:27PM +0200, Alice Ryhl wrote:
> On Wed, Aug 14, 2024 at 3:48 PM Danilo Krummrich <dakr@kernel.org> wrote:
> >
> > On Wed, Aug 14, 2024 at 03:44:56PM +0200, Alice Ryhl wrote:
> > > On Wed, Aug 14, 2024 at 3:36 PM Danilo Krummrich <dakr@kernel.org> wrote:
> > > >
> > > > On Wed, Aug 14, 2024 at 09:51:34AM +0200, Alice Ryhl wrote:
> > > > > On Mon, Aug 12, 2024 at 8:24 PM Danilo Krummrich <dakr@kernel.org> 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 | 63 +++++++++++++++++++++++++++++++++-
> > > > > > 3 files changed, 64 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);
> > > > > > }
> > > > >
> > > > > Why are the various annotations on this helper being removed?
> > > >
> > > > rust_helper_krealloc() is only called from Rust, hence neither __must_check nor
> > > > __realloc_size() should have any effect.
> > > >
> > > > I also do not apply them in subsequent commits for the vrealloc() and
> > > > kvrealloc() helpers for this reason and removed them here for consistency.
> > > >
> > > > > This deserves an explanation in the commit message.
> > > >
> > > > I can also add a separate commit for that.
> > >
> > > I think your change would be more obviously correct if you keep them.
> >
> > As in generally, or just for this patch?
> >
> > Generally, I don't think we should indicate compiler checks that actually are
> > never done.
> >
> > For this patch, yes, it's probably better to separate it.
>
> In general. If you keep it, then I don't have to think about whether
> it affects bindgen's output. That's the main reason.
Well, it doesn't.
If we keep them, we'd consequently also need to add them for vrealloc() and
kvrealloc(). But again, they don't do anything for us, and hence are more
misleading than helpful IMO.
>
> Alice
>
^ permalink raw reply [flat|nested] 95+ messages in thread* Re: [PATCH v5 04/26] rust: alloc: implement `Allocator` for `Kmalloc`
2024-08-14 14:00 ` Danilo Krummrich
@ 2024-08-14 15:03 ` Miguel Ojeda
2024-08-14 15:19 ` Danilo Krummrich
0 siblings, 1 reply; 95+ messages in thread
From: Miguel Ojeda @ 2024-08-14 15:03 UTC (permalink / raw)
To: Danilo Krummrich
Cc: Alice Ryhl, ojeda, alex.gaynor, wedsonaf, boqun.feng, gary,
bjorn3_gh, benno.lossin, a.hindborg, akpm, daniel.almeida,
faith.ekstrand, boris.brezillon, lina, mcanal, zhiw, cjia,
jhubbard, airlied, ajanulgu, lyude, linux-kernel, rust-for-linux,
linux-mm
On Wed, Aug 14, 2024 at 4:00 PM Danilo Krummrich <dakr@kernel.org> wrote:
>
> If we keep them, we'd consequently also need to add them for vrealloc() and
> kvrealloc(). But again, they don't do anything for us, and hence are more
> misleading than helpful IMO.
In general, they could do something (e.g. `noreturn`), perhaps in the future.
Apart from being potentially misleading, do we gain something by
removing them? I guess simplicity in the file, but it is also simpler
to keep them aligned to the C side (which I guess is Alice's point),
and avoids having to keep track of what could have a present or future
impact in `bindgen`.
Cheers,
Miguel
^ permalink raw reply [flat|nested] 95+ messages in thread
* Re: [PATCH v5 04/26] rust: alloc: implement `Allocator` for `Kmalloc`
2024-08-14 15:03 ` Miguel Ojeda
@ 2024-08-14 15:19 ` Danilo Krummrich
2024-08-14 15:28 ` Benno Lossin
2024-08-14 16:02 ` Miguel Ojeda
0 siblings, 2 replies; 95+ messages in thread
From: Danilo Krummrich @ 2024-08-14 15:19 UTC (permalink / raw)
To: Miguel Ojeda
Cc: Alice Ryhl, ojeda, alex.gaynor, wedsonaf, boqun.feng, gary,
bjorn3_gh, benno.lossin, a.hindborg, akpm, daniel.almeida,
faith.ekstrand, boris.brezillon, lina, mcanal, zhiw, cjia,
jhubbard, airlied, ajanulgu, lyude, linux-kernel, rust-for-linux,
linux-mm
On Wed, Aug 14, 2024 at 05:03:21PM +0200, Miguel Ojeda wrote:
> On Wed, Aug 14, 2024 at 4:00 PM Danilo Krummrich <dakr@kernel.org> wrote:
> >
> > If we keep them, we'd consequently also need to add them for vrealloc() and
> > kvrealloc(). But again, they don't do anything for us, and hence are more
> > misleading than helpful IMO.
>
> In general, they could do something (e.g. `noreturn`), perhaps in the future.
Indeed, and I think once they're honored we should add them again.
It's just that I think as long as compiler attributes aren't honored, we should
not have them in the first place to avoid confusion about whether they do or do
not have any effect.
>
> Apart from being potentially misleading, do we gain something by
> removing them?
It's not so much that I want to remove them for krealloc(), it's that I don't
want to intentionally add them for the vrealloc() and kvrealloc() helpers,
knowing that they don't do anything (yet).
And I think it's even more confusing if the krealloc() helper has those compiler
attributes, but the vrealloc() and kvrealloc() helpers do not.
> I guess simplicity in the file, but it is also simpler
> to keep them aligned to the C side (which I guess is Alice's point),
> and avoids having to keep track of what could have a present or future
> impact in `bindgen`.
>
> Cheers,
> Miguel
>
^ permalink raw reply [flat|nested] 95+ messages in thread
* Re: [PATCH v5 04/26] rust: alloc: implement `Allocator` for `Kmalloc`
2024-08-14 15:19 ` Danilo Krummrich
@ 2024-08-14 15:28 ` Benno Lossin
2024-08-14 16:01 ` Danilo Krummrich
2024-08-14 16:02 ` Miguel Ojeda
1 sibling, 1 reply; 95+ messages in thread
From: Benno Lossin @ 2024-08-14 15:28 UTC (permalink / raw)
To: Danilo Krummrich, Miguel Ojeda
Cc: Alice Ryhl, ojeda, alex.gaynor, wedsonaf, boqun.feng, gary,
bjorn3_gh, a.hindborg, akpm, daniel.almeida, faith.ekstrand,
boris.brezillon, lina, mcanal, zhiw, cjia, jhubbard, airlied,
ajanulgu, lyude, linux-kernel, rust-for-linux, linux-mm
On 14.08.24 17:19, Danilo Krummrich wrote:
> On Wed, Aug 14, 2024 at 05:03:21PM +0200, Miguel Ojeda wrote:
>> On Wed, Aug 14, 2024 at 4:00 PM Danilo Krummrich <dakr@kernel.org> wrote:
>>>
>>> If we keep them, we'd consequently also need to add them for vrealloc() and
>>> kvrealloc(). But again, they don't do anything for us, and hence are more
>>> misleading than helpful IMO.
>>
>> In general, they could do something (e.g. `noreturn`), perhaps in the future.
>
> Indeed, and I think once they're honored we should add them again.
That sounds like it will be a lot of work, going through every function
and checking if it has the given attribute. Especially when the
attributes are enabled one by one. I think we should keep them (and of
course introduce them on new functions).
---
Cheers,
Benno
> It's just that I think as long as compiler attributes aren't honored, we should
> not have them in the first place to avoid confusion about whether they do or do
> not have any effect.
^ permalink raw reply [flat|nested] 95+ messages in thread
* Re: [PATCH v5 04/26] rust: alloc: implement `Allocator` for `Kmalloc`
2024-08-14 15:28 ` Benno Lossin
@ 2024-08-14 16:01 ` Danilo Krummrich
0 siblings, 0 replies; 95+ messages in thread
From: Danilo Krummrich @ 2024-08-14 16:01 UTC (permalink / raw)
To: Benno Lossin
Cc: Miguel Ojeda, Alice Ryhl, ojeda, alex.gaynor, wedsonaf,
boqun.feng, gary, bjorn3_gh, a.hindborg, akpm, daniel.almeida,
faith.ekstrand, boris.brezillon, lina, mcanal, zhiw, cjia,
jhubbard, airlied, ajanulgu, lyude, linux-kernel, rust-for-linux,
linux-mm
On Wed, Aug 14, 2024 at 03:28:10PM +0000, Benno Lossin wrote:
> On 14.08.24 17:19, Danilo Krummrich wrote:
> > On Wed, Aug 14, 2024 at 05:03:21PM +0200, Miguel Ojeda wrote:
> >> On Wed, Aug 14, 2024 at 4:00 PM Danilo Krummrich <dakr@kernel.org> wrote:
> >>>
> >>> If we keep them, we'd consequently also need to add them for vrealloc() and
> >>> kvrealloc(). But again, they don't do anything for us, and hence are more
> >>> misleading than helpful IMO.
> >>
> >> In general, they could do something (e.g. `noreturn`), perhaps in the future.
> >
> > Indeed, and I think once they're honored we should add them again.
>
> That sounds like it will be a lot of work, going through every function
> and checking if it has the given attribute. Especially when the
> attributes are enabled one by one. I think we should keep them (and of
> course introduce them on new functions).
I don't think it's gonna be a lot of work, if they're honored one day, which we
don't know, do we?
Since it seems that everyone else prefers to have those attributes, I'll keep /
add them accordingly.
However, I think we should at least keep a comment in rust/helpers.c that
documents which attributes are honored by bindgen and which aren't. For now,
the comment should probably say that non of them are honored?
>
> ---
> Cheers,
> Benno
>
> > It's just that I think as long as compiler attributes aren't honored, we should
> > not have them in the first place to avoid confusion about whether they do or do
> > not have any effect.
>
^ permalink raw reply [flat|nested] 95+ messages in thread
* Re: [PATCH v5 04/26] rust: alloc: implement `Allocator` for `Kmalloc`
2024-08-14 15:19 ` Danilo Krummrich
2024-08-14 15:28 ` Benno Lossin
@ 2024-08-14 16:02 ` Miguel Ojeda
2024-08-14 16:16 ` Miguel Ojeda
1 sibling, 1 reply; 95+ messages in thread
From: Miguel Ojeda @ 2024-08-14 16:02 UTC (permalink / raw)
To: Danilo Krummrich
Cc: Alice Ryhl, ojeda, alex.gaynor, wedsonaf, boqun.feng, gary,
bjorn3_gh, benno.lossin, a.hindborg, akpm, daniel.almeida,
faith.ekstrand, boris.brezillon, lina, mcanal, zhiw, cjia,
jhubbard, airlied, ajanulgu, lyude, linux-kernel, rust-for-linux,
linux-mm
On Wed, Aug 14, 2024 at 5:20 PM Danilo Krummrich <dakr@kernel.org> wrote:
>
> Indeed, and I think once they're honored we should add them again.
>
> It's just that I think as long as compiler attributes aren't honored, we should
> not have them in the first place to avoid confusion about whether they do or do
> not have any effect.
In the C side, many attributes are not honored anyway, depending on
the compiler (or tool like sparse), compiler version, etc. So it would
be "OK" in that sense (even if here we may know there is no C caller).
> It's not so much that I want to remove them for krealloc(), it's that I don't
> want to intentionally add them for the vrealloc() and kvrealloc() helpers,
> knowing that they don't do anything (yet).
One can think of them as "documentation", e.g. seeing `__must_check`
tells you something about the function, so I don't think it would be
bad to add them, if they are not too much work to maintain.
I checked about `__must_check`, because it would be nice if it is used
by `bindgen`, and it turns out it already does, but behind
`--enable-function-attribute-detection` (apparently for performance
reasons):
int f(void);
__attribute__((warn_unused_result)) int g(void);
gives:
extern "C" {
pub fn f() -> ::std::os::raw::c_int;
}
extern "C" {
#[must_use]
pub fn g() -> ::std::os::raw::c_int;
}
So that one should be kept at the very least, and it is a good example
of potential improvements later on based on these attributes.
In general, I think it is not a bad idea to write this file like we
would write other C files, even if it is not a regular C file. And
adding more information to signatures is, after all, part of what we
do with Rust overall!
Cheers,
Miguel
^ permalink raw reply [flat|nested] 95+ messages in thread* Re: [PATCH v5 04/26] rust: alloc: implement `Allocator` for `Kmalloc`
2024-08-14 16:02 ` Miguel Ojeda
@ 2024-08-14 16:16 ` Miguel Ojeda
2024-08-14 16:56 ` Danilo Krummrich
0 siblings, 1 reply; 95+ messages in thread
From: Miguel Ojeda @ 2024-08-14 16:16 UTC (permalink / raw)
To: Danilo Krummrich
Cc: Alice Ryhl, ojeda, alex.gaynor, wedsonaf, boqun.feng, gary,
bjorn3_gh, benno.lossin, a.hindborg, akpm, daniel.almeida,
faith.ekstrand, boris.brezillon, lina, mcanal, zhiw, cjia,
jhubbard, airlied, ajanulgu, lyude, linux-kernel, rust-for-linux,
linux-mm
On Wed, Aug 14, 2024 at 6:02 PM Miguel Ojeda
<miguel.ojeda.sandonis@gmail.com> wrote:
>
> I checked about `__must_check`, because it would be nice if it is used
> by `bindgen`, and it turns out it already does, but behind
> `--enable-function-attribute-detection` (apparently for performance
> reasons):
I just tried, and the flag seems to work, gives us a bunch of
`#[must_use]`s which are nice, and apparently no other change (in
usual x86_64 config at least).
I don't notice any significant performance difference in our case, so
I will send a quick patch to see if others find an issue with it.
Cheers,
Miguel
^ permalink raw reply [flat|nested] 95+ messages in thread
* Re: [PATCH v5 04/26] rust: alloc: implement `Allocator` for `Kmalloc`
2024-08-14 16:16 ` Miguel Ojeda
@ 2024-08-14 16:56 ` Danilo Krummrich
0 siblings, 0 replies; 95+ messages in thread
From: Danilo Krummrich @ 2024-08-14 16:56 UTC (permalink / raw)
To: Miguel Ojeda
Cc: Alice Ryhl, ojeda, alex.gaynor, wedsonaf, boqun.feng, gary,
bjorn3_gh, benno.lossin, a.hindborg, akpm, daniel.almeida,
faith.ekstrand, boris.brezillon, lina, mcanal, zhiw, cjia,
jhubbard, airlied, ajanulgu, lyude, linux-kernel, rust-for-linux,
linux-mm
On Wed, Aug 14, 2024 at 06:16:34PM +0200, Miguel Ojeda wrote:
> On Wed, Aug 14, 2024 at 6:02 PM Miguel Ojeda
> <miguel.ojeda.sandonis@gmail.com> wrote:
> >
> > I checked about `__must_check`, because it would be nice if it is used
> > by `bindgen`, and it turns out it already does, but behind
> > `--enable-function-attribute-detection` (apparently for performance
> > reasons):
>
> I just tried, and the flag seems to work, gives us a bunch of
> `#[must_use]`s which are nice, and apparently no other change (in
> usual x86_64 config at least).
Cool! That's even better then.
>
> I don't notice any significant performance difference in our case, so
> I will send a quick patch to see if others find an issue with it.
>
> Cheers,
> Miguel
>
^ permalink raw reply [flat|nested] 95+ messages in thread
* Re: [PATCH v5 04/26] rust: alloc: implement `Allocator` for `Kmalloc`
2024-08-12 18:22 ` [PATCH v5 04/26] rust: alloc: implement `Allocator` for `Kmalloc` Danilo Krummrich
2024-08-14 7:51 ` Alice Ryhl
@ 2024-08-14 16:21 ` Benno Lossin
2024-08-14 16:59 ` Danilo Krummrich
2024-08-14 16:28 ` Benno Lossin
2 siblings, 1 reply; 95+ messages in thread
From: Benno Lossin @ 2024-08-14 16:21 UTC (permalink / raw)
To: Danilo Krummrich, ojeda, alex.gaynor, wedsonaf, boqun.feng, gary,
bjorn3_gh, a.hindborg, aliceryhl, akpm
Cc: daniel.almeida, faith.ekstrand, boris.brezillon, lina, mcanal,
zhiw, cjia, jhubbard, airlied, ajanulgu, lyude, linux-kernel,
rust-for-linux, linux-mm
On 12.08.24 20:22, Danilo Krummrich wrote:
> + /// # Safety
> + ///
> + /// This method has the same safety requirements as `Allocator::realloc`.
Please make this a link.
> + unsafe fn call(
> + &self,
> + ptr: Option<NonNull<u8>>,
> + layout: Layout,
> + flags: Flags,
> + ) -> Result<NonNull<[u8]>, AllocError> {
> + let size = aligned_size(layout);
> + let ptr = match ptr {
> + Some(ptr) => ptr.as_ptr(),
> + None => ptr::null(),
> + };
> +
> + // SAFETY: `ptr` is either NULL or valid by the safety requirements of this function.
> + let raw_ptr = unsafe {
> + // If `size == 0` and `ptr != NULL` the memory behind the pointer is freed.
> + self.0(ptr.cast(), size, flags.0).cast()
> + };
> +
> + let ptr = if size == 0 {
Why do you do this check *after* calling `self.0`?
---
Cheers,
Benno
> + NonNull::dangling()
> + } else {
> + NonNull::new(raw_ptr).ok_or(AllocError)?
> + };
> +
> + Ok(NonNull::slice_from_raw_parts(ptr, size))
> + }
> +}
^ permalink raw reply [flat|nested] 95+ messages in thread* Re: [PATCH v5 04/26] rust: alloc: implement `Allocator` for `Kmalloc`
2024-08-14 16:21 ` Benno Lossin
@ 2024-08-14 16:59 ` Danilo Krummrich
2024-08-14 17:02 ` Benno Lossin
0 siblings, 1 reply; 95+ messages in thread
From: Danilo Krummrich @ 2024-08-14 16:59 UTC (permalink / raw)
To: Benno Lossin
Cc: ojeda, alex.gaynor, wedsonaf, boqun.feng, gary, bjorn3_gh,
a.hindborg, aliceryhl, akpm, daniel.almeida, faith.ekstrand,
boris.brezillon, lina, mcanal, zhiw, cjia, jhubbard, airlied,
ajanulgu, lyude, linux-kernel, rust-for-linux, linux-mm
On Wed, Aug 14, 2024 at 04:21:38PM +0000, Benno Lossin wrote:
> On 12.08.24 20:22, Danilo Krummrich wrote:
> > + /// # Safety
> > + ///
> > + /// This method has the same safety requirements as `Allocator::realloc`.
>
> Please make this a link.
Sure.
>
> > + unsafe fn call(
> > + &self,
> > + ptr: Option<NonNull<u8>>,
> > + layout: Layout,
> > + flags: Flags,
> > + ) -> Result<NonNull<[u8]>, AllocError> {
> > + let size = aligned_size(layout);
> > + let ptr = match ptr {
> > + Some(ptr) => ptr.as_ptr(),
> > + None => ptr::null(),
> > + };
> > +
> > + // SAFETY: `ptr` is either NULL or valid by the safety requirements of this function.
> > + let raw_ptr = unsafe {
> > + // If `size == 0` and `ptr != NULL` the memory behind the pointer is freed.
> > + self.0(ptr.cast(), size, flags.0).cast()
> > + };
> > +
> > + let ptr = if size == 0 {
>
> Why do you do this check *after* calling `self.0`?
Because I need `raw_ptr` in the else case below.
>
> ---
> Cheers,
> Benno
>
> > + NonNull::dangling()
> > + } else {
> > + NonNull::new(raw_ptr).ok_or(AllocError)?
> > + };
> > +
> > + Ok(NonNull::slice_from_raw_parts(ptr, size))
> > + }
> > +}
>
^ permalink raw reply [flat|nested] 95+ messages in thread* Re: [PATCH v5 04/26] rust: alloc: implement `Allocator` for `Kmalloc`
2024-08-14 16:59 ` Danilo Krummrich
@ 2024-08-14 17:02 ` Benno Lossin
2024-08-14 17:15 ` Danilo Krummrich
0 siblings, 1 reply; 95+ messages in thread
From: Benno Lossin @ 2024-08-14 17: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, cjia, jhubbard, airlied,
ajanulgu, lyude, linux-kernel, rust-for-linux, linux-mm
On 14.08.24 18:59, Danilo Krummrich wrote:
> On Wed, Aug 14, 2024 at 04:21:38PM +0000, Benno Lossin wrote:
>> On 12.08.24 20:22, Danilo Krummrich wrote:
>>> + unsafe fn call(
>>> + &self,
>>> + ptr: Option<NonNull<u8>>,
>>> + layout: Layout,
>>> + flags: Flags,
>>> + ) -> Result<NonNull<[u8]>, AllocError> {
>>> + let size = aligned_size(layout);
>>> + let ptr = match ptr {
>>> + Some(ptr) => ptr.as_ptr(),
>>> + None => ptr::null(),
>>> + };
>>> +
>>> + // SAFETY: `ptr` is either NULL or valid by the safety requirements of this function.
>>> + let raw_ptr = unsafe {
>>> + // If `size == 0` and `ptr != NULL` the memory behind the pointer is freed.
>>> + self.0(ptr.cast(), size, flags.0).cast()
>>> + };
>>> +
>>> + let ptr = if size == 0 {
>>
>> Why do you do this check *after* calling `self.0`?
>
> Because I need `raw_ptr` in the else case below.
But you can just return early above? I would prefer the check be done
before `self.0` is called.
---
Cheers,
Benno
^ permalink raw reply [flat|nested] 95+ messages in thread* Re: [PATCH v5 04/26] rust: alloc: implement `Allocator` for `Kmalloc`
2024-08-14 17:02 ` Benno Lossin
@ 2024-08-14 17:15 ` Danilo Krummrich
2024-08-14 21:07 ` Benno Lossin
0 siblings, 1 reply; 95+ messages in thread
From: Danilo Krummrich @ 2024-08-14 17:15 UTC (permalink / raw)
To: Benno Lossin
Cc: ojeda, alex.gaynor, wedsonaf, boqun.feng, gary, bjorn3_gh,
a.hindborg, aliceryhl, akpm, daniel.almeida, faith.ekstrand,
boris.brezillon, lina, mcanal, zhiw, cjia, jhubbard, airlied,
ajanulgu, lyude, linux-kernel, rust-for-linux, linux-mm
On Wed, Aug 14, 2024 at 05:02:53PM +0000, Benno Lossin wrote:
> On 14.08.24 18:59, Danilo Krummrich wrote:
> > On Wed, Aug 14, 2024 at 04:21:38PM +0000, Benno Lossin wrote:
> >> On 12.08.24 20:22, Danilo Krummrich wrote:
> >>> + unsafe fn call(
> >>> + &self,
> >>> + ptr: Option<NonNull<u8>>,
> >>> + layout: Layout,
> >>> + flags: Flags,
> >>> + ) -> Result<NonNull<[u8]>, AllocError> {
> >>> + let size = aligned_size(layout);
> >>> + let ptr = match ptr {
> >>> + Some(ptr) => ptr.as_ptr(),
> >>> + None => ptr::null(),
> >>> + };
> >>> +
> >>> + // SAFETY: `ptr` is either NULL or valid by the safety requirements of this function.
> >>> + let raw_ptr = unsafe {
> >>> + // If `size == 0` and `ptr != NULL` the memory behind the pointer is freed.
> >>> + self.0(ptr.cast(), size, flags.0).cast()
> >>> + };
> >>> +
> >>> + let ptr = if size == 0 {
> >>
> >> Why do you do this check *after* calling `self.0`?
> >
> > Because I need `raw_ptr` in the else case below.
>
> But you can just return early above? I would prefer the check be done
> before `self.0` is called.
No, we can't return early, we need `self.0` to be called, because if `size == 0`
we free the given allocation, if any.
>
> ---
> Cheers,
> Benno
>
^ permalink raw reply [flat|nested] 95+ messages in thread* Re: [PATCH v5 04/26] rust: alloc: implement `Allocator` for `Kmalloc`
2024-08-14 17:15 ` Danilo Krummrich
@ 2024-08-14 21:07 ` Benno Lossin
0 siblings, 0 replies; 95+ messages in thread
From: Benno Lossin @ 2024-08-14 21:07 UTC (permalink / raw)
To: Danilo Krummrich
Cc: ojeda, alex.gaynor, wedsonaf, boqun.feng, gary, bjorn3_gh,
a.hindborg, aliceryhl, akpm, daniel.almeida, faith.ekstrand,
boris.brezillon, lina, mcanal, zhiw, cjia, jhubbard, airlied,
ajanulgu, lyude, linux-kernel, rust-for-linux, linux-mm
On 14.08.24 19:15, Danilo Krummrich wrote:
> On Wed, Aug 14, 2024 at 05:02:53PM +0000, Benno Lossin wrote:
>> On 14.08.24 18:59, Danilo Krummrich wrote:
>>> On Wed, Aug 14, 2024 at 04:21:38PM +0000, Benno Lossin wrote:
>>>> On 12.08.24 20:22, Danilo Krummrich wrote:
>>>>> + unsafe fn call(
>>>>> + &self,
>>>>> + ptr: Option<NonNull<u8>>,
>>>>> + layout: Layout,
>>>>> + flags: Flags,
>>>>> + ) -> Result<NonNull<[u8]>, AllocError> {
>>>>> + let size = aligned_size(layout);
>>>>> + let ptr = match ptr {
>>>>> + Some(ptr) => ptr.as_ptr(),
>>>>> + None => ptr::null(),
>>>>> + };
>>>>> +
>>>>> + // SAFETY: `ptr` is either NULL or valid by the safety requirements of this function.
>>>>> + let raw_ptr = unsafe {
>>>>> + // If `size == 0` and `ptr != NULL` the memory behind the pointer is freed.
>>>>> + self.0(ptr.cast(), size, flags.0).cast()
>>>>> + };
>>>>> +
>>>>> + let ptr = if size == 0 {
>>>>
>>>> Why do you do this check *after* calling `self.0`?
>>>
>>> Because I need `raw_ptr` in the else case below.
>>
>> But you can just return early above? I would prefer the check be done
>> before `self.0` is called.
>
> No, we can't return early, we need `self.0` to be called, because if `size == 0`
> we free the given allocation, if any.
Oh yeah of course... I don't know how I arrived at that comment...
---
Cheers,
Benno
^ permalink raw reply [flat|nested] 95+ messages in thread
* Re: [PATCH v5 04/26] rust: alloc: implement `Allocator` for `Kmalloc`
2024-08-12 18:22 ` [PATCH v5 04/26] rust: alloc: implement `Allocator` for `Kmalloc` Danilo Krummrich
2024-08-14 7:51 ` Alice Ryhl
2024-08-14 16:21 ` Benno Lossin
@ 2024-08-14 16:28 ` Benno Lossin
2024-08-14 17:13 ` Danilo Krummrich
2 siblings, 1 reply; 95+ messages in thread
From: Benno Lossin @ 2024-08-14 16:28 UTC (permalink / raw)
To: Danilo Krummrich, ojeda, alex.gaynor, wedsonaf, boqun.feng, gary,
bjorn3_gh, a.hindborg, aliceryhl, akpm
Cc: daniel.almeida, faith.ekstrand, boris.brezillon, lina, mcanal,
zhiw, cjia, jhubbard, airlied, ajanulgu, lyude, linux-kernel,
rust-for-linux, linux-mm
On 12.08.24 20:22, Danilo Krummrich wrote:
> +unsafe impl Allocator for Kmalloc {
There is a missing SAFETY comment here (and also for Vmalloc, probably
also for VKmalloc then).
---
Cheers,
Benno
> + unsafe fn realloc(
> + ptr: Option<NonNull<u8>>,
> + layout: Layout,
> + flags: Flags,
> + ) -> Result<NonNull<[u8]>, AllocError> {
> + // SAFETY: `ReallocFunc::call` has the same safety requirements as `Allocator::realloc`.
> + unsafe { ReallocFunc::KREALLOC.call(ptr, layout, flags) }
> + }
> +}
> +
> unsafe impl GlobalAlloc for Kmalloc {
> unsafe fn alloc(&self, layout: Layout) -> *mut u8 {
> // SAFETY: `ptr::null_mut()` is null and `layout` has a non-zero size by the function safety
> --
> 2.45.2
>
^ permalink raw reply [flat|nested] 95+ messages in thread* Re: [PATCH v5 04/26] rust: alloc: implement `Allocator` for `Kmalloc`
2024-08-14 16:28 ` Benno Lossin
@ 2024-08-14 17:13 ` Danilo Krummrich
2024-08-14 21:10 ` Benno Lossin
0 siblings, 1 reply; 95+ messages in thread
From: Danilo Krummrich @ 2024-08-14 17: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, cjia, jhubbard, airlied,
ajanulgu, lyude, linux-kernel, rust-for-linux, linux-mm
On Wed, Aug 14, 2024 at 04:28:04PM +0000, Benno Lossin wrote:
> On 12.08.24 20:22, Danilo Krummrich wrote:
> > +unsafe impl Allocator for Kmalloc {
>
> There is a missing SAFETY comment here (and also for Vmalloc, probably
> also for VKmalloc then).
Any suggestion on what to write here?
I'd probably come up with something like:
--
Memory returned from `Kmalloc` remains valid until explicitly freed.
It is valid to pass any pointer to an allocated memory buffer obtained with any
function of `Kmalloc` to any other function of `Kmalloc`.
If `Kmalloc::realloc` is called with a size of zero, the given memory
allocation, if any, is freed.
If `Kmalloc::realloc` is called with `None` it behaves like `Kmalloc::alloc`,
i.e. a new memory allocation is created.
--
and repeat that for `Vmalloc` and `KVmalloc`.
I'm not sure how useful that is though.
>
> ---
> Cheers,
> Benno
>
> > + unsafe fn realloc(
> > + ptr: Option<NonNull<u8>>,
> > + layout: Layout,
> > + flags: Flags,
> > + ) -> Result<NonNull<[u8]>, AllocError> {
> > + // SAFETY: `ReallocFunc::call` has the same safety requirements as `Allocator::realloc`.
> > + unsafe { ReallocFunc::KREALLOC.call(ptr, layout, flags) }
> > + }
> > +}
> > +
> > unsafe impl GlobalAlloc for Kmalloc {
> > unsafe fn alloc(&self, layout: Layout) -> *mut u8 {
> > // SAFETY: `ptr::null_mut()` is null and `layout` has a non-zero size by the function safety
> > --
> > 2.45.2
> >
>
^ permalink raw reply [flat|nested] 95+ messages in thread* Re: [PATCH v5 04/26] rust: alloc: implement `Allocator` for `Kmalloc`
2024-08-14 17:13 ` Danilo Krummrich
@ 2024-08-14 21:10 ` Benno Lossin
0 siblings, 0 replies; 95+ messages in thread
From: Benno Lossin @ 2024-08-14 21:10 UTC (permalink / raw)
To: Danilo Krummrich
Cc: ojeda, alex.gaynor, wedsonaf, boqun.feng, gary, bjorn3_gh,
a.hindborg, aliceryhl, akpm, daniel.almeida, faith.ekstrand,
boris.brezillon, lina, mcanal, zhiw, cjia, jhubbard, airlied,
ajanulgu, lyude, linux-kernel, rust-for-linux, linux-mm
On 14.08.24 19:13, Danilo Krummrich wrote:
> On Wed, Aug 14, 2024 at 04:28:04PM +0000, Benno Lossin wrote:
>> On 12.08.24 20:22, Danilo Krummrich wrote:
>>> +unsafe impl Allocator for Kmalloc {
>>
>> There is a missing SAFETY comment here (and also for Vmalloc, probably
>> also for VKmalloc then).
>
> Any suggestion on what to write here?
>
> I'd probably come up with something like:
>
> --
> Memory returned from `Kmalloc` remains valid until explicitly freed.
>
> It is valid to pass any pointer to an allocated memory buffer obtained with any
> function of `Kmalloc` to any other function of `Kmalloc`.
>
> If `Kmalloc::realloc` is called with a size of zero, the given memory
> allocation, if any, is freed.
>
> If `Kmalloc::realloc` is called with `None` it behaves like `Kmalloc::alloc`,
> i.e. a new memory allocation is created.
> --
>
> and repeat that for `Vmalloc` and `KVmalloc`.
>
> I'm not sure how useful that is though.
Ideally you would write down how the guarantees are satisfied in
addition to the invariants. But I guess this is only guaranteed by the
`ReallocFunc::call`, since that handles the ZSTs... Maybe this is a bit
excessive. If I come up with something better I will let you know.
---
Cheers,
Benno
^ permalink raw reply [flat|nested] 95+ messages in thread
* [PATCH v5 05/26] rust: alloc: add module `allocator_test`
2024-08-12 18:22 [PATCH v5 00/26] Generic `Allocator` support for Rust Danilo Krummrich
` (3 preceding siblings ...)
2024-08-12 18:22 ` [PATCH v5 04/26] rust: alloc: implement `Allocator` for `Kmalloc` Danilo Krummrich
@ 2024-08-12 18:22 ` Danilo Krummrich
2024-08-14 16:25 ` Benno Lossin
2024-08-12 18:22 ` [PATCH v5 06/26] rust: alloc: implement `Vmalloc` allocator Danilo Krummrich
` (21 subsequent siblings)
26 siblings, 1 reply; 95+ messages in thread
From: Danilo Krummrich @ 2024-08-12 18:22 UTC (permalink / raw)
To: ojeda, alex.gaynor, wedsonaf, boqun.feng, gary, bjorn3_gh,
benno.lossin, a.hindborg, aliceryhl, akpm
Cc: daniel.almeida, faith.ekstrand, boris.brezillon, lina, mcanal,
zhiw, cjia, jhubbard, airlied, ajanulgu, lyude, linux-kernel,
rust-for-linux, linux-mm, Danilo Krummrich
`Allocator`s, such as `Kmalloc`, will be used by e.g. `Box` and `Vec` in
subsequent patches, and hence this dependency propagates throughout the
whole kernel.
Add the `allocator_test` module that provides an empty implementation
for all `Allocator`s in the kernel, such that we don't break the
`rusttest` make target in subsequent patches.
Reviewed-by: Alice Ryhl <aliceryhl@google.com>
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 b732720cfb95..f0c80ab78602 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] 95+ messages in thread* Re: [PATCH v5 05/26] rust: alloc: add module `allocator_test`
2024-08-12 18:22 ` [PATCH v5 05/26] rust: alloc: add module `allocator_test` Danilo Krummrich
@ 2024-08-14 16:25 ` Benno Lossin
0 siblings, 0 replies; 95+ messages in thread
From: Benno Lossin @ 2024-08-14 16:25 UTC (permalink / raw)
To: Danilo Krummrich, ojeda, alex.gaynor, wedsonaf, boqun.feng, gary,
bjorn3_gh, a.hindborg, aliceryhl, akpm
Cc: daniel.almeida, faith.ekstrand, boris.brezillon, lina, mcanal,
zhiw, cjia, jhubbard, airlied, ajanulgu, lyude, linux-kernel,
rust-for-linux, linux-mm
On 12.08.24 20:22, 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.
>
> 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
Reviewed-by: Benno Lossin <benno.lossin@proton.me>
---
Cheers,
Benno
^ permalink raw reply [flat|nested] 95+ messages in thread
* [PATCH v5 06/26] rust: alloc: implement `Vmalloc` allocator
2024-08-12 18:22 [PATCH v5 00/26] Generic `Allocator` support for Rust Danilo Krummrich
` (4 preceding siblings ...)
2024-08-12 18:22 ` [PATCH v5 05/26] rust: alloc: add module `allocator_test` Danilo Krummrich
@ 2024-08-12 18:22 ` Danilo Krummrich
2024-08-14 16:32 ` Benno Lossin
2024-08-12 18:22 ` [PATCH v5 07/26] rust: alloc: implement `KVmalloc` allocator Danilo Krummrich
` (20 subsequent siblings)
26 siblings, 1 reply; 95+ messages in thread
From: Danilo Krummrich @ 2024-08-12 18:22 UTC (permalink / raw)
To: ojeda, alex.gaynor, wedsonaf, boqun.feng, gary, bjorn3_gh,
benno.lossin, a.hindborg, aliceryhl, akpm
Cc: daniel.almeida, faith.ekstrand, boris.brezillon, lina, mcanal,
zhiw, cjia, jhubbard, airlied, ajanulgu, lyude, linux-kernel,
rust-for-linux, linux-mm, Danilo Krummrich
Implement `Allocator` for `Vmalloc`, the kernel's virtually contiguous
allocator, typically used for larger objects, (much) larger than page
size.
All memory allocations made with `Vmalloc` end up in `vrealloc()`.
Reviewed-by: Alice Ryhl <aliceryhl@google.com>
Signed-off-by: Danilo Krummrich <dakr@kernel.org>
---
rust/helpers.c | 7 +++++++
rust/kernel/alloc/allocator.rs | 28 ++++++++++++++++++++++++++++
rust/kernel/alloc/allocator_test.rs | 1 +
3 files changed, 36 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 b46883d87715..fdda22c6983f 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.
@@ -55,6 +62,9 @@ impl ReallocFunc {
// INVARIANT: `krealloc` satisfies the type invariants.
const KREALLOC: Self = Self(bindings::krealloc);
+ // INVARIANT: `vrealloc` satisfies the type invariants.
+ const VREALLOC: Self = Self(bindings::vrealloc);
+
/// # Safety
///
/// This method has the same safety requirements as `Allocator::realloc`.
@@ -132,6 +142,24 @@ unsafe fn alloc_zeroed(&self, layout: Layout) -> *mut u8 {
}
}
+unsafe impl Allocator for Vmalloc {
+ unsafe fn realloc(
+ ptr: Option<NonNull<u8>>,
+ layout: Layout,
+ flags: Flags,
+ ) -> Result<NonNull<[u8]>, AllocError> {
+ // TODO: Support alignments larger than PAGE_SIZE.
+ if layout.align() > bindings::PAGE_SIZE {
+ pr_warn!("Vmalloc does not support alignments larger than PAGE_SIZE yet.\n");
+ return Err(AllocError);
+ }
+
+ // SAFETY: If not `None`, `ptr` is guaranteed to point to valid memory, which was previously
+ // allocated with this `Allocator`.
+ unsafe { ReallocFunc::VREALLOC.call(ptr, layout, flags) }
+ }
+}
+
#[global_allocator]
static ALLOCATOR: Kmalloc = Kmalloc;
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] 95+ messages in thread* Re: [PATCH v5 06/26] rust: alloc: implement `Vmalloc` allocator
2024-08-12 18:22 ` [PATCH v5 06/26] rust: alloc: implement `Vmalloc` allocator Danilo Krummrich
@ 2024-08-14 16:32 ` Benno Lossin
2024-08-14 22:12 ` Danilo Krummrich
0 siblings, 1 reply; 95+ messages in thread
From: Benno Lossin @ 2024-08-14 16:32 UTC (permalink / raw)
To: Danilo Krummrich, ojeda, alex.gaynor, wedsonaf, boqun.feng, gary,
bjorn3_gh, a.hindborg, aliceryhl, akpm
Cc: daniel.almeida, faith.ekstrand, boris.brezillon, lina, mcanal,
zhiw, cjia, jhubbard, airlied, ajanulgu, lyude, linux-kernel,
rust-for-linux, linux-mm
On 12.08.24 20:22, 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 | 28 ++++++++++++++++++++++++++++
> rust/kernel/alloc/allocator_test.rs | 1 +
> 3 files changed, 36 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 b46883d87715..fdda22c6983f 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.
> @@ -55,6 +62,9 @@ impl ReallocFunc {
> // INVARIANT: `krealloc` satisfies the type invariants.
> const KREALLOC: Self = Self(bindings::krealloc);
>
> + // INVARIANT: `vrealloc` satisfies the type invariants.
> + const VREALLOC: Self = Self(bindings::vrealloc);
> +
> /// # Safety
> ///
> /// This method has the same safety requirements as `Allocator::realloc`.
> @@ -132,6 +142,24 @@ unsafe fn alloc_zeroed(&self, layout: Layout) -> *mut u8 {
> }
> }
>
> +unsafe impl Allocator for Vmalloc {
Missing SAFETY comment.
> + unsafe fn realloc(
Does this need `#[inline]`?
> + ptr: Option<NonNull<u8>>,
> + layout: Layout,
> + flags: Flags,
> + ) -> Result<NonNull<[u8]>, AllocError> {
> + // TODO: Support alignments larger than PAGE_SIZE.
> + if layout.align() > bindings::PAGE_SIZE {
> + pr_warn!("Vmalloc does not support alignments larger than PAGE_SIZE yet.\n");
> + return Err(AllocError);
I think here we should first try to use `build_error!`, most often the
alignment will be specified statically, so it should get optimized away.
How difficult will it be to support this? (it is a weird requirement,
but I dislike just returning an error...)
---
Cheers,
Benno
> + }
> +
> + // SAFETY: If not `None`, `ptr` is guaranteed to point to valid memory, which was previously
> + // allocated with this `Allocator`.
> + unsafe { ReallocFunc::VREALLOC.call(ptr, layout, flags) }
> + }
> +}
> +
> #[global_allocator]
> static ALLOCATOR: Kmalloc = Kmalloc;
>
> diff --git a/rust/kernel/alloc/allocator_test.rs b/rust/kernel/alloc/allocator_test.rs
> index 4785efc474a7..e7bf2982f68f 100644
> --- a/rust/kernel/alloc/allocator_test.rs
> +++ b/rust/kernel/alloc/allocator_test.rs
> @@ -7,6 +7,7 @@
> use core::ptr::NonNull;
>
> pub struct Kmalloc;
> +pub type Vmalloc = Kmalloc;
>
> unsafe impl Allocator for Kmalloc {
> unsafe fn realloc(
> --
> 2.45.2
>
^ permalink raw reply [flat|nested] 95+ messages in thread* Re: [PATCH v5 06/26] rust: alloc: implement `Vmalloc` allocator
2024-08-14 16:32 ` Benno Lossin
@ 2024-08-14 22:12 ` Danilo Krummrich
2024-08-14 23:20 ` Danilo Krummrich
0 siblings, 1 reply; 95+ messages in thread
From: Danilo Krummrich @ 2024-08-14 22: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, cjia, jhubbard, airlied,
ajanulgu, lyude, linux-kernel, rust-for-linux, linux-mm
On Wed, Aug 14, 2024 at 04:32:34PM +0000, Benno Lossin wrote:
> On 12.08.24 20:22, 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 | 28 ++++++++++++++++++++++++++++
> > rust/kernel/alloc/allocator_test.rs | 1 +
> > 3 files changed, 36 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 b46883d87715..fdda22c6983f 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.
> > @@ -55,6 +62,9 @@ impl ReallocFunc {
> > // INVARIANT: `krealloc` satisfies the type invariants.
> > const KREALLOC: Self = Self(bindings::krealloc);
> >
> > + // INVARIANT: `vrealloc` satisfies the type invariants.
> > + const VREALLOC: Self = Self(bindings::vrealloc);
> > +
> > /// # Safety
> > ///
> > /// This method has the same safety requirements as `Allocator::realloc`.
> > @@ -132,6 +142,24 @@ unsafe fn alloc_zeroed(&self, layout: Layout) -> *mut u8 {
> > }
> > }
> >
> > +unsafe impl Allocator for Vmalloc {
>
> Missing SAFETY comment.
>
> > + unsafe fn realloc(
>
> Does this need `#[inline]`?
Given that we almost only call `ReallocFunc::VREALLOC.call`, inlining this seems
reasonable.
>
> > + ptr: Option<NonNull<u8>>,
> > + layout: Layout,
> > + flags: Flags,
> > + ) -> Result<NonNull<[u8]>, AllocError> {
> > + // TODO: Support alignments larger than PAGE_SIZE.
> > + if layout.align() > bindings::PAGE_SIZE {
> > + pr_warn!("Vmalloc does not support alignments larger than PAGE_SIZE yet.\n");
> > + return Err(AllocError);
>
> I think here we should first try to use `build_error!`, most often the
> alignment will be specified statically, so it should get optimized away.
Sure, we can try that first.
>
> How difficult will it be to support this? (it is a weird requirement,
> but I dislike just returning an error...)
It's not difficult to support at all. But it requires a C API taking an
alignment argument (same for `KVmalloc`).
Coming up with a vrealloc_aligned() is rather trivial. kvrealloc_aligned() would
be a bit weird though, because the alignment argument could only be really
honored if we run into the vrealloc() case. For the krealloc() case it'd still
depend on the bucket size that is selected for the requested size.
Adding the C API, I'm also pretty sure someone's gonna ask what we need an
alignment larger than PAGE_SIZE for and if we have a real use case for that.
I'm not entirely sure we have a reasonable answer for that.
I got some hacked up patches for that, but I'd rather polish and send them once
we actually need it.
>
> ---
> Cheers,
> Benno
>
> > + }
> > +
> > + // SAFETY: If not `None`, `ptr` is guaranteed to point to valid memory, which was previously
> > + // allocated with this `Allocator`.
> > + unsafe { ReallocFunc::VREALLOC.call(ptr, layout, flags) }
> > + }
> > +}
> > +
> > #[global_allocator]
> > static ALLOCATOR: Kmalloc = Kmalloc;
> >
> > diff --git a/rust/kernel/alloc/allocator_test.rs b/rust/kernel/alloc/allocator_test.rs
> > index 4785efc474a7..e7bf2982f68f 100644
> > --- a/rust/kernel/alloc/allocator_test.rs
> > +++ b/rust/kernel/alloc/allocator_test.rs
> > @@ -7,6 +7,7 @@
> > use core::ptr::NonNull;
> >
> > pub struct Kmalloc;
> > +pub type Vmalloc = Kmalloc;
> >
> > unsafe impl Allocator for Kmalloc {
> > unsafe fn realloc(
> > --
> > 2.45.2
> >
>
^ permalink raw reply [flat|nested] 95+ messages in thread* Re: [PATCH v5 06/26] rust: alloc: implement `Vmalloc` allocator
2024-08-14 22:12 ` Danilo Krummrich
@ 2024-08-14 23:20 ` Danilo Krummrich
2024-08-15 6:48 ` Benno Lossin
0 siblings, 1 reply; 95+ messages in thread
From: Danilo Krummrich @ 2024-08-14 23:20 UTC (permalink / raw)
To: Benno Lossin
Cc: ojeda, alex.gaynor, wedsonaf, boqun.feng, gary, bjorn3_gh,
a.hindborg, aliceryhl, akpm, daniel.almeida, faith.ekstrand,
boris.brezillon, lina, mcanal, zhiw, cjia, jhubbard, airlied,
ajanulgu, lyude, linux-kernel, rust-for-linux, linux-mm
On Thu, Aug 15, 2024 at 12:13:06AM +0200, Danilo Krummrich wrote:
>
> >
> > > + ptr: Option<NonNull<u8>>,
> > > + layout: Layout,
> > > + flags: Flags,
> > > + ) -> Result<NonNull<[u8]>, AllocError> {
> > > + // TODO: Support alignments larger than PAGE_SIZE.
> > > + if layout.align() > bindings::PAGE_SIZE {
> > > + pr_warn!("Vmalloc does not support alignments larger than PAGE_SIZE yet.\n");
> > > + return Err(AllocError);
> >
> > I think here we should first try to use `build_error!`, most often the
> > alignment will be specified statically, so it should get optimized away.
>
> Sure, we can try that first.
I think I spoke too soon here. I don't think `build_error!` or `build_assert!`
can work here, it would also fail the build when the compiler doesn't know the
value of the alignment, wouldn't it? I remember that I wasn't overly happy about
failing this on runtime either when I first thought about this case, but I also
couldn't think of something better.
In the end it's rather unlikely to ever hit this case, and probably even more
unlikely to hit it for a sane reason.
>
> >
> > How difficult will it be to support this? (it is a weird requirement,
> > but I dislike just returning an error...)
>
> It's not difficult to support at all. But it requires a C API taking an
> alignment argument (same for `KVmalloc`).
>
> Coming up with a vrealloc_aligned() is rather trivial. kvrealloc_aligned() would
> be a bit weird though, because the alignment argument could only be really
> honored if we run into the vrealloc() case. For the krealloc() case it'd still
> depend on the bucket size that is selected for the requested size.
>
> Adding the C API, I'm also pretty sure someone's gonna ask what we need an
> alignment larger than PAGE_SIZE for and if we have a real use case for that.
> I'm not entirely sure we have a reasonable answer for that.
>
> I got some hacked up patches for that, but I'd rather polish and send them once
> we actually need it.
^ permalink raw reply [flat|nested] 95+ messages in thread* Re: [PATCH v5 06/26] rust: alloc: implement `Vmalloc` allocator
2024-08-14 23:20 ` Danilo Krummrich
@ 2024-08-15 6:48 ` Benno Lossin
2024-08-15 12:29 ` Danilo Krummrich
0 siblings, 1 reply; 95+ messages in thread
From: Benno Lossin @ 2024-08-15 6:48 UTC (permalink / raw)
To: Danilo Krummrich
Cc: ojeda, alex.gaynor, wedsonaf, boqun.feng, gary, bjorn3_gh,
a.hindborg, aliceryhl, akpm, daniel.almeida, faith.ekstrand,
boris.brezillon, lina, mcanal, zhiw, cjia, jhubbard, airlied,
ajanulgu, lyude, linux-kernel, rust-for-linux, linux-mm
On 15.08.24 01:20, Danilo Krummrich wrote:
> On Thu, Aug 15, 2024 at 12:13:06AM +0200, Danilo Krummrich wrote:
>>
>>>
>>>> + ptr: Option<NonNull<u8>>,
>>>> + layout: Layout,
>>>> + flags: Flags,
>>>> + ) -> Result<NonNull<[u8]>, AllocError> {
>>>> + // TODO: Support alignments larger than PAGE_SIZE.
>>>> + if layout.align() > bindings::PAGE_SIZE {
>>>> + pr_warn!("Vmalloc does not support alignments larger than PAGE_SIZE yet.\n");
>>>> + return Err(AllocError);
>>>
>>> I think here we should first try to use `build_error!`, most often the
>>> alignment will be specified statically, so it should get optimized away.
>>
>> Sure, we can try that first.
>
> I think I spoke too soon here. I don't think `build_error!` or `build_assert!`
> can work here, it would also fail the build when the compiler doesn't know the
> value of the alignment, wouldn't it? I remember that I wasn't overly happy about
> failing this on runtime either when I first thought about this case, but I also
> couldn't think of something better.
Yes, it might fail even though the alignment at runtime will be fine.
But that's why I suggested trying `build_error!`(or `build_assert!`)
first, if nobody hits the case where the compiler cannot figure it out,
then we can keep it. If there are instances, where it fails, but the
alignment would be fine at runtime, then we can change it to the above.
(I would add such a comment above the assert).
> In the end it's rather unlikely to ever hit this case, and probably even more
> unlikely to hit it for a sane reason.
Yeah, but I still prefer the build to fail, rather than emitting a warn
message that can be overlooked at runtime.
>>> How difficult will it be to support this? (it is a weird requirement,
>>> but I dislike just returning an error...)
>>
>> It's not difficult to support at all. But it requires a C API taking an
>> alignment argument (same for `KVmalloc`).
I see, that's good to know.
>> Coming up with a vrealloc_aligned() is rather trivial. kvrealloc_aligned() would
>> be a bit weird though, because the alignment argument could only be really
>> honored if we run into the vrealloc() case. For the krealloc() case it'd still
>> depend on the bucket size that is selected for the requested size.
Yeah... Maybe some more logic on the Rust side can help with that.
>> Adding the C API, I'm also pretty sure someone's gonna ask what we need an
>> alignment larger than PAGE_SIZE for and if we have a real use case for that.
>> I'm not entirely sure we have a reasonable answer for that.
We could argue that we can remove an "ugly hack" (when we don't have the
build assert, if we do have that, I don't mind not supporting it), but I
agree that finding a user will be difficult.
>> I got some hacked up patches for that, but I'd rather polish and send them once
>> we actually need it.
Sure, just wanted to check why you don't want to do it this series.
---
Cheers,
Benno
^ permalink raw reply [flat|nested] 95+ messages in thread* Re: [PATCH v5 06/26] rust: alloc: implement `Vmalloc` allocator
2024-08-15 6:48 ` Benno Lossin
@ 2024-08-15 12:29 ` Danilo Krummrich
2024-08-15 13:44 ` Benno Lossin
0 siblings, 1 reply; 95+ messages in thread
From: Danilo Krummrich @ 2024-08-15 12:29 UTC (permalink / raw)
To: Benno Lossin
Cc: ojeda, alex.gaynor, wedsonaf, boqun.feng, gary, bjorn3_gh,
a.hindborg, aliceryhl, akpm, daniel.almeida, faith.ekstrand,
boris.brezillon, lina, mcanal, zhiw, cjia, jhubbard, airlied,
ajanulgu, lyude, linux-kernel, rust-for-linux, linux-mm
On Thu, Aug 15, 2024 at 06:48:19AM +0000, Benno Lossin wrote:
> On 15.08.24 01:20, Danilo Krummrich wrote:
> > On Thu, Aug 15, 2024 at 12:13:06AM +0200, Danilo Krummrich wrote:
> >>
> >>>
> >>>> + ptr: Option<NonNull<u8>>,
> >>>> + layout: Layout,
> >>>> + flags: Flags,
> >>>> + ) -> Result<NonNull<[u8]>, AllocError> {
> >>>> + // TODO: Support alignments larger than PAGE_SIZE.
> >>>> + if layout.align() > bindings::PAGE_SIZE {
> >>>> + pr_warn!("Vmalloc does not support alignments larger than PAGE_SIZE yet.\n");
> >>>> + return Err(AllocError);
> >>>
> >>> I think here we should first try to use `build_error!`, most often the
> >>> alignment will be specified statically, so it should get optimized away.
> >>
> >> Sure, we can try that first.
> >
> > I think I spoke too soon here. I don't think `build_error!` or `build_assert!`
> > can work here, it would also fail the build when the compiler doesn't know the
> > value of the alignment, wouldn't it? I remember that I wasn't overly happy about
> > failing this on runtime either when I first thought about this case, but I also
> > couldn't think of something better.
>
> Yes, it might fail even though the alignment at runtime will be fine.
> But that's why I suggested trying `build_error!`(or `build_assert!`)
> first, if nobody hits the case where the compiler cannot figure it out,
> then we can keep it. If there are instances, where it fails, but the
> alignment would be fine at runtime, then we can change it to the above.
> (I would add such a comment above the assert).
Unfortunately, it already does fail with just the test cases.
Anyway, even if it would have been fine, I don't think it would have been nice
for a future user to run into a build error even though the alignment is
perfectlly within bounds.
>
> > In the end it's rather unlikely to ever hit this case, and probably even more
> > unlikely to hit it for a sane reason.
>
> Yeah, but I still prefer the build to fail, rather than emitting a warn
> message that can be overlooked at runtime.
>
> >>> How difficult will it be to support this? (it is a weird requirement,
> >>> but I dislike just returning an error...)
> >>
> >> It's not difficult to support at all. But it requires a C API taking an
> >> alignment argument (same for `KVmalloc`).
>
> I see, that's good to know.
>
> >> Coming up with a vrealloc_aligned() is rather trivial. kvrealloc_aligned() would
> >> be a bit weird though, because the alignment argument could only be really
> >> honored if we run into the vrealloc() case. For the krealloc() case it'd still
> >> depend on the bucket size that is selected for the requested size.
>
> Yeah... Maybe some more logic on the Rust side can help with that.
Only if we reimplement `KVmalloc` in Rust, However, there are quite some special
cases in __kvmalloc_node_noprof(), i.e. fixup page flags, sanity check the size
on kmalloc failure, fail on certain page flags, etc.
I don't really want to duplicate this code, unless we absolutely have to.
>
> >> Adding the C API, I'm also pretty sure someone's gonna ask what we need an
> >> alignment larger than PAGE_SIZE for and if we have a real use case for that.
> >> I'm not entirely sure we have a reasonable answer for that.
>
> We could argue that we can remove an "ugly hack" (when we don't have the
> build assert, if we do have that, I don't mind not supporting it), but I
> agree that finding a user will be difficult.
I'd argue it's not really a hack to fail on something that's not supported
(yet). Allocations can (almost) always fail, this is just another case.
>
> >> I got some hacked up patches for that, but I'd rather polish and send them once
> >> we actually need it.
>
> Sure, just wanted to check why you don't want to do it this series.
>
> ---
> Cheers,
> Benno
>
^ permalink raw reply [flat|nested] 95+ messages in thread* Re: [PATCH v5 06/26] rust: alloc: implement `Vmalloc` allocator
2024-08-15 12:29 ` Danilo Krummrich
@ 2024-08-15 13:44 ` Benno Lossin
2024-08-15 14:23 ` Danilo Krummrich
0 siblings, 1 reply; 95+ messages in thread
From: Benno Lossin @ 2024-08-15 13: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, cjia, jhubbard, airlied,
ajanulgu, lyude, linux-kernel, rust-for-linux, linux-mm
On 15.08.24 14:29, Danilo Krummrich wrote:
> On Thu, Aug 15, 2024 at 06:48:19AM +0000, Benno Lossin wrote:
>> On 15.08.24 01:20, Danilo Krummrich wrote:
>>> On Thu, Aug 15, 2024 at 12:13:06AM +0200, Danilo Krummrich wrote:
>>>>
>>>>>
>>>>>> + ptr: Option<NonNull<u8>>,
>>>>>> + layout: Layout,
>>>>>> + flags: Flags,
>>>>>> + ) -> Result<NonNull<[u8]>, AllocError> {
>>>>>> + // TODO: Support alignments larger than PAGE_SIZE.
>>>>>> + if layout.align() > bindings::PAGE_SIZE {
>>>>>> + pr_warn!("Vmalloc does not support alignments larger than PAGE_SIZE yet.\n");
>>>>>> + return Err(AllocError);
>>>>>
>>>>> I think here we should first try to use `build_error!`, most often the
>>>>> alignment will be specified statically, so it should get optimized away.
>>>>
>>>> Sure, we can try that first.
>>>
>>> I think I spoke too soon here. I don't think `build_error!` or `build_assert!`
>>> can work here, it would also fail the build when the compiler doesn't know the
>>> value of the alignment, wouldn't it? I remember that I wasn't overly happy about
>>> failing this on runtime either when I first thought about this case, but I also
>>> couldn't think of something better.
>>
>> Yes, it might fail even though the alignment at runtime will be fine.
>> But that's why I suggested trying `build_error!`(or `build_assert!`)
>> first, if nobody hits the case where the compiler cannot figure it out,
>> then we can keep it. If there are instances, where it fails, but the
>> alignment would be fine at runtime, then we can change it to the above.
>> (I would add such a comment above the assert).
>
> Unfortunately, it already does fail with just the test cases.
Aw that's sad.
> Anyway, even if it would have been fine, I don't think it would have been nice
> for a future user to run into a build error even though the alignment is
> perfectlly within bounds.
I think it would have been better compared to failing with a warning at
runtime.
>>> In the end it's rather unlikely to ever hit this case, and probably even more
>>> unlikely to hit it for a sane reason.
>>
>> Yeah, but I still prefer the build to fail, rather than emitting a warn
>> message that can be overlooked at runtime.
>>
>>>>> How difficult will it be to support this? (it is a weird requirement,
>>>>> but I dislike just returning an error...)
>>>>
>>>> It's not difficult to support at all. But it requires a C API taking an
>>>> alignment argument (same for `KVmalloc`).
>>
>> I see, that's good to know.
>>
>>>> Coming up with a vrealloc_aligned() is rather trivial. kvrealloc_aligned() would
>>>> be a bit weird though, because the alignment argument could only be really
>>>> honored if we run into the vrealloc() case. For the krealloc() case it'd still
>>>> depend on the bucket size that is selected for the requested size.
>>
>> Yeah... Maybe some more logic on the Rust side can help with that.
>
> Only if we reimplement `KVmalloc` in Rust, However, there are quite some special
> cases in __kvmalloc_node_noprof(), i.e. fixup page flags, sanity check the size
> on kmalloc failure, fail on certain page flags, etc.
>
> I don't really want to duplicate this code, unless we absolutely have to.
I am under the (probably wrong) impression that kvmalloc has some size
check and selects vmalloc or kmalloc depending on that. I think that we
could check the size and if it is going to allocate via kmalloc, then we
adjust the size for alignment as usual and if it is going to select
vmalloc, then we can just pass the alignment (if the vmalloc alignment
patch is done first).
>>>> Adding the C API, I'm also pretty sure someone's gonna ask what we need an
>>>> alignment larger than PAGE_SIZE for and if we have a real use case for that.
>>>> I'm not entirely sure we have a reasonable answer for that.
>>
>> We could argue that we can remove an "ugly hack" (when we don't have the
>> build assert, if we do have that, I don't mind not supporting it), but I
>> agree that finding a user will be difficult.
>
> I'd argue it's not really a hack to fail on something that's not supported
> (yet). Allocations can (almost) always fail, this is just another case.
I guess since this is a deterministic failure, it's better than other
failures. But I would still say this is hacky.
---
Cheers,
Benno
^ permalink raw reply [flat|nested] 95+ messages in thread* Re: [PATCH v5 06/26] rust: alloc: implement `Vmalloc` allocator
2024-08-15 13:44 ` Benno Lossin
@ 2024-08-15 14:23 ` Danilo Krummrich
2024-08-15 19:08 ` Benno Lossin
0 siblings, 1 reply; 95+ messages in thread
From: Danilo Krummrich @ 2024-08-15 14:23 UTC (permalink / raw)
To: Benno Lossin
Cc: ojeda, alex.gaynor, wedsonaf, boqun.feng, gary, bjorn3_gh,
a.hindborg, aliceryhl, akpm, daniel.almeida, faith.ekstrand,
boris.brezillon, lina, mcanal, zhiw, cjia, jhubbard, airlied,
ajanulgu, lyude, linux-kernel, rust-for-linux, linux-mm
On Thu, Aug 15, 2024 at 01:44:27PM +0000, Benno Lossin wrote:
> On 15.08.24 14:29, Danilo Krummrich wrote:
> > On Thu, Aug 15, 2024 at 06:48:19AM +0000, Benno Lossin wrote:
> >> On 15.08.24 01:20, Danilo Krummrich wrote:
> >>> On Thu, Aug 15, 2024 at 12:13:06AM +0200, Danilo Krummrich wrote:
> >>>>
> >>>>>
> >>>>>> + ptr: Option<NonNull<u8>>,
> >>>>>> + layout: Layout,
> >>>>>> + flags: Flags,
> >>>>>> + ) -> Result<NonNull<[u8]>, AllocError> {
> >>>>>> + // TODO: Support alignments larger than PAGE_SIZE.
> >>>>>> + if layout.align() > bindings::PAGE_SIZE {
> >>>>>> + pr_warn!("Vmalloc does not support alignments larger than PAGE_SIZE yet.\n");
> >>>>>> + return Err(AllocError);
> >>>>>
> >>>>> I think here we should first try to use `build_error!`, most often the
> >>>>> alignment will be specified statically, so it should get optimized away.
> >>>>
> >>>> Sure, we can try that first.
> >>>
> >>> I think I spoke too soon here. I don't think `build_error!` or `build_assert!`
> >>> can work here, it would also fail the build when the compiler doesn't know the
> >>> value of the alignment, wouldn't it? I remember that I wasn't overly happy about
> >>> failing this on runtime either when I first thought about this case, but I also
> >>> couldn't think of something better.
> >>
> >> Yes, it might fail even though the alignment at runtime will be fine.
> >> But that's why I suggested trying `build_error!`(or `build_assert!`)
> >> first, if nobody hits the case where the compiler cannot figure it out,
> >> then we can keep it. If there are instances, where it fails, but the
> >> alignment would be fine at runtime, then we can change it to the above.
> >> (I would add such a comment above the assert).
> >
> > Unfortunately, it already does fail with just the test cases.
>
> Aw that's sad.
>
> > Anyway, even if it would have been fine, I don't think it would have been nice
> > for a future user to run into a build error even though the alignment is
> > perfectlly within bounds.
>
> I think it would have been better compared to failing with a warning at
> runtime.
Generally, yes. But I think it's not acceptable to make calls fail that should
actually succeed.
>
> >>> In the end it's rather unlikely to ever hit this case, and probably even more
> >>> unlikely to hit it for a sane reason.
> >>
> >> Yeah, but I still prefer the build to fail, rather than emitting a warn
> >> message that can be overlooked at runtime.
> >>
> >>>>> How difficult will it be to support this? (it is a weird requirement,
> >>>>> but I dislike just returning an error...)
> >>>>
> >>>> It's not difficult to support at all. But it requires a C API taking an
> >>>> alignment argument (same for `KVmalloc`).
> >>
> >> I see, that's good to know.
> >>
> >>>> Coming up with a vrealloc_aligned() is rather trivial. kvrealloc_aligned() would
> >>>> be a bit weird though, because the alignment argument could only be really
> >>>> honored if we run into the vrealloc() case. For the krealloc() case it'd still
> >>>> depend on the bucket size that is selected for the requested size.
> >>
> >> Yeah... Maybe some more logic on the Rust side can help with that.
> >
> > Only if we reimplement `KVmalloc` in Rust, However, there are quite some special
> > cases in __kvmalloc_node_noprof(), i.e. fixup page flags, sanity check the size
> > on kmalloc failure, fail on certain page flags, etc.
> >
> > I don't really want to duplicate this code, unless we absolutely have to.
>
> I am under the (probably wrong) impression that kvmalloc has some size
> check and selects vmalloc or kmalloc depending on that.
Basically, yes. But as mentioned above, there are quite some corner cases [1].
> I think that we
> could check the size and if it is going to allocate via kmalloc, then we
> adjust the size for alignment as usual
We don't need this adjustment any longer, see commit ad59baa31695 ("slab, rust:
extend kmalloc() alignment guarantees to remove Rust padding").
> and if it is going to select
> vmalloc, then we can just pass the alignment (if the vmalloc alignment
> patch is done first).
Yeah, but as mentioned, I'd prefer to do this in C, such that we don't need to
open code everything the C code already does.
[1] https://elixir.bootlin.com/linux/v6.11-rc3/source/mm/util.c#L628
>
> >>>> Adding the C API, I'm also pretty sure someone's gonna ask what we need an
> >>>> alignment larger than PAGE_SIZE for and if we have a real use case for that.
> >>>> I'm not entirely sure we have a reasonable answer for that.
> >>
> >> We could argue that we can remove an "ugly hack" (when we don't have the
> >> build assert, if we do have that, I don't mind not supporting it), but I
> >> agree that finding a user will be difficult.
> >
> > I'd argue it's not really a hack to fail on something that's not supported
> > (yet). Allocations can (almost) always fail, this is just another case.
>
> I guess since this is a deterministic failure, it's better than other
> failures. But I would still say this is hacky.
>
> ---
> Cheers,
> Benno
>
^ permalink raw reply [flat|nested] 95+ messages in thread* Re: [PATCH v5 06/26] rust: alloc: implement `Vmalloc` allocator
2024-08-15 14:23 ` Danilo Krummrich
@ 2024-08-15 19:08 ` Benno Lossin
0 siblings, 0 replies; 95+ messages in thread
From: Benno Lossin @ 2024-08-15 19:08 UTC (permalink / raw)
To: Danilo Krummrich
Cc: ojeda, alex.gaynor, wedsonaf, boqun.feng, gary, bjorn3_gh,
a.hindborg, aliceryhl, akpm, daniel.almeida, faith.ekstrand,
boris.brezillon, lina, mcanal, zhiw, cjia, jhubbard, airlied,
ajanulgu, lyude, linux-kernel, rust-for-linux, linux-mm
On 15.08.24 16:23, Danilo Krummrich wrote:
> On Thu, Aug 15, 2024 at 01:44:27PM +0000, Benno Lossin wrote:
>> On 15.08.24 14:29, Danilo Krummrich wrote:
>>> On Thu, Aug 15, 2024 at 06:48:19AM +0000, Benno Lossin wrote:
>>>> On 15.08.24 01:20, Danilo Krummrich wrote:
>>>>> On Thu, Aug 15, 2024 at 12:13:06AM +0200, Danilo Krummrich wrote:
>>>>>>> How difficult will it be to support this? (it is a weird requirement,
>>>>>>> but I dislike just returning an error...)
>>>>>>
>>>>>> It's not difficult to support at all. But it requires a C API taking an
>>>>>> alignment argument (same for `KVmalloc`).
>>>>
>>>> I see, that's good to know.
>>>>
>>>>>> Coming up with a vrealloc_aligned() is rather trivial. kvrealloc_aligned() would
>>>>>> be a bit weird though, because the alignment argument could only be really
>>>>>> honored if we run into the vrealloc() case. For the krealloc() case it'd still
>>>>>> depend on the bucket size that is selected for the requested size.
>>>>
>>>> Yeah... Maybe some more logic on the Rust side can help with that.
>>>
>>> Only if we reimplement `KVmalloc` in Rust, However, there are quite some special
>>> cases in __kvmalloc_node_noprof(), i.e. fixup page flags, sanity check the size
>>> on kmalloc failure, fail on certain page flags, etc.
>>>
>>> I don't really want to duplicate this code, unless we absolutely have to.
>>
>> I am under the (probably wrong) impression that kvmalloc has some size
>> check and selects vmalloc or kmalloc depending on that.
>
> Basically, yes. But as mentioned above, there are quite some corner cases [1].
>
>> I think that we
>> could check the size and if it is going to allocate via kmalloc, then we
>> adjust the size for alignment as usual
>
> We don't need this adjustment any longer, see commit ad59baa31695 ("slab, rust:
> extend kmalloc() alignment guarantees to remove Rust padding").
>
>> and if it is going to select
>> vmalloc, then we can just pass the alignment (if the vmalloc alignment
>> patch is done first).
>
> Yeah, but as mentioned, I'd prefer to do this in C, such that we don't need to
> open code everything the C code already does.
>
> [1] https://elixir.bootlin.com/linux/v6.11-rc3/source/mm/util.c#L628
I see, then it's probably better to just add an align parameter variant
on the C side. Instead of rebuilding it in Rust.
---
Cheers,
Benno
^ permalink raw reply [flat|nested] 95+ messages in thread
* [PATCH v5 07/26] rust: alloc: implement `KVmalloc` allocator
2024-08-12 18:22 [PATCH v5 00/26] Generic `Allocator` support for Rust Danilo Krummrich
` (5 preceding siblings ...)
2024-08-12 18:22 ` [PATCH v5 06/26] rust: alloc: implement `Vmalloc` allocator Danilo Krummrich
@ 2024-08-12 18:22 ` Danilo Krummrich
2024-08-12 18:22 ` [PATCH v5 08/26] rust: alloc: add __GFP_NOWARN to `Flags` Danilo Krummrich
` (19 subsequent siblings)
26 siblings, 0 replies; 95+ messages in thread
From: Danilo Krummrich @ 2024-08-12 18:22 UTC (permalink / raw)
To: ojeda, alex.gaynor, wedsonaf, boqun.feng, gary, bjorn3_gh,
benno.lossin, a.hindborg, aliceryhl, akpm
Cc: daniel.almeida, faith.ekstrand, boris.brezillon, lina, mcanal,
zhiw, cjia, jhubbard, airlied, ajanulgu, lyude, linux-kernel,
rust-for-linux, linux-mm, Danilo Krummrich
Implement `Allocator` for `KVmalloc`, an `Allocator` that tries to
allocate memory wth `kmalloc` first and, on failure, falls back to
`vmalloc`.
All memory allocations made with `KVmalloc` end up in
`kvrealloc_noprof()`; all frees in `kvfree()`.
Reviewed-by: Alice Ryhl <aliceryhl@google.com>
Signed-off-by: Danilo Krummrich <dakr@kernel.org>
---
rust/helpers.c | 6 ++++++
rust/kernel/alloc/allocator.rs | 27 +++++++++++++++++++++++++++
rust/kernel/alloc/allocator_test.rs | 1 +
3 files changed, 34 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 fdda22c6983f..243d8c49a57b 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.
@@ -65,6 +71,9 @@ impl ReallocFunc {
// INVARIANT: `vrealloc` satisfies the type invariants.
const VREALLOC: Self = Self(bindings::vrealloc);
+ // INVARIANT: `kvrealloc` satisfies the type invariants.
+ const KVREALLOC: Self = Self(bindings::kvrealloc);
+
/// # Safety
///
/// This method has the same safety requirements as `Allocator::realloc`.
@@ -160,6 +169,24 @@ unsafe fn realloc(
}
}
+unsafe impl Allocator for KVmalloc {
+ unsafe fn realloc(
+ ptr: Option<NonNull<u8>>,
+ layout: Layout,
+ flags: Flags,
+ ) -> Result<NonNull<[u8]>, AllocError> {
+ // TODO: Support alignments larger than PAGE_SIZE.
+ if layout.align() > bindings::PAGE_SIZE {
+ pr_warn!("KVmalloc does not support alignments larger than PAGE_SIZE yet.\n");
+ return Err(AllocError);
+ }
+
+ // SAFETY: If not `None`, `ptr` is guaranteed to point to valid memory, which was previously
+ // allocated with this `Allocator`.
+ unsafe { ReallocFunc::KVREALLOC.call(ptr, layout, flags) }
+ }
+}
+
#[global_allocator]
static ALLOCATOR: Kmalloc = Kmalloc;
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] 95+ messages in thread* [PATCH v5 08/26] rust: alloc: add __GFP_NOWARN to `Flags`
2024-08-12 18:22 [PATCH v5 00/26] Generic `Allocator` support for Rust Danilo Krummrich
` (6 preceding siblings ...)
2024-08-12 18:22 ` [PATCH v5 07/26] rust: alloc: implement `KVmalloc` allocator Danilo Krummrich
@ 2024-08-12 18:22 ` Danilo Krummrich
2024-08-14 7:48 ` Alice Ryhl
2024-08-14 16:35 ` Benno Lossin
2024-08-12 18:22 ` [PATCH v5 09/26] rust: alloc: implement kernel `Box` Danilo Krummrich
` (18 subsequent siblings)
26 siblings, 2 replies; 95+ messages in thread
From: Danilo Krummrich @ 2024-08-12 18:22 UTC (permalink / raw)
To: ojeda, alex.gaynor, wedsonaf, boqun.feng, gary, bjorn3_gh,
benno.lossin, a.hindborg, aliceryhl, akpm
Cc: daniel.almeida, faith.ekstrand, boris.brezillon, lina, mcanal,
zhiw, cjia, jhubbard, airlied, ajanulgu, lyude, linux-kernel,
rust-for-linux, linux-mm, Danilo Krummrich
Some test cases in subsequent patches provoke allocation failures. Add
`__GFP_NOWARN` to enable test cases to silence unpleasant warnings.
Signed-off-by: Danilo Krummrich <dakr@kernel.org>
---
rust/bindings/bindings_helper.h | 1 +
rust/kernel/alloc.rs | 5 +++++
2 files changed, 6 insertions(+)
diff --git a/rust/bindings/bindings_helper.h b/rust/bindings/bindings_helper.h
index b940a5777330..7f781256fda9 100644
--- a/rust/bindings/bindings_helper.h
+++ b/rust/bindings/bindings_helper.h
@@ -31,4 +31,5 @@ const gfp_t RUST_CONST_HELPER_GFP_KERNEL_ACCOUNT = GFP_KERNEL_ACCOUNT;
const gfp_t RUST_CONST_HELPER_GFP_NOWAIT = GFP_NOWAIT;
const gfp_t RUST_CONST_HELPER___GFP_ZERO = __GFP_ZERO;
const gfp_t RUST_CONST_HELPER___GFP_HIGHMEM = ___GFP_HIGHMEM;
+const gfp_t RUST_CONST_HELPER___GFP_NOWARN = ___GFP_NOWARN;
const blk_features_t RUST_CONST_HELPER_BLK_FEAT_ROTATIONAL = BLK_FEAT_ROTATIONAL;
diff --git a/rust/kernel/alloc.rs b/rust/kernel/alloc.rs
index f0c80ab78602..295107777a12 100644
--- a/rust/kernel/alloc.rs
+++ b/rust/kernel/alloc.rs
@@ -91,6 +91,11 @@ pub mod flags {
/// use any filesystem callback. It is very likely to fail to allocate memory, even for very
/// small allocations.
pub const GFP_NOWAIT: Flags = Flags(bindings::GFP_NOWAIT);
+
+ /// Suppresses allocation failure reports.
+ ///
+ /// This is normally or'd with other flags.
+ pub const __GFP_NOWARN: Flags = Flags(bindings::__GFP_NOWARN);
}
/// The kernel's [`Allocator`] trait.
--
2.45.2
^ permalink raw reply related [flat|nested] 95+ messages in thread* Re: [PATCH v5 08/26] rust: alloc: add __GFP_NOWARN to `Flags`
2024-08-12 18:22 ` [PATCH v5 08/26] rust: alloc: add __GFP_NOWARN to `Flags` Danilo Krummrich
@ 2024-08-14 7:48 ` Alice Ryhl
2024-08-14 16:35 ` Benno Lossin
1 sibling, 0 replies; 95+ messages in thread
From: Alice Ryhl @ 2024-08-14 7:48 UTC (permalink / raw)
To: Danilo Krummrich
Cc: ojeda, alex.gaynor, wedsonaf, boqun.feng, gary, bjorn3_gh,
benno.lossin, a.hindborg, akpm, daniel.almeida, faith.ekstrand,
boris.brezillon, lina, mcanal, zhiw, cjia, jhubbard, airlied,
ajanulgu, lyude, linux-kernel, rust-for-linux, linux-mm
On Mon, Aug 12, 2024 at 8:24 PM Danilo Krummrich <dakr@kernel.org> wrote:
>
> Some test cases in subsequent patches provoke allocation failures. Add
> `__GFP_NOWARN` to enable test cases to silence unpleasant warnings.
>
> Signed-off-by: Danilo Krummrich <dakr@kernel.org>
Reviewed-by: Alice Ryhl <aliceryhl@google.com>
> rust/bindings/bindings_helper.h | 1 +
> rust/kernel/alloc.rs | 5 +++++
> 2 files changed, 6 insertions(+)
>
> diff --git a/rust/bindings/bindings_helper.h b/rust/bindings/bindings_helper.h
> index b940a5777330..7f781256fda9 100644
> --- a/rust/bindings/bindings_helper.h
> +++ b/rust/bindings/bindings_helper.h
> @@ -31,4 +31,5 @@ const gfp_t RUST_CONST_HELPER_GFP_KERNEL_ACCOUNT = GFP_KERNEL_ACCOUNT;
> const gfp_t RUST_CONST_HELPER_GFP_NOWAIT = GFP_NOWAIT;
> const gfp_t RUST_CONST_HELPER___GFP_ZERO = __GFP_ZERO;
> const gfp_t RUST_CONST_HELPER___GFP_HIGHMEM = ___GFP_HIGHMEM;
> +const gfp_t RUST_CONST_HELPER___GFP_NOWARN = ___GFP_NOWARN;
> const blk_features_t RUST_CONST_HELPER_BLK_FEAT_ROTATIONAL = BLK_FEAT_ROTATIONAL;
> diff --git a/rust/kernel/alloc.rs b/rust/kernel/alloc.rs
> index f0c80ab78602..295107777a12 100644
> --- a/rust/kernel/alloc.rs
> +++ b/rust/kernel/alloc.rs
> @@ -91,6 +91,11 @@ pub mod flags {
> /// use any filesystem callback. It is very likely to fail to allocate memory, even for very
> /// small allocations.
> pub const GFP_NOWAIT: Flags = Flags(bindings::GFP_NOWAIT);
> +
> + /// Suppresses allocation failure reports.
> + ///
> + /// This is normally or'd with other flags.
> + pub const __GFP_NOWARN: Flags = Flags(bindings::__GFP_NOWARN);
> }
>
> /// The kernel's [`Allocator`] trait.
> --
> 2.45.2
>
^ permalink raw reply [flat|nested] 95+ messages in thread* Re: [PATCH v5 08/26] rust: alloc: add __GFP_NOWARN to `Flags`
2024-08-12 18:22 ` [PATCH v5 08/26] rust: alloc: add __GFP_NOWARN to `Flags` Danilo Krummrich
2024-08-14 7:48 ` Alice Ryhl
@ 2024-08-14 16:35 ` Benno Lossin
1 sibling, 0 replies; 95+ messages in thread
From: Benno Lossin @ 2024-08-14 16: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, cjia, jhubbard, airlied, ajanulgu, lyude, linux-kernel,
rust-for-linux, linux-mm
On 12.08.24 20:22, Danilo Krummrich wrote:
> Some test cases in subsequent patches provoke allocation failures. Add
> `__GFP_NOWARN` to enable test cases to silence unpleasant warnings.
>
> Signed-off-by: Danilo Krummrich <dakr@kernel.org>
> ---
> rust/bindings/bindings_helper.h | 1 +
> rust/kernel/alloc.rs | 5 +++++
> 2 files changed, 6 insertions(+)
Reviewed-by: Benno Lossin <benno.lossin@proton.me>
---
Cheers,
Benno
^ permalink raw reply [flat|nested] 95+ messages in thread
* [PATCH v5 09/26] rust: alloc: implement kernel `Box`
2024-08-12 18:22 [PATCH v5 00/26] Generic `Allocator` support for Rust Danilo Krummrich
` (7 preceding siblings ...)
2024-08-12 18:22 ` [PATCH v5 08/26] rust: alloc: add __GFP_NOWARN to `Flags` Danilo Krummrich
@ 2024-08-12 18:22 ` Danilo Krummrich
2024-08-14 8:26 ` Alice Ryhl
2024-08-14 17:01 ` Benno Lossin
2024-08-12 18:22 ` [PATCH v5 10/26] rust: treewide: switch to our kernel `Box` type Danilo Krummrich
` (17 subsequent siblings)
26 siblings, 2 replies; 95+ messages in thread
From: Danilo Krummrich @ 2024-08-12 18:22 UTC (permalink / raw)
To: ojeda, alex.gaynor, wedsonaf, boqun.feng, gary, bjorn3_gh,
benno.lossin, a.hindborg, aliceryhl, akpm
Cc: daniel.almeida, faith.ekstrand, boris.brezillon, lina, mcanal,
zhiw, cjia, jhubbard, airlied, ajanulgu, lyude, linux-kernel,
rust-for-linux, linux-mm, Danilo Krummrich
`Box` provides the simplest way to allocate memory for a generic type
with one of the kernel's allocators, e.g. `Kmalloc`, `Vmalloc` or
`KVmalloc`.
In contrast to Rust's `Box` type, the kernel `Box` type considers the
kernel's GFP flags for all appropriate functions, always reports
allocation failures through `Result<_, AllocError>` and remains
independent from unstable features.
Signed-off-by: Danilo Krummrich <dakr@kernel.org>
---
rust/kernel/alloc.rs | 6 +
rust/kernel/alloc/kbox.rs | 423 ++++++++++++++++++++++++++++++++++++++
rust/kernel/prelude.rs | 2 +-
3 files changed, 430 insertions(+), 1 deletion(-)
create mode 100644 rust/kernel/alloc/kbox.rs
diff --git a/rust/kernel/alloc.rs b/rust/kernel/alloc.rs
index 295107777a12..ed46b69204d0 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..67bdfc0712d2
--- /dev/null
+++ b/rust/kernel/alloc/kbox.rs
@@ -0,0 +1,423 @@
+// SPDX-License-Identifier: GPL-2.0
+
+//! Implementation of [`Box`].
+
+use super::{AllocError, Allocator, Flags};
+use core::fmt;
+use core::marker::PhantomData;
+use core::mem::ManuallyDrop;
+use core::mem::MaybeUninit;
+use core::ops::{Deref, DerefMut};
+use core::pin::Pin;
+use core::ptr::NonNull;
+use core::result::Result;
+
+use crate::init::{InPlaceInit, Init, PinInit};
+use crate::types::ForeignOwnable;
+
+/// The kernel's [`Box`] type - a heap allocation for a single value of type `T`.
+///
+/// This is the kernel's version of the Rust stdlib's `Box`. There are a couple of differences,
+/// for example no `noalias` attribute is emitted and partially moving out of a `Box` is not
+/// supported.
+///
+/// `Box` works with any of the kernel's allocators, e.g. [`super::allocator::Kmalloc`],
+/// [`super::allocator::Vmalloc`] or [`super::allocator::KVmalloc`]. There are aliases for `Box`
+/// with these allocators ([`KBox`], [`VBox`], [`KVBox`]).
+///
+/// When dropping a [`Box`], the value is also dropped and the heap memory is automatically freed.
+///
+/// # Examples
+///
+/// ```
+/// let b = KBox::<u64>::new(24_u64, GFP_KERNEL)?;
+///
+/// assert_eq!(*b, 24_u64);
+///
+/// # Ok::<(), Error>(())
+/// ```
+///
+/// ```
+/// # use kernel::bindings;
+///
+/// const SIZE: usize = bindings::KMALLOC_MAX_SIZE as usize + 1;
+/// struct Huge([u8; SIZE]);
+///
+/// assert!(KBox::<Huge>::new_uninit(GFP_KERNEL | __GFP_NOWARN).is_err());
+/// ```
+///
+/// ```
+/// # use kernel::bindings;
+///
+/// const SIZE: usize = bindings::KMALLOC_MAX_SIZE as usize + 1;
+/// struct Huge([u8; SIZE]);
+///
+/// assert!(KVBox::<Huge>::new_uninit(GFP_KERNEL).is_ok());
+/// ```
+///
+/// # Invariants
+///
+/// The [`Box`]' pointer always properly aligned and either points to memory allocated with `A` or,
+/// for zero-sized types, is a dangling pointer.
+pub struct Box<T: ?Sized, A: Allocator>(NonNull<T>, PhantomData<A>);
+
+/// Type alias for `Box` with a `Kmalloc` allocator.
+///
+/// # Examples
+///
+/// ```
+/// let b = KBox::new(24_u64, GFP_KERNEL)?;
+///
+/// assert_eq!(*b, 24_u64);
+///
+/// # Ok::<(), Error>(())
+/// ```
+pub type KBox<T> = Box<T, super::allocator::Kmalloc>;
+
+/// Type alias for `Box` with a `Vmalloc` allocator.
+///
+/// # Examples
+///
+/// ```
+/// let b = VBox::new(24_u64, GFP_KERNEL)?;
+///
+/// assert_eq!(*b, 24_u64);
+///
+/// # Ok::<(), Error>(())
+/// ```
+pub type VBox<T> = Box<T, super::allocator::Vmalloc>;
+
+/// Type alias for `Box` with a `KVmalloc` allocator.
+///
+/// # Examples
+///
+/// ```
+/// let b = KVBox::new(24_u64, GFP_KERNEL)?;
+///
+/// assert_eq!(*b, 24_u64);
+///
+/// # Ok::<(), Error>(())
+/// ```
+pub type KVBox<T> = Box<T, super::allocator::KVmalloc>;
+
+// SAFETY: `Box` is `Send` if `T` is `Send` because the data referenced by `self.0` is unaliased.
+unsafe impl<T, A> Send for Box<T, A>
+where
+ T: Send + ?Sized,
+ A: Allocator,
+{
+}
+
+// SAFETY: `Box` is `Sync` if `T` is `Sync` because the data referenced by `self.0` is unaliased.
+unsafe impl<T, A> Sync for Box<T, A>
+where
+ T: Send + ?Sized,
+ A: Allocator,
+{
+}
+
+impl<T, A> Box<T, A>
+where
+ T: ?Sized,
+ A: Allocator,
+{
+ /// Creates a new `Box<T, A>` from a raw pointer.
+ ///
+ /// # Safety
+ ///
+ /// `raw` must point to valid memory, previously be allocated with `A`, and provide at least
+ /// the size of type `T`. For ZSTs `raw` must be a dangling pointer.
+ #[inline]
+ pub const unsafe fn from_raw(raw: *mut T) -> Self {
+ // INVARIANT: Validity of `raw` is guaranteed by the safety preconditions of this function.
+ // SAFETY: By the safety preconditions of this function, `raw` is not a NULL pointer.
+ Self(unsafe { NonNull::new_unchecked(raw) }, PhantomData::<A>)
+ }
+
+ /// Consumes the `Box<T, A>` and returns a raw pointer.
+ ///
+ /// This will not run the destructor of `T` and for non-ZSTs the allocation will stay alive
+ /// indefinitely. Use [`Box::from_raw`] to recover the [`Box`], drop the value and free the
+ /// allocation, if any.
+ ///
+ /// # Examples
+ ///
+ /// ```
+ /// let x = KBox::new(24, GFP_KERNEL)?;
+ /// let ptr = KBox::into_raw(x);
+ /// let x = unsafe { KBox::from_raw(ptr) };
+ ///
+ /// assert_eq!(*x, 24);
+ ///
+ /// # Ok::<(), Error>(())
+ /// ```
+ #[inline]
+ pub fn into_raw(b: Self) -> *mut T {
+ let b = ManuallyDrop::new(b);
+
+ b.0.as_ptr()
+ }
+
+ /// Consumes and leaks the `Box<T, A>` and returns a mutable reference.
+ ///
+ /// See [Box::into_raw] for more details.
+ #[inline]
+ pub fn leak<'a>(b: Self) -> &'a mut T
+ 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>>`. If `T` does not implement [`Unpin`], then
+ /// `*b` will be pinned in memory and can't be moved.
+ ///
+ /// This moves `b` into `Pin` without moving `*b` or allocating and copying any memory.
+ #[inline]
+ pub fn into_pin(b: Self) -> Pin<Self> {
+ // SAFETY: The value wrapped inside a `Pin<Box<T, A>>` cannot be moved or replaced as long
+ // as `T` does not implement `Unpin`.
+ unsafe { Pin::new_unchecked(b) }
+ }
+}
+
+impl<T, A> Box<MaybeUninit<T>, A>
+where
+ A: Allocator,
+{
+ /// Converts a `Box<MaybeUninit<T>, A>` to a `Box<T, A>`.
+ ///
+ /// # Safety
+ ///
+ /// Callers must ensure that the value inside of `b` is in an initialized state. It is
+ /// undefined behavior to call this function while the value inside of `b` is not yet fully
+ /// initialized.
+ pub unsafe fn assume_init(b: Self) -> Box<T, A> {
+ let raw = Self::into_raw(b);
+
+ // SAFETY: `raw` comes from a previous call to `Box::into_raw`. By the safety requirements
+ // of this function, the value inside the `Box` is in an initialized state. Hence, it is
+ // safe to reconstruct the `Box` as `Box<T, A>`.
+ unsafe { Box::from_raw(raw as *mut T) }
+ }
+
+ /// 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
+ }
+
+ /// Creates a new `Box<T, A>` and initializes its contents with `x`.
+ ///
+ /// New memory is allocated with `a`. The allocation may fail, in which case an error is
+ /// returned. For ZSTs no memory is allocated.
+ pub fn new(x: T, flags: Flags) -> Result<Self, AllocError> {
+ let b = Self::new_uninit(flags)?;
+ Ok(Box::write(b, x))
+ }
+
+ /// Creates a new `Box<T, A>` with uninitialized contents.
+ ///
+ /// New memory is allocated with `a`. The allocation may fail, in which case an error is
+ /// returned. For ZSTs no memory is allocated.
+ ///
+ /// # Examples
+ ///
+ /// ```
+ /// let b = KBox::<u64>::new_uninit(GFP_KERNEL)?;
+ /// let b = KBox::write(b, 24);
+ ///
+ /// assert_eq!(*b, 24_u64);
+ ///
+ /// # Ok::<(), Error>(())
+ /// ```
+ pub fn new_uninit(flags: Flags) -> Result<Box<MaybeUninit<T>, A>, AllocError> {
+ let ptr = if Self::is_zst() {
+ NonNull::dangling()
+ } else {
+ let layout = core::alloc::Layout::new::<MaybeUninit<T>>();
+ let ptr = A::alloc(layout, flags)?;
+
+ ptr.cast()
+ };
+
+ Ok(Box(ptr, PhantomData::<A>))
+ }
+
+ /// Constructs a new `Pin<Box<T, A>>`. If `T` does not implement [`Unpin`], then `x` will be
+ /// pinned in memory and can't be moved.
+ #[inline]
+ pub fn pin(x: T, flags: Flags) -> Result<Pin<Box<T, A>>, AllocError>
+ where
+ A: 'static,
+ {
+ Ok(Self::new(x, flags)?.into())
+ }
+}
+
+impl<T, A> From<Box<T, A>> for Pin<Box<T, A>>
+where
+ T: ?Sized,
+ A: Allocator,
+{
+ /// Converts a `Box<T, A>` into a `Pin<Box<T, A>>`. If `T` does not implement [`Unpin`], then
+ /// `*b` will be pinned in memory and can't be moved.
+ ///
+ /// See [`Box::into_pin`] for more details.
+ fn from(b: Box<T, A>) -> Self {
+ Box::into_pin(b)
+ }
+}
+
+impl<T, A> InPlaceInit<T> for 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 = 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 { 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 = 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 { Box::assume_init(this) })
+ }
+}
+
+impl<T: 'static, A> ForeignOwnable for Box<T, A>
+where
+ A: Allocator,
+{
+ type Borrowed<'a> = &'a T;
+
+ fn into_foreign(self) -> *const core::ffi::c_void {
+ Box::into_raw(self) as _
+ }
+
+ unsafe fn 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 Pin<Box<T, A>>
+where
+ A: Allocator,
+{
+ type Borrowed<'a> = Pin<&'a T>;
+
+ fn into_foreign(self) -> *const core::ffi::c_void {
+ // SAFETY: We are still treating the box as pinned.
+ Box::into_raw(unsafe { Pin::into_inner_unchecked(self) }) as _
+ }
+
+ unsafe fn borrow<'a>(ptr: *const core::ffi::c_void) -> Pin<&'a T> {
+ // SAFETY: The safety requirements for this function ensure that the object is still alive,
+ // so it is safe to dereference the raw pointer.
+ // The safety requirements of `from_foreign` also ensure that the object remains alive for
+ // the lifetime of the returned value.
+ let r = unsafe { &*ptr.cast() };
+
+ // SAFETY: This pointer originates from a `Pin<Box<T>>`.
+ unsafe { Pin::new_unchecked(r) }
+ }
+
+ unsafe fn from_foreign(ptr: *const core::ffi::c_void) -> Self {
+ // SAFETY: The safety requirements of this function ensure that `ptr` comes from a previous
+ // call to `Self::into_foreign`.
+ unsafe { Pin::new_unchecked(Box::from_raw(ptr as _)) }
+ }
+}
+
+impl<T, A> Deref for Box<T, A>
+where
+ T: ?Sized,
+ A: Allocator,
+{
+ type Target = T;
+
+ fn deref(&self) -> &T {
+ // SAFETY: `self.0` is always properly aligned, dereferenceable and points to an initialized
+ // instance of `T`.
+ unsafe { self.0.as_ref() }
+ }
+}
+
+impl<T, A> DerefMut for Box<T, A>
+where
+ T: ?Sized,
+ A: Allocator,
+{
+ fn deref_mut(&mut self) -> &mut T {
+ // SAFETY: `self.0` is always properly aligned, dereferenceable and points to an initialized
+ // instance of `T`.
+ unsafe { self.0.as_mut() }
+ }
+}
+
+impl<T, A> fmt::Debug for Box<T, A>
+where
+ T: ?Sized + fmt::Debug,
+ A: Allocator,
+{
+ fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
+ fmt::Debug::fmt(&**self, f)
+ }
+}
+
+impl<T, A> Drop for Box<T, A>
+where
+ T: ?Sized,
+ A: Allocator,
+{
+ fn drop(&mut self) {
+ let size = core::mem::size_of_val::<T>(self);
+
+ // SAFETY: We need to drop `self.0` in place, before we free the backing memory.
+ unsafe { core::ptr::drop_in_place(self.0.as_ptr()) };
+
+ if size != 0 {
+ // SAFETY: `ptr` was previously allocated with `A`.
+ unsafe { A::free(self.0.cast()) };
+ }
+ }
+}
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};
--
2.45.2
^ permalink raw reply related [flat|nested] 95+ messages in thread* Re: [PATCH v5 09/26] rust: alloc: implement kernel `Box`
2024-08-12 18:22 ` [PATCH v5 09/26] rust: alloc: implement kernel `Box` Danilo Krummrich
@ 2024-08-14 8:26 ` Alice Ryhl
2024-08-14 12:22 ` Danilo Krummrich
2024-08-14 17:01 ` Benno Lossin
1 sibling, 1 reply; 95+ messages in thread
From: Alice Ryhl @ 2024-08-14 8:26 UTC (permalink / raw)
To: Danilo Krummrich
Cc: ojeda, alex.gaynor, wedsonaf, boqun.feng, gary, bjorn3_gh,
benno.lossin, a.hindborg, akpm, daniel.almeida, faith.ekstrand,
boris.brezillon, lina, mcanal, zhiw, cjia, jhubbard, airlied,
ajanulgu, lyude, linux-kernel, rust-for-linux, linux-mm
On Mon, Aug 12, 2024 at 8:25 PM Danilo Krummrich <dakr@kernel.org> wrote:
>
> `Box` provides the simplest way to allocate memory for a generic type
> with one of the kernel's allocators, e.g. `Kmalloc`, `Vmalloc` or
> `KVmalloc`.
>
> In contrast to Rust's `Box` type, the kernel `Box` type considers the
> kernel's GFP flags for all appropriate functions, always reports
> allocation failures through `Result<_, AllocError>` and remains
> independent from unstable features.
>
> Signed-off-by: Danilo Krummrich <dakr@kernel.org>
> ---
> rust/kernel/alloc.rs | 6 +
> rust/kernel/alloc/kbox.rs | 423 ++++++++++++++++++++++++++++++++++++++
> rust/kernel/prelude.rs | 2 +-
> 3 files changed, 430 insertions(+), 1 deletion(-)
> create mode 100644 rust/kernel/alloc/kbox.rs
>
> diff --git a/rust/kernel/alloc.rs b/rust/kernel/alloc.rs
> index 295107777a12..ed46b69204d0 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..67bdfc0712d2
> --- /dev/null
> +++ b/rust/kernel/alloc/kbox.rs
> @@ -0,0 +1,423 @@
> +// SPDX-License-Identifier: GPL-2.0
> +
> +//! Implementation of [`Box`].
> +
> +use super::{AllocError, Allocator, Flags};
> +use core::fmt;
> +use core::marker::PhantomData;
> +use core::mem::ManuallyDrop;
> +use core::mem::MaybeUninit;
> +use core::ops::{Deref, DerefMut};
> +use core::pin::Pin;
> +use core::ptr::NonNull;
> +use core::result::Result;
> +
> +use crate::init::{InPlaceInit, Init, PinInit};
> +use crate::types::ForeignOwnable;
> +
> +/// The kernel's [`Box`] type - a heap allocation for a single value of type `T`.
> +///
> +/// This is the kernel's version of the Rust stdlib's `Box`. There are a couple of differences,
> +/// for example no `noalias` attribute is emitted and partially moving out of a `Box` is not
> +/// supported.
> +///
> +/// `Box` works with any of the kernel's allocators, e.g. [`super::allocator::Kmalloc`],
> +/// [`super::allocator::Vmalloc`] or [`super::allocator::KVmalloc`]. There are aliases for `Box`
> +/// with these allocators ([`KBox`], [`VBox`], [`KVBox`]).
> +///
> +/// When dropping a [`Box`], the value is also dropped and the heap memory is automatically freed.
> +///
> +/// # Examples
> +///
> +/// ```
> +/// let b = KBox::<u64>::new(24_u64, GFP_KERNEL)?;
> +///
> +/// assert_eq!(*b, 24_u64);
> +///
> +/// # Ok::<(), Error>(())
This is a minor nit, but when hiding lines in examples you should
avoid having the rendered docs have empty lines at the beginning/end.
There are also several examples of this below with the
kernel::bindings import.
> +/// ```
> +///
> +/// ```
> +/// # use kernel::bindings;
> +///
> +/// const SIZE: usize = bindings::KMALLOC_MAX_SIZE as usize + 1;
> +/// struct Huge([u8; SIZE]);
> +///
> +/// assert!(KBox::<Huge>::new_uninit(GFP_KERNEL | __GFP_NOWARN).is_err());
> +/// ```
> +///
> +/// ```
> +/// # use kernel::bindings;
> +///
> +/// const SIZE: usize = bindings::KMALLOC_MAX_SIZE as usize + 1;
> +/// struct Huge([u8; SIZE]);
> +///
> +/// assert!(KVBox::<Huge>::new_uninit(GFP_KERNEL).is_ok());
> +/// ```
> +///
> +/// # Invariants
> +///
> +/// The [`Box`]' pointer always properly aligned and either points to memory allocated with `A` or,
> +/// for zero-sized types, is a dangling pointer.
> +pub struct Box<T: ?Sized, A: Allocator>(NonNull<T>, PhantomData<A>);
I was about to say this needs a PhantomData<T> too, but I guess it
isn't necessary anymore.
https://doc.rust-lang.org/nomicon/phantom-data.html#generic-parameters-and-drop-checking
> +// SAFETY: `Box` is `Send` if `T` is `Send` because the data referenced by `self.0` is unaliased.
Instead of "unaliased" I would probably just say "because the Box owns a T".
> +
> +// SAFETY: `Box` is `Sync` if `T` is `Sync` because the data referenced by `self.0` is unaliased.
> +unsafe impl<T, A> Sync for Box<T, A>
> +where
> + T: Send + ?Sized,
> + A: Allocator,
This needs to say `T: Sync` instead of `T: Send`. That matches the std Box.
> +
> +impl<T, A> Box<T, A>
> +where
> + T: ?Sized,
> + A: Allocator,
> +{
> + /// Creates a new `Box<T, A>` from a raw pointer.
> + ///
> + /// # Safety
> + ///
> + /// `raw` must point to valid memory, previously be allocated with `A`, and provide at least
> + /// the size of type `T`. For ZSTs `raw` must be a dangling pointer.
Hmm. I don't love this wording. How about this?
For non-ZSTs, `raw` must point at a live allocation allocated with `A`
that is sufficiently aligned for and holds a valid `T`. The caller
passes ownership of the allocation to the `Box`. For ZSTs, the pointer
must be non-null and aligned.
> +impl<T, A> From<Box<T, A>> for Pin<Box<T, A>>
> +where
> + T: ?Sized,
> + A: Allocator,
> +{
> + /// Converts a `Box<T, A>` into a `Pin<Box<T, A>>`. If `T` does not implement [`Unpin`], then
> + /// `*b` will be pinned in memory and can't be moved.
> + ///
> + /// See [`Box::into_pin`] for more details.
> + fn from(b: Box<T, A>) -> Self {
> + Box::into_pin(b)
I still think it makes more sense to match std and only provide From
and not an into_pin, but it's not a blocker.
Alice
^ permalink raw reply [flat|nested] 95+ messages in thread* Re: [PATCH v5 09/26] rust: alloc: implement kernel `Box`
2024-08-14 8:26 ` Alice Ryhl
@ 2024-08-14 12:22 ` Danilo Krummrich
2024-08-14 12:29 ` Alice Ryhl
0 siblings, 1 reply; 95+ messages in thread
From: Danilo Krummrich @ 2024-08-14 12:22 UTC (permalink / raw)
To: Alice Ryhl
Cc: ojeda, alex.gaynor, wedsonaf, boqun.feng, gary, bjorn3_gh,
benno.lossin, a.hindborg, akpm, daniel.almeida, faith.ekstrand,
boris.brezillon, lina, mcanal, zhiw, cjia, jhubbard, airlied,
ajanulgu, lyude, linux-kernel, rust-for-linux, linux-mm
On Wed, Aug 14, 2024 at 10:26:10AM +0200, Alice Ryhl wrote:
> On Mon, Aug 12, 2024 at 8:25 PM Danilo Krummrich <dakr@kernel.org> wrote:
> >
> > `Box` provides the simplest way to allocate memory for a generic type
> > with one of the kernel's allocators, e.g. `Kmalloc`, `Vmalloc` or
> > `KVmalloc`.
> >
> > In contrast to Rust's `Box` type, the kernel `Box` type considers the
> > kernel's GFP flags for all appropriate functions, always reports
> > allocation failures through `Result<_, AllocError>` and remains
> > independent from unstable features.
> >
> > Signed-off-by: Danilo Krummrich <dakr@kernel.org>
> > ---
> > rust/kernel/alloc.rs | 6 +
> > rust/kernel/alloc/kbox.rs | 423 ++++++++++++++++++++++++++++++++++++++
> > rust/kernel/prelude.rs | 2 +-
> > 3 files changed, 430 insertions(+), 1 deletion(-)
> > create mode 100644 rust/kernel/alloc/kbox.rs
> >
> > diff --git a/rust/kernel/alloc.rs b/rust/kernel/alloc.rs
> > index 295107777a12..ed46b69204d0 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..67bdfc0712d2
> > --- /dev/null
> > +++ b/rust/kernel/alloc/kbox.rs
> > @@ -0,0 +1,423 @@
> > +// SPDX-License-Identifier: GPL-2.0
> > +
> > +//! Implementation of [`Box`].
> > +
> > +use super::{AllocError, Allocator, Flags};
> > +use core::fmt;
> > +use core::marker::PhantomData;
> > +use core::mem::ManuallyDrop;
> > +use core::mem::MaybeUninit;
> > +use core::ops::{Deref, DerefMut};
> > +use core::pin::Pin;
> > +use core::ptr::NonNull;
> > +use core::result::Result;
> > +
> > +use crate::init::{InPlaceInit, Init, PinInit};
> > +use crate::types::ForeignOwnable;
> > +
> > +/// The kernel's [`Box`] type - a heap allocation for a single value of type `T`.
> > +///
> > +/// This is the kernel's version of the Rust stdlib's `Box`. There are a couple of differences,
> > +/// for example no `noalias` attribute is emitted and partially moving out of a `Box` is not
> > +/// supported.
> > +///
> > +/// `Box` works with any of the kernel's allocators, e.g. [`super::allocator::Kmalloc`],
> > +/// [`super::allocator::Vmalloc`] or [`super::allocator::KVmalloc`]. There are aliases for `Box`
> > +/// with these allocators ([`KBox`], [`VBox`], [`KVBox`]).
> > +///
> > +/// When dropping a [`Box`], the value is also dropped and the heap memory is automatically freed.
> > +///
> > +/// # Examples
> > +///
> > +/// ```
> > +/// let b = KBox::<u64>::new(24_u64, GFP_KERNEL)?;
> > +///
> > +/// assert_eq!(*b, 24_u64);
> > +///
> > +/// # Ok::<(), Error>(())
>
> This is a minor nit, but when hiding lines in examples you should
> avoid having the rendered docs have empty lines at the beginning/end.
> There are also several examples of this below with the
> kernel::bindings import.
Makes sense, gonna fix it.
>
> > +/// ```
> > +///
> > +/// ```
> > +/// # use kernel::bindings;
> > +///
> > +/// const SIZE: usize = bindings::KMALLOC_MAX_SIZE as usize + 1;
> > +/// struct Huge([u8; SIZE]);
> > +///
> > +/// assert!(KBox::<Huge>::new_uninit(GFP_KERNEL | __GFP_NOWARN).is_err());
> > +/// ```
> > +///
> > +/// ```
> > +/// # use kernel::bindings;
> > +///
> > +/// const SIZE: usize = bindings::KMALLOC_MAX_SIZE as usize + 1;
> > +/// struct Huge([u8; SIZE]);
> > +///
> > +/// assert!(KVBox::<Huge>::new_uninit(GFP_KERNEL).is_ok());
> > +/// ```
> > +///
> > +/// # Invariants
> > +///
> > +/// The [`Box`]' pointer always properly aligned and either points to memory allocated with `A` or,
> > +/// for zero-sized types, is a dangling pointer.
> > +pub struct Box<T: ?Sized, A: Allocator>(NonNull<T>, PhantomData<A>);
>
> I was about to say this needs a PhantomData<T> too, but I guess it
> isn't necessary anymore.
> https://doc.rust-lang.org/nomicon/phantom-data.html#generic-parameters-and-drop-checking
>
> > +// SAFETY: `Box` is `Send` if `T` is `Send` because the data referenced by `self.0` is unaliased.
>
> Instead of "unaliased" I would probably just say "because the Box owns a T".
I'm fine with either one, if that's preferred, I'll change it.
>
> > +
> > +// SAFETY: `Box` is `Sync` if `T` is `Sync` because the data referenced by `self.0` is unaliased.
> > +unsafe impl<T, A> Sync for Box<T, A>
> > +where
> > + T: Send + ?Sized,
> > + A: Allocator,
>
> This needs to say `T: Sync` instead of `T: Send`. That matches the std Box.
Good catch, pretty sure `Vec` has the same copy-paste mistake.
>
> > +
> > +impl<T, A> Box<T, A>
> > +where
> > + T: ?Sized,
> > + A: Allocator,
> > +{
> > + /// Creates a new `Box<T, A>` from a raw pointer.
> > + ///
> > + /// # Safety
> > + ///
> > + /// `raw` must point to valid memory, previously be allocated with `A`, and provide at least
> > + /// the size of type `T`. For ZSTs `raw` must be a dangling pointer.
>
> Hmm. I don't love this wording. How about this?
>
> For non-ZSTs, `raw` must point at a live allocation allocated with `A`
> that is sufficiently aligned for and holds a valid `T`. The caller
> passes ownership of the allocation to the `Box`.
I'm fine taking that, I'm not so sure about "live allocation" though, we don't
use this wording anywhere else. It's probably fine to just say "allocation",
since once it's freed it technically isn't an allocation anymore anyways.
> For ZSTs, the pointer must be non-null and aligned.
Technically, for ZSTs any pointer is aligned and NULL should be valid as well.
Maybe we just don't mentioned ZSTs at all, since we don't really need to enforce
anything for ZSTs.
>
> > +impl<T, A> From<Box<T, A>> for Pin<Box<T, A>>
> > +where
> > + T: ?Sized,
> > + A: Allocator,
> > +{
> > + /// Converts a `Box<T, A>` into a `Pin<Box<T, A>>`. If `T` does not implement [`Unpin`], then
> > + /// `*b` will be pinned in memory and can't be moved.
> > + ///
> > + /// See [`Box::into_pin`] for more details.
> > + fn from(b: Box<T, A>) -> Self {
> > + Box::into_pin(b)
>
> I still think it makes more sense to match std and only provide From
> and not an into_pin, but it's not a blocker.
Yeah, I just kept it since I'm not (yet) entirely sure what to think of the
`From` and `Into` stuff in some cases.
I don't really like that, depending on the context, it may hide relevant
details.
In the kernel, no matter how well documented an API is, I think it's rather
common to look at the code for some implementation details before using it.
Sometimes it might not be super trivial for the "occasional" reader to figure
out what's the type of some variable. Calling `into_pin` vs. just `into`
immediately tells the reader that things need to be pinned from there on.
However, I had no specific example in my mind and I'm also not overly concerned
to remove `into_pin`, but I want to at least share the reason why I kept it in
the first place.
>
> Alice
>
^ permalink raw reply [flat|nested] 95+ messages in thread* Re: [PATCH v5 09/26] rust: alloc: implement kernel `Box`
2024-08-14 12:22 ` Danilo Krummrich
@ 2024-08-14 12:29 ` Alice Ryhl
0 siblings, 0 replies; 95+ messages in thread
From: Alice Ryhl @ 2024-08-14 12:29 UTC (permalink / raw)
To: Danilo Krummrich
Cc: ojeda, alex.gaynor, wedsonaf, boqun.feng, gary, bjorn3_gh,
benno.lossin, a.hindborg, akpm, daniel.almeida, faith.ekstrand,
boris.brezillon, lina, mcanal, zhiw, cjia, jhubbard, airlied,
ajanulgu, lyude, linux-kernel, rust-for-linux, linux-mm
On Wed, Aug 14, 2024 at 2:22 PM Danilo Krummrich <dakr@kernel.org> wrote:
>
> On Wed, Aug 14, 2024 at 10:26:10AM +0200, Alice Ryhl wrote:
> > On Mon, Aug 12, 2024 at 8:25 PM Danilo Krummrich <dakr@kernel.org> wrote:
> > > +impl<T, A> From<Box<T, A>> for Pin<Box<T, A>>
> > > +where
> > > + T: ?Sized,
> > > + A: Allocator,
> > > +{
> > > + /// Converts a `Box<T, A>` into a `Pin<Box<T, A>>`. If `T` does not implement [`Unpin`], then
> > > + /// `*b` will be pinned in memory and can't be moved.
> > > + ///
> > > + /// See [`Box::into_pin`] for more details.
> > > + fn from(b: Box<T, A>) -> Self {
> > > + Box::into_pin(b)
> >
> > I still think it makes more sense to match std and only provide From
> > and not an into_pin, but it's not a blocker.
>
> Yeah, I just kept it since I'm not (yet) entirely sure what to think of the
> `From` and `Into` stuff in some cases.
>
> I don't really like that, depending on the context, it may hide relevant
> details.
>
> In the kernel, no matter how well documented an API is, I think it's rather
> common to look at the code for some implementation details before using it.
>
> Sometimes it might not be super trivial for the "occasional" reader to figure
> out what's the type of some variable. Calling `into_pin` vs. just `into`
> immediately tells the reader that things need to be pinned from there on.
>
> However, I had no specific example in my mind and I'm also not overly concerned
> to remove `into_pin`, but I want to at least share the reason why I kept it in
> the first place.
You can write `Pin::from` to convert the box. I think that reads
reasonably well.
But like I said, not a blocker for me.
Alice
^ permalink raw reply [flat|nested] 95+ messages in thread
* Re: [PATCH v5 09/26] rust: alloc: implement kernel `Box`
2024-08-12 18:22 ` [PATCH v5 09/26] rust: alloc: implement kernel `Box` Danilo Krummrich
2024-08-14 8:26 ` Alice Ryhl
@ 2024-08-14 17:01 ` Benno Lossin
2024-08-14 21:58 ` Danilo Krummrich
1 sibling, 1 reply; 95+ messages in thread
From: Benno Lossin @ 2024-08-14 17:01 UTC (permalink / raw)
To: Danilo Krummrich, ojeda, alex.gaynor, wedsonaf, boqun.feng, gary,
bjorn3_gh, a.hindborg, aliceryhl, akpm
Cc: daniel.almeida, faith.ekstrand, boris.brezillon, lina, mcanal,
zhiw, cjia, jhubbard, airlied, ajanulgu, lyude, linux-kernel,
rust-for-linux, linux-mm
On 12.08.24 20:22, Danilo Krummrich wrote:
> +/// The kernel's [`Box`] type - a heap allocation for a single value of type `T`.
> +///
> +/// This is the kernel's version of the Rust stdlib's `Box`. There are a couple of differences,
> +/// for example no `noalias` attribute is emitted and partially moving out of a `Box` is not
> +/// supported.
I would add "But otherwise it works the same." (I don't know if there is
a comma needed after the "otherwise").
Also I remember that there was one more difference with a custom box
compared to the stdlib, but I forgot what that was, does someone else
remember? We should also put that here.
> +///
> +/// `Box` works with any of the kernel's allocators, e.g. [`super::allocator::Kmalloc`],
> +/// [`super::allocator::Vmalloc`] or [`super::allocator::KVmalloc`]. There are aliases for `Box`
> +/// with these allocators ([`KBox`], [`VBox`], [`KVBox`]).
> +///
> +/// When dropping a [`Box`], the value is also dropped and the heap memory is automatically freed.
> +///
> +/// # Examples
> +///
> +/// ```
> +/// let b = KBox::<u64>::new(24_u64, GFP_KERNEL)?;
> +///
> +/// assert_eq!(*b, 24_u64);
> +///
> +/// # Ok::<(), Error>(())
> +/// ```
> +///
> +/// ```
> +/// # use kernel::bindings;
> +///
> +/// const SIZE: usize = bindings::KMALLOC_MAX_SIZE as usize + 1;
> +/// struct Huge([u8; SIZE]);
> +///
> +/// assert!(KBox::<Huge>::new_uninit(GFP_KERNEL | __GFP_NOWARN).is_err());
> +/// ```
> +///
> +/// ```
> +/// # use kernel::bindings;
> +///
> +/// const SIZE: usize = bindings::KMALLOC_MAX_SIZE as usize + 1;
> +/// struct Huge([u8; SIZE]);
> +///
> +/// assert!(KVBox::<Huge>::new_uninit(GFP_KERNEL).is_ok());
> +/// ```
> +///
> +/// # Invariants
> +///
> +/// The [`Box`]' pointer always properly aligned and either points to memory allocated with `A` or,
"pointer always properly" -> "pointer is properly"
> +/// for zero-sized types, is a dangling pointer.
I think this section would look nicer, if it were formatted using bullet
points (that way the bracketing of the "or" is also unambiguous).
Additionally, this is missing that the pointer is valid for reads and
writes.
> +pub struct Box<T: ?Sized, A: Allocator>(NonNull<T>, PhantomData<A>);
Why no `repr(transparent)`?
> +
> +/// Type alias for `Box` with a `Kmalloc` allocator.
I think we should add that this is only designed for small values.
> +///
> +/// # Examples
> +///
> +/// ```
> +/// let b = KBox::new(24_u64, GFP_KERNEL)?;
> +///
> +/// assert_eq!(*b, 24_u64);
> +///
> +/// # Ok::<(), Error>(())
> +/// ```
> +pub type KBox<T> = Box<T, super::allocator::Kmalloc>;
> +
> +/// Type alias for `Box` with a `Vmalloc` allocator.
Same here, add that this is supposed to be used for big values (or is
this also a general-purpose allocator, just not guaranteeing that the
memory is physically contiguous? in that case I would document it
here and also on `Vmalloc`).
> +///
> +/// # Examples
> +///
> +/// ```
> +/// let b = VBox::new(24_u64, GFP_KERNEL)?;
> +///
> +/// assert_eq!(*b, 24_u64);
> +///
> +/// # Ok::<(), Error>(())
> +/// ```
> +pub type VBox<T> = Box<T, super::allocator::Vmalloc>;
> +
> +/// Type alias for `Box` with a `KVmalloc` allocator.
Ditto.
> +///
> +/// # Examples
> +///
> +/// ```
> +/// let b = KVBox::new(24_u64, GFP_KERNEL)?;
> +///
> +/// assert_eq!(*b, 24_u64);
> +///
> +/// # Ok::<(), Error>(())
> +/// ```
> +pub type KVBox<T> = Box<T, super::allocator::KVmalloc>;
> +
> +// SAFETY: `Box` is `Send` if `T` is `Send` because the data referenced by `self.0` is unaliased.
> +unsafe impl<T, A> Send for Box<T, A>
> +where
> + T: Send + ?Sized,
> + A: Allocator,
> +{
> +}
> +
> +// SAFETY: `Box` is `Sync` if `T` is `Sync` because the data referenced by `self.0` is unaliased.
> +unsafe impl<T, A> Sync for Box<T, A>
> +where
> + T: Send + ?Sized,
> + A: Allocator,
> +{
> +}
> +
> +impl<T, A> Box<T, A>
> +where
> + T: ?Sized,
> + A: Allocator,
> +{
> + /// Creates a new `Box<T, A>` from a raw pointer.
> + ///
> + /// # Safety
> + ///
> + /// `raw` must point to valid memory, previously be allocated with `A`, and provide at least
> + /// the size of type `T`. For ZSTs `raw` must be a dangling pointer.
> + #[inline]
> + pub const unsafe fn from_raw(raw: *mut T) -> Self {
> + // INVARIANT: Validity of `raw` is guaranteed by the safety preconditions of this function.
> + // SAFETY: By the safety preconditions of this function, `raw` is not a NULL pointer.
> + Self(unsafe { NonNull::new_unchecked(raw) }, PhantomData::<A>)
> + }
> +
> + /// Consumes the `Box<T, A>` and returns a raw pointer.
> + ///
> + /// This will not run the destructor of `T` and for non-ZSTs the allocation will stay alive
> + /// indefinitely. Use [`Box::from_raw`] to recover the [`Box`], drop the value and free the
> + /// allocation, if any.
> + ///
> + /// # Examples
> + ///
> + /// ```
> + /// let x = KBox::new(24, GFP_KERNEL)?;
> + /// let ptr = KBox::into_raw(x);
> + /// let x = unsafe { KBox::from_raw(ptr) };
> + ///
> + /// assert_eq!(*x, 24);
> + ///
> + /// # Ok::<(), Error>(())
> + /// ```
> + #[inline]
> + pub fn into_raw(b: Self) -> *mut T {
> + let b = ManuallyDrop::new(b);
> +
> + b.0.as_ptr()
> + }
> +
> + /// Consumes and leaks the `Box<T, A>` and returns a mutable reference.
> + ///
> + /// See [Box::into_raw] for more details.
> + #[inline]
> + pub fn leak<'a>(b: Self) -> &'a mut T
> + where
> + T: 'a,
This bound shouldn't be needed, it is implicit, since `T` appears in the
type `&'a mut T`.
> + {
> + // SAFETY: `Box::into_raw` always returns a properly aligned and dereferenceable pointer
> + // which points to an initialized instance of `T`.
> + unsafe { &mut *Box::into_raw(b) }
> + }
> +
> + /// Converts a `Box<T, A>` into a `Pin<Box<T, A>>`. If `T` does not implement [`Unpin`], then
> + /// `*b` will be pinned in memory and can't be moved.
> + ///
> + /// This moves `b` into `Pin` without moving `*b` or allocating and copying any memory.
> + #[inline]
> + pub fn into_pin(b: Self) -> Pin<Self> {
I would agree with Alice, that this is not really needed, since you can
just as well write `Pin::from(my_box)`.
> + // SAFETY: The value wrapped inside a `Pin<Box<T, A>>` cannot be moved or replaced as long
> + // as `T` does not implement `Unpin`.
> + unsafe { Pin::new_unchecked(b) }
> + }
> +}
> +
> +impl<T, A> Box<MaybeUninit<T>, A>
> +where
> + A: Allocator,
> +{
> + /// Converts a `Box<MaybeUninit<T>, A>` to a `Box<T, A>`.
> + ///
> + /// # Safety
> + ///
> + /// Callers must ensure that the value inside of `b` is in an initialized state. It is
> + /// undefined behavior to call this function while the value inside of `b` is not yet fully
> + /// initialized.
The second sentence is unnecessary safety documentation. It might still
be useful as normal documentation though.
> + pub unsafe fn assume_init(b: Self) -> Box<T, A> {
> + let raw = Self::into_raw(b);
> +
> + // SAFETY: `raw` comes from a previous call to `Box::into_raw`. By the safety requirements
> + // of this function, the value inside the `Box` is in an initialized state. Hence, it is
> + // safe to reconstruct the `Box` as `Box<T, A>`.
> + unsafe { Box::from_raw(raw as *mut T) }
> + }
> +
> + /// 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
> + }
> +
> + /// Creates a new `Box<T, A>` and initializes its contents with `x`.
> + ///
> + /// New memory is allocated with `a`. The allocation may fail, in which case an error is
Wrong case on "`a`" (can this also be a link?).
> + /// returned. For ZSTs no memory is allocated.
> + pub fn new(x: T, flags: Flags) -> Result<Self, AllocError> {
> + let b = Self::new_uninit(flags)?;
> + Ok(Box::write(b, x))
> + }
> +
> + /// Creates a new `Box<T, A>` with uninitialized contents.
> + ///
> + /// New memory is allocated with `a`. The allocation may fail, in which case an error is
Ditto.
> + /// returned. For ZSTs no memory is allocated.
> + ///
> + /// # Examples
> + ///
> + /// ```
> + /// let b = KBox::<u64>::new_uninit(GFP_KERNEL)?;
> + /// let b = KBox::write(b, 24);
> + ///
> + /// assert_eq!(*b, 24_u64);
> + ///
> + /// # Ok::<(), Error>(())
> + /// ```
> + pub fn new_uninit(flags: Flags) -> Result<Box<MaybeUninit<T>, A>, AllocError> {
> + let ptr = if Self::is_zst() {
> + NonNull::dangling()
> + } else {
> + let layout = core::alloc::Layout::new::<MaybeUninit<T>>();
> + let ptr = A::alloc(layout, flags)?;
> +
> + ptr.cast()
> + };
> +
> + Ok(Box(ptr, PhantomData::<A>))
Missing INVARIANT comment.
---
Cheers,
Benno
> + }
^ permalink raw reply [flat|nested] 95+ messages in thread* Re: [PATCH v5 09/26] rust: alloc: implement kernel `Box`
2024-08-14 17:01 ` Benno Lossin
@ 2024-08-14 21:58 ` Danilo Krummrich
2024-08-15 12:44 ` Miguel Ojeda
2024-08-15 13:24 ` Benno Lossin
0 siblings, 2 replies; 95+ messages in thread
From: Danilo Krummrich @ 2024-08-14 21: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, cjia, jhubbard, airlied,
ajanulgu, lyude, linux-kernel, rust-for-linux, linux-mm
On Wed, Aug 14, 2024 at 05:01:34PM +0000, Benno Lossin wrote:
> On 12.08.24 20:22, Danilo Krummrich wrote:
> > +/// The kernel's [`Box`] type - a heap allocation for a single value of type `T`.
> > +///
> > +/// This is the kernel's version of the Rust stdlib's `Box`. There are a couple of differences,
> > +/// for example no `noalias` attribute is emitted and partially moving out of a `Box` is not
> > +/// supported.
>
> I would add "But otherwise it works the same." (I don't know if there is
> a comma needed after the "otherwise").
There are more differences we don't list here, and probably don't need to.
Hence, saying that it otherwise works the same isn't correct.
> Also I remember that there was one more difference with a custom box
> compared to the stdlib, but I forgot what that was, does someone else
> remember? We should also put that here.
Obviously, there are also quite some API differences. For instance, `Box`
generally requires two generics, value type and allocator, we take page flags
and return a `Result`, where std just panics on failure.
>
> > +///
> > +/// `Box` works with any of the kernel's allocators, e.g. [`super::allocator::Kmalloc`],
> > +/// [`super::allocator::Vmalloc`] or [`super::allocator::KVmalloc`]. There are aliases for `Box`
> > +/// with these allocators ([`KBox`], [`VBox`], [`KVBox`]).
> > +///
> > +/// When dropping a [`Box`], the value is also dropped and the heap memory is automatically freed.
> > +///
> > +/// # Examples
> > +///
> > +/// ```
> > +/// let b = KBox::<u64>::new(24_u64, GFP_KERNEL)?;
> > +///
> > +/// assert_eq!(*b, 24_u64);
> > +///
> > +/// # Ok::<(), Error>(())
> > +/// ```
> > +///
> > +/// ```
> > +/// # use kernel::bindings;
> > +///
> > +/// const SIZE: usize = bindings::KMALLOC_MAX_SIZE as usize + 1;
> > +/// struct Huge([u8; SIZE]);
> > +///
> > +/// assert!(KBox::<Huge>::new_uninit(GFP_KERNEL | __GFP_NOWARN).is_err());
> > +/// ```
> > +///
> > +/// ```
> > +/// # use kernel::bindings;
> > +///
> > +/// const SIZE: usize = bindings::KMALLOC_MAX_SIZE as usize + 1;
> > +/// struct Huge([u8; SIZE]);
> > +///
> > +/// assert!(KVBox::<Huge>::new_uninit(GFP_KERNEL).is_ok());
> > +/// ```
> > +///
> > +/// # Invariants
> > +///
> > +/// The [`Box`]' pointer always properly aligned and either points to memory allocated with `A` or,
>
> "pointer always properly" -> "pointer is properly"
>
> > +/// for zero-sized types, is a dangling pointer.
>
> I think this section would look nicer, if it were formatted using bullet
> points (that way the bracketing of the "or" is also unambiguous).
>
> Additionally, this is missing that the pointer is valid for reads and
> writes.
>
> > +pub struct Box<T: ?Sized, A: Allocator>(NonNull<T>, PhantomData<A>);
>
> Why no `repr(transparent)`?
I wasn't entirely sure whether that's OK with the additional `PhantomData`, but
I think it is, gonna add it.
>
> > +
> > +/// Type alias for `Box` with a `Kmalloc` allocator.
>
> I think we should add that this is only designed for small values.
I don't want duplicate the existing documentation around kmalloc and friends
[1].
Maybe we can refer to the existing documentation somehow.
[1] https://www.kernel.org/doc/html/latest/core-api/memory-allocation.html
>
> > +///
> > +/// # Examples
> > +///
> > +/// ```
> > +/// let b = KBox::new(24_u64, GFP_KERNEL)?;
> > +///
> > +/// assert_eq!(*b, 24_u64);
> > +///
> > +/// # Ok::<(), Error>(())
> > +/// ```
> > +pub type KBox<T> = Box<T, super::allocator::Kmalloc>;
> > +
> > +/// Type alias for `Box` with a `Vmalloc` allocator.
>
> Same here, add that this is supposed to be used for big values (or is
> this also a general-purpose allocator, just not guaranteeing that the
> memory is physically contiguous? in that case I would document it
> here and also on `Vmalloc`).
Same as above, I'd rather not duplicate that. But I'm happy to link things in,
just not sure what's the best way doing it.
^ permalink raw reply [flat|nested] 95+ messages in thread
* Re: [PATCH v5 09/26] rust: alloc: implement kernel `Box`
2024-08-14 21:58 ` Danilo Krummrich
@ 2024-08-15 12:44 ` Miguel Ojeda
2024-08-15 13:24 ` Benno Lossin
1 sibling, 0 replies; 95+ messages in thread
From: Miguel Ojeda @ 2024-08-15 12:44 UTC (permalink / raw)
To: Danilo Krummrich
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, cjia,
jhubbard, airlied, ajanulgu, lyude, linux-kernel, rust-for-linux,
linux-mm
On Wed, Aug 14, 2024 at 11:58 PM Danilo Krummrich <dakr@kernel.org> wrote:
>
> I don't want duplicate the existing documentation around kmalloc and friends
> [1].
>
> Maybe we can refer to the existing documentation somehow.
>
> [1] https://www.kernel.org/doc/html/latest/core-api/memory-allocation.html
Yeah, that is always a good idea. Sometimes we use the "Reference:
<https://...>" patterns, sometimes we linked to C
functions/macros/etc. too (as if they were intra-doc links).
In the future, the plan is to have the C side docs connected as
"external references" via a new `rustdoc` feature, so that we can just
write intra-doc links that "magically" resolve to the right place to
the C docs.
By the way, please use links to docs.kernel.org if possible, they are
a bit nicer/shorter.
Cheers,
Miguel
^ permalink raw reply [flat|nested] 95+ messages in thread
* Re: [PATCH v5 09/26] rust: alloc: implement kernel `Box`
2024-08-14 21:58 ` Danilo Krummrich
2024-08-15 12:44 ` Miguel Ojeda
@ 2024-08-15 13:24 ` Benno Lossin
2024-08-15 14:00 ` Danilo Krummrich
1 sibling, 1 reply; 95+ messages in thread
From: Benno Lossin @ 2024-08-15 13:24 UTC (permalink / raw)
To: Danilo Krummrich
Cc: ojeda, alex.gaynor, wedsonaf, boqun.feng, gary, bjorn3_gh,
a.hindborg, aliceryhl, akpm, daniel.almeida, faith.ekstrand,
boris.brezillon, lina, mcanal, zhiw, cjia, jhubbard, airlied,
ajanulgu, lyude, linux-kernel, rust-for-linux, linux-mm
On 14.08.24 23:58, Danilo Krummrich wrote:
> On Wed, Aug 14, 2024 at 05:01:34PM +0000, Benno Lossin wrote:
>> On 12.08.24 20:22, Danilo Krummrich wrote:
>>> +/// The kernel's [`Box`] type - a heap allocation for a single value of type `T`.
>>> +///
>>> +/// This is the kernel's version of the Rust stdlib's `Box`. There are a couple of differences,
>>> +/// for example no `noalias` attribute is emitted and partially moving out of a `Box` is not
>>> +/// supported.
>>
>> I would add "But otherwise it works the same." (I don't know if there is
>> a comma needed after the "otherwise").
>
> There are more differences we don't list here, and probably don't need to.
> Hence, saying that it otherwise works the same isn't correct.
>
>> Also I remember that there was one more difference with a custom box
>> compared to the stdlib, but I forgot what that was, does someone else
>> remember? We should also put that here.
>
> Obviously, there are also quite some API differences. For instance, `Box`
> generally requires two generics, value type and allocator, we take page flags
> and return a `Result`, where std just panics on failure.
Oh yeah that's true. The things listed above don't really refer to API
stuff, so I didn't consider that. How about changing "couple
differences" to "several differences"? Also adding that the APIs are
different would not hurt.
>>> +///
>>> +/// `Box` works with any of the kernel's allocators, e.g. [`super::allocator::Kmalloc`],
>>> +/// [`super::allocator::Vmalloc`] or [`super::allocator::KVmalloc`]. There are aliases for `Box`
>>> +/// with these allocators ([`KBox`], [`VBox`], [`KVBox`]).
>>> +///
>>> +/// When dropping a [`Box`], the value is also dropped and the heap memory is automatically freed.
>>> +///
>>> +/// # Examples
>>> +///
>>> +/// ```
>>> +/// let b = KBox::<u64>::new(24_u64, GFP_KERNEL)?;
>>> +///
>>> +/// assert_eq!(*b, 24_u64);
>>> +///
>>> +/// # Ok::<(), Error>(())
>>> +/// ```
>>> +///
>>> +/// ```
>>> +/// # use kernel::bindings;
>>> +///
>>> +/// const SIZE: usize = bindings::KMALLOC_MAX_SIZE as usize + 1;
>>> +/// struct Huge([u8; SIZE]);
>>> +///
>>> +/// assert!(KBox::<Huge>::new_uninit(GFP_KERNEL | __GFP_NOWARN).is_err());
>>> +/// ```
>>> +///
>>> +/// ```
>>> +/// # use kernel::bindings;
>>> +///
>>> +/// const SIZE: usize = bindings::KMALLOC_MAX_SIZE as usize + 1;
>>> +/// struct Huge([u8; SIZE]);
>>> +///
>>> +/// assert!(KVBox::<Huge>::new_uninit(GFP_KERNEL).is_ok());
>>> +/// ```
>>> +///
>>> +/// # Invariants
>>> +///
>>> +/// The [`Box`]' pointer always properly aligned and either points to memory allocated with `A` or,
>>
>> "pointer always properly" -> "pointer is properly"
>>
>>> +/// for zero-sized types, is a dangling pointer.
>>
>> I think this section would look nicer, if it were formatted using bullet
>> points (that way the bracketing of the "or" is also unambiguous).
>>
>> Additionally, this is missing that the pointer is valid for reads and
>> writes.
>>
>>> +pub struct Box<T: ?Sized, A: Allocator>(NonNull<T>, PhantomData<A>);
>>
>> Why no `repr(transparent)`?
>
> I wasn't entirely sure whether that's OK with the additional `PhantomData`, but
> I think it is, gonna add it.
Yes it is fine, `repr(transparent)` requires that at most one field is
non-ZST, but the type can have as many ZST fields as it wants.
Otherwise the compiler will complain (there is no `unsafe` here, so just
adding it is completely fine).
>>> +
>>> +/// Type alias for `Box` with a `Kmalloc` allocator.
>>
>> I think we should add that this is only designed for small values.
>
> I don't want duplicate the existing documentation around kmalloc and friends
> [1].
>
> Maybe we can refer to the existing documentation somehow.
>
> [1] https://www.kernel.org/doc/html/latest/core-api/memory-allocation.html
Oh great! With the C docs, I never know where to find them (is it in the
code and do they exist?). Yeah let's just link it.
>>> +///
>>> +/// # Examples
>>> +///
>>> +/// ```
>>> +/// let b = KBox::new(24_u64, GFP_KERNEL)?;
>>> +///
>>> +/// assert_eq!(*b, 24_u64);
>>> +///
>>> +/// # Ok::<(), Error>(())
>>> +/// ```
>>> +pub type KBox<T> = Box<T, super::allocator::Kmalloc>;
>>> +
>>> +/// Type alias for `Box` with a `Vmalloc` allocator.
>>
>> Same here, add that this is supposed to be used for big values (or is
>> this also a general-purpose allocator, just not guaranteeing that the
>> memory is physically contiguous? in that case I would document it
>> here and also on `Vmalloc`).
>
> Same as above, I'd rather not duplicate that. But I'm happy to link things in,
> just not sure what's the best way doing it.
I took a look at the link and there is the "Selecting memory allocator"
section, but there isn't really just a vmalloc or kmalloc section, it is
rather stuff that we would put in the module documentation.
What I would write on these types would be what to use these boxes for.
eg large allocations, general purpose etc. I don't think that that is
easily accessible from the docs that you linked above.
---
Cheers,
Benno
^ permalink raw reply [flat|nested] 95+ messages in thread
* Re: [PATCH v5 09/26] rust: alloc: implement kernel `Box`
2024-08-15 13:24 ` Benno Lossin
@ 2024-08-15 14:00 ` Danilo Krummrich
2024-08-15 14:10 ` Benno Lossin
0 siblings, 1 reply; 95+ messages in thread
From: Danilo Krummrich @ 2024-08-15 14:00 UTC (permalink / raw)
To: Benno Lossin
Cc: ojeda, alex.gaynor, wedsonaf, boqun.feng, gary, bjorn3_gh,
a.hindborg, aliceryhl, akpm, daniel.almeida, faith.ekstrand,
boris.brezillon, lina, mcanal, zhiw, cjia, jhubbard, airlied,
ajanulgu, lyude, linux-kernel, rust-for-linux, linux-mm
On Thu, Aug 15, 2024 at 01:24:47PM +0000, Benno Lossin wrote:
> On 14.08.24 23:58, Danilo Krummrich wrote:
> > On Wed, Aug 14, 2024 at 05:01:34PM +0000, Benno Lossin wrote:
> >> On 12.08.24 20:22, Danilo Krummrich wrote:
> >>> +/// The kernel's [`Box`] type - a heap allocation for a single value of type `T`.
> >>> +///
> >>> +/// This is the kernel's version of the Rust stdlib's `Box`. There are a couple of differences,
> >>> +/// for example no `noalias` attribute is emitted and partially moving out of a `Box` is not
> >>> +/// supported.
> >>
> >> I would add "But otherwise it works the same." (I don't know if there is
> >> a comma needed after the "otherwise").
> >
> > There are more differences we don't list here, and probably don't need to.
> > Hence, saying that it otherwise works the same isn't correct.
> >
> >> Also I remember that there was one more difference with a custom box
> >> compared to the stdlib, but I forgot what that was, does someone else
> >> remember? We should also put that here.
> >
> > Obviously, there are also quite some API differences. For instance, `Box`
> > generally requires two generics, value type and allocator, we take page flags
> > and return a `Result`, where std just panics on failure.
>
> Oh yeah that's true. The things listed above don't really refer to API
> stuff, so I didn't consider that. How about changing "couple
> differences" to "several differences"? Also adding that the APIs are
> different would not hurt.
>
Sure.
>
> >>> +
> >>> +/// Type alias for `Box` with a `Kmalloc` allocator.
> >>
> >> I think we should add that this is only designed for small values.
> >
> > I don't want duplicate the existing documentation around kmalloc and friends
> > [1].
> >
> > Maybe we can refer to the existing documentation somehow.
> >
> > [1] https://www.kernel.org/doc/html/latest/core-api/memory-allocation.html
>
> Oh great! With the C docs, I never know where to find them (is it in the
> code and do they exist?). Yeah let's just link it.
>
> >>> +///
> >>> +/// # Examples
> >>> +///
> >>> +/// ```
> >>> +/// let b = KBox::new(24_u64, GFP_KERNEL)?;
> >>> +///
> >>> +/// assert_eq!(*b, 24_u64);
> >>> +///
> >>> +/// # Ok::<(), Error>(())
> >>> +/// ```
> >>> +pub type KBox<T> = Box<T, super::allocator::Kmalloc>;
> >>> +
> >>> +/// Type alias for `Box` with a `Vmalloc` allocator.
> >>
> >> Same here, add that this is supposed to be used for big values (or is
> >> this also a general-purpose allocator, just not guaranteeing that the
> >> memory is physically contiguous? in that case I would document it
> >> here and also on `Vmalloc`).
> >
> > Same as above, I'd rather not duplicate that. But I'm happy to link things in,
> > just not sure what's the best way doing it.
>
> I took a look at the link and there is the "Selecting memory allocator"
> section, but there isn't really just a vmalloc or kmalloc section, it is
> rather stuff that we would put in the module documentation.
There are no dedicated sections, but...
> What I would write on these types would be what to use these boxes for.
> eg large allocations, general purpose etc. I don't think that that is
> easily accessible from the docs that you linked above.
...this stuff should be covered by the document, e.g.:
"The maximal size of a chunk that can be allocated with kmalloc is limited. The
actual limit depends on the hardware and the kernel configuration, but it is a
good practice to use kmalloc for objects smaller than page size."
or
"For large allocations you can use vmalloc() and vzalloc(), or directly request
pages from the page allocator. The memory allocated by vmalloc and related
functions is not physically contiguous."
I'd probably reference [1] in the module documentation as you say and otherwise
refer to the C APIs, just like `RBTree` does.
>
> ---
> Cheers,
> Benno
>
^ permalink raw reply [flat|nested] 95+ messages in thread
* Re: [PATCH v5 09/26] rust: alloc: implement kernel `Box`
2024-08-15 14:00 ` Danilo Krummrich
@ 2024-08-15 14:10 ` Benno Lossin
2024-08-15 14:17 ` Danilo Krummrich
0 siblings, 1 reply; 95+ messages in thread
From: Benno Lossin @ 2024-08-15 14:10 UTC (permalink / raw)
To: Danilo Krummrich
Cc: ojeda, alex.gaynor, wedsonaf, boqun.feng, gary, bjorn3_gh,
a.hindborg, aliceryhl, akpm, daniel.almeida, faith.ekstrand,
boris.brezillon, lina, mcanal, zhiw, cjia, jhubbard, airlied,
ajanulgu, lyude, linux-kernel, rust-for-linux, linux-mm
On 15.08.24 16:00, Danilo Krummrich wrote:
> On Thu, Aug 15, 2024 at 01:24:47PM +0000, Benno Lossin wrote:
>> On 14.08.24 23:58, Danilo Krummrich wrote:
>>> On Wed, Aug 14, 2024 at 05:01:34PM +0000, Benno Lossin wrote:
>>>> On 12.08.24 20:22, Danilo Krummrich wrote:
>>>>> +///
>>>>> +/// # Examples
>>>>> +///
>>>>> +/// ```
>>>>> +/// let b = KBox::new(24_u64, GFP_KERNEL)?;
>>>>> +///
>>>>> +/// assert_eq!(*b, 24_u64);
>>>>> +///
>>>>> +/// # Ok::<(), Error>(())
>>>>> +/// ```
>>>>> +pub type KBox<T> = Box<T, super::allocator::Kmalloc>;
>>>>> +
>>>>> +/// Type alias for `Box` with a `Vmalloc` allocator.
>>>>
>>>> Same here, add that this is supposed to be used for big values (or is
>>>> this also a general-purpose allocator, just not guaranteeing that the
>>>> memory is physically contiguous? in that case I would document it
>>>> here and also on `Vmalloc`).
>>>
>>> Same as above, I'd rather not duplicate that. But I'm happy to link things in,
>>> just not sure what's the best way doing it.
>>
>> I took a look at the link and there is the "Selecting memory allocator"
>> section, but there isn't really just a vmalloc or kmalloc section, it is
>> rather stuff that we would put in the module documentation.
>
> There are no dedicated sections, but...
>
>> What I would write on these types would be what to use these boxes for.
>> eg large allocations, general purpose etc. I don't think that that is
>> easily accessible from the docs that you linked above.
>
> ...this stuff should be covered by the document, e.g.:
>
> "The maximal size of a chunk that can be allocated with kmalloc is limited. The
> actual limit depends on the hardware and the kernel configuration, but it is a
> good practice to use kmalloc for objects smaller than page size."
>
> or
>
> "For large allocations you can use vmalloc() and vzalloc(), or directly request
> pages from the page allocator. The memory allocated by vmalloc and related
> functions is not physically contiguous."
Yeah, but for that you have to read big chunks of the document and item
docs in Rust are often very short, since you only need to know what that
one item does.
Would be a good idea to talk about how we can improve this at Kangrejos.
---
Cheers,
Benno
^ permalink raw reply [flat|nested] 95+ messages in thread
* Re: [PATCH v5 09/26] rust: alloc: implement kernel `Box`
2024-08-15 14:10 ` Benno Lossin
@ 2024-08-15 14:17 ` Danilo Krummrich
0 siblings, 0 replies; 95+ messages in thread
From: Danilo Krummrich @ 2024-08-15 14:17 UTC (permalink / raw)
To: Benno Lossin
Cc: ojeda, alex.gaynor, wedsonaf, boqun.feng, gary, bjorn3_gh,
a.hindborg, aliceryhl, akpm, daniel.almeida, faith.ekstrand,
boris.brezillon, lina, mcanal, zhiw, cjia, jhubbard, airlied,
ajanulgu, lyude, linux-kernel, rust-for-linux, linux-mm
On 8/15/24 4:10 PM, Benno Lossin wrote:
> On 15.08.24 16:00, Danilo Krummrich wrote:
>> On Thu, Aug 15, 2024 at 01:24:47PM +0000, Benno Lossin wrote:
>>> On 14.08.24 23:58, Danilo Krummrich wrote:
>>>> On Wed, Aug 14, 2024 at 05:01:34PM +0000, Benno Lossin wrote:
>>>>> On 12.08.24 20:22, Danilo Krummrich wrote:
>>>>>> +///
>>>>>> +/// # Examples
>>>>>> +///
>>>>>> +/// ```
>>>>>> +/// let b = KBox::new(24_u64, GFP_KERNEL)?;
>>>>>> +///
>>>>>> +/// assert_eq!(*b, 24_u64);
>>>>>> +///
>>>>>> +/// # Ok::<(), Error>(())
>>>>>> +/// ```
>>>>>> +pub type KBox<T> = Box<T, super::allocator::Kmalloc>;
>>>>>> +
>>>>>> +/// Type alias for `Box` with a `Vmalloc` allocator.
>>>>>
>>>>> Same here, add that this is supposed to be used for big values (or is
>>>>> this also a general-purpose allocator, just not guaranteeing that the
>>>>> memory is physically contiguous? in that case I would document it
>>>>> here and also on `Vmalloc`).
>>>>
>>>> Same as above, I'd rather not duplicate that. But I'm happy to link things in,
>>>> just not sure what's the best way doing it.
>>>
>>> I took a look at the link and there is the "Selecting memory allocator"
>>> section, but there isn't really just a vmalloc or kmalloc section, it is
>>> rather stuff that we would put in the module documentation.
>>
>> There are no dedicated sections, but...
>>
>>> What I would write on these types would be what to use these boxes for.
>>> eg large allocations, general purpose etc. I don't think that that is
>>> easily accessible from the docs that you linked above.
>>
>> ...this stuff should be covered by the document, e.g.:
>>
>> "The maximal size of a chunk that can be allocated with kmalloc is limited. The
>> actual limit depends on the hardware and the kernel configuration, but it is a
>> good practice to use kmalloc for objects smaller than page size."
>>
>> or
>>
>> "For large allocations you can use vmalloc() and vzalloc(), or directly request
>> pages from the page allocator. The memory allocated by vmalloc and related
>> functions is not physically contiguous."
>
> Yeah, but for that you have to read big chunks of the document and item
> docs in Rust are often very short, since you only need to know what that
> one item does.
In this specific case I'd argue that the contents of this document should be
read and understood by everyone who attempts to write kernel code anyways,
hence linking it should be fine.
> Would be a good idea to talk about how we can improve this at Kangrejos.
>
> ---
> Cheers,
> Benno
>
^ permalink raw reply [flat|nested] 95+ messages in thread
* [PATCH v5 10/26] rust: treewide: switch to our kernel `Box` type
2024-08-12 18:22 [PATCH v5 00/26] Generic `Allocator` support for Rust Danilo Krummrich
` (8 preceding siblings ...)
2024-08-12 18:22 ` [PATCH v5 09/26] rust: alloc: implement kernel `Box` Danilo Krummrich
@ 2024-08-12 18:22 ` Danilo Krummrich
2024-08-12 18:22 ` [PATCH v5 11/26] rust: alloc: remove `BoxExt` extension Danilo Krummrich
` (16 subsequent siblings)
26 siblings, 0 replies; 95+ messages in thread
From: Danilo Krummrich @ 2024-08-12 18:22 UTC (permalink / raw)
To: ojeda, alex.gaynor, wedsonaf, boqun.feng, gary, bjorn3_gh,
benno.lossin, a.hindborg, aliceryhl, akpm
Cc: daniel.almeida, faith.ekstrand, boris.brezillon, lina, mcanal,
zhiw, cjia, jhubbard, airlied, ajanulgu, lyude, linux-kernel,
rust-for-linux, linux-mm, Danilo Krummrich
Now that we got the kernel `Box` type in place, convert all existing
`Box` users to make use of it.
Reviewed-by: Alice Ryhl <aliceryhl@google.com>
Signed-off-by: Danilo Krummrich <dakr@kernel.org>
---
drivers/block/rnull.rs | 4 +--
rust/kernel/init.rs | 51 ++++++++++++++++---------------
rust/kernel/init/__internal.rs | 2 +-
rust/kernel/sync/arc.rs | 17 +++++------
rust/kernel/sync/condvar.rs | 4 +--
rust/kernel/sync/lock/mutex.rs | 2 +-
rust/kernel/sync/lock/spinlock.rs | 2 +-
rust/kernel/workqueue.rs | 20 ++++++------
rust/macros/lib.rs | 6 ++--
9 files changed, 54 insertions(+), 54 deletions(-)
diff --git a/drivers/block/rnull.rs b/drivers/block/rnull.rs
index b0227cf9ddd3..5de7223beb4d 100644
--- a/drivers/block/rnull.rs
+++ b/drivers/block/rnull.rs
@@ -32,7 +32,7 @@
}
struct NullBlkModule {
- _disk: Pin<Box<Mutex<GenDisk<NullBlkDevice>>>>,
+ _disk: Pin<KBox<Mutex<GenDisk<NullBlkDevice>>>>,
}
impl kernel::Module for NullBlkModule {
@@ -47,7 +47,7 @@ fn init(_module: &'static ThisModule) -> Result<Self> {
.rotational(false)
.build(format_args!("rnullb{}", 0), tagset)?;
- let disk = Box::pin_init(new_mutex!(disk, "nullb:disk"), flags::GFP_KERNEL)?;
+ let disk = KBox::pin_init(new_mutex!(disk, "nullb:disk"), flags::GFP_KERNEL)?;
Ok(Self { _disk: disk })
}
diff --git a/rust/kernel/init.rs b/rust/kernel/init.rs
index 495c09ebe3a3..d3639243f8c7 100644
--- a/rust/kernel/init.rs
+++ b/rust/kernel/init.rs
@@ -13,7 +13,7 @@
//! To initialize a `struct` with an in-place constructor you will need two things:
//! - an in-place constructor,
//! - a memory location that can hold your `struct` (this can be the [stack], an [`Arc<T>`],
-//! [`UniqueArc<T>`], [`Box<T>`] or any other smart pointer that implements [`InPlaceInit`]).
+//! [`UniqueArc<T>`], [`KBox<T>`] or any other smart pointer that implements [`InPlaceInit`]).
//!
//! To get an in-place constructor there are generally three options:
//! - directly creating an in-place constructor using the [`pin_init!`] macro,
@@ -68,7 +68,7 @@
//! # a <- new_mutex!(42, "Foo::a"),
//! # b: 24,
//! # });
-//! let foo: Result<Pin<Box<Foo>>> = Box::pin_init(foo, GFP_KERNEL);
+//! let foo: Result<Pin<KBox<Foo>>> = KBox::pin_init(foo, GFP_KERNEL);
//! ```
//!
//! For more information see the [`pin_init!`] macro.
@@ -93,14 +93,14 @@
//! struct DriverData {
//! #[pin]
//! status: Mutex<i32>,
-//! buffer: Box<[u8; 1_000_000]>,
+//! buffer: KBox<[u8; 1_000_000]>,
//! }
//!
//! impl DriverData {
//! fn new() -> impl PinInit<Self, Error> {
//! try_pin_init!(Self {
//! status <- new_mutex!(0, "DriverData::status"),
-//! buffer: Box::init(kernel::init::zeroed(), GFP_KERNEL)?,
+//! buffer: KBox::init(kernel::init::zeroed(), GFP_KERNEL)?,
//! })
//! }
//! }
@@ -211,7 +211,7 @@
//! [`pin_init!`]: crate::pin_init!
use crate::{
- alloc::{box_ext::BoxExt, AllocError, Flags},
+ alloc::{box_ext::BoxExt, AllocError, Flags, KBox},
error::{self, Error},
sync::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)
/// }
@@ -745,8 +745,8 @@ macro_rules! try_init {
/// A pin-initializer for the type `T`.
///
/// To use this initializer, you will need a suitable memory location that can hold a `T`. This can
-/// be [`Box<T>`], [`Arc<T>`], [`UniqueArc<T>`] or even the stack (see [`stack_pin_init!`]). Use the
-/// [`InPlaceInit::pin_init`] function of a smart pointer like [`Arc<T>`] on this.
+/// be [`KBox<T>`], [`Arc<T>`], [`UniqueArc<T>`] or even the stack (see [`stack_pin_init!`]). Use
+/// the [`InPlaceInit::pin_init`] function of a smart pointer like [`Arc<T>`] on this.
///
/// Also see the [module description](self).
///
@@ -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,
@@ -851,8 +851,8 @@ unsafe fn __pinned_init(self, slot: *mut T) -> Result<(), E> {
/// An initializer for `T`.
///
/// To use this initializer, you will need a suitable memory location that can hold a `T`. This can
-/// be [`Box<T>`], [`Arc<T>`], [`UniqueArc<T>`] or even the stack (see [`stack_pin_init!`]). Use the
-/// [`InPlaceInit::init`] function of a smart pointer like [`Arc<T>`] on this. Because
+/// be [`KBox<T>`], [`Arc<T>`], [`UniqueArc<T>`] or even the stack (see [`stack_pin_init!`]). Use
+/// the [`InPlaceInit::init`] function of a smart pointer like [`Arc<T>`] on this. Because
/// [`PinInit<T, E>`] is a super trait, you can use every function that takes it as well.
///
/// Also see the [module description](self).
@@ -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>(
@@ -1316,7 +1317,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) };
diff --git a/rust/macros/lib.rs b/rust/macros/lib.rs
index 159e75292970..c327fe0617c8 100644
--- a/rust/macros/lib.rs
+++ b/rust/macros/lib.rs
@@ -239,7 +239,7 @@ pub fn concat_idents(ts: TokenStream) -> TokenStream {
/// struct DriverData {
/// #[pin]
/// queue: Mutex<Vec<Command>>,
-/// buf: Box<[u8; 1024 * 1024]>,
+/// buf: KBox<[u8; 1024 * 1024]>,
/// }
/// ```
///
@@ -248,7 +248,7 @@ pub fn concat_idents(ts: TokenStream) -> TokenStream {
/// struct DriverData {
/// #[pin]
/// queue: Mutex<Vec<Command>>,
-/// buf: Box<[u8; 1024 * 1024]>,
+/// buf: KBox<[u8; 1024 * 1024]>,
/// raw_info: *mut Info,
/// }
///
@@ -278,7 +278,7 @@ pub fn pin_data(inner: TokenStream, item: TokenStream) -> TokenStream {
/// struct DriverData {
/// #[pin]
/// queue: Mutex<Vec<Command>>,
-/// buf: Box<[u8; 1024 * 1024]>,
+/// buf: KBox<[u8; 1024 * 1024]>,
/// raw_info: *mut Info,
/// }
///
--
2.45.2
^ permalink raw reply related [flat|nested] 95+ messages in thread* [PATCH v5 11/26] rust: alloc: remove `BoxExt` extension
2024-08-12 18:22 [PATCH v5 00/26] Generic `Allocator` support for Rust Danilo Krummrich
` (9 preceding siblings ...)
2024-08-12 18:22 ` [PATCH v5 10/26] rust: treewide: switch to our kernel `Box` type Danilo Krummrich
@ 2024-08-12 18:22 ` Danilo Krummrich
2024-08-14 11:55 ` Dirk Behme
2024-08-12 18:22 ` [PATCH v5 12/26] rust: alloc: add `Box` to prelude Danilo Krummrich
` (15 subsequent siblings)
26 siblings, 1 reply; 95+ messages in thread
From: Danilo Krummrich @ 2024-08-12 18:22 UTC (permalink / raw)
To: ojeda, alex.gaynor, wedsonaf, boqun.feng, gary, bjorn3_gh,
benno.lossin, a.hindborg, aliceryhl, akpm
Cc: daniel.almeida, faith.ekstrand, boris.brezillon, lina, mcanal,
zhiw, cjia, jhubbard, airlied, ajanulgu, lyude, linux-kernel,
rust-for-linux, linux-mm, Danilo Krummrich
Now that all existing `Box` users were moved to the kernel `Box` type,
remove the `BoxExt` extension and all other related extensions.
Reviewed-by: Alice Ryhl <aliceryhl@google.com>
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 ed46b69204d0..47cab5bd803e 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 d3639243f8c7..eb55787eddee 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, Flags, KBox},
+ alloc::{AllocError, Flags, KBox},
error::{self, Error},
sync::UniqueArc,
types::{Opaque, ScopeGuard},
};
-use alloc::boxed::Box;
use core::{
cell::UnsafeCell,
convert::Infallible,
@@ -589,7 +588,6 @@ macro_rules! pin_init {
/// # Examples
///
/// ```rust
-/// # #![feature(new_uninit)]
/// use kernel::{init::{self, PinInit}, error::Error};
/// #[pin_data]
/// struct BigBuf {
@@ -1149,36 +1147,6 @@ fn init<E>(init: impl Init<T, E>, flags: Flags) -> error::Result<Self>
}
}
-impl<T> InPlaceInit<T> for Box<T> {
- #[inline]
- fn try_pin_init<E>(init: impl PinInit<T, E>, flags: Flags) -> Result<Pin<Self>, E>
- where
- E: From<AllocError>,
- {
- let mut this = <Box<_> as BoxExt<_>>::new_uninit(flags)?;
- let slot = this.as_mut_ptr();
- // SAFETY: When init errors/panics, slot will get deallocated but not dropped,
- // slot is valid and will not be moved, because we pin it later.
- unsafe { init.__pinned_init(slot)? };
- // SAFETY: All fields have been initialized.
- Ok(unsafe { this.assume_init() }.into())
- }
-
- #[inline]
- fn try_init<E>(init: impl Init<T, E>, flags: Flags) -> Result<Self, E>
- where
- E: From<AllocError>,
- {
- let mut this = <Box<_> as BoxExt<_>>::new_uninit(flags)?;
- let slot = this.as_mut_ptr();
- // SAFETY: When init errors/panics, slot will get deallocated but not dropped,
- // slot is valid.
- unsafe { init.__init(slot)? };
- // SAFETY: All fields have been initialized.
- Ok(unsafe { this.assume_init() })
- }
-}
-
impl<T> InPlaceInit<T> for UniqueArc<T> {
#[inline]
fn try_pin_init<E>(init: impl PinInit<T, E>, flags: Flags) -> Result<Pin<Self>, E>
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 bd189d646adb..f48704db33c1 100644
--- a/rust/kernel/types.rs
+++ b/rust/kernel/types.rs
@@ -3,7 +3,6 @@
//! Kernel types.
use crate::init::{self, PinInit};
-use alloc::boxed::Box;
use core::{
cell::UnsafeCell,
marker::{PhantomData, PhantomPinned},
@@ -67,28 +66,6 @@ unsafe fn try_from_foreign(ptr: *const core::ffi::c_void) -> Option<Self> {
}
}
-impl<T: 'static> ForeignOwnable for Box<T> {
- type Borrowed<'a> = &'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 ForeignOwnable for () {
type Borrowed<'a> = ();
--
2.45.2
^ permalink raw reply related [flat|nested] 95+ messages in thread* Re: [PATCH v5 11/26] rust: alloc: remove `BoxExt` extension
2024-08-12 18:22 ` [PATCH v5 11/26] rust: alloc: remove `BoxExt` extension Danilo Krummrich
@ 2024-08-14 11:55 ` Dirk Behme
2024-08-14 12:08 ` Danilo Krummrich
0 siblings, 1 reply; 95+ messages in thread
From: Dirk Behme @ 2024-08-14 11:55 UTC (permalink / raw)
To: Danilo Krummrich, ojeda, alex.gaynor, wedsonaf, boqun.feng, gary,
bjorn3_gh, benno.lossin, a.hindborg, aliceryhl, akpm
Cc: daniel.almeida, faith.ekstrand, boris.brezillon, lina, mcanal,
zhiw, cjia, jhubbard, airlied, ajanulgu, lyude, linux-kernel,
rust-for-linux, linux-mm
On 12.08.2024 20:22, Danilo Krummrich wrote:
> Now that all existing `Box` users were moved to the kernel `Box` type,
> remove the `BoxExt` extension and all other related extensions.
I just noticed that in the recent 'rust-dev' branch we have a change
which *adds* something to BoxExt:
rust: kernel: add drop_contents to BoxExt
https://github.com/Rust-for-Linux/linux/commit/62c34da1da6c01a635ea2308cb42996d0571059e
I'm unclear how relevant that is. Just want to mention this in case it
would make sense to include that directly in this patch series to avoid
a future add-on patch ;)
Thanks,
Dirk
P.S.: It looks like anything like this at least makes the compiler happy:
diff --git a/rust/kernel/alloc/kbox.rs b/rust/kernel/alloc/kbox.rs
index d67f975502246..e91d441835d54 100644
--- a/rust/kernel/alloc/kbox.rs
+++ b/rust/kernel/alloc/kbox.rs
@@ -9,6 +9,7 @@
use core::mem::MaybeUninit;
use core::ops::{Deref, DerefMut};
use core::pin::Pin;
+use core::ptr;
use core::ptr::NonNull;
use core::result::Result;
@@ -270,6 +271,28 @@ pub fn new_uninit(flags: Flags) ->
Result<Box<MaybeUninit<T>, A>, AllocError> {
Ok(Box(ptr, PhantomData::<A>))
}
+ /// Drops the contents, but keeps the allocation.
+ ///
+ /// # Examples
+ ///
+ /// ```
+ /// use kernel::alloc::{Flags, KBox};
+ /// let value = KBox::new([0; 32], GFP_KERNEL)?;
+ /// assert_eq!(*value, [0; 32]);
+ /// let value = KBox::drop_contents(value);
+ /// // Now we can re-use `value`:
+ /// let value = KBox::write(value, [1; 32]);
+ /// assert_eq!(*value, [1; 32]);
+ /// # Ok::<(), Error>(())
+ /// ```
+ pub fn drop_contents(this: Self) -> Box<MaybeUninit<T>, A> {
+ let ptr = Box::into_raw(this);
+ // SAFETY: `ptr` is valid, because it came from `Box::into_raw`.
+ unsafe { ptr::drop_in_place(ptr) };
+ // SAFETY: `ptr` is valid, because it came from `Box::into_raw`.
+ unsafe { Box::from_raw(ptr.cast()) }
+ }
+
/// Constructs a new `Pin<Box<T, A>>`. If `T` does not implement
[`Unpin`], then `x` will be
/// pinned in memory and can't be moved.
#[inline]
--
^ permalink raw reply related [flat|nested] 95+ messages in thread* Re: [PATCH v5 11/26] rust: alloc: remove `BoxExt` extension
2024-08-14 11:55 ` Dirk Behme
@ 2024-08-14 12:08 ` Danilo Krummrich
0 siblings, 0 replies; 95+ messages in thread
From: Danilo Krummrich @ 2024-08-14 12:08 UTC (permalink / raw)
To: Dirk Behme
Cc: ojeda, alex.gaynor, wedsonaf, boqun.feng, gary, bjorn3_gh,
benno.lossin, a.hindborg, aliceryhl, akpm, daniel.almeida,
faith.ekstrand, boris.brezillon, lina, mcanal, zhiw, cjia,
jhubbard, airlied, ajanulgu, lyude, linux-kernel, rust-for-linux,
linux-mm
Hi Dirk,
On 8/14/24 1:55 PM, Dirk Behme wrote:
> On 12.08.2024 20:22, Danilo Krummrich wrote:
>> Now that all existing `Box` users were moved to the kernel `Box` type,
>> remove the `BoxExt` extension and all other related extensions.
> I just noticed that in the recent 'rust-dev' branch we have a change which *adds* something to BoxExt:
>
> rust: kernel: add drop_contents to BoxExt
> https://github.com/Rust-for-Linux/linux/commit/62c34da1da6c01a635ea2308cb42996d0571059e
>
> I'm unclear how relevant that is. Just want to mention this in case it would make sense to include that directly in this patch series to avoid a future add-on patch ;)
Thanks for reporting. I'm aware of this patch, we expect it to land before this series.
I'll add `drop_contents` once I rebase onto it.
- Danilo
>
> Thanks,
>
> Dirk
>
> P.S.: It looks like anything like this at least makes the compiler happy:
>
> diff --git a/rust/kernel/alloc/kbox.rs b/rust/kernel/alloc/kbox.rs
> index d67f975502246..e91d441835d54 100644
> --- a/rust/kernel/alloc/kbox.rs
> +++ b/rust/kernel/alloc/kbox.rs
> @@ -9,6 +9,7 @@
> use core::mem::MaybeUninit;
> use core::ops::{Deref, DerefMut};
> use core::pin::Pin;
> +use core::ptr;
> use core::ptr::NonNull;
> use core::result::Result;
>
> @@ -270,6 +271,28 @@ pub fn new_uninit(flags: Flags) -> Result<Box<MaybeUninit<T>, A>, AllocError> {
> Ok(Box(ptr, PhantomData::<A>))
> }
>
> + /// Drops the contents, but keeps the allocation.
> + ///
> + /// # Examples
> + ///
> + /// ```
> + /// use kernel::alloc::{Flags, KBox};
> + /// let value = KBox::new([0; 32], GFP_KERNEL)?;
> + /// assert_eq!(*value, [0; 32]);
> + /// let value = KBox::drop_contents(value);
> + /// // Now we can re-use `value`:
> + /// let value = KBox::write(value, [1; 32]);
> + /// assert_eq!(*value, [1; 32]);
> + /// # Ok::<(), Error>(())
> + /// ```
> + pub fn drop_contents(this: Self) -> Box<MaybeUninit<T>, A> {
> + let ptr = Box::into_raw(this);
> + // SAFETY: `ptr` is valid, because it came from `Box::into_raw`.
> + unsafe { ptr::drop_in_place(ptr) };
> + // SAFETY: `ptr` is valid, because it came from `Box::into_raw`.
> + unsafe { Box::from_raw(ptr.cast()) }
> + }
> +
> /// Constructs a new `Pin<Box<T, A>>`. If `T` does not implement [`Unpin`], then `x` will be
> /// pinned in memory and can't be moved.
> #[inline]
^ permalink raw reply [flat|nested] 95+ messages in thread
* [PATCH v5 12/26] rust: alloc: add `Box` to prelude
2024-08-12 18:22 [PATCH v5 00/26] Generic `Allocator` support for Rust Danilo Krummrich
` (10 preceding siblings ...)
2024-08-12 18:22 ` [PATCH v5 11/26] rust: alloc: remove `BoxExt` extension Danilo Krummrich
@ 2024-08-12 18:22 ` Danilo Krummrich
2024-08-12 18:22 ` [PATCH v5 13/26] rust: alloc: implement kernel `Vec` type Danilo Krummrich
` (14 subsequent siblings)
26 siblings, 0 replies; 95+ messages in thread
From: Danilo Krummrich @ 2024-08-12 18:22 UTC (permalink / raw)
To: ojeda, alex.gaynor, wedsonaf, boqun.feng, gary, bjorn3_gh,
benno.lossin, a.hindborg, aliceryhl, akpm
Cc: daniel.almeida, faith.ekstrand, boris.brezillon, lina, mcanal,
zhiw, cjia, jhubbard, airlied, ajanulgu, lyude, linux-kernel,
rust-for-linux, linux-mm, Danilo Krummrich
Now that we removed `BoxExt` and the corresponding includes in
prelude.rs, add the new kernel `Box` type instead.
Reviewed-by: Alice Ryhl <aliceryhl@google.com>
Reviewed-by: Benno Lossin <benno.lossin@proton.me>
Signed-off-by: Danilo Krummrich <dakr@kernel.org>
---
rust/kernel/prelude.rs | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/rust/kernel/prelude.rs b/rust/kernel/prelude.rs
index 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] 95+ messages in thread* [PATCH v5 13/26] rust: alloc: implement kernel `Vec` type
2024-08-12 18:22 [PATCH v5 00/26] Generic `Allocator` support for Rust Danilo Krummrich
` (11 preceding siblings ...)
2024-08-12 18:22 ` [PATCH v5 12/26] rust: alloc: add `Box` to prelude Danilo Krummrich
@ 2024-08-12 18:22 ` Danilo Krummrich
2024-08-14 8:42 ` Alice Ryhl
2024-08-12 18:23 ` [PATCH v5 14/26] rust: alloc: implement `IntoIterator` for `Vec` Danilo Krummrich
` (13 subsequent siblings)
26 siblings, 1 reply; 95+ messages in thread
From: Danilo Krummrich @ 2024-08-12 18:22 UTC (permalink / raw)
To: ojeda, alex.gaynor, wedsonaf, boqun.feng, gary, bjorn3_gh,
benno.lossin, a.hindborg, aliceryhl, akpm
Cc: daniel.almeida, faith.ekstrand, boris.brezillon, lina, mcanal,
zhiw, cjia, jhubbard, airlied, ajanulgu, lyude, linux-kernel,
rust-for-linux, linux-mm, Danilo Krummrich
`Vec` provides a contiguous growable array type (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 | 613 ++++++++++++++++++++++++++++++++++++++
rust/kernel/prelude.rs | 2 +-
4 files changed, 635 insertions(+), 2 deletions(-)
create mode 100644 rust/kernel/alloc/kvec.rs
diff --git a/rust/kernel/alloc.rs b/rust/kernel/alloc.rs
index 47cab5bd803e..4d7190594076 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 67bdfc0712d2..d67f97550224 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;
@@ -183,6 +183,20 @@ pub fn into_pin(b: Self) -> Pin<Self> {
}
}
+impl<T, A, const N: usize> Box<[T; N], A>
+where
+ A: Allocator,
+{
+ /// Convert a `Box<[T, N], A>` to a `Vec<T, A>`.
+ pub fn into_vec(b: Self) -> Vec<T, A> {
+ let len = b.len();
+ unsafe {
+ let ptr = Self::into_raw(b);
+ Vec::from_raw_parts(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..351c4f1702db
--- /dev/null
+++ b/rust/kernel/alloc/kvec.rs
@@ -0,0 +1,613 @@
+// SPDX-License-Identifier: GPL-2.0
+
+//! Implementation of [`Vec`].
+
+use super::{AllocError, Allocator, Flags};
+use core::{
+ fmt,
+ marker::PhantomData,
+ mem::{ManuallyDrop, MaybeUninit},
+ ops::Deref,
+ ops::DerefMut,
+ ops::Index,
+ ops::IndexMut,
+ ptr::NonNull,
+ slice,
+ slice::SliceIndex,
+};
+
+/// Create a [`Vec`] containing the arguments.
+///
+/// # Examples
+///
+/// ```
+/// let mut v = kernel::kvec![];
+/// v.push(1, GFP_KERNEL)?;
+/// assert_eq!(v, [1]);
+///
+/// let mut v = kernel::kvec![1; 3]?;
+/// v.push(4, GFP_KERNEL)?;
+/// assert_eq!(v, [1, 1, 1, 4]);
+///
+/// let mut v = kernel::kvec![1, 2, 3]?;
+/// v.push(4, GFP_KERNEL)?;
+/// assert_eq!(v, [1, 2, 3, 4]);
+///
+/// # Ok::<(), Error>(())
+/// ```
+#[macro_export]
+macro_rules! kvec {
+ () => (
+ {
+ $crate::alloc::KVec::new()
+ }
+ );
+ ($elem:expr; $n:expr) => (
+ {
+ $crate::alloc::KVec::from_elem($elem, $n, GFP_KERNEL)
+ }
+ );
+ ($($x:expr),+ $(,)?) => (
+ {
+ match $crate::alloc::KBox::new([$($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` type used
+/// to allocate (and free) the backing buffer.
+///
+/// A [`Vec`] can be deconstructed into and (re-)constructed from it's previously named raw parts
+/// and manually modified.
+///
+/// [`Vec`]'s backing buffer gets, if required, automatically increased (re-allocated) when elements
+/// are added to the vector.
+///
+/// # Invariants
+///
+/// The [`Vec`] backing buffer's pointer is always properly aligned and either points to memory
+/// allocated with `A` or, for zero-sized types, is a dangling pointer.
+///
+/// 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` type `A` of the vector is the exact same `Allocator` type the backing buffer was
+/// allocated with (and must be freed with).
+pub struct Vec<T, A: Allocator> {
+ ptr: NonNull<T>,
+ /// Represents the actual buffer size as `cap` times `size_of::<T>` bytes.
+ ///
+ /// Note: This isn't quite the same as `Self::capacity`, which in contrast returns the number of
+ /// elements we can still store without reallocating.
+ ///
+ /// # Invariants
+ ///
+ /// `cap` must be in the `0..=isize::MAX` range.
+ cap: usize,
+ len: usize,
+ _p: PhantomData<A>,
+}
+
+/// Type alias for `Vec` with a `Kmalloc` allocator.
+///
+/// # Examples
+///
+/// ```
+/// let mut v = KVec::new();
+/// v.push(1, GFP_KERNEL)?;
+/// assert_eq!(&v, &[1]);
+///
+/// # Ok::<(), Error>(())
+/// ```
+pub type KVec<T> = Vec<T, super::allocator::Kmalloc>;
+
+/// Type alias for `Vec` with a `Vmalloc` allocator.
+///
+/// # Examples
+///
+/// ```
+/// let mut v = VVec::new();
+/// v.push(1, GFP_KERNEL)?;
+/// assert_eq!(&v, &[1]);
+///
+/// # Ok::<(), Error>(())
+/// ```
+pub type VVec<T> = Vec<T, super::allocator::Vmalloc>;
+
+/// Type alias for `Vec` with a `KVmalloc` allocator.
+///
+/// # Examples
+///
+/// ```
+/// let mut v = KVVec::new();
+/// v.push(1, GFP_KERNEL)?;
+/// assert_eq!(&v, &[1]);
+///
+/// # Ok::<(), Error>(())
+/// ```
+pub type KVVec<T> = Vec<T, super::allocator::KVmalloc>;
+
+// SAFETY: `Vec` is `Send` if `T` is `Send` because the data referenced by `self.ptr` is unaliased.
+unsafe impl<T, A> Send for Vec<T, A>
+where
+ T: Send,
+ A: Allocator,
+{
+}
+
+// SAFETY: `Vec` is `Sync` if `T` is `Sync` because the data referenced by `self.ptr` is unaliased.
+unsafe impl<T, A> Sync for Vec<T, A>
+where
+ T: Send,
+ A: Allocator,
+{
+}
+
+impl<T, A> Vec<T, A>
+where
+ A: Allocator,
+{
+ #[inline]
+ fn is_zst() -> bool {
+ core::mem::size_of::<T>() == 0
+ }
+
+ /// Returns the number of elements that can be stored within the vector without allocating
+ /// additional memory.
+ pub fn capacity(&self) -> usize {
+ if Self::is_zst() {
+ usize::MAX
+ } else {
+ self.cap
+ }
+ }
+
+ /// Returns the number of elements stored within the vector.
+ #[inline]
+ pub fn len(&self) -> usize {
+ self.len
+ }
+
+ /// Forcefully sets `self.len` to `new_len`.
+ ///
+ /// # Safety
+ ///
+ /// - `new_len` must be less than or equal to [`Self::capacity`].
+ /// - If `new_len` is greater than `self.len`, all elements within the interval
+ /// [`self.len`,`new_len`] must be initialized.
+ #[inline]
+ pub unsafe fn set_len(&mut self, new_len: usize) {
+ self.len = new_len;
+ }
+
+ /// Returns a slice of the entire vector.
+ ///
+ /// Equivalent to `&s[..]`.
+ #[inline]
+ pub fn as_slice(&self) -> &[T] {
+ self
+ }
+
+ /// Returns a mutable slice of the entire vector.
+ ///
+ /// Equivalent to `&mut s[..]`.
+ #[inline]
+ pub fn as_mut_slice(&mut self) -> &mut [T] {
+ self
+ }
+
+ /// Returns a mutable raw pointer to the vector's backing buffer, or, if `T` is a ZST, a
+ /// dangling raw pointer.
+ #[inline]
+ pub fn as_mut_ptr(&self) -> *mut T {
+ self.ptr.as_ptr()
+ }
+
+ /// Returns a raw pointer to the vector's backing buffer, or, if `T` is a ZST, a dangling raw
+ /// pointer.
+ #[inline]
+ pub fn as_ptr(&self) -> *const T {
+ self.as_mut_ptr()
+ }
+
+ /// Returns `true` if the vector contains no elements, `false` otherwise.
+ ///
+ /// # Examples
+ ///
+ /// ```
+ /// let mut v = KVec::new();
+ /// assert!(v.is_empty());
+ ///
+ /// v.push(1, GFP_KERNEL);
+ /// assert!(!v.is_empty());
+ /// ```
+ #[inline]
+ pub fn is_empty(&self) -> bool {
+ self.len() == 0
+ }
+
+ /// Creates a new, empty Vec<T, A>.
+ ///
+ /// This method does not allocate by itself.
+ #[inline]
+ pub const fn new() -> Self {
+ Self {
+ ptr: NonNull::dangling(),
+ cap: 0,
+ len: 0,
+ _p: PhantomData::<A>,
+ }
+ }
+
+ /// Returns a slice of `MaybeUninit<T>` for the remaining spare capacity of the vector.
+ pub fn spare_capacity_mut(&mut self) -> &mut [MaybeUninit<T>] {
+ // SAFETY: 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> from a pointer, a length and a capacity using the allocator `A`.
+ ///
+ /// # Safety
+ ///
+ /// If `T` is a ZST:
+ ///
+ /// - `ptr` must be a dangling pointer.
+ /// - `capacity` must be zero.
+ /// - `length` must be smaller than or equal to `usize::MAX`.
+ ///
+ /// Otherwise:
+ ///
+ /// - `ptr` must have been allocated with the allocator `A`.
+ /// - `ptr` must satisfy or exceed the alignment requirements of `T`.
+ /// - `ptr` must point to memory with a size of at least `size_of::<T>` times the `capacity`
+ /// bytes.
+ /// - The allocated size in bytes must not be larger than `isize::MAX`.
+ /// - `length` must be less than or equal to `capacity`.
+ /// - The first `length` elements must be initialized values of type `T`.
+ ///
+ /// It is also valid to create an empty `Vec` passing a dangling pointer for `ptr` and zero for
+ /// `cap` and `len`.
+ ///
+ /// # 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 { NonNull::new_unchecked(ptr) },
+ cap,
+ len: length,
+ _p: PhantomData::<A>,
+ }
+ }
+
+ /// Consumes the `Vec<T, A>` and returns its raw components `pointer`, `length` and `capacity`.
+ ///
+ /// This will not run the destructor of the contained elements and for non-ZSTs the allocation
+ /// will stay alive indefinitely. Use [`Vec::from_raw_parts`] to recover the [`Vec`], drop the
+ /// elements and free the allocation, if any.
+ pub fn into_raw_parts(self) -> (*mut T, usize, usize) {
+ let me = ManuallyDrop::new(self);
+ let len = me.len();
+ let capacity = me.capacity();
+ let ptr = me.as_mut_ptr();
+ (ptr, len, capacity)
+ }
+
+ /// Ensures that the capacity exceeds the length by at least `additional`
+ /// elements.
+ ///
+ /// # Examples
+ ///
+ /// ```
+ /// let mut v = KVec::new();
+ /// v.push(1, GFP_KERNEL)?;
+ ///
+ /// v.reserve(10, GFP_KERNEL)?;
+ /// let cap = v.capacity();
+ /// assert!(cap >= 10);
+ ///
+ /// v.reserve(10, GFP_KERNEL)?;
+ /// let new_cap = v.capacity();
+ /// assert_eq!(new_cap, cap);
+ ///
+ /// # Ok::<(), Error>(())
+ /// ```
+ 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.cast())
+ };
+
+ // SAFETY: `ptr` is valid because it's either `None` or comes from a previous call to
+ // `A::realloc`. We also verified that the type is not a ZST.
+ let ptr = unsafe { A::realloc(ptr, layout, flags)? };
+
+ self.ptr = ptr.cast();
+
+ // INVARIANT: `Layout::array` fails if the resulting byte size is greater than `isize::MAX`.
+ self.cap = new_cap;
+
+ Ok(())
+ }
+}
+
+impl<T: Clone, A: Allocator> Vec<T, A> {
+ /// Extend the vector by `n` clones of value.
+ pub fn extend_with(&mut self, n: usize, value: T, flags: Flags) -> Result<(), AllocError> {
+ 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.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] 95+ messages in thread* Re: [PATCH v5 13/26] rust: alloc: implement kernel `Vec` type
2024-08-12 18:22 ` [PATCH v5 13/26] rust: alloc: implement kernel `Vec` type Danilo Krummrich
@ 2024-08-14 8:42 ` Alice Ryhl
2024-08-14 12:29 ` Danilo Krummrich
2024-08-14 22:46 ` Danilo Krummrich
0 siblings, 2 replies; 95+ messages in thread
From: Alice Ryhl @ 2024-08-14 8: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, cjia, jhubbard, airlied,
ajanulgu, lyude, linux-kernel, rust-for-linux, linux-mm
On Mon, Aug 12, 2024 at 8:25 PM Danilo Krummrich <dakr@kernel.org> wrote:
>
> `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>
> [...]
> +impl<T, A, const N: usize> Box<[T; N], A>
> +where
> + A: Allocator,
> +{
> + /// Convert a `Box<[T, N], A>` to a `Vec<T, A>`.
> + pub fn into_vec(b: Self) -> Vec<T, A> {
Nit: I would probably make this a From impl.
> +#[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),
Hmm. This currently generates code that:
1. Creates the array.
2. Allocates the memory.
3. Moves the array into the box.
Whereas the stdlib macro swaps step 1 and 2. You can do the same by
utilizing new_uninit. A sketch:
match KBox::<[_; _]>::new_uninit(GFP_KERNEL) {
Ok(b) => Ok(KVec::from(KBox::write(b, [$($x),+]))),
Err(e) => Err(e),
}
> +// SAFETY: `Vec` is `Send` if `T` is `Send` because the data referenced by `self.ptr` is unaliased.
> +unsafe impl<T, A> Send for Vec<T, A>
> +where
> + T: Send,
> + A: Allocator,
> +{
> +}
> +
> +// SAFETY: `Vec` is `Sync` if `T` is `Sync` because the data referenced by `self.ptr` is unaliased.
> +unsafe impl<T, A> Sync for Vec<T, A>
> +where
> + T: Send,
> + A: Allocator,
Same comment as Box. Ditto about "unaliased". Needs `T: Sync` instead.
> +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) {
You need to handle `n == 0` here.
Alice
^ permalink raw reply [flat|nested] 95+ messages in thread* Re: [PATCH v5 13/26] rust: alloc: implement kernel `Vec` type
2024-08-14 8:42 ` Alice Ryhl
@ 2024-08-14 12:29 ` Danilo Krummrich
2024-08-14 12:36 ` Alice Ryhl
2024-08-14 22:46 ` Danilo Krummrich
1 sibling, 1 reply; 95+ messages in thread
From: Danilo Krummrich @ 2024-08-14 12:29 UTC (permalink / raw)
To: Alice Ryhl
Cc: ojeda, alex.gaynor, wedsonaf, boqun.feng, gary, bjorn3_gh,
benno.lossin, a.hindborg, akpm, daniel.almeida, faith.ekstrand,
boris.brezillon, lina, mcanal, zhiw, cjia, jhubbard, airlied,
ajanulgu, lyude, linux-kernel, rust-for-linux, linux-mm
On Wed, Aug 14, 2024 at 10:42:28AM +0200, Alice Ryhl wrote:
> On Mon, Aug 12, 2024 at 8:25 PM Danilo Krummrich <dakr@kernel.org> wrote:
> >
> > `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>
> > [...]
> > +impl<T, A, const N: usize> Box<[T; N], A>
> > +where
> > + A: Allocator,
> > +{
> > + /// Convert a `Box<[T, N], A>` to a `Vec<T, A>`.
> > + pub fn into_vec(b: Self) -> Vec<T, A> {
>
> Nit: I would probably make this a From impl.
Please see my reply to patch 9 of this series.
>
> > +#[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),
>
> Hmm. This currently generates code that:
>
> 1. Creates the array.
> 2. Allocates the memory.
> 3. Moves the array into the box.
>
> Whereas the stdlib macro swaps step 1 and 2. You can do the same by
> utilizing new_uninit. A sketch:
>
> match KBox::<[_; _]>::new_uninit(GFP_KERNEL) {
> Ok(b) => Ok(KVec::from(KBox::write(b, [$($x),+]))),
> Err(e) => Err(e),
> }
Generally, I'm fine changing that, but what's the reason for the suggestion? It
shouldn't make a difference, does it?
>
> > +// SAFETY: `Vec` is `Send` if `T` is `Send` because the data referenced by `self.ptr` is unaliased.
> > +unsafe impl<T, A> Send for Vec<T, A>
> > +where
> > + T: Send,
> > + A: Allocator,
> > +{
> > +}
> > +
> > +// SAFETY: `Vec` is `Sync` if `T` is `Sync` because the data referenced by `self.ptr` is unaliased.
> > +unsafe impl<T, A> Sync for Vec<T, A>
> > +where
> > + T: Send,
> > + A: Allocator,
>
> Same comment as Box. Ditto about "unaliased". Needs `T: Sync` instead.
>
> > +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) {
>
> You need to handle `n == 0` here.
Gonna fix.
>
> Alice
>
^ permalink raw reply [flat|nested] 95+ messages in thread* Re: [PATCH v5 13/26] rust: alloc: implement kernel `Vec` type
2024-08-14 12:29 ` Danilo Krummrich
@ 2024-08-14 12:36 ` Alice Ryhl
2024-08-15 13:31 ` Benno Lossin
0 siblings, 1 reply; 95+ messages in thread
From: Alice Ryhl @ 2024-08-14 12:36 UTC (permalink / raw)
To: Danilo Krummrich
Cc: ojeda, alex.gaynor, wedsonaf, boqun.feng, gary, bjorn3_gh,
benno.lossin, a.hindborg, akpm, daniel.almeida, faith.ekstrand,
boris.brezillon, lina, mcanal, zhiw, cjia, jhubbard, airlied,
ajanulgu, lyude, linux-kernel, rust-for-linux, linux-mm
On Wed, Aug 14, 2024 at 2:29 PM Danilo Krummrich <dakr@kernel.org> wrote:
>
> On Wed, Aug 14, 2024 at 10:42:28AM +0200, Alice Ryhl wrote:
> > On Mon, Aug 12, 2024 at 8:25 PM Danilo Krummrich <dakr@kernel.org> wrote:
> > > +#[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),
> >
> > Hmm. This currently generates code that:
> >
> > 1. Creates the array.
> > 2. Allocates the memory.
> > 3. Moves the array into the box.
> >
> > Whereas the stdlib macro swaps step 1 and 2. You can do the same by
> > utilizing new_uninit. A sketch:
> >
> > match KBox::<[_; _]>::new_uninit(GFP_KERNEL) {
> > Ok(b) => Ok(KVec::from(KBox::write(b, [$($x),+]))),
> > Err(e) => Err(e),
> > }
>
> Generally, I'm fine changing that, but what's the reason for the suggestion? It
> shouldn't make a difference, does it?
The compiler is much more likely to not put the array on the stack
before it is copied to the heap.
In the case where $x constructs new values, it also avoids
create-then-destroy on allocator failure.
Alice
^ permalink raw reply [flat|nested] 95+ messages in thread* Re: [PATCH v5 13/26] rust: alloc: implement kernel `Vec` type
2024-08-14 12:36 ` Alice Ryhl
@ 2024-08-15 13:31 ` Benno Lossin
0 siblings, 0 replies; 95+ messages in thread
From: Benno Lossin @ 2024-08-15 13:31 UTC (permalink / raw)
To: Alice Ryhl, Danilo Krummrich
Cc: ojeda, alex.gaynor, wedsonaf, boqun.feng, gary, bjorn3_gh,
a.hindborg, akpm, daniel.almeida, faith.ekstrand, boris.brezillon,
lina, mcanal, zhiw, cjia, jhubbard, airlied, ajanulgu, lyude,
linux-kernel, rust-for-linux, linux-mm
On 14.08.24 14:36, Alice Ryhl wrote:
> On Wed, Aug 14, 2024 at 2:29 PM Danilo Krummrich <dakr@kernel.org> wrote:
>>
>> On Wed, Aug 14, 2024 at 10:42:28AM +0200, Alice Ryhl wrote:
>>> On Mon, Aug 12, 2024 at 8:25 PM Danilo Krummrich <dakr@kernel.org> wrote:
>>>> +#[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),
>>>
>>> Hmm. This currently generates code that:
>>>
>>> 1. Creates the array.
>>> 2. Allocates the memory.
>>> 3. Moves the array into the box.
>>>
>>> Whereas the stdlib macro swaps step 1 and 2. You can do the same by
>>> utilizing new_uninit. A sketch:
>>>
>>> match KBox::<[_; _]>::new_uninit(GFP_KERNEL) {
>>> Ok(b) => Ok(KVec::from(KBox::write(b, [$($x),+]))),
>>> Err(e) => Err(e),
>>> }
>>
>> Generally, I'm fine changing that, but what's the reason for the suggestion? It
>> shouldn't make a difference, does it?
>
> The compiler is much more likely to not put the array on the stack
> before it is copied to the heap.
>
> In the case where $x constructs new values, it also avoids
> create-then-destroy on allocator failure.
If we are this worried about not putting the array on the stack, then we
could also do this using `init!`, but the macro still would need to
count the number of elements in the array.
@Danilo, from the other thread:
> How do we get the size here? `#![feature(generic_arg_infer)]` seems to be
> unstable.
You can use a macro to count the number of elements:
macro_rules! count_exprs {
() => { 0 };
($e:expr $(,$rest:expr)* $(,)?) => {
1 + count_exprs!($($rest,)*)
};
}
And then do
match KBox::<[_; count_exprs!($($x),+)]>::new_uninit(GFP_KERNEL) {
Ok(b) => Ok(KVec::from(KBox::write(b, [$($x),+]))),
Err(e) => Err(e),
}
---
Cheers,
Benno
^ permalink raw reply [flat|nested] 95+ messages in thread
* Re: [PATCH v5 13/26] rust: alloc: implement kernel `Vec` type
2024-08-14 8:42 ` Alice Ryhl
2024-08-14 12:29 ` Danilo Krummrich
@ 2024-08-14 22:46 ` Danilo Krummrich
2024-08-15 7:30 ` Alice Ryhl
1 sibling, 1 reply; 95+ messages in thread
From: Danilo Krummrich @ 2024-08-14 22:46 UTC (permalink / raw)
To: Alice Ryhl
Cc: ojeda, alex.gaynor, wedsonaf, boqun.feng, gary, bjorn3_gh,
benno.lossin, a.hindborg, akpm, daniel.almeida, faith.ekstrand,
boris.brezillon, lina, mcanal, zhiw, cjia, jhubbard, airlied,
ajanulgu, lyude, linux-kernel, rust-for-linux, linux-mm
On Wed, Aug 14, 2024 at 10:42:28AM +0200, Alice Ryhl wrote:
> > +#[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),
>
> Hmm. This currently generates code that:
>
> 1. Creates the array.
> 2. Allocates the memory.
> 3. Moves the array into the box.
>
> Whereas the stdlib macro swaps step 1 and 2.
Isn't stdlib [1] doing the same thing I do?
[1] https://doc.rust-lang.org/1.80.1/src/alloc/macros.rs.html#49
> You can do the same by utilizing new_uninit. A sketch:
>
> match KBox::<[_; _]>::new_uninit(GFP_KERNEL) {
How do we get the size here? `#![feature(generic_arg_infer)]` seems to be
unstable.
> Ok(b) => Ok(KVec::from(KBox::write(b, [$($x),+]))),
> Err(e) => Err(e),
> }
^ permalink raw reply [flat|nested] 95+ messages in thread* Re: [PATCH v5 13/26] rust: alloc: implement kernel `Vec` type
2024-08-14 22:46 ` Danilo Krummrich
@ 2024-08-15 7:30 ` Alice Ryhl
2024-08-15 12:17 ` Danilo Krummrich
0 siblings, 1 reply; 95+ messages in thread
From: Alice Ryhl @ 2024-08-15 7:30 UTC (permalink / raw)
To: Danilo Krummrich
Cc: ojeda, alex.gaynor, wedsonaf, boqun.feng, gary, bjorn3_gh,
benno.lossin, a.hindborg, akpm, daniel.almeida, faith.ekstrand,
boris.brezillon, lina, mcanal, zhiw, cjia, jhubbard, airlied,
ajanulgu, lyude, linux-kernel, rust-for-linux, linux-mm
On Thu, Aug 15, 2024 at 12:46 AM Danilo Krummrich <dakr@kernel.org> wrote:
>
> On Wed, Aug 14, 2024 at 10:42:28AM +0200, Alice Ryhl wrote:
> > > +#[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),
> >
> > Hmm. This currently generates code that:
> >
> > 1. Creates the array.
> > 2. Allocates the memory.
> > 3. Moves the array into the box.
> >
> > Whereas the stdlib macro swaps step 1 and 2.
>
> Isn't stdlib [1] doing the same thing I do?
>
> [1] https://doc.rust-lang.org/1.80.1/src/alloc/macros.rs.html#49
Stdlib is using something called #[rustc_box] which has the effect I described.
> > You can do the same by utilizing new_uninit. A sketch:
> >
> > match KBox::<[_; _]>::new_uninit(GFP_KERNEL) {
>
> How do we get the size here? `#![feature(generic_arg_infer)]` seems to be
> unstable.
It probably works if you don't specify the type at all:
`KBox::new_uninit`. But you should double check.
> > Ok(b) => Ok(KVec::from(KBox::write(b, [$($x),+]))),
> > Err(e) => Err(e),
> > }
^ permalink raw reply [flat|nested] 95+ messages in thread* Re: [PATCH v5 13/26] rust: alloc: implement kernel `Vec` type
2024-08-15 7:30 ` Alice Ryhl
@ 2024-08-15 12:17 ` Danilo Krummrich
0 siblings, 0 replies; 95+ messages in thread
From: Danilo Krummrich @ 2024-08-15 12:17 UTC (permalink / raw)
To: Alice Ryhl
Cc: ojeda, alex.gaynor, wedsonaf, boqun.feng, gary, bjorn3_gh,
benno.lossin, a.hindborg, akpm, daniel.almeida, faith.ekstrand,
boris.brezillon, lina, mcanal, zhiw, cjia, jhubbard, airlied,
ajanulgu, lyude, linux-kernel, rust-for-linux, linux-mm
On 8/15/24 9:30 AM, Alice Ryhl wrote:
> On Thu, Aug 15, 2024 at 12:46 AM Danilo Krummrich <dakr@kernel.org> wrote:
>>
>> On Wed, Aug 14, 2024 at 10:42:28AM +0200, Alice Ryhl wrote:
>>>> +#[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),
>>>
>>> Hmm. This currently generates code that:
>>>
>>> 1. Creates the array.
>>> 2. Allocates the memory.
>>> 3. Moves the array into the box.
>>>
>>> Whereas the stdlib macro swaps step 1 and 2.
>>
>> Isn't stdlib [1] doing the same thing I do?
>>
>> [1] https://doc.rust-lang.org/1.80.1/src/alloc/macros.rs.html#49
>
> Stdlib is using something called #[rustc_box] which has the effect I described.
>
>>> You can do the same by utilizing new_uninit. A sketch:
>>>
>>> match KBox::<[_; _]>::new_uninit(GFP_KERNEL) {
>>
>> How do we get the size here? `#![feature(generic_arg_infer)]` seems to be
>> unstable.
>
> It probably works if you don't specify the type at all:
> `KBox::new_uninit`. But you should double check.
That works, thanks.
>
>>> Ok(b) => Ok(KVec::from(KBox::write(b, [$($x),+]))),
>>> Err(e) => Err(e),
>>> }
>
^ permalink raw reply [flat|nested] 95+ messages in thread
* [PATCH v5 14/26] rust: alloc: implement `IntoIterator` for `Vec`
2024-08-12 18:22 [PATCH v5 00/26] Generic `Allocator` support for Rust Danilo Krummrich
` (12 preceding siblings ...)
2024-08-12 18:22 ` [PATCH v5 13/26] rust: alloc: implement kernel `Vec` type Danilo Krummrich
@ 2024-08-12 18:23 ` Danilo Krummrich
2024-08-12 18:23 ` [PATCH v5 15/26] rust: alloc: implement `collect` for `IntoIter` Danilo Krummrich
` (12 subsequent siblings)
26 siblings, 0 replies; 95+ messages in thread
From: Danilo Krummrich @ 2024-08-12 18:23 UTC (permalink / raw)
To: ojeda, alex.gaynor, wedsonaf, boqun.feng, gary, bjorn3_gh,
benno.lossin, a.hindborg, aliceryhl, akpm
Cc: daniel.almeida, faith.ekstrand, boris.brezillon, lina, mcanal,
zhiw, cjia, jhubbard, airlied, ajanulgu, lyude, linux-kernel,
rust-for-linux, linux-mm, Danilo Krummrich
Implement `IntoIterator` for `Vec`, `Vec`'s `IntoIter` type, as well as
`Iterator` for `IntoIter`.
`Vec::into_iter` disassembles the `Vec` into its raw parts; additionally,
`IntoIter` keeps track of a separate pointer, which is incremented
correspondingsly as the iterator advances, while the length, or the count
of elements, is decremented.
This also means that `IntoIter` takes the ownership of the backing
buffer and is responsible to drop the remaining elements and free the
backing buffer, if it's dropped.
Signed-off-by: Danilo Krummrich <dakr@kernel.org>
---
rust/kernel/alloc.rs | 1 +
rust/kernel/alloc/kvec.rs | 184 ++++++++++++++++++++++++++++++++++++++
2 files changed, 185 insertions(+)
diff --git a/rust/kernel/alloc.rs b/rust/kernel/alloc.rs
index 4d7190594076..4035644b5dca 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 351c4f1702db..cc8f3c555d1e 100644
--- a/rust/kernel/alloc/kvec.rs
+++ b/rust/kernel/alloc/kvec.rs
@@ -11,6 +11,7 @@
ops::DerefMut,
ops::Index,
ops::IndexMut,
+ ptr,
ptr::NonNull,
slice,
slice::SliceIndex,
@@ -611,3 +612,186 @@ fn eq(&self, other: &$rhs) -> bool { self[..] == other[..] }
__impl_slice_eq! { [A: Allocator] [T], Vec<U, A> }
__impl_slice_eq! { [A: Allocator, const N: usize] Vec<T, A>, [U; N] }
__impl_slice_eq! { [A: Allocator, const N: usize] Vec<T, A>, &[U; N] }
+
+impl<'a, T, A> IntoIterator for &'a Vec<T, A>
+where
+ A: Allocator,
+{
+ type Item = &'a T;
+ type IntoIter = slice::Iter<'a, T>;
+
+ fn into_iter(self) -> Self::IntoIter {
+ self.iter()
+ }
+}
+
+impl<'a, T, A: Allocator> IntoIterator for &'a mut Vec<T, A>
+where
+ A: Allocator,
+{
+ type Item = &'a mut T;
+ type IntoIter = slice::IterMut<'a, T>;
+
+ fn into_iter(self) -> Self::IntoIter {
+ self.iter_mut()
+ }
+}
+
+/// An `Iterator` implementation for `Vec<T,A>` that moves elements out of a vector.
+///
+/// This structure is created by the `Vec::into_iter` method on [`Vec`] (provided by the
+/// [`IntoIterator`] trait).
+///
+/// # Examples
+///
+/// ```
+/// let v = kernel::kvec![0, 1, 2]?;
+/// let iter = v.into_iter();
+///
+/// # Ok::<(), Error>(())
+/// ```
+pub struct IntoIter<T, A: Allocator> {
+ ptr: *mut T,
+ buf: NonNull<T>,
+ len: usize,
+ cap: usize,
+ _p: PhantomData<A>,
+}
+
+impl<T, A> IntoIter<T, A>
+where
+ A: Allocator,
+{
+ fn as_raw_mut_slice(&mut self) -> *mut [T] {
+ ptr::slice_from_raw_parts_mut(self.ptr, self.len)
+ }
+}
+
+impl<T, A> Iterator for IntoIter<T, A>
+where
+ A: Allocator,
+{
+ type Item = T;
+
+ /// # Examples
+ ///
+ /// ```
+ /// let v = kernel::kvec![1, 2, 3]?;
+ /// let mut it = v.into_iter();
+ ///
+ /// assert_eq!(it.next(), Some(1));
+ /// assert_eq!(it.next(), Some(2));
+ /// assert_eq!(it.next(), Some(3));
+ /// 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>;
+
+ /// Consumes the `Vec<T, A>` and creates an `Iterator`, which moves each value out of the
+ /// vector (from start to end).
+ ///
+ /// # Examples
+ ///
+ /// ```
+ /// let v = kernel::kvec![1, 2]?;
+ /// let mut v_iter = v.into_iter();
+ ///
+ /// let first_element: Option<u32> = v_iter.next();
+ ///
+ /// assert_eq!(first_element, Some(1));
+ /// assert_eq!(v_iter.next(), Some(2));
+ /// assert_eq!(v_iter.next(), None);
+ ///
+ /// # Ok::<(), Error>(())
+ /// ```
+ ///
+ /// ```
+ /// let v = kernel::kvec![];
+ /// let mut v_iter = v.into_iter();
+ ///
+ /// let first_element: Option<u32> = v_iter.next();
+ ///
+ /// assert_eq!(first_element, None);
+ ///
+ /// # Ok::<(), Error>(())
+ /// ```
+ #[inline]
+ fn into_iter(self) -> Self::IntoIter {
+ let (ptr, len, cap) = self.into_raw_parts();
+
+ IntoIter {
+ ptr,
+ // SAFETY: `ptr` is either a dangling pointer or a pointer to a valid memory
+ // allocation, allocated with `A`.
+ buf: unsafe { NonNull::new_unchecked(ptr) },
+ len,
+ cap,
+ _p: PhantomData::<A>,
+ }
+ }
+}
--
2.45.2
^ permalink raw reply related [flat|nested] 95+ messages in thread* [PATCH v5 15/26] rust: alloc: implement `collect` for `IntoIter`
2024-08-12 18:22 [PATCH v5 00/26] Generic `Allocator` support for Rust Danilo Krummrich
` (13 preceding siblings ...)
2024-08-12 18:23 ` [PATCH v5 14/26] rust: alloc: implement `IntoIterator` for `Vec` Danilo Krummrich
@ 2024-08-12 18:23 ` Danilo Krummrich
2024-08-12 18:23 ` [PATCH v5 16/26] rust: treewide: switch to the kernel `Vec` type Danilo Krummrich
` (11 subsequent siblings)
26 siblings, 0 replies; 95+ messages in thread
From: Danilo Krummrich @ 2024-08-12 18:23 UTC (permalink / raw)
To: ojeda, alex.gaynor, wedsonaf, boqun.feng, gary, bjorn3_gh,
benno.lossin, a.hindborg, aliceryhl, akpm
Cc: daniel.almeida, faith.ekstrand, boris.brezillon, lina, mcanal,
zhiw, cjia, jhubbard, airlied, ajanulgu, lyude, linux-kernel,
rust-for-linux, linux-mm, Danilo Krummrich
Currently, we can't implement `FromIterator`. There are a couple of
issues with this trait in the kernel, namely:
- Rust's specialization feature is unstable. This prevents us to
optimze for the special case where `I::IntoIter` equals `Vec`'s
`IntoIter` type.
- We also can't use `I::IntoIter`'s type ID either to work around this,
since `FromIterator` doesn't require this type to be `'static`.
- `FromIterator::from_iter` does return `Self` instead of
`Result<Self, AllocError>`, hence we can't properly handle allocation
failures.
- Neither `Iterator::collect` nor `FromIterator::from_iter` can handle
additional allocation flags.
Instead, provide `IntoIter::collect`, such that we can at least convert
`IntoIter` into a `Vec` again.
Reviewed-by: Alice Ryhl <aliceryhl@google.com>
Signed-off-by: Danilo Krummrich <dakr@kernel.org>
---
rust/kernel/alloc/kvec.rs | 78 +++++++++++++++++++++++++++++++++++++++
1 file changed, 78 insertions(+)
diff --git a/rust/kernel/alloc/kvec.rs b/rust/kernel/alloc/kvec.rs
index cc8f3c555d1e..db58e764db4b 100644
--- a/rust/kernel/alloc/kvec.rs
+++ b/rust/kernel/alloc/kvec.rs
@@ -665,6 +665,84 @@ impl<T, A> IntoIter<T, A>
fn as_raw_mut_slice(&mut self) -> *mut [T] {
ptr::slice_from_raw_parts_mut(self.ptr, self.len)
}
+
+ fn into_raw_parts(self) -> (*mut T, NonNull<T>, usize, usize) {
+ let me = ManuallyDrop::new(self);
+ let ptr = me.ptr;
+ let buf = me.buf;
+ let len = me.len;
+ let cap = me.cap;
+ (ptr, buf, len, cap)
+ }
+
+ /// Same as `Iterator::collect` but specialized for `Vec`'s `IntoIter`.
+ ///
+ /// Currently, we can't implement `FromIterator`. There are a couple of issues with this trait
+ /// in the kernel, namely:
+ ///
+ /// - Rust's specialization feature is unstable. This prevents us to optimze for the special
+ /// case where `I::IntoIter` equals `Vec`'s `IntoIter` type.
+ /// - We also can't use `I::IntoIter`'s type ID either to work around this, since `FromIterator`
+ /// doesn't require this type to be `'static`.
+ /// - `FromIterator::from_iter` does return `Self` instead of `Result<Self, AllocError>`, hence
+ /// we can't properly handle allocation failures.
+ /// - Neither `Iterator::collect` nor `FromIterator::from_iter` can handle additional allocation
+ /// flags.
+ ///
+ /// Instead, provide `IntoIter::collect`, such that we can at least convert a `IntoIter` into a
+ /// `Vec` again.
+ ///
+ /// Note that `IntoIter::collect` doesn't require `Flags`, since it re-uses the existing backing
+ /// buffer. However, this backing buffer may be shrunk to the actual count of elements.
+ ///
+ /// # Examples
+ ///
+ /// ```
+ /// let v = kernel::kvec![1, 2, 3]?;
+ /// let mut it = v.into_iter();
+ ///
+ /// assert_eq!(it.next(), Some(1));
+ ///
+ /// let v = it.collect(GFP_KERNEL);
+ /// assert_eq!(v, [2, 3]);
+ ///
+ /// # Ok::<(), Error>(())
+ /// ```
+ pub fn collect(self, flags: Flags) -> Vec<T, A> {
+ let (mut ptr, buf, len, mut cap) = self.into_raw_parts();
+ let has_advanced = ptr != buf.as_ptr();
+
+ if has_advanced {
+ // SAFETY: Copy the contents we have advanced to at the beginning of the buffer.
+ // `ptr` is guaranteed to be between `buf` and `buf.add(cap)` and `ptr.add(len)` is
+ // guaranteed to be smaller than `buf.add(cap)`.
+ unsafe { ptr::copy(ptr, buf.as_ptr(), len) };
+ ptr = buf.as_ptr();
+ }
+
+ // This can never fail, `len` is guaranteed to be smaller than `cap`.
+ let layout = core::alloc::Layout::array::<T>(len).unwrap();
+
+ // SAFETY: `buf` points to the start of the backing buffer and `len` is guaranteed to be
+ // smaller than `cap`. Depending on `alloc` this operation may shrink the buffer or leaves
+ // it as it is.
+ ptr = match unsafe { A::realloc(Some(buf.cast()), layout, flags) } {
+ // If we fail to shrink, which likely can't even happen, continue with the existing
+ // buffer.
+ Err(_) => ptr,
+ Ok(ptr) => {
+ cap = len;
+ ptr.as_ptr().cast()
+ }
+ };
+
+ // SAFETY: If the iterator has been advanced, the advanced elements have been copied to
+ // the beginning of the buffer and `len` has been adjusted accordingly. `ptr` is guaranteed
+ // to point to the start of the backing buffer. `cap` is either the original capacity or,
+ // after shrinking the buffer, equal to `len`. `alloc` is guaranteed to be unchanged since
+ // `into_iter` has been called on the original `Vec`.
+ unsafe { Vec::from_raw_parts(ptr, len, cap) }
+ }
}
impl<T, A> Iterator for IntoIter<T, A>
--
2.45.2
^ permalink raw reply related [flat|nested] 95+ messages in thread* [PATCH v5 16/26] rust: treewide: switch to the kernel `Vec` type
2024-08-12 18:22 [PATCH v5 00/26] Generic `Allocator` support for Rust Danilo Krummrich
` (14 preceding siblings ...)
2024-08-12 18:23 ` [PATCH v5 15/26] rust: alloc: implement `collect` for `IntoIter` Danilo Krummrich
@ 2024-08-12 18:23 ` Danilo Krummrich
2024-08-12 18:23 ` [PATCH v5 17/26] rust: alloc: remove `VecExt` extension Danilo Krummrich
` (10 subsequent siblings)
26 siblings, 0 replies; 95+ messages in thread
From: Danilo Krummrich @ 2024-08-12 18:23 UTC (permalink / raw)
To: ojeda, alex.gaynor, wedsonaf, boqun.feng, gary, bjorn3_gh,
benno.lossin, a.hindborg, aliceryhl, akpm
Cc: daniel.almeida, faith.ekstrand, boris.brezillon, lina, mcanal,
zhiw, cjia, jhubbard, airlied, ajanulgu, lyude, linux-kernel,
rust-for-linux, linux-mm, Danilo Krummrich
Now that we got the kernel `Vec` in place, convert all existing `Vec`
users to make use of it.
Reviewed-by: Alice Ryhl <aliceryhl@google.com>
Signed-off-by: Danilo Krummrich <dakr@kernel.org>
---
rust/kernel/str.rs | 12 +++++-------
rust/kernel/sync/locked_by.rs | 2 +-
rust/kernel/types.rs | 2 +-
rust/kernel/uaccess.rs | 17 +++++++----------
rust/macros/lib.rs | 6 +++---
samples/rust/rust_minimal.rs | 4 ++--
6 files changed, 19 insertions(+), 24 deletions(-)
diff --git a/rust/kernel/str.rs b/rust/kernel/str.rs
index bb8d4f41475b..209ad1bb1a49 100644
--- a/rust/kernel/str.rs
+++ b/rust/kernel/str.rs
@@ -2,8 +2,7 @@
//! String representations.
-use crate::alloc::{flags::*, vec_ext::VecExt, AllocError};
-use alloc::vec::Vec;
+use crate::alloc::{flags::*, AllocError, KVec};
use core::fmt::{self, Write};
use core::ops::{self, Deref, DerefMut, Index};
@@ -790,7 +789,7 @@ fn write_str(&mut self, s: &str) -> fmt::Result {
/// assert_eq!(s.is_ok(), false);
/// ```
pub struct CString {
- buf: Vec<u8>,
+ buf: KVec<u8>,
}
impl CString {
@@ -803,7 +802,7 @@ pub fn try_from_fmt(args: fmt::Arguments<'_>) -> Result<Self, Error> {
let size = f.bytes_written();
// Allocate a vector with the required number of bytes, and write to it.
- let mut buf = <Vec<_> as VecExt<_>>::with_capacity(size, GFP_KERNEL)?;
+ let mut buf = KVec::with_capacity(size, GFP_KERNEL)?;
// SAFETY: The buffer stored in `buf` is at least of size `size` and is valid for writes.
let mut f = unsafe { Formatter::from_buffer(buf.as_mut_ptr(), size) };
f.write_fmt(args)?;
@@ -850,10 +849,9 @@ impl<'a> TryFrom<&'a CStr> for CString {
type Error = AllocError;
fn try_from(cstr: &'a CStr) -> Result<CString, AllocError> {
- let mut buf = Vec::new();
+ let mut buf = KVec::new();
- <Vec<_> as VecExt<_>>::extend_from_slice(&mut buf, cstr.as_bytes_with_nul(), GFP_KERNEL)
- .map_err(|_| AllocError)?;
+ buf.extend_from_slice(cstr.as_bytes_with_nul(), GFP_KERNEL)?;
// INVARIANT: The `CStr` and `CString` types have the same invariants for
// the string data, and we copied it over without changes.
diff --git a/rust/kernel/sync/locked_by.rs b/rust/kernel/sync/locked_by.rs
index babc731bd5f6..b94517231fcc 100644
--- a/rust/kernel/sync/locked_by.rs
+++ b/rust/kernel/sync/locked_by.rs
@@ -43,7 +43,7 @@
/// struct InnerDirectory {
/// /// The sum of the bytes used by all files.
/// bytes_used: u64,
-/// _files: Vec<File>,
+/// _files: KVec<File>,
/// }
///
/// struct Directory {
diff --git a/rust/kernel/types.rs b/rust/kernel/types.rs
index f48704db33c1..3c66f2fbd737 100644
--- a/rust/kernel/types.rs
+++ b/rust/kernel/types.rs
@@ -132,7 +132,7 @@ unsafe fn from_foreign(_: *const core::ffi::c_void) -> Self {}
/// # use kernel::types::ScopeGuard;
/// fn example3(arg: bool) -> Result {
/// let mut vec =
-/// ScopeGuard::new_with_data(Vec::new(), |v| pr_info!("vec had {} elements\n", v.len()));
+/// ScopeGuard::new_with_data(KVec::new(), |v| pr_info!("vec had {} elements\n", v.len()));
///
/// vec.push(10u8, GFP_KERNEL)?;
/// if arg {
diff --git a/rust/kernel/uaccess.rs b/rust/kernel/uaccess.rs
index e9347cff99ab..bc011061de45 100644
--- a/rust/kernel/uaccess.rs
+++ b/rust/kernel/uaccess.rs
@@ -11,7 +11,6 @@
prelude::*,
types::{AsBytes, FromBytes},
};
-use alloc::vec::Vec;
use core::ffi::{c_ulong, c_void};
use core::mem::{size_of, MaybeUninit};
@@ -46,7 +45,6 @@
/// every byte in the region.
///
/// ```no_run
-/// use alloc::vec::Vec;
/// use core::ffi::c_void;
/// use kernel::error::Result;
/// use kernel::uaccess::{UserPtr, UserSlice};
@@ -54,7 +52,7 @@
/// fn bytes_add_one(uptr: UserPtr, len: usize) -> Result<()> {
/// let (read, mut write) = UserSlice::new(uptr, len).reader_writer();
///
-/// let mut buf = Vec::new();
+/// let mut buf = KVec::new();
/// read.read_all(&mut buf, GFP_KERNEL)?;
///
/// for b in &mut buf {
@@ -69,7 +67,6 @@
/// Example illustrating a TOCTOU (time-of-check to time-of-use) bug.
///
/// ```no_run
-/// use alloc::vec::Vec;
/// use core::ffi::c_void;
/// use kernel::error::{code::EINVAL, Result};
/// use kernel::uaccess::{UserPtr, UserSlice};
@@ -78,21 +75,21 @@
/// fn is_valid(uptr: UserPtr, len: usize) -> Result<bool> {
/// let read = UserSlice::new(uptr, len).reader();
///
-/// let mut buf = Vec::new();
+/// let mut buf = KVec::new();
/// read.read_all(&mut buf, GFP_KERNEL)?;
///
/// todo!()
/// }
///
/// /// Returns the bytes behind this user pointer if they are valid.
-/// fn get_bytes_if_valid(uptr: UserPtr, len: usize) -> Result<Vec<u8>> {
+/// fn get_bytes_if_valid(uptr: UserPtr, len: usize) -> Result<KVec<u8>> {
/// if !is_valid(uptr, len)? {
/// return Err(EINVAL);
/// }
///
/// let read = UserSlice::new(uptr, len).reader();
///
-/// let mut buf = Vec::new();
+/// let mut buf = KVec::new();
/// read.read_all(&mut buf, GFP_KERNEL)?;
///
/// // THIS IS A BUG! The bytes could have changed since we checked them.
@@ -130,7 +127,7 @@ pub fn new(ptr: UserPtr, length: usize) -> Self {
/// Reads the entirety of the user slice, appending it to the end of the provided buffer.
///
/// Fails with [`EFAULT`] if the read happens on a bad address.
- pub fn read_all(self, buf: &mut Vec<u8>, flags: Flags) -> Result {
+ pub fn read_all(self, buf: &mut KVec<u8>, flags: Flags) -> Result {
self.reader().read_all(buf, flags)
}
@@ -291,9 +288,9 @@ pub fn read<T: FromBytes>(&mut self) -> Result<T> {
/// Reads the entirety of the user slice, appending it to the end of the provided buffer.
///
/// Fails with [`EFAULT`] if the read happens on a bad address.
- pub fn read_all(mut self, buf: &mut Vec<u8>, flags: Flags) -> Result {
+ pub fn read_all(mut self, buf: &mut KVec<u8>, flags: Flags) -> Result {
let len = self.length;
- VecExt::<u8>::reserve(buf, len, flags)?;
+ buf.reserve(len, flags)?;
// The call to `try_reserve` was successful, so the spare capacity is at least `len` bytes
// long.
diff --git a/rust/macros/lib.rs b/rust/macros/lib.rs
index c327fe0617c8..a733168d40f9 100644
--- a/rust/macros/lib.rs
+++ b/rust/macros/lib.rs
@@ -238,7 +238,7 @@ pub fn concat_idents(ts: TokenStream) -> TokenStream {
/// #[pin_data]
/// struct DriverData {
/// #[pin]
-/// queue: Mutex<Vec<Command>>,
+/// queue: Mutex<KVec<Command>>,
/// buf: KBox<[u8; 1024 * 1024]>,
/// }
/// ```
@@ -247,7 +247,7 @@ pub fn concat_idents(ts: TokenStream) -> TokenStream {
/// #[pin_data(PinnedDrop)]
/// struct DriverData {
/// #[pin]
-/// queue: Mutex<Vec<Command>>,
+/// queue: Mutex<KVec<Command>>,
/// buf: KBox<[u8; 1024 * 1024]>,
/// raw_info: *mut Info,
/// }
@@ -277,7 +277,7 @@ pub fn pin_data(inner: TokenStream, item: TokenStream) -> TokenStream {
/// #[pin_data(PinnedDrop)]
/// struct DriverData {
/// #[pin]
-/// queue: Mutex<Vec<Command>>,
+/// queue: Mutex<KVec<Command>>,
/// buf: KBox<[u8; 1024 * 1024]>,
/// raw_info: *mut Info,
/// }
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] 95+ messages in thread* [PATCH v5 17/26] rust: alloc: remove `VecExt` extension
2024-08-12 18:22 [PATCH v5 00/26] Generic `Allocator` support for Rust Danilo Krummrich
` (15 preceding siblings ...)
2024-08-12 18:23 ` [PATCH v5 16/26] rust: treewide: switch to the kernel `Vec` type Danilo Krummrich
@ 2024-08-12 18:23 ` Danilo Krummrich
2024-08-12 18:23 ` [PATCH v5 18/26] rust: alloc: add `Vec` to prelude Danilo Krummrich
` (9 subsequent siblings)
26 siblings, 0 replies; 95+ messages in thread
From: Danilo Krummrich @ 2024-08-12 18:23 UTC (permalink / raw)
To: ojeda, alex.gaynor, wedsonaf, boqun.feng, gary, bjorn3_gh,
benno.lossin, a.hindborg, aliceryhl, akpm
Cc: daniel.almeida, faith.ekstrand, boris.brezillon, lina, mcanal,
zhiw, cjia, jhubbard, airlied, ajanulgu, lyude, linux-kernel,
rust-for-linux, linux-mm, Danilo Krummrich
Now that all existing `Vec` users were moved to the kernel `Vec` type,
remove the `VecExt` extension.
Reviewed-by: Alice Ryhl <aliceryhl@google.com>
Reviewed-by: Benno Lossin <benno.lossin@proton.me>
Signed-off-by: Danilo Krummrich <dakr@kernel.org>
---
rust/kernel/alloc.rs | 1 -
rust/kernel/alloc/vec_ext.rs | 185 -----------------------------------
rust/kernel/prelude.rs | 5 +-
3 files changed, 1 insertion(+), 190 deletions(-)
delete mode 100644 rust/kernel/alloc/vec_ext.rs
diff --git a/rust/kernel/alloc.rs b/rust/kernel/alloc.rs
index 4035644b5dca..820ef6072e02 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] 95+ messages in thread* [PATCH v5 18/26] rust: alloc: add `Vec` to prelude
2024-08-12 18:22 [PATCH v5 00/26] Generic `Allocator` support for Rust Danilo Krummrich
` (16 preceding siblings ...)
2024-08-12 18:23 ` [PATCH v5 17/26] rust: alloc: remove `VecExt` extension Danilo Krummrich
@ 2024-08-12 18:23 ` Danilo Krummrich
2024-08-12 18:23 ` [PATCH v5 19/26] rust: error: use `core::alloc::LayoutError` Danilo Krummrich
` (8 subsequent siblings)
26 siblings, 0 replies; 95+ messages in thread
From: Danilo Krummrich @ 2024-08-12 18:23 UTC (permalink / raw)
To: ojeda, alex.gaynor, wedsonaf, boqun.feng, gary, bjorn3_gh,
benno.lossin, a.hindborg, aliceryhl, akpm
Cc: daniel.almeida, faith.ekstrand, boris.brezillon, lina, mcanal,
zhiw, cjia, jhubbard, airlied, ajanulgu, lyude, linux-kernel,
rust-for-linux, linux-mm, Danilo Krummrich
Now that we removed `VecExt` and the corresponding includes in
prelude.rs, add the new kernel `Vec` type instead.
Reviewed-by: Alice Ryhl <aliceryhl@google.com>
Reviewed-by: Benno Lossin <benno.lossin@proton.me>
Signed-off-by: Danilo Krummrich <dakr@kernel.org>
---
rust/kernel/prelude.rs | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/rust/kernel/prelude.rs b/rust/kernel/prelude.rs
index 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] 95+ messages in thread* [PATCH v5 19/26] rust: error: use `core::alloc::LayoutError`
2024-08-12 18:22 [PATCH v5 00/26] Generic `Allocator` support for Rust Danilo Krummrich
` (17 preceding siblings ...)
2024-08-12 18:23 ` [PATCH v5 18/26] rust: alloc: add `Vec` to prelude Danilo Krummrich
@ 2024-08-12 18:23 ` Danilo Krummrich
2024-08-12 18:23 ` [PATCH v5 20/26] rust: error: check for config `test` in `Error::name` Danilo Krummrich
` (7 subsequent siblings)
26 siblings, 0 replies; 95+ messages in thread
From: Danilo Krummrich @ 2024-08-12 18:23 UTC (permalink / raw)
To: ojeda, alex.gaynor, wedsonaf, boqun.feng, gary, bjorn3_gh,
benno.lossin, a.hindborg, aliceryhl, akpm
Cc: daniel.almeida, faith.ekstrand, boris.brezillon, lina, mcanal,
zhiw, cjia, jhubbard, airlied, ajanulgu, lyude, linux-kernel,
rust-for-linux, linux-mm, Danilo Krummrich
Use `core::alloc::LayoutError` instead of `alloc::alloc::LayoutError` in
preparation to get rid of Rust's alloc crate.
Reviewed-by: Alice Ryhl <aliceryhl@google.com>
Reviewed-by: Benno Lossin <benno.lossin@proton.me>
Signed-off-by: Danilo Krummrich <dakr@kernel.org>
---
rust/kernel/error.rs | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/rust/kernel/error.rs b/rust/kernel/error.rs
index 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] 95+ messages in thread* [PATCH v5 20/26] rust: error: check for config `test` in `Error::name`
2024-08-12 18:22 [PATCH v5 00/26] Generic `Allocator` support for Rust Danilo Krummrich
` (18 preceding siblings ...)
2024-08-12 18:23 ` [PATCH v5 19/26] rust: error: use `core::alloc::LayoutError` Danilo Krummrich
@ 2024-08-12 18:23 ` Danilo Krummrich
2024-08-12 18:23 ` [PATCH v5 21/26] rust: alloc: implement `contains` for `Flags` Danilo Krummrich
` (6 subsequent siblings)
26 siblings, 0 replies; 95+ messages in thread
From: Danilo Krummrich @ 2024-08-12 18:23 UTC (permalink / raw)
To: ojeda, alex.gaynor, wedsonaf, boqun.feng, gary, bjorn3_gh,
benno.lossin, a.hindborg, aliceryhl, akpm
Cc: daniel.almeida, faith.ekstrand, boris.brezillon, lina, mcanal,
zhiw, cjia, jhubbard, airlied, ajanulgu, lyude, linux-kernel,
rust-for-linux, linux-mm, Danilo Krummrich
Additional to `testlib` also check for `test` in `Error::name`. This is
required by a subsequent patch that (indirectly) uses `Error` in test
cases.
Reviewed-by: Alice Ryhl <aliceryhl@google.com>
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] 95+ messages in thread* [PATCH v5 21/26] rust: alloc: implement `contains` for `Flags`
2024-08-12 18:22 [PATCH v5 00/26] Generic `Allocator` support for Rust Danilo Krummrich
` (19 preceding siblings ...)
2024-08-12 18:23 ` [PATCH v5 20/26] rust: error: check for config `test` in `Error::name` Danilo Krummrich
@ 2024-08-12 18:23 ` Danilo Krummrich
2024-08-12 18:23 ` [PATCH v5 22/26] rust: alloc: implement `Cmalloc` in module allocator_test Danilo Krummrich
` (5 subsequent siblings)
26 siblings, 0 replies; 95+ messages in thread
From: Danilo Krummrich @ 2024-08-12 18:23 UTC (permalink / raw)
To: ojeda, alex.gaynor, wedsonaf, boqun.feng, gary, bjorn3_gh,
benno.lossin, a.hindborg, aliceryhl, akpm
Cc: daniel.almeida, faith.ekstrand, boris.brezillon, lina, mcanal,
zhiw, cjia, jhubbard, airlied, ajanulgu, lyude, linux-kernel,
rust-for-linux, linux-mm, Danilo Krummrich
Provide a simple helper function to check whether given flags do
contain one or multiple other flags.
This is used by a subsequent patch implementing the Cmalloc `Allocator`
to check for __GFP_ZERO.
Reviewed-by: Alice Ryhl <aliceryhl@google.com>
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 820ef6072e02..be2bd0f1e74a 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] 95+ messages in thread* [PATCH v5 22/26] rust: alloc: implement `Cmalloc` in module allocator_test
2024-08-12 18:22 [PATCH v5 00/26] Generic `Allocator` support for Rust Danilo Krummrich
` (20 preceding siblings ...)
2024-08-12 18:23 ` [PATCH v5 21/26] rust: alloc: implement `contains` for `Flags` Danilo Krummrich
@ 2024-08-12 18:23 ` Danilo Krummrich
2024-08-13 7:07 ` Heghedus Razvan
2024-08-12 18:23 ` [PATCH v5 23/26] rust: str: test: replace `alloc::format` Danilo Krummrich
` (4 subsequent siblings)
26 siblings, 1 reply; 95+ messages in thread
From: Danilo Krummrich @ 2024-08-12 18:23 UTC (permalink / raw)
To: ojeda, alex.gaynor, wedsonaf, boqun.feng, gary, bjorn3_gh,
benno.lossin, a.hindborg, aliceryhl, akpm
Cc: daniel.almeida, faith.ekstrand, boris.brezillon, lina, mcanal,
zhiw, cjia, jhubbard, airlied, ajanulgu, lyude, linux-kernel,
rust-for-linux, linux-mm, Danilo Krummrich
So far the kernel's `Box` and `Vec` types can't be used by userspace
test cases, since all users of those types (e.g. `CString`) use kernel
allocators for instantiation.
In order to allow userspace test cases to make use of such types as
well, implement the `Cmalloc` allocator within the allocator_test module
and type alias all kernel allocators to `Cmalloc`. The `Cmalloc`
allocator uses libc's realloc() function as allocator backend.
Signed-off-by: Danilo Krummrich <dakr@kernel.org>
---
I know, having an `old_size` parameter would indeed help implementing `Cmalloc`.
However, I really don't want test infrastructure to influence the design of
kernel internal APIs.
It's on the test infrastructure to find a way to deal with it, even if it's more
complicated.
---
rust/kernel/alloc/allocator_test.rs | 175 ++++++++++++++++++++++++++--
1 file changed, 168 insertions(+), 7 deletions(-)
diff --git a/rust/kernel/alloc/allocator_test.rs b/rust/kernel/alloc/allocator_test.rs
index 1b2642c547ec..7a20e64431ea 100644
--- a/rust/kernel/alloc/allocator_test.rs
+++ b/rust/kernel/alloc/allocator_test.rs
@@ -2,20 +2,181 @@
#![allow(missing_docs)]
-use super::{AllocError, Allocator, Flags};
+use super::{flags::*, AllocError, Allocator, Flags};
use core::alloc::Layout;
+use core::cmp;
+use core::mem;
+use core::ptr;
use core::ptr::NonNull;
-pub struct Kmalloc;
+pub struct Cmalloc;
+pub type Kmalloc = Cmalloc;
pub type Vmalloc = Kmalloc;
pub type KVmalloc = Kmalloc;
-unsafe impl Allocator for Kmalloc {
+extern "C" {
+ #[link_name = "aligned_alloc"]
+ fn libc_aligned_alloc(align: usize, size: usize) -> *mut core::ffi::c_void;
+
+ #[link_name = "free"]
+ fn libc_free(ptr: *mut core::ffi::c_void);
+}
+
+struct CmallocData {
+ // The actual size as requested through `Cmalloc::alloc` or `Cmalloc::realloc`.
+ size: usize,
+ // The offset from the pointer returned to the caller of `Cmalloc::alloc` or `Cmalloc::realloc`
+ // to the actual base address of the allocation.
+ offset: usize,
+}
+
+impl Cmalloc {
+ /// Adjust the size and alignment such that we can additionally store `CmallocData` right
+ /// before the actual data described by `layout`.
+ ///
+ /// Example:
+ ///
+ /// For `CmallocData` assume an alignment of 8 and a size of 16.
+ /// For `layout` assume and alignment of 16 and a size of 64.
+ ///
+ /// 0 16 32 96
+ /// |----------------|----------------|------------------------------------------------|
+ /// empty CmallocData data
+ ///
+ /// For this example the returned `Layout` has an alignment of 32 and a size of 96.
+ fn layout_adjust(layout: Layout) -> Result<Layout, AllocError> {
+ let layout = layout.pad_to_align();
+
+ // Ensure that `CmallocData` fits into half the alignment. Additionally, this guarantees
+ // that advancing a pointer aligned to `align` by `align / 2` we still satisfy or exceed
+ // the alignment requested through `layout`.
+ let align = cmp::max(
+ layout.align(),
+ mem::size_of::<CmallocData>().next_power_of_two(),
+ ) * 2;
+
+ // Add the additional space required for `CmallocData`.
+ let size = layout.size() + mem::size_of::<CmallocData>();
+
+ Ok(Layout::from_size_align(size, align)
+ .map_err(|_| AllocError)?
+ .pad_to_align())
+ }
+
+ fn alloc_store_data(layout: Layout) -> Result<NonNull<u8>, AllocError> {
+ let requested_size = layout.size();
+
+ let layout = Self::layout_adjust(layout)?;
+ let min_align = layout.align() / 2;
+
+ // SAFETY: Returns either NULL or a pointer to a memory allocation that satisfies or
+ // exceeds the given size and alignment requirements.
+ let raw_ptr = unsafe { libc_aligned_alloc(layout.align(), layout.size()) } as *mut u8;
+
+ let priv_ptr = NonNull::new(raw_ptr).ok_or(AllocError)?;
+
+ // SAFETY: Advance the pointer by `min_align`. The adjustments from `Self::layout_adjust`
+ // ensure that after this operation the original size and alignment requirements are still
+ // satisfied or exceeded.
+ let ptr = unsafe { priv_ptr.as_ptr().add(min_align) };
+
+ // SAFETY: `min_align` is greater than or equal to the size of `CmallocData`, hence we
+ // don't exceed the allocation boundaries.
+ let data_ptr: *mut CmallocData = unsafe { ptr.sub(mem::size_of::<CmallocData>()) }.cast();
+
+ let data = CmallocData {
+ size: requested_size,
+ offset: min_align,
+ };
+
+ // SAFETY: `data_ptr` is properly aligned and within the allocation boundaries reserved for
+ // `CmallocData`.
+ unsafe { data_ptr.write(data) };
+
+ NonNull::new(ptr).ok_or(AllocError)
+ }
+
+ /// # Safety
+ ///
+ /// `ptr` must have been previously allocated with `Self::alloc_store_data`.
+ unsafe fn data<'a>(ptr: NonNull<u8>) -> &'a CmallocData {
+ // SAFETY: `Self::alloc_store_data` stores the `CmallocData` right before the address
+ // returned to callers of `Self::alloc_store_data`.
+ let data_ptr: *mut CmallocData =
+ unsafe { ptr.as_ptr().sub(mem::size_of::<CmallocData>()) }.cast();
+
+ // SAFETY: The `CmallocData` has been previously stored at this offset with
+ // `Self::alloc_store_data`.
+ unsafe { &*data_ptr }
+ }
+
+ /// # Safety
+ ///
+ /// This function must not be called more than once for the same allocation.
+ ///
+ /// `ptr` must have been previously allocated with `Self::alloc_store_data`.
+ unsafe fn free_read_data(ptr: NonNull<u8>) {
+ // SAFETY: `ptr` has been created by `Self::alloc_store_data`.
+ let data = unsafe { Self::data(ptr) };
+
+ // SAFETY: `ptr` has been created by `Self::alloc_store_data`.
+ let priv_ptr = unsafe { ptr.as_ptr().sub(data.offset) };
+
+ // SAFETY: `priv_ptr` has previously been allocatored with this `Allocator`.
+ unsafe { libc_free(priv_ptr.cast()) };
+ }
+}
+
+unsafe impl Allocator for Cmalloc {
+ fn alloc(layout: Layout, flags: Flags) -> Result<NonNull<[u8]>, AllocError> {
+ if layout.size() == 0 {
+ return Ok(NonNull::slice_from_raw_parts(NonNull::dangling(), 0));
+ }
+
+ let ptr = Self::alloc_store_data(layout)?;
+
+ if flags.contains(__GFP_ZERO) {
+ // SAFETY: `Self::alloc_store_data` guarantees that `ptr` points to memory of at least
+ // `layout.size()` bytes.
+ unsafe { ptr.as_ptr().write_bytes(0, layout.size()) };
+ }
+
+ Ok(NonNull::slice_from_raw_parts(ptr, layout.size()))
+ }
+
unsafe fn realloc(
- _ptr: Option<NonNull<u8>>,
- _layout: Layout,
- _flags: Flags,
+ ptr: Option<NonNull<u8>>,
+ layout: Layout,
+ flags: Flags,
) -> Result<NonNull<[u8]>, AllocError> {
- panic!();
+ let src: NonNull<u8> = if let Some(src) = ptr {
+ src.cast()
+ } else {
+ return Self::alloc(layout, flags);
+ };
+
+ if layout.size() == 0 {
+ // SAFETY: `src` has been created by `Self::alloc_store_data`.
+ unsafe { Self::free_read_data(src) };
+
+ return Ok(NonNull::slice_from_raw_parts(NonNull::dangling(), 0));
+ }
+
+ let dst = Self::alloc(layout, flags)?;
+
+ // SAFETY: `src` has been created by `Self::alloc_store_data`.
+ let data = unsafe { Self::data(src) };
+
+ // SAFETY: `src` has previously been allocated with this `Allocator`; `dst` has just been
+ // newly allocated. Copy up to the smaller of both sizes.
+ unsafe {
+ ptr::copy_nonoverlapping(
+ src.as_ptr(),
+ dst.as_ptr().cast(),
+ cmp::min(layout.size(), data.size),
+ )
+ };
+
+ Ok(dst)
}
}
--
2.45.2
^ permalink raw reply related [flat|nested] 95+ messages in thread* Re: [PATCH v5 22/26] rust: alloc: implement `Cmalloc` in module allocator_test
2024-08-12 18:23 ` [PATCH v5 22/26] rust: alloc: implement `Cmalloc` in module allocator_test Danilo Krummrich
@ 2024-08-13 7:07 ` Heghedus Razvan
2024-08-13 12:34 ` Danilo Krummrich
0 siblings, 1 reply; 95+ messages in thread
From: Heghedus Razvan @ 2024-08-13 7:07 UTC (permalink / raw)
To: Danilo Krummrich, ojeda, alex.gaynor, wedsonaf, boqun.feng, gary,
bjorn3_gh, benno.lossin, a.hindborg, aliceryhl, akpm
Cc: daniel.almeida, faith.ekstrand, boris.brezillon, lina, mcanal,
zhiw, cjia, jhubbard, airlied, ajanulgu, lyude, linux-kernel,
rust-for-linux, linux-mm
On Mon Aug 12, 2024 at 9:23 PM EEST, 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.
>
> It's on the test infrastructure to find a way to deal with it, even if it's more
> complicated.
> ---
> rust/kernel/alloc/allocator_test.rs | 175 ++++++++++++++++++++++++++--
> 1 file changed, 168 insertions(+), 7 deletions(-)
>
> diff --git a/rust/kernel/alloc/allocator_test.rs b/rust/kernel/alloc/allocator_test.rs
> index 1b2642c547ec..7a20e64431ea 100644
> --- a/rust/kernel/alloc/allocator_test.rs
> +++ b/rust/kernel/alloc/allocator_test.rs
> @@ -2,20 +2,181 @@
>
> #![allow(missing_docs)]
>
> -use super::{AllocError, Allocator, Flags};
> +use super::{flags::*, AllocError, Allocator, Flags};
> use core::alloc::Layout;
> +use core::cmp;
> +use core::mem;
> +use core::ptr;
> use core::ptr::NonNull;
>
> -pub struct Kmalloc;
> +pub struct Cmalloc;
> +pub type Kmalloc = Cmalloc;
> pub type Vmalloc = Kmalloc;
> pub type KVmalloc = Kmalloc;
>
> -unsafe impl Allocator for Kmalloc {
> +extern "C" {
> + #[link_name = "aligned_alloc"]
> + fn libc_aligned_alloc(align: usize, size: usize) -> *mut core::ffi::c_void;
> +
> + #[link_name = "free"]
> + fn libc_free(ptr: *mut core::ffi::c_void);
> +}
> +
> +struct CmallocData {
> + // The actual size as requested through `Cmalloc::alloc` or `Cmalloc::realloc`.
> + size: usize,
> + // The offset from the pointer returned to the caller of `Cmalloc::alloc` or `Cmalloc::realloc`
> + // to the actual base address of the allocation.
> + offset: usize,
> +}
> +
> +impl Cmalloc {
> + /// Adjust the size and alignment such that we can additionally store `CmallocData` right
> + /// before the actual data described by `layout`.
> + ///
> + /// Example:
> + ///
> + /// For `CmallocData` assume an alignment of 8 and a size of 16.
> + /// For `layout` assume and alignment of 16 and a size of 64.
> + ///
> + /// 0 16 32 96
> + /// |----------------|----------------|------------------------------------------------|
> + /// empty CmallocData data
> + ///
> + /// For this example the returned `Layout` has an alignment of 32 and a size of 96.
> + fn layout_adjust(layout: Layout) -> Result<Layout, AllocError> {
> + let layout = layout.pad_to_align();
> +
> + // Ensure that `CmallocData` fits into half the alignment. Additionally, this guarantees
> + // that advancing a pointer aligned to `align` by `align / 2` we still satisfy or exceed
> + // the alignment requested through `layout`.
> + let align = cmp::max(
> + layout.align(),
> + mem::size_of::<CmallocData>().next_power_of_two(),
> + ) * 2;
> +
> + // Add the additional space required for `CmallocData`.
> + let size = layout.size() + mem::size_of::<CmallocData>();
> +
> + Ok(Layout::from_size_align(size, align)
> + .map_err(|_| AllocError)?
> + .pad_to_align())
> + }
> +
> + fn alloc_store_data(layout: Layout) -> Result<NonNull<u8>, AllocError> {
> + let requested_size = layout.size();
> +
> + let layout = Self::layout_adjust(layout)?;
> + let min_align = layout.align() / 2;
> +
> + // SAFETY: Returns either NULL or a pointer to a memory allocation that satisfies or
> + // exceeds the given size and alignment requirements.
> + let raw_ptr = unsafe { libc_aligned_alloc(layout.align(), layout.size()) } as *mut u8;
> +
> + let priv_ptr = NonNull::new(raw_ptr).ok_or(AllocError)?;
> +
> + // SAFETY: Advance the pointer by `min_align`. The adjustments from `Self::layout_adjust`
> + // ensure that after this operation the original size and alignment requirements are still
> + // satisfied or exceeded.
> + let ptr = unsafe { priv_ptr.as_ptr().add(min_align) };
> +
> + // SAFETY: `min_align` is greater than or equal to the size of `CmallocData`, hence we
> + // don't exceed the allocation boundaries.
> + let data_ptr: *mut CmallocData = unsafe { ptr.sub(mem::size_of::<CmallocData>()) }.cast();
> +
> + let data = CmallocData {
> + size: requested_size,
> + offset: min_align,
> + };
> +
> + // SAFETY: `data_ptr` is properly aligned and within the allocation boundaries reserved for
> + // `CmallocData`.
> + unsafe { data_ptr.write(data) };
> +
> + NonNull::new(ptr).ok_or(AllocError)
> + }
> +
> + /// # Safety
> + ///
> + /// `ptr` must have been previously allocated with `Self::alloc_store_data`.
> + unsafe fn data<'a>(ptr: NonNull<u8>) -> &'a CmallocData {
> + // SAFETY: `Self::alloc_store_data` stores the `CmallocData` right before the address
> + // returned to callers of `Self::alloc_store_data`.
> + let data_ptr: *mut CmallocData =
> + unsafe { ptr.as_ptr().sub(mem::size_of::<CmallocData>()) }.cast();
> +
> + // SAFETY: The `CmallocData` has been previously stored at this offset with
> + // `Self::alloc_store_data`.
> + unsafe { &*data_ptr }
> + }
> +
> + /// # Safety
> + ///
> + /// This function must not be called more than once for the same allocation.
> + ///
> + /// `ptr` must have been previously allocated with `Self::alloc_store_data`.
> + unsafe fn free_read_data(ptr: NonNull<u8>) {
> + // SAFETY: `ptr` has been created by `Self::alloc_store_data`.
> + let data = unsafe { Self::data(ptr) };
> +
> + // SAFETY: `ptr` has been created by `Self::alloc_store_data`.
> + let priv_ptr = unsafe { ptr.as_ptr().sub(data.offset) };
> +
> + // SAFETY: `priv_ptr` has previously been allocatored with this `Allocator`.
> + unsafe { libc_free(priv_ptr.cast()) };
> + }
> +}
> +
> +unsafe impl Allocator for Cmalloc {
> + fn alloc(layout: Layout, flags: Flags) -> Result<NonNull<[u8]>, AllocError> {
> + if layout.size() == 0 {
> + return Ok(NonNull::slice_from_raw_parts(NonNull::dangling(), 0));
> + }
> +
> + let ptr = Self::alloc_store_data(layout)?;
> +
> + if flags.contains(__GFP_ZERO) {
> + // SAFETY: `Self::alloc_store_data` guarantees that `ptr` points to memory of at least
> + // `layout.size()` bytes.
> + unsafe { ptr.as_ptr().write_bytes(0, layout.size()) };
> + }
> +
> + Ok(NonNull::slice_from_raw_parts(ptr, layout.size()))
> + }
> +
> unsafe fn realloc(
> - _ptr: Option<NonNull<u8>>,
> - _layout: Layout,
> - _flags: Flags,
> + ptr: Option<NonNull<u8>>,
> + layout: Layout,
> + flags: Flags,
> ) -> Result<NonNull<[u8]>, AllocError> {
> - panic!();
> + let src: NonNull<u8> = if let Some(src) = ptr {
> + src.cast()
> + } else {
> + return Self::alloc(layout, flags);
> + };
> +
> + if layout.size() == 0 {
> + // SAFETY: `src` has been created by `Self::alloc_store_data`.
> + unsafe { Self::free_read_data(src) };
> +
> + return Ok(NonNull::slice_from_raw_parts(NonNull::dangling(), 0));
> + }
> +
> + let dst = Self::alloc(layout, flags)?;
> +
> + // SAFETY: `src` has been created by `Self::alloc_store_data`.
> + let data = unsafe { Self::data(src) };
> +
> + // SAFETY: `src` has previously been allocated with this `Allocator`; `dst` has just been
> + // newly allocated. Copy up to the smaller of both sizes.
> + unsafe {
> + ptr::copy_nonoverlapping(
> + src.as_ptr(),
> + dst.as_ptr().cast(),
> + cmp::min(layout.size(), data.size),
> + )
> + };
> +
At this point should we free the src? Or is the original pointer expected to remain valid?
> + Ok(dst)
> }
> }
> --
> 2.45.2
^ permalink raw reply [flat|nested] 95+ messages in thread* Re: [PATCH v5 22/26] rust: alloc: implement `Cmalloc` in module allocator_test
2024-08-13 7:07 ` Heghedus Razvan
@ 2024-08-13 12:34 ` Danilo Krummrich
0 siblings, 0 replies; 95+ messages in thread
From: Danilo Krummrich @ 2024-08-13 12:34 UTC (permalink / raw)
To: Heghedus Razvan
Cc: ojeda, alex.gaynor, wedsonaf, boqun.feng, gary, bjorn3_gh,
benno.lossin, a.hindborg, aliceryhl, akpm, daniel.almeida,
faith.ekstrand, boris.brezillon, lina, mcanal, zhiw, cjia,
jhubbard, airlied, ajanulgu, lyude, linux-kernel, rust-for-linux,
linux-mm
On Tue, Aug 13, 2024 at 07:07:46AM +0000, Heghedus Razvan wrote:
> On Mon Aug 12, 2024 at 9:23 PM EEST, 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.
> >
> > It's on the test infrastructure to find a way to deal with it, even if it's more
> > complicated.
> > ---
> > rust/kernel/alloc/allocator_test.rs | 175 ++++++++++++++++++++++++++--
> > 1 file changed, 168 insertions(+), 7 deletions(-)
> >
> > diff --git a/rust/kernel/alloc/allocator_test.rs b/rust/kernel/alloc/allocator_test.rs
> > index 1b2642c547ec..7a20e64431ea 100644
> > --- a/rust/kernel/alloc/allocator_test.rs
> > +++ b/rust/kernel/alloc/allocator_test.rs
> > @@ -2,20 +2,181 @@
> >
> > #![allow(missing_docs)]
> >
> > -use super::{AllocError, Allocator, Flags};
> > +use super::{flags::*, AllocError, Allocator, Flags};
> > use core::alloc::Layout;
> > +use core::cmp;
> > +use core::mem;
> > +use core::ptr;
> > use core::ptr::NonNull;
> >
> > -pub struct Kmalloc;
> > +pub struct Cmalloc;
> > +pub type Kmalloc = Cmalloc;
> > pub type Vmalloc = Kmalloc;
> > pub type KVmalloc = Kmalloc;
> >
> > -unsafe impl Allocator for Kmalloc {
> > +extern "C" {
> > + #[link_name = "aligned_alloc"]
> > + fn libc_aligned_alloc(align: usize, size: usize) -> *mut core::ffi::c_void;
> > +
> > + #[link_name = "free"]
> > + fn libc_free(ptr: *mut core::ffi::c_void);
> > +}
> > +
> > +struct CmallocData {
> > + // The actual size as requested through `Cmalloc::alloc` or `Cmalloc::realloc`.
> > + size: usize,
> > + // The offset from the pointer returned to the caller of `Cmalloc::alloc` or `Cmalloc::realloc`
> > + // to the actual base address of the allocation.
> > + offset: usize,
> > +}
> > +
> > +impl Cmalloc {
> > + /// Adjust the size and alignment such that we can additionally store `CmallocData` right
> > + /// before the actual data described by `layout`.
> > + ///
> > + /// Example:
> > + ///
> > + /// For `CmallocData` assume an alignment of 8 and a size of 16.
> > + /// For `layout` assume and alignment of 16 and a size of 64.
> > + ///
> > + /// 0 16 32 96
> > + /// |----------------|----------------|------------------------------------------------|
> > + /// empty CmallocData data
> > + ///
> > + /// For this example the returned `Layout` has an alignment of 32 and a size of 96.
> > + fn layout_adjust(layout: Layout) -> Result<Layout, AllocError> {
> > + let layout = layout.pad_to_align();
> > +
> > + // Ensure that `CmallocData` fits into half the alignment. Additionally, this guarantees
> > + // that advancing a pointer aligned to `align` by `align / 2` we still satisfy or exceed
> > + // the alignment requested through `layout`.
> > + let align = cmp::max(
> > + layout.align(),
> > + mem::size_of::<CmallocData>().next_power_of_two(),
> > + ) * 2;
> > +
> > + // Add the additional space required for `CmallocData`.
> > + let size = layout.size() + mem::size_of::<CmallocData>();
> > +
> > + Ok(Layout::from_size_align(size, align)
> > + .map_err(|_| AllocError)?
> > + .pad_to_align())
> > + }
> > +
> > + fn alloc_store_data(layout: Layout) -> Result<NonNull<u8>, AllocError> {
> > + let requested_size = layout.size();
> > +
> > + let layout = Self::layout_adjust(layout)?;
> > + let min_align = layout.align() / 2;
> > +
> > + // SAFETY: Returns either NULL or a pointer to a memory allocation that satisfies or
> > + // exceeds the given size and alignment requirements.
> > + let raw_ptr = unsafe { libc_aligned_alloc(layout.align(), layout.size()) } as *mut u8;
> > +
> > + let priv_ptr = NonNull::new(raw_ptr).ok_or(AllocError)?;
> > +
> > + // SAFETY: Advance the pointer by `min_align`. The adjustments from `Self::layout_adjust`
> > + // ensure that after this operation the original size and alignment requirements are still
> > + // satisfied or exceeded.
> > + let ptr = unsafe { priv_ptr.as_ptr().add(min_align) };
> > +
> > + // SAFETY: `min_align` is greater than or equal to the size of `CmallocData`, hence we
> > + // don't exceed the allocation boundaries.
> > + let data_ptr: *mut CmallocData = unsafe { ptr.sub(mem::size_of::<CmallocData>()) }.cast();
> > +
> > + let data = CmallocData {
> > + size: requested_size,
> > + offset: min_align,
> > + };
> > +
> > + // SAFETY: `data_ptr` is properly aligned and within the allocation boundaries reserved for
> > + // `CmallocData`.
> > + unsafe { data_ptr.write(data) };
> > +
> > + NonNull::new(ptr).ok_or(AllocError)
> > + }
> > +
> > + /// # Safety
> > + ///
> > + /// `ptr` must have been previously allocated with `Self::alloc_store_data`.
> > + unsafe fn data<'a>(ptr: NonNull<u8>) -> &'a CmallocData {
> > + // SAFETY: `Self::alloc_store_data` stores the `CmallocData` right before the address
> > + // returned to callers of `Self::alloc_store_data`.
> > + let data_ptr: *mut CmallocData =
> > + unsafe { ptr.as_ptr().sub(mem::size_of::<CmallocData>()) }.cast();
> > +
> > + // SAFETY: The `CmallocData` has been previously stored at this offset with
> > + // `Self::alloc_store_data`.
> > + unsafe { &*data_ptr }
> > + }
> > +
> > + /// # Safety
> > + ///
> > + /// This function must not be called more than once for the same allocation.
> > + ///
> > + /// `ptr` must have been previously allocated with `Self::alloc_store_data`.
> > + unsafe fn free_read_data(ptr: NonNull<u8>) {
> > + // SAFETY: `ptr` has been created by `Self::alloc_store_data`.
> > + let data = unsafe { Self::data(ptr) };
> > +
> > + // SAFETY: `ptr` has been created by `Self::alloc_store_data`.
> > + let priv_ptr = unsafe { ptr.as_ptr().sub(data.offset) };
> > +
> > + // SAFETY: `priv_ptr` has previously been allocatored with this `Allocator`.
> > + unsafe { libc_free(priv_ptr.cast()) };
> > + }
> > +}
> > +
> > +unsafe impl Allocator for Cmalloc {
> > + fn alloc(layout: Layout, flags: Flags) -> Result<NonNull<[u8]>, AllocError> {
> > + if layout.size() == 0 {
> > + return Ok(NonNull::slice_from_raw_parts(NonNull::dangling(), 0));
> > + }
> > +
> > + let ptr = Self::alloc_store_data(layout)?;
> > +
> > + if flags.contains(__GFP_ZERO) {
> > + // SAFETY: `Self::alloc_store_data` guarantees that `ptr` points to memory of at least
> > + // `layout.size()` bytes.
> > + unsafe { ptr.as_ptr().write_bytes(0, layout.size()) };
> > + }
> > +
> > + Ok(NonNull::slice_from_raw_parts(ptr, layout.size()))
> > + }
> > +
> > unsafe fn realloc(
> > - _ptr: Option<NonNull<u8>>,
> > - _layout: Layout,
> > - _flags: Flags,
> > + ptr: Option<NonNull<u8>>,
> > + layout: Layout,
> > + flags: Flags,
> > ) -> Result<NonNull<[u8]>, AllocError> {
> > - panic!();
> > + let src: NonNull<u8> = if let Some(src) = ptr {
> > + src.cast()
> > + } else {
> > + return Self::alloc(layout, flags);
> > + };
> > +
> > + if layout.size() == 0 {
> > + // SAFETY: `src` has been created by `Self::alloc_store_data`.
> > + unsafe { Self::free_read_data(src) };
> > +
> > + return Ok(NonNull::slice_from_raw_parts(NonNull::dangling(), 0));
> > + }
> > +
> > + let dst = Self::alloc(layout, flags)?;
> > +
> > + // SAFETY: `src` has been created by `Self::alloc_store_data`.
> > + let data = unsafe { Self::data(src) };
> > +
> > + // SAFETY: `src` has previously been allocated with this `Allocator`; `dst` has just been
> > + // newly allocated. Copy up to the smaller of both sizes.
> > + unsafe {
> > + ptr::copy_nonoverlapping(
> > + src.as_ptr(),
> > + dst.as_ptr().cast(),
> > + cmp::min(layout.size(), data.size),
> > + )
> > + };
> > +
> At this point should we free the src? Or is the original pointer expected to remain valid?
Yes, we're indeed missing `Self::free_read_data(src)` here.
>
> > + Ok(dst)
> > }
> > }
> > --
> > 2.45.2
>
>
^ permalink raw reply [flat|nested] 95+ messages in thread
* [PATCH v5 23/26] rust: str: test: replace `alloc::format`
2024-08-12 18:22 [PATCH v5 00/26] Generic `Allocator` support for Rust Danilo Krummrich
` (21 preceding siblings ...)
2024-08-12 18:23 ` [PATCH v5 22/26] rust: alloc: implement `Cmalloc` in module allocator_test Danilo Krummrich
@ 2024-08-12 18:23 ` Danilo Krummrich
2024-08-12 18:23 ` [PATCH v5 24/26] rust: alloc: update module comment of alloc.rs Danilo Krummrich
` (3 subsequent siblings)
26 siblings, 0 replies; 95+ messages in thread
From: Danilo Krummrich @ 2024-08-12 18:23 UTC (permalink / raw)
To: ojeda, alex.gaynor, wedsonaf, boqun.feng, gary, bjorn3_gh,
benno.lossin, a.hindborg, aliceryhl, akpm
Cc: daniel.almeida, faith.ekstrand, boris.brezillon, lina, mcanal,
zhiw, cjia, jhubbard, airlied, ajanulgu, lyude, linux-kernel,
rust-for-linux, linux-mm, Danilo Krummrich
The current implementation of tests in str.rs use `format!` to format
strings for comparison, which, internally, creates a new `String`.
In order to prepare for getting rid of Rust's alloc crate, we have to
cut this dependency. Instead, implement `format!` for `CString`.
Note that for userspace tests, `Kmalloc`, which is backing `CString`'s
memory, is just a type alias to `Cmalloc`.
Reviewed-by: Alice Ryhl <aliceryhl@google.com>
Signed-off-by: Danilo Krummrich <dakr@kernel.org>
---
rust/kernel/str.rs | 23 ++++++++++++++++++++++-
1 file changed, 22 insertions(+), 1 deletion(-)
diff --git a/rust/kernel/str.rs b/rust/kernel/str.rs
index 209ad1bb1a49..726329530338 100644
--- a/rust/kernel/str.rs
+++ b/rust/kernel/str.rs
@@ -523,7 +523,28 @@ macro_rules! c_str {
#[cfg(test)]
mod tests {
use super::*;
- use alloc::format;
+
+ struct String(CString);
+
+ impl String {
+ fn from_fmt(args: fmt::Arguments<'_>) -> Self {
+ String(CString::try_from_fmt(args).unwrap())
+ }
+ }
+
+ impl Deref for String {
+ type Target = str;
+
+ fn deref(&self) -> &str {
+ self.0.to_str().unwrap()
+ }
+ }
+
+ macro_rules! format {
+ ($($f:tt)*) => ({
+ &*String::from_fmt(kernel::fmt!($($f)*))
+ })
+ }
const ALL_ASCII_CHARS: &'static str =
"\\x01\\x02\\x03\\x04\\x05\\x06\\x07\\x08\\x09\\x0a\\x0b\\x0c\\x0d\\x0e\\x0f\
--
2.45.2
^ permalink raw reply related [flat|nested] 95+ messages in thread* [PATCH v5 24/26] rust: alloc: update module comment of alloc.rs
2024-08-12 18:22 [PATCH v5 00/26] Generic `Allocator` support for Rust Danilo Krummrich
` (22 preceding siblings ...)
2024-08-12 18:23 ` [PATCH v5 23/26] rust: str: test: replace `alloc::format` Danilo Krummrich
@ 2024-08-12 18:23 ` Danilo Krummrich
2024-08-12 18:23 ` [PATCH v5 25/26] kbuild: rust: remove the `alloc` crate and `GlobalAlloc` Danilo Krummrich
` (2 subsequent siblings)
26 siblings, 0 replies; 95+ messages in thread
From: Danilo Krummrich @ 2024-08-12 18:23 UTC (permalink / raw)
To: ojeda, alex.gaynor, wedsonaf, boqun.feng, gary, bjorn3_gh,
benno.lossin, a.hindborg, aliceryhl, akpm
Cc: daniel.almeida, faith.ekstrand, boris.brezillon, lina, mcanal,
zhiw, cjia, jhubbard, airlied, ajanulgu, lyude, linux-kernel,
rust-for-linux, linux-mm, Danilo Krummrich
Before we remove Rust's alloc crate, rewrite the module comment in
alloc.rs to avoid a rustdoc warning.
Besides that, the module comment in alloc.rs isn't correct anymore,
we're no longer extending Rust's alloc crate.
Reviewed-by: Alice Ryhl <aliceryhl@google.com>
Reviewed-by: Benno Lossin <benno.lossin@proton.me>
Signed-off-by: Danilo Krummrich <dakr@kernel.org>
---
rust/kernel/alloc.rs | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/rust/kernel/alloc.rs b/rust/kernel/alloc.rs
index be2bd0f1e74a..b2a41359a18d 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] 95+ messages in thread* [PATCH v5 25/26] kbuild: rust: remove the `alloc` crate and `GlobalAlloc`
2024-08-12 18:22 [PATCH v5 00/26] Generic `Allocator` support for Rust Danilo Krummrich
` (23 preceding siblings ...)
2024-08-12 18:23 ` [PATCH v5 24/26] rust: alloc: update module comment of alloc.rs Danilo Krummrich
@ 2024-08-12 18:23 ` Danilo Krummrich
2024-08-12 18:23 ` [PATCH v5 26/26] MAINTAINERS: add entry for the Rust `alloc` module Danilo Krummrich
2024-08-14 19:32 ` [PATCH v5 00/26] Generic `Allocator` support for Rust Boqun Feng
26 siblings, 0 replies; 95+ messages in thread
From: Danilo Krummrich @ 2024-08-12 18:23 UTC (permalink / raw)
To: ojeda, alex.gaynor, wedsonaf, boqun.feng, gary, bjorn3_gh,
benno.lossin, a.hindborg, aliceryhl, akpm
Cc: daniel.almeida, faith.ekstrand, boris.brezillon, lina, mcanal,
zhiw, cjia, jhubbard, airlied, ajanulgu, lyude, linux-kernel,
rust-for-linux, linux-mm, Danilo Krummrich
Now that we have our own `Allocator`, `Box` and `Vec` types we can remove
Rust's `alloc` crate and the `new_uninit` unstable feature.
Also remove `Kmalloc`'s `GlobalAlloc` implementation -- we can't remove
this in a separate patch, since the `alloc` crate requires a
`#[global_allocator]` to set, that implements `GlobalAlloc`.
Signed-off-by: Danilo Krummrich <dakr@kernel.org>
---
rust/Makefile | 44 ++++++-----------------
rust/exports.c | 1 -
rust/kernel/alloc/allocator.rs | 59 ++-----------------------------
scripts/Makefile.build | 7 +---
scripts/generate_rust_analyzer.py | 11 ++----
5 files changed, 15 insertions(+), 107 deletions(-)
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/rust/kernel/alloc/allocator.rs b/rust/kernel/alloc/allocator.rs
index 243d8c49a57b..b1580534947d 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,23 +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 {
- let size = aligned_size(new_layout);
-
- // SAFETY:
- // - `ptr` is either null or a pointer returned from a previous `k{re}alloc()` by the
- // function safety requirement.
- // - `size` is greater than 0 since it's from `layout.size()` (which cannot be zero according
- // to the function safety requirement)
- unsafe { bindings::krealloc(ptr as *const core::ffi::c_void, size, flags.0) as *mut u8 }
-}
-
/// # Invariants
///
/// One of the following `krealloc`, `vrealloc`, `kvrealloc`.
@@ -116,41 +99,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>>,
@@ -187,9 +135,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;
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 \
diff --git a/scripts/generate_rust_analyzer.py b/scripts/generate_rust_analyzer.py
index f270c7b0cf34..3421a9ea3604 100755
--- a/scripts/generate_rust_analyzer.py
+++ b/scripts/generate_rust_analyzer.py
@@ -64,13 +64,6 @@ def generate_crates(srctree, objtree, sysroot_src, external_src, cfgs):
[],
)
- append_crate(
- "alloc",
- sysroot_src / "alloc" / "src" / "lib.rs",
- ["core", "compiler_builtins"],
- cfg=crates_cfgs.get("alloc", []),
- )
-
append_crate(
"macros",
srctree / "rust" / "macros" / "lib.rs",
@@ -96,7 +89,7 @@ def generate_crates(srctree, objtree, sysroot_src, external_src, cfgs):
append_crate(
"kernel",
srctree / "rust" / "kernel" / "lib.rs",
- ["core", "alloc", "macros", "build_error", "bindings"],
+ ["core", "macros", "build_error", "bindings"],
cfg=cfg,
)
crates[-1]["source"] = {
@@ -133,7 +126,7 @@ def generate_crates(srctree, objtree, sysroot_src, external_src, cfgs):
append_crate(
name,
path,
- ["core", "alloc", "kernel"],
+ ["core", "kernel"],
cfg=cfg,
)
--
2.45.2
^ permalink raw reply related [flat|nested] 95+ messages in thread* [PATCH v5 26/26] MAINTAINERS: add entry for the Rust `alloc` module
2024-08-12 18:22 [PATCH v5 00/26] Generic `Allocator` support for Rust Danilo Krummrich
` (24 preceding siblings ...)
2024-08-12 18:23 ` [PATCH v5 25/26] kbuild: rust: remove the `alloc` crate and `GlobalAlloc` Danilo Krummrich
@ 2024-08-12 18:23 ` Danilo Krummrich
2024-08-14 19:32 ` [PATCH v5 00/26] Generic `Allocator` support for Rust Boqun Feng
26 siblings, 0 replies; 95+ messages in thread
From: Danilo Krummrich @ 2024-08-12 18:23 UTC (permalink / raw)
To: ojeda, alex.gaynor, wedsonaf, boqun.feng, gary, bjorn3_gh,
benno.lossin, a.hindborg, aliceryhl, akpm
Cc: daniel.almeida, faith.ekstrand, boris.brezillon, lina, mcanal,
zhiw, cjia, jhubbard, airlied, ajanulgu, lyude, linux-kernel,
rust-for-linux, linux-mm, Danilo Krummrich
Add maintainers entry for the Rust `alloc` module.
Currently, this includes the `Allocator` API itself, `Allocator`
implementations, such as `Kmalloc` or `Vmalloc`, as well as the kernel's
implementation of the primary memory allocation data structures, `Box`
and `Vec`.
Signed-off-by: Danilo Krummrich <dakr@kernel.org>
---
MAINTAINERS | 7 +++++++
1 file changed, 7 insertions(+)
diff --git a/MAINTAINERS b/MAINTAINERS
index 42decde38320..560516b3aaf4 100644
--- a/MAINTAINERS
+++ b/MAINTAINERS
@@ -19925,6 +19925,13 @@ F: scripts/*rust*
F: tools/testing/selftests/rust/
K: \b(?i:rust)\b
+RUST [ALLOC]
+M: Danilo Krummrich <dakr@kernel.org>
+L: rust-for-linux@vger.kernel.org
+S: Maintained
+F: rust/kernel/alloc.rs
+F: rust/kernel/alloc/
+
RXRPC SOCKETS (AF_RXRPC)
M: David Howells <dhowells@redhat.com>
M: Marc Dionne <marc.dionne@auristor.com>
--
2.45.2
^ permalink raw reply related [flat|nested] 95+ messages in thread* Re: [PATCH v5 00/26] Generic `Allocator` support for Rust
2024-08-12 18:22 [PATCH v5 00/26] Generic `Allocator` support for Rust Danilo Krummrich
` (25 preceding siblings ...)
2024-08-12 18:23 ` [PATCH v5 26/26] MAINTAINERS: add entry for the Rust `alloc` module Danilo Krummrich
@ 2024-08-14 19:32 ` Boqun Feng
2024-08-14 20:53 ` Danilo Krummrich
2024-08-15 2:52 ` Danilo Krummrich
26 siblings, 2 replies; 95+ messages in thread
From: Boqun Feng @ 2024-08-14 19:32 UTC (permalink / raw)
To: Danilo Krummrich
Cc: ojeda, alex.gaynor, wedsonaf, gary, bjorn3_gh, benno.lossin,
a.hindborg, aliceryhl, akpm, daniel.almeida, faith.ekstrand,
boris.brezillon, lina, mcanal, zhiw, cjia, jhubbard, airlied,
ajanulgu, lyude, linux-kernel, rust-for-linux, linux-mm
Hi Danilo,
On Mon, Aug 12, 2024 at 08:22:46PM +0200, Danilo Krummrich wrote:
> 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 v5:
> - (safety) comment / documentation fixes suggested by Alice, Benno and Gary
> - remove `Unique<T>` and implement `Send` and `Sync` for `Box` and `Vec`
> - use `KMALLOC_MAX_SIZE` for `KVmalloc` test and add a `Kmalloc` test that
> expects to fail for `KMALLOC_MAX_SIZE`
> - create use constants `KREALLOC`, `VREALLOC` and `KVREALLOC` for
> `ReallocFuncs`
> - drop `Box::drop_contents` for now, will add it again, once I actually rebase
> on the original patch that introduces it
I'm trying to put your series on rust-dev, but I hit a few conflicts due
to the conflict with `Box::drop_contents`, which has been in rust-dev
for a while. And the conflict is not that trivial for me to resolve.
So just a head-up, that's a requirement for me to put it on rust-dev for
more tests from my end ;-)
Regards,
Boqun
> - improve usage of `size_of_val` in `Box`
> - move `InPlaceInit` and `ForeignOwnable` impls into kbox.rs
> - fix missing `Box` conversions in rnull.rs
> - reworked `Cmalloc` to keep track of the size of memory allocations itself
> - remove `GlobalAlloc` together with the `alloc` crate to avoid a linker error
> - remove `alloc` from scripts/generate_rust_analyzer.py
>
[...]
^ permalink raw reply [flat|nested] 95+ messages in thread* Re: [PATCH v5 00/26] Generic `Allocator` support for Rust
2024-08-14 19:32 ` [PATCH v5 00/26] Generic `Allocator` support for Rust Boqun Feng
@ 2024-08-14 20:53 ` Danilo Krummrich
2024-08-15 2:52 ` Danilo Krummrich
1 sibling, 0 replies; 95+ messages in thread
From: Danilo Krummrich @ 2024-08-14 20:53 UTC (permalink / raw)
To: Boqun Feng
Cc: ojeda, alex.gaynor, wedsonaf, gary, bjorn3_gh, benno.lossin,
a.hindborg, aliceryhl, akpm, daniel.almeida, faith.ekstrand,
boris.brezillon, lina, mcanal, zhiw, cjia, jhubbard, airlied,
ajanulgu, lyude, linux-kernel, rust-for-linux, linux-mm
On 8/14/24 9:32 PM, Boqun Feng wrote:
> Hi Danilo,
>
> On Mon, Aug 12, 2024 at 08:22:46PM +0200, Danilo Krummrich wrote:
>> 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 v5:
>> - (safety) comment / documentation fixes suggested by Alice, Benno and Gary
>> - remove `Unique<T>` and implement `Send` and `Sync` for `Box` and `Vec`
>> - use `KMALLOC_MAX_SIZE` for `KVmalloc` test and add a `Kmalloc` test that
>> expects to fail for `KMALLOC_MAX_SIZE`
>> - create use constants `KREALLOC`, `VREALLOC` and `KVREALLOC` for
>> `ReallocFuncs`
>> - drop `Box::drop_contents` for now, will add it again, once I actually rebase
>> on the original patch that introduces it
>
> I'm trying to put your series on rust-dev, but I hit a few conflicts due
> to the conflict with `Box::drop_contents`, which has been in rust-dev
> for a while. And the conflict is not that trivial for me to resolve.
> So just a head-up, that's a requirement for me to put it on rust-dev for
> more tests from my end ;-)
I'll rebase later on and send you a branch.
>
> Regards,
> Boqun
>
>> - improve usage of `size_of_val` in `Box`
>> - move `InPlaceInit` and `ForeignOwnable` impls into kbox.rs
>> - fix missing `Box` conversions in rnull.rs
>> - reworked `Cmalloc` to keep track of the size of memory allocations itself
>> - remove `GlobalAlloc` together with the `alloc` crate to avoid a linker error
>> - remove `alloc` from scripts/generate_rust_analyzer.py
>>
> [...]
>
^ permalink raw reply [flat|nested] 95+ messages in thread
* Re: [PATCH v5 00/26] Generic `Allocator` support for Rust
2024-08-14 19:32 ` [PATCH v5 00/26] Generic `Allocator` support for Rust Boqun Feng
2024-08-14 20:53 ` Danilo Krummrich
@ 2024-08-15 2:52 ` Danilo Krummrich
2024-08-15 9:20 ` Alice Ryhl
2024-08-15 17:19 ` Boqun Feng
1 sibling, 2 replies; 95+ messages in thread
From: Danilo Krummrich @ 2024-08-15 2:52 UTC (permalink / raw)
To: Boqun Feng
Cc: ojeda, alex.gaynor, wedsonaf, gary, bjorn3_gh, benno.lossin,
a.hindborg, aliceryhl, akpm, daniel.almeida, faith.ekstrand,
boris.brezillon, lina, mcanal, zhiw, cjia, jhubbard, airlied,
ajanulgu, lyude, linux-kernel, rust-for-linux, linux-mm
On Wed, Aug 14, 2024 at 12:32:15PM -0700, Boqun Feng wrote:
> Hi Danilo,
>
> I'm trying to put your series on rust-dev, but I hit a few conflicts due
> to the conflict with `Box::drop_contents`, which has been in rust-dev
> for a while. And the conflict is not that trivial for me to resolve.
> So just a head-up, that's a requirement for me to put it on rust-dev for
> more tests from my end ;-)
I rebased everything and you can fetch them from [1].
I resolved the following conflicts:
- for `Box`, implement
- `drop_contents`
- `manually_drop_contents` [2]
- ``move_out` [2]
- `BorrowedMut` for `ForeignOwnable` for `Box<T, A>` and `Pin<Box<T, A>>`
- `InPlaceWrite` and updated `InPlaceInit`
- for `RBTreeNode`, make use of `Box::move_out` to replace the original
implementation partially moving out of `Box`
@Alice: Please have a look at the changes for `RBTreeNode`. Maybe it's also
worth having them in a separate patch.
- Danilo
[1] https://git.kernel.org/pub/scm/linux/kernel/git/dakr/linux.git/log/?h=rust-dev/mm
[2] https://git.kernel.org/pub/scm/linux/kernel/git/dakr/linux.git/commit/?h=rust-dev/mm&id=ef80ccca2ccebf3c7bcafdc13d1bfe81341cbe63
[3] https://git.kernel.org/pub/scm/linux/kernel/git/dakr/linux.git/diff/rust/kernel/rbtree.rs?h=rust-dev/mm&id=c361d66df7fb7760064fbca6bf9d72171c352a73
>
> Regards,
> Boqun
^ permalink raw reply [flat|nested] 95+ messages in thread* Re: [PATCH v5 00/26] Generic `Allocator` support for Rust
2024-08-15 2:52 ` Danilo Krummrich
@ 2024-08-15 9:20 ` Alice Ryhl
2024-08-15 12:33 ` Danilo Krummrich
2024-08-15 17:19 ` Boqun Feng
1 sibling, 1 reply; 95+ messages in thread
From: Alice Ryhl @ 2024-08-15 9:20 UTC (permalink / raw)
To: Danilo Krummrich
Cc: Boqun Feng, ojeda, alex.gaynor, wedsonaf, gary, bjorn3_gh,
benno.lossin, a.hindborg, akpm, daniel.almeida, faith.ekstrand,
boris.brezillon, lina, mcanal, zhiw, cjia, jhubbard, airlied,
ajanulgu, lyude, linux-kernel, rust-for-linux, linux-mm
On Thu, Aug 15, 2024 at 4:52 AM Danilo Krummrich <dakr@kernel.org> wrote:
>
> On Wed, Aug 14, 2024 at 12:32:15PM -0700, Boqun Feng wrote:
> > Hi Danilo,
> >
> > I'm trying to put your series on rust-dev, but I hit a few conflicts due
> > to the conflict with `Box::drop_contents`, which has been in rust-dev
> > for a while. And the conflict is not that trivial for me to resolve.
> > So just a head-up, that's a requirement for me to put it on rust-dev for
> > more tests from my end ;-)
>
> I rebased everything and you can fetch them from [1].
>
> I resolved the following conflicts:
>
> - for `Box`, implement
> - `drop_contents`
> - `manually_drop_contents` [2]
Not sure I like this name. It sounds like something that runs the
destructor, but it does the exact opposite.
> - ``move_out` [2]
> - `BorrowedMut` for `ForeignOwnable` for `Box<T, A>` and `Pin<Box<T, A>>`
> - `InPlaceWrite` and updated `InPlaceInit`
> - for `RBTreeNode`, make use of `Box::move_out` to replace the original
> implementation partially moving out of `Box`
>
> @Alice: Please have a look at the changes for `RBTreeNode`. Maybe it's also
> worth having them in a separate patch.
RBTree changes LGTM.
Alice
^ permalink raw reply [flat|nested] 95+ messages in thread
* Re: [PATCH v5 00/26] Generic `Allocator` support for Rust
2024-08-15 9:20 ` Alice Ryhl
@ 2024-08-15 12:33 ` Danilo Krummrich
2024-08-15 12:34 ` Alice Ryhl
0 siblings, 1 reply; 95+ messages in thread
From: Danilo Krummrich @ 2024-08-15 12:33 UTC (permalink / raw)
To: Alice Ryhl
Cc: Boqun Feng, ojeda, alex.gaynor, wedsonaf, gary, bjorn3_gh,
benno.lossin, a.hindborg, akpm, daniel.almeida, faith.ekstrand,
boris.brezillon, lina, mcanal, zhiw, cjia, jhubbard, airlied,
ajanulgu, lyude, linux-kernel, rust-for-linux, linux-mm
On Thu, Aug 15, 2024 at 11:20:32AM +0200, Alice Ryhl wrote:
> On Thu, Aug 15, 2024 at 4:52 AM Danilo Krummrich <dakr@kernel.org> wrote:
> >
> > On Wed, Aug 14, 2024 at 12:32:15PM -0700, Boqun Feng wrote:
> > > Hi Danilo,
> > >
> > > I'm trying to put your series on rust-dev, but I hit a few conflicts due
> > > to the conflict with `Box::drop_contents`, which has been in rust-dev
> > > for a while. And the conflict is not that trivial for me to resolve.
> > > So just a head-up, that's a requirement for me to put it on rust-dev for
> > > more tests from my end ;-)
> >
> > I rebased everything and you can fetch them from [1].
> >
> > I resolved the following conflicts:
> >
> > - for `Box`, implement
> > - `drop_contents`
> > - `manually_drop_contents` [2]
>
> Not sure I like this name. It sounds like something that runs the
> destructor, but it does the exact opposite.
I thought it kinda makes sense, since it's analogous to `ManuallyDrop::new`.
What about `Box::forget_contents` instead?
>
> > - ``move_out` [2]
> > - `BorrowedMut` for `ForeignOwnable` for `Box<T, A>` and `Pin<Box<T, A>>`
> > - `InPlaceWrite` and updated `InPlaceInit`
> > - for `RBTreeNode`, make use of `Box::move_out` to replace the original
> > implementation partially moving out of `Box`
> >
> > @Alice: Please have a look at the changes for `RBTreeNode`. Maybe it's also
> > worth having them in a separate patch.
>
> RBTree changes LGTM.
>
> Alice
>
^ permalink raw reply [flat|nested] 95+ messages in thread
* Re: [PATCH v5 00/26] Generic `Allocator` support for Rust
2024-08-15 12:33 ` Danilo Krummrich
@ 2024-08-15 12:34 ` Alice Ryhl
2024-08-15 13:33 ` Danilo Krummrich
0 siblings, 1 reply; 95+ messages in thread
From: Alice Ryhl @ 2024-08-15 12:34 UTC (permalink / raw)
To: Danilo Krummrich
Cc: Boqun Feng, ojeda, alex.gaynor, wedsonaf, gary, bjorn3_gh,
benno.lossin, a.hindborg, akpm, daniel.almeida, faith.ekstrand,
boris.brezillon, lina, mcanal, zhiw, cjia, jhubbard, airlied,
ajanulgu, lyude, linux-kernel, rust-for-linux, linux-mm
On Thu, Aug 15, 2024 at 2:33 PM Danilo Krummrich <dakr@kernel.org> wrote:
>
> On Thu, Aug 15, 2024 at 11:20:32AM +0200, Alice Ryhl wrote:
> > On Thu, Aug 15, 2024 at 4:52 AM Danilo Krummrich <dakr@kernel.org> wrote:
> > >
> > > On Wed, Aug 14, 2024 at 12:32:15PM -0700, Boqun Feng wrote:
> > > > Hi Danilo,
> > > >
> > > > I'm trying to put your series on rust-dev, but I hit a few conflicts due
> > > > to the conflict with `Box::drop_contents`, which has been in rust-dev
> > > > for a while. And the conflict is not that trivial for me to resolve.
> > > > So just a head-up, that's a requirement for me to put it on rust-dev for
> > > > more tests from my end ;-)
> > >
> > > I rebased everything and you can fetch them from [1].
> > >
> > > I resolved the following conflicts:
> > >
> > > - for `Box`, implement
> > > - `drop_contents`
> > > - `manually_drop_contents` [2]
> >
> > Not sure I like this name. It sounds like something that runs the
> > destructor, but it does the exact opposite.
>
> I thought it kinda makes sense, since it's analogous to `ManuallyDrop::new`.
>
> What about `Box::forget_contents` instead?
One option is `into_manually_drop`. This uses the convention of using
the `into_*` prefix for conversions that take ownership of the
original value.
Alice
^ permalink raw reply [flat|nested] 95+ messages in thread
* Re: [PATCH v5 00/26] Generic `Allocator` support for Rust
2024-08-15 12:34 ` Alice Ryhl
@ 2024-08-15 13:33 ` Danilo Krummrich
2024-08-15 13:39 ` Benno Lossin
0 siblings, 1 reply; 95+ messages in thread
From: Danilo Krummrich @ 2024-08-15 13:33 UTC (permalink / raw)
To: Alice Ryhl
Cc: Boqun Feng, ojeda, alex.gaynor, wedsonaf, gary, bjorn3_gh,
benno.lossin, a.hindborg, akpm, daniel.almeida, faith.ekstrand,
boris.brezillon, lina, mcanal, zhiw, cjia, jhubbard, airlied,
ajanulgu, lyude, linux-kernel, rust-for-linux, linux-mm
On Thu, Aug 15, 2024 at 02:34:50PM +0200, Alice Ryhl wrote:
> On Thu, Aug 15, 2024 at 2:33 PM Danilo Krummrich <dakr@kernel.org> wrote:
> >
> > On Thu, Aug 15, 2024 at 11:20:32AM +0200, Alice Ryhl wrote:
> > > On Thu, Aug 15, 2024 at 4:52 AM Danilo Krummrich <dakr@kernel.org> wrote:
> > > >
> > > > On Wed, Aug 14, 2024 at 12:32:15PM -0700, Boqun Feng wrote:
> > > > > Hi Danilo,
> > > > >
> > > > > I'm trying to put your series on rust-dev, but I hit a few conflicts due
> > > > > to the conflict with `Box::drop_contents`, which has been in rust-dev
> > > > > for a while. And the conflict is not that trivial for me to resolve.
> > > > > So just a head-up, that's a requirement for me to put it on rust-dev for
> > > > > more tests from my end ;-)
> > > >
> > > > I rebased everything and you can fetch them from [1].
> > > >
> > > > I resolved the following conflicts:
> > > >
> > > > - for `Box`, implement
> > > > - `drop_contents`
> > > > - `manually_drop_contents` [2]
> > >
> > > Not sure I like this name. It sounds like something that runs the
> > > destructor, but it does the exact opposite.
> >
> > I thought it kinda makes sense, since it's analogous to `ManuallyDrop::new`.
> >
> > What about `Box::forget_contents` instead?
>
> One option is `into_manually_drop`. This uses the convention of using
> the `into_*` prefix for conversions that take ownership of the
> original value.
The signature of the current `Box::manually_drop_contents` is the same as for
`Box::drop_contents`, namely
`fn manually_drop_contents(this: Self) -> Box<MaybeUninit<T>, A>`.
`into_manually_drop` seems misleading for for returning a
`Box<MaybeUninit<T>, A>`.
I still think `forget_contents` hits it quite well. Just as `drop_contents`
drops the value, `forget_contents` makes the `Box` forget the value.
>
> Alice
>
^ permalink raw reply [flat|nested] 95+ messages in thread
* Re: [PATCH v5 00/26] Generic `Allocator` support for Rust
2024-08-15 13:33 ` Danilo Krummrich
@ 2024-08-15 13:39 ` Benno Lossin
2024-08-15 14:09 ` Danilo Krummrich
0 siblings, 1 reply; 95+ messages in thread
From: Benno Lossin @ 2024-08-15 13:39 UTC (permalink / raw)
To: Danilo Krummrich, Alice Ryhl
Cc: Boqun Feng, ojeda, alex.gaynor, wedsonaf, gary, bjorn3_gh,
a.hindborg, akpm, daniel.almeida, faith.ekstrand, boris.brezillon,
lina, mcanal, zhiw, cjia, jhubbard, airlied, ajanulgu, lyude,
linux-kernel, rust-for-linux, linux-mm
On 15.08.24 15:33, Danilo Krummrich wrote:
> On Thu, Aug 15, 2024 at 02:34:50PM +0200, Alice Ryhl wrote:
>> On Thu, Aug 15, 2024 at 2:33 PM Danilo Krummrich <dakr@kernel.org> wrote:
>>>
>>> On Thu, Aug 15, 2024 at 11:20:32AM +0200, Alice Ryhl wrote:
>>>> On Thu, Aug 15, 2024 at 4:52 AM Danilo Krummrich <dakr@kernel.org> wrote:
>>>>>
>>>>> On Wed, Aug 14, 2024 at 12:32:15PM -0700, Boqun Feng wrote:
>>>>>> Hi Danilo,
>>>>>>
>>>>>> I'm trying to put your series on rust-dev, but I hit a few conflicts due
>>>>>> to the conflict with `Box::drop_contents`, which has been in rust-dev
>>>>>> for a while. And the conflict is not that trivial for me to resolve.
>>>>>> So just a head-up, that's a requirement for me to put it on rust-dev for
>>>>>> more tests from my end ;-)
>>>>>
>>>>> I rebased everything and you can fetch them from [1].
>>>>>
>>>>> I resolved the following conflicts:
>>>>>
>>>>> - for `Box`, implement
>>>>> - `drop_contents`
>>>>> - `manually_drop_contents` [2]
>>>>
>>>> Not sure I like this name. It sounds like something that runs the
>>>> destructor, but it does the exact opposite.
>>>
>>> I thought it kinda makes sense, since it's analogous to `ManuallyDrop::new`.
>>>
>>> What about `Box::forget_contents` instead?
>>
>> One option is `into_manually_drop`. This uses the convention of using
>> the `into_*` prefix for conversions that take ownership of the
>> original value.
>
> The signature of the current `Box::manually_drop_contents` is the same as for
> `Box::drop_contents`, namely
> `fn manually_drop_contents(this: Self) -> Box<MaybeUninit<T>, A>`.
>
> `into_manually_drop` seems misleading for for returning a
> `Box<MaybeUninit<T>, A>`.
>
> I still think `forget_contents` hits it quite well. Just as `drop_contents`
> drops the value, `forget_contents` makes the `Box` forget the value.
I think `forget_contents` sounds good. Can you please add some more docs
to that function though? Like an example and change "Manually drops the
contents, but keeps the allocation" to "Forgets the contents (does not
run the destructor), but keeps the allocation.".
Another thing that I spotted while looking at the patch, `move_out`
doesn't need the `transmute_copy`, you should be able to just call
`read` on the pointer.
---
Cheers,
Benno
^ permalink raw reply [flat|nested] 95+ messages in thread
* Re: [PATCH v5 00/26] Generic `Allocator` support for Rust
2024-08-15 13:39 ` Benno Lossin
@ 2024-08-15 14:09 ` Danilo Krummrich
2024-08-15 14:19 ` Benno Lossin
0 siblings, 1 reply; 95+ messages in thread
From: Danilo Krummrich @ 2024-08-15 14:09 UTC (permalink / raw)
To: Benno Lossin
Cc: Alice Ryhl, Boqun Feng, ojeda, alex.gaynor, wedsonaf, gary,
bjorn3_gh, a.hindborg, akpm, daniel.almeida, faith.ekstrand,
boris.brezillon, lina, mcanal, zhiw, cjia, jhubbard, airlied,
ajanulgu, lyude, linux-kernel, rust-for-linux, linux-mm
On Thu, Aug 15, 2024 at 01:39:05PM +0000, Benno Lossin wrote:
> On 15.08.24 15:33, Danilo Krummrich wrote:
> > On Thu, Aug 15, 2024 at 02:34:50PM +0200, Alice Ryhl wrote:
> >> On Thu, Aug 15, 2024 at 2:33 PM Danilo Krummrich <dakr@kernel.org> wrote:
> >>>
> >>> On Thu, Aug 15, 2024 at 11:20:32AM +0200, Alice Ryhl wrote:
> >>>> On Thu, Aug 15, 2024 at 4:52 AM Danilo Krummrich <dakr@kernel.org> wrote:
> >>>>>
> >>>>> On Wed, Aug 14, 2024 at 12:32:15PM -0700, Boqun Feng wrote:
> >>>>>> Hi Danilo,
> >>>>>>
> >>>>>> I'm trying to put your series on rust-dev, but I hit a few conflicts due
> >>>>>> to the conflict with `Box::drop_contents`, which has been in rust-dev
> >>>>>> for a while. And the conflict is not that trivial for me to resolve.
> >>>>>> So just a head-up, that's a requirement for me to put it on rust-dev for
> >>>>>> more tests from my end ;-)
> >>>>>
> >>>>> I rebased everything and you can fetch them from [1].
> >>>>>
> >>>>> I resolved the following conflicts:
> >>>>>
> >>>>> - for `Box`, implement
> >>>>> - `drop_contents`
> >>>>> - `manually_drop_contents` [2]
> >>>>
> >>>> Not sure I like this name. It sounds like something that runs the
> >>>> destructor, but it does the exact opposite.
> >>>
> >>> I thought it kinda makes sense, since it's analogous to `ManuallyDrop::new`.
> >>>
> >>> What about `Box::forget_contents` instead?
> >>
> >> One option is `into_manually_drop`. This uses the convention of using
> >> the `into_*` prefix for conversions that take ownership of the
> >> original value.
> >
> > The signature of the current `Box::manually_drop_contents` is the same as for
> > `Box::drop_contents`, namely
> > `fn manually_drop_contents(this: Self) -> Box<MaybeUninit<T>, A>`.
> >
> > `into_manually_drop` seems misleading for for returning a
> > `Box<MaybeUninit<T>, A>`.
> >
> > I still think `forget_contents` hits it quite well. Just as `drop_contents`
> > drops the value, `forget_contents` makes the `Box` forget the value.
>
> I think `forget_contents` sounds good. Can you please add some more docs
> to that function though? Like an example and change "Manually drops the
> contents, but keeps the allocation" to "Forgets the contents (does not
> run the destructor), but keeps the allocation.".
I can't really think of a good real world example other than moving out of a
`Box`, can you? Otherwise, maybe it just shouldn't be public?
>
> Another thing that I spotted while looking at the patch, `move_out`
> doesn't need the `transmute_copy`, you should be able to just call
> `read` on the pointer.
While technically it's the same I thought `transmute_copy` is considered better,
since it has less stict safety requirements?
For `transmute_copy` we only need to say, that dst has the same type as src,
whereas for `read` the pointer must be valid for reads, properly aligned and
point to an initialized value.
>
> ---
> Cheers,
> Benno
>
^ permalink raw reply [flat|nested] 95+ messages in thread
* Re: [PATCH v5 00/26] Generic `Allocator` support for Rust
2024-08-15 14:09 ` Danilo Krummrich
@ 2024-08-15 14:19 ` Benno Lossin
0 siblings, 0 replies; 95+ messages in thread
From: Benno Lossin @ 2024-08-15 14:19 UTC (permalink / raw)
To: Danilo Krummrich
Cc: Alice Ryhl, Boqun Feng, ojeda, alex.gaynor, wedsonaf, gary,
bjorn3_gh, a.hindborg, akpm, daniel.almeida, faith.ekstrand,
boris.brezillon, lina, mcanal, zhiw, cjia, jhubbard, airlied,
ajanulgu, lyude, linux-kernel, rust-for-linux, linux-mm
On 15.08.24 16:09, Danilo Krummrich wrote:
> On Thu, Aug 15, 2024 at 01:39:05PM +0000, Benno Lossin wrote:
>> On 15.08.24 15:33, Danilo Krummrich wrote:
>>> On Thu, Aug 15, 2024 at 02:34:50PM +0200, Alice Ryhl wrote:
>>>> On Thu, Aug 15, 2024 at 2:33 PM Danilo Krummrich <dakr@kernel.org> wrote:
>>>>>
>>>>> On Thu, Aug 15, 2024 at 11:20:32AM +0200, Alice Ryhl wrote:
>>>>>> On Thu, Aug 15, 2024 at 4:52 AM Danilo Krummrich <dakr@kernel.org> wrote:
>>>>>>>
>>>>>>> On Wed, Aug 14, 2024 at 12:32:15PM -0700, Boqun Feng wrote:
>>>>>>>> Hi Danilo,
>>>>>>>>
>>>>>>>> I'm trying to put your series on rust-dev, but I hit a few conflicts due
>>>>>>>> to the conflict with `Box::drop_contents`, which has been in rust-dev
>>>>>>>> for a while. And the conflict is not that trivial for me to resolve.
>>>>>>>> So just a head-up, that's a requirement for me to put it on rust-dev for
>>>>>>>> more tests from my end ;-)
>>>>>>>
>>>>>>> I rebased everything and you can fetch them from [1].
>>>>>>>
>>>>>>> I resolved the following conflicts:
>>>>>>>
>>>>>>> - for `Box`, implement
>>>>>>> - `drop_contents`
>>>>>>> - `manually_drop_contents` [2]
>>>>>>
>>>>>> Not sure I like this name. It sounds like something that runs the
>>>>>> destructor, but it does the exact opposite.
>>>>>
>>>>> I thought it kinda makes sense, since it's analogous to `ManuallyDrop::new`.
>>>>>
>>>>> What about `Box::forget_contents` instead?
>>>>
>>>> One option is `into_manually_drop`. This uses the convention of using
>>>> the `into_*` prefix for conversions that take ownership of the
>>>> original value.
>>>
>>> The signature of the current `Box::manually_drop_contents` is the same as for
>>> `Box::drop_contents`, namely
>>> `fn manually_drop_contents(this: Self) -> Box<MaybeUninit<T>, A>`.
>>>
>>> `into_manually_drop` seems misleading for for returning a
>>> `Box<MaybeUninit<T>, A>`.
>>>
>>> I still think `forget_contents` hits it quite well. Just as `drop_contents`
>>> drops the value, `forget_contents` makes the `Box` forget the value.
>>
>> I think `forget_contents` sounds good. Can you please add some more docs
>> to that function though? Like an example and change "Manually drops the
>> contents, but keeps the allocation" to "Forgets the contents (does not
>> run the destructor), but keeps the allocation.".
>
> I can't really think of a good real world example other than moving out of a
> `Box`, can you? Otherwise, maybe it just shouldn't be public?
Oh I thought you had a user for that function. In that case making it
private makes a lot of sense.
Also, `drop_contents` can be implemented with `forget_contents`, but
that might be a good follow up as a good-first-issue.
>> Another thing that I spotted while looking at the patch, `move_out`
>> doesn't need the `transmute_copy`, you should be able to just call
>> `read` on the pointer.
>
> While technically it's the same I thought `transmute_copy` is considered better,
> since it has less stict safety requirements?
I would avoid `transmute_copy` as much as possible, transmuting
signifies to me that you somehow need to change the type and
`transmute_copy` means that the compiler isn't even able to figure out
that the two types have the same size (they are allowed to have
different size [only `Src` larger than `Dst` though], but I most often
have used this with generics where it was guaranteed to be the same
type, but the compiler failed to notice it.).
> For `transmute_copy` we only need to say, that dst has the same type as src,
> whereas for `read` the pointer must be valid for reads, properly aligned and
> point to an initialized value.
You can input a reference, so you get pointer validity for free, though
you still need to put that in the SAFETY comment.
---
Cheers,
Benno
^ permalink raw reply [flat|nested] 95+ messages in thread
* Re: [PATCH v5 00/26] Generic `Allocator` support for Rust
2024-08-15 2:52 ` Danilo Krummrich
2024-08-15 9:20 ` Alice Ryhl
@ 2024-08-15 17:19 ` Boqun Feng
2024-08-15 17:31 ` Danilo Krummrich
1 sibling, 1 reply; 95+ messages in thread
From: Boqun Feng @ 2024-08-15 17:19 UTC (permalink / raw)
To: Danilo Krummrich
Cc: ojeda, alex.gaynor, wedsonaf, gary, bjorn3_gh, benno.lossin,
a.hindborg, aliceryhl, akpm, daniel.almeida, faith.ekstrand,
boris.brezillon, lina, mcanal, zhiw, cjia, jhubbard, airlied,
ajanulgu, lyude, linux-kernel, rust-for-linux, linux-mm
On Thu, Aug 15, 2024 at 04:52:42AM +0200, Danilo Krummrich wrote:
> On Wed, Aug 14, 2024 at 12:32:15PM -0700, Boqun Feng wrote:
> > Hi Danilo,
> >
> > I'm trying to put your series on rust-dev, but I hit a few conflicts due
> > to the conflict with `Box::drop_contents`, which has been in rust-dev
> > for a while. And the conflict is not that trivial for me to resolve.
> > So just a head-up, that's a requirement for me to put it on rust-dev for
> > more tests from my end ;-)
>
> I rebased everything and you can fetch them from [1].
>
Thanks! I will take a look later today or tomorrow.
> I resolved the following conflicts:
>
> - for `Box`, implement
> - `drop_contents`
> - `manually_drop_contents` [2]
> - ``move_out` [2]
Have you considered naming this `into_inner` which is aligned to std
`Box`?
Regards,
Boqun
> - `BorrowedMut` for `ForeignOwnable` for `Box<T, A>` and `Pin<Box<T, A>>`
> - `InPlaceWrite` and updated `InPlaceInit`
> - for `RBTreeNode`, make use of `Box::move_out` to replace the original
> implementation partially moving out of `Box`
>
> @Alice: Please have a look at the changes for `RBTreeNode`. Maybe it's also
> worth having them in a separate patch.
>
> - Danilo
>
>
> [1] https://git.kernel.org/pub/scm/linux/kernel/git/dakr/linux.git/log/?h=rust-dev/mm
> [2] https://git.kernel.org/pub/scm/linux/kernel/git/dakr/linux.git/commit/?h=rust-dev/mm&id=ef80ccca2ccebf3c7bcafdc13d1bfe81341cbe63
> [3] https://git.kernel.org/pub/scm/linux/kernel/git/dakr/linux.git/diff/rust/kernel/rbtree.rs?h=rust-dev/mm&id=c361d66df7fb7760064fbca6bf9d72171c352a73
>
> >
> > Regards,
> > Boqun
^ permalink raw reply [flat|nested] 95+ messages in thread
* Re: [PATCH v5 00/26] Generic `Allocator` support for Rust
2024-08-15 17:19 ` Boqun Feng
@ 2024-08-15 17:31 ` Danilo Krummrich
0 siblings, 0 replies; 95+ messages in thread
From: Danilo Krummrich @ 2024-08-15 17:31 UTC (permalink / raw)
To: Boqun Feng
Cc: ojeda, alex.gaynor, wedsonaf, gary, bjorn3_gh, benno.lossin,
a.hindborg, aliceryhl, akpm, daniel.almeida, faith.ekstrand,
boris.brezillon, lina, mcanal, zhiw, cjia, jhubbard, airlied,
ajanulgu, lyude, linux-kernel, rust-for-linux, linux-mm
On 8/15/24 7:19 PM, Boqun Feng wrote:
> On Thu, Aug 15, 2024 at 04:52:42AM +0200, Danilo Krummrich wrote:
>> On Wed, Aug 14, 2024 at 12:32:15PM -0700, Boqun Feng wrote:
>>> Hi Danilo,
>>>
>>> I'm trying to put your series on rust-dev, but I hit a few conflicts due
>>> to the conflict with `Box::drop_contents`, which has been in rust-dev
>>> for a while. And the conflict is not that trivial for me to resolve.
>>> So just a head-up, that's a requirement for me to put it on rust-dev for
>>> more tests from my end ;-)
>>
>> I rebased everything and you can fetch them from [1].
>>
>
> Thanks! I will take a look later today or tomorrow.
I'll also send a v6 soon, which will also be based on rust-dev, so can also
take this one then.
>
>> I resolved the following conflicts:
>>
>> - for `Box`, implement
>> - `drop_contents`
>> - `manually_drop_contents` [2]
>> - ``move_out` [2]
>
> Have you considered naming this `into_inner` which is aligned to std
> `Box`?
Seems viable too. I can rename it.
>
> Regards,
> Boqun
>
>> - `BorrowedMut` for `ForeignOwnable` for `Box<T, A>` and `Pin<Box<T, A>>`
>> - `InPlaceWrite` and updated `InPlaceInit`
>> - for `RBTreeNode`, make use of `Box::move_out` to replace the original
>> implementation partially moving out of `Box`
>>
>> @Alice: Please have a look at the changes for `RBTreeNode`. Maybe it's also
>> worth having them in a separate patch.
>>
>> - Danilo
>>
>>
>> [1] https://git.kernel.org/pub/scm/linux/kernel/git/dakr/linux.git/log/?h=rust-dev/mm
>> [2] https://git.kernel.org/pub/scm/linux/kernel/git/dakr/linux.git/commit/?h=rust-dev/mm&id=ef80ccca2ccebf3c7bcafdc13d1bfe81341cbe63
>> [3] https://git.kernel.org/pub/scm/linux/kernel/git/dakr/linux.git/diff/rust/kernel/rbtree.rs?h=rust-dev/mm&id=c361d66df7fb7760064fbca6bf9d72171c352a73
>>
>>>
>>> Regards,
>>> Boqun
>
^ permalink raw reply [flat|nested] 95+ messages in thread