Fix time keeping, TSC ticks per second, and implement generic timer infrastructure.

This commit is contained in:
Ali Mashtizadeh 2015-01-16 14:32:47 -08:00
parent 64f2ca2259
commit 35b633417f
8 changed files with 178 additions and 58 deletions

View File

@ -54,6 +54,7 @@ src_common = [
"kern/syscall.c",
"kern/sysctl.c",
"kern/thread.c",
"kern/timer.c",
"kern/vfs.c",
"kern/vfsuio.c",
"dev/ahci.c",

View File

@ -23,12 +23,14 @@
#include "../dev/console.h"
extern void KTime_Init();
extern void Timer_Init();
extern void RTC_Init();
extern void PS2_Init();
extern void PCI_Init();
extern void IDE_Init();
extern void MachineBoot_AddMem();
extern void Loader_LoadInit();
extern void PAlloc_LateInit();
#define GDT_MAX 8
@ -138,6 +140,7 @@ void Machine_Init()
MachineBoot_AddMem();
KTime_Init();
RTC_Init(); // Finishes initializing KTime
IRQ_Init();
LAPIC_Init();
@ -145,7 +148,7 @@ void Machine_Init()
IOAPIC_Enable(0); // Enable timer interrupts
Thread_Init();
RTC_Init();
Timer_Init(); // Depends on RTC and KTime
PS2_Init();
PCI_Init();

View File

@ -20,6 +20,7 @@
extern uint64_t trap_table[T_MAX];
extern void trap_pop(TrapFrame *tf);
extern void Debug_Breakpoint(TrapFrame *tf);
extern void Timer_Process();
static InteruptGate64 idt[256];
static PseudoDescriptor idtdesc;
@ -177,7 +178,7 @@ trap_entry(TrapFrame *tf)
LAPIC_SendEOI();
IRQ_Handler(tf->vector - T_IRQ_BASE);
if (tf->vector == T_IRQ_TIMER) {
KTime_Tick(100); // XXX: 100 Hz & Only on CPU0
Timer_Process();
Thread_Scheduler();
}

View File

@ -17,12 +17,35 @@
#define RTC_MONTH 0x08
#define RTC_YEAR 0x09
void RTC_ReadTime();
UnixEpoch RTC_ReadTime();
void
RTC_Init()
{
RTC_ReadTime();
uint64_t startTSC, stopTSC;
UnixEpoch first, second;
first = RTC_ReadTime();
while (1) {
second = RTC_ReadTime();
if (first != second)
break;
first = second;
}
startTSC = Time_GetTSC();
first = RTC_ReadTime();
while (1) {
second = RTC_ReadTime();
if (first != second)
break;
first = second;
}
stopTSC = Time_GetTSC();
kprintf("RTC: %lld Ticks Per Second: %lld\n", second, stopTSC - startTSC);
KTime_SetTime(second, stopTSC, stopTSC - startTSC);
}
static inline uint8_t
@ -32,7 +55,7 @@ RTC_ReadReg(uint8_t reg)
return inb(0x71);
}
void
UnixEpoch
RTC_ReadTime()
{
KTime tm;
@ -72,7 +95,7 @@ RTC_ReadTime()
tm.wday -= 1;
tm.month -= 1;
KTime_SetTime(&tm);
KTime_ToEpoch(&tm);
// KTime_SetTime(&tm);
return KTime_ToEpoch(&tm);
}

View File

@ -21,7 +21,7 @@ typedef uint64_t UnixEpochNS;
void KTime_Fixup(KTime *tm);
UnixEpoch KTime_ToEpoch(const KTime *tm);
void KTime_FromEpoch(UnixEpoch time, KTime *tm);
void KTime_SetTime(const KTime *tm);
void KTime_SetTime(UnixEpoch epoch, uint64_t tsc, uint64_t tps);
void KTime_Tick(int rate);
UnixEpoch KTime_GetEpoch();
UnixEpochNS KTime_GetEpochNS();

24
sys/include/ktimer.h Normal file
View File

@ -0,0 +1,24 @@
#ifndef __SYS_KTIMER_H__
#define __SYS_KTIMER_H__
#include <sys/queue.h>
typedef void (*TimerCB)(void *);
typedef struct TimerEvent {
uint64_t refCount;
uint64_t timeout;
TimerCB cb;
void *arg;
LIST_ENTRY(TimerEvent) timerQueue;
} TimerEvent;
TimerEvent *Timer_Create(uint64_t timeout, TimerCB cb, void *arg);
void Timer_Retain(TimerEvent *evt);
void Timer_Release(TimerEvent *evt);
void Timer_Cancel(TimerEvent *evt);
void Timer_Process();
#endif /* __SYS_KTIMER_H__ */

View File

