Skip to content

Commit 7937dca

Browse files
author
Danilo Krummrich
committed
rust: alloc: implement VmallocPageIter
Introduce the VmallocPageIter type; an instance of VmallocPageIter may be exposed by owners of vmalloc allocations to provide borrowed access to its backing pages. For instance, this is useful to access and borrow the backing pages of allocation primitives, such as Box and Vec, backing a scatterlist. Reviewed-by: Daniel Almeida <daniel.almeida@collabora.com> Reviewed-by: Alexandre Courbot <acourbot@nvidia.com> Tested-by: Alexandre Courbot <acourbot@nvidia.com> Reviewed-by: Alice Ryhl <aliceryhl@google.com> Suggested-by: Alice Ryhl <aliceryhl@google.com> Link: https://lore.kernel.org/r/20250820145434.94745-4-dakr@kernel.org [ Drop VmallocPageIter::base_address(), move to allocator/iter.rs and stub VmallocPageIter for allocator_test.rs. - Danilo ] Signed-off-by: Danilo Krummrich <dakr@kernel.org>
1 parent 8e92c99 commit 7937dca

3 files changed

Lines changed: 134 additions & 0 deletions

File tree

rust/kernel/alloc/allocator.rs

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,9 @@ use crate::bindings;
1818
use crate::page;
1919
use crate::pr_warn;
2020

21+
mod iter;
22+
pub use self::iter::VmallocPageIter;
23+
2124
/// The contiguous kernel allocator.
2225
///
2326
/// `Kmalloc` is typically used for physically contiguous allocations up to page size, but also
Lines changed: 102 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,102 @@
1+
// SPDX-License-Identifier: GPL-2.0
2+
3+
use super::Vmalloc;
4+
use crate::page;
5+
use core::marker::PhantomData;
6+
use core::ptr::NonNull;
7+
8+
/// An [`Iterator`] of [`page::BorrowedPage`] items owned by a [`Vmalloc`] allocation.
9+
///
10+
/// # Guarantees
11+
///
12+
/// The pages iterated by the [`Iterator`] appear in the order as they are mapped in the CPU's
13+
/// virtual address space ascendingly.
14+
///
15+
/// # Invariants
16+
///
17+
/// - `buf` is a valid and [`page::PAGE_SIZE`] aligned pointer into a [`Vmalloc`] allocation.
18+
/// - `size` is the number of bytes from `buf` until the end of the [`Vmalloc`] allocation `buf`
19+
/// points to.
20+
pub struct VmallocPageIter<'a> {
21+
/// The base address of the [`Vmalloc`] buffer.
22+
buf: NonNull<u8>,
23+
/// The size of the buffer pointed to by `buf` in bytes.
24+
size: usize,
25+
/// The current page index of the [`Iterator`].
26+
index: usize,
27+
_p: PhantomData<page::BorrowedPage<'a>>,
28+
}
29+
30+
impl<'a> Iterator for VmallocPageIter<'a> {
31+
type Item = page::BorrowedPage<'a>;
32+
33+
fn next(&mut self) -> Option<Self::Item> {
34+
let offset = self.index.checked_mul(page::PAGE_SIZE)?;
35+
36+
// Even though `self.size()` may be smaller than `Self::page_count() * page::PAGE_SIZE`, it
37+
// is always a number between `(Self::page_count() - 1) * page::PAGE_SIZE` and
38+
// `Self::page_count() * page::PAGE_SIZE`, hence the check below is sufficient.
39+
if offset < self.size() {
40+
self.index += 1;
41+
} else {
42+
return None;
43+
}
44+
45+
// TODO: Use `NonNull::add()` instead, once the minimum supported compiler version is
46+
// bumped to 1.80 or later.
47+
//
48+
// SAFETY: `offset` is in the interval `[0, (self.page_count() - 1) * page::PAGE_SIZE]`,
49+
// hence the resulting pointer is guaranteed to be within the same allocation.
50+
let ptr = unsafe { self.buf.as_ptr().add(offset) };
51+
52+
// SAFETY: `ptr` is guaranteed to be non-null given that it is derived from `self.buf`.
53+
let ptr = unsafe { NonNull::new_unchecked(ptr) };
54+
55+
// SAFETY:
56+
// - `ptr` is a valid pointer to a `Vmalloc` allocation.
57+
// - `ptr` is valid for the duration of `'a`.
58+
Some(unsafe { Vmalloc::to_page(ptr) })
59+
}
60+
61+
fn size_hint(&self) -> (usize, Option<usize>) {
62+
let remaining = self.page_count().saturating_sub(self.index);
63+
64+
(remaining, Some(remaining))
65+
}
66+
}
67+
68+
impl<'a> VmallocPageIter<'a> {
69+
/// Creates a new [`VmallocPageIter`] instance.
70+
///
71+
/// # Safety
72+
///
73+
/// - `buf` must be a [`page::PAGE_SIZE`] aligned pointer into a [`Vmalloc`] allocation.
74+
/// - `buf` must be valid for at least the lifetime of `'a`.
75+
/// - `size` must be the number of bytes from `buf` until the end of the [`Vmalloc`] allocation
76+
/// `buf` points to.
77+
pub unsafe fn new(buf: NonNull<u8>, size: usize) -> Self {
78+
// INVARIANT: By the safety requirements, `buf` is a valid and `page::PAGE_SIZE` aligned
79+
// pointer into a [`Vmalloc`] allocation.
80+
Self {
81+
buf,
82+
size,
83+
index: 0,
84+
_p: PhantomData,
85+
}
86+
}
87+
88+
/// Returns the size of the backing [`Vmalloc`] allocation in bytes.
89+
///
90+
/// Note that this is the size the [`Vmalloc`] allocation has been allocated with. Hence, this
91+
/// number may be smaller than `[`Self::page_count`] * [`page::PAGE_SIZE`]`.
92+
#[inline]
93+
pub fn size(&self) -> usize {
94+
self.size
95+
}
96+
97+
/// Returns the number of pages owned by the backing [`Vmalloc`] allocation.
98+
#[inline]
99+
pub fn page_count(&self) -> usize {
100+
self.size().div_ceil(page::PAGE_SIZE)
101+
}
102+
}

