From: Danilo Krummrich <dakr@redhat.com>
To: Benno Lossin <benno.lossin@proton.me>
Cc: 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,
daniel.almeida@collabora.com, faith.ekstrand@collabora.com,
boris.brezillon@collabora.com, lina@asahilina.net,
mcanal@igalia.com, zhiw@nvidia.com, acurrid@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
Subject: Re: [PATCH 01/20] rust: alloc: add `Allocator` trait
Date: Sat, 6 Jul 2024 17:11:41 +0200 [thread overview]
Message-ID: <ZolerSMkVl0C4yfF@pollux.localdomain> (raw)
In-Reply-To: <2c322b00-20f8-4102-9f3b-edab0c0907b9@proton.me>
On Sat, Jul 06, 2024 at 01:17:19PM +0000, Benno Lossin wrote:
> On 06.07.24 13:05, Danilo Krummrich wrote:
> > On Sat, Jul 06, 2024 at 10:33:49AM +0000, Benno Lossin wrote:
> >> On 04.07.24 19:06, Danilo Krummrich wrote:
> >>> +pub unsafe trait Allocator {
> >>> + /// Allocate memory based on `layout` and `flags`.
> >>> + ///
> >>> + /// On success, returns a buffer represented as `NonNull<[u8]>` that satisfies the size an
> >>
> >> typo "an" -> "and"
> >>
> >>> + /// alignment requirements of layout, but may exceed the requested size.
> >>
> >> Also if it may exceed the size, then I wouldn't call that "satisfies the
> >> size [...] requirements".
> >
> > Do you have a better proposal? To me "satisfies or exceeds" sounds reasonable.
>
> I think "satisfies the layout constraints (i.e. minimum size and
> alignment as specified by `layout`)" would be better.
>
> >>> + ///
> >>> + /// This function is equivalent to `realloc` when called with a NULL pointer and an `old_size`
> >>> + /// of `0`.
> >>
> >> This is only true for the default implementation and could be
> >> overridden, since it is not a requirement of implementing this trait to
> >> keep it this way. I would remove this sentence.
> >
> > I could add a bit more generic description and say that for the default impl
> > "This function is eq..."?
> >
> >>
> >>> + fn alloc(&self, layout: Layout, flags: Flags) -> Result<NonNull<[u8]>, AllocError> {
> >>
> >> Instead of using the `Flags` type from the alloc module, we should have
> >> an associated `Flags` type in this trait.
> >
> > What does this give us?
>
> 1. IIRC not all flags can be used with every allocator (or do not have
> an effect) and it would be best if only the working ones are allowed.
Agreed, but I'm not sure if it's worth the effort having different `Flags`
types for that only.
But I guess this and the below argument justify using an associated type. I will
queue this change up.
> 2. that way the design is more flexible and could be upstreamed more
> easily.
>
> >> Similarly, it might also be a good idea to let the implementer specify a
> >> custom error type.
> >
> > Same here, why?
>
> In this case the argument is weaker, but it could allow us to implement
> an allocator with `Error = Infallible`, to statically guarantee
> allocation (e.g. when using GFP_ATOMIC). But at the moment there is no
> user.
GFP_ATOMIC can fail, I guess you mean __GFP_NOFAIL.
Not really sure how this would work other than with separate `alloc_nofail` and
`realloc_nofail` functions?
>
> >>> + // SAFETY: Passing a NULL pointer to `realloc` is valid by it's safety requirements and asks
> >>> + // for a new memory allocation.
> >>> + unsafe { self.realloc(ptr::null_mut(), 0, layout, flags) }
> >>> + }
> >>> +
> >>> + /// Re-allocate an existing memory allocation to satisfy the requested `layout`. If the
> >>> + /// requested size is zero, `realloc` behaves equivalent to `free`.
> >>
> >> This is not guaranteed by the implementation.
> >
> > Not sure what exactly you mean? Is it about "satisfy" again?
>
> If the requested size is zero, the implementation could also leak the
> memory, nothing prevents me from implementing such an Allocator.
Well, hopefully the documentation stating that `realloc` must be implemented
this exact way prevents you from doing otherwise. :-)
Please let me know if I need to document this in a different way if it's not
sufficient as it is.
>
> >>> + ///
> >>> + /// If the requested size is larger than `old_size`, 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 `old_size`, `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
> >>> + ///
> >>> + /// `ptr` must point to an existing and valid memory allocation created by this allocator
> >>> + /// instance of a size of at least `old_size`.
> >>> + ///
> >>> + /// Additionally, `ptr` is allowed to be a NULL pointer; in this case a new memory allocation is
> >>> + /// created.
> >>> + unsafe fn realloc(
> >>> + &self,
> >>> + ptr: *mut u8,
> >>> + old_size: usize,
> >>
> >> Why not request the old layout like the std Allocator's grow/shrink
> >> functions do?
> >
> > Because we only care about the size that needs to be preserved when growing the
> > buffer. The `alignment` field of `Layout` would be wasted.
>
> In the std Allocator they specified an old layout. This is probably
> because of the following: if `Layout` is ever extended to hold another
> property that would need to be updated, the signatures are already
> correct.
> In our case we could change it tree-wide, so I guess we could fix that
> issue when it comes up.
Yes, I think so too.
>
> >>> + layout: Layout,
> >>> + flags: Flags,
> >>> + ) -> Result<NonNull<[u8]>, AllocError>;
> >>> +
> >>> + /// Free an existing memory allocation.
> >>> + ///
> >>> + /// # Safety
> >>> + ///
> >>> + /// `ptr` must point to an existing and valid memory allocation created by this `Allocator`
> >>> + /// instance.
> >>> + unsafe fn free(&self, ptr: *mut u8) {
> >>
> >> `ptr` should be `NonNull<u8>`.
> >
> > Creating a `NonNull` from a raw pointer is an extra operation for any user of
> > `free` and given that all `free` functions in the kernel accept a NULL pointer,
> > I think there is not much value in making this `NonNull`.
>
> I don't think that this argument holds for Rust though. For example,
> `KBox` contains a `Unique` that contains a `NonNull`, so freeing could
> just be done with `free(self.0.0)`.
Agreed, we can indeed make it a `&NonNull<u8>`. However, I find this a bit
inconsistent with the signature of `realloc`.
Should we go with separate `shrink` / `grow`, `free` could be implemented as
shrinking to zero and allowing a NULL pointer makes not much sense.
But as mentioned, I'm not yet seeing the benefit of having `realloc` split into
`grow` and `shrink`.
>
> >>> + // SAFETY: `ptr` is guaranteed to be previously allocated with this `Allocator` or NULL.
> >>> + // Calling `realloc` with a buffer size of zero, frees the buffer `ptr` points to.
> >>> + let _ = unsafe { self.realloc(ptr, 0, Layout::new::<()>(), Flags(0)) };
> >>
> >> Why does the implementer have to guarantee this?
> >
> > Who else can guarantee this?
>
> Only the implementer yes. But they are not forced to do this i.e.
> nothing in the safety requirements of `Allocator` prevents me from doing
> a no-op on reallocating to a zero size.
Ah, I see now, this is the same as your comment on the documentation of
`realloc`. So, this indeed just about missing a safety comment.
>
> >>> + }
> >>> +}
> >>> --
> >>> 2.45.2
> >>>
> >>
> >> More general questions:
> >> - are there functions in the kernel to efficiently allocate zeroed
> >> memory? In that case, the Allocator trait should also have methods
> >> that do that (with a iterating default impl).
> >
> > We do this with GFP flags. In particular, you can pass GFP_ZERO to `alloc` and
> > `realloc` to get zeroed memory. Hence, I think having dedicated functions that
> > just do "flags | GFP_ZERO" would not add much value.
>
> Ah right, no in that case, we don't need it.
>
> >> - I am not sure putting everything into the single realloc function is a
> >> good idea, I like the grow/shrink methods of the std allocator. Is
> >> there a reason aside from concentrating the impl to go for only a
> >> single realloc function?
> >
> > Yes, `krealloc()` already provides exactly the described behaviour. See the
> > implementation of `Kmalloc`.
>
> But `kvmalloc` does not and neither does `vmalloc`. I would prefer
> multiple smaller functions over one big one in this case.
What I forsee is that:
- `alloc` becomes a `grow` from zero.
- `free` becomes a `shrink` to zero.
- `grow` and `shrink` become a `realloc` alias,
because they're almost the same
Wouldn't this just put us were we already are, effectively?
>
> ---
> Cheers,
> Benno
>
next prev parent reply other threads:[~2024-07-06 15:11 UTC|newest]
Thread overview: 41+ messages / expand[flat|nested] mbox.gz Atom feed top
2024-07-04 17:06 [PATCH 00/20] Generic `Allocator` support for Rust Danilo Krummrich
2024-07-04 17:06 ` [PATCH 01/20] rust: alloc: add `Allocator` trait Danilo Krummrich
2024-07-06 10:33 ` Benno Lossin
2024-07-06 11:05 ` Danilo Krummrich
2024-07-06 13:17 ` Benno Lossin
2024-07-06 15:11 ` Danilo Krummrich [this message]
2024-07-06 17:08 ` Benno Lossin
2024-07-06 18:47 ` Danilo Krummrich
2024-07-08 8:12 ` Benno Lossin
2024-07-08 23:12 ` Danilo Krummrich
2024-07-04 17:06 ` [PATCH 02/20] rust: alloc: separate `aligned_size` from `krealloc_aligned` Danilo Krummrich
2024-07-04 17:06 ` [PATCH 03/20] rust: alloc: rename `KernelAllocator` to `Kmalloc` Danilo Krummrich
2024-07-04 17:06 ` [PATCH 04/20] rust: alloc: implement `Allocator` for `Kmalloc` Danilo Krummrich
2024-07-06 10:37 ` Benno Lossin
2024-07-06 11:08 ` Danilo Krummrich
2024-07-04 17:06 ` [PATCH 05/20] rust: alloc: add module `allocator_test` Danilo Krummrich
2024-07-04 17:06 ` [PATCH 06/20] rust: alloc: remove `krealloc_aligned` Danilo Krummrich
2024-07-04 17:06 ` [PATCH 07/20] rust: alloc: implement `Vmalloc` allocator Danilo Krummrich
2024-07-06 10:41 ` Benno Lossin
2024-07-06 11:13 ` Danilo Krummrich
2024-07-06 13:21 ` Benno Lossin
2024-07-06 15:16 ` Danilo Krummrich
2024-07-04 17:06 ` [PATCH 08/20] rust: alloc: implement `KVmalloc` allocator Danilo Krummrich
2024-07-04 17:06 ` [PATCH 09/20] rust: types: implement `Unique<T>` Danilo Krummrich
2024-07-06 10:59 ` Benno Lossin
2024-07-06 12:40 ` Miguel Ojeda
2024-07-06 13:37 ` Benno Lossin
2024-07-04 17:06 ` [PATCH 10/20] rust: alloc: implement `KBox` Danilo Krummrich
2024-07-04 17:06 ` [PATCH 11/20] rust: treewide: switch to `KBox` Danilo Krummrich
2024-07-04 17:06 ` [PATCH 12/20] rust: alloc: remove `BoxExt` extension Danilo Krummrich
2024-07-04 17:06 ` [PATCH 13/20] rust: alloc: implement `KVec` Danilo Krummrich
2024-07-04 17:06 ` [PATCH 14/20] rust: alloc: implement `IntoIterator` for `KVec` Danilo Krummrich
2024-07-04 17:06 ` [PATCH 15/20] rust: alloc: implement `collect` for `IntoIter` Danilo Krummrich
2024-07-04 23:27 ` Boqun Feng
2024-07-05 1:23 ` Danilo Krummrich
2024-07-04 17:06 ` [PATCH 16/20] rust: treewide: switch to `KVec` Danilo Krummrich
2024-07-04 17:06 ` [PATCH 17/20] rust: alloc: remove `VecExt` extension Danilo Krummrich
2024-07-04 17:06 ` [PATCH 18/20] rust: error: use `core::alloc::LayoutError` Danilo Krummrich
2024-07-04 17:06 ` [PATCH 19/20] rust: str: test: replace `alloc::format` Danilo Krummrich
2024-07-04 17:06 ` [PATCH 20/20] kbuild: rust: remove the `alloc` crate Danilo Krummrich
2024-07-06 3:59 ` kernel test robot
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=ZolerSMkVl0C4yfF@pollux.localdomain \
--to=dakr@redhat.com \
--cc=a.hindborg@samsung.com \
--cc=acurrid@nvidia.com \
--cc=airlied@redhat.com \
--cc=ajanulgu@redhat.com \
--cc=alex.gaynor@gmail.com \
--cc=aliceryhl@google.com \
--cc=benno.lossin@proton.me \
--cc=bjorn3_gh@protonmail.com \
--cc=boqun.feng@gmail.com \
--cc=boris.brezillon@collabora.com \
--cc=cjia@nvidia.com \
--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=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).