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);

Reply via email to