Add sched_attr system call series

This commit is contained in:
js2xxx
2025-02-11 13:35:24 +00:00
committed by Tate, Hongliang Tian
parent 9e1c939a3f
commit c1dde01fc3
24 changed files with 588 additions and 42 deletions

View File

@ -0,0 +1,212 @@
// 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 = CurrentUserSpace::new(ctx.task);
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 = CurrentUserSpace::new(ctx.task);
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))
}