Skip to main content

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}