summaryrefslogtreecommitdiffstats
path: root/sys/dev/iwi
diff options
context:
space:
mode:
authordamien <damien@FreeBSD.org>2005-11-13 17:16:06 +0000
committerdamien <damien@FreeBSD.org>2005-11-13 17:16:06 +0000
commit47b715731074f364ae6467afa622d3cd4152f564 (patch)
tree9cf8299de4763b8b9c5bb31693d81aa3d22ac617 /sys/dev/iwi
parent44b90b5b5f9dec2551c85299eae2e4db00adce10 (diff)
downloadFreeBSD-src-47b715731074f364ae6467afa622d3cd4152f564.zip
FreeBSD-src-47b715731074f364ae6467afa622d3cd4152f564.tar.gz
Be more robust when handling Rx interrupts. If we can't allocate and DMA map
a new mbuf, just discard the received frame and reuse the old mbuf. This should fix kernel panics on high network traffic. Obtained from: NetBSD (joerg@) MFC after: 2 weeks
Diffstat (limited to 'sys/dev/iwi')
-rw-r--r--sys/dev/iwi/if_iwi.c60
1 files changed, 40 insertions, 20 deletions
diff --git a/sys/dev/iwi/if_iwi.c b/sys/dev/iwi/if_iwi.c
index be302b4..0d28515 100644
--- a/sys/dev/iwi/if_iwi.c
+++ b/sys/dev/iwi/if_iwi.c
@@ -1158,7 +1158,7 @@ iwi_frame_intr(struct iwi_softc *sc, struct iwi_rx_data *data, int i,
{
struct ieee80211com *ic = &sc->sc_ic;
struct ifnet *ifp = ic->ic_ifp;
- struct mbuf *m;
+ struct mbuf *mnew, *m;
struct ieee80211_frame *wh;
struct ieee80211_node *ni;
int error;
@@ -1169,10 +1169,48 @@ iwi_frame_intr(struct iwi_softc *sc, struct iwi_rx_data *data, int i,
if (le16toh(frame->len) < sizeof (struct ieee80211_frame))
return;
+ /*
+ * Try to allocate a new mbuf for this ring element and load it before
+ * processing the current mbuf. If the ring element cannot be loaded,
+ * drop the received packet and reuse the old mbuf. In the unlikely
+ * case that the old mbuf can't be reloaded either, explicitly panic.
+ */
+ mnew = m_getcl(M_DONTWAIT, MT_DATA, M_PKTHDR);
+ if (mnew == NULL) {
+ ifp->if_ierrors++;
+ return;
+ }
+
bus_dmamap_unload(sc->rxq.data_dmat, data->map);
- /* finalize mbuf */
+ error = bus_dmamap_load(sc->rxq.data_dmat, data->map,
+ mtod(mnew, void *), MCLBYTES, iwi_dma_map_addr, &data->physaddr,
+ 0);
+ if (error != 0) {
+ m_freem(mnew);
+
+ /* try to reload the old mbuf */
+ error = bus_dmamap_load(sc->rxq.data_dmat, data->map,
+ mtod(data->m, void *), MCLBYTES, iwi_dma_map_addr,
+ &data->physaddr, 0);
+ if (error != 0) {
+ /* very unlikely that it will fail... */
+ panic("%s: could not load old rx mbuf",
+ device_get_name(sc->sc_dev));
+ }
+ ifp->if_ierrors++;
+ return;
+ }
+
+ /*
+ * New mbuf successfully loaded, update Rx ring and continue
+ * processing.
+ */
m = data->m;
+ data->m = mnew;
+ CSR_WRITE_4(sc, data->reg, data->physaddr);
+
+ /* finalize mbuf */
m->m_pkthdr.rcvif = ifp;
m->m_pkthdr.len = m->m_len = sizeof (struct iwi_hdr) +
sizeof (struct iwi_frame) + le16toh(frame->len);
@@ -1205,24 +1243,6 @@ iwi_frame_intr(struct iwi_softc *sc, struct iwi_rx_data *data, int i,
/* node is no longer needed */
ieee80211_free_node(ni);
-
- data->m = m_getcl(M_DONTWAIT, MT_DATA, M_PKTHDR);
- if (data->m == NULL) {
- device_printf(sc->sc_dev, "could not allocate rx mbuf\n");
- return;
- }
-
- error = bus_dmamap_load(sc->rxq.data_dmat, data->map,
- mtod(data->m, void *), MCLBYTES, iwi_dma_map_addr, &data->physaddr,
- 0);
- if (error != 0) {
- device_printf(sc->sc_dev, "could not load rx buf DMA map\n");
- m_freem(data->m);
- data->m = NULL;
- return;
- }
-
- CSR_WRITE_4(sc, data->reg, data->physaddr);
}
static void
OpenPOWER on IntegriCloud