Skip to main content

ax_task/time/
registration.rs

1//! Runtime-backed soft and explicitly hard kernel timer registration.
2
3use crate::{
4    runtime::{
5        context::{
6            RuntimeIrqGuard, runtime_current_cpu_mut, runtime_task_system, validate_task_context,
7        },
8        cpu::{CpuLocal, SchedulerDeadlineUpdate},
9        task_runtime,
10    },
11    sched::system::DeadlineBaseGuardSource,
12    thread::TaskError,
13    time::{
14        MonotonicDeadline,
15        hard_timer::HardKernelTimerHandle,
16        queue::{
17            HardKernelTimerCallback, KernelTimerCallback, KernelTimerCancelOutcome,
18            KernelTimerEntry, KernelTimerHandle, RestartableKernelTimerCallback, TaskDeadlineError,
19        },
20    },
21};
22
23enum KernelTimerRegistrationResult {
24    Registered(KernelTimerHandle, Option<SchedulerDeadlineUpdate>),
25    Rejected(TaskError, KernelTimerEntry),
26}
27
28struct KernelTimerCancellationResult {
29    outcome: Result<KernelTimerCancelOutcome, TaskError>,
30    removed: Option<KernelTimerEntry>,
31}
32
33/// Registers a task-context callback on the calling CPU's monotonic clock base.
34///
35/// Callback ownership is allocated before IRQs are excluded. Hard IRQ only
36/// promotes the entry into the existing `ktimers/%u` service; the callback is
37/// invoked later without the deadline lock or an IRQ guard held.
38///
39/// # Errors
40///
41/// Returns [`TaskError::UnsafeContext`] outside ordinary task context,
42/// [`TaskError::TimerCapacity`] when the per-CPU callback base is full, or a
43/// runtime/clockevent error without leaving a hidden registration behind.
44pub fn register_kernel_timer(
45    deadline: MonotonicDeadline,
46    callback: KernelTimerCallback,
47) -> Result<KernelTimerHandle, TaskError> {
48    validate_task_context()?;
49    let entry = KernelTimerEntry::new(deadline, callback).map_err(kernel_timer_error)?;
50    register_kernel_timer_entry(entry)
51}
52
53/// Registers a stable callback that may rearm the same timer identity.
54///
55/// The callback runs in the owner CPU's `ktimers/%u` task and returns either
56/// [`KernelTimerAction::Complete`] or an absolute deadline for the same entry.
57/// Cancellation remains non-blocking; if it races with an executing callback,
58/// the callback may finish but cannot rearm the cancelled registration.
59pub fn register_restartable_kernel_timer(
60    deadline: MonotonicDeadline,
61    callback: RestartableKernelTimerCallback,
62) -> Result<KernelTimerHandle, TaskError> {
63    validate_task_context()?;
64    let entry =
65        KernelTimerEntry::new_restartable(deadline, callback).map_err(kernel_timer_error)?;
66    register_kernel_timer_entry(entry)
67}
68
69/// Registers a stable callback with explicit hard-IRQ expiry semantics.
70///
71/// The callback capability carries the caller's proof that invocation is
72/// bounded and hard-IRQ-safe. Completion or cancellation drops its payload in
73/// task context; returning [`crate::time::hard_timer::HardKernelTimerAction::Rearm`] preserves the same
74/// timer identity and physical clockevent owner.
75pub fn register_hard_restartable_kernel_timer(
76    deadline: MonotonicDeadline,
77    callback: HardKernelTimerCallback,
78) -> Result<HardKernelTimerHandle, TaskError> {
79    validate_task_context()?;
80    let entry =
81        KernelTimerEntry::new_hard_restartable(deadline, callback).map_err(kernel_timer_error)?;
82    register_kernel_timer_entry(entry).map(HardKernelTimerHandle::new)
83}
84
85/// Arms an inactive hard timer or requests its next arm during execution.
86///
87/// A request during execution takes precedence over the callback's return
88/// action. An already queued timer must be disarmed before it can be armed.
89/// A registration with accepted cancellation cannot be rearmed.
90///
91/// The registration identity and callback allocation are reused. A caller
92/// that moves the consumer to another CPU must destroy the old registration
93/// and create a new owner-local one rather than remotely programming a
94/// physical comparator.
95pub fn arm_hard_kernel_timer(
96    handle: HardKernelTimerHandle,
97    deadline: MonotonicDeadline,
98) -> Result<(), TaskError> {
99    validate_task_context()?;
100    let update = {
101        let mut irq = RuntimeIrqGuard::enter();
102        let cpu = runtime_current_cpu_mut(&mut irq)?;
103        if cpu.owner() != handle.owner() {
104            return Err(TaskError::CpuOwnerMismatch {
105                expected: handle.owner().as_u32(),
106                actual: cpu.owner().as_u32(),
107            });
108        }
109        let mut deadline_base = cpu
110            .remote()
111            .lock_deadline_activity(DeadlineBaseGuardSource::Registration);
112        let non_timer = deadline_base.non_timer;
113        if !deadline_base
114            .kernel_timers
115            .arm_hard(handle.into(), deadline)
116        {
117            return Err(TaskError::InvalidConfiguration);
118        }
119        match CpuLocal::update_scheduler_deadline_registration_publication_if_changed(
120            &mut deadline_base,
121            non_timer,
122        ) {
123            Ok(update) => update,
124            Err(error) => {
125                assert_eq!(
126                    deadline_base.kernel_timers.disarm_hard(handle.into()),
127                    Some(Some(deadline)),
128                    "failed hard-timer arm publication must restore inactivity"
129                );
130                return Err(error);
131            }
132        }
133    };
134    if let Some(update) = update {
135        task_runtime::publish_scheduler_deadline(update);
136    }
137    Ok(())
138}
139
140/// Disarms one stable hard timer without destroying its callback payload.
141///
142/// Remote disarm only changes the logical owner base. Any already programmed
143/// edge remains conservative and is reconciled by that CPU's firing
144/// transaction; this operation never writes another CPU's comparator.
145pub fn disarm_hard_kernel_timer(handle: HardKernelTimerHandle) -> Result<(), TaskError> {
146    validate_task_context()?;
147    let update = {
148        let mut irq = RuntimeIrqGuard::enter();
149        let current = runtime_current_cpu_mut(&mut irq)?;
150        let system = runtime_task_system()?;
151        let remote = system
152            .cpu_remote(handle.owner())
153            .ok_or(TaskError::InvalidConfiguration)?;
154        let local_owner = current.owner() == handle.owner();
155        let mut deadline_base =
156            remote.lock_deadline_activity(DeadlineBaseGuardSource::Registration);
157        let non_timer = local_owner.then_some(deadline_base.non_timer);
158        let transition = deadline_base
159            .kernel_timers
160            .disarm_hard(handle.into())
161            .ok_or(TaskError::InvalidConfiguration)?;
162        let (Some(non_timer), Some(previous_deadline)) = (non_timer, transition) else {
163            return Ok(());
164        };
165        match CpuLocal::update_scheduler_deadline_registration_publication_if_changed(
166            &mut deadline_base,
167            non_timer,
168        ) {
169            Ok(update) => update,
170            Err(error) => {
171                assert!(
172                    deadline_base
173                        .kernel_timers
174                        .arm_hard(handle.into(), previous_deadline),
175                    "failed hard-timer disarm publication must restore the active entry"
176                );
177                return Err(error);
178            }
179        }
180    };
181    if let Some(update) = update {
182        task_runtime::publish_scheduler_deadline(update);
183    }
184    Ok(())
185}
186
187fn register_kernel_timer_entry(entry: KernelTimerEntry) -> Result<KernelTimerHandle, TaskError> {
188    let result = {
189        let mut irq = RuntimeIrqGuard::enter();
190        let cpu = runtime_current_cpu_mut(&mut irq)?;
191        let owner = cpu.owner();
192        let mut deadline_base = cpu
193            .remote()
194            .lock_deadline_activity(DeadlineBaseGuardSource::Registration);
195        let non_timer = deadline_base.non_timer;
196        let inserted = deadline_base.kernel_timers.insert(owner, entry);
197        match inserted {
198            Ok(handle) => {
199                match CpuLocal::update_scheduler_deadline_registration_publication_if_changed(
200                    &mut deadline_base,
201                    non_timer,
202                ) {
203                    Ok(update) => KernelTimerRegistrationResult::Registered(handle, update),
204                    Err(error) => {
205                        let removed = deadline_base
206                            .kernel_timers
207                            .cancel(handle)
208                            .1
209                            .expect("failed timer publication must roll back its new entry");
210                        KernelTimerRegistrationResult::Rejected(error, removed)
211                    }
212                }
213            }
214            Err(entry) => KernelTimerRegistrationResult::Rejected(TaskError::TimerCapacity, entry),
215        }
216    };
217    finish_kernel_timer_registration(result)
218}
219
220/// Cancels a registration without waiting for a callback already claimed.
221///
222/// `CancellationDeferred` accepts destruction and suppresses callback restart,
223/// but is not a callback-completion or payload-reclamation barrier.
224///
225/// A remote cancellation mutates only the original owner base. It may leave a
226/// conservative stale hardware edge; only the owner CPU may reprogram its
227/// physical comparator.
228pub fn cancel_kernel_timer(
229    handle: KernelTimerHandle,
230) -> Result<KernelTimerCancelOutcome, TaskError> {
231    validate_task_context()?;
232    let system = runtime_task_system()?;
233    let result = {
234        let mut irq = RuntimeIrqGuard::enter();
235        let current = runtime_current_cpu_mut(&mut irq)?;
236        let remote = system
237            .cpu_remote(handle.owner())
238            .ok_or(TaskError::InvalidConfiguration)?;
239        let local_owner = current.owner() == handle.owner();
240        let mut deadline_base =
241            remote.lock_deadline_activity(DeadlineBaseGuardSource::Registration);
242        let non_timer = local_owner.then_some(deadline_base.non_timer);
243        let (cancel_outcome, mut removed) = deadline_base.kernel_timers.cancel(handle);
244        let outcome = if removed.is_some() {
245            if let Some(non_timer) = non_timer {
246                match CpuLocal::update_scheduler_deadline_registration_publication_if_changed(
247                    &mut deadline_base,
248                    non_timer,
249                ) {
250                    Ok(Some(update)) => {
251                        drop(deadline_base);
252                        task_runtime::publish_scheduler_deadline(update);
253                        Ok(KernelTimerCancelOutcome::Cancelled)
254                    }
255                    Ok(None) => Ok(KernelTimerCancelOutcome::Cancelled),
256                    Err(error) => {
257                        deadline_base.kernel_timers.restore_cancelled(
258                            removed
259                                .take()
260                                .expect("failed cancellation publication must restore its entry"),
261                        );
262                        Err(error)
263                    }
264                }
265            } else {
266                Ok(KernelTimerCancelOutcome::Cancelled)
267            }
268        } else {
269            Ok(cancel_outcome)
270        };
271        KernelTimerCancellationResult { outcome, removed }
272    };
273    drop(result.removed);
274    result.outcome
275}
276
277fn finish_kernel_timer_registration(
278    result: KernelTimerRegistrationResult,
279) -> Result<KernelTimerHandle, TaskError> {
280    match result {
281        KernelTimerRegistrationResult::Registered(handle, update) => {
282            if let Some(update) = update {
283                task_runtime::publish_scheduler_deadline(update);
284            }
285            Ok(handle)
286        }
287        KernelTimerRegistrationResult::Rejected(error, entry) => {
288            drop(entry);
289            Err(error)
290        }
291    }
292}
293
294fn kernel_timer_error(error: TaskDeadlineError) -> TaskError {
295    match error {
296        TaskDeadlineError::Capacity => TaskError::TimerCapacity,
297        TaskDeadlineError::GenerationExhausted | TaskDeadlineError::KindMismatch => {
298            TaskError::InvalidConfiguration
299        }
300    }
301}