ax_task/sched/system/task_system/delivery/
control.rs1use super::*;
4
5impl TaskSystem {
6 pub fn drain_owner_control(
8 &self,
9 mut cpu: Pin<&mut CpuLocal>,
10 ) -> Result<OwnerControlDrain, TaskError> {
11 let drained = self.drain_owner_control_inner(cpu.as_mut())?;
12 if drained.pending {
13 cpu.defer_scheduler_work();
16 }
17 Ok(drained)
18 }
19
20 pub(super) fn drain_owner_control_inner(
21 &self,
22 mut cpu: Pin<&mut CpuLocal>,
23 ) -> Result<OwnerControlDrain, TaskError> {
24 self.ensure_owner_cpu_context(&cpu)?;
25 self.ensure_owner_cpu_online(&cpu)?;
26 if cpu.as_ref().get_ref().switch_handoff().is_some()
35 && cpu.remote().owner_control_inbox().has_pending()
36 {
37 return Ok(OwnerControlDrain {
38 drained: 0,
39 pending: true,
40 });
41 }
42 let (drained, pending) = {
43 let remote = Arc::clone(cpu.remote());
44 let scratch = cpu.as_mut().drain_state_mut();
45 let limit = scratch.batch_limit();
46 let batch = remote
47 .owner_control_inbox()
48 .drain(limit, &mut scratch.owner_control_buffer);
49 (batch.drained(), batch.pending())
50 };
51 let mut detached = [InboxMessage::EMPTY; crate::runtime::config::DEFAULT_BATCH_LIMIT];
52 detached[..drained].copy_from_slice(&cpu.drain_state().owner_control_buffer[..drained]);
53 let completed_incoming_migration_demand = detached[..drained]
58 .iter()
59 .filter(|message| message.operation() == InboxOperation::Migration)
60 .try_fold(0_u64, |demand, message| {
61 demand.checked_add(message.placement_demand())
62 })
63 .unwrap_or_else(|| {
64 task_runtime::fatal_invariant(0x4d49_4744, cpu.owner().as_u32() as usize)
65 });
66 let _incoming_migration = IncomingMigrationBatch::new(
67 Arc::clone(cpu.remote()),
68 completed_incoming_migration_demand,
69 );
70 let mut messages = DetachedOwnerMessageBatch::new(&detached[..drained]);
71 while let Some(message) = messages.next() {
72 let operation = message.operation();
73 if operation == InboxOperation::BalanceRequest {
74 let source = message
75 .source_cpu()
76 .ok_or(TaskError::InvalidConfiguration)?;
77 let target = message
78 .target_cpu()
79 .ok_or(TaskError::InvalidConfiguration)?;
80 if source != cpu.owner() {
81 return Err(TaskError::CpuOwnerMismatch {
82 expected: source.as_u32(),
83 actual: cpu.owner().as_u32(),
84 });
85 }
86 let reservation = message
87 .balance_reservation()
88 .ok_or(TaskError::InvalidConfiguration)?;
89 let balance_class = message
90 .balance_class()
91 .ok_or(TaskError::InvalidConfiguration)?;
92 let target_remote = self
93 .cpu_remotes
94 .get(target.as_usize())
95 .ok_or(TaskError::InvalidCpu(target.as_u32()))?;
96 let Some(mut claim) = target_remote.claim_idle_pull(reservation) else {
97 continue;
98 };
99 let source_has_candidate = match balance_class {
100 SchedulingClass::Deadline | SchedulingClass::Realtime => self
101 .root_domain
102 .cpu_has_overload(cpu.owner(), balance_class),
103 SchedulingClass::Fair => cpu.load_summary().has_pushable_fair(),
104 SchedulingClass::Stop => false,
105 };
106 if !source_has_candidate {
107 drop(claim);
108 continue;
109 }
110 if !claim.commit() {
111 continue;
112 }
113 let migrated = self.transfer_owner_balance_candidate(
114 cpu.as_mut(),
115 target,
116 BalanceReason::IdlePull,
117 Some(balance_class),
118 );
119 drop(claim);
120 match migrated {
121 Ok(BalanceTransferOutcome::Migrated(_)) => {}
122 Ok(BalanceTransferOutcome::NoCandidate) => {}
127 Ok(BalanceTransferOutcome::Retry) => {}
128 Err(error) => {
129 return Err(error);
130 }
131 }
132 continue;
133 }
134 if matches!(
135 operation,
136 InboxOperation::BalanceRequest | InboxOperation::Reclaim
137 ) {
138 return Err(TaskError::InvalidConfiguration);
139 }
140 if message.payload() == 0 {
141 continue;
142 }
143 let core = unsafe {
146 Arc::from_raw(ptr::with_exposed_provenance::<ThreadCore>(
147 message.payload(),
148 ))
149 };
150 let _delivery = core.accept_scheduler_inbox_delivery();
151 if core.id() != message.thread_id() {
152 continue;
153 }
154 let Some(_activity) = core.try_scheduler_activity() else {
155 continue;
158 };
159 if core.state() == ThreadState::Exited {
160 continue;
161 }
162 let owner = cpu.owner();
163 let source = message
164 .source_cpu()
165 .ok_or(TaskError::InvalidConfiguration)?;
166 let target = message
167 .target_cpu()
168 .ok_or(TaskError::InvalidConfiguration)?;
169 if operation == InboxOperation::DeadlineRefresh {
170 if source != owner || target != owner {
171 return Err(TaskError::CpuOwnerMismatch {
172 expected: source.as_u32(),
173 actual: owner.as_u32(),
174 });
175 }
176 let mut sched = core.sched().lock();
177 if sched.placement.queued_cpu() == Some(owner) {
178 self.activate_owner_deadline_bandwidth(&core, &mut sched, cpu.as_mut(), owner);
179 }
180 self.refresh_owner_deadline_timers_locked(&core, &mut sched, cpu.as_mut());
181 continue;
182 }
183 if operation == InboxOperation::AffinityUpdate {
184 if source != owner {
185 return Err(TaskError::CpuOwnerMismatch {
186 expected: source.as_u32(),
187 actual: owner.as_u32(),
188 });
189 }
190 self.reconcile_owner_affinity_update(cpu.as_mut(), &core)?;
191 continue;
192 }
193 if operation == InboxOperation::Migration {
194 if target != owner {
195 return Err(TaskError::CpuOwnerMismatch {
196 expected: target.as_u32(),
197 actual: owner.as_u32(),
198 });
199 }
200 let mut sched = core.sched().lock();
201 let committed_here = sched.placement.committed_migration_target() == Some(owner)
202 && sched.placement.queued_cpu().is_none()
203 && sched.placement.on_cpu().is_none();
204 let delayed_migration = sched.lifecycle.state() == ThreadState::Blocked
205 && committed_here
206 && core
207 .sched()
208 .active_option(&sched)
209 .and_then(|active| active.entity().fair())
210 .is_some_and(|fair| fair.is_delayed_migrating());
211 if delayed_migration {
212 let needs_affinity_move = !sched.affinity.affinity.contains(owner)
213 || sched.placement.requested_migration().is_some();
214 cpu.remote().cancel_idle_pull_if_uncommitted();
215 let remote = Arc::clone(cpu.remote());
216 let mut transaction = OwnerRqTxn::begin(self, &remote);
217 let current_fair = transaction.current_fair_contender();
218 transaction.update_fair_virtual_time(current_fair);
219 let policy = core.sched().active(&sched).policy();
220 let metadata = sched.rq_task_metadata()?;
221 let rt_quota_exempt = sched.is_pi_boosted_rt_owner_for(policy);
222 let active = core.sched().take_active(&mut sched);
223 let enqueue = transaction.enqueue_delayed_fair_transfer(
224 QueuedThread::new(
225 core.id(),
226 active,
227 Arc::clone(&core),
228 rt_quota_exempt,
229 sched.affinity.affinity.is_migration_capable(),
230 metadata,
231 ),
232 current_fair,
233 );
234 transaction.update_fair_virtual_time(current_fair);
235 sched.placement.activate(owner);
236 core.publish_effective_schedule(policy, enqueue.entity());
237 core.set_wake_cpu_hint(owner);
238 let affinity_completed =
239 Self::complete_affinity_if_satisfied_locked(&core, &sched);
240 let scheduler_deadline_refresh_required =
241 enqueue.scheduler_deadline_refresh_required();
242 transaction.commit();
243 drop(sched);
244 if affinity_completed {
245 core.notify_affinity_waiters();
246 }
247 if needs_affinity_move {
248 self.reconcile_owner_affinity_update(cpu.as_mut(), &core)?;
249 } else if scheduler_deadline_refresh_required {
250 remote.kick_scheduler_work();
251 }
252 continue;
253 }
254
255 if sched.lifecycle.state() == ThreadState::Running
260 && !committed_here
261 && sched.placement.committed_migration_target().is_none()
262 && (sched.placement.queued_cpu() == Some(owner)
263 || sched.placement.on_cpu() == Some(owner))
264 {
265 let needs_affinity_move = !sched.affinity.affinity.contains(owner)
266 || sched.placement.requested_migration().is_some();
267 let affinity_completed =
268 Self::complete_affinity_if_satisfied_locked(&core, &sched);
269 drop(sched);
270 if affinity_completed {
271 core.notify_affinity_waiters();
272 }
273 if needs_affinity_move {
274 self.reconcile_owner_affinity_update(cpu.as_mut(), &core)?;
275 }
276 continue;
277 }
278
279 if sched.lifecycle.state() != ThreadState::Running || !committed_here {
280 return Err(TaskError::InvalidConfiguration);
281 }
282 let needs_affinity_move = !sched.affinity.affinity.contains(owner)
283 || sched.placement.requested_migration().is_some();
284 drop(sched);
285 self.enqueue_owner_thread(
286 cpu.as_mut(),
287 Arc::clone(&core),
288 EnqueueReason::Migrated,
289 )?;
290 if needs_affinity_move {
291 self.reconcile_owner_affinity_update(cpu.as_mut(), &core)?;
292 }
293 continue;
294 }
295 return Err(TaskError::InvalidConfiguration);
296 }
297 Ok(OwnerControlDrain { drained, pending })
298 }
299
300 pub(in crate::sched::system::task_system) fn drain_owner_work(
308 &self,
309 mut cpu: Pin<&mut CpuLocal>,
310 ) -> Result<(), TaskError> {
311 let policy_pending = cpu.remote().owner_control_inbox().has_pending();
312 let _drain = policy_pending
313 .then(|| self.drain_owner_control_inner(cpu.as_mut()))
314 .transpose()?;
315
316 Ok(())
317 }
318}