summaryrefslogtreecommitdiffstats
path: root/sys/dev/hyperv/utilities
diff options
context:
space:
mode:
Diffstat (limited to 'sys/dev/hyperv/utilities')
-rw-r--r--sys/dev/hyperv/utilities/hv_kvp.c10
-rw-r--r--sys/dev/hyperv/utilities/hv_snapshot.c1061
-rw-r--r--sys/dev/hyperv/utilities/hv_snapshot.h56
-rw-r--r--sys/dev/hyperv/utilities/vmbus_heartbeat.c (renamed from sys/dev/hyperv/utilities/hv_heartbeat.c)62
-rw-r--r--sys/dev/hyperv/utilities/vmbus_ic.c (renamed from sys/dev/hyperv/utilities/hv_util.c)52
-rw-r--r--sys/dev/hyperv/utilities/vmbus_icreg.h14
-rw-r--r--sys/dev/hyperv/utilities/vmbus_icvar.h (renamed from sys/dev/hyperv/utilities/hv_util.h)27
-rw-r--r--sys/dev/hyperv/utilities/vmbus_shutdown.c (renamed from sys/dev/hyperv/utilities/hv_shutdown.c)62
-rw-r--r--sys/dev/hyperv/utilities/vmbus_timesync.c (renamed from sys/dev/hyperv/utilities/hv_timesync.c)113
9 files changed, 1310 insertions, 147 deletions
diff --git a/sys/dev/hyperv/utilities/hv_kvp.c b/sys/dev/hyperv/utilities/hv_kvp.c
index 3753c2f..48218b8 100644
--- a/sys/dev/hyperv/utilities/hv_kvp.c
+++ b/sys/dev/hyperv/utilities/hv_kvp.c
@@ -64,8 +64,8 @@ __FBSDID("$FreeBSD$");
#include <dev/hyperv/include/vmbus.h>
#include <dev/hyperv/utilities/hv_utilreg.h>
#include <dev/hyperv/utilities/vmbus_icreg.h>
+#include <dev/hyperv/utilities/vmbus_icvar.h>
-#include "hv_util.h"
#include "unicode.h"
#include "hv_kvp.h"
#include "vmbus_if.h"
@@ -128,7 +128,7 @@ static struct cdevsw hv_kvp_cdevsw =
* KVP transaction requests from the host.
*/
typedef struct hv_kvp_sc {
- struct hv_util_sc util_sc;
+ struct vmbus_ic_softc util_sc;
device_t dev;
/* Unless specified the pending mutex should be
@@ -590,7 +590,7 @@ hv_kvp_process_request(void *context, int pending)
hv_kvp_log_info("%s: entering hv_kvp_process_request\n", __func__);
sc = (hv_kvp_sc*)context;
- kvp_buf = sc->util_sc.receive_buffer;;
+ kvp_buf = sc->util_sc.ic_buf;
channel = vmbus_get_channel(sc->dev);
recvlen = sc->util_sc.ic_buflen;
@@ -885,7 +885,7 @@ hv_kvp_attach(device_t dev)
return (error);
sc->hv_kvp_dev->si_drv1 = sc;
- return hv_util_attach(dev, hv_kvp_callback);
+ return (vmbus_ic_attach(dev, hv_kvp_callback));
}
static int
@@ -900,7 +900,7 @@ hv_kvp_detach(device_t dev)
}
destroy_dev(sc->hv_kvp_dev);
- return hv_util_detach(dev);
+ return (vmbus_ic_detach(dev));
}
static device_method_t kvp_methods[] = {
diff --git a/sys/dev/hyperv/utilities/hv_snapshot.c b/sys/dev/hyperv/utilities/hv_snapshot.c
new file mode 100644
index 0000000..45defe1
--- /dev/null
+++ b/sys/dev/hyperv/utilities/hv_snapshot.c
@@ -0,0 +1,1061 @@
+/*-
+ * 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.
+ */
+
+#include <sys/cdefs.h>
+__FBSDID("$FreeBSD$");
+
+#include <sys/param.h>
+#include <sys/kernel.h>
+#include <sys/conf.h>
+#include <sys/uio.h>
+#include <sys/bus.h>
+#include <sys/malloc.h>
+#include <sys/mbuf.h>
+#include <sys/module.h>
+#include <sys/lock.h>
+#include <sys/taskqueue.h>
+#include <sys/selinfo.h>
+#include <sys/sysctl.h>
+#include <sys/poll.h>
+#include <sys/proc.h>
+#include <sys/queue.h>
+#include <sys/kthread.h>
+#include <sys/syscallsubr.h>
+#include <sys/sysproto.h>
+#include <sys/un.h>
+#include <sys/endian.h>
+#include <sys/sema.h>
+#include <sys/signal.h>
+#include <sys/syslog.h>
+#include <sys/systm.h>
+#include <sys/mutex.h>
+#include <sys/callout.h>
+
+#include <dev/hyperv/include/hyperv.h>
+#include <dev/hyperv/utilities/hv_utilreg.h>
+#include <dev/hyperv/utilities/vmbus_icreg.h>
+#include <dev/hyperv/utilities/vmbus_icvar.h>
+
+#include "hv_snapshot.h"
+#include "vmbus_if.h"
+
+#define VSS_MAJOR 5
+#define VSS_MINOR 0
+#define VSS_MSGVER VMBUS_IC_VERSION(VSS_MAJOR, VSS_MINOR)
+
+#define VSS_FWVER_MAJOR 3
+#define VSS_FWVER VMBUS_IC_VERSION(VSS_FWVER_MAJOR, 0)
+
+#define TIMEOUT_LIMIT (15) // seconds
+enum hv_vss_op {
+ VSS_OP_CREATE = 0,
+ VSS_OP_DELETE,
+ VSS_OP_HOT_BACKUP,
+ VSS_OP_GET_DM_INFO,
+ VSS_OP_BU_COMPLETE,
+ /*
+ * Following operations are only supported with IC version >= 5.0
+ */
+ VSS_OP_FREEZE, /* Freeze the file systems in the VM */
+ VSS_OP_THAW, /* Unfreeze the file systems */
+ VSS_OP_AUTO_RECOVER,
+ VSS_OP_COUNT /* Number of operations, must be last */
+};
+
+/*
+ * Header for all VSS messages.
+ */
+struct hv_vss_hdr {
+ struct vmbus_icmsg_hdr ic_hdr;
+ uint8_t operation;
+ uint8_t reserved[7];
+} __packed;
+
+
+/*
+ * Flag values for the hv_vss_check_feature. Here supports only
+ * one value.
+ */
+#define VSS_HBU_NO_AUTO_RECOVERY 0x00000005
+
+struct hv_vss_check_feature {
+ uint32_t flags;
+} __packed;
+
+struct hv_vss_check_dm_info {
+ uint32_t flags;
+} __packed;
+
+struct hv_vss_msg {
+ union {
+ struct hv_vss_hdr vss_hdr;
+ } hdr;
+ union {
+ struct hv_vss_check_feature vss_cf;
+ struct hv_vss_check_dm_info dm_info;
+ } body;
+} __packed;
+
+struct hv_vss_req {
+ struct hv_vss_opt_msg opt_msg; /* used to communicate with daemon */
+ struct hv_vss_msg msg; /* used to communicate with host */
+} __packed;
+
+/* hv_vss debug control */
+static int hv_vss_log = 0;
+
+#define hv_vss_log_error(...) do { \
+ if (hv_vss_log > 0) \
+ log(LOG_ERR, "hv_vss: " __VA_ARGS__); \
+} while (0)
+
+#define hv_vss_log_info(...) do { \
+ if (hv_vss_log > 1) \
+ log(LOG_INFO, "hv_vss: " __VA_ARGS__); \
+} while (0)
+
+static const struct vmbus_ic_desc vmbus_vss_descs[] = {
+ {
+ .ic_guid = { .hv_guid = {
+ 0x29, 0x2e, 0xfa, 0x35, 0x23, 0xea, 0x36, 0x42,
+ 0x96, 0xae, 0x3a, 0x6e, 0xba, 0xcb, 0xa4, 0x40} },
+ .ic_desc = "Hyper-V VSS"
+ },
+ VMBUS_IC_DESC_END
+};
+
+static const char * vss_opt_name[] = {"None", "VSSCheck", "Freeze", "Thaw"};
+
+/* character device prototypes */
+static d_open_t hv_vss_dev_open;
+static d_close_t hv_vss_dev_close;
+static d_poll_t hv_vss_dev_daemon_poll;
+static d_ioctl_t hv_vss_dev_daemon_ioctl;
+
+static d_open_t hv_appvss_dev_open;
+static d_close_t hv_appvss_dev_close;
+static d_poll_t hv_appvss_dev_poll;
+static d_ioctl_t hv_appvss_dev_ioctl;
+
+/* hv_vss character device structure */
+static struct cdevsw hv_vss_cdevsw =
+{
+ .d_version = D_VERSION,
+ .d_open = hv_vss_dev_open,
+ .d_close = hv_vss_dev_close,
+ .d_poll = hv_vss_dev_daemon_poll,
+ .d_ioctl = hv_vss_dev_daemon_ioctl,
+ .d_name = FS_VSS_DEV_NAME,
+};
+
+static struct cdevsw hv_appvss_cdevsw =
+{
+ .d_version = D_VERSION,
+ .d_open = hv_appvss_dev_open,
+ .d_close = hv_appvss_dev_close,
+ .d_poll = hv_appvss_dev_poll,
+ .d_ioctl = hv_appvss_dev_ioctl,
+ .d_name = APP_VSS_DEV_NAME,
+};
+
+struct hv_vss_sc;
+/*
+ * Global state to track cdev
+ */
+struct hv_vss_dev_sc {
+ /*
+ * msg was transferred from host to notify queue, and
+ * ack queue. Finally, it was recyled to free list.
+ */
+ STAILQ_HEAD(, hv_vss_req_internal) to_notify_queue;
+ STAILQ_HEAD(, hv_vss_req_internal) to_ack_queue;
+ struct hv_vss_sc *sc;
+ struct proc *proc_task;
+ struct selinfo hv_vss_selinfo;
+};
+/*
+ * Global state to track and synchronize the transaction requests from the host.
+ * The VSS allows user to register their function to do freeze/thaw for application.
+ * VSS kernel will notify both vss daemon and user application if it is registered.
+ * The implementation state transition is illustrated by:
+ * https://clovertrail.github.io/assets/vssdot.png
+ */
+typedef struct hv_vss_sc {
+ struct vmbus_ic_softc util_sc;
+ device_t dev;
+
+ struct task task;
+
+ /*
+ * mutex is used to protect access of list/queue,
+ * callout in request is also used this mutex.
+ */
+ struct mtx pending_mutex;
+ /*
+ * req_free_list contains all free items
+ */
+ LIST_HEAD(, hv_vss_req_internal) req_free_list;
+
+ /* Indicates if daemon registered with driver */
+ boolean_t register_done;
+
+ boolean_t app_register_done;
+
+ /* cdev for file system freeze/thaw */
+ struct cdev *hv_vss_dev;
+ /* cdev for application freeze/thaw */
+ struct cdev *hv_appvss_dev;
+
+ /* sc for app */
+ struct hv_vss_dev_sc app_sc;
+ /* sc for deamon */
+ struct hv_vss_dev_sc daemon_sc;
+} hv_vss_sc;
+
+typedef struct hv_vss_req_internal {
+ LIST_ENTRY(hv_vss_req_internal) link;
+ STAILQ_ENTRY(hv_vss_req_internal) slink;
+ struct hv_vss_req vss_req;
+
+ /* Rcv buffer for communicating with the host*/
+ uint8_t *rcv_buf;
+ /* Length of host message */
+ uint32_t host_msg_len;
+ /* Host message id */
+ uint64_t host_msg_id;
+
+ hv_vss_sc *sc;
+
+ struct callout callout;
+} hv_vss_req_internal;
+
+#define SEARCH_REMOVE_REQ_LOCKED(reqp, queue, link, tmp, id) \
+ do { \
+ STAILQ_FOREACH_SAFE(reqp, queue, link, tmp) { \
+ if (reqp->vss_req.opt_msg.msgid == id) { \
+ STAILQ_REMOVE(queue, \
+ reqp, hv_vss_req_internal, link); \
+ break; \
+ } \
+ } \
+ } while (0)
+
+static bool
+hv_vss_is_daemon_killed_after_launch(hv_vss_sc *sc)
+{
+ return (!sc->register_done && sc->daemon_sc.proc_task);
+}
+
+/*
+ * Callback routine that gets called whenever there is a message from host
+ */
+static void
+hv_vss_callback(struct vmbus_channel *chan __unused, void *context)
+{
+ hv_vss_sc *sc = (hv_vss_sc*)context;
+ if (hv_vss_is_daemon_killed_after_launch(sc))
+ hv_vss_log_info("%s: daemon was killed!\n", __func__);
+ if (sc->register_done || sc->daemon_sc.proc_task) {
+ hv_vss_log_info("%s: Queuing work item\n", __func__);
+ if (hv_vss_is_daemon_killed_after_launch(sc))
+ hv_vss_log_info("%s: daemon was killed!\n", __func__);
+ taskqueue_enqueue(taskqueue_thread, &sc->task);
+ } else {
+ hv_vss_log_info("%s: daemon has never been registered\n", __func__);
+ }
+ hv_vss_log_info("%s: received msg from host\n", __func__);
+}
+/*
+ * Send the response back to the host.
+ */
+static void
+hv_vss_respond_host(uint8_t *rcv_buf, struct vmbus_channel *ch,
+ uint32_t recvlen, uint64_t requestid, uint32_t error)
+{
+ struct vmbus_icmsg_hdr *hv_icmsg_hdrp;
+
+ hv_icmsg_hdrp = (struct vmbus_icmsg_hdr *)rcv_buf;
+
+ hv_icmsg_hdrp->ic_status = error;
+ hv_icmsg_hdrp->ic_flags = HV_ICMSGHDRFLAG_TRANSACTION | HV_ICMSGHDRFLAG_RESPONSE;
+
+ error = vmbus_chan_send(ch, VMBUS_CHANPKT_TYPE_INBAND, 0,
+ rcv_buf, recvlen, requestid);
+ if (error)
+ hv_vss_log_info("%s: hv_vss_respond_host: sendpacket error:%d\n",
+ __func__, error);
+}
+
+static void
+hv_vss_notify_host_result_locked(struct hv_vss_req_internal *reqp, uint32_t status)
+{
+ struct hv_vss_msg* msg = (struct hv_vss_msg *)reqp->rcv_buf;
+ hv_vss_sc *sc = reqp->sc;
+ if (reqp->vss_req.opt_msg.opt == HV_VSS_CHECK) {
+ msg->body.vss_cf.flags = VSS_HBU_NO_AUTO_RECOVERY;
+ }
+ hv_vss_log_info("%s, %s response %s to host\n", __func__,
+ vss_opt_name[reqp->vss_req.opt_msg.opt],
+ status == HV_S_OK ? "Success" : "Fail");
+ hv_vss_respond_host(reqp->rcv_buf, vmbus_get_channel(reqp->sc->dev),
+ reqp->host_msg_len, reqp->host_msg_id, status);
+ /* recycle the request */
+ LIST_INSERT_HEAD(&sc->req_free_list, reqp, link);
+}
+
+static void
+hv_vss_notify_host_result(struct hv_vss_req_internal *reqp, uint32_t status)
+{
+ mtx_lock(&reqp->sc->pending_mutex);
+ hv_vss_notify_host_result_locked(reqp, status);
+ mtx_unlock(&reqp->sc->pending_mutex);
+}
+
+static void
+hv_vss_cp_vssreq_to_user(struct hv_vss_req_internal *reqp,
+ struct hv_vss_opt_msg *userdata)
+{
+ struct hv_vss_req *hv_vss_dev_buf;
+ hv_vss_dev_buf = &reqp->vss_req;
+ hv_vss_dev_buf->opt_msg.opt = HV_VSS_NONE;
+ switch (reqp->vss_req.msg.hdr.vss_hdr.operation) {
+ case VSS_OP_FREEZE:
+ hv_vss_dev_buf->opt_msg.opt = HV_VSS_FREEZE;
+ break;
+ case VSS_OP_THAW:
+ hv_vss_dev_buf->opt_msg.opt = HV_VSS_THAW;
+ break;
+ case VSS_OP_HOT_BACKUP:
+ hv_vss_dev_buf->opt_msg.opt = HV_VSS_CHECK;
+ break;
+ }
+ *userdata = hv_vss_dev_buf->opt_msg;
+ hv_vss_log_info("%s, read data from user for "
+ "%s (%ju) \n", __func__, vss_opt_name[userdata->opt],
+ (uintmax_t)userdata->msgid);
+}
+
+/**
+ * Remove the request id from app notifiy or ack queue,
+ * and recyle the request by inserting it to free list.
+ *
+ * When app was notified but not yet sending ack, the request
+ * should locate in either notify queue or ack queue.
+ */
+static struct hv_vss_req_internal*
+hv_vss_drain_req_queue_locked(hv_vss_sc *sc, uint64_t req_id)
+{
+ struct hv_vss_req_internal *reqp, *tmp;
+ SEARCH_REMOVE_REQ_LOCKED(reqp, &sc->daemon_sc.to_notify_queue,
+ slink, tmp, req_id);
+ if (reqp == NULL)
+ SEARCH_REMOVE_REQ_LOCKED(reqp, &sc->daemon_sc.to_ack_queue,
+ slink, tmp, req_id);
+ if (reqp == NULL)
+ SEARCH_REMOVE_REQ_LOCKED(reqp, &sc->app_sc.to_notify_queue,
+ slink, tmp, req_id);
+ if (reqp == NULL)
+ SEARCH_REMOVE_REQ_LOCKED(reqp, &sc->app_sc.to_ack_queue, slink,
+ tmp, req_id);
+ return (reqp);
+}
+/**
+ * Actions for daemon who has been notified.
+ */
+static void
+hv_vss_notified(struct hv_vss_dev_sc *dev_sc, struct hv_vss_opt_msg *userdata)
+{
+ struct hv_vss_req_internal *reqp;
+ mtx_lock(&dev_sc->sc->pending_mutex);
+ if (!STAILQ_EMPTY(&dev_sc->to_notify_queue)) {
+ reqp = STAILQ_FIRST(&dev_sc->to_notify_queue);
+ hv_vss_cp_vssreq_to_user(reqp, userdata);
+ STAILQ_REMOVE_HEAD(&dev_sc->to_notify_queue, slink);
+ /* insert the msg to queue for write */
+ STAILQ_INSERT_TAIL(&dev_sc->to_ack_queue, reqp, slink);
+ userdata->status = VSS_SUCCESS;
+ } else {
+ /* Timeout occur, thus request was removed from queue. */
+ hv_vss_log_info("%s: notify queue is empty!\n", __func__);
+ userdata->status = VSS_FAIL;
+ }
+ mtx_unlock(&dev_sc->sc->pending_mutex);
+}
+
+static void
+hv_vss_notify(struct hv_vss_dev_sc *dev_sc, struct hv_vss_req_internal *reqp)
+{
+ uint32_t opt = reqp->vss_req.opt_msg.opt;
+ mtx_lock(&dev_sc->sc->pending_mutex);
+ STAILQ_INSERT_TAIL(&dev_sc->to_notify_queue, reqp, slink);
+ hv_vss_log_info("%s: issuing query %s (%ju) to %s\n", __func__,
+ vss_opt_name[opt], (uintmax_t)reqp->vss_req.opt_msg.msgid,
+ &dev_sc->sc->app_sc == dev_sc ? "app" : "daemon");
+ mtx_unlock(&dev_sc->sc->pending_mutex);
+ selwakeup(&dev_sc->hv_vss_selinfo);
+}
+
+/**
+ * Actions for daemon who has acknowledged.
+ */
+static void
+hv_vss_daemon_acked(struct hv_vss_dev_sc *dev_sc, struct hv_vss_opt_msg *userdata)
+{
+ struct hv_vss_req_internal *reqp, *tmp;
+ uint64_t req_id;
+ int opt;
+ uint32_t status;
+
+ opt = userdata->opt;
+ req_id = userdata->msgid;
+ status = userdata->status;
+ /* make sure the reserved fields are all zeros. */
+ memset(&userdata->reserved, 0, sizeof(struct hv_vss_opt_msg) -
+ __offsetof(struct hv_vss_opt_msg, reserved));
+ mtx_lock(&dev_sc->sc->pending_mutex);
+ SEARCH_REMOVE_REQ_LOCKED(reqp, &dev_sc->to_ack_queue, slink, tmp, req_id);
+ mtx_unlock(&dev_sc->sc->pending_mutex);
+ if (reqp == NULL) {
+ hv_vss_log_info("%s Timeout: fail to find daemon ack request\n",
+ __func__);
+ userdata->status = VSS_FAIL;
+ return;
+ }
+ KASSERT(opt == reqp->vss_req.opt_msg.opt, ("Mismatched VSS operation!"));
+ hv_vss_log_info("%s, get response %d from daemon for %s (%ju) \n", __func__,
+ status, vss_opt_name[opt], (uintmax_t)req_id);
+ switch (opt) {
+ case HV_VSS_CHECK:
+ case HV_VSS_FREEZE:
+ callout_drain(&reqp->callout);
+ hv_vss_notify_host_result(reqp,
+ status == VSS_SUCCESS ? HV_S_OK : HV_E_FAIL);
+ break;
+ case HV_VSS_THAW:
+ if (dev_sc->sc->app_register_done) {
+ if (status == VSS_SUCCESS) {
+ hv_vss_notify(&dev_sc->sc->app_sc, reqp);
+ } else {
+ /* handle error */
+ callout_drain(&reqp->callout);
+ hv_vss_notify_host_result(reqp, HV_E_FAIL);
+ }
+ } else {
+ callout_drain(&reqp->callout);
+ hv_vss_notify_host_result(reqp,
+ status == VSS_SUCCESS ? HV_S_OK : HV_E_FAIL);
+ }
+ break;
+ }
+}
+
+/**
+ * Actions for app who has acknowledged.
+ */
+static void
+hv_vss_app_acked(struct hv_vss_dev_sc *dev_sc, struct hv_vss_opt_msg *userdata)
+{
+ struct hv_vss_req_internal *reqp, *tmp;
+ uint64_t req_id;
+ int opt;
+ uint8_t status;
+
+ opt = userdata->opt;
+ req_id = userdata->msgid;
+ status = userdata->status;
+ /* make sure the reserved fields are all zeros. */
+ memset(&userdata->reserved, 0, sizeof(struct hv_vss_opt_msg) -
+ __offsetof(struct hv_vss_opt_msg, reserved));
+ mtx_lock(&dev_sc->sc->pending_mutex);
+ SEARCH_REMOVE_REQ_LOCKED(reqp, &dev_sc->to_ack_queue, slink, tmp, req_id);
+ mtx_unlock(&dev_sc->sc->pending_mutex);
+ if (reqp == NULL) {
+ hv_vss_log_info("%s Timeout: fail to find app ack request\n",
+ __func__);
+ userdata->status = VSS_FAIL;
+ return;
+ }
+ KASSERT(opt == reqp->vss_req.opt_msg.opt, ("Mismatched VSS operation!"));
+ hv_vss_log_info("%s, get response %d from app for %s (%ju) \n",
+ __func__, status, vss_opt_name[opt], (uintmax_t)req_id);
+ if (dev_sc->sc->register_done) {
+ switch (opt) {
+ case HV_VSS_CHECK:
+ case HV_VSS_FREEZE:
+ if (status == VSS_SUCCESS) {
+ hv_vss_notify(&dev_sc->sc->daemon_sc, reqp);
+ } else {
+ /* handle error */
+ callout_drain(&reqp->callout);
+ hv_vss_notify_host_result(reqp, HV_E_FAIL);
+ }
+ break;
+ case HV_VSS_THAW:
+ callout_drain(&reqp->callout);
+ hv_vss_notify_host_result(reqp,
+ status == VSS_SUCCESS ? HV_S_OK : HV_E_FAIL);
+ break;
+ }
+ } else {
+ hv_vss_log_info("%s, Fatal: vss daemon was killed\n", __func__);
+ }
+}
+
+static int
+hv_vss_dev_open(struct cdev *dev, int oflags, int devtype, struct thread *td)
+{
+ struct proc *td_proc;
+ td_proc = td->td_proc;
+
+ struct hv_vss_dev_sc *dev_sc = (struct hv_vss_dev_sc*)dev->si_drv1;
+ hv_vss_log_info("%s: %s opens device \"%s\" successfully.\n",
+ __func__, td_proc->p_comm, FS_VSS_DEV_NAME);
+
+ if (dev_sc->sc->register_done)
+ return (EBUSY);
+
+ dev_sc->sc->register_done = true;
+ hv_vss_callback(vmbus_get_channel(dev_sc->sc->dev), dev_sc->sc);
+
+ dev_sc->proc_task = curproc;
+ return (0);
+}
+
+static int
+hv_vss_dev_close(struct cdev *dev, int fflag __unused, int devtype __unused,
+ struct thread *td)
+{
+ struct proc *td_proc;
+ td_proc = td->td_proc;
+
+ struct hv_vss_dev_sc *dev_sc = (struct hv_vss_dev_sc*)dev->si_drv1;
+
+ hv_vss_log_info("%s: %s closes device \"%s\"\n",
+ __func__, td_proc->p_comm, FS_VSS_DEV_NAME);
+ dev_sc->sc->register_done = false;
+ return (0);
+}
+
+static int
+hv_vss_dev_daemon_ioctl(struct cdev *dev, u_long cmd, caddr_t data, int flag,
+ struct thread *td)
+{
+ struct proc *td_proc;
+ struct hv_vss_dev_sc *sc;
+
+ td_proc = td->td_proc;
+ sc = (struct hv_vss_dev_sc*)dev->si_drv1;
+
+ hv_vss_log_info("%s: %s invoked vss ioctl\n", __func__, td_proc->p_comm);
+
+ struct hv_vss_opt_msg* userdata = (struct hv_vss_opt_msg*)data;
+ switch(cmd) {
+ case IOCHVVSSREAD:
+ hv_vss_notified(sc, userdata);
+ break;
+ case IOCHVVSSWRITE:
+ hv_vss_daemon_acked(sc, userdata);
+ break;
+ }
+ return (0);
+}
+
+/*
+ * hv_vss_daemon poll invokes this function to check if data is available
+ * for daemon to read.
+ */
+static int
+hv_vss_dev_daemon_poll(struct cdev *dev, int events, struct thread *td)
+{
+ int revent = 0;
+ struct hv_vss_dev_sc *dev_sc = (struct hv_vss_dev_sc*)dev->si_drv1;
+
+ mtx_lock(&dev_sc->sc->pending_mutex);
+ /**
+ * if there is data ready, inform daemon's poll
+ */
+ if (!STAILQ_EMPTY(&dev_sc->to_notify_queue))
+ revent = POLLIN;
+ if (revent == 0)
+ selrecord(td, &dev_sc->hv_vss_selinfo);
+ hv_vss_log_info("%s return 0x%x\n", __func__, revent);
+ mtx_unlock(&dev_sc->sc->pending_mutex);
+ return (revent);
+}
+
+static int
+hv_appvss_dev_open(struct cdev *dev, int oflags, int devtype, struct thread *td)
+{
+ struct proc *td_proc;
+ td_proc = td->td_proc;
+
+ struct hv_vss_dev_sc *dev_sc = (struct hv_vss_dev_sc*)dev->si_drv1;
+ hv_vss_log_info("%s: %s opens device \"%s\" successfully.\n",
+ __func__, td_proc->p_comm, APP_VSS_DEV_NAME);
+
+ if (dev_sc->sc->app_register_done)
+ return (EBUSY);
+
+ dev_sc->sc->app_register_done = true;
+ dev_sc->proc_task = curproc;
+ return (0);
+}
+
+static int
+hv_appvss_dev_close(struct cdev *dev, int fflag __unused, int devtype __unused,
+ struct thread *td)
+{
+ struct proc *td_proc;
+ td_proc = td->td_proc;
+
+ struct hv_vss_dev_sc *dev_sc = (struct hv_vss_dev_sc*)dev->si_drv1;
+
+ hv_vss_log_info("%s: %s closes device \"%s\".\n",
+ __func__, td_proc->p_comm, APP_VSS_DEV_NAME);
+ dev_sc->sc->app_register_done = false;
+ return (0);
+}
+
+static int
+hv_appvss_dev_ioctl(struct cdev *dev, u_long cmd, caddr_t data, int flag,
+ struct thread *td)
+{
+ struct proc *td_proc;
+ struct hv_vss_dev_sc *dev_sc;
+
+ td_proc = td->td_proc;
+ dev_sc = (struct hv_vss_dev_sc*)dev->si_drv1;
+
+ hv_vss_log_info("%s: %s invoked vss ioctl\n", __func__, td_proc->p_comm);
+
+ struct hv_vss_opt_msg* userdata = (struct hv_vss_opt_msg*)data;
+ switch(cmd) {
+ case IOCHVVSSREAD:
+ hv_vss_notified(dev_sc, userdata);
+ break;
+ case IOCHVVSSWRITE:
+ hv_vss_app_acked(dev_sc, userdata);
+ break;
+ }
+ return (0);
+}
+
+/*
+ * hv_vss_daemon poll invokes this function to check if data is available
+ * for daemon to read.
+ */
+static int
+hv_appvss_dev_poll(struct cdev *dev, int events, struct thread *td)
+{
+ int revent = 0;
+ struct hv_vss_dev_sc *dev_sc = (struct hv_vss_dev_sc*)dev->si_drv1;
+
+ mtx_lock(&dev_sc->sc->pending_mutex);
+ /**
+ * if there is data ready, inform daemon's poll
+ */
+ if (!STAILQ_EMPTY(&dev_sc->to_notify_queue))
+ revent = POLLIN;
+ if (revent == 0)
+ selrecord(td, &dev_sc->hv_vss_selinfo);
+ hv_vss_log_info("%s return 0x%x\n", __func__, revent);
+ mtx_unlock(&dev_sc->sc->pending_mutex);
+ return (revent);
+}
+
+static void
+hv_vss_timeout(void *arg)
+{
+ hv_vss_req_internal *reqp = arg;
+ hv_vss_req_internal *request;
+ hv_vss_sc* sc = reqp->sc;
+ uint64_t req_id = reqp->vss_req.opt_msg.msgid;
+ /* This thread is locked */
+ KASSERT(mtx_owned(&sc->pending_mutex), ("mutex lock is not owned!"));
+ request = hv_vss_drain_req_queue_locked(sc, req_id);
+ KASSERT(request != NULL, ("timeout but fail to find request"));
+ hv_vss_notify_host_result_locked(reqp, HV_E_FAIL);
+}
+
+/*
+ * This routine is called whenever a message is received from the host
+ */
+static void
+hv_vss_init_req(hv_vss_req_internal *reqp,
+ uint32_t recvlen, uint64_t requestid, uint8_t *vss_buf, hv_vss_sc *sc)
+{
+ struct timespec vm_ts;
+ struct hv_vss_msg* msg = (struct hv_vss_msg *)vss_buf;
+
+ memset(reqp, 0, __offsetof(hv_vss_req_internal, callout));
+ reqp->host_msg_len = recvlen;
+ reqp->host_msg_id = requestid;
+ reqp->rcv_buf = vss_buf;
+ reqp->sc = sc;
+ memcpy(&reqp->vss_req.msg,
+ (struct hv_vss_msg *)vss_buf, sizeof(struct hv_vss_msg));
+ /* set the opt for users */
+ switch (msg->hdr.vss_hdr.operation) {
+ case VSS_OP_FREEZE:
+ reqp->vss_req.opt_msg.opt = HV_VSS_FREEZE;
+ break;
+ case VSS_OP_THAW:
+ reqp->vss_req.opt_msg.opt = HV_VSS_THAW;
+ break;
+ case VSS_OP_HOT_BACKUP:
+ reqp->vss_req.opt_msg.opt = HV_VSS_CHECK;
+ break;
+ }
+ /* Use a timestamp as msg request ID */
+ nanotime(&vm_ts);
+ reqp->vss_req.opt_msg.msgid = (vm_ts.tv_sec * NANOSEC) + vm_ts.tv_nsec;
+}
+
+static hv_vss_req_internal*
+hv_vss_get_new_req_locked(hv_vss_sc *sc)
+{
+ hv_vss_req_internal *reqp;
+ if (!STAILQ_EMPTY(&sc->daemon_sc.to_notify_queue) ||
+ !STAILQ_EMPTY(&sc->daemon_sc.to_ack_queue) ||
+ !STAILQ_EMPTY(&sc->app_sc.to_notify_queue) ||
+ !STAILQ_EMPTY(&sc->app_sc.to_ack_queue)) {
+ /*
+ * There is request coming from host before
+ * finishing previous requests
+ */
+ hv_vss_log_info("%s: Warning: there is new request "
+ "coming before finishing previous requests\n", __func__);
+ return (NULL);
+ }
+ if (LIST_EMPTY(&sc->req_free_list)) {
+ /* TODO Error: no buffer */
+ hv_vss_log_info("Error: No buffer\n");
+ return (NULL);
+ }
+ reqp = LIST_FIRST(&sc->req_free_list);
+ LIST_REMOVE(reqp, link);
+ return (reqp);
+}
+
+static void
+hv_vss_start_notify(hv_vss_req_internal *reqp, uint32_t opt)
+{
+ hv_vss_sc *sc = reqp->sc;
+ /*
+ * Freeze/Check notification sequence: kernel -> app -> daemon(fs)
+ * Thaw notification sequence: kernel -> daemon(fs) -> app
+ *
+ * We should wake up the daemon, in case it's doing poll().
+ * The response should be received after 5s, otherwise, trigger timeout.
+ */
+ switch (opt) {
+ case VSS_OP_FREEZE:
+ case VSS_OP_HOT_BACKUP:
+ if (sc->app_register_done)
+ hv_vss_notify(&sc->app_sc, reqp);
+ else
+ hv_vss_notify(&sc->daemon_sc, reqp);
+ callout_reset(&reqp->callout, TIMEOUT_LIMIT * hz,
+ hv_vss_timeout, reqp);
+ break;
+ case VSS_OP_THAW:
+ hv_vss_notify(&sc->daemon_sc, reqp);
+ callout_reset(&reqp->callout, TIMEOUT_LIMIT * hz,
+ hv_vss_timeout, reqp);
+ break;
+ }
+}
+
+/*
+ * Function to read the vss request buffer from host
+ * and interact with daemon
+ */
+static void
+hv_vss_process_request(void *context, int pending __unused)
+{
+ uint8_t *vss_buf;
+ struct vmbus_channel *channel;
+ uint32_t recvlen = 0;
+ uint64_t requestid;
+ struct vmbus_icmsg_hdr *icmsghdrp;
+ int ret = 0;
+ hv_vss_sc *sc;
+ hv_vss_req_internal *reqp;
+
+ hv_vss_log_info("%s: entering hv_vss_process_request\n", __func__);
+
+ sc = (hv_vss_sc*)context;
+ vss_buf = sc->util_sc.ic_buf;
+ channel = vmbus_get_channel(sc->dev);
+
+ recvlen = sc->util_sc.ic_buflen;
+ ret = vmbus_chan_recv(channel, vss_buf, &recvlen, &requestid);
+ KASSERT(ret != ENOBUFS, ("hvvss recvbuf is not large enough"));
+ /* XXX check recvlen to make sure that it contains enough data */
+
+ while ((ret == 0) && (recvlen > 0)) {
+ icmsghdrp = (struct vmbus_icmsg_hdr *)vss_buf;
+
+ if (icmsghdrp->ic_type == HV_ICMSGTYPE_NEGOTIATE) {
+ ret = vmbus_ic_negomsg(&sc->util_sc, vss_buf,
+ &recvlen, VSS_FWVER, VSS_MSGVER);
+ hv_vss_respond_host(vss_buf, vmbus_get_channel(sc->dev),
+ recvlen, requestid, ret);
+ hv_vss_log_info("%s: version negotiated\n", __func__);
+ } else if (!hv_vss_is_daemon_killed_after_launch(sc)) {
+ struct hv_vss_msg* msg = (struct hv_vss_msg *)vss_buf;
+ switch(msg->hdr.vss_hdr.operation) {
+ case VSS_OP_FREEZE:
+ case VSS_OP_THAW:
+ case VSS_OP_HOT_BACKUP:
+ mtx_lock(&sc->pending_mutex);
+ reqp = hv_vss_get_new_req_locked(sc);
+ mtx_unlock(&sc->pending_mutex);
+ if (reqp == NULL) {
+ /* ignore this request from host */
+ break;
+ }
+ hv_vss_init_req(reqp, recvlen, requestid, vss_buf, sc);
+ hv_vss_log_info("%s: receive %s (%ju) from host\n",
+ __func__,
+ vss_opt_name[reqp->vss_req.opt_msg.opt],
+ (uintmax_t)reqp->vss_req.opt_msg.msgid);
+ hv_vss_start_notify(reqp, msg->hdr.vss_hdr.operation);
+ break;
+ case VSS_OP_GET_DM_INFO:
+ hv_vss_log_info("%s: receive GET_DM_INFO from host\n",
+ __func__);
+ msg->body.dm_info.flags = 0;
+ hv_vss_respond_host(vss_buf, vmbus_get_channel(sc->dev),
+ recvlen, requestid, HV_S_OK);
+ break;
+ default:
+ device_printf(sc->dev, "Unknown opt from host: %d\n",
+ msg->hdr.vss_hdr.operation);
+ break;
+ }
+ } else {
+ /* daemon was killed for some reason after it was launched */
+ struct hv_vss_msg* msg = (struct hv_vss_msg *)vss_buf;
+ switch(msg->hdr.vss_hdr.operation) {
+ case VSS_OP_FREEZE:
+ hv_vss_log_info("%s: response fail for FREEZE\n",
+ __func__);
+ break;
+ case VSS_OP_THAW:
+ hv_vss_log_info("%s: response fail for THAW\n",
+ __func__);
+ break;
+ case VSS_OP_HOT_BACKUP:
+ hv_vss_log_info("%s: response fail for HOT_BACKUP\n",
+ __func__);
+ msg->body.vss_cf.flags = VSS_HBU_NO_AUTO_RECOVERY;
+ break;
+ case VSS_OP_GET_DM_INFO:
+ hv_vss_log_info("%s: response fail for GET_DM_INFO\n",
+ __func__);
+ msg->body.dm_info.flags = 0;
+ break;
+ default:
+ device_printf(sc->dev, "Unknown opt from host: %d\n",
+ msg->hdr.vss_hdr.operation);
+ break;
+ }
+ hv_vss_respond_host(vss_buf, vmbus_get_channel(sc->dev),
+ recvlen, requestid, HV_E_FAIL);
+ }
+ /*
+ * Try reading next buffer
+ */
+ recvlen = sc->util_sc.ic_buflen;
+ ret = vmbus_chan_recv(channel, vss_buf, &recvlen, &requestid);
+ KASSERT(ret != ENOBUFS, ("hvvss recvbuf is not large enough"));
+ /* XXX check recvlen to make sure that it contains enough data */
+
+ hv_vss_log_info("%s: read: context %p, ret =%d, recvlen=%d\n",
+ __func__, context, ret, recvlen);
+ }
+}
+
+static int
+hv_vss_probe(device_t dev)
+{
+ return (vmbus_ic_probe(dev, vmbus_vss_descs));
+}
+
+static int
+hv_vss_init_send_receive_queue(device_t dev)
+{
+ hv_vss_sc *sc = (hv_vss_sc*)device_get_softc(dev);
+ int i;
+ const int max_list = 4; /* It is big enough for the list */
+ struct hv_vss_req_internal* reqp;
+
+ LIST_INIT(&sc->req_free_list);
+ STAILQ_INIT(&sc->daemon_sc.to_notify_queue);
+ STAILQ_INIT(&sc->daemon_sc.to_ack_queue);
+ STAILQ_INIT(&sc->app_sc.to_notify_queue);
+ STAILQ_INIT(&sc->app_sc.to_ack_queue);
+
+ for (i = 0; i < max_list; i++) {
+ reqp = malloc(sizeof(struct hv_vss_req_internal),
+ M_DEVBUF, M_WAITOK|M_ZERO);
+ LIST_INSERT_HEAD(&sc->req_free_list, reqp, link);
+ callout_init_mtx(&reqp->callout, &sc->pending_mutex, 0);
+ }
+ return (0);
+}
+
+static int
+hv_vss_destroy_send_receive_queue(device_t dev)
+{
+ hv_vss_sc *sc = (hv_vss_sc*)device_get_softc(dev);
+ hv_vss_req_internal* reqp;
+
+ while (!LIST_EMPTY(&sc->req_free_list)) {
+ reqp = LIST_FIRST(&sc->req_free_list);
+ LIST_REMOVE(reqp, link);
+ free(reqp, M_DEVBUF);
+ }
+
+ while (!STAILQ_EMPTY(&sc->daemon_sc.to_notify_queue)) {
+ reqp = STAILQ_FIRST(&sc->daemon_sc.to_notify_queue);
+ STAILQ_REMOVE_HEAD(&sc->daemon_sc.to_notify_queue, slink);
+ free(reqp, M_DEVBUF);
+ }
+
+ while (!STAILQ_EMPTY(&sc->daemon_sc.to_ack_queue)) {
+ reqp = STAILQ_FIRST(&sc->daemon_sc.to_ack_queue);
+ STAILQ_REMOVE_HEAD(&sc->daemon_sc.to_ack_queue, slink);
+ free(reqp, M_DEVBUF);
+ }
+
+ while (!STAILQ_EMPTY(&sc->app_sc.to_notify_queue)) {
+ reqp = STAILQ_FIRST(&sc->app_sc.to_notify_queue);
+ STAILQ_REMOVE_HEAD(&sc->app_sc.to_notify_queue, slink);
+ free(reqp, M_DEVBUF);
+ }
+
+ while (!STAILQ_EMPTY(&sc->app_sc.to_ack_queue)) {
+ reqp = STAILQ_FIRST(&sc->app_sc.to_ack_queue);
+ STAILQ_REMOVE_HEAD(&sc->app_sc.to_ack_queue, slink);
+ free(reqp, M_DEVBUF);
+ }
+ return (0);
+}
+
+static int
+hv_vss_attach(device_t dev)
+{
+ int error;
+ struct sysctl_oid_list *child;
+ struct sysctl_ctx_list *ctx;
+
+ hv_vss_sc *sc = (hv_vss_sc*)device_get_softc(dev);
+
+ sc->dev = dev;
+ mtx_init(&sc->pending_mutex, "hv_vss pending mutex", NULL, MTX_DEF);
+
+ ctx = device_get_sysctl_ctx(dev);
+ child = SYSCTL_CHILDREN(device_get_sysctl_tree(dev));
+
+ SYSCTL_ADD_INT(ctx, child, OID_AUTO, "hv_vss_log",
+ CTLFLAG_RWTUN, &hv_vss_log, 0, "Hyperv VSS service log level");
+
+ TASK_INIT(&sc->task, 0, hv_vss_process_request, sc);
+ hv_vss_init_send_receive_queue(dev);
+ /* create character device for file system freeze/thaw */
+ error = make_dev_p(MAKEDEV_CHECKNAME | MAKEDEV_WAITOK,
+ &sc->hv_vss_dev,
+ &hv_vss_cdevsw,
+ 0,
+ UID_ROOT,
+ GID_WHEEL,
+ 0640,
+ FS_VSS_DEV_NAME);
+
+ if (error != 0) {
+ hv_vss_log_info("Fail to create '%s': %d\n", FS_VSS_DEV_NAME, error);
+ return (error);
+ }
+ sc->hv_vss_dev->si_drv1 = &sc->daemon_sc;
+ sc->daemon_sc.sc = sc;
+ /* create character device for application freeze/thaw */
+ error = make_dev_p(MAKEDEV_CHECKNAME | MAKEDEV_WAITOK,
+ &sc->hv_appvss_dev,
+ &hv_appvss_cdevsw,
+ 0,
+ UID_ROOT,
+ GID_WHEEL,
+ 0640,
+ APP_VSS_DEV_NAME);
+
+ if (error != 0) {
+ hv_vss_log_info("Fail to create '%s': %d\n", APP_VSS_DEV_NAME, error);
+ return (error);
+ }
+ sc->hv_appvss_dev->si_drv1 = &sc->app_sc;
+ sc->app_sc.sc = sc;
+
+ return (vmbus_ic_attach(dev, hv_vss_callback));
+}
+
+static int
+hv_vss_detach(device_t dev)
+{
+ hv_vss_sc *sc = (hv_vss_sc*)device_get_softc(dev);
+ mtx_destroy(&sc->pending_mutex);
+ if (sc->daemon_sc.proc_task != NULL) {
+ PROC_LOCK(sc->daemon_sc.proc_task);
+ kern_psignal(sc->daemon_sc.proc_task, SIGKILL);
+ PROC_UNLOCK(sc->daemon_sc.proc_task);
+ }
+ if (sc->app_sc.proc_task != NULL) {
+ PROC_LOCK(sc->app_sc.proc_task);
+ kern_psignal(sc->app_sc.proc_task, SIGKILL);
+ PROC_UNLOCK(sc->app_sc.proc_task);
+ }
+ hv_vss_destroy_send_receive_queue(dev);
+ destroy_dev(sc->hv_vss_dev);
+ destroy_dev(sc->hv_appvss_dev);
+ return (vmbus_ic_detach(dev));
+}
+
+static device_method_t vss_methods[] = {
+ /* Device interface */
+ DEVMETHOD(device_probe, hv_vss_probe),
+ DEVMETHOD(device_attach, hv_vss_attach),
+ DEVMETHOD(device_detach, hv_vss_detach),
+ { 0, 0 }
+};
+
+static driver_t vss_driver = { "hvvss", vss_methods, sizeof(hv_vss_sc)};
+
+static devclass_t vss_devclass;
+
+DRIVER_MODULE(hv_vss, vmbus, vss_driver, vss_devclass, NULL, NULL);
+MODULE_VERSION(hv_vss, 1);
+MODULE_DEPEND(hv_vss, vmbus, 1, 1, 1);
diff --git a/sys/dev/hyperv/utilities/hv_snapshot.h b/sys/dev/hyperv/utilities/hv_snapshot.h
new file mode 100644
index 0000000..e3c9e0c
--- /dev/null
+++ b/sys/dev/hyperv/utilities/hv_snapshot.h
@@ -0,0 +1,56 @@
+/*-
+ * 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 _VSS_H
+#define _VSS_H
+#include <sys/ioccom.h>
+#define FS_VSS_DEV_NAME "hv_fsvss_dev"
+#define APP_VSS_DEV_NAME "hv_appvss_dev"
+
+#define VSS_DEV(VSS) "/dev/"VSS
+
+#define VSS_SUCCESS 0x00000000
+#define VSS_FAIL 0x00000001
+
+enum hv_vss_op_t {
+ HV_VSS_NONE = 0,
+ HV_VSS_CHECK,
+ HV_VSS_FREEZE,
+ HV_VSS_THAW,
+ HV_VSS_COUNT
+};
+
+struct hv_vss_opt_msg {
+ uint32_t opt; /* operation */
+ uint32_t status; /* 0 for success, 1 for error */
+ uint64_t msgid; /* an ID used to identify the transaction */
+ uint8_t reserved[48]; /* reserved values are all zeroes */
+};
+#define IOCHVVSSREAD _IOR('v', 2, struct hv_vss_opt_msg)
+#define IOCHVVSSWRITE _IOW('v', 3, struct hv_vss_opt_msg)
+#endif
diff --git a/sys/dev/hyperv/utilities/hv_heartbeat.c b/sys/dev/hyperv/utilities/vmbus_heartbeat.c
index 8fc7a09..f15b948 100644
--- a/sys/dev/hyperv/utilities/hv_heartbeat.c
+++ b/sys/dev/hyperv/utilities/vmbus_heartbeat.c
@@ -35,10 +35,8 @@ __FBSDID("$FreeBSD$");
#include <dev/hyperv/include/hyperv.h>
#include <dev/hyperv/include/vmbus.h>
-#include <dev/hyperv/utilities/hv_util.h>
#include <dev/hyperv/utilities/vmbus_icreg.h>
-
-#include "vmbus_if.h"
+#include <dev/hyperv/utilities/vmbus_icvar.h>
#define VMBUS_HEARTBEAT_FWVER_MAJOR 3
#define VMBUS_HEARTBEAT_FWVER \
@@ -48,6 +46,9 @@ __FBSDID("$FreeBSD$");
#define VMBUS_HEARTBEAT_MSGVER \
VMBUS_IC_VERSION(VMBUS_HEARTBEAT_MSGVER_MAJOR, 0)
+static int vmbus_heartbeat_probe(device_t);
+static int vmbus_heartbeat_attach(device_t);
+
static const struct vmbus_ic_desc vmbus_heartbeat_descs[] = {
{
.ic_guid = { .hv_guid = {
@@ -58,10 +59,31 @@ static const struct vmbus_ic_desc vmbus_heartbeat_descs[] = {
VMBUS_IC_DESC_END
};
+static device_method_t vmbus_heartbeat_methods[] = {
+ /* Device interface */
+ DEVMETHOD(device_probe, vmbus_heartbeat_probe),
+ DEVMETHOD(device_attach, vmbus_heartbeat_attach),
+ DEVMETHOD(device_detach, vmbus_ic_detach),
+ DEVMETHOD_END
+};
+
+static driver_t vmbus_heartbeat_driver = {
+ "hvheartbeat",
+ vmbus_heartbeat_methods,
+ sizeof(struct vmbus_ic_softc)
+};
+
+static devclass_t vmbus_heartbeat_devclass;
+
+DRIVER_MODULE(hv_heartbeat, vmbus, vmbus_heartbeat_driver,
+ vmbus_heartbeat_devclass, NULL, NULL);
+MODULE_VERSION(hv_heartbeat, 1);
+MODULE_DEPEND(hv_heartbeat, vmbus, 1, 1, 1);
+
static void
vmbus_heartbeat_cb(struct vmbus_channel *chan, void *xsc)
{
- struct hv_util_sc *sc = xsc;
+ struct vmbus_ic_softc *sc = xsc;
struct vmbus_icmsg_hdr *hdr;
int dlen, error;
uint64_t xactid;
@@ -70,7 +92,7 @@ vmbus_heartbeat_cb(struct vmbus_channel *chan, void *xsc)
/*
* Receive request.
*/
- data = sc->receive_buffer;
+ data = sc->ic_buf;
dlen = sc->ic_buflen;
error = vmbus_chan_recv(chan, data, &dlen, &xactid);
KASSERT(error != ENOBUFS, ("icbuf is not large enough"));
@@ -110,41 +132,21 @@ vmbus_heartbeat_cb(struct vmbus_channel *chan, void *xsc)
}
/*
- * Send response by echoing the updated request back.
+ * Send response by echoing the 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);
+ vmbus_ic_sendresp(sc, chan, data, dlen, xactid);
}
static int
-hv_heartbeat_probe(device_t dev)
+vmbus_heartbeat_probe(device_t dev)
{
return (vmbus_ic_probe(dev, vmbus_heartbeat_descs));
}
static int
-hv_heartbeat_attach(device_t dev)
+vmbus_heartbeat_attach(device_t dev)
{
- return (hv_util_attach(dev, vmbus_heartbeat_cb));
+ return (vmbus_ic_attach(dev, vmbus_heartbeat_cb));
}
-
-static device_method_t heartbeat_methods[] = {
- /* Device interface */
- DEVMETHOD(device_probe, hv_heartbeat_probe),
- DEVMETHOD(device_attach, hv_heartbeat_attach),
- DEVMETHOD(device_detach, hv_util_detach),
- { 0, 0 }
-};
-
-static driver_t heartbeat_driver = { "hvheartbeat", heartbeat_methods, sizeof(hv_util_sc)};
-
-static devclass_t heartbeat_devclass;
-
-DRIVER_MODULE(hv_heartbeat, vmbus, heartbeat_driver, heartbeat_devclass, NULL, NULL);
-MODULE_VERSION(hv_heartbeat, 1);
-MODULE_DEPEND(hv_heartbeat, vmbus, 1, 1, 1);
diff --git a/sys/dev/hyperv/utilities/hv_util.c b/sys/dev/hyperv/utilities/vmbus_ic.c
index 20b8f84..5746700 100644
--- a/sys/dev/hyperv/utilities/hv_util.c
+++ b/sys/dev/hyperv/utilities/vmbus_ic.c
@@ -22,28 +22,21 @@
* 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 <sys/cdefs.h>
+__FBSDID("$FreeBSD$");
#include <sys/param.h>
-#include <sys/kernel.h>
#include <sys/bus.h>
#include <sys/malloc.h>
-#include <sys/module.h>
-#include <sys/reboot.h>
#include <sys/systm.h>
#include <sys/sysctl.h>
-#include <sys/timetc.h>
#include <dev/hyperv/include/hyperv.h>
#include <dev/hyperv/include/vmbus.h>
-#include <dev/hyperv/utilities/hv_util.h>
#include <dev/hyperv/utilities/vmbus_icreg.h>
+#include <dev/hyperv/utilities/vmbus_icvar.h>
#include "vmbus_if.h"
@@ -58,7 +51,7 @@ static int vmbus_ic_fwver_sysctl(SYSCTL_HANDLER_ARGS);
static int vmbus_ic_msgver_sysctl(SYSCTL_HANDLER_ARGS);
int
-vmbus_ic_negomsg(struct hv_util_sc *sc, void *data, int *dlen0,
+vmbus_ic_negomsg(struct vmbus_ic_softc *sc, void *data, int *dlen0,
uint32_t fw_ver, uint32_t msg_ver)
{
struct vmbus_icmsg_negotiate *nego;
@@ -214,9 +207,9 @@ vmbus_ic_probe(device_t dev, const struct vmbus_ic_desc descs[])
}
int
-hv_util_attach(device_t dev, vmbus_chan_callback_t cb)
+vmbus_ic_attach(device_t dev, vmbus_chan_callback_t cb)
{
- struct hv_util_sc *sc = device_get_softc(dev);
+ struct vmbus_ic_softc *sc = device_get_softc(dev);
struct vmbus_channel *chan = vmbus_get_channel(dev);
struct sysctl_oid_list *child;
struct sysctl_ctx_list *ctx;
@@ -224,8 +217,7 @@ hv_util_attach(device_t dev, vmbus_chan_callback_t cb)
sc->ic_dev = dev;
sc->ic_buflen = VMBUS_IC_BRSIZE;
- sc->receive_buffer = malloc(VMBUS_IC_BRSIZE, M_DEVBUF,
- M_WAITOK | M_ZERO);
+ sc->ic_buf = malloc(VMBUS_IC_BRSIZE, M_DEVBUF, M_WAITOK | M_ZERO);
/*
* These services are not performance critical and do not need
@@ -239,7 +231,7 @@ hv_util_attach(device_t dev, vmbus_chan_callback_t cb)
error = vmbus_chan_open(chan, VMBUS_IC_BRSIZE, VMBUS_IC_BRSIZE, NULL, 0,
cb, sc);
if (error) {
- free(sc->receive_buffer, M_DEVBUF);
+ free(sc->ic_buf, M_DEVBUF);
return (error);
}
@@ -258,7 +250,7 @@ hv_util_attach(device_t dev, vmbus_chan_callback_t cb)
static int
vmbus_ic_fwver_sysctl(SYSCTL_HANDLER_ARGS)
{
- struct hv_util_sc *sc = arg1;
+ struct vmbus_ic_softc *sc = arg1;
char verstr[16];
snprintf(verstr, sizeof(verstr), "%u.%u",
@@ -269,7 +261,7 @@ vmbus_ic_fwver_sysctl(SYSCTL_HANDLER_ARGS)
static int
vmbus_ic_msgver_sysctl(SYSCTL_HANDLER_ARGS)
{
- struct hv_util_sc *sc = arg1;
+ struct vmbus_ic_softc *sc = arg1;
char verstr[16];
snprintf(verstr, sizeof(verstr), "%u.%u",
@@ -278,12 +270,30 @@ vmbus_ic_msgver_sysctl(SYSCTL_HANDLER_ARGS)
}
int
-hv_util_detach(device_t dev)
+vmbus_ic_detach(device_t dev)
{
- struct hv_util_sc *sc = device_get_softc(dev);
+ struct vmbus_ic_softc *sc = device_get_softc(dev);
vmbus_chan_close(vmbus_get_channel(dev));
- free(sc->receive_buffer, M_DEVBUF);
+ free(sc->ic_buf, M_DEVBUF);
return (0);
}
+
+int
+vmbus_ic_sendresp(struct vmbus_ic_softc *sc, struct vmbus_channel *chan,
+ void *data, int dlen, uint64_t xactid)
+{
+ struct vmbus_icmsg_hdr *hdr;
+ int error;
+
+ KASSERT(dlen >= sizeof(*hdr), ("invalid data length %d", dlen));
+ hdr = data;
+
+ 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);
+ return (error);
+}
diff --git a/sys/dev/hyperv/utilities/vmbus_icreg.h b/sys/dev/hyperv/utilities/vmbus_icreg.h
index 3435962..e962102 100644
--- a/sys/dev/hyperv/utilities/vmbus_icreg.h
+++ b/sys/dev/hyperv/utilities/vmbus_icreg.h
@@ -118,14 +118,18 @@ struct vmbus_icmsg_timesync {
uint8_t ic_tsflags; /* VMBUS_ICMSG_TS_FLAG_ */
} __packed;
+/* VMBUS_ICMSG_TYPE_TIMESYNC, MSGVER4 */
+struct vmbus_icmsg_timesync4 {
+ struct vmbus_icmsg_hdr ic_hdr;
+ uint64_t ic_hvtime;
+ uint64_t ic_sent_tc;
+ uint8_t ic_tsflags; /* VMBUS_ICMSG_TS_FLAG_ */
+ uint8_t ic_rsvd[5];
+} __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_ */
diff --git a/sys/dev/hyperv/utilities/hv_util.h b/sys/dev/hyperv/utilities/vmbus_icvar.h
index 7cf8d31..a60ecfe 100644
--- a/sys/dev/hyperv/utilities/hv_util.h
+++ b/sys/dev/hyperv/utilities/vmbus_icvar.h
@@ -28,23 +28,19 @@
* $FreeBSD$
*/
-#ifndef _HVUTIL_H_
-#define _HVUTIL_H_
+#ifndef _VMBUS_ICVAR_H_
+#define _VMBUS_ICVAR_H_
#include <dev/hyperv/include/hyperv.h>
#include <dev/hyperv/include/vmbus.h>
-/**
- * hv_util related structures
- *
- */
-typedef struct hv_util_sc {
+struct vmbus_ic_softc {
device_t ic_dev;
- uint8_t *receive_buffer;
+ uint8_t *ic_buf;
int ic_buflen;
uint32_t ic_fwver; /* framework version */
uint32_t ic_msgver; /* message version */
-} hv_util_sc;
+};
struct vmbus_ic_desc {
const struct hyperv_guid ic_guid;
@@ -53,10 +49,13 @@ struct vmbus_ic_desc {
#define VMBUS_IC_DESC_END { .ic_desc = NULL }
-int hv_util_attach(device_t dev, vmbus_chan_callback_t cb);
-int hv_util_detach(device_t dev);
+int vmbus_ic_attach(device_t dev, vmbus_chan_callback_t cb);
+int vmbus_ic_detach(device_t dev);
int vmbus_ic_probe(device_t dev, const struct vmbus_ic_desc descs[]);
-int vmbus_ic_negomsg(struct hv_util_sc *sc, void *data, int *dlen,
- uint32_t fw_ver, uint32_t msg_ver);
+int vmbus_ic_negomsg(struct vmbus_ic_softc *sc, void *data,
+ int *dlen, uint32_t fw_ver, uint32_t msg_ver);
+int vmbus_ic_sendresp(struct vmbus_ic_softc *sc,
+ struct vmbus_channel *chan, void *data, int dlen,
+ uint64_t xactid);
-#endif
+#endif /* !_VMBUS_ICVAR_H_ */
diff --git a/sys/dev/hyperv/utilities/hv_shutdown.c b/sys/dev/hyperv/utilities/vmbus_shutdown.c
index e511b21..7e54dc9 100644
--- a/sys/dev/hyperv/utilities/hv_shutdown.c
+++ b/sys/dev/hyperv/utilities/vmbus_shutdown.c
@@ -36,10 +36,8 @@ __FBSDID("$FreeBSD$");
#include <dev/hyperv/include/hyperv.h>
#include <dev/hyperv/include/vmbus.h>
-#include <dev/hyperv/utilities/hv_util.h>
#include <dev/hyperv/utilities/vmbus_icreg.h>
-
-#include "vmbus_if.h"
+#include <dev/hyperv/utilities/vmbus_icvar.h>
#define VMBUS_SHUTDOWN_FWVER_MAJOR 3
#define VMBUS_SHUTDOWN_FWVER \
@@ -49,6 +47,9 @@ __FBSDID("$FreeBSD$");
#define VMBUS_SHUTDOWN_MSGVER \
VMBUS_IC_VERSION(VMBUS_SHUTDOWN_MSGVER_MAJOR, 0)
+static int vmbus_shutdown_probe(device_t);
+static int vmbus_shutdown_attach(device_t);
+
static const struct vmbus_ic_desc vmbus_shutdown_descs[] = {
{
.ic_guid = { .hv_guid = {
@@ -59,10 +60,31 @@ static const struct vmbus_ic_desc vmbus_shutdown_descs[] = {
VMBUS_IC_DESC_END
};
+static device_method_t vmbus_shutdown_methods[] = {
+ /* Device interface */
+ DEVMETHOD(device_probe, vmbus_shutdown_probe),
+ DEVMETHOD(device_attach, vmbus_shutdown_attach),
+ DEVMETHOD(device_detach, vmbus_ic_detach),
+ DEVMETHOD_END
+};
+
+static driver_t vmbus_shutdown_driver = {
+ "hvshutdown",
+ vmbus_shutdown_methods,
+ sizeof(struct vmbus_ic_softc)
+};
+
+static devclass_t vmbus_shutdown_devclass;
+
+DRIVER_MODULE(hv_shutdown, vmbus, vmbus_shutdown_driver,
+ vmbus_shutdown_devclass, NULL, NULL);
+MODULE_VERSION(hv_shutdown, 1);
+MODULE_DEPEND(hv_shutdown, vmbus, 1, 1, 1);
+
static void
vmbus_shutdown_cb(struct vmbus_channel *chan, void *xsc)
{
- struct hv_util_sc *sc = xsc;
+ struct vmbus_ic_softc *sc = xsc;
struct vmbus_icmsg_hdr *hdr;
struct vmbus_icmsg_shutdown *msg;
int dlen, error, do_shutdown = 0;
@@ -72,7 +94,7 @@ vmbus_shutdown_cb(struct vmbus_channel *chan, void *xsc)
/*
* Receive request.
*/
- data = sc->receive_buffer;
+ data = sc->ic_buf;
dlen = sc->ic_buflen;
error = vmbus_chan_recv(chan, data, &dlen, &xactid);
KASSERT(error != ENOBUFS, ("icbuf is not large enough"));
@@ -122,44 +144,24 @@ vmbus_shutdown_cb(struct vmbus_channel *chan, void *xsc)
}
/*
- * Send response by echoing the updated request back.
+ * Send response by echoing the 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);
+ vmbus_ic_sendresp(sc, chan, data, dlen, xactid);
if (do_shutdown)
shutdown_nice(RB_POWEROFF);
}
static int
-hv_shutdown_probe(device_t dev)
+vmbus_shutdown_probe(device_t dev)
{
return (vmbus_ic_probe(dev, vmbus_shutdown_descs));
}
static int
-hv_shutdown_attach(device_t dev)
+vmbus_shutdown_attach(device_t dev)
{
- return (hv_util_attach(dev, vmbus_shutdown_cb));
+ return (vmbus_ic_attach(dev, vmbus_shutdown_cb));
}
-
-static device_method_t shutdown_methods[] = {
- /* Device interface */
- DEVMETHOD(device_probe, hv_shutdown_probe),
- DEVMETHOD(device_attach, hv_shutdown_attach),
- DEVMETHOD(device_detach, hv_util_detach),
- { 0, 0 }
-};
-
-static driver_t shutdown_driver = { "hvshutdown", shutdown_methods, sizeof(hv_util_sc)};
-
-static devclass_t shutdown_devclass;
-
-DRIVER_MODULE(hv_shutdown, vmbus, shutdown_driver, shutdown_devclass, NULL, NULL);
-MODULE_VERSION(hv_shutdown, 1);
-MODULE_DEPEND(hv_shutdown, vmbus, 1, 1, 1);
diff --git a/sys/dev/hyperv/utilities/hv_timesync.c b/sys/dev/hyperv/utilities/vmbus_timesync.c
index 5bd51b0..7610027 100644
--- a/sys/dev/hyperv/utilities/hv_timesync.c
+++ b/sys/dev/hyperv/utilities/vmbus_timesync.c
@@ -37,19 +37,27 @@ __FBSDID("$FreeBSD$");
#include <dev/hyperv/include/hyperv.h>
#include <dev/hyperv/include/vmbus.h>
-#include <dev/hyperv/utilities/hv_util.h>
#include <dev/hyperv/utilities/vmbus_icreg.h>
-
-#include "vmbus_if.h"
+#include <dev/hyperv/utilities/vmbus_icvar.h>
#define VMBUS_TIMESYNC_FWVER_MAJOR 3
#define VMBUS_TIMESYNC_FWVER \
VMBUS_IC_VERSION(VMBUS_TIMESYNC_FWVER_MAJOR, 0)
-#define VMBUS_TIMESYNC_MSGVER_MAJOR 3
+#define VMBUS_TIMESYNC_MSGVER_MAJOR 4
#define VMBUS_TIMESYNC_MSGVER \
VMBUS_IC_VERSION(VMBUS_TIMESYNC_MSGVER_MAJOR, 0)
+#define VMBUS_TIMESYNC_MSGVER4(sc) \
+ VMBUS_ICVER_LE(VMBUS_IC_VERSION(4, 0), (sc)->ic_msgver)
+
+#define VMBUS_TIMESYNC_DORTT(sc) \
+ (VMBUS_TIMESYNC_MSGVER4((sc)) &&\
+ (hyperv_features & CPUID_HV_MSR_TIME_REFCNT))
+
+static int vmbus_timesync_probe(device_t);
+static int vmbus_timesync_attach(device_t);
+
static const struct vmbus_ic_desc vmbus_timesync_descs[] = {
{
.ic_guid = { .hv_guid = {
@@ -60,6 +68,27 @@ static const struct vmbus_ic_desc vmbus_timesync_descs[] = {
VMBUS_IC_DESC_END
};
+static device_method_t vmbus_timesync_methods[] = {
+ /* Device interface */
+ DEVMETHOD(device_probe, vmbus_timesync_probe),
+ DEVMETHOD(device_attach, vmbus_timesync_attach),
+ DEVMETHOD(device_detach, vmbus_ic_detach),
+ DEVMETHOD_END
+};
+
+static driver_t vmbus_timesync_driver = {
+ "hvtimesync",
+ vmbus_timesync_methods,
+ sizeof(struct vmbus_ic_softc)
+};
+
+static devclass_t vmbus_timesync_devclass;
+
+DRIVER_MODULE(hv_timesync, vmbus, vmbus_timesync_driver,
+ vmbus_timesync_devclass, NULL, NULL);
+MODULE_VERSION(hv_timesync, 1);
+MODULE_DEPEND(hv_timesync, vmbus, 1, 1, 1);
+
SYSCTL_NODE(_hw, OID_AUTO, hvtimesync, CTLFLAG_RW | CTLFLAG_MPSAFE, NULL,
"Hyper-V timesync interface");
@@ -81,12 +110,16 @@ 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)
+vmbus_timesync(struct vmbus_ic_softc *sc, uint64_t hvtime, uint64_t sent_tc,
+ uint8_t tsflags)
{
struct timespec vm_ts;
- uint64_t hv_ns, vm_ns;
+ uint64_t hv_ns, vm_ns, rtt = 0;
+
+ if (VMBUS_TIMESYNC_DORTT(sc))
+ rtt = rdmsr(MSR_HV_TIME_REF_COUNT) - sent_tc;
- hv_ns = (hvtime - VMBUS_ICMSG_TS_BASE) * VMBUS_ICMSG_TS_FACTOR;
+ hv_ns = (hvtime - VMBUS_ICMSG_TS_BASE + rtt) * HYPERV_TIMER_NS_FACTOR;
nanotime(&vm_ts);
vm_ns = (vm_ts.tv_sec * NANOSEC) + vm_ts.tv_nsec;
@@ -106,7 +139,7 @@ vmbus_timesync(struct hv_util_sc *sc, uint64_t hvtime, uint8_t tsflags)
}
if ((tsflags & VMBUS_ICMSG_TS_FLAG_SAMPLE) &&
- vmbus_ts_sample_thresh > 0) {
+ vmbus_ts_sample_thresh >= 0) {
int64_t diff;
if (vmbus_ts_sample_verbose) {
@@ -142,9 +175,8 @@ vmbus_timesync(struct hv_util_sc *sc, uint64_t hvtime, uint8_t tsflags)
static void
vmbus_timesync_cb(struct vmbus_channel *chan, void *xsc)
{
- struct hv_util_sc *sc = xsc;
+ struct vmbus_ic_softc *sc = xsc;
struct vmbus_icmsg_hdr *hdr;
- const struct vmbus_icmsg_timesync *msg;
int dlen, error;
uint64_t xactid;
void *data;
@@ -152,7 +184,7 @@ vmbus_timesync_cb(struct vmbus_channel *chan, void *xsc)
/*
* Receive request.
*/
- data = sc->receive_buffer;
+ data = sc->ic_buf;
dlen = sc->ic_buflen;
error = vmbus_chan_recv(chan, data, &dlen, &xactid);
KASSERT(error != ENOBUFS, ("icbuf is not large enough"));
@@ -174,16 +206,33 @@ vmbus_timesync_cb(struct vmbus_channel *chan, void *xsc)
VMBUS_TIMESYNC_FWVER, VMBUS_TIMESYNC_MSGVER);
if (error)
return;
+ if (VMBUS_TIMESYNC_DORTT(sc))
+ device_printf(sc->ic_dev, "RTT\n");
break;
case VMBUS_ICMSG_TYPE_TIMESYNC:
- if (dlen < sizeof(*msg)) {
- device_printf(sc->ic_dev, "invalid timesync len %d\n",
- dlen);
- return;
+ if (VMBUS_TIMESYNC_MSGVER4(sc)) {
+ const struct vmbus_icmsg_timesync4 *msg4;
+
+ if (dlen < sizeof(*msg4)) {
+ device_printf(sc->ic_dev, "invalid timesync4 "
+ "len %d\n", dlen);
+ return;
+ }
+ msg4 = data;
+ vmbus_timesync(sc, msg4->ic_hvtime, msg4->ic_sent_tc,
+ msg4->ic_tsflags);
+ } else {
+ const struct vmbus_icmsg_timesync *msg;
+
+ if (dlen < sizeof(*msg)) {
+ device_printf(sc->ic_dev, "invalid timesync "
+ "len %d\n", dlen);
+ return;
+ }
+ msg = data;
+ vmbus_timesync(sc, msg->ic_hvtime, 0, msg->ic_tsflags);
}
- msg = data;
- vmbus_timesync(sc, msg->ic_hvtime, msg->ic_tsflags);
break;
default:
@@ -192,41 +241,21 @@ vmbus_timesync_cb(struct vmbus_channel *chan, void *xsc)
}
/*
- * Send response by echoing the updated request back.
+ * Send response by echoing the 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);
+ vmbus_ic_sendresp(sc, chan, data, dlen, xactid);
}
static int
-hv_timesync_probe(device_t dev)
+vmbus_timesync_probe(device_t dev)
{
return (vmbus_ic_probe(dev, vmbus_timesync_descs));
}
static int
-hv_timesync_attach(device_t dev)
+vmbus_timesync_attach(device_t dev)
{
- return (hv_util_attach(dev, vmbus_timesync_cb));
+ return (vmbus_ic_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_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);
OpenPOWER on IntegriCloud