diff mbox series

[v5,09/26] rust: alloc: implement kernel `Box`

Message ID 20240812182355.11641-10-dakr@kernel.org (mailing list archive)
State New
Headers show
Series Generic `Allocator` support for Rust | expand

Commit Message

Danilo Krummrich Aug. 12, 2024, 6:22 p.m. UTC
`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

Comments

Alice Ryhl Aug. 14, 2024, 8:26 a.m. UTC | #1
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
Danilo Krummrich Aug. 14, 2024, 12:22 p.m. UTC | #2
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
>
Benno Lossin Aug. 14, 2024, 5:01 p.m. UTC | #3
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

> +    }
Danilo Krummrich Aug. 14, 2024, 9:58 p.m. UTC | #4
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.
Miguel Ojeda Aug. 15, 2024, 12:44 p.m. UTC | #5
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
Benno Lossin Aug. 15, 2024, 1:24 p.m. UTC | #6
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
Danilo Krummrich Aug. 15, 2024, 2 p.m. UTC | #7
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
>
Benno Lossin Aug. 15, 2024, 2:10 p.m. UTC | #8
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
Danilo Krummrich Aug. 15, 2024, 2:17 p.m. UTC | #9
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
>
diff mbox series

Patch

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