rust-for-linux.vger.kernel.org archive mirror
 help / color / mirror / Atom feed
From: Benno Lossin <benno.lossin@proton.me>
To: Danilo Krummrich <dakr@kernel.org>,
	ojeda@kernel.org, alex.gaynor@gmail.com, wedsonaf@gmail.com,
	boqun.feng@gmail.com, gary@garyguo.net, bjorn3_gh@protonmail.com,
	a.hindborg@samsung.com, aliceryhl@google.com,
	akpm@linux-foundation.org
Cc: daniel.almeida@collabora.com, faith.ekstrand@collabora.com,
	boris.brezillon@collabora.com, lina@asahilina.net,
	mcanal@igalia.com, zhiw@nvidia.com, cjia@nvidia.com,
	jhubbard@nvidia.com, airlied@redhat.com, ajanulgu@redhat.com,
	lyude@redhat.com, linux-kernel@vger.kernel.org,
	rust-for-linux@vger.kernel.org, linux-mm@kvack.org
Subject: Re: [PATCH v5 09/26] rust: alloc: implement kernel `Box`
Date: Wed, 14 Aug 2024 17:01:34 +0000	[thread overview]
Message-ID: <a69e7280-7291-49f7-a46f-1ad465efce04@proton.me> (raw)
In-Reply-To: <20240812182355.11641-10-dakr@kernel.org>

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

> +    }


  parent reply	other threads:[~2024-08-14 17:01 UTC|newest]

