Lock-free cursor creation

This commit is contained in:
Zhang Junyang
2024-11-04 10:16:06 +08:00
committed by Tate, Hongliang Tian
parent 0ef55a396f
commit b2b00bdfd2
2 changed files with 33 additions and 28 deletions

View File

@ -65,17 +65,18 @@
//! table cursor should add additional entry point checks to prevent these defined //! table cursor should add additional entry point checks to prevent these defined
//! behaviors if they are not wanted. //! behaviors if they are not wanted.
use core::{any::TypeId, marker::PhantomData, ops::Range}; use core::{any::TypeId, marker::PhantomData, mem::ManuallyDrop, ops::Range};
use align_ext::AlignExt; use align_ext::AlignExt;
use super::{ use super::{
page_size, pte_index, Child, Entry, KernelMode, PageTable, PageTableEntryTrait, PageTableError, page_size, pte_index, Child, Entry, KernelMode, PageTable, PageTableEntryTrait, PageTableError,
PageTableMode, PageTableNode, PagingConstsTrait, PagingLevel, UserMode, PageTableMode, PageTableNode, PagingConstsTrait, PagingLevel, RawPageTableNode, UserMode,
}; };
use crate::{ use crate::{
mm::{ mm::{
kspace::should_map_as_tracked, kspace::should_map_as_tracked,
paddr_to_vaddr,
page::{meta::MapTrackingStatus, DynPage}, page::{meta::MapTrackingStatus, DynPage},
Paddr, PageProperty, Vaddr, Paddr, PageProperty, Vaddr,
}, },
@ -160,16 +161,8 @@ where
return Err(PageTableError::UnalignedVaddr); return Err(PageTableError::UnalignedVaddr);
} }
// Create a guard array that only hold the root node lock.
let guards = core::array::from_fn(|i| {
if i == (C::NR_LEVELS - 1) as usize {
Some(pt.root.clone_shallow().lock())
} else {
None
}
});
let mut cursor = Self { let mut cursor = Self {
guards, guards: core::array::from_fn(|_| None),
level: C::NR_LEVELS, level: C::NR_LEVELS,
guard_level: C::NR_LEVELS, guard_level: C::NR_LEVELS,
va: va.start, va: va.start,
@ -178,35 +171,47 @@ where
_phantom: PhantomData, _phantom: PhantomData,
}; };
let mut cur_pt_addr = pt.root.paddr();
// Go down and get proper locks. The cursor should hold a lock of a // Go down and get proper locks. The cursor should hold a lock of a
// page table node containing the virtual address range. // page table node containing the virtual address range.
// //
// While going down, previous guards of too-high levels will be released. // While going down, previous guards of too-high levels will be released.
loop { loop {
let start_idx = pte_index::<C>(va.start, cursor.level);
let level_too_high = { let level_too_high = {
let start_idx = pte_index::<C>(va.start, cursor.level);
let end_idx = pte_index::<C>(va.end - 1, cursor.level); let end_idx = pte_index::<C>(va.end - 1, cursor.level);
start_idx == end_idx cursor.level > 1 && start_idx == end_idx
}; };
if !level_too_high { if !level_too_high {
break; break;
} }
let entry = cursor.cur_entry(); let cur_pt_ptr = paddr_to_vaddr(cur_pt_addr) as *mut E;
if !entry.is_node() { // SAFETY: The pointer and index is valid since the root page table
// does not short-live it. The child page table node won't be
// recycled by another thread while we are using it.
let cur_pte = unsafe { cur_pt_ptr.add(start_idx).read() };
if cur_pte.is_present() {
if cur_pte.is_last(cursor.level) {
break;
} else {
cur_pt_addr = cur_pte.paddr();
}
} else {
break; break;
} }
let Child::PageTable(child_pt) = entry.to_owned() else { cursor.level -= 1;
unreachable!("Already checked");
};
cursor.push_level(child_pt.lock());
// Release the guard of the previous (upper) level.
cursor.guards[cursor.level as usize] = None;
cursor.guard_level -= 1;
} }
// SAFETY: The address and level corresponds to a child converted into
// a PTE and we clone it to get a new handle to the node.
let raw = unsafe { RawPageTableNode::<E, C>::from_raw_parts(cur_pt_addr, cursor.level) };
let _inc_ref = ManuallyDrop::new(raw.clone_shallow());
let lock = raw.lock();
cursor.guards[cursor.level as usize - 1] = Some(lock);
cursor.guard_level = cursor.level;
Ok(cursor) Ok(cursor)
} }
@ -306,7 +311,7 @@ where
/// This method requires locks acquired before calling it. The discarded /// This method requires locks acquired before calling it. The discarded
/// level will be unlocked. /// level will be unlocked.
fn pop_level(&mut self) { fn pop_level(&mut self) {
self.guards[(self.level - 1) as usize] = None; self.guards[self.level as usize - 1] = None;
self.level += 1; self.level += 1;
// TODO: Drop page tables if page tables become empty. // TODO: Drop page tables if page tables become empty.
@ -316,7 +321,7 @@ where
fn push_level(&mut self, child_pt: PageTableNode<E, C>) { fn push_level(&mut self, child_pt: PageTableNode<E, C>) {
self.level -= 1; self.level -= 1;
debug_assert_eq!(self.level, child_pt.level()); debug_assert_eq!(self.level, child_pt.level());
self.guards[(self.level - 1) as usize] = Some(child_pt); self.guards[self.level as usize - 1] = Some(child_pt);
} }
fn should_map_as_tracked(&self) -> bool { fn should_map_as_tracked(&self) -> bool {
@ -326,7 +331,7 @@ where
} }
fn cur_entry(&mut self) -> Entry<'_, E, C> { fn cur_entry(&mut self) -> Entry<'_, E, C> {
let node = self.guards[(self.level - 1) as usize].as_mut().unwrap(); let node = self.guards[self.level as usize - 1].as_mut().unwrap();
node.entry(pte_index::<C>(self.va, self.level)) node.entry(pte_index::<C>(self.va, self.level))
} }
} }

View File

@ -176,7 +176,7 @@ where
/// The caller must ensure that the physical address is valid and points to /// The caller must ensure that the physical address is valid and points to
/// a forgotten page table node. A forgotten page table node can only be /// a forgotten page table node. A forgotten page table node can only be
/// restored once. The level must match the level of the page table node. /// restored once. The level must match the level of the page table node.
unsafe fn from_raw_parts(paddr: Paddr, level: PagingLevel) -> Self { pub(super) unsafe fn from_raw_parts(paddr: Paddr, level: PagingLevel) -> Self {
Self { Self {
raw: paddr, raw: paddr,
level, level,