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 scheduler_work_due(
69 mut self: Pin<&mut Self>,
70 monotonic_now: MonotonicInstant,
71 ) -> SchedulerDeadlineRqObservation {
72 let rq_observation = self.scheduler_deadline_rq_observation();
73 self.as_mut()
74 .scheduler_work_due_from_rq_observation(monotonic_now, rq_observation)
75 }
76
77 pub(crate) fn scheduler_work_due_from_rq_observation(
78 self: Pin<&mut Self>,
79 monotonic_now: MonotonicInstant,
80 rq_observation: SchedulerDeadlineRqObservation,
81 ) -> SchedulerDeadlineRqObservation {
82 let this = unsafe { self.get_unchecked_mut() };
85 if matches!(
86 rq_observation.runtime_deadline,
87 SchedulerRuntimeDeadline::Due
88 ) {
89 this.remote.request_reschedule(RescheduleKind::Immediate);
90 }
91 if rq_observation.has_periodic_fair_balance_work
92 && this.dispatch.publish_fair_balance_due(monotonic_now)
93 {
94 this.remote.request_scheduler_work();
95 }
96 rq_observation
97 }
98
99 pub(crate) fn scheduler_runtime_deadline_for_rq_observation(
100 &self,
101 rq_observation: SchedulerDeadlineRqObservation,
102 ) -> SchedulerRuntimeDeadline {
103 if self.remote.immediate_preemption_requested() {
104 SchedulerRuntimeDeadline::Disarmed
105 } else {
106 rq_observation.runtime_deadline
107 }
108 }
109
110 fn shared_non_timer_deadline(&self) -> SchedulerNonTimerDeadlines {
117 SchedulerNonTimerDeadlines {
118 deadline: self.rt_bandwidth.deadline_for(self.owner),
119 }
120 }
121
122 pub(crate) fn next_scheduler_deadline_update_if_changed(
123 mut self: Pin<&mut Self>,
124 source: SchedulerDeadlineDerivationSource,
125 ) -> Result<Option<SchedulerDeadlineUpdate>, TaskError> {
126 self.as_mut()
127 .update_scheduler_deadline_publication_if_changed(source)
128 }
129
130 pub(crate) fn next_scheduler_deadline_update(
131 mut self: Pin<&mut Self>,
132 source: SchedulerDeadlineDerivationSource,
133 ) -> Result<SchedulerDeadlineUpdate, TaskError> {
134 self.as_mut()
135 .update_scheduler_deadline_publication(source)
136 .map(SchedulerDeadlinePublicationOutcome::update)
137 }
138
139 fn update_scheduler_deadline_publication(
140 self: Pin<&mut Self>,
141 source: SchedulerDeadlineDerivationSource,
142 ) -> Result<SchedulerDeadlinePublicationOutcome, TaskError> {
143 self.as_ref()
144 .get_ref()
145 .record_scheduler_deadline_derivation(source);
146 let non_timer = self.as_ref().get_ref().shared_non_timer_deadline();
150 let mut task_deadlines = self.remote.lock_deadline_publication();
151 Self::update_scheduler_deadline_publication_in_base(&mut task_deadlines, non_timer)
152 }
153
154 fn update_scheduler_deadline_publication_if_changed(
155 self: Pin<&mut Self>,
156 source: SchedulerDeadlineDerivationSource,
157 ) -> Result<Option<SchedulerDeadlineUpdate>, TaskError> {
158 self.as_ref()
159 .get_ref()
160 .record_scheduler_deadline_derivation(source);
161 let non_timer = self.as_ref().get_ref().shared_non_timer_deadline();
162 if self.remote.deadline_publication_snapshot_matches(non_timer) {
163 return Ok(None);
164 }
165 let mut task_deadlines = self.remote.lock_deadline_publication();
166 Self::update_scheduler_deadline_publication_in_base(&mut task_deadlines, non_timer)
167 .map(SchedulerDeadlinePublicationOutcome::changed_update)
168 }
169
170 fn record_scheduler_deadline_derivation(&self, source: SchedulerDeadlineDerivationSource) {
171 #[cfg(feature = "qperf-metrics")]
172 crate::diagnostics::counters::record_scheduler_deadline_derivation(source);
173 #[cfg(not(feature = "qperf-metrics"))]
174 let _ = source;
175 }
176
177 fn update_scheduler_deadline_publication_in_base(
178 task_deadlines: &mut crate::sched::system::cpu::remote::CpuDeadlineState,
179 non_timer: SchedulerNonTimerDeadlines,
180 ) -> Result<SchedulerDeadlinePublicationOutcome, TaskError> {
181 task_deadlines.non_timer = non_timer;
182 let timer = task_deadlines.timer_deadline();
183 let publication = SchedulerDeadlinePublicationState {
184 deadline: [timer, non_timer.deadline].into_iter().flatten().min(),
185 };
186 if task_deadlines.publication == Some(publication) {
187 let update =
188 SchedulerDeadlineUpdate::try_new(task_deadlines.generation, publication.deadline)
189 .ok_or(TaskError::InvalidConfiguration)?;
190 return Ok(SchedulerDeadlinePublicationOutcome::Unchanged(update));
191 }
192 Self::commit_scheduler_deadline_publication(task_deadlines, publication)
193 .map(SchedulerDeadlinePublicationOutcome::Changed)
194 }
195
196 pub(crate) fn update_scheduler_deadline_registration_publication(
197 task_deadlines: &mut crate::sched::system::cpu::remote::CpuDeadlineState,
198 non_timer: SchedulerNonTimerDeadlines,
199 ) -> Result<SchedulerDeadlineUpdate, TaskError> {
200 Self::update_scheduler_deadline_publication_in_base(task_deadlines, non_timer)
203 .map(SchedulerDeadlinePublicationOutcome::update)
204 }
205
206 pub(crate) fn update_scheduler_deadline_registration_publication_if_changed(
207 task_deadlines: &mut crate::sched::system::cpu::remote::CpuDeadlineState,
208 non_timer: SchedulerNonTimerDeadlines,
209 ) -> Result<Option<SchedulerDeadlineUpdate>, TaskError> {
210 Self::update_scheduler_deadline_publication_in_base(task_deadlines, non_timer)
211 .map(SchedulerDeadlinePublicationOutcome::changed_update)
212 }
213
214 fn commit_scheduler_deadline_publication(
215 task_deadlines: &mut crate::sched::system::cpu::remote::CpuDeadlineState,
216 publication: SchedulerDeadlinePublicationState,
217 ) -> Result<SchedulerDeadlineUpdate, TaskError> {
218 task_deadlines.generation = task_deadlines
219 .generation
220 .checked_add(1)
221 .ok_or(TaskError::InvalidConfiguration)?;
222 let update =
223 SchedulerDeadlineUpdate::try_new(task_deadlines.generation, publication.deadline)
224 .ok_or(TaskError::InvalidConfiguration)?;
225 task_deadlines.publication = Some(publication);
226 Ok(update)
227 }
228
229 pub(crate) fn scheduler_deadline_rq_observation(&self) -> SchedulerDeadlineRqObservation {
230 let run_queue = self
231 .remote
232 .lock_run_queue(RunQueueGuardSource::TimerDeadlineDerivationObservation);
233 self.scheduler_deadline_rq_observation_in_run_queue(&run_queue)
234 }
235
236 pub(crate) fn scheduler_deadline_rq_observation_in_run_queue(
237 &self,
238 run_queue: &CpuRunQueueState,
239 ) -> SchedulerDeadlineRqObservation {
240 let current_thread = run_queue.current_thread();
241 let idle = run_queue.idle();
242 let runtime_deadline = run_queue.current_runtime_deadline();
243 let current_non_idle = current_thread.is_some() && current_thread != idle;
244 let has_periodic_fair_balance_work =
245 run_queue.has_fair() && run_queue.nr_running() > usize::from(current_non_idle);
246 SchedulerDeadlineRqObservation {
247 runtime_deadline,
248 has_periodic_fair_balance_work,
249 }
250 }
251
252 pub fn load_summary(&self) -> CpuLoadSummary {
254 self.remote.load_summary()
255 }
256
257 pub fn queued_summary(&self) -> usize {
259 self.remote.queued_summary()
260 }
261
262 pub(crate) fn fair_balance_pending(&self) -> bool {
263 self.dispatch.fair_balance_pending()
264 }
265
266 pub(crate) fn reset_fair_balance(
267 self: Pin<&mut Self>,
268 now: MonotonicInstant,
269 minimum_interval_ns: u64,
270 ) {
271 let this = unsafe { self.get_unchecked_mut() };
273 let interval_ns = minimum_interval_ns.max(1);
274 this.dispatch.fair_balance_interval_ns = interval_ns;
275 this.dispatch.defer_fair_balance(now, interval_ns);
276 }
277
278 pub(crate) fn backoff_fair_balance(
279 self: Pin<&mut Self>,
280 now: MonotonicInstant,
281 minimum_interval_ns: u64,
282 maximum_interval_ns: u64,
283 ) {
284 let this = unsafe { self.get_unchecked_mut() };
286 let minimum_interval_ns = minimum_interval_ns.max(1);
287 let maximum_interval_ns = maximum_interval_ns.max(minimum_interval_ns);
288 let current_interval_ns = this
289 .dispatch
290 .fair_balance_interval_ns
291 .clamp(minimum_interval_ns, maximum_interval_ns);
292 let next_interval_ns = current_interval_ns
293 .saturating_mul(2)
294 .min(maximum_interval_ns);
295 this.dispatch.fair_balance_interval_ns = next_interval_ns;
296 this.dispatch.defer_fair_balance(now, next_interval_ns);
297 }
298
299 pub(crate) fn clear_fair_balance(self: Pin<&mut Self>) {
300 self.dispatch_state_mut().clear_fair_balance();
301 }
302
303 pub(crate) fn claim_ktimer_service_step(
309 self: Pin<&mut Self>,
310 now: MonotonicInstant,
311 task_budget: usize,
312 ) -> (Option<KtimerServiceClaim>, bool) {
313 let batch_limit = self.drain.batch_limit();
314 let Some(mut deadlines) = self
315 .remote
316 .lock_active_deadline_activity(DeadlineBaseGuardSource::SoftExpiry)
317 else {
318 return (None, false);
319 };
320 if !deadlines.kernel_timers.has_expired() && deadlines.kernel_timers.has_due_soft(now) {
321 deadlines.kernel_timers.expire_due_soft(now, 1);
322 }
323 if deadlines.expired_count == 0
324 && deadlines.queue.has_immediately_actionable_soft_entry(now)
325 {
326 Self::promote_due_task_deadlines_in_base(&mut deadlines, batch_limit, now, task_budget);
327 }
328 let claim = if let Some(completed) = deadlines.kernel_timers.claim_completed() {
329 Some(KtimerServiceClaim::Reap(completed))
330 } else {
331 let has_kernel = deadlines.kernel_timers.has_expired();
332 let has_task = deadlines.expired_count != 0;
333 match deadlines.select_service_claim_class(has_kernel, has_task) {
334 Some(KtimerClaimClass::Kernel) => {
335 let execution = deadlines
336 .kernel_timers
337 .claim_expired()
338 .expect("an expired kernel timer must remain claimable");
339 Some(KtimerServiceClaim::Kernel(execution))
340 }
341 Some(KtimerClaimClass::Task) => {
342 let event = deadlines
343 .claim_next_buffered_expiration()
344 .expect("a buffered task expiration must remain claimable");
345 Some(KtimerServiceClaim::Task(event))
346 }
347 None => None,
348 }
349 };
350 let pending = deadlines.expired_count != 0
351 || deadlines.queue.has_immediately_actionable_soft_entry(now)
352 || deadlines.kernel_timers.has_expired()
353 || deadlines.kernel_timers.has_completed()
354 || deadlines.kernel_timers.has_due_soft(now);
355 deadlines.softirq_activated = pending;
356 (claim, pending)
357 }
358
359 fn promote_due_task_deadlines_in_base(
360 task_deadlines: &mut crate::sched::system::cpu::remote::CpuDeadlineState,
361 batch_limit: usize,
362 now: MonotonicInstant,
363 budget: usize,
364 ) -> TaskDeadlineExpireBatch {
365 let expired_count = task_deadlines.expired_count;
366 let available = task_deadlines
367 .expired_buffer
368 .len()
369 .saturating_sub(expired_count);
370 let request = TaskDeadlineExpireRequest::new(now, budget.min(batch_limit).min(available));
371 let crate::sched::system::cpu::remote::CpuDeadlineState {
372 queue,
373 expired_buffer,
374 ..
375 } = &mut *task_deadlines;
376 let output = &mut expired_buffer[expired_count..];
377 let batch = queue.expire_soft(request, output);
378 task_deadlines.expired_count += batch.expired();
379 batch
380 }
381
382 pub(crate) fn claim_due_hard_timer_step(
389 self: Pin<&mut Self>,
390 now: MonotonicInstant,
391 budget: usize,
392 ) -> Result<HardTimerServiceStep, TaskError> {
393 self.as_ref()
394 .get_ref()
395 .record_scheduler_deadline_derivation(SchedulerDeadlineDerivationSource::ClockEvent);
396 let batch_limit = self.drain.batch_limit();
397 let mut task_deadlines = self
398 .remote
399 .lock_deadline_activity(DeadlineBaseGuardSource::HardExpiry);
400 let scheduler_deadline = task_deadlines.queue.next_hard_deadline();
401 let kernel_deadline = task_deadlines.kernel_timers.next_hard_deadline();
402 let claim = match (scheduler_deadline, kernel_deadline) {
403 (Some(scheduler), Some(kernel)) if kernel < scheduler => task_deadlines
404 .kernel_timers
405 .claim_due_hard(now)
406 .map(HardTimerServiceClaim::Kernel),
407 (Some(scheduler), _) if now.reached(scheduler) => task_deadlines
408 .queue
409 .claim_due_hard(now)
410 .map(|claim| match claim {
411 HardTaskDeadlineClaim::Park { event, thread } => {
412 let park_generation = event
413 .kind()
414 .and_then(TaskDeadlineKind::park_generation)
415 .expect("a hard park deadline retains its park generation");
416 let completed = thread.complete_sleep_timer(event.token().generation());
417 let ready =
418 completed && thread.ordinary_park_generation() == park_generation;
419 HardTimerServiceClaim::Park(ready.then_some(thread))
420 }
421 HardTaskDeadlineClaim::Scheduler(event) => {
422 HardTimerServiceClaim::Scheduler(event)
423 }
424 }),
425 (_, Some(kernel)) if now.reached(kernel) => task_deadlines
426 .kernel_timers
427 .claim_due_hard(now)
428 .map(HardTimerServiceClaim::Kernel),
429 _ => None,
430 };
431 if let Some(claim) = claim {
432 return Ok(HardTimerServiceStep::Claim(claim));
433 }
434
435 let task_batch =
436 Self::promote_due_task_deadlines_in_base(&mut task_deadlines, batch_limit, now, budget);
437 let kernel_batch = task_deadlines
438 .kernel_timers
439 .expire_due_soft(now, budget.saturating_sub(task_batch.processed()));
440 if task_batch.expired() != 0
441 || task_batch.pending()
442 || kernel_batch.expired() != 0
443 || kernel_batch.pending()
444 {
445 task_deadlines.softirq_activated = true;
446 }
447 let soft = SoftTimerExpireBatch {
448 expired: task_batch.expired().saturating_add(kernel_batch.expired()),
449 pending: task_batch.pending() || kernel_batch.pending(),
450 };
451 let non_timer = task_deadlines.non_timer;
452 Self::update_scheduler_deadline_publication_in_base(&mut task_deadlines, non_timer)?;
453 drop(task_deadlines);
454 if soft.pending() || soft.expired() != 0 {
455 self.remote.publish_ktimer_work();
456 }
457 Ok(HardTimerServiceStep::Complete { soft })
458 }
459
460 pub(crate) fn complete_hard_kernel_timer_execution(
461 self: Pin<&mut Self>,
462 execution: KernelTimerExecution,
463 action: HardKernelTimerAction,
464 ) {
465 let mut deadlines = self
466 .remote
467 .lock_deadline_activity(DeadlineBaseGuardSource::HardExpiry);
468 if deadlines
469 .kernel_timers
470 .complete_hard_execution(execution, action)
471 {
472 deadlines.softirq_activated = true;
476 drop(deadlines);
477 self.remote.publish_ktimer_work();
478 }
479 }
480
481 pub(crate) fn take_buffered_expiration(
485 self: Pin<&mut Self>,
486 registration: &TaskDeadlineRegistration,
487 ) -> Option<ExpiredTaskDeadline> {
488 self.remote
489 .lock_deadline_activity(DeadlineBaseGuardSource::SoftExpiry)
490 .take_buffered_expiration(registration)
491 }
492}