On Fri, Nov 08, 2019 at 07:15:18PM +0200, Stefan Sperling wrote: > On Fri, Nov 08, 2019 at 07:13:17PM +0200, Stefan Sperling wrote: > > Previous versions of this diff included monitor mode support. I have > > stripped > > this out for now and I am looking for stability tests in client mode only. > > If this diff passes through testing this time around, adding monitor mode > > on top will be easy. > > Sorry, I made a mistake when splitting the diff. The diff I sent doesn't > build. > I will post a new one shortly.
Fixed diff. diff aa91687117b06dc9f357920a56ef1a22c0de6a7e 16cec9db4484c7e9e6fa4838707a4c0c7ba2f314 blob - f7449f560623517dfa1b3fbe6d55b10a5494e26c blob + 0d5f766801f75e839476ebc2393568ed31494e62 --- sys/dev/pci/if_iwm.c +++ sys/dev/pci/if_iwm.c @@ -366,8 +366,10 @@ int iwm_get_signal_strength(struct iwm_softc *, struct void iwm_rx_rx_phy_cmd(struct iwm_softc *, struct iwm_rx_packet *, struct iwm_rx_data *); int iwm_get_noise(const struct iwm_statistics_rx_non_phy *); -void iwm_rx_rx_mpdu(struct iwm_softc *, struct iwm_rx_packet *, - struct iwm_rx_data *, struct mbuf_list *); +int iwm_rx_frame(struct iwm_softc *, struct mbuf *, uint32_t, + struct mbuf_list *); +int iwm_ccmp_decap(struct iwm_softc *, struct mbuf *, + struct ieee80211_node *); void iwm_enable_ht_cck_fallback(struct iwm_softc *, struct iwm_node *); void iwm_rx_tx_cmd_single(struct iwm_softc *, struct iwm_rx_packet *, struct iwm_node *); @@ -472,6 +474,11 @@ const char *iwm_desc_lookup(uint32_t); void iwm_nic_error(struct iwm_softc *); void iwm_nic_umac_error(struct iwm_softc *); #endif +void iwm_rx_mpdu(struct iwm_softc *, struct mbuf *, size_t, + struct mbuf_list *); +int iwm_rx_pkt_valid(struct iwm_rx_packet *); +void iwm_rx_pkt(struct iwm_softc *, struct iwm_rx_data *, + struct mbuf_list *); void iwm_notif_intr(struct iwm_softc *); int iwm_intr(void *); int iwm_match(struct device *, void *, void *); @@ -1807,7 +1814,6 @@ iwm_nic_rx_init(struct iwm_softc *sc) IWM_FH_RCSR_RX_CONFIG_CHNL_EN_ENABLE_VAL | IWM_FH_RCSR_CHNL0_RX_IGNORE_RXF_EMPTY | /* HW bug */ IWM_FH_RCSR_CHNL0_RX_CONFIG_IRQ_DEST_INT_HOST_VAL | - IWM_FH_RCSR_CHNL0_RX_CONFIG_SINGLE_FRAME_MSK | (IWM_RX_RB_TIMEOUT << IWM_FH_RCSR_RX_CONFIG_REG_IRQ_RBTH_POS) | IWM_FH_RCSR_RX_CONFIG_REG_VAL_RB_SIZE_4K | IWM_RX_QUEUE_SIZE_LOG << IWM_FH_RCSR_RX_CONFIG_RBDCB_SIZE_POS); @@ -3539,56 +3545,24 @@ iwm_get_noise(const struct iwm_statistics_rx_non_phy * return (nbant == 0) ? -127 : (total / nbant) - 107; } -void -iwm_rx_rx_mpdu(struct iwm_softc *sc, struct iwm_rx_packet *pkt, - struct iwm_rx_data *data, struct mbuf_list *ml) +int +iwm_rx_frame(struct iwm_softc *sc, struct mbuf *m, uint32_t rx_pkt_status, + struct mbuf_list *ml) { struct ieee80211com *ic = &sc->sc_ic; struct ieee80211_frame *wh; struct ieee80211_node *ni; struct ieee80211_rxinfo rxi; struct ieee80211_channel *bss_chan; - struct mbuf *m; struct iwm_rx_phy_info *phy_info; - struct iwm_rx_mpdu_res_start *rx_res; int device_timestamp; - uint32_t len; - uint32_t rx_pkt_status; int rssi, chanidx; uint8_t saved_bssid[IEEE80211_ADDR_LEN] = { 0 }; - bus_dmamap_sync(sc->sc_dmat, data->map, 0, IWM_RBUF_SIZE, - BUS_DMASYNC_POSTREAD); - phy_info = &sc->sc_last_phy_info; - rx_res = (struct iwm_rx_mpdu_res_start *)pkt->data; - wh = (struct ieee80211_frame *)(pkt->data + sizeof(*rx_res)); - len = le16toh(rx_res->byte_count); - if (len < IEEE80211_MIN_LEN) { - ic->ic_stats.is_rx_tooshort++; - IC2IFP(ic)->if_ierrors++; - return; - } - if (len > IWM_RBUF_SIZE - sizeof(*rx_res)) { - IC2IFP(ic)->if_ierrors++; - return; - } - rx_pkt_status = le32toh(*(uint32_t *)(pkt->data + - sizeof(*rx_res) + len)); - if (__predict_false(phy_info->cfg_phy_cnt > 20)) - return; + return EINVAL; - if (!(rx_pkt_status & IWM_RX_MPDU_RES_STATUS_CRC_OK) || - !(rx_pkt_status & IWM_RX_MPDU_RES_STATUS_OVERRUN_OK)) - return; /* drop */ - - m = data->m; - if (iwm_rx_addbuf(sc, IWM_RBUF_SIZE, sc->rxq.cur) != 0) - return; - m->m_data = pkt->data + sizeof(*rx_res); - m->m_pkthdr.len = m->m_len = len; - device_timestamp = le32toh(phy_info->system_timestamp); rssi = iwm_get_signal_strength(sc, phy_info); @@ -3599,6 +3573,7 @@ iwm_rx_rx_mpdu(struct iwm_softc *sc, struct iwm_rx_pac if (chanidx < 0 || chanidx >= nitems(ic->ic_channels)) chanidx = ieee80211_chan2ieee(ic, ic->ic_ibss_chan); + wh = mtod(m, struct ieee80211_frame *); ni = ieee80211_find_rxnode(ic, wh); if (ni == ic->ic_bss) { /* @@ -3672,6 +3647,8 @@ iwm_rx_rx_mpdu(struct iwm_softc *sc, struct iwm_rx_pac if (ni == ic->ic_bss && IEEE80211_ADDR_EQ(saved_bssid, ni->ni_macaddr)) ni->ni_chan = bss_chan; ieee80211_release_node(ic, ni); + + return 0; } void @@ -7525,38 +7502,103 @@ do { \ #define ADVANCE_RXQ(sc) (sc->rxq.cur = (sc->rxq.cur + 1) % IWM_RX_RING_COUNT); void -iwm_notif_intr(struct iwm_softc *sc) +iwm_rx_mpdu(struct iwm_softc *sc, struct mbuf *m, size_t maxlen, + struct mbuf_list *ml) { - struct mbuf_list ml = MBUF_LIST_INITIALIZER(); - uint16_t hw; + struct ieee80211com *ic = &sc->sc_ic; + struct ifnet *ifp = IC2IFP(ic); + struct iwm_rx_packet *pkt; + struct iwm_rx_mpdu_res_start *rx_res; + uint16_t len; + uint32_t rx_pkt_status; + int rxfail; - bus_dmamap_sync(sc->sc_dmat, sc->rxq.stat_dma.map, - 0, sc->rxq.stat_dma.size, BUS_DMASYNC_POSTREAD); + pkt = mtod(m, struct iwm_rx_packet *); + rx_res = (struct iwm_rx_mpdu_res_start *)pkt->data; + len = le16toh(rx_res->byte_count); + if (len < IEEE80211_MIN_LEN) { + ic->ic_stats.is_rx_tooshort++; + IC2IFP(ic)->if_ierrors++; + m_freem(m); + return; + } + if (len + sizeof(*rx_res) + sizeof(rx_pkt_status) > maxlen || + len > IEEE80211_MAX_LEN) { + IC2IFP(ic)->if_ierrors++; + m_freem(m); + return; + } - hw = le16toh(sc->rxq.stat->closed_rb_num) & 0xfff; - hw &= (IWM_RX_RING_COUNT - 1); - while (sc->rxq.cur != hw) { - struct iwm_rx_data *data = &sc->rxq.data[sc->rxq.cur]; - struct iwm_rx_packet *pkt; - int qid, idx, code, handled = 1; + memcpy(&rx_pkt_status, pkt->data + sizeof(*rx_res) + len, + sizeof(rx_pkt_status)); + rx_pkt_status = le32toh(rx_pkt_status); + rxfail = ((rx_pkt_status & IWM_RX_MPDU_RES_STATUS_CRC_OK) == 0 || + (rx_pkt_status & IWM_RX_MPDU_RES_STATUS_OVERRUN_OK) == 0); + if (rxfail) { + ifp->if_ierrors++; + m_freem(m); + return; + } - bus_dmamap_sync(sc->sc_dmat, data->map, 0, sizeof(*pkt), - BUS_DMASYNC_POSTREAD); - pkt = mtod(data->m, struct iwm_rx_packet *); + /* Extract the 802.11 frame. */ + m->m_data = (caddr_t)pkt->data + sizeof(*rx_res); + m->m_pkthdr.len = m->m_len = len; + if (iwm_rx_frame(sc, m, rx_pkt_status, ml) != 0) { + ifp->if_ierrors++; + m_freem(m); + } +} +int +iwm_rx_pkt_valid(struct iwm_rx_packet *pkt) +{ + int qid, idx, code; + + qid = pkt->hdr.qid & ~0x80; + idx = pkt->hdr.idx; + code = IWM_WIDE_ID(pkt->hdr.flags, pkt->hdr.code); + + return (!(qid == 0 && idx == 0 && code == 0) && + pkt->len_n_flags != htole32(IWM_FH_RSCSR_FRAME_INVALID)); +} + +void +iwm_rx_pkt(struct iwm_softc *sc, struct iwm_rx_data *data, struct mbuf_list *ml) +{ + struct ifnet *ifp = IC2IFP(&sc->sc_ic); + struct iwm_rx_packet *pkt, *nextpkt; + uint32_t offset = 0, nextoff = 0, nmpdu = 0, len; + struct mbuf *m0, *m; + const size_t minsz = sizeof(pkt->len_n_flags) + sizeof(pkt->hdr); + size_t remain = IWM_RBUF_SIZE; + int qid, idx, code, handled = 1; + + bus_dmamap_sync(sc->sc_dmat, data->map, 0, IWM_RBUF_SIZE, + BUS_DMASYNC_POSTREAD); + + m0 = data->m; + while (m0 && offset + minsz < IWM_RBUF_SIZE) { + pkt = (struct iwm_rx_packet *)(m0->m_data + offset); qid = pkt->hdr.qid; idx = pkt->hdr.idx; code = IWM_WIDE_ID(pkt->hdr.flags, pkt->hdr.code); - /* - * randomly get these from the firmware, no idea why. - * they at least seem harmless, so just ignore them for now - */ - if (__predict_false((pkt->hdr.code == 0 && (qid & ~0x80) == 0 - && idx == 0) || pkt->len_n_flags == htole32(0x55550000))) { - ADVANCE_RXQ(sc); - continue; + if (!iwm_rx_pkt_valid(pkt)) + break; + + len = sizeof(pkt->len_n_flags) + iwm_rx_packet_len(pkt); + if (len < sizeof(pkt->hdr) || + len > (IWM_RBUF_SIZE - offset - minsz)) + break; + + if (code == IWM_REPLY_RX_MPDU_CMD && ++nmpdu == 1) { + /* Take mbuf m0 off the RX ring. */ + if (iwm_rx_addbuf(sc, IWM_RBUF_SIZE, sc->rxq.cur)) { + ifp->if_ierrors++; + break; + } + KASSERT(data->m != m0); } switch (code) { @@ -7564,10 +7606,40 @@ iwm_notif_intr(struct iwm_softc *sc) iwm_rx_rx_phy_cmd(sc, pkt, data); break; - case IWM_REPLY_RX_MPDU_CMD: - iwm_rx_rx_mpdu(sc, pkt, data, &ml); - break; + case IWM_REPLY_RX_MPDU_CMD: { + nextoff = offset + + roundup(len, IWM_FH_RSCSR_FRAME_ALIGN); + nextpkt = (struct iwm_rx_packet *) + (m0->m_data + nextoff); + if (nextoff + minsz >= IWM_RBUF_SIZE || + !iwm_rx_pkt_valid(nextpkt)) { + /* No need to copy last frame in buffer. */ + if (offset > 0) + m_adj(m0, offset); + iwm_rx_mpdu(sc, m0, remain - minsz, ml); + m0 = NULL; /* stack owns m0 now; abort loop */ + } else { + /* + * Create an mbuf which points to the current + * packet. Always copy from offset zero to + * preserve m_pkthdr. + */ + m = m_copym(m0, 0, M_COPYALL, M_DONTWAIT); + if (m == NULL) { + ifp->if_ierrors++; + break; + } + m_adj(m, offset); + iwm_rx_mpdu(sc, m, remain - minsz, ml); + } + if (offset + minsz < remain) + remain -= offset; + else + remain = minsz; + break; + } + case IWM_TX_CMD: iwm_rx_tx_cmd(sc, pkt, data); break; @@ -7818,6 +7890,27 @@ iwm_notif_intr(struct iwm_softc *sc) iwm_cmd_done(sc, qid, idx, code); } + offset += roundup(len, IWM_FH_RSCSR_FRAME_ALIGN); + } + + if (m0 && m0 != data->m) + m_freem(m0); +} + +void +iwm_notif_intr(struct iwm_softc *sc) +{ + struct mbuf_list ml = MBUF_LIST_INITIALIZER(); + uint16_t hw; + + bus_dmamap_sync(sc->sc_dmat, sc->rxq.stat_dma.map, + 0, sc->rxq.stat_dma.size, BUS_DMASYNC_POSTREAD); + + hw = le16toh(sc->rxq.stat->closed_rb_num) & 0xfff; + hw &= (IWM_RX_RING_COUNT - 1); + while (sc->rxq.cur != hw) { + struct iwm_rx_data *data = &sc->rxq.data[sc->rxq.cur]; + iwm_rx_pkt(sc, data, &ml); ADVANCE_RXQ(sc); } if_input(&sc->sc_ic.ic_if, &ml);