Add nanosleep syscall
This commit is contained in:
parent
c8a921fd4b
commit
30c99add6f
@ -19,6 +19,7 @@ enclave {
|
||||
int ocall_run_new_task(void);
|
||||
void ocall_gettimeofday([out] long* sec, [out] long* us);
|
||||
void ocall_clock_gettime(int clockid, [out] long* sec, [out] long* ns);
|
||||
void ocall_nanosleep(long sec, long nsec);
|
||||
void ocall_sync(void);
|
||||
int ocall_sched_getaffinity([out] int *error, int pid, size_t cpusize, [out, size=cpusize] unsigned char* buf);
|
||||
int ocall_sched_setaffinity([out] int *error, int pid, size_t cpusize, [in, size=cpusize] const unsigned char* buf);
|
||||
|
@ -211,6 +211,8 @@ pub extern "C" fn dispatch_syscall(
|
||||
SYS_GETTIMEOFDAY => do_gettimeofday(arg0 as *mut timeval_t),
|
||||
SYS_CLOCK_GETTIME => do_clock_gettime(arg0 as clockid_t, arg1 as *mut timespec_t),
|
||||
|
||||
SYS_NANOSLEEP => do_nanosleep(arg0 as *const timespec_t, arg1 as *mut timespec_t),
|
||||
|
||||
SYS_UNAME => do_uname(arg0 as *mut utsname_t),
|
||||
|
||||
SYS_PRLIMIT64 => do_prlimit(
|
||||
@ -812,6 +814,18 @@ fn do_clock_gettime(clockid: clockid_t, ts_u: *mut timespec_t) -> Result<isize>
|
||||
Ok(0)
|
||||
}
|
||||
|
||||
// TODO: handle remainder
|
||||
fn do_nanosleep(req_u: *const timespec_t, rem_u: *mut timespec_t) -> Result<isize> {
|
||||
check_ptr(req_u)?;
|
||||
if !rem_u.is_null() {
|
||||
check_mut_ptr(rem_u)?;
|
||||
}
|
||||
|
||||
let req = timespec_t::from_raw_ptr(req_u)?;
|
||||
time::do_nanosleep(&req)?;
|
||||
Ok(0)
|
||||
}
|
||||
|
||||
// FIXME: use this
|
||||
const MAP_FAILED: *const c_void = ((-1) as i64) as *const c_void;
|
||||
|
||||
|
@ -43,6 +43,16 @@ pub struct timespec_t {
|
||||
nsec: i64,
|
||||
}
|
||||
|
||||
impl timespec_t {
|
||||
pub fn from_raw_ptr(ptr: *const timespec_t) -> Result<timespec_t> {
|
||||
let ts = unsafe { *ptr };
|
||||
if ts.sec < 0 || ts.nsec < 0 || ts.nsec >= 1_000_000_000 {
|
||||
return_errno!(EINVAL, "Invalid timespec fields");
|
||||
}
|
||||
Ok(ts)
|
||||
}
|
||||
}
|
||||
|
||||
#[allow(non_camel_case_types)]
|
||||
pub type clockid_t = i32;
|
||||
|
||||
@ -77,6 +87,10 @@ impl ClockID {
|
||||
}
|
||||
|
||||
pub fn do_clock_gettime(clockid: ClockID) -> Result<timespec_t> {
|
||||
extern "C" {
|
||||
fn ocall_clock_gettime(clockid: clockid_t, sec: *mut time_t, ns: *mut i64) -> sgx_status_t;
|
||||
}
|
||||
|
||||
let mut sec = 0;
|
||||
let mut nsec = 0;
|
||||
unsafe {
|
||||
@ -89,12 +103,17 @@ pub fn do_clock_gettime(clockid: ClockID) -> Result<timespec_t> {
|
||||
Ok(timespec_t { sec, nsec })
|
||||
}
|
||||
|
||||
extern "C" {
|
||||
fn ocall_clock_gettime(clockid: clockid_t, sec: *mut time_t, ns: *mut i64) -> sgx_status_t;
|
||||
pub fn do_nanosleep(req: ×pec_t) -> Result<()> {
|
||||
extern "C" {
|
||||
fn ocall_nanosleep(sec: time_t, nsec: i64) -> sgx_status_t;
|
||||
}
|
||||
unsafe {
|
||||
ocall_nanosleep(req.sec, req.nsec);
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
// For SEFS
|
||||
|
||||
pub struct OcclumTimeProvider;
|
||||
|
||||
impl TimeProvider for OcclumTimeProvider {
|
||||
|
@ -9,21 +9,29 @@ pub mod from_user {
|
||||
|
||||
/// Check the user pointer is within the readable memory range of the user process
|
||||
pub fn check_ptr<T>(user_ptr: *const T) -> Result<()> {
|
||||
if user_ptr.is_null() {
|
||||
return_errno!(EINVAL, "Address 0 is invalid");
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Check the mutable user pointer is within the writable memory of the user process
|
||||
pub fn check_mut_ptr<T>(user_ptr: *mut T) -> Result<()> {
|
||||
if user_ptr.is_null() {
|
||||
return_errno!(EINVAL, "Address 0 is invalid");
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Check the readonly array is within the readable memory of the user process
|
||||
pub fn check_array<T>(user_buf: *const T, count: usize) -> Result<()> {
|
||||
check_ptr(user_buf);
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Check the mutable array is within the writable memory of the user process
|
||||
pub fn check_mut_array<T>(user_buf: *mut T, count: usize) -> Result<()> {
|
||||
check_mut_ptr(user_buf);
|
||||
Ok(())
|
||||
}
|
||||
|
||||
|
@ -265,6 +265,11 @@ void ocall_clock_gettime(int clockid, time_t* sec, long* ns) {
|
||||
*ns = ts.tv_nsec;
|
||||
}
|
||||
|
||||
void ocall_nanosleep(time_t sec, long nsec) {
|
||||
struct timespec tv = { .tv_sec = sec, .tv_nsec = nsec };
|
||||
nanosleep(&tv, NULL);
|
||||
}
|
||||
|
||||
int ocall_sched_getaffinity(int* error, int pid, size_t cpusize, unsigned char* buf) {
|
||||
int ret = syscall(__NR_sched_getaffinity, pid, cpusize, buf);
|
||||
if (error) {
|
||||
|
@ -7,7 +7,7 @@ TEST_DEPS := dev_null client
|
||||
# Tests: need to be compiled and run by test-% target
|
||||
TESTS := empty env hello_world malloc mmap file fs_perms getpid spawn sched pipe time \
|
||||
truncate readdir mkdir link tls pthread uname rlimit server \
|
||||
server_epoll unix_socket cout hostfs cpuid rdtsc device
|
||||
server_epoll unix_socket cout hostfs cpuid rdtsc device sleep
|
||||
# Benchmarks: need to be compiled and run by bench-% target
|
||||
BENCHES := spawn_and_exit_latency pipe_throughput unix_socket_throughput
|
||||
|
||||
|
5
test/sleep/Makefile
Normal file
5
test/sleep/Makefile
Normal file
@ -0,0 +1,5 @@
|
||||
include ../test_common.mk
|
||||
|
||||
EXTRA_C_FLAGS := -Wno-return-stack-address
|
||||
EXTRA_LINK_FLAGS :=
|
||||
BIN_ARGS :=
|
174
test/sleep/main.c
Normal file
174
test/sleep/main.c
Normal file
@ -0,0 +1,174 @@
|
||||
#include <time.h>
|
||||
#include <unistd.h>
|
||||
#include <errno.h>
|
||||
#include <assert.h>
|
||||
#include "test.h"
|
||||
|
||||
// ============================================================================
|
||||
// Helper macros
|
||||
// ============================================================================
|
||||
|
||||
#define S (1000 * 1000 * 1000)
|
||||
#define MS (1000 * 1000)
|
||||
#define US (1000)
|
||||
#define NS (1)
|
||||
|
||||
// ============================================================================
|
||||
// Helper functions
|
||||
// ============================================================================
|
||||
|
||||
static inline void validate_timespec(const struct timespec* tv) {
|
||||
assert(tv->tv_sec >= 0 && tv->tv_nsec >= 0 && tv->tv_nsec < S);
|
||||
}
|
||||
|
||||
|
||||
// retval = (a < b) ? -1 : ((a > b) ? 1 : 0)
|
||||
static int timespec_cmp(const struct timespec *a, const struct timespec *b)
|
||||
{
|
||||
validate_timespec(a);
|
||||
validate_timespec(b);
|
||||
|
||||
if (a->tv_sec < b->tv_sec) {
|
||||
return -1;
|
||||
} else if (a->tv_sec > b->tv_sec) {
|
||||
return 1;
|
||||
} else {
|
||||
return a->tv_nsec < b->tv_nsec ? -1 :
|
||||
(a->tv_nsec > b->tv_nsec ? 1 : 0);
|
||||
}
|
||||
}
|
||||
|
||||
// diff = | a - b |
|
||||
static void timespec_diff(const struct timespec *a, const struct timespec *b,
|
||||
struct timespec *diff)
|
||||
{
|
||||
validate_timespec(a);
|
||||
validate_timespec(b);
|
||||
|
||||
const struct timespec *begin, *end;
|
||||
if (timespec_cmp(a, b) <= 0) {
|
||||
begin = a; end = b;
|
||||
} else {
|
||||
begin = b; end = a;
|
||||
}
|
||||
|
||||
diff->tv_nsec = end->tv_nsec - begin->tv_nsec;
|
||||
diff->tv_sec = end->tv_sec - begin->tv_sec;
|
||||
if (diff->tv_nsec < 0) {
|
||||
diff->tv_nsec += S;
|
||||
diff->tv_sec -= 1;
|
||||
}
|
||||
|
||||
validate_timespec(diff);
|
||||
}
|
||||
|
||||
// retval = | a - b | <= precision
|
||||
static int timespec_equal(const struct timespec *a, const struct timespec *b,
|
||||
const struct timespec *precision)
|
||||
{
|
||||
struct timespec diff;
|
||||
timespec_diff(a, b, &diff);
|
||||
return timespec_cmp(&diff, precision) <= 0;
|
||||
}
|
||||
|
||||
|
||||
static int check_nanosleep(struct timespec* expected_sleep_period) {
|
||||
// The time obtained from Occlum is not very precise.
|
||||
// Here we take 1 millisecond as the time precision of Occlum.
|
||||
static struct timespec OS_TIME_PRECISION = {
|
||||
.tv_sec = 0,
|
||||
.tv_nsec = 1 * MS,
|
||||
};
|
||||
|
||||
struct timespec begin_timestamp, end_timestamp;
|
||||
clock_gettime(CLOCK_MONOTONIC, &begin_timestamp);
|
||||
|
||||
if (nanosleep(expected_sleep_period, NULL) != 0) {
|
||||
throw_error("nanosleep failed");
|
||||
}
|
||||
|
||||
clock_gettime(CLOCK_MONOTONIC, &end_timestamp);
|
||||
struct timespec actual_sleep_period;
|
||||
timespec_diff(&begin_timestamp, &end_timestamp, &actual_sleep_period);
|
||||
|
||||
return timespec_equal(expected_sleep_period, &actual_sleep_period,
|
||||
&OS_TIME_PRECISION);
|
||||
}
|
||||
|
||||
// ============================================================================
|
||||
// Test cases
|
||||
// ============================================================================
|
||||
|
||||
int test_nanosleep_0_second() {
|
||||
struct timespec period_of_0s = { .tv_sec = 0, .tv_nsec = 0 };
|
||||
return check_nanosleep(&period_of_0s);
|
||||
}
|
||||
|
||||
int test_nanosleep_1_second() {
|
||||
struct timespec period_of_1s = { .tv_sec = 1, .tv_nsec = 0 };
|
||||
return check_nanosleep(&period_of_1s);
|
||||
}
|
||||
|
||||
int test_nanosleep_10ms() {
|
||||
struct timespec period_of_10ms = { .tv_sec = 0, .tv_nsec = 10 * MS };
|
||||
return check_nanosleep(&period_of_10ms);
|
||||
}
|
||||
|
||||
// ============================================================================
|
||||
// Test cases with invalid arguments
|
||||
// ============================================================================
|
||||
|
||||
int test_nanosleep_with_null_req() {
|
||||
if (nanosleep(NULL, NULL) != -1 && errno != EINVAL) {
|
||||
throw_error("nanosleep should report error");
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
int test_nanosleep_with_negative_tv_sec() {
|
||||
// nanosleep returns EINVAL if the value in the tv_sec field is negative
|
||||
struct timespec invalid_period = { .tv_sec = -1, .tv_nsec = 0};
|
||||
if (nanosleep(&invalid_period, NULL) != -1 && errno != EINVAL) {
|
||||
throw_error("nanosleep should report EINVAL error");
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
int test_nanosleep_with_negative_tv_nsec() {
|
||||
// nanosleep returns EINVAL if the value in the tv_nsec field
|
||||
// was not in the range 0 to 999999999.
|
||||
struct timespec invalid_period = { .tv_sec = 0, .tv_nsec = -1};
|
||||
if (nanosleep(&invalid_period, NULL) != -1 && errno != EINVAL) {
|
||||
throw_error("nanosleep should report EINVAL error");
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
int test_nanosleep_with_too_large_tv_nsec() {
|
||||
// nanosleep returns EINVAL if the value in the tv_nsec field
|
||||
// was not in the range 0 to 999999999 (10^6 - 1).
|
||||
struct timespec invalid_period = { .tv_sec = 0, .tv_nsec = S};
|
||||
if (nanosleep(&invalid_period, NULL) != -1 && errno != EINVAL) {
|
||||
throw_error("nanosleep should report EINVAL error");
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
// ============================================================================
|
||||
// Test suite main
|
||||
// ============================================================================
|
||||
|
||||
// TODO: test interruption
|
||||
static test_case_t test_cases[] = {
|
||||
TEST_CASE(test_nanosleep_0_second),
|
||||
TEST_CASE(test_nanosleep_1_second),
|
||||
TEST_CASE(test_nanosleep_10ms),
|
||||
TEST_CASE(test_nanosleep_with_null_req),
|
||||
TEST_CASE(test_nanosleep_with_negative_tv_sec),
|
||||
TEST_CASE(test_nanosleep_with_negative_tv_nsec),
|
||||
TEST_CASE(test_nanosleep_with_too_large_tv_nsec)
|
||||
};
|
||||
|
||||
int main() {
|
||||
return test_suite_run(test_cases, ARRAY_SIZE(test_cases));
|
||||
}
|
Loading…
Reference in New Issue
Block a user