Index: head/sys/dev/hyperv/include/hyperv.h =================================================================== --- head/sys/dev/hyperv/include/hyperv.h +++ head/sys/dev/hyperv/include/hyperv.h @@ -717,6 +717,7 @@ struct hv_vmbus_channel ** vmbus_get_subchan(struct hv_vmbus_channel *pri_chan, int subchan_cnt); void vmbus_rel_subchan(struct hv_vmbus_channel **subchan, int subchan_cnt); +void vmbus_drain_subchan(struct hv_vmbus_channel *pri_chan); /** * @brief Get physical address from virtual Index: head/sys/dev/hyperv/netvsc/hv_netvsc_drv_freebsd.c =================================================================== --- head/sys/dev/hyperv/netvsc/hv_netvsc_drv_freebsd.c +++ head/sys/dev/hyperv/netvsc/hv_netvsc_drv_freebsd.c @@ -345,6 +345,7 @@ static void hn_set_tx_chimney_size(struct hn_softc *, int); static void hn_channel_attach(struct hn_softc *, struct hv_vmbus_channel *); static void hn_subchan_attach(struct hn_softc *, struct hv_vmbus_channel *); +static void hn_subchan_setup(struct hn_softc *); static int hn_transmit(struct ifnet *, struct mbuf *); static void hn_xmit_qflush(struct ifnet *); @@ -575,25 +576,8 @@ device_printf(dev, "%d TX ring, %d RX ring\n", sc->hn_tx_ring_inuse, sc->hn_rx_ring_inuse); - if (sc->net_dev->num_channel > 1) { - struct hv_vmbus_channel **subchan; - int subchan_cnt = sc->net_dev->num_channel - 1; - int i; - - /* Wait for sub-channels setup to complete. */ - subchan = vmbus_get_subchan(pri_chan, subchan_cnt); - - /* Attach the sub-channels. */ - for (i = 0; i < subchan_cnt; ++i) { - /* NOTE: Calling order is critical. */ - hn_subchan_attach(sc, subchan[i]); - hv_nv_subchan_attach(subchan[i]); - } - - /* Release the sub-channels */ - vmbus_rel_subchan(subchan, subchan_cnt); - device_printf(dev, "%d sub-channels setup done\n", subchan_cnt); - } + if (sc->net_dev->num_channel > 1) + hn_subchan_setup(sc); #if __FreeBSD_version >= 1100099 if (sc->hn_rx_ring_inuse > 1) { @@ -1620,6 +1604,10 @@ NV_UNLOCK(sc); break; } + + /* Wait for subchannels to be destroyed */ + vmbus_drain_subchan(hn_dev->channel); + error = hv_rf_on_device_add(hn_dev, &device_info, sc->hn_rx_ring_inuse); if (error) { @@ -1628,6 +1616,26 @@ NV_UNLOCK(sc); break; } + KASSERT(sc->hn_rx_ring_cnt == sc->net_dev->num_channel, + ("RX ring count %d and channel count %u mismatch", + sc->hn_rx_ring_cnt, sc->net_dev->num_channel)); + if (sc->net_dev->num_channel > 1) { + int r; + + /* + * Skip the rings on primary channel; they are + * handled by the hv_rf_on_device_add() above. + */ + for (r = 1; r < sc->hn_rx_ring_cnt; ++r) { + sc->hn_rx_ring[r].hn_rx_flags &= + ~HN_RX_FLAG_ATTACHED; + } + for (r = 1; r < sc->hn_tx_ring_cnt; ++r) { + sc->hn_tx_ring[r].hn_tx_flags &= + ~HN_TX_FLAG_ATTACHED; + } + hn_subchan_setup(sc); + } sc->hn_tx_chimney_max = sc->net_dev->send_section_size; if (sc->hn_tx_ring[0].hn_tx_chimney_size > @@ -2980,6 +2988,29 @@ } static void +hn_subchan_setup(struct hn_softc *sc) +{ + struct hv_device *device_ctx = vmbus_get_devctx(sc->hn_dev); + struct hv_vmbus_channel **subchan; + int subchan_cnt = sc->net_dev->num_channel - 1; + int i; + + /* Wait for sub-channels setup to complete. */ + subchan = vmbus_get_subchan(device_ctx->channel, subchan_cnt); + + /* Attach the sub-channels. */ + for (i = 0; i < subchan_cnt; ++i) { + /* NOTE: Calling order is critical. */ + hn_subchan_attach(sc, subchan[i]); + hv_nv_subchan_attach(subchan[i]); + } + + /* Release the sub-channels */ + vmbus_rel_subchan(subchan, subchan_cnt); + if_printf(sc->hn_ifp, "%d sub-channels setup done\n", subchan_cnt); +} + +static void hn_tx_taskq_create(void *arg __unused) { if (!hn_share_tx_taskq) Index: head/sys/dev/hyperv/vmbus/hv_channel_mgmt.c =================================================================== --- head/sys/dev/hyperv/vmbus/hv_channel_mgmt.c +++ head/sys/dev/hyperv/vmbus/hv_channel_mgmt.c @@ -367,6 +367,54 @@ if (HV_VMBUS_CHAN_ISPRIMARY(chan)) { /* Only primary channel owns the hv_device */ hv_vmbus_child_device_unregister(chan->device); + /* NOTE: DO NOT free primary channel for now */ + } else { + struct vmbus_softc *sc = chan->vmbus_sc; + struct hv_vmbus_channel *pri_chan = chan->primary_channel; + 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->offer_msg.child_rel_id); + goto remove; + } + + req = vmbus_msghc_dataptr(mh); + req->chm_hdr.chm_type = VMBUS_CHANMSG_TYPE_CHFREE; + req->chm_chanid = chan->offer_msg.child_rel_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->offer_msg.child_rel_id, error); + /* NOTE: Move on! */ + } else { + if (bootverbose) { + device_printf(sc->vmbus_dev, "chan%u freed\n", + chan->offer_msg.child_rel_id); + } + } +remove: + mtx_lock(&sc->vmbus_chlist_lock); + TAILQ_REMOVE(&sc->vmbus_chlist, chan, ch_link); + mtx_unlock(&sc->vmbus_chlist_lock); + + mtx_lock(&pri_chan->sc_lock); + TAILQ_REMOVE(&pri_chan->sc_list_anchor, chan, sc_list_entry); + KASSERT(pri_chan->subchan_cnt > 0, + ("invalid subchan_cnt %d", pri_chan->subchan_cnt)); + pri_chan->subchan_cnt--; + mtx_unlock(&pri_chan->sc_lock); + wakeup(pri_chan); + + hv_vmbus_free_vmbus_channel(chan); } } @@ -504,6 +552,15 @@ } void +vmbus_drain_subchan(struct hv_vmbus_channel *pri_chan) +{ + mtx_lock(&pri_chan->sc_lock); + while (pri_chan->subchan_cnt > 0) + mtx_sleep(pri_chan, &pri_chan->sc_lock, 0, "dsubch", 0); + mtx_unlock(&pri_chan->sc_lock); +} + +void vmbus_chan_msgproc(struct vmbus_softc *sc, const struct vmbus_message *msg) { vmbus_chanmsg_proc_t msg_proc; Index: head/sys/dev/hyperv/vmbus/vmbus_reg.h =================================================================== --- head/sys/dev/hyperv/vmbus/vmbus_reg.h +++ head/sys/dev/hyperv/vmbus/vmbus_reg.h @@ -102,6 +102,7 @@ #define VMBUS_CHANMSG_TYPE_GPADL_CONNRESP 10 /* RESP */ #define VMBUS_CHANMSG_TYPE_GPADL_DISCONN 11 /* REQ */ #define VMBUS_CHANMSG_TYPE_GPADL_DISCONNRESP 12 /* RESP */ +#define VMBUS_CHANMSG_TYPE_CHFREE 13 /* REQ */ #define VMBUS_CHANMSG_TYPE_CONNECT 14 /* REQ */ #define VMBUS_CHANMSG_TYPE_CONNECT_RESP 15 /* RESP */ #define VMBUS_CHANMSG_TYPE_DISCONNECT 16 /* REQ */ @@ -206,4 +207,10 @@ uint32_t chm_gpadl; } __packed; +/* VMBUS_CHANMSG_TYPE_CHFREE */ +struct vmbus_chanmsg_chfree { + struct vmbus_chanmsg_hdr chm_hdr; + uint32_t chm_chanid; +} __packed; + #endif /* !_VMBUS_REG_H_ */