From cc68654a44be00deb5edf3b2d2319a663012f015 Mon Sep 17 00:00:00 2001 From: Andreas Kling Date: Sat, 2 Nov 2019 19:34:06 +0100 Subject: [PATCH] Kernel+LibC: Implement clock_gettime() and clock_nanosleep() Only the CLOCK_MONOTONIC clock is supported at the moment, and it only has millisecond precision. :^) --- Kernel/Process.cpp | 62 +++++++++++++++++++++++++++++++++++++++++ Kernel/Process.h | 2 ++ Kernel/Syscall.cpp | 4 +++ Kernel/Syscall.h | 12 +++++++- Kernel/Thread.cpp | 9 ++++++ Kernel/Thread.h | 1 + Kernel/UnixTypes.h | 10 +++++++ Libraries/LibC/time.cpp | 14 ++++++++++ Libraries/LibC/time.h | 20 +++++++++---- 9 files changed, 127 insertions(+), 7 deletions(-) diff --git a/Kernel/Process.cpp b/Kernel/Process.cpp index 77b4e830213..98107a2b2f2 100644 --- a/Kernel/Process.cpp +++ b/Kernel/Process.cpp @@ -3154,3 +3154,65 @@ int Process::sys$getrandom(void* buffer, size_t buffer_size, unsigned int flags return 0; } + +int Process::sys$clock_gettime(clockid_t clock_id, timespec* ts) +{ + if (!validate_write_typed(ts)) + return -EFAULT; + + switch (clock_id) { + case CLOCK_MONOTONIC: + ts->tv_sec = g_uptime / TICKS_PER_SECOND; + ts->tv_nsec = (g_uptime % TICKS_PER_SECOND) * 1000000; + break; + default: + return -EINVAL; + } + + return 0; +} + +int Process::sys$clock_nanosleep(const Syscall::SC_clock_nanosleep_params* params) +{ + if (!validate_read_typed(params)) + return -EFAULT; + + if (params->requested_sleep && !validate_read_typed(params->requested_sleep)) + return -EFAULT; + + if (params->remaining_sleep && !validate_write_typed(params->remaining_sleep)) + return -EFAULT; + + clockid_t clock_id = params->clock_id; + int flags = params->flags; + bool is_absolute = flags & TIMER_ABSTIME; + auto* requested_sleep = params->requested_sleep; + auto* remaining_sleep = params->remaining_sleep; + + switch (clock_id) { + case CLOCK_MONOTONIC: { + u64 wakeup_time; + if (is_absolute) { + u64 time_to_wake = (requested_sleep->tv_sec * 1000 + requested_sleep->tv_nsec / 1000000); + wakeup_time = current->sleep_until(time_to_wake); + } else { + u32 ticks_to_sleep = (requested_sleep->tv_sec * 1000 + requested_sleep->tv_nsec / 1000000); + if (!ticks_to_sleep) + return 0; + wakeup_time = current->sleep(ticks_to_sleep); + } + if (wakeup_time > g_uptime) { + u32 ticks_left = wakeup_time - g_uptime; + if (!is_absolute && remaining_sleep) { + remaining_sleep->tv_sec = ticks_left / TICKS_PER_SECOND; + ticks_left -= remaining_sleep->tv_sec * TICKS_PER_SECOND; + remaining_sleep->tv_nsec = ticks_left * 1000000; + } + return -EINTR; + } + return 0; + } + default: + return -EINVAL; + } +} diff --git a/Kernel/Process.h b/Kernel/Process.h index 9be94c58d46..9513a912371 100644 --- a/Kernel/Process.h +++ b/Kernel/Process.h @@ -157,6 +157,8 @@ public: int sys$sleep(unsigned seconds); int sys$usleep(useconds_t usec); int sys$gettimeofday(timeval*); + int sys$clock_gettime(clockid_t, timespec*); + int sys$clock_nanosleep(const Syscall::SC_clock_nanosleep_params*); int sys$gethostname(char*, ssize_t); int sys$uname(utsname*); int sys$readlink(const char*, char*, ssize_t); diff --git a/Kernel/Syscall.cpp b/Kernel/Syscall.cpp index 36eec19782e..b313c20729f 100644 --- a/Kernel/Syscall.cpp +++ b/Kernel/Syscall.cpp @@ -315,6 +315,10 @@ static u32 handle(RegisterDump& regs, u32 function, u32 arg1, u32 arg2, u32 arg3 return current->process().sys$realpath((const char*)arg1, (char*)arg2, (size_t)arg3); case Syscall::SC_getrandom: return current->process().sys$getrandom((void*)arg1, (size_t)arg2, (unsigned int)arg3); + case Syscall::SC_clock_gettime: + return current->process().sys$clock_gettime((clockid_t)arg1, (timespec*)arg2); + case Syscall::SC_clock_nanosleep: + return current->process().sys$clock_nanosleep((const Syscall::SC_clock_nanosleep_params*)arg1); default: kprintf("<%u> int0x82: Unknown function %u requested {%x, %x, %x}\n", current->process().pid(), function, arg1, arg2, arg3); return -ENOSYS; diff --git a/Kernel/Syscall.h b/Kernel/Syscall.h index 860d7e7e880..8d5ce8c18b8 100644 --- a/Kernel/Syscall.h +++ b/Kernel/Syscall.h @@ -8,6 +8,7 @@ extern "C" { struct timeval; +struct timespec; } #define ENUMERATE_SYSCALLS \ @@ -131,7 +132,9 @@ struct timeval; __ENUMERATE_SYSCALL(realpath) \ __ENUMERATE_SYSCALL(get_process_name) \ __ENUMERATE_SYSCALL(fchdir) \ - __ENUMERATE_SYSCALL(getrandom) + __ENUMERATE_SYSCALL(getrandom) \ + __ENUMERATE_SYSCALL(clock_gettime) \ + __ENUMERATE_SYSCALL(clock_nanosleep) namespace Syscall { @@ -181,6 +184,13 @@ struct SC_select_params { struct timeval* timeout; }; +struct SC_clock_nanosleep_params { + int clock_id; + int flags; + const struct timespec* requested_sleep; + struct timespec* remaining_sleep; +}; + struct SC_sendto_params { int sockfd; const void* data; diff --git a/Kernel/Thread.cpp b/Kernel/Thread.cpp index ca6a3271aef..ef12fb74c42 100644 --- a/Kernel/Thread.cpp +++ b/Kernel/Thread.cpp @@ -154,6 +154,15 @@ u64 Thread::sleep(u32 ticks) return wakeup_time; } +u64 Thread::sleep_until(u64 wakeup_time) +{ + ASSERT(state() == Thread::Running); + auto ret = current->block(wakeup_time); + if (wakeup_time > g_uptime) + ASSERT(ret == Thread::BlockResult::InterruptedBySignal); + return wakeup_time; +} + const char* Thread::state_string() const { switch (state()) { diff --git a/Kernel/Thread.h b/Kernel/Thread.h index e0e34ad853f..f8f54f67249 100644 --- a/Kernel/Thread.h +++ b/Kernel/Thread.h @@ -220,6 +220,7 @@ public: VirtualAddress thread_specific_data() const { return m_thread_specific_data; } u64 sleep(u32 ticks); + u64 sleep_until(u64 wakeup_time); enum class BlockResult { WokeNormally, diff --git a/Kernel/UnixTypes.h b/Kernel/UnixTypes.h index e4a0c8ed4cc..52122f87272 100644 --- a/Kernel/UnixTypes.h +++ b/Kernel/UnixTypes.h @@ -382,6 +382,16 @@ struct timeval { suseconds_t tv_usec; }; +struct timespec { + time_t tv_sec; + long tv_nsec; +}; + +typedef int clockid_t; + +#define CLOCK_MONOTONIC 1 +#define TIMER_ABSTIME 99 + #define UTSNAME_ENTRY_LEN 65 struct utsname { diff --git a/Libraries/LibC/time.cpp b/Libraries/LibC/time.cpp index 6024664fedf..8a301b3bb5f 100644 --- a/Libraries/LibC/time.cpp +++ b/Libraries/LibC/time.cpp @@ -117,4 +117,18 @@ clock_t clock() times(&tms); return tms.tms_utime + tms.tms_stime; } + +int clock_gettime(clockid_t clock_id, struct timespec* ts) +{ + int rc = syscall(SC_clock_gettime, clock_id, ts); + __RETURN_WITH_ERRNO(rc, rc, -1); +} + +int clock_nanosleep(clockid_t clock_id, int flags, const struct timespec* requested_sleep, struct timespec* remaining_sleep) +{ + Syscall::SC_clock_nanosleep_params params { clock_id, flags, requested_sleep, remaining_sleep }; + int rc = syscall(SC_clock_nanosleep, ¶ms); + __RETURN_WITH_ERRNO(rc, rc, -1); +} + } diff --git a/Libraries/LibC/time.h b/Libraries/LibC/time.h index 0a720dadf4d..fec9076e99e 100644 --- a/Libraries/LibC/time.h +++ b/Libraries/LibC/time.h @@ -36,15 +36,23 @@ char* asctime(const struct tm*); #define CLOCKS_PER_SEC 1000 clock_t clock(); -double difftime(time_t, time_t); -size_t strftime(char* s, size_t max, const char* format, const struct tm*); - -#define difftime(t1, t0) (double)(t1 - t0) - -// This is c++11+, but we have no macro for that now. struct timespec { time_t tv_sec; long tv_nsec; }; +typedef int clockid_t; + +#define CLOCK_MONOTONIC 1 +#define TIMER_ABSTIME 99 + +int clock_gettime(clockid_t, struct timespec*); +int clock_nanosleep(clockid_t, int flags, const struct timespec* requested_sleep, struct timespec* remaining_sleep); + +double difftime(time_t, time_t); +size_t strftime(char* s, size_t max, const char* format, const struct tm*); + +#define difftime(t1, t0) (double)(t1 - t0) + + __END_DECLS