From: stsp Date: Mon, 24 Apr 2017 09:31:31 +0000 (+0000) Subject: Implement support for multiple packets per receive DMA buffer in iwm(4). X-Git-Url: http://artulab.com/gitweb/?a=commitdiff_plain;h=902860fcc1ee3f633911ae04c962cdfdff4d273b;p=openbsd Implement support for multiple packets per receive DMA buffer in iwm(4). We can now clear SINGLE_FRAME_MSK in the receive configuration register. Linux has not set this bit in years, and Dragonfly stopped setting it in commit b5eb43f0280bbcfd26af51cf5a4b8e8ff3590b67. This is a prerequisite for monitor mode since the firmware seems to ignore the SINGLE_FRAME_MSK bit while running in monitor mode and sends us garbage. Tested on 7260, 7265, and 8260 devices. --- diff --git a/sys/dev/pci/if_iwm.c b/sys/dev/pci/if_iwm.c index 4970ce78369..f5deacb98fd 100644 --- a/sys/dev/pci/if_iwm.c +++ b/sys/dev/pci/if_iwm.c @@ -1,4 +1,4 @@ -/* $OpenBSD: if_iwm.c,v 1.170 2017/04/21 17:54:02 stsp Exp $ */ +/* $OpenBSD: if_iwm.c,v 1.171 2017/04/24 09:31:31 stsp Exp $ */ /* * Copyright (c) 2014, 2016 genua gmbh @@ -362,8 +362,8 @@ int iwm_get_signal_strength(struct iwm_softc *, struct iwm_rx_phy_info *); 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 *); +void iwm_rx_mpdu(struct iwm_softc *, struct mbuf *); +void iwm_rx_pkt(struct iwm_softc *, struct iwm_rx_data *); void iwm_rx_tx_cmd_single(struct iwm_softc *, struct iwm_rx_packet *, struct iwm_node *); void iwm_rx_tx_cmd(struct iwm_softc *, struct iwm_rx_packet *, @@ -449,6 +449,7 @@ const char *iwm_desc_lookup(uint32_t); void iwm_nic_error(struct iwm_softc *); void iwm_nic_umac_error(struct iwm_softc *); #endif +int iwm_rx_frame(struct iwm_softc *, struct mbuf *); void iwm_notif_intr(struct iwm_softc *); int iwm_intr(void *); int iwm_match(struct device *, void *, void *); @@ -1672,7 +1673,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); @@ -3296,43 +3296,23 @@ iwm_get_noise(const struct iwm_statistics_rx_non_phy *stats) 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) +int +iwm_rx_frame(struct iwm_softc *sc, struct mbuf *m) { struct ieee80211com *ic = &sc->sc_ic; struct ieee80211_frame *wh; struct ieee80211_node *ni; struct ieee80211_channel *c = NULL; struct ieee80211_rxinfo rxi; - 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; - 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); - rx_pkt_status = le32toh(*(uint32_t *)(pkt->data + - sizeof(*rx_res) + len)); - - m = data->m; - m->m_data = pkt->data + sizeof(*rx_res); - m->m_pkthdr.len = m->m_len = len; + wh = mtod(m, struct ieee80211_frame *); if (__predict_false(phy_info->cfg_phy_cnt > 20)) - return; - - if (!(rx_pkt_status & IWM_RX_MPDU_RES_STATUS_CRC_OK) || - !(rx_pkt_status & IWM_RX_MPDU_RES_STATUS_OVERRUN_OK)) - return; /* drop */ + return EINVAL; device_timestamp = le32toh(phy_info->system_timestamp); @@ -3344,9 +3324,6 @@ iwm_rx_rx_mpdu(struct iwm_softc *sc, struct iwm_rx_packet *pkt, rssi = (0 - IWM_MIN_DBM) + rssi; /* normalize */ rssi = MIN(rssi, ic->ic_max_rssi); /* clip to max. 100% */ - if (iwm_rx_addbuf(sc, IWM_RBUF_SIZE, sc->rxq.cur) != 0) - return; - if (le32toh(phy_info->channel) < nitems(ic->ic_channels)) c = &ic->ic_channels[le32toh(phy_info->channel)]; @@ -3415,6 +3392,8 @@ iwm_rx_rx_mpdu(struct iwm_softc *sc, struct iwm_rx_packet *pkt, #endif ieee80211_input(IC2IFP(ic), m, ni, &rxi); ieee80211_release_node(ic, ni); + + return 0; } void @@ -6472,47 +6451,93 @@ 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) { - uint16_t hw; + struct ifnet *ifp = IC2IFP(&sc->sc_ic); + struct iwm_rx_packet *pkt; + struct iwm_rx_mpdu_res_start *rx_res; + uint32_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); + rx_pkt_status = le32toh(*(uint32_t *)(pkt->data + + sizeof(*rx_res) + len)); - hw = le16toh(sc->rxq.stat->closed_rb_num) & 0xfff; - while (sc->rxq.cur != hw) { - struct iwm_rx_data *data = &sc->rxq.data[sc->rxq.cur]; - struct iwm_rx_packet *pkt; - struct iwm_cmd_response *cresp; - int qid, idx, code; + 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) != 0) { + ifp->if_ierrors++; + m_freem(m); + } +} +void +iwm_rx_pkt(struct iwm_softc *sc, struct iwm_rx_data *data) +{ + struct ifnet *ifp = IC2IFP(&sc->sc_ic); + struct iwm_rx_packet *pkt; + struct iwm_cmd_response *cresp; + uint32_t offset = 0, nmpdu = 0, len; + struct mbuf *m0; + const uint32_t minsz = sizeof(uint32_t) + sizeof(struct iwm_cmd_header); + int qid, idx, code; + + bus_dmamap_sync(sc->sc_dmat, data->map, 0, IWM_RBUF_SIZE, + BUS_DMASYNC_POSTREAD); + + m0 = data->m; + while (offset + minsz < IWM_RBUF_SIZE) { + pkt = (struct iwm_rx_packet *)(m0->m_data + offset); qid = pkt->hdr.qid & ~0x80; 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 == 0 && idx == 0) - || pkt->len_n_flags == htole32(0x55550000))) { - ADVANCE_RXQ(sc); - continue; + if ((code == 0 && qid == 0 && idx == 0) || + pkt->len_n_flags == htole32(IWM_FH_RSCSR_FRAME_INVALID)) { + 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); } + len = le32toh(pkt->len_n_flags) & IWM_FH_RSCSR_FRAME_SIZE_MSK; + len += sizeof(uint32_t); /* account for status word */ + switch (code) { case IWM_REPLY_RX_PHY_CMD: iwm_rx_rx_phy_cmd(sc, pkt, data); break; - case IWM_REPLY_RX_MPDU_CMD: - iwm_rx_rx_mpdu(sc, pkt, data); + case IWM_REPLY_RX_MPDU_CMD: { + /* Always copy from offset zero to preserve m_pkthdr. */ + struct mbuf *m = m_copym(m0, 0, M_COPYALL, M_DONTWAIT); + if (m == NULL) { + ifp->if_ierrors++; + break; + } + m_adj(m, offset); + m_adj(m, -(m->m_pkthdr.len - len)); + iwm_rx_mpdu(sc, m); break; + } case IWM_TX_CMD: iwm_rx_tx_cmd(sc, pkt, data); @@ -6731,6 +6756,26 @@ iwm_notif_intr(struct iwm_softc *sc) iwm_cmd_done(sc, pkt); } + offset += roundup(len, IWM_FH_RSCSR_FRAME_ALIGN); + } + + if (m0 != data->m) + m_freem(m0); +} + +void +iwm_notif_intr(struct iwm_softc *sc) +{ + 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; + while (sc->rxq.cur != hw) { + struct iwm_rx_data *data = &sc->rxq.data[sc->rxq.cur]; + + iwm_rx_pkt(sc, data); ADVANCE_RXQ(sc); } diff --git a/sys/dev/pci/if_iwmreg.h b/sys/dev/pci/if_iwmreg.h index b2576e5487d..288c9c456e2 100644 --- a/sys/dev/pci/if_iwmreg.h +++ b/sys/dev/pci/if_iwmreg.h @@ -1,4 +1,4 @@ -/* $OpenBSD: if_iwmreg.h,v 1.25 2017/04/04 00:40:52 claudio Exp $ */ +/* $OpenBSD: if_iwmreg.h,v 1.26 2017/04/24 09:31:31 stsp Exp $ */ /****************************************************************************** * @@ -5876,7 +5876,13 @@ struct iwm_rx_packet { * 31: flag flush RB request * 30: flag ignore TC (terminal counter) request * 29: flag fast IRQ request - * 28-14: Reserved + * 28-26: Reserved + * 25: Offload enabled + * 24: RPF enabled + * 23: RSS enabled + * 22: Checksum enabled + * 21-16: RX queue + * 15-14: Reserved * 13-00: RX frame size */ uint32_t len_n_flags; @@ -5885,6 +5891,11 @@ struct iwm_rx_packet { } __packed; #define IWM_FH_RSCSR_FRAME_SIZE_MSK 0x00003fff +#define IWM_FH_RSCSR_FRAME_INVALID 0x55550000 +#define IWM_FH_RSCSR_FRAME_ALIGN 0x40 +#define IWM_FH_RSCSR_RPA_EN (1 << 25) +#define IWM_FH_RSCSR_RXQ_POS 16 +#define IWM_FH_RSCSR_RXQ_MASK 0x3F0000 static uint32_t iwm_rx_packet_len(const struct iwm_rx_packet *pkt)