blob: c6e230efa04952314cf3f995f4c58d08ba142038 [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
125 if (local->scanning)
126 return;
127
128 /*
129 * ieee80211_queue_work() should have picked up most cases,
Walter Goldens77c20612010-05-18 04:44:54 -0700130 * here we'll pick the rest.
Johannes Bergaf6b6372009-12-23 13:15:35 +0100131 */
132 if (WARN(local->suspended, "work scheduled while going to suspend\n"))
133 return;
134
Johannes Berga1699b72010-07-30 16:46:07 +0200135 mutex_lock(&local->mtx);
Johannes Bergaf6b6372009-12-23 13:15:35 +0100136
Johannes Berg7da7cc12010-08-05 17:02:38 +0200137 ieee80211_recalc_idle(local);
138
Johannes Bergaf6b6372009-12-23 13:15:35 +0100139 list_for_each_entry_safe(wk, tmp, &local->work_list, list) {
Johannes Berg723bae72010-01-25 13:36:36 +0100140 bool started = wk->started;
141
Johannes Berge4da8c32009-12-23 13:15:43 +0100142 /* mark work as started if it's on the current off-channel */
Johannes Berg723bae72010-01-25 13:36:36 +0100143 if (!started && local->tmp_channel &&
Johannes Berge4da8c32009-12-23 13:15:43 +0100144 wk->chan == local->tmp_channel &&
145 wk->chan_type == local->tmp_channel_type) {
Johannes Berg723bae72010-01-25 13:36:36 +0100146 started = true;
Johannes Berg81ac3462010-01-06 15:30:58 +0100147 wk->timeout = jiffies;
Johannes Berge4da8c32009-12-23 13:15:43 +0100148 }
149
Johannes Berg723bae72010-01-25 13:36:36 +0100150 if (!started && !local->tmp_channel) {
Johannes Berge76aadc2011-11-29 10:20:02 +0100151 ieee80211_offchannel_stop_vifs(local, true);
Ben Greearda2fd1f2011-02-07 13:44:36 -0800152
Johannes Berge4da8c32009-12-23 13:15:43 +0100153 local->tmp_channel = wk->chan;
Johannes Berge76aadc2011-11-29 10:20:02 +0100154 local->tmp_channel_type = wk->chan_type;
155
156 ieee80211_hw_config(local, 0);
Ben Greearb23b0252011-02-04 11:54:17 -0800157
Johannes Berg723bae72010-01-25 13:36:36 +0100158 started = true;
Johannes Berge4da8c32009-12-23 13:15:43 +0100159 wk->timeout = jiffies;
160 }
161
162 /* don't try to work with items that aren't started */
Johannes Berg723bae72010-01-25 13:36:36 +0100163 if (!started)
Johannes Berge4da8c32009-12-23 13:15:43 +0100164 continue;
165
Johannes Bergaf6b6372009-12-23 13:15:35 +0100166 if (time_is_after_jiffies(wk->timeout)) {
167 /*
168 * This work item isn't supposed to be worked on
169 * right now, but take care to adjust the timer
170 * properly.
171 */
172 run_again(local, wk->timeout);
173 continue;
174 }
175
176 switch (wk->type) {
177 default:
178 WARN_ON(1);
179 /* nothing */
180 rma = WORK_ACT_NONE;
181 break;
Johannes Bergb8bc4b02009-12-23 13:15:42 +0100182 case IEEE80211_WORK_ABORT:
183 rma = WORK_ACT_TIMEOUT;
Juuso Oikarinen0e0a2282010-02-26 08:13:41 +0200184 break;
Johannes Bergb8bc4b02009-12-23 13:15:42 +0100185 case IEEE80211_WORK_REMAIN_ON_CHANNEL:
186 rma = ieee80211_remain_on_channel_timeout(wk);
187 break;
Johannes Bergf30221e2010-11-25 10:02:30 +0100188 case IEEE80211_WORK_OFFCHANNEL_TX:
189 rma = ieee80211_offchannel_tx(wk);
190 break;
Johannes Bergaf6b6372009-12-23 13:15:35 +0100191 }
192
Johannes Berg723bae72010-01-25 13:36:36 +0100193 wk->started = started;
194
Johannes Bergaf6b6372009-12-23 13:15:35 +0100195 switch (rma) {
196 case WORK_ACT_NONE:
Johannes Berge4da8c32009-12-23 13:15:43 +0100197 /* might have changed the timeout */
198 run_again(local, wk->timeout);
Johannes Bergaf6b6372009-12-23 13:15:35 +0100199 break;
200 case WORK_ACT_TIMEOUT:
201 list_del_rcu(&wk->list);
202 synchronize_rcu();
203 list_add(&wk->list, &free_work);
204 break;
205 default:
206 WARN(1, "unexpected: %d", rma);
207 }
208 }
209
Johannes Berge4da8c32009-12-23 13:15:43 +0100210 list_for_each_entry(wk, &local->work_list, list) {
211 if (!wk->started)
212 continue;
Johannes Berge76aadc2011-11-29 10:20:02 +0100213 if (wk->chan != local->tmp_channel ||
214 wk->chan_type != local->tmp_channel_type)
Johannes Berge4da8c32009-12-23 13:15:43 +0100215 continue;
216 remain_off_channel = true;
217 }
218
219 if (!remain_off_channel && local->tmp_channel) {
220 local->tmp_channel = NULL;
Johannes Berge76aadc2011-11-29 10:20:02 +0100221 ieee80211_hw_config(local, 0);
Ben Greearb23b0252011-02-04 11:54:17 -0800222
Johannes Berge76aadc2011-11-29 10:20:02 +0100223 ieee80211_offchannel_return(local, true);
Ben Greearb23b0252011-02-04 11:54:17 -0800224
Johannes Berge4da8c32009-12-23 13:15:43 +0100225 /* give connection some time to breathe */
226 run_again(local, jiffies + HZ/2);
227 }
228
Teemu Paasikivi68dd5b72010-04-09 13:07:55 +0300229 if (list_empty(&local->work_list) && local->scan_req &&
230 !local->scanning)
Johannes Bergaf6b6372009-12-23 13:15:35 +0100231 ieee80211_queue_delayed_work(&local->hw,
232 &local->scan_work,
233 round_jiffies_relative(0));
234
Johannes Berge4da8c32009-12-23 13:15:43 +0100235 ieee80211_recalc_idle(local);
236
Johannes Berg7da7cc12010-08-05 17:02:38 +0200237 mutex_unlock(&local->mtx);
238
Johannes Bergaf6b6372009-12-23 13:15:35 +0100239 list_for_each_entry_safe(wk, tmp, &free_work, list) {
240 wk->done(wk, NULL);
241 list_del(&wk->list);
242 kfree(wk);
243 }
244}
245
246void ieee80211_add_work(struct ieee80211_work *wk)
247{
248 struct ieee80211_local *local;
249
250 if (WARN_ON(!wk->chan))
251 return;
252
253 if (WARN_ON(!wk->sdata))
254 return;
255
256 if (WARN_ON(!wk->done))
257 return;
258
Johannes Berg81ac3462010-01-06 15:30:58 +0100259 if (WARN_ON(!ieee80211_sdata_running(wk->sdata)))
260 return;
261
Johannes Berge4da8c32009-12-23 13:15:43 +0100262 wk->started = false;
Johannes Bergaf6b6372009-12-23 13:15:35 +0100263
264 local = wk->sdata->local;
Johannes Berga1699b72010-07-30 16:46:07 +0200265 mutex_lock(&local->mtx);
Johannes Bergaf6b6372009-12-23 13:15:35 +0100266 list_add_tail(&wk->list, &local->work_list);
Johannes Berga1699b72010-07-30 16:46:07 +0200267 mutex_unlock(&local->mtx);
Johannes Bergaf6b6372009-12-23 13:15:35 +0100268
269 ieee80211_queue_work(&local->hw, &local->work_work);
270}
271
272void ieee80211_work_init(struct ieee80211_local *local)
273{
Johannes Bergaf6b6372009-12-23 13:15:35 +0100274 INIT_LIST_HEAD(&local->work_list);
275 setup_timer(&local->work_timer, ieee80211_work_timer,
276 (unsigned long)local);
277 INIT_WORK(&local->work_work, ieee80211_work_work);
Johannes Bergaf6b6372009-12-23 13:15:35 +0100278}
279
280void ieee80211_work_purge(struct ieee80211_sub_if_data *sdata)
281{
282 struct ieee80211_local *local = sdata->local;
Johannes Bergb8bc4b02009-12-23 13:15:42 +0100283 struct ieee80211_work *wk;
Herton Ronaldo Krzesinski8808f642010-12-13 11:43:51 -0200284 bool cleanup = false;
Johannes Bergaf6b6372009-12-23 13:15:35 +0100285
Johannes Berga1699b72010-07-30 16:46:07 +0200286 mutex_lock(&local->mtx);
Johannes Bergb8bc4b02009-12-23 13:15:42 +0100287 list_for_each_entry(wk, &local->work_list, list) {
Johannes Bergaf6b6372009-12-23 13:15:35 +0100288 if (wk->sdata != sdata)
289 continue;
Herton Ronaldo Krzesinski8808f642010-12-13 11:43:51 -0200290 cleanup = true;
Johannes Bergb8bc4b02009-12-23 13:15:42 +0100291 wk->type = IEEE80211_WORK_ABORT;
Johannes Berge4da8c32009-12-23 13:15:43 +0100292 wk->started = true;
293 wk->timeout = jiffies;
Johannes Bergb8bc4b02009-12-23 13:15:42 +0100294 }
Johannes Berga1699b72010-07-30 16:46:07 +0200295 mutex_unlock(&local->mtx);
Johannes Bergb8bc4b02009-12-23 13:15:42 +0100296
297 /* run cleanups etc. */
Herton Ronaldo Krzesinski8808f642010-12-13 11:43:51 -0200298 if (cleanup)
299 ieee80211_work_work(&local->work_work);
Johannes Bergb8bc4b02009-12-23 13:15:42 +0100300
Johannes Berga1699b72010-07-30 16:46:07 +0200301 mutex_lock(&local->mtx);
Johannes Bergb8bc4b02009-12-23 13:15:42 +0100302 list_for_each_entry(wk, &local->work_list, list) {
303 if (wk->sdata != sdata)
304 continue;
305 WARN_ON(1);
306 break;
Johannes Bergaf6b6372009-12-23 13:15:35 +0100307 }
Johannes Berga1699b72010-07-30 16:46:07 +0200308 mutex_unlock(&local->mtx);
Johannes Bergaf6b6372009-12-23 13:15:35 +0100309}
310
Johannes Berge4da8c32009-12-23 13:15:43 +0100311static enum work_done_result ieee80211_remain_done(struct ieee80211_work *wk,
312 struct sk_buff *skb)
313{
314 /*
315 * We are done serving the remain-on-channel command.
316 */
Kalle Valo1990ca62009-12-30 14:42:20 +0200317 cfg80211_remain_on_channel_expired(wk->sdata->dev, (unsigned long) wk,
Johannes Berge4da8c32009-12-23 13:15:43 +0100318 wk->chan, wk->chan_type,
319 GFP_KERNEL);
320
321 return WORK_DONE_DESTROY;
322}
323
Johannes Bergb8bc4b02009-12-23 13:15:42 +0100324int ieee80211_wk_remain_on_channel(struct ieee80211_sub_if_data *sdata,
325 struct ieee80211_channel *chan,
326 enum nl80211_channel_type channel_type,
327 unsigned int duration, u64 *cookie)
328{
Johannes Bergb8bc4b02009-12-23 13:15:42 +0100329 struct ieee80211_work *wk;
330
331 wk = kzalloc(sizeof(*wk), GFP_KERNEL);
332 if (!wk)
333 return -ENOMEM;
334
335 wk->type = IEEE80211_WORK_REMAIN_ON_CHANNEL;
336 wk->chan = chan;
Johannes Berge4da8c32009-12-23 13:15:43 +0100337 wk->chan_type = channel_type;
Johannes Bergb8bc4b02009-12-23 13:15:42 +0100338 wk->sdata = sdata;
Johannes Berge4da8c32009-12-23 13:15:43 +0100339 wk->done = ieee80211_remain_done;
Johannes Bergb8bc4b02009-12-23 13:15:42 +0100340
Johannes Berge4da8c32009-12-23 13:15:43 +0100341 wk->remain.duration = duration;
Johannes Bergb8bc4b02009-12-23 13:15:42 +0100342
Kalle Valo1990ca62009-12-30 14:42:20 +0200343 *cookie = (unsigned long) wk;
Johannes Bergb8bc4b02009-12-23 13:15:42 +0100344
345 ieee80211_add_work(wk);
346
Johannes Bergb8bc4b02009-12-23 13:15:42 +0100347 return 0;
348}
349
350int ieee80211_wk_cancel_remain_on_channel(struct ieee80211_sub_if_data *sdata,
351 u64 cookie)
352{
353 struct ieee80211_local *local = sdata->local;
354 struct ieee80211_work *wk, *tmp;
355 bool found = false;
356
Johannes Berga1699b72010-07-30 16:46:07 +0200357 mutex_lock(&local->mtx);
Johannes Bergb8bc4b02009-12-23 13:15:42 +0100358 list_for_each_entry_safe(wk, tmp, &local->work_list, list) {
Kalle Valo1990ca62009-12-30 14:42:20 +0200359 if ((unsigned long) wk == cookie) {
Johannes Berge4da8c32009-12-23 13:15:43 +0100360 wk->timeout = jiffies;
Johannes Bergb8bc4b02009-12-23 13:15:42 +0100361 found = true;
Johannes Bergb8bc4b02009-12-23 13:15:42 +0100362 break;
363 }
364 }
Johannes Berga1699b72010-07-30 16:46:07 +0200365 mutex_unlock(&local->mtx);
Johannes Bergb8bc4b02009-12-23 13:15:42 +0100366
367 if (!found)
368 return -ENOENT;
369
Johannes Berge4da8c32009-12-23 13:15:43 +0100370 ieee80211_queue_work(&local->hw, &local->work_work);
Johannes Bergb8bc4b02009-12-23 13:15:42 +0100371
372 return 0;
373}