📄 kern_systimer.cxx
字号:
/* * Copyright (C) 1998, 1999, Jonathan S. Shapiro. * * This file is part of the EROS Operating System. * * This program 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 2, * or (at your option) any later version. * * This program 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 this program; if not, write to the Free Software * Foundation, 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */#include <kerninc/kernel.hxx>#include <kerninc/IRQ.hxx>#include <kerninc/Machine.hxx>#include <kerninc/SysTimer.hxx>#include <kerninc/Thread.hxx>#include <kerninc/Task.hxx>#include <kerninc/ObjectCache.hxx>#include <eros/TimePage.h>#include <eros/TimeOfDay.h>#include <kerninc/PhysMem.hxx>/* SysTimer is a very special sort of device, in that it is only KIND * of an interrupt handler. When the system hardware clock is * configured, it gives SysTimer::Wakeup() as the routine to call when * the clock interrupt occurs. That's not really what happens. * * The clock interrupt needs to be fast when there is nothing to do, * so there is a machine specific fast path interrupt handler for the * clock interrupt that is written in assembly language. When the * clock interrupt goes off, the fast path handler increments the * value of 'now'. It then checks to see if wakeup > now, in which * case there is nothing to wake up. If that condition holds, the * fast path handler returns from the interrupt immediately WITHOUT * running the general interrupt dispatch logic. * * The end effect is that SysTimer::Wakeup() is only called when there * is really something to wake up. *//* For EROS, the desired timer granularity is milliseconds. * Unfortunately this is very much too fast for some hardware. The * rule is that you will sleep for as long as you asked for or one * hardclock tick, whichever is LONGER. Also, you will NEVER sleep * for more than a year. */#if 0 uint64_t wakeTime; /* if asleep, when to wake up, in milliseconds */ Thread *nextTimer; void (*wakeFun)(); SleepFor(uint64_t ms, void (*wakeFun)()); Disarm();#endifstruct Thread *ThreadChain = 0;struct Timer *TimerChain = 0;volatile uint64_t SysTimer::now = 0llu;volatile uint64_t SysTimer::wakeup = ~(0llu);voidSysTimer::ResetWakeTime(){ wakeup = ~(0llu); if (TimerChain && TimerChain->wakeTime < wakeup) wakeup = TimerChain->wakeTime; if (ThreadChain && ThreadChain->wakeTime < wakeup) wakeup = ThreadChain->wakeTime;}voidSysTimer::Wakeup(class fixregs_t *sa){ IRQ::DISABLE(); #if 0 extern intDepth; printf("Wakeup() at intDepth %d, sleepers? %c\n", intDepth, ThreadChain ? 'y' : 'n');#endif#if 0 printf("SysTimer::Tick() resets waketime at %d\n", (long) now);#endif wakeup = ~(0llu); /* essentially never */ if (CpuReserve::Current->expired) CpuReserve::Current->OnQuantaExpiration(); /* The awkward loop must be used because calling t->wakeup() * mutates the sleeper list. */ while (ThreadChain && ThreadChain->wakeTime <= now) { register Thread *t = ThreadChain; ThreadChain = ThreadChain->nextTimedThread;#if 0 if ( t->IsUser() ) printf("wake sleeper; now %u waketime %u nextwake %u\n", (uint32_t) now, (uint32_t)t->wakeTime, (uint32_t) wakeup);#endif t->Wakeup(); } while (TimerChain && TimerChain->wakeTime <= now) { register Timer *t = TimerChain; TimerChain = TimerChain->nextTimer; t->wakeFun(t); } ResetWakeTime(); IRQ::ENABLE(); IRQ::Enable(IRQ_FROM_EXCEPTION(sa->ExceptNo));}voidSysTimer::AddSleeper(Thread& t){ IRQ::DISABLE(); if (ThreadChain == 0 || t.wakeTime < ThreadChain->wakeTime) { t.nextTimedThread = ThreadChain; ThreadChain = &t; ResetWakeTime(); } else { Thread **sleeper = &(ThreadChain->nextTimedThread); while (*sleeper && (*sleeper)->wakeTime < t.wakeTime) sleeper = &((*sleeper)->nextTimedThread); /* We are either off the end of the list or looking at one whose * wakeup value is greater than ours: */ t.nextTimedThread = *sleeper; *sleeper = &t; }#if 1 /* Sanity check. We know there is at least 1 thread on the list. */ register Thread *cur = ThreadChain; do { register Thread *next = cur->nextTimedThread; if (next) assert(cur->wakeTime <= next->wakeTime); cur = next; } while(cur);#endif #if 0 if ( t.IsUser() ) printf("added sleeper; now %u waketime %u nextwake %u\n", (uint32_t) now, (uint32_t)t.wakeTime, (uint32_t) wakeup);#endif IRQ::ENABLE();}voidSysTimer::CancelAlarm(Thread& t){ IRQ::DISABLE();#if 0 printf("Canceling alarm on thread 0x%x\n", &t);#endif if (ThreadChain == &t) { ThreadChain = t.nextTimedThread; ResetWakeTime(); } else { for ( Thread *sleeper = ThreadChain; sleeper; sleeper = sleeper->nextTimedThread ) { if (sleeper->nextTimedThread == &t) { sleeper->nextTimedThread = t.nextTimedThread; break; } } } IRQ::ENABLE();}voidSysTimer::AddTimer(Timer& t){ IRQ::DISABLE(); if (TimerChain == 0 || t.wakeTime < TimerChain->wakeTime) {#if 0 printf("Insert first timer. nxt=0x%08x tnxt=0x%08x\n", TimerChain, t.nextTimer);#endif t.nextTimer = TimerChain; TimerChain = &t; ResetWakeTime(); } else { Timer **sleeper = &(TimerChain->nextTimer); while (*sleeper && (*sleeper)->wakeTime < t.wakeTime) sleeper = &((*sleeper)->nextTimer); /* We are either off the end of the list or looking at one whose * wakeup value is greater than ours: */ t.nextTimer = *sleeper; *sleeper = &t; }#if 1 /* Sanity check. We know there is at least 1 thread on the list. */ register Timer *cur = TimerChain; do { register Timer *next = cur->nextTimer; if (next) assert(cur->wakeTime <= next->wakeTime); cur = next; } while(cur);#endif #if 0 printf("added timer; now %u waketime %u nextwake %u\n", (uint32_t) now, (uint32_t)t.wakeTime, (uint32_t) wakeup);#endif IRQ::ENABLE();}voidSysTimer::CancelTimer(Timer& t){ IRQ::DISABLE(); if (t.wakeTime == 0ll) { IRQ::ENABLE(); return; } #if 0 printf("Canceling alarm on timer 0x%x\n", &t);#endif if (TimerChain == &t) {#if 0 printf("Remove first timer. nxt=0x%08x\n", t.nextTimer);#endif TimerChain = t.nextTimer; ResetWakeTime(); } else { for (Timer *sleeper = TimerChain; sleeper; sleeper = sleeper->nextTimer) { if (sleeper->nextTimer == &t) { sleeper->nextTimer = t.nextTimer; break; } } } t.wakeTime = 0ll; t.nextTimer = 0; IRQ::ENABLE();}voidTimer::WakeupIn(uint64_t ms, void (*wkfn)(Timer*)){ wakeTime = SysTimer::Now() + Machine::MillisecondsToTicks(ms); wakeFun = wkfn; SysTimer::AddTimer(*this);}voidTimer::WakeupAt(uint64_t ms, void (*wkfn)(Timer*)){ wakeTime = Machine::MillisecondsToTicks(ms); wakeFun = wkfn; SysTimer::AddTimer(*this);}voidTimer::WakeupAtTick(uint64_t tick, void (*wkfn)(Timer*)){ wakeTime = tick; wakeFun = wkfn; SysTimer::AddTimer(*this);}/************************************************** * SUPPORT FOR THE TIME PAGE **************************************************/static volatile TimePageStruct *eros_tod_struct = 0;ObjectHeader *SysTimer::TimePageHdr = 0;Timer TimePageTimer;struct timeval wallBase;void TimePageTick(Timer *){ uint64_t timenow = SysTimer::Now(); timenow = Machine::TicksToMilliseconds(timenow); uint32_t now_secs = timenow/1000; uint32_t now_usecs = timenow % 1000; IRQ::DISABLE(); eros_tod_struct->tps_sinceboot.tv_secs = now_secs; eros_tod_struct->tps_sinceboot.tv_usecs = now_usecs; /* Time of day init zeroed usecs, so no need to do that arithmetic. */ eros_tod_struct->tps_wall.tv_secs = wallBase.tv_secs + now_secs; eros_tod_struct->tps_wall.tv_usecs = now_usecs; IRQ::ENABLE(); TimePageTimer.WakeupIn(5ul, TimePageTick);}#define HRS_PER_DAY 24#define MINS_PER_HR 60#define SECS_PER_MIN 60#define SECS_PER_HR (SECS_PER_MIN * MINS_PER_HR)#define SECS_PER_DAY (HRS_PER_DAY * SECS_PER_HR)/* Remember that this runs before the first thread, so must not yield. */voidSysTimer::BootInit(){}voidSysTimer::InitTimePage(){ printf("Fabricating TOD Page\n"); TimePageHdr = ObjectCache::GrabPageFrame(); eros_tod_struct = (TimePageStruct *) ObjectCache::ObHdrToPage(TimePageHdr); eros_tod_struct->tps_version = TIMEPAGE_VERSION; eros_tod_struct->tps_sinceboot.tv_secs = 0; eros_tod_struct->tps_sinceboot.tv_usecs = 0; eros_tod_struct->tps_wall.tv_secs = 0; eros_tod_struct->tps_wall.tv_usecs = 0; TimeOfDay tod; Machine::GetHardwareTimeOfDay(tod); wallBase.tv_usecs = 0; wallBase.tv_secs = tod.utcDay * SECS_PER_DAY; TimePageTimer.WakeupIn(5ul, TimePageTick);}
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -