HPET race conditions
Posted: Thu Oct 24, 2024 9:06 pm
Hello,
Presently in my OS I am working on multitasking. For context, my OS is kind of a tickless system (or at least I aim for it to become one). However, I noticed today that sometimes timer events will be missed because the comparator in the HPET is not set far ahead enough of the main counter so when the comparator actually gets set, the event has already passed.
In order to fix this, for a while I would have a "minimum delta" and if a requested delta was smaller than this, I would set it to this. This turned problematic as the minimum delta varies from machine to machine and also can race with things like SMI, and also was too inaccurate to use. So I'm not entirely sure what the best way to handle this is.
One idea I have is to loop when arming, checking if the main counter is greater than the comparator. If it is, than we add a continually increasing offset to the base delta. The problem here is this feels error prone and like a hack.
The other idea I have is to artificially decrease the amount of precision available to us. This is similar to my original plan but a bit more elegant. Basically my kernel works on 64-bit nanosec based time deltas, but obviously the max available precision can be less than this depending on hardware. What I would do is if the HPET has a precision greater than a specific amount (e.g., 100 us), I would cap it at that and round all incoming deltas to the nearest 100 us up (or down if its closer and wouldn't cause it to go to 0). This still has the drawback of limiting precision (which isn't a huge deal) and also still being slightly arbitrary so that one unlucky SMI could mess things up.
My final idea (which is admittedly the simplest) i just to not use HPET for timer events and always use the APIC timer. If the system doesn't have an invariant TSC I'll still use the HPET as a clock base (as my kernel separates the abstractions of clock and timer). This is what I'm leaning towards as this would be the most reliable, however I would still like the HPET to be an option for users since it's still not dependent on CPU frequency so things like sleep states and that kind of stuff can't mess up systems without ARAT (which shouldn't be a huge deal for events and not clocks AFAICT).
What do you all think the best option is?
Presently in my OS I am working on multitasking. For context, my OS is kind of a tickless system (or at least I aim for it to become one). However, I noticed today that sometimes timer events will be missed because the comparator in the HPET is not set far ahead enough of the main counter so when the comparator actually gets set, the event has already passed.
In order to fix this, for a while I would have a "minimum delta" and if a requested delta was smaller than this, I would set it to this. This turned problematic as the minimum delta varies from machine to machine and also can race with things like SMI, and also was too inaccurate to use. So I'm not entirely sure what the best way to handle this is.
One idea I have is to loop when arming, checking if the main counter is greater than the comparator. If it is, than we add a continually increasing offset to the base delta. The problem here is this feels error prone and like a hack.
The other idea I have is to artificially decrease the amount of precision available to us. This is similar to my original plan but a bit more elegant. Basically my kernel works on 64-bit nanosec based time deltas, but obviously the max available precision can be less than this depending on hardware. What I would do is if the HPET has a precision greater than a specific amount (e.g., 100 us), I would cap it at that and round all incoming deltas to the nearest 100 us up (or down if its closer and wouldn't cause it to go to 0). This still has the drawback of limiting precision (which isn't a huge deal) and also still being slightly arbitrary so that one unlucky SMI could mess things up.
My final idea (which is admittedly the simplest) i just to not use HPET for timer events and always use the APIC timer. If the system doesn't have an invariant TSC I'll still use the HPET as a clock base (as my kernel separates the abstractions of clock and timer). This is what I'm leaning towards as this would be the most reliable, however I would still like the HPET to be an option for users since it's still not dependent on CPU frequency so things like sleep states and that kind of stuff can't mess up systems without ARAT (which shouldn't be a huge deal for events and not clocks AFAICT).
What do you all think the best option is?