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 29283280Swhu#include <sys/cdefs.h> 30283280Swhu__FBSDID("$FreeBSD: releng/10.2/sys/dev/hyperv/vmbus/hv_connection.c 283280 2015-05-22 09:03:55Z whu $"); 31283280Swhu 32250199Sgrehan#include <sys/param.h> 33250199Sgrehan#include <sys/malloc.h> 34250199Sgrehan#include <sys/systm.h> 35250199Sgrehan#include <sys/lock.h> 36250199Sgrehan#include <sys/mutex.h> 37250199Sgrehan#include <machine/bus.h> 38250199Sgrehan#include <vm/vm.h> 39250199Sgrehan#include <vm/vm_param.h> 40250199Sgrehan#include <vm/pmap.h> 41250199Sgrehan 42250199Sgrehan#include "hv_vmbus_priv.h" 43250199Sgrehan 44250199Sgrehan/* 45250199Sgrehan * Globals 46250199Sgrehan */ 47250199Sgrehanhv_vmbus_connection hv_vmbus_g_connection = 48250199Sgrehan { .connect_state = HV_DISCONNECTED, 49250199Sgrehan .next_gpadl_handle = 0xE1E10, }; 50250199Sgrehan 51283280Swhuuint32_t hv_vmbus_protocal_version = HV_VMBUS_VERSION_WS2008; 52283280Swhu 53283280Swhustatic uint32_t 54283280Swhuhv_vmbus_get_next_version(uint32_t current_ver) 55283280Swhu{ 56283280Swhu switch (current_ver) { 57283280Swhu case (HV_VMBUS_VERSION_WIN7): 58283280Swhu return(HV_VMBUS_VERSION_WS2008); 59283280Swhu 60283280Swhu case (HV_VMBUS_VERSION_WIN8): 61283280Swhu return(HV_VMBUS_VERSION_WIN7); 62283280Swhu 63283280Swhu case (HV_VMBUS_VERSION_WIN8_1): 64283280Swhu return(HV_VMBUS_VERSION_WIN8); 65283280Swhu 66283280Swhu case (HV_VMBUS_VERSION_WS2008): 67283280Swhu default: 68283280Swhu return(HV_VMBUS_VERSION_INVALID); 69283280Swhu } 70283280Swhu} 71283280Swhu 72250199Sgrehan/** 73283280Swhu * Negotiate the highest supported hypervisor version. 74283280Swhu */ 75283280Swhustatic int 76283280Swhuhv_vmbus_negotiate_version(hv_vmbus_channel_msg_info *msg_info, 77283280Swhu uint32_t version) 78283280Swhu{ 79283280Swhu int ret = 0; 80283280Swhu hv_vmbus_channel_initiate_contact *msg; 81283280Swhu 82283280Swhu sema_init(&msg_info->wait_sema, 0, "Msg Info Sema"); 83283280Swhu msg = (hv_vmbus_channel_initiate_contact*) msg_info->msg; 84283280Swhu 85283280Swhu msg->header.message_type = HV_CHANNEL_MESSAGE_INITIATED_CONTACT; 86283280Swhu msg->vmbus_version_requested = version; 87283280Swhu 88283280Swhu msg->interrupt_page = hv_get_phys_addr( 89283280Swhu hv_vmbus_g_connection.interrupt_page); 90283280Swhu 91283280Swhu msg->monitor_page_1 = hv_get_phys_addr( 92283280Swhu hv_vmbus_g_connection.monitor_pages); 93283280Swhu 94283280Swhu msg->monitor_page_2 = 95283280Swhu hv_get_phys_addr( 96283280Swhu ((uint8_t *) hv_vmbus_g_connection.monitor_pages 97283280Swhu + PAGE_SIZE)); 98283280Swhu 99283280Swhu /** 100283280Swhu * Add to list before we send the request since we may receive the 101283280Swhu * response before returning from this routine 102283280Swhu */ 103283280Swhu mtx_lock_spin(&hv_vmbus_g_connection.channel_msg_lock); 104283280Swhu 105283280Swhu TAILQ_INSERT_TAIL( 106283280Swhu &hv_vmbus_g_connection.channel_msg_anchor, 107283280Swhu msg_info, 108283280Swhu msg_list_entry); 109283280Swhu 110283280Swhu mtx_unlock_spin(&hv_vmbus_g_connection.channel_msg_lock); 111283280Swhu 112283280Swhu ret = hv_vmbus_post_message( 113283280Swhu msg, 114283280Swhu sizeof(hv_vmbus_channel_initiate_contact)); 115283280Swhu 116283280Swhu if (ret != 0) { 117283280Swhu mtx_lock_spin(&hv_vmbus_g_connection.channel_msg_lock); 118283280Swhu TAILQ_REMOVE( 119283280Swhu &hv_vmbus_g_connection.channel_msg_anchor, 120283280Swhu msg_info, 121283280Swhu msg_list_entry); 122283280Swhu mtx_unlock_spin(&hv_vmbus_g_connection.channel_msg_lock); 123283280Swhu return (ret); 124283280Swhu } 125283280Swhu 126283280Swhu /** 127283280Swhu * Wait for the connection response 128283280Swhu */ 129283280Swhu ret = sema_timedwait(&msg_info->wait_sema, 500); /* KYS 5 seconds */ 130283280Swhu 131283280Swhu mtx_lock_spin(&hv_vmbus_g_connection.channel_msg_lock); 132283280Swhu TAILQ_REMOVE( 133283280Swhu &hv_vmbus_g_connection.channel_msg_anchor, 134283280Swhu msg_info, 135283280Swhu msg_list_entry); 136283280Swhu mtx_unlock_spin(&hv_vmbus_g_connection.channel_msg_lock); 137283280Swhu 138283280Swhu /** 139283280Swhu * Check if successful 140283280Swhu */ 141283280Swhu if (msg_info->response.version_response.version_supported) { 142283280Swhu hv_vmbus_g_connection.connect_state = HV_CONNECTED; 143283280Swhu } else { 144283280Swhu ret = ECONNREFUSED; 145283280Swhu } 146283280Swhu 147283280Swhu return (ret); 148283280Swhu} 149283280Swhu 150283280Swhu/** 151250199Sgrehan * Send a connect request on the partition service connection 152250199Sgrehan */ 153250199Sgrehanint 154250199Sgrehanhv_vmbus_connect(void) { 155250199Sgrehan int ret = 0; 156283280Swhu uint32_t version; 157250199Sgrehan hv_vmbus_channel_msg_info* msg_info = NULL; 158250199Sgrehan 159250199Sgrehan /** 160250199Sgrehan * Make sure we are not connecting or connected 161250199Sgrehan */ 162250199Sgrehan if (hv_vmbus_g_connection.connect_state != HV_DISCONNECTED) { 163250199Sgrehan return (-1); 164250199Sgrehan } 165250199Sgrehan 166250199Sgrehan /** 167250199Sgrehan * Initialize the vmbus connection 168250199Sgrehan */ 169250199Sgrehan hv_vmbus_g_connection.connect_state = HV_CONNECTING; 170250199Sgrehan hv_vmbus_g_connection.work_queue = hv_work_queue_create("vmbusQ"); 171250199Sgrehan sema_init(&hv_vmbus_g_connection.control_sema, 1, "control_sema"); 172250199Sgrehan 173250199Sgrehan TAILQ_INIT(&hv_vmbus_g_connection.channel_msg_anchor); 174250199Sgrehan mtx_init(&hv_vmbus_g_connection.channel_msg_lock, "vmbus channel msg", 175250199Sgrehan NULL, MTX_SPIN); 176250199Sgrehan 177250199Sgrehan TAILQ_INIT(&hv_vmbus_g_connection.channel_anchor); 178250199Sgrehan mtx_init(&hv_vmbus_g_connection.channel_lock, "vmbus channel", 179283280Swhu NULL, MTX_DEF); 180250199Sgrehan 181250199Sgrehan /** 182250199Sgrehan * Setup the vmbus event connection for channel interrupt abstraction 183250199Sgrehan * stuff 184250199Sgrehan */ 185250199Sgrehan hv_vmbus_g_connection.interrupt_page = contigmalloc( 186250199Sgrehan PAGE_SIZE, M_DEVBUF, 187250199Sgrehan M_NOWAIT | M_ZERO, 0UL, 188250199Sgrehan BUS_SPACE_MAXADDR, 189250199Sgrehan PAGE_SIZE, 0); 190250199Sgrehan KASSERT(hv_vmbus_g_connection.interrupt_page != NULL, 191250199Sgrehan ("Error VMBUS: malloc failed to allocate Channel" 192250199Sgrehan " Request Event message!")); 193250199Sgrehan if (hv_vmbus_g_connection.interrupt_page == NULL) { 194250199Sgrehan ret = ENOMEM; 195250199Sgrehan goto cleanup; 196250199Sgrehan } 197250199Sgrehan 198250199Sgrehan hv_vmbus_g_connection.recv_interrupt_page = 199250199Sgrehan hv_vmbus_g_connection.interrupt_page; 200250199Sgrehan 201250199Sgrehan hv_vmbus_g_connection.send_interrupt_page = 202250199Sgrehan ((uint8_t *) hv_vmbus_g_connection.interrupt_page + 203250199Sgrehan (PAGE_SIZE >> 1)); 204250199Sgrehan 205250199Sgrehan /** 206250199Sgrehan * Set up the monitor notification facility. The 1st page for 207250199Sgrehan * parent->child and the 2nd page for child->parent 208250199Sgrehan */ 209250199Sgrehan hv_vmbus_g_connection.monitor_pages = contigmalloc( 210250199Sgrehan 2 * PAGE_SIZE, 211250199Sgrehan M_DEVBUF, 212250199Sgrehan M_NOWAIT | M_ZERO, 213250199Sgrehan 0UL, 214250199Sgrehan BUS_SPACE_MAXADDR, 215250199Sgrehan PAGE_SIZE, 216250199Sgrehan 0); 217250199Sgrehan KASSERT(hv_vmbus_g_connection.monitor_pages != NULL, 218250199Sgrehan ("Error VMBUS: malloc failed to allocate Monitor Pages!")); 219250199Sgrehan if (hv_vmbus_g_connection.monitor_pages == NULL) { 220250199Sgrehan ret = ENOMEM; 221250199Sgrehan goto cleanup; 222250199Sgrehan } 223250199Sgrehan 224250199Sgrehan msg_info = (hv_vmbus_channel_msg_info*) 225250199Sgrehan malloc(sizeof(hv_vmbus_channel_msg_info) + 226250199Sgrehan sizeof(hv_vmbus_channel_initiate_contact), 227250199Sgrehan M_DEVBUF, M_NOWAIT | M_ZERO); 228250199Sgrehan KASSERT(msg_info != NULL, 229250199Sgrehan ("Error VMBUS: malloc failed for Initiate Contact message!")); 230250199Sgrehan if (msg_info == NULL) { 231250199Sgrehan ret = ENOMEM; 232250199Sgrehan goto cleanup; 233250199Sgrehan } 234250199Sgrehan 235283280Swhu /* 236283280Swhu * Find the highest vmbus version number we can support. 237250199Sgrehan */ 238283280Swhu version = HV_VMBUS_VERSION_CURRENT; 239250199Sgrehan 240283280Swhu do { 241283280Swhu ret = hv_vmbus_negotiate_version(msg_info, version); 242283280Swhu if (ret == EWOULDBLOCK) { 243283280Swhu /* 244283280Swhu * We timed out. 245283280Swhu */ 246283280Swhu goto cleanup; 247283280Swhu } 248250199Sgrehan 249283280Swhu if (hv_vmbus_g_connection.connect_state == HV_CONNECTED) 250283280Swhu break; 251250199Sgrehan 252283280Swhu version = hv_vmbus_get_next_version(version); 253283280Swhu } while (version != HV_VMBUS_VERSION_INVALID); 254250199Sgrehan 255283280Swhu hv_vmbus_protocal_version = version; 256283280Swhu if (bootverbose) 257283280Swhu printf("VMBUS: Portocal Version: %d.%d\n", 258283280Swhu version >> 16, version & 0xFFFF); 259250199Sgrehan 260250199Sgrehan sema_destroy(&msg_info->wait_sema); 261250199Sgrehan free(msg_info, M_DEVBUF); 262250199Sgrehan 263250199Sgrehan return (0); 264250199Sgrehan 265250199Sgrehan /* 266250199Sgrehan * Cleanup after failure! 267250199Sgrehan */ 268250199Sgrehan cleanup: 269250199Sgrehan 270250199Sgrehan hv_vmbus_g_connection.connect_state = HV_DISCONNECTED; 271250199Sgrehan 272250199Sgrehan hv_work_queue_close(hv_vmbus_g_connection.work_queue); 273250199Sgrehan sema_destroy(&hv_vmbus_g_connection.control_sema); 274250199Sgrehan mtx_destroy(&hv_vmbus_g_connection.channel_lock); 275250199Sgrehan mtx_destroy(&hv_vmbus_g_connection.channel_msg_lock); 276250199Sgrehan 277250199Sgrehan if (hv_vmbus_g_connection.interrupt_page != NULL) { 278250199Sgrehan contigfree( 279250199Sgrehan hv_vmbus_g_connection.interrupt_page, 280250199Sgrehan PAGE_SIZE, 281250199Sgrehan M_DEVBUF); 282250199Sgrehan hv_vmbus_g_connection.interrupt_page = NULL; 283250199Sgrehan } 284250199Sgrehan 285250199Sgrehan if (hv_vmbus_g_connection.monitor_pages != NULL) { 286250199Sgrehan contigfree( 287250199Sgrehan hv_vmbus_g_connection.monitor_pages, 288250199Sgrehan 2 * PAGE_SIZE, 289250199Sgrehan M_DEVBUF); 290250199Sgrehan hv_vmbus_g_connection.monitor_pages = NULL; 291250199Sgrehan } 292250199Sgrehan 293250199Sgrehan if (msg_info) { 294250199Sgrehan sema_destroy(&msg_info->wait_sema); 295250199Sgrehan free(msg_info, M_DEVBUF); 296250199Sgrehan } 297250199Sgrehan 298250199Sgrehan return (ret); 299250199Sgrehan} 300250199Sgrehan 301250199Sgrehan/** 302250199Sgrehan * Send a disconnect request on the partition service connection 303250199Sgrehan */ 304250199Sgrehanint 305250199Sgrehanhv_vmbus_disconnect(void) { 306250199Sgrehan int ret = 0; 307250199Sgrehan hv_vmbus_channel_unload* msg; 308250199Sgrehan 309250199Sgrehan msg = malloc(sizeof(hv_vmbus_channel_unload), 310250199Sgrehan M_DEVBUF, M_NOWAIT | M_ZERO); 311250199Sgrehan KASSERT(msg != NULL, 312250199Sgrehan ("Error VMBUS: malloc failed to allocate Channel Unload Msg!")); 313250199Sgrehan if (msg == NULL) 314250199Sgrehan return (ENOMEM); 315250199Sgrehan 316250199Sgrehan msg->message_type = HV_CHANNEL_MESSAGE_UNLOAD; 317250199Sgrehan 318250199Sgrehan ret = hv_vmbus_post_message(msg, sizeof(hv_vmbus_channel_unload)); 319250199Sgrehan 320250199Sgrehan 321250199Sgrehan contigfree(hv_vmbus_g_connection.interrupt_page, PAGE_SIZE, M_DEVBUF); 322250199Sgrehan 323250199Sgrehan mtx_destroy(&hv_vmbus_g_connection.channel_msg_lock); 324250199Sgrehan 325250199Sgrehan hv_work_queue_close(hv_vmbus_g_connection.work_queue); 326250199Sgrehan sema_destroy(&hv_vmbus_g_connection.control_sema); 327250199Sgrehan 328250199Sgrehan hv_vmbus_g_connection.connect_state = HV_DISCONNECTED; 329250199Sgrehan 330250199Sgrehan free(msg, M_DEVBUF); 331250199Sgrehan 332250199Sgrehan return (ret); 333250199Sgrehan} 334250199Sgrehan 335250199Sgrehan/** 336250199Sgrehan * Get the channel object given its child relative id (ie channel id) 337250199Sgrehan */ 338250199Sgrehanhv_vmbus_channel* 339250199Sgrehanhv_vmbus_get_channel_from_rel_id(uint32_t rel_id) { 340250199Sgrehan 341250199Sgrehan hv_vmbus_channel* channel; 342250199Sgrehan hv_vmbus_channel* foundChannel = NULL; 343250199Sgrehan 344250199Sgrehan /* 345250199Sgrehan * TODO: 346250199Sgrehan * Consider optimization where relids are stored in a fixed size array 347250199Sgrehan * and channels are accessed without the need to take this lock or search 348250199Sgrehan * the list. 349250199Sgrehan */ 350283280Swhu mtx_lock(&hv_vmbus_g_connection.channel_lock); 351250199Sgrehan TAILQ_FOREACH(channel, 352250199Sgrehan &hv_vmbus_g_connection.channel_anchor, list_entry) { 353250199Sgrehan 354250199Sgrehan if (channel->offer_msg.child_rel_id == rel_id) { 355250199Sgrehan foundChannel = channel; 356250199Sgrehan break; 357250199Sgrehan } 358250199Sgrehan } 359283280Swhu mtx_unlock(&hv_vmbus_g_connection.channel_lock); 360250199Sgrehan 361250199Sgrehan return (foundChannel); 362250199Sgrehan} 363250199Sgrehan 364250199Sgrehan/** 365250199Sgrehan * Process a channel event notification 366250199Sgrehan */ 367250199Sgrehanstatic void 368250199SgrehanVmbusProcessChannelEvent(uint32_t relid) 369250199Sgrehan{ 370283280Swhu void* arg; 371283280Swhu uint32_t bytes_to_read; 372250199Sgrehan hv_vmbus_channel* channel; 373283280Swhu boolean_t is_batched_reading; 374250199Sgrehan 375250199Sgrehan /** 376250199Sgrehan * Find the channel based on this relid and invokes 377250199Sgrehan * the channel callback to process the event 378250199Sgrehan */ 379250199Sgrehan 380250199Sgrehan channel = hv_vmbus_get_channel_from_rel_id(relid); 381250199Sgrehan 382250199Sgrehan if (channel == NULL) { 383250199Sgrehan return; 384250199Sgrehan } 385250199Sgrehan /** 386250199Sgrehan * To deal with the race condition where we might 387250199Sgrehan * receive a packet while the relevant driver is 388250199Sgrehan * being unloaded, dispatch the callback while 389250199Sgrehan * holding the channel lock. The unloading driver 390250199Sgrehan * will acquire the same channel lock to set the 391250199Sgrehan * callback to NULL. This closes the window. 392250199Sgrehan */ 393250199Sgrehan 394283280Swhu /* 395283280Swhu * Disable the lock due to newly added WITNESS check in r277723. 396283280Swhu * Will seek other way to avoid race condition. 397283280Swhu * -- whu 398283280Swhu */ 399283280Swhu // mtx_lock(&channel->inbound_lock); 400250199Sgrehan if (channel->on_channel_callback != NULL) { 401283280Swhu arg = channel->channel_callback_context; 402283280Swhu is_batched_reading = channel->batched_reading; 403283280Swhu /* 404283280Swhu * Optimize host to guest signaling by ensuring: 405283280Swhu * 1. While reading the channel, we disable interrupts from 406283280Swhu * host. 407283280Swhu * 2. Ensure that we process all posted messages from the host 408283280Swhu * before returning from this callback. 409283280Swhu * 3. Once we return, enable signaling from the host. Once this 410283280Swhu * state is set we check to see if additional packets are 411283280Swhu * available to read. In this case we repeat the process. 412283280Swhu */ 413283280Swhu do { 414283280Swhu if (is_batched_reading) 415283280Swhu hv_ring_buffer_read_begin(&channel->inbound); 416283280Swhu 417283280Swhu channel->on_channel_callback(arg); 418283280Swhu 419283280Swhu if (is_batched_reading) 420283280Swhu bytes_to_read = 421283280Swhu hv_ring_buffer_read_end(&channel->inbound); 422283280Swhu else 423283280Swhu bytes_to_read = 0; 424283280Swhu } while (is_batched_reading && (bytes_to_read != 0)); 425250199Sgrehan } 426283280Swhu // mtx_unlock(&channel->inbound_lock); 427250199Sgrehan} 428250199Sgrehan 429283280Swhu#ifdef HV_DEBUG_INTR 430283280Swhuextern uint32_t hv_intr_count; 431283280Swhuextern uint32_t hv_vmbus_swintr_event_cpu[MAXCPU]; 432283280Swhuextern uint32_t hv_vmbus_intr_cpu[MAXCPU]; 433283280Swhu#endif 434283280Swhu 435250199Sgrehan/** 436250199Sgrehan * Handler for events 437250199Sgrehan */ 438250199Sgrehanvoid 439250199Sgrehanhv_vmbus_on_events(void *arg) 440250199Sgrehan{ 441283280Swhu int bit; 442283280Swhu int cpu; 443250199Sgrehan int dword; 444283280Swhu void *page_addr; 445283280Swhu uint32_t* recv_interrupt_page = NULL; 446250199Sgrehan int rel_id; 447283280Swhu int maxdword; 448283280Swhu hv_vmbus_synic_event_flags *event; 449250199Sgrehan /* int maxdword = PAGE_SIZE >> 3; */ 450250199Sgrehan 451283280Swhu cpu = (int)(long)arg; 452283280Swhu KASSERT(cpu <= mp_maxid, ("VMBUS: hv_vmbus_on_events: " 453283280Swhu "cpu out of range!")); 454250199Sgrehan 455283280Swhu#ifdef HV_DEBUG_INTR 456283280Swhu int i; 457283280Swhu hv_vmbus_swintr_event_cpu[cpu]++; 458283280Swhu if (hv_intr_count % 10000 == 0) { 459283280Swhu printf("VMBUS: Total interrupt %d\n", hv_intr_count); 460283280Swhu for (i = 0; i < mp_ncpus; i++) 461283280Swhu printf("VMBUS: hw cpu[%d]: %d, event sw intr cpu[%d]: %d\n", 462283280Swhu i, hv_vmbus_intr_cpu[i], i, hv_vmbus_swintr_event_cpu[i]); 463283280Swhu } 464283280Swhu#endif 465250199Sgrehan 466283280Swhu if ((hv_vmbus_protocal_version == HV_VMBUS_VERSION_WS2008) || 467283280Swhu (hv_vmbus_protocal_version == HV_VMBUS_VERSION_WIN7)) { 468283280Swhu maxdword = HV_MAX_NUM_CHANNELS_SUPPORTED >> 5; 469283280Swhu /* 470283280Swhu * receive size is 1/2 page and divide that by 4 bytes 471283280Swhu */ 472283280Swhu recv_interrupt_page = 473283280Swhu hv_vmbus_g_connection.recv_interrupt_page; 474283280Swhu } else { 475283280Swhu /* 476283280Swhu * On Host with Win8 or above, the event page can be 477283280Swhu * checked directly to get the id of the channel 478283280Swhu * that has the pending interrupt. 479283280Swhu */ 480283280Swhu maxdword = HV_EVENT_FLAGS_DWORD_COUNT; 481283280Swhu page_addr = hv_vmbus_g_context.syn_ic_event_page[cpu]; 482283280Swhu event = (hv_vmbus_synic_event_flags *) 483283280Swhu page_addr + HV_VMBUS_MESSAGE_SINT; 484283280Swhu recv_interrupt_page = event->flags32; 485283280Swhu } 486283280Swhu 487250199Sgrehan /* 488250199Sgrehan * Check events 489250199Sgrehan */ 490250199Sgrehan if (recv_interrupt_page != NULL) { 491250199Sgrehan for (dword = 0; dword < maxdword; dword++) { 492250199Sgrehan if (recv_interrupt_page[dword]) { 493250199Sgrehan for (bit = 0; bit < 32; bit++) { 494250199Sgrehan if (synch_test_and_clear_bit(bit, 495250199Sgrehan (uint32_t *) &recv_interrupt_page[dword])) { 496250199Sgrehan rel_id = (dword << 5) + bit; 497250199Sgrehan if (rel_id == 0) { 498250199Sgrehan /* 499250199Sgrehan * Special case - 500250199Sgrehan * vmbus channel protocol msg. 501250199Sgrehan */ 502250199Sgrehan continue; 503250199Sgrehan } else { 504250199Sgrehan VmbusProcessChannelEvent(rel_id); 505250199Sgrehan 506250199Sgrehan } 507250199Sgrehan } 508250199Sgrehan } 509250199Sgrehan } 510250199Sgrehan } 511250199Sgrehan } 512250199Sgrehan 513250199Sgrehan return; 514250199Sgrehan} 515250199Sgrehan 516250199Sgrehan/** 517250199Sgrehan * Send a msg on the vmbus's message connection 518250199Sgrehan */ 519250199Sgrehanint hv_vmbus_post_message(void *buffer, size_t bufferLen) { 520250199Sgrehan int ret = 0; 521250199Sgrehan hv_vmbus_connection_id connId; 522250199Sgrehan unsigned retries = 0; 523250199Sgrehan 524250199Sgrehan /* NetScaler delays from previous code were consolidated here */ 525250199Sgrehan static int delayAmount[] = {100, 100, 100, 500, 500, 5000, 5000, 5000}; 526250199Sgrehan 527250199Sgrehan /* for(each entry in delayAmount) try to post message, 528250199Sgrehan * delay a little bit before retrying 529250199Sgrehan */ 530250199Sgrehan for (retries = 0; 531250199Sgrehan retries < sizeof(delayAmount)/sizeof(delayAmount[0]); retries++) { 532250199Sgrehan connId.as_uint32_t = 0; 533250199Sgrehan connId.u.id = HV_VMBUS_MESSAGE_CONNECTION_ID; 534250199Sgrehan ret = hv_vmbus_post_msg_via_msg_ipc(connId, 1, buffer, bufferLen); 535250199Sgrehan if (ret != HV_STATUS_INSUFFICIENT_BUFFERS) 536250199Sgrehan break; 537250199Sgrehan /* TODO: KYS We should use a blocking wait call */ 538250199Sgrehan DELAY(delayAmount[retries]); 539250199Sgrehan } 540250199Sgrehan 541250199Sgrehan KASSERT(ret == 0, ("Error VMBUS: Message Post Failed\n")); 542250199Sgrehan 543250199Sgrehan return (ret); 544250199Sgrehan} 545250199Sgrehan 546250199Sgrehan/** 547250199Sgrehan * Send an event notification to the parent 548250199Sgrehan */ 549250199Sgrehanint 550283280Swhuhv_vmbus_set_event(hv_vmbus_channel *channel) { 551250199Sgrehan int ret = 0; 552283280Swhu uint32_t child_rel_id = channel->offer_msg.child_rel_id; 553250199Sgrehan 554250199Sgrehan /* Each uint32_t represents 32 channels */ 555250199Sgrehan 556250199Sgrehan synch_set_bit(child_rel_id & 31, 557250199Sgrehan (((uint32_t *)hv_vmbus_g_connection.send_interrupt_page 558250199Sgrehan + (child_rel_id >> 5)))); 559283280Swhu ret = hv_vmbus_signal_event(channel->signal_event_param); 560250199Sgrehan 561250199Sgrehan return (ret); 562250199Sgrehan} 563