Refactor kernel time API and add timespec API.

This commit is contained in:
Jonas 'Sortie' Termansen 2013-01-09 16:07:27 +01:00
parent 5424760719
commit 9ba7f26bf0
14 changed files with 398 additions and 116 deletions

View File

@ -108,6 +108,7 @@ strstr.o \
strtok.o \
strtok_r.o \
strxfrm.o \
timespec.o \
ungetc.o \
vfscanf.o \
vsscanf.o \

4
libc/decl/clockid_t.h Normal file
View File

@ -0,0 +1,4 @@
#ifndef _CLOCKID_T_DECL
#define _CLOCKID_T_DECL
typedef __clockid_t clockid_t;
#endif

View File

@ -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 */

View File

@ -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 <sortix/clock.h>
#include <sortix/timespec.h>
__BEGIN_DECLS

101
libc/include/timespec.h Normal file
View File

@ -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 <http://www.gnu.org/licenses/>.
timespec.h
Utility functions for manipulation of struct timespec.
*******************************************************************************/
#ifndef INCLUDE_TIMESPEC_H
#define INCLUDE_TIMESPEC_H
#include <features.h>
#include <stdbool.h>
__BEGIN_DECLS
@include(time_t.h)
__END_DECLS
#include <sortix/timespec.h>
__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

60
libc/timespec.cpp Normal file
View File

@ -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 <http://www.gnu.org/licenses/>.
timespec.cpp
Utility functions for manipulation of struct timespec.
*******************************************************************************/
#include <timespec.h>
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));
}

View File

@ -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 <http://www.gnu.org/licenses/>.
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 <features.h>
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

View File

@ -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 <http://www.gnu.org/licenses/>.
sortix/kernel/time.h
Retrieving the current time.
*******************************************************************************/
#ifndef INCLUDE_SORTIX_KERNEL_TIME_H
#define INCLUDE_SORTIX_KERNEL_TIME_H
#include <features.h>
#include <sys/types.h>
#include <stddef.h>
#include <sortix/timespec.h>
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

View File

@ -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;

View File

@ -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;

View File

@ -43,6 +43,7 @@
#include <sortix/kernel/keyboard.h>
#include <sortix/kernel/syscall.h>
#include <sortix/kernel/interrupt.h>
#include <sortix/kernel/time.h>
#include <sortix/fcntl.h>
#include <sortix/stat.h>
@ -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"

View File

@ -22,17 +22,24 @@
*******************************************************************************/
#include <sortix/kernel/platform.h>
#include <sortix/kernel/memorymanagement.h>
#include <sortix/kernel/syscall.h>
#include <sortix/kernel/interrupt.h>
#include <sys/types.h>
#include <assert.h>
#include <string.h>
#include <timespec.h>
#include <sortix/clock.h>
#include <sortix/timespec.h>
#include <sortix/kernel/platform.h>
#include <sortix/kernel/memorymanagement.h>
#include <sortix/kernel/syscall.h>
#include <sortix/kernel/interrupt.h>
#include <sortix/kernel/time.h>
#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

View File

@ -27,6 +27,7 @@
#include <sortix/kernel/memorymanagement.h>
#include <sortix/kernel/syscall.h>
#include <sortix/kernel/interrupt.h>
#include <sortix/kernel/time.h>
#include <sortix/mman.h>
#include <sortix/signal.h>
@ -38,7 +39,6 @@
#include "process.h"
#include "thread.h"
#include "scheduler.h"
#include "time.h"
namespace Sortix
{

View File

@ -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 <sys/types.h>
#include <errno.h>
#include <timespec.h>
#include <sortix/clock.h>
#include <sortix/timespec.h>
#include <sortix/kernel/platform.h>
#include <sortix/kernel/syscall.h>
#include <sortix/kernel/copy.h>
#include <sortix/kernel/interrupt.h>
#include <sortix/kernel/syscall.h>
#include <sortix/kernel/time.h>
#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