`SafePage` wraps a regular page but adds an invariant that the page data area does not incur data races. This means `SafePage` cannot be mapped to user space or shared with devices, and it becomes simpler to directly reference the contents of the page. Signed-off-by: Andreas Hindborg --- rust/kernel/page.rs | 73 +++++++++++++++++++++++++++++++++++++++++++++-------- 1 file changed, 62 insertions(+), 11 deletions(-) diff --git a/rust/kernel/page.rs b/rust/kernel/page.rs index d56ae597f692..f143b42d1bd6 100644 --- a/rust/kernel/page.rs +++ b/rust/kernel/page.rs @@ -8,8 +8,10 @@ Flags, // }, bindings, - error::code::*, - error::Result, + error::{ + code::*, + Result, // + }, types::{ Opaque, Ownable, @@ -20,7 +22,7 @@ use core::{ marker::PhantomData, mem::ManuallyDrop, - ops::Deref, + ops::{Deref, DerefMut}, ptr::{ self, NonNull, // @@ -193,14 +195,10 @@ impl Page { /// ``` #[inline] pub fn alloc_page(flags: Flags) -> Result, AllocError> { - // SAFETY: Depending on the value of `gfp_flags`, this call may sleep. Other than that, it - // is always safe to call this method. - let page = unsafe { bindings::alloc_pages(flags.as_raw(), 0) }; - let page = NonNull::new(page).ok_or(AllocError)?; - // SAFETY: We just successfully allocated a page, so we now have ownership of the newly - // allocated page. We transfer that ownership to the new `Owned` object. - // Since `Page` is transparent, we can cast the pointer directly. - Ok(unsafe { Owned::from_raw(page.cast()) }) + let page = SafePage::alloc_page(flags)?; + // SAFETY: `SafePage` is `#[repr(transparent)]` over `Page`, so a pointer to a `SafePage` + // with ownership is also a pointer to a `Page` with ownership. + Ok(unsafe { Owned::from_raw(Owned::into_raw(page).cast()) }) } /// Returns a raw pointer to the page. @@ -401,3 +399,56 @@ unsafe fn release(&mut self) { unsafe { bindings::__free_pages(ptr.cast(), 0) }; } } + +/// A page whose data area follows standard Rust aliasing rules. +/// +/// [`SafePage`] has the same usage constraints as other Rust types. Thus, it cannot be mapped to +/// user space or shared with devices. This makes it safe to reference the contents of the page +/// while the page is mapped in kernel space. +/// +/// # Invariants +/// +/// The data of this page is accessed only through references to [`SafePage`]. While a shared +/// reference to a [`SafePage`] exists, there are no writes to its data. While an exclusive +/// reference exists, there are no other reads or writes of its data. +#[repr(transparent)] +pub struct SafePage(Page); + +impl SafePage { + /// Allocate a new `SafePage`. + pub fn alloc_page(flags: Flags) -> Result, AllocError> { + // SAFETY: Depending on the value of `gfp_flags`, this call may sleep. Other than that, it + // is always safe to call this method. + let page = unsafe { bindings::alloc_pages(flags.as_raw(), 0) }; + let page = NonNull::new(page).ok_or(AllocError)?; + + // SAFETY: We just successfully allocated a page, so we now have ownership of the newly + // allocated page. We transfer that ownership to the new `Owned` object. Since + // `Page` and `SafePage` are transparent, we can cast to the raw page pointer directly. + Ok(unsafe { Owned::from_raw(page.cast()) }) + } +} + +impl Ownable for SafePage { + #[inline] + unsafe fn release(&mut self) { + let ptr: *mut Self = self; + // SAFETY: By the function safety requirements, we have ownership of the page and can free + // it. Since `SafePage` and `Page` are transparent, we can cast the raw pointer directly. + unsafe { bindings::__free_pages(ptr.cast(), 0) }; + } +} + +impl Deref for SafePage { + type Target = Page; + + fn deref(&self) -> &Self::Target { + &self.0 + } +} + +impl DerefMut for SafePage { + fn deref_mut(&mut self) -> &mut Self::Target { + &mut self.0 + } +} -- 2.51.2