Hi Stefan,
just tested today with my other laptop (Dell Latitude 7400): iwm0 at pci0 dev 20 function 3 "Intel Dual Band Wireless AC 9560" rev 0x30, msix iwm0: hw rev 0x310, fw ver 34.3125811985.0, address 60:f2:62:06:61:f2 ...against an Ubiquity UniFi AC AP Pro. Before patch: Up: Conn: 1 Mbps: 24.859 Peak Mbps: 25.577 Avg Mbps: 24.859 8017 2932200 23.271 100.00% Down: Conn: 1 Mbps: 81.806 Peak Mbps: 87.146 Avg Mbps: 81.806 12016 8855968 70.848 100.00% After patch: Up: Conn: 1 Mbps: 33.617 Peak Mbps: 33.617 Avg Mbps: 33.617 13076 3963176 31.674 100.00% Down: Conn: 1 Mbps: 87.707 Peak Mbps: 87.707 Avg Mbps: 87.707 8018 10584880 84.594 100.00% Thanks for your work! Uwe > diff refs/heads/master refs/heads/amsdu > blob - 00bf20b37ed33a652232885349c2f3dfa0d666d3 > blob + c353ee60473b7cfd237e1889e4a4b9235cb8bdc7 > --- sys/dev/pci/if_iwm.c > +++ sys/dev/pci/if_iwm.c > @@ -144,6 +144,8 @@ > #include <net80211/ieee80211_amrr.h> > #include <net80211/ieee80211_ra.h> > #include <net80211/ieee80211_radiotap.h> > +#include <net80211/ieee80211_priv.h> /* for SEQ_LT */ > +#undef DPRINTF /* defined in ieee80211_priv.h */ > > #define DEVNAME(_s) ((_s)->sc_dev.dv_xname) > > @@ -328,12 +330,17 @@ int iwm_mimo_enabled(struct iwm_softc *); > void iwm_setup_ht_rates(struct iwm_softc *); > void iwm_htprot_task(void *); > void iwm_update_htprot(struct ieee80211com *, struct ieee80211_node *); > +void iwm_init_reorder_buffer(struct iwm_reorder_buffer *, uint16_t, > + uint16_t); > +void iwm_clear_reorder_buffer(struct iwm_softc *, struct iwm_rxba_data *); > int iwm_ampdu_rx_start(struct ieee80211com *, struct ieee80211_node *, > uint8_t); > void iwm_ampdu_rx_stop(struct ieee80211com *, struct ieee80211_node *, > uint8_t); > +void iwm_rx_ba_session_expired(void *); > +void iwm_reorder_timer_expired(void *); > void iwm_sta_rx_agg(struct iwm_softc *, struct ieee80211_node *, uint8_t, > - uint16_t, uint16_t, int); > + uint16_t, uint16_t, int, int); > #ifdef notyet > int iwm_ampdu_tx_start(struct ieee80211com *, struct ieee80211_node *, > uint8_t); > @@ -372,8 +379,10 @@ int iwm_rxmq_get_signal_strength(struct iwm_softc > *, s > 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 *); > +int iwm_rx_hwdecrypt(struct iwm_softc *, struct mbuf *, uint32_t, > + struct ieee80211_rxinfo *); > int iwm_ccmp_decap(struct iwm_softc *, struct mbuf *, > - struct ieee80211_node *); > + struct ieee80211_node *, struct ieee80211_rxinfo *); > void iwm_rx_frame(struct iwm_softc *, struct mbuf *, int, uint32_t, int, int, > uint32_t, struct ieee80211_rxinfo *, struct mbuf_list *); > void iwm_rx_tx_cmd_single(struct iwm_softc *, struct iwm_rx_packet *, > @@ -490,6 +499,20 @@ void iwm_nic_umac_error(struct iwm_softc *); > #endif > void iwm_rx_mpdu(struct iwm_softc *, struct mbuf *, void *, size_t, > struct mbuf_list *); > +void iwm_flip_address(uint8_t *); > +int iwm_detect_duplicate(struct iwm_softc *, struct mbuf *, > + struct iwm_rx_mpdu_desc *, struct ieee80211_rxinfo *); > +int iwm_is_sn_less(uint16_t, uint16_t, uint16_t); > +void iwm_release_frames(struct iwm_softc *, struct ieee80211_node *, > + struct iwm_rxba_data *, struct iwm_reorder_buffer *, uint16_t, > + struct mbuf_list *); > +int iwm_oldsn_workaround(struct iwm_softc *, struct ieee80211_node *, > + int, struct iwm_reorder_buffer *, uint32_t, uint32_t); > +int iwm_rx_reorder(struct iwm_softc *, struct mbuf *, int, > + struct iwm_rx_mpdu_desc *, int, int, uint32_t, > + struct ieee80211_rxinfo *, struct mbuf_list *); > +void iwm_rx_mpdu_mq(struct iwm_softc *, struct mbuf *, void *, 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 *); > @@ -2902,11 +2925,139 @@ iwm_setup_ht_rates(struct iwm_softc *sc) > ic->ic_sup_mcs[1] = 0xff; /* MCS 8-15 */ > } > > +void > +iwm_init_reorder_buffer(struct iwm_reorder_buffer *reorder_buf, > + uint16_t ssn, uint16_t buf_size) > +{ > + reorder_buf->head_sn = ssn; > + reorder_buf->num_stored = 0; > + reorder_buf->buf_size = buf_size; > + reorder_buf->last_amsdu = 0; > + reorder_buf->last_sub_index = 0; > + reorder_buf->removed = 0; > + reorder_buf->valid = 0; > + reorder_buf->consec_oldsn_drops = 0; > + reorder_buf->consec_oldsn_ampdu_gp2 = 0; > + reorder_buf->consec_oldsn_prev_drop = 0; > +} > + > +void > +iwm_clear_reorder_buffer(struct iwm_softc *sc, struct iwm_rxba_data *rxba) > +{ > + int i; > + struct iwm_reorder_buffer *reorder_buf = &rxba->reorder_buf; > + struct iwm_reorder_buf_entry *entry; > + > + for (i = 0; i < reorder_buf->buf_size; i++) { > + entry = &rxba->entries[i]; > + ml_purge(&entry->frames); > + timerclear(&entry->reorder_time); > + } > + > + reorder_buf->removed = 1; > + timeout_del(&reorder_buf->reorder_timer); > + timerclear(&rxba->last_rx); > + timeout_del(&rxba->session_timer); > + rxba->baid = IWM_RX_REORDER_DATA_INVALID_BAID; > +} > + > +#define RX_REORDER_BUF_TIMEOUT_MQ_USEC (100000ULL) > + > +void > +iwm_rx_ba_session_expired(void *arg) > +{ > + struct iwm_rxba_data *rxba = arg; > + struct iwm_softc *sc = rxba->sc; > + struct ieee80211com *ic = &sc->sc_ic; > + struct ieee80211_node *ni = ic->ic_bss; > + struct timeval now, timeout, expiry; > + int s; > + > + s = splnet(); > + if ((sc->sc_flags & IWM_FLAG_SHUTDOWN) == 0 && > + ic->ic_state == IEEE80211_S_RUN && > + rxba->baid != IWM_RX_REORDER_DATA_INVALID_BAID) { > + getmicrouptime(&now); > + USEC_TO_TIMEVAL(RX_REORDER_BUF_TIMEOUT_MQ_USEC, &timeout); > + timeradd(&rxba->last_rx, &timeout, &expiry); > + if (timercmp(&now, &expiry, <)) { > + timeout_add_usec(&rxba->session_timer, rxba->timeout); > + } else { > + ic->ic_stats.is_ht_rx_ba_timeout++; > + ieee80211_delba_request(ic, ni, > + IEEE80211_REASON_TIMEOUT, 0, rxba->tid); > + } > + } > + splx(s); > +} > + > +void > +iwm_reorder_timer_expired(void *arg) > +{ > + struct mbuf_list ml = MBUF_LIST_INITIALIZER(); > + struct iwm_reorder_buffer *buf = arg; > + struct iwm_rxba_data *rxba = iwm_rxba_data_from_reorder_buf(buf); > + struct iwm_reorder_buf_entry *entries = &rxba->entries[0]; > + struct iwm_softc *sc = rxba->sc; > + struct ieee80211com *ic = &sc->sc_ic; > + struct ieee80211_node *ni = ic->ic_bss; > + int i, s; > + uint16_t sn = 0, index = 0; > + int expired = 0; > + int cont = 0; > + struct timeval now, timeout, expiry; > + > + if (!buf->num_stored || buf->removed) > + return; > + > + s = splnet(); > + getmicrouptime(&now); > + USEC_TO_TIMEVAL(RX_REORDER_BUF_TIMEOUT_MQ_USEC, &timeout); > + > + for (i = 0; i < buf->buf_size ; i++) { > + index = (buf->head_sn + i) % buf->buf_size; > + > + if (ml_empty(&entries[index].frames)) { > + /* > + * If there is a hole and the next frame didn't expire > + * we want to break and not advance SN. > + */ > + cont = 0; > + continue; > + } > + timeradd(&entries[index].reorder_time, &timeout, &expiry); > + if (!cont && timercmp(&now, &expiry, <)) > + break; > + > + expired = 1; > + /* continue until next hole after this expired frame */ > + cont = 1; > + sn = (buf->head_sn + (i + 1)) & 0xfff; > + } > + > + if (expired) { > + /* SN is set to the last expired frame + 1 */ > + iwm_release_frames(sc, ni, rxba, buf, sn, &ml); > + if_input(&sc->sc_ic.ic_if, &ml); > + ic->ic_stats.is_ht_rx_ba_window_gap_timeout++; > + } else { > + /* > + * If no frame expired and there are stored frames, index is now > + * pointing to the first unexpired frame - modify reorder > timeout > + * accordingly. > + */ > + timeout_add_usec(&buf->reorder_timer, > + RX_REORDER_BUF_TIMEOUT_MQ_USEC); > + } > + > + splx(s); > +} > + > #define IWM_MAX_RX_BA_SESSIONS 16 > > void > iwm_sta_rx_agg(struct iwm_softc *sc, struct ieee80211_node *ni, uint8_t tid, > - uint16_t ssn, uint16_t winsize, int start) > + uint16_t ssn, uint16_t winsize, int timeout_val, int start) > { > struct ieee80211com *ic = &sc->sc_ic; > struct iwm_add_sta_cmd cmd; > @@ -2914,9 +3065,14 @@ iwm_sta_rx_agg(struct iwm_softc *sc, struct ieee80211_ > int err, s; > uint32_t status; > size_t cmdsize; > + struct iwm_rxba_data *rxba = NULL; > + uint8_t baid = 0; > > + s = splnet(); > + > if (start && sc->sc_rx_ba_sessions >= IWM_MAX_RX_BA_SESSIONS) { > ieee80211_addba_req_refuse(ic, ni, tid); > + splx(s); > return; > } > > @@ -2945,16 +3101,71 @@ iwm_sta_rx_agg(struct iwm_softc *sc, struct ieee80211_ > err = iwm_send_cmd_pdu_status(sc, IWM_ADD_STA, cmdsize, &cmd, > &status); > > - s = splnet(); > - if (!err && (status & IWM_ADD_STA_STATUS_MASK) == IWM_ADD_STA_SUCCESS) { > + if (err || (status & IWM_ADD_STA_STATUS_MASK) != IWM_ADD_STA_SUCCESS) { > + if (start) > + ieee80211_addba_req_refuse(ic, ni, tid); > + splx(s); > + return; > + } > + > + if (sc->sc_mqrx_supported) { > + /* Deaggregation is done in hardware. */ > if (start) { > - sc->sc_rx_ba_sessions++; > - ieee80211_addba_req_accept(ic, ni, tid); > - } else if (sc->sc_rx_ba_sessions > 0) > - sc->sc_rx_ba_sessions--; > - } else if (start) > - ieee80211_addba_req_refuse(ic, ni, tid); > + if (!(status & IWM_ADD_STA_BAID_VALID_MASK)) { > + ieee80211_addba_req_refuse(ic, ni, tid); > + splx(s); > + return; > + } > + baid = (status & IWM_ADD_STA_BAID_MASK) >> > + IWM_ADD_STA_BAID_SHIFT; > + if (baid == IWM_RX_REORDER_DATA_INVALID_BAID || > + baid >= nitems(sc->sc_rxba_data)) { > + ieee80211_addba_req_refuse(ic, ni, tid); > + splx(s); > + return; > + } > + rxba = &sc->sc_rxba_data[baid]; > + if (rxba->baid != IWM_RX_REORDER_DATA_INVALID_BAID) { > + ieee80211_addba_req_refuse(ic, ni, tid); > + splx(s); > + return; > + } > + rxba->sta_id = IWM_STATION_ID; > + rxba->tid = tid; > + rxba->baid = baid; > + rxba->timeout = timeout_val; > + getmicrouptime(&rxba->last_rx); > + iwm_init_reorder_buffer(&rxba->reorder_buf, ssn, > + winsize); > + if (timeout_val != 0) { > + struct ieee80211_rx_ba *ba; > + timeout_add_usec(&rxba->session_timer, > + timeout_val); > + /* XXX disable net80211's BA timeout handler */ > + ba = &ni->ni_rx_ba[tid]; > + ba->ba_timeout_val = 0; > + } > + } else { > + int i; > + for (i = 0; i < nitems(sc->sc_rxba_data); i++) { > + rxba = &sc->sc_rxba_data[i]; > + if (rxba->baid == > + IWM_RX_REORDER_DATA_INVALID_BAID) > + continue; > + if (rxba->tid != tid) > + continue; > + iwm_clear_reorder_buffer(sc, rxba); > + break; > + } > + } > + } > > + if (start) { > + sc->sc_rx_ba_sessions++; > + ieee80211_addba_req_accept(ic, ni, tid); > + } else if (sc->sc_rx_ba_sessions > 0) > + sc->sc_rx_ba_sessions--; > + > splx(s); > } > > @@ -3002,18 +3213,20 @@ iwm_ba_task(void *arg) > struct ieee80211com *ic = &sc->sc_ic; > struct ieee80211_node *ni = ic->ic_bss; > int s = splnet(); > + int tid; > > - if (sc->sc_flags & IWM_FLAG_SHUTDOWN) { > - refcnt_rele_wake(&sc->task_refs); > - splx(s); > - return; > + for (tid = 0; tid < IWM_MAX_TID_COUNT; tid++) { > + if (sc->sc_flags & IWM_FLAG_SHUTDOWN) > + break; > + if (sc->ba_start_tidmask & (1 << tid)) { > + iwm_sta_rx_agg(sc, ni, tid, sc->ba_ssn[tid], > + sc->ba_winsize[tid], sc->ba_timeout_val[tid], 1); > + sc->ba_start_tidmask &= ~(1 << tid); > + } else if (sc->ba_stop_tidmask & (1 << tid)) { > + iwm_sta_rx_agg(sc, ni, tid, 0, 0, 0, 0); > + sc->ba_stop_tidmask &= ~(1 << tid); > + } > } > - > - if (sc->ba_start) > - iwm_sta_rx_agg(sc, ni, sc->ba_tid, sc->ba_ssn, > - sc->ba_winsize, 1); > - else > - iwm_sta_rx_agg(sc, ni, sc->ba_tid, 0, 0, 0); > > refcnt_rele_wake(&sc->task_refs); > splx(s); > @@ -3030,13 +3243,14 @@ iwm_ampdu_rx_start(struct ieee80211com *ic, struct iee > struct ieee80211_rx_ba *ba = &ni->ni_rx_ba[tid]; > struct iwm_softc *sc = IC2IFP(ic)->if_softc; > > - if (sc->sc_rx_ba_sessions >= IWM_MAX_RX_BA_SESSIONS) > + if (sc->sc_rx_ba_sessions >= IWM_MAX_RX_BA_SESSIONS || > + tid > IWM_MAX_TID_COUNT || (sc->ba_start_tidmask & (1 << tid))) > return ENOSPC; > > - sc->ba_start = 1; > - sc->ba_tid = tid; > - sc->ba_ssn = htole16(ba->ba_winstart); > - sc->ba_winsize = htole16(ba->ba_winsize); > + sc->ba_start_tidmask |= (1 << tid); > + sc->ba_ssn[tid] = ba->ba_winstart; > + sc->ba_winsize[tid] = ba->ba_winsize; > + sc->ba_timeout_val[tid] = ba->ba_timeout_val; > iwm_add_task(sc, systq, &sc->ba_task); > > return EBUSY; > @@ -3052,8 +3266,10 @@ iwm_ampdu_rx_stop(struct ieee80211com *ic, struct ieee > { > struct iwm_softc *sc = IC2IFP(ic)->if_softc; > > - sc->ba_start = 0; > - sc->ba_tid = tid; > + if (tid > IWM_MAX_TID_COUNT || sc->ba_stop_tidmask & (1 << tid)) > + return; > + > + sc->ba_stop_tidmask = (1 << tid); > iwm_add_task(sc, systq, &sc->ba_task); > } > > @@ -3907,7 +4123,8 @@ iwm_get_noise(const struct iwm_statistics_rx_non_phy * > } > > int > -iwm_ccmp_decap(struct iwm_softc *sc, struct mbuf *m, struct ieee80211_node > *ni) > +iwm_ccmp_decap(struct iwm_softc *sc, struct mbuf *m, struct ieee80211_node > *ni, > + struct ieee80211_rxinfo *rxi) > { > struct ieee80211com *ic = &sc->sc_ic; > struct ieee80211_key *k = &ni->ni_pairwise_key; > @@ -3936,7 +4153,12 @@ iwm_ccmp_decap(struct iwm_softc *sc, struct mbuf *m, s > (uint64_t)ivp[5] << 24 | > (uint64_t)ivp[6] << 32 | > (uint64_t)ivp[7] << 40; > - if (pn <= *prsc) { > + if (rxi->rxi_flags & IEEE80211_RXI_HWDEC_SAME_PN) { > + if (pn < *prsc) { > + ic->ic_stats.is_ccmp_replays++; > + return 1; > + } > + } else if (pn <= *prsc) { > ic->ic_stats.is_ccmp_replays++; > return 1; > } > @@ -3953,6 +4175,60 @@ iwm_ccmp_decap(struct iwm_softc *sc, struct mbuf *m, s > return 0; > } > > +int > +iwm_rx_hwdecrypt(struct iwm_softc *sc, struct mbuf *m, uint32_t > rx_pkt_status, > + struct ieee80211_rxinfo *rxi) > +{ > + struct ieee80211com *ic = &sc->sc_ic; > + struct ifnet *ifp = IC2IFP(ic); > + struct ieee80211_frame *wh; > + struct ieee80211_node *ni; > + int ret = 0; > + uint8_t type, subtype; > + > + wh = mtod(m, struct ieee80211_frame *); > + > + type = wh->i_fc[0] & IEEE80211_FC0_TYPE_MASK; > + if (type == IEEE80211_FC0_TYPE_CTL) > + return 0; > + > + subtype = wh->i_fc[0] & IEEE80211_FC0_SUBTYPE_MASK; > + if (ieee80211_has_qos(wh) && (subtype & IEEE80211_FC0_SUBTYPE_NODATA)) > + return 0; > + > + if (IEEE80211_IS_MULTICAST(wh->i_addr1) || > + !(wh->i_fc[1] & IEEE80211_FC1_PROTECTED)) > + return 0; > + > + ni = ieee80211_find_rxnode(ic, wh); > + /* Handle hardware decryption. */ > + if ((ni->ni_flags & IEEE80211_NODE_RXPROT) && > + ni->ni_pairwise_key.k_cipher == IEEE80211_CIPHER_CCMP) { > + if ((rx_pkt_status & IWM_RX_MPDU_RES_STATUS_SEC_ENC_MSK) != > + IWM_RX_MPDU_RES_STATUS_SEC_CCM_ENC) { > + ic->ic_stats.is_ccmp_dec_errs++; > + ret = 1; > + goto out; > + } > + /* Check whether decryption was successful or not. */ > + if ((rx_pkt_status & > + (IWM_RX_MPDU_RES_STATUS_DEC_DONE | > + IWM_RX_MPDU_RES_STATUS_MIC_OK)) != > + (IWM_RX_MPDU_RES_STATUS_DEC_DONE | > + IWM_RX_MPDU_RES_STATUS_MIC_OK)) { > + ic->ic_stats.is_ccmp_dec_errs++; > + ret = 1; > + goto out; > + } > + rxi->rxi_flags |= IEEE80211_RXI_HWDEC; > + } > +out: > + if (ret) > + ifp->if_ierrors++; > + ieee80211_release_node(ic, ni); > + return ret; > +} > + > void > iwm_rx_frame(struct iwm_softc *sc, struct mbuf *m, int chanidx, > uint32_t rx_pkt_status, int is_shortpre, int rate_n_flags, > @@ -3960,11 +4236,11 @@ iwm_rx_frame(struct iwm_softc *sc, struct mbuf *m, int > struct mbuf_list *ml) > { > struct ieee80211com *ic = &sc->sc_ic; > + struct ifnet *ifp = IC2IFP(ic); > struct ieee80211_frame *wh; > struct ieee80211_node *ni; > struct ieee80211_channel *bss_chan; > uint8_t saved_bssid[IEEE80211_ADDR_LEN] = { 0 }; > - struct ifnet *ifp = IC2IFP(ic); > > if (chanidx < 0 || chanidx >= nitems(ic->ic_channels)) > chanidx = ieee80211_chan2ieee(ic, ic->ic_ibss_chan); > @@ -3981,39 +4257,12 @@ iwm_rx_frame(struct iwm_softc *sc, struct mbuf *m, int > } > ni->ni_chan = &ic->ic_channels[chanidx]; > > - /* Handle hardware decryption. */ > - if (((wh->i_fc[0] & IEEE80211_FC0_TYPE_MASK) != IEEE80211_FC0_TYPE_CTL) > - && (wh->i_fc[1] & IEEE80211_FC1_PROTECTED) && > - !IEEE80211_IS_MULTICAST(wh->i_addr1) && > - (ni->ni_flags & IEEE80211_NODE_RXPROT) && > - ni->ni_pairwise_key.k_cipher == IEEE80211_CIPHER_CCMP) { > - if ((rx_pkt_status & IWM_RX_MPDU_RES_STATUS_SEC_ENC_MSK) != > - IWM_RX_MPDU_RES_STATUS_SEC_CCM_ENC) { > - ic->ic_stats.is_ccmp_dec_errs++; > - ifp->if_ierrors++; > - m_freem(m); > - ieee80211_release_node(ic, ni); > - return; > - } > - /* Check whether decryption was successful or not. */ > - if ((rx_pkt_status & > - (IWM_RX_MPDU_RES_STATUS_DEC_DONE | > - IWM_RX_MPDU_RES_STATUS_MIC_OK)) != > - (IWM_RX_MPDU_RES_STATUS_DEC_DONE | > - IWM_RX_MPDU_RES_STATUS_MIC_OK)) { > - ic->ic_stats.is_ccmp_dec_errs++; > - ifp->if_ierrors++; > - m_freem(m); > - ieee80211_release_node(ic, ni); > - return; > - } > - if (iwm_ccmp_decap(sc, m, ni) != 0) { > - ifp->if_ierrors++; > - m_freem(m); > - ieee80211_release_node(ic, ni); > - return; > - } > - rxi->rxi_flags |= IEEE80211_RXI_HWDEC; > + if ((rxi->rxi_flags & IEEE80211_RXI_HWDEC) && > + iwm_ccmp_decap(sc, m, ni, rxi) != 0) { > + ifp->if_ierrors++; > + m_freem(m); > + ieee80211_release_node(ic, ni); > + return; > } > > #if NBPFILTER > 0 > @@ -4089,6 +4338,8 @@ iwm_rx_mpdu(struct iwm_softc *sc, struct mbuf *m, void > uint32_t rx_pkt_status; > int rssi, chanidx, rate_n_flags; > > + memset(&rxi, 0, sizeof(rxi)); > + > phy_info = &sc->sc_last_phy_info; > rx_res = (struct iwm_rx_mpdu_res_start *)pktdata; > len = le16toh(rx_res->byte_count); > @@ -4127,6 +4378,11 @@ iwm_rx_mpdu(struct iwm_softc *sc, struct mbuf *m, void > m->m_data = pktdata + sizeof(*rx_res); > m->m_pkthdr.len = m->m_len = len; > > + if (iwm_rx_hwdecrypt(sc, m, rx_pkt_status, &rxi)) { > + m_freem(m); > + return; > + } > + > chanidx = letoh32(phy_info->channel); > device_timestamp = le32toh(phy_info->system_timestamp); > phy_flags = letoh16(phy_info->phy_flags); > @@ -4136,7 +4392,6 @@ iwm_rx_mpdu(struct iwm_softc *sc, struct mbuf *m, void > rssi = (0 - IWM_MIN_DBM) + rssi; /* normalize */ > rssi = MIN(rssi, ic->ic_max_rssi); /* clip to max. 100% */ > > - memset(&rxi, 0, sizeof(rxi)); > rxi.rxi_rssi = rssi; > rxi.rxi_tstamp = device_timestamp; > > @@ -4146,6 +4401,385 @@ iwm_rx_mpdu(struct iwm_softc *sc, struct mbuf *m, void > } > > void > +iwm_flip_address(uint8_t *addr) > +{ > + int i; > + uint8_t mac_addr[ETHER_ADDR_LEN]; > + > + for (i = 0; i < ETHER_ADDR_LEN; i++) > + mac_addr[i] = addr[ETHER_ADDR_LEN - i - 1]; > + IEEE80211_ADDR_COPY(addr, mac_addr); > +} > + > +/* > + * Drop duplicate 802.11 retransmissions > + * (IEEE 802.11-2012: 9.3.2.10 "Duplicate detection and recovery") > + * and handle pseudo-duplicate frames which result from deaggregation > + * of A-MSDU frames in hardware. > + */ > +int > +iwm_detect_duplicate(struct iwm_softc *sc, struct mbuf *m, > + struct iwm_rx_mpdu_desc *desc, struct ieee80211_rxinfo *rxi) > +{ > + struct ieee80211com *ic = &sc->sc_ic; > + struct iwm_node *in = (void *)ic->ic_bss; > + struct iwm_rxq_dup_data *dup_data = &in->dup_data; > + uint8_t tid = IWM_MAX_TID_COUNT, subframe_idx; > + struct ieee80211_frame *wh = mtod(m, struct ieee80211_frame *); > + uint8_t type = wh->i_fc[0] & IEEE80211_FC0_TYPE_MASK; > + uint8_t subtype = wh->i_fc[0] & IEEE80211_FC0_SUBTYPE_MASK; > + int hasqos = ieee80211_has_qos(wh); > + uint16_t seq; > + > + if (type == IEEE80211_FC0_TYPE_CTL || > + (hasqos && (subtype & IEEE80211_FC0_SUBTYPE_NODATA)) || > + IEEE80211_IS_MULTICAST(wh->i_addr1)) > + return 0; > + > + if (hasqos) { > + tid = (ieee80211_get_qos(wh) & IEEE80211_QOS_TID); > + if (tid > IWM_MAX_TID_COUNT) > + tid = IWM_MAX_TID_COUNT; > + } > + > + /* If this wasn't a part of an A-MSDU the sub-frame index will be 0 */ > + subframe_idx = desc->amsdu_info & > + IWM_RX_MPDU_AMSDU_SUBFRAME_IDX_MASK; > + > + seq = letoh16(*(u_int16_t *)wh->i_seq) >> IEEE80211_SEQ_SEQ_SHIFT; > + if ((wh->i_fc[1] & IEEE80211_FC1_RETRY) && > + dup_data->last_seq[tid] == seq && > + dup_data->last_sub_frame[tid] >= subframe_idx) > + return 1; > + > + /* > + * Allow the same frame sequence number for all A-MSDU subframes > + * following the first subframe. > + * Otherwise these subframes would be discarded as replays. > + */ > + if (dup_data->last_seq[tid] == seq && > + subframe_idx > dup_data->last_sub_frame[tid] && > + (desc->mac_flags2 & IWM_RX_MPDU_MFLG2_AMSDU)) { > + rxi->rxi_flags |= IEEE80211_RXI_SAME_SEQ; > + } > + > + dup_data->last_seq[tid] = seq; > + dup_data->last_sub_frame[tid] = subframe_idx; > + > + return 0; > +} > + > +/* > + * Returns true if sn2 - buffer_size < sn1 < sn2. > + * To be used only in order to compare reorder buffer head with NSSN. > + * We fully trust NSSN unless it is behind us due to reorder timeout. > + * Reorder timeout can only bring us up to buffer_size SNs ahead of NSSN. > + */ > +int > +iwm_is_sn_less(uint16_t sn1, uint16_t sn2, uint16_t buffer_size) > +{ > + return SEQ_LT(sn1, sn2) && !SEQ_LT(sn1, sn2 - buffer_size); > +} > + > +void > +iwm_release_frames(struct iwm_softc *sc, struct ieee80211_node *ni, > + struct iwm_rxba_data *rxba, struct iwm_reorder_buffer *reorder_buf, > + uint16_t nssn, struct mbuf_list *ml) > +{ > + struct iwm_reorder_buf_entry *entries = &rxba->entries[0]; > + uint16_t ssn = reorder_buf->head_sn; > + > + /* ignore nssn smaller than head sn - this can happen due to timeout */ > + if (iwm_is_sn_less(nssn, ssn, reorder_buf->buf_size)) > + goto set_timer; > + > + while (iwm_is_sn_less(ssn, nssn, reorder_buf->buf_size)) { > + int index = ssn % reorder_buf->buf_size; > + struct mbuf *m; > + int chanidx, is_shortpre; > + uint32_t rx_pkt_status, rate_n_flags, device_timestamp; > + struct ieee80211_rxinfo *rxi; > + > + /* This data is the same for all A-MSDU subframes. */ > + chanidx = entries[index].chanidx; > + rx_pkt_status = entries[index].rx_pkt_status; > + is_shortpre = entries[index].is_shortpre; > + rate_n_flags = entries[index].rate_n_flags; > + device_timestamp = entries[index].device_timestamp; > + rxi = &entries[index].rxi; > + > + /* > + * Empty the list. Will have more than one frame for A-MSDU. > + * Empty list is valid as well since nssn indicates frames were > + * received. > + */ > + while ((m = ml_dequeue(&entries[index].frames)) != NULL) { > + iwm_rx_frame(sc, m, chanidx, rx_pkt_status, is_shortpre, > + rate_n_flags, device_timestamp, rxi, ml); > + reorder_buf->num_stored--; > + > + /* > + * Allow the same frame sequence number and CCMP PN for > + * all A-MSDU subframes following the first subframe. > + * Otherwise they would be discarded as replays. > + */ > + rxi->rxi_flags |= IEEE80211_RXI_SAME_SEQ; > + rxi->rxi_flags |= IEEE80211_RXI_HWDEC_SAME_PN; > + } > + > + ssn = (ssn + 1) & 0xfff; > + } > + reorder_buf->head_sn = nssn; > + > +set_timer: > + if (reorder_buf->num_stored && !reorder_buf->removed) { > + timeout_add_usec(&reorder_buf->reorder_timer, > + RX_REORDER_BUF_TIMEOUT_MQ_USEC); > + } else > + timeout_del(&reorder_buf->reorder_timer); > +} > + > +int > +iwm_oldsn_workaround(struct iwm_softc *sc, struct ieee80211_node *ni, int > tid, > + struct iwm_reorder_buffer *buffer, uint32_t reorder_data, uint32_t gp2) > +{ > + struct ieee80211com *ic = &sc->sc_ic; > + > + if (gp2 != buffer->consec_oldsn_ampdu_gp2) { > + /* we have a new (A-)MPDU ... */ > + > + /* > + * reset counter to 0 if we didn't have any oldsn in > + * the last A-MPDU (as detected by GP2 being identical) > + */ > + if (!buffer->consec_oldsn_prev_drop) > + buffer->consec_oldsn_drops = 0; > + > + /* either way, update our tracking state */ > + buffer->consec_oldsn_ampdu_gp2 = gp2; > + } else if (buffer->consec_oldsn_prev_drop) { > + /* > + * tracking state didn't change, and we had an old SN > + * indication before - do nothing in this case, we > + * already noted this one down and are waiting for the > + * next A-MPDU (by GP2) > + */ > + return 0; > + } > + > + /* return unless this MPDU has old SN */ > + if (!(reorder_data & IWM_RX_MPDU_REORDER_BA_OLD_SN)) > + return 0; > + > + /* update state */ > + buffer->consec_oldsn_prev_drop = 1; > + buffer->consec_oldsn_drops++; > + > + /* if limit is reached, send del BA and reset state */ > + if (buffer->consec_oldsn_drops == IWM_AMPDU_CONSEC_DROPS_DELBA) { > + ieee80211_delba_request(ic, ni, IEEE80211_REASON_UNSPECIFIED, > + 0, tid); > + buffer->consec_oldsn_prev_drop = 0; > + buffer->consec_oldsn_drops = 0; > + return 1; > + } > + > + return 0; > +} > + > +/* > + * Handle re-ordering of frames which were de-aggregated in hardware. > + * Returns 1 if the MPDU was consumed (buffered or dropped). > + * Returns 0 if the MPDU should be passed to upper layer. > + */ > +int > +iwm_rx_reorder(struct iwm_softc *sc, struct mbuf *m, int chanidx, > + struct iwm_rx_mpdu_desc *desc, int is_shortpre, int rate_n_flags, > + uint32_t device_timestamp, struct ieee80211_rxinfo *rxi, > + struct mbuf_list *ml) > +{ > + struct ieee80211com *ic = &sc->sc_ic; > + struct ieee80211_frame *wh; > + struct ieee80211_node *ni; > + struct iwm_rxba_data *rxba; > + struct iwm_reorder_buffer *buffer; > + uint32_t reorder_data = le32toh(desc->reorder_data); > + int is_amsdu = (desc->mac_flags2 & IWM_RX_MPDU_MFLG2_AMSDU); > + int last_subframe = > + (desc->amsdu_info & IWM_RX_MPDU_AMSDU_LAST_SUBFRAME); > + uint8_t tid; > + uint8_t subframe_idx = (desc->amsdu_info & > + IWM_RX_MPDU_AMSDU_SUBFRAME_IDX_MASK); > + struct iwm_reorder_buf_entry *entries; > + int index; > + uint16_t nssn, sn; > + uint8_t baid, type, subtype; > + int hasqos; > + > + wh = mtod(m, struct ieee80211_frame *); > + hasqos = ieee80211_has_qos(wh); > + tid = hasqos ? ieee80211_get_qos(wh) & IEEE80211_QOS_TID : 0; > + > + 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. > + */ > + if (IEEE80211_IS_MULTICAST(wh->i_addr1)) > + return 0; > + if (hasqos) { > + if (subtype & IEEE80211_FC0_SUBTYPE_NODATA) > + return 0; > + } else { > + if (type != IEEE80211_FC0_TYPE_CTL || > + subtype != IEEE80211_FC0_SUBTYPE_BAR) > + return 0; > + } > + > + baid = (reorder_data & IWM_RX_MPDU_REORDER_BAID_MASK) >> > + IWM_RX_MPDU_REORDER_BAID_SHIFT; > + if (baid == IWM_RX_REORDER_DATA_INVALID_BAID) > + return 0; > + > + rxba = &sc->sc_rxba_data[baid]; > + if (rxba == NULL || tid != rxba->tid || rxba->sta_id != IWM_STATION_ID) > + return 0; > + > + /* Bypass A-MPDU re-ordering in net80211. */ > + rxi->rxi_flags |= IEEE80211_RXI_AMPDU_DONE; > + > + nssn = reorder_data & IWM_RX_MPDU_REORDER_NSSN_MASK; > + sn = (reorder_data & IWM_RX_MPDU_REORDER_SN_MASK) >> > + IWM_RX_MPDU_REORDER_SN_SHIFT; > + > + buffer = &rxba->reorder_buf; > + entries = &rxba->entries[0]; > + > + if (!buffer->valid) { > + if (reorder_data & IWM_RX_MPDU_REORDER_BA_OLD_SN) > + return 0; > + buffer->valid = 1; > + } > + > + if (type == IEEE80211_FC0_TYPE_CTL && > + subtype == IEEE80211_FC0_SUBTYPE_BAR) { > + iwm_release_frames(sc, ni, rxba, buffer, nssn, ml); > + goto drop; > + } > + > + /* > + * If there was a significant jump in the nssn - adjust. > + * If the SN is smaller than the NSSN it might need to first go into > + * the reorder buffer, in which case we just release up to it and the > + * rest of the function will take care of storing it and releasing up to > + * the nssn. > + */ > + if (!iwm_is_sn_less(nssn, buffer->head_sn + buffer->buf_size, > + buffer->buf_size) || > + !SEQ_LT(sn, buffer->head_sn + buffer->buf_size)) { > + uint16_t min_sn = SEQ_LT(sn, nssn) ? sn : nssn; > + ic->ic_stats.is_ht_rx_frame_above_ba_winend++; > + iwm_release_frames(sc, ni, rxba, buffer, min_sn, ml); > + } > + > + if (iwm_oldsn_workaround(sc, ni, tid, buffer, reorder_data, > + device_timestamp)) { > + /* BA session will be torn down. */ > + ic->ic_stats.is_ht_rx_ba_window_jump++; > + goto drop; > + > + } > + > + /* drop any outdated packets */ > + if (SEQ_LT(sn, buffer->head_sn)) { > + ic->ic_stats.is_ht_rx_frame_below_ba_winstart++; > + goto drop; > + } > + > + /* release immediately if allowed by nssn and no stored frames */ > + if (!buffer->num_stored && SEQ_LT(sn, nssn)) { > + if (iwm_is_sn_less(buffer->head_sn, nssn, buffer->buf_size) && > + (!is_amsdu || last_subframe)) > + buffer->head_sn = nssn; > + return 0; > + } > + > + /* > + * release immediately if there are no stored frames, and the sn is > + * equal to the head. > + * This can happen due to reorder timer, where NSSN is behind head_sn. > + * When we released everything, and we got the next frame in the > + * sequence, according to the NSSN we can't release immediately, > + * while technically there is no hole and we can move forward. > + */ > + if (!buffer->num_stored && sn == buffer->head_sn) { > + if (!is_amsdu || last_subframe) > + buffer->head_sn = (buffer->head_sn + 1) & 0xfff; > + return 0; > + } > + > + index = sn % buffer->buf_size; > + > + /* > + * Check if we already stored this frame > + * As AMSDU is either received or not as whole, logic is simple: > + * If we have frames in that position in the buffer and the last frame > + * originated from AMSDU had a different SN then it is a retransmission. > + * If it is the same SN then if the subframe index is incrementing it > + * is the same AMSDU - otherwise it is a retransmission. > + */ > + if (!ml_empty(&entries[index].frames)) { > + if (!is_amsdu) { > + ic->ic_stats.is_ht_rx_ba_no_buf++; > + goto drop; > + } else if (sn != buffer->last_amsdu || > + buffer->last_sub_index >= subframe_idx) { > + ic->ic_stats.is_ht_rx_ba_no_buf++; > + goto drop; > + } > + } else { > + /* This data is the same for all A-MSDU subframes. */ > + entries[index].chanidx = chanidx; > + entries[index].is_shortpre = is_shortpre; > + entries[index].rate_n_flags = rate_n_flags; > + entries[index].device_timestamp = device_timestamp; > + memcpy(&entries[index].rxi, rxi, sizeof(entries[index].rxi)); > + } > + > + /* put in reorder buffer */ > + ml_enqueue(&entries[index].frames, m); > + buffer->num_stored++; > + getmicrouptime(&entries[index].reorder_time); > + > + if (is_amsdu) { > + buffer->last_amsdu = sn; > + buffer->last_sub_index = subframe_idx; > + } > + > + /* > + * We cannot trust NSSN for AMSDU sub-frames that are not the last. > + * The reason is that NSSN advances on the first sub-frame, and may > + * cause the reorder buffer to advance before all the sub-frames arrive. > + * Example: reorder buffer contains SN 0 & 2, and we receive AMSDU with > + * SN 1. NSSN for first sub frame will be 3 with the result of driver > + * releasing SN 0,1, 2. When sub-frame 1 arrives - reorder buffer is > + * already ahead and it will be dropped. > + * If the last sub-frame is not on this queue - we will get frame > + * release notification with up to date NSSN. > + */ > + if (!is_amsdu || last_subframe) > + iwm_release_frames(sc, ni, rxba, buffer, nssn, ml); > + > + return 1; > + > +drop: > + m_freem(m); > + return 1; > +} > + > +void > iwm_rx_mpdu_mq(struct iwm_softc *sc, struct mbuf *m, void *pktdata, > size_t maxlen, struct mbuf_list *ml) > { > @@ -4157,6 +4791,8 @@ iwm_rx_mpdu_mq(struct iwm_softc *sc, struct mbuf *m, v > uint8_t chanidx; > uint16_t phy_info; > > + memset(&rxi, 0, sizeof(rxi)); > + > desc = (struct iwm_rx_mpdu_desc *)pktdata; > > if (!(desc->status & htole16(IWM_RX_MPDU_RES_STATUS_CRC_OK)) || > @@ -4219,6 +4855,55 @@ iwm_rx_mpdu_mq(struct iwm_softc *sc, struct mbuf *m, v > m_adj(m, 2); > } > > + /* > + * Hardware de-aggregates A-MSDUs and copies the same MAC header > + * in place for each subframe. But it leaves the 'A-MSDU present' > + * bit set in the frame header. We need to clear this bit ourselves. > + * > + * And we must allow the same CCMP PN for subframes following the > + * first subframe. Otherwise they would be discarded as replays. > + */ > + if (desc->mac_flags2 & IWM_RX_MPDU_MFLG2_AMSDU) { > + struct ieee80211_frame *wh = mtod(m, struct ieee80211_frame *); > + uint8_t subframe_idx = (desc->amsdu_info & > + IWM_RX_MPDU_AMSDU_SUBFRAME_IDX_MASK); > + if (subframe_idx > 0) > + rxi.rxi_flags |= IEEE80211_RXI_HWDEC_SAME_PN; > + if (ieee80211_has_qos(wh) && ieee80211_has_addr4(wh) && > + m->m_len >= sizeof(struct ieee80211_qosframe_addr4)) { > + struct ieee80211_qosframe_addr4 *qwh4 = mtod(m, > + struct ieee80211_qosframe_addr4 *); > + qwh4->i_qos[0] &= htole16(~IEEE80211_QOS_AMSDU); > + > + /* HW reverses addr3 and addr4. */ > + iwm_flip_address(qwh4->i_addr3); > + iwm_flip_address(qwh4->i_addr4); > + } else if (ieee80211_has_qos(wh) && > + m->m_len >= sizeof(struct ieee80211_qosframe)) { > + struct ieee80211_qosframe *qwh = mtod(m, > + struct ieee80211_qosframe *); > + qwh->i_qos[0] &= htole16(~IEEE80211_QOS_AMSDU); > + > + /* HW reverses addr3. */ > + iwm_flip_address(qwh->i_addr3); > + } > + } > + > + /* > + * Verify decryption before duplicate detection. The latter uses > + * the TID supplied in QoS frame headers and this TID is implicitly > + * verified as part of the CCMP nonce. > + */ > + if (iwm_rx_hwdecrypt(sc, m, le16toh(desc->status), &rxi)) { > + m_freem(m); > + return; > + } > + > + if (iwm_detect_duplicate(sc, m, desc, &rxi)) { > + m_freem(m); > + return; > + } > + > phy_info = le16toh(desc->phy_info); > rate_n_flags = le32toh(desc->v1.rate_n_flags); > chanidx = desc->v1.channel; > @@ -4228,10 +4913,14 @@ iwm_rx_mpdu_mq(struct iwm_softc *sc, struct mbuf *m, v > rssi = (0 - IWM_MIN_DBM) + rssi; /* normalize */ > rssi = MIN(rssi, ic->ic_max_rssi); /* clip to max. 100% */ > > - memset(&rxi, 0, sizeof(rxi)); > rxi.rxi_rssi = rssi; > rxi.rxi_tstamp = le64toh(desc->v1.tsf_on_air_rise); > > + if (iwm_rx_reorder(sc, m, chanidx, desc, > + (phy_info & IWM_RX_MPDU_PHY_SHORT_PREAMBLE), > + rate_n_flags, device_timestamp, &rxi, ml)) > + return; > + > iwm_rx_frame(sc, m, chanidx, le16toh(desc->status), > (phy_info & IWM_RX_MPDU_PHY_SHORT_PREAMBLE), > rate_n_flags, device_timestamp, &rxi, ml); > @@ -6691,6 +7380,8 @@ iwm_deauth(struct iwm_softc *sc) > } > sc->sc_flags &= ~IWM_FLAG_STA_ACTIVE; > sc->sc_rx_ba_sessions = 0; > + sc->ba_start_tidmask = 0; > + sc->ba_stop_tidmask = 0; > } > > tfd_queue_msk = 0; > @@ -6769,6 +7460,8 @@ iwm_disassoc(struct iwm_softc *sc) > } > sc->sc_flags &= ~IWM_FLAG_STA_ACTIVE; > sc->sc_rx_ba_sessions = 0; > + sc->ba_start_tidmask = 0; > + sc->ba_stop_tidmask = 0; > } > > return 0; > @@ -7327,11 +8020,16 @@ iwm_newstate(struct ieee80211com *ic, enum ieee80211_s > { > struct ifnet *ifp = IC2IFP(ic); > struct iwm_softc *sc = ifp->if_softc; > + int i; > > if (ic->ic_state == IEEE80211_S_RUN) { > timeout_del(&sc->sc_calib_to); > iwm_del_task(sc, systq, &sc->ba_task); > iwm_del_task(sc, systq, &sc->htprot_task); > + for (i = 0; i < nitems(sc->sc_rxba_data); i++) { > + struct iwm_rxba_data *rxba = &sc->sc_rxba_data[i]; > + iwm_clear_reorder_buffer(sc, rxba); > + } > } > > sc->ns_nstate = nstate; > @@ -8137,10 +8835,19 @@ iwm_stop(struct ifnet *ifp) > sc->sc_flags &= ~IWM_FLAG_SHUTDOWN; > > sc->sc_rx_ba_sessions = 0; > + sc->ba_start_tidmask = 0; > + sc->ba_stop_tidmask = 0; > + memset(sc->ba_ssn, 0, sizeof(sc->ba_ssn)); > + memset(sc->ba_winsize, 0, sizeof(sc->ba_winsize)); > + memset(sc->ba_timeout_val, 0, sizeof(sc->ba_timeout_val)); > > sc->sc_newstate(ic, IEEE80211_S_INIT, -1); > > timeout_del(&sc->sc_calib_to); /* XXX refcount? */ > + for (i = 0; i < nitems(sc->sc_rxba_data); i++) { > + struct iwm_rxba_data *rxba = &sc->sc_rxba_data[i]; > + iwm_clear_reorder_buffer(sc, rxba); > + } > iwm_led_blink_stop(sc); > ifp->if_timer = sc->sc_tx_timer = 0; > > @@ -9217,7 +9924,7 @@ iwm_attach(struct device *parent, struct device *self, > struct ifnet *ifp = &ic->ic_if; > const char *intrstr; > int err; > - int txq_i, i; > + int txq_i, i, j; > > sc->sc_pct = pa->pa_pc; > sc->sc_pcitag = pa->pa_tag; > @@ -9528,6 +10235,17 @@ iwm_attach(struct device *parent, struct device *self, > #endif > timeout_set(&sc->sc_calib_to, iwm_calib_timeout, sc); > timeout_set(&sc->sc_led_blink_to, iwm_led_blink_timeout, sc); > + for (i = 0; i < nitems(sc->sc_rxba_data); i++) { > + struct iwm_rxba_data *rxba = &sc->sc_rxba_data[i]; > + rxba->baid = IWM_RX_REORDER_DATA_INVALID_BAID; > + rxba->sc = sc; > + timeout_set(&rxba->session_timer, iwm_rx_ba_session_expired, > + rxba); > + timeout_set(&rxba->reorder_buf.reorder_timer, > + iwm_reorder_timer_expired, &rxba->reorder_buf); > + for (j = 0; j < nitems(rxba->entries); j++) > + ml_init(&rxba->entries[j].frames); > + } > task_set(&sc->init_task, iwm_init_task, sc); > task_set(&sc->newstate_task, iwm_newstate_task, sc); > task_set(&sc->ba_task, iwm_ba_task, sc); > blob - 201ce69014b9422335a6d698cd4a3cc3f314b2b5 > blob + b2c61cbc20324f3871a7dc6fab9d68de97795a17 > --- sys/dev/pci/if_iwmreg.h > +++ sys/dev/pci/if_iwmreg.h > @@ -3137,6 +3137,9 @@ struct iwm_rx_mpdu_res_start { > #define IWM_RX_MPDU_MFLG2_PAD 0x20 > #define IWM_RX_MPDU_MFLG2_AMSDU 0x40 > > +#define IWM_RX_MPDU_AMSDU_SUBFRAME_IDX_MASK 0x7f > +#define IWM_RX_MPDU_AMSDU_LAST_SUBFRAME 0x80 > + > #define IWM_RX_MPDU_PHY_AMPDU (1 << 5) > #define IWM_RX_MPDU_PHY_AMPDU_TOGGLE (1 << 6) > #define IWM_RX_MPDU_PHY_SHORT_PREAMBLE (1 << 7) > @@ -3167,6 +3170,15 @@ struct iwm_rx_mpdu_desc_v1 { > }; > } __packed; > > +#define IWM_RX_REORDER_DATA_INVALID_BAID 0x7f > + > +#define IWM_RX_MPDU_REORDER_NSSN_MASK 0x00000fff > +#define IWM_RX_MPDU_REORDER_SN_MASK 0x00fff000 > +#define IWM_RX_MPDU_REORDER_SN_SHIFT 12 > +#define IWM_RX_MPDU_REORDER_BAID_MASK 0x7f000000 > +#define IWM_RX_MPDU_REORDER_BAID_SHIFT 24 > +#define IWM_RX_MPDU_REORDER_BA_OLD_SN 0x80000000 > + > struct iwm_rx_mpdu_desc { > uint16_t mpdu_len; > uint8_t mac_flags1; > @@ -4627,6 +4639,7 @@ struct iwm_lq_cmd { > /* > * TID for non QoS frames - to be written in tid_tspec > */ > +#define IWM_MAX_TID_COUNT 8 > #define IWM_TID_NON_QOS IWM_MAX_TID_COUNT > > /* > blob - 24c965b1a8e0e97c47b00f1a80a26bd50c1d46e9 > blob + 14c16d3a321a9c10496a90a582886f9de8853570 > --- sys/dev/pci/if_iwmvar.h > +++ sys/dev/pci/if_iwmvar.h > @@ -361,6 +361,99 @@ struct iwm_bf_data { > int last_cqm_event; > }; > > +/** > + * struct iwm_reorder_buffer - per ra/tid/queue reorder buffer > + * @head_sn: reorder window head sn > + * @num_stored: number of mpdus stored in the buffer > + * @buf_size: the reorder buffer size as set by the last addba request > + * @queue: queue of this reorder buffer > + * @last_amsdu: track last ASMDU SN for duplication detection > + * @last_sub_index: track ASMDU sub frame index for duplication detection > + * @reorder_timer: timer for frames are in the reorder buffer. For AMSDU > + * it is the time of last received sub-frame > + * @removed: prevent timer re-arming > + * @valid: reordering is valid for this queue > + * @consec_oldsn_drops: consecutive drops due to old SN > + * @consec_oldsn_ampdu_gp2: A-MPDU GP2 timestamp to track > + * when to apply old SN consecutive drop workaround > + * @consec_oldsn_prev_drop: track whether or not an MPDU > + * that was single/part of the previous A-MPDU was > + * dropped due to old SN > + */ > +struct iwm_reorder_buffer { > + uint16_t head_sn; > + uint16_t num_stored; > + uint16_t buf_size; > + uint16_t last_amsdu; > + uint8_t last_sub_index; > + struct timeout reorder_timer; > + int removed; > + int valid; > + unsigned int consec_oldsn_drops; > + uint32_t consec_oldsn_ampdu_gp2; > + unsigned int consec_oldsn_prev_drop; > +#define IWM_AMPDU_CONSEC_DROPS_DELBA 10 > +}; > + > +/** > + * struct iwm_reorder_buf_entry - reorder buffer entry per frame sequence > number > + * @frames: list of mbufs stored (A-MSDU subframes share a sequence number) > + * @reorder_time: time the packet was stored in the reorder buffer > + */ > +struct iwm_reorder_buf_entry { > + struct mbuf_list frames; > + struct timeval reorder_time; > + uint32_t rx_pkt_status; > + int chanidx; > + int is_shortpre; > + uint32_t rate_n_flags; > + uint32_t device_timestamp; > + struct ieee80211_rxinfo rxi; > +}; > + > +/** > + * struct iwm_rxba_data - BA session data > + * @sta_id: station id > + * @tid: tid of the session > + * @baid: baid of the session > + * @timeout: the timeout set in the addba request > + * @entries_per_queue: # of buffers per queue > + * @last_rx: last rx timestamp, updated only if timeout passed from last > update > + * @session_timer: timer to check if BA session expired, runs at 2 * timeout > + * @sc: softc pointer, needed for timer context > + * @reorder_buf: reorder buffer > + * @reorder_buf_data: buffered frames, one entry per sequence number > + */ > +struct iwm_rxba_data { > + uint8_t sta_id; > + uint8_t tid; > + uint8_t baid; > + uint16_t timeout; > + uint16_t entries_per_queue; > + struct timeval last_rx; > + struct timeout session_timer; > + struct iwm_softc *sc; > + struct iwm_reorder_buffer reorder_buf; > + struct iwm_reorder_buf_entry entries[IEEE80211_BA_MAX_WINSZ]; > +}; > + > +static inline struct iwm_rxba_data * > +iwm_rxba_data_from_reorder_buf(struct iwm_reorder_buffer *buf) > +{ > + return (void *)((uint8_t *)buf - > + offsetof(struct iwm_rxba_data, reorder_buf)); > +} > + > +/** > + * struct iwm_rxq_dup_data - per station per rx queue data > + * @last_seq: last sequence per tid for duplicate packet detection > + * @last_sub_frame: last subframe packet > + */ > +struct iwm_rxq_dup_data { > + uint16_t last_seq[IWM_MAX_TID_COUNT + 1]; > + uint8_t last_sub_frame[IWM_MAX_TID_COUNT + 1]; > +}; > + > struct iwm_softc { > struct device sc_dev; > struct ieee80211com sc_ic; > @@ -379,10 +472,11 @@ struct iwm_softc { > > /* Task for firmware BlockAck setup/teardown and its arguments. */ > struct task ba_task; > - int ba_start; > - int ba_tid; > - uint16_t ba_ssn; > - uint16_t ba_winsize; > + uint32_t ba_start_tidmask; > + uint32_t ba_stop_tidmask; > + uint16_t ba_ssn[IWM_MAX_TID_COUNT]; > + uint16_t ba_winsize[IWM_MAX_TID_COUNT]; > + int ba_timeout_val[IWM_MAX_TID_COUNT]; > > /* Task for HT protection updates. */ > struct task htprot_task; > @@ -495,6 +589,8 @@ struct iwm_softc { > > struct iwm_rx_phy_info sc_last_phy_info; > int sc_ampdu_ref; > +#define IWM_MAX_BAID 32 > + struct iwm_rxba_data sc_rxba_data[IWM_MAX_BAID]; > > uint32_t sc_time_event_uid; > > @@ -548,6 +644,8 @@ struct iwm_node { > struct ieee80211_amrr_node in_amn; > struct ieee80211_ra_node in_rn; > int lq_rate_mismatch; > + > + struct iwm_rxq_dup_data dup_data; > }; > #define IWM_STATION_ID 0 > #define IWM_AUX_STA_ID 1 > blob - a2de00f7bcdef99ced5d09da5e9b4bc8615156bd > blob + 8532becbec317b00ee9ace0588e8bb4b9216180e > --- sys/net80211/ieee80211_input.c > +++ sys/net80211/ieee80211_input.c > @@ -59,7 +59,8 @@ > #include <net80211/ieee80211_priv.h> > > struct mbuf *ieee80211_input_hwdecrypt(struct ieee80211com *, > - struct ieee80211_node *, struct mbuf *); > + struct ieee80211_node *, struct mbuf *, > + struct ieee80211_rxinfo *rxi); > struct mbuf *ieee80211_defrag(struct ieee80211com *, struct mbuf *, > int); > void ieee80211_defrag_timeout(void *); > void ieee80211_input_ba(struct ieee80211com *, struct mbuf *, > @@ -153,7 +154,7 @@ ieee80211_get_hdrlen(const struct ieee80211_frame *wh) > /* Post-processing for drivers which perform decryption in hardware. */ > struct mbuf * > ieee80211_input_hwdecrypt(struct ieee80211com *ic, struct ieee80211_node *ni, > - struct mbuf *m) > + struct mbuf *m, struct ieee80211_rxinfo *rxi) > { > struct ieee80211_key *k; > struct ieee80211_frame *wh; > @@ -188,7 +189,12 @@ ieee80211_input_hwdecrypt(struct ieee80211com *ic, str > } > if (ieee80211_ccmp_get_pn(&pn, &prsc, m, k) != 0) > return NULL; > - if (pn <= *prsc) { > + if (rxi->rxi_flags & IEEE80211_RXI_HWDEC_SAME_PN) { > + if (pn < *prsc) { > + ic->ic_stats.is_ccmp_replays++; > + return NULL; > + } > + } else if (pn <= *prsc) { > ic->ic_stats.is_ccmp_replays++; > return NULL; > } > @@ -213,8 +219,12 @@ ieee80211_input_hwdecrypt(struct ieee80211com *ic, str > } > if (ieee80211_tkip_get_tsc(&pn, &prsc, m, k) != 0) > return NULL; > - > - if (pn <= *prsc) { > + if (rxi->rxi_flags & IEEE80211_RXI_HWDEC_SAME_PN) { > + if (pn < *prsc) { > + ic->ic_stats.is_tkip_replays++; > + return NULL; > + } > + } else if (pn <= *prsc) { > ic->ic_stats.is_tkip_replays++; > return NULL; > } > @@ -381,7 +391,13 @@ ieee80211_inputm(struct ifnet *ifp, struct mbuf *m, st > orxseq = &ni->ni_qos_rxseqs[tid]; > else > orxseq = &ni->ni_rxseq; > - if ((wh->i_fc[1] & IEEE80211_FC1_RETRY) && > + if (rxi->rxi_flags & IEEE80211_RXI_SAME_SEQ) { > + if (nrxseq != *orxseq) { > + /* duplicate, silently discarded */ > + ic->ic_stats.is_rx_dup++; > + goto out; > + } > + } else if ((wh->i_fc[1] & IEEE80211_FC1_RETRY) && > nrxseq == *orxseq) { > /* duplicate, silently discarded */ > ic->ic_stats.is_rx_dup++; > @@ -557,7 +573,7 @@ ieee80211_inputm(struct ifnet *ifp, struct mbuf *m, st > goto err; > } > } else { > - m = ieee80211_input_hwdecrypt(ic, ni, m); > + m = ieee80211_input_hwdecrypt(ic, ni, m, rxi); > if (m == NULL) > goto err; > } > @@ -2758,10 +2774,7 @@ ieee80211_recv_addba_req(struct ieee80211com *ic, stru > ba->ba_params = (params & IEEE80211_ADDBA_BA_POLICY); > ba->ba_params |= ((ba->ba_winsize << IEEE80211_ADDBA_BUFSZ_SHIFT) | > (tid << IEEE80211_ADDBA_TID_SHIFT)); > -#if 0 > - /* iwm(4) 9k and iwx(4) need more work before AMSDU can be enabled. */ > ba->ba_params |= IEEE80211_ADDBA_AMSDU; > -#endif > ba->ba_winstart = ssn; > ba->ba_winend = (ba->ba_winstart + ba->ba_winsize - 1) & 0xfff; > /* allocate and setup our reordering buffer */ > blob - 510eb534ae7ab07e69845b57367b9b79d523916e > blob + 86fe60d986e5256ea052b7e0cf8c256aa8a9df8c > --- sys/net80211/ieee80211_node.h > +++ sys/net80211/ieee80211_node.h > @@ -182,6 +182,8 @@ struct ieee80211_rxinfo { > }; > #define IEEE80211_RXI_HWDEC 0x00000001 > #define IEEE80211_RXI_AMPDU_DONE 0x00000002 > +#define IEEE80211_RXI_HWDEC_SAME_PN 0x00000004 > +#define IEEE80211_RXI_SAME_SEQ 0x00000008 > > /* Block Acknowledgement Record */ > struct ieee80211_tx_ba { > blob - 5bd45d993b558bac50a513c1c4422508d96f44ba > blob + b5c40528766d7aff8f21faf2975fd9c02257cf6c > --- sys/net80211/ieee80211_proto.c > +++ sys/net80211/ieee80211_proto.c > @@ -695,10 +695,7 @@ ieee80211_addba_request(struct ieee80211com *ic, struc > ba->ba_params = > (ba->ba_winsize << IEEE80211_ADDBA_BUFSZ_SHIFT) | > (tid << IEEE80211_ADDBA_TID_SHIFT); > -#if 0 > - /* iwm(4) 9k and iwx(4) need more work before AMSDU can be enabled. */ > ba->ba_params |= IEEE80211_ADDBA_AMSDU; > -#endif > if ((ic->ic_htcaps & IEEE80211_HTCAP_DELAYEDBA) == 0) > /* immediate BA */ > ba->ba_params |= IEEE80211_ADDBA_BA_POLICY; > -- wq: ~uw