diff --git a/kernel/CMakeLists.txt b/kernel/CMakeLists.txt index 9f66d318..4f643a06 100644 --- a/kernel/CMakeLists.txt +++ b/kernel/CMakeLists.txt @@ -47,7 +47,7 @@ set(SOURCES src/sys/signal.cpp src/sys/socket.cpp src/sys/poll.cpp - src/sys/alarm.cpp + src/sys/setitimer.cpp src/sys/pledge.cpp src/sys/memstat.cpp src/fs/VFS.cpp diff --git a/kernel/src/api/Syscall.h b/kernel/src/api/Syscall.h index d85c6914..f05dd6fe 100644 --- a/kernel/src/api/Syscall.h +++ b/kernel/src/api/Syscall.h @@ -8,8 +8,8 @@ _e(umount) _e(pstat) _e(getrusage) _e(symlinkat) _e(readlinkat) _e(umask) _e(linkat) _e(faccessat) \ _e(pivot_root) _e(sigreturn) _e(sigaction) _e(kill) _e(sigprocmask) _e(setpgid) _e(isatty) \ _e(getpgid) _e(socket) _e(bind) _e(connect) _e(listen) _e(accept) _e(poll) _e(msync) \ - _e(truncate) _e(ftruncate) _e(utimensat) _e(alarm) _e(pledge) _e(memstat) _e(setsid) \ - _e(getsid) + _e(truncate) _e(ftruncate) _e(utimensat) _e(setitimer) _e(pledge) _e(memstat) \ + _e(setsid) _e(getsid) enum Syscalls { diff --git a/kernel/src/sys/alarm.cpp b/kernel/src/sys/alarm.cpp deleted file mode 100644 index 652d08c1..00000000 --- a/kernel/src/sys/alarm.cpp +++ /dev/null @@ -1,35 +0,0 @@ -#include "Pledge.h" -#include "sys/Syscall.h" -#include "thread/Scheduler.h" -#include "thread/Timer.h" -#include - -Result sys_alarm(Registers*, SyscallArgs args) -{ - unsigned int seconds = (unsigned int)args[0]; - - auto* current = Scheduler::current(); - - TRY(check_pledge(current, Promise::p_stdio)); - - Clock* clock = nullptr; - - u64 ticks_left = current->timer ? current->timer->clock->ticks_left(current->timer) : 0; - - if (current->timer) - { - clock = current->timer->clock; - if (clock) clock->remove_from_timer_queue(current->timer); - } - else - current->timer = TRY(make()); - - if (!clock) clock = &g_realtime_clock; - - current->timer->total_ticks = ceil_div((static_cast(seconds) * 1'000'000'000), clock->resolution()); - current->timer->thread = current; - - clock->add_to_timer_queue(current->timer); - - return (ticks_left * clock->resolution()) / 1'000'000'000; -} diff --git a/kernel/src/sys/exec.cpp b/kernel/src/sys/exec.cpp index a86700f7..534e5aaa 100644 --- a/kernel/src/sys/exec.cpp +++ b/kernel/src/sys/exec.cpp @@ -107,6 +107,17 @@ Result sys_execve(Registers* regs, SyscallArgs args) guard.deactivate(); + // Reset all timers. + if (current->real_timer.clock) { current->real_timer.clock->remove_from_timer_queue(¤t->real_timer); } + if (current->virtual_timer.clock) + { + current->virtual_timer.clock->remove_from_timer_queue(¤t->virtual_timer); + } + if (current->profiling_timer.clock) + { + current->profiling_timer.clock->remove_from_timer_queue(¤t->profiling_timer); + } + for (int i = 0; i < FD_MAX; i++) { auto& descriptor = current->fd_table[i]; @@ -173,6 +184,9 @@ Result sys_fork(Registers* regs, SyscallArgs) thread->pgid = current->pgid; thread->sid = current->sid; + thread->virtual_clock.set_resolution(1'000'000); + thread->profiling_clock.set_resolution(1'000'000); + for (int i = 0; i < FD_MAX; i++) { thread->fd_table[i] = current->fd_table[i]; } image->apply(thread); diff --git a/kernel/src/sys/setitimer.cpp b/kernel/src/sys/setitimer.cpp new file mode 100644 index 00000000..422407c2 --- /dev/null +++ b/kernel/src/sys/setitimer.cpp @@ -0,0 +1,86 @@ +#define _TIMESPEC_MACROS_INCLUDED +#include "Pledge.h" +#include "memory/MemoryManager.h" +#include "sys/Syscall.h" +#include "thread/Scheduler.h" +#include "thread/Timer.h" +#include +#include + +Result sys_setitimer(Registers*, SyscallArgs args) +{ + int which = (int)args[0]; + const struct itimerval* new_timer = (const struct itimerval*)args[1]; + struct itimerval* old_timer = (struct itimerval*)args[2]; + + auto* current = Scheduler::current(); + + TRY(check_pledge(current, Promise::p_stdio)); + + Timer* timer; + Clock* clock; + switch (which) + { + case ITIMER_REAL: + timer = ¤t->real_timer; + clock = &g_realtime_clock; + break; + case ITIMER_VIRTUAL: + timer = ¤t->virtual_timer; + clock = ¤t->virtual_clock; + break; + case ITIMER_PROF: + timer = ¤t->profiling_timer; + clock = ¤t->profiling_clock; + break; + default: return err(EINVAL); + } + + if (old_timer) + { + if (timer->clock) + { + struct itimerval result; + auto total = timer->clock->from_ticks(timer->total_ticks); + auto left = timer->clock->from_ticks(timer->clock->ticks_left(timer)); + result.it_interval = TIMESPEC_TO_TIMEVAL(total); + result.it_value = TIMESPEC_TO_TIMEVAL(left); + if (!MemoryManager::copy_to_user_typed(old_timer, &result)) return err(EFAULT); + } + else + { + struct itimerval result; + memset(&result, 0, sizeof(result)); + if (!MemoryManager::copy_to_user_typed(old_timer, &result)) return err(EFAULT); + } + } + + if (new_timer) + { + if (timer->clock) timer->clock->remove_from_timer_queue(timer); + + struct itimerval itimer; + if (!MemoryManager::copy_from_user_typed(new_timer, &itimer)) return err(EFAULT); + + timer->signo = SIGALRM; // FIXME: Also use SIGVTALRM or SIGPROF for other timer types. + timer->thread = current; + + if (itimer.it_interval.tv_sec != 0 || itimer.it_interval.tv_usec != 0) + { + timer->restart = true; + timer->total_ticks = + (itimer.it_interval.tv_sec * 1'000'000'000 + itimer.it_interval.tv_usec * 1000) / clock->resolution(); + } + else + timer->restart = false; + + if (itimer.it_value.tv_sec != 0 || itimer.it_value.tv_usec != 0) + { + timer->delta_ticks = + (itimer.it_value.tv_sec * 1'000'000'000 + itimer.it_value.tv_usec * 1000) / clock->resolution(); + clock->add_to_timer_queue(timer); + } + } + + return 0; +} diff --git a/kernel/src/thread/Clock.cpp b/kernel/src/thread/Clock.cpp index edf72f16..ce8bce87 100644 --- a/kernel/src/thread/Clock.cpp +++ b/kernel/src/thread/Clock.cpp @@ -73,8 +73,6 @@ void Clock::add_to_timer_queue(Timer* timer) { check(timer->clock == nullptr); - timer->delta_ticks = timer->total_ticks; - for (auto* t : m_timer_queue) { if (timer->delta_ticks <= t->delta_ticks) @@ -159,3 +157,10 @@ u64 Clock::ticks_left(Timer* timer) } return total; } + +struct timespec Clock::from_ticks(u64 ticks) +{ + u64 nanoseconds = ticks * m_resolution; + return timespec { .tv_sec = static_cast(nanoseconds / 1'000'000'000), + .tv_nsec = static_cast(nanoseconds % 1'000'000'000) }; +} diff --git a/kernel/src/thread/Clock.h b/kernel/src/thread/Clock.h index b2aaee56..e9653937 100644 --- a/kernel/src/thread/Clock.h +++ b/kernel/src/thread/Clock.h @@ -19,6 +19,8 @@ struct Clock void get_time(struct timespec& out); long resolution(); + struct timespec from_ticks(u64 ticks); + u64 ticks_left(Timer* timer); private: diff --git a/kernel/src/thread/Scheduler.cpp b/kernel/src/thread/Scheduler.cpp index 56be87ec..fcad36f7 100644 --- a/kernel/src/thread/Scheduler.cpp +++ b/kernel/src/thread/Scheduler.cpp @@ -102,6 +102,9 @@ namespace Scheduler thread->is_kernel = true; thread->active_directory = MMU::kernel_page_directory(); + thread->virtual_clock.set_resolution(1'000'000); + thread->profiling_clock.set_resolution(1'000'000); + thread->auth = Credentials { .uid = 0, .euid = 0, .suid = 0, .gid = 0, .egid = 0, .sgid = 0 }; g_threads.append(thread); @@ -217,11 +220,7 @@ namespace Scheduler MemoryManager::unmap_owned_and_free_vm(stack.bottom(), stack.bytes() / ARCH_PAGE_SIZE).release_value(); } - if (thread->timer) - { - thread->timer->clock->remove_from_timer_queue(thread->timer); - delete thread->timer; - } + if (thread->real_timer.clock) { thread->real_timer.clock->remove_from_timer_queue(&thread->real_timer); } delete thread; @@ -302,7 +301,12 @@ namespace Scheduler if (is_in_kernel(regs)) g_current->kernel_ticks_self++; else + { + g_current->virtual_clock.tick(); g_current->user_ticks_self++; + } + + g_current->profiling_clock.tick(); g_current->ticks_left--; diff --git a/kernel/src/thread/Thread.h b/kernel/src/thread/Thread.h index f5767f34..0e9190ee 100644 --- a/kernel/src/thread/Thread.h +++ b/kernel/src/thread/Thread.h @@ -98,7 +98,12 @@ struct Thread : public LinkedListNode mode_t umask { 0 }; - Timer* timer { nullptr }; + Timer real_timer; + Timer virtual_timer; + Timer profiling_timer; + + Clock virtual_clock; + Clock profiling_clock; StaticString<128> cmdline; diff --git a/kernel/src/thread/Timer.h b/kernel/src/thread/Timer.h index f63942f3..bd1c1bf4 100644 --- a/kernel/src/thread/Timer.h +++ b/kernel/src/thread/Timer.h @@ -8,11 +8,11 @@ struct Clock; class Timer : public LinkedListNode { public: - u64 delta_ticks; - u64 total_ticks; + u64 delta_ticks { 0 }; + u64 total_ticks { 0 }; Thread* thread; int signo { SIGALRM }; bool restart { false }; - Clock* clock; + Clock* clock { nullptr }; }; diff --git a/libc/include/bits/itimer.h b/libc/include/bits/itimer.h new file mode 100644 index 00000000..2708a330 --- /dev/null +++ b/libc/include/bits/itimer.h @@ -0,0 +1,18 @@ +/* bits/itimer.h: Constants for setitimer() and getitimer(). */ + +#ifndef _BITS_ITIMER_H +#define _BITS_ITIMER_H + +#include + +#define ITIMER_REAL 0 +#define ITIMER_VIRTUAL 1 +#define ITIMER_PROF 2 + +struct itimerval +{ + struct timeval it_interval; + struct timeval it_value; +}; + +#endif diff --git a/libc/include/bits/timespec.h b/libc/include/bits/timespec.h index 66ec4dfa..4a1896cb 100644 --- a/libc/include/bits/timespec.h +++ b/libc/include/bits/timespec.h @@ -33,4 +33,13 @@ struct timeval } \ } while (0); #endif + +#define TIMEVAL_TO_TIMESPEC(x) \ + { \ + .tv_sec = (x).tv_sec, .tv_nsec = (x).tv_usec * 1000 \ + } +#define TIMESPEC_TO_TIMEVAL(x) \ + { \ + .tv_sec = (x).tv_sec, .tv_usec = (x).tv_nsec / 1000 \ + } #endif diff --git a/libc/include/sys/time.h b/libc/include/sys/time.h index e29720b8..5b4bf915 100644 --- a/libc/include/sys/time.h +++ b/libc/include/sys/time.h @@ -6,6 +6,7 @@ #define _INCLUDE_TIMESPEC_MACROS #include +#include #include #ifdef __cplusplus @@ -25,6 +26,9 @@ extern "C" /* Change a symlink's access and modification timestamps, with microsecond precision. */ int lutimes(const char* path, const struct timeval buf[2]); + /* Set an interval timer. */ + int setitimer(int which, const struct itimerval* new_timer, struct itimerval* old_timer); + #ifdef __cplusplus } #endif diff --git a/libc/src/time.cpp b/libc/src/time.cpp index 7f13646a..3f2bb102 100644 --- a/libc/src/time.cpp +++ b/libc/src/time.cpp @@ -200,4 +200,10 @@ extern "C" return ru.ru_utime.tv_sec * CLOCKS_PER_SEC + ru.ru_utime.tv_usec; } + + int setitimer(int which, const struct itimerval* new_timer, struct itimerval* old_timer) + { + long rc = syscall(SYS_setitimer, which, new_timer, old_timer); + __errno_return(rc, int); + } } diff --git a/libc/src/unistd.cpp b/libc/src/unistd.cpp index c856635c..bb54e0ee 100644 --- a/libc/src/unistd.cpp +++ b/libc/src/unistd.cpp @@ -11,6 +11,7 @@ #include #include #include +#include #include #include @@ -519,7 +520,12 @@ extern "C" unsigned int alarm(unsigned int seconds) { - return (unsigned int)syscall(SYS_alarm, seconds); + struct itimerval old_value; + struct itimerval new_value; + memset(&new_value, 0, sizeof(new_value)); + new_value.it_value.tv_sec = seconds; + if (setitimer(ITIMER_REAL, &new_value, &old_value) < 0) return 0; + return (unsigned int)old_value.it_value.tv_sec; } int pledge(const char* promises, const char* execpromises)