From 9ba7f26bf070a0c4d8f477cc95a47439ef37e818 Mon Sep 17 00:00:00 2001 From: Jonas 'Sortie' Termansen Date: Wed, 9 Jan 2013 16:07:27 +0100 Subject: [PATCH] Refactor kernel time API and add timespec API. --- libc/Makefile | 1 + libc/decl/clockid_t.h | 4 + libc/include/sys/types.h | 4 +- libc/include/time.h | 2 + libc/include/timespec.h | 101 +++++++++++ libc/timespec.cpp | 60 ++++++ sortix/{time.h => include/sortix/clock.h} | 30 ++- sortix/include/sortix/kernel/time.h | 47 +++++ sortix/include/sortix/x64/bits.h | 1 + sortix/include/sortix/x86/bits.h | 1 + sortix/kernel.cpp | 2 +- sortix/scheduler.cpp | 47 +++-- sortix/thread.cpp | 2 +- sortix/time.cpp | 212 ++++++++++++++-------- 14 files changed, 398 insertions(+), 116 deletions(-) create mode 100644 libc/decl/clockid_t.h create mode 100644 libc/include/timespec.h create mode 100644 libc/timespec.cpp rename sortix/{time.h => include/sortix/clock.h} (65%) create mode 100644 sortix/include/sortix/kernel/time.h diff --git a/libc/Makefile b/libc/Makefile index 20dcddb5..20cb8977 100644 --- a/libc/Makefile +++ b/libc/Makefile @@ -108,6 +108,7 @@ strstr.o \ strtok.o \ strtok_r.o \ strxfrm.o \ +timespec.o \ ungetc.o \ vfscanf.o \ vsscanf.o \ diff --git a/libc/decl/clockid_t.h b/libc/decl/clockid_t.h new file mode 100644 index 00000000..8f526a56 --- /dev/null +++ b/libc/decl/clockid_t.h @@ -0,0 +1,4 @@ +#ifndef _CLOCKID_T_DECL +#define _CLOCKID_T_DECL +typedef __clockid_t clockid_t; +#endif diff --git a/libc/include/sys/types.h b/libc/include/sys/types.h index 59b2f8dd..85ba2321 100644 --- a/libc/include/sys/types.h +++ b/libc/include/sys/types.h @@ -1,6 +1,6 @@ /******************************************************************************* - Copyright(C) Jonas 'Sortie' Termansen 2011. + Copyright(C) Jonas 'Sortie' Termansen 2011, 2012, 2013. This file is part of the Sortix C Library. @@ -34,7 +34,7 @@ __BEGIN_DECLS @include(blkcnt_t.h) @include(blksize_t.h) @include(clock_t.h) -/* TODO: clockid_t */ +@include(clockid_t.h) @include(dev_t.h) /* TODO: fsblkcnt_t */ /* TODO: fsfilcnt_t */ diff --git a/libc/include/time.h b/libc/include/time.h index 38613896..0d7beb43 100644 --- a/libc/include/time.h +++ b/libc/include/time.h @@ -30,12 +30,14 @@ __BEGIN_DECLS @include(clock_t.h) +@include(clockid_t.h) @include(size_t.h) @include(pid_t.h) @include(time_t.h) @include(NULL.h) __END_DECLS +#include #include __BEGIN_DECLS diff --git a/libc/include/timespec.h b/libc/include/timespec.h new file mode 100644 index 00000000..3b274653 --- /dev/null +++ b/libc/include/timespec.h @@ -0,0 +1,101 @@ +/******************************************************************************* + + Copyright(C) Jonas 'Sortie' Termansen 2013. + + This file is part of the Sortix C Library. + + The Sortix C Library is free software: you can redistribute it and/or modify + it under the terms of the GNU Lesser General Public License as published by + the Free Software Foundation, either version 3 of the License, or (at your + option) any later version. + + The Sortix C Library is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY + or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public + License for more details. + + You should have received a copy of the GNU Lesser General Public License + along with the Sortix C Library. If not, see . + + timespec.h + Utility functions for manipulation of struct timespec. + +*******************************************************************************/ + +#ifndef INCLUDE_TIMESPEC_H +#define INCLUDE_TIMESPEC_H + +#include +#include + +__BEGIN_DECLS + +@include(time_t.h) + +__END_DECLS + +#include + +__BEGIN_DECLS + +static inline bool timespec_eq(struct timespec a, struct timespec b) +{ + return a.tv_sec == b.tv_sec && a.tv_nsec == b.tv_nsec; +} + +static inline bool timespec_neq(struct timespec a, struct timespec b) +{ + return a.tv_sec != b.tv_sec || a.tv_nsec != b.tv_nsec; +} + +static inline bool timespec_lt(struct timespec a, struct timespec b) +{ + return a.tv_sec < b.tv_sec || + (a.tv_sec == b.tv_sec && a.tv_nsec < b.tv_nsec); +} + +static inline bool timespec_le(struct timespec a, struct timespec b) +{ + return a.tv_sec < b.tv_sec || + (a.tv_sec == b.tv_sec && a.tv_nsec <= b.tv_nsec); +} + +static inline bool timespec_gt(struct timespec a, struct timespec b) +{ + return a.tv_sec > b.tv_sec || + (a.tv_sec == b.tv_sec && a.tv_nsec > b.tv_nsec); +} + +static inline bool timespec_ge(struct timespec a, struct timespec b) +{ + return a.tv_sec > b.tv_sec || + (a.tv_sec == b.tv_sec && a.tv_nsec >= b.tv_nsec); +} + +static inline struct timespec timespec_make(time_t sec, long nsec) +{ + struct timespec ret; + ret.tv_sec = sec; + ret.tv_nsec = nsec; + return ret; +} + +static inline struct timespec timespec_neg(struct timespec t) +{ + if ( t.tv_nsec ) + return timespec_make(-t.tv_sec - 1, 1000000000 - t.tv_nsec); + return timespec_make(-t.tv_sec, 0); +} + +static inline struct timespec timespec_nul() +{ + return timespec_make(0, 0); +} + +struct timespec timespec_canonalize(struct timespec t); +struct timespec timespec_add(struct timespec a, struct timespec b); +struct timespec timespec_sub(struct timespec a, struct timespec b); + +__END_DECLS + +#endif diff --git a/libc/timespec.cpp b/libc/timespec.cpp new file mode 100644 index 00000000..5984ddf9 --- /dev/null +++ b/libc/timespec.cpp @@ -0,0 +1,60 @@ +/******************************************************************************* + + Copyright(C) Jonas 'Sortie' Termansen 2013. + + This file is part of the Sortix C Library. + + The Sortix C Library is free software: you can redistribute it and/or modify + it under the terms of the GNU Lesser General Public License as published by + the Free Software Foundation, either version 3 of the License, or (at your + option) any later version. + + The Sortix C Library is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY + or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public + License for more details. + + You should have received a copy of the GNU Lesser General Public License + along with the Sortix C Library. If not, see . + + timespec.cpp + Utility functions for manipulation of struct timespec. + +*******************************************************************************/ + +#include + +static const long NANOSECONDS_PER_SECOND = 1000000000L; + +// TODO: The C modulo operator doesn't do exactly what we desire. +static long proper_modulo(long a, long b) +{ + if ( 0 <= a ) + return a % b; + long tmp = - (unsigned long) a % b; + return tmp ? tmp : 0; +} + +extern "C" struct timespec timespec_canonalize(struct timespec t) +{ + t.tv_sec += t.tv_nsec / NANOSECONDS_PER_SECOND; + t.tv_nsec = proper_modulo(t.tv_nsec, NANOSECONDS_PER_SECOND); + return t; +} + +extern "C" struct timespec timespec_add(struct timespec a, struct timespec b) +{ + struct timespec ret; + a = timespec_canonalize(a); + b = timespec_canonalize(b); + ret.tv_sec = a.tv_sec + b.tv_sec; + ret.tv_nsec = a.tv_nsec + b.tv_nsec; + return timespec_canonalize(ret); +} + +extern "C" struct timespec timespec_sub(struct timespec a, struct timespec b) +{ + a = timespec_canonalize(a); + b = timespec_canonalize(b); + return timespec_add(a, timespec_neg(b)); +} diff --git a/sortix/time.h b/sortix/include/sortix/clock.h similarity index 65% rename from sortix/time.h rename to sortix/include/sortix/clock.h index dd1703b1..181b8ba3 100644 --- a/sortix/time.h +++ b/sortix/include/sortix/clock.h @@ -1,6 +1,6 @@ /******************************************************************************* - Copyright(C) Jonas 'Sortie' Termansen 2011. + Copyright(C) Jonas 'Sortie' Termansen 2013. This file is part of Sortix. @@ -17,25 +17,23 @@ You should have received a copy of the GNU General Public License along with Sortix. If not, see . - time.h - Handles interrupts whenever some time has passed and provides useful + sortix/clock.h + Supported logical clock devices. *******************************************************************************/ -#ifndef SORTIX_TIME_H -#define SORTIX_TIME_H +#ifndef INCLUDE_SORTIX_CLOCK_H +#define INCLUDE_SORTIX_CLOCK_H -#include "cpu.h" +#include -namespace Sortix -{ - namespace Time - { - void Init(); - void OnIRQ0(CPU::InterruptRegisters* Registers, void* user); - float GetTimeSinceBoot(); - uintmax_t MicrosecondsSinceBoot(); - } -} +__BEGIN_DECLS + +#define CLOCK_REALTIME 0 /* Current real time. */ +#define CLOCK_MONOTONIC 1 /* Always increasing time. */ +#define CLOCK_BOOT 2 /* Time since system boot (uptime). */ +#define CLOCK_INIT 3 /* Time since 'init' process began. */ + +__END_DECLS #endif diff --git a/sortix/include/sortix/kernel/time.h b/sortix/include/sortix/kernel/time.h new file mode 100644 index 00000000..963b2726 --- /dev/null +++ b/sortix/include/sortix/kernel/time.h @@ -0,0 +1,47 @@ +/******************************************************************************* + + Copyright(C) Jonas 'Sortie' Termansen 2011, 2012, 2013. + + This file is part of Sortix. + + Sortix is free software: you can redistribute it and/or modify it under the + terms of the GNU General Public License as published by the Free Software + Foundation, either version 3 of the License, or (at your option) any later + version. + + Sortix is distributed in the hope that it will be useful, but WITHOUT ANY + WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS + FOR A PARTICULAR PURPOSE. See the GNU General Public License for more + details. + + You should have received a copy of the GNU General Public License along with + Sortix. If not, see . + + sortix/kernel/time.h + Retrieving the current time. + +*******************************************************************************/ + +#ifndef INCLUDE_SORTIX_KERNEL_TIME_H +#define INCLUDE_SORTIX_KERNEL_TIME_H + +#include + +#include + +#include + +#include + +namespace Sortix { +namespace Time { + +void Init(); +struct timespec Get(clockid_t clock, struct timespec* res = NULL); +bool TryGet(clockid_t clock, struct timespec* time, struct timespec* res = NULL); + + +} // namespace Time +} // namespace Sortix + +#endif diff --git a/sortix/include/sortix/x64/bits.h b/sortix/include/sortix/x64/bits.h index 2af5cf34..faa9fd1c 100644 --- a/sortix/include/sortix/x64/bits.h +++ b/sortix/include/sortix/x64/bits.h @@ -149,6 +149,7 @@ typedef unsigned int __nlink_t; typedef __uintmax_t __ino_t; typedef __uintptr_t __dev_t; typedef __intmax_t __clock_t; +typedef int __clockid_t; typedef long __time_t; typedef long __suseconds_t; diff --git a/sortix/include/sortix/x86/bits.h b/sortix/include/sortix/x86/bits.h index ca91a8f9..c9e92745 100644 --- a/sortix/include/sortix/x86/bits.h +++ b/sortix/include/sortix/x86/bits.h @@ -149,6 +149,7 @@ typedef unsigned int __nlink_t; typedef __uintmax_t __ino_t; typedef __uintptr_t __dev_t; typedef __intmax_t __clock_t; +typedef int __clockid_t; typedef long __time_t; typedef long __suseconds_t; diff --git a/sortix/kernel.cpp b/sortix/kernel.cpp index 851934fb..913ee543 100644 --- a/sortix/kernel.cpp +++ b/sortix/kernel.cpp @@ -43,6 +43,7 @@ #include #include #include +#include #include #include @@ -56,7 +57,6 @@ #include "kernelinfo.h" #include "x86-family/gdt.h" #include "x86-family/float.h" -#include "time.h" #include "multiboot.h" #include "thread.h" #include "process.h" diff --git a/sortix/scheduler.cpp b/sortix/scheduler.cpp index b5dd76da..b1e10b1d 100644 --- a/sortix/scheduler.cpp +++ b/sortix/scheduler.cpp @@ -22,17 +22,24 @@ *******************************************************************************/ -#include -#include -#include -#include +#include #include #include +#include + +#include +#include + +#include +#include +#include +#include +#include + #include "x86-family/gdt.h" #include "x86-family/float.h" -#include "time.h" #include "thread.h" #include "process.h" #include "signal.h" @@ -260,21 +267,27 @@ Thread::State GetThreadState(Thread* thread) return thread->state; } -int SysSleep(size_t secs) +static void SleepUntil(struct timespec wakeat) { - uintmax_t timetosleep = ((uintmax_t) secs) * 1000ULL * 1000ULL; - uint32_t wakeat = Time::MicrosecondsSinceBoot() + timetosleep; - do { Yield(); } - while ( Time::MicrosecondsSinceBoot() < wakeat ); + while ( timespec_lt(Time::Get(CLOCK_BOOT), wakeat) ) + Yield(); +} + +int sys_sleep(size_t secs) +{ + struct timespec delay = timespec_make(secs, 0); + struct timespec now = Time::Get(CLOCK_BOOT); + SleepUntil(timespec_add(now, delay)); return 0; } -int SysUSleep(size_t usecs) +int sys_usleep(size_t usecs) { - uintmax_t timetosleep = (uintmax_t) usecs; - uint32_t wakeat = Time::MicrosecondsSinceBoot() + timetosleep; - do { Yield(); } - while ( Time::MicrosecondsSinceBoot() < wakeat ); + size_t secs = usecs / 1000000; + size_t nsecs = (usecs % 1000000) * 1000; + struct timespec delay = timespec_make(secs, nsecs); + struct timespec now = Time::Get(CLOCK_BOOT); + SleepUntil(timespec_add(now, delay)); return 0; } @@ -304,8 +317,8 @@ void Init() Interrupt::RegisterRawHandler(132, thread_exit_handler, true); Interrupt::RegisterHandler(132, ThreadExitCPU, NULL); - Syscall::Register(SYSCALL_SLEEP, (void*) SysSleep); - Syscall::Register(SYSCALL_USLEEP, (void*) SysUSleep); + Syscall::Register(SYSCALL_SLEEP, (void*) sys_sleep); + Syscall::Register(SYSCALL_USLEEP, (void*) sys_usleep); } } // namespace Scheduler diff --git a/sortix/thread.cpp b/sortix/thread.cpp index a41ee25f..99b4733b 100644 --- a/sortix/thread.cpp +++ b/sortix/thread.cpp @@ -27,6 +27,7 @@ #include #include #include +#include #include #include @@ -38,7 +39,6 @@ #include "process.h" #include "thread.h" #include "scheduler.h" -#include "time.h" namespace Sortix { diff --git a/sortix/time.cpp b/sortix/time.cpp index d45fe711..529f5ec8 100644 --- a/sortix/time.cpp +++ b/sortix/time.cpp @@ -1,6 +1,6 @@ /******************************************************************************* - Copyright(C) Jonas 'Sortie' Termansen 2011, 2012. + Copyright(C) Jonas 'Sortie' Termansen 2011, 2012, 2013. This file is part of Sortix. @@ -23,11 +23,21 @@ *******************************************************************************/ +#include + +#include +#include + +#include +#include + #include #include +#include #include +#include +#include -#include "time.h" #include "process.h" #include "scheduler.h" #include "sound.h" @@ -36,87 +46,131 @@ #include "serialterminal.h" #endif +#include "cpu.h" + #if !defined(PLATFORM_X86_FAMILY) -#error No time subsystem is available for this CPU +#error Provide a time implementation for this CPU. #endif -namespace Sortix +namespace Sortix { +namespace Time { + +struct timespec since_boot; +struct timespec since_boot_period; +uint16_t since_boot_divisor; + +static uint16_t DivisorOfFrequency(long frequency) { - namespace Time + // The value we send to the PIT is the value to divide it's input clock + // (1193180 Hz) by, to get our required frequency. Note that the divisor + // must be small enough to fit into 16 bits. + return 1193180 / frequency; +} + +static long FrequencyOfDivisor(uint16_t divisor) +{ + return 1193180 / divisor; +} + +static long RealFrequencyOfFrequency(long frequency) +{ + return FrequencyOfDivisor(DivisorOfFrequency(frequency)); +} + +static struct timespec PeriodOfFrequency(long frequency) +{ + long period_ns = 1000000000L / frequency; + return timespec_make(0, period_ns); +} + +static void RequestIRQ0(uint16_t divisor) +{ + CPU::OutPortB(0x43, 0x36); + CPU::OutPortB(0x40, divisor >> 0 & 0xFF); + CPU::OutPortB(0x40, divisor >> 8 & 0xFF); +} + +static void InitializeTimer(long frequency) +{ + long real_frequence = RealFrequencyOfFrequency(frequency); + since_boot_divisor = DivisorOfFrequency(frequency); + since_boot = timespec_nul(); + since_boot_period = PeriodOfFrequency(real_frequence); + RequestIRQ0(since_boot_divisor); +} + +static void OnIRQ0(CPU::InterruptRegisters* regs, void* /*user*/) +{ + since_boot = timespec_add(since_boot, since_boot_period); + Scheduler::Switch(regs); + + // TODO: There is a horrible bug that causes Sortix to only receive + // one IRQ0 on my laptop, but it works in virtual machines. But + // re-requesting an addtional time seems to work. Hacky and ugly. + static bool did_ugly_irq0_hack = false; + if ( !did_ugly_irq0_hack ) { - uintmax_t ticks; - uintmax_t microsecondssinceboot; - - const uint32_t Frequency = 100; // 100 Hz - - uintmax_t MicrosecondsSinceBoot() - { - return microsecondssinceboot; - } - - extern "C" void RequestIRQ0() - { - // The value we send to the PIT is the value to divide it's input clock - // (1193180 Hz) by, to get our required frequency. Important to note is - // that the divisor must be small enough to fit into 16-bits. - const uint32_t Divisor = 1193180 / Frequency; - - // Send the command byte. - CPU::OutPortB(0x43, 0x36); - - // Divisor has to be sent byte-wise, so split here into upper/lower bytes. - uint8_t L = (uint8_t) (Divisor & 0xFF); - uint8_t H = (uint8_t) ((Divisor>>8) & 0xFF); - - // Send the frequency divisor. - CPU::OutPortB(0x40, L); - CPU::OutPortB(0x40, H); - } - - bool didUglyIRQ0Hack; - - int SysUptime(uintmax_t* usecssinceboot) - { - // TODO: Validate that usecssinceboot is a valid user-space pointer. - *usecssinceboot = microsecondssinceboot; - return 0; - } - - void Init() - { - // Initialize our variables. - ticks = 0; - microsecondssinceboot = 0; - - // First, register our timer callback. - Interrupt::RegisterHandler(Interrupt::IRQ0, &OnIRQ0, NULL); - - Syscall::Register(SYSCALL_UPTIME, (void*) SysUptime); - - didUglyIRQ0Hack = false; - - RequestIRQ0(); - } - - void OnIRQ0(CPU::InterruptRegisters* Regs, void* /*user*/) - { -#ifdef PLATFORM_SERIAL - SerialTerminal::OnTick(); -#endif - - ticks++; - microsecondssinceboot += (1000*1000)/Frequency; - - // Let the scheduler switch to the next task. - // TODO: Let the scheduler know how long has passed. - Scheduler::Switch(Regs); - - // TODO: There is a horrible bug that causes Sortix to only receive - // one IRQ0 on my laptop, but it works in virtual machines. But - // re-requesting an addtional time seems to work. Hacky and ugly. - if ( !didUglyIRQ0Hack ) { RequestIRQ0(); didUglyIRQ0Hack = true; } - } - - // TODO: Implement all the other useful functions regarding time. + RequestIRQ0(since_boot_divisor); + did_ugly_irq0_hack = true; } } + +bool TryGet(clockid_t clock, struct timespec* time, struct timespec* res) +{ + switch ( clock ) + { + case CLOCK_REALTIME: + return errno = ENOTSUP, false; + case CLOCK_MONOTONIC: + case CLOCK_BOOT: + case CLOCK_INIT: + // TODO: Is is possible to implement the below as atomic operations? + Interrupt::Disable(); + if ( time ) + *time = since_boot; + if ( res ) + *res = since_boot_period; + Interrupt::Enable(); + return true; + default: + return errno = EINVAL, false; + } +} + +struct timespec Get(clockid_t clock, struct timespec* res) +{ + struct timespec ret; + if ( !TryGet(clock, &ret, res) ) + return timespec_nul(); + return ret; +} + +uintmax_t MicrosecondsOfTimespec(struct timespec time) +{ + uintmax_t seconds = time.tv_sec; + uintmax_t nano_seconds = time.tv_nsec; + return seconds * 1000000 + nano_seconds / 1000; +} + +// TODO: This system call is for compatibility. Provide user-space with better +// facilities such as sys_clock_gettime. +static int sys_uptime(uintmax_t* usecssinceboot) +{ + struct timespec now; + if ( !TryGet(CLOCK_BOOT, &now, NULL) ) + return -1; + uintmax_t ret = MicrosecondsOfTimespec(now); + if ( !CopyToUser(usecssinceboot, &ret, sizeof(ret)) ) + return -1; + return 0; +} + +void Init() +{ + Interrupt::RegisterHandler(Interrupt::IRQ0, &OnIRQ0, NULL); + Syscall::Register(SYSCALL_UPTIME, (void*) sys_uptime); + InitializeTimer(100/*Hz*/); +} + +} // namespace Time +} // namespace Sortix