rust-for-linux.vger.kernel.org archive mirror
 help / color / mirror / Atom feed
* [PATCH] rust: extend kbox with a new constructor
@ 2025-08-07 12:10 Vitaly Wool
  2025-08-07 19:56 ` Danilo Krummrich
  0 siblings, 1 reply; 4+ messages in thread
From: Vitaly Wool @ 2025-08-07 12:10 UTC (permalink / raw)
  To: rust-for-linux
  Cc: linux-kernel, Uladzislau Rezki, Danilo Krummrich, Alice Ryhl,
	Vlastimil Babka, Lorenzo Stoakes, Liam R . Howlett, Miguel Ojeda,
	Alex Gaynor, Boqun Feng, Gary Guo, Bjorn Roy Baron, Benno Lossin,
	Andreas Hindborg, Trevor Gross, Vitaly Wool

From: Alice Ryhl <aliceryhl@google.com>

Add a new constructor to KBox to facilitate KBox creation from a
pinned slice of elements. This allows to efficiently allocate memory for
e.g. arrays of structrures containing spinlocks or mutexes.

Signed-off-by: Alice Ryhl <aliceryhl@google.com>
Signed-off-by: Vitaly Wool <vitaly.wool@konsulko.se>
---
 rust/kernel/alloc/kbox.rs | 51 +++++++++++++++++++++++++++++++++++++++
 1 file changed, 51 insertions(+)

diff --git a/rust/kernel/alloc/kbox.rs b/rust/kernel/alloc/kbox.rs
index 1fef9beb57c8..74877afab0a3 100644
--- a/rust/kernel/alloc/kbox.rs
+++ b/rust/kernel/alloc/kbox.rs
@@ -290,6 +290,57 @@ pub fn pin(x: T, flags: Flags) -> Result<Pin<Box<T, A>>, AllocError>
         Ok(Self::new(x, flags)?.into())
     }
 
+    /// Construct a pinned slice of elements `Pin<Box<[T], A>>`. This is a convenient means for
+    /// creation of e.g. arrays of structrures containing spinlocks or mutexes.
+    ///
+    /// # Examples
+    ///
+    /// ```
+    /// #[pin_data]
+    /// struct Example {
+    ///     c: u32,
+    ///     #[pin]
+    ///     d: SpinLock<Inner>,
+    /// }
+    ///
+    /// impl Example {
+    ///     fn new() -> impl PinInit<Self> {
+    ///         pin_init!(Self {
+    ///             c: 10,
+    ///             d <- new_spinlock!(Inner { a: 20, b: 30 }),
+    ///         })
+    ///     }
+    /// }
+    /// // Allocate a boxed slice of 10 `Example`s.
+    /// let s = KBox::pin_slice(
+    ///     | _i | Example::new(),
+    ///     10,
+    ///     GFP_KERNEL
+    /// )?;
+    /// assert_eq!(s[5].c, 10);
+    /// assert_eq!(s[3].d.lock().a, 20),
+    /// ```
+    pub fn pin_slice<F, I, E>(mut init: F, len: usize, flags: Flags) -> Result<Pin<Box<[T], A>>, E>
+    where
+        F: FnMut(usize) -> I,
+        I: PinInit<T, E>,
+        E: From<AllocError>,
+    {
+        let mut buffer = super::Vec::<T, A>::with_capacity(len, flags)?;
+        for i in 0..len {
+            let ptr = buffer.spare_capacity_mut().as_mut_ptr().cast();
+            // SAFETY: This address is available to be initialized, and it will either be dropped
+            // on a future error or returned as a pinned location.
+            unsafe { init(i).__pinned_init(ptr)? };
+            // SAFETY: We initialized one more value.
+            unsafe { buffer.inc_len(1) };
+        }
+        let (ptr, _, _) = buffer.into_raw_parts();
+        let slice = core::ptr::slice_from_raw_parts_mut(ptr, len);
+        // SAFETY: This memory holds a valid [T] allocated with the right allocator.
+        Ok(Pin::from(unsafe { Box::from_raw(slice) }))
+    }
+
     /// Convert a [`Box<T,A>`] to a [`Pin<Box<T,A>>`]. If `T` does not implement
     /// [`Unpin`], then `x` will be pinned in memory and can't be moved.
     pub fn into_pin(this: Self) -> Pin<Self> {
-- 
2.39.2


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

* Re: [PATCH] rust: extend kbox with a new constructor
  2025-08-07 12:10 [PATCH] rust: extend kbox with a new constructor Vitaly Wool
@ 2025-08-07 19:56 ` Danilo Krummrich
  2025-08-08  9:01   ` Alice Ryhl
  0 siblings, 1 reply; 4+ messages in thread
From: Danilo Krummrich @ 2025-08-07 19:56 UTC (permalink / raw)
  To: Vitaly Wool
  Cc: rust-for-linux, linux-kernel, Uladzislau Rezki, Alice Ryhl,
	Vlastimil Babka, Lorenzo Stoakes, Liam R . Howlett, Miguel Ojeda,
	Alex Gaynor, Boqun Feng, Gary Guo, Bjorn Roy Baron, Benno Lossin,
	Andreas Hindborg, Trevor Gross

On Thu Aug 7, 2025 at 2:10 PM CEST, Vitaly Wool wrote:

Please start the patch subject with "rust: alloc:" and make the subject more
concrete, i.e. name the constructor you add, e.g. "rust: alloc: implement
Box::pin_slice()".

This makes things much more obvious when using 'git log --oneline'.

> From: Alice Ryhl <aliceryhl@google.com>
>
> Add a new constructor to KBox to facilitate KBox creation from a

NIT: You're adding it for all allorcators, hence "Box".

> pinned slice of elements. This allows to efficiently allocate memory for
> e.g. arrays of structrures containing spinlocks or mutexes.

Sounds reasonable, can you please mention where this will be used?

> Signed-off-by: Alice Ryhl <aliceryhl@google.com>
> Signed-off-by: Vitaly Wool <vitaly.wool@konsulko.se>
> ---
>  rust/kernel/alloc/kbox.rs | 51 +++++++++++++++++++++++++++++++++++++++
>  1 file changed, 51 insertions(+)
>
> diff --git a/rust/kernel/alloc/kbox.rs b/rust/kernel/alloc/kbox.rs
> index 1fef9beb57c8..74877afab0a3 100644
> --- a/rust/kernel/alloc/kbox.rs
> +++ b/rust/kernel/alloc/kbox.rs
> @@ -290,6 +290,57 @@ pub fn pin(x: T, flags: Flags) -> Result<Pin<Box<T, A>>, AllocError>
>          Ok(Self::new(x, flags)?.into())
>      }
>  
> +    /// Construct a pinned slice of elements `Pin<Box<[T], A>>`. This is a convenient means for
> +    /// creation of e.g. arrays of structrures containing spinlocks or mutexes.

