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 48250199Sgrehan/** 49250199Sgrehan * Send a connect request on the partition service connection 50250199Sgrehan */ 51250199Sgrehanint 52250199Sgrehanhv_vmbus_connect(void) { 53250199Sgrehan int ret = 0; 54250199Sgrehan hv_vmbus_channel_msg_info* msg_info = NULL; 55250199Sgrehan hv_vmbus_channel_initiate_contact* msg; 56250199Sgrehan 57250199Sgrehan /** 58250199Sgrehan * Make sure we are not connecting or connected 59250199Sgrehan */ 60250199Sgrehan if (hv_vmbus_g_connection.connect_state != HV_DISCONNECTED) { 61250199Sgrehan return (-1); 62250199Sgrehan } 63250199Sgrehan 64250199Sgrehan /** 65250199Sgrehan * Initialize the vmbus connection 66250199Sgrehan */ 67250199Sgrehan hv_vmbus_g_connection.connect_state = HV_CONNECTING; 68250199Sgrehan hv_vmbus_g_connection.work_queue = hv_work_queue_create("vmbusQ"); 69250199Sgrehan sema_init(&hv_vmbus_g_connection.control_sema, 1, "control_sema"); 70250199Sgrehan 71250199Sgrehan TAILQ_INIT(&hv_vmbus_g_connection.channel_msg_anchor); 72250199Sgrehan mtx_init(&hv_vmbus_g_connection.channel_msg_lock, "vmbus channel msg", 73250199Sgrehan NULL, MTX_SPIN); 74250199Sgrehan 75250199Sgrehan TAILQ_INIT(&hv_vmbus_g_connection.channel_anchor); 76250199Sgrehan mtx_init(&hv_vmbus_g_connection.channel_lock, "vmbus channel", 77250199Sgrehan NULL, MTX_SPIN); 78250199Sgrehan 79250199Sgrehan /** 80250199Sgrehan * Setup the vmbus event connection for channel interrupt abstraction 81250199Sgrehan * stuff 82250199Sgrehan */ 83250199Sgrehan hv_vmbus_g_connection.interrupt_page = contigmalloc( 84250199Sgrehan PAGE_SIZE, M_DEVBUF, 85250199Sgrehan M_NOWAIT | M_ZERO, 0UL, 86250199Sgrehan BUS_SPACE_MAXADDR, 87250199Sgrehan PAGE_SIZE, 0); 88250199Sgrehan KASSERT(hv_vmbus_g_connection.interrupt_page != NULL, 89250199Sgrehan ("Error VMBUS: malloc failed to allocate Channel" 90250199Sgrehan " Request Event message!")); 91250199Sgrehan if (hv_vmbus_g_connection.interrupt_page == NULL) { 92250199Sgrehan ret = ENOMEM; 93250199Sgrehan goto cleanup; 94250199Sgrehan } 95250199Sgrehan 96250199Sgrehan hv_vmbus_g_connection.recv_interrupt_page = 97250199Sgrehan hv_vmbus_g_connection.interrupt_page; 98250199Sgrehan 99250199Sgrehan hv_vmbus_g_connection.send_interrupt_page = 100250199Sgrehan ((uint8_t *) hv_vmbus_g_connection.interrupt_page + 101250199Sgrehan (PAGE_SIZE >> 1)); 102250199Sgrehan 103250199Sgrehan /** 104250199Sgrehan * Set up the monitor notification facility. The 1st page for 105250199Sgrehan * parent->child and the 2nd page for child->parent 106250199Sgrehan */ 107250199Sgrehan hv_vmbus_g_connection.monitor_pages = contigmalloc( 108250199Sgrehan 2 * PAGE_SIZE, 109250199Sgrehan M_DEVBUF, 110250199Sgrehan M_NOWAIT | M_ZERO, 111250199Sgrehan 0UL, 112250199Sgrehan BUS_SPACE_MAXADDR, 113250199Sgrehan PAGE_SIZE, 114250199Sgrehan 0); 115250199Sgrehan KASSERT(hv_vmbus_g_connection.monitor_pages != NULL, 116250199Sgrehan ("Error VMBUS: malloc failed to allocate Monitor Pages!")); 117250199Sgrehan if (hv_vmbus_g_connection.monitor_pages == NULL) { 118250199Sgrehan ret = ENOMEM; 119250199Sgrehan goto cleanup; 120250199Sgrehan } 121250199Sgrehan 122250199Sgrehan msg_info = (hv_vmbus_channel_msg_info*) 123250199Sgrehan malloc(sizeof(hv_vmbus_channel_msg_info) + 124250199Sgrehan sizeof(hv_vmbus_channel_initiate_contact), 125250199Sgrehan M_DEVBUF, M_NOWAIT | M_ZERO); 126250199Sgrehan KASSERT(msg_info != NULL, 127250199Sgrehan ("Error VMBUS: malloc failed for Initiate Contact message!")); 128250199Sgrehan if (msg_info == NULL) { 129250199Sgrehan ret = ENOMEM; 130250199Sgrehan goto cleanup; 131250199Sgrehan } 132250199Sgrehan 133250199Sgrehan sema_init(&msg_info->wait_sema, 0, "Msg Info Sema"); 134250199Sgrehan msg = (hv_vmbus_channel_initiate_contact*) msg_info->msg; 135250199Sgrehan 136250199Sgrehan msg->header.message_type = HV_CHANNEL_MESSAGE_INITIATED_CONTACT; 137250199Sgrehan msg->vmbus_version_requested = HV_VMBUS_REVISION_NUMBER; 138250199Sgrehan 139250199Sgrehan msg->interrupt_page = hv_get_phys_addr( 140250199Sgrehan hv_vmbus_g_connection.interrupt_page); 141250199Sgrehan 142250199Sgrehan msg->monitor_page_1 = hv_get_phys_addr( 143250199Sgrehan hv_vmbus_g_connection.monitor_pages); 144250199Sgrehan 145250199Sgrehan msg->monitor_page_2 = 146250199Sgrehan hv_get_phys_addr( 147250199Sgrehan ((uint8_t *) hv_vmbus_g_connection.monitor_pages 148250199Sgrehan + PAGE_SIZE)); 149250199Sgrehan 150250199Sgrehan /** 151250199Sgrehan * Add to list before we send the request since we may receive the 152250199Sgrehan * response before returning from this routine 153250199Sgrehan */ 154250199Sgrehan mtx_lock_spin(&hv_vmbus_g_connection.channel_msg_lock); 155250199Sgrehan 156250199Sgrehan TAILQ_INSERT_TAIL( 157250199Sgrehan &hv_vmbus_g_connection.channel_msg_anchor, 158250199Sgrehan msg_info, 159250199Sgrehan msg_list_entry); 160250199Sgrehan 161250199Sgrehan mtx_unlock_spin(&hv_vmbus_g_connection.channel_msg_lock); 162250199Sgrehan 163250199Sgrehan ret = hv_vmbus_post_message( 164250199Sgrehan msg, 165250199Sgrehan sizeof(hv_vmbus_channel_initiate_contact)); 166250199Sgrehan 167250199Sgrehan if (ret != 0) { 168250199Sgrehan mtx_lock_spin(&hv_vmbus_g_connection.channel_msg_lock); 169250199Sgrehan TAILQ_REMOVE( 170250199Sgrehan &hv_vmbus_g_connection.channel_msg_anchor, 171250199Sgrehan msg_info, 172250199Sgrehan msg_list_entry); 173250199Sgrehan mtx_unlock_spin(&hv_vmbus_g_connection.channel_msg_lock); 174250199Sgrehan goto cleanup; 175250199Sgrehan } 176250199Sgrehan 177250199Sgrehan /** 178250199Sgrehan * Wait for the connection response 179250199Sgrehan */ 180250199Sgrehan ret = sema_timedwait(&msg_info->wait_sema, 500); /* KYS 5 seconds */ 181250199Sgrehan 182250199Sgrehan mtx_lock_spin(&hv_vmbus_g_connection.channel_msg_lock); 183250199Sgrehan TAILQ_REMOVE( 184250199Sgrehan &hv_vmbus_g_connection.channel_msg_anchor, 185250199Sgrehan msg_info, 186250199Sgrehan msg_list_entry); 187250199Sgrehan mtx_unlock_spin(&hv_vmbus_g_connection.channel_msg_lock); 188250199Sgrehan 189250199Sgrehan /** 190250199Sgrehan * Check if successful 191250199Sgrehan */ 192250199Sgrehan if (msg_info->response.version_response.version_supported) { 193250199Sgrehan hv_vmbus_g_connection.connect_state = HV_CONNECTED; 194250199Sgrehan } else { 195250199Sgrehan ret = ECONNREFUSED; 196250199Sgrehan goto cleanup; 197250199Sgrehan } 198250199Sgrehan 199250199Sgrehan sema_destroy(&msg_info->wait_sema); 200250199Sgrehan free(msg_info, M_DEVBUF); 201250199Sgrehan 202250199Sgrehan return (0); 203250199Sgrehan 204250199Sgrehan /* 205250199Sgrehan * Cleanup after failure! 206250199Sgrehan */ 207250199Sgrehan cleanup: 208250199Sgrehan 209250199Sgrehan hv_vmbus_g_connection.connect_state = HV_DISCONNECTED; 210250199Sgrehan 211250199Sgrehan hv_work_queue_close(hv_vmbus_g_connection.work_queue); 212250199Sgrehan sema_destroy(&hv_vmbus_g_connection.control_sema); 213250199Sgrehan mtx_destroy(&hv_vmbus_g_connection.channel_lock); 214250199Sgrehan mtx_destroy(&hv_vmbus_g_connection.channel_msg_lock); 215250199Sgrehan 216250199Sgrehan if (hv_vmbus_g_connection.interrupt_page != NULL) { 217250199Sgrehan contigfree( 218250199Sgrehan hv_vmbus_g_connection.interrupt_page, 219250199Sgrehan PAGE_SIZE, 220250199Sgrehan M_DEVBUF); 221250199Sgrehan hv_vmbus_g_connection.interrupt_page = NULL; 222250199Sgrehan } 223250199Sgrehan 224250199Sgrehan if (hv_vmbus_g_connection.monitor_pages != NULL) { 225250199Sgrehan contigfree( 226250199Sgrehan hv_vmbus_g_connection.monitor_pages, 227250199Sgrehan 2 * PAGE_SIZE, 228250199Sgrehan M_DEVBUF); 229250199Sgrehan hv_vmbus_g_connection.monitor_pages = NULL; 230250199Sgrehan } 231250199Sgrehan 232250199Sgrehan if (msg_info) { 233250199Sgrehan sema_destroy(&msg_info->wait_sema); 234250199Sgrehan free(msg_info, M_DEVBUF); 235250199Sgrehan } 236250199Sgrehan 237250199Sgrehan return (ret); 238250199Sgrehan} 239250199Sgrehan 240250199Sgrehan/** 241250199Sgrehan * Send a disconnect request on the partition service connection 242250199Sgrehan */ 243250199Sgrehanint 244250199Sgrehanhv_vmbus_disconnect(void) { 245250199Sgrehan int ret = 0; 246250199Sgrehan hv_vmbus_channel_unload* msg; 247250199Sgrehan 248250199Sgrehan msg = malloc(sizeof(hv_vmbus_channel_unload), 249250199Sgrehan M_DEVBUF, M_NOWAIT | M_ZERO); 250250199Sgrehan KASSERT(msg != NULL, 251250199Sgrehan ("Error VMBUS: malloc failed to allocate Channel Unload Msg!")); 252250199Sgrehan if (msg == NULL) 253250199Sgrehan return (ENOMEM); 254250199Sgrehan 255250199Sgrehan msg->message_type = HV_CHANNEL_MESSAGE_UNLOAD; 256250199Sgrehan 257250199Sgrehan ret = hv_vmbus_post_message(msg, sizeof(hv_vmbus_channel_unload)); 258250199Sgrehan 259250199Sgrehan 260250199Sgrehan contigfree(hv_vmbus_g_connection.interrupt_page, PAGE_SIZE, M_DEVBUF); 261250199Sgrehan 262250199Sgrehan mtx_destroy(&hv_vmbus_g_connection.channel_msg_lock); 263250199Sgrehan 264250199Sgrehan hv_work_queue_close(hv_vmbus_g_connection.work_queue); 265250199Sgrehan sema_destroy(&hv_vmbus_g_connection.control_sema); 266250199Sgrehan 267250199Sgrehan hv_vmbus_g_connection.connect_state = HV_DISCONNECTED; 268250199Sgrehan 269250199Sgrehan free(msg, M_DEVBUF); 270250199Sgrehan 271250199Sgrehan return (ret); 272250199Sgrehan} 273250199Sgrehan 274250199Sgrehan/** 275250199Sgrehan * Get the channel object given its child relative id (ie channel id) 276250199Sgrehan */ 277250199Sgrehanhv_vmbus_channel* 278250199Sgrehanhv_vmbus_get_channel_from_rel_id(uint32_t rel_id) { 279250199Sgrehan 280250199Sgrehan hv_vmbus_channel* channel; 281250199Sgrehan hv_vmbus_channel* foundChannel = NULL; 282250199Sgrehan 283250199Sgrehan /* 284250199Sgrehan * TODO: 285250199Sgrehan * Consider optimization where relids are stored in a fixed size array 286250199Sgrehan * and channels are accessed without the need to take this lock or search 287250199Sgrehan * the list. 288250199Sgrehan */ 289250199Sgrehan mtx_lock_spin(&hv_vmbus_g_connection.channel_lock); 290250199Sgrehan TAILQ_FOREACH(channel, 291250199Sgrehan &hv_vmbus_g_connection.channel_anchor, list_entry) { 292250199Sgrehan 293250199Sgrehan if (channel->offer_msg.child_rel_id == rel_id) { 294250199Sgrehan foundChannel = channel; 295250199Sgrehan break; 296250199Sgrehan } 297250199Sgrehan } 298250199Sgrehan mtx_unlock_spin(&hv_vmbus_g_connection.channel_lock); 299250199Sgrehan 300250199Sgrehan return (foundChannel); 301250199Sgrehan} 302250199Sgrehan 303250199Sgrehan/** 304250199Sgrehan * Process a channel event notification 305250199Sgrehan */ 306250199Sgrehanstatic void 307250199SgrehanVmbusProcessChannelEvent(uint32_t relid) 308250199Sgrehan{ 309250199Sgrehan hv_vmbus_channel* channel; 310250199Sgrehan 311250199Sgrehan /** 312250199Sgrehan * Find the channel based on this relid and invokes 313250199Sgrehan * the channel callback to process the event 314250199Sgrehan */ 315250199Sgrehan 316250199Sgrehan channel = hv_vmbus_get_channel_from_rel_id(relid); 317250199Sgrehan 318250199Sgrehan if (channel == NULL) { 319250199Sgrehan return; 320250199Sgrehan } 321250199Sgrehan /** 322250199Sgrehan * To deal with the race condition where we might 323250199Sgrehan * receive a packet while the relevant driver is 324250199Sgrehan * being unloaded, dispatch the callback while 325250199Sgrehan * holding the channel lock. The unloading driver 326250199Sgrehan * will acquire the same channel lock to set the 327250199Sgrehan * callback to NULL. This closes the window. 328250199Sgrehan */ 329250199Sgrehan 330250199Sgrehan mtx_lock(&channel->inbound_lock); 331250199Sgrehan if (channel->on_channel_callback != NULL) { 332250199Sgrehan channel->on_channel_callback(channel->channel_callback_context); 333250199Sgrehan } 334250199Sgrehan mtx_unlock(&channel->inbound_lock); 335250199Sgrehan} 336250199Sgrehan 337250199Sgrehan/** 338250199Sgrehan * Handler for events 339250199Sgrehan */ 340250199Sgrehanvoid 341250199Sgrehanhv_vmbus_on_events(void *arg) 342250199Sgrehan{ 343250199Sgrehan int dword; 344250199Sgrehan int bit; 345250199Sgrehan int rel_id; 346250199Sgrehan int maxdword = HV_MAX_NUM_CHANNELS_SUPPORTED >> 5; 347250199Sgrehan /* int maxdword = PAGE_SIZE >> 3; */ 348250199Sgrehan 349250199Sgrehan /* 350250199Sgrehan * receive size is 1/2 page and divide that by 4 bytes 351250199Sgrehan */ 352250199Sgrehan 353250199Sgrehan uint32_t* recv_interrupt_page = 354250199Sgrehan hv_vmbus_g_connection.recv_interrupt_page; 355250199Sgrehan 356250199Sgrehan /* 357250199Sgrehan * Check events 358250199Sgrehan */ 359250199Sgrehan if (recv_interrupt_page != NULL) { 360250199Sgrehan for (dword = 0; dword < maxdword; dword++) { 361250199Sgrehan if (recv_interrupt_page[dword]) { 362250199Sgrehan for (bit = 0; bit < 32; bit++) { 363250199Sgrehan if (synch_test_and_clear_bit(bit, 364250199Sgrehan (uint32_t *) &recv_interrupt_page[dword])) { 365250199Sgrehan rel_id = (dword << 5) + bit; 366250199Sgrehan if (rel_id == 0) { 367250199Sgrehan /* 368250199Sgrehan * Special case - 369250199Sgrehan * vmbus channel protocol msg. 370250199Sgrehan */ 371250199Sgrehan continue; 372250199Sgrehan } else { 373250199Sgrehan VmbusProcessChannelEvent(rel_id); 374250199Sgrehan 375250199Sgrehan } 376250199Sgrehan } 377250199Sgrehan } 378250199Sgrehan } 379250199Sgrehan } 380250199Sgrehan } 381250199Sgrehan 382250199Sgrehan return; 383250199Sgrehan} 384250199Sgrehan 385250199Sgrehan/** 386250199Sgrehan * Send a msg on the vmbus's message connection 387250199Sgrehan */ 388250199Sgrehanint hv_vmbus_post_message(void *buffer, size_t bufferLen) { 389250199Sgrehan int ret = 0; 390250199Sgrehan hv_vmbus_connection_id connId; 391250199Sgrehan unsigned retries = 0; 392250199Sgrehan 393250199Sgrehan /* NetScaler delays from previous code were consolidated here */ 394250199Sgrehan static int delayAmount[] = {100, 100, 100, 500, 500, 5000, 5000, 5000}; 395250199Sgrehan 396250199Sgrehan /* for(each entry in delayAmount) try to post message, 397250199Sgrehan * delay a little bit before retrying 398250199Sgrehan */ 399250199Sgrehan for (retries = 0; 400250199Sgrehan retries < sizeof(delayAmount)/sizeof(delayAmount[0]); retries++) { 401250199Sgrehan connId.as_uint32_t = 0; 402250199Sgrehan connId.u.id = HV_VMBUS_MESSAGE_CONNECTION_ID; 403250199Sgrehan ret = hv_vmbus_post_msg_via_msg_ipc(connId, 1, buffer, bufferLen); 404250199Sgrehan if (ret != HV_STATUS_INSUFFICIENT_BUFFERS) 405250199Sgrehan break; 406250199Sgrehan /* TODO: KYS We should use a blocking wait call */ 407250199Sgrehan DELAY(delayAmount[retries]); 408250199Sgrehan } 409250199Sgrehan 410250199Sgrehan KASSERT(ret == 0, ("Error VMBUS: Message Post Failed\n")); 411250199Sgrehan 412250199Sgrehan return (ret); 413250199Sgrehan} 414250199Sgrehan 415250199Sgrehan/** 416250199Sgrehan * Send an event notification to the parent 417250199Sgrehan */ 418250199Sgrehanint 419250199Sgrehanhv_vmbus_set_event(uint32_t child_rel_id) { 420250199Sgrehan int ret = 0; 421250199Sgrehan 422250199Sgrehan /* Each uint32_t represents 32 channels */ 423250199Sgrehan 424250199Sgrehan synch_set_bit(child_rel_id & 31, 425250199Sgrehan (((uint32_t *)hv_vmbus_g_connection.send_interrupt_page 426250199Sgrehan + (child_rel_id >> 5)))); 427250199Sgrehan ret = hv_vmbus_signal_event(); 428250199Sgrehan 429250199Sgrehan return (ret); 430250199Sgrehan} 431250199Sgrehan 432