From 96166dadc2c0d22203a99025ac7685487fcad1f8 Mon Sep 17 00:00:00 2001 From: LI Qing Date: Wed, 8 Dec 2021 15:55:04 +0800 Subject: [PATCH] Add setpriority and getpriority syscall --- src/libos/src/process/do_clone.rs | 2 + src/libos/src/process/do_spawn/mod.rs | 2 + src/libos/src/process/mod.rs | 3 +- src/libos/src/process/process/builder.rs | 8 ++- src/libos/src/process/thread/builder.rs | 11 +++- src/libos/src/process/thread/mod.rs | 7 ++- src/libos/src/sched/do_priority.rs | 71 ++++++++++++++++++++++++ src/libos/src/sched/mod.rs | 3 + src/libos/src/sched/priority.rs | 66 ++++++++++++++++++++++ src/libos/src/sched/syscalls.rs | 18 ++++++ src/libos/src/syscall/mod.rs | 9 ++- test/sched/main.c | 56 +++++++++++++++++++ 12 files changed, 248 insertions(+), 8 deletions(-) create mode 100644 src/libos/src/sched/do_priority.rs create mode 100644 src/libos/src/sched/priority.rs diff --git a/src/libos/src/process/do_clone.rs b/src/libos/src/process/do_clone.rs index c555d83a..442f3735 100644 --- a/src/libos/src/process/do_clone.rs +++ b/src/libos/src/process/do_clone.rs @@ -48,6 +48,7 @@ pub fn do_clone( } }; let files = current.files().clone(); + let nice = current.nice().clone(); let rlimits = current.rlimits().clone(); let fs = current.fs().clone(); let name = current.name().clone(); @@ -60,6 +61,7 @@ pub fn do_clone( .fs(fs) .files(files) .name(name) + .nice(nice) .rlimits(rlimits) .sig_mask(sig_mask); if let Some(ctid) = ctid { diff --git a/src/libos/src/process/do_spawn/mod.rs b/src/libos/src/process/do_spawn/mod.rs index 3a8ef6e9..6119e292 100644 --- a/src/libos/src/process/do_spawn/mod.rs +++ b/src/libos/src/process/do_spawn/mod.rs @@ -257,6 +257,7 @@ fn new_process_common( }; let fs_ref = Arc::new(RwLock::new(current_ref.fs().read().unwrap().clone())); let sched_ref = Arc::new(SgxMutex::new(current_ref.sched().lock().unwrap().clone())); + let nice_ref = Arc::new(RwLock::new(current_ref.nice().read().unwrap().clone())); let rlimit_ref = Arc::new(SgxMutex::new(current_ref.rlimits().lock().unwrap().clone())); let sig_mask = if spawn_attributes.is_some() && spawn_attributes.unwrap().sig_mask.is_some() { @@ -311,6 +312,7 @@ fn new_process_common( .parent(parent) .task(task) .sched(sched_ref) + .nice(nice_ref) .rlimits(rlimit_ref) .fs(fs_ref) .pgrp(pgrp_ref) diff --git a/src/libos/src/process/mod.rs b/src/libos/src/process/mod.rs index 921002bf..bc18279c 100644 --- a/src/libos/src/process/mod.rs +++ b/src/libos/src/process/mod.rs @@ -12,7 +12,7 @@ use crate::fs::{FileRef, FileTable, FsView}; use crate::misc::ResourceLimits; use crate::prelude::*; -use crate::sched::SchedAgent; +use crate::sched::{NiceValue, SchedAgent}; use crate::signal::{SigDispositions, SigQueues}; use crate::vm::ProcessVM; @@ -80,3 +80,4 @@ pub type FsViewRef = Arc>; pub type SchedAgentRef = Arc>; pub type ResourceLimitsRef = Arc>; pub type ProcessGrpRef = Arc; +pub type NiceValueRef = Arc>; diff --git a/src/libos/src/process/process/builder.rs b/src/libos/src/process/process/builder.rs index ec4652dc..6430c0d4 100644 --- a/src/libos/src/process/process/builder.rs +++ b/src/libos/src/process/process/builder.rs @@ -2,8 +2,8 @@ use super::super::table; use super::super::task::Task; use super::super::thread::{ThreadBuilder, ThreadId, ThreadName}; use super::super::{ - FileTableRef, ForcedExitStatus, FsViewRef, ProcessGrpRef, ProcessRef, ProcessVMRef, - ResourceLimitsRef, SchedAgentRef, + FileTableRef, ForcedExitStatus, FsViewRef, NiceValueRef, ProcessGrpRef, ProcessRef, + ProcessVMRef, ResourceLimitsRef, SchedAgentRef, }; use super::{Process, ProcessInner}; use crate::fs::FileMode; @@ -100,6 +100,10 @@ impl ProcessBuilder { self.thread_builder(|tb| tb.sig_mask(sig_mask)) } + pub fn nice(mut self, nice: NiceValueRef) -> Self { + self.thread_builder(|tb| tb.nice(nice)) + } + pub fn rlimits(mut self, rlimits: ResourceLimitsRef) -> Self { self.thread_builder(|tb| tb.rlimits(rlimits)) } diff --git a/src/libos/src/process/thread/builder.rs b/src/libos/src/process/thread/builder.rs index d532144c..b48a1c0e 100644 --- a/src/libos/src/process/thread/builder.rs +++ b/src/libos/src/process/thread/builder.rs @@ -1,7 +1,7 @@ use std::ptr::NonNull; use super::{ - FileTableRef, FsViewRef, ProcessRef, ProcessVM, ProcessVMRef, ResourceLimitsRef, + FileTableRef, FsViewRef, NiceValueRef, ProcessRef, ProcessVM, ProcessVMRef, ResourceLimitsRef, RobustListHead, SchedAgentRef, SigQueues, SigSet, Task, Thread, ThreadId, ThreadInner, ThreadName, ThreadRef, }; @@ -20,6 +20,7 @@ pub struct ThreadBuilder { fs: Option, files: Option, sched: Option, + nice: Option, rlimits: Option, sig_mask: Option, clear_ctid: Option>, @@ -37,6 +38,7 @@ impl ThreadBuilder { fs: None, files: None, sched: None, + nice: None, rlimits: None, sig_mask: None, clear_ctid: None, @@ -85,6 +87,11 @@ impl ThreadBuilder { self } + pub fn nice(mut self, nice: NiceValueRef) -> Self { + self.nice = Some(nice); + self + } + pub fn rlimits(mut self, rlimits: ResourceLimitsRef) -> Self { self.rlimits = Some(rlimits); self @@ -122,6 +129,7 @@ impl ThreadBuilder { let fs = self.fs.unwrap_or_default(); let files = self.files.unwrap_or_default(); let sched = self.sched.unwrap_or_default(); + let nice = self.nice.unwrap_or_default(); let rlimits = self.rlimits.unwrap_or_default(); let name = RwLock::new(self.name.unwrap_or_default()); let sig_mask = RwLock::new(self.sig_mask.unwrap_or_default()); @@ -147,6 +155,7 @@ impl ThreadBuilder { fs, files, sched, + nice, rlimits, name, sig_queues, diff --git a/src/libos/src/process/thread/mod.rs b/src/libos/src/process/thread/mod.rs index 3af9bd41..cc4fb663 100644 --- a/src/libos/src/process/thread/mod.rs +++ b/src/libos/src/process/thread/mod.rs @@ -3,7 +3,7 @@ use std::ptr::NonNull; use super::task::Task; use super::{ - FileTableRef, ForcedExitStatus, FsViewRef, ProcessRef, ProcessVM, ProcessVMRef, + FileTableRef, ForcedExitStatus, FsViewRef, NiceValueRef, ProcessRef, ProcessVM, ProcessVMRef, ResourceLimitsRef, RobustListHead, SchedAgentRef, TermStatus, ThreadRef, }; use crate::events::HostEventFd; @@ -38,6 +38,7 @@ pub struct Thread { fs: FsViewRef, files: FileTableRef, sched: SchedAgentRef, + nice: NiceValueRef, rlimits: ResourceLimitsRef, // Signal sig_queues: RwLock, @@ -156,6 +157,10 @@ impl Thread { &self.fs } + pub fn nice(&self) -> &NiceValueRef { + &self.nice + } + pub fn rlimits(&self) -> &ResourceLimitsRef { &self.rlimits } diff --git a/src/libos/src/sched/do_priority.rs b/src/libos/src/sched/do_priority.rs new file mode 100644 index 00000000..099cd198 --- /dev/null +++ b/src/libos/src/sched/do_priority.rs @@ -0,0 +1,71 @@ +use super::priority::{NiceValue, PrioWhich}; +use crate::prelude::*; +use crate::process::table::{get_all_processes, get_pgrp, get_process}; + +pub fn do_set_priority(which: PrioWhich, who: i32, prio: NiceValue) -> Result<()> { + debug!( + "set_priority: which: {:?}, who: {}, prio: {:?}", + which, who, prio + ); + + let processes = get_processes(which, who)?; + for process in processes.iter() { + let main_thread = process + .main_thread() + .ok_or_else(|| errno!(ESRCH, "invalid pid"))?; + *main_thread.nice().write().unwrap() = prio; + } + Ok(()) +} + +pub fn do_get_priority(which: PrioWhich, who: i32) -> Result { + debug!("get_priority: which: {:?}, who: {}", which, who); + + let processes = get_processes(which, who)?; + let prio = { + let mut prio = NiceValue::max_value(); + for process in processes.iter() { + let main_thread = process + .main_thread() + .ok_or_else(|| errno!(ESRCH, "invalid pid"))?; + let nice_value = main_thread.nice().read().unwrap(); + // Returns the highest priority enjoyed by the processes + if *nice_value < prio { + prio = *nice_value; + } + } + prio + }; + Ok(prio) +} + +// According to POSIX, the nice value is a per-process setting. +// In our implementation, the threads belong to same process share the same nice value. +fn get_processes(which: PrioWhich, who: i32) -> Result> { + Ok(match which { + PrioWhich::PRIO_PROCESS => { + let process = if who == 0 { + current!().process().clone() + } else { + get_process(who as pid_t)? + }; + vec![process] + } + PrioWhich::PRIO_PGRP => { + let pgrp = if who == 0 { + current!().process().pgrp() + } else { + get_pgrp(who as pid_t)? + }; + pgrp.get_all_processes() + } + PrioWhich::PRIO_USER => { + if who == 0 { + get_all_processes() + } else { + warn!("only root user is supported in Occlum"); + return_errno!(ESRCH, "no such user"); + } + } + }) +} diff --git a/src/libos/src/sched/mod.rs b/src/libos/src/sched/mod.rs index 6e6fb49c..d1eb0178 100644 --- a/src/libos/src/sched/mod.rs +++ b/src/libos/src/sched/mod.rs @@ -1,11 +1,14 @@ /// CPU scheduling for threads. mod cpu_set; mod do_getcpu; +mod do_priority; mod do_sched_affinity; mod do_sched_yield; +mod priority; mod sched_agent; mod syscalls; pub use cpu_set::NCORES; +pub use priority::NiceValue; pub use sched_agent::SchedAgent; pub use syscalls::*; diff --git a/src/libos/src/sched/priority.rs b/src/libos/src/sched/priority.rs new file mode 100644 index 00000000..4305d818 --- /dev/null +++ b/src/libos/src/sched/priority.rs @@ -0,0 +1,66 @@ +use crate::prelude::*; +use core::convert::TryFrom; + +#[allow(non_camel_case_types)] +#[derive(Clone, Debug)] +#[repr(u8)] +pub enum PrioWhich { + PRIO_PROCESS = 0, + PRIO_PGRP = 1, + PRIO_USER = 2, +} + +impl TryFrom for PrioWhich { + type Error = crate::error::Error; + + fn try_from(raw: i32) -> Result { + if raw > Self::PRIO_USER as i32 || raw < Self::PRIO_PROCESS as i32 { + return_errno!(EINVAL, "invalid which value"); + } + Ok(unsafe { core::mem::transmute(raw as u8) }) + } +} + +/// Process priority value +/// +/// Lower values give a process a higher scheduling priority. +#[derive(Copy, Clone, Debug, Default, PartialEq, PartialOrd)] +pub struct NiceValue { + value: i32, +} + +impl NiceValue { + const MAX_PRIO: i32 = 19; + + const MIN_PRIO: i32 = -20; + + pub fn max_value() -> Self { + Self { + value: Self::MAX_PRIO, + } + } + + pub fn min_value() -> Self { + Self { + value: Self::MIN_PRIO, + } + } + + /// Convert [19,-20] to rlimit style value [1,40]. + pub fn to_rlimit_val(&self) -> i32 { + Self::MAX_PRIO - self.value + 1 + } +} + +impl From for NiceValue { + fn from(raw: i32) -> Self { + let value = if raw < Self::MIN_PRIO { + Self::MIN_PRIO + } else if raw > Self::MAX_PRIO { + Self::MAX_PRIO + } else { + raw + }; + Self { value } + } +} diff --git a/src/libos/src/sched/syscalls.rs b/src/libos/src/sched/syscalls.rs index e3741b09..8a4ff7cc 100644 --- a/src/libos/src/sched/syscalls.rs +++ b/src/libos/src/sched/syscalls.rs @@ -1,6 +1,8 @@ use super::cpu_set::{CpuSet, AVAIL_CPUSET}; +use super::priority::{NiceValue, PrioWhich}; use crate::prelude::*; use crate::util::mem_util::from_user::*; +use core::convert::TryFrom; pub fn do_sched_yield() -> Result { super::do_sched_yield::do_sched_yield(); @@ -78,3 +80,19 @@ pub fn do_getcpu(cpu_ptr: *mut u32, node_ptr: *mut u32) -> Result { } Ok(0) } + +pub fn do_set_priority(which: i32, who: i32, prio: i32) -> Result { + let which = PrioWhich::try_from(which)?; + let prio = NiceValue::from(prio); + super::do_priority::do_set_priority(which, who, prio)?; + Ok(0) +} + +pub fn do_get_priority(which: i32, who: i32) -> Result { + let which = PrioWhich::try_from(which)?; + let prio = super::do_priority::do_get_priority(which, who)?; + // To avoid negative return values, "getpriority()" will + // not return the normal nice-value, but a negated value that + // has been offset by 20 (ie it returns 40..1 instead of -20..19) + Ok(prio.to_rlimit_val() as isize) +} diff --git a/src/libos/src/syscall/mod.rs b/src/libos/src/syscall/mod.rs index 8f674aa6..5a3f7513 100644 --- a/src/libos/src/syscall/mod.rs +++ b/src/libos/src/syscall/mod.rs @@ -50,7 +50,10 @@ use crate::process::{ do_spawn_for_glibc, do_spawn_for_musl, do_vfork, do_wait4, pid_t, posix_spawnattr_t, FdOp, RobustListHead, SpawnFileActions, ThreadStatus, }; -use crate::sched::{do_getcpu, do_sched_getaffinity, do_sched_setaffinity, do_sched_yield}; +use crate::sched::{ + do_get_priority, do_getcpu, do_sched_getaffinity, do_sched_setaffinity, do_sched_yield, + do_set_priority, +}; use crate::signal::{ do_kill, do_rt_sigaction, do_rt_sigpending, do_rt_sigprocmask, do_rt_sigreturn, do_rt_sigtimedwait, do_sigaltstack, do_tgkill, do_tkill, sigaction_t, siginfo_t, sigset_t, @@ -230,8 +233,8 @@ macro_rules! process_syscall_table_with_callback { (Statfs = 137) => do_statfs(path: *const i8, statfs_buf: *mut Statfs), (Fstatfs = 138) => do_fstatfs(fd: FileDesc, statfs_buf: *mut Statfs), (SysFs = 139) => handle_unsupported(), - (Getpriority = 140) => handle_unsupported(), - (Setpriority = 141) => handle_unsupported(), + (Getpriority = 140) => do_get_priority(which: i32, who: i32), + (Setpriority = 141) => do_set_priority(which: i32, who: i32, prio: i32), (SchedSetparam = 142) => handle_unsupported(), (SchedGetparam = 143) => handle_unsupported(), (SchedSetscheduler = 144) => handle_unsupported(), diff --git a/test/sched/main.c b/test/sched/main.c index ca8b822a..0eac31ed 100644 --- a/test/sched/main.c +++ b/test/sched/main.c @@ -7,6 +7,7 @@ #include #include #include +#include #include #include #include "test.h" @@ -303,6 +304,58 @@ static int test_getcpu_after_setaffinity() { return 0; } +// ============================================================================ +// Test cases for setpriority and getpriority +// ============================================================================ + +static int test_set_get_priority(int which, id_t who, int prio) { + if (setpriority(which, who, prio) < 0) { + THROW_ERROR("failed to setpriority"); + } + + errno = 0; + int ret_prio = getpriority(which, who); + if (errno != 0) { + THROW_ERROR("failed to getpriority"); + } + + if (ret_prio != prio) { + THROW_ERROR("failed to check prio after set"); + } + + return 0; +} + +static int test_set_get_priority_process() { + int which = PRIO_PROCESS; + id_t who = getpid(); + int prio = 10; + if (test_set_get_priority(which, who, prio) < 0) { + THROW_ERROR("failed to test process"); + } + return 0; +} + +static int test_set_get_priority_pgrp() { + int which = PRIO_PGRP; + id_t who = getpgrp(); + int prio = -10; + if (test_set_get_priority(which, who, prio) < 0) { + THROW_ERROR("failed to test pgrp"); + } + return 0; +} + +static int test_set_get_priority_user() { + int which = PRIO_USER; + id_t who = 0; + int prio = -1; + if (test_set_get_priority(which, who, prio) < 0) { + THROW_ERROR("failed to test user"); + } + return 0; +} + // ============================================================================ // Test suite main // ============================================================================ @@ -321,6 +374,9 @@ static test_case_t test_cases[] = { TEST_CASE(test_sched_xetaffinity_children_inheritance), TEST_CASE(test_getcpu), TEST_CASE(test_getcpu_after_setaffinity), + TEST_CASE(test_set_get_priority_process), + TEST_CASE(test_set_get_priority_pgrp), + TEST_CASE(test_set_get_priority_user), }; int main() {