Page Menu
Home
FreeBSD
Search
Configure Global Search
Log In
Files
F106140372
D7232.diff
No One
Temporary
Actions
View File
Edit File
Delete File
View Transforms
Subscribe
Mute Notifications
Flag For Later
Award Token
Size
75 KB
Referenced Files
None
Subscribers
None
D7232.diff
View Options
Index: head/sys/conf/files.amd64
===================================================================
--- head/sys/conf/files.amd64
+++ head/sys/conf/files.amd64
@@ -270,11 +270,11 @@
dev/hyperv/utilities/hv_shutdown.c optional hyperv
dev/hyperv/utilities/hv_timesync.c optional hyperv
dev/hyperv/utilities/hv_util.c optional hyperv
-dev/hyperv/vmbus/hv_channel.c optional hyperv
dev/hyperv/vmbus/hv_ring_buffer.c optional hyperv
dev/hyperv/vmbus/hyperv.c optional hyperv
dev/hyperv/vmbus/hyperv_busdma.c optional hyperv
dev/hyperv/vmbus/vmbus.c optional hyperv
+dev/hyperv/vmbus/vmbus_chan.c optional hyperv
dev/hyperv/vmbus/vmbus_et.c optional hyperv
dev/hyperv/vmbus/vmbus_if.m optional hyperv
dev/hyperv/vmbus/amd64/hyperv_machdep.c optional hyperv
Index: head/sys/conf/files.i386
===================================================================
--- head/sys/conf/files.i386
+++ head/sys/conf/files.i386
@@ -246,11 +246,11 @@
dev/hyperv/utilities/hv_shutdown.c optional hyperv
dev/hyperv/utilities/hv_timesync.c optional hyperv
dev/hyperv/utilities/hv_util.c optional hyperv
-dev/hyperv/vmbus/hv_channel.c optional hyperv
dev/hyperv/vmbus/hv_ring_buffer.c optional hyperv
dev/hyperv/vmbus/hyperv.c optional hyperv
dev/hyperv/vmbus/hyperv_busdma.c optional hyperv
dev/hyperv/vmbus/vmbus.c optional hyperv
+dev/hyperv/vmbus/vmbus_chan.c optional hyperv
dev/hyperv/vmbus/vmbus_et.c optional hyperv
dev/hyperv/vmbus/vmbus_if.m optional hyperv
dev/hyperv/vmbus/i386/hyperv_machdep.c optional hyperv
Index: head/sys/dev/hyperv/vmbus/hv_channel.c
===================================================================
--- head/sys/dev/hyperv/vmbus/hv_channel.c
+++ head/sys/dev/hyperv/vmbus/hv_channel.c
@@ -1,1380 +0,0 @@
-/*-
- * Copyright (c) 2009-2012,2016 Microsoft Corp.
- * Copyright (c) 2012 NetApp Inc.
- * Copyright (c) 2012 Citrix Inc.
- * All rights reserved.
- *
- * Redistribution and use in source and binary forms, with or without
- * modification, are permitted provided that the following conditions
- * are met:
- * 1. Redistributions of source code must retain the above copyright
- * notice unmodified, this list of conditions, and the following
- * disclaimer.
- * 2. Redistributions in binary form must reproduce the above copyright
- * notice, this list of conditions and the following disclaimer in the
- * documentation and/or other materials provided with the distribution.
- *
- * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
- * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
- * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
- * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
- * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
- * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
- * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
- * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
- * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
- * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
- */
-
-#include <sys/cdefs.h>
-__FBSDID("$FreeBSD$");
-
-#include <sys/param.h>
-#include <sys/kernel.h>
-#include <sys/malloc.h>
-#include <sys/systm.h>
-#include <sys/mbuf.h>
-#include <sys/lock.h>
-#include <sys/mutex.h>
-#include <sys/sysctl.h>
-
-#include <machine/atomic.h>
-#include <machine/bus.h>
-
-#include <vm/vm.h>
-#include <vm/vm_param.h>
-#include <vm/pmap.h>
-
-#include <dev/hyperv/include/hyperv_busdma.h>
-#include <dev/hyperv/vmbus/hv_vmbus_priv.h>
-#include <dev/hyperv/vmbus/hyperv_var.h>
-#include <dev/hyperv/vmbus/vmbus_reg.h>
-#include <dev/hyperv/vmbus/vmbus_var.h>
-
-static void vmbus_chan_signal_tx(struct hv_vmbus_channel *chan);
-static void vmbus_chan_update_evtflagcnt(struct vmbus_softc *,
- const struct hv_vmbus_channel *);
-
-static void vmbus_chan_task(void *, int);
-static void vmbus_chan_task_nobatch(void *, int);
-static void vmbus_chan_detach_task(void *, int);
-
-static void vmbus_chan_msgproc_choffer(struct vmbus_softc *,
- const struct vmbus_message *);
-static void vmbus_chan_msgproc_chrescind(struct vmbus_softc *,
- const struct vmbus_message *);
-
-/*
- * Vmbus channel message processing.
- */
-static const vmbus_chanmsg_proc_t
-vmbus_chan_msgprocs[VMBUS_CHANMSG_TYPE_MAX] = {
- VMBUS_CHANMSG_PROC(CHOFFER, vmbus_chan_msgproc_choffer),
- VMBUS_CHANMSG_PROC(CHRESCIND, vmbus_chan_msgproc_chrescind),
-
- VMBUS_CHANMSG_PROC_WAKEUP(CHOPEN_RESP),
- VMBUS_CHANMSG_PROC_WAKEUP(GPADL_CONNRESP),
- VMBUS_CHANMSG_PROC_WAKEUP(GPADL_DISCONNRESP)
-};
-
-/**
- * @brief Trigger an event notification on the specified channel
- */
-static void
-vmbus_chan_signal_tx(struct hv_vmbus_channel *chan)
-{
- struct vmbus_softc *sc = chan->vmbus_sc;
- uint32_t chanid = chan->ch_id;
-
- atomic_set_long(&sc->vmbus_tx_evtflags[chanid >> VMBUS_EVTFLAG_SHIFT],
- 1UL << (chanid & VMBUS_EVTFLAG_MASK));
-
- if (chan->ch_flags & VMBUS_CHAN_FLAG_HASMNF) {
- atomic_set_int(
- &sc->vmbus_mnf2->mnf_trigs[chan->ch_montrig_idx].mt_pending,
- chan->ch_montrig_mask);
- } else {
- hypercall_signal_event(chan->ch_monprm_dma.hv_paddr);
- }
-}
-
-static int
-vmbus_chan_sysctl_mnf(SYSCTL_HANDLER_ARGS)
-{
- struct hv_vmbus_channel *chan = arg1;
- int mnf = 0;
-
- if (chan->ch_flags & VMBUS_CHAN_FLAG_HASMNF)
- mnf = 1;
- return sysctl_handle_int(oidp, &mnf, 0, req);
-}
-
-static void
-vmbus_chan_sysctl_create(struct hv_vmbus_channel *chan)
-{
- struct sysctl_oid *ch_tree, *chid_tree, *br_tree;
- struct sysctl_ctx_list *ctx;
- uint32_t ch_id;
- char name[16];
-
- /*
- * Add sysctl nodes related to this channel to this
- * channel's sysctl ctx, so that they can be destroyed
- * independently upon close of this channel, which can
- * happen even if the device is not detached.
- */
- ctx = &chan->ch_sysctl_ctx;
- sysctl_ctx_init(ctx);
-
- /*
- * Create dev.NAME.UNIT.channel tree.
- */
- ch_tree = SYSCTL_ADD_NODE(ctx,
- SYSCTL_CHILDREN(device_get_sysctl_tree(chan->ch_dev)),
- OID_AUTO, "channel", CTLFLAG_RD | CTLFLAG_MPSAFE, 0, "");
- if (ch_tree == NULL)
- return;
-
- /*
- * Create dev.NAME.UNIT.channel.CHANID tree.
- */
- if (VMBUS_CHAN_ISPRIMARY(chan))
- ch_id = chan->ch_id;
- else
- ch_id = chan->ch_prichan->ch_id;
- snprintf(name, sizeof(name), "%d", ch_id);
- chid_tree = SYSCTL_ADD_NODE(ctx, SYSCTL_CHILDREN(ch_tree),
- OID_AUTO, name, CTLFLAG_RD | CTLFLAG_MPSAFE, 0, "");
- if (chid_tree == NULL)
- return;
-
- if (!VMBUS_CHAN_ISPRIMARY(chan)) {
- /*
- * Create dev.NAME.UNIT.channel.CHANID.sub tree.
- */
- ch_tree = SYSCTL_ADD_NODE(ctx, SYSCTL_CHILDREN(chid_tree),
- OID_AUTO, "sub", CTLFLAG_RD | CTLFLAG_MPSAFE, 0, "");
- if (ch_tree == NULL)
- return;
-
- /*
- * Create dev.NAME.UNIT.channel.CHANID.sub.SUBIDX tree.
- *
- * NOTE:
- * chid_tree is changed to this new sysctl tree.
- */
- snprintf(name, sizeof(name), "%d", chan->ch_subidx);
- chid_tree = SYSCTL_ADD_NODE(ctx, SYSCTL_CHILDREN(ch_tree),
- OID_AUTO, name, CTLFLAG_RD | CTLFLAG_MPSAFE, 0, "");
- if (chid_tree == NULL)
- return;
-
- SYSCTL_ADD_UINT(ctx, SYSCTL_CHILDREN(chid_tree), OID_AUTO,
- "chanid", CTLFLAG_RD, &chan->ch_id, 0, "channel id");
- }
-
- SYSCTL_ADD_UINT(ctx, SYSCTL_CHILDREN(chid_tree), OID_AUTO,
- "cpu", CTLFLAG_RD, &chan->ch_cpuid, 0, "owner CPU id");
- SYSCTL_ADD_PROC(ctx, SYSCTL_CHILDREN(chid_tree), OID_AUTO,
- "mnf", CTLTYPE_INT | CTLFLAG_RD | CTLFLAG_MPSAFE,
- chan, 0, vmbus_chan_sysctl_mnf, "I",
- "has monitor notification facilities");
-
- /*
- * Create sysctl tree for RX bufring.
- */
- br_tree = SYSCTL_ADD_NODE(ctx, SYSCTL_CHILDREN(chid_tree), OID_AUTO,
- "in", CTLFLAG_RD | CTLFLAG_MPSAFE, 0, "");
- if (br_tree != NULL) {
- hv_ring_buffer_stat(ctx, SYSCTL_CHILDREN(br_tree),
- &chan->inbound, "inbound ring buffer stats");
- }
-
- /*
- * Create sysctl tree for TX bufring.
- */
- br_tree = SYSCTL_ADD_NODE(ctx, SYSCTL_CHILDREN(chid_tree), OID_AUTO,
- "out", CTLFLAG_RD | CTLFLAG_MPSAFE, 0, "");
- if (br_tree != NULL) {
- hv_ring_buffer_stat(ctx, SYSCTL_CHILDREN(br_tree),
- &chan->outbound, "outbound ring buffer stats");
- }
-}
-
-int
-vmbus_chan_open(struct hv_vmbus_channel *chan, int txbr_size, int rxbr_size,
- const void *udata, int udlen, vmbus_chan_callback_t cb, void *cbarg)
-{
- struct vmbus_softc *sc = chan->vmbus_sc;
- const struct vmbus_chanmsg_chopen_resp *resp;
- const struct vmbus_message *msg;
- struct vmbus_chanmsg_chopen *req;
- struct vmbus_msghc *mh;
- uint32_t status;
- int error;
- uint8_t *br;
-
- if (udlen > VMBUS_CHANMSG_CHOPEN_UDATA_SIZE) {
- device_printf(sc->vmbus_dev,
- "invalid udata len %d for chan%u\n", udlen, chan->ch_id);
- return EINVAL;
- }
- KASSERT((txbr_size & PAGE_MASK) == 0,
- ("send bufring size is not multiple page"));
- KASSERT((rxbr_size & PAGE_MASK) == 0,
- ("recv bufring size is not multiple page"));
-
- if (atomic_testandset_int(&chan->ch_stflags,
- VMBUS_CHAN_ST_OPENED_SHIFT))
- panic("double-open chan%u", chan->ch_id);
-
- chan->ch_cb = cb;
- chan->ch_cbarg = cbarg;
-
- vmbus_chan_update_evtflagcnt(sc, chan);
-
- chan->ch_tq = VMBUS_PCPU_GET(chan->vmbus_sc, event_tq, chan->ch_cpuid);
- if (chan->ch_flags & VMBUS_CHAN_FLAG_BATCHREAD)
- TASK_INIT(&chan->ch_task, 0, vmbus_chan_task, chan);
- else
- TASK_INIT(&chan->ch_task, 0, vmbus_chan_task_nobatch, chan);
-
- /*
- * Allocate the TX+RX bufrings.
- * XXX should use ch_dev dtag
- */
- br = hyperv_dmamem_alloc(bus_get_dma_tag(sc->vmbus_dev),
- PAGE_SIZE, 0, txbr_size + rxbr_size, &chan->ch_bufring_dma,
- BUS_DMA_WAITOK | BUS_DMA_ZERO);
- if (br == NULL) {
- device_printf(sc->vmbus_dev, "bufring allocation failed\n");
- error = ENOMEM;
- goto failed;
- }
- chan->ch_bufring = br;
-
- /* TX bufring comes first */
- hv_vmbus_ring_buffer_init(&chan->outbound, br, txbr_size);
- /* RX bufring immediately follows TX bufring */
- hv_vmbus_ring_buffer_init(&chan->inbound, br + txbr_size, rxbr_size);
-
- /* Create sysctl tree for this channel */
- vmbus_chan_sysctl_create(chan);
-
- /*
- * Connect the bufrings, both RX and TX, to this channel.
- */
- error = vmbus_chan_gpadl_connect(chan, chan->ch_bufring_dma.hv_paddr,
- txbr_size + rxbr_size, &chan->ch_bufring_gpadl);
- if (error) {
- device_printf(sc->vmbus_dev,
- "failed to connect bufring GPADL to chan%u\n", chan->ch_id);
- goto failed;
- }
-
- /*
- * Open channel w/ the bufring GPADL on the target CPU.
- */
- mh = vmbus_msghc_get(sc, sizeof(*req));
- if (mh == NULL) {
- device_printf(sc->vmbus_dev,
- "can not get msg hypercall for chopen(chan%u)\n",
- chan->ch_id);
- error = ENXIO;
- goto failed;
- }
-
- req = vmbus_msghc_dataptr(mh);
- req->chm_hdr.chm_type = VMBUS_CHANMSG_TYPE_CHOPEN;
- req->chm_chanid = chan->ch_id;
- req->chm_openid = chan->ch_id;
- req->chm_gpadl = chan->ch_bufring_gpadl;
- req->chm_vcpuid = chan->ch_vcpuid;
- req->chm_txbr_pgcnt = txbr_size >> PAGE_SHIFT;
- if (udlen > 0)
- memcpy(req->chm_udata, udata, udlen);
-
- error = vmbus_msghc_exec(sc, mh);
- if (error) {
- device_printf(sc->vmbus_dev,
- "chopen(chan%u) msg hypercall exec failed: %d\n",
- chan->ch_id, error);
- vmbus_msghc_put(sc, mh);
- goto failed;
- }
-
- msg = vmbus_msghc_wait_result(sc, mh);
- resp = (const struct vmbus_chanmsg_chopen_resp *)msg->msg_data;
- status = resp->chm_status;
-
- vmbus_msghc_put(sc, mh);
-
- if (status == 0) {
- if (bootverbose) {
- device_printf(sc->vmbus_dev, "chan%u opened\n",
- chan->ch_id);
- }
- return 0;
- }
-
- device_printf(sc->vmbus_dev, "failed to open chan%u\n", chan->ch_id);
- error = ENXIO;
-
-failed:
- if (chan->ch_bufring_gpadl) {
- vmbus_chan_gpadl_disconnect(chan, chan->ch_bufring_gpadl);
- chan->ch_bufring_gpadl = 0;
- }
- if (chan->ch_bufring != NULL) {
- hyperv_dmamem_free(&chan->ch_bufring_dma, chan->ch_bufring);
- chan->ch_bufring = NULL;
- }
- atomic_clear_int(&chan->ch_stflags, VMBUS_CHAN_ST_OPENED);
- return error;
-}
-
-int
-vmbus_chan_gpadl_connect(struct hv_vmbus_channel *chan, bus_addr_t paddr,
- int size, uint32_t *gpadl0)
-{
- struct vmbus_softc *sc = chan->vmbus_sc;
- struct vmbus_msghc *mh;
- struct vmbus_chanmsg_gpadl_conn *req;
- const struct vmbus_message *msg;
- size_t reqsz;
- uint32_t gpadl, status;
- int page_count, range_len, i, cnt, error;
- uint64_t page_id;
-
- /*
- * Preliminary checks.
- */
-
- KASSERT((size & PAGE_MASK) == 0,
- ("invalid GPA size %d, not multiple page size", size));
- page_count = size >> PAGE_SHIFT;
-
- KASSERT((paddr & PAGE_MASK) == 0,
- ("GPA is not page aligned %jx", (uintmax_t)paddr));
- page_id = paddr >> PAGE_SHIFT;
-
- range_len = __offsetof(struct vmbus_gpa_range, gpa_page[page_count]);
- /*
- * We don't support multiple GPA ranges.
- */
- if (range_len > UINT16_MAX) {
- device_printf(sc->vmbus_dev, "GPA too large, %d pages\n",
- page_count);
- return EOPNOTSUPP;
- }
-
- /*
- * Allocate GPADL id.
- */
- gpadl = vmbus_gpadl_alloc(sc);
- *gpadl0 = gpadl;
-
- /*
- * Connect this GPADL to the target channel.
- *
- * NOTE:
- * Since each message can only hold small set of page
- * addresses, several messages may be required to
- * complete the connection.
- */
- if (page_count > VMBUS_CHANMSG_GPADL_CONN_PGMAX)
- cnt = VMBUS_CHANMSG_GPADL_CONN_PGMAX;
- else
- cnt = page_count;
- page_count -= cnt;
-
- reqsz = __offsetof(struct vmbus_chanmsg_gpadl_conn,
- chm_range.gpa_page[cnt]);
- mh = vmbus_msghc_get(sc, reqsz);
- if (mh == NULL) {
- device_printf(sc->vmbus_dev,
- "can not get msg hypercall for gpadl->chan%u\n",
- chan->ch_id);
- return EIO;
- }
-
- req = vmbus_msghc_dataptr(mh);
- req->chm_hdr.chm_type = VMBUS_CHANMSG_TYPE_GPADL_CONN;
- req->chm_chanid = chan->ch_id;
- req->chm_gpadl = gpadl;
- req->chm_range_len = range_len;
- req->chm_range_cnt = 1;
- req->chm_range.gpa_len = size;
- req->chm_range.gpa_ofs = 0;
- for (i = 0; i < cnt; ++i)
- req->chm_range.gpa_page[i] = page_id++;
-
- error = vmbus_msghc_exec(sc, mh);
- if (error) {
- device_printf(sc->vmbus_dev,
- "gpadl->chan%u msg hypercall exec failed: %d\n",
- chan->ch_id, error);
- vmbus_msghc_put(sc, mh);
- return error;
- }
-
- while (page_count > 0) {
- struct vmbus_chanmsg_gpadl_subconn *subreq;
-
- if (page_count > VMBUS_CHANMSG_GPADL_SUBCONN_PGMAX)
- cnt = VMBUS_CHANMSG_GPADL_SUBCONN_PGMAX;
- else
- cnt = page_count;
- page_count -= cnt;
-
- reqsz = __offsetof(struct vmbus_chanmsg_gpadl_subconn,
- chm_gpa_page[cnt]);
- vmbus_msghc_reset(mh, reqsz);
-
- subreq = vmbus_msghc_dataptr(mh);
- subreq->chm_hdr.chm_type = VMBUS_CHANMSG_TYPE_GPADL_SUBCONN;
- subreq->chm_gpadl = gpadl;
- for (i = 0; i < cnt; ++i)
- subreq->chm_gpa_page[i] = page_id++;
-
- vmbus_msghc_exec_noresult(mh);
- }
- KASSERT(page_count == 0, ("invalid page count %d", page_count));
-
- msg = vmbus_msghc_wait_result(sc, mh);
- status = ((const struct vmbus_chanmsg_gpadl_connresp *)
- msg->msg_data)->chm_status;
-
- vmbus_msghc_put(sc, mh);
-
- if (status != 0) {
- device_printf(sc->vmbus_dev, "gpadl->chan%u failed: "
- "status %u\n", chan->ch_id, status);
- return EIO;
- } else {
- if (bootverbose) {
- device_printf(sc->vmbus_dev, "gpadl->chan%u "
- "succeeded\n", chan->ch_id);
- }
- }
- return 0;
-}
-
-/*
- * Disconnect the GPA from the target channel
- */
-int
-vmbus_chan_gpadl_disconnect(struct hv_vmbus_channel *chan, uint32_t gpadl)
-{
- struct vmbus_softc *sc = chan->vmbus_sc;
- struct vmbus_msghc *mh;
- struct vmbus_chanmsg_gpadl_disconn *req;
- int error;
-
- mh = vmbus_msghc_get(sc, sizeof(*req));
- if (mh == NULL) {
- device_printf(sc->vmbus_dev,
- "can not get msg hypercall for gpa x->chan%u\n",
- chan->ch_id);
- return EBUSY;
- }
-
- req = vmbus_msghc_dataptr(mh);
- req->chm_hdr.chm_type = VMBUS_CHANMSG_TYPE_GPADL_DISCONN;
- req->chm_chanid = chan->ch_id;
- req->chm_gpadl = gpadl;
-
- error = vmbus_msghc_exec(sc, mh);
- if (error) {
- device_printf(sc->vmbus_dev,
- "gpa x->chan%u msg hypercall exec failed: %d\n",
- chan->ch_id, error);
- vmbus_msghc_put(sc, mh);
- return error;
- }
-
- vmbus_msghc_wait_result(sc, mh);
- /* Discard result; no useful information */
- vmbus_msghc_put(sc, mh);
-
- return 0;
-}
-
-static void
-vmbus_chan_close_internal(struct hv_vmbus_channel *chan)
-{
- struct vmbus_softc *sc = chan->vmbus_sc;
- struct vmbus_msghc *mh;
- struct vmbus_chanmsg_chclose *req;
- struct taskqueue *tq = chan->ch_tq;
- int error;
-
- /* TODO: stringent check */
- atomic_clear_int(&chan->ch_stflags, VMBUS_CHAN_ST_OPENED);
-
- /*
- * Free this channel's sysctl tree attached to its device's
- * sysctl tree.
- */
- sysctl_ctx_free(&chan->ch_sysctl_ctx);
-
- /*
- * Set ch_tq to NULL to avoid more requests be scheduled.
- * XXX pretty broken; need rework.
- */
- chan->ch_tq = NULL;
- taskqueue_drain(tq, &chan->ch_task);
- chan->ch_cb = NULL;
-
- /*
- * Close this channel.
- */
- mh = vmbus_msghc_get(sc, sizeof(*req));
- if (mh == NULL) {
- device_printf(sc->vmbus_dev,
- "can not get msg hypercall for chclose(chan%u)\n",
- chan->ch_id);
- return;
- }
-
- req = vmbus_msghc_dataptr(mh);
- req->chm_hdr.chm_type = VMBUS_CHANMSG_TYPE_CHCLOSE;
- req->chm_chanid = chan->ch_id;
-
- error = vmbus_msghc_exec_noresult(mh);
- vmbus_msghc_put(sc, mh);
-
- if (error) {
- device_printf(sc->vmbus_dev,
- "chclose(chan%u) msg hypercall exec failed: %d\n",
- chan->ch_id, error);
- return;
- } else if (bootverbose) {
- device_printf(sc->vmbus_dev, "close chan%u\n", chan->ch_id);
- }
-
- /*
- * Disconnect the TX+RX bufrings from this channel.
- */
- if (chan->ch_bufring_gpadl) {
- vmbus_chan_gpadl_disconnect(chan, chan->ch_bufring_gpadl);
- chan->ch_bufring_gpadl = 0;
- }
-
- /*
- * Destroy the TX+RX bufrings.
- */
- hv_ring_buffer_cleanup(&chan->outbound);
- hv_ring_buffer_cleanup(&chan->inbound);
- if (chan->ch_bufring != NULL) {
- hyperv_dmamem_free(&chan->ch_bufring_dma, chan->ch_bufring);
- chan->ch_bufring = NULL;
- }
-}
-
-/*
- * Caller should make sure that all sub-channels have
- * been added to 'chan' and all to-be-closed channels
- * are not being opened.
- */
-void
-vmbus_chan_close(struct hv_vmbus_channel *chan)
-{
- int subchan_cnt;
-
- if (!VMBUS_CHAN_ISPRIMARY(chan)) {
- /*
- * Sub-channel is closed when its primary channel
- * is closed; done.
- */
- return;
- }
-
- /*
- * Close all sub-channels, if any.
- */
- subchan_cnt = chan->ch_subchan_cnt;
- if (subchan_cnt > 0) {
- struct hv_vmbus_channel **subchan;
- int i;
-
- subchan = vmbus_subchan_get(chan, subchan_cnt);
- for (i = 0; i < subchan_cnt; ++i)
- vmbus_chan_close_internal(subchan[i]);
- vmbus_subchan_rel(subchan, subchan_cnt);
- }
-
- /* Then close the primary channel. */
- vmbus_chan_close_internal(chan);
-}
-
-int
-vmbus_chan_send(struct hv_vmbus_channel *chan, uint16_t type, uint16_t flags,
- void *data, int dlen, uint64_t xactid)
-{
- struct vmbus_chanpkt pkt;
- int pktlen, pad_pktlen, hlen, error;
- uint64_t pad = 0;
- struct iovec iov[3];
- boolean_t send_evt;
-
- hlen = sizeof(pkt);
- pktlen = hlen + dlen;
- pad_pktlen = VMBUS_CHANPKT_TOTLEN(pktlen);
-
- pkt.cp_hdr.cph_type = type;
- pkt.cp_hdr.cph_flags = flags;
- VMBUS_CHANPKT_SETLEN(pkt.cp_hdr.cph_hlen, hlen);
- VMBUS_CHANPKT_SETLEN(pkt.cp_hdr.cph_tlen, pad_pktlen);
- pkt.cp_hdr.cph_xactid = xactid;
-
- iov[0].iov_base = &pkt;
- iov[0].iov_len = hlen;
- iov[1].iov_base = data;
- iov[1].iov_len = dlen;
- iov[2].iov_base = &pad;
- iov[2].iov_len = pad_pktlen - pktlen;
-
- error = hv_ring_buffer_write(&chan->outbound, iov, 3, &send_evt);
- if (!error && send_evt)
- vmbus_chan_signal_tx(chan);
- return error;
-}
-
-int
-vmbus_chan_send_sglist(struct hv_vmbus_channel *chan,
- struct vmbus_gpa sg[], int sglen, void *data, int dlen, uint64_t xactid)
-{
- struct vmbus_chanpkt_sglist pkt;
- int pktlen, pad_pktlen, hlen, error;
- struct iovec iov[4];
- boolean_t send_evt;
- uint64_t pad = 0;
-
- KASSERT(sglen < VMBUS_CHAN_SGLIST_MAX,
- ("invalid sglist len %d", sglen));
-
- hlen = __offsetof(struct vmbus_chanpkt_sglist, cp_gpa[sglen]);
- pktlen = hlen + dlen;
- pad_pktlen = VMBUS_CHANPKT_TOTLEN(pktlen);
-
- pkt.cp_hdr.cph_type = VMBUS_CHANPKT_TYPE_GPA;
- pkt.cp_hdr.cph_flags = VMBUS_CHANPKT_FLAG_RC;
- VMBUS_CHANPKT_SETLEN(pkt.cp_hdr.cph_hlen, hlen);
- VMBUS_CHANPKT_SETLEN(pkt.cp_hdr.cph_tlen, pad_pktlen);
- pkt.cp_hdr.cph_xactid = xactid;
- pkt.cp_rsvd = 0;
- pkt.cp_gpa_cnt = sglen;
-
- iov[0].iov_base = &pkt;
- iov[0].iov_len = sizeof(pkt);
- iov[1].iov_base = sg;
- iov[1].iov_len = sizeof(struct vmbus_gpa) * sglen;
- iov[2].iov_base = data;
- iov[2].iov_len = dlen;
- iov[3].iov_base = &pad;
- iov[3].iov_len = pad_pktlen - pktlen;
-
- error = hv_ring_buffer_write(&chan->outbound, iov, 4, &send_evt);
- if (!error && send_evt)
- vmbus_chan_signal_tx(chan);
- return error;
-}
-
-int
-vmbus_chan_send_prplist(struct hv_vmbus_channel *chan,
- struct vmbus_gpa_range *prp, int prp_cnt, void *data, int dlen,
- uint64_t xactid)
-{
- struct vmbus_chanpkt_prplist pkt;
- int pktlen, pad_pktlen, hlen, error;
- struct iovec iov[4];
- boolean_t send_evt;
- uint64_t pad = 0;
-
- KASSERT(prp_cnt < VMBUS_CHAN_PRPLIST_MAX,
- ("invalid prplist entry count %d", prp_cnt));
-
- hlen = __offsetof(struct vmbus_chanpkt_prplist,
- cp_range[0].gpa_page[prp_cnt]);
- pktlen = hlen + dlen;
- pad_pktlen = VMBUS_CHANPKT_TOTLEN(pktlen);
-
- pkt.cp_hdr.cph_type = VMBUS_CHANPKT_TYPE_GPA;
- pkt.cp_hdr.cph_flags = VMBUS_CHANPKT_FLAG_RC;
- VMBUS_CHANPKT_SETLEN(pkt.cp_hdr.cph_hlen, hlen);
- VMBUS_CHANPKT_SETLEN(pkt.cp_hdr.cph_tlen, pad_pktlen);
- pkt.cp_hdr.cph_xactid = xactid;
- pkt.cp_rsvd = 0;
- pkt.cp_range_cnt = 1;
-
- iov[0].iov_base = &pkt;
- iov[0].iov_len = sizeof(pkt);
- iov[1].iov_base = prp;
- iov[1].iov_len = __offsetof(struct vmbus_gpa_range, gpa_page[prp_cnt]);
- iov[2].iov_base = data;
- iov[2].iov_len = dlen;
- iov[3].iov_base = &pad;
- iov[3].iov_len = pad_pktlen - pktlen;
-
- error = hv_ring_buffer_write(&chan->outbound, iov, 4, &send_evt);
- if (!error && send_evt)
- vmbus_chan_signal_tx(chan);
- return error;
-}
-
-int
-vmbus_chan_recv(struct hv_vmbus_channel *chan, void *data, int *dlen0,
- uint64_t *xactid)
-{
- struct vmbus_chanpkt_hdr pkt;
- int error, dlen, hlen;
-
- error = hv_ring_buffer_peek(&chan->inbound, &pkt, sizeof(pkt));
- if (error)
- return error;
-
- hlen = VMBUS_CHANPKT_GETLEN(pkt.cph_hlen);
- dlen = VMBUS_CHANPKT_GETLEN(pkt.cph_tlen) - hlen;
-
- if (*dlen0 < dlen) {
- /* Return the size of this packet's data. */
- *dlen0 = dlen;
- return ENOBUFS;
- }
-
- *xactid = pkt.cph_xactid;
- *dlen0 = dlen;
-
- /* Skip packet header */
- error = hv_ring_buffer_read(&chan->inbound, data, dlen, hlen);
- KASSERT(!error, ("hv_ring_buffer_read failed"));
-
- return 0;
-}
-
-int
-vmbus_chan_recv_pkt(struct hv_vmbus_channel *chan,
- struct vmbus_chanpkt_hdr *pkt0, int *pktlen0)
-{
- struct vmbus_chanpkt_hdr pkt;
- int error, pktlen;
-
- error = hv_ring_buffer_peek(&chan->inbound, &pkt, sizeof(pkt));
- if (error)
- return error;
-
- pktlen = VMBUS_CHANPKT_GETLEN(pkt.cph_tlen);
- if (*pktlen0 < pktlen) {
- /* Return the size of this packet. */
- *pktlen0 = pktlen;
- return ENOBUFS;
- }
- *pktlen0 = pktlen;
-
- /* Include packet header */
- error = hv_ring_buffer_read(&chan->inbound, pkt0, pktlen, 0);
- KASSERT(!error, ("hv_ring_buffer_read failed"));
-
- return 0;
-}
-
-static void
-vmbus_chan_task(void *xchan, int pending __unused)
-{
- struct hv_vmbus_channel *chan = xchan;
- vmbus_chan_callback_t cb = chan->ch_cb;
- void *cbarg = chan->ch_cbarg;
-
- /*
- * Optimize host to guest signaling by ensuring:
- * 1. While reading the channel, we disable interrupts from
- * host.
- * 2. Ensure that we process all posted messages from the host
- * before returning from this callback.
- * 3. Once we return, enable signaling from the host. Once this
- * state is set we check to see if additional packets are
- * available to read. In this case we repeat the process.
- *
- * NOTE: Interrupt has been disabled in the ISR.
- */
- for (;;) {
- uint32_t left;
-
- cb(cbarg);
-
- left = hv_ring_buffer_read_end(&chan->inbound);
- if (left == 0) {
- /* No more data in RX bufring; done */
- break;
- }
- hv_ring_buffer_read_begin(&chan->inbound);
- }
-}
-
-static void
-vmbus_chan_task_nobatch(void *xchan, int pending __unused)
-{
- struct hv_vmbus_channel *chan = xchan;
-
- chan->ch_cb(chan->ch_cbarg);
-}
-
-static __inline void
-vmbus_event_flags_proc(struct vmbus_softc *sc, volatile u_long *event_flags,
- int flag_cnt)
-{
- int f;
-
- for (f = 0; f < flag_cnt; ++f) {
- uint32_t chid_base;
- u_long flags;
- int chid_ofs;
-
- if (event_flags[f] == 0)
- continue;
-
- flags = atomic_swap_long(&event_flags[f], 0);
- chid_base = f << VMBUS_EVTFLAG_SHIFT;
-
- while ((chid_ofs = ffsl(flags)) != 0) {
- struct hv_vmbus_channel *chan;
-
- --chid_ofs; /* NOTE: ffsl is 1-based */
- flags &= ~(1UL << chid_ofs);
-
- chan = sc->vmbus_chmap[chid_base + chid_ofs];
-
- /* if channel is closed or closing */
- if (chan == NULL || chan->ch_tq == NULL)
- continue;
-
- if (chan->ch_flags & VMBUS_CHAN_FLAG_BATCHREAD)
- hv_ring_buffer_read_begin(&chan->inbound);
- taskqueue_enqueue(chan->ch_tq, &chan->ch_task);
- }
- }
-}
-
-void
-vmbus_event_proc(struct vmbus_softc *sc, int cpu)
-{
- struct vmbus_evtflags *eventf;
-
- /*
- * On Host with Win8 or above, the event page can be checked directly
- * to get the id of the channel that has the pending interrupt.
- */
- eventf = VMBUS_PCPU_GET(sc, event_flags, cpu) + VMBUS_SINT_MESSAGE;
- vmbus_event_flags_proc(sc, eventf->evt_flags,
- VMBUS_PCPU_GET(sc, event_flags_cnt, cpu));
-}
-
-void
-vmbus_event_proc_compat(struct vmbus_softc *sc, int cpu)
-{
- struct vmbus_evtflags *eventf;
-
- eventf = VMBUS_PCPU_GET(sc, event_flags, cpu) + VMBUS_SINT_MESSAGE;
- if (atomic_testandclear_long(&eventf->evt_flags[0], 0)) {
- vmbus_event_flags_proc(sc, sc->vmbus_rx_evtflags,
- VMBUS_CHAN_MAX_COMPAT >> VMBUS_EVTFLAG_SHIFT);
- }
-}
-
-static void
-vmbus_chan_update_evtflagcnt(struct vmbus_softc *sc,
- const struct hv_vmbus_channel *chan)
-{
- volatile int *flag_cnt_ptr;
- int flag_cnt;
-
- flag_cnt = (chan->ch_id / VMBUS_EVTFLAG_LEN) + 1;
- flag_cnt_ptr = VMBUS_PCPU_PTR(sc, event_flags_cnt, chan->ch_cpuid);
-
- for (;;) {
- int old_flag_cnt;
-
- old_flag_cnt = *flag_cnt_ptr;
- if (old_flag_cnt >= flag_cnt)
- break;
- if (atomic_cmpset_int(flag_cnt_ptr, old_flag_cnt, flag_cnt)) {
- if (bootverbose) {
- device_printf(sc->vmbus_dev,
- "channel%u update cpu%d flag_cnt to %d\n",
- chan->ch_id, chan->ch_cpuid, flag_cnt);
- }
- break;
- }
- }
-}
-
-static struct hv_vmbus_channel *
-vmbus_chan_alloc(struct vmbus_softc *sc)
-{
- struct hv_vmbus_channel *chan;
-
- chan = malloc(sizeof(*chan), M_DEVBUF, M_WAITOK | M_ZERO);
-
- chan->ch_monprm = hyperv_dmamem_alloc(bus_get_dma_tag(sc->vmbus_dev),
- HYPERCALL_PARAM_ALIGN, 0, sizeof(struct hyperv_mon_param),
- &chan->ch_monprm_dma, BUS_DMA_WAITOK | BUS_DMA_ZERO);
- if (chan->ch_monprm == NULL) {
- device_printf(sc->vmbus_dev, "monprm alloc failed\n");
- free(chan, M_DEVBUF);
- return NULL;
- }
-
- chan->vmbus_sc = sc;
- mtx_init(&chan->ch_subchan_lock, "vmbus subchan", NULL, MTX_DEF);
- TAILQ_INIT(&chan->ch_subchans);
- TASK_INIT(&chan->ch_detach_task, 0, vmbus_chan_detach_task, chan);
-
- return chan;
-}
-
-static void
-vmbus_chan_free(struct hv_vmbus_channel *chan)
-{
- /* TODO: assert sub-channel list is empty */
- /* TODO: asset no longer on the primary channel's sub-channel list */
- /* TODO: asset no longer on the vmbus channel list */
- hyperv_dmamem_free(&chan->ch_monprm_dma, chan->ch_monprm);
- mtx_destroy(&chan->ch_subchan_lock);
- free(chan, M_DEVBUF);
-}
-
-static int
-vmbus_chan_add(struct hv_vmbus_channel *newchan)
-{
- struct vmbus_softc *sc = newchan->vmbus_sc;
- struct hv_vmbus_channel *prichan;
-
- if (newchan->ch_id == 0) {
- /*
- * XXX
- * Chan0 will neither be processed nor should be offered;
- * skip it.
- */
- device_printf(sc->vmbus_dev, "got chan0 offer, discard\n");
- return EINVAL;
- } else if (newchan->ch_id >= VMBUS_CHAN_MAX) {
- device_printf(sc->vmbus_dev, "invalid chan%u offer\n",
- newchan->ch_id);
- return EINVAL;
- }
- sc->vmbus_chmap[newchan->ch_id] = newchan;
-
- if (bootverbose) {
- device_printf(sc->vmbus_dev, "chan%u subidx%u offer\n",
- newchan->ch_id, newchan->ch_subidx);
- }
-
- mtx_lock(&sc->vmbus_prichan_lock);
- TAILQ_FOREACH(prichan, &sc->vmbus_prichans, ch_prilink) {
- /*
- * Sub-channel will have the same type GUID and instance
- * GUID as its primary channel.
- */
- if (memcmp(&prichan->ch_guid_type, &newchan->ch_guid_type,
- sizeof(struct hyperv_guid)) == 0 &&
- memcmp(&prichan->ch_guid_inst, &newchan->ch_guid_inst,
- sizeof(struct hyperv_guid)) == 0)
- break;
- }
- if (VMBUS_CHAN_ISPRIMARY(newchan)) {
- if (prichan == NULL) {
- /* Install the new primary channel */
- TAILQ_INSERT_TAIL(&sc->vmbus_prichans, newchan,
- ch_prilink);
- mtx_unlock(&sc->vmbus_prichan_lock);
- return 0;
- } else {
- mtx_unlock(&sc->vmbus_prichan_lock);
- device_printf(sc->vmbus_dev, "duplicated primary "
- "chan%u\n", newchan->ch_id);
- return EINVAL;
- }
- } else { /* Sub-channel */
- if (prichan == NULL) {
- mtx_unlock(&sc->vmbus_prichan_lock);
- device_printf(sc->vmbus_dev, "no primary chan for "
- "chan%u\n", newchan->ch_id);
- return EINVAL;
- }
- /*
- * Found the primary channel for this sub-channel and
- * move on.
- *
- * XXX refcnt prichan
- */
- }
- mtx_unlock(&sc->vmbus_prichan_lock);
-
- /*
- * This is a sub-channel; link it with the primary channel.
- */
- KASSERT(!VMBUS_CHAN_ISPRIMARY(newchan),
- ("new channel is not sub-channel"));
- KASSERT(prichan != NULL, ("no primary channel"));
-
- newchan->ch_prichan = prichan;
- newchan->ch_dev = prichan->ch_dev;
-
- mtx_lock(&prichan->ch_subchan_lock);
- TAILQ_INSERT_TAIL(&prichan->ch_subchans, newchan, ch_sublink);
- /*
- * Bump up sub-channel count and notify anyone that is
- * interested in this sub-channel, after this sub-channel
- * is setup.
- */
- prichan->ch_subchan_cnt++;
- mtx_unlock(&prichan->ch_subchan_lock);
- wakeup(prichan);
-
- return 0;
-}
-
-void
-vmbus_chan_cpu_set(struct hv_vmbus_channel *chan, int cpu)
-{
- KASSERT(cpu >= 0 && cpu < mp_ncpus, ("invalid cpu %d", cpu));
-
- if (chan->vmbus_sc->vmbus_version == VMBUS_VERSION_WS2008 ||
- chan->vmbus_sc->vmbus_version == VMBUS_VERSION_WIN7) {
- /* Only cpu0 is supported */
- cpu = 0;
- }
-
- chan->ch_cpuid = cpu;
- chan->ch_vcpuid = VMBUS_PCPU_GET(chan->vmbus_sc, vcpuid, cpu);
-
- if (bootverbose) {
- printf("vmbus_chan%u: assigned to cpu%u [vcpu%u]\n",
- chan->ch_id, chan->ch_cpuid, chan->ch_vcpuid);
- }
-}
-
-void
-vmbus_chan_cpu_rr(struct hv_vmbus_channel *chan)
-{
- static uint32_t vmbus_chan_nextcpu;
- int cpu;
-
- cpu = atomic_fetchadd_int(&vmbus_chan_nextcpu, 1) % mp_ncpus;
- vmbus_chan_cpu_set(chan, cpu);
-}
-
-static void
-vmbus_chan_cpu_default(struct hv_vmbus_channel *chan)
-{
- /*
- * By default, pin the channel to cpu0. Devices having
- * special channel-cpu mapping requirement should call
- * vmbus_chan_cpu_{set,rr}().
- */
- vmbus_chan_cpu_set(chan, 0);
-}
-
-static void
-vmbus_chan_msgproc_choffer(struct vmbus_softc *sc,
- const struct vmbus_message *msg)
-{
- const struct vmbus_chanmsg_choffer *offer;
- struct hv_vmbus_channel *chan;
- int error;
-
- offer = (const struct vmbus_chanmsg_choffer *)msg->msg_data;
-
- chan = vmbus_chan_alloc(sc);
- if (chan == NULL) {
- device_printf(sc->vmbus_dev, "allocate chan%u failed\n",
- offer->chm_chanid);
- return;
- }
-
- chan->ch_id = offer->chm_chanid;
- chan->ch_subidx = offer->chm_subidx;
- chan->ch_guid_type = offer->chm_chtype;
- chan->ch_guid_inst = offer->chm_chinst;
-
- /* Batch reading is on by default */
- chan->ch_flags |= VMBUS_CHAN_FLAG_BATCHREAD;
-
- chan->ch_monprm->mp_connid = VMBUS_CONNID_EVENT;
- if (sc->vmbus_version != VMBUS_VERSION_WS2008)
- chan->ch_monprm->mp_connid = offer->chm_connid;
-
- if (offer->chm_flags1 & VMBUS_CHOFFER_FLAG1_HASMNF) {
- /*
- * Setup MNF stuffs.
- */
- chan->ch_flags |= VMBUS_CHAN_FLAG_HASMNF;
- chan->ch_montrig_idx = offer->chm_montrig / VMBUS_MONTRIG_LEN;
- if (chan->ch_montrig_idx >= VMBUS_MONTRIGS_MAX)
- panic("invalid monitor trigger %u", offer->chm_montrig);
- chan->ch_montrig_mask =
- 1 << (offer->chm_montrig % VMBUS_MONTRIG_LEN);
- }
-
- /* Select default cpu for this channel. */
- vmbus_chan_cpu_default(chan);
-
- error = vmbus_chan_add(chan);
- if (error) {
- device_printf(sc->vmbus_dev, "add chan%u failed: %d\n",
- chan->ch_id, error);
- vmbus_chan_free(chan);
- return;
- }
-
- if (VMBUS_CHAN_ISPRIMARY(chan)) {
- /*
- * Add device for this primary channel.
- *
- * NOTE:
- * Error is ignored here; don't have much to do if error
- * really happens.
- */
- vmbus_add_child(chan);
- }
-}
-
-/*
- * XXX pretty broken; need rework.
- */
-static void
-vmbus_chan_msgproc_chrescind(struct vmbus_softc *sc,
- const struct vmbus_message *msg)
-{
- const struct vmbus_chanmsg_chrescind *note;
- struct hv_vmbus_channel *chan;
-
- note = (const struct vmbus_chanmsg_chrescind *)msg->msg_data;
- if (note->chm_chanid > VMBUS_CHAN_MAX) {
- device_printf(sc->vmbus_dev, "invalid rescinded chan%u\n",
- note->chm_chanid);
- return;
- }
-
- if (bootverbose) {
- device_printf(sc->vmbus_dev, "chan%u rescinded\n",
- note->chm_chanid);
- }
-
- chan = sc->vmbus_chmap[note->chm_chanid];
- if (chan == NULL)
- return;
- sc->vmbus_chmap[note->chm_chanid] = NULL;
-
- taskqueue_enqueue(taskqueue_thread, &chan->ch_detach_task);
-}
-
-static void
-vmbus_chan_detach_task(void *xchan, int pending __unused)
-{
- struct hv_vmbus_channel *chan = xchan;
-
- if (VMBUS_CHAN_ISPRIMARY(chan)) {
- /* Only primary channel owns the device */
- vmbus_delete_child(chan);
- /* NOTE: DO NOT free primary channel for now */
- } else {
- struct vmbus_softc *sc = chan->vmbus_sc;
- struct hv_vmbus_channel *pri_chan = chan->ch_prichan;
- struct vmbus_chanmsg_chfree *req;
- struct vmbus_msghc *mh;
- int error;
-
- mh = vmbus_msghc_get(sc, sizeof(*req));
- if (mh == NULL) {
- device_printf(sc->vmbus_dev,
- "can not get msg hypercall for chfree(chan%u)\n",
- chan->ch_id);
- goto remove;
- }
-
- req = vmbus_msghc_dataptr(mh);
- req->chm_hdr.chm_type = VMBUS_CHANMSG_TYPE_CHFREE;
- req->chm_chanid = chan->ch_id;
-
- error = vmbus_msghc_exec_noresult(mh);
- vmbus_msghc_put(sc, mh);
-
- if (error) {
- device_printf(sc->vmbus_dev,
- "chfree(chan%u) failed: %d",
- chan->ch_id, error);
- /* NOTE: Move on! */
- } else {
- if (bootverbose) {
- device_printf(sc->vmbus_dev, "chan%u freed\n",
- chan->ch_id);
- }
- }
-remove:
- mtx_lock(&pri_chan->ch_subchan_lock);
- TAILQ_REMOVE(&pri_chan->ch_subchans, chan, ch_sublink);
- KASSERT(pri_chan->ch_subchan_cnt > 0,
- ("invalid subchan_cnt %d", pri_chan->ch_subchan_cnt));
- pri_chan->ch_subchan_cnt--;
- mtx_unlock(&pri_chan->ch_subchan_lock);
- wakeup(pri_chan);
-
- vmbus_chan_free(chan);
- }
-}
-
-/*
- * Detach all devices and destroy the corresponding primary channels.
- */
-void
-vmbus_chan_destroy_all(struct vmbus_softc *sc)
-{
- struct hv_vmbus_channel *chan;
-
- mtx_lock(&sc->vmbus_prichan_lock);
- while ((chan = TAILQ_FIRST(&sc->vmbus_prichans)) != NULL) {
- KASSERT(VMBUS_CHAN_ISPRIMARY(chan), ("not primary channel"));
- TAILQ_REMOVE(&sc->vmbus_prichans, chan, ch_prilink);
- mtx_unlock(&sc->vmbus_prichan_lock);
-
- vmbus_delete_child(chan);
- vmbus_chan_free(chan);
-
- mtx_lock(&sc->vmbus_prichan_lock);
- }
- bzero(sc->vmbus_chmap,
- sizeof(struct hv_vmbus_channel *) * VMBUS_CHAN_MAX);
- mtx_unlock(&sc->vmbus_prichan_lock);
-}
-
-/*
- * The channel whose vcpu binding is closest to the currect vcpu will
- * be selected.
- * If no multi-channel, always select primary channel.
- */
-struct hv_vmbus_channel *
-vmbus_chan_cpu2chan(struct hv_vmbus_channel *prichan, int cpu)
-{
- struct hv_vmbus_channel *sel, *chan;
- uint32_t vcpu, sel_dist;
-
- KASSERT(cpu >= 0 && cpu < mp_ncpus, ("invalid cpuid %d", cpu));
- if (TAILQ_EMPTY(&prichan->ch_subchans))
- return prichan;
-
- vcpu = VMBUS_PCPU_GET(prichan->vmbus_sc, vcpuid, cpu);
-
-#define CHAN_VCPU_DIST(ch, vcpu) \
- (((ch)->ch_vcpuid > (vcpu)) ? \
- ((ch)->ch_vcpuid - (vcpu)) : ((vcpu) - (ch)->ch_vcpuid))
-
-#define CHAN_SELECT(ch) \
-do { \
- sel = ch; \
- sel_dist = CHAN_VCPU_DIST(ch, vcpu); \
-} while (0)
-
- CHAN_SELECT(prichan);
-
- mtx_lock(&prichan->ch_subchan_lock);
- TAILQ_FOREACH(chan, &prichan->ch_subchans, ch_sublink) {
- uint32_t dist;
-
- KASSERT(chan->ch_stflags & VMBUS_CHAN_ST_OPENED,
- ("chan%u is not opened", chan->ch_id));
-
- if (chan->ch_vcpuid == vcpu) {
- /* Exact match; done */
- CHAN_SELECT(chan);
- break;
- }
-
- dist = CHAN_VCPU_DIST(chan, vcpu);
- if (sel_dist <= dist) {
- /* Far or same distance; skip */
- continue;
- }
-
- /* Select the closer channel. */
- CHAN_SELECT(chan);
- }
- mtx_unlock(&prichan->ch_subchan_lock);
-
-#undef CHAN_SELECT
-#undef CHAN_VCPU_DIST
-
- return sel;
-}
-
-struct hv_vmbus_channel **
-vmbus_subchan_get(struct hv_vmbus_channel *pri_chan, int subchan_cnt)
-{
- struct hv_vmbus_channel **ret, *chan;
- int i;
-
- ret = malloc(subchan_cnt * sizeof(struct hv_vmbus_channel *), M_TEMP,
- M_WAITOK);
-
- mtx_lock(&pri_chan->ch_subchan_lock);
-
- while (pri_chan->ch_subchan_cnt < subchan_cnt)
- mtx_sleep(pri_chan, &pri_chan->ch_subchan_lock, 0, "subch", 0);
-
- i = 0;
- TAILQ_FOREACH(chan, &pri_chan->ch_subchans, ch_sublink) {
- /* TODO: refcnt chan */
- ret[i] = chan;
-
- ++i;
- if (i == subchan_cnt)
- break;
- }
- KASSERT(i == subchan_cnt, ("invalid subchan count %d, should be %d",
- pri_chan->ch_subchan_cnt, subchan_cnt));
-
- mtx_unlock(&pri_chan->ch_subchan_lock);
-
- return ret;
-}
-
-void
-vmbus_subchan_rel(struct hv_vmbus_channel **subchan, int subchan_cnt __unused)
-{
-
- free(subchan, M_TEMP);
-}
-
-void
-vmbus_subchan_drain(struct hv_vmbus_channel *pri_chan)
-{
- mtx_lock(&pri_chan->ch_subchan_lock);
- while (pri_chan->ch_subchan_cnt > 0)
- mtx_sleep(pri_chan, &pri_chan->ch_subchan_lock, 0, "dsubch", 0);
- mtx_unlock(&pri_chan->ch_subchan_lock);
-}
-
-void
-vmbus_chan_msgproc(struct vmbus_softc *sc, const struct vmbus_message *msg)
-{
- vmbus_chanmsg_proc_t msg_proc;
- uint32_t msg_type;
-
- msg_type = ((const struct vmbus_chanmsg_hdr *)msg->msg_data)->chm_type;
- KASSERT(msg_type < VMBUS_CHANMSG_TYPE_MAX,
- ("invalid message type %u", msg_type));
-
- msg_proc = vmbus_chan_msgprocs[msg_type];
- if (msg_proc != NULL)
- msg_proc(sc, msg);
-}
-
-void
-vmbus_chan_set_readbatch(struct hv_vmbus_channel *chan, bool on)
-{
- if (!on)
- chan->ch_flags &= ~VMBUS_CHAN_FLAG_BATCHREAD;
- else
- chan->ch_flags |= VMBUS_CHAN_FLAG_BATCHREAD;
-}
Index: head/sys/dev/hyperv/vmbus/vmbus_chan.c
===================================================================
--- head/sys/dev/hyperv/vmbus/vmbus_chan.c
+++ head/sys/dev/hyperv/vmbus/vmbus_chan.c
@@ -0,0 +1,1380 @@
+/*-
+ * Copyright (c) 2009-2012,2016 Microsoft Corp.
+ * Copyright (c) 2012 NetApp Inc.
+ * Copyright (c) 2012 Citrix Inc.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice unmodified, this list of conditions, and the following
+ * disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+ * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include <sys/cdefs.h>
+__FBSDID("$FreeBSD$");
+
+#include <sys/param.h>
+#include <sys/kernel.h>
+#include <sys/malloc.h>
+#include <sys/systm.h>
+#include <sys/mbuf.h>
+#include <sys/lock.h>
+#include <sys/mutex.h>
+#include <sys/sysctl.h>
+
+#include <machine/atomic.h>
+#include <machine/bus.h>
+
+#include <vm/vm.h>
+#include <vm/vm_param.h>
+#include <vm/pmap.h>
+
+#include <dev/hyperv/include/hyperv_busdma.h>
+#include <dev/hyperv/vmbus/hv_vmbus_priv.h>
+#include <dev/hyperv/vmbus/hyperv_var.h>
+#include <dev/hyperv/vmbus/vmbus_reg.h>
+#include <dev/hyperv/vmbus/vmbus_var.h>
+
+static void vmbus_chan_signal_tx(struct hv_vmbus_channel *chan);
+static void vmbus_chan_update_evtflagcnt(struct vmbus_softc *,
+ const struct hv_vmbus_channel *);
+
+static void vmbus_chan_task(void *, int);
+static void vmbus_chan_task_nobatch(void *, int);
+static void vmbus_chan_detach_task(void *, int);
+
+static void vmbus_chan_msgproc_choffer(struct vmbus_softc *,
+ const struct vmbus_message *);
+static void vmbus_chan_msgproc_chrescind(struct vmbus_softc *,
+ const struct vmbus_message *);
+
+/*
+ * Vmbus channel message processing.
+ */
+static const vmbus_chanmsg_proc_t
+vmbus_chan_msgprocs[VMBUS_CHANMSG_TYPE_MAX] = {
+ VMBUS_CHANMSG_PROC(CHOFFER, vmbus_chan_msgproc_choffer),
+ VMBUS_CHANMSG_PROC(CHRESCIND, vmbus_chan_msgproc_chrescind),
+
+ VMBUS_CHANMSG_PROC_WAKEUP(CHOPEN_RESP),
+ VMBUS_CHANMSG_PROC_WAKEUP(GPADL_CONNRESP),
+ VMBUS_CHANMSG_PROC_WAKEUP(GPADL_DISCONNRESP)
+};
+
+/**
+ * @brief Trigger an event notification on the specified channel
+ */
+static void
+vmbus_chan_signal_tx(struct hv_vmbus_channel *chan)
+{
+ struct vmbus_softc *sc = chan->vmbus_sc;
+ uint32_t chanid = chan->ch_id;
+
+ atomic_set_long(&sc->vmbus_tx_evtflags[chanid >> VMBUS_EVTFLAG_SHIFT],
+ 1UL << (chanid & VMBUS_EVTFLAG_MASK));
+
+ if (chan->ch_flags & VMBUS_CHAN_FLAG_HASMNF) {
+ atomic_set_int(
+ &sc->vmbus_mnf2->mnf_trigs[chan->ch_montrig_idx].mt_pending,
+ chan->ch_montrig_mask);
+ } else {
+ hypercall_signal_event(chan->ch_monprm_dma.hv_paddr);
+ }
+}
+
+static int
+vmbus_chan_sysctl_mnf(SYSCTL_HANDLER_ARGS)
+{
+ struct hv_vmbus_channel *chan = arg1;
+ int mnf = 0;
+
+ if (chan->ch_flags & VMBUS_CHAN_FLAG_HASMNF)
+ mnf = 1;
+ return sysctl_handle_int(oidp, &mnf, 0, req);
+}
+
+static void
+vmbus_chan_sysctl_create(struct hv_vmbus_channel *chan)
+{
+ struct sysctl_oid *ch_tree, *chid_tree, *br_tree;
+ struct sysctl_ctx_list *ctx;
+ uint32_t ch_id;
+ char name[16];
+
+ /*
+ * Add sysctl nodes related to this channel to this
+ * channel's sysctl ctx, so that they can be destroyed
+ * independently upon close of this channel, which can
+ * happen even if the device is not detached.
+ */
+ ctx = &chan->ch_sysctl_ctx;
+ sysctl_ctx_init(ctx);
+
+ /*
+ * Create dev.NAME.UNIT.channel tree.
+ */
+ ch_tree = SYSCTL_ADD_NODE(ctx,
+ SYSCTL_CHILDREN(device_get_sysctl_tree(chan->ch_dev)),
+ OID_AUTO, "channel", CTLFLAG_RD | CTLFLAG_MPSAFE, 0, "");
+ if (ch_tree == NULL)
+ return;
+
+ /*
+ * Create dev.NAME.UNIT.channel.CHANID tree.
+ */
+ if (VMBUS_CHAN_ISPRIMARY(chan))
+ ch_id = chan->ch_id;
+ else
+ ch_id = chan->ch_prichan->ch_id;
+ snprintf(name, sizeof(name), "%d", ch_id);
+ chid_tree = SYSCTL_ADD_NODE(ctx, SYSCTL_CHILDREN(ch_tree),
+ OID_AUTO, name, CTLFLAG_RD | CTLFLAG_MPSAFE, 0, "");
+ if (chid_tree == NULL)
+ return;
+
+ if (!VMBUS_CHAN_ISPRIMARY(chan)) {
+ /*
+ * Create dev.NAME.UNIT.channel.CHANID.sub tree.
+ */
+ ch_tree = SYSCTL_ADD_NODE(ctx, SYSCTL_CHILDREN(chid_tree),
+ OID_AUTO, "sub", CTLFLAG_RD | CTLFLAG_MPSAFE, 0, "");
+ if (ch_tree == NULL)
+ return;
+
+ /*
+ * Create dev.NAME.UNIT.channel.CHANID.sub.SUBIDX tree.
+ *
+ * NOTE:
+ * chid_tree is changed to this new sysctl tree.
+ */
+ snprintf(name, sizeof(name), "%d", chan->ch_subidx);
+ chid_tree = SYSCTL_ADD_NODE(ctx, SYSCTL_CHILDREN(ch_tree),
+ OID_AUTO, name, CTLFLAG_RD | CTLFLAG_MPSAFE, 0, "");
+ if (chid_tree == NULL)
+ return;
+
+ SYSCTL_ADD_UINT(ctx, SYSCTL_CHILDREN(chid_tree), OID_AUTO,
+ "chanid", CTLFLAG_RD, &chan->ch_id, 0, "channel id");
+ }
+
+ SYSCTL_ADD_UINT(ctx, SYSCTL_CHILDREN(chid_tree), OID_AUTO,
+ "cpu", CTLFLAG_RD, &chan->ch_cpuid, 0, "owner CPU id");
+ SYSCTL_ADD_PROC(ctx, SYSCTL_CHILDREN(chid_tree), OID_AUTO,
+ "mnf", CTLTYPE_INT | CTLFLAG_RD | CTLFLAG_MPSAFE,
+ chan, 0, vmbus_chan_sysctl_mnf, "I",
+ "has monitor notification facilities");
+
+ /*
+ * Create sysctl tree for RX bufring.
+ */
+ br_tree = SYSCTL_ADD_NODE(ctx, SYSCTL_CHILDREN(chid_tree), OID_AUTO,
+ "in", CTLFLAG_RD | CTLFLAG_MPSAFE, 0, "");
+ if (br_tree != NULL) {
+ hv_ring_buffer_stat(ctx, SYSCTL_CHILDREN(br_tree),
+ &chan->inbound, "inbound ring buffer stats");
+ }
+
+ /*
+ * Create sysctl tree for TX bufring.
+ */
+ br_tree = SYSCTL_ADD_NODE(ctx, SYSCTL_CHILDREN(chid_tree), OID_AUTO,
+ "out", CTLFLAG_RD | CTLFLAG_MPSAFE, 0, "");
+ if (br_tree != NULL) {
+ hv_ring_buffer_stat(ctx, SYSCTL_CHILDREN(br_tree),
+ &chan->outbound, "outbound ring buffer stats");
+ }
+}
+
+int
+vmbus_chan_open(struct hv_vmbus_channel *chan, int txbr_size, int rxbr_size,
+ const void *udata, int udlen, vmbus_chan_callback_t cb, void *cbarg)
+{
+ struct vmbus_softc *sc = chan->vmbus_sc;
+ const struct vmbus_chanmsg_chopen_resp *resp;
+ const struct vmbus_message *msg;
+ struct vmbus_chanmsg_chopen *req;
+ struct vmbus_msghc *mh;
+ uint32_t status;
+ int error;
+ uint8_t *br;
+
+ if (udlen > VMBUS_CHANMSG_CHOPEN_UDATA_SIZE) {
+ device_printf(sc->vmbus_dev,
+ "invalid udata len %d for chan%u\n", udlen, chan->ch_id);
+ return EINVAL;
+ }
+ KASSERT((txbr_size & PAGE_MASK) == 0,
+ ("send bufring size is not multiple page"));
+ KASSERT((rxbr_size & PAGE_MASK) == 0,
+ ("recv bufring size is not multiple page"));
+
+ if (atomic_testandset_int(&chan->ch_stflags,
+ VMBUS_CHAN_ST_OPENED_SHIFT))
+ panic("double-open chan%u", chan->ch_id);
+
+ chan->ch_cb = cb;
+ chan->ch_cbarg = cbarg;
+
+ vmbus_chan_update_evtflagcnt(sc, chan);
+
+ chan->ch_tq = VMBUS_PCPU_GET(chan->vmbus_sc, event_tq, chan->ch_cpuid);
+ if (chan->ch_flags & VMBUS_CHAN_FLAG_BATCHREAD)
+ TASK_INIT(&chan->ch_task, 0, vmbus_chan_task, chan);
+ else
+ TASK_INIT(&chan->ch_task, 0, vmbus_chan_task_nobatch, chan);
+
+ /*
+ * Allocate the TX+RX bufrings.
+ * XXX should use ch_dev dtag
+ */
+ br = hyperv_dmamem_alloc(bus_get_dma_tag(sc->vmbus_dev),
+ PAGE_SIZE, 0, txbr_size + rxbr_size, &chan->ch_bufring_dma,
+ BUS_DMA_WAITOK | BUS_DMA_ZERO);
+ if (br == NULL) {
+ device_printf(sc->vmbus_dev, "bufring allocation failed\n");
+ error = ENOMEM;
+ goto failed;
+ }
+ chan->ch_bufring = br;
+
+ /* TX bufring comes first */
+ hv_vmbus_ring_buffer_init(&chan->outbound, br, txbr_size);
+ /* RX bufring immediately follows TX bufring */
+ hv_vmbus_ring_buffer_init(&chan->inbound, br + txbr_size, rxbr_size);
+
+ /* Create sysctl tree for this channel */
+ vmbus_chan_sysctl_create(chan);
+
+ /*
+ * Connect the bufrings, both RX and TX, to this channel.
+ */
+ error = vmbus_chan_gpadl_connect(chan, chan->ch_bufring_dma.hv_paddr,
+ txbr_size + rxbr_size, &chan->ch_bufring_gpadl);
+ if (error) {
+ device_printf(sc->vmbus_dev,
+ "failed to connect bufring GPADL to chan%u\n", chan->ch_id);
+ goto failed;
+ }
+
+ /*
+ * Open channel w/ the bufring GPADL on the target CPU.
+ */
+ mh = vmbus_msghc_get(sc, sizeof(*req));
+ if (mh == NULL) {
+ device_printf(sc->vmbus_dev,
+ "can not get msg hypercall for chopen(chan%u)\n",
+ chan->ch_id);
+ error = ENXIO;
+ goto failed;
+ }
+
+ req = vmbus_msghc_dataptr(mh);
+ req->chm_hdr.chm_type = VMBUS_CHANMSG_TYPE_CHOPEN;
+ req->chm_chanid = chan->ch_id;
+ req->chm_openid = chan->ch_id;
+ req->chm_gpadl = chan->ch_bufring_gpadl;
+ req->chm_vcpuid = chan->ch_vcpuid;
+ req->chm_txbr_pgcnt = txbr_size >> PAGE_SHIFT;
+ if (udlen > 0)
+ memcpy(req->chm_udata, udata, udlen);
+
+ error = vmbus_msghc_exec(sc, mh);
+ if (error) {
+ device_printf(sc->vmbus_dev,
+ "chopen(chan%u) msg hypercall exec failed: %d\n",
+ chan->ch_id, error);
+ vmbus_msghc_put(sc, mh);
+ goto failed;
+ }
+
+ msg = vmbus_msghc_wait_result(sc, mh);
+ resp = (const struct vmbus_chanmsg_chopen_resp *)msg->msg_data;
+ status = resp->chm_status;
+
+ vmbus_msghc_put(sc, mh);
+
+ if (status == 0) {
+ if (bootverbose) {
+ device_printf(sc->vmbus_dev, "chan%u opened\n",
+ chan->ch_id);
+ }
+ return 0;
+ }
+
+ device_printf(sc->vmbus_dev, "failed to open chan%u\n", chan->ch_id);
+ error = ENXIO;
+
+failed:
+ if (chan->ch_bufring_gpadl) {
+ vmbus_chan_gpadl_disconnect(chan, chan->ch_bufring_gpadl);
+ chan->ch_bufring_gpadl = 0;
+ }
+ if (chan->ch_bufring != NULL) {
+ hyperv_dmamem_free(&chan->ch_bufring_dma, chan->ch_bufring);
+ chan->ch_bufring = NULL;
+ }
+ atomic_clear_int(&chan->ch_stflags, VMBUS_CHAN_ST_OPENED);
+ return error;
+}
+
+int
+vmbus_chan_gpadl_connect(struct hv_vmbus_channel *chan, bus_addr_t paddr,
+ int size, uint32_t *gpadl0)
+{
+ struct vmbus_softc *sc = chan->vmbus_sc;
+ struct vmbus_msghc *mh;
+ struct vmbus_chanmsg_gpadl_conn *req;
+ const struct vmbus_message *msg;
+ size_t reqsz;
+ uint32_t gpadl, status;
+ int page_count, range_len, i, cnt, error;
+ uint64_t page_id;
+
+ /*
+ * Preliminary checks.
+ */
+
+ KASSERT((size & PAGE_MASK) == 0,
+ ("invalid GPA size %d, not multiple page size", size));
+ page_count = size >> PAGE_SHIFT;
+
+ KASSERT((paddr & PAGE_MASK) == 0,
+ ("GPA is not page aligned %jx", (uintmax_t)paddr));
+ page_id = paddr >> PAGE_SHIFT;
+
+ range_len = __offsetof(struct vmbus_gpa_range, gpa_page[page_count]);
+ /*
+ * We don't support multiple GPA ranges.
+ */
+ if (range_len > UINT16_MAX) {
+ device_printf(sc->vmbus_dev, "GPA too large, %d pages\n",
+ page_count);
+ return EOPNOTSUPP;
+ }
+
+ /*
+ * Allocate GPADL id.
+ */
+ gpadl = vmbus_gpadl_alloc(sc);
+ *gpadl0 = gpadl;
+
+ /*
+ * Connect this GPADL to the target channel.
+ *
+ * NOTE:
+ * Since each message can only hold small set of page
+ * addresses, several messages may be required to
+ * complete the connection.
+ */
+ if (page_count > VMBUS_CHANMSG_GPADL_CONN_PGMAX)
+ cnt = VMBUS_CHANMSG_GPADL_CONN_PGMAX;
+ else
+ cnt = page_count;
+ page_count -= cnt;
+
+ reqsz = __offsetof(struct vmbus_chanmsg_gpadl_conn,
+ chm_range.gpa_page[cnt]);
+ mh = vmbus_msghc_get(sc, reqsz);
+ if (mh == NULL) {
+ device_printf(sc->vmbus_dev,
+ "can not get msg hypercall for gpadl->chan%u\n",
+ chan->ch_id);
+ return EIO;
+ }
+
+ req = vmbus_msghc_dataptr(mh);
+ req->chm_hdr.chm_type = VMBUS_CHANMSG_TYPE_GPADL_CONN;
+ req->chm_chanid = chan->ch_id;
+ req->chm_gpadl = gpadl;
+ req->chm_range_len = range_len;
+ req->chm_range_cnt = 1;
+ req->chm_range.gpa_len = size;
+ req->chm_range.gpa_ofs = 0;
+ for (i = 0; i < cnt; ++i)
+ req->chm_range.gpa_page[i] = page_id++;
+
+ error = vmbus_msghc_exec(sc, mh);
+ if (error) {
+ device_printf(sc->vmbus_dev,
+ "gpadl->chan%u msg hypercall exec failed: %d\n",
+ chan->ch_id, error);
+ vmbus_msghc_put(sc, mh);
+ return error;
+ }
+
+ while (page_count > 0) {
+ struct vmbus_chanmsg_gpadl_subconn *subreq;
+
+ if (page_count > VMBUS_CHANMSG_GPADL_SUBCONN_PGMAX)
+ cnt = VMBUS_CHANMSG_GPADL_SUBCONN_PGMAX;
+ else
+ cnt = page_count;
+ page_count -= cnt;
+
+ reqsz = __offsetof(struct vmbus_chanmsg_gpadl_subconn,
+ chm_gpa_page[cnt]);
+ vmbus_msghc_reset(mh, reqsz);
+
+ subreq = vmbus_msghc_dataptr(mh);
+ subreq->chm_hdr.chm_type = VMBUS_CHANMSG_TYPE_GPADL_SUBCONN;
+ subreq->chm_gpadl = gpadl;
+ for (i = 0; i < cnt; ++i)
+ subreq->chm_gpa_page[i] = page_id++;
+
+ vmbus_msghc_exec_noresult(mh);
+ }
+ KASSERT(page_count == 0, ("invalid page count %d", page_count));
+
+ msg = vmbus_msghc_wait_result(sc, mh);
+ status = ((const struct vmbus_chanmsg_gpadl_connresp *)
+ msg->msg_data)->chm_status;
+
+ vmbus_msghc_put(sc, mh);
+
+ if (status != 0) {
+ device_printf(sc->vmbus_dev, "gpadl->chan%u failed: "
+ "status %u\n", chan->ch_id, status);
+ return EIO;
+ } else {
+ if (bootverbose) {
+ device_printf(sc->vmbus_dev, "gpadl->chan%u "
+ "succeeded\n", chan->ch_id);
+ }
+ }
+ return 0;
+}
+
+/*
+ * Disconnect the GPA from the target channel
+ */
+int
+vmbus_chan_gpadl_disconnect(struct hv_vmbus_channel *chan, uint32_t gpadl)
+{
+ struct vmbus_softc *sc = chan->vmbus_sc;
+ struct vmbus_msghc *mh;
+ struct vmbus_chanmsg_gpadl_disconn *req;
+ int error;
+
+ mh = vmbus_msghc_get(sc, sizeof(*req));
+ if (mh == NULL) {
+ device_printf(sc->vmbus_dev,
+ "can not get msg hypercall for gpa x->chan%u\n",
+ chan->ch_id);
+ return EBUSY;
+ }
+
+ req = vmbus_msghc_dataptr(mh);
+ req->chm_hdr.chm_type = VMBUS_CHANMSG_TYPE_GPADL_DISCONN;
+ req->chm_chanid = chan->ch_id;
+ req->chm_gpadl = gpadl;
+
+ error = vmbus_msghc_exec(sc, mh);
+ if (error) {
+ device_printf(sc->vmbus_dev,
+ "gpa x->chan%u msg hypercall exec failed: %d\n",
+ chan->ch_id, error);
+ vmbus_msghc_put(sc, mh);
+ return error;
+ }
+
+ vmbus_msghc_wait_result(sc, mh);
+ /* Discard result; no useful information */
+ vmbus_msghc_put(sc, mh);
+
+ return 0;
+}
+
+static void
+vmbus_chan_close_internal(struct hv_vmbus_channel *chan)
+{
+ struct vmbus_softc *sc = chan->vmbus_sc;
+ struct vmbus_msghc *mh;
+ struct vmbus_chanmsg_chclose *req;
+ struct taskqueue *tq = chan->ch_tq;
+ int error;
+
+ /* TODO: stringent check */
+ atomic_clear_int(&chan->ch_stflags, VMBUS_CHAN_ST_OPENED);
+
+ /*
+ * Free this channel's sysctl tree attached to its device's
+ * sysctl tree.
+ */
+ sysctl_ctx_free(&chan->ch_sysctl_ctx);
+
+ /*
+ * Set ch_tq to NULL to avoid more requests be scheduled.
+ * XXX pretty broken; need rework.
+ */
+ chan->ch_tq = NULL;
+ taskqueue_drain(tq, &chan->ch_task);
+ chan->ch_cb = NULL;
+
+ /*
+ * Close this channel.
+ */
+ mh = vmbus_msghc_get(sc, sizeof(*req));
+ if (mh == NULL) {
+ device_printf(sc->vmbus_dev,
+ "can not get msg hypercall for chclose(chan%u)\n",
+ chan->ch_id);
+ return;
+ }
+
+ req = vmbus_msghc_dataptr(mh);
+ req->chm_hdr.chm_type = VMBUS_CHANMSG_TYPE_CHCLOSE;
+ req->chm_chanid = chan->ch_id;
+
+ error = vmbus_msghc_exec_noresult(mh);
+ vmbus_msghc_put(sc, mh);
+
+ if (error) {
+ device_printf(sc->vmbus_dev,
+ "chclose(chan%u) msg hypercall exec failed: %d\n",
+ chan->ch_id, error);
+ return;
+ } else if (bootverbose) {
+ device_printf(sc->vmbus_dev, "close chan%u\n", chan->ch_id);
+ }
+
+ /*
+ * Disconnect the TX+RX bufrings from this channel.
+ */
+ if (chan->ch_bufring_gpadl) {
+ vmbus_chan_gpadl_disconnect(chan, chan->ch_bufring_gpadl);
+ chan->ch_bufring_gpadl = 0;
+ }
+
+ /*
+ * Destroy the TX+RX bufrings.
+ */
+ hv_ring_buffer_cleanup(&chan->outbound);
+ hv_ring_buffer_cleanup(&chan->inbound);
+ if (chan->ch_bufring != NULL) {
+ hyperv_dmamem_free(&chan->ch_bufring_dma, chan->ch_bufring);
+ chan->ch_bufring = NULL;
+ }
+}
+
+/*
+ * Caller should make sure that all sub-channels have
+ * been added to 'chan' and all to-be-closed channels
+ * are not being opened.
+ */
+void
+vmbus_chan_close(struct hv_vmbus_channel *chan)
+{
+ int subchan_cnt;
+
+ if (!VMBUS_CHAN_ISPRIMARY(chan)) {
+ /*
+ * Sub-channel is closed when its primary channel
+ * is closed; done.
+ */
+ return;
+ }
+
+ /*
+ * Close all sub-channels, if any.
+ */
+ subchan_cnt = chan->ch_subchan_cnt;
+ if (subchan_cnt > 0) {
+ struct hv_vmbus_channel **subchan;
+ int i;
+
+ subchan = vmbus_subchan_get(chan, subchan_cnt);
+ for (i = 0; i < subchan_cnt; ++i)
+ vmbus_chan_close_internal(subchan[i]);
+ vmbus_subchan_rel(subchan, subchan_cnt);
+ }
+
+ /* Then close the primary channel. */
+ vmbus_chan_close_internal(chan);
+}
+
+int
+vmbus_chan_send(struct hv_vmbus_channel *chan, uint16_t type, uint16_t flags,
+ void *data, int dlen, uint64_t xactid)
+{
+ struct vmbus_chanpkt pkt;
+ int pktlen, pad_pktlen, hlen, error;
+ uint64_t pad = 0;
+ struct iovec iov[3];
+ boolean_t send_evt;
+
+ hlen = sizeof(pkt);
+ pktlen = hlen + dlen;
+ pad_pktlen = VMBUS_CHANPKT_TOTLEN(pktlen);
+
+ pkt.cp_hdr.cph_type = type;
+ pkt.cp_hdr.cph_flags = flags;
+ VMBUS_CHANPKT_SETLEN(pkt.cp_hdr.cph_hlen, hlen);
+ VMBUS_CHANPKT_SETLEN(pkt.cp_hdr.cph_tlen, pad_pktlen);
+ pkt.cp_hdr.cph_xactid = xactid;
+
+ iov[0].iov_base = &pkt;
+ iov[0].iov_len = hlen;
+ iov[1].iov_base = data;
+ iov[1].iov_len = dlen;
+ iov[2].iov_base = &pad;
+ iov[2].iov_len = pad_pktlen - pktlen;
+
+ error = hv_ring_buffer_write(&chan->outbound, iov, 3, &send_evt);
+ if (!error && send_evt)
+ vmbus_chan_signal_tx(chan);
+ return error;
+}
+
+int
+vmbus_chan_send_sglist(struct hv_vmbus_channel *chan,
+ struct vmbus_gpa sg[], int sglen, void *data, int dlen, uint64_t xactid)
+{
+ struct vmbus_chanpkt_sglist pkt;
+ int pktlen, pad_pktlen, hlen, error;
+ struct iovec iov[4];
+ boolean_t send_evt;
+ uint64_t pad = 0;
+
+ KASSERT(sglen < VMBUS_CHAN_SGLIST_MAX,
+ ("invalid sglist len %d", sglen));
+
+ hlen = __offsetof(struct vmbus_chanpkt_sglist, cp_gpa[sglen]);
+ pktlen = hlen + dlen;
+ pad_pktlen = VMBUS_CHANPKT_TOTLEN(pktlen);
+
+ pkt.cp_hdr.cph_type = VMBUS_CHANPKT_TYPE_GPA;
+ pkt.cp_hdr.cph_flags = VMBUS_CHANPKT_FLAG_RC;
+ VMBUS_CHANPKT_SETLEN(pkt.cp_hdr.cph_hlen, hlen);
+ VMBUS_CHANPKT_SETLEN(pkt.cp_hdr.cph_tlen, pad_pktlen);
+ pkt.cp_hdr.cph_xactid = xactid;
+ pkt.cp_rsvd = 0;
+ pkt.cp_gpa_cnt = sglen;
+
+ iov[0].iov_base = &pkt;
+ iov[0].iov_len = sizeof(pkt);
+ iov[1].iov_base = sg;
+ iov[1].iov_len = sizeof(struct vmbus_gpa) * sglen;
+ iov[2].iov_base = data;
+ iov[2].iov_len = dlen;
+ iov[3].iov_base = &pad;
+ iov[3].iov_len = pad_pktlen - pktlen;
+
+ error = hv_ring_buffer_write(&chan->outbound, iov, 4, &send_evt);
+ if (!error && send_evt)
+ vmbus_chan_signal_tx(chan);
+ return error;
+}
+
+int
+vmbus_chan_send_prplist(struct hv_vmbus_channel *chan,
+ struct vmbus_gpa_range *prp, int prp_cnt, void *data, int dlen,
+ uint64_t xactid)
+{
+ struct vmbus_chanpkt_prplist pkt;
+ int pktlen, pad_pktlen, hlen, error;
+ struct iovec iov[4];
+ boolean_t send_evt;
+ uint64_t pad = 0;
+
+ KASSERT(prp_cnt < VMBUS_CHAN_PRPLIST_MAX,
+ ("invalid prplist entry count %d", prp_cnt));
+
+ hlen = __offsetof(struct vmbus_chanpkt_prplist,
+ cp_range[0].gpa_page[prp_cnt]);
+ pktlen = hlen + dlen;
+ pad_pktlen = VMBUS_CHANPKT_TOTLEN(pktlen);
+
+ pkt.cp_hdr.cph_type = VMBUS_CHANPKT_TYPE_GPA;
+ pkt.cp_hdr.cph_flags = VMBUS_CHANPKT_FLAG_RC;
+ VMBUS_CHANPKT_SETLEN(pkt.cp_hdr.cph_hlen, hlen);
+ VMBUS_CHANPKT_SETLEN(pkt.cp_hdr.cph_tlen, pad_pktlen);
+ pkt.cp_hdr.cph_xactid = xactid;
+ pkt.cp_rsvd = 0;
+ pkt.cp_range_cnt = 1;
+
+ iov[0].iov_base = &pkt;
+ iov[0].iov_len = sizeof(pkt);
+ iov[1].iov_base = prp;
+ iov[1].iov_len = __offsetof(struct vmbus_gpa_range, gpa_page[prp_cnt]);
+ iov[2].iov_base = data;
+ iov[2].iov_len = dlen;
+ iov[3].iov_base = &pad;
+ iov[3].iov_len = pad_pktlen - pktlen;
+
+ error = hv_ring_buffer_write(&chan->outbound, iov, 4, &send_evt);
+ if (!error && send_evt)
+ vmbus_chan_signal_tx(chan);
+ return error;
+}
+
+int
+vmbus_chan_recv(struct hv_vmbus_channel *chan, void *data, int *dlen0,
+ uint64_t *xactid)
+{
+ struct vmbus_chanpkt_hdr pkt;
+ int error, dlen, hlen;
+
+ error = hv_ring_buffer_peek(&chan->inbound, &pkt, sizeof(pkt));
+ if (error)
+ return error;
+
+ hlen = VMBUS_CHANPKT_GETLEN(pkt.cph_hlen);
+ dlen = VMBUS_CHANPKT_GETLEN(pkt.cph_tlen) - hlen;
+
+ if (*dlen0 < dlen) {
+ /* Return the size of this packet's data. */
+ *dlen0 = dlen;
+ return ENOBUFS;
+ }
+
+ *xactid = pkt.cph_xactid;
+ *dlen0 = dlen;
+
+ /* Skip packet header */
+ error = hv_ring_buffer_read(&chan->inbound, data, dlen, hlen);
+ KASSERT(!error, ("hv_ring_buffer_read failed"));
+
+ return 0;
+}
+
+int
+vmbus_chan_recv_pkt(struct hv_vmbus_channel *chan,
+ struct vmbus_chanpkt_hdr *pkt0, int *pktlen0)
+{
+ struct vmbus_chanpkt_hdr pkt;
+ int error, pktlen;
+
+ error = hv_ring_buffer_peek(&chan->inbound, &pkt, sizeof(pkt));
+ if (error)
+ return error;
+
+ pktlen = VMBUS_CHANPKT_GETLEN(pkt.cph_tlen);
+ if (*pktlen0 < pktlen) {
+ /* Return the size of this packet. */
+ *pktlen0 = pktlen;
+ return ENOBUFS;
+ }
+ *pktlen0 = pktlen;
+
+ /* Include packet header */
+ error = hv_ring_buffer_read(&chan->inbound, pkt0, pktlen, 0);
+ KASSERT(!error, ("hv_ring_buffer_read failed"));
+
+ return 0;
+}
+
+static void
+vmbus_chan_task(void *xchan, int pending __unused)
+{
+ struct hv_vmbus_channel *chan = xchan;
+ vmbus_chan_callback_t cb = chan->ch_cb;
+ void *cbarg = chan->ch_cbarg;
+
+ /*
+ * Optimize host to guest signaling by ensuring:
+ * 1. While reading the channel, we disable interrupts from
+ * host.
+ * 2. Ensure that we process all posted messages from the host
+ * before returning from this callback.
+ * 3. Once we return, enable signaling from the host. Once this
+ * state is set we check to see if additional packets are
+ * available to read. In this case we repeat the process.
+ *
+ * NOTE: Interrupt has been disabled in the ISR.
+ */
+ for (;;) {
+ uint32_t left;
+
+ cb(cbarg);
+
+ left = hv_ring_buffer_read_end(&chan->inbound);
+ if (left == 0) {
+ /* No more data in RX bufring; done */
+ break;
+ }
+ hv_ring_buffer_read_begin(&chan->inbound);
+ }
+}
+
+static void
+vmbus_chan_task_nobatch(void *xchan, int pending __unused)
+{
+ struct hv_vmbus_channel *chan = xchan;
+
+ chan->ch_cb(chan->ch_cbarg);
+}
+
+static __inline void
+vmbus_event_flags_proc(struct vmbus_softc *sc, volatile u_long *event_flags,
+ int flag_cnt)
+{
+ int f;
+
+ for (f = 0; f < flag_cnt; ++f) {
+ uint32_t chid_base;
+ u_long flags;
+ int chid_ofs;
+
+ if (event_flags[f] == 0)
+ continue;
+
+ flags = atomic_swap_long(&event_flags[f], 0);
+ chid_base = f << VMBUS_EVTFLAG_SHIFT;
+
+ while ((chid_ofs = ffsl(flags)) != 0) {
+ struct hv_vmbus_channel *chan;
+
+ --chid_ofs; /* NOTE: ffsl is 1-based */
+ flags &= ~(1UL << chid_ofs);
+
+ chan = sc->vmbus_chmap[chid_base + chid_ofs];
+
+ /* if channel is closed or closing */
+ if (chan == NULL || chan->ch_tq == NULL)
+ continue;
+
+ if (chan->ch_flags & VMBUS_CHAN_FLAG_BATCHREAD)
+ hv_ring_buffer_read_begin(&chan->inbound);
+ taskqueue_enqueue(chan->ch_tq, &chan->ch_task);
+ }
+ }
+}
+
+void
+vmbus_event_proc(struct vmbus_softc *sc, int cpu)
+{
+ struct vmbus_evtflags *eventf;
+
+ /*
+ * On Host with Win8 or above, the event page can be checked directly
+ * to get the id of the channel that has the pending interrupt.
+ */
+ eventf = VMBUS_PCPU_GET(sc, event_flags, cpu) + VMBUS_SINT_MESSAGE;
+ vmbus_event_flags_proc(sc, eventf->evt_flags,
+ VMBUS_PCPU_GET(sc, event_flags_cnt, cpu));
+}
+
+void
+vmbus_event_proc_compat(struct vmbus_softc *sc, int cpu)
+{
+ struct vmbus_evtflags *eventf;
+
+ eventf = VMBUS_PCPU_GET(sc, event_flags, cpu) + VMBUS_SINT_MESSAGE;
+ if (atomic_testandclear_long(&eventf->evt_flags[0], 0)) {
+ vmbus_event_flags_proc(sc, sc->vmbus_rx_evtflags,
+ VMBUS_CHAN_MAX_COMPAT >> VMBUS_EVTFLAG_SHIFT);
+ }
+}
+
+static void
+vmbus_chan_update_evtflagcnt(struct vmbus_softc *sc,
+ const struct hv_vmbus_channel *chan)
+{
+ volatile int *flag_cnt_ptr;
+ int flag_cnt;
+
+ flag_cnt = (chan->ch_id / VMBUS_EVTFLAG_LEN) + 1;
+ flag_cnt_ptr = VMBUS_PCPU_PTR(sc, event_flags_cnt, chan->ch_cpuid);
+
+ for (;;) {
+ int old_flag_cnt;
+
+ old_flag_cnt = *flag_cnt_ptr;
+ if (old_flag_cnt >= flag_cnt)
+ break;
+ if (atomic_cmpset_int(flag_cnt_ptr, old_flag_cnt, flag_cnt)) {
+ if (bootverbose) {
+ device_printf(sc->vmbus_dev,
+ "channel%u update cpu%d flag_cnt to %d\n",
+ chan->ch_id, chan->ch_cpuid, flag_cnt);
+ }
+ break;
+ }
+ }
+}
+
+static struct hv_vmbus_channel *
+vmbus_chan_alloc(struct vmbus_softc *sc)
+{
+ struct hv_vmbus_channel *chan;
+
+ chan = malloc(sizeof(*chan), M_DEVBUF, M_WAITOK | M_ZERO);
+
+ chan->ch_monprm = hyperv_dmamem_alloc(bus_get_dma_tag(sc->vmbus_dev),
+ HYPERCALL_PARAM_ALIGN, 0, sizeof(struct hyperv_mon_param),
+ &chan->ch_monprm_dma, BUS_DMA_WAITOK | BUS_DMA_ZERO);
+ if (chan->ch_monprm == NULL) {
+ device_printf(sc->vmbus_dev, "monprm alloc failed\n");
+ free(chan, M_DEVBUF);
+ return NULL;
+ }
+
+ chan->vmbus_sc = sc;
+ mtx_init(&chan->ch_subchan_lock, "vmbus subchan", NULL, MTX_DEF);
+ TAILQ_INIT(&chan->ch_subchans);
+ TASK_INIT(&chan->ch_detach_task, 0, vmbus_chan_detach_task, chan);
+
+ return chan;
+}
+
+static void
+vmbus_chan_free(struct hv_vmbus_channel *chan)
+{
+ /* TODO: assert sub-channel list is empty */
+ /* TODO: asset no longer on the primary channel's sub-channel list */
+ /* TODO: asset no longer on the vmbus channel list */
+ hyperv_dmamem_free(&chan->ch_monprm_dma, chan->ch_monprm);
+ mtx_destroy(&chan->ch_subchan_lock);
+ free(chan, M_DEVBUF);
+}
+
+static int
+vmbus_chan_add(struct hv_vmbus_channel *newchan)
+{
+ struct vmbus_softc *sc = newchan->vmbus_sc;
+ struct hv_vmbus_channel *prichan;
+
+ if (newchan->ch_id == 0) {
+ /*
+ * XXX
+ * Chan0 will neither be processed nor should be offered;
+ * skip it.
+ */
+ device_printf(sc->vmbus_dev, "got chan0 offer, discard\n");
+ return EINVAL;
+ } else if (newchan->ch_id >= VMBUS_CHAN_MAX) {
+ device_printf(sc->vmbus_dev, "invalid chan%u offer\n",
+ newchan->ch_id);
+ return EINVAL;
+ }
+ sc->vmbus_chmap[newchan->ch_id] = newchan;
+
+ if (bootverbose) {
+ device_printf(sc->vmbus_dev, "chan%u subidx%u offer\n",
+ newchan->ch_id, newchan->ch_subidx);
+ }
+
+ mtx_lock(&sc->vmbus_prichan_lock);
+ TAILQ_FOREACH(prichan, &sc->vmbus_prichans, ch_prilink) {
+ /*
+ * Sub-channel will have the same type GUID and instance
+ * GUID as its primary channel.
+ */
+ if (memcmp(&prichan->ch_guid_type, &newchan->ch_guid_type,
+ sizeof(struct hyperv_guid)) == 0 &&
+ memcmp(&prichan->ch_guid_inst, &newchan->ch_guid_inst,
+ sizeof(struct hyperv_guid)) == 0)
+ break;
+ }
+ if (VMBUS_CHAN_ISPRIMARY(newchan)) {
+ if (prichan == NULL) {
+ /* Install the new primary channel */
+ TAILQ_INSERT_TAIL(&sc->vmbus_prichans, newchan,
+ ch_prilink);
+ mtx_unlock(&sc->vmbus_prichan_lock);
+ return 0;
+ } else {
+ mtx_unlock(&sc->vmbus_prichan_lock);
+ device_printf(sc->vmbus_dev, "duplicated primary "
+ "chan%u\n", newchan->ch_id);
+ return EINVAL;
+ }
+ } else { /* Sub-channel */
+ if (prichan == NULL) {
+ mtx_unlock(&sc->vmbus_prichan_lock);
+ device_printf(sc->vmbus_dev, "no primary chan for "
+ "chan%u\n", newchan->ch_id);
+ return EINVAL;
+ }
+ /*
+ * Found the primary channel for this sub-channel and
+ * move on.
+ *
+ * XXX refcnt prichan
+ */
+ }
+ mtx_unlock(&sc->vmbus_prichan_lock);
+
+ /*
+ * This is a sub-channel; link it with the primary channel.
+ */
+ KASSERT(!VMBUS_CHAN_ISPRIMARY(newchan),
+ ("new channel is not sub-channel"));
+ KASSERT(prichan != NULL, ("no primary channel"));
+
+ newchan->ch_prichan = prichan;
+ newchan->ch_dev = prichan->ch_dev;
+
+ mtx_lock(&prichan->ch_subchan_lock);
+ TAILQ_INSERT_TAIL(&prichan->ch_subchans, newchan, ch_sublink);
+ /*
+ * Bump up sub-channel count and notify anyone that is
+ * interested in this sub-channel, after this sub-channel
+ * is setup.
+ */
+ prichan->ch_subchan_cnt++;
+ mtx_unlock(&prichan->ch_subchan_lock);
+ wakeup(prichan);
+
+ return 0;
+}
+
+void
+vmbus_chan_cpu_set(struct hv_vmbus_channel *chan, int cpu)
+{
+ KASSERT(cpu >= 0 && cpu < mp_ncpus, ("invalid cpu %d", cpu));
+
+ if (chan->vmbus_sc->vmbus_version == VMBUS_VERSION_WS2008 ||
+ chan->vmbus_sc->vmbus_version == VMBUS_VERSION_WIN7) {
+ /* Only cpu0 is supported */
+ cpu = 0;
+ }
+
+ chan->ch_cpuid = cpu;
+ chan->ch_vcpuid = VMBUS_PCPU_GET(chan->vmbus_sc, vcpuid, cpu);
+
+ if (bootverbose) {
+ printf("vmbus_chan%u: assigned to cpu%u [vcpu%u]\n",
+ chan->ch_id, chan->ch_cpuid, chan->ch_vcpuid);
+ }
+}
+
+void
+vmbus_chan_cpu_rr(struct hv_vmbus_channel *chan)
+{
+ static uint32_t vmbus_chan_nextcpu;
+ int cpu;
+
+ cpu = atomic_fetchadd_int(&vmbus_chan_nextcpu, 1) % mp_ncpus;
+ vmbus_chan_cpu_set(chan, cpu);
+}
+
+static void
+vmbus_chan_cpu_default(struct hv_vmbus_channel *chan)
+{
+ /*
+ * By default, pin the channel to cpu0. Devices having
+ * special channel-cpu mapping requirement should call
+ * vmbus_chan_cpu_{set,rr}().
+ */
+ vmbus_chan_cpu_set(chan, 0);
+}
+
+static void
+vmbus_chan_msgproc_choffer(struct vmbus_softc *sc,
+ const struct vmbus_message *msg)
+{
+ const struct vmbus_chanmsg_choffer *offer;
+ struct hv_vmbus_channel *chan;
+ int error;
+
+ offer = (const struct vmbus_chanmsg_choffer *)msg->msg_data;
+
+ chan = vmbus_chan_alloc(sc);
+ if (chan == NULL) {
+ device_printf(sc->vmbus_dev, "allocate chan%u failed\n",
+ offer->chm_chanid);
+ return;
+ }
+
+ chan->ch_id = offer->chm_chanid;
+ chan->ch_subidx = offer->chm_subidx;
+ chan->ch_guid_type = offer->chm_chtype;
+ chan->ch_guid_inst = offer->chm_chinst;
+
+ /* Batch reading is on by default */
+ chan->ch_flags |= VMBUS_CHAN_FLAG_BATCHREAD;
+
+ chan->ch_monprm->mp_connid = VMBUS_CONNID_EVENT;
+ if (sc->vmbus_version != VMBUS_VERSION_WS2008)
+ chan->ch_monprm->mp_connid = offer->chm_connid;
+
+ if (offer->chm_flags1 & VMBUS_CHOFFER_FLAG1_HASMNF) {
+ /*
+ * Setup MNF stuffs.
+ */
+ chan->ch_flags |= VMBUS_CHAN_FLAG_HASMNF;
+ chan->ch_montrig_idx = offer->chm_montrig / VMBUS_MONTRIG_LEN;
+ if (chan->ch_montrig_idx >= VMBUS_MONTRIGS_MAX)
+ panic("invalid monitor trigger %u", offer->chm_montrig);
+ chan->ch_montrig_mask =
+ 1 << (offer->chm_montrig % VMBUS_MONTRIG_LEN);
+ }
+
+ /* Select default cpu for this channel. */
+ vmbus_chan_cpu_default(chan);
+
+ error = vmbus_chan_add(chan);
+ if (error) {
+ device_printf(sc->vmbus_dev, "add chan%u failed: %d\n",
+ chan->ch_id, error);
+ vmbus_chan_free(chan);
+ return;
+ }
+
+ if (VMBUS_CHAN_ISPRIMARY(chan)) {
+ /*
+ * Add device for this primary channel.
+ *
+ * NOTE:
+ * Error is ignored here; don't have much to do if error
+ * really happens.
+ */
+ vmbus_add_child(chan);
+ }
+}
+
+/*
+ * XXX pretty broken; need rework.
+ */
+static void
+vmbus_chan_msgproc_chrescind(struct vmbus_softc *sc,
+ const struct vmbus_message *msg)
+{
+ const struct vmbus_chanmsg_chrescind *note;
+ struct hv_vmbus_channel *chan;
+
+ note = (const struct vmbus_chanmsg_chrescind *)msg->msg_data;
+ if (note->chm_chanid > VMBUS_CHAN_MAX) {
+ device_printf(sc->vmbus_dev, "invalid rescinded chan%u\n",
+ note->chm_chanid);
+ return;
+ }
+
+ if (bootverbose) {
+ device_printf(sc->vmbus_dev, "chan%u rescinded\n",
+ note->chm_chanid);
+ }
+
+ chan = sc->vmbus_chmap[note->chm_chanid];
+ if (chan == NULL)
+ return;
+ sc->vmbus_chmap[note->chm_chanid] = NULL;
+
+ taskqueue_enqueue(taskqueue_thread, &chan->ch_detach_task);
+}
+
+static void
+vmbus_chan_detach_task(void *xchan, int pending __unused)
+{
+ struct hv_vmbus_channel *chan = xchan;
+
+ if (VMBUS_CHAN_ISPRIMARY(chan)) {
+ /* Only primary channel owns the device */
+ vmbus_delete_child(chan);
+ /* NOTE: DO NOT free primary channel for now */
+ } else {
+ struct vmbus_softc *sc = chan->vmbus_sc;
+ struct hv_vmbus_channel *pri_chan = chan->ch_prichan;
+ struct vmbus_chanmsg_chfree *req;
+ struct vmbus_msghc *mh;
+ int error;
+
+ mh = vmbus_msghc_get(sc, sizeof(*req));
+ if (mh == NULL) {
+ device_printf(sc->vmbus_dev,
+ "can not get msg hypercall for chfree(chan%u)\n",
+ chan->ch_id);
+ goto remove;
+ }
+
+ req = vmbus_msghc_dataptr(mh);
+ req->chm_hdr.chm_type = VMBUS_CHANMSG_TYPE_CHFREE;
+ req->chm_chanid = chan->ch_id;
+
+ error = vmbus_msghc_exec_noresult(mh);
+ vmbus_msghc_put(sc, mh);
+
+ if (error) {
+ device_printf(sc->vmbus_dev,
+ "chfree(chan%u) failed: %d",
+ chan->ch_id, error);
+ /* NOTE: Move on! */
+ } else {
+ if (bootverbose) {
+ device_printf(sc->vmbus_dev, "chan%u freed\n",
+ chan->ch_id);
+ }
+ }
+remove:
+ mtx_lock(&pri_chan->ch_subchan_lock);
+ TAILQ_REMOVE(&pri_chan->ch_subchans, chan, ch_sublink);
+ KASSERT(pri_chan->ch_subchan_cnt > 0,
+ ("invalid subchan_cnt %d", pri_chan->ch_subchan_cnt));
+ pri_chan->ch_subchan_cnt--;
+ mtx_unlock(&pri_chan->ch_subchan_lock);
+ wakeup(pri_chan);
+
+ vmbus_chan_free(chan);
+ }
+}
+
+/*
+ * Detach all devices and destroy the corresponding primary channels.
+ */
+void
+vmbus_chan_destroy_all(struct vmbus_softc *sc)
+{
+ struct hv_vmbus_channel *chan;
+
+ mtx_lock(&sc->vmbus_prichan_lock);
+ while ((chan = TAILQ_FIRST(&sc->vmbus_prichans)) != NULL) {
+ KASSERT(VMBUS_CHAN_ISPRIMARY(chan), ("not primary channel"));
+ TAILQ_REMOVE(&sc->vmbus_prichans, chan, ch_prilink);
+ mtx_unlock(&sc->vmbus_prichan_lock);
+
+ vmbus_delete_child(chan);
+ vmbus_chan_free(chan);
+
+ mtx_lock(&sc->vmbus_prichan_lock);
+ }
+ bzero(sc->vmbus_chmap,
+ sizeof(struct hv_vmbus_channel *) * VMBUS_CHAN_MAX);
+ mtx_unlock(&sc->vmbus_prichan_lock);
+}
+
+/*
+ * The channel whose vcpu binding is closest to the currect vcpu will
+ * be selected.
+ * If no multi-channel, always select primary channel.
+ */
+struct hv_vmbus_channel *
+vmbus_chan_cpu2chan(struct hv_vmbus_channel *prichan, int cpu)
+{
+ struct hv_vmbus_channel *sel, *chan;
+ uint32_t vcpu, sel_dist;
+
+ KASSERT(cpu >= 0 && cpu < mp_ncpus, ("invalid cpuid %d", cpu));
+ if (TAILQ_EMPTY(&prichan->ch_subchans))
+ return prichan;
+
+ vcpu = VMBUS_PCPU_GET(prichan->vmbus_sc, vcpuid, cpu);
+
+#define CHAN_VCPU_DIST(ch, vcpu) \
+ (((ch)->ch_vcpuid > (vcpu)) ? \
+ ((ch)->ch_vcpuid - (vcpu)) : ((vcpu) - (ch)->ch_vcpuid))
+
+#define CHAN_SELECT(ch) \
+do { \
+ sel = ch; \
+ sel_dist = CHAN_VCPU_DIST(ch, vcpu); \
+} while (0)
+
+ CHAN_SELECT(prichan);
+
+ mtx_lock(&prichan->ch_subchan_lock);
+ TAILQ_FOREACH(chan, &prichan->ch_subchans, ch_sublink) {
+ uint32_t dist;
+
+ KASSERT(chan->ch_stflags & VMBUS_CHAN_ST_OPENED,
+ ("chan%u is not opened", chan->ch_id));
+
+ if (chan->ch_vcpuid == vcpu) {
+ /* Exact match; done */
+ CHAN_SELECT(chan);
+ break;
+ }
+
+ dist = CHAN_VCPU_DIST(chan, vcpu);
+ if (sel_dist <= dist) {
+ /* Far or same distance; skip */
+ continue;
+ }
+
+ /* Select the closer channel. */
+ CHAN_SELECT(chan);
+ }
+ mtx_unlock(&prichan->ch_subchan_lock);
+
+#undef CHAN_SELECT
+#undef CHAN_VCPU_DIST
+
+ return sel;
+}
+
+struct hv_vmbus_channel **
+vmbus_subchan_get(struct hv_vmbus_channel *pri_chan, int subchan_cnt)
+{
+ struct hv_vmbus_channel **ret, *chan;
+ int i;
+
+ ret = malloc(subchan_cnt * sizeof(struct hv_vmbus_channel *), M_TEMP,
+ M_WAITOK);
+
+ mtx_lock(&pri_chan->ch_subchan_lock);
+
+ while (pri_chan->ch_subchan_cnt < subchan_cnt)
+ mtx_sleep(pri_chan, &pri_chan->ch_subchan_lock, 0, "subch", 0);
+
+ i = 0;
+ TAILQ_FOREACH(chan, &pri_chan->ch_subchans, ch_sublink) {
+ /* TODO: refcnt chan */
+ ret[i] = chan;
+
+ ++i;
+ if (i == subchan_cnt)
+ break;
+ }
+ KASSERT(i == subchan_cnt, ("invalid subchan count %d, should be %d",
+ pri_chan->ch_subchan_cnt, subchan_cnt));
+
+ mtx_unlock(&pri_chan->ch_subchan_lock);
+
+ return ret;
+}
+
+void
+vmbus_subchan_rel(struct hv_vmbus_channel **subchan, int subchan_cnt __unused)
+{
+
+ free(subchan, M_TEMP);
+}
+
+void
+vmbus_subchan_drain(struct hv_vmbus_channel *pri_chan)
+{
+ mtx_lock(&pri_chan->ch_subchan_lock);
+ while (pri_chan->ch_subchan_cnt > 0)
+ mtx_sleep(pri_chan, &pri_chan->ch_subchan_lock, 0, "dsubch", 0);
+ mtx_unlock(&pri_chan->ch_subchan_lock);
+}
+
+void
+vmbus_chan_msgproc(struct vmbus_softc *sc, const struct vmbus_message *msg)
+{
+ vmbus_chanmsg_proc_t msg_proc;
+ uint32_t msg_type;
+
+ msg_type = ((const struct vmbus_chanmsg_hdr *)msg->msg_data)->chm_type;
+ KASSERT(msg_type < VMBUS_CHANMSG_TYPE_MAX,
+ ("invalid message type %u", msg_type));
+
+ msg_proc = vmbus_chan_msgprocs[msg_type];
+ if (msg_proc != NULL)
+ msg_proc(sc, msg);
+}
+
+void
+vmbus_chan_set_readbatch(struct hv_vmbus_channel *chan, bool on)
+{
+ if (!on)
+ chan->ch_flags &= ~VMBUS_CHAN_FLAG_BATCHREAD;
+ else
+ chan->ch_flags |= VMBUS_CHAN_FLAG_BATCHREAD;
+}
Index: head/sys/modules/hyperv/vmbus/Makefile
===================================================================
--- head/sys/modules/hyperv/vmbus/Makefile
+++ head/sys/modules/hyperv/vmbus/Makefile
@@ -4,12 +4,12 @@
${.CURDIR}/../../../dev/hyperv/vmbus/${MACHINE_CPUARCH}
KMOD= hv_vmbus
-SRCS= hv_channel.c \
- hv_ring_buffer.c \
+SRCS= hv_ring_buffer.c \
hyperv.c \
hyperv_busdma.c \
hyperv_machdep.c \
vmbus.c \
+ vmbus_chan.c \
vmbus_et.c
SRCS+= acpi_if.h bus_if.h device_if.h opt_acpi.h vmbus_if.h
File Metadata
Details
Attached
Mime Type
text/plain
Expires
Fri, Dec 27, 1:46 AM (11 h, 17 m)
Storage Engine
blob
Storage Format
Raw Data
Storage Handle
15609985
Default Alt Text
D7232.diff (75 KB)
Attached To
Mode
D7232: hyperv/vmbus: Rename laundered vmbus channel code
Attached
Detach File
Event Timeline
Log In to Comment