Index: head/sys/dev/hyperv/utilities/hv_timesync.c =================================================================== --- head/sys/dev/hyperv/utilities/hv_timesync.c (revision 305280) +++ head/sys/dev/hyperv/utilities/hv_timesync.c (revision 305281) @@ -1,231 +1,223 @@ /*- * Copyright (c) 2014,2016 Microsoft Corp. * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice unmodified, this list of conditions, and the following * disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - * - * $FreeBSD$ */ -/* - * A common driver for all hyper-V util services. - */ +#include +__FBSDID("$FreeBSD$"); #include -#include #include -#include +#include #include -#include -#include #include -#include #include +#include #include #include -#include -#include "hv_util.h" +#include +#include + #include "vmbus_if.h" -#define HV_WLTIMEDELTA 116444736000000000L /* in 100ns unit */ -#define HV_ICTIMESYNCFLAG_PROBE 0 -#define HV_ICTIMESYNCFLAG_SYNC 1 -#define HV_ICTIMESYNCFLAG_SAMPLE 2 -#define HV_NANO_SEC_PER_SEC 1000000000 -#define HV_NANO_SEC_PER_MILLI_SEC 1000000 - static const struct vmbus_ic_desc vmbus_timesync_descs[] = { { .ic_guid = { .hv_guid = { 0x30, 0xe6, 0x27, 0x95, 0xae, 0xd0, 0x7b, 0x49, 0xad, 0xce, 0xe8, 0x0a, 0xb0, 0x17, 0x5c, 0xaf } }, .ic_desc = "Hyper-V Timesync" }, VMBUS_IC_DESC_END }; -struct hv_ictimesync_data { - uint64_t parenttime; - uint64_t childtime; - uint64_t roundtriptime; - uint8_t flags; -} __packed; - -/* - * Globals - */ SYSCTL_NODE(_hw, OID_AUTO, hvtimesync, CTLFLAG_RW | CTLFLAG_MPSAFE, NULL, "Hyper-V timesync interface"); -/* Ignore the sync request when set to 1. */ -static int ignore_sync_req = 0; -SYSCTL_INT(_hw_hvtimesync, OID_AUTO, ignore_sync_req, CTLFLAG_RWTUN, - &ignore_sync_req, 0, - "Ignore the sync request when set to 1."); +static int vmbus_ts_ignore_sync = 0; +SYSCTL_INT(_hw_hvtimesync, OID_AUTO, ignore_sync, CTLFLAG_RWTUN, + &vmbus_ts_ignore_sync, 0, "Ignore the sync request."); /* * Trigger sample sync when drift exceeds threshold (ms). * Ignore the sample request when set to 0. */ -static int sample_drift = 100; -SYSCTL_INT(_hw_hvtimesync, OID_AUTO, sample_drift, CTLFLAG_RWTUN, - &sample_drift, 0, - "Threshold that makes sample request trigger the sync."); +static int vmbus_ts_sample_thresh = 100; +SYSCTL_INT(_hw_hvtimesync, OID_AUTO, sample_thresh, CTLFLAG_RWTUN, + &vmbus_ts_sample_thresh, 0, + "Threshold that makes sample request trigger the sync (unit: ms)."); -/** - * @brief Synchronize time with host after reboot, restore, etc. - * - * ICTIMESYNCFLAG_SYNC flag bit indicates reboot, restore events of the VM. - * After reboot the flag ICTIMESYNCFLAG_SYNC is included in the first time - * message after the timesync channel is opened. Since the hv_utils module is - * loaded after hv_vmbus, the first message is usually missed. The other - * thing is, systime is automatically set to emulated hardware clock which may - * not be UTC time or in the same time zone. So, to override these effects, we - * use the first 50 time samples for initial system time setting. - */ -static inline -void hv_adj_guesttime(hv_util_sc *sc, uint64_t hosttime, uint8_t flags) +static int vmbus_ts_sample_verbose = 0; +SYSCTL_INT(_hw_hvtimesync, OID_AUTO, sample_verbose, CTLFLAG_RWTUN, + &vmbus_ts_sample_verbose, 0, "Increase sample request verbosity."); + +static void +vmbus_timesync(struct hv_util_sc *sc, uint64_t hvtime, uint8_t tsflags) { - struct timespec guest_ts, host_ts; - uint64_t host_tns, guest_tns; - int64_t diff; - int error; + struct timespec vm_ts; + uint64_t hv_ns, vm_ns; - host_tns = (hosttime - HV_WLTIMEDELTA) * 100; - host_ts.tv_sec = (time_t)(host_tns/HV_NANO_SEC_PER_SEC); - host_ts.tv_nsec = (long)(host_tns%HV_NANO_SEC_PER_SEC); + hv_ns = (hvtime - VMBUS_ICMSG_TS_BASE) * VMBUS_ICMSG_TS_FACTOR; + nanotime(&vm_ts); + vm_ns = (vm_ts.tv_sec * NANOSEC) + vm_ts.tv_nsec; - nanotime(&guest_ts); - guest_tns = guest_ts.tv_sec * HV_NANO_SEC_PER_SEC + guest_ts.tv_nsec; + if ((tsflags & VMBUS_ICMSG_TS_FLAG_SYNC) && !vmbus_ts_ignore_sync) { + struct timespec hv_ts; - if ((flags & HV_ICTIMESYNCFLAG_SYNC) != 0 && ignore_sync_req == 0) { if (bootverbose) { - device_printf(sc->ic_dev, "handle sync request " - "{host: %ju, guest: %ju}\n", - (uintmax_t)host_tns, (uintmax_t)guest_tns); + device_printf(sc->ic_dev, "apply sync request, " + "hv: %ju, vm: %ju\n", + (uintmax_t)hv_ns, (uintmax_t)vm_ns); } - - error = kern_clock_settime(curthread, CLOCK_REALTIME, - &host_ts); + hv_ts.tv_sec = hv_ns / NANOSEC; + hv_ts.tv_nsec = hv_ns % NANOSEC; + kern_clock_settime(curthread, CLOCK_REALTIME, &hv_ts); + /* Done! */ return; } - if ((flags & HV_ICTIMESYNCFLAG_SAMPLE) != 0 && sample_drift != 0) { - if (bootverbose) { - device_printf(sc->ic_dev, "handle sample request " - "{host: %ju, guest: %ju}\n", - (uintmax_t)host_tns, (uintmax_t)guest_tns); + if ((tsflags & VMBUS_ICMSG_TS_FLAG_SAMPLE) && + vmbus_ts_sample_thresh > 0) { + int64_t diff; + + if (vmbus_ts_sample_verbose) { + device_printf(sc->ic_dev, "sample request, " + "hv: %ju, vm: %ju\n", + (uintmax_t)hv_ns, (uintmax_t)vm_ns); } - diff = (int64_t)(host_tns - guest_tns) / HV_NANO_SEC_PER_MILLI_SEC; - if (diff > sample_drift || diff < -sample_drift) { - error = kern_clock_settime(curthread, CLOCK_REALTIME, - &host_ts); - if (bootverbose) - device_printf(sc->ic_dev, "trigger sample sync"); + if (hv_ns > vm_ns) + diff = hv_ns - vm_ns; + else + diff = vm_ns - hv_ns; + /* nanosec -> millisec */ + diff /= 1000000; + + if (diff > vmbus_ts_sample_thresh) { + struct timespec hv_ts; + + if (bootverbose) { + device_printf(sc->ic_dev, + "apply sample request, hv: %ju, vm: %ju\n", + (uintmax_t)hv_ns, (uintmax_t)vm_ns); + } + hv_ts.tv_sec = hv_ns / NANOSEC; + hv_ts.tv_nsec = hv_ns % NANOSEC; + kern_clock_settime(curthread, CLOCK_REALTIME, &hv_ts); } + /* Done */ return; } } -/** - * Time Sync Channel message handler - */ static void -hv_timesync_cb(struct vmbus_channel *channel, void *context) +vmbus_timesync_cb(struct vmbus_channel *chan, void *xsc) { - hv_vmbus_icmsg_hdr* icmsghdrp; - uint32_t recvlen; - uint64_t requestId; - int ret; - uint8_t* time_buf; - struct hv_ictimesync_data* timedatap; - hv_util_sc *softc; + struct hv_util_sc *sc = xsc; + struct vmbus_icmsg_hdr *hdr; + const struct vmbus_icmsg_timesync *msg; + int dlen, error; + uint64_t xactid; + void *data; - softc = (hv_util_sc*)context; - time_buf = softc->receive_buffer; + /* + * Receive request. + */ + data = sc->receive_buffer; + dlen = sc->ic_buflen; + error = vmbus_chan_recv(chan, data, &dlen, &xactid); + KASSERT(error != ENOBUFS, ("icbuf is not large enough")); + if (error) + return; - recvlen = softc->ic_buflen; - ret = vmbus_chan_recv(channel, time_buf, &recvlen, &requestId); - KASSERT(ret != ENOBUFS, ("hvtimesync recvbuf is not large enough")); - /* XXX check recvlen to make sure that it contains enough data */ + if (dlen < sizeof(*hdr)) { + device_printf(sc->ic_dev, "invalid data len %d\n", dlen); + return; + } + hdr = data; - if ((ret == 0) && recvlen > 0) { - icmsghdrp = (struct hv_vmbus_icmsg_hdr *) &time_buf[ - sizeof(struct hv_vmbus_pipe_hdr)]; - - if (icmsghdrp->icmsgtype == HV_ICMSGTYPE_NEGOTIATE) { - int error; - - error = vmbus_ic_negomsg(softc, time_buf, &recvlen); + /* + * Update request, which will be echoed back as response. + */ + switch (hdr->ic_type) { + case VMBUS_ICMSG_TYPE_NEGOTIATE: + error = vmbus_ic_negomsg(sc, data, &dlen); if (error) return; - } else { - timedatap = (struct hv_ictimesync_data *) &time_buf[ - sizeof(struct hv_vmbus_pipe_hdr) + - sizeof(struct hv_vmbus_icmsg_hdr)]; - hv_adj_guesttime(softc, timedatap->parenttime, timedatap->flags); - } + break; - icmsghdrp->icflags = HV_ICMSGHDRFLAG_TRANSACTION - | HV_ICMSGHDRFLAG_RESPONSE; + case VMBUS_ICMSG_TYPE_TIMESYNC: + if (dlen < sizeof(*msg)) { + device_printf(sc->ic_dev, "invalid timesync len %d\n", + dlen); + return; + } + msg = data; + vmbus_timesync(sc, msg->ic_hvtime, msg->ic_tsflags); + break; - vmbus_chan_send(channel, VMBUS_CHANPKT_TYPE_INBAND, 0, - time_buf, recvlen, requestId); + default: + device_printf(sc->ic_dev, "got 0x%08x icmsg\n", hdr->ic_type); + break; } + + /* + * Send response by echoing the updated request back. + */ + hdr->ic_flags = VMBUS_ICMSG_FLAG_XACT | VMBUS_ICMSG_FLAG_RESP; + error = vmbus_chan_send(chan, VMBUS_CHANPKT_TYPE_INBAND, 0, + data, dlen, xactid); + if (error) + device_printf(sc->ic_dev, "resp send failed: %d\n", error); } static int hv_timesync_probe(device_t dev) { return (vmbus_ic_probe(dev, vmbus_timesync_descs)); } static int hv_timesync_attach(device_t dev) { - return hv_util_attach(dev, hv_timesync_cb); -} -static int -hv_timesync_detach(device_t dev) -{ - return hv_util_detach(dev); + return (hv_util_attach(dev, vmbus_timesync_cb)); } static device_method_t timesync_methods[] = { /* Device interface */ DEVMETHOD(device_probe, hv_timesync_probe), DEVMETHOD(device_attach, hv_timesync_attach), - DEVMETHOD(device_detach, hv_timesync_detach), + DEVMETHOD(device_detach, hv_util_detach), { 0, 0 } }; static driver_t timesync_driver = { "hvtimesync", timesync_methods, sizeof(hv_util_sc)}; static devclass_t timesync_devclass; DRIVER_MODULE(hv_timesync, vmbus, timesync_driver, timesync_devclass, NULL, NULL); MODULE_VERSION(hv_timesync, 1); MODULE_DEPEND(hv_timesync, vmbus, 1, 1, 1); Index: head/sys/dev/hyperv/utilities/vmbus_icreg.h =================================================================== --- head/sys/dev/hyperv/utilities/vmbus_icreg.h (revision 305280) +++ head/sys/dev/hyperv/utilities/vmbus_icreg.h (revision 305281) @@ -1,105 +1,125 @@ /*- * Copyright (c) 2016 Microsoft Corp. * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice unmodified, this list of conditions, and the following * disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. * * $FreeBSD$ */ #ifndef _VMBUS_ICREG_H_ #define _VMBUS_ICREG_H_ #define VMBUS_ICMSG_TYPE_NEGOTIATE 0 #define VMBUS_ICMSG_TYPE_HEARTBEAT 1 #define VMBUS_ICMSG_TYPE_KVP 2 #define VMBUS_ICMSG_TYPE_SHUTDOWN 3 #define VMBUS_ICMSG_TYPE_TIMESYNC 4 #define VMBUS_ICMSG_TYPE_VSS 5 #define VMBUS_ICMSG_STATUS_OK 0x00000000 #define VMBUS_ICMSG_STATUS_FAIL 0x80004005 #define VMBUS_IC_VERSION(major, minor) ((major) | (((uint32_t)(minor)) << 16)) #define VMBUS_ICVER_MAJOR(ver) ((ver) & 0xffff) #define VMBUS_ICVER_MINOR(ver) (((ver) & 0xffff0000) >> 16) struct vmbus_pipe_hdr { uint32_t ph_flags; uint32_t ph_msgsz; } __packed; struct vmbus_icmsg_hdr { struct vmbus_pipe_hdr ic_pipe; uint32_t ic_fwver; /* framework version */ uint16_t ic_type; uint32_t ic_msgver; /* message version */ uint16_t ic_dsize; /* data size */ uint32_t ic_status; /* VMBUS_ICMSG_STATUS_ */ uint8_t ic_xactid; uint8_t ic_flags; /* VMBUS_ICMSG_FLAG_ */ uint8_t ic_rsvd[2]; } __packed; #define VMBUS_ICMSG_FLAG_XACT 0x0001 #define VMBUS_ICMSG_FLAG_REQ 0x0002 #define VMBUS_ICMSG_FLAG_RESP 0x0004 /* VMBUS_ICMSG_TYPE_NEGOTIATE */ struct vmbus_icmsg_negotiate { struct vmbus_icmsg_hdr ic_hdr; uint16_t ic_fwver_cnt; uint16_t ic_msgver_cnt; uint32_t ic_rsvd; /* * This version array contains two set of supported * versions: * - The first set consists of #ic_fwver_cnt supported framework * versions. * - The second set consists of #ic_msgver_cnt supported message * versions. */ uint32_t ic_ver[]; } __packed; /* VMBUS_ICMSG_TYPE_HEARTBEAT */ struct vmbus_icmsg_heartbeat { struct vmbus_icmsg_hdr ic_hdr; uint64_t ic_seq; uint32_t ic_rsvd[8]; } __packed; #define VMBUS_ICMSG_HEARTBEAT_SIZE_MIN \ __offsetof(struct vmbus_icmsg_heartbeat, ic_rsvd[0]) +/* VMBUS_ICMSG_TYPE_SHUTDOWN */ struct vmbus_icmsg_shutdown { struct vmbus_icmsg_hdr ic_hdr; uint32_t ic_code; uint32_t ic_timeo; uint32_t ic_haltflags; uint8_t ic_msg[2048]; } __packed; #define VMBUS_ICMSG_SHUTDOWN_SIZE_MIN \ __offsetof(struct vmbus_icmsg_shutdown, ic_msg[0]) + +/* VMBUS_ICMSG_TYPE_TIMESYNC */ +struct vmbus_icmsg_timesync { + struct vmbus_icmsg_hdr ic_hdr; + uint64_t ic_hvtime; + uint64_t ic_vmtime; + uint64_t ic_rtt; + uint8_t ic_tsflags; /* VMBUS_ICMSG_TS_FLAG_ */ +} __packed; + +#define VMBUS_ICMSG_TS_FLAG_SYNC 0x01 +#define VMBUS_ICMSG_TS_FLAG_SAMPLE 0x02 + +/* XXX consolidate w/ hyperv */ +#define VMBUS_ICMSG_TS_BASE 116444736000000000ULL +#define VMBUS_ICMSG_TS_FACTOR 100ULL +#ifndef NANOSEC +#define NANOSEC 1000000000ULL +#endif #endif /* !_VMBUS_ICREG_H_ */