This patch is an updated version of the one originally
authored by Rick Payne over year ago. It adds logic to
start a thread if configured so to periodically update
KVM wall clock so that date and time stays in sync with host.

The wall clock is updated by writing to wall clock MSR
as explained in this quote from the Linux's virtual/kvm/msr.txt:

"MSR_KVM_WALL_CLOCK_NEW:
....
The hypervisor is only guaranteed to update this data at the moment of MSR 
write.
Users that want to reliably query this information more than once have
to write more than once to this MSR."

The sync thread is off by default and can be enabled by
passing the '--keep-clock-in-sync' kernel option.

Co-authored-by: Rick Payne <ri...@rossfell.co.uk>
Co-authored-by: Waldemar Kozaczuk <jwkozac...@gmail.com>
Signed-off-by: Waldemar Kozaczuk <jwkozac...@gmail.com>
---
 drivers/clock.hh    |  5 +++++
 drivers/kvmclock.cc | 33 ++++++++++++++++++++++++++++++---
 loader.cc           | 12 +++++++++++-
 3 files changed, 46 insertions(+), 4 deletions(-)

diff --git a/drivers/clock.hh b/drivers/clock.hh
index 9b8aea3c..51f418f7 100644
--- a/drivers/clock.hh
+++ b/drivers/clock.hh
@@ -99,6 +99,11 @@ public:
      * Not all clocks are required to implement it.
      */
     virtual u64 processor_to_nano(u64 ticks) { return 0; }
+
+    /*
+     * Start a thread to periodically synchronize wall clock with the host
+     */
+    virtual void start_sync_thread() {}
 private:
     static clock* _c;
 };
diff --git a/drivers/kvmclock.cc b/drivers/kvmclock.cc
index 68389dfb..2de5b4ee 100644
--- a/drivers/kvmclock.cc
+++ b/drivers/kvmclock.cc
@@ -16,21 +16,28 @@
 #include <osv/pvclock-abi.hh>
 #include <osv/prio.hh>
 #include <osv/migration-lock.hh>
+#include <osv/sched.hh>
 #include <mutex>
 #include <atomic>
 
+using namespace osv::clock;
+
 class kvmclock : public pv_based_clock {
 public:
     kvmclock();
     virtual u64 processor_to_nano(u64 ticks) override 
__attribute__((no_instrument_function));
     static bool probe();
+    virtual void start_sync_thread();
 protected:
     virtual u64 wall_clock_boot();
     virtual u64 system_time();
     virtual void init_on_cpu();
+    void sync_wall_clock();
 private:
     static bool _new_kvmclock_msrs;
     pvclock_wall_clock* _wall;
+    u64 _wall_phys;
+    msr _wall_time_msr;
     static percpu<pvclock_vcpu_time_info> _sys;
     pvclock _pvclock;
 };
@@ -50,11 +57,12 @@ static u8 get_pvclock_flags()
 kvmclock::kvmclock()
     : _pvclock(get_pvclock_flags())
 {
-    auto wall_time_msr = (_new_kvmclock_msrs) ?
-                         msr::KVM_WALL_CLOCK_NEW : msr::KVM_WALL_CLOCK;
+    _wall_time_msr = (_new_kvmclock_msrs) ?
+                     msr::KVM_WALL_CLOCK_NEW : msr::KVM_WALL_CLOCK;
     _wall = new pvclock_wall_clock;
     memset(_wall, 0, sizeof(*_wall));
-    processor::wrmsr(wall_time_msr, mmu::virt_to_phys(_wall));
+    _wall_phys = mmu::virt_to_phys(_wall);
+    processor::wrmsr(_wall_time_msr, _wall_phys);
 }
 
 void kvmclock::init_on_cpu()
@@ -82,6 +90,25 @@ u64 kvmclock::wall_clock_boot()
     return _pvclock.wall_clock_boot(_wall);
 }
 
+void kvmclock::sync_wall_clock()
+{
+    processor::wrmsr(_wall_time_msr, _wall_phys);
+    _pvclock.wall_clock_boot(_wall);
+}
+
+void kvmclock::start_sync_thread()
+{
+    auto t = sched::thread::make([this] { //How to name it
+        sched::timer tmr(*sched::thread::current());
+        while (true) {
+            tmr.set(uptime::time_point(uptime::duration(this->uptime() + 
1000000000)));
+            sched::thread::wait_until([&] { return tmr.expired(); });
+            this->sync_wall_clock();
+        }
+    }, sched::thread::attr().name("kvm_wall_clock_sync"));
+    t->start();
+}
+
 u64 kvmclock::system_time()
 {
     WITH_LOCK(migration_lock) {
diff --git a/loader.cc b/loader.cc
index 324d30c1..096fd0b1 100644
--- a/loader.cc
+++ b/loader.cc
@@ -146,6 +146,7 @@ static std::chrono::nanoseconds boot_delay;
 bool opt_maxnic = false;
 int maxnic;
 bool opt_pci_disabled = false;
+bool opt_keep_wall_clock_in_sync = false;
 
 static int sampler_frequency;
 static bool opt_enable_sampler = false;
@@ -178,7 +179,8 @@ static void usage()
     std::cout << "  --delay=arg (=0)      delay in seconds before boot\n";
     std::cout << "  --redirect=arg        redirect stdout and stderr to 
file\n";
     std::cout << "  --disable_rofs_cache  disable ROFS memory cache\n";
-    std::cout << "  --nopci               disable PCI enumeration\n\n";
+    std::cout << "  --nopci               disable PCI enumeration\n";
+    std::cout << "  --keep-clock-in-sync  keep wall clock in sync\n\n";
 }
 
 static void handle_parse_error(const std::string &message)
@@ -306,6 +308,10 @@ static void parse_options(int loader_argc, char** 
loader_argv)
         opt_pci_disabled = true;
     }
 
+    if (extract_option_flag(options_values, "keep-clock-in-sync")) {
+        opt_keep_wall_clock_in_sync = true;
+    }
+
     if (!options_values.empty()) {
         for (auto other_option : options_values) {
             std::cout << "unrecognized option: " << other_option.first << 
std::endl;
@@ -468,6 +474,10 @@ void* do_main_thread(void *_main_args)
         boot_time.print_total_time();
     }
 
+    if (opt_keep_wall_clock_in_sync) {
+        ::clock::get()->start_sync_thread();
+    }
+
     if (!opt_redirect.empty()) {
         // redirect stdout and stdin to the given file, instead of the console
         // use ">>filename" to append, instead of replace, to a file.
-- 
2.20.1

-- 
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/20191219170316.28844-1-jwkozaczuk%40gmail.com.

Reply via email to