work.c
Go to the documentation of this file.
00001 /*
00002  * mac80211 work implementation
00003  *
00004  * Copyright 2003-2008, Jouni Malinen <j@w1.fi>
00005  * Copyright 2004, Instant802 Networks, Inc.
00006  * Copyright 2005, Devicescape Software, Inc.
00007  * Copyright 2006-2007  Jiri Benc <jbenc@suse.cz>
00008  * Copyright 2007, Michael Wu <flamingice@sourmilk.net>
00009  * Copyright 2009, Johannes Berg <johannes@sipsolutions.net>
00010  *
00011  * This program is free software; you can redistribute it and/or modify
00012  * it under the terms of the GNU General Public License version 2 as
00013  * published by the Free Software Foundation.
00014  */
00015 
00016 #include <linux/delay.h>
00017 #include <linux/if_ether.h>
00018 #include <linux/skbuff.h>
00019 #include <linux/if_arp.h>
00020 #include <linux/etherdevice.h>
00021 #include <linux/crc32.h>
00022 #include <linux/slab.h>
00023 #include <net/mac80211.h>
00024 #include <asm/unaligned.h>
00025 
00026 #include "ieee80211_i.h"
00027 #include "rate.h"
00028 #include "driver-ops.h"
00029 
00030 enum work_action {
00031         WORK_ACT_NONE,
00032         WORK_ACT_TIMEOUT,
00033 };
00034 
00035 
00036 /* utils */
00037 static inline void ASSERT_WORK_MTX(struct ieee80211_local *local)
00038 {
00039         lockdep_assert_held(&local->mtx);
00040 }
00041 
00042 /*
00043  * We can have multiple work items (and connection probing)
00044  * scheduling this timer, but we need to take care to only
00045  * reschedule it when it should fire _earlier_ than it was
00046  * asked for before, or if it's not pending right now. This
00047  * function ensures that. Note that it then is required to
00048  * run this function for all timeouts after the first one
00049  * has happened -- the work that runs from this timer will
00050  * do that.
00051  */
00052 static void run_again(struct ieee80211_local *local,
00053                       unsigned long timeout)
00054 {
00055         ASSERT_WORK_MTX(local);
00056 
00057         if (!timer_pending(&local->work_timer) ||
00058             time_before(timeout, local->work_timer.expires))
00059                 mod_timer(&local->work_timer, timeout);
00060 }
00061 
00062 void free_work(struct ieee80211_work *wk)
00063 {
00064         kfree_rcu(wk, rcu_head);
00065 }
00066 
00067 static enum work_action __must_check
00068 ieee80211_remain_on_channel_timeout(struct ieee80211_work *wk)
00069 {
00070         /*
00071          * First time we run, do nothing -- the generic code will
00072          * have switched to the right channel etc.
00073          */
00074         if (!wk->started) {
00075                 wk->timeout = jiffies + msecs_to_jiffies(wk->remain.duration);
00076 
00077                 cfg80211_ready_on_channel(wk->sdata->dev, (unsigned long) wk,
00078                                           wk->chan, wk->chan_type,
00079                                           wk->remain.duration, GFP_KERNEL);
00080 
00081                 return WORK_ACT_NONE;
00082         }
00083 
00084         return WORK_ACT_TIMEOUT;
00085 }
00086 
00087 static enum work_action __must_check
00088 ieee80211_offchannel_tx(struct ieee80211_work *wk)
00089 {
00090         if (!wk->started) {
00091                 wk->timeout = jiffies + msecs_to_jiffies(wk->offchan_tx.wait);
00092 
00093                 /*
00094                  * After this, offchan_tx.frame remains but now is no
00095                  * longer a valid pointer -- we still need it as the
00096                  * cookie for canceling this work/status matching.
00097                  */
00098                 ieee80211_tx_skb(wk->sdata, wk->offchan_tx.frame);
00099 
00100                 return WORK_ACT_NONE;
00101         }
00102 
00103         return WORK_ACT_TIMEOUT;
00104 }
00105 
00106 static void ieee80211_work_timer(unsigned long data)
00107 {
00108         struct ieee80211_local *local = (void *) data;
00109 
00110         if (local->quiescing)
00111                 return;
00112 
00113         ieee80211_queue_work(&local->hw, &local->work_work);
00114 }
00115 
00116 static void ieee80211_work_work(struct work_struct *work)
00117 {
00118         struct ieee80211_local *local =
00119                 container_of(work, struct ieee80211_local, work_work);
00120         struct ieee80211_work *wk, *tmp;
00121         LIST_HEAD(free_work);
00122         enum work_action rma;
00123         bool remain_off_channel = false;
00124 
00125         /*
00126          * ieee80211_queue_work() should have picked up most cases,
00127          * here we'll pick the rest.
00128          */
00129         if (WARN(local->suspended, "work scheduled while going to suspend\n"))
00130                 return;
00131 
00132         mutex_lock(&local->mtx);
00133 
00134         if (local->scanning) {
00135                 mutex_unlock(&local->mtx);
00136                 return;
00137         }
00138 
00139         ieee80211_recalc_idle(local);
00140 
00141         list_for_each_entry_safe(wk, tmp, &local->work_list, list) {
00142                 bool started = wk->started;
00143 
00144                 /* mark work as started if it's on the current off-channel */
00145                 if (!started && local->tmp_channel &&
00146                     wk->chan == local->tmp_channel &&
00147                     wk->chan_type == local->tmp_channel_type) {
00148                         started = true;
00149                         wk->timeout = jiffies;
00150                 }
00151 
00152                 if (!started && !local->tmp_channel) {
00153                         ieee80211_offchannel_stop_vifs(local, true);
00154 
00155                         local->tmp_channel = wk->chan;
00156                         local->tmp_channel_type = wk->chan_type;
00157 
00158                         ieee80211_hw_config(local, 0);
00159 
00160                         started = true;
00161                         wk->timeout = jiffies;
00162                 }
00163 
00164                 /* don't try to work with items that aren't started */
00165                 if (!started)
00166                         continue;
00167 
00168                 if (time_is_after_jiffies(wk->timeout)) {
00169                         /*
00170                          * This work item isn't supposed to be worked on
00171                          * right now, but take care to adjust the timer
00172                          * properly.
00173                          */
00174                         run_again(local, wk->timeout);
00175                         continue;
00176                 }
00177 
00178                 switch (wk->type) {
00179                 default:
00180                         WARN_ON(1);
00181                         /* nothing */
00182                         rma = WORK_ACT_NONE;
00183                         break;
00184                 case IEEE80211_WORK_ABORT:
00185                         rma = WORK_ACT_TIMEOUT;
00186                         break;
00187                 case IEEE80211_WORK_REMAIN_ON_CHANNEL:
00188                         rma = ieee80211_remain_on_channel_timeout(wk);
00189                         break;
00190                 case IEEE80211_WORK_OFFCHANNEL_TX:
00191                         rma = ieee80211_offchannel_tx(wk);
00192                         break;
00193                 }
00194 
00195                 wk->started = started;
00196 
00197                 switch (rma) {
00198                 case WORK_ACT_NONE:
00199                         /* might have changed the timeout */
00200                         run_again(local, wk->timeout);
00201                         break;
00202                 case WORK_ACT_TIMEOUT:
00203                         list_del_rcu(&wk->list);
00204                         synchronize_rcu();
00205                         list_add(&wk->list, &free_work);
00206                         break;
00207                 default:
00208                         WARN(1, "unexpected: %d", rma);
00209                 }
00210         }
00211 
00212         list_for_each_entry(wk, &local->work_list, list) {
00213                 if (!wk->started)
00214                         continue;
00215                 if (wk->chan != local->tmp_channel ||
00216                     wk->chan_type != local->tmp_channel_type)
00217                         continue;
00218                 remain_off_channel = true;
00219         }
00220 
00221         if (!remain_off_channel && local->tmp_channel) {
00222                 local->tmp_channel = NULL;
00223                 ieee80211_hw_config(local, 0);
00224 
00225                 ieee80211_offchannel_return(local, true);
00226 
00227                 /* give connection some time to breathe */
00228                 run_again(local, jiffies + HZ/2);
00229         }
00230 
00231         ieee80211_recalc_idle(local);
00232         ieee80211_run_deferred_scan(local);
00233 
00234         mutex_unlock(&local->mtx);
00235 
00236         list_for_each_entry_safe(wk, tmp, &free_work, list) {
00237                 wk->done(wk, NULL);
00238                 list_del(&wk->list);
00239                 kfree(wk);
00240         }
00241 }
00242 
00243 void ieee80211_add_work(struct ieee80211_work *wk)
00244 {
00245         struct ieee80211_local *local;
00246 
00247         if (WARN_ON(!wk->chan))
00248                 return;
00249 
00250         if (WARN_ON(!wk->sdata))
00251                 return;
00252 
00253         if (WARN_ON(!wk->done))
00254                 return;
00255 
00256         if (WARN_ON(!ieee80211_sdata_running(wk->sdata)))
00257                 return;
00258 
00259         wk->started = false;
00260 
00261         local = wk->sdata->local;
00262         mutex_lock(&local->mtx);
00263         list_add_tail(&wk->list, &local->work_list);
00264         mutex_unlock(&local->mtx);
00265 
00266         ieee80211_queue_work(&local->hw, &local->work_work);
00267 }
00268 
00269 void ieee80211_work_init(struct ieee80211_local *local)
00270 {
00271         INIT_LIST_HEAD(&local->work_list);
00272         setup_timer(&local->work_timer, ieee80211_work_timer,
00273                     (unsigned long)local);
00274         INIT_WORK(&local->work_work, ieee80211_work_work);
00275 }
00276 
00277 void ieee80211_work_purge(struct ieee80211_sub_if_data *sdata)
00278 {
00279         struct ieee80211_local *local = sdata->local;
00280         struct ieee80211_work *wk;
00281         bool cleanup = false;
00282 
00283         mutex_lock(&local->mtx);
00284         list_for_each_entry(wk, &local->work_list, list) {
00285                 if (wk->sdata != sdata)
00286                         continue;
00287                 cleanup = true;
00288                 wk->type = IEEE80211_WORK_ABORT;
00289                 wk->started = true;
00290                 wk->timeout = jiffies;
00291         }
00292         mutex_unlock(&local->mtx);
00293 
00294         /* run cleanups etc. */
00295         if (cleanup)
00296                 ieee80211_work_work(&local->work_work);
00297 
00298         mutex_lock(&local->mtx);
00299         list_for_each_entry(wk, &local->work_list, list) {
00300                 if (wk->sdata != sdata)
00301                         continue;
00302                 WARN_ON(1);
00303                 break;
00304         }
00305         mutex_unlock(&local->mtx);
00306 }
00307 
00308 static enum work_done_result ieee80211_remain_done(struct ieee80211_work *wk,
00309                                                    struct sk_buff *skb)
00310 {
00311         /*
00312          * We are done serving the remain-on-channel command.
00313          */
00314         cfg80211_remain_on_channel_expired(wk->sdata->dev, (unsigned long) wk,
00315                                            wk->chan, wk->chan_type,
00316                                            GFP_KERNEL);
00317 
00318         return WORK_DONE_DESTROY;
00319 }
00320 
00321 int ieee80211_wk_remain_on_channel(struct ieee80211_sub_if_data *sdata,
00322                                    struct ieee80211_channel *chan,
00323                                    enum nl80211_channel_type channel_type,
00324                                    unsigned int duration, u64 *cookie)
00325 {
00326         struct ieee80211_work *wk;
00327 
00328         wk = kzalloc(sizeof(*wk), GFP_KERNEL);
00329         if (!wk)
00330                 return -ENOMEM;
00331 
00332         wk->type = IEEE80211_WORK_REMAIN_ON_CHANNEL;
00333         wk->chan = chan;
00334         wk->chan_type = channel_type;
00335         wk->sdata = sdata;
00336         wk->done = ieee80211_remain_done;
00337 
00338         wk->remain.duration = duration;
00339 
00340         *cookie = (unsigned long) wk;
00341 
00342         ieee80211_add_work(wk);
00343 
00344         return 0;
00345 }
00346 
00347 int ieee80211_wk_cancel_remain_on_channel(struct ieee80211_sub_if_data *sdata,
00348                                           u64 cookie)
00349 {
00350         struct ieee80211_local *local = sdata->local;
00351         struct ieee80211_work *wk, *tmp;
00352         bool found = false;
00353 
00354         mutex_lock(&local->mtx);
00355         list_for_each_entry_safe(wk, tmp, &local->work_list, list) {
00356                 if ((unsigned long) wk == cookie) {
00357                         wk->timeout = jiffies;
00358                         found = true;
00359                         break;
00360                 }
00361         }
00362         mutex_unlock(&local->mtx);
00363 
00364         if (!found)
00365                 return -ENOENT;
00366 
00367         ieee80211_queue_work(&local->hw, &local->work_work);
00368 
00369         return 0;
00370 }


ros_rt_wmp
Author(s): Danilo Tardioli, dantard@unizar.es
autogenerated on Mon Oct 6 2014 08:27:11