Bläddra i källkod

Kernel+LibC: Implement clock_gettime() and clock_nanosleep()

Only the CLOCK_MONOTONIC clock is supported at the moment, and it only
has millisecond precision. :^)
Andreas Kling 5 år sedan
förälder
incheckning
cc68654a44
9 ändrade filer med 126 tillägg och 6 borttagningar
  1. 62 0
      Kernel/Process.cpp
  2. 2 0
      Kernel/Process.h
  3. 4 0
      Kernel/Syscall.cpp
  4. 11 1
      Kernel/Syscall.h
  5. 9 0
      Kernel/Thread.cpp
  6. 1 0
      Kernel/Thread.h
  7. 10 0
      Kernel/UnixTypes.h
  8. 14 0
      Libraries/LibC/time.cpp
  9. 13 5
      Libraries/LibC/time.h

+ 62 - 0
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;
+    }
+}

+ 2 - 0
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);

+ 4 - 0
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;

+ 11 - 1
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;

+ 9 - 0
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<Thread::SleepBlocker>(wakeup_time);
+    if (wakeup_time > g_uptime)
+        ASSERT(ret == Thread::BlockResult::InterruptedBySignal);
+    return wakeup_time;
+}
+
 const char* Thread::state_string() const
 {
     switch (state()) {

+ 1 - 0
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,

+ 10 - 0
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 {

+ 14 - 0
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, &params);
+    __RETURN_WITH_ERRNO(rc, rc, -1);
+}
+
 }

+ 13 - 5
Libraries/LibC/time.h

@@ -36,15 +36,23 @@ char* asctime(const struct tm*);
 #define CLOCKS_PER_SEC 1000
 clock_t clock();
 
+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)
 
-// This is c++11+, but we have no macro for that now.
-struct timespec {
-    time_t tv_sec;
-    long tv_nsec;
-};
 
 __END_DECLS