Remove AnyFrame

This commit is contained in:
Zhang Junyang
2024-12-24 14:28:28 +08:00
committed by Tate, Hongliang Tian
parent c9a37ccab1
commit 14308f81b6
8 changed files with 131 additions and 196 deletions

View File

@ -38,6 +38,7 @@ pub(crate) mod mapping {
use core::{ use core::{
any::Any, any::Any,
cell::UnsafeCell, cell::UnsafeCell,
fmt::Debug,
mem::{size_of, MaybeUninit}, mem::{size_of, MaybeUninit},
sync::atomic::{AtomicU32, Ordering}, sync::atomic::{AtomicU32, Ordering},
}; };
@ -113,7 +114,7 @@ const_assert_eq!(size_of::<MetaSlot>(), META_SLOT_SIZE);
/// The implemented structure must have a size less than or equal to /// The implemented structure must have a size less than or equal to
/// [`PAGE_METADATA_MAX_SIZE`] and an alignment less than or equal to /// [`PAGE_METADATA_MAX_SIZE`] and an alignment less than or equal to
/// [`PAGE_METADATA_MAX_ALIGN`]. /// [`PAGE_METADATA_MAX_ALIGN`].
pub unsafe trait FrameMeta: Any + Send + Sync + 'static { pub unsafe trait FrameMeta: Any + Send + Sync + Debug + 'static {
/// Called when the last handle to the page is dropped. /// Called when the last handle to the page is dropped.
fn on_drop(&mut self, _paddr: Paddr) {} fn on_drop(&mut self, _paddr: Paddr) {}
} }

View File

