mac80211: backport tx queue start/stop fix
authorFelix Fietkau <nbd@nbd.name>
Fri, 1 Mar 2019 12:10:53 +0000 (13:10 +0100)
committerFelix Fietkau <nbd@nbd.name>
Fri, 1 Mar 2019 12:14:29 +0000 (13:14 +0100)
Among other things, it fixes a race condition on calling ieee80211_restart_hw

Signed-off-by: Felix Fietkau <nbd@nbd.name>
package/kernel/mac80211/patches/subsys/300-mac80211-add-stop-start-logic-for-software-TXQs.patch [new file with mode: 0644]
package/kernel/mac80211/patches/subsys/357-mac80211-add-hdrlen-to-ieee80211_tx_data.patch
package/kernel/mac80211/patches/subsys/358-mac80211-add-NEED_ALIGNED4_SKBS-hw-flag.patch
package/kernel/mac80211/patches/subsys/371-mac80211-minstrel-merge-with-minstrel_ht-always-enab.patch
package/kernel/mac80211/patches/subsys/383-mac80211-minstrel_ht-add-flag-to-indicate-missing-in.patch
package/kernel/mac80211/patches/subsys/522-mac80211_configure_antenna_gain.patch

diff --git a/package/kernel/mac80211/patches/subsys/300-mac80211-add-stop-start-logic-for-software-TXQs.patch b/package/kernel/mac80211/patches/subsys/300-mac80211-add-stop-start-logic-for-software-TXQs.patch
new file mode 100644 (file)
index 0000000..422e9c2
--- /dev/null
@@ -0,0 +1,272 @@
+From: Manikanta Pubbisetty <mpubbise@codeaurora.org>
+Date: Wed, 11 Jul 2018 00:12:53 +0530
+Subject: [PATCH] mac80211: add stop/start logic for software TXQs
+
+Sometimes, it is required to stop the transmissions momentarily and
+resume it later; stopping the txqs becomes very critical in scenarios where
+the packet transmission has to be ceased completely. For example, during
+the hardware restart, during off channel operations,
+when initiating CSA(upon detecting a radar on the DFS channel), etc.
+
+The TX queue stop/start logic in mac80211 works well in stopping the TX
+when drivers make use of netdev queues, i.e, when Qdiscs in network layer
+take care of traffic scheduling. Since the devices implementing
+wake_tx_queue can run without Qdiscs, packets will be handed to mac80211
+directly without queueing them in the netdev queues.
+
+Also, mac80211 does not invoke any of the
+netif_stop_*/netif_wake_* APIs if wake_tx_queue is implemented.
+Since the queues are not stopped in this case, transmissions can continue
+and this will impact negatively on the operation of the wireless device.
+
+For example,
+During hardware restart, we stop the netdev queues so that packets are
+not sent to the driver. Since ath10k implements wake_tx_queue,
+TX queues will not be stopped and packets might reach the hardware while
+it is restarting; this can make hardware unresponsive and the only
+possible option for recovery is to reboot the entire system.
+
+There is another problem to this, it is observed that the packets
+were sent on the DFS channel for a prolonged duration after radar
+detection impacting the channel closing time.
+
+We can still invoke netif stop/wake APIs when wake_tx_queue is implemented
+but this could lead to packet drops in network layer; adding stop/start
+logic for software TXQs in mac80211 instead makes more sense; the change
+proposed adds the same in mac80211.
+
+Signed-off-by: Manikanta Pubbisetty <mpubbise@codeaurora.org>
+Signed-off-by: Johannes Berg <johannes.berg@intel.com>
+---
+
+--- a/include/net/mac80211.h
++++ b/include/net/mac80211.h
+@@ -1504,6 +1504,8 @@ enum ieee80211_vif_flags {
+  * @drv_priv: data area for driver use, will always be aligned to
+  *    sizeof(void \*).
+  * @txq: the multicast data TX queue (if driver uses the TXQ abstraction)
++ * @txqs_stopped: per AC flag to indicate that intermediate TXQs are stopped,
++ *    protected by fq->lock.
+  */
+ struct ieee80211_vif {
+       enum nl80211_iftype type;
+@@ -1528,6 +1530,8 @@ struct ieee80211_vif {
+       unsigned int probe_req_reg;
++      bool txqs_stopped[IEEE80211_NUM_ACS];
++
+       /* must be last */
+       u8 drv_priv[0] __aligned(sizeof(void *));
+ };
+--- a/net/mac80211/ieee80211_i.h
++++ b/net/mac80211/ieee80211_i.h
+@@ -818,6 +818,7 @@ enum txq_info_flags {
+       IEEE80211_TXQ_STOP,
+       IEEE80211_TXQ_AMPDU,
+       IEEE80211_TXQ_NO_AMSDU,
++      IEEE80211_TXQ_STOP_NETIF_TX,
+ };
+ /**
+@@ -1226,6 +1227,7 @@ struct ieee80211_local {
+       struct sk_buff_head pending[IEEE80211_MAX_QUEUES];
+       struct tasklet_struct tx_pending_tasklet;
++      struct tasklet_struct wake_txqs_tasklet;
+       atomic_t agg_queue_stop[IEEE80211_MAX_QUEUES];
+@@ -2039,6 +2041,7 @@ void ieee80211_txq_remove_vlan(struct ie
+                              struct ieee80211_sub_if_data *sdata);
+ void ieee80211_fill_txq_stats(struct cfg80211_txq_stats *txqstats,
+                             struct txq_info *txqi);
++void ieee80211_wake_txqs(unsigned long data);
+ void ieee80211_send_auth(struct ieee80211_sub_if_data *sdata,
+                        u16 transaction, u16 auth_alg, u16 status,
+                        const u8 *extra, size_t extra_len, const u8 *bssid,
+--- a/net/mac80211/main.c
++++ b/net/mac80211/main.c
+@@ -686,6 +686,10 @@ struct ieee80211_hw *ieee80211_alloc_hw_
+       tasklet_init(&local->tx_pending_tasklet, ieee80211_tx_pending,
+                    (unsigned long)local);
++      if (ops->wake_tx_queue)
++              tasklet_init(&local->wake_txqs_tasklet, ieee80211_wake_txqs,
++                           (unsigned long)local);
++
+       tasklet_init(&local->tasklet,
+                    ieee80211_tasklet_handler,
+                    (unsigned long) local);
+--- a/net/mac80211/tx.c
++++ b/net/mac80211/tx.c
+@@ -3482,13 +3482,19 @@ struct sk_buff *ieee80211_tx_dequeue(str
+       struct ieee80211_tx_info *info;
+       struct ieee80211_tx_data tx;
+       ieee80211_tx_result r;
+-      struct ieee80211_vif *vif;
++      struct ieee80211_vif *vif = txq->vif;
+       spin_lock_bh(&fq->lock);
+-      if (test_bit(IEEE80211_TXQ_STOP, &txqi->flags))
++      if (test_bit(IEEE80211_TXQ_STOP, &txqi->flags) ||
++          test_bit(IEEE80211_TXQ_STOP_NETIF_TX, &txqi->flags))
+               goto out;
++      if (vif->txqs_stopped[ieee80211_ac_from_tid(txq->tid)]) {
++              set_bit(IEEE80211_TXQ_STOP_NETIF_TX, &txqi->flags);
++              goto out;
++      }
++
+       /* Make sure fragments stay together. */
+       skb = __skb_dequeue(&txqi->frags);
+       if (skb)
+@@ -3583,6 +3589,7 @@ begin:
+       }
+       IEEE80211_SKB_CB(skb)->control.vif = vif;
++
+ out:
+       spin_unlock_bh(&fq->lock);
+--- a/net/mac80211/util.c
++++ b/net/mac80211/util.c
+@@ -240,6 +240,99 @@ __le16 ieee80211_ctstoself_duration(stru
+ }
+ EXPORT_SYMBOL(ieee80211_ctstoself_duration);
++static void __ieee80211_wake_txqs(struct ieee80211_sub_if_data *sdata, int ac)
++{
++      struct ieee80211_local *local = sdata->local;
++      struct ieee80211_vif *vif = &sdata->vif;
++      struct fq *fq = &local->fq;
++      struct ps_data *ps = NULL;
++      struct txq_info *txqi;
++      struct sta_info *sta;
++      int i;
++
++      spin_lock_bh(&fq->lock);
++
++      if (sdata->vif.type == NL80211_IFTYPE_AP)
++              ps = &sdata->bss->ps;
++
++      sdata->vif.txqs_stopped[ac] = false;
++
++      list_for_each_entry_rcu(sta, &local->sta_list, list) {
++              if (sdata != sta->sdata)
++                      continue;
++
++              for (i = 0; i < ARRAY_SIZE(sta->sta.txq); i++) {
++                      struct ieee80211_txq *txq = sta->sta.txq[i];
++
++                      txqi = to_txq_info(txq);
++
++                      if (ac != txq->ac)
++                              continue;
++
++                      if (!test_and_clear_bit(IEEE80211_TXQ_STOP_NETIF_TX,
++                                              &txqi->flags))
++                              continue;
++
++                      spin_unlock_bh(&fq->lock);
++                      drv_wake_tx_queue(local, txqi);
++                      spin_lock_bh(&fq->lock);
++              }
++      }
++
++      if (!vif->txq)
++              goto out;
++
++      txqi = to_txq_info(vif->txq);
++
++      if (!test_and_clear_bit(IEEE80211_TXQ_STOP_NETIF_TX, &txqi->flags) ||
++          (ps && atomic_read(&ps->num_sta_ps)) || ac != vif->txq->ac)
++              goto out;
++
++      spin_unlock_bh(&fq->lock);
++
++      drv_wake_tx_queue(local, txqi);
++      return;
++out:
++      spin_unlock_bh(&fq->lock);
++}
++
++void ieee80211_wake_txqs(unsigned long data)
++{
++      struct ieee80211_local *local = (struct ieee80211_local *)data;
++      struct ieee80211_sub_if_data *sdata;
++      int n_acs = IEEE80211_NUM_ACS;
++      unsigned long flags;
++      int i;
++
++      rcu_read_lock();
++      spin_lock_irqsave(&local->queue_stop_reason_lock, flags);
++
++      if (local->hw.queues < IEEE80211_NUM_ACS)
++              n_acs = 1;
++
++      for (i = 0; i < local->hw.queues; i++) {
++              if (local->queue_stop_reasons[i])
++                      continue;
++
++              spin_unlock_irqrestore(&local->queue_stop_reason_lock, flags);
++              list_for_each_entry_rcu(sdata, &local->interfaces, list) {
++                      int ac;
++
++                      for (ac = 0; ac < n_acs; ac++) {
++                              int ac_queue = sdata->vif.hw_queue[ac];
++
++                              if (ac_queue == i ||
++                                  sdata->vif.cab_queue == i)
++                                      __ieee80211_wake_txqs(sdata, ac);
++                      }
++              }
++              spin_lock_irqsave(&local->queue_stop_reason_lock, flags);
++      }
++
++      spin_unlock_irqrestore(&local->queue_stop_reason_lock, flags);
++      rcu_read_unlock();
++}
++
+ void ieee80211_propagate_queue_wake(struct ieee80211_local *local, int queue)
+ {
+       struct ieee80211_sub_if_data *sdata;
+@@ -308,6 +401,9 @@ static void __ieee80211_wake_queue(struc
+               rcu_read_unlock();
+       } else
+               tasklet_schedule(&local->tx_pending_tasklet);
++
++      if (local->ops->wake_tx_queue)
++              tasklet_schedule(&local->wake_txqs_tasklet);
+ }
+ void ieee80211_wake_queue_by_reason(struct ieee80211_hw *hw, int queue,
+@@ -351,9 +447,6 @@ static void __ieee80211_stop_queue(struc
+       if (__test_and_set_bit(reason, &local->queue_stop_reasons[queue]))
+               return;
+-      if (local->ops->wake_tx_queue)
+-              return;
+-
+       if (local->hw.queues < IEEE80211_NUM_ACS)
+               n_acs = 1;
+@@ -366,8 +459,15 @@ static void __ieee80211_stop_queue(struc
+               for (ac = 0; ac < n_acs; ac++) {
+                       if (sdata->vif.hw_queue[ac] == queue ||
+-                          sdata->vif.cab_queue == queue)
+-                              netif_stop_subqueue(sdata->dev, ac);
++                          sdata->vif.cab_queue == queue) {
++                              if (!local->ops->wake_tx_queue) {
++                                      netif_stop_subqueue(sdata->dev, ac);
++                                      continue;
++                              }
++                              spin_lock(&local->fq.lock);
++                              sdata->vif.txqs_stopped[ac] = true;
++                              spin_unlock(&local->fq.lock);
++                      }
+               }
+       }
+       rcu_read_unlock();
index a6bd3eab6e93bb20a2d7a5408f51f4a4971d50e7..2914461b41ba32467ab3b977e427faabe0fbf83e 100644 (file)
@@ -48,7 +48,7 @@ Signed-off-by: Janusz Dziedzic <janusz.dziedzic@tieto.com>
        if (likely(sta)) {
                if (!IS_ERR(sta))
                        tx->sta = sta;
-@@ -3507,6 +3507,7 @@ begin:
+@@ -3513,6 +3513,7 @@ begin:
        tx.local = local;
        tx.skb = skb;
        tx.sdata = vif_to_sdata(info->control.vif);
@@ -56,7 +56,7 @@ Signed-off-by: Janusz Dziedzic <janusz.dziedzic@tieto.com>
  
        if (txq->sta)
                tx.sta = container_of(txq->sta, struct sta_info, sta);
-@@ -3843,6 +3844,7 @@ ieee80211_build_data_template(struct iee
+@@ -3850,6 +3851,7 @@ ieee80211_build_data_template(struct iee
        hdr = (void *)skb->data;
        tx.sta = sta_info_get(sdata, hdr->addr1);
        tx.skb = skb;
@@ -66,7 +66,7 @@ Signed-off-by: Janusz Dziedzic <janusz.dziedzic@tieto.com>
                rcu_read_unlock();
 --- a/net/mac80211/util.c
 +++ b/net/mac80211/util.c
-@@ -1290,6 +1290,7 @@ void ieee80211_send_auth(struct ieee8021
+@@ -1390,6 +1390,7 @@ void ieee80211_send_auth(struct ieee8021
        struct ieee80211_local *local = sdata->local;
        struct sk_buff *skb;
        struct ieee80211_mgmt *mgmt;
@@ -74,7 +74,7 @@ Signed-off-by: Janusz Dziedzic <janusz.dziedzic@tieto.com>
        int err;
  
        /* 24 + 6 = header + auth_algo + auth_transaction + status_code */
-@@ -1313,8 +1314,10 @@ void ieee80211_send_auth(struct ieee8021
+@@ -1413,8 +1414,10 @@ void ieee80211_send_auth(struct ieee8021
                skb_put_data(skb, extra, extra_len);
  
        if (auth_alg == WLAN_AUTH_SHARED_KEY && transaction == 3) {
index bc87d0a45bc4b0312efaf17a9f12dba86437dbcd..f4048b0420c5d94bc40f2120e02f4bca38c31cdb 100644 (file)
@@ -23,7 +23,7 @@ Signed-off-by: Janusz Dziedzic <janusz.dziedzic@tieto.com>
 
 --- a/include/net/mac80211.h
 +++ b/include/net/mac80211.h
-@@ -2127,6 +2127,9 @@ struct ieee80211_txq {
+@@ -2131,6 +2131,9 @@ struct ieee80211_txq {
   * @IEEE80211_HW_DOESNT_SUPPORT_QOS_NDP: The driver (or firmware) doesn't
   *    support QoS NDP for AP probing - that's most likely a driver bug.
   *
@@ -33,7 +33,7 @@ Signed-off-by: Janusz Dziedzic <janusz.dziedzic@tieto.com>
   * @NUM_IEEE80211_HW_FLAGS: number of hardware flags, used for sizing arrays
   */
  enum ieee80211_hw_flags {
-@@ -2172,6 +2175,7 @@ enum ieee80211_hw_flags {
+@@ -2176,6 +2179,7 @@ enum ieee80211_hw_flags {
        IEEE80211_HW_SUPPORTS_TDLS_BUFFER_STA,
        IEEE80211_HW_DEAUTH_NEED_MGD_TX_PREP,
        IEEE80211_HW_DOESNT_SUPPORT_QOS_NDP,
@@ -53,7 +53,7 @@ Signed-off-by: Janusz Dziedzic <janusz.dziedzic@tieto.com>
  
 --- a/net/mac80211/ieee80211_i.h
 +++ b/net/mac80211/ieee80211_i.h
-@@ -1557,6 +1557,29 @@ ieee80211_vif_get_num_mcast_if(struct ie
+@@ -1559,6 +1559,29 @@ ieee80211_vif_get_num_mcast_if(struct ie
        return -1;
  }
  
@@ -203,7 +203,7 @@ Signed-off-by: Janusz Dziedzic <janusz.dziedzic@tieto.com>
        /* We store the key here so there's no point in using rcu_dereference()
         * but that's fine because the code that changes the pointers will call
         * this function after doing so. For a single CPU that would be enough,
-@@ -3534,7 +3543,7 @@ begin:
+@@ -3540,7 +3549,7 @@ begin:
  
                if (tx.key &&
                    (tx.key->conf.flags & IEEE80211_KEY_FLAG_GENERATE_IV))
@@ -214,7 +214,7 @@ Signed-off-by: Janusz Dziedzic <janusz.dziedzic@tieto.com>
                                           tx.key, skb);
 --- a/net/mac80211/util.c
 +++ b/net/mac80211/util.c
-@@ -1288,6 +1288,7 @@ void ieee80211_send_auth(struct ieee8021
+@@ -1388,6 +1388,7 @@ void ieee80211_send_auth(struct ieee8021
                         u32 tx_flags)
  {
        struct ieee80211_local *local = sdata->local;
@@ -222,7 +222,7 @@ Signed-off-by: Janusz Dziedzic <janusz.dziedzic@tieto.com>
        struct sk_buff *skb;
        struct ieee80211_mgmt *mgmt;
        unsigned int hdrlen;
-@@ -1314,7 +1315,7 @@ void ieee80211_send_auth(struct ieee8021
+@@ -1414,7 +1415,7 @@ void ieee80211_send_auth(struct ieee8021
                skb_put_data(skb, extra, extra_len);
  
        if (auth_alg == WLAN_AUTH_SHARED_KEY && transaction == 3) {
index 6f1239ccabacf87b6b58012fb7818686ffba7315..9a233753ad8a334f71a2a0be5e78a5c076509622 100644 (file)
@@ -67,7 +67,7 @@ Signed-off-by: Felix Fietkau <nbd@nbd.name>
  ccflags-y += -DDEBUG
 --- a/net/mac80211/main.c
 +++ b/net/mac80211/main.c
-@@ -1304,18 +1304,12 @@ static int __init ieee80211_init(void)
+@@ -1308,18 +1308,12 @@ static int __init ieee80211_init(void)
        if (ret)
                return ret;
  
@@ -86,7 +86,7 @@ Signed-off-by: Felix Fietkau <nbd@nbd.name>
        rc80211_minstrel_exit();
  
        return ret;
-@@ -1323,7 +1317,6 @@ static int __init ieee80211_init(void)
+@@ -1327,7 +1321,6 @@ static int __init ieee80211_init(void)
  
  static void __exit ieee80211_exit(void)
  {
index 2b9ff93db676c63c5380041f5fa16785a6a9e258..5b12e87c847770432f28618cbf193ffb6adbb2cf 100644 (file)
@@ -13,7 +13,7 @@ Signed-off-by: Felix Fietkau <nbd@nbd.name>
 
 --- a/include/net/mac80211.h
 +++ b/include/net/mac80211.h
-@@ -2130,6 +2130,9 @@ struct ieee80211_txq {
+@@ -2134,6 +2134,9 @@ struct ieee80211_txq {
   * @IEEE80211_HW_NEEDS_ALIGNED4_SKBS: Driver need aligned skbs to four-byte.
   *    Padding will be added after ieee80211_hdr, before IV/LLC.
   *
@@ -23,7 +23,7 @@ Signed-off-by: Felix Fietkau <nbd@nbd.name>
   * @NUM_IEEE80211_HW_FLAGS: number of hardware flags, used for sizing arrays
   */
  enum ieee80211_hw_flags {
-@@ -2176,6 +2179,7 @@ enum ieee80211_hw_flags {
+@@ -2180,6 +2183,7 @@ enum ieee80211_hw_flags {
        IEEE80211_HW_DEAUTH_NEED_MGD_TX_PREP,
        IEEE80211_HW_DOESNT_SUPPORT_QOS_NDP,
        IEEE80211_HW_NEEDS_ALIGNED4_SKBS,
index fe193cb4fdcf1e8ada3ab600748de4869a9501f4..b68010bfe7a724cf3683472d4c33169d245c0a7d 100644 (file)
        CFG80211_TESTMODE_CMD(ieee80211_testmode_cmd)
 --- a/net/mac80211/ieee80211_i.h
 +++ b/net/mac80211/ieee80211_i.h
-@@ -1352,6 +1352,7 @@ struct ieee80211_local {
+@@ -1354,6 +1354,7 @@ struct ieee80211_local {
        int dynamic_ps_forced_timeout;
  
        int user_power_level; /* in dBm, for all interfaces */