kernel: Add POSIX timer support

This commit is contained in:
apio 2024-01-05 22:12:58 +01:00
parent f8cc093e17
commit 41c90aa436
Signed by: apio
GPG Key ID: B8A7D06E42258954
13 changed files with 275 additions and 28 deletions

View File

@ -25,6 +25,7 @@ set(SOURCES
src/thread/ThreadImage.cpp
src/thread/Scheduler.cpp
src/thread/Clock.cpp
src/thread/Timer.cpp
src/sys/Syscall.cpp
src/sys/exit.cpp
src/sys/clock_gettime.cpp

View File

@ -9,7 +9,8 @@
_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(setitimer) _e(pledge) _e(memstat) \
_e(setsid) _e(getsid) _e(getgroups) _e(setgroups) _e(pause) _e(sigsuspend)
_e(setsid) _e(getsid) _e(getgroups) _e(setgroups) _e(pause) _e(sigsuspend) \
_e(timer_create) _e(timer_settime) _e(timer_gettime) _e(timer_delete)
enum Syscalls
{

View File

@ -108,15 +108,16 @@ Result<u64> sys_execve(Registers* regs, SyscallArgs args)
guard.deactivate();
// Reset all timers.
if (current->real_timer.clock) { current->real_timer.clock->remove_from_timer_queue(&current->real_timer); }
if (current->virtual_timer.clock)
current->real_timer.disarm();
current->virtual_timer.disarm();
current->profiling_timer.disarm();
for (int i = 0; i < MAX_POSIX_TIMERS; i++)
{
current->virtual_timer.clock->remove_from_timer_queue(&current->virtual_timer);
}
if (current->profiling_timer.clock)
{
current->profiling_timer.clock->remove_from_timer_queue(&current->profiling_timer);
if (current->posix_timers[i].has_value())
{
current->posix_timers[i]->disarm();
current->posix_timers[i] = {};
}
}
for (int i = 0; i < FD_MAX; i++)

View File

@ -4,8 +4,11 @@
#include "sys/Syscall.h"
#include "thread/Scheduler.h"
#include "thread/Timer.h"
#include <bits/clockid.h>
#include <bits/itimer.h>
#include <bits/sigevent.h>
#include <luna/Common.h>
#include <sys/types.h>
Result<u64> sys_setitimer(Registers*, SyscallArgs args)
{
@ -38,11 +41,11 @@ Result<u64> sys_setitimer(Registers*, SyscallArgs args)
if (old_timer)
{
if (timer->clock)
if (timer->active_clock)
{
struct itimerval result;
auto total = timer->clock->from_ticks(timer->total_ticks);
auto left = timer->clock->from_ticks(timer->clock->ticks_left(timer));
auto total = timer->active_clock->from_ticks(timer->interval_ticks);
auto left = timer->active_clock->from_ticks(timer->active_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);
@ -57,7 +60,7 @@ Result<u64> sys_setitimer(Registers*, SyscallArgs args)
if (new_timer)
{
if (timer->clock) timer->clock->remove_from_timer_queue(timer);
timer->disarm();
struct itimerval itimer;
if (!MemoryManager::copy_from_user_typed(new_timer, &itimer)) return err(EFAULT);
@ -68,7 +71,7 @@ Result<u64> sys_setitimer(Registers*, SyscallArgs args)
if (itimer.it_interval.tv_sec != 0 || itimer.it_interval.tv_usec != 0)
{
timer->restart = true;
timer->total_ticks =
timer->interval_ticks =
(itimer.it_interval.tv_sec * 1'000'000'000 + itimer.it_interval.tv_usec * 1000) / clock->resolution();
}
else
@ -76,11 +79,162 @@ Result<u64> sys_setitimer(Registers*, SyscallArgs args)
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);
u64 ticks = (itimer.it_value.tv_sec * 1'000'000'000 + itimer.it_value.tv_usec * 1000) / clock->resolution();
timer->arm(clock, ticks);
}
}
return 0;
}
Result<u64> sys_timer_create(Registers*, SyscallArgs args)
{
clockid_t clockid = (clockid_t)args[0];
struct sigevent* sevp = (struct sigevent*)args[1];
timer_t* timerid = (timer_t*)args[2];
auto* current = Scheduler::current();
TRY(check_pledge(current, Promise::p_stdio));
struct sigevent ksevp;
if (!sevp)
{
ksevp.sigev_notify = SIGEV_SIGNAL;
ksevp.sigev_signo = SIGALRM;
}
else if (!MemoryManager::copy_from_user_typed(sevp, &ksevp))
return err(EFAULT);
Clock* clock;
switch (clockid)
{
case CLOCK_REALTIME: clock = &g_realtime_clock; break;
case CLOCK_MONOTONIC: clock = &g_monotonic_clock; break;
default: return err(EINVAL);
}
if (ksevp.sigev_notify != SIGEV_SIGNAL) return err(ENOTSUP);
if (ksevp.sigev_signo <= 0 || ksevp.sigev_signo > NSIG) return err(EINVAL);
int id = TRY(current->allocate_timerid());
current->posix_timers[id] = Timer {};
Timer* timer = current->posix_timers[id].value_ptr();
timer->signo = ksevp.sigev_signo;
timer->designated_clock = clock;
if (!MemoryManager::copy_to_user_typed(timerid, &id)) return err(EFAULT);
return 0;
}
Result<u64> sys_timer_settime(Registers*, SyscallArgs args)
{
timer_t timerid = (timer_t)args[0];
int flags = (int)args[1];
const struct itimerspec* new_value = (const struct itimerspec*)args[2];
struct itimerspec* old_value = (struct itimerspec*)args[3];
if (timerid < 0 || timerid >= MAX_POSIX_TIMERS) return err(EINVAL);
if (flags > 0) return err(ENOTSUP);
auto* current = Scheduler::current();
TRY(check_pledge(current, Promise::p_stdio));
if (!current->posix_timers[timerid].has_value()) return err(EINVAL);
Timer* timer = current->posix_timers[timerid].value_ptr();
if (old_value)
{
if (timer->active_clock)
{
struct itimerspec result;
result.it_interval = timer->active_clock->from_ticks(timer->interval_ticks);
result.it_value = timer->active_clock->from_ticks(timer->active_clock->ticks_left(timer));
if (!MemoryManager::copy_to_user_typed(old_value, &result)) return err(EFAULT);
}
else
{
struct itimerspec result;
memset(&result, 0, sizeof(result));
if (!MemoryManager::copy_to_user_typed(old_value, &result)) return err(EFAULT);
}
}
struct itimerspec itimer;
if (!MemoryManager::copy_from_user_typed(new_value, &itimer)) return err(EFAULT);
timer->disarm();
Clock* clock = timer->designated_clock;
check(clock);
timer->thread = current;
if (itimer.it_interval.tv_sec != 0 || itimer.it_interval.tv_nsec != 0)
{
timer->restart = true;
timer->interval_ticks =
(itimer.it_interval.tv_sec * 1'000'000'000 + itimer.it_interval.tv_nsec) / clock->resolution();
}
else
timer->restart = false;
if (itimer.it_value.tv_sec != 0 || itimer.it_value.tv_nsec != 0)
{
u64 ticks = (itimer.it_value.tv_sec * 1'000'000'000 + itimer.it_value.tv_nsec) / clock->resolution();
timer->arm(clock, ticks);
}
return 0;
}
Result<u64> sys_timer_gettime(Registers*, SyscallArgs args)
{
timer_t timerid = (timer_t)args[0];
struct itimerspec* value = (struct itimerspec*)args[1];
if (timerid < 0 || timerid >= MAX_POSIX_TIMERS) return err(EINVAL);
auto* current = Scheduler::current();
TRY(check_pledge(current, Promise::p_stdio));
if (!current->posix_timers[timerid].has_value()) return err(EINVAL);
Timer* timer = current->posix_timers[timerid].value_ptr();
if (timer->active_clock)
{
struct itimerspec result;
result.it_interval = timer->active_clock->from_ticks(timer->interval_ticks);
result.it_value = timer->active_clock->from_ticks(timer->active_clock->ticks_left(timer));
if (!MemoryManager::copy_to_user_typed(value, &result)) return err(EFAULT);
}
else
{
struct itimerspec result;
memset(&result, 0, sizeof(result));
if (!MemoryManager::copy_to_user_typed(value, &result)) return err(EFAULT);
}
return 0;
}
Result<u64> sys_timer_delete(Registers*, SyscallArgs args)
{
timer_t timerid = (timer_t)args[0];
if (timerid < 0 || timerid >= MAX_POSIX_TIMERS) return err(EINVAL);
auto* current = Scheduler::current();
TRY(check_pledge(current, Promise::p_stdio));
if (!current->posix_timers[timerid].has_value()) return err(EINVAL);
Timer* timer = current->posix_timers[timerid].value_ptr();
timer->disarm();
current->posix_timers[timerid] = {};
return 0;
}

View File

@ -71,7 +71,7 @@ void Clock::update(const struct timespec& time)
void Clock::add_to_timer_queue(Timer* timer)
{
check(timer->clock == nullptr);
check(timer->active_clock == nullptr);
for (auto* t : m_timer_queue)
{
@ -86,12 +86,12 @@ void Clock::add_to_timer_queue(Timer* timer)
m_timer_queue.append(timer);
timer->clock = this;
timer->active_clock = this;
}
void Clock::remove_from_timer_queue(Timer* timer)
{
check(timer->clock == this);
check(timer->active_clock == this);
auto maybe_next = m_timer_queue.next(timer);
if (maybe_next.has_value())
@ -101,7 +101,7 @@ void Clock::remove_from_timer_queue(Timer* timer)
}
m_timer_queue.remove(timer);
timer->clock = nullptr;
timer->active_clock = nullptr;
}
void Clock::tick()
@ -122,7 +122,7 @@ void Clock::tick()
if (t->delta_ticks == 0)
{
this->m_timer_queue.remove(t);
t->clock = nullptr;
t->active_clock = nullptr;
t->thread->send_signal(t->signo);
if (t->restart) timers_to_be_restarted.append(t);
return true;
@ -131,7 +131,10 @@ void Clock::tick()
return false;
});
timers_to_be_restarted.consume([this](Timer* t) { add_to_timer_queue(t); });
timers_to_be_restarted.consume([this](Timer* t) {
t->delta_ticks = t->interval_ticks;
add_to_timer_queue(t);
});
}
void Clock::get_time(struct timespec& out)
@ -146,7 +149,7 @@ long Clock::resolution()
u64 Clock::ticks_left(Timer* timer)
{
check(timer->clock == this);
check(timer->active_clock == this);
u64 total = 0;
if (timer->delta_ticks == 0) return 0;

View File

@ -221,7 +221,14 @@ namespace Scheduler
MemoryManager::unmap_owned_and_free_vm(stack.bottom(), stack.bytes() / ARCH_PAGE_SIZE).release_value();
}
if (thread->real_timer.clock) { thread->real_timer.clock->remove_from_timer_queue(&thread->real_timer); }
// FIXME: Shouldn't all this be done when the timers' destructors are called?
thread->real_timer.disarm();
thread->virtual_timer.disarm();
thread->profiling_timer.disarm();
for (int i = 0; i < MAX_POSIX_TIMERS; i++)
{
if (thread->posix_timers[i].has_value()) thread->posix_timers[i]->disarm();
}
delete thread;

View File

@ -58,6 +58,28 @@ Result<FileDescriptor*> Thread::resolve_fd(int fd)
return maybe_descriptor.value_ptr();
}
Result<int> Thread::allocate_timerid()
{
for (int i = 0; i < MAX_POSIX_TIMERS; i++)
{
// FIXME: Possible race condition, this should be used alongside a mutex.
if (!posix_timers[i].has_value()) { return i; }
}
return err(EMFILE);
}
Result<Timer*> Thread::resolve_timerid(int tid)
{
if (tid < 0 || tid >= MAX_POSIX_TIMERS) return err(EBADF);
Option<Timer>& maybe_timer = posix_timers[tid];
if (!maybe_timer.has_value()) return err(EINVAL);
return maybe_timer.value_ptr();
}
Result<SharedPtr<VFS::Inode>> Thread::resolve_atfile(int dirfd, const String& path, bool allow_empty_path,
bool follow_last_symlink, SharedPtr<VFS::Inode>* parent_inode)
{

View File

@ -19,6 +19,8 @@
#error "Unknown architecture."
#endif
constexpr int MAX_POSIX_TIMERS = 64;
class Timer;
enum class ThreadState
@ -106,6 +108,11 @@ struct Thread : public LinkedListNode<Thread>
Clock virtual_clock;
Clock profiling_clock;
Option<Timer> posix_timers[MAX_POSIX_TIMERS];
Result<int> allocate_timerid();
Result<Timer*> resolve_timerid(int id);
StaticString<128> cmdline;
String current_directory_path = {};

View File

@ -0,0 +1,16 @@
#include "thread/Timer.h"
#include "thread/Clock.h"
void Timer::disarm()
{
if (active_clock) active_clock->remove_from_timer_queue(this);
}
void Timer::arm(Clock* c, u64 ticks)
{
disarm();
delta_ticks = ticks;
c->add_to_timer_queue(this);
}

View File

@ -9,10 +9,14 @@ class Timer : public LinkedListNode<Timer>
{
public:
u64 delta_ticks { 0 };
u64 total_ticks { 0 };
u64 interval_ticks { 0 };
Thread* thread;
int signo { SIGALRM };
bool restart { false };
Clock* clock { nullptr };
void disarm();
void arm(Clock* clock, u64 ticks);
Clock* active_clock { nullptr };
Clock* designated_clock { nullptr };
};

View File

@ -1,4 +1,4 @@
/* bits/itimer.h: Constants for setitimer() and getitimer(). */
/* bits/itimer.h: Constants for POSIX timers, setitimer() and getitimer(). */
#ifndef _BITS_ITIMER_H
#define _BITS_ITIMER_H
@ -15,4 +15,10 @@ struct itimerval
struct timeval it_value;
};
struct itimerspec
{
struct timespec it_interval;
struct timespec it_value;
};
#endif

View File

@ -0,0 +1,24 @@
/* bits/sigevent.h: The sigevent structure. */
#ifndef _BITS_SIGEVENT_H
#define _BITS_SIGEVENT_H
#define SIGEV_NONE 0
#define SIGEV_SIGNAL 1
#define SIGEV_THREAD 2
union sigval {
int sival_int;
void* sival_ptr;
};
struct sigevent
{
int sigev_notify;
int sigev_signo;
union sigval sigev_value;
void (*sigev_notify_function)(union sigval);
void* sigev_notify_attributes;
};
#endif

View File

@ -20,6 +20,7 @@ typedef __u32_t uid_t;
typedef __u32_t gid_t;
typedef __u64_t nlink_t;
typedef __i64_t suseconds_t;
typedef __i32_t timer_t;
typedef off_t fpos_t;