238 lines
9.5 KiB
C
238 lines
9.5 KiB
C
diff --git a/sys/dev/bwn/if_bwn.c b/sys/dev/bwn/if_bwn.c
|
|||
|
|
index 38bf6f5d..7a5b4230 100644
|
||
|
|
--- a/sys/dev/bwn/if_bwn.c
|
||
|
|
+++ b/sys/dev/bwn/if_bwn.c
|
||
|
|
@@ -147,6 +147,7 @@ static int bwn_raw_xmit(struct ieee80211_node *, struct mbuf *,
|
||
|
|
const struct ieee80211_bpf_params *);
|
||
|
|
static void bwn_updateslot(struct ieee80211com *);
|
||
|
|
static void bwn_update_promisc(struct ieee80211com *);
|
||
|
|
+static void bwn_update_mcast(struct ieee80211com *);
|
||
|
|
static void bwn_wme_init(struct bwn_mac *);
|
||
|
|
static int bwn_wme_update(struct ieee80211com *);
|
||
|
|
static void bwn_wme_clear(struct bwn_softc *);
|
||
|
|
@@ -250,6 +251,7 @@ static void bwn_dma_setup(struct bwn_dma_ring *);
|
||
|
|
static void bwn_dma_free_ringmemory(struct bwn_dma_ring *);
|
||
|
|
static void bwn_dma_cleanup(struct bwn_dma_ring *);
|
||
|
|
static void bwn_dma_free_descbufs(struct bwn_dma_ring *);
|
||
|
|
+static void bwn_dma_reclaim_tx(struct bwn_mac *);
|
||
|
|
static int bwn_dma_tx_reset(struct bwn_mac *, uint16_t, int);
|
||
|
|
static void bwn_dma_rx(struct bwn_dma_ring *);
|
||
|
|
static int bwn_dma_rx_reset(struct bwn_mac *, uint16_t, int);
|
||
|
|
@@ -812,6 +814,7 @@ bwn_attach_post(struct bwn_softc *sc)
|
||
|
|
ic->ic_raw_xmit = bwn_raw_xmit;
|
||
|
|
ic->ic_updateslot = bwn_updateslot;
|
||
|
|
ic->ic_update_promisc = bwn_update_promisc;
|
||
|
|
+ ic->ic_update_mcast = bwn_update_mcast;
|
||
|
|
ic->ic_wme.wme_update = bwn_wme_update;
|
||
|
|
ic->ic_scan_start = bwn_scan_start;
|
||
|
|
ic->ic_scan_end = bwn_scan_end;
|
||
|
|
@@ -855,7 +858,6 @@ bwn_detach(device_t dev)
|
||
|
|
BWN_LOCK(sc);
|
||
|
|
bwn_stop(sc);
|
||
|
|
BWN_UNLOCK(sc);
|
||
|
|
- bwn_dma_free(mac);
|
||
|
|
callout_drain(&sc->sc_led_blink_ch);
|
||
|
|
callout_drain(&sc->sc_rfswitch_ch);
|
||
|
|
callout_drain(&sc->sc_task_ch);
|
||
|
|
@@ -863,7 +865,17 @@ bwn_detach(device_t dev)
|
||
|
|
bwn_phy_detach(mac);
|
||
|
|
ieee80211_draintask(ic, &mac->mac_hwreset);
|
||
|
|
ieee80211_draintask(ic, &mac->mac_txpower);
|
||
|
|
+ /*
|
||
|
|
+ * Drop any TX-ring node references BEFORE ieee80211_ifdetach()
|
||
|
|
+ * frees all nodes. bwn_dma_free()/detach would otherwise find a
|
||
|
|
+ * dangling node pointer (the ring slot still holds mt_ni for a
|
||
|
|
+ * frame whose TX completion never ran) and double-free it
|
||
|
|
+ * (vmcore.5). This is independent of mac_status: a frame can be
|
||
|
|
+ * queued in a ring without the MAC ever reaching STARTED.
|
||
|
|
+ */
|
||
|
|
+ bwn_dma_reclaim_tx(mac);
|
||
|
|
ieee80211_ifdetach(ic);
|
||
|
|
+ bwn_dma_free(mac);
|
||
|
|
}
|
||
|
|
taskqueue_drain(sc->sc_tq, &mac->mac_intrtask);
|
||
|
|
taskqueue_free(sc->sc_tq);
|
||
|
|
@@ -1094,7 +1106,12 @@ bwn_pio_tx_start(struct bwn_mac *mac, struct ieee80211_node *ni,
|
||
|
|
tq = bwn_pio_select(mac, M_WME_GETAC(m));
|
||
|
|
KASSERT(!TAILQ_EMPTY(&tq->tq_pktlist), ("%s: fail", __func__));
|
||
|
|
tp = TAILQ_FIRST(&tq->tq_pktlist);
|
||
|
|
- tp->tp_ni = ni;
|
||
|
|
+ /*
|
||
|
|
+ * Take our own reference on the node (see bwn_dma_tx_start for the
|
||
|
|
+ * rationale); the PIO path otherwise shares the same use-after-free
|
||
|
|
+ * hazard as the DMA path.
|
||
|
|
+ */
|
||
|
|
+ tp->tp_ni = ieee80211_ref_node(ni);
|
||
|
|
tp->tp_m = m;
|
||
|
|
|
||
|
|
error = bwn_set_txhdr(mac, ni, m, &txhdr, BWN_PIO_COOKIE(tq, tp));
|
||
|
|
@@ -1218,7 +1235,15 @@ bwn_dma_tx_start(struct bwn_mac *mac, struct ieee80211_node *ni,
|
||
|
|
KASSERT(mt->mt_txtype == BWN_DMADESC_METATYPE_BODY &&
|
||
|
|
mt->mt_islast == 1, ("%s:%d: fail", __func__, __LINE__));
|
||
|
|
mt->mt_m = m;
|
||
|
|
- mt->mt_ni = ni;
|
||
|
|
+ /*
|
||
|
|
+ * Take our own reference on the node. The mbuf's rcvif reference
|
||
|
|
+ * belongs to the net80211 stack and may be released (e.g. when the
|
||
|
|
+ * VAP is torn down or a scan is cancelled) while the frame is still
|
||
|
|
+ * pending in the TX ring. Without our own reference the slot would
|
||
|
|
+ * point at a freed node, and freeing it later (on TX completion or
|
||
|
|
+ * at detach) would double-free / use-after-free and panic.
|
||
|
|
+ */
|
||
|
|
+ mt->mt_ni = ieee80211_ref_node(ni);
|
||
|
|
|
||
|
|
error = bus_dmamap_load_mbuf(dma->txbuf_dtag, mt->mt_dmap, m,
|
||
|
|
bwn_dma_buf_addr, &mt->mt_paddr, BUS_DMA_NOWAIT);
|
||
|
|
@@ -1892,6 +1917,33 @@ bwn_update_promisc(struct ieee80211com *ic)
|
||
|
|
BWN_UNLOCK(sc);
|
||
|
|
}
|
||
|
|
|
||
|
|
+static void
|
||
|
|
+bwn_update_mcast(struct ieee80211com *ic)
|
||
|
|
+{
|
||
|
|
+ struct bwn_softc *sc = ic->ic_softc;
|
||
|
|
+ struct bwn_mac *mac;
|
||
|
|
+
|
||
|
|
+ /*
|
||
|
|
+ * bwn has no hardware multicast hash filter; the only RX filter bit
|
||
|
|
+ * available for multicast is BWN_MACCTL_PROMISC (the same bit used by
|
||
|
|
+ * bwn_update_promisc). When any VAP needs all-multicast (e.g. for
|
||
|
|
+ * IPv6 ND/MLD), set PROMISC so the frames are passed up to net80211,
|
||
|
|
+ * which then does the multicast filtering in software. Without this
|
||
|
|
+ * callback net80211 uses the null stub and prints
|
||
|
|
+ * "need multicast update callback", and IPv6 multicast breaks.
|
||
|
|
+ */
|
||
|
|
+ BWN_LOCK(sc);
|
||
|
|
+ mac = sc->sc_curmac;
|
||
|
|
+ if (mac != NULL && mac->mac_status >= BWN_MAC_STATUS_INITED) {
|
||
|
|
+ if (ic->ic_allmulti > 0)
|
||
|
|
+ sc->sc_filters |= BWN_MACCTL_PROMISC;
|
||
|
|
+ else
|
||
|
|
+ sc->sc_filters &= ~BWN_MACCTL_PROMISC;
|
||
|
|
+ bwn_set_opmode(mac);
|
||
|
|
+ }
|
||
|
|
+ BWN_UNLOCK(sc);
|
||
|
|
+}
|
||
|
|
+
|
||
|
|
/*
|
||
|
|
* Callback from the 802.11 layer to update WME parameters.
|
||
|
|
*/
|
||
|
|
@@ -2999,7 +3051,17 @@ static void
|
||
|
|
bwn_dma_ringfree(struct bwn_dma_ring **dr)
|
||
|
|
{
|
||
|
|
|
||
|
|
- if (dr == NULL)
|
||
|
|
+ /*
|
||
|
|
+ * bwn_dma_attach() sets BWN_MAC_FLAG_DMA early, before the individual
|
||
|
|
+ * rings (rx/wme[]/mcast) are allocated via bwn_dma_ringsetup(). If
|
||
|
|
+ * attach fails partway through (or the device is detached after a
|
||
|
|
+ * partial attach), bwn_dma_free() is still invoked because the flag is
|
||
|
|
+ * set, but the ring pointers are still NULL. Dereferencing them here
|
||
|
|
+ * faults (the crash in bwn_dma_ringfree+0x14c reading at offset 0x30,
|
||
|
|
+ * i.e. (*dr)->dr_mac). Guard against a NULL ring so teardown after a
|
||
|
|
+ * partial attach is safe.
|
||
|
|
+ */
|
||
|
|
+ if (dr == NULL || *dr == NULL)
|
||
|
|
return;
|
||
|
|
|
||
|
|
bwn_dma_free_descbufs(*dr);
|
||
|
|
@@ -3364,8 +3426,18 @@ bwn_dma_free_descbufs(struct bwn_dma_ring *dr)
|
||
|
|
struct bwn_softc *sc = mac->mac_sc;
|
||
|
|
int i;
|
||
|
|
|
||
|
|
- if (!dr->dr_usedslot)
|
||
|
|
- return;
|
||
|
|
+ /*
|
||
|
|
+ * Do NOT skip this loop based on dr_usedslot. dr_usedslot is only an
|
||
|
|
+ * accounting hint for free-slot tracking and is not a reliable
|
||
|
|
+ * indicator of whether a frame (and its node reference) is pending;
|
||
|
|
+ * after a HW reset / ring re-init it can be inconsistent with the
|
||
|
|
+ * actual slot contents. We must unconditionally walk every slot and
|
||
|
|
+ * release any mbuf + node reference still attached, otherwise a
|
||
|
|
+ * dangling node pointer survives to bwn_dma_free()/detach and
|
||
|
|
+ * double-frees the node (vmcore.5). bwn_dma_free_descbuf() is
|
||
|
|
+ * idempotent (NULLs mt_m/mt_ni and skips empty slots), so walking
|
||
|
|
+ * all slots is safe even when none hold a frame.
|
||
|
|
+ */
|
||
|
|
for (i = 0; i < dr->dr_numslots; i++) {
|
||
|
|
dr->getdesc(dr, i, &desc, &meta);
|
||
|
|
|
||
|
|
@@ -3388,6 +3460,31 @@ bwn_dma_free_descbufs(struct bwn_dma_ring *dr)
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
+static void
|
||
|
|
+bwn_dma_reclaim_tx(struct bwn_mac *mac)
|
||
|
|
+{
|
||
|
|
+ struct bwn_dma *dma;
|
||
|
|
+
|
||
|
|
+ if ((mac->mac_flags & BWN_MAC_FLAG_DMA) == 0)
|
||
|
|
+ return;
|
||
|
|
+ dma = &mac->mac_method.dma;
|
||
|
|
+
|
||
|
|
+ /*
|
||
|
|
+ * Drop the mbuf + ieee80211_node references for any frame still
|
||
|
|
+ * pending in the TX rings. This must be called BEFORE
|
||
|
|
+ * ieee80211_ifdetach() (which frees all nodes) and is safe to
|
||
|
|
+ * call even if the MAC was never fully started, because a
|
||
|
|
+ * frame can be queued in a ring without the MAC reaching
|
||
|
|
+ * STARTED (e.g. the watchdog fired right after a transmit).
|
||
|
|
+ * See Fix 6/7 and vmcore.5 (node double-free) / vmcore.4.
|
||
|
|
+ */
|
||
|
|
+ bwn_dma_free_descbufs(dma->wme[WME_AC_BK]);
|
||
|
|
+ bwn_dma_free_descbufs(dma->wme[WME_AC_BE]);
|
||
|
|
+ bwn_dma_free_descbufs(dma->wme[WME_AC_VI]);
|
||
|
|
+ bwn_dma_free_descbufs(dma->wme[WME_AC_VO]);
|
||
|
|
+ bwn_dma_free_descbufs(dma->mcast);
|
||
|
|
+}
|
||
|
|
+
|
||
|
|
static int
|
||
|
|
bwn_dma_tx_reset(struct bwn_mac *mac, uint16_t base,
|
||
|
|
int type)
|
||
|
|
@@ -7349,6 +7446,45 @@ bwn_dma_stop(struct bwn_mac *mac)
|
||
|
|
bwn_dma_ringstop(&dma->wme[WME_AC_VI]);
|
||
|
|
bwn_dma_ringstop(&dma->wme[WME_AC_VO]);
|
||
|
|
bwn_dma_ringstop(&dma->mcast);
|
||
|
|
+
|
||
|
|
+ /*
|
||
|
|
+ * Reclaim any frames still pending in the DMA rings.
|
||
|
|
+ *
|
||
|
|
+ * When TX never completes (e.g. interrupts are not delivered, which
|
||
|
|
+ * on this chip manifests as "device timeout"), the ring slots keep
|
||
|
|
+ * their mbuf + ieee80211_node references until a TX-completion
|
||
|
|
+ * callback (bwn_dma_handle_txeof) runs. bwn_dma_handle_txeof is the
|
||
|
|
+ * ONLY place that clears mt_ni, so without it the references linger
|
||
|
|
+ * here. If net80211 later frees that node (scan cancel / VAP
|
||
|
|
+ * teardown) while we still point at it, the reference becomes
|
||
|
|
+ * dangling; then bwn_dma_free() -> bwn_dma_ringfree() ->
|
||
|
|
+ * bwn_dma_free_descbufs() -> ieee80211_free_node() walks freed
|
||
|
|
+ * memory and panics (vmcore.4: page fault in node_cleanup /
|
||
|
|
+ * ieee80211_ageq_remove).
|
||
|
|
+ *
|
||
|
|
+ * Releasing the pending frames here, at device-stop time, drops the
|
||
|
|
+ * node references while the node is still alive (this runs before
|
||
|
|
+ * ieee80211_ifdetach() during detach and during every HW reset),
|
||
|
|
+ * so no dangling reference survives to detach. bwn_dma_free_descbufs
|
||
|
|
+ * is idempotent (it NULLs mt_m/mt_ni), so the later free at detach
|
||
|
|
+ * simply finds empty slots and skips them.
|
||
|
|
+ *
|
||
|
|
+ * IMPORTANT: only the TX rings (wme[] + mcast) are reclaimed here.
|
||
|
|
+ * The RX ring MUST NOT be reclaimed at stop time: bwn_dma_stop() runs
|
||
|
|
+ * on every HW reset (every "device timeout"), and bwn_dma_free_descbufs()
|
||
|
|
+ * frees the RX mbuf WITHOUT clearing the DMA descriptor's physical
|
||
|
|
+ * address. The RX ring is persistent (it is refilled in place by
|
||
|
|
+ * bwn_rxeof() -> bwn_dma_newbuf(), never re-allocated), so after a
|
||
|
|
+ * reset the descriptor would still point at freed memory and the NIC
|
||
|
|
+ * would DMA received frames into freed kernel memory -> heap
|
||
|
|
+ * corruption (vmcore.6: TCP sb_mb NULL while sb_ccc != 0). Reclaiming
|
||
|
|
+ * the RX ring here also bought nothing: the RX ring holds no node
|
||
|
|
+ * reference, so it cannot cause the node double-free this fix targets.
|
||
|
|
+ *
|
||
|
|
+ * The actual TX-ring reclaim is factored into bwn_dma_reclaim_tx()
|
||
|
|
+ * so it can also be called from bwn_detach() before ieee80211_ifdetach().
|
||
|
|
+ */
|
||
|
|
+ bwn_dma_reclaim_tx(mac);
|
||
|
|
}
|
||
|
|
|
||
|
|
static void
|