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