vmbus_chan.c revision 307486
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: stable/11/sys/dev/hyperv/vmbus/vmbus_chan.c 307486 2016-10-17 07:16:04Z sephe $"); 31256276Sdim 32250199Sgrehan#include <sys/param.h> 33296028Ssephe#include <sys/kernel.h> 34307466Ssephe#include <sys/lock.h> 35250199Sgrehan#include <sys/malloc.h> 36250199Sgrehan#include <sys/mutex.h> 37307466Ssephe#include <sys/smp.h> 38296181Ssephe#include <sys/sysctl.h> 39307466Ssephe#include <sys/systm.h> 40301588Ssephe 41301588Ssephe#include <machine/atomic.h> 42301588Ssephe 43302872Ssephe#include <dev/hyperv/include/hyperv_busdma.h> 44302619Ssephe#include <dev/hyperv/vmbus/hyperv_var.h> 45301588Ssephe#include <dev/hyperv/vmbus/vmbus_reg.h> 46300102Ssephe#include <dev/hyperv/vmbus/vmbus_var.h> 47307463Ssephe#include <dev/hyperv/vmbus/vmbus_brvar.h> 48307463Ssephe#include <dev/hyperv/vmbus/vmbus_chanvar.h> 49250199Sgrehan 50307466Ssephestatic void vmbus_chan_update_evtflagcnt( 51307466Ssephe struct vmbus_softc *, 52307466Ssephe const struct vmbus_channel *); 53307466Ssephestatic void vmbus_chan_close_internal( 54307466Ssephe struct vmbus_channel *); 55307466Ssephestatic int vmbus_chan_sysctl_mnf(SYSCTL_HANDLER_ARGS); 56307466Ssephestatic void vmbus_chan_sysctl_create( 57307466Ssephe struct vmbus_channel *); 58307466Ssephestatic struct vmbus_channel *vmbus_chan_alloc(struct vmbus_softc *); 59307466Ssephestatic void vmbus_chan_free(struct vmbus_channel *); 60307466Ssephestatic int vmbus_chan_add(struct vmbus_channel *); 61307466Ssephestatic void vmbus_chan_cpu_default(struct vmbus_channel *); 62302864Ssephe 63307466Ssephestatic void vmbus_chan_task(void *, int); 64307466Ssephestatic void vmbus_chan_task_nobatch(void *, int); 65307466Ssephestatic void vmbus_chan_detach_task(void *, int); 66250199Sgrehan 67307466Ssephestatic void vmbus_chan_msgproc_choffer(struct vmbus_softc *, 68307466Ssephe const struct vmbus_message *); 69307466Ssephestatic void vmbus_chan_msgproc_chrescind( 70307466Ssephe struct vmbus_softc *, 71307466Ssephe const struct vmbus_message *); 72302864Ssephe 73302864Ssephe/* 74302864Ssephe * Vmbus channel message processing. 75302864Ssephe */ 76302864Ssephestatic const vmbus_chanmsg_proc_t 77302864Ssephevmbus_chan_msgprocs[VMBUS_CHANMSG_TYPE_MAX] = { 78302864Ssephe VMBUS_CHANMSG_PROC(CHOFFER, vmbus_chan_msgproc_choffer), 79302864Ssephe VMBUS_CHANMSG_PROC(CHRESCIND, vmbus_chan_msgproc_chrescind), 80302864Ssephe 81302864Ssephe VMBUS_CHANMSG_PROC_WAKEUP(CHOPEN_RESP), 82302864Ssephe VMBUS_CHANMSG_PROC_WAKEUP(GPADL_CONNRESP), 83302864Ssephe VMBUS_CHANMSG_PROC_WAKEUP(GPADL_DISCONNRESP) 84302864Ssephe}; 85302864Ssephe 86307461Ssephe/* 87307461Ssephe * Notify host that there are data pending on our TX bufring. 88250199Sgrehan */ 89307461Ssephestatic __inline void 90307461Ssephevmbus_chan_signal_tx(const struct vmbus_channel *chan) 91250199Sgrehan{ 92307461Ssephe atomic_set_long(chan->ch_evtflag, chan->ch_evtflag_mask); 93307461Ssephe if (chan->ch_txflags & VMBUS_CHAN_TXF_HASMNF) 94307461Ssephe atomic_set_int(chan->ch_montrig, chan->ch_montrig_mask); 95307461Ssephe else 96303022Ssephe hypercall_signal_event(chan->ch_monprm_dma.hv_paddr); 97250199Sgrehan} 98250199Sgrehan 99296289Ssephestatic int 100302892Ssephevmbus_chan_sysctl_mnf(SYSCTL_HANDLER_ARGS) 101296289Ssephe{ 102307461Ssephe struct vmbus_channel *chan = arg1; 103302892Ssephe int mnf = 0; 104296289Ssephe 105307461Ssephe if (chan->ch_txflags & VMBUS_CHAN_TXF_HASMNF) 106302892Ssephe mnf = 1; 107302892Ssephe return sysctl_handle_int(oidp, &mnf, 0, req); 108296289Ssephe} 109296289Ssephe 110296181Ssephestatic void 111307461Ssephevmbus_chan_sysctl_create(struct vmbus_channel *chan) 112296181Ssephe{ 113302892Ssephe struct sysctl_oid *ch_tree, *chid_tree, *br_tree; 114296181Ssephe struct sysctl_ctx_list *ctx; 115296181Ssephe uint32_t ch_id; 116296181Ssephe char name[16]; 117296181Ssephe 118302892Ssephe /* 119302892Ssephe * Add sysctl nodes related to this channel to this 120302892Ssephe * channel's sysctl ctx, so that they can be destroyed 121302892Ssephe * independently upon close of this channel, which can 122302892Ssephe * happen even if the device is not detached. 123302892Ssephe */ 124302892Ssephe ctx = &chan->ch_sysctl_ctx; 125302633Ssephe sysctl_ctx_init(ctx); 126302892Ssephe 127302892Ssephe /* 128302892Ssephe * Create dev.NAME.UNIT.channel tree. 129302892Ssephe */ 130302892Ssephe ch_tree = SYSCTL_ADD_NODE(ctx, 131302892Ssephe SYSCTL_CHILDREN(device_get_sysctl_tree(chan->ch_dev)), 132302892Ssephe OID_AUTO, "channel", CTLFLAG_RD | CTLFLAG_MPSAFE, 0, ""); 133302892Ssephe if (ch_tree == NULL) 134302892Ssephe return; 135302892Ssephe 136302892Ssephe /* 137302892Ssephe * Create dev.NAME.UNIT.channel.CHANID tree. 138302892Ssephe */ 139302892Ssephe if (VMBUS_CHAN_ISPRIMARY(chan)) 140302892Ssephe ch_id = chan->ch_id; 141302892Ssephe else 142302892Ssephe ch_id = chan->ch_prichan->ch_id; 143296181Ssephe snprintf(name, sizeof(name), "%d", ch_id); 144302892Ssephe chid_tree = SYSCTL_ADD_NODE(ctx, SYSCTL_CHILDREN(ch_tree), 145302892Ssephe OID_AUTO, name, CTLFLAG_RD | CTLFLAG_MPSAFE, 0, ""); 146302892Ssephe if (chid_tree == NULL) 147302892Ssephe return; 148296181Ssephe 149302892Ssephe if (!VMBUS_CHAN_ISPRIMARY(chan)) { 150302892Ssephe /* 151302892Ssephe * Create dev.NAME.UNIT.channel.CHANID.sub tree. 152302892Ssephe */ 153302892Ssephe ch_tree = SYSCTL_ADD_NODE(ctx, SYSCTL_CHILDREN(chid_tree), 154302892Ssephe OID_AUTO, "sub", CTLFLAG_RD | CTLFLAG_MPSAFE, 0, ""); 155302892Ssephe if (ch_tree == NULL) 156302892Ssephe return; 157296188Ssephe 158302892Ssephe /* 159302892Ssephe * Create dev.NAME.UNIT.channel.CHANID.sub.SUBIDX tree. 160302892Ssephe * 161302892Ssephe * NOTE: 162302892Ssephe * chid_tree is changed to this new sysctl tree. 163302892Ssephe */ 164302892Ssephe snprintf(name, sizeof(name), "%d", chan->ch_subidx); 165302892Ssephe chid_tree = SYSCTL_ADD_NODE(ctx, SYSCTL_CHILDREN(ch_tree), 166302892Ssephe OID_AUTO, name, CTLFLAG_RD | CTLFLAG_MPSAFE, 0, ""); 167302892Ssephe if (chid_tree == NULL) 168302892Ssephe return; 169302892Ssephe 170302892Ssephe SYSCTL_ADD_UINT(ctx, SYSCTL_CHILDREN(chid_tree), OID_AUTO, 171302892Ssephe "chanid", CTLFLAG_RD, &chan->ch_id, 0, "channel id"); 172296181Ssephe } 173296188Ssephe 174302892Ssephe SYSCTL_ADD_UINT(ctx, SYSCTL_CHILDREN(chid_tree), OID_AUTO, 175302892Ssephe "cpu", CTLFLAG_RD, &chan->ch_cpuid, 0, "owner CPU id"); 176302892Ssephe SYSCTL_ADD_PROC(ctx, SYSCTL_CHILDREN(chid_tree), OID_AUTO, 177302892Ssephe "mnf", CTLTYPE_INT | CTLFLAG_RD | CTLFLAG_MPSAFE, 178302892Ssephe chan, 0, vmbus_chan_sysctl_mnf, "I", 179302892Ssephe "has monitor notification facilities"); 180302892Ssephe 181302892Ssephe br_tree = SYSCTL_ADD_NODE(ctx, SYSCTL_CHILDREN(chid_tree), OID_AUTO, 182307462Ssephe "br", CTLFLAG_RD | CTLFLAG_MPSAFE, 0, ""); 183302892Ssephe if (br_tree != NULL) { 184307462Ssephe /* 185307462Ssephe * Create sysctl tree for RX bufring. 186307462Ssephe */ 187307464Ssephe vmbus_br_sysctl_create(ctx, br_tree, &chan->ch_rxbr.rxbr, "rx"); 188307462Ssephe /* 189307462Ssephe * Create sysctl tree for TX bufring. 190307462Ssephe */ 191307464Ssephe vmbus_br_sysctl_create(ctx, br_tree, &chan->ch_txbr.txbr, "tx"); 192302892Ssephe } 193296181Ssephe} 194296290Ssephe 195250199Sgrehanint 196307461Ssephevmbus_chan_open(struct vmbus_channel *chan, int txbr_size, int rxbr_size, 197303021Ssephe const void *udata, int udlen, vmbus_chan_callback_t cb, void *cbarg) 198250199Sgrehan{ 199307461Ssephe struct vmbus_softc *sc = chan->ch_vmbus; 200302607Ssephe const struct vmbus_chanmsg_chopen_resp *resp; 201302607Ssephe const struct vmbus_message *msg; 202302607Ssephe struct vmbus_chanmsg_chopen *req; 203302607Ssephe struct vmbus_msghc *mh; 204302607Ssephe uint32_t status; 205302986Ssephe int error; 206302872Ssephe uint8_t *br; 207250199Sgrehan 208302986Ssephe if (udlen > VMBUS_CHANMSG_CHOPEN_UDATA_SIZE) { 209302607Ssephe device_printf(sc->vmbus_dev, 210302986Ssephe "invalid udata len %d for chan%u\n", udlen, chan->ch_id); 211302607Ssephe return EINVAL; 212302607Ssephe } 213302986Ssephe KASSERT((txbr_size & PAGE_MASK) == 0, 214302872Ssephe ("send bufring size is not multiple page")); 215302986Ssephe KASSERT((rxbr_size & PAGE_MASK) == 0, 216302872Ssephe ("recv bufring size is not multiple page")); 217302607Ssephe 218302986Ssephe if (atomic_testandset_int(&chan->ch_stflags, 219302812Ssephe VMBUS_CHAN_ST_OPENED_SHIFT)) 220302986Ssephe panic("double-open chan%u", chan->ch_id); 221282212Swhu 222302986Ssephe chan->ch_cb = cb; 223302986Ssephe chan->ch_cbarg = cbarg; 224250199Sgrehan 225302986Ssephe vmbus_chan_update_evtflagcnt(sc, chan); 226300102Ssephe 227307461Ssephe chan->ch_tq = VMBUS_PCPU_GET(chan->ch_vmbus, event_tq, chan->ch_cpuid); 228302986Ssephe if (chan->ch_flags & VMBUS_CHAN_FLAG_BATCHREAD) 229302986Ssephe TASK_INIT(&chan->ch_task, 0, vmbus_chan_task, chan); 230302986Ssephe else 231302986Ssephe TASK_INIT(&chan->ch_task, 0, vmbus_chan_task_nobatch, chan); 232294886Ssephe 233302872Ssephe /* 234302872Ssephe * Allocate the TX+RX bufrings. 235302872Ssephe * XXX should use ch_dev dtag 236302872Ssephe */ 237302872Ssephe br = hyperv_dmamem_alloc(bus_get_dma_tag(sc->vmbus_dev), 238302986Ssephe PAGE_SIZE, 0, txbr_size + rxbr_size, &chan->ch_bufring_dma, 239302986Ssephe BUS_DMA_WAITOK | BUS_DMA_ZERO); 240302872Ssephe if (br == NULL) { 241302872Ssephe device_printf(sc->vmbus_dev, "bufring allocation failed\n"); 242302986Ssephe error = ENOMEM; 243302812Ssephe goto failed; 244302812Ssephe } 245302986Ssephe chan->ch_bufring = br; 246250199Sgrehan 247302872Ssephe /* TX bufring comes first */ 248307464Ssephe vmbus_txbr_setup(&chan->ch_txbr, br, txbr_size); 249302872Ssephe /* RX bufring immediately follows TX bufring */ 250307464Ssephe vmbus_rxbr_setup(&chan->ch_rxbr, br + txbr_size, rxbr_size); 251250199Sgrehan 252296290Ssephe /* Create sysctl tree for this channel */ 253302986Ssephe vmbus_chan_sysctl_create(chan); 254296181Ssephe 255302872Ssephe /* 256302872Ssephe * Connect the bufrings, both RX and TX, to this channel. 257250199Sgrehan */ 258302986Ssephe error = vmbus_chan_gpadl_connect(chan, chan->ch_bufring_dma.hv_paddr, 259302986Ssephe txbr_size + rxbr_size, &chan->ch_bufring_gpadl); 260302986Ssephe if (error) { 261302872Ssephe device_printf(sc->vmbus_dev, 262302986Ssephe "failed to connect bufring GPADL to chan%u\n", chan->ch_id); 263302872Ssephe goto failed; 264302872Ssephe } 265250199Sgrehan 266302607Ssephe /* 267302607Ssephe * Open channel w/ the bufring GPADL on the target CPU. 268250199Sgrehan */ 269302607Ssephe mh = vmbus_msghc_get(sc, sizeof(*req)); 270302607Ssephe if (mh == NULL) { 271302607Ssephe device_printf(sc->vmbus_dev, 272302607Ssephe "can not get msg hypercall for chopen(chan%u)\n", 273302986Ssephe chan->ch_id); 274302986Ssephe error = ENXIO; 275302812Ssephe goto failed; 276302607Ssephe } 277250199Sgrehan 278302607Ssephe req = vmbus_msghc_dataptr(mh); 279302607Ssephe req->chm_hdr.chm_type = VMBUS_CHANMSG_TYPE_CHOPEN; 280302986Ssephe req->chm_chanid = chan->ch_id; 281302986Ssephe req->chm_openid = chan->ch_id; 282302986Ssephe req->chm_gpadl = chan->ch_bufring_gpadl; 283302986Ssephe req->chm_vcpuid = chan->ch_vcpuid; 284302986Ssephe req->chm_txbr_pgcnt = txbr_size >> PAGE_SHIFT; 285302986Ssephe if (udlen > 0) 286302986Ssephe memcpy(req->chm_udata, udata, udlen); 287250199Sgrehan 288302986Ssephe error = vmbus_msghc_exec(sc, mh); 289302986Ssephe if (error) { 290302607Ssephe device_printf(sc->vmbus_dev, 291302607Ssephe "chopen(chan%u) msg hypercall exec failed: %d\n", 292302986Ssephe chan->ch_id, error); 293302607Ssephe vmbus_msghc_put(sc, mh); 294302812Ssephe goto failed; 295302607Ssephe } 296250199Sgrehan 297302607Ssephe msg = vmbus_msghc_wait_result(sc, mh); 298302607Ssephe resp = (const struct vmbus_chanmsg_chopen_resp *)msg->msg_data; 299302607Ssephe status = resp->chm_status; 300250199Sgrehan 301302607Ssephe vmbus_msghc_put(sc, mh); 302250199Sgrehan 303302607Ssephe if (status == 0) { 304302607Ssephe if (bootverbose) { 305302607Ssephe device_printf(sc->vmbus_dev, "chan%u opened\n", 306302986Ssephe chan->ch_id); 307302607Ssephe } 308302812Ssephe return 0; 309250199Sgrehan } 310302812Ssephe 311302986Ssephe device_printf(sc->vmbus_dev, "failed to open chan%u\n", chan->ch_id); 312302986Ssephe error = ENXIO; 313302812Ssephe 314302812Ssephefailed: 315302986Ssephe if (chan->ch_bufring_gpadl) { 316302986Ssephe vmbus_chan_gpadl_disconnect(chan, chan->ch_bufring_gpadl); 317302986Ssephe chan->ch_bufring_gpadl = 0; 318302872Ssephe } 319302986Ssephe if (chan->ch_bufring != NULL) { 320302986Ssephe hyperv_dmamem_free(&chan->ch_bufring_dma, chan->ch_bufring); 321302986Ssephe chan->ch_bufring = NULL; 322302872Ssephe } 323302986Ssephe atomic_clear_int(&chan->ch_stflags, VMBUS_CHAN_ST_OPENED); 324302986Ssephe return error; 325250199Sgrehan} 326250199Sgrehan 327302609Ssepheint 328307461Ssephevmbus_chan_gpadl_connect(struct vmbus_channel *chan, bus_addr_t paddr, 329302871Ssephe int size, uint32_t *gpadl0) 330302871Ssephe{ 331307461Ssephe struct vmbus_softc *sc = chan->ch_vmbus; 332302609Ssephe struct vmbus_msghc *mh; 333302609Ssephe struct vmbus_chanmsg_gpadl_conn *req; 334302609Ssephe const struct vmbus_message *msg; 335302609Ssephe size_t reqsz; 336302609Ssephe uint32_t gpadl, status; 337302609Ssephe int page_count, range_len, i, cnt, error; 338302871Ssephe uint64_t page_id; 339250199Sgrehan 340302609Ssephe /* 341302609Ssephe * Preliminary checks. 342302609Ssephe */ 343250199Sgrehan 344302609Ssephe KASSERT((size & PAGE_MASK) == 0, 345302871Ssephe ("invalid GPA size %d, not multiple page size", size)); 346250199Sgrehan page_count = size >> PAGE_SHIFT; 347250199Sgrehan 348302609Ssephe KASSERT((paddr & PAGE_MASK) == 0, 349302609Ssephe ("GPA is not page aligned %jx", (uintmax_t)paddr)); 350302609Ssephe page_id = paddr >> PAGE_SHIFT; 351250199Sgrehan 352302609Ssephe range_len = __offsetof(struct vmbus_gpa_range, gpa_page[page_count]); 353302609Ssephe /* 354302609Ssephe * We don't support multiple GPA ranges. 355302609Ssephe */ 356302609Ssephe if (range_len > UINT16_MAX) { 357302609Ssephe device_printf(sc->vmbus_dev, "GPA too large, %d pages\n", 358302609Ssephe page_count); 359302609Ssephe return EOPNOTSUPP; 360250199Sgrehan } 361250199Sgrehan 362302609Ssephe /* 363302609Ssephe * Allocate GPADL id. 364302609Ssephe */ 365302630Ssephe gpadl = vmbus_gpadl_alloc(sc); 366302609Ssephe *gpadl0 = gpadl; 367250199Sgrehan 368302609Ssephe /* 369302609Ssephe * Connect this GPADL to the target channel. 370302609Ssephe * 371302609Ssephe * NOTE: 372302609Ssephe * Since each message can only hold small set of page 373302609Ssephe * addresses, several messages may be required to 374302609Ssephe * complete the connection. 375302609Ssephe */ 376302609Ssephe if (page_count > VMBUS_CHANMSG_GPADL_CONN_PGMAX) 377302609Ssephe cnt = VMBUS_CHANMSG_GPADL_CONN_PGMAX; 378302609Ssephe else 379302609Ssephe cnt = page_count; 380302609Ssephe page_count -= cnt; 381250199Sgrehan 382302609Ssephe reqsz = __offsetof(struct vmbus_chanmsg_gpadl_conn, 383302609Ssephe chm_range.gpa_page[cnt]); 384302609Ssephe mh = vmbus_msghc_get(sc, reqsz); 385302609Ssephe if (mh == NULL) { 386302609Ssephe device_printf(sc->vmbus_dev, 387302609Ssephe "can not get msg hypercall for gpadl->chan%u\n", 388302871Ssephe chan->ch_id); 389302609Ssephe return EIO; 390250199Sgrehan } 391250199Sgrehan 392302609Ssephe req = vmbus_msghc_dataptr(mh); 393302609Ssephe req->chm_hdr.chm_type = VMBUS_CHANMSG_TYPE_GPADL_CONN; 394302871Ssephe req->chm_chanid = chan->ch_id; 395302609Ssephe req->chm_gpadl = gpadl; 396302609Ssephe req->chm_range_len = range_len; 397302609Ssephe req->chm_range_cnt = 1; 398302609Ssephe req->chm_range.gpa_len = size; 399302609Ssephe req->chm_range.gpa_ofs = 0; 400302609Ssephe for (i = 0; i < cnt; ++i) 401302609Ssephe req->chm_range.gpa_page[i] = page_id++; 402250199Sgrehan 403302609Ssephe error = vmbus_msghc_exec(sc, mh); 404302609Ssephe if (error) { 405302609Ssephe device_printf(sc->vmbus_dev, 406302609Ssephe "gpadl->chan%u msg hypercall exec failed: %d\n", 407302871Ssephe chan->ch_id, error); 408302609Ssephe vmbus_msghc_put(sc, mh); 409302609Ssephe return error; 410302609Ssephe } 411250199Sgrehan 412302609Ssephe while (page_count > 0) { 413302609Ssephe struct vmbus_chanmsg_gpadl_subconn *subreq; 414250199Sgrehan 415302609Ssephe if (page_count > VMBUS_CHANMSG_GPADL_SUBCONN_PGMAX) 416302609Ssephe cnt = VMBUS_CHANMSG_GPADL_SUBCONN_PGMAX; 417302609Ssephe else 418302609Ssephe cnt = page_count; 419302609Ssephe page_count -= cnt; 420250199Sgrehan 421302609Ssephe reqsz = __offsetof(struct vmbus_chanmsg_gpadl_subconn, 422302609Ssephe chm_gpa_page[cnt]); 423302609Ssephe vmbus_msghc_reset(mh, reqsz); 424250199Sgrehan 425302609Ssephe subreq = vmbus_msghc_dataptr(mh); 426302609Ssephe subreq->chm_hdr.chm_type = VMBUS_CHANMSG_TYPE_GPADL_SUBCONN; 427302609Ssephe subreq->chm_gpadl = gpadl; 428302609Ssephe for (i = 0; i < cnt; ++i) 429302609Ssephe subreq->chm_gpa_page[i] = page_id++; 430250199Sgrehan 431302609Ssephe vmbus_msghc_exec_noresult(mh); 432250199Sgrehan } 433302609Ssephe KASSERT(page_count == 0, ("invalid page count %d", page_count)); 434250199Sgrehan 435302609Ssephe msg = vmbus_msghc_wait_result(sc, mh); 436302609Ssephe status = ((const struct vmbus_chanmsg_gpadl_connresp *) 437302609Ssephe msg->msg_data)->chm_status; 438250199Sgrehan 439302609Ssephe vmbus_msghc_put(sc, mh); 440250199Sgrehan 441302609Ssephe if (status != 0) { 442302609Ssephe device_printf(sc->vmbus_dev, "gpadl->chan%u failed: " 443302871Ssephe "status %u\n", chan->ch_id, status); 444302609Ssephe return EIO; 445302632Ssephe } else { 446302632Ssephe if (bootverbose) { 447302632Ssephe device_printf(sc->vmbus_dev, "gpadl->chan%u " 448302871Ssephe "succeeded\n", chan->ch_id); 449302632Ssephe } 450302609Ssephe } 451302609Ssephe return 0; 452250199Sgrehan} 453250199Sgrehan 454302611Ssephe/* 455302611Ssephe * Disconnect the GPA from the target channel 456250199Sgrehan */ 457250199Sgrehanint 458307461Ssephevmbus_chan_gpadl_disconnect(struct vmbus_channel *chan, uint32_t gpadl) 459250199Sgrehan{ 460307461Ssephe struct vmbus_softc *sc = chan->ch_vmbus; 461302611Ssephe struct vmbus_msghc *mh; 462302611Ssephe struct vmbus_chanmsg_gpadl_disconn *req; 463302611Ssephe int error; 464250199Sgrehan 465302611Ssephe mh = vmbus_msghc_get(sc, sizeof(*req)); 466302611Ssephe if (mh == NULL) { 467302611Ssephe device_printf(sc->vmbus_dev, 468302611Ssephe "can not get msg hypercall for gpa x->chan%u\n", 469302693Ssephe chan->ch_id); 470302611Ssephe return EBUSY; 471250199Sgrehan } 472250199Sgrehan 473302611Ssephe req = vmbus_msghc_dataptr(mh); 474302611Ssephe req->chm_hdr.chm_type = VMBUS_CHANMSG_TYPE_GPADL_DISCONN; 475302693Ssephe req->chm_chanid = chan->ch_id; 476302611Ssephe req->chm_gpadl = gpadl; 477250199Sgrehan 478302611Ssephe error = vmbus_msghc_exec(sc, mh); 479302611Ssephe if (error) { 480302611Ssephe device_printf(sc->vmbus_dev, 481302611Ssephe "gpa x->chan%u msg hypercall exec failed: %d\n", 482302693Ssephe chan->ch_id, error); 483302611Ssephe vmbus_msghc_put(sc, mh); 484302611Ssephe return error; 485302611Ssephe } 486250199Sgrehan 487302611Ssephe vmbus_msghc_wait_result(sc, mh); 488302611Ssephe /* Discard result; no useful information */ 489302611Ssephe vmbus_msghc_put(sc, mh); 490250199Sgrehan 491302611Ssephe return 0; 492250199Sgrehan} 493250199Sgrehan 494282212Swhustatic void 495307461Ssephevmbus_chan_close_internal(struct vmbus_channel *chan) 496250199Sgrehan{ 497307461Ssephe struct vmbus_softc *sc = chan->ch_vmbus; 498302610Ssephe struct vmbus_msghc *mh; 499302610Ssephe struct vmbus_chanmsg_chclose *req; 500302891Ssephe struct taskqueue *tq = chan->ch_tq; 501302610Ssephe int error; 502250199Sgrehan 503302812Ssephe /* TODO: stringent check */ 504302891Ssephe atomic_clear_int(&chan->ch_stflags, VMBUS_CHAN_ST_OPENED); 505302812Ssephe 506302891Ssephe /* 507302891Ssephe * Free this channel's sysctl tree attached to its device's 508302891Ssephe * sysctl tree. 509302891Ssephe */ 510302891Ssephe sysctl_ctx_free(&chan->ch_sysctl_ctx); 511282212Swhu 512282212Swhu /* 513302891Ssephe * Set ch_tq to NULL to avoid more requests be scheduled. 514302891Ssephe * XXX pretty broken; need rework. 515294886Ssephe */ 516302891Ssephe chan->ch_tq = NULL; 517302891Ssephe taskqueue_drain(tq, &chan->ch_task); 518302891Ssephe chan->ch_cb = NULL; 519250199Sgrehan 520302891Ssephe /* 521302891Ssephe * Close this channel. 522250199Sgrehan */ 523302610Ssephe mh = vmbus_msghc_get(sc, sizeof(*req)); 524302610Ssephe if (mh == NULL) { 525302610Ssephe device_printf(sc->vmbus_dev, 526302610Ssephe "can not get msg hypercall for chclose(chan%u)\n", 527302891Ssephe chan->ch_id); 528302610Ssephe return; 529302610Ssephe } 530250199Sgrehan 531302610Ssephe req = vmbus_msghc_dataptr(mh); 532302610Ssephe req->chm_hdr.chm_type = VMBUS_CHANMSG_TYPE_CHCLOSE; 533302891Ssephe req->chm_chanid = chan->ch_id; 534250199Sgrehan 535302610Ssephe error = vmbus_msghc_exec_noresult(mh); 536302610Ssephe vmbus_msghc_put(sc, mh); 537302610Ssephe 538302610Ssephe if (error) { 539302610Ssephe device_printf(sc->vmbus_dev, 540302610Ssephe "chclose(chan%u) msg hypercall exec failed: %d\n", 541302891Ssephe chan->ch_id, error); 542302610Ssephe return; 543302610Ssephe } else if (bootverbose) { 544302891Ssephe device_printf(sc->vmbus_dev, "close chan%u\n", chan->ch_id); 545302610Ssephe } 546302610Ssephe 547302891Ssephe /* 548302891Ssephe * Disconnect the TX+RX bufrings from this channel. 549302891Ssephe */ 550302891Ssephe if (chan->ch_bufring_gpadl) { 551302891Ssephe vmbus_chan_gpadl_disconnect(chan, chan->ch_bufring_gpadl); 552302891Ssephe chan->ch_bufring_gpadl = 0; 553250199Sgrehan } 554250199Sgrehan 555302891Ssephe /* 556302891Ssephe * Destroy the TX+RX bufrings. 557302891Ssephe */ 558302891Ssephe if (chan->ch_bufring != NULL) { 559302891Ssephe hyperv_dmamem_free(&chan->ch_bufring_dma, chan->ch_bufring); 560302891Ssephe chan->ch_bufring = NULL; 561302872Ssephe } 562282212Swhu} 563250199Sgrehan 564302818Ssephe/* 565302818Ssephe * Caller should make sure that all sub-channels have 566302818Ssephe * been added to 'chan' and all to-be-closed channels 567302818Ssephe * are not being opened. 568282212Swhu */ 569282212Swhuvoid 570307461Ssephevmbus_chan_close(struct vmbus_channel *chan) 571282212Swhu{ 572302818Ssephe int subchan_cnt; 573282212Swhu 574302818Ssephe if (!VMBUS_CHAN_ISPRIMARY(chan)) { 575282212Swhu /* 576302818Ssephe * Sub-channel is closed when its primary channel 577302818Ssephe * is closed; done. 578282212Swhu */ 579282212Swhu return; 580282212Swhu } 581282212Swhu 582250199Sgrehan /* 583302818Ssephe * Close all sub-channels, if any. 584250199Sgrehan */ 585302819Ssephe subchan_cnt = chan->ch_subchan_cnt; 586302818Ssephe if (subchan_cnt > 0) { 587307461Ssephe struct vmbus_channel **subchan; 588302818Ssephe int i; 589302818Ssephe 590302890Ssephe subchan = vmbus_subchan_get(chan, subchan_cnt); 591302818Ssephe for (i = 0; i < subchan_cnt; ++i) 592302891Ssephe vmbus_chan_close_internal(subchan[i]); 593302890Ssephe vmbus_subchan_rel(subchan, subchan_cnt); 594250199Sgrehan } 595302818Ssephe 596302818Ssephe /* Then close the primary channel. */ 597302891Ssephe vmbus_chan_close_internal(chan); 598250199Sgrehan} 599250199Sgrehan 600250199Sgrehanint 601307461Ssephevmbus_chan_send(struct vmbus_channel *chan, uint16_t type, uint16_t flags, 602302882Ssephe void *data, int dlen, uint64_t xactid) 603250199Sgrehan{ 604302875Ssephe struct vmbus_chanpkt pkt; 605302881Ssephe int pktlen, pad_pktlen, hlen, error; 606302881Ssephe uint64_t pad = 0; 607302881Ssephe struct iovec iov[3]; 608302881Ssephe boolean_t send_evt; 609250199Sgrehan 610302881Ssephe hlen = sizeof(pkt); 611302881Ssephe pktlen = hlen + dlen; 612302884Ssephe pad_pktlen = VMBUS_CHANPKT_TOTLEN(pktlen); 613307471Ssephe KASSERT(pad_pktlen <= vmbus_txbr_maxpktsz(&chan->ch_txbr), 614307471Ssephe ("invalid packet size %d", pad_pktlen)); 615250199Sgrehan 616302875Ssephe pkt.cp_hdr.cph_type = type; 617302875Ssephe pkt.cp_hdr.cph_flags = flags; 618302884Ssephe VMBUS_CHANPKT_SETLEN(pkt.cp_hdr.cph_hlen, hlen); 619302884Ssephe VMBUS_CHANPKT_SETLEN(pkt.cp_hdr.cph_tlen, pad_pktlen); 620302881Ssephe pkt.cp_hdr.cph_xactid = xactid; 621250199Sgrehan 622302875Ssephe iov[0].iov_base = &pkt; 623302881Ssephe iov[0].iov_len = hlen; 624302881Ssephe iov[1].iov_base = data; 625302881Ssephe iov[1].iov_len = dlen; 626302881Ssephe iov[2].iov_base = &pad; 627302881Ssephe iov[2].iov_len = pad_pktlen - pktlen; 628250199Sgrehan 629307464Ssephe error = vmbus_txbr_write(&chan->ch_txbr, iov, 3, &send_evt); 630302881Ssephe if (!error && send_evt) 631303022Ssephe vmbus_chan_signal_tx(chan); 632302881Ssephe return error; 633250199Sgrehan} 634250199Sgrehan 635250199Sgrehanint 636307461Ssephevmbus_chan_send_sglist(struct vmbus_channel *chan, 637302876Ssephe struct vmbus_gpa sg[], int sglen, void *data, int dlen, uint64_t xactid) 638250199Sgrehan{ 639302876Ssephe struct vmbus_chanpkt_sglist pkt; 640302876Ssephe int pktlen, pad_pktlen, hlen, error; 641302876Ssephe struct iovec iov[4]; 642302876Ssephe boolean_t send_evt; 643302876Ssephe uint64_t pad = 0; 644250199Sgrehan 645302876Ssephe hlen = __offsetof(struct vmbus_chanpkt_sglist, cp_gpa[sglen]); 646302876Ssephe pktlen = hlen + dlen; 647302884Ssephe pad_pktlen = VMBUS_CHANPKT_TOTLEN(pktlen); 648307471Ssephe KASSERT(pad_pktlen <= vmbus_txbr_maxpktsz(&chan->ch_txbr), 649307471Ssephe ("invalid packet size %d", pad_pktlen)); 650250199Sgrehan 651302880Ssephe pkt.cp_hdr.cph_type = VMBUS_CHANPKT_TYPE_GPA; 652302879Ssephe pkt.cp_hdr.cph_flags = VMBUS_CHANPKT_FLAG_RC; 653302884Ssephe VMBUS_CHANPKT_SETLEN(pkt.cp_hdr.cph_hlen, hlen); 654302884Ssephe VMBUS_CHANPKT_SETLEN(pkt.cp_hdr.cph_tlen, pad_pktlen); 655302876Ssephe pkt.cp_hdr.cph_xactid = xactid; 656302876Ssephe pkt.cp_rsvd = 0; 657302876Ssephe pkt.cp_gpa_cnt = sglen; 658250199Sgrehan 659302876Ssephe iov[0].iov_base = &pkt; 660302876Ssephe iov[0].iov_len = sizeof(pkt); 661302876Ssephe iov[1].iov_base = sg; 662302876Ssephe iov[1].iov_len = sizeof(struct vmbus_gpa) * sglen; 663302876Ssephe iov[2].iov_base = data; 664302876Ssephe iov[2].iov_len = dlen; 665302876Ssephe iov[3].iov_base = &pad; 666302876Ssephe iov[3].iov_len = pad_pktlen - pktlen; 667250199Sgrehan 668307464Ssephe error = vmbus_txbr_write(&chan->ch_txbr, iov, 4, &send_evt); 669302876Ssephe if (!error && send_evt) 670303022Ssephe vmbus_chan_signal_tx(chan); 671302876Ssephe return error; 672250199Sgrehan} 673250199Sgrehan 674250199Sgrehanint 675307461Ssephevmbus_chan_send_prplist(struct vmbus_channel *chan, 676302878Ssephe struct vmbus_gpa_range *prp, int prp_cnt, void *data, int dlen, 677302878Ssephe uint64_t xactid) 678250199Sgrehan{ 679302878Ssephe struct vmbus_chanpkt_prplist pkt; 680302878Ssephe int pktlen, pad_pktlen, hlen, error; 681302878Ssephe struct iovec iov[4]; 682302878Ssephe boolean_t send_evt; 683302878Ssephe uint64_t pad = 0; 684250199Sgrehan 685302878Ssephe hlen = __offsetof(struct vmbus_chanpkt_prplist, 686302878Ssephe cp_range[0].gpa_page[prp_cnt]); 687302878Ssephe pktlen = hlen + dlen; 688302884Ssephe pad_pktlen = VMBUS_CHANPKT_TOTLEN(pktlen); 689307471Ssephe KASSERT(pad_pktlen <= vmbus_txbr_maxpktsz(&chan->ch_txbr), 690307471Ssephe ("invalid packet size %d", pad_pktlen)); 691250199Sgrehan 692302880Ssephe pkt.cp_hdr.cph_type = VMBUS_CHANPKT_TYPE_GPA; 693302879Ssephe pkt.cp_hdr.cph_flags = VMBUS_CHANPKT_FLAG_RC; 694302884Ssephe VMBUS_CHANPKT_SETLEN(pkt.cp_hdr.cph_hlen, hlen); 695302884Ssephe VMBUS_CHANPKT_SETLEN(pkt.cp_hdr.cph_tlen, pad_pktlen); 696302878Ssephe pkt.cp_hdr.cph_xactid = xactid; 697302878Ssephe pkt.cp_rsvd = 0; 698302878Ssephe pkt.cp_range_cnt = 1; 699250199Sgrehan 700302878Ssephe iov[0].iov_base = &pkt; 701302878Ssephe iov[0].iov_len = sizeof(pkt); 702302878Ssephe iov[1].iov_base = prp; 703302878Ssephe iov[1].iov_len = __offsetof(struct vmbus_gpa_range, gpa_page[prp_cnt]); 704302878Ssephe iov[2].iov_base = data; 705302878Ssephe iov[2].iov_len = dlen; 706302878Ssephe iov[3].iov_base = &pad; 707302878Ssephe iov[3].iov_len = pad_pktlen - pktlen; 708250199Sgrehan 709307464Ssephe error = vmbus_txbr_write(&chan->ch_txbr, iov, 4, &send_evt); 710302878Ssephe if (!error && send_evt) 711303022Ssephe vmbus_chan_signal_tx(chan); 712302878Ssephe return error; 713250199Sgrehan} 714250199Sgrehan 715250199Sgrehanint 716307461Ssephevmbus_chan_recv(struct vmbus_channel *chan, void *data, int *dlen0, 717302885Ssephe uint64_t *xactid) 718250199Sgrehan{ 719302885Ssephe struct vmbus_chanpkt_hdr pkt; 720302885Ssephe int error, dlen, hlen; 721250199Sgrehan 722307464Ssephe error = vmbus_rxbr_peek(&chan->ch_rxbr, &pkt, sizeof(pkt)); 723302885Ssephe if (error) 724302885Ssephe return error; 725250199Sgrehan 726302885Ssephe hlen = VMBUS_CHANPKT_GETLEN(pkt.cph_hlen); 727302885Ssephe dlen = VMBUS_CHANPKT_GETLEN(pkt.cph_tlen) - hlen; 728250199Sgrehan 729302885Ssephe if (*dlen0 < dlen) { 730302886Ssephe /* Return the size of this packet's data. */ 731302885Ssephe *dlen0 = dlen; 732302885Ssephe return ENOBUFS; 733302885Ssephe } 734250199Sgrehan 735302885Ssephe *xactid = pkt.cph_xactid; 736302885Ssephe *dlen0 = dlen; 737250199Sgrehan 738302886Ssephe /* Skip packet header */ 739307464Ssephe error = vmbus_rxbr_read(&chan->ch_rxbr, data, dlen, hlen); 740307464Ssephe KASSERT(!error, ("vmbus_rxbr_read failed")); 741250199Sgrehan 742302885Ssephe return 0; 743250199Sgrehan} 744250199Sgrehan 745250199Sgrehanint 746307461Ssephevmbus_chan_recv_pkt(struct vmbus_channel *chan, 747302886Ssephe struct vmbus_chanpkt_hdr *pkt0, int *pktlen0) 748250199Sgrehan{ 749302886Ssephe struct vmbus_chanpkt_hdr pkt; 750302886Ssephe int error, pktlen; 751250199Sgrehan 752307464Ssephe error = vmbus_rxbr_peek(&chan->ch_rxbr, &pkt, sizeof(pkt)); 753302886Ssephe if (error) 754302886Ssephe return error; 755250199Sgrehan 756302886Ssephe pktlen = VMBUS_CHANPKT_GETLEN(pkt.cph_tlen); 757302886Ssephe if (*pktlen0 < pktlen) { 758302886Ssephe /* Return the size of this packet. */ 759302886Ssephe *pktlen0 = pktlen; 760302886Ssephe return ENOBUFS; 761302886Ssephe } 762302886Ssephe *pktlen0 = pktlen; 763250199Sgrehan 764302886Ssephe /* Include packet header */ 765307464Ssephe error = vmbus_rxbr_read(&chan->ch_rxbr, pkt0, pktlen, 0); 766307464Ssephe KASSERT(!error, ("vmbus_rxbr_read failed")); 767250199Sgrehan 768302886Ssephe return 0; 769250199Sgrehan} 770294886Ssephe 771294886Ssephestatic void 772302713Ssephevmbus_chan_task(void *xchan, int pending __unused) 773294886Ssephe{ 774307461Ssephe struct vmbus_channel *chan = xchan; 775302874Ssephe vmbus_chan_callback_t cb = chan->ch_cb; 776302874Ssephe void *cbarg = chan->ch_cbarg; 777294886Ssephe 778302710Ssephe /* 779302710Ssephe * Optimize host to guest signaling by ensuring: 780302710Ssephe * 1. While reading the channel, we disable interrupts from 781302710Ssephe * host. 782302710Ssephe * 2. Ensure that we process all posted messages from the host 783302710Ssephe * before returning from this callback. 784302710Ssephe * 3. Once we return, enable signaling from the host. Once this 785302710Ssephe * state is set we check to see if additional packets are 786302710Ssephe * available to read. In this case we repeat the process. 787302713Ssephe * 788302713Ssephe * NOTE: Interrupt has been disabled in the ISR. 789302710Ssephe */ 790302713Ssephe for (;;) { 791302713Ssephe uint32_t left; 792294886Ssephe 793307461Ssephe cb(chan, cbarg); 794294886Ssephe 795307464Ssephe left = vmbus_rxbr_intr_unmask(&chan->ch_rxbr); 796302713Ssephe if (left == 0) { 797302713Ssephe /* No more data in RX bufring; done */ 798302713Ssephe break; 799302713Ssephe } 800307464Ssephe vmbus_rxbr_intr_mask(&chan->ch_rxbr); 801302713Ssephe } 802294886Ssephe} 803302692Ssephe 804302713Ssephestatic void 805302713Ssephevmbus_chan_task_nobatch(void *xchan, int pending __unused) 806302713Ssephe{ 807307461Ssephe struct vmbus_channel *chan = xchan; 808302713Ssephe 809307461Ssephe chan->ch_cb(chan, chan->ch_cbarg); 810302713Ssephe} 811302713Ssephe 812302692Ssephestatic __inline void 813302692Ssephevmbus_event_flags_proc(struct vmbus_softc *sc, volatile u_long *event_flags, 814302692Ssephe int flag_cnt) 815302692Ssephe{ 816302692Ssephe int f; 817302692Ssephe 818302692Ssephe for (f = 0; f < flag_cnt; ++f) { 819302806Ssephe uint32_t chid_base; 820302692Ssephe u_long flags; 821302806Ssephe int chid_ofs; 822302692Ssephe 823302692Ssephe if (event_flags[f] == 0) 824302692Ssephe continue; 825302692Ssephe 826302692Ssephe flags = atomic_swap_long(&event_flags[f], 0); 827302806Ssephe chid_base = f << VMBUS_EVTFLAG_SHIFT; 828302692Ssephe 829302806Ssephe while ((chid_ofs = ffsl(flags)) != 0) { 830307461Ssephe struct vmbus_channel *chan; 831302692Ssephe 832302806Ssephe --chid_ofs; /* NOTE: ffsl is 1-based */ 833302806Ssephe flags &= ~(1UL << chid_ofs); 834302692Ssephe 835303022Ssephe chan = sc->vmbus_chmap[chid_base + chid_ofs]; 836302692Ssephe 837302692Ssephe /* if channel is closed or closing */ 838303022Ssephe if (chan == NULL || chan->ch_tq == NULL) 839302692Ssephe continue; 840302692Ssephe 841303022Ssephe if (chan->ch_flags & VMBUS_CHAN_FLAG_BATCHREAD) 842307464Ssephe vmbus_rxbr_intr_mask(&chan->ch_rxbr); 843303022Ssephe taskqueue_enqueue(chan->ch_tq, &chan->ch_task); 844302692Ssephe } 845302692Ssephe } 846302692Ssephe} 847302692Ssephe 848302692Ssephevoid 849302692Ssephevmbus_event_proc(struct vmbus_softc *sc, int cpu) 850302692Ssephe{ 851302692Ssephe struct vmbus_evtflags *eventf; 852302692Ssephe 853302692Ssephe /* 854302692Ssephe * On Host with Win8 or above, the event page can be checked directly 855302692Ssephe * to get the id of the channel that has the pending interrupt. 856302692Ssephe */ 857302692Ssephe eventf = VMBUS_PCPU_GET(sc, event_flags, cpu) + VMBUS_SINT_MESSAGE; 858302692Ssephe vmbus_event_flags_proc(sc, eventf->evt_flags, 859302692Ssephe VMBUS_PCPU_GET(sc, event_flags_cnt, cpu)); 860302692Ssephe} 861302692Ssephe 862302692Ssephevoid 863302692Ssephevmbus_event_proc_compat(struct vmbus_softc *sc, int cpu) 864302692Ssephe{ 865302692Ssephe struct vmbus_evtflags *eventf; 866302692Ssephe 867302692Ssephe eventf = VMBUS_PCPU_GET(sc, event_flags, cpu) + VMBUS_SINT_MESSAGE; 868302692Ssephe if (atomic_testandclear_long(&eventf->evt_flags[0], 0)) { 869302692Ssephe vmbus_event_flags_proc(sc, sc->vmbus_rx_evtflags, 870302692Ssephe VMBUS_CHAN_MAX_COMPAT >> VMBUS_EVTFLAG_SHIFT); 871302692Ssephe } 872302692Ssephe} 873302692Ssephe 874302692Ssephestatic void 875302692Ssephevmbus_chan_update_evtflagcnt(struct vmbus_softc *sc, 876307461Ssephe const struct vmbus_channel *chan) 877302692Ssephe{ 878302692Ssephe volatile int *flag_cnt_ptr; 879302692Ssephe int flag_cnt; 880302692Ssephe 881302693Ssephe flag_cnt = (chan->ch_id / VMBUS_EVTFLAG_LEN) + 1; 882302873Ssephe flag_cnt_ptr = VMBUS_PCPU_PTR(sc, event_flags_cnt, chan->ch_cpuid); 883302692Ssephe 884302692Ssephe for (;;) { 885302692Ssephe int old_flag_cnt; 886302692Ssephe 887302692Ssephe old_flag_cnt = *flag_cnt_ptr; 888302692Ssephe if (old_flag_cnt >= flag_cnt) 889302692Ssephe break; 890302692Ssephe if (atomic_cmpset_int(flag_cnt_ptr, old_flag_cnt, flag_cnt)) { 891302692Ssephe if (bootverbose) { 892302692Ssephe device_printf(sc->vmbus_dev, 893302692Ssephe "channel%u update cpu%d flag_cnt to %d\n", 894302873Ssephe chan->ch_id, chan->ch_cpuid, flag_cnt); 895302692Ssephe } 896302692Ssephe break; 897302692Ssephe } 898302692Ssephe } 899302692Ssephe} 900302864Ssephe 901307461Ssephestatic struct vmbus_channel * 902302864Ssephevmbus_chan_alloc(struct vmbus_softc *sc) 903302864Ssephe{ 904307461Ssephe struct vmbus_channel *chan; 905302864Ssephe 906302864Ssephe chan = malloc(sizeof(*chan), M_DEVBUF, M_WAITOK | M_ZERO); 907302864Ssephe 908302864Ssephe chan->ch_monprm = hyperv_dmamem_alloc(bus_get_dma_tag(sc->vmbus_dev), 909302864Ssephe HYPERCALL_PARAM_ALIGN, 0, sizeof(struct hyperv_mon_param), 910302864Ssephe &chan->ch_monprm_dma, BUS_DMA_WAITOK | BUS_DMA_ZERO); 911302864Ssephe if (chan->ch_monprm == NULL) { 912302864Ssephe device_printf(sc->vmbus_dev, "monprm alloc failed\n"); 913302864Ssephe free(chan, M_DEVBUF); 914302864Ssephe return NULL; 915302864Ssephe } 916302864Ssephe 917307461Ssephe chan->ch_vmbus = sc; 918302864Ssephe mtx_init(&chan->ch_subchan_lock, "vmbus subchan", NULL, MTX_DEF); 919302864Ssephe TAILQ_INIT(&chan->ch_subchans); 920302864Ssephe TASK_INIT(&chan->ch_detach_task, 0, vmbus_chan_detach_task, chan); 921307464Ssephe vmbus_rxbr_init(&chan->ch_rxbr); 922307464Ssephe vmbus_txbr_init(&chan->ch_txbr); 923302864Ssephe 924302864Ssephe return chan; 925302864Ssephe} 926302864Ssephe 927302864Ssephestatic void 928307461Ssephevmbus_chan_free(struct vmbus_channel *chan) 929302864Ssephe{ 930302864Ssephe /* TODO: assert sub-channel list is empty */ 931302864Ssephe /* TODO: asset no longer on the primary channel's sub-channel list */ 932302864Ssephe /* TODO: asset no longer on the vmbus channel list */ 933302864Ssephe hyperv_dmamem_free(&chan->ch_monprm_dma, chan->ch_monprm); 934302864Ssephe mtx_destroy(&chan->ch_subchan_lock); 935307464Ssephe vmbus_rxbr_deinit(&chan->ch_rxbr); 936307464Ssephe vmbus_txbr_deinit(&chan->ch_txbr); 937302864Ssephe free(chan, M_DEVBUF); 938302864Ssephe} 939302864Ssephe 940302864Ssephestatic int 941307461Ssephevmbus_chan_add(struct vmbus_channel *newchan) 942302864Ssephe{ 943307461Ssephe struct vmbus_softc *sc = newchan->ch_vmbus; 944307461Ssephe struct vmbus_channel *prichan; 945302864Ssephe 946302864Ssephe if (newchan->ch_id == 0) { 947302864Ssephe /* 948302864Ssephe * XXX 949302864Ssephe * Chan0 will neither be processed nor should be offered; 950302864Ssephe * skip it. 951302864Ssephe */ 952302864Ssephe device_printf(sc->vmbus_dev, "got chan0 offer, discard\n"); 953302864Ssephe return EINVAL; 954302864Ssephe } else if (newchan->ch_id >= VMBUS_CHAN_MAX) { 955302864Ssephe device_printf(sc->vmbus_dev, "invalid chan%u offer\n", 956302864Ssephe newchan->ch_id); 957302864Ssephe return EINVAL; 958302864Ssephe } 959302864Ssephe sc->vmbus_chmap[newchan->ch_id] = newchan; 960302864Ssephe 961302864Ssephe if (bootverbose) { 962302864Ssephe device_printf(sc->vmbus_dev, "chan%u subidx%u offer\n", 963302864Ssephe newchan->ch_id, newchan->ch_subidx); 964302864Ssephe } 965302864Ssephe 966302864Ssephe mtx_lock(&sc->vmbus_prichan_lock); 967302864Ssephe TAILQ_FOREACH(prichan, &sc->vmbus_prichans, ch_prilink) { 968302864Ssephe /* 969302864Ssephe * Sub-channel will have the same type GUID and instance 970302864Ssephe * GUID as its primary channel. 971302864Ssephe */ 972302864Ssephe if (memcmp(&prichan->ch_guid_type, &newchan->ch_guid_type, 973302864Ssephe sizeof(struct hyperv_guid)) == 0 && 974302864Ssephe memcmp(&prichan->ch_guid_inst, &newchan->ch_guid_inst, 975302864Ssephe sizeof(struct hyperv_guid)) == 0) 976302864Ssephe break; 977302864Ssephe } 978302864Ssephe if (VMBUS_CHAN_ISPRIMARY(newchan)) { 979302864Ssephe if (prichan == NULL) { 980302864Ssephe /* Install the new primary channel */ 981302864Ssephe TAILQ_INSERT_TAIL(&sc->vmbus_prichans, newchan, 982302864Ssephe ch_prilink); 983302864Ssephe mtx_unlock(&sc->vmbus_prichan_lock); 984302864Ssephe return 0; 985302864Ssephe } else { 986302864Ssephe mtx_unlock(&sc->vmbus_prichan_lock); 987302864Ssephe device_printf(sc->vmbus_dev, "duplicated primary " 988302864Ssephe "chan%u\n", newchan->ch_id); 989302864Ssephe return EINVAL; 990302864Ssephe } 991302864Ssephe } else { /* Sub-channel */ 992302864Ssephe if (prichan == NULL) { 993302864Ssephe mtx_unlock(&sc->vmbus_prichan_lock); 994302864Ssephe device_printf(sc->vmbus_dev, "no primary chan for " 995302864Ssephe "chan%u\n", newchan->ch_id); 996302864Ssephe return EINVAL; 997302864Ssephe } 998302864Ssephe /* 999302864Ssephe * Found the primary channel for this sub-channel and 1000302864Ssephe * move on. 1001302864Ssephe * 1002302864Ssephe * XXX refcnt prichan 1003302864Ssephe */ 1004302864Ssephe } 1005302864Ssephe mtx_unlock(&sc->vmbus_prichan_lock); 1006302864Ssephe 1007302864Ssephe /* 1008302864Ssephe * This is a sub-channel; link it with the primary channel. 1009302864Ssephe */ 1010302864Ssephe KASSERT(!VMBUS_CHAN_ISPRIMARY(newchan), 1011302864Ssephe ("new channel is not sub-channel")); 1012302864Ssephe KASSERT(prichan != NULL, ("no primary channel")); 1013302864Ssephe 1014302864Ssephe newchan->ch_prichan = prichan; 1015302864Ssephe newchan->ch_dev = prichan->ch_dev; 1016302864Ssephe 1017302864Ssephe mtx_lock(&prichan->ch_subchan_lock); 1018302864Ssephe TAILQ_INSERT_TAIL(&prichan->ch_subchans, newchan, ch_sublink); 1019302864Ssephe /* 1020302864Ssephe * Bump up sub-channel count and notify anyone that is 1021302864Ssephe * interested in this sub-channel, after this sub-channel 1022302864Ssephe * is setup. 1023302864Ssephe */ 1024302864Ssephe prichan->ch_subchan_cnt++; 1025302864Ssephe mtx_unlock(&prichan->ch_subchan_lock); 1026302864Ssephe wakeup(prichan); 1027302864Ssephe 1028302864Ssephe return 0; 1029302864Ssephe} 1030302864Ssephe 1031302864Ssephevoid 1032307461Ssephevmbus_chan_cpu_set(struct vmbus_channel *chan, int cpu) 1033302864Ssephe{ 1034302864Ssephe KASSERT(cpu >= 0 && cpu < mp_ncpus, ("invalid cpu %d", cpu)); 1035302864Ssephe 1036307461Ssephe if (chan->ch_vmbus->vmbus_version == VMBUS_VERSION_WS2008 || 1037307461Ssephe chan->ch_vmbus->vmbus_version == VMBUS_VERSION_WIN7) { 1038302864Ssephe /* Only cpu0 is supported */ 1039302864Ssephe cpu = 0; 1040302864Ssephe } 1041302864Ssephe 1042302873Ssephe chan->ch_cpuid = cpu; 1043307461Ssephe chan->ch_vcpuid = VMBUS_PCPU_GET(chan->ch_vmbus, vcpuid, cpu); 1044302864Ssephe 1045302864Ssephe if (bootverbose) { 1046302864Ssephe printf("vmbus_chan%u: assigned to cpu%u [vcpu%u]\n", 1047302873Ssephe chan->ch_id, chan->ch_cpuid, chan->ch_vcpuid); 1048302864Ssephe } 1049302864Ssephe} 1050302864Ssephe 1051302864Ssephevoid 1052307461Ssephevmbus_chan_cpu_rr(struct vmbus_channel *chan) 1053302864Ssephe{ 1054302864Ssephe static uint32_t vmbus_chan_nextcpu; 1055302864Ssephe int cpu; 1056302864Ssephe 1057302864Ssephe cpu = atomic_fetchadd_int(&vmbus_chan_nextcpu, 1) % mp_ncpus; 1058302890Ssephe vmbus_chan_cpu_set(chan, cpu); 1059302864Ssephe} 1060302864Ssephe 1061302864Ssephestatic void 1062307461Ssephevmbus_chan_cpu_default(struct vmbus_channel *chan) 1063302864Ssephe{ 1064302864Ssephe /* 1065302864Ssephe * By default, pin the channel to cpu0. Devices having 1066302864Ssephe * special channel-cpu mapping requirement should call 1067302890Ssephe * vmbus_chan_cpu_{set,rr}(). 1068302864Ssephe */ 1069302890Ssephe vmbus_chan_cpu_set(chan, 0); 1070302864Ssephe} 1071302864Ssephe 1072302864Ssephestatic void 1073302864Ssephevmbus_chan_msgproc_choffer(struct vmbus_softc *sc, 1074302864Ssephe const struct vmbus_message *msg) 1075302864Ssephe{ 1076302864Ssephe const struct vmbus_chanmsg_choffer *offer; 1077307461Ssephe struct vmbus_channel *chan; 1078302864Ssephe int error; 1079302864Ssephe 1080302864Ssephe offer = (const struct vmbus_chanmsg_choffer *)msg->msg_data; 1081302864Ssephe 1082302864Ssephe chan = vmbus_chan_alloc(sc); 1083302864Ssephe if (chan == NULL) { 1084302864Ssephe device_printf(sc->vmbus_dev, "allocate chan%u failed\n", 1085302864Ssephe offer->chm_chanid); 1086302864Ssephe return; 1087302864Ssephe } 1088302864Ssephe 1089302864Ssephe chan->ch_id = offer->chm_chanid; 1090302864Ssephe chan->ch_subidx = offer->chm_subidx; 1091302864Ssephe chan->ch_guid_type = offer->chm_chtype; 1092302864Ssephe chan->ch_guid_inst = offer->chm_chinst; 1093302864Ssephe 1094302864Ssephe /* Batch reading is on by default */ 1095302864Ssephe chan->ch_flags |= VMBUS_CHAN_FLAG_BATCHREAD; 1096302864Ssephe 1097302864Ssephe chan->ch_monprm->mp_connid = VMBUS_CONNID_EVENT; 1098302864Ssephe if (sc->vmbus_version != VMBUS_VERSION_WS2008) 1099302864Ssephe chan->ch_monprm->mp_connid = offer->chm_connid; 1100302864Ssephe 1101302864Ssephe if (offer->chm_flags1 & VMBUS_CHOFFER_FLAG1_HASMNF) { 1102307461Ssephe int trig_idx; 1103307461Ssephe 1104302864Ssephe /* 1105302864Ssephe * Setup MNF stuffs. 1106302864Ssephe */ 1107307461Ssephe chan->ch_txflags |= VMBUS_CHAN_TXF_HASMNF; 1108307461Ssephe 1109307461Ssephe trig_idx = offer->chm_montrig / VMBUS_MONTRIG_LEN; 1110307461Ssephe if (trig_idx >= VMBUS_MONTRIGS_MAX) 1111302864Ssephe panic("invalid monitor trigger %u", offer->chm_montrig); 1112307461Ssephe chan->ch_montrig = 1113307461Ssephe &sc->vmbus_mnf2->mnf_trigs[trig_idx].mt_pending; 1114307461Ssephe 1115302864Ssephe chan->ch_montrig_mask = 1116302864Ssephe 1 << (offer->chm_montrig % VMBUS_MONTRIG_LEN); 1117302864Ssephe } 1118302864Ssephe 1119307461Ssephe /* 1120307461Ssephe * Setup event flag. 1121307461Ssephe */ 1122307461Ssephe chan->ch_evtflag = 1123307461Ssephe &sc->vmbus_tx_evtflags[chan->ch_id >> VMBUS_EVTFLAG_SHIFT]; 1124307461Ssephe chan->ch_evtflag_mask = 1UL << (chan->ch_id & VMBUS_EVTFLAG_MASK); 1125307461Ssephe 1126302864Ssephe /* Select default cpu for this channel. */ 1127302864Ssephe vmbus_chan_cpu_default(chan); 1128302864Ssephe 1129302864Ssephe error = vmbus_chan_add(chan); 1130302864Ssephe if (error) { 1131302864Ssephe device_printf(sc->vmbus_dev, "add chan%u failed: %d\n", 1132302864Ssephe chan->ch_id, error); 1133302864Ssephe vmbus_chan_free(chan); 1134302864Ssephe return; 1135302864Ssephe } 1136302864Ssephe 1137302864Ssephe if (VMBUS_CHAN_ISPRIMARY(chan)) { 1138302864Ssephe /* 1139302864Ssephe * Add device for this primary channel. 1140302864Ssephe * 1141302864Ssephe * NOTE: 1142302864Ssephe * Error is ignored here; don't have much to do if error 1143302864Ssephe * really happens. 1144302864Ssephe */ 1145302868Ssephe vmbus_add_child(chan); 1146302864Ssephe } 1147302864Ssephe} 1148302864Ssephe 1149302864Ssephe/* 1150302864Ssephe * XXX pretty broken; need rework. 1151302864Ssephe */ 1152302864Ssephestatic void 1153302864Ssephevmbus_chan_msgproc_chrescind(struct vmbus_softc *sc, 1154302864Ssephe const struct vmbus_message *msg) 1155302864Ssephe{ 1156302864Ssephe const struct vmbus_chanmsg_chrescind *note; 1157307461Ssephe struct vmbus_channel *chan; 1158302864Ssephe 1159302864Ssephe note = (const struct vmbus_chanmsg_chrescind *)msg->msg_data; 1160302864Ssephe if (note->chm_chanid > VMBUS_CHAN_MAX) { 1161302864Ssephe device_printf(sc->vmbus_dev, "invalid rescinded chan%u\n", 1162302864Ssephe note->chm_chanid); 1163302864Ssephe return; 1164302864Ssephe } 1165302864Ssephe 1166302864Ssephe if (bootverbose) { 1167302864Ssephe device_printf(sc->vmbus_dev, "chan%u rescinded\n", 1168302864Ssephe note->chm_chanid); 1169302864Ssephe } 1170302864Ssephe 1171302864Ssephe chan = sc->vmbus_chmap[note->chm_chanid]; 1172302864Ssephe if (chan == NULL) 1173302864Ssephe return; 1174302864Ssephe sc->vmbus_chmap[note->chm_chanid] = NULL; 1175302864Ssephe 1176302864Ssephe taskqueue_enqueue(taskqueue_thread, &chan->ch_detach_task); 1177302864Ssephe} 1178302864Ssephe 1179302864Ssephestatic void 1180302864Ssephevmbus_chan_detach_task(void *xchan, int pending __unused) 1181302864Ssephe{ 1182307461Ssephe struct vmbus_channel *chan = xchan; 1183302864Ssephe 1184302864Ssephe if (VMBUS_CHAN_ISPRIMARY(chan)) { 1185302864Ssephe /* Only primary channel owns the device */ 1186302868Ssephe vmbus_delete_child(chan); 1187302864Ssephe /* NOTE: DO NOT free primary channel for now */ 1188302864Ssephe } else { 1189307461Ssephe struct vmbus_softc *sc = chan->ch_vmbus; 1190307461Ssephe struct vmbus_channel *pri_chan = chan->ch_prichan; 1191302864Ssephe struct vmbus_chanmsg_chfree *req; 1192302864Ssephe struct vmbus_msghc *mh; 1193302864Ssephe int error; 1194302864Ssephe 1195302864Ssephe mh = vmbus_msghc_get(sc, sizeof(*req)); 1196302864Ssephe if (mh == NULL) { 1197302864Ssephe device_printf(sc->vmbus_dev, 1198302864Ssephe "can not get msg hypercall for chfree(chan%u)\n", 1199302864Ssephe chan->ch_id); 1200302864Ssephe goto remove; 1201302864Ssephe } 1202302864Ssephe 1203302864Ssephe req = vmbus_msghc_dataptr(mh); 1204302864Ssephe req->chm_hdr.chm_type = VMBUS_CHANMSG_TYPE_CHFREE; 1205302864Ssephe req->chm_chanid = chan->ch_id; 1206302864Ssephe 1207302864Ssephe error = vmbus_msghc_exec_noresult(mh); 1208302864Ssephe vmbus_msghc_put(sc, mh); 1209302864Ssephe 1210302864Ssephe if (error) { 1211302864Ssephe device_printf(sc->vmbus_dev, 1212302864Ssephe "chfree(chan%u) failed: %d", 1213302864Ssephe chan->ch_id, error); 1214302864Ssephe /* NOTE: Move on! */ 1215302864Ssephe } else { 1216302864Ssephe if (bootverbose) { 1217302864Ssephe device_printf(sc->vmbus_dev, "chan%u freed\n", 1218302864Ssephe chan->ch_id); 1219302864Ssephe } 1220302864Ssephe } 1221302864Ssepheremove: 1222302864Ssephe mtx_lock(&pri_chan->ch_subchan_lock); 1223302864Ssephe TAILQ_REMOVE(&pri_chan->ch_subchans, chan, ch_sublink); 1224302864Ssephe KASSERT(pri_chan->ch_subchan_cnt > 0, 1225302864Ssephe ("invalid subchan_cnt %d", pri_chan->ch_subchan_cnt)); 1226302864Ssephe pri_chan->ch_subchan_cnt--; 1227302864Ssephe mtx_unlock(&pri_chan->ch_subchan_lock); 1228302864Ssephe wakeup(pri_chan); 1229302864Ssephe 1230302864Ssephe vmbus_chan_free(chan); 1231302864Ssephe } 1232302864Ssephe} 1233302864Ssephe 1234302864Ssephe/* 1235302864Ssephe * Detach all devices and destroy the corresponding primary channels. 1236302864Ssephe */ 1237302864Ssephevoid 1238302864Ssephevmbus_chan_destroy_all(struct vmbus_softc *sc) 1239302864Ssephe{ 1240307461Ssephe struct vmbus_channel *chan; 1241302864Ssephe 1242302864Ssephe mtx_lock(&sc->vmbus_prichan_lock); 1243302864Ssephe while ((chan = TAILQ_FIRST(&sc->vmbus_prichans)) != NULL) { 1244302864Ssephe KASSERT(VMBUS_CHAN_ISPRIMARY(chan), ("not primary channel")); 1245302864Ssephe TAILQ_REMOVE(&sc->vmbus_prichans, chan, ch_prilink); 1246302864Ssephe mtx_unlock(&sc->vmbus_prichan_lock); 1247302864Ssephe 1248302868Ssephe vmbus_delete_child(chan); 1249302864Ssephe vmbus_chan_free(chan); 1250302864Ssephe 1251302864Ssephe mtx_lock(&sc->vmbus_prichan_lock); 1252302864Ssephe } 1253302864Ssephe bzero(sc->vmbus_chmap, 1254307461Ssephe sizeof(struct vmbus_channel *) * VMBUS_CHAN_MAX); 1255302864Ssephe mtx_unlock(&sc->vmbus_prichan_lock); 1256302864Ssephe} 1257302864Ssephe 1258303020Ssephe/* 1259302864Ssephe * The channel whose vcpu binding is closest to the currect vcpu will 1260302864Ssephe * be selected. 1261303020Ssephe * If no multi-channel, always select primary channel. 1262302864Ssephe */ 1263307461Ssephestruct vmbus_channel * 1264307461Ssephevmbus_chan_cpu2chan(struct vmbus_channel *prichan, int cpu) 1265302864Ssephe{ 1266307461Ssephe struct vmbus_channel *sel, *chan; 1267303020Ssephe uint32_t vcpu, sel_dist; 1268302864Ssephe 1269303020Ssephe KASSERT(cpu >= 0 && cpu < mp_ncpus, ("invalid cpuid %d", cpu)); 1270303020Ssephe if (TAILQ_EMPTY(&prichan->ch_subchans)) 1271303020Ssephe return prichan; 1272302864Ssephe 1273307461Ssephe vcpu = VMBUS_PCPU_GET(prichan->ch_vmbus, vcpuid, cpu); 1274302864Ssephe 1275303020Ssephe#define CHAN_VCPU_DIST(ch, vcpu) \ 1276303020Ssephe (((ch)->ch_vcpuid > (vcpu)) ? \ 1277303020Ssephe ((ch)->ch_vcpuid - (vcpu)) : ((vcpu) - (ch)->ch_vcpuid)) 1278302864Ssephe 1279303020Ssephe#define CHAN_SELECT(ch) \ 1280303020Ssephedo { \ 1281303020Ssephe sel = ch; \ 1282303020Ssephe sel_dist = CHAN_VCPU_DIST(ch, vcpu); \ 1283303020Ssephe} while (0) 1284302864Ssephe 1285303020Ssephe CHAN_SELECT(prichan); 1286302864Ssephe 1287303020Ssephe mtx_lock(&prichan->ch_subchan_lock); 1288303020Ssephe TAILQ_FOREACH(chan, &prichan->ch_subchans, ch_sublink) { 1289303020Ssephe uint32_t dist; 1290302864Ssephe 1291303020Ssephe KASSERT(chan->ch_stflags & VMBUS_CHAN_ST_OPENED, 1292303020Ssephe ("chan%u is not opened", chan->ch_id)); 1293303020Ssephe 1294303020Ssephe if (chan->ch_vcpuid == vcpu) { 1295303020Ssephe /* Exact match; done */ 1296303020Ssephe CHAN_SELECT(chan); 1297303020Ssephe break; 1298303020Ssephe } 1299303020Ssephe 1300303020Ssephe dist = CHAN_VCPU_DIST(chan, vcpu); 1301303020Ssephe if (sel_dist <= dist) { 1302303020Ssephe /* Far or same distance; skip */ 1303302864Ssephe continue; 1304302864Ssephe } 1305302864Ssephe 1306303020Ssephe /* Select the closer channel. */ 1307303020Ssephe CHAN_SELECT(chan); 1308302864Ssephe } 1309303020Ssephe mtx_unlock(&prichan->ch_subchan_lock); 1310302864Ssephe 1311303020Ssephe#undef CHAN_SELECT 1312303020Ssephe#undef CHAN_VCPU_DIST 1313303020Ssephe 1314303020Ssephe return sel; 1315302864Ssephe} 1316302864Ssephe 1317307461Ssephestruct vmbus_channel ** 1318307461Ssephevmbus_subchan_get(struct vmbus_channel *pri_chan, int subchan_cnt) 1319302864Ssephe{ 1320307461Ssephe struct vmbus_channel **ret, *chan; 1321302864Ssephe int i; 1322302864Ssephe 1323307461Ssephe ret = malloc(subchan_cnt * sizeof(struct vmbus_channel *), M_TEMP, 1324302864Ssephe M_WAITOK); 1325302864Ssephe 1326302864Ssephe mtx_lock(&pri_chan->ch_subchan_lock); 1327302864Ssephe 1328302864Ssephe while (pri_chan->ch_subchan_cnt < subchan_cnt) 1329302864Ssephe mtx_sleep(pri_chan, &pri_chan->ch_subchan_lock, 0, "subch", 0); 1330302864Ssephe 1331302864Ssephe i = 0; 1332302864Ssephe TAILQ_FOREACH(chan, &pri_chan->ch_subchans, ch_sublink) { 1333302864Ssephe /* TODO: refcnt chan */ 1334302864Ssephe ret[i] = chan; 1335302864Ssephe 1336302864Ssephe ++i; 1337302864Ssephe if (i == subchan_cnt) 1338302864Ssephe break; 1339302864Ssephe } 1340302864Ssephe KASSERT(i == subchan_cnt, ("invalid subchan count %d, should be %d", 1341302864Ssephe pri_chan->ch_subchan_cnt, subchan_cnt)); 1342302864Ssephe 1343302864Ssephe mtx_unlock(&pri_chan->ch_subchan_lock); 1344302864Ssephe 1345302864Ssephe return ret; 1346302864Ssephe} 1347302864Ssephe 1348302864Ssephevoid 1349307461Ssephevmbus_subchan_rel(struct vmbus_channel **subchan, int subchan_cnt __unused) 1350302864Ssephe{ 1351302864Ssephe 1352302864Ssephe free(subchan, M_TEMP); 1353302864Ssephe} 1354302864Ssephe 1355302864Ssephevoid 1356307461Ssephevmbus_subchan_drain(struct vmbus_channel *pri_chan) 1357302864Ssephe{ 1358302864Ssephe mtx_lock(&pri_chan->ch_subchan_lock); 1359302864Ssephe while (pri_chan->ch_subchan_cnt > 0) 1360302864Ssephe mtx_sleep(pri_chan, &pri_chan->ch_subchan_lock, 0, "dsubch", 0); 1361302864Ssephe mtx_unlock(&pri_chan->ch_subchan_lock); 1362302864Ssephe} 1363302864Ssephe 1364302864Ssephevoid 1365302864Ssephevmbus_chan_msgproc(struct vmbus_softc *sc, const struct vmbus_message *msg) 1366302864Ssephe{ 1367302864Ssephe vmbus_chanmsg_proc_t msg_proc; 1368302864Ssephe uint32_t msg_type; 1369302864Ssephe 1370302864Ssephe msg_type = ((const struct vmbus_chanmsg_hdr *)msg->msg_data)->chm_type; 1371302864Ssephe KASSERT(msg_type < VMBUS_CHANMSG_TYPE_MAX, 1372302864Ssephe ("invalid message type %u", msg_type)); 1373302864Ssephe 1374302864Ssephe msg_proc = vmbus_chan_msgprocs[msg_type]; 1375302864Ssephe if (msg_proc != NULL) 1376302864Ssephe msg_proc(sc, msg); 1377302864Ssephe} 1378303021Ssephe 1379303021Ssephevoid 1380307461Ssephevmbus_chan_set_readbatch(struct vmbus_channel *chan, bool on) 1381303021Ssephe{ 1382303021Ssephe if (!on) 1383303021Ssephe chan->ch_flags &= ~VMBUS_CHAN_FLAG_BATCHREAD; 1384303021Ssephe else 1385303021Ssephe chan->ch_flags |= VMBUS_CHAN_FLAG_BATCHREAD; 1386303021Ssephe} 1387307461Ssephe 1388307461Ssepheuint32_t 1389307461Ssephevmbus_chan_id(const struct vmbus_channel *chan) 1390307461Ssephe{ 1391307461Ssephe return chan->ch_id; 1392307461Ssephe} 1393307461Ssephe 1394307461Ssepheuint32_t 1395307461Ssephevmbus_chan_subidx(const struct vmbus_channel *chan) 1396307461Ssephe{ 1397307461Ssephe return chan->ch_subidx; 1398307461Ssephe} 1399307461Ssephe 1400307461Ssephebool 1401307461Ssephevmbus_chan_is_primary(const struct vmbus_channel *chan) 1402307461Ssephe{ 1403307461Ssephe if (VMBUS_CHAN_ISPRIMARY(chan)) 1404307461Ssephe return true; 1405307461Ssephe else 1406307461Ssephe return false; 1407307461Ssephe} 1408307461Ssephe 1409307461Ssepheconst struct hyperv_guid * 1410307461Ssephevmbus_chan_guid_inst(const struct vmbus_channel *chan) 1411307461Ssephe{ 1412307461Ssephe return &chan->ch_guid_inst; 1413307461Ssephe} 1414307486Ssephe 1415307486Ssepheint 1416307486Ssephevmbus_chan_prplist_nelem(int br_size, int prpcnt_max, int dlen_max) 1417307486Ssephe{ 1418307486Ssephe int elem_size; 1419307486Ssephe 1420307486Ssephe elem_size = __offsetof(struct vmbus_chanpkt_prplist, 1421307486Ssephe cp_range[0].gpa_page[prpcnt_max]); 1422307486Ssephe elem_size += dlen_max; 1423307486Ssephe elem_size = VMBUS_CHANPKT_TOTLEN(elem_size); 1424307486Ssephe 1425307486Ssephe return (vmbus_br_nelem(br_size, elem_size)); 1426307486Ssephe} 1427