ax_task/sched/system/cpu/local/
owner_deadline.rs1use super::*;
4use crate::sched::system::cpu::remote::SchedulerNonTimerDeadlines;
5
6#[derive(Clone, Copy, Debug, Eq, PartialEq)]
7pub(crate) struct SoftTimerExpireBatch {
8 expired: usize,
9 pending: bool,
10}
11
12pub(crate) enum KtimerServiceClaim {
13 Kernel(KernelTimerExecution),
14 Task(ExpiredTaskDeadline),
15 Reap(KernelTimerEntry),
16}
17
18pub(crate) enum HardTimerServiceClaim {
19 Kernel(KernelTimerExecution),
20 Park(Option<Arc<ThreadCore>>),
21 Scheduler(ExpiredTaskDeadline),
22}
23
24pub(crate) enum HardTimerServiceStep {
25 Claim(HardTimerServiceClaim),
26 Complete { soft: SoftTimerExpireBatch },
27}
28
29impl SoftTimerExpireBatch {
30 pub(crate) const fn expired(self) -> usize {
31 self.expired
32 }
33
34 pub(crate) const fn pending(self) -> bool {
35 self.pending
36 }
37}
38
39#[derive(Clone, Copy)]
42pub(crate) struct SchedulerDeadlineRqObservation {
43 runtime_deadline: SchedulerRuntimeDeadline,
44 has_periodic_fair_balance_work: bool,
45}
46
47enum SchedulerDeadlinePublicationOutcome {
48 Unchanged(SchedulerDeadlineUpdate),
49 Changed(SchedulerDeadlineUpdate),
50}
51
52impl SchedulerDeadlinePublicationOutcome {
53 const fn update(self) -> SchedulerDeadlineUpdate {
54 match self {
55 Self::Unchanged(update) | Self::Changed(update) => update,
56 }
57 }
58
59 const fn changed_update(self) -> Option<SchedulerDeadlineUpdate> {
60 match self {
61 Self::Unchanged(_) => None,
62 Self::Changed(update) => Some(update),
63 }
64 }
65}
66
67impl CpuLocal {
68 pub(crate) fn can_reuse_scheduler_deadline_for_rq_observation(
77 &self,
78 rq_observation: SchedulerDeadlineRqObservation,
79 ) -> bool {
80 let fair_balance = rq_observation
81 .has_periodic_fair_balance_work
82 .then(|| self.dispatch.fair_balance_deadline())
83 .flatten();
84 let rt_period = self.rt_bandwidth.deadline_for(self.owner);
85 let non_timer = SchedulerNonTimerDeadlines {
86 deadline: [fair_balance, rt_period].into_iter().flatten().min(),
87 };
88 self.remote.deadline_publication_snapshot_matches(non_timer)
89 }
90
91 pub(crate) fn scheduler_work_due(
92 mut self: Pin<&mut Self>,
93 monotonic_now: MonotonicInstant,
94 ) -> SchedulerDeadlineRqObservation {
95 let rq_observation = self.scheduler_deadline_rq_observation();
96 self.as_mut()
97 .scheduler_work_due_from_rq_observation(monotonic_now, rq_observation)
98 }
99
100 pub(crate) fn scheduler_work_due_from_rq_observation(
101 self: Pin<&mut Self>,
102 monotonic_now: MonotonicInstant,
103 rq_observation: SchedulerDeadlineRqObservation,
104 ) -> SchedulerDeadlineRqObservation {
105 let this = unsafe { self.get_unchecked_mut() };
108 if matches!(
109 rq_observation.runtime_deadline,
110 SchedulerRuntimeDeadline::Due
111 ) {
112 this.remote.request_reschedule(RescheduleKind::Immediate);
113 }
114 if rq_observation.has_periodic_fair_balance_work
115 && this.dispatch.publish_fair_balance_due(monotonic_now)
116 {
117 this.remote.request_scheduler_work();
118 }
119 rq_observation
120 }
121
122 pub(crate) fn scheduler_runtime_deadline_for_rq_observation(
123 &self,
124 rq_observation: SchedulerDeadlineRqObservation,
125 ) -> SchedulerRuntimeDeadline {
126 if self.remote.immediate_preemption_requested() {
127 SchedulerRuntimeDeadline::Disarmed
128 } else {
129 rq_observation.runtime_deadline
130 }
131 }
132
133 fn next_non_timer_deadline_from_rq_observation(
134 &self,
135 rq_observation: SchedulerDeadlineRqObservation,
136 ) -> SchedulerNonTimerDeadlines {
137 let fair_balance = rq_observation
138 .has_periodic_fair_balance_work
139 .then(|| self.dispatch.fair_balance_deadline())
140 .flatten();
141 let rt_period = self.rt_bandwidth.deadline_for(self.owner);
142 SchedulerNonTimerDeadlines {
143 deadline: [fair_balance, rt_period].into_iter().flatten().min(),
144 }
145 }
146
147 pub(crate) fn next_scheduler_deadline_update_if_changed(
148 mut self: Pin<&mut Self>,
149 source: SchedulerDeadlineDerivationSource,
150 ) -> Result<Option<SchedulerDeadlineUpdate>, TaskError> {
151 let rq_observation = self.scheduler_deadline_rq_observation();
152 self.as_mut()
153 .update_scheduler_deadline_publication_if_changed_from_rq_observation(
154 rq_observation,
155 source,
156 )
157 }
158
159 pub(crate) fn next_scheduler_deadline_update_if_changed_from_rq_observation(
160 mut self: Pin<&mut Self>,
161 rq_observation: SchedulerDeadlineRqObservation,
162 source: SchedulerDeadlineDerivationSource,
163 ) -> Result<Option<SchedulerDeadlineUpdate>, TaskError> {
164 self.as_mut()
165 .update_scheduler_deadline_publication_if_changed_from_rq_observation(
166 rq_observation,
167 source,
168 )
169 }
170
171 pub(crate) fn next_scheduler_deadline_update_from_rq_observation(
172 mut self: Pin<&mut Self>,
173 rq_observation: SchedulerDeadlineRqObservation,
174 source: SchedulerDeadlineDerivationSource,
175 ) -> Result<SchedulerDeadlineUpdate, TaskError> {
176 self.as_mut()
177 .update_scheduler_deadline_publication_from_rq_observation(rq_observation, source)
178 .map(SchedulerDeadlinePublicationOutcome::update)
179 }
180
181 fn update_scheduler_deadline_publication_from_rq_observation(
182 self: Pin<&mut Self>,
183 rq_observation: SchedulerDeadlineRqObservation,
184 source: SchedulerDeadlineDerivationSource,
185 ) -> Result<SchedulerDeadlinePublicationOutcome, TaskError> {
186 self.as_ref()
187 .get_ref()
188 .record_scheduler_deadline_derivation(source);
189 let non_timer = self
193 .as_ref()
194 .get_ref()
195 .next_non_timer_deadline_from_rq_observation(rq_observation);
196 let mut task_deadlines = self.remote.lock_deadline_publication();
197 Self::update_scheduler_deadline_publication_in_base(&mut task_deadlines, non_timer)
198 }
199
200 fn update_scheduler_deadline_publication_if_changed_from_rq_observation(
201 self: Pin<&mut Self>,
202 rq_observation: SchedulerDeadlineRqObservation,
203 source: SchedulerDeadlineDerivationSource,
204 ) -> Result<Option<SchedulerDeadlineUpdate>, TaskError> {
205 self.as_ref()
206 .get_ref()
207 .record_scheduler_deadline_derivation(source);
208 let non_timer = self
209 .as_ref()
210 .get_ref()
211 .next_non_timer_deadline_from_rq_observation(rq_observation);
212 if self.remote.deadline_publication_snapshot_matches(non_timer) {
213 return Ok(None);
214 }
215 let mut task_deadlines = self.remote.lock_deadline_publication();
216 Self::update_scheduler_deadline_publication_in_base(&mut task_deadlines, non_timer)
217 .map(SchedulerDeadlinePublicationOutcome::changed_update)
218 }
219
220 fn record_scheduler_deadline_derivation(&self, source: SchedulerDeadlineDerivationSource) {
221 #[cfg(feature = "qperf-metrics")]
222 crate::diagnostics::counters::record_scheduler_deadline_derivation(source);
223 #[cfg(not(feature = "qperf-metrics"))]
224 let _ = source;
225 }
226
227 fn update_scheduler_deadline_publication_in_base(
228 task_deadlines: &mut crate::sched::system::cpu::remote::CpuDeadlineState,
229 non_timer: SchedulerNonTimerDeadlines,
230 ) -> Result<SchedulerDeadlinePublicationOutcome, TaskError> {
231 task_deadlines.non_timer = non_timer;
232 let timer = task_deadlines.timer_deadline();
233 let publication = SchedulerDeadlinePublicationState {
234 deadline: [timer, non_timer.deadline].into_iter().flatten().min(),
235 };
236 if task_deadlines.publication == Some(publication) {
237 let update =
238 SchedulerDeadlineUpdate::try_new(task_deadlines.generation, publication.deadline)
239 .ok_or(TaskError::InvalidConfiguration)?;
240 return Ok(SchedulerDeadlinePublicationOutcome::Unchanged(update));
241 }
242 Self::commit_scheduler_deadline_publication(task_deadlines, publication)
243 .map(SchedulerDeadlinePublicationOutcome::Changed)
244 }
245
246 pub(crate) fn update_scheduler_deadline_registration_publication(
247 task_deadlines: &mut crate::sched::system::cpu::remote::CpuDeadlineState,
248 non_timer: SchedulerNonTimerDeadlines,
249 ) -> Result<SchedulerDeadlineUpdate, TaskError> {
250 Self::update_scheduler_deadline_publication_in_base(task_deadlines, non_timer)
253 .map(SchedulerDeadlinePublicationOutcome::update)
254 }
255
256 pub(crate) fn update_scheduler_deadline_registration_publication_if_changed(
257 task_deadlines: &mut crate::sched::system::cpu::remote::CpuDeadlineState,
258 non_timer: SchedulerNonTimerDeadlines,
259 ) -> Result<Option<SchedulerDeadlineUpdate>, TaskError> {
260 Self::update_scheduler_deadline_publication_in_base(task_deadlines, non_timer)
261 .map(SchedulerDeadlinePublicationOutcome::changed_update)
262 }
263
264 fn commit_scheduler_deadline_publication(
265 task_deadlines: &mut crate::sched::system::cpu::remote::CpuDeadlineState,
266 publication: SchedulerDeadlinePublicationState,
267 ) -> Result<SchedulerDeadlineUpdate, TaskError> {
268 task_deadlines.generation = task_deadlines
269 .generation
270 .checked_add(1)
271 .ok_or(TaskError::InvalidConfiguration)?;
272 let update =
273 SchedulerDeadlineUpdate::try_new(task_deadlines.generation, publication.deadline)
274 .ok_or(TaskError::InvalidConfiguration)?;
275 task_deadlines.publication = Some(publication);
276 Ok(update)
277 }
278
279 pub(crate) fn scheduler_deadline_rq_observation(&self) -> SchedulerDeadlineRqObservation {
280 let run_queue = self
281 .remote
282 .lock_run_queue(RunQueueGuardSource::TimerDeadlineDerivationObservation);
283 self.scheduler_deadline_rq_observation_in_run_queue(&run_queue)
284 }
285
286 pub(crate) fn scheduler_deadline_rq_observation_in_run_queue(
287 &self,
288 run_queue: &CpuRunQueueState,
289 ) -> SchedulerDeadlineRqObservation {
290 let current_thread = run_queue.current_thread();
291 let idle = run_queue.idle();
292 let runtime_deadline = run_queue.current_runtime_deadline();
293 let current_non_idle = current_thread.is_some() && current_thread != idle;
294 let has_periodic_fair_balance_work =
295 run_queue.has_fair() && run_queue.nr_running() > usize::from(current_non_idle);
296 SchedulerDeadlineRqObservation {
297 runtime_deadline,
298 has_periodic_fair_balance_work,
299 }
300 }
301
302 pub fn load_summary(&self) -> CpuLoadSummary {
304 self.remote.load_summary()
305 }
306
307 pub fn queued_summary(&self) -> usize {
309 self.remote.queued_summary()
310 }
311
312 pub(crate) fn fair_balance_pending(&self) -> bool {
313 self.dispatch.fair_balance_pending()
314 }
315
316 pub(crate) fn reset_fair_balance(
317 self: Pin<&mut Self>,
318 now: MonotonicInstant,
319 minimum_interval_ns: u64,
320 ) {
321 let this = unsafe { self.get_unchecked_mut() };
323 let interval_ns = minimum_interval_ns.max(1);
324 this.dispatch.fair_balance_interval_ns = interval_ns;
325 this.dispatch.defer_fair_balance(now, interval_ns);
326 }
327
328 pub(crate) fn backoff_fair_balance(
329 self: Pin<&mut Self>,
330 now: MonotonicInstant,
331 minimum_interval_ns: u64,
332 maximum_interval_ns: u64,
333 ) {
334 let this = unsafe { self.get_unchecked_mut() };
336 let minimum_interval_ns = minimum_interval_ns.max(1);
337 let maximum_interval_ns = maximum_interval_ns.max(minimum_interval_ns);
338 let current_interval_ns = this
339 .dispatch
340 .fair_balance_interval_ns
341 .clamp(minimum_interval_ns, maximum_interval_ns);
342 let next_interval_ns = current_interval_ns
343 .saturating_mul(2)
344 .min(maximum_interval_ns);
345 this.dispatch.fair_balance_interval_ns = next_interval_ns;
346 this.dispatch.defer_fair_balance(now, next_interval_ns);
347 }
348
349 pub(crate) fn clear_fair_balance(self: Pin<&mut Self>) {
350 self.dispatch_state_mut().clear_fair_balance();
351 }
352
353 pub(crate) fn claim_ktimer_service_step(
359 self: Pin<&mut Self>,
360 now: MonotonicInstant,
361 task_budget: usize,
362 ) -> (Option<KtimerServiceClaim>, bool) {
363 let batch_limit = self.drain.batch_limit();
364 let Some(mut deadlines) = self
365 .remote
366 .lock_active_deadline_activity(DeadlineBaseGuardSource::SoftExpiry)
367 else {
368 return (None, false);
369 };
370 if !deadlines.kernel_timers.has_expired() && deadlines.kernel_timers.has_due_soft(now) {
371 deadlines.kernel_timers.expire_due_soft(now, 1);
372 }
373 if deadlines.expired_count == 0
374 && deadlines.queue.has_immediately_actionable_soft_entry(now)
375 {
376 Self::promote_due_task_deadlines_in_base(&mut deadlines, batch_limit, now, task_budget);
377 }
378 let claim = if let Some(completed) = deadlines.kernel_timers.claim_completed() {
379 Some(KtimerServiceClaim::Reap(completed))
380 } else {
381 let has_kernel = deadlines.kernel_timers.has_expired();
382 let has_task = deadlines.expired_count != 0;
383 match deadlines.select_service_claim_class(has_kernel, has_task) {
384 Some(KtimerClaimClass::Kernel) => {
385 let execution = deadlines
386 .kernel_timers
387 .claim_expired()
388 .expect("an expired kernel timer must remain claimable");
389 Some(KtimerServiceClaim::Kernel(execution))
390 }
391 Some(KtimerClaimClass::Task) => {
392 let event = deadlines
393 .claim_next_buffered_expiration()
394 .expect("a buffered task expiration must remain claimable");
395 Some(KtimerServiceClaim::Task(event))
396 }
397 None => None,
398 }
399 };
400 let pending = deadlines.expired_count != 0
401 || deadlines.queue.has_immediately_actionable_soft_entry(now)
402 || deadlines.kernel_timers.has_expired()
403 || deadlines.kernel_timers.has_completed()
404 || deadlines.kernel_timers.has_due_soft(now);
405 deadlines.softirq_activated = pending;
406 (claim, pending)
407 }
408
409 fn promote_due_task_deadlines_in_base(
410 task_deadlines: &mut crate::sched::system::cpu::remote::CpuDeadlineState,
411 batch_limit: usize,
412 now: MonotonicInstant,
413 budget: usize,
414 ) -> TaskDeadlineExpireBatch {
415 let expired_count = task_deadlines.expired_count;
416 let available = task_deadlines
417 .expired_buffer
418 .len()
419 .saturating_sub(expired_count);
420 let request = TaskDeadlineExpireRequest::new(now, budget.min(batch_limit).min(available));
421 let crate::sched::system::cpu::remote::CpuDeadlineState {
422 queue,
423 expired_buffer,
424 ..
425 } = &mut *task_deadlines;
426 let output = &mut expired_buffer[expired_count..];
427 let batch = queue.expire_soft(request, output);
428 task_deadlines.expired_count += batch.expired();
429 batch
430 }
431
432 pub(crate) fn claim_due_hard_timer_step(
439 self: Pin<&mut Self>,
440 now: MonotonicInstant,
441 budget: usize,
442 ) -> Result<HardTimerServiceStep, TaskError> {
443 self.as_ref()
444 .get_ref()
445 .record_scheduler_deadline_derivation(SchedulerDeadlineDerivationSource::ClockEvent);
446 let batch_limit = self.drain.batch_limit();
447 let mut task_deadlines = self
448 .remote
449 .lock_deadline_activity(DeadlineBaseGuardSource::HardExpiry);
450 let scheduler_deadline = task_deadlines.queue.next_hard_deadline();
451 let kernel_deadline = task_deadlines.kernel_timers.next_hard_deadline();
452 let claim = match (scheduler_deadline, kernel_deadline) {
453 (Some(scheduler), Some(kernel)) if kernel < scheduler => task_deadlines
454 .kernel_timers
455 .claim_due_hard(now)
456 .map(HardTimerServiceClaim::Kernel),
457 (Some(scheduler), _) if now.reached(scheduler) => task_deadlines
458 .queue
459 .claim_due_hard(now)
460 .map(|claim| match claim {
461 HardTaskDeadlineClaim::Park { event, thread } => {
462 let park_generation = event
463 .kind()
464 .and_then(TaskDeadlineKind::park_generation)
465 .expect("a hard park deadline retains its park generation");
466 let completed = thread.complete_sleep_timer(event.token().generation());
467 let ready =
468 completed && thread.ordinary_park_generation() == park_generation;
469 HardTimerServiceClaim::Park(ready.then_some(thread))
470 }
471 HardTaskDeadlineClaim::Scheduler(event) => {
472 HardTimerServiceClaim::Scheduler(event)
473 }
474 }),
475 (_, Some(kernel)) if now.reached(kernel) => task_deadlines
476 .kernel_timers
477 .claim_due_hard(now)
478 .map(HardTimerServiceClaim::Kernel),
479 _ => None,
480 };
481 if let Some(claim) = claim {
482 return Ok(HardTimerServiceStep::Claim(claim));
483 }
484
485 let task_batch =
486 Self::promote_due_task_deadlines_in_base(&mut task_deadlines, batch_limit, now, budget);
487 let kernel_batch = task_deadlines
488 .kernel_timers
489 .expire_due_soft(now, budget.saturating_sub(task_batch.processed()));
490 if task_batch.expired() != 0
491 || task_batch.pending()
492 || kernel_batch.expired() != 0
493 || kernel_batch.pending()
494 {
495 task_deadlines.softirq_activated = true;
496 }
497 let soft = SoftTimerExpireBatch {
498 expired: task_batch.expired().saturating_add(kernel_batch.expired()),
499 pending: task_batch.pending() || kernel_batch.pending(),
500 };
501 let non_timer = task_deadlines.non_timer;
502 Self::update_scheduler_deadline_publication_in_base(&mut task_deadlines, non_timer)?;
503 drop(task_deadlines);
504 if soft.pending() || soft.expired() != 0 {
505 self.remote.publish_ktimer_work();
506 }
507 Ok(HardTimerServiceStep::Complete { soft })
508 }
509
510 pub(crate) fn complete_hard_kernel_timer_execution(
511 self: Pin<&mut Self>,
512 execution: KernelTimerExecution,
513 action: HardKernelTimerAction,
514 ) {
515 let mut deadlines = self
516 .remote
517 .lock_deadline_activity(DeadlineBaseGuardSource::HardExpiry);
518 if deadlines
519 .kernel_timers
520 .complete_hard_execution(execution, action)
521 {
522 deadlines.softirq_activated = true;
526 drop(deadlines);
527 self.remote.publish_ktimer_work();
528 }
529 }
530
531 pub(crate) fn take_buffered_expiration(
535 self: Pin<&mut Self>,
536 registration: &TaskDeadlineRegistration,
537 ) -> Option<ExpiredTaskDeadline> {
538 self.remote
539 .lock_deadline_activity(DeadlineBaseGuardSource::SoftExpiry)
540 .take_buffered_expiration(registration)
541 }
542}