Page Menu
Home
FreeBSD
Search
Configure Global Search
Log In
Files
F146082478
D8046.id20759.diff
No One
Temporary
Actions
View File
Edit File
Delete File
View Transforms
Subscribe
Mute Notifications
Flag For Later
Award Token
Size
6 KB
Referenced Files
None
Subscribers
None
D8046.id20759.diff
View Options
Index: head/sys/dev/hyperv/netvsc/hv_net_vsc.h
===================================================================
--- head/sys/dev/hyperv/netvsc/hv_net_vsc.h
+++ head/sys/dev/hyperv/netvsc/hv_net_vsc.h
@@ -178,6 +178,7 @@
bus_dma_tag_t hn_tx_data_dtag;
uint64_t hn_csum_assist;
+ int hn_suspended;
int hn_gpa_cnt;
struct vmbus_gpa hn_gpa[NETVSC_PACKET_MAXPAGE];
Index: head/sys/dev/hyperv/netvsc/hv_net_vsc.c
===================================================================
--- head/sys/dev/hyperv/netvsc/hv_net_vsc.c
+++ head/sys/dev/hyperv/netvsc/hv_net_vsc.c
@@ -348,6 +348,16 @@
return (error);
}
sc->hn_flags &= ~HN_FLAG_RXBUF_CONNECTED;
+
+ /*
+ * Wait for the hypervisor to receive this NVS request.
+ */
+ while (!vmbus_chan_tx_empty(sc->hn_prichan))
+ pause("waittx", 1);
+ /*
+ * Linger long enough for NVS to disconnect RXBUF.
+ */
+ pause("lingtx", (200 * hz) / 1000);
}
if (sc->hn_rxbuf_gpadl != 0) {
@@ -389,6 +399,17 @@
return (error);
}
sc->hn_flags &= ~HN_FLAG_CHIM_CONNECTED;
+
+ /*
+ * Wait for the hypervisor to receive this NVS request.
+ */
+ while (!vmbus_chan_tx_empty(sc->hn_prichan))
+ pause("waittx", 1);
+ /*
+ * Linger long enough for NVS to disconnect chimney
+ * sending buffer.
+ */
+ pause("lingtx", (200 * hz) / 1000);
}
if (sc->hn_chim_gpadl != 0) {
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
@@ -348,6 +348,10 @@
static void hn_chan_callback(struct vmbus_channel *chan, void *xrxr);
static void hn_set_ring_inuse(struct hn_softc *, int);
static int hn_synth_attach(struct hn_softc *, int);
+static bool hn_tx_ring_pending(struct hn_tx_ring *);
+static void hn_suspend(struct hn_softc *);
+static void hn_resume(struct hn_softc *);
+static void hn_tx_ring_qflush(struct hn_tx_ring *);
static void hn_nvs_handle_notify(struct hn_softc *sc,
const struct vmbus_chanpkt_hdr *pkt);
@@ -905,6 +909,23 @@
atomic_add_int(&txd->refs, 1);
}
+static bool
+hn_tx_ring_pending(struct hn_tx_ring *txr)
+{
+ bool pending = false;
+
+#ifndef HN_USE_TXDESC_BUFRING
+ mtx_lock_spin(&txr->hn_txlist_spin);
+ if (txr->hn_txdesc_avail != txr->hn_txdesc_cnt)
+ pending = true;
+ mtx_unlock_spin(&txr->hn_txlist_spin);
+#else
+ if (!buf_ring_full(txr->hn_txdesc_br))
+ pending = true;
+#endif
+ return (pending);
+}
+
static __inline void
hn_txeof(struct hn_tx_ring *txr)
{
@@ -1241,6 +1262,9 @@
KASSERT(txr == &sc->hn_tx_ring[0], ("not the first TX ring"));
mtx_assert(&txr->hn_tx_lock, MA_OWNED);
+ if (__predict_false(txr->hn_suspended))
+ return 0;
+
if ((ifp->if_drv_flags & (IFF_DRV_RUNNING | IFF_DRV_OACTIVE)) !=
IFF_DRV_RUNNING)
return 0;
@@ -1627,6 +1651,9 @@
hn_set_lro_lenlim(sc, HN_LRO_LENLIM_MIN(ifp));
#endif
+ if (ifp->if_drv_flags & IFF_DRV_RUNNING)
+ hn_suspend(sc);
+
/* We must remove and add back the device to cause the new
* MTU to take effect. This includes tearing down, but not
* deleting the channel, then bringing it back up.
@@ -1651,7 +1678,9 @@
if (sc->hn_tx_ring[0].hn_chim_size > sc->hn_chim_szmax)
hn_set_chim_size(sc, sc->hn_chim_szmax);
- hn_init_locked(sc);
+ /* All done! Resume now. */
+ if (ifp->if_drv_flags & IFF_DRV_RUNNING)
+ hn_resume(sc);
HN_UNLOCK(sc);
break;
@@ -2984,6 +3013,9 @@
KASSERT(hn_use_if_start == 0,
("hn_xmit is called, when if_start is enabled"));
+ if (__predict_false(txr->hn_suspended))
+ return 0;
+
if ((ifp->if_drv_flags & IFF_DRV_RUNNING) == 0 || txr->hn_oactive)
return 0;
@@ -3070,20 +3102,24 @@
}
static void
+hn_tx_ring_qflush(struct hn_tx_ring *txr)
+{
+ struct mbuf *m;
+
+ mtx_lock(&txr->hn_tx_lock);
+ while ((m = buf_ring_dequeue_sc(txr->hn_mbuf_br)) != NULL)
+ m_freem(m);
+ mtx_unlock(&txr->hn_tx_lock);
+}
+
+static void
hn_xmit_qflush(struct ifnet *ifp)
{
struct hn_softc *sc = ifp->if_softc;
int i;
- for (i = 0; i < sc->hn_tx_ring_inuse; ++i) {
- struct hn_tx_ring *txr = &sc->hn_tx_ring[i];
- struct mbuf *m;
-
- mtx_lock(&txr->hn_tx_lock);
- while ((m = buf_ring_dequeue_sc(txr->hn_mbuf_br)) != NULL)
- m_freem(m);
- mtx_unlock(&txr->hn_tx_lock);
- }
+ for (i = 0; i < sc->hn_tx_ring_inuse; ++i)
+ hn_tx_ring_qflush(&sc->hn_tx_ring[i]);
if_qflush(ifp);
}
@@ -3503,6 +3539,111 @@
}
static void
+hn_rx_drain(struct vmbus_channel *chan)
+{
+
+ while (!vmbus_chan_rx_empty(chan) || !vmbus_chan_tx_empty(chan))
+ pause("waitch", 1);
+ vmbus_chan_intr_drain(chan);
+}
+
+static void
+hn_suspend(struct hn_softc *sc)
+{
+ struct vmbus_channel **subch = NULL;
+ int i, nsubch;
+
+ HN_LOCK_ASSERT(sc);
+
+ /*
+ * Suspend TX.
+ */
+ for (i = 0; i < sc->hn_tx_ring_inuse; ++i) {
+ struct hn_tx_ring *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))
+ pause("hnwtx", 1 /* 1 tick */);
+ }
+
+ /*
+ * Disable RX.
+ */
+ hv_rf_on_close(sc);
+
+ /* Give RNDIS enough time to flush all pending data packets. */
+ pause("waitrx", (200 * hz) / 1000);
+
+ nsubch = sc->hn_rx_ring_inuse - 1;
+ if (nsubch > 0)
+ subch = vmbus_subchan_get(sc->hn_prichan, nsubch);
+
+ /*
+ * Drain RX/TX bufrings and interrupts.
+ */
+ if (subch != NULL) {
+ for (i = 0; i < nsubch; ++i)
+ hn_rx_drain(subch[i]);
+ }
+ hn_rx_drain(sc->hn_prichan);
+
+ if (subch != NULL)
+ vmbus_subchan_rel(subch, nsubch);
+}
+
+static void
+hn_resume(struct hn_softc *sc)
+{
+ struct hn_tx_ring *txr;
+ int i;
+
+ HN_LOCK_ASSERT(sc);
+
+ /*
+ * Re-enable RX.
+ */
+ hv_rf_on_open(sc);
+
+ /*
+ * Make sure to clear suspend status on "all" TX rings,
+ * since hn_tx_ring_inuse can be changed after hn_suspend().
+ */
+ for (i = 0; i < sc->hn_tx_ring_cnt; ++i) {
+ txr = &sc->hn_tx_ring[i];
+
+ mtx_lock(&txr->hn_tx_lock);
+ txr->hn_suspended = 0;
+ mtx_unlock(&txr->hn_tx_lock);
+ }
+
+ if (!hn_use_if_start) {
+ /*
+ * Flush unused drbrs, since hn_tx_ring_inuse may be
+ * reduced.
+ */
+ for (i = sc->hn_tx_ring_inuse; i < sc->hn_tx_ring_cnt; ++i)
+ hn_tx_ring_qflush(&sc->hn_tx_ring[i]);
+ }
+
+ /*
+ * Kick start TX.
+ */
+ for (i = 0; i < sc->hn_tx_ring_inuse; ++i) {
+ txr = &sc->hn_tx_ring[i];
+ /*
+ * Use txeof task, so that any pending oactive can be
+ * cleared properly.
+ */
+ taskqueue_enqueue(txr->hn_tx_taskq, &txr->hn_txeof_task);
+ }
+}
+
+static void
hn_nvs_handle_notify(struct hn_softc *sc, const struct vmbus_chanpkt_hdr *pkt)
{
const struct hn_nvs_hdr *hdr;
File Metadata
Details
Attached
Mime Type
text/plain
Expires
Sat, Feb 28, 4:04 PM (8 h, 5 m)
Storage Engine
blob
Storage Format
Raw Data
Storage Handle
29022703
Default Alt Text
D8046.id20759.diff (6 KB)
Attached To
Mode
D8046: hyperv/hn: Suspend and resume the backend properly upon MTU change
Attached
Detach File
Event Timeline
Log In to Comment