vmbus_chan.c revision 307498
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 307498 2016-10-17 08:10:24Z 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) 724307498Ssephe return (error); 725250199Sgrehan 726307498Ssephe if (__predict_false(pkt.cph_hlen < VMBUS_CHANPKT_HLEN_MIN)) { 727307498Ssephe device_printf(chan->ch_dev, "invalid hlen %u\n", 728307498Ssephe pkt.cph_hlen); 729307498Ssephe /* XXX this channel is dead actually. */ 730307498Ssephe return (EIO); 731307498Ssephe } 732307498Ssephe if (__predict_false(pkt.cph_hlen > pkt.cph_tlen)) { 733307498Ssephe device_printf(chan->ch_dev, "invalid hlen %u and tlen %u\n", 734307498Ssephe pkt.cph_hlen, pkt.cph_tlen); 735307498Ssephe /* XXX this channel is dead actually. */ 736307498Ssephe return (EIO); 737307498Ssephe } 738307498Ssephe 739302885Ssephe hlen = VMBUS_CHANPKT_GETLEN(pkt.cph_hlen); 740302885Ssephe dlen = VMBUS_CHANPKT_GETLEN(pkt.cph_tlen) - hlen; 741250199Sgrehan 742302885Ssephe if (*dlen0 < dlen) { 743302886Ssephe /* Return the size of this packet's data. */ 744302885Ssephe *dlen0 = dlen; 745307498Ssephe return (ENOBUFS); 746302885Ssephe } 747250199Sgrehan 748302885Ssephe *xactid = pkt.cph_xactid; 749302885Ssephe *dlen0 = dlen; 750250199Sgrehan 751302886Ssephe /* Skip packet header */ 752307464Ssephe error = vmbus_rxbr_read(&chan->ch_rxbr, data, dlen, hlen); 753307464Ssephe KASSERT(!error, ("vmbus_rxbr_read failed")); 754250199Sgrehan 755307498Ssephe return (0); 756250199Sgrehan} 757250199Sgrehan 758250199Sgrehanint 759307461Ssephevmbus_chan_recv_pkt(struct vmbus_channel *chan, 760302886Ssephe struct vmbus_chanpkt_hdr *pkt0, int *pktlen0) 761250199Sgrehan{ 762302886Ssephe struct vmbus_chanpkt_hdr pkt; 763302886Ssephe int error, pktlen; 764250199Sgrehan 765307464Ssephe error = vmbus_rxbr_peek(&chan->ch_rxbr, &pkt, sizeof(pkt)); 766302886Ssephe if (error) 767307498Ssephe return (error); 768250199Sgrehan 769307498Ssephe if (__predict_false(pkt.cph_hlen < VMBUS_CHANPKT_HLEN_MIN)) { 770307498Ssephe device_printf(chan->ch_dev, "invalid hlen %u\n", 771307498Ssephe pkt.cph_hlen); 772307498Ssephe /* XXX this channel is dead actually. */ 773307498Ssephe return (EIO); 774307498Ssephe } 775307498Ssephe if (__predict_false(pkt.cph_hlen > pkt.cph_tlen)) { 776307498Ssephe device_printf(chan->ch_dev, "invalid hlen %u and tlen %u\n", 777307498Ssephe pkt.cph_hlen, pkt.cph_tlen); 778307498Ssephe /* XXX this channel is dead actually. */ 779307498Ssephe return (EIO); 780307498Ssephe } 781307498Ssephe 782302886Ssephe pktlen = VMBUS_CHANPKT_GETLEN(pkt.cph_tlen); 783302886Ssephe if (*pktlen0 < pktlen) { 784302886Ssephe /* Return the size of this packet. */ 785302886Ssephe *pktlen0 = pktlen; 786307498Ssephe return (ENOBUFS); 787302886Ssephe } 788302886Ssephe *pktlen0 = pktlen; 789250199Sgrehan 790302886Ssephe /* Include packet header */ 791307464Ssephe error = vmbus_rxbr_read(&chan->ch_rxbr, pkt0, pktlen, 0); 792307464Ssephe KASSERT(!error, ("vmbus_rxbr_read failed")); 793250199Sgrehan 794307498Ssephe return (0); 795250199Sgrehan} 796294886Ssephe 797294886Ssephestatic void 798302713Ssephevmbus_chan_task(void *xchan, int pending __unused) 799294886Ssephe{ 800307461Ssephe struct vmbus_channel *chan = xchan; 801302874Ssephe vmbus_chan_callback_t cb = chan->ch_cb; 802302874Ssephe void *cbarg = chan->ch_cbarg; 803294886Ssephe 804302710Ssephe /* 805302710Ssephe * Optimize host to guest signaling by ensuring: 806302710Ssephe * 1. While reading the channel, we disable interrupts from 807302710Ssephe * host. 808302710Ssephe * 2. Ensure that we process all posted messages from the host 809302710Ssephe * before returning from this callback. 810302710Ssephe * 3. Once we return, enable signaling from the host. Once this 811302710Ssephe * state is set we check to see if additional packets are 812302710Ssephe * available to read. In this case we repeat the process. 813302713Ssephe * 814302713Ssephe * NOTE: Interrupt has been disabled in the ISR. 815302710Ssephe */ 816302713Ssephe for (;;) { 817302713Ssephe uint32_t left; 818294886Ssephe 819307461Ssephe cb(chan, cbarg); 820294886Ssephe 821307464Ssephe left = vmbus_rxbr_intr_unmask(&chan->ch_rxbr); 822302713Ssephe if (left == 0) { 823302713Ssephe /* No more data in RX bufring; done */ 824302713Ssephe break; 825302713Ssephe } 826307464Ssephe vmbus_rxbr_intr_mask(&chan->ch_rxbr); 827302713Ssephe } 828294886Ssephe} 829302692Ssephe 830302713Ssephestatic void 831302713Ssephevmbus_chan_task_nobatch(void *xchan, int pending __unused) 832302713Ssephe{ 833307461Ssephe struct vmbus_channel *chan = xchan; 834302713Ssephe 835307461Ssephe chan->ch_cb(chan, chan->ch_cbarg); 836302713Ssephe} 837302713Ssephe 838302692Ssephestatic __inline void 839302692Ssephevmbus_event_flags_proc(struct vmbus_softc *sc, volatile u_long *event_flags, 840302692Ssephe int flag_cnt) 841302692Ssephe{ 842302692Ssephe int f; 843302692Ssephe 844302692Ssephe for (f = 0; f < flag_cnt; ++f) { 845302806Ssephe uint32_t chid_base; 846302692Ssephe u_long flags; 847302806Ssephe int chid_ofs; 848302692Ssephe 849302692Ssephe if (event_flags[f] == 0) 850302692Ssephe continue; 851302692Ssephe 852302692Ssephe flags = atomic_swap_long(&event_flags[f], 0); 853302806Ssephe chid_base = f << VMBUS_EVTFLAG_SHIFT; 854302692Ssephe 855302806Ssephe while ((chid_ofs = ffsl(flags)) != 0) { 856307461Ssephe struct vmbus_channel *chan; 857302692Ssephe 858302806Ssephe --chid_ofs; /* NOTE: ffsl is 1-based */ 859302806Ssephe flags &= ~(1UL << chid_ofs); 860302692Ssephe 861303022Ssephe chan = sc->vmbus_chmap[chid_base + chid_ofs]; 862302692Ssephe 863302692Ssephe /* if channel is closed or closing */ 864303022Ssephe if (chan == NULL || chan->ch_tq == NULL) 865302692Ssephe continue; 866302692Ssephe 867303022Ssephe if (chan->ch_flags & VMBUS_CHAN_FLAG_BATCHREAD) 868307464Ssephe vmbus_rxbr_intr_mask(&chan->ch_rxbr); 869303022Ssephe taskqueue_enqueue(chan->ch_tq, &chan->ch_task); 870302692Ssephe } 871302692Ssephe } 872302692Ssephe} 873302692Ssephe 874302692Ssephevoid 875302692Ssephevmbus_event_proc(struct vmbus_softc *sc, int cpu) 876302692Ssephe{ 877302692Ssephe struct vmbus_evtflags *eventf; 878302692Ssephe 879302692Ssephe /* 880302692Ssephe * On Host with Win8 or above, the event page can be checked directly 881302692Ssephe * to get the id of the channel that has the pending interrupt. 882302692Ssephe */ 883302692Ssephe eventf = VMBUS_PCPU_GET(sc, event_flags, cpu) + VMBUS_SINT_MESSAGE; 884302692Ssephe vmbus_event_flags_proc(sc, eventf->evt_flags, 885302692Ssephe VMBUS_PCPU_GET(sc, event_flags_cnt, cpu)); 886302692Ssephe} 887302692Ssephe 888302692Ssephevoid 889302692Ssephevmbus_event_proc_compat(struct vmbus_softc *sc, int cpu) 890302692Ssephe{ 891302692Ssephe struct vmbus_evtflags *eventf; 892302692Ssephe 893302692Ssephe eventf = VMBUS_PCPU_GET(sc, event_flags, cpu) + VMBUS_SINT_MESSAGE; 894302692Ssephe if (atomic_testandclear_long(&eventf->evt_flags[0], 0)) { 895302692Ssephe vmbus_event_flags_proc(sc, sc->vmbus_rx_evtflags, 896302692Ssephe VMBUS_CHAN_MAX_COMPAT >> VMBUS_EVTFLAG_SHIFT); 897302692Ssephe } 898302692Ssephe} 899302692Ssephe 900302692Ssephestatic void 901302692Ssephevmbus_chan_update_evtflagcnt(struct vmbus_softc *sc, 902307461Ssephe const struct vmbus_channel *chan) 903302692Ssephe{ 904302692Ssephe volatile int *flag_cnt_ptr; 905302692Ssephe int flag_cnt; 906302692Ssephe 907302693Ssephe flag_cnt = (chan->ch_id / VMBUS_EVTFLAG_LEN) + 1; 908302873Ssephe flag_cnt_ptr = VMBUS_PCPU_PTR(sc, event_flags_cnt, chan->ch_cpuid); 909302692Ssephe 910302692Ssephe for (;;) { 911302692Ssephe int old_flag_cnt; 912302692Ssephe 913302692Ssephe old_flag_cnt = *flag_cnt_ptr; 914302692Ssephe if (old_flag_cnt >= flag_cnt) 915302692Ssephe break; 916302692Ssephe if (atomic_cmpset_int(flag_cnt_ptr, old_flag_cnt, flag_cnt)) { 917302692Ssephe if (bootverbose) { 918302692Ssephe device_printf(sc->vmbus_dev, 919302692Ssephe "channel%u update cpu%d flag_cnt to %d\n", 920302873Ssephe chan->ch_id, chan->ch_cpuid, flag_cnt); 921302692Ssephe } 922302692Ssephe break; 923302692Ssephe } 924302692Ssephe } 925302692Ssephe} 926302864Ssephe 927307461Ssephestatic struct vmbus_channel * 928302864Ssephevmbus_chan_alloc(struct vmbus_softc *sc) 929302864Ssephe{ 930307461Ssephe struct vmbus_channel *chan; 931302864Ssephe 932302864Ssephe chan = malloc(sizeof(*chan), M_DEVBUF, M_WAITOK | M_ZERO); 933302864Ssephe 934302864Ssephe chan->ch_monprm = hyperv_dmamem_alloc(bus_get_dma_tag(sc->vmbus_dev), 935302864Ssephe HYPERCALL_PARAM_ALIGN, 0, sizeof(struct hyperv_mon_param), 936302864Ssephe &chan->ch_monprm_dma, BUS_DMA_WAITOK | BUS_DMA_ZERO); 937302864Ssephe if (chan->ch_monprm == NULL) { 938302864Ssephe device_printf(sc->vmbus_dev, "monprm alloc failed\n"); 939302864Ssephe free(chan, M_DEVBUF); 940302864Ssephe return NULL; 941302864Ssephe } 942302864Ssephe 943307461Ssephe chan->ch_vmbus = sc; 944302864Ssephe mtx_init(&chan->ch_subchan_lock, "vmbus subchan", NULL, MTX_DEF); 945302864Ssephe TAILQ_INIT(&chan->ch_subchans); 946302864Ssephe TASK_INIT(&chan->ch_detach_task, 0, vmbus_chan_detach_task, chan); 947307464Ssephe vmbus_rxbr_init(&chan->ch_rxbr); 948307464Ssephe vmbus_txbr_init(&chan->ch_txbr); 949302864Ssephe 950302864Ssephe return chan; 951302864Ssephe} 952302864Ssephe 953302864Ssephestatic void 954307461Ssephevmbus_chan_free(struct vmbus_channel *chan) 955302864Ssephe{ 956302864Ssephe /* TODO: assert sub-channel list is empty */ 957302864Ssephe /* TODO: asset no longer on the primary channel's sub-channel list */ 958302864Ssephe /* TODO: asset no longer on the vmbus channel list */ 959302864Ssephe hyperv_dmamem_free(&chan->ch_monprm_dma, chan->ch_monprm); 960302864Ssephe mtx_destroy(&chan->ch_subchan_lock); 961307464Ssephe vmbus_rxbr_deinit(&chan->ch_rxbr); 962307464Ssephe vmbus_txbr_deinit(&chan->ch_txbr); 963302864Ssephe free(chan, M_DEVBUF); 964302864Ssephe} 965302864Ssephe 966302864Ssephestatic int 967307461Ssephevmbus_chan_add(struct vmbus_channel *newchan) 968302864Ssephe{ 969307461Ssephe struct vmbus_softc *sc = newchan->ch_vmbus; 970307461Ssephe struct vmbus_channel *prichan; 971302864Ssephe 972302864Ssephe if (newchan->ch_id == 0) { 973302864Ssephe /* 974302864Ssephe * XXX 975302864Ssephe * Chan0 will neither be processed nor should be offered; 976302864Ssephe * skip it. 977302864Ssephe */ 978302864Ssephe device_printf(sc->vmbus_dev, "got chan0 offer, discard\n"); 979302864Ssephe return EINVAL; 980302864Ssephe } else if (newchan->ch_id >= VMBUS_CHAN_MAX) { 981302864Ssephe device_printf(sc->vmbus_dev, "invalid chan%u offer\n", 982302864Ssephe newchan->ch_id); 983302864Ssephe return EINVAL; 984302864Ssephe } 985302864Ssephe sc->vmbus_chmap[newchan->ch_id] = newchan; 986302864Ssephe 987302864Ssephe if (bootverbose) { 988302864Ssephe device_printf(sc->vmbus_dev, "chan%u subidx%u offer\n", 989302864Ssephe newchan->ch_id, newchan->ch_subidx); 990302864Ssephe } 991302864Ssephe 992302864Ssephe mtx_lock(&sc->vmbus_prichan_lock); 993302864Ssephe TAILQ_FOREACH(prichan, &sc->vmbus_prichans, ch_prilink) { 994302864Ssephe /* 995302864Ssephe * Sub-channel will have the same type GUID and instance 996302864Ssephe * GUID as its primary channel. 997302864Ssephe */ 998302864Ssephe if (memcmp(&prichan->ch_guid_type, &newchan->ch_guid_type, 999302864Ssephe sizeof(struct hyperv_guid)) == 0 && 1000302864Ssephe memcmp(&prichan->ch_guid_inst, &newchan->ch_guid_inst, 1001302864Ssephe sizeof(struct hyperv_guid)) == 0) 1002302864Ssephe break; 1003302864Ssephe } 1004302864Ssephe if (VMBUS_CHAN_ISPRIMARY(newchan)) { 1005302864Ssephe if (prichan == NULL) { 1006302864Ssephe /* Install the new primary channel */ 1007302864Ssephe TAILQ_INSERT_TAIL(&sc->vmbus_prichans, newchan, 1008302864Ssephe ch_prilink); 1009302864Ssephe mtx_unlock(&sc->vmbus_prichan_lock); 1010302864Ssephe return 0; 1011302864Ssephe } else { 1012302864Ssephe mtx_unlock(&sc->vmbus_prichan_lock); 1013302864Ssephe device_printf(sc->vmbus_dev, "duplicated primary " 1014302864Ssephe "chan%u\n", newchan->ch_id); 1015302864Ssephe return EINVAL; 1016302864Ssephe } 1017302864Ssephe } else { /* Sub-channel */ 1018302864Ssephe if (prichan == NULL) { 1019302864Ssephe mtx_unlock(&sc->vmbus_prichan_lock); 1020302864Ssephe device_printf(sc->vmbus_dev, "no primary chan for " 1021302864Ssephe "chan%u\n", newchan->ch_id); 1022302864Ssephe return EINVAL; 1023302864Ssephe } 1024302864Ssephe /* 1025302864Ssephe * Found the primary channel for this sub-channel and 1026302864Ssephe * move on. 1027302864Ssephe * 1028302864Ssephe * XXX refcnt prichan 1029302864Ssephe */ 1030302864Ssephe } 1031302864Ssephe mtx_unlock(&sc->vmbus_prichan_lock); 1032302864Ssephe 1033302864Ssephe /* 1034302864Ssephe * This is a sub-channel; link it with the primary channel. 1035302864Ssephe */ 1036302864Ssephe KASSERT(!VMBUS_CHAN_ISPRIMARY(newchan), 1037302864Ssephe ("new channel is not sub-channel")); 1038302864Ssephe KASSERT(prichan != NULL, ("no primary channel")); 1039302864Ssephe 1040302864Ssephe newchan->ch_prichan = prichan; 1041302864Ssephe newchan->ch_dev = prichan->ch_dev; 1042302864Ssephe 1043302864Ssephe mtx_lock(&prichan->ch_subchan_lock); 1044302864Ssephe TAILQ_INSERT_TAIL(&prichan->ch_subchans, newchan, ch_sublink); 1045302864Ssephe /* 1046302864Ssephe * Bump up sub-channel count and notify anyone that is 1047302864Ssephe * interested in this sub-channel, after this sub-channel 1048302864Ssephe * is setup. 1049302864Ssephe */ 1050302864Ssephe prichan->ch_subchan_cnt++; 1051302864Ssephe mtx_unlock(&prichan->ch_subchan_lock); 1052302864Ssephe wakeup(prichan); 1053302864Ssephe 1054302864Ssephe return 0; 1055302864Ssephe} 1056302864Ssephe 1057302864Ssephevoid 1058307461Ssephevmbus_chan_cpu_set(struct vmbus_channel *chan, int cpu) 1059302864Ssephe{ 1060302864Ssephe KASSERT(cpu >= 0 && cpu < mp_ncpus, ("invalid cpu %d", cpu)); 1061302864Ssephe 1062307461Ssephe if (chan->ch_vmbus->vmbus_version == VMBUS_VERSION_WS2008 || 1063307461Ssephe chan->ch_vmbus->vmbus_version == VMBUS_VERSION_WIN7) { 1064302864Ssephe /* Only cpu0 is supported */ 1065302864Ssephe cpu = 0; 1066302864Ssephe } 1067302864Ssephe 1068302873Ssephe chan->ch_cpuid = cpu; 1069307461Ssephe chan->ch_vcpuid = VMBUS_PCPU_GET(chan->ch_vmbus, vcpuid, cpu); 1070302864Ssephe 1071302864Ssephe if (bootverbose) { 1072302864Ssephe printf("vmbus_chan%u: assigned to cpu%u [vcpu%u]\n", 1073302873Ssephe chan->ch_id, chan->ch_cpuid, chan->ch_vcpuid); 1074302864Ssephe } 1075302864Ssephe} 1076302864Ssephe 1077302864Ssephevoid 1078307461Ssephevmbus_chan_cpu_rr(struct vmbus_channel *chan) 1079302864Ssephe{ 1080302864Ssephe static uint32_t vmbus_chan_nextcpu; 1081302864Ssephe int cpu; 1082302864Ssephe 1083302864Ssephe cpu = atomic_fetchadd_int(&vmbus_chan_nextcpu, 1) % mp_ncpus; 1084302890Ssephe vmbus_chan_cpu_set(chan, cpu); 1085302864Ssephe} 1086302864Ssephe 1087302864Ssephestatic void 1088307461Ssephevmbus_chan_cpu_default(struct vmbus_channel *chan) 1089302864Ssephe{ 1090302864Ssephe /* 1091302864Ssephe * By default, pin the channel to cpu0. Devices having 1092302864Ssephe * special channel-cpu mapping requirement should call 1093302890Ssephe * vmbus_chan_cpu_{set,rr}(). 1094302864Ssephe */ 1095302890Ssephe vmbus_chan_cpu_set(chan, 0); 1096302864Ssephe} 1097302864Ssephe 1098302864Ssephestatic void 1099302864Ssephevmbus_chan_msgproc_choffer(struct vmbus_softc *sc, 1100302864Ssephe const struct vmbus_message *msg) 1101302864Ssephe{ 1102302864Ssephe const struct vmbus_chanmsg_choffer *offer; 1103307461Ssephe struct vmbus_channel *chan; 1104302864Ssephe int error; 1105302864Ssephe 1106302864Ssephe offer = (const struct vmbus_chanmsg_choffer *)msg->msg_data; 1107302864Ssephe 1108302864Ssephe chan = vmbus_chan_alloc(sc); 1109302864Ssephe if (chan == NULL) { 1110302864Ssephe device_printf(sc->vmbus_dev, "allocate chan%u failed\n", 1111302864Ssephe offer->chm_chanid); 1112302864Ssephe return; 1113302864Ssephe } 1114302864Ssephe 1115302864Ssephe chan->ch_id = offer->chm_chanid; 1116302864Ssephe chan->ch_subidx = offer->chm_subidx; 1117302864Ssephe chan->ch_guid_type = offer->chm_chtype; 1118302864Ssephe chan->ch_guid_inst = offer->chm_chinst; 1119302864Ssephe 1120302864Ssephe /* Batch reading is on by default */ 1121302864Ssephe chan->ch_flags |= VMBUS_CHAN_FLAG_BATCHREAD; 1122302864Ssephe 1123302864Ssephe chan->ch_monprm->mp_connid = VMBUS_CONNID_EVENT; 1124302864Ssephe if (sc->vmbus_version != VMBUS_VERSION_WS2008) 1125302864Ssephe chan->ch_monprm->mp_connid = offer->chm_connid; 1126302864Ssephe 1127302864Ssephe if (offer->chm_flags1 & VMBUS_CHOFFER_FLAG1_HASMNF) { 1128307461Ssephe int trig_idx; 1129307461Ssephe 1130302864Ssephe /* 1131302864Ssephe * Setup MNF stuffs. 1132302864Ssephe */ 1133307461Ssephe chan->ch_txflags |= VMBUS_CHAN_TXF_HASMNF; 1134307461Ssephe 1135307461Ssephe trig_idx = offer->chm_montrig / VMBUS_MONTRIG_LEN; 1136307461Ssephe if (trig_idx >= VMBUS_MONTRIGS_MAX) 1137302864Ssephe panic("invalid monitor trigger %u", offer->chm_montrig); 1138307461Ssephe chan->ch_montrig = 1139307461Ssephe &sc->vmbus_mnf2->mnf_trigs[trig_idx].mt_pending; 1140307461Ssephe 1141302864Ssephe chan->ch_montrig_mask = 1142302864Ssephe 1 << (offer->chm_montrig % VMBUS_MONTRIG_LEN); 1143302864Ssephe } 1144302864Ssephe 1145307461Ssephe /* 1146307461Ssephe * Setup event flag. 1147307461Ssephe */ 1148307461Ssephe chan->ch_evtflag = 1149307461Ssephe &sc->vmbus_tx_evtflags[chan->ch_id >> VMBUS_EVTFLAG_SHIFT]; 1150307461Ssephe chan->ch_evtflag_mask = 1UL << (chan->ch_id & VMBUS_EVTFLAG_MASK); 1151307461Ssephe 1152302864Ssephe /* Select default cpu for this channel. */ 1153302864Ssephe vmbus_chan_cpu_default(chan); 1154302864Ssephe 1155302864Ssephe error = vmbus_chan_add(chan); 1156302864Ssephe if (error) { 1157302864Ssephe device_printf(sc->vmbus_dev, "add chan%u failed: %d\n", 1158302864Ssephe chan->ch_id, error); 1159302864Ssephe vmbus_chan_free(chan); 1160302864Ssephe return; 1161302864Ssephe } 1162302864Ssephe 1163302864Ssephe if (VMBUS_CHAN_ISPRIMARY(chan)) { 1164302864Ssephe /* 1165302864Ssephe * Add device for this primary channel. 1166302864Ssephe * 1167302864Ssephe * NOTE: 1168302864Ssephe * Error is ignored here; don't have much to do if error 1169302864Ssephe * really happens. 1170302864Ssephe */ 1171302868Ssephe vmbus_add_child(chan); 1172302864Ssephe } 1173302864Ssephe} 1174302864Ssephe 1175302864Ssephe/* 1176302864Ssephe * XXX pretty broken; need rework. 1177302864Ssephe */ 1178302864Ssephestatic void 1179302864Ssephevmbus_chan_msgproc_chrescind(struct vmbus_softc *sc, 1180302864Ssephe const struct vmbus_message *msg) 1181302864Ssephe{ 1182302864Ssephe const struct vmbus_chanmsg_chrescind *note; 1183307461Ssephe struct vmbus_channel *chan; 1184302864Ssephe 1185302864Ssephe note = (const struct vmbus_chanmsg_chrescind *)msg->msg_data; 1186302864Ssephe if (note->chm_chanid > VMBUS_CHAN_MAX) { 1187302864Ssephe device_printf(sc->vmbus_dev, "invalid rescinded chan%u\n", 1188302864Ssephe note->chm_chanid); 1189302864Ssephe return; 1190302864Ssephe } 1191302864Ssephe 1192302864Ssephe if (bootverbose) { 1193302864Ssephe device_printf(sc->vmbus_dev, "chan%u rescinded\n", 1194302864Ssephe note->chm_chanid); 1195302864Ssephe } 1196302864Ssephe 1197302864Ssephe chan = sc->vmbus_chmap[note->chm_chanid]; 1198302864Ssephe if (chan == NULL) 1199302864Ssephe return; 1200302864Ssephe sc->vmbus_chmap[note->chm_chanid] = NULL; 1201302864Ssephe 1202302864Ssephe taskqueue_enqueue(taskqueue_thread, &chan->ch_detach_task); 1203302864Ssephe} 1204302864Ssephe 1205302864Ssephestatic void 1206302864Ssephevmbus_chan_detach_task(void *xchan, int pending __unused) 1207302864Ssephe{ 1208307461Ssephe struct vmbus_channel *chan = xchan; 1209302864Ssephe 1210302864Ssephe if (VMBUS_CHAN_ISPRIMARY(chan)) { 1211302864Ssephe /* Only primary channel owns the device */ 1212302868Ssephe vmbus_delete_child(chan); 1213302864Ssephe /* NOTE: DO NOT free primary channel for now */ 1214302864Ssephe } else { 1215307461Ssephe struct vmbus_softc *sc = chan->ch_vmbus; 1216307461Ssephe struct vmbus_channel *pri_chan = chan->ch_prichan; 1217302864Ssephe struct vmbus_chanmsg_chfree *req; 1218302864Ssephe struct vmbus_msghc *mh; 1219302864Ssephe int error; 1220302864Ssephe 1221302864Ssephe mh = vmbus_msghc_get(sc, sizeof(*req)); 1222302864Ssephe if (mh == NULL) { 1223302864Ssephe device_printf(sc->vmbus_dev, 1224302864Ssephe "can not get msg hypercall for chfree(chan%u)\n", 1225302864Ssephe chan->ch_id); 1226302864Ssephe goto remove; 1227302864Ssephe } 1228302864Ssephe 1229302864Ssephe req = vmbus_msghc_dataptr(mh); 1230302864Ssephe req->chm_hdr.chm_type = VMBUS_CHANMSG_TYPE_CHFREE; 1231302864Ssephe req->chm_chanid = chan->ch_id; 1232302864Ssephe 1233302864Ssephe error = vmbus_msghc_exec_noresult(mh); 1234302864Ssephe vmbus_msghc_put(sc, mh); 1235302864Ssephe 1236302864Ssephe if (error) { 1237302864Ssephe device_printf(sc->vmbus_dev, 1238302864Ssephe "chfree(chan%u) failed: %d", 1239302864Ssephe chan->ch_id, error); 1240302864Ssephe /* NOTE: Move on! */ 1241302864Ssephe } else { 1242302864Ssephe if (bootverbose) { 1243302864Ssephe device_printf(sc->vmbus_dev, "chan%u freed\n", 1244302864Ssephe chan->ch_id); 1245302864Ssephe } 1246302864Ssephe } 1247302864Ssepheremove: 1248302864Ssephe mtx_lock(&pri_chan->ch_subchan_lock); 1249302864Ssephe TAILQ_REMOVE(&pri_chan->ch_subchans, chan, ch_sublink); 1250302864Ssephe KASSERT(pri_chan->ch_subchan_cnt > 0, 1251302864Ssephe ("invalid subchan_cnt %d", pri_chan->ch_subchan_cnt)); 1252302864Ssephe pri_chan->ch_subchan_cnt--; 1253302864Ssephe mtx_unlock(&pri_chan->ch_subchan_lock); 1254302864Ssephe wakeup(pri_chan); 1255302864Ssephe 1256302864Ssephe vmbus_chan_free(chan); 1257302864Ssephe } 1258302864Ssephe} 1259302864Ssephe 1260302864Ssephe/* 1261302864Ssephe * Detach all devices and destroy the corresponding primary channels. 1262302864Ssephe */ 1263302864Ssephevoid 1264302864Ssephevmbus_chan_destroy_all(struct vmbus_softc *sc) 1265302864Ssephe{ 1266307461Ssephe struct vmbus_channel *chan; 1267302864Ssephe 1268302864Ssephe mtx_lock(&sc->vmbus_prichan_lock); 1269302864Ssephe while ((chan = TAILQ_FIRST(&sc->vmbus_prichans)) != NULL) { 1270302864Ssephe KASSERT(VMBUS_CHAN_ISPRIMARY(chan), ("not primary channel")); 1271302864Ssephe TAILQ_REMOVE(&sc->vmbus_prichans, chan, ch_prilink); 1272302864Ssephe mtx_unlock(&sc->vmbus_prichan_lock); 1273302864Ssephe 1274302868Ssephe vmbus_delete_child(chan); 1275302864Ssephe vmbus_chan_free(chan); 1276302864Ssephe 1277302864Ssephe mtx_lock(&sc->vmbus_prichan_lock); 1278302864Ssephe } 1279302864Ssephe bzero(sc->vmbus_chmap, 1280307461Ssephe sizeof(struct vmbus_channel *) * VMBUS_CHAN_MAX); 1281302864Ssephe mtx_unlock(&sc->vmbus_prichan_lock); 1282302864Ssephe} 1283302864Ssephe 1284303020Ssephe/* 1285302864Ssephe * The channel whose vcpu binding is closest to the currect vcpu will 1286302864Ssephe * be selected. 1287303020Ssephe * If no multi-channel, always select primary channel. 1288302864Ssephe */ 1289307461Ssephestruct vmbus_channel * 1290307461Ssephevmbus_chan_cpu2chan(struct vmbus_channel *prichan, int cpu) 1291302864Ssephe{ 1292307461Ssephe struct vmbus_channel *sel, *chan; 1293303020Ssephe uint32_t vcpu, sel_dist; 1294302864Ssephe 1295303020Ssephe KASSERT(cpu >= 0 && cpu < mp_ncpus, ("invalid cpuid %d", cpu)); 1296303020Ssephe if (TAILQ_EMPTY(&prichan->ch_subchans)) 1297303020Ssephe return prichan; 1298302864Ssephe 1299307461Ssephe vcpu = VMBUS_PCPU_GET(prichan->ch_vmbus, vcpuid, cpu); 1300302864Ssephe 1301303020Ssephe#define CHAN_VCPU_DIST(ch, vcpu) \ 1302303020Ssephe (((ch)->ch_vcpuid > (vcpu)) ? \ 1303303020Ssephe ((ch)->ch_vcpuid - (vcpu)) : ((vcpu) - (ch)->ch_vcpuid)) 1304302864Ssephe 1305303020Ssephe#define CHAN_SELECT(ch) \ 1306303020Ssephedo { \ 1307303020Ssephe sel = ch; \ 1308303020Ssephe sel_dist = CHAN_VCPU_DIST(ch, vcpu); \ 1309303020Ssephe} while (0) 1310302864Ssephe 1311303020Ssephe CHAN_SELECT(prichan); 1312302864Ssephe 1313303020Ssephe mtx_lock(&prichan->ch_subchan_lock); 1314303020Ssephe TAILQ_FOREACH(chan, &prichan->ch_subchans, ch_sublink) { 1315303020Ssephe uint32_t dist; 1316302864Ssephe 1317303020Ssephe KASSERT(chan->ch_stflags & VMBUS_CHAN_ST_OPENED, 1318303020Ssephe ("chan%u is not opened", chan->ch_id)); 1319303020Ssephe 1320303020Ssephe if (chan->ch_vcpuid == vcpu) { 1321303020Ssephe /* Exact match; done */ 1322303020Ssephe CHAN_SELECT(chan); 1323303020Ssephe break; 1324303020Ssephe } 1325303020Ssephe 1326303020Ssephe dist = CHAN_VCPU_DIST(chan, vcpu); 1327303020Ssephe if (sel_dist <= dist) { 1328303020Ssephe /* Far or same distance; skip */ 1329302864Ssephe continue; 1330302864Ssephe } 1331302864Ssephe 1332303020Ssephe /* Select the closer channel. */ 1333303020Ssephe CHAN_SELECT(chan); 1334302864Ssephe } 1335303020Ssephe mtx_unlock(&prichan->ch_subchan_lock); 1336302864Ssephe 1337303020Ssephe#undef CHAN_SELECT 1338303020Ssephe#undef CHAN_VCPU_DIST 1339303020Ssephe 1340303020Ssephe return sel; 1341302864Ssephe} 1342302864Ssephe 1343307461Ssephestruct vmbus_channel ** 1344307461Ssephevmbus_subchan_get(struct vmbus_channel *pri_chan, int subchan_cnt) 1345302864Ssephe{ 1346307461Ssephe struct vmbus_channel **ret, *chan; 1347302864Ssephe int i; 1348302864Ssephe 1349307461Ssephe ret = malloc(subchan_cnt * sizeof(struct vmbus_channel *), M_TEMP, 1350302864Ssephe M_WAITOK); 1351302864Ssephe 1352302864Ssephe mtx_lock(&pri_chan->ch_subchan_lock); 1353302864Ssephe 1354302864Ssephe while (pri_chan->ch_subchan_cnt < subchan_cnt) 1355302864Ssephe mtx_sleep(pri_chan, &pri_chan->ch_subchan_lock, 0, "subch", 0); 1356302864Ssephe 1357302864Ssephe i = 0; 1358302864Ssephe TAILQ_FOREACH(chan, &pri_chan->ch_subchans, ch_sublink) { 1359302864Ssephe /* TODO: refcnt chan */ 1360302864Ssephe ret[i] = chan; 1361302864Ssephe 1362302864Ssephe ++i; 1363302864Ssephe if (i == subchan_cnt) 1364302864Ssephe break; 1365302864Ssephe } 1366302864Ssephe KASSERT(i == subchan_cnt, ("invalid subchan count %d, should be %d", 1367302864Ssephe pri_chan->ch_subchan_cnt, subchan_cnt)); 1368302864Ssephe 1369302864Ssephe mtx_unlock(&pri_chan->ch_subchan_lock); 1370302864Ssephe 1371302864Ssephe return ret; 1372302864Ssephe} 1373302864Ssephe 1374302864Ssephevoid 1375307461Ssephevmbus_subchan_rel(struct vmbus_channel **subchan, int subchan_cnt __unused) 1376302864Ssephe{ 1377302864Ssephe 1378302864Ssephe free(subchan, M_TEMP); 1379302864Ssephe} 1380302864Ssephe 1381302864Ssephevoid 1382307461Ssephevmbus_subchan_drain(struct vmbus_channel *pri_chan) 1383302864Ssephe{ 1384302864Ssephe mtx_lock(&pri_chan->ch_subchan_lock); 1385302864Ssephe while (pri_chan->ch_subchan_cnt > 0) 1386302864Ssephe mtx_sleep(pri_chan, &pri_chan->ch_subchan_lock, 0, "dsubch", 0); 1387302864Ssephe mtx_unlock(&pri_chan->ch_subchan_lock); 1388302864Ssephe} 1389302864Ssephe 1390302864Ssephevoid 1391302864Ssephevmbus_chan_msgproc(struct vmbus_softc *sc, const struct vmbus_message *msg) 1392302864Ssephe{ 1393302864Ssephe vmbus_chanmsg_proc_t msg_proc; 1394302864Ssephe uint32_t msg_type; 1395302864Ssephe 1396302864Ssephe msg_type = ((const struct vmbus_chanmsg_hdr *)msg->msg_data)->chm_type; 1397302864Ssephe KASSERT(msg_type < VMBUS_CHANMSG_TYPE_MAX, 1398302864Ssephe ("invalid message type %u", msg_type)); 1399302864Ssephe 1400302864Ssephe msg_proc = vmbus_chan_msgprocs[msg_type]; 1401302864Ssephe if (msg_proc != NULL) 1402302864Ssephe msg_proc(sc, msg); 1403302864Ssephe} 1404303021Ssephe 1405303021Ssephevoid 1406307461Ssephevmbus_chan_set_readbatch(struct vmbus_channel *chan, bool on) 1407303021Ssephe{ 1408303021Ssephe if (!on) 1409303021Ssephe chan->ch_flags &= ~VMBUS_CHAN_FLAG_BATCHREAD; 1410303021Ssephe else 1411303021Ssephe chan->ch_flags |= VMBUS_CHAN_FLAG_BATCHREAD; 1412303021Ssephe} 1413307461Ssephe 1414307461Ssepheuint32_t 1415307461Ssephevmbus_chan_id(const struct vmbus_channel *chan) 1416307461Ssephe{ 1417307461Ssephe return chan->ch_id; 1418307461Ssephe} 1419307461Ssephe 1420307461Ssepheuint32_t 1421307461Ssephevmbus_chan_subidx(const struct vmbus_channel *chan) 1422307461Ssephe{ 1423307461Ssephe return chan->ch_subidx; 1424307461Ssephe} 1425307461Ssephe 1426307461Ssephebool 1427307461Ssephevmbus_chan_is_primary(const struct vmbus_channel *chan) 1428307461Ssephe{ 1429307461Ssephe if (VMBUS_CHAN_ISPRIMARY(chan)) 1430307461Ssephe return true; 1431307461Ssephe else 1432307461Ssephe return false; 1433307461Ssephe} 1434307461Ssephe 1435307461Ssepheconst struct hyperv_guid * 1436307461Ssephevmbus_chan_guid_inst(const struct vmbus_channel *chan) 1437307461Ssephe{ 1438307461Ssephe return &chan->ch_guid_inst; 1439307461Ssephe} 1440307486Ssephe 1441307486Ssepheint 1442307486Ssephevmbus_chan_prplist_nelem(int br_size, int prpcnt_max, int dlen_max) 1443307486Ssephe{ 1444307486Ssephe int elem_size; 1445307486Ssephe 1446307486Ssephe elem_size = __offsetof(struct vmbus_chanpkt_prplist, 1447307486Ssephe cp_range[0].gpa_page[prpcnt_max]); 1448307486Ssephe elem_size += dlen_max; 1449307486Ssephe elem_size = VMBUS_CHANPKT_TOTLEN(elem_size); 1450307486Ssephe 1451307486Ssephe return (vmbus_br_nelem(br_size, elem_size)); 1452307486Ssephe} 1453