2 * Copyright (c) 2009-2012 Microsoft Corp.
3 * Copyright (c) 2012 NetApp Inc.
4 * Copyright (c) 2012 Citrix Inc.
7 * Redistribution and use in source and binary forms, with or without
8 * modification, are permitted provided that the following conditions
10 * 1. Redistributions of source code must retain the above copyright
11 * notice unmodified, this list of conditions, and the following
13 * 2. Redistributions in binary form must reproduce the above copyright
14 * notice, this list of conditions and the following disclaimer in the
15 * documentation and/or other materials provided with the distribution.
17 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
18 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
19 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
20 * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
21 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
22 * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
23 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
24 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
25 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
26 * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
29 #include <sys/cdefs.h>
30 __FBSDID("$FreeBSD$");
32 #include <sys/param.h>
33 #include <sys/malloc.h>
34 #include <sys/systm.h>
36 #include <sys/mutex.h>
37 #include <machine/bus.h>
39 #include <vm/vm_param.h>
42 #include "hv_vmbus_priv.h"
47 hv_vmbus_connection hv_vmbus_g_connection =
48 { .connect_state = HV_DISCONNECTED,
49 .next_gpadl_handle = 0xE1E10, };
51 uint32_t hv_vmbus_protocal_version = HV_VMBUS_VERSION_WS2008;
54 hv_vmbus_get_next_version(uint32_t current_ver)
56 switch (current_ver) {
57 case (HV_VMBUS_VERSION_WIN7):
58 return(HV_VMBUS_VERSION_WS2008);
60 case (HV_VMBUS_VERSION_WIN8):
61 return(HV_VMBUS_VERSION_WIN7);
63 case (HV_VMBUS_VERSION_WIN8_1):
64 return(HV_VMBUS_VERSION_WIN8);
66 case (HV_VMBUS_VERSION_WS2008):
68 return(HV_VMBUS_VERSION_INVALID);
73 * Negotiate the highest supported hypervisor version.
76 hv_vmbus_negotiate_version(hv_vmbus_channel_msg_info *msg_info,
80 hv_vmbus_channel_initiate_contact *msg;
82 sema_init(&msg_info->wait_sema, 0, "Msg Info Sema");
83 msg = (hv_vmbus_channel_initiate_contact*) msg_info->msg;
85 msg->header.message_type = HV_CHANNEL_MESSAGE_INITIATED_CONTACT;
86 msg->vmbus_version_requested = version;
88 msg->interrupt_page = hv_get_phys_addr(
89 hv_vmbus_g_connection.interrupt_page);
91 msg->monitor_page_1 = hv_get_phys_addr(
92 hv_vmbus_g_connection.monitor_pages);
96 ((uint8_t *) hv_vmbus_g_connection.monitor_pages
100 * Add to list before we send the request since we may receive the
101 * response before returning from this routine
103 mtx_lock_spin(&hv_vmbus_g_connection.channel_msg_lock);
106 &hv_vmbus_g_connection.channel_msg_anchor,
110 mtx_unlock_spin(&hv_vmbus_g_connection.channel_msg_lock);
112 ret = hv_vmbus_post_message(
114 sizeof(hv_vmbus_channel_initiate_contact));
117 mtx_lock_spin(&hv_vmbus_g_connection.channel_msg_lock);
119 &hv_vmbus_g_connection.channel_msg_anchor,
122 mtx_unlock_spin(&hv_vmbus_g_connection.channel_msg_lock);
127 * Wait for the connection response
129 ret = sema_timedwait(&msg_info->wait_sema, 500); /* KYS 5 seconds */
131 mtx_lock_spin(&hv_vmbus_g_connection.channel_msg_lock);
133 &hv_vmbus_g_connection.channel_msg_anchor,
136 mtx_unlock_spin(&hv_vmbus_g_connection.channel_msg_lock);
139 * Check if successful
141 if (msg_info->response.version_response.version_supported) {
142 hv_vmbus_g_connection.connect_state = HV_CONNECTED;
151 * Send a connect request on the partition service connection
154 hv_vmbus_connect(void) {
157 hv_vmbus_channel_msg_info* msg_info = NULL;
160 * Make sure we are not connecting or connected
162 if (hv_vmbus_g_connection.connect_state != HV_DISCONNECTED) {
167 * Initialize the vmbus connection
169 hv_vmbus_g_connection.connect_state = HV_CONNECTING;
170 hv_vmbus_g_connection.work_queue = hv_work_queue_create("vmbusQ");
171 sema_init(&hv_vmbus_g_connection.control_sema, 1, "control_sema");
173 TAILQ_INIT(&hv_vmbus_g_connection.channel_msg_anchor);
174 mtx_init(&hv_vmbus_g_connection.channel_msg_lock, "vmbus channel msg",
177 TAILQ_INIT(&hv_vmbus_g_connection.channel_anchor);
178 mtx_init(&hv_vmbus_g_connection.channel_lock, "vmbus channel",
182 * Setup the vmbus event connection for channel interrupt abstraction
185 hv_vmbus_g_connection.interrupt_page = contigmalloc(
187 M_NOWAIT | M_ZERO, 0UL,
190 KASSERT(hv_vmbus_g_connection.interrupt_page != NULL,
191 ("Error VMBUS: malloc failed to allocate Channel"
192 " Request Event message!"));
193 if (hv_vmbus_g_connection.interrupt_page == NULL) {
198 hv_vmbus_g_connection.recv_interrupt_page =
199 hv_vmbus_g_connection.interrupt_page;
201 hv_vmbus_g_connection.send_interrupt_page =
202 ((uint8_t *) hv_vmbus_g_connection.interrupt_page +
206 * Set up the monitor notification facility. The 1st page for
207 * parent->child and the 2nd page for child->parent
209 hv_vmbus_g_connection.monitor_pages = contigmalloc(
217 KASSERT(hv_vmbus_g_connection.monitor_pages != NULL,
218 ("Error VMBUS: malloc failed to allocate Monitor Pages!"));
219 if (hv_vmbus_g_connection.monitor_pages == NULL) {
224 msg_info = (hv_vmbus_channel_msg_info*)
225 malloc(sizeof(hv_vmbus_channel_msg_info) +
226 sizeof(hv_vmbus_channel_initiate_contact),
227 M_DEVBUF, M_NOWAIT | M_ZERO);
228 KASSERT(msg_info != NULL,
229 ("Error VMBUS: malloc failed for Initiate Contact message!"));
230 if (msg_info == NULL) {
236 * Find the highest vmbus version number we can support.
238 version = HV_VMBUS_VERSION_CURRENT;
241 ret = hv_vmbus_negotiate_version(msg_info, version);
242 if (ret == EWOULDBLOCK) {
249 if (hv_vmbus_g_connection.connect_state == HV_CONNECTED)
252 version = hv_vmbus_get_next_version(version);
253 } while (version != HV_VMBUS_VERSION_INVALID);
255 hv_vmbus_protocal_version = version;
257 printf("VMBUS: Portocal Version: %d.%d\n",
258 version >> 16, version & 0xFFFF);
260 sema_destroy(&msg_info->wait_sema);
261 free(msg_info, M_DEVBUF);
266 * Cleanup after failure!
270 hv_vmbus_g_connection.connect_state = HV_DISCONNECTED;
272 hv_work_queue_close(hv_vmbus_g_connection.work_queue);
273 sema_destroy(&hv_vmbus_g_connection.control_sema);
274 mtx_destroy(&hv_vmbus_g_connection.channel_lock);
275 mtx_destroy(&hv_vmbus_g_connection.channel_msg_lock);
277 if (hv_vmbus_g_connection.interrupt_page != NULL) {
279 hv_vmbus_g_connection.interrupt_page,
282 hv_vmbus_g_connection.interrupt_page = NULL;
285 if (hv_vmbus_g_connection.monitor_pages != NULL) {
287 hv_vmbus_g_connection.monitor_pages,
290 hv_vmbus_g_connection.monitor_pages = NULL;
294 sema_destroy(&msg_info->wait_sema);
295 free(msg_info, M_DEVBUF);
302 * Send a disconnect request on the partition service connection
305 hv_vmbus_disconnect(void) {
307 hv_vmbus_channel_unload* msg;
309 msg = malloc(sizeof(hv_vmbus_channel_unload),
310 M_DEVBUF, M_NOWAIT | M_ZERO);
312 ("Error VMBUS: malloc failed to allocate Channel Unload Msg!"));
316 msg->message_type = HV_CHANNEL_MESSAGE_UNLOAD;
318 ret = hv_vmbus_post_message(msg, sizeof(hv_vmbus_channel_unload));
321 contigfree(hv_vmbus_g_connection.interrupt_page, PAGE_SIZE, M_DEVBUF);
323 mtx_destroy(&hv_vmbus_g_connection.channel_msg_lock);
325 hv_work_queue_close(hv_vmbus_g_connection.work_queue);
326 sema_destroy(&hv_vmbus_g_connection.control_sema);
328 hv_vmbus_g_connection.connect_state = HV_DISCONNECTED;
336 * Get the channel object given its child relative id (ie channel id)
339 hv_vmbus_get_channel_from_rel_id(uint32_t rel_id) {
341 hv_vmbus_channel* channel;
342 hv_vmbus_channel* foundChannel = NULL;
346 * Consider optimization where relids are stored in a fixed size array
347 * and channels are accessed without the need to take this lock or search
350 mtx_lock(&hv_vmbus_g_connection.channel_lock);
351 TAILQ_FOREACH(channel,
352 &hv_vmbus_g_connection.channel_anchor, list_entry) {
354 if (channel->offer_msg.child_rel_id == rel_id) {
355 foundChannel = channel;
359 mtx_unlock(&hv_vmbus_g_connection.channel_lock);
361 return (foundChannel);
365 * Process a channel event notification
368 VmbusProcessChannelEvent(uint32_t relid)
371 uint32_t bytes_to_read;
372 hv_vmbus_channel* channel;
373 boolean_t is_batched_reading;
376 * Find the channel based on this relid and invokes
377 * the channel callback to process the event
380 channel = hv_vmbus_get_channel_from_rel_id(relid);
382 if (channel == NULL) {
386 * To deal with the race condition where we might
387 * receive a packet while the relevant driver is
388 * being unloaded, dispatch the callback while
389 * holding the channel lock. The unloading driver
390 * will acquire the same channel lock to set the
391 * callback to NULL. This closes the window.
395 * Disable the lock due to newly added WITNESS check in r277723.
396 * Will seek other way to avoid race condition.
399 // mtx_lock(&channel->inbound_lock);
400 if (channel->on_channel_callback != NULL) {
401 arg = channel->channel_callback_context;
402 is_batched_reading = channel->batched_reading;
404 * Optimize host to guest signaling by ensuring:
405 * 1. While reading the channel, we disable interrupts from
407 * 2. Ensure that we process all posted messages from the host
408 * before returning from this callback.
409 * 3. Once we return, enable signaling from the host. Once this
410 * state is set we check to see if additional packets are
411 * available to read. In this case we repeat the process.
414 if (is_batched_reading)
415 hv_ring_buffer_read_begin(&channel->inbound);
417 channel->on_channel_callback(arg);
419 if (is_batched_reading)
421 hv_ring_buffer_read_end(&channel->inbound);
424 } while (is_batched_reading && (bytes_to_read != 0));
426 // mtx_unlock(&channel->inbound_lock);
430 extern uint32_t hv_intr_count;
431 extern uint32_t hv_vmbus_swintr_event_cpu[MAXCPU];
432 extern uint32_t hv_vmbus_intr_cpu[MAXCPU];
439 hv_vmbus_on_events(void *arg)
445 uint32_t* recv_interrupt_page = NULL;
448 hv_vmbus_synic_event_flags *event;
449 /* int maxdword = PAGE_SIZE >> 3; */
451 cpu = (int)(long)arg;
452 KASSERT(cpu <= mp_maxid, ("VMBUS: hv_vmbus_on_events: "
453 "cpu out of range!"));
457 hv_vmbus_swintr_event_cpu[cpu]++;
458 if (hv_intr_count % 10000 == 0) {
459 printf("VMBUS: Total interrupt %d\n", hv_intr_count);
460 for (i = 0; i < mp_ncpus; i++)
461 printf("VMBUS: hw cpu[%d]: %d, event sw intr cpu[%d]: %d\n",
462 i, hv_vmbus_intr_cpu[i], i, hv_vmbus_swintr_event_cpu[i]);
466 if ((hv_vmbus_protocal_version == HV_VMBUS_VERSION_WS2008) ||
467 (hv_vmbus_protocal_version == HV_VMBUS_VERSION_WIN7)) {
468 maxdword = HV_MAX_NUM_CHANNELS_SUPPORTED >> 5;
470 * receive size is 1/2 page and divide that by 4 bytes
472 recv_interrupt_page =
473 hv_vmbus_g_connection.recv_interrupt_page;
476 * On Host with Win8 or above, the event page can be
477 * checked directly to get the id of the channel
478 * that has the pending interrupt.
480 maxdword = HV_EVENT_FLAGS_DWORD_COUNT;
481 page_addr = hv_vmbus_g_context.syn_ic_event_page[cpu];
482 event = (hv_vmbus_synic_event_flags *)
483 page_addr + HV_VMBUS_MESSAGE_SINT;
484 recv_interrupt_page = event->flags32;
490 if (recv_interrupt_page != NULL) {
491 for (dword = 0; dword < maxdword; dword++) {
492 if (recv_interrupt_page[dword]) {
493 for (bit = 0; bit < 32; bit++) {
494 if (synch_test_and_clear_bit(bit,
495 (uint32_t *) &recv_interrupt_page[dword])) {
496 rel_id = (dword << 5) + bit;
500 * vmbus channel protocol msg.
504 VmbusProcessChannelEvent(rel_id);
517 * Send a msg on the vmbus's message connection
519 int hv_vmbus_post_message(void *buffer, size_t bufferLen) {
521 hv_vmbus_connection_id connId;
522 unsigned retries = 0;
524 /* NetScaler delays from previous code were consolidated here */
525 static int delayAmount[] = {100, 100, 100, 500, 500, 5000, 5000, 5000};
527 /* for(each entry in delayAmount) try to post message,
528 * delay a little bit before retrying
531 retries < sizeof(delayAmount)/sizeof(delayAmount[0]); retries++) {
532 connId.as_uint32_t = 0;
533 connId.u.id = HV_VMBUS_MESSAGE_CONNECTION_ID;
534 ret = hv_vmbus_post_msg_via_msg_ipc(connId, 1, buffer, bufferLen);
535 if (ret != HV_STATUS_INSUFFICIENT_BUFFERS)
537 /* TODO: KYS We should use a blocking wait call */
538 DELAY(delayAmount[retries]);
541 KASSERT(ret == 0, ("Error VMBUS: Message Post Failed\n"));
547 * Send an event notification to the parent
550 hv_vmbus_set_event(hv_vmbus_channel *channel) {
552 uint32_t child_rel_id = channel->offer_msg.child_rel_id;
554 /* Each uint32_t represents 32 channels */
556 synch_set_bit(child_rel_id & 31,
557 (((uint32_t *)hv_vmbus_g_connection.send_interrupt_page
558 + (child_rel_id >> 5))));
559 ret = hv_vmbus_signal_event(channel->signal_event_param);