Please add an empty line after the first sentence.

NIT: "slices of structures"

> +    ///
> +    /// # Examples
> +    ///
> +    /// ```
> +    /// #[pin_data]
> +    /// struct Example {
> +    ///     c: u32,
> +    ///     #[pin]
> +    ///     d: SpinLock<Inner>,
> +    /// }
> +    ///
> +    /// impl Example {
> +    ///     fn new() -> impl PinInit<Self> {
> +    ///         pin_init!(Self {
> +    ///             c: 10,
> +    ///             d <- new_spinlock!(Inner { a: 20, b: 30 }),
> +    ///         })
> +    ///     }
> +    /// }
> +    /// // Allocate a boxed slice of 10 `Example`s.
> +    /// let s = KBox::pin_slice(
> +    ///     | _i | Example::new(),
> +    ///     10,
> +    ///     GFP_KERNEL
> +    /// )?;

How would a more complex example look like where the slice items are not
identical, i.e. the impl PinInit<T, E> objects returned by the FnMut?

Do we need a temporary Vec for this? If so, it would probably make more sense to
have the following signature.

	pub fn pin_slice<F, I, E, Arg>(mut init: F, &[Arg], flags: Flags) -> Result<Pin<Box<[T], A>>, E>
	where
	   F: FnMut(&Arg) -> I,
	   I: PinInit<T, E>,
	   E: From<AllocError>,


