hv_channel_mgmt.c revision 250199
1219820Sjeff/*- 2219820Sjeff * Copyright (c) 2009-2012 Microsoft Corp. 3219820Sjeff * Copyright (c) 2012 NetApp Inc. 4219820Sjeff * Copyright (c) 2012 Citrix Inc. 5219820Sjeff * All rights reserved. 6219820Sjeff * 7219820Sjeff * Redistribution and use in source and binary forms, with or without 8219820Sjeff * modification, are permitted provided that the following conditions 9219820Sjeff * are met: 10219820Sjeff * 1. Redistributions of source code must retain the above copyright 11219820Sjeff * notice unmodified, this list of conditions, and the following 12219820Sjeff * disclaimer. 13219820Sjeff * 2. Redistributions in binary form must reproduce the above copyright 14219820Sjeff * notice, this list of conditions and the following disclaimer in the 15219820Sjeff * documentation and/or other materials provided with the distribution. 16219820Sjeff * 17219820Sjeff * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR 18219820Sjeff * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES 19219820Sjeff * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. 20219820Sjeff * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, 21219820Sjeff * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT 22219820Sjeff * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 23219820Sjeff * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 24219820Sjeff * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 25219820Sjeff * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF 26219820Sjeff * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 27219820Sjeff */ 28219820Sjeff 29219820Sjeff#include <sys/param.h> 30219820Sjeff#include <sys/mbuf.h> 31219820Sjeff 32219820Sjeff#include "hv_vmbus_priv.h" 33219820Sjeff 34219820Sjefftypedef void (*hv_pfn_channel_msg_handler)(hv_vmbus_channel_msg_header* msg); 35219820Sjeff 36219820Sjefftypedef struct hv_vmbus_channel_msg_table_entry { 37219820Sjeff hv_vmbus_channel_msg_type messageType; 38219820Sjeff hv_pfn_channel_msg_handler messageHandler; 39219820Sjeff} hv_vmbus_channel_msg_table_entry; 40219820Sjeff 41219820Sjeff/* 42219820Sjeff * Internal functions 43219820Sjeff */ 44219820Sjeff 45219820Sjeffstatic void vmbus_channel_on_offer(hv_vmbus_channel_msg_header* hdr); 46219820Sjeffstatic void vmbus_channel_on_open_result(hv_vmbus_channel_msg_header* hdr); 47219820Sjeffstatic void vmbus_channel_on_offer_rescind(hv_vmbus_channel_msg_header* hdr); 48219820Sjeffstatic void vmbus_channel_on_gpadl_created(hv_vmbus_channel_msg_header* hdr); 49219820Sjeffstatic void vmbus_channel_on_gpadl_torndown(hv_vmbus_channel_msg_header* hdr); 50219820Sjeffstatic void vmbus_channel_on_offers_delivered(hv_vmbus_channel_msg_header* hdr); 51219820Sjeffstatic void vmbus_channel_on_version_response(hv_vmbus_channel_msg_header* hdr); 52219820Sjeffstatic void vmbus_channel_process_offer(void *context); 53219820Sjeff 54219820Sjeff/** 55219820Sjeff * Channel message dispatch table 56219820Sjeff */ 57219820Sjeffhv_vmbus_channel_msg_table_entry 58219820Sjeff g_channel_message_table[HV_CHANNEL_MESSAGE_COUNT] = { 59219820Sjeff { HV_CHANNEL_MESSAGE_INVALID, NULL }, 60219820Sjeff { HV_CHANNEL_MESSAGE_OFFER_CHANNEL, vmbus_channel_on_offer }, 61219820Sjeff { HV_CHANNEL_MESSAGE_RESCIND_CHANNEL_OFFER, 62219820Sjeff vmbus_channel_on_offer_rescind }, 63219820Sjeff { HV_CHANNEL_MESSAGE_REQUEST_OFFERS, NULL }, 64219820Sjeff { HV_CHANNEL_MESSAGE_ALL_OFFERS_DELIVERED, 65219820Sjeff vmbus_channel_on_offers_delivered }, 66219820Sjeff { HV_CHANNEL_MESSAGE_OPEN_CHANNEL, NULL }, 67219820Sjeff { HV_CHANNEL_MESSAGE_OPEN_CHANNEL_RESULT, 68219820Sjeff vmbus_channel_on_open_result }, 69219820Sjeff { HV_CHANNEL_MESSAGE_CLOSE_CHANNEL, NULL }, 70219820Sjeff { HV_CHANNEL_MESSAGEL_GPADL_HEADER, NULL }, 71219820Sjeff { HV_CHANNEL_MESSAGE_GPADL_BODY, NULL }, 72219820Sjeff { HV_CHANNEL_MESSAGE_GPADL_CREATED, 73219820Sjeff vmbus_channel_on_gpadl_created }, 74219820Sjeff { HV_CHANNEL_MESSAGE_GPADL_TEARDOWN, NULL }, 75219820Sjeff { HV_CHANNEL_MESSAGE_GPADL_TORNDOWN, 76219820Sjeff vmbus_channel_on_gpadl_torndown }, 77219820Sjeff { HV_CHANNEL_MESSAGE_REL_ID_RELEASED, NULL }, 78219820Sjeff { HV_CHANNEL_MESSAGE_INITIATED_CONTACT, NULL }, 79219820Sjeff { HV_CHANNEL_MESSAGE_VERSION_RESPONSE, 80219820Sjeff vmbus_channel_on_version_response }, 81219820Sjeff { HV_CHANNEL_MESSAGE_UNLOAD, NULL } 82219820Sjeff}; 83219820Sjeff 84219820Sjeff 85219820Sjeff/** 86219820Sjeff * Implementation of the work abstraction. 87219820Sjeff */ 88219820Sjeffstatic void 89219820Sjeffwork_item_callback(void *work, int pending) 90219820Sjeff{ 91219820Sjeff struct hv_work_item *w = (struct hv_work_item *)work; 92219820Sjeff 93219820Sjeff /* 94219820Sjeff * Serialize work execution. 95219820Sjeff */ 96219820Sjeff if (w->wq->work_sema != NULL) { 97219820Sjeff sema_wait(w->wq->work_sema); 98219820Sjeff } 99219820Sjeff 100219820Sjeff w->callback(w->context); 101219820Sjeff 102219820Sjeff if (w->wq->work_sema != NULL) { 103219820Sjeff sema_post(w->wq->work_sema); 104219820Sjeff } 105219820Sjeff 106219820Sjeff free(w, M_DEVBUF); 107219820Sjeff} 108219820Sjeff 109219820Sjeffstruct hv_work_queue* 110219820Sjeffhv_work_queue_create(char* name) 111219820Sjeff{ 112219820Sjeff static unsigned int qid = 0; 113219820Sjeff char qname[64]; 114219820Sjeff int pri; 115219820Sjeff struct hv_work_queue* wq; 116219820Sjeff 117219820Sjeff wq = malloc(sizeof(struct hv_work_queue), M_DEVBUF, M_NOWAIT | M_ZERO); 118219820Sjeff KASSERT(wq != NULL, ("Error VMBUS: Failed to allocate work_queue\n")); 119219820Sjeff if (wq == NULL) 120219820Sjeff return (NULL); 121219820Sjeff 122219820Sjeff /* 123219820Sjeff * We use work abstraction to handle messages 124219820Sjeff * coming from the host and these are typically offers. 125219820Sjeff * Some FreeBsd drivers appear to have a concurrency issue 126219820Sjeff * where probe/attach needs to be serialized. We ensure that 127219820Sjeff * by having only one thread process work elements in a 128219820Sjeff * specific queue by serializing work execution. 129219820Sjeff * 130219820Sjeff */ 131219820Sjeff if (strcmp(name, "vmbusQ") == 0) { 132219820Sjeff pri = PI_DISK; 133219820Sjeff } else { /* control */ 134219820Sjeff pri = PI_NET; 135219820Sjeff /* 136219820Sjeff * Initialize semaphore for this queue by pointing 137219820Sjeff * to the globale semaphore used for synchronizing all 138219820Sjeff * control messages. 139219820Sjeff */ 140219820Sjeff wq->work_sema = &hv_vmbus_g_connection.control_sema; 141219820Sjeff } 142219820Sjeff 143219820Sjeff sprintf(qname, "hv_%s_%u", name, qid); 144219820Sjeff 145219820Sjeff /* 146219820Sjeff * Fixme: FreeBSD 8.2 has a different prototype for 147219820Sjeff * taskqueue_create(), and for certain other taskqueue functions. 148219820Sjeff * We need to research the implications of these changes. 149219820Sjeff * Fixme: Not sure when the changes were introduced. 150219820Sjeff */ 151219820Sjeff wq->queue = taskqueue_create(qname, M_NOWAIT, taskqueue_thread_enqueue, 152219820Sjeff &wq->queue 153219820Sjeff #if __FreeBSD_version < 800000 154219820Sjeff , &wq->proc 155219820Sjeff #endif 156219820Sjeff ); 157219820Sjeff 158219820Sjeff if (wq->queue == NULL) { 159219820Sjeff free(wq, M_DEVBUF); 160219820Sjeff return (NULL); 161219820Sjeff } 162219820Sjeff 163219820Sjeff if (taskqueue_start_threads(&wq->queue, 1, pri, "%s taskq", qname)) { 164219820Sjeff taskqueue_free(wq->queue); 165219820Sjeff free(wq, M_DEVBUF); 166219820Sjeff return (NULL); 167219820Sjeff } 168219820Sjeff 169219820Sjeff qid++; 170219820Sjeff 171219820Sjeff return (wq); 172219820Sjeff} 173219820Sjeff 174219820Sjeffvoid 175219820Sjeffhv_work_queue_close(struct hv_work_queue *wq) 176219820Sjeff{ 177219820Sjeff /* 178219820Sjeff * KYS: Need to drain the taskqueue 179219820Sjeff * before we close the hv_work_queue. 180219820Sjeff */ 181219820Sjeff /*KYS: taskqueue_drain(wq->tq, ); */ 182219820Sjeff taskqueue_free(wq->queue); 183219820Sjeff free(wq, M_DEVBUF); 184219820Sjeff} 185219820Sjeff 186219820Sjeff/** 187219820Sjeff * @brief Create work item 188219820Sjeff */ 189219820Sjeffint 190219820Sjeffhv_queue_work_item( 191219820Sjeff struct hv_work_queue *wq, 192219820Sjeff void (*callback)(void *), void *context) 193219820Sjeff{ 194219820Sjeff struct hv_work_item *w = malloc(sizeof(struct hv_work_item), 195219820Sjeff M_DEVBUF, M_NOWAIT | M_ZERO); 196219820Sjeff KASSERT(w != NULL, ("Error VMBUS: Failed to allocate WorkItem\n")); 197219820Sjeff if (w == NULL) 198219820Sjeff return (ENOMEM); 199219820Sjeff 200219820Sjeff w->callback = callback; 201219820Sjeff w->context = context; 202219820Sjeff w->wq = wq; 203219820Sjeff 204219820Sjeff TASK_INIT(&w->work, 0, work_item_callback, w); 205219820Sjeff 206219820Sjeff return (taskqueue_enqueue(wq->queue, &w->work)); 207219820Sjeff} 208219820Sjeff 209219820Sjeff/** 210219820Sjeff * @brief Rescind the offer by initiating a device removal 211219820Sjeff */ 212219820Sjeffstatic void 213219820Sjeffvmbus_channel_process_rescind_offer(void *context) 214219820Sjeff{ 215219820Sjeff hv_vmbus_channel* channel = (hv_vmbus_channel*) context; 216219820Sjeff hv_vmbus_child_device_unregister(channel->device); 217219820Sjeff} 218219820Sjeff 219219820Sjeff/** 220219820Sjeff * @brief Allocate and initialize a vmbus channel object 221219820Sjeff */ 222219820Sjeffhv_vmbus_channel* 223219820Sjeffhv_vmbus_allocate_channel(void) 224219820Sjeff{ 225219820Sjeff hv_vmbus_channel* channel; 226219820Sjeff 227219820Sjeff channel = (hv_vmbus_channel*) malloc( 228219820Sjeff sizeof(hv_vmbus_channel), 229219820Sjeff M_DEVBUF, 230219820Sjeff M_NOWAIT | M_ZERO); 231219820Sjeff KASSERT(channel != NULL, ("Error VMBUS: Failed to allocate channel!")); 232219820Sjeff if (channel == NULL) 233219820Sjeff return (NULL); 234219820Sjeff 235219820Sjeff mtx_init(&channel->inbound_lock, "channel inbound", NULL, MTX_DEF); 236219820Sjeff 237219820Sjeff channel->control_work_queue = hv_work_queue_create("control"); 238219820Sjeff 239219820Sjeff if (channel->control_work_queue == NULL) { 240219820Sjeff mtx_destroy(&channel->inbound_lock); 241219820Sjeff free(channel, M_DEVBUF); 242219820Sjeff return (NULL); 243219820Sjeff } 244219820Sjeff 245219820Sjeff return (channel); 246219820Sjeff} 247219820Sjeff 248219820Sjeff/** 249219820Sjeff * @brief Release the vmbus channel object itself 250219820Sjeff */ 251219820Sjeffstatic inline void 252219820SjeffReleaseVmbusChannel(void *context) 253219820Sjeff{ 254219820Sjeff hv_vmbus_channel* channel = (hv_vmbus_channel*) context; 255219820Sjeff hv_work_queue_close(channel->control_work_queue); 256219820Sjeff free(channel, M_DEVBUF); 257219820Sjeff} 258219820Sjeff 259219820Sjeff/** 260219820Sjeff * @brief Release the resources used by the vmbus channel object 261219820Sjeff */ 262219820Sjeffvoid 263219820Sjeffhv_vmbus_free_vmbus_channel(hv_vmbus_channel* channel) 264219820Sjeff{ 265219820Sjeff mtx_destroy(&channel->inbound_lock); 266219820Sjeff /* 267219820Sjeff * We have to release the channel's workqueue/thread in 268219820Sjeff * the vmbus's workqueue/thread context 269219820Sjeff * ie we can't destroy ourselves 270219820Sjeff */ 271219820Sjeff hv_queue_work_item(hv_vmbus_g_connection.work_queue, 272219820Sjeff ReleaseVmbusChannel, (void *) channel); 273219820Sjeff} 274219820Sjeff 275219820Sjeff/** 276219820Sjeff * @brief Process the offer by creating a channel/device 277219820Sjeff * associated with this offer 278219820Sjeff */ 279219820Sjeffstatic void 280219820Sjeffvmbus_channel_process_offer(void *context) 281219820Sjeff{ 282219820Sjeff int ret; 283219820Sjeff hv_vmbus_channel* new_channel; 284219820Sjeff boolean_t f_new; 285219820Sjeff hv_vmbus_channel* channel; 286219820Sjeff 287219820Sjeff new_channel = (hv_vmbus_channel*) context; 288219820Sjeff f_new = TRUE; 289219820Sjeff channel = NULL; 290 291 /* 292 * Make sure this is a new offer 293 */ 294 mtx_lock_spin(&hv_vmbus_g_connection.channel_lock); 295 296 TAILQ_FOREACH(channel, &hv_vmbus_g_connection.channel_anchor, 297 list_entry) 298 { 299 if (!memcmp( 300 &channel->offer_msg.offer.interface_type, 301 &new_channel->offer_msg.offer.interface_type, 302 sizeof(hv_guid)) 303 && !memcmp( 304 &channel->offer_msg.offer.interface_instance, 305 &new_channel->offer_msg.offer.interface_instance, 306 sizeof(hv_guid))) { 307 f_new = FALSE; 308 break; 309 } 310 } 311 312 if (f_new) { 313 /* Insert at tail */ 314 TAILQ_INSERT_TAIL( 315 &hv_vmbus_g_connection.channel_anchor, 316 new_channel, 317 list_entry); 318 } 319 mtx_unlock_spin(&hv_vmbus_g_connection.channel_lock); 320 321 if (!f_new) { 322 hv_vmbus_free_vmbus_channel(new_channel); 323 return; 324 } 325 326 /* 327 * Start the process of binding this offer to the driver 328 * (We need to set the device field before calling 329 * hv_vmbus_child_device_add()) 330 */ 331 new_channel->device = hv_vmbus_child_device_create( 332 new_channel->offer_msg.offer.interface_type, 333 new_channel->offer_msg.offer.interface_instance, new_channel); 334 335 /* 336 * TODO - the HV_CHANNEL_OPEN_STATE flag should not be set below 337 * but in the "open" channel request. The ret != 0 logic below 338 * doesn't take into account that a channel 339 * may have been opened successfully 340 */ 341 342 /* 343 * Add the new device to the bus. This will kick off device-driver 344 * binding which eventually invokes the device driver's AddDevice() 345 * method. 346 */ 347 ret = hv_vmbus_child_device_register(new_channel->device); 348 if (ret != 0) { 349 mtx_lock_spin(&hv_vmbus_g_connection.channel_lock); 350 TAILQ_REMOVE( 351 &hv_vmbus_g_connection.channel_anchor, 352 new_channel, 353 list_entry); 354 mtx_unlock_spin(&hv_vmbus_g_connection.channel_lock); 355 hv_vmbus_free_vmbus_channel(new_channel); 356 } else { 357 /* 358 * This state is used to indicate a successful open 359 * so that when we do close the channel normally, 360 * we can clean up properly 361 */ 362 new_channel->state = HV_CHANNEL_OPEN_STATE; 363 364 } 365} 366 367/** 368 * @brief Handler for channel offers from Hyper-V/Azure 369 * 370 * Handler for channel offers from vmbus in parent partition. We ignore 371 * all offers except network and storage offers. For each network and storage 372 * offers, we create a channel object and queue a work item to the channel 373 * object to process the offer synchronously 374 */ 375static void 376vmbus_channel_on_offer(hv_vmbus_channel_msg_header* hdr) 377{ 378 hv_vmbus_channel_offer_channel* offer; 379 hv_vmbus_channel* new_channel; 380 381 offer = (hv_vmbus_channel_offer_channel*) hdr; 382 383 hv_guid *guidType; 384 hv_guid *guidInstance; 385 386 guidType = &offer->offer.interface_type; 387 guidInstance = &offer->offer.interface_instance; 388 389 /* Allocate the channel object and save this offer */ 390 new_channel = hv_vmbus_allocate_channel(); 391 if (new_channel == NULL) 392 return; 393 394 memcpy(&new_channel->offer_msg, offer, 395 sizeof(hv_vmbus_channel_offer_channel)); 396 new_channel->monitor_group = (uint8_t) offer->monitor_id / 32; 397 new_channel->monitor_bit = (uint8_t) offer->monitor_id % 32; 398 399 /* TODO: Make sure the offer comes from our parent partition */ 400 hv_queue_work_item( 401 new_channel->control_work_queue, 402 vmbus_channel_process_offer, 403 new_channel); 404} 405 406/** 407 * @brief Rescind offer handler. 408 * 409 * We queue a work item to process this offer 410 * synchronously 411 */ 412static void 413vmbus_channel_on_offer_rescind(hv_vmbus_channel_msg_header* hdr) 414{ 415 hv_vmbus_channel_rescind_offer* rescind; 416 hv_vmbus_channel* channel; 417 418 rescind = (hv_vmbus_channel_rescind_offer*) hdr; 419 420 channel = hv_vmbus_get_channel_from_rel_id(rescind->child_rel_id); 421 if (channel == NULL) 422 return; 423 424 hv_queue_work_item(channel->control_work_queue, 425 vmbus_channel_process_rescind_offer, channel); 426} 427 428/** 429 * 430 * @brief Invoked when all offers have been delivered. 431 */ 432static void 433vmbus_channel_on_offers_delivered(hv_vmbus_channel_msg_header* hdr) 434{ 435} 436 437/** 438 * @brief Open result handler. 439 * 440 * This is invoked when we received a response 441 * to our channel open request. Find the matching request, copy the 442 * response and signal the requesting thread. 443 */ 444static void 445vmbus_channel_on_open_result(hv_vmbus_channel_msg_header* hdr) 446{ 447 hv_vmbus_channel_open_result* result; 448 hv_vmbus_channel_msg_info* msg_info; 449 hv_vmbus_channel_msg_header* requestHeader; 450 hv_vmbus_channel_open_channel* openMsg; 451 452 result = (hv_vmbus_channel_open_result*) hdr; 453 454 /* 455 * Find the open msg, copy the result and signal/unblock the wait event 456 */ 457 mtx_lock_spin(&hv_vmbus_g_connection.channel_msg_lock); 458 459 TAILQ_FOREACH(msg_info, &hv_vmbus_g_connection.channel_msg_anchor, 460 msg_list_entry) { 461 requestHeader = (hv_vmbus_channel_msg_header*) msg_info->msg; 462 463 if (requestHeader->message_type == 464 HV_CHANNEL_MESSAGE_OPEN_CHANNEL) { 465 openMsg = (hv_vmbus_channel_open_channel*) msg_info->msg; 466 if (openMsg->child_rel_id == result->child_rel_id 467 && openMsg->open_id == result->open_id) { 468 memcpy(&msg_info->response.open_result, result, 469 sizeof(hv_vmbus_channel_open_result)); 470 sema_post(&msg_info->wait_sema); 471 break; 472 } 473 } 474 } 475 mtx_unlock_spin(&hv_vmbus_g_connection.channel_msg_lock); 476 477} 478 479/** 480 * @brief GPADL created handler. 481 * 482 * This is invoked when we received a response 483 * to our gpadl create request. Find the matching request, copy the 484 * response and signal the requesting thread. 485 */ 486static void 487vmbus_channel_on_gpadl_created(hv_vmbus_channel_msg_header* hdr) 488{ 489 hv_vmbus_channel_gpadl_created* gpadl_created; 490 hv_vmbus_channel_msg_info* msg_info; 491 hv_vmbus_channel_msg_header* request_header; 492 hv_vmbus_channel_gpadl_header* gpadl_header; 493 494 gpadl_created = (hv_vmbus_channel_gpadl_created*) hdr; 495 496 /* Find the establish msg, copy the result and signal/unblock 497 * the wait event 498 */ 499 mtx_lock_spin(&hv_vmbus_g_connection.channel_msg_lock); 500 TAILQ_FOREACH(msg_info, &hv_vmbus_g_connection.channel_msg_anchor, 501 msg_list_entry) { 502 request_header = (hv_vmbus_channel_msg_header*) msg_info->msg; 503 if (request_header->message_type == 504 HV_CHANNEL_MESSAGEL_GPADL_HEADER) { 505 gpadl_header = 506 (hv_vmbus_channel_gpadl_header*) request_header; 507 508 if ((gpadl_created->child_rel_id == gpadl_header->child_rel_id) 509 && (gpadl_created->gpadl == gpadl_header->gpadl)) { 510 memcpy(&msg_info->response.gpadl_created, 511 gpadl_created, 512 sizeof(hv_vmbus_channel_gpadl_created)); 513 sema_post(&msg_info->wait_sema); 514 break; 515 } 516 } 517 } 518 mtx_unlock_spin(&hv_vmbus_g_connection.channel_msg_lock); 519} 520 521/** 522 * @brief GPADL torndown handler. 523 * 524 * This is invoked when we received a respons 525 * to our gpadl teardown request. Find the matching request, copy the 526 * response and signal the requesting thread 527 */ 528static void 529vmbus_channel_on_gpadl_torndown(hv_vmbus_channel_msg_header* hdr) 530{ 531 hv_vmbus_channel_gpadl_torndown* gpadl_torndown; 532 hv_vmbus_channel_msg_info* msg_info; 533 hv_vmbus_channel_msg_header* requestHeader; 534 hv_vmbus_channel_gpadl_teardown* gpadlTeardown; 535 536 gpadl_torndown = (hv_vmbus_channel_gpadl_torndown*)hdr; 537 538 /* 539 * Find the open msg, copy the result and signal/unblock the 540 * wait event. 541 */ 542 543 mtx_lock_spin(&hv_vmbus_g_connection.channel_msg_lock); 544 545 TAILQ_FOREACH(msg_info, &hv_vmbus_g_connection.channel_msg_anchor, 546 msg_list_entry) { 547 requestHeader = (hv_vmbus_channel_msg_header*) msg_info->msg; 548 549 if (requestHeader->message_type 550 == HV_CHANNEL_MESSAGE_GPADL_TEARDOWN) { 551 gpadlTeardown = 552 (hv_vmbus_channel_gpadl_teardown*) requestHeader; 553 554 if (gpadl_torndown->gpadl == gpadlTeardown->gpadl) { 555 memcpy(&msg_info->response.gpadl_torndown, 556 gpadl_torndown, 557 sizeof(hv_vmbus_channel_gpadl_torndown)); 558 sema_post(&msg_info->wait_sema); 559 break; 560 } 561 } 562 } 563 mtx_unlock_spin(&hv_vmbus_g_connection.channel_msg_lock); 564} 565 566/** 567 * @brief Version response handler. 568 * 569 * This is invoked when we received a response 570 * to our initiate contact request. Find the matching request, copy th 571 * response and signal the requesting thread. 572 */ 573static void 574vmbus_channel_on_version_response(hv_vmbus_channel_msg_header* hdr) 575{ 576 hv_vmbus_channel_msg_info* msg_info; 577 hv_vmbus_channel_msg_header* requestHeader; 578 hv_vmbus_channel_initiate_contact* initiate; 579 hv_vmbus_channel_version_response* versionResponse; 580 581 versionResponse = (hv_vmbus_channel_version_response*)hdr; 582 583 mtx_lock_spin(&hv_vmbus_g_connection.channel_msg_lock); 584 TAILQ_FOREACH(msg_info, &hv_vmbus_g_connection.channel_msg_anchor, 585 msg_list_entry) { 586 requestHeader = (hv_vmbus_channel_msg_header*) msg_info->msg; 587 if (requestHeader->message_type 588 == HV_CHANNEL_MESSAGE_INITIATED_CONTACT) { 589 initiate = 590 (hv_vmbus_channel_initiate_contact*) requestHeader; 591 memcpy(&msg_info->response.version_response, 592 versionResponse, 593 sizeof(hv_vmbus_channel_version_response)); 594 sema_post(&msg_info->wait_sema); 595 } 596 } 597 mtx_unlock_spin(&hv_vmbus_g_connection.channel_msg_lock); 598 599} 600 601/** 602 * @brief Handler for channel protocol messages. 603 * 604 * This is invoked in the vmbus worker thread context. 605 */ 606void 607hv_vmbus_on_channel_message(void *context) 608{ 609 hv_vmbus_message* msg; 610 hv_vmbus_channel_msg_header* hdr; 611 int size; 612 613 msg = (hv_vmbus_message*) context; 614 hdr = (hv_vmbus_channel_msg_header*) msg->u.payload; 615 size = msg->header.payload_size; 616 617 if (hdr->message_type >= HV_CHANNEL_MESSAGE_COUNT) { 618 free(msg, M_DEVBUF); 619 return; 620 } 621 622 if (g_channel_message_table[hdr->message_type].messageHandler) { 623 g_channel_message_table[hdr->message_type].messageHandler(hdr); 624 } 625 626 /* Free the msg that was allocated in VmbusOnMsgDPC() */ 627 free(msg, M_DEVBUF); 628} 629 630/** 631 * @brief Send a request to get all our pending offers. 632 */ 633int 634hv_vmbus_request_channel_offers(void) 635{ 636 int ret; 637 hv_vmbus_channel_msg_header* msg; 638 hv_vmbus_channel_msg_info* msg_info; 639 640 msg_info = (hv_vmbus_channel_msg_info *) 641 malloc(sizeof(hv_vmbus_channel_msg_info) 642 + sizeof(hv_vmbus_channel_msg_header), M_DEVBUF, M_NOWAIT); 643 644 if (msg_info == NULL) { 645 if(bootverbose) 646 printf("Error VMBUS: malloc failed for Request Offers\n"); 647 return (ENOMEM); 648 } 649 650 msg = (hv_vmbus_channel_msg_header*) msg_info->msg; 651 msg->message_type = HV_CHANNEL_MESSAGE_REQUEST_OFFERS; 652 653 ret = hv_vmbus_post_message(msg, sizeof(hv_vmbus_channel_msg_header)); 654 655 if (msg_info) 656 free(msg_info, M_DEVBUF); 657 658 return (ret); 659} 660 661/** 662 * @brief Release channels that are unattached/unconnected (i.e., no drivers associated) 663 */ 664void 665hv_vmbus_release_unattached_channels(void) 666{ 667 hv_vmbus_channel *channel; 668 669 mtx_lock_spin(&hv_vmbus_g_connection.channel_lock); 670 671 while (!TAILQ_EMPTY(&hv_vmbus_g_connection.channel_anchor)) { 672 channel = TAILQ_FIRST(&hv_vmbus_g_connection.channel_anchor); 673 TAILQ_REMOVE(&hv_vmbus_g_connection.channel_anchor, 674 channel, list_entry); 675 676 hv_vmbus_child_device_unregister(channel->device); 677 hv_vmbus_free_vmbus_channel(channel); 678 } 679 mtx_unlock_spin(&hv_vmbus_g_connection.channel_lock); 680} 681