I have found the reason for stuck connections over iwm/iwx
which people have been reporting against -current.
My commit which added support for Rx aggregation offload to these
drivers introduced a node leak. In other words, the reference counter
of ic_bss is incremented with every received frame, and there is
no corresponding decrement.
This in turn causes a hang when we try to roam between APs.
Roaming relies on this reference counter to drop to zero when all
Tx queues have been emptied, at which point we trigger the roaming
event knowing that we have no frames left to send towards the old AP.
The reference counter is shared between Rx and Tx and because of the
leak in the Rx code path the counter is stuck on some non-zero value.
The roaming trigger never fires. The only way to recover from this
situation is ifconfig down/up.
This patch fixes the issue.
ok?
diff 2f582782e0cb5e3b9836cc833ce069c1563538c4 /usr/src
blob - 180f46391f127d33779db0cc9946fe93a5fe2dc7
file + sys/dev/pci/if_iwm.c
--- sys/dev/pci/if_iwm.c
+++ sys/dev/pci/if_iwm.c
@@ -4876,7 +4876,6 @@ iwm_rx_reorder(struct iwm_softc *sc, struct mbuf *m, i
type = wh->i_fc[0] & IEEE80211_FC0_TYPE_MASK;
subtype = wh->i_fc[0] & IEEE80211_FC0_SUBTYPE_MASK;
- ni = ieee80211_find_rxnode(ic, wh);
/*
* We are only interested in Block Ack requests and unicast QoS data.
@@ -4918,6 +4917,7 @@ iwm_rx_reorder(struct iwm_softc *sc, struct mbuf *m, i
buffer->valid = 1;
}
+ ni = ieee80211_find_rxnode(ic, wh);
if (type == IEEE80211_FC0_TYPE_CTL &&
subtype == IEEE80211_FC0_SUBTYPE_BAR) {
iwm_release_frames(sc, ni, rxba, buffer, nssn, ml);
@@ -4958,6 +4958,7 @@ iwm_rx_reorder(struct iwm_softc *sc, struct mbuf *m, i
if (iwm_is_sn_less(buffer->head_sn, nssn, buffer->buf_size) &&
(!is_amsdu || last_subframe))
buffer->head_sn = nssn;
+ ieee80211_release_node(ic, ni);
return 0;
}
@@ -4972,6 +4973,7 @@ iwm_rx_reorder(struct iwm_softc *sc, struct mbuf *m, i
if (!buffer->num_stored && sn == buffer->head_sn) {
if (!is_amsdu || last_subframe)
buffer->head_sn = (buffer->head_sn + 1) & 0xfff;
+ ieee80211_release_node(ic, ni);
return 0;
}
@@ -5027,10 +5029,12 @@ iwm_rx_reorder(struct iwm_softc *sc, struct mbuf *m, i
if (!is_amsdu || last_subframe)
iwm_release_frames(sc, ni, rxba, buffer, nssn, ml);
+ ieee80211_release_node(ic, ni);
return 1;
drop:
m_freem(m);
+ ieee80211_release_node(ic, ni);
return 1;
}
blob - 2c9a2179d4ce0a836d55acfaad8d347ee1da290b
file + sys/dev/pci/if_iwx.c
--- sys/dev/pci/if_iwx.c
+++ sys/dev/pci/if_iwx.c
@@ -3892,7 +3892,6 @@ iwx_rx_reorder(struct iwx_softc *sc, struct mbuf *m, i
type = wh->i_fc[0] & IEEE80211_FC0_TYPE_MASK;
subtype = wh->i_fc[0] & IEEE80211_FC0_SUBTYPE_MASK;
- ni = ieee80211_find_rxnode(ic, wh);
/*
* We are only interested in Block Ack requests and unicast QoS data.
@@ -3934,6 +3933,7 @@ iwx_rx_reorder(struct iwx_softc *sc, struct mbuf *m, i
buffer->valid = 1;
}
+ ni = ieee80211_find_rxnode(ic, wh);
if (type == IEEE80211_FC0_TYPE_CTL &&
subtype == IEEE80211_FC0_SUBTYPE_BAR) {
iwx_release_frames(sc, ni, rxba, buffer, nssn, ml);
@@ -3974,6 +3974,7 @@ iwx_rx_reorder(struct iwx_softc *sc, struct mbuf *m, i
if (iwx_is_sn_less(buffer->head_sn, nssn, buffer->buf_size) &&
(!is_amsdu || last_subframe))
buffer->head_sn = nssn;
+ ieee80211_release_node(ic, ni);
return 0;
}
@@ -3988,6 +3989,7 @@ iwx_rx_reorder(struct iwx_softc *sc, struct mbuf *m, i
if (!buffer->num_stored && sn == buffer->head_sn) {
if (!is_amsdu || last_subframe)
buffer->head_sn = (buffer->head_sn + 1) & 0xfff;
+ ieee80211_release_node(ic, ni);
return 0;
}
@@ -4043,10 +4045,12 @@ iwx_rx_reorder(struct iwx_softc *sc, struct mbuf *m, i
if (!is_amsdu || last_subframe)
iwx_release_frames(sc, ni, rxba, buffer, nssn, ml);
+ ieee80211_release_node(ic, ni);
return 1;
drop:
m_freem(m);
+ ieee80211_release_node(ic, ni);
return 1;
}