mac80211: report tx status for dropped frames, should fix some remaining stability...
[openwrt.git] / package / mac80211 / patches / 300-pending_work.patch
index f7a0aed..56f5af9 100644 (file)
        WLAN_STA_BLOCK_BA,
 --- a/net/mac80211/status.c
 +++ b/net/mac80211/status.c
-@@ -517,29 +517,41 @@ void ieee80211_tx_status(struct ieee8021
+@@ -324,6 +324,75 @@ static void ieee80211_add_tx_radiotap_he
  
-       if (info->flags & IEEE80211_TX_INTFL_NL80211_FRAME_TX) {
-               u64 cookie = (unsigned long)skb;
-+              bool found = false;
+ }
++static void ieee80211_report_used_skb(struct ieee80211_local *local,
++                                    struct sk_buff *skb, bool dropped)
++{
++      struct ieee80211_tx_info *info = IEEE80211_SKB_CB(skb);
++      struct ieee80211_hdr *hdr = (void *)skb->data;
++      bool acked = info->flags & IEEE80211_TX_STAT_ACK;
++
++      if (dropped)
++              acked = false;
++
++      if (info->flags & IEEE80211_TX_INTFL_NL80211_FRAME_TX) {
++              struct ieee80211_sub_if_data *sdata = NULL;
++              struct ieee80211_sub_if_data *iter_sdata;
++              u64 cookie = (unsigned long)skb;
++
++              rcu_read_lock();
++
++              if (skb->dev) {
++                      list_for_each_entry_rcu(iter_sdata, &local->interfaces,
++                                              list) {
++                              if (!iter_sdata->dev)
++                                      continue;
++
++                              if (skb->dev == iter_sdata->dev) {
++                                      sdata = iter_sdata;
++                                      break;
++                              }
++                      }
++              } else {
++                      sdata = rcu_dereference(local->p2p_sdata);
++              }
++
++              if (!sdata)
++                      skb->dev = NULL;
++              else if (ieee80211_is_nullfunc(hdr->frame_control) ||
++                       ieee80211_is_qos_nullfunc(hdr->frame_control)) {
++                      cfg80211_probe_status(sdata->dev, hdr->addr1,
++                                            cookie, acked, GFP_ATOMIC);
++              } else {
++                      cfg80211_mgmt_tx_status(&sdata->wdev, cookie, skb->data,
++                                              skb->len, acked, GFP_ATOMIC);
++              }
++
++              rcu_read_unlock();
++      }
 +
-               acked = info->flags & IEEE80211_TX_STAT_ACK;
++      if (unlikely(info->ack_frame_id)) {
++              struct sk_buff *ack_skb;
++              unsigned long flags;
++
++              spin_lock_irqsave(&local->ack_status_lock, flags);
++              ack_skb = idr_find(&local->ack_status_frames,
++                                 info->ack_frame_id);
++              if (ack_skb)
++                      idr_remove(&local->ack_status_frames,
++                                 info->ack_frame_id);
++              spin_unlock_irqrestore(&local->ack_status_lock, flags);
++
++              if (ack_skb) {
++                      if (!dropped) {
++                              /* consumes ack_skb */
++                              skb_complete_wifi_ack(ack_skb, acked);
++                      } else {
++                              dev_kfree_skb_any(ack_skb);
++                      }
++              }
++      }
++}
++
+ /*
+  * Use a static threshold for now, best value to be determined
+  * by testing ...
+@@ -515,50 +584,7 @@ void ieee80211_tx_status(struct ieee8021
+                                       msecs_to_jiffies(10));
+       }
  
+-      if (info->flags & IEEE80211_TX_INTFL_NL80211_FRAME_TX) {
+-              u64 cookie = (unsigned long)skb;
+-              acked = info->flags & IEEE80211_TX_STAT_ACK;
+-
 -              if (ieee80211_is_nullfunc(hdr->frame_control) ||
 -                  ieee80211_is_qos_nullfunc(hdr->frame_control)) {
 -                      cfg80211_probe_status(skb->dev, hdr->addr1,
 -                              skb->len, acked, GFP_ATOMIC);
 -              } else {
 -                      struct ieee80211_sub_if_data *p2p_sdata;
-+              rcu_read_lock();
+-
 -                      rcu_read_lock();
-+              list_for_each_entry_rcu(sdata, &local->interfaces, list) {
-+                      if (!sdata->dev)
-+                              continue;
+-
 -                      p2p_sdata = rcu_dereference(local->p2p_sdata);
 -                      if (p2p_sdata) {
 -                              cfg80211_mgmt_tx_status(
 -                                      skb->len, acked, GFP_ATOMIC);
 -                      }
 -                      rcu_read_unlock();
-+                      if (skb->dev != sdata->dev)
-+                              continue;
-+
-+                      found = true;
-+                      break;
-               }
-+
-+              if (!skb->dev) {
-+                      sdata = rcu_dereference(local->p2p_sdata);
-+                      if (sdata)
-+                              found = true;
-+              }
-+
-+              if (!found)
-+                      skb->dev = NULL;
-+              else if (ieee80211_is_nullfunc(hdr->frame_control) ||
-+                       ieee80211_is_qos_nullfunc(hdr->frame_control)) {
-+                      cfg80211_probe_status(sdata->dev, hdr->addr1,
-+                                            cookie, acked, GFP_ATOMIC);
-+              } else {
-+                      cfg80211_mgmt_tx_status(&sdata->wdev, cookie, skb->data,
-+                                              skb->len, acked, GFP_ATOMIC);
-+              }
-+
-+              rcu_read_unlock();
-       }
+-              }
+-      }
+-
+-      if (unlikely(info->ack_frame_id)) {
+-              struct sk_buff *ack_skb;
+-              unsigned long flags;
+-
+-              spin_lock_irqsave(&local->ack_status_lock, flags);
+-              ack_skb = idr_find(&local->ack_status_frames,
+-                                 info->ack_frame_id);
+-              if (ack_skb)
+-                      idr_remove(&local->ack_status_frames,
+-                                 info->ack_frame_id);
+-              spin_unlock_irqrestore(&local->ack_status_lock, flags);
+-
+-              /* consumes ack_skb */
+-              if (ack_skb)
+-                      skb_complete_wifi_ack(ack_skb,
+-                              info->flags & IEEE80211_TX_STAT_ACK);
+-      }
++      ieee80211_report_used_skb(local, skb, false);
  