@ -11,10 +11,6 @@ static Spinlock ktimeLock;
static uint64_t ktimeLastEpoch;
static uint64_t ktimeLastTSC;
uint64_t ticksPerSecond;
static uint64_t ticksPrevTSC;
static uint64_t ticksCount;
// Stable Value
static uint64_t ticksPerSecondStable;
static const char *dayOfWeek[7] = { "Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat" };
static const char *months[12] = {
@ -29,9 +25,6 @@ KTime_Init()
ktimeLastEpoch = 0;
ktimeLastTSC = 0;
ticksPerSecond = 0;
ticksPrevTSC = 0;
ticksCount = 0;
ticksPerSecondStable = 0;
}
static bool
@ -163,11 +156,12 @@ KTime_FromEpoch(UnixEpoch epoch, KTime *tm)
}
void
KTime_SetTime(const KTime *tm)
KTime_SetTime(UnixEpoch epoch, uint64_t tsc, uint64_t tps)
{
Spinlock_Lock(&ktimeLock);
ktimeLastEpoch = KTime_ToEpoch(tm);
ktimeLastTSC = Time_GetTSC();
ktimeLastEpoch = epoch;
ktimeLastTSC = tsc;
ticksPerSecond = tps;
Spinlock_Unlock(&ktimeLock);
}
@ -186,10 +180,7 @@ KTime_GetEpoch()
Spinlock_Lock(&ktimeLock);
tscDiff = Time_GetTSC() - ktimeLastTSC;
if (ticksPerSecondStable)
epoch = ktimeLastEpoch + tscDiff / ticksPerSecondStable;
else
epoch = ktimeLastEpoch + tscDiff / ticksPerSecond;
epoch = ktimeLastEpoch + tscDiff / ticksPerSecond;
Spinlock_Unlock(&ktimeLock);
return epoch;
@ -203,45 +194,12 @@ KTime_GetEpochNS()
Spinlock_Lock(&ktimeLock);
tscDiff = Time_GetTSC() - ktimeLastTSC;
if (ticksPerSecondStable)
epoch = ktimeLastEpoch * 1000000000
+ tscDiff * 1000000000 / ticksPerSecondStable;
else
epoch = ktimeLastEpoch * 1000000000
+ tscDiff * 1000000000 / ticksPerSecond;
epoch = ktimeLastEpoch * 1000000000 + tscDiff * 1000000000 / ticksPerSecond;
Spinlock_Unlock(&ktimeLock);
return epoch;
}
void
KTime_Tick(int rate)
{
uint64_t now;
uint64_t tps;
if (ticksPrevTSC == 0) {
ticksPrevTSC = Time_GetTSC();
return;
}
ticksCount += 1;
now = Time_GetTSC();
tps = rate * (now - ticksPrevTSC);
if (ticksPerSecond == 0)
ticksPerSecond = tps;
else
ticksPerSecond = (tps + ticksCount * ticksPerSecond) / (ticksCount + 1);
ticksPrevTSC = now;
// So time doesn't change let's just pick an estimate within a few seconds
// of booting.
if (ticksCount == 1000) {
ticksPerSecondStable = ticksPerSecond;
}
}
void
Debug_Date()
{
@ -263,8 +221,6 @@ void
Debug_Ticks()
{
kprintf("Ticks Per Second: %lu\n", ticksPerSecond);
if (ticksPerSecondStable)
kprintf("Ticks Per Second (Stable): %lu\n", ticksPerSecondStable);
}
REGISTER_DBGCMD(ticks, "Print ticks per second", Debug_Ticks);

112
sys/kern/timer.c Normal file
View File

@ -0,0 +1,112 @@
#include <stddef.h>
#include <stdbool.h>
#include <stdint.h>
#include <sys/kassert.h>
#include <sys/queue.h>
#include <sys/spinlock.h>
#include <sys/kmem.h>
#include <sys/ktime.h>
#include <sys/ktimer.h>
#define TIMER_WHEEL_LENGTH 256
int timerHead = 0;
uint64_t timerNow = 0;
LIST_HEAD(TimerWheelHead, TimerEvent) timerSlot[TIMER_WHEEL_LENGTH];
Spinlock timerLock;
Slab timerSlab;
DEFINE_SLAB(TimerEvent, &timerSlab);
void
Timer_Init()
{
int i;
Spinlock_Init(&timerLock, "Timer Lock");
Slab_Init(&timerSlab, "TimerEvent Slab", sizeof(TimerEvent), 16);
// Initialize wheel
timerHead = 0;
timerNow = KTime_GetEpoch();
for (i = 0; i < TIMER_WHEEL_LENGTH; i++) {
LIST_INIT(&timerSlot[i]);
}
}
TimerEvent *
Timer_Create(uint64_t timeout, TimerCB cb, void *arg)
{
int slot;
TimerEvent *evt = TimerEvent_Alloc();
evt->refCount = 2; // One for the wheel and one for the callee
evt->timeout = timerNow + timeout;
evt->cb = cb;
evt->arg = arg;
Spinlock_Lock(&timerLock);
slot = (timerHead + timeout) % TIMER_WHEEL_LENGTH;
// XXX: should insert into tail
LIST_INSERT_HEAD(&timerSlot[slot], evt, timerQueue);
Spinlock_Unlock(&timerLock);
return evt;
}
void
Timer_Retain(TimerEvent *evt)
{
ASSERT(evt->refCount != 0);
__sync_fetch_and_add(&evt->refCount, 1);
}
void
Timer_Release(TimerEvent *evt)
{
ASSERT(evt->refCount != 0);
if (__sync_fetch_and_sub(&evt->refCount, 1) == 1) {
TimerEvent_Free(evt);
}
}
void
Timer_Cancel(TimerEvent *evt)
{
Spinlock_Lock(&timerLock);
LIST_REMOVE(evt, timerQueue);
Timer_Release(evt);
Spinlock_Unlock(&timerLock);
}
void
Timer_Process()
{
uint64_t now = KTime_GetEpoch();
Spinlock_Lock(&timerLock);
while (now > timerNow) {
TimerEvent *it, *tmp;
// Dispatch pending timer events
LIST_FOREACH_SAFE(it, &timerSlot[timerHead], timerQueue, tmp) {
if (it->timeout <= now) {
(it->cb)(it->arg);
LIST_REMOVE(it, timerQueue);
Timer_Release(it);
}
}
// Rotate wheel forward
timerNow = timerNow + 1;
timerHead = (timerHead + 1) % TIMER_WHEEL_LENGTH;
}
Spinlock_Unlock(&timerLock);
}