mac80211: stop queues temporarily for flushing
authorJohannes Berg <johannes.berg@intel.com>
Wed, 13 Feb 2013 11:25:28 +0000 (12:25 +0100)
committerJohannes Berg <johannes.berg@intel.com>
Mon, 18 Mar 2013 19:15:05 +0000 (20:15 +0100)
Sometimes queues are flushed in the middle of
operation, which can lead to driver issues.
Stop queues temporarily, while flushing, to
avoid transmitting new packets while they are
being flushed.

Signed-off-by: Johannes Berg <johannes.berg@intel.com>
include/net/mac80211.h
net/mac80211/ieee80211_i.h
net/mac80211/main.c
net/mac80211/mlme.c
net/mac80211/offchannel.c
net/mac80211/pm.c
net/mac80211/tx.c
net/mac80211/util.c

index 4158da74e11b066c20687f85536de6561052698f..dd73b8c6746bdd1654c08bc5e2c10f77b59a0a1f 100644 (file)
@@ -93,9 +93,11 @@ struct device;
  * enum ieee80211_max_queues - maximum number of queues
  *
  * @IEEE80211_MAX_QUEUES: Maximum number of regular device queues.
+ * @IEEE80211_MAX_QUEUE_MAP: bitmap with maximum queues set
  */
 enum ieee80211_max_queues {
        IEEE80211_MAX_QUEUES =          16,
+       IEEE80211_MAX_QUEUE_MAP =       BIT(IEEE80211_MAX_QUEUES) - 1,
 };
 
 #define IEEE80211_INVAL_HW_QUEUE       0xff
index b96c0e977752dd1d2cac14c7dc1f93c276b19001..ae2d1754b792d0af14ea47a03ef966001820ec6f 100644 (file)
@@ -809,6 +809,7 @@ enum queue_stop_reason {
        IEEE80211_QUEUE_STOP_REASON_SUSPEND,
        IEEE80211_QUEUE_STOP_REASON_SKB_ADD,
        IEEE80211_QUEUE_STOP_REASON_OFFCHANNEL,
+       IEEE80211_QUEUE_STOP_REASON_FLUSH,
 };
 
 #ifdef CONFIG_MAC80211_LEDS
@@ -1522,8 +1523,10 @@ void ieee80211_sta_tx_notify(struct ieee80211_sub_if_data *sdata,
                             struct ieee80211_hdr *hdr, bool ack);
 
 void ieee80211_wake_queues_by_reason(struct ieee80211_hw *hw,
+                                    unsigned long queues,
                                     enum queue_stop_reason reason);
 void ieee80211_stop_queues_by_reason(struct ieee80211_hw *hw,
+                                    unsigned long queues,
                                     enum queue_stop_reason reason);
 void ieee80211_wake_queue_by_reason(struct ieee80211_hw *hw, int queue,
                                    enum queue_stop_reason reason);
index eee1768e89c0ff57a1369cacf8e0ac7ffa52c029..c6f81ecc36a15bd0375aaeaef0ac5f85e4afc575 100644 (file)
@@ -277,8 +277,8 @@ void ieee80211_restart_hw(struct ieee80211_hw *hw)
                   "Hardware restart was requested\n");
 
        /* use this reason, ieee80211_reconfig will unblock it */
-       ieee80211_stop_queues_by_reason(hw,
-               IEEE80211_QUEUE_STOP_REASON_SUSPEND);
+       ieee80211_stop_queues_by_reason(hw, IEEE80211_MAX_QUEUE_MAP,
+                                       IEEE80211_QUEUE_STOP_REASON_SUSPEND);
 
        /*
         * Stop all Rx during the reconfig. We don't want state changes
index 65b38e13eb0c59a05f387b8de416673182bc7f1e..4d383a93ea734c3128ed59d0e3e00668c6f4636f 100644 (file)
@@ -1009,6 +1009,7 @@ static void ieee80211_chswitch_work(struct work_struct *work)
 
        /* XXX: wait for a beacon first? */
        ieee80211_wake_queues_by_reason(&sdata->local->hw,
+                                       IEEE80211_MAX_QUEUE_MAP,
                                        IEEE80211_QUEUE_STOP_REASON_CSA);
  out:
        ifmgd->flags &= ~IEEE80211_STA_CSA_RECEIVED;
