Index: head/sys/conf/files.amd64 =================================================================== --- head/sys/conf/files.amd64 +++ head/sys/conf/files.amd64 @@ -266,7 +266,10 @@ dev/hyperv/netvsc/hv_rndis_filter.c optional hyperv dev/hyperv/stordisengage/hv_ata_pci_disengage.c optional hyperv 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_shutdown.c optional hyperv +dev/hyperv/utilities/hv_timesync.c optional hyperv dev/hyperv/utilities/hv_util.c optional hyperv dev/hyperv/vmbus/hv_channel.c optional hyperv dev/hyperv/vmbus/hv_channel_mgmt.c optional hyperv Index: head/sys/conf/files.i386 =================================================================== --- head/sys/conf/files.i386 +++ head/sys/conf/files.i386 @@ -240,7 +240,10 @@ dev/hyperv/netvsc/hv_rndis_filter.c optional hyperv dev/hyperv/stordisengage/hv_ata_pci_disengage.c optional hyperv 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_shutdown.c optional hyperv +dev/hyperv/utilities/hv_timesync.c optional hyperv dev/hyperv/utilities/hv_util.c optional hyperv dev/hyperv/vmbus/hv_channel.c optional hyperv dev/hyperv/vmbus/hv_channel_mgmt.c optional hyperv Index: head/sys/dev/hyperv/include/hyperv.h =================================================================== --- head/sys/dev/hyperv/include/hyperv.h +++ head/sys/dev/hyperv/include/hyperv.h @@ -919,40 +919,5 @@ return (ret); } - -/** - * KVP related structures - * - */ -typedef struct hv_vmbus_service { - hv_guid guid; /* Hyper-V GUID */ - char *name; /* name of service */ - boolean_t enabled; /* service enabled */ - void* context; - struct task task; - /* - * function to initialize service - */ - int (*init)(struct hv_vmbus_service *); - - /* - * function to process Hyper-V messages - */ - void (*callback)(void *); - - /* - * function to uninitilize service - */ - int (*uninit)(struct hv_vmbus_service *); -} hv_vmbus_service; - -extern uint8_t* receive_buffer[]; -extern hv_vmbus_service service_table[]; extern uint32_t hv_vmbus_protocal_version; - -void hv_kvp_callback(void *context); -int hv_kvp_init(hv_vmbus_service *serv); -void hv_kvp_deinit(void); - #endif /* __HYPERV_H__ */ - Index: head/sys/dev/hyperv/utilities/hv_heartbeat.c =================================================================== --- head/sys/dev/hyperv/utilities/hv_heartbeat.c +++ head/sys/dev/hyperv/utilities/hv_heartbeat.c @@ -0,0 +1,129 @@ +/*- + * Copyright (c) 2014 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$ + */ + +#include +#include +#include +#include +#include +#include +#include + +#include +#include "hv_util.h" + +/* Heartbeat Service */ +static hv_guid service_guid = { .data = + {0x39, 0x4f, 0x16, 0x57, 0x15, 0x91, 0x78, 0x4e, + 0xab, 0x55, 0x38, 0x2f, 0x3b, 0xd5, 0x42, 0x2d} }; + +/** + * Process heartbeat message + */ +static void +hv_heartbeat_cb(void *context) +{ + uint8_t* buf; + hv_vmbus_channel* channel; + uint32_t recvlen; + uint64_t requestid; + int ret; + + struct hv_vmbus_heartbeat_msg_data* heartbeat_msg; + struct hv_vmbus_icmsg_hdr* icmsghdrp; + hv_util_sc *softc; + + softc = (hv_util_sc*)context; + buf = softc->receive_buffer;; + channel = softc->hv_dev->channel; + + ret = hv_vmbus_channel_recv_packet(channel, buf, PAGE_SIZE, &recvlen, + &requestid); + + if ((ret == 0) && recvlen > 0) { + + icmsghdrp = (struct hv_vmbus_icmsg_hdr *) + &buf[sizeof(struct hv_vmbus_pipe_hdr)]; + + if (icmsghdrp->icmsgtype == HV_ICMSGTYPE_NEGOTIATE) { + hv_negotiate_version(icmsghdrp, NULL, buf); + + } else { + heartbeat_msg = + (struct hv_vmbus_heartbeat_msg_data *) + &buf[sizeof(struct hv_vmbus_pipe_hdr) + + sizeof(struct hv_vmbus_icmsg_hdr)]; + + heartbeat_msg->seq_num += 1; + } + + icmsghdrp->icflags = HV_ICMSGHDRFLAG_TRANSACTION | + HV_ICMSGHDRFLAG_RESPONSE; + + hv_vmbus_channel_send_packet(channel, buf, recvlen, requestid, + HV_VMBUS_PACKET_TYPE_DATA_IN_BAND, 0); + } +} + +static int +hv_heartbeat_probe(device_t dev) +{ + const char *p = vmbus_get_type(dev); + if (!memcmp(p, &service_guid, sizeof(hv_guid))) { + device_set_desc(dev, "Hyper-V Heartbeat Service"); + return BUS_PROBE_DEFAULT; + } + + return ENXIO; +} + +static int +hv_heartbeat_attach(device_t dev) +{ + hv_util_sc *softc = (hv_util_sc*)device_get_softc(dev); + + softc->callback = hv_heartbeat_cb; + + return hv_util_attach(dev); +} + +static device_method_t heartbeat_methods[] = { + /* Device interface */ + DEVMETHOD(device_probe, hv_heartbeat_probe), + DEVMETHOD(device_attach, hv_heartbeat_attach), + DEVMETHOD(device_detach, hv_util_detach), + { 0, 0 } +}; + +static driver_t heartbeat_driver = { "hvheartbeat", heartbeat_methods, sizeof(hv_util_sc)}; + +static devclass_t heartbeat_devclass; + +DRIVER_MODULE(hv_heartbeat, vmbus, heartbeat_driver, heartbeat_devclass, NULL, NULL); +MODULE_VERSION(hv_heartbeat, 1); +MODULE_DEPEND(hv_heartbeat, vmbus, 1, 1, 1); Index: head/sys/dev/hyperv/utilities/hv_kvp.h =================================================================== --- head/sys/dev/hyperv/utilities/hv_kvp.h +++ head/sys/dev/hyperv/utilities/hv_kvp.h @@ -238,17 +238,4 @@ struct hv_kvp_ipaddr_value kvp_ip_val; } __attribute__((packed)); - -#define HV_SHUT_DOWN 0 -#define HV_TIME_SYNCH 1 -#define HV_HEART_BEAT 2 -#define HV_KVP 3 -#define HV_MAX_UTIL_SERVICES 4 - -#define HV_WLTIMEDELTA 116444736000000000L /* in 100ns unit */ -#define HV_ICTIMESYNCFLAG_PROBE 0 -#define HV_ICTIMESYNCFLAG_SYNC 1 -#define HV_ICTIMESYNCFLAG_SAMPLE 2 -#define HV_NANO_SEC_PER_SEC 1000000000 - #endif /* _KVP_H */ Index: head/sys/dev/hyperv/utilities/hv_kvp.c =================================================================== --- head/sys/dev/hyperv/utilities/hv_kvp.c +++ head/sys/dev/hyperv/utilities/hv_kvp.c @@ -63,6 +63,7 @@ #include #include +#include "hv_util.h" #include "unicode.h" #include "hv_kvp.h" @@ -74,8 +75,6 @@ /* hv_kvp debug control */ static int hv_kvp_log = 0; -SYSCTL_INT(_dev, OID_AUTO, hv_kvp_log, CTLFLAG_RW, &hv_kvp_log, 0, - "hv_kvp log"); #define hv_kvp_log_error(...) do { \ if (hv_kvp_log > 0) \ @@ -87,6 +86,10 @@ log(LOG_INFO, "hv_kvp: " __VA_ARGS__); \ } while (0) +static hv_guid service_guid = { .data = + {0xe7, 0xf4, 0xa0, 0xa9, 0x45, 0x5a, 0x96, 0x4d, + 0xb8, 0x27, 0x8a, 0x84, 0x1e, 0x8c, 0x3, 0xe6} }; + /* character device prototypes */ static d_open_t hv_kvp_dev_open; static d_close_t hv_kvp_dev_close; @@ -94,12 +97,6 @@ static d_write_t hv_kvp_dev_daemon_write; static d_poll_t hv_kvp_dev_daemon_poll; -/* hv_kvp prototypes */ -static int hv_kvp_req_in_progress(void); -static void hv_kvp_transaction_init(uint32_t, hv_vmbus_channel *, uint64_t, uint8_t *); -static void hv_kvp_send_msg_to_daemon(void); -static void hv_kvp_process_request(void *context, int pending); - /* hv_kvp character device structure */ static struct cdevsw hv_kvp_cdevsw = { @@ -111,67 +108,67 @@ .d_poll = hv_kvp_dev_daemon_poll, .d_name = "hv_kvp_dev", }; -static struct cdev *hv_kvp_dev; -static struct hv_kvp_msg *hv_kvp_dev_buf; -struct proc *daemon_task; -static struct selinfo hv_kvp_selinfo; /* * Global state to track and synchronize multiple * KVP transaction requests from the host. */ -static struct { +typedef struct hv_kvp_sc { + struct hv_util_sc util_sc; - /* Unless specified the pending mutex should be + /* Unless specified the pending mutex should be * used to alter the values of the following paramters: * 1. req_in_progress * 2. req_timed_out - * 3. pending_reqs. */ - struct mtx pending_mutex; - + struct mtx pending_mutex; + + struct task task; + /* To track if transaction is active or not */ - boolean_t req_in_progress; + boolean_t req_in_progress; /* Tracks if daemon did not reply back in time */ - boolean_t req_timed_out; + boolean_t req_timed_out; /* Tracks if daemon is serving a request currently */ boolean_t daemon_busy; - /* Count of KVP requests from Hyper-V. */ - uint64_t pending_reqs; - - - /* Length of host message */ - uint32_t host_msg_len; - /* Pointer to channel */ - hv_vmbus_channel *channelp; + /* Length of host message */ + uint32_t host_msg_len; /* Host message id */ - uint64_t host_msg_id; - + uint64_t host_msg_id; + /* Current kvp message from the host */ - struct hv_kvp_msg *host_kvp_msg; - + struct hv_kvp_msg *host_kvp_msg; + /* Current kvp message for daemon */ - struct hv_kvp_msg daemon_kvp_msg; - + struct hv_kvp_msg daemon_kvp_msg; + /* Rcv buffer for communicating with the host*/ - uint8_t *rcv_buf; - + uint8_t *rcv_buf; + /* Device semaphore to control communication */ - struct sema dev_sema; - + struct sema dev_sema; + /* Indicates if daemon registered with driver */ - boolean_t register_done; - + boolean_t register_done; + /* Character device status */ - boolean_t dev_accessed; -} kvp_globals; + boolean_t dev_accessed; + + struct cdev *hv_kvp_dev; + + struct proc *daemon_task; -/* global vars */ -MALLOC_DECLARE(M_HV_KVP_DEV_BUF); -MALLOC_DEFINE(M_HV_KVP_DEV_BUF, "hv_kvp_dev buffer", "buffer for hv_kvp_dev module"); + 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 @@ -181,10 +178,10 @@ * Check if kvp transaction is in progres */ static int -hv_kvp_req_in_progress(void) +hv_kvp_req_in_progress(hv_kvp_sc *sc) { - return (kvp_globals.req_in_progress); + return (sc->req_in_progress); } @@ -192,18 +189,17 @@ * This routine is called whenever a message is received from the host */ static void -hv_kvp_transaction_init(uint32_t rcv_len, hv_vmbus_channel *rcv_channel, +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 */ - kvp_globals.req_in_progress = true; - kvp_globals.host_msg_len = rcv_len; - kvp_globals.channelp = rcv_channel; - kvp_globals.host_msg_id = request_id; - kvp_globals.rcv_buf = rcv_buf; - kvp_globals.host_kvp_msg = (struct hv_kvp_msg *)&rcv_buf[ + 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)]; } @@ -255,12 +251,12 @@ * 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, +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, @@ -291,7 +287,7 @@ 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; @@ -386,7 +382,7 @@ 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, @@ -408,16 +404,13 @@ * Ensure utf16_utf8 takes care of the additional string terminating char!! */ static void -hv_kvp_convert_hostmsg_to_usermsg(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 = (struct hv_kvp_ip_msg *) - kvp_globals.host_kvp_msg; - - struct hv_kvp_msg *hmsg = kvp_globals.host_kvp_msg; - struct hv_kvp_msg *umsg = &kvp_globals.daemon_kvp_msg; + 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; @@ -522,14 +515,12 @@ * Prepare a host kvp msg based on user kvp msg (utf8 to utf16) */ static int -hv_kvp_convert_usermsg_to_hostmsg(void) +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_msg *umsg = &kvp_globals.daemon_kvp_msg; - struct hv_kvp_msg *hmsg = kvp_globals.host_kvp_msg; struct hv_kvp_ip_msg *host_ip_msg = (struct hv_kvp_ip_msg *)hmsg; switch (hmsg->kvp_hdr.operation) { @@ -561,7 +552,7 @@ if ((hkey_len < 0) || (hvalue_len < 0)) return (HV_KVP_E_FAIL); - + return (KVP_SUCCESS); case HV_KVP_OP_GET: @@ -577,9 +568,9 @@ /* Use values by string */ host_exchg_data->value_type = HV_REG_SZ; - if ((hkey_len < 0) || (hvalue_len < 0)) + if ((hkey_len < 0) || (hvalue_len < 0)) return (HV_KVP_E_FAIL); - + return (KVP_SUCCESS); default: @@ -592,22 +583,22 @@ * Send the response back to the host. */ static void -hv_kvp_respond_host(int error) +hv_kvp_respond_host(hv_kvp_sc *sc, int error) { struct hv_vmbus_icmsg_hdr *hv_icmsg_hdrp; hv_icmsg_hdrp = (struct hv_vmbus_icmsg_hdr *) - &kvp_globals.rcv_buf[sizeof(struct hv_vmbus_pipe_hdr)]; + &sc->rcv_buf[sizeof(struct hv_vmbus_pipe_hdr)]; if (error) error = HV_KVP_E_FAIL; hv_icmsg_hdrp->status = error; hv_icmsg_hdrp->icflags = HV_ICMSGHDRFLAG_TRANSACTION | HV_ICMSGHDRFLAG_RESPONSE; - - error = hv_vmbus_channel_send_packet(kvp_globals.channelp, - kvp_globals.rcv_buf, - kvp_globals.host_msg_len, kvp_globals.host_msg_id, + + error = hv_vmbus_channel_send_packet(sc->util_sc.hv_dev->channel, + sc->rcv_buf, + sc->host_msg_len, sc->host_msg_id, HV_VMBUS_PACKET_TYPE_DATA_IN_BAND, 0); if (error) @@ -621,16 +612,19 @@ * and the host */ static void -hv_kvp_send_msg_to_daemon(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(); + hv_kvp_convert_hostmsg_to_usermsg(hmsg, umsg); /* Send the msg to user via function deamon_read - setting sema */ - sema_post(&kvp_globals.dev_sema); + sema_post(&sc->dev_sema); /* We should wake up the daemon, in case it's doing poll() */ - selwakeup(&hv_kvp_selinfo); + selwakeup(&sc->hv_kvp_selinfo); } @@ -642,95 +636,80 @@ hv_kvp_process_request(void *context, int pending) { uint8_t *kvp_buf; - hv_vmbus_channel *channel = context; + hv_vmbus_channel *channel; uint32_t recvlen = 0; uint64_t requestid; struct hv_vmbus_icmsg_hdr *icmsghdrp; int ret = 0; - uint64_t pending_cnt = 1; - + hv_kvp_sc *sc; + hv_kvp_log_info("%s: entering hv_kvp_process_request\n", __func__); - kvp_buf = receive_buffer[HV_KVP]; + + sc = (hv_kvp_sc*)context; + kvp_buf = sc->util_sc.receive_buffer;; + channel = sc->util_sc.hv_dev->channel; + ret = hv_vmbus_channel_recv_packet(channel, kvp_buf, 2 * PAGE_SIZE, &recvlen, &requestid); - /* - * We start counting only after the daemon registers - * and therefore there could be requests pending in - * the VMBus that are not reflected in pending_cnt. - * Therefore we continue reading as long as either of - * the below conditions is true. - */ + while ((ret == 0) && (recvlen > 0)) { + + icmsghdrp = (struct hv_vmbus_icmsg_hdr *) + &kvp_buf[sizeof(struct hv_vmbus_pipe_hdr)]; - while ((pending_cnt>0) || ((ret == 0) && (recvlen > 0))) { + hv_kvp_transaction_init(sc, recvlen, requestid, kvp_buf); + if (icmsghdrp->icmsgtype == HV_ICMSGTYPE_NEGOTIATE) { + hv_kvp_negotiate_version(icmsghdrp, NULL, kvp_buf); + hv_kvp_respond_host(sc, ret); + + /* + * 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); - if ((ret == 0) && (recvlen>0)) { - - icmsghdrp = (struct hv_vmbus_icmsg_hdr *) - &kvp_buf[sizeof(struct hv_vmbus_pipe_hdr)]; - - hv_kvp_transaction_init(recvlen, channel, requestid, kvp_buf); - if (icmsghdrp->icmsgtype == HV_ICMSGTYPE_NEGOTIATE) { - hv_kvp_negotiate_version(icmsghdrp, NULL, kvp_buf); - hv_kvp_respond_host(ret); - - /* - * 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. - */ - - kvp_globals.req_in_progress = false; - hv_kvp_log_info("%s :version negotiated\n", __func__); - - } else { - if (!kvp_globals.daemon_busy) { - - hv_kvp_log_info("%s: issuing qury to daemon\n", __func__); - mtx_lock(&kvp_globals.pending_mutex); - kvp_globals.req_timed_out = false; - kvp_globals.daemon_busy = true; - mtx_unlock(&kvp_globals.pending_mutex); - - hv_kvp_send_msg_to_daemon(); - hv_kvp_log_info("%s: waiting for daemon\n", __func__); - } - - /* Wait 5 seconds for daemon to respond back */ - tsleep(&kvp_globals, 0, "kvpworkitem", 5 * hz); - hv_kvp_log_info("%s: came out of wait\n", __func__); + 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(&kvp_globals.pending_mutex); - + 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. + * is forwarded to host only when this flag is + * false. */ - kvp_globals.req_timed_out = true; + sc->req_timed_out = true; /* * Cancel request if so need be. */ - if (hv_kvp_req_in_progress()) { + 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(HV_KVP_E_FAIL); - kvp_globals.req_in_progress = false; - } - - /* - * Decrement pending request count and - */ - if (kvp_globals.pending_reqs>0) { - kvp_globals.pending_reqs = kvp_globals.pending_reqs - 1; + hv_kvp_respond_host(sc, HV_KVP_E_FAIL); + sc->req_in_progress = false; } - pending_cnt = kvp_globals.pending_reqs; - - mtx_unlock(&kvp_globals.pending_mutex); + + mtx_unlock(&sc->pending_mutex); /* * Try reading next buffer @@ -738,104 +717,43 @@ recvlen = 0; ret = hv_vmbus_channel_recv_packet(channel, kvp_buf, 2 * PAGE_SIZE, &recvlen, &requestid); - hv_kvp_log_info("%s: read: context %p, pending_cnt %llu ret =%d, recvlen=%d\n", - __func__, context, (unsigned long long)pending_cnt, ret, recvlen); - } + 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 */ -void +static void hv_kvp_callback(void *context) { - uint64_t pending_cnt = 0; - - if (kvp_globals.register_done == false) { - kvp_globals.channelp = context; - TASK_INIT(&service_table[HV_KVP].task, 0, hv_kvp_process_request, context); - } else { - mtx_lock(&kvp_globals.pending_mutex); - kvp_globals.pending_reqs = kvp_globals.pending_reqs + 1; - pending_cnt = kvp_globals.pending_reqs; - mtx_unlock(&kvp_globals.pending_mutex); - if (pending_cnt == 1) { - hv_kvp_log_info("%s: Queuing work item\n", __func__); - taskqueue_enqueue(taskqueue_thread, &service_table[HV_KVP].task); - } - } -} - - -/* - * This function is called by the hv_kvp_init - - * creates character device hv_kvp_dev - * allocates memory to hv_kvp_dev_buf - * - */ -static int -hv_kvp_dev_init(void) -{ - int error = 0; - - /* initialize semaphore */ - sema_init(&kvp_globals.dev_sema, 0, "hv_kvp device semaphore"); - /* create character device */ - error = make_dev_p(MAKEDEV_CHECKNAME | MAKEDEV_WAITOK, - &hv_kvp_dev, - &hv_kvp_cdevsw, - 0, - UID_ROOT, - GID_WHEEL, - 0640, - "hv_kvp_dev"); - - if (error != 0) - return (error); - + hv_kvp_sc *sc = (hv_kvp_sc*)context; /* - * Malloc with M_WAITOK flag will never fail. - */ - hv_kvp_dev_buf = malloc(sizeof(*hv_kvp_dev_buf), M_HV_KVP_DEV_BUF, M_WAITOK | - M_ZERO); - - return (0); -} - - -/* - * This function is called by the hv_kvp_deinit - - * destroy character device - */ -static void -hv_kvp_dev_destroy(void) -{ - - if (daemon_task != NULL) { - PROC_LOCK(daemon_task); - kern_psignal(daemon_task, SIGKILL); - PROC_UNLOCK(daemon_task); + 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); } - - destroy_dev(hv_kvp_dev); - free(hv_kvp_dev_buf, M_HV_KVP_DEV_BUF); - return; } - 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 (kvp_globals.dev_accessed) + if (sc->dev_accessed) return (-EBUSY); - - daemon_task = curproc; - kvp_globals.dev_accessed = true; - kvp_globals.daemon_busy = false; + + sc->daemon_task = curproc; + sc->dev_accessed = true; + sc->daemon_busy = false; return (0); } @@ -844,10 +762,11 @@ 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__); - kvp_globals.dev_accessed = false; - kvp_globals.register_done = false; + sc->dev_accessed = false; + sc->register_done = false; return (0); } @@ -857,18 +776,21 @@ * acts as a send to daemon */ static int -hv_kvp_dev_daemon_read(struct cdev *dev __unused, struct uio *uio, int ioflag __unused) +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; /* Check hv_kvp daemon registration status*/ - if (!kvp_globals.register_done) + if (!sc->register_done) return (KVP_ERROR); - sema_wait(&kvp_globals.dev_sema); + sema_wait(&sc->dev_sema); - memcpy(hv_kvp_dev_buf, &kvp_globals.daemon_kvp_msg, sizeof(struct hv_kvp_msg)); + 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); @@ -876,6 +798,7 @@ 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); } @@ -885,29 +808,30 @@ * acts as a recieve from daemon */ static int -hv_kvp_dev_daemon_write(struct cdev *dev __unused, struct uio *uio, int ioflag __unused) +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) + 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)); - memcpy(&kvp_globals.daemon_kvp_msg, hv_kvp_dev_buf, sizeof(struct hv_kvp_msg)); - - if (kvp_globals.register_done == false) { - if (kvp_globals.daemon_kvp_msg.kvp_hdr.operation == HV_KVP_OP_REGISTER) { - - kvp_globals.register_done = true; - if (kvp_globals.channelp) { - - hv_kvp_callback(kvp_globals.channelp); - } + 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(dev->si_drv1); } else { hv_kvp_log_info("%s, KVP Registration Failed\n", __func__); @@ -915,18 +839,20 @@ } } else { - mtx_lock(&kvp_globals.pending_mutex); + mtx_lock(&sc->pending_mutex); - if(!kvp_globals.req_timed_out) { - - hv_kvp_convert_usermsg_to_hostmsg(); - hv_kvp_respond_host(KVP_SUCCESS); - wakeup(&kvp_globals); - kvp_globals.req_in_progress = false; + if(!sc->req_timed_out) { + struct hv_kvp_msg *hmsg = sc->host_kvp_msg; + struct hv_kvp_msg *umsg = &sc->daemon_kvp_msg; + + hv_kvp_convert_usermsg_to_hostmsg(umsg, hmsg); + hv_kvp_respond_host(sc, KVP_SUCCESS); + wakeup(sc); + sc->req_in_progress = false; } - kvp_globals.daemon_busy = false; - mtx_unlock(&kvp_globals.pending_mutex); + sc->daemon_busy = false; + mtx_unlock(&sc->pending_mutex); } return (error); @@ -938,53 +864,106 @@ * for daemon to read. */ static int -hv_kvp_dev_daemon_poll(struct cdev *dev __unused, int events, struct thread *td) +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(&kvp_globals.pending_mutex); + 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 (kvp_globals.daemon_busy == true) + if (sc->daemon_busy == true) revents = POLLIN; else - selrecord(td, &hv_kvp_selinfo); + selrecord(td, &sc->hv_kvp_selinfo); - mtx_unlock(&kvp_globals.pending_mutex); + mtx_unlock(&sc->pending_mutex); return (revents); } +static int +hv_kvp_probe(device_t dev) +{ + const char *p = vmbus_get_type(dev); + if (!memcmp(p, &service_guid, sizeof(hv_guid))) { + device_set_desc(dev, "Hyper-V KVP Service"); + return BUS_PROBE_DEFAULT; + } -/* - * hv_kvp initialization function - * called from hv_util service. - * - */ -int -hv_kvp_init(hv_vmbus_service *srv) + return ENXIO; +} + +static int +hv_kvp_attach(device_t dev) { - int error = 0; + int error; + struct sysctl_oid_list *child; + struct sysctl_ctx_list *ctx; - memset(&kvp_globals, 0, sizeof(kvp_globals)); + hv_kvp_sc *sc = (hv_kvp_sc*)device_get_softc(dev); - error = hv_kvp_dev_init(); - mtx_init(&kvp_globals.pending_mutex, "hv-kvp pending mutex", + sc->util_sc.callback = hv_kvp_callback; + sema_init(&sc->dev_sema, 0, "hv_kvp device semaphore"); + mtx_init(&sc->pending_mutex, "hv-kvp pending mutex", NULL, MTX_DEF); - return (error); -} + 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_RW, &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); +} -void -hv_kvp_deinit(void) +static int +hv_kvp_detach(device_t dev) { - hv_kvp_dev_destroy(); - mtx_destroy(&kvp_globals.pending_mutex); + hv_kvp_sc *sc = (hv_kvp_sc*)device_get_softc(dev); - return; + 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); } + +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: head/sys/dev/hyperv/utilities/hv_shutdown.c =================================================================== --- head/sys/dev/hyperv/utilities/hv_shutdown.c +++ head/sys/dev/hyperv/utilities/hv_shutdown.c @@ -0,0 +1,151 @@ +/*- + * Copyright (c) 2014 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 "hv_util.h" + +static hv_guid service_guid = { .data = + {0x31, 0x60, 0x0B, 0X0E, 0x13, 0x52, 0x34, 0x49, + 0x81, 0x8B, 0x38, 0XD9, 0x0C, 0xED, 0x39, 0xDB} }; + +/** + * Shutdown + */ +static void +hv_shutdown_cb(void *context) +{ + uint8_t* buf; + hv_vmbus_channel* channel; + uint8_t execute_shutdown = 0; + hv_vmbus_icmsg_hdr* icmsghdrp; + uint32_t recv_len; + uint64_t request_id; + int ret; + hv_vmbus_shutdown_msg_data* shutdown_msg; + hv_util_sc *softc; + + softc = (hv_util_sc*)context; + buf = softc->receive_buffer;; + channel = softc->hv_dev->channel; + ret = hv_vmbus_channel_recv_packet(channel, buf, PAGE_SIZE, + &recv_len, &request_id); + + if ((ret == 0) && recv_len > 0) { + + icmsghdrp = (struct hv_vmbus_icmsg_hdr *) + &buf[sizeof(struct hv_vmbus_pipe_hdr)]; + + if (icmsghdrp->icmsgtype == HV_ICMSGTYPE_NEGOTIATE) { + hv_negotiate_version(icmsghdrp, NULL, buf); + + } else { + shutdown_msg = + (struct hv_vmbus_shutdown_msg_data *) + &buf[sizeof(struct hv_vmbus_pipe_hdr) + + sizeof(struct hv_vmbus_icmsg_hdr)]; + + switch (shutdown_msg->flags) { + case 0: + case 1: + icmsghdrp->status = HV_S_OK; + execute_shutdown = 1; + if(bootverbose) + printf("Shutdown request received -" + " graceful shutdown initiated\n"); + break; + default: + icmsghdrp->status = HV_E_FAIL; + execute_shutdown = 0; + printf("Shutdown request received -" + " Invalid request\n"); + break; + } + } + + icmsghdrp->icflags = HV_ICMSGHDRFLAG_TRANSACTION | + HV_ICMSGHDRFLAG_RESPONSE; + + hv_vmbus_channel_send_packet(channel, buf, + recv_len, request_id, + HV_VMBUS_PACKET_TYPE_DATA_IN_BAND, 0); + } + + if (execute_shutdown) + shutdown_nice(RB_POWEROFF); +} + +static int +hv_shutdown_probe(device_t dev) +{ + const char *p = vmbus_get_type(dev); + if (!memcmp(p, &service_guid, sizeof(hv_guid))) { + device_set_desc(dev, "Hyper-V Shutdown Service"); + return BUS_PROBE_DEFAULT; + } + + return ENXIO; +} + +static int +hv_shutdown_attach(device_t dev) +{ + hv_util_sc *softc = (hv_util_sc*)device_get_softc(dev); + + softc->callback = hv_shutdown_cb; + + return hv_util_attach(dev); +} + +static device_method_t shutdown_methods[] = { + /* Device interface */ + DEVMETHOD(device_probe, hv_shutdown_probe), + DEVMETHOD(device_attach, hv_shutdown_attach), + DEVMETHOD(device_detach, hv_util_detach), + { 0, 0 } +}; + +static driver_t shutdown_driver = { "hvshutdown", shutdown_methods, sizeof(hv_util_sc)}; + +static devclass_t shutdown_devclass; + +DRIVER_MODULE(hv_shutdown, vmbus, shutdown_driver, shutdown_devclass, NULL, NULL); +MODULE_VERSION(hv_shutdown, 1); +MODULE_DEPEND(hv_shutdown, vmbus, 1, 1, 1); Index: head/sys/dev/hyperv/utilities/hv_timesync.c =================================================================== --- head/sys/dev/hyperv/utilities/hv_timesync.c +++ head/sys/dev/hyperv/utilities/hv_timesync.c @@ -0,0 +1,216 @@ +/*- + * Copyright (c) 2014 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 "hv_util.h" + +#define HV_WLTIMEDELTA 116444736000000000L /* in 100ns unit */ +#define HV_ICTIMESYNCFLAG_PROBE 0 +#define HV_ICTIMESYNCFLAG_SYNC 1 +#define HV_ICTIMESYNCFLAG_SAMPLE 2 +#define HV_NANO_SEC_PER_SEC 1000000000 + +/* Time Sync data */ +typedef struct { + uint64_t data; +} time_sync_data; + + /* Time Synch Service */ +static hv_guid service_guid = {.data = + {0x30, 0xe6, 0x27, 0x95, 0xae, 0xd0, 0x7b, 0x49, + 0xad, 0xce, 0xe8, 0x0a, 0xb0, 0x17, 0x5c, 0xaf } }; + +struct hv_ictimesync_data { + uint64_t parenttime; + uint64_t childtime; + uint64_t roundtriptime; + uint8_t flags; +} __packed; + +typedef struct hv_timesync_sc { + hv_util_sc util_sc; + struct task task; + time_sync_data time_msg; +} hv_timesync_sc; + +/** + * Set host time based on time sync message from host + */ +static void +hv_set_host_time(void *context, int pending) +{ + hv_timesync_sc *softc = (hv_timesync_sc*)context; + uint64_t hosttime = softc->time_msg.data; + struct timespec guest_ts, host_ts; + uint64_t host_tns; + int64_t diff; + int error; + + host_tns = (hosttime - HV_WLTIMEDELTA) * 100; + host_ts.tv_sec = (time_t)(host_tns/HV_NANO_SEC_PER_SEC); + host_ts.tv_nsec = (long)(host_tns%HV_NANO_SEC_PER_SEC); + + nanotime(&guest_ts); + + diff = (int64_t)host_ts.tv_sec - (int64_t)guest_ts.tv_sec; + + /* + * If host differs by 5 seconds then make the guest catch up + */ + if (diff > 5 || diff < -5) { + error = kern_clock_settime(curthread, CLOCK_REALTIME, + &host_ts); + } +} + +/** + * @brief Synchronize time with host after reboot, restore, etc. + * + * ICTIMESYNCFLAG_SYNC flag bit indicates reboot, restore events of the VM. + * After reboot the flag ICTIMESYNCFLAG_SYNC is included in the first time + * message after the timesync channel is opened. Since the hv_utils module is + * loaded after hv_vmbus, the first message is usually missed. The other + * thing is, systime is automatically set to emulated hardware clock which may + * not be UTC time or in the same time zone. So, to override these effects, we + * use the first 50 time samples for initial system time setting. + */ +static inline +void hv_adj_guesttime(hv_timesync_sc *sc, uint64_t hosttime, uint8_t flags) +{ + sc->time_msg.data = hosttime; + + if (((flags & HV_ICTIMESYNCFLAG_SYNC) != 0) || + ((flags & HV_ICTIMESYNCFLAG_SAMPLE) != 0)) { + taskqueue_enqueue(taskqueue_thread, &sc->task); + } +} + +/** + * Time Sync Channel message handler + */ +static void +hv_timesync_cb(void *context) +{ + hv_vmbus_channel* channel; + hv_vmbus_icmsg_hdr* icmsghdrp; + uint32_t recvlen; + uint64_t requestId; + int ret; + uint8_t* time_buf; + struct hv_ictimesync_data* timedatap; + hv_timesync_sc *softc; + + softc = (hv_timesync_sc*)context; + channel = softc->util_sc.hv_dev->channel; + time_buf = softc->util_sc.receive_buffer; + + ret = hv_vmbus_channel_recv_packet(channel, time_buf, + PAGE_SIZE, &recvlen, &requestId); + + if ((ret == 0) && recvlen > 0) { + icmsghdrp = (struct hv_vmbus_icmsg_hdr *) &time_buf[ + sizeof(struct hv_vmbus_pipe_hdr)]; + + if (icmsghdrp->icmsgtype == HV_ICMSGTYPE_NEGOTIATE) { + hv_negotiate_version(icmsghdrp, NULL, time_buf); + } else { + timedatap = (struct hv_ictimesync_data *) &time_buf[ + sizeof(struct hv_vmbus_pipe_hdr) + + sizeof(struct hv_vmbus_icmsg_hdr)]; + hv_adj_guesttime(softc, timedatap->parenttime, timedatap->flags); + } + + icmsghdrp->icflags = HV_ICMSGHDRFLAG_TRANSACTION + | HV_ICMSGHDRFLAG_RESPONSE; + + hv_vmbus_channel_send_packet(channel, time_buf, + recvlen, requestId, + HV_VMBUS_PACKET_TYPE_DATA_IN_BAND, 0); + } +} + +static int +hv_timesync_probe(device_t dev) +{ + const char *p = vmbus_get_type(dev); + if (!memcmp(p, &service_guid, sizeof(hv_guid))) { + device_set_desc(dev, "Hyper-V Time Synch Service"); + return BUS_PROBE_DEFAULT; + } + + return ENXIO; +} + +static int +hv_timesync_attach(device_t dev) +{ + hv_timesync_sc *softc = device_get_softc(dev); + + softc->util_sc.callback = hv_timesync_cb; + TASK_INIT(&softc->task, 1, hv_set_host_time, softc); + + return hv_util_attach(dev); +} + +static int +hv_timesync_detach(device_t dev) +{ + hv_timesync_sc *softc = device_get_softc(dev); + taskqueue_drain(taskqueue_thread, &softc->task); + + return hv_util_detach(dev); +} + +static device_method_t timesync_methods[] = { + /* Device interface */ + DEVMETHOD(device_probe, hv_timesync_probe), + DEVMETHOD(device_attach, hv_timesync_attach), + DEVMETHOD(device_detach, hv_timesync_detach), + { 0, 0 } +}; + +static driver_t timesync_driver = { "hvtimesync", timesync_methods, sizeof(hv_timesync_sc)}; + +static devclass_t timesync_devclass; + +DRIVER_MODULE(hv_timesync, vmbus, timesync_driver, timesync_devclass, NULL, NULL); +MODULE_VERSION(hv_timesync, 1); +MODULE_DEPEND(hv_timesync, vmbus, 1, 1, 1); Index: head/sys/dev/hyperv/utilities/hv_util.h =================================================================== --- head/sys/dev/hyperv/utilities/hv_util.h +++ head/sys/dev/hyperv/utilities/hv_util.h @@ -0,0 +1,55 @@ +/*- + * Copyright (c) 2009-2012 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_ + +/** + * hv_util related structures + * + */ +typedef struct hv_util_sc { + /* + * function to process Hyper-V messages + */ + void (*callback)(void *); + + struct hv_device* hv_dev; + uint8_t *receive_buffer; +} hv_util_sc; + +void hv_negotiate_version( + struct hv_vmbus_icmsg_hdr* icmsghdrp, + struct hv_vmbus_icmsg_negotiate* negop, + uint8_t* buf); + +int hv_util_attach(device_t dev); +int hv_util_detach(device_t dev); +#endif Index: head/sys/dev/hyperv/utilities/hv_util.c =================================================================== --- head/sys/dev/hyperv/utilities/hv_util.c +++ head/sys/dev/hyperv/utilities/hv_util.c @@ -40,94 +40,9 @@ #include #include -#include "hv_kvp.h" +#include "hv_util.h" -/* Time Sync data */ -typedef struct { - uint64_t data; -} time_sync_data; - -static void hv_shutdown_cb(void *context); -static void hv_heartbeat_cb(void *context); -static void hv_timesync_cb(void *context); - -static int hv_timesync_init(hv_vmbus_service *serv); -static int hv_timesync_uninit(hv_vmbus_service *serv); -static void hv_set_host_time(void *context, int pending); - -/* - * Note: GUID codes below are predefined by the host hypervisor - * (Hyper-V and Azure)interface and required for correct operation. - */ -hv_vmbus_service service_table[] = { - /* Shutdown Service */ - { .guid.data = {0x31, 0x60, 0x0B, 0X0E, 0x13, 0x52, 0x34, 0x49, - 0x81, 0x8B, 0x38, 0XD9, 0x0C, 0xED, 0x39, 0xDB}, - .name = "Hyper-V Shutdown Service\n", - .enabled = TRUE, - .callback = hv_shutdown_cb, - }, - - /* Time Synch Service */ - { .guid.data = {0x30, 0xe6, 0x27, 0x95, 0xae, 0xd0, 0x7b, 0x49, - 0xad, 0xce, 0xe8, 0x0a, 0xb0, 0x17, 0x5c, 0xaf}, - .name = "Hyper-V Time Synch Service\n", - .enabled = TRUE, - .init = hv_timesync_init, - .callback = hv_timesync_cb, - .uninit = hv_timesync_uninit, - }, - - /* Heartbeat Service */ - { .guid.data = {0x39, 0x4f, 0x16, 0x57, 0x15, 0x91, 0x78, 0x4e, - 0xab, 0x55, 0x38, 0x2f, 0x3b, 0xd5, 0x42, 0x2d}, - .name = "Hyper-V Heartbeat Service\n", - .enabled = TRUE, - .callback = hv_heartbeat_cb, - }, - - /* KVP (Key Value Pair) Service */ - { .guid.data = {0xe7, 0xf4, 0xa0, 0xa9, 0x45, 0x5a, 0x96, 0x4d, - 0xb8, 0x27, 0x8a, 0x84, 0x1e, 0x8c, 0x3, 0xe6}, - .name = "Hyper-V KVP Service\n", - .enabled = TRUE, - .init = hv_kvp_init, - .callback = hv_kvp_callback, - }, -}; - -/* - * Receive buffer pointers. There is one buffer per utility service. The - * buffer is allocated during attach(). - */ -uint8_t *receive_buffer[HV_MAX_UTIL_SERVICES]; - -static boolean_t destroyed_kvp = FALSE; - -struct hv_ictimesync_data { - uint64_t parenttime; - uint64_t childtime; - uint64_t roundtriptime; - uint8_t flags; -} __packed; - -static int -hv_timesync_init(hv_vmbus_service *serv) -{ - void *time_msg = malloc(sizeof(time_sync_data), M_DEVBUF, M_WAITOK); - TASK_INIT(&serv->task, 1, hv_set_host_time, time_msg); - return (0); -} - -static int -hv_timesync_uninit(hv_vmbus_service *serv) -{ - taskqueue_drain(taskqueue_thread, &serv->task); - free(serv->task.ta_context, M_DEVBUF); - return (0); -} - -static void +void hv_negotiate_version( struct hv_vmbus_icmsg_hdr* icmsghdrp, struct hv_vmbus_icmsg_negotiate* negop, @@ -156,252 +71,19 @@ negop->icmsg_vercnt = 1; } - -/** - * Set host time based on time sync message from host - */ -static void -hv_set_host_time(void *context, int pending) -{ - time_sync_data* time_msg = (time_sync_data*) context; - uint64_t hosttime = time_msg->data; - struct timespec guest_ts, host_ts; - uint64_t host_tns; - int64_t diff; - int error; - - host_tns = (hosttime - HV_WLTIMEDELTA) * 100; - host_ts.tv_sec = (time_t)(host_tns/HV_NANO_SEC_PER_SEC); - host_ts.tv_nsec = (long)(host_tns%HV_NANO_SEC_PER_SEC); - - nanotime(&guest_ts); - - diff = (int64_t)host_ts.tv_sec - (int64_t)guest_ts.tv_sec; - - /* - * If host differs by 5 seconds then make the guest catch up - */ - if (diff > 5 || diff < -5) { - error = kern_clock_settime(curthread, CLOCK_REALTIME, - &host_ts); - } -} - -/** - * @brief Synchronize time with host after reboot, restore, etc. - * - * ICTIMESYNCFLAG_SYNC flag bit indicates reboot, restore events of the VM. - * After reboot the flag ICTIMESYNCFLAG_SYNC is included in the first time - * message after the timesync channel is opened. Since the hv_utils module is - * loaded after hv_vmbus, the first message is usually missed. The other - * thing is, systime is automatically set to emulated hardware clock which may - * not be UTC time or in the same time zone. So, to override these effects, we - * use the first 50 time samples for initial system time setting. - */ -static inline -void hv_adj_guesttime(uint64_t hosttime, uint8_t flags) -{ - time_sync_data* time_msg = service_table[HV_TIME_SYNCH].task.ta_context; - - time_msg->data = hosttime; - - if (((flags & HV_ICTIMESYNCFLAG_SYNC) != 0) || - ((flags & HV_ICTIMESYNCFLAG_SAMPLE) != 0)) { - taskqueue_enqueue(taskqueue_thread, &service_table[HV_TIME_SYNCH].task); - } -} - -/** - * Time Sync Channel message handler - */ -static void -hv_timesync_cb(void *context) -{ - hv_vmbus_channel* channel = context; - hv_vmbus_icmsg_hdr* icmsghdrp; - uint32_t recvlen; - uint64_t requestId; - int ret; - uint8_t* time_buf; - struct hv_ictimesync_data* timedatap; - - time_buf = receive_buffer[HV_TIME_SYNCH]; - - ret = hv_vmbus_channel_recv_packet(channel, time_buf, - PAGE_SIZE, &recvlen, &requestId); - - if ((ret == 0) && recvlen > 0) { - icmsghdrp = (struct hv_vmbus_icmsg_hdr *) &time_buf[ - sizeof(struct hv_vmbus_pipe_hdr)]; - - if (icmsghdrp->icmsgtype == HV_ICMSGTYPE_NEGOTIATE) { - hv_negotiate_version(icmsghdrp, NULL, time_buf); - } else { - timedatap = (struct hv_ictimesync_data *) &time_buf[ - sizeof(struct hv_vmbus_pipe_hdr) + - sizeof(struct hv_vmbus_icmsg_hdr)]; - hv_adj_guesttime(timedatap->parenttime, timedatap->flags); - } - - icmsghdrp->icflags = HV_ICMSGHDRFLAG_TRANSACTION - | HV_ICMSGHDRFLAG_RESPONSE; - - hv_vmbus_channel_send_packet(channel, time_buf, - recvlen, requestId, - HV_VMBUS_PACKET_TYPE_DATA_IN_BAND, 0); - } -} - -/** - * Shutdown - */ -static void -hv_shutdown_cb(void *context) -{ - uint8_t* buf; - hv_vmbus_channel* channel = context; - uint8_t execute_shutdown = 0; - hv_vmbus_icmsg_hdr* icmsghdrp; - uint32_t recv_len; - uint64_t request_id; - int ret; - hv_vmbus_shutdown_msg_data* shutdown_msg; - - buf = receive_buffer[HV_SHUT_DOWN]; - - ret = hv_vmbus_channel_recv_packet(channel, buf, PAGE_SIZE, - &recv_len, &request_id); - - if ((ret == 0) && recv_len > 0) { - - icmsghdrp = (struct hv_vmbus_icmsg_hdr *) - &buf[sizeof(struct hv_vmbus_pipe_hdr)]; - - if (icmsghdrp->icmsgtype == HV_ICMSGTYPE_NEGOTIATE) { - hv_negotiate_version(icmsghdrp, NULL, buf); - - } else { - shutdown_msg = - (struct hv_vmbus_shutdown_msg_data *) - &buf[sizeof(struct hv_vmbus_pipe_hdr) + - sizeof(struct hv_vmbus_icmsg_hdr)]; - - switch (shutdown_msg->flags) { - case 0: - case 1: - icmsghdrp->status = HV_S_OK; - execute_shutdown = 1; - if(bootverbose) - printf("Shutdown request received -" - " graceful shutdown initiated\n"); - break; - default: - icmsghdrp->status = HV_E_FAIL; - execute_shutdown = 0; - printf("Shutdown request received -" - " Invalid request\n"); - break; - } - } - - icmsghdrp->icflags = HV_ICMSGHDRFLAG_TRANSACTION | - HV_ICMSGHDRFLAG_RESPONSE; - - hv_vmbus_channel_send_packet(channel, buf, - recv_len, request_id, - HV_VMBUS_PACKET_TYPE_DATA_IN_BAND, 0); - } - - if (execute_shutdown) - shutdown_nice(RB_POWEROFF); -} - -/** - * Process heartbeat message - */ -static void -hv_heartbeat_cb(void *context) -{ - uint8_t* buf; - hv_vmbus_channel* channel = context; - uint32_t recvlen; - uint64_t requestid; - int ret; - - struct hv_vmbus_heartbeat_msg_data* heartbeat_msg; - struct hv_vmbus_icmsg_hdr* icmsghdrp; - - buf = receive_buffer[HV_HEART_BEAT]; - - ret = hv_vmbus_channel_recv_packet(channel, buf, PAGE_SIZE, &recvlen, - &requestid); - - if ((ret == 0) && recvlen > 0) { - - icmsghdrp = (struct hv_vmbus_icmsg_hdr *) - &buf[sizeof(struct hv_vmbus_pipe_hdr)]; - - if (icmsghdrp->icmsgtype == HV_ICMSGTYPE_NEGOTIATE) { - hv_negotiate_version(icmsghdrp, NULL, buf); - - } else { - heartbeat_msg = - (struct hv_vmbus_heartbeat_msg_data *) - &buf[sizeof(struct hv_vmbus_pipe_hdr) + - sizeof(struct hv_vmbus_icmsg_hdr)]; - - heartbeat_msg->seq_num += 1; - } - - icmsghdrp->icflags = HV_ICMSGHDRFLAG_TRANSACTION | - HV_ICMSGHDRFLAG_RESPONSE; - - hv_vmbus_channel_send_packet(channel, buf, recvlen, requestid, - HV_VMBUS_PACKET_TYPE_DATA_IN_BAND, 0); - } -} - - -static int -hv_util_probe(device_t dev) -{ - int i; - int rtn_value = ENXIO; - - for (i = 0; i < HV_MAX_UTIL_SERVICES; i++) { - const char *p = vmbus_get_type(dev); - if (service_table[i].enabled && !memcmp(p, &service_table[i].guid, sizeof(hv_guid))) { - device_set_softc(dev, (void *) (&service_table[i])); - rtn_value = BUS_PROBE_DEFAULT; - } - } - - return rtn_value; -} - -static int +int hv_util_attach(device_t dev) { - struct hv_device* hv_dev; - struct hv_vmbus_service* service; - int ret; - size_t receive_buffer_offset; + struct hv_device* hv_dev; + struct hv_util_sc* softc; + int ret; hv_dev = vmbus_get_devctx(dev); - service = device_get_softc(dev); - receive_buffer_offset = service - &service_table[0]; - device_printf(dev, "Hyper-V Service attaching: %s\n", service->name); - receive_buffer[receive_buffer_offset] = + softc = device_get_softc(dev); + softc->hv_dev = hv_dev; + softc->receive_buffer = malloc(4 * PAGE_SIZE, M_DEVBUF, M_WAITOK | M_ZERO); - if (service->init != NULL) { - ret = service->init(service); - if (ret) { - ret = ENODEV; - goto error0; - } - } - /* * These services are not performance critical and do not need * batched reading. Furthermore, some services such as KVP can @@ -412,75 +94,30 @@ hv_set_channel_read_state(hv_dev->channel, FALSE); ret = hv_vmbus_channel_open(hv_dev->channel, 4 * PAGE_SIZE, - 4 * PAGE_SIZE, NULL, 0, - service->callback, hv_dev->channel); + 4 * PAGE_SIZE, NULL, 0, + softc->callback, softc); if (ret) - goto error0; + goto error0; return (0); - error0: - - free(receive_buffer[receive_buffer_offset], M_DEVBUF); - receive_buffer[receive_buffer_offset] = NULL; - +error0: + free(softc->receive_buffer, M_DEVBUF); return (ret); } -static int +int hv_util_detach(device_t dev) { - struct hv_device* hv_dev; - struct hv_vmbus_service* service; - size_t receive_buffer_offset; - - if (!destroyed_kvp) { - hv_kvp_deinit(); - destroyed_kvp = TRUE; - } + struct hv_device* hv_dev; + struct hv_util_sc* softc; hv_dev = vmbus_get_devctx(dev); hv_vmbus_channel_close(hv_dev->channel); - service = device_get_softc(dev); - receive_buffer_offset = service - &service_table[0]; + softc = device_get_softc(dev); - if (service->uninit != NULL) - service->uninit(service); - - free(receive_buffer[receive_buffer_offset], M_DEVBUF); - receive_buffer[receive_buffer_offset] = NULL; + free(softc->receive_buffer, M_DEVBUF); return (0); } - -static int -hv_util_modevent(module_t mod, int event, void *arg) -{ - switch (event) { - case MOD_LOAD: - break; - case MOD_UNLOAD: - break; - default: - break; - } - return (0); -} - -static device_method_t util_methods[] = { - /* Device interface */ - DEVMETHOD(device_probe, hv_util_probe), - DEVMETHOD(device_attach, hv_util_attach), - DEVMETHOD(device_detach, hv_util_detach), - DEVMETHOD(device_shutdown, bus_generic_shutdown), - { 0, 0 } } -; - -static driver_t util_driver = { "hyperv-utils", util_methods, 0 }; - -static devclass_t util_devclass; - -DRIVER_MODULE(hv_utils, vmbus, util_driver, util_devclass, hv_util_modevent, 0); -MODULE_VERSION(hv_utils, 1); -MODULE_DEPEND(hv_utils, vmbus, 1, 1, 1); Index: head/sys/modules/hyperv/utilities/Makefile =================================================================== --- head/sys/modules/hyperv/utilities/Makefile +++ head/sys/modules/hyperv/utilities/Makefile @@ -3,7 +3,7 @@ .PATH: ${.CURDIR}/../../../dev/hyperv/utilities KMOD= hv_utils -SRCS= hv_util.c hv_kvp.c +SRCS= hv_util.c hv_kvp.c hv_timesync.c hv_shutdown.c hv_heartbeat.c SRCS+= bus_if.h device_if.h CFLAGS+= -I${.CURDIR}/../../../dev/hyperv/include \