Index: stable/10/sys/dev/hyperv/utilities/hv_util.h =================================================================== --- stable/10/sys/dev/hyperv/utilities/hv_util.h (revision 311229) +++ stable/10/sys/dev/hyperv/utilities/hv_util.h (nonexistent) @@ -1,62 +0,0 @@ -/*- - * Copyright (c) 2009-2012,2016 Microsoft Corp. - * Copyright (c) 2012 NetApp Inc. - * Copyright (c) 2012 Citrix Inc. - * 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 _HVUTIL_H_ -#define _HVUTIL_H_ - -#include -#include - -/** - * hv_util related structures - * - */ -typedef struct hv_util_sc { - device_t ic_dev; - uint8_t *receive_buffer; - 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; - const char *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_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); - -#endif Property changes on: stable/10/sys/dev/hyperv/utilities/hv_util.h ___________________________________________________________________ Deleted: svn:eol-style ## -1 +0,0 ## -native \ No newline at end of property Deleted: svn:keywords ## -1 +0,0 ## -FreeBSD=%H \ No newline at end of property Deleted: svn:mime-type ## -1 +0,0 ## -text/plain \ No newline at end of property Index: stable/10/sys/dev/hyperv/utilities/hv_heartbeat.c =================================================================== --- stable/10/sys/dev/hyperv/utilities/hv_heartbeat.c (revision 311229) +++ stable/10/sys/dev/hyperv/utilities/hv_heartbeat.c (revision 311230) @@ -1,150 +1,150 @@ /*- * Copyright (c) 2014,2016 Microsoft Corp. * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice unmodified, this list of conditions, and the following * disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ #include __FBSDID("$FreeBSD$"); #include #include #include #include #include #include #include -#include #include +#include #include "vmbus_if.h" #define VMBUS_HEARTBEAT_FWVER_MAJOR 3 #define VMBUS_HEARTBEAT_FWVER \ VMBUS_IC_VERSION(VMBUS_HEARTBEAT_FWVER_MAJOR, 0) #define VMBUS_HEARTBEAT_MSGVER_MAJOR 3 #define VMBUS_HEARTBEAT_MSGVER \ VMBUS_IC_VERSION(VMBUS_HEARTBEAT_MSGVER_MAJOR, 0) static const struct vmbus_ic_desc vmbus_heartbeat_descs[] = { { .ic_guid = { .hv_guid = { 0x39, 0x4f, 0x16, 0x57, 0x15, 0x91, 0x78, 0x4e, 0xab, 0x55, 0x38, 0x2f, 0x3b, 0xd5, 0x42, 0x2d} }, .ic_desc = "Hyper-V Heartbeat" }, VMBUS_IC_DESC_END }; 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; void *data; /* * 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")); if (error) return; if (dlen < sizeof(*hdr)) { device_printf(sc->ic_dev, "invalid data len %d\n", dlen); return; } hdr = data; /* * Update request, which will be echoed back as response. */ switch (hdr->ic_type) { case VMBUS_ICMSG_TYPE_NEGOTIATE: error = vmbus_ic_negomsg(sc, data, &dlen, VMBUS_HEARTBEAT_FWVER, VMBUS_HEARTBEAT_MSGVER); if (error) return; break; case VMBUS_ICMSG_TYPE_HEARTBEAT: /* Only ic_seq is a must */ if (dlen < VMBUS_ICMSG_HEARTBEAT_SIZE_MIN) { device_printf(sc->ic_dev, "invalid heartbeat len %d\n", dlen); return; } ((struct vmbus_icmsg_heartbeat *)data)->ic_seq++; break; default: device_printf(sc->ic_dev, "got 0x%08x icmsg\n", hdr->ic_type); break; } /* - * 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) { return (vmbus_ic_probe(dev, vmbus_heartbeat_descs)); } static int hv_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), + DEVMETHOD(device_detach, vmbus_ic_detach), { 0, 0 } }; -static driver_t heartbeat_driver = { "hvheartbeat", heartbeat_methods, sizeof(hv_util_sc)}; +static driver_t heartbeat_driver = { + "hvheartbeat", + heartbeat_methods, + sizeof(struct vmbus_ic_softc) +}; 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); Index: stable/10/sys/dev/hyperv/utilities/hv_kvp.c =================================================================== --- stable/10/sys/dev/hyperv/utilities/hv_kvp.c (revision 311229) +++ stable/10/sys/dev/hyperv/utilities/hv_kvp.c (revision 311230) @@ -1,920 +1,920 @@ /*- * Copyright (c) 2014,2016 Microsoft Corp. * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice unmodified, this list of conditions, and the following * disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ /* * Author: Sainath Varanasi. * Date: 4/2012 * Email: bsdic@microsoft.com */ #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 +#include -#include "hv_util.h" #include "unicode.h" #include "hv_kvp.h" #include "vmbus_if.h" /* hv_kvp defines */ #define BUFFERSIZE sizeof(struct hv_kvp_msg) #define kvp_hdr hdr.kvp_hdr #define KVP_FWVER_MAJOR 3 #define KVP_FWVER VMBUS_IC_VERSION(KVP_FWVER_MAJOR, 0) #define KVP_MSGVER_MAJOR 4 #define KVP_MSGVER VMBUS_IC_VERSION(KVP_MSGVER_MAJOR, 0) /* hv_kvp debug control */ static int hv_kvp_log = 0; #define hv_kvp_log_error(...) do { \ if (hv_kvp_log > 0) \ log(LOG_ERR, "hv_kvp: " __VA_ARGS__); \ } while (0) #define hv_kvp_log_info(...) do { \ if (hv_kvp_log > 1) \ log(LOG_INFO, "hv_kvp: " __VA_ARGS__); \ } while (0) static const struct vmbus_ic_desc vmbus_kvp_descs[] = { { .ic_guid = { .hv_guid = { 0xe7, 0xf4, 0xa0, 0xa9, 0x45, 0x5a, 0x96, 0x4d, 0xb8, 0x27, 0x8a, 0x84, 0x1e, 0x8c, 0x3, 0xe6 } }, .ic_desc = "Hyper-V KVP" }, VMBUS_IC_DESC_END }; /* character device prototypes */ static d_open_t hv_kvp_dev_open; static d_close_t hv_kvp_dev_close; static d_read_t hv_kvp_dev_daemon_read; static d_write_t hv_kvp_dev_daemon_write; static d_poll_t hv_kvp_dev_daemon_poll; /* hv_kvp character device structure */ static struct cdevsw hv_kvp_cdevsw = { .d_version = D_VERSION, .d_open = hv_kvp_dev_open, .d_close = hv_kvp_dev_close, .d_read = hv_kvp_dev_daemon_read, .d_write = hv_kvp_dev_daemon_write, .d_poll = hv_kvp_dev_daemon_poll, .d_name = "hv_kvp_dev", }; /* * Global state to track and synchronize multiple * 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 * used to alter the values of the following paramters: * 1. req_in_progress * 2. req_timed_out */ struct mtx pending_mutex; struct task task; /* To track if transaction is active or not */ boolean_t req_in_progress; /* Tracks if daemon did not reply back in time */ boolean_t req_timed_out; /* Tracks if daemon is serving a request currently */ boolean_t daemon_busy; /* Length of host message */ uint32_t host_msg_len; /* Host message id */ uint64_t host_msg_id; /* Current kvp message from the host */ struct hv_kvp_msg *host_kvp_msg; /* Current kvp message for daemon */ struct hv_kvp_msg daemon_kvp_msg; /* Rcv buffer for communicating with the host*/ uint8_t *rcv_buf; /* Device semaphore to control communication */ struct sema dev_sema; /* Indicates if daemon registered with driver */ boolean_t register_done; /* Character device status */ boolean_t dev_accessed; struct cdev *hv_kvp_dev; struct proc *daemon_task; struct selinfo hv_kvp_selinfo; } hv_kvp_sc; /* hv_kvp prototypes */ static int hv_kvp_req_in_progress(hv_kvp_sc *sc); static void hv_kvp_transaction_init(hv_kvp_sc *sc, uint32_t, uint64_t, uint8_t *); static void hv_kvp_send_msg_to_daemon(hv_kvp_sc *sc); static void hv_kvp_process_request(void *context, int pending); /* * hv_kvp low level functions */ /* * Check if kvp transaction is in progres */ static int hv_kvp_req_in_progress(hv_kvp_sc *sc) { return (sc->req_in_progress); } /* * This routine is called whenever a message is received from the host */ static void hv_kvp_transaction_init(hv_kvp_sc *sc, uint32_t rcv_len, uint64_t request_id, uint8_t *rcv_buf) { /* Store all the relevant message details in the global structure */ /* Do not need to use mutex for req_in_progress here */ sc->req_in_progress = true; sc->host_msg_len = rcv_len; sc->host_msg_id = request_id; sc->rcv_buf = rcv_buf; sc->host_kvp_msg = (struct hv_kvp_msg *)&rcv_buf[ sizeof(struct hv_vmbus_pipe_hdr) + sizeof(struct hv_vmbus_icmsg_hdr)]; } /* * Convert ip related info in umsg from utf8 to utf16 and store in hmsg */ static int hv_kvp_convert_utf8_ipinfo_to_utf16(struct hv_kvp_msg *umsg, struct hv_kvp_ip_msg *host_ip_msg) { int err_ip, err_subnet, err_gway, err_dns, err_adap; int UNUSED_FLAG = 1; utf8_to_utf16((uint16_t *)host_ip_msg->kvp_ip_val.ip_addr, MAX_IP_ADDR_SIZE, (char *)umsg->body.kvp_ip_val.ip_addr, strlen((char *)umsg->body.kvp_ip_val.ip_addr), UNUSED_FLAG, &err_ip); utf8_to_utf16((uint16_t *)host_ip_msg->kvp_ip_val.sub_net, MAX_IP_ADDR_SIZE, (char *)umsg->body.kvp_ip_val.sub_net, strlen((char *)umsg->body.kvp_ip_val.sub_net), UNUSED_FLAG, &err_subnet); utf8_to_utf16((uint16_t *)host_ip_msg->kvp_ip_val.gate_way, MAX_GATEWAY_SIZE, (char *)umsg->body.kvp_ip_val.gate_way, strlen((char *)umsg->body.kvp_ip_val.gate_way), UNUSED_FLAG, &err_gway); utf8_to_utf16((uint16_t *)host_ip_msg->kvp_ip_val.dns_addr, MAX_IP_ADDR_SIZE, (char *)umsg->body.kvp_ip_val.dns_addr, strlen((char *)umsg->body.kvp_ip_val.dns_addr), UNUSED_FLAG, &err_dns); utf8_to_utf16((uint16_t *)host_ip_msg->kvp_ip_val.adapter_id, MAX_IP_ADDR_SIZE, (char *)umsg->body.kvp_ip_val.adapter_id, strlen((char *)umsg->body.kvp_ip_val.adapter_id), UNUSED_FLAG, &err_adap); host_ip_msg->kvp_ip_val.dhcp_enabled = umsg->body.kvp_ip_val.dhcp_enabled; host_ip_msg->kvp_ip_val.addr_family = umsg->body.kvp_ip_val.addr_family; return (err_ip | err_subnet | err_gway | err_dns | err_adap); } /* * Convert ip related info in hmsg from utf16 to utf8 and store in umsg */ static int hv_kvp_convert_utf16_ipinfo_to_utf8(struct hv_kvp_ip_msg *host_ip_msg, struct hv_kvp_msg *umsg) { int err_ip, err_subnet, err_gway, err_dns, err_adap; int UNUSED_FLAG = 1; device_t *devs; int devcnt; /* IP Address */ utf16_to_utf8((char *)umsg->body.kvp_ip_val.ip_addr, MAX_IP_ADDR_SIZE, (uint16_t *)host_ip_msg->kvp_ip_val.ip_addr, MAX_IP_ADDR_SIZE, UNUSED_FLAG, &err_ip); /* Adapter ID : GUID */ utf16_to_utf8((char *)umsg->body.kvp_ip_val.adapter_id, MAX_ADAPTER_ID_SIZE, (uint16_t *)host_ip_msg->kvp_ip_val.adapter_id, MAX_ADAPTER_ID_SIZE, UNUSED_FLAG, &err_adap); if (devclass_get_devices(devclass_find("hn"), &devs, &devcnt) == 0) { for (devcnt = devcnt - 1; devcnt >= 0; devcnt--) { device_t dev = devs[devcnt]; struct vmbus_channel *chan; char buf[HYPERV_GUID_STRLEN]; int n; chan = vmbus_get_channel(dev); n = hyperv_guid2str(vmbus_chan_guid_inst(chan), buf, sizeof(buf)); /* * The string in the 'kvp_ip_val.adapter_id' has * braces around the GUID; skip the leading brace * in 'kvp_ip_val.adapter_id'. */ if (strncmp(buf, ((char *)&umsg->body.kvp_ip_val.adapter_id) + 1, n) == 0) { strlcpy((char *)umsg->body.kvp_ip_val.adapter_id, device_get_nameunit(dev), MAX_ADAPTER_ID_SIZE); break; } } free(devs, M_TEMP); } /* Address Family , DHCP , SUBNET, Gateway, DNS */ umsg->kvp_hdr.operation = host_ip_msg->operation; umsg->body.kvp_ip_val.addr_family = host_ip_msg->kvp_ip_val.addr_family; umsg->body.kvp_ip_val.dhcp_enabled = host_ip_msg->kvp_ip_val.dhcp_enabled; utf16_to_utf8((char *)umsg->body.kvp_ip_val.sub_net, MAX_IP_ADDR_SIZE, (uint16_t *)host_ip_msg->kvp_ip_val.sub_net, MAX_IP_ADDR_SIZE, UNUSED_FLAG, &err_subnet); utf16_to_utf8((char *)umsg->body.kvp_ip_val.gate_way, MAX_GATEWAY_SIZE, (uint16_t *)host_ip_msg->kvp_ip_val.gate_way, MAX_GATEWAY_SIZE, UNUSED_FLAG, &err_gway); utf16_to_utf8((char *)umsg->body.kvp_ip_val.dns_addr, MAX_IP_ADDR_SIZE, (uint16_t *)host_ip_msg->kvp_ip_val.dns_addr, MAX_IP_ADDR_SIZE, UNUSED_FLAG, &err_dns); return (err_ip | err_subnet | err_gway | err_dns | err_adap); } /* * Prepare a user kvp msg based on host kvp msg (utf16 to utf8) * Ensure utf16_utf8 takes care of the additional string terminating char!! */ static void hv_kvp_convert_hostmsg_to_usermsg(struct hv_kvp_msg *hmsg, struct hv_kvp_msg *umsg) { int utf_err = 0; uint32_t value_type; struct hv_kvp_ip_msg *host_ip_msg; host_ip_msg = (struct hv_kvp_ip_msg*)hmsg; memset(umsg, 0, sizeof(struct hv_kvp_msg)); umsg->kvp_hdr.operation = hmsg->kvp_hdr.operation; umsg->kvp_hdr.pool = hmsg->kvp_hdr.pool; switch (umsg->kvp_hdr.operation) { case HV_KVP_OP_SET_IP_INFO: hv_kvp_convert_utf16_ipinfo_to_utf8(host_ip_msg, umsg); break; case HV_KVP_OP_GET_IP_INFO: utf16_to_utf8((char *)umsg->body.kvp_ip_val.adapter_id, MAX_ADAPTER_ID_SIZE, (uint16_t *)host_ip_msg->kvp_ip_val.adapter_id, MAX_ADAPTER_ID_SIZE, 1, &utf_err); umsg->body.kvp_ip_val.addr_family = host_ip_msg->kvp_ip_val.addr_family; break; case HV_KVP_OP_SET: value_type = hmsg->body.kvp_set.data.value_type; switch (value_type) { case HV_REG_SZ: umsg->body.kvp_set.data.value_size = utf16_to_utf8( (char *)umsg->body.kvp_set.data.msg_value.value, HV_KVP_EXCHANGE_MAX_VALUE_SIZE - 1, (uint16_t *)hmsg->body.kvp_set.data.msg_value.value, hmsg->body.kvp_set.data.value_size, 1, &utf_err); /* utf8 encoding */ umsg->body.kvp_set.data.value_size = umsg->body.kvp_set.data.value_size / 2; break; case HV_REG_U32: umsg->body.kvp_set.data.value_size = sprintf(umsg->body.kvp_set.data.msg_value.value, "%d", hmsg->body.kvp_set.data.msg_value.value_u32) + 1; break; case HV_REG_U64: umsg->body.kvp_set.data.value_size = sprintf(umsg->body.kvp_set.data.msg_value.value, "%llu", (unsigned long long) hmsg->body.kvp_set.data.msg_value.value_u64) + 1; break; } umsg->body.kvp_set.data.key_size = utf16_to_utf8( umsg->body.kvp_set.data.key, HV_KVP_EXCHANGE_MAX_KEY_SIZE - 1, (uint16_t *)hmsg->body.kvp_set.data.key, hmsg->body.kvp_set.data.key_size, 1, &utf_err); /* utf8 encoding */ umsg->body.kvp_set.data.key_size = umsg->body.kvp_set.data.key_size / 2; break; case HV_KVP_OP_GET: umsg->body.kvp_get.data.key_size = utf16_to_utf8(umsg->body.kvp_get.data.key, HV_KVP_EXCHANGE_MAX_KEY_SIZE - 1, (uint16_t *)hmsg->body.kvp_get.data.key, hmsg->body.kvp_get.data.key_size, 1, &utf_err); /* utf8 encoding */ umsg->body.kvp_get.data.key_size = umsg->body.kvp_get.data.key_size / 2; break; case HV_KVP_OP_DELETE: umsg->body.kvp_delete.key_size = utf16_to_utf8(umsg->body.kvp_delete.key, HV_KVP_EXCHANGE_MAX_KEY_SIZE - 1, (uint16_t *)hmsg->body.kvp_delete.key, hmsg->body.kvp_delete.key_size, 1, &utf_err); /* utf8 encoding */ umsg->body.kvp_delete.key_size = umsg->body.kvp_delete.key_size / 2; break; case HV_KVP_OP_ENUMERATE: umsg->body.kvp_enum_data.index = hmsg->body.kvp_enum_data.index; break; default: hv_kvp_log_info("%s: daemon_kvp_msg: Invalid operation : %d\n", __func__, umsg->kvp_hdr.operation); } } /* * Prepare a host kvp msg based on user kvp msg (utf8 to utf16) */ static int hv_kvp_convert_usermsg_to_hostmsg(struct hv_kvp_msg *umsg, struct hv_kvp_msg *hmsg) { int hkey_len = 0, hvalue_len = 0, utf_err = 0; struct hv_kvp_exchg_msg_value *host_exchg_data; char *key_name, *value; struct hv_kvp_ip_msg *host_ip_msg = (struct hv_kvp_ip_msg *)hmsg; switch (hmsg->kvp_hdr.operation) { case HV_KVP_OP_GET_IP_INFO: return (hv_kvp_convert_utf8_ipinfo_to_utf16(umsg, host_ip_msg)); case HV_KVP_OP_SET_IP_INFO: case HV_KVP_OP_SET: case HV_KVP_OP_DELETE: return (0); case HV_KVP_OP_ENUMERATE: host_exchg_data = &hmsg->body.kvp_enum_data.data; key_name = umsg->body.kvp_enum_data.data.key; hkey_len = utf8_to_utf16((uint16_t *)host_exchg_data->key, ((HV_KVP_EXCHANGE_MAX_KEY_SIZE / 2) - 2), key_name, strlen(key_name), 1, &utf_err); /* utf16 encoding */ host_exchg_data->key_size = 2 * (hkey_len + 1); value = umsg->body.kvp_enum_data.data.msg_value.value; hvalue_len = utf8_to_utf16( (uint16_t *)host_exchg_data->msg_value.value, ((HV_KVP_EXCHANGE_MAX_VALUE_SIZE / 2) - 2), value, strlen(value), 1, &utf_err); host_exchg_data->value_size = 2 * (hvalue_len + 1); host_exchg_data->value_type = HV_REG_SZ; if ((hkey_len < 0) || (hvalue_len < 0)) return (EINVAL); return (0); case HV_KVP_OP_GET: host_exchg_data = &hmsg->body.kvp_get.data; value = umsg->body.kvp_get.data.msg_value.value; hvalue_len = utf8_to_utf16( (uint16_t *)host_exchg_data->msg_value.value, ((HV_KVP_EXCHANGE_MAX_VALUE_SIZE / 2) - 2), value, strlen(value), 1, &utf_err); /* Convert value size to uft16 */ host_exchg_data->value_size = 2 * (hvalue_len + 1); /* Use values by string */ host_exchg_data->value_type = HV_REG_SZ; if ((hkey_len < 0) || (hvalue_len < 0)) return (EINVAL); return (0); default: return (EINVAL); } } /* * Send the response back to the host. */ static void hv_kvp_respond_host(hv_kvp_sc *sc, uint32_t error) { struct hv_vmbus_icmsg_hdr *hv_icmsg_hdrp; hv_icmsg_hdrp = (struct hv_vmbus_icmsg_hdr *) &sc->rcv_buf[sizeof(struct hv_vmbus_pipe_hdr)]; hv_icmsg_hdrp->status = error; hv_icmsg_hdrp->icflags = HV_ICMSGHDRFLAG_TRANSACTION | HV_ICMSGHDRFLAG_RESPONSE; error = vmbus_chan_send(vmbus_get_channel(sc->dev), VMBUS_CHANPKT_TYPE_INBAND, 0, sc->rcv_buf, sc->host_msg_len, sc->host_msg_id); if (error) hv_kvp_log_info("%s: hv_kvp_respond_host: sendpacket error:%d\n", __func__, error); } /* * This is the main kvp kernel process that interacts with both user daemon * and the host */ static void hv_kvp_send_msg_to_daemon(hv_kvp_sc *sc) { struct hv_kvp_msg *hmsg = sc->host_kvp_msg; struct hv_kvp_msg *umsg = &sc->daemon_kvp_msg; /* Prepare kvp_msg to be sent to user */ hv_kvp_convert_hostmsg_to_usermsg(hmsg, umsg); /* Send the msg to user via function deamon_read - setting sema */ sema_post(&sc->dev_sema); /* We should wake up the daemon, in case it's doing poll() */ selwakeup(&sc->hv_kvp_selinfo); } /* * Function to read the kvp request buffer from host * and interact with daemon */ static void hv_kvp_process_request(void *context, int pending) { uint8_t *kvp_buf; struct vmbus_channel *channel; uint32_t recvlen = 0; uint64_t requestid; struct hv_vmbus_icmsg_hdr *icmsghdrp; int ret = 0, error; hv_kvp_sc *sc; 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; ret = vmbus_chan_recv(channel, kvp_buf, &recvlen, &requestid); KASSERT(ret != ENOBUFS, ("hvkvp 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 *) &kvp_buf[sizeof(struct hv_vmbus_pipe_hdr)]; hv_kvp_transaction_init(sc, recvlen, requestid, kvp_buf); if (icmsghdrp->icmsgtype == HV_ICMSGTYPE_NEGOTIATE) { error = vmbus_ic_negomsg(&sc->util_sc, kvp_buf, &recvlen, KVP_FWVER, KVP_MSGVER); /* XXX handle vmbus_ic_negomsg failure. */ if (!error) hv_kvp_respond_host(sc, HV_S_OK); else hv_kvp_respond_host(sc, HV_E_FAIL); /* * It is ok to not acquire the mutex before setting * req_in_progress here because negotiation is the * first thing that happens and hence there is no * chance of a race condition. */ sc->req_in_progress = false; hv_kvp_log_info("%s :version negotiated\n", __func__); } else { if (!sc->daemon_busy) { hv_kvp_log_info("%s: issuing qury to daemon\n", __func__); mtx_lock(&sc->pending_mutex); sc->req_timed_out = false; sc->daemon_busy = true; mtx_unlock(&sc->pending_mutex); hv_kvp_send_msg_to_daemon(sc); hv_kvp_log_info("%s: waiting for daemon\n", __func__); } /* Wait 5 seconds for daemon to respond back */ tsleep(sc, 0, "kvpworkitem", 5 * hz); hv_kvp_log_info("%s: came out of wait\n", __func__); } mtx_lock(&sc->pending_mutex); /* Notice that once req_timed_out is set to true * it will remain true until the next request is * sent to the daemon. The response from daemon * is forwarded to host only when this flag is * false. */ sc->req_timed_out = true; /* * Cancel request if so need be. */ 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_E_FAIL); sc->req_in_progress = false; } mtx_unlock(&sc->pending_mutex); /* * Try reading next buffer */ recvlen = sc->util_sc.ic_buflen; ret = vmbus_chan_recv(channel, kvp_buf, &recvlen, &requestid); KASSERT(ret != ENOBUFS, ("hvkvp recvbuf is not large enough")); /* XXX check recvlen to make sure that it contains enough data */ hv_kvp_log_info("%s: read: context %p, ret =%d, recvlen=%d\n", __func__, context, ret, recvlen); } } /* * Callback routine that gets called whenever there is a message from host */ static void hv_kvp_callback(struct vmbus_channel *chan __unused, void *context) { hv_kvp_sc *sc = (hv_kvp_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_kvp_log_info("%s: Queuing work item\n", __func__); taskqueue_enqueue(taskqueue_thread, &sc->task); } } static int hv_kvp_dev_open(struct cdev *dev, int oflags, int devtype, struct thread *td) { hv_kvp_sc *sc = (hv_kvp_sc*)dev->si_drv1; hv_kvp_log_info("%s: Opened device \"hv_kvp_device\" successfully.\n", __func__); if (sc->dev_accessed) return (-EBUSY); sc->daemon_task = curproc; sc->dev_accessed = true; sc->daemon_busy = false; return (0); } static int hv_kvp_dev_close(struct cdev *dev __unused, int fflag __unused, int devtype __unused, struct thread *td __unused) { hv_kvp_sc *sc = (hv_kvp_sc*)dev->si_drv1; hv_kvp_log_info("%s: Closing device \"hv_kvp_device\".\n", __func__); sc->dev_accessed = false; sc->register_done = false; return (0); } /* * hv_kvp_daemon read invokes this function * acts as a send to daemon */ static int hv_kvp_dev_daemon_read(struct cdev *dev, struct uio *uio, int ioflag __unused) { size_t amt; int error = 0; struct hv_kvp_msg *hv_kvp_dev_buf; hv_kvp_sc *sc = (hv_kvp_sc*)dev->si_drv1; /* Read is not allowed util registering is done. */ if (!sc->register_done) return (EPERM); sema_wait(&sc->dev_sema); hv_kvp_dev_buf = malloc(sizeof(*hv_kvp_dev_buf), M_TEMP, M_WAITOK); memcpy(hv_kvp_dev_buf, &sc->daemon_kvp_msg, sizeof(struct hv_kvp_msg)); amt = MIN(uio->uio_resid, uio->uio_offset >= BUFFERSIZE + 1 ? 0 : BUFFERSIZE + 1 - uio->uio_offset); if ((error = uiomove(hv_kvp_dev_buf, amt, uio)) != 0) hv_kvp_log_info("%s: hv_kvp uiomove read failed!\n", __func__); free(hv_kvp_dev_buf, M_TEMP); return (error); } /* * hv_kvp_daemon write invokes this function * acts as a recieve from daemon */ static int hv_kvp_dev_daemon_write(struct cdev *dev, struct uio *uio, int ioflag __unused) { size_t amt; int error = 0; struct hv_kvp_msg *hv_kvp_dev_buf; hv_kvp_sc *sc = (hv_kvp_sc*)dev->si_drv1; uio->uio_offset = 0; hv_kvp_dev_buf = malloc(sizeof(*hv_kvp_dev_buf), M_TEMP, M_WAITOK); amt = MIN(uio->uio_resid, BUFFERSIZE); error = uiomove(hv_kvp_dev_buf, amt, uio); if (error != 0) { free(hv_kvp_dev_buf, M_TEMP); return (error); } memcpy(&sc->daemon_kvp_msg, hv_kvp_dev_buf, sizeof(struct hv_kvp_msg)); free(hv_kvp_dev_buf, M_TEMP); if (sc->register_done == false) { if (sc->daemon_kvp_msg.kvp_hdr.operation == HV_KVP_OP_REGISTER) { sc->register_done = true; hv_kvp_callback(vmbus_get_channel(sc->dev), dev->si_drv1); } else { hv_kvp_log_info("%s, KVP Registration Failed\n", __func__); return (EINVAL); } } else { mtx_lock(&sc->pending_mutex); if(!sc->req_timed_out) { struct hv_kvp_msg *hmsg = sc->host_kvp_msg; struct hv_kvp_msg *umsg = &sc->daemon_kvp_msg; error = hv_kvp_convert_usermsg_to_hostmsg(umsg, hmsg); hv_kvp_respond_host(sc, umsg->hdr.error); wakeup(sc); sc->req_in_progress = false; if (umsg->hdr.error != HV_S_OK) hv_kvp_log_info("%s, Error 0x%x from daemon\n", __func__, umsg->hdr.error); if (error) hv_kvp_log_info("%s, Error from convert\n", __func__); } sc->daemon_busy = false; mtx_unlock(&sc->pending_mutex); } return (error); } /* * hv_kvp_daemon poll invokes this function to check if data is available * for daemon to read. */ static int hv_kvp_dev_daemon_poll(struct cdev *dev, int events, struct thread *td) { int revents = 0; hv_kvp_sc *sc = (hv_kvp_sc*)dev->si_drv1; mtx_lock(&sc->pending_mutex); /* * We check global flag daemon_busy for the data availiability for * userland to read. Deamon_busy is set to true before driver has data * for daemon to read. It is set to false after daemon sends * then response back to driver. */ if (sc->daemon_busy == true) revents = POLLIN; else selrecord(td, &sc->hv_kvp_selinfo); mtx_unlock(&sc->pending_mutex); return (revents); } static int hv_kvp_probe(device_t dev) { return (vmbus_ic_probe(dev, vmbus_kvp_descs)); } static int hv_kvp_attach(device_t dev) { int error; struct sysctl_oid_list *child; struct sysctl_ctx_list *ctx; hv_kvp_sc *sc = (hv_kvp_sc*)device_get_softc(dev); sc->dev = dev; sema_init(&sc->dev_sema, 0, "hv_kvp device semaphore"); mtx_init(&sc->pending_mutex, "hv-kvp 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_kvp_log", CTLFLAG_RWTUN, &hv_kvp_log, 0, "Hyperv KVP service log level"); TASK_INIT(&sc->task, 0, hv_kvp_process_request, sc); /* create character device */ error = make_dev_p(MAKEDEV_CHECKNAME | MAKEDEV_WAITOK, &sc->hv_kvp_dev, &hv_kvp_cdevsw, 0, UID_ROOT, GID_WHEEL, 0640, "hv_kvp_dev"); if (error != 0) 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 hv_kvp_detach(device_t dev) { hv_kvp_sc *sc = (hv_kvp_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); } destroy_dev(sc->hv_kvp_dev); - return hv_util_detach(dev); + return (vmbus_ic_detach(dev)); } static device_method_t kvp_methods[] = { /* Device interface */ DEVMETHOD(device_probe, hv_kvp_probe), DEVMETHOD(device_attach, hv_kvp_attach), DEVMETHOD(device_detach, hv_kvp_detach), { 0, 0 } }; static driver_t kvp_driver = { "hvkvp", kvp_methods, sizeof(hv_kvp_sc)}; static devclass_t kvp_devclass; DRIVER_MODULE(hv_kvp, vmbus, kvp_driver, kvp_devclass, NULL, NULL); MODULE_VERSION(hv_kvp, 1); MODULE_DEPEND(hv_kvp, vmbus, 1, 1, 1); Index: stable/10/sys/dev/hyperv/utilities/hv_shutdown.c =================================================================== --- stable/10/sys/dev/hyperv/utilities/hv_shutdown.c (revision 311229) +++ stable/10/sys/dev/hyperv/utilities/hv_shutdown.c (revision 311230) @@ -1,165 +1,165 @@ /*- * Copyright (c) 2014,2016 Microsoft Corp. * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice unmodified, this list of conditions, and the following * disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ #include __FBSDID("$FreeBSD$"); #include #include #include #include #include #include #include #include -#include #include +#include #include "vmbus_if.h" #define VMBUS_SHUTDOWN_FWVER_MAJOR 3 #define VMBUS_SHUTDOWN_FWVER \ VMBUS_IC_VERSION(VMBUS_SHUTDOWN_FWVER_MAJOR, 0) #define VMBUS_SHUTDOWN_MSGVER_MAJOR 3 #define VMBUS_SHUTDOWN_MSGVER \ VMBUS_IC_VERSION(VMBUS_SHUTDOWN_MSGVER_MAJOR, 0) static const struct vmbus_ic_desc vmbus_shutdown_descs[] = { { .ic_guid = { .hv_guid = { 0x31, 0x60, 0x0b, 0x0e, 0x13, 0x52, 0x34, 0x49, 0x81, 0x8b, 0x38, 0xd9, 0x0c, 0xed, 0x39, 0xdb } }, .ic_desc = "Hyper-V Shutdown" }, VMBUS_IC_DESC_END }; 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; uint64_t xactid; void *data; /* * 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")); if (error) return; if (dlen < sizeof(*hdr)) { device_printf(sc->ic_dev, "invalid data len %d\n", dlen); return; } hdr = data; /* * Update request, which will be echoed back as response. */ switch (hdr->ic_type) { case VMBUS_ICMSG_TYPE_NEGOTIATE: error = vmbus_ic_negomsg(sc, data, &dlen, VMBUS_SHUTDOWN_FWVER, VMBUS_SHUTDOWN_MSGVER); if (error) return; break; case VMBUS_ICMSG_TYPE_SHUTDOWN: if (dlen < VMBUS_ICMSG_SHUTDOWN_SIZE_MIN) { device_printf(sc->ic_dev, "invalid shutdown len %d\n", dlen); return; } msg = data; /* XXX ic_flags definition? */ if (msg->ic_haltflags == 0 || msg->ic_haltflags == 1) { device_printf(sc->ic_dev, "shutdown requested\n"); hdr->ic_status = VMBUS_ICMSG_STATUS_OK; do_shutdown = 1; } else { device_printf(sc->ic_dev, "unknown shutdown flags " "0x%08x\n", msg->ic_haltflags); hdr->ic_status = VMBUS_ICMSG_STATUS_FAIL; } break; default: device_printf(sc->ic_dev, "got 0x%08x icmsg\n", hdr->ic_type); break; } /* - * 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) { return (vmbus_ic_probe(dev, vmbus_shutdown_descs)); } static int hv_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), + DEVMETHOD(device_detach, vmbus_ic_detach), { 0, 0 } }; -static driver_t shutdown_driver = { "hvshutdown", shutdown_methods, sizeof(hv_util_sc)}; +static driver_t shutdown_driver = { + "hvshutdown", + shutdown_methods, + sizeof(struct vmbus_ic_softc) +}; 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); Index: stable/10/sys/dev/hyperv/utilities/hv_snapshot.c =================================================================== --- stable/10/sys/dev/hyperv/utilities/hv_snapshot.c (revision 311229) +++ stable/10/sys/dev/hyperv/utilities/hv_snapshot.c (revision 311230) @@ -1,1061 +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 __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 -#include "hv_util.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 hv_util_sc util_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.receive_buffer; + 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 hv_util_attach(dev, hv_vss_callback); + 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 hv_util_detach(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); Index: stable/10/sys/dev/hyperv/utilities/hv_timesync.c =================================================================== --- stable/10/sys/dev/hyperv/utilities/hv_timesync.c (revision 311229) +++ stable/10/sys/dev/hyperv/utilities/hv_timesync.c (revision 311230) @@ -1,243 +1,243 @@ /*- * Copyright (c) 2014,2016 Microsoft Corp. * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice unmodified, this list of conditions, and the following * disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ #include __FBSDID("$FreeBSD$"); #include #include #include #include #include #include #include #include #include -#include #include +#include #include "vmbus_if.h" #define VMBUS_TIMESYNC_FWVER_MAJOR 3 #define VMBUS_TIMESYNC_FWVER \ VMBUS_IC_VERSION(VMBUS_TIMESYNC_FWVER_MAJOR, 0) #define VMBUS_TIMESYNC_MSGVER_MAJOR 4 #define VMBUS_TIMESYNC_MSGVER \ VMBUS_IC_VERSION(VMBUS_TIMESYNC_MSGVER_MAJOR, 0) #define VMBUS_TIMESYNC_DORTT(sc) \ ((sc)->ic_msgver >= VMBUS_IC_VERSION(4, 0) && \ (hyperv_features & CPUID_HV_MSR_TIME_REFCNT)) static const struct vmbus_ic_desc vmbus_timesync_descs[] = { { .ic_guid = { .hv_guid = { 0x30, 0xe6, 0x27, 0x95, 0xae, 0xd0, 0x7b, 0x49, 0xad, 0xce, 0xe8, 0x0a, 0xb0, 0x17, 0x5c, 0xaf } }, .ic_desc = "Hyper-V Timesync" }, VMBUS_IC_DESC_END }; SYSCTL_NODE(_hw, OID_AUTO, hvtimesync, CTLFLAG_RW | CTLFLAG_MPSAFE, NULL, "Hyper-V timesync interface"); static int vmbus_ts_ignore_sync = 0; SYSCTL_INT(_hw_hvtimesync, OID_AUTO, ignore_sync, CTLFLAG_RWTUN, &vmbus_ts_ignore_sync, 0, "Ignore the sync request."); /* * Trigger sample sync when drift exceeds threshold (ms). * Ignore the sample request when set to 0. */ static int vmbus_ts_sample_thresh = 100; SYSCTL_INT(_hw_hvtimesync, OID_AUTO, sample_thresh, CTLFLAG_RWTUN, &vmbus_ts_sample_thresh, 0, "Threshold that makes sample request trigger the sync (unit: ms)."); static int vmbus_ts_sample_verbose = 0; SYSCTL_INT(_hw_hvtimesync, OID_AUTO, sample_verbose, CTLFLAG_RWTUN, &vmbus_ts_sample_verbose, 0, "Increase sample request verbosity."); static void -vmbus_timesync(struct hv_util_sc *sc, uint64_t hvtime, uint64_t sent_tc, +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, rtt = 0; if (VMBUS_TIMESYNC_DORTT(sc)) rtt = rdmsr(MSR_HV_TIME_REF_COUNT) - sent_tc; 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; if ((tsflags & VMBUS_ICMSG_TS_FLAG_SYNC) && !vmbus_ts_ignore_sync) { struct timespec hv_ts; if (bootverbose) { device_printf(sc->ic_dev, "apply sync request, " "hv: %ju, vm: %ju\n", (uintmax_t)hv_ns, (uintmax_t)vm_ns); } hv_ts.tv_sec = hv_ns / NANOSEC; hv_ts.tv_nsec = hv_ns % NANOSEC; kern_clock_settime(curthread, CLOCK_REALTIME, &hv_ts); /* Done! */ return; } if ((tsflags & VMBUS_ICMSG_TS_FLAG_SAMPLE) && vmbus_ts_sample_thresh > 0) { int64_t diff; if (vmbus_ts_sample_verbose) { device_printf(sc->ic_dev, "sample request, " "hv: %ju, vm: %ju\n", (uintmax_t)hv_ns, (uintmax_t)vm_ns); } if (hv_ns > vm_ns) diff = hv_ns - vm_ns; else diff = vm_ns - hv_ns; /* nanosec -> millisec */ diff /= 1000000; if (diff > vmbus_ts_sample_thresh) { struct timespec hv_ts; if (bootverbose) { device_printf(sc->ic_dev, "apply sample request, hv: %ju, vm: %ju\n", (uintmax_t)hv_ns, (uintmax_t)vm_ns); } hv_ts.tv_sec = hv_ns / NANOSEC; hv_ts.tv_nsec = hv_ns % NANOSEC; kern_clock_settime(curthread, CLOCK_REALTIME, &hv_ts); } /* Done */ return; } } 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; /* * 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")); if (error) return; if (dlen < sizeof(*hdr)) { device_printf(sc->ic_dev, "invalid data len %d\n", dlen); return; } hdr = data; /* * Update request, which will be echoed back as response. */ switch (hdr->ic_type) { case VMBUS_ICMSG_TYPE_NEGOTIATE: error = vmbus_ic_negomsg(sc, data, &dlen, 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; } msg = data; vmbus_timesync(sc, msg->ic_hvtime, msg->ic_sent_tc, msg->ic_tsflags); break; default: device_printf(sc->ic_dev, "got 0x%08x icmsg\n", hdr->ic_type); break; } /* - * 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) { return (vmbus_ic_probe(dev, vmbus_timesync_descs)); } static int hv_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), + DEVMETHOD(device_detach, vmbus_ic_detach), { 0, 0 } }; -static driver_t timesync_driver = { "hvtimesync", timesync_methods, sizeof(hv_util_sc)}; +static driver_t timesync_driver = { + "hvtimesync", + timesync_methods, + sizeof(struct vmbus_ic_softc) +}; static devclass_t timesync_devclass; DRIVER_MODULE(hv_timesync, vmbus, timesync_driver, timesync_devclass, NULL, NULL); MODULE_VERSION(hv_timesync, 1); MODULE_DEPEND(hv_timesync, vmbus, 1, 1, 1); Index: stable/10/sys/dev/hyperv/utilities/hv_util.c =================================================================== --- stable/10/sys/dev/hyperv/utilities/hv_util.c (revision 311229) +++ stable/10/sys/dev/hyperv/utilities/hv_util.c (revision 311230) @@ -1,289 +1,306 @@ /*- * Copyright (c) 2014,2016 Microsoft Corp. * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice unmodified, this list of conditions, and the following * disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. * * $FreeBSD$ */ /* * A common driver for all hyper-V util services. */ #include #include #include #include #include #include #include #include #include #include #include -#include #include +#include #include "vmbus_if.h" #define VMBUS_IC_BRSIZE (4 * PAGE_SIZE) #define VMBUS_IC_VERCNT 2 #define VMBUS_IC_NEGOSZ \ __offsetof(struct vmbus_icmsg_negotiate, ic_ver[VMBUS_IC_VERCNT]) CTASSERT(VMBUS_IC_NEGOSZ < VMBUS_IC_BRSIZE); 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; int i, cnt, dlen = *dlen0, error; uint32_t sel_fw_ver, sel_msg_ver; bool has_fw_ver, has_msg_ver; /* * Preliminary message verification. */ if (dlen < sizeof(*nego)) { device_printf(sc->ic_dev, "truncated ic negotiate, len %d\n", dlen); return (EINVAL); } nego = data; if (nego->ic_fwver_cnt == 0) { device_printf(sc->ic_dev, "ic negotiate does not contain " "framework version %u\n", nego->ic_fwver_cnt); return (EINVAL); } if (nego->ic_msgver_cnt == 0) { device_printf(sc->ic_dev, "ic negotiate does not contain " "message version %u\n", nego->ic_msgver_cnt); return (EINVAL); } cnt = nego->ic_fwver_cnt + nego->ic_msgver_cnt; if (dlen < __offsetof(struct vmbus_icmsg_negotiate, ic_ver[cnt])) { device_printf(sc->ic_dev, "ic negotiate does not contain " "versions %d\n", dlen); return (EINVAL); } error = EOPNOTSUPP; /* * Find the best match framework version. */ has_fw_ver = false; for (i = 0; i < nego->ic_fwver_cnt; ++i) { if (VMBUS_ICVER_LE(nego->ic_ver[i], fw_ver)) { if (!has_fw_ver) { sel_fw_ver = nego->ic_ver[i]; has_fw_ver = true; } else if (VMBUS_ICVER_GT(nego->ic_ver[i], sel_fw_ver)) { sel_fw_ver = nego->ic_ver[i]; } } } if (!has_fw_ver) { device_printf(sc->ic_dev, "failed to select framework " "version\n"); goto done; } /* * Fine the best match message version. */ has_msg_ver = false; for (i = nego->ic_fwver_cnt; i < nego->ic_fwver_cnt + nego->ic_msgver_cnt; ++i) { if (VMBUS_ICVER_LE(nego->ic_ver[i], msg_ver)) { if (!has_msg_ver) { sel_msg_ver = nego->ic_ver[i]; has_msg_ver = true; } else if (VMBUS_ICVER_GT(nego->ic_ver[i], sel_msg_ver)) { sel_msg_ver = nego->ic_ver[i]; } } } if (!has_msg_ver) { device_printf(sc->ic_dev, "failed to select message " "version\n"); goto done; } error = 0; done: if (bootverbose || !has_fw_ver || !has_msg_ver) { if (has_fw_ver) { device_printf(sc->ic_dev, "sel framework version: " "%u.%u\n", VMBUS_ICVER_MAJOR(sel_fw_ver), VMBUS_ICVER_MINOR(sel_fw_ver)); } for (i = 0; i < nego->ic_fwver_cnt; i++) { device_printf(sc->ic_dev, "supp framework version: " "%u.%u\n", VMBUS_ICVER_MAJOR(nego->ic_ver[i]), VMBUS_ICVER_MINOR(nego->ic_ver[i])); } if (has_msg_ver) { device_printf(sc->ic_dev, "sel message version: " "%u.%u\n", VMBUS_ICVER_MAJOR(sel_msg_ver), VMBUS_ICVER_MINOR(sel_msg_ver)); } for (i = nego->ic_fwver_cnt; i < nego->ic_fwver_cnt + nego->ic_msgver_cnt; i++) { device_printf(sc->ic_dev, "supp message version: " "%u.%u\n", VMBUS_ICVER_MAJOR(nego->ic_ver[i]), VMBUS_ICVER_MINOR(nego->ic_ver[i])); } } if (error) return (error); /* Record the selected versions. */ sc->ic_fwver = sel_fw_ver; sc->ic_msgver = sel_msg_ver; /* One framework version. */ nego->ic_fwver_cnt = 1; nego->ic_ver[0] = sel_fw_ver; /* One message version. */ nego->ic_msgver_cnt = 1; nego->ic_ver[1] = sel_msg_ver; /* Update data size. */ nego->ic_hdr.ic_dsize = VMBUS_IC_NEGOSZ - sizeof(struct vmbus_icmsg_hdr); /* Update total size, if necessary. */ if (dlen < VMBUS_IC_NEGOSZ) *dlen0 = VMBUS_IC_NEGOSZ; return (0); } int vmbus_ic_probe(device_t dev, const struct vmbus_ic_desc descs[]) { device_t bus = device_get_parent(dev); const struct vmbus_ic_desc *d; if (resource_disabled(device_get_name(dev), 0)) return (ENXIO); for (d = descs; d->ic_desc != NULL; ++d) { if (VMBUS_PROBE_GUID(bus, dev, &d->ic_guid) == 0) { device_set_desc(dev, d->ic_desc); return (BUS_PROBE_DEFAULT); } } return (ENXIO); } 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; int error; 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 * batched reading. Furthermore, some services such as KVP can * only handle one message from the host at a time. * Turn off batched reading for all util drivers before we open the * channel. */ vmbus_chan_set_readbatch(chan, false); 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); } ctx = device_get_sysctl_ctx(dev); child = SYSCTL_CHILDREN(device_get_sysctl_tree(dev)); SYSCTL_ADD_PROC(ctx, child, OID_AUTO, "fw_version", CTLTYPE_STRING | CTLFLAG_RD | CTLFLAG_MPSAFE, sc, 0, vmbus_ic_fwver_sysctl, "A", "framework version"); SYSCTL_ADD_PROC(ctx, child, OID_AUTO, "msg_version", CTLTYPE_STRING | CTLFLAG_RD | CTLFLAG_MPSAFE, sc, 0, vmbus_ic_msgver_sysctl, "A", "message version"); return (0); } 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", VMBUS_ICVER_MAJOR(sc->ic_fwver), VMBUS_ICVER_MINOR(sc->ic_fwver)); return sysctl_handle_string(oidp, verstr, sizeof(verstr), req); } 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", VMBUS_ICVER_MAJOR(sc->ic_msgver), VMBUS_ICVER_MINOR(sc->ic_msgver)); return sysctl_handle_string(oidp, verstr, sizeof(verstr), req); } 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); } Index: stable/10/sys/dev/hyperv/utilities/vmbus_icvar.h =================================================================== --- stable/10/sys/dev/hyperv/utilities/vmbus_icvar.h (nonexistent) +++ stable/10/sys/dev/hyperv/utilities/vmbus_icvar.h (revision 311230) @@ -0,0 +1,61 @@ +/*- + * Copyright (c) 2009-2012,2016 Microsoft Corp. + * Copyright (c) 2012 NetApp Inc. + * Copyright (c) 2012 Citrix Inc. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice unmodified, this list of conditions, and the following + * disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF + * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * $FreeBSD$ + */ + +#ifndef _VMBUS_ICVAR_H_ +#define _VMBUS_ICVAR_H_ + +#include +#include + +struct vmbus_ic_softc { + device_t ic_dev; + uint8_t *ic_buf; + int ic_buflen; + uint32_t ic_fwver; /* framework version */ + uint32_t ic_msgver; /* message version */ +}; + +struct vmbus_ic_desc { + const struct hyperv_guid ic_guid; + const char *ic_desc; +}; + +#define VMBUS_IC_DESC_END { .ic_desc = NULL } + +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 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 /* !_VMBUS_ICVAR_H_ */ Property changes on: stable/10/sys/dev/hyperv/utilities/vmbus_icvar.h ___________________________________________________________________ Added: svn:eol-style ## -0,0 +1 ## +native \ No newline at end of property Added: svn:keywords ## -0,0 +1 ## +FreeBSD=%H \ No newline at end of property Added: svn:mime-type ## -0,0 +1 ## +text/plain \ No newline at end of property Index: stable/10 =================================================================== --- stable/10 (revision 311229) +++ stable/10 (revision 311230) Property changes on: stable/10 ___________________________________________________________________ Modified: svn:mergeinfo ## -0,0 +0,1 ## Merged /head:r310312-310314