blob: b2650a9d45ff765c7555628f82dea3c673a76380 [file] [log] [blame]
Johannes Bergaf6b6372009-12-23 13:15:35 +01001/*
2 * mac80211 work implementation
3 *
4 * Copyright 2003-2008, Jouni Malinen <j@w1.fi>
5 * Copyright 2004, Instant802 Networks, Inc.
6 * Copyright 2005, Devicescape Software, Inc.
7 * Copyright 2006-2007 Jiri Benc <jbenc@suse.cz>
8 * Copyright 2007, Michael Wu <flamingice@sourmilk.net>
9 * Copyright 2009, Johannes Berg <johannes@sipsolutions.net>
10 *
11 * This program is free software; you can redistribute it and/or modify
12 * it under the terms of the GNU General Public License version 2 as
13 * published by the Free Software Foundation.
14 */
15
16#include <linux/delay.h>
17#include <linux/if_ether.h>
18#include <linux/skbuff.h>
19#include <linux/if_arp.h>
20#include <linux/etherdevice.h>
21#include <linux/crc32.h>
Tejun Heo5a0e3ad2010-03-24 17:04:11 +090022#include <linux/slab.h>
Johannes Bergaf6b6372009-12-23 13:15:35 +010023#include <net/mac80211.h>
24#include <asm/unaligned.h>
25
26#include "ieee80211_i.h"
27#include "rate.h"
Johannes Bergb2abb6e2011-07-19 10:39:53 +020028#include "driver-ops.h"
Johannes Bergaf6b6372009-12-23 13:15:35 +010029
Johannes Bergaf6b6372009-12-23 13:15:35 +010030enum work_action {
31 WORK_ACT_NONE,
32 WORK_ACT_TIMEOUT,
Johannes Bergaf6b6372009-12-23 13:15:35 +010033};
34
35
36/* utils */
37static inline void ASSERT_WORK_MTX(struct ieee80211_local *local)
38{
Johannes Berga1699b72010-07-30 16:46:07 +020039 lockdep_assert_held(&local->mtx);
Johannes Bergaf6b6372009-12-23 13:15:35 +010040}
41
42/*
43 * We can have multiple work items (and connection probing)
44 * scheduling this timer, but we need to take care to only
45 * reschedule it when it should fire _earlier_ than it was
46 * asked for before, or if it's not pending right now. This
47 * function ensures that. Note that it then is required to
48 * run this function for all timeouts after the first one
49 * has happened -- the work that runs from this timer will
50 * do that.
51 */
52static void run_again(struct ieee80211_local *local,
53 unsigned long timeout)
54{
55 ASSERT_WORK_MTX(local);
56
57 if (!timer_pending(&local->work_timer) ||
58 time_before(timeout, local->work_timer.expires))
59 mod_timer(&local->work_timer, timeout);
60}
61
Johannes Bergaf6b6372009-12-23 13:15:35 +010062void free_work(struct ieee80211_work *wk)
63{
Lai Jiangshana74ce142011-03-18 12:14:15 +080064 kfree_rcu(wk, rcu_head);
Johannes Bergaf6b6372009-12-23 13:15:35 +010065}
66
Johannes Bergb8bc4b02009-12-23 13:15:42 +010067static enum work_action __must_check
68ieee80211_remain_on_channel_timeout(struct ieee80211_work *wk)
69{
Johannes Bergb8bc4b02009-12-23 13:15:42 +010070 /*
71 * First time we run, do nothing -- the generic code will
72 * have switched to the right channel etc.
73 */
Johannes Berg723bae72010-01-25 13:36:36 +010074 if (!wk->started) {
Johannes Berge4da8c32009-12-23 13:15:43 +010075 wk->timeout = jiffies + msecs_to_jiffies(wk->remain.duration);
76
Kalle Valo1990ca62009-12-30 14:42:20 +020077 cfg80211_ready_on_channel(wk->sdata->dev, (unsigned long) wk,
78 wk->chan, wk->chan_type,
79 wk->remain.duration, GFP_KERNEL);
Johannes Berge4da8c32009-12-23 13:15:43 +010080
Johannes Bergb8bc4b02009-12-23 13:15:42 +010081 return WORK_ACT_NONE;
82 }
83
Johannes Berge4da8c32009-12-23 13:15:43 +010084 return WORK_ACT_TIMEOUT;
Johannes Bergb8bc4b02009-12-23 13:15:42 +010085}
86
Johannes Berge5b900d2010-07-29 16:08:55 +020087static enum work_action __must_check
Johannes Bergf30221e2010-11-25 10:02:30 +010088ieee80211_offchannel_tx(struct ieee80211_work *wk)
89{
90 if (!wk->started) {
91 wk->timeout = jiffies + msecs_to_jiffies(wk->offchan_tx.wait);
92
93 /*
94 * After this, offchan_tx.frame remains but now is no
95 * longer a valid pointer -- we still need it as the
Johannes Berg28a1bcd2011-10-04 18:27:10 +020096 * cookie for canceling this work/status matching.
Johannes Bergf30221e2010-11-25 10:02:30 +010097 */
98 ieee80211_tx_skb(wk->sdata, wk->offchan_tx.frame);
99
100 return WORK_ACT_NONE;
101 }
102
103 return WORK_ACT_TIMEOUT;
104}
105
Johannes Bergaf6b6372009-12-23 13:15:35 +0100106static void ieee80211_work_timer(unsigned long data)
107{
108 struct ieee80211_local *local = (void *) data;
109
110 if (local->quiescing)
111 return;
112
113 ieee80211_queue_work(&local->hw, &local->work_work);
114}
115
116static void ieee80211_work_work(struct work_struct *work)
117{
118 struct ieee80211_local *local =
119 container_of(work, struct ieee80211_local, work_work);
Johannes Bergaf6b6372009-12-23 13:15:35 +0100120 struct ieee80211_work *wk, *tmp;
121 LIST_HEAD(free_work);
122 enum work_action rma;
Johannes Berge4da8c32009-12-23 13:15:43 +0100123 bool remain_off_channel = false;
Johannes Bergaf6b6372009-12-23 13:15:35 +0100124
Johannes Bergaf6b6372009-12-23 13:15:35 +0100125 /*
126 * ieee80211_queue_work() should have picked up most cases,
Walter Goldens77c20612010-05-18 04:44:54 -0700127 * here we'll pick the rest.
Johannes Bergaf6b6372009-12-23 13:15:35 +0100128 */
129 if (WARN(local->suspended, "work scheduled while going to suspend\n"))
130 return;
131
Johannes Berga1699b72010-07-30 16:46:07 +0200132 mutex_lock(&local->mtx);
Johannes Bergaf6b6372009-12-23 13:15:35 +0100133
Stanislaw Gruszka5f546072012-03-28 16:01:20 +0200134 if (local->scanning) {
135 mutex_unlock(&local->mtx);
136 return;
137 }
138
Johannes Berg7da7cc12010-08-05 17:02:38 +0200139 ieee80211_recalc_idle(local);
140
Johannes Bergaf6b6372009-12-23 13:15:35 +0100141 list_for_each_entry_safe(wk, tmp, &local->work_list, list) {
Johannes Berg723bae72010-01-25 13:36:36 +0100142 bool started = wk->started;
143
Johannes Berge4da8c32009-12-23 13:15:43 +0100144 /* mark work as started if it's on the current off-channel */
Johannes Berg723bae72010-01-25 13:36:36 +0100145 if (!started && local->tmp_channel &&
Johannes Berge4da8c32009-12-23 13:15:43 +0100146 wk->chan == local->tmp_channel &&
147 wk->chan_type == local->tmp_channel_type) {
Johannes Berg723bae72010-01-25 13:36:36 +0100148 started = true;
Johannes Berg81ac3462010-01-06 15:30:58 +0100149 wk->timeout = jiffies;
Johannes Berge4da8c32009-12-23 13:15:43 +0100150 }
151
Johannes Berg723bae72010-01-25 13:36:36 +0100152 if (!started && !local->tmp_channel) {
Johannes Berge76aadc2011-11-29 10:20:02 +0100153 ieee80211_offchannel_stop_vifs(local, true);
Ben Greearda2fd1f2011-02-07 13:44:36 -0800154
Johannes Berge4da8c32009-12-23 13:15:43 +0100155 local->tmp_channel = wk->chan;
Johannes Berge76aadc2011-11-29 10:20:02 +0100156 local->tmp_channel_type = wk->chan_type;
157
158 ieee80211_hw_config(local, 0);
Ben Greearb23b0252011-02-04 11:54:17 -0800159
Johannes Berg723bae72010-01-25 13:36:36 +0100160 started = true;
Johannes Berge4da8c32009-12-23 13:15:43 +0100161 wk->timeout = jiffies;
162 }
163
164 /* don't try to work with items that aren't started */
Johannes Berg723bae72010-01-25 13:36:36 +0100165 if (!started)
Johannes Berge4da8c32009-12-23 13:15:43 +0100166 continue;
167
Johannes Bergaf6b6372009-12-23 13:15:35 +0100168 if (time_is_after_jiffies(wk->timeout)) {
169 /*
170 * This work item isn't supposed to be worked on
171 * right now, but take care to adjust the timer
172 * properly.
173 */
174 run_again(local, wk->timeout);
175 continue;
176 }
177
178 switch (wk->type) {
179 default:
180 WARN_ON(1);
181 /* nothing */
182 rma = WORK_ACT_NONE;
183 break;
Johannes Bergb8bc4b02009-12-23 13:15:42 +0100184 case IEEE80211_WORK_ABORT:
185 rma = WORK_ACT_TIMEOUT;
Juuso Oikarinen0e0a2282010-02-26 08:13:41 +0200186 break;
Johannes Bergb8bc4b02009-12-23 13:15:42 +0100187 case IEEE80211_WORK_REMAIN_ON_CHANNEL:
188 rma = ieee80211_remain_on_channel_timeout(wk);
189 break;
Johannes Bergf30221e2010-11-25 10:02:30 +0100190 case IEEE80211_WORK_OFFCHANNEL_TX:
191 rma = ieee80211_offchannel_tx(wk);
192 break;
Johannes Bergaf6b6372009-12-23 13:15:35 +0100193 }
194
Johannes Berg723bae72010-01-25 13:36:36 +0100195 wk->started = started;
196
Johannes Bergaf6b6372009-12-23 13:15:35 +0100197 switch (rma) {
198 case WORK_ACT_NONE:
Johannes Berge4da8c32009-12-23 13:15:43 +0100199 /* might have changed the timeout */
200 run_again(local, wk->timeout);
Johannes Bergaf6b6372009-12-23 13:15:35 +0100201 break;
202 case WORK_ACT_TIMEOUT:
203 list_del_rcu(&wk->list);
204 synchronize_rcu();
205 list_add(&wk->list, &free_work);
206 break;
207 default:
208 WARN(1, "unexpected: %d", rma);
209 }
210 }
211
Johannes Berge4da8c32009-12-23 13:15:43 +0100212 list_for_each_entry(wk, &local->work_list, list) {
213 if (!wk->started)
214 continue;
Johannes Berge76aadc2011-11-29 10:20:02 +0100215 if (wk->chan != local->tmp_channel ||
216 wk->chan_type != local->tmp_channel_type)
Johannes Berge4da8c32009-12-23 13:15:43 +0100217 continue;
218 remain_off_channel = true;
219 }
220
221 if (!remain_off_channel && local->tmp_channel) {
222 local->tmp_channel = NULL;
Johannes Berge76aadc2011-11-29 10:20:02 +0100223 ieee80211_hw_config(local, 0);
Ben Greearb23b0252011-02-04 11:54:17 -0800224
Johannes Berge76aadc2011-11-29 10:20:02 +0100225 ieee80211_offchannel_return(local, true);
Ben Greearb23b0252011-02-04 11:54:17 -0800226
Johannes Berge4da8c32009-12-23 13:15:43 +0100227 /* give connection some time to breathe */
228 run_again(local, jiffies + HZ/2);
229 }
230
Johannes Berge4da8c32009-12-23 13:15:43 +0100231 ieee80211_recalc_idle(local);
Stanislaw Gruszka133d40f2012-03-28 16:01:19 +0200232 ieee80211_run_deferred_scan(local);
Johannes Berge4da8c32009-12-23 13:15:43 +0100233
Johannes Berg7da7cc12010-08-05 17:02:38 +0200234 mutex_unlock(&local->mtx);
235
Johannes Bergaf6b6372009-12-23 13:15:35 +0100236 list_for_each_entry_safe(wk, tmp, &free_work, list) {
237 wk->done(wk, NULL);
238 list_del(&wk->list);
239 kfree(wk);
240 }
241}
242
243void ieee80211_add_work(struct ieee80211_work *wk)
244{
245 struct ieee80211_local *local;
246
247 if (WARN_ON(!wk->chan))
248 return;
249
250 if (WARN_ON(!wk->sdata))
251 return;
252
253 if (WARN_ON(!wk->done))
254 return;
255
Johannes Berg81ac3462010-01-06 15:30:58 +0100256 if (WARN_ON(!ieee80211_sdata_running(wk->sdata)))
257 return;
258
Johannes Berge4da8c32009-12-23 13:15:43 +0100259 wk->started = false;
Johannes Bergaf6b6372009-12-23 13:15:35 +0100260
261 local = wk->sdata->local;
Johannes Berga1699b72010-07-30 16:46:07 +0200262 mutex_lock(&local->mtx);
Johannes Bergaf6b6372009-12-23 13:15:35 +0100263 list_add_tail(&wk->list, &local->work_list);
Johannes Berga1699b72010-07-30 16:46:07 +0200264 mutex_unlock(&local->mtx);
Johannes Bergaf6b6372009-12-23 13:15:35 +0100265
266 ieee80211_queue_work(&local->hw, &local->work_work);
267}
268
269void ieee80211_work_init(struct ieee80211_local *local)
270{
Johannes Bergaf6b6372009-12-23 13:15:35 +0100271 INIT_LIST_HEAD(&local->work_list);
272 setup_timer(&local->work_timer, ieee80211_work_timer,
273 (unsigned long)local);
274 INIT_WORK(&local->work_work, ieee80211_work_work);
Johannes Bergaf6b6372009-12-23 13:15:35 +0100275}
276
277void ieee80211_work_purge(struct ieee80211_sub_if_data *sdata)
278{
279 struct ieee80211_local *local = sdata->local;
Johannes Bergb8bc4b02009-12-23 13:15:42 +0100280 struct ieee80211_work *wk;
Herton Ronaldo Krzesinski8808f642010-12-13 11:43:51 -0200281 bool cleanup = false;
Johannes Bergaf6b6372009-12-23 13:15:35 +0100282
Johannes Berga1699b72010-07-30 16:46:07 +0200283 mutex_lock(&local->mtx);
Johannes Bergb8bc4b02009-12-23 13:15:42 +0100284 list_for_each_entry(wk, &local->work_list, list) {
Johannes Bergaf6b6372009-12-23 13:15:35 +0100285 if (wk->sdata != sdata)
286 continue;
Herton Ronaldo Krzesinski8808f642010-12-13 11:43:51 -0200287 cleanup = true;
Johannes Bergb8bc4b02009-12-23 13:15:42 +0100288 wk->type = IEEE80211_WORK_ABORT;
Johannes Berge4da8c32009-12-23 13:15:43 +0100289 wk->started = true;
290 wk->timeout = jiffies;
Johannes Bergb8bc4b02009-12-23 13:15:42 +0100291 }
Johannes Berga1699b72010-07-30 16:46:07 +0200292 mutex_unlock(&local->mtx);
Johannes Bergb8bc4b02009-12-23 13:15:42 +0100293
294 /* run cleanups etc. */
Herton Ronaldo Krzesinski8808f642010-12-13 11:43:51 -0200295 if (cleanup)
296 ieee80211_work_work(&local->work_work);
Johannes Bergb8bc4b02009-12-23 13:15:42 +0100297
Johannes Berga1699b72010-07-30 16:46:07 +0200298 mutex_lock(&local->mtx);
Johannes Bergb8bc4b02009-12-23 13:15:42 +0100299 list_for_each_entry(wk, &local->work_list, list) {
300 if (wk->sdata != sdata)
301 continue;
302 WARN_ON(1);
303 break;
Johannes Bergaf6b6372009-12-23 13:15:35 +0100304 }
Johannes Berga1699b72010-07-30 16:46:07 +0200305 mutex_unlock(&local->mtx);
Johannes Bergaf6b6372009-12-23 13:15:35 +0100306}
307
Johannes Berge4da8c32009-12-23 13:15:43 +0100308static enum work_done_result ieee80211_remain_done(struct ieee80211_work *wk,
309 struct sk_buff *skb)
310{
311 /*
312 * We are done serving the remain-on-channel command.
313 */
Kalle Valo1990ca62009-12-30 14:42:20 +0200314 cfg80211_remain_on_channel_expired(wk->sdata->dev, (unsigned long) wk,
Johannes Berge4da8c32009-12-23 13:15:43 +0100315 wk->chan, wk->chan_type,
316 GFP_KERNEL);
317
318 return WORK_DONE_DESTROY;
319}
320
Johannes Bergb8bc4b02009-12-23 13:15:42 +0100321int ieee80211_wk_remain_on_channel(struct ieee80211_sub_if_data *sdata,
322 struct ieee80211_channel *chan,
323 enum nl80211_channel_type channel_type,
324 unsigned int duration, u64 *cookie)
325{
Johannes Bergb8bc4b02009-12-23 13:15:42 +0100326 struct ieee80211_work *wk;
327
328 wk = kzalloc(sizeof(*wk), GFP_KERNEL);
329 if (!wk)
330 return -ENOMEM;
331
332 wk->type = IEEE80211_WORK_REMAIN_ON_CHANNEL;
333 wk->chan = chan;
Johannes Berge4da8c32009-12-23 13:15:43 +0100334 wk->chan_type = channel_type;
Johannes Bergb8bc4b02009-12-23 13:15:42 +0100335 wk->sdata = sdata;
Johannes Berge4da8c32009-12-23 13:15:43 +0100336 wk->done = ieee80211_remain_done;
Johannes Bergb8bc4b02009-12-23 13:15:42 +0100337
Johannes Berge4da8c32009-12-23 13:15:43 +0100338 wk->remain.duration = duration;
Johannes Bergb8bc4b02009-12-23 13:15:42 +0100339
Kalle Valo1990ca62009-12-30 14:42:20 +0200340 *cookie = (unsigned long) wk;
Johannes Bergb8bc4b02009-12-23 13:15:42 +0100341
342 ieee80211_add_work(wk);
343
Johannes Bergb8bc4b02009-12-23 13:15:42 +0100344 return 0;
345}
346
347int ieee80211_wk_cancel_remain_on_channel(struct ieee80211_sub_if_data *sdata,
348 u64 cookie)
349{
350 struct ieee80211_local *local = sdata->local;
351 struct ieee80211_work *wk, *tmp;
352 bool found = false;
353
Johannes Berga1699b72010-07-30 16:46:07 +0200354 mutex_lock(&local->mtx);
Johannes Bergb8bc4b02009-12-23 13:15:42 +0100355 list_for_each_entry_safe(wk, tmp, &local->work_list, list) {
Kalle Valo1990ca62009-12-30 14:42:20 +0200356 if ((unsigned long) wk == cookie) {
Johannes Berge4da8c32009-12-23 13:15:43 +0100357 wk->timeout = jiffies;
Johannes Bergb8bc4b02009-12-23 13:15:42 +0100358 found = true;
Johannes Bergb8bc4b02009-12-23 13:15:42 +0100359 break;
360 }
361 }
Johannes Berga1699b72010-07-30 16:46:07 +0200362 mutex_unlock(&local->mtx);
Johannes Bergb8bc4b02009-12-23 13:15:42 +0100363
364 if (!found)
365 return -ENOENT;
366
Johannes Berge4da8c32009-12-23 13:15:43 +0100367 ieee80211_queue_work(&local->hw, &local->work_work);
Johannes Bergb8bc4b02009-12-23 13:15:42 +0100368
369 return 0;
370}