iwlwifi: mvm: loosen nssn comparison to reorder buffer head
authorSara Sharon <sara.sharon@intel.com>
Wed, 30 Mar 2016 17:04:48 +0000 (20:04 +0300)
committerLuca Coelho <luciano.coelho@intel.com>
Tue, 10 May 2016 19:34:01 +0000 (22:34 +0300)
Up till now, the reorder buffer uses standard spec based comparison
when comparing the buffer status to NSSN. This indeed works for the
regular case, since we shouldn't cross the 2048 boundary without
getting a frame release notification.
However, this is problematic due to packet filtering that may be
performed by the FW while we are in d0i3. Theoretically we may
filter over 2048 packets, and then the check of the NSSN will get
incorrect.
Change the comparison to always trust nssn unless it is 64 or less
frames behind the head - which might happen due to a timeout.
This new comparison is to be used only when comparing reorder buffer
head with nssn, and not when comparing the packet SN to nssn or
reorder buffer head.
Put this in a separate commit as the logic is a bit tricky and
stands for its own commit message.

Signed-off-by: Sara Sharon <sara.sharon@intel.com>
Signed-off-by: Luca Coelho <luciano.coelho@intel.com>
drivers/net/wireless/intel/iwlwifi/mvm/rxmq.c

index 5fe7a0e..ac2c571 100644 (file)
@@ -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);