Index: contrib/hyperv/tools/hv_kvp_daemon.c =================================================================== --- contrib/hyperv/tools/hv_kvp_daemon.c +++ contrib/hyperv/tools/hv_kvp_daemon.c @@ -54,7 +54,7 @@ #include #include "hv_kvp.h" - +#include "hv_common.h" typedef uint8_t __u8; typedef uint16_t __u16; typedef uint32_t __u32; @@ -782,11 +782,11 @@ } if ((length - *offset) < addr_length + 1) { - return (HV_KVP_E_FAIL); + return (HV_E_FAIL); } if (str == NULL) { strlcpy(buffer, "inet_ntop failed\n", length); - return (HV_KVP_E_FAIL); + return (HV_E_FAIL); } if (*offset == 0) { strlcpy(buffer, tmp, length); @@ -832,7 +832,7 @@ if (getifaddrs(&ifap)) { strlcpy(buffer, "getifaddrs failed\n", buffer_length); - return (HV_KVP_E_FAIL); + return (HV_E_FAIL); } curp = ifap; @@ -954,7 +954,7 @@ ret = fprintf(f, "%s%s%s%s\n", s1, s2, "=", s3); if (ret < 0) { - return (HV_KVP_E_FAIL); + return (HV_E_FAIL); } return (0); @@ -979,7 +979,7 @@ if (file == NULL) { KVP_LOG(LOG_ERR, "FreeBSD Failed to open config file\n"); - return (HV_KVP_E_FAIL); + return (HV_E_FAIL); } /* @@ -988,7 +988,7 @@ mac_addr = kvp_if_name_to_mac(if_name); if (mac_addr == NULL) { - error = HV_KVP_E_FAIL; + error = HV_E_FAIL; goto kvp_set_ip_info_error; } /* MAC Address */ @@ -1096,13 +1096,13 @@ KVP_LOG(LOG_DEBUG, "In kvp_op_getipinfo.\n"); ip_val = &op_msg->body.kvp_ip_val; - op_msg->hdr.error = HV_KVP_S_OK; + op_msg->hdr.error = HV_S_OK; if_name = kvp_mac_to_if_name((char *)ip_val->adapter_id); if (if_name == NULL) { /* No interface found with the mac address. */ - op_msg->hdr.error = HV_KVP_E_FAIL; + op_msg->hdr.error = HV_E_FAIL; goto kvp_op_getipinfo_done; } @@ -1126,13 +1126,13 @@ KVP_LOG(LOG_DEBUG, "In kvp_op_setipinfo.\n"); ip_val = &op_msg->body.kvp_ip_val; - op_msg->hdr.error = HV_KVP_S_OK; + op_msg->hdr.error = HV_S_OK; if_name = (char *)ip_val->adapter_id; if (if_name == NULL) { /* No adapter provided. */ - op_msg->hdr.error = HV_KVP_GUID_NOTFOUND; + op_msg->hdr.error = HV_GUID_NOTFOUND; goto kvp_op_setipinfo_done; } @@ -1154,7 +1154,7 @@ assert(op_hdlr != NULL); op_pool = op_msg->hdr.kvp_hdr.pool; - op_msg->hdr.error = HV_KVP_S_OK; + op_msg->hdr.error = HV_S_OK; switch(op_hdlr->kvp_op_key) { case HV_KVP_OP_SET: @@ -1198,7 +1198,7 @@ } if (error != 0) - op_msg->hdr.error = HV_KVP_S_CONT; + op_msg->hdr.error = HV_S_CONT; return(error); } @@ -1216,7 +1216,7 @@ op = op_msg->hdr.kvp_hdr.operation; op_pool = op_msg->hdr.kvp_hdr.pool; - op_msg->hdr.error = HV_KVP_S_OK; + op_msg->hdr.error = HV_S_OK; /* * If the pool is not HV_KVP_POOL_AUTO, read from the appropriate @@ -1229,7 +1229,7 @@ HV_KVP_EXCHANGE_MAX_KEY_SIZE, op_msg->body.kvp_enum_data.data.msg_value.value, HV_KVP_EXCHANGE_MAX_VALUE_SIZE)) { - op_msg->hdr.error = HV_KVP_S_CONT; + op_msg->hdr.error = HV_S_CONT; error = -1; } goto kvp_op_enumerate_done; @@ -1298,7 +1298,7 @@ KVP_LOG(LOG_ERR, "Auto pool Index %d not found.\n", op_msg->body.kvp_enum_data.index); #endif - op_msg->hdr.error = HV_KVP_S_CONT; + op_msg->hdr.error = HV_S_CONT; error = -1; break; } @@ -1496,7 +1496,7 @@ */ error = kvp_op_hdlrs[op].kvp_op_exec(hv_msg, (void *)&kvp_op_hdlrs[op]); - if (error != 0 && hv_msg->hdr.error != HV_KVP_S_CONT) + if (error != 0 && hv_msg->hdr.error != HV_S_CONT) KVP_LOG(LOG_WARNING, "Operation failed OP = %d, error = 0x%x\n", op, error); Index: contrib/hyperv/tools/hv_vss_daemon.c =================================================================== --- /dev/null +++ contrib/hyperv/tools/hv_vss_daemon.c @@ -0,0 +1,251 @@ +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "hv_snapshot.h" +#include "hv_common.h" + +#define _PATH_UFSSUSPEND "/dev/ufssuspend" +#define UNDEF_FREEZE_THAW (0) +#define FREEZE (1) +#define THAW (2) +#define UNOPENED_HANDLE (-2) + +#define VSS_LOG(priority, format, args...) do { \ + if (is_debugging == 1) { \ + if (is_daemon == 1) \ + syslog(priority, format, ## args); \ + else \ + printf(format, ## args); \ + } else { \ + if (priority < LOG_DEBUG) { \ + if (is_daemon == 1) \ + syslog(priority, format, ## args); \ + else \ + printf(format, ## args); \ + } \ + } \ + } while(0) + +static int is_daemon = 1; +static int is_debugging = 0; +static int g_ufs_suspend_handle = UNOPENED_HANDLE; + +static int +freeze() +{ + struct statfs *mntbuf, *statfsp; + int mntsize; + int fso; + int error; + int i; + const char *dev = "/dev"; + + if (g_ufs_suspend_handle == UNOPENED_HANDLE) + g_ufs_suspend_handle = open(_PATH_UFSSUSPEND, O_RDWR); + if (g_ufs_suspend_handle == -1) { + VSS_LOG(LOG_ERR, "unable to open %s", _PATH_UFSSUSPEND); + error = 1; + goto handle_error; + } + + mntsize = getmntinfo(&mntbuf, MNT_NOWAIT); + if (mntsize == 0) { + VSS_LOG(LOG_ERR, "There is no mount information\n"); + error = 1; + goto handle_error; + } + for(i = mntsize - 1; i >= 0; --i) + { + statfsp = &mntbuf[i]; + + if (strncmp(statfsp->f_mntonname, dev, strlen(dev)) == 0) { + continue; /* skip to freeze '/dev' */ + } else if (statfsp->f_flags & MNT_RDONLY) { + continue; /* skip to freeze RDONLY partition */ + } else if (strncmp(statfsp->f_fstypename, "ufs", 3) != 0) { + continue; /* only UFS can be freezed */ + } + /* We have ensure that all disk writes have been completed */ + sync(); + error = ioctl(g_ufs_suspend_handle, UFSSUSPEND, &statfsp->f_fsid); + if (error != 0) { + VSS_LOG(LOG_ERR, "error: %d\n", errno); + } else { + VSS_LOG(LOG_INFO, "Successfully suspend fs: %s\n", + statfsp->f_mntonname); + } + } + + return (0); +handle_error: + return (error); +} + +/** + * close the opened handle will thaw the FS. + */ +static int +thaw() +{ + int error = 0; + if (g_ufs_suspend_handle != UNOPENED_HANDLE && + g_ufs_suspend_handle != -1) { + error = close(g_ufs_suspend_handle); + if (!error) { + g_ufs_suspend_handle = UNOPENED_HANDLE; + VSS_LOG(LOG_INFO, "Successfully thaw the fs\n"); + return (0); + } else { + VSS_LOG(LOG_ERR, "Fail to thaw the fs: " + "%d %s\n", errno, strerror(errno)); + } + } else { + VSS_LOG(LOG_INFO, "The fs has already been thawed\n"); + } + + return (error); +} + +static void usage(const char* cmd) { + fprintf(stderr, "%s -f : freeze the filesystem\n" + " -t : thaw the filesystem\n", cmd); + exit(1); +} + +int main(int argc, char* argv[]) { + struct hv_vss_req hv_vss_buf; + + struct pollfd hv_vss_poll_fd[1]; + int ch, r, len, op, error; + int hv_vss_dev_fd; + + int freeze_thaw = UNDEF_FREEZE_THAW; + while ((ch = getopt(argc, argv, "dn")) != -1) { + switch (ch) { + case 'n': + /* Run as regular process for debugging purpose. */ + is_daemon = 0; + break; + case 'd': + /* Generate debugging output */ + is_debugging = 1; + break; + default: + break; + } + } + + openlog("HV_VSS", 0, LOG_USER); + + /* Become daemon first. */ + if (is_daemon == 1) + daemon(1, 0); + else + VSS_LOG(LOG_DEBUG, "Run as regular process.\n"); + + VSS_LOG(LOG_INFO, "HV_VSS starting; pid is: %d\n", getpid()); + + hv_vss_dev_fd = open("/dev/hv_vss_dev", O_RDWR); + + if (hv_vss_dev_fd < 0) { + VSS_LOG(LOG_ERR, "Fail to open /dev/hv_vss_dev, error: %d %s\n", + errno, strerror(errno)); + exit(EXIT_FAILURE); + } + hv_vss_poll_fd[0].fd = hv_vss_dev_fd; + hv_vss_poll_fd[0].events = (POLLIN | POLLRDNORM); + + memset(&hv_vss_buf, 0, sizeof(hv_vss_buf)); + hv_vss_buf.msg.hdr.vss_hdr.operation = VSS_OP_REGISTER; + len = write(hv_vss_dev_fd, &hv_vss_buf, sizeof(hv_vss_buf)); + VSS_LOG(LOG_INFO, "register to kernel\n"); + + while (1) { + r = poll(hv_vss_poll_fd, 1, INFTIM); + + VSS_LOG(LOG_DEBUG, "poll returned r = %d, revent = 0x%x\n", + r, hv_vss_poll_fd[0].revents); + + if (r == 0 || (r < 0 && errno == EAGAIN) || + (r < 0 && errno == EINTR)) { + /* Nothing to read */ + continue; + } + + if (r < 0) { + /* + * For poll return failure other than EAGAIN, + * we want to exit. + */ + VSS_LOG(LOG_ERR, "Poll failed.\n"); + perror("poll"); + exit(EIO); + } + + /* Read from character device */ + len = pread(hv_vss_dev_fd, &hv_vss_buf, + sizeof(hv_vss_buf), 0); + + if (len < 0) { + VSS_LOG(LOG_ERR, "Read failed.\n"); + perror("pread"); + exit(EIO); + } + + if (len != sizeof(struct hv_vss_req)) { + VSS_LOG(LOG_ERR, "read len is: %d\n", len); + continue; + } + + /* + * We will use the KVP header information to pass back + * the error from this daemon. So, first save the op + * and pool info to local variables. + */ + + op = hv_vss_buf.msg.hdr.vss_hdr.operation; + + switch (op) { + case VSS_OP_FREEZE: + error = freeze(); + break; + case VSS_OP_THAW: + error = thaw(); + break; + default: + VSS_LOG(LOG_ERR, "Illegal operation: %d\n", op); + } + if (error != 0) { + error = HV_E_FAIL; + } else { + error = HV_S_OK; + } + hv_vss_buf.msg.hdr.error = error; + len = pwrite(hv_vss_dev_fd, &hv_vss_buf, sizeof(hv_vss_buf), 0); + if (len != sizeof(struct hv_vss_req)) { + VSS_LOG(LOG_ERR, "Fail to write to device:" + " %d != expect %ld\n", len, sizeof(struct hv_vss_req)); + exit(EXIT_FAILURE); + } else { + VSS_LOG(LOG_INFO, "Send response %d for %s to kernel\n", + error, op == VSS_OP_FREEZE ? "Freeze" : "Thaw"); + } + } + + return (0); +} Index: etc/devd/hyperv.conf =================================================================== --- etc/devd/hyperv.conf +++ etc/devd/hyperv.conf @@ -17,3 +17,19 @@ match "cdev" "hv_kvp_dev"; action "pkill -x hv_kvp_daemon"; }; + +notify 11 { + match "system" "DEVFS"; + match "subsystem" "CDEV"; + match "type" "CREATE"; + match "cdev" "hv_vss_dev"; + action "/usr/sbin/hv_vss_daemon"; +}; + +notify 11 { + match "system" "DEVFS"; + match "subsystem" "CDEV"; + match "type" "DESTROY"; + match "cdev" "hv_vss_dev"; + action "pkill -x hv_vss_daemon"; +}; Index: sys/conf/files.amd64 =================================================================== --- sys/conf/files.amd64 +++ sys/conf/files.amd64 @@ -297,6 +297,7 @@ dev/hyperv/storvsc/hv_storvsc_drv_freebsd.c optional hyperv dev/hyperv/utilities/hv_heartbeat.c optional hyperv dev/hyperv/utilities/hv_kvp.c optional hyperv +dev/hyperv/utilities/hv_snapshot.c optional hyperv dev/hyperv/utilities/hv_shutdown.c optional hyperv dev/hyperv/utilities/hv_timesync.c optional hyperv dev/hyperv/utilities/hv_util.c optional hyperv Index: sys/conf/files.i386 =================================================================== --- sys/conf/files.i386 +++ sys/conf/files.i386 @@ -254,6 +254,7 @@ dev/hyperv/storvsc/hv_storvsc_drv_freebsd.c optional hyperv dev/hyperv/utilities/hv_heartbeat.c optional hyperv dev/hyperv/utilities/hv_kvp.c optional hyperv +dev/hyperv/utilities/hv_snapshot.c optional hyperv dev/hyperv/utilities/hv_shutdown.c optional hyperv dev/hyperv/utilities/hv_timesync.c optional hyperv dev/hyperv/utilities/hv_util.c optional hyperv Index: sys/dev/hyperv/utilities/hv_common.h =================================================================== --- sys/dev/hyperv/utilities/hv_common.h +++ sys/dev/hyperv/utilities/hv_common.h @@ -27,33 +27,32 @@ * * $FreeBSD$ */ - -#ifndef _HVUTIL_H_ -#define _HVUTIL_H_ - -#include -#include - -/** - * hv_util related structures - * +#ifndef _HVCOMMON_H_ +#define _HVCOMMON_H_ +/* + * Some Hyper-V status codes. */ -typedef struct hv_util_sc { - device_t ic_dev; - uint8_t *receive_buffer; - int ic_buflen; -} hv_util_sc; +#define HV_S_OK 0x00000000 +#define HV_E_FAIL 0x80004005 +#define HV_S_CONT 0x80070103 +#define HV_ERROR_NOT_SUPPORTED 0x80070032 +#define HV_ERROR_MACHINE_LOCKED 0x800704F7 +#define HV_ERROR_DEVICE_NOT_CONNECTED 0x8007048F +#define HV_INVALIDARG 0x80070057 +#define HV_GUID_NOTFOUND 0x80041002 -struct vmbus_ic_desc { - const struct hyperv_guid ic_guid; - const char *ic_desc; -}; +/* + * Framework version for util services. + */ +#define UTIL_FW_MINOR 0 -#define VMBUS_IC_DESC_END { .ic_desc = NULL } +#define UTIL_WS2K8_FW_MAJOR 1 +#define UTIL_WS2K8_FW_VERSION (UTIL_WS2K8_FW_MAJOR << 16 | UTIL_FW_MINOR) -int hv_util_attach(device_t dev, vmbus_chan_callback_t cb); -int hv_util_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 *, void *data, int *dlen); +#define UTIL_FW_MAJOR 3 +#define UTIL_FW_VERSION (UTIL_FW_MAJOR << 16 | UTIL_FW_MINOR) +/* status returned to daemon */ +#define SUCCESS (0) +#define ERROR (1) #endif Index: sys/dev/hyperv/utilities/hv_kvp.h =================================================================== --- sys/dev/hyperv/utilities/hv_kvp.h +++ sys/dev/hyperv/utilities/hv_kvp.h @@ -119,7 +119,13 @@ #define HV_REG_U32 4 #define HV_REG_U64 8 +#define ADDR_FAMILY_NONE 0x00 +#define ADDR_FAMILY_IPV4 0x01 +#define ADDR_FAMILY_IPV6 0x02 +#define MAX_ADAPTER_ID_SIZE 128 +#define MAX_IP_ADDR_SIZE 1024 +#define MAX_GATEWAY_SIZE 512 /* * Daemon code supporting IP injection. */ @@ -145,28 +151,6 @@ HV_KVP_POOL_COUNT /* Number of pools, must be last. */ }; - -/* - * Some Hyper-V status codes. - */ -#define HV_KVP_S_OK 0x00000000 -#define HV_KVP_E_FAIL 0x80004005 -#define HV_KVP_S_CONT 0x80070103 -#define HV_ERROR_NOT_SUPPORTED 0x80070032 -#define HV_ERROR_MACHINE_LOCKED 0x800704F7 -#define HV_ERROR_DEVICE_NOT_CONNECTED 0x8007048F -#define HV_INVALIDARG 0x80070057 -#define HV_KVP_GUID_NOTFOUND 0x80041002 - -#define ADDR_FAMILY_NONE 0x00 -#define ADDR_FAMILY_IPV4 0x01 -#define ADDR_FAMILY_IPV6 0x02 - -#define MAX_ADAPTER_ID_SIZE 128 -#define MAX_IP_ADDR_SIZE 1024 -#define MAX_GATEWAY_SIZE 512 - - struct hv_kvp_ipaddr_value { uint16_t adapter_id[MAX_ADAPTER_ID_SIZE]; uint8_t addr_family; Index: sys/dev/hyperv/utilities/hv_kvp.c =================================================================== --- sys/dev/hyperv/utilities/hv_kvp.c +++ sys/dev/hyperv/utilities/hv_kvp.c @@ -62,16 +62,32 @@ #include #include +#include #include "hv_util.h" #include "unicode.h" #include "hv_kvp.h" +#include "hv_common.h" #include "vmbus_if.h" +/* + * Pre win8 version numbers used in ws2008 and ws 2008 r2 (win7) + */ +#define WS2008_SRV_MAJOR 1 +#define WS2008_SRV_MINOR 0 +#define WS2008_SRV_VERSION (WS2008_SRV_MAJOR << 16 | WS2008_SRV_MINOR) + +#define WIN7_SRV_MAJOR 3 +#define WIN7_SRV_MINOR 0 +#define WIN7_SRV_VERSION (WIN7_SRV_MAJOR << 16 | WIN7_SRV_MINOR) + +#define WIN8_SRV_MAJOR 4 +#define WIN8_SRV_MINOR 0 +#define WIN8_SRV_VERSION (WIN8_SRV_MAJOR << 16 | WIN8_SRV_MINOR) + + /* hv_kvp defines */ #define BUFFERSIZE sizeof(struct hv_kvp_msg) -#define KVP_SUCCESS 0 -#define KVP_ERROR 1 #define kvp_hdr hdr.kvp_hdr /* hv_kvp debug control */ @@ -214,47 +230,6 @@ /* - * hv_kvp - version neogtiation function - */ -static void -hv_kvp_negotiate_version(struct hv_vmbus_icmsg_hdr *icmsghdrp, uint8_t *buf) -{ - struct hv_vmbus_icmsg_negotiate *negop; - int icframe_vercnt; - int icmsg_vercnt; - - icmsghdrp->icmsgsize = 0x10; - - negop = (struct hv_vmbus_icmsg_negotiate *)&buf[ - sizeof(struct hv_vmbus_pipe_hdr) + - sizeof(struct hv_vmbus_icmsg_hdr)]; - icframe_vercnt = negop->icframe_vercnt; - icmsg_vercnt = negop->icmsg_vercnt; - - /* - * Select the framework version number we will support - */ - if ((icframe_vercnt >= 2) && (negop->icversion_data[1].major == 3)) { - icframe_vercnt = 3; - if (icmsg_vercnt > 2) - icmsg_vercnt = 4; - else - icmsg_vercnt = 3; - } else { - icframe_vercnt = 1; - icmsg_vercnt = 1; - } - - negop->icframe_vercnt = 1; - negop->icmsg_vercnt = 1; - negop->icversion_data[0].major = icframe_vercnt; - negop->icversion_data[0].minor = 0; - negop->icversion_data[1].major = icmsg_vercnt; - negop->icversion_data[1].minor = 0; -} - - -/* * Convert ip related info in umsg from utf8 to utf16 and store in hmsg */ static int @@ -514,7 +489,7 @@ case HV_KVP_OP_SET_IP_INFO: case HV_KVP_OP_SET: case HV_KVP_OP_DELETE: - return (KVP_SUCCESS); + return (SUCCESS); case HV_KVP_OP_ENUMERATE: host_exchg_data = &hmsg->body.kvp_enum_data.data; @@ -535,9 +510,9 @@ host_exchg_data->value_type = HV_REG_SZ; if ((hkey_len < 0) || (hvalue_len < 0)) - return (HV_KVP_E_FAIL); + return (HV_E_FAIL); - return (KVP_SUCCESS); + return (SUCCESS); case HV_KVP_OP_GET: host_exchg_data = &hmsg->body.kvp_get.data; @@ -553,12 +528,12 @@ host_exchg_data->value_type = HV_REG_SZ; if ((hkey_len < 0) || (hvalue_len < 0)) - return (HV_KVP_E_FAIL); + return (HV_E_FAIL); - return (KVP_SUCCESS); + return (SUCCESS); default: - return (HV_KVP_E_FAIL); + return (HV_E_FAIL); } } @@ -575,7 +550,7 @@ &sc->rcv_buf[sizeof(struct hv_vmbus_pipe_hdr)]; if (error) - error = HV_KVP_E_FAIL; + error = HV_E_FAIL; hv_icmsg_hdrp->status = error; hv_icmsg_hdrp->icflags = HV_ICMSGHDRFLAG_TRANSACTION | HV_ICMSGHDRFLAG_RESPONSE; @@ -623,11 +598,15 @@ uint64_t requestid; struct hv_vmbus_icmsg_hdr *icmsghdrp; int ret = 0; - hv_kvp_sc *sc; + hv_kvp_sc *sc; + uint32_t vmbus_version; + int util_ver, kvp_ver; hv_kvp_log_info("%s: entering hv_kvp_process_request\n", __func__); sc = (hv_kvp_sc*)context; + + vmbus_version = VMBUS_GET_VERSION(device_get_parent(sc->dev), sc->dev); kvp_buf = sc->util_sc.receive_buffer; channel = vmbus_get_channel(sc->dev); @@ -643,7 +622,18 @@ hv_kvp_transaction_init(sc, recvlen, requestid, kvp_buf); if (icmsghdrp->icmsgtype == HV_ICMSGTYPE_NEGOTIATE) { - hv_kvp_negotiate_version(icmsghdrp, kvp_buf); + switch(vmbus_version) { + case VMBUS_VERSION_WS2008: + util_ver = UTIL_WS2K8_FW_VERSION; + kvp_ver = WS2008_SRV_VERSION; + case VMBUS_VERSION_WIN7: + util_ver = UTIL_FW_VERSION; + kvp_ver = WIN7_SRV_VERSION; + default: + util_ver = UTIL_FW_VERSION; + kvp_ver = WIN8_SRV_VERSION; + } + hv_util_negotiate_version(kvp_buf, util_ver, kvp_ver); hv_kvp_respond_host(sc, ret); /* @@ -689,7 +679,7 @@ */ if (hv_kvp_req_in_progress(sc)) { hv_kvp_log_info("%s: request was still active after wait so failing\n", __func__); - hv_kvp_respond_host(sc, HV_KVP_E_FAIL); + hv_kvp_respond_host(sc, HV_E_FAIL); sc->req_in_progress = false; } @@ -771,7 +761,7 @@ /* Check hv_kvp daemon registration status*/ if (!sc->register_done) - return (KVP_ERROR); + return (ERROR); sema_wait(&sc->dev_sema); @@ -821,7 +811,7 @@ } else { hv_kvp_log_info("%s, KVP Registration Failed\n", __func__); - return (KVP_ERROR); + return (ERROR); } } else { @@ -832,7 +822,7 @@ struct hv_kvp_msg *umsg = &sc->daemon_kvp_msg; hv_kvp_convert_usermsg_to_hostmsg(umsg, hmsg); - hv_kvp_respond_host(sc, KVP_SUCCESS); + hv_kvp_respond_host(sc, SUCCESS); wakeup(sc); sc->req_in_progress = false; } Index: sys/dev/hyperv/utilities/hv_snapshot.h =================================================================== --- /dev/null +++ sys/dev/hyperv/utilities/hv_snapshot.h @@ -0,0 +1,100 @@ +/*- + * 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 + +#define VSS_MAJOR 5 +#define VSS_MINOR 0 +#define VSS_VERSION (VSS_MAJOR << 16 | VSS_MINOR) + +/* + * Implementation of host controlled snapshot of the guest. + */ +#define VSS_OP_REGISTER 128 + +/* + Daemon code with full handshake support. + */ +#define VSS_OP_REGISTER1 129 + +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 { + uint8_t operation; + uint8_t reserved[7]; +} __attribute__((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; +} __attribute__((packed)); + +struct hv_vss_check_dm_info { + uint32_t flags; +} __attribute__((packed)); + +struct hv_vss_msg { + union { + struct hv_vss_hdr vss_hdr; + int error; + } hdr; + union { + struct hv_vss_check_feature vss_cf; + struct hv_vss_check_dm_info dm_info; + } body; +} __attribute__((packed)); + +struct hv_vss_req { + uint64_t req_id; + struct hv_vss_msg msg; +} __attribute__((packed)); +#endif Index: sys/dev/hyperv/utilities/hv_snapshot.c =================================================================== --- /dev/null +++ sys/dev/hyperv/utilities/hv_snapshot.c @@ -0,0 +1,643 @@ +/*- + * 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 +__FBSDID("$FreeBSD$"); + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include + +#include "hv_util.h" +#include "hv_snapshot.h" +#include "hv_common.h" +#include "vmbus_if.h" + +#define BUFFERSIZE sizeof(struct hv_vss_req) + +/* 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 +}; + +/* character device prototypes */ +static d_open_t hv_vss_dev_open; +static d_close_t hv_vss_dev_close; +static d_read_t hv_vss_dev_daemon_read; +static d_write_t hv_vss_dev_daemon_write; +static d_poll_t hv_vss_dev_daemon_poll; + +/* 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_read = hv_vss_dev_daemon_read, + .d_write = hv_vss_dev_daemon_write, + .d_poll = hv_vss_dev_daemon_poll, + .d_name = "hv_vss_dev", +}; + +/* + * Global state to track and synchronize multiple + * KVP transaction requests from the host. + */ +typedef struct hv_vss_sc { + struct hv_util_sc 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; + /* + * msg was transfered from host to daemon_read, and + * daemon_write. Finally, it was recyled to free list. + */ + STAILQ_HEAD(, hv_vss_req_internal) to_daemon_msg_queue; + STAILQ_HEAD(, hv_vss_req_internal) to_host_msg_queue; + + /* Indicates if daemon registered with driver */ + boolean_t register_done; + + /* Character device status */ + boolean_t dev_accessed; + + struct cdev *hv_vss_dev; + + struct proc *daemon_task; + + struct selinfo hv_vss_selinfo; +} 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; + +/* + * 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; + /* + The first request from host will not be handled until daemon is registered. + when callback is triggered without a registered daemon, callback just return. + When a new daemon gets regsitered, this callbcak is trigged from _write op. + */ + if (sc->register_done) { + hv_vss_log_info("%s: Queuing work item\n", __func__); + taskqueue_enqueue(taskqueue_thread, &sc->task); + } +} +/* + * 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, int error) +{ + struct hv_vmbus_icmsg_hdr *hv_icmsg_hdrp; + + hv_icmsg_hdrp = (struct hv_vmbus_icmsg_hdr *) + &rcv_buf[sizeof(struct hv_vmbus_pipe_hdr)]; + + if (error) + error = HV_E_FAIL; + + hv_icmsg_hdrp->status = error; + hv_icmsg_hdrp->icflags = 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 int +hv_vss_dev_open(struct cdev *dev, int oflags, int devtype, struct thread *td) +{ + hv_vss_sc *sc = (hv_vss_sc*)dev->si_drv1; + + hv_vss_log_info("%s: Opened device \"hv_vss_device\" successfully.\n", __func__); + if (sc->dev_accessed) + return (-EBUSY); + + sc->daemon_task = curproc; + sc->dev_accessed = true; + return (0); +} + +static int +hv_vss_dev_close(struct cdev *dev __unused, int fflag __unused, int devtype __unused, + struct thread *td __unused) +{ + hv_vss_sc *sc = (hv_vss_sc*)dev->si_drv1; + + hv_vss_log_info("%s: Closing device \"hv_vss_device\".\n", __func__); + sc->dev_accessed = false; + sc->register_done = false; + return (0); +} + +/* + * hv_vss_daemon read invokes this function + * acts as a send to daemon + */ +static int +hv_vss_dev_daemon_read(struct cdev *dev, struct uio *uio, int ioflag __unused) +{ + size_t amt; + int error = 0; + struct hv_vss_req_internal* reqp; + struct hv_vss_req *hv_vss_dev_buf; + hv_vss_sc *sc = (hv_vss_sc*)dev->si_drv1; + + hv_vss_log_info("%s: entered\n", __func__); + /* Check hv_vss daemon registration status */ + if (!sc->register_done) + return (ERROR); + + if (uio->uio_resid < BUFFERSIZE) { + return (EAGAIN); + } + + amt = MIN(uio->uio_resid, BUFFERSIZE); + mtx_lock(&sc->pending_mutex); + if (!STAILQ_EMPTY(&sc->to_daemon_msg_queue)) { + reqp = STAILQ_FIRST(&sc->to_daemon_msg_queue); + hv_vss_dev_buf = &reqp->vss_req; + if ((error = uiomove(hv_vss_dev_buf, amt, uio)) != 0) { + hv_vss_log_info("%s: hv_vss uiomove read failed!\n", __func__); + } + hv_vss_log_info("%s, read data from daemon for %s (%ld) \n", + __func__, + reqp->vss_req.msg.hdr.vss_hdr.operation == VSS_OP_FREEZE ? + "Freeze" : "Thaw", reqp->vss_req.req_id); + STAILQ_REMOVE_HEAD(&sc->to_daemon_msg_queue, slink); + /* insert the msg to queue for write */ + STAILQ_INSERT_TAIL(&sc->to_host_msg_queue, reqp, slink); + } + mtx_unlock(&sc->pending_mutex); + + return (error); +} + +/* + * hv_vss_daemon write invokes this function + * acts as a receive from daemon + */ +static int +hv_vss_dev_daemon_write(struct cdev *dev, struct uio *uio, int ioflag __unused) +{ + size_t amt; + int error = 0; + struct hv_vss_req hv_vss_dev_buf; + struct hv_vss_req_internal *reqp, *tmp; + uint64_t req_id; + hv_vss_sc *sc = (hv_vss_sc*)dev->si_drv1; + + uio->uio_offset = 0; + hv_vss_log_info("%s: entered\n", __func__); + amt = MIN(uio->uio_resid, BUFFERSIZE); + error = uiomove(&hv_vss_dev_buf, amt, uio); + + if (error != 0) { + return (error); + } + + if (sc->register_done == false) { + if (hv_vss_dev_buf.msg.hdr.vss_hdr.operation == VSS_OP_REGISTER) { + sc->register_done = true; + hv_vss_callback(vmbus_get_channel(sc->dev), dev->si_drv1); + } + else { + hv_vss_log_info("%s, VSS Registration Failed\n", __func__); + return (ERROR); + } + } else { + /* + * search the origin request which was read by daemon + * to cancel it timeout check. + */ + req_id = hv_vss_dev_buf.req_id; + mtx_lock(&sc->pending_mutex); + STAILQ_FOREACH_SAFE(reqp, &sc->to_host_msg_queue, slink, tmp) { + if (reqp->vss_req.req_id == req_id) { + STAILQ_REMOVE(&sc->to_host_msg_queue, + reqp, hv_vss_req_internal, slink); + break; + } + } + mtx_unlock(&sc->pending_mutex); + callout_drain(&reqp->callout); + memcpy(&reqp->vss_req, &hv_vss_dev_buf, BUFFERSIZE); + hv_vss_log_info("%s, get response from daemon for %s (%ld) \n", + __func__, + reqp->vss_req.msg.hdr.vss_hdr.operation == VSS_OP_FREEZE ? + "Freeze" : "Thaw", reqp->vss_req.req_id); + hv_vss_respond_host(reqp->rcv_buf, + vmbus_get_channel(reqp->sc->dev), + reqp->host_msg_len, reqp->host_msg_id, SUCCESS); + /* recycle the request */ + LIST_INSERT_HEAD(&sc->req_free_list, reqp, link); + } + + return (error); +} + + +/* + * 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; + hv_vss_sc *sc = (hv_vss_sc*)dev->si_drv1; + + mtx_lock(&sc->pending_mutex); + /** + * if there is data ready, inform daemon's poll + */ + if (!STAILQ_EMPTY(&sc->to_daemon_msg_queue)) { + revent = POLLIN; + } + mtx_unlock(&sc->pending_mutex); + selrecord(td, &sc->hv_vss_selinfo); + + hv_vss_log_info("%s return 0x%x\n", __func__, revent); + return (revent); +} + +static void +hv_vss_timeout(void *arg) +{ + hv_vss_req_internal *reqp = arg; + hv_vss_log_info("request sent to daemon timeout\n"); + hv_vss_respond_host(reqp->rcv_buf, + vmbus_get_channel(reqp->sc->dev), + reqp->host_msg_len, reqp->host_msg_id, ERROR); +} + +/* + * 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; + int msgoff = sizeof(struct hv_vmbus_pipe_hdr) + + sizeof(struct hv_vmbus_icmsg_hdr); + + 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[msgoff], sizeof(struct hv_vss_msg)); + /* Use a timestamp as msg request ID */ + nanotime(&vm_ts); + reqp->vss_req.req_id = (vm_ts.tv_sec * NANOSEC) + vm_ts.tv_nsec; +} + +/* + * 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 hv_vmbus_icmsg_hdr *icmsghdrp; + int ret = 0; + hv_vss_sc *sc; + hv_vss_req_internal *reqp; + int msgoff = sizeof(struct hv_vmbus_pipe_hdr) + + sizeof(struct hv_vmbus_icmsg_hdr); + + hv_vss_log_info("%s: entering hv_vss_process_request\n", __func__); + + sc = (hv_vss_sc*)context; + vss_buf = sc->util_sc.receive_buffer; + 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 hv_vmbus_icmsg_hdr *) + &vss_buf[sizeof(struct hv_vmbus_pipe_hdr)]; + + if (icmsghdrp->icmsgtype == HV_ICMSGTYPE_NEGOTIATE) { + hv_util_negotiate_version(vss_buf, UTIL_FW_VERSION, + VSS_VERSION); + hv_vss_respond_host(vss_buf, vmbus_get_channel(sc->dev), + recvlen, requestid, ret); + hv_vss_log_info("%s: version negotiated\n", __func__); + } else { + struct hv_vss_msg* msg = (struct hv_vss_msg *)&vss_buf[msgoff]; + switch(msg->hdr.vss_hdr.operation) { + case VSS_OP_FREEZE: + case VSS_OP_THAW: + mtx_lock(&sc->pending_mutex); + if (!STAILQ_EMPTY(&sc->to_daemon_msg_queue) || + !STAILQ_EMPTY(&sc->to_host_msg_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__); + } + if (!LIST_EMPTY(&sc->req_free_list)) { + reqp = LIST_FIRST(&sc->req_free_list); + hv_vss_init_req(reqp, recvlen, requestid, + vss_buf, sc); + STAILQ_INSERT_TAIL(&sc->to_daemon_msg_queue, + reqp, slink); + hv_vss_log_info("%s: issuing query %s" + " (%ld) to daemon\n", + __func__, + msg->hdr.vss_hdr.operation == + VSS_OP_FREEZE ? + "Freeze" : "Thaw", + reqp->vss_req.req_id); + /* + * We should wake up the daemon, + * in case it's doing poll() + */ + selwakeup(&sc->hv_vss_selinfo); + /* + * The response should be received after 5s, + * otherwise, trigger timeout. + */ + callout_reset_sbt(&reqp->callout, + SBT_1S * 5, 0, hv_vss_timeout, reqp, 0); + } else { + /* TODO Error: no buffer */ + hv_vss_log_info("Error: No buffer\n"); + } + mtx_unlock(&sc->pending_mutex); + + break; + case VSS_OP_HOT_BACKUP: + msg->body.vss_cf.flags = + VSS_HBU_NO_AUTO_RECOVERY; + hv_vss_respond_host(vss_buf, + vmbus_get_channel(sc->dev), + recvlen, requestid, 0); + break; + case VSS_OP_GET_DM_INFO: + msg->body.dm_info.flags = 0; + hv_vss_respond_host(vss_buf, + vmbus_get_channel(sc->dev), + recvlen, requestid, 0); + break; + default: + printf("Unknown operation from host: %d\n", + msg->hdr.vss_hdr.operation); + break; + } + } + + /* + * 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 = 64; /* It is big enough for the list */ + struct hv_vss_req_internal* reqp; + + LIST_INIT(&sc->req_free_list); + STAILQ_INIT(&sc->to_daemon_msg_queue); + STAILQ_INIT(&sc->to_host_msg_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->to_daemon_msg_queue)) { + reqp = STAILQ_FIRST(&sc->to_daemon_msg_queue); + STAILQ_REMOVE_HEAD(&sc->to_daemon_msg_queue, slink); + free(reqp, M_DEVBUF); + } + + while (!STAILQ_EMPTY(&sc->to_host_msg_queue)) { + reqp = STAILQ_FIRST(&sc->to_host_msg_queue); + STAILQ_REMOVE_HEAD(&sc->to_host_msg_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); + + /* create character device */ + error = make_dev_p(MAKEDEV_CHECKNAME | MAKEDEV_WAITOK, + &sc->hv_vss_dev, + &hv_vss_cdevsw, + 0, + UID_ROOT, + GID_WHEEL, + 0640, + "hv_vss_dev"); + + if (error != 0) { + hv_vss_log_info("Fail to create hv_vss_dev: %d\n", error); + return (error); + } + sc->hv_vss_dev->si_drv1 = sc; + + hv_vss_init_send_receive_queue(dev); + + return hv_util_attach(dev, hv_vss_callback); +} + +static int +hv_vss_detach(device_t dev) +{ + hv_vss_sc *sc = (hv_vss_sc*)device_get_softc(dev); + + if (sc->daemon_task != NULL) { + PROC_LOCK(sc->daemon_task); + kern_psignal(sc->daemon_task, SIGKILL); + PROC_UNLOCK(sc->daemon_task); + } + hv_vss_destroy_send_receive_queue(dev); + destroy_dev(sc->hv_vss_dev); + return hv_util_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); Index: sys/dev/hyperv/utilities/hv_util.h =================================================================== --- sys/dev/hyperv/utilities/hv_util.h +++ sys/dev/hyperv/utilities/hv_util.h @@ -56,4 +56,6 @@ int vmbus_ic_probe(device_t dev, const struct vmbus_ic_desc descs[]); int vmbus_ic_negomsg(struct hv_util_sc *, void *data, int *dlen); +boolean_t hv_util_negotiate_version(uint8_t *buf, + int framewrk_ver, int service_ver); #endif Index: sys/dev/hyperv/utilities/hv_util.c =================================================================== --- sys/dev/hyperv/utilities/hv_util.c +++ sys/dev/hyperv/utilities/hv_util.c @@ -42,6 +42,7 @@ #include #include #include +#include #include #include "vmbus_if.h" @@ -159,3 +160,83 @@ return (0); } + +/* + * version neogtiation function + * Create default response for Hyper-V Negotiate message + * @buf: Raw buffer channel data + * @framewrk_ver specifies the framework version that we can support + * @service_ver specifies the service version we can support. + */ + +boolean_t +hv_util_negotiate_version(uint8_t *buf, int framewrk_ver, int service_ver) +{ + struct hv_vmbus_icmsg_negotiate *negop; + int icmsg_major, icmsg_minor; + int fw_major, fw_minor; + int srv_major, srv_minor; + int i; + int icframe_major, icframe_minor; + struct hv_vmbus_icmsg_hdr *icmsghdrp; + boolean_t found = FALSE; + + icmsghdrp = (struct hv_vmbus_icmsg_hdr *) + &buf[sizeof(struct hv_vmbus_pipe_hdr)]; + icmsghdrp->icmsgsize = 0x10; + + fw_major = (framewrk_ver >> 16); + fw_minor = (framewrk_ver & 0xFFFF); + + srv_major = (service_ver >> 16); + srv_minor = (service_ver & 0xFFFF); + + negop = (struct hv_vmbus_icmsg_negotiate *)&buf[ + sizeof(struct hv_vmbus_pipe_hdr) + + sizeof(struct hv_vmbus_icmsg_hdr)]; + + icframe_major = negop->icframe_vercnt; + icframe_minor = 0; + icmsg_major = negop->icmsg_vercnt; + icmsg_minor = 0; + /* + * Select the framework version number we will support + */ + for (i = 0; i < negop->icframe_vercnt; i++) { + if ((negop->icversion_data[i].major == fw_major) && + (negop->icversion_data[i].minor == fw_minor)) { + icframe_major = negop->icversion_data[i].major; + icframe_minor = negop->icversion_data[i].minor; + found = true; + } + } + + if (!found) + goto handle_error; + found = false; + + for (i = negop->icframe_vercnt; + i < negop->icframe_vercnt + negop->icmsg_vercnt; i++) { + if ((negop->icversion_data[i].major == srv_major) && + (negop->icversion_data[i].minor == srv_minor)) { + icmsg_major = negop->icversion_data[i].major; + icmsg_minor = negop->icversion_data[i].minor; + found = true; + } + } + +handle_error: + if (!found) { + negop->icframe_vercnt = 0; + negop->icmsg_vercnt = 0; + } else { + negop->icframe_vercnt = 1; + negop->icmsg_vercnt = 1; + } + + negop->icversion_data[0].major = icframe_major; + negop->icversion_data[0].minor = icframe_minor; + negop->icversion_data[1].major = icmsg_major; + negop->icversion_data[1].minor = icmsg_minor; + return found; +} Index: sys/modules/hyperv/utilities/Makefile =================================================================== --- sys/modules/hyperv/utilities/Makefile +++ sys/modules/hyperv/utilities/Makefile @@ -3,7 +3,7 @@ .PATH: ${.CURDIR}/../../../dev/hyperv/utilities KMOD= hv_utils -SRCS= hv_util.c hv_kvp.c hv_timesync.c hv_shutdown.c hv_heartbeat.c +SRCS= hv_util.c hv_kvp.c hv_snapshot.c hv_timesync.c hv_shutdown.c hv_heartbeat.c hv_snapshot.c SRCS+= bus_if.h device_if.h vmbus_if.h CFLAGS+= -I${.CURDIR}/../../../dev/hyperv/include \ Index: =================================================================== --- /dev/null +++ /dev/null @@ -1,19 +0,0 @@ -# $FreeBSD$ -# Autogenerated - do NOT edit! - -DIRDEPS = \ - gnu/lib/csu \ - gnu/lib/libgcc \ - include \ - include/arpa \ - include/xlocale \ - lib/${CSU_DIR} \ - lib/libc \ - lib/libcompiler_rt \ - - -.include - -.if ${DEP_RELDIR} == ${_DEP_RELDIR} -# local dependencies - needed for -jN in clean tree -.endif Index: usr.sbin/hyperv/tools/kvp/Makefile =================================================================== --- usr.sbin/hyperv/tools/kvp/Makefile +++ usr.sbin/hyperv/tools/kvp/Makefile @@ -2,12 +2,12 @@ .include -HV_KVP_DAEMON_DISTDIR?= ${.CURDIR}/../../../contrib/hyperv/tools +HV_KVP_DAEMON_DISTDIR?= ${.CURDIR}/../../../../contrib/hyperv/tools .PATH: ${HV_KVP_DAEMON_DISTDIR} PROG= hv_kvp_daemon MAN= hv_kvp_daemon.8 -CFLAGS+= -I${.CURDIR}/../../../sys/dev/hyperv/utilities +CFLAGS+= -I${.CURDIR}/../../../../sys/dev/hyperv/utilities .include Index: usr.sbin/hyperv/tools/vss/Makefile =================================================================== --- /dev/null +++ usr.sbin/hyperv/tools/vss/Makefile @@ -0,0 +1,13 @@ +# $FreeBSD$ + +.include + +HV_VSS_DAEMON_DISTDIR?= ${.CURDIR}/../../../../contrib/hyperv/tools +.PATH: ${HV_VSS_DAEMON_DISTDIR} + +PROG= hv_vss_daemon +MAN= hv_vss_daemon.8 + +CFLAGS+= -I${.CURDIR}/../../../../sys/dev/hyperv/utilities + +.include