mirror of
https://github.com/asterinas/asterinas.git
synced 2025-06-28 03:43:23 +00:00
Make the RCU lifetime of page tables explicit
This commit is contained in:
committed by
Tate, Hongliang Tian
parent
ef81100958
commit
79b3f68892
@ -37,11 +37,14 @@ use core::{
|
||||
|
||||
pub(in crate::mm) use self::{child::Child, entry::Entry};
|
||||
use super::{nr_subpage_per_huge, PageTableEntryTrait};
|
||||
use crate::mm::{
|
||||
frame::{meta::AnyFrameMeta, Frame, FrameRef},
|
||||
paddr_to_vaddr,
|
||||
page_table::{load_pte, store_pte},
|
||||
FrameAllocOptions, Infallible, Paddr, PagingConstsTrait, PagingLevel, VmReader,
|
||||
use crate::{
|
||||
mm::{
|
||||
frame::{meta::AnyFrameMeta, Frame, FrameRef},
|
||||
paddr_to_vaddr,
|
||||
page_table::{load_pte, store_pte},
|
||||
FrameAllocOptions, Infallible, PagingConstsTrait, PagingLevel, VmReader,
|
||||
},
|
||||
task::atomic_mode::InAtomicMode,
|
||||
};
|
||||
|
||||
/// A smart pointer to a page table node.
|
||||
@ -55,9 +58,6 @@ use crate::mm::{
|
||||
/// [`PageTableGuard`].
|
||||
pub(super) type PageTableNode<E, C> = Frame<PageTablePageMeta<E, C>>;
|
||||
|
||||
/// A reference to a page table node.
|
||||
pub(super) type PageTableNodeRef<'a, E, C> = FrameRef<'a, PageTablePageMeta<E, C>>;
|
||||
|
||||
impl<E: PageTableEntryTrait, C: PagingConstsTrait> PageTableNode<E, C> {
|
||||
pub(super) fn level(&self) -> PagingLevel {
|
||||
self.meta().level
|
||||
@ -68,8 +68,6 @@ impl<E: PageTableEntryTrait, C: PagingConstsTrait> PageTableNode<E, C> {
|
||||
}
|
||||
|
||||
/// Allocates a new empty page table node.
|
||||
///
|
||||
/// This function returns a locked owning guard.
|
||||
pub(super) fn alloc(level: PagingLevel, is_tracked: MapTrackingStatus) -> Self {
|
||||
let meta = PageTablePageMeta::new(level, is_tracked);
|
||||
let frame = FrameAllocOptions::new()
|
||||
@ -82,22 +80,6 @@ impl<E: PageTableEntryTrait, C: PagingConstsTrait> PageTableNode<E, C> {
|
||||
frame
|
||||
}
|
||||
|
||||
/// Locks the page table node.
|
||||
pub(super) fn lock(&self) -> PageTableGuard<'_, E, C> {
|
||||
while self
|
||||
.meta()
|
||||
.lock
|
||||
.compare_exchange(0, 1, Ordering::Acquire, Ordering::Relaxed)
|
||||
.is_err()
|
||||
{
|
||||
core::hint::spin_loop();
|
||||
}
|
||||
|
||||
PageTableGuard::<'_, E, C> {
|
||||
inner: self.borrow(),
|
||||
}
|
||||
}
|
||||
|
||||
/// Activates the page table assuming it is a root page table.
|
||||
///
|
||||
/// Here we ensure not dropping an active page table by making a
|
||||
@ -145,46 +127,69 @@ impl<E: PageTableEntryTrait, C: PagingConstsTrait> PageTableNode<E, C> {
|
||||
}
|
||||
}
|
||||
|
||||
/// A guard that holds the lock of a page table node.
|
||||
#[derive(Debug)]
|
||||
pub(super) struct PageTableGuard<'a, E: PageTableEntryTrait, C: PagingConstsTrait> {
|
||||
inner: PageTableNodeRef<'a, E, C>,
|
||||
/// A reference to a page table node.
|
||||
pub(super) type PageTableNodeRef<'a, E, C> = FrameRef<'a, PageTablePageMeta<E, C>>;
|
||||
|
||||
impl<'a, E: PageTableEntryTrait, C: PagingConstsTrait> PageTableNodeRef<'a, E, C> {
|
||||
/// Locks the page table node.
|
||||
///
|
||||
/// An atomic mode guard is required to
|
||||
/// 1. prevent deadlocks;
|
||||
/// 2. provide a lifetime (`'rcu`) that the nodes are guaranteed to outlive.
|
||||
pub(super) fn lock<'rcu>(self, _guard: &'rcu dyn InAtomicMode) -> PageTableGuard<'rcu, E, C>
|
||||
where
|
||||
'a: 'rcu,
|
||||
{
|
||||
while self
|
||||
.meta()
|
||||
.lock
|
||||
.compare_exchange(0, 1, Ordering::Acquire, Ordering::Relaxed)
|
||||
.is_err()
|
||||
{
|
||||
core::hint::spin_loop();
|
||||
}
|
||||
|
||||
PageTableGuard::<'rcu, E, C> { inner: self }
|
||||
}
|
||||
|
||||
/// Creates a new [`PageTableGuard`] without checking if the page table lock is held.
|
||||
///
|
||||
/// # Safety
|
||||
///
|
||||
/// This function must be called if this task logically holds the lock.
|
||||
///
|
||||
/// Calling this function when a guard is already created is undefined behavior
|
||||
/// unless that guard was already forgotten.
|
||||
pub(super) unsafe fn make_guard_unchecked<'rcu>(
|
||||
self,
|
||||
_guard: &'rcu dyn InAtomicMode,
|
||||
) -> PageTableGuard<'rcu, E, C>
|
||||
where
|
||||
'a: 'rcu,
|
||||
{
|
||||
PageTableGuard { inner: self }
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a, E: PageTableEntryTrait, C: PagingConstsTrait> PageTableGuard<'a, E, C> {
|
||||
/// A guard that holds the lock of a page table node.
|
||||
#[derive(Debug)]
|
||||
pub(super) struct PageTableGuard<'rcu, E: PageTableEntryTrait, C: PagingConstsTrait> {
|
||||
inner: PageTableNodeRef<'rcu, E, C>,
|
||||
}
|
||||
|
||||
impl<'rcu, E: PageTableEntryTrait, C: PagingConstsTrait> PageTableGuard<'rcu, E, C> {
|
||||
/// Borrows an entry in the node at a given index.
|
||||
///
|
||||
/// # Panics
|
||||
///
|
||||
/// Panics if the index is not within the bound of
|
||||
/// [`nr_subpage_per_huge<C>`].
|
||||
pub(super) fn entry<'s>(&'s mut self, idx: usize) -> Entry<'s, 'a, E, C> {
|
||||
pub(super) fn entry(&mut self, idx: usize) -> Entry<'_, 'rcu, E, C> {
|
||||
assert!(idx < nr_subpage_per_huge::<C>());
|
||||
// SAFETY: The index is within the bound.
|
||||
unsafe { Entry::new_at(self, idx) }
|
||||
}
|
||||
|
||||
/// Converts the guard into a raw physical address.
|
||||
///
|
||||
/// It will not release the lock. It may be paired with [`Self::from_raw_paddr`]
|
||||
/// to manually manage pointers.
|
||||
pub(super) fn into_raw_paddr(self) -> Paddr {
|
||||
self.start_paddr()
|
||||
}
|
||||
|
||||
/// Converts a raw physical address to a guard.
|
||||
///
|
||||
/// # Safety
|
||||
///
|
||||
/// The caller must ensure that the physical address is valid and points to
|
||||
/// a forgotten page table node that is locked (see [`Self::into_raw_paddr`]).
|
||||
pub(super) unsafe fn from_raw_paddr(paddr: Paddr) -> Self {
|
||||
Self {
|
||||
// SAFETY: The caller ensures safety.
|
||||
inner: unsafe { PageTableNodeRef::borrow_paddr(paddr) },
|
||||
}
|
||||
}
|
||||
|
||||
/// Gets the number of valid PTEs in the node.
|
||||
pub(super) fn nr_children(&self) -> u16 {
|
||||
// SAFETY: The lock is held so we have an exclusive access.
|
||||
@ -244,8 +249,8 @@ impl<'a, E: PageTableEntryTrait, C: PagingConstsTrait> PageTableGuard<'a, E, C>
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a, E: PageTableEntryTrait, C: PagingConstsTrait> Deref for PageTableGuard<'a, E, C> {
|
||||
type Target = PageTableNodeRef<'a, E, C>;
|
||||
impl<'rcu, E: PageTableEntryTrait, C: PagingConstsTrait> Deref for PageTableGuard<'rcu, E, C> {
|
||||
type Target = PageTableNodeRef<'rcu, E, C>;
|
||||
|
||||
fn deref(&self) -> &Self::Target {
|
||||
&self.inner
|
||||
|
Reference in New Issue
Block a user