Where Arg is some generic type containing the arguments passed to the closure
to create the impl PinInit<T, E>. For the example above Args could just be ().

> +    /// assert_eq!(s[5].c, 10);
> +    /// assert_eq!(s[3].d.lock().a, 20),
> +    /// ```
> +    pub fn pin_slice<F, I, E>(mut init: F, len: usize, flags: Flags) -> Result<Pin<Box<[T], A>>, E>

That's a lot of generics, we should probably consider longer names, e.g.
<Fn, Item, E>, where E is probably obvious enough.

> +    where
> +        F: FnMut(usize) -> I,
> +        I: PinInit<T, E>,
> +        E: From<AllocError>,
> +    {
> +        let mut buffer = super::Vec::<T, A>::with_capacity(len, flags)?;
> +        for i in 0..len {
> +            let ptr = buffer.spare_capacity_mut().as_mut_ptr().cast();
> +            // SAFETY: This address is available to be initialized, and it will either be dropped
> +            // on a future error or returned as a pinned location.

I think this should be a bit closer to

    // - `ptr` is a valid pointer to uninitialized memory.
    // - `ptr` is not used if an error is returned.
    // - `ptr` won't be moved until it is dropped, i.e. it is pinned.

> +            unsafe { init(i).__pinned_init(ptr)? };
> +            // SAFETY: We initialized one more value.

The safety requirement says:

    /// - `additional` must be less than or equal to `self.capacity - self.len`.
    /// - All elements within the interval [`self.len`,`self.len + additional`) must be initialized.

Please explain that the first is covered by with_capacity(len, ...) while to
loop iterator only goes to len - 1 and the second by explaining that the loop
initializes items in ascending order.

> +            unsafe { buffer.inc_len(1) };
> +        }
> +        let (ptr, _, _) = buffer.into_raw_parts();
> +        let slice = core::ptr::slice_from_raw_parts_mut(ptr, len);
> +        // SAFETY: This memory holds a valid [T] allocated with the right allocator.

What's the right allocator? I think you want to say the same allocates as used
for the Vec::with_capacity() call.

> +        Ok(Pin::from(unsafe { Box::from_raw(slice) }))
> +    }


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

