ax_task/runtime/resource.rs
1//! Runtime resource ownership and address-space lifecycle.
2
3use crate::{
4 runtime::context::{
5 RuntimeIrqGuard, runtime_current_cpu_mut, runtime_task_system, validate_task_context,
6 },
7 thread::{TaskError, current::current_thread_id},
8};
9pub use crate::{
10 runtime::service::reclaim::notify_address_space_reclaim, thread::spec::ThreadResources,
11};
12
13/// Returns the scheduler-selected logical address space of the current task.
14///
15/// This low-level runtime query is intended for the final user-entry
16/// validation. The returned opaque handle does not transfer ownership.
17#[doc(hidden)]
18pub fn current_address_space_handle()
19-> Result<crate::runtime::resource::AddressSpaceHandle, TaskError> {
20 let current = current_thread_id()?;
21 let mut irq = RuntimeIrqGuard::enter();
22 let cpu = runtime_current_cpu_mut(&mut irq)?;
23 // SAFETY: `irq` owns the IRQ-off owner-CPU scope and the architecture
24 // current publication proved `current` belongs to this execution context.
25 unsafe { cpu.scheduler_current_address_space(current) }
26}
27
28/// Replaces the current thread's scheduler-visible address-space token.
29///
30/// The runtime must update its architecture context and hardware page table in
31/// the same outer IRQ-off transaction after this function returns. The old
32/// token remains scheduler-owned and is returned so the runtime can defer its
33/// task-context reclamation after leaving that IRQ-off transaction.
34pub fn replace_current_address_space(
35 address_space: &mut crate::runtime::resource::AddressSpaceToken,
36) -> Result<crate::runtime::resource::AddressSpaceToken, TaskError> {
37 validate_task_context()?;
38 let mut irq = RuntimeIrqGuard::enter();
39 let mut cpu = runtime_current_cpu_mut(&mut irq)?;
40 runtime_task_system()?.replace_current_address_space(cpu.as_mut(), address_space)
41}
42
43/// Detaches the current thread's scheduler-visible user address space.
44///
45/// The runtime must enter its lazy kernel address-space state before the outer
46/// IRQ-off transaction ends, then transfer the returned token to task-context
47/// reclamation.
48pub fn detach_current_address_space()
49-> Result<crate::runtime::resource::AddressSpaceToken, TaskError> {
50 validate_task_context()?;
51 let mut irq = RuntimeIrqGuard::enter();
52 let mut cpu = runtime_current_cpu_mut(&mut irq)?;
53 runtime_task_system()?.detach_current_address_space(cpu.as_mut())
54}
55
56/// Transfers an obsolete address-space token to task-context reclamation.
57///
58/// The runtime may still report the object busy while another CPU retains it
59/// as an active mm. The task-work reaper owns every retry after this function
60/// accepts the token.
61pub fn release_address_space_token(
62 address_space: crate::runtime::resource::AddressSpaceToken,
63) -> Result<(), TaskError> {
64 validate_task_context()?;
65 runtime_task_system()?.release_address_space_token(address_space);
66 Ok(())
67}
68use crate::runtime::handle::opaque_handle;
69
70opaque_handle!(
71 /// Opaque handle to an architecture execution context.
72 ExecutionContextHandle,
73 "runtime::resource"
74);
75opaque_handle!(
76 /// Opaque handle to a runtime-owned stack allocation.
77 StackHandle,
78 "runtime::resource"
79);
80opaque_handle!(
81 /// Opaque handle to a runtime-owned TLS allocation.
82 TlsHandle,
83 "runtime::resource"
84);
85opaque_handle!(
86 /// Borrowed opaque handle to a runtime-owned address space.
87 AddressSpaceHandle,
88 "runtime::resource"
89);
90
91/// Stable identity of one Linux-style address-space generation.
92///
93/// Distinct scheduler resource tokens may carry different
94/// [`AddressSpaceHandle`] values while referring to the same shared `mm`.
95/// Runtime providers must therefore derive this identity from the shared
96/// address-space owner rather than from the token allocation itself.
97#[derive(Clone, Copy, Debug, Eq, Hash, PartialEq)]
98#[repr(transparent)]
99pub struct AddressSpaceMembarrierId(usize);
100
101impl AddressSpaceMembarrierId {
102 /// Identity used by kernel threads which do not own a userspace `mm`.
103 pub const NONE: Self = Self(0);
104
105 /// Creates an identity from a runtime-owned shared address-space object.
106 ///
107 /// # Safety
108 ///
109 /// A non-zero value must remain unique for the complete lifetime of the
110 /// corresponding address-space generation. It must not be reused while an
111 /// [`AddressSpaceMembarrierState`] containing it can remain rq-visible.
112 pub const unsafe fn from_raw(raw: usize) -> Self {
113 Self(raw)
114 }
115
116 /// Returns whether this is the kernel-thread sentinel.
117 pub const fn is_none(self) -> bool {
118 self.0 == 0
119 }
120
121 /// Returns the provider-owned opaque identity.
122 pub const fn into_raw(self) -> usize {
123 self.0
124 }
125}
126
127/// One membarrier facility registered by an address space.
128#[derive(Clone, Copy, Debug, Eq, PartialEq)]
129#[repr(u32)]
130pub enum MembarrierRegistration {
131 /// Enables process-independent expedited barriers for this `mm`.
132 GlobalExpedited = 1,
133 /// Enables expedited barriers restricted to this `mm`.
134 PrivateExpedited = 2,
135}
136
137impl MembarrierRegistration {
138 /// Returns the bit stored while rq synchronization is in progress.
139 pub const fn requested_bit(self) -> u32 {
140 self as u32
141 }
142
143 /// Returns the bit published only after every running rq is synchronized.
144 pub const fn ready_bit(self) -> u32 {
145 (self as u32) << 16
146 }
147}
148
149/// Phase of one irreversible per-address-space registration.
150#[derive(Clone, Copy, Debug, Eq, PartialEq)]
151#[repr(u32)]
152pub enum MembarrierRegistrationPhase {
153 /// Publishes the requested bit before inspecting any runqueue.
154 Begin = 0,
155 /// Publishes the ready bit after synchronous rq refresh completes.
156 Complete = 1,
157}
158
159/// Allocation-free snapshot of one address space's membarrier state.
160#[derive(Clone, Copy, Debug, Eq, PartialEq)]
161#[repr(C)]
162pub struct AddressSpaceMembarrierState {
163 identity: AddressSpaceMembarrierId,
164 bits: u32,
165}
166
167impl AddressSpaceMembarrierState {
168 /// State installed for a kernel thread without a userspace `mm`.
169 pub const NONE: Self = Self {
170 identity: AddressSpaceMembarrierId::NONE,
171 bits: 0,
172 };
173
174 /// Constructs a provider snapshot from one live shared `mm` identity.
175 ///
176 /// # Safety
177 ///
178 /// `identity` must obey [`AddressSpaceMembarrierId::from_raw`], and `bits`
179 /// must contain only requested and ready bits produced by
180 /// [`MembarrierRegistration`].
181 pub const unsafe fn new(identity: AddressSpaceMembarrierId, bits: u32) -> Self {
182 Self { identity, bits }
183 }
184
185 /// Returns the shared address-space identity.
186 pub const fn identity(self) -> AddressSpaceMembarrierId {
187 self.identity
188 }
189
190 /// Reports whether registration has begun, including its synchronization
191 /// interval before the ready bit becomes visible.
192 pub const fn requested(self, registration: MembarrierRegistration) -> bool {
193 self.bits & registration.requested_bit() != 0
194 }
195
196 /// Reports whether registration completed its rq synchronization.
197 pub const fn ready(self, registration: MembarrierRegistration) -> bool {
198 self.bits & registration.ready_bit() != 0
199 }
200
201 /// Reports whether any scheduler-visible membarrier facility is active.
202 pub const fn any_requested(self) -> bool {
203 self.bits
204 & (MembarrierRegistration::GlobalExpedited.requested_bit()
205 | MembarrierRegistration::PrivateExpedited.requested_bit())
206 != 0
207 }
208
209 /// Returns the provider-owned atomic representation.
210 pub const fn bits(self) -> u32 {
211 self.bits
212 }
213}
214
215pub(crate) const fn scheduled_membarrier_state(
216 active_mm_state: AddressSpaceMembarrierState,
217 task_membarrier_state: AddressSpaceMembarrierState,
218) -> AddressSpaceMembarrierState {
219 if task_membarrier_state.identity().is_none() {
220 active_mm_state
221 } else {
222 task_membarrier_state
223 }
224}
225
226#[cfg(axtest)]
227pub const fn scheduled_membarrier_state_for_test(
228 active_mm_state: AddressSpaceMembarrierState,
229 task_membarrier_state: AddressSpaceMembarrierState,
230) -> AddressSpaceMembarrierState {
231 scheduled_membarrier_state(active_mm_state, task_membarrier_state)
232}
233
234/// Bounded operation executed synchronously on a target CPU.
235#[derive(Clone, Copy, Debug, Eq, PartialEq)]
236#[repr(u32)]
237pub enum RuntimeMembarrierAction {
238 /// Executes a full memory barrier in hard-IRQ context.
239 MemoryBarrier = 0,
240 /// Refreshes `rq->membarrier_state` from its current dispatch and executes
241 /// the corresponding full barrier.
242 RefreshRunQueue = 1,
243}
244
245/// Unique destruction right for one runtime-owned address-space object.
246///
247/// The scheduler may copy [`AddressSpaceHandle`] values derived from this
248/// token into dispatch metadata, but exactly one token owns the eventual
249/// [`crate::runtime::TaskRuntime::destroy_address_space`] operation.
250#[repr(transparent)]
251#[derive(Debug, Eq, PartialEq)]
252pub struct AddressSpaceToken(usize);
253
254impl AddressSpaceToken {
255 /// Empty token used by kernel threads and pure scheduler models.
256 pub const NONE: Self = Self(0);
257
258 /// Creates an owning token from a fresh runtime object.
259 ///
260 /// # Safety
261 ///
262 /// A non-zero value must identify a live runtime-owned address-space
263 /// object whose unique destruction right is transferred to the caller.
264 pub const unsafe fn from_raw(raw: usize) -> Self {
265 Self(raw)
266 }
267
268 /// Borrows the opaque identity without transferring destruction rights.
269 pub const fn handle(&self) -> AddressSpaceHandle {
270 // SAFETY: a live owning token keeps the same runtime object alive for
271 // the duration of the returned scalar borrow.
272 unsafe { AddressSpaceHandle::from_raw(self.0) }
273 }
274
275 /// Returns whether this token owns no runtime object.
276 pub const fn is_none(&self) -> bool {
277 self.0 == 0
278 }
279}
280
281/// Result of consuming an address-space destruction attempt.
282///
283/// The runtime accepts only a live handle derived from the matching
284/// [`AddressSpaceToken`]. A stale or malformed handle is an unrecoverable
285/// provider invariant and is not represented here.
286#[derive(Clone, Copy, Debug, Eq, PartialEq)]
287#[repr(u32)]
288pub enum AddressSpaceDestroyOutcome {
289 /// No CPU retains the address space and the runtime consumed its object.
290 Released = 0,
291 /// At least one CPU still retains the address space as its active mm.
292 Active = 1,
293}
294
295/// Result of arming the active-mm last-user notification.
296#[derive(Clone, Copy, Debug, Eq, PartialEq)]
297#[repr(u32)]
298pub enum AddressSpaceReclaimArmOutcome {
299 /// No CPU lease remains; the scheduler must retry destruction now.
300 Ready = 0,
301 /// The runtime will publish a readiness edge when the last lease leaves.
302 Armed = 1,
303}
304
305/// Stack allocation requirements supplied to the runtime.
306#[derive(Clone, Copy, Debug, Eq, PartialEq)]
307#[repr(C)]
308pub struct StackRequest {
309 /// Usable stack bytes, excluding the guard region.
310 pub usable_size: usize,
311 /// Required stack alignment in bytes.
312 pub alignment: usize,
313 /// Number of inaccessible guard bytes below the usable range.
314 pub guard_size: usize,
315}
316
317/// Kernel context entry point.
318///
319/// Per-thread arguments remain in scheduler-owned thread metadata and are
320/// recovered by the entry trampoline through the current-thread facade. This
321/// matches the four architecture `TaskContext::init` contracts, which enter a
322/// fresh context without a portable argument register contract.
323pub type KernelEntry = unsafe extern "C" fn() -> !;
324
325/// Architecture-neutral request for a new kernel execution context.
326#[derive(Clone, Copy, Debug)]
327#[repr(C)]
328pub struct KernelContextRequest {
329 /// Runtime-owned stack backing the context.
330 pub stack: StackHandle,
331 /// Initial instruction entry point.
332 pub entry: KernelEntry,
333 /// Optional TLS allocation.
334 pub tls: TlsHandle,
335}
336
337/// Architecture-neutral request for a context that will enter userspace.
338///
339/// The initial entry is still a trusted runtime trampoline. Address-space
340/// ownership and activation are scheduler resources, not register-context
341/// construction inputs.
342#[derive(Clone, Copy, Debug)]
343#[repr(C)]
344pub struct UserContextRequest {
345 /// Runtime-owned stack backing the trusted entry trampoline.
346 pub stack: StackHandle,
347 /// Initial trusted instruction entry point.
348 pub entry: KernelEntry,
349 /// Optional TLS allocation.
350 pub tls: TlsHandle,
351}