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