hv_connection.c revision 297219
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/kernel.h> 31250199Sgrehan#include <sys/malloc.h> 32250199Sgrehan#include <sys/systm.h> 33250199Sgrehan#include <sys/lock.h> 34250199Sgrehan#include <sys/mutex.h> 35250199Sgrehan#include <machine/bus.h> 36250199Sgrehan#include <vm/vm.h> 37250199Sgrehan#include <vm/vm_param.h> 38250199Sgrehan#include <vm/pmap.h> 39250199Sgrehan 40250199Sgrehan#include "hv_vmbus_priv.h" 41250199Sgrehan 42250199Sgrehan/* 43250199Sgrehan * Globals 44250199Sgrehan */ 45250199Sgrehanhv_vmbus_connection hv_vmbus_g_connection = 46250199Sgrehan { .connect_state = HV_DISCONNECTED, 47250199Sgrehan .next_gpadl_handle = 0xE1E10, }; 48282212Swhu 49282212Swhuuint32_t hv_vmbus_protocal_version = HV_VMBUS_VERSION_WS2008; 50282212Swhu 51282212Swhustatic uint32_t 52282212Swhuhv_vmbus_get_next_version(uint32_t current_ver) 53282212Swhu{ 54282212Swhu switch (current_ver) { 55282212Swhu case (HV_VMBUS_VERSION_WIN7): 56282212Swhu return(HV_VMBUS_VERSION_WS2008); 57282212Swhu 58282212Swhu case (HV_VMBUS_VERSION_WIN8): 59282212Swhu return(HV_VMBUS_VERSION_WIN7); 60282212Swhu 61282212Swhu case (HV_VMBUS_VERSION_WIN8_1): 62282212Swhu return(HV_VMBUS_VERSION_WIN8); 63282212Swhu 64282212Swhu case (HV_VMBUS_VERSION_WS2008): 65282212Swhu default: 66282212Swhu return(HV_VMBUS_VERSION_INVALID); 67282212Swhu } 68282212Swhu} 69250199Sgrehan 70282212Swhu/** 71282212Swhu * Negotiate the highest supported hypervisor version. 72282212Swhu */ 73282212Swhustatic int 74282212Swhuhv_vmbus_negotiate_version(hv_vmbus_channel_msg_info *msg_info, 75282212Swhu uint32_t version) 76282212Swhu{ 77282212Swhu int ret = 0; 78282212Swhu hv_vmbus_channel_initiate_contact *msg; 79282212Swhu 80282212Swhu sema_init(&msg_info->wait_sema, 0, "Msg Info Sema"); 81282212Swhu msg = (hv_vmbus_channel_initiate_contact*) msg_info->msg; 82282212Swhu 83282212Swhu msg->header.message_type = HV_CHANNEL_MESSAGE_INITIATED_CONTACT; 84282212Swhu msg->vmbus_version_requested = version; 85282212Swhu 86282212Swhu msg->interrupt_page = hv_get_phys_addr( 87282212Swhu hv_vmbus_g_connection.interrupt_page); 88282212Swhu 89282212Swhu msg->monitor_page_1 = hv_get_phys_addr( 90282212Swhu hv_vmbus_g_connection.monitor_page_1); 91282212Swhu 92282212Swhu msg->monitor_page_2 = hv_get_phys_addr( 93282212Swhu hv_vmbus_g_connection.monitor_page_2); 94282212Swhu 95282212Swhu /** 96282212Swhu * Add to list before we send the request since we may receive the 97282212Swhu * response before returning from this routine 98282212Swhu */ 99282212Swhu mtx_lock_spin(&hv_vmbus_g_connection.channel_msg_lock); 100282212Swhu 101282212Swhu TAILQ_INSERT_TAIL( 102282212Swhu &hv_vmbus_g_connection.channel_msg_anchor, 103282212Swhu msg_info, 104282212Swhu msg_list_entry); 105282212Swhu 106282212Swhu mtx_unlock_spin(&hv_vmbus_g_connection.channel_msg_lock); 107282212Swhu 108282212Swhu ret = hv_vmbus_post_message( 109282212Swhu msg, 110282212Swhu sizeof(hv_vmbus_channel_initiate_contact)); 111282212Swhu 112282212Swhu if (ret != 0) { 113282212Swhu mtx_lock_spin(&hv_vmbus_g_connection.channel_msg_lock); 114282212Swhu TAILQ_REMOVE( 115282212Swhu &hv_vmbus_g_connection.channel_msg_anchor, 116282212Swhu msg_info, 117282212Swhu msg_list_entry); 118282212Swhu mtx_unlock_spin(&hv_vmbus_g_connection.channel_msg_lock); 119282212Swhu return (ret); 120282212Swhu } 121282212Swhu 122282212Swhu /** 123282212Swhu * Wait for the connection response 124282212Swhu */ 125282212Swhu ret = sema_timedwait(&msg_info->wait_sema, 5 * hz); /* KYS 5 seconds */ 126282212Swhu 127282212Swhu mtx_lock_spin(&hv_vmbus_g_connection.channel_msg_lock); 128282212Swhu TAILQ_REMOVE( 129282212Swhu &hv_vmbus_g_connection.channel_msg_anchor, 130282212Swhu msg_info, 131282212Swhu msg_list_entry); 132282212Swhu mtx_unlock_spin(&hv_vmbus_g_connection.channel_msg_lock); 133282212Swhu 134282212Swhu /** 135282212Swhu * Check if successful 136282212Swhu */ 137282212Swhu if (msg_info->response.version_response.version_supported) { 138282212Swhu hv_vmbus_g_connection.connect_state = HV_CONNECTED; 139282212Swhu } else { 140282212Swhu ret = ECONNREFUSED; 141282212Swhu } 142282212Swhu 143282212Swhu return (ret); 144282212Swhu} 145282212Swhu 146282212Swhu/** 147282212Swhu * Send a connect request on the partition service connection 148250199Sgrehan */ 149250199Sgrehanint 150250199Sgrehanhv_vmbus_connect(void) { 151250199Sgrehan int ret = 0; 152250199Sgrehan uint32_t version; 153282212Swhu hv_vmbus_channel_msg_info* msg_info = NULL; 154250199Sgrehan 155250199Sgrehan /** 156250199Sgrehan * Make sure we are not connecting or connected 157250199Sgrehan */ 158250199Sgrehan if (hv_vmbus_g_connection.connect_state != HV_DISCONNECTED) { 159250199Sgrehan return (-1); 160250199Sgrehan } 161250199Sgrehan 162250199Sgrehan /** 163250199Sgrehan * Initialize the vmbus connection 164250199Sgrehan */ 165250199Sgrehan hv_vmbus_g_connection.connect_state = HV_CONNECTING; 166250199Sgrehan 167250199Sgrehan TAILQ_INIT(&hv_vmbus_g_connection.channel_msg_anchor); 168250199Sgrehan mtx_init(&hv_vmbus_g_connection.channel_msg_lock, "vmbus channel msg", 169250199Sgrehan NULL, MTX_SPIN); 170250199Sgrehan 171250199Sgrehan TAILQ_INIT(&hv_vmbus_g_connection.channel_anchor); 172250199Sgrehan mtx_init(&hv_vmbus_g_connection.channel_lock, "vmbus channel", 173250199Sgrehan NULL, MTX_DEF); 174250199Sgrehan 175250199Sgrehan /** 176282212Swhu * Setup the vmbus event connection for channel interrupt abstraction 177250199Sgrehan * stuff 178250199Sgrehan */ 179250199Sgrehan hv_vmbus_g_connection.interrupt_page = malloc( 180250199Sgrehan PAGE_SIZE, M_DEVBUF, 181250199Sgrehan M_WAITOK | M_ZERO); 182250199Sgrehan 183250199Sgrehan hv_vmbus_g_connection.recv_interrupt_page = 184250199Sgrehan hv_vmbus_g_connection.interrupt_page; 185250199Sgrehan 186250199Sgrehan hv_vmbus_g_connection.send_interrupt_page = 187250199Sgrehan ((uint8_t *) hv_vmbus_g_connection.interrupt_page + 188250199Sgrehan (PAGE_SIZE >> 1)); 189250199Sgrehan 190250199Sgrehan /** 191250199Sgrehan * Set up the monitor notification facility. The 1st page for 192250199Sgrehan * parent->child and the 2nd page for child->parent 193250199Sgrehan */ 194250199Sgrehan hv_vmbus_g_connection.monitor_page_1 = malloc( 195250199Sgrehan PAGE_SIZE, 196250199Sgrehan M_DEVBUF, 197250199Sgrehan M_WAITOK | M_ZERO); 198250199Sgrehan hv_vmbus_g_connection.monitor_page_2 = malloc( 199250199Sgrehan PAGE_SIZE, 200250199Sgrehan M_DEVBUF, 201250199Sgrehan M_WAITOK | M_ZERO); 202250199Sgrehan 203250199Sgrehan msg_info = (hv_vmbus_channel_msg_info*) 204250199Sgrehan malloc(sizeof(hv_vmbus_channel_msg_info) + 205250199Sgrehan sizeof(hv_vmbus_channel_initiate_contact), 206250199Sgrehan M_DEVBUF, M_WAITOK | M_ZERO); 207250199Sgrehan 208250199Sgrehan hv_vmbus_g_connection.channels = malloc(sizeof(hv_vmbus_channel*) * 209250199Sgrehan HV_CHANNEL_MAX_COUNT, 210250199Sgrehan M_DEVBUF, M_WAITOK | M_ZERO); 211250199Sgrehan /* 212250199Sgrehan * Find the highest vmbus version number we can support. 213250199Sgrehan */ 214250199Sgrehan version = HV_VMBUS_VERSION_CURRENT; 215250199Sgrehan 216250199Sgrehan do { 217250199Sgrehan ret = hv_vmbus_negotiate_version(msg_info, version); 218250199Sgrehan if (ret == EWOULDBLOCK) { 219250199Sgrehan /* 220250199Sgrehan * We timed out. 221250199Sgrehan */ 222250199Sgrehan goto cleanup; 223250199Sgrehan } 224250199Sgrehan 225250199Sgrehan if (hv_vmbus_g_connection.connect_state == HV_CONNECTED) 226250199Sgrehan break; 227250199Sgrehan 228250199Sgrehan version = hv_vmbus_get_next_version(version); 229250199Sgrehan } while (version != HV_VMBUS_VERSION_INVALID); 230250199Sgrehan 231250199Sgrehan hv_vmbus_protocal_version = version; 232294553Ssephe if (bootverbose) 233294553Ssephe printf("VMBUS: Protocol Version: %d.%d\n", 234294553Ssephe version >> 16, version & 0xFFFF); 235282212Swhu 236282212Swhu sema_destroy(&msg_info->wait_sema); 237250199Sgrehan free(msg_info, M_DEVBUF); 238282212Swhu 239250199Sgrehan return (0); 240282212Swhu 241282212Swhu /* 242282212Swhu * Cleanup after failure! 243282212Swhu */ 244282212Swhu cleanup: 245282212Swhu 246282212Swhu hv_vmbus_g_connection.connect_state = HV_DISCONNECTED; 247282212Swhu 248250199Sgrehan mtx_destroy(&hv_vmbus_g_connection.channel_lock); 249282212Swhu mtx_destroy(&hv_vmbus_g_connection.channel_msg_lock); 250282212Swhu 251250199Sgrehan if (hv_vmbus_g_connection.interrupt_page != NULL) { 252282212Swhu free(hv_vmbus_g_connection.interrupt_page, M_DEVBUF); 253282212Swhu hv_vmbus_g_connection.interrupt_page = NULL; 254250199Sgrehan } 255282212Swhu 256282212Swhu free(hv_vmbus_g_connection.monitor_page_1, M_DEVBUF); 257293870Ssephe free(hv_vmbus_g_connection.monitor_page_2, M_DEVBUF); 258282212Swhu 259250199Sgrehan if (msg_info) { 260250199Sgrehan sema_destroy(&msg_info->wait_sema); 261250199Sgrehan free(msg_info, M_DEVBUF); 262250199Sgrehan } 263250199Sgrehan 264250199Sgrehan free(hv_vmbus_g_connection.channels, M_DEVBUF); 265250199Sgrehan return (ret); 266250199Sgrehan} 267250199Sgrehan 268250199Sgrehan/** 269250199Sgrehan * Send a disconnect request on the partition service connection 270250199Sgrehan */ 271250199Sgrehanint 272250199Sgrehanhv_vmbus_disconnect(void) { 273250199Sgrehan int ret = 0; 274250199Sgrehan hv_vmbus_channel_unload msg; 275250199Sgrehan 276250199Sgrehan msg.message_type = HV_CHANNEL_MESSAGE_UNLOAD; 277250199Sgrehan 278250199Sgrehan ret = hv_vmbus_post_message(&msg, sizeof(hv_vmbus_channel_unload)); 279250199Sgrehan 280250199Sgrehan free(hv_vmbus_g_connection.interrupt_page, M_DEVBUF); 281250199Sgrehan 282250199Sgrehan mtx_destroy(&hv_vmbus_g_connection.channel_msg_lock); 283250199Sgrehan 284250199Sgrehan free(hv_vmbus_g_connection.channels, M_DEVBUF); 285250199Sgrehan hv_vmbus_g_connection.connect_state = HV_DISCONNECTED; 286250199Sgrehan 287250199Sgrehan return (ret); 288250199Sgrehan} 289250199Sgrehan 290250199Sgrehan/** 291250199Sgrehan * Handler for events 292250199Sgrehan */ 293250199Sgrehanvoid 294250199Sgrehanhv_vmbus_on_events(int cpu) 295250199Sgrehan{ 296250199Sgrehan int bit; 297250199Sgrehan int dword; 298294553Ssephe void *page_addr; 299250199Sgrehan uint32_t* recv_interrupt_page = NULL; 300250199Sgrehan int rel_id; 301250199Sgrehan int maxdword; 302250199Sgrehan hv_vmbus_synic_event_flags *event; 303250199Sgrehan /* int maxdword = PAGE_SIZE >> 3; */ 304250199Sgrehan 305250199Sgrehan KASSERT(cpu <= mp_maxid, ("VMBUS: hv_vmbus_on_events: " 306250199Sgrehan "cpu out of range!")); 307250199Sgrehan 308250199Sgrehan if ((hv_vmbus_protocal_version == HV_VMBUS_VERSION_WS2008) || 309250199Sgrehan (hv_vmbus_protocal_version == HV_VMBUS_VERSION_WIN7)) { 310250199Sgrehan maxdword = HV_MAX_NUM_CHANNELS_SUPPORTED >> 5; 311250199Sgrehan /* 312250199Sgrehan * receive size is 1/2 page and divide that by 4 bytes 313250199Sgrehan */ 314250199Sgrehan recv_interrupt_page = 315250199Sgrehan hv_vmbus_g_connection.recv_interrupt_page; 316250199Sgrehan } else { 317250199Sgrehan /* 318250199Sgrehan * On Host with Win8 or above, the event page can be 319250199Sgrehan * checked directly to get the id of the channel 320250199Sgrehan * that has the pending interrupt. 321250199Sgrehan */ 322250199Sgrehan maxdword = HV_EVENT_FLAGS_DWORD_COUNT; 323250199Sgrehan page_addr = hv_vmbus_g_context.syn_ic_event_page[cpu]; 324250199Sgrehan event = (hv_vmbus_synic_event_flags *) 325250199Sgrehan page_addr + HV_VMBUS_MESSAGE_SINT; 326250199Sgrehan recv_interrupt_page = event->flags32; 327250199Sgrehan } 328250199Sgrehan 329294553Ssephe /* 330250199Sgrehan * Check events 331250199Sgrehan */ 332250199Sgrehan if (recv_interrupt_page != NULL) { 333250199Sgrehan for (dword = 0; dword < maxdword; dword++) { 334250199Sgrehan if (recv_interrupt_page[dword]) { 335250199Sgrehan for (bit = 0; bit < HV_CHANNEL_DWORD_LEN; bit++) { 336250199Sgrehan if (synch_test_and_clear_bit(bit, 337250199Sgrehan (uint32_t *) &recv_interrupt_page[dword])) { 338250199Sgrehan rel_id = (dword << 5) + bit; 339250199Sgrehan if (rel_id == 0) { 340250199Sgrehan /* 341250199Sgrehan * Special case - 342250199Sgrehan * vmbus channel protocol msg. 343282212Swhu */ 344282212Swhu continue; 345250199Sgrehan } else { 346282212Swhu hv_vmbus_channel * channel = hv_vmbus_g_connection.channels[rel_id]; 347250199Sgrehan /* if channel is closed or closing */ 348250199Sgrehan if (channel == NULL || channel->rxq == NULL) 349250199Sgrehan continue; 350250199Sgrehan 351250199Sgrehan if (channel->batched_reading) 352250199Sgrehan hv_ring_buffer_read_begin(&channel->inbound); 353294553Ssephe taskqueue_enqueue(channel->rxq, &channel->channel_task); 354250199Sgrehan } 355250199Sgrehan } 356250199Sgrehan } 357250199Sgrehan } 358250199Sgrehan } 359250199Sgrehan } 360250199Sgrehan 361250199Sgrehan return; 362250199Sgrehan} 363250199Sgrehan 364250199Sgrehan/** 365250199Sgrehan * Send a msg on the vmbus's message connection 366250199Sgrehan */ 367282212Swhuint hv_vmbus_post_message(void *buffer, size_t bufferLen) 368282212Swhu{ 369282212Swhu hv_vmbus_connection_id connId; 370282212Swhu sbintime_t time = SBT_1MS; 371282212Swhu int retries; 372282212Swhu int ret; 373250199Sgrehan 374282212Swhu connId.as_uint32_t = 0; 375282212Swhu connId.u.id = HV_VMBUS_MESSAGE_CONNECTION_ID; 376282212Swhu 377282212Swhu /* 378282212Swhu * We retry to cope with transient failures caused by host side's 379282212Swhu * insufficient resources. 20 times should suffice in practice. 380282212Swhu */ 381282212Swhu for (retries = 0; retries < 20; retries++) { 382282212Swhu ret = hv_vmbus_post_msg_via_msg_ipc(connId, 1, buffer, 383282212Swhu bufferLen); 384282212Swhu if (ret == HV_STATUS_SUCCESS) 385282212Swhu return (0); 386282212Swhu 387282212Swhu pause_sbt("pstmsg", time, 0, C_HARDCLOCK); 388282212Swhu if (time < SBT_1S * 2) 389282212Swhu time *= 2; 390282212Swhu } 391282212Swhu 392282212Swhu KASSERT(ret == HV_STATUS_SUCCESS, 393282212Swhu ("Error VMBUS: Message Post Failed, ret=%d\n", ret)); 394282212Swhu 395282212Swhu return (EAGAIN); 396282212Swhu} 397282212Swhu 398250199Sgrehan/** 399282212Swhu * Send an event notification to the parent 400250199Sgrehan */ 401250199Sgrehanint 402250199Sgrehanhv_vmbus_set_event(hv_vmbus_channel *channel) { 403250199Sgrehan int ret = 0; 404250199Sgrehan uint32_t child_rel_id = channel->offer_msg.child_rel_id; 405250199Sgrehan 406250199Sgrehan /* Each uint32_t represents 32 channels */ 407250199Sgrehan 408282212Swhu synch_set_bit(child_rel_id & 31, 409282212Swhu (((uint32_t *)hv_vmbus_g_connection.send_interrupt_page 410250199Sgrehan + (child_rel_id >> 5)))); 411282212Swhu ret = hv_vmbus_signal_event(channel->signal_event_param); 412282212Swhu 413250199Sgrehan return (ret); 414282212Swhu} 415282212Swhu