Add setpriority and getpriority syscall

This commit is contained in:
LI Qing 2021-12-08 15:55:04 +08:00 committed by volcano
parent e3a0233c46
commit 96166dadc2
12 changed files with 248 additions and 8 deletions

@ -48,6 +48,7 @@ pub fn do_clone(
} }
}; };
let files = current.files().clone(); let files = current.files().clone();
let nice = current.nice().clone();
let rlimits = current.rlimits().clone(); let rlimits = current.rlimits().clone();
let fs = current.fs().clone(); let fs = current.fs().clone();
let name = current.name().clone(); let name = current.name().clone();
@ -60,6 +61,7 @@ pub fn do_clone(
.fs(fs) .fs(fs)
.files(files) .files(files)
.name(name) .name(name)
.nice(nice)
.rlimits(rlimits) .rlimits(rlimits)
.sig_mask(sig_mask); .sig_mask(sig_mask);
if let Some(ctid) = ctid { if let Some(ctid) = ctid {

@ -257,6 +257,7 @@ fn new_process_common(
}; };
let fs_ref = Arc::new(RwLock::new(current_ref.fs().read().unwrap().clone())); 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 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 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() 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) .parent(parent)
.task(task) .task(task)
.sched(sched_ref) .sched(sched_ref)
.nice(nice_ref)
.rlimits(rlimit_ref) .rlimits(rlimit_ref)
.fs(fs_ref) .fs(fs_ref)
.pgrp(pgrp_ref) .pgrp(pgrp_ref)

@ -12,7 +12,7 @@
use crate::fs::{FileRef, FileTable, FsView}; use crate::fs::{FileRef, FileTable, FsView};
use crate::misc::ResourceLimits; use crate::misc::ResourceLimits;
use crate::prelude::*; use crate::prelude::*;
use crate::sched::SchedAgent; use crate::sched::{NiceValue, SchedAgent};
use crate::signal::{SigDispositions, SigQueues}; use crate::signal::{SigDispositions, SigQueues};
use crate::vm::ProcessVM; use crate::vm::ProcessVM;
@ -80,3 +80,4 @@ pub type FsViewRef = Arc<RwLock<FsView>>;
pub type SchedAgentRef = Arc<SgxMutex<SchedAgent>>; pub type SchedAgentRef = Arc<SgxMutex<SchedAgent>>;
pub type ResourceLimitsRef = Arc<SgxMutex<ResourceLimits>>; pub type ResourceLimitsRef = Arc<SgxMutex<ResourceLimits>>;
pub type ProcessGrpRef = Arc<ProcessGrp>; pub type ProcessGrpRef = Arc<ProcessGrp>;
pub type NiceValueRef = Arc<RwLock<NiceValue>>;

@ -2,8 +2,8 @@ use super::super::table;
use super::super::task::Task; use super::super::task::Task;
use super::super::thread::{ThreadBuilder, ThreadId, ThreadName}; use super::super::thread::{ThreadBuilder, ThreadId, ThreadName};
use super::super::{ use super::super::{
FileTableRef, ForcedExitStatus, FsViewRef, ProcessGrpRef, ProcessRef, ProcessVMRef, FileTableRef, ForcedExitStatus, FsViewRef, NiceValueRef, ProcessGrpRef, ProcessRef,
ResourceLimitsRef, SchedAgentRef, ProcessVMRef, ResourceLimitsRef, SchedAgentRef,
}; };
use super::{Process, ProcessInner}; use super::{Process, ProcessInner};
use crate::fs::FileMode; use crate::fs::FileMode;
@ -100,6 +100,10 @@ impl ProcessBuilder {
self.thread_builder(|tb| tb.sig_mask(sig_mask)) 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 { pub fn rlimits(mut self, rlimits: ResourceLimitsRef) -> Self {
self.thread_builder(|tb| tb.rlimits(rlimits)) self.thread_builder(|tb| tb.rlimits(rlimits))
} }

@ -1,7 +1,7 @@
use std::ptr::NonNull; use std::ptr::NonNull;
use super::{ use super::{
FileTableRef, FsViewRef, ProcessRef, ProcessVM, ProcessVMRef, ResourceLimitsRef, FileTableRef, FsViewRef, NiceValueRef, ProcessRef, ProcessVM, ProcessVMRef, ResourceLimitsRef,
RobustListHead, SchedAgentRef, SigQueues, SigSet, Task, Thread, ThreadId, ThreadInner, RobustListHead, SchedAgentRef, SigQueues, SigSet, Task, Thread, ThreadId, ThreadInner,
ThreadName, ThreadRef, ThreadName, ThreadRef,
}; };
@ -20,6 +20,7 @@ pub struct ThreadBuilder {
fs: Option<FsViewRef>, fs: Option<FsViewRef>,
files: Option<FileTableRef>, files: Option<FileTableRef>,
sched: Option<SchedAgentRef>, sched: Option<SchedAgentRef>,
nice: Option<NiceValueRef>,
rlimits: Option<ResourceLimitsRef>, rlimits: Option<ResourceLimitsRef>,
sig_mask: Option<SigSet>, sig_mask: Option<SigSet>,
clear_ctid: Option<NonNull<pid_t>>, clear_ctid: Option<NonNull<pid_t>>,
@ -37,6 +38,7 @@ impl ThreadBuilder {
fs: None, fs: None,
files: None, files: None,
sched: None, sched: None,
nice: None,
rlimits: None, rlimits: None,
sig_mask: None, sig_mask: None,
clear_ctid: None, clear_ctid: None,
@ -85,6 +87,11 @@ impl ThreadBuilder {
self self
} }
pub fn nice(mut self, nice: NiceValueRef) -> Self {
self.nice = Some(nice);
self
}
pub fn rlimits(mut self, rlimits: ResourceLimitsRef) -> Self { pub fn rlimits(mut self, rlimits: ResourceLimitsRef) -> Self {
self.rlimits = Some(rlimits); self.rlimits = Some(rlimits);
self self
@ -122,6 +129,7 @@ impl ThreadBuilder {
let fs = self.fs.unwrap_or_default(); let fs = self.fs.unwrap_or_default();
let files = self.files.unwrap_or_default(); let files = self.files.unwrap_or_default();
let sched = self.sched.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 rlimits = self.rlimits.unwrap_or_default();
let name = RwLock::new(self.name.unwrap_or_default()); let name = RwLock::new(self.name.unwrap_or_default());
let sig_mask = RwLock::new(self.sig_mask.unwrap_or_default()); let sig_mask = RwLock::new(self.sig_mask.unwrap_or_default());
@ -147,6 +155,7 @@ impl ThreadBuilder {
fs, fs,
files, files,
sched, sched,
nice,
rlimits, rlimits,
name, name,
sig_queues, sig_queues,

@ -3,7 +3,7 @@ use std::ptr::NonNull;
use super::task::Task; use super::task::Task;
use super::{ use super::{
FileTableRef, ForcedExitStatus, FsViewRef, ProcessRef, ProcessVM, ProcessVMRef, FileTableRef, ForcedExitStatus, FsViewRef, NiceValueRef, ProcessRef, ProcessVM, ProcessVMRef,
ResourceLimitsRef, RobustListHead, SchedAgentRef, TermStatus, ThreadRef, ResourceLimitsRef, RobustListHead, SchedAgentRef, TermStatus, ThreadRef,
}; };
use crate::events::HostEventFd; use crate::events::HostEventFd;
@ -38,6 +38,7 @@ pub struct Thread {
fs: FsViewRef, fs: FsViewRef,
files: FileTableRef, files: FileTableRef,
sched: SchedAgentRef, sched: SchedAgentRef,
nice: NiceValueRef,
rlimits: ResourceLimitsRef, rlimits: ResourceLimitsRef,
// Signal // Signal
sig_queues: RwLock<SigQueues>, sig_queues: RwLock<SigQueues>,
@ -156,6 +157,10 @@ impl Thread {
&self.fs &self.fs
} }
pub fn nice(&self) -> &NiceValueRef {
&self.nice
}
pub fn rlimits(&self) -> &ResourceLimitsRef { pub fn rlimits(&self) -> &ResourceLimitsRef {
&self.rlimits &self.rlimits
} }

@ -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<NiceValue> {
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<Vec<crate::process::ProcessRef>> {
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");
}
}
})
}

@ -1,11 +1,14 @@
/// CPU scheduling for threads. /// CPU scheduling for threads.
mod cpu_set; mod cpu_set;
mod do_getcpu; mod do_getcpu;
mod do_priority;
mod do_sched_affinity; mod do_sched_affinity;
mod do_sched_yield; mod do_sched_yield;
mod priority;
mod sched_agent; mod sched_agent;
mod syscalls; mod syscalls;
pub use cpu_set::NCORES; pub use cpu_set::NCORES;
pub use priority::NiceValue;
pub use sched_agent::SchedAgent; pub use sched_agent::SchedAgent;
pub use syscalls::*; pub use syscalls::*;

@ -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<i32> for PrioWhich {
type Error = crate::error::Error;
fn try_from(raw: i32) -> Result<Self> {
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<i32> 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 }
}
}

@ -1,6 +1,8 @@
use super::cpu_set::{CpuSet, AVAIL_CPUSET}; use super::cpu_set::{CpuSet, AVAIL_CPUSET};
use super::priority::{NiceValue, PrioWhich};
use crate::prelude::*; use crate::prelude::*;
use crate::util::mem_util::from_user::*; use crate::util::mem_util::from_user::*;
use core::convert::TryFrom;
pub fn do_sched_yield() -> Result<isize> { pub fn do_sched_yield() -> Result<isize> {
super::do_sched_yield::do_sched_yield(); super::do_sched_yield::do_sched_yield();
@ -78,3 +80,19 @@ pub fn do_getcpu(cpu_ptr: *mut u32, node_ptr: *mut u32) -> Result<isize> {
} }
Ok(0) Ok(0)
} }
pub fn do_set_priority(which: i32, who: i32, prio: i32) -> Result<isize> {
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<isize> {
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)
}

@ -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, do_spawn_for_glibc, do_spawn_for_musl, do_vfork, do_wait4, pid_t, posix_spawnattr_t, FdOp,
RobustListHead, SpawnFileActions, ThreadStatus, 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::{ use crate::signal::{
do_kill, do_rt_sigaction, do_rt_sigpending, do_rt_sigprocmask, do_rt_sigreturn, 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, 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), (Statfs = 137) => do_statfs(path: *const i8, statfs_buf: *mut Statfs),
(Fstatfs = 138) => do_fstatfs(fd: FileDesc, statfs_buf: *mut Statfs), (Fstatfs = 138) => do_fstatfs(fd: FileDesc, statfs_buf: *mut Statfs),
(SysFs = 139) => handle_unsupported(), (SysFs = 139) => handle_unsupported(),
(Getpriority = 140) => handle_unsupported(), (Getpriority = 140) => do_get_priority(which: i32, who: i32),
(Setpriority = 141) => handle_unsupported(), (Setpriority = 141) => do_set_priority(which: i32, who: i32, prio: i32),
(SchedSetparam = 142) => handle_unsupported(), (SchedSetparam = 142) => handle_unsupported(),
(SchedGetparam = 143) => handle_unsupported(), (SchedGetparam = 143) => handle_unsupported(),
(SchedSetscheduler = 144) => handle_unsupported(), (SchedSetscheduler = 144) => handle_unsupported(),

@ -7,6 +7,7 @@
#include <sched.h> #include <sched.h>
#include <errno.h> #include <errno.h>
#include <spawn.h> #include <spawn.h>
#include <sys/resource.h>
#include <sys/syscall.h> #include <sys/syscall.h>
#include <sys/wait.h> #include <sys/wait.h>
#include "test.h" #include "test.h"
@ -303,6 +304,58 @@ static int test_getcpu_after_setaffinity() {
return 0; 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 // Test suite main
// ============================================================================ // ============================================================================
@ -321,6 +374,9 @@ static test_case_t test_cases[] = {
TEST_CASE(test_sched_xetaffinity_children_inheritance), TEST_CASE(test_sched_xetaffinity_children_inheritance),
TEST_CASE(test_getcpu), TEST_CASE(test_getcpu),
TEST_CASE(test_getcpu_after_setaffinity), 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() { int main() {