mac80211: Fix tx queue handling during scans
authorSeth Forshee <seth.forshee@canonical.com>
Mon, 11 Feb 2013 17:21:07 +0000 (11:21 -0600)
committerJohannes Berg <johannes.berg@intel.com>
Mon, 11 Feb 2013 21:52:21 +0000 (22:52 +0100)
Scans currently work by stopping the netdev tx queues but leaving the
mac80211 queues active. This stops the flow of incoming packets while
still allowing mac80211 to transmit nullfunc and probe request frames to
facilitate scanning. However, the driver may try to wake the mac80211
queues while in this state, which will also wake the netdev queues.

To prevent this, add a new queue stop reason,
IEEE80211_QUEUE_STOP_REASON_OFFCHANNEL, to be used when stopping the tx
queues for off-channel operation. This prevents the netdev queues from
waking when a driver wakes the mac80211 queues.

This also stops all frames from being transmitted, even those meant to
be sent off-channel. Add a new tx control flag,
IEEE80211_TX_CTL_OFFCHAN_TX_OK, which allows frames to be transmitted
when the queues are stopped only for the off-channel stop reason. Update
all locations transmitting off-channel frames to use this flag.

Signed-off-by: Seth Forshee <seth.forshee@canonical.com>
Signed-off-by: Johannes Berg <johannes.berg@intel.com>
include/net/mac80211.h
net/mac80211/cfg.c
net/mac80211/ieee80211_i.h
net/mac80211/mlme.c
net/mac80211/offchannel.c
net/mac80211/scan.c
net/mac80211/tx.c

index 86ad2c341525e628b9db80ea94a7e5f67fe1ad92..0eaa9092364bfbe2b914347595549790ab6276b8 100644 (file)
@@ -399,6 +399,9 @@ struct ieee80211_bss_conf {
  * @IEEE80211_TX_CTL_RATE_CTRL_PROBE: internal to mac80211, can be
  *     set by rate control algorithms to indicate probe rate, will
  *     be cleared for fragmented frames (except on the last fragment)
+ * @IEEE80211_TX_INTFL_OFFCHAN_TX_OK: Internal to mac80211. Used to indicate
+ *     that a frame can be transmitted while the queues are stopped for
+ *     off-channel operation.
  * @IEEE80211_TX_INTFL_NEED_TXPROCESSING: completely internal to mac80211,
  *     used to indicate that a pending frame requires TX processing before
  *     it can be sent out.
@@ -464,6 +467,7 @@ enum mac80211_tx_control_flags {
        IEEE80211_TX_STAT_AMPDU                 = BIT(10),
        IEEE80211_TX_STAT_AMPDU_NO_BACK         = BIT(11),
        IEEE80211_TX_CTL_RATE_CTRL_PROBE        = BIT(12),
+       IEEE80211_TX_INTFL_OFFCHAN_TX_OK        = BIT(13),
        IEEE80211_TX_INTFL_NEED_TXPROCESSING    = BIT(14),
        IEEE80211_TX_INTFL_RETRIED              = BIT(15),
        IEEE80211_TX_INTFL_DONT_ENCRYPT         = BIT(16),
index 8f6b593a921f39855d29116c614a4c92903e6580..e3dec80cf617a0d1b5671d66b403a6a75fd29979 100644 (file)
@@ -2749,7 +2749,8 @@ static int ieee80211_mgmt_tx(struct wiphy *wiphy, struct wireless_dev *wdev,
                goto out_unlock;
        }
 
-       IEEE80211_SKB_CB(skb)->flags |= IEEE80211_TX_CTL_TX_OFFCHAN;
+       IEEE80211_SKB_CB(skb)->flags |= IEEE80211_TX_CTL_TX_OFFCHAN |
+                                       IEEE80211_TX_INTFL_OFFCHAN_TX_OK;
        if (local->hw.flags & IEEE80211_HW_QUEUE_CONTROL)
                IEEE80211_SKB_CB(skb)->hw_queue =
                        local->hw.offchannel_tx_hw_queue;
index 5635dfc7da34c91ca50d1087a66d977c686403c0..76cdcfcd614cea37ac6adfbdd3b7185a109ea9ac 100644 (file)
@@ -809,6 +809,7 @@ enum queue_stop_reason {
        IEEE80211_QUEUE_STOP_REASON_AGGREGATION,
        IEEE80211_QUEUE_STOP_REASON_SUSPEND,
        IEEE80211_QUEUE_STOP_REASON_SKB_ADD,
+       IEEE80211_QUEUE_STOP_REASON_OFFCHANNEL,
 };
 
 #ifdef CONFIG_MAC80211_LEDS
index 76cacdb062c85e9fc83959cc9b723a111da6fef7..efb22763d56df39808d71c6196feb3d564566c99 100644 (file)
@@ -685,7 +685,8 @@ void ieee80211_send_nullfunc(struct ieee80211_local *local,
        if (powersave)
                nullfunc->frame_control |= cpu_to_le16(IEEE80211_FCTL_PM);
 
-       IEEE80211_SKB_CB(skb)->flags |= IEEE80211_TX_INTFL_DONT_ENCRYPT;
+       IEEE80211_SKB_CB(skb)->flags |= IEEE80211_TX_INTFL_DONT_ENCRYPT |
+                                       IEEE80211_TX_INTFL_OFFCHAN_TX_OK;
        if (ifmgd->flags & (IEEE80211_STA_BEACON_POLL |
                            IEEE80211_STA_CONNECTION_POLL))
                IEEE80211_SKB_CB(skb)->flags |= IEEE80211_TX_CTL_USE_MINRATE;
index 82baf5b6ecf4cbac5accf2e38d33187df1545514..4c3ee3e8285c139dc7b7348e322de77a6139ad8a 100644 (file)
@@ -113,6 +113,10 @@ void ieee80211_offchannel_stop_vifs(struct ieee80211_local *local)
         * notify the AP about us leaving the channel and stop all
         * STA interfaces.
         */
+
+       ieee80211_stop_queues_by_reason(&local->hw,
+                                       IEEE80211_QUEUE_STOP_REASON_OFFCHANNEL);
+
        mutex_lock(&local->iflist_mtx);
        list_for_each_entry(sdata, &local->interfaces, list) {
                if (!ieee80211_sdata_running(sdata))
@@ -133,12 +137,9 @@ void ieee80211_offchannel_stop_vifs(struct ieee80211_local *local)
                                sdata, BSS_CHANGED_BEACON_ENABLED);
                }
 
-               if (sdata->vif.type != NL80211_IFTYPE_MONITOR) {
-                       netif_tx_stop_all_queues(sdata->dev);
-                       if (sdata->vif.type == NL80211_IFTYPE_STATION &&
-                           sdata->u.mgd.associated)
-                               ieee80211_offchannel_ps_enable(sdata);
-               }
+               if (sdata->vif.type == NL80211_IFTYPE_STATION &&
+                   sdata->u.mgd.associated)
+                       ieee80211_offchannel_ps_enable(sdata);
        }
        mutex_unlock(&local->iflist_mtx);
 }
@@ -166,20 +167,6 @@ void ieee80211_offchannel_return(struct ieee80211_local *local)
                    sdata->u.mgd.associated)
                        ieee80211_offchannel_ps_disable(sdata);
 
-               if (sdata->vif.type != NL80211_IFTYPE_MONITOR) {
-                       /*
-                        * This may wake up queues even though the driver
-                        * currently has them stopped. This is not very
-                        * likely, since the driver won't have gotten any
-                        * (or hardly any) new packets while we weren't
-                        * on the right channel, and even if it happens
-                        * it will at most lead to queueing up one more
-                        * packet per queue in mac80211 rather than on
-                        * the interface qdisc.
-                        */
-                       netif_tx_wake_all_queues(sdata->dev);
-               }
-
                if (test_and_clear_bit(SDATA_STATE_OFFCHANNEL_BEACON_STOPPED,
                                       &sdata->state)) {
                        sdata->vif.bss_conf.enable_beacon = true;
@@ -188,6 +175,9 @@ void ieee80211_offchannel_return(struct ieee80211_local *local)
                }
        }
        mutex_unlock(&local->iflist_mtx);
+
+       ieee80211_wake_queues_by_reason(&local->hw,
+                                       IEEE80211_QUEUE_STOP_REASON_OFFCHANNEL);
 }
 
 void ieee80211_handle_roc_started(struct ieee80211_roc_work *roc)
index edd47d9acb99e713c0e5d3b1614cf61d1ee76274..d9e2df96f676a0819b69cb303d4073dbf9f50615 100644 (file)
@@ -382,6 +382,11 @@ static void ieee80211_scan_state_send_probe(struct ieee80211_local *local,
        int i;
        struct ieee80211_sub_if_data *sdata;
        enum ieee80211_band band = local->hw.conf.channel->band;
+       u32 tx_flags;
+
+       tx_flags = IEEE80211_TX_INTFL_OFFCHAN_TX_OK;
+       if (local->scan_req->no_cck)
+               tx_flags |= IEEE80211_TX_CTL_NO_CCK_RATE;
 
        sdata = rcu_dereference_protected(local->scan_sdata,
                                          lockdep_is_held(&local->mtx));
@@ -393,9 +398,7 @@ static void ieee80211_scan_state_send_probe(struct ieee80211_local *local,
                        local->scan_req->ssids[i].ssid_len,
                        local->scan_req->ie, local->scan_req->ie_len,
                        local->scan_req->rates[band], false,
-                       local->scan_req->no_cck ?
-                               IEEE80211_TX_CTL_NO_CCK_RATE : 0,
-                       local->hw.conf.channel, true);
+                       tx_flags, local->hw.conf.channel, true);
 
        /*
         * After sending probe requests, wait for probe responses
index 2ef0e19b06bb041c58d217e48e5e3d9ade30d7d2..f476aa6a771d7feb8ac54faba8404f30265f604d 100644 (file)
@@ -1230,6 +1230,21 @@ static bool ieee80211_tx_frags(struct ieee80211_local *local,
                spin_lock_irqsave(&local->queue_stop_reason_lock, flags);
                if (local->queue_stop_reasons[q] ||
                    (!txpending && !skb_queue_empty(&local->pending[q]))) {
+                       if (unlikely(info->flags &
+                                       IEEE80211_TX_INTFL_OFFCHAN_TX_OK &&
+                                    local->queue_stop_reasons[q] &
+                                       ~BIT(IEEE80211_QUEUE_STOP_REASON_OFFCHANNEL))) {
+                               /*
+                                * Drop off-channel frames if queues are stopped
+                                * for any reason other than off-channel
+                                * operation. Never queue them.
+                                */
+                               spin_unlock_irqrestore(
+                                       &local->queue_stop_reason_lock, flags);
+                               ieee80211_purge_tx_queue(&local->hw, skbs);
+                               return true;
+                       }
+
                        /*
                         * Since queue is stopped, queue up frames for later
                         * transmission from the tx-pending tasklet when the