1use alloc::{sync::Arc, vec::Vec};
4use core::{
5 any::Any,
6 fmt,
7 hash::{Hash, Hasher},
8 sync::atomic::{AtomicBool, AtomicU8, Ordering},
9};
10
11use ax_memory_addr::{MemoryAddr, PAGE_SIZE_4K, PhysAddr, VirtAddr, VirtAddrRange};
12use ax_runtime::hal::paging::HugeSplitDeposit;
13
14use super::{AddressSpaceId, MappingId, PageOrder, RssKind};
15use crate::sync::IrqMutex;
16
17#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
18pub struct PageId(u64);
19
20impl PageId {
21 pub fn allocate() -> Self {
22 static NEXT_ID: core::sync::atomic::AtomicU64 = core::sync::atomic::AtomicU64::new(1);
23 Self(NEXT_ID.fetch_add(1, Ordering::Relaxed))
24 }
25
26 pub const fn new(value: u64) -> Self {
27 Self(value)
28 }
29
30 pub const fn get(self) -> u64 {
31 self.0
32 }
33}
34
35struct FrameAllocation {
41 base: PhysAddr,
42 bytes: usize,
43 release_align: Option<usize>,
47 release: fn(PhysAddr, usize),
52 _anchor: Option<Arc<dyn Any + Send + Sync>>,
56}
57
58impl Drop for FrameAllocation {
59 fn drop(&mut self) {
60 if let Some(align) = self.release_align.take() {
61 (self.release)(self.base, align);
62 }
63 }
64}
65
66#[derive(Clone)]
70pub struct FrameLease {
71 paddr: PhysAddr,
72 bytes: usize,
73 allocation: Arc<FrameAllocation>,
74}
75
76impl fmt::Debug for FrameLease {
77 fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
78 formatter
79 .debug_struct("FrameLease")
80 .field("paddr", &self.paddr)
81 .field("bytes", &self.bytes)
82 .field("owned", &self.allocation.release_align.is_some())
83 .field("anchored", &self.allocation._anchor.is_some())
84 .finish()
85 }
86}
87
88fn release_data_frame(paddr: PhysAddr, align: usize) {
89 super::backend::dealloc_frame(paddr, align);
90}
91
92impl FrameLease {
93 pub fn new(paddr: PhysAddr) -> Self {
94 Self {
95 paddr,
96 bytes: PAGE_SIZE_4K,
97 allocation: Arc::new(FrameAllocation {
98 base: paddr,
99 bytes: PAGE_SIZE_4K,
100 release_align: None,
101 release: release_data_frame,
102 _anchor: None,
103 }),
104 }
105 }
106
107 pub fn borrowed(
110 paddr: PhysAddr,
111 bytes: usize,
112 anchor: Option<Arc<dyn Any + Send + Sync>>,
113 ) -> Option<Self> {
114 if bytes == 0 {
115 return None;
116 }
117 Some(Self {
118 paddr,
119 bytes,
120 allocation: Arc::new(FrameAllocation {
121 base: paddr,
122 bytes,
123 release_align: None,
124 release: release_data_frame,
125 _anchor: anchor,
126 }),
127 })
128 }
129
130 pub unsafe fn owned(paddr: PhysAddr, align: usize) -> Self {
146 Self {
147 paddr,
148 bytes: align,
149 allocation: Arc::new(FrameAllocation {
150 base: paddr,
151 bytes: align,
152 release_align: Some(align),
153 release: release_data_frame,
154 _anchor: None,
155 }),
156 }
157 }
158
159 pub fn owned_with_releaser(
164 paddr: PhysAddr,
165 align: usize,
166 release: fn(PhysAddr, usize),
167 ) -> Self {
168 Self {
169 paddr,
170 bytes: align,
171 allocation: Arc::new(FrameAllocation {
172 base: paddr,
173 bytes: align,
174 release_align: Some(align),
175 release,
176 _anchor: None,
177 }),
178 }
179 }
180
181 pub const fn paddr(&self) -> PhysAddr {
182 self.paddr
183 }
184
185 pub const fn size(&self) -> usize {
186 self.bytes
187 }
188
189 pub fn sublease(&self, offset: usize, bytes: usize) -> Option<Self> {
193 if bytes == 0 {
194 return None;
195 }
196 let end = offset.checked_add(bytes)?;
197 if end > self.bytes {
198 return None;
199 }
200 let paddr = self.paddr.as_usize().checked_add(offset)?;
201 let allocation_offset = paddr.checked_sub(self.allocation.base.as_usize())?;
202 if allocation_offset.checked_add(bytes)? > self.allocation.bytes {
203 return None;
204 }
205 Some(Self {
206 paddr: PhysAddr::from_usize(paddr),
207 bytes,
208 allocation: self.allocation.clone(),
209 })
210 }
211}
212
213#[repr(u8)]
214#[derive(Debug, Clone, Copy, PartialEq, Eq)]
215pub enum PageState {
216 Reserved = 0,
217 Present = 1,
218 LazyFree = 2,
220 Evicting = 3,
221 Writeback = 4,
222 Retired = 5,
223}
224
225impl PageState {
226 fn from_u8(value: u8) -> Self {
227 match value {
228 0 => Self::Reserved,
229 1 => Self::Present,
230 2 => Self::LazyFree,
231 3 => Self::Evicting,
232 4 => Self::Writeback,
233 _ => Self::Retired,
234 }
235 }
236}
237
238#[derive(Debug, Default)]
241pub struct RmapSet {
242 entries: IrqMutex<RmapEntries>,
243}
244
245#[derive(Debug, Default)]
246struct RmapEntries {
247 keys: Vec<MappingSlotKey>,
248 reserved: usize,
250}
251
252#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord)]
253pub struct MappingSlotKey {
254 pub space_id: AddressSpaceId,
255 pub va: VirtAddr,
256}
257
258pub(crate) struct MappingGraphReservation<'a> {
265 owner: &'a RmapSet,
266 additional: usize,
267 displaced: Vec<MappingSlotKey>,
268}
269
270impl Drop for MappingGraphReservation<'_> {
271 fn drop(&mut self) {
272 if self.additional != 0 {
273 let mut entries = self.owner.entries.lock();
274 entries.reserved -= self.additional;
275 }
276 drop(core::mem::take(&mut self.displaced));
278 }
279}
280
281impl Hash for MappingSlotKey {
282 fn hash<H: Hasher>(&self, state: &mut H) {
283 self.space_id.hash(state);
284 self.va.as_usize().hash(state);
285 }
286}
287
288impl RmapSet {
289 fn prepare_replace(
290 &self,
291 additional: usize,
292 ) -> Result<MappingGraphReservation<'_>, MappingGraphError> {
293 let mut replacement = Vec::new();
294 loop {
295 let mut entries = self.entries.lock();
296 let reserved = entries
297 .reserved
298 .checked_add(additional)
299 .ok_or(MappingGraphError::ResourceExhausted)?;
300 let required = entries
301 .keys
302 .len()
303 .checked_add(reserved)
304 .ok_or(MappingGraphError::ResourceExhausted)?;
305 if entries.keys.capacity() < required {
306 if replacement.capacity() < required {
307 drop(entries);
308 replacement
309 .try_reserve_exact(required)
310 .map_err(|_| MappingGraphError::ResourceExhausted)?;
311 continue;
312 }
313 replacement.extend_from_slice(&entries.keys);
314 core::mem::swap(&mut entries.keys, &mut replacement);
315 }
316 entries.reserved = reserved;
317 return Ok(MappingGraphReservation {
318 owner: self,
319 additional,
320 displaced: replacement,
321 });
322 }
323 }
324
325 fn replace_reserved(
328 &self,
329 old: &[MappingSlotKey],
330 new: &[MappingSlotKey],
331 reservation: &mut MappingGraphReservation<'_>,
332 ) -> Result<(), MappingGraphError> {
333 if !core::ptr::eq(reservation.owner, self) {
334 return Err(MappingGraphError::SlotIdentityMismatch);
335 }
336 if new.len().saturating_sub(old.len()) > reservation.additional {
337 return Err(MappingGraphError::SlotStateConflict);
338 }
339 let mut entries = self.entries.lock();
340 for (index, key) in old.iter().enumerate() {
341 if old[..index].contains(key) || !entries.keys.contains(key) {
342 return Err(MappingGraphError::MissingOldSlot);
343 }
344 }
345 for (index, key) in new.iter().enumerate() {
346 if new[..index].contains(key)
347 || (entries.keys.contains(key) && !old.contains(key))
348 {
349 return Err(MappingGraphError::DuplicateNewSlot);
350 }
351 }
352 for key in old {
353 let index = entries
354 .keys
355 .iter()
356 .position(|entry| entry == key)
357 .ok_or(MappingGraphError::MissingOldSlot)?;
358 entries.keys.swap_remove(index);
359 }
360 entries.keys.extend_from_slice(new);
361 entries.reserved -= reservation.additional;
362 reservation.additional = 0;
363 Ok(())
364 }
365
366 pub fn try_snapshot(&self) -> Result<Vec<MappingSlotKey>, MappingGraphError> {
367 let mut snapshot = Vec::new();
368 loop {
369 let required = self.entries.lock().keys.len();
370 if snapshot.capacity() < required {
371 snapshot
372 .try_reserve_exact(required.saturating_sub(snapshot.len()))
373 .map_err(|_| MappingGraphError::ResourceExhausted)?;
374 }
375 let entries = self.entries.lock();
376 if entries.keys.len() > snapshot.capacity() {
377 drop(entries);
378 continue;
379 }
380 snapshot.clear();
381 snapshot.extend_from_slice(&entries.keys);
382 return Ok(snapshot);
383 }
384 }
385
386 #[cfg(test)]
387 pub fn snapshot(&self) -> Vec<MappingSlotKey> {
388 self.try_snapshot()
389 .expect("test reverse-mapping snapshot allocation must succeed")
390 }
391
392 fn all_mappings_belong_to(&self, mm_id: AddressSpaceId, expected: u32) -> bool {
393 let entries = self.entries.lock();
394 usize::try_from(expected).is_ok_and(|expected| entries.keys.len() == expected)
395 && entries.keys.iter().all(|entry| entry.space_id == mm_id)
396 }
397
398 pub fn is_empty(&self) -> bool {
399 self.entries.lock().keys.is_empty()
400 }
401}
402
403pub struct PageObject {
404 pub id: PageId,
405 frame: FrameLease,
406 state: AtomicU8,
407 resident_kind: AtomicU8,
412 mapping_refs: core::sync::atomic::AtomicU32,
416 writeback_generation: core::sync::atomic::AtomicU64,
417 eviction_tlb_ready: AtomicBool,
421 mapping_graph: IrqMutex<()>,
426 pub rmap: RmapSet,
427}
428
429impl PageObject {
430 pub fn new(id: PageId, frame: FrameLease) -> Arc<Self> {
431 Self::new_with_resident_kind(id, frame, None)
432 }
433
434 pub fn new_with_resident_kind(
435 id: PageId,
436 frame: FrameLease,
437 resident_kind: Option<RssKind>,
438 ) -> Arc<Self> {
439 Arc::new(Self {
440 id,
441 frame,
442 state: AtomicU8::new(PageState::Reserved as u8),
443 resident_kind: AtomicU8::new(RssKind::slot_value(resident_kind)),
444 mapping_refs: core::sync::atomic::AtomicU32::new(0),
445 writeback_generation: core::sync::atomic::AtomicU64::new(0),
446 eviction_tlb_ready: AtomicBool::new(false),
447 mapping_graph: IrqMutex::new(()),
448 rmap: RmapSet::default(),
449 })
450 }
451
452 pub fn new_present(id: PageId, frame: FrameLease) -> Arc<Self> {
453 Self::new_present_with_resident_kind(id, frame, None)
454 }
455
456 pub fn new_present_with_resident_kind(
457 id: PageId,
458 frame: FrameLease,
459 resident_kind: Option<RssKind>,
460 ) -> Arc<Self> {
461 let page = Self::new_with_resident_kind(id, frame, resident_kind);
462 let _ = page.transition(PageState::Reserved, PageState::Present);
466 page
467 }
468
469 pub fn state(&self) -> PageState {
470 PageState::from_u8(self.state.load(Ordering::Acquire))
471 }
472
473 pub fn frame(&self) -> &FrameLease {
474 &self.frame
475 }
476
477 pub fn resident_kind(&self) -> Option<RssKind> {
478 RssKind::from_slot_value(self.resident_kind.load(Ordering::Acquire))
479 }
480
481 pub(crate) fn set_resident_kind(&self, kind: Option<RssKind>) {
482 self.resident_kind
483 .store(RssKind::slot_value(kind), Ordering::Release);
484 }
485
486 pub fn mapping_refs(&self) -> u32 {
487 self.mapping_refs.load(Ordering::Acquire)
488 }
489
490 pub(crate) fn exclusively_mapped_by(&self, mm_id: AddressSpaceId) -> bool {
498 let _graph = self.mapping_graph.lock();
499 if !matches!(self.state(), PageState::Present | PageState::LazyFree) {
500 return false;
501 }
502 let mappings = self.mapping_refs.load(Ordering::Acquire);
503 mappings != 0 && self.rmap.all_mappings_belong_to(mm_id, mappings)
504 }
505
506 fn publish_slot_graph(&self, key: MappingSlotKey) -> bool {
507 let Ok(mut reservation) = self.rmap.prepare_replace(1) else {
508 return false;
509 };
510 let published = {
511 let _graph = self.mapping_graph.lock();
512 if !matches!(self.state(), PageState::Present | PageState::LazyFree) {
513 false
514 } else {
515 let current = self.mapping_refs.load(Ordering::Acquire);
516 let Some(next) = current.checked_add(1) else {
517 return false;
518 };
519 match self.rmap.replace_reserved(&[], &[key], &mut reservation) {
520 Err(_) => false,
521 Ok(()) if !matches!(self.state(), PageState::Present | PageState::LazyFree) => {
522 let _ = self.rmap.replace_reserved(&[key], &[], &mut reservation);
523 false
524 }
525 Ok(()) => {
526 self.mapping_refs.store(next, Ordering::Release);
527 true
528 }
529 }
530 }
531 };
532 drop(reservation);
535 published
536 }
537
538 fn detach_slot_graph(&self, key: MappingSlotKey) -> bool {
539 let Ok(mut reservation) = self.rmap.prepare_replace(0) else {
540 return false;
541 };
542 let (detached, became_exclusive) = {
543 let _graph = self.mapping_graph.lock();
544 let current = self.mapping_refs.load(Ordering::Acquire);
545 let Some(next) = current.checked_sub(1) else {
546 return false;
547 };
548 if self
549 .rmap
550 .replace_reserved(&[key], &[], &mut reservation)
551 .is_err()
552 {
553 (false, false)
554 } else {
555 self.mapping_refs.store(next, Ordering::Release);
556 (true, current > 1 && next == 1)
557 }
558 };
559 drop(reservation);
560 if became_exclusive && self.state() == PageState::LazyFree {
561 super::lifecycle::request_lazy_free_reclaim();
566 }
567 detached
568 }
569
570 pub(crate) fn prepare_mapping_graph_replace(
571 &self,
572 old: &[MappingSlotKey],
573 new: &[MappingSlotKey],
574 ) -> Result<MappingGraphReservation<'_>, MappingGraphError> {
575 self.rmap
576 .prepare_replace(new.len().saturating_sub(old.len()))
577 }
578
579 pub(crate) fn replace_mapping_graph_reserved(
580 &self,
581 old: &[MappingSlotKey],
582 new: &[MappingSlotKey],
583 reservation: &mut MappingGraphReservation<'_>,
584 ) -> Result<(), MappingGraphError> {
585 let became_exclusive = {
586 let _graph = self.mapping_graph.lock();
587 if !matches!(self.state(), PageState::Present | PageState::LazyFree) {
588 return Err(MappingGraphError::PageNotPresent);
589 }
590 let current = self.mapping_refs.load(Ordering::Acquire);
591 let next = if new.len() >= old.len() {
592 let additional = u32::try_from(new.len() - old.len())
593 .map_err(|_| MappingGraphError::RefOverflow)?;
594 current
595 .checked_add(additional)
596 .ok_or(MappingGraphError::RefOverflow)?
597 } else {
598 let removed = u32::try_from(old.len() - new.len())
599 .map_err(|_| MappingGraphError::RefUnderflow)?;
600 current
601 .checked_sub(removed)
602 .ok_or(MappingGraphError::RefUnderflow)?
603 };
604 self.rmap.replace_reserved(old, new, reservation)?;
605 self.mapping_refs.store(next, Ordering::Release);
606 current > 1 && next == 1
607 };
608 if became_exclusive && self.state() == PageState::LazyFree {
609 super::lifecycle::request_lazy_free_reclaim();
610 }
611 Ok(())
612 }
613
614 pub(crate) fn replace_mapping_graph(
618 &self,
619 old: &[MappingSlotKey],
620 new: &[MappingSlotKey],
621 ) -> Result<(), MappingGraphError> {
622 let mut reservation = self.prepare_mapping_graph_replace(old, new)?;
623 let result = self.replace_mapping_graph_reserved(old, new, &mut reservation);
624 drop(reservation);
625 result
626 }
627
628 pub(crate) fn transition(&self, expected: PageState, next: PageState) -> bool {
629 let valid = matches!(
630 (expected, next),
631 (PageState::Reserved, PageState::Present)
632 | (PageState::Present, PageState::LazyFree)
633 | (PageState::LazyFree, PageState::Present)
634 | (PageState::LazyFree, PageState::Evicting)
635 | (PageState::LazyFree, PageState::Retired)
636 | (PageState::Present, PageState::Evicting)
637 | (PageState::Present, PageState::Writeback)
638 | (PageState::Evicting, PageState::Present)
639 | (PageState::Evicting, PageState::Retired)
640 | (PageState::Writeback, PageState::Present)
641 | (PageState::Writeback, PageState::Retired)
642 );
643 valid
644 && self
645 .state
646 .compare_exchange(
647 expected as u8,
648 next as u8,
649 Ordering::AcqRel,
650 Ordering::Acquire,
651 )
652 .is_ok()
653 }
654
655 pub(crate) fn mark_lazy_free(&self) -> bool {
656 self.transition(PageState::Present, PageState::LazyFree)
657 }
658
659 pub(crate) fn clear_lazy_free(&self) -> bool {
660 self.transition(PageState::LazyFree, PageState::Present)
661 }
662
663 pub(crate) fn eviction_lease(self: &Arc<Self>) -> Result<EvictionLease, EvictionError> {
668 let _graph = self.mapping_graph.lock();
669 if !self.transition(PageState::Present, PageState::Evicting) {
670 return Err(EvictionError::NotPresent);
671 }
672 self.eviction_tlb_ready.store(false, Ordering::Release);
673 Ok(EvictionLease { page: self.clone() })
674 }
675
676 pub(crate) fn complete_eviction_tlb(&self) {
679 if self.state() == PageState::Evicting {
680 self.eviction_tlb_ready.store(true, Ordering::Release);
681 }
682 }
683
684 pub(crate) fn resume_eviction_lease(self: &Arc<Self>) -> Result<EvictionLease, EvictionError> {
688 if self.state() != PageState::Evicting
689 || self
690 .eviction_tlb_ready
691 .compare_exchange(true, false, Ordering::AcqRel, Ordering::Acquire)
692 .is_err()
693 {
694 return Err(EvictionError::Busy);
695 }
696 Ok(EvictionLease { page: self.clone() })
697 }
698
699 pub(crate) fn writeback_lease(self: &Arc<Self>) -> Result<WritebackLease, WritebackError> {
703 let _graph = self.mapping_graph.lock();
704 if !self.transition(PageState::Present, PageState::Writeback) {
705 return Err(WritebackError::Busy);
706 }
707 let generation = match self.writeback_generation.try_update(
708 Ordering::AcqRel,
709 Ordering::Acquire,
710 |current| current.checked_add(1),
711 ) {
712 Ok(previous) => previous + 1,
713 Err(_) => {
714 let _ = self.transition(PageState::Writeback, PageState::Present);
715 return Err(WritebackError::GenerationExhausted);
716 }
717 };
718 Ok(WritebackLease {
719 page: self.clone(),
720 generation,
721 })
722 }
723}
724
725#[derive(Debug, Clone, Copy, PartialEq, Eq)]
726pub enum MappingGraphError {
727 PageNotPresent,
728 MissingOldSlot,
729 DuplicateNewSlot,
730 ResourceExhausted,
731 RefOverflow,
732 RefUnderflow,
733 SlotStateConflict,
734 SlotIdentityMismatch,
735 RollbackFailed,
736}
737
738#[derive(Debug, Clone, Copy, PartialEq, Eq)]
739pub enum EvictionError {
740 NotPresent,
741 Busy,
742}
743
744#[derive(Debug, Clone, Copy, PartialEq, Eq)]
745pub enum WritebackError {
746 Busy,
747 GenerationExhausted,
748}
749
750pub struct EvictionLease {
752 page: Arc<PageObject>,
753}
754
755impl EvictionLease {
756 pub fn page(&self) -> &Arc<PageObject> {
757 &self.page
758 }
759
760 pub(crate) fn cancel(self) -> bool {
761 self.page.eviction_tlb_ready.store(false, Ordering::Release);
762 self.page
763 .transition(PageState::Evicting, PageState::Present)
764 }
765
766 pub(crate) fn retire(self) -> Result<(), (Self, EvictionError)> {
767 if !self.page.rmap.is_empty() || self.page.mapping_refs() != 0 {
768 return Err((self, EvictionError::Busy));
769 }
770 if self
771 .page
772 .transition(PageState::Evicting, PageState::Retired)
773 {
774 Ok(())
775 } else {
776 Err((self, EvictionError::Busy))
777 }
778 }
779}
780
781pub struct WritebackLease {
783 page: Arc<PageObject>,
784 generation: u64,
785}
786
787impl WritebackLease {
788 pub const fn generation(&self) -> u64 {
789 self.generation
790 }
791
792 pub(crate) fn cancel(self) -> bool {
793 self.page
794 .transition(PageState::Writeback, PageState::Present)
795 }
796
797 pub(crate) fn complete(self) -> Result<u64, (Self, WritebackError)> {
798 if self
799 .page
800 .transition(PageState::Writeback, PageState::Present)
801 {
802 Ok(self.generation)
803 } else {
804 Err((self, WritebackError::Busy))
805 }
806 }
807}
808
809#[repr(u8)]
810#[derive(Debug, Clone, Copy, PartialEq, Eq)]
811pub enum SlotState {
812 Reserved = 0,
813 Present = 1,
814 Detached = 2,
815}
816
817impl SlotState {
818 fn from_u8(value: u8) -> Self {
819 match value {
820 0 => Self::Reserved,
821 1 => Self::Present,
822 _ => Self::Detached,
823 }
824 }
825}
826
827pub struct MappingSlot {
829 pub mapping: MappingId,
830 pub mm_id: AddressSpaceId,
831 pub va: VirtAddr,
832 pub page_order: PageOrder,
833 pub page: Arc<PageObject>,
834 frame_offset: usize,
841 resident_kind: AtomicU8,
846 huge_split_deposit: IrqMutex<Option<HugeSplitDeposit>>,
851 state: AtomicU8,
852}
853
854impl MappingSlot {
855 #[cfg(test)]
856 pub(crate) fn new(
857 mapping: MappingId,
858 mm_id: AddressSpaceId,
859 va: VirtAddr,
860 page_order: PageOrder,
861 page: Arc<PageObject>,
862 resident_kind: Option<RssKind>,
863 ) -> Self {
864 Self {
865 mapping,
866 mm_id,
867 va,
868 page_order,
869 page,
870 frame_offset: 0,
871 resident_kind: AtomicU8::new(RssKind::slot_value(resident_kind)),
872 huge_split_deposit: IrqMutex::new(None),
873 state: AtomicU8::new(SlotState::Reserved as u8),
874 }
875 }
876
877 pub(crate) fn new_with_frame_offset(
878 mapping: MappingId,
879 mm_id: AddressSpaceId,
880 va: VirtAddr,
881 page_order: PageOrder,
882 page: Arc<PageObject>,
883 frame_offset: usize,
884 resident_kind: Option<RssKind>,
885 ) -> Option<Self> {
886 let bytes = PAGE_SIZE_4K.checked_shl(page_order.get().into())?;
887 let end = frame_offset.checked_add(bytes)?;
888 if !frame_offset.is_multiple_of(PAGE_SIZE_4K) || end > page.frame().size() {
889 return None;
890 }
891 Some(Self {
892 mapping,
893 mm_id,
894 va,
895 page_order,
896 page,
897 frame_offset,
898 resident_kind: AtomicU8::new(RssKind::slot_value(resident_kind)),
899 huge_split_deposit: IrqMutex::new(None),
900 state: AtomicU8::new(SlotState::Reserved as u8),
901 })
902 }
903
904 pub(crate) const fn frame_offset(&self) -> usize {
905 self.frame_offset
906 }
907
908 pub(crate) fn mapped_paddr(&self) -> Option<PhysAddr> {
909 self.page.frame().paddr().checked_add(self.frame_offset)
910 }
911
912 pub(crate) fn attach_huge_split_deposit(
916 self,
917 deposit: HugeSplitDeposit,
918 ) -> Result<Self, HugeSplitDeposit> {
919 {
920 let mut owner = self.huge_split_deposit.lock();
921 if owner.is_some() {
922 return Err(deposit);
923 }
924 *owner = Some(deposit);
925 }
926 Ok(self)
927 }
928
929 pub(crate) fn has_huge_split_deposit(&self) -> bool {
930 self.huge_split_deposit.lock().is_some()
931 }
932
933 pub(crate) fn take_huge_split_deposit(&self) -> Option<HugeSplitDeposit> {
934 self.huge_split_deposit.lock().take()
935 }
936
937 pub(crate) fn restore_huge_split_deposit(
938 &self,
939 deposit: HugeSplitDeposit,
940 ) -> Result<(), HugeSplitDeposit> {
941 let mut owner = self.huge_split_deposit.lock();
942 if owner.is_some() {
943 return Err(deposit);
944 }
945 *owner = Some(deposit);
946 Ok(())
947 }
948
949 pub(crate) fn overlaps(&self, range: VirtAddrRange) -> bool {
950 let Some(bytes) = PAGE_SIZE_4K.checked_shl(self.page_order.get().into()) else {
951 return false;
952 };
953 let Some(end) = self.va.checked_add(bytes) else {
954 return false;
955 };
956 self.va < range.end && range.start < end
957 }
958
959 pub fn state(&self) -> SlotState {
960 SlotState::from_u8(self.state.load(Ordering::Acquire))
961 }
962
963 pub fn resident_kind(&self) -> Option<RssKind> {
964 RssKind::from_slot_value(self.resident_kind.load(Ordering::Acquire))
965 }
966
967 pub(crate) fn set_resident_kind(&self, kind: Option<RssKind>) {
972 self.resident_kind
973 .store(RssKind::slot_value(kind), Ordering::Release);
974 }
975
976 pub(crate) fn publish(&self) -> bool {
977 if self
978 .state
979 .compare_exchange(
980 SlotState::Reserved as u8,
981 SlotState::Present as u8,
982 Ordering::AcqRel,
983 Ordering::Acquire,
984 )
985 .is_err()
986 {
987 return false;
988 }
989 if self.page.publish_slot_graph(MappingSlotKey {
990 space_id: self.mm_id,
991 va: self.va,
992 }) {
993 true
994 } else {
995 let _ = self.state.compare_exchange(
996 SlotState::Present as u8,
997 SlotState::Reserved as u8,
998 Ordering::AcqRel,
999 Ordering::Acquire,
1000 );
1001 false
1002 }
1003 }
1004
1005 pub(crate) fn detach(&self) -> bool {
1006 self.detach_slot()
1007 }
1008
1009 pub(crate) fn restore(&self) -> bool {
1014 if self
1015 .state
1016 .compare_exchange(
1017 SlotState::Detached as u8,
1018 SlotState::Present as u8,
1019 Ordering::AcqRel,
1020 Ordering::Acquire,
1021 )
1022 .is_err()
1023 {
1024 return false;
1025 }
1026 if self.page.publish_slot_graph(MappingSlotKey {
1027 space_id: self.mm_id,
1028 va: self.va,
1029 }) {
1030 true
1031 } else {
1032 let _ = self.state.compare_exchange(
1033 SlotState::Present as u8,
1034 SlotState::Detached as u8,
1035 Ordering::AcqRel,
1036 Ordering::Acquire,
1037 );
1038 false
1039 }
1040 }
1041
1042 fn detach_slot(&self) -> bool {
1043 if self
1044 .state
1045 .compare_exchange(
1046 SlotState::Present as u8,
1047 SlotState::Detached as u8,
1048 Ordering::AcqRel,
1049 Ordering::Acquire,
1050 )
1051 .is_err()
1052 {
1053 return false;
1054 }
1055 if !self.page.detach_slot_graph(MappingSlotKey {
1056 space_id: self.mm_id,
1057 va: self.va,
1058 }) {
1059 let _ = self.state.compare_exchange(
1060 SlotState::Detached as u8,
1061 SlotState::Present as u8,
1062 Ordering::AcqRel,
1063 Ordering::Acquire,
1064 );
1065 return false;
1066 }
1067 true
1068 }
1069
1070 pub(crate) fn publish_after_graph_replace(&self) -> bool {
1071 self.state
1072 .compare_exchange(
1073 SlotState::Reserved as u8,
1074 SlotState::Present as u8,
1075 Ordering::AcqRel,
1076 Ordering::Acquire,
1077 )
1078 .is_ok()
1079 }
1080
1081 pub(crate) fn detach_after_graph_replace(&self) -> bool {
1082 self.state
1083 .compare_exchange(
1084 SlotState::Present as u8,
1085 SlotState::Detached as u8,
1086 Ordering::AcqRel,
1087 Ordering::Acquire,
1088 )
1089 .is_ok()
1090 }
1091
1092 pub(crate) fn relocate_to(&self, replacement: &Self) -> Result<(), MappingGraphError> {
1101 if self.state() != SlotState::Present || replacement.state() != SlotState::Reserved {
1102 return Err(MappingGraphError::SlotStateConflict);
1103 }
1104 if self.mm_id != replacement.mm_id
1105 || self.page_order != replacement.page_order
1106 || !Arc::ptr_eq(&self.page, &replacement.page)
1107 {
1108 return Err(MappingGraphError::SlotIdentityMismatch);
1109 }
1110
1111 let old_key = MappingSlotKey {
1112 space_id: self.mm_id,
1113 va: self.va,
1114 };
1115 let new_key = MappingSlotKey {
1116 space_id: replacement.mm_id,
1117 va: replacement.va,
1118 };
1119 if old_key == new_key {
1120 return Err(MappingGraphError::DuplicateNewSlot);
1121 }
1122
1123 self.page.replace_mapping_graph(&[old_key], &[new_key])?;
1124 if !replacement.publish_after_graph_replace() {
1125 if self
1126 .page
1127 .replace_mapping_graph(&[new_key], &[old_key])
1128 .is_err()
1129 {
1130 return Err(MappingGraphError::RollbackFailed);
1131 }
1132 return Err(MappingGraphError::SlotStateConflict);
1133 }
1134 if self.detach_after_graph_replace() {
1135 return Ok(());
1136 }
1137
1138 let replacement_reserved = replacement.reserve_after_graph_replace();
1139 let graph_restored = replacement_reserved
1140 && self
1141 .page
1142 .replace_mapping_graph(&[new_key], &[old_key])
1143 .is_ok();
1144 if !replacement_reserved || !graph_restored {
1145 return Err(MappingGraphError::RollbackFailed);
1146 }
1147 Err(MappingGraphError::SlotStateConflict)
1148 }
1149
1150 pub(crate) fn restore_after_graph_replace(&self) -> bool {
1151 self.state
1152 .compare_exchange(
1153 SlotState::Detached as u8,
1154 SlotState::Present as u8,
1155 Ordering::AcqRel,
1156 Ordering::Acquire,
1157 )
1158 .is_ok()
1159 }
1160
1161 pub(crate) fn reserve_after_graph_replace(&self) -> bool {
1162 self.state
1163 .compare_exchange(
1164 SlotState::Present as u8,
1165 SlotState::Reserved as u8,
1166 Ordering::AcqRel,
1167 Ordering::Acquire,
1168 )
1169 .is_ok()
1170 }
1171}
1172
1173#[cfg(test)]
1174mod tests {
1175 #[cfg(not(axtest))]
1176 use core::sync::atomic::AtomicUsize;
1177
1178 use super::*;
1179
1180 #[cfg(not(axtest))]
1181 static RELEASES: AtomicUsize = AtomicUsize::new(0);
1182
1183 #[cfg(not(axtest))]
1184 fn record_release(_paddr: PhysAddr, bytes: usize) {
1185 assert_eq!(bytes, PAGE_SIZE_4K * 4);
1186 RELEASES.fetch_add(1, Ordering::Relaxed);
1187 }
1188
1189 #[test]
1190 fn shared_page_tracks_slots_until_detach() {
1191 let page = PageObject::new(
1192 PageId::new(1),
1193 FrameLease::new(PhysAddr::from_usize(0x1000)),
1194 );
1195 assert!(page.transition(PageState::Reserved, PageState::Present));
1196 let id = AddressSpaceId::allocate();
1197 let slot_a = MappingSlot::new(
1198 MappingId::new(1),
1199 id,
1200 VirtAddr::from_usize(0x4000),
1201 PageOrder::BASE,
1202 page.clone(),
1203 Some(RssKind::Anon),
1204 );
1205 let slot_b = MappingSlot::new(
1206 MappingId::new(1),
1207 id,
1208 VirtAddr::from_usize(0x5000),
1209 PageOrder::BASE,
1210 page.clone(),
1211 Some(RssKind::Anon),
1212 );
1213 assert!(slot_a.publish());
1214 assert!(slot_b.publish());
1215 assert_eq!(page.rmap.snapshot().len(), 2);
1216 assert!(slot_a.detach());
1217 assert_eq!(page.rmap.snapshot().len(), 1);
1218 assert!(slot_b.detach());
1219 assert!(page.rmap.is_empty());
1220 }
1221
1222 #[cfg_attr(axtest, axtest::axtest)]
1223 #[cfg_attr(not(axtest), test)]
1224 fn concurrent_mapping_publication_preserves_reserved_capacity() {
1225 let page = PageObject::new_present(
1226 PageId::new(7),
1227 FrameLease::new(PhysAddr::from_usize(0x1000)),
1228 );
1229 let first = MappingSlotKey {
1230 space_id: AddressSpaceId::allocate(),
1231 va: VirtAddr::from_usize(0x4000),
1232 };
1233 let second = MappingSlotKey {
1234 space_id: AddressSpaceId::allocate(),
1235 va: first.va,
1236 };
1237 let mut first_reservation = page.prepare_mapping_graph_replace(&[], &[first]).unwrap();
1240 page.replace_mapping_graph(&[], &[second]).unwrap();
1241 page.replace_mapping_graph_reserved(&[], &[first], &mut first_reservation)
1242 .unwrap();
1243 drop(first_reservation);
1244 assert_eq!(page.mapping_refs(), 2);
1245 let mappings = page.rmap.snapshot();
1246 assert_eq!(mappings.len(), 2);
1247 assert!(mappings.contains(&first));
1248 assert!(mappings.contains(&second));
1249 let cancelled = page.prepare_mapping_graph_replace(&[], &[first]).unwrap();
1250 drop(cancelled);
1251 assert_eq!(page.rmap.entries.lock().reserved, 0);
1252 assert_eq!(page.mapping_refs(), 2);
1253 page.replace_mapping_graph(&[first, second], &[]).unwrap();
1254 assert_eq!(page.mapping_refs(), 0);
1255 assert!(page.rmap.is_empty());
1256 }
1257
1258 #[cfg_attr(axtest, axtest::axtest)]
1259 #[cfg_attr(not(axtest), test)]
1260 fn lazy_free_page_requeues_when_a_shared_mapping_becomes_exclusive() {
1261 let page = PageObject::new_present(
1262 PageId::new(5),
1263 FrameLease::new(PhysAddr::from_usize(0xa000)),
1264 );
1265 let mapping = MappingId::new(5);
1266 let parent = MappingSlot::new(
1267 mapping,
1268 AddressSpaceId::allocate(),
1269 VirtAddr::from_usize(0xa000),
1270 PageOrder::BASE,
1271 page.clone(),
1272 Some(RssKind::Anon),
1273 );
1274 let child = MappingSlot::new(
1275 mapping,
1276 AddressSpaceId::allocate(),
1277 VirtAddr::from_usize(0xb000),
1278 PageOrder::BASE,
1279 page.clone(),
1280 Some(RssKind::Anon),
1281 );
1282 assert!(parent.publish());
1283 assert!(child.publish());
1284 assert!(page.mark_lazy_free());
1285 assert_eq!(page.mapping_refs(), 2);
1286
1287 let requests = super::super::lifecycle::lazy_free_reclaim_request_count_for_test();
1288 assert!(child.detach());
1289 assert_eq!(page.mapping_refs(), 1);
1290 assert!(
1291 super::super::lifecycle::lazy_free_reclaim_request_count_for_test() > requests,
1292 "the 2 -> 1 mapping transition must publish a new reclaim edge"
1293 );
1294 assert!(parent.detach());
1295 }
1296
1297 #[cfg_attr(axtest, axtest::axtest)]
1298 #[cfg_attr(not(axtest), test)]
1299 fn lazy_free_page_requeues_after_a_batch_graph_replacement() {
1300 let page = PageObject::new_present(
1301 PageId::new(6),
1302 FrameLease::new(PhysAddr::from_usize(0xc000)),
1303 );
1304 let first = MappingSlotKey {
1305 space_id: AddressSpaceId::allocate(),
1306 va: VirtAddr::from_usize(0xc000),
1307 };
1308 let second = MappingSlotKey {
1309 space_id: AddressSpaceId::allocate(),
1310 va: VirtAddr::from_usize(0xd000),
1311 };
1312 let replacement = MappingSlotKey {
1313 space_id: first.space_id,
1314 va: VirtAddr::from_usize(0xe000),
1315 };
1316 page.replace_mapping_graph(&[], &[first, second]).unwrap();
1317 assert!(page.mark_lazy_free());
1318 assert_eq!(page.mapping_refs(), 2);
1319
1320 let requests = super::super::lifecycle::lazy_free_reclaim_request_count_for_test();
1321 let mut reservation = page
1322 .prepare_mapping_graph_replace(&[first, second], &[replacement])
1323 .unwrap();
1324 page.replace_mapping_graph_reserved(&[first, second], &[replacement], &mut reservation)
1325 .unwrap();
1326 drop(reservation);
1327
1328 assert_eq!(page.mapping_refs(), 1);
1329 assert!(
1330 super::super::lifecycle::lazy_free_reclaim_request_count_for_test() > requests,
1331 "a batch graph replacement ending at one mapping must publish a reclaim edge"
1332 );
1333 page.replace_mapping_graph(&[replacement], &[]).unwrap();
1334 }
1335
1336 #[test]
1337 fn reserved_page_cannot_publish_a_mapping_slot() {
1338 let page = PageObject::new(
1339 PageId::new(2),
1340 FrameLease::new(PhysAddr::from_usize(0x2000)),
1341 );
1342 let slot = MappingSlot::new(
1343 MappingId::new(2),
1344 AddressSpaceId::allocate(),
1345 VirtAddr::from_usize(0x6000),
1346 PageOrder::BASE,
1347 page,
1348 None,
1349 );
1350 assert!(!slot.publish());
1351 assert_eq!(slot.state(), SlotState::Reserved);
1352 }
1353
1354 #[cfg_attr(axtest, axtest::axtest)]
1355 #[cfg_attr(not(axtest), test)]
1356 fn relocation_replaces_rmap_key_without_changing_mapping_refs() {
1357 let page = PageObject::new_present(
1358 PageId::new(4),
1359 FrameLease::new(PhysAddr::from_usize(0x4000)),
1360 );
1361 let mm_id = AddressSpaceId::allocate();
1362 let old_va = VirtAddr::from_usize(0x8000);
1363 let new_va = VirtAddr::from_usize(0x9000);
1364 let old = MappingSlot::new(
1365 MappingId::new(4),
1366 mm_id,
1367 old_va,
1368 PageOrder::BASE,
1369 page.clone(),
1370 Some(RssKind::Anon),
1371 );
1372 let replacement = MappingSlot::new(
1373 MappingId::new(4),
1374 mm_id,
1375 new_va,
1376 PageOrder::BASE,
1377 page.clone(),
1378 Some(RssKind::Anon),
1379 );
1380 assert!(old.publish());
1381 assert_eq!(page.mapping_refs(), 1);
1382
1383 old.relocate_to(&replacement).unwrap();
1384
1385 assert_eq!(old.state(), SlotState::Detached);
1386 assert_eq!(replacement.state(), SlotState::Present);
1387 assert_eq!(page.mapping_refs(), 1);
1388 assert_eq!(
1389 page.rmap.snapshot(),
1390 alloc::vec![MappingSlotKey {
1391 space_id: mm_id,
1392 va: new_va,
1393 }]
1394 );
1395 assert!(replacement.detach());
1396 assert_eq!(page.mapping_refs(), 0);
1397 }
1398
1399 #[test]
1400 fn eviction_cannot_resume_before_tlb_retirement() {
1401 let page = PageObject::new_present(
1402 PageId::new(3),
1403 FrameLease::new(PhysAddr::from_usize(0x3000)),
1404 );
1405 let lease = page.eviction_lease().unwrap();
1406
1407 drop(lease);
1411 assert_eq!(page.state(), PageState::Evicting);
1412 assert!(matches!(
1413 page.resume_eviction_lease(),
1414 Err(EvictionError::Busy)
1415 ));
1416
1417 page.complete_eviction_tlb();
1418 let resumed = page.resume_eviction_lease().unwrap();
1419 assert!(resumed.cancel());
1420 assert_eq!(page.state(), PageState::Present);
1421 }
1422
1423 #[test]
1424 fn split_frame_leases_release_the_allocation_once_after_the_last_subpage() {
1425 RELEASES.store(0, Ordering::Relaxed);
1426 let owner = FrameLease::owned_with_releaser(
1427 PhysAddr::from_usize(0x20_0000),
1428 PAGE_SIZE_4K * 4,
1429 record_release,
1430 );
1431 let first = owner.sublease(0, PAGE_SIZE_4K).unwrap();
1432 let last = owner.sublease(PAGE_SIZE_4K * 3, PAGE_SIZE_4K).unwrap();
1433 assert_eq!(first.paddr(), PhysAddr::from_usize(0x20_0000));
1434 assert_eq!(last.paddr(), PhysAddr::from_usize(0x20_3000));
1435 assert!(owner.sublease(PAGE_SIZE_4K * 4, PAGE_SIZE_4K).is_none());
1436
1437 drop(owner);
1438 drop(first);
1439 assert_eq!(RELEASES.load(Ordering::Relaxed), 0);
1440 drop(last);
1441 assert_eq!(RELEASES.load(Ordering::Relaxed), 1);
1442 }
1443}