rust/kernel/alloc/allocator_test.rs

Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,8 +12,10 @@
1212
use super::{flags::*, AllocError, Allocator, Flags};
1313
use core::alloc::Layout;
1414
use core::cmp;
15+
use core::marker::PhantomData;
1516
use core::ptr;
1617
use core::ptr::NonNull;
18+
use kernel::page;
1719

1820
/// The userspace allocator based on libc.
1921
pub struct Cmalloc;
@@ -22,6 +24,33 @@ pub type Kmalloc = Cmalloc;
2224
pub type Vmalloc = Kmalloc;
2325
pub type KVmalloc = Kmalloc;
2426

27+
pub struct VmallocPageIter<'a> {
28+
_p: PhantomData<page::BorrowedPage<'a>>,
29+
}
30+
31+
impl<'a> Iterator for VmallocPageIter<'a> {
32+
type Item = page::BorrowedPage<'a>;
33+
34+
fn next(&mut self) -> Option<Self::Item> {
35+
None
36+
}
37+
}
38+
39+
impl<'a> VmallocPageIter<'a> {
40+
#[allow(clippy::missing_safety_doc)]
41+
pub unsafe fn new(_buf: NonNull<u8>, _size: usize) -> Self {
42+
Self { _p: PhantomData }
43+
}
44+
45+
pub fn size(&self) -> usize {
46+
0
47+
}
48+
49+
pub fn page_count(&self) -> usize {
50+
0
51+
}
52+
}
53+
2554
extern "C" {
2655
#[link_name = "aligned_alloc"]
2756
fn libc_aligned_alloc(align: usize, size: usize) -> *mut crate::ffi::c_void;

0 commit comments

Comments
 (0)