@@ -1108,6 +1109,7 @@ ieee80211_sta_process_chanswitch(struct ieee80211_sub_if_data *sdata,
 
        if (sw_elem->mode)
                ieee80211_stop_queues_by_reason(&sdata->local->hw,
+                               IEEE80211_MAX_QUEUE_MAP,
                                IEEE80211_QUEUE_STOP_REASON_CSA);
 
        if (sdata->local->ops->channel_switch) {
@@ -1375,6 +1377,7 @@ void ieee80211_dynamic_ps_disable_work(struct work_struct *work)
        }
 
        ieee80211_wake_queues_by_reason(&local->hw,
+                                       IEEE80211_MAX_QUEUE_MAP,
                                        IEEE80211_QUEUE_STOP_REASON_PS);
 }
 
@@ -2071,6 +2074,7 @@ static void __ieee80211_disconnect(struct ieee80211_sub_if_data *sdata)
                               true, frame_buf);
        ifmgd->flags &= ~IEEE80211_STA_CSA_RECEIVED;
        ieee80211_wake_queues_by_reason(&sdata->local->hw,
+                                       IEEE80211_MAX_QUEUE_MAP,
                                        IEEE80211_QUEUE_STOP_REASON_CSA);
        mutex_unlock(&ifmgd->mtx);
 
index d32f514074b962747f86d54cc9877b1d19479624..b01eb7314ec670ebab5d686a3ac2df7acd5616a2 100644 (file)
@@ -118,7 +118,7 @@ void ieee80211_offchannel_stop_vifs(struct ieee80211_local *local)
         * Stop queues and transmit all frames queued by the driver
         * before sending nullfunc to enable powersave at the AP.
         */
-       ieee80211_stop_queues_by_reason(&local->hw,
+       ieee80211_stop_queues_by_reason(&local->hw, IEEE80211_MAX_QUEUE_MAP,
                                        IEEE80211_QUEUE_STOP_REASON_OFFCHANNEL);
        ieee80211_flush_queues(local, NULL);
 
@@ -181,7 +181,7 @@ void ieee80211_offchannel_return(struct ieee80211_local *local)
        }
        mutex_unlock(&local->iflist_mtx);
 
-       ieee80211_wake_queues_by_reason(&local->hw,
+       ieee80211_wake_queues_by_reason(&local->hw, IEEE80211_MAX_QUEUE_MAP,
                                        IEEE80211_QUEUE_STOP_REASON_OFFCHANNEL);
 }
 
index 497f21a0d1162d2e31ccc978ec8cf79308ca4723..3d16f4e6174388f27e030f9612a64bbd752c04ea 100644 (file)
@@ -30,7 +30,8 @@ int __ieee80211_suspend(struct ieee80211_hw *hw, struct cfg80211_wowlan *wowlan)
        }
 
        ieee80211_stop_queues_by_reason(hw,
-                       IEEE80211_QUEUE_STOP_REASON_SUSPEND);
+                                       IEEE80211_MAX_QUEUE_MAP,
+                                       IEEE80211_QUEUE_STOP_REASON_SUSPEND);
 
        /* flush out all packets */
        synchronize_net();
