1use alloc::{boxed::Box, sync::Arc};
4#[cfg(feature = "uspace")]
5use core::sync::atomic::AtomicBool;
6#[cfg(feature = "qperf-metrics")]
7use core::sync::atomic::AtomicU64;
8use core::{
9 marker::PhantomData,
10 mem::align_of,
11 ptr,
12 sync::atomic::{AtomicU32, AtomicUsize, Ordering},
13};
14
15use ax_hal::percpu::CpuPin;
16use ax_memory_addr::PhysAddr;
17use ax_task::{
18 runtime::{
19 RuntimeStatus,
20 resource::{
21 AddressSpaceDestroyOutcome, AddressSpaceHandle, AddressSpaceMembarrierId,
22 AddressSpaceMembarrierState, AddressSpaceReclaimArmOutcome, AddressSpaceToken,
23 MembarrierRegistration, MembarrierRegistrationPhase,
24 },
25 },
26 thread::TaskError,
27};
28
29use super::mm_activation::UserAddressSpaceOwner;
30#[cfg(feature = "uspace")]
31use super::mm_activation::{AddressSpaceSwitchProof, SchedulerAddressSpaceActivation};
32#[cfg(feature = "uspace")]
33use super::with_current_cpu_pin;
34
35trait TaskAddressSpaceOwner: Send + Sync {
37 fn detach_from_task(&self);
40
41 #[cfg(feature = "uspace")]
42 fn prepare_activation(
43 &self,
44 _cpu: usize,
45 ) -> Result<Option<SchedulerAddressSpaceActivation>, RuntimeStatus> {
46 Ok(None)
47 }
48}
49
50struct ManagedTaskAddressSpaceOwner<T>(T);
51
52impl<T: UserAddressSpaceOwner> TaskAddressSpaceOwner for ManagedTaskAddressSpaceOwner<T> {
53 fn detach_from_task(&self) {
54 self.0.detach_from_task();
55 }
56
57 #[cfg(feature = "uspace")]
58 fn prepare_activation(
59 &self,
60 cpu: usize,
61 ) -> Result<Option<SchedulerAddressSpaceActivation>, RuntimeStatus> {
62 self.0
63 .prepare_activation(cpu)
64 .map(Some)
65 .map_err(|_| RuntimeStatus::InvalidHandle)
66 }
67}
68
69struct RetainedTaskAddressSpaceOwner<T>(T);
70
71impl<T: Send + Sync> TaskAddressSpaceOwner for RetainedTaskAddressSpaceOwner<T> {
72 fn detach_from_task(&self) {}
73}
74
75struct DetachableTaskAddressSpaceOwner<T> {
76 owner: T,
77 detached: core::sync::atomic::AtomicBool,
78 detach: fn(&T),
79}
80
81impl<T: Send + Sync> TaskAddressSpaceOwner for DetachableTaskAddressSpaceOwner<T> {
82 fn detach_from_task(&self) {
83 if !self.detached.swap(true, Ordering::AcqRel) {
84 (self.detach)(&self.owner);
85 }
86 }
87}
88
89struct RuntimeAddressSpace {
90 active_leases: AtomicUsize,
95 reclaim_waiting: AtomicUsize,
96 cpu_state: Arc<AddressSpaceCpuState>,
97 _owner: Box<dyn TaskAddressSpaceOwner>,
98}
99
100#[cfg(feature = "uspace")]
101impl RuntimeAddressSpace {
102 fn root(&self) -> usize {
103 self.cpu_state.root()
104 }
105}
106
107const _: () = assert!(crate::CPU_CAPACITY <= usize::BITS as usize);
108
109#[cfg(feature = "qperf-metrics")]
110static ACTIVE_MM_SAME_ACTIVATIONS: AtomicU64 = AtomicU64::new(0);
111#[cfg(feature = "qperf-metrics")]
112static ACTIVE_MM_DIFFERENT_ACTIVATIONS: AtomicU64 = AtomicU64::new(0);
113#[cfg(feature = "qperf-metrics")]
114static ACTIVE_MM_KERNEL_LAZY_ACTIVATIONS: AtomicU64 = AtomicU64::new(0);
115#[cfg(feature = "qperf-metrics")]
116static ACTIVE_MM_HARDWARE_ROOT_WRITES: AtomicU64 = AtomicU64::new(0);
117#[cfg(feature = "qperf-metrics")]
118static ACTIVE_MM_LEASE_ACTIVATIONS: AtomicU64 = AtomicU64::new(0);
119#[cfg(feature = "qperf-metrics")]
120static ACTIVE_MM_LEASE_DEACTIVATIONS: AtomicU64 = AtomicU64::new(0);
121#[cfg(feature = "qperf-metrics")]
122static ACTIVE_MM_RECLAIM_READY: AtomicU64 = AtomicU64::new(0);
123#[cfg(feature = "qperf-metrics")]
124static ACTIVE_MM_RECLAIM_DESTROYED: AtomicU64 = AtomicU64::new(0);
125
126#[cfg(feature = "qperf-metrics")]
127#[derive(Clone, Copy, Debug, Default, Eq, PartialEq)]
128pub(super) struct QperfAddressSpaceMetricsSnapshot {
129 pub(super) same_activations: u64,
130 pub(super) different_activations: u64,
131 pub(super) kernel_lazy_activations: u64,
132 pub(super) hardware_root_writes: u64,
133 pub(super) lease_activations: u64,
134 pub(super) lease_deactivations: u64,
135 pub(super) reclaim_ready: u64,
136 pub(super) reclaim_destroyed: u64,
137}
138
139#[cfg(feature = "qperf-metrics")]
140pub(super) fn qperf_address_space_metrics_snapshot() -> QperfAddressSpaceMetricsSnapshot {
141 QperfAddressSpaceMetricsSnapshot {
142 same_activations: ACTIVE_MM_SAME_ACTIVATIONS.load(Ordering::Relaxed),
143 different_activations: ACTIVE_MM_DIFFERENT_ACTIVATIONS.load(Ordering::Relaxed),
144 kernel_lazy_activations: ACTIVE_MM_KERNEL_LAZY_ACTIVATIONS.load(Ordering::Relaxed),
145 hardware_root_writes: ACTIVE_MM_HARDWARE_ROOT_WRITES.load(Ordering::Relaxed),
146 lease_activations: ACTIVE_MM_LEASE_ACTIVATIONS.load(Ordering::Relaxed),
147 lease_deactivations: ACTIVE_MM_LEASE_DEACTIVATIONS.load(Ordering::Relaxed),
148 reclaim_ready: ACTIVE_MM_RECLAIM_READY.load(Ordering::Relaxed),
149 reclaim_destroyed: ACTIVE_MM_RECLAIM_DESTROYED.load(Ordering::Relaxed),
150 }
151}
152
153pub struct AddressSpaceCpuState {
160 root: usize,
161 active_mask: ActiveCpuMask,
162 membarrier_bits: AtomicU32,
163}
164
165enum ActiveCpuMask {
166 Runtime(AtomicUsize),
167 Mm(Arc<AtomicUsize>),
168}
169
170impl AddressSpaceCpuState {
171 pub fn new(root: PhysAddr) -> Self {
173 Self {
174 root: root.as_usize(),
175 active_mask: ActiveCpuMask::Runtime(AtomicUsize::new(0)),
176 membarrier_bits: AtomicU32::new(0),
177 }
178 }
179
180 pub fn with_mm_active_mask(root: PhysAddr, active_mask: Arc<AtomicUsize>) -> Self {
183 Self {
184 root: root.as_usize(),
185 active_mask: ActiveCpuMask::Mm(active_mask),
186 membarrier_bits: AtomicU32::new(0),
187 }
188 }
189
190 fn matches_root(&self, root: PhysAddr) -> bool {
191 self.root() == root.as_usize()
192 }
193
194 fn root(&self) -> usize {
195 self.root
196 }
197
198 pub fn active_mask(&self) -> usize {
200 match &self.active_mask {
201 ActiveCpuMask::Runtime(mask) => mask.load(Ordering::Acquire),
202 ActiveCpuMask::Mm(mask) => mask.load(Ordering::Acquire),
203 }
204 }
205
206 fn membarrier_state(this: &Arc<Self>) -> AddressSpaceMembarrierState {
207 let raw = Arc::as_ptr(this).expose_provenance();
208 let identity = unsafe { AddressSpaceMembarrierId::from_raw(raw) };
211 let bits = this.membarrier_bits.load(Ordering::SeqCst);
212 unsafe { AddressSpaceMembarrierState::new(identity, bits) }
215 }
216
217 fn update_membarrier_state(
218 this: &Arc<Self>,
219 registration: MembarrierRegistration,
220 phase: MembarrierRegistrationPhase,
221 ) -> AddressSpaceMembarrierState {
222 let bit = match phase {
223 MembarrierRegistrationPhase::Begin => registration.requested_bit(),
224 MembarrierRegistrationPhase::Complete => {
225 assert!(
226 this.membarrier_bits.load(Ordering::SeqCst) & registration.requested_bit() != 0,
227 "membarrier registration completed before its requested phase"
228 );
229 registration.ready_bit()
230 }
231 };
232 this.membarrier_bits.fetch_or(bit, Ordering::SeqCst);
233 Self::membarrier_state(this)
234 }
235
236 #[cfg(any(feature = "uspace", test))]
237 fn cpu_bit(cpu_id: usize) -> usize {
238 1usize.checked_shl(cpu_id as u32).unwrap_or_else(|| {
239 panic!("CPU {cpu_id} cannot be represented in an address-space mask")
240 })
241 }
242
243 #[cfg(any(feature = "uspace", test))]
244 fn activate(&self, cpu_id: usize) {
245 if let ActiveCpuMask::Runtime(mask) = &self.active_mask {
246 mask.fetch_or(Self::cpu_bit(cpu_id), Ordering::Release);
247 }
248 }
249
250 #[cfg(any(feature = "uspace", test))]
251 fn deactivate(&self, cpu_id: usize) {
252 if let ActiveCpuMask::Runtime(mask) = &self.active_mask {
253 mask.fetch_and(!Self::cpu_bit(cpu_id), Ordering::Release);
254 }
255 }
256}
257
258pub struct TaskAddressSpace(Option<AddressSpaceToken>);
260
261impl TaskAddressSpace {
262 pub fn new(root: PhysAddr, owner: impl Send + Sync + 'static) -> Result<Self, TaskError> {
264 Self::new_with_owner(
265 root,
266 Arc::new(AddressSpaceCpuState::new(root)),
267 Box::new(RetainedTaskAddressSpaceOwner(owner)),
268 )
269 }
270
271 pub fn new_with_task_detach<T: Send + Sync + 'static>(
278 root: PhysAddr,
279 cpu_state: Arc<AddressSpaceCpuState>,
280 owner: T,
281 detach: fn(&T),
282 ) -> Result<Self, TaskError> {
283 Self::new_with_owner(
284 root,
285 cpu_state,
286 Box::new(DetachableTaskAddressSpaceOwner {
287 owner,
288 detached: core::sync::atomic::AtomicBool::new(false),
289 detach,
290 }),
291 )
292 }
293
294 pub fn new_managed<T: UserAddressSpaceOwner + 'static>(
297 root: PhysAddr,
298 cpu_state: Arc<AddressSpaceCpuState>,
299 owner: T,
300 ) -> Result<Self, TaskError> {
301 Self::new_with_owner(
302 root,
303 cpu_state,
304 Box::new(ManagedTaskAddressSpaceOwner(owner)),
305 )
306 }
307
308 fn new_with_owner(
309 root: PhysAddr,
310 cpu_state: Arc<AddressSpaceCpuState>,
311 owner: Box<dyn TaskAddressSpaceOwner>,
312 ) -> Result<Self, TaskError> {
313 if root.as_usize() == 0 || !cpu_state.matches_root(root) {
314 return Err(TaskError::InvalidRuntimeHandle);
315 }
316 let address_space = Box::new(RuntimeAddressSpace {
317 active_leases: AtomicUsize::new(0),
318 reclaim_waiting: AtomicUsize::new(0),
319 cpu_state,
320 _owner: owner,
321 });
322 let raw = Box::into_raw(address_space).expose_provenance();
323 Ok(Self(Some(unsafe { AddressSpaceToken::from_raw(raw) })))
326 }
327
328 pub(super) fn handle(&self) -> AddressSpaceHandle {
329 self.0
330 .as_ref()
331 .unwrap_or_else(|| unreachable!("address-space token already transferred"))
332 .handle()
333 }
334
335 #[cfg(feature = "uspace")]
336 pub(super) fn token_mut(&mut self) -> &mut AddressSpaceToken {
337 self.0
338 .as_mut()
339 .unwrap_or_else(|| unreachable!("address-space token already transferred"))
340 }
341
342 pub(super) fn take_token(&mut self) -> AddressSpaceToken {
343 self.0
344 .take()
345 .unwrap_or_else(|| unreachable!("address-space token already transferred"))
346 }
347}
348
349fn detach_runtime_address_space_owner(address_space: AddressSpaceHandle) {
350 runtime_address_space(address_space)
351 .unwrap_or_else(|_| panic!("address-space detach received an invalid owning handle"))
352 ._owner
353 .detach_from_task();
354}
355
356#[cfg(any(feature = "uspace", test))]
357fn detach_replaced_address_space_owner(address_space: AddressSpaceHandle) {
358 detach_runtime_address_space_owner(address_space);
359}
360
361impl Drop for TaskAddressSpace {
362 fn drop(&mut self) {
363 let Some(address_space) = self.0.take() else {
364 return;
365 };
366 detach_runtime_address_space_owner(address_space.handle());
367 let outcome = destroy_runtime_address_space(address_space.handle());
368 assert_eq!(
369 outcome,
370 AddressSpaceDestroyOutcome::Released,
371 "unpublished address space retained an active CPU lease"
372 );
373 }
374}
375
376#[ax_percpu::def_percpu]
377static ACTIVE_ADDRESS_SPACE: usize = 0;
378
379#[ax_percpu::def_percpu]
380#[cfg(feature = "uspace")]
381static ACTIVE_MM_ACTIVATION: Option<SchedulerAddressSpaceActivation> = None;
382
383#[cfg(feature = "uspace")]
384fn replace_active_activation(
385 pin: &CpuPin<'_>,
386 next: Option<SchedulerAddressSpaceActivation>,
387) -> Option<SchedulerAddressSpaceActivation> {
388 debug_assert!(!ax_hal::asm::irqs_enabled());
389 unsafe {
392 ax_percpu::with_exclusive_cpu(pin, |exclusive| {
393 ACTIVE_MM_ACTIVATION
394 .with_current_mut(exclusive, |active| core::mem::replace(active, next))
395 })
396 }
397}
398
399#[cfg(feature = "uspace")]
400fn install_mm_identity(installed: ax_hal::context::InstalledAddressSpace) {
401 unsafe { ax_hal::asm::install_user_address_space(installed) };
404 #[cfg(feature = "qperf-metrics")]
405 ACTIVE_MM_HARDWARE_ROOT_WRITES.fetch_add(1, Ordering::Relaxed);
406}
407
408#[ax_percpu::def_percpu]
414#[cfg(feature = "uspace")]
415static CONTEXT_SWITCH_RECLAIM_READY: AtomicBool = AtomicBool::new(false);
416
417fn runtime_address_space(
418 address_space: AddressSpaceHandle,
419) -> Result<&'static RuntimeAddressSpace, RuntimeStatus> {
420 let raw = address_space.into_raw();
421 if raw == 0 || !raw.is_multiple_of(align_of::<RuntimeAddressSpace>()) {
422 return Err(RuntimeStatus::InvalidHandle);
423 }
424 let address_space = ptr::with_exposed_provenance::<RuntimeAddressSpace>(raw);
425 Ok(unsafe { &*address_space })
428}
429
430#[cfg(feature = "uspace")]
431fn offline_kernel_root() -> usize {
432 if cfg!(any(target_arch = "x86_64", target_arch = "riscv64")) {
433 unsafe { with_current_cpu_pin(super::bootstrap::offline_kernel_root) }
436 } else {
437 0
440 }
441}
442
443#[cfg(feature = "uspace")]
444pub(super) fn current_hardware_root() -> usize {
445 ax_hal::asm::read_user_page_table().as_usize()
446}
447
448#[cfg(feature = "uspace")]
449pub(super) fn validate_current_user_address_space(
450 pin: &CpuPin<'_>,
451 selected: AddressSpaceHandle,
452) -> Result<(), RuntimeStatus> {
453 if selected.is_none() {
454 return Err(RuntimeStatus::InvalidHandle);
455 }
456 let active_raw = ACTIVE_ADDRESS_SPACE.read_current(pin);
457 if active_raw == 0 {
458 return Err(RuntimeStatus::InvalidHandle);
459 }
460 let active = runtime_address_space(unsafe { AddressSpaceHandle::from_raw(active_raw) })?;
463 let selected = runtime_address_space(selected)?;
464 let cpu_bit = AddressSpaceCpuState::cpu_bit(pin.area().cpu_index().as_usize());
465 if !same_logical_address_space(active, selected)
466 || active.active_leases.load(Ordering::Acquire) == 0
467 || active.cpu_state.active_mask() & cpu_bit == 0
468 || current_hardware_root() != active.root()
469 {
470 return Err(RuntimeStatus::InvalidHandle);
471 }
472 Ok(())
473}
474
475#[cfg(any(feature = "uspace", test))]
476#[derive(Clone, Copy, Debug, Eq, PartialEq)]
477enum HardwareAddressSpaceTransition {
478 SameAddressSpace,
479 DifferentAddressSpace,
480}
481
482#[cfg(any(feature = "uspace", test))]
483fn hardware_root_install_required(
484 current_root: usize,
485 next_root: usize,
486 transition: HardwareAddressSpaceTransition,
487) -> bool {
488 current_root != next_root || transition == HardwareAddressSpaceTransition::DifferentAddressSpace
489}
490
491#[cfg(feature = "uspace")]
492fn install_hardware_root(root: usize, transition: HardwareAddressSpaceTransition) {
493 if hardware_root_install_required(current_hardware_root(), root, transition) {
494 let root = ax_memory_addr::PhysAddr::from(root);
495 unsafe { ax_hal::asm::write_user_page_table(root) };
498 #[cfg(feature = "qperf-metrics")]
499 ACTIVE_MM_HARDWARE_ROOT_WRITES.fetch_add(1, Ordering::Relaxed);
500 #[cfg(not(target_arch = "x86_64"))]
506 ax_hal::asm::flush_tlb(None);
507 }
508}
509
510#[cfg(feature = "uspace")]
511fn enter_lazy_kernel_address_space() {
512 #[cfg(target_arch = "aarch64")]
517 install_hardware_root(0, HardwareAddressSpaceTransition::DifferentAddressSpace);
518}
519
520#[cfg(feature = "uspace")]
521fn commit_user_address_space_activation(
522 cpu_id: usize,
523 previous_raw: usize,
524 previous: Option<&RuntimeAddressSpace>,
525 next_raw: usize,
526 next: &RuntimeAddressSpace,
527 install_root: impl FnOnce(usize, HardwareAddressSpaceTransition),
528 publish_active: impl FnOnce(usize),
529) -> bool {
530 let same_address_space = next_raw == previous_raw
531 || previous.is_some_and(|previous| same_logical_address_space(previous, next));
532 if same_address_space {
533 #[cfg(feature = "qperf-metrics")]
534 ACTIVE_MM_SAME_ACTIVATIONS.fetch_add(1, Ordering::Relaxed);
535 debug_assert_eq!(
536 previous.map(RuntimeAddressSpace::root),
537 Some(next.root()),
538 "one address-space CPU tracker cannot describe different roots"
539 );
540 install_root(
546 next.root(),
547 HardwareAddressSpaceTransition::SameAddressSpace,
548 );
549 return false;
550 }
551 #[cfg(feature = "qperf-metrics")]
552 ACTIVE_MM_DIFFERENT_ACTIVATIONS.fetch_add(1, Ordering::Relaxed);
553 next.active_leases.fetch_add(1, Ordering::AcqRel);
554 next.cpu_state.activate(cpu_id);
555 #[cfg(feature = "qperf-metrics")]
556 ACTIVE_MM_LEASE_ACTIVATIONS.fetch_add(1, Ordering::Relaxed);
557 install_root(
558 next.root(),
559 HardwareAddressSpaceTransition::DifferentAddressSpace,
560 );
561 publish_active(next_raw);
562 if let Some(previous) = previous {
563 previous.cpu_state.deactivate(cpu_id);
564 #[cfg(feature = "qperf-metrics")]
565 ACTIVE_MM_LEASE_DEACTIVATIONS.fetch_add(1, Ordering::Relaxed);
566 release_active_cpu(previous)
567 } else {
568 false
569 }
570}
571
572#[cfg(feature = "uspace")]
573fn same_logical_address_space(first: &RuntimeAddressSpace, second: &RuntimeAddressSpace) -> bool {
574 Arc::ptr_eq(&first.cpu_state, &second.cpu_state)
575}
576
577enum PreparedAddressSpaceAction {
578 KernelLazy,
579 #[cfg(all(feature = "uspace", not(target_arch = "aarch64")))]
580 SameUser,
581 #[cfg(feature = "uspace")]
582 User {
583 next_raw: usize,
584 next: &'static RuntimeAddressSpace,
585 activation: Option<SchedulerAddressSpaceActivation>,
586 },
587}
588
589#[derive(Clone, Copy, Debug, Eq, PartialEq)]
590pub(super) enum AddressSpaceTransitionPhase {
591 #[cfg(feature = "uspace")]
592 CurrentTask,
593 ContextSwitch,
594}
595
596#[must_use = "a prepared address-space switch must be committed with its context switch"]
603pub(super) struct PreparedAddressSpaceSwitch<'pin, 'cpu> {
604 #[cfg(feature = "uspace")]
605 pin: &'pin CpuPin<'cpu>,
606 phase: AddressSpaceTransitionPhase,
607 #[cfg(feature = "uspace")]
608 previous_raw: usize,
609 #[cfg(feature = "uspace")]
610 previous: Option<&'static RuntimeAddressSpace>,
611 action: PreparedAddressSpaceAction,
612 _not_send_or_sync: PhantomData<(&'pin CpuPin<'cpu>, *mut ())>,
613}
614
615impl PreparedAddressSpaceSwitch<'_, '_> {
616 #[inline(always)]
618 pub(super) fn commit(self) {
619 #[cfg(feature = "uspace")]
620 let pin = self.pin;
621 match self.phase {
622 #[cfg(feature = "uspace")]
623 AddressSpaceTransitionPhase::CurrentTask => assert!(
624 !ax_hal::asm::irqs_enabled(),
625 "current-task address-space commit requires local IRQ exclusion"
626 ),
627 AddressSpaceTransitionPhase::ContextSwitch => {}
628 }
629 #[cfg(not(feature = "uspace"))]
630 debug_assert_eq!(
631 self.phase,
632 AddressSpaceTransitionPhase::ContextSwitch,
633 "kernel-only builds prepare address spaces only for scheduler switches"
634 );
635 #[cfg(feature = "uspace")]
636 assert_eq!(
637 ACTIVE_ADDRESS_SPACE.read_current(pin),
638 self.previous_raw,
639 "active address space changed after switch preparation"
640 );
641
642 match self.action {
643 PreparedAddressSpaceAction::KernelLazy => {
644 #[cfg(feature = "uspace")]
645 {
646 #[cfg(feature = "qperf-metrics")]
647 ACTIVE_MM_KERNEL_LAZY_ACTIVATIONS.fetch_add(1, Ordering::Relaxed);
648 enter_lazy_kernel_address_space();
649 }
650 }
651 #[cfg(all(feature = "uspace", not(target_arch = "aarch64")))]
652 PreparedAddressSpaceAction::SameUser => {}
653 #[cfg(feature = "uspace")]
654 PreparedAddressSpaceAction::User {
655 next_raw,
656 next,
657 mut activation,
658 } => {
659 let cpu_id = pin.area().cpu_index().as_usize();
660 let installed = activation
661 .as_ref()
662 .map(SchedulerAddressSpaceActivation::installed)
663 .or_else(|| {
664 if !self
665 .previous
666 .is_some_and(|previous| same_logical_address_space(previous, next))
667 {
668 return None;
669 }
670 ACTIVE_MM_ACTIVATION.with_current(pin, |active| {
671 active
672 .as_ref()
673 .map(SchedulerAddressSpaceActivation::installed)
674 })
675 });
676 let reclaim_ready = commit_user_address_space_activation(
677 pin.area().cpu_index().as_usize(),
678 self.previous_raw,
679 self.previous,
680 next_raw,
681 next,
682 |root, transition| {
683 if let Some(installed) = installed {
684 if hardware_root_install_required(
685 current_hardware_root(),
686 root,
687 transition,
688 ) {
689 install_mm_identity(installed);
690 }
691 } else {
692 install_hardware_root(root, transition);
693 }
694 },
695 |active| {
696 if let Some(next) = activation.as_mut() {
697 next.commit(cpu_id);
698 }
699 let previous = replace_active_activation(pin, activation.take());
700 ACTIVE_ADDRESS_SPACE.write_current(pin, active);
701 if let Some(previous) = previous {
702 previous.release(AddressSpaceSwitchProof::new(cpu_id));
703 }
704 },
705 );
706 if reclaim_ready {
707 route_reclaim_notification(
708 self.phase,
709 || {
710 CONTEXT_SWITCH_RECLAIM_READY.with_current(pin, |pending| {
711 assert!(
712 !pending.swap(true, Ordering::AcqRel),
713 "context switch retained an unconsumed active-mm reclaim edge"
714 );
715 });
716 },
717 ax_task::runtime::resource::notify_address_space_reclaim,
718 );
719 }
720 }
721 }
722 }
723}
724
725pub(super) fn prepare_runtime_address_space_switch<'pin, 'cpu>(
727 _pin: &'pin CpuPin<'cpu>,
728 previous_selected: AddressSpaceHandle,
729 next_selected: AddressSpaceHandle,
730 same_address_space: bool,
731 phase: AddressSpaceTransitionPhase,
732) -> Result<PreparedAddressSpaceSwitch<'pin, 'cpu>, RuntimeStatus> {
733 #[cfg(feature = "uspace")]
734 let pin = _pin;
735 #[cfg(feature = "uspace")]
736 let cpu_id = pin.area().cpu_index().as_usize();
737 #[cfg(any(not(feature = "uspace"), target_arch = "aarch64"))]
738 let _ = same_address_space;
739
740 #[cfg(feature = "uspace")]
741 {
742 let previous_raw = ACTIVE_ADDRESS_SPACE.read_current(pin);
743 #[cfg(not(target_arch = "aarch64"))]
744 if same_address_space {
745 debug_assert!(!previous_selected.is_none());
746 debug_assert!(!next_selected.is_none());
747 debug_assert_ne!(previous_raw, 0);
748 return Ok(PreparedAddressSpaceSwitch {
749 pin,
750 phase,
751 previous_raw,
752 previous: None,
753 action: PreparedAddressSpaceAction::SameUser,
754 _not_send_or_sync: PhantomData,
755 });
756 }
757 let previous = if previous_raw == 0 {
758 None
759 } else {
760 let previous = unsafe { AddressSpaceHandle::from_raw(previous_raw) };
763 Some(runtime_address_space(previous)?)
764 };
765
766 #[cfg(not(target_arch = "aarch64"))]
767 if let Some((previous, next)) = previous.zip(
768 (!next_selected.is_none())
769 .then(|| runtime_address_space(next_selected))
770 .transpose()?,
771 ) && same_logical_address_space(previous, next)
772 {
773 debug_assert!(!previous_selected.is_none());
781 debug_assert!(
782 runtime_address_space(previous_selected)
783 .is_ok_and(|selected| same_logical_address_space(previous, selected))
784 );
785 debug_assert_ne!(previous.active_leases.load(Ordering::Acquire), 0);
786 debug_assert!(
787 previous.cpu_state.active_mask() & AddressSpaceCpuState::cpu_bit(cpu_id) != 0
788 );
789 return Ok(PreparedAddressSpaceSwitch {
790 pin,
791 phase,
792 previous_raw,
793 previous: Some(previous),
794 action: PreparedAddressSpaceAction::SameUser,
795 _not_send_or_sync: PhantomData,
796 });
797 }
798
799 if let Some(previous) = previous {
800 let bit = AddressSpaceCpuState::cpu_bit(cpu_id);
801 if previous.active_leases.load(Ordering::Acquire) == 0
802 || previous.cpu_state.active_mask() & bit == 0
803 {
804 return Err(RuntimeStatus::InvalidHandle);
805 }
806 }
807
808 let previous_state = if previous_selected.is_none() {
809 None
810 } else {
811 let selected = runtime_address_space(previous_selected)?;
812 let Some(active) = previous else {
813 return Err(RuntimeStatus::InvalidArgument);
814 };
815 if !same_logical_address_space(active, selected) {
816 return Err(RuntimeStatus::InvalidArgument);
817 }
818 Some(selected)
819 };
820
821 let (next_state, action) = if next_selected.is_none() {
822 (None, PreparedAddressSpaceAction::KernelLazy)
823 } else {
824 let next = runtime_address_space(next_selected)?;
825 (
826 Some(next),
827 PreparedAddressSpaceAction::User {
828 next_raw: next_selected.into_raw(),
829 next,
830 activation: if previous
831 .is_some_and(|previous| same_logical_address_space(previous, next))
832 {
833 None
834 } else {
835 next._owner.prepare_activation(cpu_id)?
836 },
837 },
838 )
839 };
840
841 let changed_address_space = match (previous_state, next_state) {
846 (Some(previous), Some(next)) => !same_logical_address_space(previous, next),
847 (Some(_), None) | (None, Some(_)) => true,
848 (None, None) => false,
849 };
850 if changed_address_space {
851 core::sync::atomic::fence(Ordering::SeqCst);
855 }
856
857 Ok(PreparedAddressSpaceSwitch {
858 pin,
859 phase,
860 previous_raw,
861 previous,
862 action,
863 _not_send_or_sync: PhantomData,
864 })
865 }
866
867 #[cfg(not(feature = "uspace"))]
868 {
869 if !previous_selected.is_none() || !next_selected.is_none() {
870 return Err(RuntimeStatus::Unsupported);
871 }
872 Ok(PreparedAddressSpaceSwitch {
873 phase,
874 action: PreparedAddressSpaceAction::KernelLazy,
875 _not_send_or_sync: PhantomData,
876 })
877 }
878}
879
880pub(super) fn release_current_active_address_space() {
881 #[cfg(feature = "uspace")]
882 let reclaim_ready = unsafe {
883 with_current_cpu_pin(|pin| {
884 let previous_raw = ACTIVE_ADDRESS_SPACE.read_current(pin);
885 if previous_raw == 0 {
886 return false;
887 }
888 install_hardware_root(
889 offline_kernel_root(),
890 HardwareAddressSpaceTransition::DifferentAddressSpace,
891 );
892 ax_hal::asm::flush_tlb(None);
894 if let Some(activation) = replace_active_activation(pin, None) {
895 activation.release(AddressSpaceSwitchProof::new(
896 pin.area().cpu_index().as_usize(),
897 ));
898 }
899 ACTIVE_ADDRESS_SPACE.write_current(pin, 0);
900 let previous = AddressSpaceHandle::from_raw(previous_raw);
901 let previous = runtime_address_space(previous)
902 .unwrap_or_else(|_| panic!("offline CPU retained a stale active address space"));
903 previous
904 .cpu_state
905 .deactivate(pin.area().cpu_index().as_usize());
906 #[cfg(feature = "qperf-metrics")]
907 ACTIVE_MM_LEASE_DEACTIVATIONS.fetch_add(1, Ordering::Relaxed);
908 release_active_cpu(previous)
909 })
910 };
911 #[cfg(feature = "uspace")]
912 if reclaim_ready {
913 ax_task::runtime::resource::notify_address_space_reclaim();
914 }
915}
916
917pub(super) fn destroy_runtime_address_space(
918 address_space: AddressSpaceHandle,
919) -> AddressSpaceDestroyOutcome {
920 let address_space = runtime_address_space(address_space)
921 .unwrap_or_else(|_| panic!("address-space destruction received an invalid owning handle"));
922 if address_space.active_leases.load(Ordering::Acquire) != 0 {
923 return AddressSpaceDestroyOutcome::Active;
924 }
925 let raw = address_space as *const RuntimeAddressSpace as *mut RuntimeAddressSpace;
926 drop(unsafe { Box::from_raw(raw) });
929 #[cfg(feature = "qperf-metrics")]
930 ACTIVE_MM_RECLAIM_DESTROYED.fetch_add(1, Ordering::Relaxed);
931 AddressSpaceDestroyOutcome::Released
932}
933
934pub(super) fn arm_runtime_address_space_reclaim(
935 address_space: AddressSpaceHandle,
936) -> AddressSpaceReclaimArmOutcome {
937 let address_space = runtime_address_space(address_space)
938 .unwrap_or_else(|_| panic!("address-space reclaim arm received an invalid owning handle"));
939 address_space.reclaim_waiting.store(1, Ordering::Release);
940 if address_space.active_leases.load(Ordering::Acquire) == 0 {
941 address_space.reclaim_waiting.store(0, Ordering::Release);
942 AddressSpaceReclaimArmOutcome::Ready
943 } else {
944 AddressSpaceReclaimArmOutcome::Armed
945 }
946}
947
948pub(super) fn runtime_address_space_membarrier_state(
949 address_space: AddressSpaceHandle,
950) -> AddressSpaceMembarrierState {
951 let address_space = runtime_address_space(address_space)
952 .unwrap_or_else(|_| panic!("membarrier received an invalid address-space handle"));
953 AddressSpaceCpuState::membarrier_state(&address_space.cpu_state)
954}
955
956pub(super) fn update_runtime_address_space_membarrier_state(
957 address_space: AddressSpaceHandle,
958 registration: MembarrierRegistration,
959 phase: MembarrierRegistrationPhase,
960) -> AddressSpaceMembarrierState {
961 let address_space = runtime_address_space(address_space).unwrap_or_else(|_| {
962 panic!("membarrier registration received an invalid address-space handle")
963 });
964 AddressSpaceCpuState::update_membarrier_state(&address_space.cpu_state, registration, phase)
965}
966
967#[cfg(any(feature = "uspace", test))]
968fn release_active_cpu(address_space: &RuntimeAddressSpace) -> bool {
969 let active = address_space.active_leases.fetch_sub(1, Ordering::AcqRel);
970 assert!(active >= 1, "active address-space lease count underflow");
971 let reclaim_ready = active == 1 && address_space.reclaim_waiting.swap(0, Ordering::AcqRel) != 0;
972 #[cfg(feature = "qperf-metrics")]
973 if reclaim_ready {
974 ACTIVE_MM_RECLAIM_READY.fetch_add(1, Ordering::Relaxed);
975 }
976 reclaim_ready
977}
978
979#[cfg(any(feature = "uspace", test))]
980fn route_reclaim_notification(
981 phase: AddressSpaceTransitionPhase,
982 defer: impl FnOnce(),
983 publish: impl FnOnce(),
984) {
985 #[cfg(not(feature = "uspace"))]
986 let _ = publish;
987 match phase {
988 #[cfg(feature = "uspace")]
989 AddressSpaceTransitionPhase::CurrentTask => publish(),
990 AddressSpaceTransitionPhase::ContextSwitch => defer(),
991 }
992}
993
994pub(super) fn take_context_switch_reclaim_ready() -> bool {
995 #[cfg(feature = "uspace")]
996 {
997 unsafe {
1000 with_current_cpu_pin(|pin| {
1001 CONTEXT_SWITCH_RECLAIM_READY
1002 .with_current(pin, |pending| pending.swap(false, Ordering::AcqRel))
1003 })
1004 }
1005 }
1006 #[cfg(not(feature = "uspace"))]
1007 {
1008 false
1009 }
1010}
1011
1012#[cfg(feature = "uspace")]
1013fn commit_current_task_address_space_transition<T>(
1014 next_selected: AddressSpaceHandle,
1015 action: impl FnOnce() -> Result<T, TaskError>,
1016) -> Result<T, TaskError> {
1017 let _irq = crate::task::sync::IrqSaveGuard::new();
1018 unsafe {
1021 with_current_cpu_pin(|pin| {
1022 let previous_selected = ax_task::runtime::resource::current_address_space_handle()?;
1023 let prepared = prepare_runtime_address_space_switch(
1024 pin,
1025 previous_selected,
1026 next_selected,
1027 false,
1028 AddressSpaceTransitionPhase::CurrentTask,
1029 )
1030 .map_err(super::runtime_status_error)?;
1031 let result = action()?;
1032 prepared.commit();
1034 Ok(result)
1035 })
1036 }
1037}
1038
1039pub fn switch_current_address_space(address_space: TaskAddressSpace) -> Result<(), TaskError> {
1041 #[cfg(feature = "uspace")]
1042 {
1043 let mut address_space = address_space;
1044 let next_handle = address_space.handle();
1045 let previous = commit_current_task_address_space_transition(next_handle, || {
1046 ax_task::runtime::resource::replace_current_address_space(address_space.token_mut())
1047 })?;
1048 let transferred = address_space.take_token();
1049 debug_assert!(transferred.is_none());
1050
1051 detach_replaced_address_space_owner(previous.handle());
1054 ax_task::runtime::resource::release_address_space_token(previous)
1055 }
1056 #[cfg(not(feature = "uspace"))]
1057 {
1058 let _ = address_space;
1059 Err(TaskError::RuntimeFailure(RuntimeStatus::Unsupported as u32))
1060 }
1061}
1062
1063pub fn detach_current_address_space() -> Result<(), TaskError> {
1066 #[cfg(feature = "uspace")]
1067 {
1068 let previous = commit_current_task_address_space_transition(
1069 AddressSpaceHandle::NONE,
1070 ax_task::runtime::resource::detach_current_address_space,
1071 )?;
1072
1073 detach_runtime_address_space_owner(previous.handle());
1077 ax_task::runtime::resource::release_address_space_token(previous)
1078 }
1079 #[cfg(not(feature = "uspace"))]
1080 {
1081 Err(TaskError::RuntimeFailure(RuntimeStatus::Unsupported as u32))
1082 }
1083}
1084
1085#[cfg(test)]
1086mod tests {
1087 use alloc::sync::Arc;
1088 use core::sync::atomic::{AtomicUsize, Ordering};
1089
1090 use super::*;
1091
1092 struct CountDrop(Arc<AtomicUsize>);
1093
1094 impl Drop for CountDrop {
1095 fn drop(&mut self) {
1096 self.0.fetch_add(1, Ordering::Release);
1097 }
1098 }
1099
1100 #[test]
1101 fn switch_activation_defers_reclaim_notification_until_switch_tail() {
1102 let deferred = AtomicUsize::new(0);
1103 let published = AtomicUsize::new(0);
1104
1105 route_reclaim_notification(
1106 AddressSpaceTransitionPhase::ContextSwitch,
1107 || {
1108 deferred.fetch_add(1, Ordering::Relaxed);
1109 },
1110 || {
1111 published.fetch_add(1, Ordering::Relaxed);
1112 },
1113 );
1114
1115 assert_eq!(deferred.load(Ordering::Relaxed), 1);
1116 assert_eq!(published.load(Ordering::Relaxed), 0);
1117 }
1118
1119 #[test]
1120 fn active_cpu_lease_blocks_owner_destruction_until_release() {
1121 let drops = Arc::new(AtomicUsize::new(0));
1122 let mut token = TaskAddressSpace::new(
1123 ax_memory_addr::PhysAddr::from(0x4000),
1124 CountDrop(Arc::clone(&drops)),
1125 )
1126 .unwrap();
1127 let handle = token.handle();
1128 let runtime = runtime_address_space(handle).unwrap();
1129 runtime.active_leases.fetch_add(1, Ordering::AcqRel);
1130 let owned = token.take_token();
1131
1132 assert_eq!(
1133 destroy_runtime_address_space(handle),
1134 AddressSpaceDestroyOutcome::Active
1135 );
1136 assert_eq!(
1137 arm_runtime_address_space_reclaim(handle),
1138 AddressSpaceReclaimArmOutcome::Armed
1139 );
1140 assert_eq!(drops.load(Ordering::Acquire), 0);
1141
1142 assert!(release_active_cpu(runtime));
1143 assert_eq!(
1144 destroy_runtime_address_space(handle),
1145 AddressSpaceDestroyOutcome::Released
1146 );
1147 assert_eq!(drops.load(Ordering::Acquire), 1);
1148 assert!(!owned.is_none());
1149 }
1150
1151 #[test]
1152 fn task_detach_releases_task_owner_before_lazy_cpu_lease() {
1153 struct CountDetach {
1154 detaches: Arc<AtomicUsize>,
1155 drops: Arc<AtomicUsize>,
1156 }
1157
1158 impl Drop for CountDetach {
1159 fn drop(&mut self) {
1160 self.drops.fetch_add(1, Ordering::Release);
1161 }
1162 }
1163
1164 fn detach(owner: &CountDetach) {
1165 owner.detaches.fetch_add(1, Ordering::Release);
1166 }
1167
1168 let detaches = Arc::new(AtomicUsize::new(0));
1169 let drops = Arc::new(AtomicUsize::new(0));
1170 let mut token = TaskAddressSpace::new_with_task_detach(
1171 ax_memory_addr::PhysAddr::from(0x4000),
1172 Arc::new(AddressSpaceCpuState::new(PhysAddr::from(0x4000))),
1173 CountDetach {
1174 detaches: Arc::clone(&detaches),
1175 drops: Arc::clone(&drops),
1176 },
1177 detach,
1178 )
1179 .unwrap();
1180 let handle = token.handle();
1181 let runtime = runtime_address_space(handle).unwrap();
1182 runtime.active_leases.fetch_add(1, Ordering::AcqRel);
1183 let owned = token.take_token();
1184
1185 detach_runtime_address_space_owner(handle);
1186 detach_runtime_address_space_owner(handle);
1187 assert_eq!(detaches.load(Ordering::Acquire), 1);
1188 assert_eq!(drops.load(Ordering::Acquire), 0);
1189 assert_eq!(
1190 destroy_runtime_address_space(handle),
1191 AddressSpaceDestroyOutcome::Active
1192 );
1193
1194 assert!(!release_active_cpu(runtime));
1195 assert_eq!(
1196 destroy_runtime_address_space(handle),
1197 AddressSpaceDestroyOutcome::Released
1198 );
1199 assert_eq!(drops.load(Ordering::Acquire), 1);
1200 assert!(!owned.is_none());
1201 }
1202
1203 #[test]
1204 fn replaced_task_owner_is_detached_before_runtime_release() {
1205 struct CountDetach(Arc<AtomicUsize>);
1206
1207 fn detach(owner: &CountDetach) {
1208 owner.0.fetch_add(1, Ordering::Release);
1209 }
1210
1211 let detaches = Arc::new(AtomicUsize::new(0));
1212 let mut token = TaskAddressSpace::new_with_task_detach(
1213 PhysAddr::from(0x4000),
1214 Arc::new(AddressSpaceCpuState::new(PhysAddr::from(0x4000))),
1215 CountDetach(Arc::clone(&detaches)),
1216 detach,
1217 )
1218 .unwrap();
1219 let handle = token.handle();
1220 let owned = token.take_token();
1221
1222 detach_replaced_address_space_owner(handle);
1223 assert_eq!(detaches.load(Ordering::Acquire), 1);
1224 assert_eq!(
1225 destroy_runtime_address_space(handle),
1226 AddressSpaceDestroyOutcome::Released
1227 );
1228 assert!(!owned.is_none());
1229 }
1230
1231 #[test]
1232 fn shared_cpu_state_publishes_and_withdraws_cpu_footprints() {
1233 let tracker = AddressSpaceCpuState::new(PhysAddr::from(0x4000));
1234
1235 tracker.activate(1);
1236 tracker.activate(3);
1237 assert_eq!(tracker.active_mask(), (1usize << 1) | (1usize << 3));
1238
1239 tracker.deactivate(1);
1240 assert_eq!(tracker.active_mask(), 1usize << 3);
1241 }
1242
1243 #[test]
1244 fn address_space_cpu_state_rejects_mismatched_root() {
1245 let tracker = Arc::new(AddressSpaceCpuState::new(PhysAddr::from(0x4000)));
1246 let token =
1247 TaskAddressSpace::new_with_task_detach(PhysAddr::from(0x8000), tracker, (), |_| {});
1248
1249 assert!(matches!(token, Err(TaskError::InvalidRuntimeHandle)));
1250 }
1251
1252 #[test]
1253 fn shared_mm_tokens_share_membarrier_identity_and_registration() {
1254 let cpu_state = Arc::new(AddressSpaceCpuState::new(PhysAddr::from(0x4000)));
1255 let first = TaskAddressSpace::new_with_task_detach(
1256 PhysAddr::from(0x4000),
1257 Arc::clone(&cpu_state),
1258 (),
1259 |_| {},
1260 )
1261 .unwrap();
1262 let second = TaskAddressSpace::new_with_task_detach(
1263 PhysAddr::from(0x4000),
1264 Arc::clone(&cpu_state),
1265 (),
1266 |_| {},
1267 )
1268 .unwrap();
1269
1270 let requested = update_runtime_address_space_membarrier_state(
1271 first.handle(),
1272 MembarrierRegistration::PrivateExpedited,
1273 MembarrierRegistrationPhase::Begin,
1274 );
1275 let observed = runtime_address_space_membarrier_state(second.handle());
1276 assert_eq!(requested, observed);
1277 assert!(observed.requested(MembarrierRegistration::PrivateExpedited));
1278 assert!(!observed.ready(MembarrierRegistration::PrivateExpedited));
1279
1280 let ready = update_runtime_address_space_membarrier_state(
1281 second.handle(),
1282 MembarrierRegistration::PrivateExpedited,
1283 MembarrierRegistrationPhase::Complete,
1284 );
1285 assert_eq!(
1286 runtime_address_space_membarrier_state(first.handle()),
1287 ready
1288 );
1289 assert!(ready.ready(MembarrierRegistration::PrivateExpedited));
1290 }
1291
1292 #[cfg(feature = "uspace")]
1293 #[test]
1294 fn same_mm_activation_retains_the_existing_cpu_lease_without_hardware_work() {
1295 let tracker = Arc::new(AddressSpaceCpuState::new(PhysAddr::from(0x4000)));
1296 let previous = TaskAddressSpace::new_with_task_detach(
1297 PhysAddr::from(0x4000),
1298 Arc::clone(&tracker),
1299 (),
1300 |_| {},
1301 )
1302 .unwrap();
1303 let next = TaskAddressSpace::new_with_task_detach(
1304 PhysAddr::from(0x4000),
1305 Arc::clone(&tracker),
1306 (),
1307 |_| {},
1308 )
1309 .unwrap();
1310 let previous_runtime = runtime_address_space(previous.handle()).unwrap();
1311 let next_runtime = runtime_address_space(next.handle()).unwrap();
1312 previous_runtime.active_leases.store(1, Ordering::Release);
1313 tracker.activate(0);
1314 let hardware_root = AtomicUsize::new(0x4000);
1315 let hardware_installs = AtomicUsize::new(0);
1316 let active_publications = AtomicUsize::new(0);
1317
1318 let reclaim_ready = commit_user_address_space_activation(
1319 0,
1320 previous.handle().into_raw(),
1321 Some(previous_runtime),
1322 next.handle().into_raw(),
1323 next_runtime,
1324 |root, _transition| {
1325 if hardware_root.swap(root, Ordering::AcqRel) != root {
1326 hardware_installs.fetch_add(1, Ordering::Relaxed);
1327 }
1328 },
1329 |_| {
1330 active_publications.fetch_add(1, Ordering::Relaxed);
1331 },
1332 );
1333
1334 let previous_leases = previous_runtime.active_leases.load(Ordering::Acquire);
1335 let next_leases = next_runtime.active_leases.load(Ordering::Acquire);
1336 previous_runtime.active_leases.store(0, Ordering::Release);
1337 next_runtime.active_leases.store(0, Ordering::Release);
1338 tracker.deactivate(0);
1339
1340 assert_eq!(previous_leases, 1);
1341 assert_eq!(next_leases, 0);
1342 assert_eq!(hardware_installs.load(Ordering::Relaxed), 0);
1343 assert_eq!(active_publications.load(Ordering::Relaxed), 0);
1344 assert!(!reclaim_ready);
1345 }
1346
1347 #[cfg(feature = "uspace")]
1348 #[test]
1349 fn lazy_kernel_root_is_restored_before_same_mm_user_execution() {
1350 let tracker = Arc::new(AddressSpaceCpuState::new(PhysAddr::from(0x4000)));
1351 let previous = TaskAddressSpace::new_with_task_detach(
1352 PhysAddr::from(0x4000),
1353 Arc::clone(&tracker),
1354 (),
1355 |_| {},
1356 )
1357 .unwrap();
1358 let next = TaskAddressSpace::new_with_task_detach(
1359 PhysAddr::from(0x4000),
1360 Arc::clone(&tracker),
1361 (),
1362 |_| {},
1363 )
1364 .unwrap();
1365 let previous_runtime = runtime_address_space(previous.handle()).unwrap();
1366 let next_runtime = runtime_address_space(next.handle()).unwrap();
1367 previous_runtime.active_leases.store(1, Ordering::Release);
1368 tracker.activate(0);
1369 let hardware_root = AtomicUsize::new(0);
1370 let hardware_installs = AtomicUsize::new(0);
1371 let active_publications = AtomicUsize::new(0);
1372
1373 let reclaim_ready = commit_user_address_space_activation(
1374 0,
1375 previous.handle().into_raw(),
1376 Some(previous_runtime),
1377 next.handle().into_raw(),
1378 next_runtime,
1379 |root, _transition| {
1380 if hardware_root.swap(root, Ordering::AcqRel) != root {
1381 hardware_installs.fetch_add(1, Ordering::Relaxed);
1382 }
1383 },
1384 |_| {
1385 active_publications.fetch_add(1, Ordering::Relaxed);
1386 },
1387 );
1388
1389 let previous_leases = previous_runtime.active_leases.load(Ordering::Acquire);
1390 let next_leases = next_runtime.active_leases.load(Ordering::Acquire);
1391 previous_runtime.active_leases.store(0, Ordering::Release);
1392 next_runtime.active_leases.store(0, Ordering::Release);
1393 tracker.deactivate(0);
1394
1395 assert_eq!(hardware_root.load(Ordering::Acquire), 0x4000);
1396 assert_eq!(hardware_installs.load(Ordering::Relaxed), 1);
1397 assert_eq!(previous_leases, 1);
1398 assert_eq!(next_leases, 0);
1399 assert_eq!(active_publications.load(Ordering::Relaxed), 0);
1400 assert!(!reclaim_ready);
1401 }
1402
1403 #[cfg(feature = "uspace")]
1404 #[test]
1405 fn same_mm_chain_releases_the_retained_active_handle_on_other_mm_switch() {
1406 let shared = Arc::new(AddressSpaceCpuState::new(PhysAddr::from(0x4000)));
1407 let other = Arc::new(AddressSpaceCpuState::new(PhysAddr::from(0x8000)));
1408 let first = TaskAddressSpace::new_with_task_detach(
1409 PhysAddr::from(0x4000),
1410 Arc::clone(&shared),
1411 (),
1412 |_| {},
1413 )
1414 .unwrap();
1415 let second = TaskAddressSpace::new_with_task_detach(
1416 PhysAddr::from(0x4000),
1417 Arc::clone(&shared),
1418 (),
1419 |_| {},
1420 )
1421 .unwrap();
1422 let third = TaskAddressSpace::new_with_task_detach(
1423 PhysAddr::from(0x8000),
1424 Arc::clone(&other),
1425 (),
1426 |_| {},
1427 )
1428 .unwrap();
1429 let first_runtime = runtime_address_space(first.handle()).unwrap();
1430 let second_runtime = runtime_address_space(second.handle()).unwrap();
1431 let third_runtime = runtime_address_space(third.handle()).unwrap();
1432 first_runtime.active_leases.store(1, Ordering::Release);
1433 shared.activate(0);
1434 let active = AtomicUsize::new(first.handle().into_raw());
1435
1436 assert!(same_logical_address_space(first_runtime, second_runtime));
1437 assert!(!commit_user_address_space_activation(
1438 0,
1439 active.load(Ordering::Acquire),
1440 Some(first_runtime),
1441 second.handle().into_raw(),
1442 second_runtime,
1443 |_, _| {},
1444 |next| active.store(next, Ordering::Release),
1445 ));
1446 assert_eq!(active.load(Ordering::Acquire), first.handle().into_raw());
1447 assert_eq!(first_runtime.active_leases.load(Ordering::Acquire), 1);
1448 assert_eq!(second_runtime.active_leases.load(Ordering::Acquire), 0);
1449
1450 assert!(!commit_user_address_space_activation(
1451 0,
1452 active.load(Ordering::Acquire),
1453 Some(first_runtime),
1454 third.handle().into_raw(),
1455 third_runtime,
1456 |_, transition| {
1457 assert_eq!(
1458 transition,
1459 HardwareAddressSpaceTransition::DifferentAddressSpace
1460 );
1461 },
1462 |next| active.store(next, Ordering::Release),
1463 ));
1464 assert_eq!(active.load(Ordering::Acquire), third.handle().into_raw());
1465 assert_eq!(first_runtime.active_leases.load(Ordering::Acquire), 0);
1466 assert_eq!(second_runtime.active_leases.load(Ordering::Acquire), 0);
1467 assert_eq!(third_runtime.active_leases.load(Ordering::Acquire), 1);
1468 assert_eq!(shared.active_mask(), 0);
1469 assert_eq!(other.active_mask(), 1);
1470
1471 third_runtime.active_leases.store(0, Ordering::Release);
1472 other.deactivate(0);
1473 }
1474
1475 #[test]
1476 fn different_address_space_reusing_same_root_requires_hardware_install() {
1477 assert!(hardware_root_install_required(
1478 0x4000,
1479 0x4000,
1480 HardwareAddressSpaceTransition::DifferentAddressSpace,
1481 ));
1482 assert!(!hardware_root_install_required(
1483 0x4000,
1484 0x4000,
1485 HardwareAddressSpaceTransition::SameAddressSpace,
1486 ));
1487 }
1488}