From a33b63d06660626902343061d527347c6802390d Mon Sep 17 00:00:00 2001 From: Bananymous Date: Thu, 1 Aug 2024 21:09:31 +0300 Subject: [PATCH] Kernel/LibC: Implement alarm() and setitimer() This makes vim able to start! --- kernel/include/kernel/Process.h | 8 ++ kernel/kernel/IDT.cpp | 4 + kernel/kernel/Process.cpp | 107 ++++++++++++++++++ .../libraries/LibC/include/sys/syscall.h | 1 + userspace/libraries/LibC/sys/time.cpp | 7 ++ userspace/libraries/LibC/unistd.cpp | 12 ++ 6 files changed, 139 insertions(+) diff --git a/kernel/include/kernel/Process.h b/kernel/include/kernel/Process.h index d843a99ae0..54a2d4aba2 100644 --- a/kernel/include/kernel/Process.h +++ b/kernel/include/kernel/Process.h @@ -19,6 +19,7 @@ #include #include #include +#include #include namespace LibELF { class LoadableELF; } @@ -77,6 +78,7 @@ namespace Kernel BAN::ErrorOr sys_wait(pid_t pid, int* stat_loc, int options); BAN::ErrorOr sys_sleep(int seconds); BAN::ErrorOr sys_nanosleep(const timespec* rqtp, timespec* rmtp); + BAN::ErrorOr sys_setitimer(int which, const itimerval* value, itimerval* ovalue); BAN::ErrorOr sys_setpwd(const char* path); BAN::ErrorOr sys_getpwd(char* buffer, size_t size); @@ -207,6 +209,9 @@ namespace Kernel BAN::ErrorOr absolute_path_of(BAN::StringView) const; + // ONLY CALLED BY TIMER INTERRUPT + static void update_alarm_queue(); + private: Process(const Credentials&, pid_t pid, pid_t parent, pid_t sid, pid_t pgrp); static Process* create_process(const Credentials&, pid_t parent, pid_t sid = 0, pid_t pgrp = 0); @@ -275,6 +280,9 @@ namespace Kernel BAN::String m_working_directory; BAN::Vector m_threads; + uint64_t m_alarm_interval_ns { 0 }; + uint64_t m_alarm_wake_time_ns { 0 }; + mutable SpinLock m_signal_lock; struct sigaction m_signal_handlers[_SIGMAX + 1] { }; uint64_t m_signal_pending_mask { 0 }; diff --git a/kernel/kernel/IDT.cpp b/kernel/kernel/IDT.cpp index 0cf2630c4d..c9872f708f 100644 --- a/kernel/kernel/IDT.cpp +++ b/kernel/kernel/IDT.cpp @@ -337,6 +337,10 @@ done: { ASSERT(InterruptController::get().is_in_service(IRQ_TIMER)); InterruptController::get().eoi(IRQ_TIMER); + + if (Processor::current_is_bsb()) + Process::update_alarm_queue(); + Processor::scheduler().timer_interrupt(); auto& current_thread = Thread::current(); diff --git a/kernel/kernel/Process.cpp b/kernel/kernel/Process.cpp index 31f6a4bdd4..56bd3b9562 100644 --- a/kernel/kernel/Process.cpp +++ b/kernel/kernel/Process.cpp @@ -27,6 +27,7 @@ namespace Kernel { + static BAN::LinkedList s_alarm_processes; static BAN::Vector s_processes; static RecursiveSpinLock s_process_lock; @@ -640,6 +641,112 @@ namespace Kernel return 0; } + BAN::ErrorOr Process::sys_setitimer(int which, const itimerval* value, itimerval* ovalue) + { + switch (which) + { + case ITIMER_PROF: + case ITIMER_REAL: + case ITIMER_VIRTUAL: + break; + default: + return BAN::Error::from_errno(EINVAL); + } + + LockGuard _(m_process_lock); + + if (value) + TRY(validate_pointer_access(value, sizeof(itimerval))); + if (ovalue) + TRY(validate_pointer_access(ovalue, sizeof(itimerval))); + + { + SpinLockGuard _(s_process_lock); + + const uint64_t current_ns = SystemTimer::get().ns_since_boot(); + + if (m_alarm_wake_time_ns) + { + for (auto it = s_alarm_processes.begin(); it != s_alarm_processes.end(); it++) + { + if (*it != this) + continue; + s_alarm_processes.remove(it); + break; + } + } + + if (m_alarm_wake_time_ns && ovalue) + { + const uint64_t interval_us = m_alarm_interval_ns / 1000; + ovalue->it_interval = { + .tv_sec = static_cast(interval_us / 1'000'000), + .tv_usec = static_cast(interval_us % 1'000'000), + }; + + const uint64_t remaining_us = current_ns < m_alarm_wake_time_ns ? (current_ns - m_alarm_wake_time_ns) / 1000 : 1; + ovalue->it_value = { + .tv_sec = static_cast(remaining_us / 1'000'000), + .tv_usec = static_cast(remaining_us % 1'000'000), + }; + + m_alarm_interval_ns = 0; + m_alarm_wake_time_ns = 0; + } + + if (value) + { + const uint64_t value_us = value->it_value.tv_sec * 1'000'000 + value->it_value.tv_usec; + const uint64_t interval_us = value->it_interval.tv_sec * 1'000'000 + value->it_interval.tv_usec; + if (value_us) + { + const uint64_t wake_time_ns = current_ns + value_us * 1000; + + auto it = s_alarm_processes.begin(); + while (it != s_alarm_processes.end() && (*it)->m_alarm_wake_time_ns < wake_time_ns) + it++; + TRY(s_alarm_processes.insert(it, this)); + + m_alarm_wake_time_ns = wake_time_ns; + m_alarm_interval_ns = interval_us * 1000; + } + } + } + + return 0; + } + + void Process::update_alarm_queue() + { + ASSERT(Processor::current_is_bsb()); + + SpinLockGuard _(s_process_lock); + + const uint64_t current_ns = SystemTimer::get().ns_since_boot(); + + while (!s_alarm_processes.empty()) + { + auto* process = s_alarm_processes.front(); + if (current_ns < process->m_alarm_wake_time_ns) + break; + + process->add_pending_signal(SIGALRM); + Processor::scheduler().unblock_thread(process->m_threads.front()); + + s_alarm_processes.remove(s_alarm_processes.begin()); + + if (process->m_alarm_interval_ns == 0) + continue; + + process->m_alarm_wake_time_ns = current_ns + process->m_alarm_interval_ns; + + auto it = s_alarm_processes.begin(); + while (it != s_alarm_processes.end() && (*it)->m_alarm_wake_time_ns < process->m_alarm_wake_time_ns) + it++; + MUST(s_alarm_processes.insert(it, process)); + } + } + BAN::ErrorOr Process::create_file_or_dir(BAN::StringView path, mode_t mode) { switch (mode & Inode::Mode::TYPE_MASK) diff --git a/userspace/libraries/LibC/include/sys/syscall.h b/userspace/libraries/LibC/include/sys/syscall.h index dbcacd9f13..8b695c1364 100644 --- a/userspace/libraries/LibC/include/sys/syscall.h +++ b/userspace/libraries/LibC/include/sys/syscall.h @@ -86,6 +86,7 @@ __BEGIN_DECLS O(SYS_SIGACTION, sigaction) \ O(SYS_SIGPENDING, sigpending) \ O(SYS_SIGPROCMASK, sigprocmask) \ + O(SYS_SETITIMER, setitimer) \ enum Syscall { diff --git a/userspace/libraries/LibC/sys/time.cpp b/userspace/libraries/LibC/sys/time.cpp index 27637d3390..5f14aca376 100644 --- a/userspace/libraries/LibC/sys/time.cpp +++ b/userspace/libraries/LibC/sys/time.cpp @@ -1,5 +1,7 @@ +#include #include #include +#include int gettimeofday(struct timeval* __restrict tp, void* __restrict tzp) { @@ -13,3 +15,8 @@ int gettimeofday(struct timeval* __restrict tp, void* __restrict tzp) tp->tv_usec = ts.tv_nsec / 1000; return 0; } + +int setitimer(int which, const struct itimerval* __restrict value, struct itimerval* __restrict ovalue) +{ + return syscall(SYS_SETITIMER, which, value, ovalue); +} diff --git a/userspace/libraries/LibC/unistd.cpp b/userspace/libraries/LibC/unistd.cpp index 8a85ebab32..82dd95d146 100644 --- a/userspace/libraries/LibC/unistd.cpp +++ b/userspace/libraries/LibC/unistd.cpp @@ -8,6 +8,7 @@ #include #include #include +#include #include char** environ; @@ -514,3 +515,14 @@ int access(const char* path, int amode) { return syscall(SYS_ACCESS, path, amode); } + +unsigned alarm(unsigned seconds) +{ + itimerval value, ovalue; + value.it_value.tv_sec = seconds; + value.it_value.tv_usec = 0; + value.it_interval.tv_sec = 0; + value.it_interval.tv_usec = 0; + setitimer(ITIMER_REAL, &value, &ovalue); + return ovalue.it_value.tv_sec; +}