-       if (unlikely(info->ack_frame_id)) {
+       /* this was a transmitted frame, but now we want to reuse it */
+       skb_orphan(skb);
+@@ -634,25 +660,8 @@ EXPORT_SYMBOL(ieee80211_report_low_ack);
+ void ieee80211_free_txskb(struct ieee80211_hw *hw, struct sk_buff *skb)
+ {
+       struct ieee80211_local *local = hw_to_local(hw);
+-      struct ieee80211_tx_info *info = IEEE80211_SKB_CB(skb);
+-
+-      if (unlikely(info->ack_frame_id)) {
+-              struct sk_buff *ack_skb;
+-              unsigned long flags;
+-
+-              spin_lock_irqsave(&local->ack_status_lock, flags);
+-              ack_skb = idr_find(&local->ack_status_frames,
+-                                 info->ack_frame_id);
+-              if (ack_skb)
+-                      idr_remove(&local->ack_status_frames,
+-                                 info->ack_frame_id);
+-              spin_unlock_irqrestore(&local->ack_status_lock, flags);
+-
+-              /* consumes ack_skb */
+-              if (ack_skb)
+-                      dev_kfree_skb_any(ack_skb);
+-      }
++      ieee80211_report_used_skb(local, skb, true);
+       dev_kfree_skb_any(skb);
+ }
+ EXPORT_SYMBOL(ieee80211_free_txskb);
 --- a/drivers/net/wireless/p54/main.c
 +++ b/drivers/net/wireless/p54/main.c
 @@ -139,6 +139,7 @@ static int p54_beacon_format_ie_tim(stru
  
        ieee80211_bss_info_change_notify(sdata, changed);
  }
+@@ -3526,6 +3572,7 @@ int ieee80211_mgd_deauth(struct ieee8021
+ {
+       struct ieee80211_if_managed *ifmgd = &sdata->u.mgd;
+       u8 frame_buf[DEAUTH_DISASSOC_LEN];
++      bool tx = !req->local_state_change;
+       mutex_lock(&ifmgd->mtx);
+@@ -3542,12 +3589,12 @@ int ieee80211_mgd_deauth(struct ieee8021
+       if (ifmgd->associated &&
+           ether_addr_equal(ifmgd->associated->bssid, req->bssid)) {
+               ieee80211_set_disassoc(sdata, IEEE80211_STYPE_DEAUTH,
+-                                     req->reason_code, true, frame_buf);
++                                     req->reason_code, tx, frame_buf);
+       } else {
+               drv_mgd_prepare_tx(sdata->local, sdata);
+               ieee80211_send_deauth_disassoc(sdata, req->bssid,
+                                              IEEE80211_STYPE_DEAUTH,
+-                                             req->reason_code, true,
++                                             req->reason_code, tx,
+                                              frame_buf);
+       }
 --- a/net/mac80211/sta_info.c
 +++ b/net/mac80211/sta_info.c
 @@ -674,7 +674,7 @@ int __must_check __sta_info_destroy(stru
  
        if (local->user_power_level >= 0)
                power = min(power, local->user_power_level);
+--- a/include/net/cfg80211.h
++++ b/include/net/cfg80211.h
+@@ -1218,6 +1218,7 @@ struct cfg80211_deauth_request {
+       const u8 *ie;
+       size_t ie_len;
+       u16 reason_code;
++      bool local_state_change;
+ };
+ /**
+--- a/net/wireless/mlme.c
++++ b/net/wireless/mlme.c
+@@ -457,20 +457,14 @@ int __cfg80211_mlme_deauth(struct cfg802
+               .reason_code = reason,
+               .ie = ie,
+               .ie_len = ie_len,
++              .local_state_change = local_state_change,
+       };
+       ASSERT_WDEV_LOCK(wdev);
+-      if (local_state_change) {
+-              if (wdev->current_bss &&
+-                  ether_addr_equal(wdev->current_bss->pub.bssid, bssid)) {
+-                      cfg80211_unhold_bss(wdev->current_bss);
+-                      cfg80211_put_bss(&wdev->current_bss->pub);
+-                      wdev->current_bss = NULL;
+-              }
+-
++      if (local_state_change && (!wdev->current_bss ||
++          !ether_addr_equal(wdev->current_bss->pub.bssid, bssid)))
+               return 0;
+-      }
+       return rdev->ops->deauth(&rdev->wiphy, dev, &req);
+ }
+--- a/drivers/net/wireless/ath/ath9k/xmit.c
++++ b/drivers/net/wireless/ath/ath9k/xmit.c
+@@ -386,7 +386,7 @@ static void ath_tx_complete_aggr(struct 
+       u16 seq_st = 0, acked_cnt = 0, txfail_cnt = 0, seq_first;
+       u32 ba[WME_BA_BMP_SIZE >> 5];
+       int isaggr, txfail, txpending, sendbar = 0, needreset = 0, nbad = 0;
+-      bool rc_update = true;
++      bool rc_update = true, isba;
+       struct ieee80211_tx_rate rates[4];
+       struct ath_frame_info *fi;
+       int nframes;
+@@ -430,13 +430,17 @@ static void ath_tx_complete_aggr(struct 
+       tidno = ieee80211_get_qos_ctl(hdr)[0] & IEEE80211_QOS_CTL_TID_MASK;
+       tid = ATH_AN_2_TID(an, tidno);
+       seq_first = tid->seq_start;
++      isba = ts->ts_flags & ATH9K_TX_BA;
+       /*
+        * The hardware occasionally sends a tx status for the wrong TID.
+        * In this case, the BA status cannot be considered valid and all
+        * subframes need to be retransmitted
++       *
++       * Only BlockAcks have a TID and therefore normal Acks cannot be
++       * checked
+        */
+-      if (tidno != ts->tid)
++      if (isba && tidno != ts->tid)
+               txok = false;
+       isaggr = bf_isaggr(bf);