Fix time keeping, TSC ticks per second, and implement generic timer infrastructure.
This commit is contained in:
parent
64f2ca2259
commit
35b633417f
@ -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",
|
||||
|
@ -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();
|
||||
|
@ -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();
|
||||
}
|
||||
|
||||
|
@ -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);
|
||||
}
|
||||
|
||||
|
@ -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
24
sys/include/ktimer.h
Normal 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__ */
|
||||
|
@ -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
112
sys/kern/timer.c
Normal 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);
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user