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