mirror of
https://github.com/asterinas/asterinas.git
synced 2025-06-20 04:56:32 +00:00
213 lines
6.2 KiB
Rust
213 lines
6.2 KiB
Rust
// SPDX-License-Identifier: MPL-2.0
|
|
|
|
use core::mem;
|
|
|
|
use super::{
|
|
sched_get_priority_max::{rt_to_static, static_to_rt, SCHED_PRIORITY_RANGE},
|
|
SyscallReturn,
|
|
};
|
|
use crate::{
|
|
prelude::*,
|
|
process::posix_thread::thread_table,
|
|
sched::{Nice, RealTimePolicy, SchedAttr, SchedPolicy},
|
|
thread::Tid,
|
|
};
|
|
|
|
pub(super) const SCHED_NORMAL: u32 = 0;
|
|
pub(super) const SCHED_FIFO: u32 = 1;
|
|
pub(super) const SCHED_RR: u32 = 2;
|
|
// pub(super) const SCHED_BATCH: u32 = 3; // not supported (never).
|
|
// SCHED_ISO: reserved but not implemented yet on Linux.
|
|
pub(super) const SCHED_IDLE: u32 = 5;
|
|
// pub(super) const SCHED_DEADLINE: u32 = 6; // not supported yet.
|
|
// pub(super) const SCHED_EXT: u32 = 7; // not supported (never).
|
|
|
|
#[derive(Default, Debug, Pod, Clone, Copy)]
|
|
#[repr(C)]
|
|
pub(super) struct LinuxSchedAttr {
|
|
// Size of this structure
|
|
pub(super) size: u32,
|
|
|
|
// Policy (SCHED_*)
|
|
pub(super) sched_policy: u32,
|
|
// Flags
|
|
pub(super) sched_flags: u64,
|
|
|
|
// Nice value (SCHED_NORMAL, SCHED_BATCH)
|
|
pub(super) sched_nice: i32,
|
|
|
|
// Static priority (SCHED_FIFO, SCHED_RR)
|
|
pub(super) sched_priority: u32,
|
|
|
|
// For SCHED_DEADLINE
|
|
pub(super) sched_runtime: u64,
|
|
pub(super) sched_deadline: u64,
|
|
pub(super) sched_period: u64,
|
|
|
|
// Utilization hints
|
|
pub(super) sched_util_min: u32,
|
|
pub(super) sched_util_max: u32,
|
|
}
|
|
|
|
impl TryFrom<SchedPolicy> for LinuxSchedAttr {
|
|
type Error = Error;
|
|
|
|
fn try_from(value: SchedPolicy) -> Result<Self> {
|
|
Ok(match value {
|
|
SchedPolicy::Stop => LinuxSchedAttr {
|
|
sched_policy: SCHED_FIFO,
|
|
sched_priority: 99, // Linux uses 99 as the default priority for STOP tasks.
|
|
..Default::default()
|
|
},
|
|
|
|
SchedPolicy::RealTime { rt_prio, rt_policy } => LinuxSchedAttr {
|
|
sched_policy: match rt_policy {
|
|
RealTimePolicy::Fifo => SCHED_FIFO,
|
|
RealTimePolicy::RoundRobin { .. } => SCHED_RR,
|
|
},
|
|
sched_priority: rt_to_static(rt_prio),
|
|
..Default::default()
|
|
},
|
|
|
|
SchedPolicy::Fair(nice) => LinuxSchedAttr {
|
|
sched_policy: SCHED_NORMAL,
|
|
sched_nice: nice.value().get().into(),
|
|
..Default::default()
|
|
},
|
|
|
|
SchedPolicy::Idle => LinuxSchedAttr {
|
|
sched_policy: SCHED_IDLE,
|
|
..Default::default()
|
|
},
|
|
})
|
|
}
|
|
}
|
|
|
|
impl TryFrom<LinuxSchedAttr> for SchedPolicy {
|
|
type Error = Error;
|
|
|
|
fn try_from(value: LinuxSchedAttr) -> Result<Self> {
|
|
Ok(match value.sched_policy {
|
|
SCHED_FIFO | SCHED_RR => SchedPolicy::RealTime {
|
|
rt_prio: static_to_rt(value.sched_priority)?,
|
|
rt_policy: match value.sched_policy {
|
|
SCHED_FIFO => RealTimePolicy::Fifo,
|
|
SCHED_RR => RealTimePolicy::RoundRobin {
|
|
base_slice_factor: None,
|
|
},
|
|
_ => unreachable!(),
|
|
},
|
|
},
|
|
|
|
_ if value.sched_priority != 0 => {
|
|
return Err(Error::with_message(
|
|
Errno::EINVAL,
|
|
"Invalid scheduling priority",
|
|
))
|
|
}
|
|
|
|
SCHED_NORMAL => SchedPolicy::Fair(Nice::new(
|
|
i8::try_from(value.sched_nice)?
|
|
.try_into()
|
|
.map_err(|msg| Error::with_message(Errno::EINVAL, msg))?,
|
|
)),
|
|
|
|
SCHED_IDLE => SchedPolicy::Idle,
|
|
|
|
_ => {
|
|
return Err(Error::with_message(
|
|
Errno::EINVAL,
|
|
"Invalid scheduling policy",
|
|
))
|
|
}
|
|
})
|
|
}
|
|
}
|
|
|
|
pub(super) fn read_linux_sched_attr_from_user(
|
|
addr: Vaddr,
|
|
ctx: &Context,
|
|
) -> Result<LinuxSchedAttr> {
|
|
let type_size = mem::size_of::<LinuxSchedAttr>();
|
|
|
|
let space = ctx.user_space();
|
|
|
|
let mut attr = LinuxSchedAttr::default();
|
|
|
|
space.read_bytes(
|
|
addr,
|
|
&mut VmWriter::from(&mut attr.as_bytes_mut()[..mem::size_of::<u32>()]),
|
|
)?;
|
|
|
|
let size = type_size.min(attr.size as usize);
|
|
space.read_bytes(addr, &mut VmWriter::from(&mut attr.as_bytes_mut()[..size]))?;
|
|
|
|
if let Some(additional_size) = attr.size.checked_sub(type_size as u32) {
|
|
let mut buf = vec![0; additional_size as usize];
|
|
space.read_bytes(addr + type_size, &mut VmWriter::from(&mut *buf))?;
|
|
|
|
if buf.iter().any(|&b| b != 0) {
|
|
return Err(Error::with_message(Errno::E2BIG, "too big sched_attr"));
|
|
}
|
|
}
|
|
|
|
Ok(attr)
|
|
}
|
|
|
|
pub(super) fn write_linux_sched_attr_to_user(
|
|
mut attr: LinuxSchedAttr,
|
|
addr: Vaddr,
|
|
user_size: u32,
|
|
ctx: &Context,
|
|
) -> Result<()> {
|
|
let space = ctx.user_space();
|
|
|
|
attr.size = (mem::size_of::<LinuxSchedAttr>() as u32).min(user_size);
|
|
|
|
let range = SCHED_PRIORITY_RANGE
|
|
.get(attr.sched_policy as usize)
|
|
.ok_or_else(|| Error::with_message(Errno::EINVAL, "invalid scheduling policy"))?;
|
|
attr.sched_util_min = *range.start();
|
|
attr.sched_util_max = *range.end();
|
|
|
|
space.write_bytes(
|
|
addr,
|
|
&mut VmReader::from(&attr.as_bytes()[..attr.size as usize]),
|
|
)?;
|
|
Ok(())
|
|
}
|
|
|
|
pub(super) fn access_sched_attr_with<T>(
|
|
tid: Tid,
|
|
ctx: &Context,
|
|
f: impl FnOnce(&SchedAttr) -> Result<T>,
|
|
) -> Result<T> {
|
|
match tid {
|
|
0 => f(ctx.thread.sched_attr()),
|
|
_ if tid > (i32::MAX as u32) => Err(Error::with_message(Errno::EINVAL, "invalid tid")),
|
|
_ => f(thread_table::get_thread(tid)
|
|
.ok_or_else(|| Error::with_message(Errno::ESRCH, "thread does not exist"))?
|
|
.sched_attr()),
|
|
}
|
|
}
|
|
|
|
pub fn sys_sched_getattr(
|
|
tid: Tid,
|
|
addr: Vaddr,
|
|
user_size: u32,
|
|
flags: u32,
|
|
ctx: &Context,
|
|
) -> Result<SyscallReturn> {
|
|
if flags != 0 {
|
|
// TODO: support flags soch as `RESET_ON_FORK`.
|
|
return Err(Error::with_message(Errno::EINVAL, "unsupported flags"));
|
|
}
|
|
|
|
let policy = access_sched_attr_with(tid, ctx, |attr| Ok(attr.policy()))?;
|
|
let attr: LinuxSchedAttr = policy.try_into()?;
|
|
write_linux_sched_attr_to_user(attr, addr, user_size, ctx)
|
|
.map_err(|_| Error::new(Errno::EINVAL))?;
|
|
|
|
Ok(SyscallReturn::Return(0))
|
|
}
|