From: Waldemar Kozaczuk <jwkozac...@gmail.com>
Committer: Waldemar Kozaczuk <jwkozac...@gmail.com>
Branch: master
hpet: handle wrap-around with 32-bit counter
This patch enhances the hpet clock with 32-bit main
counter to handle wrap-arounds. It does it by maintaining
separate upper 32-bit counter per-cpu and incrementing it
when wrap around is detected.
Signed-off-by: Waldemar Kozaczuk <jwkozac...@gmail.com>
---
diff --git a/drivers/hpet.cc b/drivers/hpet.cc
--- a/drivers/hpet.cc
+++ b/drivers/hpet.cc
@@ -18,6 +18,7 @@ extern "C" {
#include <osv/xen.hh>
#include <osv/irqlock.hh>
#include "rtc.hh"
+#include <osv/percpu.hh>
using boost::intrusive::get_parent_from_member;
@@ -33,23 +34,53 @@ class hpetclock : public clock {
#define HPET_COUNTER 0x0f0
-//FIXME: Enhance this class to handle wrap-around
+// The hpet clocks are tricky to implement right. Ideally hpet clocks,
+// especially those with 32-bit main counter, should be avoided in
+// virtualized environments. Unfortunately some hypervisors like
+// hyperkit and other bhyve derivatives provide 32-bit hpet clock only and
+// we would like to support those hypervisors as well to let potential
+// users test OSv on OSX and FreeBSD.
+// The implementation below is a compromise that delivers monotonic
+// reads but may suffer from inaccurate time reads especially when
+// OSv guest gets suspended by host. In essence it maintains it own
+// upper 32-bit counter per cpu which gets incremented every time
+// it gets less than previously saved main counter read from the host.
+// In future we can improve it by handling wrap-around interrupts but
+// but it is not clear if it would be better and especially worth the
effort.
class hpet_32bit_clock : public hpetclock {
public:
- hpet_32bit_clock(mmioaddr_t hpet_mmio_address) :
hpetclock(hpet_mmio_address) {
- debug_early_u64("WARNING: hpet with 32-bit counter will wrap
around in seconds: ",
- (_period * (1UL << 32)) / 1000000000UL);
- }
+ hpet_32bit_clock(mmioaddr_t hpet_mmio_address) :
hpetclock(hpet_mmio_address) {}
+
protected:
virtual s64 time() override __attribute__((no_instrument_function)) {
- return _wall + mmio_getl(_addr + HPET_COUNTER) * _period;
+ return _wall + fetch_counter() * _period;
}
virtual s64 uptime() override __attribute__((no_instrument_function)) {
- return mmio_getl(_addr + HPET_COUNTER) * _period;
+ return fetch_counter() * _period;
}
+private:
+ s64 fetch_counter() {
+ auto last_valid_counter =
(*_last_read_counter).load(std::memory_order_relaxed);
+ auto upper_counter =
(*_upper_32bit_counter).load(std::memory_order_relaxed);
+
+ s64 counter = mmio_getl(_addr + HPET_COUNTER);
+ if (counter < last_valid_counter) {
+ // Wrap-around - increment upper counter
+ (*_upper_32bit_counter).compare_exchange_strong(upper_counter,
upper_counter + 1);
+ }
+
+ (*_last_read_counter).store(counter, std::memory_order_relaxed);
+ return ((*_upper_32bit_counter).load(std::memory_order_relaxed) <<
32) + counter;
+ }
+
+ static percpu<std::atomic<s64>> _upper_32bit_counter;
+ static percpu<std::atomic<s64>> _last_read_counter;
};
+PERCPU(std::atomic<s64>, hpet_32bit_clock::_upper_32bit_counter);
+PERCPU(std::atomic<s64>, hpet_32bit_clock::_last_read_counter);
+
class hpet_64bit_clock : public hpetclock {
public:
hpet_64bit_clock(mmioaddr_t hpet_mmio_address) :
hpetclock(hpet_mmio_address) {}
--
You received this message because you are subscribed to the Google Groups "OSv
Development" group.
To unsubscribe from this group and stop receiving emails from it, send an email
to osv-dev+unsubscr...@googlegroups.com.
To view this discussion on the web visit
https://groups.google.com/d/msgid/osv-dev/00000000000016455b05908317c5%40google.com.