Skip to content
Snippets Groups Projects
kvmclock.cc 2.63 KiB
Newer Older
  • Learn to ignore specific revisions
  • /*
     * Copyright (C) 2013 Cloudius Systems, Ltd.
     *
     * This work is open source software, licensed under the terms of the
     * BSD license as described in the LICENSE file in the top-level directory.
     */
    
    
    Avi Kivity's avatar
    Avi Kivity committed
    #include "clock.hh"
    #include "msr.hh"
    
    #include <osv/types.h>
    
    Avi Kivity's avatar
    Avi Kivity committed
    #include "mmu.hh"
    
    #include "string.h"
    
    Avi Kivity's avatar
    Avi Kivity committed
    #include "cpuid.hh"
    
    #include "barrier.hh"
    
    Avi Kivity's avatar
    Avi Kivity committed
    #include <osv/percpu.hh>
    
    #include <osv/pvclock-abi.hh>
    
    #include "prio.hh"
    
    Avi Kivity's avatar
    Avi Kivity committed
    
    class kvmclock : public clock {
    public:
        kvmclock();
    
        virtual s64 time() __attribute__((no_instrument_function));
    
    Avi Kivity's avatar
    Avi Kivity committed
    private:
        u64 wall_clock_boot();
        u64 system_time();
    
    Avi Kivity's avatar
    Avi Kivity committed
        static void setup_cpu();
    
    Avi Kivity's avatar
    Avi Kivity committed
    private:
    
    Avi Kivity's avatar
    Avi Kivity committed
        static bool _smp_init;
    
        static bool _new_kvmclock_msrs;
    
        pvclock_wall_clock* _wall;
    
        static percpu<pvclock_vcpu_time_info> _sys;
    
    Avi Kivity's avatar
    Avi Kivity committed
        sched::cpu::notifier cpu_notifier;
    
    Avi Kivity's avatar
    Avi Kivity committed
    bool kvmclock::_smp_init = false;
    
    bool kvmclock::_new_kvmclock_msrs = true;
    
    PERCPU(pvclock_vcpu_time_info, kvmclock::_sys);
    
    Avi Kivity's avatar
    Avi Kivity committed
    
    
    Avi Kivity's avatar
    Avi Kivity committed
    kvmclock::kvmclock()
    
    Avi Kivity's avatar
    Avi Kivity committed
        : cpu_notifier(&kvmclock::setup_cpu)
    
    Avi Kivity's avatar
    Avi Kivity committed
    {
    
        auto 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));
    
    Avi Kivity's avatar
    Avi Kivity committed
    }
    
    void kvmclock::setup_cpu()
    {
    
        auto system_time_msr = (_new_kvmclock_msrs) ?
                               msr::KVM_SYSTEM_TIME_NEW : msr::KVM_SYSTEM_TIME;
    
    Avi Kivity's avatar
    Avi Kivity committed
        memset(&*_sys, 0, sizeof(*_sys));
    
        processor::wrmsr(system_time_msr, mmu::virt_to_phys(&*_sys) | 1);
    
    Avi Kivity's avatar
    Avi Kivity committed
        _smp_init = true;
    
        if (processor::features().kvm_clocksource2) {
            return true;
    
        if (processor::features().kvm_clocksource) {
    
            _new_kvmclock_msrs = false;
    
            return true;
    
        return false;
    
    s64 kvmclock::time()
    
    Avi Kivity's avatar
    Avi Kivity committed
    {
    
    Avi Kivity's avatar
    Avi Kivity committed
        // Due to problems in init order dependencies (the clock depends
        // on the scheduler, for percpu initialization, and vice-versa, for
        // idle thread initialization, don't loop up system time until at least
        // one cpu is initialized.
        if (_smp_init) {
            r += system_time();
        }
        return r;
    
    Avi Kivity's avatar
    Avi Kivity committed
    }
    
    u64 kvmclock::wall_clock_boot()
    {
    
        return pvclock::wall_clock_boot(_wall);
    
    Avi Kivity's avatar
    Avi Kivity committed
    }
    
    u64 kvmclock::system_time()
    {
    
        sched::preempt_disable();
    
    Avi Kivity's avatar
    Avi Kivity committed
        auto sys = &*_sys;  // avoid recaclulating address each access
    
        auto r = pvclock::system_time(sys);
    
        sched::preempt_enable();
    
    static __attribute__((constructor(init_prio::clock))) void setup_kvmclock()
    
        if (kvmclock::probe()) {
    
    Avi Kivity's avatar
    Avi Kivity committed
            clock::register_clock(new kvmclock);
        }