* Re: [PATCH] rust: extend kbox with a new constructor
  2025-08-07 19:56 ` Danilo Krummrich
@ 2025-08-08  9:01   ` Alice Ryhl
  2025-08-08  9:10     ` Danilo Krummrich
  0 siblings, 1 reply; 4+ messages in thread
From: Alice Ryhl @ 2025-08-08  9:01 UTC (permalink / raw)
  To: Danilo Krummrich
  Cc: Vitaly Wool, rust-for-linux, linux-kernel, Uladzislau Rezki,
	Vlastimil Babka, Lorenzo Stoakes, Liam R . Howlett, Miguel Ojeda,
	Alex Gaynor, Boqun Feng, Gary Guo, Bjorn Roy Baron, Benno Lossin,
	Andreas Hindborg, Trevor Gross

On Thu, Aug 07, 2025 at 09:56:09PM +0200, Danilo Krummrich wrote:
> On Thu Aug 7, 2025 at 2:10 PM CEST, Vitaly Wool wrote:
> 
> Please start the patch subject with "rust: alloc:" and make the subject more
> concrete, i.e. name the constructor you add, e.g. "rust: alloc: implement
> Box::pin_slice()".
> 
> This makes things much more obvious when using 'git log --oneline'.
> 
> > From: Alice Ryhl <aliceryhl@google.com>
> >
> > Add a new constructor to KBox to facilitate KBox creation from a
> 
> NIT: You're adding it for all allorcators, hence "Box".
> 
> > pinned slice of elements. This allows to efficiently allocate memory for
> > e.g. arrays of structrures containing spinlocks or mutexes.
> 
> Sounds reasonable, can you please mention where this will be used?
> 
> > Signed-off-by: Alice Ryhl <aliceryhl@google.com>
> > Signed-off-by: Vitaly Wool <vitaly.wool@konsulko.se>
> > ---
> >  rust/kernel/alloc/kbox.rs | 51 +++++++++++++++++++++++++++++++++++++++
> >  1 file changed, 51 insertions(+)
> >
> > diff --git a/rust/kernel/alloc/kbox.rs b/rust/kernel/alloc/kbox.rs
> > index 1fef9beb57c8..74877afab0a3 100644
> > --- a/rust/kernel/alloc/kbox.rs
> > +++ b/rust/kernel/alloc/kbox.rs
> > @@ -290,6 +290,57 @@ pub fn pin(x: T, flags: Flags) -> Result<Pin<Box<T, A>>, AllocError>
> >          Ok(Self::new(x, flags)?.into())
> >      }
> >  
> > +    /// Construct a pinned slice of elements `Pin<Box<[T], A>>`. This is a convenient means for
> > +    /// creation of e.g. arrays of structrures containing spinlocks or mutexes.
> 
> Please add an empty line after the first sentence.
> 
> NIT: "slices of structures"
> 
> > +    ///
> > +    /// # Examples
> > +    ///
> > +    /// ```
> > +    /// #[pin_data]
> > +    /// struct Example {
> > +    ///     c: u32,
> > +    ///     #[pin]
> > +    ///     d: SpinLock<Inner>,
> > +    /// }
> > +    ///
> > +    /// impl Example {
> > +    ///     fn new() -> impl PinInit<Self> {
> > +    ///         pin_init!(Self {
> > +    ///             c: 10,
> > +    ///             d <- new_spinlock!(Inner { a: 20, b: 30 }),
> > +    ///         })
> > +    ///     }
> > +    /// }
> > +    /// // Allocate a boxed slice of 10 `Example`s.
> > +    /// let s = KBox::pin_slice(
> > +    ///     | _i | Example::new(),
> > +    ///     10,
> > +    ///     GFP_KERNEL
> > +    /// )?;
> 
> How would a more complex example look like where the slice items are not
> identical, i.e. the impl PinInit<T, E> objects returned by the FnMut?
> 
> Do we need a temporary Vec for this? If so, it would probably make more sense to
> have the following signature.
> 
> 	pub fn pin_slice<F, I, E, Arg>(mut init: F, &[Arg], flags: Flags) -> Result<Pin<Box<[T], A>>, E>
> 	where
> 	   F: FnMut(&Arg) -> I,
> 	   I: PinInit<T, E>,
> 	   E: From<AllocError>,
> 
> 
> Where Arg is some generic type containing the arguments passed to the closure
> to create the impl PinInit<T, E>. For the example above Args could just be ().

Forcing the user to construct an array of the appropriate length seems
unfortunate. Right now we provide the index being initialized, which you
can use to index an array if that's what you want.

I used a temporary Vec because its destructor does the right thing we if
exit early on error.

Alice

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

* Re: [PATCH] rust: extend kbox with a new constructor
  2025-08-08  9:01   ` Alice Ryhl
@ 2025-08-08  9:10     ` Danilo Krummrich
  0 siblings, 0 replies; 4+ messages in thread
From: Danilo Krummrich @ 2025-08-08  9:10 UTC (permalink / raw)
  To: Alice Ryhl
  Cc: Vitaly Wool, rust-for-linux, linux-kernel, Uladzislau Rezki,
	Vlastimil Babka, Lorenzo Stoakes, Liam R . Howlett, Miguel Ojeda,
	Alex Gaynor, Boqun Feng, Gary Guo, Bjorn Roy Baron, Benno Lossin,
	Andreas Hindborg, Trevor Gross

On 8/8/25 11:01 AM, Alice Ryhl wrote:
> Right now we provide the index being initialized, which you
> can use to index an array if that's what you want.

Fair enough, let's keep just the usize then.

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

end of thread, other threads:[~2025-08-08  9:10 UTC | newest]

Thread overview: 4+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2025-08-07 12:10 [PATCH] rust: extend kbox with a new constructor Vitaly Wool
2025-08-07 19:56 ` Danilo Krummrich
2025-08-08  9:01   ` Alice Ryhl
2025-08-08  9:10     ` Danilo Krummrich

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