rt2x00: merge a fix for random tx stalls
[openwrt/openwrt.git] / package / mac80211 / patches / 300-pending_work.patch
index 14082e76a3063dd9c1e34c9059e327bd808ba314..30ce96e1703b96e278b85c3e0bc3918e16e86285 100644 (file)
  #ifdef CONFIG_MAC80211_VERBOSE_PS_DEBUG
                printk(KERN_DEBUG "STA %pM aid %d: PS buffer for AC %d\n",
                       sta->sta.addr, sta->sta.aid, ac);
+@@ -1060,6 +1065,7 @@ static bool ieee80211_tx_prep_agg(struct
+ {
+       bool queued = false;
+       bool reset_agg_timer = false;
++      struct sk_buff *purge_skb = NULL;
+       if (test_bit(HT_AGG_STATE_OPERATIONAL, &tid_tx->state)) {
+               info->flags |= IEEE80211_TX_CTL_AMPDU;
+@@ -1101,8 +1107,13 @@ static bool ieee80211_tx_prep_agg(struct
+                       info->control.vif = &tx->sdata->vif;
+                       info->flags |= IEEE80211_TX_INTFL_NEED_TXPROCESSING;
+                       __skb_queue_tail(&tid_tx->pending, skb);
++                      if (skb_queue_len(&tid_tx->pending) > STA_MAX_TX_BUFFER)
++                              purge_skb = __skb_dequeue(&tid_tx->pending);
+               }
+               spin_unlock(&tx->sta->lock);
++
++              if (purge_skb)
++                      dev_kfree_skb(purge_skb);
+       }
+       /* reset session timer */
 --- a/net/mac80211/ieee80211_i.h
 +++ b/net/mac80211/ieee80211_i.h
 @@ -480,7 +480,7 @@ struct ieee80211_if_ibss {
  }
  
  /*
-@@ -1011,6 +1010,8 @@ static int ath9k_rx_skb_preprocess(struc
+@@ -988,8 +987,6 @@ static int ath9k_rx_skb_preprocess(struc
+ {
+       struct ath_hw *ah = common->ah;
+-      memset(rx_status, 0, sizeof(struct ieee80211_rx_status));
+-
+       /*
+        * everything but the rate is checked here, the rate check is done
+        * separately to avoid doing two lookups for a rate for each frame.
+@@ -1011,6 +1008,8 @@ static int ath9k_rx_skb_preprocess(struc
        rx_status->signal = ah->noise + rx_stats->rs_rssi;
        rx_status->antenna = rx_stats->rs_antenna;
        rx_status->flag |= RX_FLAG_MACTIME_MPDU;
  
        return 0;
  }
+@@ -1845,6 +1844,8 @@ int ath_rx_tasklet(struct ath_softc *sc,
+               if (sc->sc_flags & SC_OP_RXFLUSH)
+                       goto requeue_drop_frag;
++              memset(rxs, 0, sizeof(struct ieee80211_rx_status));
++
+               rxs->mactime = (tsf & ~0xffffffffULL) | rs.rs_tstamp;
+               if (rs.rs_tstamp > tsf_lower &&
+                   unlikely(rs.rs_tstamp - tsf_lower > 0x10000000))
 --- a/drivers/net/wireless/ath/ath9k/beacon.c
 +++ b/drivers/net/wireless/ath/ath9k/beacon.c
 @@ -91,7 +91,7 @@ static void ath_beacon_setup(struct ath_
                mode = ATH9K_PM_NETWORK_SLEEP;
        else
                goto unlock;
-@@ -2300,6 +2302,7 @@ static int ath9k_tx_last_beacon(struct i
+@@ -1955,6 +1957,7 @@ static void ath9k_config_bss(struct ath_
+               sc->sc_flags &= ~SC_OP_ANI_RUN;
+               del_timer_sync(&common->ani.timer);
+               memset(&sc->caldata, 0, sizeof(sc->caldata));
++              ath9k_hw_ani_init(sc->sc_ah);
+       }
+ }
+@@ -2300,6 +2303,7 @@ static int ath9k_tx_last_beacon(struct i
        struct ath_vif *avp;
        struct ath_buf *bf;
        struct ath_tx_status ts;
        int status;
  
        vif = sc->beacon.bslot[0];
-@@ -2310,7 +2313,7 @@ static int ath9k_tx_last_beacon(struct i
+@@ -2310,7 +2314,7 @@ static int ath9k_tx_last_beacon(struct i
        if (!avp->is_bslot_active)
                return 0;
  
        u32 rx_fifo_hwsize;
  };
  
+--- a/drivers/net/wireless/ath/ath9k/ani.c
++++ b/drivers/net/wireless/ath/ath9k/ani.c
+@@ -46,8 +46,8 @@ static const struct ani_ofdm_level_entry
+       {  5,  4,  1  }, /* lvl 5 */
+       {  6,  5,  1  }, /* lvl 6 */
+       {  7,  6,  1  }, /* lvl 7 */
+-      {  7,  7,  1  }, /* lvl 8 */
+-      {  7,  8,  0  }  /* lvl 9 */
++      {  7,  6,  0  }, /* lvl 8 */
++      {  7,  7,  0  }  /* lvl 9 */
+ };
+ #define ATH9K_ANI_OFDM_NUM_LEVEL \
+       ARRAY_SIZE(ofdm_level_table)
+@@ -91,8 +91,8 @@ static const struct ani_cck_level_entry 
+       {  4,  0  }, /* lvl 4 */
+       {  5,  0  }, /* lvl 5 */
+       {  6,  0  }, /* lvl 6 */
+-      {  7,  0  }, /* lvl 7 (only for high rssi) */
+-      {  8,  0  }  /* lvl 8 (only for high rssi) */
++      {  6,  0  }, /* lvl 7 (only for high rssi) */
++      {  7,  0  }  /* lvl 8 (only for high rssi) */
+ };
+ #define ATH9K_ANI_CCK_NUM_LEVEL \
+@@ -290,16 +290,9 @@ static void ath9k_hw_set_ofdm_nil(struct
+                                    ATH9K_ANI_FIRSTEP_LEVEL,
+                                    entry_ofdm->fir_step_level);
+-      if ((ah->opmode != NL80211_IFTYPE_STATION &&
+-           ah->opmode != NL80211_IFTYPE_ADHOC) ||
+-          aniState->noiseFloor <= aniState->rssiThrHigh) {
+-              if (aniState->ofdmWeakSigDetectOff)
+-                      /* force on ofdm weak sig detect */
+-                      ath9k_hw_ani_control(ah,
+-                              ATH9K_ANI_OFDM_WEAK_SIGNAL_DETECTION,
+-                                           true);
+-              else if (aniState->ofdmWeakSigDetectOff ==
+-                       entry_ofdm->ofdm_weak_signal_on)
++      if ((aniState->noiseFloor >= aniState->rssiThrHigh) &&
++          (aniState->ofdmWeakSigDetectOff !=
++           entry_ofdm->ofdm_weak_signal_on)) {
+                       ath9k_hw_ani_control(ah,
+                               ATH9K_ANI_OFDM_WEAK_SIGNAL_DETECTION,
+                               entry_ofdm->ofdm_weak_signal_on);
+@@ -717,26 +710,30 @@ void ath9k_hw_ani_monitor(struct ath_hw 
+               ofdmPhyErrRate, aniState->cckNoiseImmunityLevel,
+               cckPhyErrRate, aniState->ofdmsTurn);
+-      if (aniState->listenTime > 5 * ah->aniperiod) {
+-              if (ofdmPhyErrRate <= ah->config.ofdm_trig_low &&
+-                  cckPhyErrRate <= ah->config.cck_trig_low) {
++      if (aniState->listenTime > ah->aniperiod) {
++              if (cckPhyErrRate < ah->config.cck_trig_low &&
++                  ((ofdmPhyErrRate < ah->config.ofdm_trig_low &&
++                    aniState->ofdmNoiseImmunityLevel <
++                    ATH9K_ANI_OFDM_DEF_LEVEL) ||
++                   (ofdmPhyErrRate < ATH9K_ANI_OFDM_TRIG_LOW_ABOVE_INI &&
++                    aniState->ofdmNoiseImmunityLevel >=
++                    ATH9K_ANI_OFDM_DEF_LEVEL))) {
+                       ath9k_hw_ani_lower_immunity(ah);
+                       aniState->ofdmsTurn = !aniState->ofdmsTurn;
+-              }
+-              ath9k_ani_restart(ah);
+-      } else if (aniState->listenTime > ah->aniperiod) {
+-              /* check to see if need to raise immunity */
+-              if (ofdmPhyErrRate > ah->config.ofdm_trig_high &&
+-                  (cckPhyErrRate <= ah->config.cck_trig_high ||
+-                   aniState->ofdmsTurn)) {
++              } else if ((ofdmPhyErrRate > ah->config.ofdm_trig_high &&
++                          aniState->ofdmNoiseImmunityLevel >=
++                          ATH9K_ANI_OFDM_DEF_LEVEL) ||
++                         (ofdmPhyErrRate >
++                          ATH9K_ANI_OFDM_TRIG_HIGH_BELOW_INI &&
++                          aniState->ofdmNoiseImmunityLevel <
++                          ATH9K_ANI_OFDM_DEF_LEVEL)) {
+                       ath9k_hw_ani_ofdm_err_trigger(ah);
+-                      ath9k_ani_restart(ah);
+                       aniState->ofdmsTurn = false;
+               } else if (cckPhyErrRate > ah->config.cck_trig_high) {
+                       ath9k_hw_ani_cck_err_trigger(ah);
+-                      ath9k_ani_restart(ah);
+                       aniState->ofdmsTurn = true;
+               }
++              ath9k_ani_restart(ah);
+       }
+ }
+ EXPORT_SYMBOL(ath9k_hw_ani_monitor);
+@@ -911,3 +908,4 @@ void ath9k_hw_ani_init(struct ath_hw *ah
+       ath9k_ani_restart(ah);
+       ath9k_enable_mib_counters(ah);
+ }
++EXPORT_SYMBOL(ath9k_hw_ani_init);
+--- a/drivers/net/wireless/ath/ath9k/ani.h
++++ b/drivers/net/wireless/ath/ath9k/ani.h
+@@ -25,11 +25,13 @@
+ /* units are errors per second */
+ #define ATH9K_ANI_OFDM_TRIG_HIGH_OLD      500
+-#define ATH9K_ANI_OFDM_TRIG_HIGH_NEW      1000
++#define ATH9K_ANI_OFDM_TRIG_HIGH_NEW      3500
++#define ATH9K_ANI_OFDM_TRIG_HIGH_BELOW_INI 1000
+ /* units are errors per second */
+ #define ATH9K_ANI_OFDM_TRIG_LOW_OLD       200
+ #define ATH9K_ANI_OFDM_TRIG_LOW_NEW       400
++#define ATH9K_ANI_OFDM_TRIG_LOW_ABOVE_INI 900
+ /* units are errors per second */
+ #define ATH9K_ANI_CCK_TRIG_HIGH_OLD       200
+@@ -53,7 +55,7 @@
+ #define ATH9K_ANI_RSSI_THR_LOW            7
+ #define ATH9K_ANI_PERIOD_OLD              100
+-#define ATH9K_ANI_PERIOD_NEW              1000
++#define ATH9K_ANI_PERIOD_NEW              300
+ /* in ms */
+ #define ATH9K_ANI_POLLINTERVAL_OLD        100
+--- a/drivers/net/wireless/ath/ath9k/ar5008_phy.c
++++ b/drivers/net/wireless/ath/ath9k/ar5008_phy.c
+@@ -1056,46 +1056,8 @@ static bool ar5008_hw_ani_control_old(st
+               break;
+       }
+       case ATH9K_ANI_OFDM_WEAK_SIGNAL_DETECTION:{
+-              static const int m1ThreshLow[] = { 127, 50 };
+-              static const int m2ThreshLow[] = { 127, 40 };
+-              static const int m1Thresh[] = { 127, 0x4d };
+-              static const int m2Thresh[] = { 127, 0x40 };
+-              static const int m2CountThr[] = { 31, 16 };
+-              static const int m2CountThrLow[] = { 63, 48 };
+               u32 on = param ? 1 : 0;
+-              REG_RMW_FIELD(ah, AR_PHY_SFCORR_LOW,
+-                            AR_PHY_SFCORR_LOW_M1_THRESH_LOW,
+-                            m1ThreshLow[on]);
+-              REG_RMW_FIELD(ah, AR_PHY_SFCORR_LOW,
+-                            AR_PHY_SFCORR_LOW_M2_THRESH_LOW,
+-                            m2ThreshLow[on]);
+-              REG_RMW_FIELD(ah, AR_PHY_SFCORR,
+-                            AR_PHY_SFCORR_M1_THRESH,
+-                            m1Thresh[on]);
+-              REG_RMW_FIELD(ah, AR_PHY_SFCORR,
+-                            AR_PHY_SFCORR_M2_THRESH,
+-                            m2Thresh[on]);
+-              REG_RMW_FIELD(ah, AR_PHY_SFCORR,
+-                            AR_PHY_SFCORR_M2COUNT_THR,
+-                            m2CountThr[on]);
+-              REG_RMW_FIELD(ah, AR_PHY_SFCORR_LOW,
+-                            AR_PHY_SFCORR_LOW_M2COUNT_THR_LOW,
+-                            m2CountThrLow[on]);
+-
+-              REG_RMW_FIELD(ah, AR_PHY_SFCORR_EXT,
+-                            AR_PHY_SFCORR_EXT_M1_THRESH_LOW,
+-                            m1ThreshLow[on]);
+-              REG_RMW_FIELD(ah, AR_PHY_SFCORR_EXT,
+-                            AR_PHY_SFCORR_EXT_M2_THRESH_LOW,
+-                            m2ThreshLow[on]);
+-              REG_RMW_FIELD(ah, AR_PHY_SFCORR_EXT,
+-                            AR_PHY_SFCORR_EXT_M1_THRESH,
+-                            m1Thresh[on]);
+-              REG_RMW_FIELD(ah, AR_PHY_SFCORR_EXT,
+-                            AR_PHY_SFCORR_EXT_M2_THRESH,
+-                            m2Thresh[on]);
+-
+               if (on)
+                       REG_SET_BIT(ah, AR_PHY_SFCORR_LOW,
+                                   AR_PHY_SFCORR_LOW_USE_SELF_CORR_LOW);
+--- a/drivers/net/wireless/ath/ath9k/ar9003_phy.c
++++ b/drivers/net/wireless/ath/ath9k/ar9003_phy.c
+@@ -824,55 +824,6 @@ static bool ar9003_hw_ani_control(struct
+                * on == 0 means more noise imm
+                */
+               u32 on = param ? 1 : 0;
+-              /*
+-               * make register setting for default
+-               * (weak sig detect ON) come from INI file
+-               */
+-              int m1ThreshLow = on ?
+-                      aniState->iniDef.m1ThreshLow : m1ThreshLow_off;
+-              int m2ThreshLow = on ?
+-                      aniState->iniDef.m2ThreshLow : m2ThreshLow_off;
+-              int m1Thresh = on ?
+-                      aniState->iniDef.m1Thresh : m1Thresh_off;
+-              int m2Thresh = on ?
+-                      aniState->iniDef.m2Thresh : m2Thresh_off;
+-              int m2CountThr = on ?
+-                      aniState->iniDef.m2CountThr : m2CountThr_off;
+-              int m2CountThrLow = on ?
+-                      aniState->iniDef.m2CountThrLow : m2CountThrLow_off;
+-              int m1ThreshLowExt = on ?
+-                      aniState->iniDef.m1ThreshLowExt : m1ThreshLowExt_off;
+-              int m2ThreshLowExt = on ?
+-                      aniState->iniDef.m2ThreshLowExt : m2ThreshLowExt_off;
+-              int m1ThreshExt = on ?
+-                      aniState->iniDef.m1ThreshExt : m1ThreshExt_off;
+-              int m2ThreshExt = on ?
+-                      aniState->iniDef.m2ThreshExt : m2ThreshExt_off;
+-
+-              REG_RMW_FIELD(ah, AR_PHY_SFCORR_LOW,
+-                            AR_PHY_SFCORR_LOW_M1_THRESH_LOW,
+-                            m1ThreshLow);
+-              REG_RMW_FIELD(ah, AR_PHY_SFCORR_LOW,
+-                            AR_PHY_SFCORR_LOW_M2_THRESH_LOW,
+-                            m2ThreshLow);
+-              REG_RMW_FIELD(ah, AR_PHY_SFCORR,
+-                            AR_PHY_SFCORR_M1_THRESH, m1Thresh);
+-              REG_RMW_FIELD(ah, AR_PHY_SFCORR,
+-                            AR_PHY_SFCORR_M2_THRESH, m2Thresh);
+-              REG_RMW_FIELD(ah, AR_PHY_SFCORR,
+-                            AR_PHY_SFCORR_M2COUNT_THR, m2CountThr);
+-              REG_RMW_FIELD(ah, AR_PHY_SFCORR_LOW,
+-                            AR_PHY_SFCORR_LOW_M2COUNT_THR_LOW,
+-                            m2CountThrLow);
+-
+-              REG_RMW_FIELD(ah, AR_PHY_SFCORR_EXT,
+-                            AR_PHY_SFCORR_EXT_M1_THRESH_LOW, m1ThreshLowExt);
+-              REG_RMW_FIELD(ah, AR_PHY_SFCORR_EXT,
+-                            AR_PHY_SFCORR_EXT_M2_THRESH_LOW, m2ThreshLowExt);
+-              REG_RMW_FIELD(ah, AR_PHY_SFCORR_EXT,
+-                            AR_PHY_SFCORR_EXT_M1_THRESH, m1ThreshExt);
+-              REG_RMW_FIELD(ah, AR_PHY_SFCORR_EXT,
+-                            AR_PHY_SFCORR_EXT_M2_THRESH, m2ThreshExt);
+               if (on)
+                       REG_SET_BIT(ah, AR_PHY_SFCORR_LOW,
+--- a/drivers/net/wireless/rt2x00/rt2x00dev.c
++++ b/drivers/net/wireless/rt2x00/rt2x00dev.c
+@@ -430,10 +430,14 @@ void rt2x00lib_txdone(struct queue_entry
+       /*
+        * If the data queue was below the threshold before the txdone
+        * handler we must make sure the packet queue in the mac80211 stack
+-       * is reenabled when the txdone handler has finished.
++       * is reenabled when the txdone handler has finished. This has to be
++       * serialized with rt2x00mac_tx(), otherwise we can wake up queue
++       * before it was stopped.
+        */
++      spin_lock_bh(&entry->queue->tx_lock);
+       if (!rt2x00queue_threshold(entry->queue))
+               rt2x00queue_unpause_queue(entry->queue);
++      spin_unlock_bh(&entry->queue->tx_lock);
+ }
+ EXPORT_SYMBOL_GPL(rt2x00lib_txdone);
+--- a/drivers/net/wireless/rt2x00/rt2x00mac.c
++++ b/drivers/net/wireless/rt2x00/rt2x00mac.c
+@@ -152,13 +152,22 @@ void rt2x00mac_tx(struct ieee80211_hw *h
+       if (unlikely(rt2x00queue_write_tx_frame(queue, skb, false)))
+               goto exit_fail;
++      /*
++       * Pausing queue has to be serialized with rt2x00lib_txdone(). Note
++       * we should not use spin_lock_bh variant as bottom halve was already
++       * disabled before ieee80211_xmit() call.
++       */
++      spin_lock(&queue->tx_lock);
+       if (rt2x00queue_threshold(queue))
+               rt2x00queue_pause_queue(queue);
++      spin_unlock(&queue->tx_lock);
+       return;
+  exit_fail:
++      spin_lock(&queue->tx_lock);
+       rt2x00queue_pause_queue(queue);
++      spin_unlock(&queue->tx_lock);
+  exit_free_skb:
+       ieee80211_free_txskb(hw, skb);
+ }
+--- a/drivers/net/wireless/rt2x00/rt2x00queue.c
++++ b/drivers/net/wireless/rt2x00/rt2x00queue.c
+@@ -619,6 +619,9 @@ int rt2x00queue_write_tx_frame(struct da
+       else if (test_bit(REQUIRE_DMA, &queue->rt2x00dev->cap_flags))
+               rt2x00queue_align_frame(skb);
++      /*
++       * That function must be called with bh disabled.
++       */
+       spin_lock(&queue->tx_lock);
+       if (unlikely(rt2x00queue_full(queue))) {