Commit 467971a9 authored by Danilo Krummrich's avatar Danilo Krummrich
Browse files

rust: page: implement BorrowedPage



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.

Acked-by: default avatarAlice Ryhl <aliceryhl@google.com>
Reviewed-by: default avatarAlexandre Courbot <acourbot@nvidia.com>
Tested-by: default avatarAlexandre Courbot <acourbot@nvidia.com>
Reviewed-by: default avatarDaniel Almeida <daniel.almeida@collabora.com>
Link: https://lore.kernel.org/rust-for-linux/ZnCzLIly3DRK2eab@boqun-archlinux/ [1]
Link: https://lore.kernel.org/r/20250820145434.94745-2-dakr@kernel.org


Signed-off-by: default avatarDanilo Krummrich <dakr@kernel.org>
parent 93296e9d
Loading
Loading
Loading
Loading
+1 −0
Original line number Diff line number Diff line
@@ -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>
+74 −1
Original line number Diff line number Diff line
@@ -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