public inbox for stable@vger.kernel.org
 help / color / mirror / Atom feed
From: Miguel Ojeda <ojeda@kernel.org>
To: Greg Kroah-Hartman <gregkh@linuxfoundation.org>,
	Sasha Levin <sashal@kernel.org>,
	stable@vger.kernel.org
Cc: Danilo Krummrich <dakr@kernel.org>,
	Alice Ryhl <aliceryhl@google.com>, Alyssa Ross <hi@alyssa.is>,
	NoisyCoil <noisycoil@disroot.org>,
	patches@lists.linux.dev, Miguel Ojeda <ojeda@kernel.org>
Subject: [PATCH 6.12.y 37/60] rust: alloc: implement `collect` for `IntoIter`
Date: Fri,  7 Mar 2025 23:49:44 +0100	[thread overview]
Message-ID: <20250307225008.779961-38-ojeda@kernel.org> (raw)
In-Reply-To: <20250307225008.779961-1-ojeda@kernel.org>

From: Danilo Krummrich <dakr@kernel.org>

commit 93e602310f87b7b515b86a8f919cc0799387e5c3 upstream.

Currently, we can't implement `FromIterator`. There are a couple of
issues with this trait in the kernel, namely:

  - Rust's specialization feature is unstable. This prevents us to
    optimize for the special case where `I::IntoIter` equals `Vec`'s
    `IntoIter` type.
  - We also can't use `I::IntoIter`'s type ID either to work around this,
    since `FromIterator` doesn't require this type to be `'static`.
  - `FromIterator::from_iter` does return `Self` instead of
    `Result<Self, AllocError>`, hence we can't properly handle allocation
    failures.
  - Neither `Iterator::collect` nor `FromIterator::from_iter` can handle
    additional allocation flags.

Instead, provide `IntoIter::collect`, such that we can at least convert
`IntoIter` into a `Vec` again.

Reviewed-by: Alice Ryhl <aliceryhl@google.com>
Reviewed-by: Benno Lossin <benno.lossin@proton.me>
Signed-off-by: Danilo Krummrich <dakr@kernel.org>
Link: https://lore.kernel.org/r/20241004154149.93856-19-dakr@kernel.org
[ Added newline in documentation, changed case of section to be
  consistent with an existing one, fixed typo. - Miguel ]
Signed-off-by: Miguel Ojeda <ojeda@kernel.org>
---
 rust/kernel/alloc/kvec.rs | 95 +++++++++++++++++++++++++++++++++++++++
 1 file changed, 95 insertions(+)

diff --git a/rust/kernel/alloc/kvec.rs b/rust/kernel/alloc/kvec.rs
index 4ddc2ca6cd32..ae9d072741ce 100644
--- a/rust/kernel/alloc/kvec.rs
+++ b/rust/kernel/alloc/kvec.rs
@@ -692,6 +692,101 @@ pub struct IntoIter<T, A: Allocator> {
     _p: PhantomData<A>,
 }
 
+impl<T, A> IntoIter<T, A>
+where
+    A: Allocator,
+{
+    fn into_raw_parts(self) -> (*mut T, NonNull<T>, usize, usize) {
+        let me = ManuallyDrop::new(self);
+        let ptr = me.ptr;
+        let buf = me.buf;
+        let len = me.len;
+        let cap = me.layout.len();
+        (ptr, buf, len, cap)
+    }
+
+    /// Same as `Iterator::collect` but specialized for `Vec`'s `IntoIter`.
+    ///
+    /// # Examples
+    ///
+    /// ```
+    /// let v = kernel::kvec![1, 2, 3]?;
+    /// let mut it = v.into_iter();
+    ///
+    /// assert_eq!(it.next(), Some(1));
+    ///
+    /// let v = it.collect(GFP_KERNEL);
+    /// assert_eq!(v, [2, 3]);
+    ///
+    /// # Ok::<(), Error>(())
+    /// ```
+    ///
+    /// # Implementation details
+    ///
+    /// Currently, we can't implement `FromIterator`. There are a couple of issues with this trait
+    /// in the kernel, namely:
+    ///
+    /// - Rust's specialization feature is unstable. This prevents us to optimize for the special
+    ///   case where `I::IntoIter` equals `Vec`'s `IntoIter` type.
+    /// - We also can't use `I::IntoIter`'s type ID either to work around this, since `FromIterator`
+    ///   doesn't require this type to be `'static`.
+    /// - `FromIterator::from_iter` does return `Self` instead of `Result<Self, AllocError>`, hence
+    ///   we can't properly handle allocation failures.
+    /// - Neither `Iterator::collect` nor `FromIterator::from_iter` can handle additional allocation
+    ///   flags.
+    ///
+    /// Instead, provide `IntoIter::collect`, such that we can at least convert a `IntoIter` into a
+    /// `Vec` again.
+    ///
+    /// Note that `IntoIter::collect` doesn't require `Flags`, since it re-uses the existing backing
+    /// buffer. However, this backing buffer may be shrunk to the actual count of elements.
+    pub fn collect(self, flags: Flags) -> Vec<T, A> {
+        let old_layout = self.layout;
+        let (mut ptr, buf, len, mut cap) = self.into_raw_parts();
+        let has_advanced = ptr != buf.as_ptr();
+
+        if has_advanced {
+            // Copy the contents we have advanced to at the beginning of the buffer.
+            //
+            // SAFETY:
+            // - `ptr` is valid for reads of `len * size_of::<T>()` bytes,
+            // - `buf.as_ptr()` is valid for writes of `len * size_of::<T>()` bytes,
+            // - `ptr` and `buf.as_ptr()` are not be subject to aliasing restrictions relative to
+            //   each other,
+            // - both `ptr` and `buf.ptr()` are properly aligned.
+            unsafe { ptr::copy(ptr, buf.as_ptr(), len) };
+            ptr = buf.as_ptr();
+
+            // SAFETY: `len` is guaranteed to be smaller than `self.layout.len()`.
+            let layout = unsafe { ArrayLayout::<T>::new_unchecked(len) };
+
+            // SAFETY: `buf` points to the start of the backing buffer and `len` is guaranteed to be
+            // smaller than `cap`. Depending on `alloc` this operation may shrink the buffer or leaves
+            // it as it is.
+            ptr = match unsafe {
+                A::realloc(Some(buf.cast()), layout.into(), old_layout.into(), flags)
+            } {
+                // If we fail to shrink, which likely can't even happen, continue with the existing
+                // buffer.
+                Err(_) => ptr,
+                Ok(ptr) => {
+                    cap = len;
+                    ptr.as_ptr().cast()
+                }
+            };
+        }
+
+        // SAFETY: If the iterator has been advanced, the advanced elements have been copied to
+        // the beginning of the buffer and `len` has been adjusted accordingly.
+        //
+        // - `ptr` is guaranteed to point to the start of the backing buffer.
+        // - `cap` is either the original capacity or, after shrinking the buffer, equal to `len`.
+        // - `alloc` is guaranteed to be unchanged since `into_iter` has been called on the original
+        //   `Vec`.
+        unsafe { Vec::from_raw_parts(ptr, len, cap) }
+    }
+}
+
 impl<T, A> Iterator for IntoIter<T, A>
 where
     A: Allocator,