Thread overview: 95+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
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-14 16:13   ` Benno Lossin
2024-08-15  0:16     ` Danilo Krummrich
2024-08-15 13:49       ` Benno Lossin
2024-08-12 18:22 ` [PATCH v5 02/26] rust: alloc: separate `aligned_size` from `krealloc_aligned` Danilo Krummrich
2024-08-12 18:22 ` [PATCH v5 03/26] rust: alloc: rename `KernelAllocator` to `Kmalloc` Danilo Krummrich
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 13:44       ` Alice Ryhl
2024-08-14 13:48         ` Danilo Krummrich
2024-08-14 13:50           ` Alice Ryhl
2024-08-14 14:00             ` Danilo Krummrich
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:01                     ` Danilo Krummrich
2024-08-14 16:02                   ` Miguel Ojeda
2024-08-14 16:16                     ` Miguel Ojeda
2024-08-14 16:56                       ` Danilo Krummrich
2024-08-14 16:21   ` Benno Lossin
2024-08-14 16:59     ` Danilo Krummrich
2024-08-14 17:02       ` Benno Lossin
2024-08-14 17:15         ` Danilo Krummrich
2024-08-14 21:07           ` Benno Lossin
2024-08-14 16:28   ` Benno Lossin
2024-08-14 17:13     ` Danilo Krummrich
2024-08-14 21:10       ` Benno Lossin
2024-08-12 18:22 ` [PATCH v5 05/26] rust: alloc: add module `allocator_test` 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
2024-08-14 16:32   ` Benno Lossin
2024-08-14 22:12     ` Danilo Krummrich
2024-08-14 23:20       ` Danilo Krummrich
2024-08-15  6:48         ` Benno Lossin
2024-08-15 12:29           ` Danilo Krummrich
2024-08-15 13:44             ` Benno Lossin
2024-08-15 14:23               ` Danilo Krummrich
2024-08-15 19:08                 ` Benno Lossin
2024-08-12 18:22 ` [PATCH v5 07/26] rust: alloc: implement `KVmalloc` allocator Danilo Krummrich
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
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 12:29       ` Alice Ryhl
2024-08-14 17:01   ` Benno Lossin [this message]
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
2024-08-15 14:10           ` Benno Lossin
2024-08-15 14:17             ` Danilo Krummrich
2024-08-12 18:22 ` [PATCH v5 10/26] rust: treewide: switch to our kernel `Box` type Danilo Krummrich
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
2024-08-12 18:22 ` [PATCH v5 12/26] rust: alloc: add `Box` to prelude Danilo Krummrich
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 12:36       ` Alice Ryhl
2024-08-15 13:31         ` Benno Lossin
2024-08-14 22:46     ` Danilo Krummrich
2024-08-15  7:30       ` Alice Ryhl
2024-08-15 12:17         ` Danilo Krummrich
2024-08-12 18:23 ` [PATCH v5 14/26] rust: alloc: implement `IntoIterator` for `Vec` Danilo Krummrich
2024-08-12 18:23 ` [PATCH v5 15/26] rust: alloc: implement `collect` for `IntoIter` Danilo Krummrich
2024-08-12 18:23 ` [PATCH v5 16/26] rust: treewide: switch to the kernel `Vec` type Danilo Krummrich
2024-08-12 18:23 ` [PATCH v5 17/26] rust: alloc: remove `VecExt` extension Danilo Krummrich
2024-08-12 18:23 ` [PATCH v5 18/26] rust: alloc: add `Vec` to prelude Danilo Krummrich
2024-08-12 18:23 ` [PATCH v5 19/26] rust: error: use `core::alloc::LayoutError` Danilo Krummrich
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 ` [PATCH v5 21/26] rust: alloc: implement `contains` for `Flags` Danilo Krummrich
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
2024-08-12 18:23 ` [PATCH v5 23/26] rust: str: test: replace `alloc::format` Danilo Krummrich
2024-08-12 18:23 ` [PATCH v5 24/26] rust: alloc: update module comment of alloc.rs Danilo Krummrich
2024-08-12 18:23 ` [PATCH v5 25/26] kbuild: rust: remove the `alloc` crate and `GlobalAlloc` 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
2024-08-14 20:53   ` Danilo Krummrich
2024-08-15  2:52   ` Danilo Krummrich
2024-08-15  9:20     ` Alice Ryhl
2024-08-15 12:33       ` Danilo Krummrich
2024-08-15 12:34         ` Alice Ryhl
2024-08-15 13:33           ` Danilo Krummrich
2024-08-15 13:39             ` Benno Lossin
2024-08-15 14:09               ` Danilo Krummrich
2024-08-15 14:19                 ` Benno Lossin
2024-08-15 17:19     ` Boqun Feng
2024-08-15 17:31       ` Danilo Krummrich

Reply instructions:

You may reply publicly to this message via plain-text email
using any one of the following methods:

* Save the following mbox file, import it into your mail client,
  and reply-to-all from there: mbox

  Avoid top-posting and favor interleaved quoting:
  https://en.wikipedia.org/wiki/Posting_style#Interleaved_style

* Reply using the --to, --cc, and --in-reply-to
  switches of git-send-email(1):

  git send-email \
    --in-reply-to=a69e7280-7291-49f7-a46f-1ad465efce04@proton.me \
    --to=benno.lossin@proton.me \
    --cc=a.hindborg@samsung.com \
    --cc=airlied@redhat.com \
    --cc=ajanulgu@redhat.com \
    --cc=akpm@linux-foundation.org \
    --cc=alex.gaynor@gmail.com \
    --cc=aliceryhl@google.com \
    --cc=bjorn3_gh@protonmail.com \
    --cc=boqun.feng@gmail.com \
    --cc=boris.brezillon@collabora.com \
    --cc=cjia@nvidia.com \
    --cc=dakr@kernel.org \
    --cc=daniel.almeida@collabora.com \
    --cc=faith.ekstrand@collabora.com \
    --cc=gary@garyguo.net \
    --cc=jhubbard@nvidia.com \
    --cc=lina@asahilina.net \
    --cc=linux-kernel@vger.kernel.org \
    --cc=linux-mm@kvack.org \
    --cc=lyude@redhat.com \
    --cc=mcanal@igalia.com \
    --cc=ojeda@kernel.org \
    --cc=rust-for-linux@vger.kernel.org \
    --cc=wedsonaf@gmail.com \
    --cc=zhiw@nvidia.com \
    /path/to/YOUR_REPLY

  https://kernel.org/pub/software/scm/git/docs/git-send-email.html

* If your mail client supports setting the In-Reply-To header
  via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line before the message body.
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for NNTP newsgroup(s).