zberry7 wrote:
Hello! This is my first question/post here, I’ve been working on my OS for couple months in my spare time, and I absolutely love it, and this forum/wiki
I’ve come to the point where I need precise system time, and a timer/event system. And I have a basic idea nailed down pretty well, which is setting up an IRQ at ‘X’ Hz, and then in the handler adding ‘10^9 / X’ to a ‘nanoseconds since boot’ counter, and then using the TSC to interpolate between IRQs to get nanosecond precise time.
So, what you will your IRQ do? You will not do anything meaningful in it, and so it is just wasting CPU cycles.
Instead, you always need to have a preemption timer that the scheduler updates, and so instead of updating system time in an IRQ, it's better to do it in the scheduler when it sets up the preemption timer as it reads system time. Every time you read system time you read out the counter (PIT, HPET or whatever), subtract it from the previous read-out and add it to system time (possibly after a conversion).
Timers are best implemented by creating a list of active timers and sorting them in expire order. The expire time of the timer could be expressed in system time, which makes sure the timer related to system time is read regularly so it doesn't overflow.
zberry7 wrote:
1) Having the HPET/PIT raising an IRQ on the BSP, where it performs the logic needed to increment the counter (writing COUNTER_MAIN, LAST_TSC, and RATE_TSC). But, what if I need to sample the system time from another CPU? I could end up reading in between COUNTER_MAIN and LAST_TSC, getting a result thats ‘1000/X’ ms ahead, and a reading immediately after could be less than the older reading (or a race condition?). I was thinking of making a sort of ‘swap chain’ where there’s two sets of counters, and they are swapped after each update so we don’t write to the set of counters that might be sampled
It will depend on the hardware available. I think using the HPET for system time is optimal, and it is a global resource and therefore can be read from any processor core and still give the same result. The only difference in a multicore solution is that updating system time must use a spinlock. If the HPET is not available, and you use TSC, things get more problematic since this is a per-core function that cannot be assumed to be consistent regardless of which core it is used from.
Timers in a multicore system are best implemented per processor core, for instance using the APIC timer. If you only have the PIT, then you must make timers global, but then OTOH, such systems typically are single-core anyway.
zberry7 wrote:
2) I could(?) send the IRQ to each CPU in the system, using one of the I/O APIC entries, although I’m not sure how to do this (0xFF for destination like with IPIs?), and the documentation isn’t very clear to me. But, this would ensure there’s no issues with data races, getting bad readings during an update, or anything of that nature. But, this requires extra overhead due to each CPU having to run the IRQ, and having to find its own list of counters. Also, on reading system time this would also require getting a unique identifier and calculating where the timers are in memory.
You shouldn't use IRQs for system time, and for timers, you should have them per core and thus the IRQs will trigger per core too. If you have no per-core timer, you should set the system timer to trigger BSP only.