-- 
2.48.1


  parent reply	other threads:[~2025-03-07 22:52 UTC|newest]

Thread overview: 72+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2025-03-07 22:49 [PATCH 6.12.y 00/60] `alloc`, `#[expect]` and "Custom FFI" Miguel Ojeda
2025-03-07 22:49 ` [PATCH 6.12.y 01/60] rust: workqueue: remove unneeded ``#[allow(clippy::new_ret_no_self)]` Miguel Ojeda
2025-03-07 22:49 ` [PATCH 6.12.y 02/60] rust: sort global Rust flags Miguel Ojeda
2025-03-07 22:49 ` [PATCH 6.12.y 03/60] rust: types: avoid repetition in `{As,From}Bytes` impls Miguel Ojeda
2025-03-07 22:49 ` [PATCH 6.12.y 04/60] rust: enable `clippy::undocumented_unsafe_blocks` lint Miguel Ojeda
2025-03-07 22:49 ` [PATCH 6.12.y 05/60] rust: enable `clippy::unnecessary_safety_comment` lint Miguel Ojeda
2025-03-07 22:49 ` [PATCH 6.12.y 06/60] rust: enable `clippy::unnecessary_safety_doc` lint Miguel Ojeda
2025-03-07 22:49 ` [PATCH 6.12.y 07/60] rust: enable `clippy::ignored_unit_patterns` lint Miguel Ojeda
2025-03-07 22:49 ` [PATCH 6.12.y 08/60] rust: enable `rustdoc::unescaped_backticks` lint Miguel Ojeda
2025-03-07 22:49 ` [PATCH 6.12.y 09/60] rust: init: remove unneeded `#[allow(clippy::disallowed_names)]` Miguel Ojeda
2025-03-07 22:49 ` [PATCH 6.12.y 10/60] rust: sync: remove unneeded `#[allow(clippy::non_send_fields_in_send_ty)]` Miguel Ojeda
2025-03-07 22:49 ` [PATCH 6.12.y 11/60] rust: introduce `.clippy.toml` Miguel Ojeda
2025-03-07 22:49 ` [PATCH 6.12.y 12/60] rust: replace `clippy::dbg_macro` with `disallowed_macros` Miguel Ojeda
2025-03-07 22:49 ` [PATCH 6.12.y 13/60] rust: provide proper code documentation titles Miguel Ojeda
2025-03-07 22:49 ` [PATCH 6.12.y 14/60] rust: enable Clippy's `check-private-items` Miguel Ojeda
2025-03-07 22:49 ` [PATCH 6.12.y 15/60] Documentation: rust: add coding guidelines on lints Miguel Ojeda
2025-03-07 22:49 ` [PATCH 6.12.y 16/60] rust: start using the `#[expect(...)]` attribute Miguel Ojeda
2025-03-07 22:49 ` [PATCH 6.12.y 17/60] Documentation: rust: discuss `#[expect(...)]` in the guidelines Miguel Ojeda
2025-03-07 22:49 ` [PATCH 6.12.y 18/60] rust: error: make conversion functions public Miguel Ojeda
2025-03-07 22:49 ` [PATCH 6.12.y 19/60] rust: error: optimize error type to use nonzero Miguel Ojeda
2025-03-07 22:49 ` [PATCH 6.12.y 20/60] rust: alloc: add `Allocator` trait Miguel Ojeda
2025-03-07 22:49 ` [PATCH 6.12.y 21/60] rust: alloc: separate `aligned_size` from `krealloc_aligned` Miguel Ojeda
2025-03-07 22:49 ` [PATCH 6.12.y 22/60] rust: alloc: rename `KernelAllocator` to `Kmalloc` Miguel Ojeda
2025-03-07 22:49 ` [PATCH 6.12.y 23/60] rust: alloc: implement `ReallocFunc` Miguel Ojeda
2025-03-07 22:49 ` [PATCH 6.12.y 24/60] rust: alloc: make `allocator` module public Miguel Ojeda
2025-03-07 22:49 ` [PATCH 6.12.y 25/60] rust: alloc: implement `Allocator` for `Kmalloc` Miguel Ojeda
2025-03-07 22:49 ` [PATCH 6.12.y 26/60] rust: alloc: add module `allocator_test` Miguel Ojeda
2025-03-07 22:49 ` [PATCH 6.12.y 27/60] rust: alloc: implement `Vmalloc` allocator Miguel Ojeda
2025-03-07 22:49 ` [PATCH 6.12.y 28/60] rust: alloc: implement `KVmalloc` allocator Miguel Ojeda
2025-03-07 22:49 ` [PATCH 6.12.y 29/60] rust: alloc: add __GFP_NOWARN to `Flags` Miguel Ojeda
2025-03-07 22:49 ` [PATCH 6.12.y 30/60] rust: alloc: implement kernel `Box` Miguel Ojeda
2025-03-07 22:49 ` [PATCH 6.12.y 31/60] rust: treewide: switch to our kernel `Box` type Miguel Ojeda
2025-03-07 22:49 ` [PATCH 6.12.y 32/60] rust: alloc: remove extension of std's `Box` Miguel Ojeda
2025-03-07 22:49 ` [PATCH 6.12.y 33/60] rust: alloc: add `Box` to prelude Miguel Ojeda
2025-03-07 22:49 ` [PATCH 6.12.y 34/60] rust: alloc: introduce `ArrayLayout` Miguel Ojeda
2025-03-07 22:49 ` [PATCH 6.12.y 35/60] rust: alloc: implement kernel `Vec` type Miguel Ojeda
2025-03-07 22:49 ` [PATCH 6.12.y 36/60] rust: alloc: implement `IntoIterator` for `Vec` Miguel Ojeda
2025-03-07 22:49 ` Miguel Ojeda [this message]
2025-03-07 22:49 ` [PATCH 6.12.y 38/60] rust: treewide: switch to the kernel `Vec` type Miguel Ojeda
2025-03-07 22:49 ` [PATCH 6.12.y 39/60] rust: alloc: remove `VecExt` extension Miguel Ojeda
2025-03-07 22:49 ` [PATCH 6.12.y 40/60] rust: alloc: add `Vec` to prelude Miguel Ojeda
2025-03-07 22:49 ` [PATCH 6.12.y 41/60] rust: error: use `core::alloc::LayoutError` Miguel Ojeda
2025-03-07 22:49 ` [PATCH 6.12.y 42/60] rust: error: check for config `test` in `Error::name` Miguel Ojeda
2025-03-07 22:49 ` [PATCH 6.12.y 43/60] rust: alloc: implement `contains` for `Flags` Miguel Ojeda
2025-03-07 22:49 ` [PATCH 6.12.y 44/60] rust: alloc: implement `Cmalloc` in module allocator_test Miguel Ojeda
2025-03-07 22:49 ` [PATCH 6.12.y 45/60] rust: str: test: replace `alloc::format` Miguel Ojeda
2025-03-07 22:49 ` [PATCH 6.12.y 46/60] rust: alloc: update module comment of alloc.rs Miguel Ojeda
2025-03-07 22:49 ` [PATCH 6.12.y 47/60] kbuild: rust: remove the `alloc` crate and `GlobalAlloc` Miguel Ojeda
2025-03-07 22:49 ` [PATCH 6.12.y 48/60] MAINTAINERS: add entry for the Rust `alloc` module Miguel Ojeda
2025-03-07 22:49 ` [PATCH 6.12.y 49/60] drm/panic: avoid reimplementing Iterator::find Miguel Ojeda
2025-03-07 22:49 ` [PATCH 6.12.y 50/60] drm/panic: remove unnecessary borrow in alignment_pattern Miguel Ojeda
2025-03-07 22:49 ` [PATCH 6.12.y 51/60] drm/panic: prefer eliding lifetimes Miguel Ojeda
2025-03-07 22:49 ` [PATCH 6.12.y 52/60] drm/panic: remove redundant field when assigning value Miguel Ojeda
2025-03-07 22:50 ` [PATCH 6.12.y 53/60] drm/panic: correctly indent continuation of line in list item Miguel Ojeda
2025-03-07 22:50 ` [PATCH 6.12.y 54/60] drm/panic: allow verbose boolean for clarity Miguel Ojeda
2025-03-07 22:50 ` [PATCH 6.12.y 55/60] drm/panic: allow verbose version check Miguel Ojeda
2025-03-07 22:50 ` [PATCH 6.12.y 56/60] rust: kbuild: expand rusttest target for macros Miguel Ojeda
2025-03-07 22:50 ` [PATCH 6.12.y 57/60] rust: fix size_t in bindgen prototypes of C builtins Miguel Ojeda
2025-03-07 22:50 ` [PATCH 6.12.y 58/60] rust: map `__kernel_size_t` and friends also to usize/isize Miguel Ojeda
2025-03-07 22:50 ` [PATCH 6.12.y 59/60] rust: use custom FFI integer types Miguel Ojeda
2025-03-07 22:50 ` [PATCH 6.12.y 60/60] rust: alloc: Fix `ArrayLayout` allocations Miguel Ojeda
2025-03-09  9:47 ` [PATCH 6.12.y 00/60] `alloc`, `#[expect]` and "Custom FFI" Greg Kroah-Hartman
2025-03-09 12:41 ` Ilya K
2025-03-09 14:20   ` Miguel Ojeda
2025-03-09 16:27     ` Greg Kroah-Hartman
2025-03-09 20:42       ` [PATCH 6.12.y 0/2] The two missing ones Miguel Ojeda
2025-03-09 20:42         ` [PATCH 6.12.y 1/2] rust: finish using custom FFI integer types Miguel Ojeda
2025-03-13  9:01           ` Sasha Levin
2025-03-09 20:42         ` [PATCH 6.12.y 2/2] rust: map `long` to `isize` and `char` to `u8` Miguel Ojeda
2025-03-13  9:01           ` Sasha Levin
2025-03-13 10:59             ` Miguel Ojeda
2025-03-09 21:02         ` [PATCH 6.12.y 0/2] The two missing ones Greg Kroah-Hartman

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=20250307225008.779961-38-ojeda@kernel.org \
    --to=ojeda@kernel.org \
    --cc=aliceryhl@google.com \
    --cc=dakr@kernel.org \
    --cc=gregkh@linuxfoundation.org \
    --cc=hi@alyssa.is \
    --cc=noisycoil@disroot.org \
    --cc=patches@lists.linux.dev \
    --cc=sashal@kernel.org \
    --cc=stable@vger.kernel.org \
    /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