rust-for-linux.vger.kernel.org archive mirror
 help / color / mirror / Atom feed
* [PATCH] rust: page: implement BorrowedPage
@ 2025-08-04 19:50 Danilo Krummrich
  2025-08-04 20:02 ` Benno Lossin
                   ` (3 more replies)
  0 siblings, 4 replies; 11+ messages in thread
From: Danilo Krummrich @ 2025-08-04 19:50 UTC (permalink / raw)
  To: ojeda, alex.gaynor, boqun.feng, gary, bjorn3_gh, lossin,
	a.hindborg, aliceryhl, tmgross, abdiel.janulgue, acourbot
  Cc: rust-for-linux, Danilo Krummrich

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 lands.

This is required by the scatterlist abstractions.

Signed-off-by: Danilo Krummrich <dakr@kernel.org>
---
 rust/bindings/bindings_helper.h |  1 +
 rust/kernel/page.rs             | 83 ++++++++++++++++++++++++++++++++-
 2 files changed, 83 insertions(+), 1 deletion(-)

diff --git a/rust/bindings/bindings_helper.h b/rust/bindings/bindings_helper.h
index 84d60635e8a9..0e140e07758b 100644
--- a/rust/bindings/bindings_helper.h
+++ b/rust/bindings/bindings_helper.h
@@ -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>
diff --git a/rust/kernel/page.rs b/rust/kernel/page.rs
index 7c1b17246ed5..bea7f0ab91f4 100644
--- a/rust/kernel/page.rs
+++ b/rust/kernel/page.rs
@@ -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,82 @@ 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::ptr;
+/// # use core::ptr::NonNull;
+///
+/// trait PageOwner {
+///     fn borrow_page<'a>(&'a mut self) -> BorrowedPage<'a>;
+/// }
+///
+/// impl<T> PageOwner for VBox<T> {
+///     // Borrow the first page of a `VBox`.
+///     fn borrow_page<'a>(&'a mut self) -> BorrowedPage<'a> {
+///         let ptr = ptr::from_ref(&**self);
+///
+///         // 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 = vbox.borrow_page();
+///
+/// // SAFETY: There is no concurrent read or write to the same 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

base-commit: d2eedaa3909be9102d648a4a0a50ccf64f96c54f
-- 
2.50.1


^ permalink raw reply related	[flat|nested] 11+ messages in thread

end of thread, other threads:[~2025-08-05 17:18 UTC | newest]

Thread overview: 11+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2025-08-04 19:50 [PATCH] rust: page: implement BorrowedPage Danilo Krummrich
2025-08-04 20:02 ` Benno Lossin
2025-08-04 20:08   ` Danilo Krummrich
2025-08-05 12:15 ` Alexandre Courbot
2025-08-05 12:18 ` Alice Ryhl
2025-08-05 12:30 ` Daniel Almeida
2025-08-05 12:53   ` Danilo Krummrich
2025-08-05 13:20     ` Danilo Krummrich
2025-08-05 13:29     ` Daniel Almeida
2025-08-05 17:07       ` Andreas Hindborg
2025-08-05 17:18         ` Danilo Krummrich

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for NNTP newsgroup(s).