The iwx(4) driver in -current now contains a workaround for Tx queues
which get stuck while other Tx queues keep working. This condition
now triggers a device timeout on iwx(4) rather than leaving interface
in a semi-operational state.
Here is a corresponding patch for iwm(4).
The Linux driver applies the same workaround to iwm(4) devices, too.
ok?
diff 1499064ecae4f3d85eb41954a6cc78779c4f2d6f
4f32e28bcce503a57882fd48c65707579cc9a3fb
blob - 0fd8f7e46184c72d5a840a24f0da8d6a416f9452
blob + 8a14c3c5e5c08681fa02831bee80dc040bab6612
--- sys/dev/pci/if_iwx.c
+++ sys/dev/pci/if_iwx.c
@@ -4552,8 +4552,6 @@ iwx_rx_tx_cmd(struct iwx_softc *sc, struct iwx_rx_pack
bus_dmamap_sync(sc->sc_dmat, data->map, 0, IWX_RBUF_SIZE,
BUS_DMASYNC_POSTREAD);
- sc->sc_tx_timer = 0;
-
/* Sanity checks. */
if (sizeof(*tx_resp) > len)
return;
@@ -4563,6 +4561,8 @@ iwx_rx_tx_cmd(struct iwx_softc *sc, struct iwx_rx_pack
tx_resp->frame_count * sizeof(tx_resp->status) > len)
return;
+ sc->sc_tx_timer[qid] = 0;
+
if (tx_resp->frame_count > 1) /* A-MPDU */
return;
@@ -4658,7 +4658,7 @@ iwx_rx_compressed_ba(struct iwx_softc *sc, struct iwx_
idx = le16toh(ba_tfd->tfd_index);
if (idx >= IWX_TX_RING_COUNT)
continue;
- sc->sc_tx_timer = 0;
+ sc->sc_tx_timer[qid] = 0;
iwx_txq_advance(sc, ring, idx);
iwx_clear_oactive(sc, ring);
}
@@ -5433,6 +5433,9 @@ iwx_tx(struct iwx_softc *sc, struct mbuf *m, struct ie
sc->qfullmsk |= 1 << ring->qid;
}
+ if (ic->ic_if.if_flags & IFF_UP)
+ sc->sc_tx_timer[ring->qid] = 15;
+
return 0;
}
@@ -7973,10 +7976,8 @@ iwx_start(struct ifnet *ifp)
continue;
}
- if (ifp->if_flags & IFF_UP) {
- sc->sc_tx_timer = 15;
+ if (ifp->if_flags & IFF_UP)
ifp->if_timer = 1;
- }
}
return;
@@ -8046,7 +8047,8 @@ iwx_stop(struct ifnet *ifp)
struct iwx_rxba_data *rxba = &sc->sc_rxba_data[i];
iwx_clear_reorder_buffer(sc, rxba);
}
- ifp->if_timer = sc->sc_tx_timer = 0;
+ memset(sc->sc_tx_timer, 0, sizeof(sc->sc_tx_timer));
+ ifp->if_timer = 0;
splx(s);
}
@@ -8055,21 +8057,30 @@ void
iwx_watchdog(struct ifnet *ifp)
{
struct iwx_softc *sc = ifp->if_softc;
+ int i;
ifp->if_timer = 0;
- if (sc->sc_tx_timer > 0) {
- if (--sc->sc_tx_timer == 0) {
- printf("%s: device timeout\n", DEVNAME(sc));
- if (ifp->if_flags & IFF_DEBUG) {
- iwx_nic_error(sc);
- iwx_dump_driver_status(sc);
+
+ /*
+ * We maintain a separate timer for each Tx queue because
+ * Tx aggregation queues can get "stuck" while other queues
+ * keep working. The Linux driver uses a similar workaround.
+ */
+ for (i = 0; i < nitems(sc->sc_tx_timer); i++) {
+ if (sc->sc_tx_timer[i] > 0) {
+ if (--sc->sc_tx_timer[i] == 0) {
+ printf("%s: device timeout\n", DEVNAME(sc));
+ if (ifp->if_flags & IFF_DEBUG) {
+ iwx_nic_error(sc);
+ iwx_dump_driver_status(sc);
+ }
+ if ((sc->sc_flags & IWX_FLAG_SHUTDOWN) == 0)
+ task_add(systq, &sc->init_task);
+ ifp->if_oerrors++;
+ return;
}
- if ((sc->sc_flags & IWX_FLAG_SHUTDOWN) == 0)
- task_add(systq, &sc->init_task);
- ifp->if_oerrors++;
- return;
+ ifp->if_timer = 1;
}
- ifp->if_timer = 1;
}
ieee80211_watchdog(ifp);
blob - 719d9b6295b9eb9c31b36a98bcb0259a8a2f4cf3
blob + 50d9e6346079be78b020a0d43a3c755e5945f1d3
--- sys/dev/pci/if_iwxvar.h
+++ sys/dev/pci/if_iwxvar.h
@@ -563,7 +563,7 @@ struct iwx_softc {
struct iwx_nvm_data sc_nvm;
struct iwx_bf_data sc_bf;
- int sc_tx_timer;
+ int sc_tx_timer[IWX_NUM_TX_QUEUES];
int sc_rx_ba_sessions;
int sc_scan_last_antenna;