hv_connection.c revision 295309
1250199Sgrehan/*- 2250199Sgrehan * Copyright (c) 2009-2012 Microsoft Corp. 3250199Sgrehan * Copyright (c) 2012 NetApp Inc. 4250199Sgrehan * Copyright (c) 2012 Citrix Inc. 5250199Sgrehan * All rights reserved. 6250199Sgrehan * 7250199Sgrehan * Redistribution and use in source and binary forms, with or without 8250199Sgrehan * modification, are permitted provided that the following conditions 9250199Sgrehan * are met: 10250199Sgrehan * 1. Redistributions of source code must retain the above copyright 11250199Sgrehan * notice unmodified, this list of conditions, and the following 12250199Sgrehan * disclaimer. 13250199Sgrehan * 2. Redistributions in binary form must reproduce the above copyright 14250199Sgrehan * notice, this list of conditions and the following disclaimer in the 15250199Sgrehan * documentation and/or other materials provided with the distribution. 16250199Sgrehan * 17250199Sgrehan * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR 18250199Sgrehan * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES 19250199Sgrehan * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. 20250199Sgrehan * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, 21250199Sgrehan * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT 22250199Sgrehan * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 23250199Sgrehan * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 24250199Sgrehan * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 25250199Sgrehan * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF 26250199Sgrehan * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 27250199Sgrehan */ 28250199Sgrehan 29250199Sgrehan#include <sys/param.h> 30250199Sgrehan#include <sys/malloc.h> 31250199Sgrehan#include <sys/systm.h> 32250199Sgrehan#include <sys/lock.h> 33250199Sgrehan#include <sys/mutex.h> 34250199Sgrehan#include <machine/bus.h> 35250199Sgrehan#include <vm/vm.h> 36250199Sgrehan#include <vm/vm_param.h> 37250199Sgrehan#include <vm/pmap.h> 38250199Sgrehan 39250199Sgrehan#include "hv_vmbus_priv.h" 40250199Sgrehan 41250199Sgrehan/* 42250199Sgrehan * Globals 43250199Sgrehan */ 44250199Sgrehanhv_vmbus_connection hv_vmbus_g_connection = 45250199Sgrehan { .connect_state = HV_DISCONNECTED, 46250199Sgrehan .next_gpadl_handle = 0xE1E10, }; 47250199Sgrehan 48282212Swhuuint32_t hv_vmbus_protocal_version = HV_VMBUS_VERSION_WS2008; 49282212Swhu 50282212Swhustatic uint32_t 51282212Swhuhv_vmbus_get_next_version(uint32_t current_ver) 52282212Swhu{ 53282212Swhu switch (current_ver) { 54282212Swhu case (HV_VMBUS_VERSION_WIN7): 55282212Swhu return(HV_VMBUS_VERSION_WS2008); 56282212Swhu 57282212Swhu case (HV_VMBUS_VERSION_WIN8): 58282212Swhu return(HV_VMBUS_VERSION_WIN7); 59282212Swhu 60282212Swhu case (HV_VMBUS_VERSION_WIN8_1): 61282212Swhu return(HV_VMBUS_VERSION_WIN8); 62282212Swhu 63282212Swhu case (HV_VMBUS_VERSION_WS2008): 64282212Swhu default: 65282212Swhu return(HV_VMBUS_VERSION_INVALID); 66282212Swhu } 67282212Swhu} 68282212Swhu 69250199Sgrehan/** 70282212Swhu * Negotiate the highest supported hypervisor version. 71282212Swhu */ 72282212Swhustatic int 73282212Swhuhv_vmbus_negotiate_version(hv_vmbus_channel_msg_info *msg_info, 74282212Swhu uint32_t version) 75282212Swhu{ 76282212Swhu int ret = 0; 77282212Swhu hv_vmbus_channel_initiate_contact *msg; 78282212Swhu 79282212Swhu sema_init(&msg_info->wait_sema, 0, "Msg Info Sema"); 80282212Swhu msg = (hv_vmbus_channel_initiate_contact*) msg_info->msg; 81282212Swhu 82282212Swhu msg->header.message_type = HV_CHANNEL_MESSAGE_INITIATED_CONTACT; 83282212Swhu msg->vmbus_version_requested = version; 84282212Swhu 85282212Swhu msg->interrupt_page = hv_get_phys_addr( 86282212Swhu hv_vmbus_g_connection.interrupt_page); 87282212Swhu 88282212Swhu msg->monitor_page_1 = hv_get_phys_addr( 89295309Ssephe hv_vmbus_g_connection.monitor_page_1); 90282212Swhu 91295308Ssephe msg->monitor_page_2 = hv_get_phys_addr( 92295309Ssephe hv_vmbus_g_connection.monitor_page_2); 93282212Swhu 94282212Swhu /** 95282212Swhu * Add to list before we send the request since we may receive the 96282212Swhu * response before returning from this routine 97282212Swhu */ 98282212Swhu mtx_lock_spin(&hv_vmbus_g_connection.channel_msg_lock); 99282212Swhu 100282212Swhu TAILQ_INSERT_TAIL( 101282212Swhu &hv_vmbus_g_connection.channel_msg_anchor, 102282212Swhu msg_info, 103282212Swhu msg_list_entry); 104282212Swhu 105282212Swhu mtx_unlock_spin(&hv_vmbus_g_connection.channel_msg_lock); 106282212Swhu 107282212Swhu ret = hv_vmbus_post_message( 108282212Swhu msg, 109282212Swhu sizeof(hv_vmbus_channel_initiate_contact)); 110282212Swhu 111282212Swhu if (ret != 0) { 112282212Swhu mtx_lock_spin(&hv_vmbus_g_connection.channel_msg_lock); 113282212Swhu TAILQ_REMOVE( 114282212Swhu &hv_vmbus_g_connection.channel_msg_anchor, 115282212Swhu msg_info, 116282212Swhu msg_list_entry); 117282212Swhu mtx_unlock_spin(&hv_vmbus_g_connection.channel_msg_lock); 118282212Swhu return (ret); 119282212Swhu } 120282212Swhu 121282212Swhu /** 122282212Swhu * Wait for the connection response 123282212Swhu */ 124282212Swhu ret = sema_timedwait(&msg_info->wait_sema, 500); /* KYS 5 seconds */ 125282212Swhu 126282212Swhu mtx_lock_spin(&hv_vmbus_g_connection.channel_msg_lock); 127282212Swhu TAILQ_REMOVE( 128282212Swhu &hv_vmbus_g_connection.channel_msg_anchor, 129282212Swhu msg_info, 130282212Swhu msg_list_entry); 131282212Swhu mtx_unlock_spin(&hv_vmbus_g_connection.channel_msg_lock); 132282212Swhu 133282212Swhu /** 134282212Swhu * Check if successful 135282212Swhu */ 136282212Swhu if (msg_info->response.version_response.version_supported) { 137282212Swhu hv_vmbus_g_connection.connect_state = HV_CONNECTED; 138282212Swhu } else { 139282212Swhu ret = ECONNREFUSED; 140282212Swhu } 141282212Swhu 142282212Swhu return (ret); 143282212Swhu} 144282212Swhu 145282212Swhu/** 146250199Sgrehan * Send a connect request on the partition service connection 147250199Sgrehan */ 148250199Sgrehanint 149250199Sgrehanhv_vmbus_connect(void) { 150250199Sgrehan int ret = 0; 151282212Swhu uint32_t version; 152250199Sgrehan hv_vmbus_channel_msg_info* msg_info = NULL; 153250199Sgrehan 154250199Sgrehan /** 155250199Sgrehan * Make sure we are not connecting or connected 156250199Sgrehan */ 157250199Sgrehan if (hv_vmbus_g_connection.connect_state != HV_DISCONNECTED) { 158250199Sgrehan return (-1); 159250199Sgrehan } 160250199Sgrehan 161250199Sgrehan /** 162250199Sgrehan * Initialize the vmbus connection 163250199Sgrehan */ 164250199Sgrehan hv_vmbus_g_connection.connect_state = HV_CONNECTING; 165250199Sgrehan 166250199Sgrehan TAILQ_INIT(&hv_vmbus_g_connection.channel_msg_anchor); 167250199Sgrehan mtx_init(&hv_vmbus_g_connection.channel_msg_lock, "vmbus channel msg", 168250199Sgrehan NULL, MTX_SPIN); 169250199Sgrehan 170250199Sgrehan TAILQ_INIT(&hv_vmbus_g_connection.channel_anchor); 171250199Sgrehan mtx_init(&hv_vmbus_g_connection.channel_lock, "vmbus channel", 172282212Swhu NULL, MTX_DEF); 173250199Sgrehan 174250199Sgrehan /** 175250199Sgrehan * Setup the vmbus event connection for channel interrupt abstraction 176250199Sgrehan * stuff 177250199Sgrehan */ 178295309Ssephe hv_vmbus_g_connection.interrupt_page = malloc( 179250199Sgrehan PAGE_SIZE, M_DEVBUF, 180295309Ssephe M_WAITOK | M_ZERO); 181250199Sgrehan 182250199Sgrehan hv_vmbus_g_connection.recv_interrupt_page = 183250199Sgrehan hv_vmbus_g_connection.interrupt_page; 184250199Sgrehan 185250199Sgrehan hv_vmbus_g_connection.send_interrupt_page = 186250199Sgrehan ((uint8_t *) hv_vmbus_g_connection.interrupt_page + 187250199Sgrehan (PAGE_SIZE >> 1)); 188250199Sgrehan 189250199Sgrehan /** 190250199Sgrehan * Set up the monitor notification facility. The 1st page for 191250199Sgrehan * parent->child and the 2nd page for child->parent 192250199Sgrehan */ 193295309Ssephe hv_vmbus_g_connection.monitor_page_1 = malloc( 194295309Ssephe PAGE_SIZE, 195250199Sgrehan M_DEVBUF, 196295309Ssephe M_WAITOK | M_ZERO); 197295309Ssephe hv_vmbus_g_connection.monitor_page_2 = malloc( 198250199Sgrehan PAGE_SIZE, 199295309Ssephe M_DEVBUF, 200295309Ssephe M_WAITOK | M_ZERO); 201250199Sgrehan 202250199Sgrehan msg_info = (hv_vmbus_channel_msg_info*) 203250199Sgrehan malloc(sizeof(hv_vmbus_channel_msg_info) + 204250199Sgrehan sizeof(hv_vmbus_channel_initiate_contact), 205295308Ssephe M_DEVBUF, M_WAITOK | M_ZERO); 206250199Sgrehan 207294553Ssephe hv_vmbus_g_connection.channels = malloc(sizeof(hv_vmbus_channel*) * 208294553Ssephe HV_CHANNEL_MAX_COUNT, 209294553Ssephe M_DEVBUF, M_WAITOK | M_ZERO); 210282212Swhu /* 211282212Swhu * Find the highest vmbus version number we can support. 212250199Sgrehan */ 213282212Swhu version = HV_VMBUS_VERSION_CURRENT; 214250199Sgrehan 215282212Swhu do { 216282212Swhu ret = hv_vmbus_negotiate_version(msg_info, version); 217282212Swhu if (ret == EWOULDBLOCK) { 218282212Swhu /* 219282212Swhu * We timed out. 220282212Swhu */ 221282212Swhu goto cleanup; 222282212Swhu } 223250199Sgrehan 224282212Swhu if (hv_vmbus_g_connection.connect_state == HV_CONNECTED) 225282212Swhu break; 226250199Sgrehan 227282212Swhu version = hv_vmbus_get_next_version(version); 228282212Swhu } while (version != HV_VMBUS_VERSION_INVALID); 229250199Sgrehan 230282212Swhu hv_vmbus_protocal_version = version; 231282212Swhu if (bootverbose) 232293870Ssephe printf("VMBUS: Protocol Version: %d.%d\n", 233282212Swhu version >> 16, version & 0xFFFF); 234250199Sgrehan 235250199Sgrehan sema_destroy(&msg_info->wait_sema); 236250199Sgrehan free(msg_info, M_DEVBUF); 237250199Sgrehan 238250199Sgrehan return (0); 239250199Sgrehan 240250199Sgrehan /* 241250199Sgrehan * Cleanup after failure! 242250199Sgrehan */ 243250199Sgrehan cleanup: 244250199Sgrehan 245250199Sgrehan hv_vmbus_g_connection.connect_state = HV_DISCONNECTED; 246250199Sgrehan 247250199Sgrehan mtx_destroy(&hv_vmbus_g_connection.channel_lock); 248250199Sgrehan mtx_destroy(&hv_vmbus_g_connection.channel_msg_lock); 249250199Sgrehan 250250199Sgrehan if (hv_vmbus_g_connection.interrupt_page != NULL) { 251250199Sgrehan contigfree( 252250199Sgrehan hv_vmbus_g_connection.interrupt_page, 253250199Sgrehan PAGE_SIZE, 254250199Sgrehan M_DEVBUF); 255250199Sgrehan hv_vmbus_g_connection.interrupt_page = NULL; 256250199Sgrehan } 257250199Sgrehan 258295309Ssephe free(hv_vmbus_g_connection.monitor_page_1, M_DEVBUF); 259295309Ssephe free(hv_vmbus_g_connection.monitor_page_2, M_DEVBUF); 260250199Sgrehan 261250199Sgrehan if (msg_info) { 262250199Sgrehan sema_destroy(&msg_info->wait_sema); 263250199Sgrehan free(msg_info, M_DEVBUF); 264250199Sgrehan } 265250199Sgrehan 266294553Ssephe free(hv_vmbus_g_connection.channels, M_DEVBUF); 267250199Sgrehan return (ret); 268250199Sgrehan} 269250199Sgrehan 270250199Sgrehan/** 271250199Sgrehan * Send a disconnect request on the partition service connection 272250199Sgrehan */ 273250199Sgrehanint 274250199Sgrehanhv_vmbus_disconnect(void) { 275250199Sgrehan int ret = 0; 276295308Ssephe hv_vmbus_channel_unload msg; 277250199Sgrehan 278295308Ssephe msg.message_type = HV_CHANNEL_MESSAGE_UNLOAD; 279250199Sgrehan 280295308Ssephe ret = hv_vmbus_post_message(&msg, sizeof(hv_vmbus_channel_unload)); 281250199Sgrehan 282250199Sgrehan contigfree(hv_vmbus_g_connection.interrupt_page, PAGE_SIZE, M_DEVBUF); 283250199Sgrehan 284250199Sgrehan mtx_destroy(&hv_vmbus_g_connection.channel_msg_lock); 285250199Sgrehan 286294553Ssephe free(hv_vmbus_g_connection.channels, M_DEVBUF); 287250199Sgrehan hv_vmbus_g_connection.connect_state = HV_DISCONNECTED; 288250199Sgrehan 289250199Sgrehan return (ret); 290250199Sgrehan} 291250199Sgrehan 292250199Sgrehan/** 293250199Sgrehan * Handler for events 294250199Sgrehan */ 295250199Sgrehanvoid 296294886Ssephehv_vmbus_on_events(int cpu) 297250199Sgrehan{ 298282212Swhu int bit; 299250199Sgrehan int dword; 300282212Swhu void *page_addr; 301282212Swhu uint32_t* recv_interrupt_page = NULL; 302250199Sgrehan int rel_id; 303282212Swhu int maxdword; 304282212Swhu hv_vmbus_synic_event_flags *event; 305250199Sgrehan /* int maxdword = PAGE_SIZE >> 3; */ 306250199Sgrehan 307282212Swhu KASSERT(cpu <= mp_maxid, ("VMBUS: hv_vmbus_on_events: " 308282212Swhu "cpu out of range!")); 309250199Sgrehan 310282212Swhu if ((hv_vmbus_protocal_version == HV_VMBUS_VERSION_WS2008) || 311282212Swhu (hv_vmbus_protocal_version == HV_VMBUS_VERSION_WIN7)) { 312282212Swhu maxdword = HV_MAX_NUM_CHANNELS_SUPPORTED >> 5; 313282212Swhu /* 314282212Swhu * receive size is 1/2 page and divide that by 4 bytes 315282212Swhu */ 316282212Swhu recv_interrupt_page = 317282212Swhu hv_vmbus_g_connection.recv_interrupt_page; 318282212Swhu } else { 319282212Swhu /* 320282212Swhu * On Host with Win8 or above, the event page can be 321282212Swhu * checked directly to get the id of the channel 322282212Swhu * that has the pending interrupt. 323282212Swhu */ 324282212Swhu maxdword = HV_EVENT_FLAGS_DWORD_COUNT; 325282212Swhu page_addr = hv_vmbus_g_context.syn_ic_event_page[cpu]; 326282212Swhu event = (hv_vmbus_synic_event_flags *) 327282212Swhu page_addr + HV_VMBUS_MESSAGE_SINT; 328282212Swhu recv_interrupt_page = event->flags32; 329282212Swhu } 330282212Swhu 331250199Sgrehan /* 332250199Sgrehan * Check events 333250199Sgrehan */ 334250199Sgrehan if (recv_interrupt_page != NULL) { 335250199Sgrehan for (dword = 0; dword < maxdword; dword++) { 336250199Sgrehan if (recv_interrupt_page[dword]) { 337294553Ssephe for (bit = 0; bit < HV_CHANNEL_DWORD_LEN; bit++) { 338250199Sgrehan if (synch_test_and_clear_bit(bit, 339250199Sgrehan (uint32_t *) &recv_interrupt_page[dword])) { 340250199Sgrehan rel_id = (dword << 5) + bit; 341250199Sgrehan if (rel_id == 0) { 342250199Sgrehan /* 343250199Sgrehan * Special case - 344250199Sgrehan * vmbus channel protocol msg. 345250199Sgrehan */ 346250199Sgrehan continue; 347250199Sgrehan } else { 348294886Ssephe hv_vmbus_channel * channel = hv_vmbus_g_connection.channels[rel_id]; 349294886Ssephe /* if channel is closed or closing */ 350294886Ssephe if (channel == NULL || channel->rxq == NULL) 351294886Ssephe continue; 352250199Sgrehan 353294886Ssephe if (channel->batched_reading) 354294886Ssephe hv_ring_buffer_read_begin(&channel->inbound); 355294886Ssephe taskqueue_enqueue_fast(channel->rxq, &channel->channel_task); 356250199Sgrehan } 357250199Sgrehan } 358250199Sgrehan } 359250199Sgrehan } 360250199Sgrehan } 361250199Sgrehan } 362250199Sgrehan 363250199Sgrehan return; 364250199Sgrehan} 365250199Sgrehan 366250199Sgrehan/** 367250199Sgrehan * Send a msg on the vmbus's message connection 368250199Sgrehan */ 369250199Sgrehanint hv_vmbus_post_message(void *buffer, size_t bufferLen) { 370250199Sgrehan int ret = 0; 371250199Sgrehan hv_vmbus_connection_id connId; 372250199Sgrehan unsigned retries = 0; 373250199Sgrehan 374250199Sgrehan /* NetScaler delays from previous code were consolidated here */ 375250199Sgrehan static int delayAmount[] = {100, 100, 100, 500, 500, 5000, 5000, 5000}; 376250199Sgrehan 377250199Sgrehan /* for(each entry in delayAmount) try to post message, 378250199Sgrehan * delay a little bit before retrying 379250199Sgrehan */ 380250199Sgrehan for (retries = 0; 381250199Sgrehan retries < sizeof(delayAmount)/sizeof(delayAmount[0]); retries++) { 382250199Sgrehan connId.as_uint32_t = 0; 383250199Sgrehan connId.u.id = HV_VMBUS_MESSAGE_CONNECTION_ID; 384250199Sgrehan ret = hv_vmbus_post_msg_via_msg_ipc(connId, 1, buffer, bufferLen); 385250199Sgrehan if (ret != HV_STATUS_INSUFFICIENT_BUFFERS) 386250199Sgrehan break; 387250199Sgrehan /* TODO: KYS We should use a blocking wait call */ 388250199Sgrehan DELAY(delayAmount[retries]); 389250199Sgrehan } 390250199Sgrehan 391250199Sgrehan KASSERT(ret == 0, ("Error VMBUS: Message Post Failed\n")); 392250199Sgrehan 393250199Sgrehan return (ret); 394250199Sgrehan} 395250199Sgrehan 396250199Sgrehan/** 397250199Sgrehan * Send an event notification to the parent 398250199Sgrehan */ 399250199Sgrehanint 400282212Swhuhv_vmbus_set_event(hv_vmbus_channel *channel) { 401250199Sgrehan int ret = 0; 402282212Swhu uint32_t child_rel_id = channel->offer_msg.child_rel_id; 403250199Sgrehan 404250199Sgrehan /* Each uint32_t represents 32 channels */ 405250199Sgrehan 406250199Sgrehan synch_set_bit(child_rel_id & 31, 407250199Sgrehan (((uint32_t *)hv_vmbus_g_connection.send_interrupt_page 408250199Sgrehan + (child_rel_id >> 5)))); 409282212Swhu ret = hv_vmbus_signal_event(channel->signal_event_param); 410250199Sgrehan 411250199Sgrehan return (ret); 412250199Sgrehan} 413