diff --git a/src/Enclave.edl b/src/Enclave.edl index 9dfaeb17..4b4c7a7e 100644 --- a/src/Enclave.edl +++ b/src/Enclave.edl @@ -118,6 +118,7 @@ enclave { void occlum_ocall_clock_gettime(clockid_t clockid, [out] struct timespec* ts); void occlum_ocall_clock_getres(clockid_t clockid, [out] struct timespec* res); void occlum_ocall_rdtsc([out] uint32_t* low, [out] uint32_t* high); + void occlum_ocall_get_timerslack([out] int *timer_slack); int occlum_ocall_nanosleep( [in] const struct timespec* req, diff --git a/src/libos/src/net/io_multiplexing/select.rs b/src/libos/src/net/io_multiplexing/select.rs index ba8fe14d..66387efd 100644 --- a/src/libos/src/net/io_multiplexing/select.rs +++ b/src/libos/src/net/io_multiplexing/select.rs @@ -1,3 +1,4 @@ +use super::super::time::timer_slack::TIMERSLACK; use super::*; pub fn select( @@ -63,7 +64,10 @@ pub fn select( if !timeout.is_null() { let time_left = unsafe { *(timeout) }; time_left.validate()?; - assert!(time_left.as_duration() <= origin_timeout.as_duration()); + assert!( + // Note: TIMERSLACK is a single value use maintained by the libOS and will not vary for different threads. + time_left.as_duration() <= origin_timeout.as_duration() + (*TIMERSLACK).to_duration() + ); } debug!("returned pollfds are {:?}", pollfds); diff --git a/src/libos/src/process/prctl/macros.rs b/src/libos/src/process/prctl/macros.rs index d408c684..d5766d35 100644 --- a/src/libos/src/process/prctl/macros.rs +++ b/src/libos/src/process/prctl/macros.rs @@ -3,34 +3,31 @@ // Implement `PrctlNum` and `PrctlCmd`. #[macro_export] macro_rules! impl_prctl_nums_and_cmds { - ($( $prctl_name: ident => ( $prctl_num: expr, $($prctl_type_tt: tt)* ) ),+,) => { + ($( $prctl_name: ident => ( $prctl_num: expr, $($prctl_type_tt: tt),* ) ),+,) => { $(const $prctl_name:i32 = $prctl_num;)* impl_prctl_cmds! { $( - $prctl_name => ( $($prctl_type_tt)*), + $prctl_name => ( $($prctl_type_tt),* ), )* } } } macro_rules! impl_prctl_cmds { - ($( $prctl_name: ident => ( $($prctl_type_tt: tt)* ) ),+,) => { + ($( $prctl_name: ident => ( $($prctl_type_tt: tt),* ) ),+,) => { #[derive(Debug)] #[allow(non_camel_case_types)] pub enum PrctlCmd<'a> { $( - $prctl_name( get_arg_type!($($prctl_type_tt)*) ), + $prctl_name($(get_arg_type!($prctl_type_tt)),* ), )* } } } macro_rules! get_arg_type { - (()) => { - () - }; - ($($prctl_type_tt: tt)*) => { - $($prctl_type_tt)* + ($prctl_type_tt: tt) => { + $prctl_type_tt }; } diff --git a/src/libos/src/process/prctl/mod.rs b/src/libos/src/process/prctl/mod.rs index c03735e6..2a6976af 100644 --- a/src/libos/src/process/prctl/mod.rs +++ b/src/libos/src/process/prctl/mod.rs @@ -3,6 +3,7 @@ use alloc::vec::Vec; use std::ffi::CString; use std::os::raw::c_char; +use super::super::time::timer_slack::TIMERSLACK; use super::thread::ThreadName; use crate::prelude::*; use crate::util::mem_util::from_user::{check_array, clone_cstring_safely}; @@ -16,8 +17,9 @@ impl_prctl_nums_and_cmds! { // Format: // prctl_name => (prctl_num, prctl_type_arg, ... PR_SET_NAME => (15, ThreadName), - // Get thread name - PR_GET_NAME => (16, &'a mut [u8]), + PR_GET_NAME => (16, (&'a mut [u8])), + PR_SET_TIMERSLACK => (29, u64), + PR_GET_TIMERSLACK => (30, ()), } impl<'a> PrctlCmd<'a> { @@ -39,6 +41,8 @@ impl<'a> PrctlCmd<'a> { }; PrctlCmd::PR_GET_NAME(buf_checked) } + PR_SET_TIMERSLACK => PrctlCmd::PR_SET_TIMERSLACK(arg2), + PR_GET_TIMERSLACK => PrctlCmd::PR_GET_TIMERSLACK(()), _ => { debug!("prctl cmd num: {}", cmd); return_errno!(EINVAL, "unsupported prctl command"); @@ -59,7 +63,17 @@ pub fn do_prctl(cmd: PrctlCmd) -> Result { let name = current.name(); c_buf.copy_from_slice(name.as_slice()); } - _ => warn!("Prctl command not supported"), + PrctlCmd::PR_SET_TIMERSLACK(nanoseconds) => { + return_errno!( + EINVAL, + "Setting timer slack for different libos process is not supported" + ); + } + PrctlCmd::PR_GET_TIMERSLACK(()) => { + let nanoseconds = (*TIMERSLACK).to_u32(); + return Ok(nanoseconds as isize); + } + _ => return_errno!(EINVAL, "Prctl command not supported"), } Ok(0) diff --git a/src/libos/src/process/syscalls.rs b/src/libos/src/process/syscalls.rs index 263220f0..14bee40c 100644 --- a/src/libos/src/process/syscalls.rs +++ b/src/libos/src/process/syscalls.rs @@ -175,7 +175,7 @@ pub fn do_futex( pub fn do_prctl(option: i32, arg2: u64, arg3: u64, arg4: u64, arg5: u64) -> Result { let prctl_cmd = super::prctl::PrctlCmd::from_raw(option, arg2, arg3, arg4, arg5)?; - super::prctl::do_prctl(prctl_cmd).map(|_| 0) + super::prctl::do_prctl(prctl_cmd) } pub fn do_arch_prctl(code: u32, addr: *mut usize) -> Result { diff --git a/src/libos/src/time/mod.rs b/src/libos/src/time/mod.rs index 68a20703..454c42c6 100644 --- a/src/libos/src/time/mod.rs +++ b/src/libos/src/time/mod.rs @@ -1,3 +1,4 @@ +use self::timer_slack::*; use super::*; use core::convert::TryFrom; use process::pid_t; @@ -8,6 +9,7 @@ use std::{fmt, u64}; use syscall::SyscallNum; mod profiler; +pub mod timer_slack; pub mod up_time; pub use profiler::ThreadProfiler; @@ -182,7 +184,7 @@ pub fn do_nanosleep(req: ×pec_t, rem: Option<&mut timespec_t>) -> Result<() assert!(sgx_status == sgx_status_t::SGX_SUCCESS); assert!(ret == 0 || libc::errno() == Errno::EINTR as i32); if ret != 0 { - assert!(u_rem.as_duration() <= req.as_duration()); + assert!(u_rem.as_duration() <= req.as_duration() + (*TIMERSLACK).to_duration()); if let Some(rem) = rem { *rem = u_rem; } diff --git a/src/libos/src/time/timer_slack.rs b/src/libos/src/time/timer_slack.rs new file mode 100644 index 00000000..dcd249fd --- /dev/null +++ b/src/libos/src/time/timer_slack.rs @@ -0,0 +1,47 @@ +use super::*; +use std::time::Duration; + +pub struct TimerSlack { + nanoseconds: u32, +} + +impl TimerSlack { + pub fn new(nanoseconds: u32) -> Result { + let timerslack = Self { nanoseconds }; + timerslack.validate()?; + Ok(timerslack) + } + + pub fn validate(&self) -> Result<()> { + // Timer slack bigger than 1ms is considered invalid here. The kernel default timer slack is 50us. + if self.nanoseconds < 1_000_000 { + Ok(()) + } else { + return_errno!(EINVAL, "invalid value for TimerSlack"); + } + } + + pub fn to_u32(&self) -> u32 { + self.nanoseconds + } + + pub fn to_duration(&self) -> Duration { + Duration::from_nanos(self.to_u32() as u64) + } +} + +lazy_static! { + pub static ref TIMERSLACK: TimerSlack = + do_get_timerslack().unwrap_or(TimerSlack::new(50_000).unwrap()); // Use kernel default timer slack 50us. +} + +fn do_get_timerslack() -> Result { + extern "C" { + fn occlum_ocall_get_timerslack(nanosecond: *mut i32) -> sgx_status_t; + } + let mut timer_slack: i32 = 0; + let sgx_status = unsafe { occlum_ocall_get_timerslack(&mut timer_slack as *mut i32) }; + assert!(sgx_status == sgx_status_t::SGX_SUCCESS); + + TimerSlack::new(timer_slack as u32) +} diff --git a/src/pal/src/ocalls/time.c b/src/pal/src/ocalls/time.c index 7adb7fca..7772e1e4 100644 --- a/src/pal/src/ocalls/time.c +++ b/src/pal/src/ocalls/time.c @@ -1,5 +1,6 @@ #include #include +#include #include "ocalls.h" void occlum_ocall_gettimeofday(struct timeval *tv) { @@ -35,3 +36,8 @@ void occlum_ocall_rdtsc(uint32_t *low, uint32_t *high) { *low = (uint32_t)rax; *high = (uint32_t)rdx; } + +void occlum_ocall_get_timerslack(int *timer_slack) { + int nanoseconds = prctl(PR_GET_TIMERSLACK, 0, 0, 0, 0); + *timer_slack = nanoseconds; +} diff --git a/src/run/main.c b/src/run/main.c index 78d62702..57accc83 100644 --- a/src/run/main.c +++ b/src/run/main.c @@ -7,6 +7,7 @@ #include #include #include +#include int main(int argc, char *argv[]) { // Parse arguments diff --git a/test/prctl/main.c b/test/prctl/main.c index ec22c90c..7c38990a 100644 --- a/test/prctl/main.c +++ b/test/prctl/main.c @@ -126,6 +126,19 @@ static int test_prctl_get_default_thread_name(void) { return 0; } +static int test_prctl_get_timerslack(void) { + int nanoseconds = prctl(PR_GET_TIMERSLACK, 0, 0, 0, 0); + if (nanoseconds < 0) { + THROW_ERROR("test prctl get timer slack failed"); + }; + printf("timer slack = %d ns\n", nanoseconds); + // Kernel default timer slack is 50us + if (nanoseconds != 50000) { + THROW_ERROR("timer slack is not 50us"); + } + return 0; +} + // ============================================================================ // Test suite main // ============================================================================ @@ -133,6 +146,7 @@ static test_case_t test_cases[] = { TEST_CASE(test_prctl_set_get_long_name), // over 16 bytes TEST_CASE(test_prctl_set_get_normal_name), TEST_CASE(test_prctl_get_default_thread_name), + TEST_CASE(test_prctl_get_timerslack), }; int main() {