use std::time::Duration;
use crate::{
AppliedPriority, BrokerError, Error, FallbackReason, Grant, Mechanism, MechanismPolicy, Result,
ThreadPriority,
};
use super::affinity::{current_nice, set_thread_realtime_rr};
use super::scheduling_policy::{RT_PRIORITY, level_for_nice, nice_for};
fn rt_applied(mechanism: Mechanism) -> AppliedPriority {
AppliedPriority::new(
ThreadPriority::TimeCritical,
ThreadPriority::TimeCritical,
Grant::Realtime,
mechanism,
)
}
fn kept_timeshare(reason: FallbackReason, broker_error: Option<BrokerError>) -> AppliedPriority {
let current = current_nice().unwrap_or(nice_for(ThreadPriority::Normal));
let mut applied = AppliedPriority::new(
ThreadPriority::TimeCritical,
level_for_nice(current),
Grant::Direct,
Mechanism {
policy: MechanismPolicy::Nice,
value: current as i8,
},
)
.with_reason(reason);
if let Some(broker_error) = broker_error {
applied = applied.with_broker_error(broker_error);
}
applied
}
pub(crate) fn promote(budget: Duration) -> Result<AppliedPriority> {
if budget.is_zero() {
return Err(Error::InvalidParameter(
"real-time budget must be greater than zero".to_string(),
));
}
match set_thread_realtime_rr(RT_PRIORITY) {
Ok(()) => {
return Ok(rt_applied(Mechanism {
policy: MechanismPolicy::SchedRr,
value: RT_PRIORITY as i8,
}));
}
Err(Error::PermissionDenied(_)) => {}
Err(e) => return Err(e),
};
#[cfg(feature = "rtkit")]
{
use super::rtkit::Broker;
let mut reason = FallbackReason::NoBroker;
let mut broker_error = None;
let tid = super::affinity::current_tid()? as u64;
let pid = unsafe { libc::getpid() } as u64;
match Broker::portal() {
Ok(mut portal) => {
let prio = request_priority(portal.max_realtime_priority());
let rttime_max = portal.rttime_usec_max();
if set_rttime_rlimit(budget, rttime_max).is_ok() {
match portal.make_thread_realtime_with_pid(pid, tid, prio) {
Ok(()) => {
return Ok(rt_applied(Mechanism {
policy: MechanismPolicy::SchedRr,
value: prio as i8,
}));
}
Err(e) => {
let (r, _) = super::rtkit::classify(&e);
reason = r;
broker_error = super::rtkit::broker_error(&e);
}
}
}
}
Err(r) => {
reason = r;
}
}
match Broker::rtkit() {
Ok(mut rtkit) => {
let prio = request_priority(rtkit.max_realtime_priority());
let rttime_max = rtkit.rttime_usec_max();
if set_rttime_rlimit(budget, rttime_max).is_ok() {
match rtkit.make_thread_realtime(tid, prio) {
Ok(()) => {
return Ok(rt_applied(Mechanism {
policy: MechanismPolicy::SchedRr,
value: prio as i8,
}));
}
Err(e) => {
let (r, _) = super::rtkit::classify(&e);
reason = r;
broker_error = super::rtkit::broker_error(&e);
}
}
}
}
Err(r) => {
reason = r;
}
}
Ok(kept_timeshare(reason, broker_error))
}
#[cfg(not(feature = "rtkit"))]
{
let _ = budget;
Ok(kept_timeshare(FallbackReason::NoBroker, None))
}
}
#[cfg(feature = "rtkit")]
fn request_priority(max_realtime_priority: i64) -> u32 {
(RT_PRIORITY as i64).min(max_realtime_priority).clamp(1, 99) as u32
}
#[cfg(feature = "rtkit")]
fn set_rttime_rlimit(budget: Duration, rttime_usec_max: i64) -> Result<()> {
let daemon_max = rttime_usec_max.max(1) as libc::rlim_t;
let mut current = libc::rlimit {
rlim_cur: 0,
rlim_max: 0,
};
if unsafe { libc::getrlimit(libc::RLIMIT_RTTIME, &mut current) } != 0 {
return Err(Error::SystemCall(format!(
"getrlimit(RLIMIT_RTTIME) failed: {}",
std::io::Error::last_os_error()
)));
}
let hard = current.rlim_max.min(daemon_max);
let soft = (budget.as_micros() as libc::rlim_t).clamp(1, hard);
let wanted = libc::rlimit {
rlim_cur: soft,
rlim_max: hard,
};
if unsafe { libc::setrlimit(libc::RLIMIT_RTTIME, &wanted) } != 0 {
return Err(Error::SystemCall(format!(
"setrlimit(RLIMIT_RTTIME, soft={}, hard={}) failed: {}",
soft,
hard,
std::io::Error::last_os_error()
)));
}
Ok(())
}
pub(crate) fn demote() -> Result<()> {
let tid = super::affinity::current_tid()?;
let current = unsafe { libc::sched_getscheduler(tid as libc::pid_t) };
let reset_on_fork = if current >= 0 {
current & libc::SCHED_RESET_ON_FORK
} else {
0
};
let param: libc::sched_param = unsafe { std::mem::zeroed() };
let res = unsafe {
libc::sched_setscheduler(
tid as libc::pid_t,
libc::SCHED_OTHER | reset_on_fork,
¶m,
)
};
if res != 0 {
let err = std::io::Error::last_os_error();
return Err(Error::SystemCall(format!(
"sched_setscheduler(SCHED_OTHER) failed: {}",
err
)));
}
Ok(())
}