diff options
Diffstat (limited to 'sys/dev/hyperv/utilities/hv_util.c')
-rw-r--r-- | sys/dev/hyperv/utilities/hv_util.c | 401 |
1 files changed, 19 insertions, 382 deletions
diff --git a/sys/dev/hyperv/utilities/hv_util.c b/sys/dev/hyperv/utilities/hv_util.c index 38054ca..7d19b3f 100644 --- a/sys/dev/hyperv/utilities/hv_util.c +++ b/sys/dev/hyperv/utilities/hv_util.c @@ -40,94 +40,9 @@ #include <sys/syscallsubr.h> #include <dev/hyperv/include/hyperv.h> -#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 @@ hv_negotiate_version( 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_util_attach(device_t dev) 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]; - - if (service->uninit != NULL) - service->uninit(service); + softc = device_get_softc(dev); - 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); |