aboutsummaryrefslogtreecommitdiffstats
path: root/net/mac80211/offchannel.c
diff options
context:
space:
mode:
authorJohannes Berg <johannes.berg@intel.com>2012-06-05 08:28:42 -0400
committerJohn W. Linville <linville@tuxdriver.com>2012-06-06 15:31:18 -0400
commit2eb278e083549f4eb29838037004054b3b55df62 (patch)
tree803f076e7d7a1ed58e1c9e196bda7c2c2bb24bdf /net/mac80211/offchannel.c
parent196ac1c13d4db6c276dbb1c9190c8d7d45a83f1f (diff)
mac80211: unify SW/offload remain-on-channel
Redesign all the off-channel code, getting rid of the generic off-channel work concept, replacing it with a simple remain-on-channel list. This fixes a number of small issues with the ROC implementation: * offloaded remain-on-channel couldn't be queued, now we can queue it as well, if needed * in iwlwifi (the only user) offloaded ROC is mutually exclusive with scanning, use the new queue to handle that case -- I expect that it will later depend on a HW flag The bigger issue though is that there's a bad bug in the current implementation: if we get a mgmt TX request while HW roc is active, and this new request has a wait time, we actually schedule a software ROC instead since we can't guarantee the existing offloaded ROC will still be that long. To fix this, the queuing mechanism was needed. The queuing mechanism for offloaded ROC isn't yet optimal, ideally we should add API to have the HW extend the ROC if needed. We could add that later but for now use a software implementation. Overall, this unifies the behaviour between the offloaded and software-implemented case as much as possible. Signed-off-by: Johannes Berg <johannes.berg@intel.com> Signed-off-by: John W. Linville <linville@tuxdriver.com>
Diffstat (limited to 'net/mac80211/offchannel.c')
-rw-r--r--net/mac80211/offchannel.c280
1 files changed, 238 insertions, 42 deletions
diff --git a/net/mac80211/offchannel.c b/net/mac80211/offchannel.c
index 8f482b15bc51..abb226dc4753 100644
--- a/net/mac80211/offchannel.c
+++ b/net/mac80211/offchannel.c
@@ -16,6 +16,7 @@
16#include <net/mac80211.h> 16#include <net/mac80211.h>
17#include "ieee80211_i.h" 17#include "ieee80211_i.h"
18#include "driver-trace.h" 18#include "driver-trace.h"
19#include "driver-ops.h"
19 20
20/* 21/*
21 * Tell our hardware to disable PS. 22 * Tell our hardware to disable PS.
@@ -181,32 +182,58 @@ void ieee80211_offchannel_return(struct ieee80211_local *local,
181 mutex_unlock(&local->iflist_mtx); 182 mutex_unlock(&local->iflist_mtx);
182} 183}
183 184
185void ieee80211_handle_roc_started(struct ieee80211_roc_work *roc)
186{
187 if (roc->notified)
188 return;
189
190 if (roc->mgmt_tx_cookie) {
191 if (!WARN_ON(!roc->frame)) {
192 ieee80211_tx_skb(roc->sdata, roc->frame);
193 roc->frame = NULL;
194 }
195 } else {
196 cfg80211_ready_on_channel(roc->sdata->dev, (unsigned long)roc,
197 roc->chan, roc->chan_type,
198 roc->req_duration, GFP_KERNEL);
199 }
200
201 roc->notified = true;
202}
203
184static void ieee80211_hw_roc_start(struct work_struct *work) 204static void ieee80211_hw_roc_start(struct work_struct *work)
185{ 205{
186 struct ieee80211_local *local = 206 struct ieee80211_local *local =
187 container_of(work, struct ieee80211_local, hw_roc_start); 207 container_of(work, struct ieee80211_local, hw_roc_start);
188 struct ieee80211_sub_if_data *sdata; 208 struct ieee80211_roc_work *roc, *dep, *tmp;
189 209
190 mutex_lock(&local->mtx); 210 mutex_lock(&local->mtx);
191 211
192 if (!local->hw_roc_channel) { 212 if (list_empty(&local->roc_list))
193 mutex_unlock(&local->mtx); 213 goto out_unlock;
194 return;
195 }
196 214
197 if (local->hw_roc_skb) { 215 roc = list_first_entry(&local->roc_list, struct ieee80211_roc_work,
198 sdata = IEEE80211_DEV_TO_SUB_IF(local->hw_roc_dev); 216 list);
199 ieee80211_tx_skb(sdata, local->hw_roc_skb); 217
200 local->hw_roc_skb = NULL; 218 if (!roc->started)
201 } else { 219 goto out_unlock;
202 cfg80211_ready_on_channel(local->hw_roc_dev, 220
203 local->hw_roc_cookie, 221 roc->hw_begun = true;
204 local->hw_roc_channel, 222 roc->hw_start_time = local->hw_roc_start_time;
205 local->hw_roc_channel_type,
206 local->hw_roc_duration,
207 GFP_KERNEL);
208 }
209 223
224 ieee80211_handle_roc_started(roc);
225 list_for_each_entry_safe(dep, tmp, &roc->dependents, list) {
226 ieee80211_handle_roc_started(dep);
227
228 if (dep->duration > roc->duration) {
229 u32 dur = dep->duration;
230 dep->duration = dur - roc->duration;
231 roc->duration = dur;
232 list_del(&dep->list);
233 list_add(&dep->list, &roc->list);
234 }
235 }
236 out_unlock:
210 mutex_unlock(&local->mtx); 237 mutex_unlock(&local->mtx);
211} 238}
212 239
@@ -214,50 +241,179 @@ void ieee80211_ready_on_channel(struct ieee80211_hw *hw)
214{ 241{
215 struct ieee80211_local *local = hw_to_local(hw); 242 struct ieee80211_local *local = hw_to_local(hw);
216 243
244 local->hw_roc_start_time = jiffies;
245
217 trace_api_ready_on_channel(local); 246 trace_api_ready_on_channel(local);
218 247
219 ieee80211_queue_work(hw, &local->hw_roc_start); 248 ieee80211_queue_work(hw, &local->hw_roc_start);
220} 249}
221EXPORT_SYMBOL_GPL(ieee80211_ready_on_channel); 250EXPORT_SYMBOL_GPL(ieee80211_ready_on_channel);
222 251
223static void ieee80211_hw_roc_done(struct work_struct *work) 252void ieee80211_start_next_roc(struct ieee80211_local *local)
224{ 253{
225 struct ieee80211_local *local = 254 struct ieee80211_roc_work *roc;
226 container_of(work, struct ieee80211_local, hw_roc_done);
227 255
228 mutex_lock(&local->mtx); 256 lockdep_assert_held(&local->mtx);
229 257
230 if (!local->hw_roc_channel) { 258 if (list_empty(&local->roc_list)) {
231 mutex_unlock(&local->mtx); 259 ieee80211_run_deferred_scan(local);
232 return; 260 return;
233 } 261 }
234 262
235 /* was never transmitted */ 263 roc = list_first_entry(&local->roc_list, struct ieee80211_roc_work,
236 if (local->hw_roc_skb) { 264 list);
237 u64 cookie;
238 265
239 cookie = local->hw_roc_cookie ^ 2; 266 if (local->ops->remain_on_channel) {
267 int ret, duration = roc->duration;
240 268
241 cfg80211_mgmt_tx_status(local->hw_roc_dev, cookie, 269 /* XXX: duplicated, see ieee80211_start_roc_work() */
242 local->hw_roc_skb->data, 270 if (!duration)
243 local->hw_roc_skb->len, false, 271 duration = 10;
244 GFP_KERNEL);
245 272
246 kfree_skb(local->hw_roc_skb); 273 ret = drv_remain_on_channel(local, roc->chan,
247 local->hw_roc_skb = NULL; 274 roc->chan_type,
248 local->hw_roc_skb_for_status = NULL; 275 duration);
276
277 roc->started = true;
278
279 if (ret) {
280 wiphy_warn(local->hw.wiphy,
281 "failed to start next HW ROC (%d)\n", ret);
282 /*
283 * queue the work struct again to avoid recursion
284 * when multiple failures occur
285 */
286 ieee80211_remain_on_channel_expired(&local->hw);
287 }
288 } else {
289 /* delay it a bit */
290 ieee80211_queue_delayed_work(&local->hw, &roc->work,
291 round_jiffies_relative(HZ/2));
292 }
293}
294
295void ieee80211_roc_notify_destroy(struct ieee80211_roc_work *roc)
296{
297 struct ieee80211_roc_work *dep, *tmp;
298
299 /* was never transmitted */
300 if (roc->frame) {
301 cfg80211_mgmt_tx_status(roc->sdata->dev,
302 (unsigned long)roc->frame,
303 roc->frame->data, roc->frame->len,
304 false, GFP_KERNEL);
305 kfree_skb(roc->frame);
249 } 306 }
250 307
251 if (!local->hw_roc_for_tx) 308 if (!roc->mgmt_tx_cookie)
252 cfg80211_remain_on_channel_expired(local->hw_roc_dev, 309 cfg80211_remain_on_channel_expired(roc->sdata->dev,
253 local->hw_roc_cookie, 310 (unsigned long)roc,
254 local->hw_roc_channel, 311 roc->chan, roc->chan_type,
255 local->hw_roc_channel_type,
256 GFP_KERNEL); 312 GFP_KERNEL);
257 313
258 local->hw_roc_channel = NULL; 314 list_for_each_entry_safe(dep, tmp, &roc->dependents, list)
259 local->hw_roc_cookie = 0; 315 ieee80211_roc_notify_destroy(dep);
316
317 kfree(roc);
318}
319
320void ieee80211_sw_roc_work(struct work_struct *work)
321{
322 struct ieee80211_roc_work *roc =
323 container_of(work, struct ieee80211_roc_work, work.work);
324 struct ieee80211_sub_if_data *sdata = roc->sdata;
325 struct ieee80211_local *local = sdata->local;
326
327 mutex_lock(&local->mtx);
328
329 if (roc->abort)
330 goto finish;
331
332 if (WARN_ON(list_empty(&local->roc_list)))
333 goto out_unlock;
334
335 if (WARN_ON(roc != list_first_entry(&local->roc_list,
336 struct ieee80211_roc_work,
337 list)))
338 goto out_unlock;
339
340 if (!roc->started) {
341 struct ieee80211_roc_work *dep;
342
343 /* start this ROC */
260 344
345 /* switch channel etc */
346 ieee80211_recalc_idle(local);
347
348 local->tmp_channel = roc->chan;
349 local->tmp_channel_type = roc->chan_type;
350 ieee80211_hw_config(local, 0);
351
352 /* tell userspace or send frame */
353 ieee80211_handle_roc_started(roc);
354 list_for_each_entry(dep, &roc->dependents, list)
355 ieee80211_handle_roc_started(dep);
356
357 /* if it was pure TX, just finish right away */
358 if (!roc->duration)
359 goto finish;
360
361 roc->started = true;
362 ieee80211_queue_delayed_work(&local->hw, &roc->work,
363 msecs_to_jiffies(roc->duration));
364 } else {
365 /* finish this ROC */
366 finish:
367 list_del(&roc->list);
368 ieee80211_roc_notify_destroy(roc);
369
370 if (roc->started) {
371 drv_flush(local, false);
372
373 local->tmp_channel = NULL;
374 ieee80211_hw_config(local, 0);
375
376 ieee80211_offchannel_return(local, true);
377 }
378
379 ieee80211_recalc_idle(local);
380
381 ieee80211_start_next_roc(local);
382 ieee80211_run_deferred_scan(local);
383 }
384
385 out_unlock:
386 mutex_unlock(&local->mtx);
387}
388
389static void ieee80211_hw_roc_done(struct work_struct *work)
390{
391 struct ieee80211_local *local =
392 container_of(work, struct ieee80211_local, hw_roc_done);
393 struct ieee80211_roc_work *roc;
394
395 mutex_lock(&local->mtx);
396
397 if (list_empty(&local->roc_list))
398 goto out_unlock;
399
400 roc = list_first_entry(&local->roc_list, struct ieee80211_roc_work,
401 list);
402
403 if (!roc->started)
404 goto out_unlock;
405
406 list_del(&roc->list);
407
408 ieee80211_roc_notify_destroy(roc);
409
410 /* if there's another roc, start it now */
411 ieee80211_start_next_roc(local);
412
413 /* or scan maybe */
414 ieee80211_run_deferred_scan(local);
415
416 out_unlock:
261 mutex_unlock(&local->mtx); 417 mutex_unlock(&local->mtx);
262} 418}
263 419
@@ -271,8 +427,48 @@ void ieee80211_remain_on_channel_expired(struct ieee80211_hw *hw)
271} 427}
272EXPORT_SYMBOL_GPL(ieee80211_remain_on_channel_expired); 428EXPORT_SYMBOL_GPL(ieee80211_remain_on_channel_expired);
273 429
274void ieee80211_hw_roc_setup(struct ieee80211_local *local) 430void ieee80211_roc_setup(struct ieee80211_local *local)
275{ 431{
276 INIT_WORK(&local->hw_roc_start, ieee80211_hw_roc_start); 432 INIT_WORK(&local->hw_roc_start, ieee80211_hw_roc_start);
277 INIT_WORK(&local->hw_roc_done, ieee80211_hw_roc_done); 433 INIT_WORK(&local->hw_roc_done, ieee80211_hw_roc_done);
434 INIT_LIST_HEAD(&local->roc_list);
435}
436
437void ieee80211_roc_purge(struct ieee80211_sub_if_data *sdata)
438{
439 struct ieee80211_local *local = sdata->local;
440 struct ieee80211_roc_work *roc, *tmp;
441 LIST_HEAD(tmp_list);
442
443 mutex_lock(&local->mtx);
444 list_for_each_entry_safe(roc, tmp, &local->roc_list, list) {
445 if (roc->sdata != sdata)
446 continue;
447
448 if (roc->started && local->ops->remain_on_channel) {
449 /* can race, so ignore return value */
450 drv_cancel_remain_on_channel(local);
451 }
452
453 list_move_tail(&roc->list, &tmp_list);
454 roc->abort = true;
455 }
456
457 ieee80211_start_next_roc(local);
458 ieee80211_run_deferred_scan(local);
459 mutex_unlock(&local->mtx);
460
461 list_for_each_entry_safe(roc, tmp, &tmp_list, list) {
462 if (local->ops->remain_on_channel) {
463 list_del(&roc->list);
464 ieee80211_roc_notify_destroy(roc);
465 } else {
466 ieee80211_queue_delayed_work(&local->hw, &roc->work, 0);
467
468 /* work will clean up etc */
469 flush_delayed_work(&roc->work);
470 }
471 }
472
473 WARN_ON_ONCE(!list_empty(&tmp_list));
278} 474}