vmbus_chan.c revision 300646
1250199Sgrehan/*- 2298446Ssephe * Copyright (c) 2009-2012,2016 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 29256276Sdim#include <sys/cdefs.h> 30256276Sdim__FBSDID("$FreeBSD: head/sys/dev/hyperv/vmbus/hv_channel.c 300646 2016-05-25 04:59:20Z sephe $"); 31256276Sdim 32250199Sgrehan#include <sys/param.h> 33296028Ssephe#include <sys/kernel.h> 34250199Sgrehan#include <sys/malloc.h> 35250199Sgrehan#include <sys/systm.h> 36250199Sgrehan#include <sys/mbuf.h> 37250199Sgrehan#include <sys/lock.h> 38250199Sgrehan#include <sys/mutex.h> 39296181Ssephe#include <sys/sysctl.h> 40250199Sgrehan#include <machine/bus.h> 41250199Sgrehan#include <vm/vm.h> 42250199Sgrehan#include <vm/vm_param.h> 43250199Sgrehan#include <vm/pmap.h> 44250199Sgrehan 45300102Ssephe#include <dev/hyperv/vmbus/hv_vmbus_priv.h> 46300102Ssephe#include <dev/hyperv/vmbus/vmbus_var.h> 47250199Sgrehan 48250199Sgrehanstatic int vmbus_channel_create_gpadl_header( 49250199Sgrehan /* must be phys and virt contiguous*/ 50250199Sgrehan void* contig_buffer, 51250199Sgrehan /* page-size multiple */ 52250199Sgrehan uint32_t size, 53250199Sgrehan hv_vmbus_channel_msg_info** msg_info, 54250199Sgrehan uint32_t* message_count); 55250199Sgrehan 56250199Sgrehanstatic void vmbus_channel_set_event(hv_vmbus_channel* channel); 57294886Ssephestatic void VmbusProcessChannelEvent(void* channel, int pending); 58250199Sgrehan 59250199Sgrehan/** 60250199Sgrehan * @brief Trigger an event notification on the specified channel 61250199Sgrehan */ 62250199Sgrehanstatic void 63250199Sgrehanvmbus_channel_set_event(hv_vmbus_channel *channel) 64250199Sgrehan{ 65250199Sgrehan hv_vmbus_monitor_page *monitor_page; 66250199Sgrehan 67250199Sgrehan if (channel->offer_msg.monitor_allocated) { 68250199Sgrehan /* Each uint32_t represents 32 channels */ 69250199Sgrehan synch_set_bit((channel->offer_msg.child_rel_id & 31), 70250199Sgrehan ((uint32_t *)hv_vmbus_g_connection.send_interrupt_page 71250199Sgrehan + ((channel->offer_msg.child_rel_id >> 5)))); 72250199Sgrehan 73250199Sgrehan monitor_page = (hv_vmbus_monitor_page *) 74295309Ssephe hv_vmbus_g_connection.monitor_page_2; 75250199Sgrehan 76250199Sgrehan synch_set_bit(channel->monitor_bit, 77250199Sgrehan (uint32_t *)&monitor_page-> 78256276Sdim trigger_group[channel->monitor_group].u.pending); 79250199Sgrehan } else { 80282212Swhu hv_vmbus_set_event(channel); 81250199Sgrehan } 82250199Sgrehan 83250199Sgrehan} 84250199Sgrehan 85296289Ssephestatic int 86296289Ssephevmbus_channel_sysctl_monalloc(SYSCTL_HANDLER_ARGS) 87296289Ssephe{ 88296289Ssephe struct hv_vmbus_channel *chan = arg1; 89296289Ssephe int alloc = 0; 90296289Ssephe 91296289Ssephe if (chan->offer_msg.monitor_allocated) 92296289Ssephe alloc = 1; 93296289Ssephe return sysctl_handle_int(oidp, &alloc, 0, req); 94296289Ssephe} 95296289Ssephe 96296181Ssephestatic void 97296290Ssephevmbus_channel_sysctl_create(hv_vmbus_channel* channel) 98296181Ssephe{ 99296181Ssephe device_t dev; 100296181Ssephe struct sysctl_oid *devch_sysctl; 101296181Ssephe struct sysctl_oid *devch_id_sysctl, *devch_sub_sysctl; 102296181Ssephe struct sysctl_oid *devch_id_in_sysctl, *devch_id_out_sysctl; 103296181Ssephe struct sysctl_ctx_list *ctx; 104296181Ssephe uint32_t ch_id; 105296181Ssephe uint16_t sub_ch_id; 106296181Ssephe char name[16]; 107296181Ssephe 108296181Ssephe hv_vmbus_channel* primary_ch = channel->primary_channel; 109296181Ssephe 110296181Ssephe if (primary_ch == NULL) { 111296181Ssephe dev = channel->device->device; 112296181Ssephe ch_id = channel->offer_msg.child_rel_id; 113296181Ssephe } else { 114296181Ssephe dev = primary_ch->device->device; 115296181Ssephe ch_id = primary_ch->offer_msg.child_rel_id; 116296181Ssephe sub_ch_id = channel->offer_msg.offer.sub_channel_index; 117296181Ssephe } 118296181Ssephe ctx = device_get_sysctl_ctx(dev); 119296181Ssephe /* This creates dev.DEVNAME.DEVUNIT.channel tree */ 120296181Ssephe devch_sysctl = SYSCTL_ADD_NODE(ctx, 121296181Ssephe SYSCTL_CHILDREN(device_get_sysctl_tree(dev)), 122298693Ssephe OID_AUTO, "channel", CTLFLAG_RD | CTLFLAG_MPSAFE, 0, ""); 123296181Ssephe /* This creates dev.DEVNAME.DEVUNIT.channel.CHANID tree */ 124296181Ssephe snprintf(name, sizeof(name), "%d", ch_id); 125296181Ssephe devch_id_sysctl = SYSCTL_ADD_NODE(ctx, 126296181Ssephe SYSCTL_CHILDREN(devch_sysctl), 127298693Ssephe OID_AUTO, name, CTLFLAG_RD | CTLFLAG_MPSAFE, 0, ""); 128296181Ssephe 129296181Ssephe if (primary_ch != NULL) { 130296181Ssephe devch_sub_sysctl = SYSCTL_ADD_NODE(ctx, 131296181Ssephe SYSCTL_CHILDREN(devch_id_sysctl), 132298693Ssephe OID_AUTO, "sub", CTLFLAG_RD | CTLFLAG_MPSAFE, 0, ""); 133296181Ssephe snprintf(name, sizeof(name), "%d", sub_ch_id); 134296181Ssephe devch_id_sysctl = SYSCTL_ADD_NODE(ctx, 135296181Ssephe SYSCTL_CHILDREN(devch_sub_sysctl), 136298693Ssephe OID_AUTO, name, CTLFLAG_RD | CTLFLAG_MPSAFE, 0, ""); 137296188Ssephe 138296188Ssephe SYSCTL_ADD_UINT(ctx, SYSCTL_CHILDREN(devch_id_sysctl), 139296188Ssephe OID_AUTO, "chanid", CTLFLAG_RD, 140296188Ssephe &channel->offer_msg.child_rel_id, 0, "channel id"); 141296181Ssephe } 142296188Ssephe SYSCTL_ADD_UINT(ctx, SYSCTL_CHILDREN(devch_id_sysctl), OID_AUTO, 143296188Ssephe "cpu", CTLFLAG_RD, &channel->target_cpu, 0, "owner CPU id"); 144296289Ssephe SYSCTL_ADD_PROC(ctx, SYSCTL_CHILDREN(devch_id_sysctl), OID_AUTO, 145298693Ssephe "monitor_allocated", CTLTYPE_INT | CTLFLAG_RD | CTLFLAG_MPSAFE, 146298693Ssephe channel, 0, vmbus_channel_sysctl_monalloc, "I", 147296289Ssephe "is monitor allocated to this channel"); 148296188Ssephe 149296181Ssephe devch_id_in_sysctl = SYSCTL_ADD_NODE(ctx, 150296181Ssephe SYSCTL_CHILDREN(devch_id_sysctl), 151296181Ssephe OID_AUTO, 152296181Ssephe "in", 153298693Ssephe CTLFLAG_RD | CTLFLAG_MPSAFE, 0, ""); 154296181Ssephe devch_id_out_sysctl = SYSCTL_ADD_NODE(ctx, 155296181Ssephe SYSCTL_CHILDREN(devch_id_sysctl), 156296181Ssephe OID_AUTO, 157296181Ssephe "out", 158298693Ssephe CTLFLAG_RD | CTLFLAG_MPSAFE, 0, ""); 159296181Ssephe hv_ring_buffer_stat(ctx, 160296181Ssephe SYSCTL_CHILDREN(devch_id_in_sysctl), 161296181Ssephe &(channel->inbound), 162296181Ssephe "inbound ring buffer stats"); 163296181Ssephe hv_ring_buffer_stat(ctx, 164296181Ssephe SYSCTL_CHILDREN(devch_id_out_sysctl), 165296181Ssephe &(channel->outbound), 166296181Ssephe "outbound ring buffer stats"); 167296181Ssephe} 168296290Ssephe 169250199Sgrehan/** 170250199Sgrehan * @brief Open the specified channel 171250199Sgrehan */ 172250199Sgrehanint 173250199Sgrehanhv_vmbus_channel_open( 174250199Sgrehan hv_vmbus_channel* new_channel, 175250199Sgrehan uint32_t send_ring_buffer_size, 176250199Sgrehan uint32_t recv_ring_buffer_size, 177250199Sgrehan void* user_data, 178250199Sgrehan uint32_t user_data_len, 179250199Sgrehan hv_vmbus_pfn_channel_callback pfn_on_channel_callback, 180250199Sgrehan void* context) 181250199Sgrehan{ 182250199Sgrehan 183250199Sgrehan int ret = 0; 184250199Sgrehan void *in, *out; 185250199Sgrehan hv_vmbus_channel_open_channel* open_msg; 186250199Sgrehan hv_vmbus_channel_msg_info* open_info; 187250199Sgrehan 188282212Swhu mtx_lock(&new_channel->sc_lock); 189282212Swhu if (new_channel->state == HV_CHANNEL_OPEN_STATE) { 190282212Swhu new_channel->state = HV_CHANNEL_OPENING_STATE; 191282212Swhu } else { 192282212Swhu mtx_unlock(&new_channel->sc_lock); 193282212Swhu if(bootverbose) 194282212Swhu printf("VMBUS: Trying to open channel <%p> which in " 195282212Swhu "%d state.\n", new_channel, new_channel->state); 196282212Swhu return (EINVAL); 197282212Swhu } 198282212Swhu mtx_unlock(&new_channel->sc_lock); 199282212Swhu 200250199Sgrehan new_channel->on_channel_callback = pfn_on_channel_callback; 201250199Sgrehan new_channel->channel_callback_context = context; 202250199Sgrehan 203300102Ssephe vmbus_on_channel_open(new_channel); 204300102Ssephe 205300646Ssephe new_channel->rxq = VMBUS_PCPU_GET(vmbus_get_softc(), event_tq, 206300646Ssephe new_channel->target_cpu); 207294886Ssephe TASK_INIT(&new_channel->channel_task, 0, VmbusProcessChannelEvent, new_channel); 208294886Ssephe 209250199Sgrehan /* Allocate the ring buffer */ 210250199Sgrehan out = contigmalloc((send_ring_buffer_size + recv_ring_buffer_size), 211256350Sgrehan M_DEVBUF, M_ZERO, 0UL, BUS_SPACE_MAXADDR, PAGE_SIZE, 0); 212250199Sgrehan KASSERT(out != NULL, 213250199Sgrehan ("Error VMBUS: contigmalloc failed to allocate Ring Buffer!")); 214250199Sgrehan if (out == NULL) 215256350Sgrehan return (ENOMEM); 216250199Sgrehan 217250199Sgrehan in = ((uint8_t *) out + send_ring_buffer_size); 218250199Sgrehan 219250199Sgrehan new_channel->ring_buffer_pages = out; 220256350Sgrehan new_channel->ring_buffer_page_count = (send_ring_buffer_size + 221256350Sgrehan recv_ring_buffer_size) >> PAGE_SHIFT; 222256350Sgrehan new_channel->ring_buffer_size = send_ring_buffer_size + 223256350Sgrehan recv_ring_buffer_size; 224250199Sgrehan 225250199Sgrehan hv_vmbus_ring_buffer_init( 226250199Sgrehan &new_channel->outbound, 227250199Sgrehan out, 228250199Sgrehan send_ring_buffer_size); 229250199Sgrehan 230250199Sgrehan hv_vmbus_ring_buffer_init( 231250199Sgrehan &new_channel->inbound, 232250199Sgrehan in, 233250199Sgrehan recv_ring_buffer_size); 234250199Sgrehan 235296290Ssephe /* Create sysctl tree for this channel */ 236296290Ssephe vmbus_channel_sysctl_create(new_channel); 237296181Ssephe 238250199Sgrehan /** 239250199Sgrehan * Establish the gpadl for the ring buffer 240250199Sgrehan */ 241250199Sgrehan new_channel->ring_buffer_gpadl_handle = 0; 242250199Sgrehan 243250199Sgrehan ret = hv_vmbus_channel_establish_gpadl(new_channel, 244250199Sgrehan new_channel->outbound.ring_buffer, 245250199Sgrehan send_ring_buffer_size + recv_ring_buffer_size, 246250199Sgrehan &new_channel->ring_buffer_gpadl_handle); 247250199Sgrehan 248250199Sgrehan /** 249250199Sgrehan * Create and init the channel open message 250250199Sgrehan */ 251250199Sgrehan open_info = (hv_vmbus_channel_msg_info*) malloc( 252250199Sgrehan sizeof(hv_vmbus_channel_msg_info) + 253250199Sgrehan sizeof(hv_vmbus_channel_open_channel), 254250199Sgrehan M_DEVBUF, 255250199Sgrehan M_NOWAIT); 256250199Sgrehan KASSERT(open_info != NULL, 257250199Sgrehan ("Error VMBUS: malloc failed to allocate Open Channel message!")); 258250199Sgrehan 259250199Sgrehan if (open_info == NULL) 260250199Sgrehan return (ENOMEM); 261250199Sgrehan 262250199Sgrehan sema_init(&open_info->wait_sema, 0, "Open Info Sema"); 263250199Sgrehan 264250199Sgrehan open_msg = (hv_vmbus_channel_open_channel*) open_info->msg; 265250199Sgrehan open_msg->header.message_type = HV_CHANNEL_MESSAGE_OPEN_CHANNEL; 266250199Sgrehan open_msg->open_id = new_channel->offer_msg.child_rel_id; 267250199Sgrehan open_msg->child_rel_id = new_channel->offer_msg.child_rel_id; 268250199Sgrehan open_msg->ring_buffer_gpadl_handle = 269250199Sgrehan new_channel->ring_buffer_gpadl_handle; 270250199Sgrehan open_msg->downstream_ring_buffer_page_offset = send_ring_buffer_size 271250199Sgrehan >> PAGE_SHIFT; 272282212Swhu open_msg->target_vcpu = new_channel->target_vcpu; 273250199Sgrehan 274250199Sgrehan if (user_data_len) 275250199Sgrehan memcpy(open_msg->user_data, user_data, user_data_len); 276250199Sgrehan 277297635Ssephe mtx_lock(&hv_vmbus_g_connection.channel_msg_lock); 278250199Sgrehan TAILQ_INSERT_TAIL( 279250199Sgrehan &hv_vmbus_g_connection.channel_msg_anchor, 280250199Sgrehan open_info, 281250199Sgrehan msg_list_entry); 282297635Ssephe mtx_unlock(&hv_vmbus_g_connection.channel_msg_lock); 283250199Sgrehan 284250199Sgrehan ret = hv_vmbus_post_message( 285250199Sgrehan open_msg, sizeof(hv_vmbus_channel_open_channel)); 286250199Sgrehan 287250199Sgrehan if (ret != 0) 288250199Sgrehan goto cleanup; 289250199Sgrehan 290296028Ssephe ret = sema_timedwait(&open_info->wait_sema, 5 * hz); /* KYS 5 seconds */ 291250199Sgrehan 292282212Swhu if (ret) { 293282212Swhu if(bootverbose) 294282212Swhu printf("VMBUS: channel <%p> open timeout.\n", new_channel); 295250199Sgrehan goto cleanup; 296282212Swhu } 297250199Sgrehan 298250199Sgrehan if (open_info->response.open_result.status == 0) { 299282212Swhu new_channel->state = HV_CHANNEL_OPENED_STATE; 300250199Sgrehan if(bootverbose) 301250199Sgrehan printf("VMBUS: channel <%p> open success.\n", new_channel); 302250199Sgrehan } else { 303250199Sgrehan if(bootverbose) 304250199Sgrehan printf("Error VMBUS: channel <%p> open failed - %d!\n", 305250199Sgrehan new_channel, open_info->response.open_result.status); 306250199Sgrehan } 307250199Sgrehan 308250199Sgrehan cleanup: 309297635Ssephe mtx_lock(&hv_vmbus_g_connection.channel_msg_lock); 310250199Sgrehan TAILQ_REMOVE( 311250199Sgrehan &hv_vmbus_g_connection.channel_msg_anchor, 312250199Sgrehan open_info, 313250199Sgrehan msg_list_entry); 314297635Ssephe mtx_unlock(&hv_vmbus_g_connection.channel_msg_lock); 315250199Sgrehan sema_destroy(&open_info->wait_sema); 316250199Sgrehan free(open_info, M_DEVBUF); 317250199Sgrehan 318250199Sgrehan return (ret); 319250199Sgrehan} 320250199Sgrehan 321250199Sgrehan/** 322250199Sgrehan * @brief Create a gpadl for the specified buffer 323250199Sgrehan */ 324250199Sgrehanstatic int 325250199Sgrehanvmbus_channel_create_gpadl_header( 326250199Sgrehan void* contig_buffer, 327250199Sgrehan uint32_t size, /* page-size multiple */ 328250199Sgrehan hv_vmbus_channel_msg_info** msg_info, 329250199Sgrehan uint32_t* message_count) 330250199Sgrehan{ 331250199Sgrehan int i; 332250199Sgrehan int page_count; 333250199Sgrehan unsigned long long pfn; 334250199Sgrehan uint32_t msg_size; 335250199Sgrehan hv_vmbus_channel_gpadl_header* gpa_header; 336250199Sgrehan hv_vmbus_channel_gpadl_body* gpadl_body; 337250199Sgrehan hv_vmbus_channel_msg_info* msg_header; 338250199Sgrehan hv_vmbus_channel_msg_info* msg_body; 339250199Sgrehan 340250199Sgrehan int pfnSum, pfnCount, pfnLeft, pfnCurr, pfnSize; 341250199Sgrehan 342250199Sgrehan page_count = size >> PAGE_SHIFT; 343250199Sgrehan pfn = hv_get_phys_addr(contig_buffer) >> PAGE_SHIFT; 344250199Sgrehan 345250199Sgrehan /*do we need a gpadl body msg */ 346250199Sgrehan pfnSize = HV_MAX_SIZE_CHANNEL_MESSAGE 347250199Sgrehan - sizeof(hv_vmbus_channel_gpadl_header) 348250199Sgrehan - sizeof(hv_gpa_range); 349250199Sgrehan pfnCount = pfnSize / sizeof(uint64_t); 350250199Sgrehan 351250199Sgrehan if (page_count > pfnCount) { /* if(we need a gpadl body) */ 352250199Sgrehan /* fill in the header */ 353250199Sgrehan msg_size = sizeof(hv_vmbus_channel_msg_info) 354250199Sgrehan + sizeof(hv_vmbus_channel_gpadl_header) 355250199Sgrehan + sizeof(hv_gpa_range) 356250199Sgrehan + pfnCount * sizeof(uint64_t); 357250199Sgrehan msg_header = malloc(msg_size, M_DEVBUF, M_NOWAIT | M_ZERO); 358250199Sgrehan KASSERT( 359250199Sgrehan msg_header != NULL, 360250199Sgrehan ("Error VMBUS: malloc failed to allocate Gpadl Message!")); 361250199Sgrehan if (msg_header == NULL) 362250199Sgrehan return (ENOMEM); 363250199Sgrehan 364250199Sgrehan TAILQ_INIT(&msg_header->sub_msg_list_anchor); 365250199Sgrehan msg_header->message_size = msg_size; 366250199Sgrehan 367250199Sgrehan gpa_header = (hv_vmbus_channel_gpadl_header*) msg_header->msg; 368250199Sgrehan gpa_header->range_count = 1; 369250199Sgrehan gpa_header->range_buf_len = sizeof(hv_gpa_range) 370250199Sgrehan + page_count * sizeof(uint64_t); 371250199Sgrehan gpa_header->range[0].byte_offset = 0; 372250199Sgrehan gpa_header->range[0].byte_count = size; 373250199Sgrehan for (i = 0; i < pfnCount; i++) { 374250199Sgrehan gpa_header->range[0].pfn_array[i] = pfn + i; 375250199Sgrehan } 376250199Sgrehan *msg_info = msg_header; 377250199Sgrehan *message_count = 1; 378250199Sgrehan 379250199Sgrehan pfnSum = pfnCount; 380250199Sgrehan pfnLeft = page_count - pfnCount; 381250199Sgrehan 382250199Sgrehan /* 383250199Sgrehan * figure out how many pfns we can fit 384250199Sgrehan */ 385250199Sgrehan pfnSize = HV_MAX_SIZE_CHANNEL_MESSAGE 386250199Sgrehan - sizeof(hv_vmbus_channel_gpadl_body); 387250199Sgrehan pfnCount = pfnSize / sizeof(uint64_t); 388250199Sgrehan 389250199Sgrehan /* 390250199Sgrehan * fill in the body 391250199Sgrehan */ 392250199Sgrehan while (pfnLeft) { 393250199Sgrehan if (pfnLeft > pfnCount) { 394250199Sgrehan pfnCurr = pfnCount; 395250199Sgrehan } else { 396250199Sgrehan pfnCurr = pfnLeft; 397250199Sgrehan } 398250199Sgrehan 399250199Sgrehan msg_size = sizeof(hv_vmbus_channel_msg_info) + 400250199Sgrehan sizeof(hv_vmbus_channel_gpadl_body) + 401250199Sgrehan pfnCurr * sizeof(uint64_t); 402250199Sgrehan msg_body = malloc(msg_size, M_DEVBUF, M_NOWAIT | M_ZERO); 403250199Sgrehan KASSERT( 404250199Sgrehan msg_body != NULL, 405250199Sgrehan ("Error VMBUS: malloc failed to allocate Gpadl msg_body!")); 406250199Sgrehan if (msg_body == NULL) 407250199Sgrehan return (ENOMEM); 408250199Sgrehan 409250199Sgrehan msg_body->message_size = msg_size; 410250199Sgrehan (*message_count)++; 411250199Sgrehan gpadl_body = 412250199Sgrehan (hv_vmbus_channel_gpadl_body*) msg_body->msg; 413250199Sgrehan /* 414250199Sgrehan * gpadl_body->gpadl = kbuffer; 415250199Sgrehan */ 416250199Sgrehan for (i = 0; i < pfnCurr; i++) { 417250199Sgrehan gpadl_body->pfn[i] = pfn + pfnSum + i; 418250199Sgrehan } 419250199Sgrehan 420250199Sgrehan TAILQ_INSERT_TAIL( 421250199Sgrehan &msg_header->sub_msg_list_anchor, 422250199Sgrehan msg_body, 423250199Sgrehan msg_list_entry); 424250199Sgrehan pfnSum += pfnCurr; 425250199Sgrehan pfnLeft -= pfnCurr; 426250199Sgrehan } 427250199Sgrehan } else { /* else everything fits in a header */ 428250199Sgrehan 429250199Sgrehan msg_size = sizeof(hv_vmbus_channel_msg_info) + 430250199Sgrehan sizeof(hv_vmbus_channel_gpadl_header) + 431250199Sgrehan sizeof(hv_gpa_range) + 432250199Sgrehan page_count * sizeof(uint64_t); 433250199Sgrehan msg_header = malloc(msg_size, M_DEVBUF, M_NOWAIT | M_ZERO); 434250199Sgrehan KASSERT( 435250199Sgrehan msg_header != NULL, 436250199Sgrehan ("Error VMBUS: malloc failed to allocate Gpadl Message!")); 437250199Sgrehan if (msg_header == NULL) 438250199Sgrehan return (ENOMEM); 439250199Sgrehan 440250199Sgrehan msg_header->message_size = msg_size; 441250199Sgrehan 442250199Sgrehan gpa_header = (hv_vmbus_channel_gpadl_header*) msg_header->msg; 443250199Sgrehan gpa_header->range_count = 1; 444250199Sgrehan gpa_header->range_buf_len = sizeof(hv_gpa_range) + 445250199Sgrehan page_count * sizeof(uint64_t); 446250199Sgrehan gpa_header->range[0].byte_offset = 0; 447250199Sgrehan gpa_header->range[0].byte_count = size; 448250199Sgrehan for (i = 0; i < page_count; i++) { 449250199Sgrehan gpa_header->range[0].pfn_array[i] = pfn + i; 450250199Sgrehan } 451250199Sgrehan 452250199Sgrehan *msg_info = msg_header; 453250199Sgrehan *message_count = 1; 454250199Sgrehan } 455250199Sgrehan 456250199Sgrehan return (0); 457250199Sgrehan} 458250199Sgrehan 459250199Sgrehan/** 460250199Sgrehan * @brief Establish a GPADL for the specified buffer 461250199Sgrehan */ 462250199Sgrehanint 463250199Sgrehanhv_vmbus_channel_establish_gpadl( 464250199Sgrehan hv_vmbus_channel* channel, 465250199Sgrehan void* contig_buffer, 466250199Sgrehan uint32_t size, /* page-size multiple */ 467250199Sgrehan uint32_t* gpadl_handle) 468250199Sgrehan 469250199Sgrehan{ 470250199Sgrehan int ret = 0; 471250199Sgrehan hv_vmbus_channel_gpadl_header* gpadl_msg; 472250199Sgrehan hv_vmbus_channel_gpadl_body* gpadl_body; 473250199Sgrehan hv_vmbus_channel_msg_info* msg_info; 474250199Sgrehan hv_vmbus_channel_msg_info* sub_msg_info; 475250199Sgrehan uint32_t msg_count; 476250199Sgrehan hv_vmbus_channel_msg_info* curr; 477250199Sgrehan uint32_t next_gpadl_handle; 478250199Sgrehan 479296076Ssephe next_gpadl_handle = atomic_fetchadd_int( 480296076Ssephe &hv_vmbus_g_connection.next_gpadl_handle, 1); 481250199Sgrehan 482250199Sgrehan ret = vmbus_channel_create_gpadl_header( 483250199Sgrehan contig_buffer, size, &msg_info, &msg_count); 484250199Sgrehan 485296076Ssephe if(ret != 0) { 486296076Ssephe /* 487296076Ssephe * XXX 488296076Ssephe * We can _not_ even revert the above incremental, 489296076Ssephe * if multiple GPADL establishments are running 490296076Ssephe * parallelly, decrement the global next_gpadl_handle 491296076Ssephe * is calling for _big_ trouble. A better solution 492296076Ssephe * is to have a 0-based GPADL id bitmap ... 493296076Ssephe */ 494296076Ssephe return ret; 495250199Sgrehan } 496250199Sgrehan 497250199Sgrehan sema_init(&msg_info->wait_sema, 0, "Open Info Sema"); 498250199Sgrehan gpadl_msg = (hv_vmbus_channel_gpadl_header*) msg_info->msg; 499250199Sgrehan gpadl_msg->header.message_type = HV_CHANNEL_MESSAGEL_GPADL_HEADER; 500250199Sgrehan gpadl_msg->child_rel_id = channel->offer_msg.child_rel_id; 501250199Sgrehan gpadl_msg->gpadl = next_gpadl_handle; 502250199Sgrehan 503297635Ssephe mtx_lock(&hv_vmbus_g_connection.channel_msg_lock); 504250199Sgrehan TAILQ_INSERT_TAIL( 505250199Sgrehan &hv_vmbus_g_connection.channel_msg_anchor, 506250199Sgrehan msg_info, 507250199Sgrehan msg_list_entry); 508250199Sgrehan 509297635Ssephe mtx_unlock(&hv_vmbus_g_connection.channel_msg_lock); 510250199Sgrehan 511250199Sgrehan ret = hv_vmbus_post_message( 512250199Sgrehan gpadl_msg, 513250199Sgrehan msg_info->message_size - 514250199Sgrehan (uint32_t) sizeof(hv_vmbus_channel_msg_info)); 515250199Sgrehan 516250199Sgrehan if (ret != 0) 517250199Sgrehan goto cleanup; 518250199Sgrehan 519250199Sgrehan if (msg_count > 1) { 520250199Sgrehan TAILQ_FOREACH(curr, 521250199Sgrehan &msg_info->sub_msg_list_anchor, msg_list_entry) { 522250199Sgrehan sub_msg_info = curr; 523250199Sgrehan gpadl_body = 524250199Sgrehan (hv_vmbus_channel_gpadl_body*) sub_msg_info->msg; 525250199Sgrehan 526250199Sgrehan gpadl_body->header.message_type = 527250199Sgrehan HV_CHANNEL_MESSAGE_GPADL_BODY; 528250199Sgrehan gpadl_body->gpadl = next_gpadl_handle; 529250199Sgrehan 530250199Sgrehan ret = hv_vmbus_post_message( 531250199Sgrehan gpadl_body, 532250199Sgrehan sub_msg_info->message_size 533250199Sgrehan - (uint32_t) sizeof(hv_vmbus_channel_msg_info)); 534250199Sgrehan /* if (the post message failed) give up and clean up */ 535250199Sgrehan if(ret != 0) 536250199Sgrehan goto cleanup; 537250199Sgrehan } 538250199Sgrehan } 539250199Sgrehan 540296028Ssephe ret = sema_timedwait(&msg_info->wait_sema, 5 * hz); /* KYS 5 seconds*/ 541250199Sgrehan if (ret != 0) 542250199Sgrehan goto cleanup; 543250199Sgrehan 544250199Sgrehan *gpadl_handle = gpadl_msg->gpadl; 545250199Sgrehan 546250199Sgrehancleanup: 547250199Sgrehan 548297635Ssephe mtx_lock(&hv_vmbus_g_connection.channel_msg_lock); 549250199Sgrehan TAILQ_REMOVE(&hv_vmbus_g_connection.channel_msg_anchor, 550250199Sgrehan msg_info, msg_list_entry); 551297635Ssephe mtx_unlock(&hv_vmbus_g_connection.channel_msg_lock); 552250199Sgrehan 553250199Sgrehan sema_destroy(&msg_info->wait_sema); 554250199Sgrehan free(msg_info, M_DEVBUF); 555250199Sgrehan 556250199Sgrehan return (ret); 557250199Sgrehan} 558250199Sgrehan 559250199Sgrehan/** 560250199Sgrehan * @brief Teardown the specified GPADL handle 561250199Sgrehan */ 562250199Sgrehanint 563250199Sgrehanhv_vmbus_channel_teardown_gpdal( 564250199Sgrehan hv_vmbus_channel* channel, 565250199Sgrehan uint32_t gpadl_handle) 566250199Sgrehan{ 567250199Sgrehan int ret = 0; 568250199Sgrehan hv_vmbus_channel_gpadl_teardown* msg; 569250199Sgrehan hv_vmbus_channel_msg_info* info; 570250199Sgrehan 571250199Sgrehan info = (hv_vmbus_channel_msg_info *) 572250199Sgrehan malloc( sizeof(hv_vmbus_channel_msg_info) + 573250199Sgrehan sizeof(hv_vmbus_channel_gpadl_teardown), 574250199Sgrehan M_DEVBUF, M_NOWAIT); 575250199Sgrehan KASSERT(info != NULL, 576250199Sgrehan ("Error VMBUS: malloc failed to allocate Gpadl Teardown Msg!")); 577250199Sgrehan if (info == NULL) { 578250199Sgrehan ret = ENOMEM; 579250199Sgrehan goto cleanup; 580250199Sgrehan } 581250199Sgrehan 582250199Sgrehan sema_init(&info->wait_sema, 0, "Open Info Sema"); 583250199Sgrehan 584250199Sgrehan msg = (hv_vmbus_channel_gpadl_teardown*) info->msg; 585250199Sgrehan 586250199Sgrehan msg->header.message_type = HV_CHANNEL_MESSAGE_GPADL_TEARDOWN; 587250199Sgrehan msg->child_rel_id = channel->offer_msg.child_rel_id; 588250199Sgrehan msg->gpadl = gpadl_handle; 589250199Sgrehan 590297635Ssephe mtx_lock(&hv_vmbus_g_connection.channel_msg_lock); 591250199Sgrehan TAILQ_INSERT_TAIL(&hv_vmbus_g_connection.channel_msg_anchor, 592250199Sgrehan info, msg_list_entry); 593297635Ssephe mtx_unlock(&hv_vmbus_g_connection.channel_msg_lock); 594250199Sgrehan 595250199Sgrehan ret = hv_vmbus_post_message(msg, 596250199Sgrehan sizeof(hv_vmbus_channel_gpadl_teardown)); 597250199Sgrehan if (ret != 0) 598250199Sgrehan goto cleanup; 599250199Sgrehan 600296028Ssephe ret = sema_timedwait(&info->wait_sema, 5 * hz); /* KYS 5 seconds */ 601250199Sgrehan 602250199Sgrehancleanup: 603250199Sgrehan /* 604250199Sgrehan * Received a torndown response 605250199Sgrehan */ 606297635Ssephe mtx_lock(&hv_vmbus_g_connection.channel_msg_lock); 607250199Sgrehan TAILQ_REMOVE(&hv_vmbus_g_connection.channel_msg_anchor, 608250199Sgrehan info, msg_list_entry); 609297635Ssephe mtx_unlock(&hv_vmbus_g_connection.channel_msg_lock); 610250199Sgrehan sema_destroy(&info->wait_sema); 611250199Sgrehan free(info, M_DEVBUF); 612250199Sgrehan 613250199Sgrehan return (ret); 614250199Sgrehan} 615250199Sgrehan 616282212Swhustatic void 617282212Swhuhv_vmbus_channel_close_internal(hv_vmbus_channel *channel) 618250199Sgrehan{ 619250199Sgrehan int ret = 0; 620294886Ssephe struct taskqueue *rxq = channel->rxq; 621250199Sgrehan hv_vmbus_channel_close_channel* msg; 622250199Sgrehan hv_vmbus_channel_msg_info* info; 623250199Sgrehan 624282212Swhu channel->state = HV_CHANNEL_OPEN_STATE; 625282212Swhu 626282212Swhu /* 627294886Ssephe * set rxq to NULL to avoid more requests be scheduled 628294886Ssephe */ 629294886Ssephe channel->rxq = NULL; 630294886Ssephe taskqueue_drain(rxq, &channel->channel_task); 631250199Sgrehan channel->on_channel_callback = NULL; 632250199Sgrehan 633250199Sgrehan /** 634250199Sgrehan * Send a closing message 635250199Sgrehan */ 636250199Sgrehan info = (hv_vmbus_channel_msg_info *) 637250199Sgrehan malloc( sizeof(hv_vmbus_channel_msg_info) + 638250199Sgrehan sizeof(hv_vmbus_channel_close_channel), 639250199Sgrehan M_DEVBUF, M_NOWAIT); 640250199Sgrehan KASSERT(info != NULL, ("VMBUS: malloc failed hv_vmbus_channel_close!")); 641250199Sgrehan if(info == NULL) 642250199Sgrehan return; 643250199Sgrehan 644250199Sgrehan msg = (hv_vmbus_channel_close_channel*) info->msg; 645250199Sgrehan msg->header.message_type = HV_CHANNEL_MESSAGE_CLOSE_CHANNEL; 646250199Sgrehan msg->child_rel_id = channel->offer_msg.child_rel_id; 647250199Sgrehan 648250199Sgrehan ret = hv_vmbus_post_message( 649250199Sgrehan msg, sizeof(hv_vmbus_channel_close_channel)); 650250199Sgrehan 651250199Sgrehan /* Tear down the gpadl for the channel's ring buffer */ 652250199Sgrehan if (channel->ring_buffer_gpadl_handle) { 653250199Sgrehan hv_vmbus_channel_teardown_gpdal(channel, 654250199Sgrehan channel->ring_buffer_gpadl_handle); 655250199Sgrehan } 656250199Sgrehan 657250199Sgrehan /* TODO: Send a msg to release the childRelId */ 658250199Sgrehan 659250199Sgrehan /* cleanup the ring buffers for this channel */ 660250199Sgrehan hv_ring_buffer_cleanup(&channel->outbound); 661250199Sgrehan hv_ring_buffer_cleanup(&channel->inbound); 662250199Sgrehan 663256350Sgrehan contigfree(channel->ring_buffer_pages, channel->ring_buffer_size, 664256350Sgrehan M_DEVBUF); 665250199Sgrehan 666250199Sgrehan free(info, M_DEVBUF); 667282212Swhu} 668250199Sgrehan 669282212Swhu/** 670282212Swhu * @brief Close the specified channel 671282212Swhu */ 672282212Swhuvoid 673282212Swhuhv_vmbus_channel_close(hv_vmbus_channel *channel) 674282212Swhu{ 675282212Swhu hv_vmbus_channel* sub_channel; 676282212Swhu 677282212Swhu if (channel->primary_channel != NULL) { 678282212Swhu /* 679282212Swhu * We only close multi-channels when the primary is 680282212Swhu * closed. 681282212Swhu */ 682282212Swhu return; 683282212Swhu } 684282212Swhu 685250199Sgrehan /* 686282212Swhu * Close all multi-channels first. 687250199Sgrehan */ 688282212Swhu TAILQ_FOREACH(sub_channel, &channel->sc_list_anchor, 689282212Swhu sc_list_entry) { 690282212Swhu if (sub_channel->state != HV_CHANNEL_OPENED_STATE) 691282212Swhu continue; 692282212Swhu hv_vmbus_channel_close_internal(sub_channel); 693250199Sgrehan } 694282212Swhu /* 695282212Swhu * Then close the primary channel. 696282212Swhu */ 697282212Swhu hv_vmbus_channel_close_internal(channel); 698250199Sgrehan} 699250199Sgrehan 700250199Sgrehan/** 701250199Sgrehan * @brief Send the specified buffer on the given channel 702250199Sgrehan */ 703250199Sgrehanint 704250199Sgrehanhv_vmbus_channel_send_packet( 705250199Sgrehan hv_vmbus_channel* channel, 706250199Sgrehan void* buffer, 707250199Sgrehan uint32_t buffer_len, 708250199Sgrehan uint64_t request_id, 709250199Sgrehan hv_vmbus_packet_type type, 710250199Sgrehan uint32_t flags) 711250199Sgrehan{ 712250199Sgrehan int ret = 0; 713250199Sgrehan hv_vm_packet_descriptor desc; 714250199Sgrehan uint32_t packet_len; 715250199Sgrehan uint64_t aligned_data; 716250199Sgrehan uint32_t packet_len_aligned; 717282212Swhu boolean_t need_sig; 718250199Sgrehan hv_vmbus_sg_buffer_list buffer_list[3]; 719250199Sgrehan 720250199Sgrehan packet_len = sizeof(hv_vm_packet_descriptor) + buffer_len; 721250199Sgrehan packet_len_aligned = HV_ALIGN_UP(packet_len, sizeof(uint64_t)); 722250199Sgrehan aligned_data = 0; 723250199Sgrehan 724250199Sgrehan /* Setup the descriptor */ 725250199Sgrehan desc.type = type; /* HV_VMBUS_PACKET_TYPE_DATA_IN_BAND; */ 726250199Sgrehan desc.flags = flags; /* HV_VMBUS_DATA_PACKET_FLAG_COMPLETION_REQUESTED */ 727250199Sgrehan /* in 8-bytes granularity */ 728250199Sgrehan desc.data_offset8 = sizeof(hv_vm_packet_descriptor) >> 3; 729250199Sgrehan desc.length8 = (uint16_t) (packet_len_aligned >> 3); 730250199Sgrehan desc.transaction_id = request_id; 731250199Sgrehan 732250199Sgrehan buffer_list[0].data = &desc; 733250199Sgrehan buffer_list[0].length = sizeof(hv_vm_packet_descriptor); 734250199Sgrehan 735250199Sgrehan buffer_list[1].data = buffer; 736250199Sgrehan buffer_list[1].length = buffer_len; 737250199Sgrehan 738250199Sgrehan buffer_list[2].data = &aligned_data; 739250199Sgrehan buffer_list[2].length = packet_len_aligned - packet_len; 740250199Sgrehan 741282212Swhu ret = hv_ring_buffer_write(&channel->outbound, buffer_list, 3, 742282212Swhu &need_sig); 743250199Sgrehan 744250199Sgrehan /* TODO: We should determine if this is optional */ 745282212Swhu if (ret == 0 && need_sig) { 746250199Sgrehan vmbus_channel_set_event(channel); 747250199Sgrehan } 748250199Sgrehan 749250199Sgrehan return (ret); 750250199Sgrehan} 751250199Sgrehan 752250199Sgrehan/** 753250199Sgrehan * @brief Send a range of single-page buffer packets using 754250199Sgrehan * a GPADL Direct packet type 755250199Sgrehan */ 756250199Sgrehanint 757250199Sgrehanhv_vmbus_channel_send_packet_pagebuffer( 758250199Sgrehan hv_vmbus_channel* channel, 759250199Sgrehan hv_vmbus_page_buffer page_buffers[], 760250199Sgrehan uint32_t page_count, 761250199Sgrehan void* buffer, 762250199Sgrehan uint32_t buffer_len, 763250199Sgrehan uint64_t request_id) 764250199Sgrehan{ 765250199Sgrehan 766250199Sgrehan int ret = 0; 767282212Swhu boolean_t need_sig; 768250199Sgrehan uint32_t packet_len; 769294705Ssephe uint32_t page_buflen; 770250199Sgrehan uint32_t packetLen_aligned; 771294705Ssephe hv_vmbus_sg_buffer_list buffer_list[4]; 772250199Sgrehan hv_vmbus_channel_packet_page_buffer desc; 773250199Sgrehan uint32_t descSize; 774250199Sgrehan uint64_t alignedData = 0; 775250199Sgrehan 776250199Sgrehan if (page_count > HV_MAX_PAGE_BUFFER_COUNT) 777250199Sgrehan return (EINVAL); 778250199Sgrehan 779250199Sgrehan /* 780250199Sgrehan * Adjust the size down since hv_vmbus_channel_packet_page_buffer 781250199Sgrehan * is the largest size we support 782250199Sgrehan */ 783294705Ssephe descSize = __offsetof(hv_vmbus_channel_packet_page_buffer, range); 784294705Ssephe page_buflen = sizeof(hv_vmbus_page_buffer) * page_count; 785294705Ssephe packet_len = descSize + page_buflen + buffer_len; 786250199Sgrehan packetLen_aligned = HV_ALIGN_UP(packet_len, sizeof(uint64_t)); 787250199Sgrehan 788250199Sgrehan /* Setup the descriptor */ 789250199Sgrehan desc.type = HV_VMBUS_PACKET_TYPE_DATA_USING_GPA_DIRECT; 790250199Sgrehan desc.flags = HV_VMBUS_DATA_PACKET_FLAG_COMPLETION_REQUESTED; 791294705Ssephe /* in 8-bytes granularity */ 792294705Ssephe desc.data_offset8 = (descSize + page_buflen) >> 3; 793250199Sgrehan desc.length8 = (uint16_t) (packetLen_aligned >> 3); 794250199Sgrehan desc.transaction_id = request_id; 795250199Sgrehan desc.range_count = page_count; 796250199Sgrehan 797250199Sgrehan buffer_list[0].data = &desc; 798250199Sgrehan buffer_list[0].length = descSize; 799250199Sgrehan 800294705Ssephe buffer_list[1].data = page_buffers; 801294705Ssephe buffer_list[1].length = page_buflen; 802250199Sgrehan 803294705Ssephe buffer_list[2].data = buffer; 804294705Ssephe buffer_list[2].length = buffer_len; 805250199Sgrehan 806294705Ssephe buffer_list[3].data = &alignedData; 807294705Ssephe buffer_list[3].length = packetLen_aligned - packet_len; 808294705Ssephe 809294705Ssephe ret = hv_ring_buffer_write(&channel->outbound, buffer_list, 4, 810282212Swhu &need_sig); 811250199Sgrehan 812250199Sgrehan /* TODO: We should determine if this is optional */ 813282212Swhu if (ret == 0 && need_sig) { 814250199Sgrehan vmbus_channel_set_event(channel); 815250199Sgrehan } 816250199Sgrehan 817250199Sgrehan return (ret); 818250199Sgrehan} 819250199Sgrehan 820250199Sgrehan/** 821250199Sgrehan * @brief Send a multi-page buffer packet using a GPADL Direct packet type 822250199Sgrehan */ 823250199Sgrehanint 824250199Sgrehanhv_vmbus_channel_send_packet_multipagebuffer( 825250199Sgrehan hv_vmbus_channel* channel, 826250199Sgrehan hv_vmbus_multipage_buffer* multi_page_buffer, 827250199Sgrehan void* buffer, 828250199Sgrehan uint32_t buffer_len, 829250199Sgrehan uint64_t request_id) 830250199Sgrehan{ 831250199Sgrehan 832250199Sgrehan int ret = 0; 833250199Sgrehan uint32_t desc_size; 834282212Swhu boolean_t need_sig; 835250199Sgrehan uint32_t packet_len; 836250199Sgrehan uint32_t packet_len_aligned; 837250199Sgrehan uint32_t pfn_count; 838250199Sgrehan uint64_t aligned_data = 0; 839250199Sgrehan hv_vmbus_sg_buffer_list buffer_list[3]; 840250199Sgrehan hv_vmbus_channel_packet_multipage_buffer desc; 841250199Sgrehan 842250199Sgrehan pfn_count = 843250199Sgrehan HV_NUM_PAGES_SPANNED( 844250199Sgrehan multi_page_buffer->offset, 845250199Sgrehan multi_page_buffer->length); 846250199Sgrehan 847250199Sgrehan if ((pfn_count == 0) || (pfn_count > HV_MAX_MULTIPAGE_BUFFER_COUNT)) 848250199Sgrehan return (EINVAL); 849250199Sgrehan /* 850250199Sgrehan * Adjust the size down since hv_vmbus_channel_packet_multipage_buffer 851250199Sgrehan * is the largest size we support 852250199Sgrehan */ 853250199Sgrehan desc_size = 854250199Sgrehan sizeof(hv_vmbus_channel_packet_multipage_buffer) - 855250199Sgrehan ((HV_MAX_MULTIPAGE_BUFFER_COUNT - pfn_count) * 856250199Sgrehan sizeof(uint64_t)); 857250199Sgrehan packet_len = desc_size + buffer_len; 858250199Sgrehan packet_len_aligned = HV_ALIGN_UP(packet_len, sizeof(uint64_t)); 859250199Sgrehan 860250199Sgrehan /* 861250199Sgrehan * Setup the descriptor 862250199Sgrehan */ 863250199Sgrehan desc.type = HV_VMBUS_PACKET_TYPE_DATA_USING_GPA_DIRECT; 864250199Sgrehan desc.flags = HV_VMBUS_DATA_PACKET_FLAG_COMPLETION_REQUESTED; 865250199Sgrehan desc.data_offset8 = desc_size >> 3; /* in 8-bytes granularity */ 866250199Sgrehan desc.length8 = (uint16_t) (packet_len_aligned >> 3); 867250199Sgrehan desc.transaction_id = request_id; 868250199Sgrehan desc.range_count = 1; 869250199Sgrehan 870250199Sgrehan desc.range.length = multi_page_buffer->length; 871250199Sgrehan desc.range.offset = multi_page_buffer->offset; 872250199Sgrehan 873250199Sgrehan memcpy(desc.range.pfn_array, multi_page_buffer->pfn_array, 874250199Sgrehan pfn_count * sizeof(uint64_t)); 875250199Sgrehan 876250199Sgrehan buffer_list[0].data = &desc; 877250199Sgrehan buffer_list[0].length = desc_size; 878250199Sgrehan 879250199Sgrehan buffer_list[1].data = buffer; 880250199Sgrehan buffer_list[1].length = buffer_len; 881250199Sgrehan 882250199Sgrehan buffer_list[2].data = &aligned_data; 883250199Sgrehan buffer_list[2].length = packet_len_aligned - packet_len; 884250199Sgrehan 885282212Swhu ret = hv_ring_buffer_write(&channel->outbound, buffer_list, 3, 886282212Swhu &need_sig); 887250199Sgrehan 888250199Sgrehan /* TODO: We should determine if this is optional */ 889282212Swhu if (ret == 0 && need_sig) { 890250199Sgrehan vmbus_channel_set_event(channel); 891250199Sgrehan } 892250199Sgrehan 893250199Sgrehan return (ret); 894250199Sgrehan} 895250199Sgrehan 896250199Sgrehan/** 897250199Sgrehan * @brief Retrieve the user packet on the specified channel 898250199Sgrehan */ 899250199Sgrehanint 900250199Sgrehanhv_vmbus_channel_recv_packet( 901250199Sgrehan hv_vmbus_channel* channel, 902250199Sgrehan void* Buffer, 903250199Sgrehan uint32_t buffer_len, 904250199Sgrehan uint32_t* buffer_actual_len, 905250199Sgrehan uint64_t* request_id) 906250199Sgrehan{ 907250199Sgrehan int ret; 908250199Sgrehan uint32_t user_len; 909250199Sgrehan uint32_t packet_len; 910250199Sgrehan hv_vm_packet_descriptor desc; 911250199Sgrehan 912250199Sgrehan *buffer_actual_len = 0; 913250199Sgrehan *request_id = 0; 914250199Sgrehan 915250199Sgrehan ret = hv_ring_buffer_peek(&channel->inbound, &desc, 916250199Sgrehan sizeof(hv_vm_packet_descriptor)); 917250199Sgrehan if (ret != 0) 918250199Sgrehan return (0); 919250199Sgrehan 920250199Sgrehan packet_len = desc.length8 << 3; 921250199Sgrehan user_len = packet_len - (desc.data_offset8 << 3); 922250199Sgrehan 923250199Sgrehan *buffer_actual_len = user_len; 924250199Sgrehan 925250199Sgrehan if (user_len > buffer_len) 926250199Sgrehan return (EINVAL); 927250199Sgrehan 928250199Sgrehan *request_id = desc.transaction_id; 929250199Sgrehan 930250199Sgrehan /* Copy over the packet to the user buffer */ 931250199Sgrehan ret = hv_ring_buffer_read(&channel->inbound, Buffer, user_len, 932250199Sgrehan (desc.data_offset8 << 3)); 933250199Sgrehan 934250199Sgrehan return (0); 935250199Sgrehan} 936250199Sgrehan 937250199Sgrehan/** 938250199Sgrehan * @brief Retrieve the raw packet on the specified channel 939250199Sgrehan */ 940250199Sgrehanint 941250199Sgrehanhv_vmbus_channel_recv_packet_raw( 942250199Sgrehan hv_vmbus_channel* channel, 943250199Sgrehan void* buffer, 944250199Sgrehan uint32_t buffer_len, 945250199Sgrehan uint32_t* buffer_actual_len, 946250199Sgrehan uint64_t* request_id) 947250199Sgrehan{ 948250199Sgrehan int ret; 949250199Sgrehan uint32_t packetLen; 950250199Sgrehan hv_vm_packet_descriptor desc; 951250199Sgrehan 952250199Sgrehan *buffer_actual_len = 0; 953250199Sgrehan *request_id = 0; 954250199Sgrehan 955250199Sgrehan ret = hv_ring_buffer_peek( 956250199Sgrehan &channel->inbound, &desc, 957250199Sgrehan sizeof(hv_vm_packet_descriptor)); 958250199Sgrehan 959250199Sgrehan if (ret != 0) 960250199Sgrehan return (0); 961250199Sgrehan 962250199Sgrehan packetLen = desc.length8 << 3; 963250199Sgrehan *buffer_actual_len = packetLen; 964250199Sgrehan 965250199Sgrehan if (packetLen > buffer_len) 966250199Sgrehan return (ENOBUFS); 967250199Sgrehan 968250199Sgrehan *request_id = desc.transaction_id; 969250199Sgrehan 970250199Sgrehan /* Copy over the entire packet to the user buffer */ 971250199Sgrehan ret = hv_ring_buffer_read(&channel->inbound, buffer, packetLen, 0); 972250199Sgrehan 973250199Sgrehan return (0); 974250199Sgrehan} 975294886Ssephe 976294886Ssephe 977294886Ssephe/** 978294886Ssephe * Process a channel event notification 979294886Ssephe */ 980294886Ssephestatic void 981294886SsepheVmbusProcessChannelEvent(void* context, int pending) 982294886Ssephe{ 983294886Ssephe void* arg; 984294886Ssephe uint32_t bytes_to_read; 985294886Ssephe hv_vmbus_channel* channel = (hv_vmbus_channel*)context; 986294886Ssephe boolean_t is_batched_reading; 987294886Ssephe 988294886Ssephe /** 989294886Ssephe * Find the channel based on this relid and invokes 990294886Ssephe * the channel callback to process the event 991294886Ssephe */ 992294886Ssephe 993294886Ssephe if (channel == NULL) { 994294886Ssephe return; 995294886Ssephe } 996294886Ssephe /** 997294886Ssephe * To deal with the race condition where we might 998294886Ssephe * receive a packet while the relevant driver is 999294886Ssephe * being unloaded, dispatch the callback while 1000294886Ssephe * holding the channel lock. The unloading driver 1001294886Ssephe * will acquire the same channel lock to set the 1002294886Ssephe * callback to NULL. This closes the window. 1003294886Ssephe */ 1004294886Ssephe 1005294886Ssephe if (channel->on_channel_callback != NULL) { 1006294886Ssephe arg = channel->channel_callback_context; 1007294886Ssephe is_batched_reading = channel->batched_reading; 1008294886Ssephe /* 1009294886Ssephe * Optimize host to guest signaling by ensuring: 1010294886Ssephe * 1. While reading the channel, we disable interrupts from 1011294886Ssephe * host. 1012294886Ssephe * 2. Ensure that we process all posted messages from the host 1013294886Ssephe * before returning from this callback. 1014294886Ssephe * 3. Once we return, enable signaling from the host. Once this 1015294886Ssephe * state is set we check to see if additional packets are 1016294886Ssephe * available to read. In this case we repeat the process. 1017294886Ssephe */ 1018294886Ssephe do { 1019294886Ssephe if (is_batched_reading) 1020294886Ssephe hv_ring_buffer_read_begin(&channel->inbound); 1021294886Ssephe 1022294886Ssephe channel->on_channel_callback(arg); 1023294886Ssephe 1024294886Ssephe if (is_batched_reading) 1025294886Ssephe bytes_to_read = 1026294886Ssephe hv_ring_buffer_read_end(&channel->inbound); 1027294886Ssephe else 1028294886Ssephe bytes_to_read = 0; 1029294886Ssephe } while (is_batched_reading && (bytes_to_read != 0)); 1030294886Ssephe } 1031294886Ssephe} 1032