@@ -68,6 +69,7 @@ int __ieee80211_suspend(struct ieee80211_hw *hw, struct cfg80211_wowlan *wowlan)
                                mutex_unlock(&local->sta_mtx);
                        }
                        ieee80211_wake_queues_by_reason(hw,
+                                       IEEE80211_MAX_QUEUE_MAP,
                                        IEEE80211_QUEUE_STOP_REASON_SUSPEND);
                        return err;
                } else if (err > 0) {
index 3fcdf211810154aa99ae423ad4bc7c3841cabe3d..2a6ae8030bd9e8997cdc4662aeabbc9644ac19cf 100644 (file)
@@ -233,6 +233,7 @@ ieee80211_tx_h_dynamic_ps(struct ieee80211_tx_data *tx)
 
        if (local->hw.conf.flags & IEEE80211_CONF_PS) {
                ieee80211_stop_queues_by_reason(&local->hw,
+                                               IEEE80211_MAX_QUEUE_MAP,
                                                IEEE80211_QUEUE_STOP_REASON_PS);
                ifmgd->flags &= ~IEEE80211_STA_NULLFUNC_ACKED;
                ieee80211_queue_work(&local->hw,
index f978ddd1bb43b22bb3645a505b369fa6324778e9..a7368870c8ee10cf7ee48dc764bc466974eac8ae 100644 (file)
@@ -453,7 +453,8 @@ void ieee80211_add_pending_skbs_fn(struct ieee80211_local *local,
 }
 
 void ieee80211_stop_queues_by_reason(struct ieee80211_hw *hw,
-                                   enum queue_stop_reason reason)
+                                    unsigned long queues,
+                                    enum queue_stop_reason reason)
 {
        struct ieee80211_local *local = hw_to_local(hw);
        unsigned long flags;
@@ -461,7 +462,7 @@ void ieee80211_stop_queues_by_reason(struct ieee80211_hw *hw,
 
        spin_lock_irqsave(&local->queue_stop_reason_lock, flags);
 
-       for (i = 0; i < hw->queues; i++)
+       for_each_set_bit(i, &queues, hw->queues)
                __ieee80211_stop_queue(hw, i, reason);
 
        spin_unlock_irqrestore(&local->queue_stop_reason_lock, flags);
@@ -469,7 +470,7 @@ void ieee80211_stop_queues_by_reason(struct ieee80211_hw *hw,
 
 void ieee80211_stop_queues(struct ieee80211_hw *hw)
 {
-       ieee80211_stop_queues_by_reason(hw,
+       ieee80211_stop_queues_by_reason(hw, IEEE80211_MAX_QUEUE_MAP,
                                        IEEE80211_QUEUE_STOP_REASON_DRIVER);
 }
 EXPORT_SYMBOL(ieee80211_stop_queues);
@@ -491,6 +492,7 @@ int ieee80211_queue_stopped(struct ieee80211_hw *hw, int queue)
 EXPORT_SYMBOL(ieee80211_queue_stopped);
 
 void ieee80211_wake_queues_by_reason(struct ieee80211_hw *hw,
+                                    unsigned long queues,
                                     enum queue_stop_reason reason)
 {
        struct ieee80211_local *local = hw_to_local(hw);
@@ -499,7 +501,7 @@ void ieee80211_wake_queues_by_reason(struct ieee80211_hw *hw,
 
        spin_lock_irqsave(&local->queue_stop_reason_lock, flags);
 
-       for (i = 0; i < hw->queues; i++)
+       for_each_set_bit(i, &queues, hw->queues)
                __ieee80211_wake_queue(hw, i, reason);
 
        spin_unlock_irqrestore(&local->queue_stop_reason_lock, flags);
@@ -507,7 +509,8 @@ void ieee80211_wake_queues_by_reason(struct ieee80211_hw *hw,
 
 void ieee80211_wake_queues(struct ieee80211_hw *hw)
 {
-       ieee80211_wake_queues_by_reason(hw, IEEE80211_QUEUE_STOP_REASON_DRIVER);
+       ieee80211_wake_queues_by_reason(hw, IEEE80211_MAX_QUEUE_MAP,
+                                       IEEE80211_QUEUE_STOP_REASON_DRIVER);
 }
 EXPORT_SYMBOL(ieee80211_wake_queues);
 
@@ -533,7 +536,13 @@ void ieee80211_flush_queues(struct ieee80211_local *local,
                queues = BIT(local->hw.queues) - 1;
        }
 
+       ieee80211_stop_queues_by_reason(&local->hw, IEEE80211_MAX_QUEUE_MAP,
+                                       IEEE80211_QUEUE_STOP_REASON_FLUSH);
+
        drv_flush(local, queues, false);
+
+       ieee80211_wake_queues_by_reason(&local->hw, IEEE80211_MAX_QUEUE_MAP,
+                                       IEEE80211_QUEUE_STOP_REASON_FLUSH);
 }
 
 void ieee80211_iterate_active_interfaces(
@@ -1676,8 +1685,8 @@ int ieee80211_reconfig(struct ieee80211_local *local)
                mutex_unlock(&local->sta_mtx);
        }
 
-       ieee80211_wake_queues_by_reason(hw,
-                       IEEE80211_QUEUE_STOP_REASON_SUSPEND);
+       ieee80211_wake_queues_by_reason(hw, IEEE80211_MAX_QUEUE_MAP,
+                                       IEEE80211_QUEUE_STOP_REASON_SUSPEND);
 
        /*
         * If this is for hw restart things are still running.