1/*- 2 * Copyright (c) 2009-2012,2016 Microsoft Corp. 3 * Copyright (c) 2012 NetApp Inc. 4 * Copyright (c) 2012 Citrix Inc. 5 * All rights reserved. 6 * 7 * Redistribution and use in source and binary forms, with or without 8 * modification, are permitted provided that the following conditions 9 * are met: 10 * 1. Redistributions of source code must retain the above copyright 11 * notice unmodified, this list of conditions, and the following 12 * disclaimer. 13 * 2. Redistributions in binary form must reproduce the above copyright 14 * notice, this list of conditions and the following disclaimer in the 15 * documentation and/or other materials provided with the distribution. 16 * 17 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR 18 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES 19 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. 20 * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, 21 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT 22 * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 23 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 24 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 25 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF 26 * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 27 */ 28 29#include <sys/param.h> 30#include <sys/kernel.h> 31#include <sys/lock.h> 32#include <sys/mbuf.h> 33#include <sys/mutex.h> 34 35#include <dev/hyperv/vmbus/hv_vmbus_priv.h> 36#include <dev/hyperv/vmbus/vmbus_reg.h> 37#include <dev/hyperv/vmbus/vmbus_var.h> 38 39/* 40 * Internal functions 41 */ 42 43typedef void (*vmbus_msg_handler)(const hv_vmbus_channel_msg_header *msg); 44 45typedef struct hv_vmbus_channel_msg_table_entry { 46 hv_vmbus_channel_msg_type messageType; 47 vmbus_msg_handler messageHandler; 48} hv_vmbus_channel_msg_table_entry; 49 50static void vmbus_channel_on_offer_internal(void *context); 51static void vmbus_channel_on_offer_rescind_internal(void *context); 52 53static void vmbus_channel_on_offer(const hv_vmbus_channel_msg_header *hdr); 54static void vmbus_channel_on_open_result( 55 const hv_vmbus_channel_msg_header *hdr); 56static void vmbus_channel_on_offer_rescind( 57 const hv_vmbus_channel_msg_header *hdr); 58static void vmbus_channel_on_gpadl_created( 59 const hv_vmbus_channel_msg_header *hdr); 60static void vmbus_channel_on_gpadl_torndown( 61 const hv_vmbus_channel_msg_header *hdr); 62static void vmbus_channel_on_offers_delivered( 63 const hv_vmbus_channel_msg_header *hdr); 64static void vmbus_channel_on_version_response( 65 const hv_vmbus_channel_msg_header *hdr); 66 67/** 68 * Channel message dispatch table 69 */ 70static const hv_vmbus_channel_msg_table_entry 71 g_channel_message_table[HV_CHANNEL_MESSAGE_COUNT] = { 72 { HV_CHANNEL_MESSAGE_INVALID, 73 NULL }, 74 { HV_CHANNEL_MESSAGE_OFFER_CHANNEL, 75 vmbus_channel_on_offer }, 76 { HV_CHANNEL_MESSAGE_RESCIND_CHANNEL_OFFER, 77 vmbus_channel_on_offer_rescind }, 78 { HV_CHANNEL_MESSAGE_REQUEST_OFFERS, 79 NULL }, 80 { HV_CHANNEL_MESSAGE_ALL_OFFERS_DELIVERED, 81 vmbus_channel_on_offers_delivered }, 82 { HV_CHANNEL_MESSAGE_OPEN_CHANNEL, 83 NULL }, 84 { HV_CHANNEL_MESSAGE_OPEN_CHANNEL_RESULT, 85 vmbus_channel_on_open_result }, 86 { HV_CHANNEL_MESSAGE_CLOSE_CHANNEL, 87 NULL }, 88 { HV_CHANNEL_MESSAGEL_GPADL_HEADER, 89 NULL }, 90 { HV_CHANNEL_MESSAGE_GPADL_BODY, 91 NULL }, 92 { HV_CHANNEL_MESSAGE_GPADL_CREATED, 93 vmbus_channel_on_gpadl_created }, 94 { HV_CHANNEL_MESSAGE_GPADL_TEARDOWN, 95 NULL }, 96 { HV_CHANNEL_MESSAGE_GPADL_TORNDOWN, 97 vmbus_channel_on_gpadl_torndown }, 98 { HV_CHANNEL_MESSAGE_REL_ID_RELEASED, 99 NULL }, 100 { HV_CHANNEL_MESSAGE_INITIATED_CONTACT, 101 NULL }, 102 { HV_CHANNEL_MESSAGE_VERSION_RESPONSE, 103 vmbus_channel_on_version_response }, 104 { HV_CHANNEL_MESSAGE_UNLOAD, 105 NULL } 106}; 107 108typedef struct hv_work_item { 109 struct task work; 110 void (*callback)(void *); 111 void* context; 112} hv_work_item; 113 114static struct mtx vmbus_chwait_lock; 115MTX_SYSINIT(vmbus_chwait_lk, &vmbus_chwait_lock, "vmbus primarych wait lock", 116 MTX_DEF); 117static uint32_t vmbus_chancnt; 118static uint32_t vmbus_devcnt; 119 120#define VMBUS_CHANCNT_DONE 0x80000000 121 122/** 123 * Implementation of the work abstraction. 124 */ 125static void 126work_item_callback(void *work, int pending) 127{ 128 struct hv_work_item *w = (struct hv_work_item *)work; 129 130 w->callback(w->context); 131 132 free(w, M_DEVBUF); 133} 134 135/** 136 * @brief Create work item 137 */ 138static int 139hv_queue_work_item( 140 void (*callback)(void *), void *context) 141{ 142 struct hv_work_item *w = malloc(sizeof(struct hv_work_item), 143 M_DEVBUF, M_NOWAIT); 144 KASSERT(w != NULL, ("Error VMBUS: Failed to allocate WorkItem\n")); 145 if (w == NULL) 146 return (ENOMEM); 147 148 w->callback = callback; 149 w->context = context; 150 151 TASK_INIT(&w->work, 0, work_item_callback, w); 152 153 return (taskqueue_enqueue(taskqueue_thread, &w->work)); 154} 155 156 157/** 158 * @brief Allocate and initialize a vmbus channel object 159 */ 160hv_vmbus_channel* 161hv_vmbus_allocate_channel(void) 162{ 163 hv_vmbus_channel* channel; 164 165 channel = (hv_vmbus_channel*) malloc( 166 sizeof(hv_vmbus_channel), 167 M_DEVBUF, 168 M_WAITOK | M_ZERO); 169 170 mtx_init(&channel->sc_lock, "vmbus multi channel", NULL, MTX_DEF); 171 TAILQ_INIT(&channel->sc_list_anchor); 172 173 return (channel); 174} 175 176/** 177 * @brief Release the resources used by the vmbus channel object 178 */ 179void 180hv_vmbus_free_vmbus_channel(hv_vmbus_channel* channel) 181{ 182 mtx_destroy(&channel->sc_lock); 183 free(channel, M_DEVBUF); 184} 185 186/** 187 * @brief Process the offer by creating a channel/device 188 * associated with this offer 189 */ 190static void 191vmbus_channel_process_offer(hv_vmbus_channel *new_channel) 192{ 193 hv_vmbus_channel* channel; 194 int ret; 195 uint32_t relid; 196 197 relid = new_channel->offer_msg.child_rel_id; 198 /* 199 * Make sure this is a new offer 200 */ 201 mtx_lock(&hv_vmbus_g_connection.channel_lock); 202 if (relid == 0) { 203 /* 204 * XXX channel0 will not be processed; skip it. 205 */ 206 printf("VMBUS: got channel0 offer\n"); 207 } else { 208 hv_vmbus_g_connection.channels[relid] = new_channel; 209 } 210 211 TAILQ_FOREACH(channel, &hv_vmbus_g_connection.channel_anchor, 212 list_entry) { 213 if (memcmp(&channel->offer_msg.offer.interface_type, 214 &new_channel->offer_msg.offer.interface_type, 215 sizeof(hv_guid)) == 0 && 216 memcmp(&channel->offer_msg.offer.interface_instance, 217 &new_channel->offer_msg.offer.interface_instance, 218 sizeof(hv_guid)) == 0) 219 break; 220 } 221 222 if (channel == NULL) { 223 /* Install the new primary channel */ 224 TAILQ_INSERT_TAIL(&hv_vmbus_g_connection.channel_anchor, 225 new_channel, list_entry); 226 } 227 mtx_unlock(&hv_vmbus_g_connection.channel_lock); 228 229 if (channel != NULL) { 230 /* 231 * Check if this is a sub channel. 232 */ 233 if (new_channel->offer_msg.offer.sub_channel_index != 0) { 234 /* 235 * It is a sub channel offer, process it. 236 */ 237 new_channel->primary_channel = channel; 238 new_channel->device = channel->device; 239 mtx_lock(&channel->sc_lock); 240 TAILQ_INSERT_TAIL(&channel->sc_list_anchor, 241 new_channel, sc_list_entry); 242 mtx_unlock(&channel->sc_lock); 243 244 if (bootverbose) { 245 printf("VMBUS get multi-channel offer, " 246 "rel=%u, sub=%u\n", 247 new_channel->offer_msg.child_rel_id, 248 new_channel->offer_msg.offer.sub_channel_index); 249 } 250 251 /* Insert new channel into channel_anchor. */ 252 mtx_lock(&hv_vmbus_g_connection.channel_lock); 253 TAILQ_INSERT_TAIL(&hv_vmbus_g_connection.channel_anchor, 254 new_channel, list_entry); 255 mtx_unlock(&hv_vmbus_g_connection.channel_lock); 256 257 if(bootverbose) 258 printf("VMBUS: new multi-channel offer <%p>, " 259 "its primary channel is <%p>.\n", 260 new_channel, new_channel->primary_channel); 261 262 new_channel->state = HV_CHANNEL_OPEN_STATE; 263 264 /* 265 * Bump up sub-channel count and notify anyone that is 266 * interested in this sub-channel, after this sub-channel 267 * is setup. 268 */ 269 mtx_lock(&channel->sc_lock); 270 channel->subchan_cnt++; 271 mtx_unlock(&channel->sc_lock); 272 wakeup(channel); 273 274 return; 275 } 276 277 printf("VMBUS: duplicated primary channel%u\n", 278 new_channel->offer_msg.child_rel_id); 279 hv_vmbus_free_vmbus_channel(new_channel); 280 return; 281 } 282 283 new_channel->state = HV_CHANNEL_OPEN_STATE; 284 285 /* 286 * Start the process of binding this offer to the driver 287 * (We need to set the device field before calling 288 * hv_vmbus_child_device_add()) 289 */ 290 new_channel->device = hv_vmbus_child_device_create( 291 new_channel->offer_msg.offer.interface_type, 292 new_channel->offer_msg.offer.interface_instance, new_channel); 293 294 /* 295 * Add the new device to the bus. This will kick off device-driver 296 * binding which eventually invokes the device driver's AddDevice() 297 * method. 298 */ 299 ret = hv_vmbus_child_device_register(new_channel->device); 300 if (ret != 0) { 301 mtx_lock(&hv_vmbus_g_connection.channel_lock); 302 TAILQ_REMOVE(&hv_vmbus_g_connection.channel_anchor, 303 new_channel, list_entry); 304 mtx_unlock(&hv_vmbus_g_connection.channel_lock); 305 hv_vmbus_free_vmbus_channel(new_channel); 306 } 307 308 mtx_lock(&vmbus_chwait_lock); 309 vmbus_devcnt++; 310 mtx_unlock(&vmbus_chwait_lock); 311 wakeup(&vmbus_devcnt); 312} 313 314void 315vmbus_channel_cpu_set(struct hv_vmbus_channel *chan, int cpu) 316{ 317 KASSERT(cpu >= 0 && cpu < mp_ncpus, ("invalid cpu %d", cpu)); 318 319 if (hv_vmbus_protocal_version == HV_VMBUS_VERSION_WS2008 || 320 hv_vmbus_protocal_version == HV_VMBUS_VERSION_WIN7) { 321 /* Only cpu0 is supported */ 322 cpu = 0; 323 } 324 325 chan->target_cpu = cpu; 326 chan->target_vcpu = VMBUS_PCPU_GET(vmbus_get_softc(), vcpuid, cpu); 327 328 if (bootverbose) { 329 printf("vmbus_chan%u: assigned to cpu%u [vcpu%u]\n", 330 chan->offer_msg.child_rel_id, 331 chan->target_cpu, chan->target_vcpu); 332 } 333} 334 335/** 336 * Array of device guids that are performance critical. We try to distribute 337 * the interrupt load for these devices across all online cpus. 338 */ 339static const hv_guid high_perf_devices[] = { 340 {HV_NIC_GUID, }, 341 {HV_IDE_GUID, }, 342 {HV_SCSI_GUID, }, 343}; 344 345enum { 346 PERF_CHN_NIC = 0, 347 PERF_CHN_IDE, 348 PERF_CHN_SCSI, 349 MAX_PERF_CHN, 350}; 351 352/* 353 * We use this static number to distribute the channel interrupt load. 354 */ 355static uint32_t next_vcpu; 356 357/** 358 * Starting with Win8, we can statically distribute the incoming 359 * channel interrupt load by binding a channel to VCPU. We 360 * implement here a simple round robin scheme for distributing 361 * the interrupt load. 362 * We will bind channels that are not performance critical to cpu 0 and 363 * performance critical channels (IDE, SCSI and Network) will be uniformly 364 * distributed across all available CPUs. 365 */ 366static void 367vmbus_channel_select_defcpu(struct hv_vmbus_channel *channel) 368{ 369 uint32_t current_cpu; 370 int i; 371 boolean_t is_perf_channel = FALSE; 372 const hv_guid *guid = &channel->offer_msg.offer.interface_type; 373 374 for (i = PERF_CHN_NIC; i < MAX_PERF_CHN; i++) { 375 if (memcmp(guid->data, high_perf_devices[i].data, 376 sizeof(hv_guid)) == 0) { 377 is_perf_channel = TRUE; 378 break; 379 } 380 } 381 382 if (!is_perf_channel) { 383 /* Stick to cpu0 */ 384 vmbus_channel_cpu_set(channel, 0); 385 return; 386 } 387 /* mp_ncpus should have the number cpus currently online */ 388 current_cpu = (++next_vcpu % mp_ncpus); 389 vmbus_channel_cpu_set(channel, current_cpu); 390} 391 392/** 393 * @brief Handler for channel offers from Hyper-V/Azure 394 * 395 * Handler for channel offers from vmbus in parent partition. We ignore 396 * all offers except network and storage offers. For each network and storage 397 * offers, we create a channel object and queue a work item to the channel 398 * object to process the offer synchronously 399 */ 400static void 401vmbus_channel_on_offer(const hv_vmbus_channel_msg_header *hdr) 402{ 403 const hv_vmbus_channel_offer_channel *offer; 404 hv_vmbus_channel_offer_channel *copied; 405 406 offer = (const hv_vmbus_channel_offer_channel *)hdr; 407 408 // copy offer data 409 copied = malloc(sizeof(*copied), M_DEVBUF, M_NOWAIT); 410 if (copied == NULL) { 411 printf("fail to allocate memory\n"); 412 return; 413 } 414 415 memcpy(copied, hdr, sizeof(*copied)); 416 hv_queue_work_item(vmbus_channel_on_offer_internal, copied); 417 418 mtx_lock(&vmbus_chwait_lock); 419 if ((vmbus_chancnt & VMBUS_CHANCNT_DONE) == 0) 420 vmbus_chancnt++; 421 mtx_unlock(&vmbus_chwait_lock); 422} 423 424static void 425vmbus_channel_on_offer_internal(void* context) 426{ 427 hv_vmbus_channel* new_channel; 428 429 hv_vmbus_channel_offer_channel* offer = (hv_vmbus_channel_offer_channel*)context; 430 /* Allocate the channel object and save this offer */ 431 new_channel = hv_vmbus_allocate_channel(); 432 433 /* 434 * By default we setup state to enable batched 435 * reading. A specific service can choose to 436 * disable this prior to opening the channel. 437 */ 438 new_channel->batched_reading = TRUE; 439 440 new_channel->signal_event_param = 441 (hv_vmbus_input_signal_event *) 442 (HV_ALIGN_UP((unsigned long) 443 &new_channel->signal_event_buffer, 444 HV_HYPERCALL_PARAM_ALIGN)); 445 446 new_channel->signal_event_param->connection_id.as_uint32_t = 0; 447 new_channel->signal_event_param->connection_id.u.id = 448 HV_VMBUS_EVENT_CONNECTION_ID; 449 new_channel->signal_event_param->flag_number = 0; 450 new_channel->signal_event_param->rsvd_z = 0; 451 452 if (hv_vmbus_protocal_version != HV_VMBUS_VERSION_WS2008) { 453 new_channel->is_dedicated_interrupt = 454 (offer->is_dedicated_interrupt != 0); 455 new_channel->signal_event_param->connection_id.u.id = 456 offer->connection_id; 457 } 458 459 memcpy(&new_channel->offer_msg, offer, 460 sizeof(hv_vmbus_channel_offer_channel)); 461 new_channel->monitor_group = (uint8_t) offer->monitor_id / 32; 462 new_channel->monitor_bit = (uint8_t) offer->monitor_id % 32; 463 464 /* Select default cpu for this channel. */ 465 vmbus_channel_select_defcpu(new_channel); 466 467 vmbus_channel_process_offer(new_channel); 468 469 free(offer, M_DEVBUF); 470} 471 472/** 473 * @brief Rescind offer handler. 474 * 475 * We queue a work item to process this offer 476 * synchronously 477 */ 478static void 479vmbus_channel_on_offer_rescind(const hv_vmbus_channel_msg_header *hdr) 480{ 481 const hv_vmbus_channel_rescind_offer *rescind; 482 hv_vmbus_channel* channel; 483 484 rescind = (const hv_vmbus_channel_rescind_offer *)hdr; 485 486 channel = hv_vmbus_g_connection.channels[rescind->child_rel_id]; 487 if (channel == NULL) 488 return; 489 490 hv_queue_work_item(vmbus_channel_on_offer_rescind_internal, channel); 491 hv_vmbus_g_connection.channels[rescind->child_rel_id] = NULL; 492} 493 494static void 495vmbus_channel_on_offer_rescind_internal(void *context) 496{ 497 hv_vmbus_channel* channel; 498 499 channel = (hv_vmbus_channel*)context; 500 if (HV_VMBUS_CHAN_ISPRIMARY(channel)) { 501 /* Only primary channel owns the hv_device */ 502 hv_vmbus_child_device_unregister(channel->device); 503 } 504} 505 506/** 507 * 508 * @brief Invoked when all offers have been delivered. 509 */ 510static void 511vmbus_channel_on_offers_delivered( 512 const hv_vmbus_channel_msg_header *hdr __unused) 513{ 514 515 mtx_lock(&vmbus_chwait_lock); 516 vmbus_chancnt |= VMBUS_CHANCNT_DONE; 517 mtx_unlock(&vmbus_chwait_lock); 518 wakeup(&vmbus_chancnt); 519} 520 521/** 522 * @brief Open result handler. 523 * 524 * This is invoked when we received a response 525 * to our channel open request. Find the matching request, copy the 526 * response and signal the requesting thread. 527 */ 528static void 529vmbus_channel_on_open_result(const hv_vmbus_channel_msg_header *hdr) 530{ 531 const hv_vmbus_channel_open_result *result; 532 hv_vmbus_channel_msg_info* msg_info; 533 hv_vmbus_channel_msg_header* requestHeader; 534 hv_vmbus_channel_open_channel* openMsg; 535 536 result = (const hv_vmbus_channel_open_result *)hdr; 537 538 /* 539 * Find the open msg, copy the result and signal/unblock the wait event 540 */ 541 mtx_lock(&hv_vmbus_g_connection.channel_msg_lock); 542 543 TAILQ_FOREACH(msg_info, &hv_vmbus_g_connection.channel_msg_anchor, 544 msg_list_entry) { 545 requestHeader = (hv_vmbus_channel_msg_header*) msg_info->msg; 546 547 if (requestHeader->message_type == 548 HV_CHANNEL_MESSAGE_OPEN_CHANNEL) { 549 openMsg = (hv_vmbus_channel_open_channel*) msg_info->msg; 550 if (openMsg->child_rel_id == result->child_rel_id 551 && openMsg->open_id == result->open_id) { 552 memcpy(&msg_info->response.open_result, result, 553 sizeof(hv_vmbus_channel_open_result)); 554 sema_post(&msg_info->wait_sema); 555 break; 556 } 557 } 558 } 559 mtx_unlock(&hv_vmbus_g_connection.channel_msg_lock); 560 561} 562 563/** 564 * @brief GPADL created handler. 565 * 566 * This is invoked when we received a response 567 * to our gpadl create request. Find the matching request, copy the 568 * response and signal the requesting thread. 569 */ 570static void 571vmbus_channel_on_gpadl_created(const hv_vmbus_channel_msg_header *hdr) 572{ 573 const hv_vmbus_channel_gpadl_created *gpadl_created; 574 hv_vmbus_channel_msg_info* msg_info; 575 hv_vmbus_channel_msg_header* request_header; 576 hv_vmbus_channel_gpadl_header* gpadl_header; 577 578 gpadl_created = (const hv_vmbus_channel_gpadl_created *)hdr; 579 580 /* Find the establish msg, copy the result and signal/unblock 581 * the wait event 582 */ 583 mtx_lock(&hv_vmbus_g_connection.channel_msg_lock); 584 TAILQ_FOREACH(msg_info, &hv_vmbus_g_connection.channel_msg_anchor, 585 msg_list_entry) { 586 request_header = (hv_vmbus_channel_msg_header*) msg_info->msg; 587 if (request_header->message_type == 588 HV_CHANNEL_MESSAGEL_GPADL_HEADER) { 589 gpadl_header = 590 (hv_vmbus_channel_gpadl_header*) request_header; 591 592 if ((gpadl_created->child_rel_id == gpadl_header->child_rel_id) 593 && (gpadl_created->gpadl == gpadl_header->gpadl)) { 594 memcpy(&msg_info->response.gpadl_created, 595 gpadl_created, 596 sizeof(hv_vmbus_channel_gpadl_created)); 597 sema_post(&msg_info->wait_sema); 598 break; 599 } 600 } 601 } 602 mtx_unlock(&hv_vmbus_g_connection.channel_msg_lock); 603} 604 605/** 606 * @brief GPADL torndown handler. 607 * 608 * This is invoked when we received a respons 609 * to our gpadl teardown request. Find the matching request, copy the 610 * response and signal the requesting thread 611 */ 612static void 613vmbus_channel_on_gpadl_torndown(const hv_vmbus_channel_msg_header *hdr) 614{ 615 const hv_vmbus_channel_gpadl_torndown *gpadl_torndown; 616 hv_vmbus_channel_msg_info* msg_info; 617 hv_vmbus_channel_msg_header* requestHeader; 618 hv_vmbus_channel_gpadl_teardown* gpadlTeardown; 619 620 gpadl_torndown = (const hv_vmbus_channel_gpadl_torndown *)hdr; 621 622 /* 623 * Find the open msg, copy the result and signal/unblock the 624 * wait event. 625 */ 626 627 mtx_lock(&hv_vmbus_g_connection.channel_msg_lock); 628 629 TAILQ_FOREACH(msg_info, &hv_vmbus_g_connection.channel_msg_anchor, 630 msg_list_entry) { 631 requestHeader = (hv_vmbus_channel_msg_header*) msg_info->msg; 632 633 if (requestHeader->message_type 634 == HV_CHANNEL_MESSAGE_GPADL_TEARDOWN) { 635 gpadlTeardown = 636 (hv_vmbus_channel_gpadl_teardown*) requestHeader; 637 638 if (gpadl_torndown->gpadl == gpadlTeardown->gpadl) { 639 memcpy(&msg_info->response.gpadl_torndown, 640 gpadl_torndown, 641 sizeof(hv_vmbus_channel_gpadl_torndown)); 642 sema_post(&msg_info->wait_sema); 643 break; 644 } 645 } 646 } 647 mtx_unlock(&hv_vmbus_g_connection.channel_msg_lock); 648} 649 650/** 651 * @brief Version response handler. 652 * 653 * This is invoked when we received a response 654 * to our initiate contact request. Find the matching request, copy th 655 * response and signal the requesting thread. 656 */ 657static void 658vmbus_channel_on_version_response(const hv_vmbus_channel_msg_header *hdr) 659{ 660 hv_vmbus_channel_msg_info* msg_info; 661 hv_vmbus_channel_msg_header* requestHeader; 662 hv_vmbus_channel_initiate_contact* initiate; 663 const hv_vmbus_channel_version_response *versionResponse; 664 665 versionResponse = (const hv_vmbus_channel_version_response *)hdr; 666 667 mtx_lock(&hv_vmbus_g_connection.channel_msg_lock); 668 TAILQ_FOREACH(msg_info, &hv_vmbus_g_connection.channel_msg_anchor, 669 msg_list_entry) { 670 requestHeader = (hv_vmbus_channel_msg_header*) msg_info->msg; 671 if (requestHeader->message_type 672 == HV_CHANNEL_MESSAGE_INITIATED_CONTACT) { 673 initiate = 674 (hv_vmbus_channel_initiate_contact*) requestHeader; 675 memcpy(&msg_info->response.version_response, 676 versionResponse, 677 sizeof(hv_vmbus_channel_version_response)); 678 sema_post(&msg_info->wait_sema); 679 } 680 } 681 mtx_unlock(&hv_vmbus_g_connection.channel_msg_lock); 682 683} 684 685/** 686 * @brief Send a request to get all our pending offers. 687 */ 688int 689hv_vmbus_request_channel_offers(void) 690{ 691 int ret; 692 hv_vmbus_channel_msg_header* msg; 693 hv_vmbus_channel_msg_info* msg_info; 694 695 msg_info = (hv_vmbus_channel_msg_info *) 696 malloc(sizeof(hv_vmbus_channel_msg_info) 697 + sizeof(hv_vmbus_channel_msg_header), M_DEVBUF, M_NOWAIT); 698 699 if (msg_info == NULL) { 700 if(bootverbose) 701 printf("Error VMBUS: malloc failed for Request Offers\n"); 702 return (ENOMEM); 703 } 704 705 msg = (hv_vmbus_channel_msg_header*) msg_info->msg; 706 msg->message_type = HV_CHANNEL_MESSAGE_REQUEST_OFFERS; 707 708 ret = hv_vmbus_post_message(msg, sizeof(hv_vmbus_channel_msg_header)); 709 710 free(msg_info, M_DEVBUF); 711 712 return (ret); 713} 714 715/** 716 * @brief Release channels that are unattached/unconnected (i.e., no drivers associated) 717 */ 718void 719hv_vmbus_release_unattached_channels(void) 720{ 721 hv_vmbus_channel *channel; 722 723 mtx_lock(&hv_vmbus_g_connection.channel_lock); 724 725 while (!TAILQ_EMPTY(&hv_vmbus_g_connection.channel_anchor)) { 726 channel = TAILQ_FIRST(&hv_vmbus_g_connection.channel_anchor); 727 TAILQ_REMOVE(&hv_vmbus_g_connection.channel_anchor, 728 channel, list_entry); 729 730 if (HV_VMBUS_CHAN_ISPRIMARY(channel)) { 731 /* Only primary channel owns the hv_device */ 732 hv_vmbus_child_device_unregister(channel->device); 733 } 734 hv_vmbus_free_vmbus_channel(channel); 735 } 736 bzero(hv_vmbus_g_connection.channels, 737 sizeof(hv_vmbus_channel*) * VMBUS_CHAN_MAX); 738 mtx_unlock(&hv_vmbus_g_connection.channel_lock); 739} 740 741/** 742 * @brief Select the best outgoing channel 743 * 744 * The channel whose vcpu binding is closest to the currect vcpu will 745 * be selected. 746 * If no multi-channel, always select primary channel 747 * 748 * @param primary - primary channel 749 */ 750struct hv_vmbus_channel * 751vmbus_select_outgoing_channel(struct hv_vmbus_channel *primary) 752{ 753 hv_vmbus_channel *new_channel = NULL; 754 hv_vmbus_channel *outgoing_channel = primary; 755 int old_cpu_distance = 0; 756 int new_cpu_distance = 0; 757 int cur_vcpu = 0; 758 int smp_pro_id = PCPU_GET(cpuid); 759 760 if (TAILQ_EMPTY(&primary->sc_list_anchor)) { 761 return outgoing_channel; 762 } 763 764 if (smp_pro_id >= MAXCPU) { 765 return outgoing_channel; 766 } 767 768 cur_vcpu = VMBUS_PCPU_GET(vmbus_get_softc(), vcpuid, smp_pro_id); 769 770 TAILQ_FOREACH(new_channel, &primary->sc_list_anchor, sc_list_entry) { 771 if (new_channel->state != HV_CHANNEL_OPENED_STATE){ 772 continue; 773 } 774 775 if (new_channel->target_vcpu == cur_vcpu){ 776 return new_channel; 777 } 778 779 old_cpu_distance = ((outgoing_channel->target_vcpu > cur_vcpu) ? 780 (outgoing_channel->target_vcpu - cur_vcpu) : 781 (cur_vcpu - outgoing_channel->target_vcpu)); 782 783 new_cpu_distance = ((new_channel->target_vcpu > cur_vcpu) ? 784 (new_channel->target_vcpu - cur_vcpu) : 785 (cur_vcpu - new_channel->target_vcpu)); 786 787 if (old_cpu_distance < new_cpu_distance) { 788 continue; 789 } 790 791 outgoing_channel = new_channel; 792 } 793 794 return(outgoing_channel); 795} 796 797void 798vmbus_scan(void) 799{ 800 uint32_t chancnt; 801 802 mtx_lock(&vmbus_chwait_lock); 803 while ((vmbus_chancnt & VMBUS_CHANCNT_DONE) == 0) 804 mtx_sleep(&vmbus_chancnt, &vmbus_chwait_lock, 0, "waitch", 0); 805 chancnt = vmbus_chancnt & ~VMBUS_CHANCNT_DONE; 806 807 while (vmbus_devcnt != chancnt) 808 mtx_sleep(&vmbus_devcnt, &vmbus_chwait_lock, 0, "waitdev", 0); 809 mtx_unlock(&vmbus_chwait_lock); 810} 811 812struct hv_vmbus_channel ** 813vmbus_get_subchan(struct hv_vmbus_channel *pri_chan, int subchan_cnt) 814{ 815 struct hv_vmbus_channel **ret, *chan; 816 int i; 817 818 ret = malloc(subchan_cnt * sizeof(struct hv_vmbus_channel *), M_TEMP, 819 M_WAITOK); 820 821 mtx_lock(&pri_chan->sc_lock); 822 823 while (pri_chan->subchan_cnt < subchan_cnt) 824 mtx_sleep(pri_chan, &pri_chan->sc_lock, 0, "subch", 0); 825 826 i = 0; 827 TAILQ_FOREACH(chan, &pri_chan->sc_list_anchor, sc_list_entry) { 828 /* TODO: refcnt chan */ 829 ret[i] = chan; 830 831 ++i; 832 if (i == subchan_cnt) 833 break; 834 } 835 KASSERT(i == subchan_cnt, ("invalid subchan count %d, should be %d", 836 pri_chan->subchan_cnt, subchan_cnt)); 837 838 mtx_unlock(&pri_chan->sc_lock); 839 840 return ret; 841} 842 843void 844vmbus_rel_subchan(struct hv_vmbus_channel **subchan, int subchan_cnt __unused) 845{ 846 847 free(subchan, M_TEMP); 848} 849 850void 851vmbus_chan_msgproc(struct vmbus_softc *sc, const struct vmbus_message *msg) 852{ 853 const hv_vmbus_channel_msg_table_entry *entry; 854 const hv_vmbus_channel_msg_header *hdr; 855 hv_vmbus_channel_msg_type msg_type; 856 857 hdr = (const hv_vmbus_channel_msg_header *)msg->msg_data; 858 msg_type = hdr->message_type; 859 860 if (msg_type >= HV_CHANNEL_MESSAGE_COUNT) { 861 device_printf(sc->vmbus_dev, "unknown message type 0x%x\n", 862 msg_type); 863 return; 864 } 865 866 entry = &g_channel_message_table[msg_type]; 867 if (entry->messageHandler) 868 entry->messageHandler(hdr); 869} 870