The Pedigree Project 0.1
hosted/Timer.cc
1/*
2 * Copyright (c) 2008-2014, Pedigree Developers
3 *
4 * Please see the CONTRIB file in the root of the source tree for a full
5 * list of contributors.
6 *
7 * Permission to use, copy, modify, and distribute this software for any
8 * purpose with or without fee is hereby granted, provided that the above
9 * copyright notice and this permission notice appear in all copies.
10 *
11 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
12 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
13 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
14 * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
15 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
16 * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
17 * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
18 */
19
20#include "Timer.h"
21#include "pedigree/kernel/LockGuard.h"
22#include "pedigree/kernel/TargetInfo.h"
23#include "pedigree/kernel/compiler.h"
24#include "pedigree/kernel/core/SlamAllocator.h"
25#include "pedigree/kernel/machine/Machine.h"
26#include "pedigree/kernel/machine/Serial.h"
27#include "pedigree/kernel/machine/TimerHandler.h"
28#include "pedigree/kernel/process/Event.h"
29#include "pedigree/kernel/process/Scheduler.h"
30#include "pedigree/kernel/process/Thread.h"
31#include "pedigree/kernel/processor/Processor.h"
32#include "pedigree/kernel/processor/state.h"
33
34#include <errno.h>
35#include <stdio.h>
36
37// Millisecond interval (tick every ms)
38#define INTERVAL 1000000
39
40// Set by PhysicalMemoryManager.
41extern size_t g_FreePages;
42extern size_t g_AllocedPages;
43
44static uint64_t addAlarmDuration(uint64_t deadline, size_t count, uint64_t multiplier) {
45 if (count > ((Time::Infinity - deadline) / multiplier)) {
46 return Time::Infinity;
47 }
48 return deadline + (count * multiplier);
49}
50
52
53#if HOSTED && PEDIGREE_HOSTED_SMOKE_TESTS
54HostedTimer::AlarmSendAdmissionHook HostedTimer::m_AlarmSendAdmissionHook = nullptr;
55
56bool HostedTimer::setSignalIntervalForTest(uint64_t nanoseconds) {
57 if (!m_Instance.m_bInitialized || !nanoseconds) {
58 return false;
59 }
60
61 return m_Instance.m_TickSource.arm(nanoseconds);
62}
63#endif
64
68
69void HostedTimer::addAlarm(Event* pEvent, size_t alarmSecs, size_t alarmUsecs) {
71 uint64_t deadline = getTickCountNano();
72 deadline = addAlarmDuration(deadline, alarmSecs, Time::Multiplier::Second);
73 deadline = addAlarmDuration(deadline, alarmUsecs, Time::Multiplier::Microsecond);
74 Alarm* pAlarm = new Alarm(pEvent, deadline, Processor::information().getCurrentThread());
75 m_Alarms.pushBack(pAlarm);
76}
77
80 for (List<Alarm*>::Iterator it = m_Alarms.begin(); it != m_Alarms.end(); ++it) {
81 if ((*it)->m_pEvent == pEvent) {
82 Alarm* alarm = *it;
83 m_Alarms.erase(it);
84 delete alarm;
85 return;
86 }
87 }
88}
89
90size_t HostedTimer::removeAlarm(class Event* pEvent, bool bRetZero) {
92 const uint64_t currTime = getTickCountNano();
93
94 for (List<Alarm*>::Iterator it = m_Alarms.begin(); it != m_Alarms.end(); ++it) {
95 if ((*it)->m_pEvent == pEvent) {
96 Alarm* alarm = *it;
97 size_t ret = 0;
98 if (!bRetZero) {
99 size_t alarmEndTime = alarm->m_Time;
100
101 // Is it later than the end of the alarm?
102 if (alarmEndTime < currTime)
103 ret = 0;
104 else {
105 const uint64_t diff = alarmEndTime - currTime;
106 ret = diff / Time::Multiplier::Second;
107 if (diff % Time::Multiplier::Second) {
108 ++ret;
109 }
110 }
111 }
112
113 m_Alarms.erase(it);
114 delete alarm;
115 return ret;
116 }
117 }
118
119 return 0;
120}
121
122bool HostedTimer::registerHandler(TimerHandler* handler) {
123 return m_HandlerRegistry.registerHandler(handler);
124}
125
126bool HostedTimer::unregisterHandler(TimerHandler* handler) {
127 return m_HandlerRegistry.unregisterHandler(handler);
128}
129
130#if HOSTED && PEDIGREE_HOSTED_SMOKE_TESTS
131void HostedTimer::setHandlerPinHook(HandlerPinHook hook) {
132 m_Instance.m_HandlerRegistry.setHandlerPinHook(hook);
133}
134
135void HostedTimer::setHandlerPrePinHook(HandlerPrePinHook hook) {
136 m_Instance.m_HandlerRegistry.setHandlerPrePinHook(hook);
137}
138
139void HostedTimer::setHandlerHazardClaimHook(HandlerHazardClaimHook hook) {
140 m_Instance.m_HandlerRegistry.setHandlerHazardClaimHook(hook);
141}
142
143void HostedTimer::setHandlerAtomicDrainHook(HandlerAtomicDrainHook hook) {
144 m_Instance.m_HandlerRegistry.setHandlerAtomicDrainHook(hook);
145}
146
147void HostedTimer::withHandlerMutationLockForTest(HandlerMutationLockHook hook) {
148 m_Instance.m_HandlerRegistry.withMutationLockForTest(hook);
149}
150
151bool HostedTimer::dispatchHandlerForTest(TimerHandler* handler, uint64_t delta) {
152 return m_Instance.m_HandlerRegistry.dispatch(delta, handler);
153}
154
155size_t HostedTimer::activeDispatchCountForTest(TimerHandler* handler) {
156 return m_Instance.m_HandlerRegistry.activeDispatchCountForTest(handler);
157}
158
159size_t HostedTimer::claimedDispatchCountForTest() {
160 return m_Instance.m_HandlerRegistry.claimedDispatchCountForTest();
161}
162
163void HostedTimer::setAlarmSendAdmissionHookForTest(AlarmSendAdmissionHook hook) {
164 __atomic_store_n(&m_AlarmSendAdmissionHook, hook, __ATOMIC_RELEASE);
165}
166
167bool HostedTimer::alarmLockHeldForTest() {
168 return m_Instance.m_AlarmLock.acquired();
169}
170#endif
171
173 return m_Year;
174}
175
177 return m_Month;
178}
179
181 return m_DayOfMonth;
182}
183
185 return m_DayOfWeek;
186}
187
189 return m_Hour;
190}
191
193 return m_Minute;
194}
195
197 return m_Second;
198}
199
201 return m_Nanosecond;
202}
203
205 return getTickCountNano() / Time::Multiplier::Millisecond;
206}
207
209 struct timespec tv;
210 clock_gettime(CLOCK_MONOTONIC, &tv);
211 return (tv.tv_sec * 1000000000ULL) + (tv.tv_nsec);
212}
213
215 assert(!m_bInitialized && !m_bPrepared);
216
217 synchronise();
218
220
221 if (!m_TickSource.prepare(SIGUSR1, this)) {
222 return false;
223 }
224
225 m_bPrepared = true;
226 return true;
227}
228
230 if (!m_bPrepared || m_bInitialized) {
231 return false;
232 }
233
234 m_PendingExpirations.reset();
235 if (!initialiseSplitIrq()) {
236 m_TickSource.destroy();
237 m_bPrepared = false;
238 return false;
239 }
240
241 IrqManager& irqManager = *Machine::instance().getIrqManager();
242 m_IrqId = registerIsaSplitIrq(irqManager, 0, IrqPolicy::syntheticHard());
243 if (!m_IrqId) {
244 if (!shutdownSplitIrq()) {
245 FATAL(
246 "HostedTimer could not stop its unregistered bottom-half "
247 "worker");
248 }
249 m_TickSource.destroy();
250 m_bPrepared = false;
251 return false;
252 }
253
254 if (!m_TickSource.arm(INTERVAL)) {
255 if (!shutdownSplitIrq()) {
256 FATAL("HostedTimer could not stop after timer arming failed");
257 }
258 m_IrqId = 0;
259 m_TickSource.destroy();
260 m_bPrepared = false;
261 return false;
262 }
263
264 m_bInitialized = true;
265 return true;
266}
267
269 if (tohw)
270 return;
271
272 struct timespec tv;
273 clock_gettime(CLOCK_REALTIME, &tv);
274 struct tm conv;
275 struct tm* t = gmtime_r(&tv.tv_sec, &conv);
276 assert(t != NULL);
277
278 m_Nanosecond = tv.tv_nsec;
279 m_Second = t->tm_sec;
280 m_Minute = t->tm_min;
281 m_Hour = t->tm_hour;
282 m_DayOfMonth = t->tm_mday;
283 m_Month = t->tm_mon + 1;
284 m_Year = t->tm_year + 1900; // Years since 1900.
285 m_DayOfWeek = t->tm_wday;
286}
287
289 if (!m_bPrepared) {
290 return;
291 }
292
293 if (m_bInitialized) {
294 if (!shutdownSplitIrq()) {
295 FATAL("HostedTimer teardown could not drain its split IRQ worker");
296 }
297 m_IrqId = 0;
298 m_bInitialized = false;
299 }
300
301 m_TickSource.destroy();
302 m_bPrepared = false;
303
304 synchronise();
305
306 {
308 for (List<Alarm*>::Iterator it = m_Alarms.begin(); it != m_Alarms.end(); ++it) {
309 delete *it;
310 }
311 m_Alarms.clear();
312 }
313
315}
316
318 : SplitIrqHandler(MakeConstantString("Hosted timer bottom half")),
319 m_Year(0),
320 m_Month(0),
321 m_DayOfMonth(0),
322 m_DayOfWeek(0),
323 m_Hour(0),
324 m_Minute(0),
325 m_Second(0),
326 m_Nanosecond(0),
327 m_TickSource(),
328 m_IrqId(0),
329 m_PendingExpirations(),
330 m_HandlerRegistry(),
331 m_Alarms(),
332 m_AlarmLock(false) {}
333
334SplitIrqHandler::HardStageDisposition HostedTimer::hardIrq(irq_id_t number, InterruptState& state,
335 size_t& work) {
336 const siginfo_t* signalInfo = reinterpret_cast<const siginfo_t*>(state.getRegister(1));
337 if (number != 0 || state.getInterruptNumber() != SIGUSR1) {
338 return HardStageDisposition::NotHandled;
339 }
340
341 size_t expirations = 0;
342 const HostedTickSource::TakeResult result = m_TickSource.takeExpirations(signalInfo, expirations);
343 if (result == HostedTickSource::TakeResult::NotSource) {
344 return HardStageDisposition::NotHandled;
345 }
346 if (result == HostedTickSource::TakeResult::Invalid) {
347 FATAL_NOLOCK("HostedTimer received invalid tick-source metadata");
348 return HardStageDisposition::Handled;
349 }
350 if (!expirations) {
351 return HardStageDisposition::Handled;
352 }
353
354 if (!m_PendingExpirations.recordFromInterrupt(expirations)) {
355 FATAL_NOLOCK("HostedTimer expiration counter saturated");
356 return HardStageDisposition::Handled;
357 }
358
359 work = 1;
360 return HardStageDisposition::Deferred;
361}
362
363void HostedTimer::threadedIrq(size_t work) {
364 if (!work) {
365 return;
366 }
367
368 const size_t expirations = m_PendingExpirations.takeAll();
369 if (!expirations) {
370 return;
371 }
372
373 constexpr uint64_t MaximumDelta = ~static_cast<uint64_t>(0);
374 if (expirations > (MaximumDelta / INTERVAL)) {
375 FATAL("HostedTimer elapsed-time batch overflowed");
376 return;
377 }
378 const uint64_t delta = static_cast<uint64_t>(expirations) * INTERVAL;
379 processTimerBatch(delta);
380}
381
383 return m_TickSource.disarm();
384}
385
387 // The POSIX periodic timer remains armed after each delivered signal.
388 (void)work;
389}
390
391void HostedTimer::processTimerBatch(uint64_t delta) {
392 const bool elapsedSecond =
393 delta >= Time::Multiplier::Second || m_Nanosecond >= (Time::Multiplier::Second - delta);
394 if (elapsedSecond) {
395 synchronise();
396 } else {
397 m_Nanosecond += delta;
398 }
399
400 // Check for alarms.
402 uint64_t tickCount = getTickCountNano();
403 while (true) {
404 bool bDispatched = false;
405 for (List<Alarm*>::Iterator it = m_Alarms.begin(); it != m_Alarms.end(); it++) {
406 Alarm* pA = *it;
407 if (pA->m_Time <= tickCount) {
408 // Cancellation must observe either a queued alarm it can
409 // remove or an Event whose send admission has completed.
410 // Thread::sendEvent only publishes a ready target; it does not
411 // run that target synchronously, so this lock can cover the
412 // complete ownership handoff without a self-drain path.
413#if HOSTED && PEDIGREE_HOSTED_SMOKE_TESTS
414 AlarmSendAdmissionHook hook = __atomic_load_n(&m_AlarmSendAdmissionHook, __ATOMIC_ACQUIRE);
415 if (hook) {
416 hook(pA->m_pEvent);
417 }
418#endif
419 pA->m_pThread->sendEvent(pA->m_pEvent);
420 m_Alarms.erase(it);
421 delete pA;
422 bDispatched = true;
423 break;
424 }
425 }
426 if (!bDispatched)
427 break;
428 }
430
431 // Each batch represents at least one millisecond. Apply any interrupt
432 // mitigation policy once after all elapsed time has been captured.
433 Machine::instance().getIrqManager()->tick();
434
435 if (UNLIKELY(elapsedSecond)) {
436#if MEMORY_LOGGING_ENABLED
437 Serial* pSerial = Machine::instance().getSerial(1);
439 str += "Heap: ";
440 str += (SlamAllocator::instance().heapPageCount() * TargetInfo::getPageSize()) / 1024;
441 str += "K\tPages: ";
442 str += (g_AllocedPages * TargetInfo::getPageSize()) / 1024;
443 str += "K\t Free: ";
444 str += (g_FreePages * TargetInfo::getPageSize()) / 1024;
445 str += "K\n";
446
447 pSerial->write_str(str);
448#endif
449 }
450
451 // Timer delta is in nanoseconds.
453}
Definition Event.h:49
bool initialise1() INITIALISATION_ONLY
virtual uint8_t getDayOfMonth()
virtual ~HostedTimer()
virtual uint8_t getHour()
static HostedTimer m_Instance
void processTimerBatch(uint64_t delta)
IrqEventCounter m_PendingExpirations
HardStageDisposition hardIrq(irq_id_t number, InterruptState &state, size_t &work) override
HostedTickSource m_TickSource
virtual size_t getYear()
void rearmIrqSources(size_t work) override
virtual void addAlarm(class Event *pEvent, size_t alarmSecs, size_t alarmUsecs=0)
virtual uint8_t getMonth()
TimerHandlerRegistry m_HandlerRegistry
virtual uint8_t getMinute()
HostedTimer() INITIALISATION_ONLY
virtual void removeAlarm(class Event *pEvent)
virtual uint64_t getTickCountNano()
void threadedIrq(size_t work) override
virtual uint8_t getSecond()
virtual void synchronise(bool tohw=false)
virtual uint64_t getTickCount()
bool initialise3()
void uninitialise()
virtual uint8_t getDayOfWeek()
bool quiesceIrqSources() override
virtual uint64_t getNanosecond()
bool recordFromInterrupt(size_t occurrences=1)
virtual void tick()
Definition IrqManager.cc:37
::Iterator< T, node_t > Iterator
Definition List.h:67
virtual Serial * getSerial(size_t n)=0
static ProcessorInformation & information()
void release()
Definition Spinlock.cc:161
bool acquire(bool recurse=false, bool safe=true)
Definition Spinlock.cc:35
irq_id_t registerIsaSplitIrq(IrqManager &manager, uint8_t irq, const IrqPolicy &policy)
static constexpr size_t getPageSize() noexcept
Definition TargetInfo.h:40
bool sendEvent(Event *pEvent)
Definition Thread.cc:1158
bool dispatch(uint64_t delta, TimerHandler *onlyHandler=nullptr)
bool unregisterHandler(TimerHandler *handler)