hv_connection.c revision 251775
150476Speter/*- 21638Srgrimes * Copyright (c) 2009-2012 Microsoft Corp. 3172476Sedwin * Copyright (c) 2012 NetApp Inc. 4172476Sedwin * Copyright (c) 2012 Citrix Inc. 5172476Sedwin * All rights reserved. 6181426Sedwin * 7172476Sedwin * Redistribution and use in source and binary forms, with or without 8181426Sedwin * modification, are permitted provided that the following conditions 9172476Sedwin * are met: 10181426Sedwin * 1. Redistributions of source code must retain the above copyright 11181426Sedwin * notice unmodified, this list of conditions, and the following 12181426Sedwin * disclaimer. 13181426Sedwin * 2. Redistributions in binary form must reproduce the above copyright 14172476Sedwin * notice, this list of conditions and the following disclaimer in the 15181426Sedwin * documentation and/or other materials provided with the distribution. 16172476Sedwin * 17181426Sedwin * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR 18181426Sedwin * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES 19181426Sedwin * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. 20183865Sedwin * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, 21172476Sedwin * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT 22181426Sedwin * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 23172476Sedwin * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 24183865Sedwin * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 25181426Sedwin * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF 26183865Sedwin * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 27181426Sedwin */ 28183865Sedwin 29172476Sedwin#include <sys/param.h> 30172476Sedwin#include <sys/malloc.h> 312747Swollman#include <sys/systm.h> 321638Srgrimes#include <sys/lock.h> 332747Swollman#include <sys/mutex.h> 3412319Sgpalmer#include <machine/bus.h> 352747Swollman#include <vm/vm.h> 3612319Sgpalmer#include <vm/vm_param.h> 372747Swollman#include <vm/pmap.h> 381638Srgrimes 392747Swollman#include "hv_vmbus_priv.h" 40149653Swollman 412747Swollman/* 421638Srgrimes * Globals 432747Swollman */ 44149653Swollmanhv_vmbus_connection hv_vmbus_g_connection = 452747Swollman { .connect_state = HV_DISCONNECTED, 461638Srgrimes .next_gpadl_handle = 0xE1E10, }; 4723569Sbde 481638Srgrimes/** 4923569Sbde * Send a connect request on the partition service connection 502747Swollman */ 5183346Sruint 52136709Sruhv_vmbus_connect(void) { 53136709Sru int ret = 0; 54100872Sru hv_vmbus_channel_msg_info* msg_info = NULL; 5523569Sbde hv_vmbus_channel_initiate_contact* msg; 561638Srgrimes 57198351Sedwin /** 58198351Sedwin * Make sure we are not connecting or connected 59198351Sedwin */ 60198351Sedwin if (hv_vmbus_g_connection.connect_state != HV_DISCONNECTED) { 61198351Sedwin return (-1); 62198351Sedwin } 63198351Sedwin 64198351Sedwin /** 65198351Sedwin * Initialize the vmbus connection 66198351Sedwin */ 67198351Sedwin hv_vmbus_g_connection.connect_state = HV_CONNECTING; 68198351Sedwin hv_vmbus_g_connection.work_queue = hv_work_queue_create("vmbusQ"); 69198351Sedwin sema_init(&hv_vmbus_g_connection.control_sema, 1, "control_sema"); 70198351Sedwin 71198351Sedwin TAILQ_INIT(&hv_vmbus_g_connection.channel_msg_anchor); 72198351Sedwin mtx_init(&hv_vmbus_g_connection.channel_msg_lock, "vmbus channel msg", 73198351Sedwin NULL, MTX_SPIN); 74198351Sedwin 75198351Sedwin TAILQ_INIT(&hv_vmbus_g_connection.channel_anchor); 76198351Sedwin mtx_init(&hv_vmbus_g_connection.channel_lock, "vmbus channel", 771638Srgrimes NULL, MTX_SPIN); 78 79 /** 80 * Setup the vmbus event connection for channel interrupt abstraction 81 * stuff 82 */ 83 hv_vmbus_g_connection.interrupt_page = contigmalloc( 84 PAGE_SIZE, M_DEVBUF, 85 M_NOWAIT | M_ZERO, 0UL, 86 BUS_SPACE_MAXADDR, 87 PAGE_SIZE, 0); 88 KASSERT(hv_vmbus_g_connection.interrupt_page != NULL, 89 ("Error VMBUS: malloc failed to allocate Channel" 90 " Request Event message!")); 91 if (hv_vmbus_g_connection.interrupt_page == NULL) { 92 ret = ENOMEM; 93 goto cleanup; 94 } 95 96 hv_vmbus_g_connection.recv_interrupt_page = 97 hv_vmbus_g_connection.interrupt_page; 98 99 hv_vmbus_g_connection.send_interrupt_page = 100 ((uint8_t *) hv_vmbus_g_connection.interrupt_page + 101 (PAGE_SIZE >> 1)); 102 103 /** 104 * Set up the monitor notification facility. The 1st page for 105 * parent->child and the 2nd page for child->parent 106 */ 107 hv_vmbus_g_connection.monitor_pages = contigmalloc( 108 2 * PAGE_SIZE, 109 M_DEVBUF, 110 M_NOWAIT | M_ZERO, 111 0UL, 112 BUS_SPACE_MAXADDR, 113 PAGE_SIZE, 114 0); 115 KASSERT(hv_vmbus_g_connection.monitor_pages != NULL, 116 ("Error VMBUS: malloc failed to allocate Monitor Pages!")); 117 if (hv_vmbus_g_connection.monitor_pages == NULL) { 118 ret = ENOMEM; 119 goto cleanup; 120 } 121 122 msg_info = (hv_vmbus_channel_msg_info*) 123 malloc(sizeof(hv_vmbus_channel_msg_info) + 124 sizeof(hv_vmbus_channel_initiate_contact), 125 M_DEVBUF, M_NOWAIT | M_ZERO); 126 KASSERT(msg_info != NULL, 127 ("Error VMBUS: malloc failed for Initiate Contact message!")); 128 if (msg_info == NULL) { 129 ret = ENOMEM; 130 goto cleanup; 131 } 132 133 sema_init(&msg_info->wait_sema, 0, "Msg Info Sema"); 134 msg = (hv_vmbus_channel_initiate_contact*) msg_info->msg; 135 136 msg->header.message_type = HV_CHANNEL_MESSAGE_INITIATED_CONTACT; 137 msg->vmbus_version_requested = HV_VMBUS_REVISION_NUMBER; 138 139 msg->interrupt_page = hv_get_phys_addr( 140 hv_vmbus_g_connection.interrupt_page); 141 142 msg->monitor_page_1 = hv_get_phys_addr( 143 hv_vmbus_g_connection.monitor_pages); 144 145 msg->monitor_page_2 = 146 hv_get_phys_addr( 147 ((uint8_t *) hv_vmbus_g_connection.monitor_pages 148 + PAGE_SIZE)); 149 150 /** 151 * Add to list before we send the request since we may receive the 152 * response before returning from this routine 153 */ 154 mtx_lock_spin(&hv_vmbus_g_connection.channel_msg_lock); 155 156 TAILQ_INSERT_TAIL( 157 &hv_vmbus_g_connection.channel_msg_anchor, 158 msg_info, 159 msg_list_entry); 160 161 mtx_unlock_spin(&hv_vmbus_g_connection.channel_msg_lock); 162 163 ret = hv_vmbus_post_message( 164 msg, 165 sizeof(hv_vmbus_channel_initiate_contact)); 166 167 if (ret != 0) { 168 mtx_lock_spin(&hv_vmbus_g_connection.channel_msg_lock); 169 TAILQ_REMOVE( 170 &hv_vmbus_g_connection.channel_msg_anchor, 171 msg_info, 172 msg_list_entry); 173 mtx_unlock_spin(&hv_vmbus_g_connection.channel_msg_lock); 174 goto cleanup; 175 } 176 177 /** 178 * Wait for the connection response 179 */ 180 ret = sema_timedwait(&msg_info->wait_sema, 500); /* KYS 5 seconds */ 181 182 mtx_lock_spin(&hv_vmbus_g_connection.channel_msg_lock); 183 TAILQ_REMOVE( 184 &hv_vmbus_g_connection.channel_msg_anchor, 185 msg_info, 186 msg_list_entry); 187 mtx_unlock_spin(&hv_vmbus_g_connection.channel_msg_lock); 188 189 /** 190 * Check if successful 191 */ 192 if (msg_info->response.version_response.version_supported) { 193 hv_vmbus_g_connection.connect_state = HV_CONNECTED; 194 } else { 195 ret = ECONNREFUSED; 196 goto cleanup; 197 } 198 199 sema_destroy(&msg_info->wait_sema); 200 free(msg_info, M_DEVBUF); 201 202 return (0); 203 204 /* 205 * Cleanup after failure! 206 */ 207 cleanup: 208 209 hv_vmbus_g_connection.connect_state = HV_DISCONNECTED; 210 211 hv_work_queue_close(hv_vmbus_g_connection.work_queue); 212 sema_destroy(&hv_vmbus_g_connection.control_sema); 213 mtx_destroy(&hv_vmbus_g_connection.channel_lock); 214 mtx_destroy(&hv_vmbus_g_connection.channel_msg_lock); 215 216 if (hv_vmbus_g_connection.interrupt_page != NULL) { 217 contigfree( 218 hv_vmbus_g_connection.interrupt_page, 219 PAGE_SIZE, 220 M_DEVBUF); 221 hv_vmbus_g_connection.interrupt_page = NULL; 222 } 223 224 if (hv_vmbus_g_connection.monitor_pages != NULL) { 225 contigfree( 226 hv_vmbus_g_connection.monitor_pages, 227 2 * PAGE_SIZE, 228 M_DEVBUF); 229 hv_vmbus_g_connection.monitor_pages = NULL; 230 } 231 232 if (msg_info) { 233 sema_destroy(&msg_info->wait_sema); 234 free(msg_info, M_DEVBUF); 235 } 236 237 return (ret); 238} 239 240/** 241 * Send a disconnect request on the partition service connection 242 */ 243int 244hv_vmbus_disconnect(void) { 245 int ret = 0; 246 hv_vmbus_channel_unload* msg; 247 248 msg = malloc(sizeof(hv_vmbus_channel_unload), 249 M_DEVBUF, M_NOWAIT | M_ZERO); 250 KASSERT(msg != NULL, 251 ("Error VMBUS: malloc failed to allocate Channel Unload Msg!")); 252 if (msg == NULL) 253 return (ENOMEM); 254 255 msg->message_type = HV_CHANNEL_MESSAGE_UNLOAD; 256 257 ret = hv_vmbus_post_message(msg, sizeof(hv_vmbus_channel_unload)); 258 259 260 contigfree(hv_vmbus_g_connection.interrupt_page, PAGE_SIZE, M_DEVBUF); 261 262 mtx_destroy(&hv_vmbus_g_connection.channel_msg_lock); 263 264 hv_work_queue_close(hv_vmbus_g_connection.work_queue); 265 sema_destroy(&hv_vmbus_g_connection.control_sema); 266 267 hv_vmbus_g_connection.connect_state = HV_DISCONNECTED; 268 269 free(msg, M_DEVBUF); 270 271 return (ret); 272} 273 274/** 275 * Get the channel object given its child relative id (ie channel id) 276 */ 277hv_vmbus_channel* 278hv_vmbus_get_channel_from_rel_id(uint32_t rel_id) { 279 280 hv_vmbus_channel* channel; 281 hv_vmbus_channel* foundChannel = NULL; 282 283 /* 284 * TODO: 285 * Consider optimization where relids are stored in a fixed size array 286 * and channels are accessed without the need to take this lock or search 287 * the list. 288 */ 289 mtx_lock_spin(&hv_vmbus_g_connection.channel_lock); 290 TAILQ_FOREACH(channel, 291 &hv_vmbus_g_connection.channel_anchor, list_entry) { 292 293 if (channel->offer_msg.child_rel_id == rel_id) { 294 foundChannel = channel; 295 break; 296 } 297 } 298 mtx_unlock_spin(&hv_vmbus_g_connection.channel_lock); 299 300 return (foundChannel); 301} 302 303/** 304 * Process a channel event notification 305 */ 306static void 307VmbusProcessChannelEvent(uint32_t relid) 308{ 309 hv_vmbus_channel* channel; 310 311 /** 312 * Find the channel based on this relid and invokes 313 * the channel callback to process the event 314 */ 315 316 channel = hv_vmbus_get_channel_from_rel_id(relid); 317 318 if (channel == NULL) { 319 return; 320 } 321 /** 322 * To deal with the race condition where we might 323 * receive a packet while the relevant driver is 324 * being unloaded, dispatch the callback while 325 * holding the channel lock. The unloading driver 326 * will acquire the same channel lock to set the 327 * callback to NULL. This closes the window. 328 */ 329 330 mtx_lock(&channel->inbound_lock); 331 if (channel->on_channel_callback != NULL) { 332 channel->on_channel_callback(channel->channel_callback_context); 333 } 334 mtx_unlock(&channel->inbound_lock); 335} 336 337/** 338 * Handler for events 339 */ 340void 341hv_vmbus_on_events(void *arg) 342{ 343 int dword; 344 int bit; 345 int rel_id; 346 int maxdword = HV_MAX_NUM_CHANNELS_SUPPORTED >> 5; 347 /* int maxdword = PAGE_SIZE >> 3; */ 348 349 /* 350 * receive size is 1/2 page and divide that by 4 bytes 351 */ 352 353 uint32_t* recv_interrupt_page = 354 hv_vmbus_g_connection.recv_interrupt_page; 355 356 /* 357 * Check events 358 */ 359 if (recv_interrupt_page != NULL) { 360 for (dword = 0; dword < maxdword; dword++) { 361 if (recv_interrupt_page[dword]) { 362 for (bit = 0; bit < 32; bit++) { 363 if (synch_test_and_clear_bit(bit, 364 (uint32_t *) &recv_interrupt_page[dword])) { 365 rel_id = (dword << 5) + bit; 366 if (rel_id == 0) { 367 /* 368 * Special case - 369 * vmbus channel protocol msg. 370 */ 371 continue; 372 } else { 373 VmbusProcessChannelEvent(rel_id); 374 375 } 376 } 377 } 378 } 379 } 380 } 381 382 return; 383} 384 385/** 386 * Send a msg on the vmbus's message connection 387 */ 388int hv_vmbus_post_message(void *buffer, size_t bufferLen) { 389 int ret = 0; 390 hv_vmbus_connection_id connId; 391 unsigned retries = 0; 392 393 /* NetScaler delays from previous code were consolidated here */ 394 static int delayAmount[] = {100, 100, 100, 500, 500, 5000, 5000, 5000}; 395 396 /* for(each entry in delayAmount) try to post message, 397 * delay a little bit before retrying 398 */ 399 for (retries = 0; 400 retries < sizeof(delayAmount)/sizeof(delayAmount[0]); retries++) { 401 connId.as_uint32_t = 0; 402 connId.u.id = HV_VMBUS_MESSAGE_CONNECTION_ID; 403 ret = hv_vmbus_post_msg_via_msg_ipc(connId, 1, buffer, bufferLen); 404 if (ret != HV_STATUS_INSUFFICIENT_BUFFERS) 405 break; 406 /* TODO: KYS We should use a blocking wait call */ 407 DELAY(delayAmount[retries]); 408 } 409 410 KASSERT(ret == 0, ("Error VMBUS: Message Post Failed\n")); 411 412 return (ret); 413} 414 415/** 416 * Send an event notification to the parent 417 */ 418int 419hv_vmbus_set_event(uint32_t child_rel_id) { 420 int ret = 0; 421 422 /* Each uint32_t represents 32 channels */ 423 424 synch_set_bit(child_rel_id & 31, 425 (((uint32_t *)hv_vmbus_g_connection.send_interrupt_page 426 + (child_rel_id >> 5)))); 427 ret = hv_vmbus_signal_event(); 428 429 return (ret); 430} 431 432