// SPDX-License-Identifier: GPL-2.012use super::Vmalloc;3use crate::page;4use core::marker::PhantomData;5use core::ptr::NonNull;67/// An [`Iterator`] of [`page::BorrowedPage`] items owned by a [`Vmalloc`] allocation.8///9/// # Guarantees10///11/// The pages iterated by the [`Iterator`] appear in the order as they are mapped in the CPU's12/// virtual address space ascendingly.13///14/// # Invariants15///16/// - `buf` is a valid and [`page::PAGE_SIZE`] aligned pointer into a [`Vmalloc`] allocation.17/// - `size` is the number of bytes from `buf` until the end of the [`Vmalloc`] allocation `buf`18/// points to.19pub struct VmallocPageIter<'a> {20/// The base address of the [`Vmalloc`] buffer.21buf: NonNull<u8>,22/// The size of the buffer pointed to by `buf` in bytes.23size: usize,24/// The current page index of the [`Iterator`].25index: usize,26_p: PhantomData<page::BorrowedPage<'a>>,27}2829impl<'a> Iterator for VmallocPageIter<'a> {30type Item = page::BorrowedPage<'a>;3132fn next(&mut self) -> Option<Self::Item> {33let offset = self.index.checked_mul(page::PAGE_SIZE)?;3435// Even though `self.size()` may be smaller than `Self::page_count() * page::PAGE_SIZE`, it36// is always a number between `(Self::page_count() - 1) * page::PAGE_SIZE` and37// `Self::page_count() * page::PAGE_SIZE`, hence the check below is sufficient.38if offset < self.size() {39self.index += 1;40} else {41return None;42}4344// TODO: Use `NonNull::add()` instead, once the minimum supported compiler version is45// bumped to 1.80 or later.46//47// SAFETY: `offset` is in the interval `[0, (self.page_count() - 1) * page::PAGE_SIZE]`,48// hence the resulting pointer is guaranteed to be within the same allocation.49let ptr = unsafe { self.buf.as_ptr().add(offset) };5051// SAFETY: `ptr` is guaranteed to be non-null given that it is derived from `self.buf`.52let ptr = unsafe { NonNull::new_unchecked(ptr) };5354// SAFETY:55// - `ptr` is a valid pointer to a `Vmalloc` allocation.56// - `ptr` is valid for the duration of `'a`.57Some(unsafe { Vmalloc::to_page(ptr) })58}5960fn size_hint(&self) -> (usize, Option<usize>) {61let remaining = self.page_count().saturating_sub(self.index);6263(remaining, Some(remaining))64}65}6667impl<'a> VmallocPageIter<'a> {68/// Creates a new [`VmallocPageIter`] instance.69///70/// # Safety71///72/// - `buf` must be a [`page::PAGE_SIZE`] aligned pointer into a [`Vmalloc`] allocation.73/// - `buf` must be valid for at least the lifetime of `'a`.74/// - `size` must be the number of bytes from `buf` until the end of the [`Vmalloc`] allocation75/// `buf` points to.76pub unsafe fn new(buf: NonNull<u8>, size: usize) -> Self {77// INVARIANT: By the safety requirements, `buf` is a valid and `page::PAGE_SIZE` aligned78// pointer into a [`Vmalloc`] allocation.79Self {80buf,81size,82index: 0,83_p: PhantomData,84}85}8687/// Returns the size of the backing [`Vmalloc`] allocation in bytes.88///89/// Note that this is the size the [`Vmalloc`] allocation has been allocated with. Hence, this90/// number may be smaller than `[`Self::page_count`] * [`page::PAGE_SIZE`]`.91#[inline]92pub fn size(&self) -> usize {93self.size94}9596/// Returns the number of pages owned by the backing [`Vmalloc`] allocation.97#[inline]98pub fn page_count(&self) -> usize {99self.size().div_ceil(page::PAGE_SIZE)100}101}102103104