With iwm(4) in monitor mode, tcpdump stops receiving frames after
some time, even though the device is still giving us Rx interrupts.
In this state every incoming frame gets counted as an input error,
and 'systat pool' shows both high INUSE and FAIL counters for mcl4k.
Which means there is an mbuf leak.
In monitor mode a lot of "short" frames get counted as input errors
because the driver has a bad minimum length check (which I will address
in a separate diff; the frames in question are control frames).
So monitor mode makes this very easy to trigger but iwm will also leak
mbufs in some failure cases during regular operation.
The diff below fixes this.
Free mbufs that won't be passed to if_inputm().
diff da97bfb65d45f48449acb2521611e74181fd2eea
c337e8955e3da971f2aaa6e351b64f7047f7d1ea
blob - 61876487764f3a2bf2b262cbd6fb4bb4f78cea59
blob + f10479776ea459653a6219785d7871b46f186fb3
--- sys/dev/pci/if_iwm.c
+++ sys/dev/pci/if_iwm.c
@@ -3992,20 +3992,26 @@ iwm_rx_mpdu(struct iwm_softc *sc, struct mbuf *m, void
if (len < IEEE80211_MIN_LEN) {
ic->ic_stats.is_rx_tooshort++;
IC2IFP(ic)->if_ierrors++;
+ m_freem(m);
return;
}
if (len > maxlen - sizeof(*rx_res)) {
IC2IFP(ic)->if_ierrors++;
+ m_freem(m);
return;
}
- if (__predict_false(phy_info->cfg_phy_cnt > 20))
+ if (__predict_false(phy_info->cfg_phy_cnt > 20)) {
+ m_freem(m);
return;
+ }
rx_pkt_status = le32toh(*(uint32_t *)(pktdata + sizeof(*rx_res) + len));
if (!(rx_pkt_status & IWM_RX_MPDU_RES_STATUS_CRC_OK) ||
- !(rx_pkt_status & IWM_RX_MPDU_RES_STATUS_OVERRUN_OK))
+ !(rx_pkt_status & IWM_RX_MPDU_RES_STATUS_OVERRUN_OK)) {
+ m_freem(m);
return; /* drop */
+ }
m->m_data = pktdata + sizeof(*rx_res);
m->m_pkthdr.len = m->m_len = len;
@@ -4043,17 +4049,21 @@ iwm_rx_mpdu_mq(struct iwm_softc *sc, struct mbuf *m, v
desc = (struct iwm_rx_mpdu_desc *)pktdata;
if (!(desc->status & htole16(IWM_RX_MPDU_RES_STATUS_CRC_OK)) ||
- !(desc->status & htole16(IWM_RX_MPDU_RES_STATUS_OVERRUN_OK)))
+ !(desc->status & htole16(IWM_RX_MPDU_RES_STATUS_OVERRUN_OK))) {
+ m_freem(m);
return; /* drop */
+ }
len = le16toh(desc->mpdu_len);
if (len < IEEE80211_MIN_LEN) {
ic->ic_stats.is_rx_tooshort++;
IC2IFP(ic)->if_ierrors++;
+ m_freem(m);
return;
}
if (len > maxlen - sizeof(*desc)) {
IC2IFP(ic)->if_ierrors++;
+ m_freem(m);
return;
}
@@ -8327,6 +8337,8 @@ iwm_rx_pkt(struct iwm_softc *sc, struct iwm_rx_data *d
m = m_copym(m0, 0, M_COPYALL, M_DONTWAIT);
if (m == NULL) {
ifp->if_ierrors++;
+ m_freem(m0);
+ m0 = NULL;
break;
}
m_adj(m, offset);