Add support for setpriority and getpriority

This commit is contained in:
LI Qing 2024-01-23 16:41:12 +09:00 committed by Tate, Hongliang Tian
parent 26dff7d054
commit 437ab804f3
14 changed files with 396 additions and 18 deletions

16
Cargo.lock generated
View File

@ -199,8 +199,11 @@ dependencies = [
"aster-time",
"aster-util",
"aster-virtio",
"atomic",
"bitflags 1.3.2",
"bitvec",
"bytemuck",
"bytemuck_derive",
"controlled",
"core2",
"cpio-decoder",
@ -308,6 +311,15 @@ dependencies = [
"x86_64",
]
[[package]]
name = "atomic"
version = "0.6.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8d818003e740b63afc82337e3160717f4f63078720a810b7b903e70a5d1d2994"
dependencies = [
"bytemuck",
]
[[package]]
name = "atomic-polyfill"
version = "0.1.11"
@ -364,9 +376,9 @@ dependencies = [
[[package]]
name = "bytemuck"
version = "1.14.0"
version = "1.14.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "374d28ec25809ee0e23827c2ab573d729e293f281dfe393500e7ad618baa61c6"
checksum = "a2ef034f05691a48569bd920a96c81b9d91bbad1ab5ac7c4616c1f6ef36cb79f"
dependencies = [
"bytemuck_derive",
]

View File

@ -65,6 +65,9 @@ bitvec = { version = "1.0", default-features = false, features = ["alloc"] }
static_assertions = "1.1.0"
inherit-methods-macro = { git = "https://github.com/asterinas/inherit-methods-macro", rev = "98f7e3e" }
getset = "0.1.2"
atomic = "0.6"
bytemuck = "1.14.3"
bytemuck_derive = "1.5.0"
[dependencies.lazy_static]
version = "1.0"

View File

@ -112,8 +112,7 @@ impl DirOps for RootDirOps {
let mut cached_children = this.cached_children().write();
cached_children.put_entry_if_not_found("self", || SelfSymOps::new_inode(this_ptr.clone()));
let processes = process_table::get_all_processes();
for process in processes {
for process in process_table::process_table().iter() {
let pid = process.pid().to_string();
cached_children.put_entry_if_not_found(&pid, || {
PidDirOps::new_inode(process.clone(), this_ptr.clone())

View File

@ -1,5 +1,7 @@
// SPDX-License-Identifier: MPL-2.0
use core::sync::atomic::Ordering;
use aster_frame::{cpu::UserContext, user::UserSpace, vm::VmIo};
use aster_rights::Full;
@ -260,6 +262,9 @@ fn clone_child_process(parent_context: UserContext, clone_args: CloneArgs) -> Re
*sigmask
};
// inherit parent's nice value
let child_nice = current.nice().load(Ordering::Relaxed);
let child_tid = allocate_tid();
let child = {
@ -286,7 +291,8 @@ fn clone_child_process(parent_context: UserContext, clone_args: CloneArgs) -> Re
.file_table(child_file_table)
.fs(child_fs)
.umask(child_umask)
.sig_dispositions(child_sig_dispositions);
.sig_dispositions(child_sig_dispositions)
.nice(child_nice);
process_builder.build()?
};

View File

@ -89,13 +89,12 @@ pub fn tgkill(tid: Tid, tgid: Pid, signal: Option<UserSignal>) -> Result<()> {
/// if it is authorized to send the signal to the target group.
pub fn kill_all(signal: Option<UserSignal>) -> Result<()> {
let current = current!();
let processes = process_table::get_all_processes();
for process in processes {
if Arc::ptr_eq(&current, &process) || process.is_init_process() {
for process in process_table::process_table().iter() {
if Arc::ptr_eq(&current, process) || process.is_init_process() {
continue;
}
kill_process(&process, signal)?;
kill_process(process, signal)?;
}
Ok(())

View File

@ -246,7 +246,7 @@ impl PosixThread {
}
/// Gets the read-only credentials of the thread.
pub(in crate::process) fn credentials(&self) -> Credentials<ReadOp> {
pub fn credentials(&self) -> Credentials<ReadOp> {
self.credentials.dup().restrict()
}

View File

@ -11,6 +11,7 @@ use crate::{
signal::sig_disposition::SigDispositions,
Credentials,
},
sched::nice::Nice,
thread::Thread,
};
@ -31,6 +32,7 @@ pub struct ProcessBuilder<'a> {
resource_limits: Option<ResourceLimits>,
sig_dispositions: Option<Arc<Mutex<SigDispositions>>>,
credentials: Option<Credentials>,
nice: Option<Nice>,
}
impl<'a> ProcessBuilder<'a> {
@ -49,6 +51,7 @@ impl<'a> ProcessBuilder<'a> {
resource_limits: None,
sig_dispositions: None,
credentials: None,
nice: None,
}
}
@ -102,6 +105,11 @@ impl<'a> ProcessBuilder<'a> {
self
}
pub fn nice(&mut self, nice: Nice) -> &mut Self {
self.nice = Some(nice);
self
}
fn check_build(&self) -> Result<()> {
if self.main_thread_builder.is_some() {
debug_assert!(self.parent.upgrade().is_some());
@ -136,6 +144,7 @@ impl<'a> ProcessBuilder<'a> {
resource_limits,
sig_dispositions,
credentials,
nice,
} = self;
let process_vm = process_vm.or_else(|| Some(ProcessVm::alloc())).unwrap();
@ -160,6 +169,8 @@ impl<'a> ProcessBuilder<'a> {
.or_else(|| Some(Arc::new(Mutex::new(SigDispositions::new()))))
.unwrap();
let nice = nice.or_else(|| Some(Nice::default())).unwrap();
let process = {
let threads = Vec::new();
Arc::new(Process::new(
@ -173,6 +184,7 @@ impl<'a> ProcessBuilder<'a> {
umask,
sig_dispositions,
resource_limits,
nice,
))
};

View File

@ -16,6 +16,7 @@ use crate::{
device::tty::open_ntty_as_controlling_terminal,
fs::{file_table::FileTable, fs_resolver::FsResolver, utils::FileCreationMask},
prelude::*,
sched::nice::Nice,
thread::{allocate_tid, Thread},
vm::vmar::Vmar,
};
@ -27,6 +28,7 @@ mod session;
mod terminal;
use aster_rights::Full;
use atomic::Atomic;
pub use builder::ProcessBuilder;
pub use job_control::JobControl;
pub use process_group::ProcessGroup;
@ -72,6 +74,10 @@ pub struct Process {
umask: Arc<RwLock<FileCreationMask>>,
/// resource limits
resource_limits: Mutex<ResourceLimits>,
/// Scheduling priority nice value
/// According to POSIX.1, the nice value is a per-process attribute,
/// the threads in a process should share a nice value.
nice: Atomic<Nice>,
// Signal
/// Sig dispositions
@ -91,6 +97,7 @@ impl Process {
umask: Arc<RwLock<FileCreationMask>>,
sig_dispositions: Arc<Mutex<SigDispositions>>,
resource_limits: ResourceLimits,
nice: Nice,
) -> Self {
let children_pauser = {
// SIGCHID does not interrupt pauser. Child process will
@ -114,6 +121,7 @@ impl Process {
umask,
sig_dispositions,
resource_limits: Mutex::new(resource_limits),
nice: Atomic::new(nice),
}
}
@ -206,7 +214,11 @@ impl Process {
&self.resource_limits
}
fn main_thread(&self) -> Option<Arc<Thread>> {
pub fn nice(&self) -> &Atomic<Nice> {
&self.nice
}
pub fn main_thread(&self) -> Option<Arc<Thread>> {
self.threads
.lock()
.iter()
@ -602,6 +614,7 @@ mod test {
Arc::new(RwLock::new(FileCreationMask::default())),
Arc::new(Mutex::new(SigDispositions::default())),
ResourceLimits::default(),
Nice::default(),
))
}

View File

@ -1,5 +1,7 @@
// SPDX-License-Identifier: MPL-2.0
use alloc::collections::btree_map::Values;
use super::{Pgid, Pid, Process, Session};
use crate::{prelude::*, process::signal::signals::Signal};
@ -68,6 +70,13 @@ impl ProcessGroup {
self.pgid
}
/// Acquires a lock on the process group.
pub fn lock(&self) -> ProcessGroupGuard {
ProcessGroupGuard {
inner: self.inner.lock(),
}
}
/// Broadcasts signal to all processes in the group.
///
/// This method should only be used to broadcast fault signal and kernel signal.
@ -89,3 +98,32 @@ impl ProcessGroup {
self.inner.lock().session.upgrade()
}
}
/// A scoped lock for a process group.
///
/// It provides some public methods to prevent the exposure of the inner type.
pub struct ProcessGroupGuard<'a> {
inner: MutexGuard<'a, Inner>,
}
impl<'a> ProcessGroupGuard<'a> {
/// Returns an iterator over the processes in the group.
pub fn iter(&self) -> ProcessGroupIter {
ProcessGroupIter {
inner: self.inner.processes.values(),
}
}
}
/// An iterator over the processes of the process group.
pub struct ProcessGroupIter<'a> {
inner: Values<'a, Pid, Arc<Process>>,
}
impl<'a> Iterator for ProcessGroupIter<'a> {
type Item = &'a Arc<Process>;
fn next(&mut self) -> Option<Self::Item> {
self.inner.next()
}
}

View File

@ -4,6 +4,8 @@
//! This table can be used to get process with pid.
//! TODO: progress group, thread all need similar mapping
use alloc::collections::btree_map::Values;
use super::{Pgid, Pid, Process, ProcessGroup, Session, Sid};
use crate::{
events::{Events, Observer, Subject},
@ -26,13 +28,40 @@ pub(super) fn process_table_mut() -> MutexGuard<'static, BTreeMap<Pid, Arc<Proce
PROCESS_TABLE.lock()
}
/// Gets all processes
pub fn get_all_processes() -> Vec<Arc<Process>> {
PROCESS_TABLE
.lock()
.iter()
.map(|(_, process)| process.clone())
.collect()
/// Acquires a lock on the process table and returns a `ProcessTable`.
pub fn process_table() -> ProcessTable<'static> {
ProcessTable {
inner: PROCESS_TABLE.lock(),
}
}
/// A wrapper for the mutex-protected process table.
///
/// It provides the `iter` method to iterator over the processes in the table.
pub struct ProcessTable<'a> {
inner: MutexGuard<'a, BTreeMap<Pid, Arc<Process>>>,
}
impl<'a> ProcessTable<'a> {
/// Returns an iterator over the processes in the table.
pub fn iter(&self) -> ProcessTableIter {
ProcessTableIter {
inner: self.inner.values(),
}
}
}
/// An iterator over the processes of the process table.
pub struct ProcessTableIter<'a> {
inner: Values<'a, Pid, Arc<Process>>,
}
impl<'a> Iterator for ProcessTableIter<'a> {
type Item = &'a Arc<Process>;
fn next(&mut self) -> Option<Self::Item> {
self.inner.next()
}
}
// ************ Process Group *************

View File

@ -1,5 +1,6 @@
// SPDX-License-Identifier: MPL-2.0
pub mod nice;
mod priority_scheduler;
// There may be multiple scheduling policies in the system,

View File

@ -0,0 +1,111 @@
// SPDX-License-Identifier: MPL-2.0
use bytemuck_derive::NoUninit;
use crate::prelude::*;
/// The process scheduling nice value.
///
/// The nice value is an attribute that can be used to influence the
/// CPU scheduler to favor or disfavor a process in scheduling decisions.
///
/// It is a value in the range -20 to 19, with -20 being the highest priority
/// and 19 being the lowest priority. The smaller values give a process a higher
/// scheduling priority.
#[repr(transparent)]
#[derive(Copy, Clone, Debug, PartialEq, Eq, PartialOrd, Ord, NoUninit)]
pub struct Nice {
value: i8,
}
impl Nice {
/// The minimum nice, whose value is -20.
pub const MIN: Self = Self { value: -20 };
/// The maximum nice, whose value is 19.
pub const MAX: Self = Self { value: 19 };
/// Creates a new `Nice` from the raw value.
///
/// Values given beyond the permissible range are automatically adjusted
/// to the nearest boundary value.
pub fn new(raw: i8) -> Self {
if raw > Self::MAX.to_raw() {
Self::MAX
} else if raw < Self::MIN.to_raw() {
Self::MIN
} else {
Self { value: raw }
}
}
/// Converts to the raw value.
pub fn to_raw(self) -> i8 {
self.value
}
}
#[allow(clippy::derivable_impls)]
impl Default for Nice {
fn default() -> Self {
Self {
// The default nice value is 0
value: 0,
}
}
}
impl From<Priority> for Nice {
fn from(priority: Priority) -> Self {
Self {
value: 20 - priority.to_raw() as i8,
}
}
}
/// The process scheduling priority value.
///
/// It is a value in the range 1 (corresponding to a nice value of 19)
/// to 40 (corresponding to a nice value of -20), with 1 being the lowest priority
/// and 40 being the highest priority. The greater values give a process a higher
/// scheduling priority.
#[repr(transparent)]
#[derive(Copy, Clone, Debug, PartialEq, Eq, PartialOrd, Ord, NoUninit)]
pub struct Priority {
value: u8,
}
impl Priority {
/// The minimum priority, whose value is 1.
pub const MIN: Self = Self { value: 1 };
/// The maximum priority, whose value is 40.
pub const MAX: Self = Self { value: 40 };
/// Creates a new `Priority` from the raw value.
///
/// Values given beyond the permissible range are automatically adjusted
/// to the nearest boundary value.
pub fn new(raw: u8) -> Self {
if raw > Self::MAX.to_raw() {
Self::MAX
} else if raw < Self::MIN.to_raw() {
Self::MIN
} else {
Self { value: raw }
}
}
/// Converts to the raw value.
pub fn to_raw(self) -> u8 {
self.value
}
}
impl From<Nice> for Priority {
fn from(nice: Nice) -> Self {
Self {
value: (20 - nice.to_raw()) as u8,
}
}
}

View File

@ -72,6 +72,7 @@ use crate::{
rt_sigreturn::sys_rt_sigreturn,
sched_yield::sys_sched_yield,
select::sys_select,
set_get_priority::{sys_get_priority, sys_set_priority},
set_robust_list::sys_set_robust_list,
set_tid_address::sys_set_tid_address,
setpgid::sys_setpgid,
@ -163,6 +164,7 @@ mod rt_sigreturn;
mod sched_yield;
mod select;
mod sendto;
mod set_get_priority;
mod set_robust_list;
mod set_tid_address;
mod setfsgid;
@ -321,6 +323,8 @@ define_syscall_nums!(
SYS_SIGALTSTACK = 131,
SYS_STATFS = 137,
SYS_FSTATFS = 138,
SYS_GET_PRIORITY = 140,
SYS_SET_PRIORITY = 141,
SYS_PRCTL = 157,
SYS_ARCH_PRCTL = 158,
SYS_SYNC = 162,
@ -505,6 +509,8 @@ pub fn syscall_dispatch(
SYS_SIGALTSTACK => syscall_handler!(2, sys_sigaltstack, args),
SYS_STATFS => syscall_handler!(2, sys_statfs, args),
SYS_FSTATFS => syscall_handler!(2, sys_fstatfs, args),
SYS_GET_PRIORITY => syscall_handler!(2, sys_get_priority, args),
SYS_SET_PRIORITY => syscall_handler!(3, sys_set_priority, args),
SYS_PRCTL => syscall_handler!(5, sys_prctl, args),
SYS_ARCH_PRCTL => syscall_handler!(2, sys_arch_prctl, args, context),
SYS_SYNC => syscall_handler!(0, sys_sync),

View File

@ -0,0 +1,149 @@
// SPDX-License-Identifier: MPL-2.0
use core::sync::atomic::Ordering;
use super::{SyscallReturn, SYS_GET_PRIORITY, SYS_SET_PRIORITY};
use crate::{
log_syscall_entry,
prelude::*,
process::{credentials, posix_thread::PosixThreadExt, process_table, Pgid, Pid, Process, Uid},
sched::nice::Nice,
};
pub fn sys_set_priority(which: i32, who: u32, prio: i32) -> Result<SyscallReturn> {
log_syscall_entry!(SYS_SET_PRIORITY);
let prio_target = PriorityTarget::new(which, who)?;
let new_nice = {
let norm_prio = if prio > i8::MAX as i32 {
i8::MAX
} else if prio < i8::MIN as i32 {
i8::MIN
} else {
prio as i8
};
Nice::new(norm_prio)
};
debug!(
"set_priority prio_target: {:?}, new_nice: {:?}",
prio_target, new_nice
);
let processes = get_processes(prio_target)?;
for process in processes.iter() {
process.nice().store(new_nice, Ordering::Relaxed);
}
Ok(SyscallReturn::Return(0))
}
pub fn sys_get_priority(which: i32, who: u32) -> Result<SyscallReturn> {
log_syscall_entry!(SYS_GET_PRIORITY);
let prio_target = PriorityTarget::new(which, who)?;
debug!("get_priority prio_target: {:?}", prio_target);
let processes = get_processes(prio_target)?;
let highest_prio = {
let mut nice = Nice::MAX;
for process in processes.iter() {
let proc_nice = process.nice().load(Ordering::Relaxed);
// Returns the highest priority enjoyed by the processes
if proc_nice < nice {
nice = proc_nice;
}
}
// The system call returns nice values translated to the range 40 to 1,
// since a negative return value would be interpreted as an error.
20 - nice.to_raw()
};
Ok(SyscallReturn::Return(highest_prio as _))
}
fn get_processes(prio_target: PriorityTarget) -> Result<Vec<Arc<Process>>> {
Ok(match prio_target {
PriorityTarget::Process(pid) => {
let process = process_table::get_process(&pid).ok_or(Error::new(Errno::ESRCH))?;
vec![process]
}
PriorityTarget::ProcessGroup(pgid) => {
let process_group =
process_table::get_process_group(&pgid).ok_or(Error::new(Errno::ESRCH))?;
let processes: Vec<Arc<Process>> = process_group.lock().iter().cloned().collect();
if processes.is_empty() {
return_errno!(Errno::ESRCH);
}
processes
}
PriorityTarget::User(uid) => {
// Get the processes that are running under the specified user
let processes: Vec<Arc<Process>> = process_table::process_table()
.iter()
.filter(|process| {
let Some(main_thread) = process.main_thread() else {
return false;
};
let Some(posix_thread) = main_thread.as_posix_thread() else {
return false;
};
uid == posix_thread.credentials().ruid()
})
.cloned()
.collect();
if processes.is_empty() {
return_errno!(Errno::ESRCH);
}
processes
}
})
}
#[derive(Debug)]
enum PriorityTarget {
Process(Pid),
ProcessGroup(Pgid),
User(Uid),
}
impl PriorityTarget {
fn new(which: i32, who: u32) -> Result<Self> {
let which = Which::try_from(which)
.map_err(|_| Error::with_message(Errno::EINVAL, "invalid which value"))?;
Ok(match which {
Which::PRIO_PROCESS => {
let pid = if who == 0 {
current!().pid()
} else {
who as Pid
};
Self::Process(pid)
}
Which::PRIO_PGRP => {
let pgid = if who == 0 {
current!().pgid()
} else {
who as Pgid
};
Self::ProcessGroup(pgid)
}
Which::PRIO_USER => {
let uid = if who == 0 {
credentials().ruid()
} else {
Uid::new(who)
};
Self::User(uid)
}
})
}
}
#[allow(non_camel_case_types)]
#[derive(Clone, Debug, TryFromInt)]
#[repr(i32)]
enum Which {
PRIO_PROCESS = 0,
PRIO_PGRP = 1,
PRIO_USER = 2,
}