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.3/sys/dev/hyperv/vmbus/hv_channel_mgmt.c 303984 2016-08-12 04:01:16Z glebius $"); 31283280Swhu 32250199Sgrehan#include <sys/param.h> 33250199Sgrehan#include <sys/mbuf.h> 34250199Sgrehan 35250199Sgrehan#include "hv_vmbus_priv.h" 36250199Sgrehan 37250199Sgrehan/* 38250199Sgrehan * Internal functions 39250199Sgrehan */ 40250199Sgrehan 41250199Sgrehanstatic void vmbus_channel_on_offer(hv_vmbus_channel_msg_header* hdr); 42250199Sgrehanstatic void vmbus_channel_on_open_result(hv_vmbus_channel_msg_header* hdr); 43250199Sgrehanstatic void vmbus_channel_on_offer_rescind(hv_vmbus_channel_msg_header* hdr); 44250199Sgrehanstatic void vmbus_channel_on_gpadl_created(hv_vmbus_channel_msg_header* hdr); 45250199Sgrehanstatic void vmbus_channel_on_gpadl_torndown(hv_vmbus_channel_msg_header* hdr); 46250199Sgrehanstatic void vmbus_channel_on_offers_delivered(hv_vmbus_channel_msg_header* hdr); 47250199Sgrehanstatic void vmbus_channel_on_version_response(hv_vmbus_channel_msg_header* hdr); 48250199Sgrehan 49250199Sgrehan/** 50250199Sgrehan * Channel message dispatch table 51250199Sgrehan */ 52250199Sgrehanhv_vmbus_channel_msg_table_entry 53250199Sgrehan g_channel_message_table[HV_CHANNEL_MESSAGE_COUNT] = { 54293820Sdelphij { HV_CHANNEL_MESSAGE_INVALID, 55293820Sdelphij 0, NULL }, 56293820Sdelphij { HV_CHANNEL_MESSAGE_OFFER_CHANNEL, 57293820Sdelphij 0, vmbus_channel_on_offer }, 58250199Sgrehan { HV_CHANNEL_MESSAGE_RESCIND_CHANNEL_OFFER, 59293820Sdelphij 0, vmbus_channel_on_offer_rescind }, 60293820Sdelphij { HV_CHANNEL_MESSAGE_REQUEST_OFFERS, 61293820Sdelphij 0, NULL }, 62250199Sgrehan { HV_CHANNEL_MESSAGE_ALL_OFFERS_DELIVERED, 63293820Sdelphij 1, vmbus_channel_on_offers_delivered }, 64293820Sdelphij { HV_CHANNEL_MESSAGE_OPEN_CHANNEL, 65293820Sdelphij 0, NULL }, 66250199Sgrehan { HV_CHANNEL_MESSAGE_OPEN_CHANNEL_RESULT, 67293820Sdelphij 1, vmbus_channel_on_open_result }, 68293820Sdelphij { HV_CHANNEL_MESSAGE_CLOSE_CHANNEL, 69293820Sdelphij 0, NULL }, 70293820Sdelphij { HV_CHANNEL_MESSAGEL_GPADL_HEADER, 71293820Sdelphij 0, NULL }, 72293820Sdelphij { HV_CHANNEL_MESSAGE_GPADL_BODY, 73293820Sdelphij 0, NULL }, 74250199Sgrehan { HV_CHANNEL_MESSAGE_GPADL_CREATED, 75293820Sdelphij 1, vmbus_channel_on_gpadl_created }, 76293820Sdelphij { HV_CHANNEL_MESSAGE_GPADL_TEARDOWN, 77293820Sdelphij 0, NULL }, 78250199Sgrehan { HV_CHANNEL_MESSAGE_GPADL_TORNDOWN, 79293820Sdelphij 1, vmbus_channel_on_gpadl_torndown }, 80293820Sdelphij { HV_CHANNEL_MESSAGE_REL_ID_RELEASED, 81293820Sdelphij 0, NULL }, 82293820Sdelphij { HV_CHANNEL_MESSAGE_INITIATED_CONTACT, 83293820Sdelphij 0, NULL }, 84250199Sgrehan { HV_CHANNEL_MESSAGE_VERSION_RESPONSE, 85293820Sdelphij 1, vmbus_channel_on_version_response }, 86293820Sdelphij { HV_CHANNEL_MESSAGE_UNLOAD, 87293820Sdelphij 0, NULL } 88250199Sgrehan}; 89250199Sgrehan 90250199Sgrehan 91250199Sgrehan/** 92250199Sgrehan * Implementation of the work abstraction. 93250199Sgrehan */ 94250199Sgrehanstatic void 95250199Sgrehanwork_item_callback(void *work, int pending) 96250199Sgrehan{ 97250199Sgrehan struct hv_work_item *w = (struct hv_work_item *)work; 98250199Sgrehan 99250199Sgrehan /* 100250199Sgrehan * Serialize work execution. 101250199Sgrehan */ 102250199Sgrehan if (w->wq->work_sema != NULL) { 103250199Sgrehan sema_wait(w->wq->work_sema); 104250199Sgrehan } 105250199Sgrehan 106250199Sgrehan w->callback(w->context); 107250199Sgrehan 108250199Sgrehan if (w->wq->work_sema != NULL) { 109250199Sgrehan sema_post(w->wq->work_sema); 110250199Sgrehan } 111250199Sgrehan 112250199Sgrehan free(w, M_DEVBUF); 113250199Sgrehan} 114250199Sgrehan 115250199Sgrehanstruct hv_work_queue* 116250199Sgrehanhv_work_queue_create(char* name) 117250199Sgrehan{ 118250199Sgrehan static unsigned int qid = 0; 119250199Sgrehan char qname[64]; 120250199Sgrehan int pri; 121250199Sgrehan struct hv_work_queue* wq; 122250199Sgrehan 123250199Sgrehan wq = malloc(sizeof(struct hv_work_queue), M_DEVBUF, M_NOWAIT | M_ZERO); 124250199Sgrehan KASSERT(wq != NULL, ("Error VMBUS: Failed to allocate work_queue\n")); 125250199Sgrehan if (wq == NULL) 126250199Sgrehan return (NULL); 127250199Sgrehan 128250199Sgrehan /* 129250199Sgrehan * We use work abstraction to handle messages 130250199Sgrehan * coming from the host and these are typically offers. 131250199Sgrehan * Some FreeBsd drivers appear to have a concurrency issue 132250199Sgrehan * where probe/attach needs to be serialized. We ensure that 133250199Sgrehan * by having only one thread process work elements in a 134250199Sgrehan * specific queue by serializing work execution. 135250199Sgrehan * 136250199Sgrehan */ 137250199Sgrehan if (strcmp(name, "vmbusQ") == 0) { 138250199Sgrehan pri = PI_DISK; 139250199Sgrehan } else { /* control */ 140250199Sgrehan pri = PI_NET; 141250199Sgrehan /* 142250199Sgrehan * Initialize semaphore for this queue by pointing 143250199Sgrehan * to the globale semaphore used for synchronizing all 144250199Sgrehan * control messages. 145250199Sgrehan */ 146250199Sgrehan wq->work_sema = &hv_vmbus_g_connection.control_sema; 147250199Sgrehan } 148250199Sgrehan 149250199Sgrehan sprintf(qname, "hv_%s_%u", name, qid); 150250199Sgrehan 151250199Sgrehan /* 152250199Sgrehan * Fixme: FreeBSD 8.2 has a different prototype for 153250199Sgrehan * taskqueue_create(), and for certain other taskqueue functions. 154250199Sgrehan * We need to research the implications of these changes. 155250199Sgrehan * Fixme: Not sure when the changes were introduced. 156250199Sgrehan */ 157250199Sgrehan wq->queue = taskqueue_create(qname, M_NOWAIT, taskqueue_thread_enqueue, 158250199Sgrehan &wq->queue 159250199Sgrehan #if __FreeBSD_version < 800000 160250199Sgrehan , &wq->proc 161250199Sgrehan #endif 162250199Sgrehan ); 163250199Sgrehan 164250199Sgrehan if (wq->queue == NULL) { 165250199Sgrehan free(wq, M_DEVBUF); 166250199Sgrehan return (NULL); 167250199Sgrehan } 168250199Sgrehan 169250199Sgrehan if (taskqueue_start_threads(&wq->queue, 1, pri, "%s taskq", qname)) { 170250199Sgrehan taskqueue_free(wq->queue); 171250199Sgrehan free(wq, M_DEVBUF); 172250199Sgrehan return (NULL); 173250199Sgrehan } 174250199Sgrehan 175250199Sgrehan qid++; 176250199Sgrehan 177250199Sgrehan return (wq); 178250199Sgrehan} 179250199Sgrehan 180250199Sgrehanvoid 181250199Sgrehanhv_work_queue_close(struct hv_work_queue *wq) 182250199Sgrehan{ 183250199Sgrehan /* 184250199Sgrehan * KYS: Need to drain the taskqueue 185250199Sgrehan * before we close the hv_work_queue. 186250199Sgrehan */ 187250199Sgrehan /*KYS: taskqueue_drain(wq->tq, ); */ 188250199Sgrehan taskqueue_free(wq->queue); 189250199Sgrehan free(wq, M_DEVBUF); 190250199Sgrehan} 191250199Sgrehan 192250199Sgrehan/** 193250199Sgrehan * @brief Create work item 194250199Sgrehan */ 195250199Sgrehanint 196250199Sgrehanhv_queue_work_item( 197250199Sgrehan struct hv_work_queue *wq, 198250199Sgrehan void (*callback)(void *), void *context) 199250199Sgrehan{ 200250199Sgrehan struct hv_work_item *w = malloc(sizeof(struct hv_work_item), 201250199Sgrehan M_DEVBUF, M_NOWAIT | M_ZERO); 202250199Sgrehan KASSERT(w != NULL, ("Error VMBUS: Failed to allocate WorkItem\n")); 203250199Sgrehan if (w == NULL) 204250199Sgrehan return (ENOMEM); 205250199Sgrehan 206250199Sgrehan w->callback = callback; 207250199Sgrehan w->context = context; 208250199Sgrehan w->wq = wq; 209250199Sgrehan 210250199Sgrehan TASK_INIT(&w->work, 0, work_item_callback, w); 211250199Sgrehan 212250199Sgrehan return (taskqueue_enqueue(wq->queue, &w->work)); 213250199Sgrehan} 214250199Sgrehan 215250199Sgrehan 216250199Sgrehan/** 217250199Sgrehan * @brief Allocate and initialize a vmbus channel object 218250199Sgrehan */ 219250199Sgrehanhv_vmbus_channel* 220250199Sgrehanhv_vmbus_allocate_channel(void) 221250199Sgrehan{ 222250199Sgrehan hv_vmbus_channel* channel; 223250199Sgrehan 224250199Sgrehan channel = (hv_vmbus_channel*) malloc( 225250199Sgrehan sizeof(hv_vmbus_channel), 226250199Sgrehan M_DEVBUF, 227250199Sgrehan M_NOWAIT | M_ZERO); 228250199Sgrehan KASSERT(channel != NULL, ("Error VMBUS: Failed to allocate channel!")); 229250199Sgrehan if (channel == NULL) 230250199Sgrehan return (NULL); 231250199Sgrehan 232250199Sgrehan mtx_init(&channel->inbound_lock, "channel inbound", NULL, MTX_DEF); 233283280Swhu mtx_init(&channel->sc_lock, "vmbus multi channel", NULL, MTX_DEF); 234250199Sgrehan 235283280Swhu TAILQ_INIT(&channel->sc_list_anchor); 236283280Swhu 237250199Sgrehan return (channel); 238250199Sgrehan} 239250199Sgrehan 240250199Sgrehan/** 241250199Sgrehan * @brief Release the vmbus channel object itself 242250199Sgrehan */ 243250199Sgrehanstatic inline void 244250199SgrehanReleaseVmbusChannel(void *context) 245250199Sgrehan{ 246250199Sgrehan hv_vmbus_channel* channel = (hv_vmbus_channel*) context; 247250199Sgrehan free(channel, M_DEVBUF); 248250199Sgrehan} 249250199Sgrehan 250250199Sgrehan/** 251250199Sgrehan * @brief Release the resources used by the vmbus channel object 252250199Sgrehan */ 253250199Sgrehanvoid 254250199Sgrehanhv_vmbus_free_vmbus_channel(hv_vmbus_channel* channel) 255250199Sgrehan{ 256283280Swhu mtx_destroy(&channel->sc_lock); 257250199Sgrehan mtx_destroy(&channel->inbound_lock); 258250199Sgrehan /* 259250199Sgrehan * We have to release the channel's workqueue/thread in 260250199Sgrehan * the vmbus's workqueue/thread context 261250199Sgrehan * ie we can't destroy ourselves 262250199Sgrehan */ 263250199Sgrehan hv_queue_work_item(hv_vmbus_g_connection.work_queue, 264250199Sgrehan ReleaseVmbusChannel, (void *) channel); 265250199Sgrehan} 266250199Sgrehan 267250199Sgrehan/** 268250199Sgrehan * @brief Process the offer by creating a channel/device 269250199Sgrehan * associated with this offer 270250199Sgrehan */ 271250199Sgrehanstatic void 272293820Sdelphijvmbus_channel_process_offer(hv_vmbus_channel *new_channel) 273250199Sgrehan{ 274250199Sgrehan boolean_t f_new; 275250199Sgrehan hv_vmbus_channel* channel; 276283280Swhu int ret; 277295948Ssephe uint32_t relid; 278250199Sgrehan 279250199Sgrehan f_new = TRUE; 280250199Sgrehan channel = NULL; 281295948Ssephe relid = new_channel->offer_msg.child_rel_id; 282250199Sgrehan /* 283250199Sgrehan * Make sure this is a new offer 284250199Sgrehan */ 285283280Swhu mtx_lock(&hv_vmbus_g_connection.channel_lock); 286295948Ssephe hv_vmbus_g_connection.channels[relid] = new_channel; 287250199Sgrehan 288250199Sgrehan TAILQ_FOREACH(channel, &hv_vmbus_g_connection.channel_anchor, 289250199Sgrehan list_entry) 290250199Sgrehan { 291283280Swhu if (memcmp(&channel->offer_msg.offer.interface_type, 292283280Swhu &new_channel->offer_msg.offer.interface_type, 293283280Swhu sizeof(hv_guid)) == 0 && 294283280Swhu memcmp(&channel->offer_msg.offer.interface_instance, 295250199Sgrehan &new_channel->offer_msg.offer.interface_instance, 296283280Swhu sizeof(hv_guid)) == 0) { 297283280Swhu f_new = FALSE; 298283280Swhu break; 299283280Swhu } 300250199Sgrehan } 301250199Sgrehan 302250199Sgrehan if (f_new) { 303283280Swhu /* Insert at tail */ 304283280Swhu TAILQ_INSERT_TAIL( 305283280Swhu &hv_vmbus_g_connection.channel_anchor, 306283280Swhu new_channel, 307283280Swhu list_entry); 308250199Sgrehan } 309283280Swhu mtx_unlock(&hv_vmbus_g_connection.channel_lock); 310250199Sgrehan 311283280Swhu /*XXX add new channel to percpu_list */ 312283280Swhu 313250199Sgrehan if (!f_new) { 314283280Swhu /* 315283280Swhu * Check if this is a sub channel. 316283280Swhu */ 317283280Swhu if (new_channel->offer_msg.offer.sub_channel_index != 0) { 318283280Swhu /* 319283280Swhu * It is a sub channel offer, process it. 320283280Swhu */ 321283280Swhu new_channel->primary_channel = channel; 322283280Swhu mtx_lock(&channel->sc_lock); 323283280Swhu TAILQ_INSERT_TAIL( 324283280Swhu &channel->sc_list_anchor, 325283280Swhu new_channel, 326283280Swhu sc_list_entry); 327283280Swhu mtx_unlock(&channel->sc_lock); 328283280Swhu 329283280Swhu /* Insert new channel into channel_anchor. */ 330295948Ssephe printf("VMBUS get multi-channel offer, rel=%u,sub=%u\n", 331295948Ssephe new_channel->offer_msg.child_rel_id, 332295948Ssephe new_channel->offer_msg.offer.sub_channel_index); 333283280Swhu mtx_lock(&hv_vmbus_g_connection.channel_lock); 334283280Swhu TAILQ_INSERT_TAIL(&hv_vmbus_g_connection.channel_anchor, 335283280Swhu new_channel, list_entry); 336283280Swhu mtx_unlock(&hv_vmbus_g_connection.channel_lock); 337283280Swhu 338283280Swhu if(bootverbose) 339295948Ssephe printf("VMBUS: new multi-channel offer <%p>, " 340295948Ssephe "its primary channel is <%p>.\n", 341295948Ssephe new_channel, new_channel->primary_channel); 342283280Swhu 343283280Swhu /*XXX add it to percpu_list */ 344283280Swhu 345283280Swhu new_channel->state = HV_CHANNEL_OPEN_STATE; 346283280Swhu if (channel->sc_creation_callback != NULL) { 347283280Swhu channel->sc_creation_callback(new_channel); 348283280Swhu } 349283280Swhu return; 350283280Swhu } 351283280Swhu 352250199Sgrehan hv_vmbus_free_vmbus_channel(new_channel); 353250199Sgrehan return; 354250199Sgrehan } 355250199Sgrehan 356283280Swhu new_channel->state = HV_CHANNEL_OPEN_STATE; 357283280Swhu 358250199Sgrehan /* 359250199Sgrehan * Start the process of binding this offer to the driver 360250199Sgrehan * (We need to set the device field before calling 361250199Sgrehan * hv_vmbus_child_device_add()) 362250199Sgrehan */ 363250199Sgrehan new_channel->device = hv_vmbus_child_device_create( 364250199Sgrehan new_channel->offer_msg.offer.interface_type, 365250199Sgrehan new_channel->offer_msg.offer.interface_instance, new_channel); 366250199Sgrehan 367250199Sgrehan /* 368250199Sgrehan * Add the new device to the bus. This will kick off device-driver 369250199Sgrehan * binding which eventually invokes the device driver's AddDevice() 370250199Sgrehan * method. 371250199Sgrehan */ 372250199Sgrehan ret = hv_vmbus_child_device_register(new_channel->device); 373250199Sgrehan if (ret != 0) { 374283280Swhu mtx_lock(&hv_vmbus_g_connection.channel_lock); 375283280Swhu TAILQ_REMOVE( 376283280Swhu &hv_vmbus_g_connection.channel_anchor, 377283280Swhu new_channel, 378283280Swhu list_entry); 379283280Swhu mtx_unlock(&hv_vmbus_g_connection.channel_lock); 380283280Swhu hv_vmbus_free_vmbus_channel(new_channel); 381283280Swhu } 382283280Swhu} 383250199Sgrehan 384283280Swhu/** 385283280Swhu * Array of device guids that are performance critical. We try to distribute 386283280Swhu * the interrupt load for these devices across all online cpus. 387283280Swhu */ 388283280Swhustatic const hv_guid high_perf_devices[] = { 389283280Swhu {HV_NIC_GUID, }, 390283280Swhu {HV_IDE_GUID, }, 391283280Swhu {HV_SCSI_GUID, }, 392283280Swhu}; 393283280Swhu 394283280Swhuenum { 395283280Swhu PERF_CHN_NIC = 0, 396283280Swhu PERF_CHN_IDE, 397283280Swhu PERF_CHN_SCSI, 398283280Swhu MAX_PERF_CHN, 399283280Swhu}; 400283280Swhu 401283280Swhu/* 402283280Swhu * We use this static number to distribute the channel interrupt load. 403283280Swhu */ 404283280Swhustatic uint32_t next_vcpu; 405283280Swhu 406283280Swhu/** 407283280Swhu * Starting with Win8, we can statically distribute the incoming 408283280Swhu * channel interrupt load by binding a channel to VCPU. We 409283280Swhu * implement here a simple round robin scheme for distributing 410283280Swhu * the interrupt load. 411283280Swhu * We will bind channels that are not performance critical to cpu 0 and 412283280Swhu * performance critical channels (IDE, SCSI and Network) will be uniformly 413283280Swhu * distributed across all available CPUs. 414283280Swhu */ 415283280Swhustatic void 416283280Swhuvmbus_channel_select_cpu(hv_vmbus_channel *channel, hv_guid *guid) 417283280Swhu{ 418283280Swhu uint32_t current_cpu; 419283280Swhu int i; 420283280Swhu boolean_t is_perf_channel = FALSE; 421283280Swhu 422283280Swhu for (i = PERF_CHN_NIC; i < MAX_PERF_CHN; i++) { 423283280Swhu if (memcmp(guid->data, high_perf_devices[i].data, 424283280Swhu sizeof(hv_guid)) == 0) { 425283280Swhu is_perf_channel = TRUE; 426283280Swhu break; 427283280Swhu } 428250199Sgrehan } 429283280Swhu 430283280Swhu if ((hv_vmbus_protocal_version == HV_VMBUS_VERSION_WS2008) || 431283280Swhu (hv_vmbus_protocal_version == HV_VMBUS_VERSION_WIN7) || 432283280Swhu (!is_perf_channel)) { 433283280Swhu /* Host's view of guest cpu */ 434283280Swhu channel->target_vcpu = 0; 435283280Swhu /* Guest's own view of cpu */ 436283280Swhu channel->target_cpu = 0; 437283280Swhu return; 438283280Swhu } 439283280Swhu /* mp_ncpus should have the number cpus currently online */ 440283280Swhu current_cpu = (++next_vcpu % mp_ncpus); 441283280Swhu channel->target_cpu = current_cpu; 442283280Swhu channel->target_vcpu = 443283280Swhu hv_vmbus_g_context.hv_vcpu_index[current_cpu]; 444283280Swhu if (bootverbose) 445283280Swhu printf("VMBUS: Total online cpus %d, assign perf channel %d " 446283280Swhu "to vcpu %d, cpu %d\n", mp_ncpus, i, channel->target_vcpu, 447283280Swhu current_cpu); 448250199Sgrehan} 449250199Sgrehan 450250199Sgrehan/** 451250199Sgrehan * @brief Handler for channel offers from Hyper-V/Azure 452250199Sgrehan * 453250199Sgrehan * Handler for channel offers from vmbus in parent partition. We ignore 454250199Sgrehan * all offers except network and storage offers. For each network and storage 455250199Sgrehan * offers, we create a channel object and queue a work item to the channel 456250199Sgrehan * object to process the offer synchronously 457250199Sgrehan */ 458250199Sgrehanstatic void 459250199Sgrehanvmbus_channel_on_offer(hv_vmbus_channel_msg_header* hdr) 460250199Sgrehan{ 461250199Sgrehan hv_vmbus_channel_offer_channel* offer; 462250199Sgrehan hv_vmbus_channel* new_channel; 463250199Sgrehan 464250199Sgrehan offer = (hv_vmbus_channel_offer_channel*) hdr; 465250199Sgrehan 466250199Sgrehan hv_guid *guidType; 467250199Sgrehan hv_guid *guidInstance; 468250199Sgrehan 469250199Sgrehan guidType = &offer->offer.interface_type; 470250199Sgrehan guidInstance = &offer->offer.interface_instance; 471250199Sgrehan 472250199Sgrehan /* Allocate the channel object and save this offer */ 473250199Sgrehan new_channel = hv_vmbus_allocate_channel(); 474250199Sgrehan if (new_channel == NULL) 475250199Sgrehan return; 476250199Sgrehan 477283280Swhu /* 478283280Swhu * By default we setup state to enable batched 479283280Swhu * reading. A specific service can choose to 480283280Swhu * disable this prior to opening the channel. 481283280Swhu */ 482283280Swhu new_channel->batched_reading = TRUE; 483283280Swhu 484283280Swhu new_channel->signal_event_param = 485283280Swhu (hv_vmbus_input_signal_event *) 486283280Swhu (HV_ALIGN_UP((unsigned long) 487283280Swhu &new_channel->signal_event_buffer, 488283280Swhu HV_HYPERCALL_PARAM_ALIGN)); 489283280Swhu 490283280Swhu new_channel->signal_event_param->connection_id.as_uint32_t = 0; 491283280Swhu new_channel->signal_event_param->connection_id.u.id = 492283280Swhu HV_VMBUS_EVENT_CONNECTION_ID; 493283280Swhu new_channel->signal_event_param->flag_number = 0; 494283280Swhu new_channel->signal_event_param->rsvd_z = 0; 495283280Swhu 496283280Swhu if (hv_vmbus_protocal_version != HV_VMBUS_VERSION_WS2008) { 497283280Swhu new_channel->is_dedicated_interrupt = 498283280Swhu (offer->is_dedicated_interrupt != 0); 499283280Swhu new_channel->signal_event_param->connection_id.u.id = 500283280Swhu offer->connection_id; 501283280Swhu } 502283280Swhu 503283280Swhu /* 504283280Swhu * Bind the channel to a chosen cpu. 505283280Swhu */ 506283280Swhu vmbus_channel_select_cpu(new_channel, 507283280Swhu &offer->offer.interface_type); 508283280Swhu 509250199Sgrehan memcpy(&new_channel->offer_msg, offer, 510250199Sgrehan sizeof(hv_vmbus_channel_offer_channel)); 511250199Sgrehan new_channel->monitor_group = (uint8_t) offer->monitor_id / 32; 512250199Sgrehan new_channel->monitor_bit = (uint8_t) offer->monitor_id % 32; 513250199Sgrehan 514293820Sdelphij vmbus_channel_process_offer(new_channel); 515250199Sgrehan} 516250199Sgrehan 517250199Sgrehan/** 518250199Sgrehan * @brief Rescind offer handler. 519250199Sgrehan * 520250199Sgrehan * We queue a work item to process this offer 521250199Sgrehan * synchronously 522250199Sgrehan */ 523250199Sgrehanstatic void 524250199Sgrehanvmbus_channel_on_offer_rescind(hv_vmbus_channel_msg_header* hdr) 525250199Sgrehan{ 526250199Sgrehan hv_vmbus_channel_rescind_offer* rescind; 527250199Sgrehan hv_vmbus_channel* channel; 528250199Sgrehan 529250199Sgrehan rescind = (hv_vmbus_channel_rescind_offer*) hdr; 530250199Sgrehan 531295948Ssephe channel = hv_vmbus_g_connection.channels[rescind->child_rel_id]; 532250199Sgrehan if (channel == NULL) 533250199Sgrehan return; 534250199Sgrehan 535293820Sdelphij hv_vmbus_child_device_unregister(channel->device); 536295948Ssephe mtx_lock(&hv_vmbus_g_connection.channel_lock); 537295948Ssephe hv_vmbus_g_connection.channels[rescind->child_rel_id] = NULL; 538295948Ssephe mtx_unlock(&hv_vmbus_g_connection.channel_lock); 539250199Sgrehan} 540250199Sgrehan 541250199Sgrehan/** 542250199Sgrehan * 543250199Sgrehan * @brief Invoked when all offers have been delivered. 544250199Sgrehan */ 545250199Sgrehanstatic void 546250199Sgrehanvmbus_channel_on_offers_delivered(hv_vmbus_channel_msg_header* hdr) 547250199Sgrehan{ 548250199Sgrehan} 549250199Sgrehan 550250199Sgrehan/** 551250199Sgrehan * @brief Open result handler. 552250199Sgrehan * 553250199Sgrehan * This is invoked when we received a response 554250199Sgrehan * to our channel open request. Find the matching request, copy the 555250199Sgrehan * response and signal the requesting thread. 556250199Sgrehan */ 557250199Sgrehanstatic void 558250199Sgrehanvmbus_channel_on_open_result(hv_vmbus_channel_msg_header* hdr) 559250199Sgrehan{ 560250199Sgrehan hv_vmbus_channel_open_result* result; 561250199Sgrehan hv_vmbus_channel_msg_info* msg_info; 562250199Sgrehan hv_vmbus_channel_msg_header* requestHeader; 563250199Sgrehan hv_vmbus_channel_open_channel* openMsg; 564250199Sgrehan 565250199Sgrehan result = (hv_vmbus_channel_open_result*) hdr; 566250199Sgrehan 567250199Sgrehan /* 568250199Sgrehan * Find the open msg, copy the result and signal/unblock the wait event 569250199Sgrehan */ 570303984Sglebius mtx_lock(&hv_vmbus_g_connection.channel_msg_lock); 571250199Sgrehan 572250199Sgrehan TAILQ_FOREACH(msg_info, &hv_vmbus_g_connection.channel_msg_anchor, 573250199Sgrehan msg_list_entry) { 574250199Sgrehan requestHeader = (hv_vmbus_channel_msg_header*) msg_info->msg; 575250199Sgrehan 576250199Sgrehan if (requestHeader->message_type == 577250199Sgrehan HV_CHANNEL_MESSAGE_OPEN_CHANNEL) { 578250199Sgrehan openMsg = (hv_vmbus_channel_open_channel*) msg_info->msg; 579250199Sgrehan if (openMsg->child_rel_id == result->child_rel_id 580250199Sgrehan && openMsg->open_id == result->open_id) { 581250199Sgrehan memcpy(&msg_info->response.open_result, result, 582250199Sgrehan sizeof(hv_vmbus_channel_open_result)); 583250199Sgrehan sema_post(&msg_info->wait_sema); 584250199Sgrehan break; 585250199Sgrehan } 586250199Sgrehan } 587250199Sgrehan } 588303984Sglebius mtx_unlock(&hv_vmbus_g_connection.channel_msg_lock); 589250199Sgrehan 590250199Sgrehan} 591250199Sgrehan 592250199Sgrehan/** 593250199Sgrehan * @brief GPADL created handler. 594250199Sgrehan * 595250199Sgrehan * This is invoked when we received a response 596250199Sgrehan * to our gpadl create request. Find the matching request, copy the 597250199Sgrehan * response and signal the requesting thread. 598250199Sgrehan */ 599250199Sgrehanstatic void 600250199Sgrehanvmbus_channel_on_gpadl_created(hv_vmbus_channel_msg_header* hdr) 601250199Sgrehan{ 602250199Sgrehan hv_vmbus_channel_gpadl_created* gpadl_created; 603250199Sgrehan hv_vmbus_channel_msg_info* msg_info; 604250199Sgrehan hv_vmbus_channel_msg_header* request_header; 605250199Sgrehan hv_vmbus_channel_gpadl_header* gpadl_header; 606250199Sgrehan 607250199Sgrehan gpadl_created = (hv_vmbus_channel_gpadl_created*) hdr; 608250199Sgrehan 609250199Sgrehan /* Find the establish msg, copy the result and signal/unblock 610250199Sgrehan * the wait event 611250199Sgrehan */ 612303984Sglebius mtx_lock(&hv_vmbus_g_connection.channel_msg_lock); 613250199Sgrehan TAILQ_FOREACH(msg_info, &hv_vmbus_g_connection.channel_msg_anchor, 614250199Sgrehan msg_list_entry) { 615250199Sgrehan request_header = (hv_vmbus_channel_msg_header*) msg_info->msg; 616250199Sgrehan if (request_header->message_type == 617250199Sgrehan HV_CHANNEL_MESSAGEL_GPADL_HEADER) { 618250199Sgrehan gpadl_header = 619250199Sgrehan (hv_vmbus_channel_gpadl_header*) request_header; 620250199Sgrehan 621250199Sgrehan if ((gpadl_created->child_rel_id == gpadl_header->child_rel_id) 622250199Sgrehan && (gpadl_created->gpadl == gpadl_header->gpadl)) { 623250199Sgrehan memcpy(&msg_info->response.gpadl_created, 624250199Sgrehan gpadl_created, 625250199Sgrehan sizeof(hv_vmbus_channel_gpadl_created)); 626250199Sgrehan sema_post(&msg_info->wait_sema); 627250199Sgrehan break; 628250199Sgrehan } 629250199Sgrehan } 630250199Sgrehan } 631303984Sglebius mtx_unlock(&hv_vmbus_g_connection.channel_msg_lock); 632250199Sgrehan} 633250199Sgrehan 634250199Sgrehan/** 635250199Sgrehan * @brief GPADL torndown handler. 636250199Sgrehan * 637250199Sgrehan * This is invoked when we received a respons 638250199Sgrehan * to our gpadl teardown request. Find the matching request, copy the 639250199Sgrehan * response and signal the requesting thread 640250199Sgrehan */ 641250199Sgrehanstatic void 642250199Sgrehanvmbus_channel_on_gpadl_torndown(hv_vmbus_channel_msg_header* hdr) 643250199Sgrehan{ 644250199Sgrehan hv_vmbus_channel_gpadl_torndown* gpadl_torndown; 645250199Sgrehan hv_vmbus_channel_msg_info* msg_info; 646250199Sgrehan hv_vmbus_channel_msg_header* requestHeader; 647250199Sgrehan hv_vmbus_channel_gpadl_teardown* gpadlTeardown; 648250199Sgrehan 649250199Sgrehan gpadl_torndown = (hv_vmbus_channel_gpadl_torndown*)hdr; 650250199Sgrehan 651250199Sgrehan /* 652250199Sgrehan * Find the open msg, copy the result and signal/unblock the 653250199Sgrehan * wait event. 654250199Sgrehan */ 655250199Sgrehan 656303984Sglebius mtx_lock(&hv_vmbus_g_connection.channel_msg_lock); 657250199Sgrehan 658250199Sgrehan TAILQ_FOREACH(msg_info, &hv_vmbus_g_connection.channel_msg_anchor, 659250199Sgrehan msg_list_entry) { 660250199Sgrehan requestHeader = (hv_vmbus_channel_msg_header*) msg_info->msg; 661250199Sgrehan 662250199Sgrehan if (requestHeader->message_type 663250199Sgrehan == HV_CHANNEL_MESSAGE_GPADL_TEARDOWN) { 664250199Sgrehan gpadlTeardown = 665250199Sgrehan (hv_vmbus_channel_gpadl_teardown*) requestHeader; 666250199Sgrehan 667250199Sgrehan if (gpadl_torndown->gpadl == gpadlTeardown->gpadl) { 668250199Sgrehan memcpy(&msg_info->response.gpadl_torndown, 669250199Sgrehan gpadl_torndown, 670250199Sgrehan sizeof(hv_vmbus_channel_gpadl_torndown)); 671250199Sgrehan sema_post(&msg_info->wait_sema); 672250199Sgrehan break; 673250199Sgrehan } 674250199Sgrehan } 675250199Sgrehan } 676303984Sglebius mtx_unlock(&hv_vmbus_g_connection.channel_msg_lock); 677250199Sgrehan} 678250199Sgrehan 679250199Sgrehan/** 680250199Sgrehan * @brief Version response handler. 681250199Sgrehan * 682250199Sgrehan * This is invoked when we received a response 683250199Sgrehan * to our initiate contact request. Find the matching request, copy th 684250199Sgrehan * response and signal the requesting thread. 685250199Sgrehan */ 686250199Sgrehanstatic void 687250199Sgrehanvmbus_channel_on_version_response(hv_vmbus_channel_msg_header* hdr) 688250199Sgrehan{ 689250199Sgrehan hv_vmbus_channel_msg_info* msg_info; 690250199Sgrehan hv_vmbus_channel_msg_header* requestHeader; 691250199Sgrehan hv_vmbus_channel_initiate_contact* initiate; 692250199Sgrehan hv_vmbus_channel_version_response* versionResponse; 693250199Sgrehan 694250199Sgrehan versionResponse = (hv_vmbus_channel_version_response*)hdr; 695250199Sgrehan 696303984Sglebius mtx_lock(&hv_vmbus_g_connection.channel_msg_lock); 697250199Sgrehan TAILQ_FOREACH(msg_info, &hv_vmbus_g_connection.channel_msg_anchor, 698250199Sgrehan msg_list_entry) { 699250199Sgrehan requestHeader = (hv_vmbus_channel_msg_header*) msg_info->msg; 700250199Sgrehan if (requestHeader->message_type 701250199Sgrehan == HV_CHANNEL_MESSAGE_INITIATED_CONTACT) { 702250199Sgrehan initiate = 703250199Sgrehan (hv_vmbus_channel_initiate_contact*) requestHeader; 704250199Sgrehan memcpy(&msg_info->response.version_response, 705250199Sgrehan versionResponse, 706250199Sgrehan sizeof(hv_vmbus_channel_version_response)); 707250199Sgrehan sema_post(&msg_info->wait_sema); 708250199Sgrehan } 709250199Sgrehan } 710303984Sglebius mtx_unlock(&hv_vmbus_g_connection.channel_msg_lock); 711250199Sgrehan 712250199Sgrehan} 713250199Sgrehan 714250199Sgrehan/** 715250199Sgrehan * @brief Handler for channel protocol messages. 716250199Sgrehan * 717250199Sgrehan * This is invoked in the vmbus worker thread context. 718250199Sgrehan */ 719250199Sgrehanvoid 720250199Sgrehanhv_vmbus_on_channel_message(void *context) 721250199Sgrehan{ 722250199Sgrehan hv_vmbus_message* msg; 723250199Sgrehan hv_vmbus_channel_msg_header* hdr; 724250199Sgrehan int size; 725250199Sgrehan 726250199Sgrehan msg = (hv_vmbus_message*) context; 727250199Sgrehan hdr = (hv_vmbus_channel_msg_header*) msg->u.payload; 728250199Sgrehan size = msg->header.payload_size; 729250199Sgrehan 730250199Sgrehan if (hdr->message_type >= HV_CHANNEL_MESSAGE_COUNT) { 731250199Sgrehan free(msg, M_DEVBUF); 732250199Sgrehan return; 733250199Sgrehan } 734250199Sgrehan 735250199Sgrehan if (g_channel_message_table[hdr->message_type].messageHandler) { 736250199Sgrehan g_channel_message_table[hdr->message_type].messageHandler(hdr); 737250199Sgrehan } 738250199Sgrehan 739250199Sgrehan /* Free the msg that was allocated in VmbusOnMsgDPC() */ 740250199Sgrehan free(msg, M_DEVBUF); 741250199Sgrehan} 742250199Sgrehan 743250199Sgrehan/** 744250199Sgrehan * @brief Send a request to get all our pending offers. 745250199Sgrehan */ 746250199Sgrehanint 747250199Sgrehanhv_vmbus_request_channel_offers(void) 748250199Sgrehan{ 749250199Sgrehan int ret; 750250199Sgrehan hv_vmbus_channel_msg_header* msg; 751250199Sgrehan hv_vmbus_channel_msg_info* msg_info; 752250199Sgrehan 753250199Sgrehan msg_info = (hv_vmbus_channel_msg_info *) 754250199Sgrehan malloc(sizeof(hv_vmbus_channel_msg_info) 755250199Sgrehan + sizeof(hv_vmbus_channel_msg_header), M_DEVBUF, M_NOWAIT); 756250199Sgrehan 757250199Sgrehan if (msg_info == NULL) { 758250199Sgrehan if(bootverbose) 759250199Sgrehan printf("Error VMBUS: malloc failed for Request Offers\n"); 760250199Sgrehan return (ENOMEM); 761250199Sgrehan } 762250199Sgrehan 763250199Sgrehan msg = (hv_vmbus_channel_msg_header*) msg_info->msg; 764250199Sgrehan msg->message_type = HV_CHANNEL_MESSAGE_REQUEST_OFFERS; 765250199Sgrehan 766250199Sgrehan ret = hv_vmbus_post_message(msg, sizeof(hv_vmbus_channel_msg_header)); 767250199Sgrehan 768250199Sgrehan if (msg_info) 769250199Sgrehan free(msg_info, M_DEVBUF); 770250199Sgrehan 771250199Sgrehan return (ret); 772250199Sgrehan} 773250199Sgrehan 774250199Sgrehan/** 775250199Sgrehan * @brief Release channels that are unattached/unconnected (i.e., no drivers associated) 776250199Sgrehan */ 777250199Sgrehanvoid 778250199Sgrehanhv_vmbus_release_unattached_channels(void) 779250199Sgrehan{ 780250199Sgrehan hv_vmbus_channel *channel; 781250199Sgrehan 782283280Swhu mtx_lock(&hv_vmbus_g_connection.channel_lock); 783250199Sgrehan 784250199Sgrehan while (!TAILQ_EMPTY(&hv_vmbus_g_connection.channel_anchor)) { 785250199Sgrehan channel = TAILQ_FIRST(&hv_vmbus_g_connection.channel_anchor); 786250199Sgrehan TAILQ_REMOVE(&hv_vmbus_g_connection.channel_anchor, 787250199Sgrehan channel, list_entry); 788250199Sgrehan 789250199Sgrehan hv_vmbus_child_device_unregister(channel->device); 790250199Sgrehan hv_vmbus_free_vmbus_channel(channel); 791250199Sgrehan } 792295948Ssephe bzero(hv_vmbus_g_connection.channels, 793295948Ssephe sizeof(hv_vmbus_channel*) * HV_CHANNEL_MAX_COUNT); 794283280Swhu mtx_unlock(&hv_vmbus_g_connection.channel_lock); 795250199Sgrehan} 796283280Swhu 797283280Swhu/** 798283280Swhu * @brief Select the best outgoing channel 799283280Swhu * 800283280Swhu * The channel whose vcpu binding is closest to the currect vcpu will 801283280Swhu * be selected. 802283280Swhu * If no multi-channel, always select primary channel 803283280Swhu * 804283280Swhu * @param primary - primary channel 805283280Swhu */ 806283280Swhustruct hv_vmbus_channel * 807283280Swhuvmbus_select_outgoing_channel(struct hv_vmbus_channel *primary) 808283280Swhu{ 809283280Swhu hv_vmbus_channel *new_channel = NULL; 810283280Swhu hv_vmbus_channel *outgoing_channel = primary; 811283280Swhu int old_cpu_distance = 0; 812283280Swhu int new_cpu_distance = 0; 813283280Swhu int cur_vcpu = 0; 814283280Swhu int smp_pro_id = PCPU_GET(cpuid); 815283280Swhu 816283280Swhu if (TAILQ_EMPTY(&primary->sc_list_anchor)) { 817283280Swhu return outgoing_channel; 818283280Swhu } 819283280Swhu 820283280Swhu if (smp_pro_id >= MAXCPU) { 821283280Swhu return outgoing_channel; 822283280Swhu } 823283280Swhu 824283280Swhu cur_vcpu = hv_vmbus_g_context.hv_vcpu_index[smp_pro_id]; 825283280Swhu 826283280Swhu TAILQ_FOREACH(new_channel, &primary->sc_list_anchor, sc_list_entry) { 827283280Swhu if (new_channel->state != HV_CHANNEL_OPENED_STATE){ 828283280Swhu continue; 829283280Swhu } 830283280Swhu 831283280Swhu if (new_channel->target_vcpu == cur_vcpu){ 832283280Swhu return new_channel; 833283280Swhu } 834283280Swhu 835283280Swhu old_cpu_distance = ((outgoing_channel->target_vcpu > cur_vcpu) ? 836283280Swhu (outgoing_channel->target_vcpu - cur_vcpu) : 837283280Swhu (cur_vcpu - outgoing_channel->target_vcpu)); 838283280Swhu 839283280Swhu new_cpu_distance = ((new_channel->target_vcpu > cur_vcpu) ? 840283280Swhu (new_channel->target_vcpu - cur_vcpu) : 841283280Swhu (cur_vcpu - new_channel->target_vcpu)); 842283280Swhu 843283280Swhu if (old_cpu_distance < new_cpu_distance) { 844283280Swhu continue; 845283280Swhu } 846283280Swhu 847283280Swhu outgoing_channel = new_channel; 848283280Swhu } 849283280Swhu 850283280Swhu return(outgoing_channel); 851283280Swhu} 852