/** * @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; time_msg = malloc(sizeof(time_sync_data), M_DEVBUF, M_NOWAIT); if (time_msg == NULL) return; time_msg->data = hosttime; if ((flags & HV_ICTIMESYNCFLAG_SYNC) != 0) { hv_queue_work_item(service_table[HV_TIME_SYNCH].work_queue, hv_set_host_time, time_msg); } else if ((flags & HV_ICTIMESYNCFLAG_SAMPLE) != 0) { hv_queue_work_item(service_table[HV_TIME_SYNCH].work_queue, hv_set_host_time, time_msg); } else { free(time_msg, M_DEVBUF); } }
/** * @brief Software interrupt thread routine to handle channel messages from * the hypervisor. */ static void vmbus_msg_swintr(void *arg) { int cpu; void* page_addr; hv_vmbus_message* msg; hv_vmbus_message* copied; cpu = (int)(long)arg; KASSERT(cpu <= mp_maxid, ("VMBUS: vmbus_msg_swintr: " "cpu out of range!")); page_addr = hv_vmbus_g_context.syn_ic_msg_page[cpu]; msg = (hv_vmbus_message*) page_addr + HV_VMBUS_MESSAGE_SINT; for (;;) { if (msg->header.message_type == HV_MESSAGE_TYPE_NONE) { break; /* no message */ } else { copied = malloc(sizeof(hv_vmbus_message), M_DEVBUF, M_NOWAIT); KASSERT(copied != NULL, ("Error VMBUS: malloc failed to allocate" " hv_vmbus_message!")); if (copied == NULL) continue; memcpy(copied, msg, sizeof(hv_vmbus_message)); hv_queue_work_item(hv_vmbus_g_connection.work_queue, hv_vmbus_on_channel_message, copied); } msg->header.message_type = HV_MESSAGE_TYPE_NONE; /* * Make sure the write to message_type (ie set to * HV_MESSAGE_TYPE_NONE) happens before we read the * message_pending and EOMing. Otherwise, the EOMing will * not deliver any more messages * since there is no empty slot */ wmb(); if (msg->header.message_flags.u.message_pending) { /* * This will cause message queue rescan to possibly * deliver another msg from the hypervisor */ wrmsr(HV_X64_MSR_EOM, 0); } } }
/* Timer to establish connection with the user daemon */ static void kvp_user_rcv_timer(void *arg) { int error=KVP_SUCCESS; printf("kvp_user_rcv_timer: timer triggered\n"); //printf("kvp:guid:%s\n",kvp_hv_dev->class_id); /* If daemon is already ready, just connect to the host and return */ if (hv_kvp_ready()) { kvp_msg_state.kvp_rcv_timer.callout = NULL; error = hv_vmbus_channel_open(kvp_hv_dev->channel, 4 * PAGE_SIZE, 4 * PAGE_SIZE, NULL, 0, hv_kvp_callback, kvp_hv_dev->channel); if (error) printf("kvp_user_rcv_timer: vmbus_chan_open failed\n"); } else { hv_queue_work_item(kvp_work_queue, hv_kvp_conn_register, NULL); kvp_msg_state.kvp_rcv_timer = timeout(kvp_user_rcv_timer, NULL, 10); } }
/* * Callback routine that gets called whenever there is a message from host */ void hv_kvp_callback(void *context) { uint64_t pending_cnt = 0; if (kvp_globals.register_done == false) { kvp_globals.channelp = 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__); hv_queue_work_item( service_table[HV_KVP].work_queue, hv_kvp_process_request, context ); } } }
/** * @brief Software interrupt thread routine to handle channel messages from * the hypervisor. */ static void vmbus_msg_swintr(void *arg) { int cpu; void* page_addr; hv_vmbus_channel_msg_header *hdr; hv_vmbus_channel_msg_table_entry *entry; hv_vmbus_channel_msg_type msg_type; hv_vmbus_message* msg; hv_vmbus_message* copied; static bool warned = false; cpu = (int)(long)arg; KASSERT(cpu <= mp_maxid, ("VMBUS: vmbus_msg_swintr: " "cpu out of range!")); page_addr = hv_vmbus_g_context.syn_ic_msg_page[cpu]; msg = (hv_vmbus_message*) page_addr + HV_VMBUS_MESSAGE_SINT; for (;;) { if (msg->header.message_type == HV_MESSAGE_TYPE_NONE) break; /* no message */ hdr = (hv_vmbus_channel_msg_header *)msg->u.payload; msg_type = hdr->message_type; if (msg_type >= HV_CHANNEL_MESSAGE_COUNT && !warned) { warned = true; printf("VMBUS: unknown message type = %d\n", msg_type); goto handled; } entry = &g_channel_message_table[msg_type]; if (entry->handler_no_sleep) entry->messageHandler(hdr); else { copied = malloc(sizeof(hv_vmbus_message), M_DEVBUF, M_NOWAIT); KASSERT(copied != NULL, ("Error VMBUS: malloc failed to allocate" " hv_vmbus_message!")); if (copied == NULL) continue; memcpy(copied, msg, sizeof(hv_vmbus_message)); hv_queue_work_item(hv_vmbus_g_connection.work_queue, hv_vmbus_on_channel_message, copied); } handled: msg->header.message_type = HV_MESSAGE_TYPE_NONE; /* * Make sure the write to message_type (ie set to * HV_MESSAGE_TYPE_NONE) happens before we read the * message_pending and EOMing. Otherwise, the EOMing will * not deliver any more messages * since there is no empty slot */ wmb(); if (msg->header.message_flags.u.message_pending) { /* * This will cause message queue rescan to possibly * deliver another msg from the hypervisor */ wrmsr(HV_X64_MSR_EOM, 0); } } }
/* * Callback routine that gets called whenever there is a message from host */ static void hv_kvp_callback(void *context) { uint8_t* buf; hv_vmbus_channel* channel = context; uint32_t recvlen; uint64_t requestid; int ret; struct hv_vmbus_icmsg_hdr* icmsghdrp; buf = receive_buffer[0]; #if 0 /* If driver unloaded abruptly, return */ if (vmbus_kvp_channel == NULL) { #ifdef DEBUG printf("hv_kvp_cb: kvp unloaded abruptly\n"); #endif return; } #endif /* Check if kvp is ready to process */ if (!hv_kvp_ready()) { #ifdef DEBUG printf("hv_kvp_cb: kvp is not ready..initing\n"); #endif hv_queue_work_item(kvp_work_queue, hv_kvp_conn_register, NULL); } /* Check if already one transaction is under process */ if (hv_kvp_transaction_active()) { #ifdef DEBUG printf("hv_kvp_cb: Already one transaction is active\n"); #endif return; } ret = hv_vmbus_channel_recv_packet(channel, buf, PAGE_SIZE * 2, &recvlen, &requestid); #ifdef DEBUG printf("hv_kvp_cb: vmbus_recv ret:%d rLen:%d rId:%d\n", ret, recvlen, (int)requestid); #endif if ((ret == 0) && (recvlen > 0)) { icmsghdrp = (struct hv_vmbus_icmsg_hdr *) &buf[sizeof(struct hv_vmbus_pipe_hdr)]; if (icmsghdrp->icmsgtype == HV_ICMSGTYPE_NEGOTIATE) { #ifdef DEBUG printf("hv_kvp_cb: Negotiate message received\n"); #endif hv_negotiate_version(icmsghdrp, NULL, buf); } else { #ifdef DEBUG printf("hv_kvp_cb: New host message received\n"); #endif hv_kvp_transaction_init(recvlen,channel,requestid, buf); hv_queue_work_item(kvp_work_queue, hv_kvp_process_msg, NULL); return; /* deferred response by KVP process */ } 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); } }