@ -20,10 +20,9 @@ mod segment;
pub mod untyped; pub mod untyped;
use core::{ use core::{
any::Any,
marker::PhantomData, marker::PhantomData,
mem::ManuallyDrop, mem::ManuallyDrop,
sync::atomic::{AtomicUsize, Ordering}, sync::atomic::{AtomicU32, AtomicUsize, Ordering},
}; };
use meta::{ use meta::{
@ -39,14 +38,15 @@ static MAX_PADDR: AtomicUsize = AtomicUsize::new(0);
/// A page with a statically-known usage, whose metadata is represented by `M`. /// A page with a statically-known usage, whose metadata is represented by `M`.
#[derive(Debug)] #[derive(Debug)]
pub struct Frame<M: FrameMeta> { #[repr(transparent)]
pub struct Frame<M: FrameMeta + ?Sized> {
pub(super) ptr: *const MetaSlot, pub(super) ptr: *const MetaSlot,
pub(super) _marker: PhantomData<M>, pub(super) _marker: PhantomData<M>,
} }
unsafe impl<M: FrameMeta> Send for Frame<M> {} unsafe impl<M: FrameMeta + ?Sized> Send for Frame<M> {}
unsafe impl<M: FrameMeta> Sync for Frame<M> {} unsafe impl<M: FrameMeta + ?Sized> Sync for Frame<M> {}
impl<M: FrameMeta> Frame<M> { impl<M: FrameMeta> Frame<M> {
/// Get a `Frame` handle with a specific usage from a raw, unused page. /// Get a `Frame` handle with a specific usage from a raw, unused page.
@ -102,6 +102,78 @@ impl<M: FrameMeta> Frame<M> {
} }
} }
/// Get the metadata of this page.
pub fn meta(&self) -> &M {
// SAFETY: `self.ptr` points to the metadata storage which is valid to
// be immutably borrowed as `M` because the type is correct, it lives
// under the given lifetime, and no one will mutably borrow the page
// metadata after initialization.
unsafe { &*self.ptr.cast() }
}
}
impl<M: FrameMeta + ?Sized> Frame<M> {
/// Get the physical address.
pub fn paddr(&self) -> Paddr {
mapping::meta_to_page::<PagingConsts>(self.ptr as Vaddr)
}
/// Get the paging level of this page.
///
/// This is the level of the page table entry that maps the frame,
/// which determines the size of the frame.
///
/// Currently, the level is always 1, which means the frame is a regular
/// page frame.
pub const fn level(&self) -> PagingLevel {
1
}
/// Size of this page in bytes.
pub const fn size(&self) -> usize {
PAGE_SIZE
}
/// Get the dyncamically-typed metadata of this frame.
///
/// If the type is known at compile time, use [`Frame::meta`] instead.
pub fn dyn_meta(&self) -> &dyn FrameMeta {
let slot = self.slot();
// SAFETY: The page metadata is valid to be borrowed immutably, since it will never be
// borrowed mutably after initialization.
let vtable_ptr = unsafe { &*slot.vtable_ptr.get() };
// SAFETY: The page metadata is initialized and valid.
let vtable_ptr = *unsafe { vtable_ptr.assume_init_ref() };
let meta_ptr: *const dyn FrameMeta = core::ptr::from_raw_parts(self.ptr, vtable_ptr);
// SAFETY: `self.ptr` points to the metadata storage which is valid to be immutably
// borrowed under `vtable_ptr` because the vtable is correct, it lives under the given
// lifetime, and no one will mutably borrow the page metadata after initialization.
unsafe { &*meta_ptr }
}
/// Get the reference count of the page.
///
/// It returns the number of all references to the page, including all the
/// existing page handles ([`Frame`], [`Frame<dyn FrameMeta>`]), and all the mappings in the
/// page table that points to the page.
///
/// # Safety
///
/// The function is safe to call, but using it requires extra care. The
/// reference count can be changed by other threads at any time including
/// potentially between calling this method and acting on the result.
pub fn reference_count(&self) -> u32 {
self.ref_count().load(Ordering::Relaxed)
}
fn ref_count(&self) -> &AtomicU32 {
unsafe { &(*self.ptr).ref_count }
}
/// Forget the handle to the page. /// Forget the handle to the page.
/// ///
/// This will result in the page being leaked without calling the custom dropper. /// This will result in the page being leaked without calling the custom dropper.
@ -138,58 +210,14 @@ impl<M: FrameMeta> Frame<M> {
} }
} }
/// Get the physical address.
pub fn paddr(&self) -> Paddr {
mapping::meta_to_page::<PagingConsts>(self.ptr as Vaddr)
}
/// Get the paging level of this page.
///
/// This is the level of the page table entry that maps the frame,
/// which determines the size of the frame.
///
/// Currently, the level is always 1, which means the frame is a regular
/// page frame.
pub const fn level(&self) -> PagingLevel {
1
}
/// Size of this page in bytes.
pub const fn size(&self) -> usize {
PAGE_SIZE
}
/// Get the metadata of this page.
pub fn meta(&self) -> &M {
// SAFETY: `self.ptr` points to the metadata storage which is valid to be immutably
// borrowed as `M` because the type is correct, it lives under the given lifetime, and no
// one will mutably borrow the page metadata after initialization.
unsafe { &*self.ptr.cast() }
}
/// Get the reference count of the page.
///
/// It returns the number of all references to the page, including all the
/// existing page handles ([`Frame`], [`AnyFrame`]), and all the mappings in the
/// page table that points to the page.
///
/// # Safety
///
/// The function is safe to call, but using it requires extra care. The
/// reference count can be changed by other threads at any time including
/// potentially between calling this method and acting on the result.
pub fn reference_count(&self) -> u32 {
self.slot().ref_count.load(Ordering::Relaxed)
}
fn slot(&self) -> &MetaSlot { fn slot(&self) -> &MetaSlot {
// SAFETY: `ptr` points to a valid `MetaSlot` that will never be mutably borrowed, so taking an // SAFETY: `ptr` points to a valid `MetaSlot` that will never be
// immutable reference to it is always safe. // mutably borrowed, so taking an immutable reference to it is safe.
unsafe { &*self.ptr } unsafe { &*self.ptr }
} }
} }
impl<M: FrameMeta> Clone for Frame<M> { impl<M: FrameMeta + ?Sized> Clone for Frame<M> {
fn clone(&self) -> Self { fn clone(&self) -> Self {
// SAFETY: We have already held a reference to the page. // SAFETY: We have already held a reference to the page.
unsafe { self.slot().inc_ref_count() }; unsafe { self.slot().inc_ref_count() };
@ -201,7 +229,7 @@ impl<M: FrameMeta> Clone for Frame<M> {
} }
} }
impl<M: FrameMeta> Drop for Frame<M> { impl<M: FrameMeta + ?Sized> Drop for Frame<M> {
fn drop(&mut self) { fn drop(&mut self) {
let last_ref_cnt = self.slot().ref_count.fetch_sub(1, Ordering::Release); let last_ref_cnt = self.slot().ref_count.fetch_sub(1, Ordering::Release);
debug_assert!(last_ref_cnt != 0 && last_ref_cnt != REF_COUNT_UNUSED); debug_assert!(last_ref_cnt != 0 && last_ref_cnt != REF_COUNT_UNUSED);
@ -219,147 +247,44 @@ impl<M: FrameMeta> Drop for Frame<M> {
} }
} }
/// A page with a dynamically-known usage. impl<M: FrameMeta> TryFrom<Frame<dyn FrameMeta>> for Frame<M> {
/// type Error = Frame<dyn FrameMeta>;
/// It can also be used when the user don't care about the usage of the page.
#[derive(Debug)]
pub struct AnyFrame {
ptr: *const MetaSlot,
}
unsafe impl Send for AnyFrame {} /// Try converting a [`Frame<dyn FrameMeta>`] into the statically-typed [`Frame`].
unsafe impl Sync for AnyFrame {}
impl AnyFrame {
/// Forget the handle to the page.
///
/// This is the same as [`Frame::into_raw`].
///
/// This will result in the page being leaked without calling the custom dropper.
///
/// A physical address to the page is returned in case the page needs to be
/// restored using [`Self::from_raw`] later.
pub(in crate::mm) fn into_raw(self) -> Paddr {
let paddr = self.paddr();
core::mem::forget(self);
paddr
}
/// Restore a forgotten page from a physical address.
///
/// # Safety
///
/// The safety concerns are the same as [`Frame::from_raw`].
pub(in crate::mm) unsafe fn from_raw(paddr: Paddr) -> Self {
let vaddr = mapping::page_to_meta::<PagingConsts>(paddr);
let ptr = vaddr as *const MetaSlot;
Self { ptr }
}
/// Get the metadata of this page.
pub fn meta(&self) -> &dyn Any {
let slot = self.slot();
// SAFETY: The page metadata is valid to be borrowed immutably, since it will never be
// borrowed mutably after initialization.
let vtable_ptr = unsafe { &*slot.vtable_ptr.get() };
// SAFETY: The page metadata is initialized and valid.
let vtable_ptr = *unsafe { vtable_ptr.assume_init_ref() };
let meta_ptr: *const dyn FrameMeta = core::ptr::from_raw_parts(self.ptr, vtable_ptr);
// SAFETY: `self.ptr` points to the metadata storage which is valid to be immutably
// borrowed under `vtable_ptr` because the vtable is correct, it lives under the given
// lifetime, and no one will mutably borrow the page metadata after initialization.
(unsafe { &*meta_ptr }) as &dyn Any
}
/// Get the physical address of the start of the page
pub fn paddr(&self) -> Paddr {
mapping::meta_to_page::<PagingConsts>(self.ptr as Vaddr)
}
/// Get the paging level of this page.
pub fn level(&self) -> PagingLevel {
1
}
/// Size of this page in bytes.
pub fn size(&self) -> usize {
PAGE_SIZE
}
fn slot(&self) -> &MetaSlot {
// SAFETY: `ptr` points to a valid `MetaSlot` that will never be mutably borrowed, so taking an
// immutable reference to it is always safe.
unsafe { &*self.ptr }
}
}
impl<M: FrameMeta> TryFrom<AnyFrame> for Frame<M> {
type Error = AnyFrame;
/// Try converting a [`AnyFrame`] into the statically-typed [`Frame`].
/// ///
/// If the usage of the page is not the same as the expected usage, it will /// If the usage of the page is not the same as the expected usage, it will
/// return the dynamic page itself as is. /// return the dynamic page itself as is.
fn try_from(dyn_page: AnyFrame) -> Result<Self, Self::Error> { fn try_from(dyn_frame: Frame<dyn FrameMeta>) -> Result<Self, Self::Error> {
if dyn_page.meta().is::<M>() { if (dyn_frame.dyn_meta() as &dyn core::any::Any).is::<M>() {
let result = Frame { let result = Frame {
ptr: dyn_page.ptr, ptr: dyn_frame.ptr,
_marker: PhantomData, _marker: PhantomData,
}; };
let _ = ManuallyDrop::new(dyn_page); let _ = ManuallyDrop::new(dyn_frame);
Ok(result) Ok(result)
} else { } else {
Err(dyn_page) Err(dyn_frame)
} }
} }
} }
impl<M: FrameMeta> From<Frame<M>> for AnyFrame { impl<M: FrameMeta> From<Frame<M>> for Frame<dyn FrameMeta> {
fn from(page: Frame<M>) -> Self { fn from(frame: Frame<M>) -> Self {
let result = Self { ptr: page.ptr }; let result = Self {
let _ = ManuallyDrop::new(page); ptr: frame.ptr,
_marker: PhantomData,
};
let _ = ManuallyDrop::new(frame);
result result
} }
} }
impl From<UntypedFrame> for AnyFrame { impl From<UntypedFrame> for Frame<dyn FrameMeta> {
fn from(frame: UntypedFrame) -> Self { fn from(frame: UntypedFrame) -> Self {
Frame::<UntypedMeta>::from(frame).into() Frame::<UntypedMeta>::from(frame).into()
} }
} }
impl Clone for AnyFrame {
fn clone(&self) -> Self {
// SAFETY: We have already held a reference to the page.
unsafe { self.slot().inc_ref_count() };
Self { ptr: self.ptr }
}
}
impl Drop for AnyFrame {
fn drop(&mut self) {
let last_ref_cnt = self.slot().ref_count.fetch_sub(1, Ordering::Release);
debug_assert!(last_ref_cnt != 0 && last_ref_cnt != REF_COUNT_UNUSED);
if last_ref_cnt == 1 {
// A fence is needed here with the same reasons stated in the implementation of
// `Arc::drop`: <https://doc.rust-lang.org/std/sync/struct.Arc.html#method.drop>.
core::sync::atomic::fence(Ordering::Acquire);
// SAFETY: this is the last reference and is about to be dropped.
unsafe {
meta::drop_last_in_place(self.ptr as *mut MetaSlot);
}
}
}
}
/// Increases the reference count of the page by one. /// Increases the reference count of the page by one.
/// ///
/// # Safety /// # Safety

View File

@ -16,8 +16,8 @@ use core::mem::ManuallyDrop;
pub use segment::UntypedSegment; pub use segment::UntypedSegment;
use super::{ use super::{
meta::{impl_frame_meta_for, MetaSlot}, meta::{impl_frame_meta_for, FrameMeta, MetaSlot},
AnyFrame, Frame, Frame,
}; };
use crate::{ use crate::{
mm::{ mm::{
@ -100,14 +100,14 @@ impl From<Frame<UntypedMeta>> for UntypedFrame {
} }
} }
impl TryFrom<AnyFrame> for UntypedFrame { impl TryFrom<Frame<dyn FrameMeta>> for UntypedFrame {
type Error = AnyFrame; type Error = Frame<dyn FrameMeta>;
/// Try converting a [`AnyFrame`] into the statically-typed [`UntypedFrame`]. /// Try converting a [`Frame<dyn FrameMeta>`] into the statically-typed [`UntypedFrame`].
/// ///
/// If the dynamic page is not used as an untyped page frame, it will /// If the dynamic page is not used as an untyped page frame, it will
/// return the dynamic page itself as is. /// return the dynamic page itself as is.
fn try_from(page: AnyFrame) -> core::result::Result<Self, Self::Error> { fn try_from(page: Frame<dyn FrameMeta>) -> core::result::Result<Self, Self::Error> {
page.try_into().map(|p: Frame<UntypedMeta>| p.into()) page.try_into().map(|p: Frame<UntypedMeta>| p.into())
} }
} }

View File

@ -11,7 +11,7 @@ use super::{KERNEL_PAGE_TABLE, TRACKED_MAPPED_PAGES_RANGE, VMALLOC_VADDR_RANGE};
use crate::{ use crate::{
cpu::CpuSet, cpu::CpuSet,
mm::{ mm::{
frame::{meta::FrameMeta, AnyFrame, Frame}, frame::{meta::FrameMeta, Frame},
page_prop::PageProperty, page_prop::PageProperty,
page_table::PageTableItem, page_table::PageTableItem,
tlb::{TlbFlushOp, TlbFlusher, FLUSH_ALL_RANGE_THRESHOLD}, tlb::{TlbFlushOp, TlbFlusher, FLUSH_ALL_RANGE_THRESHOLD},
@ -232,7 +232,7 @@ impl KVirtArea<Tracked> {
/// ///
/// This function returns None if the address is not mapped (`NotMapped`), /// This function returns None if the address is not mapped (`NotMapped`),
/// while panics if the address is mapped to a `MappedUntracked` or `PageTableNode` page. /// while panics if the address is mapped to a `MappedUntracked` or `PageTableNode` page.
pub fn get_page(&self, addr: Vaddr) -> Option<AnyFrame> { pub fn get_page(&self, addr: Vaddr) -> Option<Frame<dyn FrameMeta>> {
let query_result = self.query_page(addr); let query_result = self.query_page(addr);
match query_result { match query_result {
PageTableItem::Mapped { PageTableItem::Mapped {

View File

@ -76,7 +76,9 @@ use super::{
}; };
use crate::{ use crate::{
mm::{ mm::{
frame::AnyFrame, kspace::should_map_as_tracked, paddr_to_vaddr, Paddr, PageProperty, Vaddr, frame::{meta::FrameMeta, Frame},
kspace::should_map_as_tracked,
paddr_to_vaddr, Paddr, PageProperty, Vaddr,
}, },
task::{disable_preempt, DisabledPreemptGuard}, task::{disable_preempt, DisabledPreemptGuard},
}; };
@ -89,7 +91,7 @@ pub enum PageTableItem {
}, },
Mapped { Mapped {
va: Vaddr, va: Vaddr,
page: AnyFrame, page: Frame<dyn FrameMeta>,
prop: PageProperty, prop: PageProperty,
}, },
#[allow(dead_code)] #[allow(dead_code)]
@ -400,9 +402,9 @@ where
self.0.query() self.0.query()
} }
/// Maps the range starting from the current address to a [`AnyFrame`]. /// Maps the range starting from the current address to a [`Frame<dyn FrameMeta>`].
/// ///
/// It returns the previously mapped [`AnyFrame`] if that exists. /// It returns the previously mapped [`Frame<dyn FrameMeta>`] if that exists.
/// ///
/// # Panics /// # Panics
/// ///
@ -415,7 +417,11 @@ where
/// ///
/// The caller should ensure that the virtual range being mapped does /// The caller should ensure that the virtual range being mapped does
/// not affect kernel's memory safety. /// not affect kernel's memory safety.
pub unsafe fn map(&mut self, page: AnyFrame, prop: PageProperty) -> Option<AnyFrame> { pub unsafe fn map(
&mut self,
page: Frame<dyn FrameMeta>,
prop: PageProperty,
) -> Option<Frame<dyn FrameMeta>> {
let end = self.0.va + page.size(); let end = self.0.va + page.size();
assert!(end <= self.0.barrier_va.end); assert!(end <= self.0.barrier_va.end);

View File

@ -8,7 +8,7 @@ use super::{MapTrackingStatus, PageTableEntryTrait, RawPageTableNode};
use crate::{ use crate::{
arch::mm::{PageTableEntry, PagingConsts}, arch::mm::{PageTableEntry, PagingConsts},
mm::{ mm::{
frame::{inc_page_ref_count, AnyFrame}, frame::{inc_page_ref_count, meta::FrameMeta, Frame},
page_prop::PageProperty, page_prop::PageProperty,
Paddr, PagingConstsTrait, PagingLevel, Paddr, PagingConstsTrait, PagingLevel,
}, },
@ -27,7 +27,7 @@ pub(in crate::mm) enum Child<
[(); C::NR_LEVELS as usize]:, [(); C::NR_LEVELS as usize]:,
{ {
PageTable(RawPageTableNode<E, C>), PageTable(RawPageTableNode<E, C>),
Frame(AnyFrame, PageProperty), Frame(Frame<dyn FrameMeta>, PageProperty),
/// Pages not tracked by handles. /// Pages not tracked by handles.
Untracked(Paddr, PagingLevel, PageProperty), Untracked(Paddr, PagingLevel, PageProperty),
None, None,
@ -119,7 +119,7 @@ where
match is_tracked { match is_tracked {
MapTrackingStatus::Tracked => { MapTrackingStatus::Tracked => {
// SAFETY: The physical address points to a valid page. // SAFETY: The physical address points to a valid page.
let page = unsafe { AnyFrame::from_raw(paddr) }; let page = unsafe { Frame::<dyn FrameMeta>::from_raw(paddr) };
Child::Frame(page, pte.prop()) Child::Frame(page, pte.prop())
} }
MapTrackingStatus::Untracked => Child::Untracked(paddr, level, pte.prop()), MapTrackingStatus::Untracked => Child::Untracked(paddr, level, pte.prop()),
@ -162,7 +162,7 @@ where
// the reference to the page. // the reference to the page.
unsafe { inc_page_ref_count(paddr) }; unsafe { inc_page_ref_count(paddr) };
// SAFETY: The physical address points to a valid page. // SAFETY: The physical address points to a valid page.
let page = unsafe { AnyFrame::from_raw(paddr) }; let page = unsafe { Frame::<dyn FrameMeta>::from_raw(paddr) };
Child::Frame(page, pte.prop()) Child::Frame(page, pte.prop())
} }
MapTrackingStatus::Untracked => Child::Untracked(paddr, level, pte.prop()), MapTrackingStatus::Untracked => Child::Untracked(paddr, level, pte.prop()),

View File

@ -40,7 +40,7 @@ use super::{nr_subpage_per_huge, PageTableEntryTrait};
use crate::{ use crate::{
arch::mm::{PageTableEntry, PagingConsts}, arch::mm::{PageTableEntry, PagingConsts},
mm::{ mm::{
frame::{self, inc_page_ref_count, meta::FrameMeta, AnyFrame, Frame}, frame::{self, inc_page_ref_count, meta::FrameMeta, Frame},
paddr_to_vaddr, Paddr, PagingConstsTrait, PagingLevel, PAGE_SIZE, paddr_to_vaddr, Paddr, PagingConstsTrait, PagingLevel, PAGE_SIZE,
}, },
}; };
@ -442,7 +442,7 @@ where
} else if is_tracked == MapTrackingStatus::Tracked { } else if is_tracked == MapTrackingStatus::Tracked {
// SAFETY: The PTE points to a tracked page. The ownership // SAFETY: The PTE points to a tracked page. The ownership
// of the child is transferred to the child then dropped. // of the child is transferred to the child then dropped.
drop(unsafe { AnyFrame::from_raw(paddr) }); drop(unsafe { Frame::<dyn FrameMeta>::from_raw(paddr) });
} }
} }
} }

View File

@ -5,7 +5,10 @@
use alloc::vec::Vec; use alloc::vec::Vec;
use core::ops::Range; use core::ops::Range;
use super::{frame::AnyFrame, Vaddr, PAGE_SIZE}; use super::{
frame::{meta::FrameMeta, Frame},
Vaddr, PAGE_SIZE,
};
use crate::{ use crate::{
cpu::{CpuSet, PinCurrentCpu}, cpu::{CpuSet, PinCurrentCpu},
cpu_local, cpu_local,
@ -77,7 +80,7 @@ impl<G: PinCurrentCpu> TlbFlusher<G> {
/// flushed. Otherwise if the page is recycled for other purposes, the user /// flushed. Otherwise if the page is recycled for other purposes, the user
/// space program can still access the page through the TLB entries. This /// space program can still access the page through the TLB entries. This
/// method is designed to be used in such cases. /// method is designed to be used in such cases.
pub fn issue_tlb_flush_with(&self, op: TlbFlushOp, drop_after_flush: AnyFrame) { pub fn issue_tlb_flush_with(&self, op: TlbFlushOp, drop_after_flush: Frame<dyn FrameMeta>) {
self.issue_tlb_flush_(op, Some(drop_after_flush)); self.issue_tlb_flush_(op, Some(drop_after_flush));
} }
@ -91,7 +94,7 @@ impl<G: PinCurrentCpu> TlbFlusher<G> {
self.need_self_flush self.need_self_flush
} }
fn issue_tlb_flush_(&self, op: TlbFlushOp, drop_after_flush: Option<AnyFrame>) { fn issue_tlb_flush_(&self, op: TlbFlushOp, drop_after_flush: Option<Frame<dyn FrameMeta>>) {
let op = op.optimize_for_large_range(); let op = op.optimize_for_large_range();
// Fast path for single CPU cases. // Fast path for single CPU cases.
@ -156,7 +159,7 @@ impl TlbFlushOp {
// Lock ordering: lock FLUSH_OPS before PAGE_KEEPER. // Lock ordering: lock FLUSH_OPS before PAGE_KEEPER.
cpu_local! { cpu_local! {
static FLUSH_OPS: SpinLock<OpsStack, LocalIrqDisabled> = SpinLock::new(OpsStack::new()); static FLUSH_OPS: SpinLock<OpsStack, LocalIrqDisabled> = SpinLock::new(OpsStack::new());
static PAGE_KEEPER: SpinLock<Vec<AnyFrame>, LocalIrqDisabled> = SpinLock::new(Vec::new()); static PAGE_KEEPER: SpinLock<Vec<Frame<dyn FrameMeta>>, LocalIrqDisabled> = SpinLock::new(Vec::new());
} }
fn do_remote_flush() { fn do_remote_flush() {