hyperv/hn: Fix primary channel revocation
Since hypervisor will not drain the TX bufring, once the channels are revoked: - Setup vmbus orphan handler properly. - Make sure that suspension will not wait the TX bufring draining forever. - GC the pending TX descs on detach path, before freeing the busdma stuffs. MFC after: 1 week Sponsored by: Microsoft Differential Revision: https://reviews.freebsd.org/D8559
This commit is contained in:
parent
24de97b49a
commit
25641fc705
@ -336,8 +336,13 @@ hn_nvs_disconn_rxbuf(struct hn_softc *sc)
|
||||
|
||||
/*
|
||||
* Wait for the hypervisor to receive this NVS request.
|
||||
*
|
||||
* NOTE:
|
||||
* The TX bufring will not be drained by the hypervisor,
|
||||
* if the primary channel is revoked.
|
||||
*/
|
||||
while (!vmbus_chan_tx_empty(sc->hn_prichan))
|
||||
while (!vmbus_chan_tx_empty(sc->hn_prichan) &&
|
||||
!vmbus_chan_is_revoked(sc->hn_prichan))
|
||||
pause("waittx", 1);
|
||||
/*
|
||||
* Linger long enough for NVS to disconnect RXBUF.
|
||||
@ -387,8 +392,13 @@ hn_nvs_disconn_chim(struct hn_softc *sc)
|
||||
|
||||
/*
|
||||
* Wait for the hypervisor to receive this NVS request.
|
||||
*
|
||||
* NOTE:
|
||||
* The TX bufring will not be drained by the hypervisor,
|
||||
* if the primary channel is revoked.
|
||||
*/
|
||||
while (!vmbus_chan_tx_empty(sc->hn_prichan))
|
||||
while (!vmbus_chan_tx_empty(sc->hn_prichan) &&
|
||||
!vmbus_chan_is_revoked(sc->hn_prichan))
|
||||
pause("waittx", 1);
|
||||
/*
|
||||
* Linger long enough for NVS to disconnect chimney
|
||||
|
@ -303,7 +303,8 @@ static void hn_resume(struct hn_softc *);
|
||||
static void hn_resume_data(struct hn_softc *);
|
||||
static void hn_resume_mgmt(struct hn_softc *);
|
||||
static void hn_suspend_mgmt_taskfunc(void *, int);
|
||||
static void hn_chan_drain(struct vmbus_channel *);
|
||||
static void hn_chan_drain(struct hn_softc *,
|
||||
struct vmbus_channel *);
|
||||
|
||||
static void hn_update_link_status(struct hn_softc *);
|
||||
static void hn_change_network(struct hn_softc *);
|
||||
@ -327,6 +328,8 @@ static int hn_create_tx_data(struct hn_softc *, int);
|
||||
static void hn_fixup_tx_data(struct hn_softc *);
|
||||
static void hn_destroy_tx_data(struct hn_softc *);
|
||||
static void hn_txdesc_dmamap_destroy(struct hn_txdesc *);
|
||||
static void hn_txdesc_gc(struct hn_tx_ring *,
|
||||
struct hn_txdesc *);
|
||||
static int hn_encap(struct ifnet *, struct hn_tx_ring *,
|
||||
struct hn_txdesc *, struct mbuf **);
|
||||
static int hn_txpkt(struct ifnet *, struct hn_tx_ring *,
|
||||
@ -994,8 +997,25 @@ hn_attach(device_t dev)
|
||||
*/
|
||||
sc->hn_xact = vmbus_xact_ctx_create(bus_get_dma_tag(dev),
|
||||
HN_XACT_REQ_SIZE, HN_XACT_RESP_SIZE, 0);
|
||||
if (sc->hn_xact == NULL)
|
||||
if (sc->hn_xact == NULL) {
|
||||
error = ENXIO;
|
||||
goto failed;
|
||||
}
|
||||
|
||||
/*
|
||||
* Install orphan handler for the revocation of this device's
|
||||
* primary channel.
|
||||
*
|
||||
* NOTE:
|
||||
* The processing order is critical here:
|
||||
* Install the orphan handler, _before_ testing whether this
|
||||
* device's primary channel has been revoked or not.
|
||||
*/
|
||||
vmbus_chan_set_orphan(sc->hn_prichan, sc->hn_xact);
|
||||
if (vmbus_chan_is_revoked(sc->hn_prichan)) {
|
||||
error = ENXIO;
|
||||
goto failed;
|
||||
}
|
||||
|
||||
/*
|
||||
* Attach the synthetic parts, i.e. NVS and RNDIS.
|
||||
@ -1170,6 +1190,14 @@ hn_detach(device_t dev)
|
||||
struct hn_softc *sc = device_get_softc(dev);
|
||||
struct ifnet *ifp = sc->hn_ifp;
|
||||
|
||||
if (sc->hn_xact != NULL && vmbus_chan_is_revoked(sc->hn_prichan)) {
|
||||
/*
|
||||
* In case that the vmbus missed the orphan handler
|
||||
* installation.
|
||||
*/
|
||||
vmbus_xact_ctx_orphan(sc->hn_xact);
|
||||
}
|
||||
|
||||
if (device_is_attached(dev)) {
|
||||
HN_LOCK(sc);
|
||||
if (sc->hn_flags & HN_FLAG_SYNTH_ATTACHED) {
|
||||
@ -1195,8 +1223,14 @@ hn_detach(device_t dev)
|
||||
taskqueue_free(sc->hn_tx_taskq);
|
||||
taskqueue_free(sc->hn_mgmt_taskq0);
|
||||
|
||||
if (sc->hn_xact != NULL)
|
||||
if (sc->hn_xact != NULL) {
|
||||
/*
|
||||
* Uninstall the orphan handler _before_ the xact is
|
||||
* destructed.
|
||||
*/
|
||||
vmbus_chan_unset_orphan(sc->hn_prichan);
|
||||
vmbus_xact_ctx_destroy(sc->hn_xact);
|
||||
}
|
||||
|
||||
if_free(ifp);
|
||||
|
||||
@ -1435,7 +1469,7 @@ hn_txdesc_hold(struct hn_txdesc *txd)
|
||||
{
|
||||
|
||||
/* 0->1 transition will never work */
|
||||
KASSERT(txd->refs > 0, ("invalid refs %d", txd->refs));
|
||||
KASSERT(txd->refs > 0, ("invalid txd refs %d", txd->refs));
|
||||
atomic_add_int(&txd->refs, 1);
|
||||
}
|
||||
|
||||
@ -3448,25 +3482,44 @@ hn_txdesc_dmamap_destroy(struct hn_txdesc *txd)
|
||||
bus_dmamap_destroy(txr->hn_tx_data_dtag, txd->data_dmap);
|
||||
}
|
||||
|
||||
static void
|
||||
hn_txdesc_gc(struct hn_tx_ring *txr, struct hn_txdesc *txd)
|
||||
{
|
||||
|
||||
KASSERT(txd->refs == 0 || txd->refs == 1,
|
||||
("invalid txd refs %d", txd->refs));
|
||||
|
||||
/* Aggregated txds will be freed by their aggregating txd. */
|
||||
if (txd->refs > 0 && (txd->flags & HN_TXD_FLAG_ONAGG) == 0) {
|
||||
int freed;
|
||||
|
||||
freed = hn_txdesc_put(txr, txd);
|
||||
KASSERT(freed, ("can't free txdesc"));
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
hn_tx_ring_destroy(struct hn_tx_ring *txr)
|
||||
{
|
||||
struct hn_txdesc *txd;
|
||||
int i;
|
||||
|
||||
if (txr->hn_txdesc == NULL)
|
||||
return;
|
||||
|
||||
#ifndef HN_USE_TXDESC_BUFRING
|
||||
while ((txd = SLIST_FIRST(&txr->hn_txlist)) != NULL) {
|
||||
SLIST_REMOVE_HEAD(&txr->hn_txlist, link);
|
||||
hn_txdesc_dmamap_destroy(txd);
|
||||
}
|
||||
#else
|
||||
mtx_lock(&txr->hn_tx_lock);
|
||||
while ((txd = buf_ring_dequeue_sc(txr->hn_txdesc_br)) != NULL)
|
||||
hn_txdesc_dmamap_destroy(txd);
|
||||
mtx_unlock(&txr->hn_tx_lock);
|
||||
#endif
|
||||
/*
|
||||
* NOTE:
|
||||
* Because the freeing of aggregated txds will be deferred
|
||||
* to the aggregating txd, two passes are used here:
|
||||
* - The first pass GCes any pending txds. This GC is necessary,
|
||||
* since if the channels are revoked, hypervisor will not
|
||||
* deliver send-done for all pending txds.
|
||||
* - The second pass frees the busdma stuffs, i.e. after all txds
|
||||
* were freed.
|
||||
*/
|
||||
for (i = 0; i < txr->hn_txdesc_cnt; ++i)
|
||||
hn_txdesc_gc(txr, &txr->hn_txdesc[i]);
|
||||
for (i = 0; i < txr->hn_txdesc_cnt; ++i)
|
||||
hn_txdesc_dmamap_destroy(&txr->hn_txdesc[i]);
|
||||
|
||||
if (txr->hn_tx_data_dtag != NULL)
|
||||
bus_dma_tag_destroy(txr->hn_tx_data_dtag);
|
||||
@ -4499,10 +4552,17 @@ hn_set_ring_inuse(struct hn_softc *sc, int ring_cnt)
|
||||
}
|
||||
|
||||
static void
|
||||
hn_chan_drain(struct vmbus_channel *chan)
|
||||
hn_chan_drain(struct hn_softc *sc, struct vmbus_channel *chan)
|
||||
{
|
||||
|
||||
while (!vmbus_chan_rx_empty(chan) || !vmbus_chan_tx_empty(chan))
|
||||
/*
|
||||
* NOTE:
|
||||
* The TX bufring will not be drained by the hypervisor,
|
||||
* if the primary channel is revoked.
|
||||
*/
|
||||
while (!vmbus_chan_rx_empty(chan) ||
|
||||
(!vmbus_chan_is_revoked(sc->hn_prichan) &&
|
||||
!vmbus_chan_tx_empty(chan)))
|
||||
pause("waitch", 1);
|
||||
vmbus_chan_intr_drain(chan);
|
||||
}
|
||||
@ -4511,6 +4571,7 @@ static void
|
||||
hn_suspend_data(struct hn_softc *sc)
|
||||
{
|
||||
struct vmbus_channel **subch = NULL;
|
||||
struct hn_tx_ring *txr;
|
||||
int i, nsubch;
|
||||
|
||||
HN_LOCK_ASSERT(sc);
|
||||
@ -4519,19 +4580,23 @@ hn_suspend_data(struct hn_softc *sc)
|
||||
* Suspend TX.
|
||||
*/
|
||||
for (i = 0; i < sc->hn_tx_ring_inuse; ++i) {
|
||||
struct hn_tx_ring *txr = &sc->hn_tx_ring[i];
|
||||
txr = &sc->hn_tx_ring[i];
|
||||
|
||||
mtx_lock(&txr->hn_tx_lock);
|
||||
txr->hn_suspended = 1;
|
||||
mtx_unlock(&txr->hn_tx_lock);
|
||||
/* No one is able send more packets now. */
|
||||
|
||||
/* Wait for all pending sends to finish. */
|
||||
while (hn_tx_ring_pending(txr))
|
||||
/*
|
||||
* Wait for all pending sends to finish.
|
||||
*
|
||||
* NOTE:
|
||||
* We will _not_ receive all pending send-done, if the
|
||||
* primary channel is revoked.
|
||||
*/
|
||||
while (hn_tx_ring_pending(txr) &&
|
||||
!vmbus_chan_is_revoked(sc->hn_prichan))
|
||||
pause("hnwtx", 1 /* 1 tick */);
|
||||
|
||||
taskqueue_drain(txr->hn_tx_taskq, &txr->hn_tx_task);
|
||||
taskqueue_drain(txr->hn_tx_taskq, &txr->hn_txeof_task);
|
||||
}
|
||||
|
||||
/*
|
||||
@ -4554,12 +4619,27 @@ hn_suspend_data(struct hn_softc *sc)
|
||||
|
||||
if (subch != NULL) {
|
||||
for (i = 0; i < nsubch; ++i)
|
||||
hn_chan_drain(subch[i]);
|
||||
hn_chan_drain(sc, subch[i]);
|
||||
}
|
||||
hn_chan_drain(sc->hn_prichan);
|
||||
hn_chan_drain(sc, sc->hn_prichan);
|
||||
|
||||
if (subch != NULL)
|
||||
vmbus_subchan_rel(subch, nsubch);
|
||||
|
||||
/*
|
||||
* Drain any pending TX tasks.
|
||||
*
|
||||
* NOTE:
|
||||
* The above hn_chan_drain() can dispatch TX tasks, so the TX
|
||||
* tasks will have to be drained _after_ the above hn_chan_drain()
|
||||
* calls.
|
||||
*/
|
||||
for (i = 0; i < sc->hn_tx_ring_inuse; ++i) {
|
||||
txr = &sc->hn_tx_ring[i];
|
||||
|
||||
taskqueue_drain(txr->hn_tx_taskq, &txr->hn_tx_task);
|
||||
taskqueue_drain(txr->hn_tx_taskq, &txr->hn_txeof_task);
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
|
Loading…
Reference in New Issue
Block a user