The Design and Implementation of the FreeBSD Operating System, Second Edition
Now available: The Design and Implementation of the FreeBSD Operating System (Second Edition)


[ source navigation ] [ diff markup ] [ identifier search ] [ freetext search ] [ file search ] [ list types ] [ track identifier ]

FreeBSD/Linux Kernel Cross Reference
sys/dev/kvm_clock/kvm_clock.c

Version: -  FREEBSD  -  FREEBSD-13-STABLE  -  FREEBSD-13-0  -  FREEBSD-12-STABLE  -  FREEBSD-12-0  -  FREEBSD-11-STABLE  -  FREEBSD-11-0  -  FREEBSD-10-STABLE  -  FREEBSD-10-0  -  FREEBSD-9-STABLE  -  FREEBSD-9-0  -  FREEBSD-8-STABLE  -  FREEBSD-8-0  -  FREEBSD-7-STABLE  -  FREEBSD-7-0  -  FREEBSD-6-STABLE  -  FREEBSD-6-0  -  FREEBSD-5-STABLE  -  FREEBSD-5-0  -  FREEBSD-4-STABLE  -  FREEBSD-3-STABLE  -  FREEBSD22  -  l41  -  OPENBSD  -  linux-2.6  -  MK84  -  PLAN9  -  xnu-8792 
SearchContext: -  none  -  3  -  10 

    1 /*-
    2  * SPDX-License-Identifier: BSD-2-Clause-FreeBSD
    3  *
    4  * Copyright (c) 2014 Bryan Venteicher <bryanv@FreeBSD.org>
    5  * Copyright (c) 2021 Mathieu Chouquet-Stringer
    6  * Copyright (c) 2021 Juniper Networks, Inc.
    7  * Copyright (c) 2021 Klara, Inc.
    8  *
    9  * Redistribution and use in source and binary forms, with or without
   10  * modification, are permitted provided that the following conditions
   11  * are met:
   12  * 1. Redistributions of source code must retain the above copyright
   13  *    notice, this list of conditions and the following disclaimer.
   14  * 2. Redistributions in binary form must reproduce the above copyright
   15  *    notice, this list of conditions and the following disclaimer in the
   16  *    documentation and/or other materials provided with the distribution.
   17  *
   18  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
   19  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
   20  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
   21  * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
   22  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
   23  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
   24  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
   25  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
   26  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
   27  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
   28  * SUCH DAMAGE.
   29  */
   30 
   31 /*
   32  * Linux KVM paravirtual clock support
   33  *
   34  * References:
   35  *     - [1] https://www.kernel.org/doc/html/latest/virt/kvm/cpuid.html
   36  *     - [2] https://www.kernel.org/doc/html/latest/virt/kvm/msr.html
   37  */
   38 
   39 #include <sys/cdefs.h>
   40 __FBSDID("$FreeBSD$");
   41 
   42 #include <sys/param.h>
   43 #include <sys/bus.h>
   44 #include <sys/domainset.h>
   45 #include <sys/kernel.h>
   46 #include <sys/malloc.h>
   47 #include <sys/module.h>
   48 #include <sys/smp.h>
   49 
   50 #include <vm/vm.h>
   51 #include <vm/pmap.h>
   52 #include <vm/vm_extern.h>
   53 
   54 #include <machine/pvclock.h>
   55 #include <x86/kvm.h>
   56 
   57 #include "clock_if.h"
   58 
   59 #define KVM_CLOCK_DEVNAME               "kvmclock"
   60 /*
   61  * Note: Chosen to be (1) above HPET's value (always 950), (2) above the TSC's
   62  * default value of 800, and (3) below the TSC's value when it supports the
   63  * "Invariant TSC" feature and is believed to be synchronized across all CPUs.
   64  */
   65 #define KVM_CLOCK_TC_QUALITY            975
   66 
   67 struct kvm_clock_softc {
   68         struct pvclock                   pvc;
   69         struct pvclock_wall_clock        wc;
   70         struct pvclock_vcpu_time_info   *timeinfos;
   71         u_int                            msr_tc;
   72         u_int                            msr_wc;
   73 };
   74 
   75 static devclass_t       kvm_clock_devclass;
   76 
   77 static struct pvclock_wall_clock *kvm_clock_get_wallclock(void *arg);
   78 static void     kvm_clock_system_time_enable(struct kvm_clock_softc *sc);
   79 static void     kvm_clock_system_time_enable_pcpu(void *arg);
   80 
   81 static struct pvclock_wall_clock *
   82 kvm_clock_get_wallclock(void *arg)
   83 {
   84         struct kvm_clock_softc *sc = arg;
   85 
   86         wrmsr(sc->msr_wc, vtophys(&sc->wc));
   87         return (&sc->wc);
   88 }
   89 
   90 static void
   91 kvm_clock_system_time_enable(struct kvm_clock_softc *sc)
   92 {
   93         smp_rendezvous(NULL, kvm_clock_system_time_enable_pcpu, NULL, sc);
   94 }
   95 
   96 static void
   97 kvm_clock_system_time_enable_pcpu(void *arg)
   98 {
   99         struct kvm_clock_softc *sc = arg;
  100 
  101         /*
  102          * See [2]; the lsb of this MSR is the system time enable bit.
  103          */
  104         wrmsr(sc->msr_tc, vtophys(&(sc->timeinfos)[curcpu]) | 1);
  105 }
  106 
  107 static void
  108 kvm_clock_identify(driver_t *driver, device_t parent)
  109 {
  110         u_int regs[4];
  111 
  112         kvm_cpuid_get_features(regs);
  113         if ((regs[0] &
  114             (KVM_FEATURE_CLOCKSOURCE2 | KVM_FEATURE_CLOCKSOURCE)) == 0)
  115                 return;
  116         if (device_find_child(parent, KVM_CLOCK_DEVNAME, -1))
  117                 return;
  118         BUS_ADD_CHILD(parent, 0, KVM_CLOCK_DEVNAME, 0);
  119 }
  120 
  121 static int
  122 kvm_clock_probe(device_t dev)
  123 {
  124         device_set_desc(dev, "KVM paravirtual clock");
  125         return (BUS_PROBE_DEFAULT);
  126 }
  127 
  128 static int
  129 kvm_clock_attach(device_t dev)
  130 {
  131         u_int regs[4];
  132         struct kvm_clock_softc *sc = device_get_softc(dev);
  133         bool stable_flag_supported;
  134 
  135         /* Process KVM "features" CPUID leaf content: */
  136         kvm_cpuid_get_features(regs);
  137         if ((regs[0] & KVM_FEATURE_CLOCKSOURCE2) != 0) {
  138                 sc->msr_tc = KVM_MSR_SYSTEM_TIME_NEW;
  139                 sc->msr_wc = KVM_MSR_WALL_CLOCK_NEW;
  140         } else {
  141                 KASSERT((regs[0] & KVM_FEATURE_CLOCKSOURCE) != 0,
  142                     ("Clocksource feature flags disappeared since "
  143                     "kvm_clock_identify: regs[0] %#0x.", regs[0]));
  144                 sc->msr_tc = KVM_MSR_SYSTEM_TIME;
  145                 sc->msr_wc = KVM_MSR_WALL_CLOCK;
  146         }
  147         stable_flag_supported =
  148             (regs[0] & KVM_FEATURE_CLOCKSOURCE_STABLE_BIT) != 0;
  149 
  150         /* Set up 'struct pvclock_vcpu_time_info' page(s): */
  151         sc->timeinfos = (struct pvclock_vcpu_time_info *)kmem_malloc(mp_ncpus *
  152             sizeof(struct pvclock_vcpu_time_info), M_WAITOK | M_ZERO);
  153         kvm_clock_system_time_enable(sc);
  154 
  155         /*
  156          * Init pvclock; register KVM clock wall clock, register KVM clock
  157          * timecounter, and set up the requisite infrastructure for vDSO access
  158          * to this timecounter.
  159          *     Regarding 'tc_flags': Since the KVM MSR documentation does not
  160          *     specifically discuss suspend/resume scenarios, conservatively
  161          *     leave 'TC_FLAGS_SUSPEND_SAFE' cleared and assume that the system
  162          *     time must be re-inited in such cases.
  163          */
  164         sc->pvc.get_wallclock = kvm_clock_get_wallclock;
  165         sc->pvc.get_wallclock_arg = sc;
  166         sc->pvc.timeinfos = sc->timeinfos;
  167         sc->pvc.stable_flag_supported = stable_flag_supported;
  168         pvclock_init(&sc->pvc, dev, KVM_CLOCK_DEVNAME, KVM_CLOCK_TC_QUALITY, 0);
  169         return (0);
  170 }
  171 
  172 static int
  173 kvm_clock_detach(device_t dev)
  174 {
  175         struct kvm_clock_softc *sc = device_get_softc(dev);
  176 
  177         return (pvclock_destroy(&sc->pvc));
  178 }
  179 
  180 static int
  181 kvm_clock_suspend(device_t dev)
  182 {
  183         return (0);
  184 }
  185 
  186 static int
  187 kvm_clock_resume(device_t dev)
  188 {
  189         /*
  190          * See note in 'kvm_clock_attach()' regarding 'TC_FLAGS_SUSPEND_SAFE';
  191          * conservatively assume that the system time must be re-inited in
  192          * suspend/resume scenarios.
  193          */
  194         kvm_clock_system_time_enable(device_get_softc(dev));
  195         pvclock_resume();
  196         inittodr(time_second);
  197         return (0);
  198 }
  199 
  200 static int
  201 kvm_clock_gettime(device_t dev, struct timespec *ts)
  202 {
  203         struct kvm_clock_softc *sc = device_get_softc(dev);
  204 
  205         pvclock_gettime(&sc->pvc, ts);
  206         return (0);
  207 }
  208 
  209 static int
  210 kvm_clock_settime(device_t dev, struct timespec *ts)
  211 {
  212         /*
  213          * Even though it is not possible to set the KVM clock's wall clock, to
  214          * avoid the possibility of periodic benign error messages from
  215          * 'settime_task_func()', report success rather than, e.g., 'ENODEV'.
  216          */
  217         return (0);
  218 }
  219 
  220 static device_method_t kvm_clock_methods[] = {
  221         DEVMETHOD(device_identify,      kvm_clock_identify),
  222         DEVMETHOD(device_probe,         kvm_clock_probe),
  223         DEVMETHOD(device_attach,        kvm_clock_attach),
  224         DEVMETHOD(device_detach,        kvm_clock_detach),
  225         DEVMETHOD(device_suspend,       kvm_clock_suspend),
  226         DEVMETHOD(device_resume,        kvm_clock_resume),
  227         /* clock interface */
  228         DEVMETHOD(clock_gettime,        kvm_clock_gettime),
  229         DEVMETHOD(clock_settime,        kvm_clock_settime),
  230 
  231         DEVMETHOD_END
  232 };
  233 
  234 static driver_t kvm_clock_driver = {
  235         KVM_CLOCK_DEVNAME,
  236         kvm_clock_methods,
  237         sizeof(struct kvm_clock_softc),
  238 };
  239 
  240 DRIVER_MODULE(kvm_clock, nexus, kvm_clock_driver, kvm_clock_devclass, 0, 0);

Cache object: e46e40fceeb06db368cfb2de733e3543


[ source navigation ] [ diff markup ] [ identifier search ] [ freetext search ] [ file search ] [ list types ] [ track identifier ]


This page is part of the FreeBSD/Linux Linux Kernel Cross-Reference, and was automatically generated using a modified version of the LXR engine.