at v3.4-rc2 373 lines 8.7 kB view raw
1/* 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> 22#include <linux/slab.h> 23#include <net/mac80211.h> 24#include <asm/unaligned.h> 25 26#include "ieee80211_i.h" 27#include "rate.h" 28#include "driver-ops.h" 29 30enum work_action { 31 WORK_ACT_NONE, 32 WORK_ACT_TIMEOUT, 33}; 34 35 36/* utils */ 37static inline void ASSERT_WORK_MTX(struct ieee80211_local *local) 38{ 39 lockdep_assert_held(&local->mtx); 40} 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 62void free_work(struct ieee80211_work *wk) 63{ 64 kfree_rcu(wk, rcu_head); 65} 66 67static enum work_action __must_check 68ieee80211_remain_on_channel_timeout(struct ieee80211_work *wk) 69{ 70 /* 71 * First time we run, do nothing -- the generic code will 72 * have switched to the right channel etc. 73 */ 74 if (!wk->started) { 75 wk->timeout = jiffies + msecs_to_jiffies(wk->remain.duration); 76 77 cfg80211_ready_on_channel(wk->sdata->dev, (unsigned long) wk, 78 wk->chan, wk->chan_type, 79 wk->remain.duration, GFP_KERNEL); 80 81 return WORK_ACT_NONE; 82 } 83 84 return WORK_ACT_TIMEOUT; 85} 86 87static enum work_action __must_check 88ieee80211_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 96 * cookie for canceling this work/status matching. 97 */ 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 106static 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); 120 struct ieee80211_work *wk, *tmp; 121 LIST_HEAD(free_work); 122 enum work_action rma; 123 bool remain_off_channel = false; 124 125 if (local->scanning) 126 return; 127 128 /* 129 * ieee80211_queue_work() should have picked up most cases, 130 * here we'll pick the rest. 131 */ 132 if (WARN(local->suspended, "work scheduled while going to suspend\n")) 133 return; 134 135 mutex_lock(&local->mtx); 136 137 ieee80211_recalc_idle(local); 138 139 list_for_each_entry_safe(wk, tmp, &local->work_list, list) { 140 bool started = wk->started; 141 142 /* mark work as started if it's on the current off-channel */ 143 if (!started && local->tmp_channel && 144 wk->chan == local->tmp_channel && 145 wk->chan_type == local->tmp_channel_type) { 146 started = true; 147 wk->timeout = jiffies; 148 } 149 150 if (!started && !local->tmp_channel) { 151 ieee80211_offchannel_stop_vifs(local, true); 152 153 local->tmp_channel = wk->chan; 154 local->tmp_channel_type = wk->chan_type; 155 156 ieee80211_hw_config(local, 0); 157 158 started = true; 159 wk->timeout = jiffies; 160 } 161 162 /* don't try to work with items that aren't started */ 163 if (!started) 164 continue; 165 166 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; 182 case IEEE80211_WORK_ABORT: 183 rma = WORK_ACT_TIMEOUT; 184 break; 185 case IEEE80211_WORK_REMAIN_ON_CHANNEL: 186 rma = ieee80211_remain_on_channel_timeout(wk); 187 break; 188 case IEEE80211_WORK_OFFCHANNEL_TX: 189 rma = ieee80211_offchannel_tx(wk); 190 break; 191 } 192 193 wk->started = started; 194 195 switch (rma) { 196 case WORK_ACT_NONE: 197 /* might have changed the timeout */ 198 run_again(local, wk->timeout); 199 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 210 list_for_each_entry(wk, &local->work_list, list) { 211 if (!wk->started) 212 continue; 213 if (wk->chan != local->tmp_channel || 214 wk->chan_type != local->tmp_channel_type) 215 continue; 216 remain_off_channel = true; 217 } 218 219 if (!remain_off_channel && local->tmp_channel) { 220 local->tmp_channel = NULL; 221 ieee80211_hw_config(local, 0); 222 223 ieee80211_offchannel_return(local, true); 224 225 /* give connection some time to breathe */ 226 run_again(local, jiffies + HZ/2); 227 } 228 229 if (list_empty(&local->work_list) && local->scan_req && 230 !local->scanning) 231 ieee80211_queue_delayed_work(&local->hw, 232 &local->scan_work, 233 round_jiffies_relative(0)); 234 235 ieee80211_recalc_idle(local); 236 237 mutex_unlock(&local->mtx); 238 239 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 259 if (WARN_ON(!ieee80211_sdata_running(wk->sdata))) 260 return; 261 262 wk->started = false; 263 264 local = wk->sdata->local; 265 mutex_lock(&local->mtx); 266 list_add_tail(&wk->list, &local->work_list); 267 mutex_unlock(&local->mtx); 268 269 ieee80211_queue_work(&local->hw, &local->work_work); 270} 271 272void ieee80211_work_init(struct ieee80211_local *local) 273{ 274 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); 278} 279 280void ieee80211_work_purge(struct ieee80211_sub_if_data *sdata) 281{ 282 struct ieee80211_local *local = sdata->local; 283 struct ieee80211_work *wk; 284 bool cleanup = false; 285 286 mutex_lock(&local->mtx); 287 list_for_each_entry(wk, &local->work_list, list) { 288 if (wk->sdata != sdata) 289 continue; 290 cleanup = true; 291 wk->type = IEEE80211_WORK_ABORT; 292 wk->started = true; 293 wk->timeout = jiffies; 294 } 295 mutex_unlock(&local->mtx); 296 297 /* run cleanups etc. */ 298 if (cleanup) 299 ieee80211_work_work(&local->work_work); 300 301 mutex_lock(&local->mtx); 302 list_for_each_entry(wk, &local->work_list, list) { 303 if (wk->sdata != sdata) 304 continue; 305 WARN_ON(1); 306 break; 307 } 308 mutex_unlock(&local->mtx); 309} 310 311static 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 */ 317 cfg80211_remain_on_channel_expired(wk->sdata->dev, (unsigned long) wk, 318 wk->chan, wk->chan_type, 319 GFP_KERNEL); 320 321 return WORK_DONE_DESTROY; 322} 323 324int 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{ 329 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; 337 wk->chan_type = channel_type; 338 wk->sdata = sdata; 339 wk->done = ieee80211_remain_done; 340 341 wk->remain.duration = duration; 342 343 *cookie = (unsigned long) wk; 344 345 ieee80211_add_work(wk); 346 347 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 357 mutex_lock(&local->mtx); 358 list_for_each_entry_safe(wk, tmp, &local->work_list, list) { 359 if ((unsigned long) wk == cookie) { 360 wk->timeout = jiffies; 361 found = true; 362 break; 363 } 364 } 365 mutex_unlock(&local->mtx); 366 367 if (!found) 368 return -ENOENT; 369 370 ieee80211_queue_work(&local->hw, &local->work_work); 371 372 return 0; 373}