* [PATCH v4 0/7] BorrowedPage, AsPageIter and VmallocPageIter
@ 2025-08-14 9:33 Danilo Krummrich
2025-08-14 9:33 ` [PATCH v4 1/7] rust: page: implement BorrowedPage Danilo Krummrich
` (7 more replies)
0 siblings, 8 replies; 28+ messages in thread
From: Danilo Krummrich @ 2025-08-14 9:33 UTC (permalink / raw)
To: lorenzo.stoakes, vbabka, Liam.Howlett, urezki, ojeda, alex.gaynor,
boqun.feng, gary, bjorn3_gh, lossin, a.hindborg, aliceryhl,
tmgross, abdiel.janulgue, acourbot
Cc: rust-for-linux, Danilo Krummrich
This patch series implements the BorrowedPage type, the AsPageIter trait and
VmallocPageIter type.
AsPageIter can be implemented by any type that owns pages to allow users to
borrow them through a generic interface.
For instance, this is useful to access and borrow the backing pages of
allocation primitives, such as Box and Vec, backing a scatterlist.
Hence, implement AsPageIter for VBox and VVec.
Additionally, implement Vmalloc::to_page() and ArrayLayout::size(), which are
dependencies of the above.
Changes in v4:
- Rename IntoPageIter into AsPageIter.
- Put lifetime on AsPageIter::Iter instead of AsPageIter itself.
- Implement size_hint() for VmallocPageIter.
- Use PhantomData<page::BorrowedPage<'a>> instead of PhantomData<&'a u8>.
Changes in v3:
- Generalize the previous PageOwner impl of VBox and VVec in VmallocPageIter.
- Correspondingly, replace PageOwner with IntoPageIter.
Changes in v2:
- BorrowedPage
- Add link to Ownable
- Use borrow_page() in the example
- Add PageOwner, Vmalloc::to_page(), ArrayLayout, VBox, VVec patches.
Danilo Krummrich (7):
rust: page: implement BorrowedPage
rust: alloc: vmalloc: implement Vmalloc::to_page()
rust: alloc: implement VmallocPageIter
rust: page: define trait AsPageIter
rust: alloc: kbox: implement AsPageIter for VBox
rust: alloc: layout: implement ArrayLayout::size()
rust: alloc: kvec: implement AsPageIter for VVec
rust/bindings/bindings_helper.h | 1 +
rust/kernel/alloc/allocator.rs | 147 ++++++++++++++++++++++++++++++++
rust/kernel/alloc/kbox.rs | 40 ++++++++-
rust/kernel/alloc/kvec.rs | 40 ++++++++-
rust/kernel/alloc/layout.rs | 5 ++
rust/kernel/page.rs | 87 ++++++++++++++++++-
6 files changed, 317 insertions(+), 3 deletions(-)
base-commit: 8f5ae30d69d7543eee0d70083daf4de8fe15d585
--
2.50.1
^ permalink raw reply [flat|nested] 28+ messages in thread
* [PATCH v4 1/7] rust: page: implement BorrowedPage
2025-08-14 9:33 [PATCH v4 0/7] BorrowedPage, AsPageIter and VmallocPageIter Danilo Krummrich
@ 2025-08-14 9:33 ` Danilo Krummrich
2025-08-19 12:42 ` Daniel Almeida
2025-08-14 9:33 ` [PATCH v4 2/7] rust: alloc: vmalloc: implement Vmalloc::to_page() Danilo Krummrich
` (6 subsequent siblings)
7 siblings, 1 reply; 28+ messages in thread
From: Danilo Krummrich @ 2025-08-14 9:33 UTC (permalink / raw)
To: lorenzo.stoakes, vbabka, Liam.Howlett, urezki, ojeda, alex.gaynor,
boqun.feng, gary, bjorn3_gh, lossin, a.hindborg, aliceryhl,
tmgross, abdiel.janulgue, acourbot
Cc: rust-for-linux, Danilo Krummrich
Currently, a Page always owns the underlying struct page.
However, sometimes a struct page may be owned by some other entity, e.g.
a vmalloc allocation.
Hence, introduce BorrowedPage to support such cases, until the Ownable
solution [1] lands.
This is required by the scatterlist abstractions.
Reviewed-by: Alexandre Courbot <acourbot@nvidia.com>
Acked-by: Alice Ryhl <aliceryhl@google.com>
Link: https://lore.kernel.org/rust-for-linux/ZnCzLIly3DRK2eab@boqun-archlinux/ [1]
Signed-off-by: Danilo Krummrich <dakr@kernel.org>
---
rust/bindings/bindings_helper.h | 1 +
rust/kernel/page.rs | 75 ++++++++++++++++++++++++++++++++-
2 files changed, 75 insertions(+), 1 deletion(-)
diff --git a/rust/bindings/bindings_helper.h b/rust/bindings/bindings_helper.h
index 84d60635e8a9..0e140e07758b 100644
--- a/rust/bindings/bindings_helper.h
+++ b/rust/bindings/bindings_helper.h
@@ -57,6 +57,7 @@
#include <linux/jiffies.h>
#include <linux/jump_label.h>
#include <linux/mdio.h>
+#include <linux/mm.h>
#include <linux/miscdevice.h>
#include <linux/of_device.h>
#include <linux/pci.h>
diff --git a/rust/kernel/page.rs b/rust/kernel/page.rs
index 7c1b17246ed5..631718a6ad7d 100644
--- a/rust/kernel/page.rs
+++ b/rust/kernel/page.rs
@@ -9,7 +9,12 @@
error::Result,
uaccess::UserSliceReader,
};
-use core::ptr::{self, NonNull};
+use core::{
+ marker::PhantomData,
+ mem::ManuallyDrop,
+ ops::Deref,
+ ptr::{self, NonNull},
+};
/// A bitwise shift for the page size.
pub const PAGE_SHIFT: usize = bindings::PAGE_SHIFT as usize;
@@ -30,6 +35,74 @@ pub const fn page_align(addr: usize) -> usize {
(addr + (PAGE_SIZE - 1)) & PAGE_MASK
}
+/// Representation of a non-owning reference to a [`Page`].
+///
+/// This type provides a borrowed version of a [`Page`] that is owned by some other entity, e.g. a
+/// [`Vmalloc`] allocation such as [`VBox`].
+///
+/// # Example
+///
+/// ```
+/// # use kernel::{bindings, prelude::*};
+/// use kernel::page::{BorrowedPage, Page, PAGE_SIZE};
+/// # use core::{mem::MaybeUninit, ptr, ptr::NonNull };
+///
+/// fn borrow_page<'a>(vbox: &'a mut VBox<MaybeUninit<[u8; PAGE_SIZE]>>) -> BorrowedPage<'a> {
+/// let ptr = ptr::from_ref(&**vbox);
+///
+/// // SAFETY: `ptr` is a valid pointer to `Vmalloc` memory.
+/// let page = unsafe { bindings::vmalloc_to_page(ptr.cast()) };
+///
+/// // SAFETY: `vmalloc_to_page` returns a valid pointer to a `struct page` for a valid
+/// // pointer to `Vmalloc` memory.
+/// let page = unsafe { NonNull::new_unchecked(page) };
+///
+/// // SAFETY:
+/// // - `self.0` is a valid pointer to a `struct page`.
+/// // - `self.0` is valid for the entire lifetime of `self`.
+/// unsafe { BorrowedPage::from_raw(page) }
+/// }
+///
+/// let mut vbox = VBox::<[u8; PAGE_SIZE]>::new_uninit(GFP_KERNEL)?;
+/// let page = borrow_page(&mut vbox);
+///
+/// // SAFETY: There is no concurrent read or write to this page.
+/// unsafe { page.fill_zero_raw(0, PAGE_SIZE)? };
+/// # Ok::<(), Error>(())
+/// ```
+///
+/// # Invariants
+///
+/// The borrowed underlying pointer to a `struct page` is valid for the entire lifetime `'a`.
+///
+/// [`VBox`]: kernel::alloc::VBox
+/// [`Vmalloc`]: kernel::alloc::allocator::Vmalloc
+pub struct BorrowedPage<'a>(ManuallyDrop<Page>, PhantomData<&'a Page>);
+
+impl<'a> BorrowedPage<'a> {
+ /// Constructs a [`BorrowedPage`] from a raw pointer to a `struct page`.
+ ///
+ /// # Safety
+ ///
+ /// - `ptr` must point to a valid `bindings::page`.
+ /// - `ptr` must remain valid for the entire lifetime `'a`.
+ pub unsafe fn from_raw(ptr: NonNull<bindings::page>) -> Self {
+ let page = Page { page: ptr };
+
+ // INVARIANT: The safety requirements guarantee that `ptr` is valid for the entire lifetime
+ // `'a`.
+ Self(ManuallyDrop::new(page), PhantomData)
+ }
+}
+
+impl<'a> Deref for BorrowedPage<'a> {
+ type Target = Page;
+
+ fn deref(&self) -> &Self::Target {
+ &self.0
+ }
+}
+
/// A pointer to a page that owns the page allocation.
///
/// # Invariants
--
2.50.1
^ permalink raw reply related [flat|nested] 28+ messages in thread
* [PATCH v4 2/7] rust: alloc: vmalloc: implement Vmalloc::to_page()
2025-08-14 9:33 [PATCH v4 0/7] BorrowedPage, AsPageIter and VmallocPageIter Danilo Krummrich
2025-08-14 9:33 ` [PATCH v4 1/7] rust: page: implement BorrowedPage Danilo Krummrich
@ 2025-08-14 9:33 ` Danilo Krummrich
2025-08-19 12:46 ` Daniel Almeida
2025-08-14 9:33 ` [PATCH v4 3/7] rust: alloc: implement VmallocPageIter Danilo Krummrich
` (5 subsequent siblings)
7 siblings, 1 reply; 28+ messages in thread
From: Danilo Krummrich @ 2025-08-14 9:33 UTC (permalink / raw)
To: lorenzo.stoakes, vbabka, Liam.Howlett, urezki, ojeda, alex.gaynor,
boqun.feng, gary, bjorn3_gh, lossin, a.hindborg, aliceryhl,
tmgross, abdiel.janulgue, acourbot
Cc: rust-for-linux, Danilo Krummrich
Implement an abstraction of vmalloc_to_page() for subsequent use in the
AsPageIter implementation of VBox and VVec.
Reviewed-by: Alexandre Courbot <acourbot@nvidia.com>
Reviewed-by: Alice Ryhl <aliceryhl@google.com>
Signed-off-by: Danilo Krummrich <dakr@kernel.org>
---
rust/kernel/alloc/allocator.rs | 49 ++++++++++++++++++++++++++++++++++
1 file changed, 49 insertions(+)
diff --git a/rust/kernel/alloc/allocator.rs b/rust/kernel/alloc/allocator.rs
index aa2dfa9dca4c..2315f5063011 100644
--- a/rust/kernel/alloc/allocator.rs
+++ b/rust/kernel/alloc/allocator.rs
@@ -15,6 +15,7 @@
use crate::alloc::{AllocError, Allocator};
use crate::bindings;
+use crate::page;
use crate::pr_warn;
/// The contiguous kernel allocator.
@@ -140,6 +141,54 @@ unsafe fn realloc(
}
}
+impl Vmalloc {
+ /// Convert a pointer to a [`Vmalloc`] allocation to a [`page::BorrowedPage`].
+ ///
+ /// # Examples
+ ///
+ /// ```
+ /// # use core::ptr::{NonNull, from_mut};
+ /// # use kernel::{page, prelude::*};
+ /// use kernel::alloc::allocator::Vmalloc;
+ ///
+ /// let mut vbox = VBox::<[u8; page::PAGE_SIZE]>::new_uninit(GFP_KERNEL)?;
+ ///
+ /// {
+ /// // SAFETY: By the type invariant of `Box` the inner pointer of `vbox` is non-null.
+ /// let ptr = unsafe { NonNull::new_unchecked(from_mut(&mut *vbox)) };
+ ///
+ /// // SAFETY:
+ /// // `ptr` is a valid pointer to a `Vmalloc` allocation.
+ /// // `ptr` is valid for the entire lifetime of `page`.
+ /// let page = unsafe { Vmalloc::to_page(ptr.cast()) };
+ ///
+ /// // SAFETY: There is no concurrent read or write to the same page.
+ /// unsafe { page.fill_zero_raw(0, page::PAGE_SIZE)? };
+ /// }
+ /// # Ok::<(), Error>(())
+ /// ```
+ ///
+ /// # Safety
+ ///
+ /// - `ptr` must be a valid pointer to a [`Vmalloc`] allocation.
+ /// - `ptr` must remain valid for the entire duration of `'a`.
+ pub unsafe fn to_page<'a>(ptr: NonNull<u8>) -> page::BorrowedPage<'a> {
+ // SAFETY: `ptr` is a valid pointer to `Vmalloc` memory.
+ let page = unsafe { bindings::vmalloc_to_page(ptr.as_ptr().cast()) };
+
+ // SAFETY: `vmalloc_to_page` returns a valid pointer to a `struct page` for a valid pointer
+ // to `Vmalloc` memory.
+ let page = unsafe { NonNull::new_unchecked(page) };
+
+ // SAFETY:
+ // - `page` is a valid pointer to a `struct page`, given that by the safety requirements of
+ // this function `ptr` is a valid pointer to a `Vmalloc` allocation.
+ // - By the safety requirements of this function `ptr` is valid for the entire lifetime of
+ // `'a`.
+ unsafe { page::BorrowedPage::from_raw(page) }
+ }
+}
+
// SAFETY: `realloc` delegates to `ReallocFunc::call`, which guarantees that
// - memory remains valid until it is explicitly freed,
// - passing a pointer to a valid memory allocation is OK,
--
2.50.1
^ permalink raw reply related [flat|nested] 28+ messages in thread
* [PATCH v4 3/7] rust: alloc: implement VmallocPageIter
2025-08-14 9:33 [PATCH v4 0/7] BorrowedPage, AsPageIter and VmallocPageIter Danilo Krummrich
2025-08-14 9:33 ` [PATCH v4 1/7] rust: page: implement BorrowedPage Danilo Krummrich
2025-08-14 9:33 ` [PATCH v4 2/7] rust: alloc: vmalloc: implement Vmalloc::to_page() Danilo Krummrich
@ 2025-08-14 9:33 ` Danilo Krummrich
2025-08-18 12:02 ` Alexandre Courbot
` (2 more replies)
2025-08-14 9:33 ` [PATCH v4 4/7] rust: page: define trait AsPageIter Danilo Krummrich
` (4 subsequent siblings)
7 siblings, 3 replies; 28+ messages in thread
From: Danilo Krummrich @ 2025-08-14 9:33 UTC (permalink / raw)
To: lorenzo.stoakes, vbabka, Liam.Howlett, urezki, ojeda, alex.gaynor,
boqun.feng, gary, bjorn3_gh, lossin, a.hindborg, aliceryhl,
tmgross, abdiel.janulgue, acourbot
Cc: rust-for-linux, Danilo Krummrich
Introduce the VmallocPageIter type; an instance of VmallocPageIter may
be exposed by owners of vmalloc allocations to provide borrowed access
to the backing pages of the vmalloc allocation.
For instance, this is useful to access and borrow the backing pages of
allocation primitives, such as Box and Vec, backing a scatterlist.
Suggested-by: Alice Ryhl <aliceryhl@google.com>
Signed-off-by: Danilo Krummrich <dakr@kernel.org>
---
rust/kernel/alloc/allocator.rs | 98 ++++++++++++++++++++++++++++++++++
1 file changed, 98 insertions(+)
diff --git a/rust/kernel/alloc/allocator.rs b/rust/kernel/alloc/allocator.rs
index 2315f5063011..5cff3069bff4 100644
--- a/rust/kernel/alloc/allocator.rs
+++ b/rust/kernel/alloc/allocator.rs
@@ -10,6 +10,7 @@
use super::Flags;
use core::alloc::Layout;
+use core::marker::PhantomData;
use core::ptr;
use core::ptr::NonNull;
@@ -236,3 +237,100 @@ unsafe fn realloc(
unsafe { ReallocFunc::KVREALLOC.call(ptr, layout, old_layout, flags) }
}
}
+
+/// An [`Iterator`] of [`page::BorrowedPage`] items owned by a [`Vmalloc`] allocation.
+///
+/// # Guarantees
+///
+/// The pages iterated by the [`Iterator`] appear in the order as they are mapped in the CPU's
+/// virtual address space ascendingly.
+///
+/// # Invariants
+///
+/// - `buf` is a valid pointer to the beginning of a [`Vmalloc`] allocation.
+/// - `size` is the size of the [`Vmalloc`] allocation `buf` points to.
+pub struct VmallocPageIter<'a> {
+ /// The base address of the [`Vmalloc`] buffer.
+ buf: NonNull<u8>,
+ /// The size of the buffer pointed to by `buf` in bytes.
+ size: usize,
+ /// The current page index of the [`Iterator`].
+ index: usize,
+ _p: PhantomData<page::BorrowedPage<'a>>,
+}
+
+impl<'a> Iterator for VmallocPageIter<'a> {
+ type Item = page::BorrowedPage<'a>;
+
+ fn next(&mut self) -> Option<Self::Item> {
+ let offset = self.index.checked_mul(page::PAGE_SIZE)?;
+
+ // Even though `self.size()` may be smaller than `Self::page_count() * page::PAGE_SIZE`, it
+ // is always a number between `(Self::page_count() - 1) * page::PAGE_SIZE` and
+ // `Self::page_count() * page::PAGE_SIZE`, hence the check below is sufficient.
+ if offset < self.size() {
+ self.index += 1;
+ } else {
+ return None;
+ }
+
+ // TODO: Use `NonNull::add()` instead, once the minimum supported compiler version is
+ // bumped to 1.80 or later.
+ //
+ // SAFETY: `offset` is in the interval `[0, (self.page_count() - 1) * page::PAGE_SIZE]`,
+ // hence the resulting pointer is guaranteed to be within the same allocation.
+ let ptr = unsafe { self.buf.as_ptr().add(offset) };
+
+ // SAFETY: `ptr` is guaranteed to be non-null given that it is derived from `self.buf`.
+ let ptr = unsafe { NonNull::new_unchecked(ptr) };
+
+ // SAFETY:
+ // - `ptr` is a valid pointer to a `Vmalloc` allocation.
+ // - `ptr` is valid for the duration of `'a`.
+ Some(unsafe { Vmalloc::to_page(ptr) })
+ }
+
+ fn size_hint(&self) -> (usize, Option<usize>) {
+ let remaining = self.page_count().saturating_sub(self.index);
+
+ (remaining, Some(remaining))
+ }
+}
+
+impl<'a> VmallocPageIter<'a> {
+ /// Creates a new [`VmallocPageIter`] instance.
+ ///
+ /// # Safety
+ ///
+ /// - `buf` must be a pointer to the beginning of a [`Vmalloc`] allocation.
+ /// - `buf` points to must at least be valid for the lifetime of `'a`.
+ /// - `size` must be the size of the [`Vmalloc`] allocation `buf` points to.
+ pub unsafe fn new(buf: NonNull<u8>, size: usize) -> Self {
+ // INVARIANT: `buf` is a valid pointer to the beginning of a `Vmalloc` allocation by the
+ // safety requirements of this function.
+ Self {
+ buf,
+ size,
+ index: 0,
+ _p: PhantomData,
+ }
+ }
+
+ /// Returns the base address of the backing [`Vmalloc`] allocation.
+ pub fn base_address(&self) -> NonNull<u8> {
+ self.buf
+ }
+
+ /// Returns the size of the backing [`Vmalloc`] allocation in bytes.
+ ///
+ /// Note that this is the size the [`Vmalloc`] allocation has been allocated with. Hence, this
+ /// number may be smaller than `[`Self::page_count`] * [`page::PAGE_SIZE`]`.
+ pub fn size(&self) -> usize {
+ self.size
+ }
+
+ /// Returns the number of pages owned by the backing [`Vmalloc`] allocation.
+ pub fn page_count(&self) -> usize {
+ self.size().div_ceil(page::PAGE_SIZE)
+ }
+}
--
2.50.1
^ permalink raw reply related [flat|nested] 28+ messages in thread
* [PATCH v4 4/7] rust: page: define trait AsPageIter
2025-08-14 9:33 [PATCH v4 0/7] BorrowedPage, AsPageIter and VmallocPageIter Danilo Krummrich
` (2 preceding siblings ...)
2025-08-14 9:33 ` [PATCH v4 3/7] rust: alloc: implement VmallocPageIter Danilo Krummrich
@ 2025-08-14 9:33 ` Danilo Krummrich
2025-08-18 12:06 ` Alexandre Courbot
` (2 more replies)
2025-08-14 9:33 ` [PATCH v4 5/7] rust: alloc: kbox: implement AsPageIter for VBox Danilo Krummrich
` (3 subsequent siblings)
7 siblings, 3 replies; 28+ messages in thread
From: Danilo Krummrich @ 2025-08-14 9:33 UTC (permalink / raw)
To: lorenzo.stoakes, vbabka, Liam.Howlett, urezki, ojeda, alex.gaynor,
boqun.feng, gary, bjorn3_gh, lossin, a.hindborg, aliceryhl,
tmgross, abdiel.janulgue, acourbot
Cc: rust-for-linux, Danilo Krummrich
The AsPageIter trait provides a common interface for types that
provide a page iterator, such as VmallocPageIter.
Subsequent patches will leverage this to let VBox and VVec provide a
VmallocPageIter though this trait.
Signed-off-by: Danilo Krummrich <dakr@kernel.org>
---
rust/kernel/page.rs | 12 ++++++++++++
1 file changed, 12 insertions(+)
diff --git a/rust/kernel/page.rs b/rust/kernel/page.rs
index 631718a6ad7d..8c5022c20472 100644
--- a/rust/kernel/page.rs
+++ b/rust/kernel/page.rs
@@ -103,6 +103,18 @@ fn deref(&self) -> &Self::Target {
}
}
+/// Trait to be implemented by types, which provide an [`Iterator`] implementation of
+/// [`BorrowedPage`] items, such as [`VmallocPageIter`](kernel::alloc::allocator::VmallocPageIter).
+pub trait AsPageIter {
+ /// The [`Iterator`] type, e.g. [`VmallocPageIter`](kernel::alloc::allocator::VmallocPageIter).
+ type Iter<'a>: Iterator<Item = BorrowedPage<'a>>
+ where
+ Self: 'a;
+
+ /// Returns an [`Iterator`] of [`BorrowedPage`] items over all pages owned by `self`.
+ fn page_iter(&mut self) -> Self::Iter<'_>;
+}
+
/// A pointer to a page that owns the page allocation.
///
/// # Invariants
--
2.50.1
^ permalink raw reply related [flat|nested] 28+ messages in thread
* [PATCH v4 5/7] rust: alloc: kbox: implement AsPageIter for VBox
2025-08-14 9:33 [PATCH v4 0/7] BorrowedPage, AsPageIter and VmallocPageIter Danilo Krummrich
` (3 preceding siblings ...)
2025-08-14 9:33 ` [PATCH v4 4/7] rust: page: define trait AsPageIter Danilo Krummrich
@ 2025-08-14 9:33 ` Danilo Krummrich
2025-08-18 12:07 ` Alexandre Courbot
2025-08-19 11:50 ` Alice Ryhl
2025-08-14 9:33 ` [PATCH v4 6/7] rust: alloc: layout: implement ArrayLayout::size() Danilo Krummrich
` (2 subsequent siblings)
7 siblings, 2 replies; 28+ messages in thread
From: Danilo Krummrich @ 2025-08-14 9:33 UTC (permalink / raw)
To: lorenzo.stoakes, vbabka, Liam.Howlett, urezki, ojeda, alex.gaynor,
boqun.feng, gary, bjorn3_gh, lossin, a.hindborg, aliceryhl,
tmgross, abdiel.janulgue, acourbot
Cc: rust-for-linux, Danilo Krummrich
Implement AsPageIter for VBox; this allows to iterate and borrow the
backing pages of a VBox. This, for instance, is useful in combination
with VBox backing a scatterlist.
Signed-off-by: Danilo Krummrich <dakr@kernel.org>
---
rust/kernel/alloc/kbox.rs | 40 ++++++++++++++++++++++++++++++++++++++-
1 file changed, 39 insertions(+), 1 deletion(-)
diff --git a/rust/kernel/alloc/kbox.rs b/rust/kernel/alloc/kbox.rs
index 856d05aa60f1..6c2ed6adeb89 100644
--- a/rust/kernel/alloc/kbox.rs
+++ b/rust/kernel/alloc/kbox.rs
@@ -3,7 +3,7 @@
//! Implementation of [`Box`].
#[allow(unused_imports)] // Used in doc comments.
-use super::allocator::{KVmalloc, Kmalloc, Vmalloc};
+use super::allocator::{KVmalloc, Kmalloc, Vmalloc, VmallocPageIter};
use super::{AllocError, Allocator, Flags};
use core::alloc::Layout;
use core::borrow::{Borrow, BorrowMut};
@@ -18,6 +18,7 @@
use crate::ffi::c_void;
use crate::init::InPlaceInit;
+use crate::page::AsPageIter;
use crate::types::ForeignOwnable;
use pin_init::{InPlaceWrite, Init, PinInit, ZeroableOption};
@@ -598,3 +599,40 @@ fn drop(&mut self) {
unsafe { A::free(self.0.cast(), layout) };
}
}
+
+/// # Examples
+///
+/// ```
+/// # use kernel::prelude::*;
+/// use kernel::alloc::allocator::VmallocPageIter;
+/// use kernel::page::{AsPageIter, PAGE_SIZE};
+///
+/// let mut vbox = VBox::new((), GFP_KERNEL)?;
+///
+/// assert!(vbox.page_iter().next().is_none());
+///
+/// let mut vbox = VBox::<[u8; PAGE_SIZE]>::new_uninit(GFP_KERNEL)?;
+///
+/// let page = vbox.page_iter().next().expect("At least one page should be available.\n");
+///
+/// // SAFETY: There is no concurrent read or write to the same page.
+/// unsafe { page.fill_zero_raw(0, PAGE_SIZE)? };
+/// # Ok::<(), Error>(())
+/// ```
+impl<T> AsPageIter for VBox<T> {
+ type Iter<'a>
+ = VmallocPageIter<'a>
+ where
+ T: 'a;
+
+ fn page_iter(&mut self) -> Self::Iter<'_> {
+ let ptr = self.0.cast();
+ let size = size_of::<T>();
+
+ // SAFETY:
+ // - `ptr` is a valid pointer to the beginning of a `Vmalloc` allocation.
+ // - `ptr` is guaranteed to be valid for the lifetime of `'a`.
+ // - `size` is the size of the `Vmalloc` allocation `ptr` points to.
+ unsafe { VmallocPageIter::new(ptr, size) }
+ }
+}
--
2.50.1
^ permalink raw reply related [flat|nested] 28+ messages in thread
* [PATCH v4 6/7] rust: alloc: layout: implement ArrayLayout::size()
2025-08-14 9:33 [PATCH v4 0/7] BorrowedPage, AsPageIter and VmallocPageIter Danilo Krummrich
` (4 preceding siblings ...)
2025-08-14 9:33 ` [PATCH v4 5/7] rust: alloc: kbox: implement AsPageIter for VBox Danilo Krummrich
@ 2025-08-14 9:33 ` Danilo Krummrich
2025-08-14 9:33 ` [PATCH v4 7/7] rust: alloc: kvec: implement AsPageIter for VVec Danilo Krummrich
2025-08-18 12:11 ` [PATCH v4 0/7] BorrowedPage, AsPageIter and VmallocPageIter Alexandre Courbot
7 siblings, 0 replies; 28+ messages in thread
From: Danilo Krummrich @ 2025-08-14 9:33 UTC (permalink / raw)
To: lorenzo.stoakes, vbabka, Liam.Howlett, urezki, ojeda, alex.gaynor,
boqun.feng, gary, bjorn3_gh, lossin, a.hindborg, aliceryhl,
tmgross, abdiel.janulgue, acourbot
Cc: rust-for-linux, Danilo Krummrich
Provide a convenience method for ArrayLayout to calculate the size of
the ArrayLayout in bytes.
Reviewed-by: Alexandre Courbot <acourbot@nvidia.com>
Reviewed-by: Alice Ryhl <aliceryhl@google.com>
Signed-off-by: Danilo Krummrich <dakr@kernel.org>
---
rust/kernel/alloc/layout.rs | 5 +++++
1 file changed, 5 insertions(+)
diff --git a/rust/kernel/alloc/layout.rs b/rust/kernel/alloc/layout.rs
index 93ed514f7cc7..5c3db8e9f930 100644
--- a/rust/kernel/alloc/layout.rs
+++ b/rust/kernel/alloc/layout.rs
@@ -98,6 +98,11 @@ pub const fn len(&self) -> usize {
pub const fn is_empty(&self) -> bool {
self.len == 0
}
+
+ /// Returns the size of the [`ArrayLayout`] in bytes.
+ pub const fn size(&self) -> usize {
+ self.len() * size_of::<T>()
+ }
}
impl<T> From<ArrayLayout<T>> for Layout {
--
2.50.1
^ permalink raw reply related [flat|nested] 28+ messages in thread
* [PATCH v4 7/7] rust: alloc: kvec: implement AsPageIter for VVec
2025-08-14 9:33 [PATCH v4 0/7] BorrowedPage, AsPageIter and VmallocPageIter Danilo Krummrich
` (5 preceding siblings ...)
2025-08-14 9:33 ` [PATCH v4 6/7] rust: alloc: layout: implement ArrayLayout::size() Danilo Krummrich
@ 2025-08-14 9:33 ` Danilo Krummrich
2025-08-18 12:09 ` Alexandre Courbot
` (2 more replies)
2025-08-18 12:11 ` [PATCH v4 0/7] BorrowedPage, AsPageIter and VmallocPageIter Alexandre Courbot
7 siblings, 3 replies; 28+ messages in thread
From: Danilo Krummrich @ 2025-08-14 9:33 UTC (permalink / raw)
To: lorenzo.stoakes, vbabka, Liam.Howlett, urezki, ojeda, alex.gaynor,
boqun.feng, gary, bjorn3_gh, lossin, a.hindborg, aliceryhl,
tmgross, abdiel.janulgue, acourbot
Cc: rust-for-linux, Danilo Krummrich
Implement AsPageIter for VVec; this allows to iterate and borrow the
backing pages of a VVec. This, for instance, is useful in combination
with VVec backing a scatterlist.
Signed-off-by: Danilo Krummrich <dakr@kernel.org>
---
rust/kernel/alloc/kvec.rs | 40 ++++++++++++++++++++++++++++++++++++++-
1 file changed, 39 insertions(+), 1 deletion(-)
diff --git a/rust/kernel/alloc/kvec.rs b/rust/kernel/alloc/kvec.rs
index 3c72e0bdddb8..ac438e70a1ed 100644
--- a/rust/kernel/alloc/kvec.rs
+++ b/rust/kernel/alloc/kvec.rs
@@ -3,10 +3,11 @@
//! Implementation of [`Vec`].
use super::{
- allocator::{KVmalloc, Kmalloc, Vmalloc},
+ allocator::{KVmalloc, Kmalloc, Vmalloc, VmallocPageIter},
layout::ArrayLayout,
AllocError, Allocator, Box, Flags,
};
+use crate::page::AsPageIter;
use core::{
borrow::{Borrow, BorrowMut},
fmt,
@@ -1017,6 +1018,43 @@ fn into_iter(self) -> Self::IntoIter {
}
}
+/// # Examples
+///
+/// ```
+/// # use kernel::prelude::*;
+/// use kernel::alloc::allocator::VmallocPageIter;
+/// use kernel::page::{AsPageIter, PAGE_SIZE};
+///
+/// let mut vec = VVec::<u8>::new();
+///
+/// assert!(vec.page_iter().next().is_none());
+///
+/// vec.reserve(PAGE_SIZE, GFP_KERNEL)?;
+///
+/// let page = vec.page_iter().next().expect("At least one page should be available.\n");
+///
+/// // SAFETY: There is no concurrent read or write to the same page.
+/// unsafe { page.fill_zero_raw(0, PAGE_SIZE)? };
+/// # Ok::<(), Error>(())
+/// ```
+impl<T> AsPageIter for VVec<T> {
+ type Iter<'a>
+ = VmallocPageIter<'a>
+ where
+ T: 'a;
+
+ fn page_iter(&mut self) -> Self::Iter<'_> {
+ let ptr = self.ptr.cast();
+ let size = self.layout.size();
+
+ // SAFETY:
+ // - `ptr` is a valid pointer to the beginning of a `Vmalloc` allocation.
+ // - `ptr` is guaranteed to be valid for the lifetime of `'a`.
+ // - `size` is the size of the `Vmalloc` allocation `ptr` points to.
+ unsafe { VmallocPageIter::new(ptr, size) }
+ }
+}
+
/// An [`Iterator`] implementation for [`Vec`] that moves elements out of a vector.
///
/// This structure is created by the [`Vec::into_iter`] method on [`Vec`] (provided by the
--
2.50.1
^ permalink raw reply related [flat|nested] 28+ messages in thread
* Re: [PATCH v4 3/7] rust: alloc: implement VmallocPageIter
2025-08-14 9:33 ` [PATCH v4 3/7] rust: alloc: implement VmallocPageIter Danilo Krummrich
@ 2025-08-18 12:02 ` Alexandre Courbot
2025-08-19 11:48 ` Alice Ryhl
2025-08-19 12:58 ` Daniel Almeida
2 siblings, 0 replies; 28+ messages in thread
From: Alexandre Courbot @ 2025-08-18 12:02 UTC (permalink / raw)
To: Danilo Krummrich, lorenzo.stoakes, vbabka, Liam.Howlett, urezki,
ojeda, alex.gaynor, boqun.feng, gary, bjorn3_gh, lossin,
a.hindborg, aliceryhl, tmgross, abdiel.janulgue
Cc: rust-for-linux
On Thu Aug 14, 2025 at 6:33 PM JST, Danilo Krummrich wrote:
> Introduce the VmallocPageIter type; an instance of VmallocPageIter may
> be exposed by owners of vmalloc allocations to provide borrowed access
> to the backing pages of the vmalloc allocation.
>
> For instance, this is useful to access and borrow the backing pages of
> allocation primitives, such as Box and Vec, backing a scatterlist.
>
> Suggested-by: Alice Ryhl <aliceryhl@google.com>
> Signed-off-by: Danilo Krummrich <dakr@kernel.org>
Reviewed-by: Alexandre Courbot <acourbot@nvidia.com>
With a minor nit below.
<snip>
> +impl<'a> VmallocPageIter<'a> {
> + /// Creates a new [`VmallocPageIter`] instance.
> + ///
> + /// # Safety
> + ///
> + /// - `buf` must be a pointer to the beginning of a [`Vmalloc`] allocation.
> + /// - `buf` points to must at least be valid for the lifetime of `'a`.
"the memory `buf` points to must ..."?
^ permalink raw reply [flat|nested] 28+ messages in thread
* Re: [PATCH v4 4/7] rust: page: define trait AsPageIter
2025-08-14 9:33 ` [PATCH v4 4/7] rust: page: define trait AsPageIter Danilo Krummrich
@ 2025-08-18 12:06 ` Alexandre Courbot
2025-08-18 12:07 ` Danilo Krummrich
2025-08-19 11:49 ` Alice Ryhl
2025-08-19 13:08 ` Daniel Almeida
2 siblings, 1 reply; 28+ messages in thread
From: Alexandre Courbot @ 2025-08-18 12:06 UTC (permalink / raw)
To: Danilo Krummrich, lorenzo.stoakes, vbabka, Liam.Howlett, urezki,
ojeda, alex.gaynor, boqun.feng, gary, bjorn3_gh, lossin,
a.hindborg, aliceryhl, tmgross, abdiel.janulgue
Cc: rust-for-linux
On Thu Aug 14, 2025 at 6:33 PM JST, Danilo Krummrich wrote:
> The AsPageIter trait provides a common interface for types that
> provide a page iterator, such as VmallocPageIter.
>
> Subsequent patches will leverage this to let VBox and VVec provide a
> VmallocPageIter though this trait.
>
> Signed-off-by: Danilo Krummrich <dakr@kernel.org>
Reviewed-by: Alexandre Courbot <acourbot@nvidia.com>
> ---
> rust/kernel/page.rs | 12 ++++++++++++
> 1 file changed, 12 insertions(+)
>
> diff --git a/rust/kernel/page.rs b/rust/kernel/page.rs
> index 631718a6ad7d..8c5022c20472 100644
> --- a/rust/kernel/page.rs
> +++ b/rust/kernel/page.rs
> @@ -103,6 +103,18 @@ fn deref(&self) -> &Self::Target {
> }
> }
>
> +/// Trait to be implemented by types, which provide an [`Iterator`] implementation of
> +/// [`BorrowedPage`] items, such as [`VmallocPageIter`](kernel::alloc::allocator::VmallocPageIter).
> +pub trait AsPageIter {
> + /// The [`Iterator`] type, e.g. [`VmallocPageIter`](kernel::alloc::allocator::VmallocPageIter).
> + type Iter<'a>: Iterator<Item = BorrowedPage<'a>>
> + where
> + Self: 'a;
> +
> + /// Returns an [`Iterator`] of [`BorrowedPage`] items over all pages owned by `self`.
This is almost obvious, but maybe specify that the pages must be
returned in ascending mapping order?
^ permalink raw reply [flat|nested] 28+ messages in thread
* Re: [PATCH v4 4/7] rust: page: define trait AsPageIter
2025-08-18 12:06 ` Alexandre Courbot
@ 2025-08-18 12:07 ` Danilo Krummrich
2025-08-18 12:14 ` Alexandre Courbot
0 siblings, 1 reply; 28+ messages in thread
From: Danilo Krummrich @ 2025-08-18 12:07 UTC (permalink / raw)
To: Alexandre Courbot
Cc: lorenzo.stoakes, vbabka, Liam.Howlett, urezki, ojeda, alex.gaynor,
boqun.feng, gary, bjorn3_gh, lossin, a.hindborg, aliceryhl,
tmgross, abdiel.janulgue, rust-for-linux
On 8/18/25 2:06 PM, Alexandre Courbot wrote:
> On Thu Aug 14, 2025 at 6:33 PM JST, Danilo Krummrich wrote:
>> +/// Trait to be implemented by types, which provide an [`Iterator`] implementation of
>> +/// [`BorrowedPage`] items, such as [`VmallocPageIter`](kernel::alloc::allocator::VmallocPageIter).
>> +pub trait AsPageIter {
>> + /// The [`Iterator`] type, e.g. [`VmallocPageIter`](kernel::alloc::allocator::VmallocPageIter).
>> + type Iter<'a>: Iterator<Item = BorrowedPage<'a>>
>> + where
>> + Self: 'a;
>> +
>> + /// Returns an [`Iterator`] of [`BorrowedPage`] items over all pages owned by `self`.
>
> This is almost obvious, but maybe specify that the pages must be
> returned in ascending mapping order?
There is no requirement for AsPageIter that the pages need to be mapped in any
address space.
^ permalink raw reply [flat|nested] 28+ messages in thread
* Re: [PATCH v4 5/7] rust: alloc: kbox: implement AsPageIter for VBox
2025-08-14 9:33 ` [PATCH v4 5/7] rust: alloc: kbox: implement AsPageIter for VBox Danilo Krummrich
@ 2025-08-18 12:07 ` Alexandre Courbot
2025-08-19 11:50 ` Alice Ryhl
1 sibling, 0 replies; 28+ messages in thread
From: Alexandre Courbot @ 2025-08-18 12:07 UTC (permalink / raw)
To: Danilo Krummrich, lorenzo.stoakes, vbabka, Liam.Howlett, urezki,
ojeda, alex.gaynor, boqun.feng, gary, bjorn3_gh, lossin,
a.hindborg, aliceryhl, tmgross, abdiel.janulgue
Cc: rust-for-linux
On Thu Aug 14, 2025 at 6:33 PM JST, Danilo Krummrich wrote:
> Implement AsPageIter for VBox; this allows to iterate and borrow the
> backing pages of a VBox. This, for instance, is useful in combination
> with VBox backing a scatterlist.
>
> Signed-off-by: Danilo Krummrich <dakr@kernel.org>
Reviewed-by: Alexandre Courbot <acourbot@nvidia.com>
^ permalink raw reply [flat|nested] 28+ messages in thread
* Re: [PATCH v4 7/7] rust: alloc: kvec: implement AsPageIter for VVec
2025-08-14 9:33 ` [PATCH v4 7/7] rust: alloc: kvec: implement AsPageIter for VVec Danilo Krummrich
@ 2025-08-18 12:09 ` Alexandre Courbot
2025-08-19 11:51 ` Alice Ryhl
2025-08-19 13:11 ` Daniel Almeida
2 siblings, 0 replies; 28+ messages in thread
From: Alexandre Courbot @ 2025-08-18 12:09 UTC (permalink / raw)
To: Danilo Krummrich, lorenzo.stoakes, vbabka, Liam.Howlett, urezki,
ojeda, alex.gaynor, boqun.feng, gary, bjorn3_gh, lossin,
a.hindborg, aliceryhl, tmgross, abdiel.janulgue
Cc: rust-for-linux
On Thu Aug 14, 2025 at 6:33 PM JST, Danilo Krummrich wrote:
> Implement AsPageIter for VVec; this allows to iterate and borrow the
> backing pages of a VVec. This, for instance, is useful in combination
> with VVec backing a scatterlist.
>
> Signed-off-by: Danilo Krummrich <dakr@kernel.org>
Reviewed-by: Alexandre Courbot <acourbot@nvidia.com>
^ permalink raw reply [flat|nested] 28+ messages in thread
* Re: [PATCH v4 0/7] BorrowedPage, AsPageIter and VmallocPageIter
2025-08-14 9:33 [PATCH v4 0/7] BorrowedPage, AsPageIter and VmallocPageIter Danilo Krummrich
` (6 preceding siblings ...)
2025-08-14 9:33 ` [PATCH v4 7/7] rust: alloc: kvec: implement AsPageIter for VVec Danilo Krummrich
@ 2025-08-18 12:11 ` Alexandre Courbot
7 siblings, 0 replies; 28+ messages in thread
From: Alexandre Courbot @ 2025-08-18 12:11 UTC (permalink / raw)
To: Danilo Krummrich, lorenzo.stoakes, vbabka, Liam.Howlett, urezki,
ojeda, alex.gaynor, boqun.feng, gary, bjorn3_gh, lossin,
a.hindborg, aliceryhl, tmgross, abdiel.janulgue
Cc: rust-for-linux
On Thu Aug 14, 2025 at 6:33 PM JST, Danilo Krummrich wrote:
> This patch series implements the BorrowedPage type, the AsPageIter trait and
> VmallocPageIter type.
>
> AsPageIter can be implemented by any type that owns pages to allow users to
> borrow them through a generic interface.
>
> For instance, this is useful to access and borrow the backing pages of
> allocation primitives, such as Box and Vec, backing a scatterlist.
>
> Hence, implement AsPageIter for VBox and VVec.
>
> Additionally, implement Vmalloc::to_page() and ArrayLayout::size(), which are
> dependencies of the above.
I have been able to successfully use this patchset with your scatterlist
one in nova-core. Thus, the series
Tested-by: Alexandre Courbot <acourbot@nvidia.com>
^ permalink raw reply [flat|nested] 28+ messages in thread
* Re: [PATCH v4 4/7] rust: page: define trait AsPageIter
2025-08-18 12:07 ` Danilo Krummrich
@ 2025-08-18 12:14 ` Alexandre Courbot
2025-08-18 12:18 ` Danilo Krummrich
0 siblings, 1 reply; 28+ messages in thread
From: Alexandre Courbot @ 2025-08-18 12:14 UTC (permalink / raw)
To: Danilo Krummrich
Cc: lorenzo.stoakes, vbabka, Liam.Howlett, urezki, ojeda, alex.gaynor,
boqun.feng, gary, bjorn3_gh, lossin, a.hindborg, aliceryhl,
tmgross, abdiel.janulgue, rust-for-linux
On Mon Aug 18, 2025 at 9:07 PM JST, Danilo Krummrich wrote:
> On 8/18/25 2:06 PM, Alexandre Courbot wrote:
>> On Thu Aug 14, 2025 at 6:33 PM JST, Danilo Krummrich wrote:
>>> +/// Trait to be implemented by types, which provide an [`Iterator`] implementation of
>>> +/// [`BorrowedPage`] items, such as [`VmallocPageIter`](kernel::alloc::allocator::VmallocPageIter).
>>> +pub trait AsPageIter {
>>> + /// The [`Iterator`] type, e.g. [`VmallocPageIter`](kernel::alloc::allocator::VmallocPageIter).
>>> + type Iter<'a>: Iterator<Item = BorrowedPage<'a>>
>>> + where
>>> + Self: 'a;
>>> +
>>> + /// Returns an [`Iterator`] of [`BorrowedPage`] items over all pages owned by `self`.
>>
>> This is almost obvious, but maybe specify that the pages must be
>> returned in ascending mapping order?
>
> There is no requirement for AsPageIter that the pages need to be mapped in any
> address space.
Yet, isn't the order of iteration important even if we don't intent to
map the pages?
^ permalink raw reply [flat|nested] 28+ messages in thread
* Re: [PATCH v4 4/7] rust: page: define trait AsPageIter
2025-08-18 12:14 ` Alexandre Courbot
@ 2025-08-18 12:18 ` Danilo Krummrich
0 siblings, 0 replies; 28+ messages in thread
From: Danilo Krummrich @ 2025-08-18 12:18 UTC (permalink / raw)
To: Alexandre Courbot
Cc: lorenzo.stoakes, vbabka, Liam.Howlett, urezki, ojeda, alex.gaynor,
boqun.feng, gary, bjorn3_gh, lossin, a.hindborg, aliceryhl,
tmgross, abdiel.janulgue, rust-for-linux
On 8/18/25 2:14 PM, Alexandre Courbot wrote:
> On Mon Aug 18, 2025 at 9:07 PM JST, Danilo Krummrich wrote:
>> On 8/18/25 2:06 PM, Alexandre Courbot wrote:
>>> On Thu Aug 14, 2025 at 6:33 PM JST, Danilo Krummrich wrote:
>>>> +/// Trait to be implemented by types, which provide an [`Iterator`] implementation of
>>>> +/// [`BorrowedPage`] items, such as [`VmallocPageIter`](kernel::alloc::allocator::VmallocPageIter).
>>>> +pub trait AsPageIter {
>>>> + /// The [`Iterator`] type, e.g. [`VmallocPageIter`](kernel::alloc::allocator::VmallocPageIter).
>>>> + type Iter<'a>: Iterator<Item = BorrowedPage<'a>>
>>>> + where
>>>> + Self: 'a;
>>>> +
>>>> + /// Returns an [`Iterator`] of [`BorrowedPage`] items over all pages owned by `self`.
>>>
>>> This is almost obvious, but maybe specify that the pages must be
>>> returned in ascending mapping order?
>>
>> There is no requirement for AsPageIter that the pages need to be mapped in any
>> address space.
>
> Yet, isn't the order of iteration important even if we don't intent to
> map the pages?
Not as far as the AsPageIter trait is concerned, any additional guarantees are
on AsPageIter::Iter.
If we find a specific classes of guarantees we want to provide, we should
implement additional traits on specific AsPageIter::Iter types.
This reminds me that I forgot to mention this in the context of why SGTable
wants VmallocPageIter for now.
^ permalink raw reply [flat|nested] 28+ messages in thread
* Re: [PATCH v4 3/7] rust: alloc: implement VmallocPageIter
2025-08-14 9:33 ` [PATCH v4 3/7] rust: alloc: implement VmallocPageIter Danilo Krummrich
2025-08-18 12:02 ` Alexandre Courbot
@ 2025-08-19 11:48 ` Alice Ryhl
2025-08-19 12:02 ` Danilo Krummrich
2025-08-19 12:58 ` Daniel Almeida
2 siblings, 1 reply; 28+ messages in thread
From: Alice Ryhl @ 2025-08-19 11:48 UTC (permalink / raw)
To: Danilo Krummrich
Cc: lorenzo.stoakes, vbabka, Liam.Howlett, urezki, ojeda, alex.gaynor,
boqun.feng, gary, bjorn3_gh, lossin, a.hindborg, tmgross,
abdiel.janulgue, acourbot, rust-for-linux
On Thu, Aug 14, 2025 at 11:33:42AM +0200, Danilo Krummrich wrote:
> Introduce the VmallocPageIter type; an instance of VmallocPageIter may
> be exposed by owners of vmalloc allocations to provide borrowed access
> to the backing pages of the vmalloc allocation.
>
> For instance, this is useful to access and borrow the backing pages of
> allocation primitives, such as Box and Vec, backing a scatterlist.
>
> Suggested-by: Alice Ryhl <aliceryhl@google.com>
> Signed-off-by: Danilo Krummrich <dakr@kernel.org>
Some nits below, but:
Reviewed-by: Alice Ryhl <aliceryhl@google.com>
> rust/kernel/alloc/allocator.rs | 98 ++++++++++++++++++++++++++++++++++
> 1 file changed, 98 insertions(+)
>
> diff --git a/rust/kernel/alloc/allocator.rs b/rust/kernel/alloc/allocator.rs
> index 2315f5063011..5cff3069bff4 100644
> --- a/rust/kernel/alloc/allocator.rs
> +++ b/rust/kernel/alloc/allocator.rs
> @@ -10,6 +10,7 @@
>
> use super::Flags;
> use core::alloc::Layout;
> +use core::marker::PhantomData;
> use core::ptr;
> use core::ptr::NonNull;
>
> @@ -236,3 +237,100 @@ unsafe fn realloc(
> unsafe { ReallocFunc::KVREALLOC.call(ptr, layout, old_layout, flags) }
> }
> }
> +
> +/// An [`Iterator`] of [`page::BorrowedPage`] items owned by a [`Vmalloc`] allocation.
> +///
> +/// # Guarantees
> +///
> +/// The pages iterated by the [`Iterator`] appear in the order as they are mapped in the CPU's
> +/// virtual address space ascendingly.
> +///
> +/// # Invariants
> +///
> +/// - `buf` is a valid pointer to the beginning of a [`Vmalloc`] allocation.
> +/// - `size` is the size of the [`Vmalloc`] allocation `buf` points to.
> +pub struct VmallocPageIter<'a> {
> + /// The base address of the [`Vmalloc`] buffer.
> + buf: NonNull<u8>,
> + /// The size of the buffer pointed to by `buf` in bytes.
> + size: usize,
> + /// The current page index of the [`Iterator`].
> + index: usize,
> + _p: PhantomData<page::BorrowedPage<'a>>,
> +}
> +
> +impl<'a> Iterator for VmallocPageIter<'a> {
> + type Item = page::BorrowedPage<'a>;
> +
> + fn next(&mut self) -> Option<Self::Item> {
> + let offset = self.index.checked_mul(page::PAGE_SIZE)?;
> +
> + // Even though `self.size()` may be smaller than `Self::page_count() * page::PAGE_SIZE`, it
> + // is always a number between `(Self::page_count() - 1) * page::PAGE_SIZE` and
> + // `Self::page_count() * page::PAGE_SIZE`, hence the check below is sufficient.
> + if offset < self.size() {
> + self.index += 1;
> + } else {
> + return None;
> + }
> +
> + // TODO: Use `NonNull::add()` instead, once the minimum supported compiler version is
> + // bumped to 1.80 or later.
> + //
> + // SAFETY: `offset` is in the interval `[0, (self.page_count() - 1) * page::PAGE_SIZE]`,
> + // hence the resulting pointer is guaranteed to be within the same allocation.
> + let ptr = unsafe { self.buf.as_ptr().add(offset) };
> +
> + // SAFETY: `ptr` is guaranteed to be non-null given that it is derived from `self.buf`.
> + let ptr = unsafe { NonNull::new_unchecked(ptr) };
> +
> + // SAFETY:
> + // - `ptr` is a valid pointer to a `Vmalloc` allocation.
> + // - `ptr` is valid for the duration of `'a`.
> + Some(unsafe { Vmalloc::to_page(ptr) })
> + }
> +
> + fn size_hint(&self) -> (usize, Option<usize>) {
> + let remaining = self.page_count().saturating_sub(self.index);
> +
> + (remaining, Some(remaining))
> + }
> +}
> +
> +impl<'a> VmallocPageIter<'a> {
> + /// Creates a new [`VmallocPageIter`] instance.
> + ///
> + /// # Safety
> + ///
> + /// - `buf` must be a pointer to the beginning of a [`Vmalloc`] allocation.
It has to be the beginning of the vmalloc allocation?
> + /// - `buf` points to must at least be valid for the lifetime of `'a`.
This grammar doesn't quite compute for me.
> + /// - `size` must be the size of the [`Vmalloc`] allocation `buf` points to.
> + pub unsafe fn new(buf: NonNull<u8>, size: usize) -> Self {
> + // INVARIANT: `buf` is a valid pointer to the beginning of a `Vmalloc` allocation by the
> + // safety requirements of this function.
> + Self {
> + buf,
> + size,
> + index: 0,
> + _p: PhantomData,
> + }
> + }
> +
> + /// Returns the base address of the backing [`Vmalloc`] allocation.
> + pub fn base_address(&self) -> NonNull<u8> {
> + self.buf
> + }
> +
> + /// Returns the size of the backing [`Vmalloc`] allocation in bytes.
> + ///
> + /// Note that this is the size the [`Vmalloc`] allocation has been allocated with. Hence, this
> + /// number may be smaller than `[`Self::page_count`] * [`page::PAGE_SIZE`]`.
> + pub fn size(&self) -> usize {
How about #[inline] on all these?
> + self.size
> + }
> +
> + /// Returns the number of pages owned by the backing [`Vmalloc`] allocation.
> + pub fn page_count(&self) -> usize {
> + self.size().div_ceil(page::PAGE_SIZE)
> + }
> +}
> --
> 2.50.1
>
^ permalink raw reply [flat|nested] 28+ messages in thread
* Re: [PATCH v4 4/7] rust: page: define trait AsPageIter
2025-08-14 9:33 ` [PATCH v4 4/7] rust: page: define trait AsPageIter Danilo Krummrich
2025-08-18 12:06 ` Alexandre Courbot
@ 2025-08-19 11:49 ` Alice Ryhl
2025-08-19 13:08 ` Daniel Almeida
2 siblings, 0 replies; 28+ messages in thread
From: Alice Ryhl @ 2025-08-19 11:49 UTC (permalink / raw)
To: Danilo Krummrich
Cc: lorenzo.stoakes, vbabka, Liam.Howlett, urezki, ojeda, alex.gaynor,
boqun.feng, gary, bjorn3_gh, lossin, a.hindborg, tmgross,
abdiel.janulgue, acourbot, rust-for-linux
On Thu, Aug 14, 2025 at 11:33:43AM +0200, Danilo Krummrich wrote:
> The AsPageIter trait provides a common interface for types that
> provide a page iterator, such as VmallocPageIter.
>
> Subsequent patches will leverage this to let VBox and VVec provide a
> VmallocPageIter though this trait.
>
> Signed-off-by: Danilo Krummrich <dakr@kernel.org>
Reviewed-by: Alice Ryhl <aliceryhl@google.com>
^ permalink raw reply [flat|nested] 28+ messages in thread
* Re: [PATCH v4 5/7] rust: alloc: kbox: implement AsPageIter for VBox
2025-08-14 9:33 ` [PATCH v4 5/7] rust: alloc: kbox: implement AsPageIter for VBox Danilo Krummrich
2025-08-18 12:07 ` Alexandre Courbot
@ 2025-08-19 11:50 ` Alice Ryhl
2025-08-19 12:06 ` Danilo Krummrich
1 sibling, 1 reply; 28+ messages in thread
From: Alice Ryhl @ 2025-08-19 11:50 UTC (permalink / raw)
To: Danilo Krummrich
Cc: lorenzo.stoakes, vbabka, Liam.Howlett, urezki, ojeda, alex.gaynor,
boqun.feng, gary, bjorn3_gh, lossin, a.hindborg, tmgross,
abdiel.janulgue, acourbot, rust-for-linux
On Thu, Aug 14, 2025 at 11:33:44AM +0200, Danilo Krummrich wrote:
> Implement AsPageIter for VBox; this allows to iterate and borrow the
> backing pages of a VBox. This, for instance, is useful in combination
> with VBox backing a scatterlist.
>
> Signed-off-by: Danilo Krummrich <dakr@kernel.org>
Reviewed-by: Alice Ryhl <aliceryhl@google.com>
> ---
> rust/kernel/alloc/kbox.rs | 40 ++++++++++++++++++++++++++++++++++++++-
> 1 file changed, 39 insertions(+), 1 deletion(-)
>
> diff --git a/rust/kernel/alloc/kbox.rs b/rust/kernel/alloc/kbox.rs
> index 856d05aa60f1..6c2ed6adeb89 100644
> --- a/rust/kernel/alloc/kbox.rs
> +++ b/rust/kernel/alloc/kbox.rs
> @@ -3,7 +3,7 @@
> //! Implementation of [`Box`].
>
> #[allow(unused_imports)] // Used in doc comments.
> -use super::allocator::{KVmalloc, Kmalloc, Vmalloc};
> +use super::allocator::{KVmalloc, Kmalloc, Vmalloc, VmallocPageIter};
> use super::{AllocError, Allocator, Flags};
> use core::alloc::Layout;
> use core::borrow::{Borrow, BorrowMut};
> @@ -18,6 +18,7 @@
>
> use crate::ffi::c_void;
> use crate::init::InPlaceInit;
> +use crate::page::AsPageIter;
> use crate::types::ForeignOwnable;
> use pin_init::{InPlaceWrite, Init, PinInit, ZeroableOption};
>
> @@ -598,3 +599,40 @@ fn drop(&mut self) {
> unsafe { A::free(self.0.cast(), layout) };
> }
> }
> +
> +/// # Examples
> +///
> +/// ```
> +/// # use kernel::prelude::*;
> +/// use kernel::alloc::allocator::VmallocPageIter;
> +/// use kernel::page::{AsPageIter, PAGE_SIZE};
> +///
> +/// let mut vbox = VBox::new((), GFP_KERNEL)?;
> +///
> +/// assert!(vbox.page_iter().next().is_none());
> +///
> +/// let mut vbox = VBox::<[u8; PAGE_SIZE]>::new_uninit(GFP_KERNEL)?;
> +///
> +/// let page = vbox.page_iter().next().expect("At least one page should be available.\n");
> +///
> +/// // SAFETY: There is no concurrent read or write to the same page.
> +/// unsafe { page.fill_zero_raw(0, PAGE_SIZE)? };
> +/// # Ok::<(), Error>(())
> +/// ```
> +impl<T> AsPageIter for VBox<T> {
> + type Iter<'a>
> + = VmallocPageIter<'a>
> + where
> + T: 'a;
Are you sure you ran rustfmt on this?
> +
> + fn page_iter(&mut self) -> Self::Iter<'_> {
> + let ptr = self.0.cast();
> + let size = size_of::<T>();
> +
> + // SAFETY:
> + // - `ptr` is a valid pointer to the beginning of a `Vmalloc` allocation.
> + // - `ptr` is guaranteed to be valid for the lifetime of `'a`.
> + // - `size` is the size of the `Vmalloc` allocation `ptr` points to.
> + unsafe { VmallocPageIter::new(ptr, size) }
> + }
> +}
> --
> 2.50.1
>
^ permalink raw reply [flat|nested] 28+ messages in thread
* Re: [PATCH v4 7/7] rust: alloc: kvec: implement AsPageIter for VVec
2025-08-14 9:33 ` [PATCH v4 7/7] rust: alloc: kvec: implement AsPageIter for VVec Danilo Krummrich
2025-08-18 12:09 ` Alexandre Courbot
@ 2025-08-19 11:51 ` Alice Ryhl
2025-08-19 13:11 ` Daniel Almeida
2 siblings, 0 replies; 28+ messages in thread
From: Alice Ryhl @ 2025-08-19 11:51 UTC (permalink / raw)
To: Danilo Krummrich
Cc: lorenzo.stoakes, vbabka, Liam.Howlett, urezki, ojeda, alex.gaynor,
boqun.feng, gary, bjorn3_gh, lossin, a.hindborg, tmgross,
abdiel.janulgue, acourbot, rust-for-linux
On Thu, Aug 14, 2025 at 11:33:46AM +0200, Danilo Krummrich wrote:
> Implement AsPageIter for VVec; this allows to iterate and borrow the
> backing pages of a VVec. This, for instance, is useful in combination
> with VVec backing a scatterlist.
>
> Signed-off-by: Danilo Krummrich <dakr@kernel.org>
Reviewed-by: Alice Ryhl <aliceryhl@google.com>
^ permalink raw reply [flat|nested] 28+ messages in thread
* Re: [PATCH v4 3/7] rust: alloc: implement VmallocPageIter
2025-08-19 11:48 ` Alice Ryhl
@ 2025-08-19 12:02 ` Danilo Krummrich
0 siblings, 0 replies; 28+ messages in thread
From: Danilo Krummrich @ 2025-08-19 12:02 UTC (permalink / raw)
To: Alice Ryhl
Cc: lorenzo.stoakes, vbabka, Liam.Howlett, urezki, ojeda, alex.gaynor,
boqun.feng, gary, bjorn3_gh, lossin, a.hindborg, tmgross,
abdiel.janulgue, acourbot, rust-for-linux
On Tue Aug 19, 2025 at 1:48 PM CEST, Alice Ryhl wrote:
> On Thu, Aug 14, 2025 at 11:33:42AM +0200, Danilo Krummrich wrote:
>> +impl<'a> VmallocPageIter<'a> {
>> + /// Creates a new [`VmallocPageIter`] instance.
>> + ///
>> + /// # Safety
>> + ///
>> + /// - `buf` must be a pointer to the beginning of a [`Vmalloc`] allocation.
>
> It has to be the beginning of the vmalloc allocation?
Technically, it would probably be enough if the pointer is a PAGE_SIZE aligned
pointer within a Vmalloc allocation and `size` is the remaining size, etc.
I can relax the requirement accordingly.
^ permalink raw reply [flat|nested] 28+ messages in thread
* Re: [PATCH v4 5/7] rust: alloc: kbox: implement AsPageIter for VBox
2025-08-19 11:50 ` Alice Ryhl
@ 2025-08-19 12:06 ` Danilo Krummrich
0 siblings, 0 replies; 28+ messages in thread
From: Danilo Krummrich @ 2025-08-19 12:06 UTC (permalink / raw)
To: Alice Ryhl
Cc: lorenzo.stoakes, vbabka, Liam.Howlett, urezki, ojeda, alex.gaynor,
boqun.feng, gary, bjorn3_gh, lossin, a.hindborg, tmgross,
abdiel.janulgue, acourbot, rust-for-linux
On Tue Aug 19, 2025 at 1:50 PM CEST, Alice Ryhl wrote:
>> +impl<T> AsPageIter for VBox<T> {
>> + type Iter<'a>
>> + = VmallocPageIter<'a>
>> + where
>> + T: 'a;
>
> Are you sure you ran rustfmt on this?
Heh! Yes, I am.
It seems like I'm pretty unlucky in terms of running into rustfmt quirks. I
think I spotted another weired formatting introduced by rustfmt in the
scatterlist patches.
^ permalink raw reply [flat|nested] 28+ messages in thread
* Re: [PATCH v4 1/7] rust: page: implement BorrowedPage
2025-08-14 9:33 ` [PATCH v4 1/7] rust: page: implement BorrowedPage Danilo Krummrich
@ 2025-08-19 12:42 ` Daniel Almeida
0 siblings, 0 replies; 28+ messages in thread
From: Daniel Almeida @ 2025-08-19 12:42 UTC (permalink / raw)
To: Danilo Krummrich
Cc: lorenzo.stoakes, vbabka, Liam.Howlett, urezki, ojeda, alex.gaynor,
boqun.feng, gary, bjorn3_gh, lossin, a.hindborg, aliceryhl,
tmgross, abdiel.janulgue, acourbot, rust-for-linux
Hi Danilo,
> On 14 Aug 2025, at 06:33, Danilo Krummrich <dakr@kernel.org> wrote:
>
> Currently, a Page always owns the underlying struct page.
>
> However, sometimes a struct page may be owned by some other entity, e.g.
> a vmalloc allocation.
>
> Hence, introduce BorrowedPage to support such cases, until the Ownable
> solution [1] lands.
>
> This is required by the scatterlist abstractions.
>
> Reviewed-by: Alexandre Courbot <acourbot@nvidia.com>
> Acked-by: Alice Ryhl <aliceryhl@google.com>
> Link: https://lore.kernel.org/rust-for-linux/ZnCzLIly3DRK2eab@boqun-archlinux/ [1]
> Signed-off-by: Danilo Krummrich <dakr@kernel.org>
> ---
> rust/bindings/bindings_helper.h | 1 +
> rust/kernel/page.rs | 75 ++++++++++++++++++++++++++++++++-
> 2 files changed, 75 insertions(+), 1 deletion(-)
>
> diff --git a/rust/bindings/bindings_helper.h b/rust/bindings/bindings_helper.h
> index 84d60635e8a9..0e140e07758b 100644
> --- a/rust/bindings/bindings_helper.h
> +++ b/rust/bindings/bindings_helper.h
> @@ -57,6 +57,7 @@
> #include <linux/jiffies.h>
> #include <linux/jump_label.h>
> #include <linux/mdio.h>
> +#include <linux/mm.h>
> #include <linux/miscdevice.h>
> #include <linux/of_device.h>
> #include <linux/pci.h>
> diff --git a/rust/kernel/page.rs b/rust/kernel/page.rs
> index 7c1b17246ed5..631718a6ad7d 100644
> --- a/rust/kernel/page.rs
> +++ b/rust/kernel/page.rs
> @@ -9,7 +9,12 @@
> error::Result,
> uaccess::UserSliceReader,
> };
> -use core::ptr::{self, NonNull};
> +use core::{
> + marker::PhantomData,
> + mem::ManuallyDrop,
> + ops::Deref,
> + ptr::{self, NonNull},
> +};
>
> /// A bitwise shift for the page size.
> pub const PAGE_SHIFT: usize = bindings::PAGE_SHIFT as usize;
> @@ -30,6 +35,74 @@ pub const fn page_align(addr: usize) -> usize {
> (addr + (PAGE_SIZE - 1)) & PAGE_MASK
> }
>
> +/// Representation of a non-owning reference to a [`Page`].
> +///
> +/// This type provides a borrowed version of a [`Page`] that is owned by some other entity, e.g. a
> +/// [`Vmalloc`] allocation such as [`VBox`].
> +///
> +/// # Example
> +///
> +/// ```
> +/// # use kernel::{bindings, prelude::*};
> +/// use kernel::page::{BorrowedPage, Page, PAGE_SIZE};
> +/// # use core::{mem::MaybeUninit, ptr, ptr::NonNull };
> +///
> +/// fn borrow_page<'a>(vbox: &'a mut VBox<MaybeUninit<[u8; PAGE_SIZE]>>) -> BorrowedPage<'a> {
> +/// let ptr = ptr::from_ref(&**vbox);
> +///
> +/// // SAFETY: `ptr` is a valid pointer to `Vmalloc` memory.
> +/// let page = unsafe { bindings::vmalloc_to_page(ptr.cast()) };
> +///
> +/// // SAFETY: `vmalloc_to_page` returns a valid pointer to a `struct page` for a valid
> +/// // pointer to `Vmalloc` memory.
> +/// let page = unsafe { NonNull::new_unchecked(page) };
> +///
> +/// // SAFETY:
> +/// // - `self.0` is a valid pointer to a `struct page`.
> +/// // - `self.0` is valid for the entire lifetime of `self`.
> +/// unsafe { BorrowedPage::from_raw(page) }
> +/// }
> +///
> +/// let mut vbox = VBox::<[u8; PAGE_SIZE]>::new_uninit(GFP_KERNEL)?;
> +/// let page = borrow_page(&mut vbox);
> +///
> +/// // SAFETY: There is no concurrent read or write to this page.
> +/// unsafe { page.fill_zero_raw(0, PAGE_SIZE)? };
> +/// # Ok::<(), Error>(())
> +/// ```
> +///
> +/// # Invariants
> +///
> +/// The borrowed underlying pointer to a `struct page` is valid for the entire lifetime `'a`.
> +///
> +/// [`VBox`]: kernel::alloc::VBox
> +/// [`Vmalloc`]: kernel::alloc::allocator::Vmalloc
> +pub struct BorrowedPage<'a>(ManuallyDrop<Page>, PhantomData<&'a Page>);
> +
> +impl<'a> BorrowedPage<'a> {
> + /// Constructs a [`BorrowedPage`] from a raw pointer to a `struct page`.
> + ///
> + /// # Safety
> + ///
> + /// - `ptr` must point to a valid `bindings::page`.
> + /// - `ptr` must remain valid for the entire lifetime `'a`.
> + pub unsafe fn from_raw(ptr: NonNull<bindings::page>) -> Self {
> + let page = Page { page: ptr };
> +
> + // INVARIANT: The safety requirements guarantee that `ptr` is valid for the entire lifetime
> + // `'a`.
> + Self(ManuallyDrop::new(page), PhantomData)
> + }
> +}
> +
> +impl<'a> Deref for BorrowedPage<'a> {
> + type Target = Page;
> +
> + fn deref(&self) -> &Self::Target {
> + &self.0
> + }
> +}
> +
> /// A pointer to a page that owns the page allocation.
> ///
> /// # Invariants
> --
> 2.50.1
>
>
Reviewed-by: Daniel Almeida <daniel.almeida@collabora.com>
^ permalink raw reply [flat|nested] 28+ messages in thread
* Re: [PATCH v4 2/7] rust: alloc: vmalloc: implement Vmalloc::to_page()
2025-08-14 9:33 ` [PATCH v4 2/7] rust: alloc: vmalloc: implement Vmalloc::to_page() Danilo Krummrich
@ 2025-08-19 12:46 ` Daniel Almeida
0 siblings, 0 replies; 28+ messages in thread
From: Daniel Almeida @ 2025-08-19 12:46 UTC (permalink / raw)
To: Danilo Krummrich
Cc: lorenzo.stoakes, vbabka, Liam.Howlett, urezki, ojeda, alex.gaynor,
boqun.feng, gary, bjorn3_gh, lossin, a.hindborg, aliceryhl,
tmgross, abdiel.janulgue, acourbot, rust-for-linux
> On 14 Aug 2025, at 06:33, Danilo Krummrich <dakr@kernel.org> wrote:
>
> Implement an abstraction of vmalloc_to_page() for subsequent use in the
> AsPageIter implementation of VBox and VVec.
>
> Reviewed-by: Alexandre Courbot <acourbot@nvidia.com>
> Reviewed-by: Alice Ryhl <aliceryhl@google.com>
> Signed-off-by: Danilo Krummrich <dakr@kernel.org>
> ---
> rust/kernel/alloc/allocator.rs | 49 ++++++++++++++++++++++++++++++++++
> 1 file changed, 49 insertions(+)
>
> diff --git a/rust/kernel/alloc/allocator.rs b/rust/kernel/alloc/allocator.rs
> index aa2dfa9dca4c..2315f5063011 100644
> --- a/rust/kernel/alloc/allocator.rs
> +++ b/rust/kernel/alloc/allocator.rs
> @@ -15,6 +15,7 @@
>
> use crate::alloc::{AllocError, Allocator};
> use crate::bindings;
> +use crate::page;
> use crate::pr_warn;
>
> /// The contiguous kernel allocator.
> @@ -140,6 +141,54 @@ unsafe fn realloc(
> }
> }
>
> +impl Vmalloc {
> + /// Convert a pointer to a [`Vmalloc`] allocation to a [`page::BorrowedPage`].
> + ///
> + /// # Examples
> + ///
> + /// ```
> + /// # use core::ptr::{NonNull, from_mut};
> + /// # use kernel::{page, prelude::*};
> + /// use kernel::alloc::allocator::Vmalloc;
> + ///
> + /// let mut vbox = VBox::<[u8; page::PAGE_SIZE]>::new_uninit(GFP_KERNEL)?;
> + ///
> + /// {
> + /// // SAFETY: By the type invariant of `Box` the inner pointer of `vbox` is non-null.
> + /// let ptr = unsafe { NonNull::new_unchecked(from_mut(&mut *vbox)) };
> + ///
> + /// // SAFETY:
> + /// // `ptr` is a valid pointer to a `Vmalloc` allocation.
> + /// // `ptr` is valid for the entire lifetime of `page`.
> + /// let page = unsafe { Vmalloc::to_page(ptr.cast()) };
> + ///
> + /// // SAFETY: There is no concurrent read or write to the same page.
> + /// unsafe { page.fill_zero_raw(0, page::PAGE_SIZE)? };
> + /// }
> + /// # Ok::<(), Error>(())
> + /// ```
> + ///
> + /// # Safety
> + ///
> + /// - `ptr` must be a valid pointer to a [`Vmalloc`] allocation.
> + /// - `ptr` must remain valid for the entire duration of `'a`.
> + pub unsafe fn to_page<'a>(ptr: NonNull<u8>) -> page::BorrowedPage<'a> {
> + // SAFETY: `ptr` is a valid pointer to `Vmalloc` memory.
> + let page = unsafe { bindings::vmalloc_to_page(ptr.as_ptr().cast()) };
> +
> + // SAFETY: `vmalloc_to_page` returns a valid pointer to a `struct page` for a valid pointer
> + // to `Vmalloc` memory.
> + let page = unsafe { NonNull::new_unchecked(page) };
> +
> + // SAFETY:
> + // - `page` is a valid pointer to a `struct page`, given that by the safety requirements of
> + // this function `ptr` is a valid pointer to a `Vmalloc` allocation.
> + // - By the safety requirements of this function `ptr` is valid for the entire lifetime of
> + // `'a`.
> + unsafe { page::BorrowedPage::from_raw(page) }
> + }
> +}
> +
> // SAFETY: `realloc` delegates to `ReallocFunc::call`, which guarantees that
> // - memory remains valid until it is explicitly freed,
> // - passing a pointer to a valid memory allocation is OK,
> --
> 2.50.1
>
>
Reviewed-by: Daniel Almeida <daniel.almeida@collabora.com>
^ permalink raw reply [flat|nested] 28+ messages in thread
* Re: [PATCH v4 3/7] rust: alloc: implement VmallocPageIter
2025-08-14 9:33 ` [PATCH v4 3/7] rust: alloc: implement VmallocPageIter Danilo Krummrich
2025-08-18 12:02 ` Alexandre Courbot
2025-08-19 11:48 ` Alice Ryhl
@ 2025-08-19 12:58 ` Daniel Almeida
2 siblings, 0 replies; 28+ messages in thread
From: Daniel Almeida @ 2025-08-19 12:58 UTC (permalink / raw)
To: Danilo Krummrich
Cc: lorenzo.stoakes, vbabka, Liam.Howlett, urezki, ojeda, alex.gaynor,
boqun.feng, gary, bjorn3_gh, lossin, a.hindborg, aliceryhl,
tmgross, abdiel.janulgue, acourbot, rust-for-linux
> On 14 Aug 2025, at 06:33, Danilo Krummrich <dakr@kernel.org> wrote:
>
> Introduce the VmallocPageIter type; an instance of VmallocPageIter may
> be exposed by owners of vmalloc allocations to provide borrowed access
> to the backing pages of the vmalloc allocation.
nit: “to its backing pages” reads better and avoids repetition.
>
> For instance, this is useful to access and borrow the backing pages of
> allocation primitives, such as Box and Vec, backing a scatterlist.
>
> Suggested-by: Alice Ryhl <aliceryhl@google.com>
> Signed-off-by: Danilo Krummrich <dakr@kernel.org>
> ---
> rust/kernel/alloc/allocator.rs | 98 ++++++++++++++++++++++++++++++++++
> 1 file changed, 98 insertions(+)
>
> diff --git a/rust/kernel/alloc/allocator.rs b/rust/kernel/alloc/allocator.rs
> index 2315f5063011..5cff3069bff4 100644
> --- a/rust/kernel/alloc/allocator.rs
> +++ b/rust/kernel/alloc/allocator.rs
> @@ -10,6 +10,7 @@
>
> use super::Flags;
> use core::alloc::Layout;
> +use core::marker::PhantomData;
> use core::ptr;
> use core::ptr::NonNull;
>
> @@ -236,3 +237,100 @@ unsafe fn realloc(
> unsafe { ReallocFunc::KVREALLOC.call(ptr, layout, old_layout, flags) }
> }
> }
> +
> +/// An [`Iterator`] of [`page::BorrowedPage`] items owned by a [`Vmalloc`] allocation.
> +///
> +/// # Guarantees
> +///
> +/// The pages iterated by the [`Iterator`] appear in the order as they are mapped in the CPU's
> +/// virtual address space ascendingly.
> +///
> +/// # Invariants
> +///
> +/// - `buf` is a valid pointer to the beginning of a [`Vmalloc`] allocation.
> +/// - `size` is the size of the [`Vmalloc`] allocation `buf` points to.
> +pub struct VmallocPageIter<'a> {
> + /// The base address of the [`Vmalloc`] buffer.
> + buf: NonNull<u8>,
> + /// The size of the buffer pointed to by `buf` in bytes.
> + size: usize,
> + /// The current page index of the [`Iterator`].
> + index: usize,
> + _p: PhantomData<page::BorrowedPage<'a>>,
> +}
> +
> +impl<'a> Iterator for VmallocPageIter<'a> {
> + type Item = page::BorrowedPage<'a>;
> +
> + fn next(&mut self) -> Option<Self::Item> {
> + let offset = self.index.checked_mul(page::PAGE_SIZE)?;
> +
> + // Even though `self.size()` may be smaller than `Self::page_count() * page::PAGE_SIZE`, it
> + // is always a number between `(Self::page_count() - 1) * page::PAGE_SIZE` and
> + // `Self::page_count() * page::PAGE_SIZE`, hence the check below is sufficient.
> + if offset < self.size() {
> + self.index += 1;
> + } else {
> + return None;
> + }
> +
> + // TODO: Use `NonNull::add()` instead, once the minimum supported compiler version is
> + // bumped to 1.80 or later.
> + //
> + // SAFETY: `offset` is in the interval `[0, (self.page_count() - 1) * page::PAGE_SIZE]`,
> + // hence the resulting pointer is guaranteed to be within the same allocation.
> + let ptr = unsafe { self.buf.as_ptr().add(offset) };
> +
> + // SAFETY: `ptr` is guaranteed to be non-null given that it is derived from `self.buf`.
> + let ptr = unsafe { NonNull::new_unchecked(ptr) };
> +
> + // SAFETY:
> + // - `ptr` is a valid pointer to a `Vmalloc` allocation.
> + // - `ptr` is valid for the duration of `'a`.
> + Some(unsafe { Vmalloc::to_page(ptr) })
> + }
> +
> + fn size_hint(&self) -> (usize, Option<usize>) {
> + let remaining = self.page_count().saturating_sub(self.index);
> +
> + (remaining, Some(remaining))
> + }
> +}
> +
> +impl<'a> VmallocPageIter<'a> {
> + /// Creates a new [`VmallocPageIter`] instance.
> + ///
> + /// # Safety
> + ///
> + /// - `buf` must be a pointer to the beginning of a [`Vmalloc`] allocation.
> + /// - `buf` points to must at least be valid for the lifetime of `'a`.
> + /// - `size` must be the size of the [`Vmalloc`] allocation `buf` points to.
> + pub unsafe fn new(buf: NonNull<u8>, size: usize) -> Self {
> + // INVARIANT: `buf` is a valid pointer to the beginning of a `Vmalloc` allocation by the
> + // safety requirements of this function.
> + Self {
> + buf,
> + size,
> + index: 0,
> + _p: PhantomData,
> + }
> + }
> +
> + /// Returns the base address of the backing [`Vmalloc`] allocation.
> + pub fn base_address(&self) -> NonNull<u8> {
> + self.buf
> + }
> +
> + /// Returns the size of the backing [`Vmalloc`] allocation in bytes.
> + ///
> + /// Note that this is the size the [`Vmalloc`] allocation has been allocated with. Hence, this
> + /// number may be smaller than `[`Self::page_count`] * [`page::PAGE_SIZE`]`.
> + pub fn size(&self) -> usize {
> + self.size
> + }
> +
> + /// Returns the number of pages owned by the backing [`Vmalloc`] allocation.
> + pub fn page_count(&self) -> usize {
> + self.size().div_ceil(page::PAGE_SIZE)
> + }
> +}
> --
> 2.50.1
>
>
Reviewed-by: Daniel Almeida <daniel.almeida@collabora.com>
^ permalink raw reply [flat|nested] 28+ messages in thread
* Re: [PATCH v4 4/7] rust: page: define trait AsPageIter
2025-08-14 9:33 ` [PATCH v4 4/7] rust: page: define trait AsPageIter Danilo Krummrich
2025-08-18 12:06 ` Alexandre Courbot
2025-08-19 11:49 ` Alice Ryhl
@ 2025-08-19 13:08 ` Daniel Almeida
2025-08-19 13:16 ` Danilo Krummrich
2 siblings, 1 reply; 28+ messages in thread
From: Daniel Almeida @ 2025-08-19 13:08 UTC (permalink / raw)
To: Danilo Krummrich
Cc: lorenzo.stoakes, vbabka, Liam.Howlett, urezki, ojeda, alex.gaynor,
boqun.feng, gary, bjorn3_gh, lossin, a.hindborg, aliceryhl,
tmgross, abdiel.janulgue, acourbot, rust-for-linux
> On 14 Aug 2025, at 06:33, Danilo Krummrich <dakr@kernel.org> wrote:
>
> The AsPageIter trait provides a common interface for types that
> provide a page iterator, such as VmallocPageIter.
>
> Subsequent patches will leverage this to let VBox and VVec provide a
> VmallocPageIter though this trait.
>
> Signed-off-by: Danilo Krummrich <dakr@kernel.org>
> ---
> rust/kernel/page.rs | 12 ++++++++++++
> 1 file changed, 12 insertions(+)
>
> diff --git a/rust/kernel/page.rs b/rust/kernel/page.rs
> index 631718a6ad7d..8c5022c20472 100644
> --- a/rust/kernel/page.rs
> +++ b/rust/kernel/page.rs
> @@ -103,6 +103,18 @@ fn deref(&self) -> &Self::Target {
> }
> }
>
> +/// Trait to be implemented by types, which provide an [`Iterator`] implementation of
nit: I would remove this comma.
How about “Trait to be implemented by types that provide an [`Iterator`] implementation of"
> +/// [`BorrowedPage`] items, such as [`VmallocPageIter`](kernel::alloc::allocator::VmallocPageIter).
> +pub trait AsPageIter {
> + /// The [`Iterator`] type, e.g. [`VmallocPageIter`](kernel::alloc::allocator::VmallocPageIter).
> + type Iter<'a>: Iterator<Item = BorrowedPage<'a>>
> + where
> + Self: 'a;
> +
> + /// Returns an [`Iterator`] of [`BorrowedPage`] items over all pages owned by `self`.
> + fn page_iter(&mut self) -> Self::Iter<'_>;
Does it make sense to break this into page_iter() and page_iter_mut()?
Alternatively, should this take &self instead?
> +}
> +
> /// A pointer to a page that owns the page allocation.
> ///
> /// # Invariants
> --
> 2.50.1
>
>
Reviewed-by: Daniel Almeida <daniel.almeida@collabora.com>
^ permalink raw reply [flat|nested] 28+ messages in thread
* Re: [PATCH v4 7/7] rust: alloc: kvec: implement AsPageIter for VVec
2025-08-14 9:33 ` [PATCH v4 7/7] rust: alloc: kvec: implement AsPageIter for VVec Danilo Krummrich
2025-08-18 12:09 ` Alexandre Courbot
2025-08-19 11:51 ` Alice Ryhl
@ 2025-08-19 13:11 ` Daniel Almeida
2 siblings, 0 replies; 28+ messages in thread
From: Daniel Almeida @ 2025-08-19 13:11 UTC (permalink / raw)
To: Danilo Krummrich
Cc: lorenzo.stoakes, vbabka, Liam.Howlett, urezki, ojeda, alex.gaynor,
boqun.feng, gary, bjorn3_gh, lossin, a.hindborg, aliceryhl,
tmgross, abdiel.janulgue, acourbot, rust-for-linux
> On 14 Aug 2025, at 06:33, Danilo Krummrich <dakr@kernel.org> wrote:
>
> Implement AsPageIter for VVec; this allows to iterate and borrow the
> backing pages of a VVec. This, for instance, is useful in combination
> with VVec backing a scatterlist.
>
> Signed-off-by: Danilo Krummrich <dakr@kernel.org>
> ---
> rust/kernel/alloc/kvec.rs | 40 ++++++++++++++++++++++++++++++++++++++-
> 1 file changed, 39 insertions(+), 1 deletion(-)
>
> diff --git a/rust/kernel/alloc/kvec.rs b/rust/kernel/alloc/kvec.rs
> index 3c72e0bdddb8..ac438e70a1ed 100644
> --- a/rust/kernel/alloc/kvec.rs
> +++ b/rust/kernel/alloc/kvec.rs
> @@ -3,10 +3,11 @@
> //! Implementation of [`Vec`].
>
> use super::{
> - allocator::{KVmalloc, Kmalloc, Vmalloc},
> + allocator::{KVmalloc, Kmalloc, Vmalloc, VmallocPageIter},
> layout::ArrayLayout,
> AllocError, Allocator, Box, Flags,
> };
> +use crate::page::AsPageIter;
> use core::{
> borrow::{Borrow, BorrowMut},
> fmt,
> @@ -1017,6 +1018,43 @@ fn into_iter(self) -> Self::IntoIter {
> }
> }
>
> +/// # Examples
> +///
> +/// ```
> +/// # use kernel::prelude::*;
> +/// use kernel::alloc::allocator::VmallocPageIter;
> +/// use kernel::page::{AsPageIter, PAGE_SIZE};
> +///
> +/// let mut vec = VVec::<u8>::new();
> +///
> +/// assert!(vec.page_iter().next().is_none());
> +///
> +/// vec.reserve(PAGE_SIZE, GFP_KERNEL)?;
> +///
> +/// let page = vec.page_iter().next().expect("At least one page should be available.\n");
> +///
> +/// // SAFETY: There is no concurrent read or write to the same page.
> +/// unsafe { page.fill_zero_raw(0, PAGE_SIZE)? };
> +/// # Ok::<(), Error>(())
> +/// ```
> +impl<T> AsPageIter for VVec<T> {
> + type Iter<'a>
> + = VmallocPageIter<'a>
> + where
> + T: 'a;
> +
> + fn page_iter(&mut self) -> Self::Iter<'_> {
> + let ptr = self.ptr.cast();
> + let size = self.layout.size();
> +
> + // SAFETY:
> + // - `ptr` is a valid pointer to the beginning of a `Vmalloc` allocation.
> + // - `ptr` is guaranteed to be valid for the lifetime of `'a`.
> + // - `size` is the size of the `Vmalloc` allocation `ptr` points to.
> + unsafe { VmallocPageIter::new(ptr, size) }
> + }
> +}
> +
> /// An [`Iterator`] implementation for [`Vec`] that moves elements out of a vector.
> ///
> /// This structure is created by the [`Vec::into_iter`] method on [`Vec`] (provided by the
> --
> 2.50.1
>
>
Reviewed-by: Daniel Almeida <daniel.almeida@collabora.com>
^ permalink raw reply [flat|nested] 28+ messages in thread
* Re: [PATCH v4 4/7] rust: page: define trait AsPageIter
2025-08-19 13:08 ` Daniel Almeida
@ 2025-08-19 13:16 ` Danilo Krummrich
0 siblings, 0 replies; 28+ messages in thread
From: Danilo Krummrich @ 2025-08-19 13:16 UTC (permalink / raw)
To: Daniel Almeida
Cc: lorenzo.stoakes, vbabka, Liam.Howlett, urezki, ojeda, alex.gaynor,
boqun.feng, gary, bjorn3_gh, lossin, a.hindborg, aliceryhl,
tmgross, abdiel.janulgue, acourbot, rust-for-linux
On Tue Aug 19, 2025 at 3:08 PM CEST, Daniel Almeida wrote:
>> On 14 Aug 2025, at 06:33, Danilo Krummrich <dakr@kernel.org> wrote:
>> +/// [`BorrowedPage`] items, such as [`VmallocPageIter`](kernel::alloc::allocator::VmallocPageIter).
>> +pub trait AsPageIter {
>> + /// The [`Iterator`] type, e.g. [`VmallocPageIter`](kernel::alloc::allocator::VmallocPageIter).
>> + type Iter<'a>: Iterator<Item = BorrowedPage<'a>>
>> + where
>> + Self: 'a;
>> +
>> + /// Returns an [`Iterator`] of [`BorrowedPage`] items over all pages owned by `self`.
>> + fn page_iter(&mut self) -> Self::Iter<'_>;
>
> Does it make sense to break this into page_iter() and page_iter_mut()?
> Alternatively, should this take &self instead?
The Iterator relies on the underlying data structure not being altered. For
instance, a VVec giving out a VmallocPageIter must not be re-allocated while the
VmallocPageIter exists.
^ permalink raw reply [flat|nested] 28+ messages in thread
end of thread, other threads:[~2025-08-19 13:16 UTC | newest]
Thread overview: 28+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2025-08-14 9:33 [PATCH v4 0/7] BorrowedPage, AsPageIter and VmallocPageIter Danilo Krummrich
2025-08-14 9:33 ` [PATCH v4 1/7] rust: page: implement BorrowedPage Danilo Krummrich
2025-08-19 12:42 ` Daniel Almeida
2025-08-14 9:33 ` [PATCH v4 2/7] rust: alloc: vmalloc: implement Vmalloc::to_page() Danilo Krummrich
2025-08-19 12:46 ` Daniel Almeida
2025-08-14 9:33 ` [PATCH v4 3/7] rust: alloc: implement VmallocPageIter Danilo Krummrich
2025-08-18 12:02 ` Alexandre Courbot
2025-08-19 11:48 ` Alice Ryhl
2025-08-19 12:02 ` Danilo Krummrich
2025-08-19 12:58 ` Daniel Almeida
2025-08-14 9:33 ` [PATCH v4 4/7] rust: page: define trait AsPageIter Danilo Krummrich
2025-08-18 12:06 ` Alexandre Courbot
2025-08-18 12:07 ` Danilo Krummrich
2025-08-18 12:14 ` Alexandre Courbot
2025-08-18 12:18 ` Danilo Krummrich
2025-08-19 11:49 ` Alice Ryhl
2025-08-19 13:08 ` Daniel Almeida
2025-08-19 13:16 ` Danilo Krummrich
2025-08-14 9:33 ` [PATCH v4 5/7] rust: alloc: kbox: implement AsPageIter for VBox Danilo Krummrich
2025-08-18 12:07 ` Alexandre Courbot
2025-08-19 11:50 ` Alice Ryhl
2025-08-19 12:06 ` Danilo Krummrich
2025-08-14 9:33 ` [PATCH v4 6/7] rust: alloc: layout: implement ArrayLayout::size() Danilo Krummrich
2025-08-14 9:33 ` [PATCH v4 7/7] rust: alloc: kvec: implement AsPageIter for VVec Danilo Krummrich
2025-08-18 12:09 ` Alexandre Courbot
2025-08-19 11:51 ` Alice Ryhl
2025-08-19 13:11 ` Daniel Almeida
2025-08-18 12:11 ` [PATCH v4 0/7] BorrowedPage, AsPageIter and VmallocPageIter Alexandre Courbot
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).