1*afdb7e40Snonaka /* $NetBSD: hvtimesync.c,v 1.1 2019/02/15 08:54:01 nonaka Exp $ */ 2*afdb7e40Snonaka 3*afdb7e40Snonaka /*- 4*afdb7e40Snonaka * Copyright (c) 2014,2016-2017 Microsoft Corp. 5*afdb7e40Snonaka * All rights reserved. 6*afdb7e40Snonaka * 7*afdb7e40Snonaka * Redistribution and use in source and binary forms, with or without 8*afdb7e40Snonaka * modification, are permitted provided that the following conditions 9*afdb7e40Snonaka * are met: 10*afdb7e40Snonaka * 1. Redistributions of source code must retain the above copyright 11*afdb7e40Snonaka * notice unmodified, this list of conditions, and the following 12*afdb7e40Snonaka * disclaimer. 13*afdb7e40Snonaka * 2. Redistributions in binary form must reproduce the above copyright 14*afdb7e40Snonaka * notice, this list of conditions and the following disclaimer in the 15*afdb7e40Snonaka * documentation and/or other materials provided with the distribution. 16*afdb7e40Snonaka * 17*afdb7e40Snonaka * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR 18*afdb7e40Snonaka * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES 19*afdb7e40Snonaka * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. 20*afdb7e40Snonaka * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, 21*afdb7e40Snonaka * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT 22*afdb7e40Snonaka * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 23*afdb7e40Snonaka * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 24*afdb7e40Snonaka * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 25*afdb7e40Snonaka * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF 26*afdb7e40Snonaka * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 27*afdb7e40Snonaka */ 28*afdb7e40Snonaka 29*afdb7e40Snonaka #include <sys/cdefs.h> 30*afdb7e40Snonaka #ifdef __KERNEL_RCSID 31*afdb7e40Snonaka __KERNEL_RCSID(0, "$NetBSD: hvtimesync.c,v 1.1 2019/02/15 08:54:01 nonaka Exp $"); 32*afdb7e40Snonaka #endif 33*afdb7e40Snonaka #ifdef __FBSDID 34*afdb7e40Snonaka __FBSDID("$FreeBSD: head/sys/dev/hyperv/utilities/vmbus_timesync.c 322488 2017-08-14 06:00:50Z sephe $"); 35*afdb7e40Snonaka #endif 36*afdb7e40Snonaka 37*afdb7e40Snonaka #include <sys/param.h> 38*afdb7e40Snonaka #include <sys/systm.h> 39*afdb7e40Snonaka #include <sys/device.h> 40*afdb7e40Snonaka #include <sys/module.h> 41*afdb7e40Snonaka #include <sys/pmf.h> 42*afdb7e40Snonaka #include <sys/sysctl.h> 43*afdb7e40Snonaka #include <sys/timetc.h> 44*afdb7e40Snonaka 45*afdb7e40Snonaka #include <dev/hyperv/vmbusvar.h> 46*afdb7e40Snonaka #include <dev/hyperv/vmbusicreg.h> 47*afdb7e40Snonaka #include <dev/hyperv/vmbusicvar.h> 48*afdb7e40Snonaka 49*afdb7e40Snonaka #define VMBUS_TIMESYNC_FWVER_MAJOR 3 50*afdb7e40Snonaka #define VMBUS_TIMESYNC_FWVER \ 51*afdb7e40Snonaka VMBUS_IC_VERSION(VMBUS_TIMESYNC_FWVER_MAJOR, 0) 52*afdb7e40Snonaka 53*afdb7e40Snonaka #define VMBUS_TIMESYNC_MSGVER_MAJOR 4 54*afdb7e40Snonaka #define VMBUS_TIMESYNC_MSGVER \ 55*afdb7e40Snonaka VMBUS_IC_VERSION(VMBUS_TIMESYNC_MSGVER_MAJOR, 0) 56*afdb7e40Snonaka 57*afdb7e40Snonaka #define VMBUS_TIMESYNC_MSGVER4(sc) \ 58*afdb7e40Snonaka VMBUS_ICVER_LE(VMBUS_IC_VERSION(4, 0), (sc)->sc_vmbusic.sc_msgver) 59*afdb7e40Snonaka 60*afdb7e40Snonaka #define VMBUS_TIMESYNC_DORTT(sc) \ 61*afdb7e40Snonaka (VMBUS_TIMESYNC_MSGVER4((sc)) && (hyperv_tc64 != NULL)) 62*afdb7e40Snonaka 63*afdb7e40Snonaka static int hvtimesync_match(device_t, cfdata_t, void *); 64*afdb7e40Snonaka static void hvtimesync_attach(device_t, device_t, void *); 65*afdb7e40Snonaka static int hvtimesync_detach(device_t, int); 66*afdb7e40Snonaka 67*afdb7e40Snonaka static void hvtimesync_channel_cb(void *); 68*afdb7e40Snonaka static int hvtimesync_sysctl_setup(device_t); 69*afdb7e40Snonaka 70*afdb7e40Snonaka struct hvtimesync_softc { 71*afdb7e40Snonaka struct vmbusic_softc sc_vmbusic; 72*afdb7e40Snonaka }; 73*afdb7e40Snonaka 74*afdb7e40Snonaka CFATTACH_DECL_NEW(hvtimesync, sizeof(struct hvtimesync_softc), 75*afdb7e40Snonaka hvtimesync_match, hvtimesync_attach, hvtimesync_detach, NULL); 76*afdb7e40Snonaka 77*afdb7e40Snonaka static int hvtimesync_ignore_sync; 78*afdb7e40Snonaka static int hvtimesnyc_sample_verbose; 79*afdb7e40Snonaka static int hvtimesync_sample_thresh = -1; 80*afdb7e40Snonaka 81*afdb7e40Snonaka static int 82*afdb7e40Snonaka hvtimesync_match(device_t parent, cfdata_t cf, void *aux) 83*afdb7e40Snonaka { 84*afdb7e40Snonaka struct vmbus_attach_args *aa = aux; 85*afdb7e40Snonaka 86*afdb7e40Snonaka return vmbusic_probe(aa, &hyperv_guid_timesync); 87*afdb7e40Snonaka } 88*afdb7e40Snonaka 89*afdb7e40Snonaka static void 90*afdb7e40Snonaka hvtimesync_attach(device_t parent, device_t self, void *aux) 91*afdb7e40Snonaka { 92*afdb7e40Snonaka struct vmbus_attach_args *aa = aux; 93*afdb7e40Snonaka int error; 94*afdb7e40Snonaka 95*afdb7e40Snonaka aprint_naive("\n"); 96*afdb7e40Snonaka aprint_normal(": Hyper-V Timesync\n"); 97*afdb7e40Snonaka 98*afdb7e40Snonaka error = vmbusic_attach(self, aa, hvtimesync_channel_cb); 99*afdb7e40Snonaka if (error) 100*afdb7e40Snonaka return; 101*afdb7e40Snonaka 102*afdb7e40Snonaka (void) pmf_device_register(self, NULL, NULL); 103*afdb7e40Snonaka 104*afdb7e40Snonaka (void) hvtimesync_sysctl_setup(self); 105*afdb7e40Snonaka } 106*afdb7e40Snonaka 107*afdb7e40Snonaka static int 108*afdb7e40Snonaka hvtimesync_detach(device_t self, int flags) 109*afdb7e40Snonaka { 110*afdb7e40Snonaka int error; 111*afdb7e40Snonaka 112*afdb7e40Snonaka error = vmbusic_detach(self, flags); 113*afdb7e40Snonaka if (error) 114*afdb7e40Snonaka return error; 115*afdb7e40Snonaka 116*afdb7e40Snonaka pmf_device_deregister(self); 117*afdb7e40Snonaka 118*afdb7e40Snonaka return 0; 119*afdb7e40Snonaka } 120*afdb7e40Snonaka 121*afdb7e40Snonaka static void 122*afdb7e40Snonaka do_timesync(struct hvtimesync_softc *sc, uint64_t hvtime, uint64_t sent_tc, 123*afdb7e40Snonaka uint8_t tsflags) 124*afdb7e40Snonaka { 125*afdb7e40Snonaka struct vmbusic_softc *vsc = &sc->sc_vmbusic; 126*afdb7e40Snonaka struct timespec vm_ts, hv_ts; 127*afdb7e40Snonaka uint64_t hv_ns, vm_ns, rtt = 0; 128*afdb7e40Snonaka int64_t diff; 129*afdb7e40Snonaka 130*afdb7e40Snonaka if (VMBUS_TIMESYNC_DORTT(sc)) 131*afdb7e40Snonaka rtt = hyperv_tc64() - sent_tc; 132*afdb7e40Snonaka 133*afdb7e40Snonaka hv_ns = (hvtime - VMBUS_ICMSG_TS_BASE + rtt) * HYPERV_TIMER_NS_FACTOR; 134*afdb7e40Snonaka nanotime(&vm_ts); 135*afdb7e40Snonaka vm_ns = (vm_ts.tv_sec * NANOSECOND) + vm_ts.tv_nsec; 136*afdb7e40Snonaka 137*afdb7e40Snonaka if ((tsflags & VMBUS_ICMSG_TS_FLAG_SYNC) && !hvtimesync_ignore_sync) { 138*afdb7e40Snonaka #if 0 139*afdb7e40Snonaka device_printf(vsc->sc_dev, 140*afdb7e40Snonaka "apply sync request, hv: %ju, vm: %ju\n", 141*afdb7e40Snonaka (uintmax_t)hv_ns, (uintmax_t)vm_ns); 142*afdb7e40Snonaka #endif 143*afdb7e40Snonaka hv_ts.tv_sec = hv_ns / NANOSECOND; 144*afdb7e40Snonaka hv_ts.tv_nsec = hv_ns % NANOSECOND; 145*afdb7e40Snonaka tc_setclock(&hv_ts); 146*afdb7e40Snonaka /* Done! */ 147*afdb7e40Snonaka return; 148*afdb7e40Snonaka } 149*afdb7e40Snonaka 150*afdb7e40Snonaka if ((tsflags & VMBUS_ICMSG_TS_FLAG_SAMPLE) && 151*afdb7e40Snonaka hvtimesync_sample_thresh >= 0) { 152*afdb7e40Snonaka if (hvtimesnyc_sample_verbose) { 153*afdb7e40Snonaka device_printf(vsc->sc_dev, 154*afdb7e40Snonaka "sample request, hv: %ju, vm: %ju\n", 155*afdb7e40Snonaka (uintmax_t)hv_ns, (uintmax_t)vm_ns); 156*afdb7e40Snonaka } 157*afdb7e40Snonaka 158*afdb7e40Snonaka if (hv_ns > vm_ns) 159*afdb7e40Snonaka diff = hv_ns - vm_ns; 160*afdb7e40Snonaka else 161*afdb7e40Snonaka diff = vm_ns - hv_ns; 162*afdb7e40Snonaka /* nanosec -> millisec */ 163*afdb7e40Snonaka diff /= 1000000; 164*afdb7e40Snonaka 165*afdb7e40Snonaka if (diff > hvtimesync_sample_thresh) { 166*afdb7e40Snonaka device_printf(vsc->sc_dev, 167*afdb7e40Snonaka "apply sample request, hv: %ju, vm: %ju\n", 168*afdb7e40Snonaka (uintmax_t)hv_ns, (uintmax_t)vm_ns); 169*afdb7e40Snonaka hv_ts.tv_sec = hv_ns / NANOSECOND; 170*afdb7e40Snonaka hv_ts.tv_nsec = hv_ns % NANOSECOND; 171*afdb7e40Snonaka tc_setclock(&hv_ts); 172*afdb7e40Snonaka } 173*afdb7e40Snonaka /* Done */ 174*afdb7e40Snonaka return; 175*afdb7e40Snonaka } 176*afdb7e40Snonaka } 177*afdb7e40Snonaka 178*afdb7e40Snonaka static void 179*afdb7e40Snonaka hvtimesync_channel_cb(void *arg) 180*afdb7e40Snonaka { 181*afdb7e40Snonaka struct hvtimesync_softc *sc = arg; 182*afdb7e40Snonaka struct vmbusic_softc *vsc = &sc->sc_vmbusic; 183*afdb7e40Snonaka struct vmbus_channel *ch = vsc->sc_chan; 184*afdb7e40Snonaka struct vmbus_icmsg_hdr *hdr; 185*afdb7e40Snonaka uint64_t rid; 186*afdb7e40Snonaka uint32_t rlen; 187*afdb7e40Snonaka int error; 188*afdb7e40Snonaka 189*afdb7e40Snonaka error = vmbus_channel_recv(ch, vsc->sc_buf, vsc->sc_buflen, 190*afdb7e40Snonaka &rlen, &rid, 0); 191*afdb7e40Snonaka if (error || rlen == 0) { 192*afdb7e40Snonaka if (error != EAGAIN) { 193*afdb7e40Snonaka DPRINTF("%s: timesync error=%d len=%u\n", 194*afdb7e40Snonaka device_xname(vsc->sc_dev), error, rlen); 195*afdb7e40Snonaka } 196*afdb7e40Snonaka return; 197*afdb7e40Snonaka } 198*afdb7e40Snonaka if (rlen < sizeof(*hdr)) { 199*afdb7e40Snonaka DPRINTF("%s: hvtimesync short read len=%u\n", 200*afdb7e40Snonaka device_xname(vsc->sc_dev), rlen); 201*afdb7e40Snonaka return; 202*afdb7e40Snonaka } 203*afdb7e40Snonaka 204*afdb7e40Snonaka hdr = (struct vmbus_icmsg_hdr *)vsc->sc_buf; 205*afdb7e40Snonaka switch (hdr->ic_type) { 206*afdb7e40Snonaka case VMBUS_ICMSG_TYPE_NEGOTIATE: 207*afdb7e40Snonaka error = vmbusic_negotiate(vsc, hdr, &rlen, VMBUS_TIMESYNC_FWVER, 208*afdb7e40Snonaka VMBUS_TIMESYNC_MSGVER); 209*afdb7e40Snonaka if (error) 210*afdb7e40Snonaka return; 211*afdb7e40Snonaka if (VMBUS_TIMESYNC_DORTT(sc)) { 212*afdb7e40Snonaka DPRINTF("%s: RTT\n", device_xname(vsc->sc_dev)); 213*afdb7e40Snonaka } 214*afdb7e40Snonaka break; 215*afdb7e40Snonaka 216*afdb7e40Snonaka case VMBUS_ICMSG_TYPE_TIMESYNC: 217*afdb7e40Snonaka if (VMBUS_TIMESYNC_MSGVER4(sc)) { 218*afdb7e40Snonaka struct vmbus_icmsg_timesync4 *msg4; 219*afdb7e40Snonaka 220*afdb7e40Snonaka if (rlen < sizeof(*msg4)) { 221*afdb7e40Snonaka DPRINTF("%s: invalid timesync4 len=%u\n", 222*afdb7e40Snonaka device_xname(vsc->sc_dev), rlen); 223*afdb7e40Snonaka return; 224*afdb7e40Snonaka } 225*afdb7e40Snonaka 226*afdb7e40Snonaka msg4 = (struct vmbus_icmsg_timesync4 *)hdr; 227*afdb7e40Snonaka do_timesync(sc, msg4->ic_hvtime, msg4->ic_sent_tc, 228*afdb7e40Snonaka msg4->ic_tsflags); 229*afdb7e40Snonaka } else { 230*afdb7e40Snonaka struct vmbus_icmsg_timesync *msg; 231*afdb7e40Snonaka 232*afdb7e40Snonaka if (rlen < sizeof(*msg)) { 233*afdb7e40Snonaka DPRINTF("%s: invalid timesync len=%u\n", 234*afdb7e40Snonaka device_xname(vsc->sc_dev), rlen); 235*afdb7e40Snonaka return; 236*afdb7e40Snonaka } 237*afdb7e40Snonaka 238*afdb7e40Snonaka msg = (struct vmbus_icmsg_timesync *)hdr; 239*afdb7e40Snonaka do_timesync(sc, msg->ic_hvtime, 0, msg->ic_tsflags); 240*afdb7e40Snonaka } 241*afdb7e40Snonaka break; 242*afdb7e40Snonaka 243*afdb7e40Snonaka default: 244*afdb7e40Snonaka device_printf(vsc->sc_dev, 245*afdb7e40Snonaka "unhandled _timesync message type %u\n", hdr->ic_type); 246*afdb7e40Snonaka return; 247*afdb7e40Snonaka } 248*afdb7e40Snonaka 249*afdb7e40Snonaka (void) vmbusic_sendresp(vsc, ch, vsc->sc_buf, rlen, rid); 250*afdb7e40Snonaka } 251*afdb7e40Snonaka 252*afdb7e40Snonaka static int 253*afdb7e40Snonaka hvtimesync_sysctl_setup(device_t self) 254*afdb7e40Snonaka { 255*afdb7e40Snonaka struct hvtimesync_softc *sc = device_private(self); 256*afdb7e40Snonaka struct vmbusic_softc *vsc = &sc->sc_vmbusic; 257*afdb7e40Snonaka const struct sysctlnode *node; 258*afdb7e40Snonaka int error; 259*afdb7e40Snonaka 260*afdb7e40Snonaka error = sysctl_createv(NULL, 0, NULL, &node, 261*afdb7e40Snonaka CTLFLAG_PERMANENT, CTLTYPE_NODE, "machdep", NULL, 262*afdb7e40Snonaka NULL, 0, NULL, 0, CTL_MACHDEP, CTL_EOL); 263*afdb7e40Snonaka if (error) 264*afdb7e40Snonaka return error; 265*afdb7e40Snonaka error = sysctl_createv(NULL, 0, &node, &node, 266*afdb7e40Snonaka CTLFLAG_PERMANENT, CTLTYPE_NODE, "hyperv", NULL, 267*afdb7e40Snonaka NULL, 0, NULL, 0, CTL_CREATE, CTL_EOL); 268*afdb7e40Snonaka if (error) 269*afdb7e40Snonaka return error; 270*afdb7e40Snonaka 271*afdb7e40Snonaka error = sysctl_createv(&vsc->sc_log, 0, &node, &node, 272*afdb7e40Snonaka 0, CTLTYPE_NODE, "timesync", NULL, 273*afdb7e40Snonaka NULL, 0, NULL, 0, CTL_CREATE, CTL_EOL); 274*afdb7e40Snonaka if (error) 275*afdb7e40Snonaka return error; 276*afdb7e40Snonaka 277*afdb7e40Snonaka error = sysctl_createv(&vsc->sc_log, 0, &node, NULL, 278*afdb7e40Snonaka CTLFLAG_READWRITE, 279*afdb7e40Snonaka CTLTYPE_INT, "ignore_sync", NULL, 280*afdb7e40Snonaka NULL, 0, &hvtimesync_ignore_sync, 0, 281*afdb7e40Snonaka CTL_CREATE, CTL_EOL); 282*afdb7e40Snonaka if (error) 283*afdb7e40Snonaka return error; 284*afdb7e40Snonaka error = sysctl_createv(&vsc->sc_log, 0, &node, NULL, 285*afdb7e40Snonaka CTLFLAG_READWRITE, 286*afdb7e40Snonaka CTLTYPE_INT, "sample_verbose", NULL, 287*afdb7e40Snonaka NULL, 0, &hvtimesnyc_sample_verbose, 0, 288*afdb7e40Snonaka CTL_CREATE, CTL_EOL); 289*afdb7e40Snonaka if (error) 290*afdb7e40Snonaka return error; 291*afdb7e40Snonaka error = sysctl_createv(&vsc->sc_log, 0, &node, NULL, 292*afdb7e40Snonaka CTLFLAG_READWRITE, 293*afdb7e40Snonaka CTLTYPE_INT, "sample_thresh", NULL, 294*afdb7e40Snonaka NULL, 0, &hvtimesync_sample_thresh, 0, 295*afdb7e40Snonaka CTL_CREATE, CTL_EOL); 296*afdb7e40Snonaka if (error) 297*afdb7e40Snonaka return error; 298*afdb7e40Snonaka 299*afdb7e40Snonaka return 0; 300*afdb7e40Snonaka } 301*afdb7e40Snonaka 302*afdb7e40Snonaka MODULE(MODULE_CLASS_DRIVER, hvtimesync, "vmbus"); 303*afdb7e40Snonaka 304*afdb7e40Snonaka #ifdef _MODULE 305*afdb7e40Snonaka #include "ioconf.c" 306*afdb7e40Snonaka #endif 307*afdb7e40Snonaka 308*afdb7e40Snonaka static int 309*afdb7e40Snonaka hvtimesync_modcmd(modcmd_t cmd, void *aux) 310*afdb7e40Snonaka { 311*afdb7e40Snonaka int error = 0; 312*afdb7e40Snonaka 313*afdb7e40Snonaka switch (cmd) { 314*afdb7e40Snonaka case MODULE_CMD_INIT: 315*afdb7e40Snonaka #ifdef _MODULE 316*afdb7e40Snonaka error = config_init_component(cfdriver_ioconf_hvtimesync, 317*afdb7e40Snonaka cfattach_ioconf_hvtimesync, cfdata_ioconf_hvtimesync); 318*afdb7e40Snonaka #endif 319*afdb7e40Snonaka break; 320*afdb7e40Snonaka 321*afdb7e40Snonaka case MODULE_CMD_FINI: 322*afdb7e40Snonaka #ifdef _MODULE 323*afdb7e40Snonaka error = config_fini_component(cfdriver_ioconf_hvtimesync, 324*afdb7e40Snonaka cfattach_ioconf_hvtimesync, cfdata_ioconf_hvtimesync); 325*afdb7e40Snonaka #endif 326*afdb7e40Snonaka break; 327*afdb7e40Snonaka 328*afdb7e40Snonaka case MODULE_CMD_AUTOUNLOAD: 329*afdb7e40Snonaka error = EBUSY; 330*afdb7e40Snonaka break; 331*afdb7e40Snonaka 332*afdb7e40Snonaka default: 333*afdb7e40Snonaka error = ENOTTY; 334*afdb7e40Snonaka break; 335*afdb7e40Snonaka } 336*afdb7e40Snonaka 337*afdb7e40Snonaka return error; 338*afdb7e40Snonaka } 339