diff --git a/drivers/net/wireless/intel/iwlwifi/mvm/rxmq.c b/drivers/net/wireless/intel/iwlwifi/mvm/rxmq.c index 5fe7a0e16b42..ac2c5718e454 100644 --- a/drivers/net/wireless/intel/iwlwifi/mvm/rxmq.c +++ b/drivers/net/wireless/intel/iwlwifi/mvm/rxmq.c @@ -395,6 +395,18 @@ int iwl_mvm_notify_rx_queue(struct iwl_mvm *mvm, u32 rxq_mask, return ret; } +/* + * 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. + */ +static bool iwl_mvm_is_sn_less(u16 sn1, u16 sn2, u16 buffer_size) +{ + return ieee80211_sn_less(sn1, sn2) && + !ieee80211_sn_less(sn1, sn2 - buffer_size); +} + #define RX_REORDER_BUF_TIMEOUT_MQ (HZ / 10) static void iwl_mvm_release_frames(struct iwl_mvm *mvm, @@ -408,10 +420,10 @@ static void iwl_mvm_release_frames(struct iwl_mvm *mvm, lockdep_assert_held(&reorder_buf->lock); /* ignore nssn smaller than head sn - this can happen due to timeout */ - if (ieee80211_sn_less(nssn, ssn)) + if (iwl_mvm_is_sn_less(nssn, ssn, reorder_buf->buf_size)) return; - while (ieee80211_sn_less(ssn, nssn)) { + while (iwl_mvm_is_sn_less(ssn, nssn, reorder_buf->buf_size)) { int index = ssn % reorder_buf->buf_size; struct sk_buff_head *skb_list = &reorder_buf->entries[index]; struct sk_buff *skb; @@ -625,7 +637,8 @@ static bool iwl_mvm_reorder(struct iwl_mvm *mvm, * rest of the function will take of storing it and releasing up to the * nssn */ - if (!ieee80211_sn_less(nssn, buffer->head_sn + buffer->buf_size)) { + if (!iwl_mvm_is_sn_less(nssn, buffer->head_sn + buffer->buf_size, + buffer->buf_size)) { u16 min_sn = ieee80211_sn_less(sn, nssn) ? sn : nssn; iwl_mvm_release_frames(mvm, sta, napi, buffer, min_sn); @@ -637,7 +650,8 @@ static bool iwl_mvm_reorder(struct iwl_mvm *mvm, /* release immediately if allowed by nssn and no stored frames */ if (!buffer->num_stored && ieee80211_sn_less(sn, nssn)) { - if (ieee80211_sn_less(buffer->head_sn, nssn)) + if (iwl_mvm_is_sn_less(buffer->head_sn, nssn, + buffer->buf_size)) buffer->head_sn = nssn; /* No need to update AMSDU last SN - we are moving the head */ spin_unlock_bh(&buffer->lock);