use super::*;
impl TaskSystem {
pub fn request_idle_pull(&self, mut cpu: Pin<&mut CpuLocal>) -> Result<bool, TaskError> {
self.ensure_owner_cpu_context(&cpu)?;
if task_runtime::in_hard_irq() {
return Ok(false);
}
self.ensure_owner_cpu_online(&cpu)?;
if !self.root_domain.has_idle_pull_source() {
cpu.as_mut().reset_idle_pull_scan();
return Ok(false);
}
if !cpu.idle_pull_eligible() || cpu.has_remote_work() {
cpu.as_mut().reset_idle_pull_scan();
return Ok(false);
}
let target_remote = Arc::clone(cpu.remote());
let reservation = match target_remote.begin_idle_pull() {
IdlePullReservation::Started(reservation) => reservation,
IdlePullReservation::AlreadyPending => return Ok(true),
IdlePullReservation::Busy => return Ok(false),
};
if !cpu.idle_pull_eligible() || cpu.has_remote_work() {
target_remote.cancel_idle_pull(reservation);
cpu.as_mut().reset_idle_pull_scan();
return Ok(false);
}
let target = cpu.owner();
let source = self
.root_domain
.find_idle_pull_source(target, cpu.idle_pull_visited());
let Some((source, class)) = source else {
target_remote.cancel_idle_pull(reservation);
cpu.as_mut().reset_idle_pull_scan();
return Ok(false);
};
cpu.as_mut().mark_idle_pull_source(source);
let Some(source_local) = self.cpu_remote(source) else {
target_remote.cancel_idle_pull(reservation);
cpu.request_scheduler_work();
return Ok(true);
};
let message = InboxMessage::balance_request(source, target, reservation, class);
let result = source_local.publish_owner_control(cpu.balance_request_node(), message);
match result {
PublishResult::Published => Ok(true),
PublishResult::AlreadyPending => {
target_remote.cancel_idle_pull(reservation);
cpu.request_scheduler_work();
Ok(true)
}
PublishResult::WrongKind => {
target_remote.cancel_idle_pull(reservation);
cpu.request_scheduler_work();
Ok(true)
}
}
}
pub(in crate::sched::system::task_system) fn request_fair_nohz_idle_pulls(&self) -> bool {
let mut requested = false;
for (index, target_remote) in self.cpu_remotes.iter().enumerate() {
let target = CpuId::new(index as u32);
if !self.root_domain.fair_nohz_idle_target(target)
|| !target_remote.accepts_placement()
|| !target_remote.is_scheduler_ready()
{
continue;
}
requested |= self.request_fair_nohz_idle_pull(target, target_remote);
}
requested
}
pub(super) fn request_fair_nohz_idle_pull(
&self,
target: CpuId,
target_remote: &CpuRemote,
) -> bool {
let reservation = match target_remote.begin_idle_pull() {
IdlePullReservation::Started(reservation) => reservation,
IdlePullReservation::AlreadyPending => return true,
IdlePullReservation::Busy => return false,
};
if !self.root_domain.fair_nohz_idle_target(target)
|| !target_remote.accepts_placement()
|| !target_remote.is_scheduler_ready()
{
target_remote.cancel_idle_pull(reservation);
return false;
}
let Some(source) = self
.root_domain
.find_unvisited_fair_idle_pull_source(target)
else {
target_remote.cancel_idle_pull(reservation);
return false;
};
let Some(source_remote) = self.cpu_remote(source) else {
target_remote.cancel_idle_pull(reservation);
return false;
};
let message =
InboxMessage::balance_request(source, target, reservation, SchedulingClass::Fair);
match source_remote.publish_owner_control(target_remote.balance_request_node(), message) {
PublishResult::Published => true,
PublishResult::AlreadyPending | PublishResult::WrongKind => {
target_remote.cancel_idle_pull(reservation);
false
}
}
}
pub fn push_rt_deadline(&self, cpu: Pin<&mut CpuLocal>) -> Result<Option<ThreadId>, TaskError> {
self.ensure_owner_cpu_context(&cpu)?;
if task_runtime::in_hard_irq() {
return Ok(None);
}
self.ensure_owner_cpu_online(&cpu)?;
self.push_rt_deadline_from_root_domain(cpu, None)
}
pub(in crate::sched::system::task_system) fn push_rt_deadline_from_root_domain(
&self,
mut cpu: Pin<&mut CpuLocal>,
class: Option<SchedulingClass>,
) -> Result<Option<ThreadId>, TaskError> {
if !class.map_or_else(
|| self.root_domain.cpu_has_rt_deadline_overload(cpu.owner()),
|class| self.root_domain.cpu_has_overload(cpu.owner(), class),
) {
return Ok(None);
}
let Some(selection) =
self.select_rt_deadline_balance_transfer(cpu.as_ref().get_ref(), class)
else {
return Ok(None);
};
let target = selection.target();
let outcome = self.commit_owner_balance_transfer(cpu.as_mut(), selection)?;
if outcome == BalanceTransferOutcome::Retry
&& let Some(target_remote) = self.cpu_remote(target)
{
target_remote.kick_scheduler_work();
}
Ok(outcome.migrated())
}
}