1use alloc::{sync::Arc, vec::Vec};
4use core::{
5 fmt,
6 sync::atomic::{AtomicU64, Ordering},
7};
8
9use ax_memory_addr::{MemoryAddr, PAGE_SIZE_4K, VirtAddr, VirtAddrRange};
10use ax_runtime::hal::paging::MappingFlags;
11
12use super::backend::{
13 MappingFileInfo, MappingOperation, SharedFileMappingLease, SharedMemoryObject,
14};
15use crate::{StarryError, StarryResult};
16
17pub type MappingRights = MappingFlags;
21
22#[repr(i32)]
24#[derive(Debug, Clone, Copy, PartialEq, Eq)]
25pub enum BusCode {
26 Adraln = 1,
27 AdrErr = 2,
28 ObjErr = 3,
29}
30
31#[derive(Debug, Clone, Copy, PartialEq, Eq)]
35pub enum FaultResult {
36 Handled,
37 Unmapped,
38 PermissionDenied,
39 Retry,
42 NoMemory,
46 Sigbus(BusCode),
47}
48
49#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
50pub struct VmaId(u64);
51
52impl VmaId {
53 pub const fn new(value: u64) -> Self {
54 Self(value)
55 }
56
57 pub const fn get(self) -> u64 {
58 self.0
59 }
60}
61
62#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
63pub struct MappingId(u64);
64
65impl MappingId {
66 pub const fn new(value: u64) -> Self {
67 Self(value)
68 }
69
70 pub const fn get(self) -> u64 {
71 self.0
72 }
73}
74
75#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord)]
76pub struct PageOrder(u8);
77
78impl PageOrder {
79 pub const BASE: Self = Self(0);
80
81 pub const fn new(order: u8) -> Self {
82 Self(order)
83 }
84
85 pub const fn get(self) -> u8 {
86 self.0
87 }
88}
89
90#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash, Default)]
91pub struct PageOffset(usize);
92
93impl PageOffset {
94 pub const ZERO: Self = Self(0);
95
96 pub const fn new(value: usize) -> Self {
97 Self(value)
98 }
99
100 pub const fn get(self) -> usize {
101 self.0
102 }
103}
104
105#[derive(Debug, Clone, Copy, PartialEq, Eq, Default)]
106pub enum PageSizePolicy {
107 #[default]
108 Base,
109 Transparent { order: PageOrder },
113 ExplicitHuge { order: PageOrder },
116}
117
118impl PageSizePolicy {
119 pub const TRANSPARENT_2M: Self = Self::Transparent {
120 order: PageOrder::new(9),
121 };
122
123 pub const fn for_size(size: usize) -> Self {
128 if size <= ax_memory_addr::PAGE_SIZE_4K || !size.is_power_of_two() {
129 Self::Base
130 } else {
131 Self::ExplicitHuge {
132 order: PageOrder::new((size.trailing_zeros() - 12) as u8),
133 }
134 }
135 }
136
137 pub fn fault_leaf_size(
142 self,
143 advice: HugePageAdvice,
144 mode: TransparentHugePageMode,
145 ) -> Option<usize> {
146 let order = match self {
147 Self::Base => return Some(ax_memory_addr::PAGE_SIZE_4K),
148 Self::Transparent { .. }
149 if advice != HugePageAdvice::Prefer
150 || mode == TransparentHugePageMode::Disabled =>
151 {
152 return Some(ax_memory_addr::PAGE_SIZE_4K);
153 }
154 Self::Transparent { order } | Self::ExplicitHuge { order } => order,
155 };
156 ax_memory_addr::PAGE_SIZE_4K.checked_shl(u32::from(order.get()))
157 }
158
159 pub const fn permits_fault_fallback(self) -> bool {
160 matches!(self, Self::Transparent { .. })
161 }
162}
163
164#[derive(Debug, Clone, Copy, PartialEq, Eq, Default)]
171pub enum HugePageAdvice {
172 #[default]
173 Default,
174 Prefer,
175 Avoid,
176}
177
178#[derive(Debug, Clone, Copy, PartialEq, Eq, Default)]
184#[repr(u8)]
185pub enum TransparentHugePageMode {
186 #[default]
187 Enabled = 0,
188 Disabled = 1,
189 ExceptAdvised = 3,
190}
191
192impl TransparentHugePageMode {
193 pub const fn prctl_value(self) -> u32 {
194 self as u32
195 }
196
197 pub(crate) const fn from_storage(value: u8) -> Self {
198 match value {
199 0 => Self::Enabled,
200 3 => Self::ExceptAdvised,
201 _ => Self::Disabled,
204 }
205 }
206}
207
208#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Default)]
209pub struct AnonymousSource;
210
211#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
212pub struct FileSource {
213 pub file_id: u64,
214 pub epoch: u64,
215 pub shared: bool,
216}
217
218#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Default)]
219pub struct ExternalSource;
220
221#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Default)]
222pub struct LinearSource;
223
224#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
227pub enum MappingSource {
228 Anonymous(AnonymousSource),
229 File(FileSource),
230 External(ExternalSource),
231 Linear(LinearSource),
232}
233
234impl MappingSource {
235 pub const fn key(self) -> u64 {
239 match self {
240 Self::Anonymous(_) => 1,
241 Self::File(source) => {
242 source.file_id
243 ^ source.epoch.rotate_left(17)
244 ^ (source.shared as u64).rotate_left(41)
245 }
246 Self::External(_) => 2,
247 Self::Linear(_) => 3,
248 }
249 }
250}
251
252#[derive(Debug, Clone, Copy, PartialEq, Eq)]
255pub struct VmaDescriptor {
256 pub mapping: MappingId,
257 pub source: MappingSource,
258 pub page_policy: PageSizePolicy,
259 pub source_offset: PageOffset,
260}
261
262#[derive(Debug, Clone)]
263pub struct MappingGroup {
264 pub id: MappingId,
265 pub source: Arc<MappingSource>,
266 pub page_policy: PageSizePolicy,
267}
268
269impl MappingGroup {
270 pub fn new(id: MappingId, source: MappingSource, page_policy: PageSizePolicy) -> Arc<Self> {
271 Arc::new(Self {
272 id,
273 source: Arc::new(source),
274 page_policy,
275 })
276 }
277}
278
279#[derive(Clone)]
282pub struct VmaSnapshot {
283 pub id: VmaId,
284 pub range: VirtAddrRange,
285 pub rights: MappingFlags,
286 pub reported_rights: MappingFlags,
287 pub max_rights: MappingFlags,
288 pub group: Arc<MappingGroup>,
289 pub source_offset: PageOffset,
290 pub huge_page_advice: HugePageAdvice,
291 pub lock_mode: VmaLockMode,
292 pub(crate) advice_policy: VmaAdvicePolicy,
293}
294
295#[derive(Debug, Clone, Copy, Default, PartialEq, Eq)]
302pub enum VmaLockMode {
303 #[default]
304 Unlocked,
305 Locked,
306 LockOnFault,
307}
308
309impl VmaLockMode {
310 pub const fn is_locked(self) -> bool {
311 !matches!(self, Self::Unlocked)
312 }
313}
314
315#[derive(Debug, Clone, Copy, Default, PartialEq, Eq)]
322pub(crate) struct VmaAdvicePolicy {
323 access_pattern: VmaAccessPattern,
324 dont_fork: bool,
325 dont_dump: bool,
326}
327
328impl VmaAdvicePolicy {
329 pub const DEFAULT: Self = Self {
330 access_pattern: VmaAccessPattern::Normal,
331 dont_fork: false,
332 dont_dump: false,
333 };
334
335 pub const fn dont_fork(self) -> bool {
336 self.dont_fork
337 }
338
339 pub const fn apply(self, update: VmaAdviceUpdate) -> Self {
340 match update {
341 VmaAdviceUpdate::AccessPattern(access_pattern) => Self {
342 access_pattern,
343 ..self
344 },
345 VmaAdviceUpdate::DontFork(dont_fork) => Self { dont_fork, ..self },
346 VmaAdviceUpdate::DontDump(dont_dump) => Self { dont_dump, ..self },
347 }
348 }
349}
350
351#[derive(Debug, Clone, Copy, Default, PartialEq, Eq)]
352pub(crate) enum VmaAccessPattern {
353 #[default]
354 Normal,
355 Random,
356 Sequential,
357}
358
359#[derive(Debug, Clone, Copy, PartialEq, Eq)]
360pub(crate) enum VmaAdviceUpdate {
361 AccessPattern(VmaAccessPattern),
362 DontFork(bool),
363 DontDump(bool),
364}
365
366pub type Vma = VmaSnapshot;
369
370#[derive(Debug, Clone)]
371pub(crate) struct VmaInspectionRecord {
372 pub range: VirtAddrRange,
373 pub rights: MappingRights,
374 pub reported_rights: MappingRights,
375 pub file: MappingFileInfo,
376 lock_mode: VmaLockMode,
377}
378
379impl VmaInspectionRecord {
380 pub fn start(&self) -> VirtAddr {
381 self.range.start
382 }
383
384 pub fn end(&self) -> VirtAddr {
385 self.range.end
386 }
387
388 pub fn size(&self) -> usize {
389 self.range.size()
390 }
391
392 pub fn flags(&self) -> MappingRights {
393 self.rights
394 }
395
396 pub fn reported_flags(&self) -> MappingRights {
397 self.reported_rights
398 }
399
400 pub fn file_info(&self) -> &MappingFileInfo {
401 &self.file
402 }
403
404 pub fn is_locked(&self) -> bool {
405 self.lock_mode.is_locked()
406 }
407}
408
409#[derive(Clone, Copy)]
410enum AdviceMappingKind {
411 SharedFile,
412 ReclaimUnsupported,
413 Invalid,
414}
415
416#[derive(Clone)]
420pub(crate) struct VmaAdviceFragment {
421 pub gap_before: bool,
422 pub range: VirtAddrRange,
423 file: Option<SharedFileMappingLease>,
424 kind: AdviceMappingKind,
425 private_anonymous: bool,
426 lock_mode: VmaLockMode,
427}
428
429#[derive(Clone)]
430pub(crate) struct SharedFileVmaRecord {
431 pub range: VirtAddrRange,
432 pub rights: MappingRights,
433 pub file: SharedFileMappingLease,
434}
435
436#[derive(Clone)]
439pub(crate) struct VmaResidencyProbe {
440 operation: MappingOperation,
441}
442
443#[derive(Clone)]
447pub(crate) struct VmaMremapSource {
448 snapshot: Arc<VmaSnapshot>,
449 operation: MappingOperation,
450}
451
452impl VmaMremapSource {
453 pub fn start(&self) -> VirtAddr {
454 self.snapshot.start()
455 }
456
457 pub fn end(&self) -> VirtAddr {
458 self.snapshot.end()
459 }
460
461 pub fn rights(&self) -> MappingRights {
462 self.snapshot.rights
463 }
464
465 pub fn reported_rights(&self) -> MappingRights {
466 self.snapshot.reported_rights
467 }
468
469 pub fn max_rights(&self) -> MappingRights {
470 self.snapshot.max_rights
471 }
472
473 pub fn huge_page_advice(&self) -> HugePageAdvice {
474 self.snapshot.huge_page_advice
475 }
476
477 pub fn lock_mode(&self) -> VmaLockMode {
478 self.snapshot.lock_mode
479 }
480
481 pub(crate) fn advice_policy(&self) -> VmaAdvicePolicy {
482 self.snapshot.advice_policy
483 }
484
485 pub fn alignment(&self) -> usize {
486 self.operation.mremap_alignment()
487 }
488
489 pub fn supports_dontunmap(&self) -> bool {
490 self.operation.supports_mremap_dontunmap()
491 }
492
493 pub fn is_linear(&self) -> bool {
494 self.operation.is_linear()
495 }
496
497 pub fn shared_object(&self) -> Option<Arc<SharedMemoryObject>> {
498 self.operation.shared_memory_object()
499 }
500
501 pub(super) fn relocated_operation(
502 &self,
503 target: VirtAddr,
504 source_offset: usize,
505 target_size: usize,
506 ) -> StarryResult<MappingOperation> {
507 self.operation
508 .relocated(target, source_offset)?
509 .resized(target_size)
510 }
511}
512
513impl VmaResidencyProbe {
514 pub fn mincore_resident(&self, address: VirtAddr, cred: &crate::task::Cred) -> bool {
515 self.operation.mincore_resident(address, cred)
516 }
517}
518
519impl VmaAdviceFragment {
520 pub fn shared_file(&self) -> Option<&SharedFileMappingLease> {
521 self.file.as_ref()
522 }
523
524 pub fn is_private_anonymous(&self) -> bool {
525 self.private_anonymous
526 }
527
528 pub fn is_locked(&self) -> bool {
529 self.lock_mode.is_locked()
530 }
531
532 pub fn is_special(&self) -> bool {
533 matches!(self.kind, AdviceMappingKind::Invalid)
534 }
535
536 pub fn pageout(&self) -> StarryResult {
537 match (&self.file, self.kind) {
538 (Some(file), AdviceMappingKind::SharedFile) => {
539 let outcome = file.pageout_range(self.range.start, self.range.end)?;
540 if let Some(reason) = outcome.deferred_reason() {
541 debug!(
542 "file pageout deferred after reclaiming {} pages: {:?}",
543 outcome.reclaimed(),
544 reason
545 );
546 }
547 Ok(())
548 }
549 (None, AdviceMappingKind::ReclaimUnsupported) => {
550 Err(StarryError::OperationNotSupported)
551 }
552 _ => Err(StarryError::InvalidInput),
553 }
554 }
555}
556
557impl VmaSnapshot {
558 pub const fn start(&self) -> VirtAddr {
559 self.range.start
560 }
561
562 pub const fn end(&self) -> VirtAddr {
563 self.range.end
564 }
565
566 pub fn size(&self) -> usize {
567 self.range.size()
568 }
569
570 pub const fn flags(&self) -> MappingFlags {
571 self.rights
572 }
573
574 pub const fn reported_flags(&self) -> MappingFlags {
575 self.reported_rights
576 }
577
578 pub const fn max_flags(&self) -> MappingFlags {
579 self.max_rights
580 }
581
582 pub fn contains(&self, address: VirtAddr) -> bool {
583 self.range.contains(address)
584 }
585
586 fn can_merge_with(&self, next: &Self) -> bool {
587 self.range.end == next.range.start
588 && self.rights == next.rights
589 && self.reported_rights == next.reported_rights
590 && self.max_rights == next.max_rights
591 && Arc::ptr_eq(&self.group, &next.group)
592 && self.huge_page_advice == next.huge_page_advice
593 && self.lock_mode == next.lock_mode
594 && self.advice_policy == next.advice_policy
595 && self.source_offset.get().checked_add(self.range.size())
596 == Some(next.source_offset.get())
597 }
598
599 fn merge_through(&self, last: &Self) -> Option<Self> {
600 if self.range.end > last.range.end {
601 return None;
602 }
603 Some(Self {
604 id: self.id,
605 range: VirtAddrRange::new(self.range.start, last.range.end),
606 rights: self.rights,
607 reported_rights: self.reported_rights,
608 max_rights: self.max_rights,
609 group: self.group.clone(),
610 source_offset: self.source_offset,
611 huge_page_advice: self.huge_page_advice,
612 lock_mode: self.lock_mode,
613 advice_policy: self.advice_policy,
614 })
615 }
616
617 pub(crate) fn fragment(&self, start: VirtAddr, end: VirtAddr, id: VmaId) -> Option<Self> {
618 if start >= end || start < self.range.start || end > self.range.end {
619 return None;
620 }
621 let range = VirtAddrRange::new(start, end);
622 let offset = start.checked_sub_addr(self.range.start)?;
623 Some(Self {
624 id,
625 range,
626 rights: self.rights,
627 reported_rights: self.reported_rights,
628 max_rights: self.max_rights,
629 group: self.group.clone(),
630 source_offset: PageOffset::new(self.source_offset.get().checked_add(offset)?),
631 huge_page_advice: self.huge_page_advice,
632 lock_mode: self.lock_mode,
633 advice_policy: self.advice_policy,
634 })
635 }
636}
637
638#[derive(Clone)]
644pub(super) struct VmaEntry {
645 snapshot: Arc<VmaSnapshot>,
646 operation: MappingOperation,
647}
648
649impl VmaEntry {
650 pub(super) fn new(snapshot: VmaSnapshot, operation: MappingOperation) -> Arc<Self> {
651 Arc::new(Self {
652 snapshot: Arc::new(snapshot),
653 operation,
654 })
655 }
656
657 pub(super) fn snapshot(&self) -> &Arc<VmaSnapshot> {
658 &self.snapshot
659 }
660
661 pub(super) fn operation(&self) -> &MappingOperation {
662 &self.operation
663 }
664
665 pub(super) fn operation_clone(&self) -> MappingOperation {
666 self.operation.clone()
667 }
668
669 pub(super) fn range(&self) -> VirtAddrRange {
670 self.snapshot.range
671 }
672
673 pub(super) fn start(&self) -> VirtAddr {
674 self.snapshot.range.start
675 }
676
677 pub(super) fn end(&self) -> VirtAddr {
678 self.snapshot.range.end
679 }
680
681 pub(super) fn size(&self) -> usize {
682 self.snapshot.range.size()
683 }
684
685 pub(super) fn rights(&self) -> MappingRights {
686 self.snapshot.rights
687 }
688
689 pub(super) fn reported_rights(&self) -> MappingRights {
690 self.snapshot.reported_rights
691 }
692
693 pub(super) fn max_rights(&self) -> MappingRights {
694 self.snapshot.max_rights
695 }
696
697 pub(super) fn inspection_record(&self) -> StarryResult<VmaInspectionRecord> {
698 Ok(VmaInspectionRecord {
699 range: self.range(),
700 rights: self.rights(),
701 reported_rights: self.reported_rights(),
702 file: self.operation.file_info()?,
703 lock_mode: self.snapshot.lock_mode,
704 })
705 }
706
707 pub(super) fn advice_fragment(
708 &self,
709 cursor: VirtAddr,
710 end: VirtAddr,
711 ) -> Option<VmaAdviceFragment> {
712 let fragment_start = self.start().max(cursor);
713 let fragment_end = self.end().min(end);
714 let range = VirtAddrRange::try_new(fragment_start, fragment_end)?;
715 let file = self.operation.shared_file_lease();
716 let kind = if file.is_some() {
717 AdviceMappingKind::SharedFile
718 } else if self.operation.is_linear() {
719 AdviceMappingKind::Invalid
720 } else {
721 AdviceMappingKind::ReclaimUnsupported
722 };
723 Some(VmaAdviceFragment {
724 gap_before: self.start() > cursor,
725 range,
726 file,
727 kind,
728 private_anonymous: self.operation.is_private_anonymous(),
729 lock_mode: self.snapshot.lock_mode,
730 })
731 }
732
733 pub(super) fn residency_probe(&self) -> VmaResidencyProbe {
734 VmaResidencyProbe {
735 operation: self.operation.clone(),
736 }
737 }
738
739 pub(super) fn mremap_source(&self) -> VmaMremapSource {
740 VmaMremapSource {
741 snapshot: self.snapshot.clone(),
742 operation: self.operation.clone(),
743 }
744 }
745
746 pub(super) fn shared_file_record(&self) -> Option<SharedFileVmaRecord> {
747 Some(SharedFileVmaRecord {
748 range: self.range(),
749 rights: self.rights(),
750 file: self.operation.shared_file_lease()?,
751 })
752 }
753
754 fn fragment(&self, start: VirtAddr, end: VirtAddr, id: VmaId) -> Option<Arc<Self>> {
755 let range = VirtAddrRange::new(start, end);
756 let snapshot = self.snapshot.fragment(start, end, id)?;
757 let operation = self.operation.fragment(self.snapshot.range, range).ok()?;
758 Some(Self::new(snapshot, operation))
759 }
760
761 fn extended_right(&self, additional_size: usize) -> Option<Arc<Self>> {
762 if additional_size == 0 {
763 return Some(Arc::new(self.clone()));
764 }
765 let new_end = self.snapshot.range.end.checked_add(additional_size)?;
766 let mut snapshot = (*self.snapshot).clone();
767 snapshot.range = VirtAddrRange::new(snapshot.range.start, new_end);
768 Some(Self::new(snapshot, self.operation.clone()))
769 }
770}
771
772impl fmt::Debug for VmaEntry {
773 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
774 f.debug_struct("VmaEntry")
775 .field("snapshot", &self.snapshot)
776 .finish_non_exhaustive()
777 }
778}
779
780impl fmt::Debug for VmaSnapshot {
781 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
782 f.debug_struct("VmaSnapshot")
783 .field("id", &self.id)
784 .field("range", &self.range)
785 .field("rights", &self.rights)
786 .field("reported_rights", &self.reported_rights)
787 .field("max_rights", &self.max_rights)
788 .field("group", &self.group)
789 .field("source_offset", &self.source_offset)
790 .field("huge_page_advice", &self.huge_page_advice)
791 .field("lock_mode", &self.lock_mode)
792 .field("advice_policy", &self.advice_policy)
793 .finish_non_exhaustive()
794 }
795}
796
797#[derive(Clone, Default, Debug)]
803pub struct VmaMap {
804 root: Option<Arc<VmaNode>>,
805}
806
807#[derive(Debug)]
808struct VmaNode {
809 entry: Arc<VmaEntry>,
810 left: Option<Arc<VmaNode>>,
811 right: Option<Arc<VmaNode>>,
812 height: u8,
813}
814
815impl VmaNode {
816 fn new(entry: Arc<VmaEntry>) -> Arc<Self> {
817 Arc::new(Self {
818 entry,
819 left: None,
820 right: None,
821 height: 1,
822 })
823 }
824
825 fn with_children(
826 entry: Arc<VmaEntry>,
827 left: Option<Arc<VmaNode>>,
828 right: Option<Arc<VmaNode>>,
829 ) -> Arc<Self> {
830 Arc::new(Self {
831 entry,
832 height: 1 + node_height(&left).max(node_height(&right)),
833 left,
834 right,
835 })
836 }
837}
838
839fn node_height(node: &Option<Arc<VmaNode>>) -> u8 {
840 node.as_ref().map_or(0, |node| node.height)
841}
842
843fn balance_factor(node: &VmaNode) -> i16 {
844 i16::from(node_height(&node.left)) - i16::from(node_height(&node.right))
845}
846
847fn rotate_right(node: Arc<VmaNode>) -> Arc<VmaNode> {
848 let Some(pivot) = node.left.clone() else {
849 return node;
850 };
851 let new_right =
852 VmaNode::with_children(node.entry.clone(), pivot.right.clone(), node.right.clone());
853 VmaNode::with_children(pivot.entry.clone(), pivot.left.clone(), Some(new_right))
854}
855
856fn rotate_left(node: Arc<VmaNode>) -> Arc<VmaNode> {
857 let Some(pivot) = node.right.clone() else {
858 return node;
859 };
860 let new_left =
861 VmaNode::with_children(node.entry.clone(), node.left.clone(), pivot.left.clone());
862 VmaNode::with_children(pivot.entry.clone(), Some(new_left), pivot.right.clone())
863}
864
865fn rebalance(node: Arc<VmaNode>) -> Arc<VmaNode> {
866 let balance = balance_factor(&node);
867 if balance > 1 {
868 if node
869 .left
870 .as_ref()
871 .is_some_and(|left| balance_factor(left) < 0)
872 {
873 let left = node.left.as_ref().map(|left| rotate_left(left.clone()));
874 return rotate_right(VmaNode::with_children(
875 node.entry.clone(),
876 left,
877 node.right.clone(),
878 ));
879 }
880 return rotate_right(node);
881 }
882 if balance < -1 {
883 if node
884 .right
885 .as_ref()
886 .is_some_and(|right| balance_factor(right) > 0)
887 {
888 let right = node.right.as_ref().map(|right| rotate_right(right.clone()));
889 return rotate_left(VmaNode::with_children(
890 node.entry.clone(),
891 node.left.clone(),
892 right,
893 ));
894 }
895 return rotate_left(node);
896 }
897 node
898}
899
900fn insert_node(node: Option<Arc<VmaNode>>, entry: Arc<VmaEntry>) -> Result<Arc<VmaNode>, ()> {
901 let Some(current) = node else {
902 return Ok(VmaNode::new(entry));
903 };
904 if entry.snapshot.range.end <= current.entry.snapshot.range.start {
905 let left = insert_node(current.left.clone(), entry)?;
906 return Ok(rebalance(VmaNode::with_children(
907 current.entry.clone(),
908 Some(left),
909 current.right.clone(),
910 )));
911 }
912 if entry.snapshot.range.start >= current.entry.snapshot.range.end {
913 let right = insert_node(current.right.clone(), entry)?;
914 return Ok(rebalance(VmaNode::with_children(
915 current.entry.clone(),
916 current.left.clone(),
917 Some(right),
918 )));
919 }
920 Err(())
921}
922
923fn remove_min(node: Arc<VmaNode>) -> (Option<Arc<VmaNode>>, Arc<VmaNode>) {
924 let Some(left) = node.left.clone() else {
925 return (node.right.clone(), node);
926 };
927 let (new_left, minimum) = remove_min(left);
928 (
929 Some(rebalance(VmaNode::with_children(
930 node.entry.clone(),
931 new_left,
932 node.right.clone(),
933 ))),
934 minimum,
935 )
936}
937
938fn remove_node(
939 node: Option<Arc<VmaNode>>,
940 start: VirtAddr,
941) -> (Option<Arc<VmaNode>>, Option<Arc<VmaEntry>>) {
942 let Some(current) = node else {
943 return (None, None);
944 };
945 if start < current.entry.snapshot.range.start {
946 let (left, removed) = remove_node(current.left.clone(), start);
947 return (
948 Some(rebalance(VmaNode::with_children(
949 current.entry.clone(),
950 left,
951 current.right.clone(),
952 ))),
953 removed,
954 );
955 }
956 if start > current.entry.snapshot.range.start {
957 let (right, removed) = remove_node(current.right.clone(), start);
958 return (
959 Some(rebalance(VmaNode::with_children(
960 current.entry.clone(),
961 current.left.clone(),
962 right,
963 ))),
964 removed,
965 );
966 }
967 let removed = Some(current.entry.clone());
968 match (current.left.clone(), current.right.clone()) {
969 (None, right) => (right, removed),
970 (Some(left), None) => (Some(left), removed),
971 (Some(left), Some(right)) => {
972 let (new_right, successor) = remove_min(right);
973 (
974 Some(rebalance(VmaNode::with_children(
975 successor.entry.clone(),
976 Some(left),
977 new_right,
978 ))),
979 removed,
980 )
981 }
982 }
983}
984
985impl VmaMap {
986 pub fn len(&self) -> usize {
987 self.iter().count()
988 }
989
990 pub fn is_empty(&self) -> bool {
991 self.root.is_none()
992 }
993
994 pub(crate) fn locked_pages(&self) -> Option<u64> {
999 self.iter().try_fold(0u64, |pages, vma| {
1000 if !vma.lock_mode.is_locked() {
1001 return Some(pages);
1002 }
1003 let vma_pages = u64::try_from(vma.range.size() / PAGE_SIZE_4K).ok()?;
1004 pages.checked_add(vma_pages)
1005 })
1006 }
1007
1008 pub fn lookup(&self, address: VirtAddr) -> Option<Arc<VmaSnapshot>> {
1009 self.lookup_entry(address)
1010 .map(|entry| entry.snapshot.clone())
1011 }
1012
1013 pub(super) fn lookup_entry(&self, address: VirtAddr) -> Option<Arc<VmaEntry>> {
1014 let mut node = self.root.clone();
1015 let mut candidate = None;
1016 while let Some(current) = node {
1017 if address < current.entry.snapshot.range.start {
1018 node = current.left.clone();
1019 } else {
1020 candidate = Some(current.entry.clone());
1021 node = current.right.clone();
1022 }
1023 }
1024 candidate.filter(|entry| entry.snapshot.contains(address))
1025 }
1026
1027 pub fn for_each_overlapping(
1039 &self,
1040 range: VirtAddrRange,
1041 mut visit: impl FnMut(&Arc<VmaSnapshot>) -> bool,
1042 ) {
1043 self.for_each_overlapping_entry(range, |entry| visit(&entry.snapshot));
1044 }
1045
1046 pub(super) fn for_each_overlapping_entry(
1049 &self,
1050 range: VirtAddrRange,
1051 mut visit: impl FnMut(&Arc<VmaEntry>) -> bool,
1052 ) {
1053 let mut visited = 0;
1054 Self::visit_overlapping(&self.root, range, &mut visited, &mut visit);
1055 }
1056
1057 fn visit_overlapping(
1062 node: &Option<Arc<VmaNode>>,
1063 range: VirtAddrRange,
1064 visited: &mut usize,
1065 visit: &mut impl FnMut(&Arc<VmaEntry>) -> bool,
1066 ) -> bool {
1067 let Some(current) = node else {
1068 return true;
1069 };
1070 *visited += 1;
1071 let vma = current.entry.snapshot.range;
1072 if range.start < vma.start
1073 && !Self::visit_overlapping(¤t.left, range, visited, visit)
1074 {
1075 return false;
1076 }
1077 if vma.overlaps(range) && !visit(¤t.entry) {
1078 return false;
1079 }
1080 if range.end > vma.end
1081 && !Self::visit_overlapping(¤t.right, range, visited, visit)
1082 {
1083 return false;
1084 }
1085 true
1086 }
1087
1088 pub fn lookup_range(&self, range: VirtAddrRange) -> Vec<Arc<VmaSnapshot>> {
1092 let mut found = Vec::new();
1093 self.for_each_overlapping(range, |vma| {
1094 found.push(vma.clone());
1095 true
1096 });
1097 found
1098 }
1099
1100 pub fn contains_range(&self, start: VirtAddr, size: usize) -> bool {
1101 let Some(request) = VirtAddrRange::try_from_start_size(start, size) else {
1102 return false;
1103 };
1104 if request.is_empty() {
1105 return false;
1106 }
1107 let mut cursor = request.start;
1108 let mut covered = false;
1109 self.for_each_overlapping(request, |vma| {
1110 if vma.range.end <= cursor {
1111 return true;
1112 }
1113 if vma.range.start > cursor {
1114 return false;
1115 }
1116 cursor = vma.range.end.min(request.end);
1120 if cursor >= request.end {
1121 covered = true;
1122 return false;
1123 }
1124 true
1125 });
1126 covered
1127 }
1128
1129 fn fragment_with_huge_page_advice(
1130 source: &VmaEntry,
1131 start: VirtAddr,
1132 end: VirtAddr,
1133 id: VmaId,
1134 advice: HugePageAdvice,
1135 ) -> Option<Arc<VmaEntry>> {
1136 let fragment = source.fragment(start, end, id)?;
1137 let mut snapshot = (*fragment.snapshot).clone();
1138 snapshot.huge_page_advice = advice;
1139 Some(VmaEntry::new(snapshot, fragment.operation.clone()))
1140 }
1141
1142 fn update_one_huge_page_advice(
1143 &self,
1144 source: &Arc<VmaEntry>,
1145 range: VirtAddrRange,
1146 advice: HugePageAdvice,
1147 ) -> Option<Self> {
1148 if range.start < source.snapshot.range.start || range.end > source.snapshot.range.end {
1149 return None;
1150 }
1151 if source.snapshot.huge_page_advice == advice {
1152 return Some(self.clone());
1153 }
1154
1155 let (mut updated, removed) = self.remove_entry(source.snapshot.range.start)?;
1156 if removed.snapshot.id != source.snapshot.id {
1157 return None;
1158 }
1159 let mut retained_id = Some(source.snapshot.id);
1160 if source.snapshot.range.start < range.start {
1161 let head = Self::fragment_with_huge_page_advice(
1162 source,
1163 source.snapshot.range.start,
1164 range.start,
1165 retained_id.take().unwrap_or_else(allocate_vma_id),
1166 source.snapshot.huge_page_advice,
1167 )?;
1168 updated = updated.insert_entry(head)?;
1169 }
1170 let body = Self::fragment_with_huge_page_advice(
1171 source,
1172 range.start,
1173 range.end,
1174 retained_id.take().unwrap_or_else(allocate_vma_id),
1175 advice,
1176 )?;
1177 updated = updated.insert_entry(body)?;
1178 if range.end < source.snapshot.range.end {
1179 let tail = Self::fragment_with_huge_page_advice(
1180 source,
1181 range.end,
1182 source.snapshot.range.end,
1183 retained_id.take().unwrap_or_else(allocate_vma_id),
1184 source.snapshot.huge_page_advice,
1185 )?;
1186 updated = updated.insert_entry(tail)?;
1187 }
1188 Some(updated)
1189 }
1190
1191 pub(super) fn without_range(&self, range: VirtAddrRange) -> Option<Self> {
1196 if range.is_empty() {
1197 return Some(self.clone());
1198 }
1199
1200 let affected: Vec<_> = self
1201 .iter_entries()
1202 .filter(|entry| entry.snapshot.range.overlaps(range))
1203 .collect();
1204 let mut updated = self.clone();
1205 for source in affected {
1206 let (next, removed) = updated.remove_entry(source.snapshot.range.start)?;
1207 if removed.snapshot.id != source.snapshot.id {
1208 return None;
1209 }
1210 updated = next;
1211
1212 let mut retained_id = Some(source.snapshot.id);
1213 if source.snapshot.range.start < range.start {
1214 let head = source.fragment(
1215 source.snapshot.range.start,
1216 source.snapshot.range.end.min(range.start),
1217 retained_id.take().unwrap_or_else(allocate_vma_id),
1218 )?;
1219 updated = updated.insert_entry(head)?;
1220 }
1221 if source.snapshot.range.end > range.end {
1222 let tail = source.fragment(
1223 source.snapshot.range.start.max(range.end),
1224 source.snapshot.range.end,
1225 retained_id.take().unwrap_or_else(allocate_vma_id),
1226 )?;
1227 updated = updated.insert_entry(tail)?;
1228 }
1229 }
1230 Some(updated)
1231 }
1232
1233 pub(super) fn with_permissions(
1238 &self,
1239 range: VirtAddrRange,
1240 rights: MappingRights,
1241 reported_rights: MappingRights,
1242 ) -> Option<Self> {
1243 if range.is_empty() || !self.contains_range(range.start, range.size()) {
1244 return None;
1245 }
1246
1247 let affected: Vec<_> = self
1248 .iter_entries()
1249 .filter(|entry| entry.snapshot.range.overlaps(range))
1250 .collect();
1251 let mut updated = self.clone();
1252 for source in affected {
1253 let (next, removed) = updated.remove_entry(source.snapshot.range.start)?;
1254 if removed.snapshot.id != source.snapshot.id {
1255 return None;
1256 }
1257 updated = next;
1258
1259 let intersection = VirtAddrRange::new(
1260 source.snapshot.range.start.max(range.start),
1261 source.snapshot.range.end.min(range.end),
1262 );
1263 let mut retained_id = Some(source.snapshot.id);
1264 if source.snapshot.range.start < intersection.start {
1265 let head = source.fragment(
1266 source.snapshot.range.start,
1267 intersection.start,
1268 retained_id.take().unwrap_or_else(allocate_vma_id),
1269 )?;
1270 updated = updated.insert_entry(head)?;
1271 }
1272
1273 let body = source.fragment(
1274 intersection.start,
1275 intersection.end,
1276 retained_id.take().unwrap_or_else(allocate_vma_id),
1277 )?;
1278 let mut body_snapshot = (*body.snapshot).clone();
1279 body_snapshot.rights = rights;
1280 body_snapshot.reported_rights = reported_rights;
1281 updated = updated.insert_entry(VmaEntry::new(body_snapshot, body.operation.clone()))?;
1282
1283 if intersection.end < source.snapshot.range.end {
1284 let tail = source.fragment(
1285 intersection.end,
1286 source.snapshot.range.end,
1287 retained_id.take().unwrap_or_else(allocate_vma_id),
1288 )?;
1289 updated = updated.insert_entry(tail)?;
1290 }
1291 }
1292 updated.coalesce_compatible()
1293 }
1294
1295 pub(super) fn with_lock_mode(
1300 &self,
1301 range: VirtAddrRange,
1302 lock_mode: VmaLockMode,
1303 ) -> Option<Self> {
1304 if range.is_empty() || !self.contains_range(range.start, range.size()) {
1305 return None;
1306 }
1307
1308 let affected: Vec<_> = self
1309 .iter_entries()
1310 .filter(|entry| entry.snapshot.range.overlaps(range))
1311 .collect();
1312 let mut updated = self.clone();
1313 for source in affected {
1314 let (next, removed) = updated.remove_entry(source.snapshot.range.start)?;
1315 if removed.snapshot.id != source.snapshot.id {
1316 return None;
1317 }
1318 updated = next;
1319
1320 let intersection = VirtAddrRange::new(
1321 source.snapshot.range.start.max(range.start),
1322 source.snapshot.range.end.min(range.end),
1323 );
1324 let mut retained_id = Some(source.snapshot.id);
1325 if source.snapshot.range.start < intersection.start {
1326 let head = source.fragment(
1327 source.snapshot.range.start,
1328 intersection.start,
1329 retained_id.take().unwrap_or_else(allocate_vma_id),
1330 )?;
1331 updated = updated.insert_entry(head)?;
1332 }
1333
1334 let body = source.fragment(
1335 intersection.start,
1336 intersection.end,
1337 retained_id.take().unwrap_or_else(allocate_vma_id),
1338 )?;
1339 let mut body_snapshot = (*body.snapshot).clone();
1340 body_snapshot.lock_mode = lock_mode;
1341 updated = updated.insert_entry(VmaEntry::new(body_snapshot, body.operation.clone()))?;
1342
1343 if intersection.end < source.snapshot.range.end {
1344 let tail = source.fragment(
1345 intersection.end,
1346 source.snapshot.range.end,
1347 retained_id.take().unwrap_or_else(allocate_vma_id),
1348 )?;
1349 updated = updated.insert_entry(tail)?;
1350 }
1351 }
1352 updated.coalesce_compatible()
1353 }
1354
1355 pub(super) fn with_advice_update(
1358 &self,
1359 range: VirtAddrRange,
1360 update: VmaAdviceUpdate,
1361 ) -> Option<Self> {
1362 if range.is_empty() || !self.contains_range(range.start, range.size()) {
1363 return None;
1364 }
1365
1366 let affected: Vec<_> = self
1367 .iter_entries()
1368 .filter(|entry| entry.snapshot.range.overlaps(range))
1369 .collect();
1370 let mut updated = self.clone();
1371 for source in affected {
1372 let (next, removed) = updated.remove_entry(source.snapshot.range.start)?;
1373 if removed.snapshot.id != source.snapshot.id {
1374 return None;
1375 }
1376 updated = next;
1377
1378 let intersection = VirtAddrRange::new(
1379 source.snapshot.range.start.max(range.start),
1380 source.snapshot.range.end.min(range.end),
1381 );
1382 let mut retained_id = Some(source.snapshot.id);
1383 if source.snapshot.range.start < intersection.start {
1384 let head = source.fragment(
1385 source.snapshot.range.start,
1386 intersection.start,
1387 retained_id.take().unwrap_or_else(allocate_vma_id),
1388 )?;
1389 updated = updated.insert_entry(head)?;
1390 }
1391
1392 let body = source.fragment(
1393 intersection.start,
1394 intersection.end,
1395 retained_id.take().unwrap_or_else(allocate_vma_id),
1396 )?;
1397 let mut body_snapshot = (*body.snapshot).clone();
1398 body_snapshot.advice_policy = body_snapshot.advice_policy.apply(update);
1399 updated = updated.insert_entry(VmaEntry::new(body_snapshot, body.operation.clone()))?;
1400
1401 if intersection.end < source.snapshot.range.end {
1402 let tail = source.fragment(
1403 intersection.end,
1404 source.snapshot.range.end,
1405 retained_id.take().unwrap_or_else(allocate_vma_id),
1406 )?;
1407 updated = updated.insert_entry(tail)?;
1408 }
1409 }
1410 updated.coalesce_compatible()
1411 }
1412
1413 fn coalesce_compatible(&self) -> Option<Self> {
1418 let ordered: Vec<_> = self.iter_entries().collect();
1419 let mut updated = self.clone();
1420 let mut index = 0;
1421 while index < ordered.len() {
1422 let first = index;
1423 while index + 1 < ordered.len()
1424 && ordered[index]
1425 .snapshot
1426 .can_merge_with(ordered[index + 1].snapshot.as_ref())
1427 {
1428 index += 1;
1429 }
1430 if index > first {
1431 for entry in &ordered[first..=index] {
1432 let (next, removed) = updated.remove_entry(entry.snapshot.range.start)?;
1433 if removed.snapshot.id != entry.snapshot.id {
1434 return None;
1435 }
1436 updated = next;
1437 }
1438 let merged = ordered[first]
1439 .snapshot
1440 .merge_through(ordered[index].snapshot.as_ref())?;
1441 updated =
1442 updated.insert_with_operation(merged, ordered[first].operation.clone())?;
1443 }
1444 index += 1;
1445 }
1446 Some(updated)
1447 }
1448
1449 fn coalesce_huge_page_advice_near(&self, changed: VirtAddrRange) -> Option<Self> {
1457 let ordered: Vec<_> = self.iter_entries().collect();
1458 let mut replacements = Vec::new();
1459 let mut index = 0;
1460 while index < ordered.len() {
1461 let first = index;
1462 while index + 1 < ordered.len()
1463 && ordered[index]
1464 .snapshot
1465 .can_merge_with(ordered[index + 1].snapshot.as_ref())
1466 {
1467 index += 1;
1468 }
1469 if index > first
1470 && ordered[index].snapshot.range.end >= changed.start
1471 && ordered[first].snapshot.range.start <= changed.end
1472 {
1473 let mut starts = Vec::new();
1474 starts.try_reserve(index - first + 1).ok()?;
1475 starts.extend(
1476 ordered[first..=index]
1477 .iter()
1478 .map(|entry| entry.snapshot.range.start),
1479 );
1480 let merged = ordered[first]
1481 .snapshot
1482 .merge_through(ordered[index].snapshot.as_ref())?;
1483 replacements.push((starts, merged, ordered[first].operation.clone()));
1484 }
1485 index += 1;
1486 }
1487
1488 let mut updated = self.clone();
1489 for (starts, merged, operation) in replacements {
1490 for start in starts {
1491 let (next, _) = updated.remove_entry(start)?;
1492 updated = next;
1493 }
1494 updated = updated.insert_with_operation(merged, operation)?;
1495 }
1496 Some(updated)
1497 }
1498
1499 pub fn with_huge_page_advice(
1505 &self,
1506 range: VirtAddrRange,
1507 advice: HugePageAdvice,
1508 ) -> Option<Self> {
1509 if range.is_empty() || !self.contains_range(range.start, range.size()) {
1510 return None;
1511 }
1512 let affected: Vec<_> = self
1513 .iter_entries()
1514 .filter(|entry| entry.snapshot.range.overlaps(range))
1515 .collect();
1516 let mut updated = self.clone();
1517 for source in affected {
1518 let fragment = VirtAddrRange::new(
1519 source.snapshot.range.start.max(range.start),
1520 source.snapshot.range.end.min(range.end),
1521 );
1522 updated = updated.update_one_huge_page_advice(&source, fragment, advice)?;
1523 }
1524 updated.coalesce_huge_page_advice_near(range)
1525 }
1526
1527 pub fn find_free_area(
1529 &self,
1530 hint: VirtAddr,
1531 size: usize,
1532 limit: VirtAddrRange,
1533 align: usize,
1534 ) -> Option<VirtAddr> {
1535 if limit.start >= limit.end
1540 || size == 0
1541 || align == 0
1542 || !align.is_power_of_two()
1543 || !size.is_multiple_of(align)
1544 {
1545 return None;
1546 }
1547 let align_up = |address: VirtAddr| {
1548 address
1549 .as_usize()
1550 .checked_add(align - 1)
1551 .map(|value| VirtAddr::from_usize(value & !(align - 1)))
1552 };
1553 let mut candidate = align_up(hint.max(limit.start))?;
1554 if candidate < limit.start || candidate >= limit.end {
1555 return None;
1556 }
1557 for vma in self.iter() {
1558 if vma.range.end <= candidate {
1559 continue;
1560 }
1561 if vma.range.start > candidate
1562 && candidate >= limit.start
1563 && candidate
1564 .checked_add(size)
1565 .is_some_and(|end| end <= vma.range.start)
1566 && candidate
1567 .checked_add(size)
1568 .is_some_and(|end| end <= limit.end)
1569 {
1570 return Some(candidate);
1571 }
1572 candidate = align_up(vma.range.end.max(limit.start))?;
1573 if candidate >= limit.end {
1574 return None;
1575 }
1576 }
1577 candidate
1578 .checked_add(size)
1579 .is_some_and(|end| end <= limit.end)
1580 .then_some(candidate)
1581 }
1582
1583 pub(super) fn insert_entry(&self, entry: Arc<VmaEntry>) -> Option<Self> {
1584 insert_node(self.root.clone(), entry)
1585 .ok()
1586 .map(|root| Self { root: Some(root) })
1587 }
1588
1589 fn group_for_descriptor(&self, descriptor: VmaDescriptor) -> Arc<MappingGroup> {
1590 self.iter()
1591 .find(|candidate| same_mapping_group(candidate, descriptor))
1592 .map_or_else(
1593 || {
1594 MappingGroup::new(
1595 descriptor.mapping,
1596 descriptor.source,
1597 descriptor.page_policy,
1598 )
1599 },
1600 |candidate| candidate.group.clone(),
1601 )
1602 }
1603
1604 #[allow(clippy::too_many_arguments)]
1605 pub(super) fn prepare_mapping_entry(
1606 &self,
1607 range: VirtAddrRange,
1608 rights: MappingRights,
1609 reported_rights: MappingRights,
1610 max_rights: MappingRights,
1611 huge_page_advice: HugePageAdvice,
1612 lock_mode: VmaLockMode,
1613 advice_policy: VmaAdvicePolicy,
1614 operation: MappingOperation,
1615 ) -> Option<Arc<VmaEntry>> {
1616 if range.is_empty() {
1617 return None;
1618 }
1619 let descriptor = operation.vma_descriptor(range.start);
1620 Some(VmaEntry::new(
1621 VmaSnapshot {
1622 id: allocate_vma_id(),
1623 range,
1624 rights,
1625 reported_rights,
1626 max_rights,
1627 group: self.group_for_descriptor(descriptor),
1628 source_offset: descriptor.source_offset,
1629 huge_page_advice,
1630 lock_mode,
1631 advice_policy,
1632 },
1633 operation,
1634 ))
1635 }
1636
1637 pub(super) fn with_mapping_entry(&self, entry: Arc<VmaEntry>, replace: bool) -> Option<Self> {
1640 let range = entry.range();
1641 let base = if self.overlaps(range) {
1642 if !replace {
1643 return None;
1644 }
1645 self.without_range(range)?
1646 } else {
1647 self.clone()
1648 };
1649 base.insert_entry(entry)
1650 }
1651
1652 pub(super) fn with_extended_right(
1656 &self,
1657 address: VirtAddr,
1658 additional_size: usize,
1659 ) -> Option<Self> {
1660 let source = self.lookup_entry(address)?;
1661 let (without_source, removed) = self.remove_entry(source.start())?;
1662 if removed.snapshot.id != source.snapshot.id {
1663 return None;
1664 }
1665 without_source.insert_entry(source.extended_right(additional_size)?)
1666 }
1667
1668 pub(super) fn insert_with_operation(
1669 &self,
1670 vma: VmaSnapshot,
1671 operation: MappingOperation,
1672 ) -> Option<Self> {
1673 self.insert_entry(VmaEntry::new(vma, operation))
1674 }
1675
1676 fn remove_entry(&self, start: VirtAddr) -> Option<(Self, Arc<VmaEntry>)> {
1677 let (root, removed) = remove_node(self.root.clone(), start);
1678 removed.map(|removed| (Self { root }, removed))
1679 }
1680
1681 pub fn remove(&self, start: VirtAddr) -> Option<(Self, Arc<VmaSnapshot>)> {
1682 self.remove_entry(start)
1683 .map(|(map, entry)| (map, entry.snapshot.clone()))
1684 }
1685
1686 pub fn iter(&self) -> impl Iterator<Item = Arc<VmaSnapshot>> {
1687 let mut values = Vec::new();
1688 let mut stack = Vec::new();
1689 let mut node = self.root.clone();
1690 while node.is_some() || !stack.is_empty() {
1691 while let Some(current) = node {
1692 node = current.left.clone();
1693 stack.push(current);
1694 }
1695 let Some(current) = stack.pop() else {
1696 break;
1697 };
1698 values.push(current.entry.snapshot.clone());
1699 node = current.right.clone();
1700 }
1701 values.into_iter()
1702 }
1703
1704 pub(super) fn iter_entries(&self) -> impl Iterator<Item = Arc<VmaEntry>> {
1705 let mut values = Vec::new();
1706 let mut stack = Vec::new();
1707 let mut node = self.root.clone();
1708 while node.is_some() || !stack.is_empty() {
1709 while let Some(current) = node {
1710 node = current.left.clone();
1711 stack.push(current);
1712 }
1713 let Some(current) = stack.pop() else {
1714 break;
1715 };
1716 values.push(current.entry.clone());
1717 node = current.right.clone();
1718 }
1719 values.into_iter()
1720 }
1721
1722 pub(super) fn overlaps(&self, range: VirtAddrRange) -> bool {
1723 self.lookup_entry(range.start).is_some()
1724 || self
1725 .iter_entries()
1726 .any(|entry| entry.start() >= range.start && entry.start() < range.end)
1727 }
1728}
1729
1730fn same_mapping_group(candidate: &VmaSnapshot, descriptor: VmaDescriptor) -> bool {
1731 candidate.group.id == descriptor.mapping
1732 && candidate.group.source.as_ref() == &descriptor.source
1733 && candidate.group.page_policy == descriptor.page_policy
1734}
1735
1736pub fn allocate_vma_id() -> VmaId {
1738 static NEXT_ID: AtomicU64 = AtomicU64::new(1);
1739 VmaId::new(NEXT_ID.fetch_add(1, Ordering::Relaxed))
1740}
1741
1742pub fn allocate_mapping_id() -> MappingId {
1744 static NEXT_ID: AtomicU64 = AtomicU64::new(1);
1745 MappingId::new(NEXT_ID.fetch_add(1, Ordering::Relaxed))
1746}
1747
1748#[cfg(test)]
1749mod tests {
1750 use super::*;
1751
1752 fn snapshot(start: usize, size: usize) -> (VmaSnapshot, MappingOperation) {
1753 let start = VirtAddr::from_usize(start);
1754 let operation =
1755 MappingOperation::new_alloc(start, ax_memory_addr::PAGE_SIZE_4K, "vma-test");
1756 (
1757 VmaSnapshot {
1758 id: allocate_vma_id(),
1759 range: VirtAddrRange::from_start_size(start, size),
1760 rights: MappingFlags::READ,
1761 reported_rights: MappingFlags::READ,
1762 max_rights: MappingFlags::READ,
1763 group: MappingGroup::new(
1764 MappingId::new(1),
1765 MappingSource::Anonymous(AnonymousSource),
1766 PageSizePolicy::Base,
1767 ),
1768 source_offset: PageOffset::ZERO,
1769 huge_page_advice: HugePageAdvice::Default,
1770 lock_mode: VmaLockMode::Unlocked,
1771 advice_policy: VmaAdvicePolicy::default(),
1772 },
1773 operation,
1774 )
1775 }
1776
1777 fn insert(map: &VmaMap, start: usize, size: usize) -> Option<VmaMap> {
1778 let (snapshot, operation) = snapshot(start, size);
1779 map.insert_with_operation(snapshot, operation)
1780 }
1781
1782 #[cfg(all(test, not(axtest)))]
1784 fn map_of(count: usize) -> VmaMap {
1785 let mut map = VmaMap::default();
1786 for i in 0..count {
1787 map = insert(&map, 0x1000 + i * 0x2000, 0x1000).unwrap();
1788 }
1789 map
1790 }
1791
1792 #[cfg(all(test, not(axtest)))]
1793 #[test]
1794 fn a_range_lookup_walks_the_search_path_not_the_whole_tree() {
1795 let map = map_of(256);
1796 let target = 0x1000 + 128 * 0x2000;
1797 let range = VirtAddrRange::from_start_size(VirtAddr::from_usize(target), 0x1000);
1798
1799 let mut visited = 0;
1800 let mut found = 0;
1801 let mut first = None;
1802 VmaMap::visit_overlapping(&map.root, range, &mut visited, &mut |entry| {
1803 found += 1;
1804 first.get_or_insert(entry.snapshot.range.start);
1805 true
1806 });
1807
1808 assert_eq!(found, 1);
1809 assert_eq!(first, Some(VirtAddr::from_usize(target)));
1810 assert!(
1811 visited <= 24,
1812 "a one-VMA lookup walked {visited} nodes of a 256-VMA tree",
1813 );
1814 }
1815
1816 #[cfg(all(test, not(axtest)))]
1817 #[test]
1818 fn a_spanning_lookup_returns_every_intersecting_vma_in_order() {
1819 let map = map_of(64);
1820 let start = 0x1000 + 10 * 0x2000;
1821 let end = 0x1000 + 20 * 0x2000;
1822 let range = VirtAddrRange::from_start_size(VirtAddr::from_usize(start), end - start);
1823
1824 let found = map.lookup_range(range);
1825
1826 assert_eq!(found.len(), 10);
1827 for (offset, vma) in found.iter().enumerate() {
1828 assert_eq!(
1829 vma.range.start,
1830 VirtAddr::from_usize(0x1000 + (10 + offset) * 0x2000),
1831 );
1832 }
1833 }
1834
1835 #[cfg(all(test, not(axtest)))]
1836 #[test]
1837 fn a_lookup_starting_inside_a_vma_still_reaches_its_predecessor() {
1838 let map = map_of(32);
1839 let start = 0x1000 + 7 * 0x2000 + 0x800;
1842 let range = VirtAddrRange::from_start_size(VirtAddr::from_usize(start), 0x400);
1843
1844 let found = map.lookup_range(range);
1845
1846 assert_eq!(found.len(), 1);
1847 assert_eq!(
1848 found[0].range.start,
1849 VirtAddr::from_usize(0x1000 + 7 * 0x2000),
1850 );
1851 }
1852
1853 #[test]
1854 fn snapshots_are_not_mutated_by_path_copy() {
1855 let first = insert(&VmaMap::default(), 0x1000, 0x1000).unwrap();
1856 let second = insert(&first, 0x3000, 0x1000).unwrap();
1857 assert!(first.lookup(VirtAddr::from_usize(0x3000)).is_none());
1858 assert!(second.lookup(VirtAddr::from_usize(0x3000)).is_some());
1859 }
1860
1861 #[test]
1862 fn remove_is_path_copy_and_rejects_overlap() {
1863 let first = insert(&VmaMap::default(), 0x1000, 0x2000).unwrap();
1864 assert!(insert(&first, 0x2000, 0x1000).is_none());
1865 let second = insert(&first, 0x5000, 0x1000).unwrap();
1866 let (third, removed) = second.remove(VirtAddr::from_usize(0x1000)).unwrap();
1867 assert_eq!(removed.range.start, VirtAddr::from_usize(0x1000));
1868 assert!(second.lookup(VirtAddr::from_usize(0x1800)).is_some());
1869 assert!(third.lookup(VirtAddr::from_usize(0x1800)).is_none());
1870 assert!(third.lookup(VirtAddr::from_usize(0x5000)).is_some());
1871 }
1872
1873 #[test]
1874 fn partial_lock_is_path_copied_and_unlock_coalesces_fragments() {
1875 let original = insert(&VmaMap::default(), 0x1000, 0x3000).unwrap();
1876 let middle = VirtAddrRange::from_start_size(VirtAddr::from_usize(0x2000), 0x1000);
1877 let locked = original
1878 .with_lock_mode(middle, VmaLockMode::LockOnFault)
1879 .unwrap();
1880
1881 assert_eq!(original.len(), 1);
1882 assert_eq!(locked.len(), 3);
1883 assert_eq!(
1884 original
1885 .lookup(VirtAddr::from_usize(0x2000))
1886 .unwrap()
1887 .lock_mode,
1888 VmaLockMode::Unlocked
1889 );
1890 assert_eq!(
1891 locked
1892 .lookup(VirtAddr::from_usize(0x1000))
1893 .unwrap()
1894 .lock_mode,
1895 VmaLockMode::Unlocked
1896 );
1897 assert_eq!(
1898 locked
1899 .lookup(VirtAddr::from_usize(0x2000))
1900 .unwrap()
1901 .lock_mode,
1902 VmaLockMode::LockOnFault
1903 );
1904 assert_eq!(
1905 locked
1906 .lookup(VirtAddr::from_usize(0x3000))
1907 .unwrap()
1908 .lock_mode,
1909 VmaLockMode::Unlocked
1910 );
1911
1912 let unlocked = locked
1913 .with_lock_mode(middle, VmaLockMode::Unlocked)
1914 .unwrap();
1915 assert_eq!(unlocked.len(), 1);
1916 assert_eq!(
1917 unlocked.lookup(VirtAddr::from_usize(0x2000)).unwrap().range,
1918 VirtAddrRange::from_start_size(VirtAddr::from_usize(0x1000), 0x3000)
1919 );
1920 }
1921
1922 #[test]
1923 fn unmap_successor_carves_middle_and_preserves_source_coordinates() {
1924 let original = insert(&VmaMap::default(), 0x1000, 0x4000).unwrap();
1925 let successor = original
1926 .without_range(VirtAddrRange::from_start_size(
1927 VirtAddr::from_usize(0x2000),
1928 0x2000,
1929 ))
1930 .unwrap();
1931
1932 assert_eq!(original.len(), 1);
1933 assert_eq!(successor.len(), 2);
1934 let head = successor.lookup(VirtAddr::from_usize(0x1000)).unwrap();
1935 let tail = successor.lookup(VirtAddr::from_usize(0x4000)).unwrap();
1936 assert_eq!(
1937 head.range,
1938 VirtAddrRange::from_start_size(VirtAddr::from_usize(0x1000), 0x1000)
1939 );
1940 assert_eq!(head.source_offset, PageOffset::ZERO);
1941 assert_eq!(
1942 tail.range,
1943 VirtAddrRange::from_start_size(VirtAddr::from_usize(0x4000), 0x1000)
1944 );
1945 assert_eq!(tail.source_offset, PageOffset::new(0x3000));
1946 assert!(successor.lookup(VirtAddr::from_usize(0x2000)).is_none());
1947 }
1948
1949 #[test]
1950 fn protection_successor_changes_only_the_intersection() {
1951 let original = insert(&VmaMap::default(), 0x1000, 0x4000).unwrap();
1952 let successor = original
1953 .with_permissions(
1954 VirtAddrRange::from_start_size(VirtAddr::from_usize(0x2000), 0x1000),
1955 MappingFlags::READ | MappingFlags::WRITE,
1956 MappingFlags::READ,
1957 )
1958 .unwrap();
1959
1960 assert_eq!(original.len(), 1);
1961 assert_eq!(successor.len(), 3);
1962 assert_eq!(
1963 successor
1964 .lookup(VirtAddr::from_usize(0x1000))
1965 .unwrap()
1966 .rights,
1967 MappingFlags::READ
1968 );
1969 let body = successor.lookup(VirtAddr::from_usize(0x2000)).unwrap();
1970 assert_eq!(body.rights, MappingFlags::READ | MappingFlags::WRITE);
1971 assert_eq!(body.reported_rights, MappingFlags::READ);
1972 assert_eq!(
1973 successor
1974 .lookup(VirtAddr::from_usize(0x3000))
1975 .unwrap()
1976 .rights,
1977 MappingFlags::READ
1978 );
1979 assert_eq!(original.len(), 1);
1980 }
1981
1982 fn huge_page_advice_is_vma_local_and_path_copied_for_test() {
1983 let original = insert(&VmaMap::default(), 0x1000, 0x4000).unwrap();
1984 let advised = original
1985 .with_huge_page_advice(
1986 VirtAddrRange::from_start_size(VirtAddr::from_usize(0x2000), 0x1000),
1987 HugePageAdvice::Avoid,
1988 )
1989 .unwrap();
1990
1991 assert_eq!(original.len(), 1);
1992 assert_eq!(
1993 original
1994 .lookup(VirtAddr::from_usize(0x2000))
1995 .unwrap()
1996 .huge_page_advice,
1997 HugePageAdvice::Default
1998 );
1999 assert_eq!(advised.len(), 3);
2000 assert_eq!(
2001 advised
2002 .lookup(VirtAddr::from_usize(0x1000))
2003 .unwrap()
2004 .huge_page_advice,
2005 HugePageAdvice::Default
2006 );
2007 assert_eq!(
2008 advised
2009 .lookup(VirtAddr::from_usize(0x2000))
2010 .unwrap()
2011 .huge_page_advice,
2012 HugePageAdvice::Avoid
2013 );
2014 assert_eq!(
2015 advised
2016 .lookup(VirtAddr::from_usize(0x3000))
2017 .unwrap()
2018 .huge_page_advice,
2019 HugePageAdvice::Default
2020 );
2021
2022 let restored = advised
2023 .with_huge_page_advice(
2024 VirtAddrRange::from_start_size(VirtAddr::from_usize(0x2000), 0x1000),
2025 HugePageAdvice::Default,
2026 )
2027 .unwrap();
2028 assert_eq!(restored.len(), 1);
2029 let restored_vma = restored.lookup(VirtAddr::from_usize(0x3000)).unwrap();
2030 assert_eq!(restored_vma.range, original.iter().next().unwrap().range);
2031 assert_eq!(restored_vma.huge_page_advice, HugePageAdvice::Default);
2032 assert_eq!(advised.len(), 3);
2033 }
2034
2035 fn process_thp_disable_overrides_vma_preference_for_test() {
2036 assert_eq!(
2037 PageSizePolicy::TRANSPARENT_2M
2038 .fault_leaf_size(HugePageAdvice::Prefer, TransparentHugePageMode::Disabled,),
2039 Some(ax_memory_addr::PAGE_SIZE_4K)
2040 );
2041 assert_eq!(
2042 PageSizePolicy::TRANSPARENT_2M.fault_leaf_size(
2043 HugePageAdvice::Prefer,
2044 TransparentHugePageMode::ExceptAdvised,
2045 ),
2046 Some(ax_memory_addr::PAGE_SIZE_2M)
2047 );
2048 }
2049
2050 #[cfg(all(test, not(axtest)))]
2051 #[test]
2052 fn huge_page_advice_is_vma_local_and_path_copied() {
2053 huge_page_advice_is_vma_local_and_path_copied_for_test();
2054 }
2055
2056 #[cfg(all(test, not(axtest)))]
2057 #[test]
2058 fn process_thp_disable_overrides_vma_preference() {
2059 process_thp_disable_overrides_vma_preference_for_test();
2060 }
2061
2062 #[cfg(all(test, axtest))]
2063 #[axtest::axtest]
2064 fn huge_page_advice_is_vma_local_and_path_copied() {
2065 huge_page_advice_is_vma_local_and_path_copied_for_test();
2066 }
2067
2068 #[cfg(all(test, axtest))]
2069 #[axtest::axtest]
2070 fn process_thp_disable_overrides_vma_preference() {
2071 process_thp_disable_overrides_vma_preference_for_test();
2072 }
2073}