aboutsummaryrefslogtreecommitdiffstats
path: root/net/mac80211
diff options
context:
space:
mode:
authorJohannes Berg <johannes.berg@intel.com>2010-08-27 07:45:28 -0400
committerJohn W. Linville <linville@tuxdriver.com>2010-08-27 13:53:31 -0400
commit5b714c6a3753dad0798a70a049e15c7f6bc9446b (patch)
tree39345981014b14e881cbfbef87a4a3a74469fada /net/mac80211
parentc35d02705e9c2db90a89b29142046b4ffd5a76e5 (diff)
mac80211: fix offchannel queue stop
Somebody noticed this problem, and I outlined to them how to fix it, but haven't heard back from them. So while I was adding the state field I figured I could use it to fix it. The problem, as I understand it, is that when we go offchannel while the driver has a queue stopped, the driver will likely start draining the queue and then enable it while offchannel. This in turn will enable the interface queue, and that leads to transmitting data frames on the wrong channel. Fix this by keeping track of offchannel status per interface, and not enabling the interface queues on interfaces that are offchannel when the driver enables a queue. Signed-off-by: Johannes Berg <johannes.berg@intel.com> Signed-off-by: John W. Linville <linville@tuxdriver.com>
Diffstat (limited to 'net/mac80211')
-rw-r--r--net/mac80211/ieee80211_i.h3
-rw-r--r--net/mac80211/offchannel.c19
-rw-r--r--net/mac80211/util.c5
3 files changed, 24 insertions, 3 deletions
diff --git a/net/mac80211/ieee80211_i.h b/net/mac80211/ieee80211_i.h
index d529bd5eab47..9af50fbcd48b 100644
--- a/net/mac80211/ieee80211_i.h
+++ b/net/mac80211/ieee80211_i.h
@@ -477,9 +477,12 @@ enum ieee80211_sub_if_data_flags {
477 * @SDATA_STATE_RUNNING: virtual interface is up & running; this 477 * @SDATA_STATE_RUNNING: virtual interface is up & running; this
478 * mirrors netif_running() but is separate for interface type 478 * mirrors netif_running() but is separate for interface type
479 * change handling while the interface is up 479 * change handling while the interface is up
480 * @SDATA_STATE_OFFCHANNEL: This interface is currently in offchannel
481 * mode, so queues are stopped
480 */ 482 */
481enum ieee80211_sdata_state_bits { 483enum ieee80211_sdata_state_bits {
482 SDATA_STATE_RUNNING, 484 SDATA_STATE_RUNNING,
485 SDATA_STATE_OFFCHANNEL,
483}; 486};
484 487
485struct ieee80211_sub_if_data { 488struct ieee80211_sub_if_data {
diff --git a/net/mac80211/offchannel.c b/net/mac80211/offchannel.c
index c36b1911987a..eeacaa59380a 100644
--- a/net/mac80211/offchannel.c
+++ b/net/mac80211/offchannel.c
@@ -112,8 +112,10 @@ void ieee80211_offchannel_stop_beaconing(struct ieee80211_local *local)
112 * used from user space controlled off-channel operations. 112 * used from user space controlled off-channel operations.
113 */ 113 */
114 if (sdata->vif.type != NL80211_IFTYPE_STATION && 114 if (sdata->vif.type != NL80211_IFTYPE_STATION &&
115 sdata->vif.type != NL80211_IFTYPE_MONITOR) 115 sdata->vif.type != NL80211_IFTYPE_MONITOR) {
116 set_bit(SDATA_STATE_OFFCHANNEL, &sdata->state);
116 netif_tx_stop_all_queues(sdata->dev); 117 netif_tx_stop_all_queues(sdata->dev);
118 }
117 } 119 }
118 mutex_unlock(&local->iflist_mtx); 120 mutex_unlock(&local->iflist_mtx);
119} 121}
@@ -131,6 +133,7 @@ void ieee80211_offchannel_stop_station(struct ieee80211_local *local)
131 continue; 133 continue;
132 134
133 if (sdata->vif.type == NL80211_IFTYPE_STATION) { 135 if (sdata->vif.type == NL80211_IFTYPE_STATION) {
136 set_bit(SDATA_STATE_OFFCHANNEL, &sdata->state);
134 netif_tx_stop_all_queues(sdata->dev); 137 netif_tx_stop_all_queues(sdata->dev);
135 if (sdata->u.mgd.associated) 138 if (sdata->u.mgd.associated)
136 ieee80211_offchannel_ps_enable(sdata); 139 ieee80211_offchannel_ps_enable(sdata);
@@ -155,8 +158,20 @@ void ieee80211_offchannel_return(struct ieee80211_local *local,
155 ieee80211_offchannel_ps_disable(sdata); 158 ieee80211_offchannel_ps_disable(sdata);
156 } 159 }
157 160
158 if (sdata->vif.type != NL80211_IFTYPE_MONITOR) 161 if (sdata->vif.type != NL80211_IFTYPE_MONITOR) {
162 clear_bit(SDATA_STATE_OFFCHANNEL, &sdata->state);
163 /*
164 * This may wake up queues even though the driver
165 * currently has them stopped. This is not very
166 * likely, since the driver won't have gotten any
167 * (or hardly any) new packets while we weren't
168 * on the right channel, and even if it happens
169 * it will at most lead to queueing up one more
170 * packet per queue in mac80211 rather than on
171 * the interface qdisc.
172 */
159 netif_tx_wake_all_queues(sdata->dev); 173 netif_tx_wake_all_queues(sdata->dev);
174 }
160 175
161 /* re-enable beaconing */ 176 /* re-enable beaconing */
162 if (enable_beaconing && 177 if (enable_beaconing &&
diff --git a/net/mac80211/util.c b/net/mac80211/util.c
index d38b3767e8cc..bd40b11d5ab9 100644
--- a/net/mac80211/util.c
+++ b/net/mac80211/util.c
@@ -283,8 +283,11 @@ static void __ieee80211_wake_queue(struct ieee80211_hw *hw, int queue,
283 283
284 if (skb_queue_empty(&local->pending[queue])) { 284 if (skb_queue_empty(&local->pending[queue])) {
285 rcu_read_lock(); 285 rcu_read_lock();
286 list_for_each_entry_rcu(sdata, &local->interfaces, list) 286 list_for_each_entry_rcu(sdata, &local->interfaces, list) {
287 if (test_bit(SDATA_STATE_OFFCHANNEL, &sdata->state))
288 continue;
287 netif_wake_subqueue(sdata->dev, queue); 289 netif_wake_subqueue(sdata->dev, queue);
290 }
288 rcu_read_unlock(); 291 rcu_read_unlock();
289 } else 292 } else
290 tasklet_schedule(&local->tx_pending_tasklet); 293 tasklet_schedule(&local->tx